diff --git a/.editorconfig b/.editorconfig index eadd72e499373..ef8ed24c52a50 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,6 +9,8 @@ end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true + +[!src/llvm-project] indent_style = space indent_size = 4 diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 8612a40923a8d..d2a017bd2a023 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -29,3 +29,5 @@ ec2cc761bc7067712ecc7734502f703fe3b024c8 99cb0c6bc399fb94a0ddde7e9b38e9c00d523bad # reformat with rustfmt edition 2024 c682aa162b0d41e21cc6748f4fecfe01efb69d1f +# reformat with updated edition 2024 +1fcae03369abb4c2cc180cd5a49e1f4440a81300 diff --git a/.github/ISSUE_TEMPLATE/bootstrap.md b/.github/ISSUE_TEMPLATE/bootstrap.md new file mode 100644 index 0000000000000..8d72eae85931f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bootstrap.md @@ -0,0 +1,70 @@ +--- +name: Bootstrap (Rust Build System) Report +about: Issues encountered on bootstrap build system +labels: C-bug, T-bootstrap +--- + + + +### Summary + + + +### Command used + +```sh + +``` + +### Expected behaviour + + + +### Actual behaviour + + + +### Bootstrap configuration (config.toml) +```toml + +``` + +### Operating system + + + +### HEAD + + + +### Additional context + + + + +
Build Log +

+ +```txt + +``` + +

+
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index ecf8f993f90b0..93388ddd24075 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -7,6 +7,6 @@ tracking issue or there are none, feel free to ignore this. This PR will get automatically assigned to a reviewer. In case you would like a specific user to review your work, you can assign it to them by using - r\? (with the `\` removed) + r? --> diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f0e151d25778c..59d5f779d68d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ # and also on pushes to special branches (auto, try). # # The actual definition of the executed jobs is calculated by a Python -# script located at src/ci/github-actions/calculate-job-matrix.py, which +# script located at src/ci/github-actions/ci.py, which # uses job definition data from src/ci/github-actions/jobs.yml. # You should primarily modify the `jobs.yml` file if you want to modify # what jobs are executed in CI. @@ -56,18 +56,15 @@ jobs: - name: Calculate the CI job matrix env: COMMIT_MESSAGE: ${{ github.event.head_commit.message }} - run: python3 src/ci/github-actions/calculate-job-matrix.py >> $GITHUB_OUTPUT + run: python3 src/ci/github-actions/ci.py calculate-job-matrix >> $GITHUB_OUTPUT id: jobs job: - name: ${{ matrix.name }} + name: ${{ matrix.full_name }} needs: [ calculate_matrix ] runs-on: "${{ matrix.os }}" - defaults: - run: - shell: ${{ contains(matrix.os, 'windows') && 'msys2 {0}' || 'bash' }} timeout-minutes: 360 env: - CI_JOB_NAME: ${{ matrix.image }} + CI_JOB_NAME: ${{ matrix.name }} CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse # commit of PR sha or commit sha. `GITHUB_SHA` is not accurate for PRs. HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} @@ -80,22 +77,6 @@ jobs: # Check the `calculate_matrix` job to see how is the matrix defined. include: ${{ fromJSON(needs.calculate_matrix.outputs.jobs) }} steps: - - if: contains(matrix.os, 'windows') - uses: msys2/setup-msys2@v2.22.0 - with: - # i686 jobs use mingw32. x86_64 and cross-compile jobs use mingw64. - msystem: ${{ contains(matrix.name, 'i686') && 'mingw32' || 'mingw64' }} - # don't try to download updates for already installed packages - update: false - # don't try to use the msys that comes built-in to the github runner, - # so we can control what is installed (i.e. not python) - release: true - # Inherit the full path from the Windows environment, with MSYS2's */bin/ - # dirs placed in front. This lets us run Windows-native Python etc. - path-type: inherit - install: > - make - - name: disable git crlf conversion run: git config --global core.autocrlf false @@ -109,7 +90,7 @@ jobs: # intensive jobs to run on free runners, which however also have # less disk space. - name: free up disk space - uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be + run: src/ci/scripts/free-disk-space.sh if: matrix.free_disk # Rust Log Analyzer can't currently detect the PR number of a GitHub @@ -192,6 +173,25 @@ jobs: - name: ensure the stable version number is correct run: src/ci/scripts/verify-stable-version-number.sh + # Temporary fix to unblock CI + # Remove the latest Windows SDK for 32-bit Windows MSVC builds. + # See issue https://github.com/rust-lang/rust/issues/137733 for more details. + - name: Remove Windows SDK 10.0.26100.0 + shell: powershell + if: ${{ matrix.name == 'i686-msvc-1' || matrix.name == 'i686-msvc-2' || matrix.name == 'dist-i686-msvc' }} + run: | + $kits = (Get-ItemProperty -path 'HKLM:\SOFTWARE\Microsoft\Windows Kits\Installed Roots').KitsRoot10 + $sdk_version = "10.0.26100.0" + + foreach ($kind in 'Bin', 'Lib', 'Include') { + Remove-Item -Force -Recurse $kits\$kind\$sdk_version -ErrorAction Continue + } + + # Show the environment just before we run the build + # This makes it easier to diagnose problems with the above install scripts. + - name: show the current environment + run: src/ci/scripts/dump-environment.sh + - name: run the build # Redirect stderr to stdout to avoid reordering the two streams in the GHA logs. run: src/ci/scripts/run-build-from-ci.sh 2>&1 @@ -233,7 +233,7 @@ jobs: env: DATADOG_SITE: datadoghq.com DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }} - DD_GITHUB_JOB_NAME: ${{ matrix.name }} + DD_GITHUB_JOB_NAME: ${{ matrix.full_name }} run: | cd src/ci npm ci diff --git a/.github/workflows/ghcr.yml b/.github/workflows/ghcr.yml new file mode 100644 index 0000000000000..052c9ae72b87e --- /dev/null +++ b/.github/workflows/ghcr.yml @@ -0,0 +1,68 @@ +# Mirror DockerHub images used by the Rust project to ghcr.io. +# Images are available at https://github.com/orgs/rust-lang/packages. +# +# In some CI jobs, we pull images from ghcr.io instead of Docker Hub because +# Docker Hub has a rate limit, while ghcr.io doesn't. +# Those images are pushed to ghcr.io by this job. +# +# Note that authenticating to DockerHub or other registries isn't possible +# for PR jobs, because forks can't access secrets. +# That's why we use ghcr.io: it has no rate limit and it doesn't require authentication. + +name: GHCR image mirroring + +on: + workflow_dispatch: + schedule: + # Run daily at midnight UTC + - cron: '0 0 * * *' + +jobs: + mirror: + name: DockerHub mirror + runs-on: ubuntu-24.04 + if: github.repository == 'rust-lang/rust' + permissions: + # Needed to write to the ghcr.io registry + packages: write + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Log in to registry + run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin + + # Download crane in the current directory. + # We use crane because it copies the docker image for all the architectures available in + # DockerHub for the image. + # Learn more about crane at + # https://github.com/google/go-containerregistry/blob/main/cmd/crane/README.md + - name: Download crane + run: | + curl -sL "https://github.com/google/go-containerregistry/releases/download/${VERSION}/go-containerregistry_${OS}_${ARCH}.tar.gz" | tar -xzf - + env: + VERSION: v0.20.2 + OS: Linux + ARCH: x86_64 + + - name: Mirror DockerHub + run: | + # List of DockerHub images to mirror to ghcr.io + images=( + # Mirrored because used by the mingw-check-tidy, which doesn't cache Docker images + "ubuntu:22.04" + # Mirrored because used by all linux CI jobs, including mingw-check-tidy + "moby/buildkit:buildx-stable-1" + ) + + # Mirror each image from DockerHub to ghcr.io + for img in "${images[@]}"; do + echo "Mirroring ${img}..." + # Remove namespace from the image if any. + # E.g. "moby/buildkit:buildx-stable-1" becomes "buildkit:buildx-stable-1" + dest_image=$(echo "${img}" | cut -d'/' -f2-) + ./crane copy \ + "docker.io/${img}" \ + "ghcr.io/${{ github.repository_owner }}/${dest_image}" + done diff --git a/.gitignore b/.gitignore index f84a3704ca900..2184e04f16b27 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,8 @@ no_llvm_build /library/target /src/bootstrap/target /src/tools/x/target +# Created by `x vendor` +/vendor # Created by default with `src/ci/docker/run.sh` /obj/ # Created by nix dev shell / .envrc @@ -83,6 +85,7 @@ __pycache__/ node_modules package-lock.json package.json +/src/doc/rustc-dev-guide/mermaid.min.js ## Rustdoc GUI tests tests/rustdoc-gui/src/**.lock diff --git a/.mailmap b/.mailmap index 87f32d28f5895..eb6a9fcabca99 100644 --- a/.mailmap +++ b/.mailmap @@ -88,7 +88,8 @@ boolean_coercion Boris Egorov bors bors[bot] <26634292+bors[bot]@users.noreply.github.com> bors bors[bot] -Boxy +BoxyUwU +BoxyUwU Braden Nelson Brandon Sanderson Brandon Sanderson Brett Cannon Brett Cannon diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eeff563d8ecdf..a5ddff595f5d6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,6 +12,15 @@ Documentation for contributing to the compiler or tooling is located in the [Gui Development][rustc-dev-guide], commonly known as the [rustc-dev-guide]. Documentation for the standard library in the [Standard library developers Guide][std-dev-guide], commonly known as the [std-dev-guide]. +## Making changes to subtrees and submodules + +For submodules, changes need to be made against the repository corresponding the +submodule, and not the main `rust-lang/rust` repository. + +For subtrees, prefer sending a PR against the subtree's repository if it does +not need to be made against the main `rust-lang/rust` repostory (e.g. a +rustc-dev-guide change that does not accompany a compiler change). + ## About the [rustc-dev-guide] The [rustc-dev-guide] is meant to help document how rustc –the Rust compiler– works, diff --git a/COPYRIGHT b/COPYRIGHT index 428a438a086e9..4dad74f234eaa 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -3,6 +3,7 @@ Short version for non-lawyers: The Rust Project is dual-licensed under Apache 2.0 and MIT terms. +It is Copyright (c) The Rust Project Contributors. Longer version: @@ -11,374 +12,23 @@ copyright assignment is required to contribute to the Rust project. Some files include explicit copyright notices and/or license notices. For full authorship information, see the version control history or -https://thanks.rust-lang.org - -Except as otherwise noted (below and/or in individual files), Rust is -licensed under the Apache License, Version 2.0 or - or the MIT license - or , at your option. - - -The Rust Project includes packages written by third parties. -The following third party packages are included, and carry -their own copyright notices and license terms: - -* LLVM, located in src/llvm-project, is licensed under the following - terms. - - ============================================================================== - The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: - ============================================================================== - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - - ---- LLVM Exceptions to the Apache 2.0 License ---- - - As an exception, if, as a result of your compiling your source code, portions - of this Software are embedded into an Object form of such source code, you - may redistribute such embedded portions in such Object form without complying - with the conditions of Sections 4(a), 4(b) and 4(d) of the License. - - In addition, if you combine or link compiled forms of this Software with - software that is licensed under the GPLv2 ("Combined Software") and if a - court of competent jurisdiction determines that the patent provision (Section - 3), the indemnity provision (Section 9) or other Section of the License - conflicts with the conditions of the GPLv2, you may retroactively and - prospectively choose to deem waived or otherwise exclude such Section(s) of - the License, but only in their entirety and only with respect to the Combined - Software. - - ============================================================================== - Software from third parties included in the LLVM Project: - ============================================================================== - The LLVM Project contains third party software which is under different license - terms. All such code will be identified clearly using at least one of two - mechanisms: - 1) It will be in a separate directory tree with its own `LICENSE.txt` or - `LICENSE` file at the top containing the specific license and restrictions - which apply to that software, or - 2) It will contain specific license and restriction terms at the top of every - file. - - ============================================================================== - Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy): - ============================================================================== - University of Illinois/NCSA - Open Source License - - Copyright (c) 2003-2019 University of Illinois at Urbana-Champaign. - All rights reserved. - - Developed by: - - LLVM Team - - University of Illinois at Urbana-Champaign - - http://llvm.org - - Permission is hereby granted, free of charge, to any person obtaining a copy of - this software and associated documentation files (the "Software"), to deal with - the Software without restriction, including without limitation the rights to - use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - of the Software, and to permit persons to whom the Software is furnished to do - so, subject to the following conditions: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimers. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimers in the - documentation and/or other materials provided with the distribution. - - * Neither the names of the LLVM Team, University of Illinois at - Urbana-Champaign, nor the names of its contributors may be used to - endorse or promote products derived from this Software without specific - prior written permission. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE - SOFTWARE. - -* Portions of the FFI code for interacting with the native ABI - is derived from the Clay programming language, which carries - the following license. - - Copyright (C) 2008-2010 Tachyon Technologies. - All rights reserved. - - Redistribution and use in source and binary forms, with - or without modification, are permitted provided that the - following conditions are met: - - 1. Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - - 2. Redistributions in binary form must reproduce the - above copyright notice, this list of conditions and - the following disclaimer in the documentation and/or - other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY - OF SUCH DAMAGE. - -* Portions of internationalization code use code or data from Unicode, which - carry the following license: - - UNICODE LICENSE V3 - - COPYRIGHT AND PERMISSION NOTICE - - Copyright © 1991-2024 Unicode, Inc. - - NOTICE TO USER: Carefully read the following legal agreement. BY - DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR - SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE - TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT - DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. - - Permission is hereby granted, free of charge, to any person obtaining a - copy of data files and any associated documentation (the "Data Files") or - software and any associated documentation (the "Software") to deal in the - Data Files or Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, and/or sell - copies of the Data Files or Software, and to permit persons to whom the - Data Files or Software are furnished to do so, provided that either (a) - this copyright and permission notice appear with all copies of the Data - Files or Software, or (b) this copyright and permission notice appear in - associated Documentation. - - THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY - KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF - THIRD PARTY RIGHTS. - - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE - BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, - OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, - WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA - FILES OR SOFTWARE. - - Except as contained in this notice, the name of a copyright holder shall - not be used in advertising or otherwise to promote the sale, use or other - dealings in these Data Files or Software without prior written - authorization of the copyright holder. + + +Except as otherwise noted, Rust is licensed under the Apache License, Version +2.0 or or the MIT +license or , at your option. + +We track licenses for third-party materials in two ways: + +* We use [REUSE](https://reuse.software) to track license information for + in-tree source files - both those authored by the Rust project and those + authored by third parties. See `REUSE.toml`, and our cached output of the + `reuse` tool which is committed to `license-metadata.json`. +* We use `cargo` to track license information for out-of-tree dependencies. + +These two sources of information are collected by the tool `generate-copyright` +into a file called `COPYRIGHT.html`, which is shipped with each binary release +of Rust. Please refer to that file for detailed information as to the components of +any given Rust release. We also produce a `COPYRIGHT-library.html` file which only +covers the subset of source code used in the Rust Standard Library, as opposed +to the toolchain as a whole. diff --git a/Cargo.lock b/Cargo.lock index 7d22abc8611e5..38a861727a9d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,7 +43,7 @@ dependencies = [ "cfg-if", "once_cell", "version_check", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -172,11 +172,12 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "3.0.6" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", + "once_cell", "windows-sys 0.59.0", ] @@ -257,9 +258,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" [[package]] name = "blake3" @@ -285,9 +286,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.11.1" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786a307d683a5bf92e6fd5fd69a7eb613751668d1d8d67d802846dfe367c62c8" +checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" dependencies = [ "memchr", "regex-automata 0.4.9", @@ -410,7 +411,7 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror 2.0.9", + "thiserror 2.0.11", ] [[package]] @@ -419,9 +420,9 @@ version = "0.1.0" [[package]] name = "cc" -version = "1.2.6" +version = "1.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333" +checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda" dependencies = [ "shlex", ] @@ -453,9 +454,9 @@ dependencies = [ [[package]] name = "chrono-tz" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd6dd8046d00723a59a2f8c5f295c515b9bb9a331ee4f8f3d4dd49e428acd3b6" +checksum = "9c6ac4f2c0bf0f44e9161aec9675e1050aa4a530663c4a9e37e108fa948bca9f" dependencies = [ "chrono", "chrono-tz-build", @@ -484,9 +485,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.23" +version = "4.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783" dependencies = [ "clap_builder", "clap_derive", @@ -504,9 +505,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.23" +version = "4.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121" dependencies = [ "anstream", "anstyle", @@ -517,23 +518,23 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.40" +version = "4.5.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac2e663e3e3bed2d32d065a8404024dad306e699a04263ec59919529f803aee9" +checksum = "33a7e468e750fa4b6be660e8b5651ad47372e8fb114030b594c2d75d48c5ffd0" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -544,7 +545,7 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "clippy" -version = "0.1.85" +version = "0.1.86" dependencies = [ "anstream", "cargo_metadata 0.18.1", @@ -564,18 +565,18 @@ dependencies = [ "rustc_tools_util", "serde", "serde_json", - "syn 2.0.93", + "syn 2.0.96", "tempfile", "termize", "tokio", "toml 0.7.8", - "ui_test", + "ui_test 0.26.5", "walkdir", ] [[package]] name = "clippy_config" -version = "0.1.85" +version = "0.1.86" dependencies = [ "clippy_utils", "itertools", @@ -600,7 +601,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.85" +version = "0.1.86" dependencies = [ "arrayvec", "cargo_metadata 0.18.1", @@ -623,7 +624,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.85" +version = "0.1.86" dependencies = [ "arrayvec", "itertools", @@ -674,7 +675,7 @@ dependencies = [ "nom", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -736,7 +737,7 @@ dependencies = [ "tracing-subscriber", "unified-diff", "walkdir", - "windows", + "windows 0.59.0", ] [[package]] @@ -899,7 +900,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -910,7 +911,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -947,7 +948,7 @@ checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -968,7 +969,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -978,7 +979,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -990,7 +991,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -1068,7 +1069,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -1097,9 +1098,9 @@ dependencies = [ [[package]] name = "elsa" -version = "1.7.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "848fe615fbb0a74d9ae68dcaa510106d32e37d9416207bbea4bd008bd89c47ed" +checksum = "2343daaeabe09879d4ea058bb4f1e63da3fc07dadc6634e01bda1b3d6a9d9d2b" dependencies = [ "stable_deref_trait", ] @@ -1198,13 +1199,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] -name = "field-offset" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +name = "features-status-dump" +version = "0.1.0" dependencies = [ - "memoffset", - "rustc_version", + "anyhow", + "clap", + "serde", + "serde_json", + "tidy", ] [[package]] @@ -1226,7 +1228,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", - "miniz_oxide 0.8.2", + "miniz_oxide 0.8.3", ] [[package]] @@ -1365,7 +1367,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -1444,7 +1446,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.6", ] [[package]] @@ -1505,17 +1519,18 @@ dependencies = [ [[package]] name = "handlebars" -version = "6.2.0" +version = "6.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd4ccde012831f9a071a637b0d4e31df31c0f6c525784b35ae76a9ac6bc1e315" +checksum = "3d6b224b95c1e668ac0270325ad563b2eef1469fbbb8959bc7c692c844b813d9" dependencies = [ + "derive_builder", "log", "num-order", "pest", "pest_derive", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.11", ] [[package]] @@ -1526,7 +1541,6 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", - "serde", ] [[package]] @@ -1536,6 +1550,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ "foldhash", + "serde", ] [[package]] @@ -1599,7 +1614,7 @@ dependencies = [ "markup5ever", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -1788,7 +1803,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -1860,7 +1875,6 @@ checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown 0.15.2", - "rustc-rayon", "serde", ] @@ -1956,9 +1970,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", @@ -1983,7 +1997,7 @@ dependencies = [ "anyhow", "clap", "fs-err", - "rustc-hash 2.1.0", + "rustc-hash 2.1.1", "rustdoc-json-types", "serde", "serde_json", @@ -2088,9 +2102,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.20" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" +checksum = "df9b68e50e6e0b26f672573834882eb57759f6db9b3be2ea3c35c91188bb4eaa" dependencies = [ "cc", "libc", @@ -2117,9 +2131,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "litemap" @@ -2154,9 +2168,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "lzma-sys" @@ -2271,15 +2285,6 @@ dependencies = [ "libc", ] -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - [[package]] name = "mime" version = "0.3.17" @@ -2298,9 +2303,9 @@ dependencies = [ [[package]] name = "minifier" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd559bbf5d350ac7f2c1cf92ed71a869b847a92bce0c1318b47932a5b5f65cdd" +checksum = "1cf47565b1430f5fe6c81d3afcb4b835271348d7eb35294a4d592e38dd09ea22" [[package]] name = "minimal-lexical" @@ -2319,9 +2324,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ "adler2", ] @@ -2344,18 +2349,18 @@ dependencies = [ "chrono-tz", "colored", "directories", - "getrandom", + "getrandom 0.3.1", "libc", "libffi", "libloading", "measureme", - "rand", + "rand 0.9.0", "regex", "rustc_version", "smallvec", "tempfile", "tikv-jemalloc-sys", - "ui_test", + "ui_test 0.28.0", "windows-sys 0.52.0", ] @@ -2720,7 +2725,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror 2.0.9", + "thiserror 2.0.11", "ucd-trie", ] @@ -2744,7 +2749,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -2760,21 +2765,21 @@ dependencies = [ [[package]] name = "phf" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ - "phf_shared 0.11.2", + "phf_shared 0.11.3", ] [[package]] name = "phf_codegen" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" dependencies = [ - "phf_generator 0.11.2", - "phf_shared 0.11.2", + "phf_generator 0.11.3", + "phf_shared 0.11.3", ] [[package]] @@ -2784,17 +2789,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" dependencies = [ "phf_shared 0.10.0", - "rand", + "rand 0.8.5", ] [[package]] name = "phf_generator" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "phf_shared 0.11.2", - "rand", + "phf_shared 0.11.3", + "rand 0.8.5", ] [[package]] @@ -2803,23 +2808,23 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" dependencies = [ - "siphasher", + "siphasher 0.3.11", ] [[package]] name = "phf_shared" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ - "siphasher", + "siphasher 1.0.1", ] [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -2862,7 +2867,7 @@ version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -2889,9 +2894,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -2980,8 +2985,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.0", + "zerocopy 0.8.14", ] [[package]] @@ -2991,7 +3007,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.0", ] [[package]] @@ -3000,7 +3026,17 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", +] + +[[package]] +name = "rand_core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff" +dependencies = [ + "getrandom 0.3.1", + "zerocopy 0.8.14", ] [[package]] @@ -3009,7 +3045,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" dependencies = [ - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -3047,7 +3083,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom", + "getrandom 0.2.15", "libredox", "thiserror 1.0.69", ] @@ -3152,9 +3188,9 @@ dependencies = [ "proc-macro2", "quote", "rinja_parser", - "rustc-hash 2.1.0", + "rustc-hash 2.1.1", "serde", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -3216,9 +3252,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustc-main" @@ -3232,20 +3268,14 @@ dependencies = [ "tikv-jemalloc-sys", ] -[[package]] -name = "rustc-perf-wrapper" -version = "0.1.0" -dependencies = [ - "clap", -] - [[package]] name = "rustc-rayon" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb81aadc8837ca6ecebe0fe1353f15df83b3b3cc2cf7a8afd571bc22aa121710" +checksum = "2cd9fb077db982d7ceb42a90471e5a69a990b58f71e06f0d8340bb2cf35eb751" dependencies = [ "either", + "indexmap", "rustc-rayon-core", ] @@ -3284,10 +3314,9 @@ name = "rustc_abi" version = "0.0.0" dependencies = [ "bitflags", - "rand", + "rand 0.8.5", "rand_xoshiro", "rustc_data_structures", - "rustc_feature", "rustc_index", "rustc_macros", "rustc_serialize", @@ -3344,10 +3373,12 @@ dependencies = [ name = "rustc_ast_lowering" version = "0.0.0" dependencies = [ + "rustc_abi", "rustc_ast", "rustc_ast_pretty", "rustc_data_structures", "rustc_errors", + "rustc_feature", "rustc_fluent_macro", "rustc_hir", "rustc_index", @@ -3366,6 +3397,7 @@ name = "rustc_ast_passes" version = "0.0.0" dependencies = [ "itertools", + "rustc_abi", "rustc_ast", "rustc_ast_pretty", "rustc_attr_parsing", @@ -3377,7 +3409,6 @@ dependencies = [ "rustc_parse", "rustc_session", "rustc_span", - "rustc_target", "thin-vec", ] @@ -3499,6 +3530,7 @@ name = "rustc_codegen_llvm" version = "0.0.0" dependencies = [ "bitflags", + "gimli 0.30.0", "itertools", "libc", "measureme", @@ -3537,6 +3569,7 @@ dependencies = [ "ar_archive_writer", "arrayvec", "bitflags", + "bstr", "cc", "either", "itertools", @@ -3575,7 +3608,7 @@ dependencies = [ "thorin-dwp", "tracing", "wasm-encoder 0.219.1", - "windows", + "windows 0.59.0", ] [[package]] @@ -3620,7 +3653,7 @@ dependencies = [ "memmap2", "parking_lot", "portable-atomic", - "rustc-hash 2.1.0", + "rustc-hash 2.1.1", "rustc-rayon", "rustc-stable-hash", "rustc_arena", @@ -3633,7 +3666,7 @@ dependencies = [ "tempfile", "thin-vec", "tracing", - "windows", + "windows 0.59.0", ] [[package]] @@ -3649,6 +3682,7 @@ version = "0.0.0" dependencies = [ "ctrlc", "libc", + "rustc_abi", "rustc_ast", "rustc_ast_lowering", "rustc_ast_passes", @@ -3695,7 +3729,7 @@ dependencies = [ "shlex", "time", "tracing", - "windows", + "windows 0.59.0", ] [[package]] @@ -3748,7 +3782,7 @@ dependencies = [ "termcolor", "termize", "tracing", - "windows", + "windows 0.59.0", ] [[package]] @@ -3794,7 +3828,7 @@ dependencies = [ "fluent-syntax", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", "unic-langid", ] @@ -3896,7 +3930,7 @@ dependencies = [ name = "rustc_incremental" version = "0.0.0" dependencies = [ - "rand", + "rand 0.8.5", "rustc_ast", "rustc_data_structures", "rustc_errors", @@ -3929,7 +3963,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -3957,6 +3991,7 @@ version = "0.0.0" dependencies = [ "rustc-rayon", "rustc-rayon-core", + "rustc_abi", "rustc_ast", "rustc_ast_lowering", "rustc_ast_passes", @@ -4006,6 +4041,7 @@ name = "rustc_lexer" version = "0.0.0" dependencies = [ "expect-test", + "memchr", "unicode-properties", "unicode-xid", ] @@ -4077,7 +4113,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", "synstructure", ] @@ -4117,9 +4153,7 @@ name = "rustc_middle" version = "0.0.0" dependencies = [ "bitflags", - "derive-where", "either", - "field-offset", "gsgdt", "polonius-engine", "rustc-rayon-core", @@ -4161,6 +4195,7 @@ dependencies = [ "rustc_apfloat", "rustc_arena", "rustc_ast", + "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", "rustc_fluent_macro", @@ -4233,6 +4268,7 @@ name = "rustc_monomorphize" version = "0.0.0" dependencies = [ "rustc_abi", + "rustc_ast", "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", @@ -4242,6 +4278,7 @@ dependencies = [ "rustc_middle", "rustc_session", "rustc_span", + "rustc_symbol_mangling", "rustc_target", "serde", "serde_json", @@ -4300,6 +4337,7 @@ version = "0.0.0" dependencies = [ "rustc_abi", "rustc_ast", + "rustc_ast_lowering", "rustc_ast_pretty", "rustc_attr_parsing", "rustc_data_structures", @@ -4323,7 +4361,7 @@ dependencies = [ name = "rustc_pattern_analysis" version = "0.0.0" dependencies = [ - "rustc-hash 2.1.0", + "rustc-hash 2.1.1", "rustc_abi", "rustc_apfloat", "rustc_arena", @@ -4364,7 +4402,6 @@ dependencies = [ name = "rustc_query_impl" version = "0.0.0" dependencies = [ - "field-offset", "measureme", "rustc_data_structures", "rustc_errors", @@ -4385,6 +4422,7 @@ version = "0.0.0" dependencies = [ "parking_lot", "rustc-rayon-core", + "rustc_abi", "rustc_ast", "rustc_data_structures", "rustc_errors", @@ -4396,7 +4434,6 @@ dependencies = [ "rustc_serialize", "rustc_session", "rustc_span", - "rustc_target", "smallvec", "thin-vec", "tracing", @@ -4480,7 +4517,7 @@ dependencies = [ "smallvec", "termize", "tracing", - "windows", + "windows 0.59.0", ] [[package]] @@ -4665,7 +4702,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", "synstructure", ] @@ -4719,7 +4756,7 @@ name = "rustdoc-json-types" version = "0.1.0" dependencies = [ "bincode", - "rustc-hash 2.1.0", + "rustc-hash 2.1.1", "serde", "serde_json", ] @@ -4754,7 +4791,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -4788,9 +4825,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.42" +version = "0.38.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" dependencies = [ "bitflags", "errno", @@ -4891,14 +4928,14 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] name = "serde_json" -version = "1.0.134" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" +checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" dependencies = [ "indexmap", "itoa", @@ -4971,6 +5008,12 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "slab" version = "0.4.9" @@ -5008,9 +5051,9 @@ dependencies = [ [[package]] name = "spdx" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae30cc7bfe3656d60ee99bf6836f472b0c53dddcbf335e253329abb16e535a2" +checksum = "58b69356da67e2fc1f542c71ea7e654a361a79c938e4424392ecf4fa065d2193" dependencies = [ "smallvec", ] @@ -5149,9 +5192,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.93" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -5166,7 +5209,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -5177,7 +5220,7 @@ checksum = "355dbe4f8799b304b05e1b0f05fc59b2a18d36645cf169607da45bde2f69a1be" dependencies = [ "core-foundation-sys", "libc", - "windows", + "windows 0.57.0", ] [[package]] @@ -5203,12 +5246,13 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.14.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if", "fastrand", + "getrandom 0.2.15", "once_cell", "rustix", "windows-sys 0.59.0", @@ -5271,8 +5315,8 @@ version = "0.1.0" dependencies = [ "indicatif", "num", - "rand", - "rand_chacha", + "rand 0.8.5", + "rand_chacha 0.3.1", "rayon", ] @@ -5293,11 +5337,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.9" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ - "thiserror-impl 2.0.9", + "thiserror-impl 2.0.11", ] [[package]] @@ -5308,18 +5352,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] name = "thiserror-impl" -version = "2.0.9" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -5363,8 +5407,9 @@ dependencies = [ "ignore", "miropt-test-tools", "regex", - "rustc-hash 2.1.0", + "rustc-hash 2.1.1", "semver", + "serde", "similar", "termcolor", "walkdir", @@ -5442,9 +5487,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.42.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", @@ -5520,7 +5565,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] @@ -5592,7 +5637,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", - "rand", + "rand 0.8.5", "static_assertions", ] @@ -5652,6 +5697,32 @@ dependencies = [ "spanned", ] +[[package]] +name = "ui_test" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7484683d60d50ca1d1b6433c3dbf6c5ad71d20387acdcfb16fe79573f3fba576" +dependencies = [ + "annotate-snippets 0.11.5", + "anyhow", + "bstr", + "cargo-platform", + "cargo_metadata 0.18.1", + "color-eyre", + "colored", + "comma", + "crossbeam-channel", + "indicatif", + "levenshtein", + "prettydiff", + "regex", + "rustc_version", + "rustfix", + "serde", + "serde_json", + "spanned", +] + [[package]] name = "unic-langid" version = "0.9.5" @@ -5691,7 +5762,7 @@ checksum = "1ed7f4237ba393424195053097c1516bd4590dc82b84f2f97c5c69e12704555b" dependencies = [ "proc-macro-hack", "quote", - "syn 2.0.93", + "syn 2.0.96", "unic-langid-impl", ] @@ -5829,18 +5900,18 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "744018581f9a3454a9e15beb8a33b017183f1e7c0cd170232a2d1453b23a51c4" dependencies = [ - "getrandom", + "getrandom 0.2.15", ] [[package]] name = "valuable" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "vcpkg" @@ -5870,42 +5941,52 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasi-preview1-component-adapter-provider" -version = "24.0.1" +version = "29.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f76d9fa52234153eeb40b088de91a8c13dc28a912cf6f31cd89ca4bac9024e0" +checksum = "dcd9f21bbde82ba59e415a8725e6ad0d0d7e9e460b1a3ccbca5bdee952c1a324" [[package]] name = "wasm-bindgen" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5913,28 +5994,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-component-ld" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b05c3820968b335f10e703218459e4fd2cc91fdfc8f7936a993f1aacaa0938" +checksum = "580305a8e3f1b7a79859a8db897de643533b2851c5eb080fe5800233f16dec88" dependencies = [ "anyhow", "clap", @@ -5942,7 +6026,7 @@ dependencies = [ "libc", "tempfile", "wasi-preview1-component-adapter-provider", - "wasmparser 0.219.1", + "wasmparser 0.223.0", "wat", "windows-sys 0.59.0", "winsplit", @@ -5969,19 +6053,19 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.222.0" +version = "0.223.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3432682105d7e994565ef928ccf5856cf6af4ba3dddebedb737f61caed70f956" +checksum = "7e636076193fa68103e937ac951b5f2f587624097017d764b8984d9c0f149464" dependencies = [ "leb128", - "wasmparser 0.222.0", + "wasmparser 0.223.0", ] [[package]] name = "wasm-metadata" -version = "0.219.1" +version = "0.223.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af5a8e37a5e996861e1813f8de30911c47609c9ff51a7284f7dbd754dc3a9f3" +checksum = "5c730c3379d3d20e5a0245b0724b924483e853588ca8fba547c1e21f19e7d735" dependencies = [ "anyhow", "indexmap", @@ -5989,8 +6073,9 @@ dependencies = [ "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.219.1", - "wasmparser 0.219.1", + "url", + "wasm-encoder 0.223.0", + "wasmparser 0.223.0", ] [[package]] @@ -5999,12 +6084,8 @@ version = "0.219.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c771866898879073c53b565a6c7b49953795159836714ac56a5befb581227c5" dependencies = [ - "ahash", "bitflags", - "hashbrown 0.14.5", "indexmap", - "semver", - "serde", ] [[package]] @@ -6014,28 +6095,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4adf50fde1b1a49c1add6a80d47aea500c88db70551805853aa8b88f3ea27ab5" dependencies = [ "bitflags", +] + +[[package]] +name = "wasmparser" +version = "0.223.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5a99faceb1a5a84dd6084ec4bfa4b2ab153b5793b43fd8f58b89232634afc35" +dependencies = [ + "bitflags", + "hashbrown 0.15.2", "indexmap", "semver", + "serde", ] [[package]] name = "wast" -version = "222.0.0" +version = "223.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce7191f4b7da0dd300cc32476abae6457154e4625d9b1bc26890828a9a26f6e" +checksum = "d59b2ba8a2ff9f06194b7be9524f92e45e70149f4dacc0d0c7ad92b59ac875e4" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width 0.2.0", - "wasm-encoder 0.222.0", + "wasm-encoder 0.223.0", ] [[package]] name = "wat" -version = "1.222.0" +version = "1.223.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fde61b4b52f9a84ae31b5e8902a2cd3162ea45d8bf564c729c3288fe52f4334" +checksum = "662786915c427e4918ff01eabb3c4756d4d947cd8f635761526b4cc9da2eaaad" dependencies = [ "wast", ] @@ -6091,18 +6183,23 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f919aee0a93304be7f62e8e5027811bbba96bcb1de84d6618be56e43f8a32a1" +dependencies = [ + "windows-core 0.59.0", + "windows-targets 0.53.0", +] + [[package]] name = "windows-bindgen" -version = "0.58.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91cd28d93c692351f3a6e5615567c56756e330bee1c99c6bdd57bfc5ab15f589" +checksum = "9b7fb600834d7e868f6e5bb748a86101427330fafbf9485c331b9d5f562d54a5" dependencies = [ - "proc-macro2", "rayon", - "serde", - "serde_json", - "syn 2.0.93", - "windows-metadata", ] [[package]] @@ -6120,12 +6217,25 @@ version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" dependencies = [ - "windows-implement", - "windows-interface", - "windows-result", + "windows-implement 0.57.0", + "windows-interface 0.57.0", + "windows-result 0.1.2", "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "810ce18ed2112484b0d4e15d022e5f598113e220c53e373fb31e67e21670c1ce" +dependencies = [ + "windows-implement 0.59.0", + "windows-interface 0.59.0", + "windows-result 0.3.0", + "windows-strings", + "windows-targets 0.53.0", +] + [[package]] name = "windows-implement" version = "0.57.0" @@ -6134,7 +6244,18 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", +] + +[[package]] +name = "windows-implement" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", ] [[package]] @@ -6145,14 +6266,19 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] [[package]] -name = "windows-metadata" -version = "0.58.0" +name = "windows-interface" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e837f3c3012cfe9e7086302a93f441a7999439be1ad4c530d55d2f6d2921809" +checksum = "cb26fd936d991781ea39e87c3a27285081e3c0da5ca0fcbc02d368cc6f52ff01" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] [[package]] name = "windows-result" @@ -6163,6 +6289,24 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-result" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d08106ce80268c4067c0571ca55a9b4e9516518eaa1a1fe9b37ca403ae1d1a34" +dependencies = [ + "windows-targets 0.53.0", +] + +[[package]] +name = "windows-strings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b888f919960b42ea4e11c2f408fadb55f78a9f236d5eef084103c8ce52893491" +dependencies = [ + "windows-targets 0.53.0", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -6214,13 +6358,29 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -6233,6 +6393,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -6245,6 +6411,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -6257,12 +6429,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -6275,6 +6459,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -6287,6 +6477,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -6299,6 +6495,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -6311,6 +6513,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" version = "0.5.40" @@ -6326,11 +6534,20 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ab703352da6a72f35c39a533526393725640575bb211f61987a2748323ad956" +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags", +] + [[package]] name = "wit-component" -version = "0.219.1" +version = "0.223.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1673163c0cb14a6a19ddbf44dd4efe6f015ec1ebb8156710ac32501f19fba2" +checksum = "c10ed2aeee4c8ec5715875f62f4a3de3608d6987165c116810d8c2908aa9d93b" dependencies = [ "anyhow", "bitflags", @@ -6339,17 +6556,17 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.219.1", + "wasm-encoder 0.223.0", "wasm-metadata", - "wasmparser 0.219.1", + "wasmparser 0.223.0", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.219.1" +version = "0.223.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a86f669283257e8e424b9a4fc3518e3ade0b95deb9fbc0f93a1876be3eda598" +checksum = "92772f4dcacb804b275981eea1d920b12b377993b53307f1e33d87404e080281" dependencies = [ "anyhow", "id-arena", @@ -6360,7 +6577,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.219.1", + "wasmparser 0.223.0", ] [[package]] @@ -6377,9 +6594,9 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "xattr" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +checksum = "e105d177a3871454f754b33bb0ee637ecaaac997446375fd3e5d43a2ed00c909" dependencies = [ "libc", "linux-raw-sys", @@ -6424,7 +6641,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", "synstructure", ] @@ -6435,7 +6652,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a367f292d93d4eab890745e75a778da40909cab4d6ff8173693812f79c4a2468" +dependencies = [ + "zerocopy-derive 0.8.14", ] [[package]] @@ -6446,7 +6672,18 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", ] [[package]] @@ -6466,7 +6703,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", "synstructure", ] @@ -6489,5 +6726,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.93", + "syn 2.0.96", ] diff --git a/Cargo.toml b/Cargo.toml index b773030b4cab4..97e782d0df020 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,8 +45,8 @@ members = [ "src/tools/rustdoc-gui-test", "src/tools/opt-dist", "src/tools/coverage-dump", - "src/tools/rustc-perf-wrapper", "src/tools/wasm-component-ld", + "src/tools/features-status-dump", ] exclude = [ diff --git a/LICENSE-MIT b/LICENSE-MIT index 31aa79387f27e..a2070c4be7acd 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,3 +1,5 @@ +Copyright (c) The Rust Project Contributors + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the diff --git a/RELEASES.md b/RELEASES.md index 99733bade32c4..6faacc59c81dc 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,368 @@ +Version 1.86.0 (2025-04-03) +========================== + + + +Language +-------- +- [Stabilize upcasting trait objects to supertraits.](https://github.com/rust-lang/rust/pull/134367) +- [Allow safe functions to be marked with the `#[target_feature]` attribute.](https://github.com/rust-lang/rust/pull/134090) +- [The `missing_abi` lint now warns-by-default.](https://github.com/rust-lang/rust/pull/132397) +- Rust now lints about double negations, to catch cases that might have intended to be a prefix decrement operator (`--x`) as written in other languages. This was previously a clippy lint, `clippy::double_neg`, and is [now available directly in Rust as `double_negations`.](https://github.com/rust-lang/rust/pull/126604) +- [More pointers are now detected as definitely not-null based on their alignment in const eval.](https://github.com/rust-lang/rust/pull/133700) +- [Empty `repr()` attribute applied to invalid items are now correctly rejected.](https://github.com/rust-lang/rust/pull/133925) +- [Inner attributes `#![test]` and `#![rustfmt::skip]` are no longer accepted in more places than intended.](https://github.com/rust-lang/rust/pull/134276) + + + +Compiler +-------- +- [Debug-assert that raw pointers are non-null on access.](https://github.com/rust-lang/rust/pull/134424) +- [Change `-O` to mean `-C opt-level=3` instead of `-C opt-level=2` to match Cargo's defaults.](https://github.com/rust-lang/rust/pull/135439) +- [Fix emission of `overflowing_literals` under certain macro environments.](https://github.com/rust-lang/rust/pull/136393) + + + +Platform Support +---------------- +- [Replace `i686-unknown-redox` target with `i586-unknown-redox`.](https://github.com/rust-lang/rust/pull/136698) +- [Increase baseline CPU of `i686-unknown-hurd-gnu` to Pentium 4.](https://github.com/rust-lang/rust/pull/136700) +- New tier 3 targets: + - [`{aarch64-unknown,x86_64-pc}-nto-qnx710_iosock`](https://github.com/rust-lang/rust/pull/133631). + For supporting Neutrino QNX 7.1 with `io-socket` network stack. + - [`{aarch64-unknown,x86_64-pc}-nto-qnx800`](https://github.com/rust-lang/rust/pull/133631). + For supporting Neutrino QNX 8.0 (`no_std`-only). + - [`{x86_64,i686}-win7-windows-gnu`](https://github.com/rust-lang/rust/pull/134609). + Intended for backwards compatibility with Windows 7. `{x86_64,i686}-win7-windows-msvc` are the Windows MSVC counterparts that already exist as Tier 3 targets. + - [`amdgcn-amd-amdhsa`](https://github.com/rust-lang/rust/pull/134740). + - [`x86_64-pc-cygwin`](https://github.com/rust-lang/rust/pull/134999). + - [`{mips,mipsel}-mti-none-elf`](https://github.com/rust-lang/rust/pull/135074). + Initial bare-metal support. + - [`m68k-unknown-none-elf`](https://github.com/rust-lang/rust/pull/135085). + - [`armv7a-nuttx-{eabi,eabihf}`, `aarch64-unknown-nuttx`, and `thumbv7a-nuttx-{eabi,eabihf}`](https://github.com/rust-lang/rust/pull/135757). + +Refer to Rust's [platform support page][platform-support-doc] +for more information on Rust's tiered platform support. + + + +Libraries +--------- +- The type of `FromBytesWithNulError` in `CStr::from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, FromBytesWithNulError>` was [changed from an opaque struct to an enum](https://github.com/rust-lang/rust/pull/134143), allowing users to examine why the conversion failed. +- [Remove `RustcDecodable` and `RustcEncodable`.](https://github.com/rust-lang/rust/pull/134272) +- [Deprecate libtest's `--logfile` option.](https://github.com/rust-lang/rust/pull/134283) +- [On recent versions of Windows, `std::fs::remove_file` will now remove read-only files.](https://github.com/rust-lang/rust/pull/134679) + + + +Stabilized APIs +--------------- + +- [`{float}::next_down`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.next_down) +- [`{float}::next_up`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.next_up) +- [`<[_]>::get_disjoint_mut`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.get_disjoint_mut) +- [`<[_]>::get_disjoint_unchecked_mut`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.get_disjoint_unchecked_mut) +- [`slice::GetDisjointMutError`](https://doc.rust-lang.org/stable/std/slice/enum.GetDisjointMutError.html) +- [`HashMap::get_disjoint_mut`](https://doc.rust-lang.org/std/collections/hash_map/struct.HashMap.html#method.get_disjoint_mut) +- [`HashMap::get_disjoint_unchecked_mut`](https://doc.rust-lang.org/std/collections/hash_map/struct.HashMap.html#method.get_disjoint_unchecked_mut) +- [`NonZero::count_ones`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.count_ones) +- [`Vec::pop_if`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.pop_if) +- [`sync::Once::wait`](https://doc.rust-lang.org/stable/std/sync/struct.Once.html#method.wait) +- [`sync::Once::wait_force`](https://doc.rust-lang.org/stable/std/sync/struct.Once.html#method.wait_force) +- [`sync::OnceLock::wait`](https://doc.rust-lang.org/stable/std/sync/struct.OnceLock.html#method.wait) + +These APIs are now stable in const contexts: + +- [`hint::black_box`](https://doc.rust-lang.org/stable/std/hint/fn.black_box.html) +- [`io::Cursor::get_mut`](https://doc.rust-lang.org/stable/std/io/struct.Cursor.html#method.get_mut) +- [`io::Cursor::set_position`](https://doc.rust-lang.org/stable/std/io/struct.Cursor.html#method.set_position) +- [`str::is_char_boundary`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.is_char_boundary) +- [`str::split_at`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.split_at) +- [`str::split_at_checked`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.split_at_checked) +- [`str::split_at_mut`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.split_at_mut) +- [`str::split_at_mut_checked`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.split_at_mut_checked) + + + +Cargo +----- +- [When merging, replace rather than combine configuration keys that refer to a program path and its arguments.](https://github.com/rust-lang/cargo/pull/15066/) +- [Error if both `--package` and `--workspace` are passed but the requested package is missing.](https://github.com/rust-lang/cargo/pull/15071/) This was previously silently ignored, which was considered a bug since missing packages should be reported. +- [Deprecate the token argument in `cargo login` to avoid shell history leaks.](https://github.com/rust-lang/cargo/pull/15057/) +- [Simplify the implementation of `SourceID` comparisons.](https://github.com/rust-lang/cargo/pull/14980/) This may potentially change behavior if the canonicalized URL compares differently in alternative registries. + + + +Rustdoc +----- +- [Add a sans-serif font setting.](https://github.com/rust-lang/rust/pull/133636) + + + +Compatibility Notes +------------------- +- [The `wasm_c_abi` future compatibility warning is now a hard error.](https://github.com/rust-lang/rust/pull/133951) + Users of `wasm-bindgen` should upgrade to at least version 0.2.89, otherwise compilation will fail. +- [Remove long-deprecated no-op attributes `#![no_start]` and `#![crate_id]`.](https://github.com/rust-lang/rust/pull/134300) +- [The future incompatibility lint `cenum_impl_drop_cast` has been made into a hard error.](https://github.com/rust-lang/rust/pull/135964) This means it is now an error to cast a field-less enum to an integer if the enum implements `Drop`. +- [SSE2 is now required for "i686" 32-bit x86 hard-float targets; disabling it causes a warning that will become a hard error eventually.](https://github.com/rust-lang/rust/pull/137037) + To compile for pre-SSE2 32-bit x86, use a "i586" target instead. + + + +Internal Changes +---------------- + +These changes do not affect any public interfaces of Rust, but they represent +significant improvements to the performance or internals of rustc and related +tools. + +- [Build the rustc on AArch64 Linux with ThinLTO + PGO.](https://github.com/rust-lang/rust/pull/133807) + The ARM 64-bit compiler (AArch64) on Linux is now optimized with ThinLTO and PGO, similar to the optimizations we have already performed for the x86-64 compiler on Linux. This should make it up to 30% faster. + + +Version 1.85.1 (2025-03-18) +========================== + + +- [Fix the doctest-merging feature of the 2024 Edition.](https://github.com/rust-lang/rust/pull/137899/) +- [Relax some `target_feature` checks when generating docs.](https://github.com/rust-lang/rust/pull/137632/) +- [Fix errors in `std::fs::rename` on Windows 10, version 1607.](https://github.com/rust-lang/rust/pull/137528/) +- [Downgrade bootstrap `cc` to fix custom targets.](https://github.com/rust-lang/rust/pull/137460/) +- [Skip submodule updates when building Rust from a source tarball.](https://github.com/rust-lang/rust/pull/137338/) +Version 1.85.0 (2025-02-20) +========================== + +Language +-------- +- [The 2024 Edition is now stable.](https://github.com/rust-lang/rust/pull/133349) + See [the edition guide](https://doc.rust-lang.org/nightly/edition-guide/rust-2024/index.html) for more details. +- [Stabilize async closures](https://github.com/rust-lang/rust/pull/132706) + See [RFC 3668](https://rust-lang.github.io/rfcs/3668-async-closures.html) for more details. +- [Stabilize `#[diagnostic::do_not_recommend]`](https://github.com/rust-lang/rust/pull/132056) +- [Add `unpredictable_function_pointer_comparisons` lint to warn against function pointer comparisons](https://github.com/rust-lang/rust/pull/118833) +- [Lint on combining `#[no_mangle]` and `#[export_name]` attributes.](https://github.com/rust-lang/rust/pull/131558) + +Compiler +-------- +- [The unstable flag `-Zpolymorphize` has been removed](https://github.com/rust-lang/rust/pull/133883), see https://github.com/rust-lang/compiler-team/issues/810 for some background. + +Platform Support +---------------- +- [Promote `powerpc64le-unknown-linux-musl` to tier 2 with host tools](https://github.com/rust-lang/rust/pull/133801) +Refer to Rust's [platform support page][platform-support-doc] +for more information on Rust's tiered platform support. + +Libraries +--------- +- [Panics in the standard library now have a leading `library/` in their path](https://github.com/rust-lang/rust/pull/132390) +- [`std::env::home_dir()` on Windows now ignores the non-standard `$HOME` environment variable](https://github.com/rust-lang/rust/pull/132515) + It will be un-deprecated in a subsequent release. +- [Add `AsyncFn*` to the prelude in all editions.](https://github.com/rust-lang/rust/pull/132611) + +Stabilized APIs +--------------- +- [`BuildHasherDefault::new`](https://doc.rust-lang.org/stable/std/hash/struct.BuildHasherDefault.html#method.new) +- [`ptr::fn_addr_eq`](https://doc.rust-lang.org/std/ptr/fn.fn_addr_eq.html) +- [`io::ErrorKind::QuotaExceeded`](https://doc.rust-lang.org/stable/std/io/enum.ErrorKind.html#variant.QuotaExceeded) +- [`io::ErrorKind::CrossesDevices`](https://doc.rust-lang.org/stable/std/io/enum.ErrorKind.html#variant.CrossesDevices) +- [`{float}::midpoint`](https://doc.rust-lang.org/core/primitive.f32.html#method.midpoint) +- [Unsigned `{integer}::midpoint`](https://doc.rust-lang.org/std/primitive.u64.html#method.midpoint) +- [`NonZeroU*::midpoint`](https://doc.rust-lang.org/std/num/type.NonZeroU32.html#method.midpoint) +- [impl `std::iter::Extend` for tuples with arity 1 through 12](https://doc.rust-lang.org/stable/std/iter/trait.Extend.html#impl-Extend%3C(A,)%3E-for-(EA,)) +- [`FromIterator<(A, ...)>` for tuples with arity 1 through 12](https://doc.rust-lang.org/stable/std/iter/trait.FromIterator.html#impl-FromIterator%3C(EA,)%3E-for-(A,)) +- [`std::task::Waker::noop`](https://doc.rust-lang.org/stable/std/task/struct.Waker.html#method.noop) +These APIs are now stable in const contexts: +- [`mem::size_of_val`](https://doc.rust-lang.org/stable/std/mem/fn.size_of_val.html) +- [`mem::align_of_val`](https://doc.rust-lang.org/stable/std/mem/fn.align_of_val.html) +- [`Layout::for_value`](https://doc.rust-lang.org/stable/std/alloc/struct.Layout.html#method.for_value) +- [`Layout::align_to`](https://doc.rust-lang.org/stable/std/alloc/struct.Layout.html#method.align_to) +- [`Layout::pad_to_align`](https://doc.rust-lang.org/stable/std/alloc/struct.Layout.html#method.pad_to_align) +- [`Layout::extend`](https://doc.rust-lang.org/stable/std/alloc/struct.Layout.html#method.extend) +- [`Layout::array`](https://doc.rust-lang.org/stable/std/alloc/struct.Layout.html#method.array) +- [`std::mem::swap`](https://doc.rust-lang.org/stable/std/mem/fn.swap.html) +- [`std::ptr::swap`](https://doc.rust-lang.org/stable/std/ptr/fn.swap.html) +- [`NonNull::new`](https://doc.rust-lang.org/stable/std/ptr/struct.NonNull.html#method.new) +- [`HashMap::with_hasher`](https://doc.rust-lang.org/stable/std/collections/struct.HashMap.html#method.with_hasher) +- [`HashSet::with_hasher`](https://doc.rust-lang.org/stable/std/collections/struct.HashSet.html#method.with_hasher) +- [`BuildHasherDefault::new`](https://doc.rust-lang.org/stable/std/hash/struct.BuildHasherDefault.html#method.new) +- [`::recip`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.recip) +- [`::to_degrees`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.to_degrees) +- [`::to_radians`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.to_radians) +- [`::max`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.max) +- [`::min`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.min) +- [`::clamp`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.clamp) +- [`::abs`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.abs) +- [`::signum`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.signum) +- [`::copysign`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.copysign) +- [`MaybeUninit::write`](https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.write) + +Cargo +----- +- [Add future-incompatibility warning against keywords in cfgs and add raw-idents](https://github.com/rust-lang/cargo/pull/14671/) +- [Stabilize higher precedence trailing flags](https://github.com/rust-lang/cargo/pull/14900/) +- [Pass `CARGO_CFG_FEATURE` to build scripts](https://github.com/rust-lang/cargo/pull/14902/) + +Rustdoc +----- +- [Doc comment on impl blocks shows the first line, even when the impl block is collapsed](https://github.com/rust-lang/rust/pull/132155) + +Compatibility Notes +------------------- +- [`rustc` no longer treats the `test` cfg as a well known check-cfg](https://github.com/rust-lang/rust/pull/131729), instead it is up to the build systems and users of `--check-cfg`[^check-cfg] to set it as a well known cfg using `--check-cfg=cfg(test)`. + This is done to enable build systems like Cargo to set it conditionally, as not all source files are suitable for unit tests. + [Cargo (for now) unconditionally sets the `test` cfg as a well known cfg](https://github.com/rust-lang/cargo/pull/14963). + [^check-cfg]: https://doc.rust-lang.org/nightly/rustc/check-cfg.html +- [Disable potentially incorrect type inference if there are trivial and non-trivial where-clauses](https://github.com/rust-lang/rust/pull/132325) +- `std::env::home_dir()` has been deprecated for years, because it can give surprising results in some Windows configurations if the `HOME` environment variable is set (which is not the normal configuration on Windows). We had previously avoided changing its behavior, out of concern for compatibility with code depending on this non-standard configuration. Given how long this function has been deprecated, we're now fixing its behavior as a bugfix. A subsequent release will remove the deprecation for this function. +- [Make `core::ffi::c_char` signedness more closely match that of the platform-default `char`](https://github.com/rust-lang/rust/pull/132975) + This changed `c_char` from an `i8` to `u8` or vice versa on many Tier 2 and 3 + targets (mostly Arm and RISC-V embedded targets). The new definition may + result in compilation failures but fixes compatibility issues with C. + The `libc` crate matches this change as of its 0.2.169 release. +- [When compiling a nested `macro_rules` macro from an external crate, the content of the inner `macro_rules` is now built with the edition of the external crate, not the local crate.](https://github.com/rust-lang/rust/pull/133274) +- [Increase `sparcv9-sun-solaris` and `x86_64-pc-solaris` Solaris baseline to 11.4.](https://github.com/rust-lang/rust/pull/133293) +- [Show `abi_unsupported_vector_types` lint in future breakage reports](https://github.com/rust-lang/rust/pull/133374) +- [Error if multiple super-trait instantiations of `dyn Trait` need associated types to be specified but only one is provided](https://github.com/rust-lang/rust/pull/133392) +- [Change `powerpc64-ibm-aix` default `codemodel` to large](https://github.com/rust-lang/rust/pull/133811) + +Internal Changes +---------------- +These changes do not affect any public interfaces of Rust, but they represent +significant improvements to the performance or internals of rustc and related +tools. +- [Build `x86_64-unknown-linux-gnu` with LTO for C/C++ code (e.g., `jemalloc`)](https://github.com/rust-lang/rust/pull/134690) + +Version 1.84.1 (2025-01-30) +========================== + + + +- [Fix ICE 132920 in duplicate-crate diagnostics.](https://github.com/rust-lang/rust/pull/133304/) +- [Fix errors for overlapping impls in incremental rebuilds.](https://github.com/rust-lang/rust/pull/133828/) +- [Fix slow compilation related to the next-generation trait solver.](https://github.com/rust-lang/rust/pull/135618/) +- [Fix debuginfo when LLVM's location discriminator value limit is exceeded.](https://github.com/rust-lang/rust/pull/135643/) +- Fixes for building Rust from source: + - [Only try to distribute `llvm-objcopy` if llvm tools are enabled.](https://github.com/rust-lang/rust/pull/134240/) + - [Add Profile Override for Non-Git Sources.](https://github.com/rust-lang/rust/pull/135433/) + - [Resolve symlinks of LLVM tool binaries before copying them.](https://github.com/rust-lang/rust/pull/135585/) + - [Make it possible to use ci-rustc on tarball sources.](https://github.com/rust-lang/rust/pull/135722/) + +Version 1.84.0 (2025-01-09) +========================== + + + +Language +-------- +- [Allow `#[deny]` inside `#[forbid]` as a no-op](https://github.com/rust-lang/rust/pull/121560/) +- [Show a warning when `-Ctarget-feature` is used to toggle features that can lead to unsoundness due to ABI mismatches](https://github.com/rust-lang/rust/pull/129884) +- [Use the next-generation trait solver in coherence](https://github.com/rust-lang/rust/pull/130654) +- [Allow coercions to drop the principal of trait objects](https://github.com/rust-lang/rust/pull/131857) +- [Support `/` as the path separator for `include!()` in all cases on Windows](https://github.com/rust-lang/rust/pull/125205) +- [Taking a raw ref (`raw (const|mut)`) of a deref of a pointer (`*ptr`) is now safe](https://github.com/rust-lang/rust/pull/129248) +- [Stabilize s390x inline assembly](https://github.com/rust-lang/rust/pull/131258) +- [Stabilize Arm64EC inline assembly](https://github.com/rust-lang/rust/pull/131781) +- [Lint against creating pointers to immediately dropped temporaries](https://github.com/rust-lang/rust/pull/128985) +- [Execute drop glue when unwinding in an `extern "C"` function](https://github.com/rust-lang/rust/pull/129582) + + + +Compiler +-------- +- [Add `--print host-tuple` flag to print the host target tuple and affirm the "target tuple" terminology over "target triple"](https://github.com/rust-lang/rust/pull/125579) +- [Declaring functions with a calling convention not supported on the current target now triggers a hard error](https://github.com/rust-lang/rust/pull/129935) +- [Set up indirect access to external data for `loongarch64-unknown-linux-{musl,ohos}`](https://github.com/rust-lang/rust/pull/131583) +- [Enable XRay instrumentation for LoongArch Linux targets](https://github.com/rust-lang/rust/pull/131818) +- [Extend the `unexpected_cfgs` lint to also warn in external macros](https://github.com/rust-lang/rust/pull/132577) +- [Stabilize WebAssembly `multivalue`, `reference-types`, and `tail-call` target features](https://github.com/rust-lang/rust/pull/131080) +- [Added Tier 2 support for the `wasm32v1-none` target](https://github.com/rust-lang/rust/pull/131487) + + + +Libraries +--------- +- [Implement `From<&mut {slice}>` for `Box/Rc/Arc<{slice}>`](https://github.com/rust-lang/rust/pull/129329) +- [Move `::copysign`, `::abs`, `::signum` to `core`](https://github.com/rust-lang/rust/pull/131304) +- [Add `LowerExp` and `UpperExp` implementations to `NonZero`](https://github.com/rust-lang/rust/pull/131377) +- [Implement `FromStr` for `CString` and `TryFrom` for `String`](https://github.com/rust-lang/rust/pull/130608) +- [`std::os::darwin` has been made public](https://github.com/rust-lang/rust/pull/123723) + + + +Stabilized APIs +--------------- + +- [`Ipv6Addr::is_unique_local`](https://doc.rust-lang.org/stable/core/net/struct.Ipv6Addr.html#method.is_unique_local) +- [`Ipv6Addr::is_unicast_link_local`](https://doc.rust-lang.org/stable/core/net/struct.Ipv6Addr.html#method.is_unicast_link_local) +- [`core::ptr::with_exposed_provenance`](https://doc.rust-lang.org/stable/core/ptr/fn.with_exposed_provenance.html) +- [`core::ptr::with_exposed_provenance_mut`](https://doc.rust-lang.org/stable/core/ptr/fn.with_exposed_provenance_mut.html) +- [`::addr`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.addr) +- [`::expose_provenance`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.expose_provenance) +- [`::with_addr`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.with_addr) +- [`::map_addr`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.map_addr) +- [`::isqrt`](https://doc.rust-lang.org/stable/core/primitive.i32.html#method.isqrt) +- [`::checked_isqrt`](https://doc.rust-lang.org/stable/core/primitive.i32.html#method.checked_isqrt) +- [`::isqrt`](https://doc.rust-lang.org/stable/core/primitive.u32.html#method.isqrt) +- [`NonZero::isqrt`](https://doc.rust-lang.org/stable/core/num/struct.NonZero.html#impl-NonZero%3Cu128%3E/method.isqrt) +- [`core::ptr::without_provenance`](https://doc.rust-lang.org/stable/core/ptr/fn.without_provenance.html) +- [`core::ptr::without_provenance_mut`](https://doc.rust-lang.org/stable/core/ptr/fn.without_provenance_mut.html) +- [`core::ptr::dangling`](https://doc.rust-lang.org/stable/core/ptr/fn.dangling.html) +- [`core::ptr::dangling_mut`](https://doc.rust-lang.org/stable/core/ptr/fn.dangling_mut.html) +- [`Pin::as_deref_mut`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.as_deref_mut) + +These APIs are now stable in const contexts + +- [`AtomicBool::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicBool.html#method.from_ptr) +- [`AtomicPtr::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicPtr.html#method.from_ptr) +- [`AtomicU8::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicU8.html#method.from_ptr) +- [`AtomicU16::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicU16.html#method.from_ptr) +- [`AtomicU32::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicU32.html#method.from_ptr) +- [`AtomicU64::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicU64.html#method.from_ptr) +- [`AtomicUsize::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicUsize.html#method.from_ptr) +- [`AtomicI8::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicI8.html#method.from_ptr) +- [`AtomicI16::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicI16.html#method.from_ptr) +- [`AtomicI32::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicI32.html#method.from_ptr) +- [`AtomicI64::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicI64.html#method.from_ptr) +- [`AtomicIsize::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicIsize.html#method.from_ptr) +- [`::is_null`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.is_null-1) +- [`::as_ref`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.as_ref-1) +- [`::as_mut`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.as_mut) +- [`Pin::new`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.new) +- [`Pin::new_unchecked`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.new_unchecked) +- [`Pin::get_ref`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.get_ref) +- [`Pin::into_ref`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.into_ref) +- [`Pin::get_mut`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.get_mut) +- [`Pin::get_unchecked_mut`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.get_unchecked_mut) +- [`Pin::static_ref`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.static_ref) +- [`Pin::static_mut`](https://doc.rust-lang.org/stable/core/pin/struct.Pin.html#method.static_mut) + + + +Cargo +----- +- [Stabilize MSRV-aware resolver config](https://github.com/rust-lang/cargo/pull/14639/) +- [Stabilize resolver v3](https://github.com/rust-lang/cargo/pull/14754/) + + + +Rustdoc +------- + +- [rustdoc-search: improve type-driven search](https://github.com/rust-lang/rust/pull/127589) + + + +Compatibility Notes +------------------- +- [Enable by default the `LSX` target feature for LoongArch Linux targets](https://github.com/rust-lang/rust/pull/132140) +- [The unstable `-Zprofile` flag (“gcov-style” coverage instrumentation) has been removed.](https://github.com/rust-lang/rust/pull/131829) This does not affect the stable flags for coverage instrumentation (`-Cinstrument-coverage`) and profile-guided optimization (`-Cprofile-generate`, `-Cprofile-use`), which are unrelated and remain available. +- Support for the target named `wasm32-wasi` has been removed as the target is now named `wasm32-wasip1`. This completes the [transition](https://github.com/rust-lang/compiler-team/issues/607) [plan](https://github.com/rust-lang/compiler-team/issues/695) for this target following [the introduction of `wasm32-wasip1`](https://github.com/rust-lang/rust/pull/120468) in Rust 1.78. Compiler warnings on [use of `wasm32-wasi`](https://github.com/rust-lang/rust/pull/126662) introduced in Rust 1.81 are now gone as well as the target is removed. +- [The syntax `&pin (mut|const) T` is now parsed as a type which in theory could affect macro expansion results in some edge cases](https://github.com/rust-lang/rust/pull/130635#issuecomment-2375462821) +- [Legacy syntax for calling `std::arch` functions is no longer permitted to declare items or bodies (such as closures, inline consts, or async blocks).](https://github.com/rust-lang/rust/pull/130443#issuecomment-2445678945) +- [Declaring functions with a calling convention not supported on the current target now triggers a hard error](https://github.com/rust-lang/rust/pull/129935) +- [The next-generation trait solver is now enabled for coherence, fixing multiple soundness issues](https://github.com/rust-lang/rust/pull/130654) + Version 1.83.0 (2024-11-28) ========================== @@ -243,7 +608,7 @@ Language - [`addr_of(_mut)!` macros and the newly stabilized `&raw (const|mut)` are now safe to use with all static items](https://github.com/rust-lang/rust/pull/125834) - [size_of_val_raw: for length 0 this is safe to call](https://github.com/rust-lang/rust/pull/126152/) - [Reorder trait bound modifiers *after* `for<...>` binder in trait bounds](https://github.com/rust-lang/rust/pull/127054/) -- [Stabilize opaque type precise capturing (RFC 3617)](https://github.com/rust-lang/rust/pull/127672) +- [Stabilize `+ use<'lt>` opaque type precise capturing (RFC 3617)](https://github.com/rust-lang/rust/pull/127672) - [Stabilize `&raw const` and `&raw mut` operators (RFC 2582)](https://github.com/rust-lang/rust/pull/127679) - [Stabilize unsafe extern blocks (RFC 3484)](https://github.com/rust-lang/rust/pull/127921) - [Stabilize nested field access in `offset_of!`](https://github.com/rust-lang/rust/pull/128284) @@ -457,6 +822,7 @@ Libraries - [Replace sort implementations with stable `driftsort` and unstable `ipnsort`.](https://github.com/rust-lang/rust/pull/124032/) All `slice::sort*` and `slice::select_nth*` methods are expected to see significant performance improvements. See the [research project](https://github.com/Voultapher/sort-research-rs) for more details. - [Document behavior of `create_dir_all` with respect to empty paths.](https://github.com/rust-lang/rust/pull/125112/) - [Fix interleaved output in the default panic hook when multiple threads panic simultaneously.](https://github.com/rust-lang/rust/pull/127397/) +- Fix `Command`'s batch files argument escaping not working when file name has trailing whitespace or periods (CVE-2024-43402). @@ -2067,7 +2433,7 @@ Language -------- - [Stabilize default_alloc_error_handler](https://github.com/rust-lang/rust/pull/102318/) - This allows usage of `alloc` on stable without requiring the + This allows usage of `alloc` on stable without requiring the definition of a handler for allocation failure. Defining custom handlers is still unstable. - [Stabilize `efiapi` calling convention.](https://github.com/rust-lang/rust/pull/105795/) - [Remove implicit promotion for types with drop glue](https://github.com/rust-lang/rust/pull/105085/) diff --git a/REUSE.toml b/REUSE.toml index 6b16d97ed8068..9e873e94eff25 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -92,7 +92,7 @@ SPDX-FileCopyrightText = "2015 Anders Kaseorg " SPDX-License-Identifier = "MIT" [[annotations]] -path = "src/librustdoc/html/static/fonts/FiraSans**" +path = "src/librustdoc/html/static/fonts/Fira**" precedence = "override" SPDX-FileCopyrightText = ["2014, Mozilla Foundation", "2014, Telefonica S.A."] SPDX-License-Identifier = "OFL-1.1" diff --git a/compiler/rustc_abi/Cargo.toml b/compiler/rustc_abi/Cargo.toml index 3acd25e546114..1013f1d3958d8 100644 --- a/compiler/rustc_abi/Cargo.toml +++ b/compiler/rustc_abi/Cargo.toml @@ -9,7 +9,6 @@ bitflags = "2.4.1" rand = { version = "0.8.4", default-features = false, optional = true } rand_xoshiro = { version = "0.6.0", optional = true } rustc_data_structures = { path = "../rustc_data_structures", optional = true } -rustc_feature = { path = "../rustc_feature", optional = true } rustc_index = { path = "../rustc_index", default-features = false } rustc_macros = { path = "../rustc_macros", optional = true } rustc_serialize = { path = "../rustc_serialize", optional = true } @@ -24,7 +23,6 @@ default = ["nightly", "randomize"] # without depending on rustc_data_structures, rustc_macros and rustc_serialize nightly = [ "dep:rustc_data_structures", - "dep:rustc_feature", "dep:rustc_macros", "dep:rustc_serialize", "dep:rustc_span", diff --git a/compiler/rustc_abi/src/callconv.rs b/compiler/rustc_abi/src/callconv.rs index 400395f99ff01..9fb70b80c9efb 100644 --- a/compiler/rustc_abi/src/callconv.rs +++ b/compiler/rustc_abi/src/callconv.rs @@ -1,73 +1,9 @@ -mod abi { - pub(crate) use crate::Primitive::*; - pub(crate) use crate::Variants; -} - -#[cfg(feature = "nightly")] -use rustc_macros::HashStable_Generic; - -use crate::{Align, HasDataLayout, Size}; #[cfg(feature = "nightly")] -use crate::{BackendRepr, FieldsShape, TyAbiInterface, TyAndLayout}; - -#[cfg_attr(feature = "nightly", derive(HashStable_Generic))] -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub enum RegKind { - Integer, - Float, - Vector, -} +use crate::{BackendRepr, FieldsShape, Primitive, Size, TyAbiInterface, TyAndLayout, Variants}; -#[cfg_attr(feature = "nightly", derive(HashStable_Generic))] -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct Reg { - pub kind: RegKind, - pub size: Size, -} +mod reg; -macro_rules! reg_ctor { - ($name:ident, $kind:ident, $bits:expr) => { - pub fn $name() -> Reg { - Reg { kind: RegKind::$kind, size: Size::from_bits($bits) } - } - }; -} - -impl Reg { - reg_ctor!(i8, Integer, 8); - reg_ctor!(i16, Integer, 16); - reg_ctor!(i32, Integer, 32); - reg_ctor!(i64, Integer, 64); - reg_ctor!(i128, Integer, 128); - - reg_ctor!(f32, Float, 32); - reg_ctor!(f64, Float, 64); -} - -impl Reg { - pub fn align(&self, cx: &C) -> Align { - let dl = cx.data_layout(); - match self.kind { - RegKind::Integer => match self.size.bits() { - 1 => dl.i1_align.abi, - 2..=8 => dl.i8_align.abi, - 9..=16 => dl.i16_align.abi, - 17..=32 => dl.i32_align.abi, - 33..=64 => dl.i64_align.abi, - 65..=128 => dl.i128_align.abi, - _ => panic!("unsupported integer: {self:?}"), - }, - RegKind::Float => match self.size.bits() { - 16 => dl.f16_align.abi, - 32 => dl.f32_align.abi, - 64 => dl.f64_align.abi, - 128 => dl.f128_align.abi, - _ => panic!("unsupported float: {self:?}"), - }, - RegKind::Vector => dl.vector_align(self.size).abi, - } - } -} +pub use reg::{Reg, RegKind}; /// Return value from the `homogeneous_aggregate` test function. #[derive(Copy, Clone, Debug)] @@ -134,8 +70,8 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { // The primitive for this algorithm. BackendRepr::Scalar(scalar) => { let kind = match scalar.primitive() { - abi::Int(..) | abi::Pointer(_) => RegKind::Integer, - abi::Float(_) => RegKind::Float, + Primitive::Int(..) | Primitive::Pointer(_) => RegKind::Integer, + Primitive::Float(_) => RegKind::Float, }; Ok(HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size })) } @@ -206,8 +142,8 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { let (mut result, mut total) = from_fields_at(*self, Size::ZERO)?; match &self.variants { - abi::Variants::Single { .. } | abi::Variants::Empty => {} - abi::Variants::Multiple { variants, .. } => { + Variants::Single { .. } | Variants::Empty => {} + Variants::Multiple { variants, .. } => { // Treat enum variants like union members. // HACK(eddyb) pretend the `enum` field (discriminant) // is at the start of every variant (otherwise the gap diff --git a/compiler/rustc_abi/src/callconv/reg.rs b/compiler/rustc_abi/src/callconv/reg.rs new file mode 100644 index 0000000000000..66f47c52c15d1 --- /dev/null +++ b/compiler/rustc_abi/src/callconv/reg.rs @@ -0,0 +1,63 @@ +#[cfg(feature = "nightly")] +use rustc_macros::HashStable_Generic; + +use crate::{Align, HasDataLayout, Size}; + +#[cfg_attr(feature = "nightly", derive(HashStable_Generic))] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum RegKind { + Integer, + Float, + Vector, +} + +#[cfg_attr(feature = "nightly", derive(HashStable_Generic))] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct Reg { + pub kind: RegKind, + pub size: Size, +} + +macro_rules! reg_ctor { + ($name:ident, $kind:ident, $bits:expr) => { + pub fn $name() -> Reg { + Reg { kind: RegKind::$kind, size: Size::from_bits($bits) } + } + }; +} + +impl Reg { + reg_ctor!(i8, Integer, 8); + reg_ctor!(i16, Integer, 16); + reg_ctor!(i32, Integer, 32); + reg_ctor!(i64, Integer, 64); + reg_ctor!(i128, Integer, 128); + + reg_ctor!(f32, Float, 32); + reg_ctor!(f64, Float, 64); +} + +impl Reg { + pub fn align(&self, cx: &C) -> Align { + let dl = cx.data_layout(); + match self.kind { + RegKind::Integer => match self.size.bits() { + 1 => dl.i1_align.abi, + 2..=8 => dl.i8_align.abi, + 9..=16 => dl.i16_align.abi, + 17..=32 => dl.i32_align.abi, + 33..=64 => dl.i64_align.abi, + 65..=128 => dl.i128_align.abi, + _ => panic!("unsupported integer: {self:?}"), + }, + RegKind::Float => match self.size.bits() { + 16 => dl.f16_align.abi, + 32 => dl.f32_align.abi, + 64 => dl.f64_align.abi, + 128 => dl.f128_align.abi, + _ => panic!("unsupported float: {self:?}"), + }, + RegKind::Vector => dl.vector_align(self.size).abi, + } + } +} diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs new file mode 100644 index 0000000000000..543c2f8ab12cf --- /dev/null +++ b/compiler/rustc_abi/src/extern_abi.rs @@ -0,0 +1,232 @@ +use std::cmp::Ordering; +use std::fmt; +use std::hash::{Hash, Hasher}; + +#[cfg(feature = "nightly")] +use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd}; +#[cfg(feature = "nightly")] +use rustc_macros::{Decodable, Encodable}; + +#[cfg(test)] +mod tests; + +use ExternAbi as Abi; + +#[derive(Clone, Copy, Debug)] +#[cfg_attr(feature = "nightly", derive(Encodable, Decodable))] +pub enum ExternAbi { + // Some of the ABIs come first because every time we add a new ABI, we have to re-bless all the + // hashing tests. These are used in many places, so giving them stable values reduces test + // churn. The specific values are meaningless. + Rust, + C { + unwind: bool, + }, + Cdecl { + unwind: bool, + }, + Stdcall { + unwind: bool, + }, + Fastcall { + unwind: bool, + }, + Vectorcall { + unwind: bool, + }, + Thiscall { + unwind: bool, + }, + Aapcs { + unwind: bool, + }, + Win64 { + unwind: bool, + }, + SysV64 { + unwind: bool, + }, + PtxKernel, + Msp430Interrupt, + X86Interrupt, + /// An entry-point function called by the GPU's host + // FIXME: should not be callable from Rust on GPU targets, is for host's use only + GpuKernel, + EfiApi, + AvrInterrupt, + AvrNonBlockingInterrupt, + CCmseNonSecureCall, + CCmseNonSecureEntry, + System { + unwind: bool, + }, + RustIntrinsic, + RustCall, + /// *Not* a stable ABI, just directly use the Rust types to describe the ABI for LLVM. Even + /// normally ABI-compatible Rust types can become ABI-incompatible with this ABI! + Unadjusted, + /// For things unlikely to be called, where reducing register pressure in + /// `extern "Rust"` callers is worth paying extra cost in the callee. + /// Stronger than just `#[cold]` because `fn` pointers might be incompatible. + RustCold, + RiscvInterruptM, + RiscvInterruptS, +} + +macro_rules! abi_impls { + ($e_name:ident = { + $($variant:ident $({ unwind: $uw:literal })? =><= $tok:literal,)* + }) => { + impl $e_name { + pub const ALL_VARIANTS: &[Self] = &[ + $($e_name::$variant $({ unwind: $uw })*,)* + ]; + pub const fn as_str(&self) -> &'static str { + match self { + $($e_name::$variant $( { unwind: $uw } )* => $tok,)* + } + } + } + + impl ::core::str::FromStr for $e_name { + type Err = AbiFromStrErr; + fn from_str(s: &str) -> Result<$e_name, Self::Err> { + match s { + $($tok => Ok($e_name::$variant $({ unwind: $uw })*),)* + _ => Err(AbiFromStrErr::Unknown), + } + } + } + } +} + +#[derive(Debug)] +pub enum AbiFromStrErr { + Unknown, +} + +abi_impls! { + ExternAbi = { + C { unwind: false } =><= "C", + CCmseNonSecureCall =><= "C-cmse-nonsecure-call", + CCmseNonSecureEntry =><= "C-cmse-nonsecure-entry", + C { unwind: true } =><= "C-unwind", + Rust =><= "Rust", + Aapcs { unwind: false } =><= "aapcs", + Aapcs { unwind: true } =><= "aapcs-unwind", + AvrInterrupt =><= "avr-interrupt", + AvrNonBlockingInterrupt =><= "avr-non-blocking-interrupt", + Cdecl { unwind: false } =><= "cdecl", + Cdecl { unwind: true } =><= "cdecl-unwind", + EfiApi =><= "efiapi", + Fastcall { unwind: false } =><= "fastcall", + Fastcall { unwind: true } =><= "fastcall-unwind", + GpuKernel =><= "gpu-kernel", + Msp430Interrupt =><= "msp430-interrupt", + PtxKernel =><= "ptx-kernel", + RiscvInterruptM =><= "riscv-interrupt-m", + RiscvInterruptS =><= "riscv-interrupt-s", + RustCall =><= "rust-call", + RustCold =><= "rust-cold", + RustIntrinsic =><= "rust-intrinsic", + Stdcall { unwind: false } =><= "stdcall", + Stdcall { unwind: true } =><= "stdcall-unwind", + System { unwind: false } =><= "system", + System { unwind: true } =><= "system-unwind", + SysV64 { unwind: false } =><= "sysv64", + SysV64 { unwind: true } =><= "sysv64-unwind", + Thiscall { unwind: false } =><= "thiscall", + Thiscall { unwind: true } =><= "thiscall-unwind", + Unadjusted =><= "unadjusted", + Vectorcall { unwind: false } =><= "vectorcall", + Vectorcall { unwind: true } =><= "vectorcall-unwind", + Win64 { unwind: false } =><= "win64", + Win64 { unwind: true } =><= "win64-unwind", + X86Interrupt =><= "x86-interrupt", + } +} + +impl Ord for ExternAbi { + fn cmp(&self, rhs: &Self) -> Ordering { + self.as_str().cmp(rhs.as_str()) + } +} + +impl PartialOrd for ExternAbi { + fn partial_cmp(&self, rhs: &Self) -> Option { + Some(self.cmp(rhs)) + } +} + +impl PartialEq for ExternAbi { + fn eq(&self, rhs: &Self) -> bool { + self.cmp(rhs) == Ordering::Equal + } +} + +impl Eq for ExternAbi {} + +impl Hash for ExternAbi { + fn hash(&self, state: &mut H) { + self.as_str().hash(state); + // double-assurance of a prefix breaker + u32::from_be_bytes(*b"ABI\0").hash(state); + } +} + +#[cfg(feature = "nightly")] +impl HashStable for ExternAbi { + #[inline] + fn hash_stable(&self, _: &mut C, hasher: &mut StableHasher) { + Hash::hash(self, hasher); + } +} + +#[cfg(feature = "nightly")] +impl StableOrd for ExternAbi { + const CAN_USE_UNSTABLE_SORT: bool = true; + + // because each ABI is hashed like a string, there is no possible instability + const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); +} + +impl ExternAbi { + pub fn supports_varargs(self) -> bool { + // * C and Cdecl obviously support varargs. + // * C can be based on Aapcs, SysV64 or Win64, so they must support varargs. + // * EfiApi is based on Win64 or C, so it also supports it. + // + // * Stdcall does not, because it would be impossible for the callee to clean + // up the arguments. (callee doesn't know how many arguments are there) + // * Same for Fastcall, Vectorcall and Thiscall. + // * Other calling conventions are related to hardware or the compiler itself. + match self { + Self::C { .. } + | Self::Cdecl { .. } + | Self::Aapcs { .. } + | Self::Win64 { .. } + | Self::SysV64 { .. } + | Self::EfiApi => true, + _ => false, + } + } +} + +pub fn all_names() -> Vec<&'static str> { + ExternAbi::ALL_VARIANTS.iter().map(|abi| abi.as_str()).collect() +} + +impl ExternAbi { + /// Default ABI chosen for `extern fn` declarations without an explicit ABI. + pub const FALLBACK: Abi = Abi::C { unwind: false }; + + pub fn name(self) -> &'static str { + self.as_str() + } +} + +impl fmt::Display for ExternAbi { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "\"{}\"", self.as_str()) + } +} diff --git a/compiler/rustc_abi/src/extern_abi/mod.rs b/compiler/rustc_abi/src/extern_abi/mod.rs deleted file mode 100644 index 390f2dbc10fdf..0000000000000 --- a/compiler/rustc_abi/src/extern_abi/mod.rs +++ /dev/null @@ -1,334 +0,0 @@ -use std::fmt; - -use rustc_macros::{Decodable, Encodable, HashStable_Generic}; -use rustc_span::{Span, Symbol, sym}; - -#[cfg(test)] -mod tests; - -use ExternAbi as Abi; - -#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug)] -#[derive(HashStable_Generic, Encodable, Decodable)] -pub enum ExternAbi { - // Some of the ABIs come first because every time we add a new ABI, we have to re-bless all the - // hashing tests. These are used in many places, so giving them stable values reduces test - // churn. The specific values are meaningless. - Rust, - C { - unwind: bool, - }, - Cdecl { - unwind: bool, - }, - Stdcall { - unwind: bool, - }, - Fastcall { - unwind: bool, - }, - Vectorcall { - unwind: bool, - }, - Thiscall { - unwind: bool, - }, - Aapcs { - unwind: bool, - }, - Win64 { - unwind: bool, - }, - SysV64 { - unwind: bool, - }, - PtxKernel, - Msp430Interrupt, - X86Interrupt, - EfiApi, - AvrInterrupt, - AvrNonBlockingInterrupt, - CCmseNonSecureCall, - CCmseNonSecureEntry, - System { - unwind: bool, - }, - RustIntrinsic, - RustCall, - /// *Not* a stable ABI, just directly use the Rust types to describe the ABI for LLVM. Even - /// normally ABI-compatible Rust types can become ABI-incompatible with this ABI! - Unadjusted, - /// For things unlikely to be called, where reducing register pressure in - /// `extern "Rust"` callers is worth paying extra cost in the callee. - /// Stronger than just `#[cold]` because `fn` pointers might be incompatible. - RustCold, - RiscvInterruptM, - RiscvInterruptS, -} - -impl Abi { - pub fn supports_varargs(self) -> bool { - // * C and Cdecl obviously support varargs. - // * C can be based on Aapcs, SysV64 or Win64, so they must support varargs. - // * EfiApi is based on Win64 or C, so it also supports it. - // * System falls back to C for functions with varargs. - // - // * Stdcall does not, because it would be impossible for the callee to clean - // up the arguments. (callee doesn't know how many arguments are there) - // * Same for Fastcall, Vectorcall and Thiscall. - // * Other calling conventions are related to hardware or the compiler itself. - match self { - Self::C { .. } - | Self::Cdecl { .. } - | Self::System { .. } - | Self::Aapcs { .. } - | Self::Win64 { .. } - | Self::SysV64 { .. } - | Self::EfiApi => true, - _ => false, - } - } -} - -#[derive(Copy, Clone)] -pub struct AbiData { - abi: Abi, - - /// Name of this ABI as we like it called. - name: &'static str, -} - -#[allow(non_upper_case_globals)] -const AbiDatas: &[AbiData] = &[ - AbiData { abi: Abi::Rust, name: "Rust" }, - AbiData { abi: Abi::C { unwind: false }, name: "C" }, - AbiData { abi: Abi::C { unwind: true }, name: "C-unwind" }, - AbiData { abi: Abi::Cdecl { unwind: false }, name: "cdecl" }, - AbiData { abi: Abi::Cdecl { unwind: true }, name: "cdecl-unwind" }, - AbiData { abi: Abi::Stdcall { unwind: false }, name: "stdcall" }, - AbiData { abi: Abi::Stdcall { unwind: true }, name: "stdcall-unwind" }, - AbiData { abi: Abi::Fastcall { unwind: false }, name: "fastcall" }, - AbiData { abi: Abi::Fastcall { unwind: true }, name: "fastcall-unwind" }, - AbiData { abi: Abi::Vectorcall { unwind: false }, name: "vectorcall" }, - AbiData { abi: Abi::Vectorcall { unwind: true }, name: "vectorcall-unwind" }, - AbiData { abi: Abi::Thiscall { unwind: false }, name: "thiscall" }, - AbiData { abi: Abi::Thiscall { unwind: true }, name: "thiscall-unwind" }, - AbiData { abi: Abi::Aapcs { unwind: false }, name: "aapcs" }, - AbiData { abi: Abi::Aapcs { unwind: true }, name: "aapcs-unwind" }, - AbiData { abi: Abi::Win64 { unwind: false }, name: "win64" }, - AbiData { abi: Abi::Win64 { unwind: true }, name: "win64-unwind" }, - AbiData { abi: Abi::SysV64 { unwind: false }, name: "sysv64" }, - AbiData { abi: Abi::SysV64 { unwind: true }, name: "sysv64-unwind" }, - AbiData { abi: Abi::PtxKernel, name: "ptx-kernel" }, - AbiData { abi: Abi::Msp430Interrupt, name: "msp430-interrupt" }, - AbiData { abi: Abi::X86Interrupt, name: "x86-interrupt" }, - AbiData { abi: Abi::EfiApi, name: "efiapi" }, - AbiData { abi: Abi::AvrInterrupt, name: "avr-interrupt" }, - AbiData { abi: Abi::AvrNonBlockingInterrupt, name: "avr-non-blocking-interrupt" }, - AbiData { abi: Abi::CCmseNonSecureCall, name: "C-cmse-nonsecure-call" }, - AbiData { abi: Abi::CCmseNonSecureEntry, name: "C-cmse-nonsecure-entry" }, - AbiData { abi: Abi::System { unwind: false }, name: "system" }, - AbiData { abi: Abi::System { unwind: true }, name: "system-unwind" }, - AbiData { abi: Abi::RustIntrinsic, name: "rust-intrinsic" }, - AbiData { abi: Abi::RustCall, name: "rust-call" }, - AbiData { abi: Abi::Unadjusted, name: "unadjusted" }, - AbiData { abi: Abi::RustCold, name: "rust-cold" }, - AbiData { abi: Abi::RiscvInterruptM, name: "riscv-interrupt-m" }, - AbiData { abi: Abi::RiscvInterruptS, name: "riscv-interrupt-s" }, -]; - -#[derive(Copy, Clone, Debug)] -pub enum AbiUnsupported { - Unrecognized, - Reason { explain: &'static str }, -} - -/// Returns the ABI with the given name (if any). -pub fn lookup(name: &str) -> Result { - AbiDatas.iter().find(|abi_data| name == abi_data.name).map(|&x| x.abi).ok_or_else(|| match name { - "riscv-interrupt" => AbiUnsupported::Reason { - explain: "please use one of riscv-interrupt-m or riscv-interrupt-s for machine- or supervisor-level interrupts, respectively", - }, - "riscv-interrupt-u" => AbiUnsupported::Reason { - explain: "user-mode interrupt handlers have been removed from LLVM pending standardization, see: https://reviews.llvm.org/D149314", - }, - "wasm" => AbiUnsupported::Reason { - explain: "non-standard wasm ABI is no longer supported", - }, - - _ => AbiUnsupported::Unrecognized, - - }) -} - -pub fn all_names() -> Vec<&'static str> { - AbiDatas.iter().map(|d| d.name).collect() -} - -pub fn enabled_names(features: &rustc_feature::Features, span: Span) -> Vec<&'static str> { - AbiDatas - .iter() - .map(|d| d.name) - .filter(|name| is_enabled(features, span, name).is_ok()) - .collect() -} - -pub enum AbiDisabled { - Unstable { feature: Symbol, explain: &'static str }, - Unrecognized, -} - -pub fn is_enabled( - features: &rustc_feature::Features, - span: Span, - name: &str, -) -> Result<(), AbiDisabled> { - let s = is_stable(name); - if let Err(AbiDisabled::Unstable { feature, .. }) = s { - if features.enabled(feature) || span.allows_unstable(feature) { - return Ok(()); - } - } - s -} - -pub fn is_stable(name: &str) -> Result<(), AbiDisabled> { - match name { - // Stable - "Rust" | "C" | "C-unwind" | "cdecl" | "cdecl-unwind" | "stdcall" | "stdcall-unwind" - | "fastcall" | "fastcall-unwind" | "aapcs" | "aapcs-unwind" | "win64" | "win64-unwind" - | "sysv64" | "sysv64-unwind" | "system" | "system-unwind" | "efiapi" | "thiscall" - | "thiscall-unwind" => Ok(()), - "rust-intrinsic" => Err(AbiDisabled::Unstable { - feature: sym::intrinsics, - explain: "intrinsics are subject to change", - }), - "vectorcall" => Err(AbiDisabled::Unstable { - feature: sym::abi_vectorcall, - explain: "vectorcall is experimental and subject to change", - }), - "vectorcall-unwind" => Err(AbiDisabled::Unstable { - feature: sym::abi_vectorcall, - explain: "vectorcall-unwind ABI is experimental and subject to change", - }), - "rust-call" => Err(AbiDisabled::Unstable { - feature: sym::unboxed_closures, - explain: "rust-call ABI is subject to change", - }), - "rust-cold" => Err(AbiDisabled::Unstable { - feature: sym::rust_cold_cc, - explain: "rust-cold is experimental and subject to change", - }), - "ptx-kernel" => Err(AbiDisabled::Unstable { - feature: sym::abi_ptx, - explain: "PTX ABIs are experimental and subject to change", - }), - "unadjusted" => Err(AbiDisabled::Unstable { - feature: sym::abi_unadjusted, - explain: "unadjusted ABI is an implementation detail and perma-unstable", - }), - "msp430-interrupt" => Err(AbiDisabled::Unstable { - feature: sym::abi_msp430_interrupt, - explain: "msp430-interrupt ABI is experimental and subject to change", - }), - "x86-interrupt" => Err(AbiDisabled::Unstable { - feature: sym::abi_x86_interrupt, - explain: "x86-interrupt ABI is experimental and subject to change", - }), - "avr-interrupt" | "avr-non-blocking-interrupt" => Err(AbiDisabled::Unstable { - feature: sym::abi_avr_interrupt, - explain: "avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change", - }), - "riscv-interrupt-m" | "riscv-interrupt-s" => Err(AbiDisabled::Unstable { - feature: sym::abi_riscv_interrupt, - explain: "riscv-interrupt ABIs are experimental and subject to change", - }), - "C-cmse-nonsecure-call" => Err(AbiDisabled::Unstable { - feature: sym::abi_c_cmse_nonsecure_call, - explain: "C-cmse-nonsecure-call ABI is experimental and subject to change", - }), - "C-cmse-nonsecure-entry" => Err(AbiDisabled::Unstable { - feature: sym::cmse_nonsecure_entry, - explain: "C-cmse-nonsecure-entry ABI is experimental and subject to change", - }), - _ => Err(AbiDisabled::Unrecognized), - } -} - -impl Abi { - /// Default ABI chosen for `extern fn` declarations without an explicit ABI. - pub const FALLBACK: Abi = Abi::C { unwind: false }; - - #[inline] - pub fn index(self) -> usize { - // N.B., this ordering MUST match the AbiDatas array above. - // (This is ensured by the test indices_are_correct().) - use Abi::*; - let i = match self { - // Cross-platform ABIs - Rust => 0, - C { unwind: false } => 1, - C { unwind: true } => 2, - // Platform-specific ABIs - Cdecl { unwind: false } => 3, - Cdecl { unwind: true } => 4, - Stdcall { unwind: false } => 5, - Stdcall { unwind: true } => 6, - Fastcall { unwind: false } => 7, - Fastcall { unwind: true } => 8, - Vectorcall { unwind: false } => 9, - Vectorcall { unwind: true } => 10, - Thiscall { unwind: false } => 11, - Thiscall { unwind: true } => 12, - Aapcs { unwind: false } => 13, - Aapcs { unwind: true } => 14, - Win64 { unwind: false } => 15, - Win64 { unwind: true } => 16, - SysV64 { unwind: false } => 17, - SysV64 { unwind: true } => 18, - PtxKernel => 19, - Msp430Interrupt => 20, - X86Interrupt => 21, - EfiApi => 22, - AvrInterrupt => 23, - AvrNonBlockingInterrupt => 24, - CCmseNonSecureCall => 25, - CCmseNonSecureEntry => 26, - // Cross-platform ABIs - System { unwind: false } => 27, - System { unwind: true } => 28, - RustIntrinsic => 29, - RustCall => 30, - Unadjusted => 31, - RustCold => 32, - RiscvInterruptM => 33, - RiscvInterruptS => 34, - }; - debug_assert!( - AbiDatas - .iter() - .enumerate() - .find(|(_, AbiData { abi, .. })| *abi == self) - .map(|(index, _)| index) - .expect("abi variant has associated data") - == i, - "Abi index did not match `AbiDatas` ordering" - ); - i - } - - #[inline] - pub fn data(self) -> &'static AbiData { - &AbiDatas[self.index()] - } - - pub fn name(self) -> &'static str { - self.data().name - } -} - -impl fmt::Display for Abi { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "\"{}\"", self.name()) - } -} diff --git a/compiler/rustc_abi/src/extern_abi/tests.rs b/compiler/rustc_abi/src/extern_abi/tests.rs index 4823058dd6970..fc546a6570f0a 100644 --- a/compiler/rustc_abi/src/extern_abi/tests.rs +++ b/compiler/rustc_abi/src/extern_abi/tests.rs @@ -1,29 +1,31 @@ use std::assert_matches::assert_matches; +use std::str::FromStr; use super::*; #[allow(non_snake_case)] #[test] fn lookup_Rust() { - let abi = lookup("Rust"); - assert!(abi.is_ok() && abi.unwrap().data().name == "Rust"); + let abi = ExternAbi::from_str("Rust"); + assert!(abi.is_ok() && abi.unwrap().as_str() == "Rust"); } #[test] fn lookup_cdecl() { - let abi = lookup("cdecl"); - assert!(abi.is_ok() && abi.unwrap().data().name == "cdecl"); + let abi = ExternAbi::from_str("cdecl"); + assert!(abi.is_ok() && abi.unwrap().as_str() == "cdecl"); } #[test] fn lookup_baz() { - let abi = lookup("baz"); - assert_matches!(abi, Err(AbiUnsupported::Unrecognized)); + let abi = ExternAbi::from_str("baz"); + assert_matches!(abi, Err(AbiFromStrErr::Unknown)); } #[test] -fn indices_are_correct() { - for (i, abi_data) in AbiDatas.iter().enumerate() { - assert_eq!(i, abi_data.abi.index()); - } +fn guarantee_lexicographic_ordering() { + let abis = ExternAbi::ALL_VARIANTS; + let mut sorted_abis = abis.to_vec(); + sorted_abis.sort_unstable(); + assert_eq!(abis, sorted_abis); } diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 226a46f605cc4..b8773f9ff38f6 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -119,6 +119,8 @@ impl LayoutCalculator { .chain(Niche::from_scalar(dl, Size::ZERO, a)) .max_by_key(|niche| niche.available(dl)); + let combined_seed = a.size(&self.cx).bytes().wrapping_add(b.size(&self.cx).bytes()); + LayoutData { variants: Variants::Single { index: VariantIdx::new(0) }, fields: FieldsShape::Arbitrary { @@ -131,6 +133,7 @@ impl LayoutCalculator { size, max_repr_align: None, unadjusted_abi_align: align.abi, + randomization_seed: combined_seed, } } @@ -223,6 +226,7 @@ impl LayoutCalculator { size: Size::ZERO, max_repr_align: None, unadjusted_abi_align: dl.i8_align.abi, + randomization_seed: 0, } } @@ -385,6 +389,11 @@ impl LayoutCalculator { return Err(LayoutCalculatorError::EmptyUnion); }; + let combined_seed = only_variant + .iter() + .map(|v| v.randomization_seed) + .fold(repr.field_shuffle_seed, |acc, seed| acc.wrapping_add(seed)); + Ok(LayoutData { variants: Variants::Single { index: only_variant_idx }, fields: FieldsShape::Union(union_field_count), @@ -394,6 +403,7 @@ impl LayoutCalculator { size: size.align_to(align.abi), max_repr_align, unadjusted_abi_align, + randomization_seed: combined_seed, }) } @@ -650,6 +660,11 @@ impl LayoutCalculator { BackendRepr::Memory { sized: true } }; + let combined_seed = variant_layouts + .iter() + .map(|v| v.randomization_seed) + .fold(repr.field_shuffle_seed, |acc, seed| acc.wrapping_add(seed)); + let layout = LayoutData { variants: Variants::Multiple { tag: niche_scalar, @@ -671,6 +686,7 @@ impl LayoutCalculator { align, max_repr_align, unadjusted_abi_align, + randomization_seed: combined_seed, }; Some(TmpLayout { layout, variants: variant_layouts }) @@ -961,6 +977,11 @@ impl LayoutCalculator { let largest_niche = Niche::from_scalar(dl, Size::ZERO, tag); + let combined_seed = layout_variants + .iter() + .map(|v| v.randomization_seed) + .fold(repr.field_shuffle_seed, |acc, seed| acc.wrapping_add(seed)); + let tagged_layout = LayoutData { variants: Variants::Multiple { tag, @@ -978,6 +999,7 @@ impl LayoutCalculator { size, max_repr_align, unadjusted_abi_align, + randomization_seed: combined_seed, }; let tagged_layout = TmpLayout { layout: tagged_layout, variants: layout_variants }; @@ -1030,12 +1052,15 @@ impl LayoutCalculator { let mut max_repr_align = repr.align; let mut inverse_memory_index: IndexVec = fields.indices().collect(); let optimize_field_order = !repr.inhibit_struct_field_reordering(); - if optimize_field_order && fields.len() > 1 { - let end = - if let StructKind::MaybeUnsized = kind { fields.len() - 1 } else { fields.len() }; - let optimizing = &mut inverse_memory_index.raw[..end]; - let fields_excluding_tail = &fields.raw[..end]; + let end = if let StructKind::MaybeUnsized = kind { fields.len() - 1 } else { fields.len() }; + let optimizing = &mut inverse_memory_index.raw[..end]; + let fields_excluding_tail = &fields.raw[..end]; + // unsizable tail fields are excluded so that we use the same seed for the sized and unsized layouts. + let field_seed = fields_excluding_tail + .iter() + .fold(0u64, |acc, f| acc.wrapping_add(f.randomization_seed)); + if optimize_field_order && fields.len() > 1 { // If `-Z randomize-layout` was enabled for the type definition we can shuffle // the field ordering to try and catch some code making assumptions about layouts // we don't guarantee. @@ -1046,8 +1071,9 @@ impl LayoutCalculator { use rand::seq::SliceRandom; // `ReprOptions.field_shuffle_seed` is a deterministic seed we can use to randomize field // ordering. - let mut rng = - rand_xoshiro::Xoshiro128StarStar::seed_from_u64(repr.field_shuffle_seed); + let mut rng = rand_xoshiro::Xoshiro128StarStar::seed_from_u64( + field_seed.wrapping_add(repr.field_shuffle_seed), + ); // Shuffle the ordering of the fields. optimizing.shuffle(&mut rng); @@ -1344,6 +1370,8 @@ impl LayoutCalculator { unadjusted_abi_align }; + let seed = field_seed.wrapping_add(repr.field_shuffle_seed); + Ok(LayoutData { variants: Variants::Single { index: VariantIdx::new(0) }, fields: FieldsShape::Arbitrary { offsets, memory_index }, @@ -1353,6 +1381,7 @@ impl LayoutCalculator { size, max_repr_align, unadjusted_abi_align, + randomization_seed: seed, }) } diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index d188750bfe100..221e990ae863b 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -1,13 +1,15 @@ use std::fmt; use std::ops::Deref; -use Float::*; -use Primitive::*; use rustc_data_structures::intern::Interned; use rustc_macros::HashStable_Generic; +use crate::{ + AbiAndPrefAlign, Align, BackendRepr, FieldsShape, Float, HasDataLayout, LayoutData, Niche, + PointeeInfo, Primitive, Scalar, Size, TargetDataLayout, Variants, +}; + // Explicitly import `Float` to avoid ambiguity with `Primitive::Float`. -use crate::{Float, *}; rustc_index::newtype_index! { /// The *source-order* index of a field in a variant. @@ -197,7 +199,9 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { C: HasDataLayout, { match self.backend_repr { - BackendRepr::Scalar(scalar) => matches!(scalar.primitive(), Float(F32 | F64)), + BackendRepr::Scalar(scalar) => { + matches!(scalar.primitive(), Primitive::Float(Float::F32 | Float::F64)) + } BackendRepr::Memory { .. } => { if self.fields.count() == 1 && self.fields.offset(0).bytes() == 0 { self.field(cx, 0).is_single_fp_element(cx) diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 7fa869a509cc3..da1c706d67cc4 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -52,23 +52,17 @@ use bitflags::bitflags; use rustc_data_structures::stable_hasher::StableOrd; use rustc_index::{Idx, IndexSlice, IndexVec}; #[cfg(feature = "nightly")] -use rustc_macros::HashStable_Generic; -#[cfg(feature = "nightly")] -use rustc_macros::{Decodable_Generic, Encodable_Generic}; +use rustc_macros::{Decodable_Generic, Encodable_Generic, HashStable_Generic}; mod callconv; mod layout; #[cfg(test)] mod tests; -#[cfg(feature = "nightly")] mod extern_abi; pub use callconv::{Heterogeneous, HomogeneousAggregate, Reg, RegKind}; -#[cfg(feature = "nightly")] -pub use extern_abi::{ - AbiDisabled, AbiUnsupported, ExternAbi, all_names, enabled_names, is_enabled, is_stable, lookup, -}; +pub use extern_abi::{ExternAbi, all_names}; #[cfg(feature = "nightly")] pub use layout::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx}; pub use layout::{LayoutCalculator, LayoutCalculatorError}; @@ -1184,10 +1178,13 @@ impl Scalar { #[inline] pub fn is_bool(&self) -> bool { use Integer::*; - matches!(self, Scalar::Initialized { - value: Primitive::Int(I8, false), - valid_range: WrappingRange { start: 0, end: 1 } - }) + matches!( + self, + Scalar::Initialized { + value: Primitive::Int(I8, false), + valid_range: WrappingRange { start: 0, end: 1 } + } + ) } /// Get the primitive representation of this type, ignoring the valid range and whether the @@ -1719,6 +1716,18 @@ pub struct LayoutData { /// Only used on aarch64-linux, where the argument passing ABI ignores the requested alignment /// in some cases. pub unadjusted_abi_align: Align, + + /// The randomization seed based on this type's own repr and its fields. + /// + /// Since randomization is toggled on a per-crate basis even crates that do not have randomization + /// enabled should still calculate a seed so that downstream uses can use it to distinguish different + /// types. + /// + /// For every T and U for which we do not guarantee that a repr(Rust) `Foo` can be coerced or + /// transmuted to `Foo` we aim to create probalistically distinct seeds so that Foo can choose + /// to reorder its fields based on that information. The current implementation is a conservative + /// approximation of this goal. + pub randomization_seed: u64, } impl LayoutData { @@ -1739,6 +1748,30 @@ impl LayoutData { let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar); let size = scalar.size(cx); let align = scalar.align(cx); + + let range = scalar.valid_range(cx); + + // All primitive types for which we don't have subtype coercions should get a distinct seed, + // so that types wrapping them can use randomization to arrive at distinct layouts. + // + // Some type information is already lost at this point, so as an approximation we derive + // the seed from what remains. For example on 64-bit targets usize and u64 can no longer + // be distinguished. + let randomization_seed = size + .bytes() + .wrapping_add( + match scalar.primitive() { + Primitive::Int(_, true) => 1, + Primitive::Int(_, false) => 2, + Primitive::Float(_) => 3, + Primitive::Pointer(_) => 4, + } << 32, + ) + // distinguishes references from pointers + .wrapping_add((range.start as u64).rotate_right(16)) + // distinguishes char from u32 and bool from u8 + .wrapping_add((range.end as u64).rotate_right(16)); + LayoutData { variants: Variants::Single { index: VariantIdx::new(0) }, fields: FieldsShape::Primitive, @@ -1748,6 +1781,7 @@ impl LayoutData { align, max_repr_align: None, unadjusted_abi_align: align.abi, + randomization_seed, } } } @@ -1770,6 +1804,7 @@ where variants, max_repr_align, unadjusted_abi_align, + ref randomization_seed, } = self; f.debug_struct("Layout") .field("size", size) @@ -1780,6 +1815,7 @@ where .field("variants", variants) .field("max_repr_align", max_repr_align) .field("unadjusted_abi_align", unadjusted_abi_align) + .field("randomization_seed", randomization_seed) .finish() } } diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index 4d8525989cc59..b21ccba93bb44 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -78,7 +78,7 @@ impl ArenaChunk { // been initialized. unsafe { let slice = self.storage.as_mut(); - ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(&mut slice[..len])); + slice[..len].assume_init_drop(); } } } diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 3a81b93d157ce..29c1d34a125a4 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -19,6 +19,7 @@ //! - [`UnOp`], [`BinOp`], and [`BinOpKind`]: Unary and binary operators. use std::borrow::Cow; +use std::sync::Arc; use std::{cmp, fmt}; pub use GenericArgs::*; @@ -27,7 +28,7 @@ pub use rustc_ast_ir::{Movability, Mutability, Pinnedness}; use rustc_data_structures::packed::Pu128; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_data_structures::sync::Lrc; +use rustc_data_structures::tagged_ptr::Tag; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; pub use rustc_span::AttrId; use rustc_span::source_map::{Spanned, respan}; @@ -99,7 +100,7 @@ pub struct Path { impl PartialEq for Path { #[inline] fn eq(&self, symbol: &Symbol) -> bool { - self.segments.len() == 1 && { self.segments[0].ident.name == *symbol } + matches!(&self.segments[..], [segment] if segment.ident.name == *symbol) } } @@ -120,13 +121,13 @@ impl Path { } pub fn is_global(&self) -> bool { - !self.segments.is_empty() && self.segments[0].ident.name == kw::PathRoot + self.segments.first().is_some_and(|segment| segment.ident.name == kw::PathRoot) } /// If this path is a single identifier with no arguments, does not ensure /// that the path resolves to a const param, the caller should check this. pub fn is_potential_trivial_const_arg(&self) -> bool { - self.segments.len() == 1 && self.segments[0].args.is_none() + matches!(self.segments[..], [PathSegment { args: None, .. }]) } } @@ -287,6 +288,7 @@ impl ParenthesizedArgs { } } +use crate::AstDeref; pub use crate::node_id::{CRATE_NODE_ID, DUMMY_NODE_ID, NodeId}; /// Modifiers on a trait bound like `~const`, `?` and `!`. @@ -623,7 +625,7 @@ impl Pat { PatKind::Wild | PatKind::Rest | PatKind::Never - | PatKind::Lit(_) + | PatKind::Expr(_) | PatKind::Range(..) | PatKind::Ident(..) | PatKind::Path(..) @@ -801,8 +803,8 @@ pub enum PatKind { /// A reference pattern (e.g., `&mut (a, b)`). Ref(P, Mutability), - /// A literal. - Lit(P), + /// A literal, const block or path. + Expr(P), /// A range pattern (e.g., `1...2`, `1..2`, `1..`, `..2`, `1..=2`, `..=2`). Range(Option>, Option>, Spanned), @@ -1609,7 +1611,7 @@ pub enum ExprKind { /// Added for optimization purposes to avoid the need to escape /// large binary blobs - should always behave like [`ExprKind::Lit`] /// with a `ByteStr` literal. - IncludedBytes(Lrc<[u8]>), + IncludedBytes(Arc<[u8]>), /// A `format_args!()` expression. FormatArgs(P), @@ -1655,7 +1657,7 @@ impl GenBlockKind { } /// Whether we're unwrapping or wrapping an unsafe binder -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(Encodable, Decodable, HashStable_Generic)] pub enum UnsafeBinderCastKind { // e.g. `&i32` -> `unsafe<'a> &'a i32` @@ -1902,9 +1904,9 @@ pub enum LitKind { Str(Symbol, StrStyle), /// A byte string (`b"foo"`). Not stored as a symbol because it might be /// non-utf8, and symbols only allow utf8 strings. - ByteStr(Lrc<[u8]>, StrStyle), + ByteStr(Arc<[u8]>, StrStyle), /// A C String (`c"foo"`). Guaranteed to only have `\0` at the end. - CStr(Lrc<[u8]>, StrStyle), + CStr(Arc<[u8]>, StrStyle), /// A byte char (`b'f'`). Byte(u8), /// A character literal (`'a'`). @@ -2165,6 +2167,14 @@ impl Ty { } final_ty } + + pub fn is_maybe_parenthesised_infer(&self) -> bool { + match &self.kind { + TyKind::Infer => true, + TyKind::Paren(inner) => inner.ast_deref().is_maybe_parenthesised_infer(), + _ => false, + } + } } #[derive(Clone, Encodable, Decodable, Debug)] @@ -2239,7 +2249,7 @@ pub enum TyKind { CVarArgs, /// Pattern types like `pattern_type!(u32 is 1..=)`, which is the same as `NonZero`, /// just as part of the type system. - Pat(P, P), + Pat(P, P), /// Sometimes we need a dummy value when no error has occurred. Dummy, /// Placeholder for a kind that has failed to be defined. @@ -2267,12 +2277,55 @@ impl TyKind { } } +/// A pattern type pattern. +#[derive(Clone, Encodable, Decodable, Debug)] +pub struct TyPat { + pub id: NodeId, + pub kind: TyPatKind, + pub span: Span, + pub tokens: Option, +} + +/// All the different flavors of pattern that Rust recognizes. +// +// Adding a new variant? Please update `test_pat` in `tests/ui/macros/stringify.rs`. +#[derive(Clone, Encodable, Decodable, Debug)] +pub enum TyPatKind { + /// A range pattern (e.g., `1...2`, `1..2`, `1..`, `..2`, `1..=2`, `..=2`). + Range(Option>, Option>, Spanned), + + /// Placeholder for a pattern that wasn't syntactically well formed in some way. + Err(ErrorGuaranteed), +} + /// Syntax used to declare a trait object. #[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] +#[repr(u8)] pub enum TraitObjectSyntax { - Dyn, - DynStar, - None, + // SAFETY: When adding new variants make sure to update the `Tag` impl. + Dyn = 0, + DynStar = 1, + None = 2, +} + +/// SAFETY: `TraitObjectSyntax` only has 3 data-less variants which means +/// it can be represented with a `u2`. We use `repr(u8)` to guarantee the +/// discriminants of the variants are no greater than `3`. +unsafe impl Tag for TraitObjectSyntax { + const BITS: u32 = 2; + + fn into_usize(self) -> usize { + self as u8 as usize + } + + unsafe fn from_usize(tag: usize) -> Self { + match tag { + 0 => TraitObjectSyntax::Dyn, + 1 => TraitObjectSyntax::DynStar, + 2 => TraitObjectSyntax::None, + _ => unreachable!(), + } + } } #[derive(Clone, Encodable, Decodable, Debug)] @@ -3193,7 +3246,7 @@ pub enum Extern { /// /// E.g. `extern fn foo() {}`. /// - /// This is just `extern "C"` (see `rustc_target::spec::abi::Abi::FALLBACK`). + /// This is just `extern "C"` (see `rustc_abi::ExternAbi::FALLBACK`). Implicit(Span), /// An explicit extern keyword was used with an explicit ABI. /// @@ -3316,11 +3369,18 @@ pub struct Impl { pub items: ThinVec>, } +#[derive(Clone, Encodable, Decodable, Debug, Default)] +pub struct FnContract { + pub requires: Option>, + pub ensures: Option>, +} + #[derive(Clone, Encodable, Decodable, Debug)] pub struct Fn { pub defaultness: Defaultness, pub generics: Generics, pub sig: FnSig, + pub contract: Option>, pub body: Option>, } @@ -3618,7 +3678,7 @@ mod size_asserts { static_assert_size!(Block, 32); static_assert_size!(Expr, 72); static_assert_size!(ExprKind, 40); - static_assert_size!(Fn, 160); + static_assert_size!(Fn, 168); static_assert_size!(ForeignItem, 88); static_assert_size!(ForeignItemKind, 16); static_assert_size!(GenericArg, 24); diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 97385b2eaab89..df2f4b8871249 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -302,7 +302,7 @@ impl AttrItem { impl MetaItem { /// For a single-segment meta item, returns its name; otherwise, returns `None`. pub fn ident(&self) -> Option { - if self.path.segments.len() == 1 { Some(self.path.segments[0].ident) } else { None } + if let [PathSegment { ident, .. }] = self.path.segments[..] { Some(ident) } else { None } } pub fn name_or_empty(&self) -> Symbol { @@ -723,6 +723,8 @@ impl MetaItemLit { pub trait AttributeExt: Debug { fn id(&self) -> AttrId; + /// For a single-segment attribute (i.e., `#[attr]` and not `#[path::atrr]`), + /// return the name of the attribute, else return the empty identifier. fn name_or_empty(&self) -> Symbol { self.ident().unwrap_or_else(Ident::empty).name } diff --git a/compiler/rustc_ast/src/entry.rs b/compiler/rustc_ast/src/entry.rs index ab1413d6080e5..12cbb3b2a15f7 100644 --- a/compiler/rustc_ast/src/entry.rs +++ b/compiler/rustc_ast/src/entry.rs @@ -18,12 +18,6 @@ pub enum EntryPointType { /// fn main() {} /// ``` RustcMainAttr, - /// This is a function with the `#[start]` attribute. - /// ```ignore (clashes with test entrypoint) - /// #[start] - /// fn main() {} - /// ``` - Start, /// This function is **not** an entrypoint but simply named `main` (not at the root). /// This is only used for diagnostics. /// ``` @@ -40,9 +34,7 @@ pub fn entry_point_type( at_root: bool, name: Option, ) -> EntryPointType { - if attr::contains_name(attrs, sym::start) { - EntryPointType::Start - } else if attr::contains_name(attrs, sym::rustc_main) { + if attr::contains_name(attrs, sym::rustc_main) { EntryPointType::RustcMainAttr } else if let Some(name) = name && name == sym::main diff --git a/compiler/rustc_ast/src/expand/autodiff_attrs.rs b/compiler/rustc_ast/src/expand/autodiff_attrs.rs index 7ef8bc1797384..70222f4acabe1 100644 --- a/compiler/rustc_ast/src/expand/autodiff_attrs.rs +++ b/compiler/rustc_ast/src/expand/autodiff_attrs.rs @@ -30,14 +30,6 @@ pub enum DiffMode { Forward, /// The target function, to be created using reverse mode AD. Reverse, - /// The target function, to be created using forward mode AD. - /// This target function will also be used as a source for higher order derivatives, - /// so compute it before all Forward/Reverse targets and optimize it through llvm. - ForwardFirst, - /// The target function, to be created using reverse mode AD. - /// This target function will also be used as a source for higher order derivatives, - /// so compute it before all Forward/Reverse targets and optimize it through llvm. - ReverseFirst, } /// Dual and Duplicated (and their Only variants) are getting lowered to the same Enzyme Activity. @@ -79,6 +71,7 @@ pub struct AutoDiffItem { pub target: String, pub attrs: AutoDiffAttrs, } + #[derive(Clone, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] pub struct AutoDiffAttrs { /// Conceptually either forward or reverse mode AD, as described in various autodiff papers and @@ -91,10 +84,10 @@ pub struct AutoDiffAttrs { impl DiffMode { pub fn is_rev(&self) -> bool { - matches!(self, DiffMode::Reverse | DiffMode::ReverseFirst) + matches!(self, DiffMode::Reverse) } pub fn is_fwd(&self) -> bool { - matches!(self, DiffMode::Forward | DiffMode::ForwardFirst) + matches!(self, DiffMode::Forward) } } @@ -105,8 +98,6 @@ impl Display for DiffMode { DiffMode::Source => write!(f, "Source"), DiffMode::Forward => write!(f, "Forward"), DiffMode::Reverse => write!(f, "Reverse"), - DiffMode::ForwardFirst => write!(f, "ForwardFirst"), - DiffMode::ReverseFirst => write!(f, "ReverseFirst"), } } } @@ -124,12 +115,12 @@ pub fn valid_ret_activity(mode: DiffMode, activity: DiffActivity) -> bool { match mode { DiffMode::Error => false, DiffMode::Source => false, - DiffMode::Forward | DiffMode::ForwardFirst => { + DiffMode::Forward => { activity == DiffActivity::Dual || activity == DiffActivity::DualOnly || activity == DiffActivity::Const } - DiffMode::Reverse | DiffMode::ReverseFirst => { + DiffMode::Reverse => { activity == DiffActivity::Const || activity == DiffActivity::Active || activity == DiffActivity::ActiveOnly @@ -165,10 +156,10 @@ pub fn valid_input_activity(mode: DiffMode, activity: DiffActivity) -> bool { return match mode { DiffMode::Error => false, DiffMode::Source => false, - DiffMode::Forward | DiffMode::ForwardFirst => { + DiffMode::Forward => { matches!(activity, Dual | DualOnly | Const) } - DiffMode::Reverse | DiffMode::ReverseFirst => { + DiffMode::Reverse => { matches!(activity, Active | ActiveOnly | Duplicated | DuplicatedOnly | Const) } }; @@ -199,8 +190,6 @@ impl FromStr for DiffMode { "Source" => Ok(DiffMode::Source), "Forward" => Ok(DiffMode::Forward), "Reverse" => Ok(DiffMode::Reverse), - "ForwardFirst" => Ok(DiffMode::ForwardFirst), - "ReverseFirst" => Ok(DiffMode::ReverseFirst), _ => Err(()), } } @@ -231,7 +220,7 @@ impl AutoDiffAttrs { self.ret_activity == DiffActivity::ActiveOnly } - pub fn error() -> Self { + pub const fn error() -> Self { AutoDiffAttrs { mode: DiffMode::Error, ret_activity: DiffActivity::None, diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 04cdfc93dcb15..de9f049704a4f 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -9,10 +9,10 @@ use std::ops::DerefMut; use std::panic; +use std::sync::Arc; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_data_structures::sync::Lrc; use rustc_span::source_map::Spanned; use rustc_span::{Ident, Span}; use smallvec::{Array, SmallVec, smallvec}; @@ -143,6 +143,10 @@ pub trait MutVisitor: Sized { walk_flat_map_assoc_item(self, i, ctxt) } + fn visit_contract(&mut self, c: &mut P) { + walk_contract(self, c); + } + fn visit_fn_decl(&mut self, d: &mut P) { walk_fn_decl(self, d); } @@ -206,6 +210,10 @@ pub trait MutVisitor: Sized { walk_ty(self, t); } + fn visit_ty_pat(&mut self, t: &mut P) { + walk_ty_pat(self, t); + } + fn visit_lifetime(&mut self, l: &mut Lifetime) { walk_lifetime(self, l); } @@ -566,7 +574,7 @@ pub fn walk_ty(vis: &mut T, ty: &mut P) { TyKind::Paren(ty) => vis.visit_ty(ty), TyKind::Pat(ty, pat) => { vis.visit_ty(ty); - vis.visit_pat(pat); + vis.visit_ty_pat(pat); } TyKind::Path(qself, path) => { vis.visit_qself(qself); @@ -590,6 +598,20 @@ pub fn walk_ty(vis: &mut T, ty: &mut P) { vis.visit_span(span); } +pub fn walk_ty_pat(vis: &mut T, ty: &mut P) { + let TyPat { id, kind, span, tokens } = ty.deref_mut(); + vis.visit_id(id); + match kind { + TyPatKind::Range(start, end, _include_end) => { + visit_opt(start, |c| vis.visit_anon_const(c)); + visit_opt(end, |c| vis.visit_anon_const(c)); + } + TyPatKind::Err(_) => {} + } + visit_lazy_tts(vis, tokens); + vis.visit_span(span); +} + fn walk_foreign_mod(vis: &mut T, foreign_mod: &mut ForeignMod) { let ForeignMod { extern_span: _, safety, abi: _, items } = foreign_mod; visit_safety(vis, safety); @@ -789,14 +811,14 @@ fn visit_tt(vis: &mut T, tt: &mut TokenTree) { // No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. fn visit_tts(vis: &mut T, TokenStream(tts): &mut TokenStream) { if T::VISIT_TOKENS && !tts.is_empty() { - let tts = Lrc::make_mut(tts); + let tts = Arc::make_mut(tts); visit_vec(tts, |tree| visit_tt(vis, tree)); } } fn visit_attr_tts(vis: &mut T, AttrTokenStream(tts): &mut AttrTokenStream) { if T::VISIT_TOKENS && !tts.is_empty() { - let tts = Lrc::make_mut(tts); + let tts = Arc::make_mut(tts); visit_vec(tts, |tree| visit_attr_tt(vis, tree)); } } @@ -836,7 +858,7 @@ pub fn visit_token(vis: &mut T, t: &mut Token) { vis.visit_ident(ident); } token::Interpolated(nt) => { - let nt = Lrc::make_mut(nt); + let nt = Arc::make_mut(nt); visit_nonterminal(vis, nt); } _ => {} @@ -954,11 +976,20 @@ fn walk_coroutine_kind(vis: &mut T, coroutine_kind: &mut Coroutin fn walk_fn(vis: &mut T, kind: FnKind<'_>) { match kind { - FnKind::Fn(_ctxt, _ident, FnSig { header, decl, span }, _visibility, generics, body) => { + FnKind::Fn( + _ctxt, + _ident, + _vis, + Fn { defaultness, generics, contract, body, sig: FnSig { header, decl, span } }, + ) => { // Identifier and visibility are visited as a part of the item. + visit_defaultness(vis, defaultness); vis.visit_fn_header(header); vis.visit_generics(generics); vis.visit_fn_decl(decl); + if let Some(contract) = contract { + vis.visit_contract(contract); + } if let Some(body) = body { vis.visit_block(body); } @@ -973,6 +1004,16 @@ fn walk_fn(vis: &mut T, kind: FnKind<'_>) { } } +fn walk_contract(vis: &mut T, contract: &mut P) { + let FnContract { requires, ensures } = contract.deref_mut(); + if let Some(pred) = requires { + vis.visit_expr(pred); + } + if let Some(pred) = ensures { + vis.visit_expr(pred); + } +} + fn walk_fn_decl(vis: &mut T, decl: &mut P) { let FnDecl { inputs, output } = decl.deref_mut(); inputs.flat_map_in_place(|param| vis.flat_map_param(param)); @@ -1205,13 +1246,8 @@ impl WalkItemKind for ItemKind { ItemKind::Const(item) => { visit_const_item(item, vis); } - ItemKind::Fn(box Fn { defaultness, generics, sig, body }) => { - visit_defaultness(vis, defaultness); - vis.visit_fn( - FnKind::Fn(FnCtxt::Free, ident, sig, visibility, generics, body), - span, - id, - ); + ItemKind::Fn(func) => { + vis.visit_fn(FnKind::Fn(FnCtxt::Free, ident, visibility, &mut *func), span, id); } ItemKind::Mod(safety, mod_kind) => { visit_safety(vis, safety); @@ -1329,10 +1365,9 @@ impl WalkItemKind for AssocItemKind { AssocItemKind::Const(item) => { visit_const_item(item, visitor); } - AssocItemKind::Fn(box Fn { defaultness, generics, sig, body }) => { - visit_defaultness(visitor, defaultness); + AssocItemKind::Fn(func) => { visitor.visit_fn( - FnKind::Fn(FnCtxt::Assoc(ctxt), ident, sig, visibility, generics, body), + FnKind::Fn(FnCtxt::Assoc(ctxt), ident, visibility, &mut *func), span, id, ); @@ -1476,10 +1511,9 @@ impl WalkItemKind for ForeignItemKind { visitor.visit_ty(ty); visit_opt(expr, |expr| visitor.visit_expr(expr)); } - ForeignItemKind::Fn(box Fn { defaultness, generics, sig, body }) => { - visit_defaultness(visitor, defaultness); + ForeignItemKind::Fn(func) => { visitor.visit_fn( - FnKind::Fn(FnCtxt::Foreign, ident, sig, visibility, generics, body), + FnKind::Fn(FnCtxt::Foreign, ident, visibility, &mut *func), span, id, ); @@ -1512,7 +1546,7 @@ pub fn walk_pat(vis: &mut T, pat: &mut P) { vis.visit_ident(ident); visit_opt(sub, |sub| vis.visit_pat(sub)); } - PatKind::Lit(e) => vis.visit_expr(e), + PatKind::Expr(e) => vis.visit_expr(e), PatKind::TupleStruct(qself, path, elems) => { vis.visit_qself(qself); vis.visit_path(path); @@ -1813,10 +1847,10 @@ pub fn walk_flat_map_stmt( .into_iter() .map(|kind| Stmt { id, kind, span }) .collect(); - match stmts.len() { - 0 => {} - 1 => vis.visit_span(&mut stmts[0].span), - 2.. => panic!( + match &mut stmts[..] { + [] => {} + [stmt] => vis.visit_span(&mut stmt.span), + _ => panic!( "cloning statement `NodeId`s is prohibited by default, \ the visitor should implement custom statement visiting" ), @@ -1965,14 +1999,7 @@ impl DummyAstNode for crate::ast_traits::AstNo #[derive(Debug)] pub enum FnKind<'a> { /// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`. - Fn( - FnCtxt, - &'a mut Ident, - &'a mut FnSig, - &'a mut Visibility, - &'a mut Generics, - &'a mut Option>, - ), + Fn(FnCtxt, &'a mut Ident, &'a mut Visibility, &'a mut Fn), /// E.g., `|x, y| body`. Closure( diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index f639e785bc4f4..36a7b7d87892f 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; use std::fmt; +use std::sync::Arc; pub use BinOpToken::*; pub use LitKind::*; @@ -8,7 +9,6 @@ pub use NtExprKind::*; pub use NtPatKind::*; pub use TokenKind::*; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::Lrc; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::edition::Edition; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, kw, sym}; @@ -451,7 +451,7 @@ pub enum TokenKind { /// The span in the surrounding `Token` is that of the metavariable in the /// macro's RHS. The span within the Nonterminal is that of the fragment /// passed to the macro at the call site. - Interpolated(Lrc), + Interpolated(Arc), /// A doc comment token. /// `Symbol` is the doc comment's data excluding its "quotes" (`///`, `/**`, etc) @@ -469,7 +469,7 @@ impl Clone for TokenKind { // a copy. This is faster than the `derive(Clone)` version which has a // separate path for every variant. match self { - Interpolated(nt) => Interpolated(Lrc::clone(nt)), + Interpolated(nt) => Interpolated(Arc::clone(nt)), _ => unsafe { std::ptr::read(self) }, } } @@ -527,13 +527,13 @@ impl TokenKind { /// Returns tokens that are likely to be typed accidentally instead of the current token. /// Enables better error recovery when the wrong token is found. - pub fn similar_tokens(&self) -> Option> { - match *self { - Comma => Some(vec![Dot, Lt, Semi]), - Semi => Some(vec![Colon, Comma]), - Colon => Some(vec![Semi]), - FatArrow => Some(vec![Eq, RArrow, Ge, Gt]), - _ => None, + pub fn similar_tokens(&self) -> &[TokenKind] { + match self { + Comma => &[Dot, Lt, Semi], + Semi => &[Colon, Comma], + Colon => &[Semi], + FatArrow => &[Eq, RArrow, Ge, Gt], + _ => &[], } } @@ -909,7 +909,8 @@ impl Token { self.is_keyword(kw) || (case == Case::Insensitive && self.is_non_raw_ident_where(|id| { - id.name.as_str().to_lowercase() == kw.as_str().to_lowercase() + // Do an ASCII case-insensitive match, because all keywords are ASCII. + id.name.as_str().eq_ignore_ascii_case(kw.as_str()) })) } diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index e7b393d869d2b..50f10d083a041 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -14,10 +14,11 @@ //! ownership of the original. use std::borrow::Cow; +use std::sync::Arc; use std::{cmp, fmt, iter}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::{self, Lrc}; +use rustc_data_structures::sync; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_serialize::{Decodable, Encodable}; use rustc_span::{DUMMY_SP, Span, SpanDecoder, SpanEncoder, Symbol, sym}; @@ -119,11 +120,11 @@ impl ToAttrTokenStream for AttrTokenStream { /// of an actual `TokenStream` until it is needed. /// `Box` is here only to reduce the structure size. #[derive(Clone)] -pub struct LazyAttrTokenStream(Lrc>); +pub struct LazyAttrTokenStream(Arc>); impl LazyAttrTokenStream { pub fn new(inner: impl ToAttrTokenStream + 'static) -> LazyAttrTokenStream { - LazyAttrTokenStream(Lrc::new(Box::new(inner))) + LazyAttrTokenStream(Arc::new(Box::new(inner))) } pub fn to_attr_token_stream(&self) -> AttrTokenStream { @@ -160,7 +161,7 @@ impl HashStable for LazyAttrTokenStream { /// during expansion to perform early cfg-expansion, and to process attributes /// during proc-macro invocations. #[derive(Clone, Debug, Default, Encodable, Decodable)] -pub struct AttrTokenStream(pub Lrc>); +pub struct AttrTokenStream(pub Arc>); /// Like `TokenTree`, but for `AttrTokenStream`. #[derive(Clone, Debug, Encodable, Decodable)] @@ -175,7 +176,7 @@ pub enum AttrTokenTree { impl AttrTokenStream { pub fn new(tokens: Vec) -> AttrTokenStream { - AttrTokenStream(Lrc::new(tokens)) + AttrTokenStream(Arc::new(tokens)) } /// Converts this `AttrTokenStream` to a plain `Vec`. During @@ -293,7 +294,7 @@ pub struct AttrsTarget { /// Today's `TokenTree`s can still contain AST via `token::Interpolated` for /// backwards compatibility. #[derive(Clone, Debug, Default, Encodable, Decodable)] -pub struct TokenStream(pub(crate) Lrc>); +pub struct TokenStream(pub(crate) Arc>); /// Indicates whether a token can join with the following token to form a /// compound token. Used for conversions to `proc_macro::Spacing`. Also used to @@ -412,7 +413,7 @@ impl PartialEq for TokenStream { impl TokenStream { pub fn new(tts: Vec) -> TokenStream { - TokenStream(Lrc::new(tts)) + TokenStream(Arc::new(tts)) } pub fn is_empty(&self) -> bool { @@ -544,7 +545,7 @@ impl TokenStream { /// Push `tt` onto the end of the stream, possibly gluing it to the last /// token. Uses `make_mut` to maximize efficiency. pub fn push_tree(&mut self, tt: TokenTree) { - let vec_mut = Lrc::make_mut(&mut self.0); + let vec_mut = Arc::make_mut(&mut self.0); if Self::try_glue_to_last(vec_mut, &tt) { // nothing else to do @@ -557,7 +558,7 @@ impl TokenStream { /// token tree to the last token. (No other token trees will be glued.) /// Uses `make_mut` to maximize efficiency. pub fn push_stream(&mut self, stream: TokenStream) { - let vec_mut = Lrc::make_mut(&mut self.0); + let vec_mut = Arc::make_mut(&mut self.0); let stream_iter = stream.0.iter().cloned(); @@ -577,7 +578,7 @@ impl TokenStream { } /// Desugar doc comments like `/// foo` in the stream into `#[doc = - /// r"foo"]`. Modifies the `TokenStream` via `Lrc::make_mut`, but as little + /// r"foo"]`. Modifies the `TokenStream` via `Arc::make_mut`, but as little /// as possible. pub fn desugar_doc_comments(&mut self) { if let Some(desugared_stream) = desugar_inner(self.clone()) { @@ -596,7 +597,7 @@ impl TokenStream { ) => { let desugared = desugared_tts(attr_style, data, span); let desugared_len = desugared.len(); - Lrc::make_mut(&mut stream.0).splice(i..i + 1, desugared); + Arc::make_mut(&mut stream.0).splice(i..i + 1, desugared); modified = true; i += desugared_len; } @@ -607,7 +608,7 @@ impl TokenStream { if let Some(desugared_delim_stream) = desugar_inner(delim_stream.clone()) { let new_tt = TokenTree::Delimited(sp, spacing, delim, desugared_delim_stream); - Lrc::make_mut(&mut stream.0)[i] = new_tt; + Arc::make_mut(&mut stream.0)[i] = new_tt; modified = true; } i += 1; diff --git a/compiler/rustc_ast/src/util/comments.rs b/compiler/rustc_ast/src/util/comments.rs index f39142f08ba52..e12818623d8ec 100644 --- a/compiler/rustc_ast/src/util/comments.rs +++ b/compiler/rustc_ast/src/util/comments.rs @@ -39,7 +39,7 @@ pub fn beautify_doc_string(data: Symbol, kind: CommentKind) -> Symbol { let mut i = 0; let mut j = lines.len(); // first line of all-stars should be omitted - if !lines.is_empty() && lines[0].chars().all(|c| c == '*') { + if lines.first().is_some_and(|line| line.chars().all(|c| c == '*')) { i += 1; } @@ -97,7 +97,7 @@ pub fn beautify_doc_string(data: Symbol, kind: CommentKind) -> Symbol { return None; } } - if lines.is_empty() { None } else { Some(lines[0][..i].into()) } + Some(lines.first()?[..i].to_string()) } let data_s = data.as_str(); diff --git a/compiler/rustc_ast/src/util/comments/tests.rs b/compiler/rustc_ast/src/util/comments/tests.rs index f88b534a0c114..7a87ccea62a7e 100644 --- a/compiler/rustc_ast/src/util/comments/tests.rs +++ b/compiler/rustc_ast/src/util/comments/tests.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] +#![allow(rustc::symbol_intern_string_literal)] use rustc_span::create_default_session_globals_then; diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs index 4459cb962e8e9..6896ac723fa58 100644 --- a/compiler/rustc_ast/src/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -121,7 +121,7 @@ impl LitKind { } token::ByteStrRaw(n) => { // Raw strings have no escapes so we can convert the symbol - // directly to a `Lrc`. + // directly to a `Arc`. let buf = symbol.as_str().to_owned().into_bytes(); LitKind::ByteStr(buf.into(), StrStyle::Raw(n)) } @@ -142,7 +142,7 @@ impl LitKind { } token::CStrRaw(n) => { // Raw strings have no escapes so we can convert the symbol - // directly to a `Lrc` after appending the terminating NUL + // directly to a `Arc` after appending the terminating NUL // char. let mut buf = symbol.as_str().to_owned().into_bytes(); buf.push(0); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index e99fc7b604e31..3242d4145959d 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -65,7 +65,7 @@ impl BoundKind { #[derive(Copy, Clone, Debug)] pub enum FnKind<'a> { /// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`. - Fn(FnCtxt, &'a Ident, &'a FnSig, &'a Visibility, &'a Generics, &'a Option>), + Fn(FnCtxt, &'a Ident, &'a Visibility, &'a Fn), /// E.g., `|x, y| body`. Closure(&'a ClosureBinder, &'a Option, &'a FnDecl, &'a Expr), @@ -74,7 +74,7 @@ pub enum FnKind<'a> { impl<'a> FnKind<'a> { pub fn header(&self) -> Option<&'a FnHeader> { match *self { - FnKind::Fn(_, _, sig, _, _, _) => Some(&sig.header), + FnKind::Fn(_, _, _, Fn { sig, .. }) => Some(&sig.header), FnKind::Closure(..) => None, } } @@ -88,7 +88,7 @@ impl<'a> FnKind<'a> { pub fn decl(&self) -> &'a FnDecl { match self { - FnKind::Fn(_, _, sig, _, _, _) => &sig.decl, + FnKind::Fn(_, _, _, Fn { sig, .. }) => &sig.decl, FnKind::Closure(_, _, decl, _) => decl, } } @@ -179,6 +179,9 @@ pub trait Visitor<'ast>: Sized { fn visit_ty(&mut self, t: &'ast Ty) -> Self::Result { walk_ty(self, t) } + fn visit_ty_pat(&mut self, t: &'ast TyPat) -> Self::Result { + walk_ty_pat(self, t) + } fn visit_generic_param(&mut self, param: &'ast GenericParam) -> Self::Result { walk_generic_param(self, param) } @@ -188,6 +191,9 @@ pub trait Visitor<'ast>: Sized { fn visit_closure_binder(&mut self, b: &'ast ClosureBinder) -> Self::Result { walk_closure_binder(self, b) } + fn visit_contract(&mut self, c: &'ast FnContract) -> Self::Result { + walk_contract(self, c) + } fn visit_where_predicate(&mut self, p: &'ast WherePredicate) -> Self::Result { walk_where_predicate(self, p) } @@ -374,8 +380,8 @@ impl WalkItemKind for ItemKind { try_visit!(visitor.visit_ty(ty)); visit_opt!(visitor, visit_expr, expr); } - ItemKind::Fn(box Fn { defaultness: _, generics, sig, body }) => { - let kind = FnKind::Fn(FnCtxt::Free, ident, sig, vis, generics, body); + ItemKind::Fn(func) => { + let kind = FnKind::Fn(FnCtxt::Free, ident, vis, &*func); try_visit!(visitor.visit_fn(kind, span, id)); } ItemKind::Mod(_unsafety, mod_kind) => match mod_kind { @@ -531,7 +537,7 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result { } TyKind::Pat(ty, pat) => { try_visit!(visitor.visit_ty(ty)); - try_visit!(visitor.visit_pat(pat)); + try_visit!(visitor.visit_ty_pat(pat)); } TyKind::Array(ty, length) => { try_visit!(visitor.visit_ty(ty)); @@ -552,6 +558,18 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result { V::Result::output() } +pub fn walk_ty_pat<'a, V: Visitor<'a>>(visitor: &mut V, tp: &'a TyPat) -> V::Result { + let TyPat { id: _, kind, span: _, tokens: _ } = tp; + match kind { + TyPatKind::Range(start, end, _include_end) => { + visit_opt!(visitor, visit_anon_const, start); + visit_opt!(visitor, visit_anon_const, end); + } + TyPatKind::Err(_) => {} + } + V::Result::output() +} + fn walk_qself<'a, V: Visitor<'a>>(visitor: &mut V, qself: &'a Option>) -> V::Result { if let Some(qself) = qself { let QSelf { ty, path_span: _, position: _ } = &**qself; @@ -680,7 +698,7 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Res try_visit!(visitor.visit_ident(ident)); visit_opt!(visitor, visit_pat, optional_subpattern); } - PatKind::Lit(expression) => try_visit!(visitor.visit_expr(expression)), + PatKind::Expr(expression) => try_visit!(visitor.visit_expr(expression)), PatKind::Range(lower_bound, upper_bound, _end) => { visit_opt!(visitor, visit_expr, lower_bound); visit_opt!(visitor, visit_expr, upper_bound); @@ -715,8 +733,8 @@ impl WalkItemKind for ForeignItemKind { try_visit!(visitor.visit_ty(ty)); visit_opt!(visitor, visit_expr, expr); } - ForeignItemKind::Fn(box Fn { defaultness: _, generics, sig, body }) => { - let kind = FnKind::Fn(FnCtxt::Foreign, ident, sig, vis, generics, body); + ForeignItemKind::Fn(func) => { + let kind = FnKind::Fn(FnCtxt::Foreign, ident, vis, &*func); try_visit!(visitor.visit_fn(kind, span, id)); } ForeignItemKind::TyAlias(box TyAlias { @@ -800,6 +818,17 @@ pub fn walk_closure_binder<'a, V: Visitor<'a>>( V::Result::output() } +pub fn walk_contract<'a, V: Visitor<'a>>(visitor: &mut V, c: &'a FnContract) -> V::Result { + let FnContract { requires, ensures } = c; + if let Some(pred) = requires { + visitor.visit_expr(pred); + } + if let Some(pred) = ensures { + visitor.visit_expr(pred); + } + V::Result::output() +} + pub fn walk_where_predicate<'a, V: Visitor<'a>>( visitor: &mut V, predicate: &'a WherePredicate, @@ -858,11 +887,17 @@ pub fn walk_fn_decl<'a, V: Visitor<'a>>( pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Result { match kind { - FnKind::Fn(_ctxt, _ident, FnSig { header, decl, span: _ }, _vis, generics, body) => { + FnKind::Fn( + _ctxt, + _ident, + _vis, + Fn { defaultness: _, sig: FnSig { header, decl, span: _ }, generics, contract, body }, + ) => { // Identifier and visibility are visited as a part of the item. try_visit!(visitor.visit_fn_header(header)); try_visit!(visitor.visit_generics(generics)); try_visit!(visitor.visit_fn_decl(decl)); + visit_opt!(visitor, visit_contract, contract); visit_opt!(visitor, visit_block, body); } FnKind::Closure(binder, coroutine_kind, decl, body) => { @@ -892,8 +927,8 @@ impl WalkItemKind for AssocItemKind { try_visit!(visitor.visit_ty(ty)); visit_opt!(visitor, visit_expr, expr); } - AssocItemKind::Fn(box Fn { defaultness: _, generics, sig, body }) => { - let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), ident, sig, vis, generics, body); + AssocItemKind::Fn(func) => { + let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), ident, vis, &*func); try_visit!(visitor.visit_fn(kind, span, id)); } AssocItemKind::Type(box TyAlias { diff --git a/compiler/rustc_ast_lowering/Cargo.toml b/compiler/rustc_ast_lowering/Cargo.toml index 8cc4521e0a78d..ce95f4dfa1b82 100644 --- a/compiler/rustc_ast_lowering/Cargo.toml +++ b/compiler/rustc_ast_lowering/Cargo.toml @@ -8,10 +8,12 @@ doctest = false [dependencies] # tidy-alphabetical-start +rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } +rustc_feature = { path = "../rustc_feature" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl index f96c9fe8e3273..1b91c33742d8b 100644 --- a/compiler/rustc_ast_lowering/messages.ftl +++ b/compiler/rustc_ast_lowering/messages.ftl @@ -112,7 +112,8 @@ ast_lowering_invalid_register = invalid register `{$reg}`: {$error} ast_lowering_invalid_register_class = - invalid register class `{$reg_class}`: {$error} + invalid register class `{$reg_class}`: unknown register class + .note = the following register classes are supported on this target: {$supported_register_classes} ast_lowering_match_arm_with_no_body = `match` arm with no body diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index 2f1f1269ece57..96c230ec243a2 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -152,11 +152,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { InlineAsmRegOrRegClass::RegClass(reg_class) => { asm::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch { asm::InlineAsmRegClass::parse(asm_arch, reg_class).unwrap_or_else( - |error| { + |supported_register_classes| { + let mut register_classes = + format!("`{}`", supported_register_classes[0]); + for m in &supported_register_classes[1..] { + let _ = write!(register_classes, ", `{m}`"); + } self.dcx().emit_err(InvalidRegisterClass { op_span: *op_sp, reg_class, - error, + supported_register_classes: register_classes, }); asm::InlineAsmRegClass::Err }, diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 758f1dc1c3558..d8b7cb0c3225b 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -41,13 +41,13 @@ use std::iter; use ast::visit::Visitor; use hir::def::{DefKind, PartialRes, Res}; use hir::{BodyId, HirId}; +use rustc_abi::ExternAbi; use rustc_ast::*; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::DefId; use rustc_middle::span_bug; use rustc_middle::ty::{Asyncness, ResolverAstLowering}; use rustc_span::{Ident, Span}; -use rustc_target::spec::abi; use {rustc_ast as ast, rustc_hir as hir}; use super::{GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode}; @@ -72,7 +72,7 @@ impl<'hir> LoweringContext<'_, 'hir> { if let Some(local_sig_id) = def_id.as_local() { // The value may be missing due to recursive delegation. // Error will be emitted later during HIR ty lowering. - self.resolver.delegation_fn_sigs.get(&local_sig_id).map_or(false, |sig| sig.has_self) + self.resolver.delegation_fn_sigs.get(&local_sig_id).is_some_and(|sig| sig.has_self) } else { match self.tcx.def_kind(def_id) { DefKind::Fn => false, @@ -188,7 +188,14 @@ impl<'hir> LoweringContext<'_, 'hir> { ) -> hir::FnSig<'hir> { let header = if let Some(local_sig_id) = sig_id.as_local() { match self.resolver.delegation_fn_sigs.get(&local_sig_id) { - Some(sig) => self.lower_fn_header(sig.header, hir::Safety::Safe), + Some(sig) => self.lower_fn_header( + sig.header, + // HACK: we override the default safety instead of generating attributes from the ether. + // We are not forwarding the attributes, as the delegation fn sigs are collected on the ast, + // and here we need the hir attributes. + if sig.target_feature { hir::Safety::Unsafe } else { hir::Safety::Safe }, + &[], + ), None => self.generate_header_error(), } } else { @@ -198,7 +205,11 @@ impl<'hir> LoweringContext<'_, 'hir> { Asyncness::No => hir::IsAsync::NotAsync, }; hir::FnHeader { - safety: sig.safety, + safety: if self.tcx.codegen_fn_attrs(sig_id).safe_target_features { + hir::HeaderSafety::SafeTargetFeatures + } else { + hir::HeaderSafety::Normal(sig.safety) + }, constness: self.tcx.constness(sig_id), asyncness, abi: sig.abi, @@ -384,10 +395,10 @@ impl<'hir> LoweringContext<'_, 'hir> { fn generate_header_error(&self) -> hir::FnHeader { hir::FnHeader { - safety: hir::Safety::Safe, + safety: hir::Safety::Safe.into(), constness: hir::Constness::NotConst, asyncness: hir::IsAsync::NotAsync, - abi: abi::Abi::Rust, + abi: ExternAbi::Rust, } } diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index f727691bf479c..f31e2db051d81 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -1,5 +1,5 @@ +use rustc_errors::DiagArgFromDisplay; use rustc_errors::codes::*; -use rustc_errors::{Diag, DiagArgFromDisplay, EmissionGuarantee, SubdiagMessageOp, Subdiagnostic}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Ident, Span, Symbol}; @@ -32,8 +32,6 @@ pub(crate) struct InvalidAbi { pub abi: Symbol, pub command: String, #[subdiagnostic] - pub explain: Option, - #[subdiagnostic] pub suggestion: Option, } @@ -45,19 +43,6 @@ pub(crate) struct TupleStructWithDefault { pub span: Span, } -pub(crate) struct InvalidAbiReason(pub &'static str); - -impl Subdiagnostic for InvalidAbiReason { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _: &F, - ) { - #[allow(rustc::untranslatable_diagnostic)] - diag.note(self.0); - } -} - #[derive(Subdiagnostic)] #[suggestion( ast_lowering_invalid_abi_suggestion, @@ -212,12 +197,13 @@ pub(crate) struct InvalidRegister<'a> { } #[derive(Diagnostic)] +#[note] #[diag(ast_lowering_invalid_register_class)] -pub(crate) struct InvalidRegisterClass<'a> { +pub(crate) struct InvalidRegisterClass { #[primary_span] pub op_span: Span, pub reg_class: Symbol, - pub error: &'a str, + pub supported_register_classes: String, } #[derive(Diagnostic)] diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index d16a3ce390dbe..af53c7ec21572 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1,11 +1,11 @@ use std::assert_matches::assert_matches; use std::ops::ControlFlow; +use std::sync::Arc; use rustc_ast::ptr::P as AstP; use rustc_ast::*; use rustc_ast_pretty::pprust::expr_to_string; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_data_structures::sync::Lrc; use rustc_hir as hir; use rustc_hir::HirId; use rustc_hir::def::{DefKind, Res}; @@ -102,17 +102,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let kind = match &e.kind { ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), - ExprKind::ConstBlock(c) => { - let c = self.with_new_scopes(c.value.span, |this| { - let def_id = this.local_def_id(c.id); - hir::ConstBlock { - def_id, - hir_id: this.lower_node_id(c.id), - body: this.lower_const_body(c.value.span, Some(&c.value)), - } - }); - hir::ExprKind::ConstBlock(c) - } + ExprKind::ConstBlock(c) => hir::ExprKind::ConstBlock(self.lower_const_block(c)), ExprKind::Repeat(expr, count) => { let expr = self.lower_expr(expr); let count = self.lower_array_length_to_const_arg(count); @@ -153,22 +143,11 @@ impl<'hir> LoweringContext<'_, 'hir> { let ohs = self.lower_expr(ohs); hir::ExprKind::Unary(op, ohs) } - ExprKind::Lit(token_lit) => { - let lit_kind = match LitKind::from_token_lit(*token_lit) { - Ok(lit_kind) => lit_kind, - Err(err) => { - let guar = - report_lit_error(&self.tcx.sess.psess, err, *token_lit, e.span); - LitKind::Err(guar) - } - }; - let lit = self.arena.alloc(respan(self.lower_span(e.span), lit_kind)); - hir::ExprKind::Lit(lit) - } + ExprKind::Lit(token_lit) => hir::ExprKind::Lit(self.lower_lit(token_lit, e.span)), ExprKind::IncludedBytes(bytes) => { let lit = self.arena.alloc(respan( self.lower_span(e.span), - LitKind::ByteStr(Lrc::clone(bytes), StrStyle::Cooked), + LitKind::ByteStr(Arc::clone(bytes), StrStyle::Cooked), )); hir::ExprKind::Lit(lit) } @@ -305,9 +284,6 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::Index(el, er, brackets_span) => { hir::ExprKind::Index(self.lower_expr(el), self.lower_expr(er), *brackets_span) } - ExprKind::Range(Some(e1), Some(e2), RangeLimits::Closed) => { - self.lower_expr_range_closed(e.span, e1, e2) - } ExprKind::Range(e1, e2, lims) => { self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), *lims) } @@ -335,8 +311,8 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ExprKind::Continue(self.lower_jump_destination(e.id, *opt_label)) } ExprKind::Ret(e) => { - let e = e.as_ref().map(|x| self.lower_expr(x)); - hir::ExprKind::Ret(e) + let expr = e.as_ref().map(|x| self.lower_expr(x)); + self.checked_return(expr) } ExprKind::Yeet(sub_expr) => self.lower_expr_yeet(e.span, sub_expr.as_deref()), ExprKind::Become(sub_expr) => { @@ -403,6 +379,58 @@ impl<'hir> LoweringContext<'_, 'hir> { }) } + /// Create an `ExprKind::Ret` that is optionally wrapped by a call to check + /// a contract ensures clause, if it exists. + fn checked_return(&mut self, opt_expr: Option<&'hir hir::Expr<'hir>>) -> hir::ExprKind<'hir> { + let checked_ret = + if let Some((check_span, check_ident, check_hir_id)) = self.contract_ensures { + let expr = opt_expr.unwrap_or_else(|| self.expr_unit(check_span)); + Some(self.inject_ensures_check(expr, check_span, check_ident, check_hir_id)) + } else { + opt_expr + }; + hir::ExprKind::Ret(checked_ret) + } + + /// Wraps an expression with a call to the ensures check before it gets returned. + pub(crate) fn inject_ensures_check( + &mut self, + expr: &'hir hir::Expr<'hir>, + span: Span, + check_ident: Ident, + check_hir_id: HirId, + ) -> &'hir hir::Expr<'hir> { + let checker_fn = self.expr_ident(span, check_ident, check_hir_id); + let span = self.mark_span_with_reason(DesugaringKind::Contract, span, None); + self.expr_call(span, checker_fn, std::slice::from_ref(expr)) + } + + pub(crate) fn lower_const_block(&mut self, c: &AnonConst) -> hir::ConstBlock { + self.with_new_scopes(c.value.span, |this| { + let def_id = this.local_def_id(c.id); + hir::ConstBlock { + def_id, + hir_id: this.lower_node_id(c.id), + body: this.lower_const_body(c.value.span, Some(&c.value)), + } + }) + } + + pub(crate) fn lower_lit( + &mut self, + token_lit: &token::Lit, + span: Span, + ) -> &'hir Spanned { + let lit_kind = match LitKind::from_token_lit(*token_lit) { + Ok(lit_kind) => lit_kind, + Err(err) => { + let guar = report_lit_error(&self.tcx.sess.psess, err, *token_lit, span); + LitKind::Err(guar) + } + }; + self.arena.alloc(respan(self.lower_span(span), lit_kind)) + } + fn lower_unop(&mut self, u: UnOp) -> hir::UnOp { match u { UnOp::Deref => hir::UnOp::Deref, @@ -597,7 +625,7 @@ impl<'hir> LoweringContext<'_, 'hir> { this.mark_span_with_reason( DesugaringKind::TryBlock, expr.span, - Some(Lrc::clone(&this.allow_try_trait)), + Some(Arc::clone(&this.allow_try_trait)), ), expr, ) @@ -605,7 +633,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let try_span = this.mark_span_with_reason( DesugaringKind::TryBlock, this.tcx.sess.source_map().end_point(body.span), - Some(Lrc::clone(&this.allow_try_trait)), + Some(Arc::clone(&this.allow_try_trait)), ); (try_span, this.expr_unit(try_span)) @@ -714,7 +742,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let unstable_span = self.mark_span_with_reason( DesugaringKind::Async, self.lower_span(span), - Some(Lrc::clone(&self.allow_gen_future)), + Some(Arc::clone(&self.allow_gen_future)), ); let resume_ty = self.make_lang_item_qpath(hir::LangItem::ResumeTy, unstable_span, None); @@ -798,17 +826,20 @@ impl<'hir> LoweringContext<'_, 'hir> { let unstable_span = self.mark_span_with_reason( DesugaringKind::Async, span, - Some(Lrc::clone(&self.allow_gen_future)), + Some(Arc::clone(&self.allow_gen_future)), + ); + self.lower_attrs( + inner_hir_id, + &[Attribute { + kind: AttrKind::Normal(ptr::P(NormalAttr::from_ident(Ident::new( + sym::track_caller, + span, + )))), + id: self.tcx.sess.psess.attr_id_generator.mk_attr_id(), + style: AttrStyle::Outer, + span: unstable_span, + }], ); - self.lower_attrs(inner_hir_id, &[Attribute { - kind: AttrKind::Normal(ptr::P(NormalAttr::from_ident(Ident::new( - sym::track_caller, - span, - )))), - id: self.tcx.sess.psess.attr_id_generator.mk_attr_id(), - style: AttrStyle::Outer, - span: unstable_span, - }]); } } @@ -874,13 +905,13 @@ impl<'hir> LoweringContext<'_, 'hir> { let features = match await_kind { FutureKind::Future => None, - FutureKind::AsyncIterator => Some(Lrc::clone(&self.allow_for_await)), + FutureKind::AsyncIterator => Some(Arc::clone(&self.allow_for_await)), }; let span = self.mark_span_with_reason(DesugaringKind::Await, await_kw_span, features); let gen_future_span = self.mark_span_with_reason( DesugaringKind::Await, full_span, - Some(Lrc::clone(&self.allow_gen_future)), + Some(Arc::clone(&self.allow_gen_future)), ); let expr_hir_id = expr.hir_id; @@ -1059,7 +1090,8 @@ impl<'hir> LoweringContext<'_, 'hir> { } else { None }; - let body_id = this.lower_fn_body(decl, |this| { + // FIXME(contracts): Support contracts on closures? + let body_id = this.lower_fn_body(decl, None, |this| { this.coroutine_kind = coroutine_kind; let e = this.lower_expr_mut(body); coroutine_kind = this.coroutine_kind; @@ -1386,7 +1418,11 @@ impl<'hir> LoweringContext<'_, 'hir> { None, ); // Destructure like a unit struct. - let unit_struct_pat = hir::PatKind::Path(qpath); + let unit_struct_pat = hir::PatKind::Expr(self.arena.alloc(hir::PatExpr { + kind: hir::PatExprKind::Path(qpath), + hir_id: self.next_id(), + span: self.lower_span(lhs.span), + })); return self.pat_without_dbm(lhs.span, unit_struct_pat); } } @@ -1503,15 +1539,39 @@ impl<'hir> LoweringContext<'_, 'hir> { let lang_item = match (e1, e2, lims) { (None, None, HalfOpen) => hir::LangItem::RangeFull, - (Some(..), None, HalfOpen) => hir::LangItem::RangeFrom, + (Some(..), None, HalfOpen) => { + if self.tcx.features().new_range() { + hir::LangItem::RangeFromCopy + } else { + hir::LangItem::RangeFrom + } + } (None, Some(..), HalfOpen) => hir::LangItem::RangeTo, - (Some(..), Some(..), HalfOpen) => hir::LangItem::Range, + (Some(..), Some(..), HalfOpen) => { + if self.tcx.features().new_range() { + hir::LangItem::RangeCopy + } else { + hir::LangItem::Range + } + } (None, Some(..), Closed) => hir::LangItem::RangeToInclusive, - (Some(..), Some(..), Closed) => unreachable!(), + (Some(e1), Some(e2), Closed) => { + if self.tcx.features().new_range() { + hir::LangItem::RangeInclusiveCopy + } else { + return self.lower_expr_range_closed(span, e1, e2); + } + } (start, None, Closed) => { self.dcx().emit_err(InclusiveRangeWithNoEnd { span }); match start { - Some(..) => hir::LangItem::RangeFrom, + Some(..) => { + if self.tcx.features().new_range() { + hir::LangItem::RangeFromCopy + } else { + hir::LangItem::RangeFrom + } + } None => hir::LangItem::RangeFull, } } @@ -1896,13 +1956,13 @@ impl<'hir> LoweringContext<'_, 'hir> { let unstable_span = self.mark_span_with_reason( DesugaringKind::QuestionMark, span, - Some(Lrc::clone(&self.allow_try_trait)), + Some(Arc::clone(&self.allow_try_trait)), ); let try_span = self.tcx.sess.source_map().end_point(span); let try_span = self.mark_span_with_reason( DesugaringKind::QuestionMark, try_span, - Some(Lrc::clone(&self.allow_try_trait)), + Some(Arc::clone(&self.allow_try_trait)), ); // `Try::branch()` @@ -1961,7 +2021,8 @@ impl<'hir> LoweringContext<'_, 'hir> { ), )) } else { - self.arena.alloc(self.expr(try_span, hir::ExprKind::Ret(Some(from_residual_expr)))) + let ret_expr = self.checked_return(Some(from_residual_expr)); + self.arena.alloc(self.expr(try_span, ret_expr)) }; self.lower_attrs(ret_expr.hir_id, &attrs); @@ -1996,7 +2057,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let unstable_span = self.mark_span_with_reason( DesugaringKind::YeetExpr, span, - Some(Lrc::clone(&self.allow_try_trait)), + Some(Arc::clone(&self.allow_try_trait)), ); let from_yeet_expr = self.wrap_in_try_constructor( @@ -2010,7 +2071,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let target_id = Ok(catch_id); hir::ExprKind::Break(hir::Destination { label: None, target_id }, Some(from_yeet_expr)) } else { - hir::ExprKind::Ret(Some(from_yeet_expr)) + self.checked_return(Some(from_yeet_expr)) } } @@ -2120,7 +2181,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.arena.alloc(self.expr_call_mut(span, e, args)) } - fn expr_call_lang_item_fn_mut( + pub(super) fn expr_call_lang_item_fn_mut( &mut self, span: Span, lang_item: hir::LangItem, @@ -2130,7 +2191,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.expr_call_mut(span, path, args) } - fn expr_call_lang_item_fn( + pub(super) fn expr_call_lang_item_fn( &mut self, span: Span, lang_item: hir::LangItem, @@ -2154,7 +2215,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let path = hir::ExprKind::Path(hir::QPath::TypeRelative( self.arena.alloc(self.ty(span, hir::TyKind::Path(qpath))), self.arena.alloc(hir::PathSegment::new( - Ident::new(name, span), + Ident::new(name, self.lower_span(span)), self.next_id(), Res::Err, )), diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs index 22aa1e6fc201a..07b94dbc2aebd 100644 --- a/compiler/rustc_ast_lowering/src/format.rs +++ b/compiler/rustc_ast_lowering/src/format.rs @@ -362,13 +362,16 @@ fn make_format_spec<'hir>( debug_hex, } = &placeholder.format_options; let fill = ctx.expr_char(sp, fill.unwrap_or(' ')); - let align = - ctx.expr_lang_item_type_relative(sp, hir::LangItem::FormatAlignment, match alignment { + let align = ctx.expr_lang_item_type_relative( + sp, + hir::LangItem::FormatAlignment, + match alignment { Some(FormatAlignment::Left) => sym::Left, Some(FormatAlignment::Right) => sym::Right, Some(FormatAlignment::Center) => sym::Center, None => sym::Unknown, - }); + }, + ); // This needs to match `Flag` in library/core/src/fmt/rt.rs. let flags: u32 = ((sign == Some(FormatSign::Plus)) as u32) | ((sign == Some(FormatSign::Minus)) as u32) << 1 diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index c3ff7b4b89722..d1e23bf252229 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -1,3 +1,4 @@ +use intravisit::InferKind; use rustc_data_structures::sorted_map::SortedMap; use rustc_hir as hir; use rustc_hir::def_id::{LocalDefId, LocalDefIdMap}; @@ -78,24 +79,31 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> { // Make sure that the DepNode of some node coincides with the HirId // owner of that node. - if cfg!(debug_assertions) && hir_id.owner != self.owner { - span_bug!( - span, - "inconsistent HirId at `{:?}` for `{:?}`: \ + if cfg!(debug_assertions) { + if hir_id.owner != self.owner { + span_bug!( + span, + "inconsistent HirId at `{:?}` for `{node:?}`: \ current_dep_node_owner={} ({:?}), hir_id.owner={} ({:?})", - self.tcx.sess.source_map().span_to_diagnostic_string(span), - node, - self.tcx - .definitions_untracked() - .def_path(self.owner.def_id) - .to_string_no_crate_verbose(), - self.owner, - self.tcx - .definitions_untracked() - .def_path(hir_id.owner.def_id) - .to_string_no_crate_verbose(), - hir_id.owner, - ) + self.tcx.sess.source_map().span_to_diagnostic_string(span), + self.tcx + .definitions_untracked() + .def_path(self.owner.def_id) + .to_string_no_crate_verbose(), + self.owner, + self.tcx + .definitions_untracked() + .def_path(hir_id.owner.def_id) + .to_string_no_crate_verbose(), + hir_id.owner, + ) + } + if self.tcx.sess.opts.incremental.is_some() + && span.parent().is_none() + && !span.is_dummy() + { + span_bug!(span, "span without a parent: {:#?}, {node:?}", span.data()) + } } self.nodes[hir_id.local_id] = ParentedNode { parent: self.parent_node, node }; @@ -209,6 +217,14 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { }); } + fn visit_pat_expr(&mut self, expr: &'hir PatExpr<'hir>) { + self.insert(expr.span, expr.hir_id, Node::PatExpr(expr)); + + self.with_parent(expr.hir_id, |this| { + intravisit::walk_pat_expr(this, expr); + }); + } + fn visit_pat_field(&mut self, field: &'hir PatField<'hir>) { self.insert(field.span, field.hir_id, Node::PatField(field)); self.with_parent(field.hir_id, |this| { @@ -250,14 +266,6 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { }); } - fn visit_const_arg(&mut self, const_arg: &'hir ConstArg<'hir>) { - self.insert(const_arg.span(), const_arg.hir_id, Node::ConstArg(const_arg)); - - self.with_parent(const_arg.hir_id, |this| { - intravisit::walk_const_arg(this, const_arg); - }); - } - fn visit_expr(&mut self, expr: &'hir Expr<'hir>) { self.insert(expr.span, expr.hir_id, Node::Expr(expr)); @@ -287,22 +295,41 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { intravisit::walk_path_segment(self, path_segment); } - fn visit_ty(&mut self, ty: &'hir Ty<'hir>) { - self.insert(ty.span, ty.hir_id, Node::Ty(ty)); + fn visit_ty(&mut self, ty: &'hir Ty<'hir, AmbigArg>) { + self.insert(ty.span, ty.hir_id, Node::Ty(ty.as_unambig_ty())); self.with_parent(ty.hir_id, |this| { intravisit::walk_ty(this, ty); }); } - fn visit_infer(&mut self, inf: &'hir InferArg) { - self.insert(inf.span, inf.hir_id, Node::Infer(inf)); + fn visit_const_arg(&mut self, const_arg: &'hir ConstArg<'hir, AmbigArg>) { + self.insert( + const_arg.as_unambig_ct().span(), + const_arg.hir_id, + Node::ConstArg(const_arg.as_unambig_ct()), + ); - self.with_parent(inf.hir_id, |this| { - intravisit::walk_inf(this, inf); + self.with_parent(const_arg.hir_id, |this| { + intravisit::walk_ambig_const_arg(this, const_arg); }); } + fn visit_infer( + &mut self, + inf_id: HirId, + inf_span: Span, + kind: InferKind<'hir>, + ) -> Self::Result { + match kind { + InferKind::Ty(ty) => self.insert(inf_span, inf_id, Node::Ty(ty)), + InferKind::Const(ct) => self.insert(inf_span, inf_id, Node::ConstArg(ct)), + InferKind::Ambig(inf) => self.insert(inf_span, inf_id, Node::Infer(inf)), + } + + self.visit_id(inf_id); + } + fn visit_trait_ref(&mut self, tr: &'hir TraitRef<'hir>) { self.insert(tr.path.span, tr.hir_ref_id, Node::TraitRef(tr)); @@ -385,8 +412,12 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { }); } - fn visit_pattern_type_pattern(&mut self, p: &'hir hir::Pat<'hir>) { - self.visit_pat(p) + fn visit_pattern_type_pattern(&mut self, pat: &'hir hir::TyPat<'hir>) { + self.insert(pat.span, pat.hir_id, Node::TyPat(pat)); + + self.with_parent(pat.hir_id, |this| { + intravisit::walk_ty_pat(this, pat); + }); } fn visit_precise_capturing_arg( diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 2cf6a2a909b18..bc2db41546989 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1,3 +1,4 @@ +use rustc_abi::ExternAbi; use rustc_ast::ptr::P; use rustc_ast::visit::AssocCtxt; use rustc_ast::*; @@ -11,15 +12,14 @@ use rustc_middle::span_bug; use rustc_middle::ty::{ResolverAstLowering, TyCtxt}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::{DesugaringKind, Ident, Span, Symbol, kw, sym}; -use rustc_target::spec::abi; use smallvec::{SmallVec, smallvec}; use thin_vec::ThinVec; use tracing::instrument; use super::errors::{ - InvalidAbi, InvalidAbiReason, InvalidAbiSuggestion, MisplacedRelaxTraitBound, - TupleStructWithDefault, + InvalidAbi, InvalidAbiSuggestion, MisplacedRelaxTraitBound, TupleStructWithDefault, }; +use super::stability::{enabled_names, gate_unstable_abi}; use super::{ AstOwner, FnDeclKind, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode, ResolverAstLoweringExt, @@ -207,6 +207,7 @@ impl<'hir> LoweringContext<'_, 'hir> { sig: FnSig { decl, header, span: fn_sig_span }, generics, body, + contract, .. }) => { self.with_new_scopes(*fn_sig_span, |this| { @@ -222,6 +223,8 @@ impl<'hir> LoweringContext<'_, 'hir> { decl, coroutine_kind, body.as_deref(), + attrs, + contract.as_deref(), ); let itctx = ImplTraitContext::Universal; @@ -230,10 +233,10 @@ impl<'hir> LoweringContext<'_, 'hir> { }); let sig = hir::FnSig { decl, - header: this.lower_fn_header(*header, hir::Safety::Safe), + header: this.lower_fn_header(*header, hir::Safety::Safe, attrs), span: this.lower_span(*fn_sig_span), }; - hir::ItemKind::Fn(sig, generics, body_id) + hir::ItemKind::Fn { sig, generics, body: body_id, has_body: body.is_some() } }) } ItemKind::Mod(_, mod_kind) => match mod_kind { @@ -243,7 +246,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ModKind::Unloaded => panic!("`mod` items should have been loaded by now"), }, ItemKind::ForeignMod(fm) => hir::ItemKind::ForeignMod { - abi: fm.abi.map_or(abi::Abi::FALLBACK, |abi| self.lower_abi(abi)), + abi: fm.abi.map_or(ExternAbi::FALLBACK, |abi| self.lower_abi(abi)), items: self .arena .alloc_from_iter(fm.items.iter().map(|x| self.lower_foreign_item_ref(x))), @@ -272,12 +275,15 @@ impl<'hir> LoweringContext<'_, 'hir> { ); this.arena.alloc(this.ty(span, hir::TyKind::Err(guar))) } - Some(ty) => this.lower_ty(ty, ImplTraitContext::OpaqueTy { - origin: hir::OpaqueTyOrigin::TyAlias { - parent: this.local_def_id(id), - in_assoc_ty: false, + Some(ty) => this.lower_ty( + ty, + ImplTraitContext::OpaqueTy { + origin: hir::OpaqueTyOrigin::TyAlias { + parent: this.local_def_id(id), + in_assoc_ty: false, + }, }, - }), + ), }, ); hir::ItemKind::TyAlias(ty, generics) @@ -435,11 +441,12 @@ impl<'hir> LoweringContext<'_, 'hir> { } ItemKind::Delegation(box delegation) => { let delegation_results = self.lower_delegation(delegation, id); - hir::ItemKind::Fn( - delegation_results.sig, - delegation_results.generics, - delegation_results.body_id, - ) + hir::ItemKind::Fn { + sig: delegation_results.sig, + generics: delegation_results.generics, + body: delegation_results.body_id, + has_body: true, + } } ItemKind::MacCall(..) | ItemKind::DelegationMac(..) => { panic!("macros should have been expanded by now") @@ -608,7 +615,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_foreign_item(&mut self, i: &ForeignItem) -> &'hir hir::ForeignItem<'hir> { let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id); let owner_id = hir_id.expect_owner(); - self.lower_attrs(hir_id, &i.attrs); + let attrs = self.lower_attrs(hir_id, &i.attrs); let item = hir::ForeignItem { owner_id, ident: self.lower_ident(i.ident), @@ -632,7 +639,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }); // Unmarked safety in unsafe block defaults to unsafe. - let header = self.lower_fn_header(sig.header, hir::Safety::Unsafe); + let header = self.lower_fn_header(sig.header, hir::Safety::Unsafe, attrs); hir::ForeignItemKind::Fn( hir::FnSig { header, decl, span: self.lower_span(sig.span) }, @@ -747,7 +754,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'hir> { let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id); - self.lower_attrs(hir_id, &i.attrs); + let attrs = self.lower_attrs(hir_id, &i.attrs); let trait_item_def_id = hir_id.expect_owner(); let (generics, kind, has_default) = match &i.kind { @@ -767,6 +774,8 @@ impl<'hir> LoweringContext<'_, 'hir> { (generics, kind, expr.is_some()) } AssocItemKind::Fn(box Fn { sig, generics, body: None, .. }) => { + // FIXME(contracts): Deny contract here since it won't apply to + // any impl method or callees. let names = self.lower_fn_params_to_names(&sig.decl); let (generics, sig) = self.lower_method_sig( generics, @@ -774,10 +783,11 @@ impl<'hir> LoweringContext<'_, 'hir> { i.id, FnDeclKind::Trait, sig.header.coroutine_kind, + attrs, ); (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false) } - AssocItemKind::Fn(box Fn { sig, generics, body: Some(body), .. }) => { + AssocItemKind::Fn(box Fn { sig, generics, body: Some(body), contract, .. }) => { let body_id = self.lower_maybe_coroutine_body( sig.span, i.span, @@ -785,6 +795,8 @@ impl<'hir> LoweringContext<'_, 'hir> { &sig.decl, sig.header.coroutine_kind, Some(body), + attrs, + contract.as_deref(), ); let (generics, sig) = self.lower_method_sig( generics, @@ -792,6 +804,7 @@ impl<'hir> LoweringContext<'_, 'hir> { i.id, FnDeclKind::Trait, sig.header.coroutine_kind, + attrs, ); (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true) } @@ -877,7 +890,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let has_value = true; let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value); let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id); - self.lower_attrs(hir_id, &i.attrs); + let attrs = self.lower_attrs(hir_id, &i.attrs); let (generics, kind) = match &i.kind { AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => self.lower_generics( @@ -892,7 +905,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ImplItemKind::Const(ty, body) }, ), - AssocItemKind::Fn(box Fn { sig, generics, body, .. }) => { + AssocItemKind::Fn(box Fn { sig, generics, body, contract, .. }) => { let body_id = self.lower_maybe_coroutine_body( sig.span, i.span, @@ -900,6 +913,8 @@ impl<'hir> LoweringContext<'_, 'hir> { &sig.decl, sig.header.coroutine_kind, body.as_deref(), + attrs, + contract.as_deref(), ); let (generics, sig) = self.lower_method_sig( generics, @@ -907,6 +922,7 @@ impl<'hir> LoweringContext<'_, 'hir> { i.id, if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent }, sig.header.coroutine_kind, + attrs, ); (generics, hir::ImplItemKind::Fn(sig, body_id)) @@ -928,12 +944,15 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ImplItemKind::Type(ty) } Some(ty) => { - let ty = this.lower_ty(ty, ImplTraitContext::OpaqueTy { - origin: hir::OpaqueTyOrigin::TyAlias { - parent: this.local_def_id(i.id), - in_assoc_ty: true, + let ty = this.lower_ty( + ty, + ImplTraitContext::OpaqueTy { + origin: hir::OpaqueTyOrigin::TyAlias { + parent: this.local_def_id(i.id), + in_assoc_ty: true, + }, }, - }); + ); hir::ImplItemKind::Type(ty) } }, @@ -1044,38 +1063,100 @@ impl<'hir> LoweringContext<'_, 'hir> { pub(super) fn lower_fn_body( &mut self, decl: &FnDecl, + contract: Option<&FnContract>, body: impl FnOnce(&mut Self) -> hir::Expr<'hir>, ) -> hir::BodyId { self.lower_body(|this| { - ( - this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x))), - body(this), - ) + let params = + this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x))); + + // Optionally lower the fn contract, which turns: + // + // { body } + // + // into: + // + // { contract_requires(PRECOND); let __postcond = |ret_val| POSTCOND; postcond({ body }) } + if let Some(contract) = contract { + let precond = if let Some(req) = &contract.requires { + // Lower the precondition check intrinsic. + let lowered_req = this.lower_expr_mut(&req); + let precond = this.expr_call_lang_item_fn_mut( + req.span, + hir::LangItem::ContractCheckRequires, + &*arena_vec![this; lowered_req], + ); + Some(this.stmt_expr(req.span, precond)) + } else { + None + }; + let (postcond, body) = if let Some(ens) = &contract.ensures { + let ens_span = this.lower_span(ens.span); + // Set up the postcondition `let` statement. + let check_ident: Ident = + Ident::from_str_and_span("__ensures_checker", ens_span); + let (checker_pat, check_hir_id) = this.pat_ident_binding_mode_mut( + ens_span, + check_ident, + hir::BindingMode::NONE, + ); + let lowered_ens = this.lower_expr_mut(&ens); + let postcond_checker = this.expr_call_lang_item_fn( + ens_span, + hir::LangItem::ContractBuildCheckEnsures, + &*arena_vec![this; lowered_ens], + ); + let postcond = this.stmt_let_pat( + None, + ens_span, + Some(postcond_checker), + this.arena.alloc(checker_pat), + hir::LocalSource::Contract, + ); + + // Install contract_ensures so we will intercept `return` statements, + // then lower the body. + this.contract_ensures = Some((ens_span, check_ident, check_hir_id)); + let body = this.arena.alloc(body(this)); + + // Finally, inject an ensures check on the implicit return of the body. + let body = this.inject_ensures_check(body, ens_span, check_ident, check_hir_id); + (Some(postcond), body) + } else { + let body = &*this.arena.alloc(body(this)); + (None, body) + }; + // Flatten the body into precond, then postcond, then wrapped body. + let wrapped_body = this.block_all( + body.span, + this.arena.alloc_from_iter([precond, postcond].into_iter().flatten()), + Some(body), + ); + (params, this.expr_block(wrapped_body)) + } else { + (params, body(this)) + } }) } fn lower_fn_body_block( &mut self, - span: Span, decl: &FnDecl, - body: Option<&Block>, + body: &Block, + contract: Option<&FnContract>, ) -> hir::BodyId { - self.lower_fn_body(decl, |this| this.lower_block_expr_opt(span, body)) - } - - fn lower_block_expr_opt(&mut self, span: Span, block: Option<&Block>) -> hir::Expr<'hir> { - match block { - Some(block) => self.lower_block_expr(block), - None => self.expr_err(span, self.dcx().has_errors().unwrap()), - } + self.lower_fn_body(decl, contract, |this| this.lower_block_expr(body)) } pub(super) fn lower_const_body(&mut self, span: Span, expr: Option<&Expr>) -> hir::BodyId { self.lower_body(|this| { - (&[], match expr { - Some(expr) => this.lower_expr_mut(expr), - None => this.expr_err(span, this.dcx().span_delayed_bug(span, "no block")), - }) + ( + &[], + match expr { + Some(expr) => this.lower_expr_mut(expr), + None => this.expr_err(span, this.dcx().span_delayed_bug(span, "no block")), + }, + ) }) } @@ -1089,10 +1170,41 @@ impl<'hir> LoweringContext<'_, 'hir> { decl: &FnDecl, coroutine_kind: Option, body: Option<&Block>, + attrs: &'hir [hir::Attribute], + contract: Option<&FnContract>, ) -> hir::BodyId { - let (Some(coroutine_kind), Some(body)) = (coroutine_kind, body) else { - return self.lower_fn_body_block(span, decl, body); + let Some(body) = body else { + // Functions without a body are an error, except if this is an intrinsic. For those we + // create a fake body so that the entire rest of the compiler doesn't have to deal with + // this as a special case. + return self.lower_fn_body(decl, contract, |this| { + if attrs.iter().any(|a| a.name_or_empty() == sym::rustc_intrinsic) { + let span = this.lower_span(span); + let empty_block = hir::Block { + hir_id: this.next_id(), + stmts: &[], + expr: None, + rules: hir::BlockCheckMode::DefaultBlock, + span, + targeted_by_break: false, + }; + let loop_ = hir::ExprKind::Loop( + this.arena.alloc(empty_block), + None, + hir::LoopSource::Loop, + span, + ); + hir::Expr { hir_id: this.next_id(), kind: loop_, span } + } else { + this.expr_err(span, this.dcx().has_errors().unwrap()) + } + }); + }; + let Some(coroutine_kind) = coroutine_kind else { + // Typical case: not a coroutine. + return self.lower_fn_body_block(decl, body, contract); }; + // FIXME(contracts): Support contracts on async fn. self.lower_body(|this| { let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments( decl, @@ -1319,8 +1431,9 @@ impl<'hir> LoweringContext<'_, 'hir> { id: NodeId, kind: FnDeclKind, coroutine_kind: Option, + attrs: &[hir::Attribute], ) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) { - let header = self.lower_fn_header(sig.header, hir::Safety::Safe); + let header = self.lower_fn_header(sig.header, hir::Safety::Safe, attrs); let itctx = ImplTraitContext::Universal; let (generics, decl) = self.lower_generics(generics, id, itctx, |this| { this.lower_fn_decl(&sig.decl, id, sig.span, kind, coroutine_kind) @@ -1332,37 +1445,56 @@ impl<'hir> LoweringContext<'_, 'hir> { &mut self, h: FnHeader, default_safety: hir::Safety, + attrs: &[hir::Attribute], ) -> hir::FnHeader { let asyncness = if let Some(CoroutineKind::Async { span, .. }) = h.coroutine_kind { hir::IsAsync::Async(span) } else { hir::IsAsync::NotAsync }; + + let safety = self.lower_safety(h.safety, default_safety); + + // Treat safe `#[target_feature]` functions as unsafe, but also remember that we did so. + let safety = if attrs.iter().any(|attr| attr.has_name(sym::target_feature)) + && safety.is_safe() + && !self.tcx.sess.target.is_like_wasm + { + hir::HeaderSafety::SafeTargetFeatures + } else { + safety.into() + }; + hir::FnHeader { - safety: self.lower_safety(h.safety, default_safety), + safety, asyncness, constness: self.lower_constness(h.constness), abi: self.lower_extern(h.ext), } } - pub(super) fn lower_abi(&mut self, abi: StrLit) -> abi::Abi { - abi::lookup(abi.symbol_unescaped.as_str()).unwrap_or_else(|err| { - self.error_on_invalid_abi(abi, err); - abi::Abi::Rust - }) + pub(super) fn lower_abi(&mut self, abi_str: StrLit) -> ExternAbi { + let ast::StrLit { symbol_unescaped, span, .. } = abi_str; + let extern_abi = symbol_unescaped.as_str().parse().unwrap_or_else(|_| { + self.error_on_invalid_abi(abi_str); + ExternAbi::Rust + }); + let sess = self.tcx.sess; + let features = self.tcx.features(); + gate_unstable_abi(sess, features, span, extern_abi); + extern_abi } - pub(super) fn lower_extern(&mut self, ext: Extern) -> abi::Abi { + pub(super) fn lower_extern(&mut self, ext: Extern) -> ExternAbi { match ext { - Extern::None => abi::Abi::Rust, - Extern::Implicit(_) => abi::Abi::FALLBACK, + Extern::None => ExternAbi::Rust, + Extern::Implicit(_) => ExternAbi::FALLBACK, Extern::Explicit(abi, _) => self.lower_abi(abi), } } - fn error_on_invalid_abi(&self, abi: StrLit, err: abi::AbiUnsupported) { - let abi_names = abi::enabled_names(self.tcx.features(), abi.span) + fn error_on_invalid_abi(&self, abi: StrLit) { + let abi_names = enabled_names(self.tcx.features(), abi.span) .iter() .map(|s| Symbol::intern(s)) .collect::>(); @@ -1370,10 +1502,6 @@ impl<'hir> LoweringContext<'_, 'hir> { self.dcx().emit_err(InvalidAbi { abi: abi.symbol_unescaped, span: abi.span, - explain: match err { - abi::AbiUnsupported::Reason { explain } => Some(InvalidAbiReason(explain)), - _ => None, - }, suggestion: suggested_name.map(|suggested_name| InvalidAbiSuggestion { span: abi.span, suggestion: format!("\"{suggested_name}\""), diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 46e91636cfbbb..facc9414b20d5 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -35,18 +35,21 @@ #![doc(rust_logo)] #![feature(assert_matches)] #![feature(box_patterns)] +#![feature(if_let_guard)] #![feature(let_chains)] #![feature(rustdoc_internals)] #![warn(unreachable_pub)] // tidy-alphabetical-end +use std::sync::Arc; + use rustc_ast::node_id::NodeMap; use rustc_ast::{self as ast, *}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::Lrc; +use rustc_data_structures::tagged_ptr::TaggedRef; use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey}; use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId}; @@ -81,6 +84,7 @@ mod index; mod item; mod pat; mod path; +pub mod stability; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } @@ -98,6 +102,8 @@ struct LoweringContext<'a, 'hir> { /// Collect items that were created by lowering the current owner. children: Vec<(LocalDefId, hir::MaybeOwner<'hir>)>, + contract_ensures: Option<(Span, Ident, HirId)>, + coroutine_kind: Option, /// When inside an `async` context, this is the `HirId` of the @@ -127,11 +133,11 @@ struct LoweringContext<'a, 'hir> { #[cfg(debug_assertions)] node_id_to_local_id: NodeMap, - allow_try_trait: Lrc<[Symbol]>, - allow_gen_future: Lrc<[Symbol]>, - allow_async_iterator: Lrc<[Symbol]>, - allow_for_await: Lrc<[Symbol]>, - allow_async_fn_traits: Lrc<[Symbol]>, + allow_try_trait: Arc<[Symbol]>, + allow_gen_future: Arc<[Symbol]>, + allow_async_iterator: Arc<[Symbol]>, + allow_for_await: Arc<[Symbol]>, + allow_async_fn_traits: Arc<[Symbol]>, } impl<'a, 'hir> LoweringContext<'a, 'hir> { @@ -146,6 +152,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { bodies: Vec::new(), attrs: SortedMap::default(), children: Vec::default(), + contract_ensures: None, current_hir_id_owner: hir::CRATE_OWNER_ID, item_local_id_counter: hir::ItemLocalId::ZERO, ident_and_label_to_local_id: Default::default(), @@ -416,10 +423,10 @@ fn compute_hir_hash( pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> { let sess = tcx.sess; // Queries that borrow `resolver_for_lowering`. - tcx.ensure_with_value().output_filenames(()); - tcx.ensure_with_value().early_lint_checks(()); - tcx.ensure_with_value().debugger_visualizers(LOCAL_CRATE); - tcx.ensure_with_value().get_lang_items(()); + tcx.ensure_done().output_filenames(()); + tcx.ensure_done().early_lint_checks(()); + tcx.ensure_done().debugger_visualizers(LOCAL_CRATE); + tcx.ensure_done().get_lang_items(()); let (mut resolver, krate) = tcx.resolver_for_lowering().steal(); let ast_index = index_crate(&resolver.node_id_to_def_id, &krate); @@ -720,7 +727,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &self, reason: DesugaringKind, span: Span, - allow_internal_unstable: Option>, + allow_internal_unstable: Option>, ) -> Span { self.tcx.with_stable_hashing_context(|hcx| { span.mark_with_reason(allow_internal_unstable, reason, span.edition(), hcx) @@ -832,12 +839,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let was_in_loop_condition = self.is_in_loop_condition; self.is_in_loop_condition = false; + let old_contract = self.contract_ensures.take(); + let catch_scope = self.catch_scope.take(); let loop_scope = self.loop_scope.take(); let ret = f(self); self.catch_scope = catch_scope; self.loop_scope = loop_scope; + self.contract_ensures = old_contract; + self.is_in_loop_condition = was_in_loop_condition; self.current_item = current_item; @@ -1082,17 +1093,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { match arg { ast::GenericArg::Lifetime(lt) => GenericArg::Lifetime(self.lower_lifetime(lt)), ast::GenericArg::Type(ty) => { + // We cannot just match on `TyKind::Infer` as `(_)` is represented as + // `TyKind::Paren(TyKind::Infer)` and should also be lowered to `GenericArg::Infer` + if ty.is_maybe_parenthesised_infer() { + return GenericArg::Infer(hir::InferArg { + hir_id: self.lower_node_id(ty.id), + span: self.lower_span(ty.span), + }); + } + match &ty.kind { - TyKind::Infer if self.tcx.features().generic_arg_infer() => { - return GenericArg::Infer(hir::InferArg { - hir_id: self.lower_node_id(ty.id), - span: self.lower_span(ty.span), - }); - } // We parse const arguments as path types as we cannot distinguish them during // parsing. We try to resolve that ambiguity by attempting resolution in both the // type and value namespaces. If we resolved the path in the value namespace, we // transform it into a generic const argument. + // + // FIXME: Should we be handling `(PATH_TO_CONST)`? TyKind::Path(None, path) => { if let Some(res) = self .resolver @@ -1109,15 +1125,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let ct = self.lower_const_path_to_const_arg(path, res, ty.id, ty.span); - return GenericArg::Const(ct); + return GenericArg::Const(ct.try_as_ambig_ct().unwrap()); } } } _ => {} } - GenericArg::Type(self.lower_ty(ty, itctx)) + GenericArg::Type(self.lower_ty(ty, itctx).try_as_ambig_ty().unwrap()) + } + ast::GenericArg::Const(ct) => { + GenericArg::Const(self.lower_anon_const_to_const_arg(ct).try_as_ambig_ct().unwrap()) } - ast::GenericArg::Const(ct) => GenericArg::Const(self.lower_anon_const_to_const_arg(ct)), } } @@ -1157,7 +1175,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let lifetime_bound = this.elided_dyn_bound(t.span); (bounds, lifetime_bound) }); - let kind = hir::TyKind::TraitObject(bounds, lifetime_bound, TraitObjectSyntax::None); + let kind = hir::TyKind::TraitObject( + bounds, + TaggedRef::new(lifetime_bound, TraitObjectSyntax::None), + ); return hir::Ty { kind, span: self.lower_span(t.span), hir_id: self.next_id() }; } @@ -1184,7 +1205,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_ty_direct(&mut self, t: &Ty, itctx: ImplTraitContext) -> hir::Ty<'hir> { let kind = match &t.kind { - TyKind::Infer => hir::TyKind::Infer, + TyKind::Infer => hir::TyKind::Infer(()), TyKind::Err(guar) => hir::TyKind::Err(*guar), TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)), TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)), @@ -1308,7 +1329,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { lifetime_bound.unwrap_or_else(|| this.elided_dyn_bound(t.span)); (bounds, lifetime_bound) }); - hir::TyKind::TraitObject(bounds, lifetime_bound, *kind) + hir::TyKind::TraitObject(bounds, TaggedRef::new(lifetime_bound, *kind)) } TyKind::ImplTrait(def_node_id, bounds) => { let span = t.span; @@ -1365,7 +1386,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } } - TyKind::Pat(ty, pat) => hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_pat(pat)), + TyKind::Pat(ty, pat) => { + hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_ty_pat(pat)) + } TyKind::MacCall(_) => { span_bug!(t.span, "`TyKind::MacCall` should have been expanded by now") } @@ -1652,7 +1675,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { CoroutineKind::Async { return_impl_trait_id, .. } => (return_impl_trait_id, None), CoroutineKind::Gen { return_impl_trait_id, .. } => (return_impl_trait_id, None), CoroutineKind::AsyncGen { return_impl_trait_id, .. } => { - (return_impl_trait_id, Some(Lrc::clone(&self.allow_async_iterator))) + (return_impl_trait_id, Some(Arc::clone(&self.allow_async_iterator))) } }; @@ -2031,11 +2054,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_array_length_to_const_arg(&mut self, c: &AnonConst) -> &'hir hir::ConstArg<'hir> { match c.value.kind { ExprKind::Underscore => { - if self.tcx.features().generic_arg_infer() { - let ct_kind = hir::ConstArgKind::Infer(self.lower_span(c.value.span)); - self.arena - .alloc(hir::ConstArg { hir_id: self.lower_node_id(c.id), kind: ct_kind }) - } else { + if !self.tcx.features().generic_arg_infer() { feature_err( &self.tcx.sess, sym::generic_arg_infer, @@ -2043,8 +2062,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fluent_generated::ast_lowering_underscore_array_length_unstable, ) .stash(c.value.span, StashKey::UnderscoreForArrayLengths); - self.lower_anon_const_to_const_arg(c) } + let ct_kind = hir::ConstArgKind::Infer(self.lower_span(c.value.span), ()); + self.arena.alloc(hir::ConstArg { hir_id: self.lower_node_id(c.id), kind: ct_kind }) } _ => self.lower_anon_const_to_const_arg(c), } @@ -2367,8 +2387,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir_id = self.next_id(); hir::TyKind::TraitObject( arena_vec![self; principal], - self.elided_dyn_bound(span), - TraitObjectSyntax::None, + TaggedRef::new(self.elided_dyn_bound(span), TraitObjectSyntax::None), ) } _ => hir::TyKind::Path(hir::QPath::Resolved(None, path)), diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index a4ab2561b721b..e1f3afbcf5908 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -1,9 +1,12 @@ +use std::sync::Arc; + use rustc_ast::ptr::P; use rustc_ast::*; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; use rustc_hir::def::Res; -use rustc_span::source_map::Spanned; +use rustc_middle::span_bug; +use rustc_span::source_map::{Spanned, respan}; use rustc_span::{Ident, Span}; use super::errors::{ @@ -35,8 +38,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { lower_sub, ); } - PatKind::Lit(e) => { - break hir::PatKind::Lit(self.lower_expr_within_pat(e, false)); + PatKind::Expr(e) => { + break hir::PatKind::Expr(self.lower_expr_within_pat(e, false)); } PatKind::TupleStruct(qself, path, pats) => { let qpath = self.lower_qpath( @@ -66,7 +69,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ImplTraitContext::Disallowed(ImplTraitPosition::Path), None, ); - break hir::PatKind::Path(qpath); + let kind = hir::PatExprKind::Path(qpath); + let span = self.lower_span(pattern.span); + let expr = hir::PatExpr { hir_id: pat_hir_id, span, kind }; + let expr = self.arena.alloc(expr); + return hir::Pat { + hir_id: self.next_id(), + kind: hir::PatKind::Expr(expr), + span, + default_binding_modes: true, + }; } PatKind::Struct(qself, path, fields, etc) => { let qpath = self.lower_qpath( @@ -120,8 +132,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.lower_range_end(end, e2.is_some()), ); } - // FIXME(guard_patterns): lower pattern guards to HIR - PatKind::Guard(inner, _) => pattern = inner, + PatKind::Guard(inner, cond) => { + break hir::PatKind::Guard(self.lower_pat(inner), self.lower_expr(cond)); + } PatKind::Slice(pats) => break self.lower_pat_slice(pats), PatKind::Rest => { // If we reach here the `..` pattern is not semantically allowed. @@ -300,16 +313,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) } Some(res) => { - let hir_id = self.next_id(); let res = self.lower_res(res); - hir::PatKind::Path(hir::QPath::Resolved( - None, - self.arena.alloc(hir::Path { - span: self.lower_span(ident.span), - res, - segments: arena_vec![self; hir::PathSegment::new(self.lower_ident(ident), hir_id, res)], - }), - )) + let span = self.lower_span(ident.span); + hir::PatKind::Expr(self.arena.alloc(hir::PatExpr { + kind: hir::PatExprKind::Path(hir::QPath::Resolved( + None, + self.arena.alloc(hir::Path { + span, + res, + segments: arena_vec![self; hir::PathSegment::new(self.lower_ident(ident), self.next_id(), res)], + }), + )), + hir_id: self.next_id(), + span, + })) } } } @@ -366,24 +383,69 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // } // m!(S); // ``` - fn lower_expr_within_pat(&mut self, expr: &Expr, allow_paths: bool) -> &'hir hir::Expr<'hir> { - match &expr.kind { - ExprKind::Lit(..) - | ExprKind::ConstBlock(..) - | ExprKind::IncludedBytes(..) - | ExprKind::Err(_) - | ExprKind::Dummy => {} - ExprKind::Path(..) if allow_paths => {} - ExprKind::Unary(UnOp::Neg, inner) if matches!(inner.kind, ExprKind::Lit(_)) => {} + fn lower_expr_within_pat( + &mut self, + expr: &Expr, + allow_paths: bool, + ) -> &'hir hir::PatExpr<'hir> { + let span = self.lower_span(expr.span); + let err = |guar| hir::PatExprKind::Lit { + lit: self.arena.alloc(respan(span, LitKind::Err(guar))), + negated: false, + }; + let kind = match &expr.kind { + ExprKind::Lit(lit) => { + hir::PatExprKind::Lit { lit: self.lower_lit(lit, span), negated: false } + } + ExprKind::ConstBlock(c) => hir::PatExprKind::ConstBlock(self.lower_const_block(c)), + ExprKind::IncludedBytes(bytes) => hir::PatExprKind::Lit { + lit: self + .arena + .alloc(respan(span, LitKind::ByteStr(Arc::clone(bytes), StrStyle::Cooked))), + negated: false, + }, + ExprKind::Err(guar) => err(*guar), + ExprKind::Dummy => span_bug!(span, "lowered ExprKind::Dummy"), + ExprKind::Path(qself, path) if allow_paths => hir::PatExprKind::Path(self.lower_qpath( + expr.id, + qself, + path, + ParamMode::Optional, + AllowReturnTypeNotation::No, + ImplTraitContext::Disallowed(ImplTraitPosition::Path), + None, + )), + ExprKind::Unary(UnOp::Neg, inner) if let ExprKind::Lit(lit) = &inner.kind => { + hir::PatExprKind::Lit { lit: self.lower_lit(lit, span), negated: true } + } _ => { let pattern_from_macro = expr.is_approximately_pattern(); let guar = self.dcx().emit_err(ArbitraryExpressionInPattern { - span: expr.span, + span, pattern_from_macro_note: pattern_from_macro, }); - return self.arena.alloc(self.expr_err(expr.span, guar)); + err(guar) } - } - self.lower_expr(expr) + }; + self.arena.alloc(hir::PatExpr { hir_id: self.lower_node_id(expr.id), span, kind }) + } + + pub(crate) fn lower_ty_pat(&mut self, pattern: &TyPat) -> &'hir hir::TyPat<'hir> { + self.arena.alloc(self.lower_ty_pat_mut(pattern)) + } + + fn lower_ty_pat_mut(&mut self, pattern: &TyPat) -> hir::TyPat<'hir> { + // loop here to avoid recursion + let pat_hir_id = self.lower_node_id(pattern.id); + let node = match &pattern.kind { + TyPatKind::Range(e1, e2, Spanned { node: end, .. }) => hir::TyPatKind::Range( + e1.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)), + e2.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)), + self.lower_range_end(end, e2.is_some()), + ), + TyPatKind::Err(guar) => hir::TyPatKind::Err(*guar), + }; + + hir::TyPat { hir_id: pat_hir_id, kind: node, span: self.lower_span(pattern.span) } } } diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 043144a54649b..55d0647731324 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -1,5 +1,6 @@ +use std::sync::Arc; + use rustc_ast::{self as ast, *}; -use rustc_data_structures::sync::Lrc; use rustc_hir as hir; use rustc_hir::GenericArg; use rustc_hir::def::{DefKind, PartialRes, Res}; @@ -72,7 +73,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let bound_modifier_allowed_features = if let Res::Def(DefKind::Trait, async_def_id) = res && self.tcx.async_fn_trait_kind_from_def_id(async_def_id).is_some() { - Some(Lrc::clone(&self.allow_async_fn_traits)) + Some(Arc::clone(&self.allow_async_fn_traits)) } else { None }; @@ -257,7 +258,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Additional features ungated with a bound modifier like `async`. // This is passed down to the implicit associated type binding in // parenthesized bounds. - bound_modifier_allowed_features: Option>, + bound_modifier_allowed_features: Option>, ) -> hir::PathSegment<'hir> { debug!("path_span: {:?}, lower_path_segment(segment: {:?})", path_span, segment); let (mut generic_args, infer_args) = if let Some(generic_args) = segment.args.as_deref() { @@ -490,7 +491,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &mut self, data: &ParenthesizedArgs, itctx: ImplTraitContext, - bound_modifier_allowed_features: Option>, + bound_modifier_allowed_features: Option>, ) -> (GenericArgsCtor<'hir>, bool) { // Switch to `PassThrough` mode for anonymous lifetimes; this // means that we permit things like `&Ref`, where `Ref` has @@ -525,7 +526,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } FnRetTy::Default(_) => self.arena.alloc(self.ty_tup(*span, &[])), }; - let args = smallvec![GenericArg::Type(self.arena.alloc(self.ty_tup(*inputs_span, inputs)))]; + let args = smallvec![GenericArg::Type( + self.arena.alloc(self.ty_tup(*inputs_span, inputs)).try_as_ambig_ty().unwrap() + )]; // If we have a bound like `async Fn() -> T`, make sure that we mark the // `Output = T` associated type bound with the right feature gates. diff --git a/compiler/rustc_ast_lowering/src/stability.rs b/compiler/rustc_ast_lowering/src/stability.rs new file mode 100644 index 0000000000000..a2004bbb39f09 --- /dev/null +++ b/compiler/rustc_ast_lowering/src/stability.rs @@ -0,0 +1,142 @@ +use std::fmt; + +use rustc_abi::ExternAbi; +use rustc_feature::Features; +use rustc_session::Session; +use rustc_session::parse::feature_err; +use rustc_span::symbol::sym; +use rustc_span::{Span, Symbol}; + +pub(crate) fn enabled_names(features: &rustc_feature::Features, span: Span) -> Vec<&'static str> { + ExternAbi::ALL_VARIANTS + .into_iter() + .filter(|abi| extern_abi_enabled(features, span, **abi).is_ok()) + .map(|abi| abi.as_str()) + .collect() +} + +pub(crate) fn extern_abi_enabled( + features: &rustc_feature::Features, + span: Span, + abi: ExternAbi, +) -> Result<(), UnstableAbi> { + extern_abi_stability(abi).or_else(|unstable @ UnstableAbi { feature, .. }| { + if features.enabled(feature) || span.allows_unstable(feature) { + Ok(()) + } else { + Err(unstable) + } + }) +} + +#[allow(rustc::untranslatable_diagnostic)] +pub(crate) fn gate_unstable_abi(sess: &Session, features: &Features, span: Span, abi: ExternAbi) { + match extern_abi_enabled(features, span, abi) { + Ok(_) => (), + Err(unstable_abi) => { + let explain = unstable_abi.to_string(); + feature_err(sess, unstable_abi.feature, span, explain).emit(); + } + } +} + +pub struct UnstableAbi { + abi: ExternAbi, + feature: Symbol, + explain: GateReason, +} + +enum GateReason { + Experimental, + ImplDetail, +} + +impl fmt::Display for UnstableAbi { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { abi, .. } = self; + match self.explain { + GateReason::Experimental => { + write!(f, "the extern {abi} ABI is experimental and subject to change") + } + GateReason::ImplDetail => { + write!(f, "the extern {abi} ABI is an implementation detail and perma-unstable") + } + } + } +} + +pub fn extern_abi_stability(abi: ExternAbi) -> Result<(), UnstableAbi> { + match abi { + // stable ABIs + ExternAbi::Rust + | ExternAbi::C { .. } + | ExternAbi::Cdecl { .. } + | ExternAbi::Stdcall { .. } + | ExternAbi::Fastcall { .. } + | ExternAbi::Thiscall { .. } + | ExternAbi::Aapcs { .. } + | ExternAbi::Win64 { .. } + | ExternAbi::SysV64 { .. } + | ExternAbi::System { .. } + | ExternAbi::EfiApi => Ok(()), + // implementation details + ExternAbi::RustIntrinsic => { + Err(UnstableAbi { abi, feature: sym::intrinsics, explain: GateReason::ImplDetail }) + } + ExternAbi::Unadjusted => { + Err(UnstableAbi { abi, feature: sym::abi_unadjusted, explain: GateReason::ImplDetail }) + } + // experimental + ExternAbi::Vectorcall { .. } => Err(UnstableAbi { + abi, + feature: sym::abi_vectorcall, + explain: GateReason::Experimental, + }), + ExternAbi::RustCall => Err(UnstableAbi { + abi, + feature: sym::unboxed_closures, + explain: GateReason::Experimental, + }), + ExternAbi::RustCold => { + Err(UnstableAbi { abi, feature: sym::rust_cold_cc, explain: GateReason::Experimental }) + } + ExternAbi::GpuKernel => Err(UnstableAbi { + abi, + feature: sym::abi_gpu_kernel, + explain: GateReason::Experimental, + }), + ExternAbi::PtxKernel => { + Err(UnstableAbi { abi, feature: sym::abi_ptx, explain: GateReason::Experimental }) + } + ExternAbi::Msp430Interrupt => Err(UnstableAbi { + abi, + feature: sym::abi_msp430_interrupt, + explain: GateReason::Experimental, + }), + ExternAbi::X86Interrupt => Err(UnstableAbi { + abi, + feature: sym::abi_x86_interrupt, + explain: GateReason::Experimental, + }), + ExternAbi::AvrInterrupt | ExternAbi::AvrNonBlockingInterrupt => Err(UnstableAbi { + abi, + feature: sym::abi_avr_interrupt, + explain: GateReason::Experimental, + }), + ExternAbi::RiscvInterruptM | ExternAbi::RiscvInterruptS => Err(UnstableAbi { + abi, + feature: sym::abi_riscv_interrupt, + explain: GateReason::Experimental, + }), + ExternAbi::CCmseNonSecureCall => Err(UnstableAbi { + abi, + feature: sym::abi_c_cmse_nonsecure_call, + explain: GateReason::Experimental, + }), + ExternAbi::CCmseNonSecureEntry => Err(UnstableAbi { + abi, + feature: sym::cmse_nonsecure_entry, + explain: GateReason::Experimental, + }), + } +} diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml index 8046765647e8d..e4c227532085f 100644 --- a/compiler/rustc_ast_passes/Cargo.toml +++ b/compiler/rustc_ast_passes/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start itertools = "0.12" +rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } @@ -17,6 +18,5 @@ rustc_macros = { path = "../rustc_macros" } rustc_parse = { path = "../rustc_parse" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } -rustc_target = { path = "../rustc_target" } thin-vec = "0.2.12" # tidy-alphabetical-end diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index d1cf9c53d66e7..f9f4035cb22f0 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -20,6 +20,7 @@ use std::mem; use std::ops::{Deref, DerefMut}; use itertools::{Either, Itertools}; +use rustc_abi::ExternAbi; use rustc_ast::ptr::P; use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, walk_list}; use rustc_ast::*; @@ -35,7 +36,6 @@ use rustc_session::lint::builtin::{ }; use rustc_session::lint::{BuiltinLintDiag, LintBuffer}; use rustc_span::{Ident, Span, kw, sym}; -use rustc_target::spec::abi; use thin_vec::thin_vec; use crate::errors::{self, TildeConstReason}; @@ -723,7 +723,7 @@ impl<'a> AstValidator<'a> { MISSING_ABI, id, span, - BuiltinLintDiag::MissingAbi(span, abi::Abi::FALLBACK), + BuiltinLintDiag::MissingAbi(span, ExternAbi::FALLBACK), ) } } @@ -917,10 +917,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> { walk_list!(self, visit_attribute, &item.attrs); return; // Avoid visiting again. } - ItemKind::Fn(box Fn { defaultness, sig, generics, body }) => { + ItemKind::Fn(func @ box Fn { defaultness, generics: _, sig, contract: _, body }) => { self.check_defaultness(item.span, *defaultness); - if body.is_none() { + let is_intrinsic = + item.attrs.iter().any(|a| a.name_or_empty() == sym::rustc_intrinsic); + if body.is_none() && !is_intrinsic { self.dcx().emit_err(errors::FnWithoutBody { span: item.span, replace_span: self.ending_semi_or_hi(item.span), @@ -945,7 +947,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.visit_vis(&item.vis); self.visit_ident(&item.ident); - let kind = FnKind::Fn(FnCtxt::Free, &item.ident, sig, &item.vis, generics, body); + let kind = FnKind::Fn(FnCtxt::Free, &item.ident, &item.vis, &*func); self.visit_fn(kind, item.span, item.id); walk_list!(self, visit_attribute, &item.attrs); return; // Avoid visiting again. @@ -1346,19 +1348,20 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } if let FnKind::Fn( - _, - _, - FnSig { header: FnHeader { ext: Extern::Implicit(extern_span), .. }, .. }, _, _, _, + Fn { + sig: FnSig { header: FnHeader { ext: Extern::Implicit(extern_span), .. }, .. }, + .. + }, ) = fk { self.maybe_lint_missing_abi(*extern_span, id); } // Functions without bodies cannot have patterns. - if let FnKind::Fn(ctxt, _, sig, _, _, None) = fk { + if let FnKind::Fn(ctxt, _, _, Fn { body: None, sig, .. }) = fk { Self::check_decl_no_pat(&sig.decl, |span, ident, mut_ident| { if mut_ident && matches!(ctxt, FnCtxt::Assoc(_)) { if let Some(ident) = ident { @@ -1392,7 +1395,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { .is_some(); let disallowed = (!tilde_const_allowed).then(|| match fk { - FnKind::Fn(_, ident, _, _, _, _) => TildeConstReason::Function { ident: ident.span }, + FnKind::Fn(_, ident, _, _) => TildeConstReason::Function { ident: ident.span }, FnKind::Closure(..) => TildeConstReason::Closure, }); self.with_tilde_const(disallowed, |this| visit::walk_fn(this, fk)); @@ -1468,15 +1471,14 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.outer_trait_or_trait_impl.as_ref().and_then(TraitOrTraitImpl::constness).is_some(); match &item.kind { - AssocItemKind::Fn(box Fn { sig, generics, body, .. }) + AssocItemKind::Fn(func) if parent_is_const || ctxt == AssocCtxt::Trait - || matches!(sig.header.constness, Const::Yes(_)) => + || matches!(func.sig.header.constness, Const::Yes(_)) => { self.visit_vis(&item.vis); self.visit_ident(&item.ident); - let kind = - FnKind::Fn(FnCtxt::Assoc(ctxt), &item.ident, sig, &item.vis, generics, body); + let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), &item.ident, &item.vis, &*func); walk_list!(self, visit_attribute, &item.attrs); self.visit_fn(kind, item.span, item.id); } diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 3fbf121018615..e5d8013058f65 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -1,12 +1,11 @@ use rustc_ast as ast; use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; use rustc_ast::{NodeId, PatKind, attr, token}; -use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features, GateIssue}; +use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features}; use rustc_session::Session; -use rustc_session::parse::{feature_err, feature_err_issue, feature_warn}; +use rustc_session::parse::{feature_err, feature_warn}; use rustc_span::source_map::Spanned; use rustc_span::{Span, Symbol, sym}; -use rustc_target::spec::abi; use thin_vec::ThinVec; use crate::errors; @@ -73,35 +72,6 @@ struct PostExpansionVisitor<'a> { } impl<'a> PostExpansionVisitor<'a> { - #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable - fn check_abi(&self, abi: ast::StrLit) { - let ast::StrLit { symbol_unescaped, span, .. } = abi; - - match abi::is_enabled(self.features, span, symbol_unescaped.as_str()) { - Ok(()) => (), - Err(abi::AbiDisabled::Unstable { feature, explain }) => { - feature_err_issue(&self.sess, feature, span, GateIssue::Language, explain).emit(); - } - Err(abi::AbiDisabled::Unrecognized) => { - if self.sess.opts.pretty.map_or(true, |ppm| ppm.needs_hir()) { - self.sess.dcx().span_delayed_bug( - span, - format!( - "unrecognized ABI not caught in lowering: {}", - symbol_unescaped.as_str() - ), - ); - } - } - } - } - - fn check_extern(&self, ext: ast::Extern) { - if let ast::Extern::Explicit(abi, _) = ext { - self.check_abi(abi); - } - } - /// Feature gate `impl Trait` inside `type Alias = $type_expr;`. fn check_impl_trait(&self, ty: &ast::Ty, in_associated_ty: bool) { struct ImplTraitVisitor<'a> { @@ -224,25 +194,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { fn visit_item(&mut self, i: &'a ast::Item) { match &i.kind { - ast::ItemKind::ForeignMod(foreign_module) => { - if let Some(abi) = foreign_module.abi { - self.check_abi(abi); - } + ast::ItemKind::ForeignMod(_foreign_module) => { + // handled during lowering } - - ast::ItemKind::Fn(..) => { - if attr::contains_name(&i.attrs, sym::start) { - gate!( - &self, - start, - i.span, - "`#[start]` functions are experimental and their signature may change \ - over time" - ); - } - } - - ast::ItemKind::Struct(..) => { + ast::ItemKind::Struct(..) | ast::ItemKind::Enum(..) | ast::ItemKind::Union(..) => { for attr in attr::filter_by_name(&i.attrs, sym::repr) { for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) { if item.has_name(sym::simd) { @@ -328,7 +283,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { match &ty.kind { ast::TyKind::BareFn(bare_fn_ty) => { // Function pointers cannot be `const` - self.check_extern(bare_fn_ty.ext); self.check_late_bound_lifetime_defs(&bare_fn_ty.generic_params); } ast::TyKind::Never => { @@ -431,9 +385,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) { - if let Some(header) = fn_kind.header() { + if let Some(_header) = fn_kind.header() { // Stability of const fn methods are covered in `visit_assoc_item` below. - self.check_extern(header.ext); } if let FnKind::Closure(ast::ClosureBinder::For { generic_params, .. }, ..) = fn_kind { @@ -560,6 +513,8 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(pin_ergonomics, "pinned reference syntax is experimental"); gate_all!(unsafe_fields, "`unsafe` fields are experimental"); gate_all!(unsafe_binders, "unsafe binder types are experimental"); + gate_all!(contracts, "contracts are incomplete"); + gate_all!(contracts_internals, "contract internal machinery is for internal use only"); if !visitor.features.never_patterns() { if let Some(spans) = spans.get(&sym::never_patterns) { @@ -692,7 +647,7 @@ fn check_new_solver_banned_features(sess: &Session, features: &Features) { .find(|feat| feat.gate_name == sym::generic_const_exprs) .map(|feat| feat.attr_sp) { - #[cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] + #[allow(rustc::symbol_intern_string_literal)] sess.dcx().emit_err(errors::IncompatibleFeatures { spans: vec![gce_span], f1: Symbol::intern("-Znext-solver=globally"), diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 172df10292920..0bf5de3ef8985 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -7,6 +7,7 @@ mod fixup; mod item; use std::borrow::Cow; +use std::sync::Arc; use rustc_ast::attr::AttrIdGenerator; use rustc_ast::ptr::P; @@ -21,7 +22,6 @@ use rustc_ast::{ InlineAsmOperand, InlineAsmOptions, InlineAsmRegOrRegClass, InlineAsmTemplatePiece, PatKind, RangeEnd, RangeSyntax, Safety, SelfKind, Term, attr, }; -use rustc_data_structures::sync::Lrc; use rustc_span::edition::Edition; use rustc_span::source_map::{SourceMap, Spanned}; use rustc_span::symbol::IdentPrinter; @@ -106,7 +106,7 @@ fn split_block_comment_into_lines(text: &str, col: CharPos) -> Vec { fn gather_comments(sm: &SourceMap, path: FileName, src: String) -> Vec { let sm = SourceMap::new(sm.path_mapping().clone()); let source_file = sm.new_source_file(path, src); - let text = Lrc::clone(&(*source_file.src.as_ref().unwrap())); + let text = Arc::clone(&(*source_file.src.as_ref().unwrap())); let text: &str = text.as_str(); let start_bpos = source_file.start_pos; @@ -1148,6 +1148,28 @@ impl<'a> State<'a> { } } + pub fn print_ty_pat(&mut self, pat: &ast::TyPat) { + match &pat.kind { + rustc_ast::TyPatKind::Range(start, end, include_end) => { + if let Some(start) = start { + self.print_expr_anon_const(start, &[]); + } + self.word(".."); + if let Some(end) = end { + if let RangeEnd::Included(_) = include_end.node { + self.word("="); + } + self.print_expr_anon_const(end, &[]); + } + } + rustc_ast::TyPatKind::Err(_) => { + self.popen(); + self.word("/*ERROR*/"); + self.pclose(); + } + } + } + pub fn print_type(&mut self, ty: &ast::Ty) { self.maybe_print_comment(ty.span.lo()); self.ibox(0); @@ -1252,7 +1274,7 @@ impl<'a> State<'a> { ast::TyKind::Pat(ty, pat) => { self.print_type(ty); self.word(" is "); - self.print_pat(pat); + self.print_ty_pat(pat); } } self.end(); @@ -1701,7 +1723,7 @@ impl<'a> State<'a> { self.print_pat(inner); } } - PatKind::Lit(e) => self.print_expr(e, FixupContext::default()), + PatKind::Expr(e) => self.print_expr(e, FixupContext::default()), PatKind::Range(begin, end, Spanned { node: end_kind, .. }) => { if let Some(e) = begin { self.print_expr(e, FixupContext::default()); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 897c275d850c2..c10b5ad34e102 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -34,8 +34,8 @@ impl<'a> State<'a> { self.maybe_print_comment(span.lo()); self.print_outer_attributes(attrs); match kind { - ast::ForeignItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { - self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs); + ast::ForeignItemKind::Fn(func) => { + self.print_fn_full(ident, vis, attrs, &*func); } ast::ForeignItemKind::Static(box ast::StaticItem { ty, mutability, expr, safety }) => { self.print_item_const( @@ -199,16 +199,8 @@ impl<'a> State<'a> { *defaultness, ); } - ast::ItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { - self.print_fn_full( - sig, - item.ident, - generics, - &item.vis, - *defaultness, - body.as_deref(), - &item.attrs, - ); + ast::ItemKind::Fn(func) => { + self.print_fn_full(item.ident, &item.vis, &item.attrs, &*func); } ast::ItemKind::Mod(safety, mod_kind) => { self.head(Self::to_string(|s| { @@ -542,8 +534,8 @@ impl<'a> State<'a> { self.maybe_print_comment(span.lo()); self.print_outer_attributes(attrs); match kind { - ast::AssocItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { - self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs); + ast::AssocItemKind::Fn(func) => { + self.print_fn_full(ident, vis, attrs, &*func); } ast::AssocItemKind::Const(box ast::ConstItem { defaultness, generics, ty, expr }) => { self.print_item_const( @@ -653,20 +645,22 @@ impl<'a> State<'a> { fn print_fn_full( &mut self, - sig: &ast::FnSig, name: Ident, - generics: &ast::Generics, vis: &ast::Visibility, - defaultness: ast::Defaultness, - body: Option<&ast::Block>, attrs: &[ast::Attribute], + func: &ast::Fn, ) { + let ast::Fn { defaultness, generics, sig, contract, body } = func; if body.is_some() { self.head(""); } self.print_visibility(vis); - self.print_defaultness(defaultness); + self.print_defaultness(*defaultness); self.print_fn(&sig.decl, sig.header, Some(name), generics); + if let Some(contract) = &contract { + self.nbsp(); + self.print_contract(contract); + } if let Some(body) = body { self.nbsp(); self.print_block_with_attrs(body, attrs); @@ -675,6 +669,21 @@ impl<'a> State<'a> { } } + fn print_contract(&mut self, contract: &ast::FnContract) { + if let Some(pred) = &contract.requires { + self.word("rustc_requires"); + self.popen(); + self.print_expr(pred, FixupContext::default()); + self.pclose(); + } + if let Some(pred) = &contract.ensures { + self.word("rustc_ensures"); + self.popen(); + self.print_expr(pred, FixupContext::default()); + self.pclose(); + } + } + pub(crate) fn print_fn( &mut self, decl: &ast::FnDecl, diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 8986bec57de76..b4027a096c532 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -11,6 +11,22 @@ pub enum InlineAttr { Hint, Always, Never, + /// `#[rustc_force_inline]` forces inlining to happen in the MIR inliner - it reports an error + /// if the inlining cannot happen. It is limited to only free functions so that the calls + /// can always be resolved. + Force { + attr_span: Span, + reason: Option, + }, +} + +impl InlineAttr { + pub fn always(&self) -> bool { + match self { + InlineAttr::Always | InlineAttr::Force { .. } => true, + InlineAttr::None | InlineAttr::Hint | InlineAttr::Never => false, + } + } } #[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic)] @@ -19,13 +35,25 @@ pub enum InstructionSetAttr { ArmT32, } -#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] +#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic, Default)] pub enum OptimizeAttr { - None, + /// No `#[optimize(..)]` attribute + #[default] + Default, + /// `#[optimize(none)]` + DoNotOptimize, + /// `#[optimize(speed)]` Speed, + /// `#[optimize(size)]` Size, } +impl OptimizeAttr { + pub fn do_not_optimize(&self) -> bool { + matches!(self, Self::DoNotOptimize) + } +} + #[derive(Clone, Debug, Encodable, Decodable)] pub enum DiagnosticAttribute { // tidy-alphabetical-start diff --git a/compiler/rustc_attr_data_structures/src/stability.rs b/compiler/rustc_attr_data_structures/src/stability.rs index 021fe40e3e04c..c2213fc9ed881 100644 --- a/compiler/rustc_attr_data_structures/src/stability.rs +++ b/compiler/rustc_attr_data_structures/src/stability.rs @@ -9,7 +9,12 @@ use crate::RustcVersion; /// `since` field of the `#[stable]` attribute. /// /// For more, see [this pull request](https://github.com/rust-lang/rust/pull/100591). -pub const VERSION_PLACEHOLDER: &str = "CURRENT_RUSTC_VERSION"; +pub const VERSION_PLACEHOLDER: &str = concat!("CURRENT_RUSTC_VERSIO", "N"); +// Note that the `concat!` macro above prevents `src/tools/replace-version-placeholder` from +// replacing the constant with the current version. Hardcoding the tool to skip this file doesn't +// work as the file can (and at some point will) be moved around. +// +// Turning the `concat!` macro into a string literal will make Pietro cry. That'd be sad :( /// Represents the following attributes: /// @@ -132,9 +137,9 @@ pub enum StabilityLevel { Stable { /// Rust release which stabilized this feature. since: StableSince, - /// Is this item allowed to be referred to on stable, despite being contained in unstable - /// modules? - allowed_through_unstable_modules: bool, + /// This is `Some` if this item allowed to be referred to on stable via unstable modules; + /// the `Symbol` is the deprecation message printed in that case. + allowed_through_unstable_modules: Option, }, } diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index 124f0aa3effa1..28c381160b874 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -166,7 +166,7 @@ pub fn parse_repr_attr(sess: &Session, attr: &impl AttributeExt) -> Vec Option<(Stability, Span)> { let mut stab: Option<(Stability, Span)> = None; - let mut allowed_through_unstable_modules = false; + let mut allowed_through_unstable_modules = None; for attr in attrs { match attr.name_or_empty() { - sym::rustc_allowed_through_unstable_modules => allowed_through_unstable_modules = true, + sym::rustc_allowed_through_unstable_modules => { + // The value is mandatory, but avoid ICEs in case such code reaches this function. + allowed_through_unstable_modules = Some(attr.value_str().unwrap_or_else(|| { + sess.dcx().span_delayed_bug( + item_sp, + "`#[rustc_allowed_through_unstable_modules]` without deprecation message", + ); + kw::Empty + })) + } sym::unstable => { if stab.is_some() { sess.dcx().emit_err(session_diagnostics::MultipleStabilityLevels { @@ -56,15 +65,15 @@ pub fn find_stability( } } - if allowed_through_unstable_modules { + if let Some(allowed_through_unstable_modules) = allowed_through_unstable_modules { match &mut stab { Some(( Stability { - level: StabilityLevel::Stable { allowed_through_unstable_modules, .. }, + level: StabilityLevel::Stable { allowed_through_unstable_modules: in_stab, .. }, .. }, _, - )) => *allowed_through_unstable_modules = true, + )) => *in_stab = Some(allowed_through_unstable_modules), _ => { sess.dcx() .emit_err(session_diagnostics::RustcAllowedUnstablePairing { span: item_sp }); @@ -283,7 +292,7 @@ fn parse_stability(sess: &Session, attr: &impl AttributeExt) -> Option<(Symbol, match feature { Ok(feature) => { - let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: false }; + let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: None }; Some((feature, level)) } Err(ErrorGuaranteed { .. }) => None, diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index b1d8ec497740d..92bc2a8aeb05e 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -204,21 +204,27 @@ pub(crate) struct UnsupportedLiteral { impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnsupportedLiteral { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { - let mut diag = Diag::new(dcx, level, match self.reason { - UnsupportedLiteralReason::Generic => fluent::attr_parsing_unsupported_literal_generic, - UnsupportedLiteralReason::CfgString => { - fluent::attr_parsing_unsupported_literal_cfg_string - } - UnsupportedLiteralReason::CfgBoolean => { - fluent::attr_parsing_unsupported_literal_cfg_boolean - } - UnsupportedLiteralReason::DeprecatedString => { - fluent::attr_parsing_unsupported_literal_deprecated_string - } - UnsupportedLiteralReason::DeprecatedKvPair => { - fluent::attr_parsing_unsupported_literal_deprecated_kv_pair - } - }); + let mut diag = Diag::new( + dcx, + level, + match self.reason { + UnsupportedLiteralReason::Generic => { + fluent::attr_parsing_unsupported_literal_generic + } + UnsupportedLiteralReason::CfgString => { + fluent::attr_parsing_unsupported_literal_cfg_string + } + UnsupportedLiteralReason::CfgBoolean => { + fluent::attr_parsing_unsupported_literal_cfg_boolean + } + UnsupportedLiteralReason::DeprecatedString => { + fluent::attr_parsing_unsupported_literal_deprecated_string + } + UnsupportedLiteralReason::DeprecatedKvPair => { + fluent::attr_parsing_unsupported_literal_deprecated_kv_pair + } + }, + ); diag.span(self.span); diag.code(E0565); if self.is_bytestr { diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl index ee4b2f95cb151..ada20e5c614f8 100644 --- a/compiler/rustc_borrowck/messages.ftl +++ b/compiler/rustc_borrowck/messages.ftl @@ -213,6 +213,10 @@ borrowck_suggest_create_fresh_reborrow = borrowck_suggest_iterate_over_slice = consider iterating over a slice of the `{$ty}`'s content to avoid moving into the `for` loop +borrowck_tail_expr_drop_order = relative drop order changing in Rust 2024 + .label = this temporary value will be dropped at the end of the block + .note = consider using a `let` binding to ensure the value will live long enough + borrowck_ty_no_impl_copy = {$is_partial_move -> [true] partial move diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index ff838fbbb8868..303fa469332e0 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -2,7 +2,7 @@ use std::fmt; use std::ops::Index; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::{MutatingUseContext, NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{self, Body, Local, Location, traversal}; use rustc_middle::span_bug; @@ -11,7 +11,6 @@ use rustc_mir_dataflow::move_paths::MoveData; use tracing::debug; use crate::BorrowIndex; -use crate::path_utils::allow_two_phase_borrow; use crate::place_ext::PlaceExt; pub struct BorrowSet<'tcx> { @@ -132,7 +131,7 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> { pub enum LocalsStateAtExit { AllAreInvalidated, - SomeAreInvalidated { has_storage_dead_or_moved: BitSet }, + SomeAreInvalidated { has_storage_dead_or_moved: DenseBitSet }, } impl LocalsStateAtExit { @@ -141,7 +140,7 @@ impl LocalsStateAtExit { body: &Body<'tcx>, move_data: &MoveData<'tcx>, ) -> Self { - struct HasStorageDead(BitSet); + struct HasStorageDead(DenseBitSet); impl<'tcx> Visitor<'tcx> for HasStorageDead { fn visit_local(&mut self, local: Local, ctx: PlaceContext, _: Location) { @@ -154,7 +153,8 @@ impl LocalsStateAtExit { if locals_are_invalidated_at_exit { LocalsStateAtExit::AllAreInvalidated } else { - let mut has_storage_dead = HasStorageDead(BitSet::new_empty(body.local_decls.len())); + let mut has_storage_dead = + HasStorageDead(DenseBitSet::new_empty(body.local_decls.len())); has_storage_dead.visit_body(body); let mut has_storage_dead_or_moved = has_storage_dead.0; for move_out in &move_data.moves { @@ -350,7 +350,7 @@ impl<'a, 'tcx> GatherBorrows<'a, 'tcx> { start_location, assigned_place, borrow_index, ); - if !allow_two_phase_borrow(kind) { + if !kind.allows_two_phase_borrow() { debug!(" -> {:?}", start_location); return; } diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs index 7e8c48a155136..5a89f7c351cf6 100644 --- a/compiler/rustc_borrowck/src/consumers.rs +++ b/compiler/rustc_borrowck/src/consumers.rs @@ -11,8 +11,8 @@ pub use super::dataflow::{BorrowIndex, Borrows, calculate_borrows_out_of_scope_a pub use super::place_ext::PlaceExt; pub use super::places_conflict::{PlaceConflictBias, places_conflict}; pub use super::polonius::legacy::{ - AllFacts as PoloniusInput, LocationTable, PoloniusOutput, PoloniusRegionVid, RichLocation, - RustcFacts, + PoloniusFacts as PoloniusInput, PoloniusLocationTable, PoloniusOutput, PoloniusRegionVid, + RichLocation, RustcFacts, }; pub use super::region_infer::RegionInferenceContext; @@ -33,7 +33,7 @@ pub enum ConsumerOptions { /// without significant slowdowns. /// /// Implies [`RegionInferenceContext`](ConsumerOptions::RegionInferenceContext), - /// and additionally retrieve the [`LocationTable`] and [`PoloniusInput`] that + /// and additionally retrieve the [`PoloniusLocationTable`] and [`PoloniusInput`] that /// would be given to Polonius. Critically, this does not run Polonius, which /// one may want to avoid due to performance issues on large bodies. PoloniusInputFacts, @@ -71,7 +71,7 @@ pub struct BodyWithBorrowckFacts<'tcx> { /// The table that maps Polonius points to locations in the table. /// Populated when using [`ConsumerOptions::PoloniusInputFacts`] /// or [`ConsumerOptions::PoloniusOutputFacts`]. - pub location_table: Option, + pub location_table: Option, /// Polonius input facts. /// Populated when using [`ConsumerOptions::PoloniusInputFacts`] /// or [`ConsumerOptions::PoloniusOutputFacts`]. diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index abe4d4f20ecc5..7511a55b03ae5 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -2,7 +2,7 @@ use std::fmt; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::graph; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::{ self, BasicBlock, Body, CallReturnPlaces, Location, Place, TerminatorEdges, }; @@ -180,26 +180,35 @@ pub struct Borrows<'a, 'tcx> { } struct OutOfScopePrecomputer<'a, 'tcx> { - visited: BitSet, + visited: DenseBitSet, visit_stack: Vec, body: &'a Body<'tcx>, regioncx: &'a RegionInferenceContext<'tcx>, borrows_out_of_scope_at_location: FxIndexMap>, } -impl<'a, 'tcx> OutOfScopePrecomputer<'a, 'tcx> { - fn new(body: &'a Body<'tcx>, regioncx: &'a RegionInferenceContext<'tcx>) -> Self { - OutOfScopePrecomputer { - visited: BitSet::new_empty(body.basic_blocks.len()), +impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> { + fn compute( + body: &Body<'tcx>, + regioncx: &RegionInferenceContext<'tcx>, + borrow_set: &BorrowSet<'tcx>, + ) -> FxIndexMap> { + let mut prec = OutOfScopePrecomputer { + visited: DenseBitSet::new_empty(body.basic_blocks.len()), visit_stack: vec![], body, regioncx, borrows_out_of_scope_at_location: FxIndexMap::default(), + }; + for (borrow_index, borrow_data) in borrow_set.iter_enumerated() { + let borrow_region = borrow_data.region; + let location = borrow_data.reserve_location; + prec.precompute_borrows_out_of_scope(borrow_index, borrow_region, location); } + + prec.borrows_out_of_scope_at_location } -} -impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> { fn precompute_borrows_out_of_scope( &mut self, borrow_index: BorrowIndex, @@ -280,19 +289,11 @@ pub fn calculate_borrows_out_of_scope_at_location<'tcx>( regioncx: &RegionInferenceContext<'tcx>, borrow_set: &BorrowSet<'tcx>, ) -> FxIndexMap> { - let mut prec = OutOfScopePrecomputer::new(body, regioncx); - for (borrow_index, borrow_data) in borrow_set.iter_enumerated() { - let borrow_region = borrow_data.region; - let location = borrow_data.reserve_location; - - prec.precompute_borrows_out_of_scope(borrow_index, borrow_region, location); - } - - prec.borrows_out_of_scope_at_location + OutOfScopePrecomputer::compute(body, regioncx, borrow_set) } struct PoloniusOutOfScopePrecomputer<'a, 'tcx> { - visited: BitSet, + visited: DenseBitSet, visit_stack: Vec, body: &'a Body<'tcx>, regioncx: &'a RegionInferenceContext<'tcx>, @@ -300,19 +301,30 @@ struct PoloniusOutOfScopePrecomputer<'a, 'tcx> { loans_out_of_scope_at_location: FxIndexMap>, } -impl<'a, 'tcx> PoloniusOutOfScopePrecomputer<'a, 'tcx> { - fn new(body: &'a Body<'tcx>, regioncx: &'a RegionInferenceContext<'tcx>) -> Self { - Self { - visited: BitSet::new_empty(body.basic_blocks.len()), +impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> { + fn compute( + body: &Body<'tcx>, + regioncx: &RegionInferenceContext<'tcx>, + borrow_set: &BorrowSet<'tcx>, + ) -> FxIndexMap> { + // The in-tree polonius analysis computes loans going out of scope using the + // set-of-loans model. + let mut prec = PoloniusOutOfScopePrecomputer { + visited: DenseBitSet::new_empty(body.basic_blocks.len()), visit_stack: vec![], body, regioncx, loans_out_of_scope_at_location: FxIndexMap::default(), + }; + for (loan_idx, loan_data) in borrow_set.iter_enumerated() { + let issuing_region = loan_data.region; + let loan_issued_at = loan_data.reserve_location; + prec.precompute_loans_out_of_scope(loan_idx, issuing_region, loan_issued_at); } + + prec.loans_out_of_scope_at_location } -} -impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> { /// Loans are in scope while they are live: whether they are contained within any live region. /// In the location-insensitive analysis, a loan will be contained in a region if the issuing /// region can reach it in the subset graph. So this is a reachability problem. @@ -325,10 +337,17 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> { let sccs = self.regioncx.constraint_sccs(); let universal_regions = self.regioncx.universal_regions(); + // The loop below was useful for the location-insensitive analysis but shouldn't be + // impactful in the location-sensitive case. It seems that it does, however, as without it a + // handful of tests fail. That likely means some liveness or outlives data related to choice + // regions is missing + // FIXME: investigate the impact of loans traversing applied member constraints and why some + // tests fail otherwise. + // // We first handle the cases where the loan doesn't go out of scope, depending on the // issuing region's successors. for successor in graph::depth_first_search(&self.regioncx.region_graph(), issuing_region) { - // 1. Via applied member constraints + // Via applied member constraints // // The issuing region can flow into the choice regions, and they are either: // - placeholders or free regions themselves, @@ -346,14 +365,6 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> { return; } } - - // 2. Via regions that are live at all points: placeholders and free regions. - // - // If the issuing region outlives such a region, its loan escapes the function and - // cannot go out of scope. We can early return. - if self.regioncx.is_region_live_at_all_points(successor) { - return; - } } let first_block = loan_issued_at.block; @@ -461,34 +472,12 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { regioncx: &RegionInferenceContext<'tcx>, borrow_set: &'a BorrowSet<'tcx>, ) -> Self { - let mut borrows_out_of_scope_at_location = - calculate_borrows_out_of_scope_at_location(body, regioncx, borrow_set); - - // The in-tree polonius analysis computes loans going out of scope using the set-of-loans - // model, and makes sure they're identical to the existing computation of the set-of-points - // model. - if tcx.sess.opts.unstable_opts.polonius.is_next_enabled() { - let mut polonius_prec = PoloniusOutOfScopePrecomputer::new(body, regioncx); - for (loan_idx, loan_data) in borrow_set.iter_enumerated() { - let issuing_region = loan_data.region; - let loan_issued_at = loan_data.reserve_location; - - polonius_prec.precompute_loans_out_of_scope( - loan_idx, - issuing_region, - loan_issued_at, - ); - } - - assert_eq!( - borrows_out_of_scope_at_location, polonius_prec.loans_out_of_scope_at_location, - "polonius loan scopes differ from NLL borrow scopes, for body {:?}", - body.span, - ); - - borrows_out_of_scope_at_location = polonius_prec.loans_out_of_scope_at_location; - } - + let borrows_out_of_scope_at_location = + if !tcx.sess.opts.unstable_opts.polonius.is_next_enabled() { + calculate_borrows_out_of_scope_at_location(body, regioncx, borrow_set) + } else { + PoloniusOutOfScopePrecomputer::compute(body, regioncx, borrow_set) + }; Borrows { tcx, body, borrow_set, borrows_out_of_scope_at_location } } @@ -559,7 +548,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { } } -type BorrowsDomain = BitSet; +type BorrowsDomain = DenseBitSet; /// Forward dataflow computation of the set of borrows that are in scope at a particular location. /// - we gen the introduced loans @@ -575,7 +564,7 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> { fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { // bottom = nothing is reserved or activated yet; - BitSet::new_empty(self.borrow_set.len()) + DenseBitSet::new_empty(self.borrow_set.len()) } fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 90d12ea832857..aa968a1e40f3e 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -9,8 +9,8 @@ use rustc_infer::infer::{ }; use rustc_infer::traits::ObligationCause; use rustc_infer::traits::query::{ - CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpNormalizeGoal, - CanonicalTypeOpProvePredicateGoal, + CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpDeeplyNormalizeGoal, + CanonicalTypeOpNormalizeGoal, CanonicalTypeOpProvePredicateGoal, }; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{ @@ -109,6 +109,14 @@ impl<'tcx, T: Copy + fmt::Display + TypeFoldable> + 'tcx> ToUnivers } } +impl<'tcx, T: Copy + fmt::Display + TypeFoldable> + 'tcx> ToUniverseInfo<'tcx> + for CanonicalTypeOpDeeplyNormalizeGoal<'tcx, T> +{ + fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { + UniverseInfo::TypeOp(Rc::new(DeeplyNormalizeQuery { canonical_query: self, base_universe })) + } +} + impl<'tcx> ToUniverseInfo<'tcx> for CanonicalTypeOpAscribeUserTypeGoal<'tcx> { fn to_universe_info(self, base_universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { UniverseInfo::TypeOp(Rc::new(AscribeUserTypeQuery { canonical_query: self, base_universe })) @@ -156,24 +164,25 @@ pub(crate) trait TypeOpInfo<'tcx> { return; }; - let placeholder_region = ty::Region::new_placeholder(tcx, ty::Placeholder { - universe: adjusted_universe.into(), - bound: placeholder.bound, - }); - - let error_region = - if let RegionElement::PlaceholderRegion(error_placeholder) = error_element { - let adjusted_universe = - error_placeholder.universe.as_u32().checked_sub(base_universe.as_u32()); - adjusted_universe.map(|adjusted| { - ty::Region::new_placeholder(tcx, ty::Placeholder { - universe: adjusted.into(), - bound: error_placeholder.bound, - }) - }) - } else { - None - }; + let placeholder_region = ty::Region::new_placeholder( + tcx, + ty::Placeholder { universe: adjusted_universe.into(), bound: placeholder.bound }, + ); + + let error_region = if let RegionElement::PlaceholderRegion(error_placeholder) = + error_element + { + let adjusted_universe = + error_placeholder.universe.as_u32().checked_sub(base_universe.as_u32()); + adjusted_universe.map(|adjusted| { + ty::Region::new_placeholder( + tcx, + ty::Placeholder { universe: adjusted.into(), bound: error_placeholder.bound }, + ) + }) + } else { + None + }; debug!(?placeholder_region); @@ -284,6 +293,53 @@ where } } +struct DeeplyNormalizeQuery<'tcx, T> { + canonical_query: CanonicalTypeOpDeeplyNormalizeGoal<'tcx, T>, + base_universe: ty::UniverseIndex, +} + +impl<'tcx, T> TypeOpInfo<'tcx> for DeeplyNormalizeQuery<'tcx, T> +where + T: Copy + fmt::Display + TypeFoldable> + 'tcx, +{ + fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> Diag<'tcx> { + tcx.dcx().create_err(HigherRankedLifetimeError { + cause: Some(HigherRankedErrorCause::CouldNotNormalize { + value: self.canonical_query.canonical.value.value.value.to_string(), + }), + span, + }) + } + + fn base_universe(&self) -> ty::UniverseIndex { + self.base_universe + } + + fn nice_error<'infcx>( + &self, + mbcx: &mut MirBorrowckCtxt<'_, 'infcx, 'tcx>, + cause: ObligationCause<'tcx>, + placeholder_region: ty::Region<'tcx>, + error_region: Option>, + ) -> Option> { + let (infcx, key, _) = + mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query); + let ocx = ObligationCtxt::new(&infcx); + + let (param_env, value) = key.into_parts(); + let _ = ocx.deeply_normalize(&cause, param_env, value.value); + + let diag = try_extract_error_from_fulfill_cx( + &ocx, + mbcx.mir_def_id(), + placeholder_region, + error_region, + )? + .with_dcx(mbcx.dcx()); + Some(diag) + } +} + struct AscribeUserTypeQuery<'tcx> { canonical_query: CanonicalTypeOpAscribeUserTypeGoal<'tcx>, base_universe: ty::UniverseIndex, @@ -310,7 +366,7 @@ impl<'tcx> TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> { let (infcx, key, _) = mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query); let ocx = ObligationCtxt::new(&infcx); - type_op_ascribe_user_type_with_span(&ocx, key, Some(cause.span)).ok()?; + type_op_ascribe_user_type_with_span(&ocx, key, cause.span).ok()?; let diag = try_extract_error_from_fulfill_cx( &ocx, mbcx.mir_def_id(), diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 8e5944d6cf45e..6269728e67f73 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -20,7 +20,7 @@ use rustc_middle::bug; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::{ - self, AggregateKind, BindingForm, BorrowKind, CallSource, ClearCrossCrate, ConstraintCategory, + self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory, FakeBorrowKind, FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, MutBorrowKind, Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm, VarDebugInfoContents, @@ -30,23 +30,25 @@ use rustc_middle::ty::{ self, PredicateKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, Upcast, suggest_constraining_type_params, }; -use rustc_middle::util::CallKind; use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::hygiene::DesugaringKind; use rustc_span::{BytePos, Ident, Span, Symbol, kw, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::error_reporting::traits::FindExprBySpan; +use rustc_trait_selection::error_reporting::traits::call_kind::CallKind; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; -use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; +use rustc_trait_selection::traits::{ + Obligation, ObligationCause, ObligationCtxt, supertrait_def_ids, +}; use tracing::{debug, instrument}; use super::explain_borrow::{BorrowExplanation, LaterUseKind}; use super::{DescribePlaceOpt, RegionName, RegionNameSource, UseSpans}; use crate::borrow_set::{BorrowData, TwoPhaseActivation}; use crate::diagnostics::conflict_errors::StorageDeadOrDrop::LocalStorageDead; -use crate::diagnostics::{CapturedMessageOpt, Instance, find_all_local_uses}; +use crate::diagnostics::{CapturedMessageOpt, call_kind, find_all_local_uses}; use crate::prefixes::IsPrefixOf; use crate::{InitializationRequiringAction, MirBorrowckCtxt, WriteKind, borrowck_errors}; @@ -145,10 +147,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { span, desired_action.as_noun(), partially_str, - self.describe_place_with_options(moved_place, DescribePlaceOpt { - including_downcast: true, - including_tuple_field: true, - }), + self.describe_place_with_options( + moved_place, + DescribePlaceOpt { including_downcast: true, including_tuple_field: true }, + ), ); let reinit_spans = maybe_reinitialized_locations @@ -278,15 +280,16 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.suggest_adding_bounds(&mut err, ty, copy_did, span); } - let opt_name = self.describe_place_with_options(place.as_ref(), DescribePlaceOpt { - including_downcast: true, - including_tuple_field: true, - }); + let opt_name = self.describe_place_with_options( + place.as_ref(), + DescribePlaceOpt { including_downcast: true, including_tuple_field: true }, + ); let note_msg = match opt_name { Some(name) => format!("`{name}`"), None => "value".to_owned(), }; if needs_note { + let ty = self.infcx.tcx.short_string(ty, err.long_ty_path()); if let Some(local) = place.as_local() { let span = self.body.local_decls[local].source_info.span; err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { @@ -305,7 +308,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } if let UseSpans::FnSelfUse { - kind: CallKind::DerefCoercion { deref_target, deref_target_ty, .. }, + kind: CallKind::DerefCoercion { deref_target_span, deref_target_ty, .. }, .. } = use_spans { @@ -315,8 +318,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { )); // Check first whether the source is accessible (issue #87060) - if self.infcx.tcx.sess.source_map().is_span_accessible(deref_target) { - err.span_note(deref_target, "deref defined here"); + if let Some(deref_target_span) = deref_target_span + && self.infcx.tcx.sess.source_map().is_span_accessible(deref_target_span) + { + err.span_note(deref_target_span, "deref defined here"); } } @@ -656,8 +661,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { clause.as_trait_clause().is_some_and(|tc| { tc.self_ty().skip_binder().is_param(param.index) && tc.polarity() == ty::PredicatePolarity::Positive - && tcx - .supertrait_def_ids(tc.def_id()) + && supertrait_def_ids(tcx, tc.def_id()) .flat_map(|trait_did| tcx.associated_items(trait_did).in_definition_order()) .any(|item| item.fn_has_self_parameter) }) @@ -761,17 +765,17 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } let spans: Vec<_> = spans_set.into_iter().collect(); - let (name, desc) = match self.describe_place_with_options(moved_place, DescribePlaceOpt { - including_downcast: true, - including_tuple_field: true, - }) { + let (name, desc) = match self.describe_place_with_options( + moved_place, + DescribePlaceOpt { including_downcast: true, including_tuple_field: true }, + ) { Some(name) => (format!("`{name}`"), format!("`{name}` ")), None => ("the variable".to_string(), String::new()), }; - let path = match self.describe_place_with_options(used_place, DescribePlaceOpt { - including_downcast: true, - including_tuple_field: true, - }) { + let path = match self.describe_place_with_options( + used_place, + DescribePlaceOpt { including_downcast: true, including_tuple_field: true }, + ) { Some(name) => format!("`{name}`"), None => "value".to_string(), }; @@ -1516,15 +1520,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { }); self.explain_why_borrow_contains_point(location, borrow, None) - .add_explanation_to_diagnostic( - self.infcx.tcx, - self.body, - &self.local_names, - &mut err, - "", - Some(borrow_span), - None, - ); + .add_explanation_to_diagnostic(&self, &mut err, "", Some(borrow_span), None); self.suggest_copy_for_type_in_cloned_ref(&mut err, place); let typeck_results = self.infcx.tcx.typeck(self.mir_def_id()); if let Some(expr) = self.find_expr(borrow_span) { @@ -1591,15 +1587,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { }); self.explain_why_borrow_contains_point(location, borrow, None) - .add_explanation_to_diagnostic( - self.infcx.tcx, - self.body, - &self.local_names, - &mut err, - "", - None, - None, - ); + .add_explanation_to_diagnostic(&self, &mut err, "", None, None); err } @@ -1886,9 +1874,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } explanation.add_explanation_to_diagnostic( - self.infcx.tcx, - self.body, - &self.local_names, + &self, &mut err, first_borrow_desc, None, @@ -2496,7 +2482,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // To support cases like `|| { v.call(|this| v.get()) }` // FIXME: actually support such cases (need to figure out how to move from the // capture place to original local). - && self.res.as_ref().map_or(true, |(prev_res, _)| prev_res.span.contains(ex.span)) + && self.res.as_ref().is_none_or(|(prev_res, _)| prev_res.span.contains(ex.span)) { self.res = Some((ex, closure)); } @@ -2698,22 +2684,19 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { return; } - let mut sugg = vec![]; let sm = self.infcx.tcx.sess.source_map(); - - if let Some(span) = finder.closure_arg_span { - sugg.push((sm.next_point(span.shrink_to_lo()).shrink_to_hi(), finder.suggest_arg)); - } - for span in finder.closure_change_spans { - sugg.push((span, "this".to_string())); - } - - for (span, suggest) in finder.closure_call_changes { - sugg.push((span, suggest)); - } + let sugg = finder + .closure_arg_span + .map(|span| (sm.next_point(span.shrink_to_lo()).shrink_to_hi(), finder.suggest_arg)) + .into_iter() + .chain( + finder.closure_change_spans.into_iter().map(|span| (span, "this".to_string())), + ) + .chain(finder.closure_call_changes) + .collect(); err.multipart_suggestion_verbose( - "try explicitly pass `&Self` into the Closure as an argument", + "try explicitly passing `&Self` into the closure as an argument", sugg, Applicability::MachineApplicable, ); @@ -3046,15 +3029,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if let BorrowExplanation::MustBeValidFor { .. } = explanation { } else { - explanation.add_explanation_to_diagnostic( - self.infcx.tcx, - self.body, - &self.local_names, - &mut err, - "", - None, - None, - ); + explanation.add_explanation_to_diagnostic(&self, &mut err, "", None, None); } } else { err.span_label(borrow_span, "borrowed value does not live long enough"); @@ -3067,15 +3042,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } }); - explanation.add_explanation_to_diagnostic( - self.infcx.tcx, - self.body, - &self.local_names, - &mut err, - "", - Some(borrow_span), - None, - ); + explanation.add_explanation_to_diagnostic(&self, &mut err, "", Some(borrow_span), None); } err @@ -3128,15 +3095,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { _ => {} } - explanation.add_explanation_to_diagnostic( - self.infcx.tcx, - self.body, - &self.local_names, - &mut err, - "", - None, - None, - ); + explanation.add_explanation_to_diagnostic(&self, &mut err, "", None, None); self.buffer_error(err); } @@ -3153,12 +3112,24 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { drop_span, borrow_span ); + // `TerminatorKind::Return`'s span (the `drop_span` here) `lo` can be subtly wrong and point + // at a single character after the end of the function. This is somehow relied upon in + // existing diagnostics, and changing this in `rustc_mir_build` makes diagnostics worse in + // general. We fix these here. + let sm = self.infcx.tcx.sess.source_map(); + let end_of_function = if drop_span.is_empty() + && let Ok(adjusted_span) = sm.span_extend_prev_while(drop_span, |c| c == '}') + { + adjusted_span + } else { + drop_span + }; self.thread_local_value_does_not_live_long_enough(borrow_span) .with_span_label( borrow_span, "thread-local variables cannot be borrowed beyond the end of the function", ) - .with_span_label(drop_span, "end of enclosing function is here") + .with_span_label(end_of_function, "end of enclosing function is here") } #[instrument(level = "debug", skip(self))] @@ -3309,15 +3280,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } _ => {} } - explanation.add_explanation_to_diagnostic( - self.infcx.tcx, - self.body, - &self.local_names, - &mut err, - "", - None, - None, - ); + explanation.add_explanation_to_diagnostic(&self, &mut err, "", None, None); borrow_spans.args_subdiag(&mut err, |args_span| { crate::session_diagnostics::CaptureArgLabel::Capture { @@ -3808,15 +3771,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } }); - self.explain_why_borrow_contains_point(location, loan, None).add_explanation_to_diagnostic( - self.infcx.tcx, - self.body, - &self.local_names, - &mut err, - "", - None, - None, - ); + self.explain_why_borrow_contains_point(location, loan, None) + .add_explanation_to_diagnostic(&self, &mut err, "", None, None); self.explain_deref_coercion(loan, &mut err); @@ -3825,38 +3781,27 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diag<'_>) { let tcx = self.infcx.tcx; - if let ( - Some(Terminator { - kind: TerminatorKind::Call { call_source: CallSource::OverloadedOperator, .. }, - .. - }), - Some((method_did, method_args)), - ) = ( - &self.body[loan.reserve_location.block].terminator, - rustc_middle::util::find_self_call( + if let Some(Terminator { kind: TerminatorKind::Call { call_source, fn_span, .. }, .. }) = + &self.body[loan.reserve_location.block].terminator + && let Some((method_did, method_args)) = mir::find_self_call( tcx, self.body, loan.assigned_place.local, loan.reserve_location.block, - ), - ) { - if tcx.is_diagnostic_item(sym::deref_method, method_did) { - let deref_target = - tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| { - Instance::try_resolve( - tcx, - self.infcx.typing_env(self.infcx.param_env), - deref_target, - method_args, - ) - .transpose() - }); - if let Some(Ok(instance)) = deref_target { - let deref_target_ty = - instance.ty(tcx, self.infcx.typing_env(self.infcx.param_env)); - err.note(format!("borrow occurs due to deref coercion to `{deref_target_ty}`")); - err.span_note(tcx.def_span(instance.def_id()), "deref defined here"); - } + ) + && let CallKind::DerefCoercion { deref_target_span, deref_target_ty, .. } = call_kind( + self.infcx.tcx, + self.infcx.typing_env(self.infcx.param_env), + method_did, + method_args, + *fn_span, + call_source.from_hir_call(), + Some(self.infcx.tcx.fn_arg_names(method_did)[0]), + ) + { + err.note(format!("borrow occurs due to deref coercion to `{deref_target_ty}`")); + if let Some(deref_target_span) = deref_target_span { + err.span_note(deref_target_span, "deref defined here"); } } } @@ -3976,7 +3921,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Subtype(_) - | ProjectionElem::Index(_) => kind, + | ProjectionElem::Index(_) + | ProjectionElem::UnwrapUnsafeBinder(_) => kind, }, place_ty.projection_ty(tcx, elem), ) diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 22f7f708419b9..3e474225afdfb 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -5,10 +5,9 @@ use std::assert_matches::assert_matches; -use rustc_errors::{Applicability, Diag}; +use rustc_errors::{Applicability, Diag, EmissionGuarantee}; use rustc_hir as hir; use rustc_hir::intravisit::Visitor; -use rustc_index::IndexSlice; use rustc_infer::infer::NllRegionVariableOrigin; use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault; use rustc_middle::mir::{ @@ -17,14 +16,16 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt}; -use rustc_span::{DesugaringKind, Span, Symbol, kw, sym}; +use rustc_span::{DesugaringKind, Span, kw, sym}; use rustc_trait_selection::error_reporting::traits::FindExprBySpan; +use rustc_trait_selection::error_reporting::traits::call_kind::CallKind; use tracing::{debug, instrument}; use super::{RegionName, UseSpans, find_use}; use crate::borrow_set::BorrowData; +use crate::constraints::OutlivesConstraint; use crate::nll::ConstraintDescription; -use crate::region_infer::{BlameConstraint, Cause, ExtraConstraintInfo}; +use crate::region_infer::{BlameConstraint, Cause}; use crate::{MirBorrowckCtxt, WriteKind}; #[derive(Debug)] @@ -42,7 +43,7 @@ pub(crate) enum BorrowExplanation<'tcx> { span: Span, region_name: RegionName, opt_place_desc: Option, - extra_info: Vec, + path: Vec>, }, Unexplained, } @@ -60,16 +61,18 @@ impl<'tcx> BorrowExplanation<'tcx> { pub(crate) fn is_explained(&self) -> bool { !matches!(self, BorrowExplanation::Unexplained) } - pub(crate) fn add_explanation_to_diagnostic( + pub(crate) fn add_explanation_to_diagnostic( &self, - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - local_names: &IndexSlice>, - err: &mut Diag<'_>, + cx: &MirBorrowckCtxt<'_, '_, 'tcx>, + err: &mut Diag<'_, G>, borrow_desc: &str, borrow_span: Option, multiple_borrow_span: Option<(Span, Span)>, ) { + let tcx = cx.infcx.tcx; + let body = cx.body; + let local_names = &cx.local_names; + if let Some(span) = borrow_span { let def_id = body.source.def_id(); if let Some(node) = tcx.hir().get_if_local(def_id) @@ -128,10 +131,10 @@ impl<'tcx> BorrowExplanation<'tcx> { && let Ok(pat) = tcx.sess.source_map().span_to_snippet(pat.span) { suggest_rewrite_if_let(tcx, expr, &pat, init, conseq, alt, err); - } else if path_span.map_or(true, |path_span| path_span == var_or_use_span) { + } else if path_span.is_none_or(|path_span| path_span == var_or_use_span) { // We can use `var_or_use_span` if either `path_span` is not present, or both // spans are the same. - if borrow_span.map_or(true, |sp| !sp.overlaps(var_or_use_span)) { + if borrow_span.is_none_or(|sp| !sp.overlaps(var_or_use_span)) { err.span_label( var_or_use_span, format!("{borrow_desc}borrow later {message}"), @@ -245,7 +248,98 @@ impl<'tcx> BorrowExplanation<'tcx> { ); err.span_label(body.source_info(drop_loc).span, message); - if let LocalInfo::BlockTailTemp(info) = local_decl.local_info() { + struct FindLetExpr<'hir> { + span: Span, + result: Option<(Span, &'hir hir::Pat<'hir>, &'hir hir::Expr<'hir>)>, + tcx: TyCtxt<'hir>, + } + + impl<'hir> rustc_hir::intravisit::Visitor<'hir> for FindLetExpr<'hir> { + type NestedFilter = rustc_middle::hir::nested_filter::OnlyBodies; + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() + } + fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { + if let hir::ExprKind::If(cond, _conseq, _alt) + | hir::ExprKind::Loop( + hir::Block { + expr: + Some(&hir::Expr { + kind: hir::ExprKind::If(cond, _conseq, _alt), + .. + }), + .. + }, + _, + hir::LoopSource::While, + _, + ) = expr.kind + && let hir::ExprKind::Let(hir::LetExpr { + init: let_expr_init, + span: let_expr_span, + pat: let_expr_pat, + .. + }) = cond.kind + && let_expr_init.span.contains(self.span) + { + self.result = + Some((*let_expr_span, let_expr_pat, let_expr_init)) + } else { + hir::intravisit::walk_expr(self, expr); + } + } + } + + if let &LocalInfo::IfThenRescopeTemp { if_then } = local_decl.local_info() + && let hir::Node::Expr(expr) = tcx.hir_node(if_then) + && let hir::ExprKind::If(cond, conseq, alt) = expr.kind + && let hir::ExprKind::Let(&hir::LetExpr { + span: _, + pat, + init, + // FIXME(#101728): enable rewrite when type ascription is + // stabilized again. + ty: None, + recovered: _, + }) = cond.kind + && pat.span.can_be_used_for_suggestions() + && let Ok(pat) = tcx.sess.source_map().span_to_snippet(pat.span) + { + suggest_rewrite_if_let(tcx, expr, &pat, init, conseq, alt, err); + } else if let Some((old, new)) = multiple_borrow_span + && let def_id = body.source.def_id() + && let Some(node) = tcx.hir().get_if_local(def_id) + && let Some(body_id) = node.body_id() + && let hir_body = tcx.hir().body(body_id) + && let mut expr_finder = (FindLetExpr { span: old, result: None, tcx }) + && let Some((let_expr_span, let_expr_pat, let_expr_init)) = { + expr_finder.visit_expr(hir_body.value); + expr_finder.result + } + && !let_expr_span.contains(new) + { + // #133941: The `old` expression is at the conditional part of an + // if/while let expression. Adding a semicolon won't work. + // Instead, try suggesting the `matches!` macro or a temporary. + if let_expr_pat + .walk_short(|pat| !matches!(pat.kind, hir::PatKind::Binding(..))) + { + if let Ok(pat_snippet) = + tcx.sess.source_map().span_to_snippet(let_expr_pat.span) + && let Ok(init_snippet) = + tcx.sess.source_map().span_to_snippet(let_expr_init.span) + { + err.span_suggestion_verbose( + let_expr_span, + "consider using the `matches!` macro", + format!("matches!({init_snippet}, {pat_snippet})"), + Applicability::MaybeIncorrect, + ); + } else { + err.note("consider using the `matches!` macro"); + } + } + } else if let LocalInfo::BlockTailTemp(info) = local_decl.local_info() { if info.tail_result_is_ignored { // #85581: If the first mutable borrow's scope contains // the second borrow, this suggestion isn't helpful. @@ -278,23 +372,6 @@ impl<'tcx> BorrowExplanation<'tcx> { Applicability::MaybeIncorrect, ); }; - } else if let &LocalInfo::IfThenRescopeTemp { if_then } = - local_decl.local_info() - && let hir::Node::Expr(expr) = tcx.hir_node(if_then) - && let hir::ExprKind::If(cond, conseq, alt) = expr.kind - && let hir::ExprKind::Let(&hir::LetExpr { - span: _, - pat, - init, - // FIXME(#101728): enable rewrite when type ascription is - // stabilized again. - ty: None, - recovered: _, - }) = cond.kind - && pat.span.can_be_used_for_suggestions() - && let Ok(pat) = tcx.sess.source_map().span_to_snippet(pat.span) - { - suggest_rewrite_if_let(tcx, expr, &pat, init, conseq, alt, err); } } } @@ -305,7 +382,7 @@ impl<'tcx> BorrowExplanation<'tcx> { ref region_name, ref opt_place_desc, from_closure: _, - ref extra_info, + ref path, } => { region_name.highlight_region_name(err); @@ -327,13 +404,8 @@ impl<'tcx> BorrowExplanation<'tcx> { ); }; - for extra in extra_info { - match extra { - ExtraConstraintInfo::PlaceholderFromPredicate(span) => { - err.span_note(*span, "due to current limitations in the borrow checker, this implies a `'static` lifetime"); - } - } - } + cx.add_placeholder_from_predicate_note(err, &path); + cx.add_sized_or_copy_bound_info(err, category, &path); if let ConstraintCategory::Cast { is_implicit_coercion: true, @@ -348,10 +420,10 @@ impl<'tcx> BorrowExplanation<'tcx> { } } - fn add_object_lifetime_default_note( + fn add_object_lifetime_default_note( &self, tcx: TyCtxt<'tcx>, - err: &mut Diag<'_>, + err: &mut Diag<'_, G>, unsize_ty: Ty<'tcx>, ) { if let ty::Adt(def, args) = unsize_ty.kind() { @@ -405,9 +477,9 @@ impl<'tcx> BorrowExplanation<'tcx> { } } - fn add_lifetime_bound_suggestion_to_diagnostic( + fn add_lifetime_bound_suggestion_to_diagnostic( &self, - err: &mut Diag<'_>, + err: &mut Diag<'_, G>, category: &ConstraintCategory<'tcx>, span: Span, region_name: &RegionName, @@ -434,14 +506,14 @@ impl<'tcx> BorrowExplanation<'tcx> { } } -fn suggest_rewrite_if_let( +fn suggest_rewrite_if_let( tcx: TyCtxt<'_>, expr: &hir::Expr<'_>, pat: &str, init: &hir::Expr<'_>, conseq: &hir::Expr<'_>, alt: Option<&hir::Expr<'_>>, - err: &mut Diag<'_>, + err: &mut Diag<'_, G>, ) { let source_map = tcx.sess.source_map(); err.span_note( @@ -486,8 +558,9 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { &self, borrow_region: RegionVid, outlived_region: RegionVid, - ) -> (ConstraintCategory<'tcx>, bool, Span, Option, Vec) { - let (blame_constraint, extra_info) = self.regioncx.best_blame_constraint( + ) -> (ConstraintCategory<'tcx>, bool, Span, Option, Vec>) + { + let (blame_constraint, path) = self.regioncx.best_blame_constraint( borrow_region, NllRegionVariableOrigin::FreeRegion, |r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region), @@ -496,7 +569,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { let outlived_fr_name = self.give_region_a_name(outlived_region); - (category, from_closure, cause.span, outlived_fr_name, extra_info) + (category, from_closure, cause.span, outlived_fr_name, path) } /// Returns structured explanation for *why* the borrow contains the @@ -549,8 +622,25 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { } } + // NLL doesn't consider boring locals for liveness, and wouldn't encounter a + // `Cause::LiveVar` for such a local. Polonius can't avoid computing liveness for boring + // locals yet, and will encounter them when trying to explain why a borrow contains a given + // point. + // + // We want to focus on relevant live locals in diagnostics, so when polonius is enabled, we + // ensure that we don't emit live boring locals as explanations. + let is_local_boring = |local| { + if let Some(polonius_diagnostics) = self.polonius_diagnostics { + polonius_diagnostics.boring_nll_locals.contains(&local) + } else { + assert!(!tcx.sess.opts.unstable_opts.polonius.is_next_enabled()); + + // Boring locals are never the cause of a borrow explanation in NLLs. + false + } + }; match find_use::find(body, regioncx, tcx, region_sub, use_location) { - Some(Cause::LiveVar(local, location)) => { + Some(Cause::LiveVar(local, location)) if !is_local_boring(local) => { let span = body.source_info(location).span; let spans = self .move_spans(Place::from(local).as_ref(), location) @@ -593,9 +683,11 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { } } - None => { + Some(Cause::LiveVar(..)) | None => { + // Here, under NLL: no cause was found. Under polonius: no cause was found, or a + // boring local was found, which we ignore like NLLs do to match its diagnostics. if let Some(region) = self.to_error_region_vid(borrow_region_vid) { - let (category, from_closure, span, region_name, extra_info) = + let (category, from_closure, span, region_name, path) = self.free_region_constraint_info(borrow_region_vid, region); if let Some(region_name) = region_name { let opt_place_desc = self.describe_place(borrow.borrowed_place.as_ref()); @@ -605,7 +697,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { span, region_name, opt_place_desc, - extra_info, + path, } } else { debug!("Could not generate a region name"); @@ -635,6 +727,39 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { // Used in a closure. (LaterUseKind::ClosureCapture, capture_kind_span, Some(path_span)) } + // In the case that the borrowed value (probably a temporary) + // overlaps with the method's receiver, then point at the method. + UseSpans::FnSelfUse { + var_span: span, + kind: CallKind::Normal { desugaring: None, .. }, + .. + } if span + .overlaps(self.body.local_decls[borrow.assigned_place.local].source_info.span) => + { + if let TerminatorKind::Call { func, call_source: CallSource::Normal, .. } = + &self.body.basic_blocks[location.block].terminator().kind + { + // Just point to the function, to reduce the chance of overlapping spans. + let function_span = match func { + Operand::Constant(c) => c.span, + Operand::Copy(place) | Operand::Move(place) => { + if let Some(l) = place.as_local() { + let local_decl = &self.body.local_decls[l]; + if self.local_names[l].is_none() { + local_decl.source_info.span + } else { + span + } + } else { + span + } + } + }; + (LaterUseKind::Call, function_span, None) + } else { + (LaterUseKind::Other, span, None) + } + } UseSpans::PatUse(span) | UseSpans::OtherUse(span) | UseSpans::FnSelfUse { var_span: span, .. } => { diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 180046ca25620..df83ac985c658 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -4,36 +4,40 @@ use std::collections::BTreeMap; use rustc_abi::{FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxIndexMap; -use rustc_errors::{Applicability, Diag, MultiSpan}; +use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan, listify}; use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::{self as hir, CoroutineKind, LangItem}; use rustc_index::IndexSlice; -use rustc_infer::infer::BoundRegionConversionTime; +use rustc_infer::infer::{ + BoundRegionConversionTime, NllRegionVariableOrigin, RegionVariableOrigin, +}; use rustc_infer::traits::SelectionError; use rustc_middle::bug; use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::{ - AggregateKind, CallSource, ConstOperand, FakeReadCause, Local, LocalInfo, LocalKind, Location, - Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, - TerminatorKind, + AggregateKind, CallSource, ConstOperand, ConstraintCategory, FakeReadCause, Local, LocalInfo, + LocalKind, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, + StatementKind, Terminator, TerminatorKind, find_self_call, }; use rustc_middle::ty::print::Print; -use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; -use rustc_middle::util::{CallDesugaringKind, call_kind}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult, MoveOutIndex}; use rustc_span::def_id::LocalDefId; use rustc_span::source_map::Spanned; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; +use rustc_trait_selection::error_reporting::traits::call_kind::{CallDesugaringKind, call_kind}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{ - FulfillmentErrorCode, type_known_to_meet_bound_modulo_regions, + FulfillmentError, FulfillmentErrorCode, type_known_to_meet_bound_modulo_regions, }; use tracing::debug; use super::MirBorrowckCtxt; use super::borrow_set::BorrowData; +use crate::constraints::OutlivesConstraint; use crate::fluent_generated as fluent; +use crate::nll::ConstraintDescription; use crate::session_diagnostics::{ CaptureArgLabel, CaptureReasonLabel, CaptureReasonNote, CaptureReasonSuggest, CaptureVarCause, CaptureVarKind, CaptureVarPathUseCause, OnClosureNote, @@ -59,7 +63,7 @@ pub(crate) use mutability_errors::AccessKind; pub(crate) use outlives_suggestion::OutlivesSuggestionBuilder; pub(crate) use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors}; pub(crate) use region_name::{RegionName, RegionNameSource}; -pub(crate) use rustc_middle::util::CallKind; +pub(crate) use rustc_trait_selection::error_reporting::traits::call_kind::CallKind; pub(super) struct DescribePlaceOpt { including_downcast: bool, @@ -300,10 +304,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { /// End-user visible description of `place` if one can be found. /// If the place is a temporary for instance, `None` will be returned. pub(super) fn describe_place(&self, place_ref: PlaceRef<'tcx>) -> Option { - self.describe_place_with_options(place_ref, DescribePlaceOpt { - including_downcast: false, - including_tuple_field: true, - }) + self.describe_place_with_options( + place_ref, + DescribePlaceOpt { including_downcast: false, including_tuple_field: true }, + ) } /// End-user visible description of `place` if one can be found. If the place is a temporary @@ -366,6 +370,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ProjectionElem::Downcast(..) => (), ProjectionElem::OpaqueCast(..) => (), ProjectionElem::Subtype(..) => (), + ProjectionElem::UnwrapUnsafeBinder(_) => (), ProjectionElem::Field(field, _ty) => { // FIXME(project-rfc_2229#36): print capture precisely here. if let Some(field) = self.is_upvar_field_projection(PlaceRef { @@ -446,9 +451,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx) } ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx), - ProjectionElem::Subtype(ty) | ProjectionElem::OpaqueCast(ty) => { - PlaceTy::from_ty(*ty) - } + ProjectionElem::Subtype(ty) + | ProjectionElem::OpaqueCast(ty) + | ProjectionElem::UnwrapUnsafeBinder(ty) => PlaceTy::from_ty(*ty), ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type), }, }; @@ -565,7 +570,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // If we didn't find an overloaded deref or index, then assume it's a // built in deref and check the type of the base. let base_ty = deref_base.ty(self.body, tcx).ty; - if base_ty.is_unsafe_ptr() { + if base_ty.is_raw_ptr() { BorrowedContentSource::DerefRawPointer } else if base_ty.is_mutable_ptr() { BorrowedContentSource::DerefMutableRef @@ -619,6 +624,52 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { region.print(&mut printer).unwrap(); printer.into_buffer() } + + /// Add a note to region errors and borrow explanations when higher-ranked regions in predicates + /// implicitly introduce an "outlives `'static`" constraint. + fn add_placeholder_from_predicate_note( + &self, + err: &mut Diag<'_, G>, + path: &[OutlivesConstraint<'tcx>], + ) { + let predicate_span = path.iter().find_map(|constraint| { + let outlived = constraint.sub; + if let Some(origin) = self.regioncx.var_infos.get(outlived) + && let RegionVariableOrigin::Nll(NllRegionVariableOrigin::Placeholder(_)) = + origin.origin + && let ConstraintCategory::Predicate(span) = constraint.category + { + Some(span) + } else { + None + } + }); + + if let Some(span) = predicate_span { + err.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime"); + } + } + + /// Add a label to region errors and borrow explanations when outlives constraints arise from + /// proving a type implements `Sized` or `Copy`. + fn add_sized_or_copy_bound_info( + &self, + err: &mut Diag<'_, G>, + blamed_category: ConstraintCategory<'tcx>, + path: &[OutlivesConstraint<'tcx>], + ) { + for sought_category in [ConstraintCategory::SizedBound, ConstraintCategory::CopyBound] { + if sought_category != blamed_category + && let Some(sought_constraint) = path.iter().find(|c| c.category == sought_category) + { + let label = format!( + "requirement occurs due to {}", + sought_category.description().trim_end() + ); + err.span_label(sought_constraint.span, label); + } + } + } } /// The span(s) associated to a use of a place. @@ -661,9 +712,6 @@ impl UseSpans<'_> { UseSpans::ClosureUse { args_span: span, .. } | UseSpans::PatUse(span) | UseSpans::OtherUse(span) => span, - UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => { - fn_call_span - } UseSpans::FnSelfUse { var_span, .. } => var_span, } } @@ -674,9 +722,6 @@ impl UseSpans<'_> { UseSpans::ClosureUse { path_span: span, .. } | UseSpans::PatUse(span) | UseSpans::OtherUse(span) => span, - UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => { - fn_call_span - } UseSpans::FnSelfUse { var_span, .. } => var_span, } } @@ -687,9 +732,6 @@ impl UseSpans<'_> { UseSpans::ClosureUse { capture_kind_span: span, .. } | UseSpans::PatUse(span) | UseSpans::OtherUse(span) => span, - UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => { - fn_call_span - } UseSpans::FnSelfUse { var_span, .. } => var_span, } } @@ -975,12 +1017,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { kind: TerminatorKind::Call { fn_span, call_source, .. }, .. }) = &self.body[location.block].terminator { - let Some((method_did, method_args)) = rustc_middle::util::find_self_call( - self.infcx.tcx, - self.body, - target_temp, - location.block, - ) else { + let Some((method_did, method_args)) = + find_self_call(self.infcx.tcx, self.body, target_temp, location.block) + else { return normal_ret; }; @@ -1001,6 +1040,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { kind, }; } + normal_ret } @@ -1394,17 +1434,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { error.obligation.predicate, ) } - [errors @ .., last] => { + _ => { format!( "you could `clone` the value and consume it, if the \ - following trait bounds could be satisfied: \ - {} and `{}`", - errors - .iter() - .map(|e| format!("`{}`", e.obligation.predicate)) - .collect::>() - .join(", "), - last.obligation.predicate, + following trait bounds could be satisfied: {}", + listify(&errors, |e: &FulfillmentError<'tcx>| format!( + "`{}`", + e.obligation.predicate + )) + .unwrap(), ) } }; diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index beacbdbd3fa7b..58c5c2fd7742a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -596,9 +596,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.suggest_cloning(err, place_ty, expr, None); } + let ty = self.infcx.tcx.short_string(place_ty, err.long_ty_path()); err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { is_partial_move: false, - ty: place_ty, + ty, place: &place_desc, span, }); @@ -628,9 +629,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.suggest_cloning(err, place_ty, expr, Some(use_spans)); } + let ty = self.infcx.tcx.short_string(place_ty, err.long_ty_path()); err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { is_partial_move: false, - ty: place_ty, + ty, place: &place_desc, span: use_span, }); @@ -831,9 +833,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.suggest_cloning(err, bind_to.ty, expr, None); } + let ty = self.infcx.tcx.short_string(bind_to.ty, err.long_ty_path()); err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { is_partial_move: false, - ty: bind_to.ty, + ty, place: place_desc, span: binding_span, }); diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index c690789b587f6..706dd7135f73b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -10,6 +10,7 @@ use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, BindingMode, ByRef, Node}; use rustc_middle::bug; use rustc_middle::hir::place::PlaceBase; +use rustc_middle::mir::visit::PlaceContext; use rustc_middle::mir::{ self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location, Mutability, Place, PlaceRef, ProjectionElem, @@ -22,7 +23,6 @@ use rustc_trait_selection::traits; use tracing::debug; use crate::diagnostics::BorrowedContentSource; -use crate::util::FindAssignments; use crate::{MirBorrowckCtxt, session_diagnostics}; #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -167,7 +167,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { | ProjectionElem::ConstantIndex { .. } | ProjectionElem::OpaqueCast { .. } | ProjectionElem::Subslice { .. } - | ProjectionElem::Downcast(..), + | ProjectionElem::Downcast(..) + | ProjectionElem::UnwrapUnsafeBinder(_), ], } => bug!("Unexpected immutable place."), } @@ -981,7 +982,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let arg = match hir.get_if_local(callee_def_id) { Some( - hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(sig, ..), .. }) + hir::Node::Item(hir::Item { + ident, kind: hir::ItemKind::Fn { sig, .. }, .. + }) | hir::Node::TraitItem(hir::TraitItem { ident, kind: hir::TraitItemKind::Fn(sig, _), @@ -1020,7 +1023,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // ...otherwise we are probably in the tail expression of the function, point at the // return type. match self.infcx.tcx.hir_node_by_def_id(hir.get_parent_item(fn_call_id).def_id) { - hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(sig, ..), .. }) + hir::Node::Item(hir::Item { + ident, kind: hir::ItemKind::Fn { sig, .. }, .. + }) | hir::Node::TraitItem(hir::TraitItem { ident, kind: hir::TraitItemKind::Fn(sig, _), @@ -1084,6 +1089,38 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } } + /// Finds all statements that assign directly to local (i.e., X = ...) and returns their + /// locations. + fn find_assignments(&self, local: Local) -> Vec { + use rustc_middle::mir::visit::Visitor; + + struct FindLocalAssignmentVisitor { + needle: Local, + locations: Vec, + } + + impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor { + fn visit_local( + &mut self, + local: Local, + place_context: PlaceContext, + location: Location, + ) { + if self.needle != local { + return; + } + + if place_context.is_place_assignment() { + self.locations.push(location); + } + } + } + + let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] }; + visitor.visit_body(self.body); + visitor.locations + } + fn suggest_make_local_mut(&self, err: &mut Diag<'_>, local: Local, name: Symbol) { let local_decl = &self.body.local_decls[local]; @@ -1104,10 +1141,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let amp_mut_sugg = match *local_decl.local_info() { LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => { - let suggestion = suggest_ampmut_self(self.infcx.tcx, decl_span); - let additional = - local_trait.map(|span| (span, suggest_ampmut_self(self.infcx.tcx, span))); - Some(AmpMutSugg { has_sugg: true, span: decl_span, suggestion, additional }) + let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span); + let additional = local_trait.map(|span| suggest_ampmut_self(self.infcx.tcx, span)); + Some(AmpMutSugg { has_sugg: true, span, suggestion, additional }) } LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { @@ -1117,7 +1153,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { })) => { // check if the RHS is from desugaring let opt_assignment_rhs_span = - self.body.find_assignments(local).first().map(|&location| { + self.find_assignments(local).first().map(|&location| { if let Some(mir::Statement { source_info: _, kind: @@ -1166,10 +1202,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { opt_ty_info: None, .. })) => { - let sugg = suggest_ampmut_self(self.infcx.tcx, decl_span); + let (span, sugg) = + suggest_ampmut_self(self.infcx.tcx, decl_span); Some(AmpMutSugg { has_sugg: true, - span: decl_span, + span, suggestion: sugg, additional: None, }) @@ -1425,17 +1462,12 @@ fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option(tcx: TyCtxt<'tcx>, span: Span) -> String { +fn suggest_ampmut_self(tcx: TyCtxt<'_>, span: Span) -> (Span, String) { match tcx.sess.source_map().span_to_snippet(span) { - Ok(snippet) => { - let lt_pos = snippet.find('\''); - if let Some(lt_pos) = lt_pos { - format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4]) - } else { - "&mut self".to_string() - } + Ok(snippet) if snippet.ends_with("self") => { + (span.with_hi(span.hi() - BytePos(4)).shrink_to_hi(), "mut ".to_string()) } - _ => "&mut self".to_string(), + _ => (span, "&mut self".to_string()), } } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 3555009c63f4a..b7e2510e035ac 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -8,12 +8,12 @@ use rustc_hir::QPath::Resolved; use rustc_hir::WherePredicateKind::BoundPredicate; use rustc_hir::def::Res::Def; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::VisitorExt; use rustc_hir::{PolyTraitRef, TyKind, WhereBoundPredicate}; use rustc_infer::infer::{NllRegionVariableOrigin, RelateParamBound}; use rustc_middle::bug; use rustc_middle::hir::place::PlaceBase; -use rustc_middle::mir::{ConstraintCategory, ReturnConstraint}; +use rustc_middle::mir::{AnnotationSource, ConstraintCategory, ReturnConstraint}; use rustc_middle::ty::{self, GenericArgs, Region, RegionVid, Ty, TyCtxt, TypeVisitor}; use rustc_span::{Ident, Span, kw}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; @@ -29,7 +29,7 @@ use tracing::{debug, instrument, trace}; use super::{OutlivesSuggestionBuilder, RegionName, RegionNameSource}; use crate::nll::ConstraintDescription; use crate::region_infer::values::RegionElement; -use crate::region_infer::{BlameConstraint, ExtraConstraintInfo, TypeTest}; +use crate::region_infer::{BlameConstraint, TypeTest}; use crate::session_diagnostics::{ FnMutError, FnMutReturnTypeErr, GenericDoesNotLiveLongEnough, LifetimeOutliveErr, LifetimeReturnCategoryErr, RequireStaticErr, VarHereDenote, @@ -49,8 +49,8 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> { ConstraintCategory::Cast { is_implicit_coercion: false, .. } => "cast ", ConstraintCategory::Cast { is_implicit_coercion: true, .. } => "coercion ", ConstraintCategory::CallArgument(_) => "argument ", - ConstraintCategory::TypeAnnotation => "type annotation ", - ConstraintCategory::ClosureBounds => "closure body ", + ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg) => "generic argument ", + ConstraintCategory::TypeAnnotation(_) => "type annotation ", ConstraintCategory::SizedBound => "proving this value is `Sized` ", ConstraintCategory::CopyBound => "copying this value ", ConstraintCategory::OpaqueType => "opaque type ", @@ -147,9 +147,7 @@ pub(crate) enum RegionErrorKind<'tcx> { pub(crate) struct ErrorConstraintInfo<'tcx> { // fr: outlived_fr pub(super) fr: RegionVid, - pub(super) fr_is_local: bool, pub(super) outlived_fr: RegionVid, - pub(super) outlived_fr_is_local: bool, // Category and span for best blame constraint pub(super) category: ConstraintCategory<'tcx>, @@ -316,13 +314,16 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let type_test_span = type_test.span; if let Some(lower_bound_region) = lower_bound_region { - let generic_ty = type_test.generic_kind.to_ty(self.infcx.tcx); + let generic_ty = self.regioncx.name_regions( + self.infcx.tcx, + type_test.generic_kind.to_ty(self.infcx.tcx), + ); let origin = RelateParamBound(type_test_span, generic_ty, None); self.buffer_error(self.infcx.err_ctxt().construct_generic_bound_failure( self.body.source.def_id().expect_local(), type_test_span, Some(origin), - type_test.generic_kind, + self.regioncx.name_regions(self.infcx.tcx, type_test.generic_kind), lower_bound_region, )); } else { @@ -440,10 +441,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ) { debug!("report_region_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr); - let (blame_constraint, extra_info) = - self.regioncx.best_blame_constraint(fr, fr_origin, |r| { - self.regioncx.provides_universal_region(r, fr, outlived_fr) - }); + let (blame_constraint, path) = self.regioncx.best_blame_constraint(fr, fr_origin, |r| { + self.regioncx.provides_universal_region(r, fr, outlived_fr) + }); let BlameConstraint { category, cause, variance_info, .. } = blame_constraint; debug!("report_region_error: category={:?} {:?} {:?}", category, cause, variance_info); @@ -469,14 +469,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { fr_is_local, outlived_fr_is_local, category ); - let errci = ErrorConstraintInfo { - fr, - outlived_fr, - fr_is_local, - outlived_fr_is_local, - category, - span: cause.span, - }; + let errci = ErrorConstraintInfo { fr, outlived_fr, category, span: cause.span }; let mut diag = match (category, fr_is_local, outlived_fr_is_local) { (ConstraintCategory::Return(kind), true, false) if self.is_closure_fn_mut(fr) => { @@ -554,13 +547,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } } - for extra in extra_info { - match extra { - ExtraConstraintInfo::PlaceholderFromPredicate(span) => { - diag.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime"); - } - } - } + self.add_placeholder_from_predicate_note(&mut diag, &path); + self.add_sized_or_copy_bound_info(&mut diag, category, &path); self.buffer_error(diag); } @@ -683,11 +671,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { && self.regioncx.universal_regions().defining_ty.is_fn_def()) || self.regioncx.universal_regions().defining_ty.is_const() { - return self.report_general_error(&ErrorConstraintInfo { - fr_is_local: true, - outlived_fr_is_local: false, - ..*errci - }); + return self.report_general_error(errci); } let mut diag = @@ -765,15 +749,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { /// ``` #[allow(rustc::diagnostic_outside_of_impl)] // FIXME fn report_general_error(&self, errci: &ErrorConstraintInfo<'tcx>) -> Diag<'infcx> { - let ErrorConstraintInfo { - fr, - fr_is_local, - outlived_fr, - outlived_fr_is_local, - span, - category, - .. - } = errci; + let ErrorConstraintInfo { fr, outlived_fr, span, category, .. } = errci; let mir_def_name = self.infcx.tcx.def_descr(self.mir_def_id().to_def_id()); @@ -792,19 +768,22 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let outlived_fr_name = self.give_region_a_name(*outlived_fr).unwrap(); outlived_fr_name.highlight_region_name(&mut diag); - let err_category = match (category, outlived_fr_is_local, fr_is_local) { - (ConstraintCategory::Return(_), true, _) => LifetimeReturnCategoryErr::WrongReturn { + let err_category = if matches!(category, ConstraintCategory::Return(_)) + && self.regioncx.universal_regions().is_local_free_region(*outlived_fr) + { + LifetimeReturnCategoryErr::WrongReturn { span: *span, mir_def_name, outlived_fr_name, fr_name: &fr_name, - }, - _ => LifetimeReturnCategoryErr::ShortReturn { + } + } else { + LifetimeReturnCategoryErr::ShortReturn { span: *span, category_desc: category.description(), free_region_name: &fr_name, outlived_fr_name, - }, + } }; diag.subdiagnostic(err_category); @@ -893,7 +872,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if alias_ty.span.desugaring_kind().is_some() { // Skip `async` desugaring `impl Future`. } - if let TyKind::TraitObject(_, lt, _) = alias_ty.kind { + if let TyKind::TraitObject(_, lt) = alias_ty.kind { if lt.ident.name == kw::Empty { spans_suggs.push((lt.ident.span.shrink_to_hi(), " + 'a".to_string())); } else { @@ -993,7 +972,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { for found_did in found_dids { let mut traits = vec![]; let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did); - hir_v.visit_ty(self_ty); + hir_v.visit_ty_unambig(self_ty); debug!("trait spans found: {:?}", traits); for span in &traits { let mut multi_span: MultiSpan = vec![*span].into(); @@ -1106,12 +1085,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let closure_ty = Ty::new_closure( tcx, closure_def_id.to_def_id(), - ty::ClosureArgs::new(tcx, ty::ClosureArgsParts { - parent_args: args.parent_args(), - closure_kind_ty: args.kind_ty(), - tupled_upvars_ty: args.tupled_upvars_ty(), - closure_sig_as_fn_ptr_ty, - }) + ty::ClosureArgs::new( + tcx, + ty::ClosureArgsParts { + parent_args: args.parent_args(), + closure_kind_ty: args.kind_ty(), + tupled_upvars_ty: args.tupled_upvars_ty(), + closure_sig_as_fn_ptr_ty, + }, + ) .args, ); diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index bdb880b2bced3..ccd13badad742 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -5,7 +5,7 @@ use std::fmt::{self, Display}; use std::iter; use rustc_data_structures::fx::IndexEntry; -use rustc_errors::Diag; +use rustc_errors::{Diag, EmissionGuarantee}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_middle::ty::print::RegionHighlightMode; @@ -108,7 +108,7 @@ impl RegionName { } } - pub(crate) fn highlight_region_name(&self, diag: &mut Diag<'_>) { + pub(crate) fn highlight_region_name(&self, diag: &mut Diag<'_, G>) { match &self.source { RegionNameSource::NamedLateParamRegion(span) | RegionNameSource::NamedEarlyParamRegion(span) => { @@ -432,7 +432,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { // must highlight the variable. // NOTE(eddyb) this is handled in/by the sole caller // (`give_name_if_anonymous_region_appears_in_arguments`). - hir::TyKind::Infer => None, + hir::TyKind::Infer(()) => None, _ => Some(argument_hir_ty), } @@ -615,7 +615,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { } (GenericArgKind::Type(ty), hir::GenericArg::Type(hir_ty)) => { - search_stack.push((ty, hir_ty)); + search_stack.push((ty, hir_ty.as_unambig_ty())); } (GenericArgKind::Const(_ct), hir::GenericArg::Const(_hir_ct)) => { diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index b061a450c83f7..53cf4f34ae794 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -6,6 +6,7 @@ #![feature(assert_matches)] #![feature(box_patterns)] #![feature(file_buffered)] +#![feature(if_let_guard)] #![feature(let_chains)] #![feature(never_type)] #![feature(rustc_attrs)] @@ -15,16 +16,19 @@ #![warn(unreachable_pub)] // tidy-alphabetical-end +use std::borrow::Cow; use std::cell::RefCell; use std::marker::PhantomData; -use std::ops::Deref; +use std::ops::{ControlFlow, Deref}; use rustc_abi::FieldIdx; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::graph::dominators::Dominators; +use rustc_errors::LintDiagnostic; use rustc_hir as hir; +use rustc_hir::CRATE_HIR_ID; use rustc_hir::def_id::LocalDefId; -use rustc_index::bit_set::{BitSet, MixedBitSet}; +use rustc_index::bit_set::{DenseBitSet, MixedBitSet}; use rustc_index::{IndexSlice, IndexVec}; use rustc_infer::infer::{ InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt, @@ -42,7 +46,7 @@ use rustc_mir_dataflow::move_paths::{ InitIndex, InitLocation, LookupResult, MoveData, MovePathIndex, }; use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results}; -use rustc_session::lint::builtin::UNUSED_MUT; +use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT}; use rustc_span::{Span, Symbol}; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -56,7 +60,8 @@ use crate::diagnostics::{ use crate::path_utils::*; use crate::place_ext::PlaceExt; use crate::places_conflict::{PlaceConflictBias, places_conflict}; -use crate::polonius::legacy::{LocationTable, PoloniusOutput}; +use crate::polonius::PoloniusDiagnosticsContext; +use crate::polonius::legacy::{PoloniusLocationTable, PoloniusOutput}; use crate::prefixes::PrefixSet; use crate::region_infer::RegionInferenceContext; use crate::renumber::RegionCtxt; @@ -81,7 +86,6 @@ mod session_diagnostics; mod type_check; mod universal_regions; mod used_muts; -mod util; /// A public API provided for the Rust compiler consumers. pub mod consumers; @@ -176,12 +180,9 @@ fn do_mir_borrowck<'tcx>( infcx.register_predefined_opaques_for_next_solver(def); } - let location_table = LocationTable::new(body); + let location_table = PoloniusLocationTable::new(body); let move_data = MoveData::gather_moves(body, tcx, |_| true); - let promoted_move_data = promoted - .iter_enumerated() - .map(|(idx, body)| (idx, MoveData::gather_moves(body, tcx, |_| true))); let flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data) .iterate_to_fixpoint(tcx, body, Some("borrowck")) @@ -198,7 +199,7 @@ fn do_mir_borrowck<'tcx>( polonius_output, opt_closure_req, nll_errors, - localized_outlives_constraints, + polonius_diagnostics, } = nll::compute_regions( &infcx, free_regions, @@ -239,15 +240,20 @@ fn do_mir_borrowck<'tcx>( false }; - for (idx, move_data) in promoted_move_data { + // While promoteds should mostly be correct by construction, we need to check them for + // invalid moves to detect moving out of arrays:`struct S; fn main() { &([S][0]); }`. + for promoted_body in &promoted { use rustc_middle::mir::visit::Visitor; - - let promoted_body = &promoted[idx]; + // This assumes that we won't use some of the fields of the `promoted_mbcx` + // when detecting and reporting move errors. While it would be nice to move + // this check out of `MirBorrowckCtxt`, actually doing so is far from trivial. + let move_data = MoveData::gather_moves(promoted_body, tcx, |_| true); let mut promoted_mbcx = MirBorrowckCtxt { infcx: &infcx, body: promoted_body, move_data: &move_data, - location_table: &location_table, // no need to create a real one for the promoted, it is not used + // no need to create a real location table for the promoted, it is not used + location_table: &location_table, movable_coroutine, fn_self_span_reported: Default::default(), locals_are_invalidated_at_exit, @@ -265,10 +271,8 @@ fn do_mir_borrowck<'tcx>( polonius_output: None, move_errors: Vec::new(), diags_buffer, + polonius_diagnostics: polonius_diagnostics.as_ref(), }; - MoveVisitor { ctxt: &mut promoted_mbcx }.visit_body(promoted_body); - promoted_mbcx.report_move_errors(); - struct MoveVisitor<'a, 'b, 'infcx, 'tcx> { ctxt: &'a mut MirBorrowckCtxt<'b, 'infcx, 'tcx>, } @@ -280,6 +284,8 @@ fn do_mir_borrowck<'tcx>( } } } + MoveVisitor { ctxt: &mut promoted_mbcx }.visit_body(promoted_body); + promoted_mbcx.report_move_errors(); } let mut mbcx = MirBorrowckCtxt { @@ -304,6 +310,7 @@ fn do_mir_borrowck<'tcx>( polonius_output, move_errors: Vec::new(), diags_buffer, + polonius_diagnostics: polonius_diagnostics.as_ref(), }; // Compute and report region errors, if any. @@ -325,7 +332,7 @@ fn do_mir_borrowck<'tcx>( body, ®ioncx, &borrow_set, - localized_outlives_constraints, + polonius_diagnostics.as_ref(), &opt_closure_req, ); @@ -513,7 +520,7 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> { /// Map from MIR `Location` to `LocationIndex`; created /// when MIR borrowck begins. - location_table: &'a LocationTable, + location_table: &'a PoloniusLocationTable, movable_coroutine: bool, /// This keeps track of whether local variables are free-ed when the function @@ -575,6 +582,9 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> { diags_buffer: &'a mut BorrowckDiagnosticsBuffer<'infcx, 'tcx>, move_errors: Vec>, + + /// When using `-Zpolonius=next`: the data used to compute errors and diagnostics. + polonius_diagnostics: Option<&'a PoloniusDiagnosticsContext>, } // Check that: @@ -636,9 +646,11 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt< | StatementKind::Coverage(..) // These do not actually affect borrowck | StatementKind::ConstEvalCounter - // This do not affect borrowck - | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::StorageLive(..) => {} + // This does not affect borrowck + StatementKind::BackwardIncompatibleDropHint { place, reason: BackwardIncompatibleDropReason::Edition2024 } => { + self.check_backward_incompatible_drop(location, (**place, span), state); + } StatementKind::StorageDead(local) => { self.access_place( location, @@ -823,6 +835,7 @@ use self::ReadOrWrite::{Activation, Read, Reservation, Write}; #[derive(Copy, Clone, PartialEq, Eq, Debug)] enum ArtificialField { + ArrayLength, FakeBorrow, } @@ -1007,6 +1020,24 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { } } + fn borrows_in_scope<'s>( + &self, + location: Location, + state: &'s BorrowckDomain, + ) -> Cow<'s, DenseBitSet> { + if let Some(polonius) = &self.polonius_output { + // Use polonius output if it has been enabled. + let location = self.location_table.start_index(location); + let mut polonius_output = DenseBitSet::new_empty(self.borrow_set.len()); + for &idx in polonius.errors_at(location) { + polonius_output.insert(idx); + } + Cow::Owned(polonius_output) + } else { + Cow::Borrowed(&state.borrows) + } + } + #[instrument(level = "debug", skip(self, state))] fn check_access_for_conflict( &mut self, @@ -1018,18 +1049,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { ) -> bool { let mut error_reported = false; - // Use polonius output if it has been enabled. - let mut polonius_output; - let borrows_in_scope = if let Some(polonius) = &self.polonius_output { - let location = self.location_table.start_index(location); - polonius_output = BitSet::new_empty(self.borrow_set.len()); - for &idx in polonius.errors_at(location) { - polonius_output.insert(idx); - } - &polonius_output - } else { - &state.borrows - }; + let borrows_in_scope = self.borrows_in_scope(location, state); each_borrow_involving_path( self, @@ -1054,31 +1074,31 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { rw, (borrow_index, borrow), ); - Control::Continue + ControlFlow::Continue(()) } (Read(_), BorrowKind::Shared | BorrowKind::Fake(_)) | ( Read(ReadKind::Borrow(BorrowKind::Fake(FakeBorrowKind::Shallow))), BorrowKind::Mut { .. }, - ) => Control::Continue, + ) => ControlFlow::Continue(()), (Reservation(_), BorrowKind::Fake(_) | BorrowKind::Shared) => { // This used to be a future compatibility warning (to be // disallowed on NLL). See rust-lang/rust#56254 - Control::Continue + ControlFlow::Continue(()) } (Write(WriteKind::Move), BorrowKind::Fake(FakeBorrowKind::Shallow)) => { // Handled by initialization checks. - Control::Continue + ControlFlow::Continue(()) } (Read(kind), BorrowKind::Mut { .. }) => { // Reading from mere reservations of mutable-borrows is OK. if !is_active(this.dominators(), borrow, location) { - assert!(allow_two_phase_borrow(borrow.kind)); - return Control::Continue; + assert!(borrow.kind.allows_two_phase_borrow()); + return ControlFlow::Continue(()); } error_reported = true; @@ -1094,7 +1114,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { this.buffer_error(err); } } - Control::Break + ControlFlow::Break(()) } (Reservation(kind) | Activation(kind, _) | Write(kind), _) => { @@ -1141,7 +1161,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { this.report_illegal_mutation_of_borrowed(location, place_span, borrow) } } - Control::Break + ControlFlow::Break(()) } }, ); @@ -1149,6 +1169,61 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { error_reported } + /// Through #123739, backward incompatible drops (BIDs) are introduced. + /// We would like to emit lints whether borrow checking fails at these future drop locations. + #[instrument(level = "debug", skip(self, state))] + fn check_backward_incompatible_drop( + &mut self, + location: Location, + (place, place_span): (Place<'tcx>, Span), + state: &BorrowckDomain, + ) { + let tcx = self.infcx.tcx; + // If this type does not need `Drop`, then treat it like a `StorageDead`. + // This is needed because we track the borrows of refs to thread locals, + // and we'll ICE because we don't track borrows behind shared references. + let sd = if place.ty(self.body, tcx).ty.needs_drop(tcx, self.body.typing_env(tcx)) { + AccessDepth::Drop + } else { + AccessDepth::Shallow(None) + }; + + let borrows_in_scope = self.borrows_in_scope(location, state); + + // This is a very simplified version of `Self::check_access_for_conflict`. + // We are here checking on BIDs and specifically still-live borrows of data involving the BIDs. + each_borrow_involving_path( + self, + self.infcx.tcx, + self.body, + (sd, place), + self.borrow_set, + |borrow_index| borrows_in_scope.contains(borrow_index), + |this, _borrow_index, borrow| { + if matches!(borrow.kind, BorrowKind::Fake(_)) { + return ControlFlow::Continue(()); + } + let borrowed = this.retrieve_borrow_spans(borrow).var_or_use_path_span(); + let explain = this.explain_why_borrow_contains_point( + location, + borrow, + Some((WriteKind::StorageDeadOrDrop, place)), + ); + this.infcx.tcx.node_span_lint( + TAIL_EXPR_DROP_ORDER, + CRATE_HIR_ID, + borrowed, + |diag| { + session_diagnostics::TailExprDropOrder { borrowed }.decorate_lint(diag); + explain.add_explanation_to_diagnostic(&this, diag, "", None, None); + }, + ); + // We may stop at the first case + ControlFlow::Break(()) + }, + ); + } + fn mutate_place( &mut self, location: Location, @@ -1185,7 +1260,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { } BorrowKind::Mut { .. } => { let wk = WriteKind::MutableBorrow(bk); - if allow_two_phase_borrow(bk) { + if bk.allows_two_phase_borrow() { (Deep, Reservation(wk)) } else { (Deep, Write(wk)) @@ -1215,15 +1290,18 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { ); } - &Rvalue::RawPtr(mutability, place) => { - let access_kind = match mutability { - Mutability::Mut => ( + &Rvalue::RawPtr(kind, place) => { + let access_kind = match kind { + RawPtrKind::Mut => ( Deep, Write(WriteKind::MutableBorrow(BorrowKind::Mut { kind: MutBorrowKind::Default, })), ), - Mutability::Not => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))), + RawPtrKind::Const => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))), + RawPtrKind::FakeForPtrMetadata => { + (Shallow(Some(ArtificialField::ArrayLength)), Read(ReadKind::Copy)) + } }; self.access_place( @@ -1270,11 +1348,16 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { ); } - &Rvalue::Discriminant(place) => { + &(Rvalue::Len(place) | Rvalue::Discriminant(place)) => { + let af = match *rvalue { + Rvalue::Len(..) => Some(ArtificialField::ArrayLength), + Rvalue::Discriminant(..) => None, + _ => unreachable!(), + }; self.access_place( location, (place, span), - (Shallow(None), Read(ReadKind::Copy)), + (Shallow(af), Read(ReadKind::Copy)), LocalMutationIsAllowed::No, state, ); @@ -1321,6 +1404,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { self.consume_operand(location, (operand, span), state); } } + + Rvalue::WrapUnsafeBinder(op, _) => { + self.consume_operand(location, (op, span), state); + } } } @@ -1581,9 +1668,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { match elem { ProjectionElem::Deref => match place_ty.ty.kind() { ty::Ref(..) | ty::RawPtr(..) => { - self.move_errors.push(MoveError::new(place, location, BorrowedContent { - target_place: place_ref.project_deeper(&[elem], tcx), - })); + self.move_errors.push(MoveError::new( + place, + location, + BorrowedContent { + target_place: place_ref.project_deeper(&[elem], tcx), + }, + )); return; } ty::Adt(adt, _) => { @@ -1693,7 +1784,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { // So it's safe to skip these. ProjectionElem::OpaqueCast(_) | ProjectionElem::Subtype(_) - | ProjectionElem::Downcast(_, _) => (), + | ProjectionElem::Downcast(_, _) + | ProjectionElem::UnwrapUnsafeBinder(_) => (), } place_ty = place_ty.projection_ty(tcx, elem); @@ -1927,6 +2019,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { // FIXME: is this true even if P is an adt with a dtor? { } + ProjectionElem::UnwrapUnsafeBinder(_) => { + check_parent_of_field(self, location, place_base, span, state); + } + // assigning to (*P) requires P to be initialized ProjectionElem::Deref => { self.check_if_full_path_is_moved( @@ -2307,7 +2403,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { | ProjectionElem::Subslice { .. } | ProjectionElem::Subtype(..) | ProjectionElem::OpaqueCast { .. } - | ProjectionElem::Downcast(..) => { + | ProjectionElem::Downcast(..) + | ProjectionElem::UnwrapUnsafeBinder(_) => { let upvar_field_projection = self.is_upvar_field_projection(place); if let Some(field) = upvar_field_projection { let upvar = &self.upvars[field.index()]; diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 968b6d383c1b4..2031579dfd50a 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -27,8 +27,10 @@ use tracing::{debug, instrument}; use crate::borrow_set::BorrowSet; use crate::consumers::ConsumerOptions; use crate::diagnostics::{BorrowckDiagnosticsBuffer, RegionErrors}; -use crate::polonius::LocalizedOutlivesConstraintSet; -use crate::polonius::legacy::{AllFacts, AllFactsExt, LocationTable, PoloniusOutput}; +use crate::polonius::PoloniusDiagnosticsContext; +use crate::polonius::legacy::{ + PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput, +}; use crate::region_infer::RegionInferenceContext; use crate::type_check::{self, MirTypeckResults}; use crate::universal_regions::UniversalRegions; @@ -39,13 +41,14 @@ use crate::{BorrowckInferCtxt, polonius, renumber}; pub(crate) struct NllOutput<'tcx> { pub regioncx: RegionInferenceContext<'tcx>, pub opaque_type_values: FxIndexMap>, - pub polonius_input: Option>, + pub polonius_input: Option>, pub polonius_output: Option>, pub opt_closure_req: Option>, pub nll_errors: RegionErrors<'tcx>, - /// When using `-Zpolonius=next`: the localized typeck and liveness constraints. - pub localized_outlives_constraints: Option, + /// When using `-Zpolonius=next`: the data used to compute errors and diagnostics, e.g. + /// localized typeck and liveness constraints. + pub polonius_diagnostics: Option, } /// Rewrites the regions in the MIR to use NLL variables, also scraping out the set of universal @@ -80,7 +83,7 @@ pub(crate) fn compute_regions<'a, 'tcx>( universal_regions: UniversalRegions<'tcx>, body: &Body<'tcx>, promoted: &IndexSlice>, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, move_data: &MoveData<'tcx>, borrow_set: &BorrowSet<'tcx>, @@ -91,17 +94,17 @@ pub(crate) fn compute_regions<'a, 'tcx>( || is_polonius_legacy_enabled; let polonius_output = consumer_options.map(|c| c.polonius_output()).unwrap_or_default() || is_polonius_legacy_enabled; - let mut all_facts = - (polonius_input || AllFacts::enabled(infcx.tcx)).then_some(AllFacts::default()); + let mut polonius_facts = + (polonius_input || PoloniusFacts::enabled(infcx.tcx)).then_some(PoloniusFacts::default()); - let elements = Rc::new(DenseLocationMap::new(body)); + let location_map = Rc::new(DenseLocationMap::new(body)); // Run the MIR type-checker. let MirTypeckResults { constraints, universal_region_relations, opaque_type_values, - mut polonius_context, + polonius_context, } = type_check::type_check( infcx, body, @@ -109,10 +112,10 @@ pub(crate) fn compute_regions<'a, 'tcx>( universal_regions, location_table, borrow_set, - &mut all_facts, + &mut polonius_facts, flow_inits, move_data, - Rc::clone(&elements), + Rc::clone(&location_map), ); // Create the region inference context, taking ownership of the @@ -122,7 +125,7 @@ pub(crate) fn compute_regions<'a, 'tcx>( // If requested, emit legacy polonius facts. polonius::legacy::emit_facts( - &mut all_facts, + &mut polonius_facts, infcx.tcx, location_table, body, @@ -137,23 +140,23 @@ pub(crate) fn compute_regions<'a, 'tcx>( var_infos, constraints, universal_region_relations, - elements, + location_map, ); - // If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives - // constraints. - let localized_outlives_constraints = polonius_context - .as_mut() - .map(|polonius_context| polonius_context.create_localized_constraints(&mut regioncx, body)); + // If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives constraints + // and use them to compute loan liveness. + let polonius_diagnostics = polonius_context.map(|polonius_context| { + polonius_context.compute_loan_liveness(infcx.tcx, &mut regioncx, body, borrow_set) + }); // If requested: dump NLL facts, and run legacy polonius analysis. - let polonius_output = all_facts.as_ref().and_then(|all_facts| { + let polonius_output = polonius_facts.as_ref().and_then(|polonius_facts| { if infcx.tcx.sess.opts.unstable_opts.nll_facts { let def_id = body.source.def_id(); let def_path = infcx.tcx.def_path(def_id); let dir_path = PathBuf::from(&infcx.tcx.sess.opts.unstable_opts.nll_facts_dir) .join(def_path.to_filename_friendly_no_crate()); - all_facts.write_to_dir(dir_path, location_table).unwrap(); + polonius_facts.write_to_dir(dir_path, location_table).unwrap(); } if polonius_output { @@ -162,7 +165,7 @@ pub(crate) fn compute_regions<'a, 'tcx>( let algorithm = Algorithm::from_str(&algorithm).unwrap(); debug!("compute_regions: using polonius algorithm {:?}", algorithm); let _prof_timer = infcx.tcx.prof.generic_activity("polonius_analysis"); - Some(Box::new(Output::compute(all_facts, algorithm, false))) + Some(Box::new(Output::compute(polonius_facts, algorithm, false))) } else { None } @@ -182,11 +185,11 @@ pub(crate) fn compute_regions<'a, 'tcx>( NllOutput { regioncx, opaque_type_values: remapped_opaque_tys, - polonius_input: all_facts.map(Box::new), + polonius_input: polonius_facts.map(Box::new), polonius_output, opt_closure_req: closure_region_requirements, nll_errors, - localized_outlives_constraints, + polonius_diagnostics, } } diff --git a/compiler/rustc_borrowck/src/path_utils.rs b/compiler/rustc_borrowck/src/path_utils.rs index 12a37f56fcf96..2c94a32d369ce 100644 --- a/compiler/rustc_borrowck/src/path_utils.rs +++ b/compiler/rustc_borrowck/src/path_utils.rs @@ -1,26 +1,14 @@ +use std::ops::ControlFlow; + use rustc_abi::FieldIdx; use rustc_data_structures::graph::dominators::Dominators; -use rustc_middle::mir::{BasicBlock, Body, BorrowKind, Location, Place, PlaceRef, ProjectionElem}; +use rustc_middle::mir::{BasicBlock, Body, Location, Place, PlaceRef, ProjectionElem}; use rustc_middle::ty::TyCtxt; use tracing::debug; use crate::borrow_set::{BorrowData, BorrowSet, TwoPhaseActivation}; use crate::{AccessDepth, BorrowIndex, places_conflict}; -/// Returns `true` if the borrow represented by `kind` is -/// allowed to be split into separate Reservation and -/// Activation phases. -pub(super) fn allow_two_phase_borrow(kind: BorrowKind) -> bool { - kind.allows_two_phase_borrow() -} - -/// Control for the path borrow checking code -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub(super) enum Control { - Continue, - Break, -} - /// Encapsulates the idea of iterating over every borrow that involves a particular path pub(super) fn each_borrow_involving_path<'tcx, F, I, S>( s: &mut S, @@ -31,7 +19,7 @@ pub(super) fn each_borrow_involving_path<'tcx, F, I, S>( is_candidate: I, mut op: F, ) where - F: FnMut(&mut S, BorrowIndex, &BorrowData<'tcx>) -> Control, + F: FnMut(&mut S, BorrowIndex, &BorrowData<'tcx>) -> ControlFlow<()>, I: Fn(BorrowIndex) -> bool, { let (access, place) = access_place; @@ -62,7 +50,7 @@ pub(super) fn each_borrow_involving_path<'tcx, F, I, S>( i, borrowed, place, access ); let ctrl = op(s, i, borrowed); - if ctrl == Control::Break { + if matches!(ctrl, ControlFlow::Break(_)) { return; } } diff --git a/compiler/rustc_borrowck/src/places_conflict.rs b/compiler/rustc_borrowck/src/places_conflict.rs index 560b8c0349ad6..cf3e82426e8a0 100644 --- a/compiler/rustc_borrowck/src/places_conflict.rs +++ b/compiler/rustc_borrowck/src/places_conflict.rs @@ -203,7 +203,8 @@ fn place_components_conflict<'tcx>( let base_ty = base.ty(body, tcx).ty; match (elem, base_ty.kind(), access) { - (_, _, Shallow(Some(ArtificialField::FakeBorrow))) => { + (_, _, Shallow(Some(ArtificialField::ArrayLength))) + | (_, _, Shallow(Some(ArtificialField::FakeBorrow))) => { // The array length is like additional fields on the // type; it does not overlap any existing data there. // Furthermore, if cannot actually be a prefix of any @@ -249,7 +250,8 @@ fn place_components_conflict<'tcx>( | (ProjectionElem::Subslice { .. }, _, _) | (ProjectionElem::OpaqueCast { .. }, _, _) | (ProjectionElem::Subtype(_), _, _) - | (ProjectionElem::Downcast { .. }, _, _) => { + | (ProjectionElem::Downcast { .. }, _, _) + | (ProjectionElem::UnwrapUnsafeBinder(_), _, _) => { // Recursive case. This can still be disjoint on a // further iteration if this a shallow access and // there's a deref later on, e.g., a borrow @@ -518,5 +520,9 @@ fn place_projection_conflict<'tcx>( pi1_elem, pi2_elem ), + + (ProjectionElem::UnwrapUnsafeBinder(_), _) => { + todo!() + } } } diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index a6d8014903440..5f9fa3612b8d1 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -1,24 +1,31 @@ use std::io; -use rustc_middle::mir::pretty::{PrettyPrintMirOptions, dump_mir_with_options}; -use rustc_middle::mir::{Body, ClosureRegionRequirements, PassWhere}; -use rustc_middle::ty::TyCtxt; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_index::IndexVec; +use rustc_middle::mir::pretty::{ + PassWhere, PrettyPrintMirOptions, create_dump_file, dump_enabled, dump_mir_to_writer, +}; +use rustc_middle::mir::{Body, ClosureRegionRequirements, Location}; +use rustc_middle::ty::{RegionVid, TyCtxt}; +use rustc_mir_dataflow::points::PointIndex; use rustc_session::config::MirIncludeSpans; use crate::borrow_set::BorrowSet; -use crate::polonius::{LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet}; +use crate::constraints::OutlivesConstraint; +use crate::polonius::{ + LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet, PoloniusDiagnosticsContext, +}; +use crate::region_infer::values::LivenessValues; +use crate::type_check::Locations; use crate::{BorrowckInferCtxt, RegionInferenceContext}; /// `-Zdump-mir=polonius` dumps MIR annotated with NLL and polonius specific information. -// Note: this currently duplicates most of NLL MIR, with some additions for the localized outlives -// constraints. This is ok for now as this dump will change in the near future to an HTML file to -// become more useful. pub(crate) fn dump_polonius_mir<'tcx>( infcx: &BorrowckInferCtxt<'tcx>, body: &Body<'tcx>, regioncx: &RegionInferenceContext<'tcx>, borrow_set: &BorrowSet<'tcx>, - localized_outlives_constraints: Option, + polonius_diagnostics: Option<&PoloniusDiagnosticsContext>, closure_region_requirements: &Option>, ) { let tcx = infcx.tcx; @@ -26,12 +33,135 @@ pub(crate) fn dump_polonius_mir<'tcx>( return; } - let localized_outlives_constraints = localized_outlives_constraints - .expect("missing localized constraints with `-Zpolonius=next`"); + if !dump_enabled(tcx, "polonius", body.source.def_id()) { + return; + } + + let polonius_diagnostics = + polonius_diagnostics.expect("missing diagnostics context with `-Zpolonius=next`"); - // We want the NLL extra comments printed by default in NLL MIR dumps (they were removed in - // #112346). Specifying `-Z mir-include-spans` on the CLI still has priority: for example, - // they're always disabled in mir-opt tests to make working with blessed dumps easier. + let _: io::Result<()> = try { + let mut file = create_dump_file(tcx, "html", false, "polonius", &0, body)?; + emit_polonius_dump( + tcx, + body, + regioncx, + borrow_set, + &polonius_diagnostics.localized_outlives_constraints, + closure_region_requirements, + &mut file, + )?; + }; +} + +/// The polonius dump consists of: +/// - the NLL MIR +/// - the list of polonius localized constraints +/// - a mermaid graph of the CFG +/// - a mermaid graph of the NLL regions and the constraints between them +/// - a mermaid graph of the NLL SCCs and the constraints between them +fn emit_polonius_dump<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + regioncx: &RegionInferenceContext<'tcx>, + borrow_set: &BorrowSet<'tcx>, + localized_outlives_constraints: &LocalizedOutlivesConstraintSet, + closure_region_requirements: &Option>, + out: &mut dyn io::Write, +) -> io::Result<()> { + // Prepare the HTML dump file prologue. + writeln!(out, "")?; + writeln!(out, "")?; + writeln!(out, "Polonius MIR dump")?; + writeln!(out, "")?; + + // Section 1: the NLL + Polonius MIR. + writeln!(out, "
")?; + writeln!(out, "Raw MIR dump")?; + writeln!(out, "
")?;
+    emit_html_mir(
+        tcx,
+        body,
+        regioncx,
+        borrow_set,
+        &localized_outlives_constraints,
+        closure_region_requirements,
+        out,
+    )?;
+    writeln!(out, "
")?; + writeln!(out, "
")?; + + // Section 2: mermaid visualization of the polonius constraint graph. + writeln!(out, "
")?; + writeln!(out, "Polonius constraint graph")?; + writeln!(out, "
")?;
+    let edge_count = emit_mermaid_constraint_graph(
+        borrow_set,
+        regioncx.liveness_constraints(),
+        &localized_outlives_constraints,
+        out,
+    )?;
+    writeln!(out, "
")?; + writeln!(out, "
")?; + + // Section 3: mermaid visualization of the CFG. + writeln!(out, "
")?; + writeln!(out, "Control-flow graph")?; + writeln!(out, "
")?;
+    emit_mermaid_cfg(body, out)?;
+    writeln!(out, "
")?; + writeln!(out, "
")?; + + // Section 4: mermaid visualization of the NLL region graph. + writeln!(out, "
")?; + writeln!(out, "NLL regions")?; + writeln!(out, "
")?;
+    emit_mermaid_nll_regions(regioncx, out)?;
+    writeln!(out, "
")?; + writeln!(out, "
")?; + + // Section 5: mermaid visualization of the NLL SCC graph. + writeln!(out, "
")?; + writeln!(out, "NLL SCCs")?; + writeln!(out, "
")?;
+    emit_mermaid_nll_sccs(regioncx, out)?;
+    writeln!(out, "
")?; + writeln!(out, "
")?; + + // Finalize the dump with the HTML epilogue. + writeln!( + out, + "" + )?; + writeln!(out, "")?; + writeln!(out, "")?; + writeln!(out, "")?; + + Ok(()) +} + +/// Emits the polonius MIR, as escaped HTML. +fn emit_html_mir<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + regioncx: &RegionInferenceContext<'tcx>, + borrow_set: &BorrowSet<'tcx>, + localized_outlives_constraints: &LocalizedOutlivesConstraintSet, + closure_region_requirements: &Option>, + out: &mut dyn io::Write, +) -> io::Result<()> { + // Buffer the regular MIR dump to be able to escape it. + let mut buffer = Vec::new(); + + // We want the NLL extra comments printed by default in NLL MIR dumps. Specifying `-Z + // mir-include-spans` on the CLI still has priority. let options = PrettyPrintMirOptions { include_extra_comments: matches!( tcx.sess.opts.unstable_opts.mir_include_spans, @@ -39,25 +169,45 @@ pub(crate) fn dump_polonius_mir<'tcx>( ), }; - dump_mir_with_options( + dump_mir_to_writer( tcx, - false, "polonius", &0, body, + &mut buffer, |pass_where, out| { emit_polonius_mir( tcx, regioncx, closure_region_requirements, borrow_set, - &localized_outlives_constraints, + localized_outlives_constraints, pass_where, out, ) }, options, - ); + )?; + + // Escape the handful of characters that need it. We don't need to be particularly efficient: + // we're actually writing into a buffered writer already. Note that MIR dumps are valid UTF-8. + let buffer = String::from_utf8_lossy(&buffer); + for ch in buffer.chars() { + let escaped = match ch { + '>' => ">", + '<' => "<", + '&' => "&", + '\'' => "'", + '"' => """, + _ => { + // The common case, no escaping needed. + write!(out, "{}", ch)?; + continue; + } + }; + write!(out, "{}", escaped)?; + } + Ok(()) } /// Produces the actual NLL + Polonius MIR sections to emit during the dumping process. @@ -102,3 +252,237 @@ fn emit_polonius_mir<'tcx>( Ok(()) } + +/// Emits a mermaid flowchart of the CFG blocks and edges, similar to the graphviz version. +fn emit_mermaid_cfg(body: &Body<'_>, out: &mut dyn io::Write) -> io::Result<()> { + use rustc_middle::mir::{TerminatorEdges, TerminatorKind}; + + // The mermaid chart type: a top-down flowchart. + writeln!(out, "flowchart TD")?; + + // Emit the block nodes. + for (block_idx, block) in body.basic_blocks.iter_enumerated() { + let block_idx = block_idx.as_usize(); + let cleanup = if block.is_cleanup { " (cleanup)" } else { "" }; + writeln!(out, "{block_idx}[\"bb{block_idx}{cleanup}\"]")?; + } + + // Emit the edges between blocks, from the terminator edges. + for (block_idx, block) in body.basic_blocks.iter_enumerated() { + let block_idx = block_idx.as_usize(); + let terminator = block.terminator(); + match terminator.edges() { + TerminatorEdges::None => {} + TerminatorEdges::Single(bb) => { + writeln!(out, "{block_idx} --> {}", bb.as_usize())?; + } + TerminatorEdges::Double(bb1, bb2) => { + if matches!(terminator.kind, TerminatorKind::FalseEdge { .. }) { + writeln!(out, "{block_idx} --> {}", bb1.as_usize())?; + writeln!(out, "{block_idx} -- imaginary --> {}", bb2.as_usize())?; + } else { + writeln!(out, "{block_idx} --> {}", bb1.as_usize())?; + writeln!(out, "{block_idx} -- unwind --> {}", bb2.as_usize())?; + } + } + TerminatorEdges::AssignOnReturn { return_, cleanup, .. } => { + for to_idx in return_ { + writeln!(out, "{block_idx} --> {}", to_idx.as_usize())?; + } + + if let Some(to_idx) = cleanup { + writeln!(out, "{block_idx} -- unwind --> {}", to_idx.as_usize())?; + } + } + TerminatorEdges::SwitchInt { targets, .. } => { + for to_idx in targets.all_targets() { + writeln!(out, "{block_idx} --> {}", to_idx.as_usize())?; + } + } + } + } + + Ok(()) +} + +/// Emits a region's label: index, universe, external name. +fn render_region( + region: RegionVid, + regioncx: &RegionInferenceContext<'_>, + out: &mut dyn io::Write, +) -> io::Result<()> { + let def = regioncx.region_definition(region); + let universe = def.universe; + + write!(out, "'{}", region.as_usize())?; + if !universe.is_root() { + write!(out, "/{universe:?}")?; + } + if let Some(name) = def.external_name.and_then(|e| e.get_name()) { + write!(out, " ({name})")?; + } + Ok(()) +} + +/// Emits a mermaid flowchart of the NLL regions and the outlives constraints between them, similar +/// to the graphviz version. +fn emit_mermaid_nll_regions<'tcx>( + regioncx: &RegionInferenceContext<'tcx>, + out: &mut dyn io::Write, +) -> io::Result<()> { + // The mermaid chart type: a top-down flowchart. + writeln!(out, "flowchart TD")?; + + // Emit the region nodes. + for region in regioncx.var_infos.indices() { + write!(out, "{}[\"", region.as_usize())?; + render_region(region, regioncx, out)?; + writeln!(out, "\"]")?; + } + + // Get a set of edges to check for the reverse edge being present. + let edges: FxHashSet<_> = regioncx.outlives_constraints().map(|c| (c.sup, c.sub)).collect(); + + // Order (and deduplicate) edges for traversal, to display them in a generally increasing order. + let constraint_key = |c: &OutlivesConstraint<'_>| { + let min = c.sup.min(c.sub); + let max = c.sup.max(c.sub); + (min, max) + }; + let mut ordered_edges: Vec<_> = regioncx.outlives_constraints().collect(); + ordered_edges.sort_by_key(|c| constraint_key(c)); + ordered_edges.dedup_by_key(|c| constraint_key(c)); + + for outlives in ordered_edges { + // Source node. + write!(out, "{} ", outlives.sup.as_usize())?; + + // The kind of arrow: bidirectional if the opposite edge exists in the set. + if edges.contains(&(outlives.sub, outlives.sup)) { + write!(out, "<")?; + } + write!(out, "-- ")?; + + // Edge label from its `Locations`. + match outlives.locations { + Locations::All(_) => write!(out, "All")?, + Locations::Single(location) => write!(out, "{:?}", location)?, + } + + // Target node. + writeln!(out, " --> {}", outlives.sub.as_usize())?; + } + Ok(()) +} + +/// Emits a mermaid flowchart of the NLL SCCs and the outlives constraints between them, similar +/// to the graphviz version. +fn emit_mermaid_nll_sccs<'tcx>( + regioncx: &RegionInferenceContext<'tcx>, + out: &mut dyn io::Write, +) -> io::Result<()> { + // The mermaid chart type: a top-down flowchart. + writeln!(out, "flowchart TD")?; + + // Gather and emit the SCC nodes. + let mut nodes_per_scc: IndexVec<_, _> = + regioncx.constraint_sccs().all_sccs().map(|_| Vec::new()).collect(); + for region in regioncx.var_infos.indices() { + let scc = regioncx.constraint_sccs().scc(region); + nodes_per_scc[scc].push(region); + } + for (scc, regions) in nodes_per_scc.iter_enumerated() { + // The node label: the regions contained in the SCC. + write!(out, "{scc}[\"SCC({scc}) = {{", scc = scc.as_usize())?; + for (idx, ®ion) in regions.iter().enumerate() { + render_region(region, regioncx, out)?; + if idx < regions.len() - 1 { + write!(out, ",")?; + } + } + writeln!(out, "}}\"]")?; + } + + // Emit the edges between SCCs. + let edges = regioncx.constraint_sccs().all_sccs().flat_map(|source| { + regioncx.constraint_sccs().successors(source).iter().map(move |&target| (source, target)) + }); + for (source, target) in edges { + writeln!(out, "{} --> {}", source.as_usize(), target.as_usize())?; + } + + Ok(()) +} + +/// Emits a mermaid flowchart of the polonius localized outlives constraints, with subgraphs per +/// region, and loan introductions. +fn emit_mermaid_constraint_graph<'tcx>( + borrow_set: &BorrowSet<'tcx>, + liveness: &LivenessValues, + localized_outlives_constraints: &LocalizedOutlivesConstraintSet, + out: &mut dyn io::Write, +) -> io::Result { + let location_name = |location: Location| { + // A MIR location looks like `bb5[2]`. As that is not a syntactically valid mermaid node id, + // transform it into `BB5_2`. + format!("BB{}_{}", location.block.index(), location.statement_index) + }; + let region_name = |region: RegionVid| format!("'{}", region.index()); + let node_name = |region: RegionVid, point: PointIndex| { + let location = liveness.location_from_point(point); + format!("{}_{}", region_name(region), location_name(location)) + }; + + // The mermaid chart type: a top-down flowchart, which supports subgraphs. + writeln!(out, "flowchart TD")?; + + // The loans subgraph: a node per loan. + writeln!(out, " subgraph \"Loans\"")?; + for loan_idx in 0..borrow_set.len() { + writeln!(out, " L{loan_idx}")?; + } + writeln!(out, " end\n")?; + + // And an edge from that loan node to where it enters the constraint graph. + for (loan_idx, loan) in borrow_set.iter_enumerated() { + writeln!( + out, + " L{} --> {}_{}", + loan_idx.index(), + region_name(loan.region), + location_name(loan.reserve_location), + )?; + } + writeln!(out, "")?; + + // The regions subgraphs containing the region/point nodes. + let mut points_per_region: FxIndexMap> = + FxIndexMap::default(); + for constraint in &localized_outlives_constraints.outlives { + points_per_region.entry(constraint.source).or_default().insert(constraint.from); + points_per_region.entry(constraint.target).or_default().insert(constraint.to); + } + for (region, points) in points_per_region { + writeln!(out, " subgraph \"{}\"", region_name(region))?; + for point in points { + writeln!(out, " {}", node_name(region, point))?; + } + writeln!(out, " end\n")?; + } + + // The constraint graph edges. + for constraint in &localized_outlives_constraints.outlives { + // FIXME: add killed loans and constraint kind as edge labels. + writeln!( + out, + " {} --> {}", + node_name(constraint.source, constraint.from), + node_name(constraint.target, constraint.to), + )?; + } + + // Return the number of edges: this is the biggest graph in the dump and its edge count will be + // mermaid's max edge count to support. + let edge_count = borrow_set.len() + localized_outlives_constraints.outlives.len(); + Ok(edge_count) +} diff --git a/compiler/rustc_borrowck/src/polonius/legacy/accesses.rs b/compiler/rustc_borrowck/src/polonius/legacy/accesses.rs index 4a0c8d9b4b461..edd7ca578b727 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/accesses.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/accesses.rs @@ -4,16 +4,16 @@ use rustc_middle::ty::TyCtxt; use rustc_mir_dataflow::move_paths::{LookupResult, MoveData}; use tracing::debug; -use super::{AllFacts, LocationIndex, LocationTable}; +use super::{LocationIndex, PoloniusFacts, PoloniusLocationTable}; use crate::def_use::{self, DefUse}; use crate::universal_regions::UniversalRegions; /// Emit polonius facts for variable defs, uses, drops, and path accesses. pub(crate) fn emit_access_facts<'tcx>( tcx: TyCtxt<'tcx>, - facts: &mut AllFacts, + facts: &mut PoloniusFacts, body: &Body<'tcx>, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, move_data: &MoveData<'tcx>, universal_regions: &UniversalRegions<'tcx>, ) { @@ -31,9 +31,9 @@ pub(crate) fn emit_access_facts<'tcx>( /// MIR visitor extracting point-wise facts about accesses. struct AccessFactsExtractor<'a, 'tcx> { - facts: &'a mut AllFacts, + facts: &'a mut PoloniusFacts, move_data: &'a MoveData<'tcx>, - location_table: &'a LocationTable, + location_table: &'a PoloniusLocationTable, } impl<'tcx> AccessFactsExtractor<'_, 'tcx> { diff --git a/compiler/rustc_borrowck/src/polonius/legacy/facts.rs b/compiler/rustc_borrowck/src/polonius/legacy/facts.rs index 42c4e73321833..64389b11a651a 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/facts.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/facts.rs @@ -4,13 +4,13 @@ use std::fs::{self, File}; use std::io::Write; use std::path::Path; -use polonius_engine::{AllFacts as PoloniusFacts, Atom, Output}; +use polonius_engine::{AllFacts, Atom, Output}; use rustc_macros::extension; use rustc_middle::mir::Local; use rustc_middle::ty::{RegionVid, TyCtxt}; use rustc_mir_dataflow::move_paths::MovePathIndex; -use super::{LocationIndex, LocationTable}; +use super::{LocationIndex, PoloniusLocationTable}; use crate::BorrowIndex; #[derive(Copy, Clone, Debug)] @@ -49,11 +49,11 @@ impl polonius_engine::FactTypes for RustcFacts { type Path = MovePathIndex; } -pub type AllFacts = PoloniusFacts; +pub type PoloniusFacts = AllFacts; -#[extension(pub(crate) trait AllFactsExt)] -impl AllFacts { - /// Returns `true` if there is a need to gather `AllFacts` given the +#[extension(pub(crate) trait PoloniusFactsExt)] +impl PoloniusFacts { + /// Returns `true` if there is a need to gather `PoloniusFacts` given the /// current `-Z` flags. fn enabled(tcx: TyCtxt<'_>) -> bool { tcx.sess.opts.unstable_opts.nll_facts @@ -63,7 +63,7 @@ impl AllFacts { fn write_to_dir( &self, dir: impl AsRef, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, ) -> Result<(), Box> { let dir: &Path = dir.as_ref(); fs::create_dir_all(dir)?; @@ -119,7 +119,7 @@ impl Atom for LocationIndex { } struct FactWriter<'w> { - location_table: &'w LocationTable, + location_table: &'w PoloniusLocationTable, dir: &'w Path, } @@ -141,7 +141,7 @@ trait FactRow { fn write( &self, out: &mut dyn Write, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, ) -> Result<(), Box>; } @@ -149,7 +149,7 @@ impl FactRow for PoloniusRegionVid { fn write( &self, out: &mut dyn Write, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, ) -> Result<(), Box> { write_row(out, location_table, &[self]) } @@ -163,7 +163,7 @@ where fn write( &self, out: &mut dyn Write, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, ) -> Result<(), Box> { write_row(out, location_table, &[&self.0, &self.1]) } @@ -178,7 +178,7 @@ where fn write( &self, out: &mut dyn Write, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, ) -> Result<(), Box> { write_row(out, location_table, &[&self.0, &self.1, &self.2]) } @@ -194,7 +194,7 @@ where fn write( &self, out: &mut dyn Write, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, ) -> Result<(), Box> { write_row(out, location_table, &[&self.0, &self.1, &self.2, &self.3]) } @@ -202,7 +202,7 @@ where fn write_row( out: &mut dyn Write, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, columns: &[&dyn FactCell], ) -> Result<(), Box> { for (index, c) in columns.iter().enumerate() { @@ -213,41 +213,41 @@ fn write_row( } trait FactCell { - fn to_string(&self, location_table: &LocationTable) -> String; + fn to_string(&self, location_table: &PoloniusLocationTable) -> String; } impl FactCell for BorrowIndex { - fn to_string(&self, _location_table: &LocationTable) -> String { + fn to_string(&self, _location_table: &PoloniusLocationTable) -> String { format!("{self:?}") } } impl FactCell for Local { - fn to_string(&self, _location_table: &LocationTable) -> String { + fn to_string(&self, _location_table: &PoloniusLocationTable) -> String { format!("{self:?}") } } impl FactCell for MovePathIndex { - fn to_string(&self, _location_table: &LocationTable) -> String { + fn to_string(&self, _location_table: &PoloniusLocationTable) -> String { format!("{self:?}") } } impl FactCell for PoloniusRegionVid { - fn to_string(&self, _location_table: &LocationTable) -> String { + fn to_string(&self, _location_table: &PoloniusLocationTable) -> String { format!("{self:?}") } } impl FactCell for RegionVid { - fn to_string(&self, _location_table: &LocationTable) -> String { + fn to_string(&self, _location_table: &PoloniusLocationTable) -> String { format!("{self:?}") } } impl FactCell for LocationIndex { - fn to_string(&self, location_table: &LocationTable) -> String { + fn to_string(&self, location_table: &PoloniusLocationTable) -> String { format!("{:?}", location_table.to_rich_location(*self)) } } diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index bb6d593d0d88e..0d1d8642bcacc 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -1,15 +1,13 @@ +use std::ops::ControlFlow; + use rustc_data_structures::graph::dominators::Dominators; use rustc_middle::bug; use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{ - self, BasicBlock, Body, BorrowKind, FakeBorrowKind, InlineAsmOperand, Location, Mutability, - NonDivergingIntrinsic, Operand, Place, Rvalue, Statement, StatementKind, Terminator, - TerminatorKind, -}; +use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use tracing::debug; -use super::{AllFacts, LocationTable}; +use super::{PoloniusFacts, PoloniusLocationTable}; use crate::borrow_set::BorrowSet; use crate::path_utils::*; use crate::{ @@ -20,9 +18,9 @@ use crate::{ /// Emit `loan_invalidated_at` facts. pub(super) fn emit_loan_invalidations<'tcx>( tcx: TyCtxt<'tcx>, - facts: &mut AllFacts, + facts: &mut PoloniusFacts, body: &Body<'tcx>, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, borrow_set: &BorrowSet<'tcx>, ) { let dominators = body.basic_blocks.dominators(); @@ -33,9 +31,9 @@ pub(super) fn emit_loan_invalidations<'tcx>( struct LoanInvalidationsGenerator<'a, 'tcx> { tcx: TyCtxt<'tcx>, - facts: &'a mut AllFacts, + facts: &'a mut PoloniusFacts, body: &'a Body<'tcx>, - location_table: &'a LocationTable, + location_table: &'a PoloniusLocationTable, dominators: &'a Dominators, borrow_set: &'a BorrowSet<'tcx>, } @@ -58,7 +56,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> { StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => { self.consume_operand(location, op); } - StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(mir::CopyNonOverlapping { + StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { src, dst, count, @@ -260,7 +258,7 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { } BorrowKind::Mut { .. } => { let wk = WriteKind::MutableBorrow(bk); - if allow_two_phase_borrow(bk) { + if bk.allows_two_phase_borrow() { (Deep, Reservation(wk)) } else { (Deep, Write(wk)) @@ -271,15 +269,18 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { self.access_place(location, place, access_kind, LocalMutationIsAllowed::No); } - &Rvalue::RawPtr(mutability, place) => { - let access_kind = match mutability { - Mutability::Mut => ( + &Rvalue::RawPtr(kind, place) => { + let access_kind = match kind { + RawPtrKind::Mut => ( Deep, Write(WriteKind::MutableBorrow(BorrowKind::Mut { - kind: mir::MutBorrowKind::Default, + kind: MutBorrowKind::Default, })), ), - Mutability::Not => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))), + RawPtrKind::Const => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))), + RawPtrKind::FakeForPtrMetadata => { + (Shallow(Some(ArtificialField::ArrayLength)), Read(ReadKind::Copy)) + } }; self.access_place(location, place, access_kind, LocalMutationIsAllowed::No); @@ -298,11 +299,16 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { self.consume_operand(location, op); } - &Rvalue::Discriminant(place) => { + &(Rvalue::Len(place) | Rvalue::Discriminant(place)) => { + let af = match rvalue { + Rvalue::Len(..) => Some(ArtificialField::ArrayLength), + Rvalue::Discriminant(..) => None, + _ => unreachable!(), + }; self.access_place( location, place, - (Shallow(None), Read(ReadKind::Copy)), + (Shallow(af), Read(ReadKind::Copy)), LocalMutationIsAllowed::No, ); } @@ -319,6 +325,10 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { self.consume_operand(location, operand); } } + + Rvalue::WrapUnsafeBinder(op, _) => { + self.consume_operand(location, op); + } } } @@ -378,8 +388,8 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { // Reading from mere reservations of mutable-borrows is OK. if !is_active(this.dominators, borrow, location) { // If the borrow isn't active yet, reads don't invalidate it - assert!(allow_two_phase_borrow(borrow.kind)); - return Control::Continue; + assert!(borrow.kind.allows_two_phase_borrow()); + return ControlFlow::Continue(()); } // Unique and mutable borrows are invalidated by reads from any @@ -395,7 +405,7 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { this.emit_loan_invalidated_at(borrow_index, location); } } - Control::Continue + ControlFlow::Continue(()) }, ); } diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_kills.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_kills.rs index 0148e0b286967..098c922bf7b91 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_kills.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_kills.rs @@ -6,16 +6,16 @@ use rustc_middle::mir::{ use rustc_middle::ty::TyCtxt; use tracing::debug; -use super::{AllFacts, LocationTable}; +use super::{PoloniusFacts, PoloniusLocationTable}; use crate::borrow_set::BorrowSet; use crate::places_conflict; /// Emit `loan_killed_at` and `cfg_edge` facts at the same time. pub(super) fn emit_loan_kills<'tcx>( tcx: TyCtxt<'tcx>, - facts: &mut AllFacts, + facts: &mut PoloniusFacts, body: &Body<'tcx>, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, borrow_set: &BorrowSet<'tcx>, ) { let mut visitor = LoanKillsGenerator { borrow_set, tcx, location_table, facts, body }; @@ -26,8 +26,8 @@ pub(super) fn emit_loan_kills<'tcx>( struct LoanKillsGenerator<'a, 'tcx> { tcx: TyCtxt<'tcx>, - facts: &'a mut AllFacts, - location_table: &'a LocationTable, + facts: &'a mut PoloniusFacts, + location_table: &'a PoloniusLocationTable, borrow_set: &'a BorrowSet<'tcx>, body: &'a Body<'tcx>, } diff --git a/compiler/rustc_borrowck/src/polonius/legacy/location.rs b/compiler/rustc_borrowck/src/polonius/legacy/location.rs index 4cb1202033cb9..5f816bb9bbdc4 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/location.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/location.rs @@ -13,7 +13,7 @@ use tracing::debug; /// granularity through outlives relations; however, the rich location /// table serves another purpose: it compresses locations from /// multiple words into a single u32. -pub struct LocationTable { +pub struct PoloniusLocationTable { num_points: usize, statements_before_block: IndexVec, } @@ -30,7 +30,7 @@ pub enum RichLocation { Mid(Location), } -impl LocationTable { +impl PoloniusLocationTable { pub(crate) fn new(body: &Body<'_>) -> Self { let mut num_points = 0; let statements_before_block = body @@ -43,8 +43,8 @@ impl LocationTable { }) .collect(); - debug!("LocationTable(statements_before_block={:#?})", statements_before_block); - debug!("LocationTable: num_points={:#?}", num_points); + debug!("PoloniusLocationTable(statements_before_block={:#?})", statements_before_block); + debug!("PoloniusLocationTable: num_points={:#?}", num_points); Self { num_points, statements_before_block } } diff --git a/compiler/rustc_borrowck/src/polonius/legacy/mod.rs b/compiler/rustc_borrowck/src/polonius/legacy/mod.rs index 45bdbd1e9997e..95820c07a02f8 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/mod.rs @@ -36,16 +36,16 @@ pub use self::facts::*; /// /// The rest of the facts are emitted during typeck and liveness. pub(crate) fn emit_facts<'tcx>( - all_facts: &mut Option, + facts: &mut Option, tcx: TyCtxt<'tcx>, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, body: &Body<'tcx>, borrow_set: &BorrowSet<'tcx>, move_data: &MoveData<'tcx>, universal_region_relations: &UniversalRegionRelations<'tcx>, constraints: &MirTypeckRegionConstraints<'tcx>, ) { - let Some(facts) = all_facts else { + let Some(facts) = facts else { // We don't do anything if there are no facts to fill. return; }; @@ -67,9 +67,9 @@ pub(crate) fn emit_facts<'tcx>( /// Emit facts needed for move/init analysis: moves and assignments. fn emit_move_facts( - facts: &mut AllFacts, + facts: &mut PoloniusFacts, body: &Body<'_>, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, move_data: &MoveData<'_>, ) { facts.path_is_var.extend(move_data.rev_lookup.iter_locals_enumerated().map(|(l, r)| (r, l))); @@ -139,7 +139,7 @@ fn emit_move_facts( /// Emit universal regions facts, and their relations. fn emit_universal_region_facts( - facts: &mut AllFacts, + facts: &mut PoloniusFacts, borrow_set: &BorrowSet<'_>, universal_region_relations: &UniversalRegionRelations<'_>, ) { @@ -187,10 +187,10 @@ pub(crate) fn emit_drop_facts<'tcx>( local: Local, kind: &GenericArg<'tcx>, universal_regions: &UniversalRegions<'tcx>, - all_facts: &mut Option, + facts: &mut Option, ) { debug!("emit_drop_facts(local={:?}, kind={:?}", local, kind); - let Some(facts) = all_facts.as_mut() else { return }; + let Some(facts) = facts.as_mut() else { return }; let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation"); tcx.for_each_free_region(kind, |drop_live_region| { let region_vid = universal_regions.to_region_vid(drop_live_region); @@ -201,8 +201,8 @@ pub(crate) fn emit_drop_facts<'tcx>( /// Emit facts about the outlives constraints: the `subset` base relation, i.e. not a transitive /// closure. fn emit_outlives_facts<'tcx>( - facts: &mut AllFacts, - location_table: &LocationTable, + facts: &mut PoloniusFacts, + location_table: &PoloniusLocationTable, constraints: &MirTypeckRegionConstraints<'tcx>, ) { facts.subset_base.extend(constraints.outlives_constraints.outlives().iter().flat_map( diff --git a/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs b/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs index 75ee29c9d0d50..6ab09f731c078 100644 --- a/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs +++ b/compiler/rustc_borrowck/src/polonius/liveness_constraints.rs @@ -1,7 +1,6 @@ use std::collections::BTreeMap; use rustc_index::bit_set::SparseBitMatrix; -use rustc_index::interval::SparseIntervalMatrix; use rustc_middle::mir::{Body, Location}; use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeVisitable}; @@ -9,12 +8,12 @@ use rustc_mir_dataflow::points::PointIndex; use super::{ ConstraintDirection, LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet, - PoloniusContext, + PoloniusLivenessContext, }; use crate::region_infer::values::LivenessValues; use crate::universal_regions::UniversalRegions; -impl PoloniusContext { +impl PoloniusLivenessContext { /// Record the variance of each region contained within the given value. pub(crate) fn record_live_region_variance<'tcx>( &mut self, @@ -30,25 +29,6 @@ impl PoloniusContext { }; extractor.relate(value, value).expect("Can't have a type error relating to itself"); } - - /// Unlike NLLs, in polonius we traverse the cfg to look for regions live across an edge, so we - /// need to transpose the "points where each region is live" matrix to a "live regions per point" - /// matrix. - // FIXME: avoid this conversion by always storing liveness data in this shape in the rest of - // borrowck. - pub(crate) fn record_live_regions_per_point( - &mut self, - num_regions: usize, - points_per_live_region: &SparseIntervalMatrix, - ) { - let mut live_regions_per_point = SparseBitMatrix::new(num_regions); - for region in points_per_live_region.rows() { - for point in points_per_live_region.row(region).unwrap().iter() { - live_regions_per_point.insert(point, region); - } - } - self.live_regions = Some(live_regions_per_point); - } } /// Propagate loans throughout the CFG: for each statement in the MIR, create localized outlives diff --git a/compiler/rustc_borrowck/src/polonius/loan_liveness.rs b/compiler/rustc_borrowck/src/polonius/loan_liveness.rs new file mode 100644 index 0000000000000..768c12a97a64e --- /dev/null +++ b/compiler/rustc_borrowck/src/polonius/loan_liveness.rs @@ -0,0 +1,307 @@ +use std::collections::{BTreeMap, BTreeSet}; + +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::{ + Body, Local, Location, Place, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, +}; +use rustc_middle::ty::{RegionVid, TyCtxt}; +use rustc_mir_dataflow::points::PointIndex; + +use super::{LiveLoans, LocalizedOutlivesConstraintSet}; +use crate::constraints::OutlivesConstraint; +use crate::dataflow::BorrowIndex; +use crate::region_infer::values::LivenessValues; +use crate::type_check::Locations; +use crate::{BorrowSet, PlaceConflictBias, places_conflict}; + +/// Compute loan reachability, stop at kills, and trace loan liveness throughout the CFG, by +/// traversing the full graph of constraints that combines: +/// - the localized constraints (the physical edges), +/// - with the constraints that hold at all points (the logical edges). +pub(super) fn compute_loan_liveness<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + liveness: &LivenessValues, + outlives_constraints: impl Iterator>, + borrow_set: &BorrowSet<'tcx>, + localized_outlives_constraints: &LocalizedOutlivesConstraintSet, +) -> LiveLoans { + let mut live_loans = LiveLoans::new(borrow_set.len()); + + // FIXME: it may be preferable for kills to be encoded in the edges themselves, to simplify and + // likely make traversal (and constraint generation) more efficient. We also display kills on + // edges when visualizing the constraint graph anyways. + let kills = collect_kills(body, tcx, borrow_set); + + // Create the full graph with the physical edges we've localized earlier, and the logical edges + // of constraints that hold at all points. + let logical_constraints = + outlives_constraints.filter(|c| matches!(c.locations, Locations::All(_))); + let graph = LocalizedConstraintGraph::new(&localized_outlives_constraints, logical_constraints); + let mut visited = FxHashSet::default(); + let mut stack = Vec::new(); + + // Compute reachability per loan by traversing each loan's subgraph starting from where it is + // introduced. + for (loan_idx, loan) in borrow_set.iter_enumerated() { + visited.clear(); + stack.clear(); + + let start_node = LocalizedNode { + region: loan.region, + point: liveness.point_from_location(loan.reserve_location), + }; + stack.push(start_node); + + while let Some(node) = stack.pop() { + if !visited.insert(node) { + continue; + } + + // Record the loan as being live on entry to this point. + live_loans.insert(node.point, loan_idx); + + // Here, we have a conundrum. There's currently a weakness in our theory, in that + // we're using a single notion of reachability to represent what used to be _two_ + // different transitive closures. It didn't seem impactful when coming up with the + // single-graph and reachability through space (regions) + time (CFG) concepts, but in + // practice the combination of time-traveling with kills is more impactful than + // initially anticipated. + // + // Kills should prevent a loan from reaching its successor points in the CFG, but not + // while time-traveling: we're not actually at that CFG point, but looking for + // predecessor regions that contain the loan. One of the two TCs we had pushed the + // transitive subset edges to each point instead of having backward edges, and the + // problem didn't exist before. In the abstract, naive reachability is not enough to + // model this, we'd need a slightly different solution. For example, maybe with a + // two-step traversal: + // - at each point we first traverse the subgraph (and possibly time-travel) looking for + // exit nodes while ignoring kills, + // - and then when we're back at the current point, we continue normally. + // + // Another (less annoying) subtlety is that kills and the loan use-map are + // flow-insensitive. Kills can actually appear in places before a loan is introduced, or + // at a location that is actually unreachable in the CFG from the introduction point, + // and these can also be encountered during time-traveling. + // + // The simplest change that made sense to "fix" the issues above is taking into + // account kills that are: + // - reachable from the introduction point + // - encountered during forward traversal. Note that this is not transitive like the + // two-step traversal described above: only kills encountered on exit via a backward + // edge are ignored. + // + // In our test suite, there are a couple of cases where kills are encountered while + // time-traveling, however as far as we can tell, always in cases where they would be + // unreachable. We have reason to believe that this is a property of the single-graph + // approach (but haven't proved it yet): + // - reachable kills while time-traveling would also be encountered via regular + // traversal + // - it makes _some_ sense to ignore unreachable kills, but subtleties around dead code + // in general need to be better thought through (like they were for NLLs). + // - ignoring kills is a conservative approximation: the loan is still live and could + // cause false positive errors at another place access. Soundness issues in this + // domain should look more like the absence of reachability instead. + // + // This is enough in practice to pass tests, and therefore is what we have implemented + // for now. + // + // FIXME: all of the above. Analyze potential unsoundness, possibly in concert with a + // borrowck implementation in a-mir-formality, fuzzing, or manually crafting + // counter-examples. + + // Continuing traversal will depend on whether the loan is killed at this point, and + // whether we're time-traveling. + let current_location = liveness.location_from_point(node.point); + let is_loan_killed = + kills.get(¤t_location).is_some_and(|kills| kills.contains(&loan_idx)); + + for succ in graph.outgoing_edges(node) { + // If the loan is killed at this point, it is killed _on exit_. But only during + // forward traversal. + if is_loan_killed { + let destination = liveness.location_from_point(succ.point); + if current_location.is_predecessor_of(destination, body) { + continue; + } + } + stack.push(succ); + } + } + } + + live_loans +} + +/// The localized constraint graph indexes the physical and logical edges to compute a given node's +/// successors during traversal. +struct LocalizedConstraintGraph { + /// The actual, physical, edges we have recorded for a given node. + edges: FxHashMap>, + + /// The logical edges representing the outlives constraints that hold at all points in the CFG, + /// which we don't localize to avoid creating a lot of unnecessary edges in the graph. Some CFGs + /// can be big, and we don't need to create such a physical edge for every point in the CFG. + logical_edges: FxHashMap>, +} + +/// A node in the graph to be traversed, one of the two vertices of a localized outlives constraint. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +struct LocalizedNode { + region: RegionVid, + point: PointIndex, +} + +impl LocalizedConstraintGraph { + /// Traverses the constraints and returns the indexed graph of edges per node. + fn new<'tcx>( + constraints: &LocalizedOutlivesConstraintSet, + logical_constraints: impl Iterator>, + ) -> Self { + let mut edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default(); + for constraint in &constraints.outlives { + let source = LocalizedNode { region: constraint.source, point: constraint.from }; + let target = LocalizedNode { region: constraint.target, point: constraint.to }; + edges.entry(source).or_default().insert(target); + } + + let mut logical_edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default(); + for constraint in logical_constraints { + logical_edges.entry(constraint.sup).or_default().insert(constraint.sub); + } + + LocalizedConstraintGraph { edges, logical_edges } + } + + /// Returns the outgoing edges of a given node, not its transitive closure. + fn outgoing_edges(&self, node: LocalizedNode) -> impl Iterator + use<'_> { + // The outgoing edges are: + // - the physical edges present at this node, + // - the materialized logical edges that exist virtually at all points for this node's + // region, localized at this point. + let physical_edges = + self.edges.get(&node).into_iter().flat_map(|targets| targets.iter().copied()); + let materialized_edges = + self.logical_edges.get(&node.region).into_iter().flat_map(move |targets| { + targets + .iter() + .copied() + .map(move |target| LocalizedNode { point: node.point, region: target }) + }); + physical_edges.chain(materialized_edges) + } +} + +/// Traverses the MIR and collects kills. +fn collect_kills<'tcx>( + body: &Body<'tcx>, + tcx: TyCtxt<'tcx>, + borrow_set: &BorrowSet<'tcx>, +) -> BTreeMap> { + let mut collector = KillsCollector { borrow_set, tcx, body, kills: BTreeMap::default() }; + for (block, data) in body.basic_blocks.iter_enumerated() { + collector.visit_basic_block_data(block, data); + } + collector.kills +} + +struct KillsCollector<'a, 'tcx> { + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, + borrow_set: &'a BorrowSet<'tcx>, + + /// The set of loans killed at each location. + kills: BTreeMap>, +} + +// This visitor has a similar structure to the `Borrows` dataflow computation with respect to kills, +// and the datalog polonius fact generation for the `loan_killed_at` relation. +impl<'tcx> KillsCollector<'_, 'tcx> { + /// Records the borrows on the specified place as `killed`. For example, when assigning to a + /// local, or on a call's return destination. + fn record_killed_borrows_for_place(&mut self, place: Place<'tcx>, location: Location) { + // For the reasons described in graph traversal, we also filter out kills + // unreachable from the loan's introduction point, as they would stop traversal when + // e.g. checking for reachability in the subset graph through invariance constraints + // higher up. + let filter_unreachable_kills = |loan| { + let introduction = self.borrow_set[loan].reserve_location; + let reachable = introduction.is_predecessor_of(location, self.body); + reachable + }; + + let other_borrows_of_local = self + .borrow_set + .local_map + .get(&place.local) + .into_iter() + .flat_map(|bs| bs.iter()) + .copied(); + + // If the borrowed place is a local with no projections, all other borrows of this + // local must conflict. This is purely an optimization so we don't have to call + // `places_conflict` for every borrow. + if place.projection.is_empty() { + if !self.body.local_decls[place.local].is_ref_to_static() { + self.kills + .entry(location) + .or_default() + .extend(other_borrows_of_local.filter(|&loan| filter_unreachable_kills(loan))); + } + return; + } + + // By passing `PlaceConflictBias::NoOverlap`, we conservatively assume that any given + // pair of array indices are not equal, so that when `places_conflict` returns true, we + // will be assured that two places being compared definitely denotes the same sets of + // locations. + let definitely_conflicting_borrows = other_borrows_of_local + .filter(|&i| { + places_conflict( + self.tcx, + self.body, + self.borrow_set[i].borrowed_place, + place, + PlaceConflictBias::NoOverlap, + ) + }) + .filter(|&loan| filter_unreachable_kills(loan)); + + self.kills.entry(location).or_default().extend(definitely_conflicting_borrows); + } + + /// Records the borrows on the specified local as `killed`. + fn record_killed_borrows_for_local(&mut self, local: Local, location: Location) { + if let Some(borrow_indices) = self.borrow_set.local_map.get(&local) { + self.kills.entry(location).or_default().extend(borrow_indices.iter()); + } + } +} + +impl<'tcx> Visitor<'tcx> for KillsCollector<'_, 'tcx> { + fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { + // Make sure there are no remaining borrows for locals that have gone out of scope. + if let StatementKind::StorageDead(local) = statement.kind { + self.record_killed_borrows_for_local(local, location); + } + + self.super_statement(statement, location); + } + + fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) { + // When we see `X = ...`, then kill borrows of `(*X).foo` and so forth. + self.record_killed_borrows_for_place(*place, location); + self.super_assign(place, rvalue, location); + } + + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + // A `Call` terminator's return value can be a local which has borrows, so we need to record + // those as killed as well. + if let TerminatorKind::Call { destination, .. } = terminator.kind { + self.record_killed_borrows_for_place(destination, location); + } + + self.super_terminator(terminator, location); + } +} diff --git a/compiler/rustc_borrowck/src/polonius/mod.rs b/compiler/rustc_borrowck/src/polonius/mod.rs index a853ff266a119..142ef8ba28eff 100644 --- a/compiler/rustc_borrowck/src/polonius/mod.rs +++ b/compiler/rustc_borrowck/src/polonius/mod.rs @@ -32,36 +32,77 @@ //! - //! - //! +//! +//! Data flows like this: +//! 1) during MIR typeck, record liveness data needed later: live region variances, as well as the +//! usual NLL liveness data (just computed on more locals). That's the [PoloniusLivenessContext]. +//! 2) once that is done, variance data is transferred, and the NLL region liveness is converted to +//! the polonius shape. That's the main [PoloniusContext]. +//! 3) during region inference, that data and the NLL outlives constraints are used to create the +//! localized outlives constraints, as described above. That's the [PoloniusDiagnosticsContext]. +//! 4) transfer this back to the main borrowck procedure: it handles computing errors and +//! diagnostics, debugging and MIR dumping concerns. mod constraints; mod dump; pub(crate) mod legacy; mod liveness_constraints; +mod loan_liveness; +mod typeck_constraints; use std::collections::BTreeMap; +use rustc_data_structures::fx::FxHashSet; use rustc_index::bit_set::SparseBitMatrix; -use rustc_middle::mir::{Body, Location}; -use rustc_middle::ty::RegionVid; +use rustc_index::interval::SparseIntervalMatrix; +use rustc_middle::mir::{Body, Local}; +use rustc_middle::ty::{RegionVid, TyCtxt}; use rustc_mir_dataflow::points::PointIndex; pub(crate) use self::constraints::*; pub(crate) use self::dump::dump_polonius_mir; use self::liveness_constraints::create_liveness_constraints; -use crate::RegionInferenceContext; -use crate::constraints::OutlivesConstraint; -use crate::region_infer::values::LivenessValues; -use crate::type_check::Locations; +use self::loan_liveness::compute_loan_liveness; +use self::typeck_constraints::convert_typeck_constraints; +use crate::dataflow::BorrowIndex; +use crate::{BorrowSet, RegionInferenceContext}; -/// This struct holds the data needed to create the Polonius localized constraints. -pub(crate) struct PoloniusContext { - /// The set of regions that are live at a given point in the CFG, used to create localized - /// outlives constraints between regions that are live at connected points in the CFG. - live_regions: Option>, +pub(crate) type LiveLoans = SparseBitMatrix; +/// This struct holds the liveness data created during MIR typeck, and which will be used later in +/// the process, to compute the polonius localized constraints. +#[derive(Default)] +pub(crate) struct PoloniusLivenessContext { /// The expected edge direction per live region: the kind of directed edge we'll create as /// liveness constraints depends on the variance of types with respect to each contained region. live_region_variances: BTreeMap, + + /// The regions that outlive free regions are used to distinguish relevant live locals from + /// boring locals. A boring local is one whose type contains only such regions. Polonius + /// currently has more boring locals than NLLs so we record the latter to use in errors and + /// diagnostics, to focus on the locals we consider relevant and match NLL diagnostics. + pub(crate) boring_nll_locals: FxHashSet, +} + +/// This struct holds the data needed to create the Polonius localized constraints. Its data is +/// transferred and converted from the [PoloniusLivenessContext] at the end of MIR typeck. +pub(crate) struct PoloniusContext { + /// The liveness data we recorded during MIR typeck. + liveness_context: PoloniusLivenessContext, + + /// The set of regions that are live at a given point in the CFG, used to create localized + /// outlives constraints between regions that are live at connected points in the CFG. + live_regions: SparseBitMatrix, +} + +/// This struct holds the data needed by the borrowck error computation and diagnostics. Its data is +/// computed from the [PoloniusContext] when computing NLL regions. +pub(crate) struct PoloniusDiagnosticsContext { + /// The localized outlives constraints that were computed in the main analysis. + localized_outlives_constraints: LocalizedOutlivesConstraintSet, + + /// The liveness data computed during MIR typeck: [PoloniusLivenessContext::boring_nll_locals]. + pub(crate) boring_nll_locals: FxHashSet, } /// The direction a constraint can flow into. Used to create liveness constraints according to @@ -79,76 +120,77 @@ enum ConstraintDirection { } impl PoloniusContext { - pub(crate) fn new() -> PoloniusContext { - Self { live_region_variances: BTreeMap::new(), live_regions: None } + /// Unlike NLLs, in polonius we traverse the cfg to look for regions live across an edge, so we + /// need to transpose the "points where each region is live" matrix to a "live regions per point" + /// matrix. + // FIXME: avoid this conversion by always storing liveness data in this shape in the rest of + // borrowck. + pub(crate) fn create_from_liveness( + liveness_context: PoloniusLivenessContext, + num_regions: usize, + points_per_live_region: &SparseIntervalMatrix, + ) -> PoloniusContext { + let mut live_regions_per_point = SparseBitMatrix::new(num_regions); + for region in points_per_live_region.rows() { + for point in points_per_live_region.row(region).unwrap().iter() { + live_regions_per_point.insert(point, region); + } + } + + PoloniusContext { live_regions: live_regions_per_point, liveness_context } } - /// Creates a constraint set for `-Zpolonius=next` by: + /// Computes live loans using the set of loans model for `-Zpolonius=next`. + /// + /// First, creates a constraint graph combining regions and CFG points, by: /// - converting NLL typeck constraints to be localized /// - encoding liveness constraints - pub(crate) fn create_localized_constraints<'tcx>( - &self, - regioncx: &RegionInferenceContext<'tcx>, + /// + /// Then, this graph is traversed, and combined with kills, reachability is recorded as loan + /// liveness, to be used by the loan scope and active loans computations. + /// + /// The constraint data will be used to compute errors and diagnostics. + pub(crate) fn compute_loan_liveness<'tcx>( + self, + tcx: TyCtxt<'tcx>, + regioncx: &mut RegionInferenceContext<'tcx>, body: &Body<'tcx>, - ) -> LocalizedOutlivesConstraintSet { + borrow_set: &BorrowSet<'tcx>, + ) -> PoloniusDiagnosticsContext { + let PoloniusLivenessContext { live_region_variances, boring_nll_locals } = + self.liveness_context; + let mut localized_outlives_constraints = LocalizedOutlivesConstraintSet::default(); convert_typeck_constraints( + tcx, body, regioncx.liveness_constraints(), regioncx.outlives_constraints(), + regioncx.universal_regions(), &mut localized_outlives_constraints, ); - let live_regions = self.live_regions.as_ref().expect( - "live regions per-point data should have been created at the end of MIR typeck", - ); create_liveness_constraints( body, regioncx.liveness_constraints(), - live_regions, - &self.live_region_variances, + &self.live_regions, + &live_region_variances, regioncx.universal_regions(), &mut localized_outlives_constraints, ); - // FIXME: here, we can trace loan reachability in the constraint graph and record this as loan - // liveness for the next step in the chain, the NLL loan scope and active loans computations. - - localized_outlives_constraints - } -} - -/// Propagate loans throughout the subset graph at a given point (with some subtleties around the -/// location where effects start to be visible). -fn convert_typeck_constraints<'tcx>( - body: &Body<'tcx>, - liveness: &LivenessValues, - outlives_constraints: impl Iterator>, - localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet, -) { - for outlives_constraint in outlives_constraints { - match outlives_constraint.locations { - Locations::All(_) => { - // For now, turn logical constraints holding at all points into physical edges at - // every point in the graph. - // FIXME: encode this into *traversal* instead. - for (block, bb) in body.basic_blocks.iter_enumerated() { - let statement_count = bb.statements.len(); - for statement_index in 0..=statement_count { - let current_location = Location { block, statement_index }; - let current_point = liveness.point_from_location(current_location); - - localized_outlives_constraints.push(LocalizedOutlivesConstraint { - source: outlives_constraint.sup, - from: current_point, - target: outlives_constraint.sub, - to: current_point, - }); - } - } - } + // Now that we have a complete graph, we can compute reachability to trace the liveness of + // loans for the next step in the chain, the NLL loan scope and active loans computations. + let live_loans = compute_loan_liveness( + tcx, + body, + regioncx.liveness_constraints(), + regioncx.outlives_constraints(), + borrow_set, + &localized_outlives_constraints, + ); + regioncx.record_live_loans(live_loans); - _ => {} - } + PoloniusDiagnosticsContext { localized_outlives_constraints, boring_nll_locals } } } diff --git a/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs b/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs new file mode 100644 index 0000000000000..1289b1899eb35 --- /dev/null +++ b/compiler/rustc_borrowck/src/polonius/typeck_constraints.rs @@ -0,0 +1,229 @@ +use rustc_data_structures::fx::FxHashSet; +use rustc_middle::mir::{Body, Location, Statement, StatementKind, Terminator, TerminatorKind}; +use rustc_middle::ty::{TyCtxt, TypeVisitable}; +use rustc_mir_dataflow::points::PointIndex; + +use super::{LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet}; +use crate::constraints::OutlivesConstraint; +use crate::region_infer::values::LivenessValues; +use crate::type_check::Locations; +use crate::universal_regions::UniversalRegions; + +/// Propagate loans throughout the subset graph at a given point (with some subtleties around the +/// location where effects start to be visible). +pub(super) fn convert_typeck_constraints<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + liveness: &LivenessValues, + outlives_constraints: impl Iterator>, + universal_regions: &UniversalRegions<'tcx>, + localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet, +) { + for outlives_constraint in outlives_constraints { + match outlives_constraint.locations { + Locations::All(_) => { + // We don't turn constraints holding at all points into physical edges at every + // point in the graph. They are encoded into *traversal* instead: a given node's + // successors will combine these logical edges with the regular, physical, localized + // edges. + continue; + } + + Locations::Single(location) => { + // This constraint is marked as holding at one location, we localize it to that + // location or its successor, depending on the corresponding MIR + // statement/terminator. Unfortunately, they all show up from typeck as coming "on + // entry", so for now we modify them to take effects that should apply "on exit" + // into account. + // + // FIXME: this approach is subtle, complicated, and hard to test, so we should track + // this information better in MIR typeck instead, for example with a new `Locations` + // variant that contains which node is crossing over between entry and exit. + let point = liveness.point_from_location(location); + let localized_constraint = if let Some(stmt) = + body[location.block].statements.get(location.statement_index) + { + localize_statement_constraint( + tcx, + body, + stmt, + liveness, + &outlives_constraint, + location, + point, + universal_regions, + ) + } else { + assert_eq!(location.statement_index, body[location.block].statements.len()); + let terminator = body[location.block].terminator(); + localize_terminator_constraint( + tcx, + body, + terminator, + liveness, + &outlives_constraint, + point, + universal_regions, + ) + }; + localized_outlives_constraints.push(localized_constraint); + } + } + } +} + +/// For a given outlives constraint arising from a MIR statement, localize the constraint with the +/// needed CFG `from`-`to` intra-block nodes. +fn localize_statement_constraint<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + stmt: &Statement<'tcx>, + liveness: &LivenessValues, + outlives_constraint: &OutlivesConstraint<'tcx>, + current_location: Location, + current_point: PointIndex, + universal_regions: &UniversalRegions<'tcx>, +) -> LocalizedOutlivesConstraint { + match &stmt.kind { + StatementKind::Assign(box (lhs, rhs)) => { + // To create localized outlives constraints without midpoints, we rely on the property + // that no input regions from the RHS of the assignment will flow into themselves: they + // should not appear in the output regions in the LHS. We believe this to be true by + // construction of the MIR, via temporaries, and assert it here. + // + // We think we don't need midpoints because: + // - every LHS Place has a unique set of regions that don't appear elsewhere + // - this implies that for them to be part of the RHS, the same Place must be read and + // written + // - and that should be impossible in MIR + // + // When we have a more complete implementation in the future, tested with crater, etc, + // we can relax this to a debug assert instead, or remove it. + assert!( + { + let mut lhs_regions = FxHashSet::default(); + tcx.for_each_free_region(lhs, |region| { + let region = universal_regions.to_region_vid(region); + lhs_regions.insert(region); + }); + + let mut rhs_regions = FxHashSet::default(); + tcx.for_each_free_region(rhs, |region| { + let region = universal_regions.to_region_vid(region); + rhs_regions.insert(region); + }); + + // The intersection between LHS and RHS regions should be empty. + lhs_regions.is_disjoint(&rhs_regions) + }, + "there should be no common regions between the LHS and RHS of an assignment" + ); + + // As mentioned earlier, we should be tracking these better upstream but: we want to + // relate the types on entry to the type of the place on exit. That is, outlives + // constraints on the RHS are on entry, and outlives constraints to/from the LHS are on + // exit (i.e. on entry to the successor location). + let lhs_ty = body.local_decls[lhs.local].ty; + let successor_location = Location { + block: current_location.block, + statement_index: current_location.statement_index + 1, + }; + let successor_point = liveness.point_from_location(successor_location); + compute_constraint_direction( + tcx, + outlives_constraint, + &lhs_ty, + current_point, + successor_point, + universal_regions, + ) + } + _ => { + // For the other cases, we localize an outlives constraint to where it arises. + LocalizedOutlivesConstraint { + source: outlives_constraint.sup, + from: current_point, + target: outlives_constraint.sub, + to: current_point, + } + } + } +} + +/// For a given outlives constraint arising from a MIR terminator, localize the constraint with the +/// needed CFG `from`-`to` inter-block nodes. +fn localize_terminator_constraint<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + terminator: &Terminator<'tcx>, + liveness: &LivenessValues, + outlives_constraint: &OutlivesConstraint<'tcx>, + current_point: PointIndex, + universal_regions: &UniversalRegions<'tcx>, +) -> LocalizedOutlivesConstraint { + // FIXME: check if other terminators need the same handling as `Call`s, in particular + // Assert/Yield/Drop. A handful of tests are failing with Drop related issues, as well as some + // coroutine tests, and that may be why. + match &terminator.kind { + // FIXME: also handle diverging calls. + TerminatorKind::Call { destination, target: Some(target), .. } => { + // Calls are similar to assignments, and thus follow the same pattern. If there is a + // target for the call we also relate what flows into the destination here to entry to + // that successor. + let destination_ty = destination.ty(&body.local_decls, tcx); + let successor_location = Location { block: *target, statement_index: 0 }; + let successor_point = liveness.point_from_location(successor_location); + compute_constraint_direction( + tcx, + outlives_constraint, + &destination_ty, + current_point, + successor_point, + universal_regions, + ) + } + _ => { + // Typeck constraints guide loans between regions at the current point, so we do that in + // the general case, and liveness will take care of making them flow to the terminator's + // successors. + LocalizedOutlivesConstraint { + source: outlives_constraint.sup, + from: current_point, + target: outlives_constraint.sub, + to: current_point, + } + } + } +} +/// For a given outlives constraint and CFG edge, returns the localized constraint with the +/// appropriate `from`-`to` direction. This is computed according to whether the constraint flows to +/// or from a free region in the given `value`, some kind of result for an effectful operation, like +/// the LHS of an assignment. +fn compute_constraint_direction<'tcx>( + tcx: TyCtxt<'tcx>, + outlives_constraint: &OutlivesConstraint<'tcx>, + value: &impl TypeVisitable>, + current_point: PointIndex, + successor_point: PointIndex, + universal_regions: &UniversalRegions<'tcx>, +) -> LocalizedOutlivesConstraint { + let mut to = current_point; + let mut from = current_point; + tcx.for_each_free_region(value, |region| { + let region = universal_regions.to_region_vid(region); + if region == outlives_constraint.sub { + // This constraint flows into the result, its effects start becoming visible on exit. + to = successor_point; + } else if region == outlives_constraint.sup { + // This constraint flows from the result, its effects start becoming visible on exit. + from = successor_point; + } + }); + + LocalizedOutlivesConstraint { + source: outlives_constraint.sup, + from, + target: outlives_constraint.sub, + to, + } +} diff --git a/compiler/rustc_borrowck/src/prefixes.rs b/compiler/rustc_borrowck/src/prefixes.rs index fc7e6e586410a..83cca38a5c090 100644 --- a/compiler/rustc_borrowck/src/prefixes.rs +++ b/compiler/rustc_borrowck/src/prefixes.rs @@ -66,6 +66,10 @@ impl<'tcx> Iterator for Prefixes<'tcx> { self.next = Some(cursor_base); return Some(cursor); } + ProjectionElem::UnwrapUnsafeBinder(_) => { + self.next = Some(cursor_base); + return Some(cursor); + } ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::OpaqueCast { .. } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 2150759d329ea..d2268c4779d63 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -13,15 +13,16 @@ use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin}; use rustc_middle::bug; use rustc_middle::mir::{ - BasicBlock, Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureOutlivesSubjectTy, - ClosureRegionRequirements, ConstraintCategory, Local, Location, ReturnConstraint, - TerminatorKind, + AnnotationSource, BasicBlock, Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, + ClosureOutlivesSubjectTy, ClosureRegionRequirements, ConstraintCategory, Local, Location, + ReturnConstraint, TerminatorKind, }; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, UniverseIndex}; use rustc_mir_dataflow::points::DenseLocationMap; use rustc_span::Span; +use rustc_span::hygiene::DesugaringKind; use tracing::{debug, instrument, trace}; use crate::BorrowckInferCtxt; @@ -30,6 +31,7 @@ use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstra use crate::dataflow::BorrowIndex; use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo}; use crate::member_constraints::{MemberConstraintSet, NllMemberConstraintIndex}; +use crate::polonius::LiveLoans; use crate::polonius::legacy::PoloniusOutput; use crate::region_infer::reverse_sccs::ReverseSccGraph; use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues, ToElementIndex}; @@ -315,11 +317,6 @@ enum Trace<'tcx> { NotVisited, } -#[derive(Clone, PartialEq, Eq, Debug)] -pub(crate) enum ExtraConstraintInfo { - PlaceholderFromPredicate(Span), -} - #[instrument(skip(infcx, sccs), level = "debug")] fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: &ConstraintSccs) { use crate::renumber::RegionCtxt; @@ -396,7 +393,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { var_infos: VarInfos, constraints: MirTypeckRegionConstraints<'tcx>, universal_region_relations: Frozen>, - elements: Rc, + location_map: Rc, ) -> Self { let universal_regions = &universal_region_relations.universal_regions; let MirTypeckRegionConstraints { @@ -440,7 +437,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } let mut scc_values = - RegionValues::new(elements, universal_regions.len(), placeholder_indices); + RegionValues::new(location_map, universal_regions.len(), placeholder_indices); for region in liveness_constraints.regions() { let scc = constraint_sccs.scc(region); @@ -978,7 +975,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { propagated_outlives_requirements: &mut Vec>, ) -> bool { let tcx = infcx.tcx; - let TypeTest { generic_kind, lower_bound, span: blame_span, ref verify_bound } = *type_test; + let TypeTest { generic_kind, lower_bound, span: blame_span, verify_bound: _ } = *type_test; let generic_ty = generic_kind.to_ty(tcx); let Some(subject) = self.try_promote_type_test_subject(infcx, generic_ty) else { @@ -1016,25 +1013,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { // For each region outlived by lower_bound find a non-local, // universal region (it may be the same region) and add it to // `ClosureOutlivesRequirement`. + let mut found_outlived_universal_region = false; for ur in self.scc_values.universal_regions_outlived_by(r_scc) { + found_outlived_universal_region = true; debug!("universal_region_outlived_by ur={:?}", ur); - // Check whether we can already prove that the "subject" outlives `ur`. - // If so, we don't have to propagate this requirement to our caller. - // - // To continue the example from the function, if we are trying to promote - // a requirement that `T: 'X`, and we know that `'X = '1 + '2` (i.e., the union - // `'1` and `'2`), then in this loop `ur` will be `'1` (and `'2`). So here - // we check whether `T: '1` is something we *can* prove. If so, no need - // to propagate that requirement. - // - // This is needed because -- particularly in the case - // where `ur` is a local bound -- we are sometimes in a - // position to prove things that our caller cannot. See - // #53570 for an example. - if self.eval_verify_bound(infcx, generic_ty, ur, &verify_bound) { - continue; - } - let non_local_ub = self.universal_region_relations.non_local_upper_bounds(ur); debug!(?non_local_ub); @@ -1056,6 +1038,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { propagated_outlives_requirements.push(requirement); } } + // If we succeed to promote the subject, i.e. it only contains non-local regions, + // and fail to prove the type test inside of the closure, the `lower_bound` has to + // also be at least as large as some universal region, as the type test is otherwise + // trivial. + assert!(found_outlived_universal_region); true } @@ -1948,7 +1935,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { from_region: RegionVid, from_region_origin: NllRegionVariableOrigin, target_test: impl Fn(RegionVid) -> bool, - ) -> (BlameConstraint<'tcx>, Vec) { + ) -> (BlameConstraint<'tcx>, Vec>) { // Find all paths let (path, target_region) = self .find_constraint_paths_between_regions(from_region, target_test) @@ -1970,25 +1957,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { .collect::>() ); - let mut extra_info = vec![]; - for constraint in path.iter() { - let outlived = constraint.sub; - let Some(origin) = self.var_infos.get(outlived) else { - continue; - }; - let RegionVariableOrigin::Nll(NllRegionVariableOrigin::Placeholder(p)) = origin.origin - else { - continue; - }; - debug!(?constraint, ?p); - let ConstraintCategory::Predicate(span) = constraint.category else { - continue; - }; - extra_info.push(ExtraConstraintInfo::PlaceholderFromPredicate(span)); - // We only want to point to one - break; - } - // We try to avoid reporting a `ConstraintCategory::Predicate` as our best constraint. // Instead, we use it to produce an improved `ObligationCauseCode`. // FIXME - determine what we should do if we encounter multiple @@ -2007,42 +1975,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { }) .unwrap_or_else(|| ObligationCauseCode::Misc); - // Classify each of the constraints along the path. - let mut categorized_path: Vec> = path - .iter() - .map(|constraint| BlameConstraint { - category: constraint.category, - from_closure: constraint.from_closure, - cause: ObligationCause::new(constraint.span, CRATE_DEF_ID, cause_code.clone()), - variance_info: constraint.variance_info, - }) - .collect(); - debug!("categorized_path={:#?}", categorized_path); - - // To find the best span to cite, we first try to look for the - // final constraint that is interesting and where the `sup` is - // not unified with the ultimate target region. The reason - // for this is that we have a chain of constraints that lead - // from the source to the target region, something like: - // - // '0: '1 ('0 is the source) - // '1: '2 - // '2: '3 - // '3: '4 - // '4: '5 - // '5: '6 ('6 is the target) - // - // Some of those regions are unified with `'6` (in the same - // SCC). We want to screen those out. After that point, the - // "closest" constraint we have to the end is going to be the - // most likely to be the point where the value escapes -- but - // we still want to screen for an "interesting" point to - // highlight (e.g., a call site or something). - let target_scc = self.constraint_sccs.scc(target_region); - let mut range = 0..path.len(); - - // As noted above, when reporting an error, there is typically a chain of constraints - // leading from some "source" region which must outlive some "target" region. + // When reporting an error, there is typically a chain of constraints leading from some + // "source" region which must outlive some "target" region. // In most cases, we prefer to "blame" the constraints closer to the target -- // but there is one exception. When constraints arise from higher-ranked subtyping, // we generally prefer to blame the source value, @@ -2083,78 +2017,114 @@ impl<'tcx> RegionInferenceContext<'tcx> { | NllRegionVariableOrigin::Existential { from_forall: true } => false, }; - let find_region = |i: &usize| { - let constraint = &path[*i]; - - let constraint_sup_scc = self.constraint_sccs.scc(constraint.sup); - - if blame_source { - match categorized_path[*i].category { - ConstraintCategory::OpaqueType - | ConstraintCategory::Boring - | ConstraintCategory::BoringNoLocation - | ConstraintCategory::Internal - | ConstraintCategory::Predicate(_) => false, - ConstraintCategory::TypeAnnotation - | ConstraintCategory::Return(_) - | ConstraintCategory::Yield => true, - _ => constraint_sup_scc != target_scc, - } + // To pick a constraint to blame, we organize constraints by how interesting we expect them + // to be in diagnostics, then pick the most interesting one closest to either the source or + // the target on our constraint path. + let constraint_interest = |constraint: &OutlivesConstraint<'tcx>| { + // Try to avoid blaming constraints from desugarings, since they may not clearly match + // match what users have written. As an exception, allow blaming returns generated by + // `?` desugaring, since the correspondence is fairly clear. + let category = if let Some(kind) = constraint.span.desugaring_kind() + && (kind != DesugaringKind::QuestionMark + || !matches!(constraint.category, ConstraintCategory::Return(_))) + { + ConstraintCategory::Boring } else { - !matches!( - categorized_path[*i].category, - ConstraintCategory::OpaqueType - | ConstraintCategory::Boring - | ConstraintCategory::BoringNoLocation - | ConstraintCategory::Internal - | ConstraintCategory::Predicate(_) - ) - } - }; - - let best_choice = - if blame_source { range.rev().find(find_region) } else { range.find(find_region) }; - - debug!(?best_choice, ?blame_source, ?extra_info); + constraint.category + }; - if let Some(i) = best_choice { - if let Some(next) = categorized_path.get(i + 1) { - if matches!(categorized_path[i].category, ConstraintCategory::Return(_)) - && next.category == ConstraintCategory::OpaqueType + match category { + // Returns usually provide a type to blame and have specially written diagnostics, + // so prioritize them. + ConstraintCategory::Return(_) => 0, + // Unsizing coercions are interesting, since we have a note for that: + // `BorrowExplanation::add_object_lifetime_default_note`. + // FIXME(dianne): That note shouldn't depend on a coercion being blamed; see issue + // #131008 for an example of where we currently don't emit it but should. + // Once the note is handled properly, this case should be removed. Until then, it + // should be as limited as possible; the note is prone to false positives and this + // constraint usually isn't best to blame. + ConstraintCategory::Cast { + unsize_to: Some(unsize_ty), + is_implicit_coercion: true, + } if target_region == self.universal_regions().fr_static + // Mirror the note's condition, to minimize how often this diverts blame. + && let ty::Adt(_, args) = unsize_ty.kind() + && args.iter().any(|arg| arg.as_type().is_some_and(|ty| ty.is_trait())) + // Mimic old logic for this, to minimize false positives in tests. + && !path + .iter() + .any(|c| matches!(c.category, ConstraintCategory::TypeAnnotation(_))) => { - // The return expression is being influenced by the return type being - // impl Trait, point at the return type and not the return expr. - return (next.clone(), extra_info); + 1 } + // Between other interesting constraints, order by their position on the `path`. + ConstraintCategory::Yield + | ConstraintCategory::UseAsConst + | ConstraintCategory::UseAsStatic + | ConstraintCategory::TypeAnnotation( + AnnotationSource::Ascription + | AnnotationSource::Declaration + | AnnotationSource::OpaqueCast, + ) + | ConstraintCategory::Cast { .. } + | ConstraintCategory::CallArgument(_) + | ConstraintCategory::CopyBound + | ConstraintCategory::SizedBound + | ConstraintCategory::Assignment + | ConstraintCategory::Usage + | ConstraintCategory::ClosureUpvar(_) => 2, + // Generic arguments are unlikely to be what relates regions together + ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg) => 3, + // We handle predicates and opaque types specially; don't prioritize them here. + ConstraintCategory::Predicate(_) | ConstraintCategory::OpaqueType => 4, + // `Boring` constraints can correspond to user-written code and have useful spans, + // but don't provide any other useful information for diagnostics. + ConstraintCategory::Boring => 5, + // `BoringNoLocation` constraints can point to user-written code, but are less + // specific, and are not used for relations that would make sense to blame. + ConstraintCategory::BoringNoLocation => 6, + // Do not blame internal constraints. + ConstraintCategory::Internal => 7, + ConstraintCategory::IllegalUniverse => 8, } + }; - if categorized_path[i].category == ConstraintCategory::Return(ReturnConstraint::Normal) - { - let field = categorized_path.iter().find_map(|p| { - if let ConstraintCategory::ClosureUpvar(f) = p.category { - Some(f) - } else { - None - } - }); - - if let Some(field) = field { - categorized_path[i].category = - ConstraintCategory::Return(ReturnConstraint::ClosureUpvar(field)); - } - } + let best_choice = if blame_source { + path.iter().enumerate().rev().min_by_key(|(_, c)| constraint_interest(c)).unwrap().0 + } else { + path.iter().enumerate().min_by_key(|(_, c)| constraint_interest(c)).unwrap().0 + }; - return (categorized_path[i].clone(), extra_info); - } + debug!(?best_choice, ?blame_source); - // If that search fails, that is.. unusual. Maybe everything - // is in the same SCC or something. In that case, find what - // appears to be the most interesting point to report to the - // user via an even more ad-hoc guess. - categorized_path.sort_by_key(|p| p.category); - debug!("sorted_path={:#?}", categorized_path); + let best_constraint = if let Some(next) = path.get(best_choice + 1) + && matches!(path[best_choice].category, ConstraintCategory::Return(_)) + && next.category == ConstraintCategory::OpaqueType + { + // The return expression is being influenced by the return type being + // impl Trait, point at the return type and not the return expr. + *next + } else if path[best_choice].category == ConstraintCategory::Return(ReturnConstraint::Normal) + && let Some(field) = path.iter().find_map(|p| { + if let ConstraintCategory::ClosureUpvar(f) = p.category { Some(f) } else { None } + }) + { + OutlivesConstraint { + category: ConstraintCategory::Return(ReturnConstraint::ClosureUpvar(field)), + ..path[best_choice] + } + } else { + path[best_choice] + }; - (categorized_path.remove(0), extra_info) + let blame_constraint = BlameConstraint { + category: best_constraint.category, + from_closure: best_constraint.from_closure, + cause: ObligationCause::new(best_constraint.span, CRATE_DEF_ID, cause_code.clone()), + variance_info: best_constraint.variance_info, + }; + (blame_constraint, path) } pub(crate) fn universe_info(&self, universe: ty::UniverseIndex) -> UniverseInfo<'tcx> { @@ -2202,28 +2172,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.constraint_graph.region_graph(&self.constraints, self.universal_regions().fr_static) } - /// Returns whether the given region is considered live at all points: whether it is a - /// placeholder or a free region. - pub(crate) fn is_region_live_at_all_points(&self, region: RegionVid) -> bool { - // FIXME: there must be a cleaner way to find this information. At least, when - // higher-ranked subtyping is abstracted away from the borrowck main path, we'll only - // need to check whether this is a universal region. - let origin = self.region_definition(region).origin; - let live_at_all_points = matches!( - origin, - NllRegionVariableOrigin::Placeholder(_) | NllRegionVariableOrigin::FreeRegion - ); - live_at_all_points - } - - /// Returns whether the `loan_idx` is live at the given `location`: whether its issuing - /// region is contained within the type of a variable that is live at this point. - /// Note: for now, the sets of live loans is only available when using `-Zpolonius=next`. - pub(crate) fn is_loan_live_at(&self, loan_idx: BorrowIndex, location: Location) -> bool { - let point = self.liveness_constraints.point_from_location(location); - self.liveness_constraints.is_loan_live_at(loan_idx, point) - } - /// Returns the representative `RegionVid` for a given SCC. /// See `RegionTracker` for how a region variable ID is chosen. /// @@ -2239,6 +2187,20 @@ impl<'tcx> RegionInferenceContext<'tcx> { pub(crate) fn liveness_constraints(&self) -> &LivenessValues { &self.liveness_constraints } + + /// When using `-Zpolonius=next`, records the given live loans for the loan scopes and active + /// loans dataflow computations. + pub(crate) fn record_live_loans(&mut self, live_loans: LiveLoans) { + self.liveness_constraints.record_live_loans(live_loans); + } + + /// Returns whether the `loan_idx` is live at the given `location`: whether its issuing + /// region is contained within the type of a variable that is live at this point. + /// Note: for now, the sets of live loans is only available when using `-Zpolonius=next`. + pub(crate) fn is_loan_live_at(&self, loan_idx: BorrowIndex, location: Location) -> bool { + let point = self.liveness_constraints.point_from_location(location); + self.liveness_constraints.is_loan_live_at(loan_idx, point) + } } impl<'tcx> RegionDefinition<'tcx> { diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 3e16a3ca157d2..9c19f8b3ad825 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -1,6 +1,8 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; +use rustc_hir::OpaqueTyOrigin; use rustc_hir::def_id::LocalDefId; +use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, TyCtxtInferExt as _}; use rustc_macros::extension; use rustc_middle::ty::fold::fold_regions; @@ -10,6 +12,7 @@ use rustc_middle::ty::{ TypingMode, }; use rustc_span::Span; +use rustc_trait_selection::regions::OutlivesEnvironmentBuildExt; use rustc_trait_selection::traits::ObligationCtxt; use tracing::{debug, instrument}; @@ -152,7 +155,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { let (Ok(e) | Err(e)) = prev .build_mismatch_error( &OpaqueHiddenType { ty, span: concrete_type.span }, - opaque_type_key.def_id, infcx.tcx, ) .map(|d| d.emit()); @@ -164,10 +166,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { // FIXME(oli-obk): collect multiple spans for better diagnostics down the road. prev.span = prev.span.substitute_dummy(concrete_type.span); } else { - result.insert(opaque_type_key.def_id, OpaqueHiddenType { - ty, - span: concrete_type.span, - }); + result.insert( + opaque_type_key.def_id, + OpaqueHiddenType { ty, span: concrete_type.span }, + ); } // Check that all opaque types have the same region parameters if they have the same @@ -407,10 +409,6 @@ impl<'tcx> LazyOpaqueTyEnv<'tcx> { } fn get_canonical_args(&self) -> ty::GenericArgsRef<'tcx> { - use rustc_hir as hir; - use rustc_infer::infer::outlives::env::OutlivesEnvironment; - use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; - if let Some(&canonical_args) = self.canonical_args.get() { return canonical_args; } @@ -418,9 +416,9 @@ impl<'tcx> LazyOpaqueTyEnv<'tcx> { let &Self { tcx, def_id, .. } = self; let origin = tcx.local_opaque_ty_origin(def_id); let parent = match origin { - hir::OpaqueTyOrigin::FnReturn { parent, .. } - | hir::OpaqueTyOrigin::AsyncFn { parent, .. } - | hir::OpaqueTyOrigin::TyAlias { parent, .. } => parent, + OpaqueTyOrigin::FnReturn { parent, .. } + | OpaqueTyOrigin::AsyncFn { parent, .. } + | OpaqueTyOrigin::TyAlias { parent, .. } => parent, }; let param_env = tcx.param_env(parent); let args = GenericArgs::identity_for_item(tcx, parent).extend_to( @@ -440,8 +438,7 @@ impl<'tcx> LazyOpaqueTyEnv<'tcx> { tcx.dcx().span_delayed_bug(tcx.def_span(def_id), "error getting implied bounds"); Default::default() }); - let implied_bounds = infcx.implied_bounds_tys(param_env, parent, &wf_tys); - let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); + let outlives_env = OutlivesEnvironment::new(&infcx, parent, param_env, wf_tys); let mut seen = vec![tcx.lifetimes.re_static]; let canonical_args = fold_regions(tcx, args, |r1, _| { diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index e567f3a8b0de6..f1bcb353dc61c 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -11,6 +11,7 @@ use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex}; use tracing::debug; use crate::BorrowIndex; +use crate::polonius::LiveLoans; rustc_index::newtype_index! { /// A single integer representing a `ty::Placeholder`. @@ -38,7 +39,7 @@ pub(crate) enum RegionElement { /// an interval matrix storing liveness ranges for each region-vid. pub(crate) struct LivenessValues { /// The map from locations to points. - elements: Rc, + location_map: Rc, /// Which regions are live. This is exclusive with the fine-grained tracking in `points`, and /// currently only used for validating promoteds (which don't care about more precise tracking). @@ -50,39 +51,18 @@ pub(crate) struct LivenessValues { /// region is live, only that it is. points: Option>, - /// When using `-Zpolonius=next`, for each point: the loans flowing into the live regions at - /// that point. - pub(crate) loans: Option, -} - -/// Data used to compute the loans that are live at a given point in the CFG, when using -/// `-Zpolonius=next`. -pub(crate) struct LiveLoans { - /// The set of loans that flow into a given region. When individual regions are marked as live - /// in the CFG, these inflowing loans are recorded as live. - pub(crate) inflowing_loans: SparseBitMatrix, - - /// The set of loans that are live at a given point in the CFG. - pub(crate) live_loans: SparseBitMatrix, -} - -impl LiveLoans { - pub(crate) fn new(num_loans: usize) -> Self { - LiveLoans { - live_loans: SparseBitMatrix::new(num_loans), - inflowing_loans: SparseBitMatrix::new(num_loans), - } - } + /// When using `-Zpolonius=next`, the set of loans that are live at a given point in the CFG. + live_loans: Option, } impl LivenessValues { /// Create an empty map of regions to locations where they're live. - pub(crate) fn with_specific_points(elements: Rc) -> Self { + pub(crate) fn with_specific_points(location_map: Rc) -> Self { LivenessValues { live_regions: None, - points: Some(SparseIntervalMatrix::new(elements.num_points())), - elements, - loans: None, + points: Some(SparseIntervalMatrix::new(location_map.num_points())), + location_map, + live_loans: None, } } @@ -90,12 +70,12 @@ impl LivenessValues { /// /// Unlike `with_specific_points`, does not track exact locations where something is live, only /// which regions are live. - pub(crate) fn without_specific_points(elements: Rc) -> Self { + pub(crate) fn without_specific_points(location_map: Rc) -> Self { LivenessValues { live_regions: Some(Default::default()), points: None, - elements, - loans: None, + location_map, + live_loans: None, } } @@ -122,20 +102,13 @@ impl LivenessValues { /// Records `region` as being live at the given `location`. pub(crate) fn add_location(&mut self, region: RegionVid, location: Location) { - let point = self.elements.point_from_location(location); + let point = self.location_map.point_from_location(location); debug!("LivenessValues::add_location(region={:?}, location={:?})", region, location); if let Some(points) = &mut self.points { points.insert(region, point); - } else if self.elements.point_in_range(point) { + } else if self.location_map.point_in_range(point) { self.live_regions.as_mut().unwrap().insert(region); } - - // When available, record the loans flowing into this region as live at the given point. - if let Some(loans) = self.loans.as_mut() { - if let Some(inflowing) = loans.inflowing_loans.row(region) { - loans.live_loans.union_row(point, inflowing); - } - } } /// Records `region` as being live at all the given `points`. @@ -143,20 +116,9 @@ impl LivenessValues { debug!("LivenessValues::add_points(region={:?}, points={:?})", region, points); if let Some(this) = &mut self.points { this.union_row(region, points); - } else if points.iter().any(|point| self.elements.point_in_range(point)) { + } else if points.iter().any(|point| self.location_map.point_in_range(point)) { self.live_regions.as_mut().unwrap().insert(region); } - - // When available, record the loans flowing into this region as live at the given points. - if let Some(loans) = self.loans.as_mut() { - if let Some(inflowing) = loans.inflowing_loans.row(region) { - if !inflowing.is_empty() { - for point in points.iter() { - loans.live_loans.union_row(point, inflowing); - } - } - } - } } /// Records `region` as being live at all the control-flow points. @@ -170,7 +132,7 @@ impl LivenessValues { /// Returns whether `region` is marked live at the given `location`. pub(crate) fn is_live_at(&self, region: RegionVid, location: Location) -> bool { - let point = self.elements.point_from_location(location); + let point = self.location_map.point_from_location(location); if let Some(points) = &self.points { points.row(region).is_some_and(|r| r.contains(point)) } else { @@ -191,33 +153,39 @@ impl LivenessValues { .row(region) .into_iter() .flat_map(|set| set.iter()) - .take_while(|&p| self.elements.point_in_range(p)) + .take_while(|&p| self.location_map.point_in_range(p)) } /// For debugging purposes, returns a pretty-printed string of the points where the `region` is /// live. pub(crate) fn pretty_print_live_points(&self, region: RegionVid) -> String { pretty_print_region_elements( - self.live_points(region).map(|p| RegionElement::Location(self.elements.to_location(p))), + self.live_points(region) + .map(|p| RegionElement::Location(self.location_map.to_location(p))), ) } #[inline] pub(crate) fn point_from_location(&self, location: Location) -> PointIndex { - self.elements.point_from_location(location) + self.location_map.point_from_location(location) } #[inline] pub(crate) fn location_from_point(&self, point: PointIndex) -> Location { - self.elements.to_location(point) + self.location_map.to_location(point) + } + + /// When using `-Zpolonius=next`, records the given live loans for the loan scopes and active + /// loans dataflow computations. + pub(crate) fn record_live_loans(&mut self, live_loans: LiveLoans) { + self.live_loans = Some(live_loans); } /// When using `-Zpolonius=next`, returns whether the `loan_idx` is live at the given `point`. pub(crate) fn is_loan_live_at(&self, loan_idx: BorrowIndex, point: PointIndex) -> bool { - self.loans + self.live_loans .as_ref() .expect("Accessing live loans requires `-Zpolonius=next`") - .live_loans .contains(point, loan_idx) } } @@ -272,7 +240,7 @@ impl PlaceholderIndices { /// because (since it is returned) it must live for at least `'a`. But /// it would also contain various points from within the function. pub(crate) struct RegionValues { - elements: Rc, + location_map: Rc, placeholder_indices: PlaceholderIndices, points: SparseIntervalMatrix, free_regions: SparseBitMatrix, @@ -287,14 +255,14 @@ impl RegionValues { /// Each of the regions in num_region_variables will be initialized with an /// empty set of points and no causal information. pub(crate) fn new( - elements: Rc, + location_map: Rc, num_universal_regions: usize, placeholder_indices: PlaceholderIndices, ) -> Self { - let num_points = elements.num_points(); + let num_points = location_map.num_points(); let num_placeholders = placeholder_indices.len(); Self { - elements, + location_map, points: SparseIntervalMatrix::new(num_points), placeholder_indices, free_regions: SparseBitMatrix::new(num_universal_regions), @@ -336,7 +304,7 @@ impl RegionValues { end: usize, ) -> Option { let row = self.points.row(r)?; - let block = self.elements.entry_point(block); + let block = self.location_map.entry_point(block); let start = block.plus(start); let end = block.plus(end); let first_unset = row.first_unset_in(start..=end)?; @@ -375,8 +343,8 @@ impl RegionValues { pub(crate) fn locations_outlived_by<'a>(&'a self, r: N) -> impl Iterator + 'a { self.points.row(r).into_iter().flat_map(move |set| { set.iter() - .take_while(move |&p| self.elements.point_in_range(p)) - .map(move |p| self.elements.to_location(p)) + .take_while(move |&p| self.location_map.point_in_range(p)) + .map(move |p| self.location_map.to_location(p)) }) } @@ -430,12 +398,12 @@ pub(crate) trait ToElementIndex: Debug + Copy { impl ToElementIndex for Location { fn add_to_row(self, values: &mut RegionValues, row: N) -> bool { - let index = values.elements.point_from_location(self); + let index = values.location_map.point_from_location(self); values.points.insert(row, index) } fn contained_in_row(self, values: &RegionValues, row: N) -> bool { - let index = values.elements.point_from_location(self); + let index = values.location_map.point_from_location(self); values.points.contains(row, index) } } @@ -464,14 +432,14 @@ impl ToElementIndex for ty::PlaceholderRegion { /// For debugging purposes, returns a pretty-printed string of the given points. pub(crate) fn pretty_print_points( - elements: &DenseLocationMap, + location_map: &DenseLocationMap, points: impl IntoIterator, ) -> String { pretty_print_region_elements( points .into_iter() - .take_while(|&p| elements.point_in_range(p)) - .map(|p| elements.to_location(p)) + .take_while(|&p| location_map.point_in_range(p)) + .map(|p| location_map.to_location(p)) .map(RegionElement::Location), ) } @@ -544,12 +512,12 @@ fn pretty_print_region_elements(elements: impl IntoIterator), Placeholder(Symbol), diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs index 627444a4ce5b8..11b30c145c2c2 100644 --- a/compiler/rustc_borrowck/src/session_diagnostics.rs +++ b/compiler/rustc_borrowck/src/session_diagnostics.rs @@ -459,17 +459,17 @@ pub(crate) enum OnClosureNote<'a> { } #[derive(Subdiagnostic)] -pub(crate) enum TypeNoCopy<'a, 'tcx> { +pub(crate) enum TypeNoCopy<'a> { #[label(borrowck_ty_no_impl_copy)] Label { is_partial_move: bool, - ty: Ty<'tcx>, + ty: String, place: &'a str, #[primary_span] span: Span, }, #[note(borrowck_ty_no_impl_copy)] - Note { is_partial_move: bool, ty: Ty<'tcx>, place: &'a str }, + Note { is_partial_move: bool, ty: String, place: &'a str }, } #[derive(Diagnostic)] @@ -480,3 +480,10 @@ pub(crate) struct SimdIntrinsicArgConst { pub arg: usize, pub intrinsic: String, } + +#[derive(LintDiagnostic)] +#[diag(borrowck_tail_expr_drop_order)] +pub(crate) struct TailExprDropOrder { + #[label] + pub borrowed: Span, +} diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index 3903c45fda529..b3fa786a5177c 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -149,6 +149,18 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.normalize_with_category(value, location, ConstraintCategory::Boring) } + pub(super) fn deeply_normalize(&mut self, value: T, location: impl NormalizeLocation) -> T + where + T: type_op::normalize::Normalizable<'tcx> + fmt::Display + Copy + 'tcx, + { + let result: Result<_, ErrorGuaranteed> = self.fully_perform_op( + location.to_locations(), + ConstraintCategory::Boring, + self.infcx.param_env.and(type_op::normalize::DeeplyNormalize { value }), + ); + result.unwrap_or(value) + } + #[instrument(skip(self), level = "debug")] pub(super) fn normalize_with_category( &mut self, @@ -185,7 +197,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { CustomTypeOp::new( |ocx| { let structurally_normalize = |ty| { - ocx.structurally_normalize( + ocx.structurally_normalize_ty( &ObligationCause::misc( location.to_locations().span(body), body.source.def_id().expect_local(), @@ -230,7 +242,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ConstraintCategory::Boring, CustomTypeOp::new( |ocx| { - ocx.structurally_normalize( + ocx.structurally_normalize_ty( &ObligationCause::misc( location.to_locations().span(body), body.source.def_id().expect_local(), diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 4b7f53213886e..3b48ca305c45b 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -4,15 +4,12 @@ use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate}; use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound}; use rustc_infer::infer::{self, InferCtxt, SubregionOrigin}; +use rustc_infer::traits::query::type_op::DeeplyNormalize; use rustc_middle::bug; use rustc_middle::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, ConstraintCategory}; -use rustc_middle::traits::ObligationCause; -use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; use rustc_span::Span; -use rustc_trait_selection::traits::ScrubbedTraitError; -use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput}; use tracing::{debug, instrument}; @@ -270,20 +267,8 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { ConstraintCategory<'tcx>, )>, ) -> Ty<'tcx> { - let result = CustomTypeOp::new( - |ocx| { - ocx.deeply_normalize( - &ObligationCause::dummy_with_span(self.span), - self.param_env, - ty, - ) - .map_err(|_: Vec>| NoSolution) - }, - "normalize type outlives obligation", - ) - .fully_perform(self.infcx, self.span); - - match result { + match self.param_env.and(DeeplyNormalize { value: ty }).fully_perform(self.infcx, self.span) + { Ok(TypeOpOutput { output: ty, constraints, .. }) => { if let Some(QueryRegionConstraints { outlives }) = constraints { next_outlives_predicates.extend(outlives.iter().copied()); diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index ea965eb654583..efbae1e153537 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -5,13 +5,11 @@ use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::region_constraints::GenericKind; use rustc_infer::infer::{InferCtxt, outlives}; +use rustc_infer::traits::query::type_op::DeeplyNormalize; use rustc_middle::mir::ConstraintCategory; -use rustc_middle::traits::ObligationCause; use rustc_middle::traits::query::OutlivesBound; use rustc_middle::ty::{self, RegionVid, Ty, TypeVisitableExt}; use rustc_span::{ErrorGuaranteed, Span}; -use rustc_trait_selection::error_reporting::InferCtxtErrorExt; -use rustc_trait_selection::solve::deeply_normalize; use rustc_trait_selection::traits::query::type_op::{self, TypeOp}; use tracing::{debug, instrument}; use type_op::TypeOpOutput; @@ -229,24 +227,14 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { let mut constraints = vec![]; let mut known_type_outlives_obligations = vec![]; for bound in param_env.caller_bounds() { - let Some(mut outlives) = bound.as_type_outlives_clause() else { continue }; - - // In the new solver, normalize the type-outlives obligation assumptions. - if self.infcx.next_trait_solver() { - match deeply_normalize( - self.infcx.at(&ObligationCause::misc(span, defining_ty_def_id), param_env), + if let Some(outlives) = bound.as_type_outlives_clause() { + self.normalize_and_push_type_outlives_obligation( outlives, - ) { - Ok(normalized_outlives) => { - outlives = normalized_outlives; - } - Err(e) => { - self.infcx.err_ctxt().report_fulfillment_errors(e); - } - } - } - - known_type_outlives_obligations.push(outlives); + span, + &mut known_type_outlives_obligations, + &mut constraints, + ); + }; } let unnormalized_input_output_tys = self @@ -276,7 +264,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { } let TypeOpOutput { output: norm_ty, constraints: constraints_normalize, .. } = param_env - .and(type_op::normalize::Normalize { value: ty }) + .and(DeeplyNormalize { value: ty }) .fully_perform(self.infcx, span) .unwrap_or_else(|guar| TypeOpOutput { output: Ty::new_error(self.infcx.tcx, guar), @@ -312,9 +300,8 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { // Add implied bounds from impl header. if matches!(tcx.def_kind(defining_ty_def_id), DefKind::AssocFn | DefKind::AssocConst) { for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(defining_ty_def_id)) { - let result: Result<_, ErrorGuaranteed> = param_env - .and(type_op::normalize::Normalize { value: ty }) - .fully_perform(self.infcx, span); + let result: Result<_, ErrorGuaranteed> = + param_env.and(DeeplyNormalize { value: ty }).fully_perform(self.infcx, span); let Ok(TypeOpOutput { output: norm_ty, constraints: c, .. }) = result else { continue; }; @@ -356,6 +343,36 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { } } + fn normalize_and_push_type_outlives_obligation( + &self, + mut outlives: ty::PolyTypeOutlivesPredicate<'tcx>, + span: Span, + known_type_outlives_obligations: &mut Vec>, + constraints: &mut Vec<&QueryRegionConstraints<'tcx>>, + ) { + // In the new solver, normalize the type-outlives obligation assumptions. + if self.infcx.next_trait_solver() { + let Ok(TypeOpOutput { + output: normalized_outlives, + constraints: constraints_normalize, + error_info: _, + }) = self + .param_env + .and(DeeplyNormalize { value: outlives }) + .fully_perform(self.infcx, span) + else { + self.infcx.dcx().delayed_bug(format!("could not normalize {outlives:?}")); + return; + }; + outlives = normalized_outlives; + if let Some(c) = constraints_normalize { + constraints.push(c); + } + } + + known_type_outlives_obligations.push(outlives); + } + /// Update the type of a single local, which should represent /// either the return type of the MIR or one of its arguments. At /// the same time, compute and add any implied bounds that come diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index 1f2ec168f0322..f70b17e33624b 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -75,17 +75,20 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let output_ty = Ty::new_coroutine( self.tcx(), self.tcx().coroutine_for_closure(mir_def_id), - ty::CoroutineArgs::new(self.tcx(), ty::CoroutineArgsParts { - parent_args: args.parent_args(), - kind_ty: Ty::from_coroutine_closure_kind(self.tcx(), args.kind()), - return_ty: user_provided_sig.output(), - tupled_upvars_ty, - // For async closures, none of these can be annotated, so just fill - // them with fresh ty vars. - resume_ty: next_ty_var(), - yield_ty: next_ty_var(), - witness: next_ty_var(), - }) + ty::CoroutineArgs::new( + self.tcx(), + ty::CoroutineArgsParts { + parent_args: args.parent_args(), + kind_ty: Ty::from_coroutine_closure_kind(self.tcx(), args.kind()), + return_ty: user_provided_sig.output(), + tupled_upvars_ty, + // For async closures, none of these can be annotated, so just fill + // them with fresh ty vars. + resume_ty: next_ty_var(), + yield_ty: next_ty_var(), + witness: next_ty_var(), + }, + ) .args, ); diff --git a/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs b/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs index 695a1cdac0d79..6182b68f6f411 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/local_use_map.rs @@ -82,7 +82,7 @@ impl<'a> Iterator for AppearancesIter<'a> { impl LocalUseMap { pub(crate) fn build( live_locals: &[Local], - elements: &DenseLocationMap, + location_map: &DenseLocationMap, body: &Body<'_>, ) -> Self { let nones = IndexVec::from_elem(None, &body.local_decls); @@ -101,7 +101,7 @@ impl LocalUseMap { IndexVec::from_elem(false, &body.local_decls); live_locals.iter().for_each(|&local| locals_with_use_data[local] = true); - LocalUseMapBuild { local_use_map: &mut local_use_map, elements, locals_with_use_data } + LocalUseMapBuild { local_use_map: &mut local_use_map, location_map, locals_with_use_data } .visit_body(body); local_use_map @@ -125,7 +125,7 @@ impl LocalUseMap { struct LocalUseMapBuild<'me> { local_use_map: &'me mut LocalUseMap, - elements: &'me DenseLocationMap, + location_map: &'me DenseLocationMap, // Vector used in `visit_local` to signal which `Local`s do we need // def/use/drop information on, constructed from `live_locals` (that @@ -147,7 +147,7 @@ impl Visitor<'_> for LocalUseMapBuild<'_> { DefUse::Use => &mut self.local_use_map.first_use_at[local], DefUse::Drop => &mut self.local_use_map.first_drop_at[local], }; - let point_index = self.elements.point_from_location(location); + let point_index = self.location_map.point_from_location(location); let appearance_index = self .local_use_map .appearances diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs index 3e9900cce5f57..dcc179030020d 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs @@ -14,7 +14,7 @@ use tracing::debug; use super::TypeChecker; use crate::constraints::OutlivesConstraintSet; -use crate::polonius::PoloniusContext; +use crate::polonius::PoloniusLivenessContext; use crate::region_infer::values::LivenessValues; use crate::universal_regions::UniversalRegions; @@ -32,24 +32,37 @@ mod trace; pub(super) fn generate<'a, 'tcx>( typeck: &mut TypeChecker<'_, 'tcx>, body: &Body<'tcx>, - elements: &DenseLocationMap, + location_map: &DenseLocationMap, flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, move_data: &MoveData<'tcx>, ) { debug!("liveness::generate"); - let free_regions = regions_that_outlive_free_regions( + let mut free_regions = regions_that_outlive_free_regions( typeck.infcx.num_region_vars(), &typeck.universal_regions, &typeck.constraints.outlives_constraints, ); + + // NLLs can avoid computing some liveness data here because its constraints are + // location-insensitive, but that doesn't work in polonius: locals whose type contains a region + // that outlives a free region are not necessarily live everywhere in a flow-sensitive setting, + // unlike NLLs. + // We do record these regions in the polonius context, since they're used to differentiate + // relevant and boring locals, which is a key distinction used later in diagnostics. + if typeck.tcx().sess.opts.unstable_opts.polonius.is_next_enabled() { + let (_, boring_locals) = compute_relevant_live_locals(typeck.tcx(), &free_regions, body); + typeck.polonius_liveness.as_mut().unwrap().boring_nll_locals = + boring_locals.into_iter().collect(); + free_regions = typeck.universal_regions.universal_regions_iter().collect(); + } let (relevant_live_locals, boring_locals) = compute_relevant_live_locals(typeck.tcx(), &free_regions, body); trace::trace( typeck, body, - elements, + location_map, flow_inits, move_data, relevant_live_locals, @@ -62,7 +75,7 @@ pub(super) fn generate<'a, 'tcx>( typeck.tcx(), &mut typeck.constraints.liveness_constraints, &typeck.universal_regions, - &mut typeck.polonius_context, + &mut typeck.polonius_liveness, body, ); } @@ -139,11 +152,11 @@ fn record_regular_live_regions<'tcx>( tcx: TyCtxt<'tcx>, liveness_constraints: &mut LivenessValues, universal_regions: &UniversalRegions<'tcx>, - polonius_context: &mut Option, + polonius_liveness: &mut Option, body: &Body<'tcx>, ) { let mut visitor = - LiveVariablesVisitor { tcx, liveness_constraints, universal_regions, polonius_context }; + LiveVariablesVisitor { tcx, liveness_constraints, universal_regions, polonius_liveness }; for (bb, data) in body.basic_blocks.iter_enumerated() { visitor.visit_basic_block_data(bb, data); } @@ -154,7 +167,7 @@ struct LiveVariablesVisitor<'a, 'tcx> { tcx: TyCtxt<'tcx>, liveness_constraints: &'a mut LivenessValues, universal_regions: &'a UniversalRegions<'tcx>, - polonius_context: &'a mut Option, + polonius_liveness: &'a mut Option, } impl<'a, 'tcx> Visitor<'tcx> for LiveVariablesVisitor<'a, 'tcx> { @@ -206,8 +219,8 @@ impl<'a, 'tcx> LiveVariablesVisitor<'a, 'tcx> { }); // When using `-Zpolonius=next`, we record the variance of each live region. - if let Some(polonius_context) = self.polonius_context { - polonius_context.record_live_region_variance(self.tcx, self.universal_regions, value); + if let Some(polonius_liveness) = self.polonius_liveness { + polonius_liveness.record_live_region_variance(self.tcx, self.universal_regions, value); } } } diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index c7a2d32b31dc8..62d49a62744e7 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -1,5 +1,5 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_index::interval::IntervalSet; use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::infer::outlives::for_liveness; @@ -16,7 +16,7 @@ use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOp, Type use tracing::debug; use crate::polonius; -use crate::region_infer::values::{self, LiveLoans}; +use crate::region_infer::values; use crate::type_check::liveness::local_use_map::LocalUseMap; use crate::type_check::{NormalizeLocation, TypeChecker}; @@ -37,49 +37,18 @@ use crate::type_check::{NormalizeLocation, TypeChecker}; pub(super) fn trace<'a, 'tcx>( typeck: &mut TypeChecker<'_, 'tcx>, body: &Body<'tcx>, - elements: &DenseLocationMap, + location_map: &DenseLocationMap, flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, move_data: &MoveData<'tcx>, relevant_live_locals: Vec, boring_locals: Vec, ) { - let local_use_map = &LocalUseMap::build(&relevant_live_locals, elements, body); - - // When using `-Zpolonius=next`, compute the set of loans that can reach a given region. - if typeck.tcx().sess.opts.unstable_opts.polonius.is_next_enabled() { - let borrow_set = &typeck.borrow_set; - let mut live_loans = LiveLoans::new(borrow_set.len()); - let outlives_constraints = &typeck.constraints.outlives_constraints; - let graph = outlives_constraints.graph(typeck.infcx.num_region_vars()); - let region_graph = - graph.region_graph(outlives_constraints, typeck.universal_regions.fr_static); - - // Traverse each issuing region's constraints, and record the loan as flowing into the - // outlived region. - for (loan, issuing_region_data) in borrow_set.iter_enumerated() { - for succ in rustc_data_structures::graph::depth_first_search( - ®ion_graph, - issuing_region_data.region, - ) { - // We don't need to mention that a loan flows into its issuing region. - if succ == issuing_region_data.region { - continue; - } - - live_loans.inflowing_loans.insert(succ, loan); - } - } - - // Store the inflowing loans in the liveness constraints: they will be used to compute live - // loans when liveness data is recorded there. - typeck.constraints.liveness_constraints.loans = Some(live_loans); - }; - + let local_use_map = &LocalUseMap::build(&relevant_live_locals, location_map, body); let cx = LivenessContext { typeck, body, flow_inits, - elements, + location_map, local_use_map, move_data, drop_data: FxIndexMap::default(), @@ -100,7 +69,7 @@ struct LivenessContext<'a, 'typeck, 'b, 'tcx> { typeck: &'a mut TypeChecker<'typeck, 'tcx>, /// Defines the `PointIndex` mapping - elements: &'a DenseLocationMap, + location_map: &'a DenseLocationMap, /// MIR we are analyzing. body: &'a Body<'tcx>, @@ -129,7 +98,7 @@ struct LivenessResults<'a, 'typeck, 'b, 'tcx> { cx: LivenessContext<'a, 'typeck, 'b, 'tcx>, /// Set of points that define the current local. - defs: BitSet, + defs: DenseBitSet, /// Points where the current variable is "use live" -- meaning /// that there is a future "full use" that may use its value. @@ -149,10 +118,10 @@ struct LivenessResults<'a, 'typeck, 'b, 'tcx> { impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { fn new(cx: LivenessContext<'a, 'typeck, 'b, 'tcx>) -> Self { - let num_points = cx.elements.num_points(); + let num_points = cx.location_map.num_points(); LivenessResults { cx, - defs: BitSet::new_empty(num_points), + defs: DenseBitSet::new_empty(num_points), use_live_at: IntervalSet::new(num_points), drop_live_at: IntervalSet::new(num_points), drop_locations: vec![], @@ -213,14 +182,14 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { fn add_extra_drop_facts(&mut self, relevant_live_locals: &[Local]) { // This collect is more necessary than immediately apparent // because these facts go into `add_drop_live_facts_for()`, - // which also writes to `all_facts`, and so this is genuinely + // which also writes to `polonius_facts`, and so this is genuinely // a simultaneous overlapping mutable borrow. // FIXME for future hackers: investigate whether this is // actually necessary; these facts come from Polonius // and probably maybe plausibly does not need to go back in. // It may be necessary to just pick out the parts of // `add_drop_live_facts_for()` that make sense. - let Some(facts) = self.cx.typeck.all_facts.as_ref() else { return }; + let Some(facts) = self.cx.typeck.polonius_facts.as_ref() else { return }; let facts_to_add: Vec<_> = { let relevant_live_locals: FxIndexSet<_> = relevant_live_locals.iter().copied().collect(); @@ -240,7 +209,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { .collect() }; - let live_at = IntervalSet::new(self.cx.elements.num_points()); + let live_at = IntervalSet::new(self.cx.location_map.num_points()); for (local, local_ty, location) in facts_to_add { self.cx.add_drop_live_facts_for(local, local_ty, &[location], &live_at); } @@ -279,7 +248,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { // * Inclusively, the block start // * Exclusively, the previous definition (if it's in this block) // * Exclusively, the previous live_at setting (an optimization) - let block_start = self.cx.elements.to_block_start(p); + let block_start = self.cx.location_map.to_block_start(p); let previous_defs = self.defs.last_set_in(block_start..=p); let previous_live_at = self.use_live_at.last_set_in(block_start..=p); @@ -303,12 +272,12 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { // terminators of predecessor basic blocks. Push those onto the // stack so that the next iteration(s) will process them. - let block = self.cx.elements.to_location(block_start).block; + let block = self.cx.location_map.to_location(block_start).block; self.stack.extend( self.cx.body.basic_blocks.predecessors()[block] .iter() .map(|&pred_bb| self.cx.body.terminator_loc(pred_bb)) - .map(|pred_loc| self.cx.elements.point_from_location(pred_loc)), + .map(|pred_loc| self.cx.location_map.point_from_location(pred_loc)), ); } } @@ -331,7 +300,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { // Find the drops where `local` is initialized. for drop_point in self.cx.local_use_map.drops(local) { - let location = self.cx.elements.to_location(drop_point); + let location = self.cx.location_map.to_location(drop_point); debug_assert_eq!(self.cx.body.terminator_loc(location.block), location,); if self.cx.initialized_at_terminator(location.block, mpi) @@ -367,7 +336,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { debug!( "compute_drop_live_points_for_block(mpi={:?}, term_point={:?})", self.cx.move_data.move_paths[mpi].place, - self.cx.elements.to_location(term_point), + self.cx.location_map.to_location(term_point), ); // We are only invoked with terminators where `mpi` is @@ -377,12 +346,15 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { // Otherwise, scan backwards through the statements in the // block. One of them may be either a definition or use // live point. - let term_location = self.cx.elements.to_location(term_point); + let term_location = self.cx.location_map.to_location(term_point); debug_assert_eq!(self.cx.body.terminator_loc(term_location.block), term_location,); let block = term_location.block; - let entry_point = self.cx.elements.entry_point(term_location.block); + let entry_point = self.cx.location_map.entry_point(term_location.block); for p in (entry_point..term_point).rev() { - debug!("compute_drop_live_points_for_block: p = {:?}", self.cx.elements.to_location(p)); + debug!( + "compute_drop_live_points_for_block: p = {:?}", + self.cx.location_map.to_location(p) + ); if self.defs.contains(p) { debug!("compute_drop_live_points_for_block: def site"); @@ -428,7 +400,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> { } let pred_term_loc = self.cx.body.terminator_loc(pred_block); - let pred_term_point = self.cx.elements.point_from_location(pred_term_loc); + let pred_term_point = self.cx.location_map.point_from_location(pred_term_loc); // If the terminator of this predecessor either *assigns* // our value or is a "normal use", then stop. @@ -523,7 +495,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { /// points `live_at`. fn add_use_live_facts_for(&mut self, value: Ty<'tcx>, live_at: &IntervalSet) { debug!("add_use_live_facts_for(value={:?})", value); - Self::make_all_regions_live(self.elements, self.typeck, value, live_at); + Self::make_all_regions_live(self.location_map, self.typeck, value, live_at); } /// Some variable with type `live_ty` is "drop live" at `location` @@ -547,7 +519,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { dropped_local, dropped_ty, drop_locations, - values::pretty_print_points(self.elements, live_at.iter()), + values::pretty_print_points(self.location_map, live_at.iter()), ); let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({ @@ -574,19 +546,19 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { // All things in the `outlives` array may be touched by // the destructor and must be live at this point. for &kind in &drop_data.dropck_result.kinds { - Self::make_all_regions_live(self.elements, self.typeck, kind, live_at); + Self::make_all_regions_live(self.location_map, self.typeck, kind, live_at); polonius::legacy::emit_drop_facts( self.typeck.tcx(), dropped_local, &kind, self.typeck.universal_regions, - self.typeck.all_facts, + self.typeck.polonius_facts, ); } } fn make_all_regions_live( - elements: &DenseLocationMap, + location_map: &DenseLocationMap, typeck: &mut TypeChecker<'_, 'tcx>, value: impl TypeVisitable> + Relate>, live_at: &IntervalSet, @@ -594,7 +566,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { debug!("make_all_regions_live(value={:?})", value); debug!( "make_all_regions_live: live_at={}", - values::pretty_print_points(elements, live_at.iter()), + values::pretty_print_points(location_map, live_at.iter()), ); value.visit_with(&mut for_liveness::FreeRegionsVisitor { @@ -608,8 +580,8 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> { }); // When using `-Zpolonius=next`, we record the variance of each live region. - if let Some(polonius_context) = typeck.polonius_context { - polonius_context.record_live_region_variance( + if let Some(polonius_liveness) = typeck.polonius_liveness.as_mut() { + polonius_liveness.record_live_region_variance( typeck.infcx.tcx, typeck.universal_regions, value, diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 10fb8a399a267..93081919ec79d 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -48,8 +48,8 @@ use crate::borrow_set::BorrowSet; use crate::constraints::{OutlivesConstraint, OutlivesConstraintSet}; use crate::diagnostics::UniverseInfo; use crate::member_constraints::MemberConstraintSet; -use crate::polonius::PoloniusContext; -use crate::polonius::legacy::{AllFacts, LocationTable}; +use crate::polonius::legacy::{PoloniusFacts, PoloniusLocationTable}; +use crate::polonius::{PoloniusContext, PoloniusLivenessContext}; use crate::region_infer::TypeTest; use crate::region_infer::values::{LivenessValues, PlaceholderIndex, PlaceholderIndices}; use crate::renumber::RegionCtxt; @@ -98,29 +98,29 @@ mod relate_tys; /// - `body` -- MIR body to type-check /// - `promoted` -- map of promoted constants within `body` /// - `universal_regions` -- the universal regions from `body`s function signature -/// - `location_table` -- MIR location map of `body` +/// - `location_table` -- for datalog polonius, the map between `Location`s and `RichLocation`s /// - `borrow_set` -- information about borrows occurring in `body` -/// - `all_facts` -- when using Polonius, this is the generated set of Polonius facts +/// - `polonius_facts` -- when using Polonius, this is the generated set of Polonius facts /// - `flow_inits` -- results of a maybe-init dataflow analysis /// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis -/// - `elements` -- MIR region map +/// - `location_map` -- map between MIR `Location` and `PointIndex` pub(crate) fn type_check<'a, 'tcx>( infcx: &BorrowckInferCtxt<'tcx>, body: &Body<'tcx>, promoted: &IndexSlice>, universal_regions: UniversalRegions<'tcx>, - location_table: &LocationTable, + location_table: &PoloniusLocationTable, borrow_set: &BorrowSet<'tcx>, - all_facts: &mut Option, + polonius_facts: &mut Option, flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, move_data: &MoveData<'tcx>, - elements: Rc, + location_map: Rc, ) -> MirTypeckResults<'tcx> { let implicit_region_bound = ty::Region::new_var(infcx.tcx, universal_regions.fr_fn_body); let mut constraints = MirTypeckRegionConstraints { placeholder_indices: PlaceholderIndices::default(), placeholder_index_to_region: IndexVec::default(), - liveness_constraints: LivenessValues::with_specific_points(Rc::clone(&elements)), + liveness_constraints: LivenessValues::with_specific_points(Rc::clone(&location_map)), outlives_constraints: OutlivesConstraintSet::default(), member_constraints: MemberConstraintSet::default(), type_tests: Vec::default(), @@ -148,8 +148,8 @@ pub(crate) fn type_check<'a, 'tcx>( debug!(?normalized_inputs_and_output); - let mut polonius_context = if infcx.tcx.sess.opts.unstable_opts.polonius.is_next_enabled() { - Some(PoloniusContext::new()) + let polonius_liveness = if infcx.tcx.sess.opts.unstable_opts.polonius.is_next_enabled() { + Some(PoloniusLivenessContext::default()) } else { None }; @@ -165,10 +165,10 @@ pub(crate) fn type_check<'a, 'tcx>( reported_errors: Default::default(), universal_regions: &universal_region_relations.universal_regions, location_table, - all_facts, + polonius_facts, borrow_set, constraints: &mut constraints, - polonius_context: &mut polonius_context, + polonius_liveness, }; typeck.check_user_type_annotations(); @@ -180,16 +180,19 @@ pub(crate) fn type_check<'a, 'tcx>( typeck.equate_inputs_and_outputs(body, &normalized_inputs_and_output); typeck.check_signature_annotation(body); - liveness::generate(&mut typeck, body, &elements, flow_inits, move_data); + liveness::generate(&mut typeck, body, &location_map, flow_inits, move_data); let opaque_type_values = opaque_types::take_opaques_and_register_member_constraints(&mut typeck); - if let Some(polonius_context) = typeck.polonius_context.as_mut() { - let num_regions = infcx.num_region_vars(); - let points_per_live_region = typeck.constraints.liveness_constraints.points(); - polonius_context.record_live_regions_per_point(num_regions, points_per_live_region); - } + // We're done with typeck, we can finalize the polonius liveness context for region inference. + let polonius_context = typeck.polonius_liveness.take().map(|liveness_context| { + PoloniusContext::create_from_liveness( + liveness_context, + infcx.num_region_vars(), + typeck.constraints.liveness_constraints.points(), + ) + }); MirTypeckResults { constraints, @@ -298,7 +301,26 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { context.ambient_variance(), base_ty.ty, location.to_locations(), - ConstraintCategory::TypeAnnotation, + ConstraintCategory::TypeAnnotation(AnnotationSource::OpaqueCast), + ) + .unwrap(); + } + ProjectionElem::UnwrapUnsafeBinder(ty) => { + let ty::UnsafeBinder(binder_ty) = *base_ty.ty.kind() else { + unreachable!(); + }; + let found_ty = self.typeck.infcx.instantiate_binder_with_fresh_vars( + self.body().source_info(location).span, + BoundRegionConversionTime::HigherRankedType, + binder_ty.into(), + ); + self.typeck + .relate_types( + ty, + context.ambient_variance(), + found_ty, + location.to_locations(), + ConstraintCategory::Boring, ) .unwrap(); } @@ -333,7 +355,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { ty::Invariant, &UserTypeProjection { base: annotation_index, projs: vec![] }, locations, - ConstraintCategory::Boring, + ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg), ) { let annotation = &self.typeck.user_type_annotations[annotation_index]; span_mirbug!( @@ -349,8 +371,8 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { let tcx = self.tcx(); let maybe_uneval = match constant.const_ { Const::Ty(_, ct) => match ct.kind() { - ty::ConstKind::Unevaluated(_) => { - bug!("should not encounter unevaluated Const::Ty here, got {:?}", ct) + ty::ConstKind::Unevaluated(uv) => { + Some(UnevaluatedConst { def: uv.def, args: uv.args, promoted: None }) } _ => None, }, @@ -389,10 +411,10 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { } else { self.typeck.ascribe_user_type( constant.const_.ty(), - ty::UserType::new(ty::UserTypeKind::TypeOf(uv.def, UserArgs { - args: uv.args, - user_self_ty: None, - })), + ty::UserType::new(ty::UserTypeKind::TypeOf( + uv.def, + UserArgs { args: uv.args, user_self_ty: None }, + )), locations.span(self.typeck.body), ); } @@ -455,7 +477,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { ty::Invariant, user_ty, Locations::All(*span), - ConstraintCategory::TypeAnnotation, + ConstraintCategory::TypeAnnotation(AnnotationSource::Declaration), ) { span_mirbug!( self, @@ -495,14 +517,14 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { // Use new sets of constraints and closure bounds so that we can // modify their locations. - let all_facts = &mut None; + let polonius_facts = &mut None; let mut constraints = Default::default(); let mut liveness_constraints = LivenessValues::without_specific_points(Rc::new(DenseLocationMap::new(promoted_body))); // Don't try to add borrow_region facts for the promoted MIR let mut swap_constraints = |this: &mut Self| { - mem::swap(this.typeck.all_facts, all_facts); + mem::swap(this.typeck.polonius_facts, polonius_facts); mem::swap(&mut this.typeck.constraints.outlives_constraints, &mut constraints); mem::swap(&mut this.typeck.constraints.liveness_constraints, &mut liveness_constraints); }; @@ -560,12 +582,12 @@ struct TypeChecker<'a, 'tcx> { implicit_region_bound: ty::Region<'tcx>, reported_errors: FxIndexSet<(Ty<'tcx>, Span)>, universal_regions: &'a UniversalRegions<'tcx>, - location_table: &'a LocationTable, - all_facts: &'a mut Option, + location_table: &'a PoloniusLocationTable, + polonius_facts: &'a mut Option, borrow_set: &'a BorrowSet<'tcx>, constraints: &'a mut MirTypeckRegionConstraints<'tcx>, - /// When using `-Zpolonius=next`, the helper data used to create polonius constraints. - polonius_context: &'a mut Option, + /// When using `-Zpolonius=next`, the liveness helper data used to create polonius constraints. + polonius_liveness: Option, } /// Holder struct for passing results from MIR typeck to the rest of the non-lexical regions @@ -927,7 +949,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ty::Invariant, &UserTypeProjection { base: annotation_index, projs: vec![] }, location.to_locations(), - ConstraintCategory::Boring, + ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg), ) { let annotation = &self.user_type_annotations[annotation_index]; span_mirbug!( @@ -962,7 +984,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { *variance, projection, Locations::All(stmt.source_info.span), - ConstraintCategory::TypeAnnotation, + ConstraintCategory::TypeAnnotation(AnnotationSource::Ascription), ) { let annotation = &self.user_type_annotations[projection.base]; span_mirbug!( @@ -1094,7 +1116,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ConstraintCategory::Boring, ); - let sig = self.normalize(unnormalized_sig, term_location); + let sig = self.deeply_normalize(unnormalized_sig, term_location); // HACK(#114936): `WF(sig)` does not imply `WF(normalized(sig))` // with built-in `Fn` implementations, since the impl may not be // well-formed itself. @@ -1226,6 +1248,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { Some(l) if !body.local_decls[l].is_user_variable() => { ConstraintCategory::Boring } + // The return type of a call is interesting for diagnostics. _ => ConstraintCategory::Assignment, }; @@ -1619,10 +1642,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } &Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, ty) => { - let trait_ref = - ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Sized, Some(span)), [ - ty, - ]); + let trait_ref = ty::TraitRef::new( + tcx, + tcx.require_lang_item(LangItem::Sized, Some(span)), + [ty], + ); self.prove_trait_ref( trait_ref, @@ -1630,15 +1654,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ConstraintCategory::SizedBound, ); } + &Rvalue::NullaryOp(NullOp::ContractChecks, _) => {} &Rvalue::NullaryOp(NullOp::UbChecks, _) => {} Rvalue::ShallowInitBox(operand, ty) => { self.check_operand(operand, location); - let trait_ref = - ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Sized, Some(span)), [ - *ty, - ]); + let trait_ref = ty::TraitRef::new( + tcx, + tcx.require_lang_item(LangItem::Sized, Some(span)), + [*ty], + ); self.prove_trait_ref( trait_ref, @@ -1653,7 +1679,20 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { match *cast_kind { CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, coercion_source) => { let is_implicit_coercion = coercion_source == CoercionSource::Implicit; - let src_sig = op.ty(body, tcx).fn_sig(tcx); + let src_ty = op.ty(body, tcx); + let mut src_sig = src_ty.fn_sig(tcx); + if let ty::FnDef(def_id, _) = src_ty.kind() + && let ty::FnPtr(_, target_hdr) = *ty.kind() + && tcx.codegen_fn_attrs(def_id).safe_target_features + && target_hdr.safety.is_safe() + && let Some(safe_sig) = tcx.adjust_target_feature_sig( + *def_id, + src_sig, + body.source.def_id(), + ) + { + src_sig = safe_sig; + } // HACK: This shouldn't be necessary... We can remove this when we actually // get binders with where clauses, then elaborate implied bounds into that @@ -2169,7 +2208,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ty_left, common_ty, location.to_locations(), - ConstraintCategory::Boring, + ConstraintCategory::CallArgument(None), ) .unwrap_or_else(|err| { bug!("Could not equate type variable with {:?}: {:?}", ty_left, err) @@ -2178,7 +2217,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ty_right, common_ty, location.to_locations(), - ConstraintCategory::Boring, + ConstraintCategory::CallArgument(None), ) { span_mirbug!( self, @@ -2219,8 +2258,30 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.check_operand(right, location); } + Rvalue::WrapUnsafeBinder(op, ty) => { + self.check_operand(op, location); + let operand_ty = op.ty(self.body, self.tcx()); + + let ty::UnsafeBinder(binder_ty) = *ty.kind() else { + unreachable!(); + }; + let expected_ty = self.infcx.instantiate_binder_with_fresh_vars( + self.body().source_info(location).span, + BoundRegionConversionTime::HigherRankedType, + binder_ty.into(), + ); + self.sub_types( + operand_ty, + expected_ty, + location.to_locations(), + ConstraintCategory::Boring, + ) + .unwrap(); + } + Rvalue::RawPtr(..) | Rvalue::ThreadLocalRef(..) + | Rvalue::Len(..) | Rvalue::Discriminant(..) | Rvalue::NullaryOp(NullOp::OffsetOf(..), _) => {} } @@ -2236,13 +2297,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { | Rvalue::Repeat(..) | Rvalue::Ref(..) | Rvalue::RawPtr(..) + | Rvalue::Len(..) | Rvalue::Cast(..) | Rvalue::ShallowInitBox(..) | Rvalue::BinaryOp(..) | Rvalue::NullaryOp(..) | Rvalue::CopyForDeref(..) | Rvalue::UnaryOp(..) - | Rvalue::Discriminant(..) => None, + | Rvalue::Discriminant(..) + | Rvalue::WrapUnsafeBinder(..) => None, Rvalue::Aggregate(aggregate, _) => match **aggregate { AggregateKind::Adt(_, _, _, user_ty, _) => user_ty, @@ -2327,18 +2390,18 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { borrowed_place: &Place<'tcx>, ) { // These constraints are only meaningful during borrowck: - let Self { borrow_set, location_table, all_facts, constraints, .. } = self; + let Self { borrow_set, location_table, polonius_facts, constraints, .. } = self; // In Polonius mode, we also push a `loan_issued_at` fact // linking the loan to the region (in some cases, though, // there is no loan associated with this borrow expression -- // that occurs when we are borrowing an unsafe place, for // example). - if let Some(all_facts) = all_facts { + if let Some(polonius_facts) = polonius_facts { let _prof_timer = self.infcx.tcx.prof.generic_activity("polonius_fact_generation"); if let Some(borrow_index) = borrow_set.get_index_of(&location) { let region_vid = borrow_region.as_var(); - all_facts.loan_issued_at.push(( + polonius_facts.loan_issued_at.push(( region_vid.into(), borrow_index, location_table.mid_index(location), @@ -2434,7 +2497,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { | ProjectionElem::OpaqueCast(..) | ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } => { + | ProjectionElem::Subslice { .. } + | ProjectionElem::UnwrapUnsafeBinder(_) => { // other field access } ProjectionElem::Subtype(_) => { diff --git a/compiler/rustc_borrowck/src/type_check/opaque_types.rs b/compiler/rustc_borrowck/src/type_check/opaque_types.rs index edf3b1ae092da..17482cc0cbd22 100644 --- a/compiler/rustc_borrowck/src/type_check/opaque_types.rs +++ b/compiler/rustc_borrowck/src/type_check/opaque_types.rs @@ -25,8 +25,8 @@ pub(super) fn take_opaques_and_register_member_constraints<'tcx>( let opaque_types = infcx .take_opaque_types() .into_iter() - .map(|(opaque_type_key, decl)| { - let hidden_type = infcx.resolve_vars_if_possible(decl.hidden_type); + .map(|(opaque_type_key, hidden_type)| { + let hidden_type = infcx.resolve_vars_if_possible(hidden_type); register_member_constraints( typeck, &mut member_constraints, @@ -284,7 +284,7 @@ where return; } - match ty.kind() { + match *ty.kind() { ty::Closure(_, args) => { // Skip lifetime parameters of the enclosing item(s) @@ -316,10 +316,12 @@ where args.as_coroutine().resume_ty().visit_with(self); } - ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { - // Skip lifetime parameters that are not captures. - let variances = self.tcx.variances_of(*def_id); - + ty::Alias(kind, ty::AliasTy { def_id, args, .. }) + if let Some(variances) = self.tcx.opt_alias_variances(kind, def_id) => + { + // Skip lifetime parameters that are not captured, since they do + // not need member constraints registered for them; we'll erase + // them (and hopefully in the future replace them with placeholders). for (v, s) in std::iter::zip(variances, args.iter()) { if *v != ty::Bivariant { s.visit_with(self); diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 3dc4569c57b9a..eb0079a3883f0 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -467,15 +467,13 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { self.infcx.tcx.local_parent(self.mir_def), |r| { debug!(?r); - if !indices.indices.contains_key(&r) { - let region_vid = { - let name = r.get_name_or_anon(); - self.infcx.next_nll_region_var(FR, || RegionCtxt::LateBound(name)) - }; - - debug!(?region_vid); - indices.insert_late_bound_region(r, region_vid.as_var()); - } + let region_vid = { + let name = r.get_name_or_anon(); + self.infcx.next_nll_region_var(FR, || RegionCtxt::LateBound(name)) + }; + + debug!(?region_vid); + indices.insert_late_bound_region(r, region_vid.as_var()); }, ); @@ -484,21 +482,17 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { self.infcx.num_region_vars() }; - // "Liberate" the late-bound regions. These correspond to - // "local" free regions. + // Converse of above, if this is a function/closure then the late-bound regions declared + // on its signature are local. + // + // We manually loop over `bound_inputs_and_output` instead of using + // `for_each_late_bound_region_in_item` as we may need to add the otherwise + // implicit `ClosureEnv` region. let bound_inputs_and_output = self.compute_inputs_and_output(&indices, defining_ty); - - let inputs_and_output = self.infcx.replace_bound_regions_with_nll_infer_vars( - FR, - self.mir_def, - bound_inputs_and_output, - &mut indices, - ); - // Converse of above, if this is a function/closure then the late-bound regions declared on its - // signature are local. - for_each_late_bound_region_in_item(self.infcx.tcx, self.mir_def, |r| { - debug!(?r); - if !indices.indices.contains_key(&r) { + for (idx, bound_var) in bound_inputs_and_output.bound_vars().iter().enumerate() { + if let ty::BoundVariableKind::Region(kind) = bound_var { + let kind = ty::LateParamRegionKind::from_bound(ty::BoundVar::from_usize(idx), kind); + let r = ty::Region::new_late_param(self.infcx.tcx, self.mir_def.to_def_id(), kind); let region_vid = { let name = r.get_name_or_anon(); self.infcx.next_nll_region_var(FR, || RegionCtxt::LateBound(name)) @@ -507,7 +501,12 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { debug!(?region_vid); indices.insert_late_bound_region(r, region_vid.as_var()); } - }); + } + let inputs_and_output = self.infcx.replace_bound_regions_with_nll_infer_vars( + self.mir_def, + bound_inputs_and_output, + &indices, + ); let (unnormalized_output_ty, mut unnormalized_input_tys) = inputs_and_output.split_last().unwrap(); @@ -621,10 +620,10 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { let ty = tcx .typeck(self.mir_def) .node_type(tcx.local_def_id_to_hir_id(self.mir_def)); - let args = InlineConstArgs::new(tcx, InlineConstArgsParts { - parent_args: identity_args, - ty, - }) + let args = InlineConstArgs::new( + tcx, + InlineConstArgsParts { parent_args: identity_args, ty }, + ) .args; let args = self.infcx.replace_free_regions_with_nll_infer_vars(FR, args); DefiningTy::InlineConst(self.mir_def.to_def_id(), args) @@ -832,10 +831,9 @@ impl<'tcx> BorrowckInferCtxt<'tcx> { #[instrument(level = "debug", skip(self, indices))] fn replace_bound_regions_with_nll_infer_vars( &self, - origin: NllRegionVariableOrigin, all_outlive_scope: LocalDefId, value: ty::Binder<'tcx, T>, - indices: &mut UniversalRegionIndices<'tcx>, + indices: &UniversalRegionIndices<'tcx>, ) -> T where T: TypeFoldable>, @@ -845,18 +843,7 @@ impl<'tcx> BorrowckInferCtxt<'tcx> { let kind = ty::LateParamRegionKind::from_bound(br.var, br.kind); let liberated_region = ty::Region::new_late_param(self.tcx, all_outlive_scope.to_def_id(), kind); - let region_vid = { - let name = match br.kind.get_name() { - Some(name) => name, - _ => sym::anon, - }; - - self.next_nll_region_var(origin, || RegionCtxt::Bound(name)) - }; - - indices.insert_late_bound_region(liberated_region, region_vid.as_var()); - debug!(?liberated_region, ?region_vid); - region_vid + ty::Region::new_var(self.tcx, indices.to_region_vid(liberated_region)) }); value } @@ -870,7 +857,7 @@ impl<'tcx> UniversalRegionIndices<'tcx> { /// well. These are used for error reporting. fn insert_late_bound_region(&mut self, r: ty::Region<'tcx>, vid: ty::RegionVid) { debug!("insert_late_bound_region({:?}, {:?})", r, vid); - self.indices.insert(r, vid); + assert_eq!(self.indices.insert(r, vid), None); } /// Converts `r` into a local inference variable: `r` can either diff --git a/compiler/rustc_borrowck/src/util/collect_writes.rs b/compiler/rustc_borrowck/src/util/collect_writes.rs deleted file mode 100644 index 55f1073176aef..0000000000000 --- a/compiler/rustc_borrowck/src/util/collect_writes.rs +++ /dev/null @@ -1,35 +0,0 @@ -use rustc_middle::mir::visit::{PlaceContext, Visitor}; -use rustc_middle::mir::{Body, Local, Location}; - -pub(crate) trait FindAssignments { - // Finds all statements that assign directly to local (i.e., X = ...) - // and returns their locations. - fn find_assignments(&self, local: Local) -> Vec; -} - -impl<'tcx> FindAssignments for Body<'tcx> { - fn find_assignments(&self, local: Local) -> Vec { - let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] }; - visitor.visit_body(self); - visitor.locations - } -} - -// The Visitor walks the MIR to return the assignment statements corresponding -// to a Local. -struct FindLocalAssignmentVisitor { - needle: Local, - locations: Vec, -} - -impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor { - fn visit_local(&mut self, local: Local, place_context: PlaceContext, location: Location) { - if self.needle != local { - return; - } - - if place_context.is_place_assignment() { - self.locations.push(location); - } - } -} diff --git a/compiler/rustc_borrowck/src/util/mod.rs b/compiler/rustc_borrowck/src/util/mod.rs deleted file mode 100644 index 5f2960b768b29..0000000000000 --- a/compiler/rustc_borrowck/src/util/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod collect_writes; - -pub(crate) use collect_writes::FindAssignments; diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs index d2b4e1ca824fd..78bf2195975d5 100644 --- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs +++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs @@ -67,10 +67,11 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span let layout_new = cx.std_path(&[sym::alloc, sym::Layout, sym::from_size_align_unchecked]); let layout_new = cx.expr_path(cx.path(span, layout_new)); - let layout = cx.expr_call(span, layout_new, thin_vec![ - cx.expr_ident(span, size), - cx.expr_ident(span, align) - ]); + let layout = cx.expr_call( + span, + layout_new, + thin_vec![cx.expr_ident(span, size), cx.expr_ident(span, align)], + ); let call = cx.expr_call_ident(sig_span, handler, thin_vec![layout]); @@ -85,6 +86,7 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span defaultness: ast::Defaultness::Final, sig, generics: Generics::default(), + contract: None, body, })); diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 5062cf55bb9ad..eb5b345e49ecd 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -651,7 +651,7 @@ fn expand_preparsed_asm( .map(|span| template_span.from_inner(InnerSpan::new(span.start, span.end))); for piece in unverified_pieces { match piece { - parse::Piece::String(s) => { + parse::Piece::Lit(s) => { template.push(ast::InlineAsmTemplatePiece::String(s.to_string().into())) } parse::Piece::NextArgument(arg) => { diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 8960cf6ab598f..24c54edd705a1 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -406,19 +406,21 @@ mod llvm_enzyme { let unsf_expr = ecx.expr_block(P(unsf_block)); let blackbox_call_expr = ecx.expr_path(ecx.path(span, blackbox_path)); let primal_call = gen_primal_call(ecx, span, primal, idents); - let black_box_primal_call = - ecx.expr_call(new_decl_span, blackbox_call_expr.clone(), thin_vec![ - primal_call.clone() - ]); + let black_box_primal_call = ecx.expr_call( + new_decl_span, + blackbox_call_expr.clone(), + thin_vec![primal_call.clone()], + ); let tup_args = new_names .iter() .map(|arg| ecx.expr_path(ecx.path_ident(span, Ident::from_str(arg)))) .collect(); - let black_box_remaining_args = - ecx.expr_call(sig_span, blackbox_call_expr.clone(), thin_vec![ - ecx.expr_tuple(sig_span, tup_args) - ]); + let black_box_remaining_args = ecx.expr_call( + sig_span, + blackbox_call_expr.clone(), + thin_vec![ecx.expr_tuple(sig_span, tup_args)], + ); let mut body = ecx.block(span, ThinVec::new()); body.stmts.push(ecx.stmt_semi(unsf_expr)); @@ -532,8 +534,11 @@ mod llvm_enzyme { return body; } [arg] => { - ret = ecx - .expr_call(new_decl_span, blackbox_call_expr.clone(), thin_vec![arg.clone()]); + ret = ecx.expr_call( + new_decl_span, + blackbox_call_expr.clone(), + thin_vec![arg.clone()], + ); } args => { let ret_tuple: P = ecx.expr_tuple(span, args.into()); diff --git a/compiler/rustc_builtin_macros/src/contracts.rs b/compiler/rustc_builtin_macros/src/contracts.rs new file mode 100644 index 0000000000000..6a24af361fe78 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/contracts.rs @@ -0,0 +1,171 @@ +use rustc_ast::token; +use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; +use rustc_errors::ErrorGuaranteed; +use rustc_expand::base::{AttrProcMacro, ExtCtxt}; +use rustc_span::Span; +use rustc_span::symbol::{Ident, Symbol, kw}; + +pub(crate) struct ExpandRequires; + +pub(crate) struct ExpandEnsures; + +impl AttrProcMacro for ExpandRequires { + fn expand<'cx>( + &self, + ecx: &'cx mut ExtCtxt<'_>, + span: Span, + annotation: TokenStream, + annotated: TokenStream, + ) -> Result { + expand_requires_tts(ecx, span, annotation, annotated) + } +} + +impl AttrProcMacro for ExpandEnsures { + fn expand<'cx>( + &self, + ecx: &'cx mut ExtCtxt<'_>, + span: Span, + annotation: TokenStream, + annotated: TokenStream, + ) -> Result { + expand_ensures_tts(ecx, span, annotation, annotated) + } +} + +/// Expand the function signature to include the contract clause. +/// +/// The contracts clause will be injected before the function body and the optional where clause. +/// For that, we search for the body / where token, and invoke the `inject` callback to generate the +/// contract clause in the right place. +/// +// FIXME: this kind of manual token tree munging does not have significant precedent among +// rustc builtin macros, probably because most builtin macros use direct AST manipulation to +// accomplish similar goals. But since our attributes need to take arbitrary expressions, and +// our attribute infrastructure does not yet support mixing a token-tree annotation with an AST +// annotated, we end up doing token tree manipulation. +fn expand_contract_clause( + ecx: &mut ExtCtxt<'_>, + attr_span: Span, + annotated: TokenStream, + inject: impl FnOnce(&mut TokenStream) -> Result<(), ErrorGuaranteed>, +) -> Result { + let mut new_tts = TokenStream::default(); + let mut cursor = annotated.iter(); + + let is_kw = |tt: &TokenTree, sym: Symbol| { + if let TokenTree::Token(token, _) = tt { token.is_ident_named(sym) } else { false } + }; + + // Find the `fn` keyword to check if this is a function. + if cursor + .find(|tt| { + new_tts.push_tree((*tt).clone()); + is_kw(tt, kw::Fn) + }) + .is_none() + { + return Err(ecx + .sess + .dcx() + .span_err(attr_span, "contract annotations can only be used on functions")); + } + + // Found the `fn` keyword, now find either the `where` token or the function body. + let next_tt = loop { + let Some(tt) = cursor.next() else { + return Err(ecx.sess.dcx().span_err( + attr_span, + "contract annotations is only supported in functions with bodies", + )); + }; + // If `tt` is the last element. Check if it is the function body. + if cursor.peek().is_none() { + if let TokenTree::Delimited(_, _, token::Delimiter::Brace, _) = tt { + break tt; + } else { + return Err(ecx.sess.dcx().span_err( + attr_span, + "contract annotations is only supported in functions with bodies", + )); + } + } + + if is_kw(tt, kw::Where) { + break tt; + } + new_tts.push_tree(tt.clone()); + }; + + // At this point, we've transcribed everything from the `fn` through the formal parameter list + // and return type declaration, (if any), but `tt` itself has *not* been transcribed. + // + // Now inject the AST contract form. + // + inject(&mut new_tts)?; + + // Above we injected the internal AST requires/ensures construct. Now copy over all the other + // token trees. + new_tts.push_tree(next_tt.clone()); + while let Some(tt) = cursor.next() { + new_tts.push_tree(tt.clone()); + if cursor.peek().is_none() + && !matches!(tt, TokenTree::Delimited(_, _, token::Delimiter::Brace, _)) + { + return Err(ecx.sess.dcx().span_err( + attr_span, + "contract annotations is only supported in functions with bodies", + )); + } + } + + Ok(new_tts) +} + +fn expand_requires_tts( + ecx: &mut ExtCtxt<'_>, + attr_span: Span, + annotation: TokenStream, + annotated: TokenStream, +) -> Result { + let feature_span = ecx.with_def_site_ctxt(attr_span); + expand_contract_clause(ecx, attr_span, annotated, |new_tts| { + new_tts.push_tree(TokenTree::Token( + token::Token::from_ast_ident(Ident::new(kw::ContractRequires, feature_span)), + Spacing::Joint, + )); + new_tts.push_tree(TokenTree::Token( + token::Token::new(token::TokenKind::OrOr, attr_span), + Spacing::Alone, + )); + new_tts.push_tree(TokenTree::Delimited( + DelimSpan::from_single(attr_span), + DelimSpacing::new(Spacing::JointHidden, Spacing::JointHidden), + token::Delimiter::Parenthesis, + annotation, + )); + Ok(()) + }) +} + +fn expand_ensures_tts( + ecx: &mut ExtCtxt<'_>, + attr_span: Span, + annotation: TokenStream, + annotated: TokenStream, +) -> Result { + let feature_span = ecx.with_def_site_ctxt(attr_span); + expand_contract_clause(ecx, attr_span, annotated, |new_tts| { + new_tts.push_tree(TokenTree::Token( + token::Token::from_ast_ident(Ident::new(kw::ContractEnsures, feature_span)), + Spacing::Joint, + )); + new_tts.push_tree(TokenTree::Delimited( + DelimSpan::from_single(attr_span), + DelimSpacing::new(Spacing::JointHidden, Spacing::JointHidden), + token::Delimiter::Parenthesis, + annotation, + )); + Ok(()) + }) +} diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index 78f50ba8d29ed..c3656e8244fe0 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -114,10 +114,13 @@ fn cs_clone_simple( // type parameters. } else { // let _: AssertParamIsClone; - super::assert_ty_bounds(cx, &mut stmts, field.ty.clone(), field.span, &[ - sym::clone, - sym::AssertParamIsClone, - ]); + super::assert_ty_bounds( + cx, + &mut stmts, + field.ty.clone(), + field.span, + &[sym::clone, sym::AssertParamIsClone], + ); } } }; @@ -126,10 +129,13 @@ fn cs_clone_simple( // Just a single assertion for unions, that the union impls `Copy`. // let _: AssertParamIsCopy; let self_ty = cx.ty_path(cx.path_ident(trait_span, Ident::with_dummy_span(kw::SelfUpper))); - super::assert_ty_bounds(cx, &mut stmts, self_ty, trait_span, &[ - sym::clone, - sym::AssertParamIsCopy, - ]); + super::assert_ty_bounds( + cx, + &mut stmts, + self_ty, + trait_span, + &[sym::clone, sym::AssertParamIsCopy], + ); } else { match *substr.fields { StaticStruct(vdata, ..) => { diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs index 5790350203a5a..eca79e4dc4897 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs @@ -65,10 +65,13 @@ fn cs_total_eq_assert( // Already produced an assertion for this type. } else { // let _: AssertParamIsEq; - super::assert_ty_bounds(cx, &mut stmts, field.ty.clone(), field.span, &[ - sym::cmp, - sym::AssertParamIsEq, - ]); + super::assert_ty_bounds( + cx, + &mut stmts, + field.ty.clone(), + field.span, + &[sym::cmp, sym::AssertParamIsEq], + ); } } }; diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 49706db0e0b3f..5aed9f76f144f 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -3,11 +3,11 @@ use ast::ptr::P; use rustc_ast::mut_visit::MutVisitor; use rustc_ast::visit::BoundKind; use rustc_ast::{ - self as ast, GenericArg, GenericBound, GenericParamKind, ItemKind, MetaItem, + self as ast, GenericArg, GenericBound, GenericParamKind, Generics, ItemKind, MetaItem, TraitBoundModifiers, VariantData, WherePredicate, }; -use rustc_attr_parsing as attr; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; +use rustc_errors::E0802; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_macros::Diagnostic; use rustc_span::{Ident, Span, Symbol, sym}; @@ -32,15 +32,6 @@ pub(crate) fn expand_deriving_coerce_pointee( let (name_ident, generics) = if let Annotatable::Item(aitem) = item && let ItemKind::Struct(struct_data, g) = &aitem.kind { - let is_transparent = aitem.attrs.iter().any(|attr| { - attr::find_repr_attrs(cx.sess, attr) - .into_iter() - .any(|r| matches!(r, attr::ReprTransparent)) - }); - if !is_transparent { - cx.dcx().emit_err(RequireTransparent { span }); - return; - } if !matches!( struct_data, VariantData::Struct { fields, recovered: _ } | VariantData::Tuple(fields, _) @@ -88,8 +79,7 @@ pub(crate) fn expand_deriving_coerce_pointee( } else { let mut pointees = type_params .iter() - .filter_map(|&(idx, span, is_pointee)| is_pointee.then_some((idx, span))) - .fuse(); + .filter_map(|&(idx, span, is_pointee)| is_pointee.then_some((idx, span))); match (pointees.next(), pointees.next()) { (Some((idx, _span)), None) => idx, (None, _) => { @@ -110,6 +100,52 @@ pub(crate) fn expand_deriving_coerce_pointee( // Declare helper function that adds implementation blocks. // FIXME(dingxiangfei2009): Investigate the set of attributes on target struct to be propagated to impls let attrs = thin_vec![cx.attr_word(sym::automatically_derived, span),]; + // # Validity assertion which will be checked later in `rustc_hir_analysis::coherence::builtins`. + { + let trait_path = + cx.path_all(span, true, path!(span, core::marker::CoercePointeeValidated), vec![]); + let trait_ref = cx.trait_ref(trait_path); + push(Annotatable::Item( + cx.item( + span, + Ident::empty(), + attrs.clone(), + ast::ItemKind::Impl(Box::new(ast::Impl { + safety: ast::Safety::Default, + polarity: ast::ImplPolarity::Positive, + defaultness: ast::Defaultness::Final, + constness: ast::Const::No, + generics: Generics { + params: generics + .params + .iter() + .map(|p| match &p.kind { + GenericParamKind::Lifetime => { + cx.lifetime_param(p.span(), p.ident, p.bounds.clone()) + } + GenericParamKind::Type { default: _ } => { + cx.typaram(p.span(), p.ident, p.bounds.clone(), None) + } + GenericParamKind::Const { ty, kw_span: _, default: _ } => cx + .const_param( + p.span(), + p.ident, + p.bounds.clone(), + ty.clone(), + None, + ), + }) + .collect(), + where_clause: generics.where_clause.clone(), + span: generics.span, + }, + of_trait: Some(trait_ref), + self_ty: self_type.clone(), + items: ThinVec::new(), + })), + ), + )); + } let mut add_impl_block = |generics, trait_symbol, trait_args| { let mut parts = path!(span, core::ops); parts.push(Ident::new(trait_symbol, span)); @@ -157,7 +193,7 @@ pub(crate) fn expand_deriving_coerce_pointee( { cx.dcx().emit_err(RequiresMaybeSized { span: pointee_ty_ident.span, - name: pointee_ty_ident.name.to_ident_string(), + name: pointee_ty_ident, }); return; } @@ -430,35 +466,35 @@ impl<'a, 'b> rustc_ast::visit::Visitor<'a> for AlwaysErrorOnGenericParam<'a, 'b> } #[derive(Diagnostic)] -#[diag(builtin_macros_coerce_pointee_requires_transparent)] +#[diag(builtin_macros_coerce_pointee_requires_transparent, code = E0802)] struct RequireTransparent { #[primary_span] span: Span, } #[derive(Diagnostic)] -#[diag(builtin_macros_coerce_pointee_requires_one_field)] +#[diag(builtin_macros_coerce_pointee_requires_one_field, code = E0802)] struct RequireOneField { #[primary_span] span: Span, } #[derive(Diagnostic)] -#[diag(builtin_macros_coerce_pointee_requires_one_generic)] +#[diag(builtin_macros_coerce_pointee_requires_one_generic, code = E0802)] struct RequireOneGeneric { #[primary_span] span: Span, } #[derive(Diagnostic)] -#[diag(builtin_macros_coerce_pointee_requires_one_pointee)] +#[diag(builtin_macros_coerce_pointee_requires_one_pointee, code = E0802)] struct RequireOnePointee { #[primary_span] span: Span, } #[derive(Diagnostic)] -#[diag(builtin_macros_coerce_pointee_too_many_pointees)] +#[diag(builtin_macros_coerce_pointee_too_many_pointees, code = E0802)] struct TooManyPointees { #[primary_span] one: Span, @@ -467,9 +503,9 @@ struct TooManyPointees { } #[derive(Diagnostic)] -#[diag(builtin_macros_coerce_pointee_requires_maybe_sized)] +#[diag(builtin_macros_coerce_pointee_requires_maybe_sized, code = E0802)] struct RequiresMaybeSized { #[primary_span] span: Span, - name: String, + name: Ident, } diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index eb01ca3941d83..8ab21986e68a0 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -56,7 +56,7 @@ fn show_substructure(cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> let (ident, vdata, fields) = match substr.fields { Struct(vdata, fields) => (substr.type_ident, *vdata, fields), - EnumMatching(_, v, fields) => (v.ident, &v.data, fields), + EnumMatching(v, fields) => (v.ident, &v.data, fields), AllFieldlessEnum(enum_def) => return show_fieldless_enum(cx, span, enum_def, substr), EnumDiscr(..) | StaticStruct(..) | StaticEnum(..) => { cx.dcx().span_bug(span, "nonsensical .fields in `#[derive(Debug)]`") diff --git a/compiler/rustc_builtin_macros/src/deriving/decodable.rs b/compiler/rustc_builtin_macros/src/deriving/decodable.rs deleted file mode 100644 index 6348560496e95..0000000000000 --- a/compiler/rustc_builtin_macros/src/deriving/decodable.rs +++ /dev/null @@ -1,215 +0,0 @@ -//! The compiler code necessary for `#[derive(RustcDecodable)]`. See encodable.rs for more. - -use rustc_ast::ptr::P; -use rustc_ast::{self as ast, Expr, MetaItem, Mutability}; -use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::{Ident, Span, Symbol, sym}; -use thin_vec::{ThinVec, thin_vec}; - -use crate::deriving::generic::ty::*; -use crate::deriving::generic::*; -use crate::deriving::pathvec_std; - -pub(crate) fn expand_deriving_rustc_decodable( - cx: &ExtCtxt<'_>, - span: Span, - mitem: &MetaItem, - item: &Annotatable, - push: &mut dyn FnMut(Annotatable), - is_const: bool, -) { - let krate = sym::rustc_serialize; - let typaram = sym::__D; - - let trait_def = TraitDef { - span, - path: Path::new_(vec![krate, sym::Decodable], vec![], PathKind::Global), - skip_path_as_bound: false, - needs_copy_as_bound_if_packed: true, - additional_bounds: Vec::new(), - supports_unions: false, - methods: vec![MethodDef { - name: sym::decode, - generics: Bounds { - bounds: vec![(typaram, vec![Path::new_( - vec![krate, sym::Decoder], - vec![], - PathKind::Global, - )])], - }, - explicit_self: false, - nonself_args: vec![( - Ref(Box::new(Path(Path::new_local(typaram))), Mutability::Mut), - sym::d, - )], - ret_ty: Path(Path::new_( - pathvec_std!(result::Result), - vec![ - Box::new(Self_), - Box::new(Path(Path::new_(vec![typaram, sym::Error], vec![], PathKind::Local))), - ], - PathKind::Std, - )), - attributes: ast::AttrVec::new(), - fieldless_variants_strategy: FieldlessVariantsStrategy::Default, - combine_substructure: combine_substructure(Box::new(|a, b, c| { - decodable_substructure(a, b, c, krate) - })), - }], - associated_types: Vec::new(), - is_const, - }; - - trait_def.expand(cx, mitem, item, push) -} - -fn decodable_substructure( - cx: &ExtCtxt<'_>, - trait_span: Span, - substr: &Substructure<'_>, - krate: Symbol, -) -> BlockOrExpr { - let decoder = substr.nonselflike_args[0].clone(); - let recurse = vec![ - Ident::new(krate, trait_span), - Ident::new(sym::Decodable, trait_span), - Ident::new(sym::decode, trait_span), - ]; - let exprdecode = cx.expr_path(cx.path_global(trait_span, recurse)); - // throw an underscore in front to suppress unused variable warnings - let blkarg = Ident::new(sym::_d, trait_span); - let blkdecoder = cx.expr_ident(trait_span, blkarg); - - let expr = match substr.fields { - StaticStruct(_, summary) => { - let nfields = match summary { - Unnamed(fields, _) => fields.len(), - Named(fields) => fields.len(), - }; - let fn_read_struct_field_path: Vec<_> = - cx.def_site_path(&[sym::rustc_serialize, sym::Decoder, sym::read_struct_field]); - - let path = cx.path_ident(trait_span, substr.type_ident); - let result = - decode_static_fields(cx, trait_span, path, summary, |cx, span, name, field| { - cx.expr_try( - span, - cx.expr_call_global(span, fn_read_struct_field_path.clone(), thin_vec![ - blkdecoder.clone(), - cx.expr_str(span, name), - cx.expr_usize(span, field), - exprdecode.clone(), - ]), - ) - }); - let result = cx.expr_ok(trait_span, result); - let fn_read_struct_path: Vec<_> = - cx.def_site_path(&[sym::rustc_serialize, sym::Decoder, sym::read_struct]); - - cx.expr_call_global(trait_span, fn_read_struct_path, thin_vec![ - decoder, - cx.expr_str(trait_span, substr.type_ident.name), - cx.expr_usize(trait_span, nfields), - cx.lambda1(trait_span, result, blkarg), - ]) - } - StaticEnum(_, fields) => { - let variant = Ident::new(sym::i, trait_span); - - let mut arms = ThinVec::with_capacity(fields.len() + 1); - let mut variants = ThinVec::with_capacity(fields.len()); - - let fn_read_enum_variant_arg_path: Vec<_> = - cx.def_site_path(&[sym::rustc_serialize, sym::Decoder, sym::read_enum_variant_arg]); - - for (i, &(ident, v_span, ref parts)) in fields.iter().enumerate() { - variants.push(cx.expr_str(v_span, ident.name)); - - let path = cx.path(trait_span, vec![substr.type_ident, ident]); - let decoded = - decode_static_fields(cx, v_span, path, parts, |cx, span, _, field| { - let idx = cx.expr_usize(span, field); - cx.expr_try( - span, - cx.expr_call_global( - span, - fn_read_enum_variant_arg_path.clone(), - thin_vec![blkdecoder.clone(), idx, exprdecode.clone()], - ), - ) - }); - - arms.push(cx.arm(v_span, cx.pat_lit(v_span, cx.expr_usize(v_span, i)), decoded)); - } - - arms.push(cx.arm_unreachable(trait_span)); - - let result = cx.expr_ok( - trait_span, - cx.expr_match(trait_span, cx.expr_ident(trait_span, variant), arms), - ); - let lambda = cx.lambda(trait_span, vec![blkarg, variant], result); - let variant_array_ref = cx.expr_array_ref(trait_span, variants); - let fn_read_enum_variant_path: Vec<_> = - cx.def_site_path(&[sym::rustc_serialize, sym::Decoder, sym::read_enum_variant]); - let result = cx.expr_call_global(trait_span, fn_read_enum_variant_path, thin_vec![ - blkdecoder, - variant_array_ref, - lambda - ]); - let fn_read_enum_path: Vec<_> = - cx.def_site_path(&[sym::rustc_serialize, sym::Decoder, sym::read_enum]); - - cx.expr_call_global(trait_span, fn_read_enum_path, thin_vec![ - decoder, - cx.expr_str(trait_span, substr.type_ident.name), - cx.lambda1(trait_span, result, blkarg), - ]) - } - _ => cx.dcx().bug("expected StaticEnum or StaticStruct in derive(Decodable)"), - }; - BlockOrExpr::new_expr(expr) -} - -/// Creates a decoder for a single enum variant/struct: -/// - `outer_pat_path` is the path to this enum variant/struct -/// - `getarg` should retrieve the `usize`-th field with name `@str`. -fn decode_static_fields( - cx: &ExtCtxt<'_>, - trait_span: Span, - outer_pat_path: ast::Path, - fields: &StaticFields, - mut getarg: F, -) -> P -where - F: FnMut(&ExtCtxt<'_>, Span, Symbol, usize) -> P, -{ - match fields { - Unnamed(fields, is_tuple) => { - let path_expr = cx.expr_path(outer_pat_path); - if matches!(is_tuple, IsTuple::No) { - path_expr - } else { - let fields = fields - .iter() - .enumerate() - .map(|(i, &span)| getarg(cx, span, Symbol::intern(&format!("_field{i}")), i)) - .collect(); - - cx.expr_call(trait_span, path_expr, fields) - } - } - Named(fields) => { - // use the field's span to get nicer error messages. - let fields = fields - .iter() - .enumerate() - .map(|(i, &(ident, span, _))| { - let arg = getarg(cx, span, ident.name, i); - cx.field_imm(span, ident, arg) - }) - .collect(); - cx.expr_struct(trait_span, outer_pat_path, fields) - } - } -} diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index 3c7bebd0f192e..1fe567e23f455 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -42,7 +42,7 @@ pub(crate) fn expand_deriving_default( StaticStruct(_, fields) => { default_struct_substructure(cx, trait_span, substr, fields) } - StaticEnum(enum_def, _) => { + StaticEnum(enum_def) => { default_enum_substructure(cx, trait_span, enum_def, item.span()) } _ => cx.dcx().span_bug(trait_span, "method in `derive(Default)`"), @@ -108,30 +108,38 @@ fn default_enum_substructure( Ok(default_variant) => { // We now know there is exactly one unit variant with exactly one `#[default]` attribute. match &default_variant.data { - VariantData::Unit(_) => cx.expr_path(cx.path(default_variant.span, vec![ - Ident::new(kw::SelfUpper, default_variant.span), - default_variant.ident, - ])), + VariantData::Unit(_) => cx.expr_path(cx.path( + default_variant.span, + vec![Ident::new(kw::SelfUpper, default_variant.span), default_variant.ident], + )), VariantData::Struct { fields, .. } => { // This only happens if `#![feature(default_field_values)]`. We have validated // all fields have default values in the definition. let default_fields = fields .iter() .map(|field| { - cx.field_imm(field.span, field.ident.unwrap(), match &field.default { - // We use `Default::default()`. - None => default_call(cx, field.span), - // We use the field default const expression. - Some(val) => { - cx.expr(val.value.span, ast::ExprKind::ConstBlock(val.clone())) - } - }) + cx.field_imm( + field.span, + field.ident.unwrap(), + match &field.default { + // We use `Default::default()`. + None => default_call(cx, field.span), + // We use the field default const expression. + Some(val) => cx.expr( + val.value.span, + ast::ExprKind::ConstBlock(val.clone()), + ), + }, + ) }) .collect(); - let path = cx.path(default_variant.span, vec![ - Ident::new(kw::SelfUpper, default_variant.span), - default_variant.ident, - ]); + let path = cx.path( + default_variant.span, + vec![ + Ident::new(kw::SelfUpper, default_variant.span), + default_variant.ident, + ], + ); cx.expr_struct(default_variant.span, path, default_fields) } // Logic error in `extract_default_variant`. diff --git a/compiler/rustc_builtin_macros/src/deriving/encodable.rs b/compiler/rustc_builtin_macros/src/deriving/encodable.rs deleted file mode 100644 index 20aacb2caca20..0000000000000 --- a/compiler/rustc_builtin_macros/src/deriving/encodable.rs +++ /dev/null @@ -1,284 +0,0 @@ -//! The compiler code necessary to implement the `#[derive(RustcEncodable)]` -//! (and `RustcDecodable`, in `decodable.rs`) extension. The idea here is that -//! type-defining items may be tagged with -//! `#[derive(RustcEncodable, RustcDecodable)]`. -//! -//! For example, a type like: -//! -//! ```ignore (old code) -//! #[derive(RustcEncodable, RustcDecodable)] -//! struct Node { id: usize } -//! ``` -//! -//! would generate two implementations like: -//! -//! ```ignore (old code) -//! # struct Node { id: usize } -//! impl, E> Encodable for Node { -//! fn encode(&self, s: &mut S) -> Result<(), E> { -//! s.emit_struct("Node", 1, |this| { -//! this.emit_struct_field("id", 0, |this| { -//! Encodable::encode(&self.id, this) -//! /* this.emit_usize(self.id) can also be used */ -//! }) -//! }) -//! } -//! } -//! -//! impl, E> Decodable for Node { -//! fn decode(d: &mut D) -> Result { -//! d.read_struct("Node", 1, |this| { -//! match this.read_struct_field("id", 0, |this| Decodable::decode(this)) { -//! Ok(id) => Ok(Node { id: id }), -//! Err(e) => Err(e), -//! } -//! }) -//! } -//! } -//! ``` -//! -//! Other interesting scenarios are when the item has type parameters or -//! references other non-built-in types. A type definition like: -//! -//! ```ignore (old code) -//! # #[derive(RustcEncodable, RustcDecodable)] -//! # struct Span; -//! #[derive(RustcEncodable, RustcDecodable)] -//! struct Spanned { node: T, span: Span } -//! ``` -//! -//! would yield functions like: -//! -//! ```ignore (old code) -//! # #[derive(RustcEncodable, RustcDecodable)] -//! # struct Span; -//! # struct Spanned { node: T, span: Span } -//! impl< -//! S: Encoder, -//! E, -//! T: Encodable -//! > Encodable for Spanned { -//! fn encode(&self, s: &mut S) -> Result<(), E> { -//! s.emit_struct("Spanned", 2, |this| { -//! this.emit_struct_field("node", 0, |this| self.node.encode(this)) -//! .unwrap(); -//! this.emit_struct_field("span", 1, |this| self.span.encode(this)) -//! }) -//! } -//! } -//! -//! impl< -//! D: Decoder, -//! E, -//! T: Decodable -//! > Decodable for Spanned { -//! fn decode(d: &mut D) -> Result, E> { -//! d.read_struct("Spanned", 2, |this| { -//! Ok(Spanned { -//! node: this.read_struct_field("node", 0, |this| Decodable::decode(this)) -//! .unwrap(), -//! span: this.read_struct_field("span", 1, |this| Decodable::decode(this)) -//! .unwrap(), -//! }) -//! }) -//! } -//! } -//! ``` - -use rustc_ast::{AttrVec, ExprKind, MetaItem, Mutability}; -use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::{Ident, Span, Symbol, sym}; -use thin_vec::{ThinVec, thin_vec}; - -use crate::deriving::generic::ty::*; -use crate::deriving::generic::*; -use crate::deriving::pathvec_std; - -pub(crate) fn expand_deriving_rustc_encodable( - cx: &ExtCtxt<'_>, - span: Span, - mitem: &MetaItem, - item: &Annotatable, - push: &mut dyn FnMut(Annotatable), - is_const: bool, -) { - let krate = sym::rustc_serialize; - let typaram = sym::__S; - - let trait_def = TraitDef { - span, - path: Path::new_(vec![krate, sym::Encodable], vec![], PathKind::Global), - skip_path_as_bound: false, - needs_copy_as_bound_if_packed: true, - additional_bounds: Vec::new(), - supports_unions: false, - methods: vec![MethodDef { - name: sym::encode, - generics: Bounds { - bounds: vec![(typaram, vec![Path::new_( - vec![krate, sym::Encoder], - vec![], - PathKind::Global, - )])], - }, - explicit_self: true, - nonself_args: vec![( - Ref(Box::new(Path(Path::new_local(typaram))), Mutability::Mut), - sym::s, - )], - ret_ty: Path(Path::new_( - pathvec_std!(result::Result), - vec![ - Box::new(Unit), - Box::new(Path(Path::new_(vec![typaram, sym::Error], vec![], PathKind::Local))), - ], - PathKind::Std, - )), - attributes: AttrVec::new(), - fieldless_variants_strategy: FieldlessVariantsStrategy::Default, - combine_substructure: combine_substructure(Box::new(|a, b, c| { - encodable_substructure(a, b, c, krate) - })), - }], - associated_types: Vec::new(), - is_const, - }; - - trait_def.expand(cx, mitem, item, push) -} - -fn encodable_substructure( - cx: &ExtCtxt<'_>, - trait_span: Span, - substr: &Substructure<'_>, - krate: Symbol, -) -> BlockOrExpr { - let encoder = substr.nonselflike_args[0].clone(); - // throw an underscore in front to suppress unused variable warnings - let blkarg = Ident::new(sym::_e, trait_span); - let blkencoder = cx.expr_ident(trait_span, blkarg); - let fn_path = cx.expr_path(cx.path_global(trait_span, vec![ - Ident::new(krate, trait_span), - Ident::new(sym::Encodable, trait_span), - Ident::new(sym::encode, trait_span), - ])); - - match substr.fields { - Struct(_, fields) => { - let fn_emit_struct_field_path = - cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_struct_field]); - let mut stmts = ThinVec::new(); - for (i, &FieldInfo { name, ref self_expr, span, .. }) in fields.iter().enumerate() { - let name = match name { - Some(id) => id.name, - None => Symbol::intern(&format!("_field{i}")), - }; - let self_ref = cx.expr_addr_of(span, self_expr.clone()); - let enc = - cx.expr_call(span, fn_path.clone(), thin_vec![self_ref, blkencoder.clone()]); - let lambda = cx.lambda1(span, enc, blkarg); - let call = cx.expr_call_global(span, fn_emit_struct_field_path.clone(), thin_vec![ - blkencoder.clone(), - cx.expr_str(span, name), - cx.expr_usize(span, i), - lambda, - ]); - - // last call doesn't need a try! - let last = fields.len() - 1; - let call = if i != last { - cx.expr_try(span, call) - } else { - cx.expr(span, ExprKind::Ret(Some(call))) - }; - - let stmt = cx.stmt_expr(call); - stmts.push(stmt); - } - - // unit structs have no fields and need to return Ok() - let blk = if stmts.is_empty() { - let ok = cx.expr_ok(trait_span, cx.expr_tuple(trait_span, ThinVec::new())); - cx.lambda1(trait_span, ok, blkarg) - } else { - cx.lambda_stmts_1(trait_span, stmts, blkarg) - }; - - let fn_emit_struct_path = - cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_struct]); - - let expr = cx.expr_call_global(trait_span, fn_emit_struct_path, thin_vec![ - encoder, - cx.expr_str(trait_span, substr.type_ident.name), - cx.expr_usize(trait_span, fields.len()), - blk, - ]); - BlockOrExpr::new_expr(expr) - } - - EnumMatching(idx, variant, fields) => { - // We're not generating an AST that the borrow checker is expecting, - // so we need to generate a unique local variable to take the - // mutable loan out on, otherwise we get conflicts which don't - // actually exist. - let me = cx.stmt_let(trait_span, false, blkarg, encoder); - let encoder = cx.expr_ident(trait_span, blkarg); - - let fn_emit_enum_variant_arg_path: Vec<_> = - cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_enum_variant_arg]); - - let mut stmts = ThinVec::new(); - if !fields.is_empty() { - let last = fields.len() - 1; - for (i, &FieldInfo { ref self_expr, span, .. }) in fields.iter().enumerate() { - let self_ref = cx.expr_addr_of(span, self_expr.clone()); - let enc = cx - .expr_call(span, fn_path.clone(), thin_vec![self_ref, blkencoder.clone()]); - let lambda = cx.lambda1(span, enc, blkarg); - - let call = cx.expr_call_global( - span, - fn_emit_enum_variant_arg_path.clone(), - thin_vec![blkencoder.clone(), cx.expr_usize(span, i), lambda], - ); - let call = if i != last { - cx.expr_try(span, call) - } else { - cx.expr(span, ExprKind::Ret(Some(call))) - }; - stmts.push(cx.stmt_expr(call)); - } - } else { - let ok = cx.expr_ok(trait_span, cx.expr_tuple(trait_span, ThinVec::new())); - let ret_ok = cx.expr(trait_span, ExprKind::Ret(Some(ok))); - stmts.push(cx.stmt_expr(ret_ok)); - } - - let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg); - let name = cx.expr_str(trait_span, variant.ident.name); - - let fn_emit_enum_variant_path: Vec<_> = - cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_enum_variant]); - - let call = cx.expr_call_global(trait_span, fn_emit_enum_variant_path, thin_vec![ - blkencoder, - name, - cx.expr_usize(trait_span, *idx), - cx.expr_usize(trait_span, fields.len()), - blk, - ]); - - let blk = cx.lambda1(trait_span, call, blkarg); - let fn_emit_enum_path: Vec<_> = - cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_enum]); - let expr = cx.expr_call_global(trait_span, fn_emit_enum_path, thin_vec![ - encoder, - cx.expr_str(trait_span, substr.type_ident.name), - blk - ]); - BlockOrExpr::new_mixed(thin_vec![me], Some(expr)) - } - - _ => cx.dcx().bug("expected Struct or EnumMatching in derive(Encodable)"), - } -} diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index f0a5e44e066ee..234ec8582165b 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -311,7 +311,7 @@ pub(crate) enum SubstructureFields<'a> { /// Matching variants of the enum: variant index, ast::Variant, /// fields: the field name is only non-`None` in the case of a struct /// variant. - EnumMatching(usize, &'a ast::Variant, Vec), + EnumMatching(&'a ast::Variant, Vec), /// The discriminant of an enum. The first field is a `FieldInfo` for the discriminants, as /// if they were fields. The second field is the expression to combine the @@ -322,7 +322,7 @@ pub(crate) enum SubstructureFields<'a> { StaticStruct(&'a ast::VariantData, StaticFields), /// A static method where `Self` is an enum. - StaticEnum(&'a ast::EnumDef, Vec<(Ident, Span, StaticFields)>), + StaticEnum(&'a ast::EnumDef), } /// Combine the values of all the fields together. The last argument is @@ -1034,6 +1034,7 @@ impl<'a> MethodDef<'a> { defaultness, sig, generics: fn_generics, + contract: None, body: Some(body_block), })), tokens: None, @@ -1219,10 +1220,12 @@ impl<'a> MethodDef<'a> { let discr_let_stmts: ThinVec<_> = iter::zip(&discr_idents, &selflike_args) .map(|(&ident, selflike_arg)| { - let variant_value = - deriving::call_intrinsic(cx, span, sym::discriminant_value, thin_vec![ - selflike_arg.clone() - ]); + let variant_value = deriving::call_intrinsic( + cx, + span, + sym::discriminant_value, + thin_vec![selflike_arg.clone()], + ); cx.stmt_let(span, false, ident, variant_value) }) .collect(); @@ -1270,7 +1273,7 @@ impl<'a> MethodDef<'a> { trait_, type_ident, nonselflike_args, - &EnumMatching(0, variant, Vec::new()), + &EnumMatching(variant, Vec::new()), ); } } @@ -1282,9 +1285,8 @@ impl<'a> MethodDef<'a> { // where each tuple has length = selflike_args.len() let mut match_arms: ThinVec = variants .iter() - .enumerate() - .filter(|&(_, v)| !(unify_fieldless_variants && v.data.fields().is_empty())) - .map(|(index, variant)| { + .filter(|&v| !(unify_fieldless_variants && v.data.fields().is_empty())) + .map(|variant| { // A single arm has form (&VariantK, &VariantK, ...) => BodyK // (see "Final wrinkle" note below for why.) @@ -1316,7 +1318,7 @@ impl<'a> MethodDef<'a> { // expressions for referencing every field of every // Self arg, assuming all are instances of VariantK. // Build up code associated with such a case. - let substructure = EnumMatching(index, variant, fields); + let substructure = EnumMatching(variant, fields); let arm_expr = self .call_substructure_method( cx, @@ -1344,7 +1346,7 @@ impl<'a> MethodDef<'a> { trait_, type_ident, nonselflike_args, - &EnumMatching(0, v, Vec::new()), + &EnumMatching(v, Vec::new()), ) .into_expr(cx, span), ) @@ -1407,21 +1409,12 @@ impl<'a> MethodDef<'a> { type_ident: Ident, nonselflike_args: &[P], ) -> BlockOrExpr { - let summary = enum_def - .variants - .iter() - .map(|v| { - let sp = v.span.with_ctxt(trait_.span.ctxt()); - let summary = trait_.summarise_struct(cx, &v.data); - (v.ident, sp, summary) - }) - .collect(); self.call_substructure_method( cx, trait_, type_ident, nonselflike_args, - &StaticEnum(enum_def, summary), + &StaticEnum(enum_def), ) } } diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs index af6dc62db7a90..f34a6ae1d9829 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs @@ -21,7 +21,6 @@ pub(crate) struct Path { #[derive(Clone)] pub(crate) enum PathKind { Local, - Global, Std, } @@ -57,7 +56,6 @@ impl Path { let params = tys.map(GenericArg::Type).collect(); match self.kind { - PathKind::Global => cx.path_all(span, true, idents, params), PathKind::Local => cx.path_all(span, false, idents, params), PathKind::Std => { let def_site = cx.with_def_site_ctxt(DUMMY_SP); diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index ec058b4131376..c112589b1319d 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -23,9 +23,7 @@ pub(crate) mod bounds; pub(crate) mod clone; pub(crate) mod coerce_pointee; pub(crate) mod debug; -pub(crate) mod decodable; pub(crate) mod default; -pub(crate) mod encodable; pub(crate) mod hash; #[path = "cmp/eq.rs"] diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs index 8831261759c3c..0913dc91a5342 100644 --- a/compiler/rustc_builtin_macros/src/env.rs +++ b/compiler/rustc_builtin_macros/src/env.rs @@ -77,11 +77,11 @@ pub(crate) fn expand_option_env<'cx>( let guar = cx.dcx().emit_err(errors::EnvNotUnicode { span: sp, var: *symbol }); return ExpandResult::Ready(DummyResult::any(sp, guar)); } - Ok(value) => { - cx.expr_call_global(sp, cx.std_path(&[sym::option, sym::Option, sym::Some]), thin_vec![ - cx.expr_str(sp, value) - ]) - } + Ok(value) => cx.expr_call_global( + sp, + cx.std_path(&[sym::option, sym::Option, sym::Some]), + thin_vec![cx.expr_str(sp, value)], + ), }; ExpandResult::Ready(MacEager::expr(e)) } diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 5202fe26c401e..90447da66807b 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -8,7 +8,9 @@ use rustc_ast::{ token, }; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{Applicability, Diag, MultiSpan, PResult, SingleLabelManySpans}; +use rustc_errors::{ + Applicability, Diag, MultiSpan, PResult, SingleLabelManySpans, listify, pluralize, +}; use rustc_expand::base::*; use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY; use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiag, LintId}; @@ -101,15 +103,14 @@ fn parse_args<'a>(ecx: &ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, match p.expect(exp!(Comma)) { Err(err) => { - match token::TokenKind::Comma.similar_tokens() { - Some(tks) if tks.contains(&p.token.kind) => { - // If a similar token is found, then it may be a typo. We - // consider it as a comma, and continue parsing. - err.emit(); - p.bump(); - } + if token::TokenKind::Comma.similar_tokens().contains(&p.token.kind) { + // If a similar token is found, then it may be a typo. We + // consider it as a comma, and continue parsing. + err.emit(); + p.bump(); + } else { // Otherwise stop the parsing and return the error. - _ => return Err(err), + return Err(err); } } Ok(Recovered::Yes(_)) => (), @@ -406,7 +407,7 @@ fn make_format_args( for piece in &pieces { match *piece { - parse::Piece::String(s) => { + parse::Piece::Lit(s) => { unfinished_literal.push_str(s); } parse::Piece::NextArgument(box parse::Argument { position, position_span, format }) => { @@ -976,15 +977,11 @@ fn report_invalid_references( } else { MultiSpan::from_spans(invalid_refs.iter().filter_map(|&(_, span, _, _)| span).collect()) }; - let arg_list = if let &[index] = &indexes[..] { - format!("argument {index}") - } else { - let tail = indexes.pop().unwrap(); - format!( - "arguments {head} and {tail}", - head = indexes.into_iter().map(|i| i.to_string()).collect::>().join(", ") - ) - }; + let arg_list = format!( + "argument{} {}", + pluralize!(indexes.len()), + listify(&indexes, |i: &usize| i.to_string()).unwrap_or_default() + ); e = ecx.dcx().struct_span_err( span, format!("invalid reference to positional {arg_list} ({num_args_desc})"), diff --git a/compiler/rustc_builtin_macros/src/format_foreign.rs b/compiler/rustc_builtin_macros/src/format_foreign.rs index 71320e6d5ad37..866ec72f11646 100644 --- a/compiler/rustc_builtin_macros/src/format_foreign.rs +++ b/compiler/rustc_builtin_macros/src/format_foreign.rs @@ -183,10 +183,14 @@ pub(crate) mod printf { s.push('{'); if let Some(arg) = self.parameter { - match write!(s, "{}", match arg.checked_sub(1) { - Some(a) => a, - None => return Err(None), - }) { + match write!( + s, + "{}", + match arg.checked_sub(1) { + Some(a) => a, + None => return Err(None), + } + ) { Err(_) => return Err(None), _ => {} } diff --git a/compiler/rustc_builtin_macros/src/format_foreign/printf/tests.rs b/compiler/rustc_builtin_macros/src/format_foreign/printf/tests.rs index 8fe06df48e5ec..1fd54e170b91c 100644 --- a/compiler/rustc_builtin_macros/src/format_foreign/printf/tests.rs +++ b/compiler/rustc_builtin_macros/src/format_foreign/printf/tests.rs @@ -99,12 +99,10 @@ fn test_parse() { fn test_iter() { let s = "The %d'th word %% is: `%.*s` %!\n"; let subs: Vec<_> = iter_subs(s, 0).map(|sub| sub.translate().ok()).collect(); - assert_eq!(subs.iter().map(Option::as_deref).collect::>(), vec![ - Some("{}"), - None, - Some("{:.*}"), - None - ]); + assert_eq!( + subs.iter().map(Option::as_deref).collect::>(), + vec![Some("{}"), None, Some("{:.*}"), None] + ); } /// Checks that the translations are what we expect. diff --git a/compiler/rustc_builtin_macros/src/format_foreign/shell/tests.rs b/compiler/rustc_builtin_macros/src/format_foreign/shell/tests.rs index dd2ee2b6ca9a9..145e3e529b0dc 100644 --- a/compiler/rustc_builtin_macros/src/format_foreign/shell/tests.rs +++ b/compiler/rustc_builtin_macros/src/format_foreign/shell/tests.rs @@ -38,11 +38,10 @@ fn test_iter() { use super::iter_subs; let s = "The $0'th word $$ is: `$WORD` $!\n"; let subs: Vec<_> = iter_subs(s, 0).map(|sub| sub.translate().ok()).collect(); - assert_eq!(subs.iter().map(Option::as_deref).collect::>(), vec![ - Some("{0}"), - None, - Some("{WORD}") - ]); + assert_eq!( + subs.iter().map(Option::as_deref).collect::>(), + vec![Some("{0}"), None, Some("{WORD}")] + ); } #[test] diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index 8388e9dcafb8c..8fdbbf8e704a9 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -81,6 +81,7 @@ impl AllocFnFactory<'_, '_> { defaultness: ast::Defaultness::Final, sig, generics: Generics::default(), + contract: None, body, })); let item = self.cx.item( diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 6071d36f8ebbc..ca16583a45de7 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -16,6 +16,7 @@ #![feature(proc_macro_internals)] #![feature(proc_macro_quote)] #![feature(rustdoc_internals)] +#![feature(string_from_utf8_lossy_owned)] #![feature(try_blocks)] #![warn(unreachable_pub)] // tidy-alphabetical-end @@ -54,6 +55,7 @@ mod trace_macros; pub mod asm; pub mod cmdline_attrs; +pub mod contracts; pub mod proc_macro_harness; pub mod standard_library_imports; pub mod test_harness; @@ -131,11 +133,13 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { Ord: ord::expand_deriving_ord, PartialEq: partial_eq::expand_deriving_partial_eq, PartialOrd: partial_ord::expand_deriving_partial_ord, - RustcDecodable: decodable::expand_deriving_rustc_decodable, - RustcEncodable: encodable::expand_deriving_rustc_encodable, CoercePointee: coerce_pointee::expand_deriving_coerce_pointee, } let client = proc_macro::bridge::client::Client::expand1(proc_macro::quote); register(sym::quote, SyntaxExtensionKind::Bang(Box::new(BangProcMacro { client }))); + let requires = SyntaxExtensionKind::Attr(Box::new(contracts::ExpandRequires)); + register(sym::contracts_requires, requires); + let ensures = SyntaxExtensionKind::Attr(Box::new(contracts::ExpandEnsures)); + register(sym::contracts_ensures, ensures); } diff --git a/compiler/rustc_builtin_macros/src/pattern_type.rs b/compiler/rustc_builtin_macros/src/pattern_type.rs index a600a9f316a7a..a55c7e962d098 100644 --- a/compiler/rustc_builtin_macros/src/pattern_type.rs +++ b/compiler/rustc_builtin_macros/src/pattern_type.rs @@ -1,6 +1,6 @@ use rustc_ast::ptr::P; use rustc_ast::tokenstream::TokenStream; -use rustc_ast::{Pat, Ty, ast}; +use rustc_ast::{AnonConst, DUMMY_NODE_ID, Ty, TyPat, TyPatKind, ast}; use rustc_errors::PResult; use rustc_expand::base::{self, DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult}; use rustc_parse::exp; @@ -21,12 +21,24 @@ pub(crate) fn expand<'cx>( ExpandResult::Ready(base::MacEager::ty(cx.ty(sp, ast::TyKind::Pat(ty, pat)))) } -fn parse_pat_ty<'a>(cx: &mut ExtCtxt<'a>, stream: TokenStream) -> PResult<'a, (P, P)> { +fn parse_pat_ty<'a>(cx: &mut ExtCtxt<'a>, stream: TokenStream) -> PResult<'a, (P, P)> { let mut parser = cx.new_parser_from_tts(stream); let ty = parser.parse_ty()?; parser.expect_keyword(exp!(Is))?; - let pat = parser.parse_pat_no_top_alt(None, None)?; + let pat = parser.parse_pat_no_top_alt(None, None)?.into_inner(); + + let kind = match pat.kind { + ast::PatKind::Range(start, end, include_end) => TyPatKind::Range( + start.map(|value| P(AnonConst { id: DUMMY_NODE_ID, value })), + end.map(|value| P(AnonConst { id: DUMMY_NODE_ID, value })), + include_end, + ), + ast::PatKind::Err(guar) => TyPatKind::Err(guar), + _ => TyPatKind::Err(cx.dcx().span_err(pat.span, "pattern not supported in pattern types")), + }; + + let pat = P(TyPat { id: pat.id, kind, span: pat.span, tokens: pat.tokens }); Ok((ty, pat)) } diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index dee185ff0c95c..ee6475c8b8e91 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -301,13 +301,10 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P { }; let local_path = |cx: &ExtCtxt<'_>, name| cx.expr_path(cx.path(span, vec![name])); let proc_macro_ty_method_path = |cx: &ExtCtxt<'_>, method| { - cx.expr_path(cx.path(span.with_ctxt(harness_span.ctxt()), vec![ - proc_macro, - bridge, - client, - proc_macro_ty, - method, - ])) + cx.expr_path(cx.path( + span.with_ctxt(harness_span.ctxt()), + vec![proc_macro, bridge, client, proc_macro_ty, method], + )) }; match m { ProcMacro::Derive(cd) => { @@ -340,10 +337,14 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P { // The call needs to use `harness_span` so that the const stability checker // accepts it. - cx.expr_call(harness_span, proc_macro_ty_method_path(cx, ident), thin_vec![ - cx.expr_str(span, ca.function_name.name), - local_path(cx, ca.function_name), - ]) + cx.expr_call( + harness_span, + proc_macro_ty_method_path(cx, ident), + thin_vec![ + cx.expr_str(span, ca.function_name.name), + local_path(cx, ca.function_name), + ], + ) } } }) @@ -357,12 +358,9 @@ fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P { span, cx.ty( span, - ast::TyKind::Slice(cx.ty_path(cx.path(span, vec![ - proc_macro, - bridge, - client, - proc_macro_ty, - ]))), + ast::TyKind::Slice( + cx.ty_path(cx.path(span, vec![proc_macro, bridge, client, proc_macro_ty])), + ), ), None, ast::Mutability::Not, diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 123b96f6bcaaa..8142f1518dd8d 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -1,19 +1,19 @@ use std::path::{Path, PathBuf}; use std::rc::Rc; +use std::sync::Arc; use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; use rustc_ast_pretty::pprust; -use rustc_data_structures::sync::Lrc; use rustc_expand::base::{ DummyResult, ExpandResult, ExtCtxt, MacEager, MacResult, MacroExpanderResult, resolve_path, }; use rustc_expand::module::DirOwnership; use rustc_lint_defs::BuiltinLintDiag; use rustc_parse::parser::{ForceCollect, Parser}; -use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal}; +use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal, utf8_error}; use rustc_session::lint::builtin::INCOMPLETE_INCLUDE; use rustc_span::source_map::SourceMap; use rustc_span::{Pos, Span, Symbol}; @@ -209,9 +209,10 @@ pub(crate) fn expand_include_str( let interned_src = Symbol::intern(src); MacEager::expr(cx.expr_str(cx.with_def_site_ctxt(bsp), interned_src)) } - Err(_) => { - let guar = cx.dcx().span_err(sp, format!("`{path}` wasn't a utf-8 file")); - DummyResult::any(sp, guar) + Err(utf8err) => { + let mut err = cx.dcx().struct_span_err(sp, format!("`{path}` wasn't a utf-8 file")); + utf8_error(cx.source_map(), path.as_str(), None, &mut err, utf8err, &bytes[..]); + DummyResult::any(sp, err.emit()) } }, Err(dummy) => dummy, @@ -248,7 +249,7 @@ fn load_binary_file( original_path: &Path, macro_span: Span, path_span: Span, -) -> Result<(Lrc<[u8]>, Span), Box> { +) -> Result<(Arc<[u8]>, Span), Box> { let resolved_path = match resolve_path(&cx.sess, original_path, macro_span) { Ok(path) => path, Err(err) => { @@ -273,7 +274,7 @@ fn load_binary_file( .and_then(|path| path.into_os_string().into_string().ok()); if let Some(new_path) = new_path { - err.span_suggestion( + err.span_suggestion_verbose( path_span, "there is a file with the same name in a different directory", format!("\"{}\"", new_path.replace('\\', "/").escape_debug()), diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 3f73ddbdd297e..a05fff2dcd1c1 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -169,20 +169,26 @@ pub(crate) fn expand_test_or_bench( // creates test::ShouldPanic::$name let should_panic_path = |name| { - cx.path(sp, vec![ - test_id, - Ident::from_str_and_span("ShouldPanic", sp), - Ident::from_str_and_span(name, sp), - ]) + cx.path( + sp, + vec![ + test_id, + Ident::from_str_and_span("ShouldPanic", sp), + Ident::from_str_and_span(name, sp), + ], + ) }; // creates test::TestType::$name let test_type_path = |name| { - cx.path(sp, vec![ - test_id, - Ident::from_str_and_span("TestType", sp), - Ident::from_str_and_span(name, sp), - ]) + cx.path( + sp, + vec![ + test_id, + Ident::from_str_and_span("TestType", sp), + Ident::from_str_and_span(name, sp), + ], + ) }; // creates $name: $expr @@ -202,39 +208,55 @@ pub(crate) fn expand_test_or_bench( // A simple ident for a lambda let b = Ident::from_str_and_span("b", attr_sp); - cx.expr_call(sp, cx.expr_path(test_path("StaticBenchFn")), thin_vec![ - // #[coverage(off)] - // |b| self::test::assert_test_result( - coverage_off(cx.lambda1( - sp, - cx.expr_call(sp, cx.expr_path(test_path("assert_test_result")), thin_vec![ - // super::$test_fn(b) + cx.expr_call( + sp, + cx.expr_path(test_path("StaticBenchFn")), + thin_vec![ + // #[coverage(off)] + // |b| self::test::assert_test_result( + coverage_off(cx.lambda1( + sp, cx.expr_call( - ret_ty_sp, - cx.expr_path(cx.path(sp, vec![item.ident])), - thin_vec![cx.expr_ident(sp, b)], + sp, + cx.expr_path(test_path("assert_test_result")), + thin_vec![ + // super::$test_fn(b) + cx.expr_call( + ret_ty_sp, + cx.expr_path(cx.path(sp, vec![item.ident])), + thin_vec![cx.expr_ident(sp, b)], + ), + ], ), - ],), - b, - )), // ) - ]) + b, + )), // ) + ], + ) } else { - cx.expr_call(sp, cx.expr_path(test_path("StaticTestFn")), thin_vec![ - // #[coverage(off)] - // || { - coverage_off(cx.lambda0( - sp, - // test::assert_test_result( - cx.expr_call(sp, cx.expr_path(test_path("assert_test_result")), thin_vec![ - // $test_fn() + cx.expr_call( + sp, + cx.expr_path(test_path("StaticTestFn")), + thin_vec![ + // #[coverage(off)] + // || { + coverage_off(cx.lambda0( + sp, + // test::assert_test_result( cx.expr_call( - ret_ty_sp, - cx.expr_path(cx.path(sp, vec![item.ident])), - ThinVec::new(), - ), // ) - ],), // } - )), // ) - ]) + sp, + cx.expr_path(test_path("assert_test_result")), + thin_vec![ + // $test_fn() + cx.expr_call( + ret_ty_sp, + cx.expr_path(cx.path(sp, vec![item.ident])), + ThinVec::new(), + ), // ) + ], + ), // } + )), // ) + ], + ) }; let test_path_symbol = Symbol::intern(&item_path( @@ -245,26 +267,30 @@ pub(crate) fn expand_test_or_bench( let location_info = get_location_info(cx, &item); - let mut test_const = cx.item( - sp, - Ident::new(item.ident.name, sp), - thin_vec![ - // #[cfg(test)] - cx.attr_nested_word(sym::cfg, sym::test, attr_sp), - // #[rustc_test_marker = "test_case_sort_key"] - cx.attr_name_value_str(sym::rustc_test_marker, test_path_symbol, attr_sp), - // #[doc(hidden)] - cx.attr_nested_word(sym::doc, sym::hidden, attr_sp), - ], - // const $ident: test::TestDescAndFn = - ast::ItemKind::Const( - ast::ConstItem { - defaultness: ast::Defaultness::Final, - generics: ast::Generics::default(), - ty: cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))), - // test::TestDescAndFn { - expr: Some( - cx.expr_struct(sp, test_path("TestDescAndFn"), thin_vec![ + let mut test_const = + cx.item( + sp, + Ident::new(item.ident.name, sp), + thin_vec![ + // #[cfg(test)] + cx.attr_nested_word(sym::cfg, sym::test, attr_sp), + // #[rustc_test_marker = "test_case_sort_key"] + cx.attr_name_value_str(sym::rustc_test_marker, test_path_symbol, attr_sp), + // #[doc(hidden)] + cx.attr_nested_word(sym::doc, sym::hidden, attr_sp), + ], + // const $ident: test::TestDescAndFn = + ast::ItemKind::Const( + ast::ConstItem { + defaultness: ast::Defaultness::Final, + generics: ast::Generics::default(), + ty: cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))), + // test::TestDescAndFn { + expr: Some( + cx.expr_struct( + sp, + test_path("TestDescAndFn"), + thin_vec![ // desc: test::TestDesc { field( "desc", @@ -340,12 +366,13 @@ pub(crate) fn expand_test_or_bench( ), // testfn: test::StaticTestFn(...) | test::StaticBenchFn(...) field("testfn", test_fn), // } - ]), // } - ), - } - .into(), - ), - ); + ], + ), // } + ), + } + .into(), + ), + ); test_const = test_const.map(|mut tc| { tc.vis.kind = ast::VisibilityKind::Public; tc diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 4644659894368..472e16e62d5b0 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -204,11 +204,11 @@ impl<'a> MutVisitor for EntryPointCleaner<'a> { ast::mut_visit::walk_item(self, item); self.depth -= 1; - // Remove any #[rustc_main] or #[start] from the AST so it doesn't + // Remove any #[rustc_main] from the AST so it doesn't // clash with the one we're going to add, but mark it as // #[allow(dead_code)] to avoid printing warnings. match entry_point_type(&item, self.depth == 0) { - EntryPointType::MainNamed | EntryPointType::RustcMainAttr | EntryPointType::Start => { + EntryPointType::MainNamed | EntryPointType::RustcMainAttr => { let allow_dead_code = attr::mk_attr_nested_word( &self.sess.psess.attr_id_generator, ast::AttrStyle::Outer, @@ -217,8 +217,7 @@ impl<'a> MutVisitor for EntryPointCleaner<'a> { sym::dead_code, self.def_site, ); - item.attrs - .retain(|attr| !attr.has_name(sym::rustc_main) && !attr.has_name(sym::start)); + item.attrs.retain(|attr| !attr.has_name(sym::rustc_main)); item.attrs.push(allow_dead_code); } EntryPointType::None | EntryPointType::OtherMain => {} @@ -345,6 +344,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P { defaultness, sig, generics: ast::Generics::default(), + contract: None, body: Some(main_body), })); diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml index 2ee94146c1a4e..a8333df77e6e7 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml @@ -56,11 +56,6 @@ jobs: - os: macos-latest env: TARGET_TRIPLE: x86_64-apple-darwin - # cross-compile from Linux to Windows using mingw - - os: ubuntu-latest - env: - TARGET_TRIPLE: x86_64-pc-windows-gnu - apt_deps: gcc-mingw-w64-x86-64 wine-stable - os: ubuntu-latest env: TARGET_TRIPLE: aarch64-unknown-linux-gnu @@ -113,15 +108,6 @@ jobs: - name: Prepare dependencies run: ./y.sh prepare - # The Wine version shipped with Ubuntu 22.04 doesn't implement bcryptprimitives.dll - - name: Build bcryptprimitives.dll shim for Wine - if: matrix.os == 'ubuntu-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu' - run: | - rustup target add x86_64-pc-windows-gnu - mkdir wine_shims - rustc patches/bcryptprimitives.rs -Copt-level=3 -Clto=fat --out-dir wine_shims --target x86_64-pc-windows-gnu - echo "WINEPATH=$(pwd)/wine_shims" >> $GITHUB_ENV - - name: Build run: ./y.sh build --sysroot none @@ -135,9 +121,6 @@ jobs: # This is roughly config rust-lang/rust uses for testing - name: Test with LLVM sysroot - # Skip native x86_64-pc-windows-gnu. It is way too slow and cross-compiled - # x86_64-pc-windows-gnu covers at least part of the tests. - if: matrix.os != 'windows-latest' || matrix.env.TARGET_TRIPLE != 'x86_64-pc-windows-gnu' env: TARGET_TRIPLE: ${{ matrix.env.TARGET_TRIPLE }} run: ./y.sh test --sysroot llvm --no-unstable-features @@ -215,10 +198,6 @@ jobs: - os: macos-latest env: TARGET_TRIPLE: aarch64-apple-darwin - # cross-compile from Linux to Windows using mingw - - os: ubuntu-latest - env: - TARGET_TRIPLE: x86_64-pc-windows-gnu - os: windows-latest env: TARGET_TRIPLE: x86_64-pc-windows-msvc @@ -243,12 +222,6 @@ jobs: if: matrix.os == 'macos-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-apple-darwin' run: rustup set default-host x86_64-apple-darwin - - name: Install MinGW toolchain - if: matrix.os == 'ubuntu-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu' - run: | - sudo apt-get update - sudo apt-get install -y gcc-mingw-w64-x86-64 - - name: Prepare dependencies run: ./y.sh prepare @@ -262,19 +235,11 @@ jobs: run: tar cvfJ cg_clif.tar.xz dist - name: Upload prebuilt cg_clif - if: matrix.os == 'windows-latest' || matrix.env.TARGET_TRIPLE != 'x86_64-pc-windows-gnu' uses: actions/upload-artifact@v4 with: name: cg_clif-${{ matrix.env.TARGET_TRIPLE }} path: cg_clif.tar.xz - - name: Upload prebuilt cg_clif (cross compile) - if: matrix.os != 'windows-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu' - uses: actions/upload-artifact@v4 - with: - name: cg_clif-${{ runner.os }}-cross-x86_64-mingw - path: cg_clif.tar.xz - release: runs-on: ubuntu-latest timeout-minutes: 10 diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock index d81e7214961f6..ca66ec5c6e930 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/Cargo.lock @@ -3,28 +3,22 @@ version = 4 [[package]] -name = "ahash" -version = "0.8.11" +name = "allocator-api2" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "arbitrary" -version = "1.3.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" [[package]] name = "bitflags" @@ -37,6 +31,9 @@ name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +dependencies = [ + "allocator-api2", +] [[package]] name = "cfg-if" @@ -46,24 +43,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cranelift-bforest" -version = "0.114.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ba4f80548f22dc9c43911907b5e322c5555544ee85f785115701e6a28c9abe1" +checksum = "e15d04a0ce86cb36ead88ad68cf693ffd6cda47052b9e0ac114bc47fd9cd23c4" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-bitset" -version = "0.114.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "005884e3649c3e5ff2dc79e8a94b138f11569cc08a91244a292714d2a86e9156" +checksum = "7c6e3969a7ce267259ce244b7867c5d3bc9e65b0a87e81039588dfdeaede9f34" [[package]] name = "cranelift-codegen" -version = "0.114.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4036255ec33ce9a37495dfbcfc4e1118fd34e693eff9a1e106336b7cd16a9b" +checksum = "2c22032c4cb42558371cf516bb47f26cdad1819d3475c133e93c49f50ebf304e" dependencies = [ "bumpalo", "cranelift-bforest", @@ -74,7 +71,7 @@ dependencies = [ "cranelift-entity", "cranelift-isle", "gimli", - "hashbrown", + "hashbrown 0.14.5", "log", "regalloc2", "rustc-hash", @@ -85,42 +82,42 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.114.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7ca74f4b68319da11d39e894437cb6e20ec7c2e11fbbda823c3bf207beedff7" +checksum = "c904bc71c61b27fc57827f4a1379f29de64fe95653b620a3db77d59655eee0b8" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.114.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897e54f433a0269c4187871aa06d452214d5515d228d5bdc22219585e9eef895" +checksum = "40180f5497572f644ce88c255480981ae2ec1d7bb4d8e0c0136a13b87a2f2ceb" [[package]] name = "cranelift-control" -version = "0.114.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29cb4018f5bf59fb53f515fa9d80e6f8c5ce19f198dc538984ebd23ecf8965ec" +checksum = "26d132c6d0bd8a489563472afc171759da0707804a65ece7ceb15a8c6d7dd5ef" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.114.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "305399fd781a2953ac78c1396f02ff53144f39c33eb7fc7789cf4e8936d13a96" +checksum = "4b2d0d9618275474fbf679dd018ac6e009acbd6ae6850f6a67be33fb3b00b323" dependencies = [ "cranelift-bitset", ] [[package]] name = "cranelift-frontend" -version = "0.114.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9230b460a128d53653456137751d27baf567947a3ab8c0c4d6e31fd08036d81e" +checksum = "4fac41e16729107393174b0c9e3730fb072866100e1e64e80a1a963b2e484d57" dependencies = [ "cranelift-codegen", "log", @@ -130,15 +127,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.114.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b961e24ae3ec9813a24a15ae64bbd2a42e4de4d79a7f3225a412e3b94e78d1c8" +checksum = "1ca20d576e5070044d0a72a9effc2deacf4d6aa650403189d8ea50126483944d" [[package]] name = "cranelift-jit" -version = "0.114.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62699329d4ced20fe281fbaef45e11b473b7ab310491b4bdebcd8b818a8ef7fe" +checksum = "5e65c42755a719b09662b00c700daaf76cc35d5ace1f5c002ad404b591ff1978" dependencies = [ "anyhow", "cranelift-codegen", @@ -156,9 +153,9 @@ dependencies = [ [[package]] name = "cranelift-module" -version = "0.114.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f20b0b51ba962dac30fc7e812b86e4390d908acd4f59bcc8ac7610a8f3e0977" +checksum = "4d55612bebcf16ff7306c8a6f5bdb6d45662b8aa1ee058ecce8807ad87db719b" dependencies = [ "anyhow", "cranelift-codegen", @@ -167,9 +164,9 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.114.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d5bd76df6c9151188dfa428c863b33da5b34561b67f43c0cf3f24a794f9fa1f" +checksum = "b8dee82f3f1f2c4cba9177f1cc5e350fe98764379bcd29340caa7b01f85076c7" dependencies = [ "cranelift-codegen", "libc", @@ -178,9 +175,9 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.114.0" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee231640a7ecceedd0f1f2782d9288db6a6908cc70675ed9427e3bf0ea6daacd" +checksum = "aad5a6d3e379493c3f8b35dc61c93d0bf5f27003bbe20614e0200b0ec372ef52" dependencies = [ "anyhow", "cranelift-codegen", @@ -212,6 +209,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "gimli" version = "0.31.1" @@ -228,31 +231,37 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ - "ahash", + "foldhash", ] [[package]] name = "indexmap" -version = "2.2.6" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.2", ] [[package]] name = "libc" -version = "0.2.155" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libloading" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", "windows-targets", @@ -281,50 +290,45 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "object" -version = "0.36.2" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "crc32fast", - "hashbrown", + "hashbrown 0.15.2", "indexmap", "memchr", ] -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] [[package]] name = "regalloc2" -version = "0.10.2" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12908dbeb234370af84d0579b9f68258a0f67e201412dd9a2814e6f45b2fc0f0" +checksum = "145c1c267e14f20fb0f88aa76a1c5ffec42d592c1d28b3cd9148ae35916158d3" dependencies = [ - "hashbrown", + "allocator-api2", + "bumpalo", + "hashbrown 0.15.2", "log", "rustc-hash", - "slice-group-by", "smallvec", ] @@ -342,9 +346,9 @@ dependencies = [ [[package]] name = "rustc-hash" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" [[package]] name = "rustc_codegen_cranelift" @@ -366,30 +370,24 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.210" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", "syn", ] -[[package]] -name = "slice-group-by" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" - [[package]] name = "smallvec" version = "1.13.2" @@ -404,9 +402,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "syn" -version = "2.0.70" +version = "2.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16" +checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a" dependencies = [ "proc-macro2", "quote", @@ -415,27 +413,21 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.16" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" +checksum = "dc12939a1c9b9d391e0b7135f72fd30508b73450753e28341fed159317582a77" [[package]] name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "version_check" -version = "0.9.4" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "wasmtime-jit-icache-coherence" -version = "27.0.0" +version = "29.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91b218a92866f74f35162f5d03a4e0f62cd0e1cc624285b1014275e5d4575fad" +checksum = "ec5e8552e01692e6c2e5293171704fed8abdec79d1a6995a0870ab190e5747d1" dependencies = [ "anyhow", "cfg-if", @@ -524,23 +516,3 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml index b2fed3c490edb..670d6f4eef5c1 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.toml +++ b/compiler/rustc_codegen_cranelift/Cargo.toml @@ -8,13 +8,13 @@ crate-type = ["dylib"] [dependencies] # These have to be in sync with each other -cranelift-codegen = { version = "0.114.0", default-features = false, features = ["std", "unwind", "all-native-arch"] } -cranelift-frontend = { version = "0.114.0" } -cranelift-module = { version = "0.114.0" } -cranelift-native = { version = "0.114.0" } -cranelift-jit = { version = "0.114.0", optional = true } -cranelift-object = { version = "0.114.0" } -target-lexicon = "0.12.0" +cranelift-codegen = { version = "0.116.0", default-features = false, features = ["std", "unwind", "all-native-arch"] } +cranelift-frontend = { version = "0.116.0" } +cranelift-module = { version = "0.116.0" } +cranelift-native = { version = "0.116.0" } +cranelift-jit = { version = "0.116.0", optional = true } +cranelift-object = { version = "0.116.0" } +target-lexicon = "0.13" gimli = { version = "0.31", default-features = false, features = ["write"] } object = { version = "0.36", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } @@ -23,6 +23,14 @@ libloading = { version = "0.8.0", optional = true } smallvec = "1.8.1" [patch.crates-io] +# Uncomment to use an unreleased version of cranelift +#cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-29.0.0", version = "0.116.0" } +#cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-29.0.0", version = "0.116.0" } +#cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-29.0.0", version = "0.116.0" } +#cranelift-native = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-29.0.0", version = "0.116.0" } +#cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-29.0.0", version = "0.116.0" } +#cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "release-29.0.0", version = "0.116.0" } + # Uncomment to use local checkout of cranelift #cranelift-codegen = { path = "../wasmtime/cranelift/codegen" } #cranelift-frontend = { path = "../wasmtime/cranelift/frontend" } diff --git a/compiler/rustc_codegen_cranelift/build_system/bench.rs b/compiler/rustc_codegen_cranelift/build_system/bench.rs index 73a0f325fc212..8359b7b527903 100644 --- a/compiler/rustc_codegen_cranelift/build_system/bench.rs +++ b/compiler/rustc_codegen_cranelift/build_system/bench.rs @@ -16,11 +16,7 @@ static SIMPLE_RAYTRACER_REPO: GitRepo = GitRepo::github( "", ); -pub(crate) fn benchmark(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { - benchmark_simple_raytracer(dirs, bootstrap_host_compiler); -} - -fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { +pub(crate) fn benchmark(dirs: &Dirs, compiler: &Compiler) { if std::process::Command::new("hyperfine").output().is_err() { eprintln!("Hyperfine not installed"); eprintln!("Hint: Try `cargo install hyperfine` to install hyperfine"); @@ -39,9 +35,9 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { }; eprintln!("[BENCH COMPILE] ebobby/simple-raytracer"); - let cargo_clif = dirs - .dist_dir - .join(get_file_name(&bootstrap_host_compiler.rustc, "cargo_clif", "bin").replace('_', "-")); + let cargo_clif = &compiler.cargo; + let rustc_clif = &compiler.rustc; + let rustflags = &compiler.rustflags.join("\x1f"); let manifest_path = SIMPLE_RAYTRACER_REPO.source_dir().to_path(dirs).join("Cargo.toml"); let target_dir = dirs.build_dir.join("simple_raytracer"); @@ -56,22 +52,24 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { target_dir = target_dir.display(), ); let clif_build_cmd = format!( - "RUSTC=rustc {cargo_clif} build --manifest-path {manifest_path} --target-dir {target_dir} && (rm build/raytracer_cg_clif || true) && ln build/simple_raytracer/debug/main build/raytracer_cg_clif", + "RUSTC={rustc_clif} CARGO_ENCODED_RUSTFLAGS=\"{rustflags}\" {cargo_clif} build --manifest-path {manifest_path} --target-dir {target_dir} && (rm build/raytracer_cg_clif || true) && ln build/simple_raytracer/debug/main build/raytracer_cg_clif", cargo_clif = cargo_clif.display(), + rustc_clif = rustc_clif.display(), manifest_path = manifest_path.display(), target_dir = target_dir.display(), ); let clif_build_opt_cmd = format!( - "RUSTC=rustc {cargo_clif} build --manifest-path {manifest_path} --target-dir {target_dir} --release && (rm build/raytracer_cg_clif_opt || true) && ln build/simple_raytracer/release/main build/raytracer_cg_clif_opt", + "RUSTC={rustc_clif} CARGO_ENCODED_RUSTFLAGS=\"{rustflags}\" {cargo_clif} build --manifest-path {manifest_path} --target-dir {target_dir} --release && (rm build/raytracer_cg_clif_opt || true) && ln build/simple_raytracer/release/main build/raytracer_cg_clif_opt", cargo_clif = cargo_clif.display(), + rustc_clif = rustc_clif.display(), manifest_path = manifest_path.display(), target_dir = target_dir.display(), ); - let bench_compile_markdown = dirs.dist_dir.join("bench_compile.md"); + let bench_compile_markdown = dirs.build_dir.join("bench_compile.md"); let bench_compile = hyperfine_command( - 1, + 0, bench_runs, Some(&clean_cmd), &[ @@ -92,23 +90,14 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { eprintln!("[BENCH RUN] ebobby/simple-raytracer"); - let bench_run_markdown = dirs.dist_dir.join("bench_run.md"); - - let raytracer_cg_llvm = Path::new(".").join(get_file_name( - &bootstrap_host_compiler.rustc, - "raytracer_cg_llvm", - "bin", - )); - let raytracer_cg_clif = Path::new(".").join(get_file_name( - &bootstrap_host_compiler.rustc, - "raytracer_cg_clif", - "bin", - )); - let raytracer_cg_clif_opt = Path::new(".").join(get_file_name( - &bootstrap_host_compiler.rustc, - "raytracer_cg_clif_opt", - "bin", - )); + let bench_run_markdown = dirs.build_dir.join("bench_run.md"); + + let raytracer_cg_llvm = + Path::new(".").join(get_file_name(&compiler.rustc, "raytracer_cg_llvm", "bin")); + let raytracer_cg_clif = + Path::new(".").join(get_file_name(&compiler.rustc, "raytracer_cg_clif", "bin")); + let raytracer_cg_clif_opt = + Path::new(".").join(get_file_name(&compiler.rustc, "raytracer_cg_clif_opt", "bin")); let mut bench_run = hyperfine_command( 0, bench_runs, diff --git a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs index e47e982991622..a73e3c87d43d0 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs @@ -33,14 +33,7 @@ pub(crate) fn build_sysroot( let cg_clif_dylib_path = match cg_clif_dylib_src { CodegenBackend::Local(src_path) => { // Copy the backend - let cg_clif_dylib_path = if cfg!(windows) { - // Windows doesn't have rpath support, so the cg_clif dylib needs to be next to the - // binaries. - dist_dir.join("bin") - } else { - dist_dir.join("lib") - } - .join(src_path.file_name().unwrap()); + let cg_clif_dylib_path = dist_dir.join("lib").join(src_path.file_name().unwrap()); try_hard_link(src_path, &cg_clif_dylib_path); CodegenBackend::Local(cg_clif_dylib_path) } @@ -102,19 +95,14 @@ pub(crate) fn build_sysroot( .install_into_sysroot(dist_dir); } - let mut target_compiler = { - let rustc_clif = dist_dir.join(wrapper_base_name.replace("____", "rustc-clif")); - let rustdoc_clif = dist_dir.join(wrapper_base_name.replace("____", "rustdoc-clif")); - - Compiler { - cargo: bootstrap_host_compiler.cargo.clone(), - rustc: rustc_clif.clone(), - rustdoc: rustdoc_clif.clone(), - rustflags: vec![], - rustdocflags: vec![], - triple: target_triple, - runner: vec![], - } + let mut target_compiler = Compiler { + cargo: bootstrap_host_compiler.cargo.clone(), + rustc: dist_dir.join(wrapper_base_name.replace("____", "rustc-clif")), + rustdoc: dist_dir.join(wrapper_base_name.replace("____", "rustdoc-clif")), + rustflags: vec![], + rustdocflags: vec![], + triple: target_triple, + runner: vec![], }; if !is_native { target_compiler.set_cross_linker_and_runner(); diff --git a/compiler/rustc_codegen_cranelift/build_system/config.rs b/compiler/rustc_codegen_cranelift/build_system/config.rs index ef540cf1f822b..37bc4c5d78275 100644 --- a/compiler/rustc_codegen_cranelift/build_system/config.rs +++ b/compiler/rustc_codegen_cranelift/build_system/config.rs @@ -33,23 +33,3 @@ pub(crate) fn get_bool(name: &str) -> bool { true } } - -pub(crate) fn get_value(name: &str) -> Option { - let values = load_config_file() - .into_iter() - .filter(|(key, _)| key == name) - .map(|(_, val)| val) - .collect::>(); - if values.is_empty() { - None - } else if values.len() == 1 { - if values[0].is_none() { - eprintln!("Config `{}` missing value", name); - process::exit(1); - } - values.into_iter().next().unwrap() - } else { - eprintln!("Config `{}` given multiple values: {:?}", name, values); - process::exit(1); - } -} diff --git a/compiler/rustc_codegen_cranelift/build_system/main.rs b/compiler/rustc_codegen_cranelift/build_system/main.rs index 99e6146657f34..3ff9751a3ef2d 100644 --- a/compiler/rustc_codegen_cranelift/build_system/main.rs +++ b/compiler/rustc_codegen_cranelift/build_system/main.rs @@ -156,10 +156,8 @@ fn main() { let cargo = rustc_info::get_cargo_path(); let rustc = rustc_info::get_rustc_path(); let rustdoc = rustc_info::get_rustdoc_path(); - let triple = std::env::var("HOST_TRIPLE") - .ok() - .or_else(|| config::get_value("host")) - .unwrap_or_else(|| rustc_info::get_host_triple(&rustc)); + let triple = + std::env::var("HOST_TRIPLE").unwrap_or_else(|_| rustc_info::get_host_triple(&rustc)); Compiler { cargo, rustc, @@ -170,10 +168,8 @@ fn main() { runner: vec![], } }; - let target_triple = std::env::var("TARGET_TRIPLE") - .ok() - .or_else(|| config::get_value("target")) - .unwrap_or_else(|| bootstrap_host_compiler.triple.clone()); + let target_triple = + std::env::var("TARGET_TRIPLE").unwrap_or_else(|_| bootstrap_host_compiler.triple.clone()); let dirs = path::Dirs { source_dir: current_dir.clone(), @@ -247,7 +243,7 @@ fn main() { ); } Command::Bench => { - build_sysroot::build_sysroot( + let compiler = build_sysroot::build_sysroot( &dirs, sysroot_kind, &cg_clif_dylib, @@ -255,7 +251,7 @@ fn main() { rustup_toolchain_name.as_deref(), target_triple, ); - bench::benchmark(&dirs, &bootstrap_host_compiler); + bench::benchmark(&dirs, &compiler); } } } diff --git a/compiler/rustc_codegen_cranelift/build_system/path.rs b/compiler/rustc_codegen_cranelift/build_system/path.rs index 20a81156b71d9..d6a6558b2be29 100644 --- a/compiler/rustc_codegen_cranelift/build_system/path.rs +++ b/compiler/rustc_codegen_cranelift/build_system/path.rs @@ -11,20 +11,11 @@ pub(crate) struct Dirs { #[doc(hidden)] #[derive(Debug, Copy, Clone)] -pub(crate) enum PathBase { +enum PathBase { Source, Build, } -impl PathBase { - fn to_path(self, dirs: &Dirs) -> PathBuf { - match self { - PathBase::Source => dirs.source_dir.clone(), - PathBase::Build => dirs.build_dir.clone(), - } - } -} - #[derive(Debug, Copy, Clone)] pub(crate) struct RelPath { base: PathBase, @@ -41,6 +32,9 @@ impl RelPath { } pub(crate) fn to_path(&self, dirs: &Dirs) -> PathBuf { - self.base.to_path(dirs).join(self.suffix) + match self.base { + PathBase::Source => dirs.source_dir.join(self.suffix), + PathBase::Build => dirs.build_dir.join(self.suffix), + } } } diff --git a/compiler/rustc_codegen_cranelift/build_system/prepare.rs b/compiler/rustc_codegen_cranelift/build_system/prepare.rs index a4e9cb5f5c8c2..11f73bdb61f97 100644 --- a/compiler/rustc_codegen_cranelift/build_system/prepare.rs +++ b/compiler/rustc_codegen_cranelift/build_system/prepare.rs @@ -186,7 +186,7 @@ fn init_git_repo(repo_dir: &Path) { spawn_and_wait(git_add_cmd); let mut git_commit_cmd = git_command(repo_dir, "commit"); - git_commit_cmd.arg("-m").arg("Initial commit").arg("-q"); + git_commit_cmd.arg("-m").arg("Initial commit").arg("-q").arg("--no-verify"); spawn_and_wait(git_commit_cmd); } diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs index 08736db8ba0c8..ea7e94c345ad0 100644 --- a/compiler/rustc_codegen_cranelift/build_system/tests.rs +++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs @@ -73,8 +73,6 @@ const BASE_SYSROOT_SUITE: &[TestCase] = &[ "example/arbitrary_self_types_pointers_and_wrappers.rs", &[], ), - TestCase::build_lib("build.alloc_system", "example/alloc_system.rs", "lib"), - TestCase::build_bin_and_run("aot.alloc_example", "example/alloc_example.rs", &[]), TestCase::jit_bin("jit.std_example", "example/std_example.rs", "arg"), TestCase::build_bin_and_run("aot.std_example", "example/std_example.rs", &["arg"]), TestCase::build_bin_and_run("aot.dst_field_align", "example/dst-field-align.rs", &[]), @@ -89,7 +87,6 @@ const BASE_SYSROOT_SUITE: &[TestCase] = &[ &[], ), TestCase::build_bin_and_run("aot.float-minmax-pass", "example/float-minmax-pass.rs", &[]), - TestCase::build_bin_and_run("aot.mod_bench", "example/mod_bench.rs", &[]), TestCase::build_bin_and_run("aot.issue-72793", "example/issue-72793.rs", &[]), TestCase::build_bin("aot.issue-59326", "example/issue-59326.rs"), TestCase::build_bin_and_run("aot.neon", "example/neon.rs", &[]), @@ -154,7 +151,7 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ apply_patches( &runner.dirs, "coretests", - &runner.stdlib_source.join("library/core/tests"), + &runner.stdlib_source.join("library/coretests"), &LIBCORE_TESTS_SRC.to_path(&runner.dirs), ); @@ -333,14 +330,6 @@ impl<'a> TestRunner<'a> { target_compiler.rustflags.extend(rustflags_from_env("RUSTFLAGS")); target_compiler.rustdocflags.extend(rustflags_from_env("RUSTDOCFLAGS")); - // FIXME fix `#[linkage = "extern_weak"]` without this - if target_compiler.triple.contains("darwin") { - target_compiler.rustflags.extend([ - "-Clink-arg=-undefined".to_owned(), - "-Clink-arg=dynamic_lookup".to_owned(), - ]); - } - let jit_supported = use_unstable_features && is_native && target_compiler.triple.contains("x86_64") diff --git a/compiler/rustc_codegen_cranelift/config.txt b/compiler/rustc_codegen_cranelift/config.txt index b63597f60fc6f..f578cbef35e68 100644 --- a/compiler/rustc_codegen_cranelift/config.txt +++ b/compiler/rustc_codegen_cranelift/config.txt @@ -1,15 +1,5 @@ # This file allows configuring the build system. -# Which triple to produce a compiler toolchain for. -# -# Defaults to the default triple of rustc on the host system. -#host = x86_64-unknown-linux-gnu - -# Which triple to build libraries (core/alloc/std/test/proc_macro) for. -# -# Defaults to `host`. -#target = x86_64-unknown-linux-gnu - # Disables cleaning of the sysroot dir. This will cause old compiled artifacts to be re-used when # the sysroot source hasn't changed. This is useful when the codegen backend hasn't been modified. # This option can be changed while the build system is already running for as long as sysroot @@ -31,15 +21,12 @@ aot.mini_core_hello_world testsuite.base_sysroot aot.arbitrary_self_types_pointers_and_wrappers aot.issue_91827_extern_types -build.alloc_system -aot.alloc_example jit.std_example aot.std_example aot.dst_field_align aot.subslice-patterns-const-eval aot.track-caller-attribute aot.float-minmax-pass -aot.mod_bench aot.issue-72793 aot.issue-59326 aot.neon diff --git a/compiler/rustc_codegen_cranelift/example/alloc_example.rs b/compiler/rustc_codegen_cranelift/example/alloc_example.rs deleted file mode 100644 index da70ca7943983..0000000000000 --- a/compiler/rustc_codegen_cranelift/example/alloc_example.rs +++ /dev/null @@ -1,44 +0,0 @@ -#![feature(start, core_intrinsics, alloc_error_handler, lang_items)] -#![allow(internal_features)] -#![no_std] - -extern crate alloc; -extern crate alloc_system; - -use alloc::boxed::Box; - -use alloc_system::System; - -#[global_allocator] -static ALLOC: System = System; - -#[cfg_attr(unix, link(name = "c"))] -#[cfg_attr(target_env = "msvc", link(name = "msvcrt"))] -extern "C" { - fn puts(s: *const u8) -> i32; -} - -#[panic_handler] -fn panic_handler(_: &core::panic::PanicInfo<'_>) -> ! { - core::intrinsics::abort(); -} - -#[alloc_error_handler] -fn alloc_error_handler(_: alloc::alloc::Layout) -> ! { - core::intrinsics::abort(); -} - -#[lang = "eh_personality"] -fn eh_personality() -> ! { - loop {} -} - -#[start] -fn main(_argc: isize, _argv: *const *const u8) -> isize { - let world: Box<&str> = Box::new("Hello World!\0"); - unsafe { - puts(*world as *const str as *const u8); - } - - 0 -} diff --git a/compiler/rustc_codegen_cranelift/example/alloc_system.rs b/compiler/rustc_codegen_cranelift/example/alloc_system.rs deleted file mode 100644 index 2884c9c32ae4d..0000000000000 --- a/compiler/rustc_codegen_cranelift/example/alloc_system.rs +++ /dev/null @@ -1,124 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -// SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org) - -#![no_std] - -pub struct System; - -#[cfg(any(windows, unix, target_os = "redox"))] -mod realloc_fallback { - use core::alloc::{GlobalAlloc, Layout}; - use core::{cmp, ptr}; - impl super::System { - pub(crate) unsafe fn realloc_fallback( - &self, - ptr: *mut u8, - old_layout: Layout, - new_size: usize, - ) -> *mut u8 { - // Docs for GlobalAlloc::realloc require this to be valid: - let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); - let new_ptr = GlobalAlloc::alloc(self, new_layout); - if !new_ptr.is_null() { - let size = cmp::min(old_layout.size(), new_size); - ptr::copy_nonoverlapping(ptr, new_ptr, size); - GlobalAlloc::dealloc(self, ptr, old_layout); - } - new_ptr - } - } -} -#[cfg(any(unix, target_os = "redox"))] -mod platform { - use core::alloc::{GlobalAlloc, Layout}; - use core::ffi::c_void; - use core::ptr; - - use System; - extern "C" { - fn posix_memalign(memptr: *mut *mut c_void, align: usize, size: usize) -> i32; - fn free(p: *mut c_void); - } - unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - aligned_malloc(&layout) - } - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - let ptr = self.alloc(layout.clone()); - if !ptr.is_null() { - ptr::write_bytes(ptr, 0, layout.size()); - } - ptr - } - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { - free(ptr as *mut c_void) - } - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - self.realloc_fallback(ptr, layout, new_size) - } - } - unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { - let mut out = ptr::null_mut(); - let ret = posix_memalign(&mut out, layout.align(), layout.size()); - if ret != 0 { ptr::null_mut() } else { out as *mut u8 } - } -} -#[cfg(windows)] -#[allow(nonstandard_style)] -mod platform { - use core::alloc::{GlobalAlloc, Layout}; - - use System; - type LPVOID = *mut u8; - type HANDLE = LPVOID; - type SIZE_T = usize; - type DWORD = u32; - type BOOL = i32; - extern "system" { - fn GetProcessHeap() -> HANDLE; - fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID; - fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL; - fn GetLastError() -> DWORD; - } - #[repr(C)] - struct Header(*mut u8); - const HEAP_ZERO_MEMORY: DWORD = 0x00000008; - unsafe fn get_header<'a>(ptr: *mut u8) -> &'a mut Header { - &mut *(ptr as *mut Header).sub(1) - } - unsafe fn align_ptr(ptr: *mut u8, align: usize) -> *mut u8 { - let aligned = ptr.add(align - (ptr as usize & (align - 1))); - *get_header(aligned) = Header(ptr); - aligned - } - #[inline] - unsafe fn allocate_with_flags(layout: Layout, flags: DWORD) -> *mut u8 { - let size = layout.size() + layout.align(); - let ptr = HeapAlloc(GetProcessHeap(), flags, size); - (if ptr.is_null() { ptr } else { align_ptr(ptr, layout.align()) }) as *mut u8 - } - unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - allocate_with_flags(layout, 0) - } - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - allocate_with_flags(layout, HEAP_ZERO_MEMORY) - } - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { - let header = get_header(ptr); - let err = HeapFree(GetProcessHeap(), 0, header.0 as LPVOID); - debug_assert!(err != 0, "Failed to free heap memory: {}", GetLastError()); - } - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - self.realloc_fallback(ptr, layout, new_size) - } - } -} diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs index a0a381638c061..79820232496a5 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs @@ -57,6 +57,9 @@ impl LegacyReceiver for Box {} #[lang = "copy"] pub trait Copy {} +#[lang = "bikeshed_guaranteed_no_drop"] +pub trait BikeshedGuaranteedNoDrop {} + impl Copy for bool {} impl Copy for u8 {} impl Copy for u16 {} diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs index e47431e0f873a..09d5b73fd3d9d 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs @@ -274,7 +274,7 @@ fn main() { assert_eq!(((|()| 42u8) as fn(()) -> u8)(()), 42); - #[cfg(not(any(jit, windows)))] + #[cfg(not(any(jit, target_vendor = "apple", windows)))] { extern "C" { #[linkage = "extern_weak"] diff --git a/compiler/rustc_codegen_cranelift/example/mod_bench.rs b/compiler/rustc_codegen_cranelift/example/mod_bench.rs deleted file mode 100644 index 11a3e8fc72d8d..0000000000000 --- a/compiler/rustc_codegen_cranelift/example/mod_bench.rs +++ /dev/null @@ -1,37 +0,0 @@ -#![feature(start, core_intrinsics, lang_items)] -#![allow(internal_features)] -#![no_std] - -#[cfg_attr(unix, link(name = "c"))] -#[cfg_attr(target_env = "msvc", link(name = "msvcrt"))] -extern "C" {} - -#[panic_handler] -fn panic_handler(_: &core::panic::PanicInfo<'_>) -> ! { - core::intrinsics::abort(); -} - -#[lang = "eh_personality"] -fn eh_personality() {} - -// Required for rustc_codegen_llvm -#[no_mangle] -unsafe extern "C" fn _Unwind_Resume() { - core::intrinsics::unreachable(); -} - -#[start] -fn main(_argc: isize, _argv: *const *const u8) -> isize { - for i in 2..10_000_000 { - black_box((i + 1) % i); - } - - 0 -} - -#[inline(never)] -fn black_box(i: u32) { - if i != 1 { - core::intrinsics::abort(); - } -} diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs index 0b1d83c56309f..ffdc6a7d48491 100644 --- a/compiler/rustc_codegen_cranelift/example/std_example.rs +++ b/compiler/rustc_codegen_cranelift/example/std_example.rs @@ -241,9 +241,10 @@ unsafe fn test_simd() { let (zero0, zero1) = std::mem::transmute::<_, (u64, u64)>(x); assert_eq!((zero0, zero1), (0, 0)); assert_eq!(std::mem::transmute::<_, [u16; 8]>(or), [7, 7, 7, 7, 7, 7, 7, 7]); - assert_eq!(std::mem::transmute::<_, [u16; 8]>(cmp_eq), [ - 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff - ]); + assert_eq!( + std::mem::transmute::<_, [u16; 8]>(cmp_eq), + [0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff] + ); assert_eq!(std::mem::transmute::<_, [u16; 8]>(cmp_lt), [0, 0, 0, 0, 0, 0, 0, 0]); test_mm_slli_si128(); diff --git a/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch b/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch deleted file mode 100644 index 161173d47650f..0000000000000 --- a/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch +++ /dev/null @@ -1,44 +0,0 @@ -From f6befc4bb51d84f5f1cf35938a168c953d421350 Mon Sep 17 00:00:00 2001 -From: bjorn3 -Date: Sun, 24 Nov 2019 15:10:23 +0100 -Subject: [PATCH] [core] Disable not compiling tests - ---- - library/core/tests/Cargo.toml | 8 ++++++++ - library/core/tests/num/flt2dec/mod.rs | 1 - - library/core/tests/num/int_macros.rs | 2 ++ - library/core/tests/num/uint_macros.rs | 2 ++ - library/core/tests/ptr.rs | 2 ++ - library/core/tests/slice.rs | 2 ++ - 6 files changed, 16 insertions(+), 1 deletion(-) - create mode 100644 library/core/tests/Cargo.toml - -diff --git a/Cargo.toml b/Cargo.toml -new file mode 100644 -index 0000000..46fd999 ---- /dev/null -+++ b/Cargo.toml -@@ -0,0 +1,12 @@ -+[package] -+name = "coretests" -+version = "0.0.0" -+edition = "2021" -+ -+[lib] -+name = "coretests" -+path = "lib.rs" -+ -+[dependencies] -+rand = { version = "0.8.5", default-features = false } -+rand_xorshift = { version = "0.3.0", default-features = false } -diff --git a/lib.rs b/lib.rs -index 42a26ae..5ac1042 100644 ---- a/lib.rs -+++ b/lib.rs -@@ -1,3 +1,4 @@ -+#![cfg(test)] - // tidy-alphabetical-start - #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] - #![cfg_attr(test, feature(cfg_match))] --- -2.21.0 (Apple Git-122) diff --git a/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch index 06840624ceffe..4a06dc3f7ef85 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch @@ -10,21 +10,20 @@ Cranelift doesn't support them yet library/core/tests/atomic.rs | 4 --- 4 files changed, 4 insertions(+), 50 deletions(-) -diff --git a/lib.rs b/lib.rs +diff --git a/tests/lib.rs b/tests/lib.rs index 1e336bf..35e6f54 100644 ---- a/lib.rs -+++ b/lib.rs -@@ -2,6 +2,5 @@ - #![cfg(test)] +--- a/tests/lib.rs ++++ b/tests/lib.rs +@@ -2,5 +2,4 @@ // tidy-alphabetical-start -#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![cfg_attr(test, feature(cfg_match))] #![feature(alloc_layout_extra)] #![feature(array_chunks)] -diff --git a/atomic.rs b/atomic.rs +diff --git a/tests/atomic.rs b/tests/atomic.rs index b735957..ea728b6 100644 ---- a/atomic.rs -+++ b/atomic.rs +--- a/tests/atomic.rs ++++ b/tests/atomic.rs @@ -185,10 +185,6 @@ fn atomic_alignment() { assert_eq!(align_of::(), size_of::()); #[cfg(target_has_atomic = "64")] diff --git a/compiler/rustc_codegen_cranelift/patches/0028-coretests-Disable-long-running-tests.patch b/compiler/rustc_codegen_cranelift/patches/0028-coretests-Disable-long-running-tests.patch index b98326c54a67a..f5ae66c0eb13d 100644 --- a/compiler/rustc_codegen_cranelift/patches/0028-coretests-Disable-long-running-tests.patch +++ b/compiler/rustc_codegen_cranelift/patches/0028-coretests-Disable-long-running-tests.patch @@ -7,10 +7,10 @@ Subject: [PATCH] Disable long running tests library/core/tests/slice.rs | 2 ++ 1 file changed, 2 insertions(+) -diff --git a/slice.rs b/slice.rs +diff --git a/tests/slice.rs b/tests/slice.rs index 8402833..84592e0 100644 ---- a/slice.rs -+++ b/slice.rs +--- a/tests/slice.rs ++++ b/tests/slice.rs @@ -1809,6 +1809,7 @@ fn sort_unstable() { } } @@ -36,8 +36,8 @@ index 8402833..84592e0 100644 #[cfg(not(miri))] // unused in Miri macro_rules! empty_max_mut { @@ -2485,6 +2486,7 @@ take_tests! { - (take_mut_oob_max_range_to_inclusive, (..=usize::MAX), None, empty_max_mut!()), - (take_mut_in_bounds_max_range_from, (usize::MAX..), Some(&mut [] as _), empty_max_mut!()), + (split_off_mut_oob_max_range_to_inclusive, (..=usize::MAX), None, empty_max_mut!()), + (split_off_mut_in_bounds_max_range_from, (usize::MAX..), Some(&mut [] as _), empty_max_mut!()), } +*/ diff --git a/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch b/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch index bf07e455a75f5..e3a9512dda9c6 100644 --- a/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch +++ b/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch @@ -16,8 +16,8 @@ index 7165c3e48af..968552ad435 100644 [dependencies] core = { path = "../core" } --compiler_builtins = { version = "=0.1.140", features = ['rustc-dep-of-std'] } -+compiler_builtins = { version = "=0.1.140", features = ['rustc-dep-of-std', 'no-f16-f128'] } +-compiler_builtins = { version = "=0.1.146", features = ['rustc-dep-of-std'] } ++compiler_builtins = { version = "=0.1.146", features = ['rustc-dep-of-std', 'no-f16-f128'] } [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } diff --git a/compiler/rustc_codegen_cranelift/patches/bcryptprimitives.rs b/compiler/rustc_codegen_cranelift/patches/bcryptprimitives.rs deleted file mode 100644 index 4d186485aac1d..0000000000000 --- a/compiler/rustc_codegen_cranelift/patches/bcryptprimitives.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Shim for bcryptprimitives.dll. The Wine version shipped with Ubuntu 22.04 -// doesn't support it yet. Authored by @ChrisDenton - -#![crate_type = "cdylib"] -#![allow(nonstandard_style)] - -#[no_mangle] -pub unsafe extern "system" fn ProcessPrng(mut pbData: *mut u8, mut cbData: usize) -> i32 { - while cbData > 0 { - let size = core::cmp::min(cbData, u32::MAX as usize); - RtlGenRandom(pbData, size as u32); - cbData -= size; - pbData = pbData.add(size); - } - 1 -} - -#[link(name = "advapi32")] -extern "system" { - #[link_name = "SystemFunction036"] - pub fn RtlGenRandom(RandomBuffer: *mut u8, RandomBufferLength: u32) -> u8; -} diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain index 8d935df4d1f2f..8d423319fa977 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain +++ b/compiler/rustc_codegen_cranelift/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2024-12-06" +channel = "nightly-2025-02-07" components = ["rust-src", "rustc-dev", "llvm-tools"] profile = "minimal" diff --git a/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs b/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs index 1e14f41d4a2c2..ebbb687961058 100644 --- a/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs +++ b/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs @@ -16,7 +16,7 @@ fn main() { if let Some(name) = option_env!("BUILTIN_BACKEND") { rustflags.push(format!("-Zcodegen-backend={name}")); } else { - let dylib = sysroot.join(if cfg!(windows) { "bin" } else { "lib" }).join( + let dylib = sysroot.join("lib").join( env::consts::DLL_PREFIX.to_string() + "rustc_codegen_cranelift" + env::consts::DLL_SUFFIX, diff --git a/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs b/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs index a27b9983bf181..528031af82a84 100644 --- a/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs +++ b/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs @@ -11,7 +11,7 @@ fn main() { sysroot = sysroot.parent().unwrap(); } - let cg_clif_dylib_path = sysroot.join(if cfg!(windows) { "bin" } else { "lib" }).join( + let cg_clif_dylib_path = sysroot.join("lib").join( env::consts::DLL_PREFIX.to_string() + "rustc_codegen_cranelift" + env::consts::DLL_SUFFIX, ); diff --git a/compiler/rustc_codegen_cranelift/scripts/rustdoc-clif.rs b/compiler/rustc_codegen_cranelift/scripts/rustdoc-clif.rs index 1cad312bb7918..6ebe060d8bbd1 100644 --- a/compiler/rustc_codegen_cranelift/scripts/rustdoc-clif.rs +++ b/compiler/rustc_codegen_cranelift/scripts/rustdoc-clif.rs @@ -11,7 +11,7 @@ fn main() { sysroot = sysroot.parent().unwrap(); } - let cg_clif_dylib_path = sysroot.join(if cfg!(windows) { "bin" } else { "lib" }).join( + let cg_clif_dylib_path = sysroot.join("lib").join( env::consts::DLL_PREFIX.to_string() + "rustc_codegen_cranelift" + env::consts::DLL_SUFFIX, ); diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index e291ec204649e..55230a0b5988d 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -83,7 +83,6 @@ rm tests/ui/match/match-float.rs # ================== rm tests/ui/codegen/issue-28950.rs # depends on stack size optimizations rm tests/ui/codegen/init-large-type.rs # same -rm tests/ui/issues/issue-40883.rs # same rm -r tests/run-make/fmt-write-bloat/ # tests an optimization rm tests/ui/statics/const_generics.rs # same @@ -91,13 +90,11 @@ rm tests/ui/statics/const_generics.rs # same # ====================== rm tests/incremental/thinlto/cgu_invalidated_when_import_{added,removed}.rs # requires LLVM rm -r tests/run-make/cross-lang-lto # same -rm -r tests/run-make/sepcomp-inlining # same -rm -r tests/run-make/sepcomp-separate # same -rm -r tests/run-make/sepcomp-cci-copies # same rm -r tests/run-make/volatile-intrinsics # same rm -r tests/run-make/llvm-ident # same rm -r tests/run-make/no-builtins-attribute # same rm -r tests/run-make/pgo-gen-no-imp-symbols # same +rm -r tests/run-make/llvm-location-discriminator-limit-dummy-span # same rm tests/ui/abi/stack-protector.rs # requires stack protector support rm -r tests/run-make/emit-stack-sizes # requires support for -Z emit-stack-sizes rm -r tests/run-make/optimization-remarks-dir # remarks are LLVM specific @@ -128,7 +125,12 @@ rm tests/ui/abi/large-byval-align.rs # exceeds implementation limit of Cranelift # should work when using ./x.py test the way it is intended # ============================================================ rm -r tests/run-make/remap-path-prefix-dwarf # requires llvm-dwarfdump +rm -r tests/run-make/strip # same rm -r tests/run-make/compiler-builtins # Expects lib/rustlib/src/rust to contains the standard library source +rm -r tests/run-make/translation # same +rm -r tests/run-make/missing-unstable-trait-bound # This disables support for unstable features, but running cg_clif needs some unstable features +rm -r tests/run-make/const-trait-stable-toolchain # same +rm -r tests/run-make/incr-add-rust-src-component # genuine bugs # ============ @@ -152,8 +154,6 @@ cp $(../dist/rustc-clif --print target-libdir)/libstd-*.so ../dist/lib/ # prevent $(RUSTDOC) from picking up the sysroot built by x.py. It conflicts with the one used by # rustdoc-clif -# FIXME remove the bootstrap changes once it is no longer necessary to revert rust-lang/rust#130642 -# to avoid building rustc when testing stage0 run-make. cat < Self { +@@ -34,7 +34,6 @@ pub fn bare() -> Self { #[track_caller] pub fn new() -> Self { let mut cmd = setup_common(); -- let target_rpath_dir = env_var_os("TARGET_RPATH_DIR"); -- cmd.arg(format!("-L{}", target_rpath_dir.to_string_lossy())); +- cmd.arg("-L").arg(env_var_os("TARGET_RPATH_DIR")); Self { cmd } } @@ -193,8 +192,22 @@ index e7ae773ffa1d3..04bc2d7787da7 100644 // Provide necessary library search paths for rustc. .env(dylib_env_var(), &env::join_paths(host_dylib_search_paths).unwrap()); +diff --git a/tests/run-make/linker-warning/rmake.rs b/tests/run-make/linker-warning/rmake.rs +index 30387af428c..f7895b12961 100644 +--- a/tests/run-make/linker-warning/rmake.rs ++++ b/tests/run-make/linker-warning/rmake.rs +@@ -57,7 +57,8 @@ fn main() { + .actual_text("(linker error)", out.stderr()) +- .normalize(r#"/rustc[^/]*/"#, "/rustc/") ++ .normalize(r#"/tmp/rustc[^/]*/"#, "/tmp/rustc/") ++ .normalize("libpanic_abort", "libpanic_unwind") + .normalize( + regex::escape(run_make_support::build_root().to_str().unwrap()), + "/build-root", + ) + .run(); EOF echo "[TEST] rustc test suite" -COMPILETEST_FORCE_STAGE0=1 ./x.py test --stage 0 --test-args=--nocapture tests/{codegen-units,run-make,ui,incremental} +COMPILETEST_FORCE_STAGE0=1 ./x.py test --stage 0 --test-args=--no-capture tests/{codegen-units,run-make,ui,incremental} popd diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 2466bfe60c7ab..756a2226753f6 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -65,7 +65,7 @@ pub(crate) fn conv_to_call_conv(sess: &Session, c: Conv, default_call_conv: Call sess.dcx().fatal("C-cmse-nonsecure-entry call conv is not yet implemented"); } - Conv::Msp430Intr | Conv::PtxKernel | Conv::AvrInterrupt | Conv::AvrNonBlockingInterrupt => { + Conv::Msp430Intr | Conv::GpuKernel | Conv::AvrInterrupt | Conv::AvrNonBlockingInterrupt => { unreachable!("tried to use {c:?} call conv which only exists on an unsupported target"); } } @@ -122,7 +122,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { &mut self, name: &str, params: Vec, - returns: Vec, + mut returns: Vec, args: &[Value], ) -> Cow<'_, [Value]> { // Pass i128 arguments by-ref on Windows. @@ -146,15 +146,19 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { (params, args.into()) }; - // Return i128 using a return area pointer on Windows and s390x. - let adjust_ret_param = - if self.tcx.sess.target.is_like_windows || self.tcx.sess.target.arch == "s390x" { - returns.len() == 1 && returns[0].value_type == types::I128 - } else { - false - }; + let ret_single_i128 = returns.len() == 1 && returns[0].value_type == types::I128; + if ret_single_i128 && self.tcx.sess.target.is_like_windows { + // Return i128 using the vector ABI on Windows + returns[0].value_type = types::I64X2; + + let ret = self.lib_call_unadjusted(name, params, returns, &args)[0]; - if adjust_ret_param { + // FIXME(bytecodealliance/wasmtime#6104) use bitcast instead of store to get from i64x2 to i128 + let ret_ptr = self.create_stack_slot(16, 16); + ret_ptr.store(self, ret, MemFlags::trusted()); + Cow::Owned(vec![ret_ptr.load(self, types::I128, MemFlags::trusted())]) + } else if ret_single_i128 && self.tcx.sess.target.arch == "s390x" { + // Return i128 using a return area pointer on s390x. let mut params = params; let mut args = args.to_vec(); diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 956a024fa4dc3..125a9201831ca 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -417,6 +417,16 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { Some(source_info.span), ); } + AssertKind::NullPointerDereference => { + let location = fx.get_caller_location(source_info).load_scalar(fx); + + codegen_panic_inner( + fx, + rustc_hir::LangItem::PanicNullPointerDereference, + &[location], + Some(source_info.span), + ) + } _ => { let location = fx.get_caller_location(source_info).load_scalar(fx); @@ -828,6 +838,12 @@ fn codegen_stmt<'tcx>( fx.bcx.ins().nop(); } } + Rvalue::Len(place) => { + let place = codegen_place(fx, place); + let usize_layout = fx.layout_of(fx.tcx.types.usize); + let len = codegen_array_len(fx, place); + lval.write_cvalue(fx, CValue::by_val(len, usize_layout)); + } Rvalue::ShallowInitBox(ref operand, content_ty) => { let content_ty = fx.monomorphize(content_ty); let box_layout = fx.layout_of(Ty::new_box(fx.tcx, content_ty)); @@ -852,7 +868,16 @@ fn codegen_stmt<'tcx>( NullOp::UbChecks => { let val = fx.tcx.sess.ub_checks(); let val = CValue::by_val( - fx.bcx.ins().iconst(types::I8, i64::try_from(val).unwrap()), + fx.bcx.ins().iconst(types::I8, i64::from(val)), + fx.layout_of(fx.tcx.types.bool), + ); + lval.write_cvalue(fx, val); + return; + } + NullOp::ContractChecks => { + let val = fx.tcx.sess.contract_checks(); + let val = CValue::by_val( + fx.bcx.ins().iconst(types::I8, i64::from(val)), fx.layout_of(fx.tcx.types.bool), ); lval.write_cvalue(fx, val); @@ -875,8 +900,8 @@ fn codegen_stmt<'tcx>( }; let data = codegen_operand(fx, data); let meta = codegen_operand(fx, meta); - assert!(data.layout().ty.is_unsafe_ptr()); - assert!(layout.ty.is_unsafe_ptr()); + assert!(data.layout().ty.is_raw_ptr()); + assert!(layout.ty.is_raw_ptr()); let ptr_val = if meta.layout().is_zst() { data.cast_pointer_to(layout) } else { @@ -909,6 +934,10 @@ fn codegen_stmt<'tcx>( } crate::discriminant::codegen_set_discriminant(fx, lval, variant_index); } + Rvalue::WrapUnsafeBinder(ref operand, _to_ty) => { + let operand = codegen_operand(fx, operand); + lval.write_cvalue_transmute(fx, operand); + } } } StatementKind::StorageLive(_) @@ -977,7 +1006,9 @@ pub(crate) fn codegen_place<'tcx>( cplace = cplace.place_deref(fx); } PlaceElem::OpaqueCast(ty) => bug!("encountered OpaqueCast({ty}) in codegen"), - PlaceElem::Subtype(ty) => cplace = cplace.place_transmute_type(fx, fx.monomorphize(ty)), + PlaceElem::Subtype(ty) | PlaceElem::UnwrapUnsafeBinder(ty) => { + cplace = cplace.place_transmute_type(fx, fx.monomorphize(ty)); + } PlaceElem::Field(field, _ty) => { cplace = cplace.place_field(fx, field); } diff --git a/compiler/rustc_codegen_cranelift/src/cast.rs b/compiler/rustc_codegen_cranelift/src/cast.rs index 0b5cb1547fc69..e2346324232fc 100644 --- a/compiler/rustc_codegen_cranelift/src/cast.rs +++ b/compiler/rustc_codegen_cranelift/src/cast.rs @@ -96,25 +96,12 @@ pub(crate) fn clif_int_or_float_cast( }, ); - if fx.tcx.sess.target.is_like_windows { - let ret = fx.lib_call( - &name, - vec![AbiParam::new(from_ty)], - vec![AbiParam::new(types::I64X2)], - &[from], - )[0]; - // FIXME(bytecodealliance/wasmtime#6104) use bitcast instead of store to get from i64x2 to i128 - let ret_ptr = fx.create_stack_slot(16, 16); - ret_ptr.store(fx, ret, MemFlags::trusted()); - ret_ptr.load(fx, types::I128, MemFlags::trusted()) - } else { - fx.lib_call( - &name, - vec![AbiParam::new(from_ty)], - vec![AbiParam::new(types::I128)], - &[from], - )[0] - } + fx.lib_call( + &name, + vec![AbiParam::new(from_ty)], + vec![AbiParam::new(types::I128)], + &[from], + )[0] } else if to_ty == types::I8 || to_ty == types::I16 { // FIXME implement fcvt_to_*int_sat.i8/i16 let val = if to_signed { diff --git a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs index 734574338d049..df5a79086fa3e 100644 --- a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs +++ b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs @@ -33,28 +33,14 @@ pub(crate) fn maybe_codegen<'tcx>( (BinOp::Rem, true) => "__modti3", _ => unreachable!(), }; - if fx.tcx.sess.target.is_like_windows { - let args = [lhs.load_scalar(fx), rhs.load_scalar(fx)]; - let ret = fx.lib_call( - name, - vec![AbiParam::new(types::I128), AbiParam::new(types::I128)], - vec![AbiParam::new(types::I64X2)], - &args, - )[0]; - // FIXME(bytecodealliance/wasmtime#6104) use bitcast instead of store to get from i64x2 to i128 - let ret_place = CPlace::new_stack_slot(fx, lhs.layout()); - ret_place.to_ptr().store(fx, ret, MemFlags::trusted()); - Some(ret_place.to_cvalue(fx)) - } else { - let args = [lhs.load_scalar(fx), rhs.load_scalar(fx)]; - let ret_val = fx.lib_call( - name, - vec![AbiParam::new(types::I128), AbiParam::new(types::I128)], - vec![AbiParam::new(types::I128)], - &args, - )[0]; - Some(CValue::by_val(ret_val, lhs.layout())) - } + let args = [lhs.load_scalar(fx), rhs.load_scalar(fx)]; + let ret_val = fx.lib_call( + name, + vec![AbiParam::new(types::I128), AbiParam::new(types::I128)], + vec![AbiParam::new(types::I128)], + &args, + )[0]; + Some(CValue::by_val(ret_val, lhs.layout())) } BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne | BinOp::Cmp => None, BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => None, @@ -62,9 +48,8 @@ pub(crate) fn maybe_codegen<'tcx>( } } -pub(crate) fn maybe_codegen_checked<'tcx>( +pub(crate) fn maybe_codegen_mul_checked<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, - bin_op: BinOp, lhs: CValue<'tcx>, rhs: CValue<'tcx>, ) -> Option> { @@ -77,33 +62,22 @@ pub(crate) fn maybe_codegen_checked<'tcx>( } let is_signed = type_sign(lhs.layout().ty); - - match bin_op { - BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor => unreachable!(), - BinOp::Add | BinOp::Sub => None, - BinOp::Mul => { - let out_ty = Ty::new_tup(fx.tcx, &[lhs.layout().ty, fx.tcx.types.bool]); - let out_place = CPlace::new_stack_slot(fx, fx.layout_of(out_ty)); - let param_types = vec![ - AbiParam::special(fx.pointer_type, ArgumentPurpose::StructReturn), - AbiParam::new(types::I128), - AbiParam::new(types::I128), - ]; - let args = [out_place.to_ptr().get_addr(fx), lhs.load_scalar(fx), rhs.load_scalar(fx)]; - fx.lib_call( - if is_signed { "__rust_i128_mulo" } else { "__rust_u128_mulo" }, - param_types, - vec![], - &args, - ); - Some(out_place.to_cvalue(fx)) - } - BinOp::AddUnchecked | BinOp::SubUnchecked | BinOp::MulUnchecked => unreachable!(), - BinOp::AddWithOverflow | BinOp::SubWithOverflow | BinOp::MulWithOverflow => unreachable!(), - BinOp::Offset => unreachable!("offset should only be used on pointers, not 128bit ints"), - BinOp::Div | BinOp::Rem => unreachable!(), - BinOp::Cmp => unreachable!(), - BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => unreachable!(), - BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => unreachable!(), - } + let oflow_out_place = CPlace::new_stack_slot(fx, fx.layout_of(fx.tcx.types.i32)); + let param_types = vec![ + AbiParam::new(types::I128), + AbiParam::new(types::I128), + AbiParam::special(fx.pointer_type, ArgumentPurpose::Normal), + ]; + let args = [lhs.load_scalar(fx), rhs.load_scalar(fx), oflow_out_place.to_ptr().get_addr(fx)]; + let ret = fx.lib_call( + if is_signed { "__rust_i128_mulo" } else { "__rust_u128_mulo" }, + param_types, + vec![AbiParam::new(types::I128)], + &args, + ); + let mul = ret[0]; + let oflow = oflow_out_place.to_cvalue(fx).load_scalar(fx); + let oflow = clif_intcast(fx, oflow, types::I8, false); + let layout = fx.layout_of(Ty::new_tup(fx.tcx, &[lhs.layout().ty, fx.tcx.types.bool])); + Some(CValue::by_val_pair(mul, oflow, layout)) } diff --git a/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs b/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs index f8e3a034421da..bf16e81a06f9f 100644 --- a/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs +++ b/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs @@ -43,7 +43,8 @@ builtin_functions! { fn __divti3(n: i128, d: i128) -> i128; fn __umodti3(n: u128, d: u128) -> u128; fn __modti3(n: i128, d: i128) -> i128; - fn __rust_u128_mulo(a: u128, b: u128) -> (u128, bool); + fn __rust_u128_mulo(a: u128, b: u128, oflow: &mut i32) -> u128; + fn __rust_i128_mulo(a: i128, b: i128, oflow: &mut i32) -> i128; // floats fn __floattisf(i: i128) -> f32; diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 3e7b81a96b68c..425b2adf32a37 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -6,7 +6,7 @@ use cranelift_module::*; use rustc_data_structures::fx::FxHashSet; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::interpret::{AllocId, GlobalAlloc, Scalar, read_target_uint}; -use rustc_middle::ty::{Binder, ExistentialTraitRef, ScalarInt}; +use rustc_middle::ty::{ExistentialTraitRef, ScalarInt}; use crate::prelude::*; @@ -167,7 +167,9 @@ pub(crate) fn codegen_const_value<'tcx>( &mut fx.constants_cx, fx.module, ty, - dyn_ty.principal(), + dyn_ty.principal().map(|principal| { + fx.tcx.instantiate_bound_regions_with_erased(principal) + }), ); let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func); @@ -243,7 +245,7 @@ pub(crate) fn data_id_for_vtable<'tcx>( cx: &mut ConstantCx, module: &mut dyn Module, ty: Ty<'tcx>, - trait_ref: Option>>, + trait_ref: Option>, ) -> DataId { let alloc_id = tcx.vtable_allocation((ty, trait_ref)); data_id_for_alloc_id(cx, module, alloc_id, Mutability::Not) @@ -460,9 +462,15 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant GlobalAlloc::Memory(target_alloc) => { data_id_for_alloc_id(cx, module, alloc_id, target_alloc.inner().mutability) } - GlobalAlloc::VTable(ty, dyn_ty) => { - data_id_for_vtable(tcx, cx, module, ty, dyn_ty.principal()) - } + GlobalAlloc::VTable(ty, dyn_ty) => data_id_for_vtable( + tcx, + cx, + module, + ty, + dyn_ty + .principal() + .map(|principal| tcx.instantiate_bound_regions_with_erased(principal)), + ), GlobalAlloc::Static(def_id) => { if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) { diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/object.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/object.rs index 048a388731fb0..1c6e471cc870f 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/object.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/object.rs @@ -73,16 +73,19 @@ impl WriteDebugInfo for ObjectProduct { } }; self.object - .add_relocation(from.0, Relocation { - offset: u64::from(reloc.offset), - symbol, - flags: RelocationFlags::Generic { - kind: reloc.kind, - encoding: RelocationEncoding::Generic, - size: reloc.size * 8, + .add_relocation( + from.0, + Relocation { + offset: u64::from(reloc.offset), + symbol, + flags: RelocationFlags::Generic { + kind: reloc.kind, + encoding: RelocationEncoding::Generic, + size: reloc.size * 8, + }, + addend: i64::try_from(symbol_offset).unwrap() + reloc.addend, }, - addend: i64::try_from(symbol_offset).unwrap() + reloc.addend, - }) + ) .unwrap(); } } diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 4fc30b69123dd..a52b18573b152 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -333,10 +333,17 @@ fn make_module(sess: &Session, name: String) -> UnwindModule { let mut builder = ObjectBuilder::new(isa, name + ".o", cranelift_module::default_libcall_names()).unwrap(); - // Unlike cg_llvm, cg_clif defaults to disabling -Zfunction-sections. For cg_llvm binary size - // is important, while cg_clif cares more about compilation times. Enabling -Zfunction-sections - // can easily double the amount of time necessary to perform linking. - builder.per_function_section(sess.opts.unstable_opts.function_sections.unwrap_or(false)); + + // Disable function sections by default on MSVC as it causes significant slowdowns with link.exe. + // Maybe link.exe has exponential behavior when there are many sections with the same name? Also + // explicitly disable it on MinGW as rustc already disables it by default on MinGW and as such + // isn't tested. If rustc enables it in the future on MinGW, we can re-enable it too once it has + // been on MinGW. + let default_function_sections = sess.target.function_sections && !sess.target.is_like_windows; + builder.per_function_section( + sess.opts.unstable_opts.function_sections.unwrap_or(default_function_sections), + ); + UnwindModule::new(ObjectModule::new(builder), true) } @@ -669,7 +676,7 @@ pub(crate) fn run_aot( .to_owned(); let cgus = if tcx.sess.opts.output_types.should_codegen() { - tcx.collect_and_partition_mono_items(()).1 + tcx.collect_and_partition_mono_items(()).codegen_units } else { // If only `--emit metadata` is used, we shouldn't perform any codegen. // Also `tcx.collect_and_partition_mono_items` may panic in that case. @@ -685,7 +692,7 @@ pub(crate) fn run_aot( if tcx.dep_graph.is_fully_enabled() { for cgu in cgus { - tcx.ensure().codegen_unit(cgu.name()); + tcx.ensure_ok().codegen_unit(cgu.name()); } } diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index eaab3362c7e83..2e713171ae064 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -46,7 +46,7 @@ unsafe impl Send for UnsafeMessage {} impl UnsafeMessage { /// Send the message. - fn send(self) -> Result<(), mpsc::SendError> { + fn send(self) { thread_local! { /// The Sender owned by the local thread static LOCAL_MESSAGE_SENDER: mpsc::Sender = @@ -55,7 +55,9 @@ impl UnsafeMessage { .lock().unwrap() .clone(); } - LOCAL_MESSAGE_SENDER.with(|sender| sender.send(self)) + LOCAL_MESSAGE_SENDER.with(|sender| { + sender.send(self).expect("rustc thread hung up before lazy JIT request was sent") + }) } } @@ -90,7 +92,7 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, codegen_mode: CodegenMode, jit_args: Vec< create_jit_module(tcx, matches!(codegen_mode, CodegenMode::JitLazy)); let mut cached_context = Context::new(); - let (_, cgus) = tcx.collect_and_partition_mono_items(()); + let cgus = tcx.collect_and_partition_mono_items(()).codegen_units; let mono_items = cgus .iter() .map(|cgu| cgu.items_in_deterministic_order(tcx).into_iter()) @@ -231,9 +233,7 @@ extern "C" fn clif_jit_fn( ) -> *const u8 { // send the JIT request to the rustc thread, with a channel for the response let (tx, rx) = mpsc::channel(); - UnsafeMessage::JitFn { instance_ptr, trampoline_ptr, tx } - .send() - .expect("rustc thread hung up before lazy JIT request was sent"); + UnsafeMessage::JitFn { instance_ptr, trampoline_ptr, tx }.send(); // block on JIT compilation result rx.recv().expect("rustc thread hung up before responding to sent lazy JIT request") @@ -287,7 +287,7 @@ fn dep_symbol_lookup_fn( let mut dylib_paths = Vec::new(); - let data = &crate_info.dependency_formats[&rustc_session::config::CrateType::Executable].1; + let data = &crate_info.dependency_formats[&rustc_session::config::CrateType::Executable]; // `used_crates` is in reverse postorder in terms of dependencies. Reverse the order here to // get a postorder which ensures that all dependencies of a dylib are loaded before the dylib // itself. This helps the dynamic linker to find dylibs not in the regular dynamic library @@ -342,11 +342,15 @@ fn codegen_shim<'tcx>( let instance_ptr = Box::into_raw(Box::new(inst)); let jit_fn = module - .declare_function("__clif_jit_fn", Linkage::Import, &Signature { - call_conv: module.target_config().default_call_conv, - params: vec![AbiParam::new(pointer_type), AbiParam::new(pointer_type)], - returns: vec![AbiParam::new(pointer_type)], - }) + .declare_function( + "__clif_jit_fn", + Linkage::Import, + &Signature { + call_conv: module.target_config().default_call_conv, + params: vec![AbiParam::new(pointer_type), AbiParam::new(pointer_type)], + returns: vec![AbiParam::new(pointer_type)], + }, + ) .unwrap(); let context = cached_context; diff --git a/compiler/rustc_codegen_cranelift/src/driver/mod.rs b/compiler/rustc_codegen_cranelift/src/driver/mod.rs index fb0eed07c1971..ffd47cace3807 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/mod.rs @@ -73,12 +73,14 @@ impl Drop for TimingGuard { impl cranelift_codegen::timing::Profiler for MeasuremeProfiler { fn start_pass(&self, pass: cranelift_codegen::timing::Pass) -> Box { - let mut timing_guard = - TimingGuard { profiler: std::mem::ManuallyDrop::new(self.0.clone()), inner: None }; + let mut timing_guard = Box::new(TimingGuard { + profiler: std::mem::ManuallyDrop::new(self.0.clone()), + inner: None, + }); timing_guard.inner = Some( unsafe { &*(&*timing_guard.profiler as &SelfProfilerRef as *const SelfProfilerRef) } .generic_activity(pass.description()), ); - Box::new(timing_guard) + timing_guard } } diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs index 33726056cc1cb..f2b0ec977c63d 100644 --- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -136,7 +136,7 @@ pub(crate) fn codegen_inline_asm_terminator<'tcx>( fx.bcx.ins().jump(destination_block, &[]); } None => { - fx.bcx.ins().trap(TrapCode::user(0 /* unreachable */).unwrap()); + fx.bcx.ins().trap(TrapCode::user(1 /* unreachable */).unwrap()); } } } @@ -875,11 +875,15 @@ fn call_inline_asm<'tcx>( let inline_asm_func = fx .module - .declare_function(asm_name, Linkage::Import, &Signature { - call_conv: CallConv::SystemV, - params: vec![AbiParam::new(fx.pointer_type)], - returns: vec![], - }) + .declare_function( + asm_name, + Linkage::Import, + &Signature { + call_conv: CallConv::SystemV, + params: vec![AbiParam::new(fx.pointer_type)], + returns: vec![], + }, + ) .unwrap(); let inline_asm_func = fx.module.declare_func_in_func(inline_asm_func, fx.bcx.func); if fx.clif_comments.enabled() { diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs index 39f6763d9f2cb..4c59c81296bad 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_aarch64.rs @@ -17,6 +17,14 @@ pub(crate) fn codegen_aarch64_llvm_intrinsic_call<'tcx>( fx.bcx.ins().fence(); } + "llvm.aarch64.neon.ld1x4.v16i8.p0i8" => { + intrinsic_args!(fx, args => (ptr); intrinsic); + + let ptr = ptr.load_scalar(fx); + let val = CPlace::for_ptr(Pointer::new(ptr), ret.layout()).to_cvalue(fx); + ret.write_cvalue(fx, val); + } + _ if intrinsic.starts_with("llvm.aarch64.neon.abs.v") => { intrinsic_args!(fx, args => (a); intrinsic); @@ -115,6 +123,22 @@ pub(crate) fn codegen_aarch64_llvm_intrinsic_call<'tcx>( ); } + "llvm.aarch64.neon.uaddlv.i32.v16i8" => { + intrinsic_args!(fx, args => (v); intrinsic); + + let mut res_val = fx.bcx.ins().iconst(types::I16, 0); + for lane_idx in 0..16 { + let lane = v.value_lane(fx, lane_idx).load_scalar(fx); + let lane = fx.bcx.ins().uextend(types::I16, lane); + res_val = fx.bcx.ins().iadd(res_val, lane); + } + let res = CValue::by_val( + fx.bcx.ins().uextend(types::I32, res_val), + fx.layout_of(fx.tcx.types.u32), + ); + ret.write_cvalue(fx, res); + } + _ if intrinsic.starts_with("llvm.aarch64.neon.faddv.f32.v") => { intrinsic_args!(fx, args => (v); intrinsic); diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 2e5813556aafa..26f14532b458e 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -1,4 +1,5 @@ //! Codegen of intrinsics. This includes `extern "rust-intrinsic"`, +//! functions marked with the `#[rustc_intrinsic]` attribute //! and LLVM intrinsics that have symbol names starting with `llvm.`. macro_rules! intrinsic_args { diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index e0ebe30752afa..fcccda62355c7 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -129,12 +129,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( return; } - let idx = generic_args[2] - .expect_const() - .try_to_valtree() - .expect("expected monomorphic const in codegen") - .0 - .unwrap_branch(); + let idx = generic_args[2].expect_const().to_value().valtree.unwrap_branch(); assert_eq!(x.layout(), y.layout()); let layout = x.layout(); @@ -563,9 +558,12 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( (sym::simd_round, types::F64) => "round", _ => unreachable!("{:?}", intrinsic), }; - fx.lib_call(name, vec![AbiParam::new(lane_ty)], vec![AbiParam::new(lane_ty)], &[ - lane, - ])[0] + fx.lib_call( + name, + vec![AbiParam::new(lane_ty)], + vec![AbiParam::new(lane_ty)], + &[lane], + )[0] }); } @@ -1136,7 +1134,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( _ => { fx.tcx.dcx().span_err(span, format!("Unknown SIMD intrinsic {}", intrinsic)); // Prevent verifier error - fx.bcx.ins().trap(TrapCode::user(0 /* unreachable */).unwrap()); + fx.bcx.ins().trap(TrapCode::user(1 /* unreachable */).unwrap()); return; } } diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index c38ef82e5b80c..a3f4374487578 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -27,6 +27,8 @@ extern crate rustc_metadata; extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; +#[macro_use] +extern crate tracing; // This prevents duplicating functions and statics that are already part of the host rustc process. #[allow(unused_extern_crates)] @@ -181,8 +183,8 @@ impl CodegenBackend for CraneliftCodegenBackend { ) -> Vec { // FIXME return the actually used target features. this is necessary for #[cfg(target_feature)] if sess.target.arch == "x86_64" && sess.target.os != "none" { - // x86_64 mandates SSE2 support - vec![sym::fsxr, sym::sse, sym::sse2] + // x86_64 mandates SSE2 support and rustc requires the x87 feature to be enabled + vec![sym::fsxr, sym::sse, sym::sse2, Symbol::intern("x87")] } else if sess.target.arch == "aarch64" { match &*sess.target.os { "none" => vec![], @@ -207,7 +209,7 @@ impl CodegenBackend for CraneliftCodegenBackend { metadata: EncodedMetadata, need_metadata_module: bool, ) -> Box { - tcx.dcx().abort_if_errors(); + info!("codegen crate {}", tcx.crate_name(LOCAL_CRATE)); let config = self.config.clone().unwrap_or_else(|| { BackendConfig::from_opts(&tcx.sess.opts.cg.llvm_args) .unwrap_or_else(|err| tcx.sess.dcx().fatal(err)) @@ -287,7 +289,7 @@ fn build_isa(sess: &Session) -> Arc { flags_builder.set("opt_level", "none").unwrap(); } OptLevel::Less - | OptLevel::Default + | OptLevel::More | OptLevel::Size | OptLevel::SizeMin | OptLevel::Aggressive => { diff --git a/compiler/rustc_codegen_cranelift/src/main_shim.rs b/compiler/rustc_codegen_cranelift/src/main_shim.rs index e6bf0d5b47e4c..6d5df2b00437b 100644 --- a/compiler/rustc_codegen_cranelift/src/main_shim.rs +++ b/compiler/rustc_codegen_cranelift/src/main_shim.rs @@ -1,7 +1,7 @@ use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use rustc_hir::LangItem; use rustc_middle::ty::{AssocKind, GenericArg}; -use rustc_session::config::{EntryFnType, sigpipe}; +use rustc_session::config::EntryFnType; use rustc_span::{DUMMY_SP, Ident}; use crate::prelude::*; @@ -14,11 +14,13 @@ pub(crate) fn maybe_create_entry_wrapper( is_jit: bool, is_primary_cgu: bool, ) { - let (main_def_id, (is_main_fn, sigpipe)) = match tcx.entry_fn(()) { - Some((def_id, entry_ty)) => (def_id, match entry_ty { - EntryFnType::Main { sigpipe } => (true, sigpipe), - EntryFnType::Start => (false, sigpipe::DEFAULT), - }), + let (main_def_id, sigpipe) = match tcx.entry_fn(()) { + Some((def_id, entry_ty)) => ( + def_id, + match entry_ty { + EntryFnType::Main { sigpipe } => sigpipe, + }, + ), None => return, }; @@ -31,14 +33,13 @@ pub(crate) fn maybe_create_entry_wrapper( return; } - create_entry_fn(tcx, module, main_def_id, is_jit, is_main_fn, sigpipe); + create_entry_fn(tcx, module, main_def_id, is_jit, sigpipe); fn create_entry_fn( tcx: TyCtxt<'_>, m: &mut dyn Module, rust_main_def_id: DefId, ignore_lang_start_wrapper: bool, - is_main_fn: bool, sigpipe: u8, ) { let main_ret_ty = tcx.fn_sig(rust_main_def_id).no_bound_vars().unwrap().output(); @@ -94,8 +95,8 @@ pub(crate) fn maybe_create_entry_wrapper( let main_func_ref = m.declare_func_in_func(main_func_id, &mut bcx.func); - let result = if is_main_fn && ignore_lang_start_wrapper { - // regular main fn, but ignoring #[lang = "start"] as we are running in the jit + let result = if ignore_lang_start_wrapper { + // ignoring #[lang = "start"] as we are running in the jit // FIXME set program arguments somehow let call_inst = bcx.ins().call(main_func_ref, &[]); let call_results = bcx.func.dfg.inst_results(call_inst).to_owned(); @@ -133,7 +134,8 @@ pub(crate) fn maybe_create_entry_wrapper( types::I64 => bcx.ins().sextend(types::I64, res), _ => unimplemented!("16bit systems are not yet supported"), } - } else if is_main_fn { + } else { + // Regular main fn invoked via start lang item. let start_def_id = tcx.require_lang_item(LangItem::Start, None); let start_instance = Instance::expect_resolve( tcx, @@ -150,10 +152,6 @@ pub(crate) fn maybe_create_entry_wrapper( let call_inst = bcx.ins().call(func_ref, &[main_val, arg_argc, arg_argv, arg_sigpipe]); bcx.inst_results(call_inst)[0] - } else { - // using user-defined start fn - let call_inst = bcx.ins().call(main_func_ref, &[arg_argc, arg_argv]); - bcx.inst_results(call_inst)[0] }; bcx.ins().return_(&[result]); diff --git a/compiler/rustc_codegen_cranelift/src/num.rs b/compiler/rustc_codegen_cranelift/src/num.rs index fb18f45d7dcad..f44e2459a784d 100644 --- a/compiler/rustc_codegen_cranelift/src/num.rs +++ b/compiler/rustc_codegen_cranelift/src/num.rs @@ -2,10 +2,10 @@ use crate::prelude::*; -pub(crate) fn bin_op_to_intcc(bin_op: BinOp, signed: bool) -> Option { +pub(crate) fn bin_op_to_intcc(bin_op: BinOp, signed: bool) -> IntCC { use BinOp::*; use IntCC::*; - Some(match bin_op { + match bin_op { Eq => Equal, Lt => { if signed { @@ -36,8 +36,8 @@ pub(crate) fn bin_op_to_intcc(bin_op: BinOp, signed: bool) -> Option { UnsignedGreaterThan } } - _ => return None, - }) + _ => unreachable!(), + } } fn codegen_three_way_compare<'tcx>( @@ -48,8 +48,8 @@ fn codegen_three_way_compare<'tcx>( ) -> CValue<'tcx> { // This emits `(lhs > rhs) - (lhs < rhs)`, which is cranelift's preferred form per // - let gt_cc = crate::num::bin_op_to_intcc(BinOp::Gt, signed).unwrap(); - let lt_cc = crate::num::bin_op_to_intcc(BinOp::Lt, signed).unwrap(); + let gt_cc = crate::num::bin_op_to_intcc(BinOp::Gt, signed); + let lt_cc = crate::num::bin_op_to_intcc(BinOp::Lt, signed); let gt = fx.bcx.ins().icmp(gt_cc, lhs, rhs); let lt = fx.bcx.ins().icmp(lt_cc, lhs, rhs); let val = fx.bcx.ins().isub(gt, lt); @@ -63,11 +63,7 @@ fn codegen_compare_bin_op<'tcx>( lhs: Value, rhs: Value, ) -> CValue<'tcx> { - if bin_op == BinOp::Cmp { - return codegen_three_way_compare(fx, signed, lhs, rhs); - } - - let intcc = crate::num::bin_op_to_intcc(bin_op, signed).unwrap(); + let intcc = crate::num::bin_op_to_intcc(bin_op, signed); let val = fx.bcx.ins().icmp(intcc, lhs, rhs); CValue::by_val(val, fx.layout_of(fx.tcx.types.bool)) } @@ -79,7 +75,7 @@ pub(crate) fn codegen_binop<'tcx>( in_rhs: CValue<'tcx>, ) -> CValue<'tcx> { match bin_op { - BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt | BinOp::Cmp => { + BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => { match in_lhs.layout().ty.kind() { ty::Bool | ty::Uint(_) | ty::Int(_) | ty::Char => { let signed = type_sign(in_lhs.layout().ty); @@ -91,6 +87,16 @@ pub(crate) fn codegen_binop<'tcx>( _ => {} } } + BinOp::Cmp => match in_lhs.layout().ty.kind() { + ty::Bool | ty::Uint(_) | ty::Int(_) | ty::Char => { + let signed = type_sign(in_lhs.layout().ty); + let lhs = in_lhs.load_scalar(fx); + let rhs = in_rhs.load_scalar(fx); + + return codegen_three_way_compare(fx, signed, lhs, rhs); + } + _ => {} + }, _ => {} } @@ -200,10 +206,6 @@ pub(crate) fn codegen_checked_int_binop<'tcx>( let lhs = in_lhs.load_scalar(fx); let rhs = in_rhs.load_scalar(fx); - if let Some(res) = crate::codegen_i128::maybe_codegen_checked(fx, bin_op, in_lhs, in_rhs) { - return res; - } - let signed = type_sign(in_lhs.layout().ty); let (res, has_overflow) = match bin_op { @@ -236,6 +238,10 @@ pub(crate) fn codegen_checked_int_binop<'tcx>( (val, has_overflow) } BinOp::Mul => { + if let Some(res) = crate::codegen_i128::maybe_codegen_mul_checked(fx, in_lhs, in_rhs) { + return res; + } + let ty = fx.bcx.func.dfg.value_type(lhs); match ty { types::I8 | types::I16 | types::I32 if !signed => { @@ -357,14 +363,12 @@ pub(crate) fn codegen_float_binop<'tcx>( _ => bug!(), }; - let ret_val = fx.lib_call( + fx.lib_call( name, vec![AbiParam::new(ty), AbiParam::new(ty)], vec![AbiParam::new(ty)], &[lhs, rhs], - )[0]; - - return CValue::by_val(ret_val, in_lhs.layout()); + )[0] } BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => { let fltcc = match bin_op { @@ -431,13 +435,9 @@ pub(crate) fn codegen_ptr_binop<'tcx>( BinOp::Lt | BinOp::Le | BinOp::Ge | BinOp::Gt => { let ptr_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_ptr, rhs_ptr); - let ptr_cmp = - fx.bcx.ins().icmp(bin_op_to_intcc(bin_op, false).unwrap(), lhs_ptr, rhs_ptr); - let extra_cmp = fx.bcx.ins().icmp( - bin_op_to_intcc(bin_op, false).unwrap(), - lhs_extra, - rhs_extra, - ); + let ptr_cmp = fx.bcx.ins().icmp(bin_op_to_intcc(bin_op, false), lhs_ptr, rhs_ptr); + let extra_cmp = + fx.bcx.ins().icmp(bin_op_to_intcc(bin_op, false), lhs_extra, rhs_extra); fx.bcx.ins().select(ptr_eq, extra_cmp, ptr_cmp) } diff --git a/compiler/rustc_codegen_cranelift/src/trap.rs b/compiler/rustc_codegen_cranelift/src/trap.rs index 9ef1a523d6d69..ac3f58ee1ee6e 100644 --- a/compiler/rustc_codegen_cranelift/src/trap.rs +++ b/compiler/rustc_codegen_cranelift/src/trap.rs @@ -5,11 +5,15 @@ use crate::prelude::*; fn codegen_print(fx: &mut FunctionCx<'_, '_, '_>, msg: &str) { let puts = fx .module - .declare_function("puts", Linkage::Import, &Signature { - call_conv: fx.target_config.default_call_conv, - params: vec![AbiParam::new(fx.pointer_type)], - returns: vec![AbiParam::new(types::I32)], - }) + .declare_function( + "puts", + Linkage::Import, + &Signature { + call_conv: fx.target_config.default_call_conv, + params: vec![AbiParam::new(fx.pointer_type)], + returns: vec![AbiParam::new(types::I32)], + }, + ) .unwrap(); let puts = fx.module.declare_func_in_func(puts, &mut fx.bcx.func); if fx.clif_comments.enabled() { diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index 2843e5bbdfb2c..f8bbb21492015 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -61,7 +61,12 @@ pub(crate) fn unsized_info<'tcx>( old_info } } - (_, ty::Dynamic(data, ..)) => crate::vtable::get_vtable(fx, source, data.principal()), + (_, ty::Dynamic(data, ..)) => crate::vtable::get_vtable( + fx, + source, + data.principal() + .map(|principal| fx.tcx.instantiate_bound_regions_with_erased(principal)), + ), _ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target), } } diff --git a/compiler/rustc_codegen_cranelift/src/vtable.rs b/compiler/rustc_codegen_cranelift/src/vtable.rs index 82b6178be9dcd..9d9e0462a9b72 100644 --- a/compiler/rustc_codegen_cranelift/src/vtable.rs +++ b/compiler/rustc_codegen_cranelift/src/vtable.rs @@ -48,7 +48,7 @@ pub(crate) fn get_ptr_and_method_ref<'tcx>( ) -> (Pointer, Value) { let (ptr, vtable) = 'block: { if let BackendRepr::Scalar(_) = arg.layout().backend_repr { - while !arg.layout().ty.is_unsafe_ptr() && !arg.layout().ty.is_ref() { + while !arg.layout().ty.is_raw_ptr() && !arg.layout().ty.is_ref() { let (idx, _) = arg .layout() .non_1zst_field(fx) @@ -90,7 +90,7 @@ pub(crate) fn get_ptr_and_method_ref<'tcx>( pub(crate) fn get_vtable<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>, - trait_ref: Option>, + trait_ref: Option>, ) -> Value { let data_id = data_id_for_vtable(fx.tcx, &mut fx.constants_cx, fx.module, ty, trait_ref); let local_data_id = fx.module.declare_data_in_func(data_id, fx.bcx.func); diff --git a/compiler/rustc_codegen_gcc/.github/workflows/ci.yml b/compiler/rustc_codegen_gcc/.github/workflows/ci.yml index 704d7b9c2fd6a..f96912e6b7a83 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/ci.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/ci.yml @@ -13,16 +13,15 @@ env: jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: libgccjit_version: - - { gcc: "gcc-13.deb" } - - { gcc: "gcc-13-without-int128.deb" } + - { gcc: "gcc-15.deb" } + - { gcc: "gcc-15-without-int128.deb" } commands: [ - "--mini-tests", "--std-tests", # FIXME: re-enable asm tests when GCC can emit in the right syntax. # "--asm-tests", @@ -79,6 +78,7 @@ jobs: run: | ./y.sh prepare --only-libcore ./y.sh build --sysroot + ./y.sh test --mini-tests cargo test - name: Run y.sh cargo build @@ -87,7 +87,7 @@ jobs: - name: Clean run: | - ./y.sh clean all + ./y.sh clean all - name: Prepare dependencies run: | @@ -95,9 +95,6 @@ jobs: git config --global user.name "User" ./y.sh prepare - - name: Add more failing tests because the sysroot is not compiled with LTO - run: cat tests/failing-non-lto-tests.txt >> tests/failing-ui-tests.txt - - name: Run tests run: | ./y.sh test --release --clean --build-sysroot ${{ matrix.commands }} @@ -111,13 +108,13 @@ jobs: cargo clippy --all-targets --features master -- -D warnings duplicates: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - run: python tools/check_intrinsics_duplicates.py build_system: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: Test build system diff --git a/compiler/rustc_codegen_gcc/.github/workflows/failures.yml b/compiler/rustc_codegen_gcc/.github/workflows/failures.yml index 2c1ed9ad42948..d080bbfe91fe6 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/failures.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/failures.yml @@ -13,7 +13,7 @@ env: jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false @@ -56,12 +56,12 @@ jobs: - name: Download artifact if: matrix.libgccjit_version.gcc != 'libgccjit12.so' - run: curl -LO https://github.com/rust-lang/gcc/releases/latest/download/gcc-13.deb + run: curl -LO https://github.com/rust-lang/gcc/releases/latest/download/gcc-15.deb - name: Setup path to libgccjit if: matrix.libgccjit_version.gcc != 'libgccjit12.so' run: | - sudo dpkg --force-overwrite -i gcc-13.deb + sudo dpkg --force-overwrite -i gcc-15.deb echo 'gcc-path = "/usr/lib"' > config.toml echo "LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV echo "LD_LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV @@ -90,15 +90,12 @@ jobs: if: matrix.libgccjit_version.gcc != 'libgccjit12.so' run: ./y.sh prepare - - name: Add more failing tests because the sysroot is not compiled with LTO - run: cat tests/failing-non-lto-tests.txt >> tests/failing-ui-tests.txt - - name: Run tests # TODO: re-enable those tests for libgccjit 12. if: matrix.libgccjit_version.gcc != 'libgccjit12.so' id: tests run: | - ${{ matrix.libgccjit_version.env_extra }} ./y.sh test --release --clean --build-sysroot --test-failing-rustc ${{ matrix.libgccjit_version.extra }} | tee output_log + ${{ matrix.libgccjit_version.env_extra }} ./y.sh test --release --clean --build-sysroot --test-failing-rustc ${{ matrix.libgccjit_version.extra }} 2>&1 | tee output_log rg --text "test result" output_log >> $GITHUB_STEP_SUMMARY - name: Run failing ui pattern tests for ICE @@ -106,7 +103,7 @@ jobs: if: matrix.libgccjit_version.gcc != 'libgccjit12.so' id: ui-tests run: | - ${{ matrix.libgccjit_version.env_extra }} ./y.sh test --release --test-failing-ui-pattern-tests ${{ matrix.libgccjit_version.extra }} | tee output_log_ui + ${{ matrix.libgccjit_version.env_extra }} ./y.sh test --release --test-failing-ui-pattern-tests ${{ matrix.libgccjit_version.extra }} 2>&1 | tee output_log_ui if grep -q "the compiler unexpectedly panicked" output_log_ui; then echo "Error: 'the compiler unexpectedly panicked' found in output logs. CI Error!!" exit 1 diff --git a/compiler/rustc_codegen_gcc/.github/workflows/gcc12.yml b/compiler/rustc_codegen_gcc/.github/workflows/gcc12.yml index 7dcad21a02e1d..bb9e020dc6a4c 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/gcc12.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/gcc12.yml @@ -17,7 +17,7 @@ env: jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false @@ -82,9 +82,6 @@ jobs: #- name: Add more failing tests for GCC 12 #run: cat tests/failing-ui-tests12.txt >> tests/failing-ui-tests.txt - #- name: Add more failing tests because the sysroot is not compiled with LTO - #run: cat tests/failing-non-lto-tests.txt >> tests/failing-ui-tests.txt - #- name: Run tests #run: | #./y.sh test --release --clean --build-sysroot ${{ matrix.commands }} --no-default-features diff --git a/compiler/rustc_codegen_gcc/.github/workflows/m68k.yml b/compiler/rustc_codegen_gcc/.github/workflows/m68k.yml index 1c864e04413f6..ed1fc02bd9131 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/m68k.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/m68k.yml @@ -17,13 +17,12 @@ env: jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: commands: [ - "--mini-tests", "--std-tests", # TODO(antoyo): fix those on m68k. #"--test-libcore", @@ -48,17 +47,17 @@ jobs: - name: Install packages run: | sudo apt-get update - sudo apt-get install qemu qemu-user-static + sudo apt-get install qemu-system qemu-user-static - name: Download artifact - run: curl -LO https://github.com/cross-cg-gcc-tools/cross-gcc/releases/latest/download/gcc-m68k-13.deb + run: curl -LO https://github.com/cross-cg-gcc-tools/cross-gcc/releases/latest/download/gcc-m68k-15.deb - name: Download VM artifact run: curl -LO https://github.com/cross-cg-gcc-tools/vms/releases/latest/download/debian-m68k.img - name: Setup path to libgccjit run: | - sudo dpkg -i gcc-m68k-13.deb + sudo dpkg -i gcc-m68k-15.deb echo 'gcc-path = "/usr/lib/"' > config.toml - name: Set env @@ -93,6 +92,7 @@ jobs: run: | ./y.sh prepare --only-libcore --cross ./y.sh build --sysroot --features compiler_builtins/no-f16-f128 --target-triple m68k-unknown-linux-gnu + ./y.sh test --mini-tests CG_GCC_TEST_TARGET=m68k-unknown-linux-gnu cargo test ./y.sh clean all @@ -102,9 +102,6 @@ jobs: git config --global user.name "User" ./y.sh prepare --cross - - name: Add more failing tests because the sysroot is not compiled with LTO - run: cat tests/failing-non-lto-tests.txt >> tests/failing-ui-tests.txt - - name: Run tests run: | ./y.sh test --release --clean --build-sysroot --sysroot-features compiler_builtins/no-f16-f128 ${{ matrix.commands }} diff --git a/compiler/rustc_codegen_gcc/.github/workflows/release.yml b/compiler/rustc_codegen_gcc/.github/workflows/release.yml index d5c06a836db94..886ce90b4713e 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/release.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/release.yml @@ -13,7 +13,7 @@ env: jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 strategy: fail-fast: false @@ -37,11 +37,11 @@ jobs: run: sudo apt-get install ninja-build ripgrep - name: Download artifact - run: curl -LO https://github.com/rust-lang/gcc/releases/latest/download/gcc-13.deb + run: curl -LO https://github.com/rust-lang/gcc/releases/latest/download/gcc-15.deb - name: Setup path to libgccjit run: | - sudo dpkg --force-overwrite -i gcc-13.deb + sudo dpkg --force-overwrite -i gcc-15.deb echo 'gcc-path = "/usr/lib/"' > config.toml - name: Set env @@ -54,6 +54,7 @@ jobs: run: | ./y.sh prepare --only-libcore EMBED_LTO_BITCODE=1 ./y.sh build --sysroot --release --release-sysroot + ./y.sh test --mini-tests cargo test ./y.sh clean all @@ -70,4 +71,14 @@ jobs: run: | # FIXME(antoyo): we cannot enable LTO for stdarch tests currently because of some failing LTO tests using proc-macros. echo -n 'lto = "fat"' >> build_system/build_sysroot/Cargo.toml - EMBED_LTO_BITCODE=1 ./y.sh test --release --clean --release-sysroot --build-sysroot ${{ matrix.commands }} + EMBED_LTO_BITCODE=1 ./y.sh test --release --clean --release-sysroot --build-sysroot --keep-lto-tests ${{ matrix.commands }} + + - name: Run y.sh cargo build + run: | + EMBED_LTO_BITCODE=1 CHANNEL="release" ./y.sh cargo build --release --manifest-path tests/hello-world/Cargo.toml + call_found=$(objdump -dj .text tests/hello-world/target/release/hello_world | grep -c "call .*mylib.*my_func" ) ||: + if [ $call_found -gt 0 ]; then + echo "ERROR: call my_func found in asm" + echo "Test is done with LTO enabled, hence inlining should occur across crates" + exit 1 + fi diff --git a/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml b/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml index d8818eefa96d8..d5ae6144496f1 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml @@ -73,10 +73,6 @@ jobs: echo "LD_LIBRARY_PATH="$(./y.sh info | grep -v Using) >> $GITHUB_ENV echo "LIBRARY_PATH="$(./y.sh info | grep -v Using) >> $GITHUB_ENV - - name: Build (part 2) - run: | - cargo test - - name: Clean if: ${{ !matrix.cargo_runner }} run: | @@ -92,6 +88,7 @@ jobs: if: ${{ !matrix.cargo_runner }} run: | ./y.sh test --release --clean --release-sysroot --build-sysroot --mini-tests --std-tests --test-libcore + cargo test - name: Run stdarch tests if: ${{ !matrix.cargo_runner }} diff --git a/compiler/rustc_codegen_gcc/.rustfmt.toml b/compiler/rustc_codegen_gcc/.rustfmt.toml index 725aec25a0718..a11bc41680d24 100644 --- a/compiler/rustc_codegen_gcc/.rustfmt.toml +++ b/compiler/rustc_codegen_gcc/.rustfmt.toml @@ -1,3 +1,5 @@ -version = "Two" +style_edition = "2024" use_small_heuristics = "Max" merge_derives = false +group_imports = "StdExternalCrate" +imports_granularity = "Module" diff --git a/compiler/rustc_codegen_gcc/Cargo.lock b/compiler/rustc_codegen_gcc/Cargo.lock index 6b06e7d7f278a..636e75b94a3f2 100644 --- a/compiler/rustc_codegen_gcc/Cargo.lock +++ b/compiler/rustc_codegen_gcc/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aho-corasick" @@ -11,12 +11,40 @@ dependencies = [ "memchr", ] +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + [[package]] name = "boml" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85fdb93f04c73bff54305fa437ffea5449c41edcaadfe882f35836206b166ac5" +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "fm" version = "0.2.2" @@ -28,18 +56,18 @@ dependencies = [ [[package]] name = "gccjit" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb376e98c82d9284c3a17fc1d6bf9bc921055418950238d7a553c27a7e1f6ab" +checksum = "72fd91f4adbf02b53cfc73c97bc33c5f253009043f30c56a5ec08dd5c8094dc8" dependencies = [ "gccjit_sys", ] [[package]] name = "gccjit_sys" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93b4b1be553b5df790bf25ca2a1d6add81727dc29f8d5c8742468ed306d621d1" +checksum = "0fb7b8f48a75e2cfe78c3d9a980b32771c34ffd12d196021ab3f98c49fbd2f0d" dependencies = [ "libc", ] @@ -77,9 +105,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.150" +version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "memchr" @@ -97,6 +131,12 @@ dependencies = [ "libc", ] +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + [[package]] name = "regex" version = "1.8.4" @@ -121,6 +161,20 @@ dependencies = [ "boml", "gccjit", "lang_tester", + "tempfile", +] + +[[package]] +name = "rustix" +version = "0.38.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", ] [[package]] @@ -132,6 +186,19 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "tempfile" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys", +] + [[package]] name = "termcolor" version = "1.2.0" @@ -205,3 +272,76 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/compiler/rustc_codegen_gcc/Cargo.toml b/compiler/rustc_codegen_gcc/Cargo.toml index 4828b7ddb1686..63d37358561e6 100644 --- a/compiler/rustc_codegen_gcc/Cargo.toml +++ b/compiler/rustc_codegen_gcc/Cargo.toml @@ -22,15 +22,16 @@ master = ["gccjit/master"] default = ["master"] [dependencies] -gccjit = "2.2" +gccjit = "2.4" #gccjit = { git = "https://github.com/rust-lang/gccjit.rs" } # Local copy. #gccjit = { path = "../gccjit.rs" } [dev-dependencies] -lang_tester = "0.8.0" boml = "0.3.1" +lang_tester = "0.8.0" +tempfile = "3.7.1" [profile.dev] # By compiling dependencies with optimizations, performing tests gets much faster. diff --git a/compiler/rustc_codegen_gcc/build_system/src/build.rs b/compiler/rustc_codegen_gcc/build_system/src/build.rs index d0ced211a6169..e98377f15a945 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/build.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/build.rs @@ -150,6 +150,8 @@ pub fn build_sysroot(env: &HashMap, config: &ConfigInfo) -> Resu "debug" }; + // We have a different environment variable than RUSTFLAGS to make sure those flags are only + // sent to rustc_codegen_gcc and not the LLVM backend. if let Ok(cg_rustflags) = std::env::var("CG_RUSTFLAGS") { rustflags.push(' '); rustflags.push_str(&cg_rustflags); @@ -184,8 +186,12 @@ pub fn build_sysroot(env: &HashMap, config: &ConfigInfo) -> Resu fn build_codegen(args: &mut BuildArg) -> Result<(), String> { let mut env = HashMap::new(); - env.insert("LD_LIBRARY_PATH".to_string(), args.config_info.gcc_path.clone()); - env.insert("LIBRARY_PATH".to_string(), args.config_info.gcc_path.clone()); + let gcc_path = + args.config_info.gcc_path.clone().expect( + "The config module should have emitted an error if the GCC path wasn't provided", + ); + env.insert("LD_LIBRARY_PATH".to_string(), gcc_path.clone()); + env.insert("LIBRARY_PATH".to_string(), gcc_path); if args.config_info.no_default_features { env.insert("RUSTFLAGS".to_string(), "-Csymbol-mangling-version=v0".to_string()); diff --git a/compiler/rustc_codegen_gcc/build_system/src/config.rs b/compiler/rustc_codegen_gcc/build_system/src/config.rs index 37b4b68950e49..4f9fcc9715140 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/config.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/config.rs @@ -112,7 +112,7 @@ pub struct ConfigInfo { pub sysroot_panic_abort: bool, pub cg_backend_path: String, pub sysroot_path: String, - pub gcc_path: String, + pub gcc_path: Option, config_file: Option, // This is used in particular in rust compiler bootstrap because it doesn't run at the root // of the `cg_gcc` folder, making it complicated for us to get access to local files we need @@ -173,6 +173,14 @@ impl ConfigInfo { "--release-sysroot" => self.sysroot_release_channel = true, "--release" => self.channel = Channel::Release, "--sysroot-panic-abort" => self.sysroot_panic_abort = true, + "--gcc-path" => match args.next() { + Some(arg) if !arg.is_empty() => { + self.gcc_path = Some(arg.into()); + } + _ => { + return Err("Expected a value after `--gcc-path`, found nothing".to_string()); + } + }, "--cg_gcc-path" => match args.next() { Some(arg) if !arg.is_empty() => { self.cg_gcc_path = Some(arg.into()); @@ -260,8 +268,9 @@ impl ConfigInfo { create_symlink(&libgccjit_so, output_dir.join(&format!("{}.0", libgccjit_so_name)))?; } - self.gcc_path = output_dir.display().to_string(); - println!("Using `{}` as path for libgccjit", self.gcc_path); + let gcc_path = output_dir.display().to_string(); + println!("Using `{}` as path for libgccjit", gcc_path); + self.gcc_path = Some(gcc_path); Ok(()) } @@ -273,6 +282,15 @@ impl ConfigInfo { } pub fn setup_gcc_path(&mut self) -> Result<(), String> { + // If the user used the `--gcc-path` option, no need to look at `config.toml` content + // since we already have everything we need. + if let Some(gcc_path) = &self.gcc_path { + println!( + "`--gcc-path` was provided, ignoring config file. Using `{}` as path for libgccjit", + gcc_path + ); + return Ok(()); + } let config_file = match self.config_file.as_deref() { Some(config_file) => config_file.into(), None => self.compute_path("config.toml"), @@ -283,12 +301,15 @@ impl ConfigInfo { self.download_gccjit_if_needed()?; return Ok(()); } - self.gcc_path = match gcc_path { - Some(path) => path, - None => { - return Err(format!("missing `gcc-path` value from `{}`", config_file.display(),)); - } + let Some(gcc_path) = gcc_path else { + return Err(format!("missing `gcc-path` value from `{}`", config_file.display())); }; + println!( + "GCC path retrieved from `{}`. Using `{}` as path for libgccjit", + config_file.display(), + gcc_path + ); + self.gcc_path = Some(gcc_path); Ok(()) } @@ -299,10 +320,17 @@ impl ConfigInfo { ) -> Result<(), String> { env.insert("CARGO_INCREMENTAL".to_string(), "0".to_string()); - if self.gcc_path.is_empty() && !use_system_gcc { - self.setup_gcc_path()?; - } - env.insert("GCC_PATH".to_string(), self.gcc_path.clone()); + let gcc_path = if !use_system_gcc { + if self.gcc_path.is_none() { + self.setup_gcc_path()?; + } + self.gcc_path.clone().expect( + "The config module should have emitted an error if the GCC path wasn't provided", + ) + } else { + String::new() + }; + env.insert("GCC_PATH".to_string(), gcc_path.clone()); if self.cargo_target_dir.is_empty() { match env.get("CARGO_TARGET_DIR").filter(|dir| !dir.is_empty()) { @@ -381,6 +409,8 @@ impl ConfigInfo { } // This environment variable is useful in case we want to change options of rustc commands. + // We have a different environment variable than RUSTFLAGS to make sure those flags are + // only sent to rustc_codegen_gcc and not the LLVM backend. if let Some(cg_rustflags) = env.get("CG_RUSTFLAGS") { rustflags.extend_from_slice(&split_args(&cg_rustflags)?); } @@ -414,7 +444,7 @@ impl ConfigInfo { "{target}:{sysroot}:{gcc_path}", target = self.cargo_target_dir, sysroot = sysroot.display(), - gcc_path = self.gcc_path, + gcc_path = gcc_path, ); env.insert("LIBRARY_PATH".to_string(), ld_library_path.clone()); env.insert("LD_LIBRARY_PATH".to_string(), ld_library_path.clone()); @@ -459,6 +489,7 @@ impl ConfigInfo { --release-sysroot : Build sysroot in release mode --sysroot-panic-abort : Build the sysroot without unwinding support --config-file : Location of the config file to be used + --gcc-path : Location of the GCC root folder --cg_gcc-path : Location of the rustc_codegen_gcc root folder (used when ran from another directory) --no-default-features : Add `--no-default-features` flag to cargo commands diff --git a/compiler/rustc_codegen_gcc/build_system/src/info.rs b/compiler/rustc_codegen_gcc/build_system/src/info.rs index ea38791d38c9a..bd891de2eb4c0 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/info.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/info.rs @@ -14,6 +14,8 @@ pub fn run() -> Result<(), String> { } config.no_download = true; config.setup_gcc_path()?; - println!("{}", config.gcc_path); + if let Some(gcc_path) = config.gcc_path { + println!("{}", gcc_path); + } Ok(()) } diff --git a/compiler/rustc_codegen_gcc/build_system/src/main.rs b/compiler/rustc_codegen_gcc/build_system/src/main.rs index 3a860e2b1360c..393617183061b 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/main.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/main.rs @@ -34,11 +34,11 @@ Options: --help : Displays this help message. Commands: - cargo : Executes a cargo command. + cargo : Executes a cargo command. rustc : Compiles the program using the GCC compiler. clean : Cleans the build directory, removing all compiled files and artifacts. prepare : Prepares the environment for building, including fetching dependencies and setting up configurations. - build : Compiles the project. + build : Compiles the project. test : Runs tests for the project. info : Displays information about the build environment and project configuration. clone-gcc : Clones the GCC compiler from a specified source. diff --git a/compiler/rustc_codegen_gcc/build_system/src/test.rs b/compiler/rustc_codegen_gcc/build_system/src/test.rs index c69e240c01de4..6c29c7d1825b9 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/test.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/test.rs @@ -93,6 +93,7 @@ struct TestArg { sysroot_panic_abort: bool, config_info: ConfigInfo, sysroot_features: Vec, + keep_lto_tests: bool, } impl TestArg { @@ -128,6 +129,9 @@ impl TestArg { "--sysroot-panic-abort" => { test_arg.sysroot_panic_abort = true; } + "--keep-lto-tests" => { + test_arg.keep_lto_tests = true; + } "--sysroot-features" => match args.next() { Some(feature) if !feature.is_empty() => { test_arg.sysroot_features.push(feature); @@ -194,7 +198,7 @@ fn build_if_no_backend(env: &Env, args: &TestArg) -> Result<(), String> { } fn clean(_env: &Env, args: &TestArg) -> Result<(), String> { - let _ = std::fs::remove_dir_all(&args.config_info.cargo_target_dir); + let _ = remove_dir_all(&args.config_info.cargo_target_dir); let path = Path::new(&args.config_info.cargo_target_dir).join("gccjit"); create_dir(&path) } @@ -422,19 +426,6 @@ fn std_tests(env: &Env, args: &TestArg) -> Result<(), String> { run_command_with_env(&command, None, Some(env))?; maybe_run_command_in_vm(&[&cargo_target_dir.join("track-caller-attribute")], env, args)?; - // FIXME: create a function "display_if_not_quiet" or something along the line. - println!("[AOT] mod_bench"); - let mut command = args.config_info.rustc_command_vec(); - command.extend_from_slice(&[ - &"example/mod_bench.rs", - &"--crate-type", - &"bin", - &"--target", - &args.config_info.target_triple, - ]); - run_command_with_env(&command, None, Some(env))?; - // FIXME: the compiled binary is not run. - Ok(()) } @@ -641,7 +632,7 @@ fn test_projects(env: &Env, args: &TestArg) -> Result<(), String> { //failing test is fixed upstream. //"https://github.com/marshallpierce/rust-base64", // FIXME: one test is OOM-killed. // TODO: ignore the base64 test that is OOM-killed. - "https://github.com/time-rs/time", + //"https://github.com/time-rs/time", // FIXME: one test fails (https://github.com/time-rs/time/issues/719). "https://github.com/rust-lang/log", "https://github.com/bitflags/bitflags", //"https://github.com/serde-rs/serde", // FIXME: one test fails. @@ -692,19 +683,6 @@ fn test_libcore(env: &Env, args: &TestArg) -> Result<(), String> { Ok(()) } -// echo "[BENCH COMPILE] mod_bench" -// -// COMPILE_MOD_BENCH_INLINE="$RUSTC example/mod_bench.rs --crate-type bin -Zmir-opt-level=3 -O --crate-name mod_bench_inline" -// COMPILE_MOD_BENCH_LLVM_0="rustc example/mod_bench.rs --crate-type bin -Copt-level=0 -o $cargo_target_dir/mod_bench_llvm_0 -Cpanic=abort" -// COMPILE_MOD_BENCH_LLVM_1="rustc example/mod_bench.rs --crate-type bin -Copt-level=1 -o $cargo_target_dir/mod_bench_llvm_1 -Cpanic=abort" -// COMPILE_MOD_BENCH_LLVM_2="rustc example/mod_bench.rs --crate-type bin -Copt-level=2 -o $cargo_target_dir/mod_bench_llvm_2 -Cpanic=abort" -// COMPILE_MOD_BENCH_LLVM_3="rustc example/mod_bench.rs --crate-type bin -Copt-level=3 -o $cargo_target_dir/mod_bench_llvm_3 -Cpanic=abort" -// -// Use 100 runs, because a single compilations doesn't take more than ~150ms, so it isn't very slow -// hyperfine --runs ${COMPILE_RUNS:-100} "$COMPILE_MOD_BENCH_INLINE" "$COMPILE_MOD_BENCH_LLVM_0" "$COMPILE_MOD_BENCH_LLVM_1" "$COMPILE_MOD_BENCH_LLVM_2" "$COMPILE_MOD_BENCH_LLVM_3" -// echo "[BENCH RUN] mod_bench" -// hyperfine --runs ${RUN_RUNS:-10} $cargo_target_dir/mod_bench{,_inline} $cargo_target_dir/mod_bench_llvm_* - fn extended_rand_tests(env: &Env, args: &TestArg) -> Result<(), String> { if !args.is_using_gcc_master_branch() { println!("Not using GCC master branch. Skipping `extended_rand_tests`."); @@ -835,8 +813,7 @@ fn valid_ui_error_pattern_test(file: &str) -> bool { .any(|to_ignore| file.ends_with(to_ignore)) } -#[rustfmt::skip] -fn contains_ui_error_patterns(file_path: &Path) -> Result { +fn contains_ui_error_patterns(file_path: &Path, keep_lto_tests: bool) -> Result { // Tests generating errors. let file = File::open(file_path) .map_err(|error| format!("Failed to read `{}`: {:?}", file_path.display(), error))?; @@ -849,22 +826,38 @@ fn contains_ui_error_patterns(file_path: &Path) -> Result { "//@ error-pattern:", "//@ build-fail", "//@ run-fail", + "//@ known-bug", "-Cllvm-args", "//~", "thread", ] - .iter() - .any(|check| line.contains(check)) + .iter() + .any(|check| line.contains(check)) { return Ok(true); } + + if !keep_lto_tests + && (line.contains("-Clto") + || line.contains("-C lto") + || line.contains("compile-flags: -Clinker-plugin-lto")) + && !line.contains("-Clto=thin") + { + return Ok(true); + } + if line.contains("//[") && line.contains("]~") { return Ok(true); } } - if file_path.display().to_string().contains("ambiguous-4-extern.rs") { + let file_path = file_path.display().to_string(); + if file_path.contains("ambiguous-4-extern.rs") { eprintln!("nothing found for {file_path:?}"); } + // The files in this directory contain errors. + if file_path.contains("/error-emitter/") { + return Ok(true); + } Ok(false) } @@ -903,7 +896,7 @@ where rust_path.join("tests/ui"), &mut |_dir| Ok(()), &mut |file_path| { - if contains_ui_error_patterns(file_path)? { + if contains_ui_error_patterns(file_path, args.keep_lto_tests)? { Ok(()) } else { remove_file(file_path).map_err(|e| e.to_string()) @@ -928,7 +921,7 @@ where .iter() .any(|name| *name == dir_name) { - std::fs::remove_dir_all(dir).map_err(|error| { + remove_dir_all(dir).map_err(|error| { format!("Failed to remove folder `{}`: {:?}", dir.display(), error) })?; } @@ -940,27 +933,42 @@ where // These two functions are used to remove files that are known to not be working currently // with the GCC backend to reduce noise. - fn dir_handling(dir: &Path) -> Result<(), String> { - if dir.file_name().map(|name| name == "auxiliary").unwrap_or(true) { - return Ok(()); - } + fn dir_handling(keep_lto_tests: bool) -> impl Fn(&Path) -> Result<(), String> { + move |dir| { + if dir.file_name().map(|name| name == "auxiliary").unwrap_or(true) { + return Ok(()); + } - walk_dir(dir, &mut dir_handling, &mut file_handling, false) - } - fn file_handling(file_path: &Path) -> Result<(), String> { - if !file_path.extension().map(|extension| extension == "rs").unwrap_or(false) { - return Ok(()); + walk_dir( + dir, + &mut dir_handling(keep_lto_tests), + &mut file_handling(keep_lto_tests), + false, + ) } - let path_str = file_path.display().to_string().replace("\\", "/"); - if valid_ui_error_pattern_test(&path_str) { - return Ok(()); - } else if contains_ui_error_patterns(file_path)? { - return remove_file(&file_path); + } + + fn file_handling(keep_lto_tests: bool) -> impl Fn(&Path) -> Result<(), String> { + move |file_path| { + if !file_path.extension().map(|extension| extension == "rs").unwrap_or(false) { + return Ok(()); + } + let path_str = file_path.display().to_string().replace("\\", "/"); + if valid_ui_error_pattern_test(&path_str) { + return Ok(()); + } else if contains_ui_error_patterns(file_path, keep_lto_tests)? { + return remove_file(&file_path); + } + Ok(()) } - Ok(()) } - walk_dir(rust_path.join("tests/ui"), &mut dir_handling, &mut file_handling, false)?; + walk_dir( + rust_path.join("tests/ui"), + &mut dir_handling(args.keep_lto_tests), + &mut file_handling(args.keep_lto_tests), + false, + )?; } let nb_parts = args.nb_parts.unwrap_or(0); if nb_parts > 0 { @@ -1173,7 +1181,7 @@ fn remove_files_callback<'a>( files.split('\n').map(|line| line.trim()).filter(|line| !line.is_empty()) { let path = rust_path.join(file); - if let Err(e) = std::fs::remove_dir_all(&path) { + if let Err(e) = remove_dir_all(&path) { println!("Failed to remove directory `{}`: {}", path.display(), e); } } @@ -1221,8 +1229,11 @@ pub fn run() -> Result<(), String> { if !args.use_system_gcc { args.config_info.setup_gcc_path()?; - env.insert("LIBRARY_PATH".to_string(), args.config_info.gcc_path.clone()); - env.insert("LD_LIBRARY_PATH".to_string(), args.config_info.gcc_path.clone()); + let gcc_path = args.config_info.gcc_path.clone().expect( + "The config module should have emitted an error if the GCC path wasn't provided", + ); + env.insert("LIBRARY_PATH".to_string(), gcc_path.clone()); + env.insert("LD_LIBRARY_PATH".to_string(), gcc_path); } build_if_no_backend(&env, &args)?; diff --git a/compiler/rustc_codegen_gcc/example/alloc_example.rs b/compiler/rustc_codegen_gcc/example/alloc_example.rs index 6ed8b9157f21a..9a0b46d5b221a 100644 --- a/compiler/rustc_codegen_gcc/example/alloc_example.rs +++ b/compiler/rustc_codegen_gcc/example/alloc_example.rs @@ -1,5 +1,6 @@ -#![feature(start, core_intrinsics, alloc_error_handler, lang_items)] +#![feature(core_intrinsics, alloc_error_handler, lang_items)] #![no_std] +#![no_main] #![allow(internal_features)] extern crate alloc; @@ -37,8 +38,8 @@ unsafe extern "C" fn _Unwind_Resume() { core::intrinsics::unreachable(); } -#[start] -fn main(_argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(_argc: core::ffi::c_int, _argv: *const *const u8) -> core::ffi::c_int { let world: Box<&str> = Box::new("Hello World!\0"); unsafe { puts(*world as *const str as *const u8); diff --git a/compiler/rustc_codegen_gcc/example/mini_core.rs b/compiler/rustc_codegen_gcc/example/mini_core.rs index cdd151613df84..2ff1d757fd4e0 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core.rs @@ -54,6 +54,9 @@ impl LegacyReceiver for Box {} #[lang = "copy"] pub trait Copy {} +#[lang = "bikeshed_guaranteed_no_drop"] +pub trait BikeshedGuaranteedNoDrop {} + impl Copy for bool {} impl Copy for u8 {} impl Copy for u16 {} @@ -170,6 +173,14 @@ impl Add for usize { } } +impl Add for isize { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + self + rhs + } +} + #[lang = "sub"] pub trait Sub { type Output; @@ -681,7 +692,7 @@ impl Index for [T] { } } -extern { +extern "C" { type VaListImpl; } diff --git a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs index dcfa34cb729d8..4cbe66c5e4c69 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs @@ -1,7 +1,7 @@ // Adapted from https://github.com/sunfishcode/mir2cranelift/blob/master/rust-examples/nocore-hello-world.rs #![feature( - no_core, unboxed_closures, start, lang_items, never_type, linkage, + no_core, unboxed_closures, lang_items, never_type, linkage, extern_types, thread_local )] #![no_core] @@ -258,13 +258,13 @@ fn main() { assert_eq!(((|()| 42u8) as fn(()) -> u8)(()), 42); - extern { + extern "C" { #[linkage = "weak"] static ABC: *const u8; } { - extern { + extern "C" { #[linkage = "weak"] static ABC: *const u8; } diff --git a/compiler/rustc_codegen_gcc/example/mod_bench.rs b/compiler/rustc_codegen_gcc/example/mod_bench.rs deleted file mode 100644 index cae911c1073d5..0000000000000 --- a/compiler/rustc_codegen_gcc/example/mod_bench.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![feature(start, core_intrinsics, lang_items)] -#![no_std] -#![allow(internal_features)] - -#[link(name = "c")] -extern {} - -#[panic_handler] -fn panic_handler(_: &core::panic::PanicInfo<'_>) -> ! { - core::intrinsics::abort(); -} - -#[lang="eh_personality"] -fn eh_personality(){} - -// Required for rustc_codegen_llvm -#[no_mangle] -unsafe extern "C" fn _Unwind_Resume() { - core::intrinsics::unreachable(); -} - -#[start] -fn main(_argc: isize, _argv: *const *const u8) -> isize { - for i in 2..100_000_000 { - black_box((i + 1) % i); - } - - 0 -} - -#[inline(never)] -fn black_box(i: u32) { - if i != 1 { - core::intrinsics::abort(); - } -} diff --git a/compiler/rustc_codegen_gcc/example/std_example.rs b/compiler/rustc_codegen_gcc/example/std_example.rs index 9e43b4635f0d3..5fa1e0afb0603 100644 --- a/compiler/rustc_codegen_gcc/example/std_example.rs +++ b/compiler/rustc_codegen_gcc/example/std_example.rs @@ -7,7 +7,7 @@ use std::arch::x86_64::*; use std::io::Write; use std::ops::Coroutine; -extern { +extern "C" { pub fn printf(format: *const i8, ...) -> i32; } diff --git a/compiler/rustc_codegen_gcc/libgccjit.version b/compiler/rustc_codegen_gcc/libgccjit.version index b9bbbd324c3b9..417fd5b03935d 100644 --- a/compiler/rustc_codegen_gcc/libgccjit.version +++ b/compiler/rustc_codegen_gcc/libgccjit.version @@ -1 +1 @@ -e744a9459d33864067214741daf5c5bc2a7b88c6 +e607be166673a8de9fc07f6f02c60426e556c5f2 diff --git a/compiler/rustc_codegen_gcc/messages.ftl b/compiler/rustc_codegen_gcc/messages.ftl index 85fa17a6ba501..882fff8673a19 100644 --- a/compiler/rustc_codegen_gcc/messages.ftl +++ b/compiler/rustc_codegen_gcc/messages.ftl @@ -5,9 +5,6 @@ codegen_gcc_unknown_ctarget_feature_prefix = codegen_gcc_invalid_minimum_alignment = invalid minimum global alignment: {$err} -codegen_gcc_lto_not_supported = - LTO is not supported. You may get a linker error. - codegen_gcc_forbidden_ctarget_feature = target feature `{$feature}` cannot be toggled with `-Ctarget-feature`: {$reason} diff --git a/compiler/rustc_codegen_gcc/patches/0022-core-Disable-not-compiling-tests.patch b/compiler/rustc_codegen_gcc/patches/0022-core-Disable-not-compiling-tests.patch index b2ab05691ecbf..70e3e2ba7fee1 100644 --- a/compiler/rustc_codegen_gcc/patches/0022-core-Disable-not-compiling-tests.patch +++ b/compiler/rustc_codegen_gcc/patches/0022-core-Disable-not-compiling-tests.patch @@ -1,7 +1,7 @@ -From 18793c6109890493ceb3ff36549849a36e3d8022 Mon Sep 17 00:00:00 2001 +From af0e237f056fa838c77463381a19b0dc993c0a35 Mon Sep 17 00:00:00 2001 From: None Date: Sun, 1 Sep 2024 11:42:17 -0400 -Subject: [PATCH] [core] Disable not compiling tests +Subject: [PATCH] Disable not compiling tests --- library/core/tests/Cargo.toml | 14 ++++++++++++++ @@ -30,14 +30,15 @@ index 0000000..ca326ac +rand = { version = "0.8.5", default-features = false } +rand_xorshift = { version = "0.3.0", default-features = false } diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs -index 1e336bf..5800ebb 100644 +index a4a7946..ecfe43f 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -1,4 +1,5 @@ // tidy-alphabetical-start +#![cfg(test)] - #![cfg_attr(bootstrap, feature(offset_of_nested))] #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![cfg_attr(test, feature(cfg_match))] --- -2.46.0 + #![feature(alloc_layout_extra)] +-- +2.47.1 + diff --git a/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch b/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch index 01461987ffb14..9ef5e0e4f4672 100644 --- a/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch +++ b/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch @@ -27,5 +27,4 @@ index b71786c..cf484d5 100644 mod slice; mod str; mod str_lossy; --- -2.45.2 +-- 2.45.2 diff --git a/compiler/rustc_codegen_gcc/rust-toolchain b/compiler/rustc_codegen_gcc/rust-toolchain index dca3b0c22e4e5..940b3de9f7453 100644 --- a/compiler/rustc_codegen_gcc/rust-toolchain +++ b/compiler/rustc_codegen_gcc/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-08-11" +channel = "nightly-2025-01-12" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index 14fc23593f0e6..717baebcd8cd6 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -1,6 +1,7 @@ #[cfg(feature = "master")] use gccjit::FnAttribute; use gccjit::{ToLValue, ToRValue, Type}; +use rustc_abi::{Reg, RegKind}; use rustc_codegen_ssa::traits::{AbiBuilderMethods, BaseTypeCodegenMethods}; use rustc_data_structures::fx::FxHashSet; use rustc_middle::bug; @@ -8,7 +9,7 @@ use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutOf; #[cfg(feature = "master")] use rustc_session::config; -use rustc_target::abi::call::{ArgAttributes, CastTarget, FnAbi, PassMode, Reg, RegKind}; +use rustc_target::callconv::{ArgAttributes, CastTarget, FnAbi, PassMode}; use crate::builder::Builder; use crate::context::CodegenCx; @@ -132,10 +133,10 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { if cx.sess().opts.optimize == config::OptLevel::No { return ty; } - if attrs.regular.contains(rustc_target::abi::call::ArgAttribute::NoAlias) { + if attrs.regular.contains(rustc_target::callconv::ArgAttribute::NoAlias) { ty = ty.make_restrict() } - if attrs.regular.contains(rustc_target::abi::call::ArgAttribute::NonNull) { + if attrs.regular.contains(rustc_target::callconv::ArgAttribute::NonNull) { non_null_args.push(arg_index as i32 + 1); } ty diff --git a/compiler/rustc_codegen_gcc/src/allocator.rs b/compiler/rustc_codegen_gcc/src/allocator.rs index f13a75648aea8..416f3231a13c4 100644 --- a/compiler/rustc_codegen_gcc/src/allocator.rs +++ b/compiler/rustc_codegen_gcc/src/allocator.rs @@ -1,6 +1,6 @@ -#[cfg(feature = "master")] -use gccjit::FnAttribute; use gccjit::{Context, FunctionType, GlobalKind, ToRValue, Type}; +#[cfg(feature = "master")] +use gccjit::{FnAttribute, VarAttribute}; use rustc_ast::expand::allocator::{ ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE, alloc_error_handler_name, default_fn_name, global_fn_name, @@ -10,6 +10,8 @@ use rustc_middle::ty::TyCtxt; use rustc_session::config::OomStrategy; use crate::GccContext; +#[cfg(feature = "master")] +use crate::base::symbol_visibility_to_gcc; pub(crate) unsafe fn codegen( tcx: TyCtxt<'_>, @@ -70,12 +72,20 @@ pub(crate) unsafe fn codegen( let name = OomStrategy::SYMBOL.to_string(); let global = context.new_global(None, GlobalKind::Exported, i8, name); + #[cfg(feature = "master")] + global.add_attribute(VarAttribute::Visibility(symbol_visibility_to_gcc( + tcx.sess.default_visibility(), + ))); let value = tcx.sess.opts.unstable_opts.oom.should_panic(); let value = context.new_rvalue_from_int(i8, value as i32); global.global_set_initializer_rvalue(value); let name = NO_ALLOC_SHIM_IS_UNSTABLE.to_string(); let global = context.new_global(None, GlobalKind::Exported, i8, name); + #[cfg(feature = "master")] + global.add_attribute(VarAttribute::Visibility(symbol_visibility_to_gcc( + tcx.sess.default_visibility(), + ))); let value = context.new_rvalue_from_int(i8, 0); global.global_set_initializer_rvalue(value); } @@ -105,15 +115,9 @@ fn create_wrapper_function( ); #[cfg(feature = "master")] - match tcx.sess.default_visibility() { - rustc_target::spec::SymbolVisibility::Hidden => { - func.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden)) - } - rustc_target::spec::SymbolVisibility::Protected => { - func.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Protected)) - } - rustc_target::spec::SymbolVisibility::Interposable => {} - } + func.add_attribute(FnAttribute::Visibility(symbol_visibility_to_gcc( + tcx.sess.default_visibility(), + ))); if tcx.sess.must_emit_unwind_tables() { // TODO(antoyo): emit unwind tables. diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs index 028a5ab5f716b..69b04dd579693 100644 --- a/compiler/rustc_codegen_gcc/src/attributes.rs +++ b/compiler/rustc_codegen_gcc/src/attributes.rs @@ -20,7 +20,7 @@ fn inline_attr<'gcc, 'tcx>( ) -> Option> { match inline { InlineAttr::Hint => Some(FnAttribute::Inline), - InlineAttr::Always => Some(FnAttribute::AlwaysInline), + InlineAttr::Always | InlineAttr::Force { .. } => Some(FnAttribute::AlwaysInline), InlineAttr::Never => { if cx.sess().target.arch != "amdgpu" { Some(FnAttribute::NoInline) diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index ed92f9c52412c..cb4caec8c326c 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -35,16 +35,13 @@ use rustc_middle::bug; use rustc_middle::dep_graph::WorkProduct; use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel}; use rustc_session::config::{CrateType, Lto}; +use rustc_target::spec::RelocModel; use tempfile::{TempDir, tempdir}; use crate::back::write::save_temp_bitcode; use crate::errors::{DynamicLinkingWithLTO, LtoBitcodeFromRlib, LtoDisallowed, LtoDylib}; use crate::{GccCodegenBackend, GccContext, SyncContext, to_gcc_opt_level}; -/// We keep track of the computed LTO cache keys from the previous -/// session to determine which CGUs we can reuse. -//pub const THIN_LTO_KEYS_INCR_COMP_FILE_NAME: &str = "thin-lto-past-keys.bin"; - pub fn crate_type_allows_lto(crate_type: CrateType) -> bool { match crate_type { CrateType::Executable | CrateType::Dylib | CrateType::Staticlib | CrateType::Cdylib => true, @@ -54,7 +51,7 @@ pub fn crate_type_allows_lto(crate_type: CrateType) -> bool { struct LtoData { // TODO(antoyo): use symbols_below_threshold. - //symbols_below_threshold: Vec, + //symbols_below_threshold: Vec, upstream_modules: Vec<(SerializedModule, CString)>, tmp_path: TempDir, } @@ -83,7 +80,7 @@ fn prepare_lto( let symbol_filter = &|&(ref name, info): &(String, SymbolExportInfo)| { if info.level.is_below_threshold(export_threshold) || info.used { - Some(CString::new(name.as_str()).unwrap()) + Some(name.clone()) } else { None } @@ -91,7 +88,7 @@ fn prepare_lto( let exported_symbols = cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO"); let mut symbols_below_threshold = { let _timer = cgcx.prof.generic_activity("GCC_lto_generate_symbols_below_threshold"); - exported_symbols[&LOCAL_CRATE].iter().filter_map(symbol_filter).collect::>() + exported_symbols[&LOCAL_CRATE].iter().filter_map(symbol_filter).collect::>() }; info!("{} symbols to preserve in this crate", symbols_below_threshold.len()); @@ -159,11 +156,7 @@ fn prepare_lto( } } - Ok(LtoData { - //symbols_below_threshold, - upstream_modules, - tmp_path, - }) + Ok(LtoData { upstream_modules, tmp_path }) } fn save_as_file(obj: &[u8], path: &Path) -> Result<(), LtoBitcodeFromRlib> { @@ -191,7 +184,7 @@ pub(crate) fn run_fat( cached_modules, lto_data.upstream_modules, lto_data.tmp_path, - //&symbols_below_threshold, + //<o_data.symbols_below_threshold, ) } @@ -202,7 +195,7 @@ fn fat_lto( cached_modules: Vec<(SerializedModule, WorkProduct)>, mut serialized_modules: Vec<(SerializedModule, CString)>, tmp_path: TempDir, - //symbols_below_threshold: &[*const libc::c_char], + //symbols_below_threshold: &[String], ) -> Result, FatalError> { let _timer = cgcx.prof.generic_activity("GCC_fat_lto_build_monolithic_module"); info!("going for a fat lto"); @@ -327,6 +320,7 @@ fn fat_lto( ptr as *const *const libc::c_char, symbols_below_threshold.len() as libc::size_t, );*/ + save_temp_bitcode(cgcx, &module, "lto.after-restriction"); //} } @@ -363,8 +357,6 @@ pub(crate) fn run_thin( let dcx = cgcx.create_dcx(); let dcx = dcx.handle(); let lto_data = prepare_lto(cgcx, dcx)?; - /*let symbols_below_threshold = - symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::>();*/ if cgcx.opts.cg.linker_plugin_lto.enabled() { unreachable!( "We should never reach this case if the LTO step \ @@ -377,7 +369,8 @@ pub(crate) fn run_thin( modules, lto_data.upstream_modules, lto_data.tmp_path, - cached_modules, /*, &symbols_below_threshold*/ + cached_modules, + //<o_data.symbols_below_threshold, ) } @@ -428,7 +421,7 @@ fn thin_lto( serialized_modules: Vec<(SerializedModule, CString)>, tmp_path: TempDir, cached_modules: Vec<(SerializedModule, WorkProduct)>, - //symbols_below_threshold: &[*const libc::c_char], + //_symbols_below_threshold: &[String], ) -> Result<(Vec>, Vec), FatalError> { let _timer = cgcx.prof.generic_activity("LLVM_thin_lto_global_analysis"); info!("going for that thin, thin LTO"); @@ -640,7 +633,13 @@ pub unsafe fn optimize_thin_module( } }; let module = ModuleCodegen { - module_llvm: GccContext { context, should_combine_object_files, temp_dir: None }, + module_llvm: GccContext { + context, + should_combine_object_files, + // TODO(antoyo): use the correct relocation model here. + relocation_model: RelocModel::Pic, + temp_dir: None, + }, name: thin_module.name().to_string(), kind: ModuleKind::Regular, }; @@ -660,9 +659,7 @@ pub unsafe fn optimize_thin_module( { let _timer = cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_rename", thin_module.name()); - if !llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target) { - return Err(write::llvm_err(&dcx, LlvmError::PrepareThinLtoModule)); - } + unsafe { llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target) }; save_temp_bitcode(cgcx, &module, "thin-lto-after-rename"); } @@ -713,10 +710,6 @@ pub struct ThinBuffer { context: Arc, } -// TODO: check if this makes sense to make ThinBuffer Send and Sync. -unsafe impl Send for ThinBuffer {} -unsafe impl Sync for ThinBuffer {} - impl ThinBuffer { pub(crate) fn new(context: &Arc) -> Self { Self { context: Arc::clone(context) } diff --git a/compiler/rustc_codegen_gcc/src/back/write.rs b/compiler/rustc_codegen_gcc/src/back/write.rs index 802968979c722..51c5ba73e32bc 100644 --- a/compiler/rustc_codegen_gcc/src/back/write.rs +++ b/compiler/rustc_codegen_gcc/src/back/write.rs @@ -1,6 +1,6 @@ use std::{env, fs}; -use gccjit::OutputKind; +use gccjit::{Context, OutputKind}; use rustc_codegen_ssa::back::link::ensure_removed; use rustc_codegen_ssa::back::write::{BitcodeSection, CodegenContext, EmitObj, ModuleConfig}; use rustc_codegen_ssa::{CompiledModule, ModuleCodegen}; @@ -10,6 +10,7 @@ use rustc_session::config::OutputType; use rustc_span::fatal_error::FatalError; use rustc_target::spec::SplitDebuginfo; +use crate::base::add_pic_option; use crate::errors::CopyBitcode; use crate::{GccCodegenBackend, GccContext}; @@ -31,51 +32,87 @@ pub(crate) unsafe fn codegen( // NOTE: Only generate object files with GIMPLE when this environment variable is set for // now because this requires a particular setup (same gcc/lto1/lto-wrapper commit as libgccjit). - // TODO: remove this environment variable. + // TODO(antoyo): remove this environment variable. let fat_lto = env::var("EMBED_LTO_BITCODE").as_deref() == Ok("1"); let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name); let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name); - if config.bitcode_needed() && fat_lto { - let _timer = cgcx - .prof - .generic_activity_with_arg("GCC_module_codegen_make_bitcode", &*module.name); - - // TODO(antoyo) - /*if let Some(bitcode_filename) = bc_out.file_name() { - cgcx.prof.artifact_size( - "llvm_bitcode", - bitcode_filename.to_string_lossy(), - data.len() as u64, - ); - }*/ - - if config.emit_bc || config.emit_obj == EmitObj::Bitcode { + if config.bitcode_needed() { + if fat_lto { let _timer = cgcx .prof - .generic_activity_with_arg("GCC_module_codegen_emit_bitcode", &*module.name); - context.add_command_line_option("-flto=auto"); - context.add_command_line_option("-flto-partition=one"); - // TODO: remove since we don't want fat objects when it is for Bitcode only. - context.add_command_line_option("-ffat-lto-objects"); - context - .compile_to_file(OutputKind::ObjectFile, bc_out.to_str().expect("path to str")); - } + .generic_activity_with_arg("GCC_module_codegen_make_bitcode", &*module.name); - if config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full) { - let _timer = cgcx - .prof - .generic_activity_with_arg("GCC_module_codegen_embed_bitcode", &*module.name); - // TODO(antoyo): maybe we should call embed_bitcode to have the proper iOS fixes? - //embed_bitcode(cgcx, llcx, llmod, &config.bc_cmdline, data); - - context.add_command_line_option("-flto=auto"); - context.add_command_line_option("-flto-partition=one"); - context.add_command_line_option("-ffat-lto-objects"); - // TODO(antoyo): Send -plugin/usr/lib/gcc/x86_64-pc-linux-gnu/11.1.0/liblto_plugin.so to linker (this should be done when specifying the appropriate rustc cli argument). - context - .compile_to_file(OutputKind::ObjectFile, bc_out.to_str().expect("path to str")); + // TODO(antoyo) + /*if let Some(bitcode_filename) = bc_out.file_name() { + cgcx.prof.artifact_size( + "llvm_bitcode", + bitcode_filename.to_string_lossy(), + data.len() as u64, + ); + }*/ + + if config.emit_bc || config.emit_obj == EmitObj::Bitcode { + let _timer = cgcx.prof.generic_activity_with_arg( + "GCC_module_codegen_emit_bitcode", + &*module.name, + ); + context.add_command_line_option("-flto=auto"); + context.add_command_line_option("-flto-partition=one"); + // TODO(antoyo): remove since we don't want fat objects when it is for Bitcode only. + context.add_command_line_option("-ffat-lto-objects"); + context.compile_to_file( + OutputKind::ObjectFile, + bc_out.to_str().expect("path to str"), + ); + } + + if config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full) { + let _timer = cgcx.prof.generic_activity_with_arg( + "GCC_module_codegen_embed_bitcode", + &*module.name, + ); + // TODO(antoyo): maybe we should call embed_bitcode to have the proper iOS fixes? + //embed_bitcode(cgcx, llcx, llmod, &config.bc_cmdline, data); + + context.add_command_line_option("-flto=auto"); + context.add_command_line_option("-flto-partition=one"); + context.add_command_line_option("-ffat-lto-objects"); + // TODO(antoyo): Send -plugin/usr/lib/gcc/x86_64-pc-linux-gnu/11.1.0/liblto_plugin.so to linker (this should be done when specifying the appropriate rustc cli argument). + context.compile_to_file( + OutputKind::ObjectFile, + bc_out.to_str().expect("path to str"), + ); + } + } else { + if config.emit_bc || config.emit_obj == EmitObj::Bitcode { + let _timer = cgcx.prof.generic_activity_with_arg( + "GCC_module_codegen_emit_bitcode", + &*module.name, + ); + context.compile_to_file( + OutputKind::ObjectFile, + bc_out.to_str().expect("path to str"), + ); + } + + if config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full) { + // TODO(antoyo): we might want to emit to emit an error here, saying to set the + // environment variable EMBED_LTO_BITCODE. + let _timer = cgcx.prof.generic_activity_with_arg( + "GCC_module_codegen_embed_bitcode", + &*module.name, + ); + // TODO(antoyo): maybe we should call embed_bitcode to have the proper iOS fixes? + //embed_bitcode(cgcx, llcx, llmod, &config.bc_cmdline, data); + + // TODO(antoyo): Send -plugin/usr/lib/gcc/x86_64-pc-linux-gnu/11.1.0/liblto_plugin.so to linker (this should be done when specifying the appropriate rustc cli argument). + context.compile_to_file( + OutputKind::ObjectFile, + bc_out.to_str().expect("path to str"), + ); + } } } @@ -123,6 +160,8 @@ pub(crate) unsafe fn codegen( // NOTE: without -fuse-linker-plugin, we get the following error: // lto1: internal compiler error: decompressed stream: Destination buffer is too small + // TODO(antoyo): since we do not do LTO when the linker is invoked anymore, perhaps + // the following flag is not necessary anymore. context.add_driver_option("-fuse-linker-plugin"); } @@ -131,11 +170,43 @@ pub(crate) unsafe fn codegen( // /usr/bin/ld: cannot find -lgcc_s: No such file or directory context.add_driver_option("-nostdlib"); - // NOTE: this doesn't actually generate an executable. With the above flags, it combines the .o files together in another .o. - context.compile_to_file( - OutputKind::Executable, - obj_out.to_str().expect("path to str"), - ); + let path = obj_out.to_str().expect("path to str"); + + if fat_lto { + let lto_path = format!("{}.lto", path); + // FIXME(antoyo): The LTO frontend generates the following warning: + // ../build_sysroot/sysroot_src/library/core/src/num/dec2flt/lemire.rs:150:15: warning: type of ‘_ZN4core3num7dec2flt5table17POWER_OF_FIVE_12817ha449a68fb31379e4E’ does not match original declaration [-Wlto-type-mismatch] + // 150 | let (lo5, hi5) = POWER_OF_FIVE_128[index]; + // | ^ + // lto1: note: ‘_ZN4core3num7dec2flt5table17POWER_OF_FIVE_12817ha449a68fb31379e4E’ was previously declared here + // + // This option is to mute it to make the UI tests pass with LTO enabled. + context.add_driver_option("-Wno-lto-type-mismatch"); + // NOTE: this doesn't actually generate an executable. With the above + // flags, it combines the .o files together in another .o. + context.compile_to_file(OutputKind::Executable, <o_path); + + let context = Context::default(); + if cgcx.target_arch == "x86" || cgcx.target_arch == "x86_64" { + // NOTE: it seems we need to use add_driver_option instead of + // add_command_line_option here because we use the LTO frontend via gcc. + context.add_driver_option("-masm=intel"); + } + + // NOTE: these two options are needed to invoke LTO to produce an object file. + // We need to initiate a second compilation because the arguments "-x lto" + // needs to be at the very beginning. + context.add_driver_option("-x"); + context.add_driver_option("lto"); + add_pic_option(&context, module.module_llvm.relocation_model); + context.add_driver_option(lto_path); + + context.compile_to_file(OutputKind::ObjectFile, path); + } else { + // NOTE: this doesn't actually generate an executable. With the above + // flags, it combines the .o files together in another .o. + context.compile_to_file(OutputKind::Executable, path); + } } else { context.compile_to_file( OutputKind::ObjectFile, diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs index 18aa32754e1d9..962f4b161d788 100644 --- a/compiler/rustc_codegen_gcc/src/base.rs +++ b/compiler/rustc_codegen_gcc/src/base.rs @@ -3,7 +3,7 @@ use std::env; use std::sync::Arc; use std::time::Instant; -use gccjit::{CType, FunctionType, GlobalKind}; +use gccjit::{CType, Context, FunctionType, GlobalKind}; use rustc_codegen_ssa::base::maybe_create_entry_wrapper; use rustc_codegen_ssa::mono_item::MonoItemExt; use rustc_codegen_ssa::traits::DebugInfoCodegenMethods; @@ -15,21 +15,32 @@ use rustc_middle::mir::mono::Visibility; use rustc_middle::ty::TyCtxt; use rustc_session::config::DebugInfo; use rustc_span::Symbol; -use rustc_target::spec::PanicStrategy; +#[cfg(feature = "master")] +use rustc_target::spec::SymbolVisibility; +use rustc_target::spec::{PanicStrategy, RelocModel}; use crate::builder::Builder; use crate::context::CodegenCx; use crate::{GccContext, LockedTargetInfo, SyncContext, gcc_util, new_context}; #[cfg(feature = "master")] -pub fn visibility_to_gcc(linkage: Visibility) -> gccjit::Visibility { - match linkage { +pub fn visibility_to_gcc(visibility: Visibility) -> gccjit::Visibility { + match visibility { Visibility::Default => gccjit::Visibility::Default, Visibility::Hidden => gccjit::Visibility::Hidden, Visibility::Protected => gccjit::Visibility::Protected, } } +#[cfg(feature = "master")] +pub fn symbol_visibility_to_gcc(visibility: SymbolVisibility) -> gccjit::Visibility { + match visibility { + SymbolVisibility::Hidden => gccjit::Visibility::Hidden, + SymbolVisibility::Protected => gccjit::Visibility::Protected, + SymbolVisibility::Interposable => gccjit::Visibility::Default, + } +} + pub fn global_linkage_to_gcc(linkage: Linkage) -> GlobalKind { match linkage { Linkage::External => GlobalKind::Imported, @@ -38,9 +49,7 @@ pub fn global_linkage_to_gcc(linkage: Linkage) -> GlobalKind { Linkage::LinkOnceODR => unimplemented!(), Linkage::WeakAny => unimplemented!(), Linkage::WeakODR => unimplemented!(), - Linkage::Appending => unimplemented!(), Linkage::Internal => GlobalKind::Internal, - Linkage::Private => GlobalKind::Internal, Linkage::ExternalWeak => GlobalKind::Imported, // TODO(antoyo): should be weak linkage. Linkage::Common => unimplemented!(), } @@ -55,9 +64,7 @@ pub fn linkage_to_gcc(linkage: Linkage) -> FunctionType { Linkage::LinkOnceODR => unimplemented!(), Linkage::WeakAny => FunctionType::Exported, // FIXME(antoyo): should be similar to linkonce. Linkage::WeakODR => unimplemented!(), - Linkage::Appending => unimplemented!(), Linkage::Internal => FunctionType::Internal, - Linkage::Private => FunctionType::Internal, Linkage::ExternalWeak => unimplemented!(), Linkage::Common => unimplemented!(), } @@ -140,9 +147,7 @@ pub fn compile_codegen_unit( }); } - if tcx.sess.relocation_model() == rustc_target::spec::RelocModel::Static { - context.add_command_line_option("-fno-pie"); - } + add_pic_option(&context, tcx.sess.relocation_model()); let target_cpu = gcc_util::target_cpu(tcx.sess); if target_cpu != "generic" { @@ -199,12 +204,13 @@ pub fn compile_codegen_unit( let f32_type_supported = target_info.supports_target_dependent_type(CType::Float32); let f64_type_supported = target_info.supports_target_dependent_type(CType::Float64); let f128_type_supported = target_info.supports_target_dependent_type(CType::Float128); + let u128_type_supported = target_info.supports_target_dependent_type(CType::UInt128t); // TODO: improve this to avoid passing that many arguments. let cx = CodegenCx::new( &context, cgu, tcx, - target_info.supports_128bit_int(), + u128_type_supported, f16_type_supported, f32_type_supported, f64_type_supported, @@ -235,6 +241,7 @@ pub fn compile_codegen_unit( name: cgu_name.to_string(), module_llvm: GccContext { context: Arc::new(SyncContext::new(context)), + relocation_model: tcx.sess.relocation_model(), should_combine_object_files: false, temp_dir: None, }, @@ -244,3 +251,24 @@ pub fn compile_codegen_unit( (module, cost) } + +pub fn add_pic_option<'gcc>(context: &Context<'gcc>, relocation_model: RelocModel) { + match relocation_model { + rustc_target::spec::RelocModel::Static => { + context.add_command_line_option("-fno-pie"); + context.add_driver_option("-fno-pie"); + } + rustc_target::spec::RelocModel::Pic => { + context.add_command_line_option("-fPIC"); + // NOTE: we use both add_command_line_option and add_driver_option because the usage in + // this module (compile_codegen_unit) requires add_command_line_option while the usage + // in the back::write module (codegen) requires add_driver_option. + context.add_driver_option("-fPIC"); + } + rustc_target::spec::RelocModel::Pie => { + context.add_command_line_option("-fPIE"); + context.add_driver_option("-fPIE"); + } + model => eprintln!("Unsupported relocation model: {:?}", model), + } +} diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 9a142326ad196..b8e37b604801f 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -29,7 +29,7 @@ use rustc_middle::ty::layout::{ use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_span::Span; use rustc_span::def_id::DefId; -use rustc_target::abi::call::FnAbi; +use rustc_target::callconv::FnAbi; use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, Target, WasmCAbi, X86Abi}; use crate::common::{SignType, TypeReflection, type_is_pointer}; @@ -155,14 +155,11 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { // NOTE: not sure why, but we have the wrong type here. let int_type = compare_exchange.get_param(2).to_rvalue().get_type(); let src = self.context.new_bitcast(self.location, src, int_type); - self.context.new_call(self.location, compare_exchange, &[ - dst, - expected, - src, - weak, - order, - failure_order, - ]) + self.context.new_call( + self.location, + compare_exchange, + &[dst, expected, src, weak, order, failure_order], + ) } pub fn assign(&self, lvalue: LValue<'gcc>, value: RValue<'gcc>) { @@ -668,6 +665,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { a + b } + // TODO(antoyo): should we also override the `unchecked_` versions? fn sub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { self.gcc_sub(a, b) } @@ -835,31 +833,6 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { set_rvalue_location(self, self.gcc_not(a)) } - fn unchecked_sadd(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - set_rvalue_location(self, self.gcc_add(a, b)) - } - - fn unchecked_uadd(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - set_rvalue_location(self, self.gcc_add(a, b)) - } - - fn unchecked_ssub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - set_rvalue_location(self, self.gcc_sub(a, b)) - } - - fn unchecked_usub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - // TODO(antoyo): should generate poison value? - set_rvalue_location(self, self.gcc_sub(a, b)) - } - - fn unchecked_smul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - set_rvalue_location(self, self.gcc_mul(a, b)) - } - - fn unchecked_umul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - set_rvalue_location(self, self.gcc_mul(a, b)) - } - fn fadd_fast(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> { // NOTE: it seems like we cannot enable fast-mode for a single operation in GCC. set_rvalue_location(self, lhs + rhs) @@ -1076,9 +1049,11 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { let align = dest.val.align.restrict_for_offset(dest.layout.field(self.cx(), 0).size); cg_elem.val.store(self, PlaceRef::new_sized_aligned(current_val, cg_elem.layout, align)); - let next = self.inbounds_gep(self.backend_type(cg_elem.layout), current.to_rvalue(), &[ - self.const_usize(1), - ]); + let next = self.inbounds_gep( + self.backend_type(cg_elem.layout), + current.to_rvalue(), + &[self.const_usize(1)], + ); self.llbb().add_assignment(self.location, current, next); self.br(header_bb); @@ -1102,18 +1077,24 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { val: RValue<'gcc>, ptr: RValue<'gcc>, align: Align, - _flags: MemFlags, + flags: MemFlags, ) -> RValue<'gcc> { let ptr = self.check_store(val, ptr); let destination = ptr.dereference(self.location); // NOTE: libgccjit does not support specifying the alignment on the assignment, so we cast // to type so it gets the proper alignment. let destination_type = destination.to_rvalue().get_type().unqualified(); - let aligned_type = destination_type.get_aligned(align.bytes()).make_pointer(); - let aligned_destination = self.cx.context.new_bitcast(self.location, ptr, aligned_type); - let aligned_destination = aligned_destination.dereference(self.location); - self.llbb().add_assignment(self.location, aligned_destination, val); - // TODO(antoyo): handle align and flags. + let align = if flags.contains(MemFlags::UNALIGNED) { 1 } else { align.bytes() }; + let mut modified_destination_type = destination_type.get_aligned(align); + if flags.contains(MemFlags::VOLATILE) { + modified_destination_type = modified_destination_type.make_volatile(); + } + + let modified_ptr = + self.cx.context.new_cast(self.location, ptr, modified_destination_type.make_pointer()); + let modified_destination = modified_ptr.dereference(self.location); + self.llbb().add_assignment(self.location, modified_destination, val); + // TODO(antoyo): handle `MemFlags::NONTEMPORAL`. // NOTE: dummy value here since it's never used. FIXME(antoyo): API should not return a value here? // When adding support for NONTEMPORAL, make sure to not just emit MOVNT on x86; see the // LLVM backend for details. @@ -1236,13 +1217,13 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn ptrtoint(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { - let usize_value = self.cx.const_bitcast(value, self.cx.type_isize()); + let usize_value = self.cx.context.new_cast(None, value, self.cx.type_isize()); self.intcast(usize_value, dest_ty, false) } fn inttoptr(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { let usize_value = self.intcast(value, self.cx.type_isize(), false); - self.cx.const_bitcast(usize_value, dest_ty) + self.cx.context.new_cast(None, usize_value, dest_ty) } fn bitcast(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { @@ -1901,6 +1882,15 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { v2: RValue<'gcc>, mask: RValue<'gcc>, ) -> RValue<'gcc> { + // NOTE: if the `mask` is a constant value, the following code will copy it in many places, + // which will make GCC create a lot (+4000) local variables in some cases. + // So we assign it to an explicit local variable once to avoid this. + let func = self.current_func(); + let mask_var = func.new_local(self.location, mask.get_type(), "mask"); + let block = self.block; + block.add_assignment(self.location, mask_var, mask); + let mask = mask_var.to_rvalue(); + // TODO(antoyo): use a recursive unqualified() here. let vector_type = v1.get_type().unqualified().dyncast_vector().expect("vector type"); let element_type = vector_type.get_element_type(); @@ -1917,18 +1907,35 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { self.int_type }; - let vector_type = - mask.get_type().dyncast_vector().expect("simd_shuffle mask should be of vector type"); - let mask_num_units = vector_type.get_num_units(); - let mut mask_elements = vec![]; - for i in 0..mask_num_units { - let index = self.context.new_rvalue_from_long(self.cx.type_u32(), i as _); - mask_elements.push(self.context.new_cast( - self.location, - self.extract_element(mask, index).to_rvalue(), - mask_element_type, - )); - } + // NOTE: this condition is needed because we call shuffle_vector in the implementation of + // simd_gather. + let mut mask_elements = if let Some(vector_type) = mask.get_type().dyncast_vector() { + let mask_num_units = vector_type.get_num_units(); + let mut mask_elements = vec![]; + for i in 0..mask_num_units { + let index = self.context.new_rvalue_from_long(self.cx.type_u32(), i as _); + mask_elements.push(self.context.new_cast( + self.location, + self.extract_element(mask, index).to_rvalue(), + mask_element_type, + )); + } + mask_elements + } else { + let struct_type = mask.get_type().is_struct().expect("mask should be of struct type"); + let mask_num_units = struct_type.get_field_count(); + let mut mask_elements = vec![]; + for i in 0..mask_num_units { + let field = struct_type.get_field(i as i32); + mask_elements.push(self.context.new_cast( + self.location, + mask.access_field(self.location, field).to_rvalue(), + mask_element_type, + )); + } + mask_elements + }; + let mask_num_units = mask_elements.len(); // NOTE: the mask needs to be the same length as the input vectors, so add the missing // elements in the mask if needed. diff --git a/compiler/rustc_codegen_gcc/src/callee.rs b/compiler/rustc_codegen_gcc/src/callee.rs index 65972a03e8355..c133ae4fcdd2a 100644 --- a/compiler/rustc_codegen_gcc/src/callee.rs +++ b/compiler/rustc_codegen_gcc/src/callee.rs @@ -72,95 +72,74 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>) attributes::from_fn_attrs(cx, func, instance); - let instance_def_id = instance.def_id(); - - // TODO(antoyo): set linkage and attributes. - - // Apply an appropriate linkage/visibility value to our item that we - // just declared. - // - // This is sort of subtle. Inside our codegen unit we started off - // compilation by predefining all our own `MonoItem` instances. That - // is, everything we're codegenning ourselves is already defined. That - // means that anything we're actually codegenning in this codegen unit - // will have hit the above branch in `get_declared_value`. As a result, - // we're guaranteed here that we're declaring a symbol that won't get - // defined, or in other words we're referencing a value from another - // codegen unit or even another crate. - // - // So because this is a foreign value we blanket apply an external - // linkage directive because it's coming from a different object file. - // The visibility here is where it gets tricky. This symbol could be - // referencing some foreign crate or foreign library (an `extern` - // block) in which case we want to leave the default visibility. We may - // also, though, have multiple codegen units. It could be a - // monomorphization, in which case its expected visibility depends on - // whether we are sharing generics or not. The important thing here is - // that the visibility we apply to the declaration is the same one that - // has been applied to the definition (wherever that definition may be). - let is_generic = instance.args.non_erasable_generics().next().is_some(); - - if is_generic { - // This is a monomorphization. Its expected visibility depends - // on whether we are in share-generics mode. - - if cx.tcx.sess.opts.share_generics() { - // We are in share_generics mode. - - if let Some(instance_def_id) = instance_def_id.as_local() { - // This is a definition from the current crate. If the - // definition is unreachable for downstream crates or - // the current crate does not re-export generics, the - // definition of the instance will have been declared - // as `hidden`. - if cx.tcx.is_unreachable_local_definition(instance_def_id) + #[cfg(feature = "master")] + { + let instance_def_id = instance.def_id(); + + // TODO(antoyo): set linkage and attributes. + + // Apply an appropriate linkage/visibility value to our item that we + // just declared. + // + // This is sort of subtle. Inside our codegen unit we started off + // compilation by predefining all our own `MonoItem` instances. That + // is, everything we're codegenning ourselves is already defined. That + // means that anything we're actually codegenning in this codegen unit + // will have hit the above branch in `get_declared_value`. As a result, + // we're guaranteed here that we're declaring a symbol that won't get + // defined, or in other words we're referencing a value from another + // codegen unit or even another crate. + // + // So because this is a foreign value we blanket apply an external + // linkage directive because it's coming from a different object file. + // The visibility here is where it gets tricky. This symbol could be + // referencing some foreign crate or foreign library (an `extern` + // block) in which case we want to leave the default visibility. We may + // also, though, have multiple codegen units. It could be a + // monomorphization, in which case its expected visibility depends on + // whether we are sharing generics or not. The important thing here is + // that the visibility we apply to the declaration is the same one that + // has been applied to the definition (wherever that definition may be). + let is_generic = instance.args.non_erasable_generics().next().is_some(); + + let is_hidden = if is_generic { + // This is a monomorphization of a generic function. + if !(cx.tcx.sess.opts.share_generics() + || tcx.codegen_fn_attrs(instance_def_id).inline + == rustc_attr_parsing::InlineAttr::Never) + { + // When not sharing generics, all instances are in the same + // crate and have hidden visibility. + true + } else if let Some(instance_def_id) = instance_def_id.as_local() { + // This is a monomorphization of a generic function + // defined in the current crate. It is hidden if: + // - the definition is unreachable for downstream + // crates, or + // - the current crate does not re-export generics + // (because the crate is a C library or executable) + cx.tcx.is_unreachable_local_definition(instance_def_id) || !cx.tcx.local_crate_exports_generics() - { - #[cfg(feature = "master")] - func.add_attribute(FnAttribute::Visibility(Visibility::Hidden)); - } } else { // This is a monomorphization of a generic function - // defined in an upstream crate. - if instance.upstream_monomorphization(tcx).is_some() { - // This is instantiated in another crate. It cannot - // be `hidden`. - } else { - // This is a local instantiation of an upstream definition. - // If the current crate does not re-export it - // (because it is a C library or an executable), it - // will have been declared `hidden`. - if !cx.tcx.local_crate_exports_generics() { - #[cfg(feature = "master")] - func.add_attribute(FnAttribute::Visibility(Visibility::Hidden)); - } - } + // defined in an upstream crate. It is hidden if: + // - it is instantiated in this crate, and + // - the current crate does not re-export generics + instance.upstream_monomorphization(tcx).is_none() + && !cx.tcx.local_crate_exports_generics() } } else { - // When not sharing generics, all instances are in the same - // crate and have hidden visibility - #[cfg(feature = "master")] + // This is a non-generic function. It is hidden if: + // - it is instantiated in the local crate, and + // - it is defined an upstream crate (non-local), or + // - it is not reachable + cx.tcx.is_codegened_item(instance_def_id) + && (!instance_def_id.is_local() + || !cx.tcx.is_reachable_non_generic(instance_def_id)) + }; + if is_hidden { func.add_attribute(FnAttribute::Visibility(Visibility::Hidden)); } - } else { - // This is a non-generic function - if cx.tcx.is_codegened_item(instance_def_id) { - // This is a function that is instantiated in the local crate - - if instance_def_id.is_local() { - // This is function that is defined in the local crate. - // If it is not reachable, it is hidden. - if !cx.tcx.is_reachable_non_generic(instance_def_id) { - #[cfg(feature = "master")] - func.add_attribute(FnAttribute::Visibility(Visibility::Hidden)); - } - } else { - // This is a function from an upstream crate that has - // been instantiated here. These are always hidden. - #[cfg(feature = "master")] - func.add_attribute(FnAttribute::Visibility(Visibility::Hidden)); - } - } } func diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 0d3e7083d56ad..2052a6aa21597 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -229,7 +229,12 @@ impl<'gcc, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { GlobalAlloc::VTable(ty, dyn_ty) => { let alloc = self .tcx - .global_alloc(self.tcx.vtable_allocation((ty, dyn_ty.principal()))) + .global_alloc(self.tcx.vtable_allocation(( + ty, + dyn_ty.principal().map(|principal| { + self.tcx.instantiate_bound_regions_with_erased(principal) + }), + ))) .unwrap_memory(); let init = const_alloc_to_gcc(self, alloc); self.static_addr_of(init, alloc.inner().align, None) @@ -240,14 +245,14 @@ impl<'gcc, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { } }; let ptr_type = base_addr.get_type(); - let base_addr = self.const_bitcast(base_addr, self.usize_type); + let base_addr = self.context.new_cast(None, base_addr, self.usize_type); let offset = self.context.new_rvalue_from_long(self.usize_type, offset.bytes() as i64); - let ptr = self.const_bitcast(base_addr + offset, ptr_type); + let ptr = self.context.new_cast(None, base_addr + offset, ptr_type); if !matches!(layout.primitive(), Pointer(_)) { self.const_bitcast(ptr.dereference(None).to_rvalue(), ty) } else { - self.const_bitcast(ptr, ty) + self.context.new_cast(None, ptr, ty) } } } diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index 6dc2f4ed66881..fb0ca31c54334 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -1,6 +1,7 @@ #[cfg(feature = "master")] use gccjit::{FnAttribute, VarAttribute, Visibility}; use gccjit::{Function, GlobalKind, LValue, RValue, ToRValue, Type}; +use rustc_abi::{self as abi, Align, HasDataLayout, Primitive, Size, WrappingRange}; use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods, }; @@ -14,7 +15,6 @@ use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Instance}; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::DefId; -use rustc_target::abi::{self, Align, HasDataLayout, Primitive, Size, WrappingRange}; use crate::base; use crate::context::CodegenCx; @@ -252,7 +252,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { let global = self.declare_global( sym, gcc_type, - GlobalKind::Exported, + GlobalKind::Imported, is_tls, fn_attrs.link_section, ); @@ -404,7 +404,6 @@ fn check_and_apply_linkage<'gcc, 'tcx>( // TODO(antoyo): set linkage. let value = cx.const_ptrcast(global1.get_address(None), gcc_type); global2.global_set_initializer_rvalue(value); - // TODO(antoyo): use global_set_initializer() when it will work. global2 } else { // Generate an external declaration. diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index f67dcf0cb116d..1e1f577bb3a15 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -3,6 +3,7 @@ use std::cell::{Cell, RefCell}; use gccjit::{ Block, CType, Context, Function, FunctionPtrType, FunctionType, LValue, Location, RValue, Type, }; +use rustc_abi::{HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx}; use rustc_codegen_ssa::base::wants_msvc_seh; use rustc_codegen_ssa::errors as ssa_errors; use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeCodegenMethods, MiscCodegenMethods}; @@ -14,11 +15,10 @@ use rustc_middle::ty::layout::{ FnAbiError, FnAbiOf, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, HasTypingEnv, LayoutError, LayoutOfHelpers, }; -use rustc_middle::ty::{self, Instance, PolyExistentialTraitRef, Ty, TyCtxt}; +use rustc_middle::ty::{self, ExistentialTraitRef, Instance, Ty, TyCtxt}; use rustc_session::Session; use rustc_span::source_map::respan; use rustc_span::{DUMMY_SP, Span}; -use rustc_target::abi::{HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx}; use rustc_target::spec::{ HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, Target, TlsModel, WasmCAbi, X86Abi, }; @@ -90,7 +90,7 @@ pub struct CodegenCx<'gcc, 'tcx> { pub function_instances: RefCell, Function<'gcc>>>, /// Cache generated vtables pub vtables: - RefCell, Option>), RValue<'gcc>>>, + RefCell, Option>), RValue<'gcc>>>, // TODO(antoyo): improve the SSA API to not require those. /// Mapping from function pointer type to indexes of on stack parameters. @@ -386,6 +386,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { impl<'gcc, 'tcx> BackendTypes for CodegenCx<'gcc, 'tcx> { type Value = RValue<'gcc>; type Metadata = RValue<'gcc>; + // TODO(antoyo): change to Function<'gcc>. type Function = RValue<'gcc>; type BasicBlock = Block<'gcc>; @@ -400,7 +401,7 @@ impl<'gcc, 'tcx> BackendTypes for CodegenCx<'gcc, 'tcx> { impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn vtables( &self, - ) -> &RefCell, Option>), RValue<'gcc>>> { + ) -> &RefCell, Option>), RValue<'gcc>>> { &self.vtables } @@ -512,7 +513,6 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { } else { // If the symbol already exists, it is an error: for example, the user wrote // #[no_mangle] extern "C" fn main(..) {..} - // instead of #[start] None } } diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs index 6aeb656c1ab40..55e01687400aa 100644 --- a/compiler/rustc_codegen_gcc/src/debuginfo.rs +++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs @@ -1,17 +1,17 @@ use std::ops::Range; +use std::sync::Arc; use gccjit::{Location, RValue}; +use rustc_abi::Size; use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext, VariableKind}; use rustc_codegen_ssa::traits::{DebugInfoBuilderMethods, DebugInfoCodegenMethods}; -use rustc_data_structures::sync::Lrc; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::{self, Body, SourceScope}; -use rustc_middle::ty::{Instance, PolyExistentialTraitRef, Ty}; +use rustc_middle::ty::{ExistentialTraitRef, Instance, Ty}; use rustc_session::config::DebugInfo; use rustc_span::{BytePos, Pos, SourceFile, SourceFileAndLine, Span, Symbol}; -use rustc_target::abi::Size; -use rustc_target::abi::call::FnAbi; +use rustc_target::callconv::FnAbi; use crate::builder::Builder; use crate::context::CodegenCx; @@ -69,7 +69,7 @@ fn compute_mir_scopes<'gcc, 'tcx>( ) { // Find all scopes with variables defined in them. let variables = if cx.sess().opts.debuginfo == DebugInfo::Full { - let mut vars = BitSet::new_empty(mir.source_scopes.len()); + let mut vars = DenseBitSet::new_empty(mir.source_scopes.len()); // FIXME(eddyb) take into account that arguments always have debuginfo, // irrespective of their name (assuming full debuginfo is enabled). // NOTE(eddyb) actually, on second thought, those are always in the @@ -82,7 +82,7 @@ fn compute_mir_scopes<'gcc, 'tcx>( // Nothing to emit, of course. None }; - let mut instantiated = BitSet::new_empty(mir.source_scopes.len()); + let mut instantiated = DenseBitSet::new_empty(mir.source_scopes.len()); // Instantiate all scopes. for idx in 0..mir.source_scopes.len() { let scope = SourceScope::new(idx); @@ -101,9 +101,9 @@ fn make_mir_scope<'gcc, 'tcx>( cx: &CodegenCx<'gcc, 'tcx>, _instance: Instance<'tcx>, mir: &Body<'tcx>, - variables: &Option>, + variables: &Option>, debug_context: &mut FunctionDebugContext<'tcx, (), Location<'gcc>>, - instantiated: &mut BitSet, + instantiated: &mut DenseBitSet, scope: SourceScope, ) { if instantiated.contains(scope) { @@ -113,15 +113,15 @@ fn make_mir_scope<'gcc, 'tcx>( let scope_data = &mir.source_scopes[scope]; let parent_scope = if let Some(parent) = scope_data.parent_scope { make_mir_scope(cx, _instance, mir, variables, debug_context, instantiated, parent); - debug_context.scopes[parent].unwrap() + debug_context.scopes[parent] } else { // The root is the function itself. let file = cx.sess().source_map().lookup_source_file(mir.span.lo()); - debug_context.scopes[scope] = Some(DebugScope { + debug_context.scopes[scope] = DebugScope { file_start_pos: file.start_pos, file_end_pos: file.end_position(), - ..debug_context.scopes[scope].unwrap() - }); + ..debug_context.scopes[scope] + }; instantiated.insert(scope); return; }; @@ -130,7 +130,7 @@ fn make_mir_scope<'gcc, 'tcx>( if !vars.contains(scope) && scope_data.inlined.is_none() { // Do not create a DIScope if there are no variables defined in this // MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat. - debug_context.scopes[scope] = Some(parent_scope); + debug_context.scopes[scope] = parent_scope; instantiated.insert(scope); return; } @@ -157,12 +157,12 @@ fn make_mir_scope<'gcc, 'tcx>( // TODO(tempdragon): dbg_scope: Add support for scope extension here. inlined_at.or(p_inlined_at); - debug_context.scopes[scope] = Some(DebugScope { + debug_context.scopes[scope] = DebugScope { dbg_scope, inlined_at, file_start_pos: loc.file.start_pos, file_end_pos: loc.file.end_position(), - }); + }; instantiated.insert(scope); } @@ -172,7 +172,7 @@ fn make_mir_scope<'gcc, 'tcx>( // `lookup_char_pos` return the right information instead. pub struct DebugLoc { /// Information about the original source file. - pub file: Lrc, + pub file: Arc, /// The (1-based) line number. pub line: u32, /// The (1-based) column number. @@ -214,7 +214,7 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn create_vtable_debuginfo( &self, _ty: Ty<'tcx>, - _trait_ref: Option>, + _trait_ref: Option>, _vtable: Self::Value, ) { // TODO(antoyo) @@ -232,12 +232,12 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { } // Initialize fn debug context (including scopes). - let empty_scope = Some(DebugScope { + let empty_scope = DebugScope { dbg_scope: self.dbg_scope_fn(instance, fn_abi, Some(llfn)), inlined_at: None, file_start_pos: BytePos(0), file_end_pos: BytePos(0), - }); + }; let mut fn_debug_context = FunctionDebugContext { scopes: IndexVec::from_elem(empty_scope, mir.source_scopes.as_slice()), inlined_function_scopes: Default::default(), diff --git a/compiler/rustc_codegen_gcc/src/declare.rs b/compiler/rustc_codegen_gcc/src/declare.rs index 442488b7fd650..7cdbe3c0c6290 100644 --- a/compiler/rustc_codegen_gcc/src/declare.rs +++ b/compiler/rustc_codegen_gcc/src/declare.rs @@ -4,7 +4,7 @@ use gccjit::{Function, FunctionType, GlobalKind, LValue, RValue, Type}; use rustc_codegen_ssa::traits::BaseTypeCodegenMethods; use rustc_middle::ty::Ty; use rustc_span::Symbol; -use rustc_target::abi::call::FnAbi; +use rustc_target::callconv::FnAbi; use crate::abi::{FnAbiGcc, FnAbiGccExt}; use crate::context::CodegenCx; diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs index 56849cc861098..1b59b9ac169ab 100644 --- a/compiler/rustc_codegen_gcc/src/errors.rs +++ b/compiler/rustc_codegen_gcc/src/errors.rs @@ -28,6 +28,7 @@ pub(crate) struct UnstableCTargetFeature<'a> { #[diag(codegen_gcc_forbidden_ctarget_feature)] pub(crate) struct ForbiddenCTargetFeature<'a> { pub feature: &'a str, + pub enabled: &'a str, pub reason: &'a str, } @@ -39,10 +40,6 @@ pub(crate) enum PossibleFeature<'a> { None, } -#[derive(Diagnostic)] -#[diag(codegen_gcc_lto_not_supported)] -pub(crate) struct LTONotSupported; - #[derive(Diagnostic)] #[diag(codegen_gcc_unwinding_inline_asm)] pub(crate) struct UnwindingInlineAsm { diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 058a874501b26..4e8c8aaaf5c8a 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -3,7 +3,7 @@ use gccjit::Context; use rustc_codegen_ssa::codegen_attrs::check_tied_features; use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable; use rustc_data_structures::fx::FxHashMap; -use rustc_middle::bug; +use rustc_data_structures::unord::UnordSet; use rustc_session::Session; use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES; use smallvec::{SmallVec, smallvec}; @@ -37,100 +37,103 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec return None, - Some(c @ ('+' | '-')) => c, - Some(_) => { - if diagnostics { - sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature: s }); - } - return None; - } - }; - // Get the backend feature name, if any. - // This excludes rustc-specific features, that do not get passed down to GCC. - let feature = backend_feature_name(s)?; - // Warn against use of GCC specific feature names on the CLI. - if diagnostics { - let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature); - match feature_state { - None => { - let rust_feature = - known_features.iter().find_map(|&(rust_feature, _, _)| { - let gcc_features = to_gcc_features(sess, rust_feature); - if gcc_features.contains(&feature) - && !gcc_features.contains(&rust_feature) - { - Some(rust_feature) - } else { - None - } - }); - let unknown_feature = if let Some(rust_feature) = rust_feature { - UnknownCTargetFeature { - feature, - rust_feature: PossibleFeature::Some { rust_feature }, - } - } else { - UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } - }; - sess.dcx().emit_warn(unknown_feature); - } - Some((_, stability, _)) => { - if let Err(reason) = - stability.toggle_allowed(&sess.target, enable_disable == '+') + // Compute implied features + let mut all_rust_features = vec![]; + for feature in sess.opts.cg.target_feature.split(',') { + if let Some(feature) = feature.strip_prefix('+') { + all_rust_features.extend( + UnordSet::from(sess.target.implied_target_features(std::iter::once(feature))) + .to_sorted_stable_ord() + .iter() + .map(|&&s| (true, s)), + ) + } else if let Some(feature) = feature.strip_prefix('-') { + // FIXME: Why do we not remove implied features on "-" here? + // We do the equivalent above in `target_features_cfg`. + // See . + all_rust_features.push((false, feature)); + } else if !feature.is_empty() && diagnostics { + sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature }); + } + } + // Remove features that are meant for rustc, not codegen. + all_rust_features.retain(|&(_, feature)| { + // Retain if it is not a rustc feature + !RUSTC_SPECIFIC_FEATURES.contains(&feature) + }); + + // Check feature validity. + if diagnostics { + for &(enable, feature) in &all_rust_features { + let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature); + match feature_state { + None => { + let rust_feature = known_features.iter().find_map(|&(rust_feature, _, _)| { + let gcc_features = to_gcc_features(sess, rust_feature); + if gcc_features.contains(&feature) && !gcc_features.contains(&rust_feature) { - sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason }); - } else if stability.requires_nightly().is_some() { - // An unstable feature. Warn about using it. (It makes little sense - // to hard-error here since we just warn about fully unknown - // features above). - sess.dcx().emit_warn(UnstableCTargetFeature { feature }); + Some(rust_feature) + } else { + None + } + }); + let unknown_feature = if let Some(rust_feature) = rust_feature { + UnknownCTargetFeature { + feature, + rust_feature: PossibleFeature::Some { rust_feature }, } + } else { + UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } + }; + sess.dcx().emit_warn(unknown_feature); + } + Some(&(_, stability, _)) => { + if let Err(reason) = stability.toggle_allowed() { + sess.dcx().emit_warn(ForbiddenCTargetFeature { + feature, + enabled: if enable { "enabled" } else { "disabled" }, + reason, + }); + } else if stability.requires_nightly().is_some() { + // An unstable feature. Warn about using it. (It makes little sense + // to hard-error here since we just warn about fully unknown + // features above). + sess.dcx().emit_warn(UnstableCTargetFeature { feature }); } } - - // FIXME(nagisa): figure out how to not allocate a full hashset here. - featsmap.insert(feature, enable_disable == '+'); } - // ... otherwise though we run through `to_gcc_features` when + // FIXME(nagisa): figure out how to not allocate a full hashset here. + featsmap.insert(feature, enable); + } + } + + // Translate this into GCC features. + let feats = + all_rust_features.iter().flat_map(|&(enable, feature)| { + let enable_disable = if enable { '+' } else { '-' }; + // We run through `to_gcc_features` when // passing requests down to GCC. This means that all in-language // features also work on the command line instead of having two // different names when the GCC name and the Rust name differ. - Some( - to_gcc_features(sess, feature) - .iter() - .flat_map(|feat| to_gcc_features(sess, feat).into_iter()) - .map(|feature| { - if enable_disable == '-' { - format!("-{}", feature) - } else { - feature.to_string() - } - }) - .collect::>(), - ) - }) - .flatten(); + to_gcc_features(sess, feature) + .iter() + .flat_map(|feat| to_gcc_features(sess, feat).into_iter()) + .map(|feature| { + if enable_disable == '-' { + format!("-{}", feature) + } else { + feature.to_string() + } + }) + .collect::>() + }); features.extend(feats); if diagnostics { @@ -146,26 +149,12 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec Option<&str> { - // features must start with a `+` or `-`. - let feature = s.strip_prefix(&['+', '-'][..]).unwrap_or_else(|| { - bug!("target feature `{}` must begin with a `+` or `-`", s); - }); - // Rustc-specific feature requests like `+crt-static` or `-crt-static` - // are not passed down to GCC. - if RUSTC_SPECIFIC_FEATURES.contains(&feature) { - return None; - } - Some(feature) -} - // To find a list of GCC's names, check https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html pub fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> { let arch = if sess.target.arch == "x86_64" { "x86" } else { &*sess.target.arch }; match (arch, s) { + // FIXME: seems like x87 does not exist? + ("x86", "x87") => smallvec![], ("x86", "sse4.2") => smallvec!["sse4.2", "crc32"], ("x86", "pclmulqdq") => smallvec!["pclmul"], ("x86", "rdrand") => smallvec!["rdrnd"], diff --git a/compiler/rustc_codegen_gcc/src/int.rs b/compiler/rustc_codegen_gcc/src/int.rs index 02b760dc7334c..f3552d9b12fcb 100644 --- a/compiler/rustc_codegen_gcc/src/int.rs +++ b/compiler/rustc_codegen_gcc/src/int.rs @@ -3,12 +3,11 @@ //! 128-bit integers on 32-bit platforms and thus require to be handled manually. use gccjit::{BinaryOp, ComparisonOp, FunctionType, Location, RValue, ToRValue, Type, UnaryOp}; +use rustc_abi::{Endian, ExternAbi}; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeCodegenMethods, BuilderMethods, OverflowOp}; use rustc_middle::ty::{self, Ty}; -use rustc_target::abi::Endian; -use rustc_target::abi::call::{ArgAbi, ArgAttributes, Conv, FnAbi, PassMode}; -use rustc_target::spec; +use rustc_target::callconv::{ArgAbi, ArgAttributes, Conv, FnAbi, PassMode}; use crate::builder::{Builder, ToGccComp}; use crate::common::{SignType, TypeReflection}; @@ -90,7 +89,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { } } } - } else if a_type.is_vector() && a_type.is_vector() { + } else if a_type.is_vector() && b_type.is_vector() { a >> b } else if a_native && !b_native { self.gcc_lshr(a, self.gcc_int_cast(b, a_type)) @@ -322,36 +321,26 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { }, } } else { - match new_kind { - Int(I128) | Uint(U128) => { - let func_name = match oop { - OverflowOp::Add => match new_kind { - Int(I128) => "__rust_i128_addo", - Uint(U128) => "__rust_u128_addo", - _ => unreachable!(), - }, - OverflowOp::Sub => match new_kind { - Int(I128) => "__rust_i128_subo", - Uint(U128) => "__rust_u128_subo", - _ => unreachable!(), - }, - OverflowOp::Mul => match new_kind { - Int(I128) => "__rust_i128_mulo", // TODO(antoyo): use __muloti4d instead? - Uint(U128) => "__rust_u128_mulo", - _ => unreachable!(), - }, - }; - return self.operation_with_overflow(func_name, lhs, rhs); - } - _ => match oop { - OverflowOp::Mul => match new_kind { - Int(I32) => "__mulosi4", - Int(I64) => "__mulodi4", - _ => unreachable!(), - }, - _ => unimplemented!("overflow operation for {:?}", new_kind), + let (func_name, width) = match oop { + OverflowOp::Add => match new_kind { + Int(I128) => ("__rust_i128_addo", 128), + Uint(U128) => ("__rust_u128_addo", 128), + _ => unreachable!(), }, - } + OverflowOp::Sub => match new_kind { + Int(I128) => ("__rust_i128_subo", 128), + Uint(U128) => ("__rust_u128_subo", 128), + _ => unreachable!(), + }, + OverflowOp::Mul => match new_kind { + Int(I32) => ("__mulosi4", 32), + Int(I64) => ("__mulodi4", 64), + Int(I128) => ("__rust_i128_mulo", 128), // TODO(antoyo): use __muloti4d instead? + Uint(U128) => ("__rust_u128_mulo", 128), + _ => unreachable!(), + }, + }; + return self.operation_with_overflow(func_name, lhs, rhs, width); }; let intrinsic = self.context.get_builtin_function(name); @@ -364,80 +353,87 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { (res.dereference(self.location).to_rvalue(), overflow) } + /// Non-`__builtin_*` overflow operations with a `fn(T, T, &mut i32) -> T` signature. pub fn operation_with_overflow( &self, func_name: &str, lhs: RValue<'gcc>, rhs: RValue<'gcc>, + width: u64, ) -> (RValue<'gcc>, RValue<'gcc>) { let a_type = lhs.get_type(); let b_type = rhs.get_type(); debug_assert!(a_type.dyncast_array().is_some()); debug_assert!(b_type.dyncast_array().is_some()); + let overflow_type = self.i32_type; + let overflow_param_type = overflow_type.make_pointer(); + let res_type = a_type; + + let overflow_value = + self.current_func().new_local(self.location, overflow_type, "overflow"); + let overflow_addr = overflow_value.get_address(self.location); + let param_a = self.context.new_parameter(self.location, a_type, "a"); let param_b = self.context.new_parameter(self.location, b_type, "b"); - let result_field = self.context.new_field(self.location, a_type, "result"); - let overflow_field = self.context.new_field(self.location, self.bool_type, "overflow"); - - let ret_ty = Ty::new_tup(self.tcx, &[self.tcx.types.i128, self.tcx.types.bool]); + let param_overflow = + self.context.new_parameter(self.location, overflow_param_type, "overflow"); + + let a_elem_type = a_type.dyncast_array().expect("non-array a value"); + debug_assert!(a_elem_type.is_integral()); + let res_ty = match width { + 32 => self.tcx.types.i32, + 64 => self.tcx.types.i64, + 128 => self.tcx.types.i128, + _ => unreachable!("unexpected integer size"), + }; let layout = self .tcx - .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ret_ty)) + .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(res_ty)) .unwrap(); let arg_abi = ArgAbi { layout, mode: PassMode::Direct(ArgAttributes::new()) }; let mut fn_abi = FnAbi { - args: vec![arg_abi.clone(), arg_abi.clone()].into_boxed_slice(), + args: vec![arg_abi.clone(), arg_abi.clone(), arg_abi.clone()].into_boxed_slice(), ret: arg_abi, c_variadic: false, - fixed_count: 2, + fixed_count: 3, conv: Conv::C, can_unwind: false, }; - fn_abi.adjust_for_foreign_abi(self.cx, spec::abi::Abi::C { unwind: false }).unwrap(); - - let indirect = matches!(fn_abi.ret.mode, PassMode::Indirect { .. }); - - let return_type = self - .context - .new_struct_type(self.location, "result_overflow", &[result_field, overflow_field]); - let result = if indirect { - let return_value = - self.current_func().new_local(self.location, return_type.as_type(), "return_value"); - let return_param_type = return_type.as_type().make_pointer(); - let return_param = - self.context.new_parameter(self.location, return_param_type, "return_value"); + fn_abi.adjust_for_foreign_abi(self.cx, ExternAbi::C { unwind: false }); + + let ret_indirect = matches!(fn_abi.ret.mode, PassMode::Indirect { .. }); + + let result = if ret_indirect { + let res_value = self.current_func().new_local(self.location, res_type, "result_value"); + let res_addr = res_value.get_address(self.location); + let res_param_type = res_type.make_pointer(); + let param_res = self.context.new_parameter(self.location, res_param_type, "result"); + let func = self.context.new_function( self.location, FunctionType::Extern, self.type_void(), - &[return_param, param_a, param_b], + &[param_res, param_a, param_b, param_overflow], func_name, false, ); - self.llbb().add_eval( - self.location, - self.context.new_call(self.location, func, &[ - return_value.get_address(self.location), - lhs, - rhs, - ]), - ); - return_value.to_rvalue() + let _void = + self.context.new_call(self.location, func, &[res_addr, lhs, rhs, overflow_addr]); + res_value.to_rvalue() } else { let func = self.context.new_function( self.location, FunctionType::Extern, - return_type.as_type(), - &[param_a, param_b], + res_type, + &[param_a, param_b, param_overflow], func_name, false, ); - self.context.new_call(self.location, func, &[lhs, rhs]) + self.context.new_call(self.location, func, &[lhs, rhs, overflow_addr]) }; - let overflow = result.access_field(self.location, overflow_field); - let int_result = result.access_field(self.location, result_field); - (int_result, overflow) + + (result, self.context.new_cast(self.location, overflow_value, self.bool_type).to_rvalue()) } pub fn gcc_icmp( @@ -660,7 +656,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { } } } - } else if a_type.is_vector() && a_type.is_vector() { + } else if a_type.is_vector() && b_type.is_vector() { a << b } else if a_native && !b_native { self.gcc_shl(a, self.gcc_int_cast(b, a_type)) diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs b/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs index 0a448ded6b1ae..2d731f88d7d36 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs @@ -421,7 +421,7 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>( | "__builtin_ia32_xsaveopt64" => { let new_args = args.to_vec(); let thirty_two = builder.context.new_rvalue_from_int(new_args[1].get_type(), 32); - let arg2 = new_args[1] << thirty_two | new_args[2]; + let arg2 = (new_args[1] << thirty_two) | new_args[2]; let arg2_type = gcc_func.get_param_type(1); let arg2 = builder.context.new_cast(None, arg2, arg2_type); args = vec![new_args[0], arg2].into(); @@ -687,11 +687,12 @@ pub fn adjust_intrinsic_return_value<'a, 'gcc, 'tcx>( let field2 = builder.context.new_field(None, args[1].get_type(), "carryResult"); let struct_type = builder.context.new_struct_type(None, "addcarryResult", &[field1, field2]); - return_value = - builder.context.new_struct_constructor(None, struct_type.as_type(), None, &[ - return_value, - last_arg.dereference(None).to_rvalue(), - ]); + return_value = builder.context.new_struct_constructor( + None, + struct_type.as_type(), + None, + &[return_value, last_arg.dereference(None).to_rvalue()], + ); } } "__builtin_ia32_stmxcsr" => { @@ -716,11 +717,12 @@ pub fn adjust_intrinsic_return_value<'a, 'gcc, 'tcx>( let field2 = builder.context.new_field(None, return_value.get_type(), "success"); let struct_type = builder.context.new_struct_type(None, "rdrand_result", &[field1, field2]); - return_value = - builder.context.new_struct_constructor(None, struct_type.as_type(), None, &[ - random_number, - success_variable.to_rvalue(), - ]); + return_value = builder.context.new_struct_constructor( + None, + struct_type.as_type(), + None, + &[random_number, success_variable.to_rvalue()], + ); } "fma" => { let f16_type = builder.context.new_c_type(CType::Float16); diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 78ec9741f5781..a1123fafe2f30 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -7,28 +7,29 @@ use std::iter; #[cfg(feature = "master")] use gccjit::FunctionType; use gccjit::{ComparisonOp, Function, RValue, ToRValue, Type, UnaryOp}; +#[cfg(feature = "master")] +use rustc_abi::ExternAbi; +use rustc_abi::HasDataLayout; use rustc_codegen_ssa::MemFlags; use rustc_codegen_ssa::base::wants_msvc_seh; use rustc_codegen_ssa::common::IntPredicate; use rustc_codegen_ssa::errors::InvalidMonomorphization; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue}; +#[cfg(feature = "master")] +use rustc_codegen_ssa::traits::MiscCodegenMethods; use rustc_codegen_ssa::traits::{ - ArgAbiBuilderMethods, BuilderMethods, ConstCodegenMethods, IntrinsicCallBuilderMethods, + ArgAbiBuilderMethods, BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods, + IntrinsicCallBuilderMethods, }; -#[cfg(feature = "master")] -use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, MiscCodegenMethods}; use rustc_middle::bug; -use rustc_middle::ty::layout::LayoutOf; #[cfg(feature = "master")] -use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv}; +use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt}; +use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, Instance, Ty}; use rustc_span::{Span, Symbol, sym}; -use rustc_target::abi::HasDataLayout; -use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode}; +use rustc_target::callconv::{ArgAbi, FnAbi, PassMode}; use rustc_target::spec::PanicStrategy; -#[cfg(feature = "master")] -use rustc_target::spec::abi::Abi; #[cfg(feature = "master")] use crate::abi::FnAbiGccExt; @@ -139,6 +140,18 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc &args.iter().map(|arg| arg.immediate()).collect::>(), ) } + sym::fmaf16 => { + // TODO(antoyo): use the correct builtin for f16. + let func = self.cx.context.get_builtin_function("fmaf"); + let args: Vec<_> = args + .iter() + .map(|arg| { + self.cx.context.new_cast(self.location, arg.immediate(), self.cx.type_f32()) + }) + .collect(); + let result = self.cx.context.new_call(self.location, func, &args); + self.cx.context.new_cast(self.location, result, self.cx.type_f16()) + } sym::is_val_statically_known => { let a = args[0].immediate(); let builtin = self.context.get_builtin_function("__builtin_constant_p"); @@ -988,7 +1001,8 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { 128 => "__rust_i128_addo", _ => unreachable!(), }; - let (int_result, overflow) = self.operation_with_overflow(func_name, lhs, rhs); + let (int_result, overflow) = + self.operation_with_overflow(func_name, lhs, rhs, width); self.llbb().add_assignment(self.location, res, int_result); overflow }; @@ -1058,7 +1072,8 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { 128 => "__rust_i128_subo", _ => unreachable!(), }; - let (int_result, overflow) = self.operation_with_overflow(func_name, lhs, rhs); + let (int_result, overflow) = + self.operation_with_overflow(func_name, lhs, rhs, width); self.llbb().add_assignment(self.location, res, int_result); overflow }; @@ -1223,7 +1238,7 @@ fn get_rust_try_fn<'a, 'gcc, 'tcx>( tcx.types.unit, false, rustc_hir::Safety::Unsafe, - Abi::Rust, + ExternAbi::Rust, )), ); // `unsafe fn(*mut i8, *mut i8) -> ()` @@ -1234,7 +1249,7 @@ fn get_rust_try_fn<'a, 'gcc, 'tcx>( tcx.types.unit, false, rustc_hir::Safety::Unsafe, - Abi::Rust, + ExternAbi::Rust, )), ); // `unsafe fn(unsafe fn(*mut i8) -> (), *mut i8, unsafe fn(*mut i8, *mut i8) -> ()) -> i32` @@ -1243,7 +1258,7 @@ fn get_rust_try_fn<'a, 'gcc, 'tcx>( tcx.types.i32, false, rustc_hir::Safety::Unsafe, - Abi::Rust, + ExternAbi::Rust, )); let rust_try = gen_fn(cx, "__rust_try", rust_fn_sig, codegen); cx.rust_try_fn.set(Some(rust_try)); diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index 79d1a06dd467f..84cd5b002fbb6 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -3,6 +3,7 @@ use std::iter::FromIterator; use gccjit::{BinaryOp, RValue, ToRValue, Type}; #[cfg(feature = "master")] use gccjit::{ComparisonOp, UnaryOp}; +use rustc_abi::{Align, Size}; use rustc_codegen_ssa::base::compare_simd_types; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; #[cfg(feature = "master")] @@ -17,7 +18,6 @@ use rustc_middle::mir::BinOp; use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, Symbol, sym}; -use rustc_target::abi::{Align, Size}; use crate::builder::Builder; #[cfg(not(feature = "master"))] @@ -62,11 +62,10 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( let arg_tys = sig.inputs(); if name == sym::simd_select_bitmask { - require_simd!(arg_tys[1], InvalidMonomorphization::SimdArgument { - span, - name, - ty: arg_tys[1] - }); + require_simd!( + arg_tys[1], + InvalidMonomorphization::SimdArgument { span, name, ty: arg_tys[1] } + ); let (len, _) = arg_tys[1].simd_size_and_type(bx.tcx()); let expected_int_bits = (len.max(8) - 1).next_power_of_two(); @@ -140,14 +139,17 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx()); - require!(in_len == out_len, InvalidMonomorphization::ReturnLengthInputType { - span, - name, - in_len, - in_ty, - ret_ty, - out_len - }); + require!( + in_len == out_len, + InvalidMonomorphization::ReturnLengthInputType { + span, + name, + in_len, + in_ty, + ret_ty, + out_len + } + ); require!( bx.type_kind(bx.element_type(llret_ty)) == TypeKind::Integer, InvalidMonomorphization::ReturnIntegerType { span, name, ret_ty, out_ty } @@ -269,23 +271,17 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( let lo_nibble = bx.context.new_rvalue_from_vector(None, long_byte_vector_type, &lo_nibble_elements); - let mask = bx.context.new_rvalue_from_vector(None, long_byte_vector_type, &vec![ - bx.context - .new_rvalue_from_int( - bx.u8_type, 0x0f - ); - byte_vector_type_size - as _ - ]); - - let four_vec = bx.context.new_rvalue_from_vector(None, long_byte_vector_type, &vec![ - bx.context - .new_rvalue_from_int( - bx.u8_type, 4 - ); - byte_vector_type_size - as _ - ]); + let mask = bx.context.new_rvalue_from_vector( + None, + long_byte_vector_type, + &vec![bx.context.new_rvalue_from_int(bx.u8_type, 0x0f); byte_vector_type_size as _], + ); + + let four_vec = bx.context.new_rvalue_from_vector( + None, + long_byte_vector_type, + &vec![bx.context.new_rvalue_from_int(bx.u8_type, 4); byte_vector_type_size as _], + ); // Step 2: Byte-swap the input. let swapped = simd_bswap(bx, args[0].immediate()); @@ -379,7 +375,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( // Make sure this is actually a SIMD vector. let idx_ty = args[2].layout.ty; let n: u64 = if idx_ty.is_simd() - && matches!(idx_ty.simd_size_and_type(bx.cx.tcx).1.kind(), ty::Uint(ty::UintTy::U32)) + && matches!(*idx_ty.simd_size_and_type(bx.cx.tcx).1.kind(), ty::Uint(ty::UintTy::U32)) { idx_ty.simd_size_and_type(bx.cx.tcx).0 } else { @@ -388,21 +384,14 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx()); - require!(out_len == n, InvalidMonomorphization::ReturnLength { - span, - name, - in_len: n, - ret_ty, - out_len - }); - require!(in_elem == out_ty, InvalidMonomorphization::ReturnElement { - span, - name, - in_elem, - in_ty, - ret_ty, - out_ty - }); + require!( + out_len == n, + InvalidMonomorphization::ReturnLength { span, name, in_len: n, ret_ty, out_len } + ); + require!( + in_elem == out_ty, + InvalidMonomorphization::ReturnElement { span, name, in_elem, in_ty, ret_ty, out_ty } + ); let vector = args[2].immediate(); @@ -411,13 +400,16 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( #[cfg(feature = "master")] if name == sym::simd_insert { - require!(in_elem == arg_tys[2], InvalidMonomorphization::InsertedType { - span, - name, - in_elem, - in_ty, - out_ty: arg_tys[2] - }); + require!( + in_elem == arg_tys[2], + InvalidMonomorphization::InsertedType { + span, + name, + in_elem, + in_ty, + out_ty: arg_tys[2] + } + ); let vector = args[0].immediate(); let index = args[1].immediate(); let value = args[2].immediate(); @@ -431,13 +423,10 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( #[cfg(feature = "master")] if name == sym::simd_extract { - require!(ret_ty == in_elem, InvalidMonomorphization::ReturnType { - span, - name, - in_elem, - in_ty, - ret_ty - }); + require!( + ret_ty == in_elem, + InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty } + ); let vector = args[0].immediate(); return Ok(bx.context.new_vector_access(None, vector, args[1].immediate()).to_rvalue()); } @@ -445,18 +434,15 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( if name == sym::simd_select { let m_elem_ty = in_elem; let m_len = in_len; - require_simd!(arg_tys[1], InvalidMonomorphization::SimdArgument { - span, - name, - ty: arg_tys[1] - }); + require_simd!( + arg_tys[1], + InvalidMonomorphization::SimdArgument { span, name, ty: arg_tys[1] } + ); let (v_len, _) = arg_tys[1].simd_size_and_type(bx.tcx()); - require!(m_len == v_len, InvalidMonomorphization::MismatchedLengths { - span, - name, - m_len, - v_len - }); + require!( + m_len == v_len, + InvalidMonomorphization::MismatchedLengths { span, name, m_len, v_len } + ); match *m_elem_ty.kind() { ty::Int(_) => {} _ => return_error!(InvalidMonomorphization::MaskType { span, name, ty: m_elem_ty }), @@ -468,25 +454,27 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); let (out_len, out_elem) = ret_ty.simd_size_and_type(bx.tcx()); - require!(in_len == out_len, InvalidMonomorphization::ReturnLengthInputType { - span, - name, - in_len, - in_ty, - ret_ty, - out_len - }); + require!( + in_len == out_len, + InvalidMonomorphization::ReturnLengthInputType { + span, + name, + in_len, + in_ty, + ret_ty, + out_len + } + ); match *in_elem.kind() { ty::RawPtr(p_ty, _) => { let metadata = p_ty.ptr_metadata_ty(bx.tcx, |ty| { bx.tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), ty) }); - require!(metadata.is_unit(), InvalidMonomorphization::CastWidePointer { - span, - name, - ty: in_elem - }); + require!( + metadata.is_unit(), + InvalidMonomorphization::CastWidePointer { span, name, ty: in_elem } + ); } _ => { return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: in_elem }) @@ -497,11 +485,10 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( let metadata = p_ty.ptr_metadata_ty(bx.tcx, |ty| { bx.tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), ty) }); - require!(metadata.is_unit(), InvalidMonomorphization::CastWidePointer { - span, - name, - ty: out_elem - }); + require!( + metadata.is_unit(), + InvalidMonomorphization::CastWidePointer { span, name, ty: out_elem } + ); } _ => { return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: out_elem }) @@ -524,14 +511,17 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); let (out_len, out_elem) = ret_ty.simd_size_and_type(bx.tcx()); - require!(in_len == out_len, InvalidMonomorphization::ReturnLengthInputType { - span, - name, - in_len, - in_ty, - ret_ty, - out_len - }); + require!( + in_len == out_len, + InvalidMonomorphization::ReturnLengthInputType { + span, + name, + in_len, + in_ty, + ret_ty, + out_len + } + ); match *in_elem.kind() { ty::RawPtr(_, _) => {} @@ -560,14 +550,17 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); let (out_len, out_elem) = ret_ty.simd_size_and_type(bx.tcx()); - require!(in_len == out_len, InvalidMonomorphization::ReturnLengthInputType { - span, - name, - in_len, - in_ty, - ret_ty, - out_len - }); + require!( + in_len == out_len, + InvalidMonomorphization::ReturnLengthInputType { + span, + name, + in_len, + in_ty, + ret_ty, + out_len + } + ); match *in_elem.kind() { ty::Uint(ty::UintTy::Usize) => {} @@ -596,14 +589,17 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( if name == sym::simd_cast || name == sym::simd_as { require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); let (out_len, out_elem) = ret_ty.simd_size_and_type(bx.tcx()); - require!(in_len == out_len, InvalidMonomorphization::ReturnLengthInputType { - span, - name, - in_len, - in_ty, - ret_ty, - out_len - }); + require!( + in_len == out_len, + InvalidMonomorphization::ReturnLengthInputType { + span, + name, + in_len, + in_ty, + ret_ty, + out_len + } + ); // casting cares about nominal type, not just structural type if in_elem == out_elem { return Ok(args[0].immediate()); @@ -629,14 +625,17 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( match (in_style, out_style) { (Style::Unsupported, Style::Unsupported) => { - require!(false, InvalidMonomorphization::UnsupportedCast { - span, - name, - in_ty, - in_elem, - ret_ty, - out_elem - }); + require!( + false, + InvalidMonomorphization::UnsupportedCast { + span, + name, + in_ty, + in_elem, + ret_ty, + out_elem + } + ); } _ => return Ok(bx.context.convert_vector(None, args[0].immediate(), llret_ty)), } @@ -829,6 +828,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( | sym::simd_flog | sym::simd_floor | sym::simd_fma + | sym::simd_relaxed_fma | sym::simd_fpow | sym::simd_fpowi | sym::simd_fsin @@ -913,45 +913,47 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( // All types must be simd vector types require_simd!(in_ty, InvalidMonomorphization::SimdFirst { span, name, ty: in_ty }); - require_simd!(arg_tys[1], InvalidMonomorphization::SimdSecond { - span, - name, - ty: arg_tys[1] - }); - require_simd!(arg_tys[2], InvalidMonomorphization::SimdThird { - span, - name, - ty: arg_tys[2] - }); + require_simd!( + arg_tys[1], + InvalidMonomorphization::SimdSecond { span, name, ty: arg_tys[1] } + ); + require_simd!( + arg_tys[2], + InvalidMonomorphization::SimdThird { span, name, ty: arg_tys[2] } + ); require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); // Of the same length: let (out_len, _) = arg_tys[1].simd_size_and_type(bx.tcx()); let (out_len2, _) = arg_tys[2].simd_size_and_type(bx.tcx()); - require!(in_len == out_len, InvalidMonomorphization::SecondArgumentLength { - span, - name, - in_len, - in_ty, - arg_ty: arg_tys[1], - out_len - }); - require!(in_len == out_len2, InvalidMonomorphization::ThirdArgumentLength { - span, - name, - in_len, - in_ty, - arg_ty: arg_tys[2], - out_len: out_len2 - }); + require!( + in_len == out_len, + InvalidMonomorphization::SecondArgumentLength { + span, + name, + in_len, + in_ty, + arg_ty: arg_tys[1], + out_len + } + ); + require!( + in_len == out_len2, + InvalidMonomorphization::ThirdArgumentLength { + span, + name, + in_len, + in_ty, + arg_ty: arg_tys[2], + out_len: out_len2 + } + ); // The return type must match the first argument type - require!(ret_ty == in_ty, InvalidMonomorphization::ExpectedReturnType { - span, - name, - in_ty, - ret_ty - }); + require!( + ret_ty == in_ty, + InvalidMonomorphization::ExpectedReturnType { span, name, in_ty, ret_ty } + ); // This counts how many pointers fn ptr_count(t: Ty<'_>) -> usize { @@ -978,15 +980,18 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( (ptr_count(element_ty1), non_ptr(element_ty1)) } _ => { - require!(false, InvalidMonomorphization::ExpectedElementType { - span, - name, - expected_element: element_ty1, - second_arg: arg_tys[1], - in_elem, - in_ty, - mutability: ExpectedPointerMutability::Not, - }); + require!( + false, + InvalidMonomorphization::ExpectedElementType { + span, + name, + expected_element: element_ty1, + second_arg: arg_tys[1], + in_elem, + in_ty, + mutability: ExpectedPointerMutability::Not, + } + ); unreachable!(); } }; @@ -999,12 +1004,15 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( match *element_ty2.kind() { ty::Int(_) => (), _ => { - require!(false, InvalidMonomorphization::ThirdArgElementType { - span, - name, - expected_element: element_ty2, - third_arg: arg_tys[2] - }); + require!( + false, + InvalidMonomorphization::ThirdArgElementType { + span, + name, + expected_element: element_ty2, + third_arg: arg_tys[2] + } + ); } } @@ -1028,36 +1036,40 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( // All types must be simd vector types require_simd!(in_ty, InvalidMonomorphization::SimdFirst { span, name, ty: in_ty }); - require_simd!(arg_tys[1], InvalidMonomorphization::SimdSecond { - span, - name, - ty: arg_tys[1] - }); - require_simd!(arg_tys[2], InvalidMonomorphization::SimdThird { - span, - name, - ty: arg_tys[2] - }); + require_simd!( + arg_tys[1], + InvalidMonomorphization::SimdSecond { span, name, ty: arg_tys[1] } + ); + require_simd!( + arg_tys[2], + InvalidMonomorphization::SimdThird { span, name, ty: arg_tys[2] } + ); // Of the same length: let (element_len1, _) = arg_tys[1].simd_size_and_type(bx.tcx()); let (element_len2, _) = arg_tys[2].simd_size_and_type(bx.tcx()); - require!(in_len == element_len1, InvalidMonomorphization::SecondArgumentLength { - span, - name, - in_len, - in_ty, - arg_ty: arg_tys[1], - out_len: element_len1 - }); - require!(in_len == element_len2, InvalidMonomorphization::ThirdArgumentLength { - span, - name, - in_len, - in_ty, - arg_ty: arg_tys[2], - out_len: element_len2 - }); + require!( + in_len == element_len1, + InvalidMonomorphization::SecondArgumentLength { + span, + name, + in_len, + in_ty, + arg_ty: arg_tys[1], + out_len: element_len1 + } + ); + require!( + in_len == element_len2, + InvalidMonomorphization::ThirdArgumentLength { + span, + name, + in_len, + in_ty, + arg_ty: arg_tys[2], + out_len: element_len2 + } + ); // This counts how many pointers fn ptr_count(t: Ty<'_>) -> usize { @@ -1085,15 +1097,18 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( (ptr_count(element_ty1), non_ptr(element_ty1)) } _ => { - require!(false, InvalidMonomorphization::ExpectedElementType { - span, - name, - expected_element: element_ty1, - second_arg: arg_tys[1], - in_elem, - in_ty, - mutability: ExpectedPointerMutability::Mut, - }); + require!( + false, + InvalidMonomorphization::ExpectedElementType { + span, + name, + expected_element: element_ty1, + second_arg: arg_tys[1], + in_elem, + in_ty, + mutability: ExpectedPointerMutability::Mut, + } + ); unreachable!(); } }; @@ -1105,12 +1120,15 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( match *element_ty2.kind() { ty::Int(_) => (), _ => { - require!(false, InvalidMonomorphization::ThirdArgElementType { - span, - name, - expected_element: element_ty2, - third_arg: arg_tys[2] - }); + require!( + false, + InvalidMonomorphization::ThirdArgElementType { + span, + name, + expected_element: element_ty2, + third_arg: arg_tys[2] + } + ); } } @@ -1277,13 +1295,10 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( ($name:ident : $vec_op:expr, $float_reduce:ident, $ordered:expr, $op:ident, $identity:expr) => { if name == sym::$name { - require!(ret_ty == in_elem, InvalidMonomorphization::ReturnType { - span, - name, - in_elem, - in_ty, - ret_ty - }); + require!( + ret_ty == in_elem, + InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty } + ); return match *in_elem.kind() { ty::Int(_) | ty::Uint(_) => { let r = bx.vector_reduce_op(args[0].immediate(), $vec_op); @@ -1349,13 +1364,10 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( macro_rules! minmax_red { ($name:ident: $int_red:ident, $float_red:ident) => { if name == sym::$name { - require!(ret_ty == in_elem, InvalidMonomorphization::ReturnType { - span, - name, - in_elem, - in_ty, - ret_ty - }); + require!( + ret_ty == in_elem, + InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty } + ); return match *in_elem.kind() { ty::Int(_) | ty::Uint(_) => Ok(bx.$int_red(args[0].immediate())), ty::Float(_) => Ok(bx.$float_red(args[0].immediate())), @@ -1379,13 +1391,10 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( ($name:ident : $op:expr, $boolean:expr) => { if name == sym::$name { let input = if !$boolean { - require!(ret_ty == in_elem, InvalidMonomorphization::ReturnType { - span, - name, - in_elem, - in_ty, - ret_ty - }); + require!( + ret_ty == in_elem, + InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty } + ); args[0].immediate() } else { match *in_elem.kind() { diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 7329080ce1f71..6455bcec6851b 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -27,6 +27,8 @@ // Some "regular" crates we want to share with rustc extern crate object; extern crate smallvec; +// FIXME(antoyo): clippy bug: remove the #[allow] when it's fixed. +#[allow(unused_extern_crates)] extern crate tempfile; #[macro_use] extern crate tracing; @@ -88,7 +90,6 @@ use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex}; use back::lto::{ThinBuffer, ThinData}; -use errors::LTONotSupported; use gccjit::{CType, Context, OptimizationLevel}; #[cfg(feature = "master")] use gccjit::{TargetInfo, Version}; @@ -109,9 +110,10 @@ use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; use rustc_session::Session; -use rustc_session::config::{Lto, OptLevel, OutputFilenames}; +use rustc_session::config::{OptLevel, OutputFilenames}; use rustc_span::Symbol; use rustc_span::fatal_error::FatalError; +use rustc_target::spec::RelocModel; use tempfile::TempDir; use crate::back::lto::ModuleBuffer; @@ -141,11 +143,15 @@ impl TargetInfo { false } - fn supports_128bit_int(&self) -> bool { - self.supports_128bit_integers.load(Ordering::SeqCst) - } - - fn supports_target_dependent_type(&self, _typ: CType) -> bool { + fn supports_target_dependent_type(&self, typ: CType) -> bool { + match typ { + CType::UInt128t | CType::Int128t => { + if self.supports_128bit_integers.load(Ordering::SeqCst) { + return true; + } + } + _ => (), + } false } } @@ -166,10 +172,6 @@ impl LockedTargetInfo { self.info.lock().expect("lock").cpu_supports(feature) } - fn supports_128bit_int(&self) -> bool { - self.info.lock().expect("lock").supports_128bit_int() - } - fn supports_target_dependent_type(&self, typ: CType) -> bool { self.info.lock().expect("lock").supports_target_dependent_type(typ) } @@ -202,10 +204,6 @@ impl CodegenBackend for GccCodegenBackend { #[cfg(feature = "master")] gccjit::set_global_personality_function_name(b"rust_eh_personality\0"); - if sess.lto() == Lto::Thin { - sess.dcx().emit_warn(LTONotSupported {}); - } - #[cfg(not(feature = "master"))] { let temp_dir = TempDir::new().expect("cannot create temporary directory"); @@ -297,6 +295,7 @@ impl ExtraBackendMethods for GccCodegenBackend { ) -> Self::Module { let mut mods = GccContext { context: Arc::new(SyncContext::new(new_context(tcx))), + relocation_model: tcx.sess.relocation_model(), should_combine_object_files: false, temp_dir: None, }; @@ -328,6 +327,9 @@ impl ExtraBackendMethods for GccCodegenBackend { pub struct GccContext { context: Arc, + /// This field is needed in order to be able to set the flag -fPIC when necessary when doing + /// LTO. + relocation_model: RelocModel, should_combine_object_files: bool, // Temporary directory used by LTO. We keep it here so that it's not removed before linking. temp_dir: Option, @@ -442,7 +444,6 @@ impl WriteBackendMethods for GccCodegenBackend { } fn autodiff( _cgcx: &CodegenContext, - _tcx: TyCtxt<'_>, _module: &ModuleCodegen, _diff_fncs: Vec, _config: &ModuleConfig, @@ -475,7 +476,7 @@ fn to_gcc_opt_level(optlevel: Option) -> OptimizationLevel { Some(level) => match level { OptLevel::No => OptimizationLevel::None, OptLevel::Less => OptimizationLevel::Limited, - OptLevel::Default => OptimizationLevel::Standard, + OptLevel::More => OptimizationLevel::Standard, OptLevel::Aggressive => OptimizationLevel::Aggressive, OptLevel::Size | OptLevel::SizeMin => OptimizationLevel::Limited, }, @@ -492,10 +493,11 @@ fn target_features_cfg( sess.target .rust_target_features() .iter() - .filter(|(_, gate, _)| gate.in_cfg()) - .filter_map(|(feature, gate, _)| { - if sess.is_nightly_build() || allow_unstable || gate.requires_nightly().is_none() { - Some(*feature) + .filter_map(|&(feature, gate, _)| { + if allow_unstable + || (gate.in_cfg() && (sess.is_nightly_build() || gate.requires_nightly().is_none())) + { + Some(feature) } else { None } diff --git a/compiler/rustc_codegen_gcc/src/mono_item.rs b/compiler/rustc_codegen_gcc/src/mono_item.rs index 239902df7f048..a2df7b2596fcf 100644 --- a/compiler/rustc_codegen_gcc/src/mono_item.rs +++ b/compiler/rustc_codegen_gcc/src/mono_item.rs @@ -61,10 +61,7 @@ impl<'gcc, 'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { // compiler-rt, then we want to implicitly compile everything with hidden // visibility as we're going to link this object all over the place but // don't want the symbols to get exported. - if linkage != Linkage::Internal - && linkage != Linkage::Private - && self.tcx.is_compiler_builtins(LOCAL_CRATE) - { + if linkage != Linkage::Internal && self.tcx.is_compiler_builtins(LOCAL_CRATE) { #[cfg(feature = "master")] decl.add_attribute(FnAttribute::Visibility(gccjit::Visibility::Hidden)); } else { diff --git a/compiler/rustc_codegen_gcc/src/type_.rs b/compiler/rustc_codegen_gcc/src/type_.rs index 4ea5544721dea..cb08723431a9a 100644 --- a/compiler/rustc_codegen_gcc/src/type_.rs +++ b/compiler/rustc_codegen_gcc/src/type_.rs @@ -4,13 +4,13 @@ use std::convert::TryInto; #[cfg(feature = "master")] use gccjit::CType; use gccjit::{RValue, Struct, Type}; +use rustc_abi::{AddressSpace, Align, Integer, Size}; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, DerivedTypeCodegenMethods, TypeMembershipCodegenMethods, }; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::{bug, ty}; -use rustc_target::abi::{AddressSpace, Align, Integer, Size}; use crate::common::TypeReflection; use crate::context::CodegenCx; diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index 0efdf36da485e..8b8b54753e7fd 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -3,7 +3,9 @@ use std::fmt::Write; use gccjit::{Struct, Type}; use rustc_abi as abi; use rustc_abi::Primitive::*; -use rustc_abi::{BackendRepr, FieldsShape, Integer, PointeeInfo, Size, Variants}; +use rustc_abi::{ + BackendRepr, FieldsShape, Integer, PointeeInfo, Reg, Size, TyAbiInterface, Variants, +}; use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, DerivedTypeCodegenMethods, LayoutTypeCodegenMethods, }; @@ -11,8 +13,7 @@ use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, CoroutineArgsExt, Ty, TypeVisitableExt}; -use rustc_target::abi::TyAbiInterface; -use rustc_target::abi::call::{CastTarget, FnAbi, Reg}; +use rustc_target::callconv::{CastTarget, FnAbi}; use crate::abi::{FnAbiGcc, FnAbiGccExt, GccType}; use crate::context::CodegenCx; diff --git a/compiler/rustc_codegen_gcc/tests/failing-non-lto-tests.txt b/compiler/rustc_codegen_gcc/tests/failing-non-lto-tests.txt deleted file mode 100644 index 384dfdc26fb51..0000000000000 --- a/compiler/rustc_codegen_gcc/tests/failing-non-lto-tests.txt +++ /dev/null @@ -1,11 +0,0 @@ -tests/ui/issues/issue-44056.rs -tests/ui/lto/fat-lto.rs -tests/ui/lto/debuginfo-lto.rs -tests/ui/lto/lto-many-codegen-units.rs -tests/ui/lto/issue-100772.rs -tests/ui/lto/lto-rustc-loads-linker-plugin.rs -tests/ui/panic-runtime/lto-unwind.rs -tests/ui/sanitizer/issue-111184-cfi-coroutine-witness.rs -tests/ui/sepcomp/sepcomp-lib-lto.rs -tests/ui/lto/lto-opt-level-s.rs -tests/ui/lto/lto-opt-level-z.rs diff --git a/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt b/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt index 457072b1a5bcb..082958bfe1f85 100644 --- a/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt +++ b/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt @@ -69,20 +69,22 @@ tests/ui/mir/mir_heavy_promoted.rs tests/ui/consts/const_cmp_type_id.rs tests/ui/consts/issue-73976-monomorphic.rs tests/ui/consts/issue-94675.rs -tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail.rs -tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop.rs +tests/ui/traits/const-traits/const-drop-fail.rs +tests/ui/traits/const-traits/const-drop.rs tests/ui/runtime/on-broken-pipe/child-processes.rs -tests/ui/sanitizer/cfi-assoc-ty-lifetime-issue-123053.rs -tests/ui/sanitizer/cfi-async-closures.rs -tests/ui/sanitizer/cfi-closures.rs -tests/ui/sanitizer/cfi-complex-receiver.rs -tests/ui/sanitizer/cfi-coroutine.rs -tests/ui/sanitizer/cfi-drop-in-place.rs -tests/ui/sanitizer/cfi-drop-no-principal.rs -tests/ui/sanitizer/cfi-fn-ptr.rs -tests/ui/sanitizer/cfi-self-ref.rs -tests/ui/sanitizer/cfi-supertraits.rs -tests/ui/sanitizer/cfi-virtual-auto.rs +tests/ui/sanitizer/cfi/assoc-ty-lifetime-issue-123053.rs +tests/ui/sanitizer/cfi/async-closures.rs +tests/ui/sanitizer/cfi/closures.rs +tests/ui/sanitizer/cfi/complex-receiver.rs +tests/ui/sanitizer/cfi/coroutine.rs +tests/ui/sanitizer/cfi/drop-in-place.rs +tests/ui/sanitizer/cfi/drop-no-principal.rs +tests/ui/sanitizer/cfi/fn-ptr.rs +tests/ui/sanitizer/cfi/self-ref.rs +tests/ui/sanitizer/cfi/supertraits.rs +tests/ui/sanitizer/cfi/virtual-auto.rs +tests/ui/sanitizer/cfi/sized-associated-ty.rs +tests/ui/sanitizer/cfi/can-reveal-opaques.rs tests/ui/sanitizer/kcfi-mangling.rs tests/ui/statics/const_generics.rs tests/ui/backtrace/dylib-dep.rs @@ -91,6 +93,7 @@ tests/ui/delegation/fn-header.rs tests/ui/consts/zst_no_llvm_alloc.rs tests/ui/consts/const-eval/parse_ints.rs tests/ui/simd/intrinsic/generic-arithmetic-pass.rs +tests/ui/simd/intrinsic/generic-as.rs tests/ui/backtrace/backtrace.rs tests/ui/lifetimes/tail-expr-lock-poisoning.rs tests/ui/runtime/rt-explody-panic-payloads.rs @@ -118,5 +121,4 @@ tests/ui/codegen/equal-pointers-unequal/strict-provenance/print3.rs tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs tests/ui/codegen/equal-pointers-unequal/strict-provenance/segfault.rs tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs -tests/ui/sanitizer/cfi-sized-associated-ty.rs -tests/ui/sanitizer/cfi-can-reveal-opaques.rs +tests/ui/simd/simd-bitmask-notpow2.rs diff --git a/compiler/rustc_codegen_gcc/tests/failing-ui-tests12.txt b/compiler/rustc_codegen_gcc/tests/failing-ui-tests12.txt index 1d9bdaa552c6b..b10d4bc82aa8d 100644 --- a/compiler/rustc_codegen_gcc/tests/failing-ui-tests12.txt +++ b/compiler/rustc_codegen_gcc/tests/failing-ui-tests12.txt @@ -11,7 +11,6 @@ tests/ui/simd/array-type.rs tests/ui/simd/intrinsic/float-minmax-pass.rs tests/ui/simd/intrinsic/generic-arithmetic-pass.rs tests/ui/simd/intrinsic/generic-arithmetic-saturating-pass.rs -tests/ui/simd/intrinsic/generic-as.rs tests/ui/simd/intrinsic/generic-cast-pass.rs tests/ui/simd/intrinsic/generic-cast-pointer-width.rs tests/ui/simd/intrinsic/generic-comparison-pass.rs diff --git a/compiler/rustc_codegen_gcc/tests/hello-world/Cargo.lock b/compiler/rustc_codegen_gcc/tests/hello-world/Cargo.lock new file mode 100644 index 0000000000000..fe252db442539 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/hello-world/Cargo.lock @@ -0,0 +1,14 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "hello_world" +version = "0.0.0" +dependencies = [ + "mylib", +] + +[[package]] +name = "mylib" +version = "0.1.0" diff --git a/compiler/rustc_codegen_gcc/tests/hello-world/Cargo.toml b/compiler/rustc_codegen_gcc/tests/hello-world/Cargo.toml index 0b8cdc63fbe84..c6e22f642f673 100644 --- a/compiler/rustc_codegen_gcc/tests/hello-world/Cargo.toml +++ b/compiler/rustc_codegen_gcc/tests/hello-world/Cargo.toml @@ -1,4 +1,12 @@ [package] name = "hello_world" +edition = "2024" [dependencies] +mylib = { path = "mylib" } + +[profile.dev] +lto = "thin" + +[profile.release] +lto = "fat" diff --git a/compiler/rustc_codegen_gcc/tests/hello-world/mylib/Cargo.lock b/compiler/rustc_codegen_gcc/tests/hello-world/mylib/Cargo.lock new file mode 100644 index 0000000000000..c8a0bfc635474 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/hello-world/mylib/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "mylib" +version = "0.1.0" diff --git a/compiler/rustc_codegen_gcc/tests/hello-world/mylib/Cargo.toml b/compiler/rustc_codegen_gcc/tests/hello-world/mylib/Cargo.toml new file mode 100644 index 0000000000000..d15f62bfb6dca --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/hello-world/mylib/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "mylib" +version = "0.1.0" +authors = ["Antoni Boucher "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/compiler/rustc_codegen_gcc/tests/hello-world/mylib/src/lib.rs b/compiler/rustc_codegen_gcc/tests/hello-world/mylib/src/lib.rs new file mode 100644 index 0000000000000..8d3d111bd1993 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/hello-world/mylib/src/lib.rs @@ -0,0 +1,7 @@ +pub fn my_func(a: i32, b: i32) -> i32 { + let mut res = a; + for i in a..b { + res += i; + } + res +} diff --git a/compiler/rustc_codegen_gcc/tests/hello-world/src/main.rs b/compiler/rustc_codegen_gcc/tests/hello-world/src/main.rs index e7a11a969c037..71c78d364ac88 100644 --- a/compiler/rustc_codegen_gcc/tests/hello-world/src/main.rs +++ b/compiler/rustc_codegen_gcc/tests/hello-world/src/main.rs @@ -1,3 +1,5 @@ +use mylib::my_func; + fn main() { - println!("Hello, world!"); + println!("{}", my_func(5, 10)); } diff --git a/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs b/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs index aecea37ab5a91..64c932a265819 100644 --- a/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs +++ b/compiler/rustc_codegen_gcc/tests/lang_tests_common.rs @@ -1,10 +1,8 @@ //! The common code for `tests/lang_tests_*.rs` -use std::{ - env::{self, current_dir}, - path::{Path, PathBuf}, - process::Command, -}; +use std::env::{self, current_dir}; +use std::path::{Path, PathBuf}; +use std::process::Command; use boml::Toml; use lang_tester::LangTester; @@ -22,14 +20,20 @@ pub fn main_inner(profile: Profile) { let tempdir = TempDir::new().expect("temp dir"); let current_dir = current_dir().expect("current dir"); let current_dir = current_dir.to_str().expect("current dir").to_string(); - let toml = Toml::parse(include_str!("../config.toml")).expect("Failed to parse `config.toml`"); - let gcc_path = if let Ok(gcc_path) = toml.get_string("gcc-path") { - PathBuf::from(gcc_path.to_string()) - } else { - // then we try to retrieve it from the `target` folder. - let commit = include_str!("../libgccjit.version").trim(); - Path::new("build/libgccjit").join(commit) - }; + + let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); + + let gcc_path = std::fs::read_to_string(manifest_dir.join("config.toml")) + .ok() + .and_then(|v| { + let toml = Toml::parse(&v).expect("Failed to parse `config.toml`"); + toml.get_string("gcc-path").map(PathBuf::from).ok() + }) + .unwrap_or_else(|| { + // then we try to retrieve it from the `target` folder. + let commit = include_str!("../libgccjit.version").trim(); + Path::new("build/libgccjit").join(commit) + }); let gcc_path = Path::new(&gcc_path) .canonicalize() @@ -83,6 +87,8 @@ pub fn main_inner(profile: Profile) { &format!("{}/build/build_sysroot/sysroot/", current_dir), "-C", "link-arg=-lc", + "--extern", + "mini_core=target/out/libmini_core.rlib", "-o", exe.to_str().expect("to_str"), path.to_str().expect("to_str"), diff --git a/compiler/rustc_codegen_gcc/tests/run/abort1.rs b/compiler/rustc_codegen_gcc/tests/run/abort1.rs index 696197d73772f..385e41a68817f 100644 --- a/compiler/rustc_codegen_gcc/tests/run/abort1.rs +++ b/compiler/rustc_codegen_gcc/tests/run/abort1.rs @@ -3,11 +3,12 @@ // Run-time: // status: signal -#![feature(auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs)] +#![feature(auto_traits, lang_items, no_core, intrinsics, rustc_attrs)] #![allow(internal_features)] #![no_std] #![no_core] +#![no_main] /* * Core @@ -49,7 +50,7 @@ fn test_fail() -> ! { unsafe { intrinsics::abort() }; } -#[start] -fn main(mut argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { test_fail(); } diff --git a/compiler/rustc_codegen_gcc/tests/run/abort2.rs b/compiler/rustc_codegen_gcc/tests/run/abort2.rs index 714cd6c0f3817..6c66a930e0741 100644 --- a/compiler/rustc_codegen_gcc/tests/run/abort2.rs +++ b/compiler/rustc_codegen_gcc/tests/run/abort2.rs @@ -3,11 +3,12 @@ // Run-time: // status: signal -#![feature(auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs)] +#![feature(auto_traits, lang_items, no_core, intrinsics, rustc_attrs)] #![allow(internal_features)] #![no_std] #![no_core] +#![no_main] /* * Core @@ -50,8 +51,8 @@ fn fail() -> i32 { 0 } -#[start] -fn main(mut argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { fail(); 0 } diff --git a/compiler/rustc_codegen_gcc/tests/run/array.rs b/compiler/rustc_codegen_gcc/tests/run/array.rs index d8de9f28d4cb9..e18a4ced6bc46 100644 --- a/compiler/rustc_codegen_gcc/tests/run/array.rs +++ b/compiler/rustc_codegen_gcc/tests/run/array.rs @@ -7,38 +7,13 @@ // 5 // 10 -#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs)] -#![allow(internal_features)] +#![feature(no_core)] #![no_std] #![no_core] +#![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} -impl Copy for usize {} -impl Copy for i32 {} -impl Copy for u8 {} -impl Copy for i8 {} -impl Copy for i16 {} -impl Copy for *mut T {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} +extern crate mini_core; mod libc { #[link(name = "c")] @@ -48,190 +23,14 @@ mod libc { } } -#[lang = "index"] -pub trait Index { - type Output: ?Sized; - fn index(&self, index: Idx) -> &Self::Output; -} - -impl Index for [T; 3] { - type Output = T; - - fn index(&self, index: usize) -> &Self::Output { - &self[index] - } -} - -impl Index for [T] { - type Output = T; - - fn index(&self, index: usize) -> &Self::Output { - &self[index] - } -} - -#[lang = "drop_in_place"] -#[allow(unconditional_recursion)] -pub unsafe fn drop_in_place(to_drop: *mut T) { - // Code here does not matter - this is replaced by the - // real drop glue by the compiler. - drop_in_place(to_drop); -} - -#[lang = "panic"] -#[track_caller] -#[no_mangle] -pub fn panic(_msg: &'static str) -> ! { - unsafe { - libc::puts("Panicking\0" as *const str as *const u8); - intrinsics::abort(); - } -} - -#[lang = "panic_location"] -struct PanicLocation { - file: &'static str, - line: u32, - column: u32, -} - -#[lang = "panic_bounds_check"] -#[track_caller] -#[no_mangle] -fn panic_bounds_check(index: usize, len: usize) -> ! { - unsafe { - libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index); - intrinsics::abort(); - } -} - -mod intrinsics { - #[rustc_nounwind] - #[rustc_intrinsic] - #[rustc_intrinsic_must_be_overridden] - pub fn abort() -> ! { - loop {} - } -} - -#[lang = "add"] -trait Add { - type Output; - - fn add(self, rhs: RHS) -> Self::Output; -} - -impl Add for u8 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for i8 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for i32 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for usize { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for isize { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -#[lang = "sub"] -pub trait Sub { - type Output; - - fn sub(self, rhs: RHS) -> Self::Output; -} - -impl Sub for usize { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -impl Sub for isize { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -impl Sub for u8 { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -impl Sub for i8 { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -impl Sub for i16 { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -#[track_caller] -#[lang = "panic_const_add_overflow"] -pub fn panic_const_add_overflow() -> ! { - panic("attempt to add with overflow"); -} - -#[track_caller] -#[lang = "panic_const_sub_overflow"] -pub fn panic_const_sub_overflow() -> ! { - panic("attempt to subtract with overflow"); -} - -/* - * Code - */ - static mut ONE: usize = 1; fn make_array() -> [u8; 3] { [42, 10, 5] } -#[start] -fn main(argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { let array = [42, 7, 5]; let array2 = make_array(); unsafe { diff --git a/compiler/rustc_codegen_gcc/tests/run/assign.rs b/compiler/rustc_codegen_gcc/tests/run/assign.rs index 2a47f0c2966e5..4d414c577a657 100644 --- a/compiler/rustc_codegen_gcc/tests/run/assign.rs +++ b/compiler/rustc_codegen_gcc/tests/run/assign.rs @@ -6,10 +6,11 @@ // 10 #![allow(internal_features, unused_attributes)] -#![feature(auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs, track_caller)] +#![feature(auto_traits, lang_items, no_core, intrinsics, rustc_attrs, track_caller)] #![no_std] #![no_core] +#![no_main] /* * Core @@ -142,8 +143,8 @@ fn inc(num: isize) -> isize { } -#[start] -fn main(mut argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { argc = inc(argc); unsafe { libc::printf(b"%ld\n\0" as *const u8 as *const i8, argc); diff --git a/compiler/rustc_codegen_gcc/tests/run/closure.rs b/compiler/rustc_codegen_gcc/tests/run/closure.rs index b0d0ca4ee8dfa..c7a236f74f9e6 100644 --- a/compiler/rustc_codegen_gcc/tests/run/closure.rs +++ b/compiler/rustc_codegen_gcc/tests/run/closure.rs @@ -8,202 +8,23 @@ // Int argument: 2 // Both args: 11 -#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics, - unboxed_closures, rustc_attrs)] -#![allow(internal_features)] +#![feature(no_core)] #![no_std] #![no_core] +#![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} -impl Copy for usize {} -impl Copy for i32 {} -impl Copy for u32 {} -impl Copy for u8 {} -impl Copy for i8 {} -impl Copy for *mut T {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} +extern crate mini_core; mod libc { #[link(name = "c")] extern "C" { - pub fn puts(s: *const u8) -> i32; pub fn printf(format: *const i8, ...) -> i32; } } -#[lang = "index"] -pub trait Index { - type Output: ?Sized; - fn index(&self, index: Idx) -> &Self::Output; -} - -impl Index for [T; 3] { - type Output = T; - - fn index(&self, index: usize) -> &Self::Output { - &self[index] - } -} - -impl Index for [T] { - type Output = T; - - fn index(&self, index: usize) -> &Self::Output { - &self[index] - } -} - -#[lang = "drop_in_place"] -#[allow(unconditional_recursion)] -pub unsafe fn drop_in_place(to_drop: *mut T) { - // Code here does not matter - this is replaced by the - // real drop glue by the compiler. - drop_in_place(to_drop); -} - -#[lang = "panic_location"] -struct PanicLocation { - file: &'static str, - line: u32, - column: u32, -} - -#[lang = "panic_bounds_check"] -#[track_caller] -#[no_mangle] -fn panic_bounds_check(index: usize, len: usize) -> ! { - unsafe { - libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index); - intrinsics::abort(); - } -} - -mod intrinsics { - #[rustc_nounwind] - #[rustc_intrinsic] - #[rustc_intrinsic_must_be_overridden] - pub fn abort() -> ! { - loop {} - } -} - -#[lang = "tuple_trait"] -pub trait Tuple {} - -#[lang = "unsize"] -pub trait Unsize {} - -#[lang = "coerce_unsized"] -pub trait CoerceUnsized {} - -impl<'a, 'b: 'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} -impl<'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {} -impl, U: ?Sized> CoerceUnsized<*const U> for *const T {} -impl, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} - -#[lang = "fn_once"] -#[rustc_paren_sugar] -pub trait FnOnce { - #[lang = "fn_once_output"] - type Output; - - extern "rust-call" fn call_once(self, args: Args) -> Self::Output; -} - -#[lang = "fn_mut"] -#[rustc_paren_sugar] -pub trait FnMut: FnOnce { - extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output; -} - -#[lang = "add"] -trait Add { - type Output; - - fn add(self, rhs: RHS) -> Self::Output; -} - -impl Add for u8 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for i8 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for i32 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for usize { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for isize { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -#[lang = "panic"] -#[track_caller] #[no_mangle] -pub fn panic(_msg: &'static str) -> ! { - unsafe { - libc::puts("Panicking\0" as *const str as *const u8); - intrinsics::abort(); - } -} - -#[track_caller] -#[lang = "panic_const_add_overflow"] -pub fn panic_const_add_overflow() -> ! { - panic("attempt to add with overflow"); -} - -/* - * Code - */ - -#[start] -fn main(mut argc: isize, _argv: *const *const u8) -> isize { +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { let string = "Arg: %d\n\0"; let mut closure = || { unsafe { diff --git a/compiler/rustc_codegen_gcc/tests/run/condition.rs b/compiler/rustc_codegen_gcc/tests/run/condition.rs index 770b18a89e3e0..b02359702ed2e 100644 --- a/compiler/rustc_codegen_gcc/tests/run/condition.rs +++ b/compiler/rustc_codegen_gcc/tests/run/condition.rs @@ -5,306 +5,23 @@ // stdout: true // 1 -#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs)] -#![allow(internal_features)] +#![feature(no_core)] #![no_std] #![no_core] +#![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} -impl Copy for usize {} -impl Copy for u64 {} -impl Copy for i32 {} -impl Copy for u32 {} -impl Copy for bool {} -impl Copy for u16 {} -impl Copy for i16 {} -impl Copy for char {} -impl Copy for i8 {} -impl Copy for u8 {} -impl Copy for *mut T {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} +extern crate mini_core; mod libc { #[link(name = "c")] extern "C" { pub fn printf(format: *const i8, ...) -> i32; - pub fn puts(s: *const u8) -> i32; - } -} - -#[lang = "index"] -pub trait Index { - type Output: ?Sized; - fn index(&self, index: Idx) -> &Self::Output; -} - -impl Index for [T; 3] { - type Output = T; - - fn index(&self, index: usize) -> &Self::Output { - &self[index] - } -} - -impl Index for [T] { - type Output = T; - - fn index(&self, index: usize) -> &Self::Output { - &self[index] } } -#[lang = "drop_in_place"] -#[allow(unconditional_recursion)] -pub unsafe fn drop_in_place(to_drop: *mut T) { - // Code here does not matter - this is replaced by the - // real drop glue by the compiler. - drop_in_place(to_drop); -} - -#[lang = "panic"] -#[track_caller] #[no_mangle] -pub fn panic(_msg: &'static str) -> ! { - unsafe { - libc::puts("Panicking\0" as *const str as *const u8); - intrinsics::abort(); - } -} - -#[lang = "panic_location"] -struct PanicLocation { - file: &'static str, - line: u32, - column: u32, -} - -#[lang = "panic_bounds_check"] -#[track_caller] -#[no_mangle] -fn panic_bounds_check(index: usize, len: usize) -> ! { - unsafe { - libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index); - intrinsics::abort(); - } -} - -mod intrinsics { - #[rustc_nounwind] - #[rustc_intrinsic] - #[rustc_intrinsic_must_be_overridden] - pub fn abort() -> ! { - loop {} - } -} - -#[lang = "add"] -trait Add { - type Output; - - fn add(self, rhs: RHS) -> Self::Output; -} - -impl Add for u8 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for i8 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for i32 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for usize { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for isize { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -#[lang = "sub"] -pub trait Sub { - type Output; - - fn sub(self, rhs: RHS) -> Self::Output; -} - -impl Sub for usize { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -impl Sub for isize { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -impl Sub for u8 { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -impl Sub for i8 { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -impl Sub for i16 { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -#[lang = "eq"] -pub trait PartialEq { - fn eq(&self, other: &Rhs) -> bool; - fn ne(&self, other: &Rhs) -> bool; -} - -impl PartialEq for u8 { - fn eq(&self, other: &u8) -> bool { - (*self) == (*other) - } - fn ne(&self, other: &u8) -> bool { - (*self) != (*other) - } -} - -impl PartialEq for u16 { - fn eq(&self, other: &u16) -> bool { - (*self) == (*other) - } - fn ne(&self, other: &u16) -> bool { - (*self) != (*other) - } -} - -impl PartialEq for u32 { - fn eq(&self, other: &u32) -> bool { - (*self) == (*other) - } - fn ne(&self, other: &u32) -> bool { - (*self) != (*other) - } -} - - -impl PartialEq for u64 { - fn eq(&self, other: &u64) -> bool { - (*self) == (*other) - } - fn ne(&self, other: &u64) -> bool { - (*self) != (*other) - } -} - -impl PartialEq for usize { - fn eq(&self, other: &usize) -> bool { - (*self) == (*other) - } - fn ne(&self, other: &usize) -> bool { - (*self) != (*other) - } -} - -impl PartialEq for i8 { - fn eq(&self, other: &i8) -> bool { - (*self) == (*other) - } - fn ne(&self, other: &i8) -> bool { - (*self) != (*other) - } -} - -impl PartialEq for i32 { - fn eq(&self, other: &i32) -> bool { - (*self) == (*other) - } - fn ne(&self, other: &i32) -> bool { - (*self) != (*other) - } -} - -impl PartialEq for isize { - fn eq(&self, other: &isize) -> bool { - (*self) == (*other) - } - fn ne(&self, other: &isize) -> bool { - (*self) != (*other) - } -} - -impl PartialEq for char { - fn eq(&self, other: &char) -> bool { - (*self) == (*other) - } - fn ne(&self, other: &char) -> bool { - (*self) != (*other) - } -} - -/* - * Code - */ - -#[start] -fn main(argc: isize, _argv: *const *const u8) -> isize { +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { unsafe { if argc == 1 { libc::printf(b"true\n\0" as *const u8 as *const i8); diff --git a/compiler/rustc_codegen_gcc/tests/run/empty_main.rs b/compiler/rustc_codegen_gcc/tests/run/empty_main.rs index e66a859ad698e..042e44080c53a 100644 --- a/compiler/rustc_codegen_gcc/tests/run/empty_main.rs +++ b/compiler/rustc_codegen_gcc/tests/run/empty_main.rs @@ -3,11 +3,12 @@ // Run-time: // status: 0 -#![feature(auto_traits, lang_items, no_core, start)] +#![feature(auto_traits, lang_items, no_core)] #![allow(internal_features)] #![no_std] #![no_core] +#![no_main] /* * Core @@ -34,7 +35,7 @@ pub(crate) unsafe auto trait Freeze {} * Code */ -#[start] -fn main(_argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { 0 } diff --git a/compiler/rustc_codegen_gcc/tests/run/exit.rs b/compiler/rustc_codegen_gcc/tests/run/exit.rs index bf1cbeef30205..9a7c91c0adb20 100644 --- a/compiler/rustc_codegen_gcc/tests/run/exit.rs +++ b/compiler/rustc_codegen_gcc/tests/run/exit.rs @@ -3,11 +3,12 @@ // Run-time: // status: 2 -#![feature(auto_traits, lang_items, no_core, start, intrinsics)] +#![feature(auto_traits, lang_items, no_core, intrinsics)] #![allow(internal_features)] #![no_std] #![no_core] +#![no_main] mod libc { #[link(name = "c")] @@ -41,8 +42,8 @@ pub(crate) unsafe auto trait Freeze {} * Code */ -#[start] -fn main(mut argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { unsafe { libc::exit(2); } diff --git a/compiler/rustc_codegen_gcc/tests/run/exit_code.rs b/compiler/rustc_codegen_gcc/tests/run/exit_code.rs index be7a233efdaaf..c50d2b0d7107c 100644 --- a/compiler/rustc_codegen_gcc/tests/run/exit_code.rs +++ b/compiler/rustc_codegen_gcc/tests/run/exit_code.rs @@ -3,11 +3,12 @@ // Run-time: // status: 1 -#![feature(auto_traits, lang_items, no_core, start)] +#![feature(auto_traits, lang_items, no_core)] #![allow(internal_features)] #![no_std] #![no_core] +#![no_main] /* * Core @@ -34,7 +35,7 @@ pub(crate) unsafe auto trait Freeze {} * Code */ -#[start] -fn main(mut argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { 1 } diff --git a/compiler/rustc_codegen_gcc/tests/run/fun_ptr.rs b/compiler/rustc_codegen_gcc/tests/run/fun_ptr.rs index 523544ee6bbbb..98b351e504495 100644 --- a/compiler/rustc_codegen_gcc/tests/run/fun_ptr.rs +++ b/compiler/rustc_codegen_gcc/tests/run/fun_ptr.rs @@ -4,212 +4,21 @@ // status: 0 // stdout: 1 -#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs)] -#![allow(internal_features)] +#![feature(no_core)] #![no_std] #![no_core] +#![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} -impl Copy for usize {} -impl Copy for i32 {} -impl Copy for u8 {} -impl Copy for i8 {} -impl Copy for i16 {} -impl Copy for *mut T {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} +extern crate mini_core; mod libc { #[link(name = "c")] extern "C" { pub fn printf(format: *const i8, ...) -> i32; - pub fn puts(s: *const u8) -> i32; - } -} - -#[lang = "index"] -pub trait Index { - type Output: ?Sized; - fn index(&self, index: Idx) -> &Self::Output; -} - -impl Index for [T; 3] { - type Output = T; - - fn index(&self, index: usize) -> &Self::Output { - &self[index] - } -} - -impl Index for [T] { - type Output = T; - - fn index(&self, index: usize) -> &Self::Output { - &self[index] - } -} - -#[lang = "drop_in_place"] -#[allow(unconditional_recursion)] -pub unsafe fn drop_in_place(to_drop: *mut T) { - // Code here does not matter - this is replaced by the - // real drop glue by the compiler. - drop_in_place(to_drop); -} - -#[lang = "panic"] -#[track_caller] -#[no_mangle] -pub fn panic(_msg: &'static str) -> ! { - unsafe { - libc::puts("Panicking\0" as *const str as *const u8); - intrinsics::abort(); } } -#[lang = "panic_location"] -struct PanicLocation { - file: &'static str, - line: u32, - column: u32, -} - -#[lang = "panic_bounds_check"] -#[track_caller] -#[no_mangle] -fn panic_bounds_check(index: usize, len: usize) -> ! { - unsafe { - libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index); - intrinsics::abort(); - } -} - -mod intrinsics { - #[rustc_nounwind] - #[rustc_intrinsic] - #[rustc_intrinsic_must_be_overridden] - pub fn abort() -> ! { - loop {} - } -} - -#[lang = "add"] -trait Add { - type Output; - - fn add(self, rhs: RHS) -> Self::Output; -} - -impl Add for u8 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for i8 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for i32 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for usize { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for isize { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -#[lang = "sub"] -pub trait Sub { - type Output; - - fn sub(self, rhs: RHS) -> Self::Output; -} - -impl Sub for usize { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -impl Sub for isize { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -impl Sub for u8 { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -impl Sub for i8 { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -impl Sub for i16 { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - - -/* - * Code - */ - fn i16_as_i8(a: i16) -> i8 { a as i8 } @@ -218,8 +27,8 @@ fn call_func(func: fn(i16) -> i8, param: i16) -> i8 { func(param) } -#[start] -fn main(argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { unsafe { let result = call_func(i16_as_i8, argc as i16) as isize; libc::printf(b"%ld\n\0" as *const u8 as *const i8, result); diff --git a/compiler/rustc_codegen_gcc/tests/run/int.rs b/compiler/rustc_codegen_gcc/tests/run/int.rs index bfe73c38435a3..58a26801b678c 100644 --- a/compiler/rustc_codegen_gcc/tests/run/int.rs +++ b/compiler/rustc_codegen_gcc/tests/run/int.rs @@ -3,8 +3,6 @@ // Run-time: // status: 0 -#![feature(const_black_box)] - /* * Code */ diff --git a/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs b/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs index 3ae793382164b..9be64f991ee08 100644 --- a/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs +++ b/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs @@ -8,10 +8,11 @@ // 11 #![allow(internal_features, unused_attributes)] -#![feature(auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs, track_caller)] +#![feature(auto_traits, lang_items, no_core, intrinsics, rustc_attrs, track_caller)] #![no_std] #![no_core] +#![no_main] /* * Core @@ -148,8 +149,8 @@ fn update_num(num: &mut isize) { *num = *num + 5; } -#[start] -fn main(mut argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { let mut test = test(argc); unsafe { libc::printf(b"%ld\n\0" as *const u8 as *const i8, test.field); diff --git a/compiler/rustc_codegen_gcc/tests/run/operations.rs b/compiler/rustc_codegen_gcc/tests/run/operations.rs index 2e3c021d5f772..c92d3cc0b8fbf 100644 --- a/compiler/rustc_codegen_gcc/tests/run/operations.rs +++ b/compiler/rustc_codegen_gcc/tests/run/operations.rs @@ -6,10 +6,11 @@ // 10 #![allow(internal_features, unused_attributes)] -#![feature(auto_traits, lang_items, no_core, start, intrinsics, arbitrary_self_types, rustc_attrs)] +#![feature(auto_traits, lang_items, no_core, intrinsics, arbitrary_self_types, rustc_attrs)] #![no_std] #![no_core] +#![no_main] /* * Core @@ -38,8 +39,8 @@ pub trait Deref { fn deref(&self) -> &Self::Target; } -#[lang = "receiver"] -trait Receiver { +#[lang = "legacy_receiver"] +trait LegacyReceiver { } #[lang = "freeze"] @@ -231,8 +232,8 @@ pub fn panic_const_mul_overflow() -> ! { * Code */ -#[start] -fn main(mut argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { unsafe { libc::printf(b"%ld\n\0" as *const u8 as *const i8, 40 + argc); libc::printf(b"%ld\n\0" as *const u8 as *const i8, 40 - argc); diff --git a/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs b/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs index c7510d16449ef..0ba49e7187fca 100644 --- a/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs +++ b/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs @@ -4,220 +4,29 @@ // status: 0 // stdout: 1 -#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs)] -#![allow(internal_features)] +#![feature(no_core)] #![no_std] #![no_core] +#![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} -impl Copy for usize {} -impl Copy for i32 {} -impl Copy for u8 {} -impl Copy for i8 {} -impl Copy for i16 {} -impl Copy for *mut T {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} +extern crate mini_core; mod libc { #[link(name = "c")] extern "C" { pub fn printf(format: *const i8, ...) -> i32; - pub fn puts(s: *const u8) -> i32; - } -} - -#[lang = "index"] -pub trait Index { - type Output: ?Sized; - fn index(&self, index: Idx) -> &Self::Output; -} - -impl Index for [T; 3] { - type Output = T; - - fn index(&self, index: usize) -> &Self::Output { - &self[index] - } -} - -impl Index for [T] { - type Output = T; - - fn index(&self, index: usize) -> &Self::Output { - &self[index] - } -} - -#[lang = "drop_in_place"] -#[allow(unconditional_recursion)] -pub unsafe fn drop_in_place(to_drop: *mut T) { - // Code here does not matter - this is replaced by the - // real drop glue by the compiler. - drop_in_place(to_drop); -} - -#[lang = "panic"] -#[track_caller] -#[no_mangle] -pub fn panic(_msg: &'static str) -> ! { - unsafe { - libc::puts("Panicking\0" as *const str as *const u8); - intrinsics::abort(); } } -#[lang = "panic_location"] -struct PanicLocation { - file: &'static str, - line: u32, - column: u32, -} - -#[lang = "panic_bounds_check"] -#[track_caller] -#[no_mangle] -fn panic_bounds_check(index: usize, len: usize) -> ! { - unsafe { - libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index); - intrinsics::abort(); - } -} - -mod intrinsics { - #[rustc_nounwind] - #[rustc_intrinsic] - #[rustc_intrinsic_must_be_overridden] - pub fn abort() -> ! { - loop {} - } -} - -#[lang = "add"] -trait Add { - type Output; - - fn add(self, rhs: RHS) -> Self::Output; -} - -impl Add for u8 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for i8 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for i32 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for usize { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for isize { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -#[lang = "sub"] -pub trait Sub { - type Output; - - fn sub(self, rhs: RHS) -> Self::Output; -} - -impl Sub for usize { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -impl Sub for isize { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -impl Sub for u8 { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -impl Sub for i8 { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -impl Sub for i16 { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - - -/* - * Code - */ - static mut ONE: usize = 1; fn make_array() -> [u8; 3] { [42, 10, 5] } -#[start] -fn main(argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { unsafe { let ptr = ONE as *mut usize; let value = ptr as usize; diff --git a/compiler/rustc_codegen_gcc/tests/run/return-tuple.rs b/compiler/rustc_codegen_gcc/tests/run/return-tuple.rs index 8d40deb8c85ef..3cc1e274001e7 100644 --- a/compiler/rustc_codegen_gcc/tests/run/return-tuple.rs +++ b/compiler/rustc_codegen_gcc/tests/run/return-tuple.rs @@ -6,27 +6,28 @@ // 10 // 42 -#![feature(auto_traits, lang_items, no_core, start, intrinsics)] +#![feature(auto_traits, lang_items, no_core, intrinsics)] #![allow(internal_features)] #![no_std] #![no_core] +#![no_main] #[lang = "copy"] pub unsafe trait Copy {} -unsafe impl Copy for bool {} -unsafe impl Copy for u8 {} -unsafe impl Copy for u16 {} -unsafe impl Copy for u32 {} -unsafe impl Copy for u64 {} -unsafe impl Copy for usize {} -unsafe impl Copy for i8 {} -unsafe impl Copy for i16 {} -unsafe impl Copy for i32 {} -unsafe impl Copy for isize {} -unsafe impl Copy for f32 {} -unsafe impl Copy for char {} +impl Copy for bool {} +impl Copy for u8 {} +impl Copy for u16 {} +impl Copy for u32 {} +impl Copy for u64 {} +impl Copy for usize {} +impl Copy for i8 {} +impl Copy for i16 {} +impl Copy for i32 {} +impl Copy for isize {} +impl Copy for f32 {} +impl Copy for char {} mod libc { #[link(name = "c")] @@ -43,8 +44,8 @@ mod libc { #[lang = "sized"] pub trait Sized {} -#[lang = "receiver"] -trait Receiver { +#[lang = "legacy_receiver"] +trait LegacyReceiver { } #[lang = "freeze"] @@ -61,8 +62,8 @@ fn int_cast(a: u16, b: i16) -> (u8, u16, u32, usize, i8, i16, i32, isize, u8, u3 ) } -#[start] -fn main(argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { let (a, b, c, d, e, f, g, h, i, j) = int_cast(10, 42); unsafe { libc::printf(b"%d\n\0" as *const u8 as *const i8, c); diff --git a/compiler/rustc_codegen_gcc/tests/run/slice.rs b/compiler/rustc_codegen_gcc/tests/run/slice.rs index 35ad594ecdea3..825fcb8a081e7 100644 --- a/compiler/rustc_codegen_gcc/tests/run/slice.rs +++ b/compiler/rustc_codegen_gcc/tests/run/slice.rs @@ -4,36 +4,13 @@ // status: 0 // stdout: 5 -#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs)] -#![allow(internal_features)] +#![feature(no_core)] #![no_std] #![no_core] +#![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} -impl Copy for usize {} -impl Copy for i32 {} -impl Copy for u32 {} -impl Copy for *mut T {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} +extern crate mini_core; mod libc { #[link(name = "c")] @@ -42,79 +19,6 @@ mod libc { } } -#[lang = "index"] -pub trait Index { - type Output: ?Sized; - fn index(&self, index: Idx) -> &Self::Output; -} - -impl Index for [T; 3] { - type Output = T; - - fn index(&self, index: usize) -> &Self::Output { - &self[index] - } -} - -impl Index for [T] { - type Output = T; - - fn index(&self, index: usize) -> &Self::Output { - &self[index] - } -} - -#[lang = "unsize"] -pub trait Unsize {} - -#[lang = "coerce_unsized"] -pub trait CoerceUnsized {} - -impl<'a, 'b: 'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} -impl<'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {} -impl, U: ?Sized> CoerceUnsized<*const U> for *const T {} -impl, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} - -#[lang = "drop_in_place"] -#[allow(unconditional_recursion)] -pub unsafe fn drop_in_place(to_drop: *mut T) { - // Code here does not matter - this is replaced by the - // real drop glue by the compiler. - drop_in_place(to_drop); -} - -#[lang = "panic_location"] -struct PanicLocation { - file: &'static str, - line: u32, - column: u32, -} - -#[lang = "panic_bounds_check"] -#[track_caller] -#[no_mangle] -fn panic_bounds_check(index: usize, len: usize) -> ! { - unsafe { - libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index); - intrinsics::abort(); - } -} - -mod intrinsics { - use super::Sized; - - #[rustc_nounwind] - #[rustc_intrinsic] - #[rustc_intrinsic_must_be_overridden] - pub fn abort() -> ! { - loop {} - } -} - -/* - * Code - */ - static mut TWO: usize = 2; fn index_slice(s: &[u32]) -> u32 { @@ -123,8 +27,8 @@ fn index_slice(s: &[u32]) -> u32 { } } -#[start] -fn main(mut argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { let array = [42, 7, 5]; unsafe { libc::printf(b"%ld\n\0" as *const u8 as *const i8, index_slice(&array)); diff --git a/compiler/rustc_codegen_gcc/tests/run/static.rs b/compiler/rustc_codegen_gcc/tests/run/static.rs index a17ea2a48936d..80c8782c4b1a6 100644 --- a/compiler/rustc_codegen_gcc/tests/run/static.rs +++ b/compiler/rustc_codegen_gcc/tests/run/static.rs @@ -9,11 +9,12 @@ // 12 // 1 -#![feature(auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs)] +#![feature(auto_traits, lang_items, no_core, intrinsics, rustc_attrs)] #![allow(internal_features)] #![no_std] #![no_core] +#![no_main] /* * Core @@ -98,8 +99,8 @@ static mut WITH_REF: WithRef = WithRef { refe: unsafe { &TEST }, }; -#[start] -fn main(mut argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { unsafe { libc::printf(b"%ld\n\0" as *const u8 as *const i8, CONSTANT); libc::printf(b"%ld\n\0" as *const u8 as *const i8, TEST2.field); diff --git a/compiler/rustc_codegen_gcc/tests/run/structs.rs b/compiler/rustc_codegen_gcc/tests/run/structs.rs index d6455667400c9..59b8f358863f2 100644 --- a/compiler/rustc_codegen_gcc/tests/run/structs.rs +++ b/compiler/rustc_codegen_gcc/tests/run/structs.rs @@ -5,11 +5,12 @@ // stdout: 1 // 2 -#![feature(auto_traits, lang_items, no_core, start, intrinsics)] +#![feature(auto_traits, lang_items, no_core, intrinsics)] #![allow(internal_features)] #![no_std] #![no_core] +#![no_main] /* * Core @@ -55,8 +56,8 @@ fn one() -> isize { 1 } -#[start] -fn main(mut argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { let test = Test { field: one(), }; diff --git a/compiler/rustc_codegen_gcc/tests/run/tuple.rs b/compiler/rustc_codegen_gcc/tests/run/tuple.rs index 8a7d85ae867e8..ed60a56a68c4c 100644 --- a/compiler/rustc_codegen_gcc/tests/run/tuple.rs +++ b/compiler/rustc_codegen_gcc/tests/run/tuple.rs @@ -4,11 +4,12 @@ // status: 0 // stdout: 3 -#![feature(auto_traits, lang_items, no_core, start, intrinsics)] +#![feature(auto_traits, lang_items, no_core, intrinsics)] #![allow(internal_features)] #![no_std] #![no_core] +#![no_main] /* * Core @@ -42,8 +43,8 @@ mod libc { * Code */ -#[start] -fn main(mut argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { let test: (isize, isize, isize) = (3, 1, 4); unsafe { libc::printf(b"%ld\n\0" as *const u8 as *const i8, test.0); diff --git a/compiler/rustc_codegen_gcc/tests/run/volatile2.rs b/compiler/rustc_codegen_gcc/tests/run/volatile2.rs new file mode 100644 index 0000000000000..a177b817ab35a --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/run/volatile2.rs @@ -0,0 +1,113 @@ +// Compiler: +// +// Run-time: +// status: 0 + +mod libc { + #[link(name = "c")] + extern "C" { + pub fn puts(s: *const u8) -> i32; + + pub fn sigaction(signum: i32, act: *const sigaction, oldact: *mut sigaction) -> i32; + pub fn mmap(addr: *mut (), len: usize, prot: i32, flags: i32, fd: i32, offset: i64) -> *mut (); + pub fn mprotect(addr: *mut (), len: usize, prot: i32) -> i32; + } + + pub const PROT_READ: i32 = 1; + pub const PROT_WRITE: i32 = 2; + pub const MAP_PRIVATE: i32 = 0x0002; + pub const MAP_ANONYMOUS: i32 = 0x0020; + pub const MAP_FAILED: *mut u8 = !0 as *mut u8; + + /// glibc sigaction + #[repr(C)] + pub struct sigaction { + pub sa_sigaction: Option, + pub sa_mask: [u32; 32], + pub sa_flags: i32, + pub sa_restorer: Option, + } + + pub const SA_SIGINFO: i32 = 0x00000004; + pub const SIGSEGV: i32 = 11; +} + +static mut COUNT: u32 = 0; +static mut STORAGE: *mut u8 = core::ptr::null_mut(); +const PAGE_SIZE: usize = 1 << 15; + +fn main() { + unsafe { + // Register a segfault handler + libc::sigaction( + libc::SIGSEGV, + &libc::sigaction { + sa_sigaction: Some(segv_handler), + sa_flags: libc::SA_SIGINFO, + ..core::mem::zeroed() + }, + core::ptr::null_mut(), + ); + + STORAGE = libc::mmap( + core::ptr::null_mut(), + PAGE_SIZE * 2, + 0, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ).cast(); + if STORAGE == libc::MAP_FAILED { + panic!("error: mmap failed"); + } + + let p_count = (&mut COUNT) as *mut u32; + p_count.write_volatile(0); + + // Trigger segfaults + STORAGE.add(0).write_volatile(1); + STORAGE.add(PAGE_SIZE).write_volatile(1); + STORAGE.add(0).write_volatile(1); + STORAGE.add(PAGE_SIZE).write_volatile(1); + STORAGE.add(0).write_volatile(1); + STORAGE.add(PAGE_SIZE).write_volatile(1); + STORAGE.add(0).read_volatile(); + STORAGE.add(PAGE_SIZE).read_volatile(); + STORAGE.add(0).read_volatile(); + STORAGE.add(PAGE_SIZE).read_volatile(); + STORAGE.add(0).read_volatile(); + STORAGE.add(PAGE_SIZE).read_volatile(); + STORAGE.add(0).write_volatile(1); + STORAGE.add(PAGE_SIZE).write_volatile(1); + + // The segfault handler should have been called for every `write_volatile` and + // `read_volatile` in `STORAGE`. If the compiler ignores volatility, some of these writes + // will be combined, causing a different number of segfaults. + // + // This `p_count` read is done by a volatile read. If the compiler + // ignores volatility, the compiler will speculate that `*p_count` is + // unchanged and remove this check, failing the test. + if p_count.read_volatile() != 14 { + panic!("error: segfault count mismatch: {}", p_count.read_volatile()); + } + } +} + +unsafe extern "C" fn segv_handler(_: i32, _: *mut (), _: *mut ()) { + let p_count = (&mut COUNT) as *mut u32; + p_count.write_volatile(p_count.read_volatile() + 1); + let count = p_count.read_volatile(); + + // Toggle the protected page so that the handler will be called for + // each `write_volatile` + libc::mprotect( + STORAGE.cast(), + PAGE_SIZE, + if count % 2 == 1 { libc::PROT_READ | libc::PROT_WRITE } else { 0 }, + ); + libc::mprotect( + STORAGE.add(PAGE_SIZE).cast(), + PAGE_SIZE, + if count % 2 == 0 { libc::PROT_READ | libc::PROT_WRITE } else { 0 }, + ); +} diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 689986d642d90..94f21ac5f5742 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -9,6 +9,9 @@ test = false [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" +# To avoid duplicate dependencies, this should match the version of gimli used +# by `rustc_codegen_ssa` via its `thorin-dwp` dependency. +gimli = "0.30" itertools = "0.12" libc = "0.2" measureme = "11" diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl index 3982c37528df8..399d7ffea8e6d 100644 --- a/compiler/rustc_codegen_llvm/messages.ftl +++ b/compiler/rustc_codegen_llvm/messages.ftl @@ -10,7 +10,7 @@ codegen_llvm_dynamic_linking_with_lto = codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture codegen_llvm_forbidden_ctarget_feature = - target feature `{$feature}` cannot be toggled with `-Ctarget-feature`: {$reason} + target feature `{$feature}` cannot be {$enabled} with `-Ctarget-feature`: {$reason} .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! codegen_llvm_forbidden_ctarget_feature_issue = for more information, see issue #116344 @@ -24,8 +24,6 @@ codegen_llvm_invalid_minimum_alignment_not_power_of_two = codegen_llvm_invalid_minimum_alignment_too_large = invalid minimum global alignment: {$align} is too large -codegen_llvm_invalid_target_feature_prefix = target feature `{$feature}` must begin with a `+` or `-`" - codegen_llvm_load_bitcode = failed to load bitcode of module "{$name}" codegen_llvm_load_bitcode_with_llvm_err = failed to load bitcode of module "{$name}": {$llvm_err} @@ -40,9 +38,6 @@ codegen_llvm_lto_proc_macro = lto cannot be used for `proc-macro` crate type wit codegen_llvm_mismatch_data_layout = data-layout for target `{$rustc_target}`, `{$rustc_layout}`, differs from LLVM target's `{$llvm_target}` default layout, `{$llvm_layout}` -codegen_llvm_multiple_source_dicompileunit = multiple source DICompileUnits found -codegen_llvm_multiple_source_dicompileunit_with_llvm_err = multiple source DICompileUnits found: {$llvm_err} - codegen_llvm_parse_bitcode = failed to parse bitcode for LTO module codegen_llvm_parse_bitcode_with_llvm_err = failed to parse bitcode for LTO module: {$llvm_err} diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 1d35138b01348..28f423efc2141 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -1,10 +1,10 @@ +use std::borrow::Borrow; use std::cmp; use libc::c_uint; use rustc_abi as abi; pub(crate) use rustc_abi::ExternAbi; -use rustc_abi::Primitive::Int; -use rustc_abi::{HasDataLayout, Size}; +use rustc_abi::{HasDataLayout, Primitive, Reg, RegKind, Size}; use rustc_codegen_ssa::MemFlags; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue}; @@ -312,7 +312,7 @@ impl<'ll, 'tcx> ArgAbiBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> { fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type; fn ptr_to_llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type; - fn llvm_cconv(&self) -> llvm::CallConv; + fn llvm_cconv(&self, cx: &CodegenCx<'ll, 'tcx>) -> llvm::CallConv; /// Apply attributes to a function declaration/definition. fn apply_attrs_llfn( @@ -404,8 +404,8 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { cx.type_ptr_ext(cx.data_layout().instruction_address_space) } - fn llvm_cconv(&self) -> llvm::CallConv { - self.conv.into() + fn llvm_cconv(&self, cx: &CodegenCx<'ll, 'tcx>) -> llvm::CallConv { + llvm::CallConv::from_conv(self.conv, cx.tcx.sess.target.arch.borrow()) } fn apply_attrs_llfn( @@ -439,7 +439,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { let apply_range_attr = |idx: AttributePlace, scalar: rustc_abi::Scalar| { if cx.sess().opts.optimize != config::OptLevel::No && llvm_util::get_version() >= (19, 0, 0) - && matches!(scalar.primitive(), Int(..)) + && matches!(scalar.primitive(), Primitive::Int(..)) // If the value is a boolean, the range is 0..2 and that ultimately // become 0..0 when the type becomes i1, which would be rejected // by the LLVM verifier. @@ -447,11 +447,11 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { // LLVM also rejects full range. && !scalar.is_always_valid(cx) { - attributes::apply_to_llfn(llfn, idx, &[llvm::CreateRangeAttr( - cx.llcx, - scalar.size(cx), - scalar.valid_range(cx), - )]); + attributes::apply_to_llfn( + llfn, + idx, + &[llvm::CreateRangeAttr(cx.llcx, scalar.size(cx), scalar.valid_range(cx))], + ); } }; @@ -471,10 +471,14 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { ); attributes::apply_to_llfn(llfn, llvm::AttributePlace::Argument(i), &[sret]); if cx.sess().opts.optimize != config::OptLevel::No { - attributes::apply_to_llfn(llfn, llvm::AttributePlace::Argument(i), &[ - llvm::AttributeKind::Writable.create_attr(cx.llcx), - llvm::AttributeKind::DeadOnUnwind.create_attr(cx.llcx), - ]); + attributes::apply_to_llfn( + llfn, + llvm::AttributePlace::Argument(i), + &[ + llvm::AttributeKind::Writable.create_attr(cx.llcx), + llvm::AttributeKind::DeadOnUnwind.create_attr(cx.llcx), + ], + ); } } PassMode::Cast { cast, pad_i32: _ } => { @@ -573,7 +577,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { if bx.cx.sess().opts.optimize != config::OptLevel::No && llvm_util::get_version() < (19, 0, 0) && let abi::BackendRepr::Scalar(scalar) = self.ret.layout.backend_repr - && matches!(scalar.primitive(), Int(..)) + && matches!(scalar.primitive(), Primitive::Int(..)) // If the value is a boolean, the range is 0..2 and that ultimately // become 0..0 when the type becomes i1, which would be rejected // by the LLVM verifier. @@ -592,9 +596,11 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { bx.cx.llcx, bx.cx.type_array(bx.cx.type_i8(), arg.layout.size.bytes()), ); - attributes::apply_to_callsite(callsite, llvm::AttributePlace::Argument(i), &[ - byval, - ]); + attributes::apply_to_callsite( + callsite, + llvm::AttributePlace::Argument(i), + &[byval], + ); } PassMode::Direct(attrs) | PassMode::Indirect { attrs, meta_attrs: None, on_stack: false } => { @@ -617,7 +623,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { } } - let cconv = self.llvm_cconv(); + let cconv = self.llvm_cconv(&bx.cx); if cconv != llvm::CCallConv { llvm::SetInstructionCallConv(callsite, cconv); } @@ -626,9 +632,11 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { // This will probably get ignored on all targets but those supporting the TrustZone-M // extension (thumbv8m targets). let cmse_nonsecure_call = llvm::CreateAttrString(bx.cx.llcx, "cmse_nonsecure_call"); - attributes::apply_to_callsite(callsite, llvm::AttributePlace::Function, &[ - cmse_nonsecure_call, - ]); + attributes::apply_to_callsite( + callsite, + llvm::AttributePlace::Function, + &[cmse_nonsecure_call], + ); } // Some intrinsics require that an elementtype attribute (with the pointee type of a @@ -655,8 +663,8 @@ impl<'tcx> AbiBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { } } -impl From for llvm::CallConv { - fn from(conv: Conv) -> Self { +impl llvm::CallConv { + pub(crate) fn from_conv(conv: Conv, arch: &str) -> Self { match conv { Conv::C | Conv::Rust @@ -666,11 +674,19 @@ impl From for llvm::CallConv { Conv::Cold => llvm::ColdCallConv, Conv::PreserveMost => llvm::PreserveMost, Conv::PreserveAll => llvm::PreserveAll, + Conv::GpuKernel => { + if arch == "amdgpu" { + llvm::AmdgpuKernel + } else if arch == "nvptx64" { + llvm::PtxKernel + } else { + panic!("Architecture {arch} does not support GpuKernel calling convention"); + } + } Conv::AvrInterrupt => llvm::AvrInterrupt, Conv::AvrNonBlockingInterrupt => llvm::AvrNonBlockingInterrupt, Conv::ArmAapcs => llvm::ArmAapcsCallConv, Conv::Msp430Intr => llvm::Msp430Intr, - Conv::PtxKernel => llvm::PtxKernel, Conv::X86Fastcall => llvm::X86FastcallCallConv, Conv::X86Intr => llvm::X86_Intr, Conv::X86Stdcall => llvm::X86StdcallCallConv, diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index 149ded28356b8..66723cbf88206 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -81,13 +81,13 @@ pub(crate) unsafe fn codegen( llvm::set_visibility(ll_g, llvm::Visibility::from_generic(tcx.sess.default_visibility())); let val = tcx.sess.opts.unstable_opts.oom.should_panic(); let llval = llvm::LLVMConstInt(i8, val as u64, False); - llvm::LLVMSetInitializer(ll_g, llval); + llvm::set_initializer(ll_g, llval); let name = NO_ALLOC_SHIM_IS_UNSTABLE; let ll_g = llvm::LLVMRustGetOrInsertGlobal(llmod, name.as_c_char_ptr(), name.len(), i8); llvm::set_visibility(ll_g, llvm::Visibility::from_generic(tcx.sess.default_visibility())); let llval = llvm::LLVMConstInt(i8, 0, False); - llvm::LLVMSetInitializer(ll_g, llval); + llvm::set_initializer(ll_g, llval); } if tcx.sess.opts.debuginfo != DebugInfo::None { diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 3722d4350a291..be5673eddf93e 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -286,7 +286,9 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { InlineAsmArch::M68k => { constraints.push("~{ccr}".to_string()); } - InlineAsmArch::CSKY => {} + InlineAsmArch::CSKY => { + constraints.push("~{psr}".to_string()); + } } } if !options.contains(InlineAsmOptions::NOMEM) { diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index f8454fd9960b6..3d7afa17bdf3d 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -37,7 +37,9 @@ fn inline_attr<'ll>(cx: &CodegenCx<'ll, '_>, inline: InlineAttr) -> Option<&'ll } match inline { InlineAttr::Hint => Some(AttributeKind::InlineHint.create_attr(cx.llcx)), - InlineAttr::Always => Some(AttributeKind::AlwaysInline.create_attr(cx.llcx)), + InlineAttr::Always | InlineAttr::Force { .. } => { + Some(AttributeKind::AlwaysInline.create_attr(cx.llcx)) + } InlineAttr::Never => { if cx.sess().target.arch != "amdgpu" { Some(AttributeKind::NoInline.create_attr(cx.llcx)) @@ -331,9 +333,12 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( let mut to_add = SmallVec::<[_; 16]>::new(); match codegen_fn_attrs.optimize { - OptimizeAttr::None => { + OptimizeAttr::Default => { to_add.extend(default_optimisation_attrs(cx)); } + OptimizeAttr::DoNotOptimize => { + to_add.push(llvm::AttributeKind::OptimizeNone.create_attr(cx.llcx)); + } OptimizeAttr::Size => { to_add.push(llvm::AttributeKind::MinSize.create_attr(cx.llcx)); to_add.push(llvm::AttributeKind::OptimizeForSize.create_attr(cx.llcx)); @@ -341,12 +346,12 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( OptimizeAttr::Speed => {} } - let inline = - if codegen_fn_attrs.inline == InlineAttr::None && instance.def.requires_inline(cx.tcx) { - InlineAttr::Hint - } else { - codegen_fn_attrs.inline - }; + // `optnone` requires `noinline` + let inline = match (codegen_fn_attrs.inline, &codegen_fn_attrs.optimize) { + (_, OptimizeAttr::DoNotOptimize) => InlineAttr::Never, + (InlineAttr::None, _) if instance.def.requires_inline(cx.tcx) => InlineAttr::Hint, + (inline, _) => inline, + }; to_add.extend(inline_attr(cx, inline)); // The `uwtable` attribute according to LLVM is: @@ -472,7 +477,11 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx); attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]); } - if let Some(align) = codegen_fn_attrs.alignment { + // function alignment can be set globally with the `-Zmin-function-alignment=` flag; + // the alignment from a `#[repr(align())]` is used if it specifies a higher alignment. + if let Some(align) = + Ord::max(cx.tcx.sess.opts.unstable_opts.min_function_alignment, codegen_fn_attrs.alignment) + { llvm::set_alignment(llfn, align); } if let Some(backchain) = backchain_attr(cx) { diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index 33a956e552fbe..93553f3f3648a 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -11,7 +11,7 @@ use rustc_codegen_ssa::back::archive::{ use rustc_session::Session; use crate::llvm::archive_ro::{ArchiveRO, Child}; -use crate::llvm::{self, ArchiveKind}; +use crate::llvm::{self, ArchiveKind, last_error}; /// Helper for adding many files to an archive. #[must_use = "must call build() to finish building the archive"] @@ -169,6 +169,8 @@ impl<'a> LlvmArchiveBuilder<'a> { .unwrap_or_else(|kind| self.sess.dcx().emit_fatal(UnknownArchiveKind { kind })); let mut additions = mem::take(&mut self.additions); + // Values in the `members` list below will contain pointers to the strings allocated here. + // So they need to get dropped after all elements of `members` get freed. let mut strings = Vec::new(); let mut members = Vec::new(); @@ -229,12 +231,7 @@ impl<'a> LlvmArchiveBuilder<'a> { self.sess.target.arch == "arm64ec", ); let ret = if r.into_result().is_err() { - let err = llvm::LLVMRustGetLastError(); - let msg = if err.is_null() { - "failed to write archive".into() - } else { - String::from_utf8_lossy(CStr::from_ptr(err).to_bytes()) - }; + let msg = last_error().unwrap_or_else(|| "failed to write archive".into()); Err(io::Error::new(io::ErrorKind::Other, msg)) } else { Ok(!members.is_empty()) diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 4adf99e91d08d..7262fce491191 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -1,7 +1,6 @@ use std::collections::BTreeMap; use std::ffi::{CStr, CString}; use std::fs::File; -use std::mem::ManuallyDrop; use std::path::Path; use std::sync::Arc; use std::{io, iter, slice}; @@ -9,7 +8,7 @@ use std::{io, iter, slice}; use object::read::archive::ArchiveFile; use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule, ThinShared}; use rustc_codegen_ssa::back::symbol_export; -use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput, TargetMachineFactoryConfig}; +use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{ModuleCodegen, ModuleKind, looks_like_rust_object_file}; use rustc_data_structures::fx::FxHashMap; @@ -607,10 +606,31 @@ pub(crate) fn run_pass_manager( // If this rustc version was build with enzyme/autodiff enabled, and if users applied the // `#[autodiff]` macro at least once, then we will later call llvm_optimize a second time. - let first_run = true; debug!("running llvm pm opt pipeline"); unsafe { - write::llvm_optimize(cgcx, dcx, module, config, opt_level, opt_stage, first_run)?; + write::llvm_optimize( + cgcx, + dcx, + module, + config, + opt_level, + opt_stage, + write::AutodiffStage::DuringAD, + )?; + } + // FIXME(ZuseZ4): Make this more granular + if cfg!(llvm_enzyme) && !thin { + unsafe { + write::llvm_optimize( + cgcx, + dcx, + module, + config, + opt_level, + llvm::OptStage::FatLTO, + write::AutodiffStage::PostAD, + )?; + } } debug!("lto done"); Ok(()) @@ -622,7 +642,7 @@ unsafe impl Send for ModuleBuffer {} unsafe impl Sync for ModuleBuffer {} impl ModuleBuffer { - pub fn new(m: &llvm::Module) -> ModuleBuffer { + pub(crate) fn new(m: &llvm::Module) -> ModuleBuffer { ModuleBuffer(unsafe { llvm::LLVMRustModuleBufferCreate(m) }) } } @@ -664,7 +684,7 @@ unsafe impl Send for ThinBuffer {} unsafe impl Sync for ThinBuffer {} impl ThinBuffer { - pub fn new(m: &llvm::Module, is_thin: bool, emit_summary: bool) -> ThinBuffer { + pub(crate) fn new(m: &llvm::Module, is_thin: bool, emit_summary: bool) -> ThinBuffer { unsafe { let buffer = llvm::LLVMRustThinLTOBufferCreate(m, is_thin, emit_summary); ThinBuffer(buffer) @@ -706,18 +726,15 @@ pub(crate) unsafe fn optimize_thin_module( let dcx = dcx.handle(); let module_name = &thin_module.shared.module_names[thin_module.idx]; - let tm_factory_config = TargetMachineFactoryConfig::new(cgcx, module_name.to_str().unwrap()); - let tm = (cgcx.tm_factory)(tm_factory_config).map_err(|e| write::llvm_err(dcx, e))?; // Right now the implementation we've got only works over serialized // modules, so we create a fresh new LLVM context and parse the module // into that context. One day, however, we may do this for upstream // crates but for locally codegened modules we may be able to reuse // that LLVM Context and Module. - let llcx = unsafe { llvm::LLVMRustContextCreate(cgcx.fewer_names) }; - let llmod_raw = parse_module(llcx, module_name, thin_module.data(), dcx)? as *const _; + let module_llvm = ModuleLlvm::parse(cgcx, module_name, thin_module.data(), dcx)?; let mut module = ModuleCodegen { - module_llvm: ModuleLlvm { llmod_raw, llcx, tm: ManuallyDrop::new(tm) }, + module_llvm, name: thin_module.name().to_string(), kind: ModuleKind::Regular, }; @@ -737,11 +754,7 @@ pub(crate) unsafe fn optimize_thin_module( { let _timer = cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_rename", thin_module.name()); - if unsafe { - !llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target) - } { - return Err(write::llvm_err(dcx, LlvmError::PrepareThinLtoModule)); - } + unsafe { llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target) }; save_temp_bitcode(cgcx, &module, "thin-lto-after-rename"); } diff --git a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs index 4cbd49aa44d48..f075f332462fc 100644 --- a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs +++ b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs @@ -17,7 +17,7 @@ pub struct OwnedTargetMachine { } impl OwnedTargetMachine { - pub fn new( + pub(crate) fn new( triple: &CStr, cpu: &CStr, features: &CStr, diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 509b24dd703a6..9fa10e960680b 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -40,7 +40,7 @@ use crate::errors::{ WithLlvmError, WriteBytecode, }; use crate::llvm::diagnostic::OptimizationDiagnosticKind::*; -use crate::llvm::{self, DiagnosticInfo, PassManager}; +use crate::llvm::{self, DiagnosticInfo}; use crate::type_::Type; use crate::{LlvmCodegenBackend, ModuleLlvm, base, common, llvm_util}; @@ -54,7 +54,7 @@ pub(crate) fn llvm_err<'a>(dcx: DiagCtxtHandle<'_>, err: LlvmError<'a>) -> Fatal fn write_output_file<'ll>( dcx: DiagCtxtHandle<'_>, target: &'ll llvm::TargetMachine, - pm: &llvm::PassManager<'ll>, + no_builtins: bool, m: &'ll llvm::Module, output: &Path, dwo_output: Option<&Path>, @@ -63,16 +63,19 @@ fn write_output_file<'ll>( verify_llvm_ir: bool, ) -> Result<(), FatalError> { debug!("write_output_file output={:?} dwo_output={:?}", output, dwo_output); - unsafe { - let output_c = path_to_c_string(output); - let dwo_output_c; - let dwo_output_ptr = if let Some(dwo_output) = dwo_output { - dwo_output_c = path_to_c_string(dwo_output); - dwo_output_c.as_ptr() - } else { - std::ptr::null() - }; - let result = llvm::LLVMRustWriteOutputFile( + let output_c = path_to_c_string(output); + let dwo_output_c; + let dwo_output_ptr = if let Some(dwo_output) = dwo_output { + dwo_output_c = path_to_c_string(dwo_output); + dwo_output_c.as_ptr() + } else { + std::ptr::null() + }; + let result = unsafe { + let pm = llvm::LLVMCreatePassManager(); + llvm::LLVMAddAnalysisPasses(target, pm); + llvm::LLVMRustAddLibraryInfo(pm, m, no_builtins); + llvm::LLVMRustWriteOutputFile( target, pm, m, @@ -80,22 +83,22 @@ fn write_output_file<'ll>( dwo_output_ptr, file_type, verify_llvm_ir, - ); + ) + }; - // Record artifact sizes for self-profiling - if result == llvm::LLVMRustResult::Success { - let artifact_kind = match file_type { - llvm::FileType::ObjectFile => "object_file", - llvm::FileType::AssemblyFile => "assembly_file", - }; - record_artifact_size(self_profiler_ref, artifact_kind, output); - if let Some(dwo_file) = dwo_output { - record_artifact_size(self_profiler_ref, "dwo_file", dwo_file); - } + // Record artifact sizes for self-profiling + if result == llvm::LLVMRustResult::Success { + let artifact_kind = match file_type { + llvm::FileType::ObjectFile => "object_file", + llvm::FileType::AssemblyFile => "assembly_file", + }; + record_artifact_size(self_profiler_ref, artifact_kind, output); + if let Some(dwo_file) = dwo_output { + record_artifact_size(self_profiler_ref, "dwo_file", dwo_file); } - - result.into_result().map_err(|()| llvm_err(dcx, LlvmError::WriteOutput { path: output })) } + + result.into_result().map_err(|()| llvm_err(dcx, LlvmError::WriteOutput { path: output })) } pub(crate) fn create_informational_target_machine( @@ -138,7 +141,7 @@ fn to_llvm_opt_settings(cfg: config::OptLevel) -> (llvm::CodeGenOptLevel, llvm:: match cfg { No => (llvm::CodeGenOptLevel::None, llvm::CodeGenOptSizeNone), Less => (llvm::CodeGenOptLevel::Less, llvm::CodeGenOptSizeNone), - Default => (llvm::CodeGenOptLevel::Default, llvm::CodeGenOptSizeNone), + More => (llvm::CodeGenOptLevel::Default, llvm::CodeGenOptSizeNone), Aggressive => (llvm::CodeGenOptLevel::Aggressive, llvm::CodeGenOptSizeNone), Size => (llvm::CodeGenOptLevel::Default, llvm::CodeGenOptSizeDefault), SizeMin => (llvm::CodeGenOptLevel::Default, llvm::CodeGenOptSizeAggressive), @@ -150,7 +153,7 @@ fn to_pass_builder_opt_level(cfg: config::OptLevel) -> llvm::PassBuilderOptLevel match cfg { No => llvm::PassBuilderOptLevel::O0, Less => llvm::PassBuilderOptLevel::O1, - Default => llvm::PassBuilderOptLevel::O2, + More => llvm::PassBuilderOptLevel::O2, Aggressive => llvm::PassBuilderOptLevel::O3, Size => llvm::PassBuilderOptLevel::Os, SizeMin => llvm::PassBuilderOptLevel::Oz, @@ -325,13 +328,17 @@ pub(crate) fn save_temp_bitcode( if !cgcx.save_temps { return; } + let ext = format!("{name}.bc"); + let cgu = Some(&module.name[..]); + let path = cgcx.output_filenames.temp_path_ext(&ext, cgu); + write_bitcode_to_file(module, &path) +} + +fn write_bitcode_to_file(module: &ModuleCodegen, path: &Path) { unsafe { - let ext = format!("{name}.bc"); - let cgu = Some(&module.name[..]); - let path = cgcx.output_filenames.temp_path_ext(&ext, cgu); - let cstr = path_to_c_string(&path); + let path = path_to_c_string(&path); let llmod = module.module_llvm.llmod(); - llvm::LLVMWriteBitcodeToFile(llmod, cstr.as_ptr()); + llvm::LLVMWriteBitcodeToFile(llmod, path.as_ptr()); } } @@ -530,6 +537,16 @@ fn get_instr_profile_output_path(config: &ModuleConfig) -> Option { config.instrument_coverage.then(|| c"default_%m_%p.profraw".to_owned()) } +// PreAD will run llvm opts but disable size increasing opts (vectorization, loop unrolling) +// DuringAD is the same as above, but also runs the enzyme opt and autodiff passes. +// PostAD will run all opts, including size increasing opts. +#[derive(Debug, Eq, PartialEq)] +pub(crate) enum AutodiffStage { + PreAD, + DuringAD, + PostAD, +} + pub(crate) unsafe fn llvm_optimize( cgcx: &CodegenContext, dcx: DiagCtxtHandle<'_>, @@ -537,7 +554,7 @@ pub(crate) unsafe fn llvm_optimize( config: &ModuleConfig, opt_level: config::OptLevel, opt_stage: llvm::OptStage, - skip_size_increasing_opts: bool, + autodiff_stage: AutodiffStage, ) -> Result<(), FatalError> { // Enzyme: // The whole point of compiler based AD is to differentiate optimized IR instead of unoptimized @@ -550,12 +567,16 @@ pub(crate) unsafe fn llvm_optimize( let unroll_loops; let vectorize_slp; let vectorize_loop; + let run_enzyme = cfg!(llvm_enzyme) && autodiff_stage == AutodiffStage::DuringAD; // When we build rustc with enzyme/autodiff support, we want to postpone size-increasing - // optimizations until after differentiation. FIXME(ZuseZ4): Before shipping on nightly, + // optimizations until after differentiation. Our pipeline is thus: (opt + enzyme), (full opt). + // We therefore have two calls to llvm_optimize, if autodiff is used. + // + // FIXME(ZuseZ4): Before shipping on nightly, // we should make this more granular, or at least check that the user has at least one autodiff // call in their code, to justify altering the compilation pipeline. - if skip_size_increasing_opts && cfg!(llvm_enzyme) { + if cfg!(llvm_enzyme) && autodiff_stage != AutodiffStage::PostAD { unroll_loops = false; vectorize_slp = false; vectorize_loop = false; @@ -565,7 +586,7 @@ pub(crate) unsafe fn llvm_optimize( vectorize_slp = config.vectorize_slp; vectorize_loop = config.vectorize_loop; } - trace!(?unroll_loops, ?vectorize_slp, ?vectorize_loop); + trace!(?unroll_loops, ?vectorize_slp, ?vectorize_loop, ?run_enzyme); let using_thin_buffers = opt_stage == llvm::OptStage::PreLinkThinLTO || config.bitcode_needed(); let pgo_gen_path = get_pgo_gen_path(config); let pgo_use_path = get_pgo_use_path(config); @@ -633,6 +654,7 @@ pub(crate) unsafe fn llvm_optimize( vectorize_loop, config.no_builtins, config.emit_lifetime_markers, + run_enzyme, sanitizer_options.as_ref(), pgo_gen_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), pgo_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), @@ -661,7 +683,6 @@ pub(crate) unsafe fn optimize( ) -> Result<(), FatalError> { let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_optimize", &*module.name); - let llmod = module.module_llvm.llmod(); let llcx = &*module.module_llvm.llcx; let _handlers = DiagnosticHandlers::new(cgcx, dcx, llcx, module, CodegenDiagnosticsStage::Opt); @@ -670,8 +691,7 @@ pub(crate) unsafe fn optimize( if config.emit_no_opt_bc { let out = cgcx.output_filenames.temp_path_ext("no-opt.bc", module_name); - let out = path_to_c_string(&out); - unsafe { llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr()) }; + write_bitcode_to_file(module, &out) } // FIXME(ZuseZ4): support SanitizeHWAddress and prevent illegal/unsupported opts @@ -684,18 +704,14 @@ pub(crate) unsafe fn optimize( _ => llvm::OptStage::PreLinkNoLTO, }; - // If we know that we will later run AD, then we disable vectorization and loop unrolling - let skip_size_increasing_opts = cfg!(llvm_enzyme); + // If we know that we will later run AD, then we disable vectorization and loop unrolling. + // Otherwise we pretend AD is already done and run the normal opt pipeline (=PostAD). + // FIXME(ZuseZ4): Make this more granular, only set PreAD if we actually have autodiff + // usages, not just if we build rustc with autodiff support. + let autodiff_stage = + if cfg!(llvm_enzyme) { AutodiffStage::PreAD } else { AutodiffStage::PostAD }; return unsafe { - llvm_optimize( - cgcx, - dcx, - module, - config, - opt_level, - opt_stage, - skip_size_increasing_opts, - ) + llvm_optimize(cgcx, dcx, module, config, opt_level, opt_stage, autodiff_stage) }; } Ok(()) @@ -744,37 +760,9 @@ pub(crate) unsafe fn codegen( create_msvc_imps(cgcx, llcx, llmod); } - // A codegen-specific pass manager is used to generate object - // files for an LLVM module. - // - // Apparently each of these pass managers is a one-shot kind of - // thing, so we create a new one for each type of output. The - // pass manager passed to the closure should be ensured to not - // escape the closure itself, and the manager should only be - // used once. - unsafe fn with_codegen<'ll, F, R>( - tm: &'ll llvm::TargetMachine, - llmod: &'ll llvm::Module, - no_builtins: bool, - f: F, - ) -> R - where - F: FnOnce(&'ll mut PassManager<'ll>) -> R, - { - unsafe { - let cpm = llvm::LLVMCreatePassManager(); - llvm::LLVMAddAnalysisPasses(tm, cpm); - llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins); - f(cpm) - } - } - - // Two things to note: - // - If object files are just LLVM bitcode we write bitcode, copy it to - // the .o file, and delete the bitcode if it wasn't otherwise - // requested. - // - If we don't have the integrated assembler then we need to emit - // asm from LLVM and use `gcc` to create the object file. + // Note that if object files are just LLVM bitcode we write bitcode, + // copy it to the .o file, and delete the bitcode if it wasn't + // otherwise requested. let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name); let bc_summary_out = @@ -890,21 +878,17 @@ pub(crate) unsafe fn codegen( } else { llmod }; - unsafe { - with_codegen(tm, llmod, config.no_builtins, |cpm| { - write_output_file( - dcx, - tm, - cpm, - llmod, - &path, - None, - llvm::FileType::AssemblyFile, - &cgcx.prof, - config.verify_llvm_ir, - ) - })?; - } + write_output_file( + dcx, + tm, + config.no_builtins, + llmod, + &path, + None, + llvm::FileType::AssemblyFile, + &cgcx.prof, + config.verify_llvm_ir, + )?; } match config.emit_obj { @@ -928,21 +912,17 @@ pub(crate) unsafe fn codegen( (_, SplitDwarfKind::Split) => Some(dwo_out.as_path()), }; - unsafe { - with_codegen(tm, llmod, config.no_builtins, |cpm| { - write_output_file( - dcx, - tm, - cpm, - llmod, - &obj_out, - dwo_out, - llvm::FileType::ObjectFile, - &cgcx.prof, - config.verify_llvm_ir, - ) - })?; - } + write_output_file( + dcx, + tm, + config.no_builtins, + llmod, + &obj_out, + dwo_out, + llvm::FileType::ObjectFile, + &cgcx.prof, + config.verify_llvm_ir, + )?; } EmitObj::Bitcode => { @@ -1069,24 +1049,18 @@ unsafe fn embed_bitcode( { // We don't need custom section flags, create LLVM globals. let llconst = common::bytes_in_context(llcx, bitcode); - let llglobal = llvm::LLVMAddGlobal( - llmod, - common::val_ty(llconst), - c"rustc.embedded.module".as_ptr(), - ); - llvm::LLVMSetInitializer(llglobal, llconst); + let llglobal = + llvm::add_global(llmod, common::val_ty(llconst), c"rustc.embedded.module"); + llvm::set_initializer(llglobal, llconst); llvm::set_section(llglobal, bitcode_section_name(cgcx)); llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage); llvm::LLVMSetGlobalConstant(llglobal, llvm::True); let llconst = common::bytes_in_context(llcx, cmdline.as_bytes()); - let llglobal = llvm::LLVMAddGlobal( - llmod, - common::val_ty(llconst), - c"rustc.embedded.cmdline".as_ptr(), - ); - llvm::LLVMSetInitializer(llglobal, llconst); + let llglobal = + llvm::add_global(llmod, common::val_ty(llconst), c"rustc.embedded.cmdline"); + llvm::set_initializer(llglobal, llconst); let section = if cgcx.target_is_like_osx { c"__LLVM,__cmdline" } else if cgcx.target_is_like_aix { @@ -1126,31 +1100,29 @@ fn create_msvc_imps( // underscores added in front). let prefix = if cgcx.target_arch == "x86" { "\x01__imp__" } else { "\x01__imp_" }; - unsafe { - let ptr_ty = Type::ptr_llcx(llcx); - let globals = base::iter_globals(llmod) - .filter(|&val| { - llvm::get_linkage(val) == llvm::Linkage::ExternalLinkage - && llvm::LLVMIsDeclaration(val) == 0 - }) - .filter_map(|val| { - // Exclude some symbols that we know are not Rust symbols. - let name = llvm::get_value_name(val); - if ignored(name) { None } else { Some((val, name)) } - }) - .map(move |(val, name)| { - let mut imp_name = prefix.as_bytes().to_vec(); - imp_name.extend(name); - let imp_name = CString::new(imp_name).unwrap(); - (imp_name, val) - }) - .collect::>(); + let ptr_ty = Type::ptr_llcx(llcx); + let globals = base::iter_globals(llmod) + .filter(|&val| { + llvm::get_linkage(val) == llvm::Linkage::ExternalLinkage && !llvm::is_declaration(val) + }) + .filter_map(|val| { + // Exclude some symbols that we know are not Rust symbols. + let name = llvm::get_value_name(val); + if ignored(name) { None } else { Some((val, name)) } + }) + .map(move |(val, name)| { + let mut imp_name = prefix.as_bytes().to_vec(); + imp_name.extend(name); + let imp_name = CString::new(imp_name).unwrap(); + (imp_name, val) + }) + .collect::>(); - for (imp_name, val) in globals { - let imp = llvm::LLVMAddGlobal(llmod, ptr_ty, imp_name.as_ptr()); - llvm::LLVMSetInitializer(imp, val); - llvm::set_linkage(imp, llvm::Linkage::ExternalLinkage); - } + for (imp_name, val) in globals { + let imp = llvm::add_global(llmod, ptr_ty, &imp_name); + + llvm::set_initializer(imp, val); + llvm::set_linkage(imp, llvm::Linkage::ExternalLinkage); } // Use this function to exclude certain symbols from `__imp` generation. diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index d05faf5577b01..d35c7945baec4 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -157,9 +157,7 @@ pub(crate) fn linkage_to_llvm(linkage: Linkage) -> llvm::Linkage { Linkage::LinkOnceODR => llvm::Linkage::LinkOnceODRLinkage, Linkage::WeakAny => llvm::Linkage::WeakAnyLinkage, Linkage::WeakODR => llvm::Linkage::WeakODRLinkage, - Linkage::Appending => llvm::Linkage::AppendingLinkage, Linkage::Internal => llvm::Linkage::InternalLinkage, - Linkage::Private => llvm::Linkage::PrivateLinkage, Linkage::ExternalWeak => llvm::Linkage::ExternalWeakLinkage, Linkage::Common => llvm::Linkage::CommonLinkage, } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 5a34b52e6ef30..57f09b1fa2fd2 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1,4 +1,4 @@ -use std::borrow::Cow; +use std::borrow::{Borrow, Cow}; use std::ops::Deref; use std::{iter, ptr}; @@ -31,20 +31,22 @@ use tracing::{debug, instrument}; use crate::abi::FnAbiLlvmExt; use crate::attributes; use crate::common::Funclet; -use crate::context::CodegenCx; +use crate::context::{CodegenCx, SimpleCx}; use crate::llvm::{self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, False, True}; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; -// All Builders must have an llfn associated with them #[must_use] -pub(crate) struct Builder<'a, 'll, 'tcx> { +pub(crate) struct GenericBuilder<'a, 'll, CX: Borrow>> { pub llbuilder: &'ll mut llvm::Builder<'ll>, - pub cx: &'a CodegenCx<'ll, 'tcx>, + pub cx: &'a CX, } -impl Drop for Builder<'_, '_, '_> { +pub(crate) type SBuilder<'a, 'll> = GenericBuilder<'a, 'll, SimpleCx<'ll>>; +pub(crate) type Builder<'a, 'll, 'tcx> = GenericBuilder<'a, 'll, CodegenCx<'ll, 'tcx>>; + +impl<'a, 'll, CX: Borrow>> Drop for GenericBuilder<'a, 'll, CX> { fn drop(&mut self) { unsafe { llvm::LLVMDisposeBuilder(&mut *(self.llbuilder as *mut _)); @@ -52,6 +54,112 @@ impl Drop for Builder<'_, '_, '_> { } } +impl<'a, 'll> SBuilder<'a, 'll> { + fn call( + &mut self, + llty: &'ll Type, + llfn: &'ll Value, + args: &[&'ll Value], + funclet: Option<&Funclet<'ll>>, + ) -> &'ll Value { + debug!("call {:?} with args ({:?})", llfn, args); + + let args = self.check_call("call", llty, llfn, args); + let funclet_bundle = funclet.map(|funclet| funclet.bundle()); + let mut bundles: SmallVec<[_; 2]> = SmallVec::new(); + if let Some(funclet_bundle) = funclet_bundle { + bundles.push(funclet_bundle); + } + + let call = unsafe { + llvm::LLVMBuildCallWithOperandBundles( + self.llbuilder, + llty, + llfn, + args.as_ptr() as *const &llvm::Value, + args.len() as c_uint, + bundles.as_ptr(), + bundles.len() as c_uint, + c"".as_ptr(), + ) + }; + call + } + + fn with_scx(scx: &'a SimpleCx<'ll>) -> Self { + // Create a fresh builder from the simple context. + let llbuilder = unsafe { llvm::LLVMCreateBuilderInContext(scx.llcx) }; + SBuilder { llbuilder, cx: scx } + } +} +impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { + pub(crate) fn bitcast(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { + unsafe { llvm::LLVMBuildBitCast(self.llbuilder, val, dest_ty, UNNAMED) } + } + + fn ret_void(&mut self) { + unsafe { + llvm::LLVMBuildRetVoid(self.llbuilder); + } + } + + fn ret(&mut self, v: &'ll Value) { + unsafe { + llvm::LLVMBuildRet(self.llbuilder, v); + } + } +} +impl<'a, 'll> SBuilder<'a, 'll> { + fn build(cx: &'a SimpleCx<'ll>, llbb: &'ll BasicBlock) -> SBuilder<'a, 'll> { + let bx = SBuilder::with_scx(cx); + unsafe { + llvm::LLVMPositionBuilderAtEnd(bx.llbuilder, llbb); + } + bx + } + + fn check_call<'b>( + &mut self, + typ: &str, + fn_ty: &'ll Type, + llfn: &'ll Value, + args: &'b [&'ll Value], + ) -> Cow<'b, [&'ll Value]> { + assert!( + self.cx.type_kind(fn_ty) == TypeKind::Function, + "builder::{typ} not passed a function, but {fn_ty:?}" + ); + + let param_tys = self.cx.func_params_types(fn_ty); + + let all_args_match = iter::zip(¶m_tys, args.iter().map(|&v| self.cx.val_ty(v))) + .all(|(expected_ty, actual_ty)| *expected_ty == actual_ty); + + if all_args_match { + return Cow::Borrowed(args); + } + + let casted_args: Vec<_> = iter::zip(param_tys, args) + .enumerate() + .map(|(i, (expected_ty, &actual_val))| { + let actual_ty = self.cx.val_ty(actual_val); + if expected_ty != actual_ty { + debug!( + "type mismatch in function call of {:?}. \ + Expected {:?} for param {}, got {:?}; injecting bitcast", + llfn, expected_ty, i, actual_ty + ); + self.bitcast(actual_val, expected_ty) + } else { + actual_val + } + }) + .collect(); + + Cow::Owned(casted_args) + } +} + /// Empty string, to be used where LLVM expects an instruction name, indicating /// that the instruction is to be left unnamed (i.e. numbered, in textual IR). // FIXME(eddyb) pass `&CStr` directly to FFI once it's a thin pointer. @@ -313,6 +421,51 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { unchecked_umul(x, y) => LLVMBuildNUWMul, } + fn unchecked_suadd(&mut self, a: &'ll Value, b: &'ll Value) -> &'ll Value { + unsafe { + let add = llvm::LLVMBuildAdd(self.llbuilder, a, b, UNNAMED); + if llvm::LLVMIsAInstruction(add).is_some() { + llvm::LLVMSetNUW(add, True); + llvm::LLVMSetNSW(add, True); + } + add + } + } + fn unchecked_susub(&mut self, a: &'ll Value, b: &'ll Value) -> &'ll Value { + unsafe { + let sub = llvm::LLVMBuildSub(self.llbuilder, a, b, UNNAMED); + if llvm::LLVMIsAInstruction(sub).is_some() { + llvm::LLVMSetNUW(sub, True); + llvm::LLVMSetNSW(sub, True); + } + sub + } + } + fn unchecked_sumul(&mut self, a: &'ll Value, b: &'ll Value) -> &'ll Value { + unsafe { + let mul = llvm::LLVMBuildMul(self.llbuilder, a, b, UNNAMED); + if llvm::LLVMIsAInstruction(mul).is_some() { + llvm::LLVMSetNUW(mul, True); + llvm::LLVMSetNSW(mul, True); + } + mul + } + } + + fn or_disjoint(&mut self, a: &'ll Value, b: &'ll Value) -> &'ll Value { + unsafe { + let or = llvm::LLVMBuildOr(self.llbuilder, a, b, UNNAMED); + + // If a and b are both values, then `or` is a value, rather than + // an instruction, so we need to check before setting the flag. + // (See also `LLVMBuildNUWNeg` which also needs a check.) + if llvm::LLVMIsAInstruction(or).is_some() { + llvm::LLVMSetIsDisjoint(or, True); + } + or + } + } + set_math_builder_methods! { fadd_fast(x, y) => (LLVMBuildFAdd, LLVMRustSetFastMath), fsub_fast(x, y) => (LLVMBuildFSub, LLVMRustSetFastMath), @@ -421,7 +574,8 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { unsafe { let alloca = llvm::LLVMBuildAlloca(bx.llbuilder, ty, UNNAMED); llvm::LLVMSetAlignment(alloca, align.bytes() as c_uint); - alloca + // Cast to default addrspace if necessary + llvm::LLVMBuildPointerCast(bx.llbuilder, alloca, self.cx().type_ptr(), UNNAMED) } } @@ -430,7 +584,8 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { let alloca = llvm::LLVMBuildArrayAlloca(self.llbuilder, self.cx().type_i8(), size, UNNAMED); llvm::LLVMSetAlignment(alloca, align.bytes() as c_uint); - alloca + // Cast to default addrspace if necessary + llvm::LLVMBuildPointerCast(self.llbuilder, alloca, self.cx().type_ptr(), UNNAMED) } } @@ -1217,11 +1372,21 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { impl<'ll> StaticBuilderMethods for Builder<'_, 'll, '_> { fn get_static(&mut self, def_id: DefId) -> &'ll Value { // Forward to the `get_static` method of `CodegenCx` - self.cx().get_static(def_id) + let s = self.cx().get_static(def_id); + // Cast to default address space if globals are in a different addrspace + self.cx().const_pointercast(s, self.type_ptr()) } } impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { + fn build(cx: &'a CodegenCx<'ll, 'tcx>, llbb: &'ll BasicBlock) -> Builder<'a, 'll, 'tcx> { + let bx = Builder::with_cx(cx); + unsafe { + llvm::LLVMPositionBuilderAtEnd(bx.llbuilder, llbb); + } + bx + } + fn with_cx(cx: &'a CodegenCx<'ll, 'tcx>) -> Self { // Create a fresh builder from the crate context. let llbuilder = unsafe { llvm::LLVMCreateBuilderInContext(cx.llcx) }; @@ -1231,13 +1396,16 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { pub(crate) fn llfn(&self) -> &'ll Value { unsafe { llvm::LLVMGetBasicBlockParent(self.llbb()) } } +} +impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { fn position_at_start(&mut self, llbb: &'ll BasicBlock) { unsafe { llvm::LLVMRustPositionBuilderAtStart(self.llbuilder, llbb); } } - +} +impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { fn align_metadata(&mut self, load: &'ll Value, align: Align) { unsafe { let md = [llvm::LLVMValueAsMetadata(self.cx.const_u64(align.bytes()))]; @@ -1259,7 +1427,8 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { self.set_metadata(inst, llvm::MD_unpredictable, md); } } - +} +impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { pub(crate) fn minnum(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { unsafe { llvm::LLVMRustBuildMinNum(self.llbuilder, lhs, rhs) } } @@ -1360,7 +1529,9 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { let ret = unsafe { llvm::LLVMBuildCatchRet(self.llbuilder, funclet.cleanuppad(), unwind) }; ret.expect("LLVM does not have support for catchret") } +} +impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { fn check_call<'b>( &mut self, typ: &str, @@ -1401,11 +1572,13 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { Cow::Owned(casted_args) } - +} +impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { pub(crate) fn va_arg(&mut self, list: &'ll Value, ty: &'ll Type) -> &'ll Value { unsafe { llvm::LLVMBuildVAArg(self.llbuilder, list, ty, UNNAMED) } } - +} +impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { pub(crate) fn call_intrinsic(&mut self, intrinsic: &str, args: &[&'ll Value]) -> &'ll Value { let (ty, f) = self.cx.get_intrinsic(intrinsic); self.call(ty, None, None, f, args, None, None) @@ -1423,7 +1596,8 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { self.call_intrinsic(intrinsic, &[self.cx.const_u64(size), ptr]); } - +} +impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { pub(crate) fn phi( &mut self, ty: &'ll Type, @@ -1443,7 +1617,8 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { llvm::LLVMAddIncoming(phi, &val, &bb, 1 as c_uint); } } - +} +impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { fn fptoint_sat(&mut self, signed: bool, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { let src_ty = self.cx.val_ty(val); let (float_ty, int_ty, vector_length) = if self.cx.type_kind(src_ty) == TypeKind::Vector { diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index 38f7eaa090f91..b2c1088e3fc02 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -3,20 +3,18 @@ use std::ptr; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, AutoDiffItem, DiffActivity, DiffMode}; use rustc_codegen_ssa::ModuleCodegen; use rustc_codegen_ssa::back::write::ModuleConfig; -use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods}; use rustc_errors::FatalError; -use rustc_middle::ty::TyCtxt; -use rustc_session::config::Lto; use tracing::{debug, trace}; -use crate::back::write::{llvm_err, llvm_optimize}; -use crate::builder::Builder; -use crate::declare::declare_raw_fn; +use crate::back::write::llvm_err; +use crate::builder::SBuilder; +use crate::context::SimpleCx; +use crate::declare::declare_simple_fn; use crate::errors::LlvmError; use crate::llvm::AttributePlace::Function; use crate::llvm::{Metadata, True}; use crate::value::Value; -use crate::{CodegenContext, LlvmCodegenBackend, ModuleLlvm, attributes, context, llvm}; +use crate::{CodegenContext, LlvmCodegenBackend, ModuleLlvm, attributes, llvm}; fn get_params(fnc: &Value) -> Vec<&Value> { unsafe { @@ -38,8 +36,8 @@ fn get_params(fnc: &Value) -> Vec<&Value> { /// [^1]: // FIXME(ZuseZ4): `outer_fn` should include upstream safety checks to // cover some assumptions of enzyme/autodiff, which could lead to UB otherwise. -fn generate_enzyme_call<'ll, 'tcx>( - cx: &context::CodegenCx<'ll, 'tcx>, +fn generate_enzyme_call<'ll>( + cx: &SimpleCx<'ll>, fn_to_diff: &'ll Value, outer_fn: &'ll Value, attrs: AutoDiffAttrs, @@ -54,8 +52,6 @@ fn generate_enzyme_call<'ll, 'tcx>( let mut ad_name: String = match attrs.mode { DiffMode::Forward => "__enzyme_fwddiff", DiffMode::Reverse => "__enzyme_autodiff", - DiffMode::ForwardFirst => "__enzyme_fwddiff", - DiffMode::ReverseFirst => "__enzyme_autodiff", _ => panic!("logic bug in autodiff, unrecognized mode"), } .to_string(); @@ -63,8 +59,8 @@ fn generate_enzyme_call<'ll, 'tcx>( // add outer_fn name to ad_name to make it unique, in case users apply autodiff to multiple // functions. Unwrap will only panic, if LLVM gave us an invalid string. let name = llvm::get_value_name(outer_fn); - let outer_fn_name = std::ffi::CStr::from_bytes_with_nul(name).unwrap().to_str().unwrap(); - ad_name.push_str(outer_fn_name.to_string().as_str()); + let outer_fn_name = std::str::from_utf8(name).unwrap(); + ad_name.push_str(outer_fn_name); // Let us assume the user wrote the following function square: // @@ -112,7 +108,7 @@ fn generate_enzyme_call<'ll, 'tcx>( //FIXME(ZuseZ4): the CC/Addr/Vis values are best effort guesses, we should look at tests and // think a bit more about what should go here. let cc = llvm::LLVMGetFunctionCallConv(outer_fn); - let ad_fn = declare_raw_fn( + let ad_fn = declare_simple_fn( cx, &ad_name, llvm::CallConv::try_from(cc).expect("invalid callconv"), @@ -132,7 +128,7 @@ fn generate_enzyme_call<'ll, 'tcx>( llvm::LLVMRustEraseInstFromParent(br); let last_inst = llvm::LLVMRustGetLastInstruction(entry).unwrap(); - let mut builder = Builder::build(cx, entry); + let mut builder = SBuilder::build(cx, entry); let num_args = llvm::LLVMCountParams(&fn_to_diff); let mut args = Vec::with_capacity(num_args as usize + 1); @@ -154,7 +150,7 @@ fn generate_enzyme_call<'ll, 'tcx>( _ => {} } - trace!("matching autodiff arguments"); + debug!("matching autodiff arguments"); // We now handle the issue that Rust level arguments not always match the llvm-ir level // arguments. A slice, `&[f32]`, for example, is represented as a pointer and a length on // llvm-ir level. The number of activities matches the number of Rust level arguments, so we @@ -165,10 +161,10 @@ fn generate_enzyme_call<'ll, 'tcx>( let mut activity_pos = 0; let outer_args: Vec<&llvm::Value> = get_params(outer_fn); while activity_pos < inputs.len() { - let activity = inputs[activity_pos as usize]; + let diff_activity = inputs[activity_pos as usize]; // Duplicated arguments received a shadow argument, into which enzyme will write the // gradient. - let (activity, duplicated): (&Metadata, bool) = match activity { + let (activity, duplicated): (&Metadata, bool) = match diff_activity { DiffActivity::None => panic!("not a valid input activity"), DiffActivity::Const => (enzyme_const, false), DiffActivity::Active => (enzyme_out, false), @@ -223,7 +219,15 @@ fn generate_enzyme_call<'ll, 'tcx>( // A duplicated pointer will have the following two outer_fn arguments: // (..., ptr, ptr, ...). We add the following llvm-ir to our __enzyme call: // (..., metadata! enzyme_dup, ptr, ptr, ...). - assert!(llvm::LLVMRustGetTypeKind(next_outer_ty) == llvm::TypeKind::Pointer); + if matches!( + diff_activity, + DiffActivity::Duplicated | DiffActivity::DuplicatedOnly + ) { + assert!( + llvm::LLVMRustGetTypeKind(next_outer_ty) == llvm::TypeKind::Pointer + ); + } + // In the case of Dual we don't have assumptions, e.g. f32 would be valid. args.push(next_outer_arg); outer_pos += 2; activity_pos += 1; @@ -236,7 +240,7 @@ fn generate_enzyme_call<'ll, 'tcx>( } } - let call = builder.call(enzyme_ty, None, None, ad_fn, &args, None, None); + let call = builder.call(enzyme_ty, ad_fn, &args, None); // This part is a bit iffy. LLVM requires that a call to an inlineable function has some // metadata attachted to it, but we just created this code oota. Given that the @@ -256,14 +260,14 @@ fn generate_enzyme_call<'ll, 'tcx>( // have no debug info to copy, which would then be ok. trace!("no dbg info"); } + // Now that we copied the metadata, get rid of dummy code. - llvm::LLVMRustEraseInstBefore(entry, last_inst); - llvm::LLVMRustEraseInstFromParent(last_inst); + llvm::LLVMRustEraseInstUntilInclusive(entry, last_inst); - if cx.val_ty(outer_fn) != cx.type_void() { - builder.ret(call); - } else { + if cx.val_ty(call) == cx.type_void() { builder.ret_void(); + } else { + builder.ret(call); } // Let's crash in case that we messed something up above and generated invalid IR. @@ -274,40 +278,44 @@ fn generate_enzyme_call<'ll, 'tcx>( } } -pub(crate) fn differentiate<'ll, 'tcx>( +pub(crate) fn differentiate<'ll>( module: &'ll ModuleCodegen, cgcx: &CodegenContext, - tcx: TyCtxt<'tcx>, diff_items: Vec, - config: &ModuleConfig, + _config: &ModuleConfig, ) -> Result<(), FatalError> { for item in &diff_items { trace!("{}", item); } let diag_handler = cgcx.create_dcx(); - let (_, cgus) = tcx.collect_and_partition_mono_items(()); - let cx = context::CodegenCx::new(tcx, &cgus.first().unwrap(), &module.module_llvm); + let cx = SimpleCx { llmod: module.module_llvm.llmod(), llcx: module.module_llvm.llcx }; // Before dumping the module, we want all the TypeTrees to become part of the module. for item in diff_items.iter() { let name = item.source.clone(); let fn_def: Option<&llvm::Value> = cx.get_function(&name); let Some(fn_def) = fn_def else { - return Err(llvm_err(diag_handler.handle(), LlvmError::PrepareAutoDiff { - src: item.source.clone(), - target: item.target.clone(), - error: "could not find source function".to_owned(), - })); + return Err(llvm_err( + diag_handler.handle(), + LlvmError::PrepareAutoDiff { + src: item.source.clone(), + target: item.target.clone(), + error: "could not find source function".to_owned(), + }, + )); }; debug!(?item.target); let fn_target: Option<&llvm::Value> = cx.get_function(&item.target); let Some(fn_target) = fn_target else { - return Err(llvm_err(diag_handler.handle(), LlvmError::PrepareAutoDiff { - src: item.source.clone(), - target: item.target.clone(), - error: "could not find target function".to_owned(), - })); + return Err(llvm_err( + diag_handler.handle(), + LlvmError::PrepareAutoDiff { + src: item.source.clone(), + target: item.target.clone(), + error: "could not find target function".to_owned(), + }, + )); }; generate_enzyme_call(&cx, fn_def, fn_target, item.attrs.clone()); @@ -315,29 +323,6 @@ pub(crate) fn differentiate<'ll, 'tcx>( // FIXME(ZuseZ4): support SanitizeHWAddress and prevent illegal/unsupported opts - if let Some(opt_level) = config.opt_level { - let opt_stage = match cgcx.lto { - Lto::Fat => llvm::OptStage::PreLinkFatLTO, - Lto::Thin | Lto::ThinLocal => llvm::OptStage::PreLinkThinLTO, - _ if cgcx.opts.cg.linker_plugin_lto.enabled() => llvm::OptStage::PreLinkThinLTO, - _ => llvm::OptStage::PreLinkNoLTO, - }; - // This is our second opt call, so now we run all opts, - // to make sure we get the best performance. - let skip_size_increasing_opts = false; - trace!("running Module Optimization after differentiation"); - unsafe { - llvm_optimize( - cgcx, - diag_handler.handle(), - module, - config, - opt_level, - opt_stage, - skip_size_increasing_opts, - )? - }; - } trace!("done with differentiate()"); Ok(()) diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index adfe8aeb5c539..35ac6158e9772 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -215,12 +215,14 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { let g = self.define_global(&sym, self.val_ty(sc)).unwrap_or_else(|| { bug!("symbol `{}` is already defined", sym); }); + llvm::set_initializer(g, sc); unsafe { - llvm::LLVMSetInitializer(g, sc); llvm::LLVMSetGlobalConstant(g, True); llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global); } llvm::set_linkage(g, llvm::Linkage::InternalLinkage); + // Cast to default address space if globals are in a different addrspace + let g = self.const_pointercast(g, self.type_ptr()); (s.to_owned(), g) }) .1; @@ -285,7 +287,7 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { let alloc = alloc.inner(); let value = match alloc.mutability { Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None), - _ => self.static_addr_of(init, alloc.align, None), + _ => self.static_addr_of_impl(init, alloc.align, None), }; if !self.sess().fewer_names() && llvm::get_value_name(value).is_empty() { @@ -308,10 +310,15 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { GlobalAlloc::VTable(ty, dyn_ty) => { let alloc = self .tcx - .global_alloc(self.tcx.vtable_allocation((ty, dyn_ty.principal()))) + .global_alloc(self.tcx.vtable_allocation(( + ty, + dyn_ty.principal().map(|principal| { + self.tcx.instantiate_bound_regions_with_erased(principal) + }), + ))) .unwrap_memory(); let init = const_alloc_to_llvm(self, alloc, /*static*/ false); - let value = self.static_addr_of(init, alloc.inner().align, None); + let value = self.static_addr_of_impl(init, alloc.inner().align, None); (value, AddressSpace::DATA) } GlobalAlloc::Static(def_id) => { @@ -323,7 +330,8 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { let llval = unsafe { llvm::LLVMConstInBoundsGEP2( self.type_i8(), - self.const_bitcast(base_addr, self.type_ptr_ext(base_addr_space)), + // Cast to the required address space if necessary + self.const_pointercast(base_addr, self.type_ptr_ext(base_addr_space)), &self.const_usize(offset.bytes()), 1, ) diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index c7114480d8bdf..4a5491ec7a18c 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -191,7 +191,7 @@ fn check_and_apply_linkage<'ll, 'tcx>( }) }); llvm::set_linkage(g2, llvm::Linkage::InternalLinkage); - unsafe { llvm::LLVMSetInitializer(g2, g1) }; + llvm::set_initializer(g2, g1); g2 } else if cx.tcx.sess.target.arch == "x86" && common::is_mingw_gnu_toolchain(&cx.tcx.sess.target) @@ -210,6 +210,14 @@ impl<'ll> CodegenCx<'ll, '_> { unsafe { llvm::LLVMConstBitCast(val, ty) } } + pub(crate) fn const_pointercast(&self, val: &'ll Value, ty: &'ll Type) -> &'ll Value { + unsafe { llvm::LLVMConstPointerCast(val, ty) } + } + + /// Create a global variable. + /// + /// The returned global variable is a pointer in the default address space for globals. + /// Fails if a symbol with the given name already exists. pub(crate) fn static_addr_of_mut( &self, cv: &'ll Value, @@ -227,12 +235,40 @@ impl<'ll> CodegenCx<'ll, '_> { } _ => self.define_private_global(self.val_ty(cv)), }; - unsafe { llvm::LLVMSetInitializer(gv, cv) }; + llvm::set_initializer(gv, cv); set_global_alignment(self, gv, align); llvm::SetUnnamedAddress(gv, llvm::UnnamedAddr::Global); gv } + /// Create a global constant. + /// + /// The returned global variable is a pointer in the default address space for globals. + pub(crate) fn static_addr_of_impl( + &self, + cv: &'ll Value, + align: Align, + kind: Option<&str>, + ) -> &'ll Value { + if let Some(&gv) = self.const_globals.borrow().get(&cv) { + unsafe { + // Upgrade the alignment in cases where the same constant is used with different + // alignment requirements + let llalign = align.bytes() as u32; + if llalign > llvm::LLVMGetAlignment(gv) { + llvm::LLVMSetAlignment(gv, llalign); + } + } + return gv; + } + let gv = self.static_addr_of_mut(cv, align, kind); + unsafe { + llvm::LLVMSetGlobalConstant(gv, True); + } + self.const_globals.borrow_mut().insert(cv, gv); + gv + } + #[instrument(level = "debug", skip(self))] pub(crate) fn get_static(&self, def_id: DefId) -> &'ll Value { let instance = Instance::mono(self.tcx, def_id); @@ -384,8 +420,14 @@ impl<'ll> CodegenCx<'ll, '_> { let g = if val_llty == llty { g } else { - // If we created the global with the wrong type, - // correct the type. + // codegen_static_initializer creates the global value just from the + // `Allocation` data by generating one big struct value that is just + // all the bytes and pointers after each other. This will almost never + // match the type that the static was declared with. Unfortunately + // we can't just LLVMConstBitCast our way out of it because that has very + // specific rules on what can be cast. So instead of adding a new way to + // generate static initializers that match the static's type, we picked + // the easier option and retroactively change the type of the static item itself. let name = llvm::get_value_name(g).to_vec(); llvm::set_value_name(g, b""); @@ -416,7 +458,7 @@ impl<'ll> CodegenCx<'ll, '_> { new_g }; set_global_alignment(self, g, alloc.align); - llvm::LLVMSetInitializer(g, v); + llvm::set_initializer(g, v); if self.should_assume_dso_local(g, true) { llvm::LLVMRustSetDSOLocal(g, true); @@ -505,24 +547,15 @@ impl<'ll> CodegenCx<'ll, '_> { } impl<'ll> StaticCodegenMethods for CodegenCx<'ll, '_> { + /// Get a pointer to a global variable. + /// + /// The pointer will always be in the default address space. If global variables default to a + /// different address space, an addrspacecast is inserted. fn static_addr_of(&self, cv: &'ll Value, align: Align, kind: Option<&str>) -> &'ll Value { - if let Some(&gv) = self.const_globals.borrow().get(&cv) { - unsafe { - // Upgrade the alignment in cases where the same constant is used with different - // alignment requirements - let llalign = align.bytes() as u32; - if llalign > llvm::LLVMGetAlignment(gv) { - llvm::LLVMSetAlignment(gv, llalign); - } - } - return gv; - } - let gv = self.static_addr_of_mut(cv, align, kind); - unsafe { - llvm::LLVMSetGlobalConstant(gv, True); - } - self.const_globals.borrow_mut().insert(cv, gv); - gv + let gv = self.static_addr_of_impl(cv, align, kind); + // static_addr_of_impl returns the bare global variable, which might not be in the default + // address space. Cast to the default address space if necessary. + self.const_pointercast(gv, self.type_ptr()) } fn codegen_static(&self, def_id: DefId) { diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index d8fbe51b975a0..7fe527a4c07d0 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -1,11 +1,13 @@ use std::borrow::Borrow; use std::cell::{Cell, RefCell}; use std::ffi::{CStr, c_char, c_uint}; +use std::ops::Deref; use std::str; use rustc_abi::{HasDataLayout, TargetDataLayout, VariantIdx}; use rustc_codegen_ssa::back::versioned_llvm_target; use rustc_codegen_ssa::base::{wants_msvc_seh, wants_wasm_eh}; +use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::errors as ssa_errors; use rustc_codegen_ssa::traits::*; use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, ToBaseN}; @@ -30,30 +32,52 @@ use smallvec::SmallVec; use crate::back::write::to_llvm_code_model; use crate::callee::get_fn; -use crate::common::AsCCharPtr; +use crate::common::{self, AsCCharPtr}; use crate::debuginfo::metadata::apply_vcall_visibility_metadata; use crate::llvm::{Metadata, MetadataType}; use crate::type_::Type; use crate::value::Value; use crate::{attributes, coverageinfo, debuginfo, llvm, llvm_util}; +/// `TyCtxt` (and related cache datastructures) can't be move between threads. +/// However, there are various cx related functions which we want to be available to the builder and +/// other compiler pieces. Here we define a small subset which has enough information and can be +/// moved around more freely. +pub(crate) struct SimpleCx<'ll> { + pub llmod: &'ll llvm::Module, + pub llcx: &'ll llvm::Context, +} + +impl<'ll> Borrow> for CodegenCx<'ll, '_> { + fn borrow(&self) -> &SimpleCx<'ll> { + &self.scx + } +} + +impl<'ll, 'tcx> Deref for CodegenCx<'ll, 'tcx> { + type Target = SimpleCx<'ll>; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.scx + } +} + /// There is one `CodegenCx` per codegen unit. Each one has its own LLVM /// `llvm::Context` so that several codegen units may be processed in parallel. /// All other LLVM data structures in the `CodegenCx` are tied to that `llvm::Context`. pub(crate) struct CodegenCx<'ll, 'tcx> { pub tcx: TyCtxt<'tcx>, + pub scx: SimpleCx<'ll>, pub use_dll_storage_attrs: bool, pub tls_model: llvm::ThreadLocalMode, - pub llmod: &'ll llvm::Module, - pub llcx: &'ll llvm::Context, pub codegen_unit: &'tcx CodegenUnit<'tcx>, /// Cache instances of monomorphic and polymorphic items pub instances: RefCell, &'ll Value>>, /// Cache generated vtables - pub vtables: - RefCell, Option>), &'ll Value>>, + pub vtables: RefCell, Option>), &'ll Value>>, /// Cache of constant strings, pub const_str_cache: RefCell>, @@ -553,10 +577,9 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { CodegenCx { tcx, + scx: SimpleCx { llcx, llmod }, use_dll_storage_attrs, tls_model, - llmod, - llcx, codegen_unit, instances: Default::default(), vtables: Default::default(), @@ -593,12 +616,15 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { pub(crate) fn create_used_variable_impl(&self, name: &'static CStr, values: &[&'ll Value]) { let array = self.const_array(self.type_ptr(), values); - unsafe { - let g = llvm::LLVMAddGlobal(self.llmod, self.val_ty(array), name.as_ptr()); - llvm::LLVMSetInitializer(g, array); - llvm::set_linkage(g, llvm::Linkage::AppendingLinkage); - llvm::set_section(g, c"llvm.metadata"); - } + let g = llvm::add_global(self.llmod, self.val_ty(array), name); + llvm::set_initializer(g, array); + llvm::set_linkage(g, llvm::Linkage::AppendingLinkage); + llvm::set_section(g, c"llvm.metadata"); + } +} +impl<'ll> SimpleCx<'ll> { + pub(crate) fn val_ty(&self, v: &'ll Value) -> &'ll Type { + common::val_ty(v) } pub(crate) fn get_metadata_value(&self, metadata: &'ll Metadata) -> &'ll Value { @@ -625,20 +651,23 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { llvm::LLVMMDStringInContext2(self.llcx, name.as_ptr() as *const c_char, name.len()) }) } + + pub(crate) fn type_kind(&self, ty: &'ll Type) -> TypeKind { + unsafe { llvm::LLVMRustGetTypeKind(ty).to_generic() } + } } impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn vtables( &self, - ) -> &RefCell, Option>), &'ll Value>> - { + ) -> &RefCell, Option>), &'ll Value>> { &self.vtables } fn apply_vcall_visibility_metadata( &self, ty: Ty<'tcx>, - poly_trait_ref: Option>, + poly_trait_ref: Option>, vtable: &'ll Value, ) { apply_vcall_visibility_metadata(self, ty, poly_trait_ref, vtable); @@ -741,14 +770,16 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { if self.get_declared_value(entry_name).is_none() { Some(self.declare_entry_fn( entry_name, - self.sess().target.entry_abi.into(), + llvm::CallConv::from_conv( + self.sess().target.entry_abi, + self.sess().target.arch.borrow(), + ), llvm::UnnamedAddr::Global, fn_type, )) } else { // If the symbol already exists, it is an error: for example, the user wrote // #[no_mangle] extern "C" fn main(..) {..} - // instead of #[start] None } } @@ -1176,6 +1207,20 @@ impl CodegenCx<'_, '_> { } } +// This is a duplication of the set_metadata function above. However, so far it's the only one +// shared between both contexts, so it doesn't seem worth it to make the Cx generic like we did it +// for the Builder. +impl SimpleCx<'_> { + #[allow(unused)] + /// A wrapper for [`llvm::LLVMSetMetadata`], but it takes `Metadata` as a parameter instead of `Value`. + pub(crate) fn set_metadata<'a>(&self, val: &'a Value, kind_id: MetadataType, md: &'a Metadata) { + unsafe { + let node = llvm::LLVMMetadataAsValue(&self.llcx, md); + llvm::LLVMSetMetadata(val, kind_id as c_uint, node); + } + } +} + impl HasDataLayout for CodegenCx<'_, '_> { #[inline] fn data_layout(&self) -> &TargetDataLayout { diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index b3ad2a0e4098f..9a2473d6cf23d 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -9,6 +9,7 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_index::IndexVec; use rustc_middle::mir; +use rustc_middle::mir::mono::MonoItemPartitions; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::RemapFileNameExt; use rustc_session::config::RemapPathScopeComponents; @@ -297,12 +298,13 @@ struct UsageSets<'tcx> { /// Prepare sets of definitions that are relevant to deciding whether something /// is an "unused function" for coverage purposes. fn prepare_usage_sets<'tcx>(tcx: TyCtxt<'tcx>) -> UsageSets<'tcx> { - let (all_mono_items, cgus) = tcx.collect_and_partition_mono_items(()); + let MonoItemPartitions { all_mono_items, codegen_units, .. } = + tcx.collect_and_partition_mono_items(()); // Obtain a MIR body for each function participating in codegen, via an // arbitrary instance. let mut def_ids_seen = FxHashSet::default(); - let def_and_mir_for_all_mono_fns = cgus + let def_and_mir_for_all_mono_fns = codegen_units .iter() .flat_map(|cgu| cgu.items().keys()) .filter_map(|item| match item { diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs index 5428d776f41e8..c53ea6d466610 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs @@ -11,7 +11,8 @@ use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods, }; use rustc_middle::mir::coverage::{ - CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping, MappingKind, Op, + BasicCoverageBlock, CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping, + MappingKind, Op, }; use rustc_middle::ty::{Instance, TyCtxt}; use rustc_span::Span; @@ -51,9 +52,9 @@ pub(crate) fn prepare_covfun_record<'tcx>( is_used: bool, ) -> Option> { let fn_cov_info = tcx.instance_mir(instance.def).function_coverage_info.as_deref()?; - let ids_info = tcx.coverage_ids_info(instance.def); + let ids_info = tcx.coverage_ids_info(instance.def)?; - let expressions = prepare_expressions(fn_cov_info, ids_info, is_used); + let expressions = prepare_expressions(ids_info); let mut covfun = CovfunRecord { mangled_function_name: tcx.symbol_name(instance).name, @@ -75,26 +76,14 @@ pub(crate) fn prepare_covfun_record<'tcx>( } /// Convert the function's coverage-counter expressions into a form suitable for FFI. -fn prepare_expressions( - fn_cov_info: &FunctionCoverageInfo, - ids_info: &CoverageIdsInfo, - is_used: bool, -) -> Vec { - // If any counters or expressions were removed by MIR opts, replace their - // terms with zero. - let counter_for_term = |term| { - if !is_used || ids_info.is_zero_term(term) { - ffi::Counter::ZERO - } else { - ffi::Counter::from_term(term) - } - }; +fn prepare_expressions(ids_info: &CoverageIdsInfo) -> Vec { + let counter_for_term = ffi::Counter::from_term; // We know that LLVM will optimize out any unused expressions before // producing the final coverage map, so there's no need to do the same // thing on the Rust side unless we're confident we can do much better. // (See `CounterExpressionsMinimizer` in `CoverageMappingWriter.cpp`.) - fn_cov_info + ids_info .expressions .iter() .map(move |&Expression { lhs, op, rhs }| ffi::CounterExpression { @@ -136,11 +125,16 @@ fn fill_region_tables<'tcx>( // For each counter/region pair in this function+file, convert it to a // form suitable for FFI. - let is_zero_term = |term| !covfun.is_used || ids_info.is_zero_term(term); for &Mapping { ref kind, span } in &fn_cov_info.mappings { - // If the mapping refers to counters/expressions that were removed by - // MIR opts, replace those occurrences with zero. - let kind = kind.map_terms(|term| if is_zero_term(term) { CovTerm::Zero } else { term }); + // If this function is unused, replace all counters with zero. + let counter_for_bcb = |bcb: BasicCoverageBlock| -> ffi::Counter { + let term = if covfun.is_used { + ids_info.term_for_bcb[bcb].expect("every BCB in a mapping was given a term") + } else { + CovTerm::Zero + }; + ffi::Counter::from_term(term) + }; // Convert the `Span` into coordinates that we can pass to LLVM, or // discard the span if conversion fails. In rare, cases _all_ of a @@ -154,23 +148,22 @@ fn fill_region_tables<'tcx>( continue; } - match kind { - MappingKind::Code(term) => { - code_regions - .push(ffi::CodeRegion { cov_span, counter: ffi::Counter::from_term(term) }); + match *kind { + MappingKind::Code { bcb } => { + code_regions.push(ffi::CodeRegion { cov_span, counter: counter_for_bcb(bcb) }); } - MappingKind::Branch { true_term, false_term } => { + MappingKind::Branch { true_bcb, false_bcb } => { branch_regions.push(ffi::BranchRegion { cov_span, - true_counter: ffi::Counter::from_term(true_term), - false_counter: ffi::Counter::from_term(false_term), + true_counter: counter_for_bcb(true_bcb), + false_counter: counter_for_bcb(false_bcb), }); } - MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => { + MappingKind::MCDCBranch { true_bcb, false_bcb, mcdc_params } => { mcdc_branch_regions.push(ffi::MCDCBranchRegion { cov_span, - true_counter: ffi::Counter::from_term(true_term), - false_counter: ffi::Counter::from_term(false_term), + true_counter: counter_for_bcb(true_bcb), + false_counter: counter_for_bcb(false_bcb), mcdc_branch_params: ffi::mcdc::BranchParameters::from(mcdc_params), }); } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 7311cd9d230c6..ea7f581a3cb51 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -8,7 +8,6 @@ use rustc_codegen_ssa::traits::{ use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::ty::Instance; -use rustc_middle::ty::layout::HasTyCtxt; use tracing::{debug, instrument}; use crate::builder::Builder; @@ -147,6 +146,10 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { debug!("function has a coverage statement but no coverage info"); return; }; + let Some(ids_info) = bx.tcx.coverage_ids_info(instance.def) else { + debug!("function has a coverage statement but no IDs info"); + return; + }; // Mark the instance as used in this CGU, for coverage purposes. // This includes functions that were not partitioned into this CGU, @@ -157,22 +160,12 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => unreachable!( "marker statement {kind:?} should have been removed by CleanupPostBorrowck" ), - CoverageKind::CounterIncrement { id } => { - // The number of counters passed to `llvm.instrprof.increment` might - // be smaller than the number originally inserted by the instrumentor, - // if some high-numbered counters were removed by MIR optimizations. - // If so, LLVM's profiler runtime will use fewer physical counters. - let num_counters = - bx.tcx().coverage_ids_info(instance.def).num_counters_after_mir_opts(); - assert!( - num_counters as usize <= function_coverage_info.num_counters, - "num_counters disagreement: query says {num_counters} but function info only has {}", - function_coverage_info.num_counters - ); - + CoverageKind::VirtualCounter { bcb } + if let Some(&id) = ids_info.phys_counter_for_node.get(&bcb) => + { let fn_name = bx.get_pgo_func_name_var(instance); let hash = bx.const_u64(function_coverage_info.function_source_hash); - let num_counters = bx.const_u32(num_counters); + let num_counters = bx.const_u32(ids_info.num_counters); let index = bx.const_u32(id.as_u32()); debug!( "codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, index={:?})", @@ -180,10 +173,8 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { ); bx.instrprof_increment(fn_name, hash, num_counters, index); } - CoverageKind::ExpressionUsed { id: _ } => { - // Expression-used statements are markers that are handled by - // `coverage_ids_info`, so there's nothing to codegen here. - } + // If a BCB doesn't have an associated physical counter, there's nothing to codegen. + CoverageKind::VirtualCounter { .. } => {} CoverageKind::CondBitmapUpdate { index, decision_depth } => { let cond_bitmap = coverage_cx .try_get_mcdc_condition_bitmap(&instance, decision_depth) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs index 07bd0f4d1c171..f52991b369797 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs @@ -4,12 +4,12 @@ use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext}; use rustc_codegen_ssa::traits::*; use rustc_data_structures::fx::FxHashMap; use rustc_index::Idx; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::{Body, SourceScope}; use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv}; use rustc_middle::ty::{self, Instance}; use rustc_session::config::DebugInfo; -use rustc_span::{BytePos, hygiene}; +use rustc_span::{BytePos, DUMMY_SP, hygiene}; use super::metadata::file_metadata; use super::utils::DIB; @@ -27,7 +27,7 @@ pub(crate) fn compute_mir_scopes<'ll, 'tcx>( ) { // Find all scopes with variables defined in them. let variables = if cx.sess().opts.debuginfo == DebugInfo::Full { - let mut vars = BitSet::new_empty(mir.source_scopes.len()); + let mut vars = DenseBitSet::new_empty(mir.source_scopes.len()); // FIXME(eddyb) take into account that arguments always have debuginfo, // irrespective of their name (assuming full debuginfo is enabled). // NOTE(eddyb) actually, on second thought, those are always in the @@ -40,7 +40,7 @@ pub(crate) fn compute_mir_scopes<'ll, 'tcx>( // Nothing to emit, of course. None }; - let mut instantiated = BitSet::new_empty(mir.source_scopes.len()); + let mut instantiated = DenseBitSet::new_empty(mir.source_scopes.len()); let mut discriminators = FxHashMap::default(); // Instantiate all scopes. for idx in 0..mir.source_scopes.len() { @@ -63,9 +63,9 @@ fn make_mir_scope<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>, mir: &Body<'tcx>, - variables: &Option>, + variables: &Option>, debug_context: &mut FunctionDebugContext<'tcx, &'ll DIScope, &'ll DILocation>, - instantiated: &mut BitSet, + instantiated: &mut DenseBitSet, discriminators: &mut FxHashMap, scope: SourceScope, ) { @@ -85,23 +85,15 @@ fn make_mir_scope<'ll, 'tcx>( discriminators, parent, ); - if let Some(parent_scope) = debug_context.scopes[parent] { - parent_scope - } else { - // If the parent scope could not be represented then no children - // can be either. - debug_context.scopes[scope] = None; - instantiated.insert(scope); - return; - } + debug_context.scopes[parent] } else { // The root is the function itself. let file = cx.sess().source_map().lookup_source_file(mir.span.lo()); - debug_context.scopes[scope] = Some(DebugScope { + debug_context.scopes[scope] = DebugScope { file_start_pos: file.start_pos, file_end_pos: file.end_position(), - ..debug_context.scopes[scope].unwrap() - }); + ..debug_context.scopes[scope] + }; instantiated.insert(scope); return; }; @@ -112,7 +104,7 @@ fn make_mir_scope<'ll, 'tcx>( { // Do not create a DIScope if there are no variables defined in this // MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat. - debug_context.scopes[scope] = Some(parent_scope); + debug_context.scopes[scope] = parent_scope; instantiated.insert(scope); return; } @@ -135,7 +127,7 @@ fn make_mir_scope<'ll, 'tcx>( }) } None => unsafe { - llvm::LLVMRustDIBuilderCreateLexicalBlock( + llvm::LLVMDIBuilderCreateLexicalBlock( DIB(cx), parent_scope.dbg_scope, file_metadata, @@ -145,14 +137,7 @@ fn make_mir_scope<'ll, 'tcx>( }, }; - let mut debug_scope = Some(DebugScope { - dbg_scope, - inlined_at: parent_scope.inlined_at, - file_start_pos: loc.file.start_pos, - file_end_pos: loc.file.end_position(), - }); - - if let Some((_, callsite_span)) = scope_data.inlined { + let inlined_at = scope_data.inlined.map(|(_, callsite_span)| { let callsite_span = hygiene::walk_chain_collapsed(callsite_span, mir.span); let callsite_scope = parent_scope.adjust_dbg_scope_for_span(cx, callsite_span); let loc = cx.dbg_loc(callsite_scope, parent_scope.inlined_at, callsite_span); @@ -175,29 +160,29 @@ fn make_mir_scope<'ll, 'tcx>( // Note further that we can't key this hashtable on the span itself, // because these spans could have distinct SyntaxContexts. We have // to key on exactly what we're giving to LLVM. - let inlined_at = match discriminators.entry(callsite_span.lo()) { + match discriminators.entry(callsite_span.lo()) { Entry::Occupied(mut o) => { *o.get_mut() += 1; + // NB: We have to emit *something* here or we'll fail LLVM IR verification + // in at least some circumstances (see issue #135322) so if the required + // discriminant cannot be encoded fall back to the dummy location. unsafe { llvm::LLVMRustDILocationCloneWithBaseDiscriminator(loc, *o.get()) } + .unwrap_or_else(|| { + cx.dbg_loc(callsite_scope, parent_scope.inlined_at, DUMMY_SP) + }) } Entry::Vacant(v) => { v.insert(0); - Some(loc) - } - }; - match inlined_at { - Some(inlined_at) => { - debug_scope.as_mut().unwrap().inlined_at = Some(inlined_at); - } - None => { - // LLVM has a maximum discriminator that it can encode (currently - // it uses 12 bits for 4096 possible values). If we exceed that - // there is little we can do but drop the debug info. - debug_scope = None; + loc } } - } + }); - debug_context.scopes[scope] = debug_scope; + debug_context.scopes[scope] = DebugScope { + dbg_scope, + inlined_at: inlined_at.or(parent_scope.inlined_at), + file_start_pos: loc.file.start_pos, + file_end_pos: loc.file.end_position(), + }; instantiated.insert(scope); } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs b/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs new file mode 100644 index 0000000000000..408429152223d --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs @@ -0,0 +1,37 @@ +//! Definitions of various DWARF-related constants. + +use libc::c_uint; + +/// Helper macro to let us redeclare gimli's constants as our own constants +/// with a different type, with less risk of copy-paste errors. +macro_rules! declare_constant { + ( + $name:ident : $type:ty + ) => { + #[allow(non_upper_case_globals)] + pub(crate) const $name: $type = ::gimli::constants::$name.0 as $type; + + // Assert that as-cast probably hasn't changed the value. + const _: () = assert!($name as i128 == ::gimli::constants::$name.0 as i128); + }; +} + +declare_constant!(DW_TAG_const_type: c_uint); + +// DWARF languages. +declare_constant!(DW_LANG_Rust: c_uint); + +// DWARF attribute type encodings. +declare_constant!(DW_ATE_boolean: c_uint); +declare_constant!(DW_ATE_float: c_uint); +declare_constant!(DW_ATE_signed: c_uint); +declare_constant!(DW_ATE_unsigned: c_uint); +declare_constant!(DW_ATE_UTF: c_uint); + +// DWARF expression operators. +declare_constant!(DW_OP_deref: u64); +declare_constant!(DW_OP_plus_uconst: u64); +/// Defined by LLVM in `llvm/include/llvm/BinaryFormat/Dwarf.h`. +/// Double-checked by a static assertion in `RustWrapper.cpp`. +#[allow(non_upper_case_globals)] +pub(crate) const DW_OP_LLVM_fragment: u64 = 0x1000; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs index 2c9f1cda13a8a..54c5d445f66bd 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -73,7 +73,7 @@ pub(crate) fn get_or_insert_gdb_debug_scripts_section_global<'ll>( .define_global(section_var_name, llvm_type) .unwrap_or_else(|| bug!("symbol `{}` is already defined", section_var_name)); llvm::set_section(section_var, c".debug_gdb_scripts"); - llvm::LLVMSetInitializer(section_var, cx.const_bytes(section_contents)); + llvm::set_initializer(section_var, cx.const_bytes(section_contents)); llvm::LLVMSetGlobalConstant(section_var, llvm::True); llvm::LLVMSetUnnamedAddress(section_var, llvm::UnnamedAddr::Global); llvm::set_linkage(section_var, llvm::Linkage::LinkOnceODRLinkage); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 40248a9009ae7..59c3fe635d070 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -13,7 +13,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_middle::bug; use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf, TyAndLayout}; use rustc_middle::ty::{ - self, AdtKind, CoroutineArgsExt, Instance, PolyExistentialTraitRef, Ty, TyCtxt, Visibility, + self, AdtKind, CoroutineArgsExt, ExistentialTraitRef, Instance, Ty, TyCtxt, Visibility, }; use rustc_session::config::{self, DebugInfo, Lto}; use rustc_span::{DUMMY_SP, FileName, FileNameDisplayPreference, SourceFile, Symbol, hygiene}; @@ -22,6 +22,7 @@ use rustc_target::spec::DebuginfoKind; use smallvec::smallvec; use tracing::{debug, instrument}; +pub(crate) use self::type_map::TypeMap; use self::type_map::{DINodeCreationResult, Stub, UniqueTypeId}; use super::CodegenUnitDebugContext; use super::namespace::mangled_name_of_instance; @@ -30,6 +31,7 @@ use super::utils::{ DIB, create_DIArray, debug_context, get_namespace_for_item, is_node_local_to_unit, }; use crate::common::{AsCCharPtr, CodegenCx}; +use crate::debuginfo::dwarf_const; use crate::debuginfo::metadata::type_map::build_type_with_children; use crate::debuginfo::utils::{WidePtrKind, wide_pointer_kind}; use crate::llvm::debuginfo::{ @@ -59,20 +61,6 @@ impl fmt::Debug for llvm::Metadata { } } -// From DWARF 5. -// See http://www.dwarfstd.org/ShowIssue.php?issue=140129.1. -const DW_LANG_RUST: c_uint = 0x1c; -#[allow(non_upper_case_globals)] -const DW_ATE_boolean: c_uint = 0x02; -#[allow(non_upper_case_globals)] -const DW_ATE_float: c_uint = 0x04; -#[allow(non_upper_case_globals)] -const DW_ATE_signed: c_uint = 0x05; -#[allow(non_upper_case_globals)] -const DW_ATE_unsigned: c_uint = 0x07; -#[allow(non_upper_case_globals)] -const DW_ATE_UTF: c_uint = 0x10; - pub(super) const UNKNOWN_LINE_NUMBER: c_uint = 0; pub(super) const UNKNOWN_COLUMN_NUMBER: c_uint = 0; @@ -87,8 +75,6 @@ type SmallVec = smallvec::SmallVec<[T; 16]>; mod enums; mod type_map; -pub(crate) use type_map::TypeMap; - /// Returns from the enclosing function if the type debuginfo node with the given /// unique ID can be found in the type map. macro_rules! return_if_di_node_created_in_meantime { @@ -333,19 +319,16 @@ fn build_subroutine_type_di_node<'ll, 'tcx>( // This is actually a function pointer, so wrap it in pointer DI. let name = compute_debuginfo_type_name(cx.tcx, fn_ty, false); let (size, align) = match fn_ty.kind() { - ty::FnDef(..) => (0, 1), - ty::FnPtr(..) => ( - cx.tcx.data_layout.pointer_size.bits(), - cx.tcx.data_layout.pointer_align.abi.bits() as u32, - ), + ty::FnDef(..) => (Size::ZERO, Align::ONE), + ty::FnPtr(..) => (cx.tcx.data_layout.pointer_size, cx.tcx.data_layout.pointer_align.abi), _ => unreachable!(), }; let di_node = unsafe { llvm::LLVMRustDIBuilderCreatePointerType( DIB(cx), fn_di_node, - size, - align, + size.bits(), + align.bits() as u32, 0, // Ignore DWARF address space. name.as_c_char_ptr(), name.len(), @@ -456,7 +439,7 @@ pub(crate) fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> // (or if there is no allocator argument). ty::Adt(def, args) if def.is_box() - && args.get(1).map_or(true, |arg| cx.layout_of(arg.expect_ty()).is_1zst()) => + && args.get(1).is_none_or(|arg| cx.layout_of(arg.expect_ty()).is_1zst()) => { build_pointer_or_reference_di_node(cx, t, t.expect_boxed_ty(), unique_type_id) } @@ -519,7 +502,7 @@ fn recursion_marker_type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) -> &'ll D name.as_c_char_ptr(), name.len(), cx.tcx.data_layout.pointer_size.bits(), - DW_ATE_unsigned, + dwarf_const::DW_ATE_unsigned, ) } }) @@ -778,6 +761,8 @@ fn build_basic_type_di_node<'ll, 'tcx>( // .natvis visualizers (and perhaps other existing native debuggers?) let cpp_like_debuginfo = cpp_like_debuginfo(cx.tcx); + use dwarf_const::{DW_ATE_UTF, DW_ATE_boolean, DW_ATE_float, DW_ATE_signed, DW_ATE_unsigned}; + let (name, encoding) = match t.kind() { ty::Never => ("!", DW_ATE_unsigned), ty::Tuple(elements) if elements.is_empty() => { @@ -931,8 +916,7 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>( .unwrap_or_default(); let kind = DebugEmissionKind::from_generic(tcx.sess.opts.debuginfo); - let dwarf_version = - tcx.sess.opts.unstable_opts.dwarf_version.unwrap_or(tcx.sess.target.default_dwarf_version); + let dwarf_version = tcx.sess.dwarf_version(); let is_dwarf_kind = matches!(tcx.sess.target.debuginfo_kind, DebuginfoKind::Dwarf | DebuginfoKind::DwarfDsym); // Don't emit `.debug_pubnames` and `.debug_pubtypes` on DWARFv4 or lower. @@ -944,7 +928,7 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>( unsafe { let compile_unit_file = llvm::LLVMRustDIBuilderCreateFile( - debug_context.builder, + debug_context.builder.as_ref(), name_in_debuginfo.as_c_char_ptr(), name_in_debuginfo.len(), work_dir.as_c_char_ptr(), @@ -957,8 +941,8 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>( ); let unit_metadata = llvm::LLVMRustDIBuilderCreateCompileUnit( - debug_context.builder, - DW_LANG_RUST, + debug_context.builder.as_ref(), + dwarf_const::DW_LANG_Rust, compile_unit_file, producer.as_c_char_ptr(), producer.len(), @@ -1412,7 +1396,7 @@ pub(crate) fn build_global_var_di_node<'ll>( fn build_vtable_type_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>, - poly_trait_ref: Option>, + poly_trait_ref: Option>, ) -> &'ll DIType { let tcx = cx.tcx; @@ -1500,10 +1484,30 @@ fn build_vtable_type_di_node<'ll, 'tcx>( .di_node } +/// Get the global variable for the vtable. +/// +/// When using global variables, we may have created an addrspacecast to get a pointer to the +/// default address space if global variables are created in a different address space. +/// For modifying the vtable, we need the real global variable. This function accepts either a +/// global variable (which is simply returned), or an addrspacecast constant expression. +/// If the given value is an addrspacecast, the cast is removed and the global variable behind +/// the cast is returned. +fn find_vtable_behind_cast<'ll>(vtable: &'ll Value) -> &'ll Value { + // The vtable is a global variable, which may be behind an addrspacecast. + unsafe { + if let Some(c) = llvm::LLVMIsAConstantExpr(vtable) { + if llvm::LLVMGetConstOpcode(c) == llvm::Opcode::AddrSpaceCast { + return llvm::LLVMGetOperand(c, 0).unwrap(); + } + } + } + vtable +} + pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>, - trait_ref: Option>, + trait_ref: Option>, vtable: &'ll Value, ) { // FIXME(flip1995): The virtual function elimination optimization only works with full LTO in @@ -1520,9 +1524,11 @@ pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>( let Some(trait_ref) = trait_ref else { return }; + // Unwrap potential addrspacecast + let vtable = find_vtable_behind_cast(vtable); let trait_ref_self = trait_ref.with_self_ty(cx.tcx, ty); let trait_ref_self = cx.tcx.erase_regions(trait_ref_self); - let trait_def_id = trait_ref_self.def_id(); + let trait_def_id = trait_ref_self.def_id; let trait_vis = cx.tcx.visibility(trait_def_id); let cgus = cx.sess().codegen_units().as_usize(); @@ -1581,7 +1587,7 @@ pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>( pub(crate) fn create_vtable_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>, - poly_trait_ref: Option>, + poly_trait_ref: Option>, vtable: &'ll Value, ) { if cx.dbg_cx.is_none() { @@ -1593,6 +1599,9 @@ pub(crate) fn create_vtable_di_node<'ll, 'tcx>( return; } + // Unwrap potential addrspacecast + let vtable = find_vtable_behind_cast(vtable); + // When full debuginfo is enabled, we want to try and prevent vtables from being // merged. Otherwise debuggers will have a hard time mapping from dyn pointer // to concrete type. @@ -1629,7 +1638,14 @@ pub(crate) fn extend_scope_to_file<'ll>( file: &SourceFile, ) -> &'ll DILexicalBlock { let file_metadata = file_metadata(cx, file); - unsafe { llvm::LLVMRustDIBuilderCreateLexicalBlockFile(DIB(cx), scope_metadata, file_metadata) } + unsafe { + llvm::LLVMDIBuilderCreateLexicalBlockFile( + DIB(cx), + scope_metadata, + file_metadata, + /* Discriminator (default) */ 0u32, + ) + } } fn tuple_field_name(field_index: usize) -> Cow<'static, str> { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs index 23e11748e5272..a72e205c9b249 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs @@ -12,6 +12,7 @@ use rustc_middle::ty::{self, AdtDef, CoroutineArgs, CoroutineArgsExt, Ty}; use smallvec::smallvec; use crate::common::{AsCCharPtr, CodegenCx}; +use crate::debuginfo::dwarf_const::DW_TAG_const_type; use crate::debuginfo::metadata::enums::DiscrResult; use crate::debuginfo::metadata::type_map::{self, Stub, UniqueTypeId}; use crate::debuginfo::metadata::{ @@ -566,22 +567,39 @@ fn build_variant_struct_wrapper_type_di_node<'ll, 'tcx>( None, )); - let build_assoc_const = - |name: &str, type_di_node: &'ll DIType, value: u64, align: Align| unsafe { - llvm::LLVMRustDIBuilderCreateStaticMemberType( - DIB(cx), - wrapper_struct_type_di_node, - name.as_c_char_ptr(), - name.len(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER, - type_di_node, - DIFlags::FlagZero, - Some(cx.const_u64(value)), - align.bits() as u32, - ) + let build_assoc_const = |name: &str, + type_di_node_: &'ll DIType, + value: u64, + align: Align| unsafe { + // FIXME: Currently we force all DISCR_* values to be u64's as LLDB seems to have + // problems inspecting other value types. Since DISCR_* is typically only going to be + // directly inspected via the debugger visualizer - which compares it to the `tag` value + // (whose type is not modified at all) it shouldn't cause any real problems. + let (t_di, align) = if name == ASSOC_CONST_DISCR_NAME { + (type_di_node_, align.bits() as u32) + } else { + let ty_u64 = Ty::new_uint(cx.tcx, ty::UintTy::U64); + (type_di_node(cx, ty_u64), Align::EIGHT.bits() as u32) }; + // must wrap type in a `const` modifier for LLDB to be able to inspect the value of the member + let field_type = + llvm::LLVMRustDIBuilderCreateQualifiedType(DIB(cx), DW_TAG_const_type, t_di); + + llvm::LLVMRustDIBuilderCreateStaticMemberType( + DIB(cx), + wrapper_struct_type_di_node, + name.as_c_char_ptr(), + name.len(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + field_type, + DIFlags::FlagZero, + Some(cx.const_u64(value)), + align, + ) + }; + // We also always have an associated constant for the discriminant value // of the variant. fields.push(build_assoc_const( diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs index 11824398f243e..187d97c54c873 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs @@ -437,6 +437,12 @@ fn build_enum_variant_member_di_node<'ll, 'tcx>( .source_info .unwrap_or_else(|| (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER)); + let discr = discr_value.opt_single_val().map(|value| { + let tag_base_type = tag_base_type(cx.tcx, enum_type_and_layout); + let size = cx.size_of(tag_base_type); + cx.const_uint_big(cx.type_ix(size.bits()), value) + }); + unsafe { llvm::LLVMRustDIBuilderCreateVariantMemberType( DIB(cx), @@ -448,7 +454,7 @@ fn build_enum_variant_member_di_node<'ll, 'tcx>( enum_type_and_layout.size.bits(), enum_type_and_layout.align.abi.bits() as u32, Size::ZERO.bits(), - discr_value.opt_single_val().map(|value| cx.const_u128(value)), + discr, DIFlags::FlagZero, variant_member_info.variant_struct_type_di_node, ) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs index a37e719d43f2b..af1d503ad6a43 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs @@ -6,7 +6,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_macros::HashStable; use rustc_middle::bug; -use rustc_middle::ty::{self, PolyExistentialTraitRef, Ty, TyCtxt}; +use rustc_middle::ty::{self, ExistentialTraitRef, Ty, TyCtxt}; use super::{DefinitionLocation, SmallVec, UNKNOWN_LINE_NUMBER, unknown_file_metadata}; use crate::common::{AsCCharPtr, CodegenCx}; @@ -44,7 +44,7 @@ pub(super) enum UniqueTypeId<'tcx> { /// The ID for the additional wrapper struct type describing an enum variant in CPP-like mode. VariantStructTypeCppLikeWrapper(Ty<'tcx>, VariantIdx, private::HiddenZst), /// The ID of the artificial type we create for VTables. - VTableTy(Ty<'tcx>, Option>, private::HiddenZst), + VTableTy(Ty<'tcx>, Option>, private::HiddenZst), } impl<'tcx> UniqueTypeId<'tcx> { @@ -88,7 +88,7 @@ impl<'tcx> UniqueTypeId<'tcx> { pub(crate) fn for_vtable_ty( tcx: TyCtxt<'tcx>, self_type: Ty<'tcx>, - implemented_trait: Option>, + implemented_trait: Option>, ) -> Self { assert_eq!( self_type, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index fae698bea2a6b..17f2d5f4e731f 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -2,6 +2,7 @@ use std::cell::{OnceCell, RefCell}; use std::ops::Range; +use std::sync::Arc; use std::{iter, ptr}; use libc::c_uint; @@ -10,7 +11,6 @@ use rustc_codegen_ssa::debuginfo::type_names; use rustc_codegen_ssa::mir::debuginfo::VariableKind::*; use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext, VariableKind}; use rustc_codegen_ssa::traits::*; -use rustc_data_structures::sync::Lrc; use rustc_data_structures::unord::UnordMap; use rustc_hir::def_id::{DefId, DefIdMap}; use rustc_index::IndexVec; @@ -22,6 +22,7 @@ use rustc_session::config::{self, DebugInfo}; use rustc_span::{ BytePos, Pos, SourceFile, SourceFileAndLine, SourceFileHash, Span, StableSourceFileId, Symbol, }; +use rustc_target::spec::DebuginfoKind; use smallvec::SmallVec; use tracing::debug; @@ -33,12 +34,13 @@ use crate::builder::Builder; use crate::common::{AsCCharPtr, CodegenCx}; use crate::llvm; use crate::llvm::debuginfo::{ - DIArray, DIBuilder, DIFile, DIFlags, DILexicalBlock, DILocation, DISPFlags, DIScope, DIType, + DIArray, DIBuilderBox, DIFile, DIFlags, DILexicalBlock, DILocation, DISPFlags, DIScope, DIType, DIVariable, }; use crate::value::Value; mod create_scope_map; +mod dwarf_const; mod gdb; pub(crate) mod metadata; mod namespace; @@ -47,6 +49,10 @@ mod utils; use self::create_scope_map::compute_mir_scopes; pub(crate) use self::metadata::build_global_var_di_node; +// FIXME(Zalathar): These `DW_TAG_*` constants are fake values that were +// removed from LLVM in 2015, and are only used by our own `RustWrapper.cpp` +// to decide which C++ API to call. Instead, we should just have two separate +// FFI functions and choose the correct one on the Rust side. #[allow(non_upper_case_globals)] const DW_TAG_auto_variable: c_uint = 0x100; #[allow(non_upper_case_globals)] @@ -55,7 +61,7 @@ const DW_TAG_arg_variable: c_uint = 0x101; /// A context object for maintaining all state needed by the debuginfo module. pub(crate) struct CodegenUnitDebugContext<'ll, 'tcx> { llmod: &'ll llvm::Module, - builder: &'ll mut DIBuilder<'ll>, + builder: DIBuilderBox<'ll>, created_files: RefCell, &'ll DIFile>>, type_map: metadata::TypeMap<'ll, 'tcx>, @@ -63,18 +69,10 @@ pub(crate) struct CodegenUnitDebugContext<'ll, 'tcx> { recursion_marker_type: OnceCell<&'ll DIType>, } -impl Drop for CodegenUnitDebugContext<'_, '_> { - fn drop(&mut self) { - unsafe { - llvm::LLVMRustDIBuilderDispose(&mut *(self.builder as *mut _)); - } - } -} - impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { pub(crate) fn new(llmod: &'ll llvm::Module) -> Self { debug!("CodegenUnitDebugContext::new"); - let builder = unsafe { llvm::LLVMRustDIBuilderCreate(llmod) }; + let builder = DIBuilderBox::new(llmod); // DIBuilder inherits context from the module, so we'd better use the same one CodegenUnitDebugContext { llmod, @@ -87,30 +85,36 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { } pub(crate) fn finalize(&self, sess: &Session) { - unsafe { llvm::LLVMRustDIBuilderFinalize(self.builder) }; - if !sess.target.is_like_msvc { - // Debuginfo generation in LLVM by default uses a higher - // version of dwarf than macOS currently understands. We can - // instruct LLVM to emit an older version of dwarf, however, - // for macOS to understand. For more info see #11352 - // This can be overridden using --llvm-opts -dwarf-version,N. - // Android has the same issue (#22398) - let dwarf_version = - sess.opts.unstable_opts.dwarf_version.unwrap_or(sess.target.default_dwarf_version); - llvm::add_module_flag_u32( - self.llmod, - llvm::ModuleFlagMergeBehavior::Warning, - "Dwarf Version", - dwarf_version, - ); - } else { - // Indicate that we want CodeView debug information on MSVC - llvm::add_module_flag_u32( - self.llmod, - llvm::ModuleFlagMergeBehavior::Warning, - "CodeView", - 1, - ); + unsafe { llvm::LLVMDIBuilderFinalize(self.builder.as_ref()) }; + + match sess.target.debuginfo_kind { + DebuginfoKind::Dwarf | DebuginfoKind::DwarfDsym => { + // Debuginfo generation in LLVM by default uses a higher + // version of dwarf than macOS currently understands. We can + // instruct LLVM to emit an older version of dwarf, however, + // for macOS to understand. For more info see #11352 + // This can be overridden using --llvm-opts -dwarf-version,N. + // Android has the same issue (#22398) + llvm::add_module_flag_u32( + self.llmod, + // In the case where multiple CGUs with different dwarf version + // values are being merged together, such as with cross-crate + // LTO, then we want to use the highest version of dwarf + // we can. This matches Clang's behavior as well. + llvm::ModuleFlagMergeBehavior::Max, + "Dwarf Version", + sess.dwarf_version(), + ); + } + DebuginfoKind::Pdb => { + // Indicate that we want CodeView debug information + llvm::add_module_flag_u32( + self.llmod, + llvm::ModuleFlagMergeBehavior::Warning, + "CodeView", + 1, + ); + } } // Prevent bitcode readers from deleting the debug info. @@ -152,29 +156,26 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> { indirect_offsets: &[Size], fragment: Option>, ) { + use dwarf_const::{DW_OP_LLVM_fragment, DW_OP_deref, DW_OP_plus_uconst}; + // Convert the direct and indirect offsets and fragment byte range to address ops. - // FIXME(eddyb) use `const`s instead of getting the values via FFI, - // the values should match the ones in the DWARF standard anyway. - let op_deref = || unsafe { llvm::LLVMRustDIBuilderCreateOpDeref() }; - let op_plus_uconst = || unsafe { llvm::LLVMRustDIBuilderCreateOpPlusUconst() }; - let op_llvm_fragment = || unsafe { llvm::LLVMRustDIBuilderCreateOpLLVMFragment() }; let mut addr_ops = SmallVec::<[u64; 8]>::new(); if direct_offset.bytes() > 0 { - addr_ops.push(op_plus_uconst()); + addr_ops.push(DW_OP_plus_uconst); addr_ops.push(direct_offset.bytes() as u64); } for &offset in indirect_offsets { - addr_ops.push(op_deref()); + addr_ops.push(DW_OP_deref); if offset.bytes() > 0 { - addr_ops.push(op_plus_uconst()); + addr_ops.push(DW_OP_plus_uconst); addr_ops.push(offset.bytes() as u64); } } if let Some(fragment) = fragment { // `DW_OP_LLVM_fragment` takes as arguments the fragment's // offset and size, both of them in bits. - addr_ops.push(op_llvm_fragment()); + addr_ops.push(DW_OP_LLVM_fragment); addr_ops.push(fragment.start.bits() as u64); addr_ops.push((fragment.end - fragment.start).bits() as u64); } @@ -243,7 +244,7 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> { // `lookup_char_pos` return the right information instead. struct DebugLoc { /// Information about the original source file. - file: Lrc, + file: Arc, /// The (1-based) line number. line: u32, /// The (1-based) column number. @@ -293,12 +294,12 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } // Initialize fn debug context (including scopes). - let empty_scope = Some(DebugScope { + let empty_scope = DebugScope { dbg_scope: self.dbg_scope_fn(instance, fn_abi, Some(llfn)), inlined_at: None, file_start_pos: BytePos(0), file_end_pos: BytePos(0), - }); + }; let mut fn_debug_context = FunctionDebugContext { scopes: IndexVec::from_elem(empty_scope, &mir.source_scopes), inlined_function_scopes: Default::default(), @@ -547,14 +548,17 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } } - let scope = namespace::item_namespace(cx, DefId { - krate: instance.def_id().krate, - index: cx - .tcx - .def_key(instance.def_id()) - .parent - .expect("get_containing_scope: missing parent?"), - }); + let scope = namespace::item_namespace( + cx, + DefId { + krate: instance.def_id().krate, + index: cx + .tcx + .def_key(instance.def_id()) + .parent + .expect("get_containing_scope: missing parent?"), + }, + ); (scope, false) } } @@ -577,13 +581,13 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { (line, col) }; - unsafe { llvm::LLVMRustDIBuilderCreateDebugLocation(line, col, scope, inlined_at) } + unsafe { llvm::LLVMDIBuilderCreateDebugLocation(self.llcx, line, col, scope, inlined_at) } } fn create_vtable_debuginfo( &self, ty: Ty<'tcx>, - trait_ref: Option>, + trait_ref: Option>, vtable: Self::Value, ) { metadata::create_vtable_di_node(self, ty, trait_ref, vtable) @@ -636,7 +640,7 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { true, DIFlags::FlagZero, argument_index, - align.bytes() as u32, + align.bits() as u32, ) } } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs b/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs index 33d9bc238903e..b4d639368b005 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs @@ -5,7 +5,7 @@ use rustc_hir::def_id::DefId; use rustc_middle::ty::{self, Instance}; use super::utils::{DIB, debug_context}; -use crate::common::{AsCCharPtr, CodegenCx}; +use crate::common::CodegenCx; use crate::llvm; use crate::llvm::debuginfo::DIScope; @@ -33,12 +33,12 @@ pub(crate) fn item_namespace<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId) -> &'l }; let scope = unsafe { - llvm::LLVMRustDIBuilderCreateNameSpace( + llvm::LLVMDIBuilderCreateNameSpace( DIB(cx), parent_scope, - namespace_name_string.as_c_char_ptr(), + namespace_name_string.as_ptr(), namespace_name_string.len(), - false, // ExportSymbols (only relevant for C++ anonymous namespaces) + llvm::False, // ExportSymbols (only relevant for C++ anonymous namespaces) ) }; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs index 6e84129347787..cc1d504b43017 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs @@ -41,7 +41,7 @@ pub(crate) fn debug_context<'a, 'll, 'tcx>( #[inline] #[allow(non_snake_case)] pub(crate) fn DIB<'a, 'll>(cx: &'a CodegenCx<'ll, '_>) -> &'a DIBuilder<'ll> { - cx.dbg_cx.as_ref().unwrap().builder + cx.dbg_cx.as_ref().unwrap().builder.as_ref() } pub(crate) fn get_namespace_for_item<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId) -> &'ll DIScope { diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index 3ec386f6b076c..cebceef1b93f8 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -21,26 +21,26 @@ use tracing::debug; use crate::abi::{FnAbi, FnAbiLlvmExt}; use crate::common::AsCCharPtr; -use crate::context::CodegenCx; +use crate::context::{CodegenCx, SimpleCx}; use crate::llvm::AttributePlace::Function; use crate::llvm::Visibility; use crate::type_::Type; use crate::value::Value; use crate::{attributes, llvm}; -/// Declare a function. +/// Declare a function with a SimpleCx. /// /// If there’s a value with the same name already declared, the function will /// update the declaration and return existing Value instead. -pub(crate) fn declare_raw_fn<'ll>( - cx: &CodegenCx<'ll, '_>, +pub(crate) fn declare_simple_fn<'ll>( + cx: &SimpleCx<'ll>, name: &str, callconv: llvm::CallConv, unnamed: llvm::UnnamedAddr, visibility: llvm::Visibility, ty: &'ll Type, ) -> &'ll Value { - debug!("declare_raw_fn(name={:?}, ty={:?})", name, ty); + debug!("declare_simple_fn(name={:?}, ty={:?})", name, ty); let llfn = unsafe { llvm::LLVMRustGetOrInsertFunction(cx.llmod, name.as_c_char_ptr(), name.len(), ty) }; @@ -49,6 +49,24 @@ pub(crate) fn declare_raw_fn<'ll>( llvm::SetUnnamedAddress(llfn, unnamed); llvm::set_visibility(llfn, visibility); + llfn +} + +/// Declare a function. +/// +/// If there’s a value with the same name already declared, the function will +/// update the declaration and return existing Value instead. +pub(crate) fn declare_raw_fn<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + name: &str, + callconv: llvm::CallConv, + unnamed: llvm::UnnamedAddr, + visibility: llvm::Visibility, + ty: &'ll Type, +) -> &'ll Value { + debug!("declare_raw_fn(name={:?}, ty={:?})", name, ty); + let llfn = declare_simple_fn(cx, name, callconv, unnamed, visibility, ty); + let mut attrs = SmallVec::<[_; 4]>::new(); if cx.tcx.sess.opts.cg.no_redzone.unwrap_or(cx.tcx.sess.target.disable_redzone) { @@ -125,7 +143,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { let llfn = declare_raw_fn( self, name, - fn_abi.llvm_cconv(), + fn_abi.llvm_cconv(self), llvm::UnnamedAddr::Global, llvm::Visibility::Default, fn_abi.llvm_type(self), @@ -217,7 +235,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { /// name. pub(crate) fn get_defined_value(&self, name: &str) -> Option<&'ll Value> { self.get_declared_value(name).and_then(|val| { - let declaration = unsafe { llvm::LLVMIsDeclaration(val) != 0 }; + let declaration = llvm::is_declaration(val); if !declaration { Some(val) } else { None } }) } diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index f340b06e876cd..97f4925616595 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -37,6 +37,7 @@ pub(crate) struct UnstableCTargetFeature<'a> { #[note(codegen_llvm_forbidden_ctarget_feature_issue)] pub(crate) struct ForbiddenCTargetFeature<'a> { pub feature: &'a str, + pub enabled: &'a str, pub reason: &'a str, } @@ -130,8 +131,6 @@ pub enum LlvmError<'a> { LoadBitcode { name: CString }, #[diag(codegen_llvm_write_thinlto_key)] WriteThinLtoKey { err: std::io::Error }, - #[diag(codegen_llvm_multiple_source_dicompileunit)] - MultipleSourceDiCompileUnit, #[diag(codegen_llvm_prepare_thin_lto_module)] PrepareThinLtoModule, #[diag(codegen_llvm_parse_bitcode)] @@ -154,9 +153,6 @@ impl Diagnostic<'_, G> for WithLlvmError<'_> { PrepareThinLtoContext => fluent::codegen_llvm_prepare_thin_lto_context_with_llvm_err, LoadBitcode { .. } => fluent::codegen_llvm_load_bitcode_with_llvm_err, WriteThinLtoKey { .. } => fluent::codegen_llvm_write_thinlto_key_with_llvm_err, - MultipleSourceDiCompileUnit => { - fluent::codegen_llvm_multiple_source_dicompileunit_with_llvm_err - } PrepareThinLtoModule => fluent::codegen_llvm_prepare_thin_lto_module_with_llvm_err, ParseBitcode => fluent::codegen_llvm_parse_bitcode_with_llvm_err, PrepareAutoDiff { .. } => fluent::codegen_llvm_prepare_autodiff_with_llvm_err, @@ -213,12 +209,6 @@ pub(crate) struct MismatchedDataLayout<'a> { pub llvm_layout: &'a str, } -#[derive(Diagnostic)] -#[diag(codegen_llvm_invalid_target_feature_prefix)] -pub(crate) struct InvalidTargetFeaturePrefix<'a> { - pub feature: &'a str, -} - #[derive(Diagnostic)] #[diag(codegen_llvm_fixed_x18_invalid_arch)] pub(crate) struct FixedX18InvalidArch<'a> { diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index cabcfc9b42b4e..8b97688590456 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -333,12 +333,15 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { sym::prefetch_write_instruction => (1, 0), _ => bug!(), }; - self.call_intrinsic("llvm.prefetch", &[ - args[0].immediate(), - self.const_i32(rw), - args[1].immediate(), - self.const_i32(cache_type), - ]) + self.call_intrinsic( + "llvm.prefetch", + &[ + args[0].immediate(), + self.const_i32(rw), + args[1].immediate(), + self.const_i32(cache_type), + ], + ) } sym::carrying_mul_add => { let (size, signed) = fn_args.type_at(0).int_size_and_signed(self.tcx); @@ -396,10 +399,10 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { match name { sym::ctlz | sym::cttz => { let y = self.const_bool(false); - let ret = self.call_intrinsic(&format!("llvm.{name}.i{width}"), &[ - args[0].immediate(), - y, - ]); + let ret = self.call_intrinsic( + &format!("llvm.{name}.i{width}"), + &[args[0].immediate(), y], + ); self.intcast(ret, llret_ty, false) } @@ -416,24 +419,26 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { self.intcast(ret, llret_ty, false) } sym::ctpop => { - let ret = self.call_intrinsic(&format!("llvm.ctpop.i{width}"), &[ - args[0].immediate() - ]); + let ret = self.call_intrinsic( + &format!("llvm.ctpop.i{width}"), + &[args[0].immediate()], + ); self.intcast(ret, llret_ty, false) } sym::bswap => { if width == 8 { args[0].immediate() // byte swap a u8/i8 is just a no-op } else { - self.call_intrinsic(&format!("llvm.bswap.i{width}"), &[ - args[0].immediate() - ]) + self.call_intrinsic( + &format!("llvm.bswap.i{width}"), + &[args[0].immediate()], + ) } } - sym::bitreverse => self - .call_intrinsic(&format!("llvm.bitreverse.i{width}"), &[ - args[0].immediate() - ]), + sym::bitreverse => self.call_intrinsic( + &format!("llvm.bitreverse.i{width}"), + &[args[0].immediate()], + ), sym::rotate_left | sym::rotate_right => { let is_left = name == sym::rotate_left; let val = args[0].immediate(); @@ -500,11 +505,10 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { sym::compare_bytes => { // Here we assume that the `memcmp` provided by the target is a NOP for size 0. - let cmp = self.call_intrinsic("memcmp", &[ - args[0].immediate(), - args[1].immediate(), - args[2].immediate(), - ]); + let cmp = self.call_intrinsic( + "memcmp", + &[args[0].immediate(), args[1].immediate(), args[2].immediate()], + ); // Some targets have `memcmp` returning `i16`, but the intrinsic is always `i32`. self.sext(cmp, self.type_ix(32)) } @@ -820,7 +824,7 @@ fn codegen_msvc_try<'ll>( if bx.cx.tcx.sess.target.supports_comdat() { llvm::SetUniqueComdat(bx.llmod, tydesc); } - unsafe { llvm::LLVMSetInitializer(tydesc, type_info) }; + llvm::set_initializer(tydesc, type_info); // The flag value of 8 indicates that we are catching the exception by // reference instead of by value. We can't use catch by value because @@ -1081,11 +1085,11 @@ fn codegen_emcc_try<'ll>( // Helper function to give a Block to a closure to codegen a shim function. // This is currently primarily used for the `try` intrinsic functions above. -fn gen_fn<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, +fn gen_fn<'a, 'll, 'tcx>( + cx: &'a CodegenCx<'ll, 'tcx>, name: &str, rust_fn_sig: ty::PolyFnSig<'tcx>, - codegen: &mut dyn FnMut(Builder<'_, 'll, 'tcx>), + codegen: &mut dyn FnMut(Builder<'a, 'll, 'tcx>), ) -> (&'ll Type, &'ll Value) { let fn_abi = cx.fn_abi_of_fn_ptr(rust_fn_sig, ty::List::empty()); let llty = fn_abi.llvm_type(cx); @@ -1104,9 +1108,9 @@ fn gen_fn<'ll, 'tcx>( // catch exceptions. // // This function is only generated once and is then cached. -fn get_rust_try_fn<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, - codegen: &mut dyn FnMut(Builder<'_, 'll, 'tcx>), +fn get_rust_try_fn<'a, 'll, 'tcx>( + cx: &'a CodegenCx<'ll, 'tcx>, + codegen: &mut dyn FnMut(Builder<'a, 'll, 'tcx>), ) -> (&'ll Type, &'ll Value) { if let Some(llfn) = cx.rust_try_fn.get() { return llfn; @@ -1182,6 +1186,60 @@ fn generic_simd_intrinsic<'ll, 'tcx>( }}; } + /// Returns the bitwidth of the `$ty` argument if it is an `Int` type. + macro_rules! require_int_ty { + ($ty: expr, $diag: expr) => { + match $ty { + ty::Int(i) => i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()), + _ => { + return_error!($diag); + } + } + }; + } + + /// Returns the bitwidth of the `$ty` argument if it is an `Int` or `Uint` type. + macro_rules! require_int_or_uint_ty { + ($ty: expr, $diag: expr) => { + match $ty { + ty::Int(i) => i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()), + ty::Uint(i) => { + i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()) + } + _ => { + return_error!($diag); + } + } + }; + } + + /// Converts a vector mask, where each element has a bit width equal to the data elements it is used with, + /// down to an i1 based mask that can be used by llvm intrinsics. + /// + /// The rust simd semantics are that each element should either consist of all ones or all zeroes, + /// but this information is not available to llvm. Truncating the vector effectively uses the lowest bit, + /// but codegen for several targets is better if we consider the highest bit by shifting. + /// + /// For x86 SSE/AVX targets this is beneficial since most instructions with mask parameters only consider the highest bit. + /// So even though on llvm level we have an additional shift, in the final assembly there is no shift or truncate and + /// instead the mask can be used as is. + /// + /// For aarch64 and other targets there is a benefit because a mask from the sign bit can be more + /// efficiently converted to an all ones / all zeroes mask by comparing whether each element is negative. + fn vector_mask_to_bitmask<'a, 'll, 'tcx>( + bx: &mut Builder<'a, 'll, 'tcx>, + i_xn: &'ll Value, + in_elem_bitwidth: u64, + in_len: u64, + ) -> &'ll Value { + // Shift the MSB to the right by "in_elem_bitwidth - 1" into the first bit position. + let shift_idx = bx.cx.const_int(bx.type_ix(in_elem_bitwidth), (in_elem_bitwidth - 1) as _); + let shift_indices = vec![shift_idx; in_len as _]; + let i_xn_msb = bx.lshr(i_xn, bx.const_vector(shift_indices.as_slice())); + // Truncate vector to an + bx.trunc(i_xn_msb, bx.type_vector(bx.type_i1(), in_len)) + } + let tcx = bx.tcx(); let sig = tcx.normalize_erasing_late_bound_regions(bx.typing_env(), callee_ty.fn_sig(tcx)); let arg_tys = sig.inputs(); @@ -1251,14 +1309,17 @@ fn generic_simd_intrinsic<'ll, 'tcx>( if let Some(cmp_op) = comparison { let (out_len, out_ty) = require_simd!(ret_ty, SimdReturn); - require!(in_len == out_len, InvalidMonomorphization::ReturnLengthInputType { - span, - name, - in_len, - in_ty, - ret_ty, - out_len - }); + require!( + in_len == out_len, + InvalidMonomorphization::ReturnLengthInputType { + span, + name, + in_len, + in_ty, + ret_ty, + out_len + } + ); require!( bx.type_kind(bx.element_type(llret_ty)) == TypeKind::Integer, InvalidMonomorphization::ReturnIntegerType { span, name, ret_ty, out_ty } @@ -1275,25 +1336,18 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } if name == sym::simd_shuffle_generic { - let idx = fn_args[2].expect_const().try_to_valtree().unwrap().0.unwrap_branch(); + let idx = fn_args[2].expect_const().to_value().valtree.unwrap_branch(); let n = idx.len() as u64; let (out_len, out_ty) = require_simd!(ret_ty, SimdReturn); - require!(out_len == n, InvalidMonomorphization::ReturnLength { - span, - name, - in_len: n, - ret_ty, - out_len - }); - require!(in_elem == out_ty, InvalidMonomorphization::ReturnElement { - span, - name, - in_elem, - in_ty, - ret_ty, - out_ty - }); + require!( + out_len == n, + InvalidMonomorphization::ReturnLength { span, name, in_len: n, ret_ty, out_len } + ); + require!( + in_elem == out_ty, + InvalidMonomorphization::ReturnElement { span, name, in_elem, in_ty, ret_ty, out_ty } + ); let total_len = in_len * 2; @@ -1338,21 +1392,14 @@ fn generic_simd_intrinsic<'ll, 'tcx>( }; let (out_len, out_ty) = require_simd!(ret_ty, SimdReturn); - require!(out_len == n, InvalidMonomorphization::ReturnLength { - span, - name, - in_len: n, - ret_ty, - out_len - }); - require!(in_elem == out_ty, InvalidMonomorphization::ReturnElement { - span, - name, - in_elem, - in_ty, - ret_ty, - out_ty - }); + require!( + out_len == n, + InvalidMonomorphization::ReturnLength { span, name, in_len: n, ret_ty, out_len } + ); + require!( + in_elem == out_ty, + InvalidMonomorphization::ReturnElement { span, name, in_elem, in_ty, ret_ty, out_ty } + ); let total_len = u128::from(in_len) * 2; @@ -1377,13 +1424,16 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } if name == sym::simd_insert { - require!(in_elem == arg_tys[2], InvalidMonomorphization::InsertedType { - span, - name, - in_elem, - in_ty, - out_ty: arg_tys[2] - }); + require!( + in_elem == arg_tys[2], + InvalidMonomorphization::InsertedType { + span, + name, + in_elem, + in_ty, + out_ty: arg_tys[2] + } + ); let idx = bx .const_to_opt_u128(args[1].immediate(), false) .expect("typeck should have ensure that this is a const"); @@ -1402,13 +1452,10 @@ fn generic_simd_intrinsic<'ll, 'tcx>( )); } if name == sym::simd_extract { - require!(ret_ty == in_elem, InvalidMonomorphization::ReturnType { - span, - name, - in_elem, - in_ty, - ret_ty - }); + require!( + ret_ty == in_elem, + InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty } + ); let idx = bx .const_to_opt_u128(args[1].immediate(), false) .expect("typeck should have ensure that this is a const"); @@ -1427,20 +1474,15 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let m_elem_ty = in_elem; let m_len = in_len; let (v_len, _) = require_simd!(arg_tys[1], SimdArgument); - require!(m_len == v_len, InvalidMonomorphization::MismatchedLengths { - span, - name, - m_len, - v_len - }); - match m_elem_ty.kind() { - ty::Int(_) => {} - _ => return_error!(InvalidMonomorphization::MaskType { span, name, ty: m_elem_ty }), - } - // truncate the mask to a vector of i1s - let i1 = bx.type_i1(); - let i1xn = bx.type_vector(i1, m_len as u64); - let m_i1s = bx.trunc(args[0].immediate(), i1xn); + require!( + m_len == v_len, + InvalidMonomorphization::MismatchedLengths { span, name, m_len, v_len } + ); + let in_elem_bitwidth = require_int_ty!( + m_elem_ty.kind(), + InvalidMonomorphization::MaskType { span, name, ty: m_elem_ty } + ); + let m_i1s = vector_mask_to_bitmask(bx, args[0].immediate(), in_elem_bitwidth, m_len); return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate())); } @@ -1457,33 +1499,12 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let expected_bytes = in_len.div_ceil(8); // Integer vector : - let (i_xn, in_elem_bitwidth) = match in_elem.kind() { - ty::Int(i) => ( - args[0].immediate(), - i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()), - ), - ty::Uint(i) => ( - args[0].immediate(), - i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()), - ), - _ => return_error!(InvalidMonomorphization::VectorArgument { - span, - name, - in_ty, - in_elem - }), - }; + let in_elem_bitwidth = require_int_or_uint_ty!( + in_elem.kind(), + InvalidMonomorphization::VectorArgument { span, name, in_ty, in_elem } + ); - // LLVM doesn't always know the inputs are `0` or `!0`, so we shift here so it optimizes to - // `pmovmskb` and similar on x86. - let shift_indices = - vec![ - bx.cx.const_int(bx.type_ix(in_elem_bitwidth), (in_elem_bitwidth - 1) as _); - in_len as _ - ]; - let i_xn_msb = bx.lshr(i_xn, bx.const_vector(shift_indices.as_slice())); - // Truncate vector to an - let i1xn = bx.trunc(i_xn_msb, bx.type_vector(bx.type_i1(), in_len)); + let i1xn = vector_mask_to_bitmask(bx, args[0].immediate(), in_elem_bitwidth, in_len); // Bitcast to iN: let i_ = bx.bitcast(i1xn, bx.type_ix(in_len)); @@ -1663,30 +1684,34 @@ fn generic_simd_intrinsic<'ll, 'tcx>( require_simd!(ret_ty, SimdReturn); // Of the same length: - require!(in_len == out_len, InvalidMonomorphization::SecondArgumentLength { - span, - name, - in_len, - in_ty, - arg_ty: arg_tys[1], - out_len - }); - require!(in_len == out_len2, InvalidMonomorphization::ThirdArgumentLength { - span, - name, - in_len, - in_ty, - arg_ty: arg_tys[2], - out_len: out_len2 - }); + require!( + in_len == out_len, + InvalidMonomorphization::SecondArgumentLength { + span, + name, + in_len, + in_ty, + arg_ty: arg_tys[1], + out_len + } + ); + require!( + in_len == out_len2, + InvalidMonomorphization::ThirdArgumentLength { + span, + name, + in_len, + in_ty, + arg_ty: arg_tys[2], + out_len: out_len2 + } + ); // The return type must match the first argument type - require!(ret_ty == in_ty, InvalidMonomorphization::ExpectedReturnType { - span, - name, - in_ty, - ret_ty - }); + require!( + ret_ty == in_ty, + InvalidMonomorphization::ExpectedReturnType { span, name, in_ty, ret_ty } + ); require!( matches!( @@ -1704,28 +1729,23 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } ); - match element_ty2.kind() { - ty::Int(_) => (), - _ => { - return_error!(InvalidMonomorphization::ThirdArgElementType { - span, - name, - expected_element: element_ty2, - third_arg: arg_tys[2] - }); + let mask_elem_bitwidth = require_int_ty!( + element_ty2.kind(), + InvalidMonomorphization::ThirdArgElementType { + span, + name, + expected_element: element_ty2, + third_arg: arg_tys[2] } - } + ); // Alignment of T, must be a constant integer value: let alignment_ty = bx.type_i32(); let alignment = bx.const_i32(bx.align_of(in_elem).bytes() as i32); // Truncate the mask vector to a vector of i1s: - let (mask, mask_ty) = { - let i1 = bx.type_i1(); - let i1xn = bx.type_vector(i1, in_len); - (bx.trunc(args[2].immediate(), i1xn), i1xn) - }; + let mask = vector_mask_to_bitmask(bx, args[2].immediate(), mask_elem_bitwidth, in_len); + let mask_ty = bx.type_vector(bx.type_i1(), in_len); // Type of the vector of pointers: let llvm_pointer_vec_ty = llvm_vector_ty(bx, element_ty1, in_len); @@ -1777,22 +1797,23 @@ fn generic_simd_intrinsic<'ll, 'tcx>( require_simd!(ret_ty, SimdReturn); // Of the same length: - require!(values_len == mask_len, InvalidMonomorphization::ThirdArgumentLength { - span, - name, - in_len: mask_len, - in_ty: mask_ty, - arg_ty: values_ty, - out_len: values_len - }); + require!( + values_len == mask_len, + InvalidMonomorphization::ThirdArgumentLength { + span, + name, + in_len: mask_len, + in_ty: mask_ty, + arg_ty: values_ty, + out_len: values_len + } + ); // The return type must match the last argument type - require!(ret_ty == values_ty, InvalidMonomorphization::ExpectedReturnType { - span, - name, - in_ty: values_ty, - ret_ty - }); + require!( + ret_ty == values_ty, + InvalidMonomorphization::ExpectedReturnType { span, name, in_ty: values_ty, ret_ty } + ); require!( matches!( @@ -1810,8 +1831,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } ); - require!( - matches!(mask_elem.kind(), ty::Int(_)), + let m_elem_bitwidth = require_int_ty!( + mask_elem.kind(), InvalidMonomorphization::ThirdArgElementType { span, name, @@ -1820,17 +1841,13 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } ); + let mask = vector_mask_to_bitmask(bx, args[0].immediate(), m_elem_bitwidth, mask_len); + let mask_ty = bx.type_vector(bx.type_i1(), mask_len); + // Alignment of T, must be a constant integer value: let alignment_ty = bx.type_i32(); let alignment = bx.const_i32(bx.align_of(values_elem).bytes() as i32); - // Truncate the mask vector to a vector of i1s: - let (mask, mask_ty) = { - let i1 = bx.type_i1(); - let i1xn = bx.type_vector(i1, mask_len); - (bx.trunc(args[0].immediate(), i1xn), i1xn) - }; - let llvm_pointer = bx.type_ptr(); // Type of the vector of elements: @@ -1874,14 +1891,17 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let (values_len, values_elem) = require_simd!(values_ty, SimdThird); // Of the same length: - require!(values_len == mask_len, InvalidMonomorphization::ThirdArgumentLength { - span, - name, - in_len: mask_len, - in_ty: mask_ty, - arg_ty: values_ty, - out_len: values_len - }); + require!( + values_len == mask_len, + InvalidMonomorphization::ThirdArgumentLength { + span, + name, + in_len: mask_len, + in_ty: mask_ty, + arg_ty: values_ty, + out_len: values_len + } + ); // The second argument must be a mutable pointer type matching the element type require!( @@ -1901,8 +1921,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } ); - require!( - matches!(mask_elem.kind(), ty::Int(_)), + let m_elem_bitwidth = require_int_ty!( + mask_elem.kind(), InvalidMonomorphization::ThirdArgElementType { span, name, @@ -1911,17 +1931,13 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } ); + let mask = vector_mask_to_bitmask(bx, args[0].immediate(), m_elem_bitwidth, mask_len); + let mask_ty = bx.type_vector(bx.type_i1(), mask_len); + // Alignment of T, must be a constant integer value: let alignment_ty = bx.type_i32(); let alignment = bx.const_i32(bx.align_of(values_elem).bytes() as i32); - // Truncate the mask vector to a vector of i1s: - let (mask, mask_ty) = { - let i1 = bx.type_i1(); - let i1xn = bx.type_vector(i1, in_len); - (bx.trunc(args[0].immediate(), i1xn), i1xn) - }; - let ret_t = bx.type_void(); let llvm_pointer = bx.type_ptr(); @@ -1960,22 +1976,28 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let (element_len2, element_ty2) = require_simd!(arg_tys[2], SimdThird); // Of the same length: - require!(in_len == element_len1, InvalidMonomorphization::SecondArgumentLength { - span, - name, - in_len, - in_ty, - arg_ty: arg_tys[1], - out_len: element_len1 - }); - require!(in_len == element_len2, InvalidMonomorphization::ThirdArgumentLength { - span, - name, - in_len, - in_ty, - arg_ty: arg_tys[2], - out_len: element_len2 - }); + require!( + in_len == element_len1, + InvalidMonomorphization::SecondArgumentLength { + span, + name, + in_len, + in_ty, + arg_ty: arg_tys[1], + out_len: element_len1 + } + ); + require!( + in_len == element_len2, + InvalidMonomorphization::ThirdArgumentLength { + span, + name, + in_len, + in_ty, + arg_ty: arg_tys[2], + out_len: element_len2 + } + ); require!( matches!( @@ -1995,28 +2017,23 @@ fn generic_simd_intrinsic<'ll, 'tcx>( ); // The element type of the third argument must be a signed integer type of any width: - match element_ty2.kind() { - ty::Int(_) => (), - _ => { - return_error!(InvalidMonomorphization::ThirdArgElementType { - span, - name, - expected_element: element_ty2, - third_arg: arg_tys[2] - }); + let mask_elem_bitwidth = require_int_ty!( + element_ty2.kind(), + InvalidMonomorphization::ThirdArgElementType { + span, + name, + expected_element: element_ty2, + third_arg: arg_tys[2] } - } + ); // Alignment of T, must be a constant integer value: let alignment_ty = bx.type_i32(); let alignment = bx.const_i32(bx.align_of(in_elem).bytes() as i32); // Truncate the mask vector to a vector of i1s: - let (mask, mask_ty) = { - let i1 = bx.type_i1(); - let i1xn = bx.type_vector(i1, in_len); - (bx.trunc(args[2].immediate(), i1xn), i1xn) - }; + let mask = vector_mask_to_bitmask(bx, args[2].immediate(), mask_elem_bitwidth, in_len); + let mask_ty = bx.type_vector(bx.type_i1(), in_len); let ret_t = bx.type_void(); @@ -2049,13 +2066,10 @@ fn generic_simd_intrinsic<'ll, 'tcx>( ($name:ident : $integer_reduce:ident, $float_reduce:ident, $ordered:expr, $op:ident, $identity:expr) => { if name == sym::$name { - require!(ret_ty == in_elem, InvalidMonomorphization::ReturnType { - span, - name, - in_elem, - in_ty, - ret_ty - }); + require!( + ret_ty == in_elem, + InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty } + ); return match in_elem.kind() { ty::Int(_) | ty::Uint(_) => { let r = bx.$integer_reduce(args[0].immediate()); @@ -2124,13 +2138,10 @@ fn generic_simd_intrinsic<'ll, 'tcx>( macro_rules! minmax_red { ($name:ident: $int_red:ident, $float_red:ident) => { if name == sym::$name { - require!(ret_ty == in_elem, InvalidMonomorphization::ReturnType { - span, - name, - in_elem, - in_ty, - ret_ty - }); + require!( + ret_ty == in_elem, + InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty } + ); return match in_elem.kind() { ty::Int(_i) => Ok(bx.$int_red(args[0].immediate(), true)), ty::Uint(_u) => Ok(bx.$int_red(args[0].immediate(), false)), @@ -2155,17 +2166,19 @@ fn generic_simd_intrinsic<'ll, 'tcx>( ($name:ident : $red:ident, $boolean:expr) => { if name == sym::$name { let input = if !$boolean { - require!(ret_ty == in_elem, InvalidMonomorphization::ReturnType { - span, - name, - in_elem, - in_ty, - ret_ty - }); + require!( + ret_ty == in_elem, + InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty } + ); args[0].immediate() } else { - match in_elem.kind() { - ty::Int(_) | ty::Uint(_) => {} + let bitwidth = match in_elem.kind() { + ty::Int(i) => { + i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()) + } + ty::Uint(i) => { + i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()) + } _ => return_error!(InvalidMonomorphization::UnsupportedSymbol { span, name, @@ -2174,12 +2187,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>( in_elem, ret_ty }), - } + }; - // boolean reductions operate on vectors of i1s: - let i1 = bx.type_i1(); - let i1xn = bx.type_vector(i1, in_len as u64); - bx.trunc(args[0].immediate(), i1xn) + vector_mask_to_bitmask(bx, args[0].immediate(), bitwidth, in_len as _) }; return match in_elem.kind() { ty::Int(_) | ty::Uint(_) => { @@ -2207,25 +2217,27 @@ fn generic_simd_intrinsic<'ll, 'tcx>( if name == sym::simd_cast_ptr { let (out_len, out_elem) = require_simd!(ret_ty, SimdReturn); - require!(in_len == out_len, InvalidMonomorphization::ReturnLengthInputType { - span, - name, - in_len, - in_ty, - ret_ty, - out_len - }); + require!( + in_len == out_len, + InvalidMonomorphization::ReturnLengthInputType { + span, + name, + in_len, + in_ty, + ret_ty, + out_len + } + ); match in_elem.kind() { ty::RawPtr(p_ty, _) => { let metadata = p_ty.ptr_metadata_ty(bx.tcx, |ty| { bx.tcx.normalize_erasing_regions(bx.typing_env(), ty) }); - require!(metadata.is_unit(), InvalidMonomorphization::CastWidePointer { - span, - name, - ty: in_elem - }); + require!( + metadata.is_unit(), + InvalidMonomorphization::CastWidePointer { span, name, ty: in_elem } + ); } _ => { return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: in_elem }) @@ -2236,11 +2248,10 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let metadata = p_ty.ptr_metadata_ty(bx.tcx, |ty| { bx.tcx.normalize_erasing_regions(bx.typing_env(), ty) }); - require!(metadata.is_unit(), InvalidMonomorphization::CastWidePointer { - span, - name, - ty: out_elem - }); + require!( + metadata.is_unit(), + InvalidMonomorphization::CastWidePointer { span, name, ty: out_elem } + ); } _ => { return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: out_elem }) @@ -2252,14 +2263,17 @@ fn generic_simd_intrinsic<'ll, 'tcx>( if name == sym::simd_expose_provenance { let (out_len, out_elem) = require_simd!(ret_ty, SimdReturn); - require!(in_len == out_len, InvalidMonomorphization::ReturnLengthInputType { - span, - name, - in_len, - in_ty, - ret_ty, - out_len - }); + require!( + in_len == out_len, + InvalidMonomorphization::ReturnLengthInputType { + span, + name, + in_len, + in_ty, + ret_ty, + out_len + } + ); match in_elem.kind() { ty::RawPtr(_, _) => {} @@ -2277,14 +2291,17 @@ fn generic_simd_intrinsic<'ll, 'tcx>( if name == sym::simd_with_exposed_provenance { let (out_len, out_elem) = require_simd!(ret_ty, SimdReturn); - require!(in_len == out_len, InvalidMonomorphization::ReturnLengthInputType { - span, - name, - in_len, - in_ty, - ret_ty, - out_len - }); + require!( + in_len == out_len, + InvalidMonomorphization::ReturnLengthInputType { + span, + name, + in_len, + in_ty, + ret_ty, + out_len + } + ); match in_elem.kind() { ty::Uint(ty::UintTy::Usize) => {} @@ -2302,14 +2319,17 @@ fn generic_simd_intrinsic<'ll, 'tcx>( if name == sym::simd_cast || name == sym::simd_as { let (out_len, out_elem) = require_simd!(ret_ty, SimdReturn); - require!(in_len == out_len, InvalidMonomorphization::ReturnLengthInputType { - span, - name, - in_len, - in_ty, - ret_ty, - out_len - }); + require!( + in_len == out_len, + InvalidMonomorphization::ReturnLengthInputType { + span, + name, + in_len, + in_ty, + ret_ty, + out_len + } + ); // casting cares about nominal type, not just structural type if in_elem == out_elem { return Ok(args[0].immediate()); diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 06afe8bb3ad54..e9e1b644f183e 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -13,6 +13,7 @@ #![feature(extern_types)] #![feature(file_buffered)] #![feature(hash_raw_entry)] +#![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] #![feature(iter_intersperse)] #![feature(let_chains)] @@ -29,7 +30,7 @@ use std::mem::ManuallyDrop; use back::owned_target_machine::OwnedTargetMachine; use back::write::{create_informational_target_machine, create_target_machine}; use errors::{AutoDiffWithoutLTO, ParseTargetMachineConfig}; -pub use llvm_util::target_features_cfg; +pub(crate) use llvm_util::target_features_cfg; use rustc_ast::expand::allocator::AllocatorKind; use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; @@ -71,14 +72,9 @@ mod debuginfo; mod declare; mod errors; mod intrinsic; - -// The following is a workaround that replaces `pub mod llvm;` and that fixes issue 53912. -#[path = "llvm/mod.rs"] -mod llvm_; -pub mod llvm { - pub use super::llvm_::*; -} - +// FIXME(Zalathar): Fix all the unreachable-pub warnings that would occur if +// this isn't pub, then make it not pub. +pub mod llvm; mod llvm_util; mod mono_item; mod type_; @@ -237,7 +233,6 @@ impl WriteBackendMethods for LlvmCodegenBackend { /// Generate autodiff rules fn autodiff( cgcx: &CodegenContext, - tcx: TyCtxt<'_>, module: &ModuleCodegen, diff_fncs: Vec, config: &ModuleConfig, @@ -246,13 +241,10 @@ impl WriteBackendMethods for LlvmCodegenBackend { let dcx = cgcx.create_dcx(); return Err(dcx.handle().emit_almost_fatal(AutoDiffWithoutLTO)); } - builder::autodiff::differentiate(module, cgcx, tcx, diff_fncs, config) + builder::autodiff::differentiate(module, cgcx, diff_fncs, config) } } -unsafe impl Send for LlvmCodegenBackend {} // Llvm is on a per-thread basis -unsafe impl Sync for LlvmCodegenBackend {} - impl LlvmCodegenBackend { pub fn new() -> Box { Box::new(LlvmCodegenBackend(())) diff --git a/compiler/rustc_codegen_llvm/src/llvm/archive_ro.rs b/compiler/rustc_codegen_llvm/src/llvm/archive_ro.rs index 4dabde55e9897..63b2b15c5145e 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/archive_ro.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/archive_ro.rs @@ -26,7 +26,7 @@ impl ArchiveRO { /// /// If this archive is used with a mutable method, then an error will be /// raised. - pub fn open(dst: &Path) -> Result { + pub(crate) fn open(dst: &Path) -> Result { unsafe { let s = path_to_c_string(dst); let ar = super::LLVMRustOpenArchive(s.as_ptr()).ok_or_else(|| { @@ -36,7 +36,7 @@ impl ArchiveRO { } } - pub fn iter(&self) -> Iter<'_> { + pub(crate) fn iter(&self) -> Iter<'_> { unsafe { Iter { raw: super::LLVMRustArchiveIteratorNew(self.raw) } } } } @@ -71,7 +71,7 @@ impl<'a> Drop for Iter<'a> { } impl<'a> Child<'a> { - pub fn name(&self) -> Option<&'a str> { + pub(crate) fn name(&self) -> Option<&'a str> { unsafe { let mut name_len = 0; let name_ptr = super::LLVMRustArchiveChildName(self.raw, &mut name_len); diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index 729d6f62e2431..39bac13a9680c 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -1,23 +1,31 @@ #![allow(non_camel_case_types)] +#![expect(dead_code)] use libc::{c_char, c_uint}; use super::ffi::{BasicBlock, Metadata, Module, Type, Value}; use crate::llvm::Bool; + +#[link(name = "llvm-wrapper", kind = "static")] extern "C" { // Enzyme - pub fn LLVMRustHasMetadata(I: &Value, KindID: c_uint) -> bool; - pub fn LLVMRustEraseInstBefore(BB: &BasicBlock, I: &Value); - pub fn LLVMRustGetLastInstruction<'a>(BB: &BasicBlock) -> Option<&'a Value>; - pub fn LLVMRustDIGetInstMetadata(I: &Value) -> Option<&Metadata>; - pub fn LLVMRustEraseInstFromParent(V: &Value); - pub fn LLVMRustGetTerminator<'a>(B: &BasicBlock) -> &'a Value; - pub fn LLVMRustVerifyFunction(V: &Value, action: LLVMRustVerifierFailureAction) -> Bool; + pub(crate) fn LLVMRustHasMetadata(I: &Value, KindID: c_uint) -> bool; + pub(crate) fn LLVMRustEraseInstUntilInclusive(BB: &BasicBlock, I: &Value); + pub(crate) fn LLVMRustGetLastInstruction<'a>(BB: &BasicBlock) -> Option<&'a Value>; + pub(crate) fn LLVMRustDIGetInstMetadata(I: &Value) -> Option<&Metadata>; + pub(crate) fn LLVMRustEraseInstFromParent(V: &Value); + pub(crate) fn LLVMRustGetTerminator<'a>(B: &BasicBlock) -> &'a Value; + pub(crate) fn LLVMRustVerifyFunction(V: &Value, action: LLVMRustVerifierFailureAction) -> Bool; +} - pub fn LLVMGetFunctionCallConv(F: &Value) -> c_uint; - pub fn LLVMGetReturnType(T: &Type) -> &Type; - pub fn LLVMGetParams(Fnc: &Value, parms: *mut &Value); - pub fn LLVMGetNamedFunction(M: &Module, Name: *const c_char) -> Option<&Value>; +extern "C" { + // Enzyme + pub(crate) fn LLVMDumpModule(M: &Module); + pub(crate) fn LLVMDumpValue(V: &Value); + pub(crate) fn LLVMGetFunctionCallConv(F: &Value) -> c_uint; + pub(crate) fn LLVMGetReturnType(T: &Type) -> &Type; + pub(crate) fn LLVMGetParams(Fnc: &Value, parms: *mut &Value); + pub(crate) fn LLVMGetNamedFunction(M: &Module, Name: *const c_char) -> Option<&Value>; } #[repr(C)] diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 472d4a3a72b3f..7f6f82eae48f2 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1,3 +1,15 @@ +//! Bindings to the LLVM-C API (`LLVM*`), and to our own `extern "C"` wrapper +//! functions around the unstable LLVM C++ API (`LLVMRust*`). +//! +//! ## Passing pointer/length strings as `*const c_uchar` +//! +//! Normally it's a good idea for Rust-side bindings to match the corresponding +//! C-side function declarations as closely as possible. But when passing `&str` +//! or `&[u8]` data as a pointer/length pair, it's more convenient to declare +//! the Rust-side pointer as `*const c_uchar` instead of `*const c_char`. +//! Both pointer types have the same ABI, and using `*const c_uchar` avoids +//! the need for an extra cast from `*const u8` on the Rust side. + #![allow(non_camel_case_types)] #![allow(non_upper_case_globals)] @@ -5,17 +17,18 @@ use std::fmt::Debug; use std::marker::PhantomData; use std::ptr; -use libc::{c_char, c_int, c_uint, c_ulonglong, c_void, size_t}; +use bitflags::bitflags; +use libc::{c_char, c_int, c_uchar, c_uint, c_ulonglong, c_void, size_t}; use rustc_macros::TryFromU32; use rustc_target::spec::SymbolVisibility; use super::RustString; use super::debuginfo::{ DIArray, DIBasicType, DIBuilder, DICompositeType, DIDerivedType, DIDescriptor, DIEnumerator, - DIFile, DIFlags, DIGlobalVariableExpression, DILexicalBlock, DILocation, DINameSpace, - DISPFlags, DIScope, DISubprogram, DISubrange, DITemplateTypeParameter, DIType, DIVariable, - DebugEmissionKind, DebugNameTableKind, + DIFile, DIFlags, DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram, + DISubrange, DITemplateTypeParameter, DIType, DIVariable, DebugEmissionKind, DebugNameTableKind, }; +use crate::llvm; /// In the LLVM-C API, boolean values are passed as `typedef int LLVMBool`, /// which has a different ABI from Rust or C++ `bool`. @@ -56,19 +69,6 @@ pub enum LLVMRustResult { Failure, } -/// Translation of LLVM's MachineTypes enum, defined in llvm\include\llvm\BinaryFormat\COFF.h. -/// -/// We include only architectures supported on Windows. -#[derive(Copy, Clone, PartialEq)] -#[repr(C)] -pub enum LLVMMachineType { - AMD64 = 0x8664, - I386 = 0x14c, - ARM64 = 0xaa64, - ARM64EC = 0xa641, - ARM = 0x01c0, -} - /// Must match the layout of `LLVMRustModuleFlagMergeBehavior`. /// /// When merging modules (e.g. during LTO), their metadata flags are combined. Conflicts are @@ -120,6 +120,7 @@ pub enum CallConv { X86_Intr = 83, AvrNonBlockingInterrupt = 84, AvrInterrupt = 85, + AmdgpuKernel = 91, } /// Must match the layout of `LLVMLinkage`. @@ -159,7 +160,7 @@ pub enum Visibility { } impl Visibility { - pub fn from_generic(visibility: SymbolVisibility) -> Self { + pub(crate) fn from_generic(visibility: SymbolVisibility) -> Self { match visibility { SymbolVisibility::Hidden => Visibility::Hidden, SymbolVisibility::Protected => Visibility::Protected, @@ -254,7 +255,7 @@ pub enum IntPredicate { } impl IntPredicate { - pub fn from_generic(intpre: rustc_codegen_ssa::common::IntPredicate) -> Self { + pub(crate) fn from_generic(intpre: rustc_codegen_ssa::common::IntPredicate) -> Self { use rustc_codegen_ssa::common::IntPredicate as Common; match intpre { Common::IntEQ => Self::IntEQ, @@ -294,7 +295,7 @@ pub enum RealPredicate { } impl RealPredicate { - pub fn from_generic(realp: rustc_codegen_ssa::common::RealPredicate) -> Self { + pub(crate) fn from_generic(realp: rustc_codegen_ssa::common::RealPredicate) -> Self { use rustc_codegen_ssa::common::RealPredicate as Common; match realp { Common::RealPredicateFalse => Self::RealPredicateFalse, @@ -343,7 +344,7 @@ pub enum TypeKind { } impl TypeKind { - pub fn to_generic(self) -> rustc_codegen_ssa::common::TypeKind { + pub(crate) fn to_generic(self) -> rustc_codegen_ssa::common::TypeKind { use rustc_codegen_ssa::common::TypeKind as Common; match self { Self::Void => Common::Void, @@ -387,7 +388,7 @@ pub enum AtomicRmwBinOp { } impl AtomicRmwBinOp { - pub fn from_generic(op: rustc_codegen_ssa::common::AtomicRmwBinOp) -> Self { + pub(crate) fn from_generic(op: rustc_codegen_ssa::common::AtomicRmwBinOp) -> Self { use rustc_codegen_ssa::common::AtomicRmwBinOp as Common; match op { Common::AtomicXchg => Self::AtomicXchg, @@ -421,7 +422,7 @@ pub enum AtomicOrdering { } impl AtomicOrdering { - pub fn from_generic(ao: rustc_codegen_ssa::common::AtomicOrdering) -> Self { + pub(crate) fn from_generic(ao: rustc_codegen_ssa::common::AtomicOrdering) -> Self { use rustc_codegen_ssa::common::AtomicOrdering as Common; match ao { Common::Unordered => Self::Unordered, @@ -631,16 +632,6 @@ pub enum ThreadLocalMode { LocalExec, } -/// LLVMRustTailCallKind -#[derive(Copy, Clone)] -#[repr(C)] -pub enum TailCallKind { - None, - Tail, - MustTail, - NoTail, -} - /// LLVMRustChecksumKind #[derive(Copy, Clone)] #[repr(C)] @@ -660,6 +651,79 @@ pub enum MemoryEffects { InaccessibleMemOnly, } +/// LLVMOpcode +#[derive(Copy, Clone, PartialEq, Eq)] +#[repr(C)] +pub enum Opcode { + Ret = 1, + Br = 2, + Switch = 3, + IndirectBr = 4, + Invoke = 5, + Unreachable = 7, + CallBr = 67, + FNeg = 66, + Add = 8, + FAdd = 9, + Sub = 10, + FSub = 11, + Mul = 12, + FMul = 13, + UDiv = 14, + SDiv = 15, + FDiv = 16, + URem = 17, + SRem = 18, + FRem = 19, + Shl = 20, + LShr = 21, + AShr = 22, + And = 23, + Or = 24, + Xor = 25, + Alloca = 26, + Load = 27, + Store = 28, + GetElementPtr = 29, + Trunc = 30, + ZExt = 31, + SExt = 32, + FPToUI = 33, + FPToSI = 34, + UIToFP = 35, + SIToFP = 36, + FPTrunc = 37, + FPExt = 38, + PtrToInt = 39, + IntToPtr = 40, + BitCast = 41, + AddrSpaceCast = 60, + ICmp = 42, + FCmp = 43, + PHI = 44, + Call = 45, + Select = 46, + UserOp1 = 47, + UserOp2 = 48, + VAArg = 49, + ExtractElement = 50, + InsertElement = 51, + ShuffleVector = 52, + ExtractValue = 53, + InsertValue = 54, + Freeze = 68, + Fence = 55, + AtomicCmpXchg = 56, + AtomicRMW = 57, + Resume = 58, + LandingPad = 59, + CleanupRet = 61, + CatchRet = 62, + CatchPad = 63, + CleanupPad = 64, + CatchSwitch = 65, +} + unsafe extern "C" { type Opaque; } @@ -686,7 +750,6 @@ pub struct Builder<'a>(InvariantOpaque<'a>); #[repr(C)] pub struct PassManager<'a>(InvariantOpaque<'a>); unsafe extern "C" { - pub type Pass; pub type TargetMachine; pub type Archive; } @@ -712,15 +775,52 @@ unsafe extern "C" { } pub type DiagnosticHandlerTy = unsafe extern "C" fn(&DiagnosticInfo, *mut c_void); -pub type InlineAsmDiagHandlerTy = unsafe extern "C" fn(&SMDiagnostic, *const c_void, c_uint); pub mod debuginfo { + use std::ptr; + use bitflags::bitflags; use super::{InvariantOpaque, Metadata}; + use crate::llvm::{self, Module}; + /// Opaque target type for references to an LLVM debuginfo builder. + /// + /// `&'_ DIBuilder<'ll>` corresponds to `LLVMDIBuilderRef`, which is the + /// LLVM-C wrapper for `DIBuilder *`. + /// + /// Debuginfo builders are created and destroyed during codegen, so the + /// builder reference typically has a shorter lifetime than the LLVM + /// session (`'ll`) that it participates in. #[repr(C)] - pub struct DIBuilder<'a>(InvariantOpaque<'a>); + pub struct DIBuilder<'ll>(InvariantOpaque<'ll>); + + /// Owning pointer to a `DIBuilder<'ll>` that will dispose of the builder + /// when dropped. Use `.as_ref()` to get the underlying `&DIBuilder` + /// needed for debuginfo FFI calls. + pub(crate) struct DIBuilderBox<'ll> { + raw: ptr::NonNull>, + } + + impl<'ll> DIBuilderBox<'ll> { + pub(crate) fn new(llmod: &'ll Module) -> Self { + let raw = unsafe { llvm::LLVMCreateDIBuilder(llmod) }; + let raw = ptr::NonNull::new(raw).unwrap(); + Self { raw } + } + + pub(crate) fn as_ref(&self) -> &DIBuilder<'ll> { + // SAFETY: This is an owning pointer, so `&DIBuilder` is valid + // for as long as `&self` is. + unsafe { self.raw.as_ref() } + } + } + + impl<'ll> Drop for DIBuilderBox<'ll> { + fn drop(&mut self) { + unsafe { llvm::LLVMDisposeDIBuilder(self.raw) }; + } + } pub type DIDescriptor = Metadata; pub type DILocation = Metadata; @@ -728,7 +828,6 @@ pub mod debuginfo { pub type DIFile = DIScope; pub type DILexicalBlock = DIScope; pub type DISubprogram = DIScope; - pub type DINameSpace = DIScope; pub type DIType = DIDescriptor; pub type DIBasicType = DIType; pub type DIDerivedType = DIType; @@ -740,8 +839,11 @@ pub mod debuginfo { pub type DIEnumerator = DIDescriptor; pub type DITemplateTypeParameter = DIDescriptor; - // These values **must** match with LLVMRustDIFlags!! bitflags! { + /// Must match the layout of `LLVMDIFlags` in the LLVM-C API. + /// + /// Each value declared here must also be covered by the static + /// assertions in `RustWrapper.cpp` used by `fromRust(LLVMDIFlags)`. #[repr(transparent)] #[derive(Clone, Copy, Default)] pub struct DIFlags: u32 { @@ -751,7 +853,7 @@ pub mod debuginfo { const FlagPublic = 3; const FlagFwdDecl = (1 << 2); const FlagAppleBlock = (1 << 3); - const FlagBlockByrefStruct = (1 << 4); + const FlagReservedBit4 = (1 << 4); const FlagVirtual = (1 << 5); const FlagArtificial = (1 << 6); const FlagExplicit = (1 << 7); @@ -762,10 +864,21 @@ pub mod debuginfo { const FlagStaticMember = (1 << 12); const FlagLValueReference = (1 << 13); const FlagRValueReference = (1 << 14); - const FlagExternalTypeRef = (1 << 15); + const FlagReserved = (1 << 15); + const FlagSingleInheritance = (1 << 16); + const FlagMultipleInheritance = (2 << 16); + const FlagVirtualInheritance = (3 << 16); const FlagIntroducedVirtual = (1 << 18); const FlagBitField = (1 << 19); const FlagNoReturn = (1 << 20); + // The bit at (1 << 21) is unused, but was `LLVMDIFlagMainSubprogram`. + const FlagTypePassByValue = (1 << 22); + const FlagTypePassByReference = (1 << 23); + const FlagEnumClass = (1 << 24); + const FlagThunk = (1 << 25); + const FlagNonTrivial = (1 << 26); + const FlagBigEndian = (1 << 27); + const FlagLittleEndian = (1 << 28); } } @@ -795,7 +908,7 @@ pub mod debuginfo { } impl DebugEmissionKind { - pub fn from_generic(kind: rustc_session::config::DebugInfo) -> Self { + pub(crate) fn from_generic(kind: rustc_session::config::DebugInfo) -> Self { // We should be setting LLVM's emission kind to `LineTablesOnly` if // we are compiling with "limited" debuginfo. However, some of the // existing tools relied on slightly more debuginfo being generated than @@ -826,7 +939,6 @@ pub mod debuginfo { } } -use bitflags::bitflags; // These values **must** match with LLVMRustAllocKindFlags bitflags! { #[repr(transparent)] @@ -855,49 +967,55 @@ pub type GetSymbolsErrorCallback = unsafe extern "C" fn(*const c_char) -> *mut c unsafe extern "C" { // Create and destroy contexts. - pub fn LLVMContextDispose(C: &'static mut Context); - pub fn LLVMGetMDKindIDInContext(C: &Context, Name: *const c_char, SLen: c_uint) -> c_uint; + pub(crate) fn LLVMContextDispose(C: &'static mut Context); + pub(crate) fn LLVMGetMDKindIDInContext( + C: &Context, + Name: *const c_char, + SLen: c_uint, + ) -> c_uint; // Create modules. - pub fn LLVMModuleCreateWithNameInContext(ModuleID: *const c_char, C: &Context) -> &Module; - pub fn LLVMGetModuleContext(M: &Module) -> &Context; - pub fn LLVMCloneModule(M: &Module) -> &Module; + pub(crate) fn LLVMModuleCreateWithNameInContext( + ModuleID: *const c_char, + C: &Context, + ) -> &Module; + pub(crate) fn LLVMCloneModule(M: &Module) -> &Module; /// Data layout. See Module::getDataLayout. - pub fn LLVMGetDataLayoutStr(M: &Module) -> *const c_char; - pub fn LLVMSetDataLayout(M: &Module, Triple: *const c_char); + pub(crate) fn LLVMGetDataLayoutStr(M: &Module) -> *const c_char; + pub(crate) fn LLVMSetDataLayout(M: &Module, Triple: *const c_char); /// See Module::setModuleInlineAsm. - pub fn LLVMAppendModuleInlineAsm(M: &Module, Asm: *const c_char, Len: size_t); + pub(crate) fn LLVMAppendModuleInlineAsm(M: &Module, Asm: *const c_char, Len: size_t); // Operations on integer types - pub fn LLVMInt1TypeInContext(C: &Context) -> &Type; - pub fn LLVMInt8TypeInContext(C: &Context) -> &Type; - pub fn LLVMInt16TypeInContext(C: &Context) -> &Type; - pub fn LLVMInt32TypeInContext(C: &Context) -> &Type; - pub fn LLVMInt64TypeInContext(C: &Context) -> &Type; - pub fn LLVMIntTypeInContext(C: &Context, NumBits: c_uint) -> &Type; + pub(crate) fn LLVMInt1TypeInContext(C: &Context) -> &Type; + pub(crate) fn LLVMInt8TypeInContext(C: &Context) -> &Type; + pub(crate) fn LLVMInt16TypeInContext(C: &Context) -> &Type; + pub(crate) fn LLVMInt32TypeInContext(C: &Context) -> &Type; + pub(crate) fn LLVMInt64TypeInContext(C: &Context) -> &Type; + pub(crate) fn LLVMIntTypeInContext(C: &Context, NumBits: c_uint) -> &Type; - pub fn LLVMGetIntTypeWidth(IntegerTy: &Type) -> c_uint; + pub(crate) fn LLVMGetIntTypeWidth(IntegerTy: &Type) -> c_uint; // Operations on real types - pub fn LLVMHalfTypeInContext(C: &Context) -> &Type; - pub fn LLVMFloatTypeInContext(C: &Context) -> &Type; - pub fn LLVMDoubleTypeInContext(C: &Context) -> &Type; - pub fn LLVMFP128TypeInContext(C: &Context) -> &Type; + pub(crate) fn LLVMHalfTypeInContext(C: &Context) -> &Type; + pub(crate) fn LLVMFloatTypeInContext(C: &Context) -> &Type; + pub(crate) fn LLVMDoubleTypeInContext(C: &Context) -> &Type; + pub(crate) fn LLVMFP128TypeInContext(C: &Context) -> &Type; // Operations on function types - pub fn LLVMFunctionType<'a>( + pub(crate) fn LLVMFunctionType<'a>( ReturnType: &'a Type, ParamTypes: *const &'a Type, ParamCount: c_uint, IsVarArg: Bool, ) -> &'a Type; - pub fn LLVMCountParamTypes(FunctionTy: &Type) -> c_uint; - pub fn LLVMGetParamTypes<'a>(FunctionTy: &'a Type, Dest: *mut &'a Type); + pub(crate) fn LLVMCountParamTypes(FunctionTy: &Type) -> c_uint; + pub(crate) fn LLVMGetParamTypes<'a>(FunctionTy: &'a Type, Dest: *mut &'a Type); // Operations on struct types - pub fn LLVMStructTypeInContext<'a>( + pub(crate) fn LLVMStructTypeInContext<'a>( C: &'a Context, ElementTypes: *const &'a Type, ElementCount: c_uint, @@ -905,107 +1023,122 @@ unsafe extern "C" { ) -> &'a Type; // Operations on array, pointer, and vector types (sequence types) - pub fn LLVMPointerTypeInContext(C: &Context, AddressSpace: c_uint) -> &Type; - pub fn LLVMVectorType(ElementType: &Type, ElementCount: c_uint) -> &Type; + pub(crate) fn LLVMPointerTypeInContext(C: &Context, AddressSpace: c_uint) -> &Type; + pub(crate) fn LLVMVectorType(ElementType: &Type, ElementCount: c_uint) -> &Type; - pub fn LLVMGetElementType(Ty: &Type) -> &Type; - pub fn LLVMGetVectorSize(VectorTy: &Type) -> c_uint; + pub(crate) fn LLVMGetElementType(Ty: &Type) -> &Type; + pub(crate) fn LLVMGetVectorSize(VectorTy: &Type) -> c_uint; // Operations on other types - pub fn LLVMVoidTypeInContext(C: &Context) -> &Type; - pub fn LLVMTokenTypeInContext(C: &Context) -> &Type; - pub fn LLVMMetadataTypeInContext(C: &Context) -> &Type; + pub(crate) fn LLVMVoidTypeInContext(C: &Context) -> &Type; + pub(crate) fn LLVMTokenTypeInContext(C: &Context) -> &Type; + pub(crate) fn LLVMMetadataTypeInContext(C: &Context) -> &Type; // Operations on all values - pub fn LLVMTypeOf(Val: &Value) -> &Type; - pub fn LLVMGetValueName2(Val: &Value, Length: *mut size_t) -> *const c_char; - pub fn LLVMSetValueName2(Val: &Value, Name: *const c_char, NameLen: size_t); - pub fn LLVMReplaceAllUsesWith<'a>(OldVal: &'a Value, NewVal: &'a Value); - pub fn LLVMSetMetadata<'a>(Val: &'a Value, KindID: c_uint, Node: &'a Value); - pub fn LLVMGlobalSetMetadata<'a>(Val: &'a Value, KindID: c_uint, Metadata: &'a Metadata); - pub fn LLVMValueAsMetadata(Node: &Value) -> &Metadata; + pub(crate) fn LLVMTypeOf(Val: &Value) -> &Type; + pub(crate) fn LLVMGetValueName2(Val: &Value, Length: *mut size_t) -> *const c_char; + pub(crate) fn LLVMSetValueName2(Val: &Value, Name: *const c_char, NameLen: size_t); + pub(crate) fn LLVMReplaceAllUsesWith<'a>(OldVal: &'a Value, NewVal: &'a Value); + pub(crate) fn LLVMSetMetadata<'a>(Val: &'a Value, KindID: c_uint, Node: &'a Value); + pub(crate) fn LLVMGlobalSetMetadata<'a>(Val: &'a Value, KindID: c_uint, Metadata: &'a Metadata); + pub(crate) fn LLVMValueAsMetadata(Node: &Value) -> &Metadata; // Operations on constants of any type - pub fn LLVMConstNull(Ty: &Type) -> &Value; - pub fn LLVMGetUndef(Ty: &Type) -> &Value; - pub fn LLVMGetPoison(Ty: &Type) -> &Value; + pub(crate) fn LLVMConstNull(Ty: &Type) -> &Value; + pub(crate) fn LLVMGetUndef(Ty: &Type) -> &Value; + pub(crate) fn LLVMGetPoison(Ty: &Type) -> &Value; // Operations on metadata - pub fn LLVMMDStringInContext2(C: &Context, Str: *const c_char, SLen: size_t) -> &Metadata; - pub fn LLVMMDNodeInContext2<'a>( + pub(crate) fn LLVMMDStringInContext2( + C: &Context, + Str: *const c_char, + SLen: size_t, + ) -> &Metadata; + pub(crate) fn LLVMMDNodeInContext2<'a>( C: &'a Context, Vals: *const &'a Metadata, Count: size_t, ) -> &'a Metadata; - pub fn LLVMAddNamedMetadataOperand<'a>(M: &'a Module, Name: *const c_char, Val: &'a Value); + pub(crate) fn LLVMAddNamedMetadataOperand<'a>( + M: &'a Module, + Name: *const c_char, + Val: &'a Value, + ); // Operations on scalar constants - pub fn LLVMConstInt(IntTy: &Type, N: c_ulonglong, SignExtend: Bool) -> &Value; - pub fn LLVMConstIntOfArbitraryPrecision(IntTy: &Type, Wn: c_uint, Ws: *const u64) -> &Value; - pub fn LLVMConstReal(RealTy: &Type, N: f64) -> &Value; + pub(crate) fn LLVMConstInt(IntTy: &Type, N: c_ulonglong, SignExtend: Bool) -> &Value; + pub(crate) fn LLVMConstIntOfArbitraryPrecision( + IntTy: &Type, + Wn: c_uint, + Ws: *const u64, + ) -> &Value; + pub(crate) fn LLVMConstReal(RealTy: &Type, N: f64) -> &Value; // Operations on composite constants - pub fn LLVMConstArray2<'a>( + pub(crate) fn LLVMConstArray2<'a>( ElementTy: &'a Type, ConstantVals: *const &'a Value, Length: u64, ) -> &'a Value; - pub fn LLVMArrayType2(ElementType: &Type, ElementCount: u64) -> &Type; - pub fn LLVMConstStringInContext2( + pub(crate) fn LLVMArrayType2(ElementType: &Type, ElementCount: u64) -> &Type; + pub(crate) fn LLVMConstStringInContext2( C: &Context, Str: *const c_char, Length: size_t, DontNullTerminate: Bool, ) -> &Value; - pub fn LLVMConstStructInContext<'a>( + pub(crate) fn LLVMConstStructInContext<'a>( C: &'a Context, ConstantVals: *const &'a Value, Count: c_uint, Packed: Bool, ) -> &'a Value; - pub fn LLVMConstVector(ScalarConstantVals: *const &Value, Size: c_uint) -> &Value; + pub(crate) fn LLVMConstVector(ScalarConstantVals: *const &Value, Size: c_uint) -> &Value; // Constant expressions - pub fn LLVMConstInBoundsGEP2<'a>( + pub(crate) fn LLVMConstInBoundsGEP2<'a>( ty: &'a Type, ConstantVal: &'a Value, ConstantIndices: *const &'a Value, NumIndices: c_uint, ) -> &'a Value; - pub fn LLVMConstPtrToInt<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value; - pub fn LLVMConstIntToPtr<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value; - pub fn LLVMConstBitCast<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value; - pub fn LLVMGetAggregateElement(ConstantVal: &Value, Idx: c_uint) -> Option<&Value>; + pub(crate) fn LLVMConstPtrToInt<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value; + pub(crate) fn LLVMConstIntToPtr<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value; + pub(crate) fn LLVMConstBitCast<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value; + pub(crate) fn LLVMConstPointerCast<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value; + pub(crate) fn LLVMGetAggregateElement(ConstantVal: &Value, Idx: c_uint) -> Option<&Value>; + pub(crate) fn LLVMGetConstOpcode(ConstantVal: &Value) -> Opcode; + pub(crate) fn LLVMIsAConstantExpr(Val: &Value) -> Option<&Value>; // Operations on global variables, functions, and aliases (globals) - pub fn LLVMIsDeclaration(Global: &Value) -> Bool; - pub fn LLVMGetLinkage(Global: &Value) -> RawEnum; - pub fn LLVMSetLinkage(Global: &Value, RustLinkage: Linkage); - pub fn LLVMSetSection(Global: &Value, Section: *const c_char); - pub fn LLVMGetVisibility(Global: &Value) -> RawEnum; - pub fn LLVMSetVisibility(Global: &Value, Viz: Visibility); - pub fn LLVMGetAlignment(Global: &Value) -> c_uint; - pub fn LLVMSetAlignment(Global: &Value, Bytes: c_uint); - pub fn LLVMSetDLLStorageClass(V: &Value, C: DLLStorageClass); - pub fn LLVMGlobalGetValueType(Global: &Value) -> &Type; + pub(crate) fn LLVMIsDeclaration(Global: &Value) -> Bool; + pub(crate) fn LLVMGetLinkage(Global: &Value) -> RawEnum; + pub(crate) fn LLVMSetLinkage(Global: &Value, RustLinkage: Linkage); + pub(crate) fn LLVMSetSection(Global: &Value, Section: *const c_char); + pub(crate) fn LLVMGetVisibility(Global: &Value) -> RawEnum; + pub(crate) fn LLVMSetVisibility(Global: &Value, Viz: Visibility); + pub(crate) fn LLVMGetAlignment(Global: &Value) -> c_uint; + pub(crate) fn LLVMSetAlignment(Global: &Value, Bytes: c_uint); + pub(crate) fn LLVMSetDLLStorageClass(V: &Value, C: DLLStorageClass); + pub(crate) fn LLVMGlobalGetValueType(Global: &Value) -> &Type; // Operations on global variables - pub fn LLVMIsAGlobalVariable(GlobalVar: &Value) -> Option<&Value>; - pub fn LLVMAddGlobal<'a>(M: &'a Module, Ty: &'a Type, Name: *const c_char) -> &'a Value; - pub fn LLVMGetNamedGlobal(M: &Module, Name: *const c_char) -> Option<&Value>; - pub fn LLVMGetFirstGlobal(M: &Module) -> Option<&Value>; - pub fn LLVMGetNextGlobal(GlobalVar: &Value) -> Option<&Value>; - pub fn LLVMDeleteGlobal(GlobalVar: &Value); - pub fn LLVMGetInitializer(GlobalVar: &Value) -> Option<&Value>; - pub fn LLVMSetInitializer<'a>(GlobalVar: &'a Value, ConstantVal: &'a Value); - pub fn LLVMIsThreadLocal(GlobalVar: &Value) -> Bool; - pub fn LLVMSetThreadLocalMode(GlobalVar: &Value, Mode: ThreadLocalMode); - pub fn LLVMIsGlobalConstant(GlobalVar: &Value) -> Bool; - pub fn LLVMSetGlobalConstant(GlobalVar: &Value, IsConstant: Bool); - pub fn LLVMSetTailCall(CallInst: &Value, IsTailCall: Bool); + pub(crate) fn LLVMIsAGlobalVariable(GlobalVar: &Value) -> Option<&Value>; + pub(crate) fn LLVMAddGlobal<'a>(M: &'a Module, Ty: &'a Type, Name: *const c_char) -> &'a Value; + pub(crate) fn LLVMGetNamedGlobal(M: &Module, Name: *const c_char) -> Option<&Value>; + pub(crate) fn LLVMGetFirstGlobal(M: &Module) -> Option<&Value>; + pub(crate) fn LLVMGetNextGlobal(GlobalVar: &Value) -> Option<&Value>; + pub(crate) fn LLVMDeleteGlobal(GlobalVar: &Value); + pub(crate) fn LLVMGetInitializer(GlobalVar: &Value) -> Option<&Value>; + pub(crate) fn LLVMSetInitializer<'a>(GlobalVar: &'a Value, ConstantVal: &'a Value); + pub(crate) fn LLVMIsThreadLocal(GlobalVar: &Value) -> Bool; + pub(crate) fn LLVMSetThreadLocalMode(GlobalVar: &Value, Mode: ThreadLocalMode); + pub(crate) fn LLVMIsGlobalConstant(GlobalVar: &Value) -> Bool; + pub(crate) fn LLVMSetGlobalConstant(GlobalVar: &Value, IsConstant: Bool); + pub(crate) fn LLVMSetTailCall(CallInst: &Value, IsTailCall: Bool); // Operations on attributes - pub fn LLVMCreateStringAttribute( + pub(crate) fn LLVMCreateStringAttribute( C: &Context, Name: *const c_char, NameLen: c_uint, @@ -1014,33 +1147,34 @@ unsafe extern "C" { ) -> &Attribute; // Operations on functions - pub fn LLVMSetFunctionCallConv(Fn: &Value, CC: c_uint); + pub(crate) fn LLVMSetFunctionCallConv(Fn: &Value, CC: c_uint); // Operations on parameters - pub fn LLVMIsAArgument(Val: &Value) -> Option<&Value>; - pub fn LLVMCountParams(Fn: &Value) -> c_uint; - pub fn LLVMGetParam(Fn: &Value, Index: c_uint) -> &Value; + pub(crate) fn LLVMIsAArgument(Val: &Value) -> Option<&Value>; + pub(crate) fn LLVMCountParams(Fn: &Value) -> c_uint; + pub(crate) fn LLVMGetParam(Fn: &Value, Index: c_uint) -> &Value; // Operations on basic blocks - pub fn LLVMGetBasicBlockParent(BB: &BasicBlock) -> &Value; - pub fn LLVMAppendBasicBlockInContext<'a>( + pub(crate) fn LLVMGetBasicBlockParent(BB: &BasicBlock) -> &Value; + pub(crate) fn LLVMAppendBasicBlockInContext<'a>( C: &'a Context, Fn: &'a Value, Name: *const c_char, ) -> &'a BasicBlock; // Operations on instructions - pub fn LLVMIsAInstruction(Val: &Value) -> Option<&Value>; - pub fn LLVMGetFirstBasicBlock(Fn: &Value) -> &BasicBlock; + pub(crate) fn LLVMIsAInstruction(Val: &Value) -> Option<&Value>; + pub(crate) fn LLVMGetFirstBasicBlock(Fn: &Value) -> &BasicBlock; + pub(crate) fn LLVMGetOperand(Val: &Value, Index: c_uint) -> Option<&Value>; // Operations on call sites - pub fn LLVMSetInstructionCallConv(Instr: &Value, CC: c_uint); + pub(crate) fn LLVMSetInstructionCallConv(Instr: &Value, CC: c_uint); // Operations on load/store instructions (only) - pub fn LLVMSetVolatile(MemoryAccessInst: &Value, volatile: Bool); + pub(crate) fn LLVMSetVolatile(MemoryAccessInst: &Value, volatile: Bool); // Operations on phi nodes - pub fn LLVMAddIncoming<'a>( + pub(crate) fn LLVMAddIncoming<'a>( PhiNode: &'a Value, IncomingValues: *const &'a Value, IncomingBlocks: *const &'a BasicBlock, @@ -1048,263 +1182,278 @@ unsafe extern "C" { ); // Instruction builders - pub fn LLVMCreateBuilderInContext(C: &Context) -> &mut Builder<'_>; - pub fn LLVMPositionBuilderAtEnd<'a>(Builder: &Builder<'a>, Block: &'a BasicBlock); - pub fn LLVMGetInsertBlock<'a>(Builder: &Builder<'a>) -> &'a BasicBlock; - pub fn LLVMDisposeBuilder<'a>(Builder: &'a mut Builder<'a>); + pub(crate) fn LLVMCreateBuilderInContext(C: &Context) -> &mut Builder<'_>; + pub(crate) fn LLVMPositionBuilderAtEnd<'a>(Builder: &Builder<'a>, Block: &'a BasicBlock); + pub(crate) fn LLVMGetInsertBlock<'a>(Builder: &Builder<'a>) -> &'a BasicBlock; + pub(crate) fn LLVMDisposeBuilder<'a>(Builder: &'a mut Builder<'a>); // Metadata - pub fn LLVMSetCurrentDebugLocation2<'a>(Builder: &Builder<'a>, Loc: *const Metadata); - pub fn LLVMGetCurrentDebugLocation2<'a>(Builder: &Builder<'a>) -> Option<&'a Metadata>; + pub(crate) fn LLVMSetCurrentDebugLocation2<'a>(Builder: &Builder<'a>, Loc: *const Metadata); + pub(crate) fn LLVMGetCurrentDebugLocation2<'a>(Builder: &Builder<'a>) -> Option<&'a Metadata>; // Terminators - pub fn LLVMBuildRetVoid<'a>(B: &Builder<'a>) -> &'a Value; - pub fn LLVMBuildRet<'a>(B: &Builder<'a>, V: &'a Value) -> &'a Value; - pub fn LLVMBuildBr<'a>(B: &Builder<'a>, Dest: &'a BasicBlock) -> &'a Value; - pub fn LLVMBuildCondBr<'a>( + pub(crate) fn LLVMBuildRetVoid<'a>(B: &Builder<'a>) -> &'a Value; + pub(crate) fn LLVMBuildRet<'a>(B: &Builder<'a>, V: &'a Value) -> &'a Value; + pub(crate) fn LLVMBuildBr<'a>(B: &Builder<'a>, Dest: &'a BasicBlock) -> &'a Value; + pub(crate) fn LLVMBuildCondBr<'a>( B: &Builder<'a>, If: &'a Value, Then: &'a BasicBlock, Else: &'a BasicBlock, ) -> &'a Value; - pub fn LLVMBuildSwitch<'a>( + pub(crate) fn LLVMBuildSwitch<'a>( B: &Builder<'a>, V: &'a Value, Else: &'a BasicBlock, NumCases: c_uint, ) -> &'a Value; - pub fn LLVMBuildLandingPad<'a>( + pub(crate) fn LLVMBuildLandingPad<'a>( B: &Builder<'a>, Ty: &'a Type, PersFn: Option<&'a Value>, NumClauses: c_uint, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildResume<'a>(B: &Builder<'a>, Exn: &'a Value) -> &'a Value; - pub fn LLVMBuildUnreachable<'a>(B: &Builder<'a>) -> &'a Value; + pub(crate) fn LLVMBuildResume<'a>(B: &Builder<'a>, Exn: &'a Value) -> &'a Value; + pub(crate) fn LLVMBuildUnreachable<'a>(B: &Builder<'a>) -> &'a Value; - pub fn LLVMBuildCleanupPad<'a>( + pub(crate) fn LLVMBuildCleanupPad<'a>( B: &Builder<'a>, ParentPad: Option<&'a Value>, Args: *const &'a Value, NumArgs: c_uint, Name: *const c_char, ) -> Option<&'a Value>; - pub fn LLVMBuildCleanupRet<'a>( + pub(crate) fn LLVMBuildCleanupRet<'a>( B: &Builder<'a>, CleanupPad: &'a Value, BB: Option<&'a BasicBlock>, ) -> Option<&'a Value>; - pub fn LLVMBuildCatchPad<'a>( + pub(crate) fn LLVMBuildCatchPad<'a>( B: &Builder<'a>, ParentPad: &'a Value, Args: *const &'a Value, NumArgs: c_uint, Name: *const c_char, ) -> Option<&'a Value>; - pub fn LLVMBuildCatchRet<'a>( + pub(crate) fn LLVMBuildCatchRet<'a>( B: &Builder<'a>, CatchPad: &'a Value, BB: &'a BasicBlock, ) -> Option<&'a Value>; - pub fn LLVMBuildCatchSwitch<'a>( + pub(crate) fn LLVMBuildCatchSwitch<'a>( Builder: &Builder<'a>, ParentPad: Option<&'a Value>, UnwindBB: Option<&'a BasicBlock>, NumHandlers: c_uint, Name: *const c_char, ) -> Option<&'a Value>; - pub fn LLVMAddHandler<'a>(CatchSwitch: &'a Value, Dest: &'a BasicBlock); - pub fn LLVMSetPersonalityFn<'a>(Func: &'a Value, Pers: &'a Value); + pub(crate) fn LLVMAddHandler<'a>(CatchSwitch: &'a Value, Dest: &'a BasicBlock); + pub(crate) fn LLVMSetPersonalityFn<'a>(Func: &'a Value, Pers: &'a Value); // Add a case to the switch instruction - pub fn LLVMAddCase<'a>(Switch: &'a Value, OnVal: &'a Value, Dest: &'a BasicBlock); + pub(crate) fn LLVMAddCase<'a>(Switch: &'a Value, OnVal: &'a Value, Dest: &'a BasicBlock); // Add a clause to the landing pad instruction - pub fn LLVMAddClause<'a>(LandingPad: &'a Value, ClauseVal: &'a Value); + pub(crate) fn LLVMAddClause<'a>(LandingPad: &'a Value, ClauseVal: &'a Value); // Set the cleanup on a landing pad instruction - pub fn LLVMSetCleanup(LandingPad: &Value, Val: Bool); + pub(crate) fn LLVMSetCleanup(LandingPad: &Value, Val: Bool); // Arithmetic - pub fn LLVMBuildAdd<'a>( + pub(crate) fn LLVMBuildAdd<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildFAdd<'a>( + pub(crate) fn LLVMBuildFAdd<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildSub<'a>( + pub(crate) fn LLVMBuildSub<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildFSub<'a>( + pub(crate) fn LLVMBuildFSub<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildMul<'a>( + pub(crate) fn LLVMBuildMul<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildFMul<'a>( + pub(crate) fn LLVMBuildFMul<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildUDiv<'a>( + pub(crate) fn LLVMBuildUDiv<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildExactUDiv<'a>( + pub(crate) fn LLVMBuildExactUDiv<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildSDiv<'a>( + pub(crate) fn LLVMBuildSDiv<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildExactSDiv<'a>( + pub(crate) fn LLVMBuildExactSDiv<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildFDiv<'a>( + pub(crate) fn LLVMBuildFDiv<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildURem<'a>( + pub(crate) fn LLVMBuildURem<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildSRem<'a>( + pub(crate) fn LLVMBuildSRem<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildFRem<'a>( + pub(crate) fn LLVMBuildFRem<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildShl<'a>( + pub(crate) fn LLVMBuildShl<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildLShr<'a>( + pub(crate) fn LLVMBuildLShr<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildAShr<'a>( + pub(crate) fn LLVMBuildAShr<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildNSWAdd<'a>( + pub(crate) fn LLVMBuildNSWAdd<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildNUWAdd<'a>( + pub(crate) fn LLVMBuildNUWAdd<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildNSWSub<'a>( + pub(crate) fn LLVMBuildNSWSub<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildNUWSub<'a>( + pub(crate) fn LLVMBuildNUWSub<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildNSWMul<'a>( + pub(crate) fn LLVMBuildNSWMul<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildNUWMul<'a>( + pub(crate) fn LLVMBuildNUWMul<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildAnd<'a>( + pub(crate) fn LLVMBuildAnd<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildOr<'a>( + pub(crate) fn LLVMBuildOr<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildXor<'a>( + pub(crate) fn LLVMBuildXor<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildNeg<'a>(B: &Builder<'a>, V: &'a Value, Name: *const c_char) -> &'a Value; - pub fn LLVMBuildFNeg<'a>(B: &Builder<'a>, V: &'a Value, Name: *const c_char) -> &'a Value; - pub fn LLVMBuildNot<'a>(B: &Builder<'a>, V: &'a Value, Name: *const c_char) -> &'a Value; + pub(crate) fn LLVMBuildNeg<'a>(B: &Builder<'a>, V: &'a Value, Name: *const c_char) + -> &'a Value; + pub(crate) fn LLVMBuildFNeg<'a>( + B: &Builder<'a>, + V: &'a Value, + Name: *const c_char, + ) -> &'a Value; + pub(crate) fn LLVMBuildNot<'a>(B: &Builder<'a>, V: &'a Value, Name: *const c_char) + -> &'a Value; + + // Extra flags on arithmetic + pub(crate) fn LLVMSetIsDisjoint(Instr: &Value, IsDisjoint: Bool); + pub(crate) fn LLVMSetNUW(ArithInst: &Value, HasNUW: Bool); + pub(crate) fn LLVMSetNSW(ArithInst: &Value, HasNSW: Bool); // Memory - pub fn LLVMBuildAlloca<'a>(B: &Builder<'a>, Ty: &'a Type, Name: *const c_char) -> &'a Value; - pub fn LLVMBuildArrayAlloca<'a>( + pub(crate) fn LLVMBuildAlloca<'a>( + B: &Builder<'a>, + Ty: &'a Type, + Name: *const c_char, + ) -> &'a Value; + pub(crate) fn LLVMBuildArrayAlloca<'a>( B: &Builder<'a>, Ty: &'a Type, Val: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildLoad2<'a>( + pub(crate) fn LLVMBuildLoad2<'a>( B: &Builder<'a>, Ty: &'a Type, PointerVal: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildStore<'a>(B: &Builder<'a>, Val: &'a Value, Ptr: &'a Value) -> &'a Value; + pub(crate) fn LLVMBuildStore<'a>(B: &Builder<'a>, Val: &'a Value, Ptr: &'a Value) -> &'a Value; - pub fn LLVMBuildGEP2<'a>( + pub(crate) fn LLVMBuildGEP2<'a>( B: &Builder<'a>, Ty: &'a Type, Pointer: &'a Value, @@ -1312,7 +1461,7 @@ unsafe extern "C" { NumIndices: c_uint, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildInBoundsGEP2<'a>( + pub(crate) fn LLVMBuildInBoundsGEP2<'a>( B: &Builder<'a>, Ty: &'a Type, Pointer: &'a Value, @@ -1322,85 +1471,85 @@ unsafe extern "C" { ) -> &'a Value; // Casts - pub fn LLVMBuildTrunc<'a>( + pub(crate) fn LLVMBuildTrunc<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildZExt<'a>( + pub(crate) fn LLVMBuildZExt<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildSExt<'a>( + pub(crate) fn LLVMBuildSExt<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildFPToUI<'a>( + pub(crate) fn LLVMBuildFPToUI<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildFPToSI<'a>( + pub(crate) fn LLVMBuildFPToSI<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildUIToFP<'a>( + pub(crate) fn LLVMBuildUIToFP<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildSIToFP<'a>( + pub(crate) fn LLVMBuildSIToFP<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildFPTrunc<'a>( + pub(crate) fn LLVMBuildFPTrunc<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildFPExt<'a>( + pub(crate) fn LLVMBuildFPExt<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildPtrToInt<'a>( + pub(crate) fn LLVMBuildPtrToInt<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildIntToPtr<'a>( + pub(crate) fn LLVMBuildIntToPtr<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildBitCast<'a>( + pub(crate) fn LLVMBuildBitCast<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildPointerCast<'a>( + pub(crate) fn LLVMBuildPointerCast<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildIntCast2<'a>( + pub(crate) fn LLVMBuildIntCast2<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, @@ -1409,14 +1558,14 @@ unsafe extern "C" { ) -> &'a Value; // Comparisons - pub fn LLVMBuildICmp<'a>( + pub(crate) fn LLVMBuildICmp<'a>( B: &Builder<'a>, Op: c_uint, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildFCmp<'a>( + pub(crate) fn LLVMBuildFCmp<'a>( B: &Builder<'a>, Op: c_uint, LHS: &'a Value, @@ -1425,47 +1574,48 @@ unsafe extern "C" { ) -> &'a Value; // Miscellaneous instructions - pub fn LLVMBuildPhi<'a>(B: &Builder<'a>, Ty: &'a Type, Name: *const c_char) -> &'a Value; - pub fn LLVMBuildSelect<'a>( + pub(crate) fn LLVMBuildPhi<'a>(B: &Builder<'a>, Ty: &'a Type, Name: *const c_char) + -> &'a Value; + pub(crate) fn LLVMBuildSelect<'a>( B: &Builder<'a>, If: &'a Value, Then: &'a Value, Else: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildVAArg<'a>( + pub(crate) fn LLVMBuildVAArg<'a>( B: &Builder<'a>, list: &'a Value, Ty: &'a Type, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildExtractElement<'a>( + pub(crate) fn LLVMBuildExtractElement<'a>( B: &Builder<'a>, VecVal: &'a Value, Index: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildInsertElement<'a>( + pub(crate) fn LLVMBuildInsertElement<'a>( B: &Builder<'a>, VecVal: &'a Value, EltVal: &'a Value, Index: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildShuffleVector<'a>( + pub(crate) fn LLVMBuildShuffleVector<'a>( B: &Builder<'a>, V1: &'a Value, V2: &'a Value, Mask: &'a Value, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildExtractValue<'a>( + pub(crate) fn LLVMBuildExtractValue<'a>( B: &Builder<'a>, AggVal: &'a Value, Index: c_uint, Name: *const c_char, ) -> &'a Value; - pub fn LLVMBuildInsertValue<'a>( + pub(crate) fn LLVMBuildInsertValue<'a>( B: &Builder<'a>, AggVal: &'a Value, EltVal: &'a Value, @@ -1474,7 +1624,7 @@ unsafe extern "C" { ) -> &'a Value; // Atomic Operations - pub fn LLVMBuildAtomicCmpXchg<'a>( + pub(crate) fn LLVMBuildAtomicCmpXchg<'a>( B: &Builder<'a>, LHS: &'a Value, CMP: &'a Value, @@ -1484,9 +1634,9 @@ unsafe extern "C" { SingleThreaded: Bool, ) -> &'a Value; - pub fn LLVMSetWeak(CmpXchgInst: &Value, IsWeak: Bool); + pub(crate) fn LLVMSetWeak(CmpXchgInst: &Value, IsWeak: Bool); - pub fn LLVMBuildAtomicRMW<'a>( + pub(crate) fn LLVMBuildAtomicRMW<'a>( B: &Builder<'a>, Op: AtomicRmwBinOp, LHS: &'a Value, @@ -1495,7 +1645,7 @@ unsafe extern "C" { SingleThreaded: Bool, ) -> &'a Value; - pub fn LLVMBuildFence<'a>( + pub(crate) fn LLVMBuildFence<'a>( B: &Builder<'a>, Order: AtomicOrdering, SingleThreaded: Bool, @@ -1503,36 +1653,36 @@ unsafe extern "C" { ) -> &'a Value; /// Writes a module to the specified path. Returns 0 on success. - pub fn LLVMWriteBitcodeToFile(M: &Module, Path: *const c_char) -> c_int; + pub(crate) fn LLVMWriteBitcodeToFile(M: &Module, Path: *const c_char) -> c_int; /// Creates a legacy pass manager -- only used for final codegen. - pub fn LLVMCreatePassManager<'a>() -> &'a mut PassManager<'a>; + pub(crate) fn LLVMCreatePassManager<'a>() -> &'a mut PassManager<'a>; - pub fn LLVMAddAnalysisPasses<'a>(T: &'a TargetMachine, PM: &PassManager<'a>); + pub(crate) fn LLVMAddAnalysisPasses<'a>(T: &'a TargetMachine, PM: &PassManager<'a>); - pub fn LLVMGetHostCPUFeatures() -> *mut c_char; + pub(crate) fn LLVMGetHostCPUFeatures() -> *mut c_char; - pub fn LLVMDisposeMessage(message: *mut c_char); + pub(crate) fn LLVMDisposeMessage(message: *mut c_char); - pub fn LLVMIsMultithreaded() -> Bool; + pub(crate) fn LLVMIsMultithreaded() -> Bool; - pub fn LLVMStructCreateNamed(C: &Context, Name: *const c_char) -> &Type; + pub(crate) fn LLVMStructCreateNamed(C: &Context, Name: *const c_char) -> &Type; - pub fn LLVMStructSetBody<'a>( + pub(crate) fn LLVMStructSetBody<'a>( StructTy: &'a Type, ElementTypes: *const &'a Type, ElementCount: c_uint, Packed: Bool, ); - pub fn LLVMMetadataAsValue<'a>(C: &'a Context, MD: &'a Metadata) -> &'a Value; + pub(crate) fn LLVMMetadataAsValue<'a>(C: &'a Context, MD: &'a Metadata) -> &'a Value; - pub fn LLVMSetUnnamedAddress(Global: &Value, UnnamedAddr: UnnamedAddr); + pub(crate) fn LLVMSetUnnamedAddress(Global: &Value, UnnamedAddr: UnnamedAddr); - pub fn LLVMIsAConstantInt(value_ref: &Value) -> Option<&ConstantInt>; + pub(crate) fn LLVMIsAConstantInt(value_ref: &Value) -> Option<&ConstantInt>; - pub fn LLVMGetOrInsertComdat(M: &Module, Name: *const c_char) -> &Comdat; - pub fn LLVMSetComdat(V: &Value, C: &Comdat); + pub(crate) fn LLVMGetOrInsertComdat(M: &Module, Name: *const c_char) -> &Comdat; + pub(crate) fn LLVMSetComdat(V: &Value, C: &Comdat); pub(crate) fn LLVMCreateOperandBundle( Tag: *const c_char, @@ -1579,24 +1729,72 @@ unsafe extern "C" { ) -> &'a Value; } +// FFI bindings for `DIBuilder` functions in the LLVM-C API. +// Try to keep these in the same order as in `llvm/include/llvm-c/DebugInfo.h`. +// +// FIXME(#134001): Audit all `Option` parameters, especially in lists, to check +// that they really are nullable on the C/C++ side. LLVM doesn't appear to +// actually document which ones are nullable. +unsafe extern "C" { + pub(crate) fn LLVMCreateDIBuilder<'ll>(M: &'ll Module) -> *mut DIBuilder<'ll>; + pub(crate) fn LLVMDisposeDIBuilder<'ll>(Builder: ptr::NonNull>); + + pub(crate) fn LLVMDIBuilderFinalize<'ll>(Builder: &DIBuilder<'ll>); + + pub(crate) fn LLVMDIBuilderCreateNameSpace<'ll>( + Builder: &DIBuilder<'ll>, + ParentScope: Option<&'ll Metadata>, + Name: *const c_uchar, + NameLen: size_t, + ExportSymbols: llvm::Bool, + ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateLexicalBlock<'ll>( + Builder: &DIBuilder<'ll>, + Scope: &'ll Metadata, + File: &'ll Metadata, + Line: c_uint, + Column: c_uint, + ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateLexicalBlockFile<'ll>( + Builder: &DIBuilder<'ll>, + Scope: &'ll Metadata, + File: &'ll Metadata, + Discriminator: c_uint, // (optional "DWARF path discriminator"; default is 0) + ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateDebugLocation<'ll>( + Ctx: &'ll Context, + Line: c_uint, + Column: c_uint, + Scope: &'ll Metadata, + InlinedAt: Option<&'ll Metadata>, + ) -> &'ll Metadata; +} + #[link(name = "llvm-wrapper", kind = "static")] unsafe extern "C" { - pub fn LLVMRustInstallErrorHandlers(); - pub fn LLVMRustDisableSystemDialogsOnCrash(); + pub(crate) fn LLVMRustInstallErrorHandlers(); + pub(crate) fn LLVMRustDisableSystemDialogsOnCrash(); // Create and destroy contexts. - pub fn LLVMRustContextCreate(shouldDiscardNames: bool) -> &'static mut Context; + pub(crate) fn LLVMRustContextCreate(shouldDiscardNames: bool) -> &'static mut Context; /// See llvm::LLVMTypeKind::getTypeID. - pub fn LLVMRustGetTypeKind(Ty: &Type) -> TypeKind; + pub(crate) fn LLVMRustGetTypeKind(Ty: &Type) -> TypeKind; // Operations on all values - pub fn LLVMRustGlobalAddMetadata<'a>(Val: &'a Value, KindID: c_uint, Metadata: &'a Metadata); - pub fn LLVMRustIsNonGVFunctionPointerTy(Val: &Value) -> bool; + pub(crate) fn LLVMRustGlobalAddMetadata<'a>( + Val: &'a Value, + KindID: c_uint, + Metadata: &'a Metadata, + ); + pub(crate) fn LLVMRustIsNonGVFunctionPointerTy(Val: &Value) -> bool; // Operations on scalar constants - pub fn LLVMRustConstIntGetZExtValue(ConstantVal: &ConstantInt, Value: &mut u64) -> bool; - pub fn LLVMRustConstInt128Get( + pub(crate) fn LLVMRustConstIntGetZExtValue(ConstantVal: &ConstantInt, Value: &mut u64) -> bool; + pub(crate) fn LLVMRustConstInt128Get( ConstantVal: &ConstantInt, SExt: bool, high: &mut u64, @@ -1604,36 +1802,38 @@ unsafe extern "C" { ) -> bool; // Operations on global variables, functions, and aliases (globals) - pub fn LLVMRustSetDSOLocal(Global: &Value, is_dso_local: bool); + pub(crate) fn LLVMRustSetDSOLocal(Global: &Value, is_dso_local: bool); // Operations on global variables - pub fn LLVMRustGetOrInsertGlobal<'a>( + pub(crate) fn LLVMRustGetOrInsertGlobal<'a>( M: &'a Module, Name: *const c_char, NameLen: size_t, T: &'a Type, ) -> &'a Value; - pub fn LLVMRustInsertPrivateGlobal<'a>(M: &'a Module, T: &'a Type) -> &'a Value; - pub fn LLVMRustGetNamedValue( + pub(crate) fn LLVMRustInsertPrivateGlobal<'a>(M: &'a Module, T: &'a Type) -> &'a Value; + pub(crate) fn LLVMRustGetNamedValue( M: &Module, Name: *const c_char, NameLen: size_t, ) -> Option<&Value>; - pub fn LLVMRustSetTailCallKind(CallInst: &Value, TKC: TailCallKind); // Operations on attributes - pub fn LLVMRustCreateAttrNoValue(C: &Context, attr: AttributeKind) -> &Attribute; - pub fn LLVMRustCreateAlignmentAttr(C: &Context, bytes: u64) -> &Attribute; - pub fn LLVMRustCreateDereferenceableAttr(C: &Context, bytes: u64) -> &Attribute; - pub fn LLVMRustCreateDereferenceableOrNullAttr(C: &Context, bytes: u64) -> &Attribute; - pub fn LLVMRustCreateByValAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; - pub fn LLVMRustCreateStructRetAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; - pub fn LLVMRustCreateElementTypeAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; - pub fn LLVMRustCreateUWTableAttr(C: &Context, async_: bool) -> &Attribute; - pub fn LLVMRustCreateAllocSizeAttr(C: &Context, size_arg: u32) -> &Attribute; - pub fn LLVMRustCreateAllocKindAttr(C: &Context, size_arg: u64) -> &Attribute; - pub fn LLVMRustCreateMemoryEffectsAttr(C: &Context, effects: MemoryEffects) -> &Attribute; - pub fn LLVMRustCreateRangeAttribute( + pub(crate) fn LLVMRustCreateAttrNoValue(C: &Context, attr: AttributeKind) -> &Attribute; + pub(crate) fn LLVMRustCreateAlignmentAttr(C: &Context, bytes: u64) -> &Attribute; + pub(crate) fn LLVMRustCreateDereferenceableAttr(C: &Context, bytes: u64) -> &Attribute; + pub(crate) fn LLVMRustCreateDereferenceableOrNullAttr(C: &Context, bytes: u64) -> &Attribute; + pub(crate) fn LLVMRustCreateByValAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; + pub(crate) fn LLVMRustCreateStructRetAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; + pub(crate) fn LLVMRustCreateElementTypeAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; + pub(crate) fn LLVMRustCreateUWTableAttr(C: &Context, async_: bool) -> &Attribute; + pub(crate) fn LLVMRustCreateAllocSizeAttr(C: &Context, size_arg: u32) -> &Attribute; + pub(crate) fn LLVMRustCreateAllocKindAttr(C: &Context, size_arg: u64) -> &Attribute; + pub(crate) fn LLVMRustCreateMemoryEffectsAttr( + C: &Context, + effects: MemoryEffects, + ) -> &Attribute; + pub(crate) fn LLVMRustCreateRangeAttribute( C: &Context, num_bits: c_uint, lower_words: *const u64, @@ -1641,13 +1841,13 @@ unsafe extern "C" { ) -> &Attribute; // Operations on functions - pub fn LLVMRustGetOrInsertFunction<'a>( + pub(crate) fn LLVMRustGetOrInsertFunction<'a>( M: &'a Module, Name: *const c_char, NameLen: size_t, FunctionTy: &'a Type, ) -> &'a Value; - pub fn LLVMRustAddFunctionAttributes<'a>( + pub(crate) fn LLVMRustAddFunctionAttributes<'a>( Fn: &'a Value, index: c_uint, Attrs: *const &'a Attribute, @@ -1655,19 +1855,19 @@ unsafe extern "C" { ); // Operations on call sites - pub fn LLVMRustAddCallSiteAttributes<'a>( + pub(crate) fn LLVMRustAddCallSiteAttributes<'a>( Instr: &'a Value, index: c_uint, Attrs: *const &'a Attribute, AttrsLen: size_t, ); - pub fn LLVMRustSetFastMath(Instr: &Value); - pub fn LLVMRustSetAlgebraicMath(Instr: &Value); - pub fn LLVMRustSetAllowReassoc(Instr: &Value); + pub(crate) fn LLVMRustSetFastMath(Instr: &Value); + pub(crate) fn LLVMRustSetAlgebraicMath(Instr: &Value); + pub(crate) fn LLVMRustSetAllowReassoc(Instr: &Value); // Miscellaneous instructions - pub fn LLVMRustBuildMemCpy<'a>( + pub(crate) fn LLVMRustBuildMemCpy<'a>( B: &Builder<'a>, Dst: &'a Value, DstAlign: c_uint, @@ -1676,7 +1876,7 @@ unsafe extern "C" { Size: &'a Value, IsVolatile: bool, ) -> &'a Value; - pub fn LLVMRustBuildMemMove<'a>( + pub(crate) fn LLVMRustBuildMemMove<'a>( B: &Builder<'a>, Dst: &'a Value, DstAlign: c_uint, @@ -1685,7 +1885,7 @@ unsafe extern "C" { Size: &'a Value, IsVolatile: bool, ) -> &'a Value; - pub fn LLVMRustBuildMemSet<'a>( + pub(crate) fn LLVMRustBuildMemSet<'a>( B: &Builder<'a>, Dst: &'a Value, DstAlign: c_uint, @@ -1694,47 +1894,55 @@ unsafe extern "C" { IsVolatile: bool, ) -> &'a Value; - pub fn LLVMRustBuildVectorReduceFAdd<'a>( + pub(crate) fn LLVMRustBuildVectorReduceFAdd<'a>( B: &Builder<'a>, Acc: &'a Value, Src: &'a Value, ) -> &'a Value; - pub fn LLVMRustBuildVectorReduceFMul<'a>( + pub(crate) fn LLVMRustBuildVectorReduceFMul<'a>( B: &Builder<'a>, Acc: &'a Value, Src: &'a Value, ) -> &'a Value; - pub fn LLVMRustBuildVectorReduceAdd<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; - pub fn LLVMRustBuildVectorReduceMul<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; - pub fn LLVMRustBuildVectorReduceAnd<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; - pub fn LLVMRustBuildVectorReduceOr<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; - pub fn LLVMRustBuildVectorReduceXor<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; - pub fn LLVMRustBuildVectorReduceMin<'a>( + pub(crate) fn LLVMRustBuildVectorReduceAdd<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; + pub(crate) fn LLVMRustBuildVectorReduceMul<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; + pub(crate) fn LLVMRustBuildVectorReduceAnd<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; + pub(crate) fn LLVMRustBuildVectorReduceOr<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; + pub(crate) fn LLVMRustBuildVectorReduceXor<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; + pub(crate) fn LLVMRustBuildVectorReduceMin<'a>( B: &Builder<'a>, Src: &'a Value, IsSigned: bool, ) -> &'a Value; - pub fn LLVMRustBuildVectorReduceMax<'a>( + pub(crate) fn LLVMRustBuildVectorReduceMax<'a>( B: &Builder<'a>, Src: &'a Value, IsSigned: bool, ) -> &'a Value; - pub fn LLVMRustBuildVectorReduceFMin<'a>( + pub(crate) fn LLVMRustBuildVectorReduceFMin<'a>( B: &Builder<'a>, Src: &'a Value, IsNaN: bool, ) -> &'a Value; - pub fn LLVMRustBuildVectorReduceFMax<'a>( + pub(crate) fn LLVMRustBuildVectorReduceFMax<'a>( B: &Builder<'a>, Src: &'a Value, IsNaN: bool, ) -> &'a Value; - pub fn LLVMRustBuildMinNum<'a>(B: &Builder<'a>, LHS: &'a Value, LHS: &'a Value) -> &'a Value; - pub fn LLVMRustBuildMaxNum<'a>(B: &Builder<'a>, LHS: &'a Value, LHS: &'a Value) -> &'a Value; + pub(crate) fn LLVMRustBuildMinNum<'a>( + B: &Builder<'a>, + LHS: &'a Value, + LHS: &'a Value, + ) -> &'a Value; + pub(crate) fn LLVMRustBuildMaxNum<'a>( + B: &Builder<'a>, + LHS: &'a Value, + LHS: &'a Value, + ) -> &'a Value; // Atomic Operations - pub fn LLVMRustBuildAtomicLoad<'a>( + pub(crate) fn LLVMRustBuildAtomicLoad<'a>( B: &Builder<'a>, ElementType: &'a Type, PointerVal: &'a Value, @@ -1742,21 +1950,21 @@ unsafe extern "C" { Order: AtomicOrdering, ) -> &'a Value; - pub fn LLVMRustBuildAtomicStore<'a>( + pub(crate) fn LLVMRustBuildAtomicStore<'a>( B: &Builder<'a>, Val: &'a Value, Ptr: &'a Value, Order: AtomicOrdering, ) -> &'a Value; - pub fn LLVMRustTimeTraceProfilerInitialize(); + pub(crate) fn LLVMRustTimeTraceProfilerInitialize(); - pub fn LLVMRustTimeTraceProfilerFinishThread(); + pub(crate) fn LLVMRustTimeTraceProfilerFinishThread(); - pub fn LLVMRustTimeTraceProfilerFinish(FileName: *const c_char); + pub(crate) fn LLVMRustTimeTraceProfilerFinish(FileName: *const c_char); /// Returns a string describing the last error caused by an LLVMRust* call. - pub fn LLVMRustGetLastError() -> *const c_char; + pub(crate) fn LLVMRustGetLastError() -> *const c_char; /// Prints the timing information collected by `-Ztime-llvm-passes`. pub(crate) fn LLVMRustPrintPassTimings(OutStr: &RustString); @@ -1765,7 +1973,7 @@ unsafe extern "C" { pub(crate) fn LLVMRustPrintStatistics(OutStr: &RustString); /// Prepares inline assembly. - pub fn LLVMRustInlineAsm( + pub(crate) fn LLVMRustInlineAsm( Ty: &Type, AsmString: *const c_char, AsmStringLen: size_t, @@ -1776,7 +1984,7 @@ unsafe extern "C" { Dialect: AsmDialect, CanThrow: Bool, ) -> &Value; - pub fn LLVMRustInlineAsmVerify( + pub(crate) fn LLVMRustInlineAsmVerify( Ty: &Type, Constraints: *const c_char, ConstraintsLen: size_t, @@ -1820,16 +2028,16 @@ unsafe extern "C" { pub(crate) fn LLVMRustCoverageWriteCovmapVarNameToString(OutStr: &RustString); pub(crate) fn LLVMRustCoverageMappingVersion() -> u32; - pub fn LLVMRustDebugMetadataVersion() -> u32; - pub fn LLVMRustVersionMajor() -> u32; - pub fn LLVMRustVersionMinor() -> u32; - pub fn LLVMRustVersionPatch() -> u32; + pub(crate) fn LLVMRustDebugMetadataVersion() -> u32; + pub(crate) fn LLVMRustVersionMajor() -> u32; + pub(crate) fn LLVMRustVersionMinor() -> u32; + pub(crate) fn LLVMRustVersionPatch() -> u32; /// Add LLVM module flags. /// /// In order for Rust-C LTO to work, module flags must be compatible with Clang. What /// "compatible" means depends on the merge behaviors involved. - pub fn LLVMRustAddModuleFlagU32( + pub(crate) fn LLVMRustAddModuleFlagU32( M: &Module, MergeBehavior: ModuleFlagMergeBehavior, Name: *const c_char, @@ -1837,7 +2045,7 @@ unsafe extern "C" { Value: u32, ); - pub fn LLVMRustAddModuleFlagString( + pub(crate) fn LLVMRustAddModuleFlagString( M: &Module, MergeBehavior: ModuleFlagMergeBehavior, Name: *const c_char, @@ -1846,13 +2054,7 @@ unsafe extern "C" { ValueLen: size_t, ); - pub fn LLVMRustDIBuilderCreate(M: &Module) -> &mut DIBuilder<'_>; - - pub fn LLVMRustDIBuilderDispose<'a>(Builder: &'a mut DIBuilder<'a>); - - pub fn LLVMRustDIBuilderFinalize(Builder: &DIBuilder<'_>); - - pub fn LLVMRustDIBuilderCreateCompileUnit<'a>( + pub(crate) fn LLVMRustDIBuilderCreateCompileUnit<'a>( Builder: &DIBuilder<'a>, Lang: c_uint, File: &'a DIFile, @@ -1869,7 +2071,7 @@ unsafe extern "C" { DebugNameTableKind: DebugNameTableKind, ) -> &'a DIDescriptor; - pub fn LLVMRustDIBuilderCreateFile<'a>( + pub(crate) fn LLVMRustDIBuilderCreateFile<'a>( Builder: &DIBuilder<'a>, Filename: *const c_char, FilenameLen: size_t, @@ -1882,12 +2084,12 @@ unsafe extern "C" { SourceLen: size_t, ) -> &'a DIFile; - pub fn LLVMRustDIBuilderCreateSubroutineType<'a>( + pub(crate) fn LLVMRustDIBuilderCreateSubroutineType<'a>( Builder: &DIBuilder<'a>, ParameterTypes: &'a DIArray, ) -> &'a DICompositeType; - pub fn LLVMRustDIBuilderCreateFunction<'a>( + pub(crate) fn LLVMRustDIBuilderCreateFunction<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIDescriptor, Name: *const c_char, @@ -1905,7 +2107,7 @@ unsafe extern "C" { Decl: Option<&'a DIDescriptor>, ) -> &'a DISubprogram; - pub fn LLVMRustDIBuilderCreateMethod<'a>( + pub(crate) fn LLVMRustDIBuilderCreateMethod<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIDescriptor, Name: *const c_char, @@ -1920,7 +2122,7 @@ unsafe extern "C" { TParam: &'a DIArray, ) -> &'a DISubprogram; - pub fn LLVMRustDIBuilderCreateBasicType<'a>( + pub(crate) fn LLVMRustDIBuilderCreateBasicType<'a>( Builder: &DIBuilder<'a>, Name: *const c_char, NameLen: size_t, @@ -1928,7 +2130,7 @@ unsafe extern "C" { Encoding: c_uint, ) -> &'a DIBasicType; - pub fn LLVMRustDIBuilderCreateTypedef<'a>( + pub(crate) fn LLVMRustDIBuilderCreateTypedef<'a>( Builder: &DIBuilder<'a>, Type: &'a DIBasicType, Name: *const c_char, @@ -1938,7 +2140,7 @@ unsafe extern "C" { Scope: Option<&'a DIScope>, ) -> &'a DIDerivedType; - pub fn LLVMRustDIBuilderCreatePointerType<'a>( + pub(crate) fn LLVMRustDIBuilderCreatePointerType<'a>( Builder: &DIBuilder<'a>, PointeeTy: &'a DIType, SizeInBits: u64, @@ -1948,7 +2150,7 @@ unsafe extern "C" { NameLen: size_t, ) -> &'a DIDerivedType; - pub fn LLVMRustDIBuilderCreateStructType<'a>( + pub(crate) fn LLVMRustDIBuilderCreateStructType<'a>( Builder: &DIBuilder<'a>, Scope: Option<&'a DIDescriptor>, Name: *const c_char, @@ -1966,7 +2168,7 @@ unsafe extern "C" { UniqueIdLen: size_t, ) -> &'a DICompositeType; - pub fn LLVMRustDIBuilderCreateMemberType<'a>( + pub(crate) fn LLVMRustDIBuilderCreateMemberType<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIDescriptor, Name: *const c_char, @@ -1980,7 +2182,7 @@ unsafe extern "C" { Ty: &'a DIType, ) -> &'a DIDerivedType; - pub fn LLVMRustDIBuilderCreateVariantMemberType<'a>( + pub(crate) fn LLVMRustDIBuilderCreateVariantMemberType<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIScope, Name: *const c_char, @@ -1995,7 +2197,7 @@ unsafe extern "C" { Ty: &'a DIType, ) -> &'a DIType; - pub fn LLVMRustDIBuilderCreateStaticMemberType<'a>( + pub(crate) fn LLVMRustDIBuilderCreateStaticMemberType<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIDescriptor, Name: *const c_char, @@ -2008,21 +2210,13 @@ unsafe extern "C" { AlignInBits: u32, ) -> &'a DIDerivedType; - pub fn LLVMRustDIBuilderCreateLexicalBlock<'a>( - Builder: &DIBuilder<'a>, - Scope: &'a DIScope, - File: &'a DIFile, - Line: c_uint, - Col: c_uint, - ) -> &'a DILexicalBlock; - - pub fn LLVMRustDIBuilderCreateLexicalBlockFile<'a>( + pub(crate) fn LLVMRustDIBuilderCreateQualifiedType<'a>( Builder: &DIBuilder<'a>, - Scope: &'a DIScope, - File: &'a DIFile, - ) -> &'a DILexicalBlock; + Tag: c_uint, + Type: &'a DIType, + ) -> &'a DIDerivedType; - pub fn LLVMRustDIBuilderCreateStaticVariable<'a>( + pub(crate) fn LLVMRustDIBuilderCreateStaticVariable<'a>( Builder: &DIBuilder<'a>, Context: Option<&'a DIScope>, Name: *const c_char, @@ -2038,7 +2232,7 @@ unsafe extern "C" { AlignInBits: u32, ) -> &'a DIGlobalVariableExpression; - pub fn LLVMRustDIBuilderCreateVariable<'a>( + pub(crate) fn LLVMRustDIBuilderCreateVariable<'a>( Builder: &DIBuilder<'a>, Tag: c_uint, Scope: &'a DIDescriptor, @@ -2053,7 +2247,7 @@ unsafe extern "C" { AlignInBits: u32, ) -> &'a DIVariable; - pub fn LLVMRustDIBuilderCreateArrayType<'a>( + pub(crate) fn LLVMRustDIBuilderCreateArrayType<'a>( Builder: &DIBuilder<'a>, Size: u64, AlignInBits: u32, @@ -2061,19 +2255,19 @@ unsafe extern "C" { Subscripts: &'a DIArray, ) -> &'a DIType; - pub fn LLVMRustDIBuilderGetOrCreateSubrange<'a>( + pub(crate) fn LLVMRustDIBuilderGetOrCreateSubrange<'a>( Builder: &DIBuilder<'a>, Lo: i64, Count: i64, ) -> &'a DISubrange; - pub fn LLVMRustDIBuilderGetOrCreateArray<'a>( + pub(crate) fn LLVMRustDIBuilderGetOrCreateArray<'a>( Builder: &DIBuilder<'a>, Ptr: *const Option<&'a DIDescriptor>, Count: c_uint, ) -> &'a DIArray; - pub fn LLVMRustDIBuilderInsertDeclareAtEnd<'a>( + pub(crate) fn LLVMRustDIBuilderInsertDeclareAtEnd<'a>( Builder: &DIBuilder<'a>, Val: &'a Value, VarInfo: &'a DIVariable, @@ -2083,7 +2277,7 @@ unsafe extern "C" { InsertAtEnd: &'a BasicBlock, ); - pub fn LLVMRustDIBuilderCreateEnumerator<'a>( + pub(crate) fn LLVMRustDIBuilderCreateEnumerator<'a>( Builder: &DIBuilder<'a>, Name: *const c_char, NameLen: size_t, @@ -2092,7 +2286,7 @@ unsafe extern "C" { IsUnsigned: bool, ) -> &'a DIEnumerator; - pub fn LLVMRustDIBuilderCreateEnumerationType<'a>( + pub(crate) fn LLVMRustDIBuilderCreateEnumerationType<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIScope, Name: *const c_char, @@ -2106,7 +2300,7 @@ unsafe extern "C" { IsScoped: bool, ) -> &'a DIType; - pub fn LLVMRustDIBuilderCreateUnionType<'a>( + pub(crate) fn LLVMRustDIBuilderCreateUnionType<'a>( Builder: &DIBuilder<'a>, Scope: Option<&'a DIScope>, Name: *const c_char, @@ -2122,7 +2316,7 @@ unsafe extern "C" { UniqueIdLen: size_t, ) -> &'a DIType; - pub fn LLVMRustDIBuilderCreateVariantPart<'a>( + pub(crate) fn LLVMRustDIBuilderCreateVariantPart<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIScope, Name: *const c_char, @@ -2138,7 +2332,7 @@ unsafe extern "C" { UniqueIdLen: size_t, ) -> &'a DIDerivedType; - pub fn LLVMRustDIBuilderCreateTemplateTypeParameter<'a>( + pub(crate) fn LLVMRustDIBuilderCreateTemplateTypeParameter<'a>( Builder: &DIBuilder<'a>, Scope: Option<&'a DIScope>, Name: *const c_char, @@ -2146,54 +2340,37 @@ unsafe extern "C" { Ty: &'a DIType, ) -> &'a DITemplateTypeParameter; - pub fn LLVMRustDIBuilderCreateNameSpace<'a>( - Builder: &DIBuilder<'a>, - Scope: Option<&'a DIScope>, - Name: *const c_char, - NameLen: size_t, - ExportSymbols: bool, - ) -> &'a DINameSpace; - - pub fn LLVMRustDICompositeTypeReplaceArrays<'a>( + pub(crate) fn LLVMRustDICompositeTypeReplaceArrays<'a>( Builder: &DIBuilder<'a>, CompositeType: &'a DIType, Elements: Option<&'a DIArray>, Params: Option<&'a DIArray>, ); - pub fn LLVMRustDIBuilderCreateDebugLocation<'a>( - Line: c_uint, - Column: c_uint, - Scope: &'a DIScope, - InlinedAt: Option<&'a DILocation>, - ) -> &'a DILocation; - pub fn LLVMRustDILocationCloneWithBaseDiscriminator<'a>( + pub(crate) fn LLVMRustDILocationCloneWithBaseDiscriminator<'a>( Location: &'a DILocation, BD: c_uint, ) -> Option<&'a DILocation>; - pub fn LLVMRustDIBuilderCreateOpDeref() -> u64; - pub fn LLVMRustDIBuilderCreateOpPlusUconst() -> u64; - pub fn LLVMRustDIBuilderCreateOpLLVMFragment() -> u64; - pub fn LLVMRustWriteTypeToString(Type: &Type, s: &RustString); - pub fn LLVMRustWriteValueToString(value_ref: &Value, s: &RustString); + pub(crate) fn LLVMRustWriteTypeToString(Type: &Type, s: &RustString); + pub(crate) fn LLVMRustWriteValueToString(value_ref: &Value, s: &RustString); - pub fn LLVMRustHasFeature(T: &TargetMachine, s: *const c_char) -> bool; + pub(crate) fn LLVMRustHasFeature(T: &TargetMachine, s: *const c_char) -> bool; pub(crate) fn LLVMRustPrintTargetCPUs(TM: &TargetMachine, OutStr: &RustString); - pub fn LLVMRustGetTargetFeaturesCount(T: &TargetMachine) -> size_t; - pub fn LLVMRustGetTargetFeature( + pub(crate) fn LLVMRustGetTargetFeaturesCount(T: &TargetMachine) -> size_t; + pub(crate) fn LLVMRustGetTargetFeature( T: &TargetMachine, Index: size_t, Feature: &mut *const c_char, Desc: &mut *const c_char, ); - pub fn LLVMRustGetHostCPUName(LenOut: &mut size_t) -> *const u8; + pub(crate) fn LLVMRustGetHostCPUName(LenOut: &mut size_t) -> *const u8; // This function makes copies of pointed to data, so the data's lifetime may end after this // function returns. - pub fn LLVMRustCreateTargetMachine( + pub(crate) fn LLVMRustCreateTargetMachine( Triple: *const c_char, CPU: *const c_char, Features: *const c_char, @@ -2219,22 +2396,22 @@ unsafe extern "C" { ArgsCstrBuffLen: usize, ) -> *mut TargetMachine; - pub fn LLVMRustDisposeTargetMachine(T: *mut TargetMachine); - pub fn LLVMRustAddLibraryInfo<'a>( + pub(crate) fn LLVMRustDisposeTargetMachine(T: *mut TargetMachine); + pub(crate) fn LLVMRustAddLibraryInfo<'a>( PM: &PassManager<'a>, M: &'a Module, DisableSimplifyLibCalls: bool, ); - pub fn LLVMRustWriteOutputFile<'a>( + pub(crate) fn LLVMRustWriteOutputFile<'a>( T: &'a TargetMachine, - PM: &PassManager<'a>, + PM: *mut PassManager<'a>, M: &'a Module, Output: *const c_char, DwoOutput: *const c_char, FileType: FileType, VerifyIR: bool, ) -> LLVMRustResult; - pub fn LLVMRustOptimize<'a>( + pub(crate) fn LLVMRustOptimize<'a>( M: &'a Module, TM: &'a TargetMachine, OptLevel: PassBuilderOptLevel, @@ -2250,6 +2427,7 @@ unsafe extern "C" { LoopVectorize: bool, DisableSimplifyLibCalls: bool, EmitLifetimeMarkers: bool, + RunEnzyme: bool, SanitizerOptions: Option<&SanitizerOptions>, PGOGenPath: *const c_char, PGOUsePath: *const c_char, @@ -2265,29 +2443,32 @@ unsafe extern "C" { LLVMPlugins: *const c_char, LLVMPluginsLen: size_t, ) -> LLVMRustResult; - pub fn LLVMRustPrintModule( + pub(crate) fn LLVMRustPrintModule( M: &Module, Output: *const c_char, Demangle: extern "C" fn(*const c_char, size_t, *mut c_char, size_t) -> size_t, ) -> LLVMRustResult; - pub fn LLVMRustSetLLVMOptions(Argc: c_int, Argv: *const *const c_char); - pub fn LLVMRustPrintPasses(); - pub fn LLVMRustSetNormalizedTarget(M: &Module, triple: *const c_char); - pub fn LLVMRustRunRestrictionPass(M: &Module, syms: *const *const c_char, len: size_t); - - pub fn LLVMRustOpenArchive(path: *const c_char) -> Option<&'static mut Archive>; - pub fn LLVMRustArchiveIteratorNew(AR: &Archive) -> &mut ArchiveIterator<'_>; - pub fn LLVMRustArchiveIteratorNext<'a>( + pub(crate) fn LLVMRustSetLLVMOptions(Argc: c_int, Argv: *const *const c_char); + pub(crate) fn LLVMRustPrintPasses(); + pub(crate) fn LLVMRustSetNormalizedTarget(M: &Module, triple: *const c_char); + pub(crate) fn LLVMRustRunRestrictionPass(M: &Module, syms: *const *const c_char, len: size_t); + + pub(crate) fn LLVMRustOpenArchive(path: *const c_char) -> Option<&'static mut Archive>; + pub(crate) fn LLVMRustArchiveIteratorNew(AR: &Archive) -> &mut ArchiveIterator<'_>; + pub(crate) fn LLVMRustArchiveIteratorNext<'a>( AIR: &ArchiveIterator<'a>, ) -> Option<&'a mut ArchiveChild<'a>>; - pub fn LLVMRustArchiveChildName(ACR: &ArchiveChild<'_>, size: &mut size_t) -> *const c_char; - pub fn LLVMRustArchiveChildFree<'a>(ACR: &'a mut ArchiveChild<'a>); - pub fn LLVMRustArchiveIteratorFree<'a>(AIR: &'a mut ArchiveIterator<'a>); - pub fn LLVMRustDestroyArchive(AR: &'static mut Archive); + pub(crate) fn LLVMRustArchiveChildName( + ACR: &ArchiveChild<'_>, + size: &mut size_t, + ) -> *const c_char; + pub(crate) fn LLVMRustArchiveChildFree<'a>(ACR: &'a mut ArchiveChild<'a>); + pub(crate) fn LLVMRustArchiveIteratorFree<'a>(AIR: &'a mut ArchiveIterator<'a>); + pub(crate) fn LLVMRustDestroyArchive(AR: &'static mut Archive); - pub fn LLVMRustWriteTwineToString(T: &Twine, s: &RustString); + pub(crate) fn LLVMRustWriteTwineToString(T: &Twine, s: &RustString); - pub fn LLVMRustUnpackOptimizationDiagnostic<'a>( + pub(crate) fn LLVMRustUnpackOptimizationDiagnostic<'a>( DI: &'a DiagnosticInfo, pass_name_out: &RustString, function_out: &mut Option<&'a Value>, @@ -2297,22 +2478,22 @@ unsafe extern "C" { message_out: &RustString, ); - pub fn LLVMRustUnpackInlineAsmDiagnostic<'a>( + pub(crate) fn LLVMRustUnpackInlineAsmDiagnostic<'a>( DI: &'a DiagnosticInfo, level_out: &mut DiagnosticLevel, cookie_out: &mut u64, message_out: &mut Option<&'a Twine>, ); - pub fn LLVMRustWriteDiagnosticInfoToString(DI: &DiagnosticInfo, s: &RustString); - pub fn LLVMRustGetDiagInfoKind(DI: &DiagnosticInfo) -> DiagnosticKind; + pub(crate) fn LLVMRustWriteDiagnosticInfoToString(DI: &DiagnosticInfo, s: &RustString); + pub(crate) fn LLVMRustGetDiagInfoKind(DI: &DiagnosticInfo) -> DiagnosticKind; - pub fn LLVMRustGetSMDiagnostic<'a>( + pub(crate) fn LLVMRustGetSMDiagnostic<'a>( DI: &'a DiagnosticInfo, cookie_out: &mut u64, ) -> &'a SMDiagnostic; - pub fn LLVMRustUnpackSMDiagnostic( + pub(crate) fn LLVMRustUnpackSMDiagnostic( d: &SMDiagnostic, message_out: &RustString, buffer_out: &RustString, @@ -2322,7 +2503,7 @@ unsafe extern "C" { num_ranges: &mut usize, ) -> bool; - pub fn LLVMRustWriteArchive( + pub(crate) fn LLVMRustWriteArchive( Dst: *const c_char, NumMembers: size_t, Members: *const &RustArchiveMember<'_>, @@ -2330,63 +2511,63 @@ unsafe extern "C" { Kind: ArchiveKind, isEC: bool, ) -> LLVMRustResult; - pub fn LLVMRustArchiveMemberNew<'a>( + pub(crate) fn LLVMRustArchiveMemberNew<'a>( Filename: *const c_char, Name: *const c_char, Child: Option<&ArchiveChild<'a>>, ) -> &'a mut RustArchiveMember<'a>; - pub fn LLVMRustArchiveMemberFree<'a>(Member: &'a mut RustArchiveMember<'a>); + pub(crate) fn LLVMRustArchiveMemberFree<'a>(Member: &'a mut RustArchiveMember<'a>); - pub fn LLVMRustSetDataLayoutFromTargetMachine<'a>(M: &'a Module, TM: &'a TargetMachine); + pub(crate) fn LLVMRustSetDataLayoutFromTargetMachine<'a>(M: &'a Module, TM: &'a TargetMachine); - pub fn LLVMRustPositionBuilderAtStart<'a>(B: &Builder<'a>, BB: &'a BasicBlock); + pub(crate) fn LLVMRustPositionBuilderAtStart<'a>(B: &Builder<'a>, BB: &'a BasicBlock); - pub fn LLVMRustSetModulePICLevel(M: &Module); - pub fn LLVMRustSetModulePIELevel(M: &Module); - pub fn LLVMRustSetModuleCodeModel(M: &Module, Model: CodeModel); - pub fn LLVMRustModuleBufferCreate(M: &Module) -> &'static mut ModuleBuffer; - pub fn LLVMRustModuleBufferPtr(p: &ModuleBuffer) -> *const u8; - pub fn LLVMRustModuleBufferLen(p: &ModuleBuffer) -> usize; - pub fn LLVMRustModuleBufferFree(p: &'static mut ModuleBuffer); - pub fn LLVMRustModuleCost(M: &Module) -> u64; - pub fn LLVMRustModuleInstructionStats(M: &Module, Str: &RustString); + pub(crate) fn LLVMRustSetModulePICLevel(M: &Module); + pub(crate) fn LLVMRustSetModulePIELevel(M: &Module); + pub(crate) fn LLVMRustSetModuleCodeModel(M: &Module, Model: CodeModel); + pub(crate) fn LLVMRustModuleBufferCreate(M: &Module) -> &'static mut ModuleBuffer; + pub(crate) fn LLVMRustModuleBufferPtr(p: &ModuleBuffer) -> *const u8; + pub(crate) fn LLVMRustModuleBufferLen(p: &ModuleBuffer) -> usize; + pub(crate) fn LLVMRustModuleBufferFree(p: &'static mut ModuleBuffer); + pub(crate) fn LLVMRustModuleCost(M: &Module) -> u64; + pub(crate) fn LLVMRustModuleInstructionStats(M: &Module, Str: &RustString); - pub fn LLVMRustThinLTOBufferCreate( + pub(crate) fn LLVMRustThinLTOBufferCreate( M: &Module, is_thin: bool, emit_summary: bool, ) -> &'static mut ThinLTOBuffer; - pub fn LLVMRustThinLTOBufferFree(M: &'static mut ThinLTOBuffer); - pub fn LLVMRustThinLTOBufferPtr(M: &ThinLTOBuffer) -> *const c_char; - pub fn LLVMRustThinLTOBufferLen(M: &ThinLTOBuffer) -> size_t; - pub fn LLVMRustThinLTOBufferThinLinkDataPtr(M: &ThinLTOBuffer) -> *const c_char; - pub fn LLVMRustThinLTOBufferThinLinkDataLen(M: &ThinLTOBuffer) -> size_t; - pub fn LLVMRustCreateThinLTOData( + pub(crate) fn LLVMRustThinLTOBufferFree(M: &'static mut ThinLTOBuffer); + pub(crate) fn LLVMRustThinLTOBufferPtr(M: &ThinLTOBuffer) -> *const c_char; + pub(crate) fn LLVMRustThinLTOBufferLen(M: &ThinLTOBuffer) -> size_t; + pub(crate) fn LLVMRustThinLTOBufferThinLinkDataPtr(M: &ThinLTOBuffer) -> *const c_char; + pub(crate) fn LLVMRustThinLTOBufferThinLinkDataLen(M: &ThinLTOBuffer) -> size_t; + pub(crate) fn LLVMRustCreateThinLTOData( Modules: *const ThinLTOModule, NumModules: size_t, PreservedSymbols: *const *const c_char, PreservedSymbolsLen: size_t, ) -> Option<&'static mut ThinLTOData>; - pub fn LLVMRustPrepareThinLTORename( + pub(crate) fn LLVMRustPrepareThinLTORename( Data: &ThinLTOData, Module: &Module, Target: &TargetMachine, - ) -> bool; - pub fn LLVMRustPrepareThinLTOResolveWeak(Data: &ThinLTOData, Module: &Module) -> bool; - pub fn LLVMRustPrepareThinLTOInternalize(Data: &ThinLTOData, Module: &Module) -> bool; - pub fn LLVMRustPrepareThinLTOImport( + ); + pub(crate) fn LLVMRustPrepareThinLTOResolveWeak(Data: &ThinLTOData, Module: &Module) -> bool; + pub(crate) fn LLVMRustPrepareThinLTOInternalize(Data: &ThinLTOData, Module: &Module) -> bool; + pub(crate) fn LLVMRustPrepareThinLTOImport( Data: &ThinLTOData, Module: &Module, Target: &TargetMachine, ) -> bool; - pub fn LLVMRustFreeThinLTOData(Data: &'static mut ThinLTOData); - pub fn LLVMRustParseBitcodeForLTO( + pub(crate) fn LLVMRustFreeThinLTOData(Data: &'static mut ThinLTOData); + pub(crate) fn LLVMRustParseBitcodeForLTO( Context: &Context, Data: *const u8, len: usize, Identifier: *const c_char, ) -> Option<&Module>; - pub fn LLVMRustGetSliceFromObjectDataByName( + pub(crate) fn LLVMRustGetSliceFromObjectDataByName( data: *const u8, len: usize, name: *const u8, @@ -2394,25 +2575,27 @@ unsafe extern "C" { out_len: &mut usize, ) -> *const u8; - pub fn LLVMRustLinkerNew(M: &Module) -> &mut Linker<'_>; - pub fn LLVMRustLinkerAdd( + pub(crate) fn LLVMRustLinkerNew(M: &Module) -> &mut Linker<'_>; + pub(crate) fn LLVMRustLinkerAdd( linker: &Linker<'_>, bytecode: *const c_char, bytecode_len: usize, ) -> bool; - pub fn LLVMRustLinkerFree<'a>(linker: &'a mut Linker<'a>); - pub fn LLVMRustComputeLTOCacheKey( + pub(crate) fn LLVMRustLinkerFree<'a>(linker: &'a mut Linker<'a>); + pub(crate) fn LLVMRustComputeLTOCacheKey( key_out: &RustString, mod_id: *const c_char, data: &ThinLTOData, ); - pub fn LLVMRustContextGetDiagnosticHandler(Context: &Context) -> Option<&DiagnosticHandler>; - pub fn LLVMRustContextSetDiagnosticHandler( + pub(crate) fn LLVMRustContextGetDiagnosticHandler( + Context: &Context, + ) -> Option<&DiagnosticHandler>; + pub(crate) fn LLVMRustContextSetDiagnosticHandler( context: &Context, diagnostic_handler: Option<&DiagnosticHandler>, ); - pub fn LLVMRustContextConfigureDiagnosticHandler( + pub(crate) fn LLVMRustContextConfigureDiagnosticHandler( context: &Context, diagnostic_handler_callback: DiagnosticHandlerTy, diagnostic_handler_context: *mut c_void, @@ -2423,17 +2606,15 @@ unsafe extern "C" { pgo_available: bool, ); - pub fn LLVMRustGetMangledName(V: &Value, out: &RustString); - - pub fn LLVMRustGetElementTypeArgIndex(CallSite: &Value) -> i32; + pub(crate) fn LLVMRustGetMangledName(V: &Value, out: &RustString); - pub fn LLVMRustIsBitcode(ptr: *const u8, len: usize) -> bool; + pub(crate) fn LLVMRustGetElementTypeArgIndex(CallSite: &Value) -> i32; - pub fn LLVMRustLLVMHasZlibCompressionForDebugSymbols() -> bool; + pub(crate) fn LLVMRustLLVMHasZlibCompressionForDebugSymbols() -> bool; - pub fn LLVMRustLLVMHasZstdCompressionForDebugSymbols() -> bool; + pub(crate) fn LLVMRustLLVMHasZstdCompressionForDebugSymbols() -> bool; - pub fn LLVMRustGetSymbols( + pub(crate) fn LLVMRustGetSymbols( buf_ptr: *const u8, buf_len: usize, state: *mut c_void, @@ -2441,10 +2622,10 @@ unsafe extern "C" { error_callback: GetSymbolsErrorCallback, ) -> *mut c_void; - pub fn LLVMRustIs64BitSymbolicFile(buf_ptr: *const u8, buf_len: usize) -> bool; + pub(crate) fn LLVMRustIs64BitSymbolicFile(buf_ptr: *const u8, buf_len: usize) -> bool; - pub fn LLVMRustIsECObject(buf_ptr: *const u8, buf_len: usize) -> bool; + pub(crate) fn LLVMRustIsECObject(buf_ptr: *const u8, buf_len: usize) -> bool; - pub fn LLVMRustSetNoSanitizeAddress(Global: &Value); - pub fn LLVMRustSetNoSanitizeHWAddress(Global: &Value); + pub(crate) fn LLVMRustSetNoSanitizeAddress(Global: &Value); + pub(crate) fn LLVMRustSetNoSanitizeHWAddress(Global: &Value); } diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 2592a7df95c29..efc9cf2ef691b 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -10,13 +10,9 @@ use libc::c_uint; use rustc_abi::{Align, Size, WrappingRange}; use rustc_llvm::RustString; -pub use self::AtomicRmwBinOp::*; pub use self::CallConv::*; pub use self::CodeGenOptSize::*; -pub use self::IntPredicate::*; -pub use self::Linkage::*; pub use self::MetadataType::*; -pub use self::RealPredicate::*; pub use self::ffi::*; use crate::common::AsCCharPtr; @@ -28,7 +24,7 @@ mod ffi; pub use self::enzyme_ffi::*; impl LLVMRustResult { - pub fn into_result(self) -> Result<(), ()> { + pub(crate) fn into_result(self) -> Result<(), ()> { match self { LLVMRustResult::Success => Ok(()), LLVMRustResult::Failure => Err(()), @@ -36,13 +32,17 @@ impl LLVMRustResult { } } -pub fn AddFunctionAttributes<'ll>(llfn: &'ll Value, idx: AttributePlace, attrs: &[&'ll Attribute]) { +pub(crate) fn AddFunctionAttributes<'ll>( + llfn: &'ll Value, + idx: AttributePlace, + attrs: &[&'ll Attribute], +) { unsafe { LLVMRustAddFunctionAttributes(llfn, idx.as_uint(), attrs.as_ptr(), attrs.len()); } } -pub fn AddCallSiteAttributes<'ll>( +pub(crate) fn AddCallSiteAttributes<'ll>( callsite: &'ll Value, idx: AttributePlace, attrs: &[&'ll Attribute], @@ -52,7 +52,11 @@ pub fn AddCallSiteAttributes<'ll>( } } -pub fn CreateAttrStringValue<'ll>(llcx: &'ll Context, attr: &str, value: &str) -> &'ll Attribute { +pub(crate) fn CreateAttrStringValue<'ll>( + llcx: &'ll Context, + attr: &str, + value: &str, +) -> &'ll Attribute { unsafe { LLVMCreateStringAttribute( llcx, @@ -64,7 +68,7 @@ pub fn CreateAttrStringValue<'ll>(llcx: &'ll Context, attr: &str, value: &str) - } } -pub fn CreateAttrString<'ll>(llcx: &'ll Context, attr: &str) -> &'ll Attribute { +pub(crate) fn CreateAttrString<'ll>(llcx: &'ll Context, attr: &str) -> &'ll Attribute { unsafe { LLVMCreateStringAttribute( llcx, @@ -76,39 +80,39 @@ pub fn CreateAttrString<'ll>(llcx: &'ll Context, attr: &str) -> &'ll Attribute { } } -pub fn CreateAlignmentAttr(llcx: &Context, bytes: u64) -> &Attribute { +pub(crate) fn CreateAlignmentAttr(llcx: &Context, bytes: u64) -> &Attribute { unsafe { LLVMRustCreateAlignmentAttr(llcx, bytes) } } -pub fn CreateDereferenceableAttr(llcx: &Context, bytes: u64) -> &Attribute { +pub(crate) fn CreateDereferenceableAttr(llcx: &Context, bytes: u64) -> &Attribute { unsafe { LLVMRustCreateDereferenceableAttr(llcx, bytes) } } -pub fn CreateDereferenceableOrNullAttr(llcx: &Context, bytes: u64) -> &Attribute { +pub(crate) fn CreateDereferenceableOrNullAttr(llcx: &Context, bytes: u64) -> &Attribute { unsafe { LLVMRustCreateDereferenceableOrNullAttr(llcx, bytes) } } -pub fn CreateByValAttr<'ll>(llcx: &'ll Context, ty: &'ll Type) -> &'ll Attribute { +pub(crate) fn CreateByValAttr<'ll>(llcx: &'ll Context, ty: &'ll Type) -> &'ll Attribute { unsafe { LLVMRustCreateByValAttr(llcx, ty) } } -pub fn CreateStructRetAttr<'ll>(llcx: &'ll Context, ty: &'ll Type) -> &'ll Attribute { +pub(crate) fn CreateStructRetAttr<'ll>(llcx: &'ll Context, ty: &'ll Type) -> &'ll Attribute { unsafe { LLVMRustCreateStructRetAttr(llcx, ty) } } -pub fn CreateUWTableAttr(llcx: &Context, async_: bool) -> &Attribute { +pub(crate) fn CreateUWTableAttr(llcx: &Context, async_: bool) -> &Attribute { unsafe { LLVMRustCreateUWTableAttr(llcx, async_) } } -pub fn CreateAllocSizeAttr(llcx: &Context, size_arg: u32) -> &Attribute { +pub(crate) fn CreateAllocSizeAttr(llcx: &Context, size_arg: u32) -> &Attribute { unsafe { LLVMRustCreateAllocSizeAttr(llcx, size_arg) } } -pub fn CreateAllocKindAttr(llcx: &Context, kind_arg: AllocKindFlags) -> &Attribute { +pub(crate) fn CreateAllocKindAttr(llcx: &Context, kind_arg: AllocKindFlags) -> &Attribute { unsafe { LLVMRustCreateAllocKindAttr(llcx, kind_arg.bits()) } } -pub fn CreateRangeAttr(llcx: &Context, size: Size, range: WrappingRange) -> &Attribute { +pub(crate) fn CreateRangeAttr(llcx: &Context, size: Size, range: WrappingRange) -> &Attribute { let lower = range.start; let upper = range.end.wrapping_add(1); let lower_words = [lower as u64, (lower >> 64) as u64]; @@ -131,7 +135,7 @@ pub enum AttributePlace { } impl AttributePlace { - pub fn as_uint(self) -> c_uint { + pub(crate) fn as_uint(self) -> c_uint { match self { AttributePlace::ReturnValue => 0, AttributePlace::Argument(i) => 1 + i, @@ -163,12 +167,12 @@ impl FromStr for ArchiveKind { } } -pub fn SetInstructionCallConv(instr: &Value, cc: CallConv) { +pub(crate) fn SetInstructionCallConv(instr: &Value, cc: CallConv) { unsafe { LLVMSetInstructionCallConv(instr, cc as c_uint); } } -pub fn SetFunctionCallConv(fn_: &Value, cc: CallConv) { +pub(crate) fn SetFunctionCallConv(fn_: &Value, cc: CallConv) { unsafe { LLVMSetFunctionCallConv(fn_, cc as c_uint); } @@ -180,20 +184,20 @@ pub fn SetFunctionCallConv(fn_: &Value, cc: CallConv) { // value's name as the comdat value to make sure that it is in a 1-to-1 relationship to the // function. // For more details on COMDAT sections see e.g., https://www.airs.com/blog/archives/52 -pub fn SetUniqueComdat(llmod: &Module, val: &Value) { +pub(crate) fn SetUniqueComdat(llmod: &Module, val: &Value) { let name_buf = get_value_name(val).to_vec(); let name = CString::from_vec_with_nul(name_buf).or_else(|buf| CString::new(buf.into_bytes())).unwrap(); set_comdat(llmod, val, &name); } -pub fn SetUnnamedAddress(global: &Value, unnamed: UnnamedAddr) { +pub(crate) fn SetUnnamedAddress(global: &Value, unnamed: UnnamedAddr) { unsafe { LLVMSetUnnamedAddress(global, unnamed); } } -pub fn set_thread_local_mode(global: &Value, mode: ThreadLocalMode) { +pub(crate) fn set_thread_local_mode(global: &Value, mode: ThreadLocalMode) { unsafe { LLVMSetThreadLocalMode(global, mode); } @@ -201,61 +205,65 @@ pub fn set_thread_local_mode(global: &Value, mode: ThreadLocalMode) { impl AttributeKind { /// Create an LLVM Attribute with no associated value. - pub fn create_attr(self, llcx: &Context) -> &Attribute { + pub(crate) fn create_attr(self, llcx: &Context) -> &Attribute { unsafe { LLVMRustCreateAttrNoValue(llcx, self) } } } impl MemoryEffects { /// Create an LLVM Attribute with these memory effects. - pub fn create_attr(self, llcx: &Context) -> &Attribute { + pub(crate) fn create_attr(self, llcx: &Context) -> &Attribute { unsafe { LLVMRustCreateMemoryEffectsAttr(llcx, self) } } } -pub fn set_section(llglobal: &Value, section_name: &CStr) { +pub(crate) fn set_section(llglobal: &Value, section_name: &CStr) { unsafe { LLVMSetSection(llglobal, section_name.as_ptr()); } } -pub fn add_global<'a>(llmod: &'a Module, ty: &'a Type, name_cstr: &CStr) -> &'a Value { +pub(crate) fn add_global<'a>(llmod: &'a Module, ty: &'a Type, name_cstr: &CStr) -> &'a Value { unsafe { LLVMAddGlobal(llmod, ty, name_cstr.as_ptr()) } } -pub fn set_initializer(llglobal: &Value, constant_val: &Value) { +pub(crate) fn set_initializer(llglobal: &Value, constant_val: &Value) { unsafe { LLVMSetInitializer(llglobal, constant_val); } } -pub fn set_global_constant(llglobal: &Value, is_constant: bool) { +pub(crate) fn set_global_constant(llglobal: &Value, is_constant: bool) { unsafe { LLVMSetGlobalConstant(llglobal, if is_constant { ffi::True } else { ffi::False }); } } -pub fn get_linkage(llglobal: &Value) -> Linkage { +pub(crate) fn get_linkage(llglobal: &Value) -> Linkage { unsafe { LLVMGetLinkage(llglobal) }.to_rust() } -pub fn set_linkage(llglobal: &Value, linkage: Linkage) { +pub(crate) fn set_linkage(llglobal: &Value, linkage: Linkage) { unsafe { LLVMSetLinkage(llglobal, linkage); } } -pub fn get_visibility(llglobal: &Value) -> Visibility { +pub(crate) fn is_declaration(llglobal: &Value) -> bool { + unsafe { LLVMIsDeclaration(llglobal) == ffi::True } +} + +pub(crate) fn get_visibility(llglobal: &Value) -> Visibility { unsafe { LLVMGetVisibility(llglobal) }.to_rust() } -pub fn set_visibility(llglobal: &Value, visibility: Visibility) { +pub(crate) fn set_visibility(llglobal: &Value, visibility: Visibility) { unsafe { LLVMSetVisibility(llglobal, visibility); } } -pub fn set_alignment(llglobal: &Value, align: Align) { +pub(crate) fn set_alignment(llglobal: &Value, align: Align) { unsafe { ffi::LLVMSetAlignment(llglobal, align.bytes() as c_uint); } @@ -265,7 +273,7 @@ pub fn set_alignment(llglobal: &Value, align: Align) { /// /// Inserts the comdat into `llmod` if it does not exist. /// It is an error to call this if the target does not support comdat. -pub fn set_comdat(llmod: &Module, llglobal: &Value, name: &CStr) { +pub(crate) fn set_comdat(llmod: &Module, llglobal: &Value, name: &CStr) { unsafe { let comdat = LLVMGetOrInsertComdat(llmod, name.as_ptr()); LLVMSetComdat(llglobal, comdat); @@ -273,7 +281,7 @@ pub fn set_comdat(llmod: &Module, llglobal: &Value, name: &CStr) { } /// Safe wrapper around `LLVMGetParam`, because segfaults are no fun. -pub fn get_param(llfn: &Value, index: c_uint) -> &Value { +pub(crate) fn get_param(llfn: &Value, index: c_uint) -> &Value { unsafe { assert!( index < LLVMCountParams(llfn), @@ -286,7 +294,7 @@ pub fn get_param(llfn: &Value, index: c_uint) -> &Value { } /// Safe wrapper for `LLVMGetValueName2` into a byte slice -pub fn get_value_name(value: &Value) -> &[u8] { +pub(crate) fn get_value_name(value: &Value) -> &[u8] { unsafe { let mut len = 0; let data = LLVMGetValueName2(value, &mut len); @@ -295,28 +303,28 @@ pub fn get_value_name(value: &Value) -> &[u8] { } /// Safe wrapper for `LLVMSetValueName2` from a byte slice -pub fn set_value_name(value: &Value, name: &[u8]) { +pub(crate) fn set_value_name(value: &Value, name: &[u8]) { unsafe { let data = name.as_c_char_ptr(); LLVMSetValueName2(value, data, name.len()); } } -pub fn build_string(f: impl FnOnce(&RustString)) -> Result { +pub(crate) fn build_string(f: impl FnOnce(&RustString)) -> Result { String::from_utf8(RustString::build_byte_buffer(f)) } -pub fn build_byte_buffer(f: impl FnOnce(&RustString)) -> Vec { +pub(crate) fn build_byte_buffer(f: impl FnOnce(&RustString)) -> Vec { RustString::build_byte_buffer(f) } -pub fn twine_to_string(tr: &Twine) -> String { +pub(crate) fn twine_to_string(tr: &Twine) -> String { unsafe { build_string(|s| LLVMRustWriteTwineToString(tr, s)).expect("got a non-UTF8 Twine from LLVM") } } -pub fn last_error() -> Option { +pub(crate) fn last_error() -> Option { unsafe { let cstr = LLVMRustGetLastError(); if cstr.is_null() { diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 628c0b1c29c44..b4b5d6a5b1943 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -21,8 +21,8 @@ use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATU use crate::back::write::create_informational_target_machine; use crate::errors::{ - FixedX18InvalidArch, ForbiddenCTargetFeature, InvalidTargetFeaturePrefix, PossibleFeature, - UnknownCTargetFeature, UnknownCTargetFeaturePrefix, UnstableCTargetFeature, + FixedX18InvalidArch, ForbiddenCTargetFeature, PossibleFeature, UnknownCTargetFeature, + UnknownCTargetFeaturePrefix, UnstableCTargetFeature, }; use crate::llvm; @@ -109,7 +109,10 @@ unsafe fn configure_llvm(sess: &Session) { add("-wasm-enable-eh", false); } - if sess.target.os == "emscripten" && sess.panic_strategy() == PanicStrategy::Unwind { + if sess.target.os == "emscripten" + && !sess.opts.unstable_opts.emscripten_wasm_eh + && sess.panic_strategy() == PanicStrategy::Unwind + { add("-enable-emscripten-cxx-exceptions", false); } @@ -268,6 +271,7 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option Some(LLVMFeature::new("fullfp16")), // Filter out features that are not supported by the current LLVM version ("aarch64", "fpmr") if get_version().0 != 18 => None, + ("arm", "fp16") => Some(LLVMFeature::new("fullfp16")), // In LLVM 18, `unaligned-scalar-mem` was merged with `unaligned-vector-mem` into a single // feature called `fast-unaligned-access`. In LLVM 19, it was split back out. ("riscv32" | "riscv64", "unaligned-scalar-mem") if get_version().0 == 18 => { @@ -300,7 +304,7 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option Vec { +pub(crate) fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec { let mut features: FxHashSet = Default::default(); // Add base features for the target. @@ -316,7 +320,6 @@ pub fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec sess.target .rust_target_features() .iter() - .filter(|(_, gate, _)| gate.in_cfg()) .filter(|(feature, _, _)| { // skip checking special features, as LLVM may not understand them if RUSTC_SPECIAL_FEATURES.contains(feature) { @@ -348,7 +351,16 @@ pub fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec { if enabled { // Also add all transitively implied features. - features.extend(sess.target.implied_target_features(std::iter::once(feature))); + + // We don't care about the order in `features` since the only thing we use it for is the + // `features.contains` below. + #[allow(rustc::potential_query_instability)] + features.extend( + sess.target + .implied_target_features(std::iter::once(feature.as_str())) + .iter() + .map(|s| Symbol::intern(s)), + ); } else { // Remove transitively reverse-implied features. @@ -356,7 +368,11 @@ pub fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec // `features.contains` below. #[allow(rustc::potential_query_instability)] features.retain(|f| { - if sess.target.implied_target_features(std::iter::once(*f)).contains(&feature) { + if sess + .target + .implied_target_features(std::iter::once(f.as_str())) + .contains(&feature.as_str()) + { // If `f` if implies `feature`, then `!feature` implies `!f`, so we have to // remove `f`. (This is the standard logical contraposition principle.) false @@ -372,9 +388,13 @@ pub fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec sess.target .rust_target_features() .iter() - .filter(|(_, gate, _)| gate.in_cfg()) .filter_map(|(feature, gate, _)| { - if sess.is_nightly_build() || allow_unstable || gate.requires_nightly().is_none() { + // The `allow_unstable` set is used by rustc internally to determined which target + // features are truly available, so we want to return even perma-unstable "forbidden" + // features. + if allow_unstable + || (gate.in_cfg() && (sess.is_nightly_build() || gate.requires_nightly().is_none())) + { Some(*feature) } else { None @@ -638,7 +658,7 @@ pub(crate) fn global_llvm_features( sess.target .features .split(',') - .filter(|v| !v.is_empty() && backend_feature_name(sess, v).is_some()) + .filter(|v| !v.is_empty()) // Drop +v8plus feature introduced in LLVM 20. .filter(|v| *v != "+v8plus" || get_version() >= (20, 0, 0)) .map(String::from), @@ -651,89 +671,89 @@ pub(crate) fn global_llvm_features( // -Ctarget-features if !only_base_features { let known_features = sess.target.rust_target_features(); + // Will only be filled when `diagnostics` is set! let mut featsmap = FxHashMap::default(); - // insert implied features + // Compute implied features let mut all_rust_features = vec![]; for feature in sess.opts.cg.target_feature.split(',') { - match feature.strip_prefix('+') { - Some(feature) => all_rust_features.extend( - UnordSet::from( - sess.target - .implied_target_features(std::iter::once(Symbol::intern(feature))), - ) - .to_sorted_stable_ord() - .iter() - .map(|s| format!("+{}", s.as_str())), - ), - _ => all_rust_features.push(feature.to_string()), + if let Some(feature) = feature.strip_prefix('+') { + all_rust_features.extend( + UnordSet::from(sess.target.implied_target_features(std::iter::once(feature))) + .to_sorted_stable_ord() + .iter() + .map(|&&s| (true, s)), + ) + } else if let Some(feature) = feature.strip_prefix('-') { + // FIXME: Why do we not remove implied features on "-" here? + // We do the equivalent above in `target_features_cfg`. + // See . + all_rust_features.push((false, feature)); + } else if !feature.is_empty() { + if diagnostics { + sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature }); + } } } + // Remove features that are meant for rustc, not LLVM. + all_rust_features.retain(|(_, feature)| { + // Retain if it is not a rustc feature + !RUSTC_SPECIFIC_FEATURES.contains(feature) + }); - let feats = all_rust_features - .iter() - .filter_map(|s| { - let enable_disable = match s.chars().next() { - None => return None, - Some(c @ ('+' | '-')) => c, - Some(_) => { - if diagnostics { - sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature: s }); - } - return None; - } - }; - - // Get the backend feature name, if any. - // This excludes rustc-specific features, which do not get passed to LLVM. - let feature = backend_feature_name(sess, s)?; - // Warn against use of LLVM specific feature names and unstable features on the CLI. - if diagnostics { - let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature); - match feature_state { - None => { - let rust_feature = - known_features.iter().find_map(|&(rust_feature, _, _)| { - let llvm_features = to_llvm_features(sess, rust_feature)?; - if llvm_features.contains(feature) - && !llvm_features.contains(rust_feature) - { - Some(rust_feature) - } else { - None - } - }); - let unknown_feature = if let Some(rust_feature) = rust_feature { - UnknownCTargetFeature { - feature, - rust_feature: PossibleFeature::Some { rust_feature }, + // Check feature validity. + if diagnostics { + for &(enable, feature) in &all_rust_features { + let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature); + match feature_state { + None => { + let rust_feature = + known_features.iter().find_map(|&(rust_feature, _, _)| { + let llvm_features = to_llvm_features(sess, rust_feature)?; + if llvm_features.contains(feature) + && !llvm_features.contains(rust_feature) + { + Some(rust_feature) + } else { + None } - } else { - UnknownCTargetFeature { - feature, - rust_feature: PossibleFeature::None, - } - }; - sess.dcx().emit_warn(unknown_feature); - } - Some((_, stability, _)) => { - if let Err(reason) = - stability.toggle_allowed(&sess.target, enable_disable == '+') - { - sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason }); - } else if stability.requires_nightly().is_some() { - // An unstable feature. Warn about using it. It makes little sense - // to hard-error here since we just warn about fully unknown - // features above. - sess.dcx().emit_warn(UnstableCTargetFeature { feature }); + }); + let unknown_feature = if let Some(rust_feature) = rust_feature { + UnknownCTargetFeature { + feature, + rust_feature: PossibleFeature::Some { rust_feature }, } + } else { + UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } + }; + sess.dcx().emit_warn(unknown_feature); + } + Some((_, stability, _)) => { + if let Err(reason) = stability.toggle_allowed() { + sess.dcx().emit_warn(ForbiddenCTargetFeature { + feature, + enabled: if enable { "enabled" } else { "disabled" }, + reason, + }); + } else if stability.requires_nightly().is_some() { + // An unstable feature. Warn about using it. It makes little sense + // to hard-error here since we just warn about fully unknown + // features above. + sess.dcx().emit_warn(UnstableCTargetFeature { feature }); } } - - // FIXME(nagisa): figure out how to not allocate a full hashset here. - featsmap.insert(feature, enable_disable == '+'); } + // FIXME(nagisa): figure out how to not allocate a full hashset here. + featsmap.insert(feature, enable); + } + } + + // Translate this into LLVM features. + let feats = all_rust_features + .iter() + .filter_map(|&(enable, feature)| { + let enable_disable = if enable { '+' } else { '-' }; // We run through `to_llvm_features` when // passing requests down to LLVM. This means that all in-language // features also work on the command line instead of having two @@ -746,9 +766,9 @@ pub(crate) fn global_llvm_features( enable_disable, llvm_feature.llvm_feature_name )) .chain(llvm_feature.dependency.into_iter().filter_map( - move |feat| match (enable_disable, feat) { - ('-' | '+', TargetFeatureFoldStrength::Both(f)) - | ('+', TargetFeatureFoldStrength::EnableOnly(f)) => { + move |feat| match (enable, feat) { + (_, TargetFeatureFoldStrength::Both(f)) + | (true, TargetFeatureFoldStrength::EnableOnly(f)) => { Some(format!("{enable_disable}{f}")) } _ => None, @@ -780,22 +800,6 @@ pub(crate) fn global_llvm_features( features } -/// Returns a feature name for the given `+feature` or `-feature` string. -/// -/// Only allows features that are backend specific (i.e. not [`RUSTC_SPECIFIC_FEATURES`].) -fn backend_feature_name<'a>(sess: &Session, s: &'a str) -> Option<&'a str> { - // features must start with a `+` or `-`. - let feature = s - .strip_prefix(&['+', '-'][..]) - .unwrap_or_else(|| sess.dcx().emit_fatal(InvalidTargetFeaturePrefix { feature: s })); - // Rustc-specific feature requests like `+crt-static` or `-crt-static` - // are not passed down to LLVM. - if s.is_empty() || RUSTC_SPECIFIC_FEATURES.contains(&feature) { - return None; - } - Some(feature) -} - pub(crate) fn tune_cpu(sess: &Session) -> Option<&str> { let name = sess.opts.unstable_opts.tune_cpu.as_ref()?; Some(handle_native(name)) diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 33789c6261f10..70edee21bd63a 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -71,10 +71,7 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { // compiler-rt, then we want to implicitly compile everything with hidden // visibility as we're going to link this object all over the place but // don't want the symbols to get exported. - if linkage != Linkage::Internal - && linkage != Linkage::Private - && self.tcx.is_compiler_builtins(LOCAL_CRATE) - { + if linkage != Linkage::Internal && self.tcx.is_compiler_builtins(LOCAL_CRATE) { llvm::set_visibility(lldecl, llvm::Visibility::Hidden); } else { llvm::set_visibility(lldecl, base::visibility_to_llvm(visibility)); diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 6aec078e0de34..d61ce41756270 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -1,17 +1,17 @@ use std::{fmt, ptr}; use libc::{c_char, c_uint}; -use rustc_abi::{AddressSpace, Align, Integer, Size}; +use rustc_abi::{AddressSpace, Align, Integer, Reg, Size}; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::*; use rustc_data_structures::small_c_str::SmallCStr; use rustc_middle::bug; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, Ty}; -use rustc_target::callconv::{CastTarget, FnAbi, Reg}; +use rustc_target::callconv::{CastTarget, FnAbi}; use crate::abi::{FnAbiLlvmExt, LlvmType}; -use crate::context::CodegenCx; +use crate::context::{CodegenCx, SimpleCx}; pub(crate) use crate::llvm::Type; use crate::llvm::{Bool, False, Metadata, True}; use crate::type_of::LayoutLlvmExt; @@ -35,7 +35,8 @@ impl fmt::Debug for Type { } } -impl<'ll> CodegenCx<'ll, '_> { +impl<'ll> CodegenCx<'ll, '_> {} +impl<'ll> SimpleCx<'ll> { pub(crate) fn type_named_struct(&self, name: &str) -> &'ll Type { let name = SmallCStr::new(name); unsafe { llvm::LLVMStructCreateNamed(self.llcx, name.as_ptr()) } @@ -44,11 +45,9 @@ impl<'ll> CodegenCx<'ll, '_> { pub(crate) fn set_struct_body(&self, ty: &'ll Type, els: &[&'ll Type], packed: bool) { unsafe { llvm::LLVMStructSetBody(ty, els.as_ptr(), els.len() as c_uint, packed as Bool) } } - pub(crate) fn type_void(&self) -> &'ll Type { unsafe { llvm::LLVMVoidTypeInContext(self.llcx) } } - pub(crate) fn type_token(&self) -> &'ll Type { unsafe { llvm::LLVMTokenTypeInContext(self.llcx) } } @@ -75,7 +74,8 @@ impl<'ll> CodegenCx<'ll, '_> { args } } - +} +impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { pub(crate) fn type_bool(&self) -> &'ll Type { self.type_i8() } @@ -120,7 +120,8 @@ impl<'ll> CodegenCx<'ll, '_> { assert_eq!(size % unit_size, 0); self.type_array(self.type_from_integer(unit), size / unit_size) } - +} +impl<'ll> SimpleCx<'ll> { pub(crate) fn type_variadic_func(&self, args: &[&'ll Type], ret: &'ll Type) -> &'ll Type { unsafe { llvm::LLVMFunctionType(ret, args.as_ptr(), args.len() as c_uint, True) } } @@ -236,11 +237,11 @@ impl<'ll, 'tcx> BaseTypeCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { impl Type { /// Creates an integer type with the given number of bits, e.g., i24 - pub fn ix_llcx(llcx: &llvm::Context, num_bits: u64) -> &Type { + pub(crate) fn ix_llcx(llcx: &llvm::Context, num_bits: u64) -> &Type { unsafe { llvm::LLVMIntTypeInContext(llcx, num_bits as c_uint) } } - pub fn ptr_llcx(llcx: &llvm::Context) -> &Type { + pub(crate) fn ptr_llcx(llcx: &llvm::Context) -> &Type { unsafe { llvm::LLVMPointerTypeInContext(llcx, AddressSpace::DATA.0) } } } diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index f0456e62442a6..2afc1efc1b34d 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -8,9 +8,10 @@ edition = "2021" ar_archive_writer = "0.4.2" arrayvec = { version = "0.7", default-features = false } bitflags = "2.4.1" +bstr = "1.11.3" # Pinned so `cargo update` bumps don't cause breakage. Please also update the # `cc` in `rustc_llvm` if you update the `cc` here. -cc = "=1.2.6" +cc = "=1.2.13" either = "1.5.0" itertools = "0.12" pathdiff = "0.2.0" @@ -59,5 +60,5 @@ default-features = false features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive", "write", "wasm"] [target.'cfg(windows)'.dependencies.windows] -version = "0.57.0" +version = "0.59.0" features = ["Win32_Globalization"] diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 56188714b44fd..22e262546c3a7 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -16,6 +16,8 @@ codegen_ssa_archive_build_failure = failed to build archive at `{$path}`: {$erro codegen_ssa_atomic_compare_exchange = Atomic compare-exchange intrinsic missing failure memory ordering +codegen_ssa_autodiff_without_lto = using the autodiff feature requires using fat-lto + codegen_ssa_binary_output_to_tty = option `-o` or `--emit` is used to write binary output type `{$shorthand}` to stdout, but stdout is a tty codegen_ssa_cgu_not_recorded = @@ -30,6 +32,8 @@ codegen_ssa_copy_path = could not copy {$from} to {$to}: {$error} codegen_ssa_copy_path_buf = unable to copy {$source_file} to {$output_path}: {$error} +codegen_ssa_cpu_required = target requires explicitly specifying a cpu with `-C target-cpu` + codegen_ssa_create_temp_dir = couldn't create a temp dir: {$error} codegen_ssa_dlltool_fail_import_library = @@ -67,7 +71,7 @@ codegen_ssa_failed_to_write = failed to write {$path}: {$error} codegen_ssa_field_associated_value_expected = associated value expected for `{$name}` codegen_ssa_forbidden_target_feature_attr = - target feature `{$feature}` cannot be toggled with `#[target_feature]`: {$reason} + target feature `{$feature}` cannot be enabled with `#[target_feature]`: {$reason} codegen_ssa_ignoring_emit_path = ignoring emit path because multiple .{$extension} files were produced @@ -183,6 +187,8 @@ codegen_ssa_linker_file_stem = couldn't extract file stem from specified linker codegen_ssa_linker_not_found = linker `{$linker_path}` not found .note = {$error} +codegen_ssa_linker_output = {$inner} + codegen_ssa_linker_unsupported_modifier = `as-needed` modifier not supported for current linker codegen_ssa_linking_failed = linking with `{$linker_path}` failed: {$exit_status} @@ -211,7 +217,7 @@ codegen_ssa_msvc_missing_linker = the msvc targets depend on the msvc linker but codegen_ssa_multiple_external_func_decl = multiple declarations of external function `{$function}` from library `{$library_name}` have different calling conventions codegen_ssa_multiple_main_functions = entry symbol `main` declared multiple times - .help = did you use `#[no_mangle]` on `fn main`? Use `#[start]` instead + .help = did you use `#[no_mangle]` on `fn main`? Use `#![no_main]` to suppress the usual Rust-generated entry point codegen_ssa_no_field = no field `{$name}` diff --git a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs index ab65319e3d3e4..27331ce4ca66f 100644 --- a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs +++ b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs @@ -46,8 +46,12 @@ pub fn assert_module_sources(tcx: TyCtxt<'_>, set_reuse: &dyn Fn(&mut CguReuseTr return; } - let available_cgus = - tcx.collect_and_partition_mono_items(()).1.iter().map(|cgu| cgu.name()).collect(); + let available_cgus = tcx + .collect_and_partition_mono_items(()) + .codegen_units + .iter() + .map(|cgu| cgu.name()) + .collect(); let mut ams = AssertModuleSource { tcx, diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index 58eb137c06879..60af462b6b619 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -414,10 +414,10 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> { let member_path = archive_path.parent().unwrap().join(Path::new(&file_name)); self.entries.push((file_name.into_bytes(), ArchiveEntry::File(member_path))); } else { - self.entries.push((file_name.into_bytes(), ArchiveEntry::FromArchive { - archive_index, - file_range: entry.file_range(), - })); + self.entries.push(( + file_name.into_bytes(), + ArchiveEntry::FromArchive { archive_index, file_range: entry.file_range() }, + )); } } } diff --git a/compiler/rustc_codegen_ssa/src/back/command.rs b/compiler/rustc_codegen_ssa/src/back/command.rs index b3c5b86ccf430..63023fdba20fc 100644 --- a/compiler/rustc_codegen_ssa/src/back/command.rs +++ b/compiler/rustc_codegen_ssa/src/back/command.rs @@ -13,6 +13,7 @@ pub(crate) struct Command { args: Vec, env: Vec<(OsString, OsString)>, env_remove: Vec, + env_clear: bool, } #[derive(Clone)] @@ -36,7 +37,13 @@ impl Command { } fn _new(program: Program) -> Command { - Command { program, args: Vec::new(), env: Vec::new(), env_remove: Vec::new() } + Command { + program, + args: Vec::new(), + env: Vec::new(), + env_remove: Vec::new(), + env_clear: false, + } } pub(crate) fn arg>(&mut self, arg: P) -> &mut Command { @@ -79,6 +86,11 @@ impl Command { self } + pub(crate) fn env_clear(&mut self) -> &mut Command { + self.env_clear = true; + self + } + fn _env_remove(&mut self, key: &OsStr) { self.env_remove.push(key.to_owned()); } @@ -106,6 +118,9 @@ impl Command { for k in &self.env_remove { ret.env_remove(k); } + if self.env_clear { + ret.env_clear(); + } ret } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 4bc064528f3f0..105a4cb81f0d1 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -15,12 +15,14 @@ use rustc_ast::CRATE_NODE_ID; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::temp_dir::MaybeTempDir; -use rustc_errors::DiagCtxtHandle; +use rustc_errors::{DiagCtxtHandle, LintDiagnostic}; use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; +use rustc_macros::LintDiagnostic; use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file}; use rustc_metadata::{find_native_static_library, walk_native_lib_search_dirs}; use rustc_middle::bug; +use rustc_middle::lint::lint_level; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::SymbolExportKind; @@ -29,6 +31,7 @@ use rustc_session::config::{ OutputType, PrintKind, SplitDwarfKind, Strip, }; use rustc_session::cstore::DllImport; +use rustc_session::lint::builtin::LINKER_MESSAGES; use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename}; use rustc_session::search_paths::PathKind; use rustc_session::utils::NativeLibKind; @@ -749,6 +752,14 @@ fn link_dwarf_object(sess: &Session, cg_results: &CodegenResults, executable_out } } +#[derive(LintDiagnostic)] +#[diag(codegen_ssa_linker_output)] +/// Translating this is kind of useless. We don't pass translation flags to the linker, so we'd just +/// end up with inconsistent languages within the same diagnostic. +struct LinkerOutput { + inner: String, +} + /// Create a dynamic library or executable. /// /// This will invoke the system linker/cc to create the resulting file. This links to all upstream @@ -981,6 +992,11 @@ fn link_natively( match prog { Ok(prog) => { + let is_msvc_link_exe = sess.target.is_like_msvc + && flavor == LinkerFlavor::Msvc(Lld::No) + // Match exactly "link.exe" + && linker_path.to_str() == Some("link.exe"); + if !prog.status.success() { let mut output = prog.stderr.clone(); output.extend_from_slice(&prog.stdout); @@ -991,29 +1007,19 @@ fn link_natively( command: cmd, escaped_output, verbose: sess.opts.verbose, + sysroot_dir: sess.sysroot.clone(), }; sess.dcx().emit_err(err); // If MSVC's `link.exe` was expected but the return code // is not a Microsoft LNK error then suggest a way to fix or // install the Visual Studio build tools. if let Some(code) = prog.status.code() { - if sess.target.is_like_msvc - && flavor == LinkerFlavor::Msvc(Lld::No) - // Respect the command line override - && sess.opts.cg.linker.is_none() - // Match exactly "link.exe" - && linker_path.to_str() == Some("link.exe") - // All Microsoft `link.exe` linking error codes are - // four digit numbers in the range 1000 to 9999 inclusive - && (code < 1000 || code > 9999) - { + // All Microsoft `link.exe` linking ror codes are + // four digit numbers in the range 1000 to 9999 inclusive + if is_msvc_link_exe && (code < 1000 || code > 9999) { let is_vs_installed = windows_registry::find_vs_version().is_ok(); - // FIXME(cc-rs#1265) pass only target arch to find_tool() - let has_linker = windows_registry::find_tool( - sess.opts.target_triple.tuple(), - "link.exe", - ) - .is_some(); + let has_linker = + windows_registry::find_tool(&sess.target.arch, "link.exe").is_some(); sess.dcx().emit_note(errors::LinkExeUnexpectedError); if is_vs_installed && has_linker { @@ -1032,8 +1038,49 @@ fn link_natively( sess.dcx().abort_if_errors(); } - info!("linker stderr:\n{}", escape_string(&prog.stderr)); - info!("linker stdout:\n{}", escape_string(&prog.stdout)); + + let stderr = escape_string(&prog.stderr); + let mut stdout = escape_string(&prog.stdout); + info!("linker stderr:\n{}", &stderr); + info!("linker stdout:\n{}", &stdout); + + // Hide some progress messages from link.exe that we don't care about. + // See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146 + if is_msvc_link_exe { + if let Ok(str) = str::from_utf8(&prog.stdout) { + let mut output = String::with_capacity(str.len()); + for line in stdout.lines() { + if line.starts_with(" Creating library") + || line.starts_with("Generating code") + || line.starts_with("Finished generating code") + { + continue; + } + output += line; + output += "\r\n" + } + stdout = escape_string(output.trim().as_bytes()) + } + } + + let (level, src) = codegen_results.crate_info.lint_levels.linker_messages; + let lint = |msg| { + lint_level(sess, LINKER_MESSAGES, level, src, None, |diag| { + LinkerOutput { inner: msg }.decorate_lint(diag) + }) + }; + + if !prog.stderr.is_empty() { + // We already print `warning:` at the start of the diagnostic. Remove it from the linker output if present. + let stderr = stderr + .strip_prefix("warning: ") + .unwrap_or(&stderr) + .replace(": warning: ", ": "); + lint(format!("linker stderr: {stderr}")); + } + if !stdout.is_empty() { + lint(format!("linker stdout: {}", stdout)) + } } Err(e) => { let linker_not_found = e.kind() == io::ErrorKind::NotFound; @@ -1104,14 +1151,14 @@ fn link_natively( let stripcmd = "rust-objcopy"; match (strip, crate_type) { (Strip::Debuginfo, _) => { - strip_symbols_with_external_utility(sess, stripcmd, out_filename, &["-S"]) + strip_with_external_utility(sess, stripcmd, out_filename, &["--strip-debug"]) } // Per the manpage, `-x` is the maximum safe strip level for dynamic libraries. (#93988) (Strip::Symbols, CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro) => { - strip_symbols_with_external_utility(sess, stripcmd, out_filename, &["-x"]) + strip_with_external_utility(sess, stripcmd, out_filename, &["-x"]) } (Strip::Symbols, _) => { - strip_symbols_with_external_utility(sess, stripcmd, out_filename, &[]) + strip_with_external_utility(sess, stripcmd, out_filename, &["--strip-all"]) } (Strip::None, _) => {} } @@ -1127,9 +1174,7 @@ fn link_natively( let stripcmd = if !sess.host.is_like_solaris { "rust-objcopy" } else { "/usr/bin/strip" }; match strip { // Always preserve the symbol table (-x). - Strip::Debuginfo => { - strip_symbols_with_external_utility(sess, stripcmd, out_filename, &["-x"]) - } + Strip::Debuginfo => strip_with_external_utility(sess, stripcmd, out_filename, &["-x"]), // Strip::Symbols is handled via the --strip-all linker option. Strip::Symbols => {} Strip::None => {} @@ -1145,15 +1190,11 @@ fn link_natively( match strip { Strip::Debuginfo => { // FIXME: AIX's strip utility only offers option to strip line number information. - strip_symbols_with_external_utility(sess, stripcmd, out_filename, &[ - "-X32_64", "-l", - ]) + strip_with_external_utility(sess, stripcmd, out_filename, &["-X32_64", "-l"]) } Strip::Symbols => { // Must be noted this option might remove symbol __aix_rust_metadata and thus removes .info section which contains metadata. - strip_symbols_with_external_utility(sess, stripcmd, out_filename, &[ - "-X32_64", "-r", - ]) + strip_with_external_utility(sess, stripcmd, out_filename, &["-X32_64", "-r"]) } Strip::None => {} } @@ -1166,12 +1207,7 @@ fn link_natively( } } -fn strip_symbols_with_external_utility( - sess: &Session, - util: &str, - out_filename: &Path, - options: &[&str], -) { +fn strip_with_external_utility(sess: &Session, util: &str, out_filename: &Path, options: &[&str]) { let mut cmd = Command::new(util); cmd.args(options); @@ -2466,10 +2502,12 @@ fn add_order_independent_options( } if sess.target.os == "emscripten" { - cmd.cc_arg("-s").cc_arg(if sess.panic_strategy() == PanicStrategy::Abort { - "DISABLE_EXCEPTION_CATCHING=1" + cmd.cc_arg(if sess.opts.unstable_opts.emscripten_wasm_eh { + "-fwasm-exceptions" + } else if sess.panic_strategy() == PanicStrategy::Abort { + "-sDISABLE_EXCEPTION_CATCHING=1" } else { - "DISABLE_EXCEPTION_CATCHING=0" + "-sDISABLE_EXCEPTION_CATCHING=0" }); } diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 3c6513ca26bb1..05d6ff357514d 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -50,8 +50,7 @@ pub(crate) fn get_linker<'a>( self_contained: bool, target_cpu: &'a str, ) -> Box { - // FIXME(cc-rs#1265) pass only target arch to find_tool() - let msvc_tool = windows_registry::find_tool(sess.opts.target_triple.tuple(), "link.exe"); + let msvc_tool = windows_registry::find_tool(&sess.target.arch, "link.exe"); // If our linker looks like a batch script on Windows then to execute this // we'll need to spawn `cmd` explicitly. This is primarily done to handle @@ -411,7 +410,7 @@ impl<'a> GccLinker<'a> { let opt_level = match self.sess.opts.optimize { config::OptLevel::No => "O0", config::OptLevel::Less => "O1", - config::OptLevel::Default | config::OptLevel::Size | config::OptLevel::SizeMin => "O2", + config::OptLevel::More | config::OptLevel::Size | config::OptLevel::SizeMin => "O2", config::OptLevel::Aggressive => "O3", }; @@ -686,7 +685,7 @@ impl<'a> Linker for GccLinker<'a> { // GNU-style linkers support optimization with -O. GNU ld doesn't // need a numeric argument, but other linkers do. - if self.sess.opts.optimize == config::OptLevel::Default + if self.sess.opts.optimize == config::OptLevel::More || self.sess.opts.optimize == config::OptLevel::Aggressive { self.link_arg("-O1"); @@ -1214,7 +1213,7 @@ impl<'a> Linker for EmLinker<'a> { self.cc_arg(match self.sess.opts.optimize { OptLevel::No => "-O0", OptLevel::Less => "-O1", - OptLevel::Default => "-O2", + OptLevel::More => "-O2", OptLevel::Aggressive => "-O3", OptLevel::Size => "-Os", OptLevel::SizeMin => "-Oz", @@ -1385,7 +1384,7 @@ impl<'a> Linker for WasmLd<'a> { self.link_arg(match self.sess.opts.optimize { OptLevel::No => "-O0", OptLevel::Less => "-O1", - OptLevel::Default => "-O2", + OptLevel::More => "-O2", OptLevel::Aggressive => "-O3", // Currently LLD doesn't support `Os` and `Oz`, so pass through `O2` // instead. @@ -1452,7 +1451,7 @@ impl<'a> WasmLd<'a> { let opt_level = match self.sess.opts.optimize { config::OptLevel::No => "O0", config::OptLevel::Less => "O1", - config::OptLevel::Default => "O2", + config::OptLevel::More => "O2", config::OptLevel::Aggressive => "O3", // wasm-ld only handles integer LTO opt levels. Use O2 config::OptLevel::Size | config::OptLevel::SizeMin => "O2", @@ -1526,7 +1525,7 @@ impl<'a> Linker for L4Bender<'a> { fn optimize(&mut self) { // GNU-style linkers support optimization with -O. GNU ld doesn't // need a numeric argument, but other linkers do. - if self.sess.opts.optimize == config::OptLevel::Default + if self.sess.opts.optimize == config::OptLevel::More || self.sess.opts.optimize == config::OptLevel::Aggressive { self.link_arg("-O1"); @@ -1930,7 +1929,7 @@ impl<'a> Linker for LlbcLinker<'a> { match self.sess.opts.optimize { OptLevel::No => "-O0", OptLevel::Less => "-O1", - OptLevel::Default => "-O2", + OptLevel::More => "-O2", OptLevel::Aggressive => "-O3", OptLevel::Size => "-Os", OptLevel::SizeMin => "-Oz", @@ -2007,7 +2006,7 @@ impl<'a> Linker for BpfLinker<'a> { self.link_arg(match self.sess.opts.optimize { OptLevel::No => "-O0", OptLevel::Less => "-O1", - OptLevel::Default => "-O2", + OptLevel::More => "-O2", OptLevel::Aggressive => "-O3", OptLevel::Size => "-Os", OptLevel::SizeMin => "-Oz", diff --git a/compiler/rustc_codegen_ssa/src/back/linker/tests.rs b/compiler/rustc_codegen_ssa/src/back/linker/tests.rs index bf3e8c902009e..143df085dbfde 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker/tests.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker/tests.rs @@ -10,23 +10,22 @@ fn test_rpaths_to_args() { #[test] fn test_xlinker() { let mut cmd = Command::new("foo"); - convert_link_args_to_cc_args(&mut cmd, &[ - "arg1", - "arg2", - "arg3,with,comma", - "arg4,with,comma", - "arg5", - "arg6,with,comma", - ]); + convert_link_args_to_cc_args( + &mut cmd, + &["arg1", "arg2", "arg3,with,comma", "arg4,with,comma", "arg5", "arg6,with,comma"], + ); - assert_eq!(cmd.get_args(), [ - OsStr::new("-Wl,arg1,arg2"), - OsStr::new("-Xlinker"), - OsStr::new("arg3,with,comma"), - OsStr::new("-Xlinker"), - OsStr::new("arg4,with,comma"), - OsStr::new("-Wl,arg5"), - OsStr::new("-Xlinker"), - OsStr::new("arg6,with,comma"), - ]); + assert_eq!( + cmd.get_args(), + [ + OsStr::new("-Wl,arg1,arg2"), + OsStr::new("-Xlinker"), + OsStr::new("arg3,with,comma"), + OsStr::new("-Xlinker"), + OsStr::new("arg4,with,comma"), + OsStr::new("-Wl,arg5"), + OsStr::new("-Xlinker"), + OsStr::new("arg6,with,comma"), + ] + ); } diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index efccf7687a1e4..9fd984b6419ee 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -4,7 +4,6 @@ use std::sync::Arc; use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_data_structures::memmap::Mmap; use rustc_errors::FatalError; -use rustc_middle::ty::TyCtxt; use super::write::CodegenContext; use crate::ModuleCodegen; @@ -89,13 +88,12 @@ impl LtoModuleCodegen { pub unsafe fn autodiff( self, cgcx: &CodegenContext, - tcx: TyCtxt<'_>, diff_fncs: Vec, config: &ModuleConfig, ) -> Result, FatalError> { match &self { LtoModuleCodegen::Fat(module) => { - B::autodiff(cgcx, tcx, &module, diff_fncs, config)?; + B::autodiff(cgcx, &module, diff_fncs, config)?; } _ => panic!("autodiff called with non-fat LTO module"), } diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index ba7b53321a83d..d70413b8a4741 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -704,13 +704,17 @@ pub fn create_metadata_file_for_wasm(sess: &Session, data: &[u8], section_name: let mut imports = wasm_encoder::ImportSection::new(); if sess.target.pointer_width == 64 { - imports.import("env", "__linear_memory", wasm_encoder::MemoryType { - minimum: 0, - maximum: None, - memory64: true, - shared: false, - page_size_log2: None, - }); + imports.import( + "env", + "__linear_memory", + wasm_encoder::MemoryType { + minimum: 0, + maximum: None, + memory64: true, + shared: false, + page_size_log2: None, + }, + ); } if imports.len() > 0 { diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 60ab291935256..1dbaffaa57745 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -140,11 +140,14 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap { if args.non_erasable_generics().next().is_some() { let symbol = ExportedSymbol::Generic(def, args); - symbols.push((symbol, SymbolExportInfo { - level: SymbolExportLevel::Rust, - kind: SymbolExportKind::Text, - used: false, - })); + symbols.push(( + symbol, + SymbolExportInfo { + level: SymbolExportLevel::Rust, + kind: SymbolExportKind::Text, + used: false, + }, + )); } } MonoItem::Fn(Instance { def: InstanceKind::DropGlue(_, Some(ty)), args }) => { // A little sanity-check assert_eq!(args.non_erasable_generics().next(), Some(GenericArgKind::Type(ty))); - symbols.push((ExportedSymbol::DropGlue(ty), SymbolExportInfo { - level: SymbolExportLevel::Rust, - kind: SymbolExportKind::Text, - used: false, - })); + symbols.push(( + ExportedSymbol::DropGlue(ty), + SymbolExportInfo { + level: SymbolExportLevel::Rust, + kind: SymbolExportKind::Text, + used: false, + }, + )); } MonoItem::Fn(Instance { def: InstanceKind::AsyncDropGlueCtorShim(_, Some(ty)), @@ -347,11 +377,14 @@ fn exported_symbols_provider_local( }) => { // A little sanity-check assert_eq!(args.non_erasable_generics().next(), Some(GenericArgKind::Type(ty))); - symbols.push((ExportedSymbol::AsyncDropGlueCtorShim(ty), SymbolExportInfo { - level: SymbolExportLevel::Rust, - kind: SymbolExportKind::Text, - used: false, - })); + symbols.push(( + ExportedSymbol::AsyncDropGlueCtorShim(ty), + SymbolExportInfo { + level: SymbolExportLevel::Rust, + kind: SymbolExportKind::Text, + used: false, + }, + )); } _ => { // Any other symbols don't qualify for sharing diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index b40bb4ed5d2ac..f029c08a80821 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -7,6 +7,7 @@ use std::sync::mpsc::{Receiver, Sender, channel}; use std::{fs, io, mem, str, thread}; use rustc_ast::attr; +use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::jobserver::{self, Acquired}; use rustc_data_structures::memmap::Mmap; @@ -40,7 +41,7 @@ use tracing::debug; use super::link::{self, ensure_removed}; use super::lto::{self, SerializedModule}; use super::symbol_export::symbol_name_for_instance_in_crate; -use crate::errors::ErrorCreatingRemarkDir; +use crate::errors::{AutodiffWithoutLto, ErrorCreatingRemarkDir}; use crate::traits::*; use crate::{ CachedModuleCodegen, CodegenResults, CompiledModule, CrateInfo, ModuleCodegen, ModuleKind, @@ -118,6 +119,7 @@ pub struct ModuleConfig { pub merge_functions: bool, pub emit_lifetime_markers: bool, pub llvm_plugins: Vec, + pub autodiff: Vec, } impl ModuleConfig { @@ -234,7 +236,7 @@ impl ModuleConfig { // Copy what clang does by turning on loop vectorization at O2 and // slp vectorization at O3. vectorize_loop: !sess.opts.cg.no_vectorize_loops - && (sess.opts.optimize == config::OptLevel::Default + && (sess.opts.optimize == config::OptLevel::More || sess.opts.optimize == config::OptLevel::Aggressive), vectorize_slp: !sess.opts.cg.no_vectorize_slp && sess.opts.optimize == config::OptLevel::Aggressive, @@ -258,7 +260,7 @@ impl ModuleConfig { MergeFunctions::Trampolines | MergeFunctions::Aliases => { use config::OptLevel::*; match sess.opts.optimize { - Aggressive | Default | SizeMin | Size => true, + Aggressive | More | SizeMin | Size => true, Less | No => false, } } @@ -266,6 +268,7 @@ impl ModuleConfig { emit_lifetime_markers: sess.emit_lifetime_markers(), llvm_plugins: if_regular!(sess.opts.unstable_opts.llvm_plugins.clone(), vec![]), + autodiff: if_regular!(sess.opts.unstable_opts.autodiff.clone(), vec![]), } } @@ -389,6 +392,7 @@ impl CodegenContext { fn generate_lto_work( cgcx: &CodegenContext, + autodiff: Vec, needs_fat_lto: Vec>, needs_thin_lto: Vec<(String, B::ThinBuffer)>, import_only_modules: Vec<(SerializedModule, WorkProduct)>, @@ -397,11 +401,19 @@ fn generate_lto_work( if !needs_fat_lto.is_empty() { assert!(needs_thin_lto.is_empty()); - let module = + let mut module = B::run_fat_lto(cgcx, needs_fat_lto, import_only_modules).unwrap_or_else(|e| e.raise()); + if cgcx.lto == Lto::Fat && !autodiff.is_empty() { + let config = cgcx.config(ModuleKind::Regular); + module = unsafe { module.autodiff(cgcx, autodiff, config).unwrap() }; + } // We are adding a single work item, so the cost doesn't matter. vec![(WorkItem::LTO(module), 0)] } else { + if !autodiff.is_empty() { + let dcx = cgcx.create_dcx(); + dcx.handle().emit_fatal(AutodiffWithoutLto {}); + } assert!(needs_fat_lto.is_empty()); let (lto_modules, copy_jobs) = B::run_thin_lto(cgcx, needs_thin_lto, import_only_modules) .unwrap_or_else(|e| e.raise()); @@ -1021,6 +1033,9 @@ pub(crate) enum Message { /// Sent from a backend worker thread. WorkItem { result: Result, Option>, worker_id: usize }, + /// A vector containing all the AutoDiff tasks that we have to pass to Enzyme. + AddAutoDiffItems(Vec), + /// The frontend has finished generating something (backend IR or a /// post-LTO artifact) for a codegen unit, and it should be passed to the /// backend. Sent from the main thread. @@ -1348,6 +1363,7 @@ fn start_executing_work( // This is where we collect codegen units that have gone all the way // through codegen and LLVM. + let mut autodiff_items = Vec::new(); let mut compiled_modules = vec![]; let mut compiled_allocator_module = None; let mut needs_link = Vec::new(); @@ -1459,9 +1475,13 @@ fn start_executing_work( let needs_thin_lto = mem::take(&mut needs_thin_lto); let import_only_modules = mem::take(&mut lto_import_only_modules); - for (work, cost) in - generate_lto_work(&cgcx, needs_fat_lto, needs_thin_lto, import_only_modules) - { + for (work, cost) in generate_lto_work( + &cgcx, + autodiff_items.clone(), + needs_fat_lto, + needs_thin_lto, + import_only_modules, + ) { let insertion_index = work_items .binary_search_by_key(&cost, |&(_, cost)| cost) .unwrap_or_else(|e| e); @@ -1596,6 +1616,10 @@ fn start_executing_work( main_thread_state = MainThreadState::Idle; } + Message::AddAutoDiffItems(mut items) => { + autodiff_items.append(&mut items); + } + Message::CodegenComplete => { if codegen_state != Aborted { codegen_state = Completed; @@ -2070,6 +2094,10 @@ impl OngoingCodegen { drop(self.coordinator.sender.send(Box::new(Message::CodegenComplete::))); } + pub(crate) fn submit_autodiff_items(&self, items: Vec) { + drop(self.coordinator.sender.send(Box::new(Message::::AddAutoDiffItems(items)))); + } + pub(crate) fn check_for_errors(&self, sess: &Session) { self.shared_emitter_main.check(sess, false); } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 77e1fed720dfb..e800492dad02f 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -1,5 +1,6 @@ use std::cmp; use std::collections::BTreeSet; +use std::sync::Arc; use std::time::{Duration, Instant}; use itertools::Itertools; @@ -7,7 +8,7 @@ use rustc_abi::FIRST_VARIANT; use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, AllocatorKind, global_fn_name}; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; -use rustc_data_structures::sync::{Lrc, par_map}; +use rustc_data_structures::sync::par_map; use rustc_data_structures::unord::UnordMap; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; @@ -18,14 +19,13 @@ use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, Debugger use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_middle::middle::{exported_symbols, lang_items}; use rustc_middle::mir::BinOp; -use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem}; +use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem, MonoItemPartitions}; use rustc_middle::query::Providers; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_session::Session; use rustc_session::config::{self, CrateType, EntryFnType, OptLevel, OutputType}; use rustc_span::{DUMMY_SP, Symbol, sym}; -use rustc_trait_selection::infer::at::ToTrace; use rustc_trait_selection::infer::{BoundRegionConversionTime, TyCtxtInferExt}; use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt}; use tracing::{debug, info}; @@ -44,7 +44,8 @@ use crate::mir::operand::OperandValue; use crate::mir::place::PlaceRef; use crate::traits::*; use crate::{ - CachedModuleCodegen, CompiledModule, CrateInfo, ModuleCodegen, ModuleKind, errors, meth, mir, + CachedModuleCodegen, CodegenLintLevels, CompiledModule, CrateInfo, ModuleCodegen, ModuleKind, + errors, meth, mir, }; pub(crate) fn bin_op_to_icmp_predicate(op: BinOp, signed: bool) -> IntPredicate { @@ -128,14 +129,9 @@ pub fn validate_trivial_unsize<'tcx>( BoundRegionConversionTime::HigherRankedType, hr_source_principal, ); - let Ok(()) = ocx.eq_trace( + let Ok(()) = ocx.eq( &ObligationCause::dummy(), param_env, - ToTrace::to_trace( - &ObligationCause::dummy(), - hr_target_principal, - hr_source_principal, - ), target_principal, source_principal, ) else { @@ -210,7 +206,12 @@ fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( old_info } } - (_, ty::Dynamic(data, _, _)) => meth::get_vtable(cx, source, data.principal()), + (_, ty::Dynamic(data, _, _)) => meth::get_vtable( + cx, + source, + data.principal() + .map(|principal| bx.tcx().instantiate_bound_regions_with_erased(principal)), + ), _ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target), } } @@ -388,7 +389,8 @@ pub(crate) fn build_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // exceptions. This means that the VM does the unwinding for // us pub fn wants_wasm_eh(sess: &Session) -> bool { - sess.target.is_like_wasm && sess.target.os != "emscripten" + sess.target.is_like_wasm + && (sess.target.os != "emscripten" || sess.opts.unstable_opts.emscripten_wasm_eh) } /// Returns `true` if this session's target will use SEH-based unwinding. @@ -489,8 +491,8 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let ptr_ty = cx.type_ptr(); let (arg_argc, arg_argv) = get_argc_argv(&mut bx); - let (start_fn, start_ty, args, instance) = if let EntryFnType::Main { sigpipe } = entry_type - { + let EntryFnType::Main { sigpipe } = entry_type; + let (start_fn, start_ty, args, instance) = { let start_def_id = cx.tcx().require_lang_item(LangItem::Start, None); let start_instance = ty::Instance::expect_resolve( cx.tcx(), @@ -511,10 +513,6 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( vec![rust_main, arg_argc, arg_argv, arg_sigpipe], Some(start_instance), ) - } else { - debug!("using user-defined start fn"); - let start_ty = cx.type_func(&[isize_ty, ptr_ty], isize_ty); - (rust_main, start_ty, vec![arg_argc, arg_argv], None) }; let result = bx.call(start_ty, None, None, start_fn, &args, None, instance); @@ -529,7 +527,8 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } } -/// Obtain the `argc` and `argv` values to pass to the rust start function. +/// Obtain the `argc` and `argv` values to pass to the rust start function +/// (i.e., the "start" lang item). fn get_argc_argv<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(bx: &mut Bx) -> (Bx::Value, Bx::Value) { if bx.cx().sess().target.os.contains("uefi") { // Params for UEFI @@ -616,11 +615,18 @@ pub fn codegen_crate( return ongoing_codegen; } + if tcx.sess.target.need_explicit_cpu && tcx.sess.opts.cg.target_cpu.is_none() { + // The target has no default cpu, but none is set explicitly + tcx.dcx().emit_fatal(errors::CpuRequired); + } + let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx); // Run the monomorphization collector and partition the collected items into // codegen units. - let codegen_units = tcx.collect_and_partition_mono_items(()).1; + let MonoItemPartitions { codegen_units, autodiff_items, .. } = + tcx.collect_and_partition_mono_items(()); + let autodiff_fncs = autodiff_items.to_vec(); // Force all codegen_unit queries so they are already either red or green // when compile_codegen_unit accesses them. We are not able to re-execute @@ -629,7 +635,7 @@ pub fn codegen_crate( // unnecessarily. if tcx.dep_graph.is_fully_enabled() { for cgu in codegen_units { - tcx.ensure().codegen_unit(cgu.name()); + tcx.ensure_ok().codegen_unit(cgu.name()); } } @@ -691,6 +697,10 @@ pub fn codegen_crate( ); } + if !autodiff_fncs.is_empty() { + ongoing_codegen.submit_autodiff_items(autodiff_fncs); + } + // For better throughput during parallel processing by LLVM, we used to sort // CGUs largest to smallest. This would lead to better thread utilization // by, for example, preventing a large CGU from being processed last and @@ -923,9 +933,10 @@ impl CrateInfo { crate_name: UnordMap::with_capacity(n_crates), used_crates, used_crate_source: UnordMap::with_capacity(n_crates), - dependency_formats: Lrc::clone(tcx.dependency_formats(())), + dependency_formats: Arc::clone(tcx.dependency_formats(())), windows_subsystem, natvis_debugger_visualizers: Default::default(), + lint_levels: CodegenLintLevels::from_tcx(tcx), }; info.native_libraries.reserve(n_crates); @@ -936,7 +947,7 @@ impl CrateInfo { info.crate_name.insert(cnum, tcx.crate_name(cnum)); let used_crate_source = tcx.used_crate_source(cnum); - info.used_crate_source.insert(cnum, Lrc::clone(used_crate_source)); + info.used_crate_source.insert(cnum, Arc::clone(used_crate_source)); if tcx.is_profiler_runtime(cnum) { info.profiler_runtime = Some(cnum); } @@ -1043,22 +1054,19 @@ pub(crate) fn provide(providers: &mut Providers) { config::OptLevel::No => return config::OptLevel::No, // If globally optimise-speed is already specified, just use that level. config::OptLevel::Less => return config::OptLevel::Less, - config::OptLevel::Default => return config::OptLevel::Default, + config::OptLevel::More => return config::OptLevel::More, config::OptLevel::Aggressive => return config::OptLevel::Aggressive, // If globally optimize-for-size has been requested, use -O2 instead (if optimize(size) // are present). - config::OptLevel::Size => config::OptLevel::Default, - config::OptLevel::SizeMin => config::OptLevel::Default, + config::OptLevel::Size => config::OptLevel::More, + config::OptLevel::SizeMin => config::OptLevel::More, }; - let (defids, _) = tcx.collect_and_partition_mono_items(cratenum); + let defids = tcx.collect_and_partition_mono_items(cratenum).all_mono_items; let any_for_speed = defids.items().any(|id| { let CodegenFnAttrs { optimize, .. } = tcx.codegen_fn_attrs(*id); - match optimize { - attr::OptimizeAttr::None | attr::OptimizeAttr::Size => false, - attr::OptimizeAttr::Speed => true, - } + matches!(optimize, attr::OptimizeAttr::Speed) }); if any_for_speed { diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index cdb72aba36fa0..3e9dfcea58b8b 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -1,5 +1,11 @@ +use std::str::FromStr; + +use rustc_abi::ExternAbi; use rustc_ast::attr::list_contains_name; -use rustc_ast::{MetaItemInner, attr}; +use rustc_ast::expand::autodiff_attrs::{ + AutoDiffAttrs, DiffActivity, DiffMode, valid_input_activity, valid_ret_activity, +}; +use rustc_ast::{MetaItem, MetaItemInner, attr}; use rustc_attr_parsing::{InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::codes::*; @@ -13,11 +19,13 @@ use rustc_middle::middle::codegen_fn_attrs::{ }; use rustc_middle::mir::mono::Linkage; use rustc_middle::query::Providers; +use rustc_middle::span_bug; use rustc_middle::ty::{self as ty, TyCtxt}; use rustc_session::parse::feature_err; use rustc_session::{Session, lint}; use rustc_span::{Ident, Span, sym}; -use rustc_target::spec::{SanitizerSet, abi}; +use rustc_target::spec::SanitizerSet; +use tracing::debug; use crate::errors; use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature_attr}; @@ -34,7 +42,6 @@ fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage { // ghost, dllimport, dllexport and linkonce_odr_autohide are not supported // and don't have to be, LLVM treats them as no-ops. match name { - "appending" => Appending, "available_externally" => AvailableExternally, "common" => Common, "extern_weak" => ExternalWeak, @@ -42,7 +49,6 @@ fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage { "internal" => Internal, "linkonce" => LinkOnceAny, "linkonce_odr" => LinkOnceODR, - "private" => Private, "weak" => WeakAny, "weak_odr" => WeakODR, _ => tcx.dcx().span_fatal(tcx.def_span(def_id), "invalid linkage specified"), @@ -64,6 +70,13 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; } + // If our rustc version supports autodiff/enzyme, then we call our handler + // to check for any `#[rustc_autodiff(...)]` attributes. + if cfg!(llvm_enzyme) { + let ad = autodiff_attrs(tcx, did.into()); + codegen_fn_attrs.autodiff_item = ad; + } + // When `no_builtins` is applied at the crate level, we should add the // `no-builtins` attribute to each function to ensure it takes effect in LTO. let crate_attrs = tcx.hir().attrs(rustc_hir::CRATE_HIR_ID); @@ -207,7 +220,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { if !is_closure && let Some(fn_sig) = fn_sig() - && fn_sig.skip_binder().abi() != abi::Abi::Rust + && fn_sig.skip_binder().abi() != ExternAbi::Rust { struct_span_code_err!( tcx.dcx(), @@ -249,16 +262,19 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } } sym::target_feature => { - if !tcx.is_closure_like(did.to_def_id()) - && let Some(fn_sig) = fn_sig() - && fn_sig.skip_binder().safety().is_safe() - { + let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else { + tcx.dcx().span_delayed_bug(attr.span, "target_feature applied to non-fn"); + continue; + }; + let safe_target_features = + matches!(sig.header.safety, hir::HeaderSafety::SafeTargetFeatures); + codegen_fn_attrs.safe_target_features = safe_target_features; + if safe_target_features { if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc { // The `#[target_feature]` attribute is allowed on - // WebAssembly targets on all functions, including safe - // ones. Other targets require that `#[target_feature]` is - // only applied to unsafe functions (pending the - // `target_feature_11` feature) because on most targets + // WebAssembly targets on all functions. Prior to stabilizing + // the `target_feature_11` feature, `#[target_feature]` was + // only permitted on unsafe functions because on most targets // execution of instructions that are not supported is // considered undefined behavior. For WebAssembly which is a // 100% safe target at execution time it's not possible to @@ -272,17 +288,10 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { // if a target is documenting some wasm-specific code then // it's not spuriously denied. // - // This exception needs to be kept in sync with allowing - // `#[target_feature]` on `main` and `start`. - } else if !tcx.features().target_feature_11() { - feature_err( - &tcx.sess, - sym::target_feature_11, - attr.span, - "`#[target_feature(..)]` can only be applied to `unsafe` functions", - ) - .with_span_label(tcx.def_span(did), "not an `unsafe` function") - .emit(); + // Now that `#[target_feature]` is permitted on safe functions, + // this exception must still exist for allowing the attribute on + // `main`, `start`, and other functions that are not usually + // allowed. } else { check_target_feature_trait_unsafe(tcx, did, attr.span); } @@ -525,6 +534,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { if !attr.has_name(sym::inline) { return ia; } + if attr.is_word() { InlineAttr::Hint } else if let Some(ref items) = attr.meta_item_list() { @@ -547,6 +557,20 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { ia } }); + codegen_fn_attrs.inline = attrs.iter().fold(codegen_fn_attrs.inline, |ia, attr| { + if !attr.has_name(sym::rustc_force_inline) || !tcx.features().rustc_attrs() { + return ia; + } + + if attr.is_word() { + InlineAttr::Force { attr_span: attr.span, reason: None } + } else if let Some(val) = attr.value_str() { + InlineAttr::Force { attr_span: attr.span, reason: Some(val) } + } else { + debug!("`rustc_force_inline` not checked by attribute validation"); + ia + } + }); // naked function MUST NOT be inlined! This attribute is required for the rust compiler itself, // but not for the code generation backend because at that point the naked function will just be @@ -555,7 +579,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { codegen_fn_attrs.inline = InlineAttr::Never; } - codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::None, |ia, attr| { + codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::Default, |ia, attr| { if !attr.has_name(sym::optimize) { return ia; } @@ -567,17 +591,19 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { inline_span = Some(attr.span); if items.len() != 1 { err(attr.span, "expected one argument"); - OptimizeAttr::None + OptimizeAttr::Default } else if list_contains_name(items, sym::size) { OptimizeAttr::Size } else if list_contains_name(items, sym::speed) { OptimizeAttr::Speed + } else if list_contains_name(items, sym::none) { + OptimizeAttr::DoNotOptimize } else { err(items[0].span(), "invalid argument"); - OptimizeAttr::None + OptimizeAttr::Default } } else { - OptimizeAttr::None + OptimizeAttr::Default } }); @@ -594,10 +620,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { // its parent function, which effectively inherits the features anyway. Boxing this closure // would result in this closure being compiled without the inherited target features, but this // is probably a poor usage of `#[inline(always)]` and easily avoided by not using the attribute. - if tcx.features().target_feature_11() - && tcx.is_closure_like(did.to_def_id()) - && codegen_fn_attrs.inline != InlineAttr::Always - { + if tcx.is_closure_like(did.to_def_id()) && codegen_fn_attrs.inline != InlineAttr::Always { let owner_id = tcx.parent(did.to_def_id()); if tcx.def_kind(owner_id).has_codegen_attrs() { codegen_fn_attrs @@ -606,22 +629,28 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } } - // If a function uses #[target_feature] it can't be inlined into general + // If a function uses `#[target_feature]` it can't be inlined into general // purpose functions as they wouldn't have the right target features - // enabled. For that reason we also forbid #[inline(always)] as it can't be + // enabled. For that reason we also forbid `#[inline(always)]` as it can't be // respected. - if !codegen_fn_attrs.target_features.is_empty() && codegen_fn_attrs.inline == InlineAttr::Always + // + // `#[rustc_force_inline]` doesn't need to be prohibited here, only + // `#[inline(always)]`, as forced inlining is implemented entirely within + // rustc (and so the MIR inliner can do any necessary checks for compatible target + // features). + // + // This sidesteps the LLVM blockers in enabling `target_features` + + // `inline(always)` to be used together (see rust-lang/rust#116573 and + // llvm/llvm-project#70563). + if !codegen_fn_attrs.target_features.is_empty() + && matches!(codegen_fn_attrs.inline, InlineAttr::Always) { if let Some(span) = inline_span { - tcx.dcx().span_err( - span, - "cannot use `#[inline(always)]` with \ - `#[target_feature]`", - ); + tcx.dcx().span_err(span, "cannot use `#[inline(always)]` with `#[target_feature]`"); } } - if !codegen_fn_attrs.no_sanitize.is_empty() && codegen_fn_attrs.inline == InlineAttr::Always { + if !codegen_fn_attrs.no_sanitize.is_empty() && codegen_fn_attrs.inline.always() { if let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span) { let hir_id = tcx.local_def_id_to_hir_id(did); tcx.node_span_lint( @@ -828,6 +857,107 @@ impl<'a> MixedExportNameAndNoMangleState<'a> { } } +/// We now check the #\[rustc_autodiff\] attributes which we generated from the #[autodiff(...)] +/// macros. There are two forms. The pure one without args to mark primal functions (the functions +/// being differentiated). The other form is #[rustc_autodiff(Mode, ActivityList)] on top of the +/// placeholder functions. We wrote the rustc_autodiff attributes ourself, so this should never +/// panic, unless we introduced a bug when parsing the autodiff macro. +fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option { + let attrs = tcx.get_attrs(id, sym::rustc_autodiff); + + let attrs = + attrs.filter(|attr| attr.name_or_empty() == sym::rustc_autodiff).collect::>(); + + // check for exactly one autodiff attribute on placeholder functions. + // There should only be one, since we generate a new placeholder per ad macro. + // FIXME(ZuseZ4): re-enable this check. Currently we add multiple, which doesn't cause harm but + // looks strange e.g. under cargo-expand. + let attr = match &attrs[..] { + [] => return None, + [attr] => attr, + // These two attributes are the same and unfortunately duplicated due to a previous bug. + [attr, _attr2] => attr, + _ => { + //FIXME(ZuseZ4): Once we fixed our parser, we should also prohibit the two-attribute + //branch above. + span_bug!(attrs[1].span, "cg_ssa: rustc_autodiff should only exist once per source"); + } + }; + + let list = attr.meta_item_list().unwrap_or_default(); + + // empty autodiff attribute macros (i.e. `#[autodiff]`) are used to mark source functions + if list.is_empty() { + return Some(AutoDiffAttrs::source()); + } + + let [mode, input_activities @ .., ret_activity] = &list[..] else { + span_bug!(attr.span, "rustc_autodiff attribute must contain mode and activities"); + }; + let mode = if let MetaItemInner::MetaItem(MetaItem { path: ref p1, .. }) = mode { + p1.segments.first().unwrap().ident + } else { + span_bug!(attr.span, "rustc_autodiff attribute must contain mode"); + }; + + // parse mode + let mode = match mode.as_str() { + "Forward" => DiffMode::Forward, + "Reverse" => DiffMode::Reverse, + _ => { + span_bug!(mode.span, "rustc_autodiff attribute contains invalid mode"); + } + }; + + // First read the ret symbol from the attribute + let ret_symbol = if let MetaItemInner::MetaItem(MetaItem { path: ref p1, .. }) = ret_activity { + p1.segments.first().unwrap().ident + } else { + span_bug!(attr.span, "rustc_autodiff attribute must contain the return activity"); + }; + + // Then parse it into an actual DiffActivity + let Ok(ret_activity) = DiffActivity::from_str(ret_symbol.as_str()) else { + span_bug!(ret_symbol.span, "invalid return activity"); + }; + + // Now parse all the intermediate (input) activities + let mut arg_activities: Vec = vec![]; + for arg in input_activities { + let arg_symbol = if let MetaItemInner::MetaItem(MetaItem { path: ref p2, .. }) = arg { + match p2.segments.first() { + Some(x) => x.ident, + None => { + span_bug!( + arg.span(), + "rustc_autodiff attribute must contain the input activity" + ); + } + } + } else { + span_bug!(arg.span(), "rustc_autodiff attribute must contain the input activity"); + }; + + match DiffActivity::from_str(arg_symbol.as_str()) { + Ok(arg_activity) => arg_activities.push(arg_activity), + Err(_) => { + span_bug!(arg_symbol.span, "invalid input activity"); + } + } + } + + for &input in &arg_activities { + if !valid_input_activity(mode, input) { + span_bug!(attr.span, "Invalid input activity {} for {} mode", input, mode); + } + } + if !valid_ret_activity(mode, ret_activity) { + span_bug!(attr.span, "Invalid return activity {} for {} mode", ret_activity, mode); + } + + Some(AutoDiffAttrs { mode, ret_activity, input_activity: arg_activities }) +} + pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers }; } diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 869798d8be194..53953b089c613 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -367,9 +367,7 @@ fn push_debuginfo_type_name<'tcx>( output.push_str(sig.safety.prefix_str()); if sig.abi != rustc_abi::ExternAbi::Rust { - output.push_str("extern \""); - output.push_str(sig.abi.name()); - output.push_str("\" "); + let _ = write!(output, "extern {} ", sig.abi); } output.push_str("fn("); @@ -507,7 +505,7 @@ pub enum VTableNameKind { pub fn compute_debuginfo_vtable_name<'tcx>( tcx: TyCtxt<'tcx>, t: Ty<'tcx>, - trait_ref: Option>, + trait_ref: Option>, kind: VTableNameKind, ) -> String { let cpp_like_debuginfo = cpp_like_debuginfo(tcx); @@ -530,8 +528,8 @@ pub fn compute_debuginfo_vtable_name<'tcx>( } if let Some(trait_ref) = trait_ref { - let trait_ref = tcx - .normalize_erasing_late_bound_regions(ty::TypingEnv::fully_monomorphized(), trait_ref); + let trait_ref = + tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref); push_item_name(tcx, trait_ref.def_id, true, &mut vtable_name); visited.clear(); push_generic_params_internal(tcx, trait_ref.args, &mut vtable_name, &mut visited); @@ -673,25 +671,23 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S ty::ConstKind::Param(param) => { write!(output, "{}", param.name) } - ty::ConstKind::Value(ty, valtree) => { - match ty.kind() { + ty::ConstKind::Value(cv) => { + match cv.ty.kind() { ty::Int(ity) => { - // FIXME: directly extract the bits from a valtree instead of evaluating an - // already evaluated `Const` in order to get the bits. - let bits = ct + let bits = cv .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized()) .expect("expected monomorphic const in codegen"); let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128; write!(output, "{val}") } ty::Uint(_) => { - let val = ct + let val = cv .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized()) .expect("expected monomorphic const in codegen"); write!(output, "{val}") } ty::Bool => { - let val = ct.try_to_bool().expect("expected monomorphic const in codegen"); + let val = cv.try_to_bool().expect("expected monomorphic const in codegen"); write!(output, "{val}") } _ => { @@ -703,9 +699,7 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S // avoiding collisions and will make the emitted type names shorter. let hash_short = tcx.with_stable_hashing_context(|mut hcx| { let mut hasher = StableHasher::new(); - hcx.while_hashing_spans(false, |hcx| { - (ty, valtree).hash_stable(hcx, &mut hasher) - }); + hcx.while_hashing_spans(false, |hcx| cv.hash_stable(hcx, &mut hasher)); hasher.finish::() }); diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index c7213bbc801f9..3ddbe4aeeec5d 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -39,6 +39,10 @@ pub(crate) struct CguNotRecorded<'a> { pub cgu_name: &'a str, } +#[derive(Diagnostic)] +#[diag(codegen_ssa_autodiff_without_lto)] +pub struct AutodiffWithoutLto; + #[derive(Diagnostic)] #[diag(codegen_ssa_unknown_reuse_kind)] pub(crate) struct UnknownReuseKind { @@ -351,6 +355,7 @@ pub(crate) struct LinkingFailed<'a> { pub command: Command, pub escaped_output: String, pub verbose: bool, + pub sysroot_dir: PathBuf, } impl Diagnostic<'_, G> for LinkingFailed<'_> { @@ -364,6 +369,8 @@ impl Diagnostic<'_, G> for LinkingFailed<'_> { if self.verbose { diag.note(format!("{:?}", self.command)); } else { + self.command.env_clear(); + enum ArgGroup { Regular(OsString), Objects(usize), @@ -398,26 +405,55 @@ impl Diagnostic<'_, G> for LinkingFailed<'_> { args.push(ArgGroup::Regular(arg)); } } - self.command.args(args.into_iter().map(|arg_group| match arg_group { - ArgGroup::Regular(arg) => arg, - ArgGroup::Objects(n) => OsString::from(format!("<{n} object files omitted>")), - ArgGroup::Rlibs(dir, rlibs) => { - let mut arg = dir.into_os_string(); - arg.push("/{"); - let mut first = true; - for rlib in rlibs { - if !first { - arg.push(","); + let crate_hash = regex::bytes::Regex::new(r"-[0-9a-f]+\.rlib$").unwrap(); + self.command.args(args.into_iter().map(|arg_group| { + match arg_group { + // SAFETY: we are only matching on ASCII, not any surrogate pairs, so any replacements we do will still be valid. + ArgGroup::Regular(arg) => unsafe { + use bstr::ByteSlice; + OsString::from_encoded_bytes_unchecked( + arg.as_encoded_bytes().replace( + self.sysroot_dir.as_os_str().as_encoded_bytes(), + b"", + ), + ) + }, + ArgGroup::Objects(n) => OsString::from(format!("<{n} object files omitted>")), + ArgGroup::Rlibs(mut dir, rlibs) => { + let is_sysroot_dir = match dir.strip_prefix(&self.sysroot_dir) { + Ok(short) => { + dir = Path::new("").join(short); + true + } + Err(_) => false, + }; + let mut arg = dir.into_os_string(); + arg.push("/{"); + let mut first = true; + for mut rlib in rlibs { + if !first { + arg.push(","); + } + first = false; + if is_sysroot_dir { + // SAFETY: Regex works one byte at a type, and our regex will not match surrogate pairs (because it only matches ascii). + rlib = unsafe { + OsString::from_encoded_bytes_unchecked( + crate_hash + .replace(rlib.as_encoded_bytes(), b"-*") + .into_owned(), + ) + }; + } + arg.push(rlib); } - first = false; - arg.push(rlib); + arg.push("}.rlib"); + arg } - arg.push("}"); - arg } })); - diag.note(format!("{:?}", self.command)); + diag.note(format!("{:?}", self.command).trim_start_matches("env -i").to_owned()); diag.note("some arguments are omitted. use `--verbose` to show all linker arguments"); } @@ -491,6 +527,10 @@ pub(crate) struct CheckInstalledVisualStudio; #[diag(codegen_ssa_insufficient_vs_code_product)] pub(crate) struct InsufficientVSCodeProduct; +#[derive(Diagnostic)] +#[diag(codegen_ssa_cpu_required)] +pub(crate) struct CpuRequired; + #[derive(Diagnostic)] #[diag(codegen_ssa_processing_dymutil_failed)] #[note] diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 65c6067c74066..428a45975f1ec 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -24,23 +24,28 @@ use std::collections::BTreeSet; use std::io; use std::path::{Path, PathBuf}; +use std::sync::Arc; use rustc_ast as ast; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_data_structures::sync::Lrc; use rustc_data_structures::unord::UnordMap; +use rustc_hir::CRATE_HIR_ID; use rustc_hir::def_id::CrateNum; use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_middle::dep_graph::WorkProduct; +use rustc_middle::lint::LintLevelSource; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Dependencies; use rustc_middle::middle::exported_symbols::SymbolExportKind; +use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; use rustc_serialize::opaque::{FileEncoder, MemDecoder}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_session::Session; use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT}; use rustc_session::cstore::{self, CrateSource}; +use rustc_session::lint::Level; +use rustc_session::lint::builtin::LINKER_MESSAGES; use rustc_session::utils::NativeLibKind; use rustc_span::Symbol; @@ -195,11 +200,12 @@ pub struct CrateInfo { pub native_libraries: FxIndexMap>, pub crate_name: UnordMap, pub used_libraries: Vec, - pub used_crate_source: UnordMap>, + pub used_crate_source: UnordMap>, pub used_crates: Vec, - pub dependency_formats: Lrc, + pub dependency_formats: Arc, pub windows_subsystem: Option, pub natvis_debugger_visualizers: BTreeSet, + pub lint_levels: CodegenLintLevels, } #[derive(Encodable, Decodable)] @@ -302,3 +308,19 @@ impl CodegenResults { Ok((codegen_results, outputs)) } } + +/// A list of lint levels used in codegen. +/// +/// When using `-Z link-only`, we don't have access to the tcx and must work +/// solely from the `.rlink` file. `Lint`s are defined too early to be encodeable. +/// Instead, encode exactly the information we need. +#[derive(Copy, Clone, Debug, Encodable, Decodable)] +pub struct CodegenLintLevels { + linker_messages: (Level, LintLevelSource), +} + +impl CodegenLintLevels { + pub fn from_tcx(tcx: TyCtxt<'_>) -> Self { + Self { linker_messages: tcx.lint_level_at_node(LINKER_MESSAGES, CRATE_HIR_ID) } + } +} diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs index 64cd4c38937e7..399c592432aca 100644 --- a/compiler/rustc_codegen_ssa/src/meth.rs +++ b/compiler/rustc_codegen_ssa/src/meth.rs @@ -1,5 +1,5 @@ use rustc_middle::bug; -use rustc_middle::ty::{self, GenericArgKind, Ty}; +use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt}; use rustc_session::config::Lto; use rustc_symbol_mangling::typeid_for_trait_ref; use rustc_target::callconv::FnAbi; @@ -72,12 +72,19 @@ impl<'a, 'tcx> VirtualIndex { /// This takes a valid `self` receiver type and extracts the principal trait /// ref of the type. Return `None` if there is no principal trait. -fn dyn_trait_in_self(ty: Ty<'_>) -> Option> { +fn dyn_trait_in_self<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, +) -> Option> { for arg in ty.peel_refs().walk() { if let GenericArgKind::Type(ty) = arg.unpack() && let ty::Dynamic(data, _, _) = ty.kind() { - return data.principal(); + // FIXME(arbitrary_self_types): This is likely broken for receivers which + // have a "non-self" trait objects as a generic argument. + return data + .principal() + .map(|principal| tcx.instantiate_bound_regions_with_erased(principal)); } } @@ -96,7 +103,7 @@ fn dyn_trait_in_self(ty: Ty<'_>) -> Option> { pub(crate) fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>( cx: &Cx, ty: Ty<'tcx>, - trait_ref: Option>, + trait_ref: Option>, ) -> Cx::Value { let tcx = cx.tcx(); @@ -131,7 +138,7 @@ pub(crate) fn load_vtable<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( if bx.cx().sess().opts.unstable_opts.virtual_function_elimination && bx.cx().sess().lto() == Lto::Fat { - if let Some(trait_ref) = dyn_trait_in_self(ty) { + if let Some(trait_ref) = dyn_trait_in_self(bx.tcx(), ty) { let typeid = bx.typeid_metadata(typeid_for_trait_ref(bx.tcx(), trait_ref)).unwrap(); let func = bx.type_checked_load(llvtable, vtable_byte_offset, typeid); return func; diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 43b8230c679e1..23baab3124ec6 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -2,7 +2,7 @@ //! which do not. use rustc_data_structures::graph::dominators::Dominators; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{self, DefLocation, Location, TerminatorKind, traversal}; @@ -16,7 +16,7 @@ use crate::traits::*; pub(crate) fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( fx: &FunctionCx<'a, 'tcx, Bx>, traversal_order: &[mir::BasicBlock], -) -> BitSet { +) -> DenseBitSet { let mir = fx.mir; let dominators = mir.basic_blocks.dominators(); let locals = mir @@ -44,7 +44,7 @@ pub(crate) fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( analyzer.visit_basic_block_data(bb, data); } - let mut non_ssa_locals = BitSet::new_empty(analyzer.locals.len()); + let mut non_ssa_locals = DenseBitSet::new_empty(analyzer.locals.len()); for (local, kind) in analyzer.locals.iter_enumerated() { if matches!(kind, LocalKind::Memory) { non_ssa_locals.insert(local); diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index b0a1dedd646b0..4630ed48c520e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1,6 +1,6 @@ use std::cmp; -use rustc_abi::{self as abi, ExternAbi, HasDataLayout, WrappingRange}; +use rustc_abi::{BackendRepr, ExternAbi, HasDataLayout, Reg, WrappingRange}; use rustc_ast as ast; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_hir::lang_items::LangItem; @@ -14,7 +14,7 @@ use rustc_middle::{bug, span_bug}; use rustc_session::config::OptLevel; use rustc_span::source_map::Spanned; use rustc_span::{Span, sym}; -use rustc_target::callconv::{ArgAbi, FnAbi, PassMode, Reg}; +use rustc_target::callconv::{ArgAbi, FnAbi, PassMode}; use tracing::{debug, info}; use super::operand::OperandRef; @@ -713,6 +713,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // and `#[track_caller]` adds an implicit third argument. (LangItem::PanicMisalignedPointerDereference, vec![required, found, location]) } + AssertKind::NullPointerDereference => { + // It's `fn panic_null_pointer_dereference()`, + // `#[track_caller]` adds an implicit argument. + (LangItem::PanicNullPointerDereference, vec![location]) + } _ => { // It's `pub fn panic_...()` and `#[track_caller]` adds an implicit argument. (msg.panic_function(), vec![location]) @@ -1008,7 +1013,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // // This is also relevant for `Pin<&mut Self>`, where we need to peel the // `Pin`. - while !op.layout.ty.is_unsafe_ptr() && !op.layout.ty.is_ref() { + while !op.layout.ty.is_raw_ptr() && !op.layout.ty.is_ref() { let (idx, _) = op.layout.non_1zst_field(bx).expect( "not exactly one non-1-ZST field in a `DispatchFromDyn` type", ); @@ -1040,7 +1045,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } Immediate(_) => { // See comment above explaining why we peel these newtypes - while !op.layout.ty.is_unsafe_ptr() && !op.layout.ty.is_ref() { + while !op.layout.ty.is_raw_ptr() && !op.layout.ty.is_ref() { let (idx, _) = op.layout.non_1zst_field(bx).expect( "not exactly one non-1-ZST field in a `DispatchFromDyn` type", ); @@ -1540,7 +1545,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // the load would just produce `OperandValue::Ref` instead // of the `OperandValue::Immediate` we need for the call. llval = bx.load(bx.backend_type(arg.layout), llval, align); - if let abi::BackendRepr::Scalar(scalar) = arg.layout.backend_repr { + if let BackendRepr::Scalar(scalar) = arg.layout.backend_repr { if scalar.is_bool() { bx.range_metadata(llval, WrappingRange { start: 0, end: 1 }); } @@ -1599,10 +1604,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if let Some(slot) = self.personality_slot { slot } else { - let layout = cx.layout_of(Ty::new_tup(cx.tcx(), &[ - Ty::new_mut_ptr(cx.tcx(), cx.tcx().types.u8), - cx.tcx().types.i32, - ])); + let layout = cx.layout_of(Ty::new_tup( + cx.tcx(), + &[Ty::new_mut_ptr(cx.tcx(), cx.tcx().types.u8), cx.tcx().types.i32], + )); let slot = PlaceRef::alloca(bx, layout); self.personality_slot = Some(slot); slot @@ -1703,15 +1708,32 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let mut cs_bx = Bx::build(self.cx, llbb); let cs = cs_bx.catch_switch(None, None, &[cp_llbb]); - // The "null" here is actually a RTTI type descriptor for the - // C++ personality function, but `catch (...)` has no type so - // it's null. The 64 here is actually a bitfield which - // represents that this is a catch-all block. bx = Bx::build(self.cx, cp_llbb); let null = bx.const_null(bx.type_ptr_ext(bx.cx().data_layout().instruction_address_space)); - let sixty_four = bx.const_i32(64); - funclet = Some(bx.catch_pad(cs, &[null, sixty_four, null])); + + // The `null` in first argument here is actually a RTTI type + // descriptor for the C++ personality function, but `catch (...)` + // has no type so it's null. + let args = if base::wants_msvc_seh(self.cx.sess()) { + // This bitmask is a single `HT_IsStdDotDot` flag, which + // represents that this is a C++-style `catch (...)` block that + // only captures programmatic exceptions, not all SEH + // exceptions. The second `null` points to a non-existent + // `alloca` instruction, which an LLVM pass would inline into + // the initial SEH frame allocation. + let adjectives = bx.const_i32(0x40); + &[null, adjectives, null] as &[_] + } else { + // Specifying more arguments than necessary usually doesn't + // hurt, but the `WasmEHPrepare` LLVM pass does not recognize + // anything other than a single `null` as a `catch (...)` block, + // leading to problems down the line during instruction + // selection. + &[null] as &[_] + }; + + funclet = Some(bx.catch_pad(cs, args)); } else { llbb = Bx::append_block(self.cx, self.llfn, "terminate"); bx = Bx::build(self.cx, llbb); diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index 7676e1e171aac..eafc551501c13 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -43,7 +43,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Const::Ty(_, c) => match c.kind() { // A constant that came from a const generic but was then used as an argument to // old-style simd_shuffle (passing as argument instead of as a generic param). - rustc_type_ir::ConstKind::Value(_, valtree) => return Ok(Ok(valtree)), + rustc_type_ir::ConstKind::Value(cv) => return Ok(Ok(cv.valtree)), other => span_bug!(constant.span, "{other:#?}"), }, // We should never encounter `Const::Val` unless MIR opts (like const prop) evaluate diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 843a996d2bfe5..5924c8991ad64 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -19,9 +19,7 @@ use crate::traits::*; pub struct FunctionDebugContext<'tcx, S, L> { /// Maps from source code to the corresponding debug info scope. - /// May be None if the backend is not capable of representing the scope for - /// some reason. - pub scopes: IndexVec>>, + pub scopes: IndexVec>, /// Maps from an inlined function to its debug info declaration. pub inlined_function_scopes: FxHashMap, S>, @@ -232,7 +230,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &self, source_info: mir::SourceInfo, ) -> Option<(Bx::DIScope, Option, Span)> { - let scope = &self.debug_context.as_ref()?.scopes[source_info.scope]?; + let scope = &self.debug_context.as_ref()?.scopes[source_info.scope]; let span = hygiene::walk_chain_collapsed(source_info.span, self.mir.span); Some((scope.adjust_dbg_scope_for_span(self.cx, span), scope.inlined_at, span)) } diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 304ac4544ee43..b34e966ba6ce6 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -225,6 +225,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args[1].val.unaligned_volatile_store(bx, dst); return Ok(()); } + sym::disjoint_bitor => { + let a = args[0].immediate(); + let b = args[1].immediate(); + bx.or_disjoint(a, b) + } sym::exact_div => { let ty = arg_tys[0]; match int_type_width_signed(ty, bx.tcx()) { @@ -362,7 +367,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.sess().dcx().emit_fatal(errors::AtomicCompareExchange); }; let ty = fn_args.type_at(0); - if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_unsafe_ptr() { + if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr() { let weak = instruction == "cxchgweak"; let dst = args[0].immediate(); let cmp = args[1].immediate(); @@ -390,7 +395,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { "load" => { let ty = fn_args.type_at(0); - if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_unsafe_ptr() { + if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr() { let layout = bx.layout_of(ty); let size = layout.size; let source = args[0].immediate(); @@ -408,7 +413,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { "store" => { let ty = fn_args.type_at(0); - if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_unsafe_ptr() { + if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr() { let size = bx.layout_of(ty).size; let val = args[1].immediate(); let ptr = args[0].immediate(); @@ -453,7 +458,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }; let ty = fn_args.type_at(0); - if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_unsafe_ptr() { + if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr() { let ptr = args[0].immediate(); let val = args[1].immediate(); bx.atomic_rmw(atom_op, ptr, val, parse_ordering(bx, ordering)) diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 62f69af3f2f75..3a896071bc6b8 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -1,7 +1,7 @@ use std::iter; use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::{UnwindTerminateReason, traversal}; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, TyAndLayout}; @@ -293,7 +293,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // So drop the builder of `start_llbb` to avoid having two at the same time. drop(start_bx); - let mut unreached_blocks = BitSet::new_filled(mir.basic_blocks.len()); + let mut unreached_blocks = DenseBitSet::new_filled(mir.basic_blocks.len()); // Codegen the body of each reachable block using our reverse postorder list. for bb in traversal_order { fx.codegen_block(bb); @@ -316,7 +316,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, fx: &mut FunctionCx<'a, 'tcx, Bx>, - memory_locals: &BitSet, + memory_locals: &DenseBitSet, ) -> Vec> { let mir = fx.mir; let mut idx = 0; diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index cac3cc587cb45..eb0711dbb32ff 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -1,10 +1,14 @@ +use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind}; use rustc_attr_parsing::InstructionSetAttr; +use rustc_hir::def_id::DefId; use rustc_middle::mir::mono::{Linkage, MonoItem, MonoItemData, Visibility}; use rustc_middle::mir::{Body, InlineAsmOperand}; -use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf}; -use rustc_middle::ty::{Instance, TyCtxt}; -use rustc_middle::{bug, ty}; +use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, LayoutOf}; +use rustc_middle::ty::{Instance, Ty, TyCtxt}; +use rustc_middle::{bug, span_bug, ty}; use rustc_span::sym; +use rustc_target::callconv::{ArgAbi, FnAbi, PassMode}; +use rustc_target::spec::WasmCAbi; use crate::common; use crate::traits::{AsmCodegenMethods, BuilderMethods, GlobalAsmOperandRef, MiscCodegenMethods}; @@ -32,7 +36,8 @@ pub(crate) fn codegen_naked_asm<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let item_data = cx.codegen_unit().items().get(&MonoItem::Fn(instance)).unwrap(); let name = cx.mangled_name(instance); - let (begin, end) = prefix_and_suffix(cx.tcx(), instance, &name, item_data); + let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty()); + let (begin, end) = prefix_and_suffix(cx.tcx(), instance, &name, item_data, fn_abi); let mut template_vec = Vec::new(); template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(begin.into())); @@ -103,6 +108,7 @@ enum AsmBinaryFormat { Elf, Macho, Coff, + Wasm, } impl AsmBinaryFormat { @@ -111,6 +117,8 @@ impl AsmBinaryFormat { Self::Coff } else if target.is_like_osx { Self::Macho + } else if target.is_like_wasm { + Self::Wasm } else { Self::Elf } @@ -122,6 +130,7 @@ fn prefix_and_suffix<'tcx>( instance: Instance<'tcx>, asm_name: &str, item_data: &MonoItemData, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, ) -> (String, String) { use std::fmt::Write; @@ -132,9 +141,13 @@ fn prefix_and_suffix<'tcx>( let attrs = tcx.codegen_fn_attrs(instance.def_id()); let link_section = attrs.link_section.map(|symbol| symbol.as_str().to_string()); - let align = attrs.alignment.map(|a| a.bytes()).unwrap_or(4); - // See https://sourceware.org/binutils/docs/as/ARM-Directives.html for info on these directives. + // function alignment can be set globally with the `-Zmin-function-alignment=` flag; + // the alignment from a `#[repr(align())]` is used if it specifies a higher alignment. + // if no alignment is specified, an alignment of 4 bytes is used. + let min_function_alignment = tcx.sess.opts.unstable_opts.min_function_alignment; + let align = Ord::max(min_function_alignment, attrs.alignment).map(|a| a.bytes()).unwrap_or(4); + // In particular, `.arm` can also be written `.code 32` and `.thumb` as `.code 16`. let (arch_prefix, arch_suffix) = if is_arm { ( @@ -165,7 +178,7 @@ fn prefix_and_suffix<'tcx>( } Linkage::LinkOnceAny | Linkage::LinkOnceODR | Linkage::WeakAny | Linkage::WeakODR => { match asm_binary_format { - AsmBinaryFormat::Elf | AsmBinaryFormat::Coff => { + AsmBinaryFormat::Elf | AsmBinaryFormat::Coff | AsmBinaryFormat::Wasm => { writeln!(w, ".weak {asm_name}")?; } AsmBinaryFormat::Macho => { @@ -174,10 +187,9 @@ fn prefix_and_suffix<'tcx>( } } } - Linkage::Internal | Linkage::Private => { + Linkage::Internal => { // write nothing } - Linkage::Appending => emit_fatal("Only global variables can have appending linkage!"), Linkage::Common => emit_fatal("Functions may not have common linkage"), Linkage::AvailableExternally => { // this would make the function equal an extern definition @@ -260,7 +272,161 @@ fn prefix_and_suffix<'tcx>( writeln!(end, "{}", arch_suffix).unwrap(); } } + AsmBinaryFormat::Wasm => { + let section = link_section.unwrap_or(format!(".text.{asm_name}")); + + writeln!(begin, ".section {section},\"\",@").unwrap(); + // wasm functions cannot be aligned, so skip + write_linkage(&mut begin).unwrap(); + if let Visibility::Hidden = item_data.visibility { + writeln!(begin, ".hidden {asm_name}").unwrap(); + } + writeln!(begin, ".type {asm_name}, @function").unwrap(); + if !arch_prefix.is_empty() { + writeln!(begin, "{}", arch_prefix).unwrap(); + } + writeln!(begin, "{asm_name}:").unwrap(); + writeln!( + begin, + ".functype {asm_name} {}", + wasm_functype(tcx, fn_abi, instance.def_id()) + ) + .unwrap(); + + writeln!(end).unwrap(); + // .size is ignored for function symbols, so we can skip it + writeln!(end, "end_function").unwrap(); + } } (begin, end) } + +/// The webassembly type signature for the given function. +/// +/// Used by the `.functype` directive on wasm targets. +fn wasm_functype<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, def_id: DefId) -> String { + let mut signature = String::with_capacity(64); + + let ptr_type = match tcx.data_layout.pointer_size.bits() { + 32 => "i32", + 64 => "i64", + other => bug!("wasm pointer size cannot be {other} bits"), + }; + + // FIXME: remove this once the wasm32-unknown-unknown ABI is fixed + // please also add `wasm32-unknown-unknown` back in `tests/assembly/wasm32-naked-fn.rs` + // basically the commit introducing this comment should be reverted + if let PassMode::Pair { .. } = fn_abi.ret.mode { + let _ = WasmCAbi::Legacy; + span_bug!( + tcx.def_span(def_id), + "cannot return a pair (the wasm32-unknown-unknown ABI is broken, see https://github.com/rust-lang/rust/issues/115666" + ); + } + + let hidden_return = matches!(fn_abi.ret.mode, PassMode::Indirect { .. }); + + signature.push('('); + + if hidden_return { + signature.push_str(ptr_type); + if !fn_abi.args.is_empty() { + signature.push_str(", "); + } + } + + let mut it = fn_abi.args.iter().peekable(); + while let Some(arg_abi) = it.next() { + wasm_type(tcx, &mut signature, arg_abi, ptr_type, def_id); + if it.peek().is_some() { + signature.push_str(", "); + } + } + + signature.push_str(") -> ("); + + if !hidden_return { + wasm_type(tcx, &mut signature, &fn_abi.ret, ptr_type, def_id); + } + + signature.push(')'); + + signature +} + +fn wasm_type<'tcx>( + tcx: TyCtxt<'tcx>, + signature: &mut String, + arg_abi: &ArgAbi<'_, Ty<'tcx>>, + ptr_type: &'static str, + def_id: DefId, +) { + match arg_abi.mode { + PassMode::Ignore => { /* do nothing */ } + PassMode::Direct(_) => { + let direct_type = match arg_abi.layout.backend_repr { + BackendRepr::Scalar(scalar) => wasm_primitive(scalar.primitive(), ptr_type), + BackendRepr::Vector { .. } => "v128", + BackendRepr::Memory { .. } => { + // FIXME: remove this branch once the wasm32-unknown-unknown ABI is fixed + let _ = WasmCAbi::Legacy; + span_bug!( + tcx.def_span(def_id), + "cannot use memory args (the wasm32-unknown-unknown ABI is broken, see https://github.com/rust-lang/rust/issues/115666" + ); + } + other => unreachable!("unexpected BackendRepr: {:?}", other), + }; + + signature.push_str(direct_type); + } + PassMode::Pair(_, _) => match arg_abi.layout.backend_repr { + BackendRepr::ScalarPair(a, b) => { + signature.push_str(wasm_primitive(a.primitive(), ptr_type)); + signature.push_str(", "); + signature.push_str(wasm_primitive(b.primitive(), ptr_type)); + } + other => unreachable!("{other:?}"), + }, + PassMode::Cast { pad_i32, ref cast } => { + // For wasm, Cast is used for single-field primitive wrappers like `struct Wrapper(i64);` + assert!(!pad_i32, "not currently used by wasm calling convention"); + assert!(cast.prefix[0].is_none(), "no prefix"); + assert_eq!(cast.rest.total, arg_abi.layout.size, "single item"); + + let wrapped_wasm_type = match cast.rest.unit.kind { + RegKind::Integer => match cast.rest.unit.size.bytes() { + ..=4 => "i32", + ..=8 => "i64", + _ => ptr_type, + }, + RegKind::Float => match cast.rest.unit.size.bytes() { + ..=4 => "f32", + ..=8 => "f64", + _ => ptr_type, + }, + RegKind::Vector => "v128", + }; + + signature.push_str(wrapped_wasm_type); + } + PassMode::Indirect { .. } => signature.push_str(ptr_type), + } +} + +fn wasm_primitive(primitive: Primitive, ptr_type: &'static str) -> &'static str { + match primitive { + Primitive::Int(integer, _) => match integer { + Integer::I8 | Integer::I16 | Integer::I32 => "i32", + Integer::I64 => "i64", + Integer::I128 => "i64, i64", + }, + Primitive::Float(float) => match float { + Float::F16 | Float::F32 => "f32", + Float::F64 => "f64", + Float::F128 => "i64, i64", + }, + Primitive::Pointer(_) => ptr_type, + } +} diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index c634f864ffb89..eb4270ffe8096 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -474,10 +474,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { LocalRef::Operand(..) => { if place_ref.is_indirect_first_projection() { base = 1; - let cg_base = self.codegen_consume(bx, mir::PlaceRef { - projection: &place_ref.projection[..0], - ..place_ref - }); + let cg_base = self.codegen_consume( + bx, + mir::PlaceRef { projection: &place_ref.projection[..0], ..place_ref }, + ); cg_base.deref(bx.cx()) } else { bug!("using operand local {:?} as place", place_ref); @@ -502,6 +502,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bug!("encountered OpaqueCast({ty}) in codegen") } mir::ProjectionElem::Subtype(ty) => cg_base.project_type(bx, self.monomorphize(ty)), + mir::ProjectionElem::UnwrapUnsafeBinder(ty) => { + cg_base.project_type(bx, self.monomorphize(ty)) + } mir::ProjectionElem::Index(index) => { let index = &mir::Operand::Copy(mir::Place::from(index)); let index = self.codegen_operand(bx, index); diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 3b62148abb789..63f421243acfe 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -10,9 +10,9 @@ use rustc_session::config::OptLevel; use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; -use super::FunctionCx; use super::operand::{OperandRef, OperandValue}; use super::place::PlaceRef; +use super::{FunctionCx, LocalRef}; use crate::common::IntPredicate; use crate::traits::*; use crate::{MemFlags, base}; @@ -93,23 +93,37 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return; } - if let OperandValue::Immediate(v) = cg_elem.val { + let try_init_all_same = |bx: &mut Bx, v| { let start = dest.val.llval; let size = bx.const_usize(dest.layout.size.bytes()); - // Use llvm.memset.p0i8.* to initialize all zero arrays - if bx.cx().const_to_opt_u128(v, false) == Some(0) { - let fill = bx.cx().const_u8(0); - bx.memset(start, fill, size, dest.val.align, MemFlags::empty()); - return; + // Use llvm.memset.p0i8.* to initialize all same byte arrays + if let Some(int) = bx.cx().const_to_opt_u128(v, false) { + let bytes = &int.to_le_bytes()[..cg_elem.layout.size.bytes_usize()]; + let first = bytes[0]; + if bytes[1..].iter().all(|&b| b == first) { + let fill = bx.cx().const_u8(first); + bx.memset(start, fill, size, dest.val.align, MemFlags::empty()); + return true; + } } // Use llvm.memset.p0i8.* to initialize byte arrays let v = bx.from_immediate(v); if bx.cx().val_ty(v) == bx.cx().type_i8() { bx.memset(start, v, size, dest.val.align, MemFlags::empty()); - return; + return true; + } + false + }; + + match cg_elem.val { + OperandValue::Immediate(v) => { + if try_init_all_same(bx, v) { + return; + } } + _ => (), } let count = self @@ -351,10 +365,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { use abi::Primitive::*; imm = bx.from_immediate(imm); - // When scalars are passed by value, there's no metadata recording their - // valid ranges. For example, `char`s are passed as just `i32`, with no - // way for LLVM to know that they're 0x10FFFF at most. Thus we assume - // the range of the input value too, not just the output range. + // If we have a scalar, we must already know its range. Either + // + // 1) It's a parameter with `range` parameter metadata, + // 2) It's something we `load`ed with `!range` metadata, or + // 3) After a transmute we `assume`d the range (see below). + // + // That said, last time we tried removing this, it didn't actually help + // the rustc-perf results, so might as well keep doing it + // self.assume_scalar_range(bx, imm, from_scalar, from_backend_ty); imm = match (from_scalar.primitive(), to_scalar.primitive()) { @@ -375,7 +394,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.bitcast(int_imm, to_backend_ty) } }; + + // This `assume` remains important for cases like (a conceptual) + // transmute::(x) == 0 + // since it's never passed to something with parameter metadata (especially + // after MIR inlining) so the only way to tell the backend about the + // constraint that the `transmute` introduced is to `assume` it. self.assume_scalar_range(bx, imm, to_scalar, to_backend_ty); + imm = bx.to_immediate_scalar(imm, to_scalar); imm } @@ -387,40 +413,21 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { scalar: abi::Scalar, backend_ty: Bx::Type, ) { - if matches!(self.cx.sess().opts.optimize, OptLevel::No) - // For now, the critical niches are all over `Int`eger values. - // Should floating-point values or pointers ever get more complex - // niches, then this code will probably want to handle them too. - || !matches!(scalar.primitive(), abi::Primitive::Int(..)) - || scalar.is_always_valid(self.cx) - { + if matches!(self.cx.sess().opts.optimize, OptLevel::No) || scalar.is_always_valid(self.cx) { return; } - let abi::WrappingRange { start, end } = scalar.valid_range(self.cx); - - if start <= end { - if start > 0 { - let low = bx.const_uint_big(backend_ty, start); - let cmp = bx.icmp(IntPredicate::IntUGE, imm, low); - bx.assume(cmp); + match scalar.primitive() { + abi::Primitive::Int(..) => { + let range = scalar.valid_range(self.cx); + bx.assume_integer_range(imm, backend_ty, range); } - - let type_max = scalar.size(self.cx).unsigned_int_max(); - if end < type_max { - let high = bx.const_uint_big(backend_ty, end); - let cmp = bx.icmp(IntPredicate::IntULE, imm, high); - bx.assume(cmp); + abi::Primitive::Pointer(abi::AddressSpace::DATA) + if !scalar.valid_range(self.cx).contains(0) => + { + bx.assume_nonnull(imm); } - } else { - let low = bx.const_uint_big(backend_ty, start); - let cmp_low = bx.icmp(IntPredicate::IntUGE, imm, low); - - let high = bx.const_uint_big(backend_ty, end); - let cmp_high = bx.icmp(IntPredicate::IntULE, imm, high); - - let or = bx.or(cmp_low, cmp_high); - bx.assume(or); + abi::Primitive::Pointer(..) | abi::Primitive::Float(..) => {} } } @@ -587,12 +594,21 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::CopyForDeref(place) => { self.codegen_operand(bx, &mir::Operand::Copy(place)) } - mir::Rvalue::RawPtr(mutability, place) => { - let mk_ptr = - move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| Ty::new_ptr(tcx, ty, mutability); + mir::Rvalue::RawPtr(kind, place) => { + let mk_ptr = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| { + Ty::new_ptr(tcx, ty, kind.to_mutbl_lossy()) + }; self.codegen_place_to_pointer(bx, place, mk_ptr) } + mir::Rvalue::Len(place) => { + let size = self.evaluate_array_len(bx, place); + OperandRef { + val: OperandValue::Immediate(size), + layout: bx.cx().layout_of(bx.tcx().types.usize), + } + } + mir::Rvalue::BinaryOp(op_with_overflow, box (ref lhs, ref rhs)) if let Some(op) = op_with_overflow.overflowing_to_wrapping() => { @@ -655,7 +671,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { (OperandValue::Immediate(llval), operand.layout) } mir::UnOp::PtrMetadata => { - assert!(operand.layout.ty.is_unsafe_ptr() || operand.layout.ty.is_ref(),); + assert!(operand.layout.ty.is_raw_ptr() || operand.layout.ty.is_ref(),); let (_, meta) = operand.val.pointer_parts(); assert_eq!(operand.layout.fields.count() > 1, meta.is_some()); if let Some(meta) = meta { @@ -707,6 +723,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let val = bx.tcx().sess.ub_checks(); bx.cx().const_bool(val) } + mir::NullOp::ContractChecks => { + let val = bx.tcx().sess.contract_checks(); + bx.cx().const_bool(val) + } }; let tcx = self.cx.tcx(); OperandRef { @@ -789,7 +809,31 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandRef { val: OperandValue::Immediate(val), layout: box_layout } } + mir::Rvalue::WrapUnsafeBinder(ref operand, binder_ty) => { + let operand = self.codegen_operand(bx, operand); + let binder_ty = self.monomorphize(binder_ty); + let layout = bx.cx().layout_of(binder_ty); + OperandRef { val: operand.val, layout } + } + } + } + + fn evaluate_array_len(&mut self, bx: &mut Bx, place: mir::Place<'tcx>) -> Bx::Value { + // ZST are passed as operands and require special handling + // because codegen_place() panics if Local is operand. + if let Some(index) = place.as_local() { + if let LocalRef::Operand(op) = self.locals[index] { + if let ty::Array(_, n) = op.layout.ty.kind() { + let n = n + .try_to_target_usize(bx.tcx()) + .expect("expected monomorphic const in codegen"); + return bx.cx().const_usize(n); + } + } } + // use common size calculation for non zero-sized types + let cg_value = self.codegen_place(bx, place.as_ref()); + cg_value.len(bx.cx()) } /// Codegen an `Rvalue::RawPtr` or `Rvalue::Ref` @@ -1063,6 +1107,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::Ref(..) | mir::Rvalue::CopyForDeref(..) | mir::Rvalue::RawPtr(..) | + mir::Rvalue::Len(..) | mir::Rvalue::Cast(..) | // (*) mir::Rvalue::ShallowInitBox(..) | // (*) mir::Rvalue::BinaryOp(..) | @@ -1070,7 +1115,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::Discriminant(..) | mir::Rvalue::NullaryOp(..) | mir::Rvalue::ThreadLocalRef(_) | - mir::Rvalue::Use(..) => // (*) + mir::Rvalue::Use(..) | + mir::Rvalue::WrapUnsafeBinder(..) => // (*) true, // Arrays are always aggregates, so it's not worth checking anything here. // (If it's really `[(); N]` or `[T; 0]` and we use the place path, fine.) diff --git a/compiler/rustc_codegen_ssa/src/size_of_val.rs b/compiler/rustc_codegen_ssa/src/size_of_val.rs index 71a2f916db5a5..ac2366340fb79 100644 --- a/compiler/rustc_codegen_ssa/src/size_of_val.rs +++ b/compiler/rustc_codegen_ssa/src/size_of_val.rs @@ -45,14 +45,9 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // The info in this case is the length of the str, so the size is that // times the unit size. ( - // All slice sizes must fit into `isize`, so this multiplication cannot (signed) - // wrap. - // NOTE: ideally, we want the effects of both `unchecked_smul` and `unchecked_umul` - // (resulting in `mul nsw nuw` in LLVM IR), since we know that the multiplication - // cannot signed wrap, and that both operands are non-negative. But at the time of - // writing, the `LLVM-C` binding can't do this, and it doesn't seem to enable any - // further optimizations. - bx.unchecked_smul(info.unwrap(), bx.const_usize(unit.size.bytes())), + // All slice sizes must fit into `isize`, so this multiplication cannot + // wrap -- neither signed nor unsigned. + bx.unchecked_sumul(info.unwrap(), bx.const_usize(unit.size.bytes())), bx.const_usize(unit.align.abi.bytes()), ) } diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 7e80d014ea282..95a5e96fe46e4 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -10,7 +10,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::parse::feature_err; use rustc_span::{Span, Symbol, sym}; -use rustc_target::target_features; +use rustc_target::target_features::{self, Stability}; use crate::errors; @@ -19,7 +19,7 @@ use crate::errors; pub(crate) fn from_target_feature_attr( tcx: TyCtxt<'_>, attr: &hir::Attribute, - rust_target_features: &UnordMap, + rust_target_features: &UnordMap, target_features: &mut Vec, ) { let Some(list) = attr.meta_item_list() else { return }; @@ -32,7 +32,7 @@ pub(crate) fn from_target_feature_attr( .emit(); }; let rust_features = tcx.features(); - let mut added_target_features = Vec::new(); + let abi_feature_constraints = tcx.sess.target.abi_required_features(); for item in list { // Only `enable = ...` is accepted in the meta-item list. if !item.has_name(sym::enable) { @@ -47,7 +47,7 @@ pub(crate) fn from_target_feature_attr( }; // We allow comma separation to enable multiple features. - added_target_features.extend(value.as_str().split(',').filter_map(|feature| { + for feature in value.as_str().split(',') { let Some(stability) = rust_target_features.get(feature) else { let msg = format!("the feature named `{feature}` is not valid for this target"); let mut err = tcx.dcx().struct_span_err(item.span(), msg); @@ -59,12 +59,12 @@ pub(crate) fn from_target_feature_attr( } } err.emit(); - return None; + continue; }; // Only allow target features whose feature gates have been enabled // and which are permitted to be toggled. - if let Err(reason) = stability.toggle_allowed(/*enable*/ true) { + if let Err(reason) = stability.toggle_allowed() { tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr { span: item.span(), feature, @@ -80,31 +80,30 @@ pub(crate) fn from_target_feature_attr( format!("the target feature `{feature}` is currently unstable"), ) .emit(); + } else { + // Add this and the implied features. + let feature_sym = Symbol::intern(feature); + for &name in tcx.implied_target_features(feature_sym) { + // But ensure the ABI does not forbid enabling this. + // Here we do assume that LLVM doesn't add even more implied features + // we don't know about, at least no features that would have ABI effects! + // We skip this logic in rustdoc, where we want to allow all target features of + // all targets, so we can't check their ABI compatibility and anyway we are not + // generating code so "it's fine". + if !tcx.sess.opts.actually_rustdoc { + if abi_feature_constraints.incompatible.contains(&name.as_str()) { + tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr { + span: item.span(), + feature: name.as_str(), + reason: "this feature is incompatible with the target ABI", + }); + } + } + target_features.push(TargetFeature { name, implied: name != feature_sym }) + } } - Some(Symbol::intern(feature)) - })); - } - - // Add explicit features - target_features.extend( - added_target_features.iter().copied().map(|name| TargetFeature { name, implied: false }), - ); - - // Add implied features - let mut implied_target_features = UnordSet::new(); - for feature in added_target_features.iter() { - implied_target_features.extend(tcx.implied_target_features(*feature).clone()); - } - for feature in added_target_features.iter() { - implied_target_features.remove(feature); + } } - target_features.extend( - implied_target_features - .into_sorted_stable_ord() - .iter() - .copied() - .map(|name| TargetFeature { name, implied: true }), - ) } /// Computes the set of target features used in a function for the purposes of @@ -147,25 +146,55 @@ pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { rust_target_features: |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); - let target = &tcx.sess.target; if tcx.sess.opts.actually_rustdoc { - // rustdoc needs to be able to document functions that use all the features, so - // whitelist them all - rustc_target::target_features::all_rust_features() - .map(|(a, b)| (a.to_string(), b.compute_toggleability(target))) - .collect() + // HACK: rustdoc would like to pretend that we have all the target features, so we + // have to merge all the lists into one. To ensure an unstable target never prevents + // a stable one from working, we merge the stability info of all instances of the + // same target feature name, with the "most stable" taking precedence. And then we + // hope that this doesn't cause issues anywhere else in the compiler... + let mut result: UnordMap = Default::default(); + for (name, stability) in rustc_target::target_features::all_rust_features() { + use std::collections::hash_map::Entry; + match result.entry(name.to_owned()) { + Entry::Vacant(vacant_entry) => { + vacant_entry.insert(stability); + } + Entry::Occupied(mut occupied_entry) => { + // Merge the two stabilities, "more stable" taking precedence. + match (occupied_entry.get(), stability) { + (Stability::Stable, _) + | ( + Stability::Unstable { .. }, + Stability::Unstable { .. } | Stability::Forbidden { .. }, + ) + | (Stability::Forbidden { .. }, Stability::Forbidden { .. }) => { + // The stability in the entry is at least as good as the new one, just keep it. + } + _ => { + // Overwrite stabilite. + occupied_entry.insert(stability); + } + } + } + } + } + result } else { tcx.sess .target .rust_target_features() .iter() - .map(|(a, b, _)| (a.to_string(), b.compute_toggleability(target))) + .map(|(a, b, _)| (a.to_string(), *b)) .collect() } }, - implied_target_features: |tcx, feature| { + implied_target_features: |tcx, feature: Symbol| { + let feature = feature.as_str(); UnordSet::from(tcx.sess.target.implied_target_features(std::iter::once(feature))) .into_sorted_stable_ord() + .into_iter() + .map(|s| Symbol::intern(s)) + .collect() }, asm_target_features, ..*providers diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 3ee13b19f665f..2c843e2f5e460 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -159,14 +159,42 @@ pub trait BuilderMethods<'a, 'tcx>: /// must be interpreted as unsigned and can be assumed to be less than the size of the left /// operand. fn ashr(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; - fn unchecked_sadd(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; - fn unchecked_uadd(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; - fn unchecked_ssub(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; - fn unchecked_usub(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; - fn unchecked_smul(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; - fn unchecked_umul(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + fn unchecked_sadd(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.add(lhs, rhs) + } + fn unchecked_uadd(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.add(lhs, rhs) + } + fn unchecked_suadd(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.unchecked_sadd(lhs, rhs) + } + fn unchecked_ssub(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.sub(lhs, rhs) + } + fn unchecked_usub(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.sub(lhs, rhs) + } + fn unchecked_susub(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.unchecked_ssub(lhs, rhs) + } + fn unchecked_smul(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.mul(lhs, rhs) + } + fn unchecked_umul(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.mul(lhs, rhs) + } + fn unchecked_sumul(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + // Which to default to is a fairly arbitrary choice, + // but this is what slice layout was using before. + self.unchecked_smul(lhs, rhs) + } fn and(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn or(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; + /// Defaults to [`Self::or`], but guarantees `(lhs & rhs) == 0` so some backends + /// can emit something more helpful for optimizations. + fn or_disjoint(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value { + self.or(lhs, rhs) + } fn xor(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value; fn neg(&mut self, v: Self::Value) -> Self::Value; fn fneg(&mut self, v: Self::Value) -> Self::Value; @@ -217,6 +245,40 @@ pub trait BuilderMethods<'a, 'tcx>: dest: PlaceRef<'tcx, Self::Value>, ); + /// Emits an `assume` that the integer value `imm` of type `ty` is contained in `range`. + /// + /// This *always* emits the assumption, so you probably want to check the + /// optimization level and `Scalar::is_always_valid` before calling it. + fn assume_integer_range(&mut self, imm: Self::Value, ty: Self::Type, range: WrappingRange) { + let WrappingRange { start, end } = range; + + // Perhaps one day we'll be able to use assume operand bundles for this, + // but for now this encoding with a single icmp+assume is best per + // + let shifted = if start == 0 { + imm + } else { + let low = self.const_uint_big(ty, start); + self.sub(imm, low) + }; + let width = self.const_uint_big(ty, u128::wrapping_sub(end, start)); + let cmp = self.icmp(IntPredicate::IntULE, shifted, width); + self.assume(cmp); + } + + /// Emits an `assume` that the `val` of pointer type is non-null. + /// + /// You may want to check the optimization level before bothering calling this. + fn assume_nonnull(&mut self, val: Self::Value) { + // Arguably in LLVM it'd be better to emit an assume operand bundle instead + // + // but this works fine for all backends. + + let null = self.const_null(self.type_ptr()); + let is_null = self.icmp(IntPredicate::IntNE, val, null); + self.assume(is_null); + } + fn range_metadata(&mut self, load: Self::Value, range: WrappingRange); fn nonnull_metadata(&mut self, load: Self::Value); diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index 9af463a691af0..dc6b68ceff7d8 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -1,5 +1,5 @@ +use rustc_abi as abi; use rustc_middle::mir::interpret::{ConstAllocation, Scalar}; -use rustc_target::abi; use super::BackendTypes; diff --git a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs index fe135e911fb3f..30d77c206a56f 100644 --- a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs @@ -2,7 +2,7 @@ use std::ops::Range; use rustc_abi::Size; use rustc_middle::mir; -use rustc_middle::ty::{Instance, PolyExistentialTraitRef, Ty}; +use rustc_middle::ty::{ExistentialTraitRef, Instance, Ty}; use rustc_span::{SourceFile, Span, Symbol}; use rustc_target::callconv::FnAbi; @@ -13,7 +13,7 @@ pub trait DebugInfoCodegenMethods<'tcx>: BackendTypes { fn create_vtable_debuginfo( &self, ty: Ty<'tcx>, - trait_ref: Option>, + trait_ref: Option>, vtable: Self::Value, ); diff --git a/compiler/rustc_codegen_ssa/src/traits/misc.rs b/compiler/rustc_codegen_ssa/src/traits/misc.rs index 5b33fd7ab1028..4004947b46485 100644 --- a/compiler/rustc_codegen_ssa/src/traits/misc.rs +++ b/compiler/rustc_codegen_ssa/src/traits/misc.rs @@ -10,11 +10,11 @@ use super::BackendTypes; pub trait MiscCodegenMethods<'tcx>: BackendTypes { fn vtables( &self, - ) -> &RefCell, Option>), Self::Value>>; + ) -> &RefCell, Option>), Self::Value>>; fn apply_vcall_visibility_metadata( &self, _ty: Ty<'tcx>, - _poly_trait_ref: Option>, + _poly_trait_ref: Option>, _vtable: Self::Value, ) { } diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index 6292d321f6bff..c178ebc596e1e 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -1,8 +1,8 @@ -use rustc_abi::{AddressSpace, Float, Integer}; +use rustc_abi::{AddressSpace, Float, Integer, Reg}; use rustc_middle::bug; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, TyAndLayout}; use rustc_middle::ty::{self, Ty}; -use rustc_target::callconv::{ArgAbi, CastTarget, FnAbi, Reg}; +use rustc_target::callconv::{ArgAbi, CastTarget, FnAbi}; use super::BackendTypes; use super::misc::MiscCodegenMethods; diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 51e2255efe142..97fe614aa10cd 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -1,7 +1,6 @@ use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_errors::{DiagCtxtHandle, FatalError}; use rustc_middle::dep_graph::WorkProduct; -use rustc_middle::ty::TyCtxt; use crate::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; use crate::back::write::{CodegenContext, FatLtoInput, ModuleConfig}; @@ -65,7 +64,6 @@ pub trait WriteBackendMethods: 'static + Sized + Clone { fn serialize_module(module: ModuleCodegen) -> (String, Self::ModuleBuffer); fn autodiff( cgcx: &CodegenContext, - tcx: TyCtxt<'_>, module: &ModuleCodegen, diff_fncs: Vec, config: &ModuleConfig, diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 0c2242b810b69..eecc6690f5153 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -12,8 +12,6 @@ const_eval_already_reported = const_eval_assume_false = `assume` called with `false` -const_eval_await_non_const = - cannot convert `{$ty}` into a future in {const_eval_const_context}s const_eval_bounds_check_failed = indexing out of bounds: the len is {$len} but the index is {$index} const_eval_call_nonzero_intrinsic = @@ -23,11 +21,6 @@ const_eval_closure_call = closures need an RFC before allowed to be called in {const_eval_const_context}s const_eval_closure_fndef_not_const = function defined here, but it is not `const` -const_eval_closure_non_const = - cannot call non-const closure in {const_eval_const_context}s - -const_eval_conditionally_const_call = - cannot call conditionally-const {$def_descr} `{$def_path_str}` in {const_eval_const_context}s const_eval_consider_dereferencing = consider dereferencing here @@ -62,10 +55,6 @@ const_eval_dealloc_incorrect_layout = const_eval_dealloc_kind_mismatch = deallocating {$alloc}, which is {$alloc_kind} memory, using {$kind} deallocation operation -const_eval_deref_coercion_non_const = - cannot perform deref coercion on `{$ty}` in {const_eval_const_context}s - .note = attempting to deref into `{$target_ty}` - .target_note = deref defined here const_eval_deref_function_pointer = accessing {$allocation} which contains a function const_eval_deref_vtable_pointer = @@ -109,9 +98,6 @@ const_eval_extern_type_field = `extern type` field does not have a known offset const_eval_fn_ptr_call = function pointers need an RFC before allowed to be called in {const_eval_const_context}s -const_eval_for_loop_into_iter_non_const = - cannot use `for` loop on `{$ty}` in {const_eval_const_context}s - const_eval_frame_note = {$times -> [0] {const_eval_frame_note_inner} *[other] [... {$times} additional calls {const_eval_frame_note_inner} ...] @@ -216,9 +202,6 @@ const_eval_long_running = .label = the const evaluator is currently interpreting this expression .help = the constant being evaluated -const_eval_match_eq_non_const = cannot match on `{$ty}` in {const_eval_const_context}s - .note = `{$ty}` cannot be compared in compile-time, and therefore cannot be used in `match`es - const_eval_max_num_nodes_in_const = maximum number of nodes exceeded in constant {$global_const_id} const_eval_memory_access_test = memory access failed @@ -249,11 +232,26 @@ const_eval_mutable_ref_escaping = If you really want global mutable state, try using an interior mutable `static` or a `static mut`. const_eval_nested_static_in_thread_local = #[thread_local] does not support implicit nested statics, please create explicit static items and refer to them instead + +const_eval_non_const_await = + cannot convert `{$ty}` into a future in {const_eval_const_context}s + +const_eval_non_const_closure = + cannot call {$non_or_conditionally}-const closure in {const_eval_const_context}s + +const_eval_non_const_deref_coercion = + cannot perform {$non_or_conditionally}-const deref coercion on `{$ty}` in {const_eval_const_context}s + .note = attempting to deref into `{$target_ty}` + .target_note = deref defined here + const_eval_non_const_fmt_macro_call = - cannot call non-const formatting macro in {const_eval_const_context}s + cannot call {$non_or_conditionally}-const formatting macro in {const_eval_const_context}s const_eval_non_const_fn_call = - cannot call non-const {$def_descr} `{$def_path_str}` in {const_eval_const_context}s + cannot call {$non_or_conditionally}-const {$def_descr} `{$def_path_str}` in {const_eval_const_context}s + +const_eval_non_const_for_loop_into_iter = + cannot use `for` loop on `{$ty}` in {const_eval_const_context}s const_eval_non_const_impl = impl defined here, but it is not `const` @@ -261,6 +259,20 @@ const_eval_non_const_impl = const_eval_non_const_intrinsic = cannot call non-const intrinsic `{$name}` in {const_eval_const_context}s +const_eval_non_const_match_eq = cannot match on `{$ty}` in {const_eval_const_context}s + .note = `{$ty}` cannot be compared in compile-time, and therefore cannot be used in `match`es + +const_eval_non_const_operator = + cannot call {$non_or_conditionally}-const operator in {const_eval_const_context}s + +const_eval_non_const_question_branch = + `?` is not allowed on `{$ty}` in {const_eval_const_context}s +const_eval_non_const_question_from_residual = + `?` is not allowed on `{$ty}` in {const_eval_const_context}s + +const_eval_non_const_try_block_from_output = + `try` block cannot convert `{$ty}` to the result in {const_eval_const_context}s + const_eval_not_enough_caller_args = calling a function with fewer arguments than it requires @@ -268,7 +280,9 @@ const_eval_nullary_intrinsic_fail = could not evaluate nullary intrinsic const_eval_offset_from_different_allocations = - `{$name}` called on pointers into different allocations + `{$name}` called on two different pointers that are not both derived from the same allocation +const_eval_offset_from_out_of_bounds = + `{$name}` called on two different pointers where the memory range between them is not in-bounds of an allocation const_eval_offset_from_overflow = `{$name}` called when first pointer is too far ahead of second const_eval_offset_from_test = @@ -281,8 +295,6 @@ const_eval_offset_from_unsigned_overflow = *[false] offset } than second: {$a_offset} < {$b_offset} -const_eval_operator_non_const = - cannot call non-const operator in {const_eval_const_context}s const_eval_overflow_arith = arithmetic overflow in `{$intrinsic}` const_eval_overflow_shift = @@ -325,11 +337,6 @@ const_eval_ptr_as_bytes_1 = const_eval_ptr_as_bytes_2 = the absolute address of a pointer is not known at compile-time, so such operations are not supported -const_eval_question_branch_non_const = - `?` is not allowed on `{$ty}` in {const_eval_const_context}s -const_eval_question_from_residual_non_const = - `?` is not allowed on `{$ty}` in {const_eval_const_context}s - const_eval_range = in the range {$lo}..={$hi} const_eval_range_lower = greater or equal to {$lo} const_eval_range_singular = equal to {$lo} @@ -379,8 +386,6 @@ const_eval_too_generic = const_eval_too_many_caller_args = calling a function with more arguments than it expected -const_eval_try_block_from_output_non_const = - `try` block cannot convert `{$ty}` to the result in {const_eval_const_context}s const_eval_unallowed_fn_pointer_call = function pointer calls are not allowed in {const_eval_const_context}s const_eval_unallowed_heap_allocations = @@ -400,7 +405,7 @@ const_eval_uninhabited_enum_variant_read = const_eval_uninhabited_enum_variant_written = writing discriminant of an uninhabited enum variant -const_eval_unmarked_const_fn_exposed = `{$def_path}` cannot be (indirectly) exposed to stable +const_eval_unmarked_const_item_exposed = `{$def_path}` cannot be (indirectly) exposed to stable .help = either mark the callee as `#[rustc_const_stable_indirect]`, or the caller as `#[rustc_const_unstable]` const_eval_unmarked_intrinsic_exposed = intrinsic `{$def_path}` cannot be (indirectly) exposed to stable .help = mark the caller as `#[rustc_const_unstable]`, or mark the intrinsic `#[rustc_intrinsic_const_stable_indirect]` (but this requires team approval) @@ -411,9 +416,10 @@ const_eval_unreachable_unwind = const_eval_unsized_local = unsized locals are not supported const_eval_unstable_const_fn = `{$def_path}` is not yet stable as a const fn +const_eval_unstable_const_trait = `{$def_path}` is not yet stable as a const trait const_eval_unstable_in_stable_exposed = const function that might be (indirectly) exposed to stable cannot use `#[feature({$gate})]` - .is_function_call = mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features + .is_function_call = mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features .unstable_sugg = if the {$is_function_call2 -> [true] caller *[false] function @@ -421,7 +427,7 @@ const_eval_unstable_in_stable_exposed = .bypass_sugg = otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) const_eval_unstable_intrinsic = `{$name}` is not yet stable as a const intrinsic - .help = add `#![feature({$feature})]` to the crate attributes to enable +const_eval_unstable_intrinsic_suggestion = add `#![feature({$feature})]` to the crate attributes to enable const_eval_unterminated_c_string = reading a null-terminated string starting at {$pointer} with no null found before end of allocation diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index e895c44199b81..90002d3f10905 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -8,9 +8,10 @@ use std::ops::Deref; use rustc_attr_parsing::{ConstStability, StabilityLevel}; use rustc_errors::{Diag, ErrorGuaranteed}; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem}; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; @@ -29,12 +30,18 @@ use super::ops::{self, NonConstOp, Status}; use super::qualifs::{self, HasMutInterior, NeedsDrop, NeedsNonConstDrop}; use super::resolver::FlowSensitiveAnalysis; use super::{ConstCx, Qualif}; -use crate::check_consts::is_safe_to_expose_on_stable_const_fn; +use crate::check_consts::is_fn_or_trait_safe_to_expose_on_stable; use crate::errors; type QualifResults<'mir, 'tcx, Q> = rustc_mir_dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'mir, 'mir, 'tcx, Q>>; +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum ConstConditionsHold { + Yes, + No, +} + #[derive(Default)] pub(crate) struct Qualifs<'mir, 'tcx> { has_mut_interior: Option>, @@ -172,7 +179,7 @@ pub struct Checker<'mir, 'tcx> { /// A set that stores for each local whether it is "transient", i.e. guaranteed to be dead /// when this MIR body returns. - transient_locals: Option>, + transient_locals: Option>, error_emitted: Option, secondary_errors: Vec>, @@ -242,7 +249,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { // And then check all `Return` in the MIR, and if a local is "maybe live" at a // `Return` then it is definitely not transient. - let mut transient = BitSet::new_filled(ccx.body.local_decls.len()); + let mut transient = DenseBitSet::new_filled(ccx.body.local_decls.len()); // Make sure to only visit reachable blocks, the dataflow engine can ICE otherwise. for (bb, data) in traversal::reachable(&ccx.body) { if matches!(data.terminator().kind, TerminatorKind::Return) { @@ -376,15 +383,15 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { callee: DefId, callee_args: ty::GenericArgsRef<'tcx>, call_span: Span, - ) -> bool { + ) -> Option { let tcx = self.tcx; if !tcx.is_conditionally_const(callee) { - return false; + return None; } let const_conditions = tcx.const_conditions(callee).instantiate(tcx, callee_args); if const_conditions.is_empty() { - return false; + return None; } let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(self.body.typing_env(tcx)); @@ -413,12 +420,13 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { })); let errors = ocx.select_all_or_error(); - if !errors.is_empty() { + if errors.is_empty() { + Some(ConstConditionsHold::Yes) + } else { tcx.dcx() .span_delayed_bug(call_span, "this should have reported a ~const error in HIR"); + Some(ConstConditionsHold::No) } - - true } pub fn check_drop_terminator( @@ -457,6 +465,94 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { err_span, ); } + + fn crate_inject_span(&self) -> Option { + self.tcx.hir_crate_items(()).definitions().next().and_then(|id| { + self.tcx.crate_level_attribute_injection_span(self.tcx.local_def_id_to_hir_id(id)) + }) + } + + /// Check the const stability of the given item (fn or trait). + fn check_callee_stability(&mut self, def_id: DefId) { + match self.tcx.lookup_const_stability(def_id) { + Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => { + // All good. + } + None => { + // This doesn't need a separate const-stability check -- const-stability equals + // regular stability, and regular stability is checked separately. + // However, we *do* have to worry about *recursive* const stability. + if self.enforce_recursive_const_stability() + && !is_fn_or_trait_safe_to_expose_on_stable(self.tcx, def_id) + { + self.dcx().emit_err(errors::UnmarkedConstItemExposed { + span: self.span, + def_path: self.tcx.def_path_str(def_id), + }); + } + } + Some(ConstStability { + level: StabilityLevel::Unstable { implied_by: implied_feature, issue, .. }, + feature, + .. + }) => { + // An unstable const fn/trait with a feature gate. + let callee_safe_to_expose_on_stable = + is_fn_or_trait_safe_to_expose_on_stable(self.tcx, def_id); + + // We only honor `span.allows_unstable` aka `#[allow_internal_unstable]` if + // the callee is safe to expose, to avoid bypassing recursive stability. + // This is not ideal since it means the user sees an error, not the macro + // author, but that's also the case if one forgets to set + // `#[allow_internal_unstable]` in the first place. Note that this cannot be + // integrated in the check below since we want to enforce + // `callee_safe_to_expose_on_stable` even if + // `!self.enforce_recursive_const_stability()`. + if (self.span.allows_unstable(feature) + || implied_feature.is_some_and(|f| self.span.allows_unstable(f))) + && callee_safe_to_expose_on_stable + { + return; + } + + // We can't use `check_op` to check whether the feature is enabled because + // the logic is a bit different than elsewhere: local functions don't need + // the feature gate, and there might be an "implied" gate that also suffices + // to allow this. + let feature_enabled = def_id.is_local() + || self.tcx.features().enabled(feature) + || implied_feature.is_some_and(|f| self.tcx.features().enabled(f)) + || { + // When we're compiling the compiler itself we may pull in + // crates from crates.io, but those crates may depend on other + // crates also pulled in from crates.io. We want to ideally be + // able to compile everything without requiring upstream + // modifications, so in the case that this looks like a + // `rustc_private` crate (e.g., a compiler crate) and we also have + // the `-Z force-unstable-if-unmarked` flag present (we're + // compiling a compiler crate), then let this missing feature + // annotation slide. + // This matches what we do in `eval_stability_allow_unstable` for + // regular stability. + feature == sym::rustc_private + && issue == NonZero::new(27812) + && self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked + }; + // Even if the feature is enabled, we still need check_op to double-check + // this if the callee is not safe to expose on stable. + if !feature_enabled || !callee_safe_to_expose_on_stable { + self.check_op(ops::CallUnstable { + def_id, + feature, + feature_enabled, + safe_to_expose_on_stable: callee_safe_to_expose_on_stable, + suggestion_span: self.crate_inject_span(), + is_function_call: self.tcx.def_kind(def_id) != DefKind::Trait, + }); + } + } + } + } } impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { @@ -488,7 +584,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { Rvalue::Use(_) | Rvalue::CopyForDeref(..) | Rvalue::Repeat(..) - | Rvalue::Discriminant(..) => {} + | Rvalue::Discriminant(..) + | Rvalue::Len(_) => {} Rvalue::Aggregate(kind, ..) => { if let AggregateKind::Coroutine(def_id, ..) = kind.as_ref() @@ -504,7 +601,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } Rvalue::Ref(_, BorrowKind::Mut { .. }, place) - | Rvalue::RawPtr(Mutability::Mut, place) => { + | Rvalue::RawPtr(RawPtrKind::Mut, place) => { // Inside mutable statics, we allow arbitrary mutable references. // We've allowed `static mut FOO = &mut [elements];` for a long time (the exact // reasons why are lost to history), and there is no reason to restrict that to @@ -522,7 +619,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Fake(_), place) - | Rvalue::RawPtr(Mutability::Not, place) => { + | Rvalue::RawPtr(RawPtrKind::Const, place) => { let borrowed_place_has_mut_interior = qualifs::in_place::( self.ccx, &mut |local| self.qualifs.has_mut_interior(self.ccx, local, location), @@ -534,6 +631,17 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } } + Rvalue::RawPtr(RawPtrKind::FakeForPtrMetadata, place) => { + // These are only inserted for slice length, so the place must already be indirect. + // This implies we do not have to worry about whether the borrow escapes. + if !place.is_indirect() { + self.tcx.dcx().span_delayed_bug( + self.body.source_info(location).span, + "fake borrows are always indirect", + ); + } + } + Rvalue::Cast( CastKind::PointerCoercion( PointerCoercion::MutToConstPointer @@ -567,7 +675,11 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { Rvalue::Cast(_, _, _) => {} Rvalue::NullaryOp( - NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks, + NullOp::SizeOf + | NullOp::AlignOf + | NullOp::OffsetOf(_) + | NullOp::UbChecks + | NullOp::ContractChecks, _, ) => {} Rvalue::ShallowInitBox(_, _) => {} @@ -586,12 +698,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } } UnOp::PtrMetadata => { - if !ty.is_ref() && !ty.is_unsafe_ptr() { - span_bug!( - self.span, - "non-pointer type in `Rvalue::UnaryOp({op:?})`: {ty:?}", - ); - } + // Getting the metadata from a pointer is always const. + // We already validated the type is valid in the validator. } } } @@ -602,7 +710,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { if is_int_bool_float_or_char(lhs_ty) && is_int_bool_float_or_char(rhs_ty) { // Int, bool, float, and char operations are fine. - } else if lhs_ty.is_fn_ptr() || lhs_ty.is_unsafe_ptr() { + } else if lhs_ty.is_fn_ptr() || lhs_ty.is_raw_ptr() { assert_matches!( op, BinOp::Eq @@ -624,6 +732,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { ); } } + + Rvalue::WrapUnsafeBinder(..) => { + // Unsafe binders are always trivial to create. + } } } @@ -706,11 +818,18 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { trace!("attempting to call a trait method"); let trait_is_const = tcx.is_const_trait(trait_did); - if trait_is_const { + // Only consider a trait to be const if the const conditions hold. + // Otherwise, it's really misleading to call something "conditionally" + // const when it's very obviously not conditionally const. + if trait_is_const && has_const_conditions == Some(ConstConditionsHold::Yes) { // Trait calls are always conditionally-const. - self.check_op(ops::ConditionallyConstCall { callee, args: fn_args }); - // FIXME(const_trait_impl): do a more fine-grained check whether this - // particular trait can be const-stably called. + self.check_op(ops::ConditionallyConstCall { + callee, + args: fn_args, + span: *fn_span, + call_source, + }); + self.check_callee_stability(trait_did); } else { // Not even a const trait. self.check_op(ops::FnCallNonConst { @@ -725,8 +844,13 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } // Even if we know the callee, ensure we can use conditionally-const calls. - if has_const_conditions { - self.check_op(ops::ConditionallyConstCall { callee, args: fn_args }); + if has_const_conditions.is_some() { + self.check_op(ops::ConditionallyConstCall { + callee, + args: fn_args, + span: *fn_span, + call_source, + }); } // At this point, we are calling a function, `callee`, whose `DefId` is known... @@ -781,7 +905,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // fallback body is safe to expose on stable. let is_const_stable = intrinsic.const_stable || (!intrinsic.must_be_overridden - && is_safe_to_expose_on_stable_const_fn(tcx, callee)); + && is_fn_or_trait_safe_to_expose_on_stable(tcx, callee)); match tcx.lookup_const_stability(callee) { None => { // This doesn't need a separate const-stability check -- const-stability equals @@ -803,6 +927,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { name: intrinsic.name, feature, const_stable_indirect: is_const_stable, + suggestion: self.crate_inject_span(), }); } Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => { @@ -829,82 +954,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } // Finally, stability for regular function calls -- this is the big one. - match tcx.lookup_const_stability(callee) { - Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => { - // All good. - } - None => { - // This doesn't need a separate const-stability check -- const-stability equals - // regular stability, and regular stability is checked separately. - // However, we *do* have to worry about *recursive* const stability. - if self.enforce_recursive_const_stability() - && !is_safe_to_expose_on_stable_const_fn(tcx, callee) - { - self.dcx().emit_err(errors::UnmarkedConstFnExposed { - span: self.span, - def_path: self.tcx.def_path_str(callee), - }); - } - } - Some(ConstStability { - level: StabilityLevel::Unstable { implied_by: implied_feature, issue, .. }, - feature, - .. - }) => { - // An unstable const fn with a feature gate. - let callee_safe_to_expose_on_stable = - is_safe_to_expose_on_stable_const_fn(tcx, callee); - - // We only honor `span.allows_unstable` aka `#[allow_internal_unstable]` if - // the callee is safe to expose, to avoid bypassing recursive stability. - // This is not ideal since it means the user sees an error, not the macro - // author, but that's also the case if one forgets to set - // `#[allow_internal_unstable]` in the first place. Note that this cannot be - // integrated in the check below since we want to enforce - // `callee_safe_to_expose_on_stable` even if - // `!self.enforce_recursive_const_stability()`. - if (self.span.allows_unstable(feature) - || implied_feature.is_some_and(|f| self.span.allows_unstable(f))) - && callee_safe_to_expose_on_stable - { - return; - } - - // We can't use `check_op` to check whether the feature is enabled because - // the logic is a bit different than elsewhere: local functions don't need - // the feature gate, and there might be an "implied" gate that also suffices - // to allow this. - let feature_enabled = callee.is_local() - || tcx.features().enabled(feature) - || implied_feature.is_some_and(|f| tcx.features().enabled(f)) - || { - // When we're compiling the compiler itself we may pull in - // crates from crates.io, but those crates may depend on other - // crates also pulled in from crates.io. We want to ideally be - // able to compile everything without requiring upstream - // modifications, so in the case that this looks like a - // `rustc_private` crate (e.g., a compiler crate) and we also have - // the `-Z force-unstable-if-unmarked` flag present (we're - // compiling a compiler crate), then let this missing feature - // annotation slide. - // This matches what we do in `eval_stability_allow_unstable` for - // regular stability. - feature == sym::rustc_private - && issue == NonZero::new(27812) - && self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked - }; - // Even if the feature is enabled, we still need check_op to double-check - // this if the callee is not safe to expose on stable. - if !feature_enabled || !callee_safe_to_expose_on_stable { - self.check_op(ops::FnCallUnstable { - def_id: callee, - feature, - feature_enabled, - safe_to_expose_on_stable: callee_safe_to_expose_on_stable, - }); - } - } - } + self.check_callee_stability(callee); } // Forbid all `Drop` terminators unless the place being dropped is a local with no diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs index ab68691f1b97a..bfa0a0319c344 100644 --- a/compiler/rustc_const_eval/src/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/check_consts/mod.rs @@ -56,7 +56,7 @@ impl<'mir, 'tcx> ConstCx<'mir, 'tcx> { self.const_kind == Some(hir::ConstContext::ConstFn) && (self.tcx.features().staged_api() || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked) - && is_safe_to_expose_on_stable_const_fn(self.tcx, self.def_id().to_def_id()) + && is_fn_or_trait_safe_to_expose_on_stable(self.tcx, self.def_id().to_def_id()) } fn is_async(&self) -> bool { @@ -84,28 +84,14 @@ pub fn rustc_allow_const_fn_unstable( attr::rustc_allow_const_fn_unstable(tcx.sess, attrs).any(|name| name == feature_gate) } -/// Returns `true` if the given `const fn` is "safe to expose on stable". -/// -/// Panics if the given `DefId` does not refer to a `const fn`. +/// Returns `true` if the given `def_id` (trait or function) is "safe to expose on stable". /// /// This is relevant within a `staged_api` crate. Unlike with normal features, the use of unstable /// const features *recursively* taints the functions that use them. This is to avoid accidentally /// exposing e.g. the implementation of an unstable const intrinsic on stable. So we partition the /// world into two functions: those that are safe to expose on stable (and hence may not use /// unstable features, not even recursively), and those that are not. -pub fn is_safe_to_expose_on_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - // A default body in a `#[const_trait]` is not const-stable because const trait fns currently - // cannot be const-stable. These functions can't be called from anything stable, so we shouldn't - // restrict them to only call const-stable functions. - if tcx.is_const_default_method(def_id) { - // FIXME(const_trait_impl): we have to eventually allow some of these if these things can ever be stable. - // They should probably behave like regular `const fn` for that... - return false; - } - - // Const-stability is only relevant for `const fn`. - assert!(tcx.is_const_fn(def_id)); - +pub fn is_fn_or_trait_safe_to_expose_on_stable(tcx: TyCtxt<'_>, def_id: DefId) -> bool { match tcx.lookup_const_stability(def_id) { None => { // In a `staged_api` crate, we do enforce recursive const stability for all unmarked diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index ebd680ac28a2e..7756e51c4c5f2 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -1,8 +1,8 @@ //! Concrete error types for all operations which may be invalid in a certain const context. use hir::{ConstContext, LangItem}; -use rustc_errors::Diag; use rustc_errors::codes::*; +use rustc_errors::{Applicability, Diag}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_infer::infer::TyCtxtInferExt; @@ -14,8 +14,11 @@ use rustc_middle::ty::{ self, Closure, FnDef, FnPtr, GenericArgKind, GenericArgsRef, Param, TraitRef, Ty, suggest_constraining_type_param, }; -use rustc_middle::util::{CallDesugaringKind, CallKind, call_kind}; +use rustc_session::parse::add_feature_diagnostics; use rustc_span::{BytePos, Pos, Span, Symbol, sym}; +use rustc_trait_selection::error_reporting::traits::call_kind::{ + CallDesugaringKind, CallKind, call_kind, +}; use rustc_trait_selection::traits::SelectionContext; use tracing::debug; @@ -77,6 +80,8 @@ impl<'tcx> NonConstOp<'tcx> for FnCallIndirect { pub(crate) struct ConditionallyConstCall<'tcx> { pub callee: DefId, pub args: GenericArgsRef<'tcx>, + pub span: Span, + pub call_source: CallSource, } impl<'tcx> NonConstOp<'tcx> for ConditionallyConstCall<'tcx> { @@ -91,16 +96,22 @@ impl<'tcx> NonConstOp<'tcx> for ConditionallyConstCall<'tcx> { } } - fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { - ccx.tcx.sess.create_feature_err( - errors::ConditionallyConstCall { - span, - def_path_str: ccx.tcx.def_path_str_with_args(self.callee, self.args), - def_descr: ccx.tcx.def_descr(self.callee), - kind: ccx.const_kind(), - }, - sym::const_trait_impl, - ) + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> Diag<'tcx> { + let mut diag = build_error_for_const_call( + ccx, + self.callee, + self.args, + self.span, + self.call_source, + "conditionally", + |_, _, _| {}, + ); + + // Override code and mention feature. + diag.code(E0658); + add_feature_diagnostics(&mut diag, ccx.tcx.sess, sym::const_trait_impl); + + diag } } @@ -118,244 +129,306 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { #[allow(rustc::diagnostic_outside_of_impl)] #[allow(rustc::untranslatable_diagnostic)] fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> Diag<'tcx> { - let FnCallNonConst { callee, args, span, call_source } = *self; - let ConstCx { tcx, typing_env, .. } = *ccx; + let tcx = ccx.tcx; let caller = ccx.def_id(); - let diag_trait = |err, self_ty: Ty<'_>, trait_id| { - let trait_ref = TraitRef::from_method(tcx, trait_id, args); - - match self_ty.kind() { - Param(param_ty) => { - debug!(?param_ty); - if let Some(generics) = tcx.hir_node_by_def_id(caller).generics() { - let constraint = with_no_trimmed_paths!(format!( - "~const {}", - trait_ref.print_trait_sugared(), - )); - suggest_constraining_type_param( - tcx, - generics, - err, - param_ty.name.as_str(), - &constraint, - Some(trait_ref.def_id), - None, - ); + let mut err = build_error_for_const_call( + ccx, + self.callee, + self.args, + self.span, + self.call_source, + "non", + |err, self_ty, trait_id| { + // FIXME(const_trait_impl): Do we need any of this on the non-const codepath? + + let trait_ref = TraitRef::from_method(tcx, trait_id, self.args); + + match self_ty.kind() { + Param(param_ty) => { + debug!(?param_ty); + if let Some(generics) = tcx.hir_node_by_def_id(caller).generics() { + let constraint = with_no_trimmed_paths!(format!( + "~const {}", + trait_ref.print_trait_sugared(), + )); + suggest_constraining_type_param( + tcx, + generics, + err, + param_ty.name.as_str(), + &constraint, + Some(trait_ref.def_id), + None, + ); + } } - } - ty::Adt(..) => { - let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); - let obligation = - Obligation::new(tcx, ObligationCause::dummy(), param_env, trait_ref); - let mut selcx = SelectionContext::new(&infcx); - let implsrc = selcx.select(&obligation); - if let Ok(Some(ImplSource::UserDefined(data))) = implsrc { - // FIXME(const_trait_impl) revisit this - if !tcx.is_const_trait_impl(data.impl_def_id) { - let span = tcx.def_span(data.impl_def_id); - err.subdiagnostic(errors::NonConstImplNote { span }); + ty::Adt(..) => { + let (infcx, param_env) = + tcx.infer_ctxt().build_with_typing_env(ccx.typing_env); + let obligation = + Obligation::new(tcx, ObligationCause::dummy(), param_env, trait_ref); + let mut selcx = SelectionContext::new(&infcx); + let implsrc = selcx.select(&obligation); + if let Ok(Some(ImplSource::UserDefined(data))) = implsrc { + // FIXME(const_trait_impl) revisit this + if !tcx.is_const_trait_impl(data.impl_def_id) { + let span = tcx.def_span(data.impl_def_id); + err.subdiagnostic(errors::NonConstImplNote { span }); + } } } + _ => {} } - _ => {} + }, + ); + + if let ConstContext::Static(_) = ccx.const_kind() { + err.note(fluent_generated::const_eval_lazy_lock); + } + + err + } +} + +/// Build an error message reporting that a function call is not const (or only +/// conditionally const). In case that this call is desugared (like an operator +/// or sugar from something like a `for` loop), try to build a better error message +/// that doesn't call it a method. +fn build_error_for_const_call<'tcx>( + ccx: &ConstCx<'_, 'tcx>, + callee: DefId, + args: ty::GenericArgsRef<'tcx>, + span: Span, + call_source: CallSource, + non_or_conditionally: &'static str, + note_trait_if_possible: impl FnOnce(&mut Diag<'tcx>, Ty<'tcx>, DefId), +) -> Diag<'tcx> { + let tcx = ccx.tcx; + + let call_kind = + call_kind(tcx, ccx.typing_env, callee, args, span, call_source.from_hir_call(), None); + + debug!(?call_kind); + + let mut err = match call_kind { + CallKind::Normal { desugaring: Some((kind, self_ty)), .. } => { + macro_rules! error { + ($err:ident) => { + tcx.dcx().create_err(errors::$err { + span, + ty: self_ty, + kind: ccx.const_kind(), + non_or_conditionally, + }) + }; } - }; - let call_kind = - call_kind(tcx, ccx.typing_env, callee, args, span, call_source.from_hir_call(), None); - - debug!(?call_kind); - - let mut err = match call_kind { - CallKind::Normal { desugaring: Some((kind, self_ty)), .. } => { - macro_rules! error { - ($err:ident) => { - tcx.dcx().create_err(errors::$err { - span, - ty: self_ty, - kind: ccx.const_kind(), - }) - }; + // Don't point at the trait if this is a desugaring... + // FIXME(const_trait_impl): we could perhaps do this for `Iterator`. + match kind { + CallDesugaringKind::ForLoopIntoIter | CallDesugaringKind::ForLoopNext => { + error!(NonConstForLoopIntoIter) } - - // Don't point at the trait if this is a desugaring... - // FIXME(const_trait_impl): we could perhaps do this for `Iterator`. - match kind { - CallDesugaringKind::ForLoopIntoIter | CallDesugaringKind::ForLoopNext => { - error!(NonConstForLoopIntoIter) - } - CallDesugaringKind::QuestionBranch => { - error!(NonConstQuestionBranch) - } - CallDesugaringKind::QuestionFromResidual => { - error!(NonConstQuestionFromResidual) - } - CallDesugaringKind::TryBlockFromOutput => { - error!(NonConstTryBlockFromOutput) - } - CallDesugaringKind::Await => { - error!(NonConstAwait) - } + CallDesugaringKind::QuestionBranch => { + error!(NonConstQuestionBranch) + } + CallDesugaringKind::QuestionFromResidual => { + error!(NonConstQuestionFromResidual) + } + CallDesugaringKind::TryBlockFromOutput => { + error!(NonConstTryBlockFromOutput) + } + CallDesugaringKind::Await => { + error!(NonConstAwait) } } - CallKind::FnCall { fn_trait_id, self_ty } => { - let note = match self_ty.kind() { - FnDef(def_id, ..) => { - let span = tcx.def_span(*def_id); - if ccx.tcx.is_const_fn(*def_id) { - span_bug!(span, "calling const FnDef errored when it shouldn't"); - } - - Some(errors::NonConstClosureNote::FnDef { span }) + } + CallKind::FnCall { fn_trait_id, self_ty } => { + let note = match self_ty.kind() { + FnDef(def_id, ..) => { + let span = tcx.def_span(*def_id); + if ccx.tcx.is_const_fn(*def_id) { + span_bug!(span, "calling const FnDef errored when it shouldn't"); } - FnPtr(..) => Some(errors::NonConstClosureNote::FnPtr), - Closure(..) => Some(errors::NonConstClosureNote::Closure), - _ => None, - }; - let mut err = tcx.dcx().create_err(errors::NonConstClosure { + Some(errors::NonConstClosureNote::FnDef { span }) + } + FnPtr(..) => Some(errors::NonConstClosureNote::FnPtr), + Closure(..) => Some(errors::NonConstClosureNote::Closure), + _ => None, + }; + + let mut err = tcx.dcx().create_err(errors::NonConstClosure { + span, + kind: ccx.const_kind(), + note, + non_or_conditionally, + }); + + note_trait_if_possible(&mut err, self_ty, fn_trait_id); + err + } + CallKind::Operator { trait_id, self_ty, .. } => { + let mut err = if let CallSource::MatchCmp = call_source { + tcx.dcx().create_err(errors::NonConstMatchEq { span, kind: ccx.const_kind(), - note, - }); - - diag_trait(&mut err, self_ty, fn_trait_id); - err - } - CallKind::Operator { trait_id, self_ty, .. } => { - let mut err = if let CallSource::MatchCmp = call_source { - tcx.dcx().create_err(errors::NonConstMatchEq { - span, - kind: ccx.const_kind(), - ty: self_ty, - }) - } else { - let mut sugg = None; - - if ccx.tcx.is_lang_item(trait_id, LangItem::PartialEq) { - match (args[0].unpack(), args[1].unpack()) { - (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty)) - if self_ty == rhs_ty - && self_ty.is_ref() - && self_ty.peel_refs().is_primitive() => - { - let mut num_refs = 0; - let mut tmp_ty = self_ty; - while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() { - num_refs += 1; - tmp_ty = *inner_ty; - } - let deref = "*".repeat(num_refs); - - if let Ok(call_str) = - ccx.tcx.sess.source_map().span_to_snippet(span) - { - if let Some(eq_idx) = call_str.find("==") { - if let Some(rhs_idx) = call_str[(eq_idx + 2)..] - .find(|c: char| !c.is_whitespace()) - { - let rhs_pos = span.lo() - + BytePos::from_usize(eq_idx + 2 + rhs_idx); - let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos); - sugg = Some(errors::ConsiderDereferencing { - deref, - span: span.shrink_to_lo(), - rhs_span, - }); - } + ty: self_ty, + non_or_conditionally, + }) + } else { + let mut sugg = None; + + if ccx.tcx.is_lang_item(trait_id, LangItem::PartialEq) { + match (args[0].unpack(), args[1].unpack()) { + (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty)) + if self_ty == rhs_ty + && self_ty.is_ref() + && self_ty.peel_refs().is_primitive() => + { + let mut num_refs = 0; + let mut tmp_ty = self_ty; + while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() { + num_refs += 1; + tmp_ty = *inner_ty; + } + let deref = "*".repeat(num_refs); + + if let Ok(call_str) = ccx.tcx.sess.source_map().span_to_snippet(span) { + if let Some(eq_idx) = call_str.find("==") { + if let Some(rhs_idx) = + call_str[(eq_idx + 2)..].find(|c: char| !c.is_whitespace()) + { + let rhs_pos = + span.lo() + BytePos::from_usize(eq_idx + 2 + rhs_idx); + let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos); + sugg = Some(errors::ConsiderDereferencing { + deref, + span: span.shrink_to_lo(), + rhs_span, + }); } } } - _ => {} } + _ => {} } - tcx.dcx().create_err(errors::NonConstOperator { - span, - kind: ccx.const_kind(), - sugg, - }) - }; - - diag_trait(&mut err, self_ty, trait_id); - err - } - CallKind::DerefCoercion { deref_target, deref_target_ty, self_ty } => { - // Check first whether the source is accessible (issue #87060) - let target = if tcx.sess.source_map().is_span_accessible(deref_target) { - Some(deref_target) - } else { - None - }; - - let mut err = tcx.dcx().create_err(errors::NonConstDerefCoercion { + } + tcx.dcx().create_err(errors::NonConstOperator { span, - ty: self_ty, kind: ccx.const_kind(), - target_ty: deref_target_ty, - deref_target: target, - }); + sugg, + non_or_conditionally, + }) + }; - diag_trait(&mut err, self_ty, tcx.require_lang_item(LangItem::Deref, Some(span))); - err - } - _ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::ArgumentMethods) => { - ccx.dcx().create_err(errors::NonConstFmtMacroCall { span, kind: ccx.const_kind() }) - } - _ => ccx.dcx().create_err(errors::NonConstFnCall { + note_trait_if_possible(&mut err, self_ty, trait_id); + err + } + CallKind::DerefCoercion { deref_target_span, deref_target_ty, self_ty } => { + // Check first whether the source is accessible (issue #87060) + let target = if let Some(deref_target_span) = deref_target_span + && tcx.sess.source_map().is_span_accessible(deref_target_span) + { + Some(deref_target_span) + } else { + None + }; + + let mut err = tcx.dcx().create_err(errors::NonConstDerefCoercion { span, - def_descr: ccx.tcx.def_descr(callee), - def_path_str: ccx.tcx.def_path_str_with_args(callee, args), + ty: self_ty, kind: ccx.const_kind(), - }), - }; + target_ty: deref_target_ty, + deref_target: target, + non_or_conditionally, + }); + + note_trait_if_possible( + &mut err, + self_ty, + tcx.require_lang_item(LangItem::Deref, Some(span)), + ); + err + } + _ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::ArgumentMethods) => { + ccx.dcx().create_err(errors::NonConstFmtMacroCall { + span, + kind: ccx.const_kind(), + non_or_conditionally, + }) + } + _ => ccx.dcx().create_err(errors::NonConstFnCall { + span, + def_descr: ccx.tcx.def_descr(callee), + def_path_str: ccx.tcx.def_path_str_with_args(callee, args), + kind: ccx.const_kind(), + non_or_conditionally, + }), + }; - err.note(format!( - "calls in {}s are limited to constant functions, \ + err.note(format!( + "calls in {}s are limited to constant functions, \ tuple structs and tuple variants", - ccx.const_kind(), - )); - - if let ConstContext::Static(_) = ccx.const_kind() { - err.note(fluent_generated::const_eval_lazy_lock); - } + ccx.const_kind(), + )); - err - } + err } -/// A call to an `#[unstable]` const fn or `#[rustc_const_unstable]` function. +/// A call to an `#[unstable]` const fn, `#[rustc_const_unstable]` function or trait. /// -/// Contains the name of the feature that would allow the use of this function. +/// Contains the name of the feature that would allow the use of this function/trait. #[derive(Debug)] -pub(crate) struct FnCallUnstable { +pub(crate) struct CallUnstable { pub def_id: DefId, pub feature: Symbol, /// If this is true, then the feature is enabled, but we need to still check if it is safe to /// expose on stable. pub feature_enabled: bool, pub safe_to_expose_on_stable: bool, + pub suggestion_span: Option, + /// true if `def_id` is the function we are calling, false if `def_id` is an unstable trait. + pub is_function_call: bool, } -impl<'tcx> NonConstOp<'tcx> for FnCallUnstable { +impl<'tcx> NonConstOp<'tcx> for CallUnstable { fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { Status::Unstable { gate: self.feature, gate_already_checked: self.feature_enabled, safe_to_expose_on_stable: self.safe_to_expose_on_stable, - is_function_call: true, + is_function_call: self.is_function_call, } } fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { assert!(!self.feature_enabled); - let mut err = ccx.dcx().create_err(errors::UnstableConstFn { - span, - def_path: ccx.tcx.def_path_str(self.def_id), - }); + let mut err = if self.is_function_call { + ccx.dcx().create_err(errors::UnstableConstFn { + span, + def_path: ccx.tcx.def_path_str(self.def_id), + }) + } else { + ccx.dcx().create_err(errors::UnstableConstTrait { + span, + def_path: ccx.tcx.def_path_str(self.def_id), + }) + }; // FIXME: make this translatable + let msg = format!("add `#![feature({})]` to the crate attributes to enable", self.feature); #[allow(rustc::untranslatable_diagnostic)] - err.help(format!("add `#![feature({})]` to the crate attributes to enable", self.feature)); + if let Some(span) = self.suggestion_span { + err.span_suggestion_verbose( + span, + msg, + format!("#![feature({})]\n", self.feature), + Applicability::MachineApplicable, + ); + } else { + err.help(msg); + } err } @@ -383,6 +456,7 @@ pub(crate) struct IntrinsicUnstable { pub name: Symbol, pub feature: Symbol, pub const_stable_indirect: bool, + pub suggestion: Option, } impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable { @@ -402,6 +476,8 @@ impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable { span, name: self.name, feature: self.feature, + suggestion: self.suggestion, + help: self.suggestion.is_none(), }) } } diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index b1b7fb406b105..dfcd1969a73d1 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -188,12 +188,14 @@ impl Qualif for NeedsNonConstDrop { ObligationCause::misc(cx.body.span, cx.def_id()), param_env, ty::Binder::dummy(ty::TraitRef::new(cx.tcx, destruct_def_id, [ty])) - .to_host_effect_clause(cx.tcx, match cx.const_kind() { - rustc_hir::ConstContext::ConstFn => ty::BoundConstness::Maybe, - rustc_hir::ConstContext::Static(_) | rustc_hir::ConstContext::Const { .. } => { - ty::BoundConstness::Const - } - }), + .to_host_effect_clause( + cx.tcx, + match cx.const_kind() { + rustc_hir::ConstContext::ConstFn => ty::BoundConstness::Maybe, + rustc_hir::ConstContext::Static(_) + | rustc_hir::ConstContext::Const { .. } => ty::BoundConstness::Const, + }, + ), )); !ocx.select_all_or_error().is_empty() } @@ -230,7 +232,9 @@ where Q::in_any_value_of_ty(cx, rvalue.ty(cx.body, cx.tcx)) } - Rvalue::Discriminant(place) => in_place::(cx, in_local, place.as_ref()), + Rvalue::Discriminant(place) | Rvalue::Len(place) => { + in_place::(cx, in_local, place.as_ref()) + } Rvalue::CopyForDeref(place) => in_place::(cx, in_local, place.as_ref()), @@ -256,6 +260,8 @@ where in_place::(cx, in_local, place.as_ref()) } + Rvalue::WrapUnsafeBinder(op, _) => in_operand::(cx, in_local, op), + Rvalue::Aggregate(kind, operands) => { // Return early if we know that the struct or enum being constructed is always // qualified. @@ -295,7 +301,8 @@ where | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Downcast(_, _) - | ProjectionElem::Index(_) => {} + | ProjectionElem::Index(_) + | ProjectionElem::UnwrapUnsafeBinder(_) => {} } let base_ty = place_base.ty(cx.body, cx.tcx); @@ -343,7 +350,7 @@ where Const::Ty(_, ct) if matches!( ct.kind(), - ty::ConstKind::Param(_) | ty::ConstKind::Error(_) | ty::ConstKind::Value(_, _) + ty::ConstKind::Param(_) | ty::ConstKind::Error(_) | ty::ConstKind::Value(_) ) => { None diff --git a/compiler/rustc_const_eval/src/check_consts/resolver.rs b/compiler/rustc_const_eval/src/check_consts/resolver.rs index 5a6e7ab2beef4..8cee282311f03 100644 --- a/compiler/rustc_const_eval/src/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/check_consts/resolver.rs @@ -197,11 +197,13 @@ where | mir::Rvalue::CopyForDeref(..) | mir::Rvalue::ThreadLocalRef(..) | mir::Rvalue::Repeat(..) + | mir::Rvalue::Len(..) | mir::Rvalue::BinaryOp(..) | mir::Rvalue::NullaryOp(..) | mir::Rvalue::UnaryOp(..) | mir::Rvalue::Discriminant(..) - | mir::Rvalue::Aggregate(..) => {} + | mir::Rvalue::Aggregate(..) + | mir::Rvalue::WrapUnsafeBinder(..) => {} } } diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index fee7287951d0a..cc21a18af3a09 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -140,7 +140,7 @@ where // Don't emit a new diagnostic for these errors, they are already reported elsewhere or // should remain silent. err_inval!(AlreadyReported(info)) => ErrorHandled::Reported(info, span), - err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => { + err_inval!(Layout(LayoutError::TooGeneric(_))) | err_inval!(TooGeneric) => { ErrorHandled::TooGeneric(span) } err_inval!(Layout(LayoutError::ReferencesError(guar))) => { diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index 8af17d01b0a36..35c3e3ed3150e 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -4,14 +4,18 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; -fn parent_impl_constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness { +fn parent_impl_or_trait_constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness { let parent_id = tcx.local_parent(def_id); - if matches!(tcx.def_kind(parent_id), DefKind::Impl { .. }) - && let Some(header) = tcx.impl_trait_header(parent_id) - { - header.constness - } else { - hir::Constness::NotConst + match tcx.def_kind(parent_id) { + DefKind::Impl { of_trait: true } => tcx.impl_trait_header(parent_id).unwrap().constness, + DefKind::Trait => { + if tcx.is_const_trait(parent_id.into()) { + hir::Constness::Const + } else { + hir::Constness::NotConst + } + } + _ => hir::Constness::NotConst, } } @@ -34,7 +38,7 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness { // If the function itself is not annotated with `const`, it may still be a `const fn` // if it resides in a const trait impl. - parent_impl_constness(tcx, def_id) + parent_impl_or_trait_constness(tcx, def_id) } else { tcx.dcx().span_bug( tcx.def_span(def_id), diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index ba7fbb254c66d..82e0a6e6666f3 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -21,9 +21,10 @@ use super::error::*; use crate::errors::{LongRunning, LongRunningWarn}; use crate::fluent_generated as fluent; use crate::interpret::{ - self, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, GlobalAlloc, ImmTy, - InterpCx, InterpResult, MPlaceTy, OpTy, RangeSet, Scalar, compile_time_machine, interp_ok, - throw_exhaust, throw_inval, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, + self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, + GlobalAlloc, ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, RangeSet, Scalar, + compile_time_machine, interp_ok, throw_exhaust, throw_inval, throw_ub, throw_ub_custom, + throw_unsup, throw_unsup_format, }; /// When hitting this many interpreted terminators we emit a deny by default lint @@ -360,10 +361,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { // sensitive check here. But we can at least rule out functions that are not const at // all. That said, we have to allow calling functions inside a trait marked with // #[const_trait]. These *are* const-checked! - // FIXME(const_trait_impl): why does `is_const_fn` not classify them as const? - if (!ecx.tcx.is_const_fn(def) && !ecx.tcx.is_const_default_method(def)) - || ecx.tcx.has_attr(def, sym::rustc_do_not_const_check) - { + if !ecx.tcx.is_const_fn(def) || ecx.tcx.has_attr(def, sym::rustc_do_not_const_check) { // We certainly do *not* want to actually call the fn // though, so be sure we return here. throw_unsup_format!("calling non-const function `{}`", instance) @@ -423,6 +421,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { Size::from_bytes(size), align, interpret::MemoryKind::Machine(MemoryKind::Heap), + AllocInit::Uninit, )?; ecx.write_pointer(ptr, dest)?; } @@ -509,6 +508,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { found: eval_to_int(found)?, } } + NullPointerDereference => NullPointerDereference, }; Err(ConstEvalErrKind::AssertFailure(err)).into() } diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 34f795bda7595..c0438fb3ff81a 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -1,7 +1,7 @@ // Not in interpret to make sure we do not use private implementation details use rustc_abi::VariantIdx; -use rustc_middle::query::{Key, TyCtxtAt}; +use rustc_middle::query::Key; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, mir}; @@ -35,16 +35,17 @@ pub(crate) type ValTreeCreationResult<'tcx> = Result, ValTreeC #[instrument(skip(tcx), level = "debug")] pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>( - tcx: TyCtxtAt<'tcx>, + tcx: TyCtxt<'tcx>, val: mir::ConstValue<'tcx>, ty: Ty<'tcx>, ) -> Option> { let typing_env = ty::TypingEnv::fully_monomorphized(); - let (ecx, op) = mk_eval_cx_for_const_val(tcx, typing_env, val, ty)?; + // FIXME: use a proper span here? + let (ecx, op) = mk_eval_cx_for_const_val(tcx.at(rustc_span::DUMMY_SP), typing_env, val, ty)?; // We go to `usize` as we cannot allocate anything bigger anyway. let (field_count, variant, down) = match ty.kind() { - ty::Array(_, len) => (len.try_to_target_usize(tcx.tcx)? as usize, None, op), + ty::Array(_, len) => (len.try_to_target_usize(tcx)? as usize, None, op), ty::Adt(def, _) if def.variants().is_empty() => { return None; } diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 4ff8aa9a3b418..3776fb55c2e5f 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -2,7 +2,7 @@ use rustc_abi::{BackendRepr, VariantIdx}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId, ReportedErrorInfo}; use rustc_middle::ty::layout::{LayoutCx, LayoutOf, TyAndLayout}; -use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_span::DUMMY_SP; use tracing::{debug, instrument, trace}; @@ -21,7 +21,7 @@ use crate::interpret::{ fn branches<'tcx>( ecx: &CompileTimeInterpCx<'tcx>, place: &MPlaceTy<'tcx>, - n: usize, + field_count: usize, variant: Option, num_nodes: &mut usize, ) -> ValTreeCreationResult<'tcx> { @@ -29,30 +29,28 @@ fn branches<'tcx>( Some(variant) => ecx.project_downcast(place, variant).unwrap(), None => place.clone(), }; - let variant = variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32())))); - debug!(?place, ?variant); + debug!(?place); - let mut fields = Vec::with_capacity(n); - for i in 0..n { - let field = ecx.project_field(&place, i).unwrap(); - let valtree = const_to_valtree_inner(ecx, &field, num_nodes)?; - fields.push(Some(valtree)); - } + let mut branches = Vec::with_capacity(field_count + variant.is_some() as usize); // For enums, we prepend their variant index before the variant's fields so we can figure out // the variant again when just seeing a valtree. - let branches = variant - .into_iter() - .chain(fields.into_iter()) - .collect::>>() - .expect("should have already checked for errors in ValTree creation"); + if let Some(variant) = variant { + branches.push(ty::ValTree::from_scalar_int(*ecx.tcx, variant.as_u32().into())); + } + + for i in 0..field_count { + let field = ecx.project_field(&place, i).unwrap(); + let valtree = const_to_valtree_inner(ecx, &field, num_nodes)?; + branches.push(valtree); + } // Have to account for ZSTs here if branches.len() == 0 { *num_nodes += 1; } - Ok(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches))) + Ok(ty::ValTree::from_branches(*ecx.tcx, branches)) } #[instrument(skip(ecx), level = "debug")] @@ -70,7 +68,7 @@ fn slice_branches<'tcx>( elems.push(valtree); } - Ok(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(elems))) + Ok(ty::ValTree::from_branches(*ecx.tcx, elems)) } #[instrument(skip(ecx), level = "debug")] @@ -79,6 +77,7 @@ fn const_to_valtree_inner<'tcx>( place: &MPlaceTy<'tcx>, num_nodes: &mut usize, ) -> ValTreeCreationResult<'tcx> { + let tcx = *ecx.tcx; let ty = place.layout.ty; debug!("ty kind: {:?}", ty.kind()); @@ -89,14 +88,14 @@ fn const_to_valtree_inner<'tcx>( match ty.kind() { ty::FnDef(..) => { *num_nodes += 1; - Ok(ty::ValTree::zst()) + Ok(ty::ValTree::zst(tcx)) } ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => { let val = ecx.read_immediate(place).unwrap(); let val = val.to_scalar_int().unwrap(); *num_nodes += 1; - Ok(ty::ValTree::Leaf(val)) + Ok(ty::ValTree::from_scalar_int(tcx, val)) } ty::Pat(base, ..) => { @@ -127,7 +126,7 @@ fn const_to_valtree_inner<'tcx>( return Err(ValTreeCreationError::NonSupportedType(ty)); }; // It's just a ScalarInt! - Ok(ty::ValTree::Leaf(val)) + Ok(ty::ValTree::from_scalar_int(tcx, val)) } // Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to @@ -272,59 +271,59 @@ pub(crate) fn eval_to_valtree<'tcx>( /// Converts a `ValTree` to a `ConstValue`, which is needed after mir /// construction has finished. -// FIXME Merge `valtree_to_const_value` and `valtree_into_mplace` into one function +// FIXME(valtrees): Merge `valtree_to_const_value` and `valtree_into_mplace` into one function #[instrument(skip(tcx), level = "debug", ret)] pub fn valtree_to_const_value<'tcx>( tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, - ty: Ty<'tcx>, - valtree: ty::ValTree<'tcx>, + cv: ty::Value<'tcx>, ) -> mir::ConstValue<'tcx> { // Basic idea: We directly construct `Scalar` values from trivial `ValTree`s // (those for constants with type bool, int, uint, float or char). // For all other types we create an `MPlace` and fill that by walking // the `ValTree` and using `place_projection` and `place_field` to // create inner `MPlace`s which are filled recursively. - // FIXME Does this need an example? - match *ty.kind() { + // FIXME: Does this need an example? + match *cv.ty.kind() { ty::FnDef(..) => { - assert!(valtree.unwrap_branch().is_empty()); + assert!(cv.valtree.is_zst()); mir::ConstValue::ZeroSized } ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char | ty::RawPtr(_, _) => { - match valtree { - ty::ValTree::Leaf(scalar_int) => mir::ConstValue::Scalar(Scalar::Int(scalar_int)), - ty::ValTree::Branch(_) => bug!( - "ValTrees for Bool, Int, Uint, Float, Char or RawPtr should have the form ValTree::Leaf" - ), - } + mir::ConstValue::Scalar(Scalar::Int(cv.valtree.unwrap_leaf())) + } + ty::Pat(ty, _) => { + let cv = ty::Value { valtree: cv.valtree, ty }; + valtree_to_const_value(tcx, typing_env, cv) } - ty::Pat(ty, _) => valtree_to_const_value(tcx, typing_env, ty, valtree), ty::Ref(_, inner_ty, _) => { let mut ecx = mk_eval_cx_to_read_const_val(tcx, DUMMY_SP, typing_env, CanAccessMutGlobal::No); - let imm = valtree_to_ref(&mut ecx, valtree, inner_ty); - let imm = - ImmTy::from_immediate(imm, tcx.layout_of(typing_env.as_query_input(ty)).unwrap()); + let imm = valtree_to_ref(&mut ecx, cv.valtree, inner_ty); + let imm = ImmTy::from_immediate( + imm, + tcx.layout_of(typing_env.as_query_input(cv.ty)).unwrap(), + ); op_to_const(&ecx, &imm.into(), /* for diagnostics */ false) } ty::Tuple(_) | ty::Array(_, _) | ty::Adt(..) => { - let layout = tcx.layout_of(typing_env.as_query_input(ty)).unwrap(); + let layout = tcx.layout_of(typing_env.as_query_input(cv.ty)).unwrap(); if layout.is_zst() { // Fast path to avoid some allocations. return mir::ConstValue::ZeroSized; } if layout.backend_repr.is_scalar() - && (matches!(ty.kind(), ty::Tuple(_)) - || matches!(ty.kind(), ty::Adt(def, _) if def.is_struct())) + && (matches!(cv.ty.kind(), ty::Tuple(_)) + || matches!(cv.ty.kind(), ty::Adt(def, _) if def.is_struct())) { // A Scalar tuple/struct; we can avoid creating an allocation. - let branches = valtree.unwrap_branch(); + let branches = cv.valtree.unwrap_branch(); // Find the non-ZST field. (There can be aligned ZST!) for (i, &inner_valtree) in branches.iter().enumerate() { let field = layout.field(&LayoutCx::new(tcx, typing_env), i); if !field.is_zst() { - return valtree_to_const_value(tcx, typing_env, field.ty, inner_valtree); + let cv = ty::Value { valtree: inner_valtree, ty: field.ty }; + return valtree_to_const_value(tcx, typing_env, cv); } } bug!("could not find non-ZST field during in {layout:#?}"); @@ -334,9 +333,9 @@ pub fn valtree_to_const_value<'tcx>( mk_eval_cx_to_read_const_val(tcx, DUMMY_SP, typing_env, CanAccessMutGlobal::No); // Need to create a place for this valtree. - let place = create_valtree_place(&mut ecx, layout, valtree); + let place = create_valtree_place(&mut ecx, layout, cv.valtree); - valtree_into_mplace(&mut ecx, &place, valtree); + valtree_into_mplace(&mut ecx, &place, cv.valtree); dump_place(&ecx, &place); intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &place).unwrap(); @@ -361,7 +360,7 @@ pub fn valtree_to_const_value<'tcx>( | ty::Slice(_) | ty::Dynamic(..) | ty::UnsafeBinder(_) => { - bug!("no ValTree should have been created for type {:?}", ty.kind()) + bug!("no ValTree should have been created for type {:?}", cv.ty.kind()) } } } diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 57534540019bb..c08495c012f83 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -16,7 +16,6 @@ use rustc_middle::mir::interpret::{ }; use rustc_middle::ty::{self, Mutability, Ty}; use rustc_span::{Span, Symbol}; -use rustc_target::callconv::AdjustForForeignAbiError; use crate::interpret::InternKind; @@ -121,20 +120,35 @@ pub(crate) struct UnstableConstFn { pub def_path: String, } +#[derive(Diagnostic)] +#[diag(const_eval_unstable_const_trait)] +pub(crate) struct UnstableConstTrait { + #[primary_span] + pub span: Span, + pub def_path: String, +} + #[derive(Diagnostic)] #[diag(const_eval_unstable_intrinsic)] -#[help] pub(crate) struct UnstableIntrinsic { #[primary_span] pub span: Span, pub name: Symbol, pub feature: Symbol, + #[suggestion( + const_eval_unstable_intrinsic_suggestion, + code = "#![feature({feature})]\n", + applicability = "machine-applicable" + )] + pub suggestion: Option, + #[help(const_eval_unstable_intrinsic_suggestion)] + pub help: bool, } #[derive(Diagnostic)] -#[diag(const_eval_unmarked_const_fn_exposed)] +#[diag(const_eval_unmarked_const_item_exposed)] #[help] -pub(crate) struct UnmarkedConstFnExposed { +pub(crate) struct UnmarkedConstItemExposed { #[primary_span] pub span: Span, pub def_path: String, @@ -174,16 +188,7 @@ pub(crate) struct NonConstFmtMacroCall { #[primary_span] pub span: Span, pub kind: ConstContext, -} - -#[derive(Diagnostic)] -#[diag(const_eval_conditionally_const_call)] -pub(crate) struct ConditionallyConstCall { - #[primary_span] - pub span: Span, - pub def_path_str: String, - pub def_descr: &'static str, - pub kind: ConstContext, + pub non_or_conditionally: &'static str, } #[derive(Diagnostic)] @@ -194,6 +199,7 @@ pub(crate) struct NonConstFnCall { pub def_path_str: String, pub def_descr: &'static str, pub kind: ConstContext, + pub non_or_conditionally: &'static str, } #[derive(Diagnostic)] @@ -293,68 +299,75 @@ pub struct RawBytesNote { // FIXME(fee1-dead) do not use stringly typed `ConstContext` #[derive(Diagnostic)] -#[diag(const_eval_match_eq_non_const, code = E0015)] +#[diag(const_eval_non_const_match_eq, code = E0015)] #[note] pub struct NonConstMatchEq<'tcx> { #[primary_span] pub span: Span, pub ty: Ty<'tcx>, pub kind: ConstContext, + pub non_or_conditionally: &'static str, } #[derive(Diagnostic)] -#[diag(const_eval_for_loop_into_iter_non_const, code = E0015)] +#[diag(const_eval_non_const_for_loop_into_iter, code = E0015)] pub struct NonConstForLoopIntoIter<'tcx> { #[primary_span] pub span: Span, pub ty: Ty<'tcx>, pub kind: ConstContext, + pub non_or_conditionally: &'static str, } #[derive(Diagnostic)] -#[diag(const_eval_question_branch_non_const, code = E0015)] +#[diag(const_eval_non_const_question_branch, code = E0015)] pub struct NonConstQuestionBranch<'tcx> { #[primary_span] pub span: Span, pub ty: Ty<'tcx>, pub kind: ConstContext, + pub non_or_conditionally: &'static str, } #[derive(Diagnostic)] -#[diag(const_eval_question_from_residual_non_const, code = E0015)] +#[diag(const_eval_non_const_question_from_residual, code = E0015)] pub struct NonConstQuestionFromResidual<'tcx> { #[primary_span] pub span: Span, pub ty: Ty<'tcx>, pub kind: ConstContext, + pub non_or_conditionally: &'static str, } #[derive(Diagnostic)] -#[diag(const_eval_try_block_from_output_non_const, code = E0015)] +#[diag(const_eval_non_const_try_block_from_output, code = E0015)] pub struct NonConstTryBlockFromOutput<'tcx> { #[primary_span] pub span: Span, pub ty: Ty<'tcx>, pub kind: ConstContext, + pub non_or_conditionally: &'static str, } #[derive(Diagnostic)] -#[diag(const_eval_await_non_const, code = E0015)] +#[diag(const_eval_non_const_await, code = E0015)] pub struct NonConstAwait<'tcx> { #[primary_span] pub span: Span, pub ty: Ty<'tcx>, pub kind: ConstContext, + pub non_or_conditionally: &'static str, } #[derive(Diagnostic)] -#[diag(const_eval_closure_non_const, code = E0015)] +#[diag(const_eval_non_const_closure, code = E0015)] pub struct NonConstClosure { #[primary_span] pub span: Span, pub kind: ConstContext, #[subdiagnostic] pub note: Option, + pub non_or_conditionally: &'static str, } #[derive(Subdiagnostic)] @@ -381,17 +394,18 @@ pub struct ConsiderDereferencing { } #[derive(Diagnostic)] -#[diag(const_eval_operator_non_const, code = E0015)] +#[diag(const_eval_non_const_operator, code = E0015)] pub struct NonConstOperator { #[primary_span] pub span: Span, pub kind: ConstContext, #[subdiagnostic] pub sugg: Option, + pub non_or_conditionally: &'static str, } #[derive(Diagnostic)] -#[diag(const_eval_deref_coercion_non_const, code = E0015)] +#[diag(const_eval_non_const_deref_coercion, code = E0015)] #[note] pub struct NonConstDerefCoercion<'tcx> { #[primary_span] @@ -401,6 +415,7 @@ pub struct NonConstDerefCoercion<'tcx> { pub target_ty: Ty<'tcx>, #[note(const_eval_target_note)] pub deref_target: Option, + pub non_or_conditionally: &'static str, } #[derive(Diagnostic)] @@ -562,10 +577,13 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { } ShiftOverflow { intrinsic, shift_amount } => { diag.arg("intrinsic", intrinsic); - diag.arg("shift_amount", match shift_amount { - Either::Left(v) => v.to_string(), - Either::Right(v) => v.to_string(), - }); + diag.arg( + "shift_amount", + match shift_amount { + Either::Left(v) => v.to_string(), + Either::Right(v) => v.to_string(), + }, + ); } BoundsCheckFailed { len, index } => { diag.arg("len", len); @@ -917,9 +935,6 @@ impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> { InvalidProgramInfo::TooGeneric => const_eval_too_generic, InvalidProgramInfo::AlreadyReported(_) => const_eval_already_reported, InvalidProgramInfo::Layout(e) => e.diagnostic_message(), - InvalidProgramInfo::FnAbiAdjustForForeignAbi(_) => { - rustc_middle::error::middle_adjust_for_foreign_abi_error - } } } fn add_args(self, diag: &mut Diag<'_, G>) { @@ -934,12 +949,6 @@ impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> { } dummy_diag.cancel(); } - InvalidProgramInfo::FnAbiAdjustForForeignAbi( - AdjustForForeignAbiError::Unsupported { arch, abi }, - ) => { - diag.arg("arch", arch); - diag.arg("abi", abi.name()); - } } } } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 99f0ac702c54d..29f819cca1fb6 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -5,6 +5,7 @@ use std::borrow::Cow; use either::{Left, Right}; use rustc_abi::{self as abi, ExternAbi, FieldIdx, Integer, VariantIdx}; +use rustc_hir::def_id::DefId; use rustc_middle::ty::layout::{FnAbiOf, IntegerExt, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, AdtDef, Instance, Ty, VariantDef}; use rustc_middle::{bug, mir, span_bug}; @@ -240,7 +241,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(caller == callee) } - fn check_argument_compat( + /// Returns a `bool` saying whether the two arguments are ABI-compatible. + pub fn check_argument_compat( &self, caller_abi: &ArgAbi<'tcx, Ty<'tcx>>, callee_abi: &ArgAbi<'tcx, Ty<'tcx>>, @@ -380,10 +382,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { "caller ABI: {:#?}, args: {:#?}", caller_fn_abi, args.iter() - .map(|arg| (arg.layout().ty, match arg { - FnArg::Copy(op) => format!("copy({op:?})"), - FnArg::InPlace(mplace) => format!("in-place({mplace:?})"), - })) + .map(|arg| ( + arg.layout().ty, + match arg { + FnArg::Copy(op) => format!("copy({op:?})"), + FnArg::InPlace(mplace) => format!("in-place({mplace:?})"), + } + )) .collect::>() ); trace!( @@ -566,7 +571,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { | ty::InstanceKind::ThreadLocalShim(..) | ty::InstanceKind::AsyncDropGlueCtorShim(..) | ty::InstanceKind::Item(_) => { - // We need MIR for this fn + // We need MIR for this fn. + // Note that this can be an intrinsic, if we are executing its fallback body. let Some((body, instance)) = M::find_mir_or_eval_fn( self, instance, @@ -692,25 +698,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { trace!("Virtual call dispatches to {fn_inst:#?}"); // We can also do the lookup based on `def_id` and `dyn_ty`, and check that that // produces the same result. - if cfg!(debug_assertions) { - let tcx = *self.tcx; - - let trait_def_id = tcx.trait_of_item(def_id).unwrap(); - let virtual_trait_ref = - ty::TraitRef::from_method(tcx, trait_def_id, instance.args); - let existential_trait_ref = - ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref); - let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty); - - let concrete_method = Instance::expect_resolve_for_vtable( - tcx, - self.typing_env, - def_id, - instance.args.rebase_onto(tcx, trait_def_id, concrete_trait_ref.args), - self.cur_span(), - ); - assert_eq!(fn_inst, concrete_method); - } + self.assert_virtual_instance_matches_concrete(dyn_ty, def_id, instance, fn_inst); // Adjust receiver argument. Layout can be any (thin) ptr. let receiver_ty = Ty::new_mut_ptr(self.tcx.tcx, dyn_ty); @@ -743,6 +731,30 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } + fn assert_virtual_instance_matches_concrete( + &self, + dyn_ty: Ty<'tcx>, + def_id: DefId, + virtual_instance: ty::Instance<'tcx>, + concrete_instance: ty::Instance<'tcx>, + ) { + let tcx = *self.tcx; + + let trait_def_id = tcx.trait_of_item(def_id).unwrap(); + let virtual_trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, virtual_instance.args); + let existential_trait_ref = ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref); + let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty); + + let concrete_method = Instance::expect_resolve_for_vtable( + tcx, + self.typing_env, + def_id, + virtual_instance.args.rebase_onto(tcx, trait_def_id, concrete_trait_ref.args), + self.cur_span(), + ); + assert_eq!(concrete_instance, concrete_method); + } + /// Initiate a tail call to this function -- popping the current stack frame, pushing the new /// stack frame and initializing the arguments. pub(super) fn init_fn_tail_call( @@ -866,10 +878,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ); // Check `unwinding`. - assert_eq!(unwinding, match self.frame().loc { - Left(loc) => self.body().basic_blocks[loc.block].is_cleanup, - Right(_) => true, - }); + assert_eq!( + unwinding, + match self.frame().loc { + Left(loc) => self.body().basic_blocks[loc.block].is_cleanup, + Right(_) => true, + } + ); if unwinding && self.frame_idx() == 0 { throw_ub_custom!(fluent::const_eval_unwind_past_top); } diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index ef3e96784ce86..b53c1505f6b18 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -203,7 +203,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { cast_to: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { assert!(src.layout.ty.is_any_ptr()); - assert!(cast_to.ty.is_unsafe_ptr()); + assert!(cast_to.ty.is_raw_ptr()); // Handle casting any ptr to raw ptr (might be a wide ptr). if cast_to.size == src.layout.size { // Thin or wide pointer that just has the ptr kind of target type changed. @@ -212,7 +212,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Casting the metadata away from a wide ptr. assert_eq!(src.layout.size, 2 * self.pointer_size()); assert_eq!(cast_to.size, self.pointer_size()); - assert!(src.layout.ty.is_unsafe_ptr()); + assert!(src.layout.ty.is_raw_ptr()); return match **src { Immediate::ScalarPair(data, _) => interp_ok(ImmTy::from_scalar(data, cast_to)), Immediate::Scalar(..) => span_bug!( @@ -414,36 +414,33 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Sanity-check that `supertrait_vtable_slot` in this type's vtable indeed produces // our destination trait. - if cfg!(debug_assertions) { - let vptr_entry_idx = - self.tcx.supertrait_vtable_slot((src_pointee_ty, dest_pointee_ty)); - let vtable_entries = self.vtable_entries(data_a.principal(), ty); - if let Some(entry_idx) = vptr_entry_idx { - let Some(&ty::VtblEntry::TraitVPtr(upcast_trait_ref)) = - vtable_entries.get(entry_idx) - else { - span_bug!( - self.cur_span(), - "invalid vtable entry index in {} -> {} upcast", - src_pointee_ty, - dest_pointee_ty - ); - }; - let erased_trait_ref = upcast_trait_ref - .map_bound(|r| ty::ExistentialTraitRef::erase_self_ty(*self.tcx, r)); - assert!( - data_b - .principal() - .is_some_and(|b| self.eq_in_param_env(erased_trait_ref, b)) + let vptr_entry_idx = + self.tcx.supertrait_vtable_slot((src_pointee_ty, dest_pointee_ty)); + let vtable_entries = self.vtable_entries(data_a.principal(), ty); + if let Some(entry_idx) = vptr_entry_idx { + let Some(&ty::VtblEntry::TraitVPtr(upcast_trait_ref)) = + vtable_entries.get(entry_idx) + else { + span_bug!( + self.cur_span(), + "invalid vtable entry index in {} -> {} upcast", + src_pointee_ty, + dest_pointee_ty ); - } else { - // In this case codegen would keep using the old vtable. We don't want to do - // that as it has the wrong trait. The reason codegen can do this is that - // one vtable is a prefix of the other, so we double-check that. - let vtable_entries_b = self.vtable_entries(data_b.principal(), ty); - assert!(&vtable_entries[..vtable_entries_b.len()] == vtable_entries_b); }; - } + let erased_trait_ref = + ty::ExistentialTraitRef::erase_self_ty(*self.tcx, upcast_trait_ref); + assert!(data_b.principal().is_some_and(|b| self.eq_in_param_env( + erased_trait_ref, + self.tcx.instantiate_bound_regions_with_erased(b) + ))); + } else { + // In this case codegen would keep using the old vtable. We don't want to do + // that as it has the wrong trait. The reason codegen can do this is that + // one vtable is a prefix of the other, so we double-check that. + let vtable_entries_b = self.vtable_entries(data_b.principal(), ty); + assert!(&vtable_entries[..vtable_entries_b.len()] == vtable_entries_b); + }; // Get the destination trait vtable and return that. let new_vptr = self.get_vtable_ptr(ty, data_b)?; diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 95a72d3cbc1d7..66a75113652f1 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -106,9 +106,6 @@ impl<'tcx, M: Machine<'tcx>> FnAbiOfHelpers<'tcx> for InterpCx<'tcx, M> { ) -> InterpErrorKind<'tcx> { match err { FnAbiError::Layout(err) => err_inval!(Layout(err)), - FnAbiError::AdjustForForeignAbi(err) => { - err_inval!(FnAbiAdjustForForeignAbi(err)) - } } } } diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 0cbb3b157f3f7..e14cc1dad3634 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -102,11 +102,11 @@ fn intern_as_new_static<'tcx>( alloc_id: AllocId, alloc: ConstAllocation<'tcx>, ) { - let feed = tcx.create_def(static_id, sym::nested, DefKind::Static { - safety: hir::Safety::Safe, - mutability: alloc.0.mutability, - nested: true, - }); + let feed = tcx.create_def( + static_id, + sym::nested, + DefKind::Static { safety: hir::Safety::Safe, mutability: alloc.0.mutability, nested: true }, + ); tcx.set_nested_alloc_id_static(alloc_id, feed.def_id()); if tcx.is_thread_local_static(static_id.into()) { diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 0664a882c1d50..4ca317e3a1e53 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -319,7 +319,25 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Check that the memory between them is dereferenceable at all, starting from the // origin pointer: `dist` is `a - b`, so it is based on `b`. - self.check_ptr_access_signed(b, dist, CheckInAllocMsg::OffsetFromTest)?; + self.check_ptr_access_signed(b, dist, CheckInAllocMsg::OffsetFromTest) + .map_err_kind(|_| { + // This could mean they point to different allocations, or they point to the same allocation + // but not the entire range between the pointers is in-bounds. + if let Ok((a_alloc_id, ..)) = self.ptr_try_get_alloc_id(a, 0) + && let Ok((b_alloc_id, ..)) = self.ptr_try_get_alloc_id(b, 0) + && a_alloc_id == b_alloc_id + { + err_ub_custom!( + fluent::const_eval_offset_from_out_of_bounds, + name = intrinsic_name, + ) + } else { + err_ub_custom!( + fluent::const_eval_offset_from_different_allocations, + name = intrinsic_name, + ) + } + })?; // Then check that this is also dereferenceable from `a`. This ensures that they are // derived from the same allocation. self.check_ptr_access_signed( @@ -747,7 +765,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { { let a: F = self.read_scalar(&args[0])?.to_float()?; let b: F = self.read_scalar(&args[1])?.to_float()?; - let res = self.adjust_nan(a.min(b), &[a, b]); + let res = if a == b { + // They are definitely not NaN (those are never equal), but they could be `+0` and `-0`. + // Let the machine decide which one to return. + M::equal_float_min_max(self, a, b) + } else { + self.adjust_nan(a.min(b), &[a, b]) + }; self.write_scalar(res, dest)?; interp_ok(()) } @@ -762,7 +786,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { { let a: F = self.read_scalar(&args[0])?.to_float()?; let b: F = self.read_scalar(&args[1])?.to_float()?; - let res = self.adjust_nan(a.max(b), &[a, b]); + let res = if a == b { + // They are definitely not NaN (those are never equal), but they could be `+0` and `-0`. + // Let the machine decide which one to return. + M::equal_float_min_max(self, a, b) + } else { + self.adjust_nan(a.max(b), &[a, b]) + }; self.write_scalar(res, dest)?; interp_ok(()) } diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 36e5a2ff750ae..1a799f5dea5b5 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -278,6 +278,12 @@ pub trait Machine<'tcx>: Sized { F2::NAN } + /// Determines the result of `min`/`max` on floats when the arguments are equal. + fn equal_float_min_max(_ecx: &InterpCx<'tcx, Self>, a: F, _b: F) -> F { + // By default, we pick the left argument. + a + } + /// Called before a basic block terminator is executed. #[inline] fn before_terminator(_ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> { @@ -287,6 +293,9 @@ pub trait Machine<'tcx>: Sized { /// Determines the result of a `NullaryOp::UbChecks` invocation. fn ub_checks(_ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool>; + /// Determines the result of a `NullaryOp::ContractChecks` invocation. + fn contract_checks(_ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool>; + /// Called when the interpreter encounters a `StatementKind::ConstEvalCounter` instruction. /// You can use this to detect long or endlessly running programs. #[inline] @@ -673,6 +682,13 @@ pub macro compile_time_machine(<$tcx: lifetime>) { interp_ok(true) } + #[inline(always)] + fn contract_checks(_ecx: &InterpCx<$tcx, Self>) -> InterpResult<$tcx, bool> { + // We can't look at `tcx.sess` here as that can differ across crates, which can lead to + // unsound differences in evaluating the same constant at different instantiation sites. + interp_ok(true) + } + #[inline(always)] fn adjust_global_allocation<'b>( _ecx: &InterpCx<$tcx, Self>, diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 0790db984e345..4f4b6785844dc 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -20,10 +20,10 @@ use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use tracing::{debug, instrument, trace}; use super::{ - AllocBytes, AllocId, AllocMap, AllocRange, Allocation, CheckAlignMsg, CheckInAllocMsg, - CtfeProvenance, GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, Misalignment, Pointer, - PointerArithmetic, Provenance, Scalar, alloc_range, err_ub, err_ub_custom, interp_ok, throw_ub, - throw_ub_custom, throw_unsup, throw_unsup_format, + AllocBytes, AllocId, AllocInit, AllocMap, AllocRange, Allocation, CheckAlignMsg, + CheckInAllocMsg, CtfeProvenance, GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, + Misalignment, Pointer, PointerArithmetic, Provenance, Scalar, alloc_range, err_ub, + err_ub_custom, interp_ok, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, }; use crate::fluent_generated as fluent; @@ -230,11 +230,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { size: Size, align: Align, kind: MemoryKind, + init: AllocInit, ) -> InterpResult<'tcx, Pointer> { let alloc = if M::PANIC_ON_ALLOC_FAIL { - Allocation::uninit(size, align) + Allocation::new(size, align, init) } else { - Allocation::try_uninit(size, align)? + Allocation::try_new(size, align, init)? }; self.insert_allocation(alloc, kind) } @@ -270,6 +271,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { M::adjust_alloc_root_pointer(self, Pointer::from(id), Some(kind)) } + /// If this grows the allocation, `init_growth` determines + /// whether the additional space will be initialized. pub fn reallocate_ptr( &mut self, ptr: Pointer>, @@ -277,6 +280,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { new_size: Size, new_align: Align, kind: MemoryKind, + init_growth: AllocInit, ) -> InterpResult<'tcx, Pointer> { let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr, 0)?; if offset.bytes() != 0 { @@ -289,7 +293,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // For simplicities' sake, we implement reallocate as "alloc, copy, dealloc". // This happens so rarely, the perf advantage is outweighed by the maintenance cost. - let new_ptr = self.allocate_ptr(new_size, new_align, kind)?; + // If requested, we zero-init the entire allocation, to ensure that a growing + // allocation has its new bytes properly set. For the part that is copied, + // `mem_copy` below will de-initialize things as necessary. + let new_ptr = self.allocate_ptr(new_size, new_align, kind, init_growth)?; let old_size = match old_size_and_align { Some((size, _align)) => size, None => self.get_alloc_raw(alloc_id)?.size(), @@ -830,9 +837,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// [`InterpCx::get_alloc_info`] if all you need to check is whether the kind is /// [`AllocKind::Dead`] because it doesn't have to look up the type and layout of statics. pub fn is_alloc_live(&self, id: AllocId) -> bool { - self.tcx.try_get_global_alloc(id).is_some() - || self.memory.alloc_map.contains_key_ref(&id) + self.memory.alloc_map.contains_key_ref(&id) || self.memory.extra_fn_ptr_map.contains_key(&id) + // We check `tcx` last as that has to acquire a lock in `many-seeds` mode. + // This also matches the order in `get_alloc_info`. + || self.tcx.try_get_global_alloc(id).is_some() } /// Obtain the size and alignment of an allocation, even if that allocation has @@ -1481,22 +1490,31 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Test if this value might be null. /// If the machine does not support ptr-to-int casts, this is conservative. pub fn scalar_may_be_null(&self, scalar: Scalar) -> InterpResult<'tcx, bool> { - interp_ok(match scalar.try_to_scalar_int() { - Ok(int) => int.is_null(), + match scalar.try_to_scalar_int() { + Ok(int) => interp_ok(int.is_null()), Err(_) => { - // Can only happen during CTFE. + // We can't cast this pointer to an integer. Can only happen during CTFE. let ptr = scalar.to_pointer(self)?; match self.ptr_try_get_alloc_id(ptr, 0) { Ok((alloc_id, offset, _)) => { - let size = self.get_alloc_info(alloc_id).size; - // If the pointer is out-of-bounds, it may be null. - // Note that one-past-the-end (offset == size) is still inbounds, and never null. - offset > size + let info = self.get_alloc_info(alloc_id); + // If the pointer is in-bounds (including "at the end"), it is definitely not null. + if offset <= info.size { + return interp_ok(false); + } + // If the allocation is N-aligned, and the offset is not divisible by N, + // then `base + offset` has a non-zero remainder after division by `N`, + // which means `base + offset` cannot be null. + if offset.bytes() % info.align.bytes() != 0 { + return interp_ok(false); + } + // We don't know enough, this might be null. + interp_ok(true) } Err(_offset) => bug!("a non-int scalar is always a pointer"), } } - }) + } } /// Turning a "maybe pointer" into a proper pointer (and some information diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index b861ffb6110d2..5d905cff1f216 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -704,8 +704,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { pub fn read_str(&self, mplace: &MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx, &str> { let len = mplace.len(self)?; let bytes = self.read_bytes_ptr_strip_provenance(mplace.ptr(), Size::from_bytes(len))?; - let str = std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?; - interp_ok(str) + let s = std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?; + interp_ok(s) } /// Read from a local of the current frame. Convenience method for [`InterpCx::local_at_frame_to_op`]. diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 5fa632fc57aaf..899670aeb62da 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -537,6 +537,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ImmTy::from_uint(val, usize_layout()) } UbChecks => ImmTy::from_bool(M::ub_checks(self)?, *self.tcx), + ContractChecks => ImmTy::from_bool(M::contract_checks(self)?, *self.tcx), }) } } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 0d97407161930..f5d3de7b1b270 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -12,9 +12,9 @@ use rustc_middle::{bug, mir, span_bug}; use tracing::{instrument, trace}; use super::{ - AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance, ImmTy, Immediate, InterpCx, InterpResult, - Machine, MemoryKind, Misalignment, OffsetMode, OpTy, Operand, Pointer, Projectable, Provenance, - Scalar, alloc_range, interp_ok, mir_assign_valid_types, + AllocInit, AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance, ImmTy, Immediate, InterpCx, + InterpResult, Machine, MemoryKind, Misalignment, OffsetMode, OpTy, Operand, Pointer, + Projectable, Provenance, Scalar, alloc_range, interp_ok, mir_assign_valid_types, }; #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] @@ -983,7 +983,7 @@ where let Some((size, align)) = self.size_and_align_of(&meta, &layout)? else { span_bug!(self.cur_span(), "cannot allocate space for `extern` type, size is not known") }; - let ptr = self.allocate_ptr(size, align, kind)?; + let ptr = self.allocate_ptr(size, align, kind, AllocInit::Uninit)?; interp_ok(self.ptr_with_meta_to_mplace(ptr.into(), meta, layout, /*unaligned*/ false)) } @@ -1017,9 +1017,9 @@ where /// This is allocated in immutable global memory and deduplicated. pub fn allocate_str_dedup( &mut self, - str: &str, + s: &str, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { - let bytes = str.as_bytes(); + let bytes = s.as_bytes(); let ptr = self.allocate_bytes_dedup(bytes)?; // Create length metadata for the string. diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 996142d7b03bd..8ecb3e13d5ceb 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -381,6 +381,7 @@ where OpaqueCast(ty) => { span_bug!(self.cur_span(), "OpaqueCast({ty}) encountered after borrowck") } + UnwrapUnsafeBinder(target) => base.transmute(self.layout_of(target)?, self)?, // We don't want anything happening here, this is here as a dummy. Subtype(_) => base.transmute(base.layout(), self)?, Field(field, _) => self.project_field(base, field.index())?, diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 32e77fe1024da..6a17da61c8b7b 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -9,13 +9,12 @@ use rustc_middle::ty::layout::FnAbiOf; use rustc_middle::ty::{self, Instance, Ty}; use rustc_middle::{bug, mir, span_bug}; use rustc_span::source_map::Spanned; -use rustc_span::{DesugaringKind, Span}; use rustc_target::callconv::FnAbi; use tracing::{info, instrument, trace}; use super::{ FnArg, FnVal, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemPlaceMeta, PlaceTy, - Projectable, interp_ok, throw_ub, + Projectable, Scalar, interp_ok, throw_ub, }; use crate::util; @@ -81,9 +80,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { use rustc_middle::mir::StatementKind::*; match &stmt.kind { - Assign(box (place, rvalue)) => { - self.eval_rvalue_into_place(rvalue, *place, stmt.source_info.span)? - } + Assign(box (place, rvalue)) => self.eval_rvalue_into_place(rvalue, *place)?, SetDiscriminant { place, variant_index } => { let dest = self.eval_place(**place)?; @@ -162,7 +159,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { &mut self, rvalue: &mir::Rvalue<'tcx>, place: mir::Place<'tcx>, - span: Span, ) -> InterpResult<'tcx> { let dest = self.eval_place(place)?; // FIXME: ensure some kind of non-aliasing between LHS and RHS? @@ -218,6 +214,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_repeat(operand, &dest)?; } + Len(place) => { + let src = self.eval_place(place)?; + let len = src.len(self)?; + self.write_scalar(Scalar::from_target_usize(len, self), &dest)?; + } + Ref(_, borrow_kind, place) => { let src = self.eval_place(place)?; let place = self.force_allocation(&src)?; @@ -235,11 +237,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_immediate(*val, &dest)?; } - RawPtr(_, place) => { + RawPtr(kind, place) => { // Figure out whether this is an addr_of of an already raw place. let place_base_raw = if place.is_indirect_first_projection() { let ty = self.frame().body.local_decls[place.local].ty; - ty.is_unsafe_ptr() + ty.is_raw_ptr() } else { // Not a deref, and thus not raw. false @@ -248,13 +250,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let src = self.eval_place(place)?; let place = self.force_allocation(&src)?; let mut val = ImmTy::from_immediate(place.to_ref(self), dest.layout); - if !place_base_raw - && span.desugaring_kind() != Some(DesugaringKind::IndexBoundsCheckReborrow) - { - // If this was not already raw, it needs retagging. - // As a special hack, we exclude the desugared `PtrMetadata(&raw const *_n)` - // from indexing. (Really we should not do any retag on `&raw` but that does not - // currently work with Stacked Borrows.) + if !place_base_raw && !kind.is_fake() { + // If this was not already raw, it needs retagging -- except for "fake" + // raw borrows whose defining property is that they do not get retagged. val = M::retag_ptr_value(self, mir::RetagKind::Raw, &val)?; } self.write_immediate(*val, &dest)?; @@ -279,6 +277,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let discr = self.discriminant_for_variant(op.layout.ty, variant)?; self.write_immediate(*discr, &dest)?; } + + WrapUnsafeBinder(ref op, _ty) => { + // Constructing an unsafe binder acts like a transmute + // since the operand's layout does not change. + let op = self.eval_operand(op, None)?; + self.copy_op_allow_transmute(&op, &dest)?; + } } trace!("{:?}", self.dump_place(&dest)); diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index af8d618b6b5e5..4cfaacebfcd0e 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -54,7 +54,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> &'tcx [VtblEntry<'tcx>] { if let Some(trait_) = trait_ { let trait_ref = trait_.with_self_ty(*self.tcx, dyn_ty); - let trait_ref = self.tcx.erase_regions(trait_ref); + let trait_ref = + self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_ref)); self.tcx.vtable_entries(trait_ref) } else { TyCtxt::COMMON_VTABLE_ENTRIES diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index ecb7c3fc93cd5..ba579e25f036a 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -1,12 +1,8 @@ -use std::ops::ControlFlow; - use rustc_hir::def_id::LocalDefId; use rustc_middle::mir; -use rustc_middle::mir::interpret::{Allocation, InterpResult, Pointer}; +use rustc_middle::mir::interpret::{AllocInit, Allocation, InterpResult, Pointer}; use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::ty::{ - self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, -}; +use rustc_middle::ty::{TyCtxt, TypeVisitable, TypeVisitableExt}; use tracing::debug; use super::{InterpCx, MPlaceTy, MemoryKind, interp_ok, throw_inval}; @@ -20,44 +16,10 @@ where T: TypeVisitable>, { debug!("ensure_monomorphic_enough: ty={:?}", ty); - if !ty.has_param() { - return interp_ok(()); - } - - struct FoundParam; - struct UsedParamsNeedInstantiationVisitor {} - - impl<'tcx> TypeVisitor> for UsedParamsNeedInstantiationVisitor { - type Result = ControlFlow; - - fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { - if !ty.has_param() { - return ControlFlow::Continue(()); - } - - match *ty.kind() { - ty::Param(_) => ControlFlow::Break(FoundParam), - ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) | ty::FnDef(..) => { - ControlFlow::Continue(()) - } - _ => ty.super_visit_with(self), - } - } - - fn visit_const(&mut self, c: ty::Const<'tcx>) -> Self::Result { - match c.kind() { - ty::ConstKind::Param(..) => ControlFlow::Break(FoundParam), - _ => c.super_visit_with(self), - } - } - } - - let mut vis = UsedParamsNeedInstantiationVisitor {}; - if matches!(ty.visit_with(&mut vis), ControlFlow::Break(FoundParam)) { + if ty.has_param() { throw_inval!(TooGeneric); - } else { - interp_ok(()) } + interp_ok(()) } impl<'tcx> InterpretationResult<'tcx> for mir::interpret::ConstAllocation<'tcx> { @@ -76,7 +38,7 @@ pub(crate) fn create_static_alloc<'tcx>( static_def_id: LocalDefId, layout: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, MPlaceTy<'tcx>> { - let alloc = Allocation::try_uninit(layout.size, layout.align.abi)?; + let alloc = Allocation::try_new(layout.size, layout.align.abi, AllocInit::Uninit)?; let alloc_id = ecx.tcx.reserve_and_set_static_alloc(static_def_id.into()); assert_eq!(ecx.machine.static_root_ids, None); ecx.machine.static_root_ids = Some((alloc_id, static_def_id)); diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index d75df1ad4425b..0ac34f4633b57 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -812,10 +812,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { if start == 1 && end == max_value { // Only null is the niche. So make sure the ptr is NOT null. if self.ecx.scalar_may_be_null(scalar)? { - throw_validation_failure!(self.path, NullablePtrOutOfRange { - range: valid_range, - max_value - }) + throw_validation_failure!( + self.path, + NullablePtrOutOfRange { range: valid_range, max_value } + ) } else { return interp_ok(()); } @@ -825,10 +825,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { } else { // Conservatively, we reject, because the pointer *could* have a bad // value. - throw_validation_failure!(self.path, PtrOutOfRange { - range: valid_range, - max_value - }) + throw_validation_failure!( + self.path, + PtrOutOfRange { range: valid_range, max_value } + ) } } }; @@ -836,11 +836,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { if valid_range.contains(bits) { interp_ok(()) } else { - throw_validation_failure!(self.path, OutOfRange { - value: format!("{bits}"), - range: valid_range, - max_value - }) + throw_validation_failure!( + self.path, + OutOfRange { value: format!("{bits}"), range: valid_range, max_value } + ) } } @@ -1240,6 +1239,17 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, self.visit_field(val, 0, &self.ecx.project_index(val, 0)?)?; } } + ty::Pat(base, pat) => { + // First check that the base type is valid + self.visit_value(&val.transmute(self.ecx.layout_of(*base)?, self.ecx)?)?; + // When you extend this match, make sure to also add tests to + // tests/ui/type/pattern_types/validity.rs(( + match **pat { + // Range patterns are precisely reflected into `valid_range` and thus + // handled fully by `visit_scalar` (called below). + ty::PatternKind::Range { .. } => {}, + } + } _ => { // default handler try_validation!( diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index b5adf06b30018..ed5489652fba6 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -46,12 +46,13 @@ pub fn provide(providers: &mut Providers) { }; providers.hooks.try_destructure_mir_constant_for_user_output = const_eval::try_destructure_mir_constant_for_user_output; - providers.valtree_to_const_val = |tcx, (ty, valtree)| { - const_eval::valtree_to_const_value(tcx, ty::TypingEnv::fully_monomorphized(), ty, valtree) - }; + providers.valtree_to_const_val = + |tcx, cv| const_eval::valtree_to_const_value(tcx, ty::TypingEnv::fully_monomorphized(), cv); providers.check_validity_requirement = |tcx, (init_kind, param_env_and_ty)| { util::check_validity_requirement(tcx, init_kind, param_env_and_ty) }; + providers.hooks.validate_scalar_in_layout = + |tcx, scalar, layout| util::validate_scalar_in_layout(tcx, scalar, layout); } /// `rustc_driver::main` installs a handler that will set this to `true` if diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs index 6dd9447cf5a5f..e926040e9ba15 100644 --- a/compiler/rustc_const_eval/src/util/caller_location.rs +++ b/compiler/rustc_const_eval/src/util/caller_location.rs @@ -1,7 +1,6 @@ use rustc_hir::LangItem; -use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_span::Symbol; use tracing::trace; @@ -48,15 +47,15 @@ fn alloc_caller_location<'tcx>( } pub(crate) fn const_caller_location_provider( - tcx: TyCtxtAt<'_>, + tcx: TyCtxt<'_>, file: Symbol, line: u32, col: u32, ) -> mir::ConstValue<'_> { trace!("const_caller_location: {}:{}:{}", file, line, col); let mut ecx = mk_eval_cx_to_read_const_val( - tcx.tcx, - tcx.span, + tcx, + rustc_span::DUMMY_SP, // FIXME: use a proper span here? ty::TypingEnv::fully_monomorphized(), CanAccessMutGlobal::No, ); diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs index a729d9325c84a..79baf91c3ce64 100644 --- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs +++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs @@ -1,9 +1,10 @@ use rustc_abi::{BackendRepr, FieldsShape, Scalar, Variants}; -use rustc_middle::bug; use rustc_middle::ty::layout::{ HasTyCtxt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, ValidityRequirement, }; -use rustc_middle::ty::{PseudoCanonicalInput, Ty, TyCtxt}; +use rustc_middle::ty::{PseudoCanonicalInput, ScalarInt, Ty, TyCtxt}; +use rustc_middle::{bug, ty}; +use rustc_span::DUMMY_SP; use crate::const_eval::{CanAccessMutGlobal, CheckAlignment, CompileTimeMachine}; use crate::interpret::{InterpCx, MemoryKind}; @@ -34,7 +35,7 @@ pub fn check_validity_requirement<'tcx>( let layout_cx = LayoutCx::new(tcx, input.typing_env); if kind == ValidityRequirement::Uninit || tcx.sess.opts.unstable_opts.strict_init_checks { - check_validity_requirement_strict(layout, &layout_cx, kind) + Ok(check_validity_requirement_strict(layout, &layout_cx, kind)) } else { check_validity_requirement_lax(layout, &layout_cx, kind) } @@ -46,10 +47,10 @@ fn check_validity_requirement_strict<'tcx>( ty: TyAndLayout<'tcx>, cx: &LayoutCx<'tcx>, kind: ValidityRequirement, -) -> Result> { +) -> bool { let machine = CompileTimeMachine::new(CanAccessMutGlobal::No, CheckAlignment::Error); - let mut cx = InterpCx::new(cx.tcx(), rustc_span::DUMMY_SP, cx.typing_env, machine); + let mut cx = InterpCx::new(cx.tcx(), DUMMY_SP, cx.typing_env, machine); let allocated = cx .allocate(ty, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap)) @@ -69,14 +70,13 @@ fn check_validity_requirement_strict<'tcx>( // due to this. // The value we are validating is temporary and discarded at the end of this function, so // there is no point in reseting provenance and padding. - Ok(cx - .validate_operand( - &allocated.into(), - /*recursive*/ false, - /*reset_provenance_and_padding*/ false, - ) - .discard_err() - .is_some()) + cx.validate_operand( + &allocated.into(), + /*recursive*/ false, + /*reset_provenance_and_padding*/ false, + ) + .discard_err() + .is_some() } /// Implements the 'lax' (default) version of the [`check_validity_requirement`] checks; see that @@ -168,3 +168,31 @@ fn check_validity_requirement_lax<'tcx>( Ok(true) } + +pub(crate) fn validate_scalar_in_layout<'tcx>( + tcx: TyCtxt<'tcx>, + scalar: ScalarInt, + ty: Ty<'tcx>, +) -> bool { + let machine = CompileTimeMachine::new(CanAccessMutGlobal::No, CheckAlignment::Error); + + let typing_env = ty::TypingEnv::fully_monomorphized(); + let mut cx = InterpCx::new(tcx, DUMMY_SP, typing_env, machine); + + let Ok(layout) = cx.layout_of(ty) else { + bug!("could not compute layout of {scalar:?}:{ty:?}") + }; + let allocated = cx + .allocate(layout, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap)) + .expect("OOM: failed to allocate for uninit check"); + + cx.write_scalar(scalar, &allocated).unwrap(); + + cx.validate_operand( + &allocated.into(), + /*recursive*/ false, + /*reset_provenance_and_padding*/ false, + ) + .discard_err() + .is_some() +} diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs index 25a9dbb2c1117..5be5ee8d1ae97 100644 --- a/compiler/rustc_const_eval/src/util/mod.rs +++ b/compiler/rustc_const_eval/src/util/mod.rs @@ -8,6 +8,7 @@ mod type_name; pub use self::alignment::{is_disaligned, is_within_packed}; pub use self::check_validity_requirement::check_validity_requirement; +pub(crate) use self::check_validity_requirement::validate_scalar_in_layout; pub use self::compare_types::{relate_types, sub_types}; pub use self::type_name::type_name; diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index c8ecddb046cd8..a8f83ca13e267 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -8,13 +8,13 @@ edition = "2021" arrayvec = { version = "0.7", default-features = false } bitflags = "2.4.1" either = "1.0" -elsa = "=1.7.1" +elsa = "1.11.0" ena = "0.14.3" -indexmap = { version = "2.4.0", features = ["rustc-rayon"] } +indexmap = "2.4.0" jobserver_crate = { version = "0.1.28", package = "jobserver" } measureme = "11" rustc-hash = "2.0.0" -rustc-rayon = "0.5.0" +rustc-rayon = { version = "0.5.1", features = ["indexmap"] } rustc-stable-hash = { version = "0.1.0", features = ["nightly"] } rustc_arena = { path = "../rustc_arena" } rustc_graphviz = { path = "../rustc_graphviz" } @@ -32,7 +32,7 @@ tracing = "0.1" version = "0.12" [target.'cfg(windows)'.dependencies.windows] -version = "0.57.0" +version = "0.59.0" features = [ "Win32_Foundation", "Win32_Storage_FileSystem", diff --git a/compiler/rustc_data_structures/src/flock.rs b/compiler/rustc_data_structures/src/flock.rs index e03962a54ecab..292a33d56469a 100644 --- a/compiler/rustc_data_structures/src/flock.rs +++ b/compiler/rustc_data_structures/src/flock.rs @@ -4,6 +4,7 @@ //! green/native threading. This is just a bare-bones enough solution for //! librustdoc, it is not production quality at all. +#[cfg(bootstrap)] cfg_match! { cfg(target_os = "linux") => { mod linux; @@ -27,4 +28,28 @@ cfg_match! { } } +#[cfg(not(bootstrap))] +cfg_match! { + target_os = "linux" => { + mod linux; + use linux as imp; + } + target_os = "redox" => { + mod linux; + use linux as imp; + } + unix => { + mod unix; + use unix as imp; + } + windows => { + mod windows; + use self::windows as imp; + } + _ => { + mod unsupported; + use unsupported as imp; + } +} + pub use imp::Lock; diff --git a/compiler/rustc_data_structures/src/flock/windows.rs b/compiler/rustc_data_structures/src/flock/windows.rs index 9739e501272c4..e761faee67b4f 100644 --- a/compiler/rustc_data_structures/src/flock/windows.rs +++ b/compiler/rustc_data_structures/src/flock/windows.rs @@ -60,9 +60,9 @@ impl Lock { unsafe { LockFileEx( - HANDLE(file.as_raw_handle() as isize), + HANDLE(file.as_raw_handle()), flags, - 0, + None, u32::MAX, u32::MAX, &mut overlapped, diff --git a/compiler/rustc_data_structures/src/graph/dominators/tests.rs b/compiler/rustc_data_structures/src/graph/dominators/tests.rs index ef82193a4b9df..6c078ca7c6ee4 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/tests.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/tests.rs @@ -15,17 +15,10 @@ fn diamond() { #[test] fn paper() { // example from the paper: - let graph = TestGraph::new(6, &[ - (6, 5), - (6, 4), - (5, 1), - (4, 2), - (4, 3), - (1, 2), - (2, 3), - (3, 2), - (2, 1), - ]); + let graph = TestGraph::new( + 6, + &[(6, 5), (6, 4), (5, 1), (4, 2), (4, 3), (1, 2), (2, 3), (3, 2), (2, 1)], + ); let d = dominators(&graph); assert_eq!(d.immediate_dominator(0), None); // <-- note that 0 is not in graph @@ -40,19 +33,10 @@ fn paper() { #[test] fn paper_slt() { // example from the paper: - let graph = TestGraph::new(1, &[ - (1, 2), - (1, 3), - (2, 3), - (2, 7), - (3, 4), - (3, 6), - (4, 5), - (5, 4), - (6, 7), - (7, 8), - (8, 5), - ]); + let graph = TestGraph::new( + 1, + &[(1, 2), (1, 3), (2, 3), (2, 7), (3, 4), (3, 6), (4, 5), (5, 4), (6, 7), (7, 8), (8, 5)], + ); dominators(&graph); } @@ -69,21 +53,24 @@ fn immediate_dominator() { #[test] fn transitive_dominator() { - let graph = TestGraph::new(0, &[ - // First tree branch. - (0, 1), - (1, 2), - (2, 3), - (3, 4), - // Second tree branch. - (1, 5), - (5, 6), - // Third tree branch. - (0, 7), - // These links make 0 the dominator for 2 and 3. - (7, 2), - (5, 3), - ]); + let graph = TestGraph::new( + 0, + &[ + // First tree branch. + (0, 1), + (1, 2), + (2, 3), + (3, 4), + // Second tree branch. + (1, 5), + (5, 6), + // Third tree branch. + (0, 7), + // These links make 0 the dominator for 2 and 3. + (7, 2), + (5, 3), + ], + ); let d = dominators(&graph); assert_eq!(d.immediate_dominator(2), Some(0)); diff --git a/compiler/rustc_data_structures/src/graph/implementation/mod.rs b/compiler/rustc_data_structures/src/graph/implementation/mod.rs index 43fdfe6ee0d73..7724e9347d822 100644 --- a/compiler/rustc_data_structures/src/graph/implementation/mod.rs +++ b/compiler/rustc_data_structures/src/graph/implementation/mod.rs @@ -22,7 +22,7 @@ use std::fmt::Debug; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use tracing::debug; #[cfg(test)] @@ -214,7 +214,7 @@ impl Graph { direction: Direction, entry_node: NodeIndex, ) -> Vec { - let mut visited = BitSet::new_empty(self.len_nodes()); + let mut visited = DenseBitSet::new_empty(self.len_nodes()); let mut stack = vec![]; let mut result = Vec::with_capacity(self.len_nodes()); let mut push_node = |stack: &mut Vec<_>, node: NodeIndex| { @@ -287,7 +287,7 @@ impl<'g, N: Debug, E: Debug> Iterator for AdjacentEdges<'g, N, E> { pub struct DepthFirstTraversal<'g, N, E> { graph: &'g Graph, stack: Vec, - visited: BitSet, + visited: DenseBitSet, direction: Direction, } @@ -297,7 +297,7 @@ impl<'g, N: Debug, E: Debug> DepthFirstTraversal<'g, N, E> { start_node: NodeIndex, direction: Direction, ) -> Self { - let mut visited = BitSet::new_empty(graph.len_nodes()); + let mut visited = DenseBitSet::new_empty(graph.len_nodes()); visited.insert(start_node.node_id()); DepthFirstTraversal { graph, stack: vec![start_node], visited, direction } } diff --git a/compiler/rustc_data_structures/src/graph/implementation/tests.rs b/compiler/rustc_data_structures/src/graph/implementation/tests.rs index 7a240f4e58bcc..32a6d9ec881a9 100644 --- a/compiler/rustc_data_structures/src/graph/implementation/tests.rs +++ b/compiler/rustc_data_structures/src/graph/implementation/tests.rs @@ -110,10 +110,13 @@ fn each_adjacent_from_a() { #[test] fn each_adjacent_from_b() { let graph = create_graph(); - test_adjacent_edges(&graph, NodeIndex(1), "B", &[("FB", "F"), ("AB", "A")], &[ - ("BD", "D"), - ("BC", "C"), - ]); + test_adjacent_edges( + &graph, + NodeIndex(1), + "B", + &[("FB", "F"), ("AB", "A")], + &[("BD", "D"), ("BC", "C")], + ); } #[test] diff --git a/compiler/rustc_data_structures/src/graph/iterate/mod.rs b/compiler/rustc_data_structures/src/graph/iterate/mod.rs index cbc6664d853a1..7b4573d7a84c7 100644 --- a/compiler/rustc_data_structures/src/graph/iterate/mod.rs +++ b/compiler/rustc_data_structures/src/graph/iterate/mod.rs @@ -1,6 +1,6 @@ use std::ops::ControlFlow; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_index::{IndexSlice, IndexVec}; use super::{DirectedGraph, StartNode, Successors}; @@ -78,7 +78,7 @@ where { graph: G, stack: Vec, - visited: BitSet, + visited: DenseBitSet, } impl DepthFirstSearch @@ -86,7 +86,7 @@ where G: DirectedGraph + Successors, { pub fn new(graph: G) -> Self { - Self { stack: vec![], visited: BitSet::new_empty(graph.num_nodes()), graph } + Self { stack: vec![], visited: DenseBitSet::new_empty(graph.num_nodes()), graph } } /// Version of `push_start_node` that is convenient for chained @@ -125,6 +125,16 @@ where pub fn visited(&self, node: G::Node) -> bool { self.visited.contains(node) } + + /// Returns a reference to the set of nodes that have been visited, with + /// the same caveats as [`Self::visited`]. + /// + /// When incorporating the visited nodes into another bitset, using bulk + /// operations like `union` or `intersect` can be more efficient than + /// processing each node individually. + pub fn visited_set(&self) -> &DenseBitSet { + &self.visited + } } impl std::fmt::Debug for DepthFirstSearch @@ -207,8 +217,8 @@ where { graph: &'graph G, stack: Vec>, - visited: BitSet, - settled: BitSet, + visited: DenseBitSet, + settled: DenseBitSet, } impl<'graph, G> TriColorDepthFirstSearch<'graph, G> @@ -219,8 +229,8 @@ where TriColorDepthFirstSearch { graph, stack: vec![], - visited: BitSet::new_empty(graph.num_nodes()), - settled: BitSet::new_empty(graph.num_nodes()), + visited: DenseBitSet::new_empty(graph.num_nodes()), + settled: DenseBitSet::new_empty(graph.num_nodes()), } } diff --git a/compiler/rustc_data_structures/src/graph/mod.rs b/compiler/rustc_data_structures/src/graph/mod.rs index 103ddd917bf84..4a1e5db6768db 100644 --- a/compiler/rustc_data_structures/src/graph/mod.rs +++ b/compiler/rustc_data_structures/src/graph/mod.rs @@ -4,6 +4,7 @@ pub mod dominators; pub mod implementation; pub mod iterate; mod reference; +pub mod reversed; pub mod scc; pub mod vec_graph; @@ -13,7 +14,23 @@ mod tests; pub trait DirectedGraph { type Node: Idx; + /// Returns the total number of nodes in this graph. + /// + /// Several graph algorithm implementations assume that every node ID is + /// strictly less than the number of nodes, i.e. nodes are densely numbered. + /// That assumption allows them to use `num_nodes` to allocate per-node + /// data structures, indexed by node. fn num_nodes(&self) -> usize; + + /// Iterates over all nodes of a graph in ascending numeric order. + /// + /// Assumes that nodes are densely numbered, i.e. every index in + /// `0..num_nodes` is a valid node. + fn iter_nodes( + &self, + ) -> impl Iterator + DoubleEndedIterator + ExactSizeIterator { + (0..self.num_nodes()).map(::new) + } } pub trait NumEdges: DirectedGraph { diff --git a/compiler/rustc_data_structures/src/graph/reversed.rs b/compiler/rustc_data_structures/src/graph/reversed.rs new file mode 100644 index 0000000000000..9b726deaa15b6 --- /dev/null +++ b/compiler/rustc_data_structures/src/graph/reversed.rs @@ -0,0 +1,42 @@ +use crate::graph::{DirectedGraph, Predecessors, Successors}; + +/// View that reverses the direction of edges in its underlying graph, so that +/// successors become predecessors and vice-versa. +/// +/// Because of `impl Graph for &G`, the underlying graph can be +/// wrapped by-reference instead of by-value if desired. +#[derive(Clone, Copy, Debug)] +pub struct ReversedGraph { + pub inner: G, +} + +impl ReversedGraph { + pub fn new(inner: G) -> Self { + Self { inner } + } +} + +impl DirectedGraph for ReversedGraph { + type Node = G::Node; + + fn num_nodes(&self) -> usize { + self.inner.num_nodes() + } +} + +// Implementing `StartNode` is not possible in general, because the start node +// of an underlying graph is instead an _end_ node in the reversed graph. +// But would be possible to define another wrapper type that adds an explicit +// start node to its underlying graph, if desired. + +impl Successors for ReversedGraph { + fn successors(&self, node: Self::Node) -> impl Iterator { + self.inner.predecessors(node) + } +} + +impl Predecessors for ReversedGraph { + fn predecessors(&self, node: Self::Node) -> impl Iterator { + self.inner.successors(node) + } +} diff --git a/compiler/rustc_data_structures/src/graph/scc/mod.rs b/compiler/rustc_data_structures/src/graph/scc/mod.rs index 06fedef00fc3f..93f6192b10b03 100644 --- a/compiler/rustc_data_structures/src/graph/scc/mod.rs +++ b/compiler/rustc_data_structures/src/graph/scc/mod.rs @@ -333,8 +333,8 @@ where to_annotation, }; - let scc_indices = (0..num_nodes) - .map(G::Node::new) + let scc_indices = graph + .iter_nodes() .map(|node| match this.start_walk_from(node) { WalkReturn::Complete { scc_index, .. } => scc_index, WalkReturn::Cycle { min_depth, .. } => { diff --git a/compiler/rustc_data_structures/src/graph/scc/tests.rs b/compiler/rustc_data_structures/src/graph/scc/tests.rs index 7c876c82af293..373f87bfdbcfa 100644 --- a/compiler/rustc_data_structures/src/graph/scc/tests.rs +++ b/compiler/rustc_data_structures/src/graph/scc/tests.rs @@ -326,46 +326,49 @@ fn test_bug_max_leak_minimised() { #[test] fn test_bug_max_leak() { - let graph = TestGraph::new(8, &[ - (0, 0), - (0, 18), - (0, 19), - (0, 1), - (0, 2), - (0, 7), - (0, 8), - (0, 23), - (18, 0), - (18, 12), - (19, 0), - (19, 25), - (12, 18), - (12, 3), - (12, 5), - (3, 12), - (3, 21), - (3, 22), - (5, 13), - (21, 3), - (22, 3), - (13, 5), - (13, 4), - (4, 13), - (4, 0), - (2, 11), - (7, 6), - (6, 20), - (20, 6), - (8, 17), - (17, 9), - (9, 16), - (16, 26), - (26, 15), - (15, 10), - (10, 14), - (14, 27), - (23, 24), - ]); + let graph = TestGraph::new( + 8, + &[ + (0, 0), + (0, 18), + (0, 19), + (0, 1), + (0, 2), + (0, 7), + (0, 8), + (0, 23), + (18, 0), + (18, 12), + (19, 0), + (19, 25), + (12, 18), + (12, 3), + (12, 5), + (3, 12), + (3, 21), + (3, 22), + (5, 13), + (21, 3), + (22, 3), + (13, 5), + (13, 4), + (4, 13), + (4, 0), + (2, 11), + (7, 6), + (6, 20), + (20, 6), + (8, 17), + (17, 9), + (9, 16), + (16, 26), + (26, 15), + (15, 10), + (10, 14), + (14, 27), + (23, 24), + ], + ); let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w { 22 => MaxReached(1), 24 => MaxReached(2), diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 65d586124b33a..6ef73debadd50 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -76,6 +76,7 @@ pub mod sync; pub mod tagged_ptr; pub mod temp_dir; pub mod thinvec; +pub mod thousands; pub mod transitive_relation; pub mod unhash; pub mod unord; diff --git a/compiler/rustc_data_structures/src/marker.rs b/compiler/rustc_data_structures/src/marker.rs index 2b629024bfe45..6ae97222f77fd 100644 --- a/compiler/rustc_data_structures/src/marker.rs +++ b/compiler/rustc_data_structures/src/marker.rs @@ -72,7 +72,7 @@ impl_dyn_send!( [Vec where T: DynSend, A: std::alloc::Allocator + DynSend] [Box where T: ?Sized + DynSend, A: std::alloc::Allocator + DynSend] [crate::sync::RwLock where T: DynSend] - [crate::tagged_ptr::CopyTaggedPtr where P: Send + crate::tagged_ptr::Pointer, T: Send + crate::tagged_ptr::Tag, const CP: bool] + [crate::tagged_ptr::TaggedRef<'a, P, T> where 'a, P: Sync, T: Send + crate::tagged_ptr::Tag] [rustc_arena::TypedArena where T: DynSend] [indexmap::IndexSet where V: DynSend, S: DynSend] [indexmap::IndexMap where K: DynSend, V: DynSend, S: DynSend] @@ -148,7 +148,7 @@ impl_dyn_sync!( [crate::sync::RwLock where T: DynSend + DynSync] [crate::sync::WorkerLocal where T: DynSend] [crate::intern::Interned<'a, T> where 'a, T: DynSync] - [crate::tagged_ptr::CopyTaggedPtr where P: Sync + crate::tagged_ptr::Pointer, T: Sync + crate::tagged_ptr::Tag, const CP: bool] + [crate::tagged_ptr::TaggedRef<'a, P, T> where 'a, P: Sync, T: Sync + crate::tagged_ptr::Tag] [parking_lot::lock_api::Mutex where R: DynSync, T: ?Sized + DynSend] [parking_lot::lock_api::RwLock where R: DynSync, T: ?Sized + DynSend + DynSync] [indexmap::IndexSet where V: DynSync, S: DynSync] diff --git a/compiler/rustc_data_structures/src/memmap.rs b/compiler/rustc_data_structures/src/memmap.rs index c7f66b2fee80f..d64a5862f4e76 100644 --- a/compiler/rustc_data_structures/src/memmap.rs +++ b/compiler/rustc_data_structures/src/memmap.rs @@ -3,13 +3,13 @@ use std::io; use std::ops::{Deref, DerefMut}; /// A trivial wrapper for [`memmap2::Mmap`] (or `Vec` on WASM). -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(any(miri, target_arch = "wasm32")))] pub struct Mmap(memmap2::Mmap); -#[cfg(target_arch = "wasm32")] +#[cfg(any(miri, target_arch = "wasm32"))] pub struct Mmap(Vec); -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(any(miri, target_arch = "wasm32")))] impl Mmap { /// # Safety /// @@ -29,7 +29,7 @@ impl Mmap { } } -#[cfg(target_arch = "wasm32")] +#[cfg(any(miri, target_arch = "wasm32"))] impl Mmap { #[inline] pub unsafe fn map(mut file: File) -> io::Result { @@ -56,13 +56,13 @@ impl AsRef<[u8]> for Mmap { } } -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(any(miri, target_arch = "wasm32")))] pub struct MmapMut(memmap2::MmapMut); -#[cfg(target_arch = "wasm32")] +#[cfg(any(miri, target_arch = "wasm32"))] pub struct MmapMut(Vec); -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(any(miri, target_arch = "wasm32")))] impl MmapMut { #[inline] pub fn map_anon(len: usize) -> io::Result { @@ -82,7 +82,7 @@ impl MmapMut { } } -#[cfg(target_arch = "wasm32")] +#[cfg(any(miri, target_arch = "wasm32"))] impl MmapMut { #[inline] pub fn map_anon(len: usize) -> io::Result { diff --git a/compiler/rustc_data_structures/src/obligation_forest/tests.rs b/compiler/rustc_data_structures/src/obligation_forest/tests.rs index 739ef74e3f7a9..1916a8e2b6b8b 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/tests.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/tests.rs @@ -349,10 +349,10 @@ fn diamond() { )); assert_eq!(d_count, 1); assert_eq!(ok.len(), 0); - assert_eq!(err, vec![super::Error { - error: "operation failed", - backtrace: vec!["D'", "A'.1", "A'"] - }]); + assert_eq!( + err, + vec![super::Error { error: "operation failed", backtrace: vec!["D'", "A'.1", "A'"] }] + ); let errors = forest.to_errors(()); assert_eq!(errors.len(), 0); diff --git a/compiler/rustc_data_structures/src/owned_slice.rs b/compiler/rustc_data_structures/src/owned_slice.rs index c8be0ab52e9d5..0c00e4f4a4b31 100644 --- a/compiler/rustc_data_structures/src/owned_slice.rs +++ b/compiler/rustc_data_structures/src/owned_slice.rs @@ -1,15 +1,10 @@ use std::borrow::Borrow; use std::ops::Deref; - -// Use our fake Send/Sync traits when on not parallel compiler, -// so that `OwnedSlice` only implements/requires Send/Sync -// for parallel compiler builds. -use crate::sync; -use crate::sync::Lrc; +use std::sync::Arc; /// An owned slice. /// -/// This is similar to `Lrc<[u8]>` but allows slicing and using anything as the +/// This is similar to `Arc<[u8]>` but allows slicing and using anything as the /// backing buffer. /// /// See [`slice_owned`] for `OwnedSlice` construction and examples. @@ -34,7 +29,7 @@ pub struct OwnedSlice { // \/ // ⊂(´・◡・⊂ )∘˚˳° (I am the phantom remnant of #97770) #[expect(dead_code)] - owner: Lrc, + owner: Arc, } /// Makes an [`OwnedSlice`] out of an `owner` and a `slicer` function. @@ -61,7 +56,7 @@ pub struct OwnedSlice { /// ``` pub fn slice_owned(owner: O, slicer: F) -> OwnedSlice where - O: sync::Send + sync::Sync + 'static, + O: Send + Sync + 'static, F: FnOnce(&O) -> &[u8], { try_slice_owned(owner, |x| Ok::<_, !>(slicer(x))).into_ok() @@ -72,7 +67,7 @@ where /// See [`slice_owned`] for the infallible version. pub fn try_slice_owned(owner: O, slicer: F) -> Result where - O: sync::Send + sync::Sync + 'static, + O: Send + Sync + 'static, F: FnOnce(&O) -> Result<&[u8], E>, { // We wrap the owner of the bytes in, so it doesn't move. @@ -86,7 +81,7 @@ where // N.B. the HRTB on the `slicer` is important — without it the caller could provide // a short lived slice, unrelated to the owner. - let owner = Lrc::new(owner); + let owner = Arc::new(owner); let bytes = slicer(&*owner)?; Ok(OwnedSlice { bytes, owner }) @@ -139,10 +134,10 @@ impl Borrow<[u8]> for OwnedSlice { } // Safety: `OwnedSlice` is conceptually `(&'self.1 [u8], Arc)`, which is `Send` -unsafe impl sync::Send for OwnedSlice {} +unsafe impl Send for OwnedSlice {} // Safety: `OwnedSlice` is conceptually `(&'self.1 [u8], Arc)`, which is `Sync` -unsafe impl sync::Sync for OwnedSlice {} +unsafe impl Sync for OwnedSlice {} #[cfg(test)] mod tests; diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs index 19050746c2f18..18e98e6c39fa9 100644 --- a/compiler/rustc_data_structures/src/profiling.rs +++ b/compiler/rustc_data_structures/src/profiling.rs @@ -860,6 +860,7 @@ fn get_thread_id() -> u32 { } // Memory reporting +#[cfg(bootstrap)] cfg_match! { cfg(windows) => { pub fn get_resident_set_size() -> Option { @@ -921,5 +922,67 @@ cfg_match! { } } +#[cfg(not(bootstrap))] +cfg_match! { + windows => { + pub fn get_resident_set_size() -> Option { + use std::mem; + + use windows::{ + Win32::System::ProcessStatus::{K32GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS}, + Win32::System::Threading::GetCurrentProcess, + }; + + let mut pmc = PROCESS_MEMORY_COUNTERS::default(); + let pmc_size = mem::size_of_val(&pmc); + unsafe { + K32GetProcessMemoryInfo( + GetCurrentProcess(), + &mut pmc, + pmc_size as u32, + ) + } + .ok() + .ok()?; + + Some(pmc.WorkingSetSize) + } + } + target_os = "macos" => { + pub fn get_resident_set_size() -> Option { + use libc::{c_int, c_void, getpid, proc_pidinfo, proc_taskinfo, PROC_PIDTASKINFO}; + use std::mem; + const PROC_TASKINFO_SIZE: c_int = mem::size_of::() as c_int; + + unsafe { + let mut info: proc_taskinfo = mem::zeroed(); + let info_ptr = &mut info as *mut proc_taskinfo as *mut c_void; + let pid = getpid() as c_int; + let ret = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, info_ptr, PROC_TASKINFO_SIZE); + if ret == PROC_TASKINFO_SIZE { + Some(info.pti_resident_size as usize) + } else { + None + } + } + } + } + unix => { + pub fn get_resident_set_size() -> Option { + let field = 1; + let contents = fs::read("/proc/self/statm").ok()?; + let contents = String::from_utf8(contents).ok()?; + let s = contents.split_whitespace().nth(field)?; + let npages = s.parse::().ok()?; + Some(npages * 4096) + } + } + _ => { + pub fn get_resident_set_size() -> Option { + None + } + } +} + #[cfg(test)] mod tests; diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index 0872bd2c9acc9..9cd0cc499ca32 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use std::mem; use std::num::NonZero; -use rustc_index::bit_set::{self, BitSet}; +use rustc_index::bit_set::{self, DenseBitSet}; use rustc_index::{Idx, IndexSlice, IndexVec}; use smallvec::SmallVec; @@ -544,7 +544,7 @@ where } } -impl HashStable for BitSet { +impl HashStable for DenseBitSet { fn hash_stable(&self, _ctx: &mut CTX, hasher: &mut StableHasher) { ::std::hash::Hash::hash(self, hasher); } diff --git a/compiler/rustc_data_structures/src/stable_hasher/tests.rs b/compiler/rustc_data_structures/src/stable_hasher/tests.rs index aab50a13af0e6..635f241847c43 100644 --- a/compiler/rustc_data_structures/src/stable_hasher/tests.rs +++ b/compiler/rustc_data_structures/src/stable_hasher/tests.rs @@ -17,9 +17,9 @@ fn hash>(t: &T) -> Hash128 { // Check that bit set hash includes the domain size. #[test] fn test_hash_bit_set() { - use rustc_index::bit_set::BitSet; - let a: BitSet = BitSet::new_empty(1); - let b: BitSet = BitSet::new_empty(2); + use rustc_index::bit_set::DenseBitSet; + let a: DenseBitSet = DenseBitSet::new_empty(1); + let b: DenseBitSet = DenseBitSet::new_empty(2); assert_ne!(a, b); assert_ne!(hash(&a), hash(&b)); } diff --git a/compiler/rustc_data_structures/src/stack.rs b/compiler/rustc_data_structures/src/stack.rs index 3d6d000348324..102b364091154 100644 --- a/compiler/rustc_data_structures/src/stack.rs +++ b/compiler/rustc_data_structures/src/stack.rs @@ -17,6 +17,18 @@ const STACK_PER_RECURSION: usize = 16 * 1024 * 1024; // 16MB /// /// Should not be sprinkled around carelessly, as it causes a little bit of overhead. #[inline] +#[cfg(not(miri))] pub fn ensure_sufficient_stack(f: impl FnOnce() -> R) -> R { stacker::maybe_grow(RED_ZONE, STACK_PER_RECURSION, f) } + +/// Grows the stack on demand to prevent stack overflow. Call this in strategic locations +/// to "break up" recursive calls. E.g. almost any call to `visit_expr` or equivalent can benefit +/// from this. +/// +/// Should not be sprinkled around carelessly, as it causes a little bit of overhead. +#[cfg(miri)] +#[inline] +pub fn ensure_sufficient_stack(f: impl FnOnce() -> R) -> R { + f() +} diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 7a9533031f4bd..37b54fe38ff74 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -18,15 +18,8 @@ //! //! | Type | Serial version | Parallel version | //! | ----------------------- | ------------------- | ------------------------------- | -//! | `Lrc` | `rc::Rc` | `sync::Arc` | -//! |` Weak` | `rc::Weak` | `sync::Weak` | //! | `LRef<'a, T>` [^2] | `&'a mut T` | `&'a T` | //! | | | | -//! | `AtomicBool` | `Cell` | `atomic::AtomicBool` | -//! | `AtomicU32` | `Cell` | `atomic::AtomicU32` | -//! | `AtomicU64` | `Cell` | `atomic::AtomicU64` | -//! | `AtomicUsize` | `Cell` | `atomic::AtomicUsize` | -//! | | | | //! | `Lock` | `RefCell` | `RefCell` or | //! | | | `parking_lot::Mutex` | //! | `RwLock` | `RefCell` | `parking_lot::RwLock` | @@ -104,18 +97,15 @@ mod mode { // FIXME(parallel_compiler): Get rid of these aliases across the compiler. -pub use std::marker::{Send, Sync}; +pub use std::sync::OnceLock; // Use portable AtomicU64 for targets without native 64-bit atomics #[cfg(target_has_atomic = "64")] pub use std::sync::atomic::AtomicU64; -pub use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize}; -pub use std::sync::{Arc as Lrc, OnceLock, Weak}; pub use mode::{is_dyn_thread_safe, set_dyn_thread_safe_mode}; pub use parking_lot::{ - MappedMutexGuard as MappedLockGuard, MappedRwLockReadGuard as MappedReadGuard, - MappedRwLockWriteGuard as MappedWriteGuard, RwLockReadGuard as ReadGuard, - RwLockWriteGuard as WriteGuard, + MappedRwLockReadGuard as MappedReadGuard, MappedRwLockWriteGuard as MappedWriteGuard, + RwLockReadGuard as ReadGuard, RwLockWriteGuard as WriteGuard, }; #[cfg(not(target_has_atomic = "64"))] pub use portable_atomic::AtomicU64; @@ -204,12 +194,6 @@ impl RwLock { } } - #[inline(always)] - #[track_caller] - pub fn with_read_lock R, R>(&self, f: F) -> R { - f(&*self.read()) - } - #[inline(always)] pub fn try_write(&self) -> Result, ()> { self.0.try_write().ok_or(()) @@ -224,12 +208,6 @@ impl RwLock { } } - #[inline(always)] - #[track_caller] - pub fn with_write_lock R, R>(&self, f: F) -> R { - f(&mut *self.write()) - } - #[inline(always)] #[track_caller] pub fn borrow(&self) -> ReadGuard<'_, T> { @@ -241,20 +219,4 @@ impl RwLock { pub fn borrow_mut(&self) -> WriteGuard<'_, T> { self.write() } - - #[inline(always)] - pub fn leak(&self) -> &T { - let guard = self.read(); - let ret = unsafe { &*(&raw const *guard) }; - std::mem::forget(guard); - ret - } -} - -// FIXME: Probably a bad idea -impl Clone for RwLock { - #[inline] - fn clone(&self) -> Self { - RwLock::new(self.borrow().clone()) - } } diff --git a/compiler/rustc_data_structures/src/sync/freeze.rs b/compiler/rustc_data_structures/src/sync/freeze.rs index 5236c9fe15679..9720b22ea7d1b 100644 --- a/compiler/rustc_data_structures/src/sync/freeze.rs +++ b/compiler/rustc_data_structures/src/sync/freeze.rs @@ -3,9 +3,9 @@ use std::intrinsics::likely; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::ptr::NonNull; -use std::sync::atomic::Ordering; +use std::sync::atomic::{AtomicBool, Ordering}; -use crate::sync::{AtomicBool, DynSend, DynSync, ReadGuard, RwLock, WriteGuard}; +use crate::sync::{DynSend, DynSync, ReadGuard, RwLock, WriteGuard}; /// A type which allows mutation using a lock until /// the value is frozen and can be accessed lock-free. diff --git a/compiler/rustc_data_structures/src/sync/worker_local.rs b/compiler/rustc_data_structures/src/sync/worker_local.rs index d75af00985047..402ec9827bbaa 100644 --- a/compiler/rustc_data_structures/src/sync/worker_local.rs +++ b/compiler/rustc_data_structures/src/sync/worker_local.rs @@ -106,12 +106,6 @@ pub struct WorkerLocal { registry: Registry, } -// This is safe because the `deref` call will return a reference to a `T` unique to each thread -// or it will panic for threads without an associated local. So there isn't a need for `T` to do -// it's own synchronization. The `verify` method on `RegistryId` has an issue where the id -// can be reused, but `WorkerLocal` has a reference to `Registry` which will prevent any reuse. -unsafe impl Sync for WorkerLocal {} - impl WorkerLocal { /// Creates a new worker local where the `initial` closure computes the /// value this worker local should take for each thread in the registry. @@ -138,6 +132,11 @@ impl Deref for WorkerLocal { fn deref(&self) -> &T { // This is safe because `verify` will only return values less than // `self.registry.thread_limit` which is the size of the `self.locals` array. + + // The `deref` call will return a reference to a `T` unique to each thread + // or it will panic for threads without an associated local. So there isn't a need for `T` to do + // it's own synchronization. The `verify` method on `RegistryId` has an issue where the id + // can be reused, but `WorkerLocal` has a reference to `Registry` which will prevent any reuse. unsafe { &self.locals.get_unchecked(self.registry.id().verify()).0 } } } diff --git a/compiler/rustc_data_structures/src/tagged_ptr.rs b/compiler/rustc_data_structures/src/tagged_ptr.rs index 2914eece6796b..94db421f77e89 100644 --- a/compiler/rustc_data_structures/src/tagged_ptr.rs +++ b/compiler/rustc_data_structures/src/tagged_ptr.rs @@ -1,116 +1,26 @@ -//! This module implements tagged pointers. +//! This module implements tagged pointers. In order to utilize the pointer +//! packing, you must have a tag type implementing the [`Tag`] trait. //! -//! In order to utilize the pointer packing, you must have two types: a pointer, -//! and a tag. -//! -//! The pointer must implement the [`Pointer`] trait, with the primary -//! requirement being convertible to and from a raw pointer. Note that the -//! pointer must be dereferenceable, so raw pointers generally cannot implement -//! the [`Pointer`] trait. This implies that the pointer must also be non-null. -//! -//! Many common pointer types already implement the [`Pointer`] trait. -//! -//! The tag must implement the [`Tag`] trait. -//! -//! We assert that the tag and the [`Pointer`] types are compatible at compile +//! We assert that the tag and the reference type is compatible at compile //! time. +use std::fmt; +use std::hash::{Hash, Hasher}; +use std::marker::PhantomData; +use std::num::NonZero; use std::ops::Deref; use std::ptr::NonNull; -use std::rc::Rc; -use std::sync::Arc; use crate::aligned::Aligned; +use crate::stable_hasher::{HashStable, StableHasher}; -mod copy; -mod drop; -mod impl_tag; - -pub use copy::CopyTaggedPtr; -pub use drop::TaggedPtr; - -/// This describes the pointer type encapsulated by [`TaggedPtr`] and -/// [`CopyTaggedPtr`]. -/// -/// # Safety -/// -/// The pointer returned from [`into_ptr`] must be a [valid], pointer to -/// [`::Target`]. -/// -/// Note that if `Self` implements [`DerefMut`] the pointer returned from -/// [`into_ptr`] must be valid for writes (and thus calling [`NonNull::as_mut`] -/// on it must be safe). -/// -/// The [`BITS`] constant must be correct. [`BITS`] least-significant bits, -/// must be zero on all pointers returned from [`into_ptr`]. -/// -/// For example, if the alignment of [`Self::Target`] is 2, then `BITS` should be 1. -/// -/// [`BITS`]: Pointer::BITS -/// [`into_ptr`]: Pointer::into_ptr -/// [valid]: std::ptr#safety -/// [`::Target`]: Deref::Target -/// [`Self::Target`]: Deref::Target -/// [`DerefMut`]: std::ops::DerefMut -pub unsafe trait Pointer: Deref { - /// Number of unused (always zero) **least-significant bits** in this - /// pointer, usually related to the pointees alignment. - /// - /// For example if [`BITS`] = `2`, then given `ptr = Self::into_ptr(..)`, - /// `ptr.addr() & 0b11 == 0` must be true. - /// - /// Most likely the value you want to use here is the following, unless - /// your [`Self::Target`] type is unsized (e.g., `ty::List` in rustc) - /// or your pointer is over/under aligned, in which case you'll need to - /// manually figure out what the right type to pass to [`bits_for`] is, or - /// what the value to set here. - /// - /// ```rust - /// # use std::ops::Deref; - /// # use rustc_data_structures::tagged_ptr::bits_for; - /// # struct T; - /// # impl Deref for T { type Target = u8; fn deref(&self) -> &u8 { &0 } } - /// # impl T { - /// const BITS: u32 = bits_for::<::Target>(); - /// # } - /// ``` - /// - /// [`BITS`]: Pointer::BITS - /// [`Self::Target`]: Deref::Target - const BITS: u32; - - /// Turns this pointer into a raw, non-null pointer. - /// - /// The inverse of this function is [`from_ptr`]. - /// - /// This function guarantees that the least-significant [`Self::BITS`] bits - /// are zero. - /// - /// [`from_ptr`]: Pointer::from_ptr - /// [`Self::BITS`]: Pointer::BITS - fn into_ptr(self) -> NonNull; - - /// Re-creates the original pointer, from a raw pointer returned by [`into_ptr`]. - /// - /// # Safety - /// - /// The passed `ptr` must be returned from [`into_ptr`]. - /// - /// This acts as [`ptr::read::()`] semantically, it should not be called more than - /// once on non-[`Copy`] `Pointer`s. - /// - /// [`into_ptr`]: Pointer::into_ptr - /// [`ptr::read::()`]: std::ptr::read - unsafe fn from_ptr(ptr: NonNull) -> Self; -} - -/// This describes tags that the [`TaggedPtr`] struct can hold. +/// This describes tags that the [`TaggedRef`] struct can hold. /// /// # Safety /// -/// The [`BITS`] constant must be correct. -/// -/// No more than [`BITS`] least-significant bits may be set in the returned usize. +/// - The [`BITS`] constant must be correct. +/// - No more than [`BITS`] least-significant bits may be set in the returned usize. +/// - [`Eq`] and [`Hash`] must be implementable with the returned `usize` from `into_usize`. /// /// [`BITS`]: Tag::BITS pub unsafe trait Tag: Copy { @@ -166,118 +76,217 @@ pub const fn bits_for_tags(mut tags: &[usize]) -> u32 { bits } -unsafe impl Pointer for Box { - const BITS: u32 = bits_for::(); +/// A covariant [`Copy`] tagged borrow. This is essentially `{ pointer: &'a P, tag: T }` packed +/// in a single reference. +pub struct TaggedRef<'a, Pointee: Aligned + ?Sized, T: Tag> { + /// This is semantically a pair of `pointer: &'a P` and `tag: T` fields, + /// however we pack them in a single pointer, to save space. + /// + /// We pack the tag into the **most**-significant bits of the pointer to + /// ease retrieval of the value. A left shift is a multiplication and + /// those are embeddable in instruction encoding, for example: + /// + /// ```asm + /// // () + /// example::shift_read3: + /// mov eax, dword ptr [8*rdi] + /// ret + /// + /// example::mask_read3: + /// and rdi, -8 + /// mov eax, dword ptr [rdi] + /// ret + /// ``` + /// + /// This is ASM outputted by rustc for reads of values behind tagged + /// pointers for different approaches of tagging: + /// - `shift_read3` uses `<< 3` (the tag is in the most-significant bits) + /// - `mask_read3` uses `& !0b111` (the tag is in the least-significant bits) + /// + /// The shift approach thus produces less instructions and is likely faster + /// (see ). + /// + /// Encoding diagram: + /// ```text + /// [ packed.addr ] + /// [ tag ] [ pointer.addr >> T::BITS ] <-- usize::BITS - T::BITS bits + /// ^ + /// | + /// T::BITS bits + /// ``` + /// + /// The tag can be retrieved by `packed.addr() >> T::BITS` and the pointer + /// can be retrieved by `packed.map_addr(|addr| addr << T::BITS)`. + packed: NonNull, + tag_pointer_ghost: PhantomData<(&'a Pointee, T)>, +} +impl<'a, P, T> TaggedRef<'a, P, T> +where + P: Aligned + ?Sized, + T: Tag, +{ + /// Tags `pointer` with `tag`. + /// + /// [`TaggedRef`]: crate::tagged_ptr::TaggedRef #[inline] - fn into_ptr(self) -> NonNull { - // Safety: pointers from `Box::into_raw` are valid & non-null - unsafe { NonNull::new_unchecked(Box::into_raw(self)) } + pub fn new(pointer: &'a P, tag: T) -> Self { + Self { packed: Self::pack(NonNull::from(pointer), tag), tag_pointer_ghost: PhantomData } } + /// Retrieves the pointer. #[inline] - unsafe fn from_ptr(ptr: NonNull) -> Self { - // Safety: `ptr` comes from `into_ptr` which calls `Box::into_raw` - unsafe { Box::from_raw(ptr.as_ptr()) } + pub fn pointer(self) -> &'a P { + // SAFETY: pointer_raw returns the original pointer + unsafe { self.pointer_raw().as_ref() } } -} - -unsafe impl Pointer for Rc { - const BITS: u32 = bits_for::(); + /// Retrieves the tag. #[inline] - fn into_ptr(self) -> NonNull { - // Safety: pointers from `Rc::into_raw` are valid & non-null - unsafe { NonNull::new_unchecked(Rc::into_raw(self).cast_mut()) } + pub fn tag(&self) -> T { + // Unpack the tag, according to the `self.packed` encoding scheme + let tag = self.packed.addr().get() >> Self::TAG_BIT_SHIFT; + + // Safety: + // The shift retrieves the original value from `T::into_usize`, + // satisfying `T::from_usize`'s preconditions. + unsafe { T::from_usize(tag) } } + /// Sets the tag to a new value. #[inline] - unsafe fn from_ptr(ptr: NonNull) -> Self { - // Safety: `ptr` comes from `into_ptr` which calls `Rc::into_raw` - unsafe { Rc::from_raw(ptr.as_ptr()) } + pub fn set_tag(&mut self, tag: T) { + self.packed = Self::pack(self.pointer_raw(), tag); } -} -unsafe impl Pointer for Arc { - const BITS: u32 = bits_for::(); + const TAG_BIT_SHIFT: u32 = usize::BITS - T::BITS; + const ASSERTION: () = { assert!(T::BITS <= bits_for::

()) }; + /// Pack pointer `ptr` with a `tag`, according to `self.packed` encoding scheme. #[inline] - fn into_ptr(self) -> NonNull { - // Safety: pointers from `Arc::into_raw` are valid & non-null - unsafe { NonNull::new_unchecked(Arc::into_raw(self).cast_mut()) } + fn pack(ptr: NonNull

, tag: T) -> NonNull

{ + // Trigger assert! + let () = Self::ASSERTION; + + let packed_tag = tag.into_usize() << Self::TAG_BIT_SHIFT; + + ptr.map_addr(|addr| { + // Safety: + // - The pointer is `NonNull` => it's address is `NonZero` + // - `P::BITS` least significant bits are always zero (`Pointer` contract) + // - `T::BITS <= P::BITS` (from `Self::ASSERTION`) + // + // Thus `addr >> T::BITS` is guaranteed to be non-zero. + // + // `{non_zero} | packed_tag` can't make the value zero. + + let packed = (addr.get() >> T::BITS) | packed_tag; + unsafe { NonZero::new_unchecked(packed) } + }) } + /// Retrieves the original raw pointer from `self.packed`. #[inline] - unsafe fn from_ptr(ptr: NonNull) -> Self { - // Safety: `ptr` comes from `into_ptr` which calls `Arc::into_raw` - unsafe { Arc::from_raw(ptr.as_ptr()) } + pub(super) fn pointer_raw(&self) -> NonNull

{ + self.packed.map_addr(|addr| unsafe { NonZero::new_unchecked(addr.get() << T::BITS) }) } } -unsafe impl<'a, T: 'a + ?Sized + Aligned> Pointer for &'a T { - const BITS: u32 = bits_for::(); +impl Copy for TaggedRef<'_, P, T> +where + P: Aligned + ?Sized, + T: Tag, +{ +} +impl Clone for TaggedRef<'_, P, T> +where + P: Aligned + ?Sized, + T: Tag, +{ #[inline] - fn into_ptr(self) -> NonNull { - NonNull::from(self) + fn clone(&self) -> Self { + *self } +} + +impl Deref for TaggedRef<'_, P, T> +where + P: Aligned + ?Sized, + T: Tag, +{ + type Target = P; #[inline] - unsafe fn from_ptr(ptr: NonNull) -> Self { - // Safety: - // `ptr` comes from `into_ptr` which gets the pointer from a reference - unsafe { ptr.as_ref() } + fn deref(&self) -> &Self::Target { + self.pointer() } } -unsafe impl<'a, T: 'a + ?Sized + Aligned> Pointer for &'a mut T { - const BITS: u32 = bits_for::(); +impl fmt::Debug for TaggedRef<'_, P, T> +where + P: Aligned + fmt::Debug + ?Sized, + T: Tag + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TaggedRef") + .field("pointer", &self.pointer()) + .field("tag", &self.tag()) + .finish() + } +} +impl PartialEq for TaggedRef<'_, P, T> +where + P: Aligned + ?Sized, + T: Tag, +{ #[inline] - fn into_ptr(self) -> NonNull { - NonNull::from(self) + #[allow(ambiguous_wide_pointer_comparisons)] + fn eq(&self, other: &Self) -> bool { + self.packed == other.packed } +} +impl Eq for TaggedRef<'_, P, T> {} + +impl Hash for TaggedRef<'_, P, T> { #[inline] - unsafe fn from_ptr(mut ptr: NonNull) -> Self { - // Safety: - // `ptr` comes from `into_ptr` which gets the pointer from a reference - unsafe { ptr.as_mut() } + fn hash(&self, state: &mut H) { + self.packed.hash(state); } } -/// A tag type used in [`CopyTaggedPtr`] and [`TaggedPtr`] tests. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[cfg(test)] -enum Tag2 { - B00 = 0b00, - B01 = 0b01, - B10 = 0b10, - B11 = 0b11, +impl<'a, P, T, HCX> HashStable for TaggedRef<'a, P, T> +where + P: HashStable + Aligned + ?Sized, + T: Tag + HashStable, +{ + fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { + self.pointer().hash_stable(hcx, hasher); + self.tag().hash_stable(hcx, hasher); + } } -#[cfg(test)] -unsafe impl Tag for Tag2 { - const BITS: u32 = 2; - - fn into_usize(self) -> usize { - self as _ - } +// Safety: +// `TaggedRef` is semantically just `{ ptr: P, tag: T }`, as such +// it's ok to implement `Sync` as long as `P: Sync, T: Sync` +unsafe impl Sync for TaggedRef<'_, P, T> +where + P: Sync + Aligned + ?Sized, + T: Sync + Tag, +{ +} - unsafe fn from_usize(tag: usize) -> Self { - match tag { - 0b00 => Tag2::B00, - 0b01 => Tag2::B01, - 0b10 => Tag2::B10, - 0b11 => Tag2::B11, - _ => unreachable!(), - } - } +// Safety: +// `TaggedRef` is semantically just `{ ptr: P, tag: T }`, as such +// it's ok to implement `Send` as long as `P: Send, T: Send` +unsafe impl Send for TaggedRef<'_, P, T> +where + P: Sync + Aligned + ?Sized, + T: Send + Tag, +{ } #[cfg(test)] -impl crate::stable_hasher::HashStable for Tag2 { - fn hash_stable(&self, hcx: &mut HCX, hasher: &mut crate::stable_hasher::StableHasher) { - (*self as u8).hash_stable(hcx, hasher); - } -} +mod tests; diff --git a/compiler/rustc_data_structures/src/tagged_ptr/copy.rs b/compiler/rustc_data_structures/src/tagged_ptr/copy.rs deleted file mode 100644 index 25e107b0f413c..0000000000000 --- a/compiler/rustc_data_structures/src/tagged_ptr/copy.rs +++ /dev/null @@ -1,330 +0,0 @@ -use std::fmt; -use std::hash::{Hash, Hasher}; -use std::marker::PhantomData; -use std::mem::ManuallyDrop; -use std::num::NonZero; -use std::ops::{Deref, DerefMut}; -use std::ptr::NonNull; - -use super::{Pointer, Tag}; -use crate::stable_hasher::{HashStable, StableHasher}; - -/// A [`Copy`] tagged pointer. -/// -/// This is essentially `{ pointer: P, tag: T }` packed in a single pointer. -/// -/// You should use this instead of the [`TaggedPtr`] type in all cases where -/// `P` implements [`Copy`]. -/// -/// If `COMPARE_PACKED` is true, then the pointers will be compared and hashed without -/// unpacking. Otherwise we don't implement [`PartialEq`], [`Eq`] and [`Hash`]; -/// if you want that, wrap the [`CopyTaggedPtr`]. -/// -/// [`TaggedPtr`]: crate::tagged_ptr::TaggedPtr -pub struct CopyTaggedPtr -where - P: Pointer, - T: Tag, -{ - /// This is semantically a pair of `pointer: P` and `tag: T` fields, - /// however we pack them in a single pointer, to save space. - /// - /// We pack the tag into the **most**-significant bits of the pointer to - /// ease retrieval of the value. A left shift is a multiplication and - /// those are embeddable in instruction encoding, for example: - /// - /// ```asm - /// // () - /// example::shift_read3: - /// mov eax, dword ptr [8*rdi] - /// ret - /// - /// example::mask_read3: - /// and rdi, -8 - /// mov eax, dword ptr [rdi] - /// ret - /// ``` - /// - /// This is ASM outputted by rustc for reads of values behind tagged - /// pointers for different approaches of tagging: - /// - `shift_read3` uses `<< 3` (the tag is in the most-significant bits) - /// - `mask_read3` uses `& !0b111` (the tag is in the least-significant bits) - /// - /// The shift approach thus produces less instructions and is likely faster - /// (see ). - /// - /// Encoding diagram: - /// ```text - /// [ packed.addr ] - /// [ tag ] [ pointer.addr >> T::BITS ] <-- usize::BITS - T::BITS bits - /// ^ - /// | - /// T::BITS bits - /// ``` - /// - /// The tag can be retrieved by `packed.addr() >> T::BITS` and the pointer - /// can be retrieved by `packed.map_addr(|addr| addr << T::BITS)`. - packed: NonNull, - tag_ghost: PhantomData, -} - -// Note that even though `CopyTaggedPtr` is only really expected to work with -// `P: Copy`, can't add `P: Copy` bound, because `CopyTaggedPtr` is used in the -// `TaggedPtr`'s implementation. -impl CopyTaggedPtr -where - P: Pointer, - T: Tag, -{ - /// Tags `pointer` with `tag`. - /// - /// Note that this leaks `pointer`: it won't be dropped when - /// `CopyTaggedPtr` is dropped. If you have a pointer with a significant - /// drop, use [`TaggedPtr`] instead. - /// - /// [`TaggedPtr`]: crate::tagged_ptr::TaggedPtr - #[inline] - pub fn new(pointer: P, tag: T) -> Self { - Self { packed: Self::pack(P::into_ptr(pointer), tag), tag_ghost: PhantomData } - } - - /// Retrieves the pointer. - #[inline] - pub fn pointer(self) -> P - where - P: Copy, - { - // SAFETY: pointer_raw returns the original pointer - // - // Note that this isn't going to double-drop or anything because we have - // P: Copy - unsafe { P::from_ptr(self.pointer_raw()) } - } - - /// Retrieves the tag. - #[inline] - pub fn tag(&self) -> T { - // Unpack the tag, according to the `self.packed` encoding scheme - let tag = self.packed.addr().get() >> Self::TAG_BIT_SHIFT; - - // Safety: - // The shift retrieves the original value from `T::into_usize`, - // satisfying `T::from_usize`'s preconditions. - unsafe { T::from_usize(tag) } - } - - /// Sets the tag to a new value. - #[inline] - pub fn set_tag(&mut self, tag: T) { - self.packed = Self::pack(self.pointer_raw(), tag); - } - - const TAG_BIT_SHIFT: u32 = usize::BITS - T::BITS; - const ASSERTION: () = { assert!(T::BITS <= P::BITS) }; - - /// Pack pointer `ptr` that comes from [`P::into_ptr`] with a `tag`, - /// according to `self.packed` encoding scheme. - /// - /// [`P::into_ptr`]: Pointer::into_ptr - #[inline] - fn pack(ptr: NonNull, tag: T) -> NonNull { - // Trigger assert! - let () = Self::ASSERTION; - - let packed_tag = tag.into_usize() << Self::TAG_BIT_SHIFT; - - ptr.map_addr(|addr| { - // Safety: - // - The pointer is `NonNull` => it's address is `NonZero` - // - `P::BITS` least significant bits are always zero (`Pointer` contract) - // - `T::BITS <= P::BITS` (from `Self::ASSERTION`) - // - // Thus `addr >> T::BITS` is guaranteed to be non-zero. - // - // `{non_zero} | packed_tag` can't make the value zero. - - let packed = (addr.get() >> T::BITS) | packed_tag; - unsafe { NonZero::new_unchecked(packed) } - }) - } - - /// Retrieves the original raw pointer from `self.packed`. - #[inline] - pub(super) fn pointer_raw(&self) -> NonNull { - self.packed.map_addr(|addr| unsafe { NonZero::new_unchecked(addr.get() << T::BITS) }) - } - - /// This provides a reference to the `P` pointer itself, rather than the - /// `Deref::Target`. It is used for cases where we want to call methods - /// that may be implement differently for the Pointer than the Pointee - /// (e.g., `Rc::clone` vs cloning the inner value). - pub(super) fn with_pointer_ref(&self, f: impl FnOnce(&P) -> R) -> R { - // Safety: - // - `self.raw.pointer_raw()` is originally returned from `P::into_ptr` - // and as such is valid for `P::from_ptr`. - // - This also allows us to not care whatever `f` panics or not. - // - Even though we create a copy of the pointer, we store it inside - // `ManuallyDrop` and only access it by-ref, so we don't double-drop. - // - // Semantically this is just `f(&self.pointer)` (where `self.pointer` - // is non-packed original pointer). - // - // Note that even though `CopyTaggedPtr` is only really expected to - // work with `P: Copy`, we have to assume `P: ?Copy`, because - // `CopyTaggedPtr` is used in the `TaggedPtr`'s implementation. - let ptr = unsafe { ManuallyDrop::new(P::from_ptr(self.pointer_raw())) }; - f(&ptr) - } -} - -impl Copy for CopyTaggedPtr -where - P: Pointer + Copy, - T: Tag, -{ -} - -impl Clone for CopyTaggedPtr -where - P: Pointer + Copy, - T: Tag, -{ - #[inline] - fn clone(&self) -> Self { - *self - } -} - -impl Deref for CopyTaggedPtr -where - P: Pointer, - T: Tag, -{ - type Target = P::Target; - - #[inline] - fn deref(&self) -> &Self::Target { - // Safety: - // `pointer_raw` returns the original pointer from `P::into_ptr` which, - // by the `Pointer`'s contract, must be valid. - unsafe { self.pointer_raw().as_ref() } - } -} - -impl DerefMut for CopyTaggedPtr -where - P: Pointer + DerefMut, - T: Tag, -{ - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - // Safety: - // `pointer_raw` returns the original pointer from `P::into_ptr` which, - // by the `Pointer`'s contract, must be valid for writes if - // `P: DerefMut`. - unsafe { self.pointer_raw().as_mut() } - } -} - -impl fmt::Debug for CopyTaggedPtr -where - P: Pointer + fmt::Debug, - T: Tag + fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.with_pointer_ref(|ptr| { - f.debug_struct("CopyTaggedPtr").field("pointer", ptr).field("tag", &self.tag()).finish() - }) - } -} - -impl PartialEq for CopyTaggedPtr -where - P: Pointer, - T: Tag, -{ - #[inline] - #[allow(ambiguous_wide_pointer_comparisons)] - fn eq(&self, other: &Self) -> bool { - self.packed == other.packed - } -} - -impl Eq for CopyTaggedPtr -where - P: Pointer, - T: Tag, -{ -} - -impl Hash for CopyTaggedPtr -where - P: Pointer, - T: Tag, -{ - #[inline] - fn hash(&self, state: &mut H) { - self.packed.hash(state); - } -} - -impl HashStable for CopyTaggedPtr -where - P: Pointer + HashStable, - T: Tag + HashStable, -{ - fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { - self.with_pointer_ref(|ptr| ptr.hash_stable(hcx, hasher)); - self.tag().hash_stable(hcx, hasher); - } -} - -// Safety: -// `CopyTaggedPtr` is semantically just `{ ptr: P, tag: T }`, as such -// it's ok to implement `Sync` as long as `P: Sync, T: Sync` -unsafe impl Sync for CopyTaggedPtr -where - P: Sync + Pointer, - T: Sync + Tag, -{ -} - -// Safety: -// `CopyTaggedPtr` is semantically just `{ ptr: P, tag: T }`, as such -// it's ok to implement `Send` as long as `P: Send, T: Send` -unsafe impl Send for CopyTaggedPtr -where - P: Send + Pointer, - T: Send + Tag, -{ -} - -/// Test that `new` does not compile if there is not enough alignment for the -/// tag in the pointer. -/// -/// ```compile_fail,E0080 -/// use rustc_data_structures::tagged_ptr::{CopyTaggedPtr, Tag}; -/// -/// #[derive(Copy, Clone, Debug, PartialEq, Eq)] -/// enum Tag2 { B00 = 0b00, B01 = 0b01, B10 = 0b10, B11 = 0b11 }; -/// -/// unsafe impl Tag for Tag2 { -/// const BITS: u32 = 2; -/// -/// fn into_usize(self) -> usize { todo!() } -/// unsafe fn from_usize(tag: usize) -> Self { todo!() } -/// } -/// -/// let value = 12u16; -/// let reference = &value; -/// let tag = Tag2::B01; -/// -/// let _ptr = CopyTaggedPtr::<_, _, true>::new(reference, tag); -/// ``` -// For some reason miri does not get the compile error -// probably it `check`s instead of `build`ing? -#[cfg(not(miri))] -const _: () = (); - -#[cfg(test)] -mod tests; diff --git a/compiler/rustc_data_structures/src/tagged_ptr/copy/tests.rs b/compiler/rustc_data_structures/src/tagged_ptr/copy/tests.rs deleted file mode 100644 index 160af8a65d9c2..0000000000000 --- a/compiler/rustc_data_structures/src/tagged_ptr/copy/tests.rs +++ /dev/null @@ -1,50 +0,0 @@ -use std::ptr; - -use crate::hashes::Hash128; -use crate::stable_hasher::{HashStable, StableHasher}; -use crate::tagged_ptr::{CopyTaggedPtr, Pointer, Tag, Tag2}; - -#[test] -fn smoke() { - let value = 12u32; - let reference = &value; - let tag = Tag2::B01; - - let ptr = tag_ptr(reference, tag); - - assert_eq!(ptr.tag(), tag); - assert_eq!(*ptr, 12); - assert!(ptr::eq(ptr.pointer(), reference)); - - let copy = ptr; - - let mut ptr = ptr; - ptr.set_tag(Tag2::B00); - assert_eq!(ptr.tag(), Tag2::B00); - - assert_eq!(copy.tag(), tag); - assert_eq!(*copy, 12); - assert!(ptr::eq(copy.pointer(), reference)); -} - -#[test] -fn stable_hash_hashes_as_tuple() { - let hash_packed = { - let mut hasher = StableHasher::new(); - tag_ptr(&12, Tag2::B11).hash_stable(&mut (), &mut hasher); - hasher.finish::() - }; - - let hash_tupled = { - let mut hasher = StableHasher::new(); - (&12, Tag2::B11).hash_stable(&mut (), &mut hasher); - hasher.finish::() - }; - - assert_eq!(hash_packed, hash_tupled); -} - -/// Helper to create tagged pointers without specifying `COMPARE_PACKED` if it does not matter. -fn tag_ptr(ptr: P, tag: T) -> CopyTaggedPtr { - CopyTaggedPtr::new(ptr, tag) -} diff --git a/compiler/rustc_data_structures/src/tagged_ptr/drop.rs b/compiler/rustc_data_structures/src/tagged_ptr/drop.rs deleted file mode 100644 index 319a8cdd3990a..0000000000000 --- a/compiler/rustc_data_structures/src/tagged_ptr/drop.rs +++ /dev/null @@ -1,178 +0,0 @@ -use std::fmt; -use std::hash::{Hash, Hasher}; -use std::ops::{Deref, DerefMut}; - -use super::{CopyTaggedPtr, Pointer, Tag}; -use crate::stable_hasher::{HashStable, StableHasher}; - -/// A tagged pointer that supports pointers that implement [`Drop`]. -/// -/// This is essentially `{ pointer: P, tag: T }` packed in a single pointer. -/// -/// You should use [`CopyTaggedPtr`] instead of the this type in all cases -/// where `P` implements [`Copy`]. -/// -/// If `COMPARE_PACKED` is true, then the pointers will be compared and hashed without -/// unpacking. Otherwise we don't implement [`PartialEq`], [`Eq`] and [`Hash`]; -/// if you want that, wrap the [`TaggedPtr`]. -pub struct TaggedPtr -where - P: Pointer, - T: Tag, -{ - raw: CopyTaggedPtr, -} - -impl TaggedPtr -where - P: Pointer, - T: Tag, -{ - /// Tags `pointer` with `tag`. - #[inline] - pub fn new(pointer: P, tag: T) -> Self { - TaggedPtr { raw: CopyTaggedPtr::new(pointer, tag) } - } - - /// Retrieves the tag. - #[inline] - pub fn tag(&self) -> T { - self.raw.tag() - } - - /// Sets the tag to a new value. - #[inline] - pub fn set_tag(&mut self, tag: T) { - self.raw.set_tag(tag) - } -} - -impl Clone for TaggedPtr -where - P: Pointer + Clone, - T: Tag, -{ - fn clone(&self) -> Self { - let ptr = self.raw.with_pointer_ref(P::clone); - - Self::new(ptr, self.tag()) - } -} - -impl Deref for TaggedPtr -where - P: Pointer, - T: Tag, -{ - type Target = P::Target; - - #[inline] - fn deref(&self) -> &Self::Target { - self.raw.deref() - } -} - -impl DerefMut for TaggedPtr -where - P: Pointer + DerefMut, - T: Tag, -{ - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - self.raw.deref_mut() - } -} - -impl Drop for TaggedPtr -where - P: Pointer, - T: Tag, -{ - fn drop(&mut self) { - // No need to drop the tag, as it's Copy - unsafe { - drop(P::from_ptr(self.raw.pointer_raw())); - } - } -} - -impl fmt::Debug for TaggedPtr -where - P: Pointer + fmt::Debug, - T: Tag + fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.raw.with_pointer_ref(|ptr| { - f.debug_struct("TaggedPtr").field("pointer", ptr).field("tag", &self.tag()).finish() - }) - } -} - -impl PartialEq for TaggedPtr -where - P: Pointer, - T: Tag, -{ - #[inline] - fn eq(&self, other: &Self) -> bool { - self.raw.eq(&other.raw) - } -} - -impl Eq for TaggedPtr -where - P: Pointer, - T: Tag, -{ -} - -impl Hash for TaggedPtr -where - P: Pointer, - T: Tag, -{ - #[inline] - fn hash(&self, state: &mut H) { - self.raw.hash(state); - } -} - -impl HashStable for TaggedPtr -where - P: Pointer + HashStable, - T: Tag + HashStable, -{ - fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { - self.raw.hash_stable(hcx, hasher); - } -} - -/// Test that `new` does not compile if there is not enough alignment for the -/// tag in the pointer. -/// -/// ```compile_fail,E0080 -/// use rustc_data_structures::tagged_ptr::{TaggedPtr, Tag}; -/// -/// #[derive(Copy, Clone, Debug, PartialEq, Eq)] -/// enum Tag2 { B00 = 0b00, B01 = 0b01, B10 = 0b10, B11 = 0b11 }; -/// -/// unsafe impl Tag for Tag2 { -/// const BITS: u32 = 2; -/// -/// fn into_usize(self) -> usize { todo!() } -/// unsafe fn from_usize(tag: usize) -> Self { todo!() } -/// } -/// -/// let value = 12u16; -/// let reference = &value; -/// let tag = Tag2::B01; -/// -/// let _ptr = TaggedPtr::<_, _, true>::new(reference, tag); -/// ``` -// For some reason miri does not get the compile error -// probably it `check`s instead of `build`ing? -#[cfg(not(miri))] -const _: () = (); - -#[cfg(test)] -mod tests; diff --git a/compiler/rustc_data_structures/src/tagged_ptr/drop/tests.rs b/compiler/rustc_data_structures/src/tagged_ptr/drop/tests.rs deleted file mode 100644 index 4d342c72cc52a..0000000000000 --- a/compiler/rustc_data_structures/src/tagged_ptr/drop/tests.rs +++ /dev/null @@ -1,72 +0,0 @@ -use std::ptr; -use std::sync::Arc; - -use crate::tagged_ptr::{Pointer, Tag, Tag2, TaggedPtr}; - -#[test] -fn smoke() { - let value = 12u32; - let reference = &value; - let tag = Tag2::B01; - - let ptr = tag_ptr(reference, tag); - - assert_eq!(ptr.tag(), tag); - assert_eq!(*ptr, 12); - - let clone = ptr.clone(); - assert_eq!(clone.tag(), tag); - assert_eq!(*clone, 12); - - let mut ptr = ptr; - ptr.set_tag(Tag2::B00); - assert_eq!(ptr.tag(), Tag2::B00); - - assert_eq!(clone.tag(), tag); - assert_eq!(*clone, 12); - assert!(ptr::eq(&*ptr, &*clone)) -} - -#[test] -fn boxed() { - let value = 12u32; - let boxed = Box::new(value); - let tag = Tag2::B01; - - let ptr = tag_ptr(boxed, tag); - - assert_eq!(ptr.tag(), tag); - assert_eq!(*ptr, 12); - - let clone = ptr.clone(); - assert_eq!(clone.tag(), tag); - assert_eq!(*clone, 12); - - let mut ptr = ptr; - ptr.set_tag(Tag2::B00); - assert_eq!(ptr.tag(), Tag2::B00); - - assert_eq!(clone.tag(), tag); - assert_eq!(*clone, 12); - assert!(!ptr::eq(&*ptr, &*clone)) -} - -#[test] -fn arclones() { - let value = 12u32; - let arc = Arc::new(value); - let tag = Tag2::B01; - - let ptr = tag_ptr(arc, tag); - - assert_eq!(ptr.tag(), tag); - assert_eq!(*ptr, 12); - - let clone = ptr.clone(); - assert!(ptr::eq(&*ptr, &*clone)) -} - -/// Helper to create tagged pointers without specifying `COMPARE_PACKED` if it does not matter. -fn tag_ptr(ptr: P, tag: T) -> TaggedPtr { - TaggedPtr::new(ptr, tag) -} diff --git a/compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs b/compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs deleted file mode 100644 index f17a0bf26d747..0000000000000 --- a/compiler/rustc_data_structures/src/tagged_ptr/impl_tag.rs +++ /dev/null @@ -1,144 +0,0 @@ -/// Implements [`Tag`] for a given type. -/// -/// You can use `impl_tag` on structs and enums. -/// You need to specify the type and all its possible values, -/// which can only be paths with optional fields. -/// -/// [`Tag`]: crate::tagged_ptr::Tag -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// #![feature(macro_metavar_expr)] -/// use rustc_data_structures::{impl_tag, tagged_ptr::Tag}; -/// -/// #[derive(Copy, Clone, PartialEq, Debug)] -/// enum SomeTag { -/// A, -/// B, -/// X { v: bool }, -/// Y(bool, bool), -/// } -/// -/// impl_tag! { -/// // The type for which the `Tag` will be implemented -/// impl Tag for SomeTag; -/// // You need to specify all possible tag values: -/// SomeTag::A, // 0 -/// SomeTag::B, // 1 -/// // For variants with fields, you need to specify the fields: -/// SomeTag::X { v: true }, // 2 -/// SomeTag::X { v: false }, // 3 -/// // For tuple variants use named syntax: -/// SomeTag::Y { 0: true, 1: true }, // 4 -/// SomeTag::Y { 0: false, 1: true }, // 5 -/// SomeTag::Y { 0: true, 1: false }, // 6 -/// SomeTag::Y { 0: false, 1: false }, // 7 -/// } -/// -/// // Tag values are assigned in order: -/// assert_eq!(SomeTag::A.into_usize(), 0); -/// assert_eq!(SomeTag::X { v: false }.into_usize(), 3); -/// assert_eq!(SomeTag::Y(false, true).into_usize(), 5); -/// -/// assert_eq!(unsafe { SomeTag::from_usize(1) }, SomeTag::B); -/// assert_eq!(unsafe { SomeTag::from_usize(2) }, SomeTag::X { v: true }); -/// assert_eq!(unsafe { SomeTag::from_usize(7) }, SomeTag::Y(false, false)); -/// ``` -/// -/// Structs are supported: -/// -/// ``` -/// #![feature(macro_metavar_expr)] -/// # use rustc_data_structures::impl_tag; -/// #[derive(Copy, Clone)] -/// struct Flags { a: bool, b: bool } -/// -/// impl_tag! { -/// impl Tag for Flags; -/// Flags { a: true, b: true }, -/// Flags { a: false, b: true }, -/// Flags { a: true, b: false }, -/// Flags { a: false, b: false }, -/// } -/// ``` -/// -/// Not specifying all values results in a compile error: -/// -/// ```compile_fail,E0004 -/// #![feature(macro_metavar_expr)] -/// # use rustc_data_structures::impl_tag; -/// #[derive(Copy, Clone)] -/// enum E { -/// A, -/// B, -/// } -/// -/// impl_tag! { -/// impl Tag for E; -/// E::A, -/// } -/// ``` -#[macro_export] -macro_rules! impl_tag { - ( - impl Tag for $Self:ty; - $( - $($path:ident)::* $( { $( $fields:tt )* })?, - )* - ) => { - // Safety: - // `bits_for_tags` is called on the same `${index()}`-es as - // `into_usize` returns, thus `BITS` constant is correct. - unsafe impl $crate::tagged_ptr::Tag for $Self { - const BITS: u32 = $crate::tagged_ptr::bits_for_tags(&[ - $( - ${index()}, - $( ${ignore($path)} )* - )* - ]); - - #[inline] - fn into_usize(self) -> usize { - // This forbids use of repeating patterns (`Enum::V`&`Enum::V`, etc) - // (or at least it should, see ) - #[forbid(unreachable_patterns)] - match self { - // `match` is doing heavy lifting here, by requiring exhaustiveness - $( - $($path)::* $( { $( $fields )* } )? => ${index()}, - )* - } - } - - #[inline] - unsafe fn from_usize(tag: usize) -> Self { - match tag { - $( - ${index()} => $($path)::* $( { $( $fields )* } )?, - )* - - // Safety: - // `into_usize` only returns `${index()}` of the same - // repetition as we are filtering above, thus if this is - // reached, the safety contract of this function was - // already breached. - _ => unsafe { - debug_assert!( - false, - "invalid tag: {tag}\ - (this is a bug in the caller of `from_usize`)" - ); - std::hint::unreachable_unchecked() - }, - } - } - - } - }; -} - -#[cfg(test)] -mod tests; diff --git a/compiler/rustc_data_structures/src/tagged_ptr/impl_tag/tests.rs b/compiler/rustc_data_structures/src/tagged_ptr/impl_tag/tests.rs deleted file mode 100644 index 62c926153e1e9..0000000000000 --- a/compiler/rustc_data_structures/src/tagged_ptr/impl_tag/tests.rs +++ /dev/null @@ -1,34 +0,0 @@ -#[test] -fn bits_constant() { - use crate::tagged_ptr::Tag; - - #[derive(Copy, Clone)] - struct Unit; - impl_tag! { impl Tag for Unit; Unit, } - assert_eq!(Unit::BITS, 0); - - #[derive(Copy, Clone)] - enum Enum3 { - A, - B, - C, - } - impl_tag! { impl Tag for Enum3; Enum3::A, Enum3::B, Enum3::C, } - assert_eq!(Enum3::BITS, 2); - - #[derive(Copy, Clone)] - struct Eight(bool, bool, bool); - impl_tag! { - impl Tag for Eight; - Eight { 0: true, 1: true, 2: true }, - Eight { 0: true, 1: true, 2: false }, - Eight { 0: true, 1: false, 2: true }, - Eight { 0: true, 1: false, 2: false }, - Eight { 0: false, 1: true, 2: true }, - Eight { 0: false, 1: true, 2: false }, - Eight { 0: false, 1: false, 2: true }, - Eight { 0: false, 1: false, 2: false }, - } - - assert_eq!(Eight::BITS, 3); -} diff --git a/compiler/rustc_data_structures/src/tagged_ptr/tests.rs b/compiler/rustc_data_structures/src/tagged_ptr/tests.rs new file mode 100644 index 0000000000000..b1bdee18d6d43 --- /dev/null +++ b/compiler/rustc_data_structures/src/tagged_ptr/tests.rs @@ -0,0 +1,105 @@ +use std::ptr; + +use super::*; +use crate::hashes::Hash128; +use crate::stable_hasher::{HashStable, StableHasher}; + +/// A tag type used in [`TaggedRef`] tests. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Tag2 { + B00 = 0b00, + B01 = 0b01, + B10 = 0b10, + B11 = 0b11, +} + +unsafe impl Tag for Tag2 { + const BITS: u32 = 2; + + fn into_usize(self) -> usize { + self as _ + } + + unsafe fn from_usize(tag: usize) -> Self { + match tag { + 0b00 => Tag2::B00, + 0b01 => Tag2::B01, + 0b10 => Tag2::B10, + 0b11 => Tag2::B11, + _ => unreachable!(), + } + } +} + +impl crate::stable_hasher::HashStable for Tag2 { + fn hash_stable(&self, hcx: &mut HCX, hasher: &mut crate::stable_hasher::StableHasher) { + (*self as u8).hash_stable(hcx, hasher); + } +} + +#[test] +fn smoke() { + let value = 12u32; + let reference = &value; + let tag = Tag2::B01; + + let ptr = TaggedRef::new(reference, tag); + + assert_eq!(ptr.tag(), tag); + assert_eq!(*ptr, 12); + assert!(ptr::eq(ptr.pointer(), reference)); + + let copy = ptr; + + let mut ptr = ptr; + ptr.set_tag(Tag2::B00); + assert_eq!(ptr.tag(), Tag2::B00); + + assert_eq!(copy.tag(), tag); + assert_eq!(*copy, 12); + assert!(ptr::eq(copy.pointer(), reference)); +} + +#[test] +fn stable_hash_hashes_as_tuple() { + let hash_packed = { + let mut hasher = StableHasher::new(); + TaggedRef::new(&12, Tag2::B11).hash_stable(&mut (), &mut hasher); + hasher.finish::() + }; + + let hash_tupled = { + let mut hasher = StableHasher::new(); + (&12, Tag2::B11).hash_stable(&mut (), &mut hasher); + hasher.finish::() + }; + + assert_eq!(hash_packed, hash_tupled); +} + +/// Test that `new` does not compile if there is not enough alignment for the +/// tag in the pointer. +/// +/// ```compile_fail,E0080 +/// use rustc_data_structures::tagged_ptr::{TaggedRef, Tag}; +/// +/// #[derive(Copy, Clone, Debug, PartialEq, Eq)] +/// enum Tag2 { B00 = 0b00, B01 = 0b01, B10 = 0b10, B11 = 0b11 }; +/// +/// unsafe impl Tag for Tag2 { +/// const BITS: u32 = 2; +/// +/// fn into_usize(self) -> usize { todo!() } +/// unsafe fn from_usize(tag: usize) -> Self { todo!() } +/// } +/// +/// let value = 12u16; +/// let reference = &value; +/// let tag = Tag2::B01; +/// +/// let _ptr = TaggedRef::<_, _, true>::new(reference, tag); +/// ``` +// For some reason miri does not get the compile error +// probably it `check`s instead of `build`ing? +#[cfg(not(miri))] +const _: () = (); diff --git a/compiler/rustc_data_structures/src/thousands/mod.rs b/compiler/rustc_data_structures/src/thousands/mod.rs new file mode 100644 index 0000000000000..e7ab7ec2932df --- /dev/null +++ b/compiler/rustc_data_structures/src/thousands/mod.rs @@ -0,0 +1,16 @@ +//! This is an extremely bare-bones alternative to the `thousands` crate on +//! crates.io, for printing large numbers in a readable fashion. + +#[cfg(test)] +mod tests; + +// Converts the number to a string, with underscores as the thousands separator. +pub fn format_with_underscores(n: usize) -> String { + let mut s = n.to_string(); + let mut i = s.len(); + while i > 3 { + i -= 3; + s.insert(i, '_'); + } + s +} diff --git a/compiler/rustc_data_structures/src/thousands/tests.rs b/compiler/rustc_data_structures/src/thousands/tests.rs new file mode 100644 index 0000000000000..906605d9a935d --- /dev/null +++ b/compiler/rustc_data_structures/src/thousands/tests.rs @@ -0,0 +1,14 @@ +use super::*; + +#[test] +fn test_format_with_underscores() { + assert_eq!("0", format_with_underscores(0)); + assert_eq!("1", format_with_underscores(1)); + assert_eq!("99", format_with_underscores(99)); + assert_eq!("345", format_with_underscores(345)); + assert_eq!("1_000", format_with_underscores(1_000)); + assert_eq!("12_001", format_with_underscores(12_001)); + assert_eq!("999_999", format_with_underscores(999_999)); + assert_eq!("1_000_000", format_with_underscores(1_000_000)); + assert_eq!("12_345_678", format_with_underscores(12_345_678)); +} diff --git a/compiler/rustc_data_structures/src/vec_cache.rs b/compiler/rustc_data_structures/src/vec_cache.rs index eb251b587c804..2ff60ab7f36f9 100644 --- a/compiler/rustc_data_structures/src/vec_cache.rs +++ b/compiler/rustc_data_structures/src/vec_cache.rs @@ -206,6 +206,19 @@ impl SlotIndex { } } +/// In-memory cache for queries whose keys are densely-numbered IDs +/// (e.g `CrateNum`, `LocalDefId`), and can therefore be used as indices +/// into a dense vector of cached values. +/// +/// (As of [#124780] the underlying storage is not an actual `Vec`, but rather +/// a series of increasingly-large buckets, for improved performance when the +/// parallel frontend is using multiple threads.) +/// +/// Each entry in the cache stores the query's return value (`V`), and also +/// an associated index (`I`), which in practice is a `DepNodeIndex` used for +/// query dependency tracking. +/// +/// [#124780]: https://github.com/rust-lang/rust/pull/124780 pub struct VecCache { // Entries per bucket: // Bucket 0: 4096 2^12 diff --git a/compiler/rustc_data_structures/src/vec_cache/tests.rs b/compiler/rustc_data_structures/src/vec_cache/tests.rs index a05f274136209..3b65c14162e8e 100644 --- a/compiler/rustc_data_structures/src/vec_cache/tests.rs +++ b/compiler/rustc_data_structures/src/vec_cache/tests.rs @@ -58,11 +58,14 @@ fn concurrent_stress_check() { #[test] fn slot_entries_table() { - assert_eq!(ENTRIES_BY_BUCKET, [ - 4096, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, - 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, - 2147483648 - ]); + assert_eq!( + ENTRIES_BY_BUCKET, + [ + 4096, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, + 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, + 1073741824, 2147483648 + ] + ); } #[test] diff --git a/compiler/rustc_data_structures/src/work_queue.rs b/compiler/rustc_data_structures/src/work_queue.rs index ca052e2eac6db..815756edfeba5 100644 --- a/compiler/rustc_data_structures/src/work_queue.rs +++ b/compiler/rustc_data_structures/src/work_queue.rs @@ -1,7 +1,7 @@ use std::collections::VecDeque; use rustc_index::Idx; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; /// A work queue is a handy data structure for tracking work left to /// do. (For example, basic blocks left to process.) It is basically a @@ -11,14 +11,14 @@ use rustc_index::bit_set::BitSet; /// and also use a bit set to track occupancy. pub struct WorkQueue { deque: VecDeque, - set: BitSet, + set: DenseBitSet, } impl WorkQueue { /// Creates a new work queue that starts empty, where elements range from (0..len). #[inline] pub fn with_none(len: usize) -> Self { - WorkQueue { deque: VecDeque::with_capacity(len), set: BitSet::new_empty(len) } + WorkQueue { deque: VecDeque::with_capacity(len), set: DenseBitSet::new_empty(len) } } /// Attempt to enqueue `element` in the work queue. Returns false if it was already present. diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index 2f0fe64b09608..0b45e5786e83d 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start +rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_lowering = { path = "../rustc_ast_lowering" } rustc_ast_passes = { path = "../rustc_ast_passes" } @@ -59,7 +60,7 @@ libc = "0.2" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] -version = "0.57.0" +version = "0.59.0" features = [ "Win32_System_Diagnostics_Debug", ] diff --git a/compiler/rustc_driver_impl/src/args.rs b/compiler/rustc_driver_impl/src/args.rs index 2fc767b375086..b0970144c421e 100644 --- a/compiler/rustc_driver_impl/src/args.rs +++ b/compiler/rustc_driver_impl/src/args.rs @@ -1,7 +1,6 @@ use std::{env, error, fmt, fs, io}; use rustc_session::EarlyDiagCtxt; -use rustc_span::ErrorGuaranteed; /// Expands argfiles in command line arguments. #[derive(Default)] @@ -118,22 +117,22 @@ pub fn arg_expand_all(early_dcx: &EarlyDiagCtxt, at_args: &[String]) -> Vec Result, ErrorGuaranteed> { - let mut res = Ok(Vec::new()); +pub fn raw_args(early_dcx: &EarlyDiagCtxt) -> Vec { + let mut args = Vec::new(); + let mut guar = Ok(()); for (i, arg) in env::args_os().enumerate() { match arg.into_string() { - Ok(arg) => { - if let Ok(args) = &mut res { - args.push(arg); - } - } + Ok(arg) => args.push(arg), Err(arg) => { - res = + guar = Err(early_dcx.early_err(format!("argument {i} is not valid Unicode: {arg:?}"))) } } } - res + if let Err(guar) = guar { + guar.raise_fatal(); + } + args } #[derive(Debug)] diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 90f382e72268b..2bcc33241dfa1 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -28,8 +28,8 @@ use std::io::{self, IsTerminal, Read, Write}; use std::panic::{self, PanicHookInfo, catch_unwind}; use std::path::{Path, PathBuf}; use std::process::{self, Command, Stdio}; +use std::sync::OnceLock; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Arc, OnceLock}; use std::time::{Instant, SystemTime}; use std::{env, str}; @@ -52,15 +52,14 @@ use rustc_metadata::locator; use rustc_middle::ty::TyCtxt; use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_session::config::{ - CG_OPTIONS, ErrorOutputType, Input, OutFileName, OutputType, UnstableOptions, Z_OPTIONS, - nightly_options, + CG_OPTIONS, ErrorOutputType, Input, OptionDesc, OutFileName, OutputType, UnstableOptions, + Z_OPTIONS, nightly_options, parse_target_triple, }; use rustc_session::getopts::{self, Matches}; use rustc_session::lint::{Lint, LintId}; use rustc_session::output::collect_crate_types; use rustc_session::{EarlyDiagCtxt, Session, config, filesearch}; use rustc_span::FileName; -use rustc_span::source_map::FileLoader; use rustc_target::json::ToJson; use rustc_target::spec::{Target, TargetTuple}; use time::OffsetDateTime; @@ -90,10 +89,10 @@ pub mod pretty; #[macro_use] mod print; mod session_diagnostics; -#[cfg(all(unix, any(target_env = "gnu", target_os = "macos")))] +#[cfg(all(not(miri), unix, any(target_env = "gnu", target_os = "macos")))] mod signal_handler; -#[cfg(not(all(unix, any(target_env = "gnu", target_os = "macos"))))] +#[cfg(not(all(not(miri), unix, any(target_env = "gnu", target_os = "macos"))))] mod signal_handler { /// On platforms which don't support our signal handler's requirements, /// simply use the default signal handler provided by std. @@ -161,7 +160,7 @@ pub trait Callbacks { fn after_crate_root_parsing( &mut self, _compiler: &interface::Compiler, - _queries: &ast::Crate, + _krate: &mut ast::Crate, ) -> Compilation { Compilation::Continue } @@ -208,84 +207,7 @@ pub fn diagnostics_registry() -> Registry { } /// This is the primary entry point for rustc. -pub struct RunCompiler<'a> { - at_args: &'a [String], - callbacks: &'a mut (dyn Callbacks + Send), - file_loader: Option>, - make_codegen_backend: - Option Box + Send>>, - using_internal_features: Arc, -} - -impl<'a> RunCompiler<'a> { - pub fn new(at_args: &'a [String], callbacks: &'a mut (dyn Callbacks + Send)) -> Self { - Self { - at_args, - callbacks, - file_loader: None, - make_codegen_backend: None, - using_internal_features: Arc::default(), - } - } - - /// Set a custom codegen backend. - /// - /// Has no uses within this repository, but is used by bjorn3 for "the - /// hotswapping branch of cg_clif" for "setting the codegen backend from a - /// custom driver where the custom codegen backend has arbitrary data." - /// (See #102759.) - pub fn set_make_codegen_backend( - &mut self, - make_codegen_backend: Option< - Box Box + Send>, - >, - ) -> &mut Self { - self.make_codegen_backend = make_codegen_backend; - self - } - - /// Load files from sources other than the file system. - /// - /// Has no uses within this repository, but may be used in the future by - /// bjorn3 for "hooking rust-analyzer's VFS into rustc at some point for - /// running rustc without having to save". (See #102759.) - pub fn set_file_loader( - &mut self, - file_loader: Option>, - ) -> &mut Self { - self.file_loader = file_loader; - self - } - - /// Set the session-global flag that checks whether internal features have been used, - /// suppressing the message about submitting an issue in ICEs when enabled. - #[must_use] - pub fn set_using_internal_features(mut self, using_internal_features: Arc) -> Self { - self.using_internal_features = using_internal_features; - self - } - - /// Parse args and run the compiler. - pub fn run(self) { - run_compiler( - self.at_args, - self.callbacks, - self.file_loader, - self.make_codegen_backend, - self.using_internal_features, - ); - } -} - -fn run_compiler( - at_args: &[String], - callbacks: &mut (dyn Callbacks + Send), - file_loader: Option>, - make_codegen_backend: Option< - Box Box + Send>, - >, - using_internal_features: Arc, -) { +pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) { let mut default_early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default()); // Throw away the first argument, the name of the binary. @@ -322,16 +244,16 @@ fn run_compiler( output_file: ofile, output_dir: odir, ice_file, - file_loader, + file_loader: None, locale_resources: DEFAULT_LOCALE_RESOURCES.to_vec(), lint_caps: Default::default(), psess_created: None, hash_untracked_state: None, register_lints: None, override_queries: None, - make_codegen_backend, + make_codegen_backend: None, registry: diagnostics_registry(), - using_internal_features, + using_internal_features: &USING_INTERNAL_FEATURES, expanded_args: args, }; @@ -389,13 +311,13 @@ fn run_compiler( // Parse the crate root source code (doesn't parse submodules yet) // Everything else is parsed during macro expansion. - let krate = passes::parse(sess); + let mut krate = passes::parse(sess); // If pretty printing is requested: Figure out the representation, print it and exit if let Some(pp_mode) = sess.opts.pretty { if pp_mode.needs_ast_map() { create_and_enter_global_ctxt(compiler, krate, |tcx| { - tcx.ensure().early_lint_checks(()); + tcx.ensure_ok().early_lint_checks(()); pretty::print(sess, pp_mode, pretty::PrintExtra::NeedsAstMap { tcx }); passes::write_dep_info(tcx); }); @@ -406,7 +328,7 @@ fn run_compiler( return early_exit(); } - if callbacks.after_crate_root_parsing(compiler, &krate) == Compilation::Stop { + if callbacks.after_crate_root_parsing(compiler, &mut krate) == Compilation::Stop { return early_exit(); } @@ -443,7 +365,7 @@ fn run_compiler( return early_exit(); } - tcx.ensure().analysis(()); + tcx.ensure_ok().analysis(()); if callbacks.after_analysis(compiler, tcx) == Compilation::Stop { return early_exit(); @@ -825,8 +747,7 @@ fn print_crate_info( } } CallingConventions => { - let mut calling_conventions = rustc_target::spec::abi::all_names(); - calling_conventions.sort_unstable(); + let calling_conventions = rustc_abi::all_names(); println_info!("{}", calling_conventions.join("\n")); } RelocationModels @@ -916,13 +837,7 @@ pub fn version_at_macro_invocation( safe_println!("host: {}", config::host_tuple()); safe_println!("release: {release}"); - let debug_flags = matches.opt_strs("Z"); - let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend=")); - let opts = config::Options::default(); - let sysroot = filesearch::materialize_sysroot(opts.maybe_sysroot.clone()); - let target = config::build_target_config(early_dcx, &opts, &sysroot); - - get_codegen_backend(early_dcx, &sysroot, backend_name, &target).print_version(); + get_backend_from_raw_matches(early_dcx, matches).print_version(); } } @@ -1108,7 +1023,7 @@ pub fn describe_flag_categories(early_dcx: &EarlyDiagCtxt, matches: &Matches) -> let wall = matches.opt_strs("W"); if wall.iter().any(|x| *x == "all") { print_wall_help(); - rustc_errors::FatalError.raise(); + return true; } // Don't handle -W help here, because we might first load additional lints. @@ -1124,28 +1039,33 @@ pub fn describe_flag_categories(early_dcx: &EarlyDiagCtxt, matches: &Matches) -> return true; } - if cg_flags.iter().any(|x| *x == "no-stack-check") { - early_dcx.early_warn("the `-Cno-stack-check` flag is deprecated and does nothing"); - } - - if cg_flags.iter().any(|x| x.starts_with("inline-threshold")) { - early_dcx.early_warn("the `-Cinline-threshold` flag is deprecated and does nothing (consider using `-Cllvm-args=--inline-threshold=...`)"); - } - if cg_flags.iter().any(|x| *x == "passes=list") { - let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend=")); - - let opts = config::Options::default(); - let sysroot = filesearch::materialize_sysroot(opts.maybe_sysroot.clone()); - let target = config::build_target_config(early_dcx, &opts, &sysroot); - - get_codegen_backend(early_dcx, &sysroot, backend_name, &target).print_passes(); + get_backend_from_raw_matches(early_dcx, matches).print_passes(); return true; } false } +/// Get the codegen backend based on the raw [`Matches`]. +/// +/// `rustc -vV` and `rustc -Cpasses=list` need to get the codegen backend before we have parsed all +/// arguments and created a [`Session`]. This function reads `-Zcodegen-backend`, `--target` and +/// `--sysroot` without validating any other arguments and loads the codegen backend based on these +/// arguments. +fn get_backend_from_raw_matches( + early_dcx: &EarlyDiagCtxt, + matches: &Matches, +) -> Box { + let debug_flags = matches.opt_strs("Z"); + let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend=")); + let target = parse_target_triple(early_dcx, matches); + let sysroot = filesearch::materialize_sysroot(matches.opt_str("sysroot").map(PathBuf::from)); + let target = config::build_target_config(early_dcx, &target, &sysroot); + + get_codegen_backend(early_dcx, &sysroot, backend_name, &target) +} + fn describe_debug_flags() { safe_println!("\nAvailable options:\n"); print_flag_list("-Z", config::Z_OPTIONS); @@ -1156,18 +1076,16 @@ fn describe_codegen_flags() { print_flag_list("-C", config::CG_OPTIONS); } -fn print_flag_list( - cmdline_opt: &str, - flag_list: &[(&'static str, T, &'static str, &'static str)], -) { - let max_len = flag_list.iter().map(|&(name, _, _, _)| name.chars().count()).max().unwrap_or(0); +fn print_flag_list(cmdline_opt: &str, flag_list: &[OptionDesc]) { + let max_len = + flag_list.iter().map(|opt_desc| opt_desc.name().chars().count()).max().unwrap_or(0); - for &(name, _, _, desc) in flag_list { + for opt_desc in flag_list { safe_println!( " {} {:>width$}=val -- {}", cmdline_opt, - name.replace('_', "-"), - desc, + opt_desc.name().replace('_', "-"), + opt_desc.desc(), width = max_len ); } @@ -1201,15 +1119,6 @@ fn print_flag_list( /// be public when using rustc as a library, see /// pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option { - if args.is_empty() { - // user did not write `-v` nor `-Z unstable-options`, so do not - // include that extra information. - let nightly_build = - rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build(); - usage(false, false, nightly_build); - return None; - } - // Parse with *all* options defined in the compiler, we don't worry about // option stability here we just want to parse as much as possible. let mut options = getopts::Options::new(); @@ -1221,8 +1130,8 @@ pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option = match e { getopts::Fail::UnrecognizedOption(ref opt) => CG_OPTIONS .iter() - .map(|&(name, ..)| ('C', name)) - .chain(Z_OPTIONS.iter().map(|&(name, ..)| ('Z', name))) + .map(|opt_desc| ('C', opt_desc.name())) + .chain(Z_OPTIONS.iter().map(|opt_desc| ('Z', opt_desc.name()))) .find(|&(_, name)| *opt == name.replace('_', "-")) .map(|(flag, _)| format!("{e}. Did you mean `-{flag} {opt}`?")), getopts::Fail::ArgumentMissing(ref opt) => { @@ -1255,7 +1164,7 @@ pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option R, R>(f: F) -> Result { /// Variant of `catch_fatal_errors` for the `interface::Result` return type /// that also computes the exit code. -pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 { +pub fn catch_with_exit_code(f: impl FnOnce()) -> i32 { match catch_fatal_errors(f) { - Ok(Ok(())) => EXIT_SUCCESS, + Ok(()) => EXIT_SUCCESS, _ => EXIT_FAILURE, } } @@ -1362,6 +1271,8 @@ fn ice_path_with_config(config: Option<&UnstableOptions>) -> &'static Option) -> &'static Option Arc { +pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&DiagCtxt)) { // If the user has not explicitly overridden "RUST_BACKTRACE", then produce // full backtraces. When a compiler ICE happens, we want to gather // as much information as possible to present in the issue opened @@ -1397,8 +1301,6 @@ pub fn install_ice_hook( } } - let using_internal_features = Arc::new(std::sync::atomic::AtomicBool::default()); - let using_internal_features_hook = Arc::clone(&using_internal_features); panic::update_hook(Box::new( move |default_hook: &(dyn Fn(&PanicHookInfo<'_>) + Send + Sync + 'static), info: &PanicHookInfo<'_>| { @@ -1450,11 +1352,9 @@ pub fn install_ice_hook( } // Print the ICE message - report_ice(info, bug_report_url, extra_info, &using_internal_features_hook); + report_ice(info, bug_report_url, extra_info, &USING_INTERNAL_FEATURES); }, )); - - using_internal_features } /// Prints the ICE message, including query stack, but without backtrace. @@ -1540,9 +1440,9 @@ fn report_ice( // If backtraces are enabled, also print the query stack let backtrace = env::var_os("RUST_BACKTRACE").is_some_and(|x| &x != "0"); - let num_frames = if backtrace { None } else { Some(2) }; + let limit_frames = if backtrace { None } else { Some(2) }; - interface::try_print_query_stack(dcx, num_frames, file); + interface::try_print_query_stack(dcx, limit_frames, file); // We don't trust this callback not to panic itself, so run it at the end after we're sure we've // printed all the relevant info. @@ -1573,7 +1473,7 @@ pub fn init_logger(early_dcx: &EarlyDiagCtxt, cfg: rustc_log::LoggerConfig) { /// Install our usual `ctrlc` handler, which sets [`rustc_const_eval::CTRL_C_RECEIVED`]. /// Making this handler optional lets tools can install a different handler, if they wish. pub fn install_ctrlc_handler() { - #[cfg(not(target_family = "wasm"))] + #[cfg(all(not(miri), not(target_family = "wasm")))] ctrlc::set_handler(move || { // Indicate that we have been signaled to stop, then give the rest of the compiler a bit of // time to check CTRL_C_RECEIVED and run its own shutdown logic, but after a short amount @@ -1595,15 +1495,11 @@ pub fn main() -> ! { init_rustc_env_logger(&early_dcx); signal_handler::install(); let mut callbacks = TimePassesCallbacks::default(); - let using_internal_features = install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ()); + install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ()); install_ctrlc_handler(); - let exit_code = catch_with_exit_code(|| { - RunCompiler::new(&args::raw_args(&early_dcx)?, &mut callbacks) - .set_using_internal_features(using_internal_features) - .run(); - Ok(()) - }); + let exit_code = + catch_with_exit_code(|| run_compiler(&args::raw_args(&early_dcx), &mut callbacks)); if let Some(format) = callbacks.time_passes { let end_rss = get_resident_set_size(); diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index 93f3d2ab911f8..576b1c76823fa 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -7,6 +7,7 @@ use rustc_ast_pretty::pprust as pprust_ast; use rustc_middle::bug; use rustc_middle::mir::{write_mir_graphviz, write_mir_pretty}; use rustc_middle::ty::{self, TyCtxt}; +use rustc_mir_build::thir::print::{thir_flat, thir_tree}; use rustc_session::Session; use rustc_session::config::{OutFileName, PpHirMode, PpMode, PpSourceMode}; use rustc_smir::rustc_internal::pretty::write_smir_pretty; @@ -100,6 +101,10 @@ impl<'tcx> pprust_hir::PpAnn for HirIdentifiedAnn<'tcx> { s.s.space(); s.synth_comment(format!("pat hir_id: {}", pat.hir_id)); } + pprust_hir::AnnNode::TyPat(pat) => { + s.s.space(); + s.synth_comment(format!("ty pat hir_id: {}", pat.hir_id)); + } pprust_hir::AnnNode::Arm(arm) => { s.s.space(); s.synth_comment(format!("arm hir_id: {}", arm.hir_id)); @@ -221,7 +226,7 @@ impl<'tcx> PrintExtra<'tcx> { pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { if ppm.needs_analysis() { - ex.tcx().ensure().analysis(()); + ex.tcx().ensure_ok().analysis(()); } let (src, src_name) = get_source(sess); @@ -313,7 +318,7 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { tcx.dcx().abort_if_errors(); debug!("pretty printing THIR tree"); for did in tcx.hir().body_owners() { - let _ = writeln!(out, "{:?}:\n{}\n", did, tcx.thir_tree(did)); + let _ = writeln!(out, "{:?}:\n{}\n", did, thir_tree(tcx, did)); } out } @@ -324,7 +329,7 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { tcx.dcx().abort_if_errors(); debug!("pretty printing THIR flat"); for did in tcx.hir().body_owners() { - let _ = writeln!(out, "{:?}:\n{}\n", did, tcx.thir_flat(did)); + let _ = writeln!(out, "{:?}:\n{}\n", did, thir_flat(tcx, did)); } out } diff --git a/compiler/rustc_error_codes/src/error_codes/E0038.md b/compiler/rustc_error_codes/src/error_codes/E0038.md index 014d8c4f761ca..4b06395897a87 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0038.md +++ b/compiler/rustc_error_codes/src/error_codes/E0038.md @@ -264,15 +264,15 @@ trait Foo { ### Trait contains associated constants Just like static functions, associated constants aren't stored on the method -table. If the trait or any subtrait contain an associated constant, they cannot -be made into an object. +table. If the trait or any subtrait contain an associated constant, they are not +dyn compatible. ```compile_fail,E0038 trait Foo { const X: i32; } -impl Foo {} +impl dyn Foo {} ``` A simple workaround is to use a helper method instead: diff --git a/compiler/rustc_error_codes/src/error_codes/E0132.md b/compiler/rustc_error_codes/src/error_codes/E0132.md index 51258739b89cb..cbb14510ed75a 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0132.md +++ b/compiler/rustc_error_codes/src/error_codes/E0132.md @@ -1,32 +1,3 @@ -A function with the `start` attribute was declared with type parameters. - -Erroneous code example: - -```compile_fail,E0132 -#![feature(start)] - -#[start] -fn f() {} -``` - -It is not possible to declare type parameters on a function that has the `start` -attribute. Such a function must have the following type signature (for more -information, view [the unstable book][1]): +#### Note: this error code is no longer emitted by the compiler. -[1]: https://doc.rust-lang.org/unstable-book/language-features/start.html - -``` -# let _: -fn(isize, *const *const u8) -> isize; -``` - -Example: - -``` -#![feature(start)] - -#[start] -fn my_start(argc: isize, argv: *const *const u8) -> isize { - 0 -} -``` +A function with the `start` attribute was declared with type parameters. diff --git a/compiler/rustc_error_codes/src/error_codes/E0138.md b/compiler/rustc_error_codes/src/error_codes/E0138.md index 3f5eaea9f989e..2e6ba546a16c7 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0138.md +++ b/compiler/rustc_error_codes/src/error_codes/E0138.md @@ -1,25 +1,3 @@ -More than one function was declared with the `#[start]` attribute. - -Erroneous code example: - -```compile_fail,E0138 -#![feature(start)] - -#[start] -fn foo(argc: isize, argv: *const *const u8) -> isize {} +#### Note: this error code is no longer emitted by the compiler. -#[start] -fn f(argc: isize, argv: *const *const u8) -> isize {} -// error: multiple 'start' functions -``` - -This error indicates that the compiler found multiple functions with the -`#[start]` attribute. This is an error because there must be a unique entry -point into a Rust program. Example: - -``` -#![feature(start)] - -#[start] -fn foo(argc: isize, argv: *const *const u8) -> isize { 0 } // ok! -``` +More than one function was declared with the `#[start]` attribute. diff --git a/compiler/rustc_error_codes/src/error_codes/E0207.md b/compiler/rustc_error_codes/src/error_codes/E0207.md index 95e7c9fc76ce2..5b35748f4723c 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0207.md +++ b/compiler/rustc_error_codes/src/error_codes/E0207.md @@ -195,6 +195,30 @@ impl<'a> Contains for Foo { Please note that unconstrained lifetime parameters are not supported if they are being used by an associated type. +In cases where the associated type's lifetime is meant to be tied to the the +self type, and none of the methods on the trait need ownership or different +mutability, then an option is to implement the trait on a borrowed type: + +```rust +struct Foo(i32); + +trait Contents { + type Item; + + fn get(&self) -> Self::Item; +} + +// Note the lifetime `'a` is used both for the self type... +impl<'a> Contents for &'a Foo { + // ...and the associated type. + type Item = &'a i32; + + fn get(&self) -> Self::Item { + &self.0 + } +} +``` + ### Additional information For more information, please see [RFC 447]. diff --git a/compiler/rustc_error_codes/src/error_codes/E0253.md b/compiler/rustc_error_codes/src/error_codes/E0253.md index aea51d4023821..705d1bfc53e59 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0253.md +++ b/compiler/rustc_error_codes/src/error_codes/E0253.md @@ -1,19 +1,19 @@ -Attempt was made to import an unimportable value. This can happen when trying -to import a method from a trait. +Attempt was made to import an unimportable type. This can happen when trying +to import a type from a trait. Erroneous code example: ```compile_fail,E0253 mod foo { pub trait MyTrait { - fn do_something(); + type SomeType; } } -use foo::MyTrait::do_something; -// error: `do_something` is not directly importable +use foo::MyTrait::SomeType; +// error: `SomeType` is not directly importable fn main() {} ``` -It's invalid to directly import methods belonging to a trait or concrete type. +It's invalid to directly import types belonging to a trait. diff --git a/compiler/rustc_error_codes/src/error_codes/E0393.md b/compiler/rustc_error_codes/src/error_codes/E0393.md index 50225b25163fe..c72608159051e 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0393.md +++ b/compiler/rustc_error_codes/src/error_codes/E0393.md @@ -3,12 +3,10 @@ A type parameter which references `Self` in its default value was not specified. Erroneous code example: ```compile_fail,E0393 -trait A {} +trait A {} -fn together_we_will_rule_the_galaxy(son: &A) {} -// error: the type parameter `T` must be explicitly specified in an -// object type because its default value `Self` references the -// type `Self` +fn together_we_will_rule_the_galaxy(son: &dyn A) {} +// error: the type parameter `T` must be explicitly specified ``` A trait object is defined over a single, fully-defined trait. With a regular @@ -23,7 +21,7 @@ disallowed. Making the trait concrete by explicitly specifying the value of the defaulted parameter will fix this issue. Fixed example: ``` -trait A {} +trait A {} -fn together_we_will_rule_the_galaxy(son: &A) {} // Ok! +fn together_we_will_rule_the_galaxy(son: &dyn A) {} // Ok! ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0433.md b/compiler/rustc_error_codes/src/error_codes/E0433.md index 5a64c13c9af51..36e4b66e8dcdf 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0433.md +++ b/compiler/rustc_error_codes/src/error_codes/E0433.md @@ -19,7 +19,7 @@ If you've expected to use a crate name: ```compile_fail use ferris_wheel::BigO; -// error: failed to resolve: use of undeclared crate or module `ferris_wheel` +// error: failed to resolve: use of undeclared module or unlinked crate ``` Make sure the crate has been added as a dependency in `Cargo.toml`. diff --git a/compiler/rustc_error_codes/src/error_codes/E0647.md b/compiler/rustc_error_codes/src/error_codes/E0647.md index 59bb47ba62a9f..e2f14b81aa6c4 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0647.md +++ b/compiler/rustc_error_codes/src/error_codes/E0647.md @@ -1,13 +1,3 @@ -The `start` function was defined with a where clause. - -Erroneous code example: +#### Note: this error code is no longer emitted by the compiler. -```compile_fail,E0647 -#![feature(start)] - -#[start] -fn start(_: isize, _: *const *const u8) -> isize where (): Copy { - //^ error: `#[start]` function is not allowed to have a where clause - 0 -} -``` +The `start` function was defined with a where clause. diff --git a/compiler/rustc_error_codes/src/error_codes/E0802.md b/compiler/rustc_error_codes/src/error_codes/E0802.md new file mode 100644 index 0000000000000..59061ff04359d --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0802.md @@ -0,0 +1,94 @@ +The target of `derive(CoercePointee)` macro has inadmissible specification for +a meaningful use. + +Erroneous code examples: + +The target data is not a `struct`. + +```compile_fail,E0802 +#![feature(coerce_pointee)] +use std::marker::CoercePointee; +#[derive(CoercePointee)] +enum NotStruct<'a, T: ?Sized> { + Variant(&'a T), +} +``` + +The target data has a layout that is not transparent, or `repr(transparent)` +in other words. + +```compile_fail,E0802 +#![feature(coerce_pointee)] +use std::marker::CoercePointee; +#[derive(CoercePointee)] +struct NotTransparent<'a, #[pointee] T: ?Sized> { + ptr: &'a T, +} +``` + +The target data has no data field. + +```compile_fail,E0802 +#![feature(coerce_pointee)] +use std::marker::CoercePointee; +#[derive(CoercePointee)] +#[repr(transparent)] +struct NoField<'a, #[pointee] T: ?Sized> {} +``` + +The target data is not generic over any data, or has no generic type parameter. + +```compile_fail,E0802 +#![feature(coerce_pointee)] +use std::marker::CoercePointee; +#[derive(CoercePointee)] +#[repr(transparent)] +struct NoGeneric<'a>(&'a u8); +``` + +The target data has multiple generic type parameters, but none is designated as +a pointee for coercion. + +```compile_fail,E0802 +#![feature(coerce_pointee)] +use std::marker::CoercePointee; +#[derive(CoercePointee)] +#[repr(transparent)] +struct AmbiguousPointee<'a, T1: ?Sized, T2: ?Sized> { + a: (&'a T1, &'a T2), +} +``` + +The target data has multiple generic type parameters that are designated as +pointees for coercion. + +```compile_fail,E0802 +#![feature(coerce_pointee)] +use std::marker::CoercePointee; +#[derive(CoercePointee)] +#[repr(transparent)] +struct TooManyPointees< + 'a, + #[pointee] A: ?Sized, + #[pointee] B: ?Sized> +((&'a A, &'a B)); +``` + +The type parameter that is designated as a pointee is not marked `?Sized`. + +```compile_fail,E0802 +#![feature(coerce_pointee)] +use std::marker::CoercePointee; +#[derive(CoercePointee)] +#[repr(transparent)] +struct NoMaybeSized<'a, #[pointee] T> { + ptr: &'a T, +} +``` + +In summary, the `CoercePointee` macro demands the type to be a `struct` that is +generic over at least one type or over more types, one of which is marked with +`#[pointee]`, and has at least one data field and adopts a `repr(transparent)` +layout. +The only generic type or the type marked with `#[pointee]` has to be also +marked as `?Sized`. diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs index 29f3277d3997e..e970b16f61064 100644 --- a/compiler/rustc_error_codes/src/lib.rs +++ b/compiler/rustc_error_codes/src/lib.rs @@ -24,6 +24,10 @@ // // Both columns are necessary because it's not possible in Rust to create a new identifier such as // `E0123` from an integer literal such as `0123`, unfortunately. +// +// Do *not* remove entries from this list. Instead, just add a note th the corresponding markdown +// file saying that this error is not emitted by the compiler any more (see E0001.md for an +// example), and remove all code examples that do not build any more. #[macro_export] macro_rules! error_codes { ($macro:path) => ( @@ -541,6 +545,7 @@ E0798: 0798, E0799: 0799, E0800: 0800, E0801: 0801, +E0802: 0802, ); ) } diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index a51c4140e1760..ba1c3e185c2d9 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -10,7 +10,7 @@ use std::borrow::Cow; use std::error::Error; use std::path::{Path, PathBuf}; -use std::sync::LazyLock; +use std::sync::{Arc, LazyLock}; use std::{fmt, fs, io}; use fluent_bundle::FluentResource; @@ -19,7 +19,7 @@ pub use fluent_bundle::{self, FluentArgs, FluentError, FluentValue}; use fluent_syntax::parser::ParserError; use icu_provider_adapters::fallback::{LocaleFallbackProvider, LocaleFallbacker}; use intl_memoizer::concurrent::IntlLangMemoizer; -use rustc_data_structures::sync::{IntoDynSyncSend, Lrc}; +use rustc_data_structures::sync::IntoDynSyncSend; use rustc_macros::{Decodable, Encodable}; use rustc_span::Span; use tracing::{instrument, trace}; @@ -112,7 +112,7 @@ pub fn fluent_bundle( requested_locale: Option, additional_ftl_path: Option<&Path>, with_directionality_markers: bool, -) -> Result>, TranslationBundleError> { +) -> Result>, TranslationBundleError> { if requested_locale.is_none() && additional_ftl_path.is_none() { return Ok(None); } @@ -190,7 +190,7 @@ pub fn fluent_bundle( bundle.add_resource_overriding(resource); } - let bundle = Lrc::new(bundle); + let bundle = Arc::new(bundle); Ok(Some(bundle)) } @@ -205,7 +205,7 @@ fn register_functions(bundle: &mut FluentBundle) { /// Type alias for the result of `fallback_fluent_bundle` - a reference-counted pointer to a lazily /// evaluated fluent bundle. -pub type LazyFallbackBundle = Lrc FluentBundle>>; +pub type LazyFallbackBundle = Arc FluentBundle>>; /// Return the default `FluentBundle` with standard "en-US" diagnostic messages. #[instrument(level = "trace", skip(resources))] @@ -213,7 +213,7 @@ pub fn fallback_fluent_bundle( resources: Vec<&'static str>, with_directionality_markers: bool, ) -> LazyFallbackBundle { - Lrc::new(LazyLock::new(move || { + Arc::new(LazyLock::new(move || { let mut fallback_bundle = new_bundle(vec![langid!("en-US")]); register_functions(&mut fallback_bundle); diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index 66b9adbead0b6..fbb6a1cc47559 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -31,7 +31,7 @@ tracing = "0.1" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] -version = "0.57.0" +version = "0.59.0" features = [ "Win32_Foundation", "Win32_Security", diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index b337e27940022..f0636b600b70b 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -5,8 +5,9 @@ //! //! [annotate_snippets]: https://docs.rs/crate/annotate-snippets/ +use std::sync::Arc; + use annotate_snippets::{Renderer, Snippet}; -use rustc_data_structures::sync::Lrc; use rustc_error_messages::FluentArgs; use rustc_span::SourceFile; use rustc_span::source_map::SourceMap; @@ -22,8 +23,8 @@ use crate::{ /// Generates diagnostics using annotate-snippet pub struct AnnotateSnippetEmitter { - source_map: Option>, - fluent_bundle: Option>, + source_map: Option>, + fluent_bundle: Option>, fallback_bundle: LazyFallbackBundle, /// If true, hides the longer explanation text @@ -80,7 +81,7 @@ impl Emitter for AnnotateSnippetEmitter { } /// Provides the source string for the given `line` of `file` -fn source_string(file: Lrc, line: &Line) -> String { +fn source_string(file: Arc, line: &Line) -> String { file.get_line(line.line_index - 1).map(|a| a.to_string()).unwrap_or_default() } @@ -102,8 +103,8 @@ fn annotation_level_for_level(level: Level) -> annotate_snippets::Level { impl AnnotateSnippetEmitter { pub fn new( - source_map: Option>, - fluent_bundle: Option>, + source_map: Option>, + fluent_bundle: Option>, fallback_bundle: LazyFallbackBundle, short_message: bool, macro_backtrace: bool, @@ -174,7 +175,7 @@ impl AnnotateSnippetEmitter { source_map.ensure_source_file_source_present(&file); ( format!("{}", source_map.filename_for_diagnostics(&file.name)), - source_string(Lrc::clone(&file), &line), + source_string(Arc::clone(&file), &line), line.line_index, line.annotations, ) diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index afce877547f3e..29a74ed3f4e48 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -4,6 +4,7 @@ use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::panic; +use std::path::PathBuf; use std::thread::panicking; use rustc_data_structures::fx::FxIndexMap; @@ -301,6 +302,7 @@ pub struct DiagInner { pub is_lint: Option, + pub long_ty_path: Option, /// With `-Ztrack_diagnostics` enabled, /// we print where in rustc this error was emitted. pub(crate) emitted_at: DiagLocation, @@ -324,6 +326,7 @@ impl DiagInner { args: Default::default(), sort_span: DUMMY_SP, is_lint: None, + long_ty_path: None, emitted_at: DiagLocation::caller(), } } @@ -641,7 +644,14 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { found_label: &dyn fmt::Display, found: DiagStyledString, ) -> &mut Self { - self.note_expected_found_extra(expected_label, expected, found_label, found, &"", &"") + self.note_expected_found_extra( + expected_label, + expected, + found_label, + found, + DiagStyledString::normal(""), + DiagStyledString::normal(""), + ) } #[rustc_lint_diagnostics] @@ -651,8 +661,8 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { expected: DiagStyledString, found_label: &dyn fmt::Display, found: DiagStyledString, - expected_extra: &dyn fmt::Display, - found_extra: &dyn fmt::Display, + expected_extra: DiagStyledString, + found_extra: DiagStyledString, ) -> &mut Self { let expected_label = expected_label.to_string(); let expected_label = if expected_label.is_empty() { @@ -677,10 +687,13 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { expected_label ))]; msg.extend(expected.0); - msg.push(StringPart::normal(format!("`{expected_extra}\n"))); + msg.push(StringPart::normal(format!("`"))); + msg.extend(expected_extra.0); + msg.push(StringPart::normal(format!("\n"))); msg.push(StringPart::normal(format!("{}{} `", " ".repeat(found_padding), found_label))); msg.extend(found.0); - msg.push(StringPart::normal(format!("`{found_extra}"))); + msg.push(StringPart::normal(format!("`"))); + msg.extend(found_extra.0); // For now, just attach these as notes. self.highlighted_note(msg); @@ -880,7 +893,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { ) } } - /// Show a suggestion that has multiple parts to it, always as it's own subdiagnostic. + /// Show a suggestion that has multiple parts to it, always as its own subdiagnostic. /// In other words, multiple changes need to be applied as part of this suggestion. #[rustc_lint_diagnostics] pub fn multipart_suggestion_verbose( @@ -1293,9 +1306,37 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { /// `cancel`, etc. Afterwards, `drop` is the only code that will be run on /// `self`. fn take_diag(&mut self) -> DiagInner { + if let Some(path) = &self.long_ty_path { + self.note(format!( + "the full name for the type has been written to '{}'", + path.display() + )); + self.note("consider using `--verbose` to print the full type name to the console"); + } Box::into_inner(self.diag.take().unwrap()) } + /// This method allows us to access the path of the file where "long types" are written to. + /// + /// When calling `Diag::emit`, as part of that we will check if a `long_ty_path` has been set, + /// and if it has been then we add a note mentioning the file where the "long types" were + /// written to. + /// + /// When calling `tcx.short_string()` after a `Diag` is constructed, the preferred way of doing + /// so is `tcx.short_string(ty, diag.long_ty_path())`. The diagnostic itself is the one that + /// keeps the existence of a "long type" anywhere in the diagnostic, so the note telling the + /// user where we wrote the file to is only printed once at most, *and* it makes it much harder + /// to forget to set it. + /// + /// If the diagnostic hasn't been created before a "short ty string" is created, then you should + /// ensure that this method is called to set it `*diag.long_ty_path() = path`. + /// + /// As a rule of thumb, if you see or add at least one `tcx.short_string()` call anywhere, in a + /// scope, `diag.long_ty_path()` should be called once somewhere close by. + pub fn long_ty_path(&mut self) -> &mut Option { + &mut self.long_ty_path + } + /// Most `emit_producing_guarantee` functions use this as a starting point. fn emit_producing_nothing(mut self) { let diag = self.take_diag(); diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index d179396398f01..7f383946c1463 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -93,6 +93,7 @@ into_diag_arg_using_display!( SplitDebuginfo, ExitStatus, ErrCode, + rustc_abi::ExternAbi, ); impl IntoDiagArg for rustc_type_ir::TraitRef { @@ -108,13 +109,13 @@ impl IntoDiagArg for rustc_type_ir::ExistentialTrait } impl IntoDiagArg for rustc_type_ir::UnevaluatedConst { - fn into_diag_arg(self) -> rustc_errors::DiagArgValue { + fn into_diag_arg(self) -> DiagArgValue { format!("{self:?}").into_diag_arg() } } impl IntoDiagArg for rustc_type_ir::FnSig { - fn into_diag_arg(self) -> rustc_errors::DiagArgValue { + fn into_diag_arg(self) -> DiagArgValue { format!("{self:?}").into_diag_arg() } } diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index f938352820df7..634afacf53900 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -14,10 +14,11 @@ use std::io::prelude::*; use std::io::{self, IsTerminal}; use std::iter; use std::path::Path; +use std::sync::Arc; use derive_setters::Setters; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; -use rustc_data_structures::sync::{DynSend, IntoDynSyncSend, Lrc}; +use rustc_data_structures::sync::{DynSend, IntoDynSyncSend}; use rustc_error_messages::{FluentArgs, SpanLabel}; use rustc_lexer; use rustc_lint_defs::pluralize; @@ -35,8 +36,8 @@ use crate::snippet::{ use crate::styled_buffer::StyledBuffer; use crate::translation::{Translate, to_fluent_args}; use crate::{ - CodeSuggestion, DiagCtxt, DiagInner, DiagMessage, ErrCode, FluentBundle, LazyFallbackBundle, - Level, MultiSpan, Subdiag, SubstitutionHighlight, SuggestionStyle, TerminalUrl, + CodeSuggestion, DiagInner, DiagMessage, ErrCode, FluentBundle, LazyFallbackBundle, Level, + MultiSpan, Subdiag, SubstitutionHighlight, SuggestionStyle, TerminalUrl, }; /// Default column width, used in tests and when terminal dimensions cannot be determined. @@ -537,11 +538,10 @@ impl Emitter for HumanEmitter { } /// An emitter that does nothing when emitting a non-fatal diagnostic. -/// Fatal diagnostics are forwarded to `fatal_dcx` to avoid silent +/// Fatal diagnostics are forwarded to `fatal_emitter` to avoid silent /// failures of rustc, as witnessed e.g. in issue #89358. pub struct SilentEmitter { - pub fallback_bundle: LazyFallbackBundle, - pub fatal_dcx: DiagCtxt, + pub fatal_emitter: Box, pub fatal_note: Option, pub emit_fatal_diagnostic: bool, } @@ -552,9 +552,7 @@ impl Translate for SilentEmitter { } fn fallback_fluent_bundle(&self) -> &FluentBundle { - // Ideally this field wouldn't be necessary and the fallback bundle in `fatal_dcx` would be - // used but the lock prevents this. - &self.fallback_bundle + self.fatal_emitter.fallback_fluent_bundle() } } @@ -563,12 +561,12 @@ impl Emitter for SilentEmitter { None } - fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) { + fn emit_diagnostic(&mut self, mut diag: DiagInner, registry: &Registry) { if self.emit_fatal_diagnostic && diag.level == Level::Fatal { if let Some(fatal_note) = &self.fatal_note { diag.sub(Level::Note, fatal_note.clone(), MultiSpan::new()); } - self.fatal_dcx.handle().emit_diagnostic(diag); + self.fatal_emitter.emit_diagnostic(diag, registry); } } } @@ -613,8 +611,8 @@ pub enum OutputTheme { pub struct HumanEmitter { #[setters(skip)] dst: IntoDynSyncSend, - sm: Option>, - fluent_bundle: Option>, + sm: Option>, + fluent_bundle: Option>, #[setters(skip)] fallback_bundle: LazyFallbackBundle, short_message: bool, @@ -631,7 +629,7 @@ pub struct HumanEmitter { #[derive(Debug)] pub(crate) struct FileWithAnnotatedLines { - pub(crate) file: Lrc, + pub(crate) file: Arc, pub(crate) lines: Vec, multiline_depth: usize, } @@ -715,7 +713,7 @@ impl HumanEmitter { fn render_source_line( &self, buffer: &mut StyledBuffer, - file: Lrc, + file: Arc, line: &Line, width_offset: usize, code_offset: usize, @@ -1694,7 +1692,7 @@ impl HumanEmitter { // Get the left-side margin to remove it let mut whitespace_margin = usize::MAX; for line_idx in 0..annotated_file.lines.len() { - let file = Lrc::clone(&annotated_file.file); + let file = Arc::clone(&annotated_file.file); let line = &annotated_file.lines[line_idx]; if let Some(source_string) = line.line_index.checked_sub(1).and_then(|l| file.get_line(l)) @@ -1767,7 +1765,7 @@ impl HumanEmitter { let column_width = if let Some(width) = self.diagnostic_width { width.saturating_sub(code_offset) - } else if self.ui_testing { + } else if self.ui_testing || cfg!(miri) { DEFAULT_COLUMN_WIDTH } else { termize::dimensions() @@ -1790,7 +1788,7 @@ impl HumanEmitter { let depths = self.render_source_line( &mut buffer, - Lrc::clone(&annotated_file.file), + Arc::clone(&annotated_file.file), &annotated_file.lines[line_idx], width_offset, code_offset, @@ -1978,13 +1976,16 @@ impl HumanEmitter { Some(Style::HeaderMsg), ); + let other_suggestions = suggestions.len().saturating_sub(MAX_SUGGESTIONS); + let mut row_num = 2; for (i, (complete, parts, highlights, _)) in - suggestions.iter().enumerate().take(MAX_SUGGESTIONS) + suggestions.into_iter().enumerate().take(MAX_SUGGESTIONS) { debug!(?complete, ?parts, ?highlights); - let has_deletion = parts.iter().any(|p| p.is_deletion(sm)); + let has_deletion = + parts.iter().any(|p| p.is_deletion(sm) || p.is_destructive_replacement(sm)); let is_multiline = complete.lines().count() > 1; if i == 0 { @@ -2169,7 +2170,7 @@ impl HumanEmitter { self.draw_code_line( &mut buffer, &mut row_num, - highlight_parts, + &highlight_parts, line_pos + line_start, line, show_code_change, @@ -2215,7 +2216,12 @@ impl HumanEmitter { if let DisplaySuggestion::Diff | DisplaySuggestion::Underline | DisplaySuggestion::Add = show_code_change { - for part in parts { + for mut part in parts { + // If this is a replacement of, e.g. `"a"` into `"ab"`, adjust the + // suggestion and snippet to look as if we just suggested to add + // `"b"`, which is typically much easier for the user to understand. + part.trim_trivial_replacements(sm); + let snippet = if let Ok(snippet) = sm.span_to_snippet(part.span) { snippet } else { @@ -2378,9 +2384,12 @@ impl HumanEmitter { row_num = row + 1; } } - if suggestions.len() > MAX_SUGGESTIONS { - let others = suggestions.len() - MAX_SUGGESTIONS; - let msg = format!("and {} other candidate{}", others, pluralize!(others)); + if other_suggestions > 0 { + let msg = format!( + "and {} other candidate{}", + other_suggestions, + pluralize!(other_suggestions) + ); buffer.puts(row_num, max_line_num_len + 3, &msg, Style::NoStyle); } @@ -2979,7 +2988,7 @@ impl FileWithAnnotatedLines { ) -> Vec { fn add_annotation_to_file( file_vec: &mut Vec, - file: Lrc, + file: Arc, line_index: usize, ann: Annotation, ) { @@ -3116,7 +3125,7 @@ impl FileWithAnnotatedLines { // | baz add_annotation_to_file( &mut output, - Lrc::clone(&file), + Arc::clone(&file), ann.line_start, ann.as_start(), ); @@ -3143,12 +3152,12 @@ impl FileWithAnnotatedLines { .unwrap_or(ann.line_start); for line in ann.line_start + 1..until { // Every `|` that joins the beginning of the span (`___^`) to the end (`|__^`). - add_annotation_to_file(&mut output, Lrc::clone(&file), line, ann.as_line()); + add_annotation_to_file(&mut output, Arc::clone(&file), line, ann.as_line()); } let line_end = ann.line_end - 1; - let end_is_empty = file.get_line(line_end - 1).map_or(false, |s| !filter(&s)); + let end_is_empty = file.get_line(line_end - 1).is_some_and(|s| !filter(&s)); if middle < line_end && !end_is_empty { - add_annotation_to_file(&mut output, Lrc::clone(&file), line_end, ann.as_line()); + add_annotation_to_file(&mut output, Arc::clone(&file), line_end, ann.as_line()); } } else { end_ann.annotation_type = AnnotationType::Singleline; diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 97df7f9265aa4..7d7f364fec236 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -16,12 +16,12 @@ use std::sync::{Arc, Mutex}; use std::vec; use derive_setters::Setters; -use rustc_data_structures::sync::{IntoDynSyncSend, Lrc}; +use rustc_data_structures::sync::IntoDynSyncSend; use rustc_error_messages::FluentArgs; use rustc_lint_defs::Applicability; use rustc_span::Span; use rustc_span::hygiene::ExpnData; -use rustc_span::source_map::SourceMap; +use rustc_span::source_map::{FilePathMapping, SourceMap}; use serde::Serialize; use termcolor::{ColorSpec, WriteColor}; @@ -45,8 +45,8 @@ pub struct JsonEmitter { #[setters(skip)] dst: IntoDynSyncSend>, #[setters(skip)] - sm: Lrc, - fluent_bundle: Option>, + sm: Option>, + fluent_bundle: Option>, #[setters(skip)] fallback_bundle: LazyFallbackBundle, #[setters(skip)] @@ -65,7 +65,7 @@ pub struct JsonEmitter { impl JsonEmitter { pub fn new( dst: Box, - sm: Lrc, + sm: Option>, fallback_bundle: LazyFallbackBundle, pretty: bool, json_rendered: HumanReadableErrorType, @@ -171,7 +171,7 @@ impl Emitter for JsonEmitter { } fn source_map(&self) -> Option<&SourceMap> { - Some(&self.sm) + self.sm.as_deref() } fn should_show_explain(&self) -> bool { @@ -369,9 +369,9 @@ impl Diagnostic { ColorConfig::Always | ColorConfig::Auto => dst = Box::new(termcolor::Ansi::new(dst)), ColorConfig::Never => {} } - HumanEmitter::new(dst, Lrc::clone(&je.fallback_bundle)) + HumanEmitter::new(dst, Arc::clone(&je.fallback_bundle)) .short_message(short) - .sm(Some(Lrc::clone(&je.sm))) + .sm(je.sm.clone()) .fluent_bundle(je.fluent_bundle.clone()) .diagnostic_width(je.diagnostic_width) .macro_backtrace(je.macro_backtrace) @@ -458,22 +458,34 @@ impl DiagnosticSpan { mut backtrace: impl Iterator, je: &JsonEmitter, ) -> DiagnosticSpan { - let start = je.sm.lookup_char_pos(span.lo()); + let empty_source_map; + let sm = match &je.sm { + Some(s) => s, + None => { + span = rustc_span::DUMMY_SP; + empty_source_map = Arc::new(SourceMap::new(FilePathMapping::empty())); + empty_source_map + .new_source_file(std::path::PathBuf::from("empty.rs").into(), String::new()); + &empty_source_map + } + }; + let start = sm.lookup_char_pos(span.lo()); // If this goes from the start of a line to the end and the replacement // is an empty string, increase the length to include the newline so we don't // leave an empty line if start.col.0 == 0 - && suggestion.map_or(false, |(s, _)| s.is_empty()) - && let Ok(after) = je.sm.span_to_next_source(span) + && let Some((suggestion, _)) = suggestion + && suggestion.is_empty() + && let Ok(after) = sm.span_to_next_source(span) && after.starts_with('\n') { span = span.with_hi(span.hi() + rustc_span::BytePos(1)); } - let end = je.sm.lookup_char_pos(span.hi()); + let end = sm.lookup_char_pos(span.hi()); let backtrace_step = backtrace.next().map(|bt| { let call_site = Self::from_span_full(bt.call_site, false, None, None, backtrace, je); let def_site_span = Self::from_span_full( - je.sm.guess_head_span(bt.def_site), + sm.guess_head_span(bt.def_site), false, None, None, @@ -488,7 +500,7 @@ impl DiagnosticSpan { }); DiagnosticSpan { - file_name: je.sm.filename_for_diagnostics(&start.file.name).to_string(), + file_name: sm.filename_for_diagnostics(&start.file.name).to_string(), byte_start: start.file.original_relative_byte_pos(span.lo()).0, byte_end: start.file.original_relative_byte_pos(span.hi()).0, line_start: start.line, @@ -558,19 +570,20 @@ impl DiagnosticSpanLine { /// `span` within the line. fn from_span(span: Span, je: &JsonEmitter) -> Vec { je.sm - .span_to_lines(span) - .map(|lines| { + .as_ref() + .and_then(|sm| { + let lines = sm.span_to_lines(span).ok()?; // We can't get any lines if the source is unavailable. if !should_show_source_code( &je.ignored_directories_in_source_blocks, - &je.sm, + &sm, &lines.file, ) { - return vec![]; + return None; } let sf = &*lines.file; - lines + let span_lines = lines .lines .iter() .map(|line| { @@ -581,8 +594,9 @@ impl DiagnosticSpanLine { line.end_col.0 + 1, ) }) - .collect() + .collect(); + Some(span_lines) }) - .unwrap_or_else(|_| vec![]) + .unwrap_or_default() } } diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index 0de555b83d3ab..40973e8e5d8a4 100644 --- a/compiler/rustc_errors/src/json/tests.rs +++ b/compiler/rustc_errors/src/json/tests.rs @@ -39,7 +39,7 @@ impl Write for Shared { /// Test the span yields correct positions in JSON. fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { rustc_span::create_default_session_globals_then(|| { - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); sm.new_source_file(Path::new("test.rs").to_owned().into(), code.to_owned()); let fallback_bundle = crate::fallback_fluent_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false); @@ -47,7 +47,7 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { let output = Arc::new(Mutex::new(Vec::new())); let je = JsonEmitter::new( Box::new(Shared { data: output.clone() }), - sm, + Some(sm), fallback_bundle, true, // pretty HumanReadableErrorType::Short, @@ -69,96 +69,128 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { #[test] fn empty() { - test_positions(" ", (0, 1), SpanTestData { - byte_start: 0, - byte_end: 1, - line_start: 1, - column_start: 1, - line_end: 1, - column_end: 2, - }) + test_positions( + " ", + (0, 1), + SpanTestData { + byte_start: 0, + byte_end: 1, + line_start: 1, + column_start: 1, + line_end: 1, + column_end: 2, + }, + ) } #[test] fn bom() { - test_positions("\u{feff} ", (0, 1), SpanTestData { - byte_start: 3, - byte_end: 4, - line_start: 1, - column_start: 1, - line_end: 1, - column_end: 2, - }) + test_positions( + "\u{feff} ", + (0, 1), + SpanTestData { + byte_start: 3, + byte_end: 4, + line_start: 1, + column_start: 1, + line_end: 1, + column_end: 2, + }, + ) } #[test] fn lf_newlines() { - test_positions("\nmod foo;\nmod bar;\n", (5, 12), SpanTestData { - byte_start: 5, - byte_end: 12, - line_start: 2, - column_start: 5, - line_end: 3, - column_end: 3, - }) + test_positions( + "\nmod foo;\nmod bar;\n", + (5, 12), + SpanTestData { + byte_start: 5, + byte_end: 12, + line_start: 2, + column_start: 5, + line_end: 3, + column_end: 3, + }, + ) } #[test] fn crlf_newlines() { - test_positions("\r\nmod foo;\r\nmod bar;\r\n", (5, 12), SpanTestData { - byte_start: 6, - byte_end: 14, - line_start: 2, - column_start: 5, - line_end: 3, - column_end: 3, - }) + test_positions( + "\r\nmod foo;\r\nmod bar;\r\n", + (5, 12), + SpanTestData { + byte_start: 6, + byte_end: 14, + line_start: 2, + column_start: 5, + line_end: 3, + column_end: 3, + }, + ) } #[test] fn crlf_newlines_with_bom() { - test_positions("\u{feff}\r\nmod foo;\r\nmod bar;\r\n", (5, 12), SpanTestData { - byte_start: 9, - byte_end: 17, - line_start: 2, - column_start: 5, - line_end: 3, - column_end: 3, - }) + test_positions( + "\u{feff}\r\nmod foo;\r\nmod bar;\r\n", + (5, 12), + SpanTestData { + byte_start: 9, + byte_end: 17, + line_start: 2, + column_start: 5, + line_end: 3, + column_end: 3, + }, + ) } #[test] fn span_before_crlf() { - test_positions("foo\r\nbar", (2, 3), SpanTestData { - byte_start: 2, - byte_end: 3, - line_start: 1, - column_start: 3, - line_end: 1, - column_end: 4, - }) + test_positions( + "foo\r\nbar", + (2, 3), + SpanTestData { + byte_start: 2, + byte_end: 3, + line_start: 1, + column_start: 3, + line_end: 1, + column_end: 4, + }, + ) } #[test] fn span_on_crlf() { - test_positions("foo\r\nbar", (3, 4), SpanTestData { - byte_start: 3, - byte_end: 5, - line_start: 1, - column_start: 4, - line_end: 2, - column_end: 1, - }) + test_positions( + "foo\r\nbar", + (3, 4), + SpanTestData { + byte_start: 3, + byte_end: 5, + line_start: 1, + column_start: 4, + line_end: 2, + column_end: 1, + }, + ) } #[test] fn span_after_crlf() { - test_positions("foo\r\nbar", (4, 5), SpanTestData { - byte_start: 5, - byte_end: 6, - line_start: 2, - column_start: 1, - line_end: 2, - column_end: 2, - }) + test_positions( + "foo\r\nbar", + (4, 5), + SpanTestData { + byte_start: 5, + byte_end: 6, + line_start: 2, + column_start: 1, + line_end: 2, + column_end: 2, + }, + ) } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 58fe3ec4b8580..8ff5dc1259697 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -35,6 +35,7 @@ use std::backtrace::{Backtrace, BacktraceStatus}; use std::borrow::Cow; use std::cell::Cell; use std::error::Report; +use std::ffi::OsStr; use std::hash::Hash; use std::io::Write; use std::num::NonZero; @@ -58,13 +59,13 @@ use emitter::{DynEmitter, Emitter, is_case_difference, is_different}; use rustc_data_structures::AtomicRef; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::stable_hasher::{Hash128, StableHasher}; -use rustc_data_structures::sync::Lock; +use rustc_data_structures::sync::{DynSend, Lock}; pub use rustc_error_messages::{ DiagMessage, FluentBundle, LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel, SubdiagMessage, fallback_fluent_bundle, fluent_bundle, }; use rustc_lint_defs::LintExpectationId; -pub use rustc_lint_defs::{Applicability, pluralize}; +pub use rustc_lint_defs::{Applicability, listify, pluralize}; use rustc_macros::{Decodable, Encodable}; pub use rustc_span::ErrorGuaranteed; pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker}; @@ -229,10 +230,40 @@ impl SubstitutionPart { !self.snippet.is_empty() && self.replaces_meaningful_content(sm) } + /// Whether this is a replacement that overwrites source with a snippet + /// in a way that isn't a superset of the original string. For example, + /// replacing "abc" with "abcde" is not destructive, but replacing it + /// it with "abx" is, since the "c" character is lost. + pub fn is_destructive_replacement(&self, sm: &SourceMap) -> bool { + self.is_replacement(sm) + && !sm.span_to_snippet(self.span).is_ok_and(|snippet| { + self.snippet.trim_start().starts_with(snippet.trim_start()) + || self.snippet.trim_end().ends_with(snippet.trim_end()) + }) + } + fn replaces_meaningful_content(&self, sm: &SourceMap) -> bool { sm.span_to_snippet(self.span) .map_or(!self.span.is_empty(), |snippet| !snippet.trim().is_empty()) } + + /// Try to turn a replacement into an addition when the span that is being + /// overwritten matches either the prefix or suffix of the replacement. + fn trim_trivial_replacements(&mut self, sm: &SourceMap) { + if self.snippet.is_empty() { + return; + } + let Ok(snippet) = sm.span_to_snippet(self.span) else { + return; + }; + if self.snippet.starts_with(&snippet) { + self.span = self.span.shrink_to_hi(); + self.snippet = self.snippet[snippet.len()..].to_string(); + } else if self.snippet.ends_with(&snippet) { + self.span = self.span.shrink_to_lo(); + self.snippet = self.snippet[..self.snippet.len() - snippet.len()].to_string(); + } + } } impl CodeSuggestion { @@ -566,9 +597,7 @@ pub enum StashKey { /// FRU syntax MaybeFruTypo, CallAssocMethod, - TraitMissingMethod, AssociatedTypeSuggestion, - OpaqueHiddenTypeMismatch, MaybeForgetReturn, /// Query cycle detected, stashing in favor of a better error. Cycle, @@ -677,57 +706,44 @@ impl DiagCtxt { Self { inner: Lock::new(DiagCtxtInner::new(emitter)) } } - pub fn make_silent( - &self, - fallback_bundle: LazyFallbackBundle, - fatal_note: Option, - emit_fatal_diagnostic: bool, - ) { - self.wrap_emitter(|old_dcx| { - Box::new(emitter::SilentEmitter { - fallback_bundle, - fatal_dcx: DiagCtxt { inner: Lock::new(old_dcx) }, - fatal_note, - emit_fatal_diagnostic, - }) - }); - } - - fn wrap_emitter(&self, f: F) - where - F: FnOnce(DiagCtxtInner) -> Box, - { - // A empty type that implements `Emitter` so that a `DiagCtxtInner` can be constructed - // to temporarily swap in place of the real one, which will be used in constructing - // its replacement. + pub fn make_silent(&self, fatal_note: Option, emit_fatal_diagnostic: bool) { + // An empty type that implements `Emitter` to temporarily swap in place of the real one, + // which will be used in constructing its replacement. struct FalseEmitter; impl Emitter for FalseEmitter { fn emit_diagnostic(&mut self, _: DiagInner, _: &Registry) { - unimplemented!("false emitter must only used during `wrap_emitter`") + unimplemented!("false emitter must only used during `make_silent`") } fn source_map(&self) -> Option<&SourceMap> { - unimplemented!("false emitter must only used during `wrap_emitter`") + unimplemented!("false emitter must only used during `make_silent`") } } impl translation::Translate for FalseEmitter { fn fluent_bundle(&self) -> Option<&FluentBundle> { - unimplemented!("false emitter must only used during `wrap_emitter`") + unimplemented!("false emitter must only used during `make_silent`") } fn fallback_fluent_bundle(&self) -> &FluentBundle { - unimplemented!("false emitter must only used during `wrap_emitter`") + unimplemented!("false emitter must only used during `make_silent`") } } let mut inner = self.inner.borrow_mut(); - let mut prev_dcx = DiagCtxtInner::new(Box::new(FalseEmitter)); - std::mem::swap(&mut *inner, &mut prev_dcx); - let new_emitter = f(prev_dcx); - let mut new_dcx = DiagCtxtInner::new(new_emitter); - std::mem::swap(&mut *inner, &mut new_dcx); + let mut prev_emitter = Box::new(FalseEmitter) as Box; + std::mem::swap(&mut inner.emitter, &mut prev_emitter); + let new_emitter = Box::new(emitter::SilentEmitter { + fatal_emitter: prev_emitter, + fatal_note, + emit_fatal_diagnostic, + }); + inner.emitter = new_emitter; + } + + pub fn set_emitter(&self, emitter: Box) { + self.inner.borrow_mut().emitter = emitter; } /// Translate `message` eagerly with `args` to `SubdiagMessage::Eager`. @@ -1062,8 +1078,8 @@ impl<'a> DiagCtxtHandle<'a> { /// bad results, such as spurious/uninteresting additional errors -- when /// returning an error `Result` is difficult. pub fn abort_if_errors(&self) { - if self.has_errors().is_some() { - FatalError.raise(); + if let Some(guar) = self.has_errors() { + guar.raise_fatal(); } } @@ -1719,7 +1735,7 @@ impl DiagCtxtInner { let bugs: Vec<_> = std::mem::take(&mut self.delayed_bugs).into_iter().map(|(b, _)| b).collect(); - let backtrace = std::env::var_os("RUST_BACKTRACE").map_or(true, |x| &x != "0"); + let backtrace = std::env::var_os("RUST_BACKTRACE").as_deref() != Some(OsStr::new("0")); let decorate = backtrace || self.ice_file.is_none(); let mut out = self .ice_file @@ -2000,18 +2016,6 @@ pub fn a_or_an(s: &str) -> &'static str { } } -/// Grammatical tool for displaying messages to end users in a nice form. -/// -/// Take a list ["a", "b", "c"] and output a display friendly version "a, b and c" -pub fn display_list_with_comma_and(v: &[T]) -> String { - match v { - [] => "".to_string(), - [a] => a.to_string(), - [a, b] => format!("{a} and {b}"), - [a, v @ ..] => format!("{a}, {}", display_list_with_comma_and(v)), - } -} - #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum TerminalUrl { No, diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index e6adbc0f0ac89..819694d1cdc1f 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -3,6 +3,7 @@ use std::iter; use std::path::Component::Prefix; use std::path::{Path, PathBuf}; use std::rc::Rc; +use std::sync::Arc; use rustc_ast::attr::{AttributeExt, MarkedAttrs}; use rustc_ast::ptr::P; @@ -12,7 +13,7 @@ use rustc_ast::visit::{AssocCtxt, Visitor}; use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind}; use rustc_attr_parsing::{self as attr, Deprecation, Stability}; use rustc_data_structures::fx::FxIndexMap; -use rustc_data_structures::sync::{self, Lrc}; +use rustc_data_structures::sync; use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult}; use rustc_feature::Features; use rustc_lint_defs::{BufferedEarlyLint, RegisteredTools}; @@ -522,7 +523,7 @@ impl MacResult for MacEager { return Some(P(ast::Pat { id: ast::DUMMY_NODE_ID, span: e.span, - kind: PatKind::Lit(e), + kind: PatKind::Expr(e), tokens: None, })); } @@ -727,7 +728,7 @@ pub struct SyntaxExtension { /// Span of the macro definition. pub span: Span, /// List of unstable features that are treated as stable inside this macro. - pub allow_internal_unstable: Option>, + pub allow_internal_unstable: Option>, /// The macro's stability info. pub stability: Option, /// The macro's deprecation info. @@ -986,7 +987,7 @@ pub struct Indeterminate; pub struct DeriveResolution { pub path: ast::Path, pub item: Annotatable, - pub exts: Option>, + pub exts: Option>, pub is_const: bool, } @@ -1017,7 +1018,7 @@ pub trait ResolverExpand { invoc: &Invocation, eager_expansion_root: LocalExpnId, force: bool, - ) -> Result, Indeterminate>; + ) -> Result, Indeterminate>; fn record_macro_rule_usage(&mut self, mac_id: NodeId, rule_index: usize); diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 22bfda34cc0e5..ee7f68cc2f01c 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -1,7 +1,8 @@ use rustc_ast::ptr::P; use rustc_ast::util::literal; use rustc_ast::{ - self as ast, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind, UnOp, attr, token, + self as ast, AnonConst, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind, UnOp, + attr, token, }; use rustc_span::source_map::Spanned; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; @@ -138,6 +139,42 @@ impl<'a> ExtCtxt<'a> { } } + pub fn lifetime_param( + &self, + span: Span, + ident: Ident, + bounds: ast::GenericBounds, + ) -> ast::GenericParam { + ast::GenericParam { + id: ast::DUMMY_NODE_ID, + ident: ident.with_span_pos(span), + attrs: AttrVec::new(), + bounds, + is_placeholder: false, + kind: ast::GenericParamKind::Lifetime, + colon_span: None, + } + } + + pub fn const_param( + &self, + span: Span, + ident: Ident, + bounds: ast::GenericBounds, + ty: P, + default: Option, + ) -> ast::GenericParam { + ast::GenericParam { + id: ast::DUMMY_NODE_ID, + ident: ident.with_span_pos(span), + attrs: AttrVec::new(), + bounds, + is_placeholder: false, + kind: ast::GenericParamKind::Const { ty, kw_span: DUMMY_SP, default }, + colon_span: None, + } + } + pub fn trait_ref(&self, path: ast::Path) -> ast::TraitRef { ast::TraitRef { path, ref_id: ast::DUMMY_NODE_ID } } @@ -233,11 +270,14 @@ impl<'a> ExtCtxt<'a> { } pub fn block_expr(&self, expr: P) -> P { - self.block(expr.span, thin_vec![ast::Stmt { - id: ast::DUMMY_NODE_ID, - span: expr.span, - kind: ast::StmtKind::Expr(expr), - }]) + self.block( + expr.span, + thin_vec![ast::Stmt { + id: ast::DUMMY_NODE_ID, + span: expr.span, + kind: ast::StmtKind::Expr(expr), + }], + ) } pub fn block(&self, span: Span, stmts: ThinVec) -> P { P(ast::Block { @@ -486,7 +526,7 @@ impl<'a> ExtCtxt<'a> { self.pat(span, PatKind::Wild) } pub fn pat_lit(&self, span: Span, expr: P) -> P { - self.pat(span, PatKind::Lit(expr)) + self.pat(span, PatKind::Expr(expr)) } pub fn pat_ident(&self, span: Span, ident: Ident) -> P { self.pat_ident_binding_mode(span, ident, ast::BindingMode::NONE) diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 91624c7554cb0..83255b820178f 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -18,7 +18,7 @@ use rustc_lint_defs::BuiltinLintDiag; use rustc_parse::validate_attr; use rustc_session::Session; use rustc_session::parse::feature_err; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{STDLIB_STABLE_CRATES, Span, Symbol, sym}; use thin_vec::ThinVec; use tracing::instrument; @@ -107,14 +107,11 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) - // If the enabled feature is unstable, record it. if UNSTABLE_LANG_FEATURES.iter().find(|f| name == f.name).is_some() { - // When the ICE comes from core, alloc or std (approximation of the standard - // library), there's a chance that the person hitting the ICE may be using - // -Zbuild-std or similar with an untested target. The bug is probably in the - // standard library and not the compiler in that case, but that doesn't really - // matter - we want a bug report. - if features.internal(name) - && ![sym::core, sym::alloc, sym::std].contains(&crate_name) - { + // When the ICE comes a standard library crate, there's a chance that the person + // hitting the ICE may be using -Zbuild-std or similar with an untested target. + // The bug is probably in the standard library and not the compiler in that case, + // but that doesn't really matter - we want a bug report. + if features.internal(name) && !STDLIB_STABLE_CRATES.contains(&crate_name) { sess.using_internal_features.store(true, std::sync::atomic::Ordering::Relaxed); } @@ -133,7 +130,7 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) - // Similar to above, detect internal lib features to suppress // the ICE message that asks for a report. - if features.internal(name) && ![sym::core, sym::alloc, sym::std].contains(&crate_name) { + if features.internal(name) && !STDLIB_STABLE_CRATES.contains(&crate_name) { sess.using_internal_features.store(true, std::sync::atomic::Ordering::Relaxed); } } @@ -394,7 +391,7 @@ impl<'a> StripUnconfigured<'a> { validate_attr::deny_builtin_meta_unsafety(&self.sess.psess, &meta_item); ( - parse_cfg(&meta_item, self.sess).map_or(true, |meta_item| { + parse_cfg(&meta_item, self.sess).is_none_or(|meta_item| { attr::cfg_matches(meta_item, &self.sess, self.lint_node_id, self.features) }), Some(meta_item), diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index ec497f6f8f1f3..e3f31ebeca3f3 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1,6 +1,7 @@ use std::ops::Deref; use std::path::PathBuf; use std::rc::Rc; +use std::sync::Arc; use std::{iter, mem}; use rustc_ast as ast; @@ -16,7 +17,6 @@ use rustc_ast::{ }; use rustc_ast_pretty::pprust; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; -use rustc_data_structures::sync::Lrc; use rustc_errors::PResult; use rustc_feature::Features; use rustc_parse::parser::{ @@ -579,7 +579,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { &mut self, mut fragment: AstFragment, extra_placeholders: &[NodeId], - ) -> (AstFragment, Vec<(Invocation, Option>)>) { + ) -> (AstFragment, Vec<(Invocation, Option>)>) { // Resolve `$crate`s in the fragment for pretty-printing. self.cx.resolver.resolve_dollar_crates(); @@ -1774,7 +1774,7 @@ fn build_single_delegations<'a, Node: InvocationCollectorNode>( struct InvocationCollector<'a, 'b> { cx: &'a mut ExtCtxt<'b>, - invocations: Vec<(Invocation, Option>)>, + invocations: Vec<(Invocation, Option>)>, monotonic: bool, } diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index 9f48835e15d7b..e60a9e83184fb 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -159,7 +159,7 @@ impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'match if self .best_failure .as_ref() - .map_or(true, |failure| failure.is_better_position(*approx_position)) + .is_none_or(|failure| failure.is_better_position(*approx_position)) { self.best_failure = Some(BestFailure { token: token.clone(), diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 7ac9f453baef3..b02a9b93c8af0 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -405,26 +405,35 @@ pub fn compile_declarative_macro( // ...quasiquoting this would be nice. // These spans won't matter, anyways let argument_gram = vec![ - mbe::TokenTree::Sequence(DelimSpan::dummy(), mbe::SequenceRepetition { - tts: vec![ - mbe::TokenTree::MetaVarDecl(span, lhs_nm, tt_spec), - mbe::TokenTree::token(token::FatArrow, span), - mbe::TokenTree::MetaVarDecl(span, rhs_nm, tt_spec), - ], - separator: Some(Token::new(if macro_rules { token::Semi } else { token::Comma }, span)), - kleene: mbe::KleeneToken::new(mbe::KleeneOp::OneOrMore, span), - num_captures: 2, - }), + mbe::TokenTree::Sequence( + DelimSpan::dummy(), + mbe::SequenceRepetition { + tts: vec![ + mbe::TokenTree::MetaVarDecl(span, lhs_nm, tt_spec), + mbe::TokenTree::token(token::FatArrow, span), + mbe::TokenTree::MetaVarDecl(span, rhs_nm, tt_spec), + ], + separator: Some(Token::new( + if macro_rules { token::Semi } else { token::Comma }, + span, + )), + kleene: mbe::KleeneToken::new(mbe::KleeneOp::OneOrMore, span), + num_captures: 2, + }, + ), // to phase into semicolon-termination instead of semicolon-separation - mbe::TokenTree::Sequence(DelimSpan::dummy(), mbe::SequenceRepetition { - tts: vec![mbe::TokenTree::token( - if macro_rules { token::Semi } else { token::Comma }, - span, - )], - separator: None, - kleene: mbe::KleeneToken::new(mbe::KleeneOp::ZeroOrMore, span), - num_captures: 0, - }), + mbe::TokenTree::Sequence( + DelimSpan::dummy(), + mbe::SequenceRepetition { + tts: vec![mbe::TokenTree::token( + if macro_rules { token::Semi } else { token::Comma }, + span, + )], + separator: None, + kleene: mbe::KleeneToken::new(mbe::KleeneOp::ZeroOrMore, span), + num_captures: 0, + }, + ), ]; // Convert it into `MatcherLoc` form. let argument_gram = mbe::macro_parser::compute_locs(&argument_gram); diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index a27d47892e461..b9a0615790790 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -179,10 +179,10 @@ fn parse_tree<'a>( Some(&tokenstream::TokenTree::Delimited(delim_span, _, delim, ref tts)) => { if parsing_patterns { if delim != Delimiter::Parenthesis { - span_dollar_dollar_or_metavar_in_the_lhs_err(sess, &Token { - kind: token::OpenDelim(delim), - span: delim_span.entire(), - }); + span_dollar_dollar_or_metavar_in_the_lhs_err( + sess, + &Token { kind: token::OpenDelim(delim), span: delim_span.entire() }, + ); } } else { match delim { @@ -235,12 +235,10 @@ fn parse_tree<'a>( // Count the number of captured "names" (i.e., named metavars) let num_captures = if parsing_patterns { count_metavar_decls(&sequence) } else { 0 }; - TokenTree::Sequence(delim_span, SequenceRepetition { - tts: sequence, - separator, - kleene, - num_captures, - }) + TokenTree::Sequence( + delim_span, + SequenceRepetition { tts: sequence, separator, kleene, num_captures }, + ) } // `tree` is followed by an `ident`. This could be `$meta_var` or the `$crate` @@ -261,10 +259,10 @@ fn parse_tree<'a>( _, )) => { if parsing_patterns { - span_dollar_dollar_or_metavar_in_the_lhs_err(sess, &Token { - kind: token::Dollar, - span: dollar_span2, - }); + span_dollar_dollar_or_metavar_in_the_lhs_err( + sess, + &Token { kind: token::Dollar, span: dollar_span2 }, + ); } else { maybe_emit_macro_metavar_expr_feature(features, sess, dollar_span2); } @@ -289,12 +287,14 @@ fn parse_tree<'a>( // `tree` is the beginning of a delimited set of tokens (e.g., `(` or `{`). We need to // descend into the delimited set and further parse it. - &tokenstream::TokenTree::Delimited(span, spacing, delim, ref tts) => { - TokenTree::Delimited(span, spacing, Delimited { + &tokenstream::TokenTree::Delimited(span, spacing, delim, ref tts) => TokenTree::Delimited( + span, + spacing, + Delimited { delim, tts: parse(tts, parsing_patterns, sess, node_id, features, edition), - }) - } + }, + ), } } diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index ef209c2bce123..595c8c3279f41 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -1,11 +1,11 @@ use std::mem; +use std::sync::Arc; use rustc_ast::ExprKind; use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::token::{self, Delimiter, IdentIsRaw, Lit, LitKind, Nonterminal, Token, TokenKind}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::Lrc; use rustc_errors::{Diag, DiagCtxtHandle, PResult, pluralize}; use rustc_parse::lexer::nfc_normalize; use rustc_parse::parser::ParseNtResult; @@ -282,11 +282,13 @@ pub(super) fn transcribe<'a>( } MatchedSingle(ParseNtResult::Ident(ident, is_raw)) => { marker.visit_span(&mut sp); + with_metavar_spans(|mspans| mspans.insert(ident.span, sp)); let kind = token::NtIdent(*ident, *is_raw); TokenTree::token_alone(kind, sp) } MatchedSingle(ParseNtResult::Lifetime(ident, is_raw)) => { marker.visit_span(&mut sp); + with_metavar_spans(|mspans| mspans.insert(ident.span, sp)); let kind = token::NtLifetime(*ident, *is_raw); TokenTree::token_alone(kind, sp) } @@ -295,7 +297,9 @@ pub(super) fn transcribe<'a>( // `Delimiter::Invisible` to maintain parsing priorities. // `Interpolated` is currently used for such groups in rustc parser. marker.visit_span(&mut sp); - TokenTree::token_alone(token::Interpolated(Lrc::clone(nt)), sp) + let use_span = nt.use_span(); + with_metavar_spans(|mspans| mspans.insert(use_span, sp)); + TokenTree::token_alone(token::Interpolated(Arc::clone(nt)), sp) } MatchedSeq(..) => { // We were unable to descend far enough. This is an error. @@ -410,19 +414,15 @@ fn maybe_use_metavar_location( return orig_tt.clone(); } - let insert = |mspans: &mut FxHashMap<_, _>, s, ms| match mspans.try_insert(s, ms) { - Ok(_) => true, - Err(err) => *err.entry.get() == ms, // Tried to insert the same span, still success - }; marker.visit_span(&mut metavar_span); let no_collision = match orig_tt { TokenTree::Token(token, ..) => { - with_metavar_spans(|mspans| insert(mspans, token.span, metavar_span)) + with_metavar_spans(|mspans| mspans.insert(token.span, metavar_span)) } TokenTree::Delimited(dspan, ..) => with_metavar_spans(|mspans| { - insert(mspans, dspan.open, metavar_span) - && insert(mspans, dspan.close, metavar_span) - && insert(mspans, dspan.entire(), metavar_span) + mspans.insert(dspan.open, metavar_span) + && mspans.insert(dspan.close, metavar_span) + && mspans.insert(dspan.entire(), metavar_span) }), }; if no_collision || psess.source_map().is_imported(metavar_span) { @@ -434,14 +434,14 @@ fn maybe_use_metavar_location( match orig_tt { TokenTree::Token(Token { kind, span }, spacing) => { let span = metavar_span.with_ctxt(span.ctxt()); - with_metavar_spans(|mspans| insert(mspans, span, metavar_span)); + with_metavar_spans(|mspans| mspans.insert(span, metavar_span)); TokenTree::Token(Token { kind: kind.clone(), span }, *spacing) } TokenTree::Delimited(dspan, dspacing, delimiter, tts) => { let open = metavar_span.with_ctxt(dspan.open.ctxt()); let close = metavar_span.with_ctxt(dspan.close.ctxt()); with_metavar_spans(|mspans| { - insert(mspans, open, metavar_span) && insert(mspans, close, metavar_span) + mspans.insert(open, metavar_span) && mspans.insert(close, metavar_span) }); let dspan = DelimSpan::from_pair(open, close); TokenTree::Delimited(dspan, *dspacing, *delimiter, tts.clone()) diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 7eb09a64e96ae..5581a6e65089a 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -1,4 +1,5 @@ use std::ops::{Bound, Range}; +use std::sync::Arc; use ast::token::IdentIsRaw; use pm::bridge::{ @@ -11,7 +12,6 @@ use rustc_ast::tokenstream::{self, DelimSpacing, Spacing, TokenStream}; use rustc_ast::util::literal::escape_byte_str_symbol; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::Lrc; use rustc_errors::{Diag, ErrorGuaranteed, MultiSpan, PResult}; use rustc_parse::lexer::nfc_normalize; use rustc_parse::parser::Parser; @@ -446,7 +446,7 @@ impl<'a, 'b> Rustc<'a, 'b> { impl server::Types for Rustc<'_, '_> { type FreeFunctions = FreeFunctions; type TokenStream = TokenStream; - type SourceFile = Lrc; + type SourceFile = Arc; type Span = Span; type Symbol = Symbol; } @@ -657,7 +657,7 @@ impl server::TokenStream for Rustc<'_, '_> { impl server::SourceFile for Rustc<'_, '_> { fn eq(&mut self, file1: &Self::SourceFile, file2: &Self::SourceFile) -> bool { - Lrc::ptr_eq(file1, file2) + Arc::ptr_eq(file1, file2) } fn path(&mut self, file: &Self::SourceFile) -> String { diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 776de1988ccdc..d50fe8c6a3e6b 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -9,7 +9,7 @@ macro_rules! declare_features { $(#[doc = $doc:tt])* (accepted, $feature:ident, $ver:expr, $issue:expr), )+) => { /// Formerly unstable features that have now been accepted (stabilized). - pub const ACCEPTED_LANG_FEATURES: &[Feature] = &[ + pub static ACCEPTED_LANG_FEATURES: &[Feature] = &[ $(Feature { name: sym::$feature, since: $ver, @@ -73,7 +73,7 @@ declare_features! ( /// Allows free and inherent `async fn`s, `async` blocks, and `.await` expressions. (accepted, async_await, "1.39.0", Some(50547)), /// Allows `async || body` closures. - (accepted, async_closure, "CURRENT_RUSTC_VERSION", Some(62290)), + (accepted, async_closure, "1.85.0", Some(62290)), /// Allows async functions to be declared, implemented, and used in traits. (accepted, async_fn_in_trait, "1.75.0", Some(91611)), /// Allows all literals in attribute lists and values of key-value pairs. @@ -176,7 +176,7 @@ declare_features! ( /// Allows using the `#[diagnostic]` attribute tool namespace (accepted, diagnostic_namespace, "1.78.0", Some(111996)), /// Controls errors in trait implementations. - (accepted, do_not_recommend, "CURRENT_RUSTC_VERSION", Some(51992)), + (accepted, do_not_recommend, "1.85.0", Some(51992)), /// Allows `#[doc(alias = "...")]`. (accepted, doc_alias, "1.48.0", Some(50146)), /// Allows `..` in tuple (struct) patterns. @@ -197,9 +197,6 @@ declare_features! ( (accepted, expr_fragment_specifier_2024, "1.83.0", Some(123742)), /// Allows arbitrary expressions in key-value attributes at parse time. (accepted, extended_key_value_attributes, "1.54.0", Some(78835)), - /// Allows using `efiapi`, `aapcs`, `sysv64` and `win64` as calling - /// convention for functions with varargs. - (accepted, extended_varargs_abi_support, "CURRENT_RUSTC_VERSION", Some(100189)), /// Allows resolving absolute paths as paths from other crates. (accepted, extern_absolute_paths, "1.30.0", Some(44660)), /// Allows `extern crate foo as bar;`. This puts `bar` into extern prelude. @@ -389,6 +386,8 @@ declare_features! ( (accepted, struct_variant, "1.0.0", None), /// Allows `#[target_feature(...)]`. (accepted, target_feature, "1.27.0", None), + /// Allows the use of `#[target_feature]` on safe functions. + (accepted, target_feature_11, "1.86.0", Some(69098)), /// Allows `fn main()` with return types which implements `Termination` (RFC 1937). (accepted, termination_trait, "1.26.0", Some(43301)), /// Allows `#[test]` functions where the return type implements `Termination` (RFC 1937). @@ -400,6 +399,9 @@ declare_features! ( /// Allows `#[track_caller]` to be used which provides /// accurate caller location reporting during panic (RFC 2091). (accepted, track_caller, "1.46.0", Some(47809)), + /// Allows dyn upcasting trait objects via supertraits. + /// Dyn upcasting is casting, e.g., `dyn Foo -> dyn Bar` where `Foo: Bar`. + (accepted, trait_upcasting, "1.86.0", Some(65991)), /// Allows #[repr(transparent)] on univariant enums (RFC 2645). (accepted, transparent_enums, "1.42.0", Some(60405)), /// Allows indexing tuples. diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 4112ae8098075..eb5fac96af270 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -19,6 +19,7 @@ const GATED_CFGS: &[GatedCfg] = &[ // (name in cfg, feature, function to check if the feature is enabled) (sym::overflow_checks, sym::cfg_overflow_checks, Features::cfg_overflow_checks), (sym::ub_checks, sym::cfg_ub_checks, Features::cfg_ub_checks), + (sym::contract_checks, sym::cfg_contract_checks, Features::cfg_contract_checks), (sym::target_thread_local, sym::cfg_target_thread_local, Features::cfg_target_thread_local), ( sym::target_has_atomic_equal_alignment, @@ -37,6 +38,7 @@ const GATED_CFGS: &[GatedCfg] = &[ (sym::sanitizer_cfi_normalize_integers, sym::cfg_sanitizer_cfi, Features::cfg_sanitizer_cfi), // this is consistent with naming of the compiler flag it's for (sym::fmt_debug, sym::fmt_debug, Features::fmt_debug), + (sym::emscripten_wasm_eh, sym::cfg_emscripten_wasm_eh, Features::cfg_emscripten_wasm_eh), ]; /// Find a gated cfg determined by the `pred`icate which is given the cfg's name. @@ -316,7 +318,7 @@ pub struct BuiltinAttribute { /// Attributes that have a special meaning to rustc or rustdoc. #[rustfmt::skip] -pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ +pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // ========================================================================== // Stable attributes: // ========================================================================== @@ -407,11 +409,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ crate_type, CrateLevel, template!(NameValueStr: "bin|lib|..."), DuplicatesOk, EncodeCrossCrate::No, ), - // crate_id is deprecated - ungated!( - crate_id, CrateLevel, template!(NameValueStr: "ignored"), FutureWarnFollowing, - EncodeCrossCrate::No, - ), // ABI, linking, symbols, and FFI ungated!( @@ -447,8 +444,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), // Entry point: - ungated!(start, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), - ungated!(no_start, CrateLevel, template!(Word), WarnFollowing, EncodeCrossCrate::No), ungated!(no_main, CrateLevel, template!(Word), WarnFollowing, EncodeCrossCrate::No), // Modules, prelude, and resolution: @@ -532,7 +527,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), // RFC 2412 gated!( - optimize, Normal, template!(List: "size|speed"), ErrorPreceding, + optimize, Normal, template!(List: "none|size|speed"), ErrorPreceding, EncodeCrossCrate::No, optimize_attribute, experimental!(optimize) ), @@ -571,7 +566,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // `#[coroutine]` attribute to be applied to closures to make them coroutines instead gated!( coroutine, Normal, template!(Word), ErrorFollowing, - EncodeCrossCrate::No, coroutines, experimental!(coroutines) + EncodeCrossCrate::No, coroutines, experimental!(coroutine) ), // RFC 3543 @@ -622,7 +617,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ EncodeCrossCrate::No, "allow_internal_unsafe side-steps the unsafe_code lint", ), rustc_attr!( - rustc_allowed_through_unstable_modules, Normal, template!(Word), + rustc_allowed_through_unstable_modules, Normal, template!(NameValueStr: "deprecation message"), WarnFollowing, EncodeCrossCrate::No, "rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \ through unstable paths" @@ -933,11 +928,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ "#[rustc_has_incoherent_inherent_impls] allows the addition of incoherent inherent impls for \ the given type by annotating all impl items with #[rustc_allow_incoherent_impl]." ), - rustc_attr!( - rustc_box, AttributeType::Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::No, - "#[rustc_box] allows creating boxes \ - and it is only intended to be used in `alloc`." - ), BuiltinAttribute { name: sym::rustc_diagnostic_item, @@ -1013,7 +1003,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), gated!( rustc_intrinsic, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, intrinsics, - "the `#[rustc_intrinsic]` attribute is used to declare intrinsics with function bodies", + "the `#[rustc_intrinsic]` attribute is used to declare intrinsics as function items", ), gated!( rustc_intrinsic_must_be_overridden, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, intrinsics, @@ -1023,6 +1013,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_no_mir_inline, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes, "#[rustc_no_mir_inline] prevents the MIR inliner from inlining a function while not affecting codegen" ), + rustc_attr!( + rustc_force_inline, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing, EncodeCrossCrate::Yes, + "#![rustc_force_inline] forces a free function to be inlined" + ), // ========================================================================== // Internal attributes, Testing: @@ -1143,7 +1137,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), rustc_attr!( TEST, rustc_dump_vtable, Normal, template!(Word), - WarnFollowing, EncodeCrossCrate::Yes + WarnFollowing, EncodeCrossCrate::No ), rustc_attr!( TEST, rustc_dummy, Normal, template!(Word /* doesn't matter*/), diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index 6db512ace1b4d..0b034a2ae1075 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -68,6 +68,16 @@ impl UnstableFeatures { /// If `krate` is [`Some`], then setting `RUSTC_BOOTSTRAP=krate` will enable the nightly /// features. Otherwise, only `RUSTC_BOOTSTRAP=1` will work. pub fn from_environment(krate: Option<&str>) -> Self { + Self::from_environment_value(krate, std::env::var("RUSTC_BOOTSTRAP")) + } + + /// Avoid unsafe `std::env::set_var()` by allowing tests to inject + /// `std::env::var("RUSTC_BOOTSTRAP")` with the `env_var_rustc_bootstrap` + /// arg. + fn from_environment_value( + krate: Option<&str>, + env_var_rustc_bootstrap: Result, + ) -> Self { // `true` if this is a feature-staged build, i.e., on the beta or stable channel. let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some_and(|s| s != "0"); @@ -75,7 +85,7 @@ impl UnstableFeatures { let is_unstable_crate = |var: &str| krate.is_some_and(|name| var.split(',').any(|new_krate| new_krate == name)); - let bootstrap = std::env::var("RUSTC_BOOTSTRAP").ok(); + let bootstrap = env_var_rustc_bootstrap.ok(); if let Some(val) = bootstrap.as_deref() { match val { val if val == "1" || is_unstable_crate(val) => return UnstableFeatures::Cheat, diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 388ed9d08fa67..2fb0c8e43448a 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -14,7 +14,7 @@ macro_rules! declare_features { $(#[doc = $doc:tt])* (removed, $feature:ident, $ver:expr, $issue:expr, $reason:expr), )+) => { /// Formerly unstable features that have now been removed. - pub const REMOVED_LANG_FEATURES: &[RemovedFeature] = &[ + pub static REMOVED_LANG_FEATURES: &[RemovedFeature] = &[ $(RemovedFeature { feature: Feature { name: sym::$feature, @@ -120,7 +120,7 @@ declare_features! ( /// Allows defining generators. (removed, generators, "1.21.0", Some(43122), Some("renamed to `coroutines`")), /// An extension to the `generic_associated_types` feature, allowing incomplete features. - (removed, generic_associated_types_extended, "CURRENT_RUSTC_VERSION", Some(95451), + (removed, generic_associated_types_extended, "1.85.0", Some(95451), Some( "feature needs overhaul and reimplementation pending \ better implied higher-ranked implied bounds support" @@ -163,7 +163,7 @@ declare_features! ( /// then removed. But there was no utility storing it separately, so now /// it's in this list. (removed, no_stack_check, "1.0.0", None, None), - /// Allows making `dyn Trait` well-formed even if `Trait` is not dyn-compatible (object safe). + /// Allows making `dyn Trait` well-formed even if `Trait` is not dyn compatible (object safe). /// Renamed to `dyn_compatible_for_dispatch`. (removed, object_safe_for_dispatch, "1.83.0", Some(43561), Some("renamed to `dyn_compatible_for_dispatch`")), @@ -220,8 +220,9 @@ declare_features! ( (removed, rustc_diagnostic_macros, "1.38.0", None, None), /// Allows identifying crates that contain sanitizer runtimes. (removed, sanitizer_runtime, "1.17.0", None, None), - (removed, simd, "1.0.0", Some(27731), - Some("removed in favor of `#[repr(simd)]`")), + (removed, simd, "1.0.0", Some(27731), Some("removed in favor of `#[repr(simd)]`")), + /// Allows using `#[start]` on a function indicating that it is the program entrypoint. + (removed, start, "1.0.0", Some(29633), Some("not portable enough and never RFC'd")), /// Allows `#[link(kind = "static-nobundle", ...)]`. (removed, static_nobundle, "1.16.0", Some(37403), Some(r#"subsumed by `#[link(kind = "static", modifiers = "-bundle", ...)]`"#)), diff --git a/compiler/rustc_feature/src/tests.rs b/compiler/rustc_feature/src/tests.rs index cc0e1f3120965..a5d589171d105 100644 --- a/compiler/rustc_feature/src/tests.rs +++ b/compiler/rustc_feature/src/tests.rs @@ -2,9 +2,11 @@ use super::UnstableFeatures; #[test] fn rustc_bootstrap_parsing() { - let is_bootstrap = |env, krate| { - std::env::set_var("RUSTC_BOOTSTRAP", env); - matches!(UnstableFeatures::from_environment(krate), UnstableFeatures::Cheat) + let is_bootstrap = |env: &str, krate: Option<&str>| { + matches!( + UnstableFeatures::from_environment_value(krate, Ok(env.to_string())), + UnstableFeatures::Cheat + ) }; assert!(is_bootstrap("1", None)); assert!(is_bootstrap("1", Some("x"))); @@ -22,9 +24,11 @@ fn rustc_bootstrap_parsing() { assert!(!is_bootstrap("0", None)); // `RUSTC_BOOTSTRAP=-1` is force-stable, no unstable features allowed. - let is_force_stable = |krate| { - std::env::set_var("RUSTC_BOOTSTRAP", "-1"); - matches!(UnstableFeatures::from_environment(krate), UnstableFeatures::Disallow) + let is_force_stable = |krate: Option<&str>| { + matches!( + UnstableFeatures::from_environment_value(krate, Ok("-1".to_string())), + UnstableFeatures::Disallow + ) }; assert!(is_force_stable(None)); // Does not support specifying any crate. diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index d40823d2ed627..6a317fc75a87f 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -104,7 +104,7 @@ macro_rules! declare_features { )+) => { /// Unstable language features that are being implemented or being /// considered for acceptance (stabilization) or removal. - pub const UNSTABLE_LANG_FEATURES: &[Feature] = &[ + pub static UNSTABLE_LANG_FEATURES: &[Feature] = &[ $(Feature { name: sym::$feature, since: $ver, @@ -202,6 +202,8 @@ declare_features! ( (internal, allow_internal_unstable, "1.0.0", None), /// Allows using anonymous lifetimes in argument-position impl-trait. (unstable, anonymous_lifetime_in_impl_trait, "1.63.0", None), + /// Allows access to the emscripten_wasm_eh config, used by panic_unwind and unwind + (internal, cfg_emscripten_wasm_eh, "1.86.0", None), /// Allows identifying the `compiler_builtins` crate. (internal, compiler_builtins, "1.13.0", None), /// Allows writing custom MIR @@ -270,7 +272,7 @@ declare_features! ( (unstable, doc_notable_trait, "1.52.0", Some(45040)), /// Allows using the `may_dangle` attribute (RFC 1327). (unstable, dropck_eyepatch, "1.10.0", Some(34761)), - /// Allows making `dyn Trait` well-formed even if `Trait` is not dyn-compatible[^1]. + /// Allows making `dyn Trait` well-formed even if `Trait` is not dyn compatible[^1]. /// In that case, `dyn Trait: Trait` does not hold. Moreover, coercions and /// casts in safe Rust to `dyn Trait` for such a `Trait` is also forbidden. /// @@ -298,8 +300,6 @@ declare_features! ( (internal, rustdoc_internals, "1.58.0", Some(90418)), /// Allows using the `rustdoc::missing_doc_code_examples` lint (unstable, rustdoc_missing_doc_code_examples, "1.31.0", Some(101730)), - /// Allows using `#[start]` on a function indicating that it is the program entrypoint. - (unstable, start, "1.0.0", Some(29633)), /// Allows using `#[structural_match]` which indicates that a type is structurally matchable. /// FIXME: Subsumed by trait `StructuralPartialEq`, cannot move to removed until a library /// feature with the same name exists. @@ -331,7 +331,7 @@ declare_features! ( (unstable, hexagon_target_feature, "1.27.0", Some(44839)), (unstable, lahfsahf_target_feature, "1.78.0", Some(44839)), (unstable, loongarch_target_feature, "1.73.0", Some(44839)), - (unstable, m68k_target_feature, "CURRENT_RUSTC_VERSION", Some(134328)), + (unstable, m68k_target_feature, "1.85.0", Some(134328)), (unstable, mips_target_feature, "1.27.0", Some(44839)), (unstable, powerpc_target_feature, "1.27.0", Some(44839)), (unstable, prfchw_target_feature, "1.78.0", Some(44839)), @@ -342,7 +342,7 @@ declare_features! ( (unstable, sse4a_target_feature, "1.27.0", Some(44839)), (unstable, tbm_target_feature, "1.27.0", Some(44839)), (unstable, wasm_target_feature, "1.30.0", Some(44839)), - (unstable, x87_target_feature, "CURRENT_RUSTC_VERSION", Some(44839)), + (unstable, x87_target_feature, "1.85.0", Some(44839)), // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! // Features are listed in alphabetical order. Tidy will fail if you don't keep it this way. // !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! @@ -359,6 +359,8 @@ declare_features! ( (unstable, abi_avr_interrupt, "1.45.0", Some(69664)), /// Allows `extern "C-cmse-nonsecure-call" fn()`. (unstable, abi_c_cmse_nonsecure_call, "1.51.0", Some(81391)), + /// Allows `extern "gpu-kernel" fn()`. + (unstable, abi_gpu_kernel, "1.86.0", Some(135467)), /// Allows `extern "msp430-interrupt" fn()`. (unstable, abi_msp430_interrupt, "1.16.0", Some(38487)), /// Allows `extern "ptx-*" fn()`. @@ -378,11 +380,11 @@ declare_features! ( /// Enables experimental inline assembly support for additional architectures. (unstable, asm_experimental_arch, "1.58.0", Some(93335)), /// Enables experimental register support in inline assembly. - (unstable, asm_experimental_reg, "CURRENT_RUSTC_VERSION", Some(133416)), + (unstable, asm_experimental_reg, "1.85.0", Some(133416)), /// Allows using `label` operands in inline assembly. (unstable, asm_goto, "1.78.0", Some(119364)), /// Allows using `label` operands in inline assembly together with output operands. - (unstable, asm_goto_with_outputs, "CURRENT_RUSTC_VERSION", Some(119364)), + (unstable, asm_goto_with_outputs, "1.85.0", Some(119364)), /// Allows the `may_unwind` option in inline assembly. (unstable, asm_unwind, "1.58.0", Some(93334)), /// Allows users to enforce equality of associated constants `TraitImpl`. @@ -390,17 +392,19 @@ declare_features! ( /// Allows associated type defaults. (unstable, associated_type_defaults, "1.2.0", Some(29661)), /// Allows async functions to be called from `dyn Trait`. - (incomplete, async_fn_in_dyn_trait, "CURRENT_RUSTC_VERSION", Some(133119)), + (incomplete, async_fn_in_dyn_trait, "1.85.0", Some(133119)), /// Allows `#[track_caller]` on async functions. (unstable, async_fn_track_caller, "1.73.0", Some(110011)), /// Allows `for await` loops. (unstable, async_for_loop, "1.77.0", Some(118898)), /// Allows `async` trait bound modifier. - (unstable, async_trait_bounds, "CURRENT_RUSTC_VERSION", Some(62290)), + (unstable, async_trait_bounds, "1.85.0", Some(62290)), /// Allows using C-variadics. (unstable, c_variadic, "1.34.0", Some(44930)), /// Allows the use of `#[cfg()]`. (unstable, cfg_boolean_literals, "1.83.0", Some(131204)), + /// Allows the use of `#[cfg(contract_checks)` to check if contract checks are enabled. + (unstable, cfg_contract_checks, "1.86.0", Some(128044)), /// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour. (unstable, cfg_overflow_checks, "1.71.0", Some(111466)), /// Provides the relocation model information as cfg entry @@ -434,7 +438,7 @@ declare_features! ( /// Allows `const || {}` closures in const contexts. (incomplete, const_closures, "1.68.0", Some(106003)), /// Allows using `~const Destruct` bounds and calling drop impls in const contexts. - (unstable, const_destruct, "CURRENT_RUSTC_VERSION", Some(133214)), + (unstable, const_destruct, "1.85.0", Some(133214)), /// Allows `for _ in _` loops in const contexts. (unstable, const_for, "1.56.0", Some(87575)), /// Be more precise when looking for live drops in a const context. @@ -443,6 +447,10 @@ declare_features! ( (unstable, const_trait_impl, "1.42.0", Some(67792)), /// Allows the `?` operator in const contexts. (unstable, const_try, "1.56.0", Some(74935)), + /// Allows use of contracts attributes. + (incomplete, contracts, "1.86.0", Some(128044)), + /// Allows access to internal machinery used to implement contracts. + (internal, contracts_internals, "1.86.0", Some(128044)), /// Allows coroutines to be cloned. (unstable, coroutine_clone, "1.65.0", Some(95360)), /// Allows defining coroutines. @@ -458,7 +466,7 @@ declare_features! ( (unstable, decl_macro, "1.17.0", Some(39412)), /// Allows the use of default values on struct definitions and the construction of struct /// literals with the functional update syntax without a base. - (unstable, default_field_values, "CURRENT_RUSTC_VERSION", Some(132162)), + (unstable, default_field_values, "1.85.0", Some(132162)), /// Allows using `#[deprecated_safe]` to deprecate the safeness of a function or trait (unstable, deprecated_safe, "1.61.0", Some(94978)), /// Allows having using `suggestion` in the `#[deprecated]` attribute. @@ -479,6 +487,11 @@ declare_features! ( (unstable, exhaustive_patterns, "1.13.0", Some(51085)), /// Allows explicit tail calls via `become` expression. (incomplete, explicit_tail_calls, "1.72.0", Some(112788)), + /// Allows using `aapcs`, `efiapi`, `sysv64` and `win64` as calling conventions + /// for functions with varargs. + (unstable, extended_varargs_abi_support, "1.65.0", Some(100189)), + /// Allows using `system` as a calling convention with varargs. + (unstable, extern_system_varargs, "1.86.0", Some(136946)), /// Allows defining `extern type`s. (unstable, extern_types, "1.23.0", Some(43467)), /// Allow using 128-bit (quad precision) floating point numbers. @@ -505,10 +518,12 @@ declare_features! ( (incomplete, generic_const_exprs, "1.56.0", Some(76560)), /// Allows generic parameters and where-clauses on free & associated const items. (incomplete, generic_const_items, "1.73.0", Some(113521)), + /// Allows any generic constants being used as pattern type range ends + (incomplete, generic_pattern_types, "1.86.0", Some(136574)), /// Allows registering static items globally, possibly across crates, to iterate over at runtime. (unstable, global_registration, "1.80.0", Some(125119)), /// Allows using guards in patterns. - (incomplete, guard_patterns, "CURRENT_RUSTC_VERSION", Some(129967)), + (incomplete, guard_patterns, "1.85.0", Some(129967)), /// Allows using `..=X` as a patterns in slices. (unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)), /// Allows `if let` guard in match arms. @@ -519,12 +534,16 @@ declare_features! ( (unstable, impl_trait_in_bindings, "1.64.0", Some(63065)), /// Allows `impl Trait` as output type in `Fn` traits in return position of functions. (unstable, impl_trait_in_fn_trait_return, "1.64.0", Some(99697)), + /// Allows `use` associated functions from traits. + (unstable, import_trait_associated_functions, "1.86.0", Some(134691)), /// Allows associated types in inherent impls. (incomplete, inherent_associated_types, "1.52.0", Some(8995)), /// Allow anonymous constants from an inline `const` block in pattern position (unstable, inline_const_pat, "1.58.0", Some(76001)), /// Allows using `pointer` and `reference` in intra-doc links (unstable, intra_doc_pointers, "1.51.0", Some(80896)), + // Allows using the `kl` and `widekl` target features and the associated intrinsics + (unstable, keylocker_x86, "1.86.0", Some(134813)), // Allows setting the threshold for the `large_assignments` lint. (unstable, large_assignments, "1.52.0", Some(83518)), /// Allow to have type alias types for inter-crate use. @@ -564,6 +583,8 @@ declare_features! ( (unstable, never_type, "1.13.0", Some(35121)), /// Allows diverging expressions to fall back to `!` rather than `()`. (unstable, never_type_fallback, "1.41.0", Some(65992)), + /// Switch `..` syntax to use the new (`Copy + IntoIterator`) range types. + (unstable, new_range, "1.86.0", Some(123741)), /// Allows `#![no_core]`. (unstable, no_core, "1.3.0", Some(29639)), /// Allows the use of `no_sanitize` attribute. @@ -614,15 +635,12 @@ declare_features! ( (unstable, strict_provenance_lints, "1.61.0", Some(130351)), /// Allows string patterns to dereference values to match them. (unstable, string_deref_patterns, "1.67.0", Some(87121)), - /// Allows the use of `#[target_feature]` on safe functions. - (unstable, target_feature_11, "1.45.0", Some(69098)), + /// Allows subtrait items to shadow supertrait items. + (unstable, supertrait_item_shadowing, "1.86.0", Some(89151)), /// Allows using `#[thread_local]` on `static` items. (unstable, thread_local, "1.0.0", Some(29594)), /// Allows defining `trait X = A + B;` alias items. (unstable, trait_alias, "1.24.0", Some(41517)), - /// Allows dyn upcasting trait objects via supertraits. - /// Dyn upcasting is casting, e.g., `dyn Foo -> dyn Bar` where `Foo: Bar`. - (unstable, trait_upcasting, "1.56.0", Some(65991)), /// Allows for transmuting between arrays with sizes that contain generic consts. (unstable, transmute_generic_consts, "1.70.0", Some(109929)), /// Allows #[repr(transparent)] on unions (RFC 2645). @@ -637,9 +655,9 @@ declare_features! ( /// not changed from prior instances of the same struct (RFC #2528) (unstable, type_changing_struct_update, "1.58.0", Some(86555)), /// Allows using `unsafe<'a> &'a T` unsafe binder types. - (incomplete, unsafe_binders, "CURRENT_RUSTC_VERSION", Some(130516)), + (incomplete, unsafe_binders, "1.85.0", Some(130516)), /// Allows declaring fields `unsafe`. - (incomplete, unsafe_fields, "CURRENT_RUSTC_VERSION", Some(132922)), + (incomplete, unsafe_fields, "1.85.0", Some(132922)), /// Allows const generic parameters to be defined with types that /// are not `Sized`, e.g. `fn foo() {`. (incomplete, unsized_const_params, "1.82.0", Some(95174)), @@ -716,7 +734,8 @@ impl Features { /// Some features are not allowed to be used together at the same time, if /// the two are present, produce an error. -/// -/// Currently empty, but we will probably need this again in the future, -/// so let's keep it in for now. -pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] = &[]; +pub const INCOMPATIBLE_FEATURES: &[(Symbol, Symbol)] = &[ + // Experimental match ergonomics rulesets are incompatible with each other, to simplify the + // boolean logic required to tell which typing rules to use. + (sym::ref_pat_eat_one_layer_2024, sym::ref_pat_eat_one_layer_2024_structural), +]; diff --git a/compiler/rustc_graphviz/src/tests.rs b/compiler/rustc_graphviz/src/tests.rs index 712620a2000c7..bf2f43fde2ee9 100644 --- a/compiler/rustc_graphviz/src/tests.rs +++ b/compiler/rustc_graphviz/src/tests.rs @@ -360,12 +360,16 @@ fn left_aligned_text() { let mut writer = Vec::new(); - let g = LabelledGraphWithEscStrs::new("syntax_tree", labels, vec![ - edge(0, 1, "then", Style::None), - edge(0, 2, "else", Style::None), - edge(1, 3, ";", Style::None), - edge(2, 3, ";", Style::None), - ]); + let g = LabelledGraphWithEscStrs::new( + "syntax_tree", + labels, + vec![ + edge(0, 1, "then", Style::None), + edge(0, 2, "else", Style::None), + edge(1, 3, ";", Style::None), + edge(2, 3, ";", Style::None), + ], + ); render(&g, &mut writer).unwrap(); let mut r = String::new(); diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 705de389e072e..7e3a8561da08b 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -794,7 +794,7 @@ impl Res { /// Always returns `true` if `self` is `Res::Err` pub fn matches_ns(&self, ns: Namespace) -> bool { - self.ns().map_or(true, |actual_ns| actual_ns == ns) + self.ns().is_none_or(|actual_ns| actual_ns == ns) } /// Returns whether such a resolved path can occur in a tuple struct/variant pattern diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 7a3cde4bc2fb7..eafc60f9d72f7 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -7,7 +7,7 @@ use rustc_ast::token::CommentKind; use rustc_ast::util::parser::{AssocOp, ExprPrecedence}; use rustc_ast::{ self as ast, AttrId, AttrStyle, DelimArgs, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, - IntTy, Label, LitKind, MetaItemInner, MetaItemLit, TraitObjectSyntax, UintTy, + IntTy, Label, LitIntType, LitKind, MetaItemInner, MetaItemLit, TraitObjectSyntax, UintTy, }; pub use rustc_ast::{ BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity, ByRef, CaptureBy, @@ -15,6 +15,7 @@ pub use rustc_ast::{ }; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; +use rustc_data_structures::tagged_ptr::TaggedRef; use rustc_index::IndexVec; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::def_id::LocalDefId; @@ -30,10 +31,11 @@ use crate::LangItem; use crate::def::{CtorKind, DefKind, Res}; use crate::def_id::{DefId, LocalDefIdMap}; pub(crate) use crate::hir_id::{HirId, ItemLocalId, ItemLocalMap, OwnerId}; -use crate::intravisit::FnKind; +use crate::intravisit::{FnKind, VisitorExt}; #[derive(Debug, Copy, Clone, HashStable_Generic)] pub struct Lifetime { + #[stable_hasher(ignore)] pub hir_id: HirId, /// Either "`'a`", referring to a named lifetime definition, @@ -204,7 +206,7 @@ pub type UsePath<'hir> = Path<'hir, SmallVec<[Res; 3]>>; impl Path<'_> { pub fn is_global(&self) -> bool { - !self.segments.is_empty() && self.segments[0].ident.name == kw::PathRoot + self.segments.first().is_some_and(|segment| segment.ident.name == kw::PathRoot) } } @@ -214,6 +216,7 @@ impl Path<'_> { pub struct PathSegment<'hir> { /// The identifier portion of this path segment. pub ident: Ident, + #[stable_hasher(ignore)] pub hir_id: HirId, pub res: Res, @@ -261,14 +264,58 @@ impl<'hir> PathSegment<'hir> { /// So, `ConstArg` (specifically, [`ConstArgKind`]) distinguishes between const args /// that are [just paths](ConstArgKind::Path) (currently just bare const params) /// versus const args that are literals or have arbitrary computations (e.g., `{ 1 + 3 }`). +/// +/// The `Unambig` generic parameter represents whether the position this const is from is +/// unambiguously a const or ambiguous as to whether it is a type or a const. When in an +/// ambiguous context the parameter is instantiated with an uninhabited type making the +/// [`ConstArgKind::Infer`] variant unusable and [`GenericArg::Infer`] is used instead. #[derive(Clone, Copy, Debug, HashStable_Generic)] -pub struct ConstArg<'hir> { +#[repr(C)] +pub struct ConstArg<'hir, Unambig = ()> { #[stable_hasher(ignore)] pub hir_id: HirId, - pub kind: ConstArgKind<'hir>, + pub kind: ConstArgKind<'hir, Unambig>, +} + +impl<'hir> ConstArg<'hir, AmbigArg> { + /// Converts a `ConstArg` in an ambiguous position to one in an unambiguous position. + /// + /// Functions accepting an unambiguous consts may expect the [`ConstArgKind::Infer`] variant + /// to be used. Care should be taken to separately handle infer consts when calling this + /// function as it cannot be handled by downstream code making use of the returned const. + /// + /// In practice this may mean overriding the [`Visitor::visit_infer`][visit_infer] method on hir visitors, or + /// specifically matching on [`GenericArg::Infer`] when handling generic arguments. + /// + /// [visit_infer]: [rustc_hir::intravisit::Visitor::visit_infer] + pub fn as_unambig_ct(&self) -> &ConstArg<'hir> { + // SAFETY: `ConstArg` is `repr(C)` and `ConstArgKind` is marked `repr(u8)` so that the + // layout is the same across different ZST type arguments. + let ptr = self as *const ConstArg<'hir, AmbigArg> as *const ConstArg<'hir, ()>; + unsafe { &*ptr } + } } impl<'hir> ConstArg<'hir> { + /// Converts a `ConstArg` in an unambigous position to one in an ambiguous position. This is + /// fallible as the [`ConstArgKind::Infer`] variant is not present in ambiguous positions. + /// + /// Functions accepting ambiguous consts will not handle the [`ConstArgKind::Infer`] variant, if + /// infer consts are relevant to you then care should be taken to handle them separately. + pub fn try_as_ambig_ct(&self) -> Option<&ConstArg<'hir, AmbigArg>> { + if let ConstArgKind::Infer(_, ()) = self.kind { + return None; + } + + // SAFETY: `ConstArg` is `repr(C)` and `ConstArgKind` is marked `repr(u8)` so that the layout is + // the same across different ZST type arguments. We also asserted that the `self` is + // not a `ConstArgKind::Infer` so there is no risk of transmuting a `()` to `AmbigArg`. + let ptr = self as *const ConstArg<'hir> as *const ConstArg<'hir, AmbigArg>; + Some(unsafe { &*ptr }) + } +} + +impl<'hir, Unambig> ConstArg<'hir, Unambig> { pub fn anon_const_hir_id(&self) -> Option { match self.kind { ConstArgKind::Anon(ac) => Some(ac.hir_id), @@ -280,14 +327,15 @@ impl<'hir> ConstArg<'hir> { match self.kind { ConstArgKind::Path(path) => path.span(), ConstArgKind::Anon(anon) => anon.span, - ConstArgKind::Infer(span) => span, + ConstArgKind::Infer(span, _) => span, } } } /// See [`ConstArg`]. #[derive(Clone, Copy, Debug, HashStable_Generic)] -pub enum ConstArgKind<'hir> { +#[repr(u8, C)] +pub enum ConstArgKind<'hir, Unambig = ()> { /// **Note:** Currently this is only used for bare const params /// (`N` where `fn foo(...)`), /// not paths to any const (`N` where `const N: usize = ...`). @@ -295,34 +343,38 @@ pub enum ConstArgKind<'hir> { /// However, in the future, we'll be using it for all of those. Path(QPath<'hir>), Anon(&'hir AnonConst), - /// **Note:** Not all inferred consts are represented as - /// `ConstArgKind::Infer`. In cases where it is ambiguous whether - /// a generic arg is a type or a const, inference variables are - /// represented as `GenericArg::Infer` instead. - Infer(Span), + /// This variant is not always used to represent inference consts, sometimes + /// [`GenericArg::Infer`] is used instead. + Infer(Span, Unambig), } #[derive(Clone, Copy, Debug, HashStable_Generic)] pub struct InferArg { + #[stable_hasher(ignore)] pub hir_id: HirId, pub span: Span, } impl InferArg { pub fn to_ty(&self) -> Ty<'static> { - Ty { kind: TyKind::Infer, span: self.span, hir_id: self.hir_id } + Ty { kind: TyKind::Infer(()), span: self.span, hir_id: self.hir_id } } } #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum GenericArg<'hir> { Lifetime(&'hir Lifetime), - Type(&'hir Ty<'hir>), - Const(&'hir ConstArg<'hir>), - /// **Note:** Inference variables are only represented as - /// `GenericArg::Infer` in cases where it is ambiguous whether - /// a generic arg is a type or a const. Otherwise, inference variables - /// are represented as `TyKind::Infer` or `ConstArgKind::Infer`. + Type(&'hir Ty<'hir, AmbigArg>), + Const(&'hir ConstArg<'hir, AmbigArg>), + /// Inference variables in [`GenericArg`] are always represnted by + /// `GenericArg::Infer` instead of the `Infer` variants on [`TyKind`] and + /// [`ConstArgKind`] as it is not clear until hir ty lowering whether a + /// `_` argument is a type or const argument. + /// + /// However, some builtin types' generic arguments are represented by [`TyKind`] + /// without a [`GenericArg`], instead directly storing a [`Ty`] or [`ConstArg`]. In + /// such cases they *are* represented by the `Infer` variants on [`TyKind`] and + /// [`ConstArgKind`] as it is not ambiguous whether the argument is a type or const. Infer(InferArg), } @@ -350,7 +402,7 @@ impl GenericArg<'_> { GenericArg::Lifetime(_) => "lifetime", GenericArg::Type(_) => "type", GenericArg::Const(_) => "constant", - GenericArg::Infer(_) => "inferred", + GenericArg::Infer(_) => "placeholder", } } @@ -592,6 +644,7 @@ pub enum GenericParamKind<'hir> { #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct GenericParam<'hir> { + #[stable_hasher(ignore)] pub hir_id: HirId, pub def_id: LocalDefId, pub name: ParamName, @@ -760,11 +813,8 @@ impl<'hir> Generics<'hir> { && let [.., segment] = trait_ref.path.segments && let Some(ret_ty) = segment.args().paren_sugar_output() && let ret_ty = ret_ty.peel_refs() - && let TyKind::TraitObject( - _, - _, - TraitObjectSyntax::Dyn | TraitObjectSyntax::DynStar, - ) = ret_ty.kind + && let TyKind::TraitObject(_, tagged_ptr) = ret_ty.kind + && let TraitObjectSyntax::Dyn | TraitObjectSyntax::DynStar = tagged_ptr.tag() && ret_ty.span.can_be_used_for_suggestions() { Some(ret_ty.span) @@ -850,6 +900,7 @@ impl<'hir> Generics<'hir> { /// A single predicate in a where-clause. #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct WherePredicate<'hir> { + #[stable_hasher(ignore)] pub hir_id: HirId, pub span: Span, pub kind: &'hir WherePredicateKind<'hir>, @@ -1010,10 +1061,7 @@ impl Attribute { pub fn value_lit(&self) -> Option<&MetaItemLit> { match &self.kind { - AttrKind::Normal(n) => match n.as_ref() { - AttrItem { args: AttrArgs::Eq { expr, .. }, .. } => Some(expr), - _ => None, - }, + AttrKind::Normal(box AttrItem { args: AttrArgs::Eq { expr, .. }, .. }) => Some(expr), _ => None, } } @@ -1026,12 +1074,9 @@ impl AttributeExt for Attribute { fn meta_item_list(&self) -> Option> { match &self.kind { - AttrKind::Normal(n) => match n.as_ref() { - AttrItem { args: AttrArgs::Delimited(d), .. } => { - ast::MetaItemKind::list_from_tokens(d.tokens.clone()) - } - _ => None, - }, + AttrKind::Normal(box AttrItem { args: AttrArgs::Delimited(d), .. }) => { + ast::MetaItemKind::list_from_tokens(d.tokens.clone()) + } _ => None, } } @@ -1047,23 +1092,16 @@ impl AttributeExt for Attribute { /// For a single-segment attribute, returns its name; otherwise, returns `None`. fn ident(&self) -> Option { match &self.kind { - AttrKind::Normal(n) => { - if let [ident] = n.path.segments.as_ref() { - Some(*ident) - } else { - None - } - } - AttrKind::DocComment(..) => None, + AttrKind::Normal(box AttrItem { + path: AttrPath { segments: box [ident], .. }, .. + }) => Some(*ident), + _ => None, } } fn path_matches(&self, name: &[Symbol]) -> bool { match &self.kind { - AttrKind::Normal(n) => { - n.path.segments.len() == name.len() - && n.path.segments.iter().zip(name).all(|(s, n)| s.name == *n) - } + AttrKind::Normal(n) => n.path.segments.iter().map(|segment| &segment.name).eq(name), AttrKind::DocComment(..) => false, } } @@ -1077,12 +1115,7 @@ impl AttributeExt for Attribute { } fn is_word(&self) -> bool { - match &self.kind { - AttrKind::Normal(n) => { - matches!(n.args, AttrArgs::Empty) - } - AttrKind::DocComment(..) => false, - } + matches!(self.kind, AttrKind::Normal(box AttrItem { args: AttrArgs::Empty, .. })) } fn ident_path(&self) -> Option> { @@ -1234,13 +1267,13 @@ impl fmt::Debug for OwnerNodes<'_> { .field("node", &self.nodes[ItemLocalId::ZERO]) .field( "parents", - &self - .nodes - .iter_enumerated() - .map(|(id, parented_node)| { - debug_fn(move |f| write!(f, "({id:?}, {:?})", parented_node.parent)) - }) - .collect::>(), + &fmt::from_fn(|f| { + f.debug_list() + .entries(self.nodes.iter_enumerated().map(|(id, parented_node)| { + fmt::from_fn(move |f| write!(f, "({id:?}, {:?})", parented_node.parent)) + })) + .finish() + }), ) .field("bodies", &self.bodies) .field("opt_hash_including_bodies", &self.opt_hash_including_bodies) @@ -1367,6 +1400,14 @@ impl<'hir> Block<'hir> { } } +#[derive(Debug, Clone, Copy, HashStable_Generic)] +pub struct TyPat<'hir> { + #[stable_hasher(ignore)] + pub hir_id: HirId, + pub kind: TyPatKind<'hir>, + pub span: Span, +} + #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct Pat<'hir> { #[stable_hasher(ignore)] @@ -1386,8 +1427,8 @@ impl<'hir> Pat<'hir> { use PatKind::*; match self.kind { - Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => true, - Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_short_(it), + Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Err(_) => true, + Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_short_(it), Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)), TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().all(|p| p.walk_short_(it)), Slice(before, slice, after) => { @@ -1413,8 +1454,8 @@ impl<'hir> Pat<'hir> { use PatKind::*; match self.kind { - Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => {} - Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_(it), + Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Err(_) => {} + Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_(it), Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)), TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)), Slice(before, slice, after) => { @@ -1519,6 +1560,36 @@ impl fmt::Debug for DotDotPos { } } +#[derive(Debug, Clone, Copy, HashStable_Generic)] +pub struct PatExpr<'hir> { + #[stable_hasher(ignore)] + pub hir_id: HirId, + pub span: Span, + pub kind: PatExprKind<'hir>, +} + +#[derive(Debug, Clone, Copy, HashStable_Generic)] +pub enum PatExprKind<'hir> { + Lit { + lit: &'hir Lit, + // FIXME: move this into `Lit` and handle negated literal expressions + // once instead of matching on unop neg expressions everywhere. + negated: bool, + }, + ConstBlock(ConstBlock), + /// A path pattern for a unit struct/variant or a (maybe-associated) constant. + Path(QPath<'hir>), +} + +#[derive(Debug, Clone, Copy, HashStable_Generic)] +pub enum TyPatKind<'hir> { + /// A range pattern (e.g., `1..=2` or `1..2`). + Range(Option<&'hir ConstArg<'hir>>, Option<&'hir ConstArg<'hir>>, RangeEnd), + + /// A placeholder for a pattern that wasn't well formed in some way. + Err(ErrorGuaranteed), +} + #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum PatKind<'hir> { /// Represents a wildcard pattern (i.e., `_`). @@ -1546,9 +1617,6 @@ pub enum PatKind<'hir> { /// A never pattern `!`. Never, - /// A path pattern for a unit struct/variant or a (maybe-associated) constant. - Path(QPath<'hir>), - /// A tuple pattern (e.g., `(a, b)`). /// If the `..` pattern fragment is present, then `Option` denotes its position. /// `0 <= position <= subpats.len()` @@ -1563,11 +1631,14 @@ pub enum PatKind<'hir> { /// A reference pattern (e.g., `&mut (a, b)`). Ref(&'hir Pat<'hir>, Mutability), - /// A literal. - Lit(&'hir Expr<'hir>), + /// A literal, const block or path. + Expr(&'hir PatExpr<'hir>), + + /// A guard pattern (e.g., `x if guard(x)`). + Guard(&'hir Pat<'hir>, &'hir Expr<'hir>), /// A range pattern (e.g., `1..=2` or `1..2`). - Range(Option<&'hir Expr<'hir>>, Option<&'hir Expr<'hir>>, RangeEnd), + Range(Option<&'hir PatExpr<'hir>>, Option<&'hir PatExpr<'hir>>, RangeEnd), /// A slice pattern, `[before_0, ..., before_n, (slice, after_0, ..., after_n)?]`. /// @@ -1587,6 +1658,7 @@ pub enum PatKind<'hir> { /// A statement. #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct Stmt<'hir> { + #[stable_hasher(ignore)] pub hir_id: HirId, pub kind: StmtKind<'hir>, pub span: Span, @@ -1618,6 +1690,7 @@ pub struct LetStmt<'hir> { pub init: Option<&'hir Expr<'hir>>, /// Else block for a `let...else` binding. pub els: Option<&'hir Block<'hir>>, + #[stable_hasher(ignore)] pub hir_id: HirId, pub span: Span, /// Can be `ForLoopDesugar` if the `let` statement is part of a `for` loop @@ -1899,7 +1972,7 @@ impl fmt::Display for ConstContext { } // NOTE: `IntoDiagArg` impl for `ConstContext` lives in `rustc_errors` -// due to a cyclical dependency between hir that crate. +// due to a cyclical dependency between hir and that crate. /// A literal. pub type Lit = Spanned; @@ -1914,6 +1987,7 @@ pub type Lit = Spanned; /// `const N: usize = { ... }` with `tcx.hir().opt_const_param_default_param_def_id(..)` #[derive(Copy, Clone, Debug, HashStable_Generic)] pub struct AnonConst { + #[stable_hasher(ignore)] pub hir_id: HirId, pub def_id: LocalDefId, pub body: BodyId, @@ -1923,6 +1997,7 @@ pub struct AnonConst { /// An inline constant expression `const { something }`. #[derive(Copy, Clone, Debug, HashStable_Generic)] pub struct ConstBlock { + #[stable_hasher(ignore)] pub hir_id: HirId, pub def_id: LocalDefId, pub body: BodyId, @@ -1938,6 +2013,7 @@ pub struct ConstBlock { /// [rust lang reference]: https://doc.rust-lang.org/reference/expressions.html #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct Expr<'hir> { + #[stable_hasher(ignore)] pub hir_id: HirId, pub kind: ExprKind<'hir>, pub span: Span, @@ -2071,6 +2147,18 @@ impl Expr<'_> { } } + /// Check if expression is an integer literal that can be used + /// where `usize` is expected. + pub fn is_size_lit(&self) -> bool { + matches!( + self.kind, + ExprKind::Lit(Lit { + node: LitKind::Int(_, LitIntType::Unsuffixed | LitIntType::Unsigned(UintTy::Usize)), + .. + }) + ) + } + /// If `Self.kind` is `ExprKind::DropTemps(expr)`, drill down until we get a non-`DropTemps` /// `Expr`. This is used in suggestions to ignore this `ExprKind` as it is semantically /// silent, only signaling the ownership system. By doing this, suggestions that check the @@ -2224,6 +2312,18 @@ impl Expr<'_> { [val2], StructTailExpr::None, ), + ) + | ( + ExprKind::Struct( + QPath::LangItem(LangItem::RangeFromCopy, _), + [val1], + StructTailExpr::None, + ), + ExprKind::Struct( + QPath::LangItem(LangItem::RangeFromCopy, _), + [val2], + StructTailExpr::None, + ), ) => val1.expr.equivalent_for_indexing(val2.expr), ( ExprKind::Struct( @@ -2236,6 +2336,30 @@ impl Expr<'_> { [val2, val4], StructTailExpr::None, ), + ) + | ( + ExprKind::Struct( + QPath::LangItem(LangItem::RangeCopy, _), + [val1, val3], + StructTailExpr::None, + ), + ExprKind::Struct( + QPath::LangItem(LangItem::RangeCopy, _), + [val2, val4], + StructTailExpr::None, + ), + ) + | ( + ExprKind::Struct( + QPath::LangItem(LangItem::RangeInclusiveCopy, _), + [val1, val3], + StructTailExpr::None, + ), + ExprKind::Struct( + QPath::LangItem(LangItem::RangeInclusiveCopy, _), + [val2, val4], + StructTailExpr::None, + ), ) => { val1.expr.equivalent_for_indexing(val2.expr) && val3.expr.equivalent_for_indexing(val4.expr) @@ -2265,7 +2389,10 @@ pub fn is_range_literal(expr: &Expr<'_>) -> bool { | LangItem::RangeTo | LangItem::RangeFrom | LangItem::RangeFull - | LangItem::RangeToInclusive, + | LangItem::RangeToInclusive + | LangItem::RangeCopy + | LangItem::RangeFromCopy + | LangItem::RangeInclusiveCopy, .. ) ), @@ -2509,6 +2636,8 @@ pub enum LocalSource { /// A desugared `expr = expr`, where the LHS is a tuple, struct, array or underscore expression. /// The span is that of the `=` sign. AssignDesugar(Span), + /// A contract `#[ensures(..)]` attribute injects a let binding for the check that runs at point of return. + Contract, } /// Hints at the original code for a `match _ { .. }`. @@ -2804,6 +2933,7 @@ pub enum ImplItemKind<'hir> { /// * the `f(..): Bound` in `Trait` (feature `return_type_notation`) #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct AssocItemConstraint<'hir> { + #[stable_hasher(ignore)] pub hir_id: HirId, pub ident: Ident, pub gen_args: &'hir GenericArgs<'hir>, @@ -2870,14 +3000,84 @@ impl<'hir> AssocItemConstraintKind<'hir> { } } +/// An uninhabited enum used to make `Infer` variants on [`Ty`] and [`ConstArg`] be +/// unreachable. Zero-Variant enums are guaranteed to have the same layout as the never +/// type. +#[derive(Debug, Clone, Copy, HashStable_Generic)] +pub enum AmbigArg {} + #[derive(Debug, Clone, Copy, HashStable_Generic)] -pub struct Ty<'hir> { +#[repr(C)] +/// Represents a type in the `HIR`. +/// +/// The `Unambig` generic parameter represents whether the position this type is from is +/// unambiguously a type or ambiguous as to whether it is a type or a const. When in an +/// ambiguous context the parameter is instantiated with an uninhabited type making the +/// [`TyKind::Infer`] variant unusable and [`GenericArg::Infer`] is used instead. +pub struct Ty<'hir, Unambig = ()> { + #[stable_hasher(ignore)] pub hir_id: HirId, - pub kind: TyKind<'hir>, pub span: Span, + pub kind: TyKind<'hir, Unambig>, +} + +impl<'hir> Ty<'hir, AmbigArg> { + /// Converts a `Ty` in an ambiguous position to one in an unambiguous position. + /// + /// Functions accepting an unambiguous types may expect the [`TyKind::Infer`] variant + /// to be used. Care should be taken to separately handle infer types when calling this + /// function as it cannot be handled by downstream code making use of the returned ty. + /// + /// In practice this may mean overriding the [`Visitor::visit_infer`][visit_infer] method on hir visitors, or + /// specifically matching on [`GenericArg::Infer`] when handling generic arguments. + /// + /// [visit_infer]: [rustc_hir::intravisit::Visitor::visit_infer] + pub fn as_unambig_ty(&self) -> &Ty<'hir> { + // SAFETY: `Ty` is `repr(C)` and `TyKind` is marked `repr(u8)` so that the layout is + // the same across different ZST type arguments. + let ptr = self as *const Ty<'hir, AmbigArg> as *const Ty<'hir, ()>; + unsafe { &*ptr } + } } impl<'hir> Ty<'hir> { + /// Converts a `Ty` in an unambigous position to one in an ambiguous position. This is + /// fallible as the [`TyKind::Infer`] variant is not present in ambiguous positions. + /// + /// Functions accepting ambiguous types will not handle the [`TyKind::Infer`] variant, if + /// infer types are relevant to you then care should be taken to handle them separately. + pub fn try_as_ambig_ty(&self) -> Option<&Ty<'hir, AmbigArg>> { + if let TyKind::Infer(()) = self.kind { + return None; + } + + // SAFETY: `Ty` is `repr(C)` and `TyKind` is marked `repr(u8)` so that the layout is + // the same across different ZST type arguments. We also asserted that the `self` is + // not a `TyKind::Infer` so there is no risk of transmuting a `()` to `AmbigArg`. + let ptr = self as *const Ty<'hir> as *const Ty<'hir, AmbigArg>; + Some(unsafe { &*ptr }) + } +} + +impl<'hir> Ty<'hir, AmbigArg> { + pub fn peel_refs(&self) -> &Ty<'hir> { + let mut final_ty = self.as_unambig_ty(); + while let TyKind::Ref(_, MutTy { ty, .. }) = &final_ty.kind { + final_ty = ty; + } + final_ty + } +} + +impl<'hir> Ty<'hir> { + pub fn peel_refs(&self) -> &Self { + let mut final_ty = self; + while let TyKind::Ref(_, MutTy { ty, .. }) = &final_ty.kind { + final_ty = ty; + } + final_ty + } + /// Returns `true` if `param_def_id` matches the `bounded_ty` of this predicate. pub fn as_generic_param(&self) -> Option<(DefId, Ident)> { let TyKind::Path(QPath::Resolved(None, path)) = self.kind else { @@ -2894,25 +3094,17 @@ impl<'hir> Ty<'hir> { } } - pub fn peel_refs(&self) -> &Self { - let mut final_ty = self; - while let TyKind::Ref(_, MutTy { ty, .. }) = &final_ty.kind { - final_ty = ty; - } - final_ty - } - pub fn find_self_aliases(&self) -> Vec { use crate::intravisit::Visitor; struct MyVisitor(Vec); impl<'v> Visitor<'v> for MyVisitor { - fn visit_ty(&mut self, t: &'v Ty<'v>) { + fn visit_ty(&mut self, t: &'v Ty<'v, AmbigArg>) { if matches!( &t.kind, - TyKind::Path(QPath::Resolved(_, Path { - res: crate::def::Res::SelfTyAlias { .. }, - .. - },)) + TyKind::Path(QPath::Resolved( + _, + Path { res: crate::def::Res::SelfTyAlias { .. }, .. }, + )) ) { self.0.push(t.span); return; @@ -2922,7 +3114,7 @@ impl<'hir> Ty<'hir> { } let mut my_visitor = MyVisitor(vec![]); - my_visitor.visit_ty(self); + my_visitor.visit_ty_unambig(self); my_visitor.0 } @@ -2931,14 +3123,14 @@ impl<'hir> Ty<'hir> { pub fn is_suggestable_infer_ty(&self) -> bool { fn are_suggestable_generic_args(generic_args: &[GenericArg<'_>]) -> bool { generic_args.iter().any(|arg| match arg { - GenericArg::Type(ty) => ty.is_suggestable_infer_ty(), + GenericArg::Type(ty) => ty.as_unambig_ty().is_suggestable_infer_ty(), GenericArg::Infer(_) => true, _ => false, }) } debug!(?self); match &self.kind { - TyKind::Infer => true, + TyKind::Infer(()) => true, TyKind::Slice(ty) => ty.is_suggestable_infer_ty(), TyKind::Array(ty, length) => { ty.is_suggestable_infer_ty() || matches!(length.kind, ConstArgKind::Infer(..)) @@ -3067,6 +3259,7 @@ pub struct UnsafeBinderTy<'hir> { #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct OpaqueTy<'hir> { + #[stable_hasher(ignore)] pub hir_id: HirId, pub def_id: LocalDefId, pub bounds: GenericBounds<'hir>, @@ -3103,6 +3296,7 @@ impl PreciseCapturingArg<'_> { /// since resolve_bound_vars operates on `Lifetime`s. #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct PreciseCapturingNonLifetimeArg { + #[stable_hasher(ignore)] pub hir_id: HirId, pub ident: Ident, pub res: Res, @@ -3150,7 +3344,9 @@ pub enum InferDelegationKind { /// The various kinds of types recognized by the compiler. #[derive(Debug, Clone, Copy, HashStable_Generic)] -pub enum TyKind<'hir> { +// SAFETY: `repr(u8)` is required so that `TyKind<()>` and `TyKind` are layout compatible +#[repr(u8, C)] +pub enum TyKind<'hir, Unambig = ()> { /// Actual type should be inherited from `DefId` signature InferDelegation(DefId, InferDelegationKind), /// A variable length slice (i.e., `[T]`). @@ -3180,21 +3376,22 @@ pub enum TyKind<'hir> { TraitAscription(GenericBounds<'hir>), /// A trait object type `Bound1 + Bound2 + Bound3` /// where `Bound` is a trait or a lifetime. - TraitObject(&'hir [PolyTraitRef<'hir>], &'hir Lifetime, TraitObjectSyntax), + /// + /// We use pointer tagging to represent a `&'hir Lifetime` and `TraitObjectSyntax` pair + /// as otherwise this type being `repr(C)` would result in `TyKind` increasing in size. + TraitObject(&'hir [PolyTraitRef<'hir>], TaggedRef<'hir, Lifetime, TraitObjectSyntax>), /// Unused for now. Typeof(&'hir AnonConst), - /// `TyKind::Infer` means the type should be inferred instead of it having been - /// specified. This can appear anywhere in a type. - /// - /// **Note:** Not all inferred types are represented as - /// `TyKind::Infer`. In cases where it is ambiguous whether - /// a generic arg is a type or a const, inference variables are - /// represented as `GenericArg::Infer` instead. - Infer, /// Placeholder for a type that has failed to be defined. Err(rustc_span::ErrorGuaranteed), /// Pattern types (`pattern_type!(u32 is 1..)`) - Pat(&'hir Ty<'hir>, &'hir Pat<'hir>), + Pat(&'hir Ty<'hir>, &'hir TyPat<'hir>), + /// `TyKind::Infer` means the type should be inferred instead of it having been + /// specified. This can appear anywhere in a type. + /// + /// This variant is not always used to represent inference types, sometimes + /// [`GenericArg::Infer`] is used instead. + Infer(Unambig), } #[derive(Debug, Clone, Copy, HashStable_Generic)] @@ -3249,11 +3446,10 @@ impl<'hir> InlineAsmOperand<'hir> { } pub fn is_clobber(&self) -> bool { - matches!(self, InlineAsmOperand::Out { - reg: InlineAsmRegOrRegClass::Reg(_), - late: _, - expr: None - }) + matches!( + self, + InlineAsmOperand::Out { reg: InlineAsmRegOrRegClass::Reg(_), late: _, expr: None } + ) } } @@ -3276,6 +3472,7 @@ impl InlineAsm<'_> { /// Represents a parameter in a function header. #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct Param<'hir> { + #[stable_hasher(ignore)] pub hir_id: HirId, pub pat: &'hir Pat<'hir>, pub ty_span: Span, @@ -3387,11 +3584,11 @@ impl<'hir> FnRetTy<'hir> { } } - pub fn get_infer_ret_ty(&self) -> Option<&'hir Ty<'hir>> { - if let Self::Return(ty) = self { - if ty.is_suggestable_infer_ty() { - return Some(*ty); - } + pub fn is_suggestable_infer_ty(&self) -> Option<&'hir Ty<'hir>> { + if let Self::Return(ty) = self + && ty.is_suggestable_infer_ty() + { + return Some(*ty); } None } @@ -3433,6 +3630,7 @@ pub struct Variant<'hir> { /// Name of the variant. pub ident: Ident, /// Id of the variant (not the constructor, see `VariantData::ctor_hir_id()`). + #[stable_hasher(ignore)] pub hir_id: HirId, pub def_id: LocalDefId, /// Fields and constructor id of the variant. @@ -3505,6 +3703,7 @@ pub struct FieldDef<'hir> { pub span: Span, pub vis_span: Span, pub ident: Ident, + #[stable_hasher(ignore)] pub hir_id: HirId, pub def_id: LocalDefId, pub ty: &'hir Ty<'hir>, @@ -3529,11 +3728,11 @@ pub enum VariantData<'hir> { /// A tuple variant. /// /// E.g., `Bar(..)` as in `enum Foo { Bar(..) }`. - Tuple(&'hir [FieldDef<'hir>], HirId, LocalDefId), + Tuple(&'hir [FieldDef<'hir>], #[stable_hasher(ignore)] HirId, LocalDefId), /// A unit variant. /// /// E.g., `Bar = ..` as in `enum Foo { Bar = .. }`. - Unit(HirId, LocalDefId), + Unit(#[stable_hasher(ignore)] HirId, LocalDefId), } impl<'hir> VariantData<'hir> { @@ -3640,7 +3839,7 @@ impl<'hir> Item<'hir> { ItemKind::Const(ty, generics, body), (ty, generics, *body); expect_fn, (&FnSig<'hir>, &'hir Generics<'hir>, BodyId), - ItemKind::Fn(sig, generics, body), (sig, generics, *body); + ItemKind::Fn { sig, generics, body, .. }, (sig, generics, *body); expect_macro, (&ast::MacroDef, MacroKind), ItemKind::Macro(def, mk), (def, *mk); @@ -3727,9 +3926,30 @@ impl fmt::Display for Constness { } } +/// The actualy safety specified in syntax. We may treat +/// its safety different within the type system to create a +/// "sound by default" system that needs checking this enum +/// explicitly to allow unsafe operations. +#[derive(Copy, Clone, Debug, HashStable_Generic, PartialEq, Eq)] +pub enum HeaderSafety { + /// A safe function annotated with `#[target_features]`. + /// The type system treats this function as an unsafe function, + /// but safety checking will check this enum to treat it as safe + /// and allowing calling other safe target feature functions with + /// the same features without requiring an additional unsafe block. + SafeTargetFeatures, + Normal(Safety), +} + +impl From for HeaderSafety { + fn from(v: Safety) -> Self { + Self::Normal(v) + } +} + #[derive(Copy, Clone, Debug, HashStable_Generic)] pub struct FnHeader { - pub safety: Safety, + pub safety: HeaderSafety, pub constness: Constness, pub asyncness: IsAsync, pub abi: ExternAbi, @@ -3737,15 +3957,26 @@ pub struct FnHeader { impl FnHeader { pub fn is_async(&self) -> bool { - matches!(&self.asyncness, IsAsync::Async(_)) + matches!(self.asyncness, IsAsync::Async(_)) } pub fn is_const(&self) -> bool { - matches!(&self.constness, Constness::Const) + matches!(self.constness, Constness::Const) } pub fn is_unsafe(&self) -> bool { - self.safety.is_unsafe() + self.safety().is_unsafe() + } + + pub fn is_safe(&self) -> bool { + self.safety().is_safe() + } + + pub fn safety(&self) -> Safety { + match self.safety { + HeaderSafety::SafeTargetFeatures => Safety::Unsafe, + HeaderSafety::Normal(safety) => safety, + } } } @@ -3768,7 +3999,15 @@ pub enum ItemKind<'hir> { /// A `const` item. Const(&'hir Ty<'hir>, &'hir Generics<'hir>, BodyId), /// A function declaration. - Fn(FnSig<'hir>, &'hir Generics<'hir>, BodyId), + Fn { + sig: FnSig<'hir>, + generics: &'hir Generics<'hir>, + body: BodyId, + /// Whether this function actually has a body. + /// For functions without a body, `body` is synthesized (to avoid ICEs all over the + /// compiler), but that code should never be translated. + has_body: bool, + }, /// A MBE macro definition (`macro_rules!` or `macro`). Macro(&'hir ast::MacroDef, MacroKind), /// A module. @@ -3818,16 +4057,16 @@ pub struct Impl<'hir> { impl ItemKind<'_> { pub fn generics(&self) -> Option<&Generics<'_>> { - Some(match *self { - ItemKind::Fn(_, ref generics, _) - | ItemKind::TyAlias(_, ref generics) - | ItemKind::Const(_, ref generics, _) - | ItemKind::Enum(_, ref generics) - | ItemKind::Struct(_, ref generics) - | ItemKind::Union(_, ref generics) - | ItemKind::Trait(_, _, ref generics, _, _) - | ItemKind::TraitAlias(ref generics, _) - | ItemKind::Impl(Impl { ref generics, .. }) => generics, + Some(match self { + ItemKind::Fn { generics, .. } + | ItemKind::TyAlias(_, generics) + | ItemKind::Const(_, generics, _) + | ItemKind::Enum(_, generics) + | ItemKind::Struct(_, generics) + | ItemKind::Union(_, generics) + | ItemKind::Trait(_, _, generics, _, _) + | ItemKind::TraitAlias(generics, _) + | ItemKind::Impl(Impl { generics, .. }) => generics, _ => return None, }) } @@ -3838,7 +4077,7 @@ impl ItemKind<'_> { ItemKind::Use(..) => "`use` import", ItemKind::Static(..) => "static item", ItemKind::Const(..) => "constant item", - ItemKind::Fn(..) => "function", + ItemKind::Fn { .. } => "function", ItemKind::Macro(..) => "macro", ItemKind::Mod(..) => "module", ItemKind::ForeignMod { .. } => "extern block", @@ -4004,7 +4243,7 @@ impl<'hir> OwnerNode<'hir> { match self { OwnerNode::TraitItem(TraitItem { kind: TraitItemKind::Fn(fn_sig, _), .. }) | OwnerNode::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. }) - | OwnerNode::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. }) + | OwnerNode::Item(Item { kind: ItemKind::Fn { sig: fn_sig, .. }, .. }) | OwnerNode::ForeignItem(ForeignItem { kind: ForeignItemKind::Fn(fn_sig, _, _), .. }) => Some(fn_sig), @@ -4016,7 +4255,7 @@ impl<'hir> OwnerNode<'hir> { match self { OwnerNode::TraitItem(TraitItem { kind: TraitItemKind::Fn(fn_sig, _), .. }) | OwnerNode::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. }) - | OwnerNode::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. }) + | OwnerNode::Item(Item { kind: ItemKind::Fn { sig: fn_sig, .. }, .. }) | OwnerNode::ForeignItem(ForeignItem { kind: ForeignItemKind::Fn(fn_sig, _, _), .. }) => Some(fn_sig.decl), @@ -4030,7 +4269,7 @@ impl<'hir> OwnerNode<'hir> { kind: ItemKind::Static(_, _, body) | ItemKind::Const(_, _, body) - | ItemKind::Fn(_, _, body), + | ItemKind::Fn { body, .. }, .. }) | OwnerNode::TraitItem(TraitItem { @@ -4131,8 +4370,13 @@ pub enum Node<'hir> { AssocItemConstraint(&'hir AssocItemConstraint<'hir>), TraitRef(&'hir TraitRef<'hir>), OpaqueTy(&'hir OpaqueTy<'hir>), + TyPat(&'hir TyPat<'hir>), Pat(&'hir Pat<'hir>), PatField(&'hir PatField<'hir>), + /// Needed as its own node with its own HirId for tracking + /// the unadjusted type of literals within patterns + /// (e.g. byte str literals not being of slice type). + PatExpr(&'hir PatExpr<'hir>), Arm(&'hir Arm<'hir>), Block(&'hir Block<'hir>), LetStmt(&'hir LetStmt<'hir>), @@ -4189,6 +4433,8 @@ impl<'hir> Node<'hir> { | Node::Block(..) | Node::Ctor(..) | Node::Pat(..) + | Node::TyPat(..) + | Node::PatExpr(..) | Node::Arm(..) | Node::LetStmt(..) | Node::Crate(..) @@ -4206,7 +4452,7 @@ impl<'hir> Node<'hir> { match self { Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(fn_sig, _), .. }) | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. }) - | Node::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. }) + | Node::Item(Item { kind: ItemKind::Fn { sig: fn_sig, .. }, .. }) | Node::ForeignItem(ForeignItem { kind: ForeignItemKind::Fn(fn_sig, _, _), .. }) => { Some(fn_sig.decl) } @@ -4219,16 +4465,14 @@ impl<'hir> Node<'hir> { /// Get a `hir::Impl` if the node is an impl block for the given `trait_def_id`. pub fn impl_block_of_trait(self, trait_def_id: DefId) -> Option<&'hir Impl<'hir>> { - match self { - Node::Item(Item { kind: ItemKind::Impl(impl_block), .. }) - if impl_block - .of_trait - .and_then(|trait_ref| trait_ref.trait_def_id()) - .is_some_and(|trait_id| trait_id == trait_def_id) => - { - Some(impl_block) - } - _ => None, + if let Node::Item(Item { kind: ItemKind::Impl(impl_block), .. }) = self + && let Some(trait_ref) = impl_block.of_trait + && let Some(trait_id) = trait_ref.trait_def_id() + && trait_id == trait_def_id + { + Some(impl_block) + } else { + None } } @@ -4236,7 +4480,7 @@ impl<'hir> Node<'hir> { match self { Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(fn_sig, _), .. }) | Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. }) - | Node::Item(Item { kind: ItemKind::Fn(fn_sig, _, _), .. }) + | Node::Item(Item { kind: ItemKind::Fn { sig: fn_sig, .. }, .. }) | Node::ForeignItem(ForeignItem { kind: ForeignItemKind::Fn(fn_sig, _, _), .. }) => { Some(fn_sig) } @@ -4281,7 +4525,7 @@ impl<'hir> Node<'hir> { Node::Item(Item { owner_id, kind: - ItemKind::Const(_, _, body) | ItemKind::Static(.., body) | ItemKind::Fn(.., body), + ItemKind::Const(_, _, body) | ItemKind::Static(.., body) | ItemKind::Fn { body, .. }, .. }) | Node::TraitItem(TraitItem { @@ -4338,7 +4582,7 @@ impl<'hir> Node<'hir> { pub fn fn_kind(self) -> Option> { match self { Node::Item(i) => match i.kind { - ItemKind::Fn(ref sig, ref generics, _) => { + ItemKind::Fn { sig, generics, .. } => { Some(FnKind::ItemFn(i.ident, generics, sig.header)) } _ => None, @@ -4430,12 +4674,5 @@ mod size_asserts { // tidy-alphabetical-end } -fn debug_fn(f: impl Fn(&mut fmt::Formatter<'_>) -> fmt::Result) -> impl fmt::Debug { - struct DebugFn(F); - impl) -> fmt::Result> fmt::Debug for DebugFn { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - (self.0)(fmt) - } - } - DebugFn(f) -} +#[cfg(test)] +mod tests; diff --git a/compiler/rustc_hir/src/hir/tests.rs b/compiler/rustc_hir/src/hir/tests.rs new file mode 100644 index 0000000000000..300d44355303a --- /dev/null +++ b/compiler/rustc_hir/src/hir/tests.rs @@ -0,0 +1,83 @@ +use rustc_span::def_id::DefIndex; + +use super::*; + +macro_rules! define_tests { + ($($name:ident $kind:ident $variant:ident {$($init:tt)*})*) => {$( + #[test] + fn $name() { + let unambig = $kind::$variant::<'_, ()> { $($init)* }; + let unambig_to_ambig = unsafe { std::mem::transmute::<_, $kind<'_, AmbigArg>>(unambig) }; + + assert!(matches!(&unambig_to_ambig, $kind::$variant { $($init)* })); + + let ambig_to_unambig = unsafe { std::mem::transmute::<_, $kind<'_, ()>>(unambig_to_ambig) }; + + assert!(matches!(&ambig_to_unambig, $kind::$variant { $($init)* })); + } + )*}; +} + +define_tests! { + cast_never TyKind Never {} + cast_tup TyKind Tup { 0: &[Ty { span: DUMMY_SP, hir_id: HirId::INVALID, kind: TyKind::Never }] } + cast_ptr TyKind Ptr { 0: MutTy { ty: &Ty { span: DUMMY_SP, hir_id: HirId::INVALID, kind: TyKind::Never }, mutbl: Mutability::Not }} + cast_array TyKind Array { + 0: &Ty { span: DUMMY_SP, hir_id: HirId::INVALID, kind: TyKind::Never }, + 1: &ConstArg { hir_id: HirId::INVALID, kind: ConstArgKind::Anon(&AnonConst { + hir_id: HirId::INVALID, + def_id: LocalDefId { local_def_index: DefIndex::ZERO }, + body: BodyId { hir_id: HirId::INVALID }, + span: DUMMY_SP, + })} + } + + cast_anon ConstArgKind Anon { + 0: &AnonConst { + hir_id: HirId::INVALID, + def_id: LocalDefId { local_def_index: DefIndex::ZERO }, + body: BodyId { hir_id: HirId::INVALID }, + span: DUMMY_SP, + } + } +} + +#[test] +fn trait_object_roundtrips() { + trait_object_roundtrips_impl(TraitObjectSyntax::Dyn); + trait_object_roundtrips_impl(TraitObjectSyntax::DynStar); + trait_object_roundtrips_impl(TraitObjectSyntax::None); +} + +fn trait_object_roundtrips_impl(syntax: TraitObjectSyntax) { + let unambig = TyKind::TraitObject::<'_, ()>( + &[], + TaggedRef::new( + &const { + Lifetime { + hir_id: HirId::INVALID, + ident: Ident::new(sym::name, DUMMY_SP), + res: LifetimeName::Static, + } + }, + syntax, + ), + ); + let unambig_to_ambig = unsafe { std::mem::transmute::<_, TyKind<'_, AmbigArg>>(unambig) }; + + match unambig_to_ambig { + TyKind::TraitObject(_, tagged_ref) => { + assert!(tagged_ref.tag() == syntax) + } + _ => panic!("`TyKind::TraitObject` did not roundtrip"), + }; + + let ambig_to_unambig = unsafe { std::mem::transmute::<_, TyKind<'_, ()>>(unambig_to_ambig) }; + + match ambig_to_unambig { + TyKind::TraitObject(_, tagged_ref) => { + assert!(tagged_ref.tag() == syntax) + } + _ => panic!("`TyKind::TraitObject` did not roundtrip"), + }; +} diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index a73cf367c0e0c..f632041dba9f2 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -342,28 +342,62 @@ pub trait Visitor<'v>: Sized { fn visit_pat_field(&mut self, f: &'v PatField<'v>) -> Self::Result { walk_pat_field(self, f) } + fn visit_pat_expr(&mut self, expr: &'v PatExpr<'v>) -> Self::Result { + walk_pat_expr(self, expr) + } + fn visit_lit(&mut self, _hir_id: HirId, _lit: &'v Lit, _negated: bool) -> Self::Result { + Self::Result::output() + } fn visit_anon_const(&mut self, c: &'v AnonConst) -> Self::Result { walk_anon_const(self, c) } fn visit_inline_const(&mut self, c: &'v ConstBlock) -> Self::Result { walk_inline_const(self, c) } - fn visit_const_arg(&mut self, c: &'v ConstArg<'v>) -> Self::Result { - walk_const_arg(self, c) + + fn visit_generic_arg(&mut self, generic_arg: &'v GenericArg<'v>) -> Self::Result { + walk_generic_arg(self, generic_arg) } + + /// All types are treated as ambiguous types for the purposes of hir visiting in + /// order to ensure that visitors can handle infer vars without it being too error-prone. + /// + /// See the doc comments on [`Ty`] for an explanation of what it means for a type to be + /// ambiguous. + /// + /// The [`Visitor::visit_infer`] method should be overriden in order to handle infer vars. + fn visit_ty(&mut self, t: &'v Ty<'v, AmbigArg>) -> Self::Result { + walk_ty(self, t) + } + + /// All consts are treated as ambiguous consts for the purposes of hir visiting in + /// order to ensure that visitors can handle infer vars without it being too error-prone. + /// + /// See the doc comments on [`ConstArg`] for an explanation of what it means for a const to be + /// ambiguous. + /// + /// The [`Visitor::visit_infer`] method should be overriden in order to handle infer vars. + fn visit_const_arg(&mut self, c: &'v ConstArg<'v, AmbigArg>) -> Self::Result { + walk_ambig_const_arg(self, c) + } + + #[allow(unused_variables)] + fn visit_infer(&mut self, inf_id: HirId, inf_span: Span, kind: InferKind<'v>) -> Self::Result { + self.visit_id(inf_id) + } + + fn visit_lifetime(&mut self, lifetime: &'v Lifetime) -> Self::Result { + walk_lifetime(self, lifetime) + } + fn visit_expr(&mut self, ex: &'v Expr<'v>) -> Self::Result { walk_expr(self, ex) } fn visit_expr_field(&mut self, field: &'v ExprField<'v>) -> Self::Result { walk_expr_field(self, field) } - fn visit_ty(&mut self, t: &'v Ty<'v>) -> Self::Result { - walk_ty(self, t) - } - fn visit_pattern_type_pattern(&mut self, _p: &'v Pat<'v>) { - // Do nothing. Only a few visitors need to know the details of the pattern type, - // and they opt into it. All other visitors will just choke on our fake patterns - // because they aren't in a body. + fn visit_pattern_type_pattern(&mut self, p: &'v TyPat<'v>) -> Self::Result { + walk_ty_pat(self, p) } fn visit_generic_param(&mut self, p: &'v GenericParam<'v>) -> Self::Result { walk_generic_param(self, p) @@ -441,15 +475,6 @@ pub trait Visitor<'v>: Sized { fn visit_label(&mut self, label: &'v Label) -> Self::Result { walk_label(self, label) } - fn visit_infer(&mut self, inf: &'v InferArg) -> Self::Result { - walk_inf(self, inf) - } - fn visit_generic_arg(&mut self, generic_arg: &'v GenericArg<'v>) -> Self::Result { - walk_generic_arg(self, generic_arg) - } - fn visit_lifetime(&mut self, lifetime: &'v Lifetime) -> Self::Result { - walk_lifetime(self, lifetime) - } // The span is that of the surrounding type/pattern/expr/whatever. fn visit_qpath(&mut self, qpath: &'v QPath<'v>, id: HirId, _span: Span) -> Self::Result { walk_qpath(self, qpath, id) @@ -483,6 +508,26 @@ pub trait Visitor<'v>: Sized { } } +pub trait VisitorExt<'v>: Visitor<'v> { + /// Extension trait method to visit types in unambiguous positions, this is not + /// directly on the [`Visitor`] trait as this method should never be overridden. + /// + /// Named `visit_ty_unambig` instead of `visit_unambig_ty` to aid in discovery + /// by IDes when `v.visit_ty` is written. + fn visit_ty_unambig(&mut self, t: &'v Ty<'v>) -> Self::Result { + walk_unambig_ty(self, t) + } + /// Extension trait method to visit consts in unambiguous positions, this is not + /// directly on the [`Visitor`] trait as this method should never be overridden. + /// + /// Named `visit_const_arg_unambig` instead of `visit_unambig_const_arg` to aid in + /// discovery by IDes when `v.visit_const_arg` is written. + fn visit_const_arg_unambig(&mut self, c: &'v ConstArg<'v>) -> Self::Result { + walk_const_arg(self, c) + } +} +impl<'v, V: Visitor<'v>> VisitorExt<'v> for V {} + pub fn walk_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v Param<'v>) -> V::Result { try_visit!(visitor.visit_id(param.hir_id)); visitor.visit_pat(param.pat) @@ -500,16 +545,16 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: } ItemKind::Static(ref typ, _, body) => { try_visit!(visitor.visit_id(item.hir_id())); - try_visit!(visitor.visit_ty(typ)); + try_visit!(visitor.visit_ty_unambig(typ)); try_visit!(visitor.visit_nested_body(body)); } ItemKind::Const(ref typ, ref generics, body) => { try_visit!(visitor.visit_id(item.hir_id())); - try_visit!(visitor.visit_ty(typ)); + try_visit!(visitor.visit_ty_unambig(typ)); try_visit!(visitor.visit_generics(generics)); try_visit!(visitor.visit_nested_body(body)); } - ItemKind::Fn(ref sig, ref generics, body_id) => { + ItemKind::Fn { sig, generics, body: body_id, .. } => { try_visit!(visitor.visit_id(item.hir_id())); try_visit!(visitor.visit_fn( FnKind::ItemFn(item.ident, generics, sig.header), @@ -536,7 +581,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: } ItemKind::TyAlias(ref ty, ref generics) => { try_visit!(visitor.visit_id(item.hir_id())); - try_visit!(visitor.visit_ty(ty)); + try_visit!(visitor.visit_ty_unambig(ty)); try_visit!(visitor.visit_generics(generics)); } ItemKind::Enum(ref enum_definition, ref generics) => { @@ -558,7 +603,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: try_visit!(visitor.visit_id(item.hir_id())); try_visit!(visitor.visit_generics(generics)); visit_opt!(visitor, visit_trait_ref, of_trait); - try_visit!(visitor.visit_ty(self_ty)); + try_visit!(visitor.visit_ty_unambig(self_ty)); walk_list!(visitor, visit_impl_item_ref, *items); } ItemKind::Struct(ref struct_definition, ref generics) @@ -615,7 +660,7 @@ pub fn walk_foreign_item<'v, V: Visitor<'v>>( walk_list!(visitor, visit_ident, param_names.iter().copied()); } ForeignItemKind::Static(ref typ, _, _) => { - try_visit!(visitor.visit_ty(typ)); + try_visit!(visitor.visit_ty_unambig(typ)); } ForeignItemKind::Type => (), } @@ -629,7 +674,7 @@ pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v LetStmt<'v>) - try_visit!(visitor.visit_id(local.hir_id)); try_visit!(visitor.visit_pat(local.pat)); visit_opt!(visitor, visit_block, local.els); - visit_opt!(visitor, visit_ty, local.ty); + visit_opt!(visitor, visit_ty_unambig, local.ty); V::Result::output() } @@ -658,6 +703,18 @@ pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm<'v>) -> V::Res visitor.visit_expr(arm.body) } +pub fn walk_ty_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v TyPat<'v>) -> V::Result { + try_visit!(visitor.visit_id(pattern.hir_id)); + match pattern.kind { + TyPatKind::Range(lower_bound, upper_bound, _) => { + visit_opt!(visitor, visit_const_arg_unambig, lower_bound); + visit_opt!(visitor, visit_const_arg_unambig, upper_bound); + } + TyPatKind::Err(_) => (), + } + V::Result::output() +} + pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) -> V::Result { try_visit!(visitor.visit_id(pattern.hir_id)); match pattern.kind { @@ -665,9 +722,6 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) -> V: try_visit!(visitor.visit_qpath(qpath, pattern.hir_id, pattern.span)); walk_list!(visitor, visit_pat, children); } - PatKind::Path(ref qpath) => { - try_visit!(visitor.visit_qpath(qpath, pattern.hir_id, pattern.span)); - } PatKind::Struct(ref qpath, fields, _) => { try_visit!(visitor.visit_qpath(qpath, pattern.hir_id, pattern.span)); walk_list!(visitor, visit_pat_field, fields); @@ -685,10 +739,10 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) -> V: try_visit!(visitor.visit_ident(ident)); visit_opt!(visitor, visit_pat, optional_subpattern); } - PatKind::Lit(ref expression) => try_visit!(visitor.visit_expr(expression)), + PatKind::Expr(ref expression) => try_visit!(visitor.visit_pat_expr(expression)), PatKind::Range(ref lower_bound, ref upper_bound, _) => { - visit_opt!(visitor, visit_expr, lower_bound); - visit_opt!(visitor, visit_expr, upper_bound); + visit_opt!(visitor, visit_pat_expr, lower_bound); + visit_opt!(visitor, visit_pat_expr, upper_bound); } PatKind::Never | PatKind::Wild | PatKind::Err(_) => (), PatKind::Slice(prepatterns, ref slice_pattern, postpatterns) => { @@ -696,6 +750,10 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) -> V: visit_opt!(visitor, visit_pat, slice_pattern); walk_list!(visitor, visit_pat, postpatterns); } + PatKind::Guard(subpat, condition) => { + try_visit!(visitor.visit_pat(subpat)); + try_visit!(visitor.visit_expr(condition)); + } } V::Result::output() } @@ -706,6 +764,15 @@ pub fn walk_pat_field<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v PatField<' visitor.visit_pat(field.pat) } +pub fn walk_pat_expr<'v, V: Visitor<'v>>(visitor: &mut V, expr: &'v PatExpr<'v>) -> V::Result { + try_visit!(visitor.visit_id(expr.hir_id)); + match &expr.kind { + PatExprKind::Lit { lit, negated } => visitor.visit_lit(expr.hir_id, lit, *negated), + PatExprKind::ConstBlock(c) => visitor.visit_inline_const(c), + PatExprKind::Path(qpath) => visitor.visit_qpath(qpath, expr.hir_id, expr.span), + } +} + pub fn walk_anon_const<'v, V: Visitor<'v>>(visitor: &mut V, constant: &'v AnonConst) -> V::Result { try_visit!(visitor.visit_id(constant.hir_id)); visitor.visit_nested_body(constant.body) @@ -719,18 +786,6 @@ pub fn walk_inline_const<'v, V: Visitor<'v>>( visitor.visit_nested_body(constant.body) } -pub fn walk_const_arg<'v, V: Visitor<'v>>( - visitor: &mut V, - const_arg: &'v ConstArg<'v>, -) -> V::Result { - try_visit!(visitor.visit_id(const_arg.hir_id)); - match &const_arg.kind { - ConstArgKind::Path(qpath) => visitor.visit_qpath(qpath, const_arg.hir_id, qpath.span()), - ConstArgKind::Anon(anon) => visitor.visit_anon_const(*anon), - ConstArgKind::Infer(..) => V::Result::output(), - } -} - pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) -> V::Result { try_visit!(visitor.visit_id(expression.hir_id)); match expression.kind { @@ -742,7 +797,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) } ExprKind::Repeat(ref element, ref count) => { try_visit!(visitor.visit_expr(element)); - try_visit!(visitor.visit_const_arg(count)); + try_visit!(visitor.visit_const_arg_unambig(count)); } ExprKind::Struct(ref qpath, fields, ref optional_base) => { try_visit!(visitor.visit_qpath(qpath, expression.hir_id, expression.span)); @@ -773,7 +828,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) } ExprKind::Cast(ref subexpression, ref typ) | ExprKind::Type(ref subexpression, ref typ) => { try_visit!(visitor.visit_expr(subexpression)); - try_visit!(visitor.visit_ty(typ)); + try_visit!(visitor.visit_ty_unambig(typ)); } ExprKind::DropTemps(ref subexpression) => { try_visit!(visitor.visit_expr(subexpression)); @@ -782,7 +837,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) // match the visit order in walk_local try_visit!(visitor.visit_expr(init)); try_visit!(visitor.visit_pat(pat)); - visit_opt!(visitor, visit_ty, ty); + visit_opt!(visitor, visit_ty_unambig, ty); } ExprKind::If(ref cond, ref then, ref else_opt) => { try_visit!(visitor.visit_expr(cond)); @@ -850,7 +905,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) try_visit!(visitor.visit_inline_asm(asm, expression.hir_id)); } ExprKind::OffsetOf(ref container, ref fields) => { - try_visit!(visitor.visit_ty(container)); + try_visit!(visitor.visit_ty_unambig(container)); walk_list!(visitor, visit_ident, fields.iter().copied()); } ExprKind::Yield(ref subexpression, _) => { @@ -858,9 +913,10 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) } ExprKind::UnsafeBinderCast(_kind, expr, ty) => { try_visit!(visitor.visit_expr(expr)); - visit_opt!(visitor, visit_ty, ty); + visit_opt!(visitor, visit_ty_unambig, ty); } - ExprKind::Lit(_) | ExprKind::Err(_) => {} + ExprKind::Lit(lit) => try_visit!(visitor.visit_lit(expression.hir_id, lit, false)), + ExprKind::Err(_) => {} } V::Result::output() } @@ -870,20 +926,49 @@ pub fn walk_expr_field<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v ExprField try_visit!(visitor.visit_ident(field.ident)); visitor.visit_expr(field.expr) } +/// We track whether an infer var is from a [`Ty`], [`ConstArg`], or [`GenericArg`] so that +/// HIR visitors overriding [`Visitor::visit_infer`] can determine what kind of infer is being visited +pub enum InferKind<'hir> { + Ty(&'hir Ty<'hir>), + Const(&'hir ConstArg<'hir>), + Ambig(&'hir InferArg), +} -pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Result { +pub fn walk_generic_arg<'v, V: Visitor<'v>>( + visitor: &mut V, + generic_arg: &'v GenericArg<'v>, +) -> V::Result { + match generic_arg { + GenericArg::Lifetime(lt) => visitor.visit_lifetime(lt), + GenericArg::Type(ty) => visitor.visit_ty(ty), + GenericArg::Const(ct) => visitor.visit_const_arg(ct), + GenericArg::Infer(inf) => visitor.visit_infer(inf.hir_id, inf.span, InferKind::Ambig(inf)), + } +} + +pub fn walk_unambig_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Result { + match typ.try_as_ambig_ty() { + Some(ambig_ty) => visitor.visit_ty(ambig_ty), + None => { + try_visit!(visitor.visit_id(typ.hir_id)); + visitor.visit_infer(typ.hir_id, typ.span, InferKind::Ty(typ)) + } + } +} + +pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v, AmbigArg>) -> V::Result { try_visit!(visitor.visit_id(typ.hir_id)); match typ.kind { - TyKind::Slice(ref ty) => try_visit!(visitor.visit_ty(ty)), - TyKind::Ptr(ref mutable_type) => try_visit!(visitor.visit_ty(mutable_type.ty)), + TyKind::Slice(ref ty) => try_visit!(visitor.visit_ty_unambig(ty)), + TyKind::Ptr(ref mutable_type) => try_visit!(visitor.visit_ty_unambig(mutable_type.ty)), TyKind::Ref(ref lifetime, ref mutable_type) => { try_visit!(visitor.visit_lifetime(lifetime)); - try_visit!(visitor.visit_ty(mutable_type.ty)); + try_visit!(visitor.visit_ty_unambig(mutable_type.ty)); } TyKind::Never => {} TyKind::Tup(tuple_element_types) => { - walk_list!(visitor, visit_ty, tuple_element_types); + walk_list!(visitor, visit_ty_unambig, tuple_element_types); } TyKind::BareFn(ref function_declaration) => { walk_list!(visitor, visit_generic_param, function_declaration.generic_params); @@ -891,7 +976,7 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul } TyKind::UnsafeBinder(ref unsafe_binder) => { walk_list!(visitor, visit_generic_param, unsafe_binder.generic_params); - try_visit!(visitor.visit_ty(unsafe_binder.inner_ty)); + try_visit!(visitor.visit_ty_unambig(unsafe_binder.inner_ty)); } TyKind::Path(ref qpath) => { try_visit!(visitor.visit_qpath(qpath, typ.hir_id, typ.span)); @@ -903,25 +988,49 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul walk_list!(visitor, visit_param_bound, bounds); } TyKind::Array(ref ty, ref length) => { - try_visit!(visitor.visit_ty(ty)); - try_visit!(visitor.visit_const_arg(length)); + try_visit!(visitor.visit_ty_unambig(ty)); + try_visit!(visitor.visit_const_arg_unambig(length)); } - TyKind::TraitObject(bounds, ref lifetime, _syntax) => { + TyKind::TraitObject(bounds, ref lifetime) => { for bound in bounds { try_visit!(visitor.visit_poly_trait_ref(bound)); } try_visit!(visitor.visit_lifetime(lifetime)); } TyKind::Typeof(ref expression) => try_visit!(visitor.visit_anon_const(expression)), - TyKind::Infer | TyKind::InferDelegation(..) | TyKind::Err(_) => {} + TyKind::InferDelegation(..) | TyKind::Err(_) => {} TyKind::Pat(ty, pat) => { - try_visit!(visitor.visit_ty(ty)); + try_visit!(visitor.visit_ty_unambig(ty)); try_visit!(visitor.visit_pattern_type_pattern(pat)); } } V::Result::output() } +pub fn walk_const_arg<'v, V: Visitor<'v>>( + visitor: &mut V, + const_arg: &'v ConstArg<'v>, +) -> V::Result { + match const_arg.try_as_ambig_ct() { + Some(ambig_ct) => visitor.visit_const_arg(ambig_ct), + None => { + try_visit!(visitor.visit_id(const_arg.hir_id)); + visitor.visit_infer(const_arg.hir_id, const_arg.span(), InferKind::Const(const_arg)) + } + } +} + +pub fn walk_ambig_const_arg<'v, V: Visitor<'v>>( + visitor: &mut V, + const_arg: &'v ConstArg<'v, AmbigArg>, +) -> V::Result { + try_visit!(visitor.visit_id(const_arg.hir_id)); + match &const_arg.kind { + ConstArgKind::Path(qpath) => visitor.visit_qpath(qpath, const_arg.hir_id, qpath.span()), + ConstArgKind::Anon(anon) => visitor.visit_anon_const(*anon), + } +} + pub fn walk_generic_param<'v, V: Visitor<'v>>( visitor: &mut V, param: &'v GenericParam<'v>, @@ -933,9 +1042,11 @@ pub fn walk_generic_param<'v, V: Visitor<'v>>( } match param.kind { GenericParamKind::Lifetime { .. } => {} - GenericParamKind::Type { ref default, .. } => visit_opt!(visitor, visit_ty, default), + GenericParamKind::Type { ref default, .. } => { + visit_opt!(visitor, visit_ty_unambig, default) + } GenericParamKind::Const { ref ty, ref default, synthetic: _ } => { - try_visit!(visitor.visit_ty(ty)); + try_visit!(visitor.visit_ty_unambig(ty)); if let Some(ref default) = default { try_visit!(visitor.visit_const_param_default(param.hir_id, default)); } @@ -948,7 +1059,7 @@ pub fn walk_const_param_default<'v, V: Visitor<'v>>( visitor: &mut V, ct: &'v ConstArg<'v>, ) -> V::Result { - visitor.visit_const_arg(ct) + visitor.visit_const_arg_unambig(ct) } pub fn walk_generics<'v, V: Visitor<'v>>(visitor: &mut V, generics: &'v Generics<'v>) -> V::Result { @@ -970,7 +1081,7 @@ pub fn walk_where_predicate<'v, V: Visitor<'v>>( bound_generic_params, origin: _, }) => { - try_visit!(visitor.visit_ty(bounded_ty)); + try_visit!(visitor.visit_ty_unambig(bounded_ty)); walk_list!(visitor, visit_param_bound, bounds); walk_list!(visitor, visit_generic_param, bound_generic_params); } @@ -983,8 +1094,8 @@ pub fn walk_where_predicate<'v, V: Visitor<'v>>( walk_list!(visitor, visit_param_bound, bounds); } WherePredicateKind::EqPredicate(WhereEqPredicate { ref lhs_ty, ref rhs_ty }) => { - try_visit!(visitor.visit_ty(lhs_ty)); - try_visit!(visitor.visit_ty(rhs_ty)); + try_visit!(visitor.visit_ty_unambig(lhs_ty)); + try_visit!(visitor.visit_ty_unambig(rhs_ty)); } } V::Result::output() @@ -994,13 +1105,13 @@ pub fn walk_fn_decl<'v, V: Visitor<'v>>( visitor: &mut V, function_declaration: &'v FnDecl<'v>, ) -> V::Result { - walk_list!(visitor, visit_ty, function_declaration.inputs); + walk_list!(visitor, visit_ty_unambig, function_declaration.inputs); visitor.visit_fn_ret_ty(&function_declaration.output) } pub fn walk_fn_ret_ty<'v, V: Visitor<'v>>(visitor: &mut V, ret_ty: &'v FnRetTy<'v>) -> V::Result { if let FnRetTy::Return(output_ty) = *ret_ty { - try_visit!(visitor.visit_ty(output_ty)); + try_visit!(visitor.visit_ty_unambig(output_ty)); } V::Result::output() } @@ -1053,7 +1164,7 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>( try_visit!(visitor.visit_id(hir_id)); match *kind { TraitItemKind::Const(ref ty, default) => { - try_visit!(visitor.visit_ty(ty)); + try_visit!(visitor.visit_ty_unambig(ty)); visit_opt!(visitor, visit_nested_body, default); } TraitItemKind::Fn(ref sig, TraitFn::Required(param_names)) => { @@ -1071,7 +1182,7 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>( } TraitItemKind::Type(bounds, ref default) => { walk_list!(visitor, visit_param_bound, bounds); - visit_opt!(visitor, visit_ty, default); + visit_opt!(visitor, visit_ty_unambig, default); } } V::Result::output() @@ -1109,7 +1220,7 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>( try_visit!(visitor.visit_id(impl_item.hir_id())); match *kind { ImplItemKind::Const(ref ty, body) => { - try_visit!(visitor.visit_ty(ty)); + try_visit!(visitor.visit_ty_unambig(ty)); visitor.visit_nested_body(body) } ImplItemKind::Fn(ref sig, body_id) => visitor.visit_fn( @@ -1119,7 +1230,7 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>( impl_item.span, impl_item.owner_id.def_id, ), - ImplItemKind::Type(ref ty) => visitor.visit_ty(ty), + ImplItemKind::Type(ref ty) => visitor.visit_ty_unambig(ty), } } @@ -1207,7 +1318,7 @@ pub fn walk_field_def<'v, V: Visitor<'v>>( try_visit!(visitor.visit_id(*hir_id)); try_visit!(visitor.visit_ident(*ident)); visit_opt!(visitor, visit_anon_const, default); - visitor.visit_ty(*ty) + visitor.visit_ty_unambig(*ty) } pub fn walk_enum_def<'v, V: Visitor<'v>>( @@ -1236,18 +1347,6 @@ pub fn walk_inf<'v, V: Visitor<'v>>(visitor: &mut V, inf: &'v InferArg) -> V::Re visitor.visit_id(inf.hir_id) } -pub fn walk_generic_arg<'v, V: Visitor<'v>>( - visitor: &mut V, - generic_arg: &'v GenericArg<'v>, -) -> V::Result { - match generic_arg { - GenericArg::Lifetime(lt) => visitor.visit_lifetime(lt), - GenericArg::Type(ty) => visitor.visit_ty(ty), - GenericArg::Const(ct) => visitor.visit_const_arg(ct), - GenericArg::Infer(inf) => visitor.visit_infer(inf), - } -} - pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime) -> V::Result { try_visit!(visitor.visit_id(lifetime.hir_id)); visitor.visit_ident(lifetime.ident) @@ -1260,11 +1359,11 @@ pub fn walk_qpath<'v, V: Visitor<'v>>( ) -> V::Result { match *qpath { QPath::Resolved(ref maybe_qself, ref path) => { - visit_opt!(visitor, visit_ty, maybe_qself); + visit_opt!(visitor, visit_ty_unambig, maybe_qself); visitor.visit_path(path, id) } QPath::TypeRelative(ref qself, ref segment) => { - try_visit!(visitor.visit_ty(qself)); + try_visit!(visitor.visit_ty_unambig(qself)); visitor.visit_path_segment(segment) } QPath::LangItem(..) => V::Result::output(), @@ -1304,8 +1403,8 @@ pub fn walk_assoc_item_constraint<'v, V: Visitor<'v>>( try_visit!(visitor.visit_generic_args(constraint.gen_args)); match constraint.kind { AssocItemConstraintKind::Equality { ref term } => match term { - Term::Ty(ref ty) => try_visit!(visitor.visit_ty(ty)), - Term::Const(ref c) => try_visit!(visitor.visit_const_arg(c)), + Term::Ty(ref ty) => try_visit!(visitor.visit_ty_unambig(ty)), + Term::Const(ref c) => try_visit!(visitor.visit_const_arg_unambig(c)), }, AssocItemConstraintKind::Bound { bounds } => { walk_list!(visitor, visit_param_bound, bounds) diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index fae3b778d7bdd..34c0837b25a52 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -316,6 +316,7 @@ language_item_table! { PanicAsyncFnResumedPanic, sym::panic_const_async_fn_resumed_panic, panic_const_async_fn_resumed_panic, Target::Fn, GenericRequirement::None; PanicAsyncGenFnResumedPanic, sym::panic_const_async_gen_fn_resumed_panic, panic_const_async_gen_fn_resumed_panic, Target::Fn, GenericRequirement::None; PanicGenFnNonePanic, sym::panic_const_gen_fn_none_panic, panic_const_gen_fn_none_panic, Target::Fn, GenericRequirement::None; + PanicNullPointerDereference, sym::panic_null_pointer_dereference, panic_null_pointer_dereference, Target::Fn, GenericRequirement::None; /// libstd panic entry point. Necessary for const eval to be able to catch it BeginPanic, sym::begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None; @@ -332,6 +333,10 @@ language_item_table! { FallbackSurfaceDrop, sym::fallback_surface_drop, fallback_surface_drop_fn, Target::Fn, GenericRequirement::None; AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None; + /// For all binary crates without `#![no_main]`, Rust will generate a "main" function. + /// The exact name and signature are target-dependent. The "main" function will invoke + /// this lang item, passing it the `argc` and `argv` (or null, if those don't exist + /// on the current target) as well as the user-defined `fn main` from the binary crate. Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1); EhPersonality, sym::eh_personality, eh_personality, Target::Fn, GenericRequirement::None; @@ -346,6 +351,7 @@ language_item_table! { PhantomData, sym::phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1); ManuallyDrop, sym::manually_drop, manually_drop, Target::Struct, GenericRequirement::None; + BikeshedGuaranteedNoDrop, sym::bikeshed_guaranteed_no_drop, bikeshed_guaranteed_no_drop, Target::Trait, GenericRequirement::Exact(0); MaybeUninit, sym::maybe_uninit, maybe_uninit, Target::Union, GenericRequirement::None; @@ -365,6 +371,8 @@ language_item_table! { PointerLike, sym::pointer_like, pointer_like, Target::Trait, GenericRequirement::Exact(0); + CoercePointeeValidated, sym::coerce_pointee_validated, coerce_pointee_validated_trait, Target::Trait, GenericRequirement::Exact(0); + ConstParamTy, sym::const_param_ty, const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0); UnsizedConstParamTy, sym::unsized_const_param_ty, unsized_const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0); @@ -411,13 +419,26 @@ language_item_table! { RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None; RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None; + // `new_range` types that are `Copy + IntoIterator` + RangeFromCopy, sym::RangeFromCopy, range_from_copy_struct, Target::Struct, GenericRequirement::None; + RangeCopy, sym::RangeCopy, range_copy_struct, Target::Struct, GenericRequirement::None; + RangeInclusiveCopy, sym::RangeInclusiveCopy, range_inclusive_copy_struct, Target::Struct, GenericRequirement::None; + String, sym::String, string, Target::Struct, GenericRequirement::None; CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None; + + // Experimental lang items for implementing contract pre- and post-condition checking. + ContractBuildCheckEnsures, sym::contract_build_check_ensures, contract_build_check_ensures_fn, Target::Fn, GenericRequirement::None; + ContractCheckRequires, sym::contract_check_requires, contract_check_requires_fn, Target::Fn, GenericRequirement::None; } +/// The requirement imposed on the generics of a lang item pub enum GenericRequirement { + /// No restriction on the generics None, + /// A minimum number of generics that is demanded on a lang item Minimum(usize), + /// The number of generics must match precisely as stipulated Exact(usize), } diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index 381062b142971..270d4fbec30f8 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -5,7 +5,10 @@ // tidy-alphabetical-start #![allow(internal_features)] #![feature(associated_type_defaults)] +#![feature(box_patterns)] #![feature(closure_track_caller)] +#![feature(debug_closure_helpers)] +#![feature(exhaustive_patterns)] #![feature(let_chains)] #![feature(never_type)] #![feature(rustc_attrs)] diff --git a/compiler/rustc_hir/src/pat_util.rs b/compiler/rustc_hir/src/pat_util.rs index bb853985c7d8a..8dd1e4e829274 100644 --- a/compiler/rustc_hir/src/pat_util.rs +++ b/compiler/rustc_hir/src/pat_util.rs @@ -105,7 +105,10 @@ impl hir::Pat<'_> { let mut variants = vec![]; self.walk(|p| match &p.kind { PatKind::Or(_) => false, - PatKind::Path(hir::QPath::Resolved(_, path)) + PatKind::Expr(hir::PatExpr { + kind: hir::PatExprKind::Path(hir::QPath::Resolved(_, path)), + .. + }) | PatKind::TupleStruct(hir::QPath::Resolved(_, path), ..) | PatKind::Struct(hir::QPath::Resolved(_, path), ..) => { if let Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Variant, ..), id) = diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs index db0d0fcf3b916..d7c8a3d5c0a5f 100644 --- a/compiler/rustc_hir/src/stable_hash_impls.rs +++ b/compiler/rustc_hir/src/stable_hash_impls.rs @@ -10,9 +10,7 @@ use crate::hir_id::{HirId, ItemLocalId}; /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro /// instead of implementing everything in `rustc_middle`. -pub trait HashStableContext: - rustc_ast::HashStableContext + rustc_target::HashStableContext -{ +pub trait HashStableContext: rustc_ast::HashStableContext + rustc_abi::HashStableContext { fn hash_attr(&mut self, _: &Attribute, hasher: &mut StableHasher); } diff --git a/compiler/rustc_hir/src/target.rs b/compiler/rustc_hir/src/target.rs index 6ff57396b4a19..f3e8f059c9e3c 100644 --- a/compiler/rustc_hir/src/target.rs +++ b/compiler/rustc_hir/src/target.rs @@ -106,7 +106,7 @@ impl Target { ItemKind::Use(..) => Target::Use, ItemKind::Static { .. } => Target::Static, ItemKind::Const(..) => Target::Const, - ItemKind::Fn(..) => Target::Fn, + ItemKind::Fn { .. } => Target::Fn, ItemKind::Macro(..) => Target::MacroDef, ItemKind::Mod(..) => Target::Mod, ItemKind::ForeignMod { .. } => Target::ForeignMod, diff --git a/compiler/rustc_hir/src/tests.rs b/compiler/rustc_hir/src/tests.rs index 953e48a6d3347..e0e63d183c63d 100644 --- a/compiler/rustc_hir/src/tests.rs +++ b/compiler/rustc_hir/src/tests.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] +#![allow(rustc::symbol_intern_string_literal)] use rustc_data_structures::stable_hasher::Hash64; use rustc_span::def_id::{DefPathHash, StableCrateId}; diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 0c3ed9b5c609b..5560d087e96f0 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -72,19 +72,29 @@ hir_analysis_cmse_entry_generic = functions with the `"C-cmse-nonsecure-entry"` ABI cannot contain generics in their type hir_analysis_cmse_inputs_stack_spill = - arguments for `"{$abi_name}"` function too large to pass via registers + arguments for `{$abi}` function too large to pass via registers .label = {$plural -> [false] this argument doesn't *[true] these arguments don't } fit in the available registers - .note = functions with the `"{$abi_name}"` ABI must pass all their arguments via the 4 32-bit available argument registers + .note = functions with the `{$abi}` ABI must pass all their arguments via the 4 32-bit available argument registers hir_analysis_cmse_output_stack_spill = - return value of `"{$abi_name}"` function too large to pass via registers + return value of `{$abi}` function too large to pass via registers .label = this type doesn't fit in the available registers - .note1 = functions with the `"{$abi_name}"` ABI must pass their result via the available return registers + .note1 = functions with the `{$abi}` ABI must pass their result via the available return registers .note2 = the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size +hir_analysis_coerce_pointee_no_field = `CoercePointee` can only be derived on `struct`s with at least one field + +hir_analysis_coerce_pointee_no_user_validity_assertion = asserting applicability of `derive(CoercePointee)` on a target data is forbidden + +hir_analysis_coerce_pointee_not_concrete_ty = `derive(CoercePointee)` is only applicable to `struct` + +hir_analysis_coerce_pointee_not_struct = `derive(CoercePointee)` is only applicable to `struct`, instead of `{$kind}` + +hir_analysis_coerce_pointee_not_transparent = `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout + hir_analysis_coerce_unsized_may = the trait `{$trait_name}` may only be implemented for a coercion between structures hir_analysis_coerce_unsized_multi = implementing the trait `CoerceUnsized` requires multiple coercions @@ -135,7 +145,7 @@ hir_analysis_dispatch_from_dyn_multi = implementing the `DispatchFromDyn` trait hir_analysis_dispatch_from_dyn_repr = structs implementing `DispatchFromDyn` may not have `#[repr(packed)]` or `#[repr(C)]` -hir_analysis_dispatch_from_dyn_zst = the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment, and nothing else +hir_analysis_dispatch_from_dyn_zst = the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment that don't mention type/const generics, and nothing else .note = extra field `{$name}` of type `{$ty}` is not allowed hir_analysis_drop_impl_negative = negative `Drop` impls are not supported @@ -234,6 +244,9 @@ hir_analysis_inherent_ty_outside_relevant = cannot define inherent `impl` for a .help = consider moving this inherent impl into the crate defining the type if possible .span_help = alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items +hir_analysis_invalid_base_type = `{$ty}` is not a valid base type for range patterns + .note = range patterns only support integers + hir_analysis_invalid_generic_receiver_ty = invalid generic `self` parameter type: `{$receiver_ty}` .note = type of `self` must not be a method generic parameter type @@ -353,7 +366,13 @@ hir_analysis_missing_type_params = [one] reference *[other] references } to {$parameters} - .note = because of the default `Self` reference, type parameters must be specified on object types + .note = because the parameter {$parameterCount -> + [one] default references + *[other] defaults reference + } `Self`, the {$parameterCount -> + [one] parameter + *[other] parameters + } must be specified on the object type hir_analysis_multiple_relaxed_default_bounds = type parameter has more than one relaxed default bound, only one is supported @@ -427,12 +446,8 @@ hir_analysis_paren_sugar_attribute = the `#[rustc_paren_sugar]` attribute is a t hir_analysis_parenthesized_fn_trait_expansion = parenthesized trait syntax expands to `{$expanded_type}` -hir_analysis_pattern_type_non_const_range = range patterns must have constant range start and end -hir_analysis_pattern_type_wild_pat = wildcard patterns are not permitted for pattern types - .label = this type is the same as the inner type without a pattern hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind} .label = not allowed in type signatures - hir_analysis_precise_capture_self_alias = `Self` can't be captured in `use<...>` precise captures list, since it is an alias .label = `Self` is not a generic argument, but an alias to the type of the {$what} @@ -489,22 +504,13 @@ hir_analysis_simd_ffi_highly_experimental = use of SIMD type{$snip} in FFI is hi hir_analysis_specialization_trait = implementing `rustc_specialization_trait` traits is unstable .help = add `#![feature(min_specialization)]` to the crate attributes to enable -hir_analysis_start_function_parameters = `#[start]` function is not allowed to have type parameters - .label = `#[start]` function cannot have type parameters - -hir_analysis_start_function_where = `#[start]` function is not allowed to have a `where` clause - .label = `#[start]` function cannot have a `where` clause - -hir_analysis_start_not_async = `#[start]` function is not allowed to be `async` - .label = `#[start]` is not allowed to be `async` +hir_analysis_static_specialize = cannot specialize on `'static` lifetime -hir_analysis_start_not_target_feature = `#[start]` function is not allowed to have `#[target_feature]` - .label = `#[start]` function is not allowed to have `#[target_feature]` +hir_analysis_supertrait_item_multiple_shadowee = items from several supertraits are shadowed: {$traits} -hir_analysis_start_not_track_caller = `#[start]` function is not allowed to be `#[track_caller]` - .label = `#[start]` function is not allowed to be `#[track_caller]` +hir_analysis_supertrait_item_shadowee = item from `{$supertrait}` is shadowed by a subtrait item -hir_analysis_static_specialize = cannot specialize on `'static` lifetime +hir_analysis_supertrait_item_shadowing = trait item `{$item}` from `{$subtrait}` shadows identically named item from supertrait hir_analysis_tait_forward_compat = item constrains opaque type that is not in its signature .note = this item must mention the opaque type in its signature in order to be able to register hidden types @@ -602,7 +608,7 @@ hir_analysis_value_of_associated_struct_already_specified = .label = re-bound here .previous_bound_label = `{$item_name}` bound here first -hir_analysis_variadic_function_compatible_convention = C-variadic function must have a compatible calling convention, like `C`, `cdecl`, `system`, `aapcs`, `win64`, `sysv64` or `efiapi` +hir_analysis_variadic_function_compatible_convention = C-variadic function must have a compatible calling convention, like {$conventions} .label = C-variadic function must have a compatible calling convention hir_analysis_variances_of = {$variances} diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index d8e9227a87c86..b3eade8c8ae47 100644 --- a/compiler/rustc_hir_analysis/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -86,7 +86,7 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> { if self.infcx.next_trait_solver() && let ty::Alias(..) = ty.kind() { - let (normalized_ty, obligations) = self.structurally_normalize(ty)?; + let (normalized_ty, obligations) = self.structurally_normalize_ty(ty)?; self.state.obligations.extend(obligations); (AutoderefKind::Builtin, normalized_ty) } else { @@ -166,7 +166,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { } let (normalized_ty, obligations) = - self.structurally_normalize(Ty::new_projection(tcx, trait_target_def_id, [ty]))?; + self.structurally_normalize_ty(Ty::new_projection(tcx, trait_target_def_id, [ty]))?; debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations); self.state.obligations.extend(obligations); @@ -174,12 +174,12 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { } #[instrument(level = "debug", skip(self), ret)] - pub fn structurally_normalize( + pub fn structurally_normalize_ty( &self, ty: Ty<'tcx>, ) -> Option<(Ty<'tcx>, PredicateObligations<'tcx>)> { let ocx = ObligationCtxt::new(self.infcx); - let Ok(normalized_ty) = ocx.structurally_normalize( + let Ok(normalized_ty) = ocx.structurally_normalize_ty( &traits::ObligationCause::misc(self.span, self.body_id), self.param_env, ty, diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 5548a6a6ef797..71a10ad3a0c10 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -6,7 +6,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::MultiSpan; use rustc_errors::codes::*; use rustc_hir::def::{CtorKind, DefKind}; -use rustc_hir::{Node, intravisit}; +use rustc_hir::{LangItem, Node, intravisit}; use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::{Obligation, ObligationCauseCode}; use rustc_lint_defs::builtin::{ @@ -19,7 +19,7 @@ use rustc_middle::span_bug; use rustc_middle::ty::error::TypeErrorToStringExt; use rustc_middle::ty::fold::{BottomUpFolder, fold_regions}; use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES}; -use rustc_middle::ty::util::{Discr, InspectCoroutineFields, IntTypeExt}; +use rustc_middle::ty::util::{Discr, IntTypeExt}; use rustc_middle::ty::{ AdtDef, GenericArgKind, RegionKind, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; @@ -27,7 +27,7 @@ use rustc_session::lint::builtin::UNINHABITED_STATIC; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedDirective; use rustc_trait_selection::traits; -use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_type_ir::fold::TypeFoldable; use tracing::{debug, instrument}; use ty::TypingMode; @@ -88,89 +88,76 @@ fn allowed_union_or_unsafe_field<'tcx>( typing_env: ty::TypingEnv<'tcx>, span: Span, ) -> bool { - // We don't just accept all !needs_drop fields, due to semver concerns. - let allowed = match ty.kind() { - ty::Ref(..) => true, // references never drop (even mutable refs, which are non-Copy and hence fail the later check) - ty::Tuple(tys) => { - // allow tuples of allowed types - tys.iter().all(|ty| allowed_union_or_unsafe_field(tcx, ty, typing_env, span)) - } - ty::Array(elem, _len) => { - // Like `Copy`, we do *not* special-case length 0. - allowed_union_or_unsafe_field(tcx, *elem, typing_env, span) - } - _ => { - // Fallback case: allow `ManuallyDrop` and things that are `Copy`, - // also no need to report an error if the type is unresolved. - ty.ty_adt_def().is_some_and(|adt_def| adt_def.is_manually_drop()) - || tcx.type_is_copy_modulo_regions(typing_env, ty) - || ty.references_error() - } - }; - if allowed && ty.needs_drop(tcx, typing_env) { - // This should never happen. But we can get here e.g. in case of name resolution errors. - tcx.dcx() - .span_delayed_bug(span, "we should never accept maybe-dropping union or unsafe fields"); + // HACK (not that bad of a hack don't worry): Some codegen tests don't even define proper + // impls for `Copy`. Let's short-circuit here for this validity check, since a lot of them + // use unions. We should eventually fix all the tests to define that lang item or use + // minicore stubs. + if ty.is_trivially_pure_clone_copy() { + return true; } - allowed + // If `BikeshedGuaranteedNoDrop` is not defined in a `#[no_core]` test, fall back to `Copy`. + // This is an underapproximation of `BikeshedGuaranteedNoDrop`, + let def_id = tcx + .lang_items() + .get(LangItem::BikeshedGuaranteedNoDrop) + .unwrap_or_else(|| tcx.require_lang_item(LangItem::Copy, Some(span))); + let Ok(ty) = tcx.try_normalize_erasing_regions(typing_env, ty) else { + tcx.dcx().span_delayed_bug(span, "could not normalize field type"); + return true; + }; + let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); + infcx.predicate_must_hold_modulo_regions(&Obligation::new( + tcx, + ObligationCause::dummy_with_span(span), + param_env, + ty::TraitRef::new(tcx, def_id, [ty]), + )) } /// Check that the fields of the `union` do not need dropping. fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool { - let item_type = tcx.type_of(item_def_id).instantiate_identity(); - if let ty::Adt(def, args) = item_type.kind() { - assert!(def.is_union()); - - let typing_env = ty::TypingEnv::non_body_analysis(tcx, item_def_id); - for field in &def.non_enum_variant().fields { - let Ok(field_ty) = tcx.try_normalize_erasing_regions(typing_env, field.ty(tcx, args)) - else { - tcx.dcx().span_delayed_bug(span, "could not normalize field type"); - continue; - }; + let def = tcx.adt_def(item_def_id); + assert!(def.is_union()); - if !allowed_union_or_unsafe_field(tcx, field_ty, typing_env, span) { - let (field_span, ty_span) = match tcx.hir().get_if_local(field.did) { - // We are currently checking the type this field came from, so it must be local. - Some(Node::Field(field)) => (field.span, field.ty.span), - _ => unreachable!("mir field has to correspond to hir field"), - }; - tcx.dcx().emit_err(errors::InvalidUnionField { - field_span, - sugg: errors::InvalidUnionFieldSuggestion { - lo: ty_span.shrink_to_lo(), - hi: ty_span.shrink_to_hi(), - }, - note: (), - }); - return false; - } + let typing_env = ty::TypingEnv::non_body_analysis(tcx, item_def_id); + let args = ty::GenericArgs::identity_for_item(tcx, item_def_id); + + for field in &def.non_enum_variant().fields { + if !allowed_union_or_unsafe_field(tcx, field.ty(tcx, args), typing_env, span) { + let (field_span, ty_span) = match tcx.hir().get_if_local(field.did) { + // We are currently checking the type this field came from, so it must be local. + Some(Node::Field(field)) => (field.span, field.ty.span), + _ => unreachable!("mir field has to correspond to hir field"), + }; + tcx.dcx().emit_err(errors::InvalidUnionField { + field_span, + sugg: errors::InvalidUnionFieldSuggestion { + lo: ty_span.shrink_to_lo(), + hi: ty_span.shrink_to_hi(), + }, + note: (), + }); + return false; } - } else { - span_bug!(span, "unions must be ty::Adt, but got {:?}", item_type.kind()); } + true } /// Check that the unsafe fields do not need dropping. fn check_unsafe_fields(tcx: TyCtxt<'_>, item_def_id: LocalDefId) { let span = tcx.def_span(item_def_id); - let item_type = tcx.type_of(item_def_id).instantiate_identity(); - let ty::Adt(def, args) = item_type.kind() else { - span_bug!(span, "structs/enums must be ty::Adt, but got {:?}", item_type.kind()); - }; + let def = tcx.adt_def(item_def_id); + let typing_env = ty::TypingEnv::non_body_analysis(tcx, item_def_id); + let args = ty::GenericArgs::identity_for_item(tcx, item_def_id); + for field in def.all_fields() { if !field.safety.is_unsafe() { continue; } - let Ok(field_ty) = tcx.try_normalize_erasing_regions(typing_env, field.ty(tcx, args)) - else { - tcx.dcx().span_delayed_bug(span, "could not normalize field type"); - continue; - }; - if !allowed_union_or_unsafe_field(tcx, field_ty, typing_env, span) { + if !allowed_union_or_unsafe_field(tcx, field.ty(tcx, args), typing_env, span) { let hir::Node::Field(field) = tcx.hir_node_by_def_id(field.did.expect_local()) else { unreachable!("field has to correspond to hir field") }; @@ -258,30 +245,9 @@ pub(super) fn check_opaque_for_cycles<'tcx>( // First, try to look at any opaque expansion cycles, considering coroutine fields // (even though these aren't necessarily true errors). - if tcx - .try_expand_impl_trait_type(def_id.to_def_id(), args, InspectCoroutineFields::Yes) - .is_err() - { - // Look for true opaque expansion cycles, but ignore coroutines. - // This will give us any true errors. Coroutines are only problematic - // if they cause layout computation errors. - if tcx - .try_expand_impl_trait_type(def_id.to_def_id(), args, InspectCoroutineFields::No) - .is_err() - { - let reported = opaque_type_cycle_error(tcx, def_id); - return Err(reported); - } - - // And also look for cycle errors in the layout of coroutines. - if let Err(&LayoutError::Cycle(guar)) = - tcx.layout_of( - ty::TypingEnv::post_analysis(tcx, def_id.to_def_id()) - .as_query_input(Ty::new_opaque(tcx, def_id.to_def_id(), args)), - ) - { - return Err(guar); - } + if tcx.try_expand_impl_trait_type(def_id.to_def_id(), args).is_err() { + let reported = opaque_type_cycle_error(tcx, def_id); + return Err(reported); } Ok(()) @@ -417,9 +383,7 @@ fn check_opaque_meets_bounds<'tcx>( } let wf_tys = ocx.assumed_wf_types_and_report_errors(param_env, defining_use_anchor)?; - let implied_bounds = infcx.implied_bounds_tys(param_env, def_id, &wf_tys); - let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); - ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?; + ocx.resolve_regions_and_report_errors(defining_use_anchor, param_env, wf_tys)?; if infcx.next_trait_solver() { Ok(()) @@ -436,9 +400,9 @@ fn check_opaque_meets_bounds<'tcx>( } else { // Check that any hidden types found during wf checking match the hidden types that `type_of` sees. for (mut key, mut ty) in infcx.take_opaque_types() { - ty.hidden_type.ty = infcx.resolve_vars_if_possible(ty.hidden_type.ty); + ty.ty = infcx.resolve_vars_if_possible(ty.ty); key = infcx.resolve_vars_if_possible(key); - sanity_check_found_hidden_type(tcx, key, ty.hidden_type)?; + sanity_check_found_hidden_type(tcx, key, ty)?; } Ok(()) } @@ -575,7 +539,7 @@ fn sanity_check_found_hidden_type<'tcx>( } else { let span = tcx.def_span(key.def_id); let other = ty::OpaqueHiddenType { ty: hidden_ty, span }; - Err(ty.build_mismatch_error(&other, key.def_id, tcx)?.emit()) + Err(ty.build_mismatch_error(&other, tcx)?.emit()) } } @@ -781,13 +745,13 @@ fn check_static_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) { pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) { match tcx.def_kind(def_id) { DefKind::Static { .. } => { - tcx.ensure().typeck(def_id); + tcx.ensure_ok().typeck(def_id); maybe_check_static_with_link_section(tcx, def_id); check_static_inhabited(tcx, def_id); check_static_linkage(tcx, def_id); } DefKind::Const => { - tcx.ensure().typeck(def_id); + tcx.ensure_ok().typeck(def_id); } DefKind::Enum => { check_enum(tcx, def_id); @@ -807,7 +771,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) { DefKind::Impl { of_trait } => { if of_trait && let Some(impl_trait_header) = tcx.impl_trait_header(def_id) { if tcx - .ensure() + .ensure_ok() .coherent_trait(impl_trait_header.trait_ref.instantiate_identity().def_id) .is_ok() { @@ -1045,7 +1009,7 @@ fn check_impl_items_against_trait<'tcx>( continue; }; - let res = tcx.ensure().compare_impl_item(impl_item.expect_local()); + let res = tcx.ensure_ok().compare_impl_item(impl_item.expect_local()); if res.is_ok() { match ty_impl_item.kind { @@ -1491,7 +1455,7 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) { for v in def.variants() { if let ty::VariantDiscr::Explicit(discr_def_id) = v.discr { - tcx.ensure().typeck(discr_def_id.expect_local()); + tcx.ensure_ok().typeck(discr_def_id.expect_local()); } } @@ -1637,7 +1601,6 @@ fn check_type_alias_type_params_are_used<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalD let ty = tcx.type_of(def_id).instantiate_identity(); if ty.references_error() { // If there is already another error, do not emit an error for not using a type parameter. - assert!(tcx.dcx().has_errors().is_some()); return; } @@ -1666,7 +1629,7 @@ fn check_type_alias_type_params_are_used<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalD .collect::>() }); - let mut params_used = BitSet::new_empty(generics.own_params.len()); + let mut params_used = DenseBitSet::new_empty(generics.own_params.len()); for leaf in ty.walk() { if let GenericArgKind::Type(leaf_ty) = leaf.unpack() && let ty::Param(param) = leaf_ty.kind() @@ -1845,13 +1808,18 @@ pub(super) fn check_coroutine_obligations( debug!(?typeck_results.coroutine_stalled_predicates); + let mode = if tcx.next_trait_solver_globally() { + TypingMode::post_borrowck_analysis(tcx, def_id) + } else { + TypingMode::analysis_in_body(tcx, def_id) + }; + let infcx = tcx .infer_ctxt() // typeck writeback gives us predicates with their regions erased. // As borrowck already has checked lifetimes, we do not need to do it again. .ignoring_regions() - // FIXME(#132279): This should eventually use the already defined hidden types. - .build(TypingMode::analysis_in_body(tcx, def_id)); + .build(mode); let ocx = ObligationCtxt::new_with_diagnostics(&infcx); for (predicate, cause) in &typeck_results.coroutine_stalled_predicates { @@ -1864,12 +1832,14 @@ pub(super) fn check_coroutine_obligations( return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); } - // Check that any hidden types found when checking these stalled coroutine obligations - // are valid. - for (key, ty) in infcx.take_opaque_types() { - let hidden_type = infcx.resolve_vars_if_possible(ty.hidden_type); - let key = infcx.resolve_vars_if_possible(key); - sanity_check_found_hidden_type(tcx, key, hidden_type)?; + if !tcx.next_trait_solver_globally() { + // Check that any hidden types found when checking these stalled coroutine obligations + // are valid. + for (key, ty) in infcx.take_opaque_types() { + let hidden_type = infcx.resolve_vars_if_possible(ty); + let key = infcx.resolve_vars_if_possible(key); + sanity_check_found_hidden_type(tcx, key, hidden_type)?; + } } Ok(()) diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index a6b504de3dac1..bb5087e864cb0 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -6,10 +6,9 @@ use hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err}; -use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{GenericParamKind, ImplItemKind, intravisit}; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; +use rustc_hir::intravisit::VisitorExt; +use rustc_hir::{self as hir, AmbigArg, GenericParamKind, ImplItemKind, intravisit}; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::util; use rustc_middle::ty::error::{ExpectedFound, TypeError}; @@ -24,7 +23,6 @@ use rustc_span::Span; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::regions::InferCtxtRegionExt; -use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::{ self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt, }; @@ -184,12 +182,15 @@ fn compare_method_predicate_entailment<'tcx>( // obligations. let impl_m_def_id = impl_m.def_id.expect_local(); let impl_m_span = tcx.def_span(impl_m_def_id); - let cause = - ObligationCause::new(impl_m_span, impl_m_def_id, ObligationCauseCode::CompareImplItem { + let cause = ObligationCause::new( + impl_m_span, + impl_m_def_id, + ObligationCauseCode::CompareImplItem { impl_item_def_id: impl_m_def_id, trait_item_def_id: trait_m.def_id, kind: impl_m.kind, - }); + }, + ); // Create mapping from trait method to impl method. let impl_def_id = impl_m.container_id(tcx); @@ -250,12 +251,15 @@ fn compare_method_predicate_entailment<'tcx>( let normalize_cause = traits::ObligationCause::misc(span, impl_m_def_id); let predicate = ocx.normalize(&normalize_cause, param_env, predicate); - let cause = - ObligationCause::new(span, impl_m_def_id, ObligationCauseCode::CompareImplItem { + let cause = ObligationCause::new( + span, + impl_m_def_id, + ObligationCauseCode::CompareImplItem { impl_item_def_id: impl_m_def_id, trait_item_def_id: trait_m.def_id, kind: impl_m.kind, - }); + }, + ); ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, predicate)); } @@ -272,12 +276,15 @@ fn compare_method_predicate_entailment<'tcx>( let normalize_cause = traits::ObligationCause::misc(span, impl_m_def_id); let const_condition = ocx.normalize(&normalize_cause, param_env, const_condition); - let cause = - ObligationCause::new(span, impl_m_def_id, ObligationCauseCode::CompareImplItem { + let cause = ObligationCause::new( + span, + impl_m_def_id, + ObligationCauseCode::CompareImplItem { impl_item_def_id: impl_m_def_id, trait_item_def_id: trait_m.def_id, kind: impl_m.kind, - }); + }, + ); ocx.register_obligation(traits::Obligation::new( tcx, cause, @@ -416,11 +423,7 @@ fn compare_method_predicate_entailment<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. - let outlives_env = OutlivesEnvironment::with_bounds( - param_env, - infcx.implied_bounds_tys(param_env, impl_m_def_id, &wf_tys), - ); - let errors = infcx.resolve_regions(&outlives_env); + let errors = infcx.resolve_regions(impl_m_def_id, param_env, wf_tys); if !errors.is_empty() { return Err(infcx .tainted_by_errors() @@ -430,12 +433,12 @@ fn compare_method_predicate_entailment<'tcx>( Ok(()) } -struct RemapLateParam<'a, 'tcx> { +struct RemapLateParam<'tcx> { tcx: TyCtxt<'tcx>, - mapping: &'a FxIndexMap, + mapping: FxIndexMap, } -impl<'tcx> TypeFolder> for RemapLateParam<'_, 'tcx> { +impl<'tcx> TypeFolder> for RemapLateParam<'tcx> { fn cx(&self) -> TyCtxt<'tcx> { self.tcx } @@ -499,12 +502,15 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( let impl_m_hir_id = tcx.local_def_id_to_hir_id(impl_m_def_id); let return_span = tcx.hir().fn_decl_by_hir_id(impl_m_hir_id).unwrap().output.span(); - let cause = - ObligationCause::new(return_span, impl_m_def_id, ObligationCauseCode::CompareImplItem { + let cause = ObligationCause::new( + return_span, + impl_m_def_id, + ObligationCauseCode::CompareImplItem { impl_item_def_id: impl_m_def_id, trait_item_def_id: trait_m.def_id, kind: impl_m.kind, - }); + }, + ); // Create mapping from trait to impl (i.e. impl trait header + impl method identity args). let trait_to_impl_args = GenericArgs::identity_for_item(tcx, impl_m.def_id).rebase_onto( @@ -529,6 +535,29 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( let infcx = &tcx.infer_ctxt().build(TypingMode::non_body_analysis()); let ocx = ObligationCtxt::new_with_diagnostics(infcx); + // Check that the where clauses of the impl are satisfied by the hybrid param env. + // You might ask -- what does this have to do with RPITIT inference? Nothing. + // We check these because if the where clauses of the signatures do not match + // up, then we don't want to give spurious other errors that point at the RPITITs. + // They're not necessary to check, though, because we already check them in + // `compare_method_predicate_entailment`. + let impl_m_own_bounds = tcx.predicates_of(impl_m_def_id).instantiate_own_identity(); + for (predicate, span) in impl_m_own_bounds { + let normalize_cause = traits::ObligationCause::misc(span, impl_m_def_id); + let predicate = ocx.normalize(&normalize_cause, param_env, predicate); + + let cause = ObligationCause::new( + span, + impl_m_def_id, + ObligationCauseCode::CompareImplItem { + impl_item_def_id: impl_m_def_id, + trait_item_def_id: trait_m.def_id, + kind: impl_m.kind, + }, + ); + ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, predicate)); + } + // Normalize the impl signature with fresh variables for lifetime inference. let misc_cause = ObligationCause::misc(return_span, impl_m_def_id); let impl_sig = ocx.normalize( @@ -592,13 +621,16 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( idx += 1; ( ty, - Ty::new_placeholder(tcx, ty::Placeholder { - universe, - bound: ty::BoundTy { - var: ty::BoundVar::from_usize(idx), - kind: ty::BoundTyKind::Anon, + Ty::new_placeholder( + tcx, + ty::Placeholder { + universe, + bound: ty::BoundTy { + var: ty::BoundVar::from_usize(idx), + kind: ty::BoundTyKind::Anon, + }, }, - }), + ), ) }) .collect(); @@ -639,6 +671,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( }))), terr, false, + None, ); return Err(diag.emit()); } @@ -705,11 +738,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. - let outlives_env = OutlivesEnvironment::with_bounds( - param_env, - infcx.implied_bounds_tys(param_env, impl_m_def_id, &wf_tys), - ); - ocx.resolve_regions_and_report_errors(impl_m_def_id, &outlives_env)?; + ocx.resolve_regions_and_report_errors(impl_m_def_id, param_env, wf_tys)?; let mut remapped_types = DefIdMap::default(); for (def_id, (ty, args)) in collected_types { @@ -958,10 +987,13 @@ impl<'tcx> ty::FallibleTypeFolder> for RemapHiddenTyRegions<'tcx> { return Err(guar); }; - Ok(ty::Region::new_early_param(self.tcx, ty::EarlyParamRegion { - name: e.name, - index: (e.index as usize - self.num_trait_args + self.num_impl_args) as u32, - })) + Ok(ty::Region::new_early_param( + self.tcx, + ty::EarlyParamRegion { + name: e.name, + index: (e.index as usize - self.num_trait_args + self.num_impl_args) as u32, + }, + )) } } @@ -1060,6 +1092,7 @@ fn report_trait_method_mismatch<'tcx>( }))), terr, false, + None, ); diag.emit() @@ -1590,7 +1623,7 @@ fn compare_synthetic_generics<'tcx>( struct Visitor(hir::def_id::LocalDefId); impl<'v> intravisit::Visitor<'v> for Visitor { type Result = ControlFlow; - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) -> Self::Result { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) -> Self::Result { if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = ty.kind && let Res::Def(DefKind::TyParam, def_id) = path.res && def_id == self.0.to_def_id() @@ -1602,9 +1635,9 @@ fn compare_synthetic_generics<'tcx>( } } - let span = input_tys.iter().find_map(|ty| { - intravisit::Visitor::visit_ty(&mut Visitor(impl_def_id), ty).break_value() - })?; + let span = input_tys + .iter() + .find_map(|ty| Visitor(impl_def_id).visit_ty_unambig(ty).break_value())?; let bounds = impl_m.generics.bounds_for_param(impl_def_id).next()?.bounds; let bounds = bounds.first()?.span().to(bounds.last()?.span()); @@ -1852,6 +1885,7 @@ fn compare_const_predicate_entailment<'tcx>( }))), terr, false, + None, ); return Err(diag.emit()); }; @@ -1863,8 +1897,7 @@ fn compare_const_predicate_entailment<'tcx>( return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); } - let outlives_env = OutlivesEnvironment::new(param_env); - ocx.resolve_regions_and_report_errors(impl_ct_def_id, &outlives_env) + ocx.resolve_regions_and_report_errors(impl_ct_def_id, param_env, []) } #[instrument(level = "debug", skip(tcx))] @@ -1955,12 +1988,15 @@ fn compare_type_predicate_entailment<'tcx>( let cause = ObligationCause::misc(span, impl_ty_def_id); let predicate = ocx.normalize(&cause, param_env, predicate); - let cause = - ObligationCause::new(span, impl_ty_def_id, ObligationCauseCode::CompareImplItem { + let cause = ObligationCause::new( + span, + impl_ty_def_id, + ObligationCauseCode::CompareImplItem { impl_item_def_id: impl_ty.def_id.expect_local(), trait_item_def_id: trait_ty.def_id, kind: impl_ty.kind, - }); + }, + ); ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, predicate)); } @@ -1972,12 +2008,15 @@ fn compare_type_predicate_entailment<'tcx>( let normalize_cause = traits::ObligationCause::misc(span, impl_ty_def_id); let const_condition = ocx.normalize(&normalize_cause, param_env, const_condition); - let cause = - ObligationCause::new(span, impl_ty_def_id, ObligationCauseCode::CompareImplItem { + let cause = ObligationCause::new( + span, + impl_ty_def_id, + ObligationCauseCode::CompareImplItem { impl_item_def_id: impl_ty_def_id, trait_item_def_id: trait_ty.def_id, kind: impl_ty.kind, - }); + }, + ); ocx.register_obligation(traits::Obligation::new( tcx, cause, @@ -1997,8 +2036,7 @@ fn compare_type_predicate_entailment<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. - let outlives_env = OutlivesEnvironment::new(param_env); - ocx.resolve_regions_and_report_errors(impl_ty_def_id, &outlives_env) + ocx.resolve_regions_and_report_errors(impl_ty_def_id, param_env, []) } /// Validate that `ProjectionCandidate`s created for this associated type will @@ -2023,7 +2061,7 @@ pub(super) fn check_type_bounds<'tcx>( ) -> Result<(), ErrorGuaranteed> { // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case // other `Foo` impls are incoherent. - tcx.ensure().coherent_trait(impl_trait_ref.def_id)?; + tcx.ensure_ok().coherent_trait(impl_trait_ref.def_id)?; let param_env = tcx.param_env(impl_ty.def_id); debug!(?param_env); @@ -2127,9 +2165,7 @@ pub(super) fn check_type_bounds<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. - let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_def_id, &assumed_wf_types); - let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); - ocx.resolve_regions_and_report_errors(impl_ty_def_id, &outlives_env) + ocx.resolve_regions_and_report_errors(impl_ty_def_id, param_env, assumed_wf_types) } struct ReplaceTy<'tcx> { @@ -2235,20 +2271,25 @@ fn param_env_with_gat_bounds<'tcx>( let kind = ty::BoundTyKind::Param(param.def_id, param.name); let bound_var = ty::BoundVariableKind::Ty(kind); bound_vars.push(bound_var); - Ty::new_bound(tcx, ty::INNERMOST, ty::BoundTy { - var: ty::BoundVar::from_usize(bound_vars.len() - 1), - kind, - }) + Ty::new_bound( + tcx, + ty::INNERMOST, + ty::BoundTy { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind }, + ) .into() } GenericParamDefKind::Lifetime => { let kind = ty::BoundRegionKind::Named(param.def_id, param.name); let bound_var = ty::BoundVariableKind::Region(kind); bound_vars.push(bound_var); - ty::Region::new_bound(tcx, ty::INNERMOST, ty::BoundRegion { - var: ty::BoundVar::from_usize(bound_vars.len() - 1), - kind, - }) + ty::Region::new_bound( + tcx, + ty::INNERMOST, + ty::BoundRegion { + var: ty::BoundVar::from_usize(bound_vars.len() - 1), + kind, + }, + ) .into() } GenericParamDefKind::Const { .. } => { @@ -2342,7 +2383,7 @@ fn try_report_async_mismatch<'tcx>( // the right span is a bit difficult. return Err(tcx.sess.dcx().emit_err(MethodShouldReturnFuture { span: tcx.def_span(impl_m.def_id), - method_name: trait_m.name, + method_name: tcx.item_ident(impl_m.def_id), trait_item_span: tcx.hir().span_if_local(trait_m.def_id), })); } diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs index 2b14594ea1bf4..0e9e9b48ab309 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs @@ -3,7 +3,6 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_lint_defs::builtin::{REFINING_IMPL_TRAIT_INTERNAL, REFINING_IMPL_TRAIT_REACHABLE}; use rustc_middle::span_bug; use rustc_middle::traits::ObligationCause; @@ -13,7 +12,6 @@ use rustc_middle::ty::{ }; use rustc_span::Span; use rustc_trait_selection::regions::InferCtxtRegionExt; -use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt; use rustc_trait_selection::traits::{ObligationCtxt, elaborate, normalize_param_env_or_error}; /// Check that an implementation does not refine an RPITIT from a trait method signature. @@ -170,11 +168,7 @@ pub(crate) fn check_refining_return_position_impl_trait_in_trait<'tcx>( tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (selection)"); return; } - let outlives_env = OutlivesEnvironment::with_bounds( - param_env, - infcx.implied_bounds_tys(param_env, impl_m.def_id.expect_local(), &implied_wf_types), - ); - let errors = infcx.resolve_regions(&outlives_env); + let errors = infcx.resolve_regions(impl_m.def_id.expect_local(), param_env, implied_wf_types); if !errors.is_empty() { tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (regions)"); return; @@ -305,8 +299,7 @@ fn report_mismatched_rpitit_signature<'tcx>( }) .collect(); - let mut return_ty = - trait_m_sig.output().fold_with(&mut super::RemapLateParam { tcx, mapping: &mapping }); + let mut return_ty = trait_m_sig.output().fold_with(&mut super::RemapLateParam { tcx, mapping }); if tcx.asyncness(impl_m_def_id).is_async() && tcx.asyncness(trait_m_def_id).is_async() { let ty::Alias(ty::Projection, future_ty) = return_ty.kind() else { diff --git a/compiler/rustc_hir_analysis/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs index 1c9bbe627fb07..d7dfe482da449 100644 --- a/compiler/rustc_hir_analysis/src/check/dropck.rs +++ b/compiler/rustc_hir_analysis/src/check/dropck.rs @@ -1,11 +1,6 @@ -// FIXME(@lcnr): Move this module out of `rustc_hir_analysis`. -// -// We don't do any drop checking during hir typeck. - use rustc_data_structures::fx::FxHashSet; use rustc_errors::codes::*; use rustc_errors::{ErrorGuaranteed, struct_span_code_err}; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt}; use rustc_infer::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::util::CheckRegions; @@ -33,7 +28,10 @@ use crate::hir::def_id::{DefId, LocalDefId}; /// struct/enum definition for the nominal type itself (i.e. /// cannot do `struct S; impl Drop for S { ... }`). /// -pub fn check_drop_impl(tcx: TyCtxt<'_>, drop_impl_did: DefId) -> Result<(), ErrorGuaranteed> { +pub(crate) fn check_drop_impl( + tcx: TyCtxt<'_>, + drop_impl_did: DefId, +) -> Result<(), ErrorGuaranteed> { match tcx.impl_polarity(drop_impl_did) { ty::ImplPolarity::Positive => {} ty::ImplPolarity::Negative => { @@ -192,7 +190,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( return Err(guar.unwrap()); } - let errors = ocx.infcx.resolve_regions(&OutlivesEnvironment::new(adt_env)); + let errors = ocx.infcx.resolve_regions(adt_def_id, adt_env, []); if !errors.is_empty() { let mut guar = None; for error in errors { diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs index 245085b332c8e..25c2f8554b7f4 100644 --- a/compiler/rustc_hir_analysis/src/check/entry.rs +++ b/compiler/rustc_hir_analysis/src/check/entry.rs @@ -5,7 +5,7 @@ use rustc_hir as hir; use rustc_hir::Node; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::span_bug; -use rustc_middle::ty::{self, Ty, TyCtxt, TypingMode}; +use rustc_middle::ty::{self, TyCtxt, TypingMode}; use rustc_session::config::EntryFnType; use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; use rustc_span::{Span, sym}; @@ -18,7 +18,6 @@ use crate::errors; pub(crate) fn check_for_entry_fn(tcx: TyCtxt<'_>) { match tcx.entry_fn(()) { Some((def_id, EntryFnType::Main { .. })) => check_main_fn_ty(tcx, def_id), - Some((def_id, EntryFnType::Start)) => check_start_fn_ty(tcx, def_id), _ => {} } } @@ -44,7 +43,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { return None; } match tcx.hir_node_by_def_id(def_id.expect_local()) { - Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, generics, _), .. }) => { + Node::Item(hir::Item { kind: hir::ItemKind::Fn { generics, .. }, .. }) => { generics.params.is_empty().not().then_some(generics.span) } _ => { @@ -58,7 +57,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { return None; } match tcx.hir_node_by_def_id(def_id.expect_local()) { - Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, generics, _), .. }) => { + Node::Item(hir::Item { kind: hir::ItemKind::Fn { generics, .. }, .. }) => { Some(generics.where_clause_span) } _ => { @@ -79,7 +78,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { return None; } match tcx.hir_node_by_def_id(def_id.expect_local()) { - Node::Item(hir::Item { kind: hir::ItemKind::Fn(fn_sig, _, _), .. }) => { + Node::Item(hir::Item { kind: hir::ItemKind::Fn { sig: fn_sig, .. }, .. }) => { Some(fn_sig.decl.output.span()) } _ => { @@ -192,83 +191,3 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { }); } } - -fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) { - let start_def_id = start_def_id.expect_local(); - let start_id = tcx.local_def_id_to_hir_id(start_def_id); - let start_span = tcx.def_span(start_def_id); - let start_t = tcx.type_of(start_def_id).instantiate_identity(); - match start_t.kind() { - ty::FnDef(..) => { - if let Node::Item(it) = tcx.hir_node(start_id) { - if let hir::ItemKind::Fn(sig, generics, _) = &it.kind { - let mut error = false; - if !generics.params.is_empty() { - tcx.dcx().emit_err(errors::StartFunctionParameters { span: generics.span }); - error = true; - } - if generics.has_where_clause_predicates { - tcx.dcx().emit_err(errors::StartFunctionWhere { - span: generics.where_clause_span, - }); - error = true; - } - if sig.header.asyncness.is_async() { - let span = tcx.def_span(it.owner_id); - tcx.dcx().emit_err(errors::StartAsync { span }); - error = true; - } - - let attrs = tcx.hir().attrs(start_id); - for attr in attrs { - if attr.has_name(sym::track_caller) { - tcx.dcx().emit_err(errors::StartTrackCaller { - span: attr.span, - start: start_span, - }); - error = true; - } - if attr.has_name(sym::target_feature) - // Calling functions with `#[target_feature]` is - // not unsafe on WASM, see #84988 - && !tcx.sess.target.is_like_wasm - && !tcx.sess.opts.actually_rustdoc - { - tcx.dcx().emit_err(errors::StartTargetFeature { - span: attr.span, - start: start_span, - }); - error = true; - } - } - - if error { - return; - } - } - } - - let expected_sig = ty::Binder::dummy(tcx.mk_fn_sig( - [tcx.types.isize, Ty::new_imm_ptr(tcx, Ty::new_imm_ptr(tcx, tcx.types.u8))], - tcx.types.isize, - false, - hir::Safety::Safe, - ExternAbi::Rust, - )); - - let _ = check_function_signature( - tcx, - ObligationCause::new( - start_span, - start_def_id, - ObligationCauseCode::StartFunctionType, - ), - start_def_id.into(), - expected_sig, - ); - } - _ => { - span_bug!(start_span, "start has a non-function type: found `{}`", start_t); - } - } -} diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index fd78bf3e8fc60..08e0e52a4925b 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -26,7 +26,7 @@ fn equate_intrinsic_type<'tcx>( sig: ty::PolyFnSig<'tcx>, ) { let (generics, span) = match tcx.hir_node_by_def_id(def_id) { - hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, generics, _), .. }) + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { generics, .. }, .. }) | hir::Node::ForeignItem(hir::ForeignItem { kind: hir::ForeignItemKind::Fn(_, _, generics), .. @@ -86,6 +86,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) - | sym::assert_inhabited | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid + | sym::box_new | sym::breakpoint | sym::size_of | sym::min_align_of @@ -131,6 +132,9 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) - | sym::aggregate_raw_ptr | sym::ptr_metadata | sym::ub_checks + | sym::contract_checks + | sym::contract_check_requires + | sym::contract_check_ensures | sym::fadd_algebraic | sym::fsub_algebraic | sym::fmul_algebraic @@ -181,14 +185,19 @@ pub fn check_intrinsic_type( ]); let mk_va_list_ty = |mutbl| { tcx.lang_items().va_list().map(|did| { - let region = ty::Region::new_bound(tcx, ty::INNERMOST, ty::BoundRegion { - var: ty::BoundVar::ZERO, - kind: ty::BoundRegionKind::Anon, - }); - let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, ty::BoundRegion { - var: ty::BoundVar::from_u32(2), - kind: ty::BoundRegionKind::ClosureEnv, - }); + let region = ty::Region::new_bound( + tcx, + ty::INNERMOST, + ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BoundRegionKind::Anon }, + ); + let env_region = ty::Region::new_bound( + tcx, + ty::INNERMOST, + ty::BoundRegion { + var: ty::BoundVar::from_u32(2), + kind: ty::BoundRegionKind::ClosureEnv, + }, + ); let va_list_ty = tcx.type_of(did).instantiate(tcx, &[region.into()]); (Ty::new_ref(tcx, env_region, va_list_ty, mutbl), va_list_ty) }) @@ -198,7 +207,8 @@ pub fn check_intrinsic_type( let split: Vec<&str> = name_str.split('_').collect(); assert!(split.len() >= 2, "Atomic intrinsic in an incorrect format"); - //We only care about the operation here + // Each atomic op has variants with different suffixes (`_seq_cst`, `_acquire`, etc.). Use + // string ops to strip the suffixes, because the variants all get the same treatment here. let (n_tps, inputs, output) = match split[1] { "cxchg" | "cxchgweak" => ( 1, @@ -217,6 +227,16 @@ pub fn check_intrinsic_type( } }; (n_tps, 0, 0, inputs, output, hir::Safety::Unsafe) + } else if intrinsic_name == sym::contract_check_ensures { + // contract_check_ensures::<'a, Ret, C>(&'a Ret, C) + // where C: impl Fn(&'a Ret) -> bool, + // + // so: two type params, one lifetime param, 0 const params, two inputs, no return + + let p = generics.param_at(0, tcx); + let r = ty::Region::new_early_param(tcx, p.to_early_bound_region_data()); + let ref_ret = Ty::new_imm_ref(tcx, r, param(1)); + (2, 1, 0, vec![ref_ret, param(2)], tcx.types.unit, hir::Safety::Safe) } else { let safety = intrinsic_operation_unsafety(tcx, intrinsic_id); let (n_tps, n_cts, inputs, output) = match intrinsic_name { @@ -470,7 +490,7 @@ pub fn check_intrinsic_type( vec![Ty::new_imm_ptr(tcx, param(0)), Ty::new_imm_ptr(tcx, param(0))], tcx.types.usize, ), - sym::unchecked_div | sym::unchecked_rem | sym::exact_div => { + sym::unchecked_div | sym::unchecked_rem | sym::exact_div | sym::disjoint_bitor => { (1, 0, vec![param(0), param(0)], param(0)) } sym::unchecked_shl | sym::unchecked_shr => (2, 0, vec![param(0), param(1)], param(0)), @@ -606,6 +626,13 @@ pub fn check_intrinsic_type( sym::ub_checks => (0, 0, Vec::new(), tcx.types.bool), + sym::box_new => (1, 0, vec![param(0)], Ty::new_box(tcx, param(0))), + + // contract_checks() -> bool + sym::contract_checks => (0, 0, Vec::new(), tcx.types.bool), + // contract_check_requires::(C) -> bool, where C: impl Fn() -> bool + sym::contract_check_requires => (1, 0, vec![param(0)], tcx.types.unit), + sym::simd_eq | sym::simd_ne | sym::simd_lt diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 0b0c92a726dd6..96b33bdd2506b 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -79,8 +79,7 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::{Diag, ErrorGuaranteed, pluralize, struct_span_code_err}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; -use rustc_index::bit_set::BitSet; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; +use rustc_index::bit_set::DenseBitSet; use rustc_infer::infer::{self, TyCtxtInferExt as _}; use rustc_infer::traits::ObligationCause; use rustc_middle::query::Providers; @@ -197,7 +196,7 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) { fn report_forbidden_specialization(tcx: TyCtxt<'_>, impl_item: DefId, parent_impl: DefId) { let span = tcx.def_span(impl_item); - let ident = tcx.item_name(impl_item); + let ident = tcx.item_ident(impl_item); let err = match tcx.span_of_impl(parent_impl) { Ok(sp) => errors::ImplNotMarkedDefault::Ok { span, ident, ok_label: sp }, @@ -297,7 +296,7 @@ fn default_body_is_unstable( reason: Option, issue: Option>, ) { - let missing_item_name = tcx.associated_item(item_did).name; + let missing_item_name = tcx.item_ident(item_did); let (mut some_note, mut none_note, mut reason_str) = (false, false, String::new()); match reason { Some(r) => { @@ -456,18 +455,14 @@ fn fn_sig_suggestion<'tcx>( let mut output = sig.output(); let asyncness = if tcx.asyncness(assoc.def_id).is_async() { - output = if let ty::Alias(_, alias_ty) = *output.kind() { - tcx.explicit_item_super_predicates(alias_ty.def_id) + output = if let ty::Alias(_, alias_ty) = *output.kind() + && let Some(output) = tcx + .explicit_item_self_bounds(alias_ty.def_id) .iter_instantiated_copied(tcx, alias_ty.args) .find_map(|(bound, _)| { bound.as_projection_clause()?.no_bound_vars()?.term.as_type() - }) - .unwrap_or_else(|| { - span_bug!( - ident.span, - "expected async fn to have `impl Future` output, but it returns {output}" - ) - }) + }) { + output } else { span_bug!( ident.span, @@ -650,13 +645,13 @@ pub fn check_function_signature<'tcx>( }))), err, false, + None, ); return Err(diag.emit()); } } - let outlives_env = OutlivesEnvironment::new(param_env); - if let Err(e) = ocx.resolve_regions_and_report_errors(local_id, &outlives_env) { + if let Err(e) = ocx.resolve_regions_and_report_errors(local_id, param_env, []) { return Err(e); } diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index ca6729a5bbdf6..d43c65c0023b0 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -31,7 +31,7 @@ struct Context { parent: Option<(Scope, ScopeDepth)>, } -struct RegionResolutionVisitor<'tcx> { +struct ScopeResolutionVisitor<'tcx> { tcx: TyCtxt<'tcx>, // The number of expressions and patterns visited in the current body. @@ -71,7 +71,7 @@ struct RegionResolutionVisitor<'tcx> { } /// Records the lifetime of a local variable as `cx.var_parent` -fn record_var_lifetime(visitor: &mut RegionResolutionVisitor<'_>, var_id: hir::ItemLocalId) { +fn record_var_lifetime(visitor: &mut ScopeResolutionVisitor<'_>, var_id: hir::ItemLocalId) { match visitor.cx.var_parent { None => { // this can happen in extern fn declarations like @@ -82,7 +82,7 @@ fn record_var_lifetime(visitor: &mut RegionResolutionVisitor<'_>, var_id: hir::I } } -fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx hir::Block<'tcx>) { +fn resolve_block<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, blk: &'tcx hir::Block<'tcx>) { debug!("resolve_block(blk.hir_id={:?})", blk.hir_id); let prev_cx = visitor.cx; @@ -193,7 +193,7 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h visitor.cx = prev_cx; } -fn resolve_arm<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, arm: &'tcx hir::Arm<'tcx>) { +fn resolve_arm<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, arm: &'tcx hir::Arm<'tcx>) { fn has_let_expr(expr: &Expr<'_>) -> bool { match &expr.kind { hir::ExprKind::Binary(_, lhs, rhs) => has_let_expr(lhs) || has_let_expr(rhs), @@ -220,7 +220,7 @@ fn resolve_arm<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, arm: &'tcx hir visitor.cx = prev_cx; } -fn resolve_pat<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, pat: &'tcx hir::Pat<'tcx>) { +fn resolve_pat<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, pat: &'tcx hir::Pat<'tcx>) { visitor.record_child_scope(Scope { local_id: pat.hir_id.local_id, data: ScopeData::Node }); // If this is a binding then record the lifetime of that binding. @@ -237,7 +237,7 @@ fn resolve_pat<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, pat: &'tcx hir debug!("resolve_pat - post-increment {} pat = {:?}", visitor.expr_and_pat_count, pat); } -fn resolve_stmt<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, stmt: &'tcx hir::Stmt<'tcx>) { +fn resolve_stmt<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, stmt: &'tcx hir::Stmt<'tcx>) { let stmt_id = stmt.hir_id.local_id; debug!("resolve_stmt(stmt.id={:?})", stmt_id); @@ -256,7 +256,7 @@ fn resolve_stmt<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, stmt: &'tcx h visitor.cx.parent = prev_parent; } -fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx hir::Expr<'tcx>) { +fn resolve_expr<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, expr: &'tcx hir::Expr<'tcx>) { debug!("resolve_expr - pre-increment {} expr = {:?}", visitor.expr_and_pat_count, expr); let prev_cx = visitor.cx; @@ -420,10 +420,10 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h // properly, we can't miss any types. match expr.kind { - // Manually recurse over closures and inline consts, because they are the only - // case of nested bodies that share the parent environment. - hir::ExprKind::Closure(&hir::Closure { body, .. }) - | hir::ExprKind::ConstBlock(hir::ConstBlock { body, .. }) => { + // Manually recurse over closures, because they are nested bodies + // that share the parent environment. We handle const blocks in + // `visit_inline_const`. + hir::ExprKind::Closure(&hir::Closure { body, .. }) => { let body = visitor.tcx.hir().body(body); visitor.visit_body(body); } @@ -554,7 +554,7 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h } fn resolve_local<'tcx>( - visitor: &mut RegionResolutionVisitor<'tcx>, + visitor: &mut ScopeResolutionVisitor<'tcx>, pat: Option<&'tcx hir::Pat<'tcx>>, init: Option<&'tcx hir::Expr<'tcx>>, ) { @@ -654,6 +654,7 @@ fn resolve_local<'tcx>( /// | ( ..., P&, ... ) /// | ... "|" P& "|" ... /// | box P& + /// | P& if ... /// ``` fn is_binding_pat(pat: &hir::Pat<'_>) -> bool { // Note that the code below looks for *explicit* refs only, that is, it won't @@ -694,14 +695,15 @@ fn resolve_local<'tcx>( | PatKind::TupleStruct(_, subpats, _) | PatKind::Tuple(subpats, _) => subpats.iter().any(|p| is_binding_pat(p)), - PatKind::Box(subpat) | PatKind::Deref(subpat) => is_binding_pat(subpat), + PatKind::Box(subpat) | PatKind::Deref(subpat) | PatKind::Guard(subpat, _) => { + is_binding_pat(subpat) + } PatKind::Ref(_, _) | PatKind::Binding(hir::BindingMode(hir::ByRef::No, _), ..) | PatKind::Wild | PatKind::Never - | PatKind::Path(_) - | PatKind::Lit(_) + | PatKind::Expr(_) | PatKind::Range(_, _, _) | PatKind::Err(_) => false, } @@ -722,7 +724,7 @@ fn resolve_local<'tcx>( /// | ( E& ) /// ``` fn record_rvalue_scope_if_borrow_expr<'tcx>( - visitor: &mut RegionResolutionVisitor<'tcx>, + visitor: &mut ScopeResolutionVisitor<'tcx>, expr: &hir::Expr<'_>, blk_id: Option, ) { @@ -779,7 +781,7 @@ fn resolve_local<'tcx>( } } -impl<'tcx> RegionResolutionVisitor<'tcx> { +impl<'tcx> ScopeResolutionVisitor<'tcx> { /// Records the current parent (if any) as the parent of `child_scope`. /// Returns the depth of `child_scope`. fn record_child_scope(&mut self, child_scope: Scope) -> ScopeDepth { @@ -835,7 +837,7 @@ impl<'tcx> RegionResolutionVisitor<'tcx> { } } -impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { +impl<'tcx> Visitor<'tcx> for ScopeResolutionVisitor<'tcx> { fn visit_block(&mut self, b: &'tcx Block<'tcx>) { resolve_block(self, b); } @@ -903,6 +905,10 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { fn visit_local(&mut self, l: &'tcx LetStmt<'tcx>) { resolve_local(self, Some(l.pat), l.init) } + fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) { + let body = self.tcx.hir().body(c.body); + self.visit_body(body); + } } /// Per-body `region::ScopeTree`. The `DefId` should be the owner `DefId` for the body; @@ -919,7 +925,7 @@ pub(crate) fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree { } let scope_tree = if let Some(body) = tcx.hir().maybe_body_owned_by(def_id.expect_local()) { - let mut visitor = RegionResolutionVisitor { + let mut visitor = ScopeResolutionVisitor { tcx, scope_tree: ScopeTree::default(), expr_and_pat_count: 0, diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index efffb24c81d0d..dc7f9901c940e 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -6,12 +6,13 @@ use rustc_abi::ExternAbi; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err}; -use rustc_hir::ItemKind; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::lang_items::LangItem; +use rustc_hir::{AmbigArg, ItemKind}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; +use rustc_lint_defs::builtin::SUPERTRAIT_ITEM_SHADOWING_DEFINITION; use rustc_macros::LintDiagnostic; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::query::Providers; @@ -25,11 +26,10 @@ use rustc_middle::{bug, span_bug}; use rustc_session::parse::feature_err; use rustc_span::{DUMMY_SP, Ident, Span, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; -use rustc_trait_selection::regions::InferCtxtRegionExt; +use rustc_trait_selection::regions::{InferCtxtRegionExt, OutlivesEnvironmentBuildExt}; use rustc_trait_selection::traits::misc::{ ConstParamTyImplementationError, type_allowed_to_implement_const_param_ty, }; -use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{ self, FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, ObligationCtxt, @@ -128,13 +128,17 @@ where let infcx_compat = infcx.fork(); // We specifically want to call the non-compat version of `implied_bounds_tys`; we do this always. - let implied_bounds = - infcx.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types, false); - let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); + let outlives_env = OutlivesEnvironment::new_with_implied_bounds_compat( + &infcx, + body_def_id, + param_env, + assumed_wf_types.iter().copied(), + false, + ); lint_redundant_lifetimes(tcx, body_def_id, &outlives_env); - let errors = infcx.resolve_regions(&outlives_env); + let errors = infcx.resolve_regions_with_outlives_env(&outlives_env); if errors.is_empty() { return Ok(()); } @@ -172,10 +176,14 @@ where // but that does result in slightly more work when this option is set and // just obscures what we mean here anyways. Let's just be explicit. if is_bevy && !infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat { - let implied_bounds = - infcx_compat.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types, true); - let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); - let errors_compat = infcx_compat.resolve_regions(&outlives_env); + let outlives_env = OutlivesEnvironment::new_with_implied_bounds_compat( + &infcx, + body_def_id, + param_env, + assumed_wf_types, + true, + ); + let errors_compat = infcx_compat.resolve_regions_with_outlives_env(&outlives_env); if errors_compat.is_empty() { Ok(()) } else { @@ -293,7 +301,7 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) -> Result<() } res } - hir::ItemKind::Fn(ref sig, ..) => { + hir::ItemKind::Fn { sig, .. } => { check_item_fn(tcx, def_id, item.ident, item.span, sig.decl) } hir::ItemKind::Static(ty, ..) => { @@ -321,16 +329,20 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) -> Result<() hir::ItemKind::TraitAlias(..) => check_trait(tcx, item), // `ForeignItem`s are handled separately. hir::ItemKind::ForeignMod { .. } => Ok(()), - hir::ItemKind::TyAlias(hir_ty, hir_generics) => { - if tcx.type_alias_is_lazy(item.owner_id) { - // Bounds of lazy type aliases and of eager ones that contain opaque types are respected. - // E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`. - let res = check_item_type(tcx, def_id, hir_ty.span, UnsizedHandling::Allow); - check_variances_for_type_defn(tcx, item, hir_generics); - res - } else { + hir::ItemKind::TyAlias(hir_ty, hir_generics) if tcx.type_alias_is_lazy(item.owner_id) => { + let res = enter_wf_checking_ctxt(tcx, item.span, def_id, |wfcx| { + let ty = tcx.type_of(def_id).instantiate_identity(); + let item_ty = wfcx.normalize(hir_ty.span, Some(WellFormedLoc::Ty(def_id)), ty); + wfcx.register_wf_obligation( + hir_ty.span, + Some(WellFormedLoc::Ty(def_id)), + item_ty.into(), + ); + check_where_clauses(wfcx, item.span, def_id); Ok(()) - } + }); + check_variances_for_type_defn(tcx, item, hir_generics); + res } _ => Ok(()), }; @@ -377,7 +389,12 @@ fn check_trait_item<'tcx>( hir::TraitItemKind::Type(_bounds, Some(ty)) => (None, ty.span), _ => (None, trait_item.span), }; + check_dyn_incompatible_self_trait_by_name(tcx, trait_item); + + // Check that an item definition in a subtrait is shadowing a supertrait item. + lint_item_shadowing_supertrait_item(tcx, def_id); + let mut res = check_associated_item(tcx, def_id, span, method_sig); if matches!(trait_item.kind, hir::TraitItemKind::Fn(..)) { @@ -664,10 +681,10 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable>>( // Same for the region. In our example, 'a corresponds // to the 'me parameter. let region_param = gat_generics.param_at(*region_a_idx, tcx); - let region_param = ty::Region::new_early_param(tcx, ty::EarlyParamRegion { - index: region_param.index, - name: region_param.name, - }); + let region_param = ty::Region::new_early_param( + tcx, + ty::EarlyParamRegion { index: region_param.index, name: region_param.name }, + ); // The predicate we expect to see. (In our example, // `Self: 'me`.) bounds.insert( @@ -693,16 +710,16 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable>>( debug!("required clause: {region_a} must outlive {region_b}"); // Translate into the generic parameters of the GAT. let region_a_param = gat_generics.param_at(*region_a_idx, tcx); - let region_a_param = ty::Region::new_early_param(tcx, ty::EarlyParamRegion { - index: region_a_param.index, - name: region_a_param.name, - }); + let region_a_param = ty::Region::new_early_param( + tcx, + ty::EarlyParamRegion { index: region_a_param.index, name: region_a_param.name }, + ); // Same for the region. let region_b_param = gat_generics.param_at(*region_b_idx, tcx); - let region_b_param = ty::Region::new_early_param(tcx, ty::EarlyParamRegion { - index: region_b_param.index, - name: region_b_param.name, - }); + let region_b_param = ty::Region::new_early_param( + tcx, + ty::EarlyParamRegion { index: region_b_param.index, name: region_b_param.name }, + ); // The predicate we expect to see. bounds.insert( ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate( @@ -769,12 +786,7 @@ fn test_region_obligations<'tcx>( add_constraints(&infcx); - let outlives_environment = OutlivesEnvironment::with_bounds( - param_env, - infcx.implied_bounds_tys(param_env, id, wf_tys), - ); - - let errors = infcx.resolve_regions(&outlives_environment); + let errors = infcx.resolve_regions(id, param_env, wf_tys.iter().copied()); debug!(?errors, "errors"); // If we were able to prove that the type outlives the region without @@ -892,6 +904,45 @@ fn check_dyn_incompatible_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitI } } +fn lint_item_shadowing_supertrait_item<'tcx>(tcx: TyCtxt<'tcx>, trait_item_def_id: LocalDefId) { + let item_name = tcx.item_name(trait_item_def_id.to_def_id()); + let trait_def_id = tcx.local_parent(trait_item_def_id); + + let shadowed: Vec<_> = traits::supertrait_def_ids(tcx, trait_def_id.to_def_id()) + .skip(1) + .flat_map(|supertrait_def_id| { + tcx.associated_items(supertrait_def_id).filter_by_name_unhygienic(item_name) + }) + .collect(); + if !shadowed.is_empty() { + let shadowee = if let [shadowed] = shadowed[..] { + errors::SupertraitItemShadowee::Labeled { + span: tcx.def_span(shadowed.def_id), + supertrait: tcx.item_name(shadowed.trait_container(tcx).unwrap()), + } + } else { + let (traits, spans): (Vec<_>, Vec<_>) = shadowed + .iter() + .map(|item| { + (tcx.item_name(item.trait_container(tcx).unwrap()), tcx.def_span(item.def_id)) + }) + .unzip(); + errors::SupertraitItemShadowee::Several { traits: traits.into(), spans: spans.into() } + }; + + tcx.emit_node_span_lint( + SUPERTRAIT_ITEM_SHADOWING_DEFINITION, + tcx.local_def_id_to_hir_id(trait_item_def_id), + tcx.def_span(trait_item_def_id), + errors::SupertraitItemShadowing { + item: item_name, + subtrait: tcx.item_name(trait_def_id.to_def_id()), + shadowee, + }, + ); + } +} + fn check_impl_item<'tcx>( tcx: TyCtxt<'tcx>, impl_item: &'tcx hir::ImplItem<'tcx>, @@ -1044,7 +1095,7 @@ fn check_associated_item( // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case // other `Foo` impls are incoherent. - tcx.ensure() + tcx.ensure_ok() .coherent_trait(tcx.parent(item.trait_item_def_id.unwrap_or(item_id.into())))?; let self_ty = match item.container { @@ -1120,7 +1171,7 @@ fn check_type_defn<'tcx>( } else { // Evaluate the constant proactively, to emit an error if the constant has // an unconditional error. We only do so if the const has no type params. - let _ = tcx.const_eval_poly(def_id.into()); + let _ = tcx.const_eval_poly(def_id); } } let field_id = field.did.expect_local(); @@ -1274,7 +1325,6 @@ fn check_item_fn( enum UnsizedHandling { Forbid, - Allow, AllowIfForeignTail, } @@ -1292,7 +1342,6 @@ fn check_item_type( let forbid_unsized = match unsized_handling { UnsizedHandling::Forbid => true, - UnsizedHandling::Allow => false, UnsizedHandling::AllowIfForeignTail => { let tail = tcx.struct_tail_for_codegen(item_ty, wfcx.infcx.typing_env(wfcx.param_env)); @@ -1352,7 +1401,7 @@ fn check_impl<'tcx>( let trait_ref = tcx.impl_trait_ref(item.owner_id).unwrap().instantiate_identity(); // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case // other `Foo` impls are incoherent. - tcx.ensure().coherent_trait(trait_ref.def_id)?; + tcx.ensure_ok().coherent_trait(trait_ref.def_id)?; let trait_span = hir_trait_ref.path.span; let trait_ref = wfcx.normalize( trait_span, @@ -2196,7 +2245,7 @@ impl<'tcx> Visitor<'tcx> for CollectUsageSpans<'_> { // Skip the generics. We only care about fields, not where clause/param bounds. } - fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) -> Self::Result { + fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result { if let hir::TyKind::Path(hir::QPath::Resolved(None, qpath)) = t.kind { if let Res::Def(DefKind::TyParam, def_id) = qpath.res && def_id == self.param_def_id @@ -2265,14 +2314,14 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalModDefId) -> Result<(), ErrorGuaranteed> { let items = tcx.hir_module_items(module); - let mut res = items.par_items(|item| tcx.ensure().check_well_formed(item.owner_id.def_id)); - res = - res.and(items.par_impl_items(|item| tcx.ensure().check_well_formed(item.owner_id.def_id))); - res = - res.and(items.par_trait_items(|item| tcx.ensure().check_well_formed(item.owner_id.def_id))); - res = res - .and(items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.owner_id.def_id))); - res = res.and(items.par_opaques(|item| tcx.ensure().check_well_formed(item))); + let res = items + .par_items(|item| tcx.ensure_ok().check_well_formed(item.owner_id.def_id)) + .and(items.par_impl_items(|item| tcx.ensure_ok().check_well_formed(item.owner_id.def_id))) + .and(items.par_trait_items(|item| tcx.ensure_ok().check_well_formed(item.owner_id.def_id))) + .and( + items.par_foreign_items(|item| tcx.ensure_ok().check_well_formed(item.owner_id.def_id)), + ) + .and(items.par_opaques(|item| tcx.ensure_ok().check_well_formed(item))); if module == LocalModDefId::CRATE_DEF_ID { super::entry::check_for_entry_fn(tcx); } diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 3b98f358b1e95..9da57c330c5ef 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -10,7 +10,6 @@ use rustc_hir as hir; use rustc_hir::ItemKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{self, RegionResolutionError, TyCtxtInferExt}; use rustc_infer::traits::Obligation; use rustc_middle::ty::adjustment::CoerceUnsizedInfo; @@ -49,6 +48,10 @@ pub(super) fn check_trait<'tcx>( checker .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn)?; checker.check(lang_items.pointer_like(), visit_implementation_of_pointer_like)?; + checker.check( + lang_items.coerce_pointee_validated_trait(), + visit_implementation_of_coerce_pointee_validity, + )?; Ok(()) } @@ -193,7 +196,7 @@ fn visit_implementation_of_coerce_unsized(checker: &Checker<'_>) -> Result<(), E // errors; other parts of the code may demand it for the info of // course. let span = tcx.def_span(impl_did); - tcx.at(span).ensure().coerce_unsized_info(impl_did) + tcx.at(span).ensure_ok().coerce_unsized_info(impl_did) } fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { @@ -259,22 +262,40 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() let coerced_fields = fields .iter() .filter(|field| { + // Ignore PhantomData fields + let unnormalized_ty = tcx.type_of(field.did).instantiate_identity(); + if tcx + .try_normalize_erasing_regions( + ty::TypingEnv::non_body_analysis(tcx, def_a.did()), + unnormalized_ty, + ) + .unwrap_or(unnormalized_ty) + .is_phantom_data() + { + return false; + } + let ty_a = field.ty(tcx, args_a); let ty_b = field.ty(tcx, args_b); - if let Ok(layout) = - tcx.layout_of(infcx.typing_env(param_env).as_query_input(ty_a)) - { - if layout.is_1zst() { + // FIXME: We could do normalization here, but is it really worth it? + if ty_a == ty_b { + // Allow 1-ZSTs that don't mention type params. + // + // Allowing type params here would allow us to possibly transmute + // between ZSTs, which may be used to create library unsoundness. + if let Ok(layout) = + tcx.layout_of(infcx.typing_env(param_env).as_query_input(ty_a)) + && layout.is_1zst() + && !ty_a.has_non_region_param() + { // ignore 1-ZST fields return false; } - } - if ty_a == ty_b { res = Err(tcx.dcx().emit_err(errors::DispatchFromDynZST { span, - name: field.name, + name: field.ident(tcx), ty: ty_a, })); @@ -316,10 +337,11 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() tcx, cause.clone(), param_env, - ty::TraitRef::new(tcx, dispatch_from_dyn_trait, [ - field.ty(tcx, args_a), - field.ty(tcx, args_b), - ]), + ty::TraitRef::new( + tcx, + dispatch_from_dyn_trait, + [field.ty(tcx, args_a), field.ty(tcx, args_b)], + ), )); } let errors = ocx.select_all_or_error(); @@ -328,8 +350,7 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() } // Finally, resolve all regions. - let outlives_env = OutlivesEnvironment::new(param_env); - res = res.and(ocx.resolve_regions_and_report_errors(impl_did, &outlives_env)); + res = res.and(ocx.resolve_regions_and_report_errors(impl_did, param_env, [])); } res } @@ -388,17 +409,12 @@ pub(crate) fn coerce_unsized_info<'tcx>( check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ref(tcx, r_b, ty)) } - (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(ty_b, mutbl_b)) => check_mutbl( - ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }, - ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b }, - &|ty| Ty::new_imm_ptr(tcx, ty), - ), - - (&ty::RawPtr(ty_a, mutbl_a), &ty::RawPtr(ty_b, mutbl_b)) => check_mutbl( - ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }, - ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b }, - &|ty| Ty::new_imm_ptr(tcx, ty), - ), + (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(ty_b, mutbl_b)) + | (&ty::RawPtr(ty_a, mutbl_a), &ty::RawPtr(ty_b, mutbl_b)) => { + let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; + let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b }; + check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ptr(tcx, ty)) + } (&ty::Adt(def_a, args_a), &ty::Adt(def_b, args_b)) if def_a.is_struct() && def_b.is_struct() => @@ -460,8 +476,16 @@ pub(crate) fn coerce_unsized_info<'tcx>( .filter_map(|(i, f)| { let (a, b) = (f.ty(tcx, args_a), f.ty(tcx, args_b)); - if tcx.type_of(f.did).instantiate_identity().is_phantom_data() { - // Ignore PhantomData fields + // Ignore PhantomData fields + let unnormalized_ty = tcx.type_of(f.did).instantiate_identity(); + if tcx + .try_normalize_erasing_regions( + ty::TypingEnv::non_body_analysis(tcx, def_a.did()), + unnormalized_ty, + ) + .unwrap_or(unnormalized_ty) + .is_phantom_data() + { return None; } @@ -538,8 +562,7 @@ pub(crate) fn coerce_unsized_info<'tcx>( } // Finally, resolve all regions. - let outlives_env = OutlivesEnvironment::new(param_env); - let _ = ocx.resolve_regions_and_report_errors(impl_did, &outlives_env); + let _ = ocx.resolve_regions_and_report_errors(impl_did, param_env, []); Ok(CoerceUnsizedInfo { custom_kind: kind }) } @@ -764,3 +787,32 @@ fn visit_implementation_of_pointer_like(checker: &Checker<'_>) -> Result<(), Err .with_note(why_disqualified) .emit()) } + +fn visit_implementation_of_coerce_pointee_validity( + checker: &Checker<'_>, +) -> Result<(), ErrorGuaranteed> { + let tcx = checker.tcx; + let self_ty = tcx.impl_trait_ref(checker.impl_def_id).unwrap().instantiate_identity().self_ty(); + let span = tcx.def_span(checker.impl_def_id); + if !tcx.is_builtin_derived(checker.impl_def_id.into()) { + return Err(tcx.dcx().emit_err(errors::CoercePointeeNoUserValidityAssertion { span })); + } + let ty::Adt(def, _args) = self_ty.kind() else { + return Err(tcx.dcx().emit_err(errors::CoercePointeeNotConcreteType { span })); + }; + let did = def.did(); + // Now get a more precise span of the `struct`. + let span = tcx.def_span(did); + if !def.is_struct() { + return Err(tcx + .dcx() + .emit_err(errors::CoercePointeeNotStruct { span, kind: def.descr().into() })); + } + if !def.repr().transparent() { + return Err(tcx.dcx().emit_err(errors::CoercePointeeNotTransparent { span })); + } + if def.all_fields().next().is_none() { + return Err(tcx.dcx().emit_err(errors::CoercePointeeNoField { span })); + } + Ok(()) +} diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs index 5127e73d9784d..c72f6201831ae 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs @@ -251,10 +251,13 @@ impl<'tcx> InherentOverlapChecker<'tcx> { for ident in &idents_to_add { connected_region_ids.insert(*ident, id_to_set); } - connected_regions.insert(id_to_set, ConnectedRegion { - idents: idents_to_add, - impl_blocks: std::iter::once(i).collect(), - }); + connected_regions.insert( + id_to_set, + ConnectedRegion { + idents: idents_to_add, + impl_blocks: std::iter::once(i).collect(), + }, + ); } // Take the only id inside the list &[id_to_set] => { diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index 1be4aa2f63ac4..1bc60087ab5ee 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -13,6 +13,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_session::parse::feature_err; use rustc_span::{ErrorGuaranteed, sym}; +use rustc_type_ir::elaborate; use tracing::debug; use crate::errors; @@ -150,19 +151,19 @@ fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) -> Result<(), ErrorGuaranteed> let Some(impls) = tcx.all_local_trait_impls(()).get(&def_id) else { return Ok(()) }; // Trigger building the specialization graph for the trait. This will detect and report any // overlap errors. - let mut res = tcx.ensure().specialization_graph_of(def_id); + let mut res = tcx.ensure_ok().specialization_graph_of(def_id); for &impl_def_id in impls { let trait_header = tcx.impl_trait_header(impl_def_id).unwrap(); let trait_ref = trait_header.trait_ref.instantiate_identity(); let trait_def = tcx.trait_def(trait_ref.def_id); - res = res.and(check_impl(tcx, impl_def_id, trait_ref, trait_def)); - res = res.and(check_object_overlap(tcx, impl_def_id, trait_ref)); - - res = res.and(unsafety::check_item(tcx, impl_def_id, trait_header, trait_def)); - res = res.and(tcx.ensure().orphan_check_impl(impl_def_id)); - res = res.and(builtin::check_trait(tcx, def_id, impl_def_id, trait_header)); + res = res + .and(check_impl(tcx, impl_def_id, trait_ref, trait_def)) + .and(check_object_overlap(tcx, impl_def_id, trait_ref)) + .and(unsafety::check_item(tcx, impl_def_id, trait_header, trait_def)) + .and(tcx.ensure_ok().orphan_check_impl(impl_def_id)) + .and(builtin::check_trait(tcx, def_id, impl_def_id, trait_header)); } res @@ -184,7 +185,7 @@ fn check_object_overlap<'tcx>( // check for overlap with the automatic `impl Trait for dyn Trait` if let ty::Dynamic(data, ..) = trait_ref.self_ty().kind() { // This is something like `impl Trait1 for Trait2`. Illegal if - // Trait1 is a supertrait of Trait2 or Trait2 is not dyn-compatible. + // Trait1 is a supertrait of Trait2 or Trait2 is not dyn compatible. let component_def_ids = data.iter().flat_map(|predicate| { match predicate.skip_binder() { @@ -198,14 +199,13 @@ fn check_object_overlap<'tcx>( for component_def_id in component_def_ids { if !tcx.is_dyn_compatible(component_def_id) { - // FIXME(dyn_compat_renaming): Rename test and update comment. // Without the 'dyn_compatible_for_dispatch' feature this is an error // which will be reported by wfcheck. Ignore it here. - // This is tested by `coherence-impl-trait-for-trait-object-safe.rs`. + // This is tested by `coherence-impl-trait-for-trait-dyn-compatible.rs`. // With the feature enabled, the trait is not implemented automatically, // so this is valid. } else { - let mut supertrait_def_ids = tcx.supertrait_def_ids(component_def_id); + let mut supertrait_def_ids = elaborate::supertrait_def_ids(tcx, component_def_id); if supertrait_def_ids .any(|d| d == trait_def_id && tcx.trait_def(d).implement_via_object) { diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index 7d651155781a1..dbf7a7378f5ab 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -320,7 +320,7 @@ fn orphan_check<'tcx>( } let ty = if infcx.next_trait_solver() { - ocx.structurally_normalize( + ocx.structurally_normalize_ty( &cause, ty::ParamEnv::empty(), infcx.resolve_vars_if_possible(ty), @@ -465,8 +465,8 @@ fn emit_orphan_check_error<'tcx>( traits::OrphanCheckErr::UncoveredTyParams(UncoveredTyParams { uncovered, local_ty }) => { let mut reported = None; for param_def_id in uncovered { - let span = tcx.def_ident_span(param_def_id).unwrap(); - let name = tcx.item_name(param_def_id); + let name = tcx.item_ident(param_def_id); + let span = name.span; reported.get_or_insert(match local_ty { Some(local_type) => tcx.dcx().emit_err(errors::TyParamFirstLocal { @@ -492,7 +492,7 @@ fn lint_uncovered_ty_params<'tcx>( for param_def_id in uncovered { let span = tcx.def_ident_span(param_def_id).unwrap(); - let name = tcx.item_name(param_def_id); + let name = tcx.item_ident(param_def_id); match local_ty { Some(local_type) => tcx.emit_node_span_lint( diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index ada70117b626f..126237799562f 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -28,7 +28,7 @@ use rustc_errors::{ }; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::{self, Visitor, walk_generics}; +use rustc_hir::intravisit::{self, InferKind, Visitor, VisitorExt, walk_generics}; use rustc_hir::{self as hir, GenericParamKind, HirId, Node}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; @@ -57,7 +57,7 @@ mod type_of; /////////////////////////////////////////////////////////////////////////// -pub fn provide(providers: &mut Providers) { +pub(crate) fn provide(providers: &mut Providers) { resolve_bound_vars::provide(providers); *providers = Providers { type_of: type_of::type_of, @@ -65,9 +65,9 @@ pub fn provide(providers: &mut Providers) { type_alias_is_lazy: type_of::type_alias_is_lazy, item_bounds: item_bounds::item_bounds, explicit_item_bounds: item_bounds::explicit_item_bounds, - item_super_predicates: item_bounds::item_super_predicates, - explicit_item_super_predicates: item_bounds::explicit_item_super_predicates, - item_non_self_assumptions: item_bounds::item_non_self_assumptions, + item_self_bounds: item_bounds::item_self_bounds, + explicit_item_self_bounds: item_bounds::explicit_item_self_bounds, + item_non_self_bounds: item_bounds::item_non_self_bounds, impl_super_outlives: item_bounds::impl_super_outlives, generics_of: generics_of::generics_of, predicates_of: predicates_of::predicates_of, @@ -122,7 +122,7 @@ pub fn provide(providers: &mut Providers) { /// `ItemCtxt` is parameterized by a `DefId` that it uses to satisfy /// `probe_ty_param_bounds` requests, drawing the information from /// the HIR (`hir::Generics`), recursively. -pub struct ItemCtxt<'tcx> { +pub(crate) struct ItemCtxt<'tcx> { tcx: TyCtxt<'tcx>, item_def_id: LocalDefId, tainted_by_errors: Cell>, @@ -131,34 +131,24 @@ pub struct ItemCtxt<'tcx> { /////////////////////////////////////////////////////////////////////////// #[derive(Default)] -pub(crate) struct HirPlaceholderCollector(pub(crate) Vec); +pub(crate) struct HirPlaceholderCollector { + pub spans: Vec, + // If any of the spans points to a const infer var, then suppress any messages + // that may try to turn that const infer into a type parameter. + pub may_contain_const_infer: bool, +} impl<'v> Visitor<'v> for HirPlaceholderCollector { - fn visit_ty(&mut self, t: &'v hir::Ty<'v>) { - if let hir::TyKind::Infer = t.kind { - self.0.push(t.span); - } - intravisit::walk_ty(self, t) - } - fn visit_generic_arg(&mut self, generic_arg: &'v hir::GenericArg<'v>) { - match generic_arg { - hir::GenericArg::Infer(inf) => { - self.0.push(inf.span); - intravisit::walk_inf(self, inf); - } - hir::GenericArg::Type(t) => self.visit_ty(t), - _ => {} - } - } - fn visit_const_arg(&mut self, const_arg: &'v hir::ConstArg<'v>) { - if let hir::ConstArgKind::Infer(span) = const_arg.kind { - self.0.push(span); + fn visit_infer(&mut self, _inf_id: HirId, inf_span: Span, kind: InferKind<'v>) -> Self::Result { + self.spans.push(inf_span); + + if let InferKind::Const(_) | InferKind::Ambig(_) = kind { + self.may_contain_const_infer = true; } - intravisit::walk_const_arg(self, const_arg) } } -pub struct CollectItemTypesVisitor<'tcx> { +pub(crate) struct CollectItemTypesVisitor<'tcx> { pub tcx: TyCtxt<'tcx>, } @@ -277,8 +267,8 @@ fn reject_placeholder_type_signatures_in_item<'tcx>( placeholder_type_error( icx.lowerer(), Some(generics), - visitor.0, - suggest, + visitor.spans, + suggest && !visitor.may_contain_const_infer, None, item.kind.descr(), ); @@ -302,16 +292,16 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { match param.kind { hir::GenericParamKind::Lifetime { .. } => {} hir::GenericParamKind::Type { default: Some(_), .. } => { - self.tcx.ensure().type_of(param.def_id); + self.tcx.ensure_ok().type_of(param.def_id); } hir::GenericParamKind::Type { .. } => {} hir::GenericParamKind::Const { default, .. } => { - self.tcx.ensure().type_of(param.def_id); + self.tcx.ensure_ok().type_of(param.def_id); if let Some(default) = default { // need to store default and type of default - self.tcx.ensure().const_param_default(param.def_id); + self.tcx.ensure_ok().const_param_default(param.def_id); if let hir::ConstArgKind::Anon(ac) = default.kind { - self.tcx.ensure().type_of(ac.def_id); + self.tcx.ensure_ok().type_of(ac.def_id); } } } @@ -322,8 +312,8 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { if let hir::ExprKind::Closure(closure) = expr.kind { - self.tcx.ensure().generics_of(closure.def_id); - self.tcx.ensure().codegen_fn_attrs(closure.def_id); + self.tcx.ensure_ok().generics_of(closure.def_id); + self.tcx.ensure_ok().codegen_fn_attrs(closure.def_id); // We do not call `type_of` for closures here as that // depends on typecheck and would therefore hide // any further errors in case one typeck fails. @@ -335,15 +325,15 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { /// `check_item_type` ensures that it's called instead. fn visit_opaque_ty(&mut self, opaque: &'tcx hir::OpaqueTy<'tcx>) { let def_id = opaque.def_id; - self.tcx.ensure().generics_of(def_id); - self.tcx.ensure().predicates_of(def_id); - self.tcx.ensure().explicit_item_bounds(def_id); - self.tcx.ensure().explicit_item_super_predicates(def_id); - self.tcx.ensure().item_bounds(def_id); - self.tcx.ensure().item_super_predicates(def_id); + self.tcx.ensure_ok().generics_of(def_id); + self.tcx.ensure_ok().predicates_of(def_id); + self.tcx.ensure_ok().explicit_item_bounds(def_id); + self.tcx.ensure_ok().explicit_item_self_bounds(def_id); + self.tcx.ensure_ok().item_bounds(def_id); + self.tcx.ensure_ok().item_self_bounds(def_id); if self.tcx.is_conditionally_const(def_id) { - self.tcx.ensure().explicit_implied_const_bounds(def_id); - self.tcx.ensure().const_conditions(def_id); + self.tcx.ensure_ok().explicit_implied_const_bounds(def_id); + self.tcx.ensure_ok().const_conditions(def_id); } intravisit::walk_opaque_ty(self, opaque); } @@ -374,19 +364,19 @@ fn bad_placeholder<'cx, 'tcx>( } impl<'tcx> ItemCtxt<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, item_def_id: LocalDefId) -> ItemCtxt<'tcx> { + pub(crate) fn new(tcx: TyCtxt<'tcx>, item_def_id: LocalDefId) -> ItemCtxt<'tcx> { ItemCtxt { tcx, item_def_id, tainted_by_errors: Cell::new(None) } } - pub fn lower_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { + pub(crate) fn lower_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { self.lowerer().lower_ty(hir_ty) } - pub fn hir_id(&self) -> hir::HirId { + pub(crate) fn hir_id(&self) -> hir::HirId { self.tcx.local_def_id_to_hir_id(self.item_def_id) } - pub fn node(&self) -> hir::Node<'tcx> { + pub(crate) fn node(&self) -> hir::Node<'tcx> { self.tcx.hir_node(self.hir_id()) } @@ -576,7 +566,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { .iter() .enumerate() .map(|(i, a)| { - if let hir::TyKind::Infer = a.kind { + if let hir::TyKind::Infer(()) = a.kind { if let Some(suggested_ty) = self.lowerer().suggest_trait_fn_ty_for_impl_fn_infer(hir_id, Some(i)) { @@ -586,37 +576,37 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { } // Only visit the type looking for `_` if we didn't fix the type above - visitor.visit_ty(a); + visitor.visit_ty_unambig(a); self.lowerer().lower_arg_ty(a, None) }) .collect(); let output_ty = match decl.output { hir::FnRetTy::Return(output) => { - if let hir::TyKind::Infer = output.kind + if let hir::TyKind::Infer(()) = output.kind && let Some(suggested_ty) = self.lowerer().suggest_trait_fn_ty_for_impl_fn_infer(hir_id, None) { infer_replacements.push((output.span, suggested_ty.to_string())); Ty::new_error_with_message(tcx, output.span, suggested_ty.to_string()) } else { - visitor.visit_ty(output); + visitor.visit_ty_unambig(output); self.lower_ty(output) } } hir::FnRetTy::DefaultReturn(..) => tcx.types.unit, }; - if !(visitor.0.is_empty() && infer_replacements.is_empty()) { + if !(visitor.spans.is_empty() && infer_replacements.is_empty()) { // We check for the presence of // `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`. let mut diag = crate::collect::placeholder_type_error_diag( self, generics, - visitor.0, + visitor.spans, infer_replacements.iter().map(|(s, _)| *s).collect(), - true, + !visitor.may_contain_const_infer, hir_ty, "function", ); @@ -624,9 +614,10 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { if !infer_replacements.is_empty() { diag.multipart_suggestion( format!( - "try replacing `_` with the type{} in the corresponding trait method signature", - rustc_errors::pluralize!(infer_replacements.len()), - ), + "try replacing `_` with the type{} in the corresponding trait method \ + signature", + rustc_errors::pluralize!(infer_replacements.len()), + ), infer_replacements, Applicability::MachineApplicable, ); @@ -693,26 +684,26 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { hir::ItemKind::ForeignMod { items, .. } => { for item in *items { let item = tcx.hir().foreign_item(item.id); - tcx.ensure().generics_of(item.owner_id); - tcx.ensure().type_of(item.owner_id); - tcx.ensure().predicates_of(item.owner_id); + tcx.ensure_ok().generics_of(item.owner_id); + tcx.ensure_ok().type_of(item.owner_id); + tcx.ensure_ok().predicates_of(item.owner_id); if tcx.is_conditionally_const(def_id) { - tcx.ensure().explicit_implied_const_bounds(def_id); - tcx.ensure().const_conditions(def_id); + tcx.ensure_ok().explicit_implied_const_bounds(def_id); + tcx.ensure_ok().const_conditions(def_id); } match item.kind { hir::ForeignItemKind::Fn(..) => { - tcx.ensure().codegen_fn_attrs(item.owner_id); - tcx.ensure().fn_sig(item.owner_id) + tcx.ensure_ok().codegen_fn_attrs(item.owner_id); + tcx.ensure_ok().fn_sig(item.owner_id) } hir::ForeignItemKind::Static(..) => { - tcx.ensure().codegen_fn_attrs(item.owner_id); + tcx.ensure_ok().codegen_fn_attrs(item.owner_id); let mut visitor = HirPlaceholderCollector::default(); visitor.visit_foreign_item(item); placeholder_type_error( icx.lowerer(), None, - visitor.0, + visitor.spans, false, None, "static variable", @@ -723,40 +714,40 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { } } hir::ItemKind::Enum(..) => { - tcx.ensure().generics_of(def_id); - tcx.ensure().type_of(def_id); - tcx.ensure().predicates_of(def_id); + tcx.ensure_ok().generics_of(def_id); + tcx.ensure_ok().type_of(def_id); + tcx.ensure_ok().predicates_of(def_id); lower_enum_variant_types(tcx, def_id.to_def_id()); } hir::ItemKind::Impl { .. } => { - tcx.ensure().generics_of(def_id); - tcx.ensure().type_of(def_id); - tcx.ensure().impl_trait_header(def_id); - tcx.ensure().predicates_of(def_id); - tcx.ensure().associated_items(def_id); + tcx.ensure_ok().generics_of(def_id); + tcx.ensure_ok().type_of(def_id); + tcx.ensure_ok().impl_trait_header(def_id); + tcx.ensure_ok().predicates_of(def_id); + tcx.ensure_ok().associated_items(def_id); } hir::ItemKind::Trait(..) => { - tcx.ensure().generics_of(def_id); - tcx.ensure().trait_def(def_id); + tcx.ensure_ok().generics_of(def_id); + tcx.ensure_ok().trait_def(def_id); tcx.at(it.span).explicit_super_predicates_of(def_id); - tcx.ensure().predicates_of(def_id); - tcx.ensure().associated_items(def_id); + tcx.ensure_ok().predicates_of(def_id); + tcx.ensure_ok().associated_items(def_id); } hir::ItemKind::TraitAlias(..) => { - tcx.ensure().generics_of(def_id); + tcx.ensure_ok().generics_of(def_id); tcx.at(it.span).explicit_implied_predicates_of(def_id); tcx.at(it.span).explicit_super_predicates_of(def_id); - tcx.ensure().predicates_of(def_id); + tcx.ensure_ok().predicates_of(def_id); } hir::ItemKind::Struct(struct_def, _) | hir::ItemKind::Union(struct_def, _) => { - tcx.ensure().generics_of(def_id); - tcx.ensure().type_of(def_id); - tcx.ensure().predicates_of(def_id); + tcx.ensure_ok().generics_of(def_id); + tcx.ensure_ok().type_of(def_id); + tcx.ensure_ok().predicates_of(def_id); for f in struct_def.fields() { - tcx.ensure().generics_of(f.def_id); - tcx.ensure().type_of(f.def_id); - tcx.ensure().predicates_of(f.def_id); + tcx.ensure_ok().generics_of(f.def_id); + tcx.ensure_ok().type_of(f.def_id); + tcx.ensure_ok().predicates_of(f.def_id); } if let Some(ctor_def_id) = struct_def.ctor_def_id() { @@ -765,22 +756,22 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { } hir::ItemKind::TyAlias(..) => { - tcx.ensure().generics_of(def_id); - tcx.ensure().type_of(def_id); - tcx.ensure().predicates_of(def_id); + tcx.ensure_ok().generics_of(def_id); + tcx.ensure_ok().type_of(def_id); + tcx.ensure_ok().predicates_of(def_id); } hir::ItemKind::Static(ty, ..) | hir::ItemKind::Const(ty, ..) => { - tcx.ensure().generics_of(def_id); - tcx.ensure().type_of(def_id); - tcx.ensure().predicates_of(def_id); + tcx.ensure_ok().generics_of(def_id); + tcx.ensure_ok().type_of(def_id); + tcx.ensure_ok().predicates_of(def_id); if !ty.is_suggestable_infer_ty() { let mut visitor = HirPlaceholderCollector::default(); visitor.visit_item(it); placeholder_type_error( icx.lowerer(), None, - visitor.0, + visitor.spans, false, None, it.kind.descr(), @@ -788,12 +779,12 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { } } - hir::ItemKind::Fn(..) => { - tcx.ensure().generics_of(def_id); - tcx.ensure().type_of(def_id); - tcx.ensure().predicates_of(def_id); - tcx.ensure().fn_sig(def_id); - tcx.ensure().codegen_fn_attrs(def_id); + hir::ItemKind::Fn { .. } => { + tcx.ensure_ok().generics_of(def_id); + tcx.ensure_ok().type_of(def_id); + tcx.ensure_ok().predicates_of(def_id); + tcx.ensure_ok().fn_sig(def_id); + tcx.ensure_ok().codegen_fn_attrs(def_id); } } } @@ -801,18 +792,18 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { let trait_item = tcx.hir().trait_item(trait_item_id); let def_id = trait_item_id.owner_id; - tcx.ensure().generics_of(def_id); + tcx.ensure_ok().generics_of(def_id); let icx = ItemCtxt::new(tcx, def_id.def_id); match trait_item.kind { hir::TraitItemKind::Fn(..) => { - tcx.ensure().codegen_fn_attrs(def_id); - tcx.ensure().type_of(def_id); - tcx.ensure().fn_sig(def_id); + tcx.ensure_ok().codegen_fn_attrs(def_id); + tcx.ensure_ok().type_of(def_id); + tcx.ensure_ok().fn_sig(def_id); } hir::TraitItemKind::Const(ty, body_id) => { - tcx.ensure().type_of(def_id); + tcx.ensure_ok().type_of(def_id); if !tcx.dcx().has_stashed_diagnostic(ty.span, StashKey::ItemNoType) && !(ty.is_suggestable_infer_ty() && body_id.is_some()) { @@ -822,7 +813,7 @@ fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { placeholder_type_error( icx.lowerer(), None, - visitor.0, + visitor.spans, false, None, "associated constant", @@ -831,48 +822,69 @@ fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { } hir::TraitItemKind::Type(_, Some(_)) => { - tcx.ensure().item_bounds(def_id); - tcx.ensure().item_super_predicates(def_id); - tcx.ensure().type_of(def_id); + tcx.ensure_ok().item_bounds(def_id); + tcx.ensure_ok().item_self_bounds(def_id); + tcx.ensure_ok().type_of(def_id); // Account for `type T = _;`. let mut visitor = HirPlaceholderCollector::default(); visitor.visit_trait_item(trait_item); - placeholder_type_error(icx.lowerer(), None, visitor.0, false, None, "associated type"); + placeholder_type_error( + icx.lowerer(), + None, + visitor.spans, + false, + None, + "associated type", + ); } hir::TraitItemKind::Type(_, None) => { - tcx.ensure().item_bounds(def_id); - tcx.ensure().item_super_predicates(def_id); + tcx.ensure_ok().item_bounds(def_id); + tcx.ensure_ok().item_self_bounds(def_id); // #74612: Visit and try to find bad placeholders // even if there is no concrete type. let mut visitor = HirPlaceholderCollector::default(); visitor.visit_trait_item(trait_item); - placeholder_type_error(icx.lowerer(), None, visitor.0, false, None, "associated type"); + placeholder_type_error( + icx.lowerer(), + None, + visitor.spans, + false, + None, + "associated type", + ); } }; - tcx.ensure().predicates_of(def_id); + tcx.ensure_ok().predicates_of(def_id); } fn lower_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) { let def_id = impl_item_id.owner_id; - tcx.ensure().generics_of(def_id); - tcx.ensure().type_of(def_id); - tcx.ensure().predicates_of(def_id); + tcx.ensure_ok().generics_of(def_id); + tcx.ensure_ok().type_of(def_id); + tcx.ensure_ok().predicates_of(def_id); let impl_item = tcx.hir().impl_item(impl_item_id); let icx = ItemCtxt::new(tcx, def_id.def_id); match impl_item.kind { hir::ImplItemKind::Fn(..) => { - tcx.ensure().codegen_fn_attrs(def_id); - tcx.ensure().fn_sig(def_id); + tcx.ensure_ok().codegen_fn_attrs(def_id); + tcx.ensure_ok().fn_sig(def_id); } hir::ImplItemKind::Type(_) => { // Account for `type T = _;` let mut visitor = HirPlaceholderCollector::default(); visitor.visit_impl_item(impl_item); - placeholder_type_error(icx.lowerer(), None, visitor.0, false, None, "associated type"); + placeholder_type_error( + icx.lowerer(), + None, + visitor.spans, + false, + None, + "associated type", + ); } hir::ImplItemKind::Const(ty, _) => { // Account for `const T: _ = ..;` @@ -882,7 +894,7 @@ fn lower_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) { placeholder_type_error( icx.lowerer(), None, - visitor.0, + visitor.spans, false, None, "associated constant", @@ -893,9 +905,9 @@ fn lower_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) { } fn lower_variant_ctor(tcx: TyCtxt<'_>, def_id: LocalDefId) { - tcx.ensure().generics_of(def_id); - tcx.ensure().type_of(def_id); - tcx.ensure().predicates_of(def_id); + tcx.ensure_ok().generics_of(def_id); + tcx.ensure_ok().type_of(def_id); + tcx.ensure_ok().predicates_of(def_id); } fn lower_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId) { @@ -917,7 +929,7 @@ fn lower_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId) { tcx.dcx().emit_err(errors::EnumDiscriminantOverflowed { span, discr: prev_discr.unwrap().to_string(), - item_name: tcx.item_name(variant.def_id), + item_name: tcx.item_ident(variant.def_id), wrapped_discr: wrapped_discr.to_string(), }); None @@ -926,9 +938,9 @@ fn lower_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId) { ); for f in &variant.fields { - tcx.ensure().generics_of(f.did); - tcx.ensure().type_of(f.did); - tcx.ensure().predicates_of(f.did); + tcx.ensure_ok().generics_of(f.did); + tcx.ensure_ok().type_of(f.did); + tcx.ensure_ok().predicates_of(f.did); } // Lower the ctor, if any. This also registers the variant as an item. @@ -979,11 +991,10 @@ impl<'tcx> FieldUniquenessCheckContext<'tcx> { } /// Check if a given field `ident` declared at `field_decl` has been declared elsewhere before. - fn check_field_decl(&mut self, ident: Ident, field_decl: FieldDeclSpan) { + fn check_field_decl(&mut self, field_name: Ident, field_decl: FieldDeclSpan) { use FieldDeclSpan::*; - let field_name = ident.name; - let ident = ident.normalize_to_macros_2_0(); - match (field_decl, self.seen_fields.get(&ident).copied()) { + let field_name = field_name.normalize_to_macros_2_0(); + match (field_decl, self.seen_fields.get(&field_name).copied()) { (NotNested(span), Some(NotNested(prev_span))) => { self.tcx.dcx().emit_err(errors::FieldAlreadyDeclared::NotNested { field_name, @@ -1024,7 +1035,7 @@ impl<'tcx> FieldUniquenessCheckContext<'tcx> { }); } (field_decl, None) => { - self.seen_fields.insert(ident, field_decl); + self.seen_fields.insert(field_name, field_decl); } } } @@ -1297,7 +1308,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn generics, .. }) - | Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), .. }) => { + | Item(hir::Item { kind: ItemKind::Fn { sig, generics, .. }, .. }) => { lower_fn_sig_recovering_infer_ret_ty(&icx, sig, generics, def_id) } @@ -1308,7 +1319,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn { icx.lowerer().lower_fn_ty( hir_id, - sig.header.safety, + sig.header.safety(), sig.header.abi, sig.decl, Some(generics), @@ -1323,13 +1334,18 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn kind: TraitItemKind::Fn(FnSig { header, decl, span: _ }, _), generics, .. - }) => { - icx.lowerer().lower_fn_ty(hir_id, header.safety, header.abi, decl, Some(generics), None) - } + }) => icx.lowerer().lower_fn_ty( + hir_id, + header.safety(), + header.abi, + decl, + Some(generics), + None, + ), ForeignItem(&hir::ForeignItem { kind: ForeignItemKind::Fn(sig, _, _), .. }) => { let abi = tcx.hir().get_foreign_abi(hir_id); - compute_sig_of_foreign_fn_decl(tcx, def_id, sig.decl, abi, sig.header.safety) + compute_sig_of_foreign_fn_decl(tcx, def_id, sig.decl, abi, sig.header.safety()) } Ctor(data) | Variant(hir::Variant { data, .. }) if data.ctor().is_some() => { @@ -1371,13 +1387,13 @@ fn lower_fn_sig_recovering_infer_ret_ty<'tcx>( generics: &'tcx hir::Generics<'tcx>, def_id: LocalDefId, ) -> ty::PolyFnSig<'tcx> { - if let Some(infer_ret_ty) = sig.decl.output.get_infer_ret_ty() { + if let Some(infer_ret_ty) = sig.decl.output.is_suggestable_infer_ty() { return recover_infer_ret_ty(icx, infer_ret_ty, generics, def_id); } icx.lowerer().lower_fn_ty( icx.tcx().local_def_id_to_hir_id(def_id), - sig.header.safety, + sig.header.safety(), sig.header.abi, sig.decl, Some(generics), @@ -1420,9 +1436,9 @@ fn recover_infer_ret_ty<'tcx>( }); let mut visitor = HirPlaceholderCollector::default(); - visitor.visit_ty(infer_ret_ty); + visitor.visit_ty_unambig(infer_ret_ty); - let mut diag = bad_placeholder(icx.lowerer(), visitor.0, "return type"); + let mut diag = bad_placeholder(icx.lowerer(), visitor.spans, "return type"); let ret_ty = fn_sig.output(); // Don't leak types into signatures unless they're nameable! diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs index f1022d9575315..41f8465ae91f4 100644 --- a/compiler/rustc_hir_analysis/src/collect/dump.rs +++ b/compiler/rustc_hir_analysis/src/collect/dump.rs @@ -1,7 +1,8 @@ +use rustc_hir as hir; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; use rustc_hir::intravisit; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_span::sym; pub(crate) fn opaque_hidden_types(tcx: TyCtxt<'_>) { @@ -10,6 +11,15 @@ pub(crate) fn opaque_hidden_types(tcx: TyCtxt<'_>) { } for id in tcx.hir_crate_items(()).opaques() { + if let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. } + | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, .. } = + tcx.hir().expect_opaque_ty(id).origin + && let hir::Node::TraitItem(trait_item) = tcx.hir_node_by_def_id(fn_def_id) + && let (_, hir::TraitFn::Required(..)) = trait_item.expect_fn() + { + continue; + } + let ty = tcx.type_of(id).instantiate_identity(); let span = tcx.def_span(id); tcx.dcx().emit_err(crate::errors::TypeOf { span, ty }); @@ -87,3 +97,82 @@ pub(crate) fn def_parents(tcx: TyCtxt<'_>) { } } } + +pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) { + for id in tcx.hir().items() { + let def_id = id.owner_id.def_id; + + let Some(attr) = tcx.get_attr(def_id, sym::rustc_dump_vtable) else { + continue; + }; + + let vtable_entries = match tcx.hir().item(id).kind { + hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => { + let trait_ref = tcx.impl_trait_ref(def_id).unwrap().instantiate_identity(); + if trait_ref.has_non_region_param() { + tcx.dcx().span_err( + attr.span, + "`rustc_dump_vtable` must be applied to non-generic impl", + ); + continue; + } + if !tcx.is_dyn_compatible(trait_ref.def_id) { + tcx.dcx().span_err( + attr.span, + "`rustc_dump_vtable` must be applied to dyn-compatible trait", + ); + continue; + } + let Ok(trait_ref) = tcx + .try_normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref) + else { + tcx.dcx().span_err( + attr.span, + "`rustc_dump_vtable` applied to impl header that cannot be normalized", + ); + continue; + }; + tcx.vtable_entries(trait_ref) + } + hir::ItemKind::TyAlias(_, _) => { + let ty = tcx.type_of(def_id).instantiate_identity(); + if ty.has_non_region_param() { + tcx.dcx().span_err( + attr.span, + "`rustc_dump_vtable` must be applied to non-generic type", + ); + continue; + } + let Ok(ty) = + tcx.try_normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), ty) + else { + tcx.dcx().span_err( + attr.span, + "`rustc_dump_vtable` applied to type alias that cannot be normalized", + ); + continue; + }; + let ty::Dynamic(data, _, _) = *ty.kind() else { + tcx.dcx().span_err(attr.span, "`rustc_dump_vtable` to type alias of dyn type"); + continue; + }; + if let Some(principal) = data.principal() { + tcx.vtable_entries( + tcx.instantiate_bound_regions_with_erased(principal).with_self_ty(tcx, ty), + ) + } else { + TyCtxt::COMMON_VTABLE_ENTRIES + } + } + _ => { + tcx.dcx().span_err( + attr.span, + "`rustc_dump_vtable` only applies to impl, or type alias of dyn type", + ); + continue; + } + }; + + tcx.dcx().span_err(tcx.def_span(def_id), format!("vtable entries: {vtable_entries:#?}")); + } +} diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 075615de52279..c0902398a54ae 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -3,9 +3,10 @@ use std::ops::ControlFlow; use hir::intravisit::{self, Visitor}; use hir::{GenericParamKind, HirId, Node}; -use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::VisitorExt; +use rustc_hir::{self as hir, AmbigArg}; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint; use rustc_span::{Span, Symbol, kw}; @@ -197,6 +198,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { { Some(parent_did) } + Node::TyPat(_) => Some(parent_did), _ => None, } } @@ -461,7 +463,7 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option Visitor<'tcx> for LateBoundRegionsDetector<'tcx> { type Result = ControlFlow; - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) -> ControlFlow { + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) -> ControlFlow { match ty.kind { hir::TyKind::BareFn(..) => { self.outer_index.shift_in(1); @@ -539,7 +541,7 @@ impl<'v> Visitor<'v> for AnonConstInParamTyDetector { if let GenericParamKind::Const { ty, default: _, synthetic: _ } = p.kind { let prev = self.in_param_ty; self.in_param_ty = true; - let res = self.visit_ty(ty); + let res = self.visit_ty_unambig(ty); self.in_param_ty = prev; res } else { diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index d3ff1f7bebe65..41c4cfbaa825d 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -30,53 +30,55 @@ fn associated_type_bounds<'tcx>( span: Span, filter: PredicateFilter, ) -> &'tcx [(ty::Clause<'tcx>, Span)] { - let item_ty = Ty::new_projection_from_args( - tcx, - assoc_item_def_id.to_def_id(), - GenericArgs::identity_for_item(tcx, assoc_item_def_id), - ); - - let icx = ItemCtxt::new(tcx, assoc_item_def_id); - let mut bounds = Bounds::default(); - icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter); - // Associated types are implicitly sized unless a `?Sized` bound is found - match filter { - PredicateFilter::All - | PredicateFilter::SelfOnly - | PredicateFilter::SelfTraitThatDefines(_) - | PredicateFilter::SelfAndAssociatedTypeBounds => { - icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span); + ty::print::with_reduced_queries!({ + let item_ty = Ty::new_projection_from_args( + tcx, + assoc_item_def_id.to_def_id(), + GenericArgs::identity_for_item(tcx, assoc_item_def_id), + ); + + let icx = ItemCtxt::new(tcx, assoc_item_def_id); + let mut bounds = Bounds::default(); + icx.lowerer().lower_bounds(item_ty, hir_bounds, &mut bounds, ty::List::empty(), filter); + // Associated types are implicitly sized unless a `?Sized` bound is found + match filter { + PredicateFilter::All + | PredicateFilter::SelfOnly + | PredicateFilter::SelfTraitThatDefines(_) + | PredicateFilter::SelfAndAssociatedTypeBounds => { + icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span); + } + // `ConstIfConst` is only interested in `~const` bounds. + PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {} } - // `ConstIfConst` is only interested in `~const` bounds. - PredicateFilter::ConstIfConst | PredicateFilter::SelfConstIfConst => {} - } - let trait_def_id = tcx.local_parent(assoc_item_def_id); - let trait_predicates = tcx.trait_explicit_predicates_and_bounds(trait_def_id); - - let item_trait_ref = ty::TraitRef::identity(tcx, tcx.parent(assoc_item_def_id.to_def_id())); - let bounds_from_parent = - trait_predicates.predicates.iter().copied().filter_map(|(clause, span)| { - remap_gat_vars_and_recurse_into_nested_projections( - tcx, - filter, - item_trait_ref, - assoc_item_def_id, - span, - clause, - ) - }); - - let all_bounds = tcx.arena.alloc_from_iter(bounds.clauses().chain(bounds_from_parent)); - debug!( - "associated_type_bounds({}) = {:?}", - tcx.def_path_str(assoc_item_def_id.to_def_id()), - all_bounds - ); + let trait_def_id = tcx.local_parent(assoc_item_def_id); + let trait_predicates = tcx.trait_explicit_predicates_and_bounds(trait_def_id); + + let item_trait_ref = ty::TraitRef::identity(tcx, tcx.parent(assoc_item_def_id.to_def_id())); + let bounds_from_parent = + trait_predicates.predicates.iter().copied().filter_map(|(clause, span)| { + remap_gat_vars_and_recurse_into_nested_projections( + tcx, + filter, + item_trait_ref, + assoc_item_def_id, + span, + clause, + ) + }); - assert_only_contains_predicates_from(filter, all_bounds, item_ty); + let all_bounds = tcx.arena.alloc_from_iter(bounds.clauses().chain(bounds_from_parent)); + debug!( + "associated_type_bounds({}) = {:?}", + tcx.def_path_str(assoc_item_def_id.to_def_id()), + all_bounds + ); - all_bounds + assert_only_contains_predicates_from(filter, all_bounds, item_ty); + + all_bounds + }) } /// The code below is quite involved, so let me explain. @@ -242,10 +244,11 @@ impl<'tcx> TypeFolder> for MapAndCompressBoundVars<'tcx> { // Allocate a new var idx, and insert a new bound ty. let var = ty::BoundVar::from_usize(self.still_bound_vars.len()); self.still_bound_vars.push(ty::BoundVariableKind::Ty(old_bound.kind)); - let mapped = Ty::new_bound(self.tcx, ty::INNERMOST, ty::BoundTy { - var, - kind: old_bound.kind, - }); + let mapped = Ty::new_bound( + self.tcx, + ty::INNERMOST, + ty::BoundTy { var, kind: old_bound.kind }, + ); self.mapping.insert(old_bound.var, mapped.into()); mapped }; @@ -265,10 +268,11 @@ impl<'tcx> TypeFolder> for MapAndCompressBoundVars<'tcx> { } else { let var = ty::BoundVar::from_usize(self.still_bound_vars.len()); self.still_bound_vars.push(ty::BoundVariableKind::Region(old_bound.kind)); - let mapped = ty::Region::new_bound(self.tcx, ty::INNERMOST, ty::BoundRegion { - var, - kind: old_bound.kind, - }); + let mapped = ty::Region::new_bound( + self.tcx, + ty::INNERMOST, + ty::BoundRegion { var, kind: old_bound.kind }, + ); self.mapping.insert(old_bound.var, mapped.into()); mapped }; @@ -350,7 +354,7 @@ pub(super) fn explicit_item_bounds( explicit_item_bounds_with_filter(tcx, def_id, PredicateFilter::All) } -pub(super) fn explicit_item_super_predicates( +pub(super) fn explicit_item_self_bounds( tcx: TyCtxt<'_>, def_id: LocalDefId, ) -> ty::EarlyBinder<'_, &'_ [(ty::Clause<'_>, Span)]> { @@ -434,11 +438,11 @@ pub(super) fn item_bounds(tcx: TyCtxt<'_>, def_id: DefId) -> ty::EarlyBinder<'_, }) } -pub(super) fn item_super_predicates( +pub(super) fn item_self_bounds( tcx: TyCtxt<'_>, def_id: DefId, ) -> ty::EarlyBinder<'_, ty::Clauses<'_>> { - tcx.explicit_item_super_predicates(def_id).map_bound(|bounds| { + tcx.explicit_item_self_bounds(def_id).map_bound(|bounds| { tcx.mk_clauses_from_iter( util::elaborate(tcx, bounds.iter().map(|&(bound, _span)| bound)).filter_only_self(), ) @@ -447,13 +451,12 @@ pub(super) fn item_super_predicates( /// This exists as an optimization to compute only the item bounds of the item /// that are not `Self` bounds. -pub(super) fn item_non_self_assumptions( +pub(super) fn item_non_self_bounds( tcx: TyCtxt<'_>, def_id: DefId, ) -> ty::EarlyBinder<'_, ty::Clauses<'_>> { let all_bounds: FxIndexSet<_> = tcx.item_bounds(def_id).skip_binder().iter().collect(); - let own_bounds: FxIndexSet<_> = - tcx.item_super_predicates(def_id).skip_binder().iter().collect(); + let own_bounds: FxIndexSet<_> = tcx.item_self_bounds(def_id).skip_binder().iter().collect(); if all_bounds.len() == own_bounds.len() { ty::EarlyBinder::bind(ty::ListWithCachedTypeInfo::empty()) } else { diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 8f84492146f11..d94383d6f3d88 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -350,10 +350,10 @@ fn compute_bidirectional_outlives_predicates<'tcx>( for param in opaque_own_params { let orig_lifetime = tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()); if let ty::ReEarlyParam(..) = *orig_lifetime { - let dup_lifetime = ty::Region::new_early_param(tcx, ty::EarlyParamRegion { - index: param.index, - name: param.name, - }); + let dup_lifetime = ty::Region::new_early_param( + tcx, + ty::EarlyParamRegion { index: param.index, name: param.name }, + ); let span = tcx.def_span(param.def_id); predicates.push(( ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(orig_lifetime, dup_lifetime)) @@ -653,7 +653,7 @@ pub(super) fn implied_predicates_with_filter<'tcx>( } } } - PredicateFilter::SelfAndAssociatedTypeBounds => { + PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => { for &(pred, span) in implied_bounds { debug!("superbound: {:?}", pred); if let ty::ClauseKind::Trait(bound) = pred.kind().skip_binder() @@ -969,7 +969,7 @@ pub(super) fn const_conditions<'tcx>( { Node::Item(item) => match item.kind { hir::ItemKind::Impl(impl_) => (impl_.generics, None, false), - hir::ItemKind::Fn(_, generics, _) => (generics, None, false), + hir::ItemKind::Fn { generics, .. } => (generics, None, false), hir::ItemKind::Trait(_, _, generics, supertraits, _) => { (generics, Some((item.owner_id.def_id, supertraits)), false) } @@ -1036,7 +1036,7 @@ pub(super) fn const_conditions<'tcx>( icx.lowerer().lower_bounds( tcx.types.self_param, - supertraits.into_iter(), + supertraits, &mut bounds, ty::List::empty(), PredicateFilter::ConstIfConst, diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 7c65e9613b69c..c03a1f6240fa3 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -12,13 +12,11 @@ use std::ops::ControlFlow; use rustc_ast::visit::walk_list; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; -use rustc_data_structures::sorted_map::SortedMap; use rustc_errors::ErrorGuaranteed; -use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::intravisit::{self, InferKind, Visitor, VisitorExt}; use rustc_hir::{ - GenericArg, GenericParam, GenericParamKind, HirId, ItemLocalMap, LifetimeName, Node, + self as hir, AmbigArg, GenericArg, GenericParam, GenericParamKind, HirId, LifetimeName, Node, }; use rustc_macros::extension; use rustc_middle::hir::nested_filter; @@ -26,7 +24,7 @@ use rustc_middle::middle::resolve_bound_vars::*; use rustc_middle::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeSuperVisitable, TypeVisitor}; use rustc_middle::{bug, span_bug}; -use rustc_span::def_id::{DefId, LocalDefId, LocalDefIdMap}; +use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::{Ident, Span, sym}; use tracing::{debug, debug_span, instrument}; @@ -62,33 +60,9 @@ impl ResolvedArg { } } -/// Maps the id of each bound variable reference to the variable decl -/// that it corresponds to. -/// -/// FIXME. This struct gets converted to a `ResolveBoundVars` for -/// actual use. It has the same data, but indexed by `LocalDefId`. This -/// is silly. -#[derive(Debug, Default)] -struct NamedVarMap { - // maps from every use of a named (not anonymous) bound var to a - // `ResolvedArg` describing how that variable is bound - defs: ItemLocalMap, - - // Maps relevant hir items to the bound vars on them. These include: - // - function defs - // - function pointers - // - closures - // - trait refs - // - bound types (like `T` in `for<'a> T<'a>: Foo`) - late_bound_vars: ItemLocalMap>, - - // List captured variables for each opaque type. - opaque_captured_lifetimes: LocalDefIdMap>, -} - struct BoundVarContext<'a, 'tcx> { tcx: TyCtxt<'tcx>, - map: &'a mut NamedVarMap, + rbv: &'a mut ResolveBoundVars, scope: ScopeRef<'a>, } @@ -184,29 +158,11 @@ enum Scope<'a> { }, } -#[derive(Copy, Clone, Debug)] -enum BinderScopeType { - /// Any non-concatenating binder scopes. - Normal, - /// Within a syntactic trait ref, there may be multiple poly trait refs that - /// are nested (under the `associated_type_bounds` feature). The binders of - /// the inner poly trait refs are extended from the outer poly trait refs - /// and don't increase the late bound depth. If you had - /// `T: for<'a> Foo Baz<'a, 'b>>`, then the `for<'b>` scope - /// would be `Concatenating`. This also used in trait refs in where clauses - /// where we have two binders `for<> T: for<> Foo` (I've intentionally left - /// out any lifetimes because they aren't needed to show the two scopes). - /// The inner `for<>` has a scope of `Concatenating`. - Concatenating, -} - -// A helper struct for debugging scopes without printing parent scopes -struct TruncatedScopeDebug<'a>(&'a Scope<'a>); - -impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 { - Scope::Binder { bound_vars, scope_type, hir_id, where_bound_origin, s: _ } => f +impl<'a> Scope<'a> { + // A helper for debugging scopes without printing parent scopes + fn debug_truncated(&'a self) -> impl fmt::Debug + 'a { + fmt::from_fn(move |f| match self { + Self::Binder { bound_vars, scope_type, hir_id, where_bound_origin, s: _ } => f .debug_struct("Binder") .field("bound_vars", bound_vars) .field("scope_type", scope_type) @@ -214,38 +170,54 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { .field("where_bound_origin", where_bound_origin) .field("s", &"..") .finish(), - Scope::Opaque { captures, def_id, s: _ } => f + Self::Opaque { captures, def_id, s: _ } => f .debug_struct("Opaque") .field("def_id", def_id) .field("captures", &captures.borrow()) .field("s", &"..") .finish(), - Scope::Body { id, s: _ } => { + Self::Body { id, s: _ } => { f.debug_struct("Body").field("id", id).field("s", &"..").finish() } - Scope::ObjectLifetimeDefault { lifetime, s: _ } => f + Self::ObjectLifetimeDefault { lifetime, s: _ } => f .debug_struct("ObjectLifetimeDefault") .field("lifetime", lifetime) .field("s", &"..") .finish(), - Scope::Supertrait { bound_vars, s: _ } => f + Self::Supertrait { bound_vars, s: _ } => f .debug_struct("Supertrait") .field("bound_vars", bound_vars) .field("s", &"..") .finish(), - Scope::TraitRefBoundary { s: _ } => f.debug_struct("TraitRefBoundary").finish(), - Scope::LateBoundary { s: _, what, deny_late_regions } => f + Self::TraitRefBoundary { s: _ } => f.debug_struct("TraitRefBoundary").finish(), + Self::LateBoundary { s: _, what, deny_late_regions } => f .debug_struct("LateBoundary") .field("what", what) .field("deny_late_regions", deny_late_regions) .finish(), - Scope::Root { opt_parent_item } => { + Self::Root { opt_parent_item } => { f.debug_struct("Root").field("opt_parent_item", &opt_parent_item).finish() } - } + }) } } +#[derive(Copy, Clone, Debug)] +enum BinderScopeType { + /// Any non-concatenating binder scopes. + Normal, + /// Within a syntactic trait ref, there may be multiple poly trait refs that + /// are nested (under the `associated_type_bounds` feature). The binders of + /// the inner poly trait refs are extended from the outer poly trait refs + /// and don't increase the late bound depth. If you had + /// `T: for<'a> Foo Baz<'a, 'b>>`, then the `for<'b>` scope + /// would be `Concatenating`. This also used in trait refs in where clauses + /// where we have two binders `for<> T: for<> Foo` (I've intentionally left + /// out any lifetimes because they aren't needed to show the two scopes). + /// The inner `for<>` has a scope of `Concatenating`. + Concatenating, +} + type ScopeRef<'a> = &'a Scope<'a>; pub(crate) fn provide(providers: &mut Providers) { @@ -269,19 +241,12 @@ pub(crate) fn provide(providers: &mut Providers) { /// Computes the `ResolveBoundVars` map that contains data for an entire `Item`. /// You should not read the result of this query directly, but rather use -/// `named_variable_map`, `is_late_bound_map`, etc. +/// `named_variable_map`, `late_bound_vars_map`, etc. #[instrument(level = "debug", skip(tcx))] fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBoundVars { - let mut named_variable_map = NamedVarMap { - defs: Default::default(), - late_bound_vars: Default::default(), - opaque_captured_lifetimes: Default::default(), - }; - let mut visitor = BoundVarContext { - tcx, - map: &mut named_variable_map, - scope: &Scope::Root { opt_parent_item: None }, - }; + let mut rbv = ResolveBoundVars::default(); + let mut visitor = + BoundVarContext { tcx, rbv: &mut rbv, scope: &Scope::Root { opt_parent_item: None } }; match tcx.hir_owner_node(local_def_id) { hir::OwnerNode::Item(item) => visitor.visit_item(item), hir::OwnerNode::ForeignItem(item) => visitor.visit_foreign_item(item), @@ -301,19 +266,10 @@ fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBou hir::OwnerNode::Synthetic => unreachable!(), } - let defs = named_variable_map.defs.into_sorted_stable_ord(); - let late_bound_vars = named_variable_map.late_bound_vars.into_sorted_stable_ord(); - let opaque_captured_lifetimes = named_variable_map.opaque_captured_lifetimes; - let rl = ResolveBoundVars { - defs: SortedMap::from_presorted_elements(defs), - late_bound_vars: SortedMap::from_presorted_elements(late_bound_vars), - opaque_captured_lifetimes, - }; - - debug!(?rl.defs); - debug!(?rl.late_bound_vars); - debug!(?rl.opaque_captured_lifetimes); - rl + debug!(?rbv.defs); + debug!(?rbv.late_bound_vars); + debug!(?rbv.opaque_captured_lifetimes); + rbv } fn late_arg_as_bound_arg<'tcx>( @@ -406,7 +362,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { Scope::Binder { hir_id, .. } => { // Nested poly trait refs have the binders concatenated let mut full_binders = - self.map.late_bound_vars.entry(hir_id.local_id).or_default().clone(); + self.rbv.late_bound_vars.get_mut_or_insert_default(hir_id.local_id).clone(); full_binders.extend(supertrait_bound_vars); break (full_binders, BinderScopeType::Concatenating); } @@ -489,15 +445,17 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { struct FindInferInClosureWithBinder; impl<'v> Visitor<'v> for FindInferInClosureWithBinder { type Result = ControlFlow; - fn visit_ty(&mut self, t: &'v hir::Ty<'v>) -> Self::Result { - if matches!(t.kind, hir::TyKind::Infer) { - ControlFlow::Break(t.span) - } else { - intravisit::walk_ty(self, t) - } + + fn visit_infer( + &mut self, + _inf_id: HirId, + inf_span: Span, + _kind: InferKind<'v>, + ) -> Self::Result { + ControlFlow::Break(inf_span) } } - FindInferInClosureWithBinder.visit_ty(ty).break_value() + FindInferInClosureWithBinder.visit_ty_unambig(ty).break_value() } let infer_in_rt_sp = match fn_decl.output { @@ -646,7 +604,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { let captures = captures.into_inner().into_iter().collect(); debug!(?captures); - self.map.opaque_captured_lifetimes.insert(opaque.def_id, captures); + self.rbv.opaque_captured_lifetimes.insert(opaque.def_id, captures); } #[instrument(level = "debug", skip(self))] @@ -660,7 +618,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { _ => {} } match item.kind { - hir::ItemKind::Fn(_, generics, _) => { + hir::ItemKind::Fn { generics, .. } => { self.visit_early_late(item.hir_id(), generics, |this| { intravisit::walk_item(this, item); }); @@ -749,7 +707,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } #[instrument(level = "debug", skip(self))] - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { match ty.kind { hir::TyKind::BareFn(c) => { let (mut bound_vars, binders): (FxIndexMap, Vec<_>) = c @@ -810,7 +768,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { intravisit::walk_ty(this, ty); }); } - hir::TyKind::TraitObject(bounds, lifetime, _) => { + hir::TyKind::TraitObject(bounds, lifetime) => { + let lifetime = lifetime.pointer(); + debug!(?bounds, ?lifetime, "TraitObject"); let scope = Scope::TraitRefBoundary { s: self.scope }; self.with(scope, |this| { @@ -827,7 +787,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { // use the object lifetime defaulting // rules. So e.g., `Box` becomes // `Box`. - self.resolve_object_lifetime_default(lifetime) + self.resolve_object_lifetime_default(&*lifetime) } LifetimeName::Infer => { // If the user writes `'_`, we use the *ordinary* elision @@ -838,7 +798,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } LifetimeName::Param(..) | LifetimeName::Static => { // If the user wrote an explicit name, use that. - self.visit_lifetime(lifetime); + self.visit_lifetime(&*lifetime); } LifetimeName::Error => {} } @@ -846,10 +806,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { hir::TyKind::Ref(lifetime_ref, ref mt) => { self.visit_lifetime(lifetime_ref); let scope = Scope::ObjectLifetimeDefault { - lifetime: self.map.defs.get(&lifetime_ref.hir_id.local_id).cloned(), + lifetime: self.rbv.defs.get(&lifetime_ref.hir_id.local_id).cloned(), s: self.scope, }; - self.with(scope, |this| this.visit_ty(mt.ty)); + self.with(scope, |this| this.visit_ty_unambig(mt.ty)); } hir::TyKind::TraitAscription(bounds) => { let scope = Scope::TraitRefBoundary { s: self.scope }; @@ -871,8 +831,8 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } #[instrument(level = "debug", skip(self))] - fn visit_pattern_type_pattern(&mut self, p: &'tcx hir::Pat<'tcx>) { - intravisit::walk_pat(self, p) + fn visit_pattern_type_pattern(&mut self, p: &'tcx hir::TyPat<'tcx>) { + intravisit::walk_ty_pat(self, p) } #[instrument(level = "debug", skip(self))] @@ -891,7 +851,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { this.visit_param_bound(bound); } if let Some(ty) = ty { - this.visit_ty(ty); + this.visit_ty_unambig(ty); } }) } @@ -910,7 +870,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { }), Type(ty) => self.visit_early(impl_item.hir_id(), impl_item.generics, |this| { this.visit_generics(impl_item.generics); - this.visit_ty(ty); + this.visit_ty_unambig(ty); }), Const(_, _) => self.visit_early(impl_item.hir_id(), impl_item.generics, |this| { intravisit::walk_impl_item(this, impl_item) @@ -964,7 +924,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { let bound_vars: Vec<_> = self.tcx.fn_sig(sig_id).skip_binder().bound_vars().iter().collect(); let hir_id = self.tcx.local_def_id_to_hir_id(def_id); - self.map.late_bound_vars.insert(hir_id.local_id, bound_vars); + self.rbv.late_bound_vars.insert(hir_id.local_id, bound_vars); } self.visit_fn_like_elision(fd.inputs, output, matches!(fk, intravisit::FnKind::Closure)); intravisit::walk_fn_kind(self, fk); @@ -1019,7 +979,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { }; self.with(scope, |this| { walk_list!(this, visit_generic_param, bound_generic_params); - this.visit_ty(bounded_ty); + this.visit_ty_unambig(bounded_ty); walk_list!(this, visit_param_bound, bounds); }) } @@ -1034,8 +994,8 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { &hir::WherePredicateKind::EqPredicate(hir::WhereEqPredicate { lhs_ty, rhs_ty, .. }) => { - self.visit_ty(lhs_ty); - self.visit_ty(rhs_ty); + self.visit_ty_unambig(lhs_ty); + self.visit_ty_unambig(rhs_ty); } } } @@ -1068,13 +1028,13 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { GenericParamKind::Lifetime { .. } => {} GenericParamKind::Type { default, .. } => { if let Some(ty) = default { - self.visit_ty(ty); + self.visit_ty_unambig(ty); } } GenericParamKind::Const { ty, default, .. } => { - self.visit_ty(ty); + self.visit_ty_unambig(ty); if let Some(default) = default { - self.visit_const_arg(default); + self.visit_const_arg_unambig(default); } } } @@ -1138,9 +1098,9 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { where F: for<'b> FnOnce(&mut BoundVarContext<'b, 'tcx>), { - let BoundVarContext { tcx, map, .. } = self; - let mut this = BoundVarContext { tcx: *tcx, map, scope: &wrap_scope }; - let span = debug_span!("scope", scope = ?TruncatedScopeDebug(this.scope)); + let BoundVarContext { tcx, rbv, .. } = self; + let mut this = BoundVarContext { tcx: *tcx, rbv, scope: &wrap_scope }; + let span = debug_span!("scope", scope = ?this.scope.debug_truncated()); { let _enter = span.enter(); f(&mut this); @@ -1148,10 +1108,10 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { } fn record_late_bound_vars(&mut self, hir_id: HirId, binder: Vec) { - if let Some(old) = self.map.late_bound_vars.insert(hir_id.local_id, binder) { + if let Some(old) = self.rbv.late_bound_vars.insert(hir_id.local_id, binder) { bug!( "overwrote bound vars for {hir_id:?}:\nold={old:?}\nnew={:?}", - self.map.late_bound_vars[&hir_id.local_id] + self.rbv.late_bound_vars[&hir_id.local_id] ) } } @@ -1183,20 +1143,23 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { .params .iter() .map(|param| { - (param.def_id, match param.kind { - GenericParamKind::Lifetime { .. } => { - if self.tcx.is_late_bound(param.hir_id) { - let late_bound_idx = named_late_bound_vars; - named_late_bound_vars += 1; - ResolvedArg::late(late_bound_idx, param) - } else { + ( + param.def_id, + match param.kind { + GenericParamKind::Lifetime { .. } => { + if self.tcx.is_late_bound(param.hir_id) { + let late_bound_idx = named_late_bound_vars; + named_late_bound_vars += 1; + ResolvedArg::late(late_bound_idx, param) + } else { + ResolvedArg::early(param) + } + } + GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { ResolvedArg::early(param) } - } - GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { - ResolvedArg::early(param) - } - }) + }, + ) }) .collect(); @@ -1379,7 +1342,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { } else if let Some(body_id) = outermost_body { let fn_id = self.tcx.hir().body_owner(body_id); match self.tcx.hir_node(fn_id) { - Node::Item(hir::Item { owner_id, kind: hir::ItemKind::Fn(..), .. }) + Node::Item(hir::Item { owner_id, kind: hir::ItemKind::Fn { .. }, .. }) | Node::TraitItem(hir::TraitItem { owner_id, kind: hir::TraitItemKind::Fn(..), @@ -1595,9 +1558,9 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { kind.descr(param_def_id.to_def_id()) ), }; - self.map.defs.insert(hir_id.local_id, ResolvedArg::Error(guar)); + self.rbv.defs.insert(hir_id.local_id, ResolvedArg::Error(guar)); } else { - self.map.defs.insert(hir_id.local_id, def); + self.rbv.defs.insert(hir_id.local_id, def); } return; } @@ -1630,7 +1593,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { bug!("unexpected def-kind: {}", kind.descr(param_def_id.to_def_id())) } }); - self.map.defs.insert(hir_id.local_id, ResolvedArg::Error(guar)); + self.rbv.defs.insert(hir_id.local_id, ResolvedArg::Error(guar)); return; } Scope::Root { .. } => break, @@ -1723,7 +1686,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { } }; - let map = &self.map; + let rbv = &self.rbv; let generics = self.tcx.generics_of(def_id); // `type_def_id` points to an item, so there is nothing to inherit generics from. @@ -1742,7 +1705,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { // This index can be used with `generic_args` since `parent_count == 0`. let index = generics.param_def_id_to_index[¶m_def_id] as usize; generic_args.args.get(index).and_then(|arg| match arg { - GenericArg::Lifetime(lt) => map.defs.get(<.hir_id.local_id).copied(), + GenericArg::Lifetime(lt) => rbv.defs.get(<.hir_id.local_id).copied(), _ => None, }) } @@ -1983,15 +1946,15 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { }, |this| { for input in inputs { - this.visit_ty(input); + this.visit_ty_unambig(input); } if !in_closure && let Some(output) = output { - this.visit_ty(output); + this.visit_ty_unambig(output); } }, ); if in_closure && let Some(output) = output { - self.visit_ty(output); + self.visit_ty_unambig(output); } } @@ -2040,7 +2003,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { #[instrument(level = "debug", skip(self))] fn insert_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime, def: ResolvedArg) { debug!(span = ?lifetime_ref.ident.span); - self.map.defs.insert(lifetime_ref.hir_id.local_id, def); + self.rbv.defs.insert(lifetime_ref.hir_id.local_id, def); } // When we have a return type notation type in a where clause, like @@ -2195,7 +2158,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { // See where these vars are used in `HirTyLowerer::lower_ty_maybe_return_type_notation`. // And this is exercised in: // `tests/ui/associated-type-bounds/return-type-notation/higher-ranked-bound-works.rs`. - let existing_bound_vars = self.map.late_bound_vars.get_mut(&hir_id.local_id).unwrap(); + let existing_bound_vars = self.rbv.late_bound_vars.get_mut(&hir_id.local_id).unwrap(); let existing_bound_vars_saved = existing_bound_vars.clone(); existing_bound_vars.extend(bound_vars); self.record_late_bound_vars(item_segment.hir_id, existing_bound_vars_saved); @@ -2309,7 +2272,7 @@ fn is_late_bound_map( let mut constrained_by_input = ConstrainedCollector { regions: Default::default(), tcx }; for arg_ty in sig.decl.inputs { - constrained_by_input.visit_ty(arg_ty); + constrained_by_input.visit_ty_unambig(arg_ty); } let mut appears_in_output = @@ -2417,7 +2380,7 @@ fn is_late_bound_map( } impl<'v> Visitor<'v> for ConstrainedCollector<'_> { - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) { match ty.kind { hir::TyKind::Path( hir::QPath::Resolved(Some(_), _) | hir::QPath::TypeRelative(..), diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index c0526903e8881..4e12db190fd04 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -1,9 +1,9 @@ use core::ops::ControlFlow; use rustc_errors::{Applicability, StashKey, Suggestions}; -use rustc_hir as hir; -use rustc_hir::HirId; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::intravisit::VisitorExt; +use rustc_hir::{self as hir, AmbigArg, HirId}; use rustc_middle::query::plumbing::CyclePlaceholder; use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::print::with_forced_trimmed_paths; @@ -12,15 +12,16 @@ use rustc_middle::ty::{self, Article, IsSuggestable, Ty, TyCtxt, TypeVisitableEx use rustc_middle::{bug, span_bug}; use rustc_span::{DUMMY_SP, Ident, Span}; -use super::{ItemCtxt, bad_placeholder}; +use super::{HirPlaceholderCollector, ItemCtxt, bad_placeholder}; use crate::errors::TypeofReservedKeywordUsed; use crate::hir_ty_lowering::HirTyLowerer; mod opaque; -fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { +fn anon_const_type_of<'tcx>(icx: &ItemCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { use hir::*; use rustc_middle::ty::Ty; + let tcx = icx.tcx; let hir_id = tcx.local_def_id_to_hir_id(def_id); let node = tcx.hir_node(hir_id); @@ -54,7 +55,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { hir_id: arg_hir_id, kind: ConstArgKind::Anon(&AnonConst { hir_id: anon_hir_id, .. }), .. - }) if anon_hir_id == hir_id => const_arg_anon_type_of(tcx, arg_hir_id, span), + }) if anon_hir_id == hir_id => const_arg_anon_type_of(icx, arg_hir_id, span), // Anon consts outside the type system. Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. }) @@ -138,10 +139,12 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { } } -fn const_arg_anon_type_of<'tcx>(tcx: TyCtxt<'tcx>, arg_hir_id: HirId, span: Span) -> Ty<'tcx> { +fn const_arg_anon_type_of<'tcx>(icx: &ItemCtxt<'tcx>, arg_hir_id: HirId, span: Span) -> Ty<'tcx> { use hir::*; use rustc_middle::ty::Ty; + let tcx = icx.tcx; + match tcx.parent_hir_node(arg_hir_id) { // Array length const arguments do not have `type_of` fed as there is never a corresponding // generic parameter definition. @@ -149,7 +152,15 @@ fn const_arg_anon_type_of<'tcx>(tcx: TyCtxt<'tcx>, arg_hir_id: HirId, span: Span | Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. }) if constant.hir_id == arg_hir_id => { - return tcx.types.usize; + tcx.types.usize + } + + Node::TyPat(pat) => { + let hir::TyKind::Pat(ty, p) = tcx.parent_hir_node(pat.hir_id).expect_ty().kind else { + bug!() + }; + assert_eq!(p.hir_id, pat.hir_id); + icx.lower_ty(ty) } // This is not a `bug!` as const arguments in path segments that did not resolve to anything @@ -293,7 +304,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ } _ => icx.lower_ty(*self_ty), }, - ItemKind::Fn(..) => { + ItemKind::Fn { .. } => { let args = ty::GenericArgs::identity_for_item(tcx, def_id); Ty::new_fn_def(tcx, def_id.to_def_id(), args) } @@ -344,7 +355,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ tcx.typeck(def_id).node_type(hir_id) } - Node::AnonConst(_) => anon_const_type_of(tcx, def_id), + Node::AnonConst(_) => anon_const_type_of(&icx, def_id), Node::ConstBlock(_) => { let args = ty::GenericArgs::identity_for_item(tcx, def_id.to_def_id()); @@ -412,7 +423,7 @@ fn infer_placeholder_type<'tcx>( kind: &'static str, ) -> Ty<'tcx> { let tcx = cx.tcx(); - let ty = tcx.diagnostic_only_typeck(def_id).node_type(body_id.hir_id); + let ty = tcx.typeck(def_id).node_type(body_id.hir_id); // If this came from a free `const` or `static mut?` item, // then the user may have written e.g. `const A = 42;`. @@ -447,13 +458,37 @@ fn infer_placeholder_type<'tcx>( } }) .unwrap_or_else(|| { - let mut diag = bad_placeholder(cx, vec![span], kind); - - if !ty.references_error() { + let mut visitor = HirPlaceholderCollector::default(); + let node = tcx.hir_node_by_def_id(def_id); + if let Some(ty) = node.ty() { + visitor.visit_ty_unambig(ty); + } + // If we have just one span, let's try to steal a const `_` feature error. + let try_steal_span = if !tcx.features().generic_arg_infer() && visitor.spans.len() == 1 + { + visitor.spans.first().copied() + } else { + None + }; + // If we didn't find any infer tys, then just fallback to `span`. + if visitor.spans.is_empty() { + visitor.spans.push(span); + } + let mut diag = bad_placeholder(cx, visitor.spans, kind); + + // HACK(#69396): Stashing and stealing diagnostics does not interact + // well with macros which may delay more than one diagnostic on the + // same span. If this happens, we will fall through to this arm, so + // we need to suppress the suggestion since it's invalid. Ideally we + // would suppress the duplicated error too, but that's really hard. + if span.is_empty() && span.from_expansion() { + // An approximately better primary message + no suggestion... + diag.primary_message("missing type for item"); + } else if !ty.references_error() { if let Some(ty) = ty.make_suggestable(tcx, false, None) { - diag.span_suggestion( + diag.span_suggestion_verbose( span, - "replace with the correct type", + "replace this with a fully-specified type", ty, Applicability::MachineApplicable, ); @@ -464,7 +499,16 @@ fn infer_placeholder_type<'tcx>( )); } } - diag.emit() + + if let Some(try_steal_span) = try_steal_span { + cx.dcx().try_steal_replace_and_emit_err( + try_steal_span, + StashKey::UnderscoreForArrayLengths, + diag, + ) + } else { + diag.emit() + } }); Ty::new_error(tcx, guar) } @@ -491,7 +535,7 @@ pub(crate) fn type_alias_is_lazy<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> struct HasTait; impl<'tcx> Visitor<'tcx> for HasTait { type Result = ControlFlow<()>; - fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) -> Self::Result { + fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result { if let hir::TyKind::OpaqueDef(..) = t.kind { ControlFlow::Break(()) } else { @@ -499,5 +543,5 @@ pub(crate) fn type_alias_is_lazy<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> } } } - HasTait.visit_ty(tcx.hir().expect_item(def_id).expect_ty_alias().0).is_break() + HasTait.visit_ty_unambig(tcx.hir().expect_item(def_id).expect_ty_alias().0).is_break() } diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs index 66255829dcffe..e2b9fe0f9f72d 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs @@ -1,4 +1,3 @@ -use rustc_errors::StashKey; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, Visitor}; @@ -45,7 +44,7 @@ pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type( if !hidden.ty.references_error() { for concrete_type in locator.typeck_types { if concrete_type.ty != tcx.erase_regions(hidden.ty) { - if let Ok(d) = hidden.build_mismatch_error(&concrete_type, def_id, tcx) { + if let Ok(d) = hidden.build_mismatch_error(&concrete_type, tcx) { d.emit(); } } @@ -56,7 +55,7 @@ pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type( } else { let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType { span: tcx.def_span(def_id), - name: tcx.item_name(parent_def_id.to_def_id()), + name: tcx.item_ident(parent_def_id.to_def_id()), what: "impl", }); Ty::new_error(tcx, reported) @@ -121,7 +120,7 @@ pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: Local if !hidden.ty.references_error() { for concrete_type in locator.typeck_types { if concrete_type.ty != tcx.erase_regions(hidden.ty) { - if let Ok(d) = hidden.build_mismatch_error(&concrete_type, def_id, tcx) { + if let Ok(d) = hidden.build_mismatch_error(&concrete_type, tcx) { d.emit(); } } @@ -137,7 +136,7 @@ pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: Local } let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType { span: tcx.def_span(def_id), - name: tcx.item_name(parent_def_id.to_def_id()), + name: tcx.item_ident(parent_def_id.to_def_id()), what: match tcx.hir_node(scope) { _ if scope == hir::CRATE_HIR_ID => "module", Node::Item(hir::Item { kind: hir::ItemKind::Mod(_), .. }) => "module", @@ -187,7 +186,7 @@ impl TaitConstraintLocator<'_> { "foreign items cannot constrain opaque types", ); if let Some(hir_sig) = hir_node.fn_sig() - && hir_sig.decl.output.get_infer_ret_ty().is_some() + && hir_sig.decl.output.is_suggestable_infer_ty().is_some() { let guar = self.tcx.dcx().span_delayed_bug( hir_sig.decl.output.span(), @@ -285,9 +284,8 @@ impl TaitConstraintLocator<'_> { debug!(?concrete_type, "found constraint"); if let Some(prev) = &mut self.found { if concrete_type.ty != prev.ty { - let (Ok(guar) | Err(guar)) = prev - .build_mismatch_error(&concrete_type, self.def_id, self.tcx) - .map(|d| d.emit()); + let (Ok(guar) | Err(guar)) = + prev.build_mismatch_error(&concrete_type, self.tcx).map(|d| d.emit()); prev.ty = Ty::new_error(self.tcx, guar); } } else { @@ -361,11 +359,8 @@ pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>( ); if let Some(prev) = &mut hir_opaque_ty { if concrete_type.ty != prev.ty { - if let Ok(d) = prev.build_mismatch_error(&concrete_type, def_id, tcx) { - d.stash( - tcx.def_span(opaque_type_key.def_id), - StashKey::OpaqueHiddenTypeMismatch, - ); + if let Ok(d) = prev.build_mismatch_error(&concrete_type, tcx) { + d.emit(); } } } else { @@ -435,9 +430,7 @@ impl RpitConstraintChecker<'_> { debug!(?concrete_type, "found constraint"); if concrete_type.ty != self.found.ty { - if let Ok(d) = - self.found.build_mismatch_error(&concrete_type, self.def_id, self.tcx) - { + if let Ok(d) = self.found.build_mismatch_error(&concrete_type, self.tcx) { d.emit(); } } diff --git a/compiler/rustc_hir_analysis/src/delegation.rs b/compiler/rustc_hir_analysis/src/delegation.rs index e65420ea8bf44..4dbdfa3d85a9b 100644 --- a/compiler/rustc_hir_analysis/src/delegation.rs +++ b/compiler/rustc_hir_analysis/src/delegation.rs @@ -41,10 +41,10 @@ impl<'tcx> TypeFolder> for ParamIndexRemapper<'tcx> { if let ty::ReEarlyParam(param) = r.kind() && let Some(index) = self.remap_table.get(¶m.index).copied() { - return ty::Region::new_early_param(self.tcx, ty::EarlyParamRegion { - index, - name: param.name, - }); + return ty::Region::new_early_param( + self.tcx, + ty::EarlyParamRegion { index, name: param.name }, + ); } r } @@ -100,213 +100,156 @@ enum InheritanceKind { Own, } -struct GenericsBuilder<'tcx> { +fn build_generics<'tcx>( tcx: TyCtxt<'tcx>, sig_id: DefId, parent: Option, inh_kind: InheritanceKind, -} - -impl<'tcx> GenericsBuilder<'tcx> { - fn new(tcx: TyCtxt<'tcx>, sig_id: DefId) -> GenericsBuilder<'tcx> { - GenericsBuilder { tcx, sig_id, parent: None, inh_kind: InheritanceKind::WithParent(false) } - } - - fn with_parent(mut self, parent: DefId) -> Self { - self.parent = Some(parent); - self - } - - fn with_inheritance_kind(mut self, inh_kind: InheritanceKind) -> Self { - self.inh_kind = inh_kind; - self - } - - fn build(self) -> ty::Generics { - let mut own_params = vec![]; +) -> ty::Generics { + let mut own_params = vec![]; - let sig_generics = self.tcx.generics_of(self.sig_id); - if let InheritanceKind::WithParent(has_self) = self.inh_kind - && let Some(parent_def_id) = sig_generics.parent - { - let sig_parent_generics = self.tcx.generics_of(parent_def_id); - own_params.append(&mut sig_parent_generics.own_params.clone()); - if !has_self { - own_params.remove(0); - } + let sig_generics = tcx.generics_of(sig_id); + if let InheritanceKind::WithParent(has_self) = inh_kind + && let Some(parent_def_id) = sig_generics.parent + { + let sig_parent_generics = tcx.generics_of(parent_def_id); + own_params.append(&mut sig_parent_generics.own_params.clone()); + if !has_self { + own_params.remove(0); } - own_params.append(&mut sig_generics.own_params.clone()); + } + own_params.append(&mut sig_generics.own_params.clone()); + + // Lifetime parameters must be declared before type and const parameters. + // Therefore, When delegating from a free function to a associated function, + // generic parameters need to be reordered: + // + // trait Trait<'a, A> { + // fn foo<'b, B>(...) {...} + // } + // + // reuse Trait::foo; + // desugaring: + // fn foo<'a, 'b, This: Trait<'a, A>, A, B>(...) { + // Trait::foo(...) + // } + own_params.sort_by_key(|key| key.kind.is_ty_or_const()); + + let param_def_id_to_index = + own_params.iter().map(|param| (param.def_id, param.index)).collect(); + + let (parent_count, has_self) = if let Some(def_id) = parent { + let parent_generics = tcx.generics_of(def_id); + let parent_kind = tcx.def_kind(def_id); + (parent_generics.count(), parent_kind == DefKind::Trait) + } else { + (0, false) + }; - // Lifetime parameters must be declared before type and const parameters. - // Therefore, When delegating from a free function to a associated function, - // generic parameters need to be reordered: + for (idx, param) in own_params.iter_mut().enumerate() { + param.index = (idx + parent_count) as u32; + // FIXME(fn_delegation): Default parameters are not inherited, because they are + // not permitted in functions. Therefore, there are 2 options here: // - // trait Trait<'a, A> { - // fn foo<'b, B>(...) {...} - // } + // - We can create non-default generic parameters. + // - We can substitute default parameters into the signature. // - // reuse Trait::foo; - // desugaring: - // fn foo<'a, 'b, This: Trait<'a, A>, A, B>(...) { - // Trait::foo(...) - // } - own_params.sort_by_key(|key| key.kind.is_ty_or_const()); - - let param_def_id_to_index = - own_params.iter().map(|param| (param.def_id, param.index)).collect(); - - let (parent_count, has_self) = if let Some(def_id) = self.parent { - let parent_generics = self.tcx.generics_of(def_id); - let parent_kind = self.tcx.def_kind(def_id); - (parent_generics.count(), parent_kind == DefKind::Trait) - } else { - (0, false) - }; - - for (idx, param) in own_params.iter_mut().enumerate() { - param.index = (idx + parent_count) as u32; - // FIXME(fn_delegation): Default parameters are not inherited, because they are - // not permitted in functions. Therefore, there are 2 options here: - // - // - We can create non-default generic parameters. - // - We can substitute default parameters into the signature. - // - // At the moment, first option has been selected as the most general. - if let ty::GenericParamDefKind::Type { has_default, .. } - | ty::GenericParamDefKind::Const { has_default, .. } = &mut param.kind - { - *has_default = false; - } + // At the moment, first option has been selected as the most general. + if let ty::GenericParamDefKind::Type { has_default, .. } + | ty::GenericParamDefKind::Const { has_default, .. } = &mut param.kind + { + *has_default = false; } + } - ty::Generics { - parent: self.parent, - parent_count, - own_params, - param_def_id_to_index, - has_self, - has_late_bound_regions: sig_generics.has_late_bound_regions, - } + ty::Generics { + parent, + parent_count, + own_params, + param_def_id_to_index, + has_self, + has_late_bound_regions: sig_generics.has_late_bound_regions, } } -struct PredicatesBuilder<'tcx> { +fn build_predicates<'tcx>( tcx: TyCtxt<'tcx>, sig_id: DefId, parent: Option, inh_kind: InheritanceKind, args: ty::GenericArgsRef<'tcx>, -} - -impl<'tcx> PredicatesBuilder<'tcx> { - fn new( +) -> ty::GenericPredicates<'tcx> { + struct PredicatesCollector<'tcx> { tcx: TyCtxt<'tcx>, + preds: Vec<(ty::Clause<'tcx>, Span)>, args: ty::GenericArgsRef<'tcx>, - sig_id: DefId, - ) -> PredicatesBuilder<'tcx> { - PredicatesBuilder { - tcx, - sig_id, - parent: None, - inh_kind: InheritanceKind::WithParent(false), - args, - } - } - - fn with_parent(mut self, parent: DefId) -> Self { - self.parent = Some(parent); - self } - fn with_inheritance_kind(mut self, inh_kind: InheritanceKind) -> Self { - self.inh_kind = inh_kind; - self - } - - fn build(self) -> ty::GenericPredicates<'tcx> { - struct PredicatesCollector<'tcx> { - tcx: TyCtxt<'tcx>, - preds: Vec<(ty::Clause<'tcx>, Span)>, - args: ty::GenericArgsRef<'tcx>, + impl<'tcx> PredicatesCollector<'tcx> { + fn new(tcx: TyCtxt<'tcx>, args: ty::GenericArgsRef<'tcx>) -> PredicatesCollector<'tcx> { + PredicatesCollector { tcx, preds: vec![], args } } - impl<'tcx> PredicatesCollector<'tcx> { - fn new(tcx: TyCtxt<'tcx>, args: ty::GenericArgsRef<'tcx>) -> PredicatesCollector<'tcx> { - PredicatesCollector { tcx, preds: vec![], args } - } - - fn with_own_preds( - mut self, - f: impl Fn(DefId) -> ty::GenericPredicates<'tcx>, - def_id: DefId, - ) -> Self { - let preds = f(def_id).instantiate_own(self.tcx, self.args); - self.preds.extend(preds); - self - } + fn with_own_preds( + mut self, + f: impl Fn(DefId) -> ty::GenericPredicates<'tcx>, + def_id: DefId, + ) -> Self { + let preds = f(def_id).instantiate_own(self.tcx, self.args); + self.preds.extend(preds); + self + } - fn with_preds( - mut self, - f: impl Fn(DefId) -> ty::GenericPredicates<'tcx> + Copy, - def_id: DefId, - ) -> Self { - let preds = f(def_id); - if let Some(parent_def_id) = preds.parent { - self = self.with_own_preds(f, parent_def_id); - } - self.with_own_preds(f, def_id) + fn with_preds( + mut self, + f: impl Fn(DefId) -> ty::GenericPredicates<'tcx> + Copy, + def_id: DefId, + ) -> Self { + let preds = f(def_id); + if let Some(parent_def_id) = preds.parent { + self = self.with_own_preds(f, parent_def_id); } + self.with_own_preds(f, def_id) } - let collector = PredicatesCollector::new(self.tcx, self.args); - - // `explicit_predicates_of` is used here to avoid copying `Self: Trait` predicate. - // Note: `predicates_of` query can also add inferred outlives predicates, but that - // is not the case here as `sig_id` is either a trait or a function. - let preds = match self.inh_kind { - InheritanceKind::WithParent(false) => { - collector.with_preds(|def_id| self.tcx.explicit_predicates_of(def_id), self.sig_id) - } - InheritanceKind::WithParent(true) => { - collector.with_preds(|def_id| self.tcx.predicates_of(def_id), self.sig_id) - } - InheritanceKind::Own => { - collector.with_own_preds(|def_id| self.tcx.predicates_of(def_id), self.sig_id) - } + } + let collector = PredicatesCollector::new(tcx, args); + + // `explicit_predicates_of` is used here to avoid copying `Self: Trait` predicate. + // Note: `predicates_of` query can also add inferred outlives predicates, but that + // is not the case here as `sig_id` is either a trait or a function. + let preds = match inh_kind { + InheritanceKind::WithParent(false) => { + collector.with_preds(|def_id| tcx.explicit_predicates_of(def_id), sig_id) } - .preds; - - ty::GenericPredicates { - parent: self.parent, - predicates: self.tcx.arena.alloc_from_iter(preds), + InheritanceKind::WithParent(true) => { + collector.with_preds(|def_id| tcx.predicates_of(def_id), sig_id) + } + InheritanceKind::Own => { + collector.with_own_preds(|def_id| tcx.predicates_of(def_id), sig_id) } } + .preds; + + ty::GenericPredicates { parent, predicates: tcx.arena.alloc_from_iter(preds) } } -struct GenericArgsBuilder<'tcx> { +fn build_generic_args<'tcx>( tcx: TyCtxt<'tcx>, - remap_table: RemapTable, sig_id: DefId, def_id: LocalDefId, -} + args: ty::GenericArgsRef<'tcx>, +) -> ty::GenericArgsRef<'tcx> { + let caller_generics = tcx.generics_of(def_id); + let callee_generics = tcx.generics_of(sig_id); -impl<'tcx> GenericArgsBuilder<'tcx> { - fn new(tcx: TyCtxt<'tcx>, sig_id: DefId, def_id: LocalDefId) -> GenericArgsBuilder<'tcx> { - GenericArgsBuilder { tcx, remap_table: FxHashMap::default(), sig_id, def_id } + let mut remap_table = FxHashMap::default(); + for caller_param in &caller_generics.own_params { + let callee_index = callee_generics.param_def_id_to_index(tcx, caller_param.def_id).unwrap(); + remap_table.insert(callee_index, caller_param.index); } - fn build_from_args(mut self, args: ty::GenericArgsRef<'tcx>) -> ty::GenericArgsRef<'tcx> { - let caller_generics = self.tcx.generics_of(self.def_id); - let callee_generics = self.tcx.generics_of(self.sig_id); - - for caller_param in &caller_generics.own_params { - let callee_index = - callee_generics.param_def_id_to_index(self.tcx, caller_param.def_id).unwrap(); - self.remap_table.insert(callee_index, caller_param.index); - } - - let mut folder = ParamIndexRemapper { tcx: self.tcx, remap_table: self.remap_table }; - args.fold_with(&mut folder) - } + let mut folder = ParamIndexRemapper { tcx, remap_table }; + args.fold_with(&mut folder) } fn create_generic_args<'tcx>( @@ -314,8 +257,6 @@ fn create_generic_args<'tcx>( def_id: LocalDefId, sig_id: DefId, ) -> ty::GenericArgsRef<'tcx> { - let builder = GenericArgsBuilder::new(tcx, sig_id, def_id); - let caller_kind = fn_kind(tcx, def_id.into()); let callee_kind = fn_kind(tcx, sig_id); match (caller_kind, callee_kind) { @@ -325,7 +266,7 @@ fn create_generic_args<'tcx>( | (FnKind::AssocTrait, FnKind::Free) | (FnKind::AssocTrait, FnKind::AssocTrait) => { let args = ty::GenericArgs::identity_for_item(tcx, sig_id); - builder.build_from_args(args) + build_generic_args(tcx, sig_id, def_id, args) } (FnKind::AssocTraitImpl, FnKind::AssocTrait) => { @@ -335,8 +276,9 @@ fn create_generic_args<'tcx>( tcx.impl_trait_header(parent).unwrap().trait_ref.instantiate_identity().args; let trait_args = ty::GenericArgs::identity_for_item(tcx, sig_id); - let method_args = tcx.mk_args_from_iter(trait_args.iter().skip(callee_generics.parent_count)); - let method_args = builder.build_from_args(method_args); + let method_args = + tcx.mk_args_from_iter(trait_args.iter().skip(callee_generics.parent_count)); + let method_args = build_generic_args(tcx, sig_id, def_id, method_args); tcx.mk_args_from_iter(parent_args.iter().chain(method_args)) } @@ -347,16 +289,16 @@ fn create_generic_args<'tcx>( let generic_self_ty = ty::GenericArg::from(self_ty); let trait_args = ty::GenericArgs::identity_for_item(tcx, sig_id); - let trait_args = builder.build_from_args(trait_args); + let trait_args = build_generic_args(tcx, sig_id, def_id, trait_args); let args = std::iter::once(generic_self_ty).chain(trait_args.iter().skip(1)); tcx.mk_args_from_iter(args) } // For trait impl's `sig_id` is always equal to the corresponding trait method. + // For inherent methods delegation is not yet supported. (FnKind::AssocTraitImpl, _) | (_, FnKind::AssocTraitImpl) - // Delegation to inherent methods is not yet supported. | (_, FnKind::AssocInherentImpl) => unreachable!(), } } @@ -377,39 +319,31 @@ pub(crate) fn inherit_generics_for_delegation_item<'tcx>( def_id: LocalDefId, sig_id: DefId, ) -> ty::Generics { - let builder = GenericsBuilder::new(tcx, sig_id); - let caller_kind = fn_kind(tcx, def_id.into()); let callee_kind = fn_kind(tcx, sig_id); match (caller_kind, callee_kind) { - (FnKind::Free, FnKind::Free) - | (FnKind::Free, FnKind::AssocTrait) => builder.with_inheritance_kind(InheritanceKind::WithParent(true)).build(), + (FnKind::Free, FnKind::Free) | (FnKind::Free, FnKind::AssocTrait) => { + build_generics(tcx, sig_id, None, InheritanceKind::WithParent(true)) + } (FnKind::AssocTraitImpl, FnKind::AssocTrait) => { - builder - .with_parent(tcx.parent(def_id.into())) - .with_inheritance_kind(InheritanceKind::Own) - .build() + build_generics(tcx, sig_id, Some(tcx.parent(def_id.into())), InheritanceKind::Own) } (FnKind::AssocInherentImpl, FnKind::AssocTrait) - | (FnKind::AssocTrait, FnKind::AssocTrait) => { - builder - .with_parent(tcx.parent(def_id.into())) - .build() - } - - (FnKind::AssocInherentImpl, FnKind::Free) - | (FnKind::AssocTrait, FnKind::Free) => { - builder - .with_parent(tcx.parent(def_id.into())) - .build() - } + | (FnKind::AssocTrait, FnKind::AssocTrait) + | (FnKind::AssocInherentImpl, FnKind::Free) + | (FnKind::AssocTrait, FnKind::Free) => build_generics( + tcx, + sig_id, + Some(tcx.parent(def_id.into())), + InheritanceKind::WithParent(false), + ), // For trait impl's `sig_id` is always equal to the corresponding trait method. + // For inherent methods delegation is not yet supported. (FnKind::AssocTraitImpl, _) | (_, FnKind::AssocTraitImpl) - // Delegation to inherent methods is not yet supported. | (_, FnKind::AssocInherentImpl) => unreachable!(), } } @@ -420,36 +354,36 @@ pub(crate) fn inherit_predicates_for_delegation_item<'tcx>( sig_id: DefId, ) -> ty::GenericPredicates<'tcx> { let args = create_generic_args(tcx, def_id, sig_id); - let builder = PredicatesBuilder::new(tcx, args, sig_id); - let caller_kind = fn_kind(tcx, def_id.into()); let callee_kind = fn_kind(tcx, sig_id); match (caller_kind, callee_kind) { - (FnKind::Free, FnKind::Free) - | (FnKind::Free, FnKind::AssocTrait) => { - builder.with_inheritance_kind(InheritanceKind::WithParent(true)).build() + (FnKind::Free, FnKind::Free) | (FnKind::Free, FnKind::AssocTrait) => { + build_predicates(tcx, sig_id, None, InheritanceKind::WithParent(true), args) } - (FnKind::AssocTraitImpl, FnKind::AssocTrait) => { - builder - .with_parent(tcx.parent(def_id.into())) - .with_inheritance_kind(InheritanceKind::Own) - .build() - } + (FnKind::AssocTraitImpl, FnKind::AssocTrait) => build_predicates( + tcx, + sig_id, + Some(tcx.parent(def_id.into())), + InheritanceKind::Own, + args, + ), (FnKind::AssocInherentImpl, FnKind::AssocTrait) | (FnKind::AssocTrait, FnKind::AssocTrait) | (FnKind::AssocInherentImpl, FnKind::Free) - | (FnKind::AssocTrait, FnKind::Free) => { - builder - .with_parent(tcx.parent(def_id.into())) - .build() - } + | (FnKind::AssocTrait, FnKind::Free) => build_predicates( + tcx, + sig_id, + Some(tcx.parent(def_id.into())), + InheritanceKind::WithParent(false), + args, + ), // For trait impl's `sig_id` is always equal to the corresponding trait method. + // For inherent methods delegation is not yet supported. (FnKind::AssocTraitImpl, _) | (_, FnKind::AssocTraitImpl) - // Delegation to inherent methods is not yet supported. | (_, FnKind::AssocInherentImpl) => unreachable!(), } } diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 00ba1741ed729..14ea10461cbea 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1,8 +1,10 @@ //! Errors emitted by `rustc_hir_analysis`. +use rustc_abi::ExternAbi; use rustc_errors::codes::*; use rustc_errors::{ - Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, MultiSpan, + Applicability, Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, Level, + MultiSpan, }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::Ty; @@ -217,7 +219,7 @@ pub(crate) struct DropImplOnWrongItem { pub(crate) enum FieldAlreadyDeclared { #[diag(hir_analysis_field_already_declared, code = E0124)] NotNested { - field_name: Symbol, + field_name: Ident, #[primary_span] #[label] span: Span, @@ -226,7 +228,7 @@ pub(crate) enum FieldAlreadyDeclared { }, #[diag(hir_analysis_field_already_declared_current_nested)] CurrentNested { - field_name: Symbol, + field_name: Ident, #[primary_span] #[label] span: Span, @@ -239,7 +241,7 @@ pub(crate) enum FieldAlreadyDeclared { }, #[diag(hir_analysis_field_already_declared_previous_nested)] PreviousNested { - field_name: Symbol, + field_name: Ident, #[primary_span] #[label] span: Span, @@ -252,7 +254,7 @@ pub(crate) enum FieldAlreadyDeclared { }, #[diag(hir_analysis_field_already_declared_both_nested)] BothNested { - field_name: Symbol, + field_name: Ident, #[primary_span] #[label] span: Span, @@ -418,7 +420,7 @@ pub(crate) struct ValueOfAssociatedStructAlreadySpecified { pub(crate) struct UnconstrainedOpaqueType { #[primary_span] pub span: Span, - pub name: Symbol, + pub name: Ident, pub what: &'static str, } @@ -619,48 +621,6 @@ pub(crate) struct TargetFeatureOnMain { pub main: Span, } -#[derive(Diagnostic)] -#[diag(hir_analysis_start_not_track_caller)] -pub(crate) struct StartTrackCaller { - #[primary_span] - pub span: Span, - #[label] - pub start: Span, -} - -#[derive(Diagnostic)] -#[diag(hir_analysis_start_not_target_feature)] -pub(crate) struct StartTargetFeature { - #[primary_span] - pub span: Span, - #[label] - pub start: Span, -} - -#[derive(Diagnostic)] -#[diag(hir_analysis_start_not_async, code = E0752)] -pub(crate) struct StartAsync { - #[primary_span] - #[label] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(hir_analysis_start_function_where, code = E0647)] -pub(crate) struct StartFunctionWhere { - #[primary_span] - #[label] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(hir_analysis_start_function_parameters, code = E0132)] -pub(crate) struct StartFunctionParameters { - #[primary_span] - #[label] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(hir_analysis_main_function_return_type_generic, code = E0131)] pub(crate) struct MainFunctionReturnTypeGeneric { @@ -688,10 +648,11 @@ pub(crate) struct MainFunctionGenericParameters { #[derive(Diagnostic)] #[diag(hir_analysis_variadic_function_compatible_convention, code = E0045)] -pub(crate) struct VariadicFunctionCompatibleConvention { +pub(crate) struct VariadicFunctionCompatibleConvention<'a> { #[primary_span] #[label] pub span: Span, + pub conventions: &'a str, } #[derive(Diagnostic)] @@ -844,7 +805,7 @@ pub(crate) struct EnumDiscriminantOverflowed { #[label] pub span: Span, pub discr: String, - pub item_name: Symbol, + pub item_name: Ident, pub wrapped_discr: String, } @@ -935,7 +896,7 @@ pub(crate) enum ImplNotMarkedDefault { span: Span, #[label(hir_analysis_ok_label)] ok_label: Span, - ident: Symbol, + ident: Ident, }, #[diag(hir_analysis_impl_not_marked_default_err, code = E0520)] #[note] @@ -943,7 +904,7 @@ pub(crate) enum ImplNotMarkedDefault { #[primary_span] span: Span, cname: Symbol, - ident: Symbol, + ident: Ident, }, } @@ -1019,7 +980,7 @@ pub(crate) struct MissingTraitItemUnstable { pub some_note: bool, #[note(hir_analysis_none_note)] pub none_note: bool, - pub missing_item_name: Symbol, + pub missing_item_name: Ident, pub feature: Symbol, pub reason: String, } @@ -1222,6 +1183,42 @@ pub(crate) struct DispatchFromDynRepr { pub span: Span, } +#[derive(Diagnostic)] +#[diag(hir_analysis_coerce_pointee_not_struct, code = E0802)] +pub(crate) struct CoercePointeeNotStruct { + #[primary_span] + pub span: Span, + pub kind: String, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_coerce_pointee_not_concrete_ty, code = E0802)] +pub(crate) struct CoercePointeeNotConcreteType { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_coerce_pointee_no_user_validity_assertion, code = E0802)] +pub(crate) struct CoercePointeeNoUserValidityAssertion { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_coerce_pointee_not_transparent, code = E0802)] +pub(crate) struct CoercePointeeNotTransparent { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_coerce_pointee_no_field, code = E0802)] +pub(crate) struct CoercePointeeNoField { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(hir_analysis_inherent_ty_outside_relevant, code = E0390)] #[help] @@ -1291,7 +1288,7 @@ pub(crate) struct InherentNominal { pub(crate) struct DispatchFromDynZST<'a> { #[primary_span] pub span: Span, - pub name: Symbol, + pub name: Ident, pub ty: Ty<'a>, } @@ -1431,7 +1428,7 @@ pub(crate) struct TyParamFirstLocal<'tcx> { pub span: Span, #[note(hir_analysis_case_note)] pub note: (), - pub param: Symbol, + pub param: Ident, pub local_type: Ty<'tcx>, } @@ -1443,7 +1440,7 @@ pub(crate) struct TyParamFirstLocalLint<'tcx> { pub span: Span, #[note(hir_analysis_case_note)] pub note: (), - pub param: Symbol, + pub param: Ident, pub local_type: Ty<'tcx>, } @@ -1456,7 +1453,7 @@ pub(crate) struct TyParamSome { pub span: Span, #[note(hir_analysis_only_note)] pub note: (), - pub param: Symbol, + pub param: Ident, } #[derive(LintDiagnostic)] @@ -1467,7 +1464,7 @@ pub(crate) struct TyParamSomeLint { pub span: Span, #[note(hir_analysis_only_note)] pub note: (), - pub param: Symbol, + pub param: Ident, } #[derive(Diagnostic)] @@ -1575,7 +1572,7 @@ pub(crate) struct UnsupportedDelegation<'a> { pub(crate) struct MethodShouldReturnFuture { #[primary_span] pub span: Span, - pub method_name: Symbol, + pub method_name: Ident, #[note] pub trait_item_span: Option, } @@ -1627,7 +1624,7 @@ pub(crate) struct UnconstrainedGenericParameter { #[primary_span] #[label] pub span: Span, - pub param_name: Symbol, + pub param_name: Ident, pub param_def_kind: &'static str, #[note(hir_analysis_const_param_note)] pub const_param_note: bool, @@ -1647,13 +1644,6 @@ pub(crate) struct OpaqueCapturesHigherRankedLifetime { pub bad_place: &'static str, } -#[derive(Diagnostic)] -#[diag(hir_analysis_pattern_type_non_const_range)] -pub(crate) struct NonConstRange { - #[primary_span] - pub span: Span, -} - #[derive(Subdiagnostic)] pub(crate) enum InvalidReceiverTyHint { #[note(hir_analysis_invalid_receiver_ty_help_weak_note)] @@ -1702,7 +1692,7 @@ pub(crate) struct CmseInputsStackSpill { #[label] pub span: Span, pub plural: bool, - pub abi_name: &'static str, + pub abi: ExternAbi, } #[derive(Diagnostic)] @@ -1713,7 +1703,7 @@ pub(crate) struct CmseOutputStackSpill { #[primary_span] #[label] pub span: Span, - pub abi_name: &'static str, + pub abi: ExternAbi, } #[derive(Diagnostic)] @@ -1744,3 +1734,28 @@ pub(crate) struct RegisterTypeUnstable<'a> { pub span: Span, pub ty: Ty<'a>, } + +#[derive(LintDiagnostic)] +#[diag(hir_analysis_supertrait_item_shadowing)] +pub(crate) struct SupertraitItemShadowing { + pub item: Symbol, + pub subtrait: Symbol, + #[subdiagnostic] + pub shadowee: SupertraitItemShadowee, +} + +#[derive(Subdiagnostic)] +pub(crate) enum SupertraitItemShadowee { + #[note(hir_analysis_supertrait_item_shadowee)] + Labeled { + #[primary_span] + span: Span, + supertrait: Symbol, + }, + #[note(hir_analysis_supertrait_item_multiple_shadowee)] + Several { + #[primary_span] + spans: MultiSpan, + traits: DiagSymbolList, + }, +} diff --git a/compiler/rustc_hir_analysis/src/errors/pattern_types.rs b/compiler/rustc_hir_analysis/src/errors/pattern_types.rs index bb771d6ea170e..ec7b3aaa1c14c 100644 --- a/compiler/rustc_hir_analysis/src/errors/pattern_types.rs +++ b/compiler/rustc_hir_analysis/src/errors/pattern_types.rs @@ -1,9 +1,14 @@ use rustc_macros::Diagnostic; +use rustc_middle::ty::Ty; use rustc_span::Span; #[derive(Diagnostic)] -#[diag(hir_analysis_pattern_type_wild_pat)] -pub(crate) struct WildPatTy { +#[diag(hir_analysis_invalid_base_type)] +pub(crate) struct InvalidBaseType<'tcx> { + pub ty: Ty<'tcx>, #[primary_span] - pub span: Span, + pub ty_span: Span, + pub pat: &'static str, + #[note] + pub pat_span: Span, } diff --git a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs index 5ae7944f6d53e..b2501d647a55b 100644 --- a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs @@ -495,7 +495,8 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { .iter() .any(|constraint| constraint.ident.name == item.name) }) - .map(|item| item.name.to_ident_string()) + .filter(|item| !item.is_impl_trait_in_trait()) + .map(|item| self.tcx.item_ident(item.def_id).to_string()) .collect() } else { Vec::default() diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index e949d4a11261e..ef6167907b5b2 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -102,8 +102,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { seen_sized_unbound = true; continue; } - // There was a `?Trait` bound, but it was not `?Sized`; warn. - self.dcx().span_warn( + // There was a `?Trait` bound, but it was not `?Sized` + self.dcx().span_err( unbound.span, "relaxing a default bound only does something for `?Sized`; \ all other traits are not bound by default", @@ -640,13 +640,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let mut num_bound_vars = candidate.bound_vars().len(); let args = candidate.skip_binder().args.extend_to(tcx, item_def_id, |param, _| { let arg = match param.kind { - ty::GenericParamDefKind::Lifetime => { - ty::Region::new_bound(tcx, ty::INNERMOST, ty::BoundRegion { + ty::GenericParamDefKind::Lifetime => ty::Region::new_bound( + tcx, + ty::INNERMOST, + ty::BoundRegion { var: ty::BoundVar::from_usize(num_bound_vars), kind: ty::BoundRegionKind::Named(param.def_id, param.name), - }) - .into() - } + }, + ) + .into(), ty::GenericParamDefKind::Type { .. } => { let guar = *emitted_bad_param_err.get_or_insert_with(|| { self.dcx().emit_err(crate::errors::ReturnTypeNotationIllegalParam::Type { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 394719314612e..5fed2e352879c 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -17,8 +17,6 @@ pub(crate) fn validate_cmse_abi<'tcx>( abi: ExternAbi, fn_sig: ty::PolyFnSig<'tcx>, ) { - let abi_name = abi.name(); - match abi { ExternAbi::CCmseNonSecureCall => { let hir_node = tcx.hir_node(hir_id); @@ -56,7 +54,7 @@ pub(crate) fn validate_cmse_abi<'tcx>( .to(bare_fn_ty.decl.inputs[index].span) .to(bare_fn_ty.decl.inputs.last().unwrap().span); let plural = bare_fn_ty.param_names.len() - index != 1; - dcx.emit_err(errors::CmseInputsStackSpill { span, plural, abi_name }); + dcx.emit_err(errors::CmseInputsStackSpill { span, plural, abi }); } Err(layout_err) => { if should_emit_generic_error(abi, layout_err) { @@ -69,7 +67,7 @@ pub(crate) fn validate_cmse_abi<'tcx>( Ok(true) => {} Ok(false) => { let span = bare_fn_ty.decl.output.span(); - dcx.emit_err(errors::CmseOutputStackSpill { span, abi_name }); + dcx.emit_err(errors::CmseOutputStackSpill { span, abi }); } Err(layout_err) => { if should_emit_generic_error(abi, layout_err) { @@ -92,7 +90,7 @@ pub(crate) fn validate_cmse_abi<'tcx>( // ^^^^^^ let span = decl.inputs[index].span.to(decl.inputs.last().unwrap().span); let plural = decl.inputs.len() - index != 1; - dcx.emit_err(errors::CmseInputsStackSpill { span, plural, abi_name }); + dcx.emit_err(errors::CmseInputsStackSpill { span, plural, abi }); } Err(layout_err) => { if should_emit_generic_error(abi, layout_err) { @@ -105,7 +103,7 @@ pub(crate) fn validate_cmse_abi<'tcx>( Ok(true) => {} Ok(false) => { let span = decl.output.span(); - dcx.emit_err(errors::CmseOutputStackSpill { span, abi_name }); + dcx.emit_err(errors::CmseOutputStackSpill { span, abi }); } Err(layout_err) => { if should_emit_generic_error(abi, layout_err) { @@ -201,7 +199,7 @@ fn should_emit_generic_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError use LayoutError::*; match layout_err { - Unknown(ty) => { + TooGeneric(ty) => { match abi { ExternAbi::CCmseNonSecureCall => { // prevent double reporting of this error @@ -211,7 +209,11 @@ fn should_emit_generic_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError _ => bug!("invalid ABI: {abi}"), } } - SizeOverflow(..) | NormalizationFailure(..) | ReferencesError(..) | Cycle(..) => { + Unknown(..) + | SizeOverflow(..) + | NormalizationFailure(..) + | ReferencesError(..) + | Cycle(..) => { false // not our job to report these } } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs index 71a5727ed6cdb..a8b37fa505420 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs @@ -4,13 +4,12 @@ use rustc_errors::struct_span_code_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS; -use rustc_middle::span_bug; use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::{ self, DynKind, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast, }; -use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; +use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility; use rustc_trait_selection::traits::{self, hir_ty_lowering_dyn_compatibility_violations}; use rustc_type_ir::elaborate::ClauseWithSupertraitSpan; @@ -30,16 +29,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, span: Span, hir_id: hir::HirId, - hir_trait_bounds: &[hir::PolyTraitRef<'tcx>], + hir_bounds: &[hir::PolyTraitRef<'tcx>], lifetime: &hir::Lifetime, representation: DynKind, ) -> Ty<'tcx> { let tcx = self.tcx(); + let dummy_self = tcx.types.trait_object_dummy_self; - let mut bounds = Bounds::default(); + let mut user_written_bounds = Bounds::default(); let mut potential_assoc_types = Vec::new(); - let dummy_self = self.tcx().types.trait_object_dummy_self; - for trait_bound in hir_trait_bounds.iter().rev() { + for trait_bound in hir_bounds.iter() { if let hir::BoundPolarity::Maybe(_) = trait_bound.modifiers.polarity { continue; } @@ -53,92 +52,66 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir::BoundConstness::Never, hir::BoundPolarity::Positive, dummy_self, - &mut bounds, + &mut user_written_bounds, PredicateFilter::SelfOnly, ) { potential_assoc_types.extend(cur_potential_assoc_types); } } - let mut trait_bounds = vec![]; - let mut projection_bounds = vec![]; - for (pred, span) in bounds.clauses() { - let bound_pred = pred.kind(); - match bound_pred.skip_binder() { - ty::ClauseKind::Trait(trait_pred) => { - assert_eq!(trait_pred.polarity, ty::PredicatePolarity::Positive); - trait_bounds.push((bound_pred.rebind(trait_pred.trait_ref), span)); - } - ty::ClauseKind::Projection(proj) => { - projection_bounds.push((bound_pred.rebind(proj), span)); - } - ty::ClauseKind::TypeOutlives(_) => { - // Do nothing, we deal with regions separately - } - ty::ClauseKind::RegionOutlives(_) - | ty::ClauseKind::ConstArgHasType(..) - | ty::ClauseKind::WellFormed(_) - | ty::ClauseKind::ConstEvaluatable(_) - | ty::ClauseKind::HostEffect(..) => { - span_bug!(span, "did not expect {pred} clause in object bounds"); - } - } - } - - // Expand trait aliases recursively and check that only one regular (non-auto) trait - // is used and no 'maybe' bounds are used. - let expanded_traits = - traits::expand_trait_aliases(tcx, trait_bounds.iter().map(|&(a, b)| (a, b))); - - let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = - expanded_traits.partition(|i| tcx.trait_is_auto(i.trait_ref().def_id())); + let (trait_bounds, mut projection_bounds) = + traits::expand_trait_aliases(tcx, user_written_bounds.clauses()); + let (regular_traits, mut auto_traits): (Vec<_>, Vec<_>) = trait_bounds + .into_iter() + .partition(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id())); + // We don't support empty trait objects. + if regular_traits.is_empty() && auto_traits.is_empty() { + let guar = + self.report_trait_object_with_no_traits_error(span, user_written_bounds.clauses()); + return Ty::new_error(tcx, guar); + } // We don't support >1 principal if regular_traits.len() > 1 { let guar = self.report_trait_object_addition_traits_error(®ular_traits); return Ty::new_error(tcx, guar); } - // We don't support empty trait objects. - if regular_traits.is_empty() && auto_traits.is_empty() { - let guar = self.report_trait_object_with_no_traits_error(span, &trait_bounds); - return Ty::new_error(tcx, guar); - } // Don't create a dyn trait if we have errors in the principal. - if let Err(guar) = trait_bounds.error_reported() { + if let Err(guar) = regular_traits.error_reported() { return Ty::new_error(tcx, guar); } // Check that there are no gross dyn-compatibility violations; // most importantly, that the supertraits don't contain `Self`, // to avoid ICEs. - for item in ®ular_traits { - let violations = - hir_ty_lowering_dyn_compatibility_violations(tcx, item.trait_ref().def_id()); - if !violations.is_empty() { - let reported = report_dyn_incompatibility( - tcx, - span, - Some(hir_id), - item.trait_ref().def_id(), - &violations, - ) - .emit(); - return Ty::new_error(tcx, reported); + for (clause, span) in user_written_bounds.clauses() { + if let Some(trait_pred) = clause.as_trait_clause() { + let violations = + hir_ty_lowering_dyn_compatibility_violations(tcx, trait_pred.def_id()); + if !violations.is_empty() { + let reported = report_dyn_incompatibility( + tcx, + span, + Some(hir_id), + trait_pred.def_id(), + &violations, + ) + .emit(); + return Ty::new_error(tcx, reported); + } } } - let mut needed_associated_types = FxIndexSet::default(); - - let principal_span = regular_traits.first().map_or(DUMMY_SP, |info| info.bottom().1); - let regular_traits_refs_spans = trait_bounds - .into_iter() - .filter(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id())); + let principal_trait = regular_traits.into_iter().next(); - for (base_trait_ref, original_span) in regular_traits_refs_spans { - let base_pred: ty::Predicate<'tcx> = base_trait_ref.upcast(tcx); - for ClauseWithSupertraitSpan { pred, supertrait_span } in - traits::elaborate(tcx, [ClauseWithSupertraitSpan::new(base_pred, original_span)]) - .filter_only_self() + let mut needed_associated_types = FxIndexSet::default(); + if let Some((principal_trait, spans)) = &principal_trait { + let pred: ty::Predicate<'tcx> = (*principal_trait).upcast(tcx); + for ClauseWithSupertraitSpan { pred, supertrait_span } in traits::elaborate( + tcx, + [ClauseWithSupertraitSpan::new(pred, *spans.last().unwrap())], + ) + .filter_only_self() { debug!("observing object predicate `{pred:?}`"); @@ -179,7 +152,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // } // ``` // - // Here, the user could theoretically write `dyn MyTrait`, + // Here, the user could theoretically write `dyn MyTrait`, // but actually supporting that would "expand" to an infinitely-long type // `fix $ τ → dyn MyTrait::MyOutput`. // @@ -188,12 +161,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // the discussion in #56288 for alternatives. if !references_self { // Include projections defined on supertraits. - projection_bounds.push((pred, original_span)); + projection_bounds.push((pred, supertrait_span)); } self.check_elaborated_projection_mentions_input_lifetimes( pred, - original_span, + *spans.first().unwrap(), supertrait_span, ); } @@ -202,11 +175,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - // `dyn Trait` desugars to (not Rust syntax) `dyn Trait where ::Assoc = Foo`. - // So every `Projection` clause is an `Assoc = Foo` bound. `associated_types` contains all associated - // types's `DefId`, so the following loop removes all the `DefIds` of the associated types that have a - // corresponding `Projection` clause - for (projection_bound, span) in &projection_bounds { + // `dyn Trait` desugars to (not Rust syntax) `dyn Trait where + // ::Assoc = Foo`. So every `Projection` clause is an + // `Assoc = Foo` bound. `needed_associated_types` contains all associated + // types that we expect to be provided by the user, so the following loop + // removes all the associated types that have a corresponding `Projection` + // clause, either from expanding trait aliases or written by the user. + for &(projection_bound, span) in &projection_bounds { let def_id = projection_bound.item_def_id(); let trait_ref = tcx.anonymize_bound_vars( projection_bound.map_bound(|p| p.projection_term.trait_ref(tcx)), @@ -216,17 +191,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { tcx.emit_node_span_lint( UNUSED_ASSOCIATED_TYPE_BOUNDS, hir_id, - *span, - crate::errors::UnusedAssociatedTypeBounds { span: *span }, + span, + crate::errors::UnusedAssociatedTypeBounds { span }, ); } } if let Err(guar) = self.check_for_required_assoc_tys( - principal_span, + principal_trait.as_ref().map_or(smallvec![], |(_, spans)| spans.clone()), needed_associated_types, potential_assoc_types, - hir_trait_bounds, + hir_bounds, ) { return Ty::new_error(tcx, guar); } @@ -236,45 +211,42 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // We remove duplicates by inserting into a `FxHashSet` to avoid re-ordering // the bounds let mut duplicates = FxHashSet::default(); - auto_traits.retain(|i| duplicates.insert(i.trait_ref().def_id())); - debug!(?regular_traits); + auto_traits.retain(|(trait_pred, _)| duplicates.insert(trait_pred.def_id())); + + debug!(?principal_trait); debug!(?auto_traits); // Erase the `dummy_self` (`trait_object_dummy_self`) used above. - let existential_trait_refs = regular_traits.iter().map(|i| { - i.trait_ref().map_bound(|trait_ref: ty::TraitRef<'tcx>| { + let principal_trait_ref = principal_trait.map(|(trait_pred, spans)| { + trait_pred.map_bound(|trait_pred| { + let trait_ref = trait_pred.trait_ref; + assert_eq!(trait_pred.polarity, ty::PredicatePolarity::Positive); assert_eq!(trait_ref.self_ty(), dummy_self); + let span = *spans.first().unwrap(); + // Verify that `dummy_self` did not leak inside default type parameters. This // could not be done at path creation, since we need to see through trait aliases. let mut missing_type_params = vec![]; - let mut references_self = false; let generics = tcx.generics_of(trait_ref.def_id); let args: Vec<_> = trait_ref .args .iter() .enumerate() - .skip(1) // Remove `Self` for `ExistentialPredicate`. + // Skip `Self` + .skip(1) .map(|(index, arg)| { - if arg == dummy_self.into() { + if arg.walk().any(|arg| arg == dummy_self.into()) { let param = &generics.own_params[index]; missing_type_params.push(param.name); Ty::new_misc_error(tcx).into() - } else if arg.walk().any(|arg| arg == dummy_self.into()) { - references_self = true; - let guar = self.dcx().span_delayed_bug( - span, - "trait object trait bounds reference `Self`", - ); - replace_dummy_self_with_error(tcx, arg, guar) } else { arg } }) .collect(); - let span = i.bottom().1; - let empty_generic_args = hir_trait_bounds.iter().any(|hir_bound| { + let empty_generic_args = hir_bounds.iter().any(|hir_bound| { hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id) && hir_bound.span.contains(span) }); @@ -285,26 +257,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { empty_generic_args, ); - if references_self { - let def_id = i.bottom().0.def_id(); - struct_span_code_err!( - self.dcx(), - i.bottom().1, - E0038, - "the {} `{}` cannot be made into an object", - tcx.def_descr(def_id), - tcx.item_name(def_id), - ) - .with_note( - rustc_middle::traits::DynCompatibilityViolation::SupertraitSelf( - smallvec![], - ) - .error_msg(), - ) - .emit(); - } - - ty::ExistentialTraitRef::new(tcx, trait_ref.def_id, args) + ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::new( + tcx, + trait_ref.def_id, + args, + )) }) }); @@ -327,21 +284,24 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { b.projection_term = replace_dummy_self_with_error(tcx, b.projection_term, guar); } - ty::ExistentialProjection::erase_self_ty(tcx, b) + ty::ExistentialPredicate::Projection(ty::ExistentialProjection::erase_self_ty( + tcx, b, + )) }) }); - let regular_trait_predicates = existential_trait_refs - .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)); - let auto_trait_predicates = auto_traits.into_iter().map(|trait_ref| { - ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_ref.trait_ref().def_id())) + let auto_trait_predicates = auto_traits.into_iter().map(|(trait_pred, _)| { + assert_eq!(trait_pred.polarity(), ty::PredicatePolarity::Positive); + assert_eq!(trait_pred.self_ty().skip_binder(), dummy_self); + + ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_pred.def_id())) }); + // N.b. principal, projections, auto traits // FIXME: This is actually wrong with multiple principals in regards to symbol mangling - let mut v = regular_trait_predicates - .chain( - existential_projections.map(|x| x.map_bound(ty::ExistentialPredicate::Projection)), - ) + let mut v = principal_trait_ref + .into_iter() + .chain(existential_projections) .chain(auto_trait_predicates) .collect::>(); v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 0623d35853e32..7eb982a31798c 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -3,15 +3,16 @@ use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::unord::UnordMap; use rustc_errors::codes::*; use rustc_errors::{ - Applicability, Diag, ErrorGuaranteed, MultiSpan, pluralize, struct_span_code_err, + Applicability, Diag, ErrorGuaranteed, MultiSpan, listify, pluralize, struct_span_code_err, }; use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_middle::bug; +use rustc_middle::ty::fast_reject::{TreatParams, simplify_type}; use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _}; use rustc_middle::ty::{ - self, AdtDef, Binder, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeVisitableExt, + self, AdtDef, GenericParamDefKind, Ty, TyCtxt, TypeVisitableExt, suggest_constraining_type_param, }; use rustc_session::parse::feature_err; @@ -19,8 +20,9 @@ use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, kw, sym}; use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility; use rustc_trait_selection::traits::{ - FulfillmentError, TraitAliasExpansionInfo, dyn_compatibility_violations_for_assoc_item, + FulfillmentError, dyn_compatibility_violations_for_assoc_item, }; +use smallvec::SmallVec; use crate::errors::{ self, AssocItemConstraintsNotAllowedHere, ManualImplementation, MissingTypeParams, @@ -179,7 +181,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // all visible traits. If there's one clear winner, just suggest that. let visible_traits: Vec<_> = tcx - .all_traits() + .visible_traits() .filter(|trait_def_id| { let viz = tcx.visibility(*trait_def_id); let def_id = self.item_def_id(); @@ -720,7 +722,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// emit a generic note suggesting using a `where` clause to constraint instead. pub(crate) fn check_for_required_assoc_tys( &self, - principal_span: Span, + spans: SmallVec<[Span; 1]>, missing_assoc_types: FxIndexSet<(DefId, ty::PolyTraitRef<'tcx>)>, potential_assoc_types: Vec, trait_bounds: &[hir::PolyTraitRef<'_>], @@ -729,6 +731,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { return Ok(()); } + let principal_span = *spans.first().unwrap(); + let tcx = self.tcx(); // FIXME: This logic needs some more care w.r.t handling of conflicts let missing_assoc_types: Vec<_> = missing_assoc_types @@ -804,14 +808,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .map(|(trait_, mut assocs)| { assocs.sort(); let trait_ = trait_.print_trait_sugared(); - format!("{} in `{trait_}`", match &assocs[..] { - [] => String::new(), - [only] => format!("`{only}`"), - [assocs @ .., last] => format!( - "{} and `{last}`", - assocs.iter().map(|a| format!("`{a}`")).collect::>().join(", ") - ), - }) + format!( + "{} in `{trait_}`", + listify(&assocs[..], |a| format!("`{a}`")).unwrap_or_default() + ) }) .collect::>(); names.sort(); @@ -995,12 +995,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { )), .. }) = node - && let Some(adt_def) = qself_ty.ty_adt_def() - && let [inherent_impl] = tcx.inherent_impls(adt_def.did()) - && let name = format!("{ident2}_{ident3}") - && let Some(ty::AssocItem { kind: ty::AssocKind::Fn, .. }) = tcx - .associated_items(inherent_impl) - .filter_by_name_unhygienic(Symbol::intern(&name)) + && let Some(inherent_impls) = qself_ty + .ty_adt_def() + .map(|adt_def| tcx.inherent_impls(adt_def.did())) + .or_else(|| { + simplify_type(tcx, qself_ty, TreatParams::InstantiateWithInfer) + .map(|simple_ty| tcx.incoherent_impls(simple_ty)) + }) + && let name = Symbol::intern(&format!("{ident2}_{ident3}")) + && let Some(ty::AssocItem { kind: ty::AssocKind::Fn, .. }) = inherent_impls + .iter() + .flat_map(|inherent_impl| { + tcx.associated_items(inherent_impl).filter_by_name_unhygienic(name) + }) .next() { Err(struct_span_code_err!(self.dcx(), span, E0223, "ambiguous associated type") @@ -1020,7 +1027,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, segments: impl Iterator> + Clone, args_visitors: impl Iterator> + Clone, - err_extend: GenericsArgsErrExtend<'_>, + err_extend: GenericsArgsErrExtend<'a>, ) -> ErrorGuaranteed { #[derive(PartialEq, Eq, Hash)] enum ProhibitGenericsArg { @@ -1040,23 +1047,24 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; }); + let segments: Vec<_> = segments.collect(); let types_and_spans: Vec<_> = segments - .clone() + .iter() .flat_map(|segment| { if segment.args().args.is_empty() { None } else { Some(( match segment.res { - hir::def::Res::PrimTy(ty) => { + Res::PrimTy(ty) => { format!("{} `{}`", segment.res.descr(), ty.name()) } - hir::def::Res::Def(_, def_id) + Res::Def(_, def_id) if let Some(name) = self.tcx().opt_item_name(def_id) => { format!("{} `{name}`", segment.res.descr()) } - hir::def::Res::Err => "this type".to_string(), + Res::Err => "this type".to_string(), _ => segment.res.descr().to_string(), }, segment.ident.span, @@ -1064,24 +1072,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } }) .collect(); - let this_type = match &types_and_spans[..] { - [.., _, (last, _)] => format!( - "{} and {last}", - types_and_spans[..types_and_spans.len() - 1] - .iter() - .map(|(x, _)| x.as_str()) - .intersperse(", ") - .collect::() - ), - [(only, _)] => only.to_string(), - [] => bug!("expected one segment to deny"), - }; + let this_type = listify(&types_and_spans, |(t, _)| t.to_string()) + .expect("expected one segment to deny"); - let arg_spans: Vec = segments - .clone() - .flat_map(|segment| segment.args().args) - .map(|arg| arg.span()) - .collect(); + let arg_spans: Vec = + segments.iter().flat_map(|segment| segment.args().args).map(|arg| arg.span()).collect(); let mut kinds = Vec::with_capacity(4); prohibit_args.iter().for_each(|arg| match arg { @@ -1091,21 +1086,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ProhibitGenericsArg::Infer => kinds.push("generic"), }); - let (kind, s) = match kinds[..] { - [.., _, last] => ( - format!( - "{} and {last}", - kinds[..kinds.len() - 1] - .iter() - .map(|&x| x) - .intersperse(", ") - .collect::() - ), - "s", - ), - [only] => (only.to_string(), ""), - [] => bug!("expected at least one generic to prohibit"), - }; + let s = pluralize!(kinds.len()); + let kind = + listify(&kinds, |k| k.to_string()).expect("expected at least one generic to prohibit"); let last_span = *arg_spans.last().unwrap(); let span: MultiSpan = arg_spans.into(); let mut err = struct_span_code_err!( @@ -1118,35 +1101,42 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { for (what, span) in types_and_spans { err.span_label(span, format!("not allowed on {what}")); } - generics_args_err_extend(self.tcx(), segments, &mut err, err_extend); + generics_args_err_extend(self.tcx(), segments.into_iter(), &mut err, err_extend); err.emit() } pub fn report_trait_object_addition_traits_error( &self, - regular_traits: &Vec>, + regular_traits: &Vec<(ty::PolyTraitPredicate<'tcx>, SmallVec<[Span; 1]>)>, ) -> ErrorGuaranteed { - let first_trait = ®ular_traits[0]; - let additional_trait = ®ular_traits[1]; + // we use the last span to point at the traits themselves, + // and all other preceding spans are trait alias expansions. + let (&first_span, first_alias_spans) = regular_traits[0].1.split_last().unwrap(); + let (&second_span, second_alias_spans) = regular_traits[1].1.split_last().unwrap(); let mut err = struct_span_code_err!( self.dcx(), - additional_trait.bottom().1, + *regular_traits[1].1.first().unwrap(), E0225, "only auto traits can be used as additional traits in a trait object" ); - additional_trait.label_with_exp_info( - &mut err, - "additional non-auto trait", - "additional use", - ); - first_trait.label_with_exp_info(&mut err, "first non-auto trait", "first use"); + err.span_label(first_span, "first non-auto trait"); + for &alias_span in first_alias_spans { + err.span_label(alias_span, "first non-auto trait comes from this alias"); + } + err.span_label(second_span, "additional non-auto trait"); + for &alias_span in second_alias_spans { + err.span_label(alias_span, "second non-auto trait comes from this alias"); + } err.help(format!( "consider creating a new trait with all of these as supertraits and using that \ trait here instead: `trait NewTrait: {} {{}}`", regular_traits .iter() // FIXME: This should `print_sugared`, but also needs to integrate projection bounds... - .map(|t| t.trait_ref().print_only_trait_path().to_string()) + .map(|(pred, _)| pred + .map_bound(|pred| pred.trait_ref) + .print_only_trait_path() + .to_string()) .collect::>() .join(" + "), )); @@ -1161,14 +1151,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { pub fn report_trait_object_with_no_traits_error( &self, span: Span, - trait_bounds: &Vec<(Binder<'tcx, TraitRef<'tcx>>, Span)>, + user_written_clauses: impl IntoIterator, Span)>, ) -> ErrorGuaranteed { let tcx = self.tcx(); - let trait_alias_span = trait_bounds - .iter() - .map(|&(trait_ref, _)| trait_ref.def_id()) - .find(|&trait_ref| tcx.is_trait_alias(trait_ref)) - .map(|trait_ref| tcx.def_span(trait_ref)); + let trait_alias_span = user_written_clauses + .into_iter() + .filter_map(|(clause, _)| clause.as_trait_clause()) + .find(|trait_ref| tcx.is_trait_alias(trait_ref.def_id())) + .map(|trait_ref| tcx.def_span(trait_ref.def_id())); self.dcx().emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span }) } @@ -1408,7 +1398,7 @@ pub enum GenericsArgsErrExtend<'tcx> { }, SelfTyParam(Span), Param(DefId), - DefVariant, + DefVariant(&'tcx [hir::PathSegment<'tcx>]), None, } @@ -1416,7 +1406,7 @@ fn generics_args_err_extend<'a>( tcx: TyCtxt<'_>, segments: impl Iterator> + Clone, err: &mut Diag<'_>, - err_extend: GenericsArgsErrExtend<'_>, + err_extend: GenericsArgsErrExtend<'a>, ) { match err_extend { GenericsArgsErrExtend::EnumVariant { qself, assoc_segment, adt_def } => { @@ -1504,6 +1494,32 @@ fn generics_args_err_extend<'a>( ]; err.multipart_suggestion_verbose(msg, suggestion, Applicability::MaybeIncorrect); } + GenericsArgsErrExtend::DefVariant(segments) => { + let args: Vec = segments + .iter() + .filter_map(|segment| match segment.res { + Res::Def( + DefKind::Ctor(CtorOf::Variant, _) | DefKind::Variant | DefKind::Enum, + _, + ) => segment.args().span_ext().map(|s| s.with_lo(segment.ident.span.hi())), + _ => None, + }) + .collect(); + if args.len() > 1 + && let Some(span) = args.into_iter().last() + { + err.note( + "generic arguments are not allowed on both an enum and its variant's path \ + segments simultaneously; they are only valid in one place or the other", + ); + err.span_suggestion_verbose( + span, + "remove the generics arguments from one of the path segments", + String::new(), + Applicability::MaybeIncorrect, + ); + } + } GenericsArgsErrExtend::PrimTy(prim_ty) => { let name = prim_ty.name_str(); for segment in segments { @@ -1520,9 +1536,6 @@ fn generics_args_err_extend<'a>( GenericsArgsErrExtend::OpaqueTy => { err.note("`impl Trait` types can't have type parameters"); } - GenericsArgsErrExtend::DefVariant => { - err.note("enum variants can't have type parameters"); - } GenericsArgsErrExtend::Param(def_id) => { let span = tcx.def_ident_span(def_id).unwrap(); let kind = tcx.def_descr(def_id); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index df00948dd211c..4313739787011 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -1,10 +1,9 @@ use rustc_ast::ast::ParamKindOrd; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, struct_span_code_err}; -use rustc_hir as hir; -use rustc_hir::GenericArg; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, GenericArg}; use rustc_middle::ty::{ self, GenericArgsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty, }; @@ -41,17 +40,6 @@ fn generic_arg_mismatch_err( param.kind.descr(), ); - if let GenericParamDefKind::Const { .. } = param.kind { - if matches!(arg, GenericArg::Type(hir::Ty { kind: hir::TyKind::Infer, .. })) { - err.help("const arguments cannot yet be inferred with `_`"); - tcx.disabled_nightly_features( - &mut err, - param.def_id.as_local().map(|local| tcx.local_def_id_to_hir_id(local)), - [(String::new(), sym::generic_arg_infer)], - ); - } - } - let add_braces_suggestion = |arg: &GenericArg<'_>, err: &mut Diag<'_>| { let suggestions = vec![ (arg.span().shrink_to_lo(), String::from("{ ")), @@ -270,7 +258,22 @@ pub fn lower_generic_args<'tcx: 'a, 'a>( GenericParamDefKind::Const { .. }, _, ) => { - args.push(ctx.provided_kind(&args, param, arg)); + if let GenericParamDefKind::Const { .. } = param.kind + && let GenericArg::Infer(inf) = arg + && !tcx.features().generic_arg_infer() + { + rustc_session::parse::feature_err( + tcx.sess, + sym::generic_arg_infer, + inf.span, + "const arguments cannot yet be inferred with `_`", + ) + .emit(); + } + + // We lower to an infer even when the feature gate is not enabled + // as it is useful for diagnostics to be able to see a `ConstKind::Infer` + args.push(ctx.provided_kind(param, arg)); args_iter.next(); params.next(); } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs index fd49e7e44398b..44f7a035a10fc 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs @@ -2,10 +2,12 @@ use rustc_ast::TraitObjectSyntax; use rustc_errors::codes::*; use rustc_errors::{Diag, EmissionGuarantee, ErrorGuaranteed, StashKey, Suggestions}; use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::{DefKind, Namespace, Res}; +use rustc_hir::def_id::DefId; use rustc_lint_defs::Applicability; use rustc_lint_defs::builtin::BARE_TRAIT_OBJECTS; use rustc_span::Span; +use rustc_span::edit_distance::find_best_match_for_name; use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName; use super::HirTyLowerer; @@ -21,9 +23,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) -> Option { let tcx = self.tcx(); - let hir::TyKind::TraitObject([poly_trait_ref, ..], _, TraitObjectSyntax::None) = + let poly_trait_ref = if let hir::TyKind::TraitObject([poly_trait_ref, ..], tagged_ptr) = self_ty.kind - else { + && let TraitObjectSyntax::None = tagged_ptr.tag() + { + poly_trait_ref + } else { return None; }; @@ -36,8 +41,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { kind: hir::ExprKind::Path(hir::QPath::TypeRelative(qself, _)), .. }) - | hir::Node::Pat(hir::Pat { - kind: hir::PatKind::Path(hir::QPath::TypeRelative(qself, _)), + | hir::Node::PatExpr(hir::PatExpr { + kind: hir::PatExprKind::Path(hir::QPath::TypeRelative(qself, _)), .. }) if qself.hir_id == self_ty.hir_id => true, _ => false, @@ -86,7 +91,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // Check if the impl trait that we are considering is an impl of a local trait. self.maybe_suggest_blanket_trait_impl(self_ty, &mut diag); self.maybe_suggest_assoc_ty_bound(self_ty, &mut diag); - // In case there is an associate type with the same name + self.maybe_suggest_typoed_method( + self_ty, + poly_trait_ref.trait_ref.trait_def_id(), + &mut diag, + ); + // In case there is an associated type with the same name // Add the suggestion to this error if let Some(mut sugg) = tcx.dcx().steal_non_err(self_ty.span, StashKey::AssociatedTypeSuggestion) @@ -96,7 +106,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { s1.append(s2); sugg.cancel(); } - diag.stash(self_ty.span, StashKey::TraitMissingMethod) + Some(diag.emit()) } else { tcx.node_span_lint(BARE_TRAIT_OBJECTS, self_ty.hir_id, self_ty.span, |lint| { lint.primary_message("trait objects without an explicit `dyn` are deprecated"); @@ -189,9 +199,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // 2. Functions inside trait blocks // 3. Functions inside impl blocks let (sig, generics) = match tcx.hir_node_by_def_id(parent_id) { - hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, generics, _), .. }) => { - (sig, generics) - } + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn { sig, generics, .. }, .. + }) => (sig, generics), hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(sig, _), generics, @@ -287,7 +297,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let (dyn_str, paren_dyn_str) = if borrowed { ("dyn ", "(dyn ") } else { ("&dyn ", "&(dyn ") }; - let sugg = if let hir::TyKind::TraitObject([_, _, ..], _, _) = self_ty.kind { + let sugg = if let hir::TyKind::TraitObject([_, _, ..], _) = self_ty.kind { // There are more than one trait bound, we need surrounding parentheses. vec![ (self_ty.span.shrink_to_lo(), paren_dyn_str.to_string()), @@ -343,4 +353,44 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); } } + + fn maybe_suggest_typoed_method( + &self, + self_ty: &hir::Ty<'_>, + trait_def_id: Option, + diag: &mut Diag<'_>, + ) { + let tcx = self.tcx(); + let Some(trait_def_id) = trait_def_id else { + return; + }; + let hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Path(hir::QPath::TypeRelative(path_ty, segment)), + .. + }) = tcx.parent_hir_node(self_ty.hir_id) + else { + return; + }; + if path_ty.hir_id != self_ty.hir_id { + return; + } + let names: Vec<_> = tcx + .associated_items(trait_def_id) + .in_definition_order() + .filter(|assoc| assoc.kind.namespace() == Namespace::ValueNS) + .map(|cand| cand.name) + .collect(); + if let Some(typo) = find_best_match_for_name(&names, segment.ident.name, None) { + diag.span_suggestion_verbose( + segment.ident.span, + format!( + "you may have misspelled this associated item, causing `{}` \ + to be interpreted as a type rather than a trait", + tcx.item_name(trait_def_id), + ), + typo, + Applicability::MaybeIncorrect, + ); + } + } } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 2154568c51267..88323db6dda65 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -35,7 +35,7 @@ use rustc_hir::{self as hir, AnonConst, GenericArg, GenericArgs, HirId}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; use rustc_middle::middle::stability::AllowUnstable; -use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; +use rustc_middle::mir::interpret::LitToConstInput; use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::print::PrintPolyTraitRefExt as _; use rustc_middle::ty::{ @@ -53,7 +53,7 @@ use tracing::{debug, instrument}; use crate::bounds::Bounds; use crate::check::check_abi_fn_ptr; -use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, WildPatTy}; +use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, InvalidBaseType}; use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint}; use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args}; use crate::middle::resolve_bound_vars as rbv; @@ -296,7 +296,6 @@ pub trait GenericArgsLowerer<'a, 'tcx> { fn provided_kind( &mut self, - preceding_args: &[ty::GenericArg<'tcx>], param: &ty::GenericParamDef, arg: &GenericArg<'tcx>, ) -> ty::GenericArg<'tcx>; @@ -480,7 +479,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { fn provided_kind( &mut self, - _preceding_args: &[ty::GenericArg<'tcx>], param: &ty::GenericParamDef, arg: &GenericArg<'tcx>, ) -> ty::GenericArg<'tcx> { @@ -517,14 +515,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.lowerer.lower_lifetime(lt, RegionInferReason::Param(param)).into() } (&GenericParamDefKind::Type { has_default, .. }, GenericArg::Type(ty)) => { - handle_ty_args(has_default, ty) + // We handle the other parts of `Ty` in the match arm below + handle_ty_args(has_default, ty.as_unambig_ty()) } (&GenericParamDefKind::Type { has_default, .. }, GenericArg::Infer(inf)) => { handle_ty_args(has_default, &inf.to_ty()) } - (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { - self.lowerer.lower_const_arg(ct, FeedConstTy::Param(param.def_id)).into() - } + (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => self + .lowerer + // Ambig portions of `ConstArg` are handled in the match arm below + .lower_const_arg(ct.as_unambig_ct(), FeedConstTy::Param(param.def_id)) + .into(), (&GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { self.lowerer.ct_infer(Some(param), inf.span).into() } @@ -1693,7 +1694,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { pub fn prohibit_generic_args<'a>( &self, segments: impl Iterator> + Clone, - err_extend: GenericsArgsErrExtend<'_>, + err_extend: GenericsArgsErrExtend<'a>, ) -> Result<(), ErrorGuaranteed> { let args_visitors = segments.clone().flat_map(|segment| segment.args().args); let mut result = Ok(()); @@ -1910,7 +1911,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { path.segments.iter().enumerate().filter_map(|(index, seg)| { if !indices.contains(&index) { Some(seg) } else { None } }), - GenericsArgsErrExtend::DefVariant, + GenericsArgsErrExtend::DefVariant(&path.segments), ); let GenericPathSegment(def_id, index) = generic_segments.last().unwrap(); @@ -2115,7 +2116,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { format!("Const::lower_const_arg: invalid qpath {qpath:?}"), ), hir::ConstArgKind::Anon(anon) => self.lower_anon_const(anon), - hir::ConstArgKind::Infer(span) => self.ct_infer(None, span), + hir::ConstArgKind::Infer(span, ()) => self.ct_infer(None, span), } } @@ -2153,11 +2154,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span_bug!(span, "use of bare `static` ConstArgKind::Path's not yet supported") } // FIXME(const_generics): create real const to allow fn items as const paths - Res::Def(DefKind::Fn | DefKind::AssocFn, _) => ty::Const::new_error_with_message( - tcx, - span, - "fn items cannot be used as const args", - ), + Res::Def(DefKind::Fn | DefKind::AssocFn, did) => { + self.dcx().span_delayed_bug(span, "function items cannot be used as const args"); + let args = self.lower_generic_args_of_path_segment( + span, + did, + path.segments.last().unwrap(), + ); + ty::Const::zero_sized(tcx, Ty::new_fn_def(tcx, did, args)) + } // Exhaustive match to be clear about what exactly we're considering to be // an invalid Res for a const path. @@ -2218,10 +2223,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { match self.try_lower_anon_const_lit(ty, expr) { Some(v) => v, - None => ty::Const::new_unevaluated(tcx, ty::UnevaluatedConst { - def: anon.def_id.to_def_id(), - args: ty::GenericArgs::identity_for_item(tcx, anon.def_id.to_def_id()), - }), + None => ty::Const::new_unevaluated( + tcx, + ty::UnevaluatedConst { + def: anon.def_id.to_def_id(), + args: ty::GenericArgs::identity_for_item(tcx, anon.def_id.to_def_id()), + }, + ), } } @@ -2262,25 +2270,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { _ => None, }; - if let Some(lit_input) = lit_input { - // If an error occurred, ignore that it's a literal and leave reporting the error up to - // mir. - match tcx.at(expr.span).lit_to_const(lit_input) { - Ok(c) => return Some(c), - Err(_) if lit_input.ty.has_aliases() => { - // allow the `ty` to be an alias type, though we cannot handle it here - return None; - } - Err(e) => { - tcx.dcx().span_delayed_bug( - expr.span, - format!("try_lower_anon_const_lit: couldn't lit_to_const {e:?}"), - ); - } - } - } - - None + lit_input + // Allow the `ty` to be an alias type, though we cannot handle it here, we just go through + // the more expensive anon const code path. + .filter(|l| !l.ty.has_aliases()) + .map(|l| tcx.at(expr.span).lit_to_const(l)) } fn lower_delegation_ty(&self, idx: hir::InferDelegationKind) -> Ty<'tcx> { @@ -2325,7 +2319,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { tcx.late_bound_vars(hir_ty.hir_id), ), ), - hir::TyKind::TraitObject(bounds, lifetime, repr) => { + hir::TyKind::TraitObject(bounds, tagged_ptr) => { + let lifetime = tagged_ptr.pointer(); + let repr = tagged_ptr.tag(); + if let Some(guar) = self.prohibit_or_lint_bare_trait_object_ty(hir_ty) { // Don't continue with type analysis if the `dyn` keyword is missing // It generates confusing errors, especially if the user meant to use another @@ -2434,7 +2431,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Ty::new_array_with_const_len(tcx, self.lower_ty(ty), length) } hir::TyKind::Typeof(e) => tcx.type_of(e.def_id).instantiate_identity(), - hir::TyKind::Infer => { + hir::TyKind::Infer(()) => { // Infer also appears as the type of arguments or return // values in an ExprKind::Closure, or as // the type of local variables. Both of these cases are @@ -2442,69 +2439,24 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.ty_infer(None, hir_ty.span) } hir::TyKind::Pat(ty, pat) => { + let ty_span = ty.span; let ty = self.lower_ty(ty); let pat_ty = match pat.kind { - hir::PatKind::Wild => { - let err = self.dcx().emit_err(WildPatTy { span: pat.span }); - Ty::new_error(tcx, err) - } - hir::PatKind::Range(start, end, include_end) => { - let expr_to_const = |expr: &'tcx hir::Expr<'tcx>| -> ty::Const<'tcx> { - let (expr, neg) = match expr.kind { - hir::ExprKind::Unary(hir::UnOp::Neg, negated) => { - (negated, Some((expr.hir_id, expr.span))) - } - _ => (expr, None), - }; - let (c, c_ty) = match &expr.kind { - hir::ExprKind::Lit(lit) => { - let lit_input = - LitToConstInput { lit: &lit.node, ty, neg: neg.is_some() }; - let ct = match tcx.lit_to_const(lit_input) { - Ok(c) => c, - Err(LitToConstError::Reported(err)) => { - ty::Const::new_error(tcx, err) - } - Err(LitToConstError::TypeError) => todo!(), - }; - (ct, ty) - } - - hir::ExprKind::Path(hir::QPath::Resolved( - _, - path @ &hir::Path { - res: Res::Def(DefKind::ConstParam, def_id), - .. - }, - )) => { - let _ = self.prohibit_generic_args( - path.segments.iter(), - GenericsArgsErrExtend::Param(def_id), - ); - let ty = tcx - .type_of(def_id) - .no_bound_vars() - .expect("const parameter types cannot be generic"); - let ct = self.lower_const_param(def_id, expr.hir_id); - (ct, ty) - } - - _ => { - let err = tcx - .dcx() - .emit_err(crate::errors::NonConstRange { span: expr.span }); - (ty::Const::new_error(tcx, err), Ty::new_error(tcx, err)) - } - }; - self.record_ty(expr.hir_id, c_ty, expr.span); - if let Some((id, span)) = neg { - self.record_ty(id, c_ty, span); - } - c + hir::TyPatKind::Range(start, end, include_end) => { + let ty = match ty.kind() { + ty::Int(_) | ty::Uint(_) | ty::Char => ty, + _ => Ty::new_error( + tcx, + self.dcx().emit_err(InvalidBaseType { + ty, + pat: "range", + ty_span, + pat_span: pat.span, + }), + ), }; - - let start = start.map(expr_to_const); - let end = end.map(expr_to_const); + let start = start.map(|expr| self.lower_const_arg(expr, FeedConstTy::No)); + let end = end.map(|expr| self.lower_const_arg(expr, FeedConstTy::No)); let include_end = match include_end { hir::RangeEnd::Included => true, @@ -2514,12 +2466,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let pat = tcx.mk_pat(ty::PatternKind::Range { start, end, include_end }); Ty::new_pat(tcx, ty, pat) } - hir::PatKind::Err(e) => Ty::new_error(tcx, e), - _ => Ty::new_error_with_message( - tcx, - pat.span, - format!("unsupported pattern for pattern type: {pat:#?}"), - ), + hir::TyPatKind::Err(e) => Ty::new_error(tcx, e), }; self.record_ty(pat.hir_id, ty, pat.span); pat_ty @@ -2575,7 +2522,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { pub fn lower_arg_ty(&self, ty: &hir::Ty<'tcx>, expected_ty: Option>) -> Ty<'tcx> { match ty.kind { - hir::TyKind::Infer if let Some(expected_ty) = expected_ty => { + hir::TyKind::Infer(()) if let Some(expected_ty) = expected_ty => { self.record_ty(ty.hir_id, expected_ty, ty.span); expected_ty } @@ -2614,27 +2561,29 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // reject function types that violate cmse ABI requirements cmse::validate_cmse_abi(self.tcx(), self.dcx(), hir_id, abi, bare_fn_ty); - // Find any late-bound regions declared in return type that do - // not appear in the arguments. These are not well-formed. - // - // Example: - // for<'a> fn() -> &'a str <-- 'a is bad - // for<'a> fn(&'a String) -> &'a str <-- 'a is ok - let inputs = bare_fn_ty.inputs(); - let late_bound_in_args = - tcx.collect_constrained_late_bound_regions(inputs.map_bound(|i| i.to_owned())); - let output = bare_fn_ty.output(); - let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(output); - - self.validate_late_bound_regions(late_bound_in_args, late_bound_in_ret, |br_name| { - struct_span_code_err!( - self.dcx(), - decl.output.span(), - E0581, - "return type references {}, which is not constrained by the fn input types", - br_name - ) - }); + if !bare_fn_ty.references_error() { + // Find any late-bound regions declared in return type that do + // not appear in the arguments. These are not well-formed. + // + // Example: + // for<'a> fn() -> &'a str <-- 'a is bad + // for<'a> fn(&'a String) -> &'a str <-- 'a is ok + let inputs = bare_fn_ty.inputs(); + let late_bound_in_args = + tcx.collect_constrained_late_bound_regions(inputs.map_bound(|i| i.to_owned())); + let output = bare_fn_ty.output(); + let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(output); + + self.validate_late_bound_regions(late_bound_in_args, late_bound_in_ret, |br_name| { + struct_span_code_err!( + self.dcx(), + decl.output.span(), + E0581, + "return type references {}, which is not constrained by the fn input types", + br_name + ) + }); + } bare_fn_ty } diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index c0fb94d2cb294..f5abcd234401b 100644 --- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -1,6 +1,5 @@ -use rustc_hir as hir; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{ForeignItem, ForeignItemKind}; +use rustc_hir::intravisit::{self, Visitor, VisitorExt}; +use rustc_hir::{self as hir, AmbigArg, ForeignItem, ForeignItemKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{ObligationCause, WellFormedLoc}; use rustc_middle::bug; @@ -68,11 +67,13 @@ fn diagnostic_hir_wf_check<'tcx>( } impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> { - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { let infcx = self.tcx.infer_ctxt().build(TypingMode::non_body_analysis()); let ocx = ObligationCtxt::new_with_diagnostics(&infcx); - let tcx_ty = self.icx.lower_ty(ty); + // We don't handle infer vars but we wouldn't handle them anyway as we're creating a + // fresh `InferCtxt` in this function. + let tcx_ty = self.icx.lower_ty(ty.as_unambig_ty()); // This visitor can walk into binders, resulting in the `tcx_ty` to // potentially reference escaping bound variables. We simply erase // those here. @@ -149,7 +150,11 @@ fn diagnostic_hir_wf_check<'tcx>( .iter() .flat_map(|seg| seg.args().args) .filter_map(|arg| { - if let hir::GenericArg::Type(ty) = arg { Some(*ty) } else { None } + if let hir::GenericArg::Type(ty) = arg { + Some(ty.as_unambig_ty()) + } else { + None + } }) .chain([impl_.self_ty]) .collect(), @@ -196,7 +201,7 @@ fn diagnostic_hir_wf_check<'tcx>( } }; for ty in tys { - visitor.visit_ty(ty); + visitor.visit_ty_unambig(ty); } visitor.cause } diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs index d9c70c3cee655..c30b39dfe7656 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs @@ -57,22 +57,24 @@ pub(crate) fn check_impl_wf( tcx: TyCtxt<'_>, impl_def_id: LocalDefId, ) -> Result<(), ErrorGuaranteed> { - let min_specialization = tcx.features().min_specialization(); - let mut res = Ok(()); debug_assert_matches!(tcx.def_kind(impl_def_id), DefKind::Impl { .. }); - res = res.and(enforce_impl_params_are_constrained(tcx, impl_def_id)); - if min_specialization { + + // Check that the args are constrained. We queryfied the check for ty/const params + // since unconstrained type/const params cause ICEs in projection, so we want to + // detect those specifically and project those to `TyKind::Error`. + let mut res = tcx.ensure_ok().enforce_impl_non_lifetime_params_are_constrained(impl_def_id); + res = res.and(enforce_impl_lifetime_params_are_constrained(tcx, impl_def_id)); + + if tcx.features().min_specialization() { res = res.and(check_min_specialization(tcx, impl_def_id)); } - res } -fn enforce_impl_params_are_constrained( +pub(crate) fn enforce_impl_lifetime_params_are_constrained( tcx: TyCtxt<'_>, impl_def_id: LocalDefId, ) -> Result<(), ErrorGuaranteed> { - // Every lifetime used in an associated type must be constrained. let impl_self_ty = tcx.type_of(impl_def_id).instantiate_identity(); if impl_self_ty.references_error() { // Don't complain about unconstrained type params when self ty isn't known due to errors. @@ -88,6 +90,7 @@ fn enforce_impl_params_are_constrained( // Compilation must continue in order for other important diagnostics to keep showing up. return Ok(()); } + let impl_generics = tcx.generics_of(impl_def_id); let impl_predicates = tcx.predicates_of(impl_def_id); let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).map(ty::EarlyBinder::instantiate_identity); @@ -121,6 +124,84 @@ fn enforce_impl_params_are_constrained( }) .collect(); + let mut res = Ok(()); + for param in &impl_generics.own_params { + match param.kind { + ty::GenericParamDefKind::Lifetime => { + // This is a horrible concession to reality. I think it'd be + // better to just ban unconstrained lifetimes outright, but in + // practice people do non-hygienic macros like: + // + // ``` + // macro_rules! __impl_slice_eq1 { + // ($Lhs: ty, $Rhs: ty, $Bound: ident) => { + // impl<'a, 'b, A: $Bound, B> PartialEq<$Rhs> for $Lhs where A: PartialEq { + // .... + // } + // } + // } + // ``` + // + // In a concession to backwards compatibility, we continue to + // permit those, so long as the lifetimes aren't used in + // associated types. I believe this is sound, because lifetimes + // used elsewhere are not projected back out. + let param_lt = cgp::Parameter::from(param.to_early_bound_region_data()); + if lifetimes_in_associated_types.contains(¶m_lt) + && !input_parameters.contains(¶m_lt) + { + let mut diag = tcx.dcx().create_err(UnconstrainedGenericParameter { + span: tcx.def_span(param.def_id), + param_name: tcx.item_ident(param.def_id), + param_def_kind: tcx.def_descr(param.def_id), + const_param_note: false, + const_param_note2: false, + }); + diag.code(E0207); + res = Err(diag.emit()); + } + } + ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Const { .. } => { + // Enforced in `enforce_impl_non_lifetime_params_are_constrained`. + } + } + } + res +} + +pub(crate) fn enforce_impl_non_lifetime_params_are_constrained( + tcx: TyCtxt<'_>, + impl_def_id: LocalDefId, +) -> Result<(), ErrorGuaranteed> { + let impl_self_ty = tcx.type_of(impl_def_id).instantiate_identity(); + if impl_self_ty.references_error() { + // Don't complain about unconstrained type params when self ty isn't known due to errors. + // (#36836) + tcx.dcx().span_delayed_bug( + tcx.def_span(impl_def_id), + format!( + "potentially unconstrained type parameters weren't evaluated: {impl_self_ty:?}", + ), + ); + // This is super fishy, but our current `rustc_hir_analysis::check_crate` pipeline depends on + // `type_of` having been called much earlier, and thus this value being read from cache. + // Compilation must continue in order for other important diagnostics to keep showing up. + return Ok(()); + } + let impl_generics = tcx.generics_of(impl_def_id); + let impl_predicates = tcx.predicates_of(impl_def_id); + let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).map(ty::EarlyBinder::instantiate_identity); + + impl_trait_ref.error_reported()?; + + let mut input_parameters = cgp::parameters_for_impl(tcx, impl_self_ty, impl_trait_ref); + cgp::identify_constrained_generic_params( + tcx, + impl_predicates, + impl_trait_ref, + &mut input_parameters, + ); + let mut res = Ok(()); for param in &impl_generics.own_params { let err = match param.kind { @@ -129,21 +210,20 @@ fn enforce_impl_params_are_constrained( let param_ty = ty::ParamTy::for_def(param); !input_parameters.contains(&cgp::Parameter::from(param_ty)) } - ty::GenericParamDefKind::Lifetime => { - let param_lt = cgp::Parameter::from(param.to_early_bound_region_data()); - lifetimes_in_associated_types.contains(¶m_lt) && // (*) - !input_parameters.contains(¶m_lt) - } ty::GenericParamDefKind::Const { .. } => { let param_ct = ty::ParamConst::for_def(param); !input_parameters.contains(&cgp::Parameter::from(param_ct)) } + ty::GenericParamDefKind::Lifetime => { + // Enforced in `enforce_impl_type_params_are_constrained`. + false + } }; if err { let const_param_note = matches!(param.kind, ty::GenericParamDefKind::Const { .. }); let mut diag = tcx.dcx().create_err(UnconstrainedGenericParameter { span: tcx.def_span(param.def_id), - param_name: param.name, + param_name: tcx.item_ident(param.def_id), param_def_kind: tcx.def_descr(param.def_id), const_param_note, const_param_note2: const_param_note, @@ -153,23 +233,4 @@ fn enforce_impl_params_are_constrained( } } res - - // (*) This is a horrible concession to reality. I think it'd be - // better to just ban unconstrained lifetimes outright, but in - // practice people do non-hygienic macros like: - // - // ``` - // macro_rules! __impl_slice_eq1 { - // ($Lhs: ty, $Rhs: ty, $Bound: ident) => { - // impl<'a, 'b, A: $Bound, B> PartialEq<$Rhs> for $Lhs where A: PartialEq { - // .... - // } - // } - // } - // ``` - // - // In a concession to backwards compatibility, we continue to - // permit those, so long as the lifetimes aren't used in - // associated types. I believe this is sound, because lifetimes - // used elsewhere are not projected back out. } diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index ee55e1bc21a60..af1107b499f4e 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -34,7 +34,7 @@ //! impl> SpecExtend for I { /* default impl */ } //! ``` //! -//! We get that the generic pamameters for `impl2` are `[T, std::vec::IntoIter]`. +//! We get that the generic parameters for `impl2` are `[T, std::vec::IntoIter]`. //! `T` is constrained to be `::Item`, so we check only //! `std::vec::IntoIter` for repeated parameters, which it doesn't have. The //! predicates of `impl1` are only `T: Sized`, which is also a predicate of @@ -68,7 +68,6 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::TyCtxtInferExt; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::traits::ObligationCause; use rustc_infer::traits::specialization_graph::Node; use rustc_middle::ty::trait_def::TraitSpecializationKind; @@ -77,7 +76,6 @@ use rustc_middle::ty::{ }; use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; -use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::{self, ObligationCtxt, translate_args_with_cause, wf}; use tracing::{debug, instrument}; @@ -121,7 +119,6 @@ fn check_always_applicable( impl2_node: Node, ) -> Result<(), ErrorGuaranteed> { let span = tcx.def_span(impl1_def_id); - let mut res = check_has_items(tcx, impl1_def_id, impl2_node, span); let (impl1_args, impl2_args) = get_impl_args(tcx, impl1_def_id, impl2_node)?; let impl2_def_id = impl2_node.def_id(); @@ -133,11 +130,10 @@ fn check_always_applicable( unconstrained_parent_impl_args(tcx, impl2_def_id, impl2_args) }; - res = res.and(check_static_lifetimes(tcx, &parent_args, span)); - res = res.and(check_duplicate_params(tcx, impl1_args, parent_args, span)); - res = res.and(check_predicates(tcx, impl1_def_id, impl1_args, impl2_node, impl2_args, span)); - - res + check_has_items(tcx, impl1_def_id, impl2_node, span) + .and(check_static_lifetimes(tcx, &parent_args, span)) + .and(check_duplicate_params(tcx, impl1_args, parent_args, span)) + .and(check_predicates(tcx, impl1_def_id, impl1_args, impl2_node, impl2_args, span)) } fn check_has_items( @@ -176,7 +172,6 @@ fn get_impl_args( let ocx = ObligationCtxt::new_with_diagnostics(infcx); let param_env = tcx.param_env(impl1_def_id); let impl1_span = tcx.def_span(impl1_def_id); - let assumed_wf_types = ocx.assumed_wf_types_and_report_errors(param_env, impl1_def_id)?; let impl1_args = GenericArgs::identity_for_item(tcx, impl1_def_id); let impl2_args = translate_args_with_cause( @@ -194,9 +189,8 @@ fn get_impl_args( return Err(guar); } - let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_def_id, &assumed_wf_types); - let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); - let _ = ocx.resolve_regions_and_report_errors(impl1_def_id, &outlives_env); + let assumed_wf_types = ocx.assumed_wf_types_and_report_errors(param_env, impl1_def_id)?; + let _ = ocx.resolve_regions_and_report_errors(impl1_def_id, param_env, assumed_wf_types); let Ok(impl2_args) = infcx.fully_resolve(impl2_args) else { let span = tcx.def_span(impl1_def_id); let guar = tcx.dcx().emit_err(GenericArgsOnOverriddenImpl { span }); diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 87fd4de26a5af..8a529e9c686e0 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -63,6 +63,7 @@ This API is completely unstable and subject to change. #![doc(rust_logo)] #![feature(assert_matches)] #![feature(coroutines)] +#![feature(debug_closure_helpers)] #![feature(if_let_guard)] #![feature(iter_from_coroutine)] #![feature(iter_intersperse)] @@ -82,12 +83,11 @@ pub mod autoderef; mod bounds; mod check_unused; mod coherence; -mod delegation; -pub mod hir_ty_lowering; -// FIXME: This module shouldn't be public. -pub mod collect; +mod collect; mod constrained_generic_params; +mod delegation; mod errors; +pub mod hir_ty_lowering; pub mod hir_wf_check; mod impl_wf_check; mod outlives; @@ -100,10 +100,13 @@ use rustc_middle::middle; use rustc_middle::mir::interpret::GlobalId; use rustc_middle::query::Providers; use rustc_middle::ty::{self, Const, Ty, TyCtxt}; -use rustc_span::Span; +use rustc_session::parse::feature_err; +use rustc_span::symbol::sym; +use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::traits; -use self::hir_ty_lowering::{FeedConstTy, HirTyLowerer}; +pub use crate::collect::suggest_impl_trait; +use crate::hir_ty_lowering::{FeedConstTy, HirTyLowerer}; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } @@ -113,9 +116,48 @@ fn require_c_abi_if_c_variadic( abi: ExternAbi, span: Span, ) { - if decl.c_variadic && !abi.supports_varargs() { - tcx.dcx().emit_err(errors::VariadicFunctionCompatibleConvention { span }); + const CONVENTIONS_UNSTABLE: &str = + "`C`, `cdecl`, `system`, `aapcs`, `win64`, `sysv64` or `efiapi`"; + const CONVENTIONS_STABLE: &str = "`C` or `cdecl`"; + const UNSTABLE_EXPLAIN: &str = + "using calling conventions other than `C` or `cdecl` for varargs functions is unstable"; + + // ABIs which can stably use varargs + if !decl.c_variadic || matches!(abi, ExternAbi::C { .. } | ExternAbi::Cdecl { .. }) { + return; + } + + // ABIs with feature-gated stability + let extended_abi_support = tcx.features().extended_varargs_abi_support(); + let extern_system_varargs = tcx.features().extern_system_varargs(); + + // If the feature gate has been enabled, we can stop here + if extern_system_varargs && let ExternAbi::System { .. } = abi { + return; + }; + if extended_abi_support && abi.supports_varargs() { + return; + }; + + // Looks like we need to pick an error to emit. + // Is there any feature which we could have enabled to make this work? + match abi { + ExternAbi::System { .. } => { + feature_err(&tcx.sess, sym::extern_system_varargs, span, UNSTABLE_EXPLAIN) + } + abi if abi.supports_varargs() => { + feature_err(&tcx.sess, sym::extended_varargs_abi_support, span, UNSTABLE_EXPLAIN) + } + _ => tcx.dcx().create_err(errors::VariadicFunctionCompatibleConvention { + span, + conventions: if tcx.sess.opts.unstable_features.is_nightly_build() { + CONVENTIONS_UNSTABLE + } else { + CONVENTIONS_STABLE + }, + }), } + .emit(); } pub fn provide(providers: &mut Providers) { @@ -128,6 +170,8 @@ pub fn provide(providers: &mut Providers) { hir_wf_check::provide(providers); *providers = Providers { inherit_sig_for_delegation_item: delegation::inherit_sig_for_delegation_item, + enforce_impl_non_lifetime_params_are_constrained: + impl_wf_check::enforce_impl_non_lifetime_params_are_constrained, ..*providers }; } @@ -136,24 +180,31 @@ pub fn check_crate(tcx: TyCtxt<'_>) { let _prof_timer = tcx.sess.timer("type_check_crate"); tcx.sess.time("coherence_checking", || { + // When discarding query call results, use an explicit type to indicate + // what we are intending to discard, to help future type-based refactoring. + type R = Result<(), ErrorGuaranteed>; + tcx.hir().par_for_each_module(|module| { - let _ = tcx.ensure().check_mod_type_wf(module); + let _: R = tcx.ensure_ok().check_mod_type_wf(module); }); for &trait_def_id in tcx.all_local_trait_impls(()).keys() { - let _ = tcx.ensure().coherent_trait(trait_def_id); + let _: R = tcx.ensure_ok().coherent_trait(trait_def_id); } // these queries are executed for side-effects (error reporting): - let _ = tcx.ensure().crate_inherent_impls_validity_check(()); - let _ = tcx.ensure().crate_inherent_impls_overlap_check(()); + let _: R = tcx.ensure_ok().crate_inherent_impls_validity_check(()); + let _: R = tcx.ensure_ok().crate_inherent_impls_overlap_check(()); }); if tcx.features().rustc_attrs() { - tcx.sess.time("outlives_dumping", || outlives::dump::inferred_outlives(tcx)); - tcx.sess.time("variance_dumping", || variance::dump::variances(tcx)); - collect::dump::opaque_hidden_types(tcx); - collect::dump::predicates_and_item_bounds(tcx); - collect::dump::def_parents(tcx); + tcx.sess.time("dumping_rustc_attr_data", || { + outlives::dump::inferred_outlives(tcx); + variance::dump::variances(tcx); + collect::dump::opaque_hidden_types(tcx); + collect::dump::predicates_and_item_bounds(tcx); + collect::dump::def_parents(tcx); + collect::dump::vtables(tcx); + }); } // Make sure we evaluate all static and (non-associated) const items, even if unused. @@ -161,12 +212,12 @@ pub fn check_crate(tcx: TyCtxt<'_>) { tcx.hir().par_body_owners(|item_def_id| { let def_kind = tcx.def_kind(item_def_id); match def_kind { - DefKind::Static { .. } => tcx.ensure().eval_static_initializer(item_def_id), + DefKind::Static { .. } => tcx.ensure_ok().eval_static_initializer(item_def_id), DefKind::Const if tcx.generics_of(item_def_id).is_empty() => { let instance = ty::Instance::new(item_def_id.into(), ty::GenericArgs::empty()); let cid = GlobalId { instance, promoted: None }; let typing_env = ty::TypingEnv::fully_monomorphized(); - tcx.ensure().eval_to_const_value_raw(typing_env.as_query_input(cid)); + tcx.ensure_ok().eval_to_const_value_raw(typing_env.as_query_input(cid)); } _ => (), } @@ -179,11 +230,11 @@ pub fn check_crate(tcx: TyCtxt<'_>) { tcx.hir().par_body_owners(|item_def_id| { let def_kind = tcx.def_kind(item_def_id); if !matches!(def_kind, DefKind::AnonConst) { - tcx.ensure().typeck(item_def_id); + tcx.ensure_ok().typeck(item_def_id); } }); - tcx.ensure().check_unused_traits(()); + tcx.ensure_ok().check_unused_traits(()); } /// Lower a [`hir::Ty`] to a [`Ty`]. diff --git a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs index c2377b4781c28..036163b9f1409 100644 --- a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs +++ b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs @@ -24,7 +24,7 @@ pub(super) fn infer_predicates( // If new predicates were added then we need to re-calculate // all crates since there could be new implied predicates. - 'outer: loop { + loop { let mut predicates_added = false; // Visit all the crates and infer predicates @@ -90,7 +90,7 @@ pub(super) fn infer_predicates( } if !predicates_added { - break 'outer; + break; } } diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs index 02cfb57b836f6..a7760326bb4c3 100644 --- a/compiler/rustc_hir_analysis/src/variance/mod.rs +++ b/compiler/rustc_hir_analysis/src/variance/mod.rs @@ -27,9 +27,6 @@ mod solve; pub(crate) mod dump; -/// Code for transforming variances. -mod xform; - pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { variances_of, crate_variances, ..*providers }; } diff --git a/compiler/rustc_hir_analysis/src/variance/solve.rs b/compiler/rustc_hir_analysis/src/variance/solve.rs index d0bdca8677922..4106c1a5b6355 100644 --- a/compiler/rustc_hir_analysis/src/variance/solve.rs +++ b/compiler/rustc_hir_analysis/src/variance/solve.rs @@ -12,8 +12,26 @@ use tracing::debug; use super::constraints::*; use super::terms::VarianceTerm::*; use super::terms::*; -use super::xform::*; +fn glb(v1: ty::Variance, v2: ty::Variance) -> ty::Variance { + // Greatest lower bound of the variance lattice as defined in The Paper: + // + // * + // - + + // o + match (v1, v2) { + (ty::Invariant, _) | (_, ty::Invariant) => ty::Invariant, + + (ty::Covariant, ty::Contravariant) => ty::Invariant, + (ty::Contravariant, ty::Covariant) => ty::Invariant, + + (ty::Covariant, ty::Covariant) => ty::Covariant, + + (ty::Contravariant, ty::Contravariant) => ty::Contravariant, + + (x, ty::Bivariant) | (ty::Bivariant, x) => x, + } +} struct SolveContext<'a, 'tcx> { terms_cx: TermsContext<'a, 'tcx>, constraints: Vec>, diff --git a/compiler/rustc_hir_analysis/src/variance/xform.rs b/compiler/rustc_hir_analysis/src/variance/xform.rs deleted file mode 100644 index 2e9964788e6e5..0000000000000 --- a/compiler/rustc_hir_analysis/src/variance/xform.rs +++ /dev/null @@ -1,22 +0,0 @@ -use rustc_middle::ty; - -pub(crate) fn glb(v1: ty::Variance, v2: ty::Variance) -> ty::Variance { - // Greatest lower bound of the variance lattice as - // defined in The Paper: - // - // * - // - + - // o - match (v1, v2) { - (ty::Invariant, _) | (_, ty::Invariant) => ty::Invariant, - - (ty::Covariant, ty::Contravariant) => ty::Invariant, - (ty::Contravariant, ty::Covariant) => ty::Invariant, - - (ty::Covariant, ty::Covariant) => ty::Covariant, - - (ty::Contravariant, ty::Contravariant) => ty::Contravariant, - - (x, ty::Bivariant) | (ty::Bivariant, x) => x, - } -} diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 5c1c58921907e..57df3127a025a 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -18,7 +18,7 @@ use rustc_ast_pretty::pprust::state::MacHeader; use rustc_ast_pretty::pprust::{Comments, PrintState}; use rustc_hir::{ BindingMode, ByRef, ConstArgKind, GenericArg, GenericBound, GenericParam, GenericParamKind, - HirId, LifetimeParamKind, Node, PatKind, PreciseCapturingArg, RangeEnd, Term, + HirId, LifetimeParamKind, Node, PatKind, PreciseCapturingArg, RangeEnd, Term, TyPatKind, }; use rustc_span::source_map::SourceMap; use rustc_span::{FileName, Ident, Span, Symbol, kw}; @@ -35,6 +35,7 @@ pub enum AnnNode<'a> { SubItem(HirId), Expr(&'a hir::Expr<'a>), Pat(&'a hir::Pat<'a>), + TyPat(&'a hir::TyPat<'a>), Arm(&'a hir::Arm<'a>), } @@ -198,7 +199,9 @@ impl<'a> State<'a> { Node::TraitRef(a) => self.print_trait_ref(a), Node::OpaqueTy(o) => self.print_opaque_ty(o), Node::Pat(a) => self.print_pat(a), + Node::TyPat(a) => self.print_ty_pat(a), Node::PatField(a) => self.print_patfield(a), + Node::PatExpr(a) => self.print_pat_expr(a), Node::Arm(a) => self.print_arm(a), Node::Infer(_) => self.word("_"), Node::PreciseCapturingNonLifetimeArg(param) => self.print_ident(param.ident), @@ -223,6 +226,16 @@ impl<'a> State<'a> { Node::Err(_) => self.word("/*ERROR*/"), } } + + fn print_generic_arg(&mut self, generic_arg: &GenericArg<'_>, elide_lifetimes: bool) { + match generic_arg { + GenericArg::Lifetime(lt) if !elide_lifetimes => self.print_lifetime(lt), + GenericArg::Lifetime(_) => {} + GenericArg::Type(ty) => self.print_type(ty.as_unambig_ty()), + GenericArg::Const(ct) => self.print_const_arg(ct.as_unambig_ct()), + GenericArg::Infer(_inf) => self.word("_"), + } + } } impl std::ops::Deref for State<'_> { @@ -308,6 +321,14 @@ pub fn pat_to_string(ann: &dyn PpAnn, pat: &hir::Pat<'_>) -> String { to_string(ann, |s| s.print_pat(pat)) } +pub fn expr_to_string(ann: &dyn PpAnn, pat: &hir::Expr<'_>) -> String { + to_string(ann, |s| s.print_expr(pat)) +} + +pub fn item_to_string(ann: &dyn PpAnn, pat: &hir::Item<'_>) -> String { + to_string(ann, |s| s.print_item(pat)) +} + impl<'a> State<'a> { fn bclose_maybe_open(&mut self, span: rustc_span::Span, close_box: bool) { self.maybe_print_comment(span.hi()); @@ -401,7 +422,8 @@ impl<'a> State<'a> { self.print_bounds("impl", bounds); } hir::TyKind::Path(ref qpath) => self.print_qpath(qpath, false), - hir::TyKind::TraitObject(bounds, lifetime, syntax) => { + hir::TyKind::TraitObject(bounds, lifetime) => { + let syntax = lifetime.tag(); match syntax { ast::TraitObjectSyntax::Dyn => self.word_nbsp("dyn"), ast::TraitObjectSyntax::DynStar => self.word_nbsp("dyn*"), @@ -420,7 +442,7 @@ impl<'a> State<'a> { if !lifetime.is_elided() { self.nbsp(); self.word_space("+"); - self.print_lifetime(lifetime); + self.print_lifetime(lifetime.pointer()); } } hir::TyKind::Array(ty, ref length) => { @@ -440,13 +462,13 @@ impl<'a> State<'a> { self.word("/*ERROR*/"); self.pclose(); } - hir::TyKind::Infer | hir::TyKind::InferDelegation(..) => { + hir::TyKind::Infer(()) | hir::TyKind::InferDelegation(..) => { self.word("_"); } hir::TyKind::Pat(ty, pat) => { self.print_type(ty); self.word(" is "); - self.print_pat(pat); + self.print_ty_pat(pat); } } self.end() @@ -634,7 +656,7 @@ impl<'a> State<'a> { self.word(";"); self.end(); // end the outer cbox } - hir::ItemKind::Fn(ref sig, generics, body) => { + hir::ItemKind::Fn { sig, generics, body, .. } => { self.head(""); self.print_fn( sig.decl, @@ -1795,13 +1817,7 @@ impl<'a> State<'a> { if nonelided_generic_args { start_or_comma(self); self.commasep(Inconsistent, generic_args.args, |s, generic_arg| { - match generic_arg { - GenericArg::Lifetime(lt) if !elide_lifetimes => s.print_lifetime(lt), - GenericArg::Lifetime(_) => {} - GenericArg::Type(ty) => s.print_type(ty), - GenericArg::Const(ct) => s.print_const_arg(ct), - GenericArg::Infer(_inf) => s.word("_"), - } + s.print_generic_arg(generic_arg, elide_lifetimes) }); } @@ -1849,6 +1865,46 @@ impl<'a> State<'a> { } } + fn print_pat_expr(&mut self, expr: &hir::PatExpr<'_>) { + match &expr.kind { + hir::PatExprKind::Lit { lit, negated } => { + if *negated { + self.word("-"); + } + self.print_literal(lit); + } + hir::PatExprKind::ConstBlock(c) => self.print_inline_const(c), + hir::PatExprKind::Path(qpath) => self.print_qpath(qpath, true), + } + } + + fn print_ty_pat(&mut self, pat: &hir::TyPat<'_>) { + self.maybe_print_comment(pat.span.lo()); + self.ann.pre(self, AnnNode::TyPat(pat)); + // Pat isn't normalized, but the beauty of it + // is that it doesn't matter + match pat.kind { + TyPatKind::Range(begin, end, end_kind) => { + if let Some(expr) = begin { + self.print_const_arg(expr); + } + match end_kind { + RangeEnd::Included => self.word("..."), + RangeEnd::Excluded => self.word(".."), + } + if let Some(expr) = end { + self.print_const_arg(expr); + } + } + TyPatKind::Err(_) => { + self.popen(); + self.word("/*ERROR*/"); + self.pclose(); + } + } + self.ann.post(self, AnnNode::TyPat(pat)) + } + fn print_pat(&mut self, pat: &hir::Pat<'_>) { self.maybe_print_comment(pat.span.lo()); self.ann.pre(self, AnnNode::Pat(pat)); @@ -1891,9 +1947,6 @@ impl<'a> State<'a> { } self.pclose(); } - PatKind::Path(ref qpath) => { - self.print_qpath(qpath, true); - } PatKind::Struct(ref qpath, fields, etc) => { self.print_qpath(qpath, true); self.nbsp(); @@ -1966,17 +2019,17 @@ impl<'a> State<'a> { self.pclose(); } } - PatKind::Lit(e) => self.print_expr(e), + PatKind::Expr(e) => self.print_pat_expr(e), PatKind::Range(begin, end, end_kind) => { if let Some(expr) = begin { - self.print_expr(expr); + self.print_pat_expr(expr); } match end_kind { RangeEnd::Included => self.word("..."), RangeEnd::Excluded => self.word(".."), } if let Some(expr) = end { - self.print_expr(expr); + self.print_pat_expr(expr); } } PatKind::Slice(before, slice, after) => { @@ -1999,6 +2052,12 @@ impl<'a> State<'a> { self.commasep(Inconsistent, after, |s, p| s.print_pat(p)); self.word("]"); } + PatKind::Guard(inner, cond) => { + self.print_pat(inner); + self.space(); + self.word_space("if"); + self.print_expr(cond); + } PatKind::Err(_) => { self.popen(); self.word("/*ERROR*/"); @@ -2130,7 +2189,7 @@ impl<'a> State<'a> { s.ann.nested(s, Nested::BodyParamPat(body_id, i)); i += 1; - if let hir::TyKind::Infer = ty.kind { + if let hir::TyKind::Infer(()) = ty.kind { // Print nothing. } else { s.word(":"); @@ -2167,10 +2226,13 @@ impl<'a> State<'a> { let generic_params = generic_params .iter() .filter(|p| { - matches!(p, GenericParam { - kind: GenericParamKind::Lifetime { kind: LifetimeParamKind::Explicit }, - .. - }) + matches!( + p, + GenericParam { + kind: GenericParamKind::Lifetime { kind: LifetimeParamKind::Explicit }, + .. + } + ) }) .collect::>(); @@ -2387,7 +2449,7 @@ impl<'a> State<'a> { self.print_fn( decl, hir::FnHeader { - safety, + safety: safety.into(), abi, constness: hir::Constness::NotConst, asyncness: hir::IsAsync::NotAsync, @@ -2403,12 +2465,20 @@ impl<'a> State<'a> { fn print_fn_header_info(&mut self, header: hir::FnHeader) { self.print_constness(header.constness); + let safety = match header.safety { + hir::HeaderSafety::SafeTargetFeatures => { + self.word_nbsp("#[target_feature]"); + hir::Safety::Safe + } + hir::HeaderSafety::Normal(safety) => safety, + }; + match header.asyncness { hir::IsAsync::NotAsync => {} hir::IsAsync::Async(_) => self.word_nbsp("async"), } - self.print_safety(header.safety); + self.print_safety(safety); if header.abi != ExternAbi::Rust { self.word_nbsp("extern"); diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index a93da52b2703c..a994b31aeb437 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -165,6 +165,8 @@ hir_typeck_remove_semi_for_coerce_ret = the `match` arms can conform to this ret hir_typeck_remove_semi_for_coerce_semi = the `match` is a statement because of this semicolon, consider removing it hir_typeck_remove_semi_for_coerce_suggestion = remove this semicolon +hir_typeck_replace_comma_with_semicolon = replace the comma with a semicolon to create {$descr} + hir_typeck_return_stmt_outside_of_fn_body = {$statement_kind} statement outside of function body .encl_body_label = the {$statement_kind} is part of this body... @@ -190,6 +192,14 @@ hir_typeck_suggest_boxing_when_appropriate = store this in the heap by calling ` hir_typeck_suggest_ptr_null_mut = consider using `core::ptr::null_mut` instead +hir_typeck_supertrait_item_multiple_shadowee = items from several supertraits are shadowed: {$traits} + +hir_typeck_supertrait_item_shadowee = item from `{$supertrait}` is shadowed by a subtrait item + +hir_typeck_supertrait_item_shadower = item from `{$subtrait}` shadows a supertrait item + +hir_typeck_supertrait_item_shadowing = trait item `{$item}` from `{$subtrait}` shadows identically named item from supertrait + hir_typeck_trivial_cast = trivial {$numeric -> [true] numeric cast *[false] cast diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 87300f5bb83c6..3d40c5ee80450 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -391,7 +391,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let hir::Node::Block(block) = node { // check that the body's parent is an fn let parent = self.tcx.parent_hir_node(self.tcx.parent_hir_id(block.hir_id)); - if let (Some(expr), hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. })) = + if let (Some(expr), hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { .. }, .. })) = (&block.expr, parent) { // check that the `if` expr without `else` is the fn body's expr diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index e2a26ddacabf2..f2d0b9117314c 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -49,7 +49,7 @@ pub(crate) fn check_legal_trait_for_method_call( }; return Err(tcx.dcx().emit_err(errors::ExplicitDestructorCall { span, sugg })); } - tcx.ensure().coherent_trait(trait_id) + tcx.ensure_ok().coherent_trait(trait_id) } #[derive(Debug)] @@ -153,13 +153,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { closure_sig, ); let adjustments = self.adjust_steps(autoderef); - self.record_deferred_call_resolution(def_id, DeferredCallResolution { - call_expr, - callee_expr, - closure_ty: adjusted_ty, - adjustments, - fn_sig: closure_sig, - }); + self.record_deferred_call_resolution( + def_id, + DeferredCallResolution { + call_expr, + callee_expr, + closure_ty: adjusted_ty, + adjustments, + fn_sig: closure_sig, + }, + ); return Some(CallStep::DeferredClosure(def_id, closure_sig)); } @@ -196,13 +199,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { coroutine_closure_sig.abi, ); let adjustments = self.adjust_steps(autoderef); - self.record_deferred_call_resolution(def_id, DeferredCallResolution { - call_expr, - callee_expr, - closure_ty: adjusted_ty, - adjustments, - fn_sig: call_sig, - }); + self.record_deferred_call_resolution( + def_id, + DeferredCallResolution { + call_expr, + callee_expr, + closure_ty: adjusted_ty, + adjustments, + fn_sig: call_sig, + }, + ); return Some(CallStep::DeferredClosure(def_id, call_sig)); } @@ -705,7 +711,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for id in self.tcx.hir().items() { if let Some(node) = self.tcx.hir().get_if_local(id.owner_id.into()) && let hir::Node::Item(item) = node - && let hir::ItemKind::Fn(..) = item.kind + && let hir::ItemKind::Fn { .. } = item.kind && item.ident.name == segment.ident.name { err.span_label( diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 7b07e0ee939c5..f5f6ada12c3cc 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -45,6 +45,7 @@ use rustc_session::lint; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::{DUMMY_SP, Span, sym}; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_type_ir::elaborate; use tracing::{debug, instrument}; use super::FnCtxt; @@ -665,11 +666,12 @@ impl<'a, 'tcx> CastCheck<'tcx> { }; let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty); - fcx.tcx.emit_node_span_lint(lint, self.expr.hir_id, self.span, errors::TrivialCast { - numeric, - expr_ty, - cast_ty, - }); + fcx.tcx.emit_node_span_lint( + lint, + self.expr.hir_id, + self.span, + errors::TrivialCast { numeric, expr_ty, cast_ty }, + ); } #[instrument(skip(fcx), level = "debug")] @@ -688,7 +690,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { } else { match self.try_coercion_cast(fcx) { Ok(()) => { - if self.expr_ty.is_unsafe_ptr() && self.cast_ty.is_unsafe_ptr() { + if self.expr_ty.is_raw_ptr() && self.cast_ty.is_raw_ptr() { // When casting a raw pointer to another raw pointer, we cannot convert the cast into // a coercion because the pointee types might only differ in regions, which HIR typeck // cannot distinguish. This would cause us to erroneously discard a cast which will @@ -830,7 +832,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { // prim -> prim (Int(CEnum), Int(_)) => { - self.cenum_impl_drop_lint(fcx); + self.err_if_cenum_impl_drop(fcx); Ok(CastKind::EnumCast) } (Int(Char) | Int(Bool), Int(_)) => Ok(CastKind::PrimIntCast), @@ -923,7 +925,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { let src_auto: FxHashSet<_> = src_tty .auto_traits() .chain( - tcx.supertrait_def_ids(src_principal.def_id()) + elaborate::supertrait_def_ids(tcx, src_principal.def_id()) .filter(|def_id| tcx.trait_is_auto(*def_id)), ) .collect(); @@ -1090,19 +1092,14 @@ impl<'a, 'tcx> CastCheck<'tcx> { } } - fn cenum_impl_drop_lint(&self, fcx: &FnCtxt<'a, 'tcx>) { + fn err_if_cenum_impl_drop(&self, fcx: &FnCtxt<'a, 'tcx>) { if let ty::Adt(d, _) = self.expr_ty.kind() && d.has_dtor(fcx.tcx) { let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty); - fcx.tcx.emit_node_span_lint( - lint::builtin::CENUM_IMPL_DROP_CAST, - self.expr.hir_id, - self.span, - errors::CastEnumDrop { expr_ty, cast_ty }, - ); + fcx.dcx().emit_err(errors::CastEnumDrop { span: self.span, expr_ty, cast_ty }); } } diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index b0698183b5779..fc63499677f33 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -191,18 +191,21 @@ fn check_panic_info_fn(tcx: TyCtxt<'_>, fn_id: LocalDefId, fn_sig: ty::FnSig<'_> let panic_info_did = tcx.require_lang_item(hir::LangItem::PanicInfo, Some(span)); // build type `for<'a, 'b> fn(&'a PanicInfo<'b>) -> !` - let panic_info_ty = tcx.type_of(panic_info_did).instantiate(tcx, &[ty::GenericArg::from( - ty::Region::new_bound(tcx, ty::INNERMOST, ty::BoundRegion { - var: ty::BoundVar::from_u32(1), - kind: ty::BoundRegionKind::Anon, - }), - )]); + let panic_info_ty = tcx.type_of(panic_info_did).instantiate( + tcx, + &[ty::GenericArg::from(ty::Region::new_bound( + tcx, + ty::INNERMOST, + ty::BoundRegion { var: ty::BoundVar::from_u32(1), kind: ty::BoundRegionKind::Anon }, + ))], + ); let panic_info_ref_ty = Ty::new_imm_ref( tcx, - ty::Region::new_bound(tcx, ty::INNERMOST, ty::BoundRegion { - var: ty::BoundVar::ZERO, - kind: ty::BoundRegionKind::Anon, - }), + ty::Region::new_bound( + tcx, + ty::INNERMOST, + ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BoundRegionKind::Anon }, + ), panic_info_ty, ); diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index b8652d82d91bc..43124f44ca600 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -103,12 +103,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None => self.next_ty_var(expr_span), }; - let closure_args = ty::ClosureArgs::new(tcx, ty::ClosureArgsParts { - parent_args, - closure_kind_ty, - closure_sig_as_fn_ptr_ty: Ty::new_fn_ptr(tcx, sig), - tupled_upvars_ty, - }); + let closure_args = ty::ClosureArgs::new( + tcx, + ty::ClosureArgsParts { + parent_args, + closure_kind_ty, + closure_sig_as_fn_ptr_ty: Ty::new_fn_ptr(tcx, sig), + tupled_upvars_ty, + }, + ); (Ty::new_closure(tcx, expr_def_id.to_def_id(), closure_args.args), None) } @@ -177,15 +180,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => tcx.types.unit, }; - let coroutine_args = ty::CoroutineArgs::new(tcx, ty::CoroutineArgsParts { - parent_args, - kind_ty, - resume_ty, - yield_ty, - return_ty: liberated_sig.output(), - witness: interior, - tupled_upvars_ty, - }); + let coroutine_args = ty::CoroutineArgs::new( + tcx, + ty::CoroutineArgsParts { + parent_args, + kind_ty, + resume_ty, + yield_ty, + return_ty: liberated_sig.output(), + witness: interior, + tupled_upvars_ty, + }, + ); ( Ty::new_coroutine(tcx, expr_def_id.to_def_id(), coroutine_args.args), @@ -216,8 +222,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let coroutine_captures_by_ref_ty = self.next_ty_var(expr_span); - let closure_args = - ty::CoroutineClosureArgs::new(tcx, ty::CoroutineClosureArgsParts { + let closure_args = ty::CoroutineClosureArgs::new( + tcx, + ty::CoroutineClosureArgsParts { parent_args, closure_kind_ty, signature_parts_ty: Ty::new_fn_ptr( @@ -238,7 +245,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tupled_upvars_ty, coroutine_captures_by_ref_ty, coroutine_witness_ty: interior, - }); + }, + ); let coroutine_kind_ty = match expected_kind { Some(kind) => Ty::from_coroutine_closure_kind(tcx, kind), @@ -296,7 +304,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Given the expected type, figures out what it can about this closure we /// are about to type check: - #[instrument(skip(self), level = "debug")] + #[instrument(skip(self), level = "debug", ret)] fn deduce_closure_signature( &self, expected_ty: Ty<'tcx>, @@ -308,7 +316,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty, closure_kind, self.tcx - .explicit_item_super_predicates(def_id) + .explicit_item_self_bounds(def_id) .iter_instantiated_copied(self.tcx, args) .map(|(c, s)| (c.as_predicate(), s)), ), @@ -378,6 +386,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bound_predicate.rebind(proj_predicate), ), ); + // Make sure that we didn't infer a signature that mentions itself. // This can happen when we elaborate certain supertrait bounds that // mention projections containing the `Self` type. See #105401. @@ -395,8 +404,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - if inferred_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() { - expected_sig = inferred_sig; + + // Don't infer a closure signature from a goal that names the closure type as this will + // (almost always) lead to occurs check errors later in type checking. + if self.next_trait_solver() + && let Some(inferred_sig) = inferred_sig + { + // In the new solver it is difficult to explicitly normalize the inferred signature as we + // would have to manually handle universes and rewriting bound vars and placeholders back + // and forth. + // + // Instead we take advantage of the fact that we relating an inference variable with an alias + // will only instantiate the variable if the alias is rigid(*not quite). Concretely we: + // - Create some new variable `?sig` + // - Equate `?sig` with the unnormalized signature, e.g. `fn( as Trait>::Assoc)` + // - Depending on whether ` as Trait>::Assoc` is rigid, ambiguous or normalizeable, + // we will either wind up with `?sig= as Trait>::Assoc/?y/ConcreteTy` respectively. + // + // *: In cases where there are ambiguous aliases in the signature that make use of bound vars + // they will wind up present in `?sig` even though they are non-rigid. + // + // This is a bit weird and means we may wind up discarding the goal due to it naming `expected_ty` + // even though the normalized form may not name `expected_ty`. However, this matches the existing + // behaviour of the old solver and would be technically a breaking change to fix. + let generalized_fnptr_sig = self.next_ty_var(span); + let inferred_fnptr_sig = Ty::new_fn_ptr(self.tcx, inferred_sig.sig); + self.demand_eqtype(span, inferred_fnptr_sig, generalized_fnptr_sig); + + let resolved_sig = self.resolve_vars_if_possible(generalized_fnptr_sig); + + if resolved_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() { + expected_sig = Some(ExpectedSig { + cause_span: inferred_sig.cause_span, + sig: resolved_sig.fn_sig(self.tcx), + }); + } + } else { + if inferred_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() { + expected_sig = inferred_sig; + } } } @@ -981,7 +1027,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => self .tcx - .explicit_item_super_predicates(def_id) + .explicit_item_self_bounds(def_id) .iter_instantiated_copied(self.tcx, args) .find_map(|(p, s)| get_future_output(p.as_predicate(), s))?, ty::Error(_) => return Some(ret_ty), diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index bd26be11279ee..ad378367e3054 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -38,6 +38,7 @@ use std::ops::Deref; use rustc_abi::ExternAbi; +use rustc_attr_parsing::InlineAttr; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, struct_span_code_err}; use rustc_hir as hir; @@ -49,7 +50,6 @@ use rustc_infer::traits::{ IfExpressionCause, MatchExpressionArmCause, Obligation, PredicateObligation, PredicateObligations, }; -use rustc_middle::lint::in_external_macro; use rustc_middle::span_bug; use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::adjustment::{ @@ -219,7 +219,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // or pin-ergonomics. match *b.kind() { ty::RawPtr(_, b_mutbl) => { - return self.coerce_unsafe_ptr(a, b, b_mutbl); + return self.coerce_raw_ptr(a, b, b_mutbl); } ty::Ref(r_b, _, mutbl_b) => { return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b); @@ -460,9 +460,17 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // to the target type), since that should be the least // confusing. let Some(InferOk { value: ty, mut obligations }) = found else { - let err = first_error.expect("coerce_borrowed_pointer had no error"); - debug!("coerce_borrowed_pointer: failed with err = {:?}", err); - return Err(err); + if let Some(first_error) = first_error { + debug!("coerce_borrowed_pointer: failed with err = {:?}", first_error); + return Err(first_error); + } else { + // This may happen in the new trait solver since autoderef requires + // the pointee to be structurally normalizable, or else it'll just bail. + // So when we have a type like `&`, then we get no + // autoderef steps (even though there should be at least one). That means + // we get no type mismatches, since the loop above just exits early. + return Err(TypeError::Mismatch); + } }; if ty == a && mt_a.mutbl.is_not() && autoderef.step_count() == 1 { @@ -547,18 +555,24 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // the reborrow in coerce_borrowed_ptr will pick it up. let mutbl = AutoBorrowMutability::new(mutbl_b, AllowTwoPhase::No); - Some((Adjustment { kind: Adjust::Deref(None), target: ty_a }, Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), - target: Ty::new_ref(self.tcx, r_borrow, ty_a, mutbl_b), - })) + Some(( + Adjustment { kind: Adjust::Deref(None), target: ty_a }, + Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), + target: Ty::new_ref(self.tcx, r_borrow, ty_a, mutbl_b), + }, + )) } (&ty::Ref(_, ty_a, mt_a), &ty::RawPtr(_, mt_b)) => { coerce_mutbls(mt_a, mt_b)?; - Some((Adjustment { kind: Adjust::Deref(None), target: ty_a }, Adjustment { - kind: Adjust::Borrow(AutoBorrow::RawPtr(mt_b)), - target: Ty::new_ptr(self.tcx, ty_a, mt_b), - })) + Some(( + Adjustment { kind: Adjust::Deref(None), target: ty_a }, + Adjustment { + kind: Adjust::Borrow(AutoBorrow::RawPtr(mt_b)), + target: Ty::new_ptr(self.tcx, ty_a, mt_b), + }, + )) } _ => None, }; @@ -597,7 +611,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { )]; let mut has_unsized_tuple_coercion = false; - let mut has_trait_upcasting_coercion = None; // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid // emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where @@ -682,13 +695,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // these here and emit a feature error if coercion doesn't fail // due to another reason. match impl_source { - traits::ImplSource::Builtin( - BuiltinImplSource::TraitUpcasting { .. }, - _, - ) => { - has_trait_upcasting_coercion = - Some((trait_pred.self_ty(), trait_pred.trait_ref.args.type_at(1))); - } traits::ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => { has_unsized_tuple_coercion = true; } @@ -699,21 +705,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } } - if let Some((sub, sup)) = has_trait_upcasting_coercion - && !self.tcx().features().trait_upcasting() - { - // Renders better when we erase regions, since they're not really the point here. - let (sub, sup) = self.tcx.erase_regions((sub, sup)); - let mut err = feature_err( - &self.tcx.sess, - sym::trait_upcasting, - self.cause.span, - format!("cannot cast `{sub}` to `{sup}`, trait upcasting coercion is experimental"), - ); - err.note(format!("required when coercing `{source}` into `{target}`")); - err.emit(); - } - if has_unsized_tuple_coercion && !self.tcx.features().unsized_tuple_coercion() { feature_err( &self.tcx.sess, @@ -919,19 +910,32 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { match b.kind() { ty::FnPtr(_, b_hdr) => { - let a_sig = a.fn_sig(self.tcx); + let mut a_sig = a.fn_sig(self.tcx); if let ty::FnDef(def_id, _) = *a.kind() { // Intrinsics are not coercible to function pointers if self.tcx.intrinsic(def_id).is_some() { return Err(TypeError::IntrinsicCast); } - // Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396). + let fn_attrs = self.tcx.codegen_fn_attrs(def_id); + if matches!(fn_attrs.inline, InlineAttr::Force { .. }) { + return Err(TypeError::ForceInlineCast); + } if b_hdr.safety.is_safe() - && !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() + && self.tcx.codegen_fn_attrs(def_id).safe_target_features { - return Err(TypeError::TargetFeatureCast(def_id)); + // Allow the coercion if the current function has all the features that would be + // needed to call the coercee safely. + if let Some(safe_sig) = self.tcx.adjust_target_feature_sig( + def_id, + a_sig, + self.fcx.body_id.into(), + ) { + a_sig = safe_sig; + } else { + return Err(TypeError::TargetFeatureCast(def_id)); + } } } @@ -1013,13 +1017,13 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } } - fn coerce_unsafe_ptr( + fn coerce_raw_ptr( &self, a: Ty<'tcx>, b: Ty<'tcx>, mutbl_b: hir::Mutability, ) -> CoerceResult<'tcx> { - debug!("coerce_unsafe_ptr(a={:?}, b={:?})", a, b); + debug!("coerce_raw_ptr(a={:?}, b={:?})", a, b); let (is_ref, mt_a) = match *a.kind() { ty::Ref(_, ty, mutbl) => (true, ty::TypeAndMut { ty, mutbl }), @@ -1029,21 +1033,21 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { coerce_mutbls(mt_a.mutbl, mutbl_b)?; // Check that the types which they point at are compatible. - let a_unsafe = Ty::new_ptr(self.tcx, mt_a.ty, mutbl_b); - // Although references and unsafe ptrs have the same + let a_raw = Ty::new_ptr(self.tcx, mt_a.ty, mutbl_b); + // Although references and raw ptrs have the same // representation, we still register an Adjust::DerefRef so that // regionck knows that the region for `a` must be valid here. if is_ref { - self.unify_and(a_unsafe, b, |target| { - vec![Adjustment { kind: Adjust::Deref(None), target: mt_a.ty }, Adjustment { - kind: Adjust::Borrow(AutoBorrow::RawPtr(mutbl_b)), - target, - }] + self.unify_and(a_raw, b, |target| { + vec![ + Adjustment { kind: Adjust::Deref(None), target: mt_a.ty }, + Adjustment { kind: Adjust::Borrow(AutoBorrow::RawPtr(mutbl_b)), target }, + ] }) } else if mt_a.mutbl != mutbl_b { - self.unify_and(a_unsafe, b, simple(Adjust::Pointer(PointerCoercion::MutToConstPointer))) + self.unify_and(a_raw, b, simple(Adjust::Pointer(PointerCoercion::MutToConstPointer))) } else { - self.unify_and(a_unsafe, b, identity) + self.unify_and(a_raw, b, identity) } } } @@ -1110,7 +1114,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.next_trait_solver() && let ty::Alias(..) = ty.kind() { - ocx.structurally_normalize(&cause, self.param_env, ty) + ocx.structurally_normalize_ty(&cause, self.param_env, ty) } else { Ok(ty) } @@ -1197,6 +1201,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Ok(prev_ty); } + let is_force_inline = |ty: Ty<'tcx>| { + if let ty::FnDef(did, _) = ty.kind() { + matches!(self.tcx.codegen_fn_attrs(did).inline, InlineAttr::Force { .. }) + } else { + false + } + }; + if is_force_inline(prev_ty) || is_force_inline(new_ty) { + return Err(TypeError::ForceInlineCast); + } + // Special-case that coercion alone cannot handle: // Function items or non-capturing closures of differing IDs or GenericArgs. let (a_sig, b_sig) = { @@ -1279,10 +1294,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => span_bug!(new.span, "should not try to coerce a {new_ty} to a fn pointer"), }; for expr in exprs.iter().map(|e| e.as_coercion_site()) { - self.apply_adjustments(expr, vec![Adjustment { - kind: prev_adjustment.clone(), - target: fn_ptr, - }]); + self.apply_adjustments( + expr, + vec![Adjustment { kind: prev_adjustment.clone(), target: fn_ptr }], + ); } self.apply_adjustments(new, vec![Adjustment { kind: next_adjustment, target: fn_ptr }]); return Ok(fn_ptr); @@ -1814,30 +1829,26 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { fcx.probe(|_| { let ocx = ObligationCtxt::new(fcx); ocx.register_obligations( - fcx.tcx.item_super_predicates(rpit_def_id).iter_identity().filter_map( - |clause| { - let predicate = clause - .kind() - .map_bound(|clause| match clause { - ty::ClauseKind::Trait(trait_pred) => Some( - ty::ClauseKind::Trait(trait_pred.with_self_ty(fcx.tcx, ty)), - ), - ty::ClauseKind::Projection(proj_pred) => { - Some(ty::ClauseKind::Projection( - proj_pred.with_self_ty(fcx.tcx, ty), - )) - } - _ => None, - }) - .transpose()?; - Some(Obligation::new( - fcx.tcx, - ObligationCause::dummy(), - fcx.param_env, - predicate, - )) - }, - ), + fcx.tcx.item_self_bounds(rpit_def_id).iter_identity().filter_map(|clause| { + let predicate = clause + .kind() + .map_bound(|clause| match clause { + ty::ClauseKind::Trait(trait_pred) => Some(ty::ClauseKind::Trait( + trait_pred.with_self_ty(fcx.tcx, ty), + )), + ty::ClauseKind::Projection(proj_pred) => Some( + ty::ClauseKind::Projection(proj_pred.with_self_ty(fcx.tcx, ty)), + ), + _ => None, + }) + .transpose()?; + Some(Obligation::new( + fcx.tcx, + ObligationCause::dummy(), + fcx.param_env, + predicate, + )) + }), ); ocx.select_where_possible().is_empty() }) @@ -1908,7 +1919,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { cond_expr.span.desugaring_kind(), None | Some(DesugaringKind::WhileLoop) ) - && !in_external_macro(fcx.tcx.sess, cond_expr.span) + && !cond_expr.span.in_external_macro(fcx.tcx.sess.source_map()) && !matches!( cond_expr.kind, hir::ExprKind::Match(.., hir::MatchSource::TryDesugar(_)) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index e51323fc5c852..85e949952f8de 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -1,4 +1,4 @@ -use rustc_errors::{Applicability, Diag, MultiSpan}; +use rustc_errors::{Applicability, Diag, MultiSpan, listify}; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::intravisit::Visitor; @@ -30,7 +30,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if expr_ty == expected { return; } - self.annotate_alternative_method_deref(err, expr, error); self.explain_self_literal(err, expr, expected, expr_ty); @@ -39,6 +38,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || self.suggest_missing_unwrap_expect(err, expr, expected, expr_ty) || self.suggest_remove_last_method_call(err, expr, expected) || self.suggest_associated_const(err, expr, expected) + || self.suggest_semicolon_in_repeat_expr(err, expr, expr_ty) || self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr) || self.suggest_option_to_bool(err, expr, expr_ty, expected) || self.suggest_compatible_variants(err, expr, expected, expr_ty) @@ -85,6 +85,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.annotate_expected_due_to_let_ty(err, expr, error); self.annotate_loop_expected_due_to_inference(err, expr, error); + if self.annotate_mut_binding_to_immutable_binding(err, expr, error) { + return; + } // FIXME(#73154): For now, we do leak check when coercing function // pointers in typeck, instead of only during borrowck. This can lead @@ -795,6 +798,98 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Detect the following case + /// + /// ```text + /// fn change_object(mut a: &Ty) { + /// let a = Ty::new(); + /// b = a; + /// } + /// ``` + /// + /// where the user likely meant to modify the value behind there reference, use `a` as an out + /// parameter, instead of mutating the local binding. When encountering this we suggest: + /// + /// ```text + /// fn change_object(a: &'_ mut Ty) { + /// let a = Ty::new(); + /// *b = a; + /// } + /// ``` + fn annotate_mut_binding_to_immutable_binding( + &self, + err: &mut Diag<'_>, + expr: &hir::Expr<'_>, + error: Option>, + ) -> bool { + if let Some(TypeError::Sorts(ExpectedFound { expected, found })) = error + && let ty::Ref(_, inner, hir::Mutability::Not) = expected.kind() + + // The difference between the expected and found values is one level of borrowing. + && self.can_eq(self.param_env, *inner, found) + + // We have an `ident = expr;` assignment. + && let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Assign(lhs, rhs, _), .. }) = + self.tcx.parent_hir_node(expr.hir_id) + && rhs.hir_id == expr.hir_id + + // We are assigning to some binding. + && let hir::ExprKind::Path(hir::QPath::Resolved( + None, + hir::Path { res: hir::def::Res::Local(hir_id), .. }, + )) = lhs.kind + && let hir::Node::Pat(pat) = self.tcx.hir_node(*hir_id) + + // The pattern we have is an fn argument. + && let hir::Node::Param(hir::Param { ty_span, .. }) = + self.tcx.parent_hir_node(pat.hir_id) + && let item = self.tcx.hir().get_parent_item(pat.hir_id) + && let item = self.tcx.hir_owner_node(item) + && let Some(fn_decl) = item.fn_decl() + + // We have a mutable binding in the argument. + && let hir::PatKind::Binding(hir::BindingMode::MUT, _hir_id, ident, _) = pat.kind + + // Look for the type corresponding to the argument pattern we have in the argument list. + && let Some(ty_ref) = fn_decl + .inputs + .iter() + .filter_map(|ty| match ty.kind { + hir::TyKind::Ref(lt, mut_ty) if ty.span == *ty_span => Some((lt, mut_ty)), + _ => None, + }) + .next() + { + let mut sugg = if ty_ref.1.mutbl.is_mut() { + // Leave `&'name mut Ty` and `&mut Ty` as they are (#136028). + vec![] + } else { + // `&'name Ty` -> `&'name mut Ty` or `&Ty` -> `&mut Ty` + vec![( + ty_ref.1.ty.span.shrink_to_lo(), + format!( + "{}mut ", + if ty_ref.0.ident.span.lo() == ty_ref.0.ident.span.hi() { "" } else { " " }, + ), + )] + }; + sugg.extend([ + (pat.span.until(ident.span), String::new()), + (lhs.span.shrink_to_lo(), "*".to_string()), + ]); + // We suggest changing the argument from `mut ident: &Ty` to `ident: &'_ mut Ty` and the + // assignment from `ident = val;` to `*ident = val;`. + err.multipart_suggestion_verbose( + "you might have meant to mutate the pointed at value being passed in, instead of \ + changing the reference in the local binding", + sugg, + Applicability::MaybeIncorrect, + ); + return true; + } + false + } + fn annotate_alternative_method_deref( &self, err: &mut Diag<'_>, @@ -921,18 +1016,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, self.tcx.def_path_str(candidate.item.container_id(self.tcx)) ), - [.., last] if other_methods_in_scope.len() < 5 => { + _ if other_methods_in_scope.len() < 5 => { format!( - "the methods of the same name on {} and `{}`", - other_methods_in_scope[..other_methods_in_scope.len() - 1] - .iter() - .map(|c| format!( - "`{}`", - self.tcx.def_path_str(c.item.container_id(self.tcx)) - )) - .collect::>() - .join(", "), - self.tcx.def_path_str(last.item.container_id(self.tcx)) + "the methods of the same name on {}", + listify( + &other_methods_in_scope[..other_methods_in_scope.len() - 1], + |c| format!("`{}`", self.tcx.def_path_str(c.item.container_id(self.tcx))) + ) + .unwrap_or_default(), ) } _ => format!( diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index ff09583cc65a6..1bf8aa4f78d4d 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -19,8 +19,15 @@ use crate::fluent_generated as fluent; pub(crate) struct BaseExpressionDoubleDot { #[primary_span] pub span: Span, + #[suggestion( + hir_typeck_base_expression_double_dot_enable_default_field_values, + code = "#![feature(default_field_values)]\n", + applicability = "machine-applicable", + style = "verbose" + )] + pub default_field_values_suggestion: Option, #[subdiagnostic] - pub default_field_values: Option, + pub default_field_values_help: Option, #[subdiagnostic] pub add_expr: Option, #[subdiagnostic] @@ -670,9 +677,11 @@ pub(crate) struct CannotCastToBool<'tcx> { pub help: CannotCastToBoolHelp, } -#[derive(LintDiagnostic)] +#[derive(Diagnostic)] #[diag(hir_typeck_cast_enum_drop)] pub(crate) struct CastEnumDrop<'tcx> { + #[primary_span] + pub span: Span, pub expr_ty: Ty<'tcx>, pub cast_ty: Ty<'tcx>, } @@ -846,3 +855,51 @@ pub(crate) struct PassFnItemToVariadicFunction { pub sugg_span: Span, pub replace: String, } + +#[derive(Subdiagnostic)] +#[suggestion( + hir_typeck_replace_comma_with_semicolon, + applicability = "machine-applicable", + style = "verbose", + code = "; " +)] +pub(crate) struct ReplaceCommaWithSemicolon { + #[primary_span] + pub comma_span: Span, + pub descr: &'static str, +} + +#[derive(LintDiagnostic)] +#[diag(hir_typeck_supertrait_item_shadowing)] +pub(crate) struct SupertraitItemShadowing { + pub item: Symbol, + pub subtrait: Symbol, + #[subdiagnostic] + pub shadower: SupertraitItemShadower, + #[subdiagnostic] + pub shadowee: SupertraitItemShadowee, +} + +#[derive(Subdiagnostic)] +#[note(hir_typeck_supertrait_item_shadower)] +pub(crate) struct SupertraitItemShadower { + pub subtrait: Symbol, + #[primary_span] + pub span: Span, +} + +#[derive(Subdiagnostic)] +pub(crate) enum SupertraitItemShadowee { + #[note(hir_typeck_supertrait_item_shadowee)] + Labeled { + #[primary_span] + span: Span, + supertrait: Symbol, + }, + #[note(hir_typeck_supertrait_item_multiple_shadowee)] + Several { + #[primary_span] + spans: MultiSpan, + traits: DiagSymbolList, + }, +} diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 2f7ed8dd2a877..9ccd674608744 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -11,7 +11,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::unord::UnordMap; use rustc_errors::codes::*; use rustc_errors::{ - Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey, Subdiagnostic, pluralize, + Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey, Subdiagnostic, listify, pluralize, struct_span_code_err, }; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -84,10 +84,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let adj_ty = self.next_ty_var(expr.span); - self.apply_adjustments(expr, vec![Adjustment { - kind: Adjust::NeverToAny, - target: adj_ty, - }]); + self.apply_adjustments( + expr, + vec![Adjustment { kind: Adjust::NeverToAny, target: adj_ty }], + ); ty = adj_ty; } @@ -269,6 +269,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // diverging expression (e.g. it arose from desugaring of `try { return }`), // we skip issuing a warning because it is autogenerated code. ExprKind::Call(..) if expr.span.is_desugaring(DesugaringKind::TryBlock) => {} + // Likewise, do not lint unreachable code injected via contracts desugaring. + ExprKind::Call(..) if expr.span.is_desugaring(DesugaringKind::Contract) => {} ExprKind::Call(callee, _) => self.warn_if_unreachable(expr.hir_id, callee.span, "call"), ExprKind::MethodCall(segment, ..) => { self.warn_if_unreachable(expr.hir_id, segment.ident.span, "call") @@ -412,7 +414,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) => true, - hir::Node::Pat(_) => { + hir::Node::TyPat(_) | hir::Node::Pat(_) => { self.dcx().span_delayed_bug(expr.span, "place expr not allowed in pattern"); true } @@ -430,6 +432,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | hir::Node::AssocItemConstraint(_) | hir::Node::TraitRef(_) | hir::Node::PatField(_) + | hir::Node::PatExpr(_) | hir::Node::LetStmt(_) | hir::Node::Synthetic | hir::Node::Err(_) @@ -456,6 +459,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Does not constitute a read. hir::PatKind::Wild => false, + // Might not constitute a read, since the condition might be false. + hir::PatKind::Guard(_, _) => true, + // This is unnecessarily restrictive when the pattern that doesn't // constitute a read is unreachable. // @@ -476,12 +482,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::PatKind::Binding(_, _, _, _) | hir::PatKind::Struct(_, _, _) | hir::PatKind::TupleStruct(_, _, _) - | hir::PatKind::Path(_) | hir::PatKind::Tuple(_, _) | hir::PatKind::Box(_) | hir::PatKind::Ref(_, _) | hir::PatKind::Deref(_) - | hir::PatKind::Lit(_) + | hir::PatKind::Expr(_) | hir::PatKind::Range(_, _, _) | hir::PatKind::Slice(_, _, _) | hir::PatKind::Err(_) => true, @@ -834,7 +839,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We always require that the type provided as the value for // a type parameter outlives the moment of instantiation. let args = self.typeck_results.borrow().node_args(expr.hir_id); - self.add_wf_bounds(args, expr); + self.add_wf_bounds(args, expr.span); ty } @@ -1127,7 +1132,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let encl_item_id = self.tcx.hir().get_parent_item(expr.hir_id); if let hir::Node::Item(hir::Item { - kind: hir::ItemKind::Fn(..), span: encl_fn_span, .. + kind: hir::ItemKind::Fn { .. }, + span: encl_fn_span, + .. }) | hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)), @@ -1653,8 +1660,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir_ty: Option<&'tcx hir::Ty<'tcx>>, expected: Expectation<'tcx>, ) -> Ty<'tcx> { - self.dcx().span_err(inner_expr.span, "unsafe binder casts are not fully implemented"); - match kind { hir::UnsafeBinderCastKind::Wrap => { let ascribed_ty = @@ -1771,12 +1776,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let parent_node = self.tcx.hir().parent_iter(expr.hir_id).find(|(_, node)| { !matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::AddrOf(..), .. })) }); - let Some(( - _, - hir::Node::LetStmt(hir::LetStmt { ty: Some(ty), .. }) - | hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _, _), .. }), - )) = parent_node - else { + let Some((_, hir::Node::LetStmt(hir::LetStmt { ty: Some(ty), .. }))) = parent_node else { return; }; if let hir::TyKind::Array(_, ct) = ty.peel_refs().kind { @@ -1796,7 +1796,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn check_expr_const_block( + pub(super) fn check_expr_const_block( &self, block: &'tcx hir::ConstBlock, expected: Expectation<'tcx>, @@ -1990,18 +1990,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { adt_ty: Ty<'tcx>, expected: Expectation<'tcx>, expr: &hir::Expr<'_>, - span: Span, + path_span: Span, variant: &'tcx ty::VariantDef, hir_fields: &'tcx [hir::ExprField<'tcx>], base_expr: &'tcx hir::StructTailExpr<'tcx>, ) { let tcx = self.tcx; - let adt_ty = self.try_structurally_resolve_type(span, adt_ty); + let adt_ty = self.try_structurally_resolve_type(path_span, adt_ty); let adt_ty_hint = expected.only_has_type(self).and_then(|expected| { self.fudge_inference_if_ok(|| { let ocx = ObligationCtxt::new(self); - ocx.sup(&self.misc(span), self.param_env, expected, adt_ty)?; + ocx.sup(&self.misc(path_span), self.param_env, expected, adt_ty)?; if !ocx.select_where_possible().is_empty() { return Err(TypeError::Mismatch); } @@ -2011,11 +2011,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); if let Some(adt_ty_hint) = adt_ty_hint { // re-link the variables that the fudging above can create. - self.demand_eqtype(span, adt_ty_hint, adt_ty); + self.demand_eqtype(path_span, adt_ty_hint, adt_ty); } let ty::Adt(adt, args) = adt_ty.kind() else { - span_bug!(span, "non-ADT passed to check_expr_struct_fields"); + span_bug!(path_span, "non-ADT passed to check_expr_struct_fields"); }; let adt_kind = adt.adt_kind(); @@ -2106,7 +2106,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if adt_kind == AdtKind::Union && hir_fields.len() != 1 { struct_span_code_err!( self.dcx(), - span, + path_span, E0784, "union expressions should have exactly one field", ) @@ -2137,13 +2137,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } if !self.tcx.features().default_field_values() { + let sugg = self.tcx.crate_level_attribute_injection_span(expr.hir_id); self.dcx().emit_err(BaseExpressionDoubleDot { span: span.shrink_to_hi(), // We only mention enabling the feature if this is a nightly rustc *and* the // expression would make sense with the feature enabled. - default_field_values: if self.tcx.sess.is_nightly_build() + default_field_values_suggestion: if self.tcx.sess.is_nightly_build() && missing_mandatory_fields.is_empty() && !missing_optional_fields.is_empty() + && sugg.is_some() + { + sugg + } else { + None + }, + default_field_values_help: if self.tcx.sess.is_nightly_build() + && missing_mandatory_fields.is_empty() + && !missing_optional_fields.is_empty() + && sugg.is_none() { Some(BaseExpressionDoubleDotEnableDefaultFieldValues) } else { @@ -2166,15 +2177,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); return; } + if variant.fields.is_empty() { + let mut err = self.dcx().struct_span_err( + span, + format!( + "`{adt_ty}` has no fields, `..` needs at least one default field in the \ + struct definition", + ), + ); + err.span_label(path_span, "this type has no fields"); + err.emit(); + } if !missing_mandatory_fields.is_empty() { let s = pluralize!(missing_mandatory_fields.len()); - let fields: Vec<_> = - missing_mandatory_fields.iter().map(|f| format!("`{f}`")).collect(); - let fields = match &fields[..] { - [] => unreachable!(), - [only] => only.to_string(), - [start @ .., last] => format!("{} and {last}", start.join(", ")), - }; + let fields = listify(&missing_mandatory_fields, |f| format!("`{f}`")).unwrap(); self.dcx() .struct_span_err( span.shrink_to_hi(), @@ -2315,11 +2331,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect(); if !private_fields.is_empty() { - self.report_private_fields(adt_ty, span, expr.span, private_fields, hir_fields); + self.report_private_fields( + adt_ty, + path_span, + expr.span, + private_fields, + hir_fields, + ); } else { self.report_missing_fields( adt_ty, - span, + path_span, + expr.span, remaining_fields, variant, hir_fields, @@ -2357,6 +2380,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, adt_ty: Ty<'tcx>, span: Span, + full_span: Span, remaining_fields: UnordMap, variant: &'tcx ty::VariantDef, hir_fields: &'tcx [hir::ExprField<'tcx>], @@ -2396,6 +2420,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); err.span_label(span, format!("missing {remaining_fields_names}{truncated_fields_error}")); + if remaining_fields.items().all(|(_, (_, field))| field.value.is_some()) + && self.tcx.sess.is_nightly_build() + { + let msg = format!( + "all remaining fields have default values, {you_can} use those values with `..`", + you_can = if self.tcx.features().default_field_values() { + "you can" + } else { + "if you added `#![feature(default_field_values)]` to your crate you could" + }, + ); + if let Some(hir_field) = hir_fields.last() { + err.span_suggestion_verbose( + hir_field.span.shrink_to_hi(), + msg, + ", ..".to_string(), + Applicability::MachineApplicable, + ); + } else if hir_fields.is_empty() { + err.span_suggestion_verbose( + span.shrink_to_hi().with_hi(full_span.hi()), + msg, + " { .. }".to_string(), + Applicability::MachineApplicable, + ); + } + } + if let Some(hir_field) = hir_fields.last() { self.suggest_fru_from_range_and_emit(hir_field, variant, args, err); } else { @@ -2495,25 +2547,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .partition(|field| field.2); err.span_labels(used_private_fields.iter().map(|(_, span, _)| *span), "private field"); if !remaining_private_fields.is_empty() { - let remaining_private_fields_len = remaining_private_fields.len(); - let names = match &remaining_private_fields - .iter() - .map(|(name, _, _)| name) - .collect::>()[..] - { - _ if remaining_private_fields_len > 6 => String::new(), - [name] => format!("`{name}` "), - [names @ .., last] => { - let names = names.iter().map(|name| format!("`{name}`")).collect::>(); - format!("{} and `{last}` ", names.join(", ")) - } - [] => bug!("expected at least one private field to report"), + let names = if remaining_private_fields.len() > 6 { + String::new() + } else { + format!( + "{} ", + listify(&remaining_private_fields, |(name, _, _)| format!("`{name}`")) + .expect("expected at least one private field to report") + ) }; err.note(format!( "{}private field{s} {names}that {were} not provided", if used_fields.is_empty() { "" } else { "...and other " }, - s = pluralize!(remaining_private_fields_len), - were = pluralize!("was", remaining_private_fields_len), + s = pluralize!(remaining_private_fields.len()), + were = pluralize!("was", remaining_private_fields.len()), )); } @@ -3278,10 +3325,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) .map(|mut field_path| { field_path.pop(); - field_path - .iter() - .map(|id| format!("{}.", id.name.to_ident_string())) - .collect::() + field_path.iter().map(|id| format!("{}.", id)).collect::() }) .collect::>(); candidate_fields.sort(); @@ -3512,7 +3556,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - if base_t.is_unsafe_ptr() && idx_t.is_integral() { + if base_t.is_raw_ptr() && idx_t.is_integral() { err.multipart_suggestion( "consider using `wrapping_add` or `add` for indexing into raw pointer", vec![ diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index ecbae6ac72fa2..860e619be7187 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -11,10 +11,9 @@ use hir::def::DefKind; use hir::pat_util::EnumerateAndAdjustIterator as _; use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxIndexMap; -use rustc_hir as hir; use rustc_hir::def::{CtorOf, Res}; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{HirId, PatKind}; +use rustc_hir::{self as hir, HirId, PatExpr, PatExprKind, PatKind}; use rustc_lint::LateContext; use rustc_middle::hir::place::ProjectionKind; // Export these here so that Clippy can use them. @@ -564,11 +563,11 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // FIXME(never_patterns): does this do what I expect? needs_to_be_read = true; } - PatKind::Path(qpath) => { + PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => { // A `Path` pattern is just a name like `Foo`. This is either a // named constant or else it refers to an ADT variant - let res = self.cx.typeck_results().qpath_res(qpath, pat.hir_id); + let res = self.cx.typeck_results().qpath_res(qpath, *hir_id); match res { Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => { // Named constants have to be equated with the value @@ -581,7 +580,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // Otherwise, this is a struct/enum variant, and so it's // only a read if we need to read the discriminant. needs_to_be_read |= - self.is_multivariant_adt(place.place.ty(), pat.span); + self.is_multivariant_adt(place.place.ty(), *span); } } } @@ -595,7 +594,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx let place_ty = place.place.ty(); needs_to_be_read |= self.is_multivariant_adt(place_ty, pat.span); } - PatKind::Lit(_) | PatKind::Range(..) => { + PatKind::Expr(_) | PatKind::Range(..) => { // If the PatKind is a Lit or a Range then we want // to borrow discr. needs_to_be_read = true; @@ -615,6 +614,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx | PatKind::Box(_) | PatKind::Deref(_) | PatKind::Ref(..) + | PatKind::Guard(..) | PatKind::Wild | PatKind::Err(_) => { // If the PatKind is Or, Box, or Ref, the decision is made later @@ -892,57 +892,75 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx let tcx = self.cx.tcx(); self.cat_pattern(discr_place.clone(), pat, &mut |place, pat| { - if let PatKind::Binding(_, canonical_id, ..) = pat.kind { - debug!("walk_pat: binding place={:?} pat={:?}", place, pat); - if let Some(bm) = - self.cx.typeck_results().extract_binding_mode(tcx.sess, pat.hir_id, pat.span) - { - debug!("walk_pat: pat.hir_id={:?} bm={:?}", pat.hir_id, bm); - - // pat_ty: the type of the binding being produced. - let pat_ty = self.node_ty(pat.hir_id)?; - debug!("walk_pat: pat_ty={:?}", pat_ty); + match pat.kind { + PatKind::Binding(_, canonical_id, ..) => { + debug!("walk_pat: binding place={:?} pat={:?}", place, pat); + if let Some(bm) = self + .cx + .typeck_results() + .extract_binding_mode(tcx.sess, pat.hir_id, pat.span) + { + debug!("walk_pat: pat.hir_id={:?} bm={:?}", pat.hir_id, bm); - let def = Res::Local(canonical_id); - if let Ok(ref binding_place) = self.cat_res(pat.hir_id, pat.span, pat_ty, def) { - self.delegate.borrow_mut().bind(binding_place, binding_place.hir_id); - } + // pat_ty: the type of the binding being produced. + let pat_ty = self.node_ty(pat.hir_id)?; + debug!("walk_pat: pat_ty={:?}", pat_ty); - // Subtle: MIR desugaring introduces immutable borrows for each pattern - // binding when lowering pattern guards to ensure that the guard does not - // modify the scrutinee. - if has_guard { - self.delegate.borrow_mut().borrow( - place, - discr_place.hir_id, - BorrowKind::Immutable, - ); - } + let def = Res::Local(canonical_id); + if let Ok(ref binding_place) = + self.cat_res(pat.hir_id, pat.span, pat_ty, def) + { + self.delegate.borrow_mut().bind(binding_place, binding_place.hir_id); + } - // It is also a borrow or copy/move of the value being matched. - // In a cases of pattern like `let pat = upvar`, don't use the span - // of the pattern, as this just looks confusing, instead use the span - // of the discriminant. - match bm.0 { - hir::ByRef::Yes(m) => { - let bk = ty::BorrowKind::from_mutbl(m); - self.delegate.borrow_mut().borrow(place, discr_place.hir_id, bk); + // Subtle: MIR desugaring introduces immutable borrows for each pattern + // binding when lowering pattern guards to ensure that the guard does not + // modify the scrutinee. + if has_guard { + self.delegate.borrow_mut().borrow( + place, + discr_place.hir_id, + BorrowKind::Immutable, + ); } - hir::ByRef::No => { - debug!("walk_pat binding consuming pat"); - self.consume_or_copy(place, discr_place.hir_id); + + // It is also a borrow or copy/move of the value being matched. + // In a cases of pattern like `let pat = upvar`, don't use the span + // of the pattern, as this just looks confusing, instead use the span + // of the discriminant. + match bm.0 { + hir::ByRef::Yes(m) => { + let bk = ty::BorrowKind::from_mutbl(m); + self.delegate.borrow_mut().borrow(place, discr_place.hir_id, bk); + } + hir::ByRef::No => { + debug!("walk_pat binding consuming pat"); + self.consume_or_copy(place, discr_place.hir_id); + } } } } - } else if let PatKind::Deref(subpattern) = pat.kind { - // A deref pattern is a bit special: the binding mode of its inner bindings - // determines whether to borrow *at the level of the deref pattern* rather than - // borrowing the bound place (since that inner place is inside the temporary that - // stores the result of calling `deref()`/`deref_mut()` so can't be captured). - let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(subpattern); - let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not }; - let bk = ty::BorrowKind::from_mutbl(mutability); - self.delegate.borrow_mut().borrow(place, discr_place.hir_id, bk); + PatKind::Deref(subpattern) => { + // A deref pattern is a bit special: the binding mode of its inner bindings + // determines whether to borrow *at the level of the deref pattern* rather than + // borrowing the bound place (since that inner place is inside the temporary that + // stores the result of calling `deref()`/`deref_mut()` so can't be captured). + let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(subpattern); + let mutability = + if mutable { hir::Mutability::Mut } else { hir::Mutability::Not }; + let bk = ty::BorrowKind::from_mutbl(mutability); + self.delegate.borrow_mut().borrow(place, discr_place.hir_id, bk); + } + PatKind::Never => { + // A `!` pattern always counts as an immutable read of the discriminant, + // even in an irrefutable pattern. + self.delegate.borrow_mut().borrow( + place, + discr_place.hir_id, + BorrowKind::Immutable, + ); + } + _ => {} } Ok(()) @@ -1737,7 +1755,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx } } - PatKind::Binding(.., Some(subpat)) => { + PatKind::Binding(.., Some(subpat)) | PatKind::Guard(subpat, _) => { self.cat_pattern(place_with_id, subpat, op)?; } @@ -1800,9 +1818,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx } } - PatKind::Path(_) - | PatKind::Binding(.., None) - | PatKind::Lit(..) + PatKind::Binding(.., None) + | PatKind::Expr(..) | PatKind::Range(..) | PatKind::Never | PatKind::Wild diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index c128485d93ed7..4618c5d3849b3 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -10,7 +10,7 @@ use rustc_hir as hir; use rustc_hir::HirId; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::{InferKind, Visitor}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable}; use rustc_session::lint; use rustc_span::def_id::LocalDefId; @@ -641,23 +641,29 @@ impl<'tcx> AnnotateUnitFallbackVisitor<'_, 'tcx> { impl<'tcx> Visitor<'tcx> for AnnotateUnitFallbackVisitor<'_, 'tcx> { type Result = ControlFlow; - fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) -> Self::Result { + fn visit_infer( + &mut self, + inf_id: HirId, + inf_span: Span, + _kind: InferKind<'tcx>, + ) -> Self::Result { // Try to replace `_` with `()`. - if let hir::TyKind::Infer = hir_ty.kind - && let Some(ty) = self.fcx.typeck_results.borrow().node_type_opt(hir_ty.hir_id) + if let Some(ty) = self.fcx.typeck_results.borrow().node_type_opt(inf_id) && let Some(vid) = self.fcx.root_vid(ty) && self.reachable_vids.contains(&vid) + && inf_span.can_be_used_for_suggestions() { - return ControlFlow::Break(errors::SuggestAnnotation::Unit(hir_ty.span)); + return ControlFlow::Break(errors::SuggestAnnotation::Unit(inf_span)); } - hir::intravisit::walk_ty(self, hir_ty) + + ControlFlow::Continue(()) } fn visit_qpath( &mut self, qpath: &'tcx rustc_hir::QPath<'tcx>, id: HirId, - _span: Span, + span: Span, ) -> Self::Result { let arg_segment = match qpath { hir::QPath::Resolved(_, path) => { @@ -669,13 +675,21 @@ impl<'tcx> Visitor<'tcx> for AnnotateUnitFallbackVisitor<'_, 'tcx> { } }; // Alternatively, try to turbofish `::<_, (), _>`. - if let Some(def_id) = self.fcx.typeck_results.borrow().qpath_res(qpath, id).opt_def_id() { + if let Some(def_id) = self.fcx.typeck_results.borrow().qpath_res(qpath, id).opt_def_id() + && span.can_be_used_for_suggestions() + { self.suggest_for_segment(arg_segment, def_id, id)?; } hir::intravisit::walk_qpath(self, qpath, id) } fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Self::Result { + if let hir::ExprKind::Closure(&hir::Closure { body, .. }) + | hir::ExprKind::ConstBlock(hir::ConstBlock { body, .. }) = expr.kind + { + self.visit_body(self.fcx.tcx.hir().body(body))?; + } + // Try to suggest adding an explicit qself `()` to a trait method path. // i.e. changing `Default::default()` to `<() as Default>::default()`. if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind @@ -686,17 +700,21 @@ impl<'tcx> Visitor<'tcx> for AnnotateUnitFallbackVisitor<'_, 'tcx> { && let Some(vid) = self.fcx.root_vid(self_ty) && self.reachable_vids.contains(&vid) && let [.., trait_segment, _method_segment] = path.segments + && expr.span.can_be_used_for_suggestions() { let span = path.span.shrink_to_lo().to(trait_segment.ident.span); return ControlFlow::Break(errors::SuggestAnnotation::Path(span)); } + // Or else, try suggesting turbofishing the method args. if let hir::ExprKind::MethodCall(segment, ..) = expr.kind && let Some(def_id) = self.fcx.typeck_results.borrow().type_dependent_def_id(expr.hir_id) + && expr.span.can_be_used_for_suggestions() { self.suggest_for_segment(segment, def_id, expr.hir_id)?; } + hir::intravisit::walk_expr(self, expr) } @@ -707,6 +725,7 @@ impl<'tcx> Visitor<'tcx> for AnnotateUnitFallbackVisitor<'_, 'tcx> { && let Some(ty) = self.fcx.typeck_results.borrow().node_type_opt(local.hir_id) && let Some(vid) = self.fcx.root_vid(ty) && self.reachable_vids.contains(&vid) + && local.span.can_be_used_for_suggestions() { return ControlFlow::Break(errors::SuggestAnnotation::Local( local.pat.span.shrink_to_hi(), diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index b77071523a6a0..1113f7f70953b 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -3,12 +3,12 @@ use std::slice; use rustc_abi::FieldIdx; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey}; +use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan}; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::VisitorExt; use rustc_hir::lang_items::LangItem; -use rustc_hir::{self as hir, ExprKind, GenericArg, HirId, Node, QPath, intravisit}; +use rustc_hir::{self as hir, AmbigArg, ExprKind, GenericArg, HirId, Node, QPath, intravisit}; use rustc_hir_analysis::hir_ty_lowering::errors::GenericsArgsErrExtend; use rustc_hir_analysis::hir_ty_lowering::generics::{ check_generic_arg_count_for_call, lower_generic_args, @@ -253,6 +253,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } + let mut expr_ty = self.typeck_results.borrow().expr_ty_adjusted(expr); + for a in &adj { match a.kind { Adjust::NeverToAny => { @@ -266,7 +268,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None, expr.span, overloaded_deref.method_call(self.tcx), - self.tcx.mk_args(&[a.target.into()]), + self.tcx.mk_args(&[expr_ty.into()]), ); } Adjust::Deref(None) => { @@ -283,13 +285,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // No effects to enforce here. } } + + expr_ty = a.target; } let autoborrow_mut = adj.iter().any(|adj| { - matches!(adj, &Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Mut { .. })), - .. - }) + matches!( + adj, + &Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Mut { .. })), + .. + } + ) }); match self.typeck_results.borrow_mut().adjustments_mut().entry(expr.hir_id) { @@ -470,7 +477,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } impl<'tcx> intravisit::Visitor<'tcx> for CollectClauses<'_, 'tcx> { - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { if let Some(clauses) = self.fcx.trait_ascriptions.borrow().get(&ty.hir_id.local_id) { self.clauses.extend(clauses.iter().cloned()); @@ -480,7 +487,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let mut clauses = CollectClauses { clauses: vec![], fcx: self }; - clauses.visit_ty(hir_ty); + clauses.visit_ty_unambig(hir_ty); self.tcx.mk_clauses(&clauses.clauses) } @@ -577,11 +584,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Registers obligations that all `args` are well-formed. - pub(crate) fn add_wf_bounds(&self, args: GenericArgsRef<'tcx>, expr: &hir::Expr<'_>) { + pub(crate) fn add_wf_bounds(&self, args: GenericArgsRef<'tcx>, span: Span) { for arg in args.iter().filter(|arg| { matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) }) { - self.register_wf_obligation(arg, expr.span, ObligationCauseCode::WellFormed(None)); + self.register_wf_obligation(arg, span, ObligationCauseCode::WellFormed(None)); } } @@ -791,13 +798,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bug!("`resolve_ty_and_res_fully_qualified_call` called on `LangItem`") } }; + + self.register_wf_obligation( + ty.raw.into(), + qself.span, + ObligationCauseCode::WellFormed(None), + ); + self.select_obligations_where_possible(|_| {}); + if let Some(&cached_result) = self.typeck_results.borrow().type_dependent_defs().get(hir_id) { - self.register_wf_obligation( - ty.raw.into(), - qself.span, - ObligationCauseCode::WellFormed(None), - ); // Return directly on cache hit. This is useful to avoid doubly reporting // errors with default match binding modes. See #44614. let def = cached_result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)); @@ -806,17 +816,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let item_name = item_segment.ident; let result = self .resolve_fully_qualified_call(span, item_name, ty.normalized, qself.span, hir_id) - .map(|r| { - // lint bare trait if the method is found in the trait - if span.edition().at_least_rust_2021() { - self.dcx().try_steal_modify_and_emit_err( - qself.span, - StashKey::TraitMissingMethod, - |_err| {}, - ); - } - r - }) .or_else(|error| { let guar = self .dcx() @@ -828,29 +827,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let trait_missing_method = matches!(error, method::MethodError::NoMatch(_)) && ty.normalized.is_trait(); - // If we have a path like `MyTrait::missing_method`, then don't register - // a WF obligation for `dyn MyTrait` when method lookup fails. Otherwise, - // register a WF obligation so that we can detect any additional - // errors in the self type. - if !trait_missing_method { - self.register_wf_obligation( - ty.raw.into(), - qself.span, - ObligationCauseCode::WellFormed(None), - ); - } - - // Emit the diagnostic for bare traits. (We used to cancel for slightly better - // error messages, but cancelling stashed diagnostics is no longer allowed because - // it causes problems when tracking whether errors have actually occurred.) - if span.edition().at_least_rust_2021() { - self.dcx().try_steal_modify_and_emit_err( - qself.span, - StashKey::TraitMissingMethod, - |_err| {}, - ); - } - if item_name.name != kw::Empty { self.report_method_error( hir_id, @@ -864,14 +840,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { result }); - if result.is_ok() { - self.register_wf_obligation( - ty.raw.into(), - qself.span, - ObligationCauseCode::WellFormed(None), - ); - } - // Write back the new resolution. self.write_resolution(hir_id, result); ( @@ -891,7 +859,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.hir().get_fn_id_for_return_block(blk_id).and_then(|item_id| { match self.tcx.hir_node(item_id) { Node::Item(&hir::Item { - kind: hir::ItemKind::Fn(ref sig, ..), owner_id, .. + kind: hir::ItemKind::Fn { sig, .. }, owner_id, .. }) => Some((owner_id.def_id, sig.decl)), Node::TraitItem(&hir::TraitItem { kind: hir::TraitItemKind::Fn(ref sig, ..), @@ -920,7 +888,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )) => { let (sig, owner_id) = match self.tcx.parent_hir_node(hir_id) { Node::Item(&hir::Item { - kind: hir::ItemKind::Fn(ref sig, ..), + kind: hir::ItemKind::Fn { ref sig, .. }, owner_id, .. }) => (sig, owner_id), @@ -990,11 +958,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut sp = MultiSpan::from_span(path_segment.ident.span); sp.push_span_label( path_segment.ident.span, - format!("this call modifies {} in-place", match rcvr.kind { - ExprKind::Path(QPath::Resolved(None, hir::Path { segments: [segment], .. })) => - format!("`{}`", segment.ident), - _ => "its receiver".to_string(), - }), + format!( + "this call modifies {} in-place", + match rcvr.kind { + ExprKind::Path(QPath::Resolved( + None, + hir::Path { segments: [segment], .. }, + )) => format!("`{}`", segment.ident), + _ => "its receiver".to_string(), + } + ), ); let modifies_rcvr_note = @@ -1039,17 +1012,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { def_id, span, ), + Res::Err => { + return ( + Ty::new_error( + tcx, + tcx.dcx().span_delayed_bug(span, "could not resolve path {:?}"), + ), + res, + ); + } _ => bug!("instantiate_value_path on {:?}", res), }; let mut user_self_ty = None; let mut is_alias_variant_ctor = false; + let mut err_extend = GenericsArgsErrExtend::None; match res { Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) if let Some(self_ty) = self_ty => { let adt_def = self_ty.normalized.ty_adt_def().unwrap(); user_self_ty = Some(UserSelfTy { impl_def_id: adt_def.did(), self_ty: self_ty.raw }); is_alias_variant_ctor = true; + err_extend = GenericsArgsErrExtend::DefVariant(segments); + } + Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) => { + err_extend = GenericsArgsErrExtend::DefVariant(segments); } Res::Def(DefKind::AssocFn | DefKind::AssocConst, def_id) => { let assoc_item = tcx.associated_item(def_id); @@ -1096,7 +1083,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { segments.iter().enumerate().filter_map(|(index, seg)| { if !indices.contains(&index) || is_alias_variant_ctor { Some(seg) } else { None } }), - GenericsArgsErrExtend::None, + err_extend, ); if let Res::Local(hid) = res { @@ -1274,7 +1261,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn provided_kind( &mut self, - _preceding_args: &[ty::GenericArg<'tcx>], param: &ty::GenericParamDef, arg: &GenericArg<'tcx>, ) -> ty::GenericArg<'tcx> { @@ -1285,14 +1271,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .lower_lifetime(lt, RegionInferReason::Param(param)) .into(), (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { - self.fcx.lower_ty(ty).raw.into() - } - (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { - self.fcx.lower_const_arg(ct, FeedConstTy::Param(param.def_id)).into() + // We handle the ambig portions of `Ty` in match arm below + self.fcx.lower_ty(ty.as_unambig_ty()).raw.into() } (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => { - self.fcx.ty_infer(Some(param), inf.span).into() + self.fcx.lower_ty(&inf.to_ty()).raw.into() } + (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => self + .fcx + // Ambiguous parts of `ConstArg` are handled in the match arms below + .lower_const_arg(ct.as_unambig_ct(), FeedConstTy::Param(param.def_id)) + .into(), (&GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { self.fcx.ct_infer(Some(param), inf.span).into() } @@ -1446,7 +1435,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // in a reentrant borrow, causing an ICE. let result = self .at(&self.misc(sp), self.param_env) - .structurally_normalize(ty, &mut **self.fulfillment_cx.borrow_mut()); + .structurally_normalize_ty(ty, &mut **self.fulfillment_cx.borrow_mut()); match result { Ok(normalized_ty) => normalized_ty, Err(errors) => { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index d02e669e790f5..77081548d1156 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1,11 +1,10 @@ -use std::{iter, mem}; +use std::{fmt, iter, mem}; use itertools::Itertools; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::codes::*; use rustc_errors::{ - Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey, a_or_an, - display_list_with_comma_and, pluralize, + Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey, a_or_an, listify, pluralize, }; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; @@ -22,10 +21,11 @@ use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::Session; -use rustc_span::{DUMMY_SP, Ident, Span, kw, sym}; +use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use rustc_trait_selection::error_reporting::infer::{FailureCode, ObligationCauseExt}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt, SelectionContext}; +use smallvec::SmallVec; use tracing::debug; use {rustc_ast as ast, rustc_hir as hir}; @@ -45,6 +45,12 @@ use crate::{ struct_span_code_err, }; +rustc_index::newtype_index! { + #[orderable] + #[debug_format = "GenericIdx({})"] + pub(crate) struct GenericIdx {} +} + #[derive(Clone, Copy, Default)] pub(crate) enum DivergingBlockBehavior { /// This is the current stable behavior: @@ -127,7 +133,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Err(guar) => Err(guar), }; if let Err(guar) = has_error { - let err_inputs = self.err_args(args_no_rcvr.len(), guar); + let err_inputs = self.err_args( + method.map_or(args_no_rcvr.len(), |method| method.sig.inputs().len() - 1), + guar, + ); let err_output = Ty::new_error(self.tcx, guar); let err_inputs = match tuple_arguments { @@ -1147,6 +1156,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(self.param_env.and(trace.values)), e, true, + None, ); } } @@ -1210,7 +1220,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // - foo((), "current", 42u32, "next") // + foo((), 42u32) { - prev_extra_idx.map_or(true, |prev_extra_idx| { + prev_extra_idx.is_none_or(|prev_extra_idx| { prev_extra_idx + 1 == arg_idx.index() }) } @@ -1616,6 +1626,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => { let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() { ty::Int(_) | ty::Uint(_) => Some(ty), + // These exist to direct casts like `0x61 as char` to use + // the right integer type to cast from, instead of falling back to + // i32 due to no further constraints. ty::Char => Some(tcx.types.u8), ty::RawPtr(..) => Some(tcx.types.usize), ty::FnDef(..) | ty::FnPtr(..) => Some(tcx.types.usize), @@ -2041,7 +2054,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn parent_item_span(&self, id: HirId) -> Option { let node = self.tcx.hir_node_by_def_id(self.tcx.hir().get_parent_item(id).def_id); match node { - Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) + Node::Item(&hir::Item { kind: hir::ItemKind::Fn { body: body_id, .. }, .. }) | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body_id), .. }) => { let body = self.tcx.hir().body(body_id); if let ExprKind::Block(block, _) = &body.value.kind { @@ -2189,7 +2202,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } hir::Node::Item(item) => { - if let hir::ItemKind::Fn(..) = item.kind { + if let hir::ItemKind::Fn { .. } = item.kind { break; } } @@ -2285,7 +2298,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If we're calling a method of a Fn/FnMut/FnOnce trait object implicitly // (eg invoking a closure) we want to point at the underlying callable, // not the method implicitly invoked (eg call_once). - if let Some(assoc_item) = self.tcx.opt_associated_item(def_id) + // TupleArguments is set only when this is an implicit call (my_closure(...)) rather than explicit (my_closure.call(...)) + if tuple_arguments == TupleArguments + && let Some(assoc_item) = self.tcx.opt_associated_item(def_id) // Since this is an associated item, it might point at either an impl or a trait item. // We want it to always point to the trait item. // If we're pointing at an inherent function, we don't need to do anything, @@ -2295,8 +2310,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Just an easy way to check "trait_def_id == Fn/FnMut/FnOnce" && let Some(call_kind) = self.tcx.fn_trait_kind_from_def_id(maybe_trait_def_id) && let Some(callee_ty) = callee_ty - // TupleArguments is set only when this is an implicit call (my_closure(...)) rather than explicit (my_closure.call(...)) - && tuple_arguments == TupleArguments { let callee_ty = callee_ty.peel_refs(); match *callee_ty.kind() { @@ -2365,171 +2378,136 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && !def_span.is_dummy() { let mut spans: MultiSpan = def_span.into(); - - if let Some(params_with_generics) = self.get_hir_params_with_generics(def_id, is_method) + if let Some((params_with_generics, hir_generics)) = + self.get_hir_param_info(def_id, is_method) { - debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); - - let mut generics_with_unmatched_params = Vec::new(); - - let check_for_matched_generics = || { - if matched_inputs.iter().any(|x| x.is_some()) - && params_with_generics.iter().any(|x| x.1.is_some()) - { - for &(idx, generic, _) in ¶ms_with_generics { - // Param has to have a generic and be matched to be relevant - if matched_inputs[idx.into()].is_none() { - continue; - } - - let Some(generic) = generic else { - continue; - }; + struct MismatchedParam<'a> { + idx: ExpectedIdx, + generic: GenericIdx, + param: &'a FnParam<'a>, + deps: SmallVec<[ExpectedIdx; 4]>, + } - for unmatching_idx in idx + 1..params_with_generics.len() { - if matched_inputs[unmatching_idx.into()].is_none() - && let Some(unmatched_idx_param_generic) = - params_with_generics[unmatching_idx].1 - && unmatched_idx_param_generic.name.ident() - == generic.name.ident() - { - // We found a parameter that didn't match that needed to - return true; - } - } - } + debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); + // Gather all mismatched parameters with generics. + let mut mismatched_params = Vec::>::new(); + if let Some(expected_idx) = expected_idx { + let expected_idx = ExpectedIdx::from_usize(expected_idx); + let &(expected_generic, ref expected_param) = + ¶ms_with_generics[expected_idx]; + if let Some(expected_generic) = expected_generic { + mismatched_params.push(MismatchedParam { + idx: expected_idx, + generic: expected_generic, + param: expected_param, + deps: SmallVec::new(), + }); + } else { + // Still mark the mismatched parameter + spans.push_span_label(expected_param.span(), ""); } - false - }; - - let check_for_matched_generics = check_for_matched_generics(); - - for &(idx, generic_param, param) in - params_with_generics.iter().filter(|&(idx, _, _)| { - check_for_matched_generics - || expected_idx.is_none_or(|expected_idx| expected_idx == *idx) - }) - { - let Some(generic_param) = generic_param else { - spans.push_span_label(param.span, ""); - continue; - }; - - let other_params_matched: Vec<(usize, &hir::Param<'_>)> = params_with_generics - .iter() - .filter(|(other_idx, other_generic_param, _)| { - if *other_idx == idx { - return false; - } - let Some(other_generic_param) = other_generic_param else { - return false; - }; - if matched_inputs[idx.into()].is_none() - && matched_inputs[(*other_idx).into()].is_none() - { - return false; - } - if matched_inputs[idx.into()].is_some() - && matched_inputs[(*other_idx).into()].is_some() - { - return false; - } - other_generic_param.name.ident() == generic_param.name.ident() - }) - .map(|&(other_idx, _, other_param)| (other_idx, other_param)) - .collect(); - - if !other_params_matched.is_empty() { - let other_param_matched_names: Vec = other_params_matched - .iter() - .map(|(idx, other_param)| { - if let hir::PatKind::Binding(_, _, ident, _) = other_param.pat.kind - { - format!("`{ident}`") + } else { + mismatched_params.extend( + params_with_generics.iter_enumerated().zip(matched_inputs).filter_map( + |((idx, &(generic, ref param)), matched_idx)| { + if matched_idx.is_some() { + None + } else if let Some(generic) = generic { + Some(MismatchedParam { + idx, + generic, + param, + deps: SmallVec::new(), + }) } else { - format!("parameter #{}", idx + 1) + // Still mark mismatched parameters + spans.push_span_label(param.span(), ""); + None } - }) - .collect(); + }, + ), + ); + } - let matched_ty = self - .resolve_vars_if_possible(formal_and_expected_inputs[idx.into()].1) - .sort_string(self.tcx); + if !mismatched_params.is_empty() { + // For each mismatched paramter, create a two-way link to each matched parameter + // of the same type. + let mut dependants = IndexVec::::from_fn_n( + |_| SmallVec::<[u32; 4]>::new(), + params_with_generics.len(), + ); + let mut generic_uses = IndexVec::::from_fn_n( + |_| SmallVec::<[ExpectedIdx; 4]>::new(), + hir_generics.params.len(), + ); + for (idx, param) in mismatched_params.iter_mut().enumerate() { + for ((other_idx, &(other_generic, _)), &other_matched_idx) in + params_with_generics.iter_enumerated().zip(matched_inputs) + { + if other_generic == Some(param.generic) && other_matched_idx.is_some() { + generic_uses[param.generic].extend([param.idx, other_idx]); + dependants[other_idx].push(idx as u32); + param.deps.push(other_idx); + } + } + } - if matched_inputs[idx.into()].is_some() { + // Highlight each mismatched type along with a note about which other parameters + // the type depends on (if any). + for param in &mismatched_params { + if let Some(deps_list) = listify(¶m.deps, |&dep| { + params_with_generics[dep].1.display(dep.as_usize()).to_string() + }) { spans.push_span_label( - param.span, + param.param.span(), format!( - "{} {} to match the {} type of this parameter", - display_list_with_comma_and(&other_param_matched_names), - format!( - "need{}", - pluralize!(if other_param_matched_names.len() == 1 { - 0 - } else { - 1 - }) - ), - matched_ty, + "this parameter needs to match the {} type of {deps_list}", + self.resolve_vars_if_possible( + formal_and_expected_inputs[param.deps[0]].1 + ) + .sort_string(self.tcx), ), ); } else { + // Still mark mismatched parameters + spans.push_span_label(param.param.span(), ""); + } + } + // Highligh each parameter being depended on for a generic type. + for ((&(_, param), deps), &(_, expected_ty)) in + params_with_generics.iter().zip(&dependants).zip(formal_and_expected_inputs) + { + if let Some(deps_list) = listify(deps, |&dep| { + let param = &mismatched_params[dep as usize]; + param.param.display(param.idx.as_usize()).to_string() + }) { spans.push_span_label( - param.span, + param.span(), format!( - "this parameter needs to match the {} type of {}", - matched_ty, - display_list_with_comma_and(&other_param_matched_names), + "{deps_list} need{} to match the {} type of this parameter", + pluralize!((deps.len() != 1) as u32), + self.resolve_vars_if_possible(expected_ty) + .sort_string(self.tcx), ), ); } - generics_with_unmatched_params.push(generic_param); - } else { - spans.push_span_label(param.span, ""); } - } - - for generic_param in self - .tcx - .hir() - .get_if_local(def_id) - .and_then(|node| node.generics()) - .into_iter() - .flat_map(|x| x.params) - .filter(|x| { - generics_with_unmatched_params - .iter() - .any(|y| x.name.ident() == y.name.ident()) - }) - { - let param_idents_matching: Vec = params_with_generics - .iter() - .filter(|(_, generic, _)| { - if let Some(generic) = generic { - generic.name.ident() == generic_param.name.ident() - } else { - false - } - }) - .map(|(idx, _, param)| { - if let hir::PatKind::Binding(_, _, ident, _) = param.pat.kind { - format!("`{ident}`") - } else { - format!("parameter #{}", idx + 1) - } - }) - .collect(); - - if !param_idents_matching.is_empty() { - spans.push_span_label( - generic_param.span, - format!( - "{} {} reference this parameter `{}`", - display_list_with_comma_and(¶m_idents_matching), - if param_idents_matching.len() == 2 { "both" } else { "all" }, - generic_param.name.ident().name, - ), - ); + // Highlight each generic parameter in use. + for (param, uses) in hir_generics.params.iter().zip(&mut generic_uses) { + uses.sort(); + uses.dedup(); + if let Some(param_list) = listify(uses, |&idx| { + params_with_generics[idx].1.display(idx.as_usize()).to_string() + }) { + spans.push_span_label( + param.span, + format!( + "{param_list} {} reference this parameter `{}`", + if uses.len() == 2 { "both" } else { "all" }, + param.name.ident().name, + ), + ); + } } } } @@ -2605,14 +2583,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; }; - if let Some(params_with_generics) = self.get_hir_params_with_generics(def_id, is_method) { + if let Some((params_with_generics, _)) = self.get_hir_param_info(def_id, is_method) { debug_assert_eq!(params_with_generics.len(), matched_inputs.len()); - for &(idx, generic_param, _) in ¶ms_with_generics { - if matched_inputs[idx.into()].is_none() { + for (idx, (generic_param, _)) in params_with_generics.iter_enumerated() { + if matched_inputs[idx].is_none() { continue; } - let Some((_, matched_arg_span)) = provided_arg_tys.get(idx.into()) else { + let Some((_, matched_arg_span)) = provided_arg_tys.get(idx.to_provided_idx()) + else { continue; }; @@ -2620,32 +2599,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { continue; }; - let mut idxs_matched: Vec = vec![]; - for &(other_idx, _, _) in - params_with_generics.iter().filter(|&&(other_idx, other_generic_param, _)| { + let idxs_matched = params_with_generics + .iter_enumerated() + .filter(|&(other_idx, (other_generic_param, _))| { if other_idx == idx { return false; } let Some(other_generic_param) = other_generic_param else { return false; }; - if matched_inputs[other_idx.into()].is_some() { + if matched_inputs[other_idx].is_some() { return false; } - other_generic_param.name.ident() == generic_param.name.ident() + other_generic_param == generic_param }) - { - idxs_matched.push(other_idx); - } + .count(); - if idxs_matched.is_empty() { + if idxs_matched == 0 { continue; } let expected_display_type = self .resolve_vars_if_possible(formal_and_expected_inputs[idx.into()].1) .sort_string(self.tcx); - let label = if idxs_matched.len() == params_with_generics.len() - 1 { + let label = if idxs_matched == params_with_generics.len() - 1 { format!( "expected all arguments to be this {} type because they need to match the type of this parameter", expected_display_type @@ -2664,62 +2641,79 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Returns the parameters of a function, with their generic parameters if those are the full - /// type of that parameter. Returns `None` if the function body is unavailable (eg is an instrinsic). - fn get_hir_params_with_generics( + /// type of that parameter. + /// + /// Returns `None` if the body is not a named function (e.g. a closure). + fn get_hir_param_info( &self, def_id: DefId, is_method: bool, - ) -> Option>, &hir::Param<'_>)>> { - let fn_node = self.tcx.hir().get_if_local(def_id)?; - let fn_decl = fn_node.fn_decl()?; - - let generic_params: Vec>> = fn_decl - .inputs - .into_iter() - .skip(if is_method { 1 } else { 0 }) - .map(|param| { - if let hir::TyKind::Path(QPath::Resolved( - _, - hir::Path { res: Res::Def(_, res_def_id), .. }, - )) = param.kind - { - fn_node - .generics() - .into_iter() - .flat_map(|generics| generics.params) - .find(|param| ¶m.def_id.to_def_id() == res_def_id) - } else { - None - } + ) -> Option<(IndexVec, FnParam<'_>)>, &hir::Generics<'_>)> + { + let (sig, generics, body_id, param_names) = match self.tcx.hir().get_if_local(def_id)? { + hir::Node::TraitItem(&hir::TraitItem { + generics, + kind: hir::TraitItemKind::Fn(sig, trait_fn), + .. + }) => match trait_fn { + hir::TraitFn::Required(params) => (sig, generics, None, Some(params)), + hir::TraitFn::Provided(body) => (sig, generics, Some(body), None), + }, + hir::Node::ImplItem(&hir::ImplItem { + generics, + kind: hir::ImplItemKind::Fn(sig, body), + .. }) - .collect(); - - let mut params: Vec<&hir::Param<'_>> = self - .tcx - .hir() - .body(fn_node.body_id()?) - .params - .into_iter() - .skip(if is_method { 1 } else { 0 }) - .collect(); - - // The surrounding code expects variadic functions to not have a parameter representing - // the "..." parameter. This is already true of the FnDecl but not of the body params, so - // we drop it if it exists. + | hir::Node::Item(&hir::Item { + kind: hir::ItemKind::Fn { sig, generics, body, .. }, + .. + }) => (sig, generics, Some(body), None), + hir::Node::ForeignItem(&hir::ForeignItem { + kind: hir::ForeignItemKind::Fn(sig, params, generics), + .. + }) => (sig, generics, None, Some(params)), + _ => return None, + }; - if fn_decl.c_variadic { - params.pop(); + // Make sure to remove both the receiver and variadic argument. Both are removed + // when matching parameter types. + let fn_inputs = sig.decl.inputs.get(is_method as usize..)?.iter().map(|param| { + if let hir::TyKind::Path(QPath::Resolved( + _, + &hir::Path { res: Res::Def(_, res_def_id), .. }, + )) = param.kind + { + generics + .params + .iter() + .position(|param| param.def_id.to_def_id() == res_def_id) + .map(GenericIdx::from_usize) + } else { + None + } + }); + match (body_id, param_names) { + (Some(_), Some(_)) | (None, None) => unreachable!(), + (Some(body), None) => { + let params = self.tcx.hir().body(body).params; + let params = + params.get(is_method as usize..params.len() - sig.decl.c_variadic as usize)?; + debug_assert_eq!(params.len(), fn_inputs.len()); + Some(( + fn_inputs.zip(params.iter().map(|param| FnParam::Param(param))).collect(), + generics, + )) + } + (None, Some(params)) => { + let params = + params.get(is_method as usize..params.len() - sig.decl.c_variadic as usize)?; + debug_assert_eq!(params.len(), fn_inputs.len()); + Some(( + fn_inputs.zip(params.iter().map(|param| FnParam::Name(param))).collect(), + generics, + )) + } } - - debug_assert_eq!(params.len(), generic_params.len()); - Some( - generic_params - .into_iter() - .zip(params) - .enumerate() - .map(|(a, (b, c))| (a, b, c)) - .collect(), - ) } } @@ -2742,3 +2736,41 @@ impl<'tcx> Visitor<'tcx> for FindClosureArg<'tcx> { hir::intravisit::walk_expr(self, ex); } } + +#[derive(Clone, Copy)] +enum FnParam<'hir> { + Param(&'hir hir::Param<'hir>), + Name(&'hir Ident), +} +impl FnParam<'_> { + fn span(&self) -> Span { + match self { + Self::Param(x) => x.span, + Self::Name(x) => x.span, + } + } + + fn name(&self) -> Option { + match self { + Self::Param(x) if let hir::PatKind::Binding(_, _, ident, _) = x.pat.kind => { + Some(ident.name) + } + Self::Name(x) if x.name != kw::Empty => Some(x.name), + _ => None, + } + } + + fn display(&self, idx: usize) -> impl '_ + fmt::Display { + struct D<'a>(FnParam<'a>, usize); + impl fmt::Display for D<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(name) = self.0.name() { + write!(f, "`{name}`") + } else { + write!(f, "parameter #{}", self.1 + 1) + } + } + } + D(*self, idx) + } +} diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs index eb5fe3a86e4c5..dc10b53fd8390 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -7,6 +7,7 @@ use rustc_span::Span; use rustc_trait_selection::solve::inspect::{ InspectConfig, InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor, }; +use rustc_type_ir::solve::GoalSource; use tracing::{debug, instrument, trace}; use crate::FnCtxt; @@ -119,7 +120,21 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'a, 'tcx> { fn visit_goal(&mut self, inspect_goal: &InspectGoal<'_, 'tcx>) { let tcx = self.fcx.tcx; let goal = inspect_goal.goal(); - if self.fcx.predicate_has_self_ty(goal.predicate, self.self_ty) { + if self.fcx.predicate_has_self_ty(goal.predicate, self.self_ty) + // We do not push the instantiated forms of goals as it would cause any + // aliases referencing bound vars to go from having escaping bound vars to + // being able to be normalized to an inference variable. + // + // This is mostly just a hack as arbitrary nested goals could still contain + // such aliases while having a different `GoalSource`. Closure signature inference + // however can't really handle *every* higher ranked `Fn` goal also being present + // in the form of `?c: Fn<(>::Assoc)`. + // + // This also just better matches the behaviour of the old solver where we do not + // encounter instantiated forms of goals, only nested goals that referred to bound + // vars from instantiated goals. + && !matches!(inspect_goal.source(), GoalSource::InstantiateHigherRanked) + { self.obligations_for_self_ty.push(traits::Obligation::new( tcx, self.root_cause.clone(), diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index d432199f03735..b77e6de52ff1c 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -31,12 +31,12 @@ use crate::{CoroutineTypes, Diverges, EnclosingBreakables, TypeckRootCtxt}; /// functions, closures, and `const`s, including performing type inference /// with [`InferCtxt`]. /// -/// This is in contrast to [`ItemCtxt`], which is used to type-check item *signatures* -/// and thus does not perform type inference. +/// This is in contrast to `rustc_hir_analysis::collect::ItemCtxt`, which is +/// used to type-check item *signatures* and thus does not perform type +/// inference. /// -/// See [`ItemCtxt`]'s docs for more. +/// See `ItemCtxt`'s docs for more. /// -/// [`ItemCtxt`]: rustc_hir_analysis::collect::ItemCtxt /// [`InferCtxt`]: infer::InferCtxt pub(crate) struct FnCtxt<'a, 'tcx> { pub(super) body_id: LocalDefId, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 964ef5b2106b1..4ec8d0b1f60a2 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -4,17 +4,16 @@ use core::iter; use hir::def_id::LocalDefId; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::packed::Pu128; -use rustc_errors::{Applicability, Diag, MultiSpan}; -use rustc_hir as hir; +use rustc_errors::{Applicability, Diag, MultiSpan, listify}; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ - Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId, - Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicateKind, expr_needs_parens, + self as hir, Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, + GenericBound, HirId, Node, PatExpr, PatExprKind, Path, QPath, Stmt, StmtKind, TyKind, + WherePredicateKind, expr_needs_parens, }; -use rustc_hir_analysis::collect::suggest_impl_trait; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; -use rustc_middle::lint::in_external_macro; +use rustc_hir_analysis::suggest_impl_trait; use rustc_middle::middle::stability::EvalResult; use rustc_middle::span_bug; use rustc_middle::ty::print::with_no_trimmed_paths; @@ -185,6 +184,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_ty: Ty<'tcx>, can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool, ) -> bool { + if lhs_expr.span.in_derive_expansion() || rhs_expr.span.in_derive_expansion() { + return false; + } let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_ty) else { return false; }; @@ -767,7 +769,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the expression is from an external macro, then do not suggest // adding a semicolon, because there's nowhere to put it. // See issue #81943. - && !in_external_macro(self.tcx.sess, expression.span) => + && !expression.span.in_external_macro(self.tcx.sess.source_map()) => { if needs_block { err.multipart_suggestion( @@ -981,14 +983,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let hir::Node::Item(hir::Item { kind: - hir::ItemKind::Fn( - hir::FnSig { - decl: hir::FnDecl { inputs: fn_parameters, output: fn_return, .. }, - .. - }, - hir::Generics { params, predicates, .. }, - _body_id, - ), + hir::ItemKind::Fn { + sig: + hir::FnSig { + decl: hir::FnDecl { inputs: fn_parameters, output: fn_return, .. }, + .. + }, + generics: hir::Generics { params, predicates, .. }, + .. + }, .. }) = fn_node else { @@ -1142,7 +1145,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.may_coerce(found, ty) } hir::FnRetTy::DefaultReturn(_) if in_closure => { - self.ret_coercion.as_ref().map_or(false, |ret| { + self.ret_coercion.as_ref().is_some_and(|ret| { let ret_ty = ret.borrow().expected_ty(); self.may_coerce(found, ret_ty) }) @@ -1319,14 +1322,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let span = expr.span.shrink_to_hi(); let subdiag = if self.type_is_copy_modulo_regions(self.param_env, ty) { errors::OptionResultRefMismatch::Copied { span, def_path } - } else if let Some(clone_did) = self.tcx.lang_items().clone_trait() - && rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions( - self, - self.param_env, - ty, - clone_did, - ) - { + } else if self.type_is_clone_modulo_regions(self.param_env, ty) { errors::OptionResultRefMismatch::Cloned { span, def_path } } else { return false; @@ -1425,8 +1421,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // since the user probably just misunderstood how `let else` // and `&&` work together. if let Some((_, hir::Node::LetStmt(local))) = cond_parent - && let hir::PatKind::Path(qpath) | hir::PatKind::TupleStruct(qpath, _, _) = - &local.pat.kind + && let hir::PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. }) + | hir::PatKind::TupleStruct(qpath, _, _) = &local.pat.kind && let hir::QPath::Resolved(None, path) = qpath && let Some(did) = path .res @@ -1790,14 +1786,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let results = self.typeck_results.borrow(); // First, look for a `Clone::clone` call if segment.ident.name == sym::clone - && results.type_dependent_def_id(expr.hir_id).map_or( - false, - |did| { + && results.type_dependent_def_id(expr.hir_id).is_some_and(|did| { let assoc_item = self.tcx.associated_item(did); assoc_item.container == ty::AssocItemContainer::Trait && assoc_item.container_id(self.tcx) == clone_trait_did - }, - ) + }) // If that clone call hasn't already dereferenced the self type (i.e. don't give this // diagnostic in cases where we have `(&&T).clone()` and we expect `T`). && !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..))) @@ -1842,16 +1835,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error.obligation.predicate, )); } - [errors @ .., last] => { + _ => { diag.help(format!( "`Clone` is not implemented because the following trait bounds \ - could not be satisfied: {} and `{}`", - errors - .iter() - .map(|e| format!("`{}`", e.obligation.predicate)) - .collect::>() - .join(", "), - last.obligation.predicate, + could not be satisfied: {}", + listify(&errors, |e| format!("`{}`", e.obligation.predicate)) + .unwrap(), )); } } @@ -1999,17 +1988,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr.kind, hir::ExprKind::Call( hir::Expr { - kind: hir::ExprKind::Path(hir::QPath::Resolved(None, hir::Path { - res: Res::Def(hir::def::DefKind::Ctor(_, _), _), - .. - },)), + kind: hir::ExprKind::Path(hir::QPath::Resolved( + None, + hir::Path { res: Res::Def(hir::def::DefKind::Ctor(_, _), _), .. }, + )), .. }, .., - ) | hir::ExprKind::Path(hir::QPath::Resolved(None, hir::Path { - res: Res::Def(hir::def::DefKind::Ctor(_, _), _), - .. - },)), + ) | hir::ExprKind::Path(hir::QPath::Resolved( + None, + hir::Path { res: Res::Def(hir::def::DefKind::Ctor(_, _), _), .. }, + )), ); let (article, kind, variant, sugg_operator) = @@ -2181,6 +2170,87 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Suggest replacing comma with semicolon in incorrect repeat expressions + /// like `["_", 10]` or `vec![String::new(), 10]`. + pub(crate) fn suggest_semicolon_in_repeat_expr( + &self, + err: &mut Diag<'_>, + expr: &hir::Expr<'_>, + expr_ty: Ty<'tcx>, + ) -> bool { + // Check if `expr` is contained in array of two elements + if let hir::Node::Expr(array_expr) = self.tcx.parent_hir_node(expr.hir_id) + && let hir::ExprKind::Array(elements) = array_expr.kind + && let [first, second] = &elements[..] + && second.hir_id == expr.hir_id + { + // Span between the two elements of the array + let comma_span = first.span.between(second.span); + + // Check if `expr` is a constant value of type `usize`. + // This can only detect const variable declarations and + // calls to const functions. + + // Checking this here instead of rustc_hir::hir because + // this check needs access to `self.tcx` but rustc_hir + // has no access to `TyCtxt`. + let expr_is_const_usize = expr_ty.is_usize() + && match expr.kind { + ExprKind::Path(QPath::Resolved( + None, + Path { res: Res::Def(DefKind::Const, _), .. }, + )) => true, + ExprKind::Call( + Expr { + kind: + ExprKind::Path(QPath::Resolved( + None, + Path { res: Res::Def(DefKind::Fn, fn_def_id), .. }, + )), + .. + }, + _, + ) => self.tcx.is_const_fn(*fn_def_id), + _ => false, + }; + + // Type of the first element is guaranteed to be checked + // when execution reaches here because `mismatched types` + // error occurs only when type of second element of array + // is not the same as type of first element. + let first_ty = self.typeck_results.borrow().expr_ty(first); + + // `array_expr` is from a macro `vec!["a", 10]` if + // 1. array expression's span is imported from a macro + // 2. first element of array implements `Clone` trait + // 3. second element is an integer literal or is an expression of `usize` like type + if self.tcx.sess.source_map().is_imported(array_expr.span) + && self.type_is_clone_modulo_regions(self.param_env, first_ty) + && (expr.is_size_lit() || expr_ty.is_usize_like()) + { + err.subdiagnostic(errors::ReplaceCommaWithSemicolon { + comma_span, + descr: "a vector", + }); + return true; + } + + // `array_expr` is from an array `["a", 10]` if + // 1. first element of array implements `Copy` trait + // 2. second element is an integer literal or is a const value of type `usize` + if self.type_is_copy_modulo_regions(self.param_env, first_ty) + && (expr.is_size_lit() || expr_is_const_usize) + { + err.subdiagnostic(errors::ReplaceCommaWithSemicolon { + comma_span, + descr: "an array", + }); + return true; + } + } + false + } + /// If the expected type is an enum (Issue #55250) with any variants whose /// sole field is of the found type, suggest such variants. (Issue #42764) pub(crate) fn suggest_compatible_variants( @@ -2190,7 +2260,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, expr_ty: Ty<'tcx>, ) -> bool { - if in_external_macro(self.tcx.sess, expr.span) { + if expr.span.in_external_macro(self.tcx.sess.source_map()) { return false; } if let ty::Adt(expected_adt, args) = expected.kind() { @@ -2518,14 +2588,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )> { let sess = self.sess(); let sp = expr.span; + let sm = sess.source_map(); // If the span is from an external macro, there's no suggestion we can make. - if in_external_macro(sess, sp) { + if sp.in_external_macro(sm) { return None; } - let sm = sess.source_map(); - let replace_prefix = |s: &str, old: &str, new: &str| { s.strip_prefix(old).map(|stripped| new.to_string() + stripped) }; @@ -2607,6 +2676,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let hir::ExprKind::Unary(hir::UnOp::Deref, inner) = expr.kind && let Some(1) = self.deref_steps_for_suggestion(expected, checked_ty) + && self.typeck_results.borrow().expr_ty(inner).is_ref() { // We have `*&T`, check if what was expected was `&T`. // If so, we may want to suggest removing a `*`. diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs index 789530d35dd81..54e9e699353fa 100644 --- a/compiler/rustc_hir_typeck/src/intrinsicck.rs +++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs @@ -40,13 +40,25 @@ fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + /// FIXME: Move this check out of typeck, since it'll easily cycle when revealing opaques, + /// and we shouldn't need to check anything here if the typeck results are tainted. pub(crate) fn check_transmute(&self, from: Ty<'tcx>, to: Ty<'tcx>, hir_id: HirId) { let tcx = self.tcx; let dl = &tcx.data_layout; let span = tcx.hir().span(hir_id); let normalize = |ty| { let ty = self.resolve_vars_if_possible(ty); - self.tcx.normalize_erasing_regions(self.typing_env(self.param_env), ty) + if let Ok(ty) = + self.tcx.try_normalize_erasing_regions(self.typing_env(self.param_env), ty) + { + ty + } else { + Ty::new_error_with_message( + tcx, + span, + "tried to normalize non-wf type in check_transmute", + ) + } }; let from = normalize(from); let to = normalize(to); @@ -110,7 +122,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("generic size {size}") } } - Err(LayoutError::Unknown(bad)) => { + Err(LayoutError::TooGeneric(bad)) => { if *bad == ty { "this type does not have a fixed size".to_owned() } else { diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 91ea34eb54d35..07e013e4afa67 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -87,21 +87,7 @@ fn used_trait_imports(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &UnordSet(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> { - let fallback = move || tcx.type_of(def_id.to_def_id()).instantiate_identity(); - typeck_with_fallback(tcx, def_id, fallback, None) -} - -/// Used only to get `TypeckResults` for type inference during error recovery. -/// Currently only used for type inference of `static`s and `const`s to avoid type cycle errors. -fn diagnostic_only_typeck<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, -) -> &'tcx ty::TypeckResults<'tcx> { - let fallback = move || { - let span = tcx.hir().span(tcx.local_def_id_to_hir_id(def_id)); - Ty::new_error_with_message(tcx, span, "diagnostic only typeck table used") - }; - typeck_with_fallback(tcx, def_id, fallback, None) + typeck_with_inspect(tcx, def_id, None) } /// Same as `typeck` but `inspect` is invoked on evaluation of each root obligation. @@ -113,15 +99,13 @@ pub fn inspect_typeck<'tcx>( def_id: LocalDefId, inspect: ObligationInspector<'tcx>, ) -> &'tcx ty::TypeckResults<'tcx> { - let fallback = move || tcx.type_of(def_id.to_def_id()).instantiate_identity(); - typeck_with_fallback(tcx, def_id, fallback, Some(inspect)) + typeck_with_inspect(tcx, def_id, Some(inspect)) } -#[instrument(level = "debug", skip(tcx, fallback, inspector), ret)] -fn typeck_with_fallback<'tcx>( +#[instrument(level = "debug", skip(tcx, inspector), ret)] +fn typeck_with_inspect<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, - fallback: impl Fn() -> Ty<'tcx> + 'tcx, inspector: Option>, ) -> &'tcx ty::TypeckResults<'tcx> { // Closures' typeck results come from their outermost function, @@ -150,8 +134,12 @@ fn typeck_with_fallback<'tcx>( let mut fcx = FnCtxt::new(&root_ctxt, param_env, def_id); if let Some(hir::FnSig { header, decl, .. }) = node.fn_sig() { - let fn_sig = if decl.output.get_infer_ret_ty().is_some() { - fcx.lowerer().lower_fn_ty(id, header.safety, header.abi, decl, None, None) + let fn_sig = if decl.output.is_suggestable_infer_ty().is_some() { + // In the case that we're recovering `fn() -> W<_>` or some other return + // type that has an infer in it, lower the type directly so that it'll + // be correctly filled with infer. We'll use this inference to provide + // a suggestion later on. + fcx.lowerer().lower_fn_ty(id, header.safety(), header.abi, decl, None, None) } else { tcx.fn_sig(def_id).instantiate_identity() }; @@ -159,13 +147,39 @@ fn typeck_with_fallback<'tcx>( check_abi(tcx, span, fn_sig.abi()); // Compute the function signature from point of view of inside the fn. - let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig); - let fn_sig = fcx.normalize(body.value.span, fn_sig); + let mut fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig); + + // Normalize the input and output types one at a time, using a different + // `WellFormedLoc` for each. We cannot call `normalize_associated_types` + // on the entire `FnSig`, since this would use the same `WellFormedLoc` + // for each type, preventing the HIR wf check from generating + // a nice error message. + let arg_span = + |idx| decl.inputs.get(idx).map_or(decl.output.span(), |arg: &hir::Ty<'_>| arg.span); + + fn_sig.inputs_and_output = tcx.mk_type_list_from_iter( + fn_sig + .inputs_and_output + .iter() + .enumerate() + .map(|(idx, ty)| fcx.normalize(arg_span(idx), ty)), + ); check_fn(&mut fcx, fn_sig, None, decl, def_id, body, tcx.features().unsized_fn_params()); } else { - let expected_type = infer_type_if_missing(&fcx, node); - let expected_type = expected_type.unwrap_or_else(fallback); + let expected_type = if let Some(infer_ty) = infer_type_if_missing(&fcx, node) { + infer_ty + } else if let Some(ty) = node.ty() + && ty.is_suggestable_infer_ty() + { + // In the case that we're recovering `const X: [T; _]` or some other + // type that has an infer in it, lower the type directly so that it'll + // be correctly filled with infer. We'll use this inference to provide + // a suggestion later on. + fcx.lowerer().lower_ty(ty) + } else { + tcx.type_of(def_id).instantiate_identity() + }; let expected_type = fcx.normalize(body.value.span, expected_type); @@ -237,7 +251,8 @@ fn typeck_with_fallback<'tcx>( fn infer_type_if_missing<'tcx>(fcx: &FnCtxt<'_, 'tcx>, node: Node<'tcx>) -> Option> { let tcx = fcx.tcx; let def_id = fcx.body_id; - let expected_type = if let Some(&hir::Ty { kind: hir::TyKind::Infer, span, .. }) = node.ty() { + let expected_type = if let Some(&hir::Ty { kind: hir::TyKind::Infer(()), span, .. }) = node.ty() + { if let Some(item) = tcx.opt_associated_item(def_id.into()) && let ty::AssocKind::Const = item.kind && let ty::AssocItemContainer::Impl = item.container @@ -438,7 +453,7 @@ fn report_unexpected_variant_res( ); let fields = fields .iter() - .map(|field| format!("{}: _", field.name.to_ident_string())) + .map(|field| format!("{}: _", field.ident(tcx))) .collect::>() .join(", "); let sugg = format!(" {{ {} }}", fields); @@ -506,5 +521,5 @@ fn fatally_break_rust(tcx: TyCtxt<'_>, span: Span) -> ! { pub fn provide(providers: &mut Providers) { method::provide(providers); - *providers = Providers { typeck, diagnostic_only_typeck, used_trait_imports, ..*providers }; + *providers = Providers { typeck, used_trait_imports, ..*providers }; } diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 0c93c9817b467..8fb425ff0c94c 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -10,6 +10,7 @@ use rustc_hir_analysis::hir_ty_lowering::{ FeedConstTy, GenericArgsLowerer, HirTyLowerer, IsMethodCall, RegionInferReason, }; use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk}; +use rustc_lint::builtin::SUPERTRAIT_ITEM_SHADOWING_USAGE; use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext}; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion, @@ -24,6 +25,7 @@ use rustc_trait_selection::traits; use tracing::debug; use super::{MethodCallee, probe}; +use crate::errors::{SupertraitItemShadowee, SupertraitItemShadower, SupertraitItemShadowing}; use crate::{FnCtxt, callee}; struct ConfirmContext<'a, 'tcx> { @@ -141,7 +143,10 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { let method_sig = ty::Binder::dummy(method_sig); // Make sure nobody calls `drop()` explicitly. - self.enforce_illegal_method_limitations(pick); + self.check_for_illegal_method_calls(pick); + + // Lint when an item is shadowing a supertrait item. + self.lint_shadowed_supertrait_items(pick, segment); // Add any trait/regions obligations specified on the method's type parameters. // We won't add these if we encountered an illegal sized bound, so that we can use @@ -347,9 +352,17 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // yield an object-type (e.g., `&Object` or `Box` // etc). - // FIXME: this feels, like, super dubious - self.fcx - .autoderef(self.span, self_ty) + let mut autoderef = self.fcx.autoderef(self.span, self_ty); + + // We don't need to gate this behind arbitrary self types + // per se, but it does make things a bit more gated. + if self.tcx.features().arbitrary_self_types() + || self.tcx.features().arbitrary_self_types_pointers() + { + autoderef = autoderef.use_receiver_trait(); + } + + autoderef .include_raw_pointers() .find_map(|(ty, _)| match ty.kind() { ty::Dynamic(data, ..) => Some(closure( @@ -413,7 +426,6 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { fn provided_kind( &mut self, - _preceding_args: &[ty::GenericArg<'tcx>], param: &ty::GenericParamDef, arg: &GenericArg<'tcx>, ) -> ty::GenericArg<'tcx> { @@ -425,14 +437,17 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { .lower_lifetime(lt, RegionInferReason::Param(param)) .into(), (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { - self.cfcx.lower_ty(ty).raw.into() - } - (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { - self.cfcx.lower_const_arg(ct, FeedConstTy::Param(param.def_id)).into() + // We handle the ambig portions of `Ty` in the match arms below + self.cfcx.lower_ty(ty.as_unambig_ty()).raw.into() } (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => { - self.cfcx.ty_infer(Some(param), inf.span).into() + self.cfcx.lower_ty(&inf.to_ty()).raw.into() } + (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => self + .cfcx + // We handle the ambig portions of `ConstArg` in the match arms below + .lower_const_arg(ct.as_unambig_ct(), FeedConstTy::Param(param.def_id)) + .into(), (GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { self.cfcx.ct_infer(Some(param), inf.span).into() } @@ -611,7 +626,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // this is a projection from a trait reference, so we have to // make sure that the trait reference inputs are well-formed. - self.add_wf_bounds(all_args, self.call_expr); + self.add_wf_bounds(all_args, self.call_expr.span); // the function type must also be well-formed (this is not // implied by the args being well-formed because of inherent @@ -646,7 +661,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { }) } - fn enforce_illegal_method_limitations(&self, pick: &probe::Pick<'_>) { + fn check_for_illegal_method_calls(&self, pick: &probe::Pick<'_>) { // Disallow calls to the method `drop` defined in the `Drop` trait. if let Some(trait_def_id) = pick.item.trait_container(self.tcx) { if let Err(e) = callee::check_legal_trait_for_method_call( @@ -662,6 +677,45 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { } } + fn lint_shadowed_supertrait_items( + &self, + pick: &probe::Pick<'_>, + segment: &hir::PathSegment<'tcx>, + ) { + if pick.shadowed_candidates.is_empty() { + return; + } + + let shadower_span = self.tcx.def_span(pick.item.def_id); + let subtrait = self.tcx.item_name(pick.item.trait_container(self.tcx).unwrap()); + let shadower = SupertraitItemShadower { span: shadower_span, subtrait }; + + let shadowee = if let [shadowee] = &pick.shadowed_candidates[..] { + let shadowee_span = self.tcx.def_span(shadowee.def_id); + let supertrait = self.tcx.item_name(shadowee.trait_container(self.tcx).unwrap()); + SupertraitItemShadowee::Labeled { span: shadowee_span, supertrait } + } else { + let (traits, spans): (Vec<_>, Vec<_>) = pick + .shadowed_candidates + .iter() + .map(|item| { + ( + self.tcx.item_name(item.trait_container(self.tcx).unwrap()), + self.tcx.def_span(item.def_id), + ) + }) + .unzip(); + SupertraitItemShadowee::Several { traits: traits.into(), spans: spans.into() } + }; + + self.tcx.emit_node_span_lint( + SUPERTRAIT_ITEM_SHADOWING_USAGE, + segment.hir_id, + segment.ident.span, + SupertraitItemShadowing { shadower, shadowee, item: segment.ident.name, subtrait }, + ); + } + fn upcast( &mut self, source_trait_ref: ty::PolyTraitRef<'tcx>, @@ -671,17 +725,23 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { traits::upcast_choices(self.tcx, source_trait_ref, target_trait_def_id); // must be exactly one trait ref or we'd get an ambig error etc - let [upcast_trait_ref] = upcast_trait_refs.as_slice() else { - span_bug!( + if let &[upcast_trait_ref] = upcast_trait_refs.as_slice() { + upcast_trait_ref + } else { + self.dcx().span_delayed_bug( self.span, - "cannot uniquely upcast `{:?}` to `{:?}`: `{:?}`", - source_trait_ref, - target_trait_def_id, - upcast_trait_refs - ) - }; + format!( + "cannot uniquely upcast `{:?}` to `{:?}`: `{:?}`", + source_trait_ref, target_trait_def_id, upcast_trait_refs + ), + ); - *upcast_trait_ref + ty::Binder::dummy(ty::TraitRef::new_from_args( + self.tcx, + target_trait_def_id, + ty::GenericArgs::extend_with_error(self.tcx, target_trait_def_id, &[]), + )) + } } fn instantiate_binder_with_fresh_vars(&self, value: ty::Binder<'tcx, T>) -> T diff --git a/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs index 5ccfcf93f69ef..69d7a6c97cb36 100644 --- a/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs +++ b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs @@ -8,7 +8,7 @@ use rustc_lint::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER}; use rustc_middle::span_bug; use rustc_middle::ty::{self, Ty}; use rustc_session::lint::builtin::{RUST_2021_PRELUDE_COLLISIONS, RUST_2024_PRELUDE_COLLISIONS}; -use rustc_span::{Ident, Span, kw, sym}; +use rustc_span::{Ident, STDLIB_STABLE_CRATES, Span, kw, sym}; use rustc_trait_selection::infer::InferCtxtExt; use tracing::debug; @@ -76,7 +76,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; // No need to lint if method came from std/core, as that will now be in the prelude - if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) { + if STDLIB_STABLE_CRATES.contains(&self.tcx.crate_name(pick.item.def_id.krate)) { return; } @@ -252,7 +252,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // No need to lint if method came from std/core, as that will now be in the prelude - if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) { + if STDLIB_STABLE_CRATES.contains(&self.tcx.crate_name(pick.item.def_id.krate)) { return; } diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 116765325a91f..b3e48fd5bb592 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -1,9 +1,9 @@ use std::cell::{Cell, RefCell}; use std::cmp::max; -use std::iter; use std::ops::Deref; use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::sso::SsoHashSet; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::HirId; @@ -34,6 +34,7 @@ use rustc_trait_selection::traits::query::method_autoderef::{ CandidateStep, MethodAutoderefBadTy, MethodAutoderefStepsResult, }; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCtxt}; +use rustc_type_ir::elaborate::supertrait_def_ids; use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; @@ -225,6 +226,9 @@ pub(crate) struct Pick<'tcx> { /// to identify this method. Used only for deshadowing errors. /// Only applies for inherent impls. pub receiver_steps: Option, + + /// Candidates that were shadowed by supertraits. + pub shadowed_candidates: Vec, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -599,7 +603,7 @@ fn method_autoderef_steps<'tcx>( unsize: false, reachable_via_deref, }; - if ty.is_unsafe_ptr() { + if ty.is_raw_ptr() { // all the subsequent steps will be from_unsafe_deref reached_raw_pointer = true; } @@ -619,7 +623,7 @@ fn method_autoderef_steps<'tcx>( unsize: false, reachable_via_deref: true, }; - if ty.is_unsafe_ptr() { + if ty.is_raw_ptr() { // all the subsequent steps will be from_unsafe_deref reached_raw_pointer = true; } @@ -1009,11 +1013,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { if self.tcx.is_trait_alias(trait_def_id) { // For trait aliases, recursively assume all explicitly named traits are relevant - for expansion in traits::expand_trait_aliases( - self.tcx, - iter::once((ty::Binder::dummy(trait_ref), self.span)), - ) { - let bound_trait_ref = expansion.trait_ref(); + for (bound_trait_pred, _) in + traits::expand_trait_aliases(self.tcx, [(trait_ref.upcast(self.tcx), self.span)]).0 + { + assert_eq!(bound_trait_pred.polarity(), ty::PredicatePolarity::Positive); + let bound_trait_ref = bound_trait_pred.map_bound(|pred| pred.trait_ref); for item in self.impl_or_trait_item(bound_trait_ref.def_id()) { if !self.has_applicable_self(&item) { self.record_static_candidate(CandidateSource::Trait( @@ -1635,6 +1639,17 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } if applicable_candidates.len() > 1 { + // We collapse to a subtrait pick *after* filtering unstable candidates + // to make sure we don't prefer a unstable subtrait method over a stable + // supertrait method. + if self.tcx.features().supertrait_item_shadowing() { + if let Some(pick) = + self.collapse_candidates_to_subtrait_pick(self_ty, &applicable_candidates) + { + return Some(Ok(pick)); + } + } + let sources = candidates.iter().map(|p| self.candidate_source(p, self_ty)).collect(); return Some(Err(MethodError::Ambiguity(sources))); } @@ -1673,6 +1688,7 @@ impl<'tcx> Pick<'tcx> { self_ty, unstable_candidates: _, receiver_steps: _, + shadowed_candidates: _, } = *self; self_ty != other.self_ty || def_id != other.item.def_id } @@ -2082,6 +2098,83 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self_ty, unstable_candidates: vec![], receiver_steps: None, + shadowed_candidates: vec![], + }) + } + + /// Much like `collapse_candidates_to_trait_pick`, this method allows us to collapse + /// multiple conflicting picks if there is one pick whose trait container is a subtrait + /// of the trait containers of all of the other picks. + /// + /// This implements RFC #3624. + fn collapse_candidates_to_subtrait_pick( + &self, + self_ty: Ty<'tcx>, + probes: &[(&Candidate<'tcx>, ProbeResult)], + ) -> Option> { + let mut child_candidate = probes[0].0; + let mut child_trait = child_candidate.item.trait_container(self.tcx)?; + let mut supertraits: SsoHashSet<_> = supertrait_def_ids(self.tcx, child_trait).collect(); + + let mut remaining_candidates: Vec<_> = probes[1..].iter().map(|&(p, _)| p).collect(); + while !remaining_candidates.is_empty() { + let mut made_progress = false; + let mut next_round = vec![]; + + for remaining_candidate in remaining_candidates { + let remaining_trait = remaining_candidate.item.trait_container(self.tcx)?; + if supertraits.contains(&remaining_trait) { + made_progress = true; + continue; + } + + // This pick is not a supertrait of the `child_pick`. + // Check if it's a subtrait of the `child_pick`, instead. + // If it is, then it must have been a subtrait of every + // other pick we've eliminated at this point. It will + // take over at this point. + let remaining_trait_supertraits: SsoHashSet<_> = + supertrait_def_ids(self.tcx, remaining_trait).collect(); + if remaining_trait_supertraits.contains(&child_trait) { + child_candidate = remaining_candidate; + child_trait = remaining_trait; + supertraits = remaining_trait_supertraits; + made_progress = true; + continue; + } + + // `child_pick` is not a supertrait of this pick. + // Don't bail here, since we may be comparing two supertraits + // of a common subtrait. These two supertraits won't be related + // at all, but we will pick them up next round when we find their + // child as we continue iterating in this round. + next_round.push(remaining_candidate); + } + + if made_progress { + // If we've made progress, iterate again. + remaining_candidates = next_round; + } else { + // Otherwise, we must have at least two candidates which + // are not related to each other at all. + return None; + } + } + + Some(Pick { + item: child_candidate.item, + kind: TraitPick, + import_ids: child_candidate.import_ids.clone(), + autoderefs: 0, + autoref_or_ptr_adjustment: None, + self_ty, + unstable_candidates: vec![], + shadowed_candidates: probes + .iter() + .map(|(c, _)| c.item) + .filter(|item| item.def_id != child_candidate.item.def_id) + .collect(), + receiver_steps: None, }) } @@ -2379,6 +2472,7 @@ impl<'tcx> Candidate<'tcx> { InherentImplCandidate { receiver_steps, .. } => Some(receiver_steps), _ => None, }, + shadowed_candidates: vec![], } } } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 0b008fd10b507..6c4ad65be6a63 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -5,6 +5,7 @@ use core::ops::ControlFlow; use std::borrow::Cow; +use std::path::PathBuf; use hir::Expr; use rustc_ast::ast::Mutability; @@ -14,7 +15,7 @@ use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, MultiSpan, StashKey, pluralize, struct_span_code_err}; -use rustc_hir::def::DefKind; +use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::lang_items::LangItem; @@ -170,10 +171,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, .. }) + | hir::Node::PatExpr(&hir::PatExpr { + kind: hir::PatExprKind::Path(QPath::TypeRelative(rcvr, segment)), + span, + .. + }) | hir::Node::Pat(&hir::Pat { kind: - hir::PatKind::Path(QPath::TypeRelative(rcvr, segment)) - | hir::PatKind::Struct(QPath::TypeRelative(rcvr, segment), ..) + hir::PatKind::Struct(QPath::TypeRelative(rcvr, segment), ..) | hir::PatKind::TupleStruct(QPath::TypeRelative(rcvr, segment), ..), span, .. @@ -358,14 +363,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn suggest_missing_writer(&self, rcvr_ty: Ty<'tcx>, rcvr_expr: &hir::Expr<'tcx>) -> Diag<'_> { let mut file = None; - let ty_str = self.tcx.short_ty_string(rcvr_ty, &mut file); let mut err = struct_span_code_err!( self.dcx(), rcvr_expr.span, E0599, "cannot write into `{}`", - ty_str + self.tcx.short_string(rcvr_ty, &mut file), ); + *err.long_ty_path() = file; err.span_note( rcvr_expr.span, "must implement `io::Write`, `fmt::Write`, or have a `write_fmt` method", @@ -376,11 +381,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "a writer is needed before this format string", ); }; - if let Some(file) = file { - err.note(format!("the full type name has been written to '{}'", file.display())); - err.note("consider using `--verbose` to print the full type name to the console"); - } - err } @@ -591,7 +591,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (predicates.to_string(), with_forced_trimmed_paths!(predicates.to_string())) } else { ( - tcx.short_ty_string(rcvr_ty, &mut ty_file), + tcx.short_string(rcvr_ty, &mut ty_file), with_forced_trimmed_paths!(rcvr_ty.to_string()), ) }; @@ -620,6 +620,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, item_name, &short_ty_str, + &mut ty_file, ) { return guar; } @@ -631,6 +632,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_kind, item_name, &short_ty_str, + &mut ty_file, ) { return guar; } @@ -690,6 +692,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } + // Check if we wrote `Self::Assoc(1)` as if it were a tuple ctor. + if let SelfSource::QPath(ty) = source + && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind + && let Res::SelfTyAlias { alias_to: impl_def_id, .. } = path.res + && let DefKind::Impl { .. } = self.tcx.def_kind(impl_def_id) + && let Some(candidate) = tcx.associated_items(impl_def_id).find_by_name_and_kind( + self.tcx, + item_name, + ty::AssocKind::Type, + impl_def_id, + ) + && let Some(adt_def) = tcx.type_of(candidate.def_id).skip_binder().ty_adt_def() + && adt_def.is_struct() + && adt_def.non_enum_variant().ctor_kind() == Some(CtorKind::Fn) + { + let def_path = tcx.def_path_str(adt_def.did()); + err.span_suggestion( + ty.span.to(item_name.span), + format!("to construct a value of type `{}`, use the explicit path", def_path), + def_path, + Applicability::MachineApplicable, + ); + } + err }; if tcx.sess.source_map().is_multiline(sugg_span) { @@ -700,10 +726,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty_str = short_ty_str; } - if let Some(file) = ty_file { - err.note(format!("the full type name has been written to '{}'", file.display(),)); - err.note("consider using `--verbose` to print the full type name to the console"); - } if rcvr_ty.references_error() { err.downgrade_to_delayed_bug(); } @@ -1067,7 +1089,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) ) { continue; - }; + } match self.tcx.hir().get_if_local(item_def_id) { // Unmet obligation comes from a `derive` macro, point at it once to @@ -1181,8 +1203,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { entry.1.insert((cause_span, "unsatisfied trait bound introduced here")); entry.2.push(p); } - Some(node) => unreachable!("encountered `{node:?}` due to `{cause:#?}`"), - None => (), + _ => { + // It's possible to use well-formedness clauses to get obligations + // which point arbitrary items like ADTs, so there's no use in ICEing + // here if we find that the obligation originates from some other + // node that we don't handle. + } } } let mut spanned_predicates: Vec<_> = spanned_predicates.into_iter().collect(); @@ -1282,7 +1308,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bound_list.into_iter().map(|(_, path)| path).collect::>().join("\n"); let actual_prefix = rcvr_ty.prefix_string(self.tcx); info!("unimplemented_traits.len() == {}", unimplemented_traits.len()); - let mut long_ty_file = None; let (primary_message, label, notes) = if unimplemented_traits.len() == 1 && unimplemented_traits_only { @@ -1297,7 +1322,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let OnUnimplementedNote { message, label, notes, .. } = self .err_ctxt() - .on_unimplemented_note(trait_ref, &obligation, &mut long_ty_file); + .on_unimplemented_note(trait_ref, &obligation, &mut ty_file); (message, label, notes) }) .unwrap() @@ -1311,15 +1336,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) }); err.primary_message(primary_message); - if let Some(file) = long_ty_file { - err.note(format!( - "the full name for the type has been written to '{}'", - file.display(), - )); - err.note( - "consider using `--verbose` to print the full type name to the console", - ); - } if let Some(label) = label { custom_span_label = true; err.span_label(span, label); @@ -2371,6 +2387,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, item_name: Ident, ty_str: &str, + long_ty_path: &mut Option, ) -> Result<(), ErrorGuaranteed> { if let SelfSource::MethodCall(expr) = source { for (_, parent) in tcx.hir().parent_iter(expr.hir_id).take(5) { @@ -2378,6 +2395,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let lang_item = match parent_expr.kind { ExprKind::Struct(qpath, _, _) => match *qpath { QPath::LangItem(LangItem::Range, ..) => Some(LangItem::Range), + QPath::LangItem(LangItem::RangeCopy, ..) => Some(LangItem::RangeCopy), + QPath::LangItem(LangItem::RangeInclusiveCopy, ..) => { + Some(LangItem::RangeInclusiveCopy) + } QPath::LangItem(LangItem::RangeTo, ..) => Some(LangItem::RangeTo), QPath::LangItem(LangItem::RangeToInclusive, ..) => { Some(LangItem::RangeToInclusive) @@ -2428,7 +2449,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); if pick.is_ok() { let range_span = parent_expr.span.with_hi(expr.span.hi()); - return Err(self.dcx().emit_err(errors::MissingParenthesesInRange { + let mut err = self.dcx().create_err(errors::MissingParenthesesInRange { span, ty_str: ty_str.to_string(), method_name: item_name.as_str().to_string(), @@ -2437,7 +2458,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { left: range_span.shrink_to_lo(), right: range_span.shrink_to_hi(), }), - })); + }); + *err.long_ty_path() = long_ty_path.take(); + return Err(err.emit()); } } } @@ -2454,6 +2477,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_kind: &str, item_name: Ident, ty_str: &str, + long_ty_path: &mut Option, ) -> Result<(), ErrorGuaranteed> { let found_candidate = all_traits(self.tcx) .into_iter() @@ -2494,6 +2518,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_name, ty_str ); + *err.long_ty_path() = long_ty_path.take(); let concrete_type = if actual.is_integral() { "i32" } else { "f32" }; match expr.kind { ExprKind::Lit(lit) => { @@ -2681,7 +2706,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .map(|field_path| { field_path .iter() - .map(|id| id.name.to_ident_string()) + .map(|id| id.to_string()) .collect::>() .join(".") }) @@ -3728,18 +3753,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::TraitFn::Required([ident, ..]) => { ident.name == kw::SelfLower } - hir::TraitFn::Provided(body_id) => { - self.tcx.hir().body(*body_id).params.first().map_or( - false, - |param| { - matches!( - param.pat.kind, - hir::PatKind::Binding(_, _, ident, _) - if ident.name == kw::SelfLower - ) - }, - ) - } + hir::TraitFn::Provided(body_id) => self + .tcx + .hir() + .body(*body_id) + .params + .first() + .is_some_and(|param| { + matches!( + param.pat.kind, + hir::PatKind::Binding(_, _, ident, _) + if ident.name == kw::SelfLower + ) + }), _ => false, }; @@ -3896,11 +3922,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let all_suggs = candidate_strs.iter().map(|cand| { - let suggestion = format!("{} {cand}", match introducer { - Introducer::Plus => " +", - Introducer::Colon => ":", - Introducer::Nothing => "", - },); + let suggestion = format!( + "{} {cand}", + match introducer { + Introducer::Plus => " +", + Introducer::Colon => ":", + Introducer::Nothing => "", + }, + ); let mut suggs = vec![]; diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 805079afdb006..1750c2af1c7ff 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -306,6 +306,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lang_item_for_op(self.tcx, Op::Binary(op, is_assign), op.span); let missing_trait = trait_def_id .map(|def_id| with_no_trimmed_paths!(self.tcx.def_path_str(def_id))); + let mut path = None; + let lhs_ty_str = self.tcx.short_string(lhs_ty, &mut path); + let rhs_ty_str = self.tcx.short_string(rhs_ty, &mut path); let (mut err, output_def_id) = match is_assign { IsAssign::Yes => { let mut err = struct_span_code_err!( @@ -314,11 +317,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { E0368, "binary assignment operation `{}=` cannot be applied to type `{}`", op.node.as_str(), - lhs_ty, + lhs_ty_str, ); err.span_label( lhs_expr.span, - format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty), + format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty_str), ); self.note_unmet_impls_on_type(&mut err, errors, false); (err, None) @@ -326,41 +329,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsAssign::No => { let message = match op.node { hir::BinOpKind::Add => { - format!("cannot add `{rhs_ty}` to `{lhs_ty}`") + format!("cannot add `{rhs_ty_str}` to `{lhs_ty_str}`") } hir::BinOpKind::Sub => { - format!("cannot subtract `{rhs_ty}` from `{lhs_ty}`") + format!("cannot subtract `{rhs_ty_str}` from `{lhs_ty_str}`") } hir::BinOpKind::Mul => { - format!("cannot multiply `{lhs_ty}` by `{rhs_ty}`") + format!("cannot multiply `{lhs_ty_str}` by `{rhs_ty_str}`") } hir::BinOpKind::Div => { - format!("cannot divide `{lhs_ty}` by `{rhs_ty}`") + format!("cannot divide `{lhs_ty_str}` by `{rhs_ty_str}`") } hir::BinOpKind::Rem => { format!( - "cannot calculate the remainder of `{lhs_ty}` divided by `{rhs_ty}`" + "cannot calculate the remainder of `{lhs_ty_str}` divided by \ + `{rhs_ty_str}`" ) } hir::BinOpKind::BitAnd => { - format!("no implementation for `{lhs_ty} & {rhs_ty}`") + format!("no implementation for `{lhs_ty_str} & {rhs_ty_str}`") } hir::BinOpKind::BitXor => { - format!("no implementation for `{lhs_ty} ^ {rhs_ty}`") + format!("no implementation for `{lhs_ty_str} ^ {rhs_ty_str}`") } hir::BinOpKind::BitOr => { - format!("no implementation for `{lhs_ty} | {rhs_ty}`") + format!("no implementation for `{lhs_ty_str} | {rhs_ty_str}`") } hir::BinOpKind::Shl => { - format!("no implementation for `{lhs_ty} << {rhs_ty}`") + format!("no implementation for `{lhs_ty_str} << {rhs_ty_str}`") } hir::BinOpKind::Shr => { - format!("no implementation for `{lhs_ty} >> {rhs_ty}`") + format!("no implementation for `{lhs_ty_str} >> {rhs_ty_str}`") } _ => format!( "binary operation `{}` cannot be applied to type `{}`", op.node.as_str(), - lhs_ty + lhs_ty_str, ), }; let output_def_id = trait_def_id.and_then(|def_id| { @@ -375,14 +379,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut err = struct_span_code_err!(self.dcx(), op.span, E0369, "{message}"); if !lhs_expr.span.eq(&rhs_expr.span) { - err.span_label(lhs_expr.span, lhs_ty.to_string()); - err.span_label(rhs_expr.span, rhs_ty.to_string()); + err.span_label(lhs_expr.span, lhs_ty_str.clone()); + err.span_label(rhs_expr.span, rhs_ty_str); } let suggest_derive = self.can_eq(self.param_env, lhs_ty, rhs_ty); self.note_unmet_impls_on_type(&mut err, errors, suggest_derive); (err, output_def_id) } }; + *err.long_ty_path() = path; // Try to suggest a semicolon if it's `A \n *B` where `B` is a place expr let maybe_missing_semi = self.check_for_missing_semi(expr, &mut err); @@ -417,7 +422,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsAssign::Yes => "=", IsAssign::No => "", }, - lhs_deref_ty, + self.tcx.short_string(lhs_deref_ty, err.long_ty_path()), ); err.span_suggestion_verbose( lhs_expr.span.shrink_to_lo(), @@ -443,8 +448,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .is_ok() { - let op_str = op.node.as_str(); - err.note(format!("an implementation for `{lhs_adjusted_ty} {op_str} {rhs_adjusted_ty}` exists")); + let lhs = self.tcx.short_string(lhs_adjusted_ty, err.long_ty_path()); + let rhs = self.tcx.short_string(rhs_adjusted_ty, err.long_ty_path()); + let op = op.node.as_str(); + err.note(format!("an implementation for `{lhs} {op} {rhs}` exists")); if let Some(lhs_new_mutbl) = lhs_new_mutbl && let Some(rhs_new_mutbl) = rhs_new_mutbl @@ -628,7 +635,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // When we know that a missing bound is responsible, we don't show // this note as it is redundant. err.note(format!( - "the trait `{missing_trait}` is not implemented for `{lhs_ty}`" + "the trait `{missing_trait}` is not implemented for `{lhs_ty_str}`" )); } } @@ -638,7 +645,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // pointer + {integer} or pointer - pointer. if op.span.can_be_used_for_suggestions() { match op.node { - hir::BinOpKind::Add if lhs_ty.is_unsafe_ptr() && rhs_ty.is_integral() => { + hir::BinOpKind::Add if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() => { err.multipart_suggestion( "consider using `wrapping_add` or `add` for pointer + {integer}", vec![ @@ -652,26 +659,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } hir::BinOpKind::Sub => { - if lhs_ty.is_unsafe_ptr() && rhs_ty.is_integral() { + if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() { err.multipart_suggestion( - "consider using `wrapping_sub` or `sub` for pointer - {integer}", + "consider using `wrapping_sub` or `sub` for \ + pointer - {integer}", vec![ - (lhs_expr.span.between(rhs_expr.span), ".wrapping_sub(".to_owned()), + ( + lhs_expr.span.between(rhs_expr.span), + ".wrapping_sub(".to_owned(), + ), (rhs_expr.span.shrink_to_hi(), ")".to_owned()), ], - Applicability::MaybeIncorrect + Applicability::MaybeIncorrect, ); } - if lhs_ty.is_unsafe_ptr() && rhs_ty.is_unsafe_ptr() { + if lhs_ty.is_raw_ptr() && rhs_ty.is_raw_ptr() { err.multipart_suggestion( - "consider using `offset_from` for pointer - pointer if the pointers point to the same allocation", + "consider using `offset_from` for pointer - pointer if the \ + pointers point to the same allocation", vec![ (lhs_expr.span.shrink_to_lo(), "unsafe { ".to_owned()), - (lhs_expr.span.between(rhs_expr.span), ".offset_from(".to_owned()), + ( + lhs_expr.span.between(rhs_expr.span), + ".offset_from(".to_owned(), + ), (rhs_expr.span.shrink_to_hi(), ") }".to_owned()), ], - Applicability::MaybeIncorrect + Applicability::MaybeIncorrect, ); } } @@ -793,14 +808,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Err(errors) => { let actual = self.resolve_vars_if_possible(operand_ty); let guar = actual.error_reported().err().unwrap_or_else(|| { + let mut file = None; + let ty_str = self.tcx.short_string(actual, &mut file); let mut err = struct_span_code_err!( self.dcx(), ex.span, E0600, - "cannot apply unary operator `{}` to type `{}`", + "cannot apply unary operator `{}` to type `{ty_str}`", op.as_str(), - actual ); + *err.long_ty_path() = file; err.span_label( ex.span, format!("cannot apply unary operator `{}`", op.as_str()), @@ -901,13 +918,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let opname = Ident::with_dummy_span(opname); let (opt_rhs_expr, opt_rhs_ty) = opt_rhs.unzip(); - let cause = self.cause(span, ObligationCauseCode::BinOp { - lhs_hir_id: lhs_expr.hir_id, - rhs_hir_id: opt_rhs_expr.map(|expr| expr.hir_id), - rhs_span: opt_rhs_expr.map(|expr| expr.span), - rhs_is_lit: opt_rhs_expr.is_some_and(|expr| matches!(expr.kind, hir::ExprKind::Lit(_))), - output_ty: expected.only_has_type(self), - }); + let cause = self.cause( + span, + ObligationCauseCode::BinOp { + lhs_hir_id: lhs_expr.hir_id, + rhs_hir_id: opt_rhs_expr.map(|expr| expr.hir_id), + rhs_span: opt_rhs_expr.map(|expr| expr.span), + rhs_is_lit: opt_rhs_expr + .is_some_and(|expr| matches!(expr.kind, hir::ExprKind::Lit(_))), + output_ty: expected.only_has_type(self), + }, + ); let method = self.lookup_method_in_trait(cause.clone(), opname, trait_did, lhs_ty, opt_rhs_ty); diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 98b28240f4cb9..7c2a2b3fdf7da 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -11,8 +11,8 @@ use rustc_errors::{ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::{ - self as hir, BindingMode, ByRef, ExprKind, HirId, LangItem, Mutability, Pat, PatKind, - expr_needs_parens, + self as hir, BindingMode, ByRef, ExprKind, HirId, LangItem, Mutability, Pat, PatExpr, + PatExprKind, PatKind, expr_needs_parens, }; use rustc_infer::infer; use rustc_middle::traits::PatternOriginExpr; @@ -21,6 +21,7 @@ use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_session::parse::feature_err; use rustc_span::edit_distance::find_best_match_for_name; +use rustc_span::edition::Edition; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::Spanned; use rustc_span::{BytePos, DUMMY_SP, Ident, Span, kw, sym}; @@ -30,6 +31,7 @@ use tracing::{debug, instrument, trace}; use ty::VariantDef; use super::report_unexpected_variant_res; +use crate::expectation::Expectation; use crate::gather_locals::DeclOrigin; use crate::{FnCtxt, LoweredTy, errors}; @@ -168,15 +170,16 @@ enum AdjustMode { Pass, } -/// `ref mut` patterns (explicit or match-ergonomics) -/// are not allowed behind an `&` reference. +/// `ref mut` bindings (explicit or match-ergonomics) are not allowed behind an `&` reference. +/// Normally, the borrow checker enforces this, but for (currently experimental) match ergonomics, +/// we track this when typing patterns for two purposes: /// -/// This includes explicit `ref mut` behind `&` patterns -/// that match against `&mut` references, -/// where the code would have compiled -/// had the pattern been written as `&mut`. -/// However, the borrow checker will not catch -/// this last case, so we need to throw an error ourselves. +/// - For RFC 3627's Rule 3, when this would prevent us from binding with `ref mut`, we limit the +/// default binding mode to be by shared `ref` when it would otherwise be `ref mut`. +/// +/// - For RFC 3627's Rule 5, we allow `&` patterns to match against `&mut` references, treating them +/// as if they were shared references. Since the scrutinee is mutable in this case, the borrow +/// checker won't catch if we bind with `ref mut`, so we need to throw an error ourselves. #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum MutblCap { /// Mutability restricted to immutable. @@ -212,7 +215,66 @@ impl MutblCap { } } +/// Variations on RFC 3627's Rule 4: when do reference patterns match against inherited references? +/// +/// "Inherited reference" designates the `&`/`&mut` types that arise from using match ergonomics, i.e. +/// from matching a reference type with a non-reference pattern. E.g. when `Some(x)` matches on +/// `&mut Option<&T>`, `x` gets type `&mut &T` and the outer `&mut` is considered "inherited". +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum InheritedRefMatchRule { + /// Reference patterns consume only the inherited reference if possible, regardless of whether + /// the underlying type being matched against is a reference type. If there is no inherited + /// reference, a reference will be consumed from the underlying type. + EatOuter, + /// Reference patterns consume only a reference from the underlying type if possible. If the + /// underlying type is not a reference type, the inherited reference will be consumed. + EatInner, + /// When the underlying type is a reference type, reference patterns consume both layers of + /// reference, i.e. they both reset the binding mode and consume the reference type. Reference + /// patterns are not permitted when there is no underlying reference type, i.e. they can't eat + /// only an inherited reference. This is the current stable Rust behavior. + EatBoth, +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + /// Experimental pattern feature: after matching against a shared reference, do we limit the + /// default binding mode in subpatterns to be `ref` when it would otherwise be `ref mut`? + /// This corresponds to Rule 3 of RFC 3627. + fn downgrade_mut_inside_shared(&self) -> bool { + // NB: RFC 3627 proposes stabilizing Rule 3 in all editions. If we adopt the same behavior + // across all editions, this may be removed. + self.tcx.features().ref_pat_eat_one_layer_2024_structural() + } + + /// Experimental pattern feature: when do reference patterns match against inherited references? + /// This corresponds to variations on Rule 4 of RFC 3627. + fn ref_pat_matches_inherited_ref(&self, edition: Edition) -> InheritedRefMatchRule { + // NB: The particular rule used here is likely to differ across editions, so calls to this + // may need to become edition checks after match ergonomics stabilize. + if edition.at_least_rust_2024() { + if self.tcx.features().ref_pat_eat_one_layer_2024() { + InheritedRefMatchRule::EatOuter + } else if self.tcx.features().ref_pat_eat_one_layer_2024_structural() { + InheritedRefMatchRule::EatInner + } else { + // Currently, matching against an inherited ref on edition 2024 is an error. + // Use `EatBoth` as a fallback to be similar to stable Rust. + InheritedRefMatchRule::EatBoth + } + } else { + InheritedRefMatchRule::EatBoth + } + } + + /// Experimental pattern feature: do `&` patterns match against `&mut` references, treating them + /// as if they were shared references? This corresponds to Rule 5 of RFC 3627. + fn ref_pat_matches_mut_ref(&self) -> bool { + // NB: RFC 3627 proposes stabilizing Rule 5 in all editions. If we adopt the same behavior + // across all editions, this may be removed. + self.tcx.features().ref_pat_eat_one_layer_2024() + || self.tcx.features().ref_pat_eat_one_layer_2024_structural() + } + /// Type check the given top level pattern against the `expected` type. /// /// If a `Some(span)` is provided and `origin_expr` holds, @@ -249,9 +311,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'_, 'tcx>) { let PatInfo { binding_mode, max_ref_mutbl, top_info: ti, current_depth, .. } = pat_info; - let path_res = match &pat.kind { - PatKind::Path(qpath) => { - Some(self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span)) + let path_res = match pat.kind { + PatKind::Expr(PatExpr { kind: PatExprKind::Path(ref qpath), hir_id, span }) => { + Some(self.resolve_ty_and_res_fully_qualified_call(qpath, *hir_id, *span)) } _ => None, }; @@ -270,7 +332,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::Wild | PatKind::Err(_) => expected, // We allow any type here; we ensure that the type is uninhabited during match checking. PatKind::Never => expected, - PatKind::Lit(lt) => self.check_pat_lit(pat.span, lt, expected, ti), + PatKind::Expr(PatExpr { kind: PatExprKind::Path(ref qpath), hir_id, span }) => { + let ty = self.check_pat_path( + *hir_id, + pat.hir_id, + *span, + qpath, + path_res.unwrap(), + expected, + ti, + ); + self.write_ty(*hir_id, ty); + ty + } + PatKind::Expr(lt) => self.check_pat_lit(pat.span, lt, expected, ti), PatKind::Range(lhs, rhs, _) => self.check_pat_range(pat.span, lhs, rhs, expected, ti), PatKind::Binding(ba, var_id, ident, sub) => { self.check_pat_ident(pat, ba, var_id, ident, sub, expected, pat_info) @@ -278,12 +353,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::TupleStruct(ref qpath, subpats, ddpos) => { self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, pat_info) } - PatKind::Path(ref qpath) => { - self.check_pat_path(pat, qpath, path_res.unwrap(), expected, ti) - } PatKind::Struct(ref qpath, fields, has_rest_pat) => { self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, pat_info) } + PatKind::Guard(pat, cond) => { + self.check_pat(pat, expected, pat_info); + self.check_expr_has_type_or_error(cond, self.tcx.types.bool, |_| {}); + expected + } PatKind::Or(pats) => { for pat in pats { self.check_pat(pat, expected, pat_info); @@ -357,7 +434,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { max_ref_mutbl: MutblCap, ) -> (Ty<'tcx>, ByRef, MutblCap) { #[cfg(debug_assertions)] - if def_br == ByRef::Yes(Mutability::Mut) && max_ref_mutbl != MutblCap::Mut { + if def_br == ByRef::Yes(Mutability::Mut) + && max_ref_mutbl != MutblCap::Mut + && self.downgrade_mut_inside_shared() + { span_bug!(pat.span, "Pattern mutability cap violated!"); } match adjust_mode { @@ -388,16 +468,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | PatKind::Slice(..) => AdjustMode::Peel, // A never pattern behaves somewhat like a literal or unit variant. PatKind::Never => AdjustMode::Peel, - // String and byte-string literals result in types `&str` and `&[u8]` respectively. - // All other literals result in non-reference types. - // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}`. - // - // Call `resolve_vars_if_possible` here for inline const blocks. - PatKind::Lit(lt) => match self.resolve_vars_if_possible(self.check_expr(lt)).kind() { - ty::Ref(..) => AdjustMode::Pass, - _ => AdjustMode::Peel, - }, - PatKind::Path(_) => match opt_path_res.unwrap() { + PatKind::Expr(PatExpr { kind: PatExprKind::Path(_), .. }) => match opt_path_res.unwrap() { // These constants can be of a reference type, e.g. `const X: &u8 = &0;`. // Peeling the reference types too early will cause type checking failures. // Although it would be possible to *also* peel the types of the constants too. @@ -408,6 +479,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // a reference type wherefore peeling doesn't give up any expressiveness. _ => AdjustMode::Peel, }, + + // String and byte-string literals result in types `&str` and `&[u8]` respectively. + // All other literals result in non-reference types. + // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}`. + // + // Call `resolve_vars_if_possible` here for inline const blocks. + PatKind::Expr(lt) => match self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt)).kind() { + ty::Ref(..) => AdjustMode::Pass, + _ => AdjustMode::Peel, + }, + // Ref patterns are complicated, we handle them in `check_pat_ref`. PatKind::Ref(..) => AdjustMode::Pass, // A `_` pattern works with any expected type, so there's no need to do anything. @@ -422,7 +504,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // An OR-pattern just propagates to each individual alternative. // This is maximally flexible, allowing e.g., `Some(mut x) | &Some(mut x)`. // In that example, `Some(mut x)` results in `Peel` whereas `&Some(mut x)` in `Reset`. - | PatKind::Or(_) => AdjustMode::Pass, + | PatKind::Or(_) + // Like or-patterns, guard patterns just propogate to their subpatterns. + | PatKind::Guard(..) => AdjustMode::Pass, } } @@ -466,13 +550,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); } - let features = self.tcx.features(); - if features.ref_pat_eat_one_layer_2024() || features.ref_pat_eat_one_layer_2024_structural() - { + if self.downgrade_mut_inside_shared() { def_br = def_br.cap_ref_mutability(max_ref_mutbl.as_mutbl()); - if def_br == ByRef::Yes(Mutability::Not) { - max_ref_mutbl = MutblCap::Not; - } + } + if def_br == ByRef::Yes(Mutability::Not) { + max_ref_mutbl = MutblCap::Not; } if !pat_adjustments.is_empty() { @@ -486,10 +568,36 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (expected, def_br, max_ref_mutbl) } + fn check_pat_expr_unadjusted(&self, lt: &'tcx hir::PatExpr<'tcx>) -> Ty<'tcx> { + let ty = match <.kind { + rustc_hir::PatExprKind::Lit { lit, negated } => { + let ty = self.check_expr_lit(lit, Expectation::NoExpectation); + if *negated { + self.register_bound( + ty, + self.tcx.require_lang_item(LangItem::Neg, Some(lt.span)), + ObligationCause::dummy_with_span(lt.span), + ); + } + ty + } + rustc_hir::PatExprKind::ConstBlock(c) => { + self.check_expr_const_block(c, Expectation::NoExpectation) + } + rustc_hir::PatExprKind::Path(qpath) => { + let (res, opt_ty, segments) = + self.resolve_ty_and_res_fully_qualified_call(qpath, lt.hir_id, lt.span); + self.instantiate_value_path(segments, opt_ty, res, lt.span, lt.span, lt.hir_id).0 + } + }; + self.write_ty(lt.hir_id, ty); + ty + } + fn check_pat_lit( &self, span: Span, - lt: &hir::Expr<'tcx>, + lt: &hir::PatExpr<'tcx>, expected: Ty<'tcx>, ti: &TopInfo<'tcx>, ) -> Ty<'tcx> { @@ -500,7 +608,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Byte string patterns behave the same way as array patterns // They can denote both statically and dynamically-sized byte arrays. let mut pat_ty = ty; - if let hir::ExprKind::Lit(Spanned { node: ast::LitKind::ByteStr(..), .. }) = lt.kind { + if let hir::PatExprKind::Lit { + lit: Spanned { node: ast::LitKind::ByteStr(..), .. }, .. + } = lt.kind + { let expected = self.structurally_resolve_type(span, expected); if let ty::Ref(_, inner_ty, _) = *expected.kind() && self.try_structurally_resolve_type(span, inner_ty).is_slice() @@ -517,7 +628,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if self.tcx.features().string_deref_patterns() - && let hir::ExprKind::Lit(Spanned { node: ast::LitKind::Str(..), .. }) = lt.kind + && let hir::PatExprKind::Lit { + lit: Spanned { node: ast::LitKind::Str(..), .. }, .. + } = lt.kind { let tcx = self.tcx; let expected = self.resolve_vars_if_possible(expected); @@ -558,15 +671,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pat_range( &self, span: Span, - lhs: Option<&'tcx hir::Expr<'tcx>>, - rhs: Option<&'tcx hir::Expr<'tcx>>, + lhs: Option<&'tcx hir::PatExpr<'tcx>>, + rhs: Option<&'tcx hir::PatExpr<'tcx>>, expected: Ty<'tcx>, ti: &TopInfo<'tcx>, ) -> Ty<'tcx> { - let calc_side = |opt_expr: Option<&'tcx hir::Expr<'tcx>>| match opt_expr { + let calc_side = |opt_expr: Option<&'tcx hir::PatExpr<'tcx>>| match opt_expr { None => None, Some(expr) => { - let ty = self.check_expr(expr); + let ty = self.check_pat_expr_unadjusted(expr); // Check that the end-point is possibly of numeric or char type. // The early check here is not for correctness, but rather better // diagnostics (e.g. when `&str` is being matched, `expected` will @@ -699,7 +812,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Determine the binding mode... let bm = match user_bind_annot { - BindingMode(ByRef::No, Mutability::Mut) if matches!(def_br, ByRef::Yes(_)) => { + BindingMode(ByRef::No, Mutability::Mut) if let ByRef::Yes(def_br_mutbl) = def_br => { + // Only mention the experimental `mut_ref` feature if if we're in edition 2024 and + // using other experimental matching features compatible with it. if pat.span.at_least_rust_2024() && (self.tcx.features().ref_pat_eat_one_layer_2024() || self.tcx.features().ref_pat_eat_one_layer_2024_structural()) @@ -719,22 +834,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // `mut` resets the binding mode on edition <= 2021 self.add_rust_2024_migration_desugared_pat( pat_info.top_info.hir_id, - pat.span, + pat, ident.span, - "requires binding by-value, but the implicit default is by-reference", + def_br_mutbl, ); BindingMode(ByRef::No, Mutability::Mut) } } BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl), BindingMode(ByRef::Yes(_), _) => { - if matches!(def_br, ByRef::Yes(_)) { + if let ByRef::Yes(def_br_mutbl) = def_br { // `ref`/`ref mut` overrides the binding mode on edition <= 2021 self.add_rust_2024_migration_desugared_pat( pat_info.top_info.hir_id, - pat.span, + pat, ident.span, - "cannot override to bind by-reference when that is the implicit default", + def_br_mutbl, ); } user_bind_annot @@ -901,17 +1016,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Or(..) + | PatKind::Guard(..) | PatKind::Tuple(..) | PatKind::Slice(..) => "binding", PatKind::Wild | PatKind::Never | PatKind::Binding(..) - | PatKind::Path(..) | PatKind::Box(..) | PatKind::Deref(_) | PatKind::Ref(..) - | PatKind::Lit(..) + | PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) => break 'block None, }, @@ -1045,7 +1160,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pat_path( &self, - pat: &Pat<'tcx>, + path_id: HirId, + pat_id_for_diag: HirId, + span: Span, qpath: &hir::QPath<'_>, path_resolution: (Res, Option>, &'tcx [hir::PathSegment<'tcx>]), expected: Ty<'tcx>, @@ -1064,8 +1181,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Variant, _) => { let expected = "unit struct, unit variant or constant"; - let e = - report_unexpected_variant_res(tcx, res, None, qpath, pat.span, E0533, expected); + let e = report_unexpected_variant_res(tcx, res, None, qpath, span, E0533, expected); return Ty::new_error(tcx, e); } Res::SelfCtor(def_id) => { @@ -1080,7 +1196,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { res, None, qpath, - pat.span, + span, E0533, "unit struct", ); @@ -1099,11 +1215,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Type-check the path. let (pat_ty, pat_res) = - self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.span, pat.hir_id); + self.instantiate_value_path(segments, opt_ty, res, span, span, path_id); if let Err(err) = - self.demand_suptype_with_origin(&self.pattern_cause(ti, pat.span), expected, pat_ty) + self.demand_suptype_with_origin(&self.pattern_cause(ti, span), expected, pat_ty) { - self.emit_bad_pat_path(err, pat, res, pat_res, pat_ty, segments); + self.emit_bad_pat_path(err, pat_id_for_diag, span, res, pat_res, pat_ty, segments); } pat_ty } @@ -1146,13 +1262,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn emit_bad_pat_path( &self, mut e: Diag<'_>, - pat: &hir::Pat<'tcx>, + hir_id: HirId, + pat_span: Span, res: Res, pat_res: Res, pat_ty: Ty<'tcx>, segments: &'tcx [hir::PathSegment<'tcx>], ) { - let pat_span = pat.span; if let Some(span) = self.tcx.hir().res_span(pat_res) { e.span_label(span, format!("{} defined here", res.descr())); if let [hir::PathSegment { ident, .. }] = &*segments { @@ -1165,7 +1281,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { res.descr(), ), ); - match self.tcx.parent_hir_node(pat.hir_id) { + match self.tcx.parent_hir_node(hir_id) { hir::Node::PatField(..) => { e.span_suggestion_verbose( ident.span.shrink_to_hi(), @@ -1805,9 +1921,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } else if inexistent_fields.len() == 1 { match pat_field.pat.kind { - PatKind::Lit(expr) + PatKind::Expr(_) if !self.may_coerce( - self.typeck_results.borrow().expr_ty(expr), + self.typeck_results.borrow().node_type(pat_field.pat.hir_id), self.field_ty(field.span, field_def, args), ) => {} _ => { @@ -2196,81 +2312,85 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { mut pat_info: PatInfo<'_, 'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; - let features = tcx.features(); - let ref_pat_eat_one_layer_2024 = features.ref_pat_eat_one_layer_2024(); - let ref_pat_eat_one_layer_2024_structural = - features.ref_pat_eat_one_layer_2024_structural(); - - let no_ref_mut_behind_and = - ref_pat_eat_one_layer_2024 || ref_pat_eat_one_layer_2024_structural; - let new_match_ergonomics = pat.span.at_least_rust_2024() && no_ref_mut_behind_and; let pat_prefix_span = inner.span.find_ancestor_inside(pat.span).map(|end| pat.span.until(end)); - if no_ref_mut_behind_and { - if pat_mutbl == Mutability::Not { - // Prevent the inner pattern from binding with `ref mut`. - pat_info.max_ref_mutbl = pat_info.max_ref_mutbl.cap_to_weakly_not(pat_prefix_span); - } - } else { - pat_info.max_ref_mutbl = MutblCap::Mut; + let ref_pat_matches_mut_ref = self.ref_pat_matches_mut_ref(); + if ref_pat_matches_mut_ref && pat_mutbl == Mutability::Not { + // If `&` patterns can match against mutable reference types (RFC 3627, Rule 5), we need + // to prevent subpatterns from binding with `ref mut`. Subpatterns of a shared reference + // pattern should have read-only access to the scrutinee, and the borrow checker won't + // catch it in this case. + pat_info.max_ref_mutbl = pat_info.max_ref_mutbl.cap_to_weakly_not(pat_prefix_span); } expected = self.try_structurally_resolve_type(pat.span, expected); - if new_match_ergonomics { - if let ByRef::Yes(inh_mut) = pat_info.binding_mode { - if !ref_pat_eat_one_layer_2024 && let ty::Ref(_, _, r_mutbl) = *expected.kind() { - // Don't attempt to consume inherited reference - pat_info.binding_mode = pat_info.binding_mode.cap_ref_mutability(r_mutbl); - } else { + // Determine whether we're consuming an inherited reference and resetting the default + // binding mode, based on edition and enabled experimental features. + if let ByRef::Yes(inh_mut) = pat_info.binding_mode { + match self.ref_pat_matches_inherited_ref(pat.span.edition()) { + InheritedRefMatchRule::EatOuter => { // ref pattern attempts to consume inherited reference if pat_mutbl > inh_mut { // Tried to match inherited `ref` with `&mut` - if !ref_pat_eat_one_layer_2024_structural { - let err_msg = "mismatched types"; - let err = if let Some(span) = pat_prefix_span { - let mut err = self.dcx().struct_span_err(span, err_msg); - err.code(E0308); - err.note("cannot match inherited `&` with `&mut` pattern"); - err.span_suggestion_verbose( - span, - "replace this `&mut` pattern with `&`", - "&", - Applicability::MachineApplicable, - ); - err - } else { - self.dcx().struct_span_err(pat.span, err_msg) - }; - err.emit(); + // NB: This assumes that `&` patterns can match against mutable references + // (RFC 3627, Rule 5). If we implement a pattern typing ruleset with Rule 4E + // but not Rule 5, we'll need to check that here. + debug_assert!(ref_pat_matches_mut_ref); + self.error_inherited_ref_mutability_mismatch(pat, pat_prefix_span); + } - pat_info.binding_mode = ByRef::No; - self.typeck_results - .borrow_mut() - .skipped_ref_pats_mut() - .insert(pat.hir_id); - self.check_pat(inner, expected, pat_info); - return expected; - } + pat_info.binding_mode = ByRef::No; + self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id); + self.check_pat(inner, expected, pat_info); + return expected; + } + InheritedRefMatchRule::EatInner => { + if let ty::Ref(_, _, r_mutbl) = *expected.kind() + && pat_mutbl <= r_mutbl + { + // Match against the reference type; don't consume the inherited ref. + // NB: The check for compatible pattern and ref type mutability assumes that + // `&` patterns can match against mutable references (RFC 3627, Rule 5). If + // we implement a pattern typing ruleset with Rule 4 (including the fallback + // to matching the inherited ref when the inner ref can't match) but not + // Rule 5, we'll need to check that here. + debug_assert!(ref_pat_matches_mut_ref); + // NB: For RFC 3627's Rule 3, we limit the default binding mode's ref + // mutability to `pat_info.max_ref_mutbl`. If we implement a pattern typing + // ruleset with Rule 4 but not Rule 3, we'll need to check that here. + debug_assert!(self.downgrade_mut_inside_shared()); + let mutbl_cap = cmp::min(r_mutbl, pat_info.max_ref_mutbl.as_mutbl()); + pat_info.binding_mode = pat_info.binding_mode.cap_ref_mutability(mutbl_cap); } else { + // The reference pattern can't match against the expected type, so try + // matching against the inherited ref instead. + if pat_mutbl > inh_mut { + // We can't match an inherited shared reference with `&mut`. + // NB: This assumes that `&` patterns can match against mutable + // references (RFC 3627, Rule 5). If we implement a pattern typing + // ruleset with Rule 4 but not Rule 5, we'll need to check that here. + debug_assert!(ref_pat_matches_mut_ref); + self.error_inherited_ref_mutability_mismatch(pat, pat_prefix_span); + } + pat_info.binding_mode = ByRef::No; self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id); self.check_pat(inner, expected, pat_info); return expected; } } - } - } else { - // Reset binding mode on old editions - if pat_info.binding_mode != ByRef::No { - pat_info.binding_mode = ByRef::No; - self.add_rust_2024_migration_desugared_pat( - pat_info.top_info.hir_id, - pat.span, - inner.span, - "cannot implicitly match against multiple layers of reference", - ) + InheritedRefMatchRule::EatBoth => { + // Reset binding mode on old editions + pat_info.binding_mode = ByRef::No; + self.add_rust_2024_migration_desugared_pat( + pat_info.top_info.hir_id, + pat, + inner.span, + inh_mut, + ) + } } } @@ -2285,10 +2405,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("check_pat_ref: expected={:?}", expected); match *expected.kind() { ty::Ref(_, r_ty, r_mutbl) - if (no_ref_mut_behind_and && r_mutbl >= pat_mutbl) + if (ref_pat_matches_mut_ref && r_mutbl >= pat_mutbl) || r_mutbl == pat_mutbl => { - if no_ref_mut_behind_and && r_mutbl == Mutability::Not { + if r_mutbl == Mutability::Not { pat_info.max_ref_mutbl = MutblCap::Not; } @@ -2332,6 +2452,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ty::new_ref(self.tcx, region, ty, mutbl) } + fn error_inherited_ref_mutability_mismatch( + &self, + pat: &'tcx Pat<'tcx>, + pat_prefix_span: Option, + ) -> ErrorGuaranteed { + let err_msg = "mismatched types"; + let err = if let Some(span) = pat_prefix_span { + let mut err = self.dcx().struct_span_err(span, err_msg); + err.code(E0308); + err.note("cannot match inherited `&` with `&mut` pattern"); + err.span_suggestion_verbose( + span, + "replace this `&mut` pattern with `&`", + "&", + Applicability::MachineApplicable, + ); + err + } else { + self.dcx().struct_span_err(pat.span, err_msg) + }; + err.emit() + } + fn try_resolve_slice_ty_to_array_ty( &self, before: &'tcx [Pat<'tcx>], @@ -2635,33 +2778,65 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn add_rust_2024_migration_desugared_pat( &self, pat_id: HirId, - subpat_span: Span, + subpat: &'tcx Pat<'tcx>, cutoff_span: Span, - detailed_label: &str, + def_br_mutbl: Mutability, ) { // Try to trim the span we're labeling to just the `&` or binding mode that's an issue. // If the subpattern's span is is from an expansion, the emitted label will not be trimmed. let source_map = self.tcx.sess.source_map(); let cutoff_span = source_map - .span_extend_prev_while(cutoff_span, char::is_whitespace) + .span_extend_prev_while(cutoff_span, |c| c.is_whitespace() || c == '(') .unwrap_or(cutoff_span); - // Ensure we use the syntax context and thus edition of `subpat_span`; this will be a hard + // Ensure we use the syntax context and thus edition of `subpat.span`; this will be a hard // error if the subpattern is of edition >= 2024. - let trimmed_span = subpat_span.until(cutoff_span).with_ctxt(subpat_span.ctxt()); + let trimmed_span = subpat.span.until(cutoff_span).with_ctxt(subpat.span.ctxt()); + + let mut typeck_results = self.typeck_results.borrow_mut(); + let mut table = typeck_results.rust_2024_migration_desugared_pats_mut(); + // FIXME(ref_pat_eat_one_layer_2024): The migration diagnostic doesn't know how to track the + // default binding mode in the presence of Rule 3 or Rule 5. As a consequence, the labels it + // gives for default binding modes are wrong, as well as suggestions based on the default + // binding mode. This keeps it from making those suggestions, as doing so could panic. + let info = table.entry(pat_id).or_insert_with(|| ty::Rust2024IncompatiblePatInfo { + primary_labels: Vec::new(), + bad_modifiers: false, + bad_ref_pats: false, + suggest_eliding_modes: !self.tcx.features().ref_pat_eat_one_layer_2024() + && !self.tcx.features().ref_pat_eat_one_layer_2024_structural(), + }); // Only provide a detailed label if the problematic subpattern isn't from an expansion. // In the case that it's from a macro, we'll add a more detailed note in the emitter. - let desc = if subpat_span.from_expansion() { - "default binding mode is reset within expansion" + let from_expansion = subpat.span.from_expansion(); + let primary_label = if from_expansion { + // NB: This wording assumes the only expansions that can produce problematic reference + // patterns and bindings are macros. If a desugaring or AST pass is added that can do + // so, we may want to inspect the span's source callee or macro backtrace. + "occurs within macro expansion".to_owned() } else { - detailed_label + let pat_kind = if let PatKind::Binding(user_bind_annot, _, _, _) = subpat.kind { + info.bad_modifiers |= true; + // If the user-provided binding modifier doesn't match the default binding mode, we'll + // need to suggest reference patterns, which can affect other bindings. + // For simplicity, we opt to suggest making the pattern fully explicit. + info.suggest_eliding_modes &= + user_bind_annot == BindingMode(ByRef::Yes(def_br_mutbl), Mutability::Not); + "binding modifier" + } else { + info.bad_ref_pats |= true; + // For simplicity, we don't try to suggest eliding reference patterns. Thus, we'll + // suggest adding them instead, which can affect the types assigned to bindings. + // As such, we opt to suggest making the pattern fully explicit. + info.suggest_eliding_modes = false; + "reference pattern" + }; + let dbm_str = match def_br_mutbl { + Mutability::Not => "ref", + Mutability::Mut => "ref mut", + }; + format!("{pat_kind} not allowed under `{dbm_str}` default binding mode") }; - - self.typeck_results - .borrow_mut() - .rust_2024_migration_desugared_pats_mut() - .entry(pat_id) - .or_default() - .push((trimmed_span, desc.to_owned())); + info.primary_labels.push((trimmed_span, primary_label)); } } diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index ba63507113578..4fc903cf68b88 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -1,6 +1,7 @@ use rustc_errors::Applicability; use rustc_hir_analysis::autoderef::Autoderef; use rustc_infer::infer::InferOk; +use rustc_infer::traits::{Obligation, ObligationCauseCode}; use rustc_middle::span_bug; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, OverloadedDeref, @@ -29,10 +30,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ok = self.try_overloaded_deref(expr.span, oprnd_ty)?; let method = self.register_infer_ok_obligations(ok); if let ty::Ref(_, _, hir::Mutability::Not) = method.sig.inputs()[0].kind() { - self.apply_adjustments(oprnd_expr, vec![Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Not)), - target: method.sig.inputs()[0], - }]); + self.apply_adjustments( + oprnd_expr, + vec![Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Not)), + target: method.sig.inputs()[0], + }], + ); } else { span_bug!(expr.span, "input to deref is not a ref?"); } @@ -136,8 +140,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut self_ty = adjusted_ty; if unsize { // We only unsize arrays here. - if let ty::Array(element_ty, _) = adjusted_ty.kind() { - self_ty = Ty::new_slice(self.tcx, *element_ty); + if let ty::Array(element_ty, ct) = *adjusted_ty.kind() { + self.register_predicate(Obligation::new( + self.tcx, + self.cause(base_expr.span, ObligationCauseCode::ArrayLen(adjusted_ty)), + self.param_env, + ty::ClauseKind::ConstArgHasType(ct, self.tcx.types.usize), + )); + self_ty = Ty::new_slice(self.tcx, element_ty); } else { continue; } diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index b308584314153..b54e430a9d9fa 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -147,15 +147,16 @@ impl<'a, 'tcx> Visitor<'tcx> for InferBorrowKindVisitor<'a, 'tcx> { self.visit_body(body); self.fcx.analyze_closure(expr.hir_id, expr.span, body_id, body, capture_clause); } - hir::ExprKind::ConstBlock(anon_const) => { - let body = self.fcx.tcx.hir().body(anon_const.body); - self.visit_body(body); - } _ => {} } intravisit::walk_expr(self, expr); } + + fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) { + let body = self.fcx.tcx.hir().body(c.body); + self.visit_body(body); + } } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -287,11 +288,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bug!(); }; let place = self.place_for_root_variable(closure_def_id, local_id); - delegate.capture_information.push((place, ty::CaptureInfo { - capture_kind_expr_id: Some(init.hir_id), - path_expr_id: Some(init.hir_id), - capture_kind: UpvarCapture::ByValue, - })); + delegate.capture_information.push(( + place, + ty::CaptureInfo { + capture_kind_expr_id: Some(init.hir_id), + path_expr_id: Some(init.hir_id), + capture_kind: UpvarCapture::ByValue, + }, + )); } } @@ -378,11 +382,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let UpvarArgs::CoroutineClosure(args) = args && !args.references_error() { - let closure_env_region: ty::Region<'_> = - ty::Region::new_bound(self.tcx, ty::INNERMOST, ty::BoundRegion { - var: ty::BoundVar::ZERO, - kind: ty::BoundRegionKind::ClosureEnv, - }); + let closure_env_region: ty::Region<'_> = ty::Region::new_bound( + self.tcx, + ty::INNERMOST, + ty::BoundRegion { var: ty::BoundVar::ZERO, kind: ty::BoundRegionKind::ClosureEnv }, + ); let num_args = args .as_coroutine_closure() @@ -1935,7 +1939,7 @@ fn drop_location_span(tcx: TyCtxt<'_>, hir_id: HirId) -> Span { let owner_node = tcx.hir_node(owner_id); let owner_span = match owner_node { hir::Node::Item(item) => match item.kind { - hir::ItemKind::Fn(_, _, owner_id) => tcx.hir().span(owner_id.hir_id), + hir::ItemKind::Fn { body: owner_id, .. } => tcx.hir().span(owner_id.hir_id), _ => { bug!("Drop location span error: need to handle more ItemKind '{:?}'", item.kind); } @@ -1987,17 +1991,18 @@ struct InferBorrowKind<'tcx> { impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> { fn fake_read( &mut self, - place: &PlaceWithHirId<'tcx>, + place_with_id: &PlaceWithHirId<'tcx>, cause: FakeReadCause, diag_expr_id: HirId, ) { - let PlaceBase::Upvar(_) = place.place.base else { return }; + let PlaceBase::Upvar(_) = place_with_id.place.base else { return }; // We need to restrict Fake Read precision to avoid fake reading unsafe code, // such as deref of a raw pointer. let dummy_capture_kind = ty::UpvarCapture::ByRef(ty::BorrowKind::Immutable); - let (place, _) = restrict_capture_precision(place.place.clone(), dummy_capture_kind); + let (place, _) = + restrict_capture_precision(place_with_id.place.clone(), dummy_capture_kind); let (place, _) = restrict_repr_packed_field_ref_capture(place, dummy_capture_kind); self.fake_reads.push((place, cause, diag_expr_id)); @@ -2008,11 +2013,14 @@ impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> { let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else { return }; assert_eq!(self.closure_def_id, upvar_id.closure_expr_id); - self.capture_information.push((place_with_id.place.clone(), ty::CaptureInfo { - capture_kind_expr_id: Some(diag_expr_id), - path_expr_id: Some(diag_expr_id), - capture_kind: ty::UpvarCapture::ByValue, - })); + self.capture_information.push(( + place_with_id.place.clone(), + ty::CaptureInfo { + capture_kind_expr_id: Some(diag_expr_id), + path_expr_id: Some(diag_expr_id), + capture_kind: ty::UpvarCapture::ByValue, + }, + )); } #[instrument(skip(self), level = "debug")] @@ -2035,15 +2043,18 @@ impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> { restrict_repr_packed_field_ref_capture(place_with_id.place.clone(), capture_kind); // Raw pointers don't inherit mutability - if place_with_id.place.deref_tys().any(Ty::is_unsafe_ptr) { + if place_with_id.place.deref_tys().any(Ty::is_raw_ptr) { capture_kind = ty::UpvarCapture::ByRef(ty::BorrowKind::Immutable); } - self.capture_information.push((place, ty::CaptureInfo { - capture_kind_expr_id: Some(diag_expr_id), - path_expr_id: Some(diag_expr_id), - capture_kind, - })); + self.capture_information.push(( + place, + ty::CaptureInfo { + capture_kind_expr_id: Some(diag_expr_id), + path_expr_id: Some(diag_expr_id), + capture_kind, + }, + )); } #[instrument(skip(self), level = "debug")] @@ -2083,7 +2094,7 @@ fn restrict_precision_for_unsafe( mut place: Place<'_>, mut curr_mode: ty::UpvarCapture, ) -> (Place<'_>, ty::UpvarCapture) { - if place.base_ty.is_unsafe_ptr() { + if place.base_ty.is_raw_ptr() { truncate_place_to_len_and_update_capture_kind(&mut place, &mut curr_mode, 0); } @@ -2092,8 +2103,8 @@ fn restrict_precision_for_unsafe( } for (i, proj) in place.projections.iter().enumerate() { - if proj.ty.is_unsafe_ptr() { - // Don't apply any projections on top of an unsafe ptr. + if proj.ty.is_raw_ptr() { + // Don't apply any projections on top of a raw ptr. truncate_place_to_len_and_update_capture_kind(&mut place, &mut curr_mode, i + 1); break; } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 5612aa75aae44..1bf5b19d68d0c 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -5,10 +5,9 @@ use std::mem; use rustc_data_structures::unord::ExtendUnord; -use rustc_errors::{ErrorGuaranteed, StashKey}; -use rustc_hir as hir; -use rustc_hir::HirId; -use rustc_hir::intravisit::{self, Visitor}; +use rustc_errors::ErrorGuaranteed; +use rustc_hir::intravisit::{self, InferKind, Visitor}; +use rustc_hir::{self as hir, AmbigArg, HirId}; use rustc_middle::span_bug; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion}; @@ -246,6 +245,13 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } } + + fn visit_const_block(&mut self, span: Span, anon_const: &hir::ConstBlock) { + self.visit_node_id(span, anon_const.hir_id); + + let body = self.tcx().hir().body(anon_const.body); + self.visit_body(body); + } } /////////////////////////////////////////////////////////////////////////// @@ -275,11 +281,8 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { hir::ExprKind::Field(..) | hir::ExprKind::OffsetOf(..) => { self.visit_field_id(e.hir_id); } - hir::ExprKind::ConstBlock(anon_const) => { - self.visit_node_id(e.span, anon_const.hir_id); - - let body = self.tcx().hir().body(anon_const.body); - self.visit_body(body); + hir::ExprKind::ConstBlock(ref anon_const) => { + self.visit_const_block(e.span, anon_const); } _ => {} } @@ -335,6 +338,14 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { intravisit::walk_pat(self, p); } + fn visit_pat_expr(&mut self, expr: &'tcx hir::PatExpr<'tcx>) { + self.visit_node_id(expr.span, expr.hir_id); + if let hir::PatExprKind::ConstBlock(c) = &expr.kind { + self.visit_const_block(expr.span, c); + } + intravisit::walk_pat_expr(self, expr); + } + fn visit_local(&mut self, l: &'tcx hir::LetStmt<'tcx>) { intravisit::walk_local(self, l); let var_ty = self.fcx.local_ty(l.span, l.hir_id); @@ -342,7 +353,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { self.write_ty_to_typeck_results(l.hir_id, var_ty); } - fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx, AmbigArg>) { intravisit::walk_ty(self, hir_ty); // If there are type checking errors, Type privacy pass will stop, // so we may not get the type from hid_id, see #104513 @@ -352,12 +363,20 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { } } - fn visit_infer(&mut self, inf: &'tcx hir::InferArg) { - intravisit::walk_inf(self, inf); - // Ignore cases where the inference is a const. - if let Some(ty) = self.fcx.node_ty_opt(inf.hir_id) { - let ty = self.resolve(ty, &inf.span); - self.write_ty_to_typeck_results(inf.hir_id, ty); + fn visit_infer( + &mut self, + inf_id: HirId, + inf_span: Span, + _kind: InferKind<'cx>, + ) -> Self::Result { + self.visit_id(inf_id); + + // We don't currently write inference results of const infer vars to + // the typeck results as there is not yet any part of the compiler that + // needs this information. + if let Some(ty) = self.fcx.node_ty_opt(inf_id) { + let ty = self.resolve(ty, &inf_span); + self.write_ty_to_typeck_results(inf_id, ty); } } } @@ -550,9 +569,9 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { // types or by using this function at the end of writeback and running it as a // fixpoint. let opaque_types = self.fcx.infcx.clone_opaque_types(); - for (opaque_type_key, decl) in opaque_types { - let hidden_type = self.resolve(decl.hidden_type, &decl.hidden_type.span); - let opaque_type_key = self.resolve(opaque_type_key, &decl.hidden_type.span); + for (opaque_type_key, hidden_type) in opaque_types { + let hidden_type = self.resolve(hidden_type, &hidden_type.span); + let opaque_type_key = self.resolve(opaque_type_key, &hidden_type.span); if !self.fcx.next_trait_solver() { if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind() @@ -570,15 +589,8 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { && last_opaque_ty.ty != hidden_type.ty { assert!(!self.fcx.next_trait_solver()); - if let Ok(d) = hidden_type.build_mismatch_error( - &last_opaque_ty, - opaque_type_key.def_id, - self.tcx(), - ) { - d.stash( - self.tcx().def_span(opaque_type_key.def_id), - StashKey::OpaqueHiddenTypeMismatch, - ); + if let Ok(d) = hidden_type.build_mismatch_error(&last_opaque_ty, self.tcx()) { + d.emit(); } } } diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index 6dab6468870a5..563ed7614c609 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -24,7 +24,7 @@ use rustc_middle::util::Providers; #[allow(missing_docs)] pub fn provide(providers: &mut Providers) { providers.hooks.save_dep_graph = - |tcx| tcx.sess.time("serialize_dep_graph", || persist::save_dep_graph(tcx.tcx)); + |tcx| tcx.sess.time("serialize_dep_graph", || persist::save_dep_graph(tcx)); } rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index b99872e7ae674..30590bf5d3cb1 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -107,11 +107,10 @@ const LABELS_FN_IN_TRAIT: &[&[&str]] = const LABELS_HIR_ONLY: &[&[&str]] = &[BASE_HIR]; /// Impl `DepNode`s. -const LABELS_TRAIT: &[&[&str]] = &[BASE_HIR, &[ - label_strs::associated_item_def_ids, - label_strs::predicates_of, - label_strs::generics_of, -]]; +const LABELS_TRAIT: &[&[&str]] = &[ + BASE_HIR, + &[label_strs::associated_item_def_ids, label_strs::predicates_of, label_strs::generics_of], +]; /// Impl `DepNode`s. const LABELS_IMPL: &[&[&str]] = &[BASE_HIR, BASE_IMPL]; @@ -253,7 +252,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { HirItem::Const(..) => ("ItemConst", LABELS_CONST), // A function declaration - HirItem::Fn(..) => ("ItemFn", LABELS_FN), + HirItem::Fn { .. } => ("ItemFn", LABELS_FN), // // A module HirItem::Mod(..) => ("ItemMod", LABELS_HIR_ONLY), diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 38e2dbbde7d07..f12df831cb503 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -97,7 +97,13 @@ macro_rules! bit_relations_inherent_impls { /// A fixed-size bitset type with a dense representation. /// -/// NOTE: Use [`GrowableBitSet`] if you need support for resizing after creation. +/// Note 1: Since this bitset is dense, if your domain is big, and/or relatively +/// homogeneous (for example, with long runs of bits set or unset), then it may +/// be preferable to instead use a [MixedBitSet], or an +/// [IntervalSet](crate::interval::IntervalSet). They should be more suited to +/// sparse, or highly-compressible, domains. +/// +/// Note 2: Use [`GrowableBitSet`] if you need support for resizing after creation. /// /// `T` is an index type, typically a newtyped `usize` wrapper, but it can also /// just be `usize`. @@ -108,33 +114,33 @@ macro_rules! bit_relations_inherent_impls { /// #[cfg_attr(feature = "nightly", derive(Decodable_Generic, Encodable_Generic))] #[derive(Eq, PartialEq, Hash)] -pub struct BitSet { +pub struct DenseBitSet { domain_size: usize, words: SmallVec<[Word; 2]>, marker: PhantomData, } -impl BitSet { +impl DenseBitSet { /// Gets the domain size. pub fn domain_size(&self) -> usize { self.domain_size } } -impl BitSet { +impl DenseBitSet { /// Creates a new, empty bitset with a given `domain_size`. #[inline] - pub fn new_empty(domain_size: usize) -> BitSet { + pub fn new_empty(domain_size: usize) -> DenseBitSet { let num_words = num_words(domain_size); - BitSet { domain_size, words: smallvec![0; num_words], marker: PhantomData } + DenseBitSet { domain_size, words: smallvec![0; num_words], marker: PhantomData } } /// Creates a new, filled bitset with a given `domain_size`. #[inline] - pub fn new_filled(domain_size: usize) -> BitSet { + pub fn new_filled(domain_size: usize) -> DenseBitSet { let num_words = num_words(domain_size); let mut result = - BitSet { domain_size, words: smallvec![!0; num_words], marker: PhantomData }; + DenseBitSet { domain_size, words: smallvec![!0; num_words], marker: PhantomData }; result.clear_excess_bits(); result } @@ -165,7 +171,7 @@ impl BitSet { /// Is `self` is a (non-strict) superset of `other`? #[inline] - pub fn superset(&self, other: &BitSet) -> bool { + pub fn superset(&self, other: &DenseBitSet) -> bool { assert_eq!(self.domain_size, other.domain_size); self.words.iter().zip(&other.words).all(|(a, b)| (a & b) == *b) } @@ -275,35 +281,57 @@ impl BitSet { } bit_relations_inherent_impls! {} + + /// Sets `self = self | !other`. + /// + /// FIXME: Incorporate this into [`BitRelations`] and fill out + /// implementations for other bitset types, if needed. + pub fn union_not(&mut self, other: &DenseBitSet) { + assert_eq!(self.domain_size, other.domain_size); + + // FIXME(Zalathar): If we were to forcibly _set_ all excess bits before + // the bitwise update, and then clear them again afterwards, we could + // quickly and accurately detect whether the update changed anything. + // But that's only worth doing if there's an actual use-case. + + bitwise(&mut self.words, &other.words, |a, b| a | !b); + // The bitwise update `a | !b` can result in the last word containing + // out-of-domain bits, so we need to clear them. + self.clear_excess_bits(); + } } // dense REL dense -impl BitRelations> for BitSet { - fn union(&mut self, other: &BitSet) -> bool { +impl BitRelations> for DenseBitSet { + fn union(&mut self, other: &DenseBitSet) -> bool { assert_eq!(self.domain_size, other.domain_size); bitwise(&mut self.words, &other.words, |a, b| a | b) } - fn subtract(&mut self, other: &BitSet) -> bool { + fn subtract(&mut self, other: &DenseBitSet) -> bool { assert_eq!(self.domain_size, other.domain_size); bitwise(&mut self.words, &other.words, |a, b| a & !b) } - fn intersect(&mut self, other: &BitSet) -> bool { + fn intersect(&mut self, other: &DenseBitSet) -> bool { assert_eq!(self.domain_size, other.domain_size); bitwise(&mut self.words, &other.words, |a, b| a & b) } } -impl From> for BitSet { +impl From> for DenseBitSet { fn from(bit_set: GrowableBitSet) -> Self { bit_set.bit_set } } -impl Clone for BitSet { +impl Clone for DenseBitSet { fn clone(&self) -> Self { - BitSet { domain_size: self.domain_size, words: self.words.clone(), marker: PhantomData } + DenseBitSet { + domain_size: self.domain_size, + words: self.words.clone(), + marker: PhantomData, + } } fn clone_from(&mut self, from: &Self) { @@ -312,13 +340,13 @@ impl Clone for BitSet { } } -impl fmt::Debug for BitSet { +impl fmt::Debug for DenseBitSet { fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result { w.debug_list().entries(self.iter()).finish() } } -impl ToString for BitSet { +impl ToString for DenseBitSet { fn to_string(&self) -> String { let mut result = String::new(); let mut sep = '['; @@ -902,7 +930,7 @@ impl BitRelations> for ChunkedBitSet { } } -impl BitRelations> for BitSet { +impl BitRelations> for DenseBitSet { fn union(&mut self, other: &ChunkedBitSet) -> bool { sequential_update(|elem| self.insert(elem), other.iter()) } @@ -1077,6 +1105,18 @@ impl fmt::Debug for ChunkedBitSet { } } +/// Sets `out_vec[i] = op(out_vec[i], in_vec[i])` for each index `i` in both +/// slices. The slices must have the same length. +/// +/// Returns true if at least one bit in `out_vec` was changed. +/// +/// ## Warning +/// Some bitwise operations (e.g. union-not, xor) can set output bits that were +/// unset in in both inputs. If this happens in the last word/chunk of a bitset, +/// it can cause the bitset to contain out-of-domain values, which need to +/// be cleared with `clear_excess_bits_in_final_word`. This also makes the +/// "changed" return value unreliable, because the change might have only +/// affected excess bits. #[inline] fn bitwise(out_vec: &mut [Word], in_vec: &[Word], op: Op) -> bool where @@ -1114,10 +1154,10 @@ where false } -/// A bitset with a mixed representation, using `BitSet` for small and medium -/// bitsets, and `ChunkedBitSet` for large bitsets, i.e. those with enough bits -/// for at least two chunks. This is a good choice for many bitsets that can -/// have large domain sizes (e.g. 5000+). +/// A bitset with a mixed representation, using `DenseBitSet` for small and +/// medium bitsets, and `ChunkedBitSet` for large bitsets, i.e. those with +/// enough bits for at least two chunks. This is a good choice for many bitsets +/// that can have large domain sizes (e.g. 5000+). /// /// `T` is an index type, typically a newtyped `usize` wrapper, but it can also /// just be `usize`. @@ -1127,7 +1167,7 @@ where /// will panic if the bitsets have differing domain sizes. #[derive(PartialEq, Eq)] pub enum MixedBitSet { - Small(BitSet), + Small(DenseBitSet), Large(ChunkedBitSet), } @@ -1144,7 +1184,7 @@ impl MixedBitSet { #[inline] pub fn new_empty(domain_size: usize) -> MixedBitSet { if domain_size <= CHUNK_BITS { - MixedBitSet::Small(BitSet::new_empty(domain_size)) + MixedBitSet::Small(DenseBitSet::new_empty(domain_size)) } else { MixedBitSet::Large(ChunkedBitSet::new_empty(domain_size)) } @@ -1283,7 +1323,7 @@ impl<'a, T: Idx> Iterator for MixedBitIter<'a, T> { /// to or greater than the domain size. #[derive(Clone, Debug, PartialEq)] pub struct GrowableBitSet { - bit_set: BitSet, + bit_set: DenseBitSet, } impl Default for GrowableBitSet { @@ -1306,11 +1346,11 @@ impl GrowableBitSet { } pub fn new_empty() -> GrowableBitSet { - GrowableBitSet { bit_set: BitSet::new_empty(0) } + GrowableBitSet { bit_set: DenseBitSet::new_empty(0) } } pub fn with_capacity(capacity: usize) -> GrowableBitSet { - GrowableBitSet { bit_set: BitSet::new_empty(capacity) } + GrowableBitSet { bit_set: DenseBitSet::new_empty(capacity) } } /// Returns `true` if the set has changed. @@ -1349,8 +1389,8 @@ impl GrowableBitSet { } } -impl From> for GrowableBitSet { - fn from(bit_set: BitSet) -> Self { +impl From> for GrowableBitSet { + fn from(bit_set: DenseBitSet) -> Self { Self { bit_set } } } @@ -1386,7 +1426,7 @@ impl BitMatrix { } /// Creates a new matrix, with `row` used as the value for every row. - pub fn from_row_n(row: &BitSet, num_rows: usize) -> BitMatrix { + pub fn from_row_n(row: &DenseBitSet, num_rows: usize) -> BitMatrix { let num_columns = row.domain_size(); let words_per_row = num_words(num_columns); assert_eq!(words_per_row, row.words.len()); @@ -1484,7 +1524,7 @@ impl BitMatrix { /// Adds the bits from `with` to the bits from row `write`, and /// returns `true` if anything changed. - pub fn union_row_with(&mut self, with: &BitSet, write: R) -> bool { + pub fn union_row_with(&mut self, with: &DenseBitSet, write: R) -> bool { assert!(write.index() < self.num_rows); assert_eq!(with.domain_size(), self.num_columns); let (write_start, write_end) = self.range(write); @@ -1541,8 +1581,8 @@ impl fmt::Debug for BitMatrix { /// A fixed-column-size, variable-row-size 2D bit matrix with a moderately /// sparse representation. /// -/// Initially, every row has no explicit representation. If any bit within a -/// row is set, the entire row is instantiated as `Some()`. +/// Initially, every row has no explicit representation. If any bit within a row +/// is set, the entire row is instantiated as `Some()`. /// Furthermore, any previously uninstantiated rows prior to it will be /// instantiated as `None`. Those prior rows may themselves become fully /// instantiated later on if any of their bits are set. @@ -1556,7 +1596,7 @@ where C: Idx, { num_columns: usize, - rows: IndexVec>>, + rows: IndexVec>>, } impl SparseBitMatrix { @@ -1565,10 +1605,10 @@ impl SparseBitMatrix { Self { num_columns, rows: IndexVec::new() } } - fn ensure_row(&mut self, row: R) -> &mut BitSet { - // Instantiate any missing rows up to and including row `row` with an empty `BitSet`. - // Then replace row `row` with a full `BitSet` if necessary. - self.rows.get_or_insert_with(row, || BitSet::new_empty(self.num_columns)) + fn ensure_row(&mut self, row: R) -> &mut DenseBitSet { + // Instantiate any missing rows up to and including row `row` with an empty `DenseBitSet`. + // Then replace row `row` with a full `DenseBitSet` if necessary. + self.rows.get_or_insert_with(row, || DenseBitSet::new_empty(self.num_columns)) } /// Sets the cell at `(row, column)` to true. Put another way, insert @@ -1642,17 +1682,17 @@ impl SparseBitMatrix { self.row(row).into_iter().flat_map(|r| r.iter()) } - pub fn row(&self, row: R) -> Option<&BitSet> { + pub fn row(&self, row: R) -> Option<&DenseBitSet> { self.rows.get(row)?.as_ref() } - /// Intersects `row` with `set`. `set` can be either `BitSet` or + /// Intersects `row` with `set`. `set` can be either `DenseBitSet` or /// `ChunkedBitSet`. Has no effect if `row` does not exist. /// /// Returns true if the row was changed. pub fn intersect_row(&mut self, row: R, set: &Set) -> bool where - BitSet: BitRelations, + DenseBitSet: BitRelations, { match self.rows.get_mut(row) { Some(Some(row)) => row.intersect(set), @@ -1660,13 +1700,13 @@ impl SparseBitMatrix { } } - /// Subtracts `set` from `row`. `set` can be either `BitSet` or + /// Subtracts `set` from `row`. `set` can be either `DenseBitSet` or /// `ChunkedBitSet`. Has no effect if `row` does not exist. /// /// Returns true if the row was changed. pub fn subtract_row(&mut self, row: R, set: &Set) -> bool where - BitSet: BitRelations, + DenseBitSet: BitRelations, { match self.rows.get_mut(row) { Some(Some(row)) => row.subtract(set), @@ -1674,13 +1714,13 @@ impl SparseBitMatrix { } } - /// Unions `row` with `set`. `set` can be either `BitSet` or + /// Unions `row` with `set`. `set` can be either `DenseBitSet` or /// `ChunkedBitSet`. /// /// Returns true if the row was changed. pub fn union_row(&mut self, row: R, set: &Set) -> bool where - BitSet: BitRelations, + DenseBitSet: BitRelations, { self.ensure_row(row).union(set) } diff --git a/compiler/rustc_index/src/bit_set/tests.rs b/compiler/rustc_index/src/bit_set/tests.rs index f614232397935..323a66ddc6f20 100644 --- a/compiler/rustc_index/src/bit_set/tests.rs +++ b/compiler/rustc_index/src/bit_set/tests.rs @@ -8,7 +8,7 @@ use test::Bencher; #[test] fn test_new_filled() { for i in 0..128 { - let idx_buf = BitSet::new_filled(i); + let idx_buf = DenseBitSet::new_filled(i); let elems: Vec = idx_buf.iter().collect(); let expected: Vec = (0..i).collect(); assert_eq!(elems, expected); @@ -17,7 +17,7 @@ fn test_new_filled() { #[test] fn bitset_iter_works() { - let mut bitset: BitSet = BitSet::new_empty(100); + let mut bitset: DenseBitSet = DenseBitSet::new_empty(100); bitset.insert(1); bitset.insert(10); bitset.insert(19); @@ -32,7 +32,7 @@ fn bitset_iter_works() { #[test] fn bitset_iter_works_2() { - let mut bitset: BitSet = BitSet::new_empty(320); + let mut bitset: DenseBitSet = DenseBitSet::new_empty(320); bitset.insert(0); bitset.insert(127); bitset.insert(191); @@ -43,25 +43,25 @@ fn bitset_iter_works_2() { #[test] fn bitset_clone_from() { - let mut a: BitSet = BitSet::new_empty(10); + let mut a: DenseBitSet = DenseBitSet::new_empty(10); a.insert(4); a.insert(7); a.insert(9); - let mut b = BitSet::new_empty(2); + let mut b = DenseBitSet::new_empty(2); b.clone_from(&a); assert_eq!(b.domain_size(), 10); assert_eq!(b.iter().collect::>(), [4, 7, 9]); - b.clone_from(&BitSet::new_empty(40)); + b.clone_from(&DenseBitSet::new_empty(40)); assert_eq!(b.domain_size(), 40); assert_eq!(b.iter().collect::>(), []); } #[test] fn union_two_sets() { - let mut set1: BitSet = BitSet::new_empty(65); - let mut set2: BitSet = BitSet::new_empty(65); + let mut set1: DenseBitSet = DenseBitSet::new_empty(65); + let mut set2: DenseBitSet = DenseBitSet::new_empty(65); assert!(set1.insert(3)); assert!(!set1.insert(3)); assert!(set2.insert(5)); @@ -75,6 +75,32 @@ fn union_two_sets() { assert!(set1.contains(64)); } +#[test] +fn union_not() { + let mut a = DenseBitSet::::new_empty(100); + let mut b = DenseBitSet::::new_empty(100); + + a.insert(3); + a.insert(5); + a.insert(80); + a.insert(81); + + b.insert(5); // Already in `a`. + b.insert(7); + b.insert(63); + b.insert(81); // Already in `a`. + b.insert(90); + + a.union_not(&b); + + // After union-not, `a` should contain all values in the domain, except for + // the ones that are in `b` and were _not_ already in `a`. + assert_eq!( + a.iter().collect::>(), + (0usize..100).filter(|&x| !matches!(x, 7 | 63 | 90)).collect::>(), + ); +} + #[test] fn chunked_bitset() { let mut b0 = ChunkedBitSet::::new_empty(0); @@ -92,11 +118,10 @@ fn chunked_bitset() { //----------------------------------------------------------------------- let mut b1 = ChunkedBitSet::::new_empty(1); - assert_eq!(b1, ChunkedBitSet { - domain_size: 1, - chunks: Box::new([Zeros(1)]), - marker: PhantomData - }); + assert_eq!( + b1, + ChunkedBitSet { domain_size: 1, chunks: Box::new([Zeros(1)]), marker: PhantomData } + ); b1.assert_valid(); assert!(!b1.contains(0)); @@ -115,11 +140,10 @@ fn chunked_bitset() { //----------------------------------------------------------------------- let mut b100 = ChunkedBitSet::::new_filled(100); - assert_eq!(b100, ChunkedBitSet { - domain_size: 100, - chunks: Box::new([Ones(100)]), - marker: PhantomData - }); + assert_eq!( + b100, + ChunkedBitSet { domain_size: 100, chunks: Box::new([Ones(100)]), marker: PhantomData } + ); b100.assert_valid(); for i in 0..100 { @@ -134,17 +158,20 @@ fn chunked_bitset() { ); assert_eq!(b100.count(), 97); assert!(!b100.contains(20) && b100.contains(30) && !b100.contains(99) && b100.contains(50)); - assert_eq!(b100.chunks(), vec![Mixed( - 100, - 97, - #[rustfmt::skip] + assert_eq!( + b100.chunks(), + vec![Mixed( + 100, + 97, + #[rustfmt::skip] Rc::new([ 0b11111111_11111111_11111110_11111111_11111111_11101111_11111111_11111111, 0b00000000_00000000_00000000_00000111_11111111_11111111_11111111_11111111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]) - )],); + )], + ); b100.assert_valid(); let mut num_removed = 0; for i in 0..100 { @@ -159,11 +186,14 @@ fn chunked_bitset() { //----------------------------------------------------------------------- let mut b2548 = ChunkedBitSet::::new_empty(2548); - assert_eq!(b2548, ChunkedBitSet { - domain_size: 2548, - chunks: Box::new([Zeros(2048), Zeros(500)]), - marker: PhantomData, - }); + assert_eq!( + b2548, + ChunkedBitSet { + domain_size: 2548, + chunks: Box::new([Zeros(2048), Zeros(500)]), + marker: PhantomData, + } + ); b2548.assert_valid(); b2548.insert(14); @@ -180,11 +210,14 @@ fn chunked_bitset() { //----------------------------------------------------------------------- let mut b4096 = ChunkedBitSet::::new_empty(4096); - assert_eq!(b4096, ChunkedBitSet { - domain_size: 4096, - chunks: Box::new([Zeros(2048), Zeros(2048)]), - marker: PhantomData, - }); + assert_eq!( + b4096, + ChunkedBitSet { + domain_size: 4096, + chunks: Box::new([Zeros(2048), Zeros(2048)]), + marker: PhantomData, + } + ); b4096.assert_valid(); for i in 0..4096 { @@ -214,11 +247,14 @@ fn chunked_bitset() { //----------------------------------------------------------------------- let mut b10000 = ChunkedBitSet::::new_empty(10000); - assert_eq!(b10000, ChunkedBitSet { - domain_size: 10000, - chunks: Box::new([Zeros(2048), Zeros(2048), Zeros(2048), Zeros(2048), Zeros(1808),]), - marker: PhantomData, - }); + assert_eq!( + b10000, + ChunkedBitSet { + domain_size: 10000, + chunks: Box::new([Zeros(2048), Zeros(2048), Zeros(2048), Zeros(2048), Zeros(1808),]), + marker: PhantomData, + } + ); b10000.assert_valid(); assert!(b10000.insert(3000) && b10000.insert(5000)); @@ -268,8 +304,8 @@ fn with_elements_chunked(elements: &[usize], domain_size: usize) -> ChunkedBitSe s } -fn with_elements_standard(elements: &[usize], domain_size: usize) -> BitSet { - let mut s = BitSet::new_empty(domain_size); +fn with_elements_standard(elements: &[usize], domain_size: usize) -> DenseBitSet { + let mut s = DenseBitSet::new_empty(domain_size); for &e in elements { assert!(s.insert(e)); } @@ -503,15 +539,15 @@ fn sparse_matrix_operations() { matrix.insert(2, 99); matrix.insert(4, 0); - let mut disjoint: BitSet = BitSet::new_empty(100); + let mut disjoint: DenseBitSet = DenseBitSet::new_empty(100); disjoint.insert(33); - let mut superset = BitSet::new_empty(100); + let mut superset = DenseBitSet::new_empty(100); superset.insert(22); superset.insert(75); superset.insert(33); - let mut subset = BitSet::new_empty(100); + let mut subset = DenseBitSet::new_empty(100); subset.insert(22); // SparseBitMatrix::remove @@ -568,7 +604,7 @@ fn dense_insert_range() { where R: RangeBounds + Clone + IntoIterator + std::fmt::Debug, { - let mut set = BitSet::new_empty(domain); + let mut set = DenseBitSet::new_empty(domain); set.insert_range(range.clone()); for i in set.iter() { assert!(range.contains(&i)); @@ -609,7 +645,7 @@ fn dense_insert_range() { #[test] fn dense_last_set_before() { - fn easy(set: &BitSet, needle: impl RangeBounds) -> Option { + fn easy(set: &DenseBitSet, needle: impl RangeBounds) -> Option { let mut last_leq = None; for e in set.iter() { if needle.contains(&e) { @@ -620,7 +656,7 @@ fn dense_last_set_before() { } #[track_caller] - fn cmp(set: &BitSet, needle: impl RangeBounds + Clone + std::fmt::Debug) { + fn cmp(set: &DenseBitSet, needle: impl RangeBounds + Clone + std::fmt::Debug) { assert_eq!( set.last_set_in(needle.clone()), easy(set, needle.clone()), @@ -629,7 +665,7 @@ fn dense_last_set_before() { set ); } - let mut set = BitSet::new_empty(300); + let mut set = DenseBitSet::new_empty(300); cmp(&set, 50..=50); set.insert(WORD_BITS); cmp(&set, WORD_BITS..=WORD_BITS); @@ -645,7 +681,7 @@ fn dense_last_set_before() { for i in 0..=WORD_BITS * 2 { for j in i..=WORD_BITS * 2 { for k in 0..WORD_BITS * 2 { - let mut set = BitSet::new_empty(300); + let mut set = DenseBitSet::new_empty(300); cmp(&set, i..j); cmp(&set, i..=j); set.insert(k); @@ -658,7 +694,7 @@ fn dense_last_set_before() { #[bench] fn bench_insert(b: &mut Bencher) { - let mut bs = BitSet::new_filled(99999usize); + let mut bs = DenseBitSet::new_filled(99999usize); b.iter(|| { black_box(bs.insert(black_box(100u32))); }); @@ -666,7 +702,7 @@ fn bench_insert(b: &mut Bencher) { #[bench] fn bench_remove(b: &mut Bencher) { - let mut bs = BitSet::new_filled(99999usize); + let mut bs = DenseBitSet::new_filled(99999usize); b.iter(|| { black_box(bs.remove(black_box(100u32))); }); @@ -674,7 +710,7 @@ fn bench_remove(b: &mut Bencher) { #[bench] fn bench_iter(b: &mut Bencher) { - let bs = BitSet::new_filled(99999usize); + let bs = DenseBitSet::new_filled(99999usize); b.iter(|| { bs.iter().map(|b: usize| black_box(b)).for_each(drop); }); @@ -682,8 +718,8 @@ fn bench_iter(b: &mut Bencher) { #[bench] fn bench_intersect(b: &mut Bencher) { - let mut ba: BitSet = BitSet::new_filled(99999usize); - let bb = BitSet::new_filled(99999usize); + let mut ba: DenseBitSet = DenseBitSet::new_filled(99999usize); + let bb = DenseBitSet::new_filled(99999usize); b.iter(|| { ba.intersect(black_box(&bb)); }); diff --git a/compiler/rustc_index/src/interval.rs b/compiler/rustc_index/src/interval.rs index 34f541a8cc639..a3d87f59567fe 100644 --- a/compiler/rustc_index/src/interval.rs +++ b/compiler/rustc_index/src/interval.rs @@ -258,7 +258,7 @@ impl IntervalSet { } current = Some(*end); } - current.map_or(true, |x| x < self.domain as u32) + current.is_none_or(|x| x < self.domain as u32) } } diff --git a/compiler/rustc_index/src/vec/tests.rs b/compiler/rustc_index/src/vec/tests.rs index 9cee2f2f5b23b..381d79c24fcba 100644 --- a/compiler/rustc_index/src/vec/tests.rs +++ b/compiler/rustc_index/src/vec/tests.rs @@ -29,21 +29,19 @@ fn index_size_is_optimized() { #[test] fn range_iterator_iterates_forwards() { let range = MyIdx::from_u32(1)..MyIdx::from_u32(4); - assert_eq!(range.collect::>(), [ - MyIdx::from_u32(1), - MyIdx::from_u32(2), - MyIdx::from_u32(3) - ]); + assert_eq!( + range.collect::>(), + [MyIdx::from_u32(1), MyIdx::from_u32(2), MyIdx::from_u32(3)] + ); } #[test] fn range_iterator_iterates_backwards() { let range = MyIdx::from_u32(1)..MyIdx::from_u32(4); - assert_eq!(range.rev().collect::>(), [ - MyIdx::from_u32(3), - MyIdx::from_u32(2), - MyIdx::from_u32(1) - ]); + assert_eq!( + range.rev().collect::>(), + [MyIdx::from_u32(3), MyIdx::from_u32(2), MyIdx::from_u32(1)] + ); } #[test] diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 12e2bbc968f0f..2cd67cc4da213 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -137,6 +137,7 @@ impl<'a, 'tcx> At<'a, 'tcx> { expected, ty::Contravariant, actual, + self.cause.span, ) .map(|goals| self.goals_to_obligations(goals)) } else { @@ -163,8 +164,15 @@ impl<'a, 'tcx> At<'a, 'tcx> { T: ToTrace<'tcx>, { if self.infcx.next_trait_solver { - NextSolverRelate::relate(self.infcx, self.param_env, expected, ty::Covariant, actual) - .map(|goals| self.goals_to_obligations(goals)) + NextSolverRelate::relate( + self.infcx, + self.param_env, + expected, + ty::Covariant, + actual, + self.cause.span, + ) + .map(|goals| self.goals_to_obligations(goals)) } else { let mut op = TypeRelating::new( self.infcx, @@ -208,8 +216,15 @@ impl<'a, 'tcx> At<'a, 'tcx> { T: Relate>, { if self.infcx.next_trait_solver { - NextSolverRelate::relate(self.infcx, self.param_env, expected, ty::Invariant, actual) - .map(|goals| self.goals_to_obligations(goals)) + NextSolverRelate::relate( + self.infcx, + self.param_env, + expected, + ty::Invariant, + actual, + self.cause.span, + ) + .map(|goals| self.goals_to_obligations(goals)) } else { let mut op = TypeRelating::new( self.infcx, @@ -402,6 +417,18 @@ impl<'tcx> ToTrace<'tcx> for ty::PolyExistentialTraitRef<'tcx> { } } +impl<'tcx> ToTrace<'tcx> for ty::ExistentialTraitRef<'tcx> { + fn to_trace(cause: &ObligationCause<'tcx>, a: Self, b: Self) -> TypeTrace<'tcx> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::ExistentialTraitRef(ExpectedFound::new( + ty::Binder::dummy(a), + ty::Binder::dummy(b), + )), + } + } +} + impl<'tcx> ToTrace<'tcx> for ty::PolyExistentialProjection<'tcx> { fn to_trace(cause: &ObligationCause<'tcx>, a: Self, b: Self) -> TypeTrace<'tcx> { TypeTrace { @@ -410,3 +437,15 @@ impl<'tcx> ToTrace<'tcx> for ty::PolyExistentialProjection<'tcx> { } } } + +impl<'tcx> ToTrace<'tcx> for ty::ExistentialProjection<'tcx> { + fn to_trace(cause: &ObligationCause<'tcx>, a: Self, b: Self) -> TypeTrace<'tcx> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::ExistentialProjection(ExpectedFound::new( + ty::Binder::dummy(a), + ty::Binder::dummy(b), + )), + } + } +} diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index d5aab4781de86..23f63af778d07 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -155,12 +155,12 @@ impl<'tcx> InferCtxt<'tcx> { .opaque_type_storage .opaque_types .iter() - .map(|(k, v)| (*k, v.hidden_type.ty)) + .map(|(k, v)| (*k, v.ty)) .collect() } fn take_opaque_types_for_query_response(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> { - self.take_opaque_types().into_iter().map(|(k, v)| (k, v.hidden_type.ty)).collect() + self.take_opaque_types().into_iter().map(|(k, v)| (k, v.ty)).collect() } /// Given the (canonicalized) result to a canonical query, diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index 5fc9b679c8acf..eae69ec3e0fe8 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -5,7 +5,7 @@ use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::relate::combine::PredicateEmittingRelation; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::{DUMMY_SP, ErrorGuaranteed}; +use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use super::{BoundRegionConversionTime, InferCtxt, RegionVariableOrigin, SubregionOrigin}; @@ -114,7 +114,7 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { ) } - fn enter_forall> + Copy, U>( + fn enter_forall>, U>( &self, value: ty::Binder<'tcx, T>, f: impl FnOnce(T) -> U, @@ -203,23 +203,23 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { self.probe(|_| probe()) } - fn sub_regions(&self, sub: ty::Region<'tcx>, sup: ty::Region<'tcx>) { + fn sub_regions(&self, sub: ty::Region<'tcx>, sup: ty::Region<'tcx>, span: Span) { self.inner.borrow_mut().unwrap_region_constraints().make_subregion( - SubregionOrigin::RelateRegionParamBound(DUMMY_SP, None), + SubregionOrigin::RelateRegionParamBound(span, None), sub, sup, ); } - fn equate_regions(&self, a: ty::Region<'tcx>, b: ty::Region<'tcx>) { + fn equate_regions(&self, a: ty::Region<'tcx>, b: ty::Region<'tcx>, span: Span) { self.inner.borrow_mut().unwrap_region_constraints().make_eqregion( - SubregionOrigin::RelateRegionParamBound(DUMMY_SP, None), + SubregionOrigin::RelateRegionParamBound(span, None), a, b, ); } - fn register_ty_outlives(&self, ty: Ty<'tcx>, r: ty::Region<'tcx>) { - self.register_region_obligation_with_cause(ty, r, &ObligationCause::dummy()); + fn register_ty_outlives(&self, ty: Ty<'tcx>, r: ty::Region<'tcx>, span: Span) { + self.register_region_obligation_with_cause(ty, r, &ObligationCause::dummy_with_span(span)); } } diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index 28eac5b7496dd..74c8b463fc899 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -170,7 +170,7 @@ impl<'a, 'tcx> TypeFolder> for TypeFreshener<'a, 'tcx> { } ty::ConstKind::Param(_) - | ty::ConstKind::Value(_, _) + | ty::ConstKind::Value(_) | ty::ConstKind::Unevaluated(..) | ty::ConstKind::Expr(..) | ty::ConstKind::Error(_) => ct.super_fold_with(self), diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 1f7180fb80a0c..c2513a1af1988 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -26,7 +26,6 @@ use rustc_macros::extension; pub use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::bug; use rustc_middle::infer::canonical::{CanonicalQueryInput, CanonicalVarValues}; -use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::select; pub use rustc_middle::ty::IntVarValue; @@ -46,6 +45,7 @@ use tracing::{debug, instrument}; use type_variable::TypeVariableOrigin; use crate::infer::region_constraints::UndoLog; +use crate::infer::unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}; use crate::traits::{ self, ObligationCause, ObligationInspector, PredicateObligations, TraitEngine, }; @@ -64,6 +64,7 @@ pub mod relate; pub mod resolve; pub(crate) mod snapshot; mod type_variable; +mod unify_key; /// `InferOk<'tcx, ()>` is used a lot. It may seem like a useless wrapper /// around `PredicateObligations<'tcx>`, but it has one important property: @@ -234,7 +235,7 @@ impl<'tcx> InferCtxtInner<'tcx> { pub fn iter_opaque_types( &self, ) -> impl Iterator, ty::OpaqueHiddenType<'tcx>)> + '_ { - self.opaque_type_storage.opaque_types.iter().map(|(&k, v)| (k, v.hidden_type)) + self.opaque_type_storage.opaque_types.iter().map(|(&k, &v)| (k, v)) } } @@ -1055,7 +1056,7 @@ impl<'tcx> InferCtxt<'tcx> { | ty::ConstKind::Bound(_, _) | ty::ConstKind::Placeholder(_) | ty::ConstKind::Unevaluated(_) - | ty::ConstKind::Value(_, _) + | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) | ty::ConstKind::Expr(_) => ct, } diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs index 137d438a47955..f6ef3f40e622d 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -19,20 +19,9 @@ use crate::traits::{self, Obligation, PredicateObligations}; mod table; -pub(crate) type OpaqueTypeMap<'tcx> = FxIndexMap, OpaqueTypeDecl<'tcx>>; +pub(crate) type OpaqueTypeMap<'tcx> = FxIndexMap, OpaqueHiddenType<'tcx>>; pub(crate) use table::{OpaqueTypeStorage, OpaqueTypeTable}; -/// Information about the opaque types whose values we -/// are inferring in this function (these are the `impl Trait` that -/// appear in the return type). -#[derive(Clone, Debug)] -pub struct OpaqueTypeDecl<'tcx> { - /// The hidden types that have been inferred for this opaque type. - /// There can be multiple, but they are all `lub`ed together at the end - /// to obtain the canonical hidden type. - pub hidden_type: OpaqueHiddenType<'tcx>, -} - impl<'tcx> InferCtxt<'tcx> { /// This is a backwards compatibility hack to prevent breaking changes from /// lazy TAIT around RPIT handling. diff --git a/compiler/rustc_infer/src/infer/opaque_types/table.rs b/compiler/rustc_infer/src/infer/opaque_types/table.rs index 047d8edad3de7..ba6cc0d783dd3 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/table.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/table.rs @@ -3,7 +3,7 @@ use rustc_middle::bug; use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, Ty}; use tracing::instrument; -use super::{OpaqueTypeDecl, OpaqueTypeMap}; +use super::OpaqueTypeMap; use crate::infer::snapshot::undo_log::{InferCtxtUndoLogs, UndoLog}; #[derive(Default, Debug, Clone)] @@ -11,15 +11,19 @@ pub(crate) struct OpaqueTypeStorage<'tcx> { /// Opaque types found in explicit return types and their /// associated fresh inference variable. Writeback resolves these /// variables to get the concrete type, which can be used to - /// 'de-opaque' OpaqueTypeDecl, after typeck is done with all functions. + /// 'de-opaque' OpaqueHiddenType, after typeck is done with all functions. pub opaque_types: OpaqueTypeMap<'tcx>, } impl<'tcx> OpaqueTypeStorage<'tcx> { #[instrument(level = "debug")] - pub(crate) fn remove(&mut self, key: OpaqueTypeKey<'tcx>, idx: Option>) { - if let Some(idx) = idx { - self.opaque_types.get_mut(&key).unwrap().hidden_type = idx; + pub(crate) fn remove( + &mut self, + key: OpaqueTypeKey<'tcx>, + prev: Option>, + ) { + if let Some(prev) = prev { + *self.opaque_types.get_mut(&key).unwrap() = prev; } else { // FIXME(#120456) - is `swap_remove` correct? match self.opaque_types.swap_remove(&key) { @@ -59,13 +63,12 @@ impl<'a, 'tcx> OpaqueTypeTable<'a, 'tcx> { key: OpaqueTypeKey<'tcx>, hidden_type: OpaqueHiddenType<'tcx>, ) -> Option> { - if let Some(decl) = self.storage.opaque_types.get_mut(&key) { - let prev = std::mem::replace(&mut decl.hidden_type, hidden_type); + if let Some(entry) = self.storage.opaque_types.get_mut(&key) { + let prev = std::mem::replace(entry, hidden_type); self.undo_log.push(UndoLog::OpaqueTypes(key, Some(prev))); return Some(prev.ty); } - let decl = OpaqueTypeDecl { hidden_type }; - self.storage.opaque_types.insert(key, decl); + self.storage.opaque_types.insert(key, hidden_type); self.undo_log.push(UndoLog::OpaqueTypes(key, None)); None } diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index 9300fc574dc29..e924c974a02c9 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -31,26 +31,14 @@ use crate::traits::query::OutlivesBound; pub struct OutlivesEnvironment<'tcx> { pub param_env: ty::ParamEnv<'tcx>, free_region_map: FreeRegionMap<'tcx>, - - // Contains the implied region bounds in scope for our current body. - // - // Example: - // - // ``` - // fn foo<'a, 'b, T>(x: &'a T, y: &'b ()) { - // bar(x, y, |y: &'b T| { .. } // body B1) - // } // body B0 - // ``` - // - // Here, when checking the body B0, the list would be `[T: 'a]`, because we - // infer that `T` must outlive `'a` from the implied bounds on the - // fn declaration. - // - // For the body B1 however, the list would be `[T: 'a, T: 'b]`, because we - // also can see that -- within the closure body! -- `T` must - // outlive `'b`. This is not necessarily true outside the closure - // body, since the closure may never be called. + /// FIXME: Your first reaction may be that this is a bit strange. `RegionBoundPairs` + /// does not contain lifetimes, which are instead in the `FreeRegionMap`, and other + /// known type outlives are stored in the `known_type_outlives` set. So why do we + /// have these at all? It turns out that removing these and using `known_type_outlives` + /// everywhere is just enough of a perf regression to matter. This can/should be + /// optimized in the future, though. region_bound_pairs: RegionBoundPairs<'tcx>, + known_type_outlives: Vec>, } /// "Region-bound pairs" tracks outlives relations that are known to @@ -59,15 +47,10 @@ pub struct OutlivesEnvironment<'tcx> { pub type RegionBoundPairs<'tcx> = FxIndexSet>>; impl<'tcx> OutlivesEnvironment<'tcx> { - /// Create a new `OutlivesEnvironment` without extra outlives bounds. - #[inline] - pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self { - Self::with_bounds(param_env, vec![]) - } - - /// Create a new `OutlivesEnvironment` with extra outlives bounds. - pub fn with_bounds( + /// Create a new `OutlivesEnvironment` from normalized outlives bounds. + pub fn from_normalized_bounds( param_env: ty::ParamEnv<'tcx>, + known_type_outlives: Vec>, extra_bounds: impl IntoIterator>, ) -> Self { let mut region_relation = TransitiveRelationBuilder::default(); @@ -102,18 +85,21 @@ impl<'tcx> OutlivesEnvironment<'tcx> { OutlivesEnvironment { param_env, + known_type_outlives, free_region_map: FreeRegionMap { relation: region_relation.freeze() }, region_bound_pairs, } } - /// Borrows current value of the `free_region_map`. pub fn free_region_map(&self) -> &FreeRegionMap<'tcx> { &self.free_region_map } - /// Borrows current `region_bound_pairs`. pub fn region_bound_pairs(&self) -> &RegionBoundPairs<'tcx> { &self.region_bound_pairs } + + pub fn known_type_outlives(&self) -> &[ty::PolyTypeOutlivesPredicate<'tcx>] { + &self.known_type_outlives + } } diff --git a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs index c02ab98b2baeb..379410641fe5b 100644 --- a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs +++ b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs @@ -48,7 +48,7 @@ where return ty.super_visit_with(self); } - match ty.kind() { + match *ty.kind() { // We can prove that an alias is live two ways: // 1. All the components are live. // @@ -95,11 +95,9 @@ where assert!(r.type_flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS)); r.visit_with(self); } else { - // Skip lifetime parameters that are not captures. - let variances = match kind { - ty::Opaque => Some(self.tcx.variances_of(*def_id)), - _ => None, - }; + // Skip lifetime parameters that are not captured, since they do + // not need to be live. + let variances = tcx.opt_alias_variances(kind, def_id); for (idx, s) in args.iter().enumerate() { if variances.map(|variances| variances[idx]) != Some(ty::Bivariant) { diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index b1d5d688295d9..a89cef50c9b40 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -67,7 +67,6 @@ use rustc_middle::ty::{ self, GenericArgKind, GenericArgsRef, PolyTypeOutlivesPredicate, Region, Ty, TyCtxt, TypeFoldable as _, TypeVisitableExt, }; -use rustc_span::DUMMY_SP; use rustc_type_ir::outlives::{Component, push_outlives_components}; use smallvec::smallvec; use tracing::{debug, instrument}; @@ -101,16 +100,20 @@ impl<'tcx> InferCtxt<'tcx> { ) { debug!(?sup_type, ?sub_region, ?cause); let origin = SubregionOrigin::from_obligation_cause(cause, || { - infer::RelateParamBound(cause.span, sup_type, match cause.code().peel_derives() { - ObligationCauseCode::WhereClause(_, span) - | ObligationCauseCode::WhereClauseInExpr(_, span, ..) - | ObligationCauseCode::OpaqueTypeBound(span, _) - if !span.is_dummy() => - { - Some(*span) - } - _ => None, - }) + infer::RelateParamBound( + cause.span, + sup_type, + match cause.code().peel_derives() { + ObligationCauseCode::WhereClause(_, span) + | ObligationCauseCode::WhereClauseInExpr(_, span, ..) + | ObligationCauseCode::OpaqueTypeBound(span, _) + if !span.is_dummy() => + { + Some(*span) + } + _ => None, + }, + ) }); self.register_region_obligation(RegionObligation { sup_type, sub_region, origin }); @@ -142,25 +145,6 @@ impl<'tcx> InferCtxt<'tcx> { ) -> Result<(), (PolyTypeOutlivesPredicate<'tcx>, SubregionOrigin<'tcx>)> { assert!(!self.in_snapshot(), "cannot process registered region obligations in a snapshot"); - let normalized_caller_bounds: Vec<_> = outlives_env - .param_env - .caller_bounds() - .iter() - .filter_map(|clause| { - let outlives = clause.as_type_outlives_clause()?; - Some( - deeply_normalize_ty( - outlives, - SubregionOrigin::AscribeUserTypeProvePredicate(DUMMY_SP), - ) - // FIXME(-Znext-solver): How do we accurately report an error span here :( - .map_err(|NoSolution| { - (outlives, SubregionOrigin::AscribeUserTypeProvePredicate(DUMMY_SP)) - }), - ) - }) - .try_collect()?; - // Must loop since the process of normalizing may itself register region obligations. for iteration in 0.. { let my_region_obligations = self.take_registered_region_obligations(); @@ -194,7 +178,7 @@ impl<'tcx> InferCtxt<'tcx> { self.tcx, outlives_env.region_bound_pairs(), None, - &normalized_caller_bounds, + outlives_env.known_type_outlives(), ); let category = origin.to_constraint_category(); outlives.type_must_outlive(origin, sup_type, sub_region, category); @@ -363,6 +347,13 @@ where return; } + if alias_ty.has_non_region_infer() { + self.tcx + .dcx() + .span_delayed_bug(origin.span(), "an alias has infers during region solving"); + return; + } + // This case is thorny for inference. The fundamental problem is // that there are many cases where we have choice, and inference // doesn't like choice (the current region inference in @@ -388,26 +379,9 @@ where // Compute the bounds we can derive from the environment. This // is an "approximate" match -- in some cases, these bounds // may not apply. - let mut approx_env_bounds = self.verify_bound.approx_declared_bounds_from_env(alias_ty); + let approx_env_bounds = self.verify_bound.approx_declared_bounds_from_env(alias_ty); debug!(?approx_env_bounds); - // Remove outlives bounds that we get from the environment but - // which are also deducible from the trait. This arises (cc - // #55756) in cases where you have e.g., `>::Item: - // 'a` in the environment but `trait Foo<'b> { type Item: 'b - // }` in the trait definition. - approx_env_bounds.retain(|bound_outlives| { - // OK to skip binder because we only manipulate and compare against other values from - // the same binder. e.g. if we have (e.g.) `for<'a> >::Item: 'a` in - // `bound`, the `'a` will be a `^1` (bound, debruijn index == innermost) region. If the - // declaration is `trait Trait<'b> { type Item: 'b; }`, then - // `projection_declared_bounds_from_trait` will be invoked with `['b => ^1]` and so we - // will get `^1` returned. - let bound = bound_outlives.skip_binder(); - let ty::Alias(_, alias_ty) = bound.0.kind() else { bug!("expected AliasTy") }; - self.verify_bound.declared_bounds_from_definition(*alias_ty).all(|r| r != bound.1) - }); - // If declared bounds list is empty, the only applicable rule is // OutlivesProjectionComponent. If there are inference variables, // then, we can break down the outlives into more primitive @@ -422,13 +396,13 @@ where // the problem is to add `T: 'r`, which isn't true. So, if there are no // inference variables, we use a verify constraint instead of adding // edges, which winds up enforcing the same condition. - let is_opaque = alias_ty.kind(self.tcx) == ty::Opaque; + let kind = alias_ty.kind(self.tcx); if approx_env_bounds.is_empty() && trait_bounds.is_empty() - && (alias_ty.has_infer() || is_opaque) + && (alias_ty.has_infer_regions() || kind == ty::Opaque) { debug!("no declared bounds"); - let opt_variances = is_opaque.then(|| self.tcx.variances_of(alias_ty.def_id)); + let opt_variances = self.tcx.opt_alias_variances(kind, alias_ty.def_id); self.args_must_outlive(alias_ty.args, origin, region, opt_variances); return; } diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 247fbc259652e..c14c288c6e4e6 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -102,12 +102,11 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { #[instrument(level = "debug", skip(self))] pub(crate) fn alias_bound(&self, alias_ty: ty::AliasTy<'tcx>) -> VerifyBound<'tcx> { - let alias_ty_as_ty = alias_ty.to_ty(self.tcx); - // Search the env for where clauses like `P: 'a`. let env_bounds = self.approx_declared_bounds_from_env(alias_ty).into_iter().map(|binder| { if let Some(ty::OutlivesPredicate(ty, r)) = binder.no_bound_vars() - && ty == alias_ty_as_ty + && let ty::Alias(_, alias_ty_from_bound) = *ty.kind() + && alias_ty_from_bound == alias_ty { // Micro-optimize if this is an exact match (this // occurs often when there are no region variables @@ -127,7 +126,8 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // see the extensive comment in projection_must_outlive let recursive_bound = { let mut components = smallvec![]; - compute_alias_components_recursive(self.tcx, alias_ty_as_ty, &mut components); + let kind = alias_ty.kind(self.tcx); + compute_alias_components_recursive(self.tcx, kind, alias_ty, &mut components); self.bound_from_components(&components) }; @@ -192,7 +192,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { /// Obviously these must be approximate -- they are in fact both *over* and /// and *under* approximated: /// - /// * Over-approximated because we erase regions, so + /// * Over-approximated because we don't consider equality of regions. /// * Under-approximated because we look for syntactic equality and so for complex types /// like `>::Item` or whatever we may fail to figure out /// all the subtleties. @@ -205,13 +205,14 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { erased_ty: Ty<'tcx>, ) -> Vec> { let tcx = self.tcx; + let mut bounds = vec![]; // To start, collect bounds from user environment. Note that // parameter environments are already elaborated, so we don't // have to worry about that. - let param_bounds = self.caller_bounds.iter().copied().filter(move |outlives_predicate| { + bounds.extend(self.caller_bounds.iter().copied().filter(move |outlives_predicate| { super::test_type_match::can_match_erased_ty(tcx, *outlives_predicate, erased_ty) - }); + })); // Next, collect regions we scraped from the well-formedness // constraints in the fn signature. To do that, we walk the list @@ -224,37 +225,27 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // The problem is that the type of `x` is `&'a A`. To be // well-formed, then, A must outlive `'a`, but we don't know that // this holds from first principles. - let from_region_bound_pairs = - self.region_bound_pairs.iter().filter_map(|&OutlivesPredicate(p, r)| { - debug!( - "declared_generic_bounds_from_env_for_erased_ty: region_bound_pair = {:?}", - (r, p) - ); - // Fast path for the common case. - match (&p, erased_ty.kind()) { - // In outlive routines, all types are expected to be fully normalized. - // And therefore we can safely use structural equality for alias types. - (GenericKind::Param(p1), ty::Param(p2)) if p1 == p2 => {} - (GenericKind::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => {} - (GenericKind::Alias(a1), ty::Alias(_, a2)) if a1.def_id == a2.def_id => {} - _ => return None, - } + bounds.extend(self.region_bound_pairs.iter().filter_map(|&OutlivesPredicate(p, r)| { + debug!( + "declared_generic_bounds_from_env_for_erased_ty: region_bound_pair = {:?}", + (r, p) + ); + // Fast path for the common case. + match (&p, erased_ty.kind()) { + // In outlive routines, all types are expected to be fully normalized. + // And therefore we can safely use structural equality for alias types. + (GenericKind::Param(p1), ty::Param(p2)) if p1 == p2 => {} + (GenericKind::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => {} + (GenericKind::Alias(a1), ty::Alias(_, a2)) if a1.def_id == a2.def_id => {} + _ => return None, + } - let p_ty = p.to_ty(tcx); - let erased_p_ty = self.tcx.erase_regions(p_ty); - (erased_p_ty == erased_ty) - .then_some(ty::Binder::dummy(ty::OutlivesPredicate(p_ty, r))) - }); + let p_ty = p.to_ty(tcx); + let erased_p_ty = self.tcx.erase_regions(p_ty); + (erased_p_ty == erased_ty).then_some(ty::Binder::dummy(ty::OutlivesPredicate(p_ty, r))) + })); - param_bounds - .chain(from_region_bound_pairs) - .inspect(|bound| { - debug!( - "declared_generic_bounds_from_env_for_erased_ty: result predicate = {:?}", - bound - ) - }) - .collect() + bounds } /// Given a projection like `>::Bar`, returns any bounds @@ -290,7 +281,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { alias_ty: ty::AliasTy<'tcx>, ) -> impl Iterator> { let tcx = self.tcx; - let bounds = tcx.item_super_predicates(alias_ty.def_id); + let bounds = tcx.item_self_bounds(alias_ty.def_id); trace!("{:#?}", bounds.skip_binder()); bounds .iter_instantiated(tcx, alias_ty.args) diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 6496f38269a5e..57555db37abd5 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -8,7 +8,6 @@ use rustc_data_structures::undo_log::UndoLogs; use rustc_data_structures::unify as ut; use rustc_index::IndexVec; use rustc_macros::{TypeFoldable, TypeVisitable}; -use rustc_middle::infer::unify_key::{RegionVariableValue, RegionVidKey}; use rustc_middle::ty::{self, ReBound, ReStatic, ReVar, Region, RegionVid, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use tracing::{debug, instrument}; @@ -17,6 +16,7 @@ use self::CombineMapType::*; use self::UndoLog::*; use super::{MiscVariable, RegionVariableOrigin, Rollback, SubregionOrigin}; use crate::infer::snapshot::undo_log::{InferCtxtUndoLogs, Snapshot}; +use crate::infer::unify_key::{RegionVariableValue, RegionVidKey}; mod leak_check; diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index 21c47967eada9..ce2d07f4af996 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -4,7 +4,6 @@ use rustc_data_structures::sso::SsoHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::def_id::DefId; use rustc_middle::bug; -use rustc_middle::infer::unify_key::ConstVariableValue; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::visit::MaxUniverse; use rustc_middle::ty::{ @@ -18,6 +17,7 @@ use super::{ PredicateEmittingRelation, Relate, RelateResult, StructurallyRelateAliases, TypeRelation, }; use crate::infer::type_variable::TypeVariableValue; +use crate::infer::unify_key::ConstVariableValue; use crate::infer::{InferCtxt, RegionVariableOrigin, relate}; impl<'tcx> InferCtxt<'tcx> { diff --git a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs index 7908733e7348e..0998f3c479024 100644 --- a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs +++ b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs @@ -24,9 +24,9 @@ impl<'tcx> InferCtxt<'tcx> { #[instrument(level = "debug", skip(self), ret)] pub fn enter_forall_and_leak_universe(&self, binder: ty::Binder<'tcx, T>) -> T where - T: TypeFoldable> + Copy, + T: TypeFoldable>, { - if let Some(inner) = binder.no_bound_vars() { + if let Some(inner) = binder.clone().no_bound_vars() { return inner; } @@ -34,22 +34,22 @@ impl<'tcx> InferCtxt<'tcx> { let delegate = FnMutDelegate { regions: &mut |br: ty::BoundRegion| { - ty::Region::new_placeholder(self.tcx, ty::PlaceholderRegion { - universe: next_universe, - bound: br, - }) + ty::Region::new_placeholder( + self.tcx, + ty::PlaceholderRegion { universe: next_universe, bound: br }, + ) }, types: &mut |bound_ty: ty::BoundTy| { - Ty::new_placeholder(self.tcx, ty::PlaceholderType { - universe: next_universe, - bound: bound_ty, - }) + Ty::new_placeholder( + self.tcx, + ty::PlaceholderType { universe: next_universe, bound: bound_ty }, + ) }, consts: &mut |bound_var: ty::BoundVar| { - ty::Const::new_placeholder(self.tcx, ty::PlaceholderConst { - universe: next_universe, - bound: bound_var, - }) + ty::Const::new_placeholder( + self.tcx, + ty::PlaceholderConst { universe: next_universe, bound: bound_var }, + ) }, }; @@ -71,7 +71,7 @@ impl<'tcx> InferCtxt<'tcx> { #[instrument(level = "debug", skip(self, f))] pub fn enter_forall(&self, forall: ty::Binder<'tcx, T>, f: impl FnOnce(T) -> U) -> U where - T: TypeFoldable> + Copy, + T: TypeFoldable>, { // FIXME: currently we do nothing to prevent placeholders with the new universe being // used after exiting `f`. For example region subtyping can result in outlives constraints diff --git a/compiler/rustc_infer/src/infer/snapshot/fudge.rs b/compiler/rustc_infer/src/infer/snapshot/fudge.rs index 394e07a81e7da..3a47b13665d45 100644 --- a/compiler/rustc_infer/src/infer/snapshot/fudge.rs +++ b/compiler/rustc_infer/src/infer/snapshot/fudge.rs @@ -1,7 +1,6 @@ use std::ops::Range; use rustc_data_structures::{snapshot_vec as sv, unify as ut}; -use rustc_middle::infer::unify_key::{ConstVariableValue, ConstVidKey}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::{self, ConstVid, FloatVid, IntVid, RegionVid, Ty, TyCtxt, TyVid}; use rustc_type_ir::visit::TypeVisitableExt; @@ -10,6 +9,7 @@ use ut::UnifyKey; use super::VariableLengths; use crate::infer::type_variable::TypeVariableOrigin; +use crate::infer::unify_key::{ConstVariableValue, ConstVidKey}; use crate::infer::{ConstVariableOrigin, InferCtxt, RegionVariableOrigin, UnificationTable}; fn vars_since_snapshot<'tcx, T>( diff --git a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs index 713389f461836..ba7d8f588e68e 100644 --- a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs +++ b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs @@ -2,10 +2,10 @@ use std::marker::PhantomData; use rustc_data_structures::undo_log::{Rollback, UndoLogs}; use rustc_data_structures::{snapshot_vec as sv, unify as ut}; -use rustc_middle::infer::unify_key::{ConstVidKey, RegionVidKey}; use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey}; use tracing::debug; +use crate::infer::unify_key::{ConstVidKey, RegionVidKey}; use crate::infer::{InferCtxtInner, region_constraints, type_variable}; use crate::traits; diff --git a/compiler/rustc_middle/src/infer/unify_key.rs b/compiler/rustc_infer/src/infer/unify_key.rs similarity index 94% rename from compiler/rustc_middle/src/infer/unify_key.rs rename to compiler/rustc_infer/src/infer/unify_key.rs index 7f9211043d690..3ba8aea1d3a05 100644 --- a/compiler/rustc_middle/src/infer/unify_key.rs +++ b/compiler/rustc_infer/src/infer/unify_key.rs @@ -2,23 +2,18 @@ use std::cmp; use std::marker::PhantomData; use rustc_data_structures::unify::{NoError, UnifyKey, UnifyValue}; +use rustc_middle::{bug, ty}; use rustc_span::Span; use rustc_span::def_id::DefId; -use crate::ty::{self, Ty, TyCtxt}; - -pub trait ToType { - fn to_type<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx>; -} - #[derive(Copy, Clone, Debug)] -pub enum RegionVariableValue<'tcx> { +pub(crate) enum RegionVariableValue<'tcx> { Known { value: ty::Region<'tcx> }, Unknown { universe: ty::UniverseIndex }, } #[derive(PartialEq, Copy, Clone, Debug)] -pub struct RegionVidKey<'tcx> { +pub(crate) struct RegionVidKey<'tcx> { pub vid: ty::RegionVid, pub phantom: PhantomData>, } @@ -44,7 +39,8 @@ impl<'tcx> UnifyKey for RegionVidKey<'tcx> { } } -pub struct RegionUnificationError; +pub(crate) struct RegionUnificationError; + impl<'tcx> UnifyValue for RegionVariableValue<'tcx> { type Error = RegionUnificationError; @@ -100,7 +96,7 @@ pub struct ConstVariableOrigin { } #[derive(Copy, Clone, Debug)] -pub enum ConstVariableValue<'tcx> { +pub(crate) enum ConstVariableValue<'tcx> { Known { value: ty::Const<'tcx> }, Unknown { origin: ConstVariableOrigin, universe: ty::UniverseIndex }, } @@ -108,7 +104,7 @@ pub enum ConstVariableValue<'tcx> { impl<'tcx> ConstVariableValue<'tcx> { /// If this value is known, returns the const it is known to be. /// Otherwise, `None`. - pub fn known(&self) -> Option> { + pub(crate) fn known(&self) -> Option> { match *self { ConstVariableValue::Unknown { .. } => None, ConstVariableValue::Known { value } => Some(value), @@ -117,7 +113,7 @@ impl<'tcx> ConstVariableValue<'tcx> { } #[derive(PartialEq, Copy, Clone, Debug)] -pub struct ConstVidKey<'tcx> { +pub(crate) struct ConstVidKey<'tcx> { pub vid: ty::ConstVid, pub phantom: PhantomData>, } diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs index 51282b900ed14..1eae10673b62b 100644 --- a/compiler/rustc_infer/src/traits/engine.rs +++ b/compiler/rustc_infer/src/traits/engine.rs @@ -45,12 +45,15 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx { cause: ObligationCause<'tcx>, ) { let trait_ref = ty::TraitRef::new(infcx.tcx, def_id, [ty]); - self.register_predicate_obligation(infcx, Obligation { - cause, - recursion_depth: 0, - param_env, - predicate: trait_ref.upcast(infcx.tcx), - }); + self.register_predicate_obligation( + infcx, + Obligation { + cause, + recursion_depth: 0, + param_env, + predicate: trait_ref.upcast(infcx.tcx), + }, + ); } fn register_predicate_obligation( diff --git a/compiler/rustc_infer/src/traits/project.rs b/compiler/rustc_infer/src/traits/project.rs index cab2ce9f5a0fc..9082db4f4488c 100644 --- a/compiler/rustc_infer/src/traits/project.rs +++ b/compiler/rustc_infer/src/traits/project.rs @@ -193,10 +193,10 @@ impl<'tcx> ProjectionCache<'_, 'tcx> { if result.must_apply_considering_regions() { ty.obligations = PredicateObligations::new(); } - map.insert(key, ProjectionCacheEntry::NormalizedTerm { - ty, - complete: Some(result), - }); + map.insert( + key, + ProjectionCacheEntry::NormalizedTerm { ty, complete: Some(result) }, + ); } ref value => { // Type inference could "strand behind" old cache entries. Leave diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index ab8ada1596c81..66ed49fe32676 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -1,5 +1,5 @@ use rustc_data_structures::fx::FxHashSet; -use rustc_middle::ty::{self, ToPolyTraitRef, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_span::{Ident, Span}; pub use rustc_type_ir::elaborate::*; @@ -125,8 +125,8 @@ pub fn transitive_bounds_that_define_assoc_item<'tcx>( .iter_identity_copied() .map(|(clause, _)| clause.instantiate_supertrait(tcx, trait_ref)) .filter_map(|clause| clause.as_trait_clause()) - // FIXME: Negative supertraits are elaborated here lol - .map(|trait_pred| trait_pred.to_poly_trait_ref()), + .filter(|clause| clause.polarity() == ty::PredicatePolarity::Positive) + .map(|clause| clause.map_bound(|clause| clause.trait_ref)), ); return Some(trait_ref); diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index dcb9c5d22d61b..295a988d2da5d 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" # tidy-alphabetical-start rustc-rayon = { version = "0.5.0" } rustc-rayon-core = { version = "0.5.0" } +rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_lowering = { path = "../rustc_ast_lowering" } rustc_ast_passes = { path = "../rustc_ast_passes" } diff --git a/compiler/rustc_interface/messages.ftl b/compiler/rustc_interface/messages.ftl index 47dfbc1d7fbf1..31123625369bd 100644 --- a/compiler/rustc_interface/messages.ftl +++ b/compiler/rustc_interface/messages.ftl @@ -1,3 +1,8 @@ +interface_abi_required_feature = + target feature `{$feature}` must be {$enabled} to ensure that the ABI of the current target can be implemented correctly + .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +interface_abi_required_feature_issue = for more information, see issue #116344 + interface_cant_emit_mir = could not emit MIR: {$error} diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs index 939980a932fdb..b62950d670964 100644 --- a/compiler/rustc_interface/src/errors.rs +++ b/compiler/rustc_interface/src/errors.rs @@ -103,3 +103,12 @@ pub struct IgnoringOutDir; #[derive(Diagnostic)] #[diag(interface_multiple_output_types_to_stdout)] pub struct MultipleOutputTypesToStdout; + +#[derive(Diagnostic)] +#[diag(interface_abi_required_feature)] +#[note] +#[note(interface_abi_required_feature_issue)] +pub(crate) struct AbiRequiredTargetFeature<'a> { + pub feature: &'a str, + pub enabled: &'a str, +} diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 1456255ea14cd..b35703d8e737b 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -7,7 +7,6 @@ use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::jobserver; use rustc_data_structures::stable_hasher::StableHasher; -use rustc_data_structures::sync::Lrc; use rustc_errors::registry::Registry; use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed}; use rustc_lint::LintStore; @@ -309,6 +308,11 @@ pub struct Config { pub output_dir: Option, pub output_file: Option, pub ice_file: Option, + /// Load files from sources other than the file system. + /// + /// Has no uses within this repository, but may be used in the future by + /// bjorn3 for "hooking rust-analyzer's VFS into rustc at some point for + /// running rustc without having to save". (See #102759.) pub file_loader: Option>, /// The list of fluent resources, used for lints declared with /// [`Diagnostic`](rustc_errors::Diagnostic) and [`LintDiagnostic`](rustc_errors::LintDiagnostic). @@ -337,6 +341,11 @@ pub struct Config { pub override_queries: Option, /// This is a callback from the driver that is called to create a codegen backend. + /// + /// Has no uses within this repository, but is used by bjorn3 for "the + /// hotswapping branch of cg_clif" for "setting the codegen backend from a + /// custom driver where the custom codegen backend has arbitrary data." + /// (See #102759.) pub make_codegen_backend: Option Box + Send>>, @@ -346,8 +355,7 @@ pub struct Config { /// The inner atomic value is set to true when a feature marked as `internal` is /// enabled. Makes it so that "please report a bug" is hidden, as ICEs with /// internal features are wontfix, and they are usually the cause of the ICEs. - /// None signifies that this is not tracked. - pub using_internal_features: Arc, + pub using_internal_features: &'static std::sync::atomic::AtomicBool, /// All commandline args used to invoke the compiler, with @file args fully expanded. /// This will only be used within debug info, e.g. in the pdb file on windows @@ -383,7 +391,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se crate::callbacks::setup_callbacks(); let sysroot = filesearch::materialize_sysroot(config.opts.maybe_sysroot.clone()); - let target = config::build_target_config(&early_dcx, &config.opts, &sysroot); + let target = config::build_target_config(&early_dcx, &config.opts.target_triple, &sysroot); let file_loader = config.file_loader.unwrap_or_else(|| Box::new(RealFileLoader)); let path_mapping = config.opts.file_path_mapping(); let hash_kind = config.opts.unstable_opts.src_hash_algorithm(&target); @@ -434,7 +442,6 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se locale_resources.push(codegen_backend.locale_resource()); let mut sess = rustc_session::build_session( - early_dcx, config.opts, CompilerIO { input: config.input, @@ -482,7 +489,9 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se if let Some(register_lints) = config.register_lints.as_deref() { register_lints(&sess, &mut lint_store); } - sess.lint_store = Some(Lrc::new(lint_store)); + sess.lint_store = Some(Arc::new(lint_store)); + + util::check_abi_required_features(&sess); let compiler = Compiler { sess, @@ -533,7 +542,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se pub fn try_print_query_stack( dcx: DiagCtxtHandle<'_>, - num_frames: Option, + limit_frames: Option, file: Option, ) { eprintln!("query stack during panic:"); @@ -541,13 +550,13 @@ pub fn try_print_query_stack( // Be careful relying on global state here: this code is called from // a panic hook, which means that the global `DiagCtxt` may be in a weird // state if it was responsible for triggering the panic. - let i = ty::tls::with_context_opt(|icx| { + let all_frames = ty::tls::with_context_opt(|icx| { if let Some(icx) = icx { ty::print::with_no_queries!(print_query_stack( QueryCtxt::new(icx.tcx), icx.query, dcx, - num_frames, + limit_frames, file, )) } else { @@ -555,9 +564,14 @@ pub fn try_print_query_stack( } }); - if num_frames == None || num_frames >= Some(i) { - eprintln!("end of query stack"); + if let Some(limit_frames) = limit_frames + && all_frames > limit_frames + { + eprintln!( + "... and {} other queries... use `env RUST_BACKTRACE=1` to see the full query stack", + all_frames - limit_frames + ); } else { - eprintln!("we're just showing a limited slice of the query stack"); + eprintln!("end of query stack"); } } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index aff66e48fbbee..e5adcdb244f6d 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -9,7 +9,7 @@ use rustc_ast as ast; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::parallel; use rustc_data_structures::steal::Steal; -use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, Lrc, OnceLock, WorkerLocal}; +use rustc_data_structures::sync::{AppendOnlyIndexVec, FreezeLock, OnceLock, WorkerLocal}; use rustc_expand::base::{ExtCtxt, LintStoreExpand}; use rustc_feature::Features; use rustc_fs_util::try_canonicalize; @@ -26,7 +26,6 @@ use rustc_parse::{ }; use rustc_passes::{abi_test, input_stats, layout_test}; use rustc_resolve::Resolver; -use rustc_session::code_stats::VTableSizeInfo; use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType}; use rustc_session::cstore::Untracked; use rustc_session::output::{collect_crate_types, filename_for_input, find_crate_name}; @@ -269,6 +268,7 @@ fn configure_and_expand( resolver.resolve_crate(&krate); + CStore::from_tcx(tcx).report_incompatible_target_modifiers(tcx, &krate); krate } @@ -602,7 +602,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P fn resolver_for_lowering_raw<'tcx>( tcx: TyCtxt<'tcx>, (): (), -) -> (&'tcx Steal<(ty::ResolverAstLowering, Lrc)>, &'tcx ty::ResolverGlobalCtxt) { +) -> (&'tcx Steal<(ty::ResolverAstLowering, Arc)>, &'tcx ty::ResolverGlobalCtxt) { let arenas = Resolver::arenas(); let _ = tcx.registered_tools(()); // Uses `crate_for_resolver`. let (krate, pre_configured_attrs) = tcx.crate_for_resolver(()).steal(); @@ -624,7 +624,7 @@ fn resolver_for_lowering_raw<'tcx>( } = resolver.into_outputs(); let resolutions = tcx.arena.alloc(untracked_resolutions); - (tcx.arena.alloc(Steal::new((untracked_resolver_for_lowering, Lrc::new(krate)))), resolutions) + (tcx.arena.alloc(Steal::new((untracked_resolver_for_lowering, Arc::new(krate)))), resolutions) } pub fn write_dep_info(tcx: TyCtxt<'_>) { @@ -827,26 +827,28 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { if tcx.sess.opts.unstable_opts.input_stats { rustc_passes::input_stats::print_hir_stats(tcx); } - #[cfg(debug_assertions)] + // When using rustdoc's "jump to def" feature, it enters this code and `check_crate` + // is not defined. So we need to cfg it out. + #[cfg(all(not(doc), debug_assertions))] rustc_passes::hir_id_validator::check_crate(tcx); let sess = tcx.sess; sess.time("misc_checking_1", || { parallel!( { - sess.time("looking_for_entry_point", || tcx.ensure().entry_fn(())); + sess.time("looking_for_entry_point", || tcx.ensure_ok().entry_fn(())); sess.time("looking_for_derive_registrar", || { - tcx.ensure().proc_macro_decls_static(()) + tcx.ensure_ok().proc_macro_decls_static(()) }); CStore::from_tcx(tcx).report_unused_deps(tcx); }, { tcx.hir().par_for_each_module(|module| { - tcx.ensure().check_mod_loops(module); - tcx.ensure().check_mod_attrs(module); - tcx.ensure().check_mod_naked_functions(module); - tcx.ensure().check_mod_unstable_api_usage(module); + tcx.ensure_ok().check_mod_loops(module); + tcx.ensure_ok().check_mod_attrs(module); + tcx.ensure_ok().check_mod_naked_functions(module); + tcx.ensure_ok().check_mod_unstable_api_usage(module); }); }, { @@ -859,8 +861,8 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { // since they might not otherwise get called. // This marks the corresponding crate-level attributes // as used, and ensures that their values are valid. - tcx.ensure().limits(()); - tcx.ensure().stability_index(()); + tcx.ensure_ok().limits(()); + tcx.ensure_ok().stability_index(()); } ); }); @@ -869,12 +871,14 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { sess.time("MIR_coroutine_by_move_body", || { tcx.hir().par_body_owners(|def_id| { if tcx.needs_coroutine_by_move_body_def_id(def_id.to_def_id()) { - tcx.ensure_with_value().coroutine_by_move_body_def_id(def_id); + tcx.ensure_done().coroutine_by_move_body_def_id(def_id); } }); }); // Freeze definitions as we don't add new ones at this point. // We need to wait until now since we synthesize a by-move body + // for all coroutine-closures. + // // This improves performance by allowing lock-free access to them. tcx.untracked().definitions.freeze(); @@ -882,13 +886,13 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { tcx.hir().par_body_owners(|def_id| { // Run unsafety check because it's responsible for stealing and // deallocating THIR. - tcx.ensure().check_unsafety(def_id); - tcx.ensure().mir_borrowck(def_id) + tcx.ensure_ok().check_unsafety(def_id); + tcx.ensure_ok().mir_borrowck(def_id) }); }); sess.time("MIR_effect_checking", || { - for def_id in tcx.hir().body_owners() { - tcx.ensure().has_ffi_unwind_calls(def_id); + tcx.hir().par_body_owners(|def_id| { + tcx.ensure_ok().has_ffi_unwind_calls(def_id); // If we need to codegen, ensure that we emit all errors from // `mir_drops_elaborated_and_const_checked` now, to avoid discovering @@ -896,17 +900,24 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { if tcx.sess.opts.output_types.should_codegen() || tcx.hir().body_const_context(def_id).is_some() { - tcx.ensure().mir_drops_elaborated_and_const_checked(def_id); + tcx.ensure_ok().mir_drops_elaborated_and_const_checked(def_id); } - } + }); }); - tcx.hir().par_body_owners(|def_id| { - if tcx.is_coroutine(def_id.to_def_id()) { - tcx.ensure().mir_coroutine_witnesses(def_id); - tcx.ensure().check_coroutine_obligations( - tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(), - ); - } + sess.time("coroutine_obligations", || { + tcx.hir().par_body_owners(|def_id| { + if tcx.is_coroutine(def_id.to_def_id()) { + tcx.ensure_ok().mir_coroutine_witnesses(def_id); + tcx.ensure_ok().check_coroutine_obligations( + tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(), + ); + // Eagerly check the unsubstituted layout for cycles. + tcx.ensure_ok().layout_of( + ty::TypingEnv::post_analysis(tcx, def_id.to_def_id()) + .as_query_input(tcx.type_of(def_id).instantiate_identity()), + ); + } + }); }); sess.time("layout_testing", || layout_test::test_layout(tcx)); @@ -947,15 +958,16 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) { sess.time("misc_checking_3", || { parallel!( { - tcx.ensure().effective_visibilities(()); + tcx.ensure_ok().effective_visibilities(()); parallel!( { - tcx.ensure().check_private_in_public(()); + tcx.ensure_ok().check_private_in_public(()); }, { - tcx.hir() - .par_for_each_module(|module| tcx.ensure().check_mod_deathness(module)); + tcx.hir().par_for_each_module(|module| { + tcx.ensure_ok().check_mod_deathness(module) + }); }, { sess.time("lint_checking", || { @@ -963,14 +975,14 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) { }); }, { - tcx.ensure().clashing_extern_declarations(()); + tcx.ensure_ok().clashing_extern_declarations(()); } ); }, { sess.time("privacy_checking_modules", || { tcx.hir().par_for_each_module(|module| { - tcx.ensure().check_mod_privacy(module); + tcx.ensure_ok().check_mod_privacy(module); }); }); } @@ -978,97 +990,13 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) { // This check has to be run after all lints are done processing. We don't // define a lint filter, as all lint checks should have finished at this point. - sess.time("check_lint_expectations", || tcx.ensure().check_expectations(None)); + sess.time("check_lint_expectations", || tcx.ensure_ok().check_expectations(None)); // This query is only invoked normally if a diagnostic is emitted that needs any // diagnostic item. If the crate compiles without checking any diagnostic items, // we will fail to emit overlap diagnostics. Thus we invoke it here unconditionally. let _ = tcx.all_diagnostic_items(()); }); - - if sess.opts.unstable_opts.print_vtable_sizes { - let traits = tcx.traits(LOCAL_CRATE); - - for &tr in traits { - if !tcx.is_dyn_compatible(tr) { - continue; - } - - let name = ty::print::with_no_trimmed_paths!(tcx.def_path_str(tr)); - - let mut first_dsa = true; - - // Number of vtable entries, if we didn't have upcasting - let mut entries_ignoring_upcasting = 0; - // Number of vtable entries needed solely for upcasting - let mut entries_for_upcasting = 0; - - let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, tr)); - - // A slightly edited version of the code in - // `rustc_trait_selection::traits::vtable::vtable_entries`, that works without self - // type and just counts number of entries. - // - // Note that this is technically wrong, for traits which have associated types in - // supertraits: - // - // trait A: AsRef + AsRef<()> { type T; } - // - // Without self type we can't normalize `Self::T`, so we can't know if `AsRef` - // and `AsRef<()>` are the same trait, thus we assume that those are different, and - // potentially over-estimate how many vtable entries there are. - // - // Similarly this is wrong for traits that have methods with possibly-impossible bounds. - // For example: - // - // trait B { fn f(&self) where T: Copy; } - // - // Here `dyn B` will have 4 entries, while `dyn B` will only have 3. - // However, since we don't know `T`, we can't know if `T: Copy` holds or not, - // thus we lean on the bigger side and say it has 4 entries. - traits::vtable::prepare_vtable_segments(tcx, trait_ref, |segment| { - match segment { - traits::vtable::VtblSegment::MetadataDSA => { - // If this is the first dsa, it would be included either way, - // otherwise it's needed for upcasting - if std::mem::take(&mut first_dsa) { - entries_ignoring_upcasting += 3; - } else { - entries_for_upcasting += 3; - } - } - - traits::vtable::VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { - // Lookup the shape of vtable for the trait. - let own_existential_entries = - tcx.own_existential_vtable_entries(trait_ref.def_id()); - - // The original code here ignores the method if its predicates are - // impossible. We can't really do that as, for example, all not trivial - // bounds on generic parameters are impossible (since we don't know the - // parameters...), see the comment above. - entries_ignoring_upcasting += own_existential_entries.len(); - - if emit_vptr { - entries_for_upcasting += 1; - } - } - } - - std::ops::ControlFlow::Continue::(()) - }); - - sess.code_stats.record_vtable_size(tr, &name, VTableSizeInfo { - trait_name: name.clone(), - entries: entries_ignoring_upcasting + entries_for_upcasting, - entries_ignoring_upcasting, - entries_for_upcasting, - upcasting_cost_percent: entries_for_upcasting as f64 - / entries_ignoring_upcasting as f64 - * 100., - }) - } - } } /// Check for the `#[rustc_error]` annotation, which forces an error in codegen. This is used @@ -1087,7 +1015,7 @@ fn check_for_rustc_errors_attr(tcx: TyCtxt<'_>) { ) }) => { - tcx.ensure().trigger_delayed_bug(def_id); + tcx.ensure_ok().trigger_delayed_bug(def_id); } // Bare `#[rustc_error]`. @@ -1149,12 +1077,6 @@ pub(crate) fn start_codegen<'tcx>( tcx.sess.code_stats.print_type_sizes(); } - if tcx.sess.opts.unstable_opts.print_vtable_sizes { - let crate_name = tcx.crate_name(LOCAL_CRATE); - - tcx.sess.code_stats.print_vtable_sizes(crate_name); - } - codegen } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 9ad690399140e..233bfcb529710 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -2,18 +2,19 @@ use std::collections::{BTreeMap, BTreeSet}; use std::num::NonZero; use std::path::{Path, PathBuf}; -use std::sync::Arc; +use std::sync::atomic::AtomicBool; +use rustc_abi::Align; use rustc_data_structures::profiling::TimePassesFormat; use rustc_errors::emitter::HumanReadableErrorType; use rustc_errors::{ColorConfig, registry}; use rustc_session::config::{ - BranchProtection, CFGuard, Cfg, CollapseMacroDebuginfo, CoverageLevel, CoverageOptions, - DebugInfo, DumpMonoStatsFormat, ErrorOutputType, ExternEntry, ExternLocation, Externs, - FmtDebug, FunctionReturn, InliningThreshold, Input, InstrumentCoverage, InstrumentXRay, - LinkSelfContained, LinkerPluginLto, LocationDetail, LtoCli, MirIncludeSpans, NextSolverConfig, - OomStrategy, Options, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet, Passes, - PatchableFunctionEntry, Polonius, ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, + AutoDiff, BranchProtection, CFGuard, Cfg, CollapseMacroDebuginfo, CoverageLevel, + CoverageOptions, DebugInfo, DumpMonoStatsFormat, ErrorOutputType, ExternEntry, ExternLocation, + Externs, FmtDebug, FunctionReturn, InliningThreshold, Input, InstrumentCoverage, + InstrumentXRay, LinkSelfContained, LinkerPluginLto, LocationDetail, LtoCli, MirIncludeSpans, + NextSolverConfig, OomStrategy, Options, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet, + Passes, PatchableFunctionEntry, Polonius, ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel, build_configuration, build_session_options, rustc_optgroups, }; @@ -41,7 +42,8 @@ where let matches = optgroups().parse(args).unwrap(); let sessopts = build_session_options(&mut early_dcx, &matches); let sysroot = filesearch::materialize_sysroot(sessopts.maybe_sysroot.clone()); - let target = rustc_session::config::build_target_config(&early_dcx, &sessopts, &sysroot); + let target = + rustc_session::config::build_target_config(&early_dcx, &sessopts.target_triple, &sysroot); let hash_kind = sessopts.unstable_opts.src_hash_algorithm(&target); let checksum_hash_kind = sessopts.unstable_opts.checksum_hash_algorithm(); let sm_inputs = Some(SourceMapInputs { @@ -60,8 +62,9 @@ where temps_dir, }; + static USING_INTERNAL_FEATURES: AtomicBool = AtomicBool::new(false); + let sess = build_session( - early_dcx, sessopts, io, None, @@ -72,7 +75,7 @@ where sysroot, "", None, - Arc::default(), + &USING_INTERNAL_FEATURES, Default::default(), ); let cfg = parse_cfg(sess.dcx(), matches.opt_strs("cfg")); @@ -582,6 +585,7 @@ fn test_codegen_options_tracking_hash() { untracked!(dlltool, Some(PathBuf::from("custom_dlltool.exe"))); untracked!(extra_filename, String::from("extra-filename")); untracked!(incremental, Some(String::from("abc"))); + untracked!(inline_threshold, Some(0xf007ba11)); // `link_arg` is omitted because it just forwards to `link_args`. untracked!(link_args, vec![String::from("abc"), String::from("def")]); untracked!(link_self_contained, LinkSelfContained::on()); @@ -613,7 +617,6 @@ fn test_codegen_options_tracking_hash() { tracked!(embed_bitcode, false); tracked!(force_frame_pointers, FramePointer::Always); tracked!(force_unwind_tables, Some(true)); - tracked!(inline_threshold, Some(0xf007ba11)); tracked!(instrument_coverage, InstrumentCoverage::Yes); tracked!(link_dead_code, Some(true)); tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto); @@ -756,6 +759,7 @@ fn test_unstable_options_tracking_hash() { tracked!(allow_features, Some(vec![String::from("lang_items")])); tracked!(always_encode_mir, true); tracked!(assume_incomplete_release, true); + tracked!(autodiff, vec![AutoDiff::Print]); tracked!(binary_dep_depinfo, true); tracked!(box_noalias, false); tracked!( @@ -766,11 +770,14 @@ fn test_unstable_options_tracking_hash() { }) ); tracked!(codegen_backend, Some("abc".to_string())); - tracked!(coverage_options, CoverageOptions { - level: CoverageLevel::Mcdc, - no_mir_spans: true, - discard_all_spans_in_codegen: true - }); + tracked!( + coverage_options, + CoverageOptions { + level: CoverageLevel::Mcdc, + no_mir_spans: true, + discard_all_spans_in_codegen: true + } + ); tracked!(crate_attr, vec!["abc".to_string()]); tracked!(cross_crate_inline_threshold, InliningThreshold::Always); tracked!(debug_info_for_profiling, true); @@ -782,6 +789,7 @@ fn test_unstable_options_tracking_hash() { tracked!(dwarf_version, Some(5)); tracked!(embed_source, true); tracked!(emit_thin_lto, false); + tracked!(emscripten_wasm_eh, true); tracked!(export_executable_symbols, true); tracked!(fewer_names, Some(true)); tracked!(fixed_x18, true); @@ -792,7 +800,6 @@ fn test_unstable_options_tracking_hash() { tracked!(function_sections, Some(false)); tracked!(human_readable_cgu_names, true); tracked!(incremental_ignore_spans, true); - tracked!(inline_in_all_cgus, Some(true)); tracked!(inline_mir, Some(true)); tracked!(inline_mir_hint_threshold, Some(123)); tracked!(inline_mir_threshold, Some(123)); @@ -806,6 +813,7 @@ fn test_unstable_options_tracking_hash() { tracked!(location_detail, LocationDetail { file: true, line: false, column: false }); tracked!(maximal_hir_to_mir_coverage, true); tracked!(merge_functions, Some(MergeFunctions::Disabled)); + tracked!(min_function_alignment, Some(Align::EIGHT)); tracked!(mir_emit_retag, true); tracked!(mir_enable_passes, vec![("DestProp".to_string(), false)]); tracked!(mir_keep_place_mention, true); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 984b8104f539f..e900ec14fcab6 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -18,21 +18,25 @@ use rustc_session::{EarlyDiagCtxt, Session, filesearch}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::source_map::SourceMapInputs; -use rustc_span::sym; +use rustc_span::{Symbol, sym}; use rustc_target::spec::Target; use tracing::info; use crate::errors; /// Function pointer type that constructs a new CodegenBackend. -pub type MakeBackendFn = fn() -> Box; +type MakeBackendFn = fn() -> Box; /// Adds `target_feature = "..."` cfgs for a variety of platform /// specific features (SSE, NEON etc.). /// /// This is performed by checking whether a set of permitted features /// is available on the target machine, by querying the codegen backend. -pub fn add_configuration(cfg: &mut Cfg, sess: &mut Session, codegen_backend: &dyn CodegenBackend) { +pub(crate) fn add_configuration( + cfg: &mut Cfg, + sess: &mut Session, + codegen_backend: &dyn CodegenBackend, +) { let tf = sym::target_feature; let unstable_target_features = codegen_backend.target_features_cfg(sess, true); @@ -48,6 +52,34 @@ pub fn add_configuration(cfg: &mut Cfg, sess: &mut Session, codegen_backend: &dy } } +/// Ensures that all target features required by the ABI are present. +/// Must be called after `unstable_target_features` has been populated! +pub(crate) fn check_abi_required_features(sess: &Session) { + let abi_feature_constraints = sess.target.abi_required_features(); + // We check this against `unstable_target_features` as that is conveniently already + // back-translated to rustc feature names, taking into account `-Ctarget-cpu` and `-Ctarget-feature`. + // Just double-check that the features we care about are actually on our list. + for feature in + abi_feature_constraints.required.iter().chain(abi_feature_constraints.incompatible.iter()) + { + assert!( + sess.target.rust_target_features().iter().any(|(name, ..)| feature == name), + "target feature {feature} is required/incompatible for the current ABI but not a recognized feature for this target" + ); + } + + for feature in abi_feature_constraints.required { + if !sess.unstable_target_features.contains(&Symbol::intern(feature)) { + sess.dcx().emit_warn(errors::AbiRequiredTargetFeature { feature, enabled: "enabled" }); + } + } + for feature in abi_feature_constraints.incompatible { + if sess.unstable_target_features.contains(&Symbol::intern(feature)) { + sess.dcx().emit_warn(errors::AbiRequiredTargetFeature { feature, enabled: "disabled" }); + } + } +} + pub static STACK_SIZE: OnceLock = OnceLock::new(); pub const DEFAULT_STACK_SIZE: usize = 8 * 1024 * 1024; diff --git a/compiler/rustc_lexer/Cargo.toml b/compiler/rustc_lexer/Cargo.toml index 84b9e29229553..4b3492fdeda25 100644 --- a/compiler/rustc_lexer/Cargo.toml +++ b/compiler/rustc_lexer/Cargo.toml @@ -14,6 +14,7 @@ Rust lexer used by rustc. No stability guarantees are provided. # Note that this crate purposefully does not depend on other rustc crates [dependencies] +memchr = "2.7.4" unicode-xid = "0.2.0" [dependencies.unicode-properties] diff --git a/compiler/rustc_lexer/src/cursor.rs b/compiler/rustc_lexer/src/cursor.rs index d173c3ac0327b..e0e3bd0e30b16 100644 --- a/compiler/rustc_lexer/src/cursor.rs +++ b/compiler/rustc_lexer/src/cursor.rs @@ -103,4 +103,11 @@ impl<'a> Cursor<'a> { self.bump(); } } + + pub(crate) fn eat_until(&mut self, byte: u8) { + self.chars = match memchr::memchr(byte, self.as_str().as_bytes()) { + Some(index) => self.as_str()[index..].chars(), + None => "".chars(), + } + } } diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index aa4abf678b9f2..c63ab77decac9 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -483,7 +483,7 @@ impl Cursor<'_> { _ => None, }; - self.eat_while(|c| c != '\n'); + self.eat_until(b'\n'); LineComment { doc_style } } @@ -888,7 +888,7 @@ impl Cursor<'_> { // Skip the string contents and on each '#' character met, check if this is // a raw string termination. loop { - self.eat_while(|c| c != '"'); + self.eat_until(b'"'); if self.is_eof() { return Err(RawStrError::NoTerminator { diff --git a/compiler/rustc_lexer/src/tests.rs b/compiler/rustc_lexer/src/tests.rs index db7225fc2a810..8203ae70b0700 100644 --- a/compiler/rustc_lexer/src/tests.rs +++ b/compiler/rustc_lexer/src/tests.rs @@ -131,7 +131,9 @@ fn check_lexing(src: &str, expect: Expect) { #[test] fn smoke_test() { - check_lexing("/* my source file */ fn main() { println!(\"zebra\"); }\n", expect![[r#" + check_lexing( + "/* my source file */ fn main() { println!(\"zebra\"); }\n", + expect![[r#" Token { kind: BlockComment { doc_style: None, terminated: true }, len: 20 } Token { kind: Whitespace, len: 1 } Token { kind: Ident, len: 2 } @@ -151,7 +153,8 @@ fn smoke_test() { Token { kind: Whitespace, len: 1 } Token { kind: CloseBrace, len: 1 } Token { kind: Whitespace, len: 1 } - "#]]) + "#]], + ) } #[test] @@ -194,35 +197,47 @@ fn comment_flavors() { #[test] fn nested_block_comments() { - check_lexing("/* /* */ */'a'", expect![[r#" + check_lexing( + "/* /* */ */'a'", + expect![[r#" Token { kind: BlockComment { doc_style: None, terminated: true }, len: 11 } Token { kind: Literal { kind: Char { terminated: true }, suffix_start: 3 }, len: 3 } - "#]]) + "#]], + ) } #[test] fn characters() { - check_lexing("'a' ' ' '\\n'", expect![[r#" + check_lexing( + "'a' ' ' '\\n'", + expect![[r#" Token { kind: Literal { kind: Char { terminated: true }, suffix_start: 3 }, len: 3 } Token { kind: Whitespace, len: 1 } Token { kind: Literal { kind: Char { terminated: true }, suffix_start: 3 }, len: 3 } Token { kind: Whitespace, len: 1 } Token { kind: Literal { kind: Char { terminated: true }, suffix_start: 4 }, len: 4 } - "#]]); + "#]], + ); } #[test] fn lifetime() { - check_lexing("'abc", expect![[r#" + check_lexing( + "'abc", + expect![[r#" Token { kind: Lifetime { starts_with_number: false }, len: 4 } - "#]]); + "#]], + ); } #[test] fn raw_string() { - check_lexing("r###\"\"#a\\b\x00c\"\"###", expect![[r#" + check_lexing( + "r###\"\"#a\\b\x00c\"\"###", + expect![[r#" Token { kind: Literal { kind: RawStr { n_hashes: Some(3) }, suffix_start: 17 }, len: 17 } - "#]]) + "#]], + ) } #[test] diff --git a/compiler/rustc_lexer/src/unescape/tests.rs b/compiler/rustc_lexer/src/unescape/tests.rs index 6fa7a150516b8..5b99495f47581 100644 --- a/compiler/rustc_lexer/src/unescape/tests.rs +++ b/compiler/rustc_lexer/src/unescape/tests.rs @@ -108,12 +108,15 @@ fn test_unescape_str_warn() { check("\\\n", &[]); check("\\\n ", &[]); - check("\\\n \u{a0} x", &[ - (0..5, Err(EscapeError::UnskippedWhitespaceWarning)), - (3..5, Ok('\u{a0}')), - (5..6, Ok(' ')), - (6..7, Ok('x')), - ]); + check( + "\\\n \u{a0} x", + &[ + (0..5, Err(EscapeError::UnskippedWhitespaceWarning)), + (3..5, Ok('\u{a0}')), + (5..6, Ok(' ')), + (6..7, Ok('x')), + ], + ); check("\\\n \n x", &[(0..7, Err(EscapeError::MultipleSkippedLinesWarning)), (7..8, Ok('x'))]); } diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 7522e21d0ef09..480d97e377a40 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -69,15 +69,18 @@ lint_builtin_const_no_mangle = const items should never be `#[no_mangle]` lint_builtin_decl_unsafe_fn = declaration of an `unsafe` function lint_builtin_decl_unsafe_method = declaration of an `unsafe` method -lint_builtin_deprecated_attr_default_suggestion = remove this attribute lint_builtin_deprecated_attr_link = use of deprecated attribute `{$name}`: {$reason}. See {$link} .msg_suggestion = {$msg} .default_suggestion = remove this attribute -lint_builtin_deprecated_attr_used = use of deprecated attribute `{$name}`: no longer used lint_builtin_deref_nullptr = dereferencing a null pointer .label = this code causes undefined behavior when executed +lint_builtin_double_negations = use of a double negation + .note = the prefix `--` could be misinterpreted as a decrement operator which exists in other languages + .note_decrement = use `-= 1` if you meant to decrement the value + .add_parens_suggestion = add parentheses for clarity + lint_builtin_ellipsis_inclusive_range_patterns = `...` range patterns are deprecated .suggestion = use `..=` for an inclusive range @@ -209,7 +212,9 @@ lint_dangling_pointers_from_temporaries = a dangling pointer will be produced be .label_ptr = this pointer will immediately be invalid .label_temporary = this `{$ty}` is deallocated at the end of the statement, bind it to a variable to extend its lifetime .note = pointers do not have a lifetime; when calling `{$callee}` the `{$ty}` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - .help = for more information, see + .help_bind = you must make sure that the variable you bind the `{$ty}` to lives at least as long as the pointer returned by the call to `{$callee}` + .help_returned = in particular, if this pointer is returned from the current function, binding the `{$ty}` inside the function will not suffice + .help_visit = for more information, see lint_default_hash_types = prefer `{$preferred}` over `{$used}`, it has better performance .note = a `use rustc_data_structures::fx::{$preferred}` may be necessary @@ -385,9 +390,6 @@ lint_improper_ctypes_only_phantomdata = composed only of `PhantomData` lint_improper_ctypes_opaque = opaque types have no C equivalent -lint_improper_ctypes_pat_help = consider using the base type instead - -lint_improper_ctypes_pat_reason = pattern types have no C equivalent lint_improper_ctypes_slice_help = consider using a raw pointer instead lint_improper_ctypes_slice_reason = slices have no C equivalent @@ -970,8 +972,7 @@ lint_unused_result = unused result of type `{$ty}` lint_use_let_underscore_ignore_suggestion = use `let _ = ...` to ignore the expression or result +lint_uses_power_alignment = repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + lint_variant_size_differences = enum variant is more than three times larger ({$largest} bytes) than the next largest - -lint_wasm_c_abi = - older versions of the `wasm-bindgen` crate will be incompatible with future versions of Rust; please update to `wasm-bindgen` v0.2.88 diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 6e823957cc6d6..ecd40a52e75f6 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -20,7 +20,7 @@ use rustc_abi::BackendRepr; use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::visit::{FnCtxt, FnKind}; use rustc_ast::{self as ast, *}; -use rustc_ast_pretty::pprust::{self, expr_to_string}; +use rustc_ast_pretty::pprust::expr_to_string; use rustc_errors::{Applicability, LintDiagnostic}; use rustc_feature::{AttributeGate, BuiltinAttribute, GateIssue, Stability, deprecated_attributes}; use rustc_hir as hir; @@ -29,7 +29,6 @@ use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; use rustc_hir::intravisit::FnKind as HirFnKind; use rustc_hir::{Body, FnDecl, GenericParamKind, PatKind, PredicateOrigin}; use rustc_middle::bug; -use rustc_middle::lint::in_external_macro; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, Upcast, VariantDef}; @@ -49,16 +48,16 @@ use rustc_trait_selection::traits::{self}; use crate::errors::BuiltinEllipsisInclusiveRangePatterns; use crate::lints::{ BuiltinAnonymousParams, BuiltinConstNoMangle, BuiltinDeprecatedAttrLink, - BuiltinDeprecatedAttrLinkSuggestion, BuiltinDeprecatedAttrUsed, BuiltinDerefNullptr, - BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives, - BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote, BuiltinIncompleteFeatures, - BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents, - BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc, BuiltinMutablesTransmutes, - BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns, BuiltinSpecialModuleNameUsed, - BuiltinTrivialBounds, BuiltinTypeAliasBounds, BuiltinUngatedAsyncFnTrackCaller, - BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, - BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub, - BuiltinWhileTrue, InvalidAsmLabel, + BuiltinDeprecatedAttrLinkSuggestion, BuiltinDerefNullptr, BuiltinDoubleNegations, + BuiltinDoubleNegationsAddParens, BuiltinEllipsisInclusiveRangePatternsLint, + BuiltinExplicitOutlives, BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote, + BuiltinIncompleteFeatures, BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, + BuiltinKeywordIdents, BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc, + BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns, + BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasBounds, + BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, + BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, + BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel, }; use crate::nonstandard_style::{MethodLateContext, method_context}; use crate::{ @@ -90,19 +89,11 @@ declare_lint! { declare_lint_pass!(WhileTrue => [WHILE_TRUE]); -/// Traverse through any amount of parenthesis and return the first non-parens expression. -fn pierce_parens(mut expr: &ast::Expr) -> &ast::Expr { - while let ast::ExprKind::Paren(sub) = &expr.kind { - expr = sub; - } - expr -} - impl EarlyLintPass for WhileTrue { #[inline] fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { if let ast::ExprKind::While(cond, _, label) = &e.kind - && let ast::ExprKind::Lit(token_lit) = pierce_parens(cond).kind + && let ast::ExprKind::Lit(token_lit) = cond.peel_parens().kind && let token::Lit { kind: token::Bool, symbol: kw::True, .. } = token_lit && !cond.span.from_expansion() { @@ -111,10 +102,11 @@ impl EarlyLintPass for WhileTrue { "{}loop", label.map_or_else(String::new, |label| format!("{}: ", label.ident,)) ); - cx.emit_span_lint(WHILE_TRUE, condition_span, BuiltinWhileTrue { - suggestion: condition_span, - replace, - }); + cx.emit_span_lint( + WHILE_TRUE, + condition_span, + BuiltinWhileTrue { suggestion: condition_span, replace }, + ); } } } @@ -338,10 +330,12 @@ impl EarlyLintPass for UnsafeCode { if let FnKind::Fn( ctxt, _, - ast::FnSig { header: ast::FnHeader { safety: ast::Safety::Unsafe(_), .. }, .. }, _, - _, - body, + ast::Fn { + sig: ast::FnSig { header: ast::FnHeader { safety: ast::Safety::Unsafe(_), .. }, .. }, + body, + .. + }, ) = fk { let decorator = match ctxt { @@ -428,10 +422,11 @@ impl MissingDoc { let attrs = cx.tcx.hir().attrs(cx.tcx.local_def_id_to_hir_id(def_id)); let has_doc = attrs.iter().any(has_doc); if !has_doc { - cx.emit_span_lint(MISSING_DOCS, cx.tcx.def_span(def_id), BuiltinMissingDoc { - article, - desc, - }); + cx.emit_span_lint( + MISSING_DOCS, + cx.tcx.def_span(def_id), + BuiltinMissingDoc { article, desc }, + ); } } } @@ -581,7 +576,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { // and recommending Copy might be a bad idea. for field in def.all_fields() { let did = field.did; - if cx.tcx.type_of(did).instantiate_identity().is_unsafe_ptr() { + if cx.tcx.type_of(did).instantiate_identity().is_raw_ptr() { return; } } @@ -713,10 +708,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations { .next() .is_some(); if !has_impl { - cx.emit_span_lint(MISSING_DEBUG_IMPLEMENTATIONS, item.span, BuiltinMissingDebugImpl { - tcx: cx.tcx, - def_id: debug, - }); + cx.emit_span_lint( + MISSING_DEBUG_IMPLEMENTATIONS, + item.span, + BuiltinMissingDebugImpl { tcx: cx.tcx, def_id: debug }, + ); } } } @@ -838,22 +834,15 @@ impl EarlyLintPass for DeprecatedAttr { BuiltinDeprecatedAttrLinkSuggestion::Default { suggestion: attr.span } } }; - cx.emit_span_lint(DEPRECATED, attr.span, BuiltinDeprecatedAttrLink { - name, - reason, - link, - suggestion, - }); + cx.emit_span_lint( + DEPRECATED, + attr.span, + BuiltinDeprecatedAttrLink { name, reason, link, suggestion }, + ); } return; } } - if attr.has_name(sym::no_start) || attr.has_name(sym::crate_id) { - cx.emit_span_lint(DEPRECATED, attr.span, BuiltinDeprecatedAttrUsed { - name: pprust::path_to_string(&attr.get_normal_item().path), - suggestion: attr.span, - }); - } } } @@ -887,11 +876,11 @@ fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: & BuiltinUnusedDocCommentSub::BlockHelp } }; - cx.emit_span_lint(UNUSED_DOC_COMMENTS, span, BuiltinUnusedDocComment { - kind: node_kind, - label: node_span, - sub, - }); + cx.emit_span_lint( + UNUSED_DOC_COMMENTS, + span, + BuiltinUnusedDocComment { kind: node_kind, label: node_span, sub }, + ); } } } @@ -1021,16 +1010,18 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { match param.kind { GenericParamKind::Lifetime { .. } => {} GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { - cx.emit_span_lint(NO_MANGLE_GENERIC_ITEMS, span, BuiltinNoMangleGeneric { - suggestion: no_mangle_attr.span, - }); + cx.emit_span_lint( + NO_MANGLE_GENERIC_ITEMS, + span, + BuiltinNoMangleGeneric { suggestion: no_mangle_attr.span }, + ); break; } } } }; match it.kind { - hir::ItemKind::Fn(.., generics, _) => { + hir::ItemKind::Fn { generics, .. } => { if let Some(no_mangle_attr) = attr::find_by_name(attrs, sym::no_mangle) { check_no_mangle_on_generic_fn(no_mangle_attr, None, generics, it.span); } @@ -1050,9 +1041,11 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { // Const items do not refer to a particular location in memory, and therefore // don't have anything to attach a symbol to - cx.emit_span_lint(NO_MANGLE_CONST_ITEMS, it.span, BuiltinConstNoMangle { - suggestion, - }); + cx.emit_span_lint( + NO_MANGLE_CONST_ITEMS, + it.span, + BuiltinConstNoMangle { suggestion }, + ); } } hir::ItemKind::Impl(hir::Impl { generics, items, .. }) => { @@ -1244,8 +1237,8 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller { declare_lint! { /// The `unreachable_pub` lint triggers for `pub` items not reachable from other crates - that - /// means neither directly accessible, nor reexported, nor leaked through things like return - /// types. + /// means neither directly accessible, nor reexported (with `pub use`), nor leaked through + /// things like return types (which the [`unnameable_types`] lint can detect if desired). /// /// ### Example /// @@ -1271,9 +1264,10 @@ declare_lint! { /// `pub(crate)` visibility is recommended to be used instead. This more clearly expresses the /// intent that the item is only visible within its own crate. /// - /// This lint is "allow" by default because it will trigger for a large - /// amount existing Rust code, and has some false-positives. Eventually it - /// is desired for this to become warn-by-default. + /// This lint is "allow" by default because it will trigger for a large amount of existing Rust code. + /// Eventually it is desired for this to become warn-by-default. + /// + /// [`unnameable_types`]: #unnameable-types pub UNREACHABLE_PUB, Allow, "`pub` items not reachable from crate root" @@ -1302,9 +1296,9 @@ impl UnreachablePub { cx.effective_visibilities.effective_vis(def_id).map(|effective_vis| { effective_vis.at_level(rustc_middle::middle::privacy::Level::Reachable) }) - && let parent_parent = cx.tcx.parent_module_from_def_id( - cx.tcx.parent_module_from_def_id(def_id.into()).into(), - ) + && let parent_parent = cx + .tcx + .parent_module_from_def_id(cx.tcx.parent_module_from_def_id(def_id).into()) && *restricted_did == parent_parent.to_local_def_id() && !restricted_did.to_def_id().is_crate_root() { @@ -1317,12 +1311,16 @@ impl UnreachablePub { applicability = Applicability::MaybeIncorrect; } let def_span = cx.tcx.def_span(def_id); - cx.emit_span_lint(UNREACHABLE_PUB, def_span, BuiltinUnreachablePub { - what, - new_vis, - suggestion: (vis_span, applicability), - help: exportable, - }); + cx.emit_span_lint( + UNREACHABLE_PUB, + def_span, + BuiltinUnreachablePub { + what, + new_vis, + suggestion: (vis_span, applicability), + help: exportable, + }, + ); } } } @@ -1466,24 +1464,32 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { let enable_feat_help = cx.tcx.sess.is_nightly_build(); if let [.., label_sp] = *where_spans { - cx.emit_span_lint(TYPE_ALIAS_BOUNDS, where_spans, BuiltinTypeAliasBounds { - in_where_clause: true, - label: label_sp, - enable_feat_help, - suggestions: vec![(generics.where_clause_span, String::new())], - preds: generics.predicates, - ty: ty.take(), - }); + cx.emit_span_lint( + TYPE_ALIAS_BOUNDS, + where_spans, + BuiltinTypeAliasBounds { + in_where_clause: true, + label: label_sp, + enable_feat_help, + suggestions: vec![(generics.where_clause_span, String::new())], + preds: generics.predicates, + ty: ty.take(), + }, + ); } if let [.., label_sp] = *inline_spans { - cx.emit_span_lint(TYPE_ALIAS_BOUNDS, inline_spans, BuiltinTypeAliasBounds { - in_where_clause: false, - label: label_sp, - enable_feat_help, - suggestions: inline_sugg, - preds: generics.predicates, - ty, - }); + cx.emit_span_lint( + TYPE_ALIAS_BOUNDS, + inline_spans, + BuiltinTypeAliasBounds { + in_where_clause: false, + label: label_sp, + enable_feat_help, + suggestions: inline_sugg, + preds: generics.predicates, + ty, + }, + ); } } } @@ -1571,16 +1577,73 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints { | ty::ClauseKind::HostEffect(..) => continue, }; if predicate.is_global() { - cx.emit_span_lint(TRIVIAL_BOUNDS, span, BuiltinTrivialBounds { - predicate_kind_name, - predicate, - }); + cx.emit_span_lint( + TRIVIAL_BOUNDS, + span, + BuiltinTrivialBounds { predicate_kind_name, predicate }, + ); } } } } } +declare_lint! { + /// The `double_negations` lint detects expressions of the form `--x`. + /// + /// ### Example + /// + /// ```rust + /// fn main() { + /// let x = 1; + /// let _b = --x; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Negating something twice is usually the same as not negating it at all. + /// However, a double negation in Rust can easily be confused with the + /// prefix decrement operator that exists in many languages derived from C. + /// Use `-(-x)` if you really wanted to negate the value twice. + /// + /// To decrement a value, use `x -= 1` instead. + pub DOUBLE_NEGATIONS, + Warn, + "detects expressions of the form `--x`" +} + +declare_lint_pass!( + /// Lint for expressions of the form `--x` that can be confused with C's + /// prefix decrement operator. + DoubleNegations => [DOUBLE_NEGATIONS] +); + +impl EarlyLintPass for DoubleNegations { + #[inline] + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { + // only lint on the innermost `--` in a chain of `-` operators, + // even if there are 3 or more negations + if let ExprKind::Unary(UnOp::Neg, ref inner) = expr.kind + && let ExprKind::Unary(UnOp::Neg, ref inner2) = inner.kind + && !matches!(inner2.kind, ExprKind::Unary(UnOp::Neg, _)) + { + cx.emit_span_lint( + DOUBLE_NEGATIONS, + expr.span, + BuiltinDoubleNegations { + add_parens: BuiltinDoubleNegationsAddParens { + start_span: inner.span.shrink_to_lo(), + end_span: inner.span.shrink_to_hi(), + }, + }, + ); + } + } +} + declare_lint_pass!( /// Does nothing as a lint pass, but registers some `Lint`s /// which are used by other parts of the compiler. @@ -1599,7 +1662,8 @@ declare_lint_pass!( UNSTABLE_FEATURES, UNREACHABLE_PUB, TYPE_ALIAS_BOUNDS, - TRIVIAL_BOUNDS + TRIVIAL_BOUNDS, + DOUBLE_NEGATIONS ] ); @@ -1890,12 +1954,11 @@ impl KeywordIdents { return; } - cx.emit_span_lint(lint, ident.span, BuiltinKeywordIdents { - kw: ident, - next: edition, - suggestion: ident.span, - prefix, - }); + cx.emit_span_lint( + lint, + ident.span, + BuiltinKeywordIdents { kw: ident, next: edition, suggestion: ident.span, prefix }, + ); } } @@ -1987,7 +2050,7 @@ impl ExplicitOutlivesRequirements { } let span = bound.span().find_ancestor_inside(predicate_span)?; - if in_external_macro(tcx.sess, span) { + if span.in_external_macro(tcx.sess.source_map()) { return None; } @@ -2316,11 +2379,11 @@ impl EarlyLintPass for IncompleteInternalFeatures { let help = HAS_MIN_FEATURES.contains(&name).then_some(BuiltinIncompleteFeaturesHelp); - cx.emit_span_lint(INCOMPLETE_FEATURES, span, BuiltinIncompleteFeatures { - name, - note, - help, - }); + cx.emit_span_lint( + INCOMPLETE_FEATURES, + span, + BuiltinIncompleteFeatures { name, note, help }, + ); } else { cx.emit_span_lint(INTERNAL_FEATURES, span, BuiltinInternalFeatures { name }); } @@ -2643,20 +2706,24 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { InitKind::Uninit => fluent::lint_builtin_unpermitted_type_init_uninit, }; let sub = BuiltinUnpermittedTypeInitSub { err }; - cx.emit_span_lint(INVALID_VALUE, expr.span, BuiltinUnpermittedTypeInit { - msg, - ty: conjured_ty, - label: expr.span, - sub, - tcx: cx.tcx, - }); + cx.emit_span_lint( + INVALID_VALUE, + expr.span, + BuiltinUnpermittedTypeInit { + msg, + ty: conjured_ty, + label: expr.span, + sub, + tcx: cx.tcx, + }, + ); } } } } declare_lint! { - /// The `deref_nullptr` lint detects when an null pointer is dereferenced, + /// The `deref_nullptr` lint detects when a null pointer is dereferenced, /// which causes [undefined behavior]. /// /// ### Example @@ -2735,9 +2802,11 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr { { // `&raw *NULL` is ok. } else { - cx.emit_span_lint(DEREF_NULLPTR, expr.span, BuiltinDerefNullptr { - label: expr.span, - }); + cx.emit_span_lint( + DEREF_NULLPTR, + expr.span, + BuiltinDerefNullptr { label: expr.span }, + ); } } } @@ -2954,14 +3023,18 @@ impl<'tcx> LateLintPass<'tcx> for AsmLabels { let span = span.unwrap_or(*template_span); match label_kind { AsmLabelKind::Named => { - cx.emit_span_lint(NAMED_ASM_LABELS, span, InvalidAsmLabel::Named { - missing_precise_span, - }); + cx.emit_span_lint( + NAMED_ASM_LABELS, + span, + InvalidAsmLabel::Named { missing_precise_span }, + ); } AsmLabelKind::FormatArg => { - cx.emit_span_lint(NAMED_ASM_LABELS, span, InvalidAsmLabel::FormatArg { - missing_precise_span, - }); + cx.emit_span_lint( + NAMED_ASM_LABELS, + span, + InvalidAsmLabel::FormatArg { missing_precise_span }, + ); } // the binary asm issue only occurs when using intel syntax on x86 targets AsmLabelKind::Binary @@ -2971,10 +3044,11 @@ impl<'tcx> LateLintPass<'tcx> for AsmLabels { Some(InlineAsmArch::X86 | InlineAsmArch::X86_64) | None ) => { - cx.emit_span_lint(BINARY_ASM_LABELS, span, InvalidAsmLabel::Binary { - missing_precise_span, + cx.emit_span_lint( + BINARY_ASM_LABELS, span, - }) + InvalidAsmLabel::Binary { missing_precise_span, span }, + ) } // No lint on anything other than x86 AsmLabelKind::Binary => (), diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 50bb1fb942e98..a67b404e6e16c 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -4,7 +4,7 @@ //! overview of how lints are implemented. use std::cell::Cell; -use std::{iter, slice}; +use std::slice; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync; @@ -233,11 +233,14 @@ impl LintStore { } pub fn register_group_alias(&mut self, lint_name: &'static str, alias: &'static str) { - self.lint_groups.insert(alias, LintGroup { - lint_ids: vec![], - is_externally_loaded: false, - depr: Some(LintAlias { name: lint_name, silent: true }), - }); + self.lint_groups.insert( + alias, + LintGroup { + lint_ids: vec![], + is_externally_loaded: false, + depr: Some(LintAlias { name: lint_name, silent: true }), + }, + ); } pub fn register_group( @@ -252,11 +255,14 @@ impl LintStore { .insert(name, LintGroup { lint_ids: to, is_externally_loaded, depr: None }) .is_none(); if let Some(deprecated) = deprecated_name { - self.lint_groups.insert(deprecated, LintGroup { - lint_ids: vec![], - is_externally_loaded, - depr: Some(LintAlias { name, silent: false }), - }); + self.lint_groups.insert( + deprecated, + LintGroup { + lint_ids: vec![], + is_externally_loaded, + depr: Some(LintAlias { name, silent: false }), + }, + ); } if !new { @@ -718,30 +724,6 @@ impl<'tcx> LateContext<'tcx> { } } - /// Check if a `DefId`'s path matches the given absolute type path usage. - /// - /// Anonymous scopes such as `extern` imports are matched with `kw::Empty`; - /// inherent `impl` blocks are matched with the name of the type. - /// - /// Instead of using this method, it is often preferable to instead use - /// `rustc_diagnostic_item` or a `lang_item`. This is less prone to errors - /// as paths get invalidated if the target definition moves. - /// - /// # Examples - /// - /// ```rust,ignore (no context or def id available) - /// if cx.match_def_path(def_id, &[sym::core, sym::option, sym::Option]) { - /// // The given `def_id` is that of an `Option` type - /// } - /// ``` - /// - /// Used by clippy, but should be replaced by diagnostic items eventually. - pub fn match_def_path(&self, def_id: DefId, path: &[Symbol]) -> bool { - let names = self.get_def_path(def_id); - - names.len() == path.len() && iter::zip(names, path).all(|(a, &b)| a == b) - } - /// Gets the absolute path of `def_id` as a vector of `Symbol`. /// /// # Examples diff --git a/compiler/rustc_lint/src/dangling.rs b/compiler/rustc_lint/src/dangling.rs index 98b717a307068..fd6b3e90adabc 100644 --- a/compiler/rustc_lint/src/dangling.rs +++ b/compiler/rustc_lint/src/dangling.rs @@ -141,7 +141,7 @@ fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { expr.hir_id, method.ident.span, DanglingPointersFromTemporaries { - callee: method.ident.name, + callee: method.ident, ty, ptr_span: method.ident.span, temporary_span: receiver.span, diff --git a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs index 657642e093ccd..ec8f84415759f 100644 --- a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs +++ b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs @@ -1,6 +1,5 @@ use rustc_hir::{self as hir, LangItem}; use rustc_middle::ty; -use rustc_session::lint::FutureIncompatibilityReason; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::sym; use rustc_trait_selection::traits::supertraits; @@ -9,11 +8,12 @@ use crate::lints::{SupertraitAsDerefTarget, SupertraitAsDerefTargetLabel}; use crate::{LateContext, LateLintPass, LintContext}; declare_lint! { - /// The `deref_into_dyn_supertrait` lint is output whenever there is a use of the - /// `Deref` implementation with a `dyn SuperTrait` type as `Output`. + /// The `deref_into_dyn_supertrait` lint is emitted whenever there is a `Deref` implementation + /// for `dyn SubTrait` with a `dyn SuperTrait` type as the `Output` type. /// - /// These implementations will become shadowed when the `trait_upcasting` feature is stabilized. - /// The `deref` functions will no longer be called implicitly, so there might be behavior change. + /// These implementations are "shadowed" by trait upcasting (stabilized since + /// 1.86.0). The `deref` functions is no longer called implicitly, which might + /// change behavior compared to previous rustc versions. /// /// ### Example /// @@ -43,15 +43,14 @@ declare_lint! { /// /// ### Explanation /// - /// The dyn upcasting coercion feature adds new coercion rules, taking priority - /// over certain other coercion rules, which will cause some behavior change. + /// The trait upcasting coercion added a new coercion rule, taking priority over certain other + /// coercion rules, which causes some behavior change compared to older `rustc` versions. + /// + /// `deref` can be still called explicitly, it just isn't called as part of a deref coercion + /// (since trait upcasting coercion takes priority). pub DEREF_INTO_DYN_SUPERTRAIT, - Warn, - "`Deref` implementation usage with a supertrait trait object for output might be shadowed in the future", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseSemanticsChange, - reference: "issue #89460 ", - }; + Allow, + "`Deref` implementation with a supertrait trait object for output is shadowed by trait upcasting", } declare_lint_pass!(DerefIntoDynSupertrait => [DEREF_INTO_DYN_SUPERTRAIT]); @@ -86,14 +85,19 @@ impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait { .find_map(|i| (i.ident.name == sym::Target).then_some(i.span)) .map(|label| SupertraitAsDerefTargetLabel { label }); let span = tcx.def_span(item.owner_id.def_id); - cx.emit_span_lint(DEREF_INTO_DYN_SUPERTRAIT, span, SupertraitAsDerefTarget { - self_ty, - supertrait_principal: supertrait_principal - .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)), - target_principal, - label: span, - label2, - }); + cx.emit_span_lint( + DEREF_INTO_DYN_SUPERTRAIT, + span, + SupertraitAsDerefTarget { + self_ty, + supertrait_principal: supertrait_principal.map_bound(|trait_ref| { + ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) + }), + target_principal, + label: span, + label2, + }, + ); } } } diff --git a/compiler/rustc_lint/src/drop_forget_useless.rs b/compiler/rustc_lint/src/drop_forget_useless.rs index ce23892508b5d..1ca2e4e74ea6f 100644 --- a/compiler/rustc_lint/src/drop_forget_useless.rs +++ b/compiler/rustc_lint/src/drop_forget_useless.rs @@ -163,32 +163,44 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetUseless { }; match fn_name { sym::mem_drop if arg_ty.is_ref() && !drop_is_single_call_in_arm => { - cx.emit_span_lint(DROPPING_REFERENCES, expr.span, DropRefDiag { - arg_ty, - label: arg.span, - sugg: let_underscore_ignore_sugg(), - }); + cx.emit_span_lint( + DROPPING_REFERENCES, + expr.span, + DropRefDiag { arg_ty, label: arg.span, sugg: let_underscore_ignore_sugg() }, + ); } sym::mem_forget if arg_ty.is_ref() => { - cx.emit_span_lint(FORGETTING_REFERENCES, expr.span, ForgetRefDiag { - arg_ty, - label: arg.span, - sugg: let_underscore_ignore_sugg(), - }); + cx.emit_span_lint( + FORGETTING_REFERENCES, + expr.span, + ForgetRefDiag { + arg_ty, + label: arg.span, + sugg: let_underscore_ignore_sugg(), + }, + ); } sym::mem_drop if is_copy && !drop_is_single_call_in_arm => { - cx.emit_span_lint(DROPPING_COPY_TYPES, expr.span, DropCopyDiag { - arg_ty, - label: arg.span, - sugg: let_underscore_ignore_sugg(), - }); + cx.emit_span_lint( + DROPPING_COPY_TYPES, + expr.span, + DropCopyDiag { + arg_ty, + label: arg.span, + sugg: let_underscore_ignore_sugg(), + }, + ); } sym::mem_forget if is_copy => { - cx.emit_span_lint(FORGETTING_COPY_TYPES, expr.span, ForgetCopyDiag { - arg_ty, - label: arg.span, - sugg: let_underscore_ignore_sugg(), - }); + cx.emit_span_lint( + FORGETTING_COPY_TYPES, + expr.span, + ForgetCopyDiag { + arg_ty, + label: arg.span, + sugg: let_underscore_ignore_sugg(), + }, + ); } sym::mem_drop if let ty::Adt(adt, _) = arg_ty.kind() diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index bc7cd3d118c5a..723b894c43bc4 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -246,6 +246,14 @@ impl<'ast, 'ecx, 'tcx, T: EarlyLintPass> ast_visit::Visitor<'ast> } } ast_visit::walk_assoc_item(cx, item, ctxt); + match ctxt { + ast_visit::AssocCtxt::Trait => { + lint_callback!(cx, check_trait_item_post, item); + } + ast_visit::AssocCtxt::Impl => { + lint_callback!(cx, check_impl_item_post, item); + } + } }); } diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 6d73715562b2e..aeb5a03a4f7e3 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -160,8 +160,7 @@ pub(super) fn decorate_lint( .decorate_lint(diag); } BuiltinLintDiag::MissingAbi(label_span, default_abi) => { - lints::MissingAbi { span: label_span, default_abi: default_abi.name() } - .decorate_lint(diag); + lints::MissingAbi { span: label_span, default_abi }.decorate_lint(diag); } BuiltinLintDiag::LegacyDeriveHelpers(label_span) => { lints::LegacyDeriveHelpers { span: label_span }.decorate_lint(diag); @@ -430,7 +429,6 @@ pub(super) fn decorate_lint( BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => { lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag) } - BuiltinLintDiag::WasmCAbi => lints::WasmCAbi.decorate_lint(diag), BuiltinLintDiag::IllFormedAttributeInput { suggestions } => { lints::IllFormedAttributeInput { num_suggestions: suggestions.len(), diff --git a/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs b/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs index eebb131599a2b..946dbc34f7138 100644 --- a/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs +++ b/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs @@ -10,19 +10,35 @@ use crate::lints; const MAX_CHECK_CFG_NAMES_OR_VALUES: usize = 35; +enum FilterWellKnownNames { + Yes, + No, +} + fn sort_and_truncate_possibilities( sess: &Session, mut possibilities: Vec, + filter_well_known_names: FilterWellKnownNames, ) -> (Vec, usize) { + let possibilities_len = possibilities.len(); + let n_possibilities = if sess.opts.unstable_opts.check_cfg_all_expected { possibilities.len() } else { + match filter_well_known_names { + FilterWellKnownNames::Yes => { + possibilities.retain(|cfg_name| { + !sess.psess.check_config.well_known_names.contains(cfg_name) + }); + } + FilterWellKnownNames::No => {} + }; std::cmp::min(possibilities.len(), MAX_CHECK_CFG_NAMES_OR_VALUES) }; possibilities.sort_by(|s1, s2| s1.as_str().cmp(s2.as_str())); - let and_more = possibilities.len().saturating_sub(n_possibilities); + let and_more = possibilities_len.saturating_sub(n_possibilities); possibilities.truncate(n_possibilities); (possibilities, and_more) } @@ -119,7 +135,7 @@ pub(super) fn unexpected_cfg_name( }; let is_from_cargo = rustc_session::utils::was_invoked_from_cargo(); - let is_from_external_macro = rustc_middle::lint::in_external_macro(sess, name_span); + let is_from_external_macro = name_span.in_external_macro(sess.source_map()); let mut is_feature_cfg = name == sym::feature; let code_sugg = if is_feature_cfg && is_from_cargo { @@ -198,8 +214,10 @@ pub(super) fn unexpected_cfg_name( } else { vec![] }; + + let (possibilities, and_more) = + sort_and_truncate_possibilities(sess, possibilities, FilterWellKnownNames::Yes); let expected_names = if !possibilities.is_empty() { - let (possibilities, and_more) = sort_and_truncate_possibilities(sess, possibilities); let possibilities: Vec<_> = possibilities.into_iter().map(|s| Ident::new(s, name_span)).collect(); Some(lints::unexpected_cfg_name::ExpectedNames { @@ -263,14 +281,17 @@ pub(super) fn unexpected_cfg_value( .collect(); let is_from_cargo = rustc_session::utils::was_invoked_from_cargo(); - let is_from_external_macro = rustc_middle::lint::in_external_macro(sess, name_span); + let is_from_external_macro = name_span.in_external_macro(sess.source_map()); // Show the full list if all possible values for a given name, but don't do it // for names as the possibilities could be very long let code_sugg = if !possibilities.is_empty() { let expected_values = { - let (possibilities, and_more) = - sort_and_truncate_possibilities(sess, possibilities.clone()); + let (possibilities, and_more) = sort_and_truncate_possibilities( + sess, + possibilities.clone(), + FilterWellKnownNames::No, + ); lints::unexpected_cfg_value::ExpectedValues { name, have_none_possibility, diff --git a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs index 6556a8d8f2d05..7ead8eafbd5aa 100644 --- a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs +++ b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs @@ -54,10 +54,11 @@ fn enforce_mem_discriminant( ) { let ty_param = cx.typeck_results().node_args(func_expr.hir_id).type_at(0); if is_non_enum(ty_param) { - cx.emit_span_lint(ENUM_INTRINSICS_NON_ENUMS, expr_span, EnumIntrinsicsMemDiscriminate { - ty_param, - note: args_span, - }); + cx.emit_span_lint( + ENUM_INTRINSICS_NON_ENUMS, + expr_span, + EnumIntrinsicsMemDiscriminate { ty_param, note: args_span }, + ); } } diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs index 289e2c9b72243..ef79f1301e5f6 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -57,7 +57,7 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option) { let expect_id = canonicalize_id(expect_id); if !fulfilled_expectations.contains(&expect_id) - && tool_filter.map_or(true, |filter| expectation.lint_tool == Some(filter)) + && tool_filter.is_none_or(|filter| expectation.lint_tool == Some(filter)) { let rationale = expectation.reason.map(|rationale| ExpectationNote { rationale }); let note = expectation.is_unfulfilled_lint_expectations; diff --git a/compiler/rustc_lint/src/for_loops_over_fallibles.rs b/compiler/rustc_lint/src/for_loops_over_fallibles.rs index dbc920ea5aee0..0a5c52d65ec97 100644 --- a/compiler/rustc_lint/src/for_loops_over_fallibles.rs +++ b/compiler/rustc_lint/src/for_loops_over_fallibles.rs @@ -96,14 +96,11 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles { end_span: pat.span.between(arg.span), }; - cx.emit_span_lint(FOR_LOOPS_OVER_FALLIBLES, arg.span, ForLoopsOverFalliblesDiag { - article, - ref_prefix, - ty, - sub, - question_mark, - suggestion, - }); + cx.emit_span_lint( + FOR_LOOPS_OVER_FALLIBLES, + arg.span, + ForLoopsOverFalliblesDiag { article, ref_prefix, ty, sub, question_mark, suggestion }, + ); } } diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index 45b188205d228..636779fe9b4cf 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -241,10 +241,7 @@ fn structurally_same_type_impl<'tcx>( if let ty::Adt(def, args) = *ty.kind() { let is_transparent = def.repr().transparent(); let is_non_null = types::nonnull_optimization_guaranteed(tcx, def); - debug!( - "non_transparent_ty({:?}) -- type is transparent? {}, type is non-null? {}", - ty, is_transparent, is_non_null - ); + debug!(?ty, is_transparent, is_non_null); if is_transparent && !is_non_null { debug_assert_eq!(def.variants().len(), 1); let v = &def.variant(FIRST_VARIANT); @@ -378,14 +375,14 @@ fn structurally_same_type_impl<'tcx>( // An Adt and a primitive or pointer type. This can be FFI-safe if non-null // enum layout optimisation is being applied. - (Adt(..), _) if is_primitive_or_pointer(b) => { + (Adt(..) | Pat(..), _) if is_primitive_or_pointer(b) => { if let Some(a_inner) = types::repr_nullable_ptr(tcx, typing_env, a, ckind) { a_inner == b } else { false } } - (_, Adt(..)) if is_primitive_or_pointer(a) => { + (_, Adt(..) | Pat(..)) if is_primitive_or_pointer(a) => { if let Some(b_inner) = types::repr_nullable_ptr(tcx, typing_env, b, ckind) { b_inner == a } else { diff --git a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs index 406aa1005dfba..491c2826baaa1 100644 --- a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs +++ b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs @@ -74,13 +74,11 @@ impl HiddenUnicodeCodepoints { HiddenUnicodeCodepointsDiagSub::NoEscape { spans } }; - cx.emit_span_lint(TEXT_DIRECTION_CODEPOINT_IN_LITERAL, span, HiddenUnicodeCodepointsDiag { - label, - count, - span_label: span, - labels, - sub, - }); + cx.emit_span_lint( + TEXT_DIRECTION_CODEPOINT_IN_LITERAL, + span, + HiddenUnicodeCodepointsDiag { label, count, span_label: span, labels, sub }, + ); } fn check_literal( diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index 259ea908fc60b..297559f012832 100644 --- a/compiler/rustc_lint/src/if_let_rescope.rs +++ b/compiler/rustc_lint/src/if_let_rescope.rs @@ -1,7 +1,7 @@ use std::iter::repeat; use std::ops::ControlFlow; -use hir::intravisit::Visitor; +use hir::intravisit::{self, Visitor}; use rustc_ast::Recovered; use rustc_errors::{ Applicability, Diag, EmissionGuarantee, SubdiagMessageOp, Subdiagnostic, SuggestionStyle, @@ -9,7 +9,8 @@ use rustc_errors::{ use rustc_hir::{self as hir, HirIdSet}; use rustc_macros::LintDiagnostic; use rustc_middle::ty::TyCtxt; -use rustc_session::lint::{FutureIncompatibilityReason, Level}; +use rustc_middle::ty::adjustment::Adjust; +use rustc_session::lint::{FutureIncompatibilityReason, LintId}; use rustc_session::{declare_lint, impl_lint_pass}; use rustc_span::Span; use rustc_span::edition::Edition; @@ -160,7 +161,7 @@ impl IfLetRescope { let lifetime_end = source_map.end_point(conseq.span); if let ControlFlow::Break(significant_dropper) = - (FindSignificantDropper { cx }).visit_expr(init) + (FindSignificantDropper { cx }).check_if_let_scrutinee(init) { first_if_to_lint = first_if_to_lint.or_else(|| Some((span, expr.hir_id))); significant_droppers.push(significant_dropper); @@ -221,20 +222,25 @@ impl IfLetRescope { } } if let Some((span, hir_id)) = first_if_to_lint { - tcx.emit_node_span_lint(IF_LET_RESCOPE, hir_id, span, IfLetRescopeLint { - significant_droppers, - lifetime_ends, - rewrite: first_if_to_rewrite.then_some(IfLetRescopeRewrite { - match_heads, - consequent_heads, - closing_brackets: ClosingBrackets { - span: expr_end, - count: closing_brackets, - empty_alt, - }, - alt_heads, - }), - }); + tcx.emit_node_span_lint( + IF_LET_RESCOPE, + hir_id, + span, + IfLetRescopeLint { + significant_droppers, + lifetime_ends, + rewrite: first_if_to_rewrite.then_some(IfLetRescopeRewrite { + match_heads, + consequent_heads, + closing_brackets: ClosingBrackets { + span: expr_end, + count: closing_brackets, + empty_alt, + }, + alt_heads, + }), + }, + ); } } } @@ -245,12 +251,12 @@ impl_lint_pass!( impl<'tcx> LateLintPass<'tcx> for IfLetRescope { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { - if expr.span.edition().at_least_rust_2024() { - return; - } - if let (Level::Allow, _) = cx.tcx.lint_level_at_node(IF_LET_RESCOPE, expr.hir_id) { + if expr.span.edition().at_least_rust_2024() + || cx.tcx.lints_that_dont_need_to_run(()).contains(&LintId::of(IF_LET_RESCOPE)) + { return; } + if let hir::ExprKind::Loop(block, _label, hir::LoopSource::While, _span) = expr.kind && let Some(value) = block.expr && let hir::ExprKind::If(cond, _conseq, _alt) = value.kind @@ -290,7 +296,6 @@ struct IfLetRescopeLint { rewrite: Option, } -// #[derive(Subdiagnostic)] struct IfLetRescopeRewrite { match_heads: Vec, consequent_heads: Vec, @@ -359,96 +364,97 @@ enum SingleArmMatchBegin { WithoutOpenBracket(Span), } -struct FindSignificantDropper<'tcx, 'a> { +struct FindSignificantDropper<'a, 'tcx> { cx: &'a LateContext<'tcx>, } -impl<'tcx, 'a> Visitor<'tcx> for FindSignificantDropper<'tcx, 'a> { - type Result = ControlFlow; +impl<'tcx> FindSignificantDropper<'_, 'tcx> { + /// Check the scrutinee of an `if let` to see if it promotes any temporary values + /// that would change drop order in edition 2024. Specifically, it checks the value + /// of the scrutinee itself, and also recurses into the expression to find any ref + /// exprs (or autoref) which would promote temporaries that would be scoped to the + /// end of this `if`. + fn check_if_let_scrutinee(&mut self, init: &'tcx hir::Expr<'tcx>) -> ControlFlow { + self.check_promoted_temp_with_drop(init)?; + self.visit_expr(init) + } - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Self::Result { - if self + /// Check that an expression is not a promoted temporary with a significant + /// drop impl. + /// + /// An expression is a promoted temporary if it has an addr taken (i.e. `&expr` or autoref) + /// or is the scrutinee of the `if let`, *and* the expression is not a place + /// expr, and it has a significant drop. + fn check_promoted_temp_with_drop(&self, expr: &'tcx hir::Expr<'tcx>) -> ControlFlow { + if !expr.is_place_expr(|base| { + self.cx + .typeck_results() + .adjustments() + .get(base.hir_id) + .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_)))) + }) && self .cx .typeck_results() .expr_ty(expr) .has_significant_drop(self.cx.tcx, self.cx.typing_env()) { - return ControlFlow::Break(expr.span); + ControlFlow::Break(expr.span) + } else { + ControlFlow::Continue(()) } - match expr.kind { - hir::ExprKind::ConstBlock(_) - | hir::ExprKind::Lit(_) - | hir::ExprKind::Path(_) - | hir::ExprKind::Assign(_, _, _) - | hir::ExprKind::AssignOp(_, _, _) - | hir::ExprKind::Break(_, _) - | hir::ExprKind::Continue(_) - | hir::ExprKind::Ret(_) - | hir::ExprKind::Become(_) - | hir::ExprKind::InlineAsm(_) - | hir::ExprKind::OffsetOf(_, _) - | hir::ExprKind::Repeat(_, _) - | hir::ExprKind::Err(_) - | hir::ExprKind::Struct(_, _, _) - | hir::ExprKind::Closure(_) - | hir::ExprKind::Block(_, _) - | hir::ExprKind::DropTemps(_) - | hir::ExprKind::Loop(_, _, _, _) => ControlFlow::Continue(()), + } +} - hir::ExprKind::Tup(exprs) | hir::ExprKind::Array(exprs) => { - for expr in exprs { - self.visit_expr(expr)?; - } - ControlFlow::Continue(()) - } - hir::ExprKind::Call(callee, args) => { - self.visit_expr(callee)?; - for expr in args { - self.visit_expr(expr)?; - } - ControlFlow::Continue(()) - } - hir::ExprKind::MethodCall(_, receiver, args, _) => { - self.visit_expr(receiver)?; - for expr in args { - self.visit_expr(expr)?; +impl<'tcx> Visitor<'tcx> for FindSignificantDropper<'_, 'tcx> { + type Result = ControlFlow; + + fn visit_block(&mut self, b: &'tcx hir::Block<'tcx>) -> Self::Result { + // Blocks introduce temporary terminating scope for all of its + // statements, so just visit the tail expr, skipping over any + // statements. This prevents false positives like `{ let x = &Drop; }`. + if let Some(expr) = b.expr { self.visit_expr(expr) } else { ControlFlow::Continue(()) } + } + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Self::Result { + // Check for promoted temporaries from autoref, e.g. + // `if let None = TypeWithDrop.as_ref() {} else {}` + // where `fn as_ref(&self) -> Option<...>`. + for adj in self.cx.typeck_results().expr_adjustments(expr) { + match adj.kind { + // Skip when we hit the first deref expr. + Adjust::Deref(_) => break, + Adjust::Borrow(_) => { + self.check_promoted_temp_with_drop(expr)?; } - ControlFlow::Continue(()) + _ => {} } - hir::ExprKind::Index(left, right, _) | hir::ExprKind::Binary(_, left, right) => { - self.visit_expr(left)?; - self.visit_expr(right) - } - hir::ExprKind::Unary(_, expr) - | hir::ExprKind::Cast(expr, _) - | hir::ExprKind::Type(expr, _) - | hir::ExprKind::UnsafeBinderCast(_, expr, _) - | hir::ExprKind::Yield(expr, _) - | hir::ExprKind::AddrOf(_, _, expr) - | hir::ExprKind::Match(expr, _, _) - | hir::ExprKind::Field(expr, _) - | hir::ExprKind::Let(&hir::LetExpr { - init: expr, - span: _, - pat: _, - ty: _, - recovered: Recovered::No, - }) => self.visit_expr(expr), - hir::ExprKind::Let(_) => ControlFlow::Continue(()), + } - hir::ExprKind::If(cond, _, _) => { - if let hir::ExprKind::Let(hir::LetExpr { - init, - span: _, - pat: _, - ty: _, - recovered: Recovered::No, - }) = cond.kind - { - self.visit_expr(init)?; - } - ControlFlow::Continue(()) + match expr.kind { + // Account for cases like `if let None = Some(&Drop) {} else {}`. + hir::ExprKind::AddrOf(_, _, expr) => { + self.check_promoted_temp_with_drop(expr)?; + intravisit::walk_expr(self, expr) + } + // `(Drop, ()).1` introduces a temporary and then moves out of + // part of it, therefore we should check it for temporaries. + // FIXME: This may have false positives if we move the part + // that actually has drop, but oh well. + hir::ExprKind::Index(expr, _, _) | hir::ExprKind::Field(expr, _) => { + self.check_promoted_temp_with_drop(expr)?; + intravisit::walk_expr(self, expr) } + // If always introduces a temporary terminating scope for its cond and arms, + // so don't visit them. + hir::ExprKind::If(..) => ControlFlow::Continue(()), + // Match introduces temporary terminating scopes for arms, so don't visit + // them, and only visit the scrutinee to account for cases like: + // `if let None = match &Drop { _ => Some(1) } {} else {}`. + hir::ExprKind::Match(scrut, _, _) => self.visit_expr(scrut), + // Self explanatory. + hir::ExprKind::DropTemps(_) => ControlFlow::Continue(()), + // Otherwise, walk into the expr's parts. + _ => intravisit::walk_expr(self, expr), } } } diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index b5dd6cf16eefa..d2956d94685f1 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -25,8 +25,8 @@ use rustc_span::{Span, Symbol}; use rustc_trait_selection::errors::{ AddPreciseCapturingForOvercapture, impl_trait_overcapture_suggestion, }; +use rustc_trait_selection::regions::OutlivesEnvironmentBuildExt; use rustc_trait_selection::traits::ObligationCtxt; -use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt; use crate::{LateContext, LateLintPass, fluent_generated as fluent}; @@ -99,7 +99,7 @@ declare_lint! { /// To fix this, remove the `use<'a>`, since the lifetime is already captured /// since it is in scope. pub IMPL_TRAIT_REDUNDANT_CAPTURES, - Warn, + Allow, "redundant precise-capturing `use<...>` syntax on an `impl Trait`", } @@ -112,7 +112,7 @@ declare_lint_pass!( impl<'tcx> LateLintPass<'tcx> for ImplTraitOvercaptures { fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'tcx>) { match &it.kind { - hir::ItemKind::Fn(..) => check_fn(cx.tcx, it.owner_id.def_id), + hir::ItemKind::Fn { .. } => check_fn(cx.tcx, it.owner_id.def_id), _ => {} } } @@ -190,9 +190,7 @@ fn check_fn(tcx: TyCtxt<'_>, parent_def_id: LocalDefId) { let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); let ocx = ObligationCtxt::new(&infcx); let assumed_wf_tys = ocx.assumed_wf_types(param_env, parent_def_id).unwrap_or_default(); - let implied_bounds = - infcx.implied_bounds_tys_compat(param_env, parent_def_id, &assumed_wf_tys, false); - OutlivesEnvironment::with_bounds(param_env, implied_bounds) + OutlivesEnvironment::new(&infcx, parent_def_id, param_env, assumed_wf_tys) }), }); } @@ -316,12 +314,10 @@ where // We only computed variance of lifetimes... debug_assert_matches!(self.tcx.def_kind(def_id), DefKind::LifetimeParam); let uncaptured = match *kind { - ParamKind::Early(name, index) => { - ty::Region::new_early_param(self.tcx, ty::EarlyParamRegion { - name, - index, - }) - } + ParamKind::Early(name, index) => ty::Region::new_early_param( + self.tcx, + ty::EarlyParamRegion { name, index }, + ), ParamKind::Free(def_id, name) => ty::Region::new_late_param( self.tcx, self.parent_def_id.to_def_id(), diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index b31a4c74787fa..ddc9ae1594f90 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -5,8 +5,8 @@ use rustc_ast as ast; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_hir::{ - BinOp, BinOpKind, Expr, ExprKind, GenericArg, HirId, Impl, Item, ItemKind, Node, Pat, PatKind, - Path, PathSegment, QPath, Ty, TyKind, + AmbigArg, BinOp, BinOpKind, Expr, ExprKind, GenericArg, HirId, Impl, Item, ItemKind, Node, Pat, + PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Ty, TyKind, }; use rustc_middle::ty::{self, GenericArgsRef, Ty as MiddleTy}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -48,10 +48,11 @@ impl LateLintPass<'_> for DefaultHashTypes { Some(sym::HashSet) => "FxHashSet", _ => return, }; - cx.emit_span_lint(DEFAULT_HASH_TYPES, path.span, DefaultHashTypesDiag { - preferred, - used: cx.tcx.item_name(def_id), - }); + cx.emit_span_lint( + DEFAULT_HASH_TYPES, + path.span, + DefaultHashTypesDiag { preferred, used: cx.tcx.item_name(def_id) }, + ); } } @@ -107,14 +108,18 @@ impl LateLintPass<'_> for QueryStability { { let def_id = instance.def_id(); if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) { - cx.emit_span_lint(POTENTIAL_QUERY_INSTABILITY, span, QueryInstability { - query: cx.tcx.item_name(def_id), - }); + cx.emit_span_lint( + POTENTIAL_QUERY_INSTABILITY, + span, + QueryInstability { query: cx.tcx.item_name(def_id) }, + ); } if cx.tcx.has_attr(def_id, sym::rustc_lint_untracked_query_information) { - cx.emit_span_lint(UNTRACKED_QUERY_INFORMATION, span, QueryUntracked { - method: cx.tcx.item_name(def_id), - }); + cx.emit_span_lint( + UNTRACKED_QUERY_INFORMATION, + span, + QueryUntracked { method: cx.tcx.item_name(def_id) }, + ); } } } @@ -159,16 +164,14 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { } } - fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx Ty<'tcx>) { + fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx Ty<'tcx, AmbigArg>) { match &ty.kind { TyKind::Path(QPath::Resolved(_, path)) => { if lint_ty_kind_usage(cx, &path.res) { let span = match cx.tcx.parent_hir_node(ty.hir_id) { - Node::Pat(Pat { - kind: - PatKind::Path(qpath) - | PatKind::TupleStruct(qpath, ..) - | PatKind::Struct(qpath, ..), + Node::PatExpr(PatExpr { kind: PatExprKind::Path(qpath), .. }) + | Node::Pat(Pat { + kind: PatKind::TupleStruct(qpath, ..) | PatKind::Struct(qpath, ..), .. }) | Node::Expr( @@ -188,9 +191,11 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { match span { Some(span) => { - cx.emit_span_lint(USAGE_OF_TY_TYKIND, path.span, TykindKind { - suggestion: span, - }); + cx.emit_span_lint( + USAGE_OF_TY_TYKIND, + path.span, + TykindKind { suggestion: span }, + ); } None => cx.emit_span_lint(USAGE_OF_TY_TYKIND, path.span, TykindDiag), } @@ -198,10 +203,11 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { && path.segments.len() > 1 && let Some(ty) = is_ty_or_ty_ctxt(cx, path) { - cx.emit_span_lint(USAGE_OF_QUALIFIED_TY, path.span, TyQualified { - ty, - suggestion: path.span, - }); + cx.emit_span_lint( + USAGE_OF_QUALIFIED_TY, + path.span, + TyQualified { ty, suggestion: path.span }, + ); } } _ => {} @@ -555,9 +561,11 @@ impl LateLintPass<'_> for BadOptAccess { && let Some(lit) = item.lit() && let ast::LitKind::Str(val, _) = lit.kind { - cx.emit_span_lint(BAD_OPT_ACCESS, expr.span, BadOptAccessDiag { - msg: val.as_str(), - }); + cx.emit_span_lint( + BAD_OPT_ACCESS, + expr.span, + BadOptAccessDiag { msg: val.as_str() }, + ); } } } diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index a4d50c731042b..3ee908ba9bf44 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -8,9 +8,8 @@ use std::cell::Cell; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::sync::join; -use rustc_hir as hir; use rustc_hir::def_id::{LocalDefId, LocalModDefId}; -use rustc_hir::{HirId, intravisit as hir_visit}; +use rustc_hir::{self as hir, AmbigArg, HirId, intravisit as hir_visit}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::Session; @@ -153,6 +152,10 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas hir_visit::walk_pat(self, p); } + fn visit_lit(&mut self, hir_id: HirId, lit: &'tcx hir::Lit, negated: bool) { + lint_callback!(self, check_lit, hir_id, lit, negated); + } + fn visit_expr_field(&mut self, field: &'tcx hir::ExprField<'tcx>) { self.with_lint_attrs(field.hir_id, |cx| hir_visit::walk_expr_field(cx, field)) } @@ -214,15 +217,11 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas }) } - fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) { lint_callback!(self, check_ty, t); hir_visit::walk_ty(self, t); } - fn visit_infer(&mut self, inf: &'tcx hir::InferArg) { - hir_visit::walk_inf(self, inf); - } - fn visit_mod(&mut self, m: &'tcx hir::Mod<'tcx>, _: Span, n: HirId) { if !self.context.only_module { self.process_mod(m, n); @@ -463,7 +462,7 @@ pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>) { || { tcx.sess.time("module_lints", || { // Run per-module lints - tcx.hir().par_for_each_module(|module| tcx.ensure().lint_mod(module)); + tcx.hir().par_for_each_module(|module| tcx.ensure_ok().lint_mod(module)); }); }, ); diff --git a/compiler/rustc_lint/src/let_underscore.rs b/compiler/rustc_lint/src/let_underscore.rs index 9e4e83331646c..3e2c1af97f437 100644 --- a/compiler/rustc_lint/src/let_underscore.rs +++ b/compiler/rustc_lint/src/let_underscore.rs @@ -156,10 +156,11 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { }; if is_sync_lock { let span = MultiSpan::from_span(pat.span); - cx.emit_span_lint(LET_UNDERSCORE_LOCK, span, NonBindingLet::SyncLock { - sub, - pat: pat.span, - }); + cx.emit_span_lint( + LET_UNDERSCORE_LOCK, + span, + NonBindingLet::SyncLock { sub, pat: pat.span }, + ); // Only emit let_underscore_drop for top-level `_` patterns. } else if can_use_init.is_some() { cx.emit_span_lint(LET_UNDERSCORE_DROP, local.span, NonBindingLet::DropType { sub }); diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 1465c2cff7bc1..c8de5e877531e 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -21,6 +21,7 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(trait_upcasting))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(array_windows)] @@ -32,7 +33,7 @@ #![feature(let_chains)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] -#![feature(trait_upcasting)] +#![feature(try_blocks)] #![warn(unreachable_pub)] // tidy-alphabetical-end @@ -181,6 +182,7 @@ early_lint_methods!( UnusedDocComment: UnusedDocComment, Expr2024: Expr2024, Precedence: Precedence, + DoubleNegations: DoubleNegations, ] ] ); @@ -594,6 +596,11 @@ fn register_builtins(store: &mut LintStore) { for more information", ); store.register_removed("unsupported_calling_conventions", "converted into hard error"); + store.register_removed( + "cenum_impl_drop_cast", + "converted into hard error, \ + see for more information", + ); } fn register_internals(store: &mut LintStore) { @@ -621,18 +628,23 @@ fn register_internals(store: &mut LintStore) { // `DIAGNOSTIC_OUTSIDE_OF_IMPL` here because `-Wrustc::internal` is provided to every crate and // these lints will trigger all of the time - change this once migration to diagnostic structs // and translation is completed - store.register_group(false, "rustc::internal", None, vec![ - LintId::of(DEFAULT_HASH_TYPES), - LintId::of(POTENTIAL_QUERY_INSTABILITY), - LintId::of(UNTRACKED_QUERY_INFORMATION), - LintId::of(USAGE_OF_TY_TYKIND), - LintId::of(PASS_BY_VALUE), - LintId::of(LINT_PASS_IMPL_WITHOUT_MACRO), - LintId::of(USAGE_OF_QUALIFIED_TY), - LintId::of(NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT), - LintId::of(BAD_OPT_ACCESS), - LintId::of(SPAN_USE_EQ_CTXT), - ]); + store.register_group( + false, + "rustc::internal", + None, + vec![ + LintId::of(DEFAULT_HASH_TYPES), + LintId::of(POTENTIAL_QUERY_INSTABILITY), + LintId::of(UNTRACKED_QUERY_INFORMATION), + LintId::of(USAGE_OF_TY_TYKIND), + LintId::of(PASS_BY_VALUE), + LintId::of(LINT_PASS_IMPL_WITHOUT_MACRO), + LintId::of(USAGE_OF_QUALIFIED_TY), + LintId::of(NON_GLOB_IMPORT_OF_TYPE_IR_INHERENT), + LintId::of(BAD_OPT_ACCESS), + LintId::of(SPAN_USE_EQ_CTXT), + ], + ); } #[cfg(test)] diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index ac995b59caff1..368d36bfdd0b2 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2,6 +2,7 @@ #![allow(rustc::untranslatable_diagnostic)] use std::num::NonZero; +use rustc_abi::ExternAbi; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagMessage, DiagStyledString, ElidedLifetimeInPathSubdiag, @@ -9,6 +10,7 @@ use rustc_errors::{ }; use rustc_hir::def::Namespace; use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::VisitorExt; use rustc_hir::{self as hir, MissingLifetimeKind}; use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::inhabitedness::InhabitedPredicate; @@ -176,19 +178,6 @@ pub(crate) enum BuiltinDeprecatedAttrLinkSuggestion<'a> { }, } -#[derive(LintDiagnostic)] -#[diag(lint_builtin_deprecated_attr_used)] -pub(crate) struct BuiltinDeprecatedAttrUsed { - pub name: String, - #[suggestion( - lint_builtin_deprecated_attr_default_suggestion, - style = "short", - code = "", - applicability = "machine-applicable" - )] - pub suggestion: Span, -} - #[derive(LintDiagnostic)] #[diag(lint_builtin_unused_doc_comment)] pub(crate) struct BuiltinUnusedDocComment<'a> { @@ -293,7 +282,7 @@ impl<'a> LintDiagnostic<'a, ()> for BuiltinTypeAliasBounds<'_> { // avoid doing throwaway work in case the lint ends up getting suppressed. let mut collector = ShorthandAssocTyCollector { qselves: Vec::new() }; if let Some(ty) = self.ty { - hir::intravisit::Visitor::visit_ty(&mut collector, ty); + collector.visit_ty_unambig(ty); } let affect_object_lifetime_defaults = self @@ -343,6 +332,24 @@ pub(crate) struct BuiltinTrivialBounds<'a> { pub predicate: Clause<'a>, } +#[derive(LintDiagnostic)] +#[diag(lint_builtin_double_negations)] +#[note(lint_note)] +#[note(lint_note_decrement)] +pub(crate) struct BuiltinDoubleNegations { + #[subdiagnostic] + pub add_parens: BuiltinDoubleNegationsAddParens, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(lint_add_parens_suggestion, applicability = "maybe-incorrect")] +pub(crate) struct BuiltinDoubleNegationsAddParens { + #[suggestion_part(code = "(")] + pub start_span: Span, + #[suggestion_part(code = ")")] + pub end_span: Span, +} + #[derive(LintDiagnostic)] pub(crate) enum BuiltinEllipsisInclusiveRangePatternsLint { #[diag(lint_builtin_ellipsis_inclusive_range_patterns)] @@ -1139,10 +1146,12 @@ pub(crate) struct IgnoredUnlessCrateSpecified<'a> { #[derive(LintDiagnostic)] #[diag(lint_dangling_pointers_from_temporaries)] #[note] -#[help] +#[help(lint_help_bind)] +#[help(lint_help_returned)] +#[help(lint_help_visit)] // FIXME: put #[primary_span] on `ptr_span` once it does not cause conflicts pub(crate) struct DanglingPointersFromTemporaries<'tcx> { - pub callee: Symbol, + pub callee: Ident, pub ty: Ty<'tcx>, #[label(lint_label_ptr)] pub ptr_span: Span, @@ -1343,7 +1352,7 @@ pub(crate) enum NonUpperCaseGlobalSub { #[diag(lint_noop_method_call)] #[note] pub(crate) struct NoopMethodCallDiag<'a> { - pub method: Symbol, + pub method: Ident, pub orig_ty: Ty<'a>, pub trait_: Symbol, #[suggestion(code = "", applicability = "machine-applicable")] @@ -1692,6 +1701,10 @@ pub(crate) struct OverflowingLiteral<'a> { pub lit: String, } +#[derive(LintDiagnostic)] +#[diag(lint_uses_power_alignment)] +pub(crate) struct UsesPowerAlignment; + #[derive(LintDiagnostic)] #[diag(lint_unused_comparisons)] pub(crate) struct UnusedComparisons; @@ -2559,10 +2572,6 @@ pub(crate) struct UnusedCrateDependency { pub local_crate: Symbol, } -#[derive(LintDiagnostic)] -#[diag(lint_wasm_c_abi)] -pub(crate) struct WasmCAbi; - #[derive(LintDiagnostic)] #[diag(lint_ill_formed_attribute_input)] pub(crate) struct IllFormedAttributeInput { @@ -2825,9 +2834,9 @@ pub(crate) struct PatternsInFnsWithoutBodySub { #[derive(LintDiagnostic)] #[diag(lint_extern_without_abi)] pub(crate) struct MissingAbi { - #[suggestion(code = "extern \"{default_abi}\"", applicability = "machine-applicable")] + #[suggestion(code = "extern {default_abi}", applicability = "machine-applicable")] pub span: Span, - pub default_abi: &'static str, + pub default_abi: ExternAbi, } #[derive(LintDiagnostic)] diff --git a/compiler/rustc_lint/src/map_unit_fn.rs b/compiler/rustc_lint/src/map_unit_fn.rs index 776d51a672718..3b27e45613690 100644 --- a/compiler/rustc_lint/src/map_unit_fn.rs +++ b/compiler/rustc_lint/src/map_unit_fn.rs @@ -60,25 +60,39 @@ impl<'tcx> LateLintPass<'tcx> for MapUnitFn { let fn_ty = cx.tcx.fn_sig(id).skip_binder(); let ret_ty = fn_ty.output().skip_binder(); if is_unit_type(ret_ty) { - cx.emit_span_lint(MAP_UNIT_FN, span, MappingToUnit { - function_label: cx.tcx.span_of_impl(*id).unwrap_or(default_span), - argument_label: args[0].span, - map_label: arg_ty.default_span(cx.tcx), - suggestion: path.ident.span, - replace: "for_each".to_string(), - }) + cx.emit_span_lint( + MAP_UNIT_FN, + span, + MappingToUnit { + function_label: cx + .tcx + .span_of_impl(*id) + .unwrap_or(default_span), + argument_label: args[0].span, + map_label: arg_ty.default_span(cx.tcx), + suggestion: path.ident.span, + replace: "for_each".to_string(), + }, + ) } } else if let ty::Closure(id, subs) = arg_ty.kind() { let cl_ty = subs.as_closure().sig(); let ret_ty = cl_ty.output().skip_binder(); if is_unit_type(ret_ty) { - cx.emit_span_lint(MAP_UNIT_FN, span, MappingToUnit { - function_label: cx.tcx.span_of_impl(*id).unwrap_or(default_span), - argument_label: args[0].span, - map_label: arg_ty.default_span(cx.tcx), - suggestion: path.ident.span, - replace: "for_each".to_string(), - }) + cx.emit_span_lint( + MAP_UNIT_FN, + span, + MappingToUnit { + function_label: cx + .tcx + .span_of_impl(*id) + .unwrap_or(default_span), + argument_label: args[0].span, + map_label: arg_ty.default_span(cx.tcx), + suggestion: path.ident.span, + replace: "for_each".to_string(), + }, + ) } } } diff --git a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs index 9fde35f82d823..35db8632625ec 100644 --- a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs +++ b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs @@ -37,7 +37,7 @@ declare_lint_pass!(MultipleSupertraitUpcastable => [MULTIPLE_SUPERTRAIT_UPCASTAB impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { let def_id = item.owner_id.to_def_id(); - // NOTE(nbdd0121): use `object_safety_violations` instead of `is_dyn_compatible` because + // NOTE(nbdd0121): use `dyn_compatibility_violations` instead of `is_dyn_compatible` because // the latter will report `where_clause_object_safety` lint. if let hir::ItemKind::Trait(_, _, _, _, _) = item.kind && cx.tcx.is_dyn_compatible(def_id) diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs index 593c8616c1d7c..2f9cf98848e94 100644 --- a/compiler/rustc_lint/src/non_ascii_idents.rs +++ b/compiler/rustc_lint/src/non_ascii_idents.rs @@ -209,22 +209,30 @@ impl EarlyLintPass for NonAsciiIdents { if codepoints.is_empty() { continue; } - cx.emit_span_lint(UNCOMMON_CODEPOINTS, sp, IdentifierUncommonCodepoints { - codepoints_len: codepoints.len(), - codepoints: codepoints.into_iter().map(|(c, _)| c).collect(), - identifier_type: id_ty_descr, - }); + cx.emit_span_lint( + UNCOMMON_CODEPOINTS, + sp, + IdentifierUncommonCodepoints { + codepoints_len: codepoints.len(), + codepoints: codepoints.into_iter().map(|(c, _)| c).collect(), + identifier_type: id_ty_descr, + }, + ); } let remaining = chars .extract_if(.., |(c, _)| !GeneralSecurityProfile::identifier_allowed(*c)) .collect::>(); if !remaining.is_empty() { - cx.emit_span_lint(UNCOMMON_CODEPOINTS, sp, IdentifierUncommonCodepoints { - codepoints_len: remaining.len(), - codepoints: remaining.into_iter().map(|(c, _)| c).collect(), - identifier_type: "Restricted", - }); + cx.emit_span_lint( + UNCOMMON_CODEPOINTS, + sp, + IdentifierUncommonCodepoints { + codepoints_len: remaining.len(), + codepoints: remaining.into_iter().map(|(c, _)| c).collect(), + identifier_type: "Restricted", + }, + ); } } } @@ -253,12 +261,16 @@ impl EarlyLintPass for NonAsciiIdents { .entry(skeleton_sym) .and_modify(|(existing_symbol, existing_span, existing_is_ascii)| { if !*existing_is_ascii || !is_ascii { - cx.emit_span_lint(CONFUSABLE_IDENTS, sp, ConfusableIdentifierPair { - existing_sym: *existing_symbol, - sym: symbol, - label: *existing_span, - main_label: sp, - }); + cx.emit_span_lint( + CONFUSABLE_IDENTS, + sp, + ConfusableIdentifierPair { + existing_sym: *existing_symbol, + sym: symbol, + label: *existing_span, + main_label: sp, + }, + ); } if *existing_is_ascii && !is_ascii { *existing_symbol = symbol; @@ -370,10 +382,11 @@ impl EarlyLintPass for NonAsciiIdents { let char_info = format!("'{}' (U+{:04X})", ch, ch as u32); includes += &char_info; } - cx.emit_span_lint(MIXED_SCRIPT_CONFUSABLES, sp, MixedScriptConfusables { - set: script_set.to_string(), - includes, - }); + cx.emit_span_lint( + MIXED_SCRIPT_CONFUSABLES, + sp, + MixedScriptConfusables { set: script_set.to_string(), includes }, + ); } } } diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs index 6f047b4da4954..ac9f8d92dacb7 100644 --- a/compiler/rustc_lint/src/non_fmt_panic.rs +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -2,7 +2,6 @@ use rustc_ast as ast; use rustc_errors::Applicability; use rustc_hir::{self as hir, LangItem}; use rustc_infer::infer::TyCtxtInferExt; -use rustc_middle::lint::in_external_macro; use rustc_middle::{bug, ty}; use rustc_parse_format::{ParseMode, Parser, Piece}; use rustc_session::lint::FutureIncompatibilityReason; @@ -100,7 +99,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc let (span, panic, symbol) = panic_call(cx, f); - if in_external_macro(cx.sess(), span) { + if span.in_external_macro(cx.sess().source_map()) { // Nothing that can be done about it in the current crate. return; } @@ -229,14 +228,15 @@ fn check_panic_str<'tcx>( let (span, _, _) = panic_call(cx, f); - if in_external_macro(cx.sess(), span) && in_external_macro(cx.sess(), arg.span) { + let sm = cx.sess().source_map(); + if span.in_external_macro(sm) && arg.span.in_external_macro(sm) { // Nothing that can be done about it in the current crate. return; } let fmt_span = arg.span.source_callsite(); - let (snippet, style) = match cx.sess().psess.source_map().span_to_snippet(fmt_span) { + let (snippet, style) = match sm.span_to_snippet(fmt_span) { Ok(snippet) => { // Count the number of `#`s between the `r` and `"`. let style = snippet.strip_prefix('r').and_then(|s| s.find('"')); @@ -256,10 +256,14 @@ fn check_panic_str<'tcx>( .map(|span| fmt_span.from_inner(InnerSpan::new(span.start, span.end))) .collect(), }; - cx.emit_span_lint(NON_FMT_PANICS, arg_spans, NonFmtPanicUnused { - count: n_arguments, - suggestion: is_arg_inside_call(arg.span, span).then_some(arg.span), - }); + cx.emit_span_lint( + NON_FMT_PANICS, + arg_spans, + NonFmtPanicUnused { + count: n_arguments, + suggestion: is_arg_inside_call(arg.span, span).then_some(arg.span), + }, + ); } else { let brace_spans: Option> = snippet.filter(|s| s.starts_with('"') || s.starts_with("r#")).map(|s| { @@ -283,7 +287,7 @@ fn check_panic_str<'tcx>( /// Given the span of `some_macro!(args);`, gives the span of `(` and `)`, /// and the type of (opening) delimiter used. fn find_delimiters(cx: &LateContext<'_>, span: Span) -> Option<(Span, Span, char)> { - let snippet = cx.sess().psess.source_map().span_to_snippet(span).ok()?; + let snippet = cx.sess().source_map().span_to_snippet(span).ok()?; let (open, open_ch) = snippet.char_indices().find(|&(_, c)| "([{".contains(c))?; let close = snippet.rfind(|c| ")]}".contains(c))?; Some(( diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index a1cc3a85109ad..0890890d12cbf 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -1,6 +1,6 @@ use rustc_errors::MultiSpan; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::intravisit::{self, Visitor, VisitorExt}; use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, TyKind}; use rustc_middle::ty::TyCtxt; use rustc_session::{declare_lint, impl_lint_pass}; @@ -126,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { // 1. We collect all the `hir::Path` from the `Self` type and `Trait` ref // of the `impl` definition let mut collector = PathCollector { paths: Vec::new() }; - collector.visit_ty(&impl_.self_ty); + collector.visit_ty_unambig(&impl_.self_ty); if let Some(of_trait) = &impl_.of_trait { collector.visit_trait_ref(of_trait); } @@ -222,17 +222,21 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { None }; - cx.emit_span_lint(NON_LOCAL_DEFINITIONS, ms, NonLocalDefinitionsDiag::Impl { - depth: self.body_depth, - body_kind_descr: cx.tcx.def_kind_descr(parent_def_kind, parent), - body_name: parent_opt_item_name - .map(|s| s.to_ident_string()) - .unwrap_or_else(|| "".to_string()), - cargo_update: cargo_update(), - const_anon, - doctest, - macro_to_change, - }) + cx.emit_span_lint( + NON_LOCAL_DEFINITIONS, + ms, + NonLocalDefinitionsDiag::Impl { + depth: self.body_depth, + body_kind_descr: cx.tcx.def_kind_descr(parent_def_kind, parent), + body_name: parent_opt_item_name + .map(|s| s.to_ident_string()) + .unwrap_or_else(|| "".to_string()), + cargo_update: cargo_update(), + const_anon, + doctest, + macro_to_change, + }, + ) } ItemKind::Macro(_macro, MacroKind::Bang) if cx.tcx.has_attr(item.owner_id.def_id, sym::macro_export) => @@ -343,5 +347,5 @@ fn path_span_without_args(path: &Path<'_>) -> Span { /// Return a "error message-able" ident for the last segment of the `Path` fn path_name_to_string(path: &Path<'_>) -> String { - path.segments.last().unwrap().ident.name.to_ident_string() + path.segments.last().unwrap().ident.to_string() } diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 70dce78b57245..5636f80d60020 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -1,7 +1,7 @@ use rustc_abi::ExternAbi; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::FnKind; -use rustc_hir::{AttrArgs, AttrItem, AttrKind, GenericParamKind, PatKind}; +use rustc_hir::{AttrArgs, AttrItem, AttrKind, GenericParamKind, PatExprKind, PatKind}; use rustc_middle::ty; use rustc_session::config::CrateType; use rustc_session::{declare_lint, declare_lint_pass}; @@ -150,11 +150,11 @@ impl NonCamelCaseTypes { } else { NonCamelCaseTypeSub::Label { span: ident.span } }; - cx.emit_span_lint(NON_CAMEL_CASE_TYPES, ident.span, NonCamelCaseType { - sort, - name, - sub, - }); + cx.emit_span_lint( + NON_CAMEL_CASE_TYPES, + ident.span, + NonCamelCaseType { sort, name, sub }, + ); } } } @@ -234,10 +234,10 @@ declare_lint! { declare_lint_pass!(NonSnakeCase => [NON_SNAKE_CASE]); impl NonSnakeCase { - fn to_snake_case(mut str: &str) -> String { + fn to_snake_case(mut name: &str) -> String { let mut words = vec![]; // Preserve leading underscores - str = str.trim_start_matches(|c: char| { + name = name.trim_start_matches(|c: char| { if c == '_' { words.push(String::new()); true @@ -245,7 +245,7 @@ impl NonSnakeCase { false } }); - for s in str.split('_') { + for s in name.split('_') { let mut last_upper = false; let mut buf = String::new(); if s.is_empty() { @@ -488,11 +488,11 @@ impl NonUpperCaseGlobals { } else { NonUpperCaseGlobalSub::Label { span: ident.span } }; - cx.emit_span_lint(NON_UPPER_CASE_GLOBALS, ident.span, NonUpperCaseGlobal { - sort, - name, - sub, - }); + cx.emit_span_lint( + NON_UPPER_CASE_GLOBALS, + ident.span, + NonUpperCaseGlobal { sort, name, sub }, + ); } } } @@ -527,7 +527,11 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals { fn check_pat(&mut self, cx: &LateContext<'_>, p: &hir::Pat<'_>) { // Lint for constants that look like binding identifiers (#7526) - if let PatKind::Path(hir::QPath::Resolved(None, path)) = p.kind { + if let PatKind::Expr(hir::PatExpr { + kind: PatExprKind::Path(hir::QPath::Resolved(None, path)), + .. + }) = p.kind + { if let Res::Def(DefKind::Const, _) = path.res { if let [segment] = path.segments { NonUpperCaseGlobals::check_upper_case( diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index fa519281be531..b7835e6c36ab1 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -128,13 +128,17 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { ty::Adt(def, _) => Some(cx.tcx.def_span(def.did()).shrink_to_lo()), _ => None, }; - cx.emit_span_lint(NOOP_METHOD_CALL, span, NoopMethodCallDiag { - method: call.ident.name, - orig_ty, - trait_, - label: span, - suggest_derive, - }); + cx.emit_span_lint( + NOOP_METHOD_CALL, + span, + NoopMethodCallDiag { + method: call.ident, + orig_ty, + trait_, + label: span, + suggest_derive, + }, + ); } else { match name { // If `type_of(x) == T` and `x.borrow()` is used to get `&T`, diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index 53a411e2b8712..7eaf83f5acf31 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -1,4 +1,4 @@ -use rustc_hir as hir; +use rustc_hir::{self as hir, AmbigArg}; use rustc_infer::infer::TyCtxtInferExt; use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::fold::BottomUpFolder; @@ -67,7 +67,7 @@ declare_lint! { declare_lint_pass!(OpaqueHiddenInferredBound => [OPAQUE_HIDDEN_INFERRED_BOUND]); impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { - fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'tcx>) { + fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { let hir::TyKind::OpaqueDef(opaque) = &ty.kind else { return; }; @@ -113,10 +113,13 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { // return type is well-formed in traits even when `Self` isn't sized. if let ty::Param(param_ty) = *proj_term.kind() && param_ty.name == kw::SelfUpper - && matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn { - in_trait_or_impl: Some(hir::RpitContext::Trait), - .. - }) + && matches!( + opaque.origin, + hir::OpaqueTyOrigin::AsyncFn { + in_trait_or_impl: Some(hir::RpitContext::Trait), + .. + } + ) { return; } diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/pass_by_value.rs index 3f264859d4835..d85618f664d6f 100644 --- a/compiler/rustc_lint/src/pass_by_value.rs +++ b/compiler/rustc_lint/src/pass_by_value.rs @@ -1,6 +1,5 @@ -use rustc_hir as hir; use rustc_hir::def::Res; -use rustc_hir::{GenericArg, PathSegment, QPath, TyKind}; +use rustc_hir::{self as hir, AmbigArg, GenericArg, PathSegment, QPath, TyKind}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -22,7 +21,7 @@ declare_tool_lint! { declare_lint_pass!(PassByValue => [PASS_BY_VALUE]); impl<'tcx> LateLintPass<'tcx> for PassByValue { - fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) { + fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { match &ty.kind { TyKind::Ref(_, hir::MutTy { ty: inner_ty, mutbl: hir::Mutability::Not }) => { if let Some(impl_did) = cx.tcx.impl_of_method(ty.hir_id.owner.to_def_id()) { @@ -31,10 +30,11 @@ impl<'tcx> LateLintPass<'tcx> for PassByValue { } } if let Some(t) = path_for_pass_by_value(cx, inner_ty) { - cx.emit_span_lint(PASS_BY_VALUE, ty.span, PassByValueDiag { - ty: t, - suggestion: ty.span, - }); + cx.emit_span_lint( + PASS_BY_VALUE, + ty.span, + PassByValueDiag { ty: t, suggestion: ty.span }, + ); } } _ => {} @@ -46,7 +46,7 @@ fn path_for_pass_by_value(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Option { - let name = cx.tcx.item_name(def_id).to_ident_string(); + let name = cx.tcx.item_ident(def_id); let path_segment = path.segments.last().unwrap(); return Some(format!("{}{}", name, gen_args(cx, path_segment))); } diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index 3a323298bee83..409a23d1da039 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -23,9 +23,10 @@ macro_rules! late_lint_methods { fn check_stmt(a: &'tcx rustc_hir::Stmt<'tcx>); fn check_arm(a: &'tcx rustc_hir::Arm<'tcx>); fn check_pat(a: &'tcx rustc_hir::Pat<'tcx>); + fn check_lit(hir_id: rustc_hir::HirId, a: &'tcx rustc_hir::Lit, negated: bool); fn check_expr(a: &'tcx rustc_hir::Expr<'tcx>); fn check_expr_post(a: &'tcx rustc_hir::Expr<'tcx>); - fn check_ty(a: &'tcx rustc_hir::Ty<'tcx>); + fn check_ty(a: &'tcx rustc_hir::Ty<'tcx, rustc_hir::AmbigArg>); fn check_generic_param(a: &'tcx rustc_hir::GenericParam<'tcx>); fn check_generics(a: &'tcx rustc_hir::Generics<'tcx>); fn check_poly_trait_ref(a: &'tcx rustc_hir::PolyTraitRef<'tcx>); @@ -161,7 +162,9 @@ macro_rules! early_lint_methods { c: rustc_span::Span, d_: rustc_ast::NodeId); fn check_trait_item(a: &rustc_ast::AssocItem); + fn check_trait_item_post(a: &rustc_ast::AssocItem); fn check_impl_item(a: &rustc_ast::AssocItem); + fn check_impl_item_post(a: &rustc_ast::AssocItem); fn check_variant(a: &rustc_ast::Variant); fn check_attribute(a: &rustc_ast::Attribute); fn check_attributes(a: &[rustc_ast::Attribute]); diff --git a/compiler/rustc_lint/src/redundant_semicolon.rs b/compiler/rustc_lint/src/redundant_semicolon.rs index 036bfd06856f6..b43e4938b736c 100644 --- a/compiler/rustc_lint/src/redundant_semicolon.rs +++ b/compiler/rustc_lint/src/redundant_semicolon.rs @@ -50,9 +50,10 @@ fn maybe_lint_redundant_semis(cx: &EarlyContext<'_>, seq: &mut Option<(Span, boo return; } - cx.emit_span_lint(REDUNDANT_SEMICOLONS, span, RedundantSemicolonsDiag { - multiple, - suggestion: span, - }); + cx.emit_span_lint( + REDUNDANT_SEMICOLONS, + span, + RedundantSemicolonsDiag { multiple, suggestion: span }, + ); } } diff --git a/compiler/rustc_lint/src/shadowed_into_iter.rs b/compiler/rustc_lint/src/shadowed_into_iter.rs index f5ab44d74692a..7cc35e20fcb64 100644 --- a/compiler/rustc_lint/src/shadowed_into_iter.rs +++ b/compiler/rustc_lint/src/shadowed_into_iter.rs @@ -147,11 +147,10 @@ impl<'tcx> LateLintPass<'tcx> for ShadowedIntoIter { None }; - cx.emit_span_lint(lint, call.ident.span, ShadowedIntoIterDiag { - target, - edition, - suggestion: call.ident.span, - sub, - }); + cx.emit_span_lint( + lint, + call.ident.span, + ShadowedIntoIterDiag { target, edition, suggestion: call.ident.span, sub }, + ); } } diff --git a/compiler/rustc_lint/src/static_mut_refs.rs b/compiler/rustc_lint/src/static_mut_refs.rs index fed5c29284b3f..50021157ddab7 100644 --- a/compiler/rustc_lint/src/static_mut_refs.rs +++ b/compiler/rustc_lint/src/static_mut_refs.rs @@ -157,11 +157,9 @@ fn emit_static_mut_refs( } }; - cx.emit_span_lint(STATIC_MUT_REFS, span, RefOfMutStatic { + cx.emit_span_lint( + STATIC_MUT_REFS, span, - sugg, - shared_label, - shared_note, - mut_note, - }); + RefOfMutStatic { span, sugg, shared_label, shared_note, mut_note }, + ); } diff --git a/compiler/rustc_lint/src/tests.rs b/compiler/rustc_lint/src/tests.rs index 186dec5904b49..f49301b0215db 100644 --- a/compiler/rustc_lint/src/tests.rs +++ b/compiler/rustc_lint/src/tests.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] +#![allow(rustc::symbol_intern_string_literal)] use rustc_span::{Symbol, create_default_session_globals_then}; diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs index a9797c3b32a0e..99222742b65de 100644 --- a/compiler/rustc_lint/src/traits.rs +++ b/compiler/rustc_lint/src/traits.rs @@ -1,4 +1,4 @@ -use rustc_hir::{self as hir, LangItem}; +use rustc_hir::{self as hir, AmbigArg, LangItem}; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::sym; @@ -101,17 +101,19 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { continue; } let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { return }; - cx.emit_span_lint(DROP_BOUNDS, span, DropTraitConstraintsDiag { - predicate, - tcx: cx.tcx, - def_id, - }); + cx.emit_span_lint( + DROP_BOUNDS, + span, + DropTraitConstraintsDiag { predicate, tcx: cx.tcx, def_id }, + ); } } } - fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) { - let hir::TyKind::TraitObject(bounds, _lifetime, _syntax) = &ty.kind else { return }; + fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { + let hir::TyKind::TraitObject(bounds, _lifetime_and_syntax_pointer) = &ty.kind else { + return; + }; for bound in &bounds[..] { let def_id = bound.trait_ref.trait_def_id(); if def_id.is_some_and(|def_id| cx.tcx.is_lang_item(def_id, LangItem::Drop)) { diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index ef9aa11ef7b8b..68b1f435a4cf7 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1,14 +1,16 @@ use std::iter; use std::ops::ControlFlow; -use rustc_abi::{BackendRepr, ExternAbi, TagEncoding, Variants, WrappingRange}; +use rustc_abi::{BackendRepr, ExternAbi, TagEncoding, VariantIdx, Variants, WrappingRange}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::DiagMessage; -use rustc_hir::{Expr, ExprKind, LangItem}; +use rustc_hir::intravisit::VisitorExt; +use rustc_hir::{AmbigArg, Expr, ExprKind, HirId, LangItem}; use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton}; use rustc_middle::ty::{ - self, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, + self, Adt, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, }; use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; use rustc_span::def_id::LocalDefId; @@ -23,7 +25,7 @@ use crate::lints::{ AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons, InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons, - UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons, + UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons, UsesPowerAlignment, VariantSizeDifferencesDiag, }; use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; @@ -534,6 +536,20 @@ fn lint_fn_pointer<'tcx>( } impl<'tcx> LateLintPass<'tcx> for TypeLimits { + fn check_lit( + &mut self, + cx: &LateContext<'tcx>, + hir_id: HirId, + lit: &'tcx hir::Lit, + negated: bool, + ) { + if negated { + self.negated_expr_id = Some(hir_id); + self.negated_expr_span = Some(lit.span); + } + lint_literal(cx, self, hir_id, lit.span, lit, negated); + } + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) { match e.kind { hir::ExprKind::Unary(hir::UnOp::Neg, expr) => { @@ -555,7 +571,6 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { } } } - hir::ExprKind::Lit(lit) => lint_literal(cx, self, e, lit), hir::ExprKind::Call(path, [l, r]) if let ExprKind::Path(ref qpath) = path.kind && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() @@ -588,13 +603,16 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { } fn rev_binop(binop: hir::BinOp) -> hir::BinOp { - source_map::respan(binop.span, match binop.node { - hir::BinOpKind::Lt => hir::BinOpKind::Gt, - hir::BinOpKind::Le => hir::BinOpKind::Ge, - hir::BinOpKind::Gt => hir::BinOpKind::Lt, - hir::BinOpKind::Ge => hir::BinOpKind::Le, - _ => return binop, - }) + source_map::respan( + binop.span, + match binop.node { + hir::BinOpKind::Lt => hir::BinOpKind::Gt, + hir::BinOpKind::Le => hir::BinOpKind::Ge, + hir::BinOpKind::Gt => hir::BinOpKind::Lt, + hir::BinOpKind::Ge => hir::BinOpKind::Le, + _ => return binop, + }, + ) } fn check_limits( @@ -727,7 +745,60 @@ declare_lint! { "proper use of libc types in foreign item definitions" } -declare_lint_pass!(ImproperCTypesDefinitions => [IMPROPER_CTYPES_DEFINITIONS]); +declare_lint! { + /// The `uses_power_alignment` lint detects specific `repr(C)` + /// aggregates on AIX. + /// In its platform C ABI, AIX uses the "power" (as in PowerPC) alignment + /// rule (detailed in https://www.ibm.com/docs/en/xl-c-and-cpp-aix/16.1?topic=data-using-alignment-modes#alignment), + /// which can also be set for XLC by `#pragma align(power)` or + /// `-qalign=power`. Aggregates with a floating-point type as the + /// recursively first field (as in "at offset 0") modify the layout of + /// *subsequent* fields of the associated structs to use an alignment value + /// where the floating-point type is aligned on a 4-byte boundary. + /// + /// The power alignment rule for structs needed for C compatibility is + /// unimplementable within `repr(C)` in the compiler without building in + /// handling of references to packed fields and infectious nested layouts, + /// so a warning is produced in these situations. + /// + /// ### Example + /// + /// ```rust,ignore (fails on non-powerpc64-ibm-aix) + /// #[repr(C)] + /// pub struct Floats { + /// a: f64, + /// b: u8, + /// c: f64, + /// } + /// ``` + /// + /// This will produce: + /// + /// ```text + /// warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + /// --> :5:3 + /// | + /// 5 | c: f64, + /// | ^^^^^^ + /// | + /// = note: `#[warn(uses_power_alignment)]` on by default + /// ``` + /// + /// ### Explanation + /// + /// The power alignment rule specifies that the above struct has the + /// following alignment: + /// - offset_of!(Floats, a) == 0 + /// - offset_of!(Floats, b) == 8 + /// - offset_of!(Floats, c) == 12 + /// However, rust currently aligns `c` at offset_of!(Floats, c) == 16. + /// Thus, a warning should be produced for the above struct in this case. + USES_POWER_ALIGNMENT, + Warn, + "Structs do not follow the power alignment rule under repr(C)" +} + +declare_lint_pass!(ImproperCTypesDefinitions => [IMPROPER_CTYPES_DEFINITIONS, USES_POWER_ALIGNMENT]); #[derive(Clone, Copy)] pub(crate) enum CItemKind { @@ -806,6 +877,37 @@ fn ty_is_known_nonnull<'tcx>( .filter_map(|variant| transparent_newtype_field(tcx, variant)) .any(|field| ty_is_known_nonnull(tcx, typing_env, field.ty(tcx, args), mode)) } + ty::Pat(base, pat) => { + ty_is_known_nonnull(tcx, typing_env, *base, mode) + || Option::unwrap_or_default( + try { + match **pat { + ty::PatternKind::Range { start, end, include_end } => { + match (start, end) { + (Some(start), None) => { + start.try_to_value()?.try_to_bits(tcx, typing_env)? > 0 + } + (Some(start), Some(end)) => { + let start = + start.try_to_value()?.try_to_bits(tcx, typing_env)?; + let end = + end.try_to_value()?.try_to_bits(tcx, typing_env)?; + + if include_end { + // This also works for negative numbers, as we just need + // to ensure we aren't wrapping over zero. + start > 0 && end >= start + } else { + start > 0 && end > start + } + } + _ => false, + } + } + } + }, + ) + } _ => false, } } @@ -836,9 +938,8 @@ fn get_nullable_type<'tcx>( }; return get_nullable_type(tcx, typing_env, inner_field_ty); } - ty::Int(ty) => Ty::new_int(tcx, ty), - ty::Uint(ty) => Ty::new_uint(tcx, ty), - ty::RawPtr(ty, mutbl) => Ty::new_ptr(tcx, ty, mutbl), + ty::Pat(base, ..) => return get_nullable_type(tcx, typing_env, base), + ty::Int(_) | ty::Uint(_) | ty::RawPtr(..) => ty, // As these types are always non-null, the nullable equivalent of // `Option` of these types are their raw pointer counterparts. ty::Ref(_region, ty, mutbl) => Ty::new_ptr(tcx, ty, mutbl), @@ -894,63 +995,69 @@ pub(crate) fn repr_nullable_ptr<'tcx>( ckind: CItemKind, ) -> Option> { debug!("is_repr_nullable_ptr(tcx, ty = {:?})", ty); - if let ty::Adt(ty_def, args) = ty.kind() { - let field_ty = match &ty_def.variants().raw[..] { - [var_one, var_two] => match (&var_one.fields.raw[..], &var_two.fields.raw[..]) { - ([], [field]) | ([field], []) => field.ty(tcx, args), - ([field1], [field2]) => { - let ty1 = field1.ty(tcx, args); - let ty2 = field2.ty(tcx, args); - - if is_niche_optimization_candidate(tcx, typing_env, ty1) { - ty2 - } else if is_niche_optimization_candidate(tcx, typing_env, ty2) { - ty1 - } else { - return None; + match ty.kind() { + ty::Adt(ty_def, args) => { + let field_ty = match &ty_def.variants().raw[..] { + [var_one, var_two] => match (&var_one.fields.raw[..], &var_two.fields.raw[..]) { + ([], [field]) | ([field], []) => field.ty(tcx, args), + ([field1], [field2]) => { + let ty1 = field1.ty(tcx, args); + let ty2 = field2.ty(tcx, args); + + if is_niche_optimization_candidate(tcx, typing_env, ty1) { + ty2 + } else if is_niche_optimization_candidate(tcx, typing_env, ty2) { + ty1 + } else { + return None; + } } - } + _ => return None, + }, _ => return None, - }, - _ => return None, - }; + }; - if !ty_is_known_nonnull(tcx, typing_env, field_ty, ckind) { - return None; - } + if !ty_is_known_nonnull(tcx, typing_env, field_ty, ckind) { + return None; + } - // At this point, the field's type is known to be nonnull and the parent enum is Option-like. - // If the computed size for the field and the enum are different, the nonnull optimization isn't - // being applied (and we've got a problem somewhere). - let compute_size_skeleton = |t| SizeSkeleton::compute(t, tcx, typing_env).ok(); - if !compute_size_skeleton(ty)?.same_size(compute_size_skeleton(field_ty)?) { - bug!("improper_ctypes: Option nonnull optimization not applied?"); - } + // At this point, the field's type is known to be nonnull and the parent enum is Option-like. + // If the computed size for the field and the enum are different, the nonnull optimization isn't + // being applied (and we've got a problem somewhere). + let compute_size_skeleton = |t| SizeSkeleton::compute(t, tcx, typing_env).ok(); + if !compute_size_skeleton(ty)?.same_size(compute_size_skeleton(field_ty)?) { + bug!("improper_ctypes: Option nonnull optimization not applied?"); + } - // Return the nullable type this Option-like enum can be safely represented with. - let field_ty_layout = tcx.layout_of(typing_env.as_query_input(field_ty)); - if field_ty_layout.is_err() && !field_ty.has_non_region_param() { - bug!("should be able to compute the layout of non-polymorphic type"); - } + // Return the nullable type this Option-like enum can be safely represented with. + let field_ty_layout = tcx.layout_of(typing_env.as_query_input(field_ty)); + if field_ty_layout.is_err() && !field_ty.has_non_region_param() { + bug!("should be able to compute the layout of non-polymorphic type"); + } - let field_ty_abi = &field_ty_layout.ok()?.backend_repr; - if let BackendRepr::Scalar(field_ty_scalar) = field_ty_abi { - match field_ty_scalar.valid_range(&tcx) { - WrappingRange { start: 0, end } - if end == field_ty_scalar.size(&tcx).unsigned_int_max() - 1 => - { - return Some(get_nullable_type(tcx, typing_env, field_ty).unwrap()); - } - WrappingRange { start: 1, .. } => { - return Some(get_nullable_type(tcx, typing_env, field_ty).unwrap()); - } - WrappingRange { start, end } => { - unreachable!("Unhandled start and end range: ({}, {})", start, end) - } - }; + let field_ty_abi = &field_ty_layout.ok()?.backend_repr; + if let BackendRepr::Scalar(field_ty_scalar) = field_ty_abi { + match field_ty_scalar.valid_range(&tcx) { + WrappingRange { start: 0, end } + if end == field_ty_scalar.size(&tcx).unsigned_int_max() - 1 => + { + return Some(get_nullable_type(tcx, typing_env, field_ty).unwrap()); + } + WrappingRange { start: 1, .. } => { + return Some(get_nullable_type(tcx, typing_env, field_ty).unwrap()); + } + WrappingRange { start, end } => { + unreachable!("Unhandled start and end range: ({}, {})", start, end) + } + }; + } + None } + ty::Pat(base, pat) => match **pat { + ty::PatternKind::Range { .. } => get_nullable_type(tcx, typing_env, *base), + }, + _ => None, } - None } impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { @@ -1185,11 +1292,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { help: Some(fluent::lint_improper_ctypes_char_help), }, - ty::Pat(..) => FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_pat_reason, - help: Some(fluent::lint_improper_ctypes_pat_help), - }, + // It's just extra invariants on the type that you need to uphold, + // but only the base type is relevant for being representable in FFI. + ty::Pat(base, ..) => self.check_type_for_ffi(acc, base), ty::Int(ty::IntTy::I128) | ty::Uint(ty::UintTy::U128) => { FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_128bit, help: None } @@ -1322,14 +1427,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } else { None }; - self.cx.emit_span_lint(lint, sp, ImproperCTypes { - ty, - desc, - label: sp, - help, - note, - span_note, - }); + self.cx.emit_span_lint( + lint, + sp, + ImproperCTypes { ty, desc, label: sp, help, note, span_note }, + ); } fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { @@ -1472,7 +1574,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } impl<'a, 'b, 'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'a, 'b, 'tcx> { - fn visit_ty(&mut self, ty: &'_ hir::Ty<'_>) { + fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) { debug!(?ty); if let hir::TyKind::BareFn(hir::BareFnTy { abi, .. }) = ty.kind && !self.visitor.is_internal_abi(*abi) @@ -1500,7 +1602,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { let mut visitor = FnPtrFinder { visitor: self, spans: Vec::new(), tys: Vec::new() }; ty.visit_with(&mut visitor); - hir::intravisit::Visitor::visit_ty(&mut visitor, hir_ty); + visitor.visit_ty_unambig(hir_ty); iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)).collect() } @@ -1539,6 +1641,71 @@ impl ImproperCTypesDefinitions { vis.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, true, false); } } + + fn check_arg_for_power_alignment<'tcx>( + &mut self, + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + ) -> bool { + // Structs (under repr(C)) follow the power alignment rule if: + // - the first field of the struct is a floating-point type that + // is greater than 4-bytes, or + // - the first field of the struct is an aggregate whose + // recursively first field is a floating-point type greater than + // 4 bytes. + if cx.tcx.sess.target.os != "aix" { + return false; + } + if ty.is_floating_point() && ty.primitive_size(cx.tcx).bytes() > 4 { + return true; + } else if let Adt(adt_def, _) = ty.kind() + && adt_def.is_struct() + { + let struct_variant = adt_def.variant(VariantIdx::ZERO); + // Within a nested struct, all fields are examined to correctly + // report if any fields after the nested struct within the + // original struct are misaligned. + for struct_field in &struct_variant.fields { + let field_ty = cx.tcx.type_of(struct_field.did).instantiate_identity(); + if self.check_arg_for_power_alignment(cx, field_ty) { + return true; + } + } + } + return false; + } + + fn check_struct_for_power_alignment<'tcx>( + &mut self, + cx: &LateContext<'tcx>, + item: &'tcx hir::Item<'tcx>, + ) { + let adt_def = cx.tcx.adt_def(item.owner_id.to_def_id()); + if adt_def.repr().c() + && !adt_def.repr().packed() + && cx.tcx.sess.target.os == "aix" + && !adt_def.all_fields().next().is_none() + { + let struct_variant_data = item.expect_struct().0; + for (index, ..) in struct_variant_data.fields().iter().enumerate() { + // Struct fields (after the first field) are checked for the + // power alignment rule, as fields after the first are likely + // to be the fields that are misaligned. + if index != 0 { + let first_field_def = struct_variant_data.fields()[index]; + let def_id = first_field_def.def_id; + let ty = cx.tcx.type_of(def_id).instantiate_identity(); + if self.check_arg_for_power_alignment(cx, ty) { + cx.emit_span_lint( + USES_POWER_ALIGNMENT, + first_field_def.span, + UsesPowerAlignment, + ); + } + } + } + } + } } /// `ImproperCTypesDefinitions` checks items outside of foreign items (e.g. stuff that isn't in @@ -1561,9 +1728,14 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { ); } // See `check_fn`.. - hir::ItemKind::Fn(..) => {} + hir::ItemKind::Fn { .. } => {} + // Structs are checked based on if they follow the power alignment + // rule (under repr(C)). + hir::ItemKind::Struct(..) => { + self.check_struct_for_power_alignment(cx, item); + } // See `check_field_def`.. - hir::ItemKind::Union(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) => {} + hir::ItemKind::Union(..) | hir::ItemKind::Enum(..) => {} // Doesn't define something that can contain a external type to be checked. hir::ItemKind::Impl(..) | hir::ItemKind::TraitAlias(..) @@ -1796,11 +1968,11 @@ impl InvalidAtomicOrdering { } fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) { - let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[ - sym::fetch_update, - sym::compare_exchange, - sym::compare_exchange_weak, - ]) else { + let Some((method, args)) = Self::inherent_atomic_method_call( + cx, + expr, + &[sym::fetch_update, sym::compare_exchange, sym::compare_exchange_weak], + ) else { return; }; diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index 83942918e3b58..3e4532a6dbeef 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -1,8 +1,10 @@ use hir::{ExprKind, Node, is_range_literal}; use rustc_abi::{Integer, Size}; +use rustc_hir::HirId; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::{bug, ty}; +use rustc_span::Span; use {rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir}; use crate::LateContext; @@ -21,21 +23,22 @@ fn lint_overflowing_range_endpoint<'tcx>( lit: &hir::Lit, lit_val: u128, max: u128, - expr: &'tcx hir::Expr<'tcx>, + hir_id: HirId, + lit_span: Span, ty: &str, ) -> bool { // Look past casts to support cases like `0..256 as u8` - let (expr, lit_span) = if let Node::Expr(par_expr) = cx.tcx.parent_hir_node(expr.hir_id) + let (hir_id, span) = if let Node::Expr(par_expr) = cx.tcx.parent_hir_node(hir_id) && let ExprKind::Cast(_, _) = par_expr.kind { - (par_expr, expr.span) + (par_expr.hir_id, par_expr.span) } else { - (expr, expr.span) + (hir_id, lit_span) }; // We only want to handle exclusive (`..`) ranges, // which are represented as `ExprKind::Struct`. - let Node::ExprField(field) = cx.tcx.parent_hir_node(expr.hir_id) else { return false }; + let Node::ExprField(field) = cx.tcx.parent_hir_node(hir_id) else { return false }; let Node::Expr(struct_expr) = cx.tcx.parent_hir_node(field.hir_id) else { return false }; if !is_range_literal(struct_expr) { return false; @@ -45,7 +48,7 @@ fn lint_overflowing_range_endpoint<'tcx>( // We can suggest using an inclusive range // (`..=`) instead only if it is the `end` that is // overflowing and only by 1. - if !(end.expr.hir_id == expr.hir_id && lit_val - 1 == max) { + if !(end.expr.hir_id == hir_id && lit_val - 1 == max) { return false; }; @@ -57,7 +60,7 @@ fn lint_overflowing_range_endpoint<'tcx>( _ => bug!(), }; - let sub_sugg = if expr.span.lo() == lit_span.lo() { + let sub_sugg = if span.lo() == lit_span.lo() { let Ok(start) = cx.sess().source_map().span_to_snippet(start.span) else { return false }; UseInclusiveRange::WithoutParen { sugg: struct_expr.span.shrink_to_lo().to(lit_span.shrink_to_hi()), @@ -67,17 +70,18 @@ fn lint_overflowing_range_endpoint<'tcx>( } } else { UseInclusiveRange::WithParen { - eq_sugg: expr.span.shrink_to_lo(), + eq_sugg: span.shrink_to_lo(), lit_sugg: lit_span, literal: lit_val - 1, suffix, } }; - cx.emit_span_lint(OVERFLOWING_LITERALS, struct_expr.span, RangeEndpointOutOfRange { - ty, - sub: sub_sugg, - }); + cx.emit_span_lint( + OVERFLOWING_LITERALS, + struct_expr.span, + RangeEndpointOutOfRange { ty, sub: sub_sugg }, + ); // We've just emitted a lint, special cased for `(...)..MAX+1` ranges, // return `true` so the callers don't also emit a lint @@ -125,7 +129,8 @@ fn get_bin_hex_repr(cx: &LateContext<'_>, lit: &hir::Lit) -> Option { fn report_bin_hex_error( cx: &LateContext<'_>, - expr: &hir::Expr<'_>, + hir_id: HirId, + span: Span, ty: attr::IntType, size: Size, repr_str: String, @@ -144,11 +149,11 @@ fn report_bin_hex_error( }; let sign = if negative { OverflowingBinHexSign::Negative } else { OverflowingBinHexSign::Positive }; - let sub = get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative).map( + let sub = get_type_suggestion(cx.typeck_results().node_type(hir_id), val, negative).map( |suggestion_ty| { if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') { let (sans_suffix, _) = repr_str.split_at(pos); - OverflowingBinHexSub::Suggestion { span: expr.span, suggestion_ty, sans_suffix } + OverflowingBinHexSub::Suggestion { span, suggestion_ty, sans_suffix } } else { OverflowingBinHexSub::Help { suggestion_ty } } @@ -156,7 +161,7 @@ fn report_bin_hex_error( ); let sign_bit_sub = (!negative) .then(|| { - let ty::Int(int_ty) = cx.typeck_results().node_type(expr.hir_id).kind() else { + let ty::Int(int_ty) = cx.typeck_results().node_type(hir_id).kind() else { return None; }; @@ -177,7 +182,7 @@ fn report_bin_hex_error( }; Some(OverflowingBinHexSignBitSub { - span: expr.span, + span, lit_no_suffix, negative_val: actually.clone(), int_ty: int_ty.name_str(), @@ -186,15 +191,19 @@ fn report_bin_hex_error( }) .flatten(); - cx.emit_span_lint(OVERFLOWING_LITERALS, expr.span, OverflowingBinHex { - ty: t, - lit: repr_str.clone(), - dec: val, - actually, - sign, - sub, - sign_bit_sub, - }) + cx.emit_span_lint( + OVERFLOWING_LITERALS, + span, + OverflowingBinHex { + ty: t, + lit: repr_str.clone(), + dec: val, + actually, + sign, + sub, + sign_bit_sub, + }, + ) } // Find the "next" fitting integer and return a suggestion string @@ -204,24 +213,40 @@ fn get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static match t.kind() { ty::Uint(ty::UintTy::Usize) | ty::Int(ty::IntTy::Isize) => None, ty::Uint(_) => Some(Integer::fit_unsigned(val).uint_ty_str()), - ty::Int(_) if negative => Some(Integer::fit_signed(-(val as i128)).int_ty_str()), - ty::Int(int) => { - let signed = Integer::fit_signed(val as i128); - let unsigned = Integer::fit_unsigned(val); - Some(if Some(unsigned.size().bits()) == int.bit_width() { - unsigned.uint_ty_str() + ty::Int(_) => { + let signed = literal_to_i128(val, negative).map(Integer::fit_signed); + if negative { + signed.map(Integer::int_ty_str) } else { - signed.int_ty_str() - }) + let unsigned = Integer::fit_unsigned(val); + Some(if let Some(signed) = signed { + if unsigned.size() < signed.size() { + unsigned.uint_ty_str() + } else { + signed.int_ty_str() + } + } else { + unsigned.uint_ty_str() + }) + } } _ => None, } } +fn literal_to_i128(val: u128, negative: bool) -> Option { + if negative { + (val <= i128::MAX as u128 + 1).then(|| val.wrapping_neg() as i128) + } else { + val.try_into().ok() + } +} + fn lint_int_literal<'tcx>( cx: &LateContext<'tcx>, type_limits: &TypeLimits, - e: &'tcx hir::Expr<'tcx>, + hir_id: HirId, + span: Span, lit: &hir::Lit, t: ty::IntTy, v: u128, @@ -229,7 +254,7 @@ fn lint_int_literal<'tcx>( let int_type = t.normalize(cx.sess().target.pointer_width); let (min, max) = int_ty_range(int_type); let max = max as u128; - let negative = type_limits.negated_expr_id == Some(e.hir_id); + let negative = type_limits.negated_expr_id == Some(hir_id); // Detect literal value out of range [min, max] inclusive // avoiding use of -min to prevent overflow/panic @@ -237,7 +262,8 @@ fn lint_int_literal<'tcx>( if let Some(repr_str) = get_bin_hex_repr(cx, lit) { report_bin_hex_error( cx, - e, + hir_id, + span, attr::IntType::SignedInt(ty::ast_int_ty(t)), Integer::from_int_ty(cx, t).size(), repr_str, @@ -247,33 +273,32 @@ fn lint_int_literal<'tcx>( return; } - if lint_overflowing_range_endpoint(cx, lit, v, max, e, t.name_str()) { + if lint_overflowing_range_endpoint(cx, lit, v, max, hir_id, span, t.name_str()) { // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`. return; } - let span = if negative { type_limits.negated_expr_span.unwrap() } else { e.span }; + let span = if negative { type_limits.negated_expr_span.unwrap() } else { span }; let lit = cx .sess() .source_map() .span_to_snippet(span) .unwrap_or_else(|_| if negative { format!("-{v}") } else { v.to_string() }); - let help = get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative) + let help = get_type_suggestion(cx.typeck_results().node_type(hir_id), v, negative) .map(|suggestion_ty| OverflowingIntHelp { suggestion_ty }); - cx.emit_span_lint(OVERFLOWING_LITERALS, span, OverflowingInt { - ty: t.name_str(), - lit, - min, - max, - help, - }); + cx.emit_span_lint( + OVERFLOWING_LITERALS, + span, + OverflowingInt { ty: t.name_str(), lit, min, max, help }, + ); } } fn lint_uint_literal<'tcx>( cx: &LateContext<'tcx>, - e: &'tcx hir::Expr<'tcx>, + hir_id: HirId, + span: Span, lit: &hir::Lit, t: ty::UintTy, ) { @@ -287,28 +312,30 @@ fn lint_uint_literal<'tcx>( }; if lit_val < min || lit_val > max { - if let Node::Expr(par_e) = cx.tcx.parent_hir_node(e.hir_id) { + if let Node::Expr(par_e) = cx.tcx.parent_hir_node(hir_id) { match par_e.kind { hir::ExprKind::Cast(..) => { if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() { - cx.emit_span_lint(OVERFLOWING_LITERALS, par_e.span, OnlyCastu8ToChar { - span: par_e.span, - literal: lit_val, - }); + cx.emit_span_lint( + OVERFLOWING_LITERALS, + par_e.span, + OnlyCastu8ToChar { span: par_e.span, literal: lit_val }, + ); return; } } _ => {} } } - if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, t.name_str()) { + if lint_overflowing_range_endpoint(cx, lit, lit_val, max, hir_id, span, t.name_str()) { // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`. return; } if let Some(repr_str) = get_bin_hex_repr(cx, lit) { report_bin_hex_error( cx, - e, + hir_id, + span, attr::IntType::UnsignedInt(ty::ast_uint_ty(t)), Integer::from_uint_ty(cx, t).size(), repr_str, @@ -317,35 +344,44 @@ fn lint_uint_literal<'tcx>( ); return; } - cx.emit_span_lint(OVERFLOWING_LITERALS, e.span, OverflowingUInt { - ty: t.name_str(), - lit: cx - .sess() - .source_map() - .span_to_snippet(lit.span) - .unwrap_or_else(|_| lit_val.to_string()), - min, - max, - }); + cx.emit_span_lint( + OVERFLOWING_LITERALS, + span, + OverflowingUInt { + ty: t.name_str(), + lit: cx + .sess() + .source_map() + .span_to_snippet(lit.span) + .unwrap_or_else(|_| lit_val.to_string()), + min, + max, + }, + ); } } pub(crate) fn lint_literal<'tcx>( cx: &LateContext<'tcx>, type_limits: &TypeLimits, - e: &'tcx hir::Expr<'tcx>, + hir_id: HirId, + span: Span, lit: &hir::Lit, + negated: bool, ) { - match *cx.typeck_results().node_type(e.hir_id).kind() { + match *cx.typeck_results().node_type(hir_id).kind() { ty::Int(t) => { match lit.node { ast::LitKind::Int(v, ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed) => { - lint_int_literal(cx, type_limits, e, lit, t, v.get()) + lint_int_literal(cx, type_limits, hir_id, span, lit, t, v.get()) } _ => bug!(), }; } - ty::Uint(t) => lint_uint_literal(cx, e, lit, t), + ty::Uint(t) => { + assert!(!negated); + lint_uint_literal(cx, hir_id, span, lit, t) + } ty::Float(t) => { let (is_infinite, sym) = match lit.node { ast::LitKind::Float(v, _) => match t { @@ -359,14 +395,18 @@ pub(crate) fn lint_literal<'tcx>( _ => bug!(), }; if is_infinite == Ok(true) { - cx.emit_span_lint(OVERFLOWING_LITERALS, e.span, OverflowingLiteral { - ty: t.name_str(), - lit: cx - .sess() - .source_map() - .span_to_snippet(lit.span) - .unwrap_or_else(|_| sym.to_string()), - }); + cx.emit_span_lint( + OVERFLOWING_LITERALS, + span, + OverflowingLiteral { + ty: t.name_str(), + lit: cx + .sess() + .source_map() + .span_to_snippet(lit.span) + .unwrap_or_else(|_| sym.to_string()), + }, + ); } } _ => {} diff --git a/compiler/rustc_lint/src/unit_bindings.rs b/compiler/rustc_lint/src/unit_bindings.rs index 3c2c5f8fae095..ed015908ae54a 100644 --- a/compiler/rustc_lint/src/unit_bindings.rs +++ b/compiler/rustc_lint/src/unit_bindings.rs @@ -63,9 +63,11 @@ impl<'tcx> LateLintPass<'tcx> for UnitBindings { && !matches!(init.kind, hir::ExprKind::Tup([])) && !matches!(local.pat.kind, hir::PatKind::Tuple([], ..)) { - cx.emit_span_lint(UNIT_BINDINGS, local.span, UnitBindingsDiag { - label: local.pat.span, - }); + cx.emit_span_lint( + UNIT_BINDINGS, + local.span, + UnitBindingsDiag { label: local.pat.span }, + ); } } } diff --git a/compiler/rustc_lint/src/unqualified_local_imports.rs b/compiler/rustc_lint/src/unqualified_local_imports.rs index c9dd6b32d8822..b27398a950c81 100644 --- a/compiler/rustc_lint/src/unqualified_local_imports.rs +++ b/compiler/rustc_lint/src/unqualified_local_imports.rs @@ -12,6 +12,7 @@ declare_lint! { /// ### Example /// /// ```rust,edition2018 + /// #![feature(unqualified_local_imports)] /// #![warn(unqualified_local_imports)] /// /// mod localmod { diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 8b1526bc747d4..7b43aac90c741 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1,5 +1,4 @@ use std::iter; -use std::ops::ControlFlow; use rustc_ast as ast; use rustc_ast::util::{classify, parser}; @@ -184,18 +183,22 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { let mut op_warned = false; if let Some(must_use_op) = must_use_op { - cx.emit_span_lint(UNUSED_MUST_USE, expr.span, UnusedOp { - op: must_use_op, - label: expr.span, - suggestion: if expr_is_from_block { - UnusedOpSuggestion::BlockTailExpr { - before_span: expr.span.shrink_to_lo(), - after_span: expr.span.shrink_to_hi(), - } - } else { - UnusedOpSuggestion::NormalExpr { span: expr.span.shrink_to_lo() } + cx.emit_span_lint( + UNUSED_MUST_USE, + expr.span, + UnusedOp { + op: must_use_op, + label: expr.span, + suggestion: if expr_is_from_block { + UnusedOpSuggestion::BlockTailExpr { + before_span: expr.span.shrink_to_lo(), + after_span: expr.span.shrink_to_hi(), + } + } else { + UnusedOpSuggestion::NormalExpr { span: expr.span.shrink_to_lo() } + }, }, - }); + ); op_warned = true; } @@ -289,25 +292,22 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { } ty::Adt(def, _) => is_def_must_use(cx, def.did(), span), ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => { - elaborate( - cx.tcx, - cx.tcx.explicit_item_super_predicates(def).iter_identity_copied(), - ) - // We only care about self bounds for the impl-trait - .filter_only_self() - .find_map(|(pred, _span)| { - // We only look at the `DefId`, so it is safe to skip the binder here. - if let ty::ClauseKind::Trait(ref poly_trait_predicate) = - pred.kind().skip_binder() - { - let def_id = poly_trait_predicate.trait_ref.def_id; - - is_def_must_use(cx, def_id, span) - } else { - None - } - }) - .map(|inner| MustUsePath::Opaque(Box::new(inner))) + elaborate(cx.tcx, cx.tcx.explicit_item_self_bounds(def).iter_identity_copied()) + // We only care about self bounds for the impl-trait + .filter_only_self() + .find_map(|(pred, _span)| { + // We only look at the `DefId`, so it is safe to skip the binder here. + if let ty::ClauseKind::Trait(ref poly_trait_predicate) = + pred.kind().skip_binder() + { + let def_id = poly_trait_predicate.trait_ref.def_id; + + is_def_must_use(cx, def_id, span) + } else { + None + } + }) + .map(|inner| MustUsePath::Opaque(Box::new(inner))) } ty::Dynamic(binders, _, _) => binders.iter().find_map(|predicate| { if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() @@ -492,35 +492,39 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { ); } MustUsePath::Closure(span) => { - cx.emit_span_lint(UNUSED_MUST_USE, *span, UnusedClosure { - count: plural_len, - pre: descr_pre, - post: descr_post, - }); + cx.emit_span_lint( + UNUSED_MUST_USE, + *span, + UnusedClosure { count: plural_len, pre: descr_pre, post: descr_post }, + ); } MustUsePath::Coroutine(span) => { - cx.emit_span_lint(UNUSED_MUST_USE, *span, UnusedCoroutine { - count: plural_len, - pre: descr_pre, - post: descr_post, - }); + cx.emit_span_lint( + UNUSED_MUST_USE, + *span, + UnusedCoroutine { count: plural_len, pre: descr_pre, post: descr_post }, + ); } MustUsePath::Def(span, def_id, reason) => { - cx.emit_span_lint(UNUSED_MUST_USE, *span, UnusedDef { - pre: descr_pre, - post: descr_post, - cx, - def_id: *def_id, - note: *reason, - suggestion: (!is_inner).then_some(if expr_is_from_block { - UnusedDefSuggestion::BlockTailExpr { - before_span: span.shrink_to_lo(), - after_span: span.shrink_to_hi(), - } - } else { - UnusedDefSuggestion::NormalExpr { span: span.shrink_to_lo() } - }), - }); + cx.emit_span_lint( + UNUSED_MUST_USE, + *span, + UnusedDef { + pre: descr_pre, + post: descr_post, + cx, + def_id: *def_id, + note: *reason, + suggestion: (!is_inner).then_some(if expr_is_from_block { + UnusedDefSuggestion::BlockTailExpr { + before_span: span.shrink_to_lo(), + after_span: span.shrink_to_hi(), + } + } else { + UnusedDefSuggestion::NormalExpr { span: span.shrink_to_lo() } + }), + }, + ); } } } @@ -776,29 +780,6 @@ trait UnusedDelimLint { right_pos: Option, is_kw: bool, ) { - // If `value` has `ExprKind::Err`, unused delim lint can be broken. - // For example, the following code caused ICE. - // This is because the `ExprKind::Call` in `value` has `ExprKind::Err` as its argument - // and this leads to wrong spans. #104897 - // - // ``` - // fn f(){(print!(á - // ``` - use rustc_ast::visit::{Visitor, walk_expr}; - struct ErrExprVisitor; - impl<'ast> Visitor<'ast> for ErrExprVisitor { - type Result = ControlFlow<()>; - fn visit_expr(&mut self, expr: &'ast ast::Expr) -> ControlFlow<()> { - if let ExprKind::Err(_) = expr.kind { - ControlFlow::Break(()) - } else { - walk_expr(self, expr) - } - } - } - if ErrExprVisitor.visit_expr(value).is_break() { - return; - } let spans = match value.kind { ast::ExprKind::Block(ref block, None) if let [stmt] = block.stmts.as_slice() => stmt .span @@ -870,11 +851,11 @@ trait UnusedDelimLint { end_replace: hi_replace, } }); - cx.emit_span_lint(self.lint(), primary_span, UnusedDelim { - delim: Self::DELIM_STR, - item: msg, - suggestion, - }); + cx.emit_span_lint( + self.lint(), + primary_span, + UnusedDelim { delim: Self::DELIM_STR, item: msg, suggestion }, + ); } fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { @@ -1220,7 +1201,7 @@ impl EarlyLintPass for UnusedParens { // Do not lint on `(..)` as that will result in the other arms being useless. Paren(_) // The other cases do not contain sub-patterns. - | Wild | Never | Rest | Lit(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) | Err(_) => {}, + | Wild | Never | Rest | Expr(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) | Err(_) => {}, // These are list-like patterns; parens can always be removed. TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps { self.check_unused_parens_pat(cx, p, false, false, keep_space); @@ -1561,9 +1542,11 @@ impl UnusedImportBraces { ast::UseTreeKind::Nested { .. } => return, }; - cx.emit_span_lint(UNUSED_IMPORT_BRACES, item.span, UnusedImportBracesDiag { - node: node_name, - }); + cx.emit_span_lint( + UNUSED_IMPORT_BRACES, + item.span, + UnusedImportBracesDiag { node: node_name }, + ); } } } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 2f23ab27492ca..10bf4ec77ed93 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -27,7 +27,6 @@ declare_lint_pass! { BARE_TRAIT_OBJECTS, BINDINGS_WITH_VARIANT_NAME, BREAK_WITH_LABEL_AND_LOOP, - CENUM_IMPL_DROP_CAST, COHERENCE_LEAK_CHECK, CONFLICTING_REPR_HINTS, CONST_EVALUATABLE_UNCHECKED, @@ -60,6 +59,7 @@ declare_lint_pass! { LARGE_ASSIGNMENTS, LATE_BOUND_LIFETIME_ARGUMENTS, LEGACY_DERIVE_HELPERS, + LINKER_MESSAGES, LONG_RUNNING_CONST_EVAL, LOSSY_PROVENANCE_CASTS, MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, @@ -101,6 +101,8 @@ declare_lint_pass! { SINGLE_USE_LIFETIMES, SOFT_UNSTABLE, STABLE_FEATURES, + SUPERTRAIT_ITEM_SHADOWING_DEFINITION, + SUPERTRAIT_ITEM_SHADOWING_USAGE, TAIL_EXPR_DROP_ORDER, TEST_UNSTABLE_LINT, TEXT_DIRECTION_CODEPOINT_IN_COMMENT, @@ -143,7 +145,6 @@ declare_lint_pass! { UNUSED_VARIABLES, USELESS_DEPRECATED, WARNINGS, - WASM_C_ABI, // tidy-alphabetical-end ] } @@ -2612,58 +2613,6 @@ declare_lint! { @edition Edition2024 => Warn; } -declare_lint! { - /// The `cenum_impl_drop_cast` lint detects an `as` cast of a field-less - /// `enum` that implements [`Drop`]. - /// - /// [`Drop`]: https://doc.rust-lang.org/std/ops/trait.Drop.html - /// - /// ### Example - /// - /// ```rust,compile_fail - /// # #![allow(unused)] - /// enum E { - /// A, - /// } - /// - /// impl Drop for E { - /// fn drop(&mut self) { - /// println!("Drop"); - /// } - /// } - /// - /// fn main() { - /// let e = E::A; - /// let i = e as u32; - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// Casting a field-less `enum` that does not implement [`Copy`] to an - /// integer moves the value without calling `drop`. This can result in - /// surprising behavior if it was expected that `drop` should be called. - /// Calling `drop` automatically would be inconsistent with other move - /// operations. Since neither behavior is clear or consistent, it was - /// decided that a cast of this nature will no longer be allowed. - /// - /// This is a [future-incompatible] lint to transition this to a hard error - /// in the future. See [issue #73333] for more details. - /// - /// [future-incompatible]: ../index.md#future-incompatible-lints - /// [issue #73333]: https://github.com/rust-lang/rust/issues/73333 - /// [`Copy`]: https://doc.rust-lang.org/std/marker/trait.Copy.html - pub CENUM_IMPL_DROP_CAST, - Deny, - "a C-like enum implementing Drop is cast", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, - reference: "issue #73333 ", - }; -} - declare_lint! { /// The `fuzzy_provenance_casts` lint detects an `as` cast between an integer /// and a pointer. @@ -2671,6 +2620,7 @@ declare_lint! { /// ### Example /// /// ```rust + /// #![feature(strict_provenance_lints)] /// #![warn(fuzzy_provenance_casts)] /// /// fn main() { @@ -2714,6 +2664,7 @@ declare_lint! { /// ### Example /// /// ```rust + /// #![feature(strict_provenance_lints)] /// #![warn(lossy_provenance_casts)] /// /// fn main() { @@ -3595,7 +3546,7 @@ declare_lint! { /// /// [Other ABIs]: https://doc.rust-lang.org/reference/items/external-blocks.html#abi pub MISSING_ABI, - Allow, + Warn, "No declared ABI for extern declaration" } @@ -4033,6 +3984,8 @@ declare_lint! { /// ### Example /// /// ```rust + /// // This lint is intentionally used to test the compiler's behavior + /// // when an unstable lint is enabled without the corresponding feature gate. /// #![allow(test_unstable_lint)] /// ``` /// @@ -4081,6 +4034,47 @@ declare_lint! { "call to foreign functions or function pointers with FFI-unwind ABI" } +declare_lint! { + /// The `linker_messages` lint forwards warnings from the linker. + /// + /// ### Example + /// + /// ```rust,ignore (needs CLI args, platform-specific) + /// #[warn(linker_messages)] + /// extern "C" { + /// fn foo(); + /// } + /// fn main () { unsafe { foo(); } } + /// ``` + /// + /// On Linux, using `gcc -Wl,--warn-unresolved-symbols` as a linker, this will produce + /// + /// ```text + /// warning: linker stderr: rust-lld: undefined symbol: foo + /// >>> referenced by rust_out.69edbd30df4ae57d-cgu.0 + /// >>> rust_out.rust_out.69edbd30df4ae57d-cgu.0.rcgu.o:(rust_out::main::h3a90094b06757803) + /// | + /// note: the lint level is defined here + /// --> warn.rs:1:9 + /// | + /// 1 | #![warn(linker_messages)] + /// | ^^^^^^^^^^^^^^^ + /// warning: 1 warning emitted + /// ``` + /// + /// ### Explanation + /// + /// Linkers emit platform-specific and program-specific warnings that cannot be predicted in + /// advance by the Rust compiler. Such messages are ignored by default for now. While linker + /// warnings could be very useful they have been ignored for many years by essentially all + /// users, so we need to do a bit more work than just surfacing their text to produce a clear + /// and actionable warning of similar quality to our other diagnostics. See this tracking + /// issue for more details: . + pub LINKER_MESSAGES, + Allow, + "warnings emitted at runtime by the target-specific linker program" +} + declare_lint! { /// The `named_arguments_used_positionally` lint detects cases where named arguments are only /// used positionally in format strings. This usage is valid but potentially very confusing. @@ -4362,7 +4356,7 @@ declare_lint! { /// ### Explanation /// /// It is often expected that if you can obtain an object of type `T`, then - /// you can name the type `T` as well, this lint attempts to enforce this rule. + /// you can name the type `T` as well; this lint attempts to enforce this rule. /// The recommended action is to either reexport the type properly to make it nameable, /// or document that users are not supposed to be able to name it for one reason or another. /// @@ -4643,44 +4637,6 @@ declare_lint! { }; } -declare_lint! { - /// The `wasm_c_abi` lint detects crate dependencies that are incompatible - /// with future versions of Rust that will emit spec-compliant C ABI. - /// - /// ### Example - /// - /// ```rust,ignore (needs extern crate) - /// #![deny(wasm_c_abi)] - /// ``` - /// - /// This will produce: - /// - /// ```text - /// error: the following packages contain code that will be rejected by a future version of Rust: wasm-bindgen v0.2.87 - /// | - /// note: the lint level is defined here - /// --> src/lib.rs:1:9 - /// | - /// 1 | #![deny(wasm_c_abi)] - /// | ^^^^^^^^^^ - /// ``` - /// - /// ### Explanation - /// - /// Rust has historically emitted non-spec-compliant C ABI. This has caused - /// incompatibilities between other compilers and Wasm targets. In a future - /// version of Rust this will be fixed and therefore dependencies relying - /// on the non-spec-compliant C ABI will stop functioning. - pub WASM_C_ABI, - Deny, - "detects dependencies that are incompatible with the Wasm C ABI", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, - reference: "issue #71871 ", - }; - crate_level_only -} - declare_lint! { /// The `uncovered_param_in_projection` lint detects a violation of one of Rust's orphan rules for /// foreign trait implementations that concerns the use of type parameters inside trait associated @@ -4962,6 +4918,87 @@ declare_lint! { }; } +declare_lint! { + /// The `supertrait_item_shadowing_usage` lint detects when the + /// usage of an item that is provided by both a subtrait and supertrait + /// is shadowed, preferring the subtrait. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![feature(supertrait_item_shadowing)] + /// #![deny(supertrait_item_shadowing_usage)] + /// + /// trait Upstream { + /// fn hello(&self) {} + /// } + /// impl Upstream for T {} + /// + /// trait Downstream: Upstream { + /// fn hello(&self) {} + /// } + /// impl Downstream for T {} + /// + /// struct MyType; + /// MyType.hello(); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// RFC 3624 specified a heuristic in which a supertrait item would be + /// shadowed by a subtrait item when ambiguity occurs during item + /// selection. In order to mitigate side-effects of this happening + /// silently, this lint detects these cases when users want to deny them + /// or fix the call sites. + pub SUPERTRAIT_ITEM_SHADOWING_USAGE, + // FIXME(supertrait_item_shadowing): It is not decided if this should + // warn by default at the call site. + Allow, + "detects when a supertrait item is shadowed by a subtrait item", + @feature_gate = supertrait_item_shadowing; +} + +declare_lint! { + /// The `supertrait_item_shadowing_definition` lint detects when the + /// definition of an item that is provided by both a subtrait and + /// supertrait is shadowed, preferring the subtrait. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![feature(supertrait_item_shadowing)] + /// #![deny(supertrait_item_shadowing_definition)] + /// + /// trait Upstream { + /// fn hello(&self) {} + /// } + /// impl Upstream for T {} + /// + /// trait Downstream: Upstream { + /// fn hello(&self) {} + /// } + /// impl Downstream for T {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// RFC 3624 specified a heuristic in which a supertrait item would be + /// shadowed by a subtrait item when ambiguity occurs during item + /// selection. In order to mitigate side-effects of this happening + /// silently, this lint detects these cases when users want to deny them + /// or fix their trait definitions. + pub SUPERTRAIT_ITEM_SHADOWING_DEFINITION, + // FIXME(supertrait_item_shadowing): It is not decided if this should + // warn by default at the usage site. + Allow, + "detects when a supertrait item is shadowed by a subtrait item", + @feature_gate = supertrait_item_shadowing; +} + declare_lint! { /// The `ptr_to_integer_transmute_in_consts` lint detects pointer to integer /// transmute in const functions and associated constants. diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 7786d3eb59af8..7ffe4e4e4901c 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -42,6 +42,23 @@ macro_rules! pluralize { }; } +/// Grammatical tool for displaying messages to end users in a nice form. +/// +/// Take a list of items and a function to turn those items into a `String`, and output a display +/// friendly comma separated list of those items. +// FIXME(estebank): this needs to be changed to go through the translation machinery. +pub fn listify(list: &[T], fmt: impl Fn(&T) -> String) -> Option { + Some(match list { + [only] => fmt(&only), + [others @ .., last] => format!( + "{} and {}", + others.iter().map(|i| fmt(i)).collect::>().join(", "), + fmt(&last), + ), + [] => return None, + }) +} + /// Indicates the confidence in the correctness of a suggestion. /// /// All suggestions are marked with an `Applicability`. Tools use the applicability of a suggestion @@ -161,7 +178,19 @@ impl ToStableHashKey for LintExpectation /// Setting for how to handle a lint. /// /// See: -#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, HashStable_Generic)] +#[derive( + Clone, + Copy, + PartialEq, + PartialOrd, + Eq, + Ord, + Debug, + Hash, + Encodable, + Decodable, + HashStable_Generic +)] pub enum Level { /// The `allow` level will not issue any message. Allow, @@ -783,7 +812,6 @@ pub enum BuiltinLintDiag { extern_crate: Symbol, local_crate: Symbol, }, - WasmCAbi, IllFormedAttributeInput { suggestions: Vec, }, diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml index b2a14a0022e8b..2168a0df9ec87 100644 --- a/compiler/rustc_llvm/Cargo.toml +++ b/compiler/rustc_llvm/Cargo.toml @@ -12,5 +12,5 @@ libc = "0.2.73" # tidy-alphabetical-start # Pinned so `cargo update` bumps don't cause breakage. Please also update the # pinned `cc` in `rustc_codegen_ssa` if you update `cc` here. -cc = "=1.2.6" +cc = "=1.2.13" # tidy-alphabetical-end diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index d9d28299413b1..48806888b43df 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -193,6 +193,10 @@ fn main() { cfg.define(&flag, None); } + if tracked_env_var_os("LLVM_ENZYME").is_some() { + cfg.define("ENZYME", None); + } + if tracked_env_var_os("LLVM_RUSTLLVM").is_some() { cfg.define("LLVM_RUSTLLVM", None); } diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index de14c6d188365..a6b2384f2d7b2 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -688,14 +688,20 @@ struct LLVMRustSanitizerOptions { bool SanitizeKernelAddressRecover; }; +// This symbol won't be available or used when Enzyme is not enabled +#ifdef ENZYME +extern "C" void registerEnzyme(llvm::PassBuilder &PB); +#endif + extern "C" LLVMRustResult LLVMRustOptimize( LLVMModuleRef ModuleRef, LLVMTargetMachineRef TMRef, LLVMRustPassBuilderOptLevel OptLevelRust, LLVMRustOptStage OptStage, bool IsLinkerPluginLTO, bool NoPrepopulatePasses, bool VerifyIR, bool LintIR, bool UseThinLTOBuffers, bool MergeFunctions, bool UnrollLoops, bool SLPVectorize, bool LoopVectorize, bool DisableSimplifyLibCalls, - bool EmitLifetimeMarkers, LLVMRustSanitizerOptions *SanitizerOptions, - const char *PGOGenPath, const char *PGOUsePath, bool InstrumentCoverage, + bool EmitLifetimeMarkers, bool RunEnzyme, + LLVMRustSanitizerOptions *SanitizerOptions, const char *PGOGenPath, + const char *PGOUsePath, bool InstrumentCoverage, const char *InstrProfileOutput, const char *PGOSampleUsePath, bool DebugInfoForProfiling, void *LlvmSelfProfiler, LLVMRustSelfProfileBeforePassCallback BeforePassCallback, @@ -1010,6 +1016,18 @@ extern "C" LLVMRustResult LLVMRustOptimize( MPM.addPass(NameAnonGlobalPass()); } + // now load "-enzyme" pass: +#ifdef ENZYME + if (RunEnzyme) { + registerEnzyme(PB); + if (auto Err = PB.parsePassPipeline(MPM, "enzyme")) { + std::string ErrMsg = toString(std::move(Err)); + LLVMRustSetLastError(ErrMsg.c_str()); + return LLVMRustResult::Failure; + } + } +#endif + // Upgrade all calls to old intrinsics first. for (Module::iterator I = TheModule->begin(), E = TheModule->end(); I != E;) UpgradeCallsToIntrinsic(&*I++); // must be post-increment, as we remove @@ -1389,20 +1407,14 @@ static bool clearDSOLocalOnDeclarations(Module &Mod, TargetMachine &TM) { return ClearDSOLocalOnDeclarations; } -extern "C" bool LLVMRustPrepareThinLTORename(const LLVMRustThinLTOData *Data, +extern "C" void LLVMRustPrepareThinLTORename(const LLVMRustThinLTOData *Data, LLVMModuleRef M, LLVMTargetMachineRef TM) { Module &Mod = *unwrap(M); TargetMachine &Target = *unwrap(TM); bool ClearDSOLocal = clearDSOLocalOnDeclarations(Mod, Target); - bool error = renameModuleForThinLTO(Mod, Data->Index, ClearDSOLocal); - - if (error) { - LLVMRustSetLastError("renameModuleForThinLTO failed"); - return false; - } - return true; + renameModuleForThinLTO(Mod, Data->Index, ClearDSOLocal); } extern "C" bool diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 87b1c944aa873..b8cef6a7e250e 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -2,6 +2,7 @@ #include "llvm-c/Analysis.h" #include "llvm-c/Core.h" +#include "llvm-c/DebugInfo.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" @@ -54,6 +55,10 @@ using namespace llvm; using namespace llvm::sys; using namespace llvm::object; +// This opcode is an LLVM detail that could hypothetically change (?), so +// verify that the hard-coded value in `dwarf_const.rs` still agrees with LLVM. +static_assert(dwarf::DW_OP_LLVM_fragment == 0x1000); + // LLVMAtomicOrdering is already an enum - don't create another // one. static AtomicOrdering fromRust(LLVMAtomicOrdering Ordering) { @@ -190,33 +195,6 @@ LLVMRustVerifyFunction(LLVMValueRef Fn, LLVMRustVerifierFailureAction Action) { return LLVMVerifyFunction(Fn, fromRust(Action)); } -enum class LLVMRustTailCallKind { - None, - Tail, - MustTail, - NoTail, -}; - -static CallInst::TailCallKind fromRust(LLVMRustTailCallKind Kind) { - switch (Kind) { - case LLVMRustTailCallKind::None: - return CallInst::TailCallKind::TCK_None; - case LLVMRustTailCallKind::Tail: - return CallInst::TailCallKind::TCK_Tail; - case LLVMRustTailCallKind::MustTail: - return CallInst::TailCallKind::TCK_MustTail; - case LLVMRustTailCallKind::NoTail: - return CallInst::TailCallKind::TCK_NoTail; - default: - report_fatal_error("bad CallInst::TailCallKind."); - } -} - -extern "C" void LLVMRustSetTailCallKind(LLVMValueRef Call, - LLVMRustTailCallKind TCK) { - unwrap(Call)->setTailCallKind(fromRust(TCK)); -} - extern "C" LLVMValueRef LLVMRustGetOrInsertFunction(LLVMModuleRef M, const char *Name, size_t NameLen, @@ -314,7 +292,11 @@ static Attribute::AttrKind fromRust(LLVMRustAttributeKind Kind) { case LLVMRustAttributeKind::NoAlias: return Attribute::NoAlias; case LLVMRustAttributeKind::NoCapture: +#if LLVM_VERSION_GE(21, 0) + report_fatal_error("NoCapture doesn't exist in LLVM 21"); +#else return Attribute::NoCapture; +#endif case LLVMRustAttributeKind::NoCfCheck: return Attribute::NoCfCheck; case LLVMRustAttributeKind::NoInline: @@ -426,6 +408,12 @@ extern "C" void LLVMRustEraseInstFromParent(LLVMValueRef Instr) { extern "C" LLVMAttributeRef LLVMRustCreateAttrNoValue(LLVMContextRef C, LLVMRustAttributeKind RustAttr) { +#if LLVM_VERSION_GE(21, 0) + // LLVM 21 replaced the NoCapture attribute with Captures(none). + if (RustAttr == LLVMRustAttributeKind::NoCapture) { + return wrap(Attribute::getWithCaptureInfo(*unwrap(C), CaptureInfo::none())); + } +#endif return wrap(Attribute::get(*unwrap(C), fromRust(RustAttr))); } @@ -662,8 +650,6 @@ extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, char *Constraints, unwrap(Ty), StringRef(Constraints, ConstraintsLen))); } -typedef DIBuilder *LLVMRustDIBuilderRef; - template DIT *unwrapDIPtr(LLVMMetadataRef Ref) { return (DIT *)(Ref ? unwrap(Ref) : nullptr); } @@ -672,120 +658,67 @@ template DIT *unwrapDIPtr(LLVMMetadataRef Ref) { #define DIArray DINodeArray #define unwrapDI unwrapDIPtr -// These values **must** match debuginfo::DIFlags! They also *happen* -// to match LLVM, but that isn't required as we do giant sets of -// matching below. The value shouldn't be directly passed to LLVM. -enum class LLVMRustDIFlags : uint32_t { - FlagZero = 0, - FlagPrivate = 1, - FlagProtected = 2, - FlagPublic = 3, - FlagFwdDecl = (1 << 2), - FlagAppleBlock = (1 << 3), - FlagBlockByrefStruct = (1 << 4), - FlagVirtual = (1 << 5), - FlagArtificial = (1 << 6), - FlagExplicit = (1 << 7), - FlagPrototyped = (1 << 8), - FlagObjcClassComplete = (1 << 9), - FlagObjectPointer = (1 << 10), - FlagVector = (1 << 11), - FlagStaticMember = (1 << 12), - FlagLValueReference = (1 << 13), - FlagRValueReference = (1 << 14), - FlagExternalTypeRef = (1 << 15), - FlagIntroducedVirtual = (1 << 18), - FlagBitField = (1 << 19), - FlagNoReturn = (1 << 20), - // Do not add values that are not supported by the minimum LLVM - // version we support! see llvm/include/llvm/IR/DebugInfoFlags.def -}; - -inline LLVMRustDIFlags operator&(LLVMRustDIFlags A, LLVMRustDIFlags B) { - return static_cast(static_cast(A) & - static_cast(B)); -} - -inline LLVMRustDIFlags operator|(LLVMRustDIFlags A, LLVMRustDIFlags B) { - return static_cast(static_cast(A) | - static_cast(B)); -} - -inline LLVMRustDIFlags &operator|=(LLVMRustDIFlags &A, LLVMRustDIFlags B) { - return A = A | B; -} - -inline bool isSet(LLVMRustDIFlags F) { return F != LLVMRustDIFlags::FlagZero; } - -inline LLVMRustDIFlags visibility(LLVMRustDIFlags F) { - return static_cast(static_cast(F) & 0x3); -} - -static DINode::DIFlags fromRust(LLVMRustDIFlags Flags) { - DINode::DIFlags Result = DINode::DIFlags::FlagZero; - - switch (visibility(Flags)) { - case LLVMRustDIFlags::FlagPrivate: - Result |= DINode::DIFlags::FlagPrivate; - break; - case LLVMRustDIFlags::FlagProtected: - Result |= DINode::DIFlags::FlagProtected; - break; - case LLVMRustDIFlags::FlagPublic: - Result |= DINode::DIFlags::FlagPublic; - break; - default: - // The rest are handled below - break; - } - - if (isSet(Flags & LLVMRustDIFlags::FlagFwdDecl)) { - Result |= DINode::DIFlags::FlagFwdDecl; - } - if (isSet(Flags & LLVMRustDIFlags::FlagAppleBlock)) { - Result |= DINode::DIFlags::FlagAppleBlock; - } - if (isSet(Flags & LLVMRustDIFlags::FlagVirtual)) { - Result |= DINode::DIFlags::FlagVirtual; - } - if (isSet(Flags & LLVMRustDIFlags::FlagArtificial)) { - Result |= DINode::DIFlags::FlagArtificial; - } - if (isSet(Flags & LLVMRustDIFlags::FlagExplicit)) { - Result |= DINode::DIFlags::FlagExplicit; - } - if (isSet(Flags & LLVMRustDIFlags::FlagPrototyped)) { - Result |= DINode::DIFlags::FlagPrototyped; - } - if (isSet(Flags & LLVMRustDIFlags::FlagObjcClassComplete)) { - Result |= DINode::DIFlags::FlagObjcClassComplete; - } - if (isSet(Flags & LLVMRustDIFlags::FlagObjectPointer)) { - Result |= DINode::DIFlags::FlagObjectPointer; - } - if (isSet(Flags & LLVMRustDIFlags::FlagVector)) { - Result |= DINode::DIFlags::FlagVector; - } - if (isSet(Flags & LLVMRustDIFlags::FlagStaticMember)) { - Result |= DINode::DIFlags::FlagStaticMember; - } - if (isSet(Flags & LLVMRustDIFlags::FlagLValueReference)) { - Result |= DINode::DIFlags::FlagLValueReference; - } - if (isSet(Flags & LLVMRustDIFlags::FlagRValueReference)) { - Result |= DINode::DIFlags::FlagRValueReference; - } - if (isSet(Flags & LLVMRustDIFlags::FlagIntroducedVirtual)) { - Result |= DINode::DIFlags::FlagIntroducedVirtual; - } - if (isSet(Flags & LLVMRustDIFlags::FlagBitField)) { - Result |= DINode::DIFlags::FlagBitField; - } - if (isSet(Flags & LLVMRustDIFlags::FlagNoReturn)) { - Result |= DINode::DIFlags::FlagNoReturn; - } - - return Result; +// Statically assert that `LLVMDIFlags` (C) and `DIFlags` (C++) have the same +// layout, at least for the flags we know about. This isn't guaranteed, but is +// likely to remain true, and as long as it is true it makes conversions easy. +#define ASSERT_DIFLAG_VALUE(FLAG, VALUE) \ + static_assert((LLVMDI##FLAG == (VALUE)) && (DINode::DIFlags::FLAG == (VALUE))) +ASSERT_DIFLAG_VALUE(FlagZero, 0); +ASSERT_DIFLAG_VALUE(FlagPrivate, 1); +ASSERT_DIFLAG_VALUE(FlagProtected, 2); +ASSERT_DIFLAG_VALUE(FlagPublic, 3); +// Bit (1 << 1) is part of the private/protected/public values above. +ASSERT_DIFLAG_VALUE(FlagFwdDecl, 1 << 2); +ASSERT_DIFLAG_VALUE(FlagAppleBlock, 1 << 3); +ASSERT_DIFLAG_VALUE(FlagReservedBit4, 1 << 4); +ASSERT_DIFLAG_VALUE(FlagVirtual, 1 << 5); +ASSERT_DIFLAG_VALUE(FlagArtificial, 1 << 6); +ASSERT_DIFLAG_VALUE(FlagExplicit, 1 << 7); +ASSERT_DIFLAG_VALUE(FlagPrototyped, 1 << 8); +ASSERT_DIFLAG_VALUE(FlagObjcClassComplete, 1 << 9); +ASSERT_DIFLAG_VALUE(FlagObjectPointer, 1 << 10); +ASSERT_DIFLAG_VALUE(FlagVector, 1 << 11); +ASSERT_DIFLAG_VALUE(FlagStaticMember, 1 << 12); +ASSERT_DIFLAG_VALUE(FlagLValueReference, 1 << 13); +ASSERT_DIFLAG_VALUE(FlagRValueReference, 1 << 14); +// Bit (1 << 15) has been recycled, but the C API value hasn't been renamed. +static_assert((LLVMDIFlagReserved == (1 << 15)) && + (DINode::DIFlags::FlagExportSymbols == (1 << 15))); +ASSERT_DIFLAG_VALUE(FlagSingleInheritance, 1 << 16); +ASSERT_DIFLAG_VALUE(FlagMultipleInheritance, 2 << 16); +ASSERT_DIFLAG_VALUE(FlagVirtualInheritance, 3 << 16); +// Bit (1 << 17) is part of the inheritance values above. +ASSERT_DIFLAG_VALUE(FlagIntroducedVirtual, 1 << 18); +ASSERT_DIFLAG_VALUE(FlagBitField, 1 << 19); +ASSERT_DIFLAG_VALUE(FlagNoReturn, 1 << 20); +// Bit (1 << 21) is unused, but was `LLVMDIFlagMainSubprogram`. +ASSERT_DIFLAG_VALUE(FlagTypePassByValue, 1 << 22); +ASSERT_DIFLAG_VALUE(FlagTypePassByReference, 1 << 23); +ASSERT_DIFLAG_VALUE(FlagEnumClass, 1 << 24); +ASSERT_DIFLAG_VALUE(FlagThunk, 1 << 25); +ASSERT_DIFLAG_VALUE(FlagNonTrivial, 1 << 26); +ASSERT_DIFLAG_VALUE(FlagBigEndian, 1 << 27); +ASSERT_DIFLAG_VALUE(FlagLittleEndian, 1 << 28); +ASSERT_DIFLAG_VALUE(FlagIndirectVirtualBase, (1 << 2) | (1 << 5)); +#undef ASSERT_DIFLAG_VALUE + +// There are two potential ways to convert `LLVMDIFlags` to `DIFlags`: +// - Check and copy every individual bit/subvalue from input to output. +// - Statically assert that both have the same layout, and cast. +// As long as the static assertions succeed, a cast is easier and faster. +// In the (hopefully) unlikely event that the assertions do fail someday, and +// LLVM doesn't expose its own conversion function, we'll have to switch over +// to copying each bit/subvalue. +static DINode::DIFlags fromRust(LLVMDIFlags Flags) { + // Check that all set bits are covered by the static assertions above. + const unsigned UNKNOWN_BITS = (1 << 31) | (1 << 30) | (1 << 29) | (1 << 21); + if (Flags & UNKNOWN_BITS) { + report_fatal_error("bad LLVMDIFlags"); + } + + // As long as the static assertions are satisfied and no unknown bits are + // present, we can convert from `LLVMDIFlags` to `DIFlags` with a cast. + return static_cast(Flags); } // These values **must** match debuginfo::DISPFlags! They also *happen* @@ -997,7 +930,8 @@ extern "C" LLVMValueRef LLVMRustGetLastInstruction(LLVMBasicBlockRef BB) { return nullptr; } -extern "C" void LLVMRustEraseInstBefore(LLVMBasicBlockRef bb, LLVMValueRef I) { +extern "C" void LLVMRustEraseInstUntilInclusive(LLVMBasicBlockRef bb, + LLVMValueRef I) { auto &BB = *unwrap(bb); auto &Inst = *unwrap(I); auto It = BB.begin(); @@ -1005,8 +939,6 @@ extern "C" void LLVMRustEraseInstBefore(LLVMBasicBlockRef bb, LLVMValueRef I) { ++It; // Make sure we found the Instruction. assert(It != BB.end()); - // We don't want to erase the instruction itself. - It--; // Delete in rev order to ensure no dangling references. while (It != BB.begin()) { auto Prev = std::prev(It); @@ -1036,34 +968,30 @@ extern "C" void LLVMRustGlobalAddMetadata(LLVMValueRef Global, unsigned Kind, unwrap(Global)->addMetadata(Kind, *unwrap(MD)); } -extern "C" LLVMRustDIBuilderRef LLVMRustDIBuilderCreate(LLVMModuleRef M) { - return new DIBuilder(*unwrap(M)); -} - -extern "C" void LLVMRustDIBuilderDispose(LLVMRustDIBuilderRef Builder) { - delete Builder; +extern "C" LLVMDIBuilderRef LLVMRustDIBuilderCreate(LLVMModuleRef M) { + return wrap(new DIBuilder(*unwrap(M))); } -extern "C" void LLVMRustDIBuilderFinalize(LLVMRustDIBuilderRef Builder) { - Builder->finalize(); +extern "C" void LLVMRustDIBuilderDispose(LLVMDIBuilderRef Builder) { + delete unwrap(Builder); } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateCompileUnit( - LLVMRustDIBuilderRef Builder, unsigned Lang, LLVMMetadataRef FileRef, + LLVMDIBuilderRef Builder, unsigned Lang, LLVMMetadataRef FileRef, const char *Producer, size_t ProducerLen, bool isOptimized, const char *Flags, unsigned RuntimeVer, const char *SplitName, size_t SplitNameLen, LLVMRustDebugEmissionKind Kind, uint64_t DWOId, bool SplitDebugInlining, LLVMRustDebugNameTableKind TableKind) { auto *File = unwrapDI(FileRef); - return wrap(Builder->createCompileUnit( + return wrap(unwrap(Builder)->createCompileUnit( Lang, File, StringRef(Producer, ProducerLen), isOptimized, Flags, RuntimeVer, StringRef(SplitName, SplitNameLen), fromRust(Kind), DWOId, SplitDebugInlining, false, fromRust(TableKind))); } extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateFile(LLVMRustDIBuilderRef Builder, const char *Filename, +LLVMRustDIBuilderCreateFile(LLVMDIBuilderRef Builder, const char *Filename, size_t FilenameLen, const char *Directory, size_t DirectoryLen, LLVMRustChecksumKind CSKind, const char *Checksum, size_t ChecksumLen, @@ -1076,29 +1004,29 @@ LLVMRustDIBuilderCreateFile(LLVMRustDIBuilderRef Builder, const char *Filename, std::optional oSource{}; if (Source) oSource = StringRef(Source, SourceLen); - return wrap(Builder->createFile(StringRef(Filename, FilenameLen), - StringRef(Directory, DirectoryLen), CSInfo, - oSource)); + return wrap(unwrap(Builder)->createFile(StringRef(Filename, FilenameLen), + StringRef(Directory, DirectoryLen), + CSInfo, oSource)); } extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateSubroutineType(LLVMRustDIBuilderRef Builder, +LLVMRustDIBuilderCreateSubroutineType(LLVMDIBuilderRef Builder, LLVMMetadataRef ParameterTypes) { - return wrap(Builder->createSubroutineType( + return wrap(unwrap(Builder)->createSubroutineType( DITypeRefArray(unwrap(ParameterTypes)))); } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, + LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, const char *LinkageName, size_t LinkageNameLen, LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Ty, - unsigned ScopeLine, LLVMRustDIFlags Flags, LLVMRustDISPFlags SPFlags, + unsigned ScopeLine, LLVMDIFlags Flags, LLVMRustDISPFlags SPFlags, LLVMValueRef MaybeFn, LLVMMetadataRef TParam, LLVMMetadataRef Decl) { DITemplateParameterArray TParams = DITemplateParameterArray(unwrap(TParam)); DISubprogram::DISPFlags llvmSPFlags = fromRust(SPFlags); DINode::DIFlags llvmFlags = fromRust(Flags); - DISubprogram *Sub = Builder->createFunction( + DISubprogram *Sub = unwrap(Builder)->createFunction( unwrapDI(Scope), StringRef(Name, NameLen), StringRef(LinkageName, LinkageNameLen), unwrapDI(File), LineNo, unwrapDI(Ty), ScopeLine, llvmFlags, llvmSPFlags, @@ -1109,15 +1037,15 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction( } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMethod( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, + LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, const char *LinkageName, size_t LinkageNameLen, LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Ty, - LLVMRustDIFlags Flags, LLVMRustDISPFlags SPFlags, LLVMMetadataRef TParam) { + LLVMDIFlags Flags, LLVMRustDISPFlags SPFlags, LLVMMetadataRef TParam) { DITemplateParameterArray TParams = DITemplateParameterArray(unwrap(TParam)); DISubprogram::DISPFlags llvmSPFlags = fromRust(SPFlags); DINode::DIFlags llvmFlags = fromRust(Flags); - DISubprogram *Sub = Builder->createMethod( + DISubprogram *Sub = unwrap(Builder)->createMethod( unwrapDI(Scope), StringRef(Name, NameLen), StringRef(LinkageName, LinkageNameLen), unwrapDI(File), LineNo, unwrapDI(Ty), 0, 0, @@ -1127,39 +1055,39 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMethod( } extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateBasicType(LLVMRustDIBuilderRef Builder, const char *Name, +LLVMRustDIBuilderCreateBasicType(LLVMDIBuilderRef Builder, const char *Name, size_t NameLen, uint64_t SizeInBits, unsigned Encoding) { - return wrap( - Builder->createBasicType(StringRef(Name, NameLen), SizeInBits, Encoding)); + return wrap(unwrap(Builder)->createBasicType(StringRef(Name, NameLen), + SizeInBits, Encoding)); } extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateTypedef(LLVMRustDIBuilderRef Builder, - LLVMMetadataRef Type, const char *Name, - size_t NameLen, LLVMMetadataRef File, - unsigned LineNo, LLVMMetadataRef Scope) { - return wrap(Builder->createTypedef( +LLVMRustDIBuilderCreateTypedef(LLVMDIBuilderRef Builder, LLVMMetadataRef Type, + const char *Name, size_t NameLen, + LLVMMetadataRef File, unsigned LineNo, + LLVMMetadataRef Scope) { + return wrap(unwrap(Builder)->createTypedef( unwrap(Type), StringRef(Name, NameLen), unwrap(File), LineNo, unwrapDIPtr(Scope))); } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreatePointerType( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef PointeeTy, - uint64_t SizeInBits, uint32_t AlignInBits, unsigned AddressSpace, - const char *Name, size_t NameLen) { - return wrap(Builder->createPointerType(unwrapDI(PointeeTy), - SizeInBits, AlignInBits, AddressSpace, - StringRef(Name, NameLen))); + LLVMDIBuilderRef Builder, LLVMMetadataRef PointeeTy, uint64_t SizeInBits, + uint32_t AlignInBits, unsigned AddressSpace, const char *Name, + size_t NameLen) { + return wrap(unwrap(Builder)->createPointerType( + unwrapDI(PointeeTy), SizeInBits, AlignInBits, AddressSpace, + StringRef(Name, NameLen))); } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStructType( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, + LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNumber, - uint64_t SizeInBits, uint32_t AlignInBits, LLVMRustDIFlags Flags, + uint64_t SizeInBits, uint32_t AlignInBits, LLVMDIFlags Flags, LLVMMetadataRef DerivedFrom, LLVMMetadataRef Elements, unsigned RunTimeLang, LLVMMetadataRef VTableHolder, const char *UniqueId, size_t UniqueIdLen) { - return wrap(Builder->createStructType( + return wrap(unwrap(Builder)->createStructType( unwrapDI(Scope), StringRef(Name, NameLen), unwrapDI(File), LineNumber, SizeInBits, AlignInBits, fromRust(Flags), unwrapDI(DerivedFrom), @@ -1168,12 +1096,12 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStructType( } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantPart( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, + LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNumber, - uint64_t SizeInBits, uint32_t AlignInBits, LLVMRustDIFlags Flags, + uint64_t SizeInBits, uint32_t AlignInBits, LLVMDIFlags Flags, LLVMMetadataRef Discriminator, LLVMMetadataRef Elements, const char *UniqueId, size_t UniqueIdLen) { - return wrap(Builder->createVariantPart( + return wrap(unwrap(Builder)->createVariantPart( unwrapDI(Scope), StringRef(Name, NameLen), unwrapDI(File), LineNumber, SizeInBits, AlignInBits, fromRust(Flags), unwrapDI(Discriminator), @@ -1182,57 +1110,50 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantPart( } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMemberType( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, + LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNo, uint64_t SizeInBits, - uint32_t AlignInBits, uint64_t OffsetInBits, LLVMRustDIFlags Flags, + uint32_t AlignInBits, uint64_t OffsetInBits, LLVMDIFlags Flags, LLVMMetadataRef Ty) { - return wrap(Builder->createMemberType( + return wrap(unwrap(Builder)->createMemberType( unwrapDI(Scope), StringRef(Name, NameLen), unwrapDI(File), LineNo, SizeInBits, AlignInBits, OffsetInBits, fromRust(Flags), unwrapDI(Ty))); } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantMemberType( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, + LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNo, uint64_t SizeInBits, uint32_t AlignInBits, uint64_t OffsetInBits, LLVMValueRef Discriminant, - LLVMRustDIFlags Flags, LLVMMetadataRef Ty) { + LLVMDIFlags Flags, LLVMMetadataRef Ty) { llvm::ConstantInt *D = nullptr; if (Discriminant) { D = unwrap(Discriminant); } - return wrap(Builder->createVariantMemberType( + return wrap(unwrap(Builder)->createVariantMemberType( unwrapDI(Scope), StringRef(Name, NameLen), unwrapDI(File), LineNo, SizeInBits, AlignInBits, OffsetInBits, D, fromRust(Flags), unwrapDI(Ty))); } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticMemberType( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, + LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Ty, - LLVMRustDIFlags Flags, LLVMValueRef val, uint32_t AlignInBits) { - return wrap(Builder->createStaticMemberType( + LLVMDIFlags Flags, LLVMValueRef val, uint32_t AlignInBits) { + return wrap(unwrap(Builder)->createStaticMemberType( unwrapDI(Scope), StringRef(Name, NameLen), unwrapDI(File), LineNo, unwrapDI(Ty), fromRust(Flags), unwrap(val), llvm::dwarf::DW_TAG_member, AlignInBits)); } extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateLexicalBlock(LLVMRustDIBuilderRef Builder, - LLVMMetadataRef Scope, LLVMMetadataRef File, - unsigned Line, unsigned Col) { - return wrap(Builder->createLexicalBlock(unwrapDI(Scope), - unwrapDI(File), Line, Col)); -} - -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateLexicalBlockFile( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, LLVMMetadataRef File) { - return wrap(Builder->createLexicalBlockFile(unwrapDI(Scope), - unwrapDI(File))); +LLVMRustDIBuilderCreateQualifiedType(LLVMDIBuilderRef Builder, unsigned Tag, + LLVMMetadataRef Type) { + return wrap( + unwrap(Builder)->createQualifiedType(Tag, unwrapDI(Type))); } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Context, const char *Name, + LLVMDIBuilderRef Builder, LLVMMetadataRef Context, const char *Name, size_t NameLen, const char *LinkageName, size_t LinkageNameLen, LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Ty, bool IsLocalToUnit, LLVMValueRef V, LLVMMetadataRef Decl = nullptr, @@ -1241,16 +1162,16 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable( llvm::DIExpression *InitExpr = nullptr; if (llvm::ConstantInt *IntVal = llvm::dyn_cast(InitVal)) { - InitExpr = Builder->createConstantValueExpression( + InitExpr = unwrap(Builder)->createConstantValueExpression( IntVal->getValue().getSExtValue()); } else if (llvm::ConstantFP *FPVal = llvm::dyn_cast(InitVal)) { - InitExpr = Builder->createConstantValueExpression( + InitExpr = unwrap(Builder)->createConstantValueExpression( FPVal->getValueAPF().bitcastToAPInt().getZExtValue()); } llvm::DIGlobalVariableExpression *VarExpr = - Builder->createGlobalVariableExpression( + unwrap(Builder)->createGlobalVariableExpression( unwrapDI(Context), StringRef(Name, NameLen), StringRef(LinkageName, LinkageNameLen), unwrapDI(File), LineNo, unwrapDI(Ty), IsLocalToUnit, @@ -1263,17 +1184,17 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable( } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable( - LLVMRustDIBuilderRef Builder, unsigned Tag, LLVMMetadataRef Scope, + LLVMDIBuilderRef Builder, unsigned Tag, LLVMMetadataRef Scope, const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNo, - LLVMMetadataRef Ty, bool AlwaysPreserve, LLVMRustDIFlags Flags, - unsigned ArgNo, uint32_t AlignInBits) { + LLVMMetadataRef Ty, bool AlwaysPreserve, LLVMDIFlags Flags, unsigned ArgNo, + uint32_t AlignInBits) { if (Tag == 0x100) { // DW_TAG_auto_variable - return wrap(Builder->createAutoVariable( + return wrap(unwrap(Builder)->createAutoVariable( unwrapDI(Scope), StringRef(Name, NameLen), unwrapDI(File), LineNo, unwrapDI(Ty), AlwaysPreserve, fromRust(Flags), AlignInBits)); } else { - return wrap(Builder->createParameterVariable( + return wrap(unwrap(Builder)->createParameterVariable( unwrapDI(Scope), StringRef(Name, NameLen), ArgNo, unwrapDI(File), LineNo, unwrapDI(Ty), AlwaysPreserve, fromRust(Flags))); @@ -1281,53 +1202,56 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable( } extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateArrayType(LLVMRustDIBuilderRef Builder, uint64_t Size, +LLVMRustDIBuilderCreateArrayType(LLVMDIBuilderRef Builder, uint64_t Size, uint32_t AlignInBits, LLVMMetadataRef Ty, LLVMMetadataRef Subscripts) { - return wrap( - Builder->createArrayType(Size, AlignInBits, unwrapDI(Ty), - DINodeArray(unwrapDI(Subscripts)))); + return wrap(unwrap(Builder)->createArrayType( + Size, AlignInBits, unwrapDI(Ty), + DINodeArray(unwrapDI(Subscripts)))); } extern "C" LLVMMetadataRef -LLVMRustDIBuilderGetOrCreateSubrange(LLVMRustDIBuilderRef Builder, int64_t Lo, +LLVMRustDIBuilderGetOrCreateSubrange(LLVMDIBuilderRef Builder, int64_t Lo, int64_t Count) { - return wrap(Builder->getOrCreateSubrange(Lo, Count)); + return wrap(unwrap(Builder)->getOrCreateSubrange(Lo, Count)); } extern "C" LLVMMetadataRef -LLVMRustDIBuilderGetOrCreateArray(LLVMRustDIBuilderRef Builder, +LLVMRustDIBuilderGetOrCreateArray(LLVMDIBuilderRef Builder, LLVMMetadataRef *Ptr, unsigned Count) { Metadata **DataValue = unwrap(Ptr); - return wrap( - Builder->getOrCreateArray(ArrayRef(DataValue, Count)).get()); + return wrap(unwrap(Builder) + ->getOrCreateArray(ArrayRef(DataValue, Count)) + .get()); } -extern "C" void LLVMRustDIBuilderInsertDeclareAtEnd( - LLVMRustDIBuilderRef Builder, LLVMValueRef V, LLVMMetadataRef VarInfo, - uint64_t *AddrOps, unsigned AddrOpsCount, LLVMMetadataRef DL, - LLVMBasicBlockRef InsertAtEnd) { - Builder->insertDeclare(unwrap(V), unwrap(VarInfo), - Builder->createExpression( - llvm::ArrayRef(AddrOps, AddrOpsCount)), - DebugLoc(cast(unwrap(DL))), - unwrap(InsertAtEnd)); +extern "C" void +LLVMRustDIBuilderInsertDeclareAtEnd(LLVMDIBuilderRef Builder, LLVMValueRef V, + LLVMMetadataRef VarInfo, uint64_t *AddrOps, + unsigned AddrOpsCount, LLVMMetadataRef DL, + LLVMBasicBlockRef InsertAtEnd) { + unwrap(Builder)->insertDeclare( + unwrap(V), unwrap(VarInfo), + unwrap(Builder)->createExpression( + llvm::ArrayRef(AddrOps, AddrOpsCount)), + DebugLoc(cast(unwrap(DL))), unwrap(InsertAtEnd)); } -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerator( - LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen, - const uint64_t Value[2], unsigned SizeInBits, bool IsUnsigned) { - return wrap(Builder->createEnumerator( +extern "C" LLVMMetadataRef +LLVMRustDIBuilderCreateEnumerator(LLVMDIBuilderRef Builder, const char *Name, + size_t NameLen, const uint64_t Value[2], + unsigned SizeInBits, bool IsUnsigned) { + return wrap(unwrap(Builder)->createEnumerator( StringRef(Name, NameLen), APSInt(APInt(SizeInBits, ArrayRef(Value, 2)), IsUnsigned))); } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerationType( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, + LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNumber, uint64_t SizeInBits, uint32_t AlignInBits, LLVMMetadataRef Elements, LLVMMetadataRef ClassTy, bool IsScoped) { - return wrap(Builder->createEnumerationType( + return wrap(unwrap(Builder)->createEnumerationType( unwrapDI(Scope), StringRef(Name, NameLen), unwrapDI(File), LineNumber, SizeInBits, AlignInBits, DINodeArray(unwrapDI(Elements)), unwrapDI(ClassTy), @@ -1335,12 +1259,12 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerationType( } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateUnionType( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, + LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNumber, - uint64_t SizeInBits, uint32_t AlignInBits, LLVMRustDIFlags Flags, + uint64_t SizeInBits, uint32_t AlignInBits, LLVMDIFlags Flags, LLVMMetadataRef Elements, unsigned RunTimeLang, const char *UniqueId, size_t UniqueIdLen) { - return wrap(Builder->createUnionType( + return wrap(unwrap(Builder)->createUnionType( unwrapDI(Scope), StringRef(Name, NameLen), unwrapDI(File), LineNumber, SizeInBits, AlignInBits, fromRust(Flags), DINodeArray(unwrapDI(Elements)), RunTimeLang, @@ -1348,38 +1272,20 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateUnionType( } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTemplateTypeParameter( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, + LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, LLVMMetadataRef Ty) { bool IsDefault = false; // FIXME: should we ever set this true? - return wrap(Builder->createTemplateTypeParameter( + return wrap(unwrap(Builder)->createTemplateTypeParameter( unwrapDI(Scope), StringRef(Name, NameLen), unwrapDI(Ty), IsDefault)); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateNameSpace(LLVMRustDIBuilderRef Builder, - LLVMMetadataRef Scope, const char *Name, - size_t NameLen, bool ExportSymbols) { - return wrap(Builder->createNameSpace( - unwrapDI(Scope), StringRef(Name, NameLen), ExportSymbols)); -} - extern "C" void LLVMRustDICompositeTypeReplaceArrays( - LLVMRustDIBuilderRef Builder, LLVMMetadataRef CompositeTy, + LLVMDIBuilderRef Builder, LLVMMetadataRef CompositeTy, LLVMMetadataRef Elements, LLVMMetadataRef Params) { DICompositeType *Tmp = unwrapDI(CompositeTy); - Builder->replaceArrays(Tmp, DINodeArray(unwrap(Elements)), - DINodeArray(unwrap(Params))); -} - -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateDebugLocation(unsigned Line, unsigned Column, - LLVMMetadataRef ScopeRef, - LLVMMetadataRef InlinedAt) { - MDNode *Scope = unwrapDIPtr(ScopeRef); - DILocation *Loc = DILocation::get(Scope->getContext(), Line, Column, Scope, - unwrapDIPtr(InlinedAt)); - return wrap(Loc); + unwrap(Builder)->replaceArrays(Tmp, DINodeArray(unwrap(Elements)), + DINodeArray(unwrap(Params))); } extern "C" LLVMMetadataRef @@ -1390,18 +1296,6 @@ LLVMRustDILocationCloneWithBaseDiscriminator(LLVMMetadataRef Location, return wrap(NewLoc.has_value() ? NewLoc.value() : nullptr); } -extern "C" uint64_t LLVMRustDIBuilderCreateOpDeref() { - return dwarf::DW_OP_deref; -} - -extern "C" uint64_t LLVMRustDIBuilderCreateOpPlusUconst() { - return dwarf::DW_OP_plus_uconst; -} - -extern "C" uint64_t LLVMRustDIBuilderCreateOpLLVMFragment() { - return dwarf::DW_OP_LLVM_fragment; -} - extern "C" void LLVMRustWriteTypeToString(LLVMTypeRef Ty, RustStringRef Str) { auto OS = RawRustStringOstream(Str); unwrap(Ty)->print(OS); @@ -2055,10 +1949,6 @@ extern "C" int32_t LLVMRustGetElementTypeArgIndex(LLVMValueRef CallSite) { return -1; } -extern "C" bool LLVMRustIsBitcode(char *ptr, size_t len) { - return identify_magic(StringRef(ptr, len)) == file_magic::bitcode; -} - extern "C" bool LLVMRustIsNonGVFunctionPointerTy(LLVMValueRef V) { if (unwrap(V)->getType()->isPointerTy()) { if (auto *GV = dyn_cast(unwrap(V))) { diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs index a3890fc937e79..d0ef82f4a6ce2 100644 --- a/compiler/rustc_log/src/lib.rs +++ b/compiler/rustc_log/src/lib.rs @@ -130,11 +130,11 @@ pub fn init_logger(cfg: LoggerConfig) -> Result<(), Error> { let subscriber = tracing_subscriber::Registry::default().with(filter).with(layer); match cfg.backtrace { - Ok(str) => { + Ok(backtrace_target) => { let fmt_layer = tracing_subscriber::fmt::layer() .with_writer(io::stderr) .without_time() - .event_format(BacktraceFormatter { backtrace_target: str }); + .event_format(BacktraceFormatter { backtrace_target }); let subscriber = subscriber.with(fmt_layer); tracing::subscriber::set_global_default(subscriber).unwrap(); } diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 88ea6e07d6eb6..62bf34ad5adce 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -118,11 +118,17 @@ struct QueryModifiers { /// Generate a `feed` method to set the query's value from another query. feedable: Option, - /// Forward the result on ensure if the query gets recomputed, and - /// return `Ok(())` otherwise. Only applicable to queries returning - /// `Result`. The `T` is not returned from `ensure` - /// invocations. - ensure_forwards_result_if_red: Option, + /// When this query is called via `tcx.ensure_ok()`, it returns + /// `Result<(), ErrorGuaranteed>` instead of `()`. If the query needs to + /// be executed, and that execution returns an error, the error result is + /// returned to the caller. + /// + /// If execution is skipped, a synthetic `Ok(())` is returned, on the + /// assumption that a query with all-green inputs must have succeeded. + /// + /// Can only be applied to queries with a return value of + /// `Result<_, ErrorGuaranteed>`. + return_result_from_ensure_ok: Option, } fn parse_query_modifiers(input: ParseStream<'_>) -> Result { @@ -138,7 +144,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { let mut depth_limit = None; let mut separate_provide_extern = None; let mut feedable = None; - let mut ensure_forwards_result_if_red = None; + let mut return_result_from_ensure_ok = None; while !input.is_empty() { let modifier: Ident = input.parse()?; @@ -200,8 +206,8 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { try_insert!(separate_provide_extern = modifier); } else if modifier == "feedable" { try_insert!(feedable = modifier); - } else if modifier == "ensure_forwards_result_if_red" { - try_insert!(ensure_forwards_result_if_red = modifier); + } else if modifier == "return_result_from_ensure_ok" { + try_insert!(return_result_from_ensure_ok = modifier); } else { return Err(Error::new(modifier.span(), "unknown query modifier")); } @@ -222,7 +228,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { depth_limit, separate_provide_extern, feedable, - ensure_forwards_result_if_red, + return_result_from_ensure_ok, }) } @@ -354,7 +360,7 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream { eval_always, depth_limit, separate_provide_extern, - ensure_forwards_result_if_red, + return_result_from_ensure_ok, ); if modifiers.cache.is_some() { diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs index ce692abfb9834..2c33a0ac0aafe 100644 --- a/compiler/rustc_macros/src/serialize.rs +++ b/compiler/rustc_macros/src/serialize.rs @@ -105,11 +105,14 @@ fn decodable_body( }; s.underscore_const(true); - s.bound_impl(quote!(::rustc_serialize::Decodable<#decoder_ty>), quote! { - fn decode(__decoder: &mut #decoder_ty) -> Self { - #decode_body - } - }) + s.bound_impl( + quote!(::rustc_serialize::Decodable<#decoder_ty>), + quote! { + fn decode(__decoder: &mut #decoder_ty) -> Self { + #decode_body + } + }, + ) } fn decode_field(field: &syn::Field) -> proc_macro2::TokenStream { @@ -285,13 +288,16 @@ fn encodable_body( quote! {} }; - s.bound_impl(quote!(::rustc_serialize::Encodable<#encoder_ty>), quote! { - fn encode( - &self, - __encoder: &mut #encoder_ty, - ) { - #lints - #encode_body - } - }) + s.bound_impl( + quote!(::rustc_serialize::Encodable<#encoder_ty>), + quote! { + fn encode( + &self, + __encoder: &mut #encoder_ty, + ) { + #lints + #encode_body + } + }, + ) } diff --git a/compiler/rustc_macros/src/symbols.rs b/compiler/rustc_macros/src/symbols.rs index 2552c0a0cfc79..37200f62eb5a2 100644 --- a/compiler/rustc_macros/src/symbols.rs +++ b/compiler/rustc_macros/src/symbols.rs @@ -156,14 +156,14 @@ impl Entries { Entries { map: HashMap::with_capacity(capacity) } } - fn insert(&mut self, span: Span, str: &str, errors: &mut Errors) -> u32 { - if let Some(prev) = self.map.get(str) { - errors.error(span, format!("Symbol `{str}` is duplicated")); + fn insert(&mut self, span: Span, s: &str, errors: &mut Errors) -> u32 { + if let Some(prev) = self.map.get(s) { + errors.error(span, format!("Symbol `{s}` is duplicated")); errors.error(prev.span_of_name, "location of previous definition".to_string()); prev.idx } else { let idx = self.len(); - self.map.insert(str.to_string(), Preinterned { idx, span_of_name: span }); + self.map.insert(s.to_string(), Preinterned { idx, span_of_name: span }); idx } } @@ -192,14 +192,14 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec) { let mut entries = Entries::with_capacity(input.keywords.len() + input.symbols.len() + 10); let mut prev_key: Option<(Span, String)> = None; - let mut check_order = |span: Span, str: &str, errors: &mut Errors| { + let mut check_order = |span: Span, s: &str, errors: &mut Errors| { if let Some((prev_span, ref prev_str)) = prev_key { - if str < prev_str { - errors.error(span, format!("Symbol `{str}` must precede `{prev_str}`")); + if s < prev_str { + errors.error(span, format!("Symbol `{s}` must precede `{prev_str}`")); errors.error(prev_span, format!("location of previous symbol `{prev_str}`")); } } - prev_key = Some((span, str.to_string())); + prev_key = Some((span, s.to_string())); }; // Generate the listed keywords. diff --git a/compiler/rustc_macros/src/symbols/tests.rs b/compiler/rustc_macros/src/symbols/tests.rs index b32ce60e204ab..9c53453df5b54 100644 --- a/compiler/rustc_macros/src/symbols/tests.rs +++ b/compiler/rustc_macros/src/symbols/tests.rs @@ -94,8 +94,8 @@ fn check_symbol_order() { aardvark, } }; - test_symbols_macro(input, &[ - "Symbol `aardvark` must precede `zebra`", - "location of previous symbol `zebra`", - ]); + test_symbols_macro( + input, + &["Symbol `aardvark` must precede `zebra`", "location of previous symbol `zebra`"], + ); } diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index 6d7d88fa8d7af..6fc84b06647f5 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -113,6 +113,14 @@ metadata_incompatible_rustc = found crate `{$crate_name}` compiled by an incompatible version of rustc{$add_info} .help = please recompile that crate using this compiler ({$rustc_version}) (consider running `cargo clean` first) +metadata_incompatible_target_modifiers = + mixing `{$flag_name_prefixed}` will cause an ABI mismatch in crate `{$local_crate}` + .note = `{$flag_name_prefixed}={$flag_local_value}` in this crate is incompatible with `{$flag_name_prefixed}={$flag_extern_value}` in dependency `{$extern_crate}` + .help = the `{$flag_name_prefixed}` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + +metadata_incompatible_target_modifiers_help_allow = if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch={$flag_name}` to silence this error +metadata_incompatible_target_modifiers_help_fix = set `{$flag_name_prefixed}={$flag_extern_value}` in this crate or `{$flag_name_prefixed}={$flag_local_value}` in `{$extern_crate}` + metadata_incompatible_wasm_link = `wasm_import_module` is incompatible with other arguments in `#[link]` attributes @@ -284,12 +292,17 @@ metadata_unknown_link_kind = metadata_unknown_link_modifier = unknown linking modifier `{$modifier}`, expected one of: bundle, verbatim, whole-archive, as-needed +metadata_unknown_target_modifier_unsafe_allowed = unknown target modifier `{$flag_name}`, requested by `-Cunsafe-allow-abi-mismatch={$flag_name}` + metadata_unsupported_abi = ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture metadata_unsupported_abi_i686 = ABI not supported by `#[link(kind = "raw-dylib")]` on i686 +metadata_wasm_c_abi = + older versions of the `wasm-bindgen` crate are incompatible with current versions of Rust; please update to `wasm-bindgen` v0.2.88 + metadata_wasm_import_form = wasm import module must be of the form `wasm_import_module = "string"` diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index c8715f94d5dd3..c2dda21bb72e6 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -23,19 +23,24 @@ use rustc_hir::definitions::Definitions; use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::ty::{TyCtxt, TyCtxtFeed}; -use rustc_session::config::{self, CrateType, ExternLocation}; +use rustc_session::config::{ + self, CrateType, ExtendedTargetModifierInfo, ExternLocation, OptionsTargetModifiers, + TargetModifier, +}; use rustc_session::cstore::{CrateDepKind, CrateSource, ExternCrate, ExternCrateSource}; use rustc_session::lint::{self, BuiltinLintDiag}; use rustc_session::output::validate_crate_name; use rustc_session::search_paths::PathKind; use rustc_span::edition::Edition; -use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym}; +use rustc_span::{DUMMY_SP, Ident, STDLIB_STABLE_CRATES, Span, Symbol, sym}; use rustc_target::spec::{PanicStrategy, Target, TargetTuple}; use tracing::{debug, info, trace}; use crate::errors; use crate::locator::{CrateError, CrateLocator, CratePaths}; -use crate::rmeta::{CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob}; +use crate::rmeta::{ + CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob, TargetModifiers, +}; /// The backend's way to give the crate store access to the metadata in a library. /// Note that it returns the raw metadata bytes stored in the library file, whether @@ -296,6 +301,94 @@ impl CStore { } } + fn report_target_modifiers_extended( + tcx: TyCtxt<'_>, + krate: &Crate, + mods: &TargetModifiers, + dep_mods: &TargetModifiers, + data: &CrateMetadata, + ) { + let span = krate.spans.inner_span.shrink_to_lo(); + let allowed_flag_mismatches = &tcx.sess.opts.cg.unsafe_allow_abi_mismatch; + let name = tcx.crate_name(LOCAL_CRATE); + let tmod_extender = |tmod: &TargetModifier| (tmod.extend(), tmod.clone()); + let report_diff = |prefix: &String, + opt_name: &String, + flag_local_value: &String, + flag_extern_value: &String| { + if allowed_flag_mismatches.contains(&opt_name) { + return; + } + tcx.dcx().emit_err(errors::IncompatibleTargetModifiers { + span, + extern_crate: data.name(), + local_crate: name, + flag_name: opt_name.clone(), + flag_name_prefixed: format!("-{}{}", prefix, opt_name), + flag_local_value: flag_local_value.to_string(), + flag_extern_value: flag_extern_value.to_string(), + }); + }; + let mut it1 = mods.iter().map(tmod_extender); + let mut it2 = dep_mods.iter().map(tmod_extender); + let mut left_name_val: Option<(ExtendedTargetModifierInfo, TargetModifier)> = None; + let mut right_name_val: Option<(ExtendedTargetModifierInfo, TargetModifier)> = None; + let no_val = "*".to_string(); + loop { + left_name_val = left_name_val.or_else(|| it1.next()); + right_name_val = right_name_val.or_else(|| it2.next()); + match (&left_name_val, &right_name_val) { + (Some(l), Some(r)) => match l.1.opt.cmp(&r.1.opt) { + cmp::Ordering::Equal => { + if l.0.tech_value != r.0.tech_value { + report_diff(&l.0.prefix, &l.0.name, &l.1.value_name, &r.1.value_name); + } + left_name_val = None; + right_name_val = None; + } + cmp::Ordering::Greater => { + report_diff(&r.0.prefix, &r.0.name, &no_val, &r.1.value_name); + right_name_val = None; + } + cmp::Ordering::Less => { + report_diff(&l.0.prefix, &l.0.name, &l.1.value_name, &no_val); + left_name_val = None; + } + }, + (Some(l), None) => { + report_diff(&l.0.prefix, &l.0.name, &l.1.value_name, &no_val); + left_name_val = None; + } + (None, Some(r)) => { + report_diff(&r.0.prefix, &r.0.name, &no_val, &r.1.value_name); + right_name_val = None; + } + (None, None) => break, + } + } + } + + pub fn report_incompatible_target_modifiers(&self, tcx: TyCtxt<'_>, krate: &Crate) { + for flag_name in &tcx.sess.opts.cg.unsafe_allow_abi_mismatch { + if !OptionsTargetModifiers::is_target_modifier(flag_name) { + tcx.dcx().emit_err(errors::UnknownTargetModifierUnsafeAllowed { + span: krate.spans.inner_span.shrink_to_lo(), + flag_name: flag_name.clone(), + }); + } + } + let mods = tcx.sess.opts.gather_target_modifiers(); + for (_cnum, data) in self.iter_crate_data() { + if data.is_proc_macro_crate() { + continue; + } + let dep_mods = data.target_modifiers(); + if mods != dep_mods { + Self::report_target_modifiers_extended(tcx, krate, &mods, &dep_mods, data); + } + } + } + pub fn new(metadata_loader: Box) -> CStore { CStore { metadata_loader, @@ -390,19 +483,52 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { None } - // The `dependency` type is determined by the command line arguments(`--extern`) and - // `private_dep`. However, sometimes the directly dependent crate is not specified by - // `--extern`, in this case, `private-dep` is none during loading. This is equivalent to the - // scenario where the command parameter is set to `public-dependency` - fn is_private_dep(&self, name: &str, private_dep: Option) -> bool { - self.sess.opts.externs.get(name).map_or(private_dep.unwrap_or(false), |e| e.is_private_dep) - && private_dep.unwrap_or(true) + /// Determine whether a dependency should be considered private. + /// + /// Dependencies are private if they get extern option specified, e.g. `--extern priv:mycrate`. + /// This is stored in metadata, so `private_dep` can be correctly set during load. A `Some` + /// value for `private_dep` indicates that the crate is known to be private or public (note + /// that any `None` or `Some(false)` use of the same crate will make it public). + /// + /// Sometimes the directly dependent crate is not specified by `--extern`, in this case, + /// `private-dep` is none during loading. This is equivalent to the scenario where the + /// command parameter is set to `public-dependency` + fn is_private_dep( + &self, + name: Symbol, + private_dep: Option, + dep_root: Option<&CratePaths>, + ) -> bool { + // Standard library crates are never private. + if STDLIB_STABLE_CRATES.contains(&name) { + tracing::info!("returning false for {name} is private"); + return false; + } + + let extern_private = self.sess.opts.externs.get(name.as_str()).map(|e| e.is_private_dep); + + // Any descendants of `std` should be private. These crates are usually not marked + // private in metadata, so we ignore that field. + if extern_private.is_none() + && let Some(dep) = dep_root + && STDLIB_STABLE_CRATES.contains(&dep.name) + { + return true; + } + + match (extern_private, private_dep) { + // Explicit non-private via `--extern`, explicit non-private from metadata, or + // unspecified with default to public. + (Some(false), _) | (_, Some(false)) | (None, None) => false, + // Marked private via `--extern priv:mycrate` or in metadata. + (Some(true) | None, Some(true) | None) => true, + } } fn register_crate( &mut self, host_lib: Option, - root: Option<&CratePaths>, + dep_root: Option<&CratePaths>, lib: Library, dep_kind: CrateDepKind, name: Symbol, @@ -414,7 +540,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { let Library { source, metadata } = lib; let crate_root = metadata.get_root(); let host_hash = host_lib.as_ref().map(|lib| lib.metadata.get_root().hash()); - let private_dep = self.is_private_dep(name.as_str(), private_dep); + let private_dep = self.is_private_dep(name, private_dep, dep_root); // Claim this crate number and cache it let feed = self.cstore.intern_stable_crate_id(&crate_root, self.tcx)?; @@ -430,14 +556,14 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { // Maintain a reference to the top most crate. // Stash paths for top-most crate locally if necessary. let crate_paths; - let root = if let Some(root) = root { - root + let dep_root = if let Some(dep_root) = dep_root { + dep_root } else { crate_paths = CratePaths::new(crate_root.name(), source.clone()); &crate_paths }; - let cnum_map = self.resolve_crate_deps(root, &crate_root, &metadata, cnum, dep_kind)?; + let cnum_map = self.resolve_crate_deps(dep_root, &crate_root, &metadata, cnum, dep_kind)?; let raw_proc_macros = if crate_root.is_proc_macro_crate() { let temp_root; @@ -559,23 +685,21 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { &'b mut self, name: Symbol, mut dep_kind: CrateDepKind, - dep: Option<(&'b CratePaths, &'b CrateDep)>, + dep_of: Option<(&'b CratePaths, &'b CrateDep)>, ) -> Result { info!("resolving crate `{}`", name); if !name.as_str().is_ascii() { return Err(CrateError::NonAsciiName(name)); } - let (root, hash, host_hash, extra_filename, path_kind, private_dep) = match dep { - Some((root, dep)) => ( - Some(root), - Some(dep.hash), - dep.host_hash, - Some(&dep.extra_filename[..]), - PathKind::Dependency, - Some(dep.is_private), - ), - None => (None, None, None, None, PathKind::Crate, None), - }; + + let dep_root = dep_of.map(|d| d.0); + let dep = dep_of.map(|d| d.1); + let hash = dep.map(|d| d.hash); + let host_hash = dep.map(|d| d.host_hash).flatten(); + let extra_filename = dep.map(|d| &d.extra_filename[..]); + let path_kind = if dep.is_some() { PathKind::Dependency } else { PathKind::Crate }; + let private_dep = dep.map(|d| d.is_private); + let result = if let Some(cnum) = self.existing_match(name, hash, path_kind) { (LoadResult::Previous(cnum), None) } else { @@ -599,7 +723,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { dep_kind = CrateDepKind::MacrosOnly; match self.load_proc_macro(&mut locator, path_kind, host_hash)? { Some(res) => res, - None => return Err(locator.into_error(root.cloned())), + None => return Err(locator.into_error(dep_root.cloned())), } } } @@ -612,7 +736,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { // not specified by `--extern` on command line parameters, it may be // `private-dependency` when `register_crate` is called for the first time. Then it must be updated to // `public-dependency` here. - let private_dep = self.is_private_dep(name.as_str(), private_dep); + let private_dep = self.is_private_dep(name, private_dep, dep_root); let data = self.cstore.get_crate_data_mut(cnum); if data.is_proc_macro_crate() { dep_kind = CrateDepKind::MacrosOnly; @@ -623,7 +747,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { } (LoadResult::Loaded(library), host_library) => { info!("register newly loaded library for `{}`", name); - self.register_crate(host_library, root, library, dep_kind, name, private_dep) + self.register_crate(host_library, dep_root, library, dep_kind, name, private_dep) } _ => panic!(), } @@ -663,16 +787,20 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { })) } - // Go through the crate metadata and load any crates that it references + /// Go through the crate metadata and load any crates that it references. fn resolve_crate_deps( &mut self, - root: &CratePaths, + dep_root: &CratePaths, crate_root: &CrateRoot, metadata: &MetadataBlob, krate: CrateNum, dep_kind: CrateDepKind, ) -> Result { - debug!("resolving deps of external crate"); + debug!( + "resolving deps of external crate `{}` with dep root `{}`", + crate_root.name(), + dep_root.name + ); if crate_root.is_proc_macro_crate() { return Ok(CrateNumMap::new()); } @@ -685,14 +813,17 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { crate_num_map.push(krate); for dep in deps { info!( - "resolving dep crate {} hash: `{}` extra filename: `{}`", - dep.name, dep.hash, dep.extra_filename + "resolving dep `{}`->`{}` hash: `{}` extra filename: `{}`", + crate_root.name(), + dep.name, + dep.hash, + dep.extra_filename ); let dep_kind = match dep_kind { CrateDepKind::MacrosOnly => CrateDepKind::MacrosOnly, _ => dep.kind, }; - let cnum = self.maybe_resolve_crate(dep.name, dep_kind, Some((root, &dep)))?; + let cnum = self.maybe_resolve_crate(dep.name, dep_kind, Some((dep_root, &dep)))?; crate_num_map.push(cnum); } @@ -867,7 +998,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { // First up we check for global allocators. Look at the crate graph here // and see what's a global allocator, including if we ourselves are a // global allocator. - #[cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] + #[allow(rustc::symbol_intern_string_literal)] let this_crate = Symbol::intern("this crate"); let mut global_allocator = self.cstore.has_global_allocator.then_some(this_crate); @@ -1038,12 +1169,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { // Make a point span rather than covering the whole file let span = krate.spans.inner_span.shrink_to_lo(); - self.sess.psess.buffer_lint( - lint::builtin::WASM_C_ABI, - span, - ast::CRATE_NODE_ID, - BuiltinLintDiag::WasmCAbi, - ); + self.sess.dcx().emit_err(errors::WasmCAbi { span }); } } @@ -1087,12 +1213,15 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { let cnum = self.resolve_crate(name, item.span, dep_kind)?; let path_len = definitions.def_path(def_id).data.len(); - self.cstore.update_extern_crate(cnum, ExternCrate { - src: ExternCrateSource::Extern(def_id.to_def_id()), - span: item.span, - path_len, - dependency_of: LOCAL_CRATE, - }); + self.cstore.update_extern_crate( + cnum, + ExternCrate { + src: ExternCrateSource::Extern(def_id.to_def_id()), + span: item.span, + path_len, + dependency_of: LOCAL_CRATE, + }, + ); Some(cnum) } _ => bug!(), @@ -1102,13 +1231,16 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { pub fn process_path_extern(&mut self, name: Symbol, span: Span) -> Option { let cnum = self.resolve_crate(name, span, CrateDepKind::Explicit)?; - self.cstore.update_extern_crate(cnum, ExternCrate { - src: ExternCrateSource::Path, - span, - // to have the least priority in `update_extern_crate` - path_len: usize::MAX, - dependency_of: LOCAL_CRATE, - }); + self.cstore.update_extern_crate( + cnum, + ExternCrate { + src: ExternCrateSource::Path, + span, + // to have the least priority in `update_extern_crate` + path_len: usize::MAX, + dependency_of: LOCAL_CRATE, + }, + ); Some(cnum) } diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index b6c5619ec18ae..a77f9bc623b5b 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -732,3 +732,35 @@ pub struct ImportNameTypeRaw { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(metadata_wasm_c_abi)] +pub(crate) struct WasmCAbi { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(metadata_incompatible_target_modifiers)] +#[help] +#[note] +#[help(metadata_incompatible_target_modifiers_help_fix)] +#[help(metadata_incompatible_target_modifiers_help_allow)] +pub struct IncompatibleTargetModifiers { + #[primary_span] + pub span: Span, + pub extern_crate: Symbol, + pub local_crate: Symbol, + pub flag_name: String, + pub flag_name_prefixed: String, + pub flag_local_value: String, + pub flag_extern_value: String, +} + +#[derive(Diagnostic)] +#[diag(metadata_unknown_target_modifier_unsafe_allowed)] +pub struct UnknownTargetModifierUnsafeAllowed { + #[primary_span] + pub span: Span, + pub flag_name: String, +} diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index b9ebf17af248f..2ddabeb49f70d 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -262,7 +262,7 @@ pub(crate) struct CrateLocator<'a> { #[derive(Clone)] pub(crate) struct CratePaths { - name: Symbol, + pub(crate) name: Symbol, source: CrateSource, } @@ -765,10 +765,10 @@ impl<'a> CrateLocator<'a> { self.extract_lib(rlibs, rmetas, dylibs).map(|opt| opt.map(|(_, lib)| lib)) } - pub(crate) fn into_error(self, root: Option) -> CrateError { + pub(crate) fn into_error(self, dep_root: Option) -> CrateError { CrateError::LocatorCombined(Box::new(CombinedLocatorError { crate_name: self.crate_name, - root, + dep_root, triple: self.tuple, dll_prefix: self.target.dll_prefix.to_string(), dll_suffix: self.target.dll_suffix.to_string(), @@ -914,7 +914,7 @@ struct CrateRejections { /// otherwise they are ignored. pub(crate) struct CombinedLocatorError { crate_name: Symbol, - root: Option, + dep_root: Option, triple: TargetTuple, dll_prefix: String, dll_suffix: String, @@ -987,7 +987,7 @@ impl CrateError { } CrateError::LocatorCombined(locator) => { let crate_name = locator.crate_name; - let add_info = match &locator.root { + let add_info = match &locator.dep_root { None => String::new(), Some(r) => format!(" which `{}` depends on", r.name), }; @@ -1012,7 +1012,7 @@ impl CrateError { path.display() )); } - if let Some(r) = locator.root { + if let Some(r) = locator.dep_root { for path in r.source.paths() { found_crates.push_str(&format!( "\ncrate `{}`: {}", diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index c2b5e318bda72..e8dcda875e67a 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -2,6 +2,7 @@ use std::iter::TrustedLen; use std::path::Path; +use std::sync::Arc; use std::{io, iter, mem}; pub(super) use cstore_impl::provide; @@ -11,7 +12,7 @@ use rustc_data_structures::captures::Captures; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::owned_slice::OwnedSlice; -use rustc_data_structures::sync::{Lock, Lrc, OnceLock}; +use rustc_data_structures::sync::{Lock, OnceLock}; use rustc_data_structures::unhash::UnhashMap; use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; use rustc_expand::proc_macro::{AttrProcMacro, BangProcMacro, DeriveProcMacro}; @@ -29,6 +30,7 @@ use rustc_middle::{bug, implement_ty_decoder}; use rustc_serialize::opaque::MemDecoder; use rustc_serialize::{Decodable, Decoder}; use rustc_session::Session; +use rustc_session::config::TargetModifier; use rustc_session::cstore::{CrateSource, ExternCrate}; use rustc_span::hygiene::HygieneDecodeContext; use rustc_span::{BytePos, DUMMY_SP, Pos, SpanData, SpanDecoder, SyntaxContext, kw}; @@ -73,6 +75,9 @@ impl MetadataBlob { /// own crate numbers. pub(crate) type CrateNumMap = IndexVec; +/// Target modifiers - abi or exploit mitigations flags +pub(crate) type TargetModifiers = Vec; + pub(crate) struct CrateMetadata { /// The primary crate data - binary metadata blob. blob: MetadataBlob, @@ -113,7 +118,7 @@ pub(crate) struct CrateMetadata { /// How to link (or not link) this crate to the currently compiled crate. dep_kind: CrateDepKind, /// Filesystem location of this crate. - source: Lrc, + source: Arc, /// Whether or not this crate should be consider a private dependency. /// Used by the 'exported_private_dependencies' lint, and for determining /// whether to emit suggestions that reference this crate. @@ -145,7 +150,7 @@ struct ImportedSourceFile { /// The end of this SourceFile within the source_map of its original crate original_end_pos: rustc_span::BytePos, /// The imported SourceFile's representation within the local source_map - translated_source_file: Lrc, + translated_source_file: Arc, } pub(super) struct DecodeContext<'a, 'tcx> { @@ -871,7 +876,7 @@ impl MetadataBlob { let def_kind = root.tables.def_kind.get(blob, item).unwrap(); let def_key = root.tables.def_keys.get(blob, item).unwrap().decode(blob); - #[cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] + #[allow(rustc::symbol_intern_string_literal)] let def_name = if item == CRATE_DEF_INDEX { kw::Crate } else { @@ -961,6 +966,13 @@ impl CrateRoot { ) -> impl ExactSizeIterator + Captures<'a> { self.crate_deps.decode(metadata) } + + pub(crate) fn decode_target_modifiers<'a>( + &self, + metadata: &'a MetadataBlob, + ) -> impl ExactSizeIterator + Captures<'a> { + self.target_modifiers.decode(metadata) + } } impl<'a> CrateMetadataRef<'a> { @@ -1855,7 +1867,7 @@ impl CrateMetadata { cnum_map, dependencies, dep_kind, - source: Lrc::new(source), + source: Arc::new(source), private_dep, host_hash, used: false, @@ -1883,6 +1895,10 @@ impl CrateMetadata { self.dependencies.push(cnum); } + pub(crate) fn target_modifiers(&self) -> TargetModifiers { + self.root.decode_target_modifiers(&self.blob).collect() + } + pub(crate) fn update_extern_crate(&mut self, new_extern_crate: ExternCrate) -> bool { let update = Some(new_extern_crate.rank()) > self.extern_crate.as_ref().map(ExternCrate::rank); diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 527f2f10205a6..776b081a4630f 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -1,8 +1,8 @@ use std::any::Any; use std::mem; +use std::sync::Arc; use rustc_attr_parsing::Deprecation; -use rustc_data_structures::sync::Lrc; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE}; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; @@ -165,7 +165,7 @@ macro_rules! provide_one { // doesn't need to do this (and can't, as it would cause a query cycle). use rustc_middle::dep_graph::dep_kinds; if dep_kinds::$name != dep_kinds::crate_hash && $tcx.dep_graph.is_fully_enabled() { - $tcx.ensure().crate_hash($def_id.krate); + $tcx.ensure_ok().crate_hash($def_id.krate); } let cdata = rustc_data_structures::sync::FreezeReadGuard::map(CStore::from_tcx($tcx), |c| { @@ -241,7 +241,7 @@ impl IntoArgs for (CrateNum, SimplifiedType) { provide! { tcx, def_id, other, cdata, explicit_item_bounds => { table_defaulted_array } - explicit_item_super_predicates => { table_defaulted_array } + explicit_item_self_bounds => { table_defaulted_array } explicit_predicates_of => { table } generics_of => { table } inferred_outlives_of => { table_defaulted_array } @@ -409,7 +409,7 @@ provide! { tcx, def_id, other, cdata, matches!(cdata.extern_crate, Some(extern_crate) if !extern_crate.is_direct()) } - used_crate_source => { Lrc::clone(&cdata.source) } + used_crate_source => { Arc::clone(&cdata.source) } debugger_visualizers => { cdata.get_debugger_visualizers() } exported_symbols => { @@ -548,7 +548,7 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { visible_parent_map }, - dependency_formats: |tcx, ()| Lrc::new(crate::dependency_format::calculate(tcx)), + dependency_formats: |tcx, ()| Arc::new(crate::dependency_format::calculate(tcx)), has_global_allocator: |tcx, LocalCrate| CStore::from_tcx(tcx).has_global_allocator(), has_alloc_error_handler: |tcx, LocalCrate| CStore::from_tcx(tcx).has_alloc_error_handler(), postorder_cnums: |tcx, ()| { @@ -689,7 +689,7 @@ fn provide_cstore_hooks(providers: &mut Providers) { providers.hooks.def_path_hash_to_def_id_extern = |tcx, hash, stable_crate_id| { // If this is a DefPathHash from an upstream crate, let the CrateStore map // it to a DefId. - let cstore = CStore::from_tcx(tcx.tcx); + let cstore = CStore::from_tcx(tcx); let cnum = *tcx .untracked() .stable_crate_ids @@ -702,11 +702,11 @@ fn provide_cstore_hooks(providers: &mut Providers) { }; providers.hooks.expn_hash_to_expn_id = |tcx, cnum, index_guess, hash| { - let cstore = CStore::from_tcx(tcx.tcx); + let cstore = CStore::from_tcx(tcx); cstore.get_crate_data(cnum).expn_hash_to_expn_id(tcx.sess, index_guess, hash) }; providers.hooks.import_source_files = |tcx, cnum| { - let cstore = CStore::from_tcx(tcx.tcx); + let cstore = CStore::from_tcx(tcx); let cdata = cstore.get_crate_data(cnum); for file_index in 0..cdata.root.source_map.size() { cdata.imported_source_file(file_index as u32, tcx.sess); diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index c538ab99fb54b..d8a7c32a299c2 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -3,12 +3,14 @@ use std::collections::hash_map::Entry; use std::fs::File; use std::io::{Read, Seek, Write}; use std::path::{Path, PathBuf}; +use std::sync::Arc; use rustc_ast::attr::AttributeExt; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::memmap::{Mmap, MmapMut}; -use rustc_data_structures::sync::{Lrc, join, par_for_each_in}; +use rustc_data_structures::sync::{join, par_for_each_in}; use rustc_data_structures::temp_dir::MaybeTempDir; +use rustc_data_structures::thousands::format_with_underscores; use rustc_feature::Features; use rustc_hir as hir; use rustc_hir::def_id::{CRATE_DEF_ID, CRATE_DEF_INDEX, LOCAL_CRATE, LocalDefId, LocalDefIdSet}; @@ -22,10 +24,9 @@ use rustc_middle::traits::specialization_graph; use rustc_middle::ty::codec::TyEncoder; use rustc_middle::ty::fast_reject::{self, TreatParams}; use rustc_middle::ty::{AssocItemContainer, SymbolName}; -use rustc_middle::util::common::to_readable_str; use rustc_middle::{bug, span_bug}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder, opaque}; -use rustc_session::config::{CrateType, OptLevel}; +use rustc_session::config::{CrateType, OptLevel, TargetModifier}; use rustc_span::hygiene::HygieneEncodeContext; use rustc_span::{ ExternalSource, FileName, SourceFile, SpanData, SpanEncoder, StableSourceFileId, SyntaxContext, @@ -52,7 +53,7 @@ pub(super) struct EncodeContext<'a, 'tcx> { // This is used to speed up Span encoding. // The `usize` is an index into the `MonotonicVec` // that stores the `SourceFile` - source_file_cache: (Lrc, usize), + source_file_cache: (Arc, usize), // The indices (into the `SourceMap`'s `MonotonicVec`) // of all of the `SourceFiles` that we need to serialize. // When we serialize a `Span`, we insert the index of its @@ -278,7 +279,7 @@ impl<'a, 'tcx> Encodable> for SpanData { let source_map = s.tcx.sess.source_map(); let source_file_index = source_map.lookup_source_file_idx(self.lo); s.source_file_cache = - (Lrc::clone(&source_map.files()[source_file_index]), source_file_index); + (Arc::clone(&source_map.files()[source_file_index]), source_file_index); } let (ref source_file, source_file_index) = s.source_file_cache; debug_assert!(source_file.contains(self.lo)); @@ -692,6 +693,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // Encode source_map. This needs to be done last, because encoding `Span`s tells us which // `SourceFiles` we actually need to encode. let source_map = stat!("source-map", || self.encode_source_map()); + let target_modifiers = stat!("target-modifiers", || self.encode_target_modifiers()); let root = stat!("final", || { let attrs = tcx.hir().krate_attrs(); @@ -735,6 +737,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { native_libraries, foreign_modules, source_map, + target_modifiers, traits, impls, incoherent_impls, @@ -782,7 +785,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { "{} {:<23}{:>10} ({:4.1}%)", prefix, label, - to_readable_str(size), + format_with_underscores(size), perc(size) ); } @@ -791,7 +794,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { "{} {:<23}{:>10} (of which {:.1}% are zero bytes)", prefix, "Total", - to_readable_str(total_bytes), + format_with_underscores(total_bytes), perc(zero_bytes) ); eprintln!("{prefix}"); @@ -1554,7 +1557,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } if let DefKind::OpaqueTy = def_kind { self.encode_explicit_item_bounds(def_id); - self.encode_explicit_item_super_predicates(def_id); + self.encode_explicit_item_self_bounds(def_id); record!(self.tables.opaque_ty_origin[def_id] <- self.tcx.opaque_ty_origin(def_id)); self.encode_precise_capturing_args(def_id); if tcx.is_conditionally_const(def_id) { @@ -1667,10 +1670,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { record_defaulted_array!(self.tables.explicit_item_bounds[def_id] <- bounds); } - fn encode_explicit_item_super_predicates(&mut self, def_id: DefId) { - debug!("EncodeContext::encode_explicit_item_super_predicates({:?})", def_id); - let bounds = self.tcx.explicit_item_super_predicates(def_id).skip_binder(); - record_defaulted_array!(self.tables.explicit_item_super_predicates[def_id] <- bounds); + fn encode_explicit_item_self_bounds(&mut self, def_id: DefId) { + debug!("EncodeContext::encode_explicit_item_self_bounds({:?})", def_id); + let bounds = self.tcx.explicit_item_self_bounds(def_id).skip_binder(); + record_defaulted_array!(self.tables.explicit_item_self_bounds[def_id] <- bounds); } #[instrument(level = "debug", skip(self))] @@ -1685,7 +1688,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { AssocItemContainer::Trait => { if let ty::AssocKind::Type = item.kind { self.encode_explicit_item_bounds(def_id); - self.encode_explicit_item_super_predicates(def_id); + self.encode_explicit_item_self_bounds(def_id); if tcx.is_conditionally_const(def_id) { record_defaulted_array!(self.tables.explicit_implied_const_bounds[def_id] <- self.tcx.explicit_implied_const_bounds(def_id).skip_binder()); @@ -2009,6 +2012,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.lazy_array(deps.iter().map(|(_, dep)| dep)) } + fn encode_target_modifiers(&mut self) -> LazyArray { + empty_proc_macro!(self); + let tcx = self.tcx; + self.lazy_array(tcx.sess.opts.gather_target_modifiers()) + } + fn encode_lib_features(&mut self) -> LazyArray<(Symbol, FeatureStability)> { empty_proc_macro!(self); let tcx = self.tcx; @@ -2191,13 +2200,13 @@ fn prefetch_mir(tcx: TyCtxt<'_>) { let (encode_const, encode_opt) = should_encode_mir(tcx, reachable_set, def_id); if encode_const { - tcx.ensure_with_value().mir_for_ctfe(def_id); + tcx.ensure_done().mir_for_ctfe(def_id); } if encode_opt { - tcx.ensure_with_value().optimized_mir(def_id); + tcx.ensure_done().optimized_mir(def_id); } if encode_opt || encode_const { - tcx.ensure_with_value().promoted_mir(def_id); + tcx.ensure_done().promoted_mir(def_id); } }) } @@ -2263,10 +2272,7 @@ impl Decodable for EncodedMetadata { let len = d.read_usize(); let mmap = if len > 0 { let mut mmap = MmapMut::map_anon(len).unwrap(); - for _ in 0..len { - (&mut mmap[..]).write_all(&[d.read_u8()]).unwrap(); - } - mmap.flush().unwrap(); + mmap.copy_from_slice(d.read_raw_bytes(len)); Some(mmap.make_read_only().unwrap()) } else { None @@ -2298,7 +2304,7 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path) { encoder.emit_raw_bytes(&0u64.to_le_bytes()); let source_map_files = tcx.sess.source_map().files(); - let source_file_cache = (Lrc::clone(&source_map_files[0]), 0); + let source_file_cache = (Arc::clone(&source_map_files[0]), 0); let required_source_files = Some(FxIndexSet::default()); drop(source_map_files); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 4fe8f73efd601..7b34e605c5307 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -1,7 +1,7 @@ use std::marker::PhantomData; use std::num::NonZero; -pub(crate) use decoder::{CrateMetadata, CrateNumMap, MetadataBlob}; +pub(crate) use decoder::{CrateMetadata, CrateNumMap, MetadataBlob, TargetModifiers}; use decoder::{DecodeContext, Metadata}; use def_path_hash_map::DefPathHashMapRef; use encoder::EncodeContext; @@ -15,7 +15,7 @@ use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIndex, DefPathHash, Stable use rustc_hir::definitions::DefKey; use rustc_hir::lang_items::LangItem; use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_macros::{ Decodable, Encodable, MetadataDecodable, MetadataEncodable, TyDecodable, TyEncodable, }; @@ -32,7 +32,7 @@ use rustc_middle::ty::{ use rustc_middle::util::Providers; use rustc_middle::{mir, trivially_parameterized_over_tcx}; use rustc_serialize::opaque::FileEncoder; -use rustc_session::config::SymbolManglingVersion; +use rustc_session::config::{SymbolManglingVersion, TargetModifier}; use rustc_session::cstore::{CrateDepKind, ForeignModule, LinkagePreference, NativeLib}; use rustc_span::edition::Edition; use rustc_span::hygiene::{ExpnIndex, MacroKind, SyntaxContextData}; @@ -282,6 +282,7 @@ pub(crate) struct CrateRoot { def_path_hash_map: LazyValue>, source_map: LazyTable>>, + target_modifiers: LazyArray, compiler_builtins: bool, needs_allocator: bool, @@ -386,7 +387,7 @@ define_tables! { // corresponding DefPathHash. def_path_hashes: Table, explicit_item_bounds: Table, Span)>>, - explicit_item_super_predicates: Table, Span)>>, + explicit_item_self_bounds: Table, Span)>>, inferred_outlives_of: Table, Span)>>, explicit_super_predicates_of: Table, Span)>>, explicit_implied_predicates_of: Table, Span)>>, @@ -450,7 +451,7 @@ define_tables! { trait_item_def_id: Table, expn_that_defined: Table>, default_fields: Table>, - params_in_repr: Table>>, + params_in_repr: Table>>, repr_options: Table>, // `def_keys` and `def_path_hashes` represent a lazy version of a // `DefPathTable`. This allows us to avoid deserializing an entire diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index e64500f812a1d..de722e62043cd 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -6,9 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" -derive-where = "1.2.7" either = "1.5.0" -field-offset = "0.3.5" gsgdt = "0.1.2" polonius-engine = "0.13.0" rustc-rayon-core = { version = "0.5.0" } diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl index 5dd85978f007f..09c16222be19c 100644 --- a/compiler/rustc_middle/messages.ftl +++ b/compiler/rustc_middle/messages.ftl @@ -1,6 +1,3 @@ -middle_adjust_for_foreign_abi_error = - target architecture {$arch} does not support `extern {$abi}` ABI - middle_assert_async_resume_after_panic = `async fn` resumed after panicking middle_assert_async_resume_after_return = `async fn` resumed after completion @@ -17,6 +14,9 @@ middle_assert_gen_resume_after_panic = `gen` fn or block cannot be further itera middle_assert_misaligned_ptr_deref = misaligned pointer dereference: address must be a multiple of {$required} but is {$found} +middle_assert_null_ptr_deref = + null pointer dereference occurred + middle_assert_op_overflow = attempt to compute `{$left} {$op} {$right}`, which would overflow @@ -32,6 +32,8 @@ middle_assert_shl_overflow = middle_assert_shr_overflow = attempt to shift right by `{$val}`, which would overflow +middle_autodiff_unsafe_inner_const_ref = reading from a `Duplicated` const {$ty} is unsafe + middle_bounds_check = index out of bounds: the length is {$len} but the index is {$index} @@ -100,11 +102,16 @@ middle_strict_coherence_needs_negative_coherence = to use `strict_coherence` on this trait, the `with_negative_coherence` feature must be enabled .label = due to this attribute +middle_too_generic = `{$ty}` does not have a fixed size + middle_type_length_limit = reached the type-length limit while instantiating `{$shrunk}` middle_unknown_layout = the type `{$ty}` has an unknown layout +middle_unsupported_union = we don't support unions yet: '{$ty_name}' + middle_values_too_big = values of the type `{$ty}` are too big for the target architecture + middle_written_to_path = the full type name has been written to '{$path}' diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 750531b638e4d..aef56ea46e957 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -31,7 +31,7 @@ macro_rules! arena_types { [decode] borrowck_result: rustc_middle::mir::BorrowCheckResult<'tcx>, [] resolver: rustc_data_structures::steal::Steal<( rustc_middle::ty::ResolverAstLowering, - rustc_data_structures::sync::Lrc, + std::sync::Arc, )>, [] crate_for_resolver: rustc_data_structures::steal::Steal<(rustc_ast::Crate, rustc_ast::AttrVec)>, [] resolutions: rustc_middle::ty::ResolverGlobalCtxt, @@ -87,8 +87,10 @@ macro_rules! arena_types { [] codegen_unit: rustc_middle::mir::mono::CodegenUnit<'tcx>, [decode] attribute: rustc_hir::Attribute, [] name_set: rustc_data_structures::unord::UnordSet, + [] autodiff_item: rustc_ast::expand::autodiff_attrs::AutoDiffItem, [] ordered_name_set: rustc_data_structures::fx::FxIndexSet, [] pats: rustc_middle::ty::PatternKind<'tcx>, + [] valtree: rustc_middle::ty::ValTreeKind<'tcx>, // Note that this deliberately duplicates items in the `rustc_hir::arena`, // since we need to allocate this type on both the `rustc_hir` arena diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index fcfc31575f8aa..7525aeb922729 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -1,61 +1,3 @@ -//! Nodes in the dependency graph. -//! -//! A node in the [dependency graph] is represented by a [`DepNode`]. -//! A `DepNode` consists of a [`DepKind`] (which -//! specifies the kind of thing it represents, like a piece of HIR, MIR, etc.) -//! and a [`Fingerprint`], a 128-bit hash value, the exact meaning of which -//! depends on the node's `DepKind`. Together, the kind and the fingerprint -//! fully identify a dependency node, even across multiple compilation sessions. -//! In other words, the value of the fingerprint does not depend on anything -//! that is specific to a given compilation session, like an unpredictable -//! interning key (e.g., `NodeId`, `DefId`, `Symbol`) or the numeric value of a -//! pointer. The concept behind this could be compared to how git commit hashes -//! uniquely identify a given commit. The fingerprinting approach has -//! a few advantages: -//! -//! * A `DepNode` can simply be serialized to disk and loaded in another session -//! without the need to do any "rebasing" (like we have to do for Spans and -//! NodeIds) or "retracing" (like we had to do for `DefId` in earlier -//! implementations of the dependency graph). -//! * A `Fingerprint` is just a bunch of bits, which allows `DepNode` to -//! implement `Copy`, `Sync`, `Send`, `Freeze`, etc. -//! * Since we just have a bit pattern, `DepNode` can be mapped from disk into -//! memory without any post-processing (e.g., "abomination-style" pointer -//! reconstruction). -//! * Because a `DepNode` is self-contained, we can instantiate `DepNodes` that -//! refer to things that do not exist anymore. In previous implementations -//! `DepNode` contained a `DefId`. A `DepNode` referring to something that -//! had been removed between the previous and the current compilation session -//! could not be instantiated because the current compilation session -//! contained no `DefId` for thing that had been removed. -//! -//! `DepNode` definition happens in the `define_dep_nodes!()` macro. This macro -//! defines the `DepKind` enum. Each `DepKind` has its own parameters that are -//! needed at runtime in order to construct a valid `DepNode` fingerprint. -//! However, only `CompileCodegenUnit` and `CompileMonoItem` are constructed -//! explicitly (with `make_compile_codegen_unit` cq `make_compile_mono_item`). -//! -//! Because the macro sees what parameters a given `DepKind` requires, it can -//! "infer" some properties for each kind of `DepNode`: -//! -//! * Whether a `DepNode` of a given kind has any parameters at all. Some -//! `DepNode`s could represent global concepts with only one value. -//! * Whether it is possible, in principle, to reconstruct a query key from a -//! given `DepNode`. Many `DepKind`s only require a single `DefId` parameter, -//! in which case it is possible to map the node's fingerprint back to the -//! `DefId` it was computed from. In other cases, too much information gets -//! lost during fingerprint computation. -//! -//! `make_compile_codegen_unit` and `make_compile_mono_items`, together with -//! `DepNode::new()`, ensures that only valid `DepNode` instances can be -//! constructed. For example, the API does not allow for constructing -//! parameterless `DepNode`s with anything other than a zeroed out fingerprint. -//! More generally speaking, it relieves the user of the `DepNode` API of -//! having to know how to compute the expected fingerprint for a given set of -//! node parameters. -//! -//! [dependency graph]: https://rustc-dev-guide.rust-lang.org/query.html - use rustc_data_structures::fingerprint::Fingerprint; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId, ModDefId}; use rustc_hir::definitions::DefPathHash; @@ -158,26 +100,14 @@ pub(crate) fn make_compile_mono_item<'tcx>( } pub trait DepNodeExt: Sized { - /// Extracts the DefId corresponding to this DepNode. This will work - /// if two conditions are met: - /// - /// 1. The Fingerprint of the DepNode actually is a DefPathHash, and - /// 2. the item that the DefPath refers to exists in the current tcx. - /// - /// Condition (1) is determined by the DepKind variant of the - /// DepNode. Condition (2) might not be fulfilled if a DepNode - /// refers to something from the previous compilation session that - /// has been removed. fn extract_def_id(&self, tcx: TyCtxt<'_>) -> Option; - /// Used in testing fn from_label_string( tcx: TyCtxt<'_>, label: &str, def_path_hash: DefPathHash, ) -> Result; - /// Used in testing fn has_label_string(label: &str) -> bool; } @@ -399,52 +329,46 @@ impl<'tcx> DepNodeParams> for HirId { } } -macro_rules! impl_for_typed_def_id { - ($Name:ident, $LocalName:ident) => { - impl<'tcx> DepNodeParams> for $Name { - #[inline(always)] - fn fingerprint_style() -> FingerprintStyle { - FingerprintStyle::DefPathHash - } +impl<'tcx> DepNodeParams> for ModDefId { + #[inline(always)] + fn fingerprint_style() -> FingerprintStyle { + FingerprintStyle::DefPathHash + } - #[inline(always)] - fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { - self.to_def_id().to_fingerprint(tcx) - } + #[inline(always)] + fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { + self.to_def_id().to_fingerprint(tcx) + } - #[inline(always)] - fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { - self.to_def_id().to_debug_str(tcx) - } + #[inline(always)] + fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { + self.to_def_id().to_debug_str(tcx) + } - #[inline(always)] - fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { - DefId::recover(tcx, dep_node).map($Name::new_unchecked) - } - } + #[inline(always)] + fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { + DefId::recover(tcx, dep_node).map(ModDefId::new_unchecked) + } +} - impl<'tcx> DepNodeParams> for $LocalName { - #[inline(always)] - fn fingerprint_style() -> FingerprintStyle { - FingerprintStyle::DefPathHash - } +impl<'tcx> DepNodeParams> for LocalModDefId { + #[inline(always)] + fn fingerprint_style() -> FingerprintStyle { + FingerprintStyle::DefPathHash + } - #[inline(always)] - fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { - self.to_def_id().to_fingerprint(tcx) - } + #[inline(always)] + fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { + self.to_def_id().to_fingerprint(tcx) + } - #[inline(always)] - fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { - self.to_def_id().to_debug_str(tcx) - } + #[inline(always)] + fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String { + self.to_def_id().to_debug_str(tcx) + } - #[inline(always)] - fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { - LocalDefId::recover(tcx, dep_node).map($LocalName::new_unchecked) - } - } - }; + #[inline(always)] + fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { + LocalDefId::recover(tcx, dep_node).map(LocalModDefId::new_unchecked) + } } - -impl_for_typed_def_id! { ModDefId, LocalModDefId } diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index 6300d856393c6..91b18295b43b3 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -11,7 +11,7 @@ use crate::ty::Ty; #[derive(Diagnostic)] #[diag(middle_drop_check_overflow, code = E0320)] #[note] -pub struct DropCheckOverflow<'tcx> { +pub(crate) struct DropCheckOverflow<'tcx> { #[primary_span] pub span: Span, pub ty: Ty<'tcx>, @@ -20,14 +20,14 @@ pub struct DropCheckOverflow<'tcx> { #[derive(Diagnostic)] #[diag(middle_failed_writing_file)] -pub struct FailedWritingFile<'a> { +pub(crate) struct FailedWritingFile<'a> { pub path: &'a Path, pub error: io::Error, } #[derive(Diagnostic)] #[diag(middle_opaque_hidden_type_mismatch)] -pub struct OpaqueHiddenTypeMismatch<'tcx> { +pub(crate) struct OpaqueHiddenTypeMismatch<'tcx> { pub self_ty: Ty<'tcx>, pub other_ty: Ty<'tcx>, #[primary_span] @@ -37,6 +37,22 @@ pub struct OpaqueHiddenTypeMismatch<'tcx> { pub sub: TypeMismatchReason, } +// FIXME(autodiff): I should get used somewhere +#[derive(Diagnostic)] +#[diag(middle_unsupported_union)] +pub struct UnsupportedUnion { + pub ty_name: String, +} + +// FIXME(autodiff): I should get used somewhere +#[derive(Diagnostic)] +#[diag(middle_autodiff_unsafe_inner_const_ref)] +pub struct AutodiffUnsafeInnerConstRef { + #[primary_span] + pub span: Span, + pub ty: String, +} + #[derive(Subdiagnostic)] pub enum TypeMismatchReason { #[label(middle_conflict_types)] @@ -53,7 +69,7 @@ pub enum TypeMismatchReason { #[derive(Diagnostic)] #[diag(middle_limit_invalid)] -pub struct LimitInvalid<'a> { +pub(crate) struct LimitInvalid<'a> { #[primary_span] pub span: Span, #[label] @@ -64,14 +80,14 @@ pub struct LimitInvalid<'a> { #[derive(Diagnostic)] #[diag(middle_recursion_limit_reached)] #[help] -pub struct RecursionLimitReached<'tcx> { +pub(crate) struct RecursionLimitReached<'tcx> { pub ty: Ty<'tcx>, pub suggested_limit: rustc_session::Limit, } #[derive(Diagnostic)] #[diag(middle_const_eval_non_int)] -pub struct ConstEvalNonIntError { +pub(crate) struct ConstEvalNonIntError { #[primary_span] pub span: Span, } @@ -129,6 +145,9 @@ pub enum LayoutError<'tcx> { #[diag(middle_unknown_layout)] Unknown { ty: Ty<'tcx> }, + #[diag(middle_too_generic)] + TooGeneric { ty: Ty<'tcx> }, + #[diag(middle_values_too_big)] Overflow { ty: Ty<'tcx> }, @@ -142,27 +161,17 @@ pub enum LayoutError<'tcx> { ReferencesError, } -#[derive(Diagnostic)] -#[diag(middle_adjust_for_foreign_abi_error)] -pub struct UnsupportedFnAbi { - pub arch: Symbol, - pub abi: &'static str, -} - #[derive(Diagnostic)] #[diag(middle_erroneous_constant)] -pub struct ErroneousConstant { +pub(crate) struct ErroneousConstant { #[primary_span] pub span: Span, } -/// Used by `rustc_const_eval` -pub use crate::fluent_generated::middle_adjust_for_foreign_abi_error; - #[derive(Diagnostic)] #[diag(middle_type_length_limit)] #[help(middle_consider_type_length_limit)] -pub struct TypeLengthLimit { +pub(crate) struct TypeLengthLimit { #[primary_span] pub span: Span, pub shrunk: String, diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 308078ddf87d9..83c7bb0f52f4a 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -10,11 +10,10 @@ use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; use rustc_hir::intravisit::Visitor; use rustc_hir::*; use rustc_hir_pretty as pprust_hir; -use rustc_middle::hir::nested_filter; use rustc_span::def_id::StableCrateId; -use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; +use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym, with_metavar_spans}; -use crate::hir::ModuleItems; +use crate::hir::{ModuleItems, nested_filter}; use crate::middle::debugger_visualizer::DebuggerVisualizerFile; use crate::query::LocalCrate; use crate::ty::TyCtxt; @@ -634,7 +633,7 @@ impl<'hir> Map<'hir> { for (hir_id, node) in self.parent_iter(hir_id) { if let Node::Item(Item { kind: - ItemKind::Fn(..) + ItemKind::Fn { .. } | ItemKind::Const(..) | ItemKind::Static(..) | ItemKind::Mod(..) @@ -823,7 +822,7 @@ impl<'hir> Map<'hir> { let span = match self.tcx.hir_node(hir_id) { // Function-like. - Node::Item(Item { kind: ItemKind::Fn(sig, ..), span: outer_span, .. }) + Node::Item(Item { kind: ItemKind::Fn { sig, .. }, span: outer_span, .. }) | Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(sig, ..), span: outer_span, @@ -937,7 +936,9 @@ impl<'hir> Map<'hir> { Node::TraitRef(tr) => tr.path.span, Node::OpaqueTy(op) => op.span, Node::Pat(pat) => pat.span, + Node::TyPat(pat) => pat.span, Node::PatField(field) => field.span, + Node::PatExpr(lit) => lit.span, Node::Arm(arm) => arm.span, Node::Block(block) => block.span, Node::Ctor(..) => self.span_with_body(self.tcx.parent_hir_id(hir_id)), @@ -1116,6 +1117,9 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, _: LocalCrate) -> Svh { // the fly in the resolver, storing only their accumulated hash in `ResolverGlobalCtxt`, // and combining it with other hashes here. resolutions.visibilities_for_hashing.hash_stable(&mut hcx, &mut stable_hasher); + with_metavar_spans(|mspans| { + mspans.freeze_and_get_read_spans().hash_stable(&mut hcx, &mut stable_hasher); + }); stable_hasher.finish() }); @@ -1149,7 +1153,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { ItemKind::Use(..) => "use", ItemKind::Static(..) => "static", ItemKind::Const(..) => "const", - ItemKind::Fn(..) => "fn", + ItemKind::Fn { .. } => "fn", ItemKind::Macro(..) => "macro", ItemKind::Mod(..) => "mod", ItemKind::ForeignMod { .. } => "foreign mod", @@ -1208,7 +1212,9 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { Node::TraitRef(_) => node_str("trait ref"), Node::OpaqueTy(_) => node_str("opaque type"), Node::Pat(_) => node_str("pat"), + Node::TyPat(_) => node_str("pat ty"), Node::PatField(_) => node_str("pattern field"), + Node::PatExpr(_) => node_str("pattern literal"), Node::Param(_) => node_str("param"), Node::Arm(_) => node_str("arm"), Node::Block(_) => node_str("block"), @@ -1244,6 +1250,7 @@ pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalModDefId) -> Mod foreign_items, body_owners, opaques, + nested_bodies, .. } = collector; ModuleItems { @@ -1254,6 +1261,7 @@ pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalModDefId) -> Mod foreign_items: foreign_items.into_boxed_slice(), body_owners: body_owners.into_boxed_slice(), opaques: opaques.into_boxed_slice(), + nested_bodies: nested_bodies.into_boxed_slice(), } } @@ -1274,6 +1282,7 @@ pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems { foreign_items, body_owners, opaques, + nested_bodies, .. } = collector; @@ -1285,6 +1294,7 @@ pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems { foreign_items: foreign_items.into_boxed_slice(), body_owners: body_owners.into_boxed_slice(), opaques: opaques.into_boxed_slice(), + nested_bodies: nested_bodies.into_boxed_slice(), } } @@ -1300,6 +1310,7 @@ struct ItemCollector<'tcx> { foreign_items: Vec, body_owners: Vec, opaques: Vec, + nested_bodies: Vec, } impl<'tcx> ItemCollector<'tcx> { @@ -1314,6 +1325,7 @@ impl<'tcx> ItemCollector<'tcx> { foreign_items: Vec::default(), body_owners: Vec::default(), opaques: Vec::default(), + nested_bodies: Vec::default(), } } } @@ -1356,6 +1368,7 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> { fn visit_inline_const(&mut self, c: &'hir ConstBlock) { self.body_owners.push(c.def_id); + self.nested_bodies.push(c.def_id); intravisit::walk_inline_const(self, c) } @@ -1367,6 +1380,7 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> { fn visit_expr(&mut self, ex: &'hir Expr<'hir>) { if let ExprKind::Closure(closure) = ex.kind { self.body_owners.push(closure.def_id); + self.nested_bodies.push(closure.def_id); } intravisit::walk_expr(self, ex) } diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index ffefd81cd08e4..0d2acf96d08f1 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -30,6 +30,7 @@ pub struct ModuleItems { foreign_items: Box<[ForeignItemId]>, opaques: Box<[LocalDefId]>, body_owners: Box<[LocalDefId]>, + nested_bodies: Box<[LocalDefId]>, } impl ModuleItems { @@ -70,6 +71,10 @@ impl ModuleItems { self.opaques.iter().copied() } + pub fn nested_bodies(&self) -> impl Iterator + '_ { + self.nested_bodies.iter().copied() + } + pub fn definitions(&self) -> impl Iterator + '_ { self.owners().map(|id| id.def_id) } diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index 92fa64b0987ff..c5ce6efcb817e 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -1,16 +1,14 @@ -//! "Hooks" provide a way for `tcx` functionality to be provided by some downstream crate without -//! everything in rustc having to depend on that crate. This is somewhat similar to queries, but -//! queries come with a lot of machinery for caching and incremental compilation, whereas hooks are -//! just plain function pointers without any of the query magic. +//! "Hooks" let you write `tcx` methods in downstream crates and call them in this crate, reducing +//! the amount of code that needs to be in this crate (which is already very big). This is somewhat +//! similar to queries, but queries come with a lot of machinery for caching and incremental +//! compilation, whereas hooks are just plain function pointers without any of the query magic. use rustc_hir::def_id::{DefId, DefPathHash}; use rustc_session::StableCrateId; use rustc_span::def_id::{CrateNum, LocalDefId}; -use rustc_span::{DUMMY_SP, ExpnHash, ExpnId}; -use tracing::instrument; +use rustc_span::{ExpnHash, ExpnId}; use crate::mir; -use crate::query::TyCtxtAt; use crate::ty::{Ty, TyCtxt}; macro_rules! declare_hooks { @@ -22,26 +20,14 @@ macro_rules! declare_hooks { #[inline(always)] pub fn $name(self, $($arg: $K,)*) -> $V { - self.at(DUMMY_SP).$name($($arg,)*) - } - )* - } - - impl<'tcx> TyCtxtAt<'tcx> { - $( - $(#[$attr])* - #[inline(always)] - #[instrument(level = "debug", skip(self), ret)] - pub fn $name(self, $($arg: $K,)*) -> $V - { - (self.tcx.hooks.$name)(self, $($arg,)*) + (self.hooks.$name)(self, $($arg,)*) } )* } pub struct Providers { $(pub $name: for<'tcx> fn( - TyCtxtAt<'tcx>, + TyCtxt<'tcx>, $($arg: $K,)* ) -> $V,)* } @@ -75,12 +61,6 @@ declare_hooks! { /// (Eligible functions might nevertheless be skipped for other reasons.) hook is_eligible_for_coverage(key: LocalDefId) -> bool; - /// Create the MIR for a given `DefId` - this includes - /// unreachable code. - /// You do not want to call this yourself, instead use the cached version - /// via `mir_built` - hook build_mir(key: LocalDefId) -> mir::Body<'tcx>; - /// Imports all `SourceFile`s from the given crate into the current session. /// This normally happens automatically when we decode a `Span` from /// that crate's metadata - however, the incr comp cache needs @@ -99,14 +79,11 @@ declare_hooks! { /// Will fetch a DefId from a DefPathHash for a foreign crate. hook def_path_hash_to_def_id_extern(hash: DefPathHash, stable_crate_id: StableCrateId) -> DefId; - /// Create a THIR tree for debugging. - hook thir_tree(key: LocalDefId) -> String; - - /// Create a list-like THIR representation for debugging. - hook thir_flat(key: LocalDefId) -> String; - /// Returns `true` if we should codegen an instance in the local crate, or returns `false` if we /// can just link to the upstream crate and therefore don't need a mono item. + /// + /// Note: this hook isn't called within `rustc_middle` but #127779 suggests it's a hook instead + /// of a normal function because external tools might want to override it. hook should_codegen_locally(instance: crate::ty::Instance<'tcx>) -> bool; hook alloc_self_profile_query_strings() -> (); @@ -121,6 +98,10 @@ declare_hooks! { hook save_dep_graph() -> (); hook query_key_hash_verify_all() -> (); + + /// Ensure the given scalar is valid for the given type. + /// This checks non-recursive runtime validity. + hook validate_scalar_in_layout(scalar: crate::ty::ScalarInt, ty: Ty<'tcx>) -> bool; } #[cold] diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 0f408375e05f3..3cd148cd44278 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -142,10 +142,6 @@ impl<'tcx, R> QueryResponse<'tcx, R> { pub type QueryOutlivesConstraint<'tcx> = (ty::OutlivesPredicate<'tcx, GenericArg<'tcx>>, ConstraintCategory<'tcx>); -TrivialTypeTraversalImpls! { - crate::infer::canonical::Certainty, -} - #[derive(Default)] pub struct CanonicalParamEnvCache<'tcx> { map: Lock< diff --git a/compiler/rustc_middle/src/infer/mod.rs b/compiler/rustc_middle/src/infer/mod.rs index 3dfcf90cb9356..73e8971c3bdea 100644 --- a/compiler/rustc_middle/src/infer/mod.rs +++ b/compiler/rustc_middle/src/infer/mod.rs @@ -1,2 +1 @@ pub mod canonical; -pub mod unify_key; diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 04a06ba7464cb..95128a5d903bb 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -1,10 +1,11 @@ //! The "main crate" of the Rust compiler. This crate contains common //! type definitions that are used by the other crates in the rustc -//! "family". Some prominent examples (note that each of these modules -//! has their own README with further details). +//! "family". The following are some prominent examples. //! //! - **HIR.** The "high-level (H) intermediate representation (IR)" is //! defined in the [`hir`] module. +//! - **THIR.** The "typed high-level (H) intermediate representation (IR)" +//! is defined in the [`thir`] module. //! - **MIR.** The "mid-level (M) intermediate representation (IR)" is //! defined in the [`mir`] module. This module contains only the //! *definition* of the MIR; the passes that transform and operate @@ -28,6 +29,7 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::potential_query_instability)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(bootstrap, feature(trait_upcasting))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(allocator_api)] @@ -37,9 +39,9 @@ #![feature(box_as_ptr)] #![feature(box_patterns)] #![feature(closure_track_caller)] -#![feature(const_type_name)] #![feature(core_intrinsics)] #![feature(coroutines)] +#![feature(debug_closure_helpers)] #![feature(decl_macro)] #![feature(discriminant_kind)] #![feature(extern_types)] @@ -49,14 +51,12 @@ #![feature(intra_doc_pointers)] #![feature(iter_from_coroutine)] #![feature(let_chains)] -#![feature(macro_metavar_expr)] #![feature(min_specialization)] #![feature(negative_impls)] #![feature(never_type)] #![feature(ptr_alignment_type)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] -#![feature(trait_upcasting)] #![feature(trusted_len)] #![feature(try_blocks)] #![feature(try_trait_v2)] diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 620d9f1c35790..ab711aca573e5 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -4,18 +4,17 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sorted_map::SortedMap; use rustc_errors::{Diag, MultiSpan}; use rustc_hir::{HirId, ItemLocalId}; -use rustc_macros::HashStable; +use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_session::Session; use rustc_session::lint::builtin::{self, FORBIDDEN_LINT_GROUPS}; use rustc_session::lint::{FutureIncompatibilityReason, Level, Lint, LintExpectationId, LintId}; -use rustc_span::hygiene::{ExpnKind, MacroKind}; -use rustc_span::{DUMMY_SP, DesugaringKind, Span, Symbol, kw}; +use rustc_span::{DUMMY_SP, Span, Symbol, kw}; use tracing::instrument; use crate::ty::TyCtxt; /// How a lint level was set. -#[derive(Clone, Copy, PartialEq, Eq, HashStable, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, HashStable, Debug)] pub enum LintLevelSource { /// Lint is at the default level as declared in rustc. Default, @@ -173,7 +172,7 @@ impl TyCtxt<'_> { /// This struct represents a lint expectation and holds all required information /// to emit the `unfulfilled_lint_expectations` lint if it is unfulfilled after /// the `LateLintPass` has completed. -#[derive(Clone, Debug, HashStable)] +#[derive(Clone, Debug, Encodable, Decodable, HashStable)] pub struct LintExpectation { /// The reason for this expectation that can optionally be added as part of /// the attribute. It will be displayed as part of the lint message. @@ -201,7 +200,7 @@ impl LintExpectation { } } -pub fn explain_lint_level_source( +fn explain_lint_level_source( lint: &'static Lint, level: Level, src: LintLevelSource, @@ -325,7 +324,7 @@ pub fn lint_level( // If this code originates in a foreign macro, aka something that this crate // did not itself author, then it's likely that there's nothing this crate // can do about it. We probably want to skip the lint entirely. - if err.span.primary_spans().iter().any(|s| in_external_macro(sess, *s)) { + if err.span.primary_spans().iter().any(|s| s.in_external_macro(sess.source_map())) { // Any suggestions made here are likely to be incorrect, so anything we // emit shouldn't be automatically fixed by rustfix. err.disable_suggestions(); @@ -422,36 +421,3 @@ pub fn lint_level( } lint_level_impl(sess, lint, level, src, span, Box::new(decorate)) } - -/// Returns whether `span` originates in a foreign crate's external macro. -/// -/// This is used to test whether a lint should not even begin to figure out whether it should -/// be reported on the current node. -pub fn in_external_macro(sess: &Session, span: Span) -> bool { - let expn_data = span.ctxt().outer_expn_data(); - match expn_data.kind { - ExpnKind::Root - | ExpnKind::Desugaring( - DesugaringKind::ForLoop - | DesugaringKind::WhileLoop - | DesugaringKind::OpaqueTy - | DesugaringKind::Async - | DesugaringKind::Await, - ) => false, - ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => true, // well, it's "external" - ExpnKind::Macro(MacroKind::Bang, _) => { - // Dummy span for the `def_site` means it's an external macro. - expn_data.def_site.is_dummy() || sess.source_map().is_imported(expn_data.def_site) - } - ExpnKind::Macro { .. } => true, // definitely a plugin - } -} - -/// Return whether `span` is generated by `async` or `await`. -pub fn is_from_async_await(span: Span) -> bool { - let expn_data = span.ctxt().outer_expn_data(); - match expn_data.kind { - ExpnKind::Desugaring(DesugaringKind::Async | DesugaringKind::Await) => true, - _ => false, - } -} diff --git a/compiler/rustc_middle/src/macros.rs b/compiler/rustc_middle/src/macros.rs index 39816c17b985f..b3064d8fe25cb 100644 --- a/compiler/rustc_middle/src/macros.rs +++ b/compiler/rustc_middle/src/macros.rs @@ -4,8 +4,8 @@ /// /// If you have a span available, you should use [`span_bug`] instead. /// -/// If the bug should only be emitted when compilation didn't fail, [`DiagCtxtHandle::span_delayed_bug`] -/// may be useful. +/// If the bug should only be emitted when compilation didn't fail, +/// [`DiagCtxtHandle::span_delayed_bug`] may be useful. /// /// [`DiagCtxtHandle::span_delayed_bug`]: rustc_errors::DiagCtxtHandle::span_delayed_bug /// [`span_bug`]: crate::span_bug @@ -14,14 +14,8 @@ macro_rules! bug { () => ( $crate::bug!("impossible case reached") ); - ($msg:expr) => ( - $crate::util::bug::bug_fmt(::std::format_args!($msg)) - ); - ($msg:expr,) => ( - $crate::bug!($msg) - ); - ($fmt:expr, $($arg:tt)+) => ( - $crate::util::bug::bug_fmt(::std::format_args!($fmt, $($arg)+)) + ($($arg:tt)+) => ( + $crate::util::bug::bug_fmt(::std::format_args!($($arg)+)) ); } @@ -30,20 +24,14 @@ macro_rules! bug { /// at the code the compiler was compiling when it ICEd. This is the preferred way to trigger /// ICEs. /// -/// If the bug should only be emitted when compilation didn't fail, [`DiagCtxtHandle::span_delayed_bug`] -/// may be useful. +/// If the bug should only be emitted when compilation didn't fail, +/// [`DiagCtxtHandle::span_delayed_bug`] may be useful. /// /// [`DiagCtxtHandle::span_delayed_bug`]: rustc_errors::DiagCtxtHandle::span_delayed_bug #[macro_export] macro_rules! span_bug { - ($span:expr, $msg:expr) => ( - $crate::util::bug::span_bug_fmt($span, ::std::format_args!($msg)) - ); - ($span:expr, $msg:expr,) => ( - $crate::span_bug!($span, $msg) - ); - ($span:expr, $fmt:expr, $($arg:tt)+) => ( - $crate::util::bug::span_bug_fmt($span, ::std::format_args!($fmt, $($arg)+)) + ($span:expr, $($arg:tt)+) => ( + $crate::util::bug::span_bug_fmt($span, ::std::format_args!($($arg)+)) ); } @@ -53,7 +41,6 @@ macro_rules! span_bug { // When possible, use one of these (relatively) convenient macros to write // the impls for you. -#[macro_export] macro_rules! TrivialLiftImpls { ($($ty:ty),+ $(,)?) => { $( @@ -69,7 +56,6 @@ macro_rules! TrivialLiftImpls { /// Used for types that are `Copy` and which **do not care about arena /// allocated data** (i.e., don't need to be folded). -#[macro_export] macro_rules! TrivialTypeTraversalImpls { ($($ty:ty),+ $(,)?) => { $( @@ -104,7 +90,6 @@ macro_rules! TrivialTypeTraversalImpls { }; } -#[macro_export] macro_rules! TrivialTypeTraversalAndLiftImpls { ($($t:tt)*) => { TrivialTypeTraversalImpls! { $($t)* } diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 241767fe249b1..311bc60c3cd39 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -1,4 +1,5 @@ use rustc_abi::Align; +use rustc_ast::expand::autodiff_attrs::AutoDiffAttrs; use rustc_attr_parsing::{InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::Symbol; @@ -28,7 +29,10 @@ pub struct CodegenFnAttrs { pub link_ordinal: Option, /// The `#[target_feature(enable = "...")]` attribute and the enabled /// features (only enabled features are supported right now). + /// Implied target features have already been applied. pub target_features: Vec, + /// Whether the function was declared safe, but has target features + pub safe_target_features: bool, /// The `#[linkage = "..."]` attribute on Rust-defined items and the value we found. pub linkage: Option, /// The `#[linkage = "..."]` attribute on foreign items and the value we found. @@ -49,6 +53,8 @@ pub struct CodegenFnAttrs { /// The `#[patchable_function_entry(...)]` attribute. Indicates how many nops should be around /// the function entry. pub patchable_function_entry: Option, + /// For the `#[autodiff]` macros. + pub autodiff_item: Option, } #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] @@ -144,11 +150,12 @@ impl CodegenFnAttrs { CodegenFnAttrs { flags: CodegenFnAttrFlags::empty(), inline: InlineAttr::None, - optimize: OptimizeAttr::None, + optimize: OptimizeAttr::Default, export_name: None, link_name: None, link_ordinal: None, target_features: vec![], + safe_target_features: false, linkage: None, import_linkage: None, link_section: None, @@ -156,6 +163,7 @@ impl CodegenFnAttrs { instruction_set: None, alignment: None, patchable_function_entry: None, + autodiff_item: None, } } @@ -170,7 +178,7 @@ impl CodegenFnAttrs { || match self.linkage { // These are private, so make sure we don't try to consider // them external. - None | Some(Linkage::Internal | Linkage::Private) => false, + None | Some(Linkage::Internal) => false, Some(_) => true, } } diff --git a/compiler/rustc_middle/src/middle/debugger_visualizer.rs b/compiler/rustc_middle/src/middle/debugger_visualizer.rs index c241c06fce44f..5a811321f58b9 100644 --- a/compiler/rustc_middle/src/middle/debugger_visualizer.rs +++ b/compiler/rustc_middle/src/middle/debugger_visualizer.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; +use std::sync::Arc; -use rustc_data_structures::sync::Lrc; use rustc_macros::{Decodable, Encodable, HashStable}; #[derive(HashStable)] @@ -15,7 +15,7 @@ pub enum DebuggerVisualizerType { #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Encodable, Decodable)] pub struct DebuggerVisualizerFile { /// The complete debugger visualizer source. - pub src: Lrc<[u8]>, + pub src: Arc<[u8]>, /// Indicates which visualizer type this targets. pub visualizer_type: DebuggerVisualizerType, /// The file path to the visualizer file. This is used for reporting @@ -26,13 +26,13 @@ pub struct DebuggerVisualizerFile { } impl DebuggerVisualizerFile { - pub fn new(src: Lrc<[u8]>, visualizer_type: DebuggerVisualizerType, path: PathBuf) -> Self { + pub fn new(src: Arc<[u8]>, visualizer_type: DebuggerVisualizerType, path: PathBuf) -> Self { DebuggerVisualizerFile { src, visualizer_type, path: Some(path) } } pub fn path_erased(&self) -> Self { DebuggerVisualizerFile { - src: Lrc::clone(&self.src), + src: Arc::clone(&self.src), visualizer_type: self.visualizer_type, path: None, } diff --git a/compiler/rustc_middle/src/middle/resolve_bound_vars.rs b/compiler/rustc_middle/src/middle/resolve_bound_vars.rs index 111ac990bc77e..51a079e8bc122 100644 --- a/compiler/rustc_middle/src/middle/resolve_bound_vars.rs +++ b/compiler/rustc_middle/src/middle/resolve_bound_vars.rs @@ -45,15 +45,22 @@ pub enum ObjectLifetimeDefault { Param(DefId), } -/// Maps the id of each lifetime reference to the lifetime decl +/// Maps the id of each bound variable reference to the variable decl /// that it corresponds to. -#[derive(HashStable, Debug)] +#[derive(Debug, Default, HashStable)] pub struct ResolveBoundVars { - /// Maps from every use of a named (not anonymous) lifetime to a - /// `Region` describing how that region is bound + // Maps from every use of a named (not anonymous) bound var to a + // `ResolvedArg` describing how that variable is bound. pub defs: SortedMap, + // Maps relevant hir items to the bound vars on them. These include: + // - function defs + // - function pointers + // - closures + // - trait refs + // - bound types (like `T` in `for<'a> T<'a>: Foo`) pub late_bound_vars: SortedMap>, + // List captured variables for each opaque type. pub opaque_captured_lifetimes: LocalDefIdMap>, } diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 84c3c2eb49e27..93bbf6d7fa4ff 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -13,7 +13,6 @@ use rustc_feature::GateIssue; use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdMap}; use rustc_hir::{self as hir, HirId}; use rustc_macros::{Decodable, Encodable, HashStable, Subdiagnostic}; -use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_session::Session; use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE}; use rustc_session::lint::{BuiltinLintDiag, DeprecatedSinceKind, Level, Lint, LintBuffer}; @@ -23,6 +22,7 @@ use tracing::debug; pub use self::StabilityLevel::*; use crate::ty::TyCtxt; +use crate::ty::print::with_no_trimmed_paths; #[derive(PartialEq, Clone, Copy, Debug)] pub enum StabilityLevel { @@ -30,6 +30,14 @@ pub enum StabilityLevel { Stable, } +#[derive(Copy, Clone)] +pub enum UnstableKind { + /// Enforcing regular stability of an item + Regular, + /// Enforcing const stability of an item + Const(Span), +} + /// An entry in the `depr_map`. #[derive(Copy, Clone, HashStable, Debug, Encodable, Decodable)] pub struct DeprecationEntry { @@ -108,10 +116,16 @@ pub fn report_unstable( is_soft: bool, span: Span, soft_handler: impl FnOnce(&'static Lint, Span, String), + kind: UnstableKind, ) { + let qual = match kind { + UnstableKind::Regular => "", + UnstableKind::Const(_) => " const", + }; + let msg = match reason { - Some(r) => format!("use of unstable library feature `{feature}`: {r}"), - None => format!("use of unstable library feature `{feature}`"), + Some(r) => format!("use of unstable{qual} library feature `{feature}`: {r}"), + None => format!("use of unstable{qual} library feature `{feature}`"), }; if is_soft { @@ -121,6 +135,9 @@ pub fn report_unstable( if let Some((inner_types, msg, sugg, applicability)) = suggestion { err.span_suggestion(inner_types, msg, sugg, applicability); } + if let UnstableKind::Const(kw) = kind { + err.span_label(kw, "trait is not stable as const yet"); + } err.emit(); } } @@ -232,9 +249,18 @@ fn late_report_deprecation( return; } + let is_in_effect = depr.is_in_effect(); + let lint = deprecation_lint(is_in_effect); + + // Calculating message for lint involves calling `self.def_path_str`, + // which will by default invoke the expensive `visible_parent_map` query. + // Skip all that work if the lint is allowed anyway. + if tcx.lint_level_at_node(lint, hir_id).0 == Level::Allow { + return; + } + let def_path = with_no_trimmed_paths!(tcx.def_path_str(def_id)); let def_kind = tcx.def_descr(def_id); - let is_in_effect = depr.is_in_effect(); let method_span = method_span.unwrap_or(span); let suggestion = @@ -250,7 +276,7 @@ fn late_report_deprecation( note: depr.note, since_kind: deprecated_since_kind(is_in_effect, depr.since), }; - tcx.emit_node_span_lint(deprecation_lint(is_in_effect), hir_id, method_span, diag); + tcx.emit_node_span_lint(lint, hir_id, method_span, diag); } /// Result of `TyCtxt::eval_stability`. @@ -360,13 +386,7 @@ impl<'tcx> TyCtxt<'tcx> { // hierarchy. let depr_attr = &depr_entry.attr; if !skip || depr_attr.is_since_rustc_version() { - // Calculating message for lint involves calling `self.def_path_str`. - // Which by default to calculate visible path will invoke expensive `visible_parent_map` query. - // So we skip message calculation altogether, if lint is allowed. - let lint = deprecation_lint(depr_attr.is_in_effect()); - if self.lint_level_at_node(lint, id).0 != Level::Allow { - late_report_deprecation(self, depr_attr, span, method_span, id, def_id); - } + late_report_deprecation(self, depr_attr, span, method_span, id, def_id); } }; } @@ -587,6 +607,7 @@ impl<'tcx> TyCtxt<'tcx> { is_soft, span, soft_handler, + UnstableKind::Regular, ), EvalResult::Unmarked => unmarked(span, def_id), } @@ -594,6 +615,73 @@ impl<'tcx> TyCtxt<'tcx> { is_allowed } + /// This function is analogous to `check_optional_stability` but with the logic in + /// `eval_stability_allow_unstable` inlined, and which operating on const stability + /// instead of regular stability. + /// + /// This enforces *syntactical* const stability of const traits. In other words, + /// it enforces the ability to name `~const`/`const` traits in trait bounds in various + /// syntax positions in HIR (including in the trait of an impl header). + pub fn check_const_stability(self, def_id: DefId, span: Span, const_kw_span: Span) { + let is_staged_api = self.lookup_stability(def_id.krate.as_def_id()).is_some(); + if !is_staged_api { + return; + } + + // Only the cross-crate scenario matters when checking unstable APIs + let cross_crate = !def_id.is_local(); + if !cross_crate { + return; + } + + let stability = self.lookup_const_stability(def_id); + debug!( + "stability: \ + inspecting def_id={:?} span={:?} of stability={:?}", + def_id, span, stability + ); + + match stability { + Some(ConstStability { + level: attr::StabilityLevel::Unstable { reason, issue, is_soft, implied_by, .. }, + feature, + .. + }) => { + assert!(!is_soft); + + if span.allows_unstable(feature) { + debug!("body stability: skipping span={:?} since it is internal", span); + return; + } + if self.features().enabled(feature) { + return; + } + + // If this item was previously part of a now-stabilized feature which is still + // enabled (i.e. the user hasn't removed the attribute for the stabilized feature + // yet) then allow use of this item. + if let Some(implied_by) = implied_by + && self.features().enabled(implied_by) + { + return; + } + + report_unstable( + self.sess, + feature, + reason.to_opt_reason(), + issue, + None, + false, + span, + |_, _, _| {}, + UnstableKind::Const(const_kw_span), + ); + } + Some(_) | None => {} + } + } + pub fn lookup_deprecation(self, id: DefId) -> Option { self.lookup_deprecation_entry(id).map(|depr| depr.attr) } diff --git a/compiler/rustc_middle/src/mir/basic_blocks.rs b/compiler/rustc_middle/src/mir/basic_blocks.rs index 3eb563d7d6e57..c32cf5f825334 100644 --- a/compiler/rustc_middle/src/mir/basic_blocks.rs +++ b/compiler/rustc_middle/src/mir/basic_blocks.rs @@ -163,6 +163,7 @@ impl<'tcx> graph::Predecessors for BasicBlocks<'tcx> { } } +// Done here instead of in `structural_impls.rs` because `Cache` is private, as is `basic_blocks`. TrivialTypeTraversalImpls! { Cache } impl Encodable for Cache { diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 1231ea8856961..923160cc0cc85 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -250,7 +250,7 @@ impl<'tcx> Const<'tcx> { // Dont use the outer ty as on invalid code we can wind up with them not being the same. // this then results in allowing const eval to add `1_i64 + 1_usize` in cases where the mir // was originally `({N: usize} + 1_usize)` under `generic_const_exprs`. - ty::ConstKind::Value(ty, _) => ty, + ty::ConstKind::Value(cv) => cv.ty, _ => *ty, } } @@ -264,7 +264,7 @@ impl<'tcx> Const<'tcx> { pub fn is_required_const(&self) -> bool { match self { Const::Ty(_, c) => match c.kind() { - ty::ConstKind::Value(_, _) => false, // already a value, cannot error + ty::ConstKind::Value(_) => false, // already a value, cannot error _ => true, }, Const::Val(..) => false, // already a value, cannot error @@ -276,11 +276,11 @@ impl<'tcx> Const<'tcx> { pub fn try_to_scalar(self) -> Option { match self { Const::Ty(_, c) => match c.kind() { - ty::ConstKind::Value(ty, valtree) if ty.is_primitive() => { + ty::ConstKind::Value(cv) if cv.ty.is_primitive() => { // A valtree of a type where leaves directly represent the scalar const value. // Just checking whether it is a leaf is insufficient as e.g. references are leafs // but the leaf value is the value they point to, not the reference itself! - Some(valtree.unwrap_leaf().into()) + Some(cv.valtree.unwrap_leaf().into()) } _ => None, }, @@ -295,9 +295,7 @@ impl<'tcx> Const<'tcx> { match self { Const::Val(ConstValue::Scalar(Scalar::Int(x)), _) => Some(x), Const::Ty(_, c) => match c.kind() { - ty::ConstKind::Value(ty, valtree) if ty.is_primitive() => { - Some(valtree.unwrap_leaf()) - } + ty::ConstKind::Value(cv) if cv.ty.is_primitive() => Some(cv.valtree.unwrap_leaf()), _ => None, }, _ => None, @@ -328,7 +326,7 @@ impl<'tcx> Const<'tcx> { } match c.kind() { - ConstKind::Value(ty, val) => Ok(tcx.valtree_to_const_val((ty, val))), + ConstKind::Value(cv) => Ok(tcx.valtree_to_const_val(cv)), ConstKind::Expr(_) => { bug!("Normalization of `ty::ConstKind::Expr` is unimplemented") } @@ -353,13 +351,13 @@ impl<'tcx> Const<'tcx> { typing_env: ty::TypingEnv<'tcx>, ) -> Option { if let Const::Ty(_, c) = self - && let ty::ConstKind::Value(ty, val) = c.kind() - && ty.is_primitive() + && let ty::ConstKind::Value(cv) = c.kind() + && cv.ty.is_primitive() { // Avoid the `valtree_to_const_val` query. Can only be done on primitive types that // are valtree leaves, and *not* on references. (References should return the // pointer here, which valtrees don't represent.) - Some(val.unwrap_leaf().into()) + Some(cv.valtree.unwrap_leaf().into()) } else { self.eval(tcx, typing_env, DUMMY_SP).ok()?.try_to_scalar() } @@ -460,20 +458,6 @@ impl<'tcx> Const<'tcx> { Self::Val(val, ty) } - pub fn from_ty_const(c: ty::Const<'tcx>, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Self { - match c.kind() { - ty::ConstKind::Value(ty, valtree) => { - // Make sure that if `c` is normalized, then the return value is normalized. - let const_val = tcx.valtree_to_const_val((ty, valtree)); - Self::Val(const_val, ty) - } - ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args }) => { - Self::Unevaluated(UnevaluatedConst { def, args, promoted: None }, ty) - } - _ => Self::Ty(ty, c), - } - } - /// Return true if any evaluation of this constant always returns the same value, /// taking into account even pointer identity tests. pub fn is_deterministic(&self) -> bool { @@ -487,7 +471,7 @@ impl<'tcx> Const<'tcx> { // A valtree may be a reference. Valtree references correspond to a // different allocation each time they are evaluated. Valtrees for primitive // types are fine though. - ty::ConstKind::Value(ty, _) => ty.is_primitive(), + ty::ConstKind::Value(cv) => cv.ty.is_primitive(), ty::ConstKind::Unevaluated(..) | ty::ConstKind::Expr(..) => false, // This can happen if evaluation of a constant failed. The result does not matter // much since compilation is doomed. diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 29f26180c973f..8c6b11a681ef6 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -2,9 +2,9 @@ use std::fmt::{self, Debug, Formatter}; -use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; -use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; +use rustc_data_structures::fx::FxIndexMap; +use rustc_index::{Idx, IndexVec}; +use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::Span; rustc_index::newtype_index! { @@ -71,12 +71,8 @@ impl ConditionId { /// Enum that can hold a constant zero value, the ID of an physical coverage /// counter, or the ID of a coverage-counter expression. -/// -/// This was originally only used for expression operands (and named `Operand`), -/// but the zero/counter/expression distinction is also useful for representing -/// the value of code/gap mappings, and the true/false arms of branch mappings. -#[derive(Copy, Clone, PartialEq, Eq)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub enum CovTerm { Zero, Counter(CounterId), @@ -93,7 +89,7 @@ impl Debug for CovTerm { } } -#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] pub enum CoverageKind { /// Marks a span that might otherwise not be represented in MIR, so that /// coverage instrumentation can associate it with its enclosing block/BCB. @@ -107,23 +103,12 @@ pub enum CoverageKind { /// Should be erased before codegen (at some point after `InstrumentCoverage`). BlockMarker { id: BlockMarkerId }, - /// Marks the point in MIR control flow represented by a coverage counter. - /// - /// This is eventually lowered to `llvm.instrprof.increment` in LLVM IR. - /// - /// If this statement does not survive MIR optimizations, any mappings that - /// refer to this counter can have those references simplified to zero. - CounterIncrement { id: CounterId }, - - /// Marks the point in MIR control-flow represented by a coverage expression. + /// Marks its enclosing basic block with the ID of the coverage graph node + /// that it was part of during the `InstrumentCoverage` MIR pass. /// - /// If this statement does not survive MIR optimizations, any mappings that - /// refer to this expression can have those references simplified to zero. - /// - /// (This is only inserted for expression IDs that are directly used by - /// mappings. Intermediate expressions with no direct mappings are - /// retained/zeroed based on whether they are transitively used.) - ExpressionUsed { id: ExpressionId }, + /// During codegen, this might be lowered to `llvm.instrprof.increment` or + /// to a no-op, depending on the outcome of counter-creation. + VirtualCounter { bcb: BasicCoverageBlock }, /// Marks the point in MIR control flow represented by a evaluated condition. /// @@ -142,8 +127,7 @@ impl Debug for CoverageKind { match self { SpanMarker => write!(fmt, "SpanMarker"), BlockMarker { id } => write!(fmt, "BlockMarker({:?})", id.index()), - CounterIncrement { id } => write!(fmt, "CounterIncrement({:?})", id.index()), - ExpressionUsed { id } => write!(fmt, "ExpressionUsed({:?})", id.index()), + VirtualCounter { bcb } => write!(fmt, "VirtualCounter({bcb:?})"), CondBitmapUpdate { index, decision_depth } => { write!(fmt, "CondBitmapUpdate(index={:?}, depth={:?})", index, decision_depth) } @@ -155,7 +139,7 @@ impl Debug for CoverageKind { } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)] -#[derive(TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable)] pub enum Op { Subtract, Add, @@ -171,8 +155,8 @@ impl Op { } } -#[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct Expression { pub lhs: CovTerm, pub op: Op, @@ -180,39 +164,24 @@ pub struct Expression { } #[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub enum MappingKind { /// Associates a normal region of code with a counter/expression/zero. - Code(CovTerm), + Code { bcb: BasicCoverageBlock }, /// Associates a branch region with separate counters for true and false. - Branch { true_term: CovTerm, false_term: CovTerm }, + Branch { true_bcb: BasicCoverageBlock, false_bcb: BasicCoverageBlock }, /// Associates a branch region with separate counters for true and false. - MCDCBranch { true_term: CovTerm, false_term: CovTerm, mcdc_params: ConditionInfo }, + MCDCBranch { + true_bcb: BasicCoverageBlock, + false_bcb: BasicCoverageBlock, + mcdc_params: ConditionInfo, + }, /// Associates a decision region with a bitmap and number of conditions. MCDCDecision(DecisionInfo), } -impl MappingKind { - /// Returns a copy of this mapping kind, in which all coverage terms have - /// been replaced with ones returned by the given function. - pub fn map_terms(&self, map_fn: impl Fn(CovTerm) -> CovTerm) -> Self { - match *self { - Self::Code(term) => Self::Code(map_fn(term)), - Self::Branch { true_term, false_term } => { - Self::Branch { true_term: map_fn(true_term), false_term: map_fn(false_term) } - } - Self::MCDCBranch { true_term, false_term, mcdc_params } => Self::MCDCBranch { - true_term: map_fn(true_term), - false_term: map_fn(false_term), - mcdc_params, - }, - Self::MCDCDecision(param) => Self::MCDCDecision(param), - } - } -} - #[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct Mapping { pub kind: MappingKind, pub span: Span, @@ -222,14 +191,19 @@ pub struct Mapping { /// to be used in conjunction with the individual coverage statements injected /// into the function's basic blocks. #[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct FunctionCoverageInfo { pub function_source_hash: u64, pub body_span: Span, - pub num_counters: usize, - pub mcdc_bitmap_bits: usize, - pub expressions: IndexVec, + + /// Used in conjunction with `priority_list` to create physical counters + /// and counter expressions, after MIR optimizations. + pub node_flow_data: NodeFlowData, + pub priority_list: Vec, + pub mappings: Vec, + + pub mcdc_bitmap_bits: usize, /// The depth of the deepest decision is used to know how many /// temp condbitmaps should be allocated for the function. pub mcdc_num_condition_bitmaps: usize, @@ -242,7 +216,7 @@ pub struct FunctionCoverageInfo { /// ("Hi" indicates that this is "high-level" information collected at the /// THIR/MIR boundary, before the MIR-based coverage instrumentation pass.) #[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct CoverageInfoHi { /// 1 more than the highest-numbered [`CoverageKind::BlockMarker`] that was /// injected into the MIR body. This makes it possible to allocate per-ID @@ -256,7 +230,7 @@ pub struct CoverageInfoHi { } #[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct BranchSpan { pub span: Span, pub true_marker: BlockMarkerId, @@ -264,7 +238,7 @@ pub struct BranchSpan { } #[derive(Copy, Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct ConditionInfo { pub condition_id: ConditionId, pub true_next_id: Option, @@ -272,7 +246,7 @@ pub struct ConditionInfo { } #[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct MCDCBranchSpan { pub span: Span, pub condition_info: ConditionInfo, @@ -281,14 +255,14 @@ pub struct MCDCBranchSpan { } #[derive(Copy, Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct DecisionInfo { pub bitmap_idx: u32, pub num_conditions: u16, } #[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct MCDCDecisionSpan { pub span: Span, pub end_markers: Vec, @@ -296,40 +270,55 @@ pub struct MCDCDecisionSpan { pub num_conditions: usize, } -/// Summarizes coverage IDs inserted by the `InstrumentCoverage` MIR pass -/// (for compiler option `-Cinstrument-coverage`), after MIR optimizations -/// have had a chance to potentially remove some of them. +/// Contains information needed during codegen, obtained by inspecting the +/// function's MIR after MIR optimizations. /// -/// Used by the `coverage_ids_info` query. +/// Returned by the `coverage_ids_info` query. #[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable)] pub struct CoverageIdsInfo { - pub counters_seen: BitSet, - pub zero_expressions: BitSet, + pub num_counters: u32, + pub phys_counter_for_node: FxIndexMap, + pub term_for_bcb: IndexVec>, + pub expressions: IndexVec, } -impl CoverageIdsInfo { - /// Coverage codegen needs to know how many coverage counters are ever - /// incremented within a function, so that it can set the `num-counters` - /// argument of the `llvm.instrprof.increment` intrinsic. +rustc_index::newtype_index! { + /// During the `InstrumentCoverage` MIR pass, a BCB is a node in the + /// "coverage graph", which is a refinement of the MIR control-flow graph + /// that merges or omits some blocks that aren't relevant to coverage. /// - /// This may be less than the highest counter ID emitted by the - /// InstrumentCoverage MIR pass, if the highest-numbered counter increments - /// were removed by MIR optimizations. - pub fn num_counters_after_mir_opts(&self) -> u32 { - // FIXME(Zalathar): Currently this treats an unused counter as "used" - // if its ID is less than that of the highest counter that really is - // used. Fixing this would require adding a renumbering step somewhere. - self.counters_seen.last_set_in(..).map_or(0, |max| max.as_u32() + 1) + /// After that pass is complete, the coverage graph no longer exists, so a + /// BCB is effectively an opaque ID. + #[derive(HashStable)] + #[encodable] + #[orderable] + #[debug_format = "bcb{}"] + pub struct BasicCoverageBlock { + const START_BCB = 0; } +} - /// Returns `true` if the given term is known to have a value of zero, taking - /// into account knowledge of which counters are unused and which expressions - /// are always zero. - pub fn is_zero_term(&self, term: CovTerm) -> bool { - match term { - CovTerm::Zero => true, - CovTerm::Counter(id) => !self.counters_seen.contains(id), - CovTerm::Expression(id) => self.zero_expressions.contains(id), - } - } +/// Data representing a view of some underlying graph, in which each node's +/// successors have been merged into a single "supernode". +/// +/// The resulting supernodes have no obvious meaning on their own. +/// However, merging successor nodes means that a node's out-edges can all +/// be combined into a single out-edge, whose flow is the same as the flow +/// (execution count) of its corresponding node in the original graph. +/// +/// With all node flows now in the original graph now represented as edge flows +/// in the merged graph, it becomes possible to analyze the original node flows +/// using techniques for analyzing edge flows. +#[derive(Clone, Debug)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] +pub struct NodeFlowData { + /// Maps each node to the supernode that contains it, indicated by some + /// arbitrary "root" node that is part of that supernode. + pub supernodes: IndexVec, + /// For each node, stores the single supernode that all of its successors + /// have been merged into. + /// + /// (Note that each node in a supernode can potentially have a _different_ + /// successor supernode from its peers.) + pub succ_supernodes: IndexVec, } diff --git a/compiler/rustc_middle/src/mir/generic_graph.rs b/compiler/rustc_middle/src/mir/generic_graph.rs index a52ec58a1ee69..3fd73712b0968 100644 --- a/compiler/rustc_middle/src/mir/generic_graph.rs +++ b/compiler/rustc_middle/src/mir/generic_graph.rs @@ -1,5 +1,6 @@ use gsgdt::{Edge, Graph, Node, NodeStyle}; -use rustc_middle::mir::*; + +use crate::mir::*; /// Convert an MIR function into a gsgdt Graph pub(crate) fn mir_fn_to_generic_graph<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Graph { diff --git a/compiler/rustc_middle/src/mir/generic_graphviz.rs b/compiler/rustc_middle/src/mir/generic_graphviz.rs index e1c3d8156d83e..bce7beb521da4 100644 --- a/compiler/rustc_middle/src/mir/generic_graphviz.rs +++ b/compiler/rustc_middle/src/mir/generic_graphviz.rs @@ -2,7 +2,8 @@ use std::io::{self, Write}; use rustc_data_structures::graph::{self, iterate}; use rustc_graphviz as dot; -use rustc_middle::ty::TyCtxt; + +use crate::ty::TyCtxt; pub struct GraphvizWriter< 'a, diff --git a/compiler/rustc_middle/src/mir/graphviz.rs b/compiler/rustc_middle/src/mir/graphviz.rs index 7bb41193d5c9c..a64b122fbc9e5 100644 --- a/compiler/rustc_middle/src/mir/graphviz.rs +++ b/compiler/rustc_middle/src/mir/graphviz.rs @@ -2,10 +2,10 @@ use std::io::{self, Write}; use gsgdt::GraphvizSettings; use rustc_graphviz as dot; -use rustc_middle::mir::*; use super::generic_graph::mir_fn_to_generic_graph; use super::pretty::dump_mir_def_ids; +use crate::mir::*; /// Write a graphviz DOT graph of a list of MIRs. pub fn write_mir_graphviz(tcx: TyCtxt<'_>, single: Option, w: &mut W) -> io::Result<()> diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index d6f8fed755f08..697a0e6592df0 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -270,6 +270,12 @@ impl AllocRange { } } +/// Whether a new allocation should be initialized with zero-bytes. +pub enum AllocInit { + Uninit, + Zero, +} + // The constructors are all without extra; the extra gets added by a machine hook later. impl Allocation { /// Creates an allocation initialized by the given bytes @@ -294,7 +300,12 @@ impl Allocation { Allocation::from_bytes(slice, Align::ONE, Mutability::Not) } - fn uninit_inner(size: Size, align: Align, fail: impl FnOnce() -> R) -> Result { + fn new_inner( + size: Size, + align: Align, + init: AllocInit, + fail: impl FnOnce() -> R, + ) -> Result { // We raise an error if we cannot create the allocation on the host. // This results in an error that can happen non-deterministically, since the memory // available to the compiler can change between runs. Normally queries are always @@ -306,7 +317,13 @@ impl Allocation { Ok(Allocation { bytes, provenance: ProvenanceMap::new(), - init_mask: InitMask::new(size, false), + init_mask: InitMask::new( + size, + match init { + AllocInit::Uninit => false, + AllocInit::Zero => true, + }, + ), align, mutability: Mutability::Mut, extra: (), @@ -315,8 +332,8 @@ impl Allocation { /// Try to create an Allocation of `size` bytes, failing if there is not enough memory /// available to the compiler to do so. - pub fn try_uninit<'tcx>(size: Size, align: Align) -> InterpResult<'tcx, Self> { - Self::uninit_inner(size, align, || { + pub fn try_new<'tcx>(size: Size, align: Align, init: AllocInit) -> InterpResult<'tcx, Self> { + Self::new_inner(size, align, init, || { ty::tls::with(|tcx| tcx.dcx().delayed_bug("exhausted memory during interpretation")); InterpErrorKind::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted) }) @@ -328,8 +345,8 @@ impl Allocation { /// /// Example use case: To obtain an Allocation filled with specific data, /// first call this function and then call write_scalar to fill in the right data. - pub fn uninit(size: Size, align: Align) -> Self { - match Self::uninit_inner(size, align, || { + pub fn new(size: Size, align: Align, init: AllocInit) -> Self { + match Self::new_inner(size, align, init, || { panic!( "interpreter ran out of memory: cannot create allocation of {} bytes", size.bytes() diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs index 3a83b184d83d5..78196b0536190 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs @@ -97,7 +97,7 @@ impl ProvenanceMap { debug_assert!(prov.len() <= 1); if let Some(entry) = prov.first() { // If it overlaps with this byte, it is on this byte. - debug_assert!(self.bytes.as_ref().map_or(true, |b| b.get(&offset).is_none())); + debug_assert!(self.bytes.as_ref().is_none_or(|b| !b.contains_key(&offset))); Some(entry.1) } else { // Look up per-byte provenance. @@ -301,7 +301,7 @@ impl ProvenanceMap { // For really small copies, make sure we don't start before `src` does. let entry_start = cmp::max(entry.0, src.start); for offset in entry_start..src.end() { - if bytes.last().map_or(true, |bytes_entry| bytes_entry.0 < offset) { + if bytes.last().is_none_or(|bytes_entry| bytes_entry.0 < offset) { // The last entry, if it exists, has a lower offset than us. bytes.push((offset, entry.1)); } else { diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 8c085fa310ab5..8861e31b0991c 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -95,8 +95,6 @@ impl From for ErrorGuaranteed { } } -TrivialTypeTraversalImpls! { ErrorHandled } - pub type EvalToAllocationRawResult<'tcx> = Result, ErrorHandled>; pub type EvalStaticInitializerRawResult<'tcx> = Result, ErrorHandled>; pub type EvalToConstValueResult<'tcx> = Result, ErrorHandled>; @@ -218,10 +216,6 @@ pub enum InvalidProgramInfo<'tcx> { AlreadyReported(ReportedErrorInfo), /// An error occurred during layout computation. Layout(layout::LayoutError<'tcx>), - /// An error occurred during FnAbi computation: the passed --target lacks FFI support - /// (which unfortunately typeck does not reject). - /// Not using `FnAbiError` as that contains a nested `LayoutError`. - FnAbiAdjustForForeignAbi(rustc_target::callconv::AdjustForForeignAbiError), } /// Details of why a pointer had to be in-bounds. diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 8d73c9e76de1a..c48cfffa05cc4 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -15,12 +15,11 @@ use std::{fmt, io}; use rustc_abi::{AddressSpace, Align, Endian, HasDataLayout, Size}; use rustc_ast::{LitKind, Mutability}; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::Lock; -use rustc_errors::ErrorGuaranteed; +use rustc_data_structures::sharded::ShardedHashMap; +use rustc_data_structures::sync::{AtomicU64, Lock}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; -use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_serialize::{Decodable, Encodable}; use tracing::{debug, trace}; // Also make the error macros available from this module. @@ -31,8 +30,8 @@ pub use { }; pub use self::allocation::{ - AllocBytes, AllocError, AllocRange, AllocResult, Allocation, ConstAllocation, InitChunk, - InitChunkIter, alloc_range, + AllocBytes, AllocError, AllocInit, AllocRange, AllocResult, Allocation, ConstAllocation, + InitChunk, InitChunkIter, alloc_range, }; pub use self::error::{ BadBytesAccess, CheckAlignMsg, CheckInAllocMsg, ErrorHandled, EvalStaticInitializerRawResult, @@ -46,6 +45,7 @@ pub use self::pointer::{CtfeProvenance, Pointer, PointerArithmetic, Provenance}; pub use self::value::Scalar; use crate::mir; use crate::ty::codec::{TyDecoder, TyEncoder}; +use crate::ty::print::with_no_trimmed_paths; use crate::ty::{self, Instance, Ty, TyCtxt}; /// Uniquely identifies one of the following: @@ -84,16 +84,6 @@ pub struct LitToConstInput<'tcx> { pub neg: bool, } -/// Error type for `tcx.lit_to_const`. -#[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable)] -pub enum LitToConstError { - /// The literal's inferred type did not match the expected `ty` in the input. - /// This is used for graceful error handling (`span_delayed_bug`) in - /// type checking (`Const::from_anon_const`). - TypeError, - Reported(ErrorGuaranteed), -} - #[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct AllocId(pub NonZero); @@ -400,35 +390,39 @@ pub const CTFE_ALLOC_SALT: usize = 0; pub(crate) struct AllocMap<'tcx> { /// Maps `AllocId`s to their corresponding allocations. - alloc_map: FxHashMap>, + // Note that this map on rustc workloads seems to be rather dense, but in miri workloads should + // be pretty sparse. In #136105 we considered replacing it with a (dense) Vec-based map, but + // since there are workloads where it can be sparse we decided to go with sharding for now. At + // least up to 32 cores the one workload tested didn't exhibit much difference between the two. + // + // Should be locked *after* locking dedup if locking both to avoid deadlocks. + to_alloc: ShardedHashMap>, /// Used to deduplicate global allocations: functions, vtables, string literals, ... /// /// The `usize` is a "salt" used by Miri to make deduplication imperfect, thus better emulating /// the actual guarantees. - dedup: FxHashMap<(GlobalAlloc<'tcx>, usize), AllocId>, + dedup: Lock, usize), AllocId>>, /// The `AllocId` to assign to the next requested ID. /// Always incremented; never gets smaller. - next_id: AllocId, + next_id: AtomicU64, } impl<'tcx> AllocMap<'tcx> { pub(crate) fn new() -> Self { AllocMap { - alloc_map: Default::default(), + to_alloc: Default::default(), dedup: Default::default(), - next_id: AllocId(NonZero::new(1).unwrap()), + next_id: AtomicU64::new(1), } } - fn reserve(&mut self) -> AllocId { - let next = self.next_id; - self.next_id.0 = self.next_id.0.checked_add(1).expect( - "You overflowed a u64 by incrementing by 1... \ - You've just earned yourself a free drink if we ever meet. \ - Seriously, how did you do that?!", - ); - next + fn reserve(&self) -> AllocId { + // Technically there is a window here where we overflow and then another thread + // increments `next_id` *again* and uses it before we panic and tear down the entire session. + // We consider this fine since such overflows cannot realistically occur. + let next_id = self.next_id.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + AllocId(NonZero::new(next_id).unwrap()) } } @@ -439,26 +433,34 @@ impl<'tcx> TyCtxt<'tcx> { /// Make sure to call `set_alloc_id_memory` or `set_alloc_id_same_memory` before returning such /// an `AllocId` from a query. pub fn reserve_alloc_id(self) -> AllocId { - self.alloc_map.lock().reserve() + self.alloc_map.reserve() } /// Reserves a new ID *if* this allocation has not been dedup-reserved before. /// Should not be used for mutable memory. fn reserve_and_set_dedup(self, alloc: GlobalAlloc<'tcx>, salt: usize) -> AllocId { - let mut alloc_map = self.alloc_map.lock(); if let GlobalAlloc::Memory(mem) = alloc { if mem.inner().mutability.is_mut() { bug!("trying to dedup-reserve mutable memory"); } } let alloc_salt = (alloc, salt); - if let Some(&alloc_id) = alloc_map.dedup.get(&alloc_salt) { + // Locking this *before* `to_alloc` also to ensure correct lock order. + let mut dedup = self.alloc_map.dedup.lock(); + if let Some(&alloc_id) = dedup.get(&alloc_salt) { return alloc_id; } - let id = alloc_map.reserve(); + let id = self.alloc_map.reserve(); debug!("creating alloc {:?} with id {id:?}", alloc_salt.0); - alloc_map.alloc_map.insert(id, alloc_salt.0.clone()); - alloc_map.dedup.insert(alloc_salt, id); + let had_previous = self + .alloc_map + .to_alloc + .lock_shard_by_value(&id) + .insert(id, alloc_salt.0.clone()) + .is_some(); + // We just reserved, so should always be unique. + assert!(!had_previous); + dedup.insert(alloc_salt, id); id } @@ -508,7 +510,7 @@ impl<'tcx> TyCtxt<'tcx> { /// local dangling pointers and allocations in constants/statics. #[inline] pub fn try_get_global_alloc(self, id: AllocId) -> Option> { - self.alloc_map.lock().alloc_map.get(&id).cloned() + self.alloc_map.to_alloc.lock_shard_by_value(&id).get(&id).cloned() } #[inline] @@ -527,7 +529,9 @@ impl<'tcx> TyCtxt<'tcx> { /// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. Trying to /// call this function twice, even with the same `Allocation` will ICE the compiler. pub fn set_alloc_id_memory(self, id: AllocId, mem: ConstAllocation<'tcx>) { - if let Some(old) = self.alloc_map.lock().alloc_map.insert(id, GlobalAlloc::Memory(mem)) { + if let Some(old) = + self.alloc_map.to_alloc.lock_shard_by_value(&id).insert(id, GlobalAlloc::Memory(mem)) + { bug!("tried to set allocation ID {id:?}, but it was already existing as {old:#?}"); } } @@ -535,8 +539,11 @@ impl<'tcx> TyCtxt<'tcx> { /// Freezes an `AllocId` created with `reserve` by pointing it at a static item. Trying to /// call this function twice, even with the same `DefId` will ICE the compiler. pub fn set_nested_alloc_id_static(self, id: AllocId, def_id: LocalDefId) { - if let Some(old) = - self.alloc_map.lock().alloc_map.insert(id, GlobalAlloc::Static(def_id.to_def_id())) + if let Some(old) = self + .alloc_map + .to_alloc + .lock_shard_by_value(&id) + .insert(id, GlobalAlloc::Static(def_id.to_def_id())) { bug!("tried to set allocation ID {id:?}, but it was already existing as {old:#?}"); } diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 4c47d9636d3f2..78749428c6df3 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -9,7 +9,7 @@ use super::{ ReportedErrorInfo, }; use crate::mir; -use crate::query::TyCtxtEnsure; +use crate::query::TyCtxtEnsureOk; use crate::ty::visit::TypeVisitableExt; use crate::ty::{self, GenericArgs, TyCtxt}; @@ -198,7 +198,7 @@ impl<'tcx> TyCtxt<'tcx> { } } -impl<'tcx> TyCtxtEnsure<'tcx> { +impl<'tcx> TyCtxtEnsureOk<'tcx> { /// Evaluates a constant without providing any generic parameters. This is useful to evaluate consts /// that can't take any generic arguments like const items or enum discriminants. If a /// generic parameter is used within the constant `ErrorHandled::TooGeneric` will be returned. diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 98ef7d58a5024..795cfcef2d36d 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -21,20 +21,19 @@ use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_hir::{ self as hir, BindingMode, ByRef, CoroutineDesugaring, CoroutineKind, HirId, ImplicitSelfKind, }; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use rustc_serialize::{Decodable, Encodable}; use rustc_span::source_map::Spanned; use rustc_span::{DUMMY_SP, Span, Symbol}; -use tracing::trace; +use tracing::{debug, trace}; pub use self::query::*; use self::visit::TyContext; use crate::mir::interpret::{AllocRange, Scalar}; use crate::mir::visit::MirVisitable; use crate::ty::codec::{TyDecoder, TyEncoder}; -use crate::ty::fold::{FallibleTypeFolder, TypeFoldable}; use crate::ty::print::{FmtPrinter, Printer, pretty_print_const, with_no_trimmed_paths}; use crate::ty::visit::TypeVisitableExt; use crate::ty::{ @@ -50,7 +49,6 @@ pub mod generic_graphviz; pub mod graphviz; pub mod interpret; pub mod mono; -pub mod patch; pub mod pretty; mod query; mod statement; @@ -59,7 +57,6 @@ pub mod tcx; mod terminator; pub mod traversal; -mod type_foldable; pub mod visit; pub use consts::*; @@ -358,6 +355,8 @@ pub struct Body<'tcx> { /// /// Only present if coverage is enabled and this function is eligible. /// Boxed to limit space overhead in non-coverage builds. + #[type_foldable(identity)] + #[type_visitable(ignore)] pub coverage_info_hi: Option>, /// Per-function coverage information added by the `InstrumentCoverage` @@ -366,6 +365,8 @@ pub struct Body<'tcx> { /// /// If `-Cinstrument-coverage` is not active, or if an individual function /// is not eligible for coverage, then this should always be `None`. + #[type_foldable(identity)] + #[type_visitable(ignore)] pub function_coverage_info: Option>, } @@ -923,8 +924,6 @@ pub enum BindingForm<'tcx> { RefForGuard, } -TrivialTypeTraversalImpls! { BindingForm<'tcx> } - mod binding_form_impl { use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_query_system::ich::StableHashingContext; @@ -1410,10 +1409,10 @@ impl<'tcx> BasicBlockData<'tcx> { // existing elements from before the gap to the end of the gap. // For now, this is safe code, emulating a gap but initializing it. let mut gap = self.statements.len()..self.statements.len() + extra_stmts; - self.statements.resize(gap.end, Statement { - source_info: SourceInfo::outermost(DUMMY_SP), - kind: StatementKind::Nop, - }); + self.statements.resize( + gap.end, + Statement { source_info: SourceInfo::outermost(DUMMY_SP), kind: StatementKind::Nop }, + ); for (splice_start, new_stmts) in splices.into_iter().rev() { let splice_end = splice_start + new_stmts.size_hint().0; while gap.end > splice_end { @@ -1792,6 +1791,47 @@ impl DefLocation { } } +/// Checks if the specified `local` is used as the `self` parameter of a method call +/// in the provided `BasicBlock`. If it is, then the `DefId` of the called method is +/// returned. +pub fn find_self_call<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + local: Local, + block: BasicBlock, +) -> Option<(DefId, GenericArgsRef<'tcx>)> { + debug!("find_self_call(local={:?}): terminator={:?}", local, body[block].terminator); + if let Some(Terminator { kind: TerminatorKind::Call { func, args, .. }, .. }) = + &body[block].terminator + && let Operand::Constant(box ConstOperand { const_, .. }) = func + && let ty::FnDef(def_id, fn_args) = *const_.ty().kind() + && let Some(ty::AssocItem { fn_has_self_parameter: true, .. }) = + tcx.opt_associated_item(def_id) + && let [Spanned { node: Operand::Move(self_place) | Operand::Copy(self_place), .. }, ..] = + **args + { + if self_place.as_local() == Some(local) { + return Some((def_id, fn_args)); + } + + // Handle the case where `self_place` gets reborrowed. + // This happens when the receiver is `&T`. + for stmt in &body[block].statements { + if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind + && let Some(reborrow_local) = place.as_local() + && self_place.as_local() == Some(reborrow_local) + && let Rvalue::Ref(_, _, deref_place) = rvalue + && let PlaceRef { local: deref_local, projection: [ProjectionElem::Deref] } = + deref_place.as_ref() + && deref_local == local + { + return Some((def_id, fn_args)); + } + } + } + None +} + // Some nodes are used a lot. Make sure they don't unintentionally get bigger. #[cfg(target_pointer_width = "64")] mod size_asserts { diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 27168b2a9f246..d4a9aac3733ff 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -1,6 +1,7 @@ use std::fmt; use std::hash::Hash; +use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_attr_parsing::InlineAttr; use rustc_data_structures::base_n::{BaseNString, CASE_INSENSITIVE, ToBaseN}; use rustc_data_structures::fingerprint::Fingerprint; @@ -8,7 +9,7 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher, ToStableHashKey}; use rustc_data_structures::unord::UnordMap; use rustc_hir::ItemId; -use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdSet, LOCAL_CRATE}; use rustc_index::Idx; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_query_system::ich::StableHashingContext; @@ -91,56 +92,89 @@ impl<'tcx> MonoItem<'tcx> { } pub fn instantiation_mode(&self, tcx: TyCtxt<'tcx>) -> InstantiationMode { - let generate_cgu_internal_copies = tcx - .sess - .opts - .unstable_opts - .inline_in_all_cgus - .unwrap_or_else(|| tcx.sess.opts.optimize != OptLevel::No) - && !tcx.sess.link_dead_code(); + // The case handling here is written in the same style as cross_crate_inlinable, we first + // handle the cases where we must use a particular instantiation mode, then cascade down + // through a sequence of heuristics. - match *self { - MonoItem::Fn(ref instance) => { - let entry_def_id = tcx.entry_fn(()).map(|(id, _)| id); - // If this function isn't inlined or otherwise has an extern - // indicator, then we'll be creating a globally shared version. - let codegen_fn_attrs = tcx.codegen_fn_attrs(instance.def_id()); - if codegen_fn_attrs.contains_extern_indicator() - || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) - || !instance.def.generates_cgu_internal_copy(tcx) - || Some(instance.def_id()) == entry_def_id - { - return InstantiationMode::GloballyShared { may_conflict: false }; - } - - if let InlineAttr::Never = tcx.codegen_fn_attrs(instance.def_id()).inline - && self.is_generic_fn() - { - // Upgrade inline(never) to a globally shared instance. - return InstantiationMode::GloballyShared { may_conflict: true }; - } - - // At this point we don't have explicit linkage and we're an - // inlined function. If we're inlining into all CGUs then we'll - // be creating a local copy per CGU. - if generate_cgu_internal_copies { - return InstantiationMode::LocalCopy; - } - - // Finally, if this is `#[inline(always)]` we're sure to respect - // that with an inline copy per CGU, but otherwise we'll be - // creating one copy of this `#[inline]` function which may - // conflict with upstream crates as it could be an exported - // symbol. - match tcx.codegen_fn_attrs(instance.def_id()).inline { - InlineAttr::Always => InstantiationMode::LocalCopy, - _ => InstantiationMode::GloballyShared { may_conflict: true }, - } - } + // The first thing we do is detect MonoItems which we must instantiate exactly once in the + // whole program. + + // Statics and global_asm! must be instantiated exactly once. + let instance = match *self { + MonoItem::Fn(instance) => instance, MonoItem::Static(..) | MonoItem::GlobalAsm(..) => { - InstantiationMode::GloballyShared { may_conflict: false } + return InstantiationMode::GloballyShared { may_conflict: false }; + } + }; + + // Similarly, the executable entrypoint must be instantiated exactly once. + if let Some((entry_def_id, _)) = tcx.entry_fn(()) { + if instance.def_id() == entry_def_id { + return InstantiationMode::GloballyShared { may_conflict: false }; } } + + // If the function is #[naked] or contains any other attribute that requires exactly-once + // instantiation: + let codegen_fn_attrs = tcx.codegen_fn_attrs(instance.def_id()); + if codegen_fn_attrs.contains_extern_indicator() + || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) + { + return InstantiationMode::GloballyShared { may_conflict: false }; + } + + // FIXME: The logic for which functions are permitted to get LocalCopy is actually spread + // across 4 functions: + // * cross_crate_inlinable(def_id) + // * InstanceKind::requires_inline + // * InstanceKind::generate_cgu_internal_copy + // * MonoItem::instantiation_mode + // Since reachable_non_generics calls InstanceKind::generates_cgu_internal_copy to decide + // which symbols this crate exports, we are obligated to only generate LocalCopy when + // generates_cgu_internal_copy returns true. + if !instance.def.generates_cgu_internal_copy(tcx) { + return InstantiationMode::GloballyShared { may_conflict: false }; + } + + // Beginning of heuristics. The handling of link-dead-code and inline(always) are QoL only, + // the compiler should not crash and linkage should work, but codegen may be undesirable. + + // -Clink-dead-code was given an unfortunate name; the point of the flag is to assist + // coverage tools which rely on having every function in the program appear in the + // generated code. If we select LocalCopy, functions which are not used because they are + // missing test coverage will disappear from such coverage reports, defeating the point. + // Note that -Cinstrument-coverage does not require such assistance from us, only coverage + // tools implemented without compiler support ironically require a special compiler flag. + if tcx.sess.link_dead_code() { + return InstantiationMode::GloballyShared { may_conflict: true }; + } + + // To ensure that #[inline(always)] can be inlined as much as possible, especially in unoptimized + // builds, we always select LocalCopy. + if codegen_fn_attrs.inline.always() { + return InstantiationMode::LocalCopy; + } + + // #[inline(never)] functions in general are poor candidates for inlining and thus since + // LocalCopy generally increases code size for the benefit of optimizations from inlining, + // we want to give them GloballyShared codegen. + // The slight problem is that generic functions need to always support cross-crate + // compilation, so all previous stages of the compiler are obligated to treat generic + // functions the same as those that unconditionally get LocalCopy codegen. It's only when + // we get here that we can at least not codegen a #[inline(never)] generic function in all + // of our CGUs. + if let InlineAttr::Never = tcx.codegen_fn_attrs(instance.def_id()).inline + && self.is_generic_fn() + { + return InstantiationMode::GloballyShared { may_conflict: true }; + } + + // The fallthrough case is to generate LocalCopy for all optimized builds, and + // GloballyShared with conflict prevention when optimizations are disabled. + match tcx.sess.opts.optimize { + OptLevel::No => InstantiationMode::GloballyShared { may_conflict: true }, + _ => InstantiationMode::LocalCopy, + } } pub fn explicit_linkage(&self, tcx: TyCtxt<'tcx>) -> Option { @@ -246,6 +280,13 @@ impl ToStableHashKey> for MonoItem<'_> { } } +#[derive(Debug, HashStable, Copy, Clone)] +pub struct MonoItemPartitions<'tcx> { + pub codegen_units: &'tcx [CodegenUnit<'tcx>], + pub all_mono_items: &'tcx DefIdSet, + pub autodiff_items: &'tcx [AutoDiffItem], +} + #[derive(Debug, HashStable)] pub struct CodegenUnit<'tcx> { /// A name for this CGU. Incremental compilation requires that @@ -286,9 +327,7 @@ pub enum Linkage { LinkOnceODR, WeakAny, WeakODR, - Appending, Internal, - Private, ExternalWeak, Common, } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 47522f00bb1b3..b0df6c71014a3 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1,21 +1,20 @@ use std::collections::BTreeSet; use std::fmt::{Display, Write as _}; -use std::fs; -use std::io::{self, Write as _}; use std::path::{Path, PathBuf}; +use std::{fs, io}; use rustc_abi::Size; use rustc_ast::InlineAsmTemplatePiece; -use rustc_middle::mir::interpret::{ - AllocBytes, AllocId, Allocation, GlobalAlloc, Pointer, Provenance, alloc_range, - read_target_uint, -}; -use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::*; use tracing::trace; +use ty::print::PrettyPrinter; use super::graphviz::write_mir_fn_graphviz; -use crate::mir::interpret::ConstAllocation; +use crate::mir::interpret::{ + AllocBytes, AllocId, Allocation, ConstAllocation, GlobalAlloc, Pointer, Provenance, + alloc_range, read_target_uint, +}; +use crate::mir::visit::Visitor; +use crate::mir::*; const INDENT: &str = " "; /// Alignment for lining up comments following MIR statements @@ -149,37 +148,59 @@ pub fn dump_enabled(tcx: TyCtxt<'_>, pass_name: &str, def_id: DefId) -> bool { // `def_path_str()` would otherwise trigger `type_of`, and this can // run while we are already attempting to evaluate `type_of`. +/// Most use-cases of dumping MIR should use the [dump_mir] entrypoint instead, which will also +/// check if dumping MIR is enabled, and if this body matches the filters passed on the CLI. +/// +/// That being said, if the above requirements have been validated already, this function is where +/// most of the MIR dumping occurs, if one needs to export it to a file they have created with +/// [create_dump_file], rather than to a new file created as part of [dump_mir], or to stdout/stderr +/// for debugging purposes. +pub fn dump_mir_to_writer<'tcx, F>( + tcx: TyCtxt<'tcx>, + pass_name: &str, + disambiguator: &dyn Display, + body: &Body<'tcx>, + w: &mut dyn io::Write, + mut extra_data: F, + options: PrettyPrintMirOptions, +) -> io::Result<()> +where + F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>, +{ + // see notes on #41697 above + let def_path = + ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id())); + // ignore-tidy-odd-backticks the literal below is fine + write!(w, "// MIR for `{def_path}")?; + match body.source.promoted { + None => write!(w, "`")?, + Some(promoted) => write!(w, "::{promoted:?}`")?, + } + writeln!(w, " {disambiguator} {pass_name}")?; + if let Some(ref layout) = body.coroutine_layout_raw() { + writeln!(w, "/* coroutine_layout = {layout:#?} */")?; + } + writeln!(w)?; + extra_data(PassWhere::BeforeCFG, w)?; + write_user_type_annotations(tcx, body, w)?; + write_mir_fn(tcx, body, &mut extra_data, w, options)?; + extra_data(PassWhere::AfterCFG, w) +} + fn dump_matched_mir_node<'tcx, F>( tcx: TyCtxt<'tcx>, pass_num: bool, pass_name: &str, disambiguator: &dyn Display, body: &Body<'tcx>, - mut extra_data: F, + extra_data: F, options: PrettyPrintMirOptions, ) where F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>, { let _: io::Result<()> = try { let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, body)?; - // see notes on #41697 above - let def_path = - ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id())); - // ignore-tidy-odd-backticks the literal below is fine - write!(file, "// MIR for `{def_path}")?; - match body.source.promoted { - None => write!(file, "`")?, - Some(promoted) => write!(file, "::{promoted:?}`")?, - } - writeln!(file, " {disambiguator} {pass_name}")?; - if let Some(ref layout) = body.coroutine_layout_raw() { - writeln!(file, "/* coroutine_layout = {layout:#?} */")?; - } - writeln!(file)?; - extra_data(PassWhere::BeforeCFG, &mut file)?; - write_user_type_annotations(tcx, body, &mut file)?; - write_mir_fn(tcx, body, &mut extra_data, &mut file, options)?; - extra_data(PassWhere::AfterCFG, &mut file)?; + dump_mir_to_writer(tcx, pass_name, disambiguator, body, &mut file, extra_data, options)?; }; if tcx.sess.opts.unstable_opts.dump_mir_graphviz { @@ -597,13 +618,9 @@ fn write_function_coverage_info( function_coverage_info: &coverage::FunctionCoverageInfo, w: &mut dyn io::Write, ) -> io::Result<()> { - let coverage::FunctionCoverageInfo { body_span, expressions, mappings, .. } = - function_coverage_info; + let coverage::FunctionCoverageInfo { body_span, mappings, .. } = function_coverage_info; writeln!(w, "{INDENT}coverage body span: {body_span:?}")?; - for (id, expression) in expressions.iter_enumerated() { - writeln!(w, "{INDENT}coverage {id:?} => {expression:?};")?; - } for coverage::Mapping { kind, span } in mappings { writeln!(w, "{INDENT}coverage {kind:?} => {span:?};")?; } @@ -1068,6 +1085,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { pretty_print_const(b, fmt, false)?; write!(fmt, "]") } + Len(ref a) => write!(fmt, "Len({a:?})"), Cast(ref kind, ref place, ref ty) => { with_no_trimmed_paths!(write!(fmt, "{place:?} as {ty} ({kind:?})")) } @@ -1081,6 +1099,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { NullOp::AlignOf => write!(fmt, "AlignOf({t})"), NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"), NullOp::UbChecks => write!(fmt, "UbChecks()"), + NullOp::ContractChecks => write!(fmt, "ContractChecks()"), } } ThreadLocalRef(did) => ty::tls::with(|tcx| { @@ -1226,6 +1245,10 @@ impl<'tcx> Debug for Rvalue<'tcx> { ShallowInitBox(ref place, ref ty) => { with_no_trimmed_paths!(write!(fmt, "ShallowInitBox({place:?}, {ty})")) } + + WrapUnsafeBinder(ref op, ty) => { + with_no_trimmed_paths!(write!(fmt, "wrap_binder!({op:?}; {ty})")) + } } } } @@ -1286,6 +1309,9 @@ fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {} + ProjectionElem::UnwrapUnsafeBinder(_) => { + write!(fmt, "unwrap_binder!(")?; + } } } @@ -1334,6 +1360,9 @@ fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> ProjectionElem::Subslice { from, to, from_end: false } => { write!(fmt, "[{from:?}..{to:?}]")?; } + ProjectionElem::UnwrapUnsafeBinder(ty) => { + write!(fmt, "; {ty})")?; + } } } @@ -1407,10 +1436,10 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { }) }; - // FIXME: call pretty_print_const_valtree? - let fmt_valtree = |valtree: &ty::ValTree<'tcx>| match valtree { - ty::ValTree::Leaf(leaf) => format!("Leaf({leaf:?})"), - ty::ValTree::Branch(_) => "Branch(..)".to_string(), + let fmt_valtree = |cv: &ty::Value<'tcx>| { + let mut cx = FmtPrinter::new(self.tcx, Namespace::ValueNS); + cx.pretty_print_const_valtree(*cv, /*print_ty*/ true).unwrap(); + cx.into_buffer() }; let val = match const_ { @@ -1419,7 +1448,9 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { ty::ConstKind::Unevaluated(uv) => { format!("ty::Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.args,) } - ty::ConstKind::Value(_, val) => format!("ty::Valtree({})", fmt_valtree(&val)), + ty::ConstKind::Value(cv) => { + format!("ty::Valtree({})", fmt_valtree(&cv)) + } // No `ty::` prefix since we also use this to represent errors from `mir::Unevaluated`. ty::ConstKind::Error(_) => "Error".to_string(), // These variants shouldn't exist in the MIR. @@ -1555,16 +1586,22 @@ pub fn write_allocations<'tcx>( write!(w, " (vtable: impl {dyn_ty} for {ty})")? } Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => { - match tcx.eval_static_initializer(did) { - Ok(alloc) => { - write!(w, " (static: {}, ", tcx.def_path_str(did))?; - write_allocation_track_relocs(w, alloc)?; + write!(w, " (static: {}", tcx.def_path_str(did))?; + if body.phase <= MirPhase::Runtime(RuntimePhase::PostCleanup) + && tcx.hir().body_const_context(body.source.def_id()).is_some() + { + // Statics may be cyclic and evaluating them too early + // in the MIR pipeline may cause cycle errors even though + // normal compilation is fine. + write!(w, ")")?; + } else { + match tcx.eval_static_initializer(did) { + Ok(alloc) => { + write!(w, ", ")?; + write_allocation_track_relocs(w, alloc)?; + } + Err(_) => write!(w, ", error during initializer evaluation)")?, } - Err(_) => write!( - w, - " (static: {}, error during initializer evaluation)", - tcx.def_path_str(did) - )?, } } Some(GlobalAlloc::Static(did)) => { diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 429be9bc725be..50494355e3e49 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -1,9 +1,7 @@ //! Values computed by queries that use MIR. -use std::cell::Cell; use std::fmt::{self, Debug}; -use derive_where::derive_where; use rustc_abi::{FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; @@ -63,55 +61,26 @@ pub struct CoroutineLayout<'tcx> { impl Debug for CoroutineLayout<'_> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - /// Prints an iterator of (key, value) tuples as a map. - struct MapPrinter<'a, K, V>(Cell + 'a>>>); - impl<'a, K, V> MapPrinter<'a, K, V> { - fn new(iter: impl Iterator + 'a) -> Self { - Self(Cell::new(Some(Box::new(iter)))) - } - } - impl<'a, K: Debug, V: Debug> Debug for MapPrinter<'a, K, V> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_map().entries(self.0.take().unwrap()).finish() - } - } - - /// Prints the coroutine variant name. - struct GenVariantPrinter(VariantIdx); - impl From for GenVariantPrinter { - fn from(idx: VariantIdx) -> Self { - GenVariantPrinter(idx) - } - } - impl Debug for GenVariantPrinter { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let variant_name = ty::CoroutineArgs::variant_name(self.0); - if fmt.alternate() { - write!(fmt, "{:9}({:?})", variant_name, self.0) - } else { - write!(fmt, "{variant_name}") - } - } - } - - /// Forces its contents to print in regular mode instead of alternate mode. - struct OneLinePrinter(T); - impl Debug for OneLinePrinter { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "{:?}", self.0) - } - } - fmt.debug_struct("CoroutineLayout") - .field("field_tys", &MapPrinter::new(self.field_tys.iter_enumerated())) - .field( - "variant_fields", - &MapPrinter::new( - self.variant_fields - .iter_enumerated() - .map(|(k, v)| (GenVariantPrinter(k), OneLinePrinter(v))), - ), - ) + .field_with("field_tys", |fmt| { + fmt.debug_map().entries(self.field_tys.iter_enumerated()).finish() + }) + .field_with("variant_fields", |fmt| { + let mut map = fmt.debug_map(); + for (idx, fields) in self.variant_fields.iter_enumerated() { + map.key_with(|fmt| { + let variant_name = ty::CoroutineArgs::variant_name(idx); + if fmt.alternate() { + write!(fmt, "{variant_name:9}({idx:?})") + } else { + write!(fmt, "{variant_name}") + } + }); + // Force variant fields to print in regular mode instead of alternate mode. + map.value_with(|fmt| write!(fmt, "{fields:?}")); + } + map.finish() + }) .field("storage_conflicts", &self.storage_conflicts) .finish() } @@ -225,29 +194,22 @@ rustc_data_structures::static_assert_size!(ConstraintCategory<'_>, 16); /// See also `rustc_const_eval::borrow_check::constraints`. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] #[derive(TyEncodable, TyDecodable, HashStable, TypeVisitable, TypeFoldable)] -#[derive_where(PartialOrd, Ord)] pub enum ConstraintCategory<'tcx> { Return(ReturnConstraint), Yield, UseAsConst, UseAsStatic, - TypeAnnotation, + TypeAnnotation(AnnotationSource), Cast { /// Whether this cast is a coercion that was automatically inserted by the compiler. is_implicit_coercion: bool, /// Whether this is an unsizing coercion and if yes, this contains the target type. /// Region variables are erased to ReErased. - #[derive_where(skip)] unsize_to: Option>, }, - /// A constraint that came from checking the body of a closure. - /// - /// We try to get the category that the closure used when reporting this. - ClosureBounds, - /// Contains the function type if available. - CallArgument(#[derive_where(skip)] Option>), + CallArgument(Option>), CopyBound, SizedBound, Assignment, @@ -276,13 +238,22 @@ pub enum ConstraintCategory<'tcx> { IllegalUniverse, } -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] #[derive(TyEncodable, TyDecodable, HashStable, TypeVisitable, TypeFoldable)] pub enum ReturnConstraint { Normal, ClosureUpvar(FieldIdx), } +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +#[derive(TyEncodable, TyDecodable, HashStable, TypeVisitable, TypeFoldable)] +pub enum AnnotationSource { + Ascription, + Declaration, + OpaqueCast, + GenericArg, +} + /// The subject of a `ClosureOutlivesRequirement` -- that is, the thing /// that must outlive some region. #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index da3fa9e324a4d..d345c99f902f2 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -62,7 +62,8 @@ impl ProjectionElem { | Self::Subtype(_) | Self::ConstantIndex { .. } | Self::Subslice { .. } - | Self::Downcast(_, _) => false, + | Self::Downcast(_, _) + | Self::UnwrapUnsafeBinder(..) => false, } } @@ -76,7 +77,8 @@ impl ProjectionElem { | Self::Subtype(_) | Self::ConstantIndex { .. } | Self::Subslice { .. } - | Self::Downcast(_, _) => true, + | Self::Downcast(_, _) + | Self::UnwrapUnsafeBinder(..) => true, } } @@ -102,6 +104,9 @@ impl ProjectionElem { | Self::Subtype(_) | Self::OpaqueCast(_) | Self::Subslice { .. } => false, + + // FIXME(unsafe_binders): Figure this out. + Self::UnwrapUnsafeBinder(..) => false, } } } @@ -424,6 +429,7 @@ impl<'tcx> Rvalue<'tcx> { | Rvalue::Ref(_, _, _) | Rvalue::ThreadLocalRef(_) | Rvalue::RawPtr(_, _) + | Rvalue::Len(_) | Rvalue::Cast( CastKind::IntToInt | CastKind::FloatToInt @@ -442,7 +448,8 @@ impl<'tcx> Rvalue<'tcx> { | Rvalue::UnaryOp(_, _) | Rvalue::Discriminant(_) | Rvalue::Aggregate(_, _) - | Rvalue::ShallowInitBox(_, _) => true, + | Rvalue::ShallowInitBox(_, _) + | Rvalue::WrapUnsafeBinder(_, _) => true, } } } @@ -455,6 +462,8 @@ impl BorrowKind { } } + /// Returns whether borrows represented by this kind are allowed to be split into separate + /// Reservation and Activation phases. pub fn allows_two_phase_borrow(&self) -> bool { match *self { BorrowKind::Shared diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index bbbaffc5a35cc..9cec8d832dd14 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -180,6 +180,59 @@ pub enum BorrowKind { Mut { kind: MutBorrowKind }, } +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TyEncodable, TyDecodable)] +#[derive(Hash, HashStable)] +pub enum RawPtrKind { + Mut, + Const, + /// Creates a raw pointer to a place that will only be used to access its metadata, + /// not the data behind the pointer. Note that this limitation is *not* enforced + /// by the validator. + /// + /// The borrow checker allows overlap of these raw pointers with references to the + /// data. This is sound even if the pointer is "misused" since any such use is anyway + /// unsafe. In terms of the operational semantics (i.e., Miri), this is equivalent + /// to `RawPtrKind::Mut`, but will never incur a retag. + FakeForPtrMetadata, +} + +impl From for RawPtrKind { + fn from(other: Mutability) -> Self { + match other { + Mutability::Mut => RawPtrKind::Mut, + Mutability::Not => RawPtrKind::Const, + } + } +} + +impl RawPtrKind { + pub fn is_fake(self) -> bool { + match self { + RawPtrKind::Mut | RawPtrKind::Const => false, + RawPtrKind::FakeForPtrMetadata => true, + } + } + + pub fn to_mutbl_lossy(self) -> Mutability { + match self { + RawPtrKind::Mut => Mutability::Mut, + RawPtrKind::Const => Mutability::Not, + + // We have no type corresponding to a fake borrow, so use + // `*const` as an approximation. + RawPtrKind::FakeForPtrMetadata => Mutability::Not, + } + } + + pub fn ptr_str(self) -> &'static str { + match self { + RawPtrKind::Mut => "mut", + RawPtrKind::Const => "const", + RawPtrKind::FakeForPtrMetadata => "const (fake)", + } + } +} + #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TyEncodable, TyDecodable)] #[derive(Hash, HashStable)] pub enum MutBorrowKind { @@ -417,7 +470,14 @@ pub enum StatementKind<'tcx> { /// /// Interpreters and codegen backends that don't support coverage instrumentation /// can usually treat this as a no-op. - Coverage(CoverageKind), + Coverage( + // Coverage statements are unlikely to ever contain type information in + // the foreseeable future, so excluding them from TypeFoldable/TypeVisitable + // avoids some unhelpful derive boilerplate. + #[type_foldable(identity)] + #[type_visitable(ignore)] + CoverageKind, + ), /// Denotes a call to an intrinsic that does not require an unwind path and always returns. /// This avoids adding a new block and a terminator for simple intrinsics. @@ -1016,6 +1076,7 @@ pub enum AssertKind { ResumedAfterReturn(CoroutineKind), ResumedAfterPanic(CoroutineKind), MisalignedPointerDereference { required: O, found: O }, + NullPointerDereference, } #[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] @@ -1215,6 +1276,10 @@ pub enum ProjectionElem { /// requiring an intermediate variable. OpaqueCast(T), + /// A transmute from an unsafe binder to the type that it wraps. This is a projection + /// of a place, so it doesn't necessarily constitute a move out of the binder. + UnwrapUnsafeBinder(T), + /// A `Subtype(T)` projection is applied to any `StatementKind::Assign` where /// type of lvalue doesn't match the type of rvalue, the primary goal is making subtyping /// explicit during optimizations and codegen. @@ -1349,7 +1414,17 @@ pub enum Rvalue<'tcx> { /// /// Like with references, the semantics of this operation are heavily dependent on the aliasing /// model. - RawPtr(Mutability, Place<'tcx>), + RawPtr(RawPtrKind, Place<'tcx>), + + /// Yields the length of the place, as a `usize`. + /// + /// If the type of the place is an array, this is the array length. For slices (`[T]`, not + /// `&[T]`) this accesses the place's metadata to determine the length. This rvalue is + /// ill-formed for places of other types. + /// + /// This cannot be a `UnOp(PtrMetadata, _)` because that expects a value, and we only + /// have a place, and `UnOp(PtrMetadata, RawPtr(place))` is not a thing. + Len(Place<'tcx>), /// Performs essentially all of the casts that can be performed via `as`. /// @@ -1422,6 +1497,9 @@ pub enum Rvalue<'tcx> { /// optimizations and codegen backends that previously had to handle deref operations anywhere /// in a place. CopyForDeref(Place<'tcx>), + + /// Wraps a value in an unsafe binder. + WrapUnsafeBinder(Operand<'tcx>, Ty<'tcx>), } #[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] @@ -1513,6 +1591,9 @@ pub enum NullOp<'tcx> { /// Returns whether we should perform some UB-checking at runtime. /// See the `ub_checks` intrinsic docs for details. UbChecks, + /// Returns whether we should perform contract-checking at runtime. + /// See the `contract_checks` intrinsic docs for details. + ContractChecks, } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index cbb26b83c79cf..af23c8b2ea76d 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -146,6 +146,11 @@ impl<'tcx> PlaceTy<'tcx> { ProjectionElem::Subtype(ty) => { PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty)) } + + // FIXME(unsafe_binders): Rename `handle_opaque_cast_and_subtype` to be more general. + ProjectionElem::UnwrapUnsafeBinder(ty) => { + PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty)) + } }; debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer); answer @@ -206,10 +211,11 @@ impl<'tcx> Rvalue<'tcx> { let place_ty = place.ty(local_decls, tcx).ty; Ty::new_ref(tcx, reg, place_ty, bk.to_mutbl_lossy()) } - Rvalue::RawPtr(mutability, ref place) => { + Rvalue::RawPtr(kind, ref place) => { let place_ty = place.ty(local_decls, tcx).ty; - Ty::new_ptr(tcx, place_ty, mutability) + Ty::new_ptr(tcx, place_ty, kind.to_mutbl_lossy()) } + Rvalue::Len(..) => tcx.types.usize, Rvalue::Cast(.., ty) => ty, Rvalue::BinaryOp(op, box (ref lhs, ref rhs)) => { let lhs_ty = lhs.ty(local_decls, tcx); @@ -224,7 +230,8 @@ impl<'tcx> Rvalue<'tcx> { Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { tcx.types.usize } - Rvalue::NullaryOp(NullOp::UbChecks, _) => tcx.types.bool, + Rvalue::NullaryOp(NullOp::ContractChecks, _) + | Rvalue::NullaryOp(NullOp::UbChecks, _) => tcx.types.bool, Rvalue::Aggregate(ref ak, ref ops) => match **ak { AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64), AggregateKind::Tuple => { @@ -240,6 +247,7 @@ impl<'tcx> Rvalue<'tcx> { }, Rvalue::ShallowInitBox(_, ty) => Ty::new_box(tcx, ty), Rvalue::CopyForDeref(ref place) => place.ty(local_decls, tcx).ty, + Rvalue::WrapUnsafeBinder(_, ty) => ty, } } diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index 473b817aed076..9357e19f7c579 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -206,6 +206,7 @@ impl AssertKind { ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => { LangItem::PanicGenFnNonePanic } + NullPointerDereference => LangItem::PanicNullPointerDereference, BoundsCheck { .. } | MisalignedPointerDereference { .. } => { bug!("Unexpected AssertKind") @@ -271,6 +272,7 @@ impl AssertKind { "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {required:?}, {found:?}" ) } + NullPointerDereference => write!(f, "\"null pointer dereference occurred\""), ResumedAfterReturn(CoroutineKind::Coroutine(_)) => { write!(f, "\"coroutine resumed after completion\"") } @@ -341,7 +343,7 @@ impl AssertKind { ResumedAfterPanic(CoroutineKind::Coroutine(_)) => { middle_assert_coroutine_resume_after_panic } - + NullPointerDereference => middle_assert_null_ptr_deref, MisalignedPointerDereference { .. } => middle_assert_misaligned_ptr_deref, } } @@ -374,7 +376,7 @@ impl AssertKind { add!("left", format!("{left:#?}")); add!("right", format!("{right:#?}")); } - ResumedAfterReturn(_) | ResumedAfterPanic(_) => {} + ResumedAfterReturn(_) | ResumedAfterPanic(_) | NullPointerDereference => {} MisalignedPointerDereference { required, found } => { add!("required", format!("{required:#?}")); add!("found", format!("{found:#?}")); @@ -581,9 +583,11 @@ impl<'tcx> TerminatorKind<'tcx> { pub enum TerminatorEdges<'mir, 'tcx> { /// For terminators that have no successor, like `return`. None, - /// For terminators that a single successor, like `goto`, and `assert` without cleanup block. + /// For terminators that have a single successor, like `goto`, and `assert` without a cleanup + /// block. Single(BasicBlock), - /// For terminators that two successors, `assert` with cleanup block and `falseEdge`. + /// For terminators that have two successors, like `assert` with a cleanup block, and + /// `falseEdge`. Double(BasicBlock, BasicBlock), /// Special action for `Yield`, `Call` and `InlineAsm` terminators. AssignOnReturn { diff --git a/compiler/rustc_middle/src/mir/traversal.rs b/compiler/rustc_middle/src/mir/traversal.rs index b8b74da401c9f..0e7dcc24dafa6 100644 --- a/compiler/rustc_middle/src/mir/traversal.rs +++ b/compiler/rustc_middle/src/mir/traversal.rs @@ -21,7 +21,7 @@ use super::*; #[derive(Clone)] pub struct Preorder<'a, 'tcx> { body: &'a Body<'tcx>, - visited: BitSet, + visited: DenseBitSet, worklist: Vec, root_is_start_block: bool, } @@ -32,7 +32,7 @@ impl<'a, 'tcx> Preorder<'a, 'tcx> { Preorder { body, - visited: BitSet::new_empty(body.basic_blocks.len()), + visited: DenseBitSet::new_empty(body.basic_blocks.len()), worklist, root_is_start_block: root == START_BLOCK, } @@ -106,7 +106,7 @@ impl<'a, 'tcx> Iterator for Preorder<'a, 'tcx> { /// A Postorder traversal of this graph is `D B C A` or `D C B A` pub struct Postorder<'a, 'tcx, C> { basic_blocks: &'a IndexSlice>, - visited: BitSet, + visited: DenseBitSet, visit_stack: Vec<(BasicBlock, Successors<'a>)>, root_is_start_block: bool, extra: C, @@ -123,7 +123,7 @@ where ) -> Postorder<'a, 'tcx, C> { let mut po = Postorder { basic_blocks, - visited: BitSet::new_empty(basic_blocks.len()), + visited: DenseBitSet::new_empty(basic_blocks.len()), visit_stack: Vec::new(), root_is_start_block: root == START_BLOCK, extra, @@ -285,8 +285,8 @@ pub fn reachable<'a, 'tcx>( preorder(body) } -/// Returns a `BitSet` containing all basic blocks reachable from the `START_BLOCK`. -pub fn reachable_as_bitset(body: &Body<'_>) -> BitSet { +/// Returns a `DenseBitSet` containing all basic blocks reachable from the `START_BLOCK`. +pub fn reachable_as_bitset(body: &Body<'_>) -> DenseBitSet { let mut iter = preorder(body); while let Some(_) = iter.next() {} iter.visited @@ -340,13 +340,13 @@ pub fn mono_reachable<'a, 'tcx>( MonoReachable::new(body, tcx, instance) } -/// [`MonoReachable`] internally accumulates a [`BitSet`] of visited blocks. This is just a +/// [`MonoReachable`] internally accumulates a [`DenseBitSet`] of visited blocks. This is just a /// convenience function to run that traversal then extract its set of reached blocks. pub fn mono_reachable_as_bitset<'a, 'tcx>( body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, -) -> BitSet { +) -> DenseBitSet { let mut iter = mono_reachable(body, tcx, instance); while let Some(_) = iter.next() {} iter.visited @@ -356,11 +356,11 @@ pub struct MonoReachable<'a, 'tcx> { body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, - visited: BitSet, + visited: DenseBitSet, // Other traversers track their worklist in a Vec. But we don't care about order, so we can - // store ours in a BitSet and thus save allocations because BitSet has a small size + // store ours in a DenseBitSet and thus save allocations because DenseBitSet has a small size // optimization. - worklist: BitSet, + worklist: DenseBitSet, } impl<'a, 'tcx> MonoReachable<'a, 'tcx> { @@ -369,13 +369,13 @@ impl<'a, 'tcx> MonoReachable<'a, 'tcx> { tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, ) -> MonoReachable<'a, 'tcx> { - let mut worklist = BitSet::new_empty(body.basic_blocks.len()); + let mut worklist = DenseBitSet::new_empty(body.basic_blocks.len()); worklist.insert(START_BLOCK); MonoReachable { body, tcx, instance, - visited: BitSet::new_empty(body.basic_blocks.len()), + visited: DenseBitSet::new_empty(body.basic_blocks.len()), worklist, } } diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs deleted file mode 100644 index b798f0788007f..0000000000000 --- a/compiler/rustc_middle/src/mir/type_foldable.rs +++ /dev/null @@ -1,64 +0,0 @@ -//! `TypeFoldable` implementations for MIR types - -use rustc_ast::InlineAsmTemplatePiece; -use rustc_hir::def_id::LocalDefId; - -use super::*; - -TrivialTypeTraversalImpls! { - BlockTailInfo, - MirPhase, - SourceInfo, - FakeReadCause, - RetagKind, - SourceScope, - SourceScopeLocalData, - UserTypeAnnotationIndex, - BorrowKind, - CastKind, - BasicBlock, - SwitchTargets, - CoroutineKind, - CoroutineSavedLocal, -} - -TrivialTypeTraversalImpls! { - ConstValue<'tcx>, - NullOp<'tcx>, -} - -impl<'tcx> TypeFoldable> for &'tcx [InlineAsmTemplatePiece] { - fn try_fold_with>>( - self, - _folder: &mut F, - ) -> Result { - Ok(self) - } -} - -impl<'tcx> TypeFoldable> for &'tcx [Span] { - fn try_fold_with>>( - self, - _folder: &mut F, - ) -> Result { - Ok(self) - } -} - -impl<'tcx> TypeFoldable> for &'tcx ty::List { - fn try_fold_with>>( - self, - _folder: &mut F, - ) -> Result { - Ok(self) - } -} - -impl<'tcx> TypeFoldable> for &'tcx ty::List> { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - ty::util::fold_list(self, folder, |tcx, v| tcx.mk_place_elems(v)) - } -} diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 12a024a219e8e..8ad88fbda7c89 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -527,8 +527,9 @@ macro_rules! make_mir_visitor { target: _, unwind: _, call_source: _, - fn_span: _ + fn_span, } => { + self.visit_span($(& $mutability)? *fn_span); self.visit_operand(func, location); for arg in args { self.visit_operand(&$($mutability)? arg.node, location); @@ -543,8 +544,9 @@ macro_rules! make_mir_visitor { TerminatorKind::TailCall { func, args, - fn_span: _, + fn_span, } => { + self.visit_span($(& $mutability)? *fn_span); self.visit_operand(func, location); for arg in args { self.visit_operand(&$($mutability)? arg.node, location); @@ -636,7 +638,7 @@ macro_rules! make_mir_visitor { OverflowNeg(op) | DivisionByZero(op) | RemainderByZero(op) => { self.visit_operand(op, location); } - ResumedAfterReturn(_) | ResumedAfterPanic(_) => { + ResumedAfterReturn(_) | ResumedAfterPanic(_) | NullPointerDereference => { // Nothing to visit } MisalignedPointerDereference { required, found } => { @@ -685,16 +687,27 @@ macro_rules! make_mir_visitor { Rvalue::RawPtr(m, path) => { let ctx = match m { - Mutability::Mut => PlaceContext::MutatingUse( + RawPtrKind::Mut => PlaceContext::MutatingUse( MutatingUseContext::RawBorrow ), - Mutability::Not => PlaceContext::NonMutatingUse( + RawPtrKind::Const => PlaceContext::NonMutatingUse( NonMutatingUseContext::RawBorrow ), + RawPtrKind::FakeForPtrMetadata => PlaceContext::NonMutatingUse( + NonMutatingUseContext::Inspect + ), }; self.visit_place(path, ctx, location); } + Rvalue::Len(path) => { + self.visit_place( + path, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect), + location + ); + } + Rvalue::Cast(_cast_kind, operand, ty) => { self.visit_operand(operand, location); self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); @@ -770,6 +783,11 @@ macro_rules! make_mir_visitor { self.visit_operand(operand, location); self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); } + + Rvalue::WrapUnsafeBinder(op, ty) => { + self.visit_operand(op, location); + self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); + } } } @@ -837,6 +855,8 @@ macro_rules! make_mir_visitor { local_info: _, } = local_decl; + self.visit_source_info(source_info); + self.visit_ty($(& $mutability)? *ty, TyContext::LocalDecl { local, source_info: *source_info, @@ -846,7 +866,6 @@ macro_rules! make_mir_visitor { self.visit_user_type_projection(user_ty); } } - self.visit_source_info(source_info); } fn super_var_debug_info( @@ -1140,6 +1159,11 @@ macro_rules! visit_place_fns { self.visit_ty(&mut new_ty, TyContext::Location(location)); if ty != new_ty { Some(PlaceElem::Subtype(new_ty)) } else { None } } + PlaceElem::UnwrapUnsafeBinder(ty) => { + let mut new_ty = ty; + self.visit_ty(&mut new_ty, TyContext::Location(location)); + if ty != new_ty { Some(PlaceElem::UnwrapUnsafeBinder(new_ty)) } else { None } + } PlaceElem::Deref | PlaceElem::ConstantIndex { .. } | PlaceElem::Subslice { .. } @@ -1208,7 +1232,8 @@ macro_rules! visit_place_fns { match elem { ProjectionElem::OpaqueCast(ty) | ProjectionElem::Subtype(ty) - | ProjectionElem::Field(_, ty) => { + | ProjectionElem::Field(_, ty) + | ProjectionElem::UnwrapUnsafeBinder(ty) => { self.visit_ty(ty, TyContext::Location(location)); } ProjectionElem::Index(local) => { diff --git a/compiler/rustc_middle/src/query/arena_cached.rs b/compiler/rustc_middle/src/query/arena_cached.rs new file mode 100644 index 0000000000000..ec6e466ff688c --- /dev/null +++ b/compiler/rustc_middle/src/query/arena_cached.rs @@ -0,0 +1,47 @@ +/// Helper trait that allows `arena_cache` queries to return `Option<&T>` +/// instead of `&Option`, and avoid allocating `None` in the arena. +/// +/// An arena-cached query must be declared to return a type that implements +/// this trait, i.e. either `&'tcx T` or `Option<&'tcx T>`. This trait then +/// determines the types returned by the provider and stored in the arena, +/// and provides a function to bridge between the three types. +pub trait ArenaCached<'tcx>: Sized { + /// Type that is returned by the query provider. + type Provided; + /// Type that is stored in the arena. + type Allocated: 'tcx; + + /// Takes a provided value, and allocates it in the arena (if appropriate) + /// with the help of the given `arena_alloc` closure. + fn alloc_in_arena( + arena_alloc: impl Fn(Self::Allocated) -> &'tcx Self::Allocated, + value: Self::Provided, + ) -> Self; +} + +impl<'tcx, T> ArenaCached<'tcx> for &'tcx T { + type Provided = T; + type Allocated = T; + + fn alloc_in_arena( + arena_alloc: impl Fn(Self::Allocated) -> &'tcx Self::Allocated, + value: Self::Provided, + ) -> Self { + // Just allocate in the arena normally. + arena_alloc(value) + } +} + +impl<'tcx, T> ArenaCached<'tcx> for Option<&'tcx T> { + type Provided = Option; + /// The provide value is `Option`, but we only store `T` in the arena. + type Allocated = T; + + fn alloc_in_arena( + arena_alloc: impl Fn(Self::Allocated) -> &'tcx Self::Allocated, + value: Self::Provided, + ) -> Self { + // Don't store None in the arena, and wrap the allocated reference in Some. + value.map(arena_alloc) + } +} diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index b72c0e776fe90..cbd60920bc572 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -101,9 +101,9 @@ impl EraseType for Result<&'_ T, &'_ ty::layout::FnAbiError<'_>> { type Result = [u8; size_of::>>()]; } -impl EraseType for Result<(&'_ T, rustc_middle::thir::ExprId), rustc_errors::ErrorGuaranteed> { +impl EraseType for Result<(&'_ T, crate::thir::ExprId), rustc_errors::ErrorGuaranteed> { type Result = [u8; size_of::< - Result<(&'static (), rustc_middle::thir::ExprId), rustc_errors::ErrorGuaranteed>, + Result<(&'static (), crate::thir::ExprId), rustc_errors::ErrorGuaranteed>, >()]; } @@ -141,14 +141,6 @@ impl EraseType for Result>, &ty::layout::Layou >()]; } -impl EraseType for Result, mir::interpret::LitToConstError> { - type Result = [u8; size_of::, mir::interpret::LitToConstError>>()]; -} - -impl EraseType for Result, mir::interpret::LitToConstError> { - type Result = [u8; size_of::, mir::interpret::LitToConstError>>()]; -} - impl EraseType for Result, mir::interpret::ErrorHandled> { type Result = [u8; size_of::, mir::interpret::ErrorHandled>>()]; } @@ -296,7 +288,6 @@ trivial! { rustc_middle::mir::interpret::AllocId, rustc_middle::mir::interpret::CtfeProvenance, rustc_middle::mir::interpret::ErrorHandled, - rustc_middle::mir::interpret::LitToConstError, rustc_middle::thir::ExprId, rustc_middle::traits::CodegenObligationError, rustc_middle::traits::EvaluationResult, @@ -358,6 +349,7 @@ tcx_lifetime! { rustc_middle::mir::interpret::GlobalId, rustc_middle::mir::interpret::LitToConstInput, rustc_middle::mir::interpret::EvalStaticInitializerRawResult, + rustc_middle::mir::mono::MonoItemPartitions, rustc_middle::traits::query::MethodAutoderefStepsResult, rustc_middle::traits::query::type_op::AscribeUserType, rustc_middle::traits::query::type_op::Eq, diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index e243425c0b7ba..1489d57aba645 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -20,6 +20,12 @@ pub struct LocalCrate; /// The `Key` trait controls what types can legally be used as the key /// for a query. pub trait Key: Sized { + /// The type of in-memory cache to use for queries with this key type. + /// + /// In practice the cache type must implement [`QueryCache`], though that + /// constraint is not enforced here. + /// + /// [`QueryCache`]: rustc_query_system::query::QueryCache // N.B. Most of the keys down below have `type Cache = DefaultCache;`, // it would be reasonable to use associated type defaults, to remove the duplication... // @@ -95,7 +101,7 @@ impl<'tcx> Key for mir::interpret::GlobalId<'tcx> { } } -impl<'tcx> Key for (Ty<'tcx>, Option>) { +impl<'tcx> Key for (Ty<'tcx>, Option>) { type Cache = DefaultCache; fn default_span(&self, _: TyCtxt<'_>) -> Span { @@ -550,7 +556,7 @@ impl<'tcx> Key for (ty::Instance<'tcx>, &'tcx ty::List>) { } } -impl<'tcx> Key for (Ty<'tcx>, ty::ValTree<'tcx>) { +impl<'tcx> Key for ty::Value<'tcx> { type Cache = DefaultCache; fn default_span(&self, _: TyCtxt<'_>) -> Span { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 7e7b602c560ba..cd81890598e73 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -7,7 +7,6 @@ #![allow(unused_parens)] use std::mem; -use std::ops::Deref; use std::path::PathBuf; use std::sync::Arc; @@ -19,12 +18,11 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::steal::Steal; use rustc_data_structures::svh::Svh; -use rustc_data_structures::sync::Lrc; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::{DefKind, DocLinkResMap}; use rustc_hir::def_id::{ - CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId, LocalDefIdMap, LocalDefIdSet, LocalModDefId, + CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap, LocalDefIdSet, LocalModDefId, }; use rustc_hir::lang_items::{LangItem, LanguageItems}; use rustc_hir::{Crate, ItemLocalId, ItemLocalMap, TraitCandidate}; @@ -57,9 +55,9 @@ use crate::middle::resolve_bound_vars::{ObjectLifetimeDefault, ResolveBoundVars, use crate::middle::stability::{self, DeprecationEntry}; use crate::mir::interpret::{ EvalStaticInitializerRawResult, EvalToAllocationRawResult, EvalToConstValueResult, - EvalToValTreeResult, GlobalId, LitToConstError, LitToConstInput, + EvalToValTreeResult, GlobalId, LitToConstInput, }; -use crate::mir::mono::{CodegenUnit, CollectionMode, MonoItem}; +use crate::mir::mono::{CodegenUnit, CollectionMode, MonoItem, MonoItemPartitions}; use crate::query::erase::{Erase, erase, restore}; use crate::query::plumbing::{ CyclePlaceholder, DynamicQuery, query_ensure, query_ensure_error_guaranteed, query_get_at, @@ -85,13 +83,14 @@ use crate::ty::{ }; use crate::{dep_graph, mir, thir}; +mod arena_cached; pub mod erase; mod keys; pub use keys::{AsLocalKey, Key, LocalCrate}; pub mod on_disk_cache; #[macro_use] pub mod plumbing; -pub use plumbing::{IntoQueryParam, TyCtxtAt, TyCtxtEnsure, TyCtxtEnsureWithValue}; +pub use plumbing::{IntoQueryParam, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk}; // Each of these queries corresponds to a function pointer field in the // `Providers` struct for requesting a value of that type, and a method @@ -117,7 +116,7 @@ rustc_queries! { } query early_lint_checks(_: ()) { - desc { "perform lints prior to macro expansion" } + desc { "perform lints prior to AST lowering" } } query resolutions(_: ()) -> &'tcx ty::ResolverGlobalCtxt { @@ -125,7 +124,7 @@ rustc_queries! { desc { "getting the resolver outputs" } } - query resolver_for_lowering_raw(_: ()) -> (&'tcx Steal<(ty::ResolverAstLowering, Lrc)>, &'tcx ty::ResolverGlobalCtxt) { + query resolver_for_lowering_raw(_: ()) -> (&'tcx Steal<(ty::ResolverAstLowering, Arc)>, &'tcx ty::ResolverGlobalCtxt) { eval_always no_hash desc { "getting the resolver for lowering" } @@ -297,7 +296,7 @@ rustc_queries! { separate_provide_extern } - query unsizing_params_for_adt(key: DefId) -> &'tcx rustc_index::bit_set::BitSet + query unsizing_params_for_adt(key: DefId) -> &'tcx rustc_index::bit_set::DenseBitSet { arena_cache desc { |tcx| @@ -393,7 +392,7 @@ rustc_queries! { /// like closure signature deduction. /// /// [explicit item bounds]: Self::explicit_item_bounds - query explicit_item_super_predicates(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { + query explicit_item_self_bounds(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern @@ -427,11 +426,11 @@ rustc_queries! { desc { |tcx| "elaborating item bounds for `{}`", tcx.def_path_str(key) } } - query item_super_predicates(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { + query item_self_bounds(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { desc { |tcx| "elaborating item assumptions for `{}`", tcx.def_path_str(key) } } - query item_non_self_assumptions(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { + query item_non_self_bounds(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { desc { |tcx| "elaborating item assumptions for `{}`", tcx.def_path_str(key) } } @@ -494,7 +493,7 @@ rustc_queries! { } /// Set of param indexes for type params that are in the type's representation - query params_in_repr(key: DefId) -> &'tcx rustc_index::bit_set::BitSet { + query params_in_repr(key: DefId) -> &'tcx rustc_index::bit_set::DenseBitSet { desc { "finding type parameters in the representation" } arena_cache no_hash @@ -586,7 +585,7 @@ rustc_queries! { separate_provide_extern } - query mir_coroutine_witnesses(key: DefId) -> &'tcx Option> { + query mir_coroutine_witnesses(key: DefId) -> Option<&'tcx mir::CoroutineLayout<'tcx>> { arena_cache desc { |tcx| "coroutine witness types for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } @@ -615,10 +614,19 @@ rustc_queries! { feedable } - /// Summarizes coverage IDs inserted by the `InstrumentCoverage` MIR pass - /// (for compiler option `-Cinstrument-coverage`), after MIR optimizations - /// have had a chance to potentially remove some of them. - query coverage_ids_info(key: ty::InstanceKind<'tcx>) -> &'tcx mir::coverage::CoverageIdsInfo { + /// Scans through a function's MIR after MIR optimizations, to prepare the + /// information needed by codegen when `-Cinstrument-coverage` is active. + /// + /// This includes the details of where to insert `llvm.instrprof.increment` + /// intrinsics, and the expression tables to be embedded in the function's + /// coverage metadata. + /// + /// FIXME(Zalathar): This query's purpose has drifted a bit and should + /// probably be renamed, but that can wait until after the potential + /// follow-ups to #136053 have settled down. + /// + /// Returns `None` for functions that were not instrumented. + query coverage_ids_info(key: ty::InstanceKind<'tcx>) -> Option<&'tcx mir::coverage::CoverageIdsInfo> { desc { |tcx| "retrieving coverage IDs info from MIR for `{}`", tcx.def_path_str(key.def_id()) } arena_cache } @@ -1085,7 +1093,7 @@ rustc_queries! { query check_mod_type_wf(key: LocalModDefId) -> Result<(), ErrorGuaranteed> { desc { |tcx| "checking that types are well-formed in {}", describe_as_module(key, tcx) } - ensure_forwards_result_if_red + return_result_from_ensure_ok } /// Caches `CoerceUnsized` kinds for impls on custom types. @@ -1093,16 +1101,13 @@ rustc_queries! { desc { |tcx| "computing CoerceUnsized info for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern - ensure_forwards_result_if_red + return_result_from_ensure_ok } query typeck(key: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> { desc { |tcx| "type-checking `{}`", tcx.def_path_str(key) } cache_on_disk_if(tcx) { !tcx.is_typeck_child(key.to_def_id()) } } - query diagnostic_only_typeck(key: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> { - desc { |tcx| "type-checking `{}`", tcx.def_path_str(key) } - } query used_trait_imports(key: LocalDefId) -> &'tcx UnordSet { desc { |tcx| "finding used_trait_imports `{}`", tcx.def_path_str(key) } @@ -1111,7 +1116,7 @@ rustc_queries! { query coherent_trait(def_id: DefId) -> Result<(), ErrorGuaranteed> { desc { |tcx| "coherence checking all impls of trait `{}`", tcx.def_path_str(def_id) } - ensure_forwards_result_if_red + return_result_from_ensure_ok } /// Borrow-checks the function body. If this is a closure, returns @@ -1141,7 +1146,7 @@ rustc_queries! { /// query crate_inherent_impls_validity_check(_: ()) -> Result<(), ErrorGuaranteed> { desc { "check for inherent impls that should not be defined in crate" } - ensure_forwards_result_if_red + return_result_from_ensure_ok } /// Checks all types in the crate for overlap in their inherent impls. Reports errors. @@ -1153,7 +1158,7 @@ rustc_queries! { /// query crate_inherent_impls_overlap_check(_: ()) -> Result<(), ErrorGuaranteed> { desc { "check for overlap between inherent impls defined in this crate" } - ensure_forwards_result_if_red + return_result_from_ensure_ok } /// Checks whether all impls in the crate pass the overlap check, returning @@ -1163,12 +1168,11 @@ rustc_queries! { "checking whether impl `{}` follows the orphan rules", tcx.def_path_str(key), } - ensure_forwards_result_if_red + return_result_from_ensure_ok } /// Check whether the function has any recursion that could cause the inliner to trigger - /// a cycle. Returns the call stack causing the cycle. The call stack does not contain the - /// current function, just all intermediate functions. + /// a cycle. query mir_callgraph_reachable(key: (ty::Instance<'tcx>, LocalDefId)) -> bool { fatal_cycle desc { |tcx| @@ -1246,6 +1250,7 @@ rustc_queries! { "simplifying constant for the type system `{}`", key.value.display(tcx) } + depth_limit cache_on_disk_if { true } } @@ -1257,9 +1262,9 @@ rustc_queries! { desc { "evaluating type-level constant" } } - /// Converts a type level constant value into `ConstValue` - query valtree_to_const_val(key: (Ty<'tcx>, ty::ValTree<'tcx>)) -> mir::ConstValue<'tcx> { - desc { "converting type-level constant value to mir constant value"} + /// Converts a type-level constant value into a MIR constant value. + query valtree_to_const_val(key: ty::Value<'tcx>) -> mir::ConstValue<'tcx> { + desc { "converting type-level constant value to MIR constant value"} } /// Destructures array, ADT or tuple constants into the constants @@ -1271,7 +1276,7 @@ rustc_queries! { // FIXME get rid of this with valtrees query lit_to_const( key: LitToConstInput<'tcx> - ) -> Result, LitToConstError> { + ) -> ty::Const<'tcx> { desc { "converting literal to const" } } @@ -1438,9 +1443,9 @@ rustc_queries! { desc { |tcx| "finding all existential vtable entries for trait `{}`", tcx.def_path_str(key) } } - query vtable_entries(key: ty::PolyTraitRef<'tcx>) + query vtable_entries(key: ty::TraitRef<'tcx>) -> &'tcx [ty::VtblEntry<'tcx>] { - desc { |tcx| "finding all vtable entries for trait `{}`", tcx.def_path_str(key.def_id()) } + desc { |tcx| "finding all vtable entries for trait `{}`", tcx.def_path_str(key.def_id) } } query first_method_vtable_slot(key: ty::TraitRef<'tcx>) -> usize { @@ -1452,7 +1457,7 @@ rustc_queries! { key.1, key.0 } } - query vtable_allocation(key: (Ty<'tcx>, Option>)) -> mir::interpret::AllocId { + query vtable_allocation(key: (Ty<'tcx>, Option>)) -> mir::interpret::AllocId { desc { |tcx| "vtable const allocation for <{} as {}>", key.0, key.1.map(|trait_ref| format!("{trait_ref}")).unwrap_or("_".to_owned()) @@ -1480,7 +1485,7 @@ rustc_queries! { query specialization_graph_of(trait_id: DefId) -> Result<&'tcx specialization_graph::Graph, ErrorGuaranteed> { desc { |tcx| "building specialization graph of trait `{}`", tcx.def_path_str(trait_id) } cache_on_disk_if { true } - ensure_forwards_result_if_red + return_result_from_ensure_ok } query dyn_compatibility_violations(trait_id: DefId) -> &'tcx [DynCompatibilityViolation] { desc { |tcx| "determining dyn-compatibility of trait `{}`", tcx.def_path_str(trait_id) } @@ -1629,7 +1634,7 @@ rustc_queries! { separate_provide_extern } - query dependency_formats(_: ()) -> &'tcx Lrc { + query dependency_formats(_: ()) -> &'tcx Arc { arena_cache desc { "getting the linkage format of all dependencies" } } @@ -1716,7 +1721,12 @@ rustc_queries! { query check_well_formed(key: LocalDefId) -> Result<(), ErrorGuaranteed> { desc { |tcx| "checking that `{}` is well-formed", tcx.def_path_str(key) } - ensure_forwards_result_if_red + return_result_from_ensure_ok + } + + query enforce_impl_non_lifetime_params_are_constrained(key: LocalDefId) -> Result<(), ErrorGuaranteed> { + desc { |tcx| "checking that `{}`'s generics are constrained by the impl header", tcx.def_path_str(key) } + return_result_from_ensure_ok } // The `DefId`s of all non-generic functions and statics in the given crate @@ -2073,7 +2083,7 @@ rustc_queries! { desc { "seeing if we're missing an `extern crate` item for this crate" } separate_provide_extern } - query used_crate_source(_: CrateNum) -> &'tcx Lrc { + query used_crate_source(_: CrateNum) -> &'tcx Arc { arena_cache eval_always desc { "looking at the source for a crate" } @@ -2126,6 +2136,8 @@ rustc_queries! { eval_always desc { "calculating the stability index for the local crate" } } + /// All available crates in the graph, including those that should not be user-facing + /// (such as private crates). query crates(_: ()) -> &'tcx [CrateNum] { eval_always desc { "fetching all foreign CrateNum instances" } @@ -2160,7 +2172,7 @@ rustc_queries! { separate_provide_extern } - query collect_and_partition_mono_items(_: ()) -> (&'tcx DefIdSet, &'tcx [CodegenUnit<'tcx>]) { + query collect_and_partition_mono_items(_: ()) -> MonoItemPartitions<'tcx> { eval_always desc { "collect_and_partition_mono_items" } } @@ -2355,7 +2367,7 @@ rustc_queries! { } /// Returns the Rust target features for the current target. These are not always the same as LLVM target features! - query rust_target_features(_: CrateNum) -> &'tcx UnordMap { + query rust_target_features(_: CrateNum) -> &'tcx UnordMap { arena_cache eval_always desc { "looking up Rust target features" } @@ -2411,7 +2423,7 @@ rustc_queries! { /// because the `ty::Ty`-based wfcheck is always run. query diagnostic_hir_wf_check( key: (ty::Predicate<'tcx>, WellFormedLoc) - ) -> &'tcx Option> { + ) -> Option<&'tcx ObligationCause<'tcx>> { arena_cache eval_always no_hash @@ -2436,7 +2448,7 @@ rustc_queries! { /// Any other def id will ICE. query compare_impl_item(key: LocalDefId) -> Result<(), ErrorGuaranteed> { desc { |tcx| "checking assoc item `{}` is compatible with trait definition", tcx.def_path_str(key) } - ensure_forwards_result_if_red + return_result_from_ensure_ok } query deduced_param_attrs(def_id: DefId) -> &'tcx [ty::DeducedParamAttrs] { diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 4a144ebb89940..d9035efaf56d0 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -1,21 +1,16 @@ use std::collections::hash_map::Entry; use std::mem; +use std::sync::Arc; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::memmap::Mmap; -use rustc_data_structures::sync::{HashMapExt, Lock, Lrc, RwLock}; +use rustc_data_structures::sync::{HashMapExt, Lock, RwLock}; use rustc_data_structures::unhash::UnhashMap; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LOCAL_CRATE, LocalDefId, StableCrateId}; use rustc_hir::definitions::DefPathHash; use rustc_index::{Idx, IndexVec}; use rustc_macros::{Decodable, Encodable}; -use rustc_middle::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; -use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState}; -use rustc_middle::mir::mono::MonoItem; -use rustc_middle::mir::{self, interpret}; -use rustc_middle::ty::codec::{RefDecodable, TyDecoder, TyEncoder}; -use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_query_system::query::QuerySideEffects; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder, IntEncodedWithFixedSize, MemDecoder}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; @@ -29,6 +24,13 @@ use rustc_span::{ SpanDecoder, SpanEncoder, StableSourceFileId, Symbol, }; +use crate::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; +use crate::mir::interpret::{AllocDecodingSession, AllocDecodingState}; +use crate::mir::mono::MonoItem; +use crate::mir::{self, interpret}; +use crate::ty::codec::{RefDecodable, TyDecoder, TyEncoder}; +use crate::ty::{self, Ty, TyCtxt}; + const TAG_FILE_FOOTER: u128 = 0xC0FFEE_C0FFEE_C0FFEE_C0FFEE_C0FFEE; // A normal span encoded with both location information and a `SyntaxContext` @@ -60,7 +62,7 @@ pub struct OnDiskCache { file_index_to_stable_id: FxHashMap, // Caches that are populated lazily during decoding. - file_index_to_file: Lock>>, + file_index_to_file: Lock>>, // A map from dep-node to the position of the cached query result in // `serialized_data`. @@ -326,15 +328,18 @@ impl OnDiskCache { // Encode the file footer. let footer_pos = encoder.position() as u64; - encoder.encode_tagged(TAG_FILE_FOOTER, &Footer { - file_index_to_stable_id, - query_result_index, - side_effects_index, - interpret_alloc_index, - syntax_contexts, - expn_data, - foreign_expn_data, - }); + encoder.encode_tagged( + TAG_FILE_FOOTER, + &Footer { + file_index_to_stable_id, + query_result_index, + side_effects_index, + interpret_alloc_index, + syntax_contexts, + expn_data, + foreign_expn_data, + }, + ); // Encode the position of the footer as the last 8 bytes of the // file so we know where to look for it. @@ -453,7 +458,7 @@ impl OnDiskCache { pub struct CacheDecoder<'a, 'tcx> { tcx: TyCtxt<'tcx>, opaque: MemDecoder<'a>, - file_index_to_file: &'a Lock>>, + file_index_to_file: &'a Lock>>, file_index_to_stable_id: &'a FxHashMap, alloc_decoding_session: AllocDecodingSession<'a>, syntax_contexts: &'a FxHashMap, @@ -464,10 +469,10 @@ pub struct CacheDecoder<'a, 'tcx> { impl<'a, 'tcx> CacheDecoder<'a, 'tcx> { #[inline] - fn file_index_to_file(&self, index: SourceFileIndex) -> Lrc { + fn file_index_to_file(&self, index: SourceFileIndex) -> Arc { let CacheDecoder { tcx, file_index_to_file, file_index_to_stable_id, .. } = *self; - Lrc::clone(file_index_to_file.borrow_mut().entry(index).or_insert_with(|| { + Arc::clone(file_index_to_file.borrow_mut().entry(index).or_insert_with(|| { let source_file_id = &file_index_to_stable_id[&index]; let source_file_cnum = tcx.stable_crate_id_to_crate_num(source_file_id.stable_crate_id); @@ -559,7 +564,7 @@ impl<'a, 'tcx> TyDecoder for CacheDecoder<'a, 'tcx> { } } -rustc_middle::implement_ty_decoder!(CacheDecoder<'a, 'tcx>); +crate::implement_ty_decoder!(CacheDecoder<'a, 'tcx>); // This ensures that the `Decodable::decode` specialization for `Vec` is used // when a `CacheDecoder` is passed to `Decodable::decode`. Unfortunately, we have to manually opt @@ -824,7 +829,7 @@ pub struct CacheEncoder<'a, 'tcx> { impl<'a, 'tcx> CacheEncoder<'a, 'tcx> { #[inline] - fn source_file_index(&mut self, source_file: Lrc) -> SourceFileIndex { + fn source_file_index(&mut self, source_file: Arc) -> SourceFileIndex { self.file_to_file_index[&(&raw const *source_file)] } diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 3337f7ceee7d7..690b8128b1aef 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -1,6 +1,5 @@ use std::ops::Deref; -use field_offset::FieldOffset; use rustc_data_structures::sync::{AtomicU64, WorkerLocal}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::hir_id::OwnerId; @@ -24,8 +23,10 @@ pub struct DynamicQuery<'tcx, C: QueryCache> { pub eval_always: bool, pub dep_kind: DepKind, pub handle_cycle_error: HandleCycleError, - pub query_state: FieldOffset, QueryState>, - pub query_cache: FieldOffset, C>, + // Offset of this query's state field in the QueryStates struct + pub query_state: usize, + // Offset of this query's cache field in the QueryCaches struct + pub query_cache: usize, pub cache_on_disk: fn(tcx: TyCtxt<'tcx>, key: &C::Key) -> bool, pub execute_query: fn(tcx: TyCtxt<'tcx>, k: C::Key) -> C::Value, pub compute: fn(tcx: TyCtxt<'tcx>, key: C::Key) -> C::Value, @@ -88,30 +89,68 @@ impl<'tcx> Deref for TyCtxtAt<'tcx> { } #[derive(Copy, Clone)] -pub struct TyCtxtEnsure<'tcx> { +#[must_use] +pub struct TyCtxtEnsureOk<'tcx> { pub tcx: TyCtxt<'tcx>, } #[derive(Copy, Clone)] -pub struct TyCtxtEnsureWithValue<'tcx> { +#[must_use] +pub struct TyCtxtEnsureDone<'tcx> { pub tcx: TyCtxt<'tcx>, } impl<'tcx> TyCtxt<'tcx> { - /// Returns a transparent wrapper for `TyCtxt`, which ensures queries - /// are executed instead of just returning their results. + /// Wrapper that calls queries in a special "ensure OK" mode, for callers + /// that don't need the return value and just want to invoke a query for + /// its potential side-effect of emitting fatal errors. + /// + /// This can be more efficient than a normal query call, because if the + /// query's inputs are all green, the call can return immediately without + /// needing to obtain a value (by decoding one from disk or by executing + /// the query). + /// + /// (As with all query calls, execution is also skipped if the query result + /// is already cached in memory.) + /// + /// ## WARNING + /// A subsequent normal call to the same query might still cause it to be + /// executed! This can occur when the inputs are all green, but the query's + /// result is not cached on disk, so the query must be executed to obtain a + /// return value. + /// + /// Therefore, this call mode is not appropriate for callers that want to + /// ensure that the query is _never_ executed in the future. + /// + /// ## `return_result_from_ensure_ok` + /// If a query has the `return_result_from_ensure_ok` modifier, calls via + /// `ensure_ok` will instead return `Result<(), ErrorGuaranteed>`. If the + /// query needs to be executed, and execution returns an error, that error + /// is returned to the caller. #[inline(always)] - pub fn ensure(self) -> TyCtxtEnsure<'tcx> { - TyCtxtEnsure { tcx: self } + pub fn ensure_ok(self) -> TyCtxtEnsureOk<'tcx> { + TyCtxtEnsureOk { tcx: self } } - /// Returns a transparent wrapper for `TyCtxt`, which ensures queries - /// are executed instead of just returning their results. + /// Wrapper that calls queries in a special "ensure done" mode, for callers + /// that don't need the return value and just want to guarantee that the + /// query won't be executed in the future, by executing it now if necessary. /// - /// This version verifies that the computed result exists in the cache before returning. + /// This is useful for queries that read from a [`Steal`] value, to ensure + /// that they are executed before the query that will steal the value. + /// + /// Unlike [`Self::ensure_ok`], a query with all-green inputs will only be + /// skipped if its return value is stored in the disk-cache. This is still + /// more efficient than a regular query, because in that situation the + /// return value doesn't necessarily need to be decoded. + /// + /// (As with all query calls, execution is also skipped if the query result + /// is already cached in memory.) + /// + /// [`Steal`]: rustc_data_structures::steal::Steal #[inline(always)] - pub fn ensure_with_value(self) -> TyCtxtEnsureWithValue<'tcx> { - TyCtxtEnsureWithValue { tcx: self } + pub fn ensure_done(self) -> TyCtxtEnsureDone<'tcx> { + TyCtxtEnsureDone { tcx: self } } /// Returns a transparent wrapper for `TyCtxt` which uses @@ -193,7 +232,7 @@ macro_rules! query_ensure { ([]$($args:tt)*) => { query_ensure($($args)*) }; - ([(ensure_forwards_result_if_red) $($rest:tt)*]$($args:tt)*) => { + ([(return_result_from_ensure_ok) $($rest:tt)*]$($args:tt)*) => { query_ensure_error_guaranteed($($args)*).map(|_| ()) }; ([$other:tt $($modifiers:tt)*]$($args:tt)*) => { @@ -248,15 +287,15 @@ macro_rules! separate_provide_extern_decl { }; } -macro_rules! ensure_result { - ([][$ty:ty]) => { +macro_rules! ensure_ok_result { + ( [] ) => { () }; - ([(ensure_forwards_result_if_red) $($rest:tt)*][$ty:ty]) => { + ( [(return_result_from_ensure_ok) $($rest:tt)*] ) => { Result<(), ErrorGuaranteed> }; - ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => { - ensure_result!([$($modifiers)*][$($args)*]) + ( [$other:tt $($modifiers:tt)*] ) => { + ensure_ok_result!( [$($modifiers)*] ) }; } @@ -289,10 +328,10 @@ macro_rules! define_callbacks { /// This type alias specifies the type returned from query providers and the type /// used for decoding. For regular queries this is the declared returned type `V`, - /// but `arena_cache` will use `::Target` instead. + /// but `arena_cache` will use `::Provided` instead. pub type ProvidedValue<'tcx> = query_if_arena!( [$($modifiers)*] - (<$V as Deref>::Target) + (<$V as $crate::query::arena_cached::ArenaCached<'tcx>>::Provided) ($V) ); @@ -307,10 +346,18 @@ macro_rules! define_callbacks { ) -> Erase> { erase(query_if_arena!([$($modifiers)*] { - if mem::needs_drop::>() { - &*_tcx.query_system.arenas.$name.alloc(value) + use $crate::query::arena_cached::ArenaCached; + + if mem::needs_drop::<<$V as ArenaCached<'tcx>>::Allocated>() { + <$V as ArenaCached>::alloc_in_arena( + |v| _tcx.query_system.arenas.$name.alloc(v), + value, + ) } else { - &*_tcx.arena.dropless.alloc(value) + <$V as ArenaCached>::alloc_in_arena( + |v| _tcx.arena.dropless.alloc(v), + value, + ) } } (value) @@ -354,7 +401,7 @@ macro_rules! define_callbacks { pub struct QueryArenas<'tcx> { $($(#[$attr])* pub $name: query_if_arena!([$($modifiers)*] - (TypedArena<<$V as Deref>::Target>) + (TypedArena<<$V as $crate::query::arena_cached::ArenaCached<'tcx>>::Allocated>) () ),)* } @@ -375,10 +422,13 @@ macro_rules! define_callbacks { $($(#[$attr])* pub $name: queries::$name::Storage<'tcx>,)* } - impl<'tcx> TyCtxtEnsure<'tcx> { + impl<'tcx> TyCtxtEnsureOk<'tcx> { $($(#[$attr])* #[inline(always)] - pub fn $name(self, key: query_helper_param_ty!($($K)*)) -> ensure_result!([$($modifiers)*][$V]) { + pub fn $name( + self, + key: query_helper_param_ty!($($K)*), + ) -> ensure_ok_result!([$($modifiers)*]) { query_ensure!( [$($modifiers)*] self.tcx, @@ -390,7 +440,7 @@ macro_rules! define_callbacks { })* } - impl<'tcx> TyCtxtEnsureWithValue<'tcx> { + impl<'tcx> TyCtxtEnsureDone<'tcx> { $($(#[$attr])* #[inline(always)] pub fn $name(self, key: query_helper_param_ty!($($K)*)) { @@ -548,7 +598,6 @@ macro_rules! define_feedable { let dep_node_index = tcx.dep_graph.with_feed_task( dep_node, tcx, - key, &value, hash_result!([$($modifiers)*]), ); diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 86014c34b4584..2ab8750f727c0 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -11,6 +11,7 @@ use std::cmp::Ordering; use std::fmt; use std::ops::Index; +use std::sync::Arc; use rustc_abi::{FieldIdx, Integer, Size, VariantIdx}; use rustc_ast::{AsmMacro, InlineAsmOptions, InlineAsmTemplatePiece}; @@ -18,28 +19,26 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::{BindingMode, ByRef, HirId, MatchSource, RangeEnd}; use rustc_index::{IndexVec, newtype_index}; -use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeVisitable}; -use rustc_middle::middle::region; -use rustc_middle::mir::interpret::AllocId; -use rustc_middle::mir::{self, BinOp, BorrowKind, FakeReadCause, UnOp}; -use rustc_middle::ty::adjustment::PointerCoercion; -use rustc_middle::ty::layout::IntegerExt; -use rustc_middle::ty::{ - self, AdtDef, CanonicalUserType, CanonicalUserTypeAnnotation, FnSig, GenericArgsRef, List, Ty, - TyCtxt, UpvarArgs, -}; +use rustc_macros::{HashStable, TypeVisitable}; use rustc_span::def_id::LocalDefId; use rustc_span::{ErrorGuaranteed, Span, Symbol}; use rustc_target::asm::InlineAsmRegOrRegClass; use tracing::instrument; +use crate::middle::region; +use crate::mir::interpret::AllocId; +use crate::mir::{self, BinOp, BorrowKind, FakeReadCause, UnOp}; +use crate::ty::adjustment::PointerCoercion; +use crate::ty::layout::IntegerExt; +use crate::ty::{ + self, AdtDef, CanonicalUserType, CanonicalUserTypeAnnotation, FnSig, GenericArgsRef, List, Ty, + TyCtxt, UpvarArgs, +}; + pub mod visit; macro_rules! thir_with_elements { ( - $($field_name:ident: $field_ty:ty,)* - - @elements: $($name:ident: $id:ty => $value:ty => $format:literal,)* ) => { $( @@ -53,22 +52,18 @@ macro_rules! thir_with_elements { /// A container for a THIR body. /// /// This can be indexed directly by any THIR index (e.g. [`ExprId`]). - #[derive(Debug, HashStable, Clone)] + #[derive(Debug, HashStable)] pub struct Thir<'tcx> { - $( - pub $field_name: $field_ty, - )* + pub body_type: BodyTy<'tcx>, $( pub $name: IndexVec<$id, $value>, )* } impl<'tcx> Thir<'tcx> { - pub fn new($($field_name: $field_ty,)*) -> Thir<'tcx> { + pub fn new(body_type: BodyTy<'tcx>) -> Thir<'tcx> { Thir { - $( - $field_name, - )* + body_type, $( $name: IndexVec::new(), )* @@ -88,9 +83,6 @@ macro_rules! thir_with_elements { } thir_with_elements! { - body_type: BodyTy<'tcx>, - -@elements: arms: ArmId => Arm<'tcx> => "a{}", blocks: BlockId => Block => "b{}", exprs: ExprId => Expr<'tcx> => "e{}", @@ -98,14 +90,14 @@ thir_with_elements! { params: ParamId => Param<'tcx> => "p{}", } -#[derive(Debug, HashStable, Clone)] +#[derive(Debug, HashStable)] pub enum BodyTy<'tcx> { Const(Ty<'tcx>), Fn(FnSig<'tcx>), } /// Description of a type-checked function parameter. -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub struct Param<'tcx> { /// The pattern that appears in the parameter list, or None for implicit parameters. pub pat: Option>>, @@ -125,7 +117,7 @@ pub enum LintLevel { Explicit(HirId), } -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub struct Block { /// Whether the block itself has a label. Used by `label: {}` /// and `try` blocks. @@ -145,7 +137,7 @@ pub struct Block { type UserTy<'tcx> = Option>>; -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub struct AdtExpr<'tcx> { /// The ADT we're constructing. pub adt_def: AdtDef<'tcx>, @@ -162,7 +154,7 @@ pub struct AdtExpr<'tcx> { pub base: AdtExprBase<'tcx>, } -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub enum AdtExprBase<'tcx> { /// A struct expression where all the fields are explicitly enumerated: `Foo { a, b }`. None, @@ -175,7 +167,7 @@ pub enum AdtExprBase<'tcx> { DefaultFields(Box<[Ty<'tcx>]>), } -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub struct ClosureExpr<'tcx> { pub closure_id: LocalDefId, pub args: UpvarArgs<'tcx>, @@ -184,7 +176,7 @@ pub struct ClosureExpr<'tcx> { pub fake_reads: Vec<(ExprId, FakeReadCause, HirId)>, } -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub struct InlineAsmExpr<'tcx> { pub asm_macro: AsmMacro, pub template: &'tcx [InlineAsmTemplatePiece], @@ -202,12 +194,12 @@ pub enum BlockSafety { ExplicitUnsafe(HirId), } -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub struct Stmt<'tcx> { pub kind: StmtKind<'tcx>, } -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub enum StmtKind<'tcx> { /// An expression with a trailing semicolon. Expr { @@ -247,11 +239,11 @@ pub enum StmtKind<'tcx> { }, } -#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, HashStable)] pub struct LocalVarId(pub HirId); /// A THIR expression. -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub struct Expr<'tcx> { /// kind of expression pub kind: ExprKind<'tcx>, @@ -278,7 +270,7 @@ pub struct TempLifetime { pub backwards_incompatible: Option, } -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub enum ExprKind<'tcx> { /// `Scope`s are used to explicitly mark destruction scopes, /// and to track the `HirId` of the expressions within the scope. @@ -489,6 +481,19 @@ pub enum ExprKind<'tcx> { user_ty: UserTy<'tcx>, user_ty_span: Span, }, + /// An unsafe binder cast on a place, e.g. `unwrap_binder!(*ptr)`. + PlaceUnwrapUnsafeBinder { + source: ExprId, + }, + /// An unsafe binder cast on a value, e.g. `unwrap_binder!(rvalue())`, + /// which makes a temporary. + ValueUnwrapUnsafeBinder { + source: ExprId, + }, + /// Construct an unsafe binder, e.g. `wrap_binder(&ref)`. + WrapUnsafeBinder { + source: ExprId, + }, /// A closure definition. Closure(Box>), /// A literal. @@ -543,20 +548,20 @@ pub enum ExprKind<'tcx> { /// Represents the association of a field identifier and an expression. /// /// This is used in struct constructors. -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub struct FieldExpr { pub name: FieldIdx, pub expr: ExprId, } -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub struct FruInfo<'tcx> { pub base: ExprId, pub field_types: Box<[Ty<'tcx>]>, } /// A `match` arm. -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub struct Arm<'tcx> { pub pattern: Box>, pub guard: Option, @@ -574,7 +579,7 @@ pub enum LogicalOp { Or, } -#[derive(Clone, Debug, HashStable)] +#[derive(Debug, HashStable)] pub enum InlineAsmOperand<'tcx> { In { reg: InlineAsmRegOrRegClass, @@ -612,13 +617,13 @@ pub enum InlineAsmOperand<'tcx> { }, } -#[derive(Clone, Debug, HashStable, TypeVisitable)] +#[derive(Debug, HashStable, TypeVisitable)] pub struct FieldPat<'tcx> { pub field: FieldIdx, - pub pattern: Box>, + pub pattern: Pat<'tcx>, } -#[derive(Clone, Debug, HashStable, TypeVisitable)] +#[derive(Debug, HashStable, TypeVisitable)] pub struct Pat<'tcx> { pub ty: Ty<'tcx>, pub span: Span, @@ -676,7 +681,7 @@ impl<'tcx> Pat<'tcx> { Or { pats } => pats.iter().for_each(|p| p.walk_(it)), Array { box ref prefix, ref slice, box ref suffix } | Slice { box ref prefix, ref slice, box ref suffix } => { - prefix.iter().chain(slice.iter()).chain(suffix.iter()).for_each(|p| p.walk_(it)) + prefix.iter().chain(slice.as_deref()).chain(suffix.iter()).for_each(|p| p.walk_(it)) } } } @@ -726,7 +731,7 @@ impl<'tcx> Pat<'tcx> { } } -#[derive(Clone, Debug, HashStable, TypeVisitable)] +#[derive(Debug, HashStable, TypeVisitable)] pub struct Ascription<'tcx> { pub annotation: CanonicalUserTypeAnnotation<'tcx>, /// Variance to use when relating the `user_ty` to the **type of the value being @@ -750,7 +755,7 @@ pub struct Ascription<'tcx> { pub variance: ty::Variance, } -#[derive(Clone, Debug, HashStable, TypeVisitable)] +#[derive(Debug, HashStable, TypeVisitable)] pub enum PatKind<'tcx> { /// A wildcard pattern: `_`. Wild, @@ -833,28 +838,28 @@ pub enum PatKind<'tcx> { subpattern: Box>, }, - Range(Box>), + Range(Arc>), /// Matches against a slice, checking the length and extracting elements. /// irrefutable when there is a slice pattern and both `prefix` and `suffix` are empty. /// e.g., `&[ref xs @ ..]`. Slice { - prefix: Box<[Box>]>, + prefix: Box<[Pat<'tcx>]>, slice: Option>>, - suffix: Box<[Box>]>, + suffix: Box<[Pat<'tcx>]>, }, /// Fixed match against an array; irrefutable. Array { - prefix: Box<[Box>]>, + prefix: Box<[Pat<'tcx>]>, slice: Option>>, - suffix: Box<[Box>]>, + suffix: Box<[Pat<'tcx>]>, }, /// An or-pattern, e.g. `p | q`. /// Invariant: `pats.len() >= 2`. Or { - pats: Box<[Box>]>, + pats: Box<[Pat<'tcx>]>, }, /// A never pattern `!`. diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index 64bac12b2666a..13b8af55e518e 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -1,8 +1,7 @@ use super::{ - AdtExpr, Arm, Block, ClosureExpr, Expr, ExprKind, InlineAsmExpr, InlineAsmOperand, Pat, - PatKind, Stmt, StmtKind, Thir, + AdtExpr, AdtExprBase, Arm, Block, ClosureExpr, Expr, ExprKind, InlineAsmExpr, InlineAsmOperand, + Pat, PatKind, Stmt, StmtKind, Thir, }; -use crate::thir::AdtExprBase; pub trait Visitor<'thir, 'tcx: 'thir>: Sized { fn thir(&self) -> &'thir Thir<'tcx>; @@ -136,6 +135,9 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( | ValueTypeAscription { source, user_ty: _, user_ty_span: _ } => { visitor.visit_expr(&visitor.thir()[source]) } + PlaceUnwrapUnsafeBinder { source } + | ValueUnwrapUnsafeBinder { source } + | WrapUnsafeBinder { source } => visitor.visit_expr(&visitor.thir()[source]), Closure(box ClosureExpr { closure_id: _, args: _, diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 99211c1f92440..f039da772fd4d 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -10,8 +10,8 @@ mod structural_impls; use std::borrow::Cow; use std::hash::{Hash, Hasher}; +use std::sync::Arc; -use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, Diag, EmissionGuarantee}; use rustc_hir as hir; use rustc_hir::HirId; @@ -21,13 +21,12 @@ use rustc_macros::{ }; use rustc_span::def_id::{CRATE_DEF_ID, LocalDefId}; use rustc_span::{DUMMY_SP, Span, Symbol}; -// FIXME: Remove this import and import via `solve::` -pub use rustc_type_ir::solve::BuiltinImplSource; use smallvec::{SmallVec, smallvec}; use thin_vec::ThinVec; pub use self::select::{EvaluationCache, EvaluationResult, OverflowError, SelectionCache}; use crate::mir::ConstraintCategory; +pub use crate::traits::solve::BuiltinImplSource; use crate::ty::abstract_const::NotConstEvaluatable; use crate::ty::{self, AdtKind, GenericArgsRef, Ty}; @@ -125,6 +124,15 @@ impl<'tcx> ObligationCause<'tcx> { self } + pub fn derived_host_cause( + mut self, + parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>, + variant: impl FnOnce(DerivedHostCause<'tcx>) -> ObligationCauseCode<'tcx>, + ) -> ObligationCause<'tcx> { + self.code = variant(DerivedHostCause { parent_host_pred, parent_code: self.code }).into(); + self + } + pub fn to_constraint_category(&self) -> ConstraintCategory<'tcx> { match self.code() { ObligationCauseCode::MatchImpl(cause, _) => cause.to_constraint_category(), @@ -149,7 +157,7 @@ pub struct UnifyReceiverContext<'tcx> { pub struct InternedObligationCauseCode<'tcx> { /// `None` for `ObligationCauseCode::Misc` (a common case, occurs ~60% of /// the time). `Some` otherwise. - code: Option>>, + code: Option>>, } impl<'tcx> std::fmt::Debug for InternedObligationCauseCode<'tcx> { @@ -163,7 +171,7 @@ impl<'tcx> ObligationCauseCode<'tcx> { #[inline(always)] fn into(self) -> InternedObligationCauseCode<'tcx> { InternedObligationCauseCode { - code: if let ObligationCauseCode::Misc = self { None } else { Some(Lrc::new(self)) }, + code: if let ObligationCauseCode::Misc = self { None } else { Some(Arc::new(self)) }, } } } @@ -185,6 +193,9 @@ pub enum ObligationCauseCode<'tcx> { /// A slice or array is WF only if `T: Sized`. SliceOrArrayElem, + /// An array `[T; N]` can only be indexed (and is only well-formed if) `N` has type usize. + ArrayLen(Ty<'tcx>), + /// A tuple is WF only if its middle elements are `Sized`. TupleElem, @@ -278,6 +289,14 @@ pub enum ObligationCauseCode<'tcx> { /// Derived obligation for WF goals. WellFormedDerived(DerivedCause<'tcx>), + /// Derived obligation (i.e. `where` clause) on an user-provided impl + /// or a trait alias. + ImplDerivedHost(Box>), + + /// Derived obligation (i.e. `where` clause) on an user-provided impl + /// or a trait alias. + BuiltinDerivedHost(DerivedHostCause<'tcx>), + /// Derived obligation refined to point at a specific argument in /// a call or method expression. FunctionArg { @@ -328,9 +347,6 @@ pub enum ObligationCauseCode<'tcx> { /// `main` has wrong type MainFunctionType, - /// `start` has wrong type - StartFunctionType, - /// language function has wrong type LangFunctionType(Symbol), @@ -411,10 +427,6 @@ pub enum IsConstable { Ctor, } -crate::TrivialTypeTraversalAndLiftImpls! { - IsConstable, -} - /// The 'location' at which we try to perform HIR-based wf checking. /// This information is used to obtain an `hir::Ty`, which /// we can walk in order to obtain precise spans for any @@ -437,36 +449,38 @@ pub enum WellFormedLoc { }, } -#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] -#[derive(TypeVisitable, TypeFoldable)] -pub struct ImplDerivedCause<'tcx> { - pub derived: DerivedCause<'tcx>, - /// The `DefId` of the `impl` that gave rise to the `derived` obligation. - /// If the `derived` obligation arose from a trait alias, which conceptually has a synthetic impl, - /// then this will be the `DefId` of that trait alias. Care should therefore be taken to handle - /// that exceptional case where appropriate. - pub impl_or_alias_def_id: DefId, - /// The index of the derived predicate in the parent impl's predicates. - pub impl_def_predicate_index: Option, - pub span: Span, -} - impl<'tcx> ObligationCauseCode<'tcx> { /// Returns the base obligation, ignoring derived obligations. pub fn peel_derives(&self) -> &Self { let mut base_cause = self; - while let Some((parent_code, _)) = base_cause.parent() { + while let Some(parent_code) = base_cause.parent() { base_cause = parent_code; } base_cause } + pub fn parent(&self) -> Option<&Self> { + match self { + ObligationCauseCode::FunctionArg { parent_code, .. } => Some(parent_code), + ObligationCauseCode::BuiltinDerived(derived) + | ObligationCauseCode::WellFormedDerived(derived) + | ObligationCauseCode::ImplDerived(box ImplDerivedCause { derived, .. }) => { + Some(&derived.parent_code) + } + ObligationCauseCode::BuiltinDerivedHost(derived) + | ObligationCauseCode::ImplDerivedHost(box ImplDerivedHostCause { derived, .. }) => { + Some(&derived.parent_code) + } + _ => None, + } + } + /// Returns the base obligation and the base trait predicate, if any, ignoring /// derived obligations. pub fn peel_derives_with_predicate(&self) -> (&Self, Option>) { let mut base_cause = self; let mut base_trait_pred = None; - while let Some((parent_code, parent_pred)) = base_cause.parent() { + while let Some((parent_code, parent_pred)) = base_cause.parent_with_predicate() { base_cause = parent_code; if let Some(parent_pred) = parent_pred { base_trait_pred = Some(parent_pred); @@ -476,7 +490,7 @@ impl<'tcx> ObligationCauseCode<'tcx> { (base_cause, base_trait_pred) } - pub fn parent(&self) -> Option<(&Self, Option>)> { + pub fn parent_with_predicate(&self) -> Option<(&Self, Option>)> { match self { ObligationCauseCode::FunctionArg { parent_code, .. } => Some((parent_code, None)), ObligationCauseCode::BuiltinDerived(derived) @@ -573,6 +587,42 @@ pub struct DerivedCause<'tcx> { pub parent_code: InternedObligationCauseCode<'tcx>, } +#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] +#[derive(TypeVisitable, TypeFoldable)] +pub struct ImplDerivedCause<'tcx> { + pub derived: DerivedCause<'tcx>, + /// The `DefId` of the `impl` that gave rise to the `derived` obligation. + /// If the `derived` obligation arose from a trait alias, which conceptually has a synthetic impl, + /// then this will be the `DefId` of that trait alias. Care should therefore be taken to handle + /// that exceptional case where appropriate. + pub impl_or_alias_def_id: DefId, + /// The index of the derived predicate in the parent impl's predicates. + pub impl_def_predicate_index: Option, + pub span: Span, +} + +#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] +#[derive(TypeVisitable, TypeFoldable)] +pub struct DerivedHostCause<'tcx> { + /// The trait predicate of the parent obligation that led to the + /// current obligation. Note that only trait obligations lead to + /// derived obligations, so we just store the trait predicate here + /// directly. + pub parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>, + + /// The parent trait had this cause. + pub parent_code: InternedObligationCauseCode<'tcx>, +} + +#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)] +#[derive(TypeVisitable, TypeFoldable)] +pub struct ImplDerivedHostCause<'tcx> { + pub derived: DerivedHostCause<'tcx>, + /// The `DefId` of the `impl` that gave rise to the `derived` obligation. + pub impl_def_id: DefId, + pub span: Span, +} + #[derive(Clone, Debug, PartialEq, Eq, TypeVisitable)] pub enum SelectionError<'tcx> { /// The trait is not implemented. diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index f049da95f29ae..4203c8fd86149 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -7,11 +7,10 @@ use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; use rustc_span::Span; -// FIXME: Remove this import and import via `traits::solve`. -pub use rustc_type_ir::solve::NoSolution; use crate::error::DropCheckOverflow; use crate::infer::canonical::{Canonical, CanonicalQueryInput, QueryResponse}; +pub use crate::traits::solve::NoSolution; use crate::ty::{self, GenericArg, Ty, TyCtxt}; pub mod type_op { @@ -42,11 +41,18 @@ pub mod type_op { pub predicate: Predicate<'tcx>, } + /// Normalizes, but not in the new solver. #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)] pub struct Normalize { pub value: T, } + /// Normalizes, and deeply normalizes in the new solver. + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)] + pub struct DeeplyNormalize { + pub value: T, + } + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)] pub struct ImpliedOutlivesBounds<'tcx> { pub ty: Ty<'tcx>, @@ -81,6 +87,9 @@ pub type CanonicalTypeOpProvePredicateGoal<'tcx> = pub type CanonicalTypeOpNormalizeGoal<'tcx, T> = CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize>>; +pub type CanonicalTypeOpDeeplyNormalizeGoal<'tcx, T> = + CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, type_op::DeeplyNormalize>>; + pub type CanonicalImpliedOutlivesBoundsGoal<'tcx> = CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, type_op::ImpliedOutlivesBounds<'tcx>>>; diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 094fc62afbbf1..811bd8fb4588a 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -168,6 +168,8 @@ pub enum SelectionCandidate<'tcx> { BuiltinObjectCandidate, BuiltinUnsizeCandidate, + + BikeshedGuaranteedNoDropCandidate, } /// The result of trait evaluation. The order is important @@ -260,8 +262,6 @@ impl From for OverflowError { } } -TrivialTypeTraversalImpls! { OverflowError } - impl<'tcx> From for SelectionError<'tcx> { fn from(overflow_error: OverflowError) -> SelectionError<'tcx> { match overflow_error { diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs index 002d381962124..c9b9ec771b324 100644 --- a/compiler/rustc_middle/src/ty/abstract_const.rs +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -30,8 +30,6 @@ impl From for NotConstEvaluatable { } } -TrivialTypeTraversalImpls! { NotConstEvaluatable } - pub type BoundAbstractConst<'tcx> = Result>>, ErrorGuaranteed>; diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index e28fcc555dcd1..b529d17540a82 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -217,6 +217,10 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef> for AdtDef<'tcx> { self.is_phantom_data() } + fn is_manually_drop(self) -> bool { + self.is_manually_drop() + } + fn all_field_tys( self, tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_middle/src/ty/cast.rs b/compiler/rustc_middle/src/ty/cast.rs index b1316ceef5ac7..10f7d58963623 100644 --- a/compiler/rustc_middle/src/ty/cast.rs +++ b/compiler/rustc_middle/src/ty/cast.rs @@ -2,8 +2,8 @@ // typeck and codegen. use rustc_macros::{HashStable, TyDecodable, TyEncodable}; -use rustc_middle::mir; +use crate::mir; use crate::ty::{self, Ty}; /// Types that are represented as ints. diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index 33d39b137b6d9..c4d5367e2f03a 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -433,7 +433,7 @@ pub fn analyze_coroutine_closure_captures<'a, 'tcx: 'a, T>( // A parent matches a child if they share the same prefix of projections. // The child may have more, if it is capturing sub-fields out of // something that is captured by-move in the parent closure. - while child_captures.peek().map_or(false, |(_, child_capture)| { + while child_captures.peek().is_some_and(|(_, child_capture)| { child_prefix_matches_parent_projections(parent_capture, child_capture) }) { let (child_field_idx, child_capture) = child_captures.next().unwrap(); diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 94bf1aa4f0356..41958949836a7 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -13,8 +13,6 @@ use std::marker::DiscriminantKind; use rustc_abi::{FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::LocalDefId; -use rustc_middle::mir::mono::MonoItem; -use rustc_middle::ty::TyCtxt; use rustc_serialize::{Decodable, Encodable}; use rustc_span::Span; use rustc_span::source_map::Spanned; @@ -23,9 +21,10 @@ pub use rustc_type_ir::{TyDecoder, TyEncoder}; use crate::arena::ArenaAllocatable; use crate::infer::canonical::{CanonicalVarInfo, CanonicalVarInfos}; use crate::mir::interpret::{AllocId, ConstAllocation, CtfeProvenance}; +use crate::mir::mono::MonoItem; use crate::mir::{self}; use crate::traits; -use crate::ty::{self, AdtDef, GenericArgsRef, Ty}; +use crate::ty::{self, AdtDef, GenericArgsRef, Ty, TyCtxt}; /// The shorthand encoding uses an enum's variant index `usize` /// and is offset by this value so it never matches a real variant. @@ -147,6 +146,12 @@ impl<'tcx, E: TyEncoder>> Encodable for ty::Pattern<'tcx> { } } +impl<'tcx, E: TyEncoder>> Encodable for ty::ValTree<'tcx> { + fn encode(&self, e: &mut E) { + self.0.0.encode(e); + } +} + impl<'tcx, E: TyEncoder>> Encodable for ConstAllocation<'tcx> { fn encode(&self, e: &mut E) { self.inner().encode(e) @@ -356,12 +361,9 @@ impl<'tcx, D: TyDecoder>> Decodable for ty::Pattern<'tcx> { } } -impl<'tcx, D: TyDecoder>> RefDecodable<'tcx, D> for [ty::ValTree<'tcx>] { - fn decode(decoder: &mut D) -> &'tcx Self { - decoder - .interner() - .arena - .alloc_from_iter((0..decoder.read_usize()).map(|_| Decodable::decode(decoder))) +impl<'tcx, D: TyDecoder>> Decodable for ty::ValTree<'tcx> { + fn decode(decoder: &mut D) -> Self { + decoder.interner().intern_valtree(Decodable::decode(decoder)) } } diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 310552764224e..d30520a0222fc 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -5,7 +5,6 @@ use rustc_error_messages::MultiSpan; use rustc_macros::HashStable; use rustc_type_ir::{self as ir, TypeFlags, WithCachedTypeInfo}; -use crate::mir::interpret::Scalar; use crate::ty::{self, Ty, TyCtxt}; mod int; @@ -21,7 +20,7 @@ pub type ConstKind<'tcx> = ir::ConstKind>; pub type UnevaluatedConst<'tcx> = ir::UnevaluatedConst>; #[cfg(target_pointer_width = "64")] -rustc_data_structures::static_assert_size!(ConstKind<'_>, 32); +rustc_data_structures::static_assert_size!(ConstKind<'_>, 24); #[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable)] #[rustc_pass_by_value] @@ -110,8 +109,8 @@ impl<'tcx> Const<'tcx> { } #[inline] - pub fn new_value(tcx: TyCtxt<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> { - Const::new(tcx, ty::ConstKind::Value(ty, val)) + pub fn new_value(tcx: TyCtxt<'tcx>, valtree: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> { + Const::new(tcx, ty::ConstKind::Value(ty::Value { ty, valtree })) } #[inline] @@ -191,7 +190,7 @@ impl<'tcx> Const<'tcx> { .size; ty::Const::new_value( tcx, - ty::ValTree::from_scalar_int(ScalarInt::try_from_uint(bits, size).unwrap()), + ty::ValTree::from_scalar_int(tcx, ScalarInt::try_from_uint(bits, size).unwrap()), ty, ) } @@ -199,7 +198,7 @@ impl<'tcx> Const<'tcx> { #[inline] /// Creates an interned zst constant. pub fn zero_sized(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Self { - ty::Const::new_value(tcx, ty::ValTree::zst(), ty) + ty::Const::new_value(tcx, ty::ValTree::zst(tcx), ty) } #[inline] @@ -214,47 +213,31 @@ impl<'tcx> Const<'tcx> { Self::from_bits(tcx, n as u128, ty::TypingEnv::fully_monomorphized(), tcx.types.usize) } - /// Panics if self.kind != ty::ConstKind::Value - pub fn to_valtree(self) -> (ty::ValTree<'tcx>, Ty<'tcx>) { + /// Panics if `self.kind != ty::ConstKind::Value`. + pub fn to_value(self) -> ty::Value<'tcx> { match self.kind() { - ty::ConstKind::Value(ty, valtree) => (valtree, ty), + ty::ConstKind::Value(cv) => cv, _ => bug!("expected ConstKind::Value, got {:?}", self.kind()), } } - /// Attempts to convert to a `ValTree` - pub fn try_to_valtree(self) -> Option<(ty::ValTree<'tcx>, Ty<'tcx>)> { + /// Attempts to convert to a value. + /// + /// Note that this does not evaluate the constant. + pub fn try_to_value(self) -> Option> { match self.kind() { - ty::ConstKind::Value(ty, valtree) => Some((valtree, ty)), + ty::ConstKind::Value(cv) => Some(cv), _ => None, } } - #[inline] - pub fn try_to_scalar(self) -> Option<(Scalar, Ty<'tcx>)> { - let (valtree, ty) = self.try_to_valtree()?; - Some((valtree.try_to_scalar()?, ty)) - } - - pub fn try_to_bool(self) -> Option { - self.try_to_valtree()?.0.try_to_scalar_int()?.try_to_bool().ok() - } - + /// Convenience method to extract the value of a usize constant, + /// useful to get the length of an array type. + /// + /// Note that this does not evaluate the constant. #[inline] pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option { - self.try_to_valtree()?.0.try_to_target_usize(tcx) - } - - /// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of - /// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it - /// contains const generic parameters or pointers). - #[inline] - pub fn try_to_bits(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> Option { - let (scalar, ty) = self.try_to_scalar()?; - let scalar = scalar.try_to_scalar_int().ok()?; - let input = typing_env.with_post_analysis_normalized(tcx).as_query_input(ty); - let size = tcx.layout_of(input).ok()?.size; - Some(scalar.to_bits(size)) + self.try_to_value()?.try_to_target_usize(tcx) } pub fn is_ct_infer(self) -> bool { diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs index 9f9bf41c3355a..72263d8458085 100644 --- a/compiler/rustc_middle/src/ty/consts/valtree.rs +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -1,11 +1,13 @@ -use rustc_macros::{HashStable, TyDecodable, TyEncodable}; +use std::fmt; +use std::ops::Deref; + +use rustc_data_structures::intern::Interned; +use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; use super::ScalarInt; use crate::mir::interpret::Scalar; use crate::ty::{self, Ty, TyCtxt}; -#[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq)] -#[derive(HashStable)] /// This datastructure is used to represent the value of constants used in the type system. /// /// We explicitly choose a different datastructure from the way values are processed within @@ -18,7 +20,9 @@ use crate::ty::{self, Ty, TyCtxt}; /// /// `ValTree` does not have this problem with representation, as it only contains integers or /// lists of (nested) `ValTree`. -pub enum ValTree<'tcx> { +#[derive(Clone, Debug, Hash, Eq, PartialEq)] +#[derive(HashStable, TyEncodable, TyDecodable)] +pub enum ValTreeKind<'tcx> { /// integers, `bool`, `char` are represented as scalars. /// See the `ScalarInt` documentation for how `ScalarInt` guarantees that equal values /// of these types have the same representation. @@ -33,60 +37,137 @@ pub enum ValTree<'tcx> { /// the fields of the variant. /// /// ZST types are represented as an empty slice. - Branch(&'tcx [ValTree<'tcx>]), + Branch(Box<[ValTree<'tcx>]>), } -impl<'tcx> ValTree<'tcx> { - pub fn zst() -> Self { - Self::Branch(&[]) - } - +impl<'tcx> ValTreeKind<'tcx> { #[inline] - pub fn unwrap_leaf(self) -> ScalarInt { + pub fn unwrap_leaf(&self) -> ScalarInt { match self { - Self::Leaf(s) => s, + Self::Leaf(s) => *s, _ => bug!("expected leaf, got {:?}", self), } } #[inline] - pub fn unwrap_branch(self) -> &'tcx [Self] { + pub fn unwrap_branch(&self) -> &[ValTree<'tcx>] { match self { - Self::Branch(branch) => branch, + Self::Branch(branch) => &**branch, _ => bug!("expected branch, got {:?}", self), } } - pub fn from_raw_bytes<'a>(tcx: TyCtxt<'tcx>, bytes: &'a [u8]) -> Self { - let branches = bytes.iter().map(|b| Self::Leaf(ScalarInt::from(*b))); - let interned = tcx.arena.alloc_from_iter(branches); + pub fn try_to_scalar(&self) -> Option { + self.try_to_scalar_int().map(Scalar::Int) + } + + pub fn try_to_scalar_int(&self) -> Option { + match self { + Self::Leaf(s) => Some(*s), + Self::Branch(_) => None, + } + } + + pub fn try_to_branch(&self) -> Option<&[ValTree<'tcx>]> { + match self { + Self::Branch(branch) => Some(&**branch), + Self::Leaf(_) => None, + } + } +} - Self::Branch(interned) +/// An interned valtree. Use this rather than `ValTreeKind`, whenever possible. +/// +/// See the docs of [`ValTreeKind`] or the [dev guide] for an explanation of this type. +/// +/// [dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html#valtrees +#[derive(Copy, Clone, Hash, Eq, PartialEq)] +#[derive(HashStable)] +pub struct ValTree<'tcx>(pub(crate) Interned<'tcx, ValTreeKind<'tcx>>); + +impl<'tcx> ValTree<'tcx> { + /// Returns the zero-sized valtree: `Branch([])`. + pub fn zst(tcx: TyCtxt<'tcx>) -> Self { + tcx.consts.valtree_zst } - pub fn from_scalar_int(i: ScalarInt) -> Self { - Self::Leaf(i) + pub fn is_zst(self) -> bool { + matches!(*self, ValTreeKind::Branch(box [])) } - pub fn try_to_scalar(self) -> Option { - self.try_to_scalar_int().map(Scalar::Int) + pub fn from_raw_bytes(tcx: TyCtxt<'tcx>, bytes: &[u8]) -> Self { + let branches = bytes.iter().map(|&b| Self::from_scalar_int(tcx, b.into())); + Self::from_branches(tcx, branches) } - pub fn try_to_scalar_int(self) -> Option { - match self { - Self::Leaf(s) => Some(s), - Self::Branch(_) => None, + pub fn from_branches(tcx: TyCtxt<'tcx>, branches: impl IntoIterator) -> Self { + tcx.intern_valtree(ValTreeKind::Branch(branches.into_iter().collect())) + } + + pub fn from_scalar_int(tcx: TyCtxt<'tcx>, i: ScalarInt) -> Self { + tcx.intern_valtree(ValTreeKind::Leaf(i)) + } +} + +impl<'tcx> Deref for ValTree<'tcx> { + type Target = &'tcx ValTreeKind<'tcx>; + + #[inline] + fn deref(&self) -> &&'tcx ValTreeKind<'tcx> { + &self.0.0 + } +} + +impl fmt::Debug for ValTree<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +/// A type-level constant value. +/// +/// Represents a typed, fully evaluated constant. +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +#[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable, Lift)] +pub struct Value<'tcx> { + pub ty: Ty<'tcx>, + pub valtree: ValTree<'tcx>, +} + +impl<'tcx> Value<'tcx> { + /// Attempts to extract the raw bits from the constant. + /// + /// Fails if the value can't be represented as bits (e.g. because it is a reference + /// or an aggregate). + #[inline] + pub fn try_to_bits(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> Option { + let (ty::Bool | ty::Char | ty::Uint(_) | ty::Int(_) | ty::Float(_)) = self.ty.kind() else { + return None; + }; + let scalar = self.valtree.try_to_scalar_int()?; + let input = typing_env.with_post_analysis_normalized(tcx).as_query_input(self.ty); + let size = tcx.layout_of(input).ok()?.size; + Some(scalar.to_bits(size)) + } + + pub fn try_to_bool(self) -> Option { + if !self.ty.is_bool() { + return None; } + self.valtree.try_to_scalar_int()?.try_to_bool().ok() } pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option { - self.try_to_scalar_int().map(|s| s.to_target_usize(tcx)) + if !self.ty.is_usize() { + return None; + } + self.valtree.try_to_scalar_int().map(|s| s.to_target_usize(tcx)) } /// Get the values inside the ValTree as a slice of bytes. This only works for /// constants with types &str, &[u8], or [u8; _]. - pub fn try_to_raw_bytes(self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx [u8]> { - match ty.kind() { + pub fn try_to_raw_bytes(self, tcx: TyCtxt<'tcx>) -> Option<&'tcx [u8]> { + match self.ty.kind() { ty::Ref(_, inner_ty, _) => match inner_ty.kind() { // `&str` can be interpreted as raw bytes ty::Str => {} @@ -101,9 +182,18 @@ impl<'tcx> ValTree<'tcx> { _ => return None, } - Some( - tcx.arena - .alloc_from_iter(self.unwrap_branch().into_iter().map(|v| v.unwrap_leaf().to_u8())), - ) + Some(tcx.arena.alloc_from_iter( + self.valtree.unwrap_branch().into_iter().map(|v| v.unwrap_leaf().to_u8()), + )) + } +} + +impl<'tcx> rustc_type_ir::inherent::ValueConst> for Value<'tcx> { + fn ty(self) -> Ty<'tcx> { + self.ty + } + + fn valtree(self) -> ValTree<'tcx> { + self.valtree } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index d26c007d227df..cb8dc6921487e 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -10,7 +10,7 @@ use std::cmp::Ordering; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use std::ops::{Bound, Deref}; -use std::sync::OnceLock; +use std::sync::{Arc, OnceLock}; use std::{fmt, iter, mem}; use rustc_abi::{ExternAbi, FieldIdx, Layout, LayoutData, TargetDataLayout, VariantIdx}; @@ -24,7 +24,7 @@ use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{ - self, DynSend, DynSync, FreezeReadGuard, Lock, Lrc, RwLock, WorkerLocal, + self, DynSend, DynSync, FreezeReadGuard, Lock, RwLock, WorkerLocal, }; use rustc_data_structures::unord::UnordSet; use rustc_errors::{ @@ -33,7 +33,7 @@ use rustc_errors::{ use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::definitions::Definitions; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::VisitorExt; use rustc_hir::lang_items::LangItem; use rustc_hir::{self as hir, Attribute, HirId, Node, TraitCandidate}; use rustc_index::IndexVec; @@ -52,7 +52,9 @@ use rustc_type_ir::TyKind::*; use rustc_type_ir::fold::TypeFoldable; use rustc_type_ir::lang_items::TraitSolverLangItem; pub use rustc_type_ir::lift::Lift; -use rustc_type_ir::{CollectAndApply, Interner, TypeFlags, WithCachedTypeInfo, search_graph}; +use rustc_type_ir::{ + CollectAndApply, Interner, TypeFlags, WithCachedTypeInfo, elaborate, search_graph, +}; use tracing::{debug, instrument}; use crate::arena::Arena; @@ -60,7 +62,7 @@ use crate::dep_graph::{DepGraph, DepKindStruct}; use crate::infer::canonical::{CanonicalParamEnvCache, CanonicalVarInfo, CanonicalVarInfos}; use crate::lint::lint_level; use crate::metadata::ModChild; -use crate::middle::codegen_fn_attrs::CodegenFnAttrs; +use crate::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature}; use crate::middle::{resolve_bound_vars, stability}; use crate::mir::interpret::{self, Allocation, ConstAllocation}; use crate::mir::{Body, Local, Place, PlaceElem, ProjectionKind, Promoted}; @@ -74,11 +76,11 @@ use crate::traits::solve::{ }; use crate::ty::predicate::ExistentialPredicateStableCmpExt as _; use crate::ty::{ - self, AdtDef, AdtDefData, AdtKind, Binder, BoundConstness, Clause, Clauses, Const, GenericArg, - GenericArgs, GenericArgsRef, GenericParamDefKind, ImplPolarity, List, ListWithCachedTypeInfo, - ParamConst, ParamTy, Pattern, PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate, - PredicateKind, PredicatePolarity, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, - TyKind, TyVid, Visibility, + self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Clauses, Const, GenericArg, GenericArgs, + GenericArgsRef, GenericParamDefKind, List, ListWithCachedTypeInfo, ParamConst, ParamTy, + Pattern, PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, + PredicatePolarity, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, + ValTree, ValTreeKind, Visibility, }; #[allow(rustc::usage_of_ty_tykind)] @@ -140,10 +142,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type ParamConst = ty::ParamConst; type BoundConst = ty::BoundVar; - type ValueConst = ty::ValTree<'tcx>; + type ValueConst = ty::Value<'tcx>; type ExprConst = ty::Expr<'tcx>; - type Region = Region<'tcx>; + type ValTree = ty::ValTree<'tcx>; + type Region = Region<'tcx>; type EarlyParamRegion = ty::EarlyParamRegion; type LateParamRegion = ty::LateParamRegion; type BoundRegion = ty::BoundRegion; @@ -191,6 +194,14 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.variances_of(def_id) } + fn opt_alias_variances( + self, + kind: impl Into, + def_id: DefId, + ) -> Option<&'tcx [ty::Variance]> { + self.opt_alias_variances(kind, def_id) + } + fn type_of(self, def_id: DefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { self.type_of(def_id) } @@ -343,6 +354,20 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.item_bounds(def_id).map_bound(IntoIterator::into_iter) } + fn item_self_bounds( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { + self.item_self_bounds(def_id).map_bound(IntoIterator::into_iter) + } + + fn item_non_self_bounds( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { + self.item_non_self_bounds(def_id).map_bound(IntoIterator::into_iter) + } + fn predicates_of( self, def_id: DefId, @@ -613,7 +638,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.coroutine_is_async_gen(coroutine_def_id) } - type UnsizingParams = &'tcx rustc_index::bit_set::BitSet; + type UnsizingParams = &'tcx rustc_index::bit_set::DenseBitSet; fn unsizing_params_for_adt(self, adt_def_id: DefId) -> Self::UnsizingParams { self.unsizing_params_for_adt(adt_def_id) } @@ -665,6 +690,7 @@ bidirectional_lang_item_map! { AsyncFnOnce, AsyncFnOnceOutput, AsyncIterator, + BikeshedGuaranteedNoDrop, CallOnceFuture, CallRefFuture, Clone, @@ -781,6 +807,7 @@ pub struct CtxtInterners<'tcx> { local_def_ids: InternedSet<'tcx, List>, captures: InternedSet<'tcx, List<&'tcx ty::CapturedPlace<'tcx>>>, offset_of: InternedSet<'tcx, List<(VariantIdx, FieldIdx)>>, + valtree: InternedSet<'tcx, ty::ValTreeKind<'tcx>>, } impl<'tcx> CtxtInterners<'tcx> { @@ -810,6 +837,7 @@ impl<'tcx> CtxtInterners<'tcx> { local_def_ids: Default::default(), captures: Default::default(), offset_of: Default::default(), + valtree: Default::default(), } } @@ -1001,6 +1029,8 @@ pub struct CommonConsts<'tcx> { pub unit: Const<'tcx>, pub true_: Const<'tcx>, pub false_: Const<'tcx>, + /// Use [`ty::ValTree::zst`] instead. + pub(crate) valtree_zst: ValTree<'tcx>, } impl<'tcx> CommonTypes<'tcx> { @@ -1069,10 +1099,13 @@ impl<'tcx> CommonLifetimes<'tcx> { .map(|i| { (0..NUM_PREINTERNED_RE_LATE_BOUNDS_V) .map(|v| { - mk(ty::ReBound(ty::DebruijnIndex::from(i), ty::BoundRegion { - var: ty::BoundVar::from(v), - kind: ty::BoundRegionKind::Anon, - })) + mk(ty::ReBound( + ty::DebruijnIndex::from(i), + ty::BoundRegion { + var: ty::BoundVar::from(v), + kind: ty::BoundRegionKind::Anon, + }, + )) }) .collect() }) @@ -1101,16 +1134,30 @@ impl<'tcx> CommonConsts<'tcx> { ) }; + let mk_valtree = |v| { + ty::ValTree(Interned::new_unchecked( + interners.valtree.intern(v, |v| InternedInSet(interners.arena.alloc(v))).0, + )) + }; + + let valtree_zst = mk_valtree(ty::ValTreeKind::Branch(Box::default())); + let valtree_true = mk_valtree(ty::ValTreeKind::Leaf(ty::ScalarInt::TRUE)); + let valtree_false = mk_valtree(ty::ValTreeKind::Leaf(ty::ScalarInt::FALSE)); + CommonConsts { - unit: mk_const(ty::ConstKind::Value(types.unit, ty::ValTree::zst())), - true_: mk_const(ty::ConstKind::Value( - types.bool, - ty::ValTree::Leaf(ty::ScalarInt::TRUE), - )), - false_: mk_const(ty::ConstKind::Value( - types.bool, - ty::ValTree::Leaf(ty::ScalarInt::FALSE), - )), + unit: mk_const(ty::ConstKind::Value(ty::Value { + ty: types.unit, + valtree: valtree_zst, + })), + true_: mk_const(ty::ConstKind::Value(ty::Value { + ty: types.bool, + valtree: valtree_true, + })), + false_: mk_const(ty::ConstKind::Value(ty::Value { + ty: types.bool, + valtree: valtree_false, + })), + valtree_zst, } } } @@ -1270,9 +1317,6 @@ pub struct TyCtxt<'tcx> { gcx: &'tcx GlobalCtxt<'tcx>, } -// Explicitly implement `DynSync` and `DynSend` for `TyCtxt` to short circuit trait resolution. -unsafe impl DynSend for TyCtxt<'_> {} -unsafe impl DynSync for TyCtxt<'_> {} fn _assert_tcx_fields() { sync::assert_dyn_sync::<&'_ GlobalCtxt<'_>>(); sync::assert_dyn_send::<&'_ GlobalCtxt<'_>>(); @@ -1346,7 +1390,34 @@ pub struct GlobalCtxt<'tcx> { pub data_layout: TargetDataLayout, /// Stores memory for globals (statics/consts). - pub(crate) alloc_map: Lock>, + pub(crate) alloc_map: interpret::AllocMap<'tcx>, + + current_gcx: CurrentGcx, +} + +impl<'tcx> GlobalCtxt<'tcx> { + /// Installs `self` in a `TyCtxt` and `ImplicitCtxt` for the duration of + /// `f`. + pub fn enter(&'tcx self, f: F) -> R + where + F: FnOnce(TyCtxt<'tcx>) -> R, + { + let icx = tls::ImplicitCtxt::new(self); + + // Reset `current_gcx` to `None` when we exit. + let _on_drop = defer(move || { + *self.current_gcx.value.write() = None; + }); + + // Set this `GlobalCtxt` as the current one. + { + let mut guard = self.current_gcx.value.write(); + assert!(guard.is_none(), "no `GlobalCtxt` is currently set"); + *guard = Some(self as *const _ as *const ()); + } + + tls::enter_context(&icx, || f(icx.tcx)) + } } /// This is used to get a reference to a `GlobalCtxt` if one is available. @@ -1359,7 +1430,7 @@ pub struct GlobalCtxt<'tcx> { pub struct CurrentGcx { /// This stores a pointer to a `GlobalCtxt`. This is set to `Some` inside `GlobalCtxt::enter` /// and reset to `None` when that function returns or unwinds. - value: Lrc>>, + value: Arc>>, } unsafe impl DynSend for CurrentGcx {} @@ -1367,7 +1438,7 @@ unsafe impl DynSync for CurrentGcx {} impl CurrentGcx { pub fn new() -> Self { - Self { value: Lrc::new(RwLock::new(None)) } + Self { value: Arc::new(RwLock::new(None)) } } pub fn access(&self, f: impl for<'tcx> FnOnce(&'tcx GlobalCtxt<'tcx>) -> R) -> R { @@ -1536,24 +1607,12 @@ impl<'tcx> TyCtxt<'tcx> { new_solver_evaluation_cache: Default::default(), canonical_param_env_cache: Default::default(), data_layout, - alloc_map: Lock::new(interpret::AllocMap::new()), - }); - - let icx = tls::ImplicitCtxt::new(&gcx); - - // Reset `current_gcx` to `None` when we exit. - let _on_drop = defer(|| { - *current_gcx.value.write() = None; + alloc_map: interpret::AllocMap::new(), + current_gcx, }); - // Set this `GlobalCtxt` as the current one. - { - let mut guard = current_gcx.value.write(); - assert!(guard.is_none(), "no `GlobalCtxt` is currently set"); - *guard = Some(&gcx as *const _ as *const ()); - } - - tls::enter_context(&icx, || f(icx.tcx)) + // This is a separate function to work around a crash with parallel rustc (#135870) + gcx.enter(f) } /// Obtain all lang items of this crate and all dependencies (recursively) @@ -1776,6 +1835,37 @@ impl<'tcx> TyCtxt<'tcx> { pub fn dcx(self) -> DiagCtxtHandle<'tcx> { self.sess.dcx() } + + pub fn is_target_feature_call_safe( + self, + callee_features: &[TargetFeature], + body_features: &[TargetFeature], + ) -> bool { + // If the called function has target features the calling function hasn't, + // the call requires `unsafe`. Don't check this on wasm + // targets, though. For more information on wasm see the + // is_like_wasm check in hir_analysis/src/collect.rs + self.sess.target.options.is_like_wasm + || callee_features + .iter() + .all(|feature| body_features.iter().any(|f| f.name == feature.name)) + } + + /// Returns the safe version of the signature of the given function, if calling it + /// would be safe in the context of the given caller. + pub fn adjust_target_feature_sig( + self, + fun_def: DefId, + fun_sig: ty::Binder<'tcx, ty::FnSig<'tcx>>, + caller: DefId, + ) -> Option>> { + let fun_features = &self.codegen_fn_attrs(fun_def).target_features; + let callee_features = &self.codegen_fn_attrs(caller).target_features; + if self.is_target_feature_call_safe(&fun_features, &callee_features) { + return Some(fun_sig.map_bound(|sig| ty::FnSig { safety: hir::Safety::Safe, ..sig })); + } + None + } } impl<'tcx> TyCtxtAt<'tcx> { @@ -1891,7 +1981,7 @@ impl<'tcx> TyCtxt<'tcx> { ) -> &'tcx rustc_hir::def_path_hash_map::DefPathHashMap { // Create a dependency to the crate to be sure we re-execute this when the amount of // definitions change. - self.ensure().hir_crate(()); + self.ensure_ok().hir_crate(()); // Freeze definitions once we start iterating on them, to prevent adding new ones // while iterating. If some query needs to add definitions, it should be `ensure`d above. self.untracked.definitions.freeze().def_path_hash_to_def_index_map() @@ -1995,7 +2085,7 @@ impl<'tcx> TyCtxt<'tcx> { }; let mut v = TraitObjectVisitor(vec![], self.hir()); - v.visit_ty(hir_output); + v.visit_ty_unambig(hir_output); v.0 } @@ -2017,7 +2107,7 @@ impl<'tcx> TyCtxt<'tcx> { && let Some(alias_ty) = self.hir_node_by_def_id(local_id).alias_ty() // it is type alias && let Some(alias_generics) = self.hir_node_by_def_id(local_id).generics() { - v.visit_ty(alias_ty); + v.visit_ty_unambig(alias_ty); if !v.0.is_empty() { return Some(( v.0, @@ -2078,12 +2168,23 @@ impl<'tcx> TyCtxt<'tcx> { self.limits(()).move_size_limit } + /// All traits in the crate graph, including those not visible to the user. pub fn all_traits(self) -> impl Iterator + 'tcx { iter::once(LOCAL_CRATE) .chain(self.crates(()).iter().copied()) .flat_map(move |cnum| self.traits(cnum).iter().copied()) } + /// All traits that are visible within the crate graph (i.e. excluding private dependencies). + pub fn visible_traits(self) -> impl Iterator + 'tcx { + let visible_crates = + self.crates(()).iter().copied().filter(move |cnum| self.is_user_visible_dep(*cnum)); + + iter::once(LOCAL_CRATE) + .chain(visible_crates) + .flat_map(move |cnum| self.traits(cnum).iter().copied()) + } + #[inline] pub fn local_visibility(self, def_id: LocalDefId) -> Visibility { self.visibility(def_id).expect_local() @@ -2166,45 +2267,24 @@ macro_rules! nop_list_lift { }; } -nop_lift! {type_; Ty<'a> => Ty<'tcx>} -nop_lift! {region; Region<'a> => Region<'tcx>} -nop_lift! {const_; Const<'a> => Const<'tcx>} -nop_lift! {pat; Pattern<'a> => Pattern<'tcx>} -nop_lift! {const_allocation; ConstAllocation<'a> => ConstAllocation<'tcx>} -nop_lift! {predicate; Predicate<'a> => Predicate<'tcx>} -nop_lift! {predicate; Clause<'a> => Clause<'tcx>} -nop_lift! {layout; Layout<'a> => Layout<'tcx>} - -nop_list_lift! {type_lists; Ty<'a> => Ty<'tcx>} -nop_list_lift! {poly_existential_predicates; PolyExistentialPredicate<'a> => PolyExistentialPredicate<'tcx>} -nop_list_lift! {bound_variable_kinds; ty::BoundVariableKind => ty::BoundVariableKind} - -// This is the impl for `&'a GenericArgs<'a>`. -nop_list_lift! {args; GenericArg<'a> => GenericArg<'tcx>} - -macro_rules! nop_slice_lift { - ($ty:ty => $lifted:ty) => { - impl<'a, 'tcx> Lift> for &'a [$ty] { - type Lifted = &'tcx [$lifted]; - fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Option { - if self.is_empty() { - return Some(&[]); - } - tcx.interners - .arena - .dropless - .contains_slice(self) - .then(|| unsafe { mem::transmute(self) }) - } - } - }; +nop_lift! { type_; Ty<'a> => Ty<'tcx> } +nop_lift! { region; Region<'a> => Region<'tcx> } +nop_lift! { const_; Const<'a> => Const<'tcx> } +nop_lift! { pat; Pattern<'a> => Pattern<'tcx> } +nop_lift! { const_allocation; ConstAllocation<'a> => ConstAllocation<'tcx> } +nop_lift! { predicate; Predicate<'a> => Predicate<'tcx> } +nop_lift! { predicate; Clause<'a> => Clause<'tcx> } +nop_lift! { layout; Layout<'a> => Layout<'tcx> } +nop_lift! { valtree; ValTree<'a> => ValTree<'tcx> } + +nop_list_lift! { type_lists; Ty<'a> => Ty<'tcx> } +nop_list_lift! { + poly_existential_predicates; PolyExistentialPredicate<'a> => PolyExistentialPredicate<'tcx> } +nop_list_lift! { bound_variable_kinds; ty::BoundVariableKind => ty::BoundVariableKind } -nop_slice_lift! {ty::ValTree<'a> => ty::ValTree<'tcx>} - -TrivialLiftImpls! { - ImplPolarity, PredicatePolarity, Promoted, BoundConstness, -} +// This is the impl for `&'a GenericArgs<'a>`. +nop_list_lift! { args; GenericArg<'a> => GenericArg<'tcx> } macro_rules! sty_debug_print { ($fmt: expr, $ctxt: expr, $($variant: ident),*) => {{ @@ -2281,51 +2361,41 @@ macro_rules! sty_debug_print { } impl<'tcx> TyCtxt<'tcx> { - pub fn debug_stats(self) -> impl std::fmt::Debug + 'tcx { - struct DebugStats<'tcx>(TyCtxt<'tcx>); - - impl<'tcx> std::fmt::Debug for DebugStats<'tcx> { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - sty_debug_print!( - fmt, - self.0, - Adt, - Array, - Slice, - RawPtr, - Ref, - FnDef, - FnPtr, - UnsafeBinder, - Placeholder, - Coroutine, - CoroutineWitness, - Dynamic, - Closure, - CoroutineClosure, - Tuple, - Bound, - Param, - Infer, - Alias, - Pat, - Foreign - )?; - - writeln!(fmt, "GenericArgs interner: #{}", self.0.interners.args.len())?; - writeln!(fmt, "Region interner: #{}", self.0.interners.region.len())?; - writeln!( - fmt, - "Const Allocation interner: #{}", - self.0.interners.const_allocation.len() - )?; - writeln!(fmt, "Layout interner: #{}", self.0.interners.layout.len())?; - - Ok(()) - } - } - - DebugStats(self) + pub fn debug_stats(self) -> impl fmt::Debug + 'tcx { + fmt::from_fn(move |fmt| { + sty_debug_print!( + fmt, + self, + Adt, + Array, + Slice, + RawPtr, + Ref, + FnDef, + FnPtr, + UnsafeBinder, + Placeholder, + Coroutine, + CoroutineWitness, + Dynamic, + Closure, + CoroutineClosure, + Tuple, + Bound, + Param, + Infer, + Alias, + Pat, + Foreign + )?; + + writeln!(fmt, "GenericArgs interner: #{}", self.interners.args.len())?; + writeln!(fmt, "Region interner: #{}", self.interners.region.len())?; + writeln!(fmt, "Const Allocation interner: #{}", self.interners.const_allocation.len())?; + writeln!(fmt, "Layout interner: #{}", self.interners.layout.len())?; + + Ok(()) + }) } } @@ -2460,6 +2530,7 @@ macro_rules! direct_interners { // crate only, and have a corresponding `mk_` function. direct_interners! { region: pub(crate) intern_region(RegionKind<'tcx>): Region -> Region<'tcx>, + valtree: pub(crate) intern_valtree(ValTreeKind<'tcx>): ValTree -> ValTree<'tcx>, pat: pub mk_pat(PatternKind<'tcx>): Pattern -> Pattern<'tcx>, const_allocation: pub mk_const_alloc(Allocation): ConstAllocation -> ConstAllocation<'tcx>, layout: pub mk_layout(LayoutData): Layout -> Layout<'tcx>, @@ -2516,7 +2587,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Given the def_id of a Trait `trait_def_id` and the name of an associated item `assoc_name` /// returns true if the `trait_def_id` defines an associated item of name `assoc_name`. pub fn trait_may_define_assoc_item(self, trait_def_id: DefId, assoc_name: Ident) -> bool { - self.supertrait_def_ids(trait_def_id).any(|trait_did| { + elaborate::supertrait_def_ids(self, trait_def_id).any(|trait_did| { self.associated_items(trait_did) .filter_by_name_unhygienic(assoc_name.name) .any(|item| self.hygienic_eq(assoc_name, item.ident(self), trait_did)) @@ -2528,7 +2599,7 @@ impl<'tcx> TyCtxt<'tcx> { let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = ty.kind() else { return false }; let future_trait = self.require_lang_item(LangItem::Future, None); - self.explicit_item_super_predicates(def_id).skip_binder().iter().any(|&(predicate, _)| { + self.explicit_item_self_bounds(def_id).skip_binder().iter().any(|&(predicate, _)| { let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() else { return false; }; @@ -2537,14 +2608,6 @@ impl<'tcx> TyCtxt<'tcx> { }) } - /// Computes the def-ids of the transitive supertraits of `trait_def_id`. This (intentionally) - /// does not compute the full elaborated super-predicates but just the set of def-ids. It is used - /// to identify which traits may define a given associated type to help avoid cycle errors, - /// and to make size estimates for vtable layout computation. - pub fn supertrait_def_ids(self, trait_def_id: DefId) -> impl Iterator + 'tcx { - rustc_type_ir::elaborate::supertrait_def_ids(self, trait_def_id) - } - /// Given a closure signature, returns an equivalent fn signature. Detuples /// and so forth -- so e.g., if we have a sig with `Fn<(u32, i32)>` then /// you would get a `fn(u32, i32)`. @@ -3081,12 +3144,15 @@ impl<'tcx> TyCtxt<'tcx> { } let generics = self.generics_of(new_parent); - return ty::Region::new_early_param(self, ty::EarlyParamRegion { - index: generics - .param_def_id_to_index(self, ebv.to_def_id()) - .expect("early-bound var should be present in fn generics"), - name: self.item_name(ebv.to_def_id()), - }); + return ty::Region::new_early_param( + self, + ty::EarlyParamRegion { + index: generics + .param_def_id_to_index(self, ebv.to_def_id()) + .expect("early-bound var should be present in fn generics"), + name: self.item_name(ebv.to_def_id()), + }, + ); } resolve_bound_vars::ResolvedArg::LateBound(_, _, lbv) => { let new_parent = self.local_parent(lbv); @@ -3165,7 +3231,7 @@ impl<'tcx> TyCtxt<'tcx> { self.resolutions(()).module_children.get(&def_id).map_or(&[], |v| &v[..]) } - pub fn resolver_for_lowering(self) -> &'tcx Steal<(ty::ResolverAstLowering, Lrc)> { + pub fn resolver_for_lowering(self) -> &'tcx Steal<(ty::ResolverAstLowering, Arc)> { self.resolver_for_lowering_raw(()).0 } diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 406e732744bbb..cb218a27e6237 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -5,11 +5,11 @@ use std::ops::ControlFlow; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{ - Applicability, Diag, DiagArgValue, IntoDiagArg, into_diag_arg_using_display, pluralize, + Applicability, Diag, DiagArgValue, IntoDiagArg, into_diag_arg_using_display, listify, pluralize, }; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, LangItem, PredicateOrigin, WherePredicateKind}; +use rustc_hir::{self as hir, AmbigArg, LangItem, PredicateOrigin, WherePredicateKind}; use rustc_span::{BytePos, Span}; use rustc_type_ir::TyKind::*; @@ -69,10 +69,10 @@ impl<'tcx> Ty<'tcx> { /// description in error messages. This is used in the primary span label. Beyond what /// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to /// ADTs with no type arguments. - pub fn is_simple_text(self, tcx: TyCtxt<'tcx>) -> bool { + pub fn is_simple_text(self) -> bool { match self.kind() { Adt(_, args) => args.non_erasable_generics().next().is_none(), - Ref(_, ty, _) => ty.is_simple_text(tcx), + Ref(_, ty, _) => ty.is_simple_text(), _ => self.is_simple_ty(), } } @@ -336,7 +336,7 @@ pub fn suggest_constraining_type_params<'a>( .collect(); constraints - .retain(|(_, def_id, _)| def_id.map_or(true, |def| !bound_trait_defs.contains(&def))); + .retain(|(_, def_id, _)| def_id.is_none_or(|def| !bound_trait_defs.contains(&def))); if constraints.is_empty() { continue; @@ -362,11 +362,8 @@ pub fn suggest_constraining_type_params<'a>( let n = trait_names.len(); let stable = if all_stable { "" } else { "unstable " }; let trait_ = if all_known { format!("trait{}", pluralize!(n)) } else { String::new() }; - format!("{stable}{trait_}{}", match &trait_names[..] { - [t] => format!(" {t}"), - [ts @ .., last] => format!(" {} and {last}", ts.join(", ")), - [] => return false, - },) + let Some(trait_names) = listify(&trait_names, |n| n.to_string()) else { return false }; + format!("{stable}{trait_} {trait_names}") } else { // We're more explicit when there's a mix of stable and unstable traits. let mut trait_names = constraints @@ -378,10 +375,9 @@ pub fn suggest_constraining_type_params<'a>( .collect::>(); trait_names.sort(); trait_names.dedup(); - match &trait_names[..] { - [t] => t.to_string(), - [ts @ .., last] => format!("{} and {last}", ts.join(", ")), - [] => return false, + match listify(&trait_names, |t| t.to_string()) { + Some(names) => names, + None => return false, } }; let constraint = constraint.join(" + "); @@ -570,18 +566,18 @@ pub fn suggest_constraining_type_params<'a>( pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>, pub crate::hir::map::Map<'tcx>); impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> { - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) { match ty.kind { - hir::TyKind::TraitObject( - _, - hir::Lifetime { + hir::TyKind::TraitObject(_, tagged_ptr) + if let hir::Lifetime { res: hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static, .. - }, - _, - ) - | hir::TyKind::OpaqueDef(..) => self.0.push(ty), + } = tagged_ptr.pointer() => + { + self.0.push(ty.as_unambig_ty()) + } + hir::TyKind::OpaqueDef(..) => self.0.push(ty.as_unambig_ty()), _ => {} } hir::intravisit::walk_ty(self, ty); diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 714094db0530f..8c1991ddb36ee 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -10,8 +10,8 @@ use rustc_hir::def::{CtorOf, DefKind}; use rustc_macros::extension; pub use rustc_type_ir::error::ExpectedFound; -use crate::ty::print::{FmtPrinter, PrettyPrinter, with_forced_trimmed_paths}; -use crate::ty::{self, Ty, TyCtxt}; +use crate::ty::print::{FmtPrinter, Print, with_forced_trimmed_paths}; +use crate::ty::{self, Lift, Ty, TyCtxt}; pub type TypeError<'tcx> = rustc_type_ir::error::TypeError>; @@ -109,6 +109,9 @@ impl<'tcx> TypeError<'tcx> { TypeError::ConstMismatch(ref values) => { format!("expected `{}`, found `{}`", values.expected, values.found).into() } + TypeError::ForceInlineCast => { + "cannot coerce functions which must be inlined to function pointers".into() + } TypeError::IntrinsicCast => "cannot coerce intrinsics to function pointers".into(), TypeError::TargetFeatureCast(_) => { "cannot coerce functions with `#[target_feature]` to safe function pointers".into() @@ -156,8 +159,8 @@ impl<'tcx> Ty<'tcx> { ty::Error(_) => "type error".into(), _ => { let width = tcx.sess.diagnostic_width(); - let length_limit = std::cmp::max(width / 4, 15); - format!("`{}`", tcx.ty_string_with_limit(self, length_limit)).into() + let length_limit = std::cmp::max(width / 4, 40); + format!("`{}`", tcx.string_with_limit(self, length_limit)).into() } } } @@ -210,10 +213,14 @@ impl<'tcx> Ty<'tcx> { } impl<'tcx> TyCtxt<'tcx> { - pub fn ty_string_with_limit(self, ty: Ty<'tcx>, length_limit: usize) -> String { + pub fn string_with_limit<'a, T>(self, p: T, length_limit: usize) -> String + where + T: Print<'tcx, FmtPrinter<'a, 'tcx>> + Lift> + Copy, + >>::Lifted: Print<'tcx, FmtPrinter<'a, 'tcx>>, + { let mut type_limit = 50; let regular = FmtPrinter::print_string(self, hir::def::Namespace::TypeNS, |cx| { - cx.pretty_print_type(ty) + self.lift(p).expect("could not lift for printing").print(cx) }) .expect("could not write to `String`"); if regular.len() <= length_limit { @@ -228,7 +235,10 @@ impl<'tcx> TyCtxt<'tcx> { hir::def::Namespace::TypeNS, rustc_session::Limit(type_limit), ); - cx.pretty_print_type(ty).expect("could not write to `String`"); + self.lift(p) + .expect("could not lift for printing") + .print(&mut cx) + .expect("could not print type"); cx.into_buffer() }); if short.len() <= length_limit || type_limit == 0 { @@ -239,9 +249,17 @@ impl<'tcx> TyCtxt<'tcx> { short } - pub fn short_ty_string(self, ty: Ty<'tcx>, path: &mut Option) -> String { + /// When calling this after a `Diag` is constructed, the preferred way of doing so is + /// `tcx.short_string(ty, diag.long_ty_path())`. The diagnostic itself is the one that keeps + /// the existence of a "long type" anywhere in the diagnostic, so the note telling the user + /// where we wrote the file to is only printed once. + pub fn short_string<'a, T>(self, p: T, path: &mut Option) -> String + where + T: Print<'tcx, FmtPrinter<'a, 'tcx>> + Lift> + Copy + Hash, + >>::Lifted: Print<'tcx, FmtPrinter<'a, 'tcx>>, + { let regular = FmtPrinter::print_string(self, hir::def::Namespace::TypeNS, |cx| { - cx.pretty_print_type(ty) + self.lift(p).expect("could not lift for printing").print(cx) }) .expect("could not write to `String`"); @@ -254,13 +272,13 @@ impl<'tcx> TyCtxt<'tcx> { if regular.len() <= width * 2 / 3 { return regular; } - let short = self.ty_string_with_limit(ty, length_limit); + let short = self.string_with_limit(p, length_limit); if regular == short { return regular; } // Ensure we create an unique file for the type passed in when we create a file. let mut s = DefaultHasher::new(); - ty.hash(&mut s); + p.hash(&mut s); let hash = s.finish(); *path = Some(path.take().unwrap_or_else(|| { self.output_filenames(()).temp_path_ext(&format!("long-type-{hash}.txt"), None) diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index 0af57f636aaab..ec0498b168c01 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -381,7 +381,7 @@ impl FlagComputation { self.add_flags(TypeFlags::HAS_CT_PLACEHOLDER); self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); } - ty::ConstKind::Value(ty, _) => self.add_ty(ty), + ty::ConstKind::Value(cv) => self.add_ty(cv.ty), ty::ConstKind::Expr(e) => self.add_args(e.args()), ty::ConstKind::Error(_) => self.add_flags(TypeFlags::HAS_ERROR), } diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index 067516917ef77..4ea4050ed8b1d 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -280,21 +280,26 @@ impl<'tcx> TyCtxt<'tcx> { T: TypeFoldable>, { let shift_bv = |bv: ty::BoundVar| ty::BoundVar::from_usize(bv.as_usize() + bound_vars); - self.replace_escaping_bound_vars_uncached(value, FnMutDelegate { - regions: &mut |r: ty::BoundRegion| { - ty::Region::new_bound(self, ty::INNERMOST, ty::BoundRegion { - var: shift_bv(r.var), - kind: r.kind, - }) + self.replace_escaping_bound_vars_uncached( + value, + FnMutDelegate { + regions: &mut |r: ty::BoundRegion| { + ty::Region::new_bound( + self, + ty::INNERMOST, + ty::BoundRegion { var: shift_bv(r.var), kind: r.kind }, + ) + }, + types: &mut |t: ty::BoundTy| { + Ty::new_bound( + self, + ty::INNERMOST, + ty::BoundTy { var: shift_bv(t.var), kind: t.kind }, + ) + }, + consts: &mut |c| ty::Const::new_bound(self, ty::INNERMOST, shift_bv(c)), }, - types: &mut |t: ty::BoundTy| { - Ty::new_bound(self, ty::INNERMOST, ty::BoundTy { - var: shift_bv(t.var), - kind: t.kind, - }) - }, - consts: &mut |c| ty::Const::new_bound(self, ty::INNERMOST, shift_bv(c)), - }) + ) } /// Replaces any late-bound regions bound in `value` with `'erased`. Useful in codegen but also diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 49b5588e261af..98ca71b86be07 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -5,18 +5,18 @@ use std::path::PathBuf; use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; -use rustc_hir::def::Namespace; +use rustc_hir::def::{CtorKind, DefKind, Namespace}; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::lang_items::LangItem; use rustc_index::bit_set::FiniteBitSet; use rustc_macros::{Decodable, Encodable, HashStable, Lift, TyDecodable, TyEncodable}; -use rustc_middle::ty::normalize_erasing_regions::NormalizationError; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::{DUMMY_SP, Span, Symbol}; use tracing::{debug, instrument}; use crate::error; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use crate::ty::normalize_erasing_regions::NormalizationError; use crate::ty::print::{FmtPrinter, Printer, shrunk_instance_name}; use crate::ty::{ self, EarlyBinder, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, @@ -111,8 +111,9 @@ pub enum InstanceKind<'tcx> { /// Dynamic dispatch to `::fn`. /// - /// This `InstanceKind` does not have callable MIR. Calls to `Virtual` instances must be - /// codegen'd as virtual calls through the vtable. + /// This `InstanceKind` may have a callable MIR as the default implementation. + /// Calls to `Virtual` instances must be codegen'd as virtual calls through the vtable. + /// *This means we might not know exactly what is being called.* /// /// If this is reified to a `fn` pointer, a `ReifyShim` is used (see `ReifyShim` above for more /// details on that). @@ -328,7 +329,7 @@ impl<'tcx> InstanceKind<'tcx> { // We include enums without destructors to allow, say, optimizing // drops of `Option::None` before LTO. We also respect the intent of // `#[inline]` on `Drop::drop` implementations. - return ty.ty_adt_def().map_or(true, |adt_def| { + return ty.ty_adt_def().is_none_or(|adt_def| { match *self { ty::InstanceKind::DropGlue(..) => adt_def.destructor(tcx).map(|dtor| dtor.did), ty::InstanceKind::AsyncDropGlueCtorShim(..) => { @@ -498,7 +499,8 @@ impl<'tcx> Instance<'tcx> { /// Resolves a `(def_id, args)` pair to an (optional) instance -- most commonly, /// this is used to find the precise code that will run for a trait method invocation, - /// if known. + /// if known. This should only be used for functions and consts. If you want to + /// resolve an associated type, use [`TyCtxt::try_normalize_erasing_regions`]. /// /// Returns `Ok(None)` if we cannot resolve `Instance` to a specific instance. /// For example, in a context like this, @@ -527,6 +529,23 @@ impl<'tcx> Instance<'tcx> { def_id: DefId, args: GenericArgsRef<'tcx>, ) -> Result>, ErrorGuaranteed> { + assert_matches!( + tcx.def_kind(def_id), + DefKind::Fn + | DefKind::AssocFn + | DefKind::Const + | DefKind::AssocConst + | DefKind::AnonConst + | DefKind::InlineConst + | DefKind::Static { .. } + | DefKind::Ctor(_, CtorKind::Fn) + | DefKind::Closure + | DefKind::SyntheticCoroutineBody, + "`Instance::try_resolve` should only be used to resolve instances of \ + functions, statics, and consts; to resolve associated types, use \ + `try_normalize_erasing_regions`." + ); + // Rust code can easily create exponentially-long types using only a // polynomial recursion depth. Even with the default recursion // depth, you can easily get cases that take >2^60 steps to run, diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 6e6da6de749a1..e5a4e38c87500 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -24,7 +24,6 @@ use rustc_target::spec::{ use tracing::debug; use {rustc_abi as abi, rustc_hir as hir}; -use crate::error::UnsupportedFnAbi; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::query::TyCtxtAt; use crate::ty::normalize_erasing_regions::NormalizationError; @@ -231,6 +230,7 @@ impl fmt::Display for ValidityRequirement { pub enum LayoutError<'tcx> { Unknown(Ty<'tcx>), SizeOverflow(Ty<'tcx>), + TooGeneric(Ty<'tcx>), NormalizationFailure(Ty<'tcx>, NormalizationError<'tcx>), ReferencesError(ErrorGuaranteed), Cycle(ErrorGuaranteed), @@ -244,6 +244,7 @@ impl<'tcx> LayoutError<'tcx> { match self { Unknown(_) => middle_unknown_layout, SizeOverflow(_) => middle_values_too_big, + TooGeneric(_) => middle_too_generic, NormalizationFailure(_, _) => middle_cannot_be_normalized, Cycle(_) => middle_cycle, ReferencesError(_) => middle_layout_references_error, @@ -257,6 +258,7 @@ impl<'tcx> LayoutError<'tcx> { match self { Unknown(ty) => E::Unknown { ty }, SizeOverflow(ty) => E::Overflow { ty }, + TooGeneric(ty) => E::TooGeneric { ty }, NormalizationFailure(ty, e) => { E::NormalizationFailure { ty, failure_ty: e.get_type_for_failure() } } @@ -272,6 +274,9 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { LayoutError::Unknown(ty) => write!(f, "the type `{ty}` has an unknown layout"), + LayoutError::TooGeneric(ty) => { + write!(f, "`{ty}` does not have a fixed size") + } LayoutError::SizeOverflow(ty) => { write!(f, "values of the type `{ty}` are too big for the target architecture") } @@ -350,10 +355,11 @@ impl<'tcx> SizeSkeleton<'tcx> { return Err(tcx.arena.alloc(LayoutError::Unknown(ty))); } } - Err(err @ LayoutError::Unknown(_)) => err, + Err(err @ LayoutError::TooGeneric(_)) => err, // We can't extract SizeSkeleton info from other layout errors Err( e @ LayoutError::Cycle(_) + | e @ LayoutError::Unknown(_) | e @ LayoutError::SizeOverflow(_) | e @ LayoutError::NormalizationFailure(..) | e @ LayoutError::ReferencesError(_), @@ -362,7 +368,7 @@ impl<'tcx> SizeSkeleton<'tcx> { match *ty.kind() { ty::Ref(_, pointee, _) | ty::RawPtr(pointee, _) => { - let non_zero = !ty.is_unsafe_ptr(); + let non_zero = !ty.is_raw_ptr(); let tail = tcx.struct_tail_raw( pointee, @@ -413,10 +419,9 @@ impl<'tcx> SizeSkeleton<'tcx> { // Alignment is unchanged by arrays. return Ok(SizeSkeleton::Known(Size::from_bytes(size), a)); } - Err(tcx.arena.alloc(LayoutError::Unknown(ty))) + Err(err) } - SizeSkeleton::Pointer { .. } => Err(err), - SizeSkeleton::Generic(_) => Err(tcx.arena.alloc(LayoutError::Unknown(ty))), + SizeSkeleton::Pointer { .. } | SizeSkeleton::Generic(_) => Err(err), } } @@ -498,6 +503,9 @@ impl<'tcx> SizeSkeleton<'tcx> { } } + // Pattern types are always the same size as their base. + ty::Pat(base, _) => SizeSkeleton::compute(base, tcx, typing_env), + _ => Err(err), } } @@ -770,6 +778,7 @@ where size: Size::ZERO, max_repr_align: None, unadjusted_abi_align: tcx.data_layout.i8_align.abi, + randomization_seed: 0, }) } @@ -831,7 +840,7 @@ where // as the `Abi` or `FieldsShape` is checked by users. if i == 0 { let nil = tcx.types.unit; - let unit_ptr_ty = if this.ty.is_unsafe_ptr() { + let unit_ptr_ty = if this.ty.is_raw_ptr() { Ty::new_mut_ptr(tcx, nil) } else { Ty::new_mut_ref(tcx, tcx.lifetimes.re_static, nil) @@ -848,7 +857,12 @@ where } let mk_dyn_vtable = |principal: Option>| { - let min_count = ty::vtable_min_entries(tcx, principal); + let min_count = ty::vtable_min_entries( + tcx, + principal.map(|principal| { + tcx.instantiate_bound_regions_with_erased(principal) + }), + ); Ty::new_imm_ref( tcx, tcx.lifetimes.re_static, @@ -1240,6 +1254,7 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option, abi: ExternAbi) PtxKernel | Msp430Interrupt | X86Interrupt + | GpuKernel | EfiApi | AvrInterrupt | AvrNonBlockingInterrupt @@ -1259,18 +1274,12 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option, abi: ExternAbi) pub enum FnAbiError<'tcx> { /// Error produced by a `layout_of` call, while computing `FnAbi` initially. Layout(LayoutError<'tcx>), - - /// Error produced by attempting to adjust a `FnAbi`, for a "foreign" ABI. - AdjustForForeignAbi(rustc_target::callconv::AdjustForForeignAbiError), } impl<'a, 'b, G: EmissionGuarantee> Diagnostic<'a, G> for FnAbiError<'b> { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { match self { Self::Layout(e) => e.into_diagnostic().into_diag(dcx, level), - Self::AdjustForForeignAbi( - rustc_target::callconv::AdjustForForeignAbiError::Unsupported { arch, abi }, - ) => UnsupportedFnAbi { arch, abi: abi.name() }.into_diag(dcx, level), } } } @@ -1353,10 +1362,11 @@ pub trait FnAbiOf<'tcx>: FnAbiOfHelpers<'tcx> { // `def_span` unconditionally (which may have a perf penalty). let span = if !span.is_dummy() { span } else { tcx.def_span(instance.def_id()) }; - self.handle_fn_abi_err(*err, span, FnAbiRequest::OfInstance { - instance, - extra_args, - }) + self.handle_fn_abi_err( + *err, + span, + FnAbiRequest::OfInstance { instance, extra_args }, + ) }), ) } diff --git a/compiler/rustc_middle/src/ty/list.rs b/compiler/rustc_middle/src/ty/list.rs index 30a5586f59ca4..6718493f6b32a 100644 --- a/compiler/rustc_middle/src/ty/list.rs +++ b/compiler/rustc_middle/src/ty/list.rs @@ -21,7 +21,7 @@ use crate::arena::Arena; /// pointer. /// - Because of this, you cannot get a `List` that is a sub-list of another /// `List`. You can get a sub-slice `&[T]`, however. -/// - `List` can be used with `CopyTaggedPtr`, which is useful within +/// - `List` can be used with `TaggedRef`, which is useful within /// structs whose size must be minimized. /// - Because of the uniqueness assumption, we can use the address of a /// `List` for faster equality comparisons and hashing. diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 5e929fbec0bf8..52cb8f57a88a9 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -32,7 +32,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::intern::Interned; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; -use rustc_errors::{Diag, ErrorGuaranteed, StashKey}; +use rustc_errors::{Diag, ErrorGuaranteed}; use rustc_hir::LangItem; use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; @@ -60,7 +60,8 @@ pub use self::closure::{ place_to_string_for_capture, }; pub use self::consts::{ - Const, ConstInt, ConstKind, Expr, ExprKind, ScalarInt, UnevaluatedConst, ValTree, + Const, ConstInt, ConstKind, Expr, ExprKind, ScalarInt, UnevaluatedConst, ValTree, ValTreeKind, + Value, }; pub use self::context::{ CtxtInterners, CurrentGcx, DeducedParamAttrs, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, @@ -79,8 +80,7 @@ pub use self::predicate::{ PolyExistentialPredicate, PolyExistentialProjection, PolyExistentialTraitRef, PolyProjectionPredicate, PolyRegionOutlivesPredicate, PolySubtypePredicate, PolyTraitPredicate, PolyTraitRef, PolyTypeOutlivesPredicate, Predicate, PredicateKind, ProjectionPredicate, - RegionOutlivesPredicate, SubtypePredicate, ToPolyTraitRef, TraitPredicate, TraitRef, - TypeOutlivesPredicate, + RegionOutlivesPredicate, SubtypePredicate, TraitPredicate, TraitRef, TypeOutlivesPredicate, }; pub use self::region::{ BoundRegion, BoundRegionKind, EarlyParamRegion, LateParamRegion, LateParamRegionKind, Region, @@ -95,7 +95,7 @@ pub use self::sty::{ pub use self::trait_def::TraitDef; pub use self::typeck_results::{ CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, IsIdentity, - TypeckResults, UserType, UserTypeAnnotationIndex, UserTypeKind, + Rust2024IncompatiblePatInfo, TypeckResults, UserType, UserTypeAnnotationIndex, UserTypeKind, }; pub use self::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; use crate::error::{OpaqueHiddenTypeMismatch, TypeMismatchReason}; @@ -222,6 +222,7 @@ pub struct DelegationFnSig { pub param_count: usize, pub has_self: bool, pub c_variadic: bool, + pub target_feature: bool, } #[derive(Clone, Copy, Debug)] @@ -782,18 +783,8 @@ impl<'tcx> OpaqueHiddenType<'tcx> { pub fn build_mismatch_error( &self, other: &Self, - opaque_def_id: LocalDefId, tcx: TyCtxt<'tcx>, ) -> Result, ErrorGuaranteed> { - // We used to cancel here for slightly better error messages, but - // cancelling stashed diagnostics is no longer allowed because it - // causes problems when tracking whether errors have actually - // occurred. - tcx.sess.dcx().try_steal_modify_and_emit_err( - tcx.def_span(opaque_def_id), - StashKey::OpaqueHiddenTypeMismatch, - |_err| {}, - ); (self.ty, other.ty).error_reported()?; // Found different concrete types for the opaque type. let sub_diag = if self.span == other.span { @@ -1417,8 +1408,8 @@ impl Hash for FieldDef { impl<'tcx> FieldDef { /// Returns the type of this field. The resulting type is not normalized. The `arg` is /// typically obtained via the second field of [`TyKind::Adt`]. - pub fn ty(&self, tcx: TyCtxt<'tcx>, arg: GenericArgsRef<'tcx>) -> Ty<'tcx> { - tcx.type_of(self.did).instantiate(tcx, arg) + pub fn ty(&self, tcx: TyCtxt<'tcx>, args: GenericArgsRef<'tcx>) -> Ty<'tcx> { + tcx.type_of(self.did).instantiate(tcx, args) } /// Computes the `Ident` of this variant by looking up the `Span` @@ -1606,6 +1597,15 @@ impl<'tcx> TyCtxt<'tcx> { Some(Ident::new(def, span)) } + /// Look up the name and span of a definition. + /// + /// See [`item_name`][Self::item_name] for more information. + pub fn item_ident(self, def_id: DefId) -> Ident { + self.opt_item_ident(def_id).unwrap_or_else(|| { + bug!("item_ident: no name for {:?}", self.def_path(def_id)); + }) + } + pub fn opt_associated_item(self, def_id: DefId) -> Option { if let DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy = self.def_kind(def_id) { Some(self.associated_item(def_id)) diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs index f611b69905c9c..e86e01451fefb 100644 --- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs +++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs @@ -165,10 +165,14 @@ impl<'tcx> NormalizeAfterErasingRegionsFolder<'tcx> { arg: ty::GenericArg<'tcx>, ) -> ty::GenericArg<'tcx> { let arg = self.typing_env.as_query_input(arg); - self.tcx.try_normalize_generic_arg_after_erasing_regions(arg).unwrap_or_else(|_| bug!( - "Failed to normalize {:?}, maybe try to call `try_normalize_erasing_regions` instead", - arg.value - )) + self.tcx.try_normalize_generic_arg_after_erasing_regions(arg).unwrap_or_else(|_| { + bug!( + "Failed to normalize {:?} in typing_env={:?}, \ + maybe try to call `try_normalize_erasing_regions` instead", + arg.value, + self.typing_env, + ) + }) } } diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index 86a95827e843f..8eaf0a58f7086 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -96,11 +96,12 @@ trivially_parameterized_over_tcx! { rustc_hir::def_id::DefIndex, rustc_hir::definitions::DefKey, rustc_hir::OpaqueTyOrigin, - rustc_index::bit_set::BitSet, + rustc_index::bit_set::DenseBitSet, rustc_index::bit_set::FiniteBitSet, rustc_session::cstore::ForeignModule, rustc_session::cstore::LinkagePreference, rustc_session::cstore::NativeLib, + rustc_session::config::TargetModifier, rustc_span::ExpnData, rustc_span::ExpnHash, rustc_span::ExpnId, diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index 3ecaa3e22d39c..584cac22ae8dd 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -476,16 +476,6 @@ impl<'tcx> Clause<'tcx> { } } -pub trait ToPolyTraitRef<'tcx> { - fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx>; -} - -impl<'tcx> ToPolyTraitRef<'tcx> for PolyTraitPredicate<'tcx> { - fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx> { - self.map_bound_ref(|trait_pred| trait_pred.trait_ref) - } -} - impl<'tcx> UpcastFrom, PredicateKind<'tcx>> for Predicate<'tcx> { fn upcast_from(from: PredicateKind<'tcx>, tcx: TyCtxt<'tcx>) -> Self { ty::Binder::dummy(from).upcast(tcx) @@ -634,6 +624,28 @@ impl<'tcx> UpcastFrom, PolyProjectionPredicate<'tcx>> for Clause<'t } } +impl<'tcx> UpcastFrom, ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>> + for Predicate<'tcx> +{ + fn upcast_from( + from: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>, + tcx: TyCtxt<'tcx>, + ) -> Self { + from.map_bound(ty::ClauseKind::HostEffect).upcast(tcx) + } +} + +impl<'tcx> UpcastFrom, ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>> + for Clause<'tcx> +{ + fn upcast_from( + from: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>, + tcx: TyCtxt<'tcx>, + ) -> Self { + from.map_bound(ty::ClauseKind::HostEffect).upcast(tcx) + } +} + impl<'tcx> UpcastFrom, NormalizesTo<'tcx>> for Predicate<'tcx> { fn upcast_from(from: NormalizesTo<'tcx>, tcx: TyCtxt<'tcx>) -> Self { PredicateKind::NormalizesTo(from).upcast(tcx) diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index b0150bc11920b..dc2040aa5cf85 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -45,10 +45,25 @@ pub trait Printer<'tcx>: Sized { &mut self, impl_def_id: DefId, args: &'tcx [GenericArg<'tcx>], - self_ty: Ty<'tcx>, - trait_ref: Option>, ) -> Result<(), PrintError> { - self.default_print_impl_path(impl_def_id, args, self_ty, trait_ref) + let tcx = self.tcx(); + let self_ty = tcx.type_of(impl_def_id); + let impl_trait_ref = tcx.impl_trait_ref(impl_def_id); + let (self_ty, impl_trait_ref) = if tcx.generics_of(impl_def_id).count() <= args.len() { + ( + self_ty.instantiate(tcx, args), + impl_trait_ref.map(|impl_trait_ref| impl_trait_ref.instantiate(tcx, args)), + ) + } else { + // We are probably printing a nested item inside of an impl. + // Use the identity substitutions for the impl. + ( + self_ty.instantiate_identity(), + impl_trait_ref.map(|impl_trait_ref| impl_trait_ref.instantiate_identity()), + ) + }; + + self.default_print_impl_path(impl_def_id, self_ty, impl_trait_ref) } fn print_region(&mut self, region: ty::Region<'tcx>) -> Result<(), PrintError>; @@ -90,6 +105,10 @@ pub trait Printer<'tcx>: Sized { args: &[GenericArg<'tcx>], ) -> Result<(), PrintError>; + fn should_truncate(&mut self) -> bool { + false + } + // Defaults (should not be overridden): #[instrument(skip(self), level = "debug")] @@ -107,23 +126,7 @@ pub trait Printer<'tcx>: Sized { self.path_crate(def_id.krate) } - DefPathData::Impl => { - let generics = self.tcx().generics_of(def_id); - let self_ty = self.tcx().type_of(def_id); - let impl_trait_ref = self.tcx().impl_trait_ref(def_id); - let (self_ty, impl_trait_ref) = if args.len() >= generics.count() { - ( - self_ty.instantiate(self.tcx(), args), - impl_trait_ref.map(|i| i.instantiate(self.tcx(), args)), - ) - } else { - ( - self_ty.instantiate_identity(), - impl_trait_ref.map(|i| i.instantiate_identity()), - ) - }; - self.print_impl_path(def_id, args, self_ty, impl_trait_ref) - } + DefPathData::Impl => self.print_impl_path(def_id, args), _ => { let parent_def_id = DefId { index: key.parent.unwrap(), ..def_id }; @@ -201,7 +204,6 @@ pub trait Printer<'tcx>: Sized { fn default_print_impl_path( &mut self, impl_def_id: DefId, - _args: &'tcx [GenericArg<'tcx>], self_ty: Ty<'tcx>, impl_trait_ref: Option>, ) -> Result<(), PrintError> { diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 9fe1caa4b58f5..7166c284c3d70 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -690,7 +690,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { if with_reduced_queries() { p!(print_def_path(def_id, args)); } else { - let sig = self.tcx().fn_sig(def_id).instantiate(self.tcx(), args); + let mut sig = self.tcx().fn_sig(def_id).instantiate(self.tcx(), args); + if self.tcx().codegen_fn_attrs(def_id).safe_target_features { + p!("#[target_features] "); + sig = sig.map_bound(|mut sig| { + sig.safety = hir::Safety::Safe; + sig + }); + } p!(print(sig), " {{", print_value_path(def_id, args), "}}"); } } @@ -858,7 +865,6 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { p!(write("{{")); if !self.tcx().sess.verbose_internals() { p!("coroutine witness"); - // FIXME(eddyb) should use `def_span`. if let Some(did) = did.as_local() { let span = self.tcx().def_span(did); p!(write( @@ -880,26 +886,30 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { p!(write("{{")); if !self.should_print_verbose() { p!(write("closure")); - // FIXME(eddyb) should use `def_span`. - if let Some(did) = did.as_local() { - if self.tcx().sess.opts.unstable_opts.span_free_formats { - p!("@", print_def_path(did.to_def_id(), args)); - } else { - let span = self.tcx().def_span(did); - let preference = if with_forced_trimmed_paths() { - FileNameDisplayPreference::Short + if self.should_truncate() { + write!(self, "@...}}")?; + return Ok(()); + } else { + if let Some(did) = did.as_local() { + if self.tcx().sess.opts.unstable_opts.span_free_formats { + p!("@", print_def_path(did.to_def_id(), args)); } else { - FileNameDisplayPreference::Remapped - }; - p!(write( - "@{}", - // This may end up in stderr diagnostics but it may also be emitted - // into MIR. Hence we use the remapped path if available - self.tcx().sess.source_map().span_to_string(span, preference) - )); + let span = self.tcx().def_span(did); + let preference = if with_forced_trimmed_paths() { + FileNameDisplayPreference::Short + } else { + FileNameDisplayPreference::Remapped + }; + p!(write( + "@{}", + // This may end up in stderr diagnostics but it may also be emitted + // into MIR. Hence we use the remapped path if available + self.tcx().sess.source_map().span_to_string(span, preference) + )); + } + } else { + p!(write("@"), print_def_path(did, args)); } - } else { - p!(write("@"), print_def_path(did, args)); } } else { p!(print_def_path(did, args)); @@ -935,7 +945,6 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { "coroutine from coroutine-closure should have CoroutineSource::Closure" ), } - // FIXME(eddyb) should use `def_span`. if let Some(did) = did.as_local() { if self.tcx().sess.opts.unstable_opts.span_free_formats { p!("@", print_def_path(did.to_def_id(), args)); @@ -1477,8 +1486,8 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { _ => write!(self, "_")?, }, ty::ConstKind::Param(ParamConst { name, .. }) => p!(write("{}", name)), - ty::ConstKind::Value(ty, value) => { - return self.pretty_print_const_valtree(value, ty, print_ty); + ty::ConstKind::Value(cv) => { + return self.pretty_print_const_valtree(cv, print_ty); } ty::ConstKind::Bound(debruijn, bound_var) => { @@ -1503,7 +1512,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::ExprKind::Binop(op) => { let (_, _, c1, c2) = expr.binop_args(); - let precedence = |binop: rustc_middle::mir::BinOp| { + let precedence = |binop: crate::mir::BinOp| { use rustc_ast::util::parser::AssocOp; AssocOp::from_ast_binop(binop.to_hir_binop()).precedence() }; @@ -1549,7 +1558,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::ExprKind::UnOp(op) => { let (_, ct) = expr.unop_args(); - use rustc_middle::mir::UnOp; + use crate::mir::UnOp; let formatted_op = match op { UnOp::Not => "!", UnOp::Neg => "-", @@ -1630,33 +1639,30 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { match ty.kind() { // Byte strings (&[u8; N]) ty::Ref(_, inner, _) => { - if let ty::Array(elem, len) = inner.kind() { - if let ty::Uint(ty::UintTy::U8) = elem.kind() { - if let ty::ConstKind::Value(_, ty::ValTree::Leaf(int)) = len.kind() { - match self.tcx().try_get_global_alloc(prov.alloc_id()) { - Some(GlobalAlloc::Memory(alloc)) => { - let len = int.to_bits(self.tcx().data_layout.pointer_size); - let range = - AllocRange { start: offset, size: Size::from_bytes(len) }; - if let Ok(byte_str) = - alloc.inner().get_bytes_strip_provenance(&self.tcx(), range) - { - p!(pretty_print_byte_str(byte_str)) - } else { - p!("") - } - } - // FIXME: for statics, vtables, and functions, we could in principle print more detail. - Some(GlobalAlloc::Static(def_id)) => { - p!(write("", def_id)) - } - Some(GlobalAlloc::Function { .. }) => p!(""), - Some(GlobalAlloc::VTable(..)) => p!(""), - None => p!(""), + if let ty::Array(elem, ct_len) = inner.kind() + && let ty::Uint(ty::UintTy::U8) = elem.kind() + && let Some(len) = ct_len.try_to_target_usize(self.tcx()) + { + match self.tcx().try_get_global_alloc(prov.alloc_id()) { + Some(GlobalAlloc::Memory(alloc)) => { + let range = AllocRange { start: offset, size: Size::from_bytes(len) }; + if let Ok(byte_str) = + alloc.inner().get_bytes_strip_provenance(&self.tcx(), range) + { + p!(pretty_print_byte_str(byte_str)) + } else { + p!("") } - return Ok(()); } + // FIXME: for statics, vtables, and functions, we could in principle print more detail. + Some(GlobalAlloc::Static(def_id)) => { + p!(write("", def_id)) + } + Some(GlobalAlloc::Function { .. }) => p!(""), + Some(GlobalAlloc::VTable(..)) => p!(""), + None => p!(""), } + return Ok(()); } } ty::FnPtr(..) => { @@ -1733,6 +1739,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { " as ", )?; } + ty::Pat(base_ty, pat) if self.tcx().validate_scalar_in_layout(int, ty) => { + self.pretty_print_const_scalar_int(int, *base_ty, print_ty)?; + p!(write(" is {pat:?}")); + } // Nontrivial types with scalar bit representation _ => { let print = |this: &mut Self| { @@ -1777,45 +1787,45 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { fn pretty_print_const_valtree( &mut self, - valtree: ty::ValTree<'tcx>, - ty: Ty<'tcx>, + cv: ty::Value<'tcx>, print_ty: bool, ) -> Result<(), PrintError> { define_scoped_cx!(self); if self.should_print_verbose() { - p!(write("ValTree({:?}: ", valtree), print(ty), ")"); + p!(write("ValTree({:?}: ", cv.valtree), print(cv.ty), ")"); return Ok(()); } let u8_type = self.tcx().types.u8; - match (valtree, ty.kind()) { - (ty::ValTree::Branch(_), ty::Ref(_, inner_ty, _)) => match inner_ty.kind() { + match (*cv.valtree, *cv.ty.kind()) { + (ty::ValTreeKind::Branch(_), ty::Ref(_, inner_ty, _)) => match inner_ty.kind() { ty::Slice(t) if *t == u8_type => { - let bytes = valtree.try_to_raw_bytes(self.tcx(), ty).unwrap_or_else(|| { + let bytes = cv.try_to_raw_bytes(self.tcx()).unwrap_or_else(|| { bug!( "expected to convert valtree {:?} to raw bytes for type {:?}", - valtree, + cv.valtree, t ) }); return self.pretty_print_byte_str(bytes); } ty::Str => { - let bytes = valtree.try_to_raw_bytes(self.tcx(), ty).unwrap_or_else(|| { - bug!("expected to convert valtree to raw bytes for type {:?}", ty) + let bytes = cv.try_to_raw_bytes(self.tcx()).unwrap_or_else(|| { + bug!("expected to convert valtree to raw bytes for type {:?}", cv.ty) }); p!(write("{:?}", String::from_utf8_lossy(bytes))); return Ok(()); } _ => { + let cv = ty::Value { valtree: cv.valtree, ty: inner_ty }; p!("&"); - p!(pretty_print_const_valtree(valtree, *inner_ty, print_ty)); + p!(pretty_print_const_valtree(cv, print_ty)); return Ok(()); } }, - (ty::ValTree::Branch(_), ty::Array(t, _)) if *t == u8_type => { - let bytes = valtree.try_to_raw_bytes(self.tcx(), ty).unwrap_or_else(|| { + (ty::ValTreeKind::Branch(_), ty::Array(t, _)) if t == u8_type => { + let bytes = cv.try_to_raw_bytes(self.tcx()).unwrap_or_else(|| { bug!("expected to convert valtree to raw bytes for type {:?}", t) }); p!("*"); @@ -1823,11 +1833,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { return Ok(()); } // Aggregates, printed as array/tuple/struct/variant construction syntax. - (ty::ValTree::Branch(_), ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) => { - let contents = - self.tcx().destructure_const(ty::Const::new_value(self.tcx(), valtree, ty)); + (ty::ValTreeKind::Branch(_), ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) => { + let contents = self.tcx().destructure_const(ty::Const::new_value( + self.tcx(), + cv.valtree, + cv.ty, + )); let fields = contents.fields.iter().copied(); - match *ty.kind() { + match *cv.ty.kind() { ty::Array(..) => { p!("[", comma_sep(fields), "]"); } @@ -1844,7 +1857,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { write!(this, "unreachable()")?; Ok(()) }, - |this| this.print_type(ty), + |this| this.print_type(cv.ty), ": ", )?; } @@ -1876,12 +1889,17 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } return Ok(()); } - (ty::ValTree::Leaf(leaf), ty::Ref(_, inner_ty, _)) => { + (ty::ValTreeKind::Leaf(leaf), ty::Ref(_, inner_ty, _)) => { p!(write("&")); - return self.pretty_print_const_scalar_int(leaf, *inner_ty, print_ty); + return self.pretty_print_const_scalar_int(*leaf, inner_ty, print_ty); + } + (ty::ValTreeKind::Leaf(leaf), _) => { + return self.pretty_print_const_scalar_int(*leaf, cv.ty, print_ty); } - (ty::ValTree::Leaf(leaf), _) => { - return self.pretty_print_const_scalar_int(leaf, ty, print_ty); + (_, ty::FnDef(def_id, args)) => { + // Never allowed today, but we still encounter them in invalid const args. + p!(print_value_path(def_id, args)); + return Ok(()); } // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading // their fields instead of just dumping the memory. @@ -1889,13 +1907,13 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } // fallback - if valtree == ty::ValTree::zst() { + if cv.valtree.is_zst() { p!(write("")); } else { - p!(write("{:?}", valtree)); + p!(write("{:?}", cv.valtree)); } if print_ty { - p!(": ", print(ty)); + p!(": ", print(cv.ty)); } Ok(()) } @@ -1983,7 +2001,6 @@ pub struct FmtPrinterData<'a, 'tcx> { binder_depth: usize, printed_type_count: usize, type_length_limit: Limit, - truncated: bool, pub region_highlight_mode: RegionHighlightMode<'tcx>, @@ -2035,7 +2052,6 @@ impl<'a, 'tcx> FmtPrinter<'a, 'tcx> { binder_depth: 0, printed_type_count: 0, type_length_limit, - truncated: false, region_highlight_mode: RegionHighlightMode::default(), ty_infer_name_resolver: None, const_infer_name_resolver: None, @@ -2172,16 +2188,49 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { } fn print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError> { - if self.type_length_limit.value_within_limit(self.printed_type_count) { - self.printed_type_count += 1; - self.pretty_print_type(ty) - } else { - self.truncated = true; - write!(self, "...")?; - Ok(()) + match ty.kind() { + ty::Tuple(tys) if tys.len() == 0 && self.should_truncate() => { + // Don't truncate `()`. + self.printed_type_count += 1; + self.pretty_print_type(ty) + } + ty::Adt(..) + | ty::Foreign(_) + | ty::Pat(..) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::UnsafeBinder(..) + | ty::Dynamic(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Tuple(_) + | ty::Alias(..) + | ty::Param(_) + | ty::Bound(..) + | ty::Placeholder(_) + | ty::Error(_) + if self.should_truncate() => + { + // We only truncate types that we know are likely to be much longer than 3 chars. + // There's no point in replacing `i32` or `!`. + write!(self, "...")?; + Ok(()) + } + _ => { + self.printed_type_count += 1; + self.pretty_print_type(ty) + } } } + fn should_truncate(&mut self) -> bool { + !self.type_length_limit.value_within_limit(self.printed_type_count) + } + fn print_dyn_existential( &mut self, predicates: &'tcx ty::List>, @@ -2931,7 +2980,7 @@ impl<'tcx> ty::TraitPredicate<'tcx> { } } -#[derive(Copy, Clone, TypeFoldable, TypeVisitable, Lift)] +#[derive(Copy, Clone, TypeFoldable, TypeVisitable, Lift, Hash)] pub struct TraitPredPrintWithBoundConstness<'tcx>( ty::TraitPredicate<'tcx>, Option, diff --git a/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs b/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs index 21c605f8296d1..568e504b940a6 100644 --- a/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs +++ b/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs @@ -4,7 +4,7 @@ use crate::ty::{self, ExistentialPredicateStableCmpExt, TyCtxt}; impl<'tcx> TyCtxt<'tcx> { /// Given a `def_id` of a trait or impl method, compute whether that method needs to - /// have an RPITIT shim applied to it for it to be object safe. If so, return the + /// have an RPITIT shim applied to it for it to be dyn compatible. If so, return the /// `def_id` of the RPITIT, and also the args of trait method that returns the RPITIT. /// /// NOTE that these args are not, in general, the same as than the RPITIT's args. They @@ -64,7 +64,7 @@ impl<'tcx> TyCtxt<'tcx> { args: ty::GenericArgsRef<'tcx>, ) -> &'tcx ty::List> { let mut bounds: Vec<_> = self - .item_super_predicates(def_id) + .item_self_bounds(def_id) .iter_instantiated(self, args) .filter_map(|clause| { clause diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 68cb56f35837d..6c15e033bb04b 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -6,15 +6,18 @@ use std::fmt::{self, Debug}; use rustc_abi::TyAndLayout; +use rustc_ast::InlineAsmTemplatePiece; use rustc_ast_ir::try_visit; use rustc_ast_ir::visit::VisitorResult; use rustc_hir::def::Namespace; +use rustc_hir::def_id::LocalDefId; +use rustc_span::Span; use rustc_span::source_map::Spanned; use rustc_type_ir::ConstKind; use super::print::PrettyPrinter; use super::{GenericArg, GenericArgKind, Pattern, Region}; -use crate::mir::interpret; +use crate::mir::PlaceElem; use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}; use crate::ty::print::{FmtPrinter, Printer, with_no_trimmed_paths}; use crate::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; @@ -162,16 +165,11 @@ impl<'tcx> fmt::Debug for ty::consts::Expr<'tcx> { impl<'tcx> fmt::Debug for ty::Const<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // If this is a value, we spend some effort to make it look nice. - if let ConstKind::Value(_, _) = self.kind() { + if let ConstKind::Value(cv) = self.kind() { return ty::tls::with(move |tcx| { - // Somehow trying to lift the valtree results in lifetime errors, so we lift the - // entire constant. - let lifted = tcx.lift(*self).unwrap(); - let ConstKind::Value(ty, valtree) = lifted.kind() else { - bug!("we checked that this is a valtree") - }; + let cv = tcx.lift(cv).unwrap(); let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS); - cx.pretty_print_const_valtree(valtree, ty, /*print_ty*/ true)?; + cx.pretty_print_const_valtree(cv, /*print_ty*/ true)?; f.write_str(&cx.into_buffer()) }); } @@ -222,76 +220,89 @@ impl<'tcx> fmt::Debug for Region<'tcx> { // copy...), just add them to one of these lists as appropriate. // For things for which the type library provides traversal implementations -// for all Interners, we only need to provide a Lift implementation: +// for all Interners, we only need to provide a Lift implementation. TrivialLiftImpls! { - (), - bool, - usize, - u64, + (), + bool, + usize, + u64, + // tidy-alphabetical-start + crate::mir::interpret::AllocId, + crate::mir::interpret::Scalar, + crate::mir::Promoted, + rustc_abi::ExternAbi, + rustc_abi::Size, + rustc_hir::Safety, + rustc_type_ir::BoundConstness, + rustc_type_ir::PredicatePolarity, + // tidy-alphabetical-end } // For some things about which the type library does not know, or does not // provide any traversal implementations, we need to provide a traversal // implementation (only for TyCtxt<'_> interners). TrivialTypeTraversalImpls! { - ::rustc_abi::FieldIdx, - ::rustc_abi::VariantIdx, - crate::middle::region::Scope, - ::rustc_ast::InlineAsmOptions, - ::rustc_ast::InlineAsmTemplatePiece, - ::rustc_ast::NodeId, - ::rustc_hir::def::Res, - ::rustc_hir::def_id::LocalDefId, - ::rustc_hir::ByRef, - ::rustc_hir::HirId, - ::rustc_hir::MatchSource, - ::rustc_target::asm::InlineAsmRegOrRegClass, - crate::mir::coverage::BlockMarkerId, - crate::mir::coverage::CounterId, - crate::mir::coverage::ExpressionId, - crate::mir::coverage::ConditionId, + // tidy-alphabetical-start + crate::infer::canonical::Certainty, + crate::mir::BasicBlock, + crate::mir::BindingForm<'tcx>, + crate::mir::BlockTailInfo, + crate::mir::BorrowKind, + crate::mir::CastKind, + crate::mir::ConstValue<'tcx>, + crate::mir::CoroutineSavedLocal, + crate::mir::FakeReadCause, crate::mir::Local, + crate::mir::MirPhase, + crate::mir::NullOp<'tcx>, crate::mir::Promoted, + crate::mir::RawPtrKind, + crate::mir::RetagKind, + crate::mir::SourceInfo, + crate::mir::SourceScope, + crate::mir::SourceScopeLocalData, + crate::mir::SwitchTargets, + crate::traits::IsConstable, + crate::traits::OverflowError, + crate::ty::abstract_const::NotConstEvaluatable, crate::ty::adjustment::AutoBorrowMutability, + crate::ty::adjustment::PointerCoercion, crate::ty::AdtKind, - crate::ty::BoundRegion, - // Including `BoundRegionKind` is a *bit* dubious, but direct - // references to bound region appear in `ty::Error`, and aren't - // really meant to be folded. In general, we can only fold a fully - // general `Region`. - crate::ty::BoundRegionKind, crate::ty::AssocItem, crate::ty::AssocKind, + crate::ty::BoundRegion, + crate::ty::BoundVar, crate::ty::Placeholder, crate::ty::Placeholder, crate::ty::Placeholder, - crate::ty::LateParamRegion, - crate::ty::adjustment::PointerCoercion, - ::rustc_span::Ident, - ::rustc_span::Span, - ::rustc_span::Symbol, - ty::BoundVar, - ty::ValTree<'tcx>, + crate::ty::UserTypeAnnotationIndex, + crate::ty::ValTree<'tcx>, + rustc_abi::FieldIdx, + rustc_abi::VariantIdx, + rustc_ast::InlineAsmOptions, + rustc_ast::InlineAsmTemplatePiece, + rustc_hir::CoroutineKind, + rustc_hir::def_id::LocalDefId, + rustc_hir::HirId, + rustc_hir::MatchSource, + rustc_span::Ident, + rustc_span::Span, + rustc_span::Symbol, + rustc_target::asm::InlineAsmRegOrRegClass, + // tidy-alphabetical-end } + // For some things about which the type library does not know, or does not // provide any traversal implementations, we need to provide a traversal // implementation and a lift implementation (the former only for TyCtxt<'_> // interners). TrivialTypeTraversalAndLiftImpls! { - ::rustc_hir::def_id::DefId, - crate::ty::ClosureKind, + // tidy-alphabetical-start + crate::ty::instance::ReifyReason, crate::ty::ParamConst, crate::ty::ParamTy, - crate::ty::instance::ReifyReason, - interpret::AllocId, - interpret::CtfeProvenance, - interpret::Scalar, - rustc_abi::Size, -} - -TrivialLiftImpls! { - ::rustc_hir::Safety, - ::rustc_abi::ExternAbi, + rustc_hir::def_id::DefId, + // tidy-alphabetical-end } /////////////////////////////////////////////////////////////////////////// @@ -589,9 +600,7 @@ impl<'tcx> TypeSuperFoldable> for ty::Const<'tcx> { } ConstKind::Placeholder(p) => ConstKind::Placeholder(p.try_fold_with(folder)?), ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.try_fold_with(folder)?), - ConstKind::Value(t, v) => { - ConstKind::Value(t.try_fold_with(folder)?, v.try_fold_with(folder)?) - } + ConstKind::Value(v) => ConstKind::Value(v.try_fold_with(folder)?), ConstKind::Error(e) => ConstKind::Error(e.try_fold_with(folder)?), ConstKind::Expr(e) => ConstKind::Expr(e.try_fold_with(folder)?), }; @@ -610,10 +619,7 @@ impl<'tcx> TypeSuperVisitable> for ty::Const<'tcx> { } ConstKind::Placeholder(p) => p.visit_with(visitor), ConstKind::Unevaluated(uv) => uv.visit_with(visitor), - ConstKind::Value(t, v) => { - try_visit!(t.visit_with(visitor)); - v.visit_with(visitor) - } + ConstKind::Value(v) => v.visit_with(visitor), ConstKind::Error(e) => e.visit_with(visitor), ConstKind::Expr(e) => e.visit_with(visitor), } @@ -678,3 +684,39 @@ impl<'tcx, T: TypeFoldable> + Debug + Clone> TypeFoldable TypeFoldable> for &'tcx [InlineAsmTemplatePiece] { + fn try_fold_with>>( + self, + _folder: &mut F, + ) -> Result { + Ok(self) + } +} + +impl<'tcx> TypeFoldable> for &'tcx [Span] { + fn try_fold_with>>( + self, + _folder: &mut F, + ) -> Result { + Ok(self) + } +} + +impl<'tcx> TypeFoldable> for &'tcx ty::List { + fn try_fold_with>>( + self, + _folder: &mut F, + ) -> Result { + Ok(self) + } +} + +impl<'tcx> TypeFoldable> for &'tcx ty::List> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + ty::util::fold_list(self, folder, |tcx, v| tcx.mk_place_elems(v)) + } +} diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 92b3632c8acc7..8a31b2960188e 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -27,7 +27,7 @@ use crate::infer::canonical::Canonical; use crate::ty::InferTy::*; use crate::ty::{ self, AdtDef, BoundRegionKind, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv, - Region, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, + Region, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, }; // Re-export and re-parameterize some `I = TyCtxt<'tcx>` types here @@ -401,7 +401,7 @@ impl<'tcx> Ty<'tcx> { /// The more specific methods will often optimize their creation. #[allow(rustc::usage_of_ty_tykind)] #[inline] - pub fn new(tcx: TyCtxt<'tcx>, st: TyKind<'tcx>) -> Ty<'tcx> { + fn new(tcx: TyCtxt<'tcx>, st: TyKind<'tcx>) -> Ty<'tcx> { tcx.mk_ty_from_kind(st) } @@ -613,6 +613,41 @@ impl<'tcx> Ty<'tcx> { #[inline] pub fn new_adt(tcx: TyCtxt<'tcx>, def: AdtDef<'tcx>, args: GenericArgsRef<'tcx>) -> Ty<'tcx> { tcx.debug_assert_args_compatible(def.did(), args); + if cfg!(debug_assertions) { + match tcx.def_kind(def.did()) { + DefKind::Struct | DefKind::Union | DefKind::Enum => {} + DefKind::Mod + | DefKind::Variant + | DefKind::Trait + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::TraitAlias + | DefKind::AssocTy + | DefKind::TyParam + | DefKind::Fn + | DefKind::Const + | DefKind::ConstParam + | DefKind::Static { .. } + | DefKind::Ctor(..) + | DefKind::AssocFn + | DefKind::AssocConst + | DefKind::Macro(..) + | DefKind::ExternCrate + | DefKind::Use + | DefKind::ForeignMod + | DefKind::AnonConst + | DefKind::InlineConst + | DefKind::OpaqueTy + | DefKind::Field + | DefKind::LifetimeParam + | DefKind::GlobalAsm + | DefKind::Impl { .. } + | DefKind::Closure + | DefKind::SyntheticCoroutineBody => { + bug!("not an adt: {def:?} ({:?})", tcx.def_kind(def.did())) + } + } + } Ty::new(tcx, Adt(def, args)) } @@ -772,7 +807,7 @@ impl<'tcx> Ty<'tcx> { } } }); - Ty::new(tcx, Adt(adt_def, args)) + Ty::new_adt(tcx, adt_def, args) } #[inline] @@ -1017,6 +1052,18 @@ impl<'tcx> Ty<'tcx> { } } + /// Check if type is an `usize`. + #[inline] + pub fn is_usize(self) -> bool { + matches!(self.kind(), Uint(UintTy::Usize)) + } + + /// Check if type is an `usize` or an integral type variable. + #[inline] + pub fn is_usize_like(self) -> bool { + matches!(self.kind(), Uint(UintTy::Usize) | Infer(IntVar(_))) + } + #[inline] pub fn is_never(self) -> bool { matches!(self.kind(), Never) @@ -1152,7 +1199,7 @@ impl<'tcx> Ty<'tcx> { } #[inline] - pub fn is_unsafe_ptr(self) -> bool { + pub fn is_raw_ptr(self) -> bool { matches!(self.kind(), RawPtr(_, _)) } @@ -1160,7 +1207,7 @@ impl<'tcx> Ty<'tcx> { /// `Box` is *not* considered a pointer here! #[inline] pub fn is_any_ptr(self) -> bool { - self.is_ref() || self.is_unsafe_ptr() || self.is_fn_ptr() + self.is_ref() || self.is_raw_ptr() || self.is_fn_ptr() } #[inline] @@ -1347,7 +1394,7 @@ impl<'tcx> Ty<'tcx> { /// Returns the type and mutability of `*ty`. /// /// The parameter `explicit` indicates if this is an *explicit* dereference. - /// Some types -- notably unsafe ptrs -- can only be dereferenced explicitly. + /// Some types -- notably raw ptrs -- can only be dereferenced explicitly. pub fn builtin_deref(self, explicit: bool) -> Option> { match *self.kind() { _ if let Some(boxed) = self.boxed_ty() => Some(boxed), @@ -1847,11 +1894,11 @@ impl<'tcx> Ty<'tcx> { ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false, - ty::Tuple(tys) => tys.last().map_or(true, |ty| ty.is_trivially_sized(tcx)), + ty::Tuple(tys) => tys.last().is_none_or(|ty| ty.is_trivially_sized(tcx)), ty::Adt(def, args) => def .sized_constraint(tcx) - .map_or(true, |ty| ty.instantiate(tcx, args).is_trivially_sized(tcx)), + .is_none_or(|ty| ty.instantiate(tcx, args).is_trivially_sized(tcx)), ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) | ty::Bound(..) => false, diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index f94f52e4f6180..1b5b791bb24ae 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -14,13 +14,13 @@ use rustc_hir::{ }; use rustc_index::IndexVec; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; -use rustc_middle::mir::FakeReadCause; use rustc_session::Session; use rustc_span::Span; use super::RvalueScopes; use crate::hir::place::Place as HirPlace; use crate::infer::canonical::Canonical; +use crate::mir::FakeReadCause; use crate::traits::ObligationCause; use crate::ty::{ self, BoundVar, CanonicalPolyFnSig, ClosureSizeProfileData, GenericArgKind, GenericArgs, @@ -73,9 +73,9 @@ pub struct TypeckResults<'tcx> { /// Stores the actual binding mode for all instances of [`BindingMode`]. pat_binding_modes: ItemLocalMap, - /// Top-level patterns whose match ergonomics need to be desugared by the Rust 2021 -> 2024 - /// migration lint. Problematic subpatterns are stored in the `Vec` for the lint to highlight. - rust_2024_migration_desugared_pats: ItemLocalMap>, + /// Top-level patterns incompatible with Rust 2024's match ergonomics. These will be translated + /// to a form valid in all Editions, either as a lint diagnostic or hard error. + rust_2024_migration_desugared_pats: ItemLocalMap, /// Stores the types which were implicitly dereferenced in pattern binding modes /// for later usage in THIR lowering. For example, @@ -148,7 +148,7 @@ pub struct TypeckResults<'tcx> { /// Set of trait imports actually used in the method resolution. /// This is used for warning unused imports. During type - /// checking, this `Lrc` should not be cloned: it must have a ref-count + /// checking, this `Arc` should not be cloned: it must have a ref-count /// of 1 so that we can insert things into the set mutably. pub used_trait_imports: UnordSet, @@ -420,7 +420,7 @@ impl<'tcx> TypeckResults<'tcx> { pub fn rust_2024_migration_desugared_pats( &self, - ) -> LocalTableInContext<'_, Vec<(Span, String)>> { + ) -> LocalTableInContext<'_, Rust2024IncompatiblePatInfo> { LocalTableInContext { hir_owner: self.hir_owner, data: &self.rust_2024_migration_desugared_pats, @@ -429,7 +429,7 @@ impl<'tcx> TypeckResults<'tcx> { pub fn rust_2024_migration_desugared_pats_mut( &mut self, - ) -> LocalTableInContextMut<'_, Vec<(Span, String)>> { + ) -> LocalTableInContextMut<'_, Rust2024IncompatiblePatInfo> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.rust_2024_migration_desugared_pats, @@ -811,3 +811,17 @@ impl<'tcx> std::fmt::Display for UserTypeKind<'tcx> { } } } + +/// Information on a pattern incompatible with Rust 2024, for use by the error/migration diagnostic +/// emitted during THIR construction. +#[derive(TyEncodable, TyDecodable, Debug, HashStable)] +pub struct Rust2024IncompatiblePatInfo { + /// Labeled spans for `&`s, `&mut`s, and binding modifiers incompatible with Rust 2024. + pub primary_labels: Vec<(Span, String)>, + /// Whether any binding modifiers occur under a non-`move` default binding mode. + pub bad_modifiers: bool, + /// Whether any `&` or `&mut` patterns occur under a non-`move` default binding mode. + pub bad_ref_pats: bool, + /// If `true`, we can give a simpler suggestion solely by eliding explicit binding modifiers. + pub suggest_eliding_modes: bool, +} diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index ab8285f87d6ce..94bd359f6eb0f 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -20,6 +20,7 @@ use tracing::{debug, instrument}; use super::TypingEnv; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use crate::mir; use crate::query::Providers; use crate::ty::fold::fold_regions; use crate::ty::layout::{FloatExt, IntegerExt}; @@ -366,7 +367,7 @@ impl<'tcx> TyCtxt<'tcx> { validate: impl Fn(Self, DefId) -> Result<(), ErrorGuaranteed>, ) -> Option { let drop_trait = self.lang_items().drop_trait()?; - self.ensure().coherent_trait(drop_trait).ok()?; + self.ensure_ok().coherent_trait(drop_trait).ok()?; let ty = self.type_of(adt_did).instantiate_identity(); let mut dtor_candidate = None; @@ -403,7 +404,7 @@ impl<'tcx> TyCtxt<'tcx> { validate: impl Fn(Self, DefId) -> Result<(), ErrorGuaranteed>, ) -> Option { let async_drop_trait = self.lang_items().async_drop_trait()?; - self.ensure().coherent_trait(async_drop_trait).ok()?; + self.ensure_ok().coherent_trait(async_drop_trait).ok()?; let ty = self.type_of(adt_did).instantiate_identity(); let mut dtor_candidate = None; @@ -758,10 +759,11 @@ impl<'tcx> TyCtxt<'tcx> { assert_eq!(re, self.lifetimes.re_erased); let var = ty::BoundVar::from_usize(vars.len()); vars.push(ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon)); - ty::Region::new_bound(self, debruijn, ty::BoundRegion { - var, - kind: ty::BoundRegionKind::Anon, - }) + ty::Region::new_bound( + self, + debruijn, + ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon }, + ) }); ty::EarlyBinder::bind(ty::Binder::bind_with_vars( ty, @@ -776,7 +778,6 @@ impl<'tcx> TyCtxt<'tcx> { self, def_id: DefId, args: GenericArgsRef<'tcx>, - inspect_coroutine_fields: InspectCoroutineFields, ) -> Result, Ty<'tcx>> { let mut visitor = OpaqueTypeExpander { seen_opaque_tys: FxHashSet::default(), @@ -785,9 +786,7 @@ impl<'tcx> TyCtxt<'tcx> { found_recursion: false, found_any_recursion: false, check_recursion: true, - expand_coroutines: true, tcx: self, - inspect_coroutine_fields, }; let expanded_type = visitor.expand_opaque_ty(def_id, args).unwrap(); @@ -876,6 +875,11 @@ impl<'tcx> TyCtxt<'tcx> { /// [public]: TyCtxt::is_private_dep /// [direct]: rustc_session::cstore::ExternCrate::is_direct pub fn is_user_visible_dep(self, key: CrateNum) -> bool { + // `#![rustc_private]` overrides defaults to make private dependencies usable. + if self.features().enabled(sym::rustc_private) { + return true; + } + // | Private | Direct | Visible | | // |---------|--------|---------|--------------------| // | Yes | Yes | Yes | !true || true | @@ -945,6 +949,29 @@ impl<'tcx> TyCtxt<'tcx> { ty } + + // Computes the variances for an alias (opaque or RPITIT) that represent + // its (un)captured regions. + pub fn opt_alias_variances( + self, + kind: impl Into, + def_id: DefId, + ) -> Option<&'tcx [ty::Variance]> { + match kind.into() { + ty::AliasTermKind::ProjectionTy => { + if self.is_impl_trait_in_trait(def_id) { + Some(self.variances_of(def_id)) + } else { + None + } + } + ty::AliasTermKind::OpaqueTy => Some(self.variances_of(def_id)), + ty::AliasTermKind::InherentTy + | ty::AliasTermKind::WeakTy + | ty::AliasTermKind::UnevaluatedConst + | ty::AliasTermKind::ProjectionConst => None, + } + } } struct OpaqueTypeExpander<'tcx> { @@ -959,19 +986,11 @@ struct OpaqueTypeExpander<'tcx> { primary_def_id: Option, found_recursion: bool, found_any_recursion: bool, - expand_coroutines: bool, /// Whether or not to check for recursive opaque types. /// This is `true` when we're explicitly checking for opaque type /// recursion, and 'false' otherwise to avoid unnecessary work. check_recursion: bool, tcx: TyCtxt<'tcx>, - inspect_coroutine_fields: InspectCoroutineFields, -} - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum InspectCoroutineFields { - No, - Yes, } impl<'tcx> OpaqueTypeExpander<'tcx> { @@ -1003,41 +1022,6 @@ impl<'tcx> OpaqueTypeExpander<'tcx> { None } } - - fn expand_coroutine(&mut self, def_id: DefId, args: GenericArgsRef<'tcx>) -> Option> { - if self.found_any_recursion { - return None; - } - let args = args.fold_with(self); - if !self.check_recursion || self.seen_opaque_tys.insert(def_id) { - let expanded_ty = match self.expanded_cache.get(&(def_id, args)) { - Some(expanded_ty) => *expanded_ty, - None => { - if matches!(self.inspect_coroutine_fields, InspectCoroutineFields::Yes) { - for bty in self.tcx.bound_coroutine_hidden_types(def_id) { - let hidden_ty = self.tcx.instantiate_bound_regions_with_erased( - bty.instantiate(self.tcx, args), - ); - self.fold_ty(hidden_ty); - } - } - let expanded_ty = Ty::new_coroutine_witness(self.tcx, def_id, args); - self.expanded_cache.insert((def_id, args), expanded_ty); - expanded_ty - } - }; - if self.check_recursion { - self.seen_opaque_tys.remove(&def_id); - } - Some(expanded_ty) - } else { - // If another opaque type that we contain is recursive, then it - // will report the error, so we don't have to. - self.found_any_recursion = true; - self.found_recursion = def_id == *self.primary_def_id.as_ref().unwrap(); - None - } - } } impl<'tcx> TypeFolder> for OpaqueTypeExpander<'tcx> { @@ -1046,19 +1030,13 @@ impl<'tcx> TypeFolder> for OpaqueTypeExpander<'tcx> { } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - let mut t = if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) = *t.kind() { + if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) = *t.kind() { self.expand_opaque_ty(def_id, args).unwrap_or(t) - } else if t.has_opaque_types() || t.has_coroutines() { + } else if t.has_opaque_types() { t.super_fold_with(self) } else { t - }; - if self.expand_coroutines { - if let ty::CoroutineWitness(def_id, args) = *t.kind() { - t = self.expand_coroutine(def_id, args).unwrap_or(t); - } } - t } fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { @@ -1178,18 +1156,18 @@ impl<'tcx> Ty<'tcx> { /// Returns the maximum value for the given numeric type (including `char`s) /// or returns `None` if the type is not numeric. - pub fn numeric_max_val(self, tcx: TyCtxt<'tcx>) -> Option> { + pub fn numeric_max_val(self, tcx: TyCtxt<'tcx>) -> Option> { let typing_env = TypingEnv::fully_monomorphized(); self.numeric_min_and_max_as_bits(tcx) - .map(|(_, max)| ty::Const::from_bits(tcx, max, typing_env, self)) + .map(|(_, max)| mir::Const::from_bits(tcx, max, typing_env, self)) } /// Returns the minimum value for the given numeric type (including `char`s) /// or returns `None` if the type is not numeric. - pub fn numeric_min_val(self, tcx: TyCtxt<'tcx>) -> Option> { + pub fn numeric_min_val(self, tcx: TyCtxt<'tcx>) -> Option> { let typing_env = TypingEnv::fully_monomorphized(); self.numeric_min_and_max_as_bits(tcx) - .map(|(min, _)| ty::Const::from_bits(tcx, min, typing_env, self)) + .map(|(min, _)| mir::Const::from_bits(tcx, min, typing_env, self)) } /// Checks whether values of this type `T` have a size known at @@ -1747,9 +1725,7 @@ pub fn reveal_opaque_types_in_bounds<'tcx>( found_recursion: false, found_any_recursion: false, check_recursion: false, - expand_coroutines: false, tcx, - inspect_coroutine_fields: InspectCoroutineFields::No, }; val.fold_with(&mut visitor) } @@ -1778,9 +1754,16 @@ pub fn intrinsic_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { + !has_body + } + _ => true, + }; Some(ty::IntrinsicDef { name: tcx.item_name(def_id.into()), - must_be_overridden: tcx.has_attr(def_id, sym::rustc_intrinsic_must_be_overridden), + must_be_overridden, const_stable: tcx.has_attr(def_id, sym::rustc_intrinsic_const_stable_indirect), }) } else { diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs index 09a05104e490a..6c9e0e7c0eb8a 100644 --- a/compiler/rustc_middle/src/ty/vtable.rs +++ b/compiler/rustc_middle/src/ty/vtable.rs @@ -2,9 +2,12 @@ use std::fmt; use rustc_ast::Mutability; use rustc_macros::HashStable; +use rustc_type_ir::elaborate; -use crate::mir::interpret::{AllocId, Allocation, CTFE_ALLOC_SALT, Pointer, Scalar, alloc_range}; -use crate::ty::{self, Instance, PolyTraitRef, Ty, TyCtxt}; +use crate::mir::interpret::{ + AllocId, AllocInit, Allocation, CTFE_ALLOC_SALT, Pointer, Scalar, alloc_range, +}; +use crate::ty::{self, Instance, TraitRef, Ty, TyCtxt}; #[derive(Clone, Copy, PartialEq, HashStable)] pub enum VtblEntry<'tcx> { @@ -19,7 +22,7 @@ pub enum VtblEntry<'tcx> { /// dispatchable associated function Method(Instance<'tcx>), /// pointer to a separate supertrait vtable, can be used by trait upcasting coercion - TraitVPtr(PolyTraitRef<'tcx>), + TraitVPtr(TraitRef<'tcx>), } impl<'tcx> fmt::Debug for VtblEntry<'tcx> { @@ -56,7 +59,7 @@ pub const COMMON_VTABLE_ENTRIES_ALIGN: usize = 2; // function is an accurate approximation. We verify this when actually computing the vtable below. pub(crate) fn vtable_min_entries<'tcx>( tcx: TyCtxt<'tcx>, - trait_ref: Option>, + trait_ref: Option>, ) -> usize { let mut count = TyCtxt::COMMON_VTABLE_ENTRIES.len(); let Some(trait_ref) = trait_ref else { @@ -64,7 +67,7 @@ pub(crate) fn vtable_min_entries<'tcx>( }; // This includes self in supertraits. - for def_id in tcx.supertrait_def_ids(trait_ref.def_id()) { + for def_id in elaborate::supertrait_def_ids(tcx, trait_ref.def_id) { count += tcx.own_existential_vtable_entries(def_id).len(); } @@ -80,7 +83,7 @@ pub(crate) fn vtable_min_entries<'tcx>( /// initial contents.) pub(super) fn vtable_allocation_provider<'tcx>( tcx: TyCtxt<'tcx>, - key: (Ty<'tcx>, Option>), + key: (Ty<'tcx>, Option>), ) -> AllocId { let (ty, poly_trait_ref) = key; @@ -107,7 +110,7 @@ pub(super) fn vtable_allocation_provider<'tcx>( let ptr_align = tcx.data_layout.pointer_align.abi; let vtable_size = ptr_size * u64::try_from(vtable_entries.len()).unwrap(); - let mut vtable = Allocation::uninit(vtable_size, ptr_align); + let mut vtable = Allocation::new(vtable_size, ptr_align, AllocInit::Uninit); // No need to do any alignment checks on the memory accesses below, because we know the // allocation is correctly aligned as we created it above. Also we're only offsetting by @@ -115,7 +118,7 @@ pub(super) fn vtable_allocation_provider<'tcx>( for (idx, entry) in vtable_entries.iter().enumerate() { let idx: u64 = u64::try_from(idx).unwrap(); - let scalar = match entry { + let scalar = match *entry { VtblEntry::MetadataDropInPlace => { if ty.needs_drop(tcx, ty::TypingEnv::fully_monomorphized()) { let instance = ty::Instance::resolve_drop_in_place(tcx, ty); @@ -131,13 +134,12 @@ pub(super) fn vtable_allocation_provider<'tcx>( VtblEntry::Vacant => continue, VtblEntry::Method(instance) => { // Prepare the fn ptr we write into the vtable. - let fn_alloc_id = tcx.reserve_and_set_fn_alloc(*instance, CTFE_ALLOC_SALT); + let fn_alloc_id = tcx.reserve_and_set_fn_alloc(instance, CTFE_ALLOC_SALT); let fn_ptr = Pointer::from(fn_alloc_id); Scalar::from_pointer(fn_ptr, &tcx) } VtblEntry::TraitVPtr(trait_ref) => { - let super_trait_ref = trait_ref - .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); + let super_trait_ref = ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref); let supertrait_alloc_id = tcx.vtable_allocation((ty, Some(super_trait_ref))); let vptr = Pointer::from(supertrait_alloc_id); Scalar::from_pointer(vptr, &tcx) diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs index 2dcba8c2f82c4..3e8a3d1a28923 100644 --- a/compiler/rustc_middle/src/ty/walk.rs +++ b/compiler/rustc_middle/src/ty/walk.rs @@ -206,7 +206,7 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>) | ty::ConstKind::Bound(..) | ty::ConstKind::Error(_) => {} - ty::ConstKind::Value(ty, _) => stack.push(ty.into()), + ty::ConstKind::Value(cv) => stack.push(cv.ty.into()), ty::ConstKind::Expr(expr) => stack.extend(expr.args().iter().rev()), ty::ConstKind::Unevaluated(ct) => { diff --git a/compiler/rustc_middle/src/util/bug.rs b/compiler/rustc_middle/src/util/bug.rs index b99336c2c85ba..7dda68b8393a1 100644 --- a/compiler/rustc_middle/src/util/bug.rs +++ b/compiler/rustc_middle/src/util/bug.rs @@ -1,4 +1,4 @@ -// These functions are used by macro expansion for bug! and span_bug! +// These functions are used by macro expansion for `bug!` and `span_bug!`. use std::fmt; use std::panic::{Location, panic_any}; @@ -8,15 +8,15 @@ use rustc_span::Span; use crate::ty::{TyCtxt, tls}; +// This wrapper makes for more compact code at callsites than calling `opt_span_buf_fmt` directly. #[cold] #[inline(never)] #[track_caller] pub fn bug_fmt(args: fmt::Arguments<'_>) -> ! { - // this wrapper mostly exists so I don't have to write a fully - // qualified path of None:: inside the bug!() macro definition opt_span_bug_fmt(None::, args, Location::caller()); } +// This wrapper makes for more compact code at callsites than calling `opt_span_buf_fmt` directly. #[cold] #[inline(never)] #[track_caller] diff --git a/compiler/rustc_middle/src/util/call_kind.rs b/compiler/rustc_middle/src/util/call_kind.rs deleted file mode 100644 index 0e39533168792..0000000000000 --- a/compiler/rustc_middle/src/util/call_kind.rs +++ /dev/null @@ -1,149 +0,0 @@ -//! Common logic for borrowck use-after-move errors when moved into a `fn(self)`, -//! as well as errors when attempting to call a non-const function in a const -//! context. - -use rustc_hir::def_id::DefId; -use rustc_hir::{LangItem, lang_items}; -use rustc_span::{DesugaringKind, Ident, Span, sym}; -use tracing::debug; - -use crate::ty::{AssocItemContainer, GenericArgsRef, Instance, Ty, TyCtxt, TypingEnv}; - -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub enum CallDesugaringKind { - /// for _ in x {} calls x.into_iter() - ForLoopIntoIter, - /// for _ in x {} calls iter.next() - ForLoopNext, - /// x? calls x.branch() - QuestionBranch, - /// x? calls type_of(x)::from_residual() - QuestionFromResidual, - /// try { ..; x } calls type_of(x)::from_output(x) - TryBlockFromOutput, - /// `.await` calls `IntoFuture::into_future` - Await, -} - -impl CallDesugaringKind { - pub fn trait_def_id(self, tcx: TyCtxt<'_>) -> DefId { - match self { - Self::ForLoopIntoIter => tcx.get_diagnostic_item(sym::IntoIterator).unwrap(), - Self::ForLoopNext => tcx.require_lang_item(LangItem::Iterator, None), - Self::QuestionBranch | Self::TryBlockFromOutput => { - tcx.require_lang_item(LangItem::Try, None) - } - Self::QuestionFromResidual => tcx.get_diagnostic_item(sym::FromResidual).unwrap(), - Self::Await => tcx.get_diagnostic_item(sym::IntoFuture).unwrap(), - } - } -} - -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub enum CallKind<'tcx> { - /// A normal method call of the form `receiver.foo(a, b, c)` - Normal { - self_arg: Option, - desugaring: Option<(CallDesugaringKind, Ty<'tcx>)>, - method_did: DefId, - method_args: GenericArgsRef<'tcx>, - }, - /// A call to `Fn(..)::call(..)`, desugared from `my_closure(a, b, c)` - FnCall { fn_trait_id: DefId, self_ty: Ty<'tcx> }, - /// A call to an operator trait, desugared from operator syntax (e.g. `a << b`) - Operator { self_arg: Option, trait_id: DefId, self_ty: Ty<'tcx> }, - DerefCoercion { - /// The `Span` of the `Target` associated type - /// in the `Deref` impl we are using. - deref_target: Span, - /// The type `T::Deref` we are dereferencing to - deref_target_ty: Ty<'tcx>, - self_ty: Ty<'tcx>, - }, -} - -pub fn call_kind<'tcx>( - tcx: TyCtxt<'tcx>, - typing_env: TypingEnv<'tcx>, - method_did: DefId, - method_args: GenericArgsRef<'tcx>, - fn_call_span: Span, - from_hir_call: bool, - self_arg: Option, -) -> CallKind<'tcx> { - let parent = tcx.opt_associated_item(method_did).and_then(|assoc| { - let container_id = assoc.container_id(tcx); - match assoc.container { - AssocItemContainer::Impl => tcx.trait_id_of_impl(container_id), - AssocItemContainer::Trait => Some(container_id), - } - }); - - let fn_call = parent.and_then(|p| { - lang_items::FN_TRAITS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p) - }); - - let operator = if !from_hir_call && let Some(p) = parent { - lang_items::OPERATORS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p) - } else { - None - }; - - let is_deref = !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did); - - // Check for a 'special' use of 'self' - - // an FnOnce call, an operator (e.g. `<<`), or a - // deref coercion. - let kind = if let Some(trait_id) = fn_call { - Some(CallKind::FnCall { fn_trait_id: trait_id, self_ty: method_args.type_at(0) }) - } else if let Some(trait_id) = operator { - Some(CallKind::Operator { self_arg, trait_id, self_ty: method_args.type_at(0) }) - } else if is_deref { - let deref_target = tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| { - Instance::try_resolve(tcx, typing_env, deref_target, method_args).transpose() - }); - if let Some(Ok(instance)) = deref_target { - let deref_target_ty = instance.ty(tcx, typing_env); - Some(CallKind::DerefCoercion { - deref_target: tcx.def_span(instance.def_id()), - deref_target_ty, - self_ty: method_args.type_at(0), - }) - } else { - None - } - } else { - None - }; - - kind.unwrap_or_else(|| { - // This isn't a 'special' use of `self` - debug!(?method_did, ?fn_call_span); - let desugaring = if tcx.is_lang_item(method_did, LangItem::IntoIterIntoIter) - && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop) - { - Some((CallDesugaringKind::ForLoopIntoIter, method_args.type_at(0))) - } else if tcx.is_lang_item(method_did, LangItem::IteratorNext) - && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop) - { - Some((CallDesugaringKind::ForLoopNext, method_args.type_at(0))) - } else if fn_call_span.desugaring_kind() == Some(DesugaringKind::QuestionMark) { - if tcx.is_lang_item(method_did, LangItem::TryTraitBranch) { - Some((CallDesugaringKind::QuestionBranch, method_args.type_at(0))) - } else if tcx.is_lang_item(method_did, LangItem::TryTraitFromResidual) { - Some((CallDesugaringKind::QuestionFromResidual, method_args.type_at(0))) - } else { - None - } - } else if tcx.is_lang_item(method_did, LangItem::TryTraitFromOutput) - && fn_call_span.desugaring_kind() == Some(DesugaringKind::TryBlock) - { - Some((CallDesugaringKind::TryBlockFromOutput, method_args.type_at(0))) - } else if fn_call_span.is_desugaring(DesugaringKind::Await) { - Some((CallDesugaringKind::Await, method_args.type_at(0))) - } else { - None - }; - CallKind::Normal { self_arg, desugaring, method_did, method_args } - }) -} diff --git a/compiler/rustc_middle/src/util/common.rs b/compiler/rustc_middle/src/util/common.rs deleted file mode 100644 index 223b2b3bfe4de..0000000000000 --- a/compiler/rustc_middle/src/util/common.rs +++ /dev/null @@ -1,22 +0,0 @@ -#[cfg(test)] -mod tests; - -pub fn to_readable_str(mut val: usize) -> String { - let mut groups = vec![]; - loop { - let group = val % 1000; - - val /= 1000; - - if val == 0 { - groups.push(group.to_string()); - break; - } else { - groups.push(format!("{group:03}")); - } - } - - groups.reverse(); - - groups.join("_") -} diff --git a/compiler/rustc_middle/src/util/common/tests.rs b/compiler/rustc_middle/src/util/common/tests.rs deleted file mode 100644 index 9a9fb203c625e..0000000000000 --- a/compiler/rustc_middle/src/util/common/tests.rs +++ /dev/null @@ -1,14 +0,0 @@ -use super::*; - -#[test] -fn test_to_readable_str() { - assert_eq!("0", to_readable_str(0)); - assert_eq!("1", to_readable_str(1)); - assert_eq!("99", to_readable_str(99)); - assert_eq!("999", to_readable_str(999)); - assert_eq!("1_000", to_readable_str(1_000)); - assert_eq!("1_001", to_readable_str(1_001)); - assert_eq!("999_999", to_readable_str(999_999)); - assert_eq!("1_000_000", to_readable_str(1_000_000)); - assert_eq!("1_234_567", to_readable_str(1_234_567)); -} diff --git a/compiler/rustc_middle/src/util/find_self_call.rs b/compiler/rustc_middle/src/util/find_self_call.rs deleted file mode 100644 index ec6051d0a771a..0000000000000 --- a/compiler/rustc_middle/src/util/find_self_call.rs +++ /dev/null @@ -1,44 +0,0 @@ -use rustc_span::def_id::DefId; -use rustc_span::source_map::Spanned; -use tracing::debug; - -use crate::mir::*; -use crate::ty::{self, GenericArgsRef, TyCtxt}; - -/// Checks if the specified `local` is used as the `self` parameter of a method call -/// in the provided `BasicBlock`. If it is, then the `DefId` of the called method is -/// returned. -pub fn find_self_call<'tcx>( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - local: Local, - block: BasicBlock, -) -> Option<(DefId, GenericArgsRef<'tcx>)> { - debug!("find_self_call(local={:?}): terminator={:?}", local, body[block].terminator); - if let Some(Terminator { kind: TerminatorKind::Call { func, args, .. }, .. }) = - &body[block].terminator - { - debug!("find_self_call: func={:?}", func); - if let Operand::Constant(box ConstOperand { const_, .. }) = func { - if let ty::FnDef(def_id, fn_args) = *const_.ty().kind() { - if let Some(ty::AssocItem { fn_has_self_parameter: true, .. }) = - tcx.opt_associated_item(def_id) - { - debug!("find_self_call: args={:?}", fn_args); - if let [ - Spanned { - node: Operand::Move(self_place) | Operand::Copy(self_place), .. - }, - .., - ] = **args - { - if self_place.as_local() == Some(local) { - return Some((def_id, fn_args)); - } - } - } - } - } - } - None -} diff --git a/compiler/rustc_middle/src/util/mod.rs b/compiler/rustc_middle/src/util/mod.rs index 8dafc42264485..85519fb0a7d2c 100644 --- a/compiler/rustc_middle/src/util/mod.rs +++ b/compiler/rustc_middle/src/util/mod.rs @@ -1,16 +1,10 @@ pub mod bug; -pub mod call_kind; -pub mod common; -pub mod find_self_call; - -pub use call_kind::{CallDesugaringKind, CallKind, call_kind}; -pub use find_self_call::find_self_call; #[derive(Default, Copy, Clone)] pub struct Providers { - pub queries: rustc_middle::query::Providers, - pub extern_queries: rustc_middle::query::ExternProviders, - pub hooks: rustc_middle::hooks::Providers, + pub queries: crate::query::Providers, + pub extern_queries: crate::query::ExternProviders, + pub hooks: crate::hooks::Providers, } /// Backwards compatibility hack to keep the diff small. This @@ -23,7 +17,7 @@ impl std::ops::DerefMut for Providers { } impl std::ops::Deref for Providers { - type Target = rustc_middle::query::Providers; + type Target = crate::query::Providers; fn deref(&self) -> &Self::Target { &self.queries diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index 390909bb0ab0c..433f7542bd704 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -7,7 +7,6 @@ use rustc_errors::codes::*; use rustc_errors::{Applicability, MultiSpan, pluralize, struct_span_code_err}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_middle::ty::{self, Representability, Ty, TyCtxt}; use rustc_query_system::Value; use rustc_query_system::query::{CycleError, report_cycle}; use rustc_span::def_id::LocalDefId; @@ -15,6 +14,7 @@ use rustc_span::{ErrorGuaranteed, Span}; use crate::dep_graph::dep_kinds; use crate::query::plumbing::CyclePlaceholder; +use crate::ty::{self, Representability, Ty, TyCtxt}; impl<'tcx> Value> for Ty<'_> { fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &CycleError, guar: ErrorGuaranteed) -> Self { @@ -360,7 +360,7 @@ fn find_item_ty_spans( if let Res::Def(kind, def_id) = path.res && matches!(kind, DefKind::Enum | DefKind::Struct | DefKind::Union) { - let check_params = def_id.as_local().map_or(true, |def_id| { + let check_params = def_id.as_local().is_none_or(|def_id| { if def_id == needle { spans.push(ty.span); } @@ -374,7 +374,13 @@ fn find_item_ty_spans( if let hir::GenericArg::Type(ty) = arg && params_in_repr.contains(i as u32) { - find_item_ty_spans(tcx, ty, needle, spans, seen_representable); + find_item_ty_spans( + tcx, + ty.as_unambig_ty(), + needle, + spans, + seen_representable, + ); } } } diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml index 119047227431d..1f3689926bcf3 100644 --- a/compiler/rustc_mir_build/Cargo.toml +++ b/compiler/rustc_mir_build/Cargo.toml @@ -12,6 +12,7 @@ rustc_abi = { path = "../rustc_abi" } rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index edba247c7b0df..fae159103e70d 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -285,12 +285,16 @@ mir_build_pointer_pattern = function pointers and raw pointers not derived from mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future -mir_build_rust_2024_incompatible_pat = this pattern relies on behavior which may change in edition 2024 - -mir_build_rustc_box_attribute_error = `#[rustc_box]` attribute used incorrectly - .attributes = no other attributes may be applied - .not_box = `#[rustc_box]` may only be applied to a `Box::new()` call - .missing_box = `#[rustc_box]` requires the `owned_box` lang item +mir_build_rust_2024_incompatible_pat = {$bad_modifiers -> + *[true] binding modifiers{$bad_ref_pats -> + *[true] {" "}and reference patterns + [false] {""} + } + [false] reference patterns + } may only be written when the default binding mode is `move`{$is_hard_error -> + *[true] {""} + [false] {" "}in Rust 2024 + } mir_build_static_in_pattern = statics cannot be referenced in patterns .label = can't be used in patterns @@ -329,12 +333,6 @@ mir_build_type_not_structural_more_info = see https://doc.rust-lang.org/stable/s mir_build_type_not_structural_tip = the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details -mir_build_unconditional_recursion = function cannot return without recursing - .label = cannot return without recursing - .help = a `loop` may express intention better if this is on purpose - -mir_build_unconditional_recursion_call_site_label = recursive call site - mir_build_union_field_requires_unsafe = access to union field is unsafe and requires unsafe block .note = the field may not be properly initialized: using uninitialized data will cause undefined behavior @@ -370,6 +368,18 @@ mir_build_unreachable_pattern = unreachable pattern .unreachable_pattern_let_binding = there is a binding of the same name; if you meant to pattern match against the value of that binding, that is a feature of constants that is not available for `let` bindings .suggestion = remove the match arm +mir_build_unsafe_binder_cast_requires_unsafe = + unsafe binder cast is unsafe and requires unsafe block + .label = unsafe binder cast + .note = casting to or from an `unsafe<...>` binder type is unsafe since it erases lifetime + information that may be required to uphold safety guarantees of a type + +mir_build_unsafe_binder_cast_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = + unsafe binder cast is unsafe and requires unsafe block or unsafe fn + .label = unsafe binder cast + .note = casting to or from an `unsafe<...>` binder type is unsafe since it erases lifetime + information that may be required to uphold safety guarantees of a type + mir_build_unsafe_field_requires_unsafe = use of unsafe field is unsafe and requires unsafe block .note = unsafe fields may carry library invariants diff --git a/compiler/rustc_mir_build/src/builder/cfg.rs b/compiler/rustc_mir_build/src/builder/cfg.rs index 42212f2518b0a..082cdc2e2a49e 100644 --- a/compiler/rustc_mir_build/src/builder/cfg.rs +++ b/compiler/rustc_mir_build/src/builder/cfg.rs @@ -40,10 +40,10 @@ impl<'tcx> CFG<'tcx> { place: Place<'tcx>, rvalue: Rvalue<'tcx>, ) { - self.push(block, Statement { - source_info, - kind: StatementKind::Assign(Box::new((place, rvalue))), - }); + self.push( + block, + Statement { source_info, kind: StatementKind::Assign(Box::new((place, rvalue))) }, + ); } pub(crate) fn push_assign_constant( diff --git a/compiler/rustc_mir_build/src/builder/custom/mod.rs b/compiler/rustc_mir_build/src/builder/custom/mod.rs index ca2e1dd7a1a81..bfc16816e2e5e 100644 --- a/compiler/rustc_mir_build/src/builder/custom/mod.rs +++ b/compiler/rustc_mir_build/src/builder/custom/mod.rs @@ -6,7 +6,7 @@ //! present, and if so we branch off into this module, which implements the attribute by //! implementing a custom lowering from THIR to MIR. //! -//! The result of this lowering is returned "normally" from the `build_mir` hook, with the only +//! The result of this lowering is returned "normally" from `build_mir`, with the only //! notable difference being that the `injected` field in the body is set. Various components of the //! MIR pipeline, like borrowck and the pass manager will then consult this field (via //! `body.should_skip()`) to skip the parts of the MIR pipeline that precede the MIR phase the user diff --git a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs index 3dd5de0223081..eab414e150fa3 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs @@ -246,13 +246,14 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { let offset = self.parse_operand(args[1])?; Ok(Rvalue::BinaryOp(BinOp::Offset, Box::new((ptr, offset)))) }, + @call(mir_len, args) => Ok(Rvalue::Len(self.parse_place(args[0])?)), @call(mir_ptr_metadata, args) => Ok(Rvalue::UnaryOp(UnOp::PtrMetadata, self.parse_operand(args[0])?)), @call(mir_copy_for_deref, args) => Ok(Rvalue::CopyForDeref(self.parse_place(args[0])?)), ExprKind::Borrow { borrow_kind, arg } => Ok( Rvalue::Ref(self.tcx.lifetimes.re_erased, *borrow_kind, self.parse_place(*arg)?) ), ExprKind::RawBorrow { mutability, arg } => Ok( - Rvalue::RawPtr(*mutability, self.parse_place(*arg)?) + Rvalue::RawPtr((*mutability).into(), self.parse_place(*arg)?) ), ExprKind::Binary { op, lhs, rhs } => Ok( Rvalue::BinaryOp(*op, Box::new((self.parse_operand(*lhs)?, self.parse_operand(*rhs)?))) diff --git a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs index 177c1e33a83bb..84c9297e65898 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -3,13 +3,12 @@ use rustc_abi::Size; use rustc_ast as ast; use rustc_hir::LangItem; -use rustc_middle::mir::interpret::{ - Allocation, CTFE_ALLOC_SALT, LitToConstError, LitToConstInput, Scalar, -}; +use rustc_middle::mir::interpret::{Allocation, CTFE_ALLOC_SALT, LitToConstInput, Scalar}; use rustc_middle::mir::*; use rustc_middle::thir::*; use rustc_middle::ty::{ - self, CanonicalUserType, CanonicalUserTypeAnnotation, Ty, TyCtxt, UserTypeAnnotationIndex, + self, CanonicalUserType, CanonicalUserTypeAnnotation, Ty, TyCtxt, TypeVisitableExt as _, + UserTypeAnnotationIndex, }; use rustc_middle::{bug, mir, span_bug}; use tracing::{instrument, trace}; @@ -50,16 +49,7 @@ pub(crate) fn as_constant_inner<'tcx>( let Expr { ty, temp_lifetime: _, span, ref kind } = *expr; match *kind { ExprKind::Literal { lit, neg } => { - let const_ = match lit_to_mir_constant(tcx, LitToConstInput { lit: &lit.node, ty, neg }) - { - Ok(c) => c, - Err(LitToConstError::Reported(guar)) => { - Const::Ty(Ty::new_error(tcx, guar), ty::Const::new_error(tcx, guar)) - } - Err(LitToConstError::TypeError) => { - bug!("encountered type error in `lit_to_mir_constant`") - } - }; + let const_ = lit_to_mir_constant(tcx, LitToConstInput { lit: &lit.node, ty, neg }); ConstOperand { span, user_ty: None, const_ } } @@ -108,22 +98,23 @@ pub(crate) fn as_constant_inner<'tcx>( } #[instrument(skip(tcx, lit_input))] -fn lit_to_mir_constant<'tcx>( - tcx: TyCtxt<'tcx>, - lit_input: LitToConstInput<'tcx>, -) -> Result, LitToConstError> { +fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx>) -> Const<'tcx> { let LitToConstInput { lit, ty, neg } = lit_input; - let trunc = |n| { - let width = match tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)) { - Ok(layout) => layout.size, - Err(_) => { - tcx.dcx().bug(format!("couldn't compute width of literal: {:?}", lit_input.lit)) - } - }; + + if let Err(guar) = ty.error_reported() { + return Const::Ty(Ty::new_error(tcx, guar), ty::Const::new_error(tcx, guar)); + } + + let trunc = |n, width: ty::UintTy| { + let width = width + .normalize(tcx.data_layout.pointer_size.bits().try_into().unwrap()) + .bit_width() + .unwrap(); + let width = Size::from_bits(width); trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits()); let result = width.truncate(n); trace!("trunc result: {}", result); - Ok(ConstValue::Scalar(Scalar::from_uint(result, width))) + ConstValue::Scalar(Scalar::from_uint(result, width)) }; let value = match (lit, ty.kind()) { @@ -153,21 +144,21 @@ fn lit_to_mir_constant<'tcx>( (ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => { ConstValue::Scalar(Scalar::from_uint(*n, Size::from_bytes(1))) } - (ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => { - trunc(if neg { (n.get() as i128).overflowing_neg().0 as u128 } else { n.get() })? + (ast::LitKind::Int(n, _), ty::Uint(ui)) if !neg => trunc(n.get(), *ui), + (ast::LitKind::Int(n, _), ty::Int(i)) => trunc( + if neg { (n.get() as i128).overflowing_neg().0 as u128 } else { n.get() }, + i.to_unsigned(), + ), + (ast::LitKind::Float(n, _), ty::Float(fty)) => { + parse_float_into_constval(*n, *fty, neg).unwrap() } - (ast::LitKind::Float(n, _), ty::Float(fty)) => parse_float_into_constval(*n, *fty, neg) - .ok_or_else(|| { - LitToConstError::Reported( - tcx.dcx() - .delayed_bug(format!("couldn't parse float literal: {:?}", lit_input.lit)), - ) - })?, (ast::LitKind::Bool(b), ty::Bool) => ConstValue::Scalar(Scalar::from_bool(*b)), (ast::LitKind::Char(c), ty::Char) => ConstValue::Scalar(Scalar::from_char(*c)), - (ast::LitKind::Err(guar), _) => return Err(LitToConstError::Reported(*guar)), - _ => return Err(LitToConstError::TypeError), + (ast::LitKind::Err(guar), _) => { + return Const::Ty(Ty::new_error(tcx, *guar), ty::Const::new_error(tcx, *guar)); + } + _ => bug!("invalid lit/ty combination in `lit_to_mir_constant`: {lit:?}: {ty:?}"), }; - Ok(Const::Val(value, ty)) + Const::Val(value, ty) } diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index 89c7bb357ef51..308ca442b40cc 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -11,7 +11,7 @@ use rustc_middle::mir::*; use rustc_middle::thir::*; use rustc_middle::ty::{self, AdtDef, CanonicalUserTypeAnnotation, Ty, Variance}; use rustc_middle::{bug, span_bug}; -use rustc_span::{DesugaringKind, Span}; +use rustc_span::Span; use tracing::{debug, instrument, trace}; use crate::builder::ForGuard::{OutsideGuard, RefWithinGuard}; @@ -105,7 +105,8 @@ fn convert_to_hir_projections_and_truncate_for_capture( ProjectionElem::OpaqueCast(_) | ProjectionElem::Subtype(..) => continue, ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } => { + | ProjectionElem::Subslice { .. } + | ProjectionElem::UnwrapUnsafeBinder(_) => { // We don't capture array-access projections. // We can stop here as arrays are captured completely. break; @@ -483,16 +484,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }); let place = place_builder.to_place(this); - this.cfg.push(block, Statement { - source_info: ty_source_info, - kind: StatementKind::AscribeUserType( - Box::new((place, UserTypeProjection { - base: annotation_index, - projs: vec![], - })), - Variance::Invariant, - ), - }); + this.cfg.push( + block, + Statement { + source_info: ty_source_info, + kind: StatementKind::AscribeUserType( + Box::new(( + place, + UserTypeProjection { base: annotation_index, projs: vec![] }, + )), + Variance::Invariant, + ), + }, + ); } block.and(place_builder) } @@ -509,20 +513,37 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { user_ty: user_ty.clone(), inferred_ty: expr.ty, }); - this.cfg.push(block, Statement { - source_info: ty_source_info, - kind: StatementKind::AscribeUserType( - Box::new((Place::from(temp), UserTypeProjection { - base: annotation_index, - projs: vec![], - })), - Variance::Invariant, - ), - }); + this.cfg.push( + block, + Statement { + source_info: ty_source_info, + kind: StatementKind::AscribeUserType( + Box::new(( + Place::from(temp), + UserTypeProjection { base: annotation_index, projs: vec![] }, + )), + Variance::Invariant, + ), + }, + ); } block.and(PlaceBuilder::from(temp)) } + ExprKind::PlaceUnwrapUnsafeBinder { source } => { + let place_builder = unpack!( + block = this.expr_as_place(block, source, mutability, fake_borrow_temps,) + ); + block.and(place_builder.project(PlaceElem::UnwrapUnsafeBinder(expr.ty))) + } + ExprKind::ValueUnwrapUnsafeBinder { source } => { + let source_expr = &this.thir[source]; + let temp = unpack!( + block = this.as_temp(block, source_expr.temp_lifetime, source, mutability) + ); + block.and(PlaceBuilder::from(temp).project(PlaceElem::UnwrapUnsafeBinder(expr.ty))) + } + ExprKind::Array { .. } | ExprKind::Tuple { .. } | ExprKind::Adt { .. } @@ -560,7 +581,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::OffsetOf { .. } | ExprKind::Yield { .. } | ExprKind::ThreadLocalRef(_) - | ExprKind::Call { .. } => { + | ExprKind::Call { .. } + | ExprKind::WrapUnsafeBinder { .. } => { // these are not places, so we need to make a temporary. debug_assert!(!matches!(Category::of(&expr.kind), Some(Category::Place))); let temp = @@ -635,7 +657,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// For arrays it'll be `Operand::Constant` with the actual length; /// For slices it'll be `Operand::Move` of a local using `PtrMetadata`. - pub(in crate::builder) fn len_of_slice_or_array( + fn len_of_slice_or_array( &mut self, block: BasicBlock, place: Place<'tcx>, @@ -643,35 +665,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { source_info: SourceInfo, ) -> Operand<'tcx> { let place_ty = place.ty(&self.local_decls, self.tcx).ty; - let usize_ty = self.tcx.types.usize; - match place_ty.kind() { ty::Array(_elem_ty, len_const) => { - let ty_const = if let Some((_, len_ty)) = len_const.try_to_valtree() - && len_ty != self.tcx.types.usize - { - // Bad const generics can give us a constant from the type that's - // not actually a `usize`, so in that case give an error instead. - // FIXME: It'd be nice if the type checker made sure this wasn't - // possible, instead. - let err = self.tcx.dcx().span_delayed_bug( - span, - format!( - "Array length should have already been a type error, as it's {len_ty:?}" - ), - ); - ty::Const::new_error(self.tcx, err) - } else { - // We know how long an array is, so just use that as a constant - // directly -- no locals needed. We do need one statement so - // that borrow- and initialization-checking consider it used, - // though. FIXME: Do we really *need* to count this as a use? - // Could partial array tracking work off something else instead? - self.cfg.push_fake_read(block, source_info, FakeReadCause::ForIndex, place); - *len_const - }; - - let const_ = Const::from_ty_const(ty_const, usize_ty, self.tcx); + // We know how long an array is, so just use that as a constant + // directly -- no locals needed. We do need one statement so + // that borrow- and initialization-checking consider it used, + // though. FIXME: Do we really *need* to count this as a use? + // Could partial array tracking work off something else instead? + self.cfg.push_fake_read(block, source_info, FakeReadCause::ForIndex, place); + let const_ = Const::Ty(self.tcx.types.usize, *len_const); Operand::Constant(Box::new(ConstOperand { span, user_ty: None, const_ })) } ty::Slice(_elem_ty) => { @@ -686,27 +688,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // the MIR we're building here needs to pass NLL later. Operand::Copy(Place::from(place.local)) } else { - let len_span = self.tcx.with_stable_hashing_context(|hcx| { - let span = source_info.span; - span.mark_with_reason( - None, - DesugaringKind::IndexBoundsCheckReborrow, - span.edition(), - hcx, - ) - }); let ptr_ty = Ty::new_imm_ptr(self.tcx, place_ty); let slice_ptr = self.temp(ptr_ty, span); self.cfg.push_assign( block, - SourceInfo { span: len_span, ..source_info }, + source_info, slice_ptr, - Rvalue::RawPtr(Mutability::Not, place), + Rvalue::RawPtr(RawPtrKind::FakeForPtrMetadata, place), ); Operand::Move(slice_ptr) }; - let len = self.temp(usize_ty, span); + let len = self.temp(self.tcx.types.usize, span); self.cfg.push_assign( block, source_info, @@ -805,7 +798,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ProjectionElem::OpaqueCast(..) | ProjectionElem::Subtype(..) | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } => (), + | ProjectionElem::Subslice { .. } + | ProjectionElem::UnwrapUnsafeBinder(_) => (), } } } diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs index 9961c2488ef42..2c9a1de7f9978 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs @@ -151,19 +151,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); let storage = this.temp(Ty::new_mut_ptr(tcx, tcx.types.u8), expr_span); let success = this.cfg.start_new_block(); - this.cfg.terminate(block, source_info, TerminatorKind::Call { - func: exchange_malloc, - args: [Spanned { node: Operand::Move(size), span: DUMMY_SP }, Spanned { - node: Operand::Move(align), - span: DUMMY_SP, - }] - .into(), - destination: storage, - target: Some(success), - unwind: UnwindAction::Continue, - call_source: CallSource::Misc, - fn_span: expr_span, - }); + this.cfg.terminate( + block, + source_info, + TerminatorKind::Call { + func: exchange_malloc, + args: [ + Spanned { node: Operand::Move(size), span: DUMMY_SP }, + Spanned { node: Operand::Move(align), span: DUMMY_SP }, + ] + .into(), + destination: storage, + target: Some(success), + unwind: UnwindAction::Continue, + call_source: CallSource::Misc, + fn_span: expr_span, + }, + ); this.diverge_from(block); block = success; @@ -171,10 +175,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // and therefore is not considered during coroutine auto-trait // determination. See the comment about `box` at `yield_in_scope`. let result = this.local_decls.push(LocalDecl::new(expr.ty, expr_span)); - this.cfg.push(block, Statement { - source_info, - kind: StatementKind::StorageLive(result), - }); + this.cfg.push( + block, + Statement { source_info, kind: StatementKind::StorageLive(result) }, + ); if let Some(scope) = scope.temp_lifetime { // schedule a shallow free of that memory, lest we unwind: this.schedule_drop_storage_and_value(expr_span, scope, result); @@ -272,12 +276,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); merge_place }; - this.cfg.push(block, Statement { - source_info, - kind: StatementKind::Intrinsic(Box::new( - NonDivergingIntrinsic::Assume(Operand::Move(assert_place)), - )), - }); + this.cfg.push( + block, + Statement { + source_info, + kind: StatementKind::Intrinsic(Box::new( + NonDivergingIntrinsic::Assume(Operand::Move(assert_place)), + )), + }, + ); } (op, ty) @@ -508,6 +515,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.and(Rvalue::Use(Operand::Constant(Box::new(constant)))) } + ExprKind::WrapUnsafeBinder { source } => { + let source = unpack!( + block = this.as_operand( + block, + scope, + source, + LocalInfo::Boring, + NeedsTemporary::Maybe + ) + ); + block.and(Rvalue::WrapUnsafeBinder(source, expr.ty)) + } + ExprKind::Yield { .. } | ExprKind::Block { .. } | ExprKind::Match { .. } @@ -532,7 +552,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::Become { .. } | ExprKind::InlineAsm { .. } | ExprKind::PlaceTypeAscription { .. } - | ExprKind::ValueTypeAscription { .. } => { + | ExprKind::ValueTypeAscription { .. } + | ExprKind::PlaceUnwrapUnsafeBinder { .. } + | ExprKind::ValueUnwrapUnsafeBinder { .. } => { // these do not have corresponding `Rvalue` variants, // so make an operand and then return that debug_assert!(!matches!( @@ -724,12 +746,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); if let Operand::Move(to_drop) = value_operand { let success = this.cfg.start_new_block(); - this.cfg.terminate(block, outer_source_info, TerminatorKind::Drop { - place: to_drop, - target: success, - unwind: UnwindAction::Continue, - replace: false, - }); + this.cfg.terminate( + block, + outer_source_info, + TerminatorKind::Drop { + place: to_drop, + target: success, + unwind: UnwindAction::Continue, + replace: false, + }, + ); this.diverge_from(block); block = success; } diff --git a/compiler/rustc_mir_build/src/builder/expr/category.rs b/compiler/rustc_mir_build/src/builder/expr/category.rs index e0349e3e3f668..ca55d36bfc659 100644 --- a/compiler/rustc_mir_build/src/builder/expr/category.rs +++ b/compiler/rustc_mir_build/src/builder/expr/category.rs @@ -41,7 +41,9 @@ impl Category { | ExprKind::UpvarRef { .. } | ExprKind::VarRef { .. } | ExprKind::PlaceTypeAscription { .. } - | ExprKind::ValueTypeAscription { .. } => Some(Category::Place), + | ExprKind::ValueTypeAscription { .. } + | ExprKind::PlaceUnwrapUnsafeBinder { .. } + | ExprKind::ValueUnwrapUnsafeBinder { .. } => Some(Category::Place), ExprKind::LogicalOp { .. } | ExprKind::Match { .. } @@ -68,7 +70,8 @@ impl Category { | ExprKind::Assign { .. } | ExprKind::AssignOp { .. } | ExprKind::ThreadLocalRef(_) - | ExprKind::OffsetOf { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)), + | ExprKind::OffsetOf { .. } + | ExprKind::WrapUnsafeBinder { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)), ExprKind::ConstBlock { .. } | ExprKind::Literal { .. } diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 88f63d4e22cbd..65dd061003d8d 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -220,10 +220,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.in_breakable_scope(Some(loop_block), destination, expr_span, move |this| { // conduct the test, if necessary let body_block = this.cfg.start_new_block(); - this.cfg.terminate(loop_block, source_info, TerminatorKind::FalseUnwind { - real_target: body_block, - unwind: UnwindAction::Continue, - }); + this.cfg.terminate( + loop_block, + source_info, + TerminatorKind::FalseUnwind { + real_target: body_block, + unwind: UnwindAction::Continue, + }, + ); this.diverge_from(loop_block); // The “return” value of the loop body must always be a unit. We therefore @@ -254,30 +258,34 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { debug!("expr_into_dest: fn_span={:?}", fn_span); - this.cfg.terminate(block, source_info, TerminatorKind::Call { - func: fun, - args, - unwind: UnwindAction::Continue, - destination, - // The presence or absence of a return edge affects control-flow sensitive - // MIR checks and ultimately whether code is accepted or not. We can only - // omit the return edge if a return type is visibly uninhabited to a module - // that makes the call. - target: expr - .ty - .is_inhabited_from( - this.tcx, - this.parent_module, - this.infcx.typing_env(this.param_env), - ) - .then_some(success), - call_source: if from_hir_call { - CallSource::Normal - } else { - CallSource::OverloadedOperator + this.cfg.terminate( + block, + source_info, + TerminatorKind::Call { + func: fun, + args, + unwind: UnwindAction::Continue, + destination, + // The presence or absence of a return edge affects control-flow sensitive + // MIR checks and ultimately whether code is accepted or not. We can only + // omit the return edge if a return type is visibly uninhabited to a module + // that makes the call. + target: expr + .ty + .is_inhabited_from( + this.tcx, + this.parent_module, + this.infcx.typing_env(this.param_env), + ) + .then_some(success), + call_source: if from_hir_call { + CallSource::Normal + } else { + CallSource::OverloadedOperator + }, + fn_span, }, - fn_span, - }); + ); this.diverge_from(block); success.unit() } @@ -303,7 +311,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { hir::Mutability::Not => this.as_read_only_place(block, arg), hir::Mutability::Mut => this.as_place(block, arg), }; - let address_of = Rvalue::RawPtr(mutability, unpack!(block = place)); + let address_of = Rvalue::RawPtr(mutability.into(), unpack!(block = place)); this.cfg.push_assign(block, source_info, destination, address_of); block.unit() } @@ -494,9 +502,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let tmp = this.get_unit_temp(); let target = this.ast_block(tmp, target, block, source_info).into_block(); - this.cfg.terminate(target, source_info, TerminatorKind::Goto { - target: destination_block, - }); + this.cfg.terminate( + target, + source_info, + TerminatorKind::Goto { target: destination_block }, + ); mir::InlineAsmOperand::Label { target_index } } @@ -515,19 +525,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { AsmMacro::NakedAsm => InlineAsmMacro::NakedAsm, }; - this.cfg.terminate(block, source_info, TerminatorKind::InlineAsm { - asm_macro, - template, - operands, - options, - line_spans, - targets: targets.into_boxed_slice(), - unwind: if options.contains(InlineAsmOptions::MAY_UNWIND) { - UnwindAction::Continue - } else { - UnwindAction::Unreachable + this.cfg.terminate( + block, + source_info, + TerminatorKind::InlineAsm { + asm_macro, + template, + operands, + options, + line_spans, + targets: targets.into_boxed_slice(), + unwind: if options.contains(InlineAsmOptions::MAY_UNWIND) { + UnwindAction::Continue + } else { + UnwindAction::Unreachable + }, }, - }); + ); if options.contains(InlineAsmOptions::MAY_UNWIND) { this.diverge_from(block); } @@ -554,7 +568,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::VarRef { .. } | ExprKind::UpvarRef { .. } | ExprKind::PlaceTypeAscription { .. } - | ExprKind::ValueTypeAscription { .. } => { + | ExprKind::ValueTypeAscription { .. } + | ExprKind::PlaceUnwrapUnsafeBinder { .. } + | ExprKind::ValueUnwrapUnsafeBinder { .. } => { debug_assert!(Category::of(&expr.kind) == Some(Category::Place)); let place = unpack!(block = this.as_place(block, expr_id)); @@ -585,12 +601,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.as_operand(block, scope, value, LocalInfo::Boring, NeedsTemporary::No) ); let resume = this.cfg.start_new_block(); - this.cfg.terminate(block, source_info, TerminatorKind::Yield { - value, - resume, - resume_arg: destination, - drop: None, - }); + this.cfg.terminate( + block, + source_info, + TerminatorKind::Yield { value, resume, resume_arg: destination, drop: None }, + ); this.coroutine_drop_cleanup(block); resume.unit() } @@ -613,7 +628,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::ConstParam { .. } | ExprKind::ThreadLocalRef(_) | ExprKind::StaticRef { .. } - | ExprKind::OffsetOf { .. } => { + | ExprKind::OffsetOf { .. } + | ExprKind::WrapUnsafeBinder { .. } => { debug_assert!(match Category::of(&expr.kind).unwrap() { // should be handled above Category::Rvalue(RvalueFunc::Into) => false, diff --git a/compiler/rustc_mir_build/src/builder/expr/stmt.rs b/compiler/rustc_mir_build/src/builder/expr/stmt.rs index 4ae3536d9c24b..58090d3748b46 100644 --- a/compiler/rustc_mir_build/src/builder/expr/stmt.rs +++ b/compiler/rustc_mir_build/src/builder/expr/stmt.rs @@ -123,11 +123,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { unpack!(block = this.break_for_tail_call(block, &args, source_info)); - this.cfg.terminate(block, source_info, TerminatorKind::TailCall { - func: fun, - args, - fn_span, - }); + this.cfg.terminate( + block, + source_info, + TerminatorKind::TailCall { func: fun, args, fn_span }, + ); this.cfg.start_new_block().unit() }) diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index 9d59ffc88ba23..ee331713736e7 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use rustc_middle::mir::*; use rustc_middle::thir::{self, *}; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; @@ -12,11 +14,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// [`PatKind::Leaf`]. /// /// Used internally by [`MatchPairTree::for_pattern`]. - fn field_match_pairs<'pat>( + fn field_match_pairs( &mut self, place: PlaceBuilder<'tcx>, - subpatterns: &'pat [FieldPat<'tcx>], - ) -> Vec> { + subpatterns: &[FieldPat<'tcx>], + ) -> Vec> { subpatterns .iter() .map(|fieldpat| { @@ -31,13 +33,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// array pattern or slice pattern, and adds those trees to `match_pairs`. /// /// Used internally by [`MatchPairTree::for_pattern`]. - fn prefix_slice_suffix<'pat>( + fn prefix_slice_suffix( &mut self, - match_pairs: &mut Vec>, + match_pairs: &mut Vec>, place: &PlaceBuilder<'tcx>, - prefix: &'pat [Box>], - opt_slice: &'pat Option>>, - suffix: &'pat [Box>], + prefix: &[Pat<'tcx>], + opt_slice: &Option>>, + suffix: &[Pat<'tcx>], ) { let tcx = self.tcx; let (min_length, exact_size) = if let Some(place_resolved) = place.try_to_place(self) { @@ -83,14 +85,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } -impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> { +impl<'tcx> MatchPairTree<'tcx> { /// Recursively builds a match pair tree for the given pattern and its /// subpatterns. pub(in crate::builder) fn for_pattern( mut place_builder: PlaceBuilder<'tcx>, - pattern: &'pat Pat<'tcx>, + pattern: &Pat<'tcx>, cx: &mut Builder<'_, 'tcx>, - ) -> MatchPairTree<'pat, 'tcx> { + ) -> MatchPairTree<'tcx> { // Force the place type to the pattern's type. // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks? if let Some(resolved) = place_builder.resolve_upvar(cx) { @@ -125,7 +127,7 @@ impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> { if range.is_full_range(cx.tcx) == Some(true) { default_irrefutable() } else { - TestCase::Range(range) + TestCase::Range(Arc::clone(range)) } } @@ -171,10 +173,13 @@ impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> { let ascription = place.map(|source| { let span = pattern.span; let parent_id = cx.tcx.typeck_root_def_id(cx.def_id.to_def_id()); - let args = ty::InlineConstArgs::new(cx.tcx, ty::InlineConstArgsParts { - parent_args: ty::GenericArgs::identity_for_item(cx.tcx, parent_id), - ty: cx.infcx.next_ty_var(span), - }) + let args = ty::InlineConstArgs::new( + cx.tcx, + ty::InlineConstArgsParts { + parent_args: ty::GenericArgs::identity_for_item(cx.tcx, parent_id), + ty: cx.infcx.next_ty_var(span), + }, + ) .args; let user_ty = cx.infcx.canonicalize_user_type_annotation(ty::UserType::new( ty::UserTypeKind::TypeOf(def_id, ty::UserArgs { args, user_self_ty: None }), @@ -255,6 +260,12 @@ impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> { PatKind::Never => TestCase::Never, }; - MatchPairTree { place, test_case, subpairs, pattern } + MatchPairTree { + place, + test_case, + subpairs, + pattern_ty: pattern.ty, + pattern_span: pattern.span, + } } } diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index b944d13fb0d24..ed577f7adeb3d 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -33,6 +33,7 @@ mod util; use std::assert_matches::assert_matches; use std::borrow::Borrow; use std::mem; +use std::sync::Arc; /// Arguments to [`Builder::then_else_break_inner`] that are usually forwarded /// to recursive invocations. @@ -103,11 +104,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { variable_source_info: SourceInfo, declare_let_bindings: DeclareLetBindings, ) -> BlockAnd<()> { - self.then_else_break_inner(block, expr_id, ThenElseArgs { - temp_scope_override, - variable_source_info, - declare_let_bindings, - }) + self.then_else_break_inner( + block, + expr_id, + ThenElseArgs { temp_scope_override, variable_source_info, declare_let_bindings }, + ) } fn then_else_break_inner( @@ -133,16 +134,24 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let local_scope = this.local_scope(); let (lhs_success_block, failure_block) = this.in_if_then_scope(local_scope, expr_span, |this| { - this.then_else_break_inner(block, lhs, ThenElseArgs { - declare_let_bindings: DeclareLetBindings::LetNotPermitted, - ..args - }) + this.then_else_break_inner( + block, + lhs, + ThenElseArgs { + declare_let_bindings: DeclareLetBindings::LetNotPermitted, + ..args + }, + ) }); let rhs_success_block = this - .then_else_break_inner(failure_block, rhs, ThenElseArgs { - declare_let_bindings: DeclareLetBindings::LetNotPermitted, - ..args - }) + .then_else_break_inner( + failure_block, + rhs, + ThenElseArgs { + declare_let_bindings: DeclareLetBindings::LetNotPermitted, + ..args + }, + ) .into_block(); // Make the LHS and RHS success arms converge to a common block. @@ -169,10 +178,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if this.tcx.sess.instrument_coverage() { this.cfg.push_coverage_span_marker(block, this.source_info(expr_span)); } - this.then_else_break_inner(block, arg, ThenElseArgs { - declare_let_bindings: DeclareLetBindings::LetNotPermitted, - ..args - }) + this.then_else_break_inner( + block, + arg, + ThenElseArgs { + declare_let_bindings: DeclareLetBindings::LetNotPermitted, + ..args + }, + ) }); this.break_for_else(success_block, args.variable_source_info); failure_block.unit() @@ -361,11 +374,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let scrutinee_place = unpack!(block = self.lower_scrutinee(block, scrutinee_id, scrutinee_span)); - let arms = arms.iter().map(|arm| &self.thir[*arm]); let match_start_span = span.shrink_to_lo().to(scrutinee_span); let patterns = arms - .clone() - .map(|arm| { + .iter() + .map(|&arm| { + let arm = &self.thir[arm]; let has_match_guard = if arm.guard.is_some() { HasMatchGuard::Yes } else { HasMatchGuard::No }; (&*arm.pattern, has_match_guard) @@ -412,20 +425,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// (by [Builder::lower_match_tree]). /// /// `outer_source_info` is the SourceInfo for the whole match. - fn lower_match_arms<'pat>( + fn lower_match_arms( &mut self, destination: Place<'tcx>, scrutinee_place_builder: PlaceBuilder<'tcx>, scrutinee_span: Span, - arms: impl IntoIterator>, + arms: &[ArmId], built_match_tree: BuiltMatchTree<'tcx>, outer_source_info: SourceInfo, - ) -> BlockAnd<()> - where - 'tcx: 'pat, - { + ) -> BlockAnd<()> { let arm_end_blocks: Vec = arms - .into_iter() + .iter() + .map(|&arm| &self.thir[arm]) .zip(built_match_tree.branches) .map(|(arm, branch)| { debug!("lowering arm {:?}\ncorresponding branch = {:?}", arm, branch); @@ -604,19 +615,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Optimize the case of `let x: T = ...` to write directly // into `x` and then require that `T == typeof(x)`. PatKind::AscribeUserType { - subpattern: - box Pat { - kind: - PatKind::Binding { - mode: BindingMode(ByRef::No, _), - var, - subpattern: None, - .. - }, - .. - }, + ref subpattern, ascription: thir::Ascription { ref annotation, variance: _ }, - } => { + } if let PatKind::Binding { + mode: BindingMode(ByRef::No, _), + var, + subpattern: None, + .. + } = subpattern.kind => + { let place = self.storage_live_binding( block, var, @@ -634,27 +641,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let ty_source_info = self.source_info(annotation.span); let base = self.canonical_user_type_annotations.push(annotation.clone()); - self.cfg.push(block, Statement { - source_info: ty_source_info, - kind: StatementKind::AscribeUserType( - Box::new((place, UserTypeProjection { base, projs: Vec::new() })), - // We always use invariant as the variance here. This is because the - // variance field from the ascription refers to the variance to use - // when applying the type to the value being matched, but this - // ascription applies rather to the type of the binding. e.g., in this - // example: - // - // ``` - // let x: T = - // ``` - // - // We are creating an ascription that defines the type of `x` to be - // exactly `T` (i.e., with invariance). The variance field, in - // contrast, is intended to be used to relate `T` to the type of - // ``. - ty::Invariant, - ), - }); + self.cfg.push( + block, + Statement { + source_info: ty_source_info, + kind: StatementKind::AscribeUserType( + Box::new((place, UserTypeProjection { base, projs: Vec::new() })), + // We always use invariant as the variance here. This is because the + // variance field from the ascription refers to the variance to use + // when applying the type to the value being matched, but this + // ascription applies rather to the type of the binding. e.g., in this + // example: + // + // ``` + // let x: T = + // ``` + // + // We are creating an ascription that defines the type of `x` to be + // exactly `T` (i.e., with invariance). The variance field, in + // contrast, is intended to be used to relate `T` to the type of + // ``. + ty::Invariant, + ), + }, + ); self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard); block.unit() @@ -989,23 +999,19 @@ impl<'tcx> PatternExtraData<'tcx> { /// /// Will typically be incorporated into a [`Candidate`]. #[derive(Debug, Clone)] -struct FlatPat<'pat, 'tcx> { +struct FlatPat<'tcx> { /// To match the pattern, all of these must be satisfied... // Invariant: all the match pairs are recursively simplified. // Invariant: or-patterns must be sorted to the end. - match_pairs: Vec>, + match_pairs: Vec>, extra_data: PatternExtraData<'tcx>, } -impl<'tcx, 'pat> FlatPat<'pat, 'tcx> { +impl<'tcx> FlatPat<'tcx> { /// Creates a `FlatPat` containing a simplified [`MatchPairTree`] list/forest /// for the given pattern. - fn new( - place: PlaceBuilder<'tcx>, - pattern: &'pat Pat<'tcx>, - cx: &mut Builder<'_, 'tcx>, - ) -> Self { + fn new(place: PlaceBuilder<'tcx>, pattern: &Pat<'tcx>, cx: &mut Builder<'_, 'tcx>) -> Self { // First, recursively build a tree of match pairs for the given pattern. let mut match_pairs = vec![MatchPairTree::for_pattern(place, pattern, cx)]; let mut extra_data = PatternExtraData { @@ -1033,7 +1039,7 @@ impl<'tcx, 'pat> FlatPat<'pat, 'tcx> { /// of candidates, where each "leaf" candidate represents one of the ways for /// the arm pattern to successfully match. #[derive(Debug)] -struct Candidate<'pat, 'tcx> { +struct Candidate<'tcx> { /// For the candidate to match, all of these must be satisfied... /// /// --- @@ -1055,7 +1061,7 @@ struct Candidate<'pat, 'tcx> { /// Invariants: /// - All [`TestCase::Irrefutable`] patterns have been removed by simplification. /// - All or-patterns ([`TestCase::Or`]) have been sorted to the end. - match_pairs: Vec>, + match_pairs: Vec>, /// ...and if this is non-empty, one of these subcandidates also has to match... /// @@ -1072,7 +1078,7 @@ struct Candidate<'pat, 'tcx> { /// Invariant: at the end of match tree lowering, this must not contain an /// `is_never` candidate, because that would break binding consistency. /// - See [`Builder::remove_never_subcandidates`]. - subcandidates: Vec>, + subcandidates: Vec>, /// ...and if there is a guard it must be evaluated; if it's `false` then branch to `otherwise_block`. /// @@ -1107,10 +1113,10 @@ struct Candidate<'pat, 'tcx> { false_edge_start_block: Option, } -impl<'tcx, 'pat> Candidate<'pat, 'tcx> { +impl<'tcx> Candidate<'tcx> { fn new( place: PlaceBuilder<'tcx>, - pattern: &'pat Pat<'tcx>, + pattern: &Pat<'tcx>, has_guard: HasMatchGuard, cx: &mut Builder<'_, 'tcx>, ) -> Self { @@ -1123,7 +1129,7 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> { } /// Incorporates an already-simplified [`FlatPat`] into a new candidate. - fn from_flat_pat(flat_pat: FlatPat<'pat, 'tcx>, has_guard: bool) -> Self { + fn from_flat_pat(flat_pat: FlatPat<'tcx>, has_guard: bool) -> Self { Candidate { match_pairs: flat_pat.match_pairs, extra_data: flat_pat.extra_data, @@ -1172,7 +1178,7 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> { /// reference or by value, and to allow a mutable "context" to be shared by the /// traversal callbacks. Most traversals can use the simpler /// [`Candidate::visit_leaves`] wrapper instead. -fn traverse_candidate<'pat, 'tcx: 'pat, C, T, I>( +fn traverse_candidate<'tcx, C, T, I>( candidate: C, context: &mut T, // Called when visiting a "leaf" candidate (with no subcandidates). @@ -1184,7 +1190,7 @@ fn traverse_candidate<'pat, 'tcx: 'pat, C, T, I>( // Called after visiting a "node" candidate's children. complete_children: impl Copy + Fn(&mut T), ) where - C: Borrow>, // Typically `Candidate` or `&mut Candidate` + C: Borrow>, // Typically `Candidate` or `&mut Candidate` I: Iterator, { if candidate.borrow().subcandidates.is_empty() { @@ -1234,20 +1240,20 @@ struct Ascription<'tcx> { /// participate in or-pattern expansion, where they are transformed into subcandidates. /// - See [`Builder::expand_and_match_or_candidates`]. #[derive(Debug, Clone)] -enum TestCase<'pat, 'tcx> { +enum TestCase<'tcx> { Irrefutable { binding: Option>, ascription: Option> }, Variant { adt_def: ty::AdtDef<'tcx>, variant_index: VariantIdx }, Constant { value: mir::Const<'tcx> }, - Range(&'pat PatRange<'tcx>), + Range(Arc>), Slice { len: usize, variable_length: bool }, Deref { temp: Place<'tcx>, mutability: Mutability }, Never, - Or { pats: Box<[FlatPat<'pat, 'tcx>]> }, + Or { pats: Box<[FlatPat<'tcx>]> }, } -impl<'pat, 'tcx> TestCase<'pat, 'tcx> { - fn as_range(&self) -> Option<&'pat PatRange<'tcx>> { - if let Self::Range(v) = self { Some(*v) } else { None } +impl<'tcx> TestCase<'tcx> { + fn as_range(&self) -> Option<&PatRange<'tcx>> { + if let Self::Range(v) = self { Some(v.as_ref()) } else { None } } } @@ -1257,7 +1263,7 @@ impl<'pat, 'tcx> TestCase<'pat, 'tcx> { /// Each node also has a list of subpairs (possibly empty) that must also match, /// and a reference to the THIR pattern it represents. #[derive(Debug, Clone)] -pub(crate) struct MatchPairTree<'pat, 'tcx> { +pub(crate) struct MatchPairTree<'tcx> { /// This place... /// /// --- @@ -1272,7 +1278,7 @@ pub(crate) struct MatchPairTree<'pat, 'tcx> { /// --- /// Invariant: after creation and simplification in [`FlatPat::new`], /// this must not be [`TestCase::Irrefutable`]. - test_case: TestCase<'pat, 'tcx>, + test_case: TestCase<'tcx>, /// ... and these subpairs must match. /// @@ -1283,8 +1289,10 @@ pub(crate) struct MatchPairTree<'pat, 'tcx> { /// that tests its field for the value `3`. subpairs: Vec, - /// The pattern this was created from. - pattern: &'pat Pat<'tcx>, + /// Type field of the pattern this node was created from. + pattern_ty: Ty<'tcx>, + /// Span field of the pattern this node was created from. + pattern_span: Span, } /// See [`Test`] for more. @@ -1320,7 +1328,7 @@ enum TestKind<'tcx> { }, /// Test whether the value falls within an inclusive or exclusive range. - Range(Box>), + Range(Arc>), /// Test that the length of the slice is `== len` or `>= len`. Len { len: u64, op: BinOp }, @@ -1423,7 +1431,7 @@ struct BuiltMatchTree<'tcx> { impl<'tcx> MatchTreeSubBranch<'tcx> { fn from_sub_candidate( - candidate: Candidate<'_, 'tcx>, + candidate: Candidate<'tcx>, parent_data: &Vec>, ) -> Self { debug_assert!(candidate.match_pairs.is_empty()); @@ -1449,12 +1457,12 @@ impl<'tcx> MatchTreeSubBranch<'tcx> { } impl<'tcx> MatchTreeBranch<'tcx> { - fn from_candidate(candidate: Candidate<'_, 'tcx>) -> Self { + fn from_candidate(candidate: Candidate<'tcx>) -> Self { let mut sub_branches = Vec::new(); traverse_candidate( candidate, &mut Vec::new(), - &mut |candidate: Candidate<'_, '_>, parent_data: &mut Vec>| { + &mut |candidate: Candidate<'_>, parent_data: &mut Vec>| { sub_branches.push(MatchTreeSubBranch::from_sub_candidate(candidate, parent_data)); }, |inner_candidate, parent_data| { @@ -1485,23 +1493,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// `refutable` indicates whether the candidate list is refutable (for `if let` and `let else`) /// or not (for `let` and `match`). In the refutable case we return the block to which we branch /// on failure. - fn lower_match_tree<'pat>( + fn lower_match_tree( &mut self, block: BasicBlock, scrutinee_span: Span, scrutinee_place_builder: &PlaceBuilder<'tcx>, match_start_span: Span, - patterns: Vec<(&'pat Pat<'tcx>, HasMatchGuard)>, + patterns: Vec<(&Pat<'tcx>, HasMatchGuard)>, refutable: bool, - ) -> BuiltMatchTree<'tcx> - where - 'tcx: 'pat, - { + ) -> BuiltMatchTree<'tcx> { // Assemble the initial list of candidates. These top-level candidates are 1:1 with the // input patterns, but other parts of match lowering also introduce subcandidates (for // sub-or-patterns). So inside the algorithm, the candidates list may not correspond to // match arms directly. - let mut candidates: Vec> = patterns + let mut candidates: Vec> = patterns .into_iter() .map(|(pat, has_guard)| { Candidate::new(scrutinee_place_builder.clone(), pat, has_guard, self) @@ -1664,7 +1669,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span: Span, scrutinee_span: Span, start_block: BasicBlock, - candidates: &mut [&mut Candidate<'_, 'tcx>], + candidates: &mut [&mut Candidate<'tcx>], ) -> BasicBlock { ensure_sufficient_stack(|| { self.match_candidates_inner(span, scrutinee_span, start_block, candidates) @@ -1678,7 +1683,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span: Span, scrutinee_span: Span, mut start_block: BasicBlock, - candidates: &mut [&mut Candidate<'_, 'tcx>], + candidates: &mut [&mut Candidate<'tcx>], ) -> BasicBlock { if let [first, ..] = candidates { if first.false_edge_start_block.is_none() { @@ -1747,7 +1752,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// [otherwise block]: Candidate::otherwise_block fn select_matched_candidate( &mut self, - candidate: &mut Candidate<'_, 'tcx>, + candidate: &mut Candidate<'tcx>, start_block: BasicBlock, ) -> BasicBlock { assert!(candidate.otherwise_block.is_none()); @@ -1765,13 +1770,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Takes a list of candidates such that some of the candidates' first match pairs are /// or-patterns. This expands as many or-patterns as possible and processes the resulting /// candidates. Returns the unprocessed candidates if any. - fn expand_and_match_or_candidates<'pat, 'b, 'c>( + fn expand_and_match_or_candidates<'b, 'c>( &mut self, span: Span, scrutinee_span: Span, start_block: BasicBlock, - candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>], - ) -> BlockAnd<&'b mut [&'c mut Candidate<'pat, 'tcx>]> { + candidates: &'b mut [&'c mut Candidate<'tcx>], + ) -> BlockAnd<&'b mut [&'c mut Candidate<'tcx>]> { // We can't expand or-patterns freely. The rule is: // - If a candidate doesn't start with an or-pattern, we include it in // the expansion list as-is (i.e. it "expands" to itself). @@ -1865,14 +1870,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Given a match-pair that corresponds to an or-pattern, expand each subpattern into a new /// subcandidate. Any candidate that has been expanded this way should also be postprocessed /// at the end of [`Self::expand_and_match_or_candidates`]. - fn create_or_subcandidates<'pat>( + fn create_or_subcandidates( &mut self, - candidate: &mut Candidate<'pat, 'tcx>, - match_pair: MatchPairTree<'pat, 'tcx>, + candidate: &mut Candidate<'tcx>, + match_pair: MatchPairTree<'tcx>, ) { let TestCase::Or { pats } = match_pair.test_case else { bug!() }; debug!("expanding or-pattern: candidate={:#?}\npats={:#?}", candidate, pats); - candidate.or_span = Some(match_pair.pattern.span); + candidate.or_span = Some(match_pair.pattern_span); candidate.subcandidates = pats .into_vec() .into_iter() @@ -1938,7 +1943,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// Note that this takes place _after_ the subcandidates have participated /// in match tree lowering. - fn merge_trivial_subcandidates(&mut self, candidate: &mut Candidate<'_, 'tcx>) { + fn merge_trivial_subcandidates(&mut self, candidate: &mut Candidate<'tcx>) { assert!(!candidate.subcandidates.is_empty()); if candidate.has_guard { // FIXME(or_patterns; matthewjasper) Don't give up if we have a guard. @@ -1981,11 +1986,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Never subcandidates may have a set of bindings inconsistent with their siblings, /// which would break later code. So we filter them out. Note that we can't filter out /// top-level candidates this way. - fn remove_never_subcandidates(&mut self, candidate: &mut Candidate<'_, 'tcx>) { + fn remove_never_subcandidates(&mut self, candidate: &mut Candidate<'tcx>) { if candidate.subcandidates.is_empty() { return; } + let false_edge_start_block = candidate.subcandidates[0].false_edge_start_block; candidate.subcandidates.retain_mut(|candidate| { if candidate.extra_data.is_never { candidate.visit_leaves(|subcandidate| { @@ -2000,8 +2006,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } }); if candidate.subcandidates.is_empty() { - // If `candidate` has become a leaf candidate, ensure it has a `pre_binding_block`. - candidate.pre_binding_block = Some(self.cfg.start_new_block()); + // If `candidate` has become a leaf candidate, ensure it has a `pre_binding_block` and `otherwise_block`. + let next_block = self.cfg.start_new_block(); + candidate.pre_binding_block = Some(next_block); + candidate.otherwise_block = Some(next_block); + // In addition, if `candidate` doesn't have `false_edge_start_block`, it should be assigned here. + if candidate.false_edge_start_block.is_none() { + candidate.false_edge_start_block = false_edge_start_block; + } } } @@ -2013,7 +2025,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, span: Span, scrutinee_span: Span, - candidate: &mut Candidate<'_, 'tcx>, + candidate: &mut Candidate<'tcx>, ) { if candidate.match_pairs.is_empty() { return; @@ -2079,7 +2091,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// [`Switch`]: TestKind::Switch /// [`SwitchInt`]: TestKind::SwitchInt /// [`Range`]: TestKind::Range - fn pick_test(&mut self, candidates: &[&mut Candidate<'_, 'tcx>]) -> (Place<'tcx>, Test<'tcx>) { + fn pick_test(&mut self, candidates: &[&mut Candidate<'tcx>]) -> (Place<'tcx>, Test<'tcx>) { // Extract the match-pair from the highest priority candidate let match_pair = &candidates[0].match_pairs[0]; let test = self.pick_test_for_match_pair(match_pair); @@ -2130,18 +2142,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// The sorted candidates are mutated to remove entailed match pairs: /// - candidate 0 becomes `[z @ true]` since we know that `x` was `true`; /// - candidate 1 becomes `[y @ false]` since we know that `x` was `false`. - fn sort_candidates<'b, 'c, 'pat>( + fn sort_candidates<'b, 'c>( &mut self, match_place: Place<'tcx>, test: &Test<'tcx>, - mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>], + mut candidates: &'b mut [&'c mut Candidate<'tcx>], ) -> ( - &'b mut [&'c mut Candidate<'pat, 'tcx>], - FxIndexMap, Vec<&'b mut Candidate<'pat, 'tcx>>>, + &'b mut [&'c mut Candidate<'tcx>], + FxIndexMap, Vec<&'b mut Candidate<'tcx>>>, ) { // For each of the possible outcomes, collect vector of candidates that apply if the test // has that particular outcome. - let mut target_candidates: FxIndexMap<_, Vec<&mut Candidate<'_, '_>>> = Default::default(); + let mut target_candidates: FxIndexMap<_, Vec<&mut Candidate<'_>>> = Default::default(); let total_candidate_count = candidates.len(); @@ -2267,13 +2279,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// ``` /// /// We return the unprocessed candidates. - fn test_candidates<'pat, 'b, 'c>( + fn test_candidates<'b, 'c>( &mut self, span: Span, scrutinee_span: Span, - candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>], + candidates: &'b mut [&'c mut Candidate<'tcx>], start_block: BasicBlock, - ) -> BlockAnd<&'b mut [&'c mut Candidate<'pat, 'tcx>]> { + ) -> BlockAnd<&'b mut [&'c mut Candidate<'tcx>]> { // Choose a match pair from the first candidate, and use it to determine a // test to perform that will confirm or refute that match pair. let (match_place, test) = self.pick_test(candidates); @@ -2552,13 +2564,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let source_info = self.source_info(ascription.annotation.span); let base = self.canonical_user_type_annotations.push(ascription.annotation); - self.cfg.push(block, Statement { - source_info, - kind: StatementKind::AscribeUserType( - Box::new((ascription.source, UserTypeProjection { base, projs: Vec::new() })), - ascription.variance, - ), - }); + self.cfg.push( + block, + Statement { + source_info, + kind: StatementKind::AscribeUserType( + Box::new(( + ascription.source, + UserTypeProjection { base, projs: Vec::new() }, + )), + ascription.variance, + ), + }, + ); } } diff --git a/compiler/rustc_mir_build/src/builder/matches/simplify.rs b/compiler/rustc_mir_build/src/builder/matches/simplify.rs index ebaed1e431bbc..15c860151dc9e 100644 --- a/compiler/rustc_mir_build/src/builder/matches/simplify.rs +++ b/compiler/rustc_mir_build/src/builder/matches/simplify.rs @@ -23,9 +23,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Simplify a list of match pairs so they all require a test. Stores relevant bindings and /// ascriptions in `extra_data`. #[instrument(skip(self), level = "debug")] - pub(super) fn simplify_match_pairs<'pat>( + pub(super) fn simplify_match_pairs( &mut self, - match_pairs: &mut Vec>, + match_pairs: &mut Vec>, extra_data: &mut PatternExtraData<'tcx>, ) { // In order to please the borrow checker, in a pattern like `x @ pat` we must lower the diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index 0d36b7bb3ee7b..a7a028bff97f7 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -6,6 +6,7 @@ // the candidates based on the result. use std::cmp::Ordering; +use std::sync::Arc; use rustc_data_structures::fx::FxIndexMap; use rustc_hir::{LangItem, RangeEnd}; @@ -26,20 +27,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Identifies what test is needed to decide if `match_pair` is applicable. /// /// It is a bug to call this with a not-fully-simplified pattern. - pub(super) fn pick_test_for_match_pair<'pat>( + pub(super) fn pick_test_for_match_pair( &mut self, - match_pair: &MatchPairTree<'pat, 'tcx>, + match_pair: &MatchPairTree<'tcx>, ) -> Test<'tcx> { let kind = match match_pair.test_case { TestCase::Variant { adt_def, variant_index: _ } => TestKind::Switch { adt_def }, - TestCase::Constant { .. } if match_pair.pattern.ty.is_bool() => TestKind::If, - TestCase::Constant { .. } if is_switch_ty(match_pair.pattern.ty) => TestKind::SwitchInt, - TestCase::Constant { value } => TestKind::Eq { value, ty: match_pair.pattern.ty }, + TestCase::Constant { .. } if match_pair.pattern_ty.is_bool() => TestKind::If, + TestCase::Constant { .. } if is_switch_ty(match_pair.pattern_ty) => TestKind::SwitchInt, + TestCase::Constant { value } => TestKind::Eq { value, ty: match_pair.pattern_ty }, - TestCase::Range(range) => { - assert_eq!(range.ty, match_pair.pattern.ty); - TestKind::Range(Box::new(range.clone())) + TestCase::Range(ref range) => { + assert_eq!(range.ty, match_pair.pattern_ty); + TestKind::Range(Arc::clone(range)) } TestCase::Slice { len, variable_length } => { @@ -56,13 +57,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { TestCase::Or { .. } => bug!("or-patterns should have already been handled"), TestCase::Irrefutable { .. } => span_bug!( - match_pair.pattern.span, + match_pair.pattern_span, "simplifiable pattern found: {:?}", - match_pair.pattern + match_pair.pattern_span ), }; - Test { span: match_pair.pattern.span, kind } + Test { span: match_pair.pattern_span, kind } } #[instrument(skip(self, target_blocks, place), level = "debug")] @@ -141,43 +142,49 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.terminate(block, self.source_info(match_start_span), terminator); } - TestKind::Eq { value, ty } => { + TestKind::Eq { value, mut ty } => { let tcx = self.tcx; let success_block = target_block(TestBranch::Success); let fail_block = target_block(TestBranch::Failure); - if let ty::Adt(def, _) = ty.kind() - && tcx.is_lang_item(def.did(), LangItem::String) - { - if !tcx.features().string_deref_patterns() { - span_bug!( + + let expect_ty = value.ty(); + let expect = self.literal_operand(test.span, value); + + let mut place = place; + let mut block = block; + match ty.kind() { + ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::String) => { + if !tcx.features().string_deref_patterns() { + span_bug!( + test.span, + "matching on `String` went through without enabling string_deref_patterns" + ); + } + let re_erased = tcx.lifetimes.re_erased; + let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_); + let ref_str = self.temp(ref_str_ty, test.span); + let eq_block = self.cfg.start_new_block(); + // `let ref_str: &str = ::deref(&place);` + self.call_deref( + block, + eq_block, + place, + Mutability::Not, + ty, + ref_str, test.span, - "matching on `String` went through without enabling string_deref_patterns" ); + // Since we generated a `ref_str = ::deref(&place) -> eq_block` terminator, + // we need to add all further statements to `eq_block`. + // Similarly, the normal test code should be generated for the `&str`, instead of the `String`. + block = eq_block; + place = ref_str; + ty = ref_str_ty; } - let re_erased = tcx.lifetimes.re_erased; - let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_); - let ref_str = self.temp(ref_str_ty, test.span); - let eq_block = self.cfg.start_new_block(); - // `let ref_str: &str = ::deref(&place);` - self.call_deref( - block, - eq_block, - place, - Mutability::Not, - ty, - ref_str, - test.span, - ); - self.non_scalar_compare( - eq_block, - success_block, - fail_block, - source_info, - value, - ref_str, - ref_str_ty, - ); - } else if !ty.is_scalar() { + _ => {} + } + + if !ty.is_scalar() { // Use `PartialEq::eq` instead of `BinOp::Eq` // (the binop can only handle primitives) self.non_scalar_compare( @@ -185,14 +192,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { success_block, fail_block, source_info, - value, - place, + expect, + expect_ty, + Operand::Copy(place), ty, ); } else { - assert_eq!(value.ty(), ty); - let expect = self.literal_operand(test.span, value); - let val = Operand::Copy(place); + assert_eq!(expect_ty, ty); self.compare( block, success_block, @@ -200,7 +206,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { source_info, BinOp::Eq, expect, - val, + Operand::Copy(place), ); } } @@ -243,8 +249,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } TestKind::Len { len, op } => { + let usize_ty = self.tcx.types.usize; + let actual = self.temp(usize_ty, test.span); + // actual = len(place) - let actual = self.len_of_slice_or_array(block, place, test.span, source_info); + self.cfg.push_assign(block, source_info, actual, Rvalue::Len(place)); // expected = let expected = self.push_usize(block, source_info, len); @@ -259,7 +268,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fail_block, source_info, op, - actual, + Operand::Move(actual), Operand::Move(expected), ); } @@ -318,15 +327,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); // `let temp = ::deref(ref_src);` // or `let temp = ::deref_mut(ref_src);` - self.cfg.terminate(block, source_info, TerminatorKind::Call { - func: Operand::Constant(Box::new(ConstOperand { span, user_ty: None, const_: method })), - args: [Spanned { node: Operand::Move(ref_src), span }].into(), - destination: temp, - target: Some(target_block), - unwind: UnwindAction::Continue, - call_source: CallSource::Misc, - fn_span: source_info.span, - }); + self.cfg.terminate( + block, + source_info, + TerminatorKind::Call { + func: Operand::Constant(Box::new(ConstOperand { + span, + user_ty: None, + const_: method, + })), + args: [Spanned { node: Operand::Move(ref_src), span }].into(), + destination: temp, + target: Some(target_block), + unwind: UnwindAction::Continue, + call_source: CallSource::Misc, + fn_span: source_info.span, + }, + ); } /// Compare using the provided built-in comparison operator @@ -368,12 +385,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { success_block: BasicBlock, fail_block: BasicBlock, source_info: SourceInfo, - value: Const<'tcx>, - mut val: Place<'tcx>, + mut expect: Operand<'tcx>, + expect_ty: Ty<'tcx>, + mut val: Operand<'tcx>, mut ty: Ty<'tcx>, ) { - let mut expect = self.literal_operand(source_info.span, value); - // If we're using `b"..."` as a pattern, we need to insert an // unsizing coercion, as the byte string has the type `&[u8; N]`. // @@ -388,7 +404,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { _ => None, }; let opt_ref_ty = unsize(ty); - let opt_ref_test_ty = unsize(value.ty()); + let opt_ref_test_ty = unsize(expect_ty); match (opt_ref_ty, opt_ref_test_ty) { // nothing to do, neither is an array (None, None) => {} @@ -407,11 +423,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { PointerCoercion::Unsize, CoercionSource::Implicit, ), - Operand::Copy(val), + val, ty, ), ); - val = temp; + val = Operand::Copy(temp); } if opt_ref_test_ty.is_some() { let slice = self.temp(ty, source_info.span); @@ -455,29 +471,33 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let bool_ty = self.tcx.types.bool; let eq_result = self.temp(bool_ty, source_info.span); let eq_block = self.cfg.start_new_block(); - self.cfg.terminate(block, source_info, TerminatorKind::Call { - func: Operand::Constant(Box::new(ConstOperand { - span: source_info.span, - - // FIXME(#54571): This constant comes from user input (a - // constant in a pattern). Are there forms where users can add - // type annotations here? For example, an associated constant? - // Need to experiment. - user_ty: None, - - const_: method, - })), - args: [Spanned { node: Operand::Copy(val), span: DUMMY_SP }, Spanned { - node: expect, - span: DUMMY_SP, - }] - .into(), - destination: eq_result, - target: Some(eq_block), - unwind: UnwindAction::Continue, - call_source: CallSource::MatchCmp, - fn_span: source_info.span, - }); + self.cfg.terminate( + block, + source_info, + TerminatorKind::Call { + func: Operand::Constant(Box::new(ConstOperand { + span: source_info.span, + + // FIXME(#54571): This constant comes from user input (a + // constant in a pattern). Are there forms where users can add + // type annotations here? For example, an associated constant? + // Need to experiment. + user_ty: None, + + const_: method, + })), + args: [ + Spanned { node: val, span: DUMMY_SP }, + Spanned { node: expect, span: DUMMY_SP }, + ] + .into(), + destination: eq_result, + target: Some(eq_block), + unwind: UnwindAction::Continue, + call_source: CallSource::MatchCmp, + fn_span: source_info.span, + }, + ); self.diverge_from(block); // check the result @@ -517,8 +537,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, test_place: Place<'tcx>, test: &Test<'tcx>, - candidate: &mut Candidate<'_, 'tcx>, - sorted_candidates: &FxIndexMap, Vec<&mut Candidate<'_, 'tcx>>>, + candidate: &mut Candidate<'tcx>, + sorted_candidates: &FxIndexMap, Vec<&mut Candidate<'tcx>>>, ) -> Option> { // Find the match_pair for this place (if any). At present, // afaik, there can be at most one. (In the future, if we @@ -554,14 +574,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // FIXME(#29623) we could use PatKind::Range to rule // things out here, in some cases. (TestKind::SwitchInt, &TestCase::Constant { value }) - if is_switch_ty(match_pair.pattern.ty) => + if is_switch_ty(match_pair.pattern_ty) => { // An important invariant of candidate sorting is that a candidate // must not match in multiple branches. For `SwitchInt` tests, adding // a new value might invalidate that property for range patterns that // have already been sorted into the failure arm, so we must take care // not to add such values here. - let is_covering_range = |test_case: &TestCase<'_, 'tcx>| { + let is_covering_range = |test_case: &TestCase<'tcx>| { test_case.as_range().is_some_and(|range| { matches!( range.contains(value, self.tcx, self.typing_env()), @@ -569,7 +589,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) }) }; - let is_conflicting_candidate = |candidate: &&mut Candidate<'_, 'tcx>| { + let is_conflicting_candidate = |candidate: &&mut Candidate<'tcx>| { candidate .match_pairs .iter() @@ -681,8 +701,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - (TestKind::Range(test), &TestCase::Range(pat)) => { - if test.as_ref() == pat { + (TestKind::Range(test), TestCase::Range(pat)) => { + if test == pat { fully_matched = true; Some(TestBranch::Success) } else { diff --git a/compiler/rustc_mir_build/src/builder/matches/util.rs b/compiler/rustc_mir_build/src/builder/matches/util.rs index 1bd399e511b39..589e350a03fc3 100644 --- a/compiler/rustc_mir_build/src/builder/matches/util.rs +++ b/compiler/rustc_mir_build/src/builder/matches/util.rs @@ -20,10 +20,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { source_info: SourceInfo, ) { if imaginary_target != real_target { - self.cfg.terminate(from_block, source_info, TerminatorKind::FalseEdge { - real_target, - imaginary_target, - }); + self.cfg.terminate( + from_block, + source_info, + TerminatorKind::FalseEdge { real_target, imaginary_target }, + ); } else { self.cfg.goto(from_block, source_info, real_target) } @@ -67,7 +68,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// a MIR pass run after borrow checking. pub(super) fn collect_fake_borrows<'tcx>( cx: &mut Builder<'_, 'tcx>, - candidates: &[Candidate<'_, 'tcx>], + candidates: &[Candidate<'tcx>], temp_span: Span, scrutinee_base: PlaceBase, ) -> Vec<(Place<'tcx>, Local, FakeBorrowKind)> { @@ -135,7 +136,7 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { } } - fn visit_candidate(&mut self, candidate: &Candidate<'_, 'tcx>) { + fn visit_candidate(&mut self, candidate: &Candidate<'tcx>) { for binding in &candidate.extra_data.bindings { self.visit_binding(binding); } @@ -144,7 +145,7 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { } } - fn visit_flat_pat(&mut self, flat_pat: &FlatPat<'_, 'tcx>) { + fn visit_flat_pat(&mut self, flat_pat: &FlatPat<'tcx>) { for binding in &flat_pat.extra_data.bindings { self.visit_binding(binding); } @@ -153,7 +154,7 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { } } - fn visit_match_pair(&mut self, match_pair: &MatchPairTree<'_, 'tcx>) { + fn visit_match_pair(&mut self, match_pair: &MatchPairTree<'tcx>) { if let TestCase::Or { pats, .. } = &match_pair.test_case { for flat_pat in pats.iter() { self.visit_flat_pat(flat_pat) diff --git a/compiler/rustc_mir_build/src/builder/misc.rs b/compiler/rustc_mir_build/src/builder/misc.rs index 9ea56a9574fde..6e8e74fd4fc8d 100644 --- a/compiler/rustc_mir_build/src/builder/misc.rs +++ b/compiler/rustc_mir_build/src/builder/misc.rs @@ -45,11 +45,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) -> Place<'tcx> { let usize_ty = self.tcx.types.usize; let temp = self.temp(usize_ty, source_info.span); - self.cfg.push_assign_constant(block, source_info, temp, ConstOperand { - span: source_info.span, - user_ty: None, - const_: Const::from_usize(self.tcx, value), - }); + self.cfg.push_assign_constant( + block, + source_info, + temp, + ConstOperand { + span: source_info.span, + user_ty: None, + const_: Const::from_usize(self.tcx, value), + }, + ); temp } diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 0a60899248acc..e04c70b588377 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -20,13 +20,11 @@ use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_middle::hir::place::PlaceBase as HirPlaceBase; use rustc_middle::middle::region; use rustc_middle::mir::*; -use rustc_middle::query::TyCtxtAt; use rustc_middle::thir::{self, ExprId, LintLevel, LocalVarId, Param, ParamId, PatKind, Thir}; use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt, TypeVisitableExt, TypingMode}; use rustc_middle::{bug, span_bug}; use rustc_span::{Span, Symbol, sym}; -use super::lints; use crate::builder::expr::as_place::PlaceBuilder; use crate::builder::scope::DropKind; @@ -46,10 +44,10 @@ pub(crate) fn closure_saved_names_of_captured_variables<'tcx>( .collect() } -/// Construct the MIR for a given `DefId`. -pub(crate) fn mir_build<'tcx>(tcx: TyCtxtAt<'tcx>, def: LocalDefId) -> Body<'tcx> { - let tcx = tcx.tcx; - tcx.ensure_with_value().thir_abstract_const(def); +/// Create the MIR for a given `DefId`, including unreachable code. Do not call +/// this directly; instead use the cached version via `mir_built`. +pub fn build_mir<'tcx>(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Body<'tcx> { + tcx.ensure_done().thir_abstract_const(def); if let Err(e) = tcx.check_match(def) { return construct_error(tcx, def, e); } @@ -70,7 +68,7 @@ pub(crate) fn mir_build<'tcx>(tcx: TyCtxtAt<'tcx>, def: LocalDefId) -> Body<'tcx // "not all control paths return a value" is reported here. // // maybe move the check to a MIR pass? - tcx.ensure().check_liveness(def); + tcx.ensure_ok().check_liveness(def); // Don't steal here, instead steal in unsafeck. This is so that // pattern inline constants can be evaluated as part of building the @@ -79,8 +77,6 @@ pub(crate) fn mir_build<'tcx>(tcx: TyCtxtAt<'tcx>, def: LocalDefId) -> Body<'tcx } }; - lints::check(tcx, &body); - // The borrow checker will replace all the regions here with its own // inference variables. There's no point having non-erased regions here. // The exception is `body.user_type_annotations`, which is used unmodified @@ -1005,11 +1001,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if let Some(source_scope) = scope { self.source_scope = source_scope; } + if self.tcx.intrinsic(self.def_id).is_some_and(|i| i.must_be_overridden) { let source_info = self.source_info(rustc_span::DUMMY_SP); self.cfg.terminate(block, source_info, TerminatorKind::Unreachable); self.cfg.start_new_block().unit() } else { + // Ensure we don't silently codegen functions with fake bodies. + match self.tcx.hir_node(self.hir_id) { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn { has_body: false, .. }, + .. + }) => { + self.tcx.dcx().span_delayed_bug( + expr_span, + format!("fn item without body has reached MIR building: {:?}", self.def_id), + ); + } + _ => {} + } self.expr_into_dest(Place::return_place(), block, expr_id) } } diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 35c98037827af..d3551ea3a97b4 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -532,12 +532,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { (Some(normal_block), Some(exit_block)) => { let target = self.cfg.start_new_block(); let source_info = self.source_info(span); - self.cfg.terminate(normal_block.into_block(), source_info, TerminatorKind::Goto { - target, - }); - self.cfg.terminate(exit_block.into_block(), source_info, TerminatorKind::Goto { - target, - }); + self.cfg.terminate( + normal_block.into_block(), + source_info, + TerminatorKind::Goto { target }, + ); + self.cfg.terminate( + exit_block.into_block(), + source_info, + TerminatorKind::Goto { target }, + ); target.unit() } } @@ -785,6 +789,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let local = place.as_local().unwrap_or_else(|| bug!("projection in tail call args")); + if !self.local_decls[local].ty.needs_drop(self.tcx, self.typing_env()) { + return None; + } + Some(DropData { source_info, local, kind: DropKind::Value }) } Operand::Constant(_) => None, @@ -795,6 +803,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.scopes.scopes.iter().rev().nth(1).unwrap().region_scope, DUMMY_SP, ); + let typing_env = self.typing_env(); let unwind_drops = &mut self.scopes.unwind_drops; // the innermost scope contains only the destructors for the tail call arguments @@ -805,6 +814,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let source_info = drop_data.source_info; let local = drop_data.local; + if !self.local_decls[local].ty.needs_drop(self.tcx, typing_env) { + continue; + } + match drop_data.kind { DropKind::Value => { // `unwind_to` should drop the value that we're about to @@ -824,30 +837,37 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { unwind_drops.add_entry_point(block, unwind_entry_point); let next = self.cfg.start_new_block(); - self.cfg.terminate(block, source_info, TerminatorKind::Drop { - place: local.into(), - target: next, - unwind: UnwindAction::Continue, - replace: false, - }); + self.cfg.terminate( + block, + source_info, + TerminatorKind::Drop { + place: local.into(), + target: next, + unwind: UnwindAction::Continue, + replace: false, + }, + ); block = next; } DropKind::ForLint => { - self.cfg.push(block, Statement { - source_info, - kind: StatementKind::BackwardIncompatibleDropHint { - place: Box::new(local.into()), - reason: BackwardIncompatibleDropReason::Edition2024, + self.cfg.push( + block, + Statement { + source_info, + kind: StatementKind::BackwardIncompatibleDropHint { + place: Box::new(local.into()), + reason: BackwardIncompatibleDropReason::Edition2024, + }, }, - }); + ); } DropKind::Storage => { // Only temps and vars need their storage dead. assert!(local.index() > self.arg_count); - self.cfg.push(block, Statement { - source_info, - kind: StatementKind::StorageDead(local), - }); + self.cfg.push( + block, + Statement { source_info, kind: StatementKind::StorageDead(local) }, + ); } } } @@ -1131,15 +1151,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Schedule emission of a backwards incompatible drop lint hint. /// Applicable only to temporary values for now. + #[instrument(level = "debug", skip(self))] pub(crate) fn schedule_backwards_incompatible_drop( &mut self, span: Span, region_scope: region::Scope, local: Local, ) { - if !self.local_decls[local].ty.has_significant_drop(self.tcx, self.typing_env()) { - return; - } + // Note that we are *not* gating BIDs here on whether they have significant destructor. + // We need to know all of them so that we can capture potential borrow-checking errors. for scope in self.scopes.scopes.iter_mut().rev() { // Since we are inserting linting MIR statement, we have to invalidate the caches scope.invalidate_cache(); @@ -1341,12 +1361,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let assign_unwind = self.cfg.start_new_cleanup_block(); self.cfg.push_assign(assign_unwind, source_info, place, value.clone()); - self.cfg.terminate(block, source_info, TerminatorKind::Drop { - place, - target: assign, - unwind: UnwindAction::Cleanup(assign_unwind), - replace: true, - }); + self.cfg.terminate( + block, + source_info, + TerminatorKind::Drop { + place, + target: assign, + unwind: UnwindAction::Cleanup(assign_unwind), + replace: true, + }, + ); self.diverge_from(block); assign.unit() @@ -1366,13 +1390,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let source_info = self.source_info(span); let success_block = self.cfg.start_new_block(); - self.cfg.terminate(block, source_info, TerminatorKind::Assert { - cond, - expected, - msg: Box::new(msg), - target: success_block, - unwind: UnwindAction::Continue, - }); + self.cfg.terminate( + block, + source_info, + TerminatorKind::Assert { + cond, + expected, + msg: Box::new(msg), + target: success_block, + unwind: UnwindAction::Continue, + }, + ); self.diverge_from(block); success_block @@ -1472,12 +1500,16 @@ fn build_scope_drops<'tcx>( unwind_drops.add_entry_point(block, unwind_to); let next = cfg.start_new_block(); - cfg.terminate(block, source_info, TerminatorKind::Drop { - place: local.into(), - target: next, - unwind: UnwindAction::Continue, - replace: false, - }); + cfg.terminate( + block, + source_info, + TerminatorKind::Drop { + place: local.into(), + target: next, + unwind: UnwindAction::Continue, + replace: false, + }, + ); block = next; } DropKind::ForLint => { @@ -1500,13 +1532,16 @@ fn build_scope_drops<'tcx>( continue; } - cfg.push(block, Statement { - source_info, - kind: StatementKind::BackwardIncompatibleDropHint { - place: Box::new(local.into()), - reason: BackwardIncompatibleDropReason::Edition2024, + cfg.push( + block, + Statement { + source_info, + kind: StatementKind::BackwardIncompatibleDropHint { + place: Box::new(local.into()), + reason: BackwardIncompatibleDropReason::Edition2024, + }, }, - }); + ); } DropKind::Storage => { // Ordinarily, storage-dead nodes are not emitted on unwind, so we don't diff --git a/compiler/rustc_mir_build/src/check_tail_calls.rs b/compiler/rustc_mir_build/src/check_tail_calls.rs index 0659e3ea314bb..18db8c1debb14 100644 --- a/compiler/rustc_mir_build/src/check_tail_calls.rs +++ b/compiler/rustc_mir_build/src/check_tail_calls.rs @@ -1,4 +1,5 @@ use rustc_abi::ExternAbi; +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::Applicability; use rustc_hir::LangItem; use rustc_hir::def::DefKind; @@ -131,11 +132,24 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> { } { + // `#[track_caller]` affects the ABI of a function (by adding a location argument), + // so a `track_caller` can only tail call other `track_caller` functions. + // + // The issue is however that we can't know if a function is `track_caller` or not at + // this point (THIR can be polymorphic, we may have an unresolved trait function). + // We could only allow functions that we *can* resolve and *are* `track_caller`, + // but that would turn changing `track_caller`-ness into a breaking change, + // which is probably undesirable. + // + // Also note that we don't check callee's `track_caller`-ness at all, mostly for the + // reasons above, but also because we can always tailcall the shim we'd generate for + // coercing the function to an `fn()` pointer. (although in that case the tailcall is + // basically useless -- the shim calls the actual function, so tailcalling the shim is + // equivalent to calling the function) let caller_needs_location = self.needs_location(self.caller_ty); - let callee_needs_location = self.needs_location(ty); - if caller_needs_location != callee_needs_location { - self.report_track_caller_mismatch(expr.span, caller_needs_location); + if caller_needs_location { + self.report_track_caller_caller(expr.span); } } @@ -149,7 +163,9 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> { } /// Returns true if function of type `ty` needs location argument - /// (i.e. if a function is marked as `#[track_caller]`) + /// (i.e. if a function is marked as `#[track_caller]`). + /// + /// Panics if the function's instance can't be immediately resolved. fn needs_location(&self, ty: Ty<'tcx>) -> bool { if let &ty::FnDef(did, substs) = ty.kind() { let instance = @@ -292,25 +308,15 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> { self.found_errors = Err(err); } - fn report_track_caller_mismatch(&mut self, sp: Span, caller_needs_location: bool) { - let err = match caller_needs_location { - true => self - .tcx - .dcx() - .struct_span_err( - sp, - "a function marked with `#[track_caller]` cannot tail-call one that is not", - ) - .emit(), - false => self - .tcx - .dcx() - .struct_span_err( - sp, - "a function mot marked with `#[track_caller]` cannot tail-call one that is", - ) - .emit(), - }; + fn report_track_caller_caller(&mut self, sp: Span) { + let err = self + .tcx + .dcx() + .struct_span_err( + sp, + "a function marked with `#[track_caller]` cannot perform a tail-call", + ) + .emit(); self.found_errors = Err(err); } @@ -344,12 +350,14 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for TailCallCkVisitor<'a, 'tcx> { } fn visit_expr(&mut self, expr: &'a Expr<'tcx>) { - if let ExprKind::Become { value } = expr.kind { - let call = &self.thir[value]; - self.check_tail_call(call, expr); - } + ensure_sufficient_stack(|| { + if let ExprKind::Become { value } = expr.kind { + let call = &self.thir[value]; + self.check_tail_call(call, expr); + } - visit::walk_expr(self, expr); + visit::walk_expr(self, expr); + }); } } diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index f7071eb139f35..1bd33475e6071 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; use std::mem; use std::ops::Bound; +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::DiagArgValue; use rustc_hir::def::DefKind; use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability}; @@ -200,7 +201,7 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { fn visit_inner_body(&mut self, def: LocalDefId) { if let Ok((inner_thir, expr)) = self.tcx.thir_body(def) { // Runs all other queries that depend on THIR. - self.tcx.ensure_with_value().mir_built(def); + self.tcx.ensure_done().mir_built(def); let inner_thir = &inner_thir.steal(); let hir_context = self.tcx.local_def_id_to_hir_id(def); let safety_context = mem::replace(&mut self.safety_context, SafetyContext::Safe); @@ -266,7 +267,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for LayoutConstrainedPlaceVisitor<'a, 'tcx> { // place, i.e. the expression is a place expression and not a dereference // (since dereferencing something leads us to a different place). ExprKind::Deref { .. } => {} - ref kind if ExprCategory::of(kind).map_or(true, |cat| cat == ExprCategory::Place) => { + ref kind if ExprCategory::of(kind).is_none_or(|cat| cat == ExprCategory::Place) => { visit::walk_expr(self, expr); } @@ -439,6 +440,9 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { | ExprKind::NeverToAny { .. } | ExprKind::PlaceTypeAscription { .. } | ExprKind::ValueTypeAscription { .. } + | ExprKind::PlaceUnwrapUnsafeBinder { .. } + | ExprKind::ValueUnwrapUnsafeBinder { .. } + | ExprKind::WrapUnsafeBinder { .. } | ExprKind::PointerCoercion { .. } | ExprKind::Repeat { .. } | ExprKind::StaticRef { .. } @@ -473,28 +477,33 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { ExprKind::Scope { value, lint_level: LintLevel::Explicit(hir_id), region_scope: _ } => { let prev_id = self.hir_context; self.hir_context = hir_id; - self.visit_expr(&self.thir[value]); + ensure_sufficient_stack(|| { + self.visit_expr(&self.thir[value]); + }); self.hir_context = prev_id; return; // don't visit the whole expression } ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => { - if self.thir[fun].ty.fn_sig(self.tcx).safety().is_unsafe() { - let func_id = if let ty::FnDef(func_id, _) = self.thir[fun].ty.kind() { + let fn_ty = self.thir[fun].ty; + let sig = fn_ty.fn_sig(self.tcx); + let (callee_features, safe_target_features): (&[_], _) = match fn_ty.kind() { + ty::FnDef(func_id, ..) => { + let cg_attrs = self.tcx.codegen_fn_attrs(func_id); + (&cg_attrs.target_features, cg_attrs.safe_target_features) + } + _ => (&[], false), + }; + if sig.safety().is_unsafe() && !safe_target_features { + let func_id = if let ty::FnDef(func_id, _) = fn_ty.kind() { Some(*func_id) } else { None }; self.requires_unsafe(expr.span, CallToUnsafeFunction(func_id)); - } else if let &ty::FnDef(func_did, _) = self.thir[fun].ty.kind() { - // If the called function has target features the calling function hasn't, - // the call requires `unsafe`. Don't check this on wasm - // targets, though. For more information on wasm see the - // is_like_wasm check in hir_analysis/src/collect.rs - let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features; - if !self.tcx.sess.target.options.is_like_wasm - && !callee_features.iter().all(|feature| { - self.body_target_features.iter().any(|f| f.name == feature.name) - }) + } else if let &ty::FnDef(func_did, _) = fn_ty.kind() { + if !self + .tcx + .is_target_feature_call_safe(callee_features, self.body_target_features) { let missing: Vec<_> = callee_features .iter() @@ -516,11 +525,10 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { .copied() .filter(|feature| missing.contains(feature)) .collect(); - self.requires_unsafe(expr.span, CallToFunctionWith { - function: func_did, - missing, - build_enabled, - }); + self.requires_unsafe( + expr.span, + CallToFunctionWith { function: func_did, missing, build_enabled }, + ); } } } @@ -546,7 +554,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { _ => self.requires_unsafe(expr.span, UseOfExternStatic), } } - } else if self.thir[arg].ty.is_unsafe_ptr() { + } else if self.thir[arg].ty.is_raw_ptr() { self.requires_unsafe(expr.span, DerefOfRawPointer); } } @@ -677,6 +685,11 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { } } } + ExprKind::PlaceUnwrapUnsafeBinder { .. } + | ExprKind::ValueUnwrapUnsafeBinder { .. } + | ExprKind::WrapUnsafeBinder { .. } => { + self.requires_unsafe(expr.span, UnsafeBinderCast); + } _ => {} } visit::walk_expr(self, expr); @@ -725,6 +738,7 @@ enum UnsafeOpKind { /// (e.g., with `-C target-feature`). build_enabled: Vec, }, + UnsafeBinderCast, } use UnsafeOpKind::*; @@ -739,7 +753,10 @@ impl UnsafeOpKind { ) { let parent_id = tcx.hir().get_parent_item(hir_id); let parent_owner = tcx.hir_owner_node(parent_id); - let should_suggest = parent_owner.fn_sig().is_some_and(|sig| sig.header.is_unsafe()); + let should_suggest = parent_owner.fn_sig().is_some_and(|sig| { + // Do not suggest for safe target_feature functions + matches!(sig.header.safety, hir::HeaderSafety::Normal(hir::Safety::Unsafe)) + }); let unsafe_not_inherited_note = if should_suggest { suggest_unsafe_block.then(|| { let body_span = tcx.hir().body(parent_owner.body_id().unwrap()).value.span; @@ -885,6 +902,15 @@ impl UnsafeOpKind { unsafe_not_inherited_note, }, ), + UnsafeBinderCast => tcx.emit_node_span_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnUnsafeBinderCastRequiresUnsafe { + span, + unsafe_not_inherited_note, + }, + ), } } @@ -902,7 +928,7 @@ impl UnsafeOpKind { { true } else if let Some(sig) = tcx.hir().fn_sig_by_hir_id(*id) - && sig.header.is_unsafe() + && matches!(sig.header.safety, hir::HeaderSafety::Normal(hir::Safety::Unsafe)) { true } else { @@ -1093,6 +1119,15 @@ impl UnsafeOpKind { function: tcx.def_path_str(*function), }); } + UnsafeBinderCast if unsafe_op_in_unsafe_fn_allowed => { + dcx.emit_err(UnsafeBinderCastRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + span, + unsafe_not_inherited_note, + }); + } + UnsafeBinderCast => { + dcx.emit_err(UnsafeBinderCastRequiresUnsafe { span, unsafe_not_inherited_note }); + } } } } @@ -1106,12 +1141,21 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) { let Ok((thir, expr)) = tcx.thir_body(def) else { return }; // Runs all other queries that depend on THIR. - tcx.ensure_with_value().mir_built(def); + tcx.ensure_done().mir_built(def); let thir = &thir.steal(); let hir_id = tcx.local_def_id_to_hir_id(def); let safety_context = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(SafetyContext::Safe, |fn_sig| { - if fn_sig.header.safety.is_unsafe() { SafetyContext::UnsafeFn } else { SafetyContext::Safe } + match fn_sig.header.safety { + // We typeck the body as safe, but otherwise treat it as unsafe everywhere else. + // Call sites to other SafeTargetFeatures functions are checked explicitly and don't need + // to care about safety of the body. + hir::HeaderSafety::SafeTargetFeatures => SafetyContext::Safe, + hir::HeaderSafety::Normal(safety) => match safety { + hir::Safety::Unsafe => SafetyContext::UnsafeFn, + hir::Safety::Safe => SafetyContext::Safe, + }, + } }); let body_target_features = &tcx.body_codegen_attrs(def.to_def_id()).target_features; let mut warnings = Vec::new(); @@ -1141,9 +1185,11 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) { warnings.sort_by_key(|w| w.block_span); for UnusedUnsafeWarning { hir_id, block_span, enclosing_unsafe } in warnings { let block_span = tcx.sess.source_map().guess_head_span(block_span); - tcx.emit_node_span_lint(UNUSED_UNSAFE, hir_id, block_span, UnusedUnsafe { - span: block_span, - enclosing: enclosing_unsafe, - }); + tcx.emit_node_span_lint( + UNUSED_UNSAFE, + hir_id, + block_span, + UnusedUnsafe { span: block_span, enclosing: enclosing_unsafe }, + ); } } diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index be5f8bdffb50b..07bdc59756aa7 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -1,3 +1,4 @@ +use rustc_data_structures::fx::FxIndexMap; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, @@ -7,20 +8,10 @@ use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{self, Ty}; use rustc_pattern_analysis::errors::Uncovered; use rustc_pattern_analysis::rustc::RustcPatCtxt; -use rustc_span::{Span, Symbol}; +use rustc_span::{Ident, Span, Symbol}; use crate::fluent_generated as fluent; -#[derive(LintDiagnostic)] -#[diag(mir_build_unconditional_recursion)] -#[help] -pub(crate) struct UnconditionalRecursion { - #[label] - pub(crate) span: Span, - #[label(mir_build_unconditional_recursion_call_site_label)] - pub(crate) call_sites: Vec, -} - #[derive(LintDiagnostic)] #[diag(mir_build_call_to_deprecated_safe_fn_requires_unsafe)] pub(crate) struct CallToDeprecatedSafeFnRequiresUnsafe { @@ -170,6 +161,18 @@ pub(crate) struct UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe pub(crate) unsafe_not_inherited_note: Option, } +#[derive(LintDiagnostic)] +#[diag( + mir_build_unsafe_binder_cast_requires_unsafe, + code = E0133, +)] +pub(crate) struct UnsafeOpInUnsafeFnUnsafeBinderCastRequiresUnsafe { + #[label] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) unsafe_not_inherited_note: Option, +} + #[derive(LintDiagnostic)] #[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_fn_with_requires_unsafe, code = E0133)] #[help] @@ -504,6 +507,32 @@ pub(crate) struct CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { pub(crate) unsafe_not_inherited_note: Option, } +#[derive(Diagnostic)] +#[diag( + mir_build_unsafe_binder_cast_requires_unsafe, + code = E0133, +)] +pub(crate) struct UnsafeBinderCastRequiresUnsafe { + #[primary_span] + #[label] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) unsafe_not_inherited_note: Option, +} + +#[derive(Diagnostic)] +#[diag( + mir_build_unsafe_binder_cast_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, + code = E0133, +)] +pub(crate) struct UnsafeBinderCastRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) unsafe_not_inherited_note: Option, +} + #[derive(Subdiagnostic)] #[label(mir_build_unsafe_not_inherited)] pub(crate) struct UnsafeNotInheritedNote { @@ -763,7 +792,7 @@ pub(crate) struct BindingsWithVariantName { #[suggestion(code = "{ty_path}::{name}", applicability = "machine-applicable")] pub(crate) suggestion: Option, pub(crate) ty_path: String, - pub(crate) name: Symbol, + pub(crate) name: Ident, } #[derive(LintDiagnostic)] @@ -800,15 +829,15 @@ pub(crate) struct IrrefutableLetPatternsWhileLet { #[derive(Diagnostic)] #[diag(mir_build_borrow_of_moved_value)] -pub(crate) struct BorrowOfMovedValue<'tcx> { +pub(crate) struct BorrowOfMovedValue { #[primary_span] #[label] #[label(mir_build_occurs_because_label)] pub(crate) binding_span: Span, #[label(mir_build_value_borrowed_label)] pub(crate) conflicts_ref: Vec, - pub(crate) name: Symbol, - pub(crate) ty: Ty<'tcx>, + pub(crate) name: Ident, + pub(crate) ty: String, #[suggestion(code = "ref ", applicability = "machine-applicable")] pub(crate) suggest_borrowing: Option, } @@ -1067,62 +1096,72 @@ pub(crate) enum MiscPatternSuggestion { }, } -#[derive(Diagnostic)] -#[diag(mir_build_rustc_box_attribute_error)] -pub(crate) struct RustcBoxAttributeError { - #[primary_span] - pub(crate) span: Span, - #[subdiagnostic] - pub(crate) reason: RustcBoxAttrReason, -} - -#[derive(Subdiagnostic)] -pub(crate) enum RustcBoxAttrReason { - #[note(mir_build_attributes)] - Attributes, - #[note(mir_build_not_box)] - NotBoxNew, - #[note(mir_build_missing_box)] - MissingBox, -} - #[derive(LintDiagnostic)] #[diag(mir_build_rust_2024_incompatible_pat)] -pub(crate) struct Rust2024IncompatiblePat<'a> { +pub(crate) struct Rust2024IncompatiblePat { #[subdiagnostic] - pub(crate) sugg: Rust2024IncompatiblePatSugg<'a>, + pub(crate) sugg: Rust2024IncompatiblePatSugg, + pub(crate) bad_modifiers: bool, + pub(crate) bad_ref_pats: bool, + pub(crate) is_hard_error: bool, } -pub(crate) struct Rust2024IncompatiblePatSugg<'a> { +pub(crate) struct Rust2024IncompatiblePatSugg { + /// If true, our suggestion is to elide explicit binding modifiers. + /// If false, our suggestion is to make the pattern fully explicit. + pub(crate) suggest_eliding_modes: bool, pub(crate) suggestion: Vec<(Span, String)>, pub(crate) ref_pattern_count: usize, pub(crate) binding_mode_count: usize, - /// Labeled spans for subpatterns invalid in Rust 2024. - pub(crate) labels: &'a [(Span, String)], + /// Internal state: the ref-mutability of the default binding mode at the subpattern being + /// lowered, with the span where it was introduced. `None` for a by-value default mode. + pub(crate) default_mode_span: Option<(Span, ty::Mutability)>, + /// Labels for where incompatibility-causing by-ref default binding modes were introduced. + pub(crate) default_mode_labels: FxIndexMap, } -impl<'a> Subdiagnostic for Rust2024IncompatiblePatSugg<'a> { +impl Subdiagnostic for Rust2024IncompatiblePatSugg { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, _f: &F, ) { + // Format and emit explanatory notes about default binding modes. Reversing the spans' order + // means if we have nested spans, the innermost ones will be visited first. + for (span, def_br_mutbl) in self.default_mode_labels.into_iter().rev() { + // Don't point to a macro call site. + if !span.from_expansion() { + let note_msg = "matching on a reference type with a non-reference pattern changes the default binding mode"; + let label_msg = + format!("this matches on type `{}_`", def_br_mutbl.ref_prefix_str()); + let mut label = MultiSpan::from(span); + label.push_span_label(span, label_msg); + diag.span_note(label, note_msg); + } + } + + // Format and emit the suggestion. let applicability = if self.suggestion.iter().all(|(span, _)| span.can_be_used_for_suggestions()) { Applicability::MachineApplicable } else { Applicability::MaybeIncorrect }; - let plural_derefs = pluralize!(self.ref_pattern_count); - let and_modes = if self.binding_mode_count > 0 { - format!(" and variable binding mode{}", pluralize!(self.binding_mode_count)) + let msg = if self.suggest_eliding_modes { + let plural_modes = pluralize!(self.binding_mode_count); + format!("remove the unnecessary binding modifier{plural_modes}") } else { - String::new() + let plural_derefs = pluralize!(self.ref_pattern_count); + let and_modes = if self.binding_mode_count > 0 { + format!(" and variable binding mode{}", pluralize!(self.binding_mode_count)) + } else { + String::new() + }; + format!("make the implied reference pattern{plural_derefs}{and_modes} explicit") }; - diag.multipart_suggestion_verbose( - format!("make the implied reference pattern{plural_derefs}{and_modes} explicit"), - self.suggestion, - applicability, - ); + // FIXME(dianne): for peace of mind, don't risk emitting a 0-part suggestion (that panics!) + if !self.suggestion.is_empty() { + diag.multipart_suggestion_verbose(msg, self.suggestion, applicability); + } } } diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 467725841dcd0..fa5db32d9134c 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -14,12 +14,11 @@ // The `builder` module used to be named `build`, but that was causing GitHub's // "Go to file" feature to silently ignore all files in the module, probably // because it assumes that "build" is a build-output directory. See #134365. -mod builder; +pub mod builder; mod check_tail_calls; mod check_unsafety; mod errors; -pub mod lints; -mod thir; +pub mod thir; use rustc_middle::util::Providers; @@ -28,12 +27,9 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" } pub fn provide(providers: &mut Providers) { providers.check_match = thir::pattern::check_match; providers.lit_to_const = thir::constant::lit_to_const; - providers.hooks.build_mir = builder::mir_build; providers.closure_saved_names_of_captured_variables = builder::closure_saved_names_of_captured_variables; providers.check_unsafety = check_unsafety::check_unsafety; providers.check_tail_calls = check_tail_calls::check_tail_calls; providers.thir_body = thir::cx::thir_body; - providers.hooks.thir_tree = thir::print::thir_tree; - providers.hooks.thir_flat = thir::print::thir_flat; } diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs deleted file mode 100644 index 5cf33868adeee..0000000000000 --- a/compiler/rustc_mir_build/src/lints.rs +++ /dev/null @@ -1,261 +0,0 @@ -use std::ops::ControlFlow; - -use rustc_data_structures::graph::iterate::{ - NodeStatus, TriColorDepthFirstSearch, TriColorVisitor, -}; -use rustc_hir::def::DefKind; -use rustc_middle::mir::{self, BasicBlock, BasicBlocks, Body, Terminator, TerminatorKind}; -use rustc_middle::ty::{self, GenericArg, GenericArgs, Instance, Ty, TyCtxt}; -use rustc_session::lint::builtin::UNCONDITIONAL_RECURSION; -use rustc_span::Span; - -use crate::errors::UnconditionalRecursion; - -pub(crate) fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { - check_call_recursion(tcx, body); -} - -fn check_call_recursion<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { - let def_id = body.source.def_id().expect_local(); - - if let DefKind::Fn | DefKind::AssocFn = tcx.def_kind(def_id) { - // If this is trait/impl method, extract the trait's args. - let trait_args = match tcx.trait_of_item(def_id.to_def_id()) { - Some(trait_def_id) => { - let trait_args_count = tcx.generics_of(trait_def_id).count(); - &GenericArgs::identity_for_item(tcx, def_id)[..trait_args_count] - } - _ => &[], - }; - - check_recursion(tcx, body, CallRecursion { trait_args }) - } -} - -fn check_recursion<'tcx>( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - classifier: impl TerminatorClassifier<'tcx>, -) { - let def_id = body.source.def_id().expect_local(); - - if let DefKind::Fn | DefKind::AssocFn = tcx.def_kind(def_id) { - let mut vis = Search { tcx, body, classifier, reachable_recursive_calls: vec![] }; - if let Some(NonRecursive) = - TriColorDepthFirstSearch::new(&body.basic_blocks).run_from_start(&mut vis) - { - return; - } - if vis.reachable_recursive_calls.is_empty() { - return; - } - - vis.reachable_recursive_calls.sort(); - - let sp = tcx.def_span(def_id); - let hir_id = tcx.local_def_id_to_hir_id(def_id); - tcx.emit_node_span_lint(UNCONDITIONAL_RECURSION, hir_id, sp, UnconditionalRecursion { - span: sp, - call_sites: vis.reachable_recursive_calls, - }); - } -} - -/// Requires drop elaboration to have been performed first. -pub fn check_drop_recursion<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { - let def_id = body.source.def_id().expect_local(); - - // First check if `body` is an `fn drop()` of `Drop` - if let DefKind::AssocFn = tcx.def_kind(def_id) - && let Some(trait_ref) = - tcx.impl_of_method(def_id.to_def_id()).and_then(|def_id| tcx.impl_trait_ref(def_id)) - && let Some(drop_trait) = tcx.lang_items().drop_trait() - && drop_trait == trait_ref.instantiate_identity().def_id - // avoid erroneous `Drop` impls from causing ICEs below - && let sig = tcx.fn_sig(def_id).instantiate_identity() - && sig.inputs().skip_binder().len() == 1 - { - // It was. Now figure out for what type `Drop` is implemented and then - // check for recursion. - if let ty::Ref(_, dropped_ty, _) = - tcx.liberate_late_bound_regions(def_id.to_def_id(), sig.input(0)).kind() - { - check_recursion(tcx, body, RecursiveDrop { drop_for: *dropped_ty }); - } - } -} - -trait TerminatorClassifier<'tcx> { - fn is_recursive_terminator( - &self, - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - terminator: &Terminator<'tcx>, - ) -> bool; -} - -struct NonRecursive; - -struct Search<'mir, 'tcx, C: TerminatorClassifier<'tcx>> { - tcx: TyCtxt<'tcx>, - body: &'mir Body<'tcx>, - classifier: C, - - reachable_recursive_calls: Vec, -} - -struct CallRecursion<'tcx> { - trait_args: &'tcx [GenericArg<'tcx>], -} - -struct RecursiveDrop<'tcx> { - /// The type that `Drop` is implemented for. - drop_for: Ty<'tcx>, -} - -impl<'tcx> TerminatorClassifier<'tcx> for CallRecursion<'tcx> { - /// Returns `true` if `func` refers to the function we are searching in. - fn is_recursive_terminator( - &self, - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - terminator: &Terminator<'tcx>, - ) -> bool { - let TerminatorKind::Call { func, args, .. } = &terminator.kind else { - return false; - }; - - // Resolving function type to a specific instance that is being called is expensive. To - // avoid the cost we check the number of arguments first, which is sufficient to reject - // most of calls as non-recursive. - if args.len() != body.arg_count { - return false; - } - let caller = body.source.def_id(); - let typing_env = body.typing_env(tcx); - - let func_ty = func.ty(body, tcx); - if let ty::FnDef(callee, args) = *func_ty.kind() { - let Ok(normalized_args) = tcx.try_normalize_erasing_regions(typing_env, args) else { - return false; - }; - let (callee, call_args) = if let Ok(Some(instance)) = - Instance::try_resolve(tcx, typing_env, callee, normalized_args) - { - (instance.def_id(), instance.args) - } else { - (callee, normalized_args) - }; - - // FIXME(#57965): Make this work across function boundaries - - // If this is a trait fn, the args on the trait have to match, or we might be - // calling into an entirely different method (for example, a call from the default - // method in the trait to `>::method`, where `A` and/or `B` are - // specific types). - return callee == caller && &call_args[..self.trait_args.len()] == self.trait_args; - } - - false - } -} - -impl<'tcx> TerminatorClassifier<'tcx> for RecursiveDrop<'tcx> { - fn is_recursive_terminator( - &self, - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - terminator: &Terminator<'tcx>, - ) -> bool { - let TerminatorKind::Drop { place, .. } = &terminator.kind else { return false }; - - let dropped_ty = place.ty(body, tcx).ty; - dropped_ty == self.drop_for - } -} - -impl<'mir, 'tcx, C: TerminatorClassifier<'tcx>> TriColorVisitor> - for Search<'mir, 'tcx, C> -{ - type BreakVal = NonRecursive; - - fn node_examined( - &mut self, - bb: BasicBlock, - prior_status: Option, - ) -> ControlFlow { - // Back-edge in the CFG (loop). - if let Some(NodeStatus::Visited) = prior_status { - return ControlFlow::Break(NonRecursive); - } - - match self.body[bb].terminator().kind { - // These terminators return control flow to the caller. - TerminatorKind::UnwindTerminate(_) - | TerminatorKind::CoroutineDrop - | TerminatorKind::UnwindResume - | TerminatorKind::Return - | TerminatorKind::Unreachable - | TerminatorKind::Yield { .. } => ControlFlow::Break(NonRecursive), - - // A InlineAsm without targets (diverging and contains no labels) - // is treated as non-recursing. - TerminatorKind::InlineAsm { ref targets, .. } => { - if !targets.is_empty() { - ControlFlow::Continue(()) - } else { - ControlFlow::Break(NonRecursive) - } - } - - // These do not. - TerminatorKind::Assert { .. } - | TerminatorKind::Call { .. } - | TerminatorKind::Drop { .. } - | TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } - | TerminatorKind::Goto { .. } - | TerminatorKind::SwitchInt { .. } => ControlFlow::Continue(()), - - // Note that tail call terminator technically returns to the caller, - // but for purposes of this lint it makes sense to count it as possibly recursive, - // since it's still a call. - // - // If this'll be repurposed for something else, this might need to be changed. - TerminatorKind::TailCall { .. } => ControlFlow::Continue(()), - } - } - - fn node_settled(&mut self, bb: BasicBlock) -> ControlFlow { - // When we examine a node for the last time, remember it if it is a recursive call. - let terminator = self.body[bb].terminator(); - - // FIXME(explicit_tail_calls): highlight tail calls as "recursive call site" - // - // We don't want to lint functions that recurse only through tail calls - // (such as `fn g() { become () }`), so just adding `| TailCall { ... }` - // here won't work. - // - // But at the same time we would like to highlight both calls in a function like - // `fn f() { if false { become f() } else { f() } }`, so we need to figure something out. - if self.classifier.is_recursive_terminator(self.tcx, self.body, terminator) { - self.reachable_recursive_calls.push(terminator.source_info.span); - } - - ControlFlow::Continue(()) - } - - fn ignore_edge(&mut self, bb: BasicBlock, target: BasicBlock) -> bool { - let terminator = self.body[bb].terminator(); - let ignore_unwind = terminator.unwind() == Some(&mir::UnwindAction::Cleanup(target)) - && terminator.successors().count() > 1; - if ignore_unwind || self.classifier.is_recursive_terminator(self.tcx, self.body, terminator) - { - return true; - } - match &terminator.kind { - TerminatorKind::FalseEdge { imaginary_target, .. } => imaginary_target == &target, - _ => false, - } - } -} diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index ce1c635d1b9f5..b3210813703ca 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -1,7 +1,8 @@ -use rustc_ast as ast; +use rustc_abi::Size; +use rustc_ast::{self as ast}; use rustc_hir::LangItem; use rustc_middle::bug; -use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; +use rustc_middle::mir::interpret::LitToConstInput; use rustc_middle::ty::{self, ScalarInt, TyCtxt, TypeVisitableExt as _}; use tracing::trace; @@ -10,26 +11,25 @@ use crate::builder::parse_float_into_scalar; pub(crate) fn lit_to_const<'tcx>( tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx>, -) -> Result, LitToConstError> { +) -> ty::Const<'tcx> { let LitToConstInput { lit, ty, neg } = lit_input; if let Err(guar) = ty.error_reported() { - return Ok(ty::Const::new_error(tcx, guar)); + return ty::Const::new_error(tcx, guar); } - let trunc = |n| { - let width = match tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)) { - Ok(layout) => layout.size, - Err(_) => { - tcx.dcx().bug(format!("couldn't compute width of literal: {:?}", lit_input.lit)) - } - }; + let trunc = |n, width: ty::UintTy| { + let width = width + .normalize(tcx.data_layout.pointer_size.bits().try_into().unwrap()) + .bit_width() + .unwrap(); + let width = Size::from_bits(width); trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits()); let result = width.truncate(n); trace!("trunc result: {}", result); - Ok(ScalarInt::try_from_uint(result, width) - .unwrap_or_else(|| bug!("expected to create ScalarInt from uint {:?}", result))) + ScalarInt::try_from_uint(result, width) + .unwrap_or_else(|| bug!("expected to create ScalarInt from uint {:?}", result)) }; let valtree = match (lit, ty.kind()) { @@ -48,29 +48,35 @@ pub(crate) fn lit_to_const<'tcx>( ty::ValTree::from_raw_bytes(tcx, bytes) } (ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => { - ty::ValTree::from_scalar_int((*n).into()) + ty::ValTree::from_scalar_int(tcx, (*n).into()) } (ast::LitKind::CStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::CStr)) => { let bytes = data as &[u8]; ty::ValTree::from_raw_bytes(tcx, bytes) } - (ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => { - let scalar_int = - trunc(if neg { (n.get() as i128).overflowing_neg().0 as u128 } else { n.get() })?; - ty::ValTree::from_scalar_int(scalar_int) + (ast::LitKind::Int(n, _), ty::Uint(ui)) if !neg => { + let scalar_int = trunc(n.get(), *ui); + ty::ValTree::from_scalar_int(tcx, scalar_int) } - (ast::LitKind::Bool(b), ty::Bool) => ty::ValTree::from_scalar_int((*b).into()), + (ast::LitKind::Int(n, _), ty::Int(i)) => { + let scalar_int = trunc( + if neg { (n.get() as i128).overflowing_neg().0 as u128 } else { n.get() }, + i.to_unsigned(), + ); + ty::ValTree::from_scalar_int(tcx, scalar_int) + } + (ast::LitKind::Bool(b), ty::Bool) => ty::ValTree::from_scalar_int(tcx, (*b).into()), (ast::LitKind::Float(n, _), ty::Float(fty)) => { - let bits = parse_float_into_scalar(*n, *fty, neg).ok_or_else(|| { + let bits = parse_float_into_scalar(*n, *fty, neg).unwrap_or_else(|| { tcx.dcx().bug(format!("couldn't parse float literal: {:?}", lit_input.lit)) - })?; - ty::ValTree::from_scalar_int(bits) + }); + ty::ValTree::from_scalar_int(tcx, bits) } - (ast::LitKind::Char(c), ty::Char) => ty::ValTree::from_scalar_int((*c).into()), - (ast::LitKind::Err(guar), _) => return Err(LitToConstError::Reported(*guar)), - _ => return Err(LitToConstError::TypeError), + (ast::LitKind::Char(c), ty::Char) => ty::ValTree::from_scalar_int(tcx, (*c).into()), + (ast::LitKind::Err(guar), _) => return ty::Const::new_error(tcx, *guar), + _ => return ty::Const::new_misc_error(tcx), }; - Ok(ty::Const::new_value(tcx, valtree, ty)) + ty::Const::new_value(tcx, valtree, ty) } diff --git a/compiler/rustc_mir_build/src/thir/cx/block.rs b/compiler/rustc_mir_build/src/thir/cx/block.rs index c9df027687ab9..e858b629ab14b 100644 --- a/compiler/rustc_mir_build/src/thir/cx/block.rs +++ b/compiler/rustc_mir_build/src/thir/cx/block.rs @@ -6,9 +6,9 @@ use rustc_middle::ty; use rustc_middle::ty::CanonicalUserTypeAnnotation; use tracing::debug; -use crate::thir::cx::Cx; +use crate::thir::cx::ThirBuildCx; -impl<'tcx> Cx<'tcx> { +impl<'tcx> ThirBuildCx<'tcx> { pub(crate) fn mirror_block(&mut self, block: &'tcx hir::Block<'tcx>) -> BlockId { // We have to eagerly lower the "spine" of the statements // in order to get the lexical scoping correctly. diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 0338ac674e5e2..54da6924db444 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -1,5 +1,6 @@ use itertools::Itertools; use rustc_abi::{FIRST_VARIANT, FieldIdx}; +use rustc_ast::UnsafeBinderCastKind; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; @@ -20,11 +21,9 @@ use rustc_middle::{bug, span_bug}; use rustc_span::{Span, sym}; use tracing::{debug, info, instrument, trace}; -use crate::errors; -use crate::thir::cx::Cx; -use crate::thir::util::UserAnnotatedTyHelpers; +use crate::thir::cx::ThirBuildCx; -impl<'tcx> Cx<'tcx> { +impl<'tcx> ThirBuildCx<'tcx> { /// Create a THIR expression for the given HIR expression. This expands all /// adjustments and directly adds the type information from the /// `typeck_results`. See the [dev-guide] for more details. @@ -142,9 +141,9 @@ impl<'tcx> Cx<'tcx> { Adjust::Deref(Some(deref)) => { // We don't need to do call adjust_span here since // deref coercions always start with a built-in deref. - let call_def_id = deref.method_call(self.tcx()); + let call_def_id = deref.method_call(self.tcx); let overloaded_callee = - Ty::new_fn_def(self.tcx(), call_def_id, self.tcx().mk_args(&[expr.ty.into()])); + Ty::new_fn_def(self.tcx, call_def_id, self.tcx.mk_args(&[expr.ty.into()])); expr = Expr { temp_lifetime, @@ -253,10 +252,10 @@ impl<'tcx> Cx<'tcx> { // Check to see if this cast is a "coercion cast", where the cast is actually done // using a coercion (or is a no-op). - if self.typeck_results().is_coercion_cast(source.hir_id) { + if self.typeck_results.is_coercion_cast(source.hir_id) { // Convert the lexpr to a vexpr. ExprKind::Use { source: self.mirror_expr(source) } - } else if self.typeck_results().expr_ty(source).is_ref() { + } else if self.typeck_results.expr_ty(source).is_ref() { // Special cased so that we can type check that the element // type of the source matches the pointed to type of the // destination. @@ -266,8 +265,8 @@ impl<'tcx> Cx<'tcx> { is_from_as_cast: true, } } else if let hir::ExprKind::Path(ref qpath) = source.kind - && let res = self.typeck_results().qpath_res(qpath, source.hir_id) - && let ty = self.typeck_results().node_type(source.hir_id) + && let res = self.typeck_results.qpath_res(qpath, source.hir_id) + && let ty = self.typeck_results.node_type(source.hir_id) && let ty::Adt(adt_def, args) = ty.kind() && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), variant_ctor_id) = res { @@ -330,7 +329,7 @@ impl<'tcx> Cx<'tcx> { #[instrument(level = "debug", skip(self), ret)] fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> { let tcx = self.tcx; - let expr_ty = self.typeck_results().expr_ty(expr); + let expr_ty = self.typeck_results.expr_ty(expr); let (temp_lifetime, backwards_incompatible) = self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id); @@ -354,7 +353,7 @@ impl<'tcx> Cx<'tcx> { } hir::ExprKind::Call(fun, ref args) => { - if self.typeck_results().is_method_call(expr) { + if self.typeck_results.is_method_call(expr) { // The callee is something implementing Fn, FnMut, or FnOnce. // Find the actual method implementation being called and // build the appropriate UFCS call expression with the @@ -364,7 +363,7 @@ impl<'tcx> Cx<'tcx> { let method = self.method_callee(expr, fun.span, None); - let arg_tys = args.iter().map(|e| self.typeck_results().expr_ty_adjusted(e)); + let arg_tys = args.iter().map(|e| self.typeck_results.expr_ty_adjusted(e)); let tupled_args = Expr { ty: Ty::new_tup_from_iter(tcx, arg_tys), temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, @@ -380,45 +379,25 @@ impl<'tcx> Cx<'tcx> { from_hir_call: true, fn_span: expr.span, } - } else { - let attrs = tcx.hir().attrs(expr.hir_id); - if attrs.iter().any(|a| a.name_or_empty() == sym::rustc_box) { - if attrs.len() != 1 { - tcx.dcx().emit_err(errors::RustcBoxAttributeError { - span: attrs[0].span, - reason: errors::RustcBoxAttrReason::Attributes, - }); - } else if let Some(box_item) = tcx.lang_items().owned_box() { - if let hir::ExprKind::Path(hir::QPath::TypeRelative(ty, fn_path)) = - fun.kind - && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind - && path.res.opt_def_id().is_some_and(|did| did == box_item) - && fn_path.ident.name == sym::new - && let [value] = args - { - return Expr { - temp_lifetime: TempLifetime { - temp_lifetime, - backwards_incompatible, - }, - ty: expr_ty, - span: expr.span, - kind: ExprKind::Box { value: self.mirror_expr(value) }, - }; - } else { - tcx.dcx().emit_err(errors::RustcBoxAttributeError { - span: expr.span, - reason: errors::RustcBoxAttrReason::NotBoxNew, - }); - } - } else { - tcx.dcx().emit_err(errors::RustcBoxAttributeError { - span: attrs[0].span, - reason: errors::RustcBoxAttrReason::MissingBox, - }); - } + } else if let ty::FnDef(def_id, _) = self.typeck_results.expr_ty(fun).kind() + && let Some(intrinsic) = self.tcx.intrinsic(def_id) + && intrinsic.name == sym::box_new + { + // We don't actually evaluate `fun` here, so make sure that doesn't miss any side-effects. + if !matches!(fun.kind, hir::ExprKind::Path(_)) { + span_bug!( + expr.span, + "`box_new` intrinsic can only be called via path expression" + ); } - + let value = &args[0]; + return Expr { + temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, + ty: expr_ty, + span: expr.span, + kind: ExprKind::Box { value: self.mirror_expr(value) }, + }; + } else { // Tuple-like ADTs are represented as ExprKind::Call. We convert them here. let adt_data = if let hir::ExprKind::Path(ref qpath) = fun.kind && let Some(adt_def) = expr_ty.ty_adt_def() @@ -433,7 +412,7 @@ impl<'tcx> Cx<'tcx> { }, hir::QPath::TypeRelative(_ty, _) => { if let Some((DefKind::Ctor(_, CtorKind::Fn), ctor_id)) = - self.typeck_results().type_dependent_def(fun.hir_id) + self.typeck_results.type_dependent_def(fun.hir_id) { Some((adt_def, adt_def.variant_index_with_ctor_id(ctor_id))) } else { @@ -446,8 +425,8 @@ impl<'tcx> Cx<'tcx> { None }; if let Some((adt_def, index)) = adt_data { - let node_args = self.typeck_results().node_args(fun.hir_id); - let user_provided_types = self.typeck_results().user_provided_types(); + let node_args = self.typeck_results.node_args(fun.hir_id); + let user_provided_types = self.typeck_results.user_provided_types(); let user_ty = user_provided_types.get(fun.hir_id).copied().map(|mut u_ty| { if let ty::UserTypeKind::TypeOf(ref mut did, _) = @@ -477,7 +456,7 @@ impl<'tcx> Cx<'tcx> { })) } else { ExprKind::Call { - ty: self.typeck_results().node_type(fun.hir_id), + ty: self.typeck_results.node_type(fun.hir_id), fun: self.mirror_expr(fun), args: self.mirror_exprs(args), from_hir_call: true, @@ -502,7 +481,7 @@ impl<'tcx> Cx<'tcx> { } hir::ExprKind::AssignOp(op, lhs, rhs) => { - if self.typeck_results().is_method_call(expr) { + if self.typeck_results.is_method_call(expr) { let lhs = self.mirror_expr(lhs); let rhs = self.mirror_expr(rhs); self.overloaded_operator(expr, Box::new([lhs, rhs])) @@ -518,7 +497,7 @@ impl<'tcx> Cx<'tcx> { hir::ExprKind::Lit(lit) => ExprKind::Literal { lit, neg: false }, hir::ExprKind::Binary(op, lhs, rhs) => { - if self.typeck_results().is_method_call(expr) { + if self.typeck_results.is_method_call(expr) { let lhs = self.mirror_expr(lhs); let rhs = self.mirror_expr(rhs); self.overloaded_operator(expr, Box::new([lhs, rhs])) @@ -547,7 +526,7 @@ impl<'tcx> Cx<'tcx> { } hir::ExprKind::Index(lhs, index, brackets_span) => { - if self.typeck_results().is_method_call(expr) { + if self.typeck_results.is_method_call(expr) { let lhs = self.mirror_expr(lhs); let index = self.mirror_expr(index); self.overloaded_place( @@ -563,7 +542,7 @@ impl<'tcx> Cx<'tcx> { } hir::ExprKind::Unary(hir::UnOp::Deref, arg) => { - if self.typeck_results().is_method_call(expr) { + if self.typeck_results.is_method_call(expr) { let arg = self.mirror_expr(arg); self.overloaded_place(expr, expr_ty, None, Box::new([arg]), expr.span) } else { @@ -572,7 +551,7 @@ impl<'tcx> Cx<'tcx> { } hir::ExprKind::Unary(hir::UnOp::Not, arg) => { - if self.typeck_results().is_method_call(expr) { + if self.typeck_results.is_method_call(expr) { let arg = self.mirror_expr(arg); self.overloaded_operator(expr, Box::new([arg])) } else { @@ -581,7 +560,7 @@ impl<'tcx> Cx<'tcx> { } hir::ExprKind::Unary(hir::UnOp::Neg, arg) => { - if self.typeck_results().is_method_call(expr) { + if self.typeck_results.is_method_call(expr) { let arg = self.mirror_expr(arg); self.overloaded_operator(expr, Box::new([arg])) } else if let hir::ExprKind::Lit(lit) = arg.kind { @@ -594,7 +573,7 @@ impl<'tcx> Cx<'tcx> { hir::ExprKind::Struct(qpath, fields, ref base) => match expr_ty.kind() { ty::Adt(adt, args) => match adt.adt_kind() { AdtKind::Struct | AdtKind::Union => { - let user_provided_types = self.typeck_results().user_provided_types(); + let user_provided_types = self.typeck_results.user_provided_types(); let user_ty = user_provided_types.get(expr.hir_id).copied().map(Box::new); debug!("make_mirror_unadjusted: (struct/union) user_ty={:?}", user_ty); ExprKind::Adt(Box::new(AdtExpr { @@ -606,15 +585,14 @@ impl<'tcx> Cx<'tcx> { base: match base { hir::StructTailExpr::Base(base) => AdtExprBase::Base(FruInfo { base: self.mirror_expr(base), - field_types: self.typeck_results().fru_field_types() - [expr.hir_id] + field_types: self.typeck_results.fru_field_types()[expr.hir_id] .iter() .copied() .collect(), }), hir::StructTailExpr::DefaultFields(_) => { AdtExprBase::DefaultFields( - self.typeck_results().fru_field_types()[expr.hir_id] + self.typeck_results.fru_field_types()[expr.hir_id] .iter() .copied() .collect(), @@ -625,7 +603,7 @@ impl<'tcx> Cx<'tcx> { })) } AdtKind::Enum => { - let res = self.typeck_results().qpath_res(qpath, expr.hir_id); + let res = self.typeck_results.qpath_res(qpath, expr.hir_id); match res { Res::Def(DefKind::Variant, variant_id) => { assert!(matches!( @@ -635,8 +613,7 @@ impl<'tcx> Cx<'tcx> { )); let index = adt.variant_index_with_id(variant_id); - let user_provided_types = - self.typeck_results().user_provided_types(); + let user_provided_types = self.typeck_results.user_provided_types(); let user_ty = user_provided_types.get(expr.hir_id).copied().map(Box::new); debug!("make_mirror_unadjusted: (variant) user_ty={:?}", user_ty); @@ -649,8 +626,7 @@ impl<'tcx> Cx<'tcx> { base: match base { hir::StructTailExpr::DefaultFields(_) => { AdtExprBase::DefaultFields( - self.typeck_results().fru_field_types() - [expr.hir_id] + self.typeck_results.fru_field_types()[expr.hir_id] .iter() .copied() .collect(), @@ -675,7 +651,7 @@ impl<'tcx> Cx<'tcx> { }, hir::ExprKind::Closure { .. } => { - let closure_ty = self.typeck_results().expr_ty(expr); + let closure_ty = self.typeck_results.expr_ty(expr); let (def_id, args, movability) = match *closure_ty.kind() { ty::Closure(def_id, args) => (def_id, UpvarArgs::Closure(args), None), ty::Coroutine(def_id, args) => { @@ -723,7 +699,7 @@ impl<'tcx> Cx<'tcx> { } hir::ExprKind::Path(ref qpath) => { - let res = self.typeck_results().qpath_res(qpath, expr.hir_id); + let res = self.typeck_results.qpath_res(qpath, expr.hir_id); self.convert_path_expr(expr, res) } @@ -792,7 +768,7 @@ impl<'tcx> Cx<'tcx> { } hir::ExprKind::ConstBlock(ref anon_const) => { - let ty = self.typeck_results().node_type(anon_const.hir_id); + let ty = self.typeck_results.node_type(anon_const.hir_id); let did = anon_const.def_id.to_def_id(); let typeck_root_def_id = tcx.typeck_root_def_id(did); let parent_args = @@ -803,7 +779,7 @@ impl<'tcx> Cx<'tcx> { } // Now comes the rote stuff: hir::ExprKind::Repeat(v, _) => { - let ty = self.typeck_results().expr_ty(expr); + let ty = self.typeck_results.expr_ty(expr); let ty::Array(_, count) = ty.kind() else { span_bug!(expr.span, "unexpected repeat expr ty: {:?}", ty); }; @@ -857,7 +833,7 @@ impl<'tcx> Cx<'tcx> { match_source, }, hir::ExprKind::Loop(body, ..) => { - let block_ty = self.typeck_results().node_type(body.hir_id); + let block_ty = self.typeck_results.node_type(body.hir_id); let (temp_lifetime, backwards_incompatible) = self .rvalue_scopes .temporary_scope(self.region_scope_tree, body.hir_id.local_id); @@ -931,8 +907,19 @@ impl<'tcx> Cx<'tcx> { } } - hir::ExprKind::UnsafeBinderCast(_kind, _source, _ty) => { - unreachable!("unsafe binders are not yet implemented") + hir::ExprKind::UnsafeBinderCast(UnsafeBinderCastKind::Unwrap, source, _ty) => { + // FIXME(unsafe_binders): Take into account the ascribed type, too. + let mirrored = self.mirror_expr(source); + if source.is_syntactic_place_expr() { + ExprKind::PlaceUnwrapUnsafeBinder { source: mirrored } + } else { + ExprKind::ValueUnwrapUnsafeBinder { source: mirrored } + } + } + hir::ExprKind::UnsafeBinderCast(UnsafeBinderCastKind::Wrap, source, _ty) => { + // FIXME(unsafe_binders): Take into account the ascribed type, too. + let mirrored = self.mirror_expr(source); + ExprKind::WrapUnsafeBinder { source: mirrored } } hir::ExprKind::DropTemps(source) => ExprKind::Use { source: self.mirror_expr(source) }, @@ -966,7 +953,7 @@ impl<'tcx> Cx<'tcx> { | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) | Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => { - self.typeck_results().user_provided_types().get(hir_id).copied().map(Box::new) + self.typeck_results.user_provided_types().get(hir_id).copied().map(Box::new) } // A unit struct/variant which is used as a value (e.g., @@ -998,17 +985,13 @@ impl<'tcx> Cx<'tcx> { Some(fn_def) => (fn_def, None), None => { let (kind, def_id) = - self.typeck_results().type_dependent_def(expr.hir_id).unwrap_or_else(|| { + self.typeck_results.type_dependent_def(expr.hir_id).unwrap_or_else(|| { span_bug!(expr.span, "no type-dependent def for method callee") }); let user_ty = self.user_args_applied_to_res(expr.hir_id, Res::Def(kind, def_id)); debug!("method_callee: user_ty={:?}", user_ty); ( - Ty::new_fn_def( - self.tcx(), - def_id, - self.typeck_results().node_args(expr.hir_id), - ), + Ty::new_fn_def(self.tcx, def_id, self.typeck_results.node_args(expr.hir_id)), user_ty, ) } @@ -1034,7 +1017,7 @@ impl<'tcx> Cx<'tcx> { } fn convert_path_expr(&mut self, expr: &'tcx hir::Expr<'tcx>, res: Res) -> ExprKind<'tcx> { - let args = self.typeck_results().node_args(expr.hir_id); + let args = self.typeck_results.node_args(expr.hir_id); match res { // A regular function, constructor function or a constant. Res::Def(DefKind::Fn, _) @@ -1069,7 +1052,7 @@ impl<'tcx> Cx<'tcx> { let user_provided_types = self.typeck_results.user_provided_types(); let user_ty = user_provided_types.get(expr.hir_id).copied().map(Box::new); debug!("convert_path_expr: user_ty={:?}", user_ty); - let ty = self.typeck_results().node_type(expr.hir_id); + let ty = self.typeck_results.node_type(expr.hir_id); match ty.kind() { // A unit struct/variant which is used as a value. // We return a completely different ExprKind here to account for this special case. diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index a98c2bb61f666..a01609012b8f1 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -16,15 +16,15 @@ use rustc_middle::ty::{self, RvalueScopes, TyCtxt}; use tracing::instrument; use crate::thir::pattern::pat_from_hir; -use crate::thir::util::UserAnnotatedTyHelpers; +/// Query implementation for [`TyCtxt::thir_body`]. pub(crate) fn thir_body( tcx: TyCtxt<'_>, owner_def: LocalDefId, ) -> Result<(&Steal>, ExprId), ErrorGuaranteed> { let hir = tcx.hir(); let body = hir.body_owned_by(owner_def); - let mut cx = Cx::new(tcx, owner_def); + let mut cx = ThirBuildCx::new(tcx, owner_def); if let Some(reported) = cx.typeck_results.tainted_by_errors { return Err(reported); } @@ -52,8 +52,10 @@ pub(crate) fn thir_body( Ok((tcx.alloc_steal_thir(cx.thir), expr)) } -struct Cx<'tcx> { +/// Context for lowering HIR to THIR for a single function body (or other kind of body). +struct ThirBuildCx<'tcx> { tcx: TyCtxt<'tcx>, + /// The THIR data that this context is building. thir: Thir<'tcx>, typing_env: ty::TypingEnv<'tcx>, @@ -69,8 +71,8 @@ struct Cx<'tcx> { body_owner: DefId, } -impl<'tcx> Cx<'tcx> { - fn new(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Cx<'tcx> { +impl<'tcx> ThirBuildCx<'tcx> { + fn new(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Self { let typeck_results = tcx.typeck(def); let hir = tcx.hir(); let hir_id = tcx.local_def_id_to_hir_id(def); @@ -94,7 +96,7 @@ impl<'tcx> Cx<'tcx> { BodyTy::Const(typeck_results.node_type(hir_id)) }; - Cx { + Self { tcx, thir: Thir::new(body_type), // FIXME(#132279): We're in a body, we should use a typing @@ -113,7 +115,7 @@ impl<'tcx> Cx<'tcx> { #[instrument(level = "debug", skip(self))] fn pattern_from_hir(&mut self, p: &'tcx hir::Pat<'tcx>) -> Box> { - pat_from_hir(self.tcx, self.typing_env, self.typeck_results(), p) + pat_from_hir(self.tcx, self.typing_env, self.typeck_results, p) } fn closure_env_param(&self, owner_def: LocalDefId, expr_id: HirId) -> Option> { @@ -184,11 +186,9 @@ impl<'tcx> Cx<'tcx> { let ty = if fn_decl.c_variadic && index == fn_decl.inputs.len() { let va_list_did = self.tcx.require_lang_item(LangItem::VaList, Some(param.span)); - self.tcx.type_of(va_list_did).instantiate(self.tcx, &[self - .tcx - .lifetimes - .re_erased - .into()]) + self.tcx + .type_of(va_list_did) + .instantiate(self.tcx, &[self.tcx.lifetimes.re_erased.into()]) } else { fn_sig.inputs()[index] }; @@ -197,15 +197,12 @@ impl<'tcx> Cx<'tcx> { Param { pat: Some(pat), ty, ty_span, self_kind, hir_id: Some(param.hir_id) } }) } -} - -impl<'tcx> UserAnnotatedTyHelpers<'tcx> for Cx<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - fn typeck_results(&self) -> &ty::TypeckResults<'tcx> { - self.typeck_results + fn user_args_applied_to_ty_of_hir_id( + &self, + hir_id: HirId, + ) -> Option> { + crate::thir::util::user_args_applied_to_ty_of_hir_id(self.typeck_results, hir_id) } } diff --git a/compiler/rustc_mir_build/src/thir/mod.rs b/compiler/rustc_mir_build/src/thir/mod.rs index ca26cc13b5e87..c7c88801d4d81 100644 --- a/compiler/rustc_mir_build/src/thir/mod.rs +++ b/compiler/rustc_mir_build/src/thir/mod.rs @@ -7,5 +7,5 @@ pub(crate) mod constant; pub(crate) mod cx; pub(crate) mod pattern; -pub(crate) mod print; +pub mod print; mod util; diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index a13b00e192167..697cb7cf37a3c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -25,7 +25,7 @@ use rustc_session::lint::builtin::{ }; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::hygiene::DesugaringKind; -use rustc_span::{Span, sym}; +use rustc_span::{Ident, Span, sym}; use rustc_trait_selection::infer::InferCtxtExt; use tracing::instrument; @@ -326,9 +326,10 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { | Use { source } | PointerCoercion { source, .. } | PlaceTypeAscription { source, .. } - | ValueTypeAscription { source, .. } => { - self.is_known_valid_scrutinee(&self.thir()[*source]) - } + | ValueTypeAscription { source, .. } + | PlaceUnwrapUnsafeBinder { source } + | ValueUnwrapUnsafeBinder { source } + | WrapUnsafeBinder { source } => self.is_known_valid_scrutinee(&self.thir()[*source]), // These diverge. Become { .. } | Break { .. } | Continue { .. } | Return { .. } => true, @@ -675,12 +676,14 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { let mut interpreted_as_const = None; let mut interpreted_as_const_sugg = None; - if let PatKind::ExpandedConstant { def_id, is_inline: false, .. } - | PatKind::AscribeUserType { - subpattern: - box Pat { kind: PatKind::ExpandedConstant { def_id, is_inline: false, .. }, .. }, - .. - } = pat.kind + // These next few matches want to peek through `AscribeUserType` to see + // the underlying pattern. + let mut unpeeled_pat = pat; + while let PatKind::AscribeUserType { ref subpattern, .. } = unpeeled_pat.kind { + unpeeled_pat = subpattern; + } + + if let PatKind::ExpandedConstant { def_id, is_inline: false, .. } = unpeeled_pat.kind && let DefKind::Const = self.tcx.def_kind(def_id) && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span) // We filter out paths with multiple path::segments. @@ -691,11 +694,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { // When we encounter a constant as the binding name, point at the `const` definition. interpreted_as_const = Some(span); interpreted_as_const_sugg = Some(InterpretedAsConst { span: pat.span, variable }); - } else if let PatKind::Constant { .. } - | PatKind::AscribeUserType { - subpattern: box Pat { kind: PatKind::Constant { .. }, .. }, - .. - } = pat.kind + } else if let PatKind::Constant { .. } = unpeeled_pat.kind && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span) { // If the pattern to match is an integer literal: @@ -795,13 +794,17 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: } }); if !conflicts_ref.is_empty() { - sess.dcx().emit_err(BorrowOfMovedValue { + let mut path = None; + let ty = cx.tcx.short_string(ty, &mut path); + let mut err = sess.dcx().create_err(BorrowOfMovedValue { binding_span: pat.span, conflicts_ref, - name, + name: Ident::new(name, pat.span), ty, suggest_borrowing: Some(pat.span.shrink_to_lo()), }); + *err.long_ty_path() = path; + err.emit(); } return; } @@ -904,7 +907,7 @@ fn check_for_bindings_named_same_as_variants( None }, ty_path, - name, + name: Ident::new(name, pat.span), }, ) } @@ -1086,14 +1089,13 @@ fn find_fallback_pattern_typo<'tcx>( let vis = cx.tcx.visibility(item.owner_id); if vis.is_accessible_from(parent, cx.tcx) { accessible.push(item_name); - let path = if item_name == name { - // We know that the const wasn't in scope because it has the exact - // same name, so we suggest the full path. - with_no_trimmed_paths!(cx.tcx.def_path_str(item.owner_id)) - } else { - // The const is likely just typoed, and nothing else. - cx.tcx.def_path_str(item.owner_id) - }; + // FIXME: the line below from PR #135310 is a workaround for the ICE in issue + // #135289, where a macro in a dependency can create unreachable patterns in the + // current crate. Path trimming expects diagnostics for a typoed const, but no + // diagnostics are emitted and we ICE. See + // `tests/ui/resolve/const-with-typo-in-pattern-binding-ice-135289.rs` for a + // test that reproduces the ICE if we don't use `with_no_trimmed_paths!`. + let path = with_no_trimmed_paths!(cx.tcx.def_path_str(item.owner_id)); accessible_path.push(path); } else if name == item_name { // The const exists somewhere in this crate, but it can't be imported diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index aed00aecefc99..551ec5cf4e9c6 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -1,3 +1,5 @@ +use core::ops::ControlFlow; + use rustc_abi::{FieldIdx, VariantIdx}; use rustc_apfloat::Float; use rustc_data_structures::fx::FxHashSet; @@ -8,7 +10,9 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::thir::{FieldPat, Pat, PatKind}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypeVisitor, ValTree}; +use rustc_middle::ty::{ + self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitableExt, TypeVisitor, ValTree, +}; use rustc_middle::{mir, span_bug}; use rustc_span::def_id::DefId; use rustc_span::{Span, sym}; @@ -42,7 +46,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { match c.kind() { ty::ConstKind::Unevaluated(uv) => convert.unevaluated_to_pat(uv, ty), - ty::ConstKind::Value(_, val) => convert.valtree_to_pat(val, ty), + ty::ConstKind::Value(cv) => convert.valtree_to_pat(cv.valtree, cv.ty), _ => span_bug!(span, "Invalid `ConstKind` for `const_to_pat`: {:?}", c), } } @@ -185,7 +189,7 @@ impl<'tcx> ConstToPat<'tcx> { if !inlined_const_as_pat.references_error() { // Always check for `PartialEq` if we had no other errors yet. - if !type_has_partial_eq_impl(self.tcx, typing_env, ty).0 { + if !type_has_partial_eq_impl(self.tcx, typing_env, ty).has_impl { let mut err = self.tcx.dcx().create_err(TypeNotPartialEq { span: self.span, ty }); extend_type_not_partial_eq(self.tcx, typing_env, ty, &mut err); return self.mk_err(err, ty); @@ -204,12 +208,13 @@ impl<'tcx> ConstToPat<'tcx> { let field = FieldIdx::new(idx); // Patterns can only use monomorphic types. let ty = self.tcx.normalize_erasing_regions(self.typing_env, ty); - FieldPat { field, pattern: self.valtree_to_pat(val, ty) } + FieldPat { field, pattern: *self.valtree_to_pat(val, ty) } }) .collect() } // Recursive helper for `to_pat`; invoke that (instead of calling this directly). + // FIXME(valtrees): Accept `ty::Value` instead of `Ty` and `ty::ValTree` separately. #[instrument(skip(self), level = "debug")] fn valtree_to_pat(&self, cv: ValTree<'tcx>, ty: Ty<'tcx>) -> Box> { let span = self.span; @@ -219,12 +224,13 @@ impl<'tcx> ConstToPat<'tcx> { // Extremely important check for all ADTs! Make sure they opted-in to be used in // patterns. debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, ty); - let (_impls_partial_eq, derived, structural, impl_def_id) = - type_has_partial_eq_impl(self.tcx, self.typing_env, ty); + let PartialEqImplStatus { + is_derived, structural_partial_eq, non_blanket_impl, .. + } = type_has_partial_eq_impl(self.tcx, self.typing_env, ty); let (manual_partialeq_impl_span, manual_partialeq_impl_note) = - match (structural, impl_def_id) { + match (structural_partial_eq, non_blanket_impl) { (true, _) => (None, false), - (_, Some(def_id)) if def_id.is_local() && !derived => { + (_, Some(def_id)) if def_id.is_local() && !is_derived => { (Some(tcx.def_span(def_id)), false) } _ => (None, true), @@ -271,7 +277,7 @@ impl<'tcx> ConstToPat<'tcx> { prefix: cv .unwrap_branch() .iter() - .map(|val| self.valtree_to_pat(*val, *elem_ty)) + .map(|val| *self.valtree_to_pat(*val, *elem_ty)) .collect(), slice: None, suffix: Box::new([]), @@ -280,7 +286,7 @@ impl<'tcx> ConstToPat<'tcx> { prefix: cv .unwrap_branch() .iter() - .map(|val| self.valtree_to_pat(*val, *elem_ty)) + .map(|val| *self.valtree_to_pat(*val, *elem_ty)) .collect(), slice: None, suffix: Box::new([]), @@ -379,41 +385,50 @@ fn extend_type_not_partial_eq<'tcx>( adts_without_partialeq: FxHashSet, /// The user has written `impl PartialEq for Ty` which means it's non-structual, /// but we don't have a span to point at, so we'll just add them as a `note`. - manual: Vec>, + manual: FxHashSet>, /// The type has no `PartialEq` implementation, neither manual or derived, but /// we don't have a span to point at, so we'll just add them as a `note`. - without: Vec>, + without: FxHashSet>, } impl<'tcx> TypeVisitor> for UsedParamsNeedInstantiationVisitor<'tcx> { + type Result = ControlFlow<()>; fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { - if let ty::Adt(def, _args) = ty.kind() { - let ty_def_id = def.did(); - let ty_def_span = self.tcx.def_span(ty_def_id); - let (impls_partial_eq, derived, structural, impl_def_id) = - type_has_partial_eq_impl(self.tcx, self.typing_env, ty); - match (impls_partial_eq, derived, structural, impl_def_id) { - (_, _, true, _) => {} - (true, false, _, Some(def_id)) if def_id.is_local() => { - self.adts_with_manual_partialeq.insert(self.tcx.def_span(def_id)); - } - (true, false, _, _) if ty_def_id.is_local() => { - self.adts_with_manual_partialeq.insert(ty_def_span); - } - (false, _, _, _) if ty_def_id.is_local() => { - self.adts_without_partialeq.insert(ty_def_span); - } - (true, false, _, _) => { - self.manual.push(ty); - } - (false, _, _, _) => { - self.without.push(ty); - } - _ => {} - }; + match ty.kind() { + ty::Dynamic(..) => return ControlFlow::Break(()), + ty::FnPtr(..) => return ControlFlow::Continue(()), + ty::Adt(def, _args) => { + let ty_def_id = def.did(); + let ty_def_span = self.tcx.def_span(ty_def_id); + let PartialEqImplStatus { + has_impl, + is_derived, + structural_partial_eq, + non_blanket_impl, + } = type_has_partial_eq_impl(self.tcx, self.typing_env, ty); + match (has_impl, is_derived, structural_partial_eq, non_blanket_impl) { + (_, _, true, _) => {} + (true, false, _, Some(def_id)) if def_id.is_local() => { + self.adts_with_manual_partialeq.insert(self.tcx.def_span(def_id)); + } + (true, false, _, _) if ty_def_id.is_local() => { + self.adts_with_manual_partialeq.insert(ty_def_span); + } + (false, _, _, _) if ty_def_id.is_local() => { + self.adts_without_partialeq.insert(ty_def_span); + } + (true, false, _, _) => { + self.manual.insert(ty); + } + (false, _, _, _) => { + self.without.insert(ty); + } + _ => {} + }; + ty.super_visit_with(self) + } + _ => ty.super_visit_with(self), } - use rustc_middle::ty::TypeSuperVisitable; - ty.super_visit_with(self) } } let mut v = UsedParamsNeedInstantiationVisitor { @@ -421,10 +436,12 @@ fn extend_type_not_partial_eq<'tcx>( typing_env, adts_with_manual_partialeq: FxHashSet::default(), adts_without_partialeq: FxHashSet::default(), - manual: vec![], - without: vec![], + manual: FxHashSet::default(), + without: FxHashSet::default(), }; - v.visit_ty(ty); + if v.visit_ty(ty).is_break() { + return; + } #[allow(rustc::potential_query_instability)] // Span labels will be sorted by the rendering for span in v.adts_with_manual_partialeq { err.span_note(span, "the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details"); @@ -436,29 +453,38 @@ fn extend_type_not_partial_eq<'tcx>( "must be annotated with `#[derive(PartialEq)]` to be usable in patterns", ); } - for ty in v.manual { + #[allow(rustc::potential_query_instability)] + let mut manual: Vec<_> = v.manual.into_iter().map(|t| t.to_string()).collect(); + manual.sort(); + for ty in manual { err.note(format!( "`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details" )); } - for ty in v.without { + #[allow(rustc::potential_query_instability)] + let mut without: Vec<_> = v.without.into_iter().map(|t| t.to_string()).collect(); + without.sort(); + for ty in without { err.note(format!( "`{ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns" )); } } +#[derive(Debug)] +struct PartialEqImplStatus { + has_impl: bool, + is_derived: bool, + structural_partial_eq: bool, + non_blanket_impl: Option, +} + #[instrument(level = "trace", skip(tcx), ret)] fn type_has_partial_eq_impl<'tcx>( tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, ty: Ty<'tcx>, -) -> ( - /* has impl */ bool, - /* is derived */ bool, - /* structural partial eq */ bool, - /* non-blanket impl */ Option, -) { +) -> PartialEqImplStatus { let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); // double-check there even *is* a semantic `PartialEq` to dispatch to. // @@ -491,10 +517,14 @@ fn type_has_partial_eq_impl<'tcx>( // `PartialEq` for some lifetime but *not* for `'static`? If this ever becomes a problem // we'll need to leave some sort of trace of this requirement in the MIR so that borrowck // can ensure that the type really implements `PartialEq`. - ( - infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation), - automatically_derived, - structural_peq, - impl_def_id, - ) + // We also do *not* require `const PartialEq`, not even in `const fn`. This violates the model + // that patterns can only do things that the code could also do without patterns, but it is + // needed for backwards compatibility. The actual pattern matching compares primitive values, + // `PartialEq::eq` never gets invoked, so there's no risk of us running non-const code. + PartialEqImplStatus { + has_impl: infcx.predicate_must_hold_modulo_regions(&partial_eq_obligation), + is_derived: automatically_derived, + structural_partial_eq: structural_peq, + non_blanket_impl: impl_def_id, + } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index bdf243c87b6f1..22b8bfef09d26 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -4,6 +4,7 @@ mod check_match; mod const_to_pat; use std::cmp::Ordering; +use std::sync::Arc; use rustc_abi::{FieldIdx, Integer}; use rustc_errors::MultiSpan; @@ -13,7 +14,7 @@ use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::{self as hir, ByRef, Mutability, RangeEnd}; use rustc_index::Idx; use rustc_lint as lint; -use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; +use rustc_middle::mir::interpret::LitToConstInput; use rustc_middle::thir::{ Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary, }; @@ -27,7 +28,6 @@ use tracing::{debug, instrument}; pub(crate) use self::check_match::check_match; use crate::errors::*; use crate::fluent_generated as fluent; -use crate::thir::util::UserAnnotatedTyHelpers; struct PatCtxt<'a, 'tcx> { tcx: TyCtxt<'tcx>, @@ -35,7 +35,7 @@ struct PatCtxt<'a, 'tcx> { typeck_results: &'a ty::TypeckResults<'tcx>, /// Used by the Rust 2024 migration lint. - rust_2024_migration_suggestion: Option>, + rust_2024_migration_suggestion: Option, } pub(super) fn pat_from_hir<'a, 'tcx>( @@ -44,25 +44,30 @@ pub(super) fn pat_from_hir<'a, 'tcx>( typeck_results: &'a ty::TypeckResults<'tcx>, pat: &'tcx hir::Pat<'tcx>, ) -> Box> { + let migration_info = typeck_results.rust_2024_migration_desugared_pats().get(pat.hir_id); let mut pcx = PatCtxt { tcx, typing_env, typeck_results, - rust_2024_migration_suggestion: typeck_results - .rust_2024_migration_desugared_pats() - .get(pat.hir_id) - .map(|labels| Rust2024IncompatiblePatSugg { + rust_2024_migration_suggestion: migration_info.and_then(|info| { + Some(Rust2024IncompatiblePatSugg { + suggest_eliding_modes: info.suggest_eliding_modes, suggestion: Vec::new(), ref_pattern_count: 0, binding_mode_count: 0, - labels: labels.as_slice(), - }), + default_mode_span: None, + default_mode_labels: Default::default(), + }) + }), }; let result = pcx.lower_pattern(pat); debug!("pat_from_hir({:?}) = {:?}", pat, result); - if let Some(sugg) = pcx.rust_2024_migration_suggestion { - let mut spans = MultiSpan::from_spans(sugg.labels.iter().map(|(span, _)| *span).collect()); - for (span, label) in sugg.labels { + if let Some(info) = migration_info + && let Some(sugg) = pcx.rust_2024_migration_suggestion + { + let mut spans = + MultiSpan::from_spans(info.primary_labels.iter().map(|(span, _)| *span).collect()); + for (span, label) in &info.primary_labels { spans.push_span_label(*span, label.clone()); } // If a relevant span is from at least edition 2024, this is a hard error. @@ -70,10 +75,13 @@ pub(super) fn pat_from_hir<'a, 'tcx>( if is_hard_error { let mut err = tcx.dcx().struct_span_err(spans, fluent::mir_build_rust_2024_incompatible_pat); - if let Some(info) = lint::builtin::RUST_2024_INCOMPATIBLE_PAT.future_incompatible { + if let Some(lint_info) = lint::builtin::RUST_2024_INCOMPATIBLE_PAT.future_incompatible { // provide the same reference link as the lint - err.note(format!("for more information, see {}", info.reference)); + err.note(format!("for more information, see {}", lint_info.reference)); } + err.arg("bad_modifiers", info.bad_modifiers); + err.arg("bad_ref_pats", info.bad_ref_pats); + err.arg("is_hard_error", true); err.subdiagnostic(sugg); err.emit(); } else { @@ -81,7 +89,12 @@ pub(super) fn pat_from_hir<'a, 'tcx>( lint::builtin::RUST_2024_INCOMPATIBLE_PAT, pat.hir_id, spans, - Rust2024IncompatiblePat { sugg }, + Rust2024IncompatiblePat { + sugg, + bad_modifiers: info.bad_modifiers, + bad_ref_pats: info.bad_ref_pats, + is_hard_error, + }, ); } } @@ -90,6 +103,35 @@ pub(super) fn pat_from_hir<'a, 'tcx>( impl<'a, 'tcx> PatCtxt<'a, 'tcx> { fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box> { + let adjustments: &[Ty<'tcx>] = + self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v); + + let mut opt_old_mode_span = None; + if let Some(s) = &mut self.rust_2024_migration_suggestion + && !adjustments.is_empty() + { + let implicit_deref_mutbls = adjustments.iter().map(|ref_ty| { + let &ty::Ref(_, _, mutbl) = ref_ty.kind() else { + span_bug!(pat.span, "pattern implicitly dereferences a non-ref type"); + }; + mutbl + }); + + if !s.suggest_eliding_modes { + let suggestion_str: String = + implicit_deref_mutbls.clone().map(|mutbl| mutbl.ref_prefix_str()).collect(); + s.suggestion.push((pat.span.shrink_to_lo(), suggestion_str)); + s.ref_pattern_count += adjustments.len(); + } + + // Remember if this changed the default binding mode, in case we want to label it. + let min_mutbl = implicit_deref_mutbls.min().unwrap(); + if s.default_mode_span.is_none_or(|(_, old_mutbl)| min_mutbl < old_mutbl) { + opt_old_mode_span = Some(s.default_mode_span); + s.default_mode_span = Some((pat.span, min_mutbl)); + } + }; + // When implicit dereferences have been inserted in this pattern, the unadjusted lowered // pattern has the type that results *after* dereferencing. For example, in this code: // @@ -118,8 +160,6 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { _ => self.lower_pattern_unadjusted(pat), }; - let adjustments: &[Ty<'tcx>] = - self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v); let adjusted_pat = adjustments.iter().rev().fold(unadjusted_pat, |thir_pat, ref_ty| { debug!("{:?}: wrapping pattern with type {:?}", thir_pat, ref_ty); Box::new(Pat { @@ -130,67 +170,52 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { }); if let Some(s) = &mut self.rust_2024_migration_suggestion - && !adjustments.is_empty() + && let Some(old_mode_span) = opt_old_mode_span { - let suggestion_str: String = adjustments - .iter() - .map(|ref_ty| { - let &ty::Ref(_, _, mutbl) = ref_ty.kind() else { - span_bug!(pat.span, "pattern implicitly dereferences a non-ref type"); - }; - - match mutbl { - ty::Mutability::Not => "&", - ty::Mutability::Mut => "&mut ", - } - }) - .collect(); - s.suggestion.push((pat.span.shrink_to_lo(), suggestion_str)); - s.ref_pattern_count += adjustments.len(); - }; + s.default_mode_span = old_mode_span; + } adjusted_pat } fn lower_pattern_range_endpoint( &mut self, - expr: Option<&'tcx hir::Expr<'tcx>>, - ) -> Result< - (Option>, Option>, Option), - ErrorGuaranteed, - > { - match expr { - None => Ok((None, None, None)), - Some(expr) => { - let (kind, ascr, inline_const) = match self.lower_lit(expr) { - PatKind::ExpandedConstant { subpattern, def_id, is_inline: true } => { - (subpattern.kind, None, def_id.as_local()) - } - PatKind::ExpandedConstant { subpattern, is_inline: false, .. } => { - (subpattern.kind, None, None) - } - PatKind::AscribeUserType { ascription, subpattern: box Pat { kind, .. } } => { - (kind, Some(ascription), None) - } - kind => (kind, None, None), - }; - let value = match kind { - PatKind::Constant { value } => value, - PatKind::ExpandedConstant { subpattern, .. } - if let PatKind::Constant { value } = subpattern.kind => - { - value - } - _ => { - let msg = format!( - "found bad range pattern endpoint `{expr:?}` outside of error recovery" - ); - return Err(self.tcx.dcx().span_delayed_bug(expr.span, msg)); + expr: Option<&'tcx hir::PatExpr<'tcx>>, + // Out-parameters collecting extra data to be reapplied by the caller + ascriptions: &mut Vec>, + inline_consts: &mut Vec, + ) -> Result>, ErrorGuaranteed> { + let Some(expr) = expr else { return Ok(None) }; + + // Lower the endpoint into a temporary `PatKind` that will then be + // deconstructed to obtain the constant value and other data. + let mut kind: PatKind<'tcx> = self.lower_pat_expr(expr); + + // Unpeel any ascription or inline-const wrapper nodes. + loop { + match kind { + PatKind::AscribeUserType { ascription, subpattern } => { + ascriptions.push(ascription); + kind = subpattern.kind; + } + PatKind::ExpandedConstant { is_inline, def_id, subpattern } => { + if is_inline { + inline_consts.extend(def_id.as_local()); } - }; - Ok((Some(PatRangeBoundary::Finite(value)), ascr, inline_const)) + kind = subpattern.kind; + } + _ => break, } } + + // The unpeeled kind should now be a constant, giving us the endpoint value. + let PatKind::Constant { value } = kind else { + let msg = + format!("found bad range pattern endpoint `{expr:?}` outside of error recovery"); + return Err(self.tcx.dcx().span_delayed_bug(expr.span, msg)); + }; + + Ok(Some(PatRangeBoundary::Finite(value))) } /// Overflowing literals are linted against in a late pass. This is mostly fine, except when we @@ -200,13 +225,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// This is only called when the range is already known to be malformed. fn error_on_literal_overflow( &self, - expr: Option<&'tcx hir::Expr<'tcx>>, + expr: Option<&'tcx hir::PatExpr<'tcx>>, ty: Ty<'tcx>, ) -> Result<(), ErrorGuaranteed> { - use hir::{ExprKind, UnOp}; use rustc_ast::ast::LitKind; - let Some(mut expr) = expr else { + let Some(expr) = expr else { return Ok(()); }; let span = expr.span; @@ -214,12 +238,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // We need to inspect the original expression, because if we only inspect the output of // `eval_bits`, an overflowed value has already been wrapped around. // We mostly copy the logic from the `rustc_lint::OVERFLOWING_LITERALS` lint. - let mut negated = false; - if let ExprKind::Unary(UnOp::Neg, sub_expr) = expr.kind { - negated = true; - expr = sub_expr; - } - let ExprKind::Lit(lit) = expr.kind else { + let hir::PatExprKind::Lit { lit, negated } = expr.kind else { return Ok(()); }; let LitKind::Int(lit_val, _) = lit.node else { @@ -248,8 +267,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { fn lower_pattern_range( &mut self, - lo_expr: Option<&'tcx hir::Expr<'tcx>>, - hi_expr: Option<&'tcx hir::Expr<'tcx>>, + lo_expr: Option<&'tcx hir::PatExpr<'tcx>>, + hi_expr: Option<&'tcx hir::PatExpr<'tcx>>, end: RangeEnd, ty: Ty<'tcx>, span: Span, @@ -259,14 +278,18 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { self.tcx.dcx().span_bug(span, msg); } - let (lo, lo_ascr, lo_inline) = self.lower_pattern_range_endpoint(lo_expr)?; - let (hi, hi_ascr, hi_inline) = self.lower_pattern_range_endpoint(hi_expr)?; + // Collect extra data while lowering the endpoints, to be reapplied later. + let mut ascriptions = vec![]; + let mut inline_consts = vec![]; - let lo = lo.unwrap_or(PatRangeBoundary::NegInfinity); - let hi = hi.unwrap_or(PatRangeBoundary::PosInfinity); + let mut lower_endpoint = + |expr| self.lower_pattern_range_endpoint(expr, &mut ascriptions, &mut inline_consts); + + let lo = lower_endpoint(lo_expr)?.unwrap_or(PatRangeBoundary::NegInfinity); + let hi = lower_endpoint(hi_expr)?.unwrap_or(PatRangeBoundary::PosInfinity); let cmp = lo.compare_with(hi, ty, self.tcx, self.typing_env); - let mut kind = PatKind::Range(Box::new(PatRange { lo, hi, end, ty })); + let mut kind = PatKind::Range(Arc::new(PatRange { lo, hi, end, ty })); match (end, cmp) { // `x..y` where `x < y`. (RangeEnd::Excluded, Some(Ordering::Less)) => {} @@ -304,13 +327,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { // If we are handling a range with associated constants (e.g. // `Foo::<'a>::A..=Foo::B`), we need to put the ascriptions for the associated // constants somewhere. Have them on the range pattern. - for ascription in [lo_ascr, hi_ascr].into_iter().flatten() { + for ascription in ascriptions { kind = PatKind::AscribeUserType { ascription, subpattern: Box::new(Pat { span, ty, kind }), }; } - for def in [lo_inline, hi_inline].into_iter().flatten() { + for def in inline_consts { kind = PatKind::ExpandedConstant { def_id: def.to_def_id(), is_inline: true, @@ -330,7 +353,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { hir::PatKind::Never => PatKind::Never, - hir::PatKind::Lit(value) => self.lower_lit(value), + hir::PatKind::Expr(value) => self.lower_pat_expr(value), hir::PatKind::Range(ref lo_expr, ref hi_expr, end) => { let (lo_expr, hi_expr) = (lo_expr.as_deref(), hi_expr.as_deref()); @@ -338,16 +361,27 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { .unwrap_or_else(PatKind::Error) } - hir::PatKind::Path(ref qpath) => { - return self.lower_path(qpath, pat.hir_id, pat.span); - } - hir::PatKind::Deref(subpattern) => { let mutable = self.typeck_results.pat_has_ref_mut_binding(subpattern); let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not }; PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern), mutability } } - hir::PatKind::Ref(subpattern, _) | hir::PatKind::Box(subpattern) => { + hir::PatKind::Ref(subpattern, _) => { + // Track the default binding mode for the Rust 2024 migration suggestion. + let old_mode_span = self.rust_2024_migration_suggestion.as_mut().and_then(|s| { + if let Some((default_mode_span, default_ref_mutbl)) = s.default_mode_span { + // If this eats a by-ref default binding mode, label the binding mode. + s.default_mode_labels.insert(default_mode_span, default_ref_mutbl); + } + s.default_mode_span.take() + }); + let subpattern = self.lower_pattern(subpattern); + if let Some(s) = &mut self.rust_2024_migration_suggestion { + s.default_mode_span = old_mode_span; + } + PatKind::Deref { subpattern } + } + hir::PatKind::Box(subpattern) => { PatKind::Deref { subpattern: self.lower_pattern(subpattern) } } @@ -374,19 +408,32 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { .get(pat.hir_id) .expect("missing binding mode"); - if let Some(s) = &mut self.rust_2024_migration_suggestion - && explicit_ba.0 == ByRef::No - && let ByRef::Yes(mutbl) = mode.0 - { - let sugg_str = match mutbl { - Mutability::Not => "ref ", - Mutability::Mut => "ref mut ", - }; - s.suggestion.push(( - pat.span.with_lo(ident.span.lo()).shrink_to_lo(), - sugg_str.to_owned(), - )); - s.binding_mode_count += 1; + if let Some(s) = &mut self.rust_2024_migration_suggestion { + if explicit_ba != hir::BindingMode::NONE + && let Some((default_mode_span, default_ref_mutbl)) = s.default_mode_span + { + // If this overrides a by-ref default binding mode, label the binding mode. + s.default_mode_labels.insert(default_mode_span, default_ref_mutbl); + // If our suggestion is to elide redundnt modes, this will be one of them. + if s.suggest_eliding_modes { + s.suggestion.push((pat.span.with_hi(ident.span.lo()), String::new())); + s.binding_mode_count += 1; + } + } + if !s.suggest_eliding_modes + && explicit_ba.0 == ByRef::No + && let ByRef::Yes(mutbl) = mode.0 + { + let sugg_str = match mutbl { + Mutability::Not => "ref ", + Mutability::Mut => "ref mut ", + }; + s.suggestion.push(( + pat.span.with_lo(ident.span.lo()).shrink_to_lo(), + sugg_str.to_owned(), + )); + s.binding_mode_count += 1; + } } // A ref x pattern is the same node used for x, and as such it has @@ -426,7 +473,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { .iter() .map(|field| FieldPat { field: self.typeck_results.field_index(field.hir_id), - pattern: self.lower_pattern(field.pat), + pattern: *self.lower_pattern(field.pat), }) .collect(); @@ -435,6 +482,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { hir::PatKind::Or(pats) => PatKind::Or { pats: self.lower_patterns(pats) }, + // FIXME(guard_patterns): implement guard pattern lowering + hir::PatKind::Guard(pat, _) => self.lower_pattern(pat).kind, + hir::PatKind::Err(guar) => PatKind::Error(guar), }; @@ -451,13 +501,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { .enumerate_and_adjust(expected_len, gap_pos) .map(|(i, subpattern)| FieldPat { field: FieldIdx::new(i), - pattern: self.lower_pattern(subpattern), + pattern: *self.lower_pattern(subpattern), }) .collect() } - fn lower_patterns(&mut self, pats: &'tcx [hir::Pat<'tcx>]) -> Box<[Box>]> { - pats.iter().map(|p| self.lower_pattern(p)).collect() + fn lower_patterns(&mut self, pats: &'tcx [hir::Pat<'tcx>]) -> Box<[Pat<'tcx>]> { + pats.iter().map(|p| *self.lower_pattern(p)).collect() } fn lower_opt_pattern(&mut self, pat: Option<&'tcx hir::Pat<'tcx>>) -> Option>> { @@ -544,16 +594,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { _ => { let e = match res { Res::Def(DefKind::ConstParam, def_id) => { - self.tcx.dcx().emit_err(ConstParamInPattern { - span, - const_span: self.tcx().def_span(def_id), - }) + let const_span = self.tcx.def_span(def_id); + self.tcx.dcx().emit_err(ConstParamInPattern { span, const_span }) } Res::Def(DefKind::Static { .. }, def_id) => { - self.tcx.dcx().emit_err(StaticInPattern { - span, - static_span: self.tcx().def_span(def_id), - }) + let static_span = self.tcx.def_span(def_id); + self.tcx.dcx().emit_err(StaticInPattern { span, static_span }) } _ => self.tcx.dcx().emit_err(NonConstPath { span }), }; @@ -577,6 +623,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { kind } + fn user_args_applied_to_ty_of_hir_id( + &self, + hir_id: hir::HirId, + ) -> Option> { + crate::thir::util::user_args_applied_to_ty_of_hir_id(self.typeck_results, hir_id) + } + /// Takes a HIR Path. If the path is a constant, evaluates it and feeds /// it to `const_to_pat`. Any other path (like enum variants without fields) /// is converted to the corresponding pattern via `lower_variant_or_leaf`. @@ -585,54 +638,57 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let ty = self.typeck_results.node_type(id); let res = self.typeck_results.qpath_res(qpath, id); - let pat_from_kind = |kind| Box::new(Pat { span, ty, kind }); - - let (def_id, is_associated_const) = match res { - Res::Def(DefKind::Const, def_id) => (def_id, false), - Res::Def(DefKind::AssocConst, def_id) => (def_id, true), + let (def_id, user_ty) = match res { + Res::Def(DefKind::Const, def_id) => (def_id, None), + Res::Def(DefKind::AssocConst, def_id) => { + (def_id, self.typeck_results.user_provided_types().get(id)) + } - _ => return pat_from_kind(self.lower_variant_or_leaf(res, id, span, ty, vec![])), + _ => { + // The path isn't the name of a constant, so it must actually + // be a unit struct or unit variant (e.g. `Option::None`). + let kind = self.lower_variant_or_leaf(res, id, span, ty, vec![]); + return Box::new(Pat { span, ty, kind }); + } }; + // Lower the named constant to a THIR pattern. let args = self.typeck_results.node_args(id); let c = ty::Const::new_unevaluated(self.tcx, ty::UnevaluatedConst { def: def_id, args }); let subpattern = self.const_to_pat(c, ty, id, span); - let pattern = Box::new(Pat { - span, - ty, - kind: PatKind::ExpandedConstant { subpattern, def_id, is_inline: false }, - }); - if !is_associated_const { - return pattern; - } + // Wrap the pattern in a marker node to indicate that it is the result + // of lowering a named constant. This marker is used for improved + // diagnostics in some situations, but has no effect at runtime. + let mut pattern = { + let kind = PatKind::ExpandedConstant { subpattern, def_id, is_inline: false }; + Box::new(Pat { span, ty, kind }) + }; - let user_provided_types = self.typeck_results().user_provided_types(); - if let Some(&user_ty) = user_provided_types.get(id) { + // If this is an associated constant with an explicit user-written + // type, add an ascription node (e.g. ` as MyTrait>::CONST`). + if let Some(&user_ty) = user_ty { let annotation = CanonicalUserTypeAnnotation { user_ty: Box::new(user_ty), span, - inferred_ty: self.typeck_results().node_type(id), + inferred_ty: self.typeck_results.node_type(id), }; - Box::new(Pat { - span, - kind: PatKind::AscribeUserType { - subpattern: pattern, - ascription: Ascription { - annotation, - // Note that use `Contravariant` here. See the - // `variance` field documentation for details. - variance: ty::Contravariant, - }, + let kind = PatKind::AscribeUserType { + subpattern: pattern, + ascription: Ascription { + annotation, + // Note that we use `Contravariant` here. See the + // `variance` field documentation for details. + variance: ty::Contravariant, }, - ty, - }) - } else { - pattern + }; + pattern = Box::new(Pat { span, kind, ty }); } + + pattern } - /// Converts inline const patterns. + /// Lowers an inline const block (e.g. `const { 1 + 1 }`) to a pattern. fn lower_inline_const( &mut self, block: &'tcx hir::ConstBlock, @@ -652,47 +708,30 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let ct = ty::UnevaluatedConst { def: def_id.to_def_id(), args }; let subpattern = self.const_to_pat(ty::Const::new_unevaluated(self.tcx, ct), ty, id, span); + + // Wrap the pattern in a marker node to indicate that it is the result + // of lowering an inline const block. PatKind::ExpandedConstant { subpattern, def_id: def_id.to_def_id(), is_inline: true } } - /// Converts literals, paths and negation of literals to patterns. - /// The special case for negation exists to allow things like `-128_i8` - /// which would overflow if we tried to evaluate `128_i8` and then negate - /// afterwards. - fn lower_lit(&mut self, expr: &'tcx hir::Expr<'tcx>) -> PatKind<'tcx> { - let (lit, neg) = match expr.kind { - hir::ExprKind::Path(ref qpath) => { + /// Lowers the kinds of "expression" that can appear in a HIR pattern: + /// - Paths (e.g. `FOO`, `foo::BAR`, `Option::None`) + /// - Inline const blocks (e.g. `const { 1 + 1 }`) + /// - Literals, possibly negated (e.g. `-128u8`, `"hello"`) + fn lower_pat_expr(&mut self, expr: &'tcx hir::PatExpr<'tcx>) -> PatKind<'tcx> { + let (lit, neg) = match &expr.kind { + hir::PatExprKind::Path(qpath) => { return self.lower_path(qpath, expr.hir_id, expr.span).kind; } - hir::ExprKind::ConstBlock(ref anon_const) => { + hir::PatExprKind::ConstBlock(anon_const) => { return self.lower_inline_const(anon_const, expr.hir_id, expr.span); } - hir::ExprKind::Lit(ref lit) => (lit, false), - hir::ExprKind::Unary(hir::UnOp::Neg, ref expr) => { - let hir::ExprKind::Lit(ref lit) = expr.kind else { - span_bug!(expr.span, "not a literal: {:?}", expr); - }; - (lit, true) - } - _ => span_bug!(expr.span, "not a literal: {:?}", expr), + hir::PatExprKind::Lit { lit, negated } => (lit, *negated), }; - let ct_ty = self.typeck_results.expr_ty(expr); + let ct_ty = self.typeck_results.node_type(expr.hir_id); let lit_input = LitToConstInput { lit: &lit.node, ty: ct_ty, neg }; - match self.tcx.at(expr.span).lit_to_const(lit_input) { - Ok(constant) => self.const_to_pat(constant, ct_ty, expr.hir_id, lit.span).kind, - Err(LitToConstError::Reported(e)) => PatKind::Error(e), - Err(LitToConstError::TypeError) => bug!("lower_lit: had type error"), - } - } -} - -impl<'tcx> UserAnnotatedTyHelpers<'tcx> for PatCtxt<'_, 'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn typeck_results(&self) -> &ty::TypeckResults<'tcx> { - self.typeck_results + let constant = self.tcx.at(expr.span).lit_to_const(lit_input); + self.const_to_pat(constant, ct_ty, expr.hir_id, lit.span).kind } } diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index 2bcdb67c58a98..9ab87dd99ffc0 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -1,12 +1,13 @@ use std::fmt::{self, Write}; -use rustc_middle::query::TyCtxtAt; use rustc_middle::thir::*; use rustc_middle::ty; +use rustc_middle::ty::TyCtxt; use rustc_span::def_id::LocalDefId; -pub(crate) fn thir_tree(tcx: TyCtxtAt<'_>, owner_def: LocalDefId) -> String { - match super::cx::thir_body(*tcx, owner_def) { +/// Create a THIR tree for debugging. +pub fn thir_tree(tcx: TyCtxt<'_>, owner_def: LocalDefId) -> String { + match super::cx::thir_body(tcx, owner_def) { Ok((thir, _)) => { let thir = thir.steal(); let mut printer = ThirPrinter::new(&thir); @@ -17,8 +18,9 @@ pub(crate) fn thir_tree(tcx: TyCtxtAt<'_>, owner_def: LocalDefId) -> String { } } -pub(crate) fn thir_flat(tcx: TyCtxtAt<'_>, owner_def: LocalDefId) -> String { - match super::cx::thir_body(*tcx, owner_def) { +/// Create a list-like THIR representation for debugging. +pub fn thir_flat(tcx: TyCtxt<'_>, owner_def: LocalDefId) -> String { + match super::cx::thir_body(tcx, owner_def) { Ok((thir, _)) => format!("{:#?}", thir.steal()), Err(_) => "error".into(), } @@ -475,6 +477,24 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { self.print_expr(*source, depth_lvl + 2); print_indented!(self, "}", depth_lvl); } + PlaceUnwrapUnsafeBinder { source } => { + print_indented!(self, "PlaceUnwrapUnsafeBinder {", depth_lvl); + print_indented!(self, "source:", depth_lvl + 1); + self.print_expr(*source, depth_lvl + 2); + print_indented!(self, "}", depth_lvl); + } + ValueUnwrapUnsafeBinder { source } => { + print_indented!(self, "ValueUnwrapUnsafeBinder {", depth_lvl); + print_indented!(self, "source:", depth_lvl + 1); + self.print_expr(*source, depth_lvl + 2); + print_indented!(self, "}", depth_lvl); + } + WrapUnsafeBinder { source } => { + print_indented!(self, "WrapUnsafeBinder {", depth_lvl); + print_indented!(self, "source:", depth_lvl + 1); + self.print_expr(*source, depth_lvl + 2); + print_indented!(self, "}", depth_lvl); + } Closure(closure_expr) => { print_indented!(self, "Closure {", depth_lvl); print_indented!(self, "closure_expr:", depth_lvl + 1); @@ -623,8 +643,8 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, "}", depth_lvl); } - fn print_pat(&mut self, pat: &Box>, depth_lvl: usize) { - let Pat { ty, span, kind } = &**pat; + fn print_pat(&mut self, pat: &Pat<'tcx>, depth_lvl: usize) { + let &Pat { ty, span, ref kind } = pat; print_indented!(self, "Pat: {", depth_lvl); print_indented!(self, format!("ty: {:?}", ty), depth_lvl + 1); diff --git a/compiler/rustc_mir_build/src/thir/util.rs b/compiler/rustc_mir_build/src/thir/util.rs index ed7c7e4099304..4dff093afd0d9 100644 --- a/compiler/rustc_mir_build/src/thir/util.rs +++ b/compiler/rustc_mir_build/src/thir/util.rs @@ -1,33 +1,27 @@ use rustc_hir as hir; use rustc_middle::bug; -use rustc_middle::ty::{self, CanonicalUserType, TyCtxt}; +use rustc_middle::ty::{self, CanonicalUserType}; use tracing::debug; -pub(crate) trait UserAnnotatedTyHelpers<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx>; - - fn typeck_results(&self) -> &ty::TypeckResults<'tcx>; - - /// Looks up the type associated with this hir-id and applies the - /// user-given generic parameters; the hir-id must map to a suitable - /// type. - fn user_args_applied_to_ty_of_hir_id( - &self, - hir_id: hir::HirId, - ) -> Option> { - let user_provided_types = self.typeck_results().user_provided_types(); - let mut user_ty = *user_provided_types.get(hir_id)?; - debug!("user_subts_applied_to_ty_of_hir_id: user_ty={:?}", user_ty); - let ty = self.typeck_results().node_type(hir_id); - match ty.kind() { - ty::Adt(adt_def, ..) => { - if let ty::UserTypeKind::TypeOf(ref mut did, _) = &mut user_ty.value.kind { - *did = adt_def.did(); - } - Some(user_ty) +/// Looks up the type associated with this hir-id and applies the +/// user-given generic parameters; the hir-id must map to a suitable +/// type. +pub(crate) fn user_args_applied_to_ty_of_hir_id<'tcx>( + typeck_results: &ty::TypeckResults<'tcx>, + hir_id: hir::HirId, +) -> Option> { + let user_provided_types = typeck_results.user_provided_types(); + let mut user_ty = *user_provided_types.get(hir_id)?; + debug!("user_subts_applied_to_ty_of_hir_id: user_ty={:?}", user_ty); + let ty = typeck_results.node_type(hir_id); + match ty.kind() { + ty::Adt(adt_def, ..) => { + if let ty::UserTypeKind::TypeOf(ref mut did, _) = &mut user_ty.value.kind { + *did = adt_def.did(); } - ty::FnDef(..) => Some(user_ty), - _ => bug!("ty: {:?} should not have user provided type {:?} recorded ", ty, user_ty), + Some(user_ty) } + ty::FnDef(..) => Some(user_ty), + _ => bug!("ty: {:?} should not have user provided type {:?} recorded ", ty, user_ty), } } diff --git a/compiler/rustc_mir_dataflow/src/debuginfo.rs b/compiler/rustc_mir_dataflow/src/debuginfo.rs index fd5e8cf295552..0d25ce91c9a9e 100644 --- a/compiler/rustc_mir_dataflow/src/debuginfo.rs +++ b/compiler/rustc_mir_dataflow/src/debuginfo.rs @@ -1,17 +1,17 @@ -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; /// Return the set of locals that appear in debuginfo. -pub fn debuginfo_locals(body: &Body<'_>) -> BitSet { - let mut visitor = DebuginfoLocals(BitSet::new_empty(body.local_decls.len())); +pub fn debuginfo_locals(body: &Body<'_>) -> DenseBitSet { + let mut visitor = DebuginfoLocals(DenseBitSet::new_empty(body.local_decls.len())); for debuginfo in body.var_debug_info.iter() { visitor.visit_var_debug_info(debuginfo); } visitor.0 } -struct DebuginfoLocals(BitSet); +struct DebuginfoLocals(DenseBitSet); impl Visitor<'_> for DebuginfoLocals { fn visit_local(&mut self, local: Local, _: PlaceContext, _: Location) { diff --git a/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs b/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs index e5cddd0e5e4b7..496a342cf4c55 100644 --- a/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs +++ b/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs @@ -3,7 +3,26 @@ use rustc_middle::mir::{self, Body, Location, Terminator, TerminatorKind}; use tracing::debug; use super::move_paths::{InitKind, LookupResult, MoveData, MovePathIndex}; -use crate::elaborate_drops::DropFlagState; + +/// The value of an inserted drop flag. +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum DropFlagState { + /// The tracked value is initialized and needs to be dropped when leaving its scope. + Present, + + /// The tracked value is uninitialized or was moved out of and does not need to be dropped when + /// leaving its scope. + Absent, +} + +impl DropFlagState { + pub fn value(self) -> bool { + match self { + DropFlagState::Present => true, + DropFlagState::Absent => false, + } + } +} pub fn move_path_children_matching<'tcx, F>( move_data: &MoveData<'tcx>, diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs deleted file mode 100644 index f8a8467494753..0000000000000 --- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs +++ /dev/null @@ -1,1036 +0,0 @@ -use std::{fmt, iter, mem}; - -use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; -use rustc_hir::lang_items::LangItem; -use rustc_index::Idx; -use rustc_middle::mir::patch::MirPatch; -use rustc_middle::mir::*; -use rustc_middle::span_bug; -use rustc_middle::ty::adjustment::PointerCoercion; -use rustc_middle::ty::util::IntTypeExt; -use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt}; -use rustc_span::DUMMY_SP; -use rustc_span::source_map::Spanned; -use tracing::{debug, instrument}; - -/// The value of an inserted drop flag. -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -pub enum DropFlagState { - /// The tracked value is initialized and needs to be dropped when leaving its scope. - Present, - - /// The tracked value is uninitialized or was moved out of and does not need to be dropped when - /// leaving its scope. - Absent, -} - -impl DropFlagState { - pub fn value(self) -> bool { - match self { - DropFlagState::Present => true, - DropFlagState::Absent => false, - } - } -} - -/// Describes how/if a value should be dropped. -#[derive(Debug)] -pub enum DropStyle { - /// The value is already dead at the drop location, no drop will be executed. - Dead, - - /// The value is known to always be initialized at the drop location, drop will always be - /// executed. - Static, - - /// Whether the value needs to be dropped depends on its drop flag. - Conditional, - - /// An "open" drop is one where only the fields of a value are dropped. - /// - /// For example, this happens when moving out of a struct field: The rest of the struct will be - /// dropped in such an "open" drop. It is also used to generate drop glue for the individual - /// components of a value, for example for dropping array elements. - Open, -} - -/// Which drop flags to affect/check with an operation. -#[derive(Debug)] -pub enum DropFlagMode { - /// Only affect the top-level drop flag, not that of any contained fields. - Shallow, - /// Affect all nested drop flags in addition to the top-level one. - Deep, -} - -/// Describes if unwinding is necessary and where to unwind to if a panic occurs. -#[derive(Copy, Clone, Debug)] -pub enum Unwind { - /// Unwind to this block. - To(BasicBlock), - /// Already in an unwind path, any panic will cause an abort. - InCleanup, -} - -impl Unwind { - fn is_cleanup(self) -> bool { - match self { - Unwind::To(..) => false, - Unwind::InCleanup => true, - } - } - - fn into_action(self) -> UnwindAction { - match self { - Unwind::To(bb) => UnwindAction::Cleanup(bb), - Unwind::InCleanup => UnwindAction::Terminate(UnwindTerminateReason::InCleanup), - } - } - - fn map(self, f: F) -> Self - where - F: FnOnce(BasicBlock) -> BasicBlock, - { - match self { - Unwind::To(bb) => Unwind::To(f(bb)), - Unwind::InCleanup => Unwind::InCleanup, - } - } -} - -pub trait DropElaborator<'a, 'tcx>: fmt::Debug { - /// The type representing paths that can be moved out of. - /// - /// Users can move out of individual fields of a struct, such as `a.b.c`. This type is used to - /// represent such move paths. Sometimes tracking individual move paths is not necessary, in - /// which case this may be set to (for example) `()`. - type Path: Copy + fmt::Debug; - - // Accessors - - fn patch(&mut self) -> &mut MirPatch<'tcx>; - fn body(&self) -> &'a Body<'tcx>; - fn tcx(&self) -> TyCtxt<'tcx>; - fn typing_env(&self) -> ty::TypingEnv<'tcx>; - - // Drop logic - - /// Returns how `path` should be dropped, given `mode`. - fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle; - - /// Returns the drop flag of `path` as a MIR `Operand` (or `None` if `path` has no drop flag). - fn get_drop_flag(&mut self, path: Self::Path) -> Option>; - - /// Modifies the MIR patch so that the drop flag of `path` (if any) is cleared at `location`. - /// - /// If `mode` is deep, drop flags of all child paths should also be cleared by inserting - /// additional statements. - fn clear_drop_flag(&mut self, location: Location, path: Self::Path, mode: DropFlagMode); - - // Subpaths - - /// Returns the subpath of a field of `path` (or `None` if there is no dedicated subpath). - /// - /// If this returns `None`, `field` will not get a dedicated drop flag. - fn field_subpath(&self, path: Self::Path, field: FieldIdx) -> Option; - - /// Returns the subpath of a dereference of `path` (or `None` if there is no dedicated subpath). - /// - /// If this returns `None`, `*path` will not get a dedicated drop flag. - /// - /// This is only relevant for `Box`, where the contained `T` can be moved out of the box. - fn deref_subpath(&self, path: Self::Path) -> Option; - - /// Returns the subpath of downcasting `path` to one of its variants. - /// - /// If this returns `None`, the downcast of `path` will not get a dedicated drop flag. - fn downcast_subpath(&self, path: Self::Path, variant: VariantIdx) -> Option; - - /// Returns the subpath of indexing a fixed-size array `path`. - /// - /// If this returns `None`, elements of `path` will not get a dedicated drop flag. - /// - /// This is only relevant for array patterns, which can move out of individual array elements. - fn array_subpath(&self, path: Self::Path, index: u64, size: u64) -> Option; -} - -#[derive(Debug)] -struct DropCtxt<'a, 'b, 'tcx, D> -where - D: DropElaborator<'b, 'tcx>, -{ - elaborator: &'a mut D, - - source_info: SourceInfo, - - place: Place<'tcx>, - path: D::Path, - succ: BasicBlock, - unwind: Unwind, -} - -/// "Elaborates" a drop of `place`/`path` and patches `bb`'s terminator to execute it. -/// -/// The passed `elaborator` is used to determine what should happen at the drop terminator. It -/// decides whether the drop can be statically determined or whether it needs a dynamic drop flag, -/// and whether the drop is "open", ie. should be expanded to drop all subfields of the dropped -/// value. -/// -/// When this returns, the MIR patch in the `elaborator` contains the necessary changes. -pub fn elaborate_drop<'b, 'tcx, D>( - elaborator: &mut D, - source_info: SourceInfo, - place: Place<'tcx>, - path: D::Path, - succ: BasicBlock, - unwind: Unwind, - bb: BasicBlock, -) where - D: DropElaborator<'b, 'tcx>, - 'tcx: 'b, -{ - DropCtxt { elaborator, source_info, place, path, succ, unwind }.elaborate_drop(bb) -} - -impl<'a, 'b, 'tcx, D> DropCtxt<'a, 'b, 'tcx, D> -where - D: DropElaborator<'b, 'tcx>, - 'tcx: 'b, -{ - #[instrument(level = "trace", skip(self), ret)] - fn place_ty(&self, place: Place<'tcx>) -> Ty<'tcx> { - place.ty(self.elaborator.body(), self.tcx()).ty - } - - fn tcx(&self) -> TyCtxt<'tcx> { - self.elaborator.tcx() - } - - /// This elaborates a single drop instruction, located at `bb`, and - /// patches over it. - /// - /// The elaborated drop checks the drop flags to only drop what - /// is initialized. - /// - /// In addition, the relevant drop flags also need to be cleared - /// to avoid double-drops. However, in the middle of a complex - /// drop, one must avoid clearing some of the flags before they - /// are read, as that would cause a memory leak. - /// - /// In particular, when dropping an ADT, multiple fields may be - /// joined together under the `rest` subpath. They are all controlled - /// by the primary drop flag, but only the last rest-field dropped - /// should clear it (and it must also not clear anything else). - // - // FIXME: I think we should just control the flags externally, - // and then we do not need this machinery. - #[instrument(level = "debug")] - fn elaborate_drop(&mut self, bb: BasicBlock) { - match self.elaborator.drop_style(self.path, DropFlagMode::Deep) { - DropStyle::Dead => { - self.elaborator - .patch() - .patch_terminator(bb, TerminatorKind::Goto { target: self.succ }); - } - DropStyle::Static => { - self.elaborator.patch().patch_terminator(bb, TerminatorKind::Drop { - place: self.place, - target: self.succ, - unwind: self.unwind.into_action(), - replace: false, - }); - } - DropStyle::Conditional => { - let drop_bb = self.complete_drop(self.succ, self.unwind); - self.elaborator - .patch() - .patch_terminator(bb, TerminatorKind::Goto { target: drop_bb }); - } - DropStyle::Open => { - let drop_bb = self.open_drop(); - self.elaborator - .patch() - .patch_terminator(bb, TerminatorKind::Goto { target: drop_bb }); - } - } - } - - /// Returns the place and move path for each field of `variant`, - /// (the move path is `None` if the field is a rest field). - fn move_paths_for_fields( - &self, - base_place: Place<'tcx>, - variant_path: D::Path, - variant: &'tcx ty::VariantDef, - args: GenericArgsRef<'tcx>, - ) -> Vec<(Place<'tcx>, Option)> { - variant - .fields - .iter() - .enumerate() - .map(|(i, f)| { - let field = FieldIdx::new(i); - let subpath = self.elaborator.field_subpath(variant_path, field); - let tcx = self.tcx(); - - assert_eq!(self.elaborator.typing_env().typing_mode, ty::TypingMode::PostAnalysis); - let field_ty = - tcx.normalize_erasing_regions(self.elaborator.typing_env(), f.ty(tcx, args)); - - (tcx.mk_place_field(base_place, field, field_ty), subpath) - }) - .collect() - } - - fn drop_subpath( - &mut self, - place: Place<'tcx>, - path: Option, - succ: BasicBlock, - unwind: Unwind, - ) -> BasicBlock { - if let Some(path) = path { - debug!("drop_subpath: for std field {:?}", place); - - DropCtxt { - elaborator: self.elaborator, - source_info: self.source_info, - path, - place, - succ, - unwind, - } - .elaborated_drop_block() - } else { - debug!("drop_subpath: for rest field {:?}", place); - - DropCtxt { - elaborator: self.elaborator, - source_info: self.source_info, - place, - succ, - unwind, - // Using `self.path` here to condition the drop on - // our own drop flag. - path: self.path, - } - .complete_drop(succ, unwind) - } - } - - /// Creates one-half of the drop ladder for a list of fields, and return - /// the list of steps in it in reverse order, with the first step - /// dropping 0 fields and so on. - /// - /// `unwind_ladder` is such a list of steps in reverse order, - /// which is called if the matching step of the drop glue panics. - fn drop_halfladder( - &mut self, - unwind_ladder: &[Unwind], - mut succ: BasicBlock, - fields: &[(Place<'tcx>, Option)], - ) -> Vec { - iter::once(succ) - .chain(fields.iter().rev().zip(unwind_ladder).map(|(&(place, path), &unwind_succ)| { - succ = self.drop_subpath(place, path, succ, unwind_succ); - succ - })) - .collect() - } - - fn drop_ladder_bottom(&mut self) -> (BasicBlock, Unwind) { - // Clear the "master" drop flag at the end. This is needed - // because the "master" drop protects the ADT's discriminant, - // which is invalidated after the ADT is dropped. - (self.drop_flag_reset_block(DropFlagMode::Shallow, self.succ, self.unwind), self.unwind) - } - - /// Creates a full drop ladder, consisting of 2 connected half-drop-ladders - /// - /// For example, with 3 fields, the drop ladder is - /// - /// .d0: - /// ELAB(drop location.0 [target=.d1, unwind=.c1]) - /// .d1: - /// ELAB(drop location.1 [target=.d2, unwind=.c2]) - /// .d2: - /// ELAB(drop location.2 [target=`self.succ`, unwind=`self.unwind`]) - /// .c1: - /// ELAB(drop location.1 [target=.c2]) - /// .c2: - /// ELAB(drop location.2 [target=`self.unwind`]) - /// - /// NOTE: this does not clear the master drop flag, so you need - /// to point succ/unwind on a `drop_ladder_bottom`. - fn drop_ladder( - &mut self, - fields: Vec<(Place<'tcx>, Option)>, - succ: BasicBlock, - unwind: Unwind, - ) -> (BasicBlock, Unwind) { - debug!("drop_ladder({:?}, {:?})", self, fields); - - let mut fields = fields; - fields.retain(|&(place, _)| { - self.place_ty(place).needs_drop(self.tcx(), self.elaborator.typing_env()) - }); - - debug!("drop_ladder - fields needing drop: {:?}", fields); - - let unwind_ladder = vec![Unwind::InCleanup; fields.len() + 1]; - let unwind_ladder: Vec<_> = if let Unwind::To(target) = unwind { - let halfladder = self.drop_halfladder(&unwind_ladder, target, &fields); - halfladder.into_iter().map(Unwind::To).collect() - } else { - unwind_ladder - }; - - let normal_ladder = self.drop_halfladder(&unwind_ladder, succ, &fields); - - (*normal_ladder.last().unwrap(), *unwind_ladder.last().unwrap()) - } - - fn open_drop_for_tuple(&mut self, tys: &[Ty<'tcx>]) -> BasicBlock { - debug!("open_drop_for_tuple({:?}, {:?})", self, tys); - - let fields = tys - .iter() - .enumerate() - .map(|(i, &ty)| { - ( - self.tcx().mk_place_field(self.place, FieldIdx::new(i), ty), - self.elaborator.field_subpath(self.path, FieldIdx::new(i)), - ) - }) - .collect(); - - let (succ, unwind) = self.drop_ladder_bottom(); - self.drop_ladder(fields, succ, unwind).0 - } - - /// Drops the T contained in a `Box` if it has not been moved out of - #[instrument(level = "debug", ret)] - fn open_drop_for_box_contents( - &mut self, - adt: ty::AdtDef<'tcx>, - args: GenericArgsRef<'tcx>, - succ: BasicBlock, - unwind: Unwind, - ) -> BasicBlock { - // drop glue is sent straight to codegen - // box cannot be directly dereferenced - let unique_ty = adt.non_enum_variant().fields[FieldIdx::ZERO].ty(self.tcx(), args); - let unique_variant = unique_ty.ty_adt_def().unwrap().non_enum_variant(); - let nonnull_ty = unique_variant.fields[FieldIdx::ZERO].ty(self.tcx(), args); - let ptr_ty = Ty::new_imm_ptr(self.tcx(), args[0].expect_ty()); - - let unique_place = self.tcx().mk_place_field(self.place, FieldIdx::ZERO, unique_ty); - let nonnull_place = self.tcx().mk_place_field(unique_place, FieldIdx::ZERO, nonnull_ty); - let ptr_place = self.tcx().mk_place_field(nonnull_place, FieldIdx::ZERO, ptr_ty); - let interior = self.tcx().mk_place_deref(ptr_place); - - let interior_path = self.elaborator.deref_subpath(self.path); - - self.drop_subpath(interior, interior_path, succ, unwind) - } - - #[instrument(level = "debug", ret)] - fn open_drop_for_adt( - &mut self, - adt: ty::AdtDef<'tcx>, - args: GenericArgsRef<'tcx>, - ) -> BasicBlock { - if adt.variants().is_empty() { - return self.elaborator.patch().new_block(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { - source_info: self.source_info, - kind: TerminatorKind::Unreachable, - }), - is_cleanup: self.unwind.is_cleanup(), - }); - } - - let skip_contents = adt.is_union() || adt.is_manually_drop(); - let contents_drop = if skip_contents { - (self.succ, self.unwind) - } else { - self.open_drop_for_adt_contents(adt, args) - }; - - if adt.is_box() { - // we need to drop the inside of the box before running the destructor - let succ = self.destructor_call_block(contents_drop); - let unwind = contents_drop - .1 - .map(|unwind| self.destructor_call_block((unwind, Unwind::InCleanup))); - - self.open_drop_for_box_contents(adt, args, succ, unwind) - } else if adt.has_dtor(self.tcx()) { - self.destructor_call_block(contents_drop) - } else { - contents_drop.0 - } - } - - fn open_drop_for_adt_contents( - &mut self, - adt: ty::AdtDef<'tcx>, - args: GenericArgsRef<'tcx>, - ) -> (BasicBlock, Unwind) { - let (succ, unwind) = self.drop_ladder_bottom(); - if !adt.is_enum() { - let fields = - self.move_paths_for_fields(self.place, self.path, adt.variant(FIRST_VARIANT), args); - self.drop_ladder(fields, succ, unwind) - } else { - self.open_drop_for_multivariant(adt, args, succ, unwind) - } - } - - fn open_drop_for_multivariant( - &mut self, - adt: ty::AdtDef<'tcx>, - args: GenericArgsRef<'tcx>, - succ: BasicBlock, - unwind: Unwind, - ) -> (BasicBlock, Unwind) { - let mut values = Vec::with_capacity(adt.variants().len()); - let mut normal_blocks = Vec::with_capacity(adt.variants().len()); - let mut unwind_blocks = - if unwind.is_cleanup() { None } else { Some(Vec::with_capacity(adt.variants().len())) }; - - let mut have_otherwise_with_drop_glue = false; - let mut have_otherwise = false; - let tcx = self.tcx(); - - for (variant_index, discr) in adt.discriminants(tcx) { - let variant = &adt.variant(variant_index); - let subpath = self.elaborator.downcast_subpath(self.path, variant_index); - - if let Some(variant_path) = subpath { - let base_place = tcx.mk_place_elem( - self.place, - ProjectionElem::Downcast(Some(variant.name), variant_index), - ); - let fields = self.move_paths_for_fields(base_place, variant_path, variant, args); - values.push(discr.val); - if let Unwind::To(unwind) = unwind { - // We can't use the half-ladder from the original - // drop ladder, because this breaks the - // "funclet can't have 2 successor funclets" - // requirement from MSVC: - // - // switch unwind-switch - // / \ / \ - // v1.0 v2.0 v2.0-unwind v1.0-unwind - // | | / | - // v1.1-unwind v2.1-unwind | - // ^ | - // \-------------------------------/ - // - // Create a duplicate half-ladder to avoid that. We - // could technically only do this on MSVC, but I - // I want to minimize the divergence between MSVC - // and non-MSVC. - - let unwind_blocks = unwind_blocks.as_mut().unwrap(); - let unwind_ladder = vec![Unwind::InCleanup; fields.len() + 1]; - let halfladder = self.drop_halfladder(&unwind_ladder, unwind, &fields); - unwind_blocks.push(halfladder.last().cloned().unwrap()); - } - let (normal, _) = self.drop_ladder(fields, succ, unwind); - normal_blocks.push(normal); - } else { - have_otherwise = true; - - let typing_env = self.elaborator.typing_env(); - let have_field_with_drop_glue = variant - .fields - .iter() - .any(|field| field.ty(tcx, args).needs_drop(tcx, typing_env)); - if have_field_with_drop_glue { - have_otherwise_with_drop_glue = true; - } - } - } - - if !have_otherwise { - values.pop(); - } else if !have_otherwise_with_drop_glue { - normal_blocks.push(self.goto_block(succ, unwind)); - if let Unwind::To(unwind) = unwind { - unwind_blocks.as_mut().unwrap().push(self.goto_block(unwind, Unwind::InCleanup)); - } - } else { - normal_blocks.push(self.drop_block(succ, unwind)); - if let Unwind::To(unwind) = unwind { - unwind_blocks.as_mut().unwrap().push(self.drop_block(unwind, Unwind::InCleanup)); - } - } - - ( - self.adt_switch_block(adt, normal_blocks, &values, succ, unwind), - unwind.map(|unwind| { - self.adt_switch_block( - adt, - unwind_blocks.unwrap(), - &values, - unwind, - Unwind::InCleanup, - ) - }), - ) - } - - fn adt_switch_block( - &mut self, - adt: ty::AdtDef<'tcx>, - blocks: Vec, - values: &[u128], - succ: BasicBlock, - unwind: Unwind, - ) -> BasicBlock { - // If there are multiple variants, then if something - // is present within the enum the discriminant, tracked - // by the rest path, must be initialized. - // - // Additionally, we do not want to switch on the - // discriminant after it is free-ed, because that - // way lies only trouble. - let discr_ty = adt.repr().discr_type().to_ty(self.tcx()); - let discr = Place::from(self.new_temp(discr_ty)); - let discr_rv = Rvalue::Discriminant(self.place); - let switch_block = BasicBlockData { - statements: vec![self.assign(discr, discr_rv)], - terminator: Some(Terminator { - source_info: self.source_info, - kind: TerminatorKind::SwitchInt { - discr: Operand::Move(discr), - targets: SwitchTargets::new( - values.iter().copied().zip(blocks.iter().copied()), - *blocks.last().unwrap(), - ), - }, - }), - is_cleanup: unwind.is_cleanup(), - }; - let switch_block = self.elaborator.patch().new_block(switch_block); - self.drop_flag_test_block(switch_block, succ, unwind) - } - - fn destructor_call_block(&mut self, (succ, unwind): (BasicBlock, Unwind)) -> BasicBlock { - debug!("destructor_call_block({:?}, {:?})", self, succ); - let tcx = self.tcx(); - let drop_trait = tcx.require_lang_item(LangItem::Drop, None); - let drop_fn = tcx.associated_item_def_ids(drop_trait)[0]; - let ty = self.place_ty(self.place); - - let ref_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, ty); - let ref_place = self.new_temp(ref_ty); - let unit_temp = Place::from(self.new_temp(tcx.types.unit)); - - let result = BasicBlockData { - statements: vec![self.assign( - Place::from(ref_place), - Rvalue::Ref( - tcx.lifetimes.re_erased, - BorrowKind::Mut { kind: MutBorrowKind::Default }, - self.place, - ), - )], - terminator: Some(Terminator { - kind: TerminatorKind::Call { - func: Operand::function_handle( - tcx, - drop_fn, - [ty.into()], - self.source_info.span, - ), - args: [Spanned { node: Operand::Move(Place::from(ref_place)), span: DUMMY_SP }] - .into(), - destination: unit_temp, - target: Some(succ), - unwind: unwind.into_action(), - call_source: CallSource::Misc, - fn_span: self.source_info.span, - }, - source_info: self.source_info, - }), - is_cleanup: unwind.is_cleanup(), - }; - - let destructor_block = self.elaborator.patch().new_block(result); - - let block_start = Location { block: destructor_block, statement_index: 0 }; - self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow); - - self.drop_flag_test_block(destructor_block, succ, unwind) - } - - /// Create a loop that drops an array: - /// - /// ```text - /// loop-block: - /// can_go = cur == len - /// if can_go then succ else drop-block - /// drop-block: - /// ptr = &raw mut P[cur] - /// cur = cur + 1 - /// drop(ptr) - /// ``` - fn drop_loop( - &mut self, - succ: BasicBlock, - cur: Local, - len: Local, - ety: Ty<'tcx>, - unwind: Unwind, - ) -> BasicBlock { - let copy = |place: Place<'tcx>| Operand::Copy(place); - let move_ = |place: Place<'tcx>| Operand::Move(place); - let tcx = self.tcx(); - - let ptr_ty = Ty::new_mut_ptr(tcx, ety); - let ptr = Place::from(self.new_temp(ptr_ty)); - let can_go = Place::from(self.new_temp(tcx.types.bool)); - let one = self.constant_usize(1); - - let drop_block = BasicBlockData { - statements: vec![ - self.assign( - ptr, - Rvalue::RawPtr(Mutability::Mut, tcx.mk_place_index(self.place, cur)), - ), - self.assign( - cur.into(), - Rvalue::BinaryOp(BinOp::Add, Box::new((move_(cur.into()), one))), - ), - ], - is_cleanup: unwind.is_cleanup(), - terminator: Some(Terminator { - source_info: self.source_info, - // this gets overwritten by drop elaboration. - kind: TerminatorKind::Unreachable, - }), - }; - let drop_block = self.elaborator.patch().new_block(drop_block); - - let loop_block = BasicBlockData { - statements: vec![self.assign( - can_go, - Rvalue::BinaryOp(BinOp::Eq, Box::new((copy(Place::from(cur)), copy(len.into())))), - )], - is_cleanup: unwind.is_cleanup(), - terminator: Some(Terminator { - source_info: self.source_info, - kind: TerminatorKind::if_(move_(can_go), succ, drop_block), - }), - }; - let loop_block = self.elaborator.patch().new_block(loop_block); - - self.elaborator.patch().patch_terminator(drop_block, TerminatorKind::Drop { - place: tcx.mk_place_deref(ptr), - target: loop_block, - unwind: unwind.into_action(), - replace: false, - }); - - loop_block - } - - fn open_drop_for_array( - &mut self, - array_ty: Ty<'tcx>, - ety: Ty<'tcx>, - opt_size: Option, - ) -> BasicBlock { - debug!("open_drop_for_array({:?}, {:?}, {:?})", array_ty, ety, opt_size); - let tcx = self.tcx(); - - if let Some(size) = opt_size { - enum ProjectionKind { - Drop(std::ops::Range), - Keep(u64, Path), - } - // Previously, we'd make a projection for every element in the array and create a drop - // ladder if any `array_subpath` was `Some`, i.e. moving out with an array pattern. - // This caused huge memory usage when generating the drops for large arrays, so we instead - // record the *subslices* which are dropped and the *indexes* which are kept - let mut drop_ranges = vec![]; - let mut dropping = true; - let mut start = 0; - for i in 0..size { - let path = self.elaborator.array_subpath(self.path, i, size); - if dropping && path.is_some() { - drop_ranges.push(ProjectionKind::Drop(start..i)); - dropping = false; - } else if !dropping && path.is_none() { - dropping = true; - start = i; - } - if let Some(path) = path { - drop_ranges.push(ProjectionKind::Keep(i, path)); - } - } - if !drop_ranges.is_empty() { - if dropping { - drop_ranges.push(ProjectionKind::Drop(start..size)); - } - let fields = drop_ranges - .iter() - .rev() - .map(|p| { - let (project, path) = match p { - ProjectionKind::Drop(r) => ( - ProjectionElem::Subslice { - from: r.start, - to: r.end, - from_end: false, - }, - None, - ), - &ProjectionKind::Keep(offset, path) => ( - ProjectionElem::ConstantIndex { - offset, - min_length: size, - from_end: false, - }, - Some(path), - ), - }; - (tcx.mk_place_elem(self.place, project), path) - }) - .collect::>(); - let (succ, unwind) = self.drop_ladder_bottom(); - return self.drop_ladder(fields, succ, unwind).0; - } - } - - let array_ptr_ty = Ty::new_mut_ptr(tcx, array_ty); - let array_ptr = self.new_temp(array_ptr_ty); - - let slice_ty = Ty::new_slice(tcx, ety); - let slice_ptr_ty = Ty::new_mut_ptr(tcx, slice_ty); - let slice_ptr = self.new_temp(slice_ptr_ty); - - let mut delegate_block = BasicBlockData { - statements: vec![ - self.assign(Place::from(array_ptr), Rvalue::RawPtr(Mutability::Mut, self.place)), - self.assign( - Place::from(slice_ptr), - Rvalue::Cast( - CastKind::PointerCoercion( - PointerCoercion::Unsize, - CoercionSource::Implicit, - ), - Operand::Move(Place::from(array_ptr)), - slice_ptr_ty, - ), - ), - ], - is_cleanup: self.unwind.is_cleanup(), - terminator: None, - }; - - let array_place = mem::replace( - &mut self.place, - Place::from(slice_ptr).project_deeper(&[PlaceElem::Deref], tcx), - ); - let slice_block = self.drop_loop_pair_for_slice(ety); - self.place = array_place; - - delegate_block.terminator = Some(Terminator { - source_info: self.source_info, - kind: TerminatorKind::Goto { target: slice_block }, - }); - self.elaborator.patch().new_block(delegate_block) - } - - /// Creates a pair of drop-loops of `place`, which drops its contents, even - /// in the case of 1 panic. - fn drop_loop_pair_for_slice(&mut self, ety: Ty<'tcx>) -> BasicBlock { - debug!("drop_loop_pair_for_slice({:?})", ety); - let tcx = self.tcx(); - let len = self.new_temp(tcx.types.usize); - let cur = self.new_temp(tcx.types.usize); - - let unwind = - self.unwind.map(|unwind| self.drop_loop(unwind, cur, len, ety, Unwind::InCleanup)); - - let loop_block = self.drop_loop(self.succ, cur, len, ety, unwind); - - let [PlaceElem::Deref] = self.place.projection.as_slice() else { - span_bug!( - self.source_info.span, - "Expected place for slice drop shim to be *_n, but it's {:?}", - self.place, - ); - }; - - let zero = self.constant_usize(0); - let block = BasicBlockData { - statements: vec![ - self.assign( - len.into(), - Rvalue::UnaryOp( - UnOp::PtrMetadata, - Operand::Copy(Place::from(self.place.local)), - ), - ), - self.assign(cur.into(), Rvalue::Use(zero)), - ], - is_cleanup: unwind.is_cleanup(), - terminator: Some(Terminator { - source_info: self.source_info, - kind: TerminatorKind::Goto { target: loop_block }, - }), - }; - - let drop_block = self.elaborator.patch().new_block(block); - // FIXME(#34708): handle partially-dropped array/slice elements. - let reset_block = self.drop_flag_reset_block(DropFlagMode::Deep, drop_block, unwind); - self.drop_flag_test_block(reset_block, self.succ, unwind) - } - - /// The slow-path - create an "open", elaborated drop for a type - /// which is moved-out-of only partially, and patch `bb` to a jump - /// to it. This must not be called on ADTs with a destructor, - /// as these can't be moved-out-of, except for `Box`, which is - /// special-cased. - /// - /// This creates a "drop ladder" that drops the needed fields of the - /// ADT, both in the success case or if one of the destructors fail. - fn open_drop(&mut self) -> BasicBlock { - let ty = self.place_ty(self.place); - match ty.kind() { - ty::Closure(_, args) => self.open_drop_for_tuple(args.as_closure().upvar_tys()), - ty::CoroutineClosure(_, args) => { - self.open_drop_for_tuple(args.as_coroutine_closure().upvar_tys()) - } - // Note that `elaborate_drops` only drops the upvars of a coroutine, - // and this is ok because `open_drop` here can only be reached - // within that own coroutine's resume function. - // This should only happen for the self argument on the resume function. - // It effectively only contains upvars until the coroutine transformation runs. - // See librustc_body/transform/coroutine.rs for more details. - ty::Coroutine(_, args) => self.open_drop_for_tuple(args.as_coroutine().upvar_tys()), - ty::Tuple(fields) => self.open_drop_for_tuple(fields), - ty::Adt(def, args) => self.open_drop_for_adt(*def, args), - ty::Dynamic(..) => self.complete_drop(self.succ, self.unwind), - ty::Array(ety, size) => { - let size = size.try_to_target_usize(self.tcx()); - self.open_drop_for_array(ty, *ety, size) - } - ty::Slice(ety) => self.drop_loop_pair_for_slice(*ety), - - _ => span_bug!(self.source_info.span, "open drop from non-ADT `{:?}`", ty), - } - } - - fn complete_drop(&mut self, succ: BasicBlock, unwind: Unwind) -> BasicBlock { - debug!("complete_drop(succ={:?}, unwind={:?})", succ, unwind); - - let drop_block = self.drop_block(succ, unwind); - - self.drop_flag_test_block(drop_block, succ, unwind) - } - - /// Creates a block that resets the drop flag. If `mode` is deep, all children drop flags will - /// also be cleared. - fn drop_flag_reset_block( - &mut self, - mode: DropFlagMode, - succ: BasicBlock, - unwind: Unwind, - ) -> BasicBlock { - debug!("drop_flag_reset_block({:?},{:?})", self, mode); - - if unwind.is_cleanup() { - // The drop flag isn't read again on the unwind path, so don't - // bother setting it. - return succ; - } - let block = self.new_block(unwind, TerminatorKind::Goto { target: succ }); - let block_start = Location { block, statement_index: 0 }; - self.elaborator.clear_drop_flag(block_start, self.path, mode); - block - } - - fn elaborated_drop_block(&mut self) -> BasicBlock { - debug!("elaborated_drop_block({:?})", self); - let blk = self.drop_block(self.succ, self.unwind); - self.elaborate_drop(blk); - blk - } - - fn drop_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock { - let block = TerminatorKind::Drop { - place: self.place, - target, - unwind: unwind.into_action(), - replace: false, - }; - self.new_block(unwind, block) - } - - fn goto_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock { - let block = TerminatorKind::Goto { target }; - self.new_block(unwind, block) - } - - /// Returns the block to jump to in order to test the drop flag and execute the drop. - /// - /// Depending on the required `DropStyle`, this might be a generated block with an `if` - /// terminator (for dynamic/open drops), or it might be `on_set` or `on_unset` itself, in case - /// the drop can be statically determined. - fn drop_flag_test_block( - &mut self, - on_set: BasicBlock, - on_unset: BasicBlock, - unwind: Unwind, - ) -> BasicBlock { - let style = self.elaborator.drop_style(self.path, DropFlagMode::Shallow); - debug!( - "drop_flag_test_block({:?},{:?},{:?},{:?}) - {:?}", - self, on_set, on_unset, unwind, style - ); - - match style { - DropStyle::Dead => on_unset, - DropStyle::Static => on_set, - DropStyle::Conditional | DropStyle::Open => { - let flag = self.elaborator.get_drop_flag(self.path).unwrap(); - let term = TerminatorKind::if_(flag, on_set, on_unset); - self.new_block(unwind, term) - } - } - } - - fn new_block(&mut self, unwind: Unwind, k: TerminatorKind<'tcx>) -> BasicBlock { - self.elaborator.patch().new_block(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { source_info: self.source_info, kind: k }), - is_cleanup: unwind.is_cleanup(), - }) - } - - fn new_temp(&mut self, ty: Ty<'tcx>) -> Local { - self.elaborator.patch().new_temp(ty, self.source_info.span) - } - - fn constant_usize(&self, val: u16) -> Operand<'tcx> { - Operand::Constant(Box::new(ConstOperand { - span: self.source_info.span, - user_ty: None, - const_: Const::from_usize(self.tcx(), val.into()), - })) - } - - fn assign(&self, lhs: Place<'tcx>, rhs: Rvalue<'tcx>) -> Statement<'tcx> { - Statement { - source_info: self.source_info, - kind: StatementKind::Assign(Box::new((lhs, rhs))), - } - } -} diff --git a/compiler/rustc_mir_dataflow/src/framework/cursor.rs b/compiler/rustc_mir_dataflow/src/framework/cursor.rs index 89ff93d9943a4..c46ae9775cf68 100644 --- a/compiler/rustc_mir_dataflow/src/framework/cursor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/cursor.rs @@ -4,7 +4,7 @@ use std::cmp::Ordering; use std::ops::{Deref, DerefMut}; #[cfg(debug_assertions)] -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::{self, BasicBlock, Location}; use super::{Analysis, Direction, Effect, EffectIndex, Results}; @@ -71,7 +71,7 @@ where state_needs_reset: bool, #[cfg(debug_assertions)] - reachable_blocks: BitSet, + reachable_blocks: DenseBitSet, } impl<'mir, 'tcx, A> ResultsCursor<'mir, 'tcx, A> diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index 6427fd0fd295b..07517d7edab01 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -305,10 +305,11 @@ impl Direction for Forward { // `exit_state`, so pass it directly to `apply_switch_int_edge_effect` to save // a clone of the dataflow state. let otherwise = targets.otherwise(); - analysis.apply_switch_int_edge_effect(&mut data, exit_state, SwitchIntTarget { - value: None, - target: otherwise, - }); + analysis.apply_switch_int_edge_effect( + &mut data, + exit_state, + SwitchIntTarget { value: None, target: otherwise }, + ); propagate(otherwise, exit_state); } else { for target in targets.all_targets() { diff --git a/compiler/rustc_mir_dataflow/src/framework/fmt.rs b/compiler/rustc_mir_dataflow/src/framework/fmt.rs index faf2c411ddefe..38599cd094933 100644 --- a/compiler/rustc_mir_dataflow/src/framework/fmt.rs +++ b/compiler/rustc_mir_dataflow/src/framework/fmt.rs @@ -4,7 +4,7 @@ use std::fmt; use rustc_index::Idx; -use rustc_index::bit_set::{BitSet, ChunkedBitSet, MixedBitSet}; +use rustc_index::bit_set::{ChunkedBitSet, DenseBitSet, MixedBitSet}; use super::lattice::MaybeReachable; @@ -73,7 +73,7 @@ where // Impls -impl DebugWithContext for BitSet +impl DebugWithContext for DenseBitSet where T: Idx + DebugWithContext, { diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index 60c97710c7feb..448fad2dc3ece 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -9,7 +9,7 @@ use std::{io, ops, str}; use regex::Regex; use rustc_hir::def_id::DefId; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::{ self, BasicBlock, Body, Location, create_dump_file, dump_enabled, graphviz_safe_def_name, traversal, @@ -205,7 +205,7 @@ where // the operations that involve the mutation, i.e. within the `borrow_mut`. cursor: RefCell>, style: OutputStyle, - reachable: BitSet, + reachable: DenseBitSet, } impl<'mir, 'tcx, A> Formatter<'mir, 'tcx, A> @@ -608,7 +608,7 @@ where let before = diffs_before.as_mut().map(next_in_dataflow_order); assert!(diffs_after.is_empty()); - assert!(diffs_before.as_ref().map_or(true, ExactSizeIterator::is_empty)); + assert!(diffs_before.as_ref().is_none_or(ExactSizeIterator::is_empty)); let terminator = self.cursor.body()[block].terminator(); let mut terminator_str = String::new(); @@ -670,10 +670,10 @@ where r#"{state}"#, colspan = this.style.num_state_columns(), fmt = fmt, - state = dot::escape_html(&format!("{:?}", DebugWithAdapter { - this: state, - ctxt: analysis - })), + state = dot::escape_html(&format!( + "{:?}", + DebugWithAdapter { this: state, ctxt: analysis } + )), ) }) } diff --git a/compiler/rustc_mir_dataflow/src/framework/lattice.rs b/compiler/rustc_mir_dataflow/src/framework/lattice.rs index cb8159ce37bf3..919e346bda747 100644 --- a/compiler/rustc_mir_dataflow/src/framework/lattice.rs +++ b/compiler/rustc_mir_dataflow/src/framework/lattice.rs @@ -39,7 +39,7 @@ //! [poset]: https://en.wikipedia.org/wiki/Partially_ordered_set use rustc_index::Idx; -use rustc_index::bit_set::{BitSet, MixedBitSet}; +use rustc_index::bit_set::{DenseBitSet, MixedBitSet}; use crate::framework::BitSetExt; @@ -68,10 +68,10 @@ pub trait HasTop { const TOP: Self; } -/// A `BitSet` represents the lattice formed by the powerset of all possible values of -/// the index type `T` ordered by inclusion. Equivalently, it is a tuple of "two-point" lattices, -/// one for each possible value of `T`. -impl JoinSemiLattice for BitSet { +/// A `DenseBitSet` represents the lattice formed by the powerset of all possible values of the +/// index type `T` ordered by inclusion. Equivalently, it is a tuple of "two-point" lattices, one +/// for each possible value of `T`. +impl JoinSemiLattice for DenseBitSet { fn join(&mut self, other: &Self) -> bool { self.union(other) } diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index 3de2c6e3f47c1..60c5cb0cae8ad 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -35,7 +35,7 @@ use std::cmp::Ordering; use rustc_data_structures::work_queue::WorkQueue; -use rustc_index::bit_set::{BitSet, MixedBitSet}; +use rustc_index::bit_set::{DenseBitSet, MixedBitSet}; use rustc_index::{Idx, IndexVec}; use rustc_middle::bug; use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges, traversal}; @@ -65,7 +65,7 @@ pub trait BitSetExt { fn contains(&self, elem: T) -> bool; } -impl BitSetExt for BitSet { +impl BitSetExt for DenseBitSet { fn contains(&self, elem: T) -> bool { self.contains(elem) } @@ -334,7 +334,7 @@ pub trait GenKill { } } -impl GenKill for BitSet { +impl GenKill for DenseBitSet { fn gen_(&mut self, elem: T) { self.insert(elem); } diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs index 8e7d4ab0fa33b..ae0f1179e6fac 100644 --- a/compiler/rustc_mir_dataflow/src/framework/tests.rs +++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs @@ -30,26 +30,32 @@ fn mock_body<'tcx>() -> mir::Body<'tcx> { block(4, mir::TerminatorKind::Return); block(1, mir::TerminatorKind::Return); - block(2, mir::TerminatorKind::Call { - func: mir::Operand::Copy(dummy_place.clone()), - args: [].into(), - destination: dummy_place.clone(), - target: Some(mir::START_BLOCK), - unwind: mir::UnwindAction::Continue, - call_source: mir::CallSource::Misc, - fn_span: DUMMY_SP, - }); + block( + 2, + mir::TerminatorKind::Call { + func: mir::Operand::Copy(dummy_place.clone()), + args: [].into(), + destination: dummy_place.clone(), + target: Some(mir::START_BLOCK), + unwind: mir::UnwindAction::Continue, + call_source: mir::CallSource::Misc, + fn_span: DUMMY_SP, + }, + ); block(3, mir::TerminatorKind::Return); block(0, mir::TerminatorKind::Return); - block(4, mir::TerminatorKind::Call { - func: mir::Operand::Copy(dummy_place.clone()), - args: [].into(), - destination: dummy_place.clone(), - target: Some(mir::START_BLOCK), - unwind: mir::UnwindAction::Continue, - call_source: mir::CallSource::Misc, - fn_span: DUMMY_SP, - }); + block( + 4, + mir::TerminatorKind::Call { + func: mir::Operand::Copy(dummy_place.clone()), + args: [].into(), + destination: dummy_place.clone(), + target: Some(mir::START_BLOCK), + unwind: mir::UnwindAction::Continue, + call_source: mir::CallSource::Misc, + fn_span: DUMMY_SP, + }, + ); mir::Body::new_cfg_only(blocks) } @@ -84,13 +90,13 @@ impl MockAnalysis<'_, D> { /// The entry set for each `BasicBlock` is the ID of that block offset by a fixed amount to /// avoid colliding with the statement/terminator effects. - fn mock_entry_set(&self, bb: BasicBlock) -> BitSet { + fn mock_entry_set(&self, bb: BasicBlock) -> DenseBitSet { let mut ret = self.bottom_value(self.body); ret.insert(Self::BASIC_BLOCK_OFFSET + bb.index()); ret } - fn mock_entry_states(&self) -> IndexVec> { + fn mock_entry_states(&self) -> IndexVec> { let empty = self.bottom_value(self.body); let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks); @@ -121,7 +127,7 @@ impl MockAnalysis<'_, D> { /// For example, the expected state when calling /// `seek_before_primary_effect(Location { block: 2, statement_index: 2 })` /// would be `[102, 0, 1, 2, 3, 4]`. - fn expected_state_at_target(&self, target: SeekTarget) -> BitSet { + fn expected_state_at_target(&self, target: SeekTarget) -> DenseBitSet { let block = target.block(); let mut ret = self.bottom_value(self.body); ret.insert(Self::BASIC_BLOCK_OFFSET + block.index()); @@ -155,13 +161,13 @@ impl MockAnalysis<'_, D> { } impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { - type Domain = BitSet; + type Domain = DenseBitSet; type Direction = D; const NAME: &'static str = "mock"; fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { - BitSet::new_empty(Self::BASIC_BLOCK_OFFSET + body.basic_blocks.len()) + DenseBitSet::new_empty(Self::BASIC_BLOCK_OFFSET + body.basic_blocks.len()) } fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index 217594b3238ab..9abb83434321d 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -1,4 +1,4 @@ -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; @@ -21,12 +21,12 @@ impl MaybeBorrowedLocals { } impl<'tcx> Analysis<'tcx> for MaybeBorrowedLocals { - type Domain = BitSet; + type Domain = DenseBitSet; const NAME: &'static str = "maybe_borrowed_locals"; fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain { // bottom = unborrowed - BitSet::new_empty(body.local_decls().len()) + DenseBitSet::new_empty(body.local_decls().len()) } fn initialize_start_block(&self, _: &Body<'tcx>, _: &mut Self::Domain) { @@ -91,12 +91,14 @@ where | Rvalue::Use(..) | Rvalue::ThreadLocalRef(..) | Rvalue::Repeat(..) + | Rvalue::Len(..) | Rvalue::BinaryOp(..) | Rvalue::NullaryOp(..) | Rvalue::UnaryOp(..) | Rvalue::Discriminant(..) | Rvalue::Aggregate(..) - | Rvalue::CopyForDeref(..) => {} + | Rvalue::CopyForDeref(..) + | Rvalue::WrapUnsafeBinder(..) => {} } } @@ -137,8 +139,8 @@ where } /// The set of locals that are borrowed at some point in the MIR body. -pub fn borrowed_locals(body: &Body<'_>) -> BitSet { - struct Borrowed(BitSet); +pub fn borrowed_locals(body: &Body<'_>) -> DenseBitSet { + struct Borrowed(DenseBitSet); impl GenKill for Borrowed { #[inline] @@ -151,7 +153,7 @@ pub fn borrowed_locals(body: &Body<'_>) -> BitSet { } } - let mut borrowed = Borrowed(BitSet::new_empty(body.local_decls.len())); + let mut borrowed = Borrowed(DenseBitSet::new_empty(body.local_decls.len())); TransferFunction { trans: &mut borrowed }.visit_body(body); borrowed.0 } diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs index 769f9c7cfc3eb..3be450a0b3f47 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -2,14 +2,14 @@ use std::assert_matches::assert_matches; use rustc_abi::VariantIdx; use rustc_index::Idx; -use rustc_index::bit_set::{BitSet, MixedBitSet}; +use rustc_index::bit_set::{DenseBitSet, MixedBitSet}; use rustc_middle::bug; use rustc_middle::mir::{self, Body, CallReturnPlaces, Location, TerminatorEdges}; use rustc_middle::ty::util::Discr; use rustc_middle::ty::{self, TyCtxt}; use tracing::{debug, instrument}; -use crate::elaborate_drops::DropFlagState; +use crate::drop_flag_effects::DropFlagState; use crate::framework::SwitchIntTarget; use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex}; use crate::{ @@ -207,7 +207,7 @@ pub struct MaybeUninitializedPlaces<'a, 'tcx> { move_data: &'a MoveData<'tcx>, mark_inactive_variants_as_uninit: bool, - skip_unreachable_unwind: BitSet, + skip_unreachable_unwind: DenseBitSet, } impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> { @@ -217,7 +217,7 @@ impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> { body, move_data, mark_inactive_variants_as_uninit: false, - skip_unreachable_unwind: BitSet::new_empty(body.basic_blocks.len()), + skip_unreachable_unwind: DenseBitSet::new_empty(body.basic_blocks.len()), } } @@ -233,7 +233,7 @@ impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> { pub fn skipping_unreachable_unwind( mut self, - unreachable_unwind: BitSet, + unreachable_unwind: DenseBitSet, ) -> Self { self.skip_unreachable_unwind = unreachable_unwind; self diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index b2050a6adf987..6ec1b03a34e68 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -1,4 +1,4 @@ -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{ self, CallReturnPlaces, Local, Location, Place, StatementKind, TerminatorEdges, @@ -26,14 +26,14 @@ use crate::{Analysis, Backward, GenKill}; pub struct MaybeLiveLocals; impl<'tcx> Analysis<'tcx> for MaybeLiveLocals { - type Domain = BitSet; + type Domain = DenseBitSet; type Direction = Backward; const NAME: &'static str = "liveness"; fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { // bottom = not live - BitSet::new_empty(body.local_decls.len()) + DenseBitSet::new_empty(body.local_decls.len()) } fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { @@ -81,7 +81,7 @@ impl<'tcx> Analysis<'tcx> for MaybeLiveLocals { } } -pub struct TransferFunction<'a>(pub &'a mut BitSet); +pub struct TransferFunction<'a>(pub &'a mut DenseBitSet); impl<'tcx> Visitor<'tcx> for TransferFunction<'_> { fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, location: Location) { @@ -117,7 +117,7 @@ impl<'tcx> Visitor<'tcx> for TransferFunction<'_> { } } -struct YieldResumeEffect<'a>(&'a mut BitSet); +struct YieldResumeEffect<'a>(&'a mut DenseBitSet); impl<'tcx> Visitor<'tcx> for YieldResumeEffect<'_> { fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, location: Location) { @@ -137,7 +137,7 @@ enum DefUse { } impl DefUse { - fn apply(state: &mut BitSet, place: Place<'_>, context: PlaceContext) { + fn apply(state: &mut DenseBitSet, place: Place<'_>, context: PlaceContext) { match DefUse::for_place(place, context) { Some(DefUse::Def) => state.kill(place.local), Some(DefUse::Use) => state.gen_(place.local), @@ -204,7 +204,7 @@ impl DefUse { /// /// All of the caveats of `MaybeLiveLocals` apply. pub struct MaybeTransitiveLiveLocals<'a> { - always_live: &'a BitSet, + always_live: &'a DenseBitSet, } impl<'a> MaybeTransitiveLiveLocals<'a> { @@ -212,20 +212,20 @@ impl<'a> MaybeTransitiveLiveLocals<'a> { /// considered live. /// /// This should include at least all locals that are ever borrowed. - pub fn new(always_live: &'a BitSet) -> Self { + pub fn new(always_live: &'a DenseBitSet) -> Self { MaybeTransitiveLiveLocals { always_live } } } impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { - type Domain = BitSet; + type Domain = DenseBitSet; type Direction = Backward; const NAME: &'static str = "transitive liveness"; fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { // bottom = not live - BitSet::new_empty(body.local_decls.len()) + DenseBitSet::new_empty(body.local_decls.len()) } fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index 65b480d3a5efc..e3aa8f5a62014 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -10,8 +10,8 @@ use crate::{Analysis, GenKill, ResultsCursor}; /// The set of locals in a MIR body that do not have `StorageLive`/`StorageDead` annotations. /// /// These locals have fixed storage for the duration of the body. -pub fn always_storage_live_locals(body: &Body<'_>) -> BitSet { - let mut always_live_locals = BitSet::new_filled(body.local_decls.len()); +pub fn always_storage_live_locals(body: &Body<'_>) -> DenseBitSet { + let mut always_live_locals = DenseBitSet::new_filled(body.local_decls.len()); for block in &*body.basic_blocks { for statement in &block.statements { @@ -25,23 +25,23 @@ pub fn always_storage_live_locals(body: &Body<'_>) -> BitSet { } pub struct MaybeStorageLive<'a> { - always_live_locals: Cow<'a, BitSet>, + always_live_locals: Cow<'a, DenseBitSet>, } impl<'a> MaybeStorageLive<'a> { - pub fn new(always_live_locals: Cow<'a, BitSet>) -> Self { + pub fn new(always_live_locals: Cow<'a, DenseBitSet>) -> Self { MaybeStorageLive { always_live_locals } } } impl<'a, 'tcx> Analysis<'tcx> for MaybeStorageLive<'a> { - type Domain = BitSet; + type Domain = DenseBitSet; const NAME: &'static str = "maybe_storage_live"; fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain { // bottom = dead - BitSet::new_empty(body.local_decls.len()) + DenseBitSet::new_empty(body.local_decls.len()) } fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) { @@ -67,23 +67,23 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeStorageLive<'a> { } pub struct MaybeStorageDead<'a> { - always_live_locals: Cow<'a, BitSet>, + always_live_locals: Cow<'a, DenseBitSet>, } impl<'a> MaybeStorageDead<'a> { - pub fn new(always_live_locals: Cow<'a, BitSet>) -> Self { + pub fn new(always_live_locals: Cow<'a, DenseBitSet>) -> Self { MaybeStorageDead { always_live_locals } } } impl<'a, 'tcx> Analysis<'tcx> for MaybeStorageDead<'a> { - type Domain = BitSet; + type Domain = DenseBitSet; const NAME: &'static str = "maybe_storage_dead"; fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain { // bottom = live - BitSet::new_empty(body.local_decls.len()) + DenseBitSet::new_empty(body.local_decls.len()) } fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) { @@ -125,13 +125,13 @@ impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> { } impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { - type Domain = BitSet; + type Domain = DenseBitSet; const NAME: &'static str = "requires_storage"; fn bottom_value(&self, body: &Body<'tcx>) -> Self::Domain { // bottom = dead - BitSet::new_empty(body.local_decls.len()) + DenseBitSet::new_empty(body.local_decls.len()) } fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) { @@ -304,7 +304,7 @@ impl<'tcx> MaybeRequiresStorage<'_, 'tcx> { struct MoveVisitor<'a, 'mir, 'tcx> { borrowed_locals: &'a mut BorrowedLocalsResults<'mir, 'tcx>, - state: &'a mut BitSet, + state: &'a mut DenseBitSet, } impl<'tcx> Visitor<'tcx> for MoveVisitor<'_, '_, 'tcx> { diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index 0cc79b0c93903..a8a56baa1ffc0 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -15,7 +15,7 @@ use rustc_middle::ty; // Please change the public `use` directives cautiously, as they might be used by external tools. // See issue #120130. pub use self::drop_flag_effects::{ - drop_flag_effects_for_function_entry, drop_flag_effects_for_location, + DropFlagState, drop_flag_effects_for_function_entry, drop_flag_effects_for_location, move_path_children_matching, on_all_children_bits, on_lookup_result_bits, }; pub use self::framework::{ @@ -26,7 +26,6 @@ use self::move_paths::MoveData; pub mod debuginfo; mod drop_flag_effects; -pub mod elaborate_drops; mod errors; mod framework; pub mod impls; diff --git a/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs b/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs index d79d2c316ee78..d056ad3d4b4ca 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs @@ -32,6 +32,7 @@ impl<'tcx> Lift for PlaceElem<'tcx> { } ProjectionElem::Downcast(a, u) => ProjectionElem::Downcast(a, u), ProjectionElem::Subtype(_ty) => ProjectionElem::Subtype(()), + ProjectionElem::UnwrapUnsafeBinder(_ty) => ProjectionElem::UnwrapUnsafeBinder(()), } } } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 80875f32e4f66..b6c259aa4e0ab 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -208,7 +208,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { | ty::Infer(_) | ty::Error(_) | ty::Placeholder(_) => bug!( - "When Place contains ProjectionElem::Field it's type shouldn't be {place_ty:#?}" + "When Place contains ProjectionElem::Field its type shouldn't be {place_ty:#?}" ), }, ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { @@ -226,6 +226,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { } _ => bug!("Unexpected type {place_ty:#?}"), }, + ProjectionElem::UnwrapUnsafeBinder(_) => {} // `OpaqueCast`:Only transmutes the type, so no moves there. // `Downcast` :Only changes information about a `Place` without moving. // `Subtype` :Only transmutes the type, so moves. @@ -399,7 +400,8 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { | Rvalue::Repeat(ref operand, _) | Rvalue::Cast(_, ref operand, _) | Rvalue::ShallowInitBox(ref operand, _) - | Rvalue::UnaryOp(_, ref operand) => self.gather_operand(operand), + | Rvalue::UnaryOp(_, ref operand) + | Rvalue::WrapUnsafeBinder(ref operand, _) => self.gather_operand(operand), Rvalue::BinaryOp(ref _binop, box (ref lhs, ref rhs)) => { self.gather_operand(lhs); self.gather_operand(rhs); @@ -413,8 +415,13 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { Rvalue::Ref(..) | Rvalue::RawPtr(..) | Rvalue::Discriminant(..) + | Rvalue::Len(..) | Rvalue::NullaryOp( - NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::UbChecks, + NullOp::SizeOf + | NullOp::AlignOf + | NullOp::OffsetOf(..) + | NullOp::UbChecks + | NullOp::ContractChecks, _, ) => {} } diff --git a/compiler/rustc_mir_dataflow/src/points.rs b/compiler/rustc_mir_dataflow/src/points.rs index 74209da876ab6..5d2a78acbf526 100644 --- a/compiler/rustc_mir_dataflow/src/points.rs +++ b/compiler/rustc_mir_dataflow/src/points.rs @@ -1,4 +1,4 @@ -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_index::interval::SparseIntervalMatrix; use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::{self, BasicBlock, Body, Location}; @@ -102,7 +102,7 @@ pub fn save_as_intervals<'tcx, N, A>( ) -> SparseIntervalMatrix where N: Idx, - A: Analysis<'tcx, Domain = BitSet>, + A: Analysis<'tcx, Domain = DenseBitSet>, { let values = SparseIntervalMatrix::new(elements.num_points()); let mut visitor = Visitor { elements, values }; @@ -122,7 +122,7 @@ struct Visitor<'a, N: Idx> { impl<'mir, 'tcx, A, N> ResultsVisitor<'mir, 'tcx, A> for Visitor<'_, N> where - A: Analysis<'tcx, Domain = BitSet>, + A: Analysis<'tcx, Domain = DenseBitSet>, N: Idx, { fn visit_after_primary_statement_effect( diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 9328870c7ae04..a51af8c40fd43 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -6,7 +6,7 @@ use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashMap, FxIndexSet, StdEntry}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -399,7 +399,7 @@ impl<'tcx> Map<'tcx> { &mut self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - exclude: BitSet, + exclude: DenseBitSet, value_limit: Option, ) { // Start by constructing the places for each bare local. @@ -912,9 +912,9 @@ pub fn iter_fields<'tcx>( } /// Returns all locals with projections that have their reference or address taken. -pub fn excluded_locals(body: &Body<'_>) -> BitSet { +pub fn excluded_locals(body: &Body<'_>) -> DenseBitSet { struct Collector { - result: BitSet, + result: DenseBitSet, } impl<'tcx> Visitor<'tcx> for Collector { @@ -932,7 +932,7 @@ pub fn excluded_locals(body: &Body<'_>) -> BitSet { } } - let mut collector = Collector { result: BitSet::new_empty(body.local_decls.len()) }; + let mut collector = Collector { result: DenseBitSet::new_empty(body.local_decls.len()) }; collector.visit_body(body); collector.result } diff --git a/compiler/rustc_mir_transform/messages.ftl b/compiler/rustc_mir_transform/messages.ftl index d00bfc66a6a57..5628f4c9381b3 100644 --- a/compiler/rustc_mir_transform/messages.ftl +++ b/compiler/rustc_mir_transform/messages.ftl @@ -19,6 +19,23 @@ mir_transform_ffi_unwind_call = call to {$foreign -> mir_transform_fn_item_ref = taking a reference to a function item does not give a function pointer .suggestion = cast `{$ident}` to obtain a function pointer +mir_transform_force_inline = + `{$callee}` could not be inlined into `{$caller}` but is required to be inlined + .call = ...`{$callee}` called here + .attr = inlining due to this annotation + .caller = within `{$caller}`... + .callee = `{$callee}` defined here + .note = could not be inlined due to: {$reason} + +mir_transform_force_inline_attr = + `{$callee}` is incompatible with `#[rustc_force_inline]` + .attr = annotation here + .callee = `{$callee}` defined here + .note = incompatible due to: {$reason} + +mir_transform_force_inline_justification = + `{$callee}` is required to be inlined to: {$sym} + mir_transform_must_not_suspend = {$pre}`{$def_path}`{$post} held across a suspend point, but should not be .label = the value is held across this suspend point .note = {$reason} @@ -55,6 +72,12 @@ mir_transform_unaligned_packed_ref = reference to packed field is unaligned .note_ub = creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) .help = copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) +mir_transform_unconditional_recursion = function cannot return without recursing + .label = cannot return without recursing + .help = a `loop` may express intention better if this is on purpose + +mir_transform_unconditional_recursion_call_site_label = recursive call site + mir_transform_undefined_transmute = pointers cannot be transmuted to integers during const eval .note = at compile-time, pointers do not have an integer value .note2 = avoiding this restriction via `union` or raw pointers leads to compile-time undefined behavior diff --git a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs index 03f11885d58ae..5bd6fdcf48577 100644 --- a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs +++ b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs @@ -116,4 +116,8 @@ impl<'tcx> crate::MirPass<'tcx> for AbortUnwindingCalls { // We may have invalidated some `cleanup` blocks so clean those up now. super::simplify::remove_dead_blocks(body); } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/add_call_guards.rs b/compiler/rustc_mir_transform/src/add_call_guards.rs index 24c955c0c78c6..55694cacd92e0 100644 --- a/compiler/rustc_mir_transform/src/add_call_guards.rs +++ b/compiler/rustc_mir_transform/src/add_call_guards.rs @@ -75,4 +75,8 @@ impl<'tcx> crate::MirPass<'tcx> for AddCallGuards { body.basic_blocks_mut().extend(new_blocks); } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs index 12a2fe23b149e..b33326cb873df 100644 --- a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs +++ b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs @@ -1,8 +1,8 @@ -use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::*; use rustc_middle::ty::{self, TyCtxt}; use tracing::debug; +use crate::patch::MirPatch; use crate::util; /// This pass moves values being dropped that are within a packed @@ -68,6 +68,10 @@ impl<'tcx> crate::MirPass<'tcx> for AddMovesForPackedDrops { patch.apply(body); } + + fn is_required(&self) -> bool { + true + } } fn add_move_for_packed_drop<'tcx>( @@ -95,10 +99,13 @@ fn add_move_for_packed_drop<'tcx>( patch.add_statement(loc, StatementKind::StorageLive(temp)); patch.add_assign(loc, Place::from(temp), Rvalue::Use(Operand::Move(*place))); - patch.patch_terminator(loc.block, TerminatorKind::Drop { - place: Place::from(temp), - target: storage_dead_block, - unwind, - replace, - }); + patch.patch_terminator( + loc.block, + TerminatorKind::Drop { + place: Place::from(temp), + target: storage_dead_block, + unwind, + replace, + }, + ); } diff --git a/compiler/rustc_mir_transform/src/add_retag.rs b/compiler/rustc_mir_transform/src/add_retag.rs index 53176eec9bcb9..e5a28d1b66c8c 100644 --- a/compiler/rustc_mir_transform/src/add_retag.rs +++ b/compiler/rustc_mir_transform/src/add_retag.rs @@ -111,10 +111,13 @@ impl<'tcx> crate::MirPass<'tcx> for AddRetag { .collect::>(); // Now we go over the returns we collected to retag the return values. for (source_info, dest_place, dest_block) in returns { - basic_blocks[dest_block].statements.insert(0, Statement { - source_info, - kind: StatementKind::Retag(RetagKind::Default, Box::new(dest_place)), - }); + basic_blocks[dest_block].statements.insert( + 0, + Statement { + source_info, + kind: StatementKind::Retag(RetagKind::Default, Box::new(dest_place)), + }, + ); } // PART 3 @@ -169,11 +172,18 @@ impl<'tcx> crate::MirPass<'tcx> for AddRetag { }; // Insert a retag after the statement. let source_info = block_data.statements[i].source_info; - block_data.statements.insert(i + 1, Statement { - source_info, - kind: StatementKind::Retag(retag_kind, Box::new(place)), - }); + block_data.statements.insert( + i + 1, + Statement { + source_info, + kind: StatementKind::Retag(retag_kind, Box::new(place)), + }, + ); } } } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs index e585e338613d8..b5cd41334598b 100644 --- a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs +++ b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs @@ -1,8 +1,9 @@ -use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::visit::MutVisitor; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; +use crate::patch::MirPatch; + pub(super) struct Subtyper; struct SubTypeChecker<'a, 'tcx> { @@ -61,4 +62,8 @@ impl<'tcx> crate::MirPass<'tcx> for Subtyper { } checker.patcher.apply(body); } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/check_alignment.rs b/compiler/rustc_mir_transform/src/check_alignment.rs index 1b7c89fd25183..b70cca1484070 100644 --- a/compiler/rustc_mir_transform/src/check_alignment.rs +++ b/compiler/rustc_mir_transform/src/check_alignment.rs @@ -1,11 +1,11 @@ -use rustc_hir::lang_items::LangItem; use rustc_index::IndexVec; use rustc_middle::mir::interpret::Scalar; -use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::visit::PlaceContext; use rustc_middle::mir::*; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{Ty, TyCtxt}; use rustc_session::Session; -use tracing::{debug, trace}; + +use crate::check_pointers::{BorrowCheckMode, PointerCheck, check_pointers}; pub(super) struct CheckAlignment; @@ -19,162 +19,54 @@ impl<'tcx> crate::MirPass<'tcx> for CheckAlignment { } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - // This pass emits new panics. If for whatever reason we do not have a panic - // implementation, running this pass may cause otherwise-valid code to not compile. - if tcx.lang_items().get(LangItem::PanicImpl).is_none() { - return; - } - - let typing_env = body.typing_env(tcx); - let basic_blocks = body.basic_blocks.as_mut(); - let local_decls = &mut body.local_decls; - - // This pass inserts new blocks. Each insertion changes the Location for all - // statements/blocks after. Iterating or visiting the MIR in order would require updating - // our current location after every insertion. By iterating backwards, we dodge this issue: - // The only Locations that an insertion changes have already been handled. - for block in (0..basic_blocks.len()).rev() { - let block = block.into(); - for statement_index in (0..basic_blocks[block].statements.len()).rev() { - let location = Location { block, statement_index }; - let statement = &basic_blocks[block].statements[statement_index]; - let source_info = statement.source_info; - - let mut finder = - PointerFinder { tcx, local_decls, typing_env, pointers: Vec::new() }; - finder.visit_statement(statement, location); - - for (local, ty) in finder.pointers { - debug!("Inserting alignment check for {:?}", ty); - let new_block = split_block(basic_blocks, location); - insert_alignment_check( - tcx, - local_decls, - &mut basic_blocks[block], - local, - ty, - source_info, - new_block, - ); - } - } - } + // Skip trivially aligned place types. + let excluded_pointees = [tcx.types.bool, tcx.types.i8, tcx.types.u8]; + + // We have to exclude borrows here: in `&x.field`, the exact + // requirement is that the final reference must be aligned, but + // `check_pointers` would check that `x` is aligned, which would be wrong. + check_pointers( + tcx, + body, + &excluded_pointees, + insert_alignment_check, + BorrowCheckMode::ExcludeBorrows, + ); } -} - -struct PointerFinder<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - local_decls: &'a mut LocalDecls<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - pointers: Vec<(Place<'tcx>, Ty<'tcx>)>, -} - -impl<'a, 'tcx> Visitor<'tcx> for PointerFinder<'a, 'tcx> { - fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { - // We want to only check reads and writes to Places, so we specifically exclude - // Borrow and RawBorrow. - match context { - PlaceContext::MutatingUse( - MutatingUseContext::Store - | MutatingUseContext::AsmOutput - | MutatingUseContext::Call - | MutatingUseContext::Yield - | MutatingUseContext::Drop, - ) => {} - PlaceContext::NonMutatingUse( - NonMutatingUseContext::Copy | NonMutatingUseContext::Move, - ) => {} - _ => { - return; - } - } - - if !place.is_indirect() { - return; - } - - // Since Deref projections must come first and only once, the pointer for an indirect place - // is the Local that the Place is based on. - let pointer = Place::from(place.local); - let pointer_ty = self.local_decls[place.local].ty; - // We only want to check places based on unsafe pointers - if !pointer_ty.is_unsafe_ptr() { - trace!("Indirect, but not based on an unsafe ptr, not checking {:?}", place); - return; - } - - let pointee_ty = - pointer_ty.builtin_deref(true).expect("no builtin_deref for an unsafe pointer"); - // Ideally we'd support this in the future, but for now we are limited to sized types. - if !pointee_ty.is_sized(self.tcx, self.typing_env) { - debug!("Unsafe pointer, but pointee is not known to be sized: {:?}", pointer_ty); - return; - } - - // Try to detect types we are sure have an alignment of 1 and skip the check - // We don't need to look for str and slices, we already rejected unsized types above - let element_ty = match pointee_ty.kind() { - ty::Array(ty, _) => *ty, - _ => pointee_ty, - }; - if [self.tcx.types.bool, self.tcx.types.i8, self.tcx.types.u8].contains(&element_ty) { - debug!("Trivially aligned place type: {:?}", pointee_ty); - return; - } - - // Ensure that this place is based on an aligned pointer. - self.pointers.push((pointer, pointee_ty)); - - self.super_place(place, context, location); + fn is_required(&self) -> bool { + true } } -fn split_block( - basic_blocks: &mut IndexVec>, - location: Location, -) -> BasicBlock { - let block_data = &mut basic_blocks[location.block]; - - // Drain every statement after this one and move the current terminator to a new basic block - let new_block = BasicBlockData { - statements: block_data.statements.split_off(location.statement_index), - terminator: block_data.terminator.take(), - is_cleanup: block_data.is_cleanup, - }; - - basic_blocks.push(new_block) -} - +/// Inserts the actual alignment check's logic. Returns a +/// [AssertKind::MisalignedPointerDereference] on failure. fn insert_alignment_check<'tcx>( tcx: TyCtxt<'tcx>, - local_decls: &mut IndexVec>, - block_data: &mut BasicBlockData<'tcx>, pointer: Place<'tcx>, pointee_ty: Ty<'tcx>, + _context: PlaceContext, + local_decls: &mut IndexVec>, + stmts: &mut Vec>, source_info: SourceInfo, - new_block: BasicBlock, -) { - // Cast the pointer to a *const () +) -> PointerCheck<'tcx> { + // Cast the pointer to a *const (). let const_raw_ptr = Ty::new_imm_ptr(tcx, tcx.types.unit); let rvalue = Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(pointer), const_raw_ptr); let thin_ptr = local_decls.push(LocalDecl::with_source_info(const_raw_ptr, source_info)).into(); - block_data - .statements + stmts .push(Statement { source_info, kind: StatementKind::Assign(Box::new((thin_ptr, rvalue))) }); - // Transmute the pointer to a usize (equivalent to `ptr.addr()`) + // Transmute the pointer to a usize (equivalent to `ptr.addr()`). let rvalue = Rvalue::Cast(CastKind::Transmute, Operand::Copy(thin_ptr), tcx.types.usize); let addr = local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into(); - block_data - .statements - .push(Statement { source_info, kind: StatementKind::Assign(Box::new((addr, rvalue))) }); + stmts.push(Statement { source_info, kind: StatementKind::Assign(Box::new((addr, rvalue))) }); // Get the alignment of the pointee let alignment = local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into(); let rvalue = Rvalue::NullaryOp(NullOp::AlignOf, pointee_ty); - block_data.statements.push(Statement { + stmts.push(Statement { source_info, kind: StatementKind::Assign(Box::new((alignment, rvalue))), }); @@ -187,7 +79,7 @@ fn insert_alignment_check<'tcx>( user_ty: None, const_: Const::Val(ConstValue::Scalar(Scalar::from_target_usize(1, &tcx)), tcx.types.usize), })); - block_data.statements.push(Statement { + stmts.push(Statement { source_info, kind: StatementKind::Assign(Box::new(( alignment_mask, @@ -198,7 +90,7 @@ fn insert_alignment_check<'tcx>( // BitAnd the alignment mask with the pointer let alignment_bits = local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into(); - block_data.statements.push(Statement { + stmts.push(Statement { source_info, kind: StatementKind::Assign(Box::new(( alignment_bits, @@ -216,7 +108,7 @@ fn insert_alignment_check<'tcx>( user_ty: None, const_: Const::Val(ConstValue::Scalar(Scalar::from_target_usize(0, &tcx)), tcx.types.usize), })); - block_data.statements.push(Statement { + stmts.push(Statement { source_info, kind: StatementKind::Assign(Box::new(( is_ok, @@ -224,21 +116,13 @@ fn insert_alignment_check<'tcx>( ))), }); - // Set this block's terminator to our assert, continuing to new_block if we pass - block_data.terminator = Some(Terminator { - source_info, - kind: TerminatorKind::Assert { - cond: Operand::Copy(is_ok), - expected: true, - target: new_block, - msg: Box::new(AssertKind::MisalignedPointerDereference { - required: Operand::Copy(alignment), - found: Operand::Copy(addr), - }), - // This calls panic_misaligned_pointer_dereference, which is #[rustc_nounwind]. - // We never want to insert an unwind into unsafe code, because unwinding could - // make a failing UB check turn into much worse UB when we start unwinding. - unwind: UnwindAction::Unreachable, - }, - }); + // Emit a check that asserts on the alignment and otherwise triggers a + // AssertKind::MisalignedPointerDereference. + PointerCheck { + cond: Operand::Copy(is_ok), + assert_kind: Box::new(AssertKind::MisalignedPointerDereference { + required: Operand::Copy(alignment), + found: Operand::Copy(addr), + }), + } } diff --git a/compiler/rustc_mir_transform/src/check_call_recursion.rs b/compiler/rustc_mir_transform/src/check_call_recursion.rs new file mode 100644 index 0000000000000..e49723a6c39db --- /dev/null +++ b/compiler/rustc_mir_transform/src/check_call_recursion.rs @@ -0,0 +1,268 @@ +use std::ops::ControlFlow; + +use rustc_data_structures::graph::iterate::{ + NodeStatus, TriColorDepthFirstSearch, TriColorVisitor, +}; +use rustc_hir::def::DefKind; +use rustc_middle::mir::{self, BasicBlock, BasicBlocks, Body, Terminator, TerminatorKind}; +use rustc_middle::ty::{self, GenericArg, GenericArgs, Instance, Ty, TyCtxt}; +use rustc_session::lint::builtin::UNCONDITIONAL_RECURSION; +use rustc_span::Span; + +use crate::errors::UnconditionalRecursion; +use crate::pass_manager::MirLint; + +pub(super) struct CheckCallRecursion; + +impl<'tcx> MirLint<'tcx> for CheckCallRecursion { + fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { + let def_id = body.source.def_id().expect_local(); + + if let DefKind::Fn | DefKind::AssocFn = tcx.def_kind(def_id) { + // If this is trait/impl method, extract the trait's args. + let trait_args = match tcx.trait_of_item(def_id.to_def_id()) { + Some(trait_def_id) => { + let trait_args_count = tcx.generics_of(trait_def_id).count(); + &GenericArgs::identity_for_item(tcx, def_id)[..trait_args_count] + } + _ => &[], + }; + + check_recursion(tcx, body, CallRecursion { trait_args }) + } + } +} + +/// Requires drop elaboration to have been performed. +pub(super) struct CheckDropRecursion; + +impl<'tcx> MirLint<'tcx> for CheckDropRecursion { + fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { + let def_id = body.source.def_id().expect_local(); + + // First check if `body` is an `fn drop()` of `Drop` + if let DefKind::AssocFn = tcx.def_kind(def_id) + && let Some(trait_ref) = + tcx.impl_of_method(def_id.to_def_id()).and_then(|def_id| tcx.impl_trait_ref(def_id)) + && let Some(drop_trait) = tcx.lang_items().drop_trait() + && drop_trait == trait_ref.instantiate_identity().def_id + // avoid erroneous `Drop` impls from causing ICEs below + && let sig = tcx.fn_sig(def_id).instantiate_identity() + && sig.inputs().skip_binder().len() == 1 + { + // It was. Now figure out for what type `Drop` is implemented and then + // check for recursion. + if let ty::Ref(_, dropped_ty, _) = + tcx.liberate_late_bound_regions(def_id.to_def_id(), sig.input(0)).kind() + { + check_recursion(tcx, body, RecursiveDrop { drop_for: *dropped_ty }); + } + } + } +} + +fn check_recursion<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + classifier: impl TerminatorClassifier<'tcx>, +) { + let def_id = body.source.def_id().expect_local(); + + if let DefKind::Fn | DefKind::AssocFn = tcx.def_kind(def_id) { + let mut vis = Search { tcx, body, classifier, reachable_recursive_calls: vec![] }; + if let Some(NonRecursive) = + TriColorDepthFirstSearch::new(&body.basic_blocks).run_from_start(&mut vis) + { + return; + } + if vis.reachable_recursive_calls.is_empty() { + return; + } + + vis.reachable_recursive_calls.sort(); + + let sp = tcx.def_span(def_id); + let hir_id = tcx.local_def_id_to_hir_id(def_id); + tcx.emit_node_span_lint( + UNCONDITIONAL_RECURSION, + hir_id, + sp, + UnconditionalRecursion { span: sp, call_sites: vis.reachable_recursive_calls }, + ); + } +} + +trait TerminatorClassifier<'tcx> { + fn is_recursive_terminator( + &self, + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + terminator: &Terminator<'tcx>, + ) -> bool; +} + +struct NonRecursive; + +struct Search<'mir, 'tcx, C: TerminatorClassifier<'tcx>> { + tcx: TyCtxt<'tcx>, + body: &'mir Body<'tcx>, + classifier: C, + + reachable_recursive_calls: Vec, +} + +struct CallRecursion<'tcx> { + trait_args: &'tcx [GenericArg<'tcx>], +} + +struct RecursiveDrop<'tcx> { + /// The type that `Drop` is implemented for. + drop_for: Ty<'tcx>, +} + +impl<'tcx> TerminatorClassifier<'tcx> for CallRecursion<'tcx> { + /// Returns `true` if `func` refers to the function we are searching in. + fn is_recursive_terminator( + &self, + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + terminator: &Terminator<'tcx>, + ) -> bool { + let TerminatorKind::Call { func, args, .. } = &terminator.kind else { + return false; + }; + + // Resolving function type to a specific instance that is being called is expensive. To + // avoid the cost we check the number of arguments first, which is sufficient to reject + // most of calls as non-recursive. + if args.len() != body.arg_count { + return false; + } + let caller = body.source.def_id(); + let typing_env = body.typing_env(tcx); + + let func_ty = func.ty(body, tcx); + if let ty::FnDef(callee, args) = *func_ty.kind() { + let Ok(normalized_args) = tcx.try_normalize_erasing_regions(typing_env, args) else { + return false; + }; + let (callee, call_args) = if let Ok(Some(instance)) = + Instance::try_resolve(tcx, typing_env, callee, normalized_args) + { + (instance.def_id(), instance.args) + } else { + (callee, normalized_args) + }; + + // FIXME(#57965): Make this work across function boundaries + + // If this is a trait fn, the args on the trait have to match, or we might be + // calling into an entirely different method (for example, a call from the default + // method in the trait to `>::method`, where `A` and/or `B` are + // specific types). + return callee == caller && &call_args[..self.trait_args.len()] == self.trait_args; + } + + false + } +} + +impl<'tcx> TerminatorClassifier<'tcx> for RecursiveDrop<'tcx> { + fn is_recursive_terminator( + &self, + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + terminator: &Terminator<'tcx>, + ) -> bool { + let TerminatorKind::Drop { place, .. } = &terminator.kind else { return false }; + + let dropped_ty = place.ty(body, tcx).ty; + dropped_ty == self.drop_for + } +} + +impl<'mir, 'tcx, C: TerminatorClassifier<'tcx>> TriColorVisitor> + for Search<'mir, 'tcx, C> +{ + type BreakVal = NonRecursive; + + fn node_examined( + &mut self, + bb: BasicBlock, + prior_status: Option, + ) -> ControlFlow { + // Back-edge in the CFG (loop). + if let Some(NodeStatus::Visited) = prior_status { + return ControlFlow::Break(NonRecursive); + } + + match self.body[bb].terminator().kind { + // These terminators return control flow to the caller. + TerminatorKind::UnwindTerminate(_) + | TerminatorKind::CoroutineDrop + | TerminatorKind::UnwindResume + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::Yield { .. } => ControlFlow::Break(NonRecursive), + + // A InlineAsm without targets (diverging and contains no labels) + // is treated as non-recursing. + TerminatorKind::InlineAsm { ref targets, .. } => { + if !targets.is_empty() { + ControlFlow::Continue(()) + } else { + ControlFlow::Break(NonRecursive) + } + } + + // These do not. + TerminatorKind::Assert { .. } + | TerminatorKind::Call { .. } + | TerminatorKind::Drop { .. } + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::Goto { .. } + | TerminatorKind::SwitchInt { .. } => ControlFlow::Continue(()), + + // Note that tail call terminator technically returns to the caller, + // but for purposes of this lint it makes sense to count it as possibly recursive, + // since it's still a call. + // + // If this'll be repurposed for something else, this might need to be changed. + TerminatorKind::TailCall { .. } => ControlFlow::Continue(()), + } + } + + fn node_settled(&mut self, bb: BasicBlock) -> ControlFlow { + // When we examine a node for the last time, remember it if it is a recursive call. + let terminator = self.body[bb].terminator(); + + // FIXME(explicit_tail_calls): highlight tail calls as "recursive call site" + // + // We don't want to lint functions that recurse only through tail calls + // (such as `fn g() { become () }`), so just adding `| TailCall { ... }` + // here won't work. + // + // But at the same time we would like to highlight both calls in a function like + // `fn f() { if false { become f() } else { f() } }`, so we need to figure something out. + if self.classifier.is_recursive_terminator(self.tcx, self.body, terminator) { + self.reachable_recursive_calls.push(terminator.source_info.span); + } + + ControlFlow::Continue(()) + } + + fn ignore_edge(&mut self, bb: BasicBlock, target: BasicBlock) -> bool { + let terminator = self.body[bb].terminator(); + let ignore_unwind = terminator.unwind() == Some(&mir::UnwindAction::Cleanup(target)) + && terminator.successors().count() > 1; + if ignore_unwind || self.classifier.is_recursive_terminator(self.tcx, self.body, terminator) + { + return true; + } + match &terminator.kind { + TerminatorKind::FalseEdge { imaginary_target, .. } => imaginary_target == &target, + _ => false, + } + } +} diff --git a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs index 7968b666dff2f..3affe4abbfad8 100644 --- a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs +++ b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs @@ -133,7 +133,7 @@ impl<'tcx> Visitor<'tcx> for ConstMutationChecker<'_, 'tcx> { // the `self` parameter of a method call (as the terminator of our current // BasicBlock). If so, we emit a more specific lint. let method_did = self.target_local.and_then(|target_local| { - rustc_middle::util::find_self_call(self.tcx, self.body, target_local, loc.block) + find_self_call(self.tcx, self.body, target_local, loc.block) }); let lint_loc = if method_did.is_some() { self.body.terminator_loc(loc.block) } else { loc }; diff --git a/compiler/rustc_mir_transform/src/check_inline.rs b/compiler/rustc_mir_transform/src/check_inline.rs new file mode 100644 index 0000000000000..497f4a660eaae --- /dev/null +++ b/compiler/rustc_mir_transform/src/check_inline.rs @@ -0,0 +1,91 @@ +//! Check that a body annotated with `#[rustc_force_inline]` will not fail to inline based on its +//! definition alone (irrespective of any specific caller). + +use rustc_attr_parsing::InlineAttr; +use rustc_hir::def_id::DefId; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::mir::{Body, TerminatorKind}; +use rustc_middle::ty; +use rustc_middle::ty::TyCtxt; +use rustc_span::sym; + +use crate::pass_manager::MirLint; + +pub(super) struct CheckForceInline; + +impl<'tcx> MirLint<'tcx> for CheckForceInline { + fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { + let def_id = body.source.def_id(); + if !tcx.hir().body_owner_kind(def_id).is_fn_or_closure() || !def_id.is_local() { + return; + } + let InlineAttr::Force { attr_span, .. } = tcx.codegen_fn_attrs(def_id).inline else { + return; + }; + + if let Err(reason) = + is_inline_valid_on_fn(tcx, def_id).and_then(|_| is_inline_valid_on_body(tcx, body)) + { + tcx.dcx().emit_err(crate::errors::InvalidForceInline { + attr_span, + callee_span: tcx.def_span(def_id), + callee: tcx.def_path_str(def_id), + reason, + }); + } + } +} + +pub(super) fn is_inline_valid_on_fn<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, +) -> Result<(), &'static str> { + let codegen_attrs = tcx.codegen_fn_attrs(def_id); + if tcx.has_attr(def_id, sym::rustc_no_mir_inline) { + return Err("#[rustc_no_mir_inline]"); + } + + // FIXME(#127234): Coverage instrumentation currently doesn't handle inlined + // MIR correctly when Modified Condition/Decision Coverage is enabled. + if tcx.sess.instrument_coverage_mcdc() { + return Err("incompatible with MC/DC coverage"); + } + + let ty = tcx.type_of(def_id); + if match ty.instantiate_identity().kind() { + ty::FnDef(..) => tcx.fn_sig(def_id).instantiate_identity().c_variadic(), + ty::Closure(_, args) => args.as_closure().sig().c_variadic(), + _ => false, + } { + return Err("C variadic"); + } + + if codegen_attrs.flags.contains(CodegenFnAttrFlags::COLD) { + return Err("cold"); + } + + // Intrinsic fallback bodies are automatically made cross-crate inlineable, + // but at this stage we don't know whether codegen knows the intrinsic, + // so just conservatively don't inline it. This also ensures that we do not + // accidentally inline the body of an intrinsic that *must* be overridden. + if tcx.has_attr(def_id, sym::rustc_intrinsic) { + return Err("callee is an intrinsic"); + } + + Ok(()) +} + +pub(super) fn is_inline_valid_on_body<'tcx>( + _: TyCtxt<'tcx>, + body: &Body<'tcx>, +) -> Result<(), &'static str> { + if body + .basic_blocks + .iter() + .any(|bb| matches!(bb.terminator().kind, TerminatorKind::TailCall { .. })) + { + return Err("can't inline functions with tail calls"); + } + + Ok(()) +} diff --git a/compiler/rustc_mir_transform/src/check_null.rs b/compiler/rustc_mir_transform/src/check_null.rs new file mode 100644 index 0000000000000..543e1845e6558 --- /dev/null +++ b/compiler/rustc_mir_transform/src/check_null.rs @@ -0,0 +1,133 @@ +use rustc_index::IndexVec; +use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext}; +use rustc_middle::mir::*; +use rustc_middle::ty::{Ty, TyCtxt}; +use rustc_session::Session; + +use crate::check_pointers::{BorrowCheckMode, PointerCheck, check_pointers}; + +pub(super) struct CheckNull; + +impl<'tcx> crate::MirPass<'tcx> for CheckNull { + fn is_enabled(&self, sess: &Session) -> bool { + sess.ub_checks() + } + + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + check_pointers(tcx, body, &[], insert_null_check, BorrowCheckMode::IncludeBorrows); + } + + fn is_required(&self) -> bool { + true + } +} + +fn insert_null_check<'tcx>( + tcx: TyCtxt<'tcx>, + pointer: Place<'tcx>, + pointee_ty: Ty<'tcx>, + context: PlaceContext, + local_decls: &mut IndexVec>, + stmts: &mut Vec>, + source_info: SourceInfo, +) -> PointerCheck<'tcx> { + // Cast the pointer to a *const (). + let const_raw_ptr = Ty::new_imm_ptr(tcx, tcx.types.unit); + let rvalue = Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(pointer), const_raw_ptr); + let thin_ptr = local_decls.push(LocalDecl::with_source_info(const_raw_ptr, source_info)).into(); + stmts + .push(Statement { source_info, kind: StatementKind::Assign(Box::new((thin_ptr, rvalue))) }); + + // Transmute the pointer to a usize (equivalent to `ptr.addr()`). + let rvalue = Rvalue::Cast(CastKind::Transmute, Operand::Copy(thin_ptr), tcx.types.usize); + let addr = local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into(); + stmts.push(Statement { source_info, kind: StatementKind::Assign(Box::new((addr, rvalue))) }); + + let zero = Operand::Constant(Box::new(ConstOperand { + span: source_info.span, + user_ty: None, + const_: Const::Val(ConstValue::from_target_usize(0, &tcx), tcx.types.usize), + })); + + let pointee_should_be_checked = match context { + // Borrows pointing to "null" are UB even if the pointee is a ZST. + PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) + | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => { + // Pointer should be checked unconditionally. + Operand::Constant(Box::new(ConstOperand { + span: source_info.span, + user_ty: None, + const_: Const::Val(ConstValue::from_bool(true), tcx.types.bool), + })) + } + // Other usages of null pointers only are UB if the pointee is not a ZST. + _ => { + let rvalue = Rvalue::NullaryOp(NullOp::SizeOf, pointee_ty); + let sizeof_pointee = + local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into(); + stmts.push(Statement { + source_info, + kind: StatementKind::Assign(Box::new((sizeof_pointee, rvalue))), + }); + + // Check that the pointee is not a ZST. + let is_pointee_not_zst = + local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into(); + stmts.push(Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + is_pointee_not_zst, + Rvalue::BinaryOp( + BinOp::Ne, + Box::new((Operand::Copy(sizeof_pointee), zero.clone())), + ), + ))), + }); + + // Pointer needs to be checked only if pointee is not a ZST. + Operand::Copy(is_pointee_not_zst) + } + }; + + // Check whether the pointer is null. + let is_null = local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into(); + stmts.push(Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + is_null, + Rvalue::BinaryOp(BinOp::Eq, Box::new((Operand::Copy(addr), zero))), + ))), + }); + + // We want to throw an exception if the pointer is null and the pointee is not unconditionally + // allowed (which for all non-borrow place uses, is when the pointee is ZST). + let should_throw_exception = + local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into(); + stmts.push(Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + should_throw_exception, + Rvalue::BinaryOp( + BinOp::BitAnd, + Box::new((Operand::Copy(is_null), pointee_should_be_checked)), + ), + ))), + }); + + // The final condition whether this pointer usage is ok or not. + let is_ok = local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into(); + stmts.push(Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + is_ok, + Rvalue::UnaryOp(UnOp::Not, Operand::Copy(should_throw_exception)), + ))), + }); + + // Emit a PointerCheck that asserts on the condition and otherwise triggers + // a AssertKind::NullPointerDereference. + PointerCheck { + cond: Operand::Copy(is_ok), + assert_kind: Box::new(AssertKind::NullPointerDereference), + } +} diff --git a/compiler/rustc_mir_transform/src/check_pointers.rs b/compiler/rustc_mir_transform/src/check_pointers.rs new file mode 100644 index 0000000000000..d693f739180f4 --- /dev/null +++ b/compiler/rustc_mir_transform/src/check_pointers.rs @@ -0,0 +1,236 @@ +use rustc_hir::lang_items::LangItem; +use rustc_index::IndexVec; +use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::*; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use tracing::{debug, trace}; + +/// Details of a pointer check, the condition on which we decide whether to +/// fail the assert and an [AssertKind] that defines the behavior on failure. +pub(crate) struct PointerCheck<'tcx> { + pub(crate) cond: Operand<'tcx>, + pub(crate) assert_kind: Box>>, +} + +/// Indicates whether we insert the checks for borrow places of a raw pointer. +/// Concretely places with [MutatingUseContext::Borrow] or +/// [NonMutatingUseContext::SharedBorrow]. +#[derive(Copy, Clone)] +pub(crate) enum BorrowCheckMode { + IncludeBorrows, + ExcludeBorrows, +} + +/// Utility for adding a check for read/write on every sized, raw pointer. +/// +/// Visits every read/write access to a [Sized], raw pointer and inserts a +/// new basic block directly before the pointer access. (Read/write accesses +/// are determined by the `PlaceContext` of the MIR visitor.) Then calls +/// `on_finding` to insert the actual logic for a pointer check (e.g. check for +/// alignment). A check can choose to be inserted for (mutable) borrows of +/// raw pointers via the `borrow_check_mode` parameter. +/// +/// This utility takes care of the right order of blocks, the only thing a +/// caller must do in `on_finding` is: +/// - Append [Statement]s to `stmts`. +/// - Append [LocalDecl]s to `local_decls`. +/// - Return a [PointerCheck] that contains the condition and an [AssertKind]. +/// The AssertKind must be a panic with `#[rustc_nounwind]`. The condition +/// should always return the boolean `is_ok`, so evaluate to true in case of +/// success and fail the check otherwise. +/// This utility will insert a terminator block that asserts on the condition +/// and panics on failure. +pub(crate) fn check_pointers<'tcx, F>( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + excluded_pointees: &[Ty<'tcx>], + on_finding: F, + borrow_check_mode: BorrowCheckMode, +) where + F: Fn( + /* tcx: */ TyCtxt<'tcx>, + /* pointer: */ Place<'tcx>, + /* pointee_ty: */ Ty<'tcx>, + /* context: */ PlaceContext, + /* local_decls: */ &mut IndexVec>, + /* stmts: */ &mut Vec>, + /* source_info: */ SourceInfo, + ) -> PointerCheck<'tcx>, +{ + // This pass emits new panics. If for whatever reason we do not have a panic + // implementation, running this pass may cause otherwise-valid code to not compile. + if tcx.lang_items().get(LangItem::PanicImpl).is_none() { + return; + } + + let typing_env = body.typing_env(tcx); + let basic_blocks = body.basic_blocks.as_mut(); + let local_decls = &mut body.local_decls; + + // This operation inserts new blocks. Each insertion changes the Location for all + // statements/blocks after. Iterating or visiting the MIR in order would require updating + // our current location after every insertion. By iterating backwards, we dodge this issue: + // The only Locations that an insertion changes have already been handled. + for block in (0..basic_blocks.len()).rev() { + let block = block.into(); + for statement_index in (0..basic_blocks[block].statements.len()).rev() { + let location = Location { block, statement_index }; + let statement = &basic_blocks[block].statements[statement_index]; + let source_info = statement.source_info; + + let mut finder = PointerFinder::new( + tcx, + local_decls, + typing_env, + excluded_pointees, + borrow_check_mode, + ); + finder.visit_statement(statement, location); + + for (local, ty, context) in finder.into_found_pointers() { + debug!("Inserting check for {:?}", ty); + let new_block = split_block(basic_blocks, location); + + // Invoke `on_finding` which appends to `local_decls` and the + // blocks statements. It returns information about the assert + // we're performing in the Terminator. + let block_data = &mut basic_blocks[block]; + let pointer_check = on_finding( + tcx, + local, + ty, + context, + local_decls, + &mut block_data.statements, + source_info, + ); + block_data.terminator = Some(Terminator { + source_info, + kind: TerminatorKind::Assert { + cond: pointer_check.cond, + expected: true, + target: new_block, + msg: pointer_check.assert_kind, + // This calls a panic function associated with the pointer check, which + // is #[rustc_nounwind]. We never want to insert an unwind into unsafe + // code, because unwinding could make a failing UB check turn into much + // worse UB when we start unwinding. + unwind: UnwindAction::Unreachable, + }, + }); + } + } + } +} + +struct PointerFinder<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + local_decls: &'a mut LocalDecls<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + pointers: Vec<(Place<'tcx>, Ty<'tcx>, PlaceContext)>, + excluded_pointees: &'a [Ty<'tcx>], + borrow_check_mode: BorrowCheckMode, +} + +impl<'a, 'tcx> PointerFinder<'a, 'tcx> { + fn new( + tcx: TyCtxt<'tcx>, + local_decls: &'a mut LocalDecls<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + excluded_pointees: &'a [Ty<'tcx>], + borrow_check_mode: BorrowCheckMode, + ) -> Self { + PointerFinder { + tcx, + local_decls, + typing_env, + excluded_pointees, + pointers: Vec::new(), + borrow_check_mode, + } + } + + fn into_found_pointers(self) -> Vec<(Place<'tcx>, Ty<'tcx>, PlaceContext)> { + self.pointers + } + + /// Whether or not we should visit a [Place] with [PlaceContext]. + /// + /// We generally only visit Reads/Writes to a place and only Borrows if + /// requested. + fn should_visit_place(&self, context: PlaceContext) -> bool { + match context { + PlaceContext::MutatingUse( + MutatingUseContext::Store + | MutatingUseContext::Call + | MutatingUseContext::Yield + | MutatingUseContext::Drop, + ) => true, + PlaceContext::NonMutatingUse( + NonMutatingUseContext::Copy | NonMutatingUseContext::Move, + ) => true, + PlaceContext::MutatingUse(MutatingUseContext::Borrow) + | PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) => { + matches!(self.borrow_check_mode, BorrowCheckMode::IncludeBorrows) + } + _ => false, + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for PointerFinder<'a, 'tcx> { + fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { + if !self.should_visit_place(context) || !place.is_indirect() { + return; + } + + // Since Deref projections must come first and only once, the pointer for an indirect place + // is the Local that the Place is based on. + let pointer = Place::from(place.local); + let pointer_ty = self.local_decls[place.local].ty; + + // We only want to check places based on raw pointers + if !pointer_ty.is_raw_ptr() { + trace!("Indirect, but not based on an raw ptr, not checking {:?}", place); + return; + } + + let pointee_ty = + pointer_ty.builtin_deref(true).expect("no builtin_deref for an raw pointer"); + // Ideally we'd support this in the future, but for now we are limited to sized types. + if !pointee_ty.is_sized(self.tcx, self.typing_env) { + trace!("Raw pointer, but pointee is not known to be sized: {:?}", pointer_ty); + return; + } + + // We don't need to look for slices, we already rejected unsized types above. + let element_ty = match pointee_ty.kind() { + ty::Array(ty, _) => *ty, + _ => pointee_ty, + }; + if self.excluded_pointees.contains(&element_ty) { + trace!("Skipping pointer for type: {:?}", pointee_ty); + return; + } + + self.pointers.push((pointer, pointee_ty, context)); + + self.super_place(place, context, location); + } +} + +fn split_block( + basic_blocks: &mut IndexVec>, + location: Location, +) -> BasicBlock { + let block_data = &mut basic_blocks[location.block]; + + // Drain every statement after this one and move the current terminator to a new basic block. + let new_block = BasicBlockData { + statements: block_data.statements.split_off(location.statement_index), + terminator: block_data.terminator.take(), + is_cleanup: block_data.is_cleanup, + }; + + basic_blocks.push(new_block) +} diff --git a/compiler/rustc_mir_transform/src/check_undefined_transmutes.rs b/compiler/rustc_mir_transform/src/check_undefined_transmutes.rs index 8ba14a1158ef9..ed3b1ae4f42f1 100644 --- a/compiler/rustc_mir_transform/src/check_undefined_transmutes.rs +++ b/compiler/rustc_mir_transform/src/check_undefined_transmutes.rs @@ -47,7 +47,7 @@ impl<'a, 'tcx> UndefinedTransmutesChecker<'a, 'tcx> { { let fn_sig = function.ty(self.body, self.tcx).fn_sig(self.tcx).skip_binder(); if let [input] = fn_sig.inputs() { - return input.is_unsafe_ptr() && fn_sig.output().is_integral(); + return input.is_raw_ptr() && fn_sig.output().is_integral(); } } false diff --git a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs index 6a22a58470c95..cb84401985735 100644 --- a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs +++ b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs @@ -72,4 +72,8 @@ impl<'tcx> crate::MirPass<'tcx> for CleanupPostBorrowck { decl.user_ty = None; } } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/copy_prop.rs b/compiler/rustc_mir_transform/src/copy_prop.rs index 9b3443d3209e7..27af5818982d0 100644 --- a/compiler/rustc_mir_transform/src/copy_prop.rs +++ b/compiler/rustc_mir_transform/src/copy_prop.rs @@ -1,5 +1,5 @@ use rustc_index::IndexSlice; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; @@ -34,7 +34,7 @@ impl<'tcx> crate::MirPass<'tcx> for CopyProp { let fully_moved = fully_moved_locals(&ssa, body); debug!(?fully_moved); - let mut storage_to_remove = BitSet::new_empty(fully_moved.domain_size()); + let mut storage_to_remove = DenseBitSet::new_empty(fully_moved.domain_size()); for (local, &head) in ssa.copy_classes().iter_enumerated() { if local != head { storage_to_remove.insert(head); @@ -56,6 +56,10 @@ impl<'tcx> crate::MirPass<'tcx> for CopyProp { crate::simplify::remove_unused_definitions(body); } } + + fn is_required(&self) -> bool { + false + } } /// `SsaLocals` computed equivalence classes between locals considering copy/move assignments. @@ -68,8 +72,8 @@ impl<'tcx> crate::MirPass<'tcx> for CopyProp { /// This means that replacing it by a copy of `_a` if ok, since this copy happens before `_c` is /// moved, and therefore that `_d` is moved. #[instrument(level = "trace", skip(ssa, body))] -fn fully_moved_locals(ssa: &SsaLocals, body: &Body<'_>) -> BitSet { - let mut fully_moved = BitSet::new_filled(body.local_decls.len()); +fn fully_moved_locals(ssa: &SsaLocals, body: &Body<'_>) -> DenseBitSet { + let mut fully_moved = DenseBitSet::new_filled(body.local_decls.len()); for (_, rvalue, _) in ssa.assignments(body) { let (Rvalue::Use(Operand::Copy(place) | Operand::Move(place)) @@ -96,9 +100,9 @@ fn fully_moved_locals(ssa: &SsaLocals, body: &Body<'_>) -> BitSet { /// Utility to help performing substitution of `*pattern` by `target`. struct Replacer<'a, 'tcx> { tcx: TyCtxt<'tcx>, - fully_moved: BitSet, - storage_to_remove: BitSet, - borrowed_locals: &'a BitSet, + fully_moved: DenseBitSet, + storage_to_remove: DenseBitSet, + borrowed_locals: &'a DenseBitSet, copy_classes: &'a IndexSlice, } diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index a3320f99cc374..afc49c5cc54af 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -60,7 +60,7 @@ use rustc_errors::pluralize; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; use rustc_hir::{CoroutineDesugaring, CoroutineKind}; -use rustc_index::bit_set::{BitMatrix, BitSet, GrowableBitSet}; +use rustc_index::bit_set::{BitMatrix, DenseBitSet, GrowableBitSet}; use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -185,13 +185,13 @@ struct TransformVisitor<'tcx> { remap: IndexVec, VariantIdx, FieldIdx)>>, // A map from a suspension point in a block to the locals which have live storage at that point - storage_liveness: IndexVec>>, + storage_liveness: IndexVec>>, // A list of suspension points, generated during the transform suspension_points: Vec>, // The set of locals that have no `StorageLive`/`StorageDead` annotations. - always_live_locals: BitSet, + always_live_locals: DenseBitSet, // The original RETURN_PLACE local old_ret_local: Local, @@ -633,7 +633,7 @@ struct LivenessInfo { saved_locals: CoroutineSavedLocals, /// The set of saved locals live at each suspension point. - live_locals_at_suspension_points: Vec>, + live_locals_at_suspension_points: Vec>, /// Parallel vec to the above with SourceInfo for each yield terminator. source_info_at_suspension_points: Vec, @@ -645,7 +645,7 @@ struct LivenessInfo { /// For every suspending block, the locals which are storage-live across /// that suspension point. - storage_liveness: IndexVec>>, + storage_liveness: IndexVec>>, } /// Computes which locals have to be stored in the state-machine for the @@ -659,7 +659,7 @@ struct LivenessInfo { fn locals_live_across_suspend_points<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - always_live_locals: &BitSet, + always_live_locals: &DenseBitSet, movable: bool, ) -> LivenessInfo { // Calculate when MIR locals have live storage. This gives us an upper bound of their @@ -688,7 +688,7 @@ fn locals_live_across_suspend_points<'tcx>( let mut storage_liveness_map = IndexVec::from_elem(None, &body.basic_blocks); let mut live_locals_at_suspension_points = Vec::new(); let mut source_info_at_suspension_points = Vec::new(); - let mut live_locals_at_any_suspension_point = BitSet::new_empty(body.local_decls.len()); + let mut live_locals_at_any_suspension_point = DenseBitSet::new_empty(body.local_decls.len()); for (block, data) in body.basic_blocks.iter_enumerated() { if let TerminatorKind::Yield { .. } = data.terminator().kind { @@ -768,7 +768,7 @@ fn locals_live_across_suspend_points<'tcx>( /// `CoroutineSavedLocal` is indexed in terms of the elements in this set; /// i.e. `CoroutineSavedLocal::new(1)` corresponds to the second local /// included in this set. -struct CoroutineSavedLocals(BitSet); +struct CoroutineSavedLocals(DenseBitSet); impl CoroutineSavedLocals { /// Returns an iterator over each `CoroutineSavedLocal` along with the `Local` it corresponds @@ -777,11 +777,11 @@ impl CoroutineSavedLocals { self.iter().enumerate().map(|(i, l)| (CoroutineSavedLocal::from(i), l)) } - /// Transforms a `BitSet` that contains only locals saved across yield points to the - /// equivalent `BitSet`. - fn renumber_bitset(&self, input: &BitSet) -> BitSet { + /// Transforms a `DenseBitSet` that contains only locals saved across yield points to the + /// equivalent `DenseBitSet`. + fn renumber_bitset(&self, input: &DenseBitSet) -> DenseBitSet { assert!(self.superset(input), "{:?} not a superset of {:?}", self.0, input); - let mut out = BitSet::new_empty(self.count()); + let mut out = DenseBitSet::new_empty(self.count()); for (saved_local, local) in self.iter_enumerated() { if input.contains(local) { out.insert(saved_local); @@ -801,7 +801,7 @@ impl CoroutineSavedLocals { } impl ops::Deref for CoroutineSavedLocals { - type Target = BitSet; + type Target = DenseBitSet; fn deref(&self) -> &Self::Target { &self.0 @@ -815,7 +815,7 @@ impl ops::Deref for CoroutineSavedLocals { fn compute_storage_conflicts<'mir, 'tcx>( body: &'mir Body<'tcx>, saved_locals: &'mir CoroutineSavedLocals, - always_live_locals: BitSet, + always_live_locals: DenseBitSet, mut requires_storage: Results<'tcx, MaybeRequiresStorage<'mir, 'tcx>>, ) -> BitMatrix { assert_eq!(body.local_decls.len(), saved_locals.domain_size()); @@ -833,7 +833,7 @@ fn compute_storage_conflicts<'mir, 'tcx>( body, saved_locals, local_conflicts: BitMatrix::from_row_n(&ineligible_locals, body.local_decls.len()), - eligible_storage_live: BitSet::new_empty(body.local_decls.len()), + eligible_storage_live: DenseBitSet::new_empty(body.local_decls.len()), }; requires_storage.visit_reachable_with(body, &mut visitor); @@ -871,7 +871,7 @@ struct StorageConflictVisitor<'a, 'tcx> { // benchmarks for coroutines. local_conflicts: BitMatrix, // We keep this bitset as a buffer to avoid reallocating memory. - eligible_storage_live: BitSet, + eligible_storage_live: DenseBitSet, } impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, MaybeRequiresStorage<'a, 'tcx>> @@ -880,7 +880,7 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, MaybeRequiresStorage<'a, 'tcx>> fn visit_after_early_statement_effect( &mut self, _results: &mut Results<'tcx, MaybeRequiresStorage<'a, 'tcx>>, - state: &BitSet, + state: &DenseBitSet, _statement: &'a Statement<'tcx>, loc: Location, ) { @@ -890,7 +890,7 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, MaybeRequiresStorage<'a, 'tcx>> fn visit_after_early_terminator_effect( &mut self, _results: &mut Results<'tcx, MaybeRequiresStorage<'a, 'tcx>>, - state: &BitSet, + state: &DenseBitSet, _terminator: &'a Terminator<'tcx>, loc: Location, ) { @@ -899,7 +899,7 @@ impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, MaybeRequiresStorage<'a, 'tcx>> } impl StorageConflictVisitor<'_, '_> { - fn apply_state(&mut self, state: &BitSet, loc: Location) { + fn apply_state(&mut self, state: &DenseBitSet, loc: Location) { // Ignore unreachable blocks. if let TerminatorKind::Unreachable = self.body.basic_blocks[loc.block].terminator().kind { return; @@ -924,7 +924,7 @@ fn compute_layout<'tcx>( ) -> ( IndexVec, VariantIdx, FieldIdx)>>, CoroutineLayout<'tcx>, - IndexVec>>, + IndexVec>>, ) { let LivenessInfo { saved_locals, @@ -1044,11 +1044,14 @@ fn insert_switch<'tcx>( let switch = TerminatorKind::SwitchInt { discr: Operand::Move(discr), targets: switch_targets }; let source_info = SourceInfo::outermost(body.span); - body.basic_blocks_mut().raw.insert(0, BasicBlockData { - statements: vec![assign], - terminator: Some(Terminator { source_info, kind: switch }), - is_cleanup: false, - }); + body.basic_blocks_mut().raw.insert( + 0, + BasicBlockData { + statements: vec![assign], + terminator: Some(Terminator { source_info, kind: switch }), + is_cleanup: false, + }, + ); let blocks = body.basic_blocks_mut().iter_mut(); @@ -1058,9 +1061,8 @@ fn insert_switch<'tcx>( } fn elaborate_coroutine_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - use rustc_middle::mir::patch::MirPatch; - use rustc_mir_dataflow::elaborate_drops::{Unwind, elaborate_drop}; - + use crate::elaborate_drop::{Unwind, elaborate_drop}; + use crate::patch::MirPatch; use crate::shim::DropShimElaborator; // Note that `elaborate_drops` only drops the upvars of a coroutine, and @@ -1594,13 +1596,16 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { // (which is now a generator interior). let source_info = SourceInfo::outermost(body.span); let stmts = &mut body.basic_blocks_mut()[START_BLOCK].statements; - stmts.insert(0, Statement { - source_info, - kind: StatementKind::Assign(Box::new(( - old_resume_local.into(), - Rvalue::Use(Operand::Move(resume_local.into())), - ))), - }); + stmts.insert( + 0, + Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + old_resume_local.into(), + Rvalue::Use(Operand::Move(resume_local.into())), + ))), + }, + ); let always_live_locals = always_storage_live_locals(body); @@ -1688,6 +1693,10 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { // Run derefer to fix Derefs that are not in the first place deref_finder(tcx, body); } + + fn is_required(&self) -> bool { + true + } } /// Looks for any assignments between locals (e.g., `_4 = _5`) that will both be converted to fields @@ -1822,9 +1831,6 @@ impl<'tcx> Visitor<'tcx> for EnsureCoroutineFieldAssignmentsNeverAlias<'_> { fn check_suspend_tys<'tcx>(tcx: TyCtxt<'tcx>, layout: &CoroutineLayout<'tcx>, body: &Body<'tcx>) { let mut linted_tys = FxHashSet::default(); - // We want a user-facing param-env. - let param_env = tcx.param_env(body.source.def_id()); - for (variant, yield_source_info) in layout.variant_fields.iter().zip(&layout.variant_source_info) { @@ -1838,12 +1844,17 @@ fn check_suspend_tys<'tcx>(tcx: TyCtxt<'tcx>, layout: &CoroutineLayout<'tcx>, bo continue; }; - check_must_not_suspend_ty(tcx, decl.ty, hir_id, param_env, SuspendCheckData { - source_span: decl.source_info.span, - yield_span: yield_source_info.span, - plural_len: 1, - ..Default::default() - }); + check_must_not_suspend_ty( + tcx, + decl.ty, + hir_id, + SuspendCheckData { + source_span: decl.source_info.span, + yield_span: yield_source_info.span, + plural_len: 1, + ..Default::default() + }, + ); } } } @@ -1868,7 +1879,6 @@ fn check_must_not_suspend_ty<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, hir_id: hir::HirId, - param_env: ty::ParamEnv<'tcx>, data: SuspendCheckData<'_>, ) -> bool { if ty.is_unit() { @@ -1883,14 +1893,15 @@ fn check_must_not_suspend_ty<'tcx>( ty::Adt(_, args) if ty.is_box() => { let boxed_ty = args.type_at(0); let allocator_ty = args.type_at(1); - check_must_not_suspend_ty(tcx, boxed_ty, hir_id, param_env, SuspendCheckData { - descr_pre: &format!("{}boxed ", data.descr_pre), - ..data - }) || check_must_not_suspend_ty( + check_must_not_suspend_ty( + tcx, + boxed_ty, + hir_id, + SuspendCheckData { descr_pre: &format!("{}boxed ", data.descr_pre), ..data }, + ) || check_must_not_suspend_ty( tcx, allocator_ty, hir_id, - param_env, SuspendCheckData { descr_pre: &format!("{}allocator ", data.descr_pre), ..data }, ) } @@ -1905,10 +1916,12 @@ fn check_must_not_suspend_ty<'tcx>( { let def_id = poly_trait_predicate.trait_ref.def_id; let descr_pre = &format!("{}implementer{} of ", data.descr_pre, plural_suffix); - if check_must_not_suspend_def(tcx, def_id, hir_id, SuspendCheckData { - descr_pre, - ..data - }) { + if check_must_not_suspend_def( + tcx, + def_id, + hir_id, + SuspendCheckData { descr_pre, ..data }, + ) { has_emitted = true; break; } @@ -1922,10 +1935,12 @@ fn check_must_not_suspend_ty<'tcx>( if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { let def_id = trait_ref.def_id; let descr_post = &format!(" trait object{}{}", plural_suffix, data.descr_post); - if check_must_not_suspend_def(tcx, def_id, hir_id, SuspendCheckData { - descr_post, - ..data - }) { + if check_must_not_suspend_def( + tcx, + def_id, + hir_id, + SuspendCheckData { descr_post, ..data }, + ) { has_emitted = true; break; } @@ -1937,10 +1952,12 @@ fn check_must_not_suspend_ty<'tcx>( let mut has_emitted = false; for (i, ty) in fields.iter().enumerate() { let descr_post = &format!(" in tuple element {i}"); - if check_must_not_suspend_ty(tcx, ty, hir_id, param_env, SuspendCheckData { - descr_post, - ..data - }) { + if check_must_not_suspend_ty( + tcx, + ty, + hir_id, + SuspendCheckData { descr_post, ..data }, + ) { has_emitted = true; } } @@ -1948,21 +1965,23 @@ fn check_must_not_suspend_ty<'tcx>( } ty::Array(ty, len) => { let descr_pre = &format!("{}array{} of ", data.descr_pre, plural_suffix); - check_must_not_suspend_ty(tcx, ty, hir_id, param_env, SuspendCheckData { - descr_pre, - // FIXME(must_not_suspend): This is wrong. We should handle printing unevaluated consts. - plural_len: len.try_to_target_usize(tcx).unwrap_or(0) as usize + 1, - ..data - }) + check_must_not_suspend_ty( + tcx, + ty, + hir_id, + SuspendCheckData { + descr_pre, + // FIXME(must_not_suspend): This is wrong. We should handle printing unevaluated consts. + plural_len: len.try_to_target_usize(tcx).unwrap_or(0) as usize + 1, + ..data + }, + ) } // If drop tracking is enabled, we want to look through references, since the referent // may not be considered live across the await point. ty::Ref(_region, ty, _mutability) => { let descr_pre = &format!("{}reference{} to ", data.descr_pre, plural_suffix); - check_must_not_suspend_ty(tcx, ty, hir_id, param_env, SuspendCheckData { - descr_pre, - ..data - }) + check_must_not_suspend_ty(tcx, ty, hir_id, SuspendCheckData { descr_pre, ..data }) } _ => false, } diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index 9f5bb015fa335..0f5fcb0d8eb99 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -138,10 +138,10 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>( // If the parent capture is by-ref, then we need to apply an additional // deref before applying any further projections to this place. if parent_capture.is_by_ref() { - child_precise_captures.insert(0, Projection { - ty: parent_capture.place.ty(), - kind: ProjectionKind::Deref, - }); + child_precise_captures.insert( + 0, + Projection { ty: parent_capture.place.ty(), kind: ProjectionKind::Deref }, + ); } // If the child capture is by-ref, then we need to apply a "ref" // projection (i.e. `&`) at the end. But wait! We don't have that diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index 9e80f1f1c4ac2..039f346495be0 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -1,117 +1,182 @@ use std::cmp::Ordering; -use std::fmt::{self, Debug}; -use rustc_data_structures::captures::Captures; -use rustc_data_structures::fx::FxHashMap; +use either::Either; +use itertools::Itertools; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::graph::DirectedGraph; use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::coverage::{CounterId, CovTerm, Expression, ExpressionId, Op}; -use tracing::{debug, debug_span, instrument}; -use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, ReadyFirstTraversal}; +use crate::coverage::counters::balanced_flow::BalancedFlowGraph; +use crate::coverage::counters::node_flow::{ + CounterTerm, NodeCounters, NodeFlowData, node_flow_data_for_balanced_graph, +}; +use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; -#[cfg(test)] -mod tests; +mod balanced_flow; +pub(crate) mod node_flow; +mod union_find; -/// The coverage counter or counter expression associated with a particular -/// BCB node or BCB edge. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -enum BcbCounter { - Counter { id: CounterId }, - Expression { id: ExpressionId }, +/// Struct containing the results of [`prepare_bcb_counters_data`]. +pub(crate) struct BcbCountersData { + pub(crate) node_flow_data: NodeFlowData, + pub(crate) priority_list: Vec, } -impl BcbCounter { - fn as_term(&self) -> CovTerm { - match *self { - BcbCounter::Counter { id, .. } => CovTerm::Counter(id), - BcbCounter::Expression { id, .. } => CovTerm::Expression(id), - } - } +/// Analyzes the coverage graph to create intermediate data structures that +/// will later be used (during codegen) to create physical counters or counter +/// expressions for each BCB node that needs one. +pub(crate) fn prepare_bcb_counters_data(graph: &CoverageGraph) -> BcbCountersData { + // Create the derived graphs that are necessary for subsequent steps. + let balanced_graph = BalancedFlowGraph::for_graph(graph, |n| !graph[n].is_out_summable); + let node_flow_data = node_flow_data_for_balanced_graph(&balanced_graph); + + // Also create a "priority list" of coverage graph nodes, to help determine + // which ones get physical counters or counter expressions. This needs to + // be done now, because the later parts of the counter-creation process + // won't have access to the original coverage graph. + let priority_list = make_node_flow_priority_list(graph, balanced_graph); + + BcbCountersData { node_flow_data, priority_list } } -impl Debug for BcbCounter { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Counter { id, .. } => write!(fmt, "Counter({:?})", id.index()), - Self::Expression { id } => write!(fmt, "Expression({:?})", id.index()), - } - } +/// Arranges the nodes in `balanced_graph` into a list, such that earlier nodes +/// take priority in being given a counter expression instead of a physical counter. +fn make_node_flow_priority_list( + graph: &CoverageGraph, + balanced_graph: BalancedFlowGraph<&CoverageGraph>, +) -> Vec { + // A "reloop" node has exactly one out-edge, which jumps back to the top + // of an enclosing loop. Reloop nodes are typically visited more times + // than loop-exit nodes, so try to avoid giving them physical counters. + let is_reloop_node = IndexVec::from_fn_n( + |node| match graph.successors[node].as_slice() { + &[succ] => graph.dominates(succ, node), + _ => false, + }, + graph.num_nodes(), + ); + + let mut nodes = balanced_graph.iter_nodes().rev().collect::>(); + // The first node is the sink, which must not get a physical counter. + assert_eq!(nodes[0], balanced_graph.sink); + // Sort the real nodes, such that earlier (lesser) nodes take priority + // in being given a counter expression instead of a physical counter. + nodes[1..].sort_by(|&a, &b| { + // Start with a dummy `Equal` to make the actual tests line up nicely. + Ordering::Equal + // Prefer a physical counter for return/yield nodes. + .then_with(|| Ord::cmp(&graph[a].is_out_summable, &graph[b].is_out_summable)) + // Prefer an expression for reloop nodes (see definition above). + .then_with(|| Ord::cmp(&is_reloop_node[a], &is_reloop_node[b]).reverse()) + // Otherwise, prefer a physical counter for dominating nodes. + .then_with(|| graph.cmp_in_dominator_order(a, b).reverse()) + }); + nodes } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -struct BcbExpression { - lhs: BcbCounter, - op: Op, - rhs: BcbCounter, -} +// Converts node counters into a form suitable for embedding into MIR. +pub(crate) fn transcribe_counters( + old: &NodeCounters, + bcb_needs_counter: &DenseBitSet, + bcbs_seen: &DenseBitSet, +) -> CoverageCounters { + let mut new = CoverageCounters::with_num_bcbs(bcb_needs_counter.domain_size()); + + for bcb in bcb_needs_counter.iter() { + if !bcbs_seen.contains(bcb) { + // This BCB's code was removed by MIR opts, so its counter is always zero. + new.set_node_counter(bcb, CovTerm::Zero); + continue; + } + + // Our counter-creation algorithm doesn't guarantee that a node's list + // of terms starts or ends with a positive term, so partition the + // counters into "positive" and "negative" lists for easier handling. + let (mut pos, mut neg): (Vec<_>, Vec<_>) = old.counter_terms[bcb] + .iter() + // Filter out any BCBs that were removed by MIR opts; + // this treats them as having an execution count of 0. + .filter(|term| bcbs_seen.contains(term.node)) + .partition_map(|&CounterTerm { node, op }| match op { + Op::Add => Either::Left(node), + Op::Subtract => Either::Right(node), + }); + + // These intermediate sorts are not strictly necessary, but were helpful + // in reducing churn when switching to the current counter-creation scheme. + // They also help to slightly decrease the overall size of the expression + // table, due to more subexpressions being shared. + pos.sort(); + neg.sort(); + + let mut new_counters_for_sites = |sites: Vec| { + sites.into_iter().map(|node| new.ensure_phys_counter(node)).collect::>() + }; + let pos = new_counters_for_sites(pos); + let neg = new_counters_for_sites(neg); -/// Enum representing either a node or an edge in the coverage graph. -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub(super) enum Site { - Node { bcb: BasicCoverageBlock }, - Edge { from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock }, + let pos_counter = new.make_sum(&pos).unwrap_or(CovTerm::Zero); + let new_counter = new.make_subtracted_sum(pos_counter, &neg); + new.set_node_counter(bcb, new_counter); + } + + new } /// Generates and stores coverage counter and coverage expression information -/// associated with nodes/edges in the BCB graph. +/// associated with nodes in the coverage graph. pub(super) struct CoverageCounters { /// List of places where a counter-increment statement should be injected /// into MIR, each with its corresponding counter ID. - counter_increment_sites: IndexVec, + pub(crate) phys_counter_for_node: FxIndexMap, + pub(crate) next_counter_id: CounterId, /// Coverage counters/expressions that are associated with individual BCBs. - node_counters: IndexVec>, + pub(crate) node_counters: IndexVec>, /// Table of expression data, associating each expression ID with its /// corresponding operator (+ or -) and its LHS/RHS operands. - expressions: IndexVec, + pub(crate) expressions: IndexVec, /// Remember expressions that have already been created (or simplified), /// so that we don't create unnecessary duplicates. - expressions_memo: FxHashMap, + expressions_memo: FxHashMap, } impl CoverageCounters { - /// Ensures that each BCB node needing a counter has one, by creating physical - /// counters or counter expressions for nodes and edges as required. - pub(super) fn make_bcb_counters( - graph: &CoverageGraph, - bcb_needs_counter: &BitSet, - ) -> Self { - let mut builder = CountersBuilder::new(graph, bcb_needs_counter); - builder.make_bcb_counters(); - - builder.into_coverage_counters() - } - fn with_num_bcbs(num_bcbs: usize) -> Self { Self { - counter_increment_sites: IndexVec::new(), + phys_counter_for_node: FxIndexMap::default(), + next_counter_id: CounterId::ZERO, node_counters: IndexVec::from_elem_n(None, num_bcbs), expressions: IndexVec::new(), expressions_memo: FxHashMap::default(), } } - /// Creates a new physical counter for a BCB node or edge. - fn make_phys_counter(&mut self, site: Site) -> BcbCounter { - let id = self.counter_increment_sites.push(site); - BcbCounter::Counter { id } + /// Returns the physical counter for the given node, creating it if necessary. + fn ensure_phys_counter(&mut self, bcb: BasicCoverageBlock) -> CovTerm { + let id = *self.phys_counter_for_node.entry(bcb).or_insert_with(|| { + let id = self.next_counter_id; + self.next_counter_id = id + 1; + id + }); + CovTerm::Counter(id) } - fn make_expression(&mut self, lhs: BcbCounter, op: Op, rhs: BcbCounter) -> BcbCounter { - let new_expr = BcbExpression { lhs, op, rhs }; - *self.expressions_memo.entry(new_expr).or_insert_with(|| { + fn make_expression(&mut self, lhs: CovTerm, op: Op, rhs: CovTerm) -> CovTerm { + let new_expr = Expression { lhs, op, rhs }; + *self.expressions_memo.entry(new_expr.clone()).or_insert_with(|| { let id = self.expressions.push(new_expr); - BcbCounter::Expression { id } + CovTerm::Expression(id) }) } /// Creates a counter that is the sum of the given counters. /// /// Returns `None` if the given list of counters was empty. - fn make_sum(&mut self, counters: &[BcbCounter]) -> Option { + fn make_sum(&mut self, counters: &[CovTerm]) -> Option { counters .iter() .copied() @@ -119,16 +184,12 @@ impl CoverageCounters { } /// Creates a counter whose value is `lhs - SUM(rhs)`. - fn make_subtracted_sum(&mut self, lhs: BcbCounter, rhs: &[BcbCounter]) -> BcbCounter { + fn make_subtracted_sum(&mut self, lhs: CovTerm, rhs: &[CovTerm]) -> CovTerm { let Some(rhs_sum) = self.make_sum(rhs) else { return lhs }; self.make_expression(lhs, Op::Subtract, rhs_sum) } - pub(super) fn num_counters(&self) -> usize { - self.counter_increment_sites.len() - } - - fn set_node_counter(&mut self, bcb: BasicCoverageBlock, counter: BcbCounter) -> BcbCounter { + fn set_node_counter(&mut self, bcb: BasicCoverageBlock, counter: CovTerm) -> CovTerm { let existing = self.node_counters[bcb].replace(counter); assert!( existing.is_none(), @@ -136,453 +197,4 @@ impl CoverageCounters { ); counter } - - pub(super) fn term_for_bcb(&self, bcb: BasicCoverageBlock) -> Option { - self.node_counters[bcb].map(|counter| counter.as_term()) - } - - /// Returns an iterator over all the nodes/edges in the coverage graph that - /// should have a counter-increment statement injected into MIR, along with - /// each site's corresponding counter ID. - pub(super) fn counter_increment_sites( - &self, - ) -> impl Iterator + Captures<'_> { - self.counter_increment_sites.iter_enumerated().map(|(id, &site)| (id, site)) - } - - /// Returns an iterator over the subset of BCB nodes that have been associated - /// with a counter *expression*, along with the ID of that expression. - pub(super) fn bcb_nodes_with_coverage_expressions( - &self, - ) -> impl Iterator + Captures<'_> { - self.node_counters.iter_enumerated().filter_map(|(bcb, &counter)| match counter { - // Yield the BCB along with its associated expression ID. - Some(BcbCounter::Expression { id }) => Some((bcb, id)), - // This BCB is associated with a counter or nothing, so skip it. - Some(BcbCounter::Counter { .. }) | None => None, - }) - } - - pub(super) fn into_expressions(self) -> IndexVec { - let old_len = self.expressions.len(); - let expressions = self - .expressions - .into_iter() - .map(|BcbExpression { lhs, op, rhs }| Expression { - lhs: lhs.as_term(), - op, - rhs: rhs.as_term(), - }) - .collect::>(); - - // Expression IDs are indexes into this vector, so make sure we didn't - // accidentally invalidate them by changing its length. - assert_eq!(old_len, expressions.len()); - expressions - } -} - -/// Symbolic representation of the coverage counter to be used for a particular -/// node or edge in the coverage graph. The same site counter can be used for -/// multiple sites, if they have been determined to have the same count. -#[derive(Clone, Copy, Debug)] -enum SiteCounter { - /// A physical counter at some node/edge. - Phys { site: Site }, - /// A counter expression for a node that takes the sum of all its in-edge - /// counters. - NodeSumExpr { bcb: BasicCoverageBlock }, - /// A counter expression for an edge that takes the counter of its source - /// node, and subtracts the counters of all its sibling out-edges. - EdgeDiffExpr { from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock }, -} - -/// Yields the graph successors of `from_bcb` that aren't `to_bcb`. This is -/// used when creating a counter expression for [`SiteCounter::EdgeDiffExpr`]. -/// -/// For example, in this diagram the sibling out-edge targets of edge `AC` are -/// the nodes `B` and `D`. -/// -/// ```text -/// A -/// / | \ -/// B C D -/// ``` -fn sibling_out_edge_targets( - graph: &CoverageGraph, - from_bcb: BasicCoverageBlock, - to_bcb: BasicCoverageBlock, -) -> impl Iterator + Captures<'_> { - graph.successors[from_bcb].iter().copied().filter(move |&t| t != to_bcb) -} - -/// Helper struct that allows counter creation to inspect the BCB graph, and -/// the set of nodes that need counters. -struct CountersBuilder<'a> { - graph: &'a CoverageGraph, - bcb_needs_counter: &'a BitSet, - - site_counters: FxHashMap, -} - -impl<'a> CountersBuilder<'a> { - fn new(graph: &'a CoverageGraph, bcb_needs_counter: &'a BitSet) -> Self { - assert_eq!(graph.num_nodes(), bcb_needs_counter.domain_size()); - Self { graph, bcb_needs_counter, site_counters: FxHashMap::default() } - } - - fn make_bcb_counters(&mut self) { - debug!("make_bcb_counters(): adding a counter or expression to each BasicCoverageBlock"); - - // Traverse the coverage graph, ensuring that every node that needs a - // coverage counter has one. - for bcb in ReadyFirstTraversal::new(self.graph) { - let _span = debug_span!("traversal", ?bcb).entered(); - if self.bcb_needs_counter.contains(bcb) { - self.make_node_counter_and_out_edge_counters(bcb); - } - } - } - - /// Make sure the given node has a node counter, and then make sure each of - /// its out-edges has an edge counter (if appropriate). - #[instrument(level = "debug", skip(self))] - fn make_node_counter_and_out_edge_counters(&mut self, from_bcb: BasicCoverageBlock) { - // First, ensure that this node has a counter of some kind. - // We might also use that counter to compute one of the out-edge counters. - self.get_or_make_node_counter(from_bcb); - - // If this node's out-edges won't sum to the node's counter, - // then there's no reason to create edge counters here. - if !self.graph[from_bcb].is_out_summable { - return; - } - - // When choosing which out-edge should be given a counter expression, ignore edges that - // already have counters, or could use the existing counter of their target node. - let out_edge_has_counter = |to_bcb| { - if self.site_counters.contains_key(&Site::Edge { from_bcb, to_bcb }) { - return true; - } - self.graph.sole_predecessor(to_bcb) == Some(from_bcb) - && self.site_counters.contains_key(&Site::Node { bcb: to_bcb }) - }; - - // Determine the set of out-edges that could benefit from being given an expression. - let candidate_successors = self.graph.successors[from_bcb] - .iter() - .copied() - .filter(|&to_bcb| !out_edge_has_counter(to_bcb)) - .collect::>(); - debug!(?candidate_successors); - - // If there are out-edges without counters, choose one to be given an expression - // (computed from this node and the other out-edges) instead of a physical counter. - let Some(to_bcb) = self.choose_out_edge_for_expression(from_bcb, &candidate_successors) - else { - return; - }; - - // For each out-edge other than the one that was chosen to get an expression, - // ensure that it has a counter (existing counter/expression or a new counter). - for target in sibling_out_edge_targets(self.graph, from_bcb, to_bcb) { - self.get_or_make_edge_counter(from_bcb, target); - } - - // Now create an expression for the chosen edge, by taking the counter - // for its source node and subtracting the sum of its sibling out-edges. - let counter = SiteCounter::EdgeDiffExpr { from_bcb, to_bcb }; - self.site_counters.insert(Site::Edge { from_bcb, to_bcb }, counter); - } - - #[instrument(level = "debug", skip(self))] - fn get_or_make_node_counter(&mut self, bcb: BasicCoverageBlock) -> SiteCounter { - // If the BCB already has a counter, return it. - if let Some(&counter) = self.site_counters.get(&Site::Node { bcb }) { - debug!("{bcb:?} already has a counter: {counter:?}"); - return counter; - } - - let counter = self.make_node_counter_inner(bcb); - self.site_counters.insert(Site::Node { bcb }, counter); - counter - } - - fn make_node_counter_inner(&mut self, bcb: BasicCoverageBlock) -> SiteCounter { - // If the node's sole in-edge already has a counter, use that. - if let Some(sole_pred) = self.graph.sole_predecessor(bcb) - && let Some(&edge_counter) = - self.site_counters.get(&Site::Edge { from_bcb: sole_pred, to_bcb: bcb }) - { - return edge_counter; - } - - let predecessors = self.graph.predecessors[bcb].as_slice(); - - // Handle cases where we can't compute a node's count from its in-edges: - // - START_BCB has no in-edges, so taking the sum would panic (or be wrong). - // - For nodes with one in-edge, or that directly loop to themselves, - // trying to get the in-edge counts would require this node's counter, - // leading to infinite recursion. - if predecessors.len() <= 1 || predecessors.contains(&bcb) { - debug!(?bcb, ?predecessors, "node has <=1 predecessors or is its own predecessor"); - let counter = SiteCounter::Phys { site: Site::Node { bcb } }; - debug!(?bcb, ?counter, "node gets a physical counter"); - return counter; - } - - // A BCB with multiple incoming edges can compute its count by ensuring that counters - // exist for each of those edges, and then adding them up to get a total count. - for &from_bcb in predecessors { - self.get_or_make_edge_counter(from_bcb, bcb); - } - let sum_of_in_edges = SiteCounter::NodeSumExpr { bcb }; - - debug!("{bcb:?} gets a new counter (sum of predecessor counters): {sum_of_in_edges:?}"); - sum_of_in_edges - } - - #[instrument(level = "debug", skip(self))] - fn get_or_make_edge_counter( - &mut self, - from_bcb: BasicCoverageBlock, - to_bcb: BasicCoverageBlock, - ) -> SiteCounter { - // If the edge already has a counter, return it. - if let Some(&counter) = self.site_counters.get(&Site::Edge { from_bcb, to_bcb }) { - debug!("Edge {from_bcb:?}->{to_bcb:?} already has a counter: {counter:?}"); - return counter; - } - - let counter = self.make_edge_counter_inner(from_bcb, to_bcb); - self.site_counters.insert(Site::Edge { from_bcb, to_bcb }, counter); - counter - } - - fn make_edge_counter_inner( - &mut self, - from_bcb: BasicCoverageBlock, - to_bcb: BasicCoverageBlock, - ) -> SiteCounter { - // If the target node has exactly one in-edge (i.e. this one), then just - // use the node's counter, since it will have the same value. - if let Some(sole_pred) = self.graph.sole_predecessor(to_bcb) { - assert_eq!(sole_pred, from_bcb); - // This call must take care not to invoke `get_or_make_edge` for - // this edge, since that would result in infinite recursion! - return self.get_or_make_node_counter(to_bcb); - } - - // If the source node has exactly one out-edge (i.e. this one) and would have - // the same execution count as that edge, then just use the node's counter. - if let Some(simple_succ) = self.graph.simple_successor(from_bcb) { - assert_eq!(simple_succ, to_bcb); - return self.get_or_make_node_counter(from_bcb); - } - - // Make a new counter to count this edge. - let counter = SiteCounter::Phys { site: Site::Edge { from_bcb, to_bcb } }; - debug!(?from_bcb, ?to_bcb, ?counter, "edge gets a physical counter"); - counter - } - - /// Given a set of candidate out-edges (represented by their successor node), - /// choose one to be given a counter expression instead of a physical counter. - fn choose_out_edge_for_expression( - &self, - from_bcb: BasicCoverageBlock, - candidate_successors: &[BasicCoverageBlock], - ) -> Option { - // Try to find a candidate that leads back to the top of a loop, - // because reloop edges tend to be executed more times than loop-exit edges. - if let Some(reloop_target) = self.find_good_reloop_edge(from_bcb, &candidate_successors) { - debug!("Selecting reloop target {reloop_target:?} to get an expression"); - return Some(reloop_target); - } - - // We couldn't identify a "good" edge, so just choose an arbitrary one. - let arbitrary_target = candidate_successors.first().copied()?; - debug!(?arbitrary_target, "selecting arbitrary out-edge to get an expression"); - Some(arbitrary_target) - } - - /// Given a set of candidate out-edges (represented by their successor node), - /// tries to find one that leads back to the top of a loop. - /// - /// Reloop edges are good candidates for counter expressions, because they - /// will tend to be executed more times than a loop-exit edge, so it's nice - /// for them to be able to avoid a physical counter increment. - fn find_good_reloop_edge( - &self, - from_bcb: BasicCoverageBlock, - candidate_successors: &[BasicCoverageBlock], - ) -> Option { - // If there are no candidates, avoid iterating over the loop stack. - if candidate_successors.is_empty() { - return None; - } - - // Consider each loop on the current traversal context stack, top-down. - for loop_header_node in self.graph.loop_headers_containing(from_bcb) { - // Try to find a candidate edge that doesn't exit this loop. - for &target_bcb in candidate_successors { - // An edge is a reloop edge if its target dominates any BCB that has - // an edge back to the loop header. (Otherwise it's an exit edge.) - let is_reloop_edge = self - .graph - .reloop_predecessors(loop_header_node) - .any(|reloop_bcb| self.graph.dominates(target_bcb, reloop_bcb)); - if is_reloop_edge { - // We found a good out-edge to be given an expression. - return Some(target_bcb); - } - } - - // All of the candidate edges exit this loop, so keep looking - // for a good reloop edge for one of the outer loops. - } - - None - } - - fn into_coverage_counters(self) -> CoverageCounters { - Transcriber::new(&self).transcribe_counters() - } -} - -/// Helper struct for converting `CountersBuilder` into a final `CoverageCounters`. -struct Transcriber<'a> { - old: &'a CountersBuilder<'a>, - new: CoverageCounters, - phys_counter_for_site: FxHashMap, -} - -impl<'a> Transcriber<'a> { - fn new(old: &'a CountersBuilder<'a>) -> Self { - Self { - old, - new: CoverageCounters::with_num_bcbs(old.graph.num_nodes()), - phys_counter_for_site: FxHashMap::default(), - } - } - - fn transcribe_counters(mut self) -> CoverageCounters { - for bcb in self.old.bcb_needs_counter.iter() { - let site = Site::Node { bcb }; - let site_counter = self.site_counter(site); - - // Resolve the site counter into flat lists of nodes/edges whose - // physical counts contribute to the counter for this node. - // Distinguish between counts that will be added vs subtracted. - let mut pos = vec![]; - let mut neg = vec![]; - self.push_resolved_sites(site_counter, &mut pos, &mut neg); - - // Simplify by cancelling out sites that appear on both sides. - let (mut pos, mut neg) = sort_and_cancel(pos, neg); - - if pos.is_empty() { - // If we somehow end up with no positive terms after cancellation, - // fall back to creating a physical counter. There's no known way - // for this to happen, but it's hard to confidently rule it out. - debug_assert!(false, "{site:?} has no positive counter terms"); - pos = vec![Some(site)]; - neg = vec![]; - } - - let mut new_counters_for_sites = |sites: Vec>| { - sites - .into_iter() - .filter_map(|id| try { self.ensure_phys_counter(id?) }) - .collect::>() - }; - let mut pos = new_counters_for_sites(pos); - let mut neg = new_counters_for_sites(neg); - - pos.sort(); - neg.sort(); - - let pos_counter = self.new.make_sum(&pos).expect("`pos` should not be empty"); - let new_counter = self.new.make_subtracted_sum(pos_counter, &neg); - self.new.set_node_counter(bcb, new_counter); - } - - self.new - } - - fn site_counter(&self, site: Site) -> SiteCounter { - self.old.site_counters.get(&site).copied().unwrap_or_else(|| { - // We should have already created all necessary site counters. - // But if we somehow didn't, avoid crashing in release builds, - // and just use an extra physical counter instead. - debug_assert!(false, "{site:?} should have a counter"); - SiteCounter::Phys { site } - }) - } - - fn ensure_phys_counter(&mut self, site: Site) -> BcbCounter { - *self.phys_counter_for_site.entry(site).or_insert_with(|| self.new.make_phys_counter(site)) - } - - /// Resolves the given counter into flat lists of nodes/edges, whose counters - /// will then be added and subtracted to form a counter expression. - fn push_resolved_sites(&self, counter: SiteCounter, pos: &mut Vec, neg: &mut Vec) { - match counter { - SiteCounter::Phys { site } => pos.push(site), - SiteCounter::NodeSumExpr { bcb } => { - for &from_bcb in &self.old.graph.predecessors[bcb] { - let edge_counter = self.site_counter(Site::Edge { from_bcb, to_bcb: bcb }); - self.push_resolved_sites(edge_counter, pos, neg); - } - } - SiteCounter::EdgeDiffExpr { from_bcb, to_bcb } => { - // First, add the count for `from_bcb`. - let node_counter = self.site_counter(Site::Node { bcb: from_bcb }); - self.push_resolved_sites(node_counter, pos, neg); - - // Then subtract the counts for the other out-edges. - for target in sibling_out_edge_targets(self.old.graph, from_bcb, to_bcb) { - let edge_counter = self.site_counter(Site::Edge { from_bcb, to_bcb: target }); - // Swap `neg` and `pos` so that the counter is subtracted. - self.push_resolved_sites(edge_counter, neg, pos); - } - } - } - } -} - -/// Given two lists: -/// - Sorts each list. -/// - Converts each list to `Vec>`. -/// - Scans for values that appear in both lists, and cancels them out by -/// replacing matching pairs of values with `None`. -fn sort_and_cancel(mut pos: Vec, mut neg: Vec) -> (Vec>, Vec>) { - pos.sort(); - neg.sort(); - - // Convert to `Vec>`. If `T` has a niche, this should be zero-cost. - let mut pos = pos.into_iter().map(Some).collect::>(); - let mut neg = neg.into_iter().map(Some).collect::>(); - - // Scan through the lists using two cursors. When either cursor reaches the - // end of its list, there can be no more equal pairs, so stop. - let mut p = 0; - let mut n = 0; - while p < pos.len() && n < neg.len() { - // If the values are equal, remove them and advance both cursors. - // Otherwise, advance whichever cursor points to the lesser value. - // (Choosing which cursor to advance relies on both lists being sorted.) - match pos[p].cmp(&neg[n]) { - Ordering::Less => p += 1, - Ordering::Equal => { - pos[p] = None; - neg[n] = None; - p += 1; - n += 1; - } - Ordering::Greater => n += 1, - } - } - - (pos, neg) } diff --git a/compiler/rustc_mir_transform/src/coverage/counters/balanced_flow.rs b/compiler/rustc_mir_transform/src/coverage/counters/balanced_flow.rs new file mode 100644 index 0000000000000..4c20722a04347 --- /dev/null +++ b/compiler/rustc_mir_transform/src/coverage/counters/balanced_flow.rs @@ -0,0 +1,131 @@ +//! A control-flow graph can be said to have “balanced flow” if the flow +//! (execution count) of each node is equal to the sum of its in-edge flows, +//! and also equal to the sum of its out-edge flows. +//! +//! Control-flow graphs typically have one or more nodes that don't satisfy the +//! balanced-flow property, e.g.: +//! - The start node has out-edges, but no in-edges. +//! - Return nodes have in-edges, but no out-edges. +//! - `Yield` nodes can have an out-flow that is less than their in-flow. +//! - Inescapable loops cause the in-flow/out-flow relationship to break down. +//! +//! Balanced-flow graphs are nevertheless useful for analysis, so this module +//! provides a wrapper type ([`BalancedFlowGraph`]) that imposes balanced flow +//! on an underlying graph. This is done by non-destructively adding synthetic +//! nodes and edges as necessary. + +use rustc_data_structures::graph; +use rustc_data_structures::graph::iterate::DepthFirstSearch; +use rustc_data_structures::graph::reversed::ReversedGraph; +use rustc_index::Idx; +use rustc_index::bit_set::DenseBitSet; + +/// A view of an underlying graph that has been augmented to have “balanced flow”. +/// This means that the flow (execution count) of each node is equal to the +/// sum of its in-edge flows, and also equal to the sum of its out-edge flows. +/// +/// To achieve this, a synthetic "sink" node is non-destructively added to the +/// graph, with synthetic in-edges from these nodes: +/// - Any node that has no out-edges. +/// - Any node that explicitly requires a sink edge, as indicated by a +/// caller-supplied `force_sink_edge` function. +/// - Any node that would otherwise be unable to reach the sink, because it is +/// part of an inescapable loop. +/// +/// To make the graph fully balanced, there is also a synthetic edge from the +/// sink node back to the start node. +/// +/// --- +/// The benefit of having a balanced-flow graph is that it can be subsequently +/// transformed in ways that are guaranteed to preserve balanced flow +/// (e.g. merging nodes together), which is useful for discovering relationships +/// between the node flows of different nodes in the graph. +pub(crate) struct BalancedFlowGraph { + graph: G, + sink_edge_nodes: DenseBitSet, + pub(crate) sink: G::Node, +} + +impl BalancedFlowGraph { + /// Creates a balanced view of an underlying graph, by adding a synthetic + /// sink node that has in-edges from nodes that need or request such an edge, + /// and a single out-edge to the start node. + /// + /// Assumes that all nodes in the underlying graph are reachable from the + /// start node. + pub(crate) fn for_graph(graph: G, force_sink_edge: impl Fn(G::Node) -> bool) -> Self + where + G: graph::ControlFlowGraph, + { + let mut sink_edge_nodes = DenseBitSet::new_empty(graph.num_nodes()); + let mut dfs = DepthFirstSearch::new(ReversedGraph::new(&graph)); + + // First, determine the set of nodes that explicitly request or require + // an out-edge to the sink. + for node in graph.iter_nodes() { + if force_sink_edge(node) || graph.successors(node).next().is_none() { + sink_edge_nodes.insert(node); + dfs.push_start_node(node); + } + } + + // Next, find all nodes that are currently not reverse-reachable from + // `sink_edge_nodes`, and add them to the set as well. + dfs.complete_search(); + sink_edge_nodes.union_not(dfs.visited_set()); + + // The sink node is 1 higher than the highest real node. + let sink = G::Node::new(graph.num_nodes()); + + BalancedFlowGraph { graph, sink_edge_nodes, sink } + } +} + +impl graph::DirectedGraph for BalancedFlowGraph +where + G: graph::DirectedGraph, +{ + type Node = G::Node; + + /// Returns the number of nodes in this balanced-flow graph, which is 1 + /// more than the number of nodes in the underlying graph, to account for + /// the synthetic sink node. + fn num_nodes(&self) -> usize { + // The sink node's index is already the size of the underlying graph, + // so just add 1 to that instead. + self.sink.index() + 1 + } +} + +impl graph::StartNode for BalancedFlowGraph +where + G: graph::StartNode, +{ + fn start_node(&self) -> Self::Node { + self.graph.start_node() + } +} + +impl graph::Successors for BalancedFlowGraph +where + G: graph::StartNode + graph::Successors, +{ + fn successors(&self, node: Self::Node) -> impl Iterator { + let real_edges; + let sink_edge; + + if node == self.sink { + // The sink node has no real out-edges, and one synthetic out-edge + // to the start node. + real_edges = None; + sink_edge = Some(self.graph.start_node()); + } else { + // Real nodes have their real out-edges, and possibly one synthetic + // out-edge to the sink node. + real_edges = Some(self.graph.successors(node)); + sink_edge = self.sink_edge_nodes.contains(node).then_some(self.sink); + } + + real_edges.into_iter().flatten().chain(sink_edge) + } +} diff --git a/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs b/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs new file mode 100644 index 0000000000000..91ed54b8b59f5 --- /dev/null +++ b/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs @@ -0,0 +1,274 @@ +//! For each node in a control-flow graph, determines whether that node should +//! have a physical counter, or a counter expression that is derived from the +//! physical counters of other nodes. +//! +//! Based on the algorithm given in +//! "Optimal measurement points for program frequency counts" +//! (Knuth & Stevenson, 1973). + +use rustc_data_structures::graph; +use rustc_index::bit_set::DenseBitSet; +use rustc_index::{Idx, IndexSlice, IndexVec}; +pub(crate) use rustc_middle::mir::coverage::NodeFlowData; +use rustc_middle::mir::coverage::Op; + +use crate::coverage::counters::union_find::UnionFind; + +#[cfg(test)] +mod tests; + +/// Creates a "merged" view of an underlying graph. +/// +/// The given graph is assumed to have [“balanced flow”](balanced-flow), +/// though it does not necessarily have to be a `BalancedFlowGraph`. +/// +/// [balanced-flow]: `crate::coverage::counters::balanced_flow::BalancedFlowGraph`. +pub(crate) fn node_flow_data_for_balanced_graph(graph: G) -> NodeFlowData +where + G: graph::Successors, +{ + let mut supernodes = UnionFind::::new(graph.num_nodes()); + + // For each node, merge its successors into a single supernode, and + // arbitrarily choose one of those successors to represent all of them. + let successors = graph + .iter_nodes() + .map(|node| { + graph + .successors(node) + .reduce(|a, b| supernodes.unify(a, b)) + .expect("each node in a balanced graph must have at least one out-edge") + }) + .collect::>(); + + // Now that unification is complete, take a snapshot of the supernode forest, + // and resolve each arbitrarily-chosen successor to its canonical root. + // (This avoids having to explicitly resolve them later.) + let supernodes = supernodes.snapshot(); + let succ_supernodes = successors.into_iter().map(|succ| supernodes[succ]).collect(); + + NodeFlowData { supernodes, succ_supernodes } +} + +/// Uses the graph information in `node_flow_data`, together with a given +/// permutation of all nodes in the graph, to create physical counters and +/// counter expressions for each node in the underlying graph. +/// +/// The given list must contain exactly one copy of each node in the +/// underlying balanced-flow graph. The order of nodes is used as a hint to +/// influence counter allocation: +/// - Earlier nodes are more likely to receive counter expressions. +/// - Later nodes are more likely to receive physical counters. +pub(crate) fn make_node_counters( + node_flow_data: &NodeFlowData, + priority_list: &[Node], +) -> NodeCounters { + let mut builder = SpantreeBuilder::new(node_flow_data); + + for &node in priority_list { + builder.visit_node(node); + } + + NodeCounters { counter_terms: builder.finish() } +} + +/// End result of allocating physical counters and counter expressions for the +/// nodes of a graph. +#[derive(Debug)] +pub(crate) struct NodeCounters { + /// For the given node, returns the finished list of terms that represent + /// its physical counter or counter expression. Always non-empty. + /// + /// If a node was given a physical counter, the term list will contain + /// that counter as its sole element. + pub(crate) counter_terms: IndexVec>>, +} + +#[derive(Debug)] +struct SpantreeEdge { + /// If true, this edge in the spantree has been reversed an odd number of + /// times, so all physical counters added to its node's counter expression + /// need to be negated. + is_reversed: bool, + /// Each spantree edge is "claimed" by the (regular) node that caused it to + /// be created. When a node with a physical counter traverses this edge, + /// that counter is added to the claiming node's counter expression. + claiming_node: Node, + /// Supernode at the other end of this spantree edge. Transitively points + /// to the "root" of this supernode's spantree component. + span_parent: Node, +} + +/// Part of a node's counter expression, which is a sum of counter terms. +#[derive(Debug)] +pub(crate) struct CounterTerm { + /// Whether to add or subtract the value of the node's physical counter. + pub(crate) op: Op, + /// The node whose physical counter is represented by this term. + pub(crate) node: Node, +} + +#[derive(Debug)] +struct SpantreeBuilder<'a, Node: Idx> { + supernodes: &'a IndexSlice, + succ_supernodes: &'a IndexSlice, + + is_unvisited: DenseBitSet, + /// Links supernodes to each other, gradually forming a spanning tree of + /// the merged-flow graph. + /// + /// A supernode without a span edge is the root of its component of the + /// spantree. Nodes that aren't supernodes cannot have a spantree edge. + span_edges: IndexVec>>, + /// Shared path buffer recycled by all calls to `yank_to_spantree_root`. + yank_buffer: Vec, + /// An in-progress counter expression for each node. Each expression is + /// initially empty, and will be filled in as relevant nodes are visited. + counter_terms: IndexVec>>, +} + +impl<'a, Node: Idx> SpantreeBuilder<'a, Node> { + fn new(node_flow_data: &'a NodeFlowData) -> Self { + let NodeFlowData { supernodes, succ_supernodes } = node_flow_data; + let num_nodes = supernodes.len(); + Self { + supernodes, + succ_supernodes, + is_unvisited: DenseBitSet::new_filled(num_nodes), + span_edges: IndexVec::from_fn_n(|_| None, num_nodes), + yank_buffer: vec![], + counter_terms: IndexVec::from_fn_n(|_| vec![], num_nodes), + } + } + + fn is_supernode(&self, node: Node) -> bool { + self.supernodes[node] == node + } + + /// Given a supernode, finds the supernode that is the "root" of its + /// spantree component. Two nodes that have the same spantree root are + /// connected in the spantree. + fn spantree_root(&self, this: Node) -> Node { + debug_assert!(self.is_supernode(this)); + + match self.span_edges[this] { + None => this, + Some(SpantreeEdge { span_parent, .. }) => self.spantree_root(span_parent), + } + } + + /// Rotates edges in the spantree so that `this` is the root of its + /// spantree component. + fn yank_to_spantree_root(&mut self, this: Node) { + debug_assert!(self.is_supernode(this)); + + // The rotation is done iteratively, by first traversing from `this` to + // its root and storing the path in a buffer, and then traversing the + // path buffer backwards to reverse all the edges. + + // Recycle the same path buffer for all calls to this method. + let path_buf = &mut self.yank_buffer; + path_buf.clear(); + path_buf.push(this); + + // Traverse the spantree until we reach a supernode that has no + // span-parent, which must be the root. + let mut curr = this; + while let &Some(SpantreeEdge { span_parent, .. }) = &self.span_edges[curr] { + path_buf.push(span_parent); + curr = span_parent; + } + + // For each spantree edge `a -> b` in the path that was just traversed, + // reverse it to become `a <- b`, while preserving `claiming_node`. + for &[a, b] in path_buf.array_windows::<2>().rev() { + let SpantreeEdge { is_reversed, claiming_node, span_parent } = self.span_edges[a] + .take() + .expect("all nodes in the path (except the last) have a `span_parent`"); + debug_assert_eq!(span_parent, b); + debug_assert!(self.span_edges[b].is_none()); + self.span_edges[b] = + Some(SpantreeEdge { is_reversed: !is_reversed, claiming_node, span_parent: a }); + } + + // The result of the rotation is that `this` is now a spantree root. + debug_assert!(self.span_edges[this].is_none()); + } + + /// Must be called exactly once for each node in the balanced-flow graph. + fn visit_node(&mut self, this: Node) { + // Assert that this node was unvisited, and mark it visited. + assert!(self.is_unvisited.remove(this), "node has already been visited: {this:?}"); + + // Get the supernode containing `this`, and make it the root of its + // component of the spantree. + let this_supernode = self.supernodes[this]; + self.yank_to_spantree_root(this_supernode); + + // Get the supernode containing all of this's successors. + let succ_supernode = self.succ_supernodes[this]; + debug_assert!(self.is_supernode(succ_supernode)); + + // If two supernodes are already connected in the spantree, they will + // have the same spantree root. (Each supernode is connected to itself.) + if this_supernode != self.spantree_root(succ_supernode) { + // Adding this node's flow edge to the spantree would cause two + // previously-disconnected supernodes to become connected, so add + // it. That spantree-edge is now "claimed" by this node. + // + // Claiming a spantree-edge means that this node will get a counter + // expression instead of a physical counter. That expression is + // currently empty, but will be built incrementally as the other + // nodes are visited. + self.span_edges[this_supernode] = Some(SpantreeEdge { + is_reversed: false, + claiming_node: this, + span_parent: succ_supernode, + }); + } else { + // This node's flow edge would join two supernodes that are already + // connected in the spantree (or are the same supernode). That would + // create a cycle in the spantree, so don't add an edge. + // + // Instead, create a physical counter for this node, and add that + // counter to all expressions on the path from `succ_supernode` to + // `this_supernode`. + + // Instead of setting `this.measure = true` as in the original paper, + // we just add the node's ID to its own list of terms. + self.counter_terms[this].push(CounterTerm { node: this, op: Op::Add }); + + // Walk the spantree from `this.successor` back to `this`. For each + // spantree edge along the way, add this node's physical counter to + // the counter expression of the node that claimed the spantree edge. + let mut curr = succ_supernode; + while curr != this_supernode { + let &SpantreeEdge { is_reversed, claiming_node, span_parent } = + self.span_edges[curr].as_ref().unwrap(); + let op = if is_reversed { Op::Subtract } else { Op::Add }; + self.counter_terms[claiming_node].push(CounterTerm { node: this, op }); + + curr = span_parent; + } + } + } + + /// Asserts that all nodes have been visited, and returns the computed + /// counter expressions (made up of physical counters) for each node. + fn finish(self) -> IndexVec>> { + let Self { ref span_edges, ref is_unvisited, ref counter_terms, .. } = self; + assert!(is_unvisited.is_empty(), "some nodes were never visited: {is_unvisited:?}"); + debug_assert!( + span_edges + .iter_enumerated() + .all(|(node, span_edge)| { span_edge.is_some() <= self.is_supernode(node) }), + "only supernodes can have a span edge", + ); + debug_assert!( + counter_terms.iter().all(|terms| !terms.is_empty()), + "after visiting all nodes, every node should have at least one term", + ); + + self.counter_terms + } +} diff --git a/compiler/rustc_mir_transform/src/coverage/counters/node_flow/tests.rs b/compiler/rustc_mir_transform/src/coverage/counters/node_flow/tests.rs new file mode 100644 index 0000000000000..46c46c743c2c4 --- /dev/null +++ b/compiler/rustc_mir_transform/src/coverage/counters/node_flow/tests.rs @@ -0,0 +1,62 @@ +use itertools::Itertools; +use rustc_data_structures::graph; +use rustc_data_structures::graph::vec_graph::VecGraph; +use rustc_index::Idx; +use rustc_middle::mir::coverage::Op; + +use crate::coverage::counters::node_flow::{ + CounterTerm, NodeCounters, NodeFlowData, make_node_counters, node_flow_data_for_balanced_graph, +}; + +fn node_flow_data(graph: G) -> NodeFlowData { + node_flow_data_for_balanced_graph(graph) +} + +fn make_graph(num_nodes: usize, edge_pairs: Vec<(Node, Node)>) -> VecGraph { + VecGraph::new(num_nodes, edge_pairs) +} + +/// Example used in "Optimal Measurement Points for Program Frequency Counts" +/// (Knuth & Stevenson, 1973), but with 0-based node IDs. +#[test] +fn example_driver() { + let graph = make_graph::( + 5, + vec![(0, 1), (0, 3), (1, 0), (1, 2), (2, 1), (2, 4), (3, 3), (3, 4), (4, 0)], + ); + + let node_flow_data = node_flow_data(&graph); + let counters = make_node_counters(&node_flow_data, &[3, 1, 2, 0, 4]); + + assert_eq!( + format_counter_expressions(&counters), + &[ + // (comment to force vertical formatting for clarity) + "[0]: +c0", + "[1]: +c0 +c2 -c4", + "[2]: +c2", + "[3]: +c3", + "[4]: +c4", + ] + ); +} + +fn format_counter_expressions(counters: &NodeCounters) -> Vec { + let format_item = |&CounterTerm { node, op }| { + let op = match op { + Op::Subtract => '-', + Op::Add => '+', + }; + format!("{op}c{node:?}") + }; + + counters + .counter_terms + .indices() + .map(|node| { + let mut terms = counters.counter_terms[node].iter().collect::>(); + terms.sort_by_key(|item| item.node.index()); + format!("[{node:?}]: {}", terms.into_iter().map(format_item).join(" ")) + }) + .collect() +} diff --git a/compiler/rustc_mir_transform/src/coverage/counters/tests.rs b/compiler/rustc_mir_transform/src/coverage/counters/tests.rs deleted file mode 100644 index 794d4358f8237..0000000000000 --- a/compiler/rustc_mir_transform/src/coverage/counters/tests.rs +++ /dev/null @@ -1,41 +0,0 @@ -use std::fmt::Debug; - -use super::sort_and_cancel; - -fn flatten(input: Vec>) -> Vec { - input.into_iter().flatten().collect() -} - -fn sort_and_cancel_and_flatten(pos: Vec, neg: Vec) -> (Vec, Vec) { - let (pos_actual, neg_actual) = sort_and_cancel(pos, neg); - (flatten(pos_actual), flatten(neg_actual)) -} - -#[track_caller] -fn check_test_case( - pos: Vec, - neg: Vec, - pos_expected: Vec, - neg_expected: Vec, -) { - eprintln!("pos = {pos:?}; neg = {neg:?}"); - let output = sort_and_cancel_and_flatten(pos, neg); - assert_eq!(output, (pos_expected, neg_expected)); -} - -#[test] -fn cancellation() { - let cases: &[(Vec, Vec, Vec, Vec)] = &[ - (vec![], vec![], vec![], vec![]), - (vec![4, 2, 1, 5, 3], vec![], vec![1, 2, 3, 4, 5], vec![]), - (vec![5, 5, 5, 5, 5], vec![5], vec![5, 5, 5, 5], vec![]), - (vec![1, 1, 2, 2, 3, 3], vec![1, 2, 3], vec![1, 2, 3], vec![]), - (vec![1, 1, 2, 2, 3, 3], vec![2, 4, 2], vec![1, 1, 3, 3], vec![4]), - ]; - - for (pos, neg, pos_expected, neg_expected) in cases { - check_test_case(pos.to_vec(), neg.to_vec(), pos_expected.to_vec(), neg_expected.to_vec()); - // Same test case, but with its inputs flipped and its outputs flipped. - check_test_case(neg.to_vec(), pos.to_vec(), neg_expected.to_vec(), pos_expected.to_vec()); - } -} diff --git a/compiler/rustc_mir_transform/src/coverage/counters/union_find.rs b/compiler/rustc_mir_transform/src/coverage/counters/union_find.rs new file mode 100644 index 0000000000000..a826a953fa679 --- /dev/null +++ b/compiler/rustc_mir_transform/src/coverage/counters/union_find.rs @@ -0,0 +1,96 @@ +use std::cmp::Ordering; +use std::mem; + +use rustc_index::{Idx, IndexVec}; + +#[cfg(test)] +mod tests; + +/// Simple implementation of a union-find data structure, i.e. a disjoint-set +/// forest. +#[derive(Debug)] +pub(crate) struct UnionFind { + table: IndexVec>, +} + +#[derive(Debug)] +struct UnionFindEntry { + /// Transitively points towards the "root" of the set containing this key. + /// + /// Invariant: A root key is its own parent. + parent: Key, + /// When merging two "root" keys, their ranks determine which key becomes + /// the new root, to prevent the parent tree from becoming unnecessarily + /// tall. See [`UnionFind::unify`] for details. + rank: u32, +} + +impl UnionFind { + /// Creates a new disjoint-set forest containing the keys `0..num_keys`. + /// Initially, every key is part of its own one-element set. + pub(crate) fn new(num_keys: usize) -> Self { + // Initially, every key is the root of its own set, so its parent is itself. + Self { table: IndexVec::from_fn_n(|key| UnionFindEntry { parent: key, rank: 0 }, num_keys) } + } + + /// Returns the "root" key of the disjoint-set containing the given key. + /// If two keys have the same root, they belong to the same set. + /// + /// Also updates internal data structures to make subsequent `find` + /// operations faster. + pub(crate) fn find(&mut self, key: Key) -> Key { + // Loop until we find a key that is its own parent. + let mut curr = key; + while let parent = self.table[curr].parent + && curr != parent + { + // Perform "path compression" by peeking one layer ahead, and + // setting the current key's parent to that value. + // (This works even when `parent` is the root of its set, because + // of the invariant that a root is its own parent.) + let parent_parent = self.table[parent].parent; + self.table[curr].parent = parent_parent; + + // Advance by one step and continue. + curr = parent; + } + curr + } + + /// Merges the set containing `a` and the set containing `b` into one set. + /// + /// Returns the common root of both keys, after the merge. + pub(crate) fn unify(&mut self, a: Key, b: Key) -> Key { + let mut a = self.find(a); + let mut b = self.find(b); + + // If both keys have the same root, they're already in the same set, + // so there's nothing more to do. + if a == b { + return a; + }; + + // Ensure that `a` has strictly greater rank, swapping if necessary. + // If both keys have the same rank, increment the rank of `a` so that + // future unifications will also prefer `a`, leading to flatter trees. + match Ord::cmp(&self.table[a].rank, &self.table[b].rank) { + Ordering::Less => mem::swap(&mut a, &mut b), + Ordering::Equal => self.table[a].rank += 1, + Ordering::Greater => {} + } + + debug_assert!(self.table[a].rank > self.table[b].rank); + debug_assert_eq!(self.table[b].parent, b); + + // Make `a` the parent of `b`. + self.table[b].parent = a; + + a + } + + /// Takes a "snapshot" of the current state of this disjoint-set forest, in + /// the form of a vector that directly maps each key to its current root. + pub(crate) fn snapshot(&mut self) -> IndexVec { + self.table.indices().map(|key| self.find(key)).collect() + } +} diff --git a/compiler/rustc_mir_transform/src/coverage/counters/union_find/tests.rs b/compiler/rustc_mir_transform/src/coverage/counters/union_find/tests.rs new file mode 100644 index 0000000000000..34a4e4f8e6edc --- /dev/null +++ b/compiler/rustc_mir_transform/src/coverage/counters/union_find/tests.rs @@ -0,0 +1,32 @@ +use super::UnionFind; + +#[test] +fn empty() { + let mut sets = UnionFind::::new(10); + + for i in 1..10 { + assert_eq!(sets.find(i), i); + } +} + +#[test] +fn transitive() { + let mut sets = UnionFind::::new(10); + + sets.unify(3, 7); + sets.unify(4, 2); + + assert_eq!(sets.find(7), sets.find(3)); + assert_eq!(sets.find(2), sets.find(4)); + assert_ne!(sets.find(3), sets.find(4)); + + sets.unify(7, 4); + + assert_eq!(sets.find(7), sets.find(3)); + assert_eq!(sets.find(2), sets.find(4)); + assert_eq!(sets.find(3), sets.find(4)); + + for i in [0, 1, 5, 6, 8, 9] { + assert_eq!(sets.find(i), i); + } +} diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index ad6774fccd6f5..6d84248ddfbdd 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -1,14 +1,14 @@ use std::cmp::Ordering; -use std::collections::VecDeque; use std::ops::{Index, IndexMut}; -use std::{iter, mem, slice}; +use std::{mem, slice}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::graph::dominators::Dominators; use rustc_data_structures::graph::{self, DirectedGraph, StartNode}; use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; +pub(crate) use rustc_middle::mir::coverage::{BasicCoverageBlock, START_BCB}; use rustc_middle::mir::{self, BasicBlock, Terminator, TerminatorKind}; use tracing::debug; @@ -27,7 +27,7 @@ pub(crate) struct CoverageGraph { /// their relative order is consistent but arbitrary. dominator_order_rank: IndexVec, /// A loop header is a node that dominates one or more of its predecessors. - is_loop_header: BitSet, + is_loop_header: DenseBitSet, /// For each node, the loop header node of its nearest enclosing loop. /// This forms a linked list that can be traversed to find all enclosing loops. enclosing_loop_header: IndexVec>, @@ -72,7 +72,7 @@ impl CoverageGraph { predecessors, dominators: None, dominator_order_rank: IndexVec::from_elem_n(0, num_nodes), - is_loop_header: BitSet::new_empty(num_nodes), + is_loop_header: DenseBitSet::new_empty(num_nodes), enclosing_loop_header: IndexVec::from_elem_n(None, num_nodes), }; assert_eq!(num_nodes, this.num_nodes()); @@ -136,7 +136,7 @@ impl CoverageGraph { bb_to_bcb[bb] = Some(bcb); } - let is_out_summable = basic_blocks.last().map_or(false, |&bb| { + let is_out_summable = basic_blocks.last().is_some_and(|&bb| { bcb_filtered_successors(mir_body[bb].terminator()).is_out_summable() }); let bcb_data = BasicCoverageBlockData { basic_blocks, is_out_summable }; @@ -211,54 +211,6 @@ impl CoverageGraph { self.dominator_order_rank[a].cmp(&self.dominator_order_rank[b]) } - /// Returns the source of this node's sole in-edge, if it has exactly one. - /// That edge can be assumed to have the same execution count as the node - /// itself (in the absence of panics). - pub(crate) fn sole_predecessor( - &self, - to_bcb: BasicCoverageBlock, - ) -> Option { - // Unlike `simple_successor`, there is no need for extra checks here. - if let &[from_bcb] = self.predecessors[to_bcb].as_slice() { Some(from_bcb) } else { None } - } - - /// Returns the target of this node's sole out-edge, if it has exactly - /// one, but only if that edge can be assumed to have the same execution - /// count as the node itself (in the absence of panics). - pub(crate) fn simple_successor( - &self, - from_bcb: BasicCoverageBlock, - ) -> Option { - // If a node's count is the sum of its out-edges, and it has exactly - // one out-edge, then that edge has the same count as the node. - if self.bcbs[from_bcb].is_out_summable - && let &[to_bcb] = self.successors[from_bcb].as_slice() - { - Some(to_bcb) - } else { - None - } - } - - /// For each loop that contains the given node, yields the "loop header" - /// node representing that loop, from innermost to outermost. If the given - /// node is itself a loop header, it is yielded first. - pub(crate) fn loop_headers_containing( - &self, - bcb: BasicCoverageBlock, - ) -> impl Iterator + Captures<'_> { - let self_if_loop_header = self.is_loop_header.contains(bcb).then_some(bcb).into_iter(); - - let mut curr = Some(bcb); - let strictly_enclosing = iter::from_fn(move || { - let enclosing = self.enclosing_loop_header[curr?]; - curr = enclosing; - enclosing - }); - - self_if_loop_header.chain(strictly_enclosing) - } - /// For the given node, yields the subset of its predecessor nodes that /// it dominates. If that subset is non-empty, the node is a "loop header", /// and each of those predecessors represents an in-edge that jumps back to @@ -318,15 +270,6 @@ impl graph::Predecessors for CoverageGraph { } } -rustc_index::newtype_index! { - /// A node in the control-flow graph of CoverageGraph. - #[orderable] - #[debug_format = "bcb{}"] - pub(crate) struct BasicCoverageBlock { - const START_BCB = 0; - } -} - /// `BasicCoverageBlockData` holds the data indexed by a `BasicCoverageBlock`. /// /// A `BasicCoverageBlock` (BCB) represents the maximal-length sequence of MIR `BasicBlock`s without @@ -489,126 +432,3 @@ impl<'a, 'tcx> graph::Successors for CoverageRelevantSubgraph<'a, 'tcx> { self.coverage_successors(bb).into_iter() } } - -/// State of a node in the coverage graph during ready-first traversal. -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -enum ReadyState { - /// This node has not yet been added to the fallback queue or ready queue. - Unqueued, - /// This node is currently in the fallback queue. - InFallbackQueue, - /// This node's predecessors have all been visited, so it is in the ready queue. - /// (It might also have a stale entry in the fallback queue.) - InReadyQueue, - /// This node has been visited. - /// (It might also have a stale entry in the fallback queue.) - Visited, -} - -/// Iterator that visits nodes in the coverage graph, in an order that always -/// prefers "ready" nodes whose predecessors have already been visited. -pub(crate) struct ReadyFirstTraversal<'a> { - graph: &'a CoverageGraph, - - /// For each node, the number of its predecessor nodes that haven't been visited yet. - n_unvisited_preds: IndexVec, - /// Indicates whether a node has been visited, or which queue it is in. - state: IndexVec, - - /// Holds unvisited nodes whose predecessors have all been visited. - ready_queue: VecDeque, - /// Holds unvisited nodes with some unvisited predecessors. - /// Also contains stale entries for nodes that were upgraded to ready. - fallback_queue: VecDeque, -} - -impl<'a> ReadyFirstTraversal<'a> { - pub(crate) fn new(graph: &'a CoverageGraph) -> Self { - let num_nodes = graph.num_nodes(); - - let n_unvisited_preds = - IndexVec::from_fn_n(|node| graph.predecessors[node].len() as u32, num_nodes); - let mut state = IndexVec::from_elem_n(ReadyState::Unqueued, num_nodes); - - // We know from coverage graph construction that the start node is the - // only node with no predecessors. - debug_assert!( - n_unvisited_preds.iter_enumerated().all(|(node, &n)| (node == START_BCB) == (n == 0)) - ); - let ready_queue = VecDeque::from(vec![START_BCB]); - state[START_BCB] = ReadyState::InReadyQueue; - - Self { graph, state, n_unvisited_preds, ready_queue, fallback_queue: VecDeque::new() } - } - - /// Returns the next node from the ready queue, or else the next unvisited - /// node from the fallback queue. - fn next_inner(&mut self) -> Option { - // Always prefer to yield a ready node if possible. - if let Some(node) = self.ready_queue.pop_front() { - assert_eq!(self.state[node], ReadyState::InReadyQueue); - return Some(node); - } - - while let Some(node) = self.fallback_queue.pop_front() { - match self.state[node] { - // This entry in the fallback queue is not stale, so yield it. - ReadyState::InFallbackQueue => return Some(node), - // This node was added to the fallback queue, but later became - // ready and was visited via the ready queue. Ignore it here. - ReadyState::Visited => {} - // Unqueued nodes can't be in the fallback queue, by definition. - // We know that the ready queue is empty at this point. - ReadyState::Unqueued | ReadyState::InReadyQueue => unreachable!( - "unexpected state for {node:?} in the fallback queue: {:?}", - self.state[node] - ), - } - } - - None - } - - fn mark_visited_and_enqueue_successors(&mut self, node: BasicCoverageBlock) { - assert!(self.state[node] < ReadyState::Visited); - self.state[node] = ReadyState::Visited; - - // For each of this node's successors, decrease the successor's - // "unvisited predecessors" count, and enqueue it if appropriate. - for &succ in &self.graph.successors[node] { - let is_unqueued = match self.state[succ] { - ReadyState::Unqueued => true, - ReadyState::InFallbackQueue => false, - ReadyState::InReadyQueue => { - unreachable!("nodes in the ready queue have no unvisited predecessors") - } - // The successor was already visited via one of its other predecessors. - ReadyState::Visited => continue, - }; - - self.n_unvisited_preds[succ] -= 1; - if self.n_unvisited_preds[succ] == 0 { - // This node's predecessors have all been visited, so add it to - // the ready queue. If it's already in the fallback queue, that - // fallback entry will be ignored later. - self.state[succ] = ReadyState::InReadyQueue; - self.ready_queue.push_back(succ); - } else if is_unqueued { - // This node has unvisited predecessors, so add it to the - // fallback queue in case we run out of ready nodes later. - self.state[succ] = ReadyState::InFallbackQueue; - self.fallback_queue.push_back(succ); - } - } - } -} - -impl<'a> Iterator for ReadyFirstTraversal<'a> { - type Item = BasicCoverageBlock; - - fn next(&mut self) -> Option { - let node = self.next_inner()?; - self.mark_visited_and_enqueue_successors(node); - Some(node) - } -} diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs index 4185b3f4d4d85..d83c0d40a7e54 100644 --- a/compiler/rustc_mir_transform/src/coverage/mappings.rs +++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs @@ -1,9 +1,7 @@ use std::collections::BTreeSet; use rustc_data_structures::fx::FxIndexMap; -use rustc_data_structures::graph::DirectedGraph; use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; use rustc_middle::mir::coverage::{ BlockMarkerId, BranchSpan, ConditionId, ConditionInfo, CoverageInfoHi, CoverageKind, }; @@ -63,10 +61,6 @@ const MCDC_MAX_BITMAP_SIZE: usize = i32::MAX as usize; #[derive(Default)] pub(super) struct ExtractedMappings { - /// Store our own copy of [`CoverageGraph::num_nodes`], so that we don't - /// need access to the whole graph when allocating per-BCB data. This is - /// only public so that other code can still use exhaustive destructuring. - pub(super) num_bcbs: usize, pub(super) code_mappings: Vec, pub(super) branch_pairs: Vec, pub(super) mcdc_bitmap_bits: usize, @@ -118,7 +112,6 @@ pub(super) fn extract_all_mapping_info_from_mir<'tcx>( ); ExtractedMappings { - num_bcbs: graph.num_nodes(), code_mappings, branch_pairs, mcdc_bitmap_bits, @@ -127,60 +120,6 @@ pub(super) fn extract_all_mapping_info_from_mir<'tcx>( } } -impl ExtractedMappings { - pub(super) fn all_bcbs_with_counter_mappings(&self) -> BitSet { - // Fully destructure self to make sure we don't miss any fields that have mappings. - let Self { - num_bcbs, - code_mappings, - branch_pairs, - mcdc_bitmap_bits: _, - mcdc_degraded_branches, - mcdc_mappings, - } = self; - - // Identify which BCBs have one or more mappings. - let mut bcbs_with_counter_mappings = BitSet::new_empty(*num_bcbs); - let mut insert = |bcb| { - bcbs_with_counter_mappings.insert(bcb); - }; - - for &CodeMapping { span: _, bcb } in code_mappings { - insert(bcb); - } - for &BranchPair { true_bcb, false_bcb, .. } in branch_pairs { - insert(true_bcb); - insert(false_bcb); - } - for &MCDCBranch { true_bcb, false_bcb, .. } in mcdc_degraded_branches - .iter() - .chain(mcdc_mappings.iter().map(|(_, branches)| branches.into_iter()).flatten()) - { - insert(true_bcb); - insert(false_bcb); - } - - // MC/DC decisions refer to BCBs, but don't require those BCBs to have counters. - if bcbs_with_counter_mappings.is_empty() { - debug_assert!( - mcdc_mappings.is_empty(), - "A function with no counter mappings shouldn't have any decisions: {mcdc_mappings:?}", - ); - } - - bcbs_with_counter_mappings - } - - /// Returns the set of BCBs that have one or more `Code` mappings. - pub(super) fn bcbs_with_ordinary_code_mappings(&self) -> BitSet { - let mut bcbs = BitSet::new_empty(self.num_bcbs); - for &CodeMapping { span: _, bcb } in &self.code_mappings { - bcbs.insert(bcb); - } - bcbs - } -} - fn resolve_block_markers( coverage_info_hi: &CoverageInfoHi, mir_body: &mir::Body<'_>, @@ -367,9 +306,8 @@ fn calc_test_vectors_index(conditions: &mut Vec) -> usize { }) .collect::>(); - let mut queue = std::collections::VecDeque::from_iter( - next_conditions.swap_remove(&ConditionId::START).into_iter(), - ); + let mut queue = + std::collections::VecDeque::from_iter(next_conditions.swap_remove(&ConditionId::START)); num_paths_stats[ConditionId::START] = 1; let mut decision_end_nodes = Vec::new(); while let Some(branch) = queue.pop_front() { diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 57956448414b5..e195681bc92c7 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -15,16 +15,13 @@ use rustc_middle::hir::nested_filter; use rustc_middle::mir::coverage::{ CoverageKind, DecisionInfo, FunctionCoverageInfo, Mapping, MappingKind, }; -use rustc_middle::mir::{ - self, BasicBlock, BasicBlockData, SourceInfo, Statement, StatementKind, Terminator, - TerminatorKind, -}; +use rustc_middle::mir::{self, BasicBlock, Statement, StatementKind, TerminatorKind}; use rustc_middle::ty::TyCtxt; use rustc_span::Span; use rustc_span::def_id::LocalDefId; use tracing::{debug, debug_span, trace}; -use crate::coverage::counters::{CoverageCounters, Site}; +use crate::coverage::counters::BcbCountersData; use crate::coverage::graph::CoverageGraph; use crate::coverage::mappings::ExtractedMappings; @@ -64,6 +61,10 @@ impl<'tcx> crate::MirPass<'tcx> for InstrumentCoverage { instrument_function_for_coverage(tcx, mir_body); } + + fn is_required(&self) -> bool { + false + } } fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir::Body<'tcx>) { @@ -81,29 +82,21 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir: let extracted_mappings = mappings::extract_all_mapping_info_from_mir(tcx, mir_body, &hir_info, &graph); - //////////////////////////////////////////////////// - // Create an optimized mix of `Counter`s and `Expression`s for the `CoverageGraph`. Ensure - // every coverage span has a `Counter` or `Expression` assigned to its `BasicCoverageBlock` - // and all `Expression` dependencies (operands) are also generated, for any other - // `BasicCoverageBlock`s not already associated with a coverage span. - let bcbs_with_counter_mappings = extracted_mappings.all_bcbs_with_counter_mappings(); - if bcbs_with_counter_mappings.is_empty() { - // No relevant spans were found in MIR, so skip instrumenting this function. - return; - } - - let coverage_counters = - CoverageCounters::make_bcb_counters(&graph, &bcbs_with_counter_mappings); - - let mappings = create_mappings(&extracted_mappings, &coverage_counters); + let mappings = create_mappings(&extracted_mappings); if mappings.is_empty() { // No spans could be converted into valid mappings, so skip this function. debug!("no spans could be converted into valid mappings; skipping"); return; } - inject_coverage_statements(mir_body, &graph, &extracted_mappings, &coverage_counters); + // Use the coverage graph to prepare intermediate data that will eventually + // be used to assign physical counters and counter expressions to points in + // the control-flow graph + let BcbCountersData { node_flow_data, priority_list } = + counters::prepare_bcb_counters_data(&graph); + // Inject coverage statements into MIR. + inject_coverage_statements(mir_body, &graph); inject_mcdc_statements(mir_body, &graph, &extracted_mappings); let mcdc_num_condition_bitmaps = extracted_mappings @@ -116,29 +109,25 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir: mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo { function_source_hash: hir_info.function_source_hash, body_span: hir_info.body_span, - num_counters: coverage_counters.num_counters(), - mcdc_bitmap_bits: extracted_mappings.mcdc_bitmap_bits, - expressions: coverage_counters.into_expressions(), + + node_flow_data, + priority_list, + mappings, + + mcdc_bitmap_bits: extracted_mappings.mcdc_bitmap_bits, mcdc_num_condition_bitmaps, })); } -/// For each coverage span extracted from MIR, create a corresponding -/// mapping. +/// For each coverage span extracted from MIR, create a corresponding mapping. /// -/// Precondition: All BCBs corresponding to those spans have been given -/// coverage counters. -fn create_mappings( - extracted_mappings: &ExtractedMappings, - coverage_counters: &CoverageCounters, -) -> Vec { - let term_for_bcb = - |bcb| coverage_counters.term_for_bcb(bcb).expect("all BCBs with spans were given counters"); - +/// FIXME(Zalathar): This used to be where BCBs in the extracted mappings were +/// resolved to a `CovTerm`. But that is now handled elsewhere, so this +/// function can potentially be simplified even further. +fn create_mappings(extracted_mappings: &ExtractedMappings) -> Vec { // Fully destructure the mappings struct to make sure we don't miss any kinds. let ExtractedMappings { - num_bcbs: _, code_mappings, branch_pairs, mcdc_bitmap_bits: _, @@ -150,23 +139,18 @@ fn create_mappings( mappings.extend(code_mappings.iter().map( // Ordinary code mappings are the simplest kind. |&mappings::CodeMapping { span, bcb }| { - let kind = MappingKind::Code(term_for_bcb(bcb)); + let kind = MappingKind::Code { bcb }; Mapping { kind, span } }, )); mappings.extend(branch_pairs.iter().map( |&mappings::BranchPair { span, true_bcb, false_bcb }| { - let true_term = term_for_bcb(true_bcb); - let false_term = term_for_bcb(false_bcb); - let kind = MappingKind::Branch { true_term, false_term }; + let kind = MappingKind::Branch { true_bcb, false_bcb }; Mapping { kind, span } }, )); - let term_for_bcb = - |bcb| coverage_counters.term_for_bcb(bcb).expect("all BCBs with spans were given counters"); - // MCDC branch mappings are appended with their decisions in case decisions were ignored. mappings.extend(mcdc_degraded_branches.iter().map( |&mappings::MCDCBranch { @@ -176,15 +160,16 @@ fn create_mappings( condition_info: _, true_index: _, false_index: _, - }| { - let true_term = term_for_bcb(true_bcb); - let false_term = term_for_bcb(false_bcb); - Mapping { kind: MappingKind::Branch { true_term, false_term }, span } - }, + }| { Mapping { kind: MappingKind::Branch { true_bcb, false_bcb }, span } }, )); for (decision, branches) in mcdc_mappings { - let num_conditions = branches.len() as u16; + // FIXME(#134497): Previously it was possible for some of these branch + // conversions to fail, in which case the remaining branches in the + // decision would be degraded to plain `MappingKind::Branch`. + // The changes in #134497 made that failure impossible, because the + // fallible step was deferred to codegen. But the corresponding code + // in codegen wasn't updated to detect the need for a degrade step. let conditions = branches .into_iter() .map( @@ -196,12 +181,10 @@ fn create_mappings( true_index: _, false_index: _, }| { - let true_term = term_for_bcb(true_bcb); - let false_term = term_for_bcb(false_bcb); Mapping { kind: MappingKind::MCDCBranch { - true_term, - false_term, + true_bcb, + false_bcb, mcdc_params: condition_info, }, span, @@ -210,83 +193,23 @@ fn create_mappings( ) .collect::>(); - if conditions.len() == num_conditions as usize { - // LLVM requires end index for counter mapping regions. - let kind = MappingKind::MCDCDecision(DecisionInfo { - bitmap_idx: (decision.bitmap_idx + decision.num_test_vectors) as u32, - num_conditions, - }); - let span = decision.span; - mappings.extend(std::iter::once(Mapping { kind, span }).chain(conditions.into_iter())); - } else { - mappings.extend(conditions.into_iter().map(|mapping| { - let MappingKind::MCDCBranch { true_term, false_term, mcdc_params: _ } = - mapping.kind - else { - unreachable!("all mappings here are MCDCBranch as shown above"); - }; - Mapping { kind: MappingKind::Branch { true_term, false_term }, span: mapping.span } - })) - } + // LLVM requires end index for counter mapping regions. + let kind = MappingKind::MCDCDecision(DecisionInfo { + bitmap_idx: (decision.bitmap_idx + decision.num_test_vectors) as u32, + num_conditions: u16::try_from(conditions.len()).unwrap(), + }); + let span = decision.span; + mappings.extend(std::iter::once(Mapping { kind, span }).chain(conditions.into_iter())); } mappings } -/// For each BCB node or BCB edge that has an associated coverage counter, -/// inject any necessary coverage statements into MIR. -fn inject_coverage_statements<'tcx>( - mir_body: &mut mir::Body<'tcx>, - graph: &CoverageGraph, - extracted_mappings: &ExtractedMappings, - coverage_counters: &CoverageCounters, -) { - // Inject counter-increment statements into MIR. - for (id, site) in coverage_counters.counter_increment_sites() { - // Determine the block to inject a counter-increment statement into. - // For BCB nodes this is just their first block, but for edges we need - // to create a new block between the two BCBs, and inject into that. - let target_bb = match site { - Site::Node { bcb } => graph[bcb].leader_bb(), - Site::Edge { from_bcb, to_bcb } => { - // Create a new block between the last block of `from_bcb` and - // the first block of `to_bcb`. - let from_bb = graph[from_bcb].last_bb(); - let to_bb = graph[to_bcb].leader_bb(); - - let new_bb = inject_edge_counter_basic_block(mir_body, from_bb, to_bb); - debug!( - "Edge {from_bcb:?} (last {from_bb:?}) -> {to_bcb:?} (leader {to_bb:?}) \ - requires a new MIR BasicBlock {new_bb:?} for counter increment {id:?}", - ); - new_bb - } - }; - - inject_statement(mir_body, CoverageKind::CounterIncrement { id }, target_bb); - } - - // For each counter expression that is directly associated with at least one - // span, we inject an "expression-used" statement, so that coverage codegen - // can check whether the injected statement survived MIR optimization. - // (BCB edges can't have spans, so we only need to process BCB nodes here.) - // - // We only do this for ordinary `Code` mappings, because branch and MC/DC - // mappings might have expressions that don't correspond to any single - // point in the control-flow graph. - // - // See the code in `rustc_codegen_llvm::coverageinfo::map_data` that deals - // with "expressions seen" and "zero terms". - let eligible_bcbs = extracted_mappings.bcbs_with_ordinary_code_mappings(); - for (bcb, expression_id) in coverage_counters - .bcb_nodes_with_coverage_expressions() - .filter(|&(bcb, _)| eligible_bcbs.contains(bcb)) - { - inject_statement( - mir_body, - CoverageKind::ExpressionUsed { id: expression_id }, - graph[bcb].leader_bb(), - ); +/// Inject any necessary coverage statements into MIR, so that they influence codegen. +fn inject_coverage_statements<'tcx>(mir_body: &mut mir::Body<'tcx>, graph: &CoverageGraph) { + for (bcb, data) in graph.iter_enumerated() { + let target_bb = data.leader_bb(); + inject_statement(mir_body, CoverageKind::VirtualCounter { bcb }, target_bb); } } @@ -335,31 +258,6 @@ fn inject_mcdc_statements<'tcx>( } } -/// Given two basic blocks that have a control-flow edge between them, creates -/// and returns a new block that sits between those blocks. -fn inject_edge_counter_basic_block( - mir_body: &mut mir::Body<'_>, - from_bb: BasicBlock, - to_bb: BasicBlock, -) -> BasicBlock { - let span = mir_body[from_bb].terminator().source_info.span.shrink_to_hi(); - let new_bb = mir_body.basic_blocks_mut().push(BasicBlockData { - statements: vec![], // counter will be injected here - terminator: Some(Terminator { - source_info: SourceInfo::outermost(span), - kind: TerminatorKind::Goto { target: to_bb }, - }), - is_cleanup: false, - }); - let edge_ref = mir_body[from_bb] - .terminator_mut() - .successors_mut() - .find(|successor| **successor == to_bb) - .expect("from_bb should have a successor for to_bb"); - *edge_ref = new_bb; - new_bb -} - fn inject_statement(mir_body: &mut mir::Body<'_>, counter_kind: CoverageKind, bb: BasicBlock) { debug!(" injecting statement {counter_kind:?} for {bb:?}"); let data = &mut mir_body[bb]; diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index edaec3c796511..ef86358b2057a 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -1,22 +1,20 @@ use rustc_data_structures::captures::Captures; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc_middle::mir::coverage::{ - CounterId, CovTerm, CoverageIdsInfo, CoverageKind, Expression, ExpressionId, - FunctionCoverageInfo, MappingKind, Op, -}; +use rustc_middle::mir::coverage::{BasicCoverageBlock, CoverageIdsInfo, CoverageKind, MappingKind}; use rustc_middle::mir::{Body, Statement, StatementKind}; -use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::util::Providers; use rustc_span::def_id::LocalDefId; use rustc_span::sym; use tracing::trace; +use crate::coverage::counters::node_flow::make_node_counters; +use crate::coverage::counters::{CoverageCounters, transcribe_counters}; + /// Registers query/hook implementations related to coverage. pub(crate) fn provide(providers: &mut Providers) { - providers.hooks.is_eligible_for_coverage = - |TyCtxtAt { tcx, .. }, def_id| is_eligible_for_coverage(tcx, def_id); + providers.hooks.is_eligible_for_coverage = is_eligible_for_coverage; providers.queries.coverage_attr_on = coverage_attr_on; providers.queries.coverage_ids_info = coverage_ids_info; } @@ -87,49 +85,70 @@ fn coverage_attr_on(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { fn coverage_ids_info<'tcx>( tcx: TyCtxt<'tcx>, instance_def: ty::InstanceKind<'tcx>, -) -> CoverageIdsInfo { +) -> Option { let mir_body = tcx.instance_mir(instance_def); + let fn_cov_info = mir_body.function_coverage_info.as_deref()?; - let Some(fn_cov_info) = mir_body.function_coverage_info.as_deref() else { - return CoverageIdsInfo { - counters_seen: BitSet::new_empty(0), - zero_expressions: BitSet::new_empty(0), - }; - }; - - let mut counters_seen = BitSet::new_empty(fn_cov_info.num_counters); - let mut expressions_seen = BitSet::new_filled(fn_cov_info.expressions.len()); - - // For each expression ID that is directly used by one or more mappings, - // mark it as not-yet-seen. This indicates that we expect to see a - // corresponding `ExpressionUsed` statement during MIR traversal. - for mapping in fn_cov_info.mappings.iter() { - // Currently we only worry about ordinary code mappings. - // For branch and MC/DC mappings, expressions might not correspond - // to any particular point in the control-flow graph. - // (Keep this in sync with the injection of `ExpressionUsed` - // statements in the `InstrumentCoverage` MIR pass.) - if let MappingKind::Code(CovTerm::Expression(id)) = mapping.kind { - expressions_seen.remove(id); - } - } - + // Scan through the final MIR to see which BCBs survived MIR opts. + // Any BCB not in this set was optimized away. + let mut bcbs_seen = DenseBitSet::new_empty(fn_cov_info.priority_list.len()); for kind in all_coverage_in_mir_body(mir_body) { match *kind { - CoverageKind::CounterIncrement { id } => { - counters_seen.insert(id); - } - CoverageKind::ExpressionUsed { id } => { - expressions_seen.insert(id); + CoverageKind::VirtualCounter { bcb } => { + bcbs_seen.insert(bcb); } _ => {} } } - let zero_expressions = - identify_zero_expressions(fn_cov_info, &counters_seen, &expressions_seen); + // Determine the set of BCBs that are referred to by mappings, and therefore + // need a counter. Any node not in this set will only get a counter if it + // is part of the counter expression for a node that is in the set. + let mut bcb_needs_counter = + DenseBitSet::::new_empty(fn_cov_info.priority_list.len()); + for mapping in &fn_cov_info.mappings { + match mapping.kind { + MappingKind::Code { bcb } => { + bcb_needs_counter.insert(bcb); + } + MappingKind::Branch { true_bcb, false_bcb } => { + bcb_needs_counter.insert(true_bcb); + bcb_needs_counter.insert(false_bcb); + } + MappingKind::MCDCBranch { true_bcb, false_bcb, mcdc_params: _ } => { + bcb_needs_counter.insert(true_bcb); + bcb_needs_counter.insert(false_bcb); + } + MappingKind::MCDCDecision(_) => {} + } + } - CoverageIdsInfo { counters_seen, zero_expressions } + // Clone the priority list so that we can re-sort it. + let mut priority_list = fn_cov_info.priority_list.clone(); + // The first ID in the priority list represents the synthetic "sink" node, + // and must remain first so that it _never_ gets a physical counter. + debug_assert_eq!(priority_list[0], priority_list.iter().copied().max().unwrap()); + assert!(!bcbs_seen.contains(priority_list[0])); + // Partition the priority list, so that unreachable nodes (removed by MIR opts) + // are sorted later and therefore are _more_ likely to get a physical counter. + // This is counter-intuitive, but it means that `transcribe_counters` can + // easily skip those unused physical counters and replace them with zero. + // (The original ordering remains in effect within both partitions.) + priority_list[1..].sort_by_key(|&bcb| !bcbs_seen.contains(bcb)); + + let node_counters = make_node_counters(&fn_cov_info.node_flow_data, &priority_list); + let coverage_counters = transcribe_counters(&node_counters, &bcb_needs_counter, &bcbs_seen); + + let CoverageCounters { + phys_counter_for_node, next_counter_id, node_counters, expressions, .. + } = coverage_counters; + + Some(CoverageIdsInfo { + num_counters: next_counter_id.as_u32(), + phys_counter_for_node, + term_for_bcb: node_counters, + expressions, + }) } fn all_coverage_in_mir_body<'a, 'tcx>( @@ -147,94 +166,3 @@ fn is_inlined(body: &Body<'_>, statement: &Statement<'_>) -> bool { let scope_data = &body.source_scopes[statement.source_info.scope]; scope_data.inlined.is_some() || scope_data.inlined_parent_scope.is_some() } - -/// Identify expressions that will always have a value of zero, and note -/// their IDs in a `BitSet`. Mappings that refer to a zero expression -/// can instead become mappings to a constant zero value. -/// -/// This function mainly exists to preserve the simplifications that were -/// already being performed by the Rust-side expression renumbering, so that -/// the resulting coverage mappings don't get worse. -fn identify_zero_expressions( - fn_cov_info: &FunctionCoverageInfo, - counters_seen: &BitSet, - expressions_seen: &BitSet, -) -> BitSet { - // The set of expressions that either were optimized out entirely, or - // have zero as both of their operands, and will therefore always have - // a value of zero. Other expressions that refer to these as operands - // can have those operands replaced with `CovTerm::Zero`. - let mut zero_expressions = BitSet::new_empty(fn_cov_info.expressions.len()); - - // Simplify a copy of each expression based on lower-numbered expressions, - // and then update the set of always-zero expressions if necessary. - // (By construction, expressions can only refer to other expressions - // that have lower IDs, so one pass is sufficient.) - for (id, expression) in fn_cov_info.expressions.iter_enumerated() { - if !expressions_seen.contains(id) { - // If an expression was not seen, it must have been optimized away, - // so any operand that refers to it can be replaced with zero. - zero_expressions.insert(id); - continue; - } - - // We don't need to simplify the actual expression data in the - // expressions list; we can just simplify a temporary copy and then - // use that to update the set of always-zero expressions. - let Expression { mut lhs, op, mut rhs } = *expression; - - // If an expression has an operand that is also an expression, the - // operand's ID must be strictly lower. This is what lets us find - // all zero expressions in one pass. - let assert_operand_expression_is_lower = |operand_id: ExpressionId| { - assert!( - operand_id < id, - "Operand {operand_id:?} should be less than {id:?} in {expression:?}", - ) - }; - - // If an operand refers to a counter or expression that is always - // zero, then that operand can be replaced with `CovTerm::Zero`. - let maybe_set_operand_to_zero = |operand: &mut CovTerm| { - if let CovTerm::Expression(id) = *operand { - assert_operand_expression_is_lower(id); - } - - if is_zero_term(&counters_seen, &zero_expressions, *operand) { - *operand = CovTerm::Zero; - } - }; - maybe_set_operand_to_zero(&mut lhs); - maybe_set_operand_to_zero(&mut rhs); - - // Coverage counter values cannot be negative, so if an expression - // involves subtraction from zero, assume that its RHS must also be zero. - // (Do this after simplifications that could set the LHS to zero.) - if lhs == CovTerm::Zero && op == Op::Subtract { - rhs = CovTerm::Zero; - } - - // After the above simplifications, if both operands are zero, then - // we know that this expression is always zero too. - if lhs == CovTerm::Zero && rhs == CovTerm::Zero { - zero_expressions.insert(id); - } - } - - zero_expressions -} - -/// Returns `true` if the given term is known to have a value of zero, taking -/// into account knowledge of which counters are unused and which expressions -/// are always zero. -fn is_zero_term( - counters_seen: &BitSet, - zero_expressions: &BitSet, - term: CovTerm, -) -> bool { - match term { - CovTerm::Zero => true, - CovTerm::Counter(id) => !counters_seen.contains(id), - CovTerm::Expression(id) => zero_expressions.contains(id), - } -} diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index 26ce743be3613..73b68d7155cfb 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -137,8 +137,7 @@ fn filtered_statement_span(statement: &Statement<'_>) -> Option { // These coverage statements should not exist prior to coverage instrumentation. StatementKind::Coverage( - CoverageKind::CounterIncrement { .. } - | CoverageKind::ExpressionUsed { .. } + CoverageKind::VirtualCounter { .. } | CoverageKind::CondBitmapUpdate { .. } | CoverageKind::TestVectorBitmapUpdate { .. }, ) => bug!( diff --git a/compiler/rustc_mir_transform/src/coverage/tests.rs b/compiler/rustc_mir_transform/src/coverage/tests.rs index b2ee50de50a23..3c0053c610d14 100644 --- a/compiler/rustc_mir_transform/src/coverage/tests.rs +++ b/compiler/rustc_mir_transform/src/coverage/tests.rs @@ -129,15 +129,18 @@ impl<'tcx> MockBlocks<'tcx> { } fn call(&mut self, some_from_block: Option) -> BasicBlock { - self.add_block_from(some_from_block, TerminatorKind::Call { - func: Operand::Copy(self.dummy_place.clone()), - args: [].into(), - destination: self.dummy_place.clone(), - target: Some(TEMP_BLOCK), - unwind: UnwindAction::Continue, - call_source: CallSource::Misc, - fn_span: DUMMY_SP, - }) + self.add_block_from( + some_from_block, + TerminatorKind::Call { + func: Operand::Copy(self.dummy_place.clone()), + args: [].into(), + destination: self.dummy_place.clone(), + target: Some(TEMP_BLOCK), + unwind: UnwindAction::Continue, + call_source: CallSource::Misc, + fn_span: DUMMY_SP, + }, + ) } fn goto(&mut self, some_from_block: Option) -> BasicBlock { diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs index e1f1dd83f0df4..4f45d9588a890 100644 --- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs +++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs @@ -46,7 +46,7 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { // #[inline(never)] to force code generation. match codegen_fn_attrs.inline { InlineAttr::Never => return false, - InlineAttr::Hint | InlineAttr::Always => return true, + InlineAttr::Hint | InlineAttr::Always | InlineAttr::Force { .. } => return true, _ => {} } @@ -69,8 +69,9 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { // Don't do any inference if codegen optimizations are disabled and also MIR inlining is not // enabled. This ensures that we do inference even if someone only passes -Zinline-mir, // which is less confusing than having to also enable -Copt-level=1. - if matches!(tcx.sess.opts.optimize, OptLevel::No) && !pm::should_run_pass(tcx, &inline::Inline) - { + let inliner_will_run = pm::should_run_pass(tcx, &inline::Inline, pm::Optimizations::Allowed) + || inline::ForceInline::should_run_pass_for_callee(tcx, def_id.to_def_id()); + if matches!(tcx.sess.opts.optimize, OptLevel::No) && !inliner_will_run { return false; } diff --git a/compiler/rustc_mir_transform/src/ctfe_limit.rs b/compiler/rustc_mir_transform/src/ctfe_limit.rs index bd58b1b668997..d0b313e149aca 100644 --- a/compiler/rustc_mir_transform/src/ctfe_limit.rs +++ b/compiler/rustc_mir_transform/src/ctfe_limit.rs @@ -36,6 +36,10 @@ impl<'tcx> crate::MirPass<'tcx> for CtfeLimit { ); } } + + fn is_required(&self) -> bool { + true + } } fn has_back_edge( diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index cc44114782cf3..90173da17f0fc 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -71,6 +71,10 @@ impl<'tcx> crate::MirPass<'tcx> for DataflowConstProp { let mut patch = visitor.patch; debug_span!("patch").in_scope(|| patch.visit_body_preserves_cfg(body)); } + + fn is_required(&self) -> bool { + false + } } // Note: Currently, places that have their reference taken cannot be tracked. Although this would @@ -408,6 +412,18 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { state: &mut State>, ) -> ValueOrPlace> { let val = match rvalue { + Rvalue::Len(place) => { + let place_ty = place.ty(self.local_decls, self.tcx); + if let ty::Array(_, len) = place_ty.ty.kind() { + Const::Ty(self.tcx.types.usize, *len) + .try_eval_scalar(self.tcx, self.typing_env) + .map_or(FlatSet::Top, FlatSet::Elem) + } else if let [ProjectionElem::Deref] = place.projection[..] { + state.get_len(place.local.into(), &self.map) + } else { + FlatSet::Top + } + } Rvalue::Cast(CastKind::IntToInt | CastKind::IntToFloat, operand, ty) => { let Ok(layout) = self.tcx.layout_of(self.typing_env.as_query_input(*ty)) else { return ValueOrPlace::Value(FlatSet::Top); @@ -488,7 +504,8 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { | Rvalue::Cast(..) | Rvalue::BinaryOp(..) | Rvalue::Aggregate(..) - | Rvalue::ShallowInitBox(..) => { + | Rvalue::ShallowInitBox(..) + | Rvalue::WrapUnsafeBinder(..) => { // No modification is possible through these r-values. return ValueOrPlace::TOP; } diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index 0c75cdadc92d7..eea2b0990d730 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -26,8 +26,8 @@ use crate::util::is_within_packed; /// Performs the optimization on the body /// -/// The `borrowed` set must be a `BitSet` of all the locals that are ever borrowed in this body. It -/// can be generated via the [`borrowed_locals`] function. +/// The `borrowed` set must be a `DenseBitSet` of all the locals that are ever borrowed in this +/// body. It can be generated via the [`borrowed_locals`] function. fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let borrowed_locals = borrowed_locals(body); @@ -147,4 +147,8 @@ impl<'tcx> crate::MirPass<'tcx> for DeadStoreElimination { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { eliminate(tcx, body); } + + fn is_required(&self) -> bool { + false + } } diff --git a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs index 67b215c7c9d63..049f13ce96d74 100644 --- a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs +++ b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs @@ -6,7 +6,7 @@ //! dependent crates can use them. use rustc_hir::def_id::LocalDefId; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{Body, Location, Operand, Place, RETURN_PLACE, Terminator, TerminatorKind}; use rustc_middle::ty::{self, DeducedParamAttrs, Ty, TyCtxt}; @@ -18,13 +18,13 @@ struct DeduceReadOnly { /// Each bit is indexed by argument number, starting at zero (so 0 corresponds to local decl /// 1). The bit is true if the argument may have been mutated or false if we know it hasn't /// been up to the point we're at. - mutable_args: BitSet, + mutable_args: DenseBitSet, } impl DeduceReadOnly { /// Returns a new DeduceReadOnly instance. fn new(arg_count: usize) -> Self { - Self { mutable_args: BitSet::new_empty(arg_count) } + Self { mutable_args: DenseBitSet::new_empty(arg_count) } } } diff --git a/compiler/rustc_mir_transform/src/deduplicate_blocks.rs b/compiler/rustc_mir_transform/src/deduplicate_blocks.rs deleted file mode 100644 index 26b28c8c4870e..0000000000000 --- a/compiler/rustc_mir_transform/src/deduplicate_blocks.rs +++ /dev/null @@ -1,191 +0,0 @@ -//! This pass finds basic blocks that are completely equal, -//! and replaces all uses with just one of them. - -use std::collections::hash_map::Entry; -use std::hash::{Hash, Hasher}; -use std::iter; - -use rustc_data_structures::fx::FxHashMap; -use rustc_middle::mir::visit::MutVisitor; -use rustc_middle::mir::*; -use rustc_middle::ty::TyCtxt; -use tracing::debug; - -use super::simplify::simplify_cfg; - -pub(super) struct DeduplicateBlocks; - -impl<'tcx> crate::MirPass<'tcx> for DeduplicateBlocks { - fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - sess.mir_opt_level() >= 4 - } - - fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - debug!("Running DeduplicateBlocks on `{:?}`", body.source); - let duplicates = find_duplicates(body); - let has_opts_to_apply = !duplicates.is_empty(); - - if has_opts_to_apply { - let mut opt_applier = OptApplier { tcx, duplicates }; - opt_applier.visit_body(body); - simplify_cfg(body); - } - } -} - -struct OptApplier<'tcx> { - tcx: TyCtxt<'tcx>, - duplicates: FxHashMap, -} - -impl<'tcx> MutVisitor<'tcx> for OptApplier<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) { - for target in terminator.successors_mut() { - if let Some(replacement) = self.duplicates.get(target) { - debug!("SUCCESS: Replacing: `{:?}` with `{:?}`", target, replacement); - *target = *replacement; - } - } - - self.super_terminator(terminator, location); - } -} - -fn find_duplicates(body: &Body<'_>) -> FxHashMap { - let mut duplicates = FxHashMap::default(); - - let bbs_to_go_through = - body.basic_blocks.iter_enumerated().filter(|(_, bbd)| !bbd.is_cleanup).count(); - - let mut same_hashes = - FxHashMap::with_capacity_and_hasher(bbs_to_go_through, Default::default()); - - // Go through the basic blocks backwards. This means that in case of duplicates, - // we can use the basic block with the highest index as the replacement for all lower ones. - // For example, if bb1, bb2 and bb3 are duplicates, we will first insert bb3 in same_hashes. - // Then we will see that bb2 is a duplicate of bb3, - // and insert bb2 with the replacement bb3 in the duplicates list. - // When we see bb1, we see that it is a duplicate of bb3, and therefore insert it in the - // duplicates list with replacement bb3. - // When the duplicates are removed, we will end up with only bb3. - for (bb, bbd) in body.basic_blocks.iter_enumerated().rev().filter(|(_, bbd)| !bbd.is_cleanup) { - // Basic blocks can get really big, so to avoid checking for duplicates in basic blocks - // that are unlikely to have duplicates, we stop early. The early bail number has been - // found experimentally by eprintln while compiling the crates in the rustc-perf suite. - if bbd.statements.len() > 10 { - continue; - } - - let to_hash = BasicBlockHashable { basic_block_data: bbd }; - let entry = same_hashes.entry(to_hash); - match entry { - Entry::Occupied(occupied) => { - // The basic block was already in the hashmap, which means we have a duplicate - let value = *occupied.get(); - debug!("Inserting {:?} -> {:?}", bb, value); - duplicates.try_insert(bb, value).expect("key was already inserted"); - } - Entry::Vacant(vacant) => { - vacant.insert(bb); - } - } - } - - duplicates -} - -struct BasicBlockHashable<'a, 'tcx> { - basic_block_data: &'a BasicBlockData<'tcx>, -} - -impl Hash for BasicBlockHashable<'_, '_> { - fn hash(&self, state: &mut H) { - hash_statements(state, self.basic_block_data.statements.iter()); - // Note that since we only hash the kind, we lose span information if we deduplicate the - // blocks. - self.basic_block_data.terminator().kind.hash(state); - } -} - -impl Eq for BasicBlockHashable<'_, '_> {} - -impl PartialEq for BasicBlockHashable<'_, '_> { - fn eq(&self, other: &Self) -> bool { - self.basic_block_data.statements.len() == other.basic_block_data.statements.len() - && &self.basic_block_data.terminator().kind == &other.basic_block_data.terminator().kind - && iter::zip(&self.basic_block_data.statements, &other.basic_block_data.statements) - .all(|(x, y)| statement_eq(&x.kind, &y.kind)) - } -} - -fn hash_statements<'a, 'tcx, H: Hasher>( - hasher: &mut H, - iter: impl Iterator>, -) where - 'tcx: 'a, -{ - for stmt in iter { - statement_hash(hasher, &stmt.kind); - } -} - -fn statement_hash(hasher: &mut H, stmt: &StatementKind<'_>) { - match stmt { - StatementKind::Assign(box (place, rvalue)) => { - place.hash(hasher); - rvalue_hash(hasher, rvalue) - } - x => x.hash(hasher), - }; -} - -fn rvalue_hash(hasher: &mut H, rvalue: &Rvalue<'_>) { - match rvalue { - Rvalue::Use(op) => operand_hash(hasher, op), - x => x.hash(hasher), - }; -} - -fn operand_hash(hasher: &mut H, operand: &Operand<'_>) { - match operand { - Operand::Constant(box ConstOperand { user_ty: _, const_, span: _ }) => const_.hash(hasher), - x => x.hash(hasher), - }; -} - -fn statement_eq<'tcx>(lhs: &StatementKind<'tcx>, rhs: &StatementKind<'tcx>) -> bool { - let res = match (lhs, rhs) { - ( - StatementKind::Assign(box (place, rvalue)), - StatementKind::Assign(box (place2, rvalue2)), - ) => place == place2 && rvalue_eq(rvalue, rvalue2), - (x, y) => x == y, - }; - debug!("statement_eq lhs: `{:?}` rhs: `{:?}` result: {:?}", lhs, rhs, res); - res -} - -fn rvalue_eq<'tcx>(lhs: &Rvalue<'tcx>, rhs: &Rvalue<'tcx>) -> bool { - let res = match (lhs, rhs) { - (Rvalue::Use(op1), Rvalue::Use(op2)) => operand_eq(op1, op2), - (x, y) => x == y, - }; - debug!("rvalue_eq lhs: `{:?}` rhs: `{:?}` result: {:?}", lhs, rhs, res); - res -} - -fn operand_eq<'tcx>(lhs: &Operand<'tcx>, rhs: &Operand<'tcx>) -> bool { - let res = match (lhs, rhs) { - ( - Operand::Constant(box ConstOperand { user_ty: _, const_, span: _ }), - Operand::Constant(box ConstOperand { user_ty: _, const_: const2, span: _ }), - ) => const_ == const2, - (x, y) => x == y, - }; - debug!("operand_eq lhs: `{:?}` rhs: `{:?}` result: {:?}", lhs, rhs, res); - res -} diff --git a/compiler/rustc_mir_transform/src/deref_separator.rs b/compiler/rustc_mir_transform/src/deref_separator.rs index ad7ccef4976aa..bc914ea656415 100644 --- a/compiler/rustc_mir_transform/src/deref_separator.rs +++ b/compiler/rustc_mir_transform/src/deref_separator.rs @@ -1,9 +1,10 @@ -use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::visit::NonUseContext::VarDebugInfo; use rustc_middle::mir::visit::{MutVisitor, PlaceContext}; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; +use crate::patch::MirPatch; + pub(super) struct Derefer; struct DerefChecker<'a, 'tcx> { @@ -81,4 +82,8 @@ impl<'tcx> crate::MirPass<'tcx> for Derefer { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { deref_finder(tcx, body); } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index e99bee6a01f5a..7395ad496dbdf 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -132,7 +132,7 @@ //! [attempt 3]: https://github.com/rust-lang/rust/pull/72632 use rustc_data_structures::fx::{FxIndexMap, IndexEntry, IndexOccupiedEntry}; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_index::interval::SparseIntervalMatrix; use rustc_middle::bug; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; @@ -204,7 +204,8 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation { // Because we only filter once per round, it is unsound to use a local for more than // one merge operation within a single round of optimizations. We store here which ones // we have already used. - let mut merged_locals: BitSet = BitSet::new_empty(body.local_decls.len()); + let mut merged_locals: DenseBitSet = + DenseBitSet::new_empty(body.local_decls.len()); // This is the set of merges we will apply this round. It is a subset of the candidates. let mut merges = FxIndexMap::default(); @@ -239,6 +240,10 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation { trace!(round_count); } + + fn is_required(&self) -> bool { + false + } } #[derive(Debug, Default)] @@ -274,7 +279,7 @@ fn apply_merges<'tcx>( body: &mut Body<'tcx>, tcx: TyCtxt<'tcx>, merges: FxIndexMap, - merged_locals: BitSet, + merged_locals: DenseBitSet, ) { let mut merger = Merger { tcx, merges, merged_locals }; merger.visit_body_preserves_cfg(body); @@ -283,7 +288,7 @@ fn apply_merges<'tcx>( struct Merger<'tcx> { tcx: TyCtxt<'tcx>, merges: FxIndexMap, - merged_locals: BitSet, + merged_locals: DenseBitSet, } impl<'tcx> MutVisitor<'tcx> for Merger<'tcx> { @@ -351,7 +356,7 @@ impl Candidates { /// Collects the candidates for merging. /// /// This is responsible for enforcing the first and third bullet point. - fn reset_and_find<'tcx>(&mut self, body: &Body<'tcx>, borrowed: &BitSet) { + fn reset_and_find<'tcx>(&mut self, body: &Body<'tcx>, borrowed: &DenseBitSet) { self.c.clear(); self.reverse.clear(); let mut visitor = FindAssignments { body, candidates: &mut self.c, borrowed }; @@ -570,10 +575,14 @@ impl WriteInfo { self.add_operand(op); } } + Rvalue::WrapUnsafeBinder(op, _) => { + self.add_operand(op); + } Rvalue::ThreadLocalRef(_) | Rvalue::NullaryOp(_, _) | Rvalue::Ref(_, _, _) | Rvalue::RawPtr(_, _) + | Rvalue::Len(_) | Rvalue::Discriminant(_) | Rvalue::CopyForDeref(_) => {} } @@ -735,7 +744,7 @@ fn places_to_candidate_pair<'tcx>( struct FindAssignments<'a, 'tcx> { body: &'a Body<'tcx>, candidates: &'a mut FxIndexMap>, - borrowed: &'a BitSet, + borrowed: &'a DenseBitSet, } impl<'tcx> Visitor<'tcx> for FindAssignments<'_, 'tcx> { diff --git a/compiler/rustc_mir_transform/src/dump_mir.rs b/compiler/rustc_mir_transform/src/dump_mir.rs index 5dd84975b88ed..e4fcbaa483d04 100644 --- a/compiler/rustc_mir_transform/src/dump_mir.rs +++ b/compiler/rustc_mir_transform/src/dump_mir.rs @@ -15,6 +15,10 @@ impl<'tcx> crate::MirPass<'tcx> for Marker { } fn run_pass(&self, _tcx: TyCtxt<'tcx>, _body: &mut Body<'tcx>) {} + + fn is_required(&self) -> bool { + false + } } pub fn emit_mir(tcx: TyCtxt<'_>) -> io::Result<()> { diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs index 91e1395e76415..57f7893be1b8c 100644 --- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs +++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs @@ -1,11 +1,11 @@ use std::fmt::Debug; -use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::*; use rustc_middle::ty::{Ty, TyCtxt}; use tracing::trace; use super::simplify::simplify_cfg; +use crate::patch::MirPatch; /// This pass optimizes something like /// ```ignore (syntax-highlighting-only) @@ -227,6 +227,10 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch { simplify_cfg(body); } } + + fn is_required(&self) -> bool { + false + } } #[derive(Debug)] diff --git a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs index b909dfa132052..5c344a806880c 100644 --- a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs +++ b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs @@ -4,12 +4,13 @@ use rustc_abi::FieldIdx; use rustc_hir::def_id::DefId; -use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::visit::MutVisitor; use rustc_middle::mir::*; use rustc_middle::span_bug; use rustc_middle::ty::{Ty, TyCtxt}; +use crate::patch::MirPatch; + /// Constructs the types used when accessing a Box's pointer fn build_ptr_tys<'tcx>( tcx: TyCtxt<'tcx>, @@ -29,13 +30,8 @@ fn build_ptr_tys<'tcx>( pub(super) fn build_projection<'tcx>( unique_ty: Ty<'tcx>, nonnull_ty: Ty<'tcx>, - ptr_ty: Ty<'tcx>, -) -> [PlaceElem<'tcx>; 3] { - [ - PlaceElem::Field(FieldIdx::ZERO, unique_ty), - PlaceElem::Field(FieldIdx::ZERO, nonnull_ty), - PlaceElem::Field(FieldIdx::ZERO, ptr_ty), - ] +) -> [PlaceElem<'tcx>; 2] { + [PlaceElem::Field(FieldIdx::ZERO, unique_ty), PlaceElem::Field(FieldIdx::ZERO, nonnull_ty)] } struct ElaborateBoxDerefVisitor<'a, 'tcx> { @@ -75,10 +71,14 @@ impl<'a, 'tcx> MutVisitor<'tcx> for ElaborateBoxDerefVisitor<'a, 'tcx> { self.patch.add_assign( location, Place::from(ptr_local), - Rvalue::Use(Operand::Copy( - Place::from(place.local) - .project_deeper(&build_projection(unique_ty, nonnull_ty, ptr_ty), tcx), - )), + Rvalue::Cast( + CastKind::Transmute, + Operand::Copy( + Place::from(place.local) + .project_deeper(&build_projection(unique_ty, nonnull_ty), tcx), + ), + ptr_ty, + ), ); place.local = ptr_local; @@ -133,8 +133,10 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateBoxDerefs { let (unique_ty, nonnull_ty, ptr_ty) = build_ptr_tys(tcx, boxed_ty, unique_did, nonnull_did); - new_projections - .extend_from_slice(&build_projection(unique_ty, nonnull_ty, ptr_ty)); + new_projections.extend_from_slice(&build_projection(unique_ty, nonnull_ty)); + // While we can't project into `NonNull<_>` in a basic block + // due to MCP#807, this is debug info where it's fine. + new_projections.push(PlaceElem::Field(FieldIdx::ZERO, ptr_ty)); new_projections.push(PlaceElem::Deref); } else if let Some(new_projections) = new_projections.as_mut() { // Keep building up our projections list once we've started it. @@ -149,4 +151,8 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateBoxDerefs { } } } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/elaborate_drop.rs b/compiler/rustc_mir_transform/src/elaborate_drop.rs new file mode 100644 index 0000000000000..ed4903017f353 --- /dev/null +++ b/compiler/rustc_mir_transform/src/elaborate_drop.rs @@ -0,0 +1,1023 @@ +use std::{fmt, iter, mem}; + +use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; +use rustc_hir::lang_items::LangItem; +use rustc_index::Idx; +use rustc_middle::mir::*; +use rustc_middle::span_bug; +use rustc_middle::ty::adjustment::PointerCoercion; +use rustc_middle::ty::util::IntTypeExt; +use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt}; +use rustc_span::DUMMY_SP; +use rustc_span::source_map::Spanned; +use tracing::{debug, instrument}; + +use crate::patch::MirPatch; + +/// Describes how/if a value should be dropped. +#[derive(Debug)] +pub(crate) enum DropStyle { + /// The value is already dead at the drop location, no drop will be executed. + Dead, + + /// The value is known to always be initialized at the drop location, drop will always be + /// executed. + Static, + + /// Whether the value needs to be dropped depends on its drop flag. + Conditional, + + /// An "open" drop is one where only the fields of a value are dropped. + /// + /// For example, this happens when moving out of a struct field: The rest of the struct will be + /// dropped in such an "open" drop. It is also used to generate drop glue for the individual + /// components of a value, for example for dropping array elements. + Open, +} + +/// Which drop flags to affect/check with an operation. +#[derive(Debug)] +pub(crate) enum DropFlagMode { + /// Only affect the top-level drop flag, not that of any contained fields. + Shallow, + /// Affect all nested drop flags in addition to the top-level one. + Deep, +} + +/// Describes if unwinding is necessary and where to unwind to if a panic occurs. +#[derive(Copy, Clone, Debug)] +pub(crate) enum Unwind { + /// Unwind to this block. + To(BasicBlock), + /// Already in an unwind path, any panic will cause an abort. + InCleanup, +} + +impl Unwind { + fn is_cleanup(self) -> bool { + match self { + Unwind::To(..) => false, + Unwind::InCleanup => true, + } + } + + fn into_action(self) -> UnwindAction { + match self { + Unwind::To(bb) => UnwindAction::Cleanup(bb), + Unwind::InCleanup => UnwindAction::Terminate(UnwindTerminateReason::InCleanup), + } + } + + fn map(self, f: F) -> Self + where + F: FnOnce(BasicBlock) -> BasicBlock, + { + match self { + Unwind::To(bb) => Unwind::To(f(bb)), + Unwind::InCleanup => Unwind::InCleanup, + } + } +} + +pub(crate) trait DropElaborator<'a, 'tcx>: fmt::Debug { + /// The type representing paths that can be moved out of. + /// + /// Users can move out of individual fields of a struct, such as `a.b.c`. This type is used to + /// represent such move paths. Sometimes tracking individual move paths is not necessary, in + /// which case this may be set to (for example) `()`. + type Path: Copy + fmt::Debug; + + // Accessors + + fn patch(&mut self) -> &mut MirPatch<'tcx>; + fn body(&self) -> &'a Body<'tcx>; + fn tcx(&self) -> TyCtxt<'tcx>; + fn typing_env(&self) -> ty::TypingEnv<'tcx>; + + // Drop logic + + /// Returns how `path` should be dropped, given `mode`. + fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle; + + /// Returns the drop flag of `path` as a MIR `Operand` (or `None` if `path` has no drop flag). + fn get_drop_flag(&mut self, path: Self::Path) -> Option>; + + /// Modifies the MIR patch so that the drop flag of `path` (if any) is cleared at `location`. + /// + /// If `mode` is deep, drop flags of all child paths should also be cleared by inserting + /// additional statements. + fn clear_drop_flag(&mut self, location: Location, path: Self::Path, mode: DropFlagMode); + + // Subpaths + + /// Returns the subpath of a field of `path` (or `None` if there is no dedicated subpath). + /// + /// If this returns `None`, `field` will not get a dedicated drop flag. + fn field_subpath(&self, path: Self::Path, field: FieldIdx) -> Option; + + /// Returns the subpath of a dereference of `path` (or `None` if there is no dedicated subpath). + /// + /// If this returns `None`, `*path` will not get a dedicated drop flag. + /// + /// This is only relevant for `Box`, where the contained `T` can be moved out of the box. + fn deref_subpath(&self, path: Self::Path) -> Option; + + /// Returns the subpath of downcasting `path` to one of its variants. + /// + /// If this returns `None`, the downcast of `path` will not get a dedicated drop flag. + fn downcast_subpath(&self, path: Self::Path, variant: VariantIdx) -> Option; + + /// Returns the subpath of indexing a fixed-size array `path`. + /// + /// If this returns `None`, elements of `path` will not get a dedicated drop flag. + /// + /// This is only relevant for array patterns, which can move out of individual array elements. + fn array_subpath(&self, path: Self::Path, index: u64, size: u64) -> Option; +} + +#[derive(Debug)] +struct DropCtxt<'a, 'b, 'tcx, D> +where + D: DropElaborator<'b, 'tcx>, +{ + elaborator: &'a mut D, + + source_info: SourceInfo, + + place: Place<'tcx>, + path: D::Path, + succ: BasicBlock, + unwind: Unwind, +} + +/// "Elaborates" a drop of `place`/`path` and patches `bb`'s terminator to execute it. +/// +/// The passed `elaborator` is used to determine what should happen at the drop terminator. It +/// decides whether the drop can be statically determined or whether it needs a dynamic drop flag, +/// and whether the drop is "open", ie. should be expanded to drop all subfields of the dropped +/// value. +/// +/// When this returns, the MIR patch in the `elaborator` contains the necessary changes. +pub(crate) fn elaborate_drop<'b, 'tcx, D>( + elaborator: &mut D, + source_info: SourceInfo, + place: Place<'tcx>, + path: D::Path, + succ: BasicBlock, + unwind: Unwind, + bb: BasicBlock, +) where + D: DropElaborator<'b, 'tcx>, + 'tcx: 'b, +{ + DropCtxt { elaborator, source_info, place, path, succ, unwind }.elaborate_drop(bb) +} + +impl<'a, 'b, 'tcx, D> DropCtxt<'a, 'b, 'tcx, D> +where + D: DropElaborator<'b, 'tcx>, + 'tcx: 'b, +{ + #[instrument(level = "trace", skip(self), ret)] + fn place_ty(&self, place: Place<'tcx>) -> Ty<'tcx> { + place.ty(self.elaborator.body(), self.tcx()).ty + } + + fn tcx(&self) -> TyCtxt<'tcx> { + self.elaborator.tcx() + } + + /// This elaborates a single drop instruction, located at `bb`, and + /// patches over it. + /// + /// The elaborated drop checks the drop flags to only drop what + /// is initialized. + /// + /// In addition, the relevant drop flags also need to be cleared + /// to avoid double-drops. However, in the middle of a complex + /// drop, one must avoid clearing some of the flags before they + /// are read, as that would cause a memory leak. + /// + /// In particular, when dropping an ADT, multiple fields may be + /// joined together under the `rest` subpath. They are all controlled + /// by the primary drop flag, but only the last rest-field dropped + /// should clear it (and it must also not clear anything else). + // + // FIXME: I think we should just control the flags externally, + // and then we do not need this machinery. + #[instrument(level = "debug")] + fn elaborate_drop(&mut self, bb: BasicBlock) { + match self.elaborator.drop_style(self.path, DropFlagMode::Deep) { + DropStyle::Dead => { + self.elaborator + .patch() + .patch_terminator(bb, TerminatorKind::Goto { target: self.succ }); + } + DropStyle::Static => { + self.elaborator.patch().patch_terminator( + bb, + TerminatorKind::Drop { + place: self.place, + target: self.succ, + unwind: self.unwind.into_action(), + replace: false, + }, + ); + } + DropStyle::Conditional => { + let drop_bb = self.complete_drop(self.succ, self.unwind); + self.elaborator + .patch() + .patch_terminator(bb, TerminatorKind::Goto { target: drop_bb }); + } + DropStyle::Open => { + let drop_bb = self.open_drop(); + self.elaborator + .patch() + .patch_terminator(bb, TerminatorKind::Goto { target: drop_bb }); + } + } + } + + /// Returns the place and move path for each field of `variant`, + /// (the move path is `None` if the field is a rest field). + fn move_paths_for_fields( + &self, + base_place: Place<'tcx>, + variant_path: D::Path, + variant: &'tcx ty::VariantDef, + args: GenericArgsRef<'tcx>, + ) -> Vec<(Place<'tcx>, Option)> { + variant + .fields + .iter() + .enumerate() + .map(|(i, f)| { + let field = FieldIdx::new(i); + let subpath = self.elaborator.field_subpath(variant_path, field); + let tcx = self.tcx(); + + assert_eq!(self.elaborator.typing_env().typing_mode, ty::TypingMode::PostAnalysis); + let field_ty = + tcx.normalize_erasing_regions(self.elaborator.typing_env(), f.ty(tcx, args)); + + (tcx.mk_place_field(base_place, field, field_ty), subpath) + }) + .collect() + } + + fn drop_subpath( + &mut self, + place: Place<'tcx>, + path: Option, + succ: BasicBlock, + unwind: Unwind, + ) -> BasicBlock { + if let Some(path) = path { + debug!("drop_subpath: for std field {:?}", place); + + DropCtxt { + elaborator: self.elaborator, + source_info: self.source_info, + path, + place, + succ, + unwind, + } + .elaborated_drop_block() + } else { + debug!("drop_subpath: for rest field {:?}", place); + + DropCtxt { + elaborator: self.elaborator, + source_info: self.source_info, + place, + succ, + unwind, + // Using `self.path` here to condition the drop on + // our own drop flag. + path: self.path, + } + .complete_drop(succ, unwind) + } + } + + /// Creates one-half of the drop ladder for a list of fields, and return + /// the list of steps in it in reverse order, with the first step + /// dropping 0 fields and so on. + /// + /// `unwind_ladder` is such a list of steps in reverse order, + /// which is called if the matching step of the drop glue panics. + fn drop_halfladder( + &mut self, + unwind_ladder: &[Unwind], + mut succ: BasicBlock, + fields: &[(Place<'tcx>, Option)], + ) -> Vec { + iter::once(succ) + .chain(fields.iter().rev().zip(unwind_ladder).map(|(&(place, path), &unwind_succ)| { + succ = self.drop_subpath(place, path, succ, unwind_succ); + succ + })) + .collect() + } + + fn drop_ladder_bottom(&mut self) -> (BasicBlock, Unwind) { + // Clear the "master" drop flag at the end. This is needed + // because the "master" drop protects the ADT's discriminant, + // which is invalidated after the ADT is dropped. + (self.drop_flag_reset_block(DropFlagMode::Shallow, self.succ, self.unwind), self.unwind) + } + + /// Creates a full drop ladder, consisting of 2 connected half-drop-ladders + /// + /// For example, with 3 fields, the drop ladder is + /// + /// .d0: + /// ELAB(drop location.0 [target=.d1, unwind=.c1]) + /// .d1: + /// ELAB(drop location.1 [target=.d2, unwind=.c2]) + /// .d2: + /// ELAB(drop location.2 [target=`self.succ`, unwind=`self.unwind`]) + /// .c1: + /// ELAB(drop location.1 [target=.c2]) + /// .c2: + /// ELAB(drop location.2 [target=`self.unwind`]) + /// + /// NOTE: this does not clear the master drop flag, so you need + /// to point succ/unwind on a `drop_ladder_bottom`. + fn drop_ladder( + &mut self, + fields: Vec<(Place<'tcx>, Option)>, + succ: BasicBlock, + unwind: Unwind, + ) -> (BasicBlock, Unwind) { + debug!("drop_ladder({:?}, {:?})", self, fields); + + let mut fields = fields; + fields.retain(|&(place, _)| { + self.place_ty(place).needs_drop(self.tcx(), self.elaborator.typing_env()) + }); + + debug!("drop_ladder - fields needing drop: {:?}", fields); + + let unwind_ladder = vec![Unwind::InCleanup; fields.len() + 1]; + let unwind_ladder: Vec<_> = if let Unwind::To(target) = unwind { + let halfladder = self.drop_halfladder(&unwind_ladder, target, &fields); + halfladder.into_iter().map(Unwind::To).collect() + } else { + unwind_ladder + }; + + let normal_ladder = self.drop_halfladder(&unwind_ladder, succ, &fields); + + (*normal_ladder.last().unwrap(), *unwind_ladder.last().unwrap()) + } + + fn open_drop_for_tuple(&mut self, tys: &[Ty<'tcx>]) -> BasicBlock { + debug!("open_drop_for_tuple({:?}, {:?})", self, tys); + + let fields = tys + .iter() + .enumerate() + .map(|(i, &ty)| { + ( + self.tcx().mk_place_field(self.place, FieldIdx::new(i), ty), + self.elaborator.field_subpath(self.path, FieldIdx::new(i)), + ) + }) + .collect(); + + let (succ, unwind) = self.drop_ladder_bottom(); + self.drop_ladder(fields, succ, unwind).0 + } + + /// Drops the T contained in a `Box` if it has not been moved out of + #[instrument(level = "debug", ret)] + fn open_drop_for_box_contents( + &mut self, + adt: ty::AdtDef<'tcx>, + args: GenericArgsRef<'tcx>, + succ: BasicBlock, + unwind: Unwind, + ) -> BasicBlock { + // drop glue is sent straight to codegen + // box cannot be directly dereferenced + let unique_ty = adt.non_enum_variant().fields[FieldIdx::ZERO].ty(self.tcx(), args); + let unique_variant = unique_ty.ty_adt_def().unwrap().non_enum_variant(); + let nonnull_ty = unique_variant.fields[FieldIdx::ZERO].ty(self.tcx(), args); + let ptr_ty = Ty::new_imm_ptr(self.tcx(), args[0].expect_ty()); + + let unique_place = self.tcx().mk_place_field(self.place, FieldIdx::ZERO, unique_ty); + let nonnull_place = self.tcx().mk_place_field(unique_place, FieldIdx::ZERO, nonnull_ty); + let ptr_place = self.tcx().mk_place_field(nonnull_place, FieldIdx::ZERO, ptr_ty); + let interior = self.tcx().mk_place_deref(ptr_place); + + let interior_path = self.elaborator.deref_subpath(self.path); + + self.drop_subpath(interior, interior_path, succ, unwind) + } + + #[instrument(level = "debug", ret)] + fn open_drop_for_adt( + &mut self, + adt: ty::AdtDef<'tcx>, + args: GenericArgsRef<'tcx>, + ) -> BasicBlock { + if adt.variants().is_empty() { + return self.elaborator.patch().new_block(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + source_info: self.source_info, + kind: TerminatorKind::Unreachable, + }), + is_cleanup: self.unwind.is_cleanup(), + }); + } + + let skip_contents = adt.is_union() || adt.is_manually_drop(); + let contents_drop = if skip_contents { + (self.succ, self.unwind) + } else { + self.open_drop_for_adt_contents(adt, args) + }; + + if adt.is_box() { + // we need to drop the inside of the box before running the destructor + let succ = self.destructor_call_block(contents_drop); + let unwind = contents_drop + .1 + .map(|unwind| self.destructor_call_block((unwind, Unwind::InCleanup))); + + self.open_drop_for_box_contents(adt, args, succ, unwind) + } else if adt.has_dtor(self.tcx()) { + self.destructor_call_block(contents_drop) + } else { + contents_drop.0 + } + } + + fn open_drop_for_adt_contents( + &mut self, + adt: ty::AdtDef<'tcx>, + args: GenericArgsRef<'tcx>, + ) -> (BasicBlock, Unwind) { + let (succ, unwind) = self.drop_ladder_bottom(); + if !adt.is_enum() { + let fields = + self.move_paths_for_fields(self.place, self.path, adt.variant(FIRST_VARIANT), args); + self.drop_ladder(fields, succ, unwind) + } else { + self.open_drop_for_multivariant(adt, args, succ, unwind) + } + } + + fn open_drop_for_multivariant( + &mut self, + adt: ty::AdtDef<'tcx>, + args: GenericArgsRef<'tcx>, + succ: BasicBlock, + unwind: Unwind, + ) -> (BasicBlock, Unwind) { + let mut values = Vec::with_capacity(adt.variants().len()); + let mut normal_blocks = Vec::with_capacity(adt.variants().len()); + let mut unwind_blocks = + if unwind.is_cleanup() { None } else { Some(Vec::with_capacity(adt.variants().len())) }; + + let mut have_otherwise_with_drop_glue = false; + let mut have_otherwise = false; + let tcx = self.tcx(); + + for (variant_index, discr) in adt.discriminants(tcx) { + let variant = &adt.variant(variant_index); + let subpath = self.elaborator.downcast_subpath(self.path, variant_index); + + if let Some(variant_path) = subpath { + let base_place = tcx.mk_place_elem( + self.place, + ProjectionElem::Downcast(Some(variant.name), variant_index), + ); + let fields = self.move_paths_for_fields(base_place, variant_path, variant, args); + values.push(discr.val); + if let Unwind::To(unwind) = unwind { + // We can't use the half-ladder from the original + // drop ladder, because this breaks the + // "funclet can't have 2 successor funclets" + // requirement from MSVC: + // + // switch unwind-switch + // / \ / \ + // v1.0 v2.0 v2.0-unwind v1.0-unwind + // | | / | + // v1.1-unwind v2.1-unwind | + // ^ | + // \-------------------------------/ + // + // Create a duplicate half-ladder to avoid that. We + // could technically only do this on MSVC, but I + // I want to minimize the divergence between MSVC + // and non-MSVC. + + let unwind_blocks = unwind_blocks.as_mut().unwrap(); + let unwind_ladder = vec![Unwind::InCleanup; fields.len() + 1]; + let halfladder = self.drop_halfladder(&unwind_ladder, unwind, &fields); + unwind_blocks.push(halfladder.last().cloned().unwrap()); + } + let (normal, _) = self.drop_ladder(fields, succ, unwind); + normal_blocks.push(normal); + } else { + have_otherwise = true; + + let typing_env = self.elaborator.typing_env(); + let have_field_with_drop_glue = variant + .fields + .iter() + .any(|field| field.ty(tcx, args).needs_drop(tcx, typing_env)); + if have_field_with_drop_glue { + have_otherwise_with_drop_glue = true; + } + } + } + + if !have_otherwise { + values.pop(); + } else if !have_otherwise_with_drop_glue { + normal_blocks.push(self.goto_block(succ, unwind)); + if let Unwind::To(unwind) = unwind { + unwind_blocks.as_mut().unwrap().push(self.goto_block(unwind, Unwind::InCleanup)); + } + } else { + normal_blocks.push(self.drop_block(succ, unwind)); + if let Unwind::To(unwind) = unwind { + unwind_blocks.as_mut().unwrap().push(self.drop_block(unwind, Unwind::InCleanup)); + } + } + + ( + self.adt_switch_block(adt, normal_blocks, &values, succ, unwind), + unwind.map(|unwind| { + self.adt_switch_block( + adt, + unwind_blocks.unwrap(), + &values, + unwind, + Unwind::InCleanup, + ) + }), + ) + } + + fn adt_switch_block( + &mut self, + adt: ty::AdtDef<'tcx>, + blocks: Vec, + values: &[u128], + succ: BasicBlock, + unwind: Unwind, + ) -> BasicBlock { + // If there are multiple variants, then if something + // is present within the enum the discriminant, tracked + // by the rest path, must be initialized. + // + // Additionally, we do not want to switch on the + // discriminant after it is free-ed, because that + // way lies only trouble. + let discr_ty = adt.repr().discr_type().to_ty(self.tcx()); + let discr = Place::from(self.new_temp(discr_ty)); + let discr_rv = Rvalue::Discriminant(self.place); + let switch_block = BasicBlockData { + statements: vec![self.assign(discr, discr_rv)], + terminator: Some(Terminator { + source_info: self.source_info, + kind: TerminatorKind::SwitchInt { + discr: Operand::Move(discr), + targets: SwitchTargets::new( + values.iter().copied().zip(blocks.iter().copied()), + *blocks.last().unwrap(), + ), + }, + }), + is_cleanup: unwind.is_cleanup(), + }; + let switch_block = self.elaborator.patch().new_block(switch_block); + self.drop_flag_test_block(switch_block, succ, unwind) + } + + fn destructor_call_block(&mut self, (succ, unwind): (BasicBlock, Unwind)) -> BasicBlock { + debug!("destructor_call_block({:?}, {:?})", self, succ); + let tcx = self.tcx(); + let drop_trait = tcx.require_lang_item(LangItem::Drop, None); + let drop_fn = tcx.associated_item_def_ids(drop_trait)[0]; + let ty = self.place_ty(self.place); + + let ref_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, ty); + let ref_place = self.new_temp(ref_ty); + let unit_temp = Place::from(self.new_temp(tcx.types.unit)); + + let result = BasicBlockData { + statements: vec![self.assign( + Place::from(ref_place), + Rvalue::Ref( + tcx.lifetimes.re_erased, + BorrowKind::Mut { kind: MutBorrowKind::Default }, + self.place, + ), + )], + terminator: Some(Terminator { + kind: TerminatorKind::Call { + func: Operand::function_handle( + tcx, + drop_fn, + [ty.into()], + self.source_info.span, + ), + args: [Spanned { node: Operand::Move(Place::from(ref_place)), span: DUMMY_SP }] + .into(), + destination: unit_temp, + target: Some(succ), + unwind: unwind.into_action(), + call_source: CallSource::Misc, + fn_span: self.source_info.span, + }, + source_info: self.source_info, + }), + is_cleanup: unwind.is_cleanup(), + }; + + let destructor_block = self.elaborator.patch().new_block(result); + + let block_start = Location { block: destructor_block, statement_index: 0 }; + self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow); + + self.drop_flag_test_block(destructor_block, succ, unwind) + } + + /// Create a loop that drops an array: + /// + /// ```text + /// loop-block: + /// can_go = cur == len + /// if can_go then succ else drop-block + /// drop-block: + /// ptr = &raw mut P[cur] + /// cur = cur + 1 + /// drop(ptr) + /// ``` + fn drop_loop( + &mut self, + succ: BasicBlock, + cur: Local, + len: Local, + ety: Ty<'tcx>, + unwind: Unwind, + ) -> BasicBlock { + let copy = |place: Place<'tcx>| Operand::Copy(place); + let move_ = |place: Place<'tcx>| Operand::Move(place); + let tcx = self.tcx(); + + let ptr_ty = Ty::new_mut_ptr(tcx, ety); + let ptr = Place::from(self.new_temp(ptr_ty)); + let can_go = Place::from(self.new_temp(tcx.types.bool)); + let one = self.constant_usize(1); + + let drop_block = BasicBlockData { + statements: vec![ + self.assign( + ptr, + Rvalue::RawPtr(RawPtrKind::Mut, tcx.mk_place_index(self.place, cur)), + ), + self.assign( + cur.into(), + Rvalue::BinaryOp(BinOp::Add, Box::new((move_(cur.into()), one))), + ), + ], + is_cleanup: unwind.is_cleanup(), + terminator: Some(Terminator { + source_info: self.source_info, + // this gets overwritten by drop elaboration. + kind: TerminatorKind::Unreachable, + }), + }; + let drop_block = self.elaborator.patch().new_block(drop_block); + + let loop_block = BasicBlockData { + statements: vec![self.assign( + can_go, + Rvalue::BinaryOp(BinOp::Eq, Box::new((copy(Place::from(cur)), copy(len.into())))), + )], + is_cleanup: unwind.is_cleanup(), + terminator: Some(Terminator { + source_info: self.source_info, + kind: TerminatorKind::if_(move_(can_go), succ, drop_block), + }), + }; + let loop_block = self.elaborator.patch().new_block(loop_block); + + self.elaborator.patch().patch_terminator( + drop_block, + TerminatorKind::Drop { + place: tcx.mk_place_deref(ptr), + target: loop_block, + unwind: unwind.into_action(), + replace: false, + }, + ); + + loop_block + } + + fn open_drop_for_array( + &mut self, + array_ty: Ty<'tcx>, + ety: Ty<'tcx>, + opt_size: Option, + ) -> BasicBlock { + debug!("open_drop_for_array({:?}, {:?}, {:?})", array_ty, ety, opt_size); + let tcx = self.tcx(); + + if let Some(size) = opt_size { + enum ProjectionKind { + Drop(std::ops::Range), + Keep(u64, Path), + } + // Previously, we'd make a projection for every element in the array and create a drop + // ladder if any `array_subpath` was `Some`, i.e. moving out with an array pattern. + // This caused huge memory usage when generating the drops for large arrays, so we instead + // record the *subslices* which are dropped and the *indexes* which are kept + let mut drop_ranges = vec![]; + let mut dropping = true; + let mut start = 0; + for i in 0..size { + let path = self.elaborator.array_subpath(self.path, i, size); + if dropping && path.is_some() { + drop_ranges.push(ProjectionKind::Drop(start..i)); + dropping = false; + } else if !dropping && path.is_none() { + dropping = true; + start = i; + } + if let Some(path) = path { + drop_ranges.push(ProjectionKind::Keep(i, path)); + } + } + if !drop_ranges.is_empty() { + if dropping { + drop_ranges.push(ProjectionKind::Drop(start..size)); + } + let fields = drop_ranges + .iter() + .rev() + .map(|p| { + let (project, path) = match p { + ProjectionKind::Drop(r) => ( + ProjectionElem::Subslice { + from: r.start, + to: r.end, + from_end: false, + }, + None, + ), + &ProjectionKind::Keep(offset, path) => ( + ProjectionElem::ConstantIndex { + offset, + min_length: size, + from_end: false, + }, + Some(path), + ), + }; + (tcx.mk_place_elem(self.place, project), path) + }) + .collect::>(); + let (succ, unwind) = self.drop_ladder_bottom(); + return self.drop_ladder(fields, succ, unwind).0; + } + } + + let array_ptr_ty = Ty::new_mut_ptr(tcx, array_ty); + let array_ptr = self.new_temp(array_ptr_ty); + + let slice_ty = Ty::new_slice(tcx, ety); + let slice_ptr_ty = Ty::new_mut_ptr(tcx, slice_ty); + let slice_ptr = self.new_temp(slice_ptr_ty); + + let mut delegate_block = BasicBlockData { + statements: vec![ + self.assign(Place::from(array_ptr), Rvalue::RawPtr(RawPtrKind::Mut, self.place)), + self.assign( + Place::from(slice_ptr), + Rvalue::Cast( + CastKind::PointerCoercion( + PointerCoercion::Unsize, + CoercionSource::Implicit, + ), + Operand::Move(Place::from(array_ptr)), + slice_ptr_ty, + ), + ), + ], + is_cleanup: self.unwind.is_cleanup(), + terminator: None, + }; + + let array_place = mem::replace( + &mut self.place, + Place::from(slice_ptr).project_deeper(&[PlaceElem::Deref], tcx), + ); + let slice_block = self.drop_loop_pair_for_slice(ety); + self.place = array_place; + + delegate_block.terminator = Some(Terminator { + source_info: self.source_info, + kind: TerminatorKind::Goto { target: slice_block }, + }); + self.elaborator.patch().new_block(delegate_block) + } + + /// Creates a pair of drop-loops of `place`, which drops its contents, even + /// in the case of 1 panic. + fn drop_loop_pair_for_slice(&mut self, ety: Ty<'tcx>) -> BasicBlock { + debug!("drop_loop_pair_for_slice({:?})", ety); + let tcx = self.tcx(); + let len = self.new_temp(tcx.types.usize); + let cur = self.new_temp(tcx.types.usize); + + let unwind = + self.unwind.map(|unwind| self.drop_loop(unwind, cur, len, ety, Unwind::InCleanup)); + + let loop_block = self.drop_loop(self.succ, cur, len, ety, unwind); + + let [PlaceElem::Deref] = self.place.projection.as_slice() else { + span_bug!( + self.source_info.span, + "Expected place for slice drop shim to be *_n, but it's {:?}", + self.place, + ); + }; + + let zero = self.constant_usize(0); + let block = BasicBlockData { + statements: vec![ + self.assign( + len.into(), + Rvalue::UnaryOp( + UnOp::PtrMetadata, + Operand::Copy(Place::from(self.place.local)), + ), + ), + self.assign(cur.into(), Rvalue::Use(zero)), + ], + is_cleanup: unwind.is_cleanup(), + terminator: Some(Terminator { + source_info: self.source_info, + kind: TerminatorKind::Goto { target: loop_block }, + }), + }; + + let drop_block = self.elaborator.patch().new_block(block); + // FIXME(#34708): handle partially-dropped array/slice elements. + let reset_block = self.drop_flag_reset_block(DropFlagMode::Deep, drop_block, unwind); + self.drop_flag_test_block(reset_block, self.succ, unwind) + } + + /// The slow-path - create an "open", elaborated drop for a type + /// which is moved-out-of only partially, and patch `bb` to a jump + /// to it. This must not be called on ADTs with a destructor, + /// as these can't be moved-out-of, except for `Box`, which is + /// special-cased. + /// + /// This creates a "drop ladder" that drops the needed fields of the + /// ADT, both in the success case or if one of the destructors fail. + fn open_drop(&mut self) -> BasicBlock { + let ty = self.place_ty(self.place); + match ty.kind() { + ty::Closure(_, args) => self.open_drop_for_tuple(args.as_closure().upvar_tys()), + ty::CoroutineClosure(_, args) => { + self.open_drop_for_tuple(args.as_coroutine_closure().upvar_tys()) + } + // Note that `elaborate_drops` only drops the upvars of a coroutine, + // and this is ok because `open_drop` here can only be reached + // within that own coroutine's resume function. + // This should only happen for the self argument on the resume function. + // It effectively only contains upvars until the coroutine transformation runs. + // See librustc_body/transform/coroutine.rs for more details. + ty::Coroutine(_, args) => self.open_drop_for_tuple(args.as_coroutine().upvar_tys()), + ty::Tuple(fields) => self.open_drop_for_tuple(fields), + ty::Adt(def, args) => self.open_drop_for_adt(*def, args), + ty::Dynamic(..) => self.complete_drop(self.succ, self.unwind), + ty::Array(ety, size) => { + let size = size.try_to_target_usize(self.tcx()); + self.open_drop_for_array(ty, *ety, size) + } + ty::Slice(ety) => self.drop_loop_pair_for_slice(*ety), + + _ => span_bug!(self.source_info.span, "open drop from non-ADT `{:?}`", ty), + } + } + + fn complete_drop(&mut self, succ: BasicBlock, unwind: Unwind) -> BasicBlock { + debug!("complete_drop(succ={:?}, unwind={:?})", succ, unwind); + + let drop_block = self.drop_block(succ, unwind); + + self.drop_flag_test_block(drop_block, succ, unwind) + } + + /// Creates a block that resets the drop flag. If `mode` is deep, all children drop flags will + /// also be cleared. + fn drop_flag_reset_block( + &mut self, + mode: DropFlagMode, + succ: BasicBlock, + unwind: Unwind, + ) -> BasicBlock { + debug!("drop_flag_reset_block({:?},{:?})", self, mode); + + if unwind.is_cleanup() { + // The drop flag isn't read again on the unwind path, so don't + // bother setting it. + return succ; + } + let block = self.new_block(unwind, TerminatorKind::Goto { target: succ }); + let block_start = Location { block, statement_index: 0 }; + self.elaborator.clear_drop_flag(block_start, self.path, mode); + block + } + + fn elaborated_drop_block(&mut self) -> BasicBlock { + debug!("elaborated_drop_block({:?})", self); + let blk = self.drop_block(self.succ, self.unwind); + self.elaborate_drop(blk); + blk + } + + fn drop_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock { + let block = TerminatorKind::Drop { + place: self.place, + target, + unwind: unwind.into_action(), + replace: false, + }; + self.new_block(unwind, block) + } + + fn goto_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock { + let block = TerminatorKind::Goto { target }; + self.new_block(unwind, block) + } + + /// Returns the block to jump to in order to test the drop flag and execute the drop. + /// + /// Depending on the required `DropStyle`, this might be a generated block with an `if` + /// terminator (for dynamic/open drops), or it might be `on_set` or `on_unset` itself, in case + /// the drop can be statically determined. + fn drop_flag_test_block( + &mut self, + on_set: BasicBlock, + on_unset: BasicBlock, + unwind: Unwind, + ) -> BasicBlock { + let style = self.elaborator.drop_style(self.path, DropFlagMode::Shallow); + debug!( + "drop_flag_test_block({:?},{:?},{:?},{:?}) - {:?}", + self, on_set, on_unset, unwind, style + ); + + match style { + DropStyle::Dead => on_unset, + DropStyle::Static => on_set, + DropStyle::Conditional | DropStyle::Open => { + let flag = self.elaborator.get_drop_flag(self.path).unwrap(); + let term = TerminatorKind::if_(flag, on_set, on_unset); + self.new_block(unwind, term) + } + } + } + + fn new_block(&mut self, unwind: Unwind, k: TerminatorKind<'tcx>) -> BasicBlock { + self.elaborator.patch().new_block(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { source_info: self.source_info, kind: k }), + is_cleanup: unwind.is_cleanup(), + }) + } + + fn new_temp(&mut self, ty: Ty<'tcx>) -> Local { + self.elaborator.patch().new_temp(ty, self.source_info.span) + } + + fn constant_usize(&self, val: u16) -> Operand<'tcx> { + Operand::Constant(Box::new(ConstOperand { + span: self.source_info.span, + user_ty: None, + const_: Const::from_usize(self.tcx(), val.into()), + })) + } + + fn assign(&self, lhs: Place<'tcx>, rhs: Rvalue<'tcx>) -> Statement<'tcx> { + Statement { + source_info: self.source_info, + kind: StatementKind::Assign(Box::new((lhs, rhs))), + } + } +} diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index 3ebc911372507..2d74fcff415ed 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -2,22 +2,21 @@ use std::fmt; use rustc_abi::{FieldIdx, VariantIdx}; use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; -use rustc_middle::mir::patch::MirPatch; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::*; use rustc_middle::ty::{self, TyCtxt}; -use rustc_mir_dataflow::elaborate_drops::{ - DropElaborator, DropFlagMode, DropFlagState, DropStyle, Unwind, elaborate_drop, -}; use rustc_mir_dataflow::impls::{MaybeInitializedPlaces, MaybeUninitializedPlaces}; use rustc_mir_dataflow::move_paths::{LookupResult, MoveData, MovePathIndex}; use rustc_mir_dataflow::{ - Analysis, MoveDataTypingEnv, ResultsCursor, on_all_children_bits, on_lookup_result_bits, + Analysis, DropFlagState, MoveDataTypingEnv, ResultsCursor, on_all_children_bits, + on_lookup_result_bits, }; use rustc_span::Span; use tracing::{debug, instrument}; use crate::deref_separator::deref_finder; +use crate::elaborate_drop::{DropElaborator, DropFlagMode, DropStyle, Unwind, elaborate_drop}; +use crate::patch::MirPatch; /// During MIR building, Drop terminators are inserted in every place where a drop may occur. /// However, in this phase, the presence of these terminators does not guarantee that a destructor @@ -88,6 +87,10 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateDrops { elaborate_patch.apply(body); deref_finder(tcx, body); } + + fn is_required(&self) -> bool { + true + } } /// Records unwind edges which are known to be unreachable, because they are in `drop` terminators @@ -96,10 +99,10 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateDrops { fn compute_dead_unwinds<'a, 'tcx>( body: &'a Body<'tcx>, flow_inits: &mut ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>, -) -> BitSet { +) -> DenseBitSet { // We only need to do this pass once, because unwind edges can only // reach cleanup blocks, which can't have unwind edges themselves. - let mut dead_unwinds = BitSet::new_empty(body.basic_blocks.len()); + let mut dead_unwinds = DenseBitSet::new_empty(body.basic_blocks.len()); for (bb, bb_data) in body.basic_blocks.iter_enumerated() { let TerminatorKind::Drop { place, unwind: UnwindAction::Cleanup(_), .. } = bb_data.terminator().kind diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 2d9eeddea2e22..29698b0c2e445 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -4,11 +4,33 @@ use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::mir::AssertKind; use rustc_middle::ty::TyCtxt; use rustc_session::lint::{self, Lint}; -use rustc_span::Span; use rustc_span::def_id::DefId; +use rustc_span::{Ident, Span, Symbol}; use crate::fluent_generated as fluent; +#[derive(LintDiagnostic)] +#[diag(mir_transform_unconditional_recursion)] +#[help] +pub(crate) struct UnconditionalRecursion { + #[label] + pub(crate) span: Span, + #[label(mir_transform_unconditional_recursion_call_site_label)] + pub(crate) call_sites: Vec, +} + +#[derive(Diagnostic)] +#[diag(mir_transform_force_inline_attr)] +#[note] +pub(crate) struct InvalidForceInline { + #[primary_span] + pub attr_span: Span, + #[label(mir_transform_callee)] + pub callee_span: Span, + pub callee: String, + pub reason: &'static str, +} + #[derive(LintDiagnostic)] pub(crate) enum ConstMutate { #[diag(mir_transform_const_modify)] @@ -92,7 +114,7 @@ pub(crate) struct FnItemRef { #[suggestion(code = "{sugg}", applicability = "unspecified")] pub span: Span, pub sugg: String, - pub ident: String, + pub ident: Ident, } #[derive(Diagnostic)] @@ -142,3 +164,29 @@ pub(crate) struct MustNotSuspendReason { #[note(mir_transform_note2)] #[help] pub(crate) struct UndefinedTransmute; + +#[derive(Diagnostic)] +#[diag(mir_transform_force_inline)] +#[note] +pub(crate) struct ForceInlineFailure { + #[label(mir_transform_caller)] + pub caller_span: Span, + #[label(mir_transform_callee)] + pub callee_span: Span, + #[label(mir_transform_attr)] + pub attr_span: Span, + #[primary_span] + #[label(mir_transform_call)] + pub call_span: Span, + pub callee: String, + pub caller: String, + pub reason: &'static str, + #[subdiagnostic] + pub justification: Option, +} + +#[derive(Subdiagnostic)] +#[note(mir_transform_force_inline_justification)] +pub(crate) struct ForceInlineJustification { + pub sym: Symbol, +} diff --git a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs index 3d560bdf75c31..9a6a153c7ba95 100644 --- a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs +++ b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs @@ -90,10 +90,12 @@ fn has_ffi_unwind_calls(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> bool { let span = terminator.source_info.span; let foreign = fn_def_id.is_some(); - tcx.emit_node_span_lint(FFI_UNWIND_CALLS, lint_root, span, errors::FfiUnwindCall { + tcx.emit_node_span_lint( + FFI_UNWIND_CALLS, + lint_root, span, - foreign, - }); + errors::FfiUnwindCall { span, foreign }, + ); tainted = true; } diff --git a/compiler/rustc_mir_transform/src/function_item_references.rs b/compiler/rustc_mir_transform/src/function_item_references.rs index fb21bf9977f15..73e47bb79f0bf 100644 --- a/compiler/rustc_mir_transform/src/function_item_references.rs +++ b/compiler/rustc_mir_transform/src/function_item_references.rs @@ -161,14 +161,9 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> { let unsafety = fn_sig.safety().prefix_str(); let abi = match fn_sig.abi() { ExternAbi::Rust => String::from(""), - other_abi => { - let mut s = String::from("extern \""); - s.push_str(other_abi.name()); - s.push_str("\" "); - s - } + other_abi => format!("extern {other_abi} "), }; - let ident = self.tcx.item_name(fn_id).to_ident_string(); + let ident = self.tcx.item_ident(fn_id); let ty_params = fn_args.types().map(|ty| format!("{ty}")); let const_params = fn_args.consts().map(|c| format!("{c}")); let params = ty_params.chain(const_params).join(", "); @@ -177,7 +172,7 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> { let ret = if fn_sig.output().skip_binder().is_unit() { "" } else { " -> _" }; let sugg = format!( "{} as {}{}fn({}{}){}", - if params.is_empty() { ident.clone() } else { format!("{ident}::<{params}>") }, + if params.is_empty() { ident.to_string() } else { format!("{ident}::<{params}>") }, unsafety, abi, vec!["_"; num_args].join(", "), diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 283ed94b61513..77a3854ebdef3 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -94,7 +94,7 @@ use rustc_const_eval::interpret::{ use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::graph::dominators::Dominators; use rustc_hir::def::DefKind; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_index::{IndexVec, newtype_index}; use rustc_middle::bug; use rustc_middle::mir::interpret::GlobalAlloc; @@ -163,6 +163,10 @@ impl<'tcx> crate::MirPass<'tcx> for GVN { // statements. StorageRemover { tcx, reused_locals: state.reused_locals }.visit_body_preserves_cfg(body); } + + fn is_required(&self) -> bool { + false + } } newtype_index! { @@ -188,7 +192,7 @@ enum AggregateTy<'tcx> { #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] enum AddressKind { Ref(BorrowKind), - Address(Mutability), + Address(RawPtrKind), } #[derive(Debug, PartialEq, Eq, Hash)] @@ -223,6 +227,8 @@ enum Value<'tcx> { Projection(VnIndex, ProjectionElem>), /// Discriminant of the given value. Discriminant(VnIndex), + /// Length of an array or slice. + Len(VnIndex), // Operations. NullaryOp(NullOp<'tcx>, Ty<'tcx>), @@ -256,7 +262,7 @@ struct VnState<'body, 'tcx> { feature_unsized_locals: bool, ssa: &'body SsaLocals, dominators: Dominators, - reused_locals: BitSet, + reused_locals: DenseBitSet, } impl<'body, 'tcx> VnState<'body, 'tcx> { @@ -287,7 +293,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { feature_unsized_locals: tcx.features().unsized_locals(), ssa, dominators, - reused_locals: BitSet::new_empty(local_decls.len()), + reused_locals: DenseBitSet::new_empty(local_decls.len()), } } @@ -470,6 +476,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty), ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(ty), + ProjectionElem::UnwrapUnsafeBinder(ty) => { + ProjectionElem::UnwrapUnsafeBinder(ty) + } // This should have been replaced by a `ConstantIndex` earlier. ProjectionElem::Index(_) => return None, }; @@ -498,7 +507,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { mplace.layout.ty, bk.to_mutbl_lossy(), ), - AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, mplace.layout.ty, mutbl), + AddressKind::Address(mutbl) => { + Ty::new_ptr(self.tcx, mplace.layout.ty, mutbl.to_mutbl_lossy()) + } }; let layout = self.ecx.layout_of(ty).ok()?; ImmTy::from_immediate(pointer, layout).into() @@ -511,6 +522,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { self.ecx.discriminant_for_variant(base.layout.ty, variant).discard_err()?; discr_value.into() } + Len(slice) => { + let slice = self.evaluated[slice].as_ref()?; + let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap(); + let len = slice.len(&self.ecx).discard_err()?; + let imm = ImmTy::from_uint(len, usize_layout); + imm.into() + } NullaryOp(null_op, ty) => { let layout = self.ecx.layout_of(ty).ok()?; if let NullOp::SizeOf | NullOp::AlignOf = null_op @@ -527,6 +545,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { .offset_of_subfield(self.typing_env(), layout, fields.iter()) .bytes(), NullOp::UbChecks => return None, + NullOp::ContractChecks => return None, }; let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap(); let imm = ImmTy::from_uint(val, usize_layout); @@ -698,6 +717,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty), ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(ty), + ProjectionElem::UnwrapUnsafeBinder(ty) => ProjectionElem::UnwrapUnsafeBinder(ty), }; Some(self.insert(Value::Projection(value, proj))) @@ -852,8 +872,12 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { self.simplify_place_projection(place, location); return self.new_pointer(*place, AddressKind::Address(mutbl)); } + Rvalue::WrapUnsafeBinder(ref mut op, _) => { + return self.simplify_operand(op, location); + } // Operations. + Rvalue::Len(ref mut place) => return self.simplify_len(place, location), Rvalue::Cast(ref mut kind, ref mut value, to) => { return self.simplify_cast(kind, value, to, location); } @@ -915,6 +939,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ProjectionElem::Downcast(symbol, idx) => ProjectionElem::Downcast(symbol, idx), ProjectionElem::OpaqueCast(idx) => ProjectionElem::OpaqueCast(idx), ProjectionElem::Subtype(idx) => ProjectionElem::Subtype(idx), + ProjectionElem::UnwrapUnsafeBinder(ty) => ProjectionElem::UnwrapUnsafeBinder(ty), }) } @@ -1164,11 +1189,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ) if let ty::Slice(..) = to.builtin_deref(true).unwrap().kind() && let ty::Array(_, len) = from.builtin_deref(true).unwrap().kind() => { - return self.insert_constant(Const::from_ty_const( - *len, - self.tcx.types.usize, - self.tcx, - )); + return self.insert_constant(Const::Ty(self.tcx.types.usize, *len)); } _ => Value::UnaryOp(op, arg_index), }; @@ -1346,16 +1367,17 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn simplify_cast( &mut self, - kind: &mut CastKind, - operand: &mut Operand<'tcx>, + initial_kind: &mut CastKind, + initial_operand: &mut Operand<'tcx>, to: Ty<'tcx>, location: Location, ) -> Option { use CastKind::*; use rustc_middle::ty::adjustment::PointerCoercion::*; - let mut from = operand.ty(self.local_decls, self.tcx); - let mut value = self.simplify_operand(operand, location)?; + let mut from = initial_operand.ty(self.local_decls, self.tcx); + let mut kind = *initial_kind; + let mut value = self.simplify_operand(initial_operand, location)?; if from == to { return Some(value); } @@ -1366,61 +1388,146 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { return self.new_opaque(); } - let mut was_updated = false; + let mut was_ever_updated = false; + loop { + let mut was_updated_this_iteration = false; + + // Transmuting between raw pointers is just a pointer cast so long as + // they have the same metadata type (like `*const i32` <=> `*mut u64` + // or `*mut [i32]` <=> `*const [u64]`), including the common special + // case of `*const T` <=> `*mut T`. + if let Transmute = kind + && from.is_raw_ptr() + && to.is_raw_ptr() + && self.pointers_have_same_metadata(from, to) + { + kind = PtrToPtr; + was_updated_this_iteration = true; + } - // If that cast just casts away the metadata again, - if let PtrToPtr = kind - && let Value::Aggregate(AggregateTy::RawPtr { data_pointer_ty, .. }, _, fields) = - self.get(value) - && let ty::RawPtr(to_pointee, _) = to.kind() - && to_pointee.is_sized(self.tcx, self.typing_env()) - { - from = *data_pointer_ty; - value = fields[0]; - was_updated = true; - if *data_pointer_ty == to { - return Some(fields[0]); + // If a cast just casts away the metadata again, then we can get it by + // casting the original thin pointer passed to `from_raw_parts` + if let PtrToPtr = kind + && let Value::Aggregate(AggregateTy::RawPtr { data_pointer_ty, .. }, _, fields) = + self.get(value) + && let ty::RawPtr(to_pointee, _) = to.kind() + && to_pointee.is_sized(self.tcx, self.typing_env()) + { + from = *data_pointer_ty; + value = fields[0]; + was_updated_this_iteration = true; + if *data_pointer_ty == to { + return Some(fields[0]); + } } - } - // PtrToPtr-then-PtrToPtr can skip the intermediate step - if let PtrToPtr = kind - && let Value::Cast { kind: inner_kind, value: inner_value, from: inner_from, to: _ } = - *self.get(value) - && let PtrToPtr = inner_kind - { - from = inner_from; - value = inner_value; - was_updated = true; - if inner_from == to { - return Some(inner_value); + // Aggregate-then-Transmute can just transmute the original field value, + // so long as the bytes of a value from only from a single field. + if let Transmute = kind + && let Value::Aggregate(_aggregate_ty, variant_idx, field_values) = self.get(value) + && let Some((field_idx, field_ty)) = + self.value_is_all_in_one_field(from, *variant_idx) + { + from = field_ty; + value = field_values[field_idx.as_usize()]; + was_updated_this_iteration = true; + if field_ty == to { + return Some(value); + } } - } - // PtrToPtr-then-Transmute can just transmute the original, so long as the - // PtrToPtr didn't change metadata (and thus the size of the pointer) - if let Transmute = kind - && let Value::Cast { - kind: PtrToPtr, + // Various cast-then-cast cases can be simplified. + if let Value::Cast { + kind: inner_kind, value: inner_value, from: inner_from, to: inner_to, } = *self.get(value) - && self.pointers_have_same_metadata(inner_from, inner_to) - { - from = inner_from; - value = inner_value; - was_updated = true; - if inner_from == to { - return Some(inner_value); + { + let new_kind = match (inner_kind, kind) { + // Even if there's a narrowing cast in here that's fine, because + // things like `*mut [i32] -> *mut i32 -> *const i32` and + // `*mut [i32] -> *const [i32] -> *const i32` can skip the middle in MIR. + (PtrToPtr, PtrToPtr) => Some(PtrToPtr), + // PtrToPtr-then-Transmute is fine so long as the pointer cast is identity: + // `*const T -> *mut T -> NonNull` is fine, but we need to check for narrowing + // to skip things like `*const [i32] -> *const i32 -> NonNull`. + (PtrToPtr, Transmute) + if self.pointers_have_same_metadata(inner_from, inner_to) => + { + Some(Transmute) + } + // Similarly, for Transmute-then-PtrToPtr. Note that we need to check different + // variables for their metadata, and thus this can't merge with the previous arm. + (Transmute, PtrToPtr) if self.pointers_have_same_metadata(from, to) => { + Some(Transmute) + } + // If would be legal to always do this, but we don't want to hide information + // from the backend that it'd otherwise be able to use for optimizations. + (Transmute, Transmute) + if !self.type_may_have_niche_of_interest_to_backend(inner_to) => + { + Some(Transmute) + } + _ => None, + }; + if let Some(new_kind) = new_kind { + kind = new_kind; + from = inner_from; + value = inner_value; + was_updated_this_iteration = true; + if inner_from == to { + return Some(inner_value); + } + } + } + + if was_updated_this_iteration { + was_ever_updated = true; + } else { + break; } } - if was_updated && let Some(op) = self.try_as_operand(value, location) { - *operand = op; + if was_ever_updated && let Some(op) = self.try_as_operand(value, location) { + *initial_operand = op; + *initial_kind = kind; } - Some(self.insert(Value::Cast { kind: *kind, value, from, to })) + Some(self.insert(Value::Cast { kind, value, from, to })) + } + + fn simplify_len(&mut self, place: &mut Place<'tcx>, location: Location) -> Option { + // Trivial case: we are fetching a statically known length. + let place_ty = place.ty(self.local_decls, self.tcx).ty; + if let ty::Array(_, len) = place_ty.kind() { + return self.insert_constant(Const::Ty(self.tcx.types.usize, *len)); + } + + let mut inner = self.simplify_place_value(place, location)?; + + // The length information is stored in the wide pointer. + // Reborrowing copies length information from one pointer to the other. + while let Value::Address { place: borrowed, .. } = self.get(inner) + && let [PlaceElem::Deref] = borrowed.projection[..] + && let Some(borrowed) = self.locals[borrowed.local] + { + inner = borrowed; + } + + // We have an unsizing cast, which assigns the length to wide pointer metadata. + if let Value::Cast { kind, from, to, .. } = self.get(inner) + && let CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) = kind + && let Some(from) = from.builtin_deref(true) + && let ty::Array(_, len) = from.kind() + && let Some(to) = to.builtin_deref(true) + && let ty::Slice(..) = to.kind() + { + return self.insert_constant(Const::Ty(self.tcx.types.usize, *len)); + } + + // Fallback: a symbolic `Len`. + Some(self.insert(Value::Len(inner))) } fn pointers_have_same_metadata(&self, left_ptr_ty: Ty<'tcx>, right_ptr_ty: Ty<'tcx>) -> bool { @@ -1438,6 +1545,54 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { false } } + + /// Returns `false` if we know for sure that this type has no interesting niche, + /// and thus we can skip transmuting through it without worrying. + /// + /// The backend will emit `assume`s when transmuting between types with niches, + /// so we want to preserve `i32 -> char -> u32` so that that data is around, + /// but it's fine to skip whole-range-is-value steps like `A -> u32 -> B`. + fn type_may_have_niche_of_interest_to_backend(&self, ty: Ty<'tcx>) -> bool { + let Ok(layout) = self.ecx.layout_of(ty) else { + // If it's too generic or something, then assume it might be interesting later. + return true; + }; + + match layout.backend_repr { + BackendRepr::Uninhabited => true, + BackendRepr::Scalar(a) => !a.is_always_valid(&self.ecx), + BackendRepr::ScalarPair(a, b) => { + !a.is_always_valid(&self.ecx) || !b.is_always_valid(&self.ecx) + } + BackendRepr::Vector { .. } | BackendRepr::Memory { .. } => false, + } + } + + fn value_is_all_in_one_field( + &self, + ty: Ty<'tcx>, + variant: VariantIdx, + ) -> Option<(FieldIdx, Ty<'tcx>)> { + if let Ok(layout) = self.ecx.layout_of(ty) + && let abi::Variants::Single { index } = layout.variants + && index == variant + && let Some((field_idx, field_layout)) = layout.non_1zst_field(&self.ecx) + && layout.size == field_layout.size + { + // We needed to check the variant to avoid trying to read the tag + // field from an enum where no fields have variants, since that tag + // field isn't in the `Aggregate` from which we're getting values. + Some((FieldIdx::from_usize(field_idx), field_layout.ty)) + } else if let ty::Adt(adt, args) = ty.kind() + && adt.is_struct() + && adt.repr().transparent() + && let [single_field] = adt.non_enum_variant().fields.raw.as_slice() + { + Some((FieldIdx::ZERO, single_field.ty(self.tcx, args))) + } else { + None + } + } } fn op_to_prop_const<'tcx>( @@ -1615,7 +1770,7 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> { struct StorageRemover<'tcx> { tcx: TyCtxt<'tcx>, - reused_locals: BitSet, + reused_locals: DenseBitSet, } impl<'tcx> MutVisitor<'tcx> for StorageRemover<'tcx> { diff --git a/compiler/rustc_mir_transform/src/impossible_predicates.rs b/compiler/rustc_mir_transform/src/impossible_predicates.rs new file mode 100644 index 0000000000000..86e2bf6cb3c60 --- /dev/null +++ b/compiler/rustc_mir_transform/src/impossible_predicates.rs @@ -0,0 +1,60 @@ +//! Check if it's even possible to satisfy the 'where' clauses +//! for this item. +//! +//! It's possible to `#!feature(trivial_bounds)]` to write +//! a function with impossible to satisfy clauses, e.g.: +//! `fn foo() where String: Copy {}`. +//! +//! We don't usually need to worry about this kind of case, +//! since we would get a compilation error if the user tried +//! to call it. However, since we optimize even without any +//! calls to the function, we need to make sure that it even +//! makes sense to try to evaluate the body. +//! +//! If there are unsatisfiable where clauses, then all bets are +//! off, and we just give up. +//! +//! We manually filter the predicates, skipping anything that's not +//! "global". We are in a potentially generic context +//! (e.g. we are evaluating a function without instantiating generic +//! parameters, so this filtering serves two purposes: +//! +//! 1. We skip evaluating any predicates that we would +//! never be able prove are unsatisfiable (e.g. `` +//! 2. We avoid trying to normalize predicates involving generic +//! parameters (e.g. `::MyItem`). This can confuse +//! the normalization code (leading to cycle errors), since +//! it's usually never invoked in this way. + +use rustc_middle::mir::{Body, START_BLOCK, TerminatorKind}; +use rustc_middle::ty::{TyCtxt, TypeVisitableExt}; +use rustc_trait_selection::traits; +use tracing::trace; + +use crate::pass_manager::MirPass; + +pub(crate) struct ImpossiblePredicates; + +impl<'tcx> MirPass<'tcx> for ImpossiblePredicates { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let predicates = tcx + .predicates_of(body.source.def_id()) + .predicates + .iter() + .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None }); + if traits::impossible_predicates(tcx, traits::elaborate(tcx, predicates).collect()) { + trace!("found unsatisfiable predicates for {:?}", body.source); + // Clear the body to only contain a single `unreachable` statement. + let bbs = body.basic_blocks.as_mut(); + bbs.raw.truncate(1); + bbs[START_BLOCK].statements.clear(); + bbs[START_BLOCK].terminator_mut().kind = TerminatorKind::Unreachable; + body.var_debug_info.clear(); + body.local_decls.raw.truncate(body.arg_count + 1); + } + } + + fn is_required(&self) -> bool { + true + } +} diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 35699acb318db..1f8392b211870 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -4,35 +4,30 @@ use std::iter; use std::ops::{Range, RangeFrom}; use rustc_abi::{ExternAbi, FieldIdx}; -use rustc_attr_parsing::InlineAttr; +use rustc_attr_parsing::{InlineAttr, OptimizeAttr}; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_index::Idx; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::bug; -use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::{self, Instance, InstanceKind, Ty, TyCtxt, TypeFlags, TypeVisitableExt}; use rustc_session::config::{DebugInfo, OptLevel}; use rustc_span::source_map::Spanned; -use rustc_span::sym; use tracing::{debug, instrument, trace, trace_span}; use crate::cost_checker::CostChecker; use crate::deref_separator::deref_finder; use crate::simplify::simplify_cfg; -use crate::util; use crate::validate::validate_types; +use crate::{check_inline, util}; pub(crate) mod cycle; const TOP_DOWN_DEPTH_LIMIT: usize = 5; -// Made public so that `mir_drops_elaborated_and_const_checked` can be overridden -// by custom rustc drivers, running all the steps by themselves. See #114628. -pub struct Inline; - #[derive(Clone, Debug)] struct CallSite<'tcx> { callee: Instance<'tcx>, @@ -41,14 +36,12 @@ struct CallSite<'tcx> { source_info: SourceInfo, } +// Made public so that `mir_drops_elaborated_and_const_checked` can be overridden +// by custom rustc drivers, running all the steps by themselves. See #114628. +pub struct Inline; + impl<'tcx> crate::MirPass<'tcx> for Inline { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - // FIXME(#127234): Coverage instrumentation currently doesn't handle inlined - // MIR correctly when Modified Condition/Decision Coverage is enabled. - if sess.instrument_coverage_mcdc() { - return false; - } - if let Some(enabled) = sess.opts.unstable_opts.inline_mir { return enabled; } @@ -56,8 +49,7 @@ impl<'tcx> crate::MirPass<'tcx> for Inline { match sess.mir_opt_level() { 0 | 1 => false, 2 => { - (sess.opts.optimize == OptLevel::Default - || sess.opts.optimize == OptLevel::Aggressive) + (sess.opts.optimize == OptLevel::More || sess.opts.optimize == OptLevel::Aggressive) && sess.opts.incremental == None } _ => true, @@ -67,55 +59,99 @@ impl<'tcx> crate::MirPass<'tcx> for Inline { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let span = trace_span!("inline", body = %tcx.def_path_str(body.source.def_id())); let _guard = span.enter(); - if inline(tcx, body) { + if inline::>(tcx, body) { debug!("running simplify cfg on {:?}", body.source); simplify_cfg(body); deref_finder(tcx, body); } } + + fn is_required(&self) -> bool { + false + } } -fn inline<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool { - let def_id = body.source.def_id().expect_local(); +pub struct ForceInline; - // Only do inlining into fn bodies. - if !tcx.hir().body_owner_kind(def_id).is_fn_or_closure() { - return false; +impl ForceInline { + pub fn should_run_pass_for_callee<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { + matches!(tcx.codegen_fn_attrs(def_id).inline, InlineAttr::Force { .. }) } - if body.source.promoted.is_some() { - return false; +} + +impl<'tcx> crate::MirPass<'tcx> for ForceInline { + fn is_enabled(&self, _: &rustc_session::Session) -> bool { + true } - // Avoid inlining into coroutines, since their `optimized_mir` is used for layout computation, - // which can create a cycle, even when no attempt is made to inline the function in the other - // direction. - if body.coroutine.is_some() { - return false; + + fn can_be_overridden(&self) -> bool { + false } - let typing_env = body.typing_env(tcx); - let codegen_fn_attrs = tcx.codegen_fn_attrs(def_id); + fn is_required(&self) -> bool { + true + } - let mut this = Inliner { - tcx, - typing_env, - codegen_fn_attrs, - history: Vec::new(), - changed: false, - caller_is_inline_forwarder: matches!( - codegen_fn_attrs.inline, - InlineAttr::Hint | InlineAttr::Always - ) && body_is_forwarder(body), - }; - let blocks = START_BLOCK..body.basic_blocks.next_index(); - this.process_blocks(body, blocks); - this.changed + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let span = trace_span!("force_inline", body = %tcx.def_path_str(body.source.def_id())); + let _guard = span.enter(); + if inline::>(tcx, body) { + debug!("running simplify cfg on {:?}", body.source); + simplify_cfg(body); + deref_finder(tcx, body); + } + } } -struct Inliner<'tcx> { +trait Inliner<'tcx> { + fn new(tcx: TyCtxt<'tcx>, def_id: DefId, body: &Body<'tcx>) -> Self; + + fn tcx(&self) -> TyCtxt<'tcx>; + fn typing_env(&self) -> ty::TypingEnv<'tcx>; + fn history(&self) -> &[DefId]; + fn caller_def_id(&self) -> DefId; + + /// Has the caller body been changed? + fn changed(self) -> bool; + + /// Should inlining happen for a given callee? + fn should_inline_for_callee(&self, def_id: DefId) -> bool; + + fn check_caller_mir_body(&self, body: &Body<'tcx>) -> bool; + + /// Returns inlining decision that is based on the examination of callee MIR body. + /// Assumes that codegen attributes have been checked for compatibility already. + fn check_callee_mir_body( + &self, + callsite: &CallSite<'tcx>, + callee_body: &Body<'tcx>, + callee_attrs: &CodegenFnAttrs, + ) -> Result<(), &'static str>; + + // How many callsites in a body are we allowed to inline? We need to limit this in order + // to prevent super-linear growth in MIR size. + fn inline_limit_for_block(&self) -> Option; + + /// Called when inlining succeeds. + fn on_inline_success( + &mut self, + callsite: &CallSite<'tcx>, + caller_body: &mut Body<'tcx>, + new_blocks: std::ops::Range, + ); + + /// Called when inlining failed or was not performed. + fn on_inline_failure(&self, callsite: &CallSite<'tcx>, reason: &'static str); + + /// Called when the inline limit for a body is reached. + fn on_inline_limit_reached(&self) -> bool; +} + +struct ForceInliner<'tcx> { tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, - /// Caller codegen attributes. - codegen_fn_attrs: &'tcx CodegenFnAttrs, + /// `DefId` of caller. + def_id: DefId, /// Stack of inlined instances. /// We only check the `DefId` and not the args because we want to /// avoid inlining cases of polymorphic recursion. @@ -124,365 +160,203 @@ struct Inliner<'tcx> { history: Vec, /// Indicates that the caller body has been modified. changed: bool, - /// Indicates that the caller is #[inline] and just calls another function, - /// and thus we can inline less into it as it'll be inlined itself. - caller_is_inline_forwarder: bool, } -impl<'tcx> Inliner<'tcx> { - fn process_blocks(&mut self, caller_body: &mut Body<'tcx>, blocks: Range) { - // How many callsites in this body are we allowed to inline? We need to limit this in order - // to prevent super-linear growth in MIR size - let inline_limit = match self.history.len() { - 0 => usize::MAX, - 1..=TOP_DOWN_DEPTH_LIMIT => 1, - _ => return, - }; - let mut inlined_count = 0; - for bb in blocks { - let bb_data = &caller_body[bb]; - if bb_data.is_cleanup { - continue; - } - - let Some(callsite) = self.resolve_callsite(caller_body, bb, bb_data) else { - continue; - }; - - let span = trace_span!("process_blocks", %callsite.callee, ?bb); - let _guard = span.enter(); - - match self.try_inlining(caller_body, &callsite) { - Err(reason) => { - debug!("not-inlined {} [{}]", callsite.callee, reason); - } - Ok(new_blocks) => { - debug!("inlined {}", callsite.callee); - self.changed = true; - - self.history.push(callsite.callee.def_id()); - self.process_blocks(caller_body, new_blocks); - self.history.pop(); - - inlined_count += 1; - if inlined_count == inline_limit { - debug!("inline count reached"); - return; - } - } - } - } +impl<'tcx> Inliner<'tcx> for ForceInliner<'tcx> { + fn new(tcx: TyCtxt<'tcx>, def_id: DefId, body: &Body<'tcx>) -> Self { + Self { tcx, typing_env: body.typing_env(tcx), def_id, history: Vec::new(), changed: false } } - /// Attempts to inline a callsite into the caller body. When successful returns basic blocks - /// containing the inlined body. Otherwise returns an error describing why inlining didn't take - /// place. - fn try_inlining( - &self, - caller_body: &mut Body<'tcx>, - callsite: &CallSite<'tcx>, - ) -> Result, &'static str> { - self.check_mir_is_available(caller_body, callsite.callee)?; - - let callee_attrs = self.tcx.codegen_fn_attrs(callsite.callee.def_id()); - let cross_crate_inlinable = self.tcx.cross_crate_inlinable(callsite.callee.def_id()); - self.check_codegen_attributes(callsite, callee_attrs, cross_crate_inlinable)?; - - // Intrinsic fallback bodies are automatically made cross-crate inlineable, - // but at this stage we don't know whether codegen knows the intrinsic, - // so just conservatively don't inline it. - if self.tcx.has_attr(callsite.callee.def_id(), sym::rustc_intrinsic) { - return Err("Callee is an intrinsic, do not inline fallback bodies"); - } - - let terminator = caller_body[callsite.block].terminator.as_ref().unwrap(); - let TerminatorKind::Call { args, destination, .. } = &terminator.kind else { bug!() }; - let destination_ty = destination.ty(&caller_body.local_decls, self.tcx).ty; - for arg in args { - if !arg.node.ty(&caller_body.local_decls, self.tcx).is_sized(self.tcx, self.typing_env) - { - // We do not allow inlining functions with unsized params. Inlining these functions - // could create unsized locals, which are unsound and being phased out. - return Err("Call has unsized argument"); - } - } - - let callee_body = try_instance_mir(self.tcx, callsite.callee.def)?; - self.check_mir_body(callsite, callee_body, callee_attrs, cross_crate_inlinable)?; - - let Ok(callee_body) = callsite.callee.try_instantiate_mir_and_normalize_erasing_regions( - self.tcx, - self.typing_env, - ty::EarlyBinder::bind(callee_body.clone()), - ) else { - return Err("failed to normalize callee body"); - }; - - // Normally, this shouldn't be required, but trait normalization failure can create a - // validation ICE. - if !validate_types(self.tcx, self.typing_env, &callee_body, &caller_body).is_empty() { - return Err("failed to validate callee body"); - } - - // Check call signature compatibility. - // Normally, this shouldn't be required, but trait normalization failure can create a - // validation ICE. - let output_type = callee_body.return_ty(); - if !util::sub_types(self.tcx, self.typing_env, output_type, destination_ty) { - trace!(?output_type, ?destination_ty); - return Err("failed to normalize return type"); - } - if callsite.fn_sig.abi() == ExternAbi::RustCall { - // FIXME: Don't inline user-written `extern "rust-call"` functions, - // since this is generally perf-negative on rustc, and we hope that - // LLVM will inline these functions instead. - if callee_body.spread_arg.is_some() { - return Err("do not inline user-written rust-call functions"); - } + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } - let (self_arg, arg_tuple) = match &args[..] { - [arg_tuple] => (None, arg_tuple), - [self_arg, arg_tuple] => (Some(self_arg), arg_tuple), - _ => bug!("Expected `rust-call` to have 1 or 2 args"), - }; + fn typing_env(&self) -> ty::TypingEnv<'tcx> { + self.typing_env + } - let self_arg_ty = - self_arg.map(|self_arg| self_arg.node.ty(&caller_body.local_decls, self.tcx)); + fn history(&self) -> &[DefId] { + &self.history + } - let arg_tuple_ty = arg_tuple.node.ty(&caller_body.local_decls, self.tcx); - let ty::Tuple(arg_tuple_tys) = *arg_tuple_ty.kind() else { - bug!("Closure arguments are not passed as a tuple"); - }; + fn caller_def_id(&self) -> DefId { + self.def_id + } - for (arg_ty, input) in - self_arg_ty.into_iter().chain(arg_tuple_tys).zip(callee_body.args_iter()) - { - let input_type = callee_body.local_decls[input].ty; - if !util::sub_types(self.tcx, self.typing_env, input_type, arg_ty) { - trace!(?arg_ty, ?input_type); - return Err("failed to normalize tuple argument type"); - } - } - } else { - for (arg, input) in args.iter().zip(callee_body.args_iter()) { - let input_type = callee_body.local_decls[input].ty; - let arg_ty = arg.node.ty(&caller_body.local_decls, self.tcx); - if !util::sub_types(self.tcx, self.typing_env, input_type, arg_ty) { - trace!(?arg_ty, ?input_type); - return Err("failed to normalize argument type"); - } - } - } + fn changed(self) -> bool { + self.changed + } - let old_blocks = caller_body.basic_blocks.next_index(); - self.inline_call(caller_body, callsite, callee_body); - let new_blocks = old_blocks..caller_body.basic_blocks.next_index(); + fn should_inline_for_callee(&self, def_id: DefId) -> bool { + ForceInline::should_run_pass_for_callee(self.tcx(), def_id) + } - Ok(new_blocks) + fn check_caller_mir_body(&self, _: &Body<'tcx>) -> bool { + true } - fn check_mir_is_available( + #[instrument(level = "debug", skip(self, callee_body))] + fn check_callee_mir_body( &self, - caller_body: &Body<'tcx>, - callee: Instance<'tcx>, + _: &CallSite<'tcx>, + callee_body: &Body<'tcx>, + callee_attrs: &CodegenFnAttrs, ) -> Result<(), &'static str> { - let caller_def_id = caller_body.source.def_id(); - let callee_def_id = callee.def_id(); - if callee_def_id == caller_def_id { - return Err("self-recursion"); - } - - match callee.def { - InstanceKind::Item(_) => { - // If there is no MIR available (either because it was not in metadata or - // because it has no MIR because it's an extern function), then the inliner - // won't cause cycles on this. - if !self.tcx.is_mir_available(callee_def_id) { - return Err("item MIR unavailable"); - } - } - // These have no own callable MIR. - InstanceKind::Intrinsic(_) | InstanceKind::Virtual(..) => { - return Err("instance without MIR (intrinsic / virtual)"); - } - - // FIXME(#127030): `ConstParamHasTy` has bad interactions with - // the drop shim builder, which does not evaluate predicates in - // the correct param-env for types being dropped. Stall resolving - // the MIR for this instance until all of its const params are - // substituted. - InstanceKind::DropGlue(_, Some(ty)) if ty.has_type_flags(TypeFlags::HAS_CT_PARAM) => { - return Err("still needs substitution"); - } - - // This cannot result in an immediate cycle since the callee MIR is a shim, which does - // not get any optimizations run on it. Any subsequent inlining may cause cycles, but we - // do not need to catch this here, we can wait until the inliner decides to continue - // inlining a second time. - InstanceKind::VTableShim(_) - | InstanceKind::ReifyShim(..) - | InstanceKind::FnPtrShim(..) - | InstanceKind::ClosureOnceShim { .. } - | InstanceKind::ConstructCoroutineInClosureShim { .. } - | InstanceKind::DropGlue(..) - | InstanceKind::CloneShim(..) - | InstanceKind::ThreadLocalShim(..) - | InstanceKind::FnPtrAddrShim(..) - | InstanceKind::AsyncDropGlueCtorShim(..) => return Ok(()), - } - - if self.tcx.is_constructor(callee_def_id) { - trace!("constructors always have MIR"); - // Constructor functions cannot cause a query cycle. - return Ok(()); + if callee_body.tainted_by_errors.is_some() { + return Err("body has errors"); } - if callee_def_id.is_local() { - // If we know for sure that the function we're calling will itself try to - // call us, then we avoid inlining that function. - if self.tcx.mir_callgraph_reachable((callee, caller_def_id.expect_local())) { - return Err("caller might be reachable from callee (query cycle avoidance)"); - } - - Ok(()) + let caller_attrs = self.tcx().codegen_fn_attrs(self.caller_def_id()); + if callee_attrs.instruction_set != caller_attrs.instruction_set + && callee_body + .basic_blocks + .iter() + .any(|bb| matches!(bb.terminator().kind, TerminatorKind::InlineAsm { .. })) + { + // During the attribute checking stage we allow a callee with no + // instruction_set assigned to count as compatible with a function that does + // assign one. However, during this stage we require an exact match when any + // inline-asm is detected. LLVM will still possibly do an inline later on + // if the no-attribute function ends up with the same instruction set anyway. + Err("cannot move inline-asm across instruction sets") } else { - // This cannot result in an immediate cycle since the callee MIR is from another crate - // and is already optimized. Any subsequent inlining may cause cycles, but we do - // not need to catch this here, we can wait until the inliner decides to continue - // inlining a second time. - trace!("functions from other crates always have MIR"); Ok(()) } } - fn resolve_callsite( - &self, - caller_body: &Body<'tcx>, - bb: BasicBlock, - bb_data: &BasicBlockData<'tcx>, - ) -> Option> { - // Only consider direct calls to functions - let terminator = bb_data.terminator(); - - // FIXME(explicit_tail_calls): figure out if we can inline tail calls - if let TerminatorKind::Call { ref func, fn_span, .. } = terminator.kind { - let func_ty = func.ty(caller_body, self.tcx); - if let ty::FnDef(def_id, args) = *func_ty.kind() { - // To resolve an instance its args have to be fully normalized. - let args = self.tcx.try_normalize_erasing_regions(self.typing_env, args).ok()?; - let callee = Instance::try_resolve(self.tcx, self.typing_env, def_id, args) - .ok() - .flatten()?; - - if let InstanceKind::Virtual(..) | InstanceKind::Intrinsic(_) = callee.def { - return None; - } - - if self.history.contains(&callee.def_id()) { - return None; - } + fn inline_limit_for_block(&self) -> Option { + Some(usize::MAX) + } - let fn_sig = self.tcx.fn_sig(def_id).instantiate(self.tcx, args); + fn on_inline_success( + &mut self, + callsite: &CallSite<'tcx>, + caller_body: &mut Body<'tcx>, + new_blocks: std::ops::Range, + ) { + self.changed = true; - // Additionally, check that the body that we're inlining actually agrees - // with the ABI of the trait that the item comes from. - if let InstanceKind::Item(instance_def_id) = callee.def - && self.tcx.def_kind(instance_def_id) == DefKind::AssocFn - && let instance_fn_sig = self.tcx.fn_sig(instance_def_id).skip_binder() - && instance_fn_sig.abi() != fn_sig.abi() - { - return None; - } + self.history.push(callsite.callee.def_id()); + process_blocks(self, caller_body, new_blocks); + self.history.pop(); + } - let source_info = SourceInfo { span: fn_span, ..terminator.source_info }; + fn on_inline_failure(&self, callsite: &CallSite<'tcx>, reason: &'static str) { + let tcx = self.tcx(); + let InlineAttr::Force { attr_span, reason: justification } = + tcx.codegen_fn_attrs(callsite.callee.def_id()).inline + else { + bug!("called on item without required inlining"); + }; - return Some(CallSite { callee, fn_sig, block: bb, source_info }); - } - } + let call_span = callsite.source_info.span; + tcx.dcx().emit_err(crate::errors::ForceInlineFailure { + call_span, + attr_span, + caller_span: tcx.def_span(self.def_id), + caller: tcx.def_path_str(self.def_id), + callee_span: tcx.def_span(callsite.callee.def_id()), + callee: tcx.def_path_str(callsite.callee.def_id()), + reason, + justification: justification.map(|sym| crate::errors::ForceInlineJustification { sym }), + }); + } - None + fn on_inline_limit_reached(&self) -> bool { + false } +} - /// Returns an error if inlining is not possible based on codegen attributes alone. A success - /// indicates that inlining decision should be based on other criteria. - fn check_codegen_attributes( - &self, - callsite: &CallSite<'tcx>, - callee_attrs: &CodegenFnAttrs, - cross_crate_inlinable: bool, - ) -> Result<(), &'static str> { - if self.tcx.has_attr(callsite.callee.def_id(), sym::rustc_no_mir_inline) { - return Err("#[rustc_no_mir_inline]"); - } +struct NormalInliner<'tcx> { + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + /// `DefId` of caller. + def_id: DefId, + /// Stack of inlined instances. + /// We only check the `DefId` and not the args because we want to + /// avoid inlining cases of polymorphic recursion. + /// The number of `DefId`s is finite, so checking history is enough + /// to ensure that we do not loop endlessly while inlining. + history: Vec, + /// Indicates that the caller body has been modified. + changed: bool, + /// Indicates that the caller is #[inline] and just calls another function, + /// and thus we can inline less into it as it'll be inlined itself. + caller_is_inline_forwarder: bool, +} - if let InlineAttr::Never = callee_attrs.inline { - return Err("never inline hint"); +impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> { + fn new(tcx: TyCtxt<'tcx>, def_id: DefId, body: &Body<'tcx>) -> Self { + let typing_env = body.typing_env(tcx); + let codegen_fn_attrs = tcx.codegen_fn_attrs(def_id); + + Self { + tcx, + typing_env, + def_id, + history: Vec::new(), + changed: false, + caller_is_inline_forwarder: matches!( + codegen_fn_attrs.inline, + InlineAttr::Hint | InlineAttr::Always | InlineAttr::Force { .. } + ) && body_is_forwarder(body), } + } - // Reachability pass defines which functions are eligible for inlining. Generally inlining - // other functions is incorrect because they could reference symbols that aren't exported. - let is_generic = callsite.callee.args.non_erasable_generics().next().is_some(); - if !is_generic && !cross_crate_inlinable { - return Err("not exported"); - } + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } - if callsite.fn_sig.c_variadic() { - return Err("C variadic"); - } + fn caller_def_id(&self) -> DefId { + self.def_id + } - if callee_attrs.flags.contains(CodegenFnAttrFlags::COLD) { - return Err("cold"); - } + fn typing_env(&self) -> ty::TypingEnv<'tcx> { + self.typing_env + } - if callee_attrs.no_sanitize != self.codegen_fn_attrs.no_sanitize { - return Err("incompatible sanitizer set"); - } + fn history(&self) -> &[DefId] { + &self.history + } - // Two functions are compatible if the callee has no attribute (meaning - // that it's codegen agnostic), or sets an attribute that is identical - // to this function's attribute. - if callee_attrs.instruction_set.is_some() - && callee_attrs.instruction_set != self.codegen_fn_attrs.instruction_set - { - return Err("incompatible instruction set"); - } + fn changed(self) -> bool { + self.changed + } - let callee_feature_names = callee_attrs.target_features.iter().map(|f| f.name); - let this_feature_names = self.codegen_fn_attrs.target_features.iter().map(|f| f.name); - if callee_feature_names.ne(this_feature_names) { - // In general it is not correct to inline a callee with target features that are a - // subset of the caller. This is because the callee might contain calls, and the ABI of - // those calls depends on the target features of the surrounding function. By moving a - // `Call` terminator from one MIR body to another with more target features, we might - // change the ABI of that call! - return Err("incompatible target features"); + fn should_inline_for_callee(&self, _: DefId) -> bool { + true + } + + fn check_caller_mir_body(&self, body: &Body<'tcx>) -> bool { + // Avoid inlining into coroutines, since their `optimized_mir` is used for layout computation, + // which can create a cycle, even when no attempt is made to inline the function in the other + // direction. + if body.coroutine.is_some() { + return false; } - Ok(()) + true } - /// Returns inlining decision that is based on the examination of callee MIR body. - /// Assumes that codegen attributes have been checked for compatibility already. #[instrument(level = "debug", skip(self, callee_body))] - fn check_mir_body( + fn check_callee_mir_body( &self, callsite: &CallSite<'tcx>, callee_body: &Body<'tcx>, callee_attrs: &CodegenFnAttrs, - cross_crate_inlinable: bool, ) -> Result<(), &'static str> { - let tcx = self.tcx; + let tcx = self.tcx(); if let Some(_) = callee_body.tainted_by_errors { - return Err("Body is tainted"); + return Err("body has errors"); } let mut threshold = if self.caller_is_inline_forwarder { - self.tcx.sess.opts.unstable_opts.inline_mir_forwarder_threshold.unwrap_or(30) - } else if cross_crate_inlinable { - self.tcx.sess.opts.unstable_opts.inline_mir_hint_threshold.unwrap_or(100) + tcx.sess.opts.unstable_opts.inline_mir_forwarder_threshold.unwrap_or(30) + } else if tcx.cross_crate_inlinable(callsite.callee.def_id()) { + tcx.sess.opts.unstable_opts.inline_mir_hint_threshold.unwrap_or(100) } else { - self.tcx.sess.opts.unstable_opts.inline_mir_threshold.unwrap_or(50) + tcx.sess.opts.unstable_opts.inline_mir_threshold.unwrap_or(50) }; // Give a bonus functions with a small number of blocks, @@ -496,13 +370,13 @@ impl<'tcx> Inliner<'tcx> { // FIXME: Give a bonus to functions with only a single caller let mut checker = - CostChecker::new(self.tcx, self.typing_env, Some(callsite.callee), callee_body); + CostChecker::new(tcx, self.typing_env(), Some(callsite.callee), callee_body); checker.add_function_level_costs(); // Traverse the MIR manually so we can account for the effects of inlining on the CFG. let mut work_list = vec![START_BLOCK]; - let mut visited = BitSet::new_empty(callee_body.basic_blocks.len()); + let mut visited = DenseBitSet::new_empty(callee_body.basic_blocks.len()); while let Some(bb) = work_list.pop() { if !visited.insert(bb.index()) { continue; @@ -512,20 +386,20 @@ impl<'tcx> Inliner<'tcx> { checker.visit_basic_block_data(bb, blk); let term = blk.terminator(); + let caller_attrs = tcx.codegen_fn_attrs(self.caller_def_id()); if let TerminatorKind::Drop { ref place, target, unwind, replace: _ } = term.kind { work_list.push(target); // If the place doesn't actually need dropping, treat it like a regular goto. - let ty = callsite.callee.instantiate_mir( - self.tcx, - ty::EarlyBinder::bind(&place.ty(callee_body, tcx).ty), - ); - if ty.needs_drop(tcx, self.typing_env) + let ty = callsite + .callee + .instantiate_mir(tcx, ty::EarlyBinder::bind(&place.ty(callee_body, tcx).ty)); + if ty.needs_drop(tcx, self.typing_env()) && let UnwindAction::Cleanup(unwind) = unwind { work_list.push(unwind); } - } else if callee_attrs.instruction_set != self.codegen_fn_attrs.instruction_set + } else if callee_attrs.instruction_set != caller_attrs.instruction_set && matches!(term.kind, TerminatorKind::InlineAsm { .. }) { // During the attribute checking stage we allow a callee with no @@ -533,7 +407,7 @@ impl<'tcx> Inliner<'tcx> { // assign one. However, during this stage we require an exact match when any // inline-asm is detected. LLVM will still possibly do an inline later on // if the no-attribute function ends up with the same instruction set anyway. - return Err("Cannot move inline-asm across instruction sets"); + return Err("cannot move inline-asm across instruction sets"); } else if let TerminatorKind::TailCall { .. } = term.kind { // FIXME(explicit_tail_calls): figure out how exactly functions containing tail // calls can be inlined (and if they even should) @@ -557,321 +431,695 @@ impl<'tcx> Inliner<'tcx> { } } - fn inline_call( - &self, - caller_body: &mut Body<'tcx>, + fn inline_limit_for_block(&self) -> Option { + match self.history.len() { + 0 => Some(usize::MAX), + 1..=TOP_DOWN_DEPTH_LIMIT => Some(1), + _ => None, + } + } + + fn on_inline_success( + &mut self, callsite: &CallSite<'tcx>, - mut callee_body: Body<'tcx>, + caller_body: &mut Body<'tcx>, + new_blocks: std::ops::Range, ) { - let terminator = caller_body[callsite.block].terminator.take().unwrap(); - let TerminatorKind::Call { func, args, destination, unwind, target, .. } = terminator.kind - else { - bug!("unexpected terminator kind {:?}", terminator.kind); - }; + self.changed = true; - let return_block = if let Some(block) = target { - // Prepare a new block for code that should execute when call returns. We don't use - // target block directly since it might have other predecessors. - let data = BasicBlockData::new( - Some(Terminator { - source_info: terminator.source_info, - kind: TerminatorKind::Goto { target: block }, - }), - caller_body[block].is_cleanup, - ); - Some(caller_body.basic_blocks_mut().push(data)) - } else { - None + self.history.push(callsite.callee.def_id()); + process_blocks(self, caller_body, new_blocks); + self.history.pop(); + } + + fn on_inline_limit_reached(&self) -> bool { + true + } + + fn on_inline_failure(&self, _: &CallSite<'tcx>, _: &'static str) {} +} + +fn inline<'tcx, T: Inliner<'tcx>>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool { + let def_id = body.source.def_id(); + + // Only do inlining into fn bodies. + if !tcx.hir().body_owner_kind(def_id).is_fn_or_closure() { + return false; + } + + let mut inliner = T::new(tcx, def_id, body); + if !inliner.check_caller_mir_body(body) { + return false; + } + + let blocks = START_BLOCK..body.basic_blocks.next_index(); + process_blocks(&mut inliner, body, blocks); + inliner.changed() +} + +fn process_blocks<'tcx, I: Inliner<'tcx>>( + inliner: &mut I, + caller_body: &mut Body<'tcx>, + blocks: Range, +) { + let Some(inline_limit) = inliner.inline_limit_for_block() else { return }; + let mut inlined_count = 0; + for bb in blocks { + let bb_data = &caller_body[bb]; + if bb_data.is_cleanup { + continue; + } + + let Some(callsite) = resolve_callsite(inliner, caller_body, bb, bb_data) else { + continue; }; - // If the call is something like `a[*i] = f(i)`, where - // `i : &mut usize`, then just duplicating the `a[*i]` - // Place could result in two different locations if `f` - // writes to `i`. To prevent this we need to create a temporary - // borrow of the place and pass the destination as `*temp` instead. - fn dest_needs_borrow(place: Place<'_>) -> bool { - for elem in place.projection.iter() { - match elem { - ProjectionElem::Deref | ProjectionElem::Index(_) => return true, - _ => {} + let span = trace_span!("process_blocks", %callsite.callee, ?bb); + let _guard = span.enter(); + + match try_inlining(inliner, caller_body, &callsite) { + Err(reason) => { + debug!("not-inlined {} [{}]", callsite.callee, reason); + inliner.on_inline_failure(&callsite, reason); + } + Ok(new_blocks) => { + debug!("inlined {}", callsite.callee); + inliner.on_inline_success(&callsite, caller_body, new_blocks); + + inlined_count += 1; + if inlined_count == inline_limit { + if inliner.on_inline_limit_reached() { + return; + } } } + } + } +} + +fn resolve_callsite<'tcx, I: Inliner<'tcx>>( + inliner: &I, + caller_body: &Body<'tcx>, + bb: BasicBlock, + bb_data: &BasicBlockData<'tcx>, +) -> Option> { + let tcx = inliner.tcx(); + // Only consider direct calls to functions + let terminator = bb_data.terminator(); + + // FIXME(explicit_tail_calls): figure out if we can inline tail calls + if let TerminatorKind::Call { ref func, fn_span, .. } = terminator.kind { + let func_ty = func.ty(caller_body, tcx); + if let ty::FnDef(def_id, args) = *func_ty.kind() { + if !inliner.should_inline_for_callee(def_id) { + debug!("not enabled"); + return None; + } + + // To resolve an instance its args have to be fully normalized. + let args = tcx.try_normalize_erasing_regions(inliner.typing_env(), args).ok()?; + let callee = + Instance::try_resolve(tcx, inliner.typing_env(), def_id, args).ok().flatten()?; + + if let InstanceKind::Virtual(..) | InstanceKind::Intrinsic(_) = callee.def { + return None; + } + + if inliner.history().contains(&callee.def_id()) { + return None; + } - false + let fn_sig = tcx.fn_sig(def_id).instantiate(tcx, args); + + // Additionally, check that the body that we're inlining actually agrees + // with the ABI of the trait that the item comes from. + if let InstanceKind::Item(instance_def_id) = callee.def + && tcx.def_kind(instance_def_id) == DefKind::AssocFn + && let instance_fn_sig = tcx.fn_sig(instance_def_id).skip_binder() + && instance_fn_sig.abi() != fn_sig.abi() + { + return None; + } + + let source_info = SourceInfo { span: fn_span, ..terminator.source_info }; + + return Some(CallSite { callee, fn_sig, block: bb, source_info }); } + } - let dest = if dest_needs_borrow(destination) { - trace!("creating temp for return destination"); - let dest = Rvalue::Ref( - self.tcx.lifetimes.re_erased, - BorrowKind::Mut { kind: MutBorrowKind::Default }, - destination, - ); - let dest_ty = dest.ty(caller_body, self.tcx); - let temp = - Place::from(self.new_call_temp(caller_body, callsite, dest_ty, return_block)); - caller_body[callsite.block].statements.push(Statement { - source_info: callsite.source_info, - kind: StatementKind::Assign(Box::new((temp, dest))), - }); - self.tcx.mk_place_deref(temp) - } else { - destination - }; + None +} - // Always create a local to hold the destination, as `RETURN_PLACE` may appear - // where a full `Place` is not allowed. - let (remap_destination, destination_local) = if let Some(d) = dest.as_local() { - (false, d) - } else { - ( - true, - self.new_call_temp( - caller_body, - callsite, - destination.ty(caller_body, self.tcx).ty, - return_block, - ), - ) - }; +/// Attempts to inline a callsite into the caller body. When successful returns basic blocks +/// containing the inlined body. Otherwise returns an error describing why inlining didn't take +/// place. +fn try_inlining<'tcx, I: Inliner<'tcx>>( + inliner: &I, + caller_body: &mut Body<'tcx>, + callsite: &CallSite<'tcx>, +) -> Result, &'static str> { + let tcx = inliner.tcx(); + check_mir_is_available(inliner, caller_body, callsite.callee)?; + + let callee_attrs = tcx.codegen_fn_attrs(callsite.callee.def_id()); + check_inline::is_inline_valid_on_fn(tcx, callsite.callee.def_id())?; + check_codegen_attributes(inliner, callsite, callee_attrs)?; + + let terminator = caller_body[callsite.block].terminator.as_ref().unwrap(); + let TerminatorKind::Call { args, destination, .. } = &terminator.kind else { bug!() }; + let destination_ty = destination.ty(&caller_body.local_decls, tcx).ty; + for arg in args { + if !arg.node.ty(&caller_body.local_decls, tcx).is_sized(tcx, inliner.typing_env()) { + // We do not allow inlining functions with unsized params. Inlining these functions + // could create unsized locals, which are unsound and being phased out. + return Err("call has unsized argument"); + } + } - // Copy the arguments if needed. - let args = self.make_call_args(args, callsite, caller_body, &callee_body, return_block); + let callee_body = try_instance_mir(tcx, callsite.callee.def)?; + check_inline::is_inline_valid_on_body(tcx, callee_body)?; + inliner.check_callee_mir_body(callsite, callee_body, callee_attrs)?; - let mut integrator = Integrator { - args: &args, - new_locals: Local::new(caller_body.local_decls.len()).., - new_scopes: SourceScope::new(caller_body.source_scopes.len()).., - new_blocks: BasicBlock::new(caller_body.basic_blocks.len()).., - destination: destination_local, - callsite_scope: caller_body.source_scopes[callsite.source_info.scope].clone(), - callsite, - cleanup_block: unwind, - in_cleanup_block: false, - return_block, - tcx: self.tcx, - always_live_locals: BitSet::new_filled(callee_body.local_decls.len()), + let Ok(callee_body) = callsite.callee.try_instantiate_mir_and_normalize_erasing_regions( + tcx, + inliner.typing_env(), + ty::EarlyBinder::bind(callee_body.clone()), + ) else { + debug!("failed to normalize callee body"); + return Err("implementation limitation"); + }; + + // Normally, this shouldn't be required, but trait normalization failure can create a + // validation ICE. + if !validate_types(tcx, inliner.typing_env(), &callee_body, &caller_body).is_empty() { + debug!("failed to validate callee body"); + return Err("implementation limitation"); + } + + // Check call signature compatibility. + // Normally, this shouldn't be required, but trait normalization failure can create a + // validation ICE. + let output_type = callee_body.return_ty(); + if !util::sub_types(tcx, inliner.typing_env(), output_type, destination_ty) { + trace!(?output_type, ?destination_ty); + debug!("failed to normalize return type"); + return Err("implementation limitation"); + } + if callsite.fn_sig.abi() == ExternAbi::RustCall { + // FIXME: Don't inline user-written `extern "rust-call"` functions, + // since this is generally perf-negative on rustc, and we hope that + // LLVM will inline these functions instead. + if callee_body.spread_arg.is_some() { + return Err("user-written rust-call functions"); + } + + let (self_arg, arg_tuple) = match &args[..] { + [arg_tuple] => (None, arg_tuple), + [self_arg, arg_tuple] => (Some(self_arg), arg_tuple), + _ => bug!("Expected `rust-call` to have 1 or 2 args"), }; - // Map all `Local`s, `SourceScope`s and `BasicBlock`s to new ones - // (or existing ones, in a few special cases) in the caller. - integrator.visit_body(&mut callee_body); + let self_arg_ty = self_arg.map(|self_arg| self_arg.node.ty(&caller_body.local_decls, tcx)); - // If there are any locals without storage markers, give them storage only for the - // duration of the call. - for local in callee_body.vars_and_temps_iter() { - if integrator.always_live_locals.contains(local) { - let new_local = integrator.map_local(local); - caller_body[callsite.block].statements.push(Statement { - source_info: callsite.source_info, - kind: StatementKind::StorageLive(new_local), - }); + let arg_tuple_ty = arg_tuple.node.ty(&caller_body.local_decls, tcx); + let ty::Tuple(arg_tuple_tys) = *arg_tuple_ty.kind() else { + bug!("Closure arguments are not passed as a tuple"); + }; + + for (arg_ty, input) in + self_arg_ty.into_iter().chain(arg_tuple_tys).zip(callee_body.args_iter()) + { + let input_type = callee_body.local_decls[input].ty; + if !util::sub_types(tcx, inliner.typing_env(), input_type, arg_ty) { + trace!(?arg_ty, ?input_type); + debug!("failed to normalize tuple argument type"); + return Err("implementation limitation"); } } - if let Some(block) = return_block { - // To avoid repeated O(n) insert, push any new statements to the end and rotate - // the slice once. - let mut n = 0; - if remap_destination { - caller_body[block].statements.push(Statement { - source_info: callsite.source_info, - kind: StatementKind::Assign(Box::new(( - dest, - Rvalue::Use(Operand::Move(destination_local.into())), - ))), - }); - n += 1; + } else { + for (arg, input) in args.iter().zip(callee_body.args_iter()) { + let input_type = callee_body.local_decls[input].ty; + let arg_ty = arg.node.ty(&caller_body.local_decls, tcx); + if !util::sub_types(tcx, inliner.typing_env(), input_type, arg_ty) { + trace!(?arg_ty, ?input_type); + debug!("failed to normalize argument type"); + return Err("implementation limitation"); } - for local in callee_body.vars_and_temps_iter().rev() { - if integrator.always_live_locals.contains(local) { - let new_local = integrator.map_local(local); - caller_body[block].statements.push(Statement { - source_info: callsite.source_info, - kind: StatementKind::StorageDead(new_local), - }); - n += 1; - } + } + } + + let old_blocks = caller_body.basic_blocks.next_index(); + inline_call(inliner, caller_body, callsite, callee_body); + let new_blocks = old_blocks..caller_body.basic_blocks.next_index(); + + Ok(new_blocks) +} + +fn check_mir_is_available<'tcx, I: Inliner<'tcx>>( + inliner: &I, + caller_body: &Body<'tcx>, + callee: Instance<'tcx>, +) -> Result<(), &'static str> { + let caller_def_id = caller_body.source.def_id(); + let callee_def_id = callee.def_id(); + if callee_def_id == caller_def_id { + return Err("self-recursion"); + } + + match callee.def { + InstanceKind::Item(_) => { + // If there is no MIR available (either because it was not in metadata or + // because it has no MIR because it's an extern function), then the inliner + // won't cause cycles on this. + if !inliner.tcx().is_mir_available(callee_def_id) { + debug!("item MIR unavailable"); + return Err("implementation limitation"); } - caller_body[block].statements.rotate_right(n); + } + // These have no own callable MIR. + InstanceKind::Intrinsic(_) | InstanceKind::Virtual(..) => { + debug!("instance without MIR (intrinsic / virtual)"); + return Err("implementation limitation"); } - // Insert all of the (mapped) parts of the callee body into the caller. - caller_body.local_decls.extend(callee_body.drain_vars_and_temps()); - caller_body.source_scopes.append(&mut callee_body.source_scopes); - if self - .tcx - .sess - .opts - .unstable_opts - .inline_mir_preserve_debug - .unwrap_or(self.tcx.sess.opts.debuginfo != DebugInfo::None) - { - // Note that we need to preserve these in the standard library so that - // people working on rust can build with or without debuginfo while - // still getting consistent results from the mir-opt tests. - caller_body.var_debug_info.append(&mut callee_body.var_debug_info); + // FIXME(#127030): `ConstParamHasTy` has bad interactions with + // the drop shim builder, which does not evaluate predicates in + // the correct param-env for types being dropped. Stall resolving + // the MIR for this instance until all of its const params are + // substituted. + InstanceKind::DropGlue(_, Some(ty)) if ty.has_type_flags(TypeFlags::HAS_CT_PARAM) => { + debug!("still needs substitution"); + return Err("implementation limitation"); } - caller_body.basic_blocks_mut().append(callee_body.basic_blocks_mut()); - caller_body[callsite.block].terminator = Some(Terminator { - source_info: callsite.source_info, - kind: TerminatorKind::Goto { target: integrator.map_block(START_BLOCK) }, - }); + // This cannot result in an immediate cycle since the callee MIR is a shim, which does + // not get any optimizations run on it. Any subsequent inlining may cause cycles, but we + // do not need to catch this here, we can wait until the inliner decides to continue + // inlining a second time. + InstanceKind::VTableShim(_) + | InstanceKind::ReifyShim(..) + | InstanceKind::FnPtrShim(..) + | InstanceKind::ClosureOnceShim { .. } + | InstanceKind::ConstructCoroutineInClosureShim { .. } + | InstanceKind::DropGlue(..) + | InstanceKind::CloneShim(..) + | InstanceKind::ThreadLocalShim(..) + | InstanceKind::FnPtrAddrShim(..) + | InstanceKind::AsyncDropGlueCtorShim(..) => return Ok(()), + } - // Copy required constants from the callee_body into the caller_body. Although we are only - // pushing unevaluated consts to `required_consts`, here they may have been evaluated - // because we are calling `instantiate_and_normalize_erasing_regions` -- so we filter again. - caller_body.required_consts.as_mut().unwrap().extend( - callee_body.required_consts().into_iter().filter(|ct| ct.const_.is_required_const()), - ); - // Now that we incorporated the callee's `required_consts`, we can remove the callee from - // `mentioned_items` -- but we have to take their `mentioned_items` in return. This does - // some extra work here to save the monomorphization collector work later. It helps a lot, - // since monomorphization can avoid a lot of work when the "mentioned items" are similar to - // the actually used items. By doing this we can entirely avoid visiting the callee! - // We need to reconstruct the `required_item` for the callee so that we can find and - // remove it. - let callee_item = MentionedItem::Fn(func.ty(caller_body, self.tcx)); - let caller_mentioned_items = caller_body.mentioned_items.as_mut().unwrap(); - if let Some(idx) = caller_mentioned_items.iter().position(|item| item.node == callee_item) { - // We found the callee, so remove it and add its items instead. - caller_mentioned_items.remove(idx); - caller_mentioned_items.extend(callee_body.mentioned_items()); - } else { - // If we can't find the callee, there's no point in adding its items. Probably it - // already got removed by being inlined elsewhere in the same function, so we already - // took its items. + if inliner.tcx().is_constructor(callee_def_id) { + trace!("constructors always have MIR"); + // Constructor functions cannot cause a query cycle. + return Ok(()); + } + + if callee_def_id.is_local() + && !inliner + .tcx() + .is_lang_item(inliner.tcx().parent(caller_def_id), rustc_hir::LangItem::FnOnce) + { + // If we know for sure that the function we're calling will itself try to + // call us, then we avoid inlining that function. + if inliner.tcx().mir_callgraph_reachable((callee, caller_def_id.expect_local())) { + debug!("query cycle avoidance"); + return Err("caller might be reachable from callee"); } + + Ok(()) + } else { + // This cannot result in an immediate cycle since the callee MIR is from another crate + // and is already optimized. Any subsequent inlining may cause cycles, but we do + // not need to catch this here, we can wait until the inliner decides to continue + // inlining a second time. + trace!("functions from other crates always have MIR"); + Ok(()) } +} - fn make_call_args( - &self, - args: Box<[Spanned>]>, - callsite: &CallSite<'tcx>, - caller_body: &mut Body<'tcx>, - callee_body: &Body<'tcx>, - return_block: Option, - ) -> Box<[Local]> { - let tcx = self.tcx; - - // There is a bit of a mismatch between the *caller* of a closure and the *callee*. - // The caller provides the arguments wrapped up in a tuple: - // - // tuple_tmp = (a, b, c) - // Fn::call(closure_ref, tuple_tmp) - // - // meanwhile the closure body expects the arguments (here, `a`, `b`, and `c`) - // as distinct arguments. (This is the "rust-call" ABI hack.) Normally, codegen has - // the job of unpacking this tuple. But here, we are codegen. =) So we want to create - // a vector like - // - // [closure_ref, tuple_tmp.0, tuple_tmp.1, tuple_tmp.2] - // - // Except for one tiny wrinkle: we don't actually want `tuple_tmp.0`. It's more convenient - // if we "spill" that into *another* temporary, so that we can map the argument - // variable in the callee MIR directly to an argument variable on our side. - // So we introduce temporaries like: - // - // tmp0 = tuple_tmp.0 - // tmp1 = tuple_tmp.1 - // tmp2 = tuple_tmp.2 - // - // and the vector is `[closure_ref, tmp0, tmp1, tmp2]`. - if callsite.fn_sig.abi() == ExternAbi::RustCall && callee_body.spread_arg.is_none() { - // FIXME(edition_2024): switch back to a normal method call. - let mut args = <_>::into_iter(args); - let self_ = self.create_temp_if_necessary( - args.next().unwrap().node, - callsite, - caller_body, - return_block, - ); - let tuple = self.create_temp_if_necessary( - args.next().unwrap().node, - callsite, - caller_body, - return_block, - ); - assert!(args.next().is_none()); - - let tuple = Place::from(tuple); - let ty::Tuple(tuple_tys) = tuple.ty(caller_body, tcx).ty.kind() else { - bug!("Closure arguments are not passed as a tuple"); - }; +/// Returns an error if inlining is not possible based on codegen attributes alone. A success +/// indicates that inlining decision should be based on other criteria. +fn check_codegen_attributes<'tcx, I: Inliner<'tcx>>( + inliner: &I, + callsite: &CallSite<'tcx>, + callee_attrs: &CodegenFnAttrs, +) -> Result<(), &'static str> { + let tcx = inliner.tcx(); + if let InlineAttr::Never = callee_attrs.inline { + return Err("never inline attribute"); + } - // The `closure_ref` in our example above. - let closure_ref_arg = iter::once(self_); + if let OptimizeAttr::DoNotOptimize = callee_attrs.optimize { + return Err("has DoNotOptimize attribute"); + } - // The `tmp0`, `tmp1`, and `tmp2` in our example above. - let tuple_tmp_args = tuple_tys.iter().enumerate().map(|(i, ty)| { - // This is e.g., `tuple_tmp.0` in our example above. - let tuple_field = Operand::Move(tcx.mk_place_field(tuple, FieldIdx::new(i), ty)); + // Reachability pass defines which functions are eligible for inlining. Generally inlining + // other functions is incorrect because they could reference symbols that aren't exported. + let is_generic = callsite.callee.args.non_erasable_generics().next().is_some(); + if !is_generic && !tcx.cross_crate_inlinable(callsite.callee.def_id()) { + return Err("not exported"); + } - // Spill to a local to make e.g., `tmp0`. - self.create_temp_if_necessary(tuple_field, callsite, caller_body, return_block) - }); + let codegen_fn_attrs = tcx.codegen_fn_attrs(inliner.caller_def_id()); + if callee_attrs.no_sanitize != codegen_fn_attrs.no_sanitize { + return Err("incompatible sanitizer set"); + } - closure_ref_arg.chain(tuple_tmp_args).collect() - } else { - // FIXME(edition_2024): switch back to a normal method call. - <_>::into_iter(args) - .map(|a| self.create_temp_if_necessary(a.node, callsite, caller_body, return_block)) - .collect() - } + // Two functions are compatible if the callee has no attribute (meaning + // that it's codegen agnostic), or sets an attribute that is identical + // to this function's attribute. + if callee_attrs.instruction_set.is_some() + && callee_attrs.instruction_set != codegen_fn_attrs.instruction_set + { + return Err("incompatible instruction set"); } - /// If `arg` is already a temporary, returns it. Otherwise, introduces a fresh - /// temporary `T` and an instruction `T = arg`, and returns `T`. - fn create_temp_if_necessary( - &self, - arg: Operand<'tcx>, - callsite: &CallSite<'tcx>, - caller_body: &mut Body<'tcx>, - return_block: Option, - ) -> Local { - // Reuse the operand if it is a moved temporary. - if let Operand::Move(place) = &arg - && let Some(local) = place.as_local() - && caller_body.local_kind(local) == LocalKind::Temp - { - return local; + let callee_feature_names = callee_attrs.target_features.iter().map(|f| f.name); + let this_feature_names = codegen_fn_attrs.target_features.iter().map(|f| f.name); + if callee_feature_names.ne(this_feature_names) { + // In general it is not correct to inline a callee with target features that are a + // subset of the caller. This is because the callee might contain calls, and the ABI of + // those calls depends on the target features of the surrounding function. By moving a + // `Call` terminator from one MIR body to another with more target features, we might + // change the ABI of that call! + return Err("incompatible target features"); + } + + Ok(()) +} + +fn inline_call<'tcx, I: Inliner<'tcx>>( + inliner: &I, + caller_body: &mut Body<'tcx>, + callsite: &CallSite<'tcx>, + mut callee_body: Body<'tcx>, +) { + let tcx = inliner.tcx(); + let terminator = caller_body[callsite.block].terminator.take().unwrap(); + let TerminatorKind::Call { func, args, destination, unwind, target, .. } = terminator.kind + else { + bug!("unexpected terminator kind {:?}", terminator.kind); + }; + + let return_block = if let Some(block) = target { + // Prepare a new block for code that should execute when call returns. We don't use + // target block directly since it might have other predecessors. + let data = BasicBlockData::new( + Some(Terminator { + source_info: terminator.source_info, + kind: TerminatorKind::Goto { target: block }, + }), + caller_body[block].is_cleanup, + ); + Some(caller_body.basic_blocks_mut().push(data)) + } else { + None + }; + + // If the call is something like `a[*i] = f(i)`, where + // `i : &mut usize`, then just duplicating the `a[*i]` + // Place could result in two different locations if `f` + // writes to `i`. To prevent this we need to create a temporary + // borrow of the place and pass the destination as `*temp` instead. + fn dest_needs_borrow(place: Place<'_>) -> bool { + for elem in place.projection.iter() { + match elem { + ProjectionElem::Deref | ProjectionElem::Index(_) => return true, + _ => {} + } } - // Otherwise, create a temporary for the argument. - trace!("creating temp for argument {:?}", arg); - let arg_ty = arg.ty(caller_body, self.tcx); - let local = self.new_call_temp(caller_body, callsite, arg_ty, return_block); - caller_body[callsite.block].statements.push(Statement { - source_info: callsite.source_info, - kind: StatementKind::Assign(Box::new((Place::from(local), Rvalue::Use(arg)))), - }); - local + false } - /// Introduces a new temporary into the caller body that is live for the duration of the call. - fn new_call_temp( - &self, - caller_body: &mut Body<'tcx>, - callsite: &CallSite<'tcx>, - ty: Ty<'tcx>, - return_block: Option, - ) -> Local { - let local = caller_body.local_decls.push(LocalDecl::new(ty, callsite.source_info.span)); - + let dest = if dest_needs_borrow(destination) { + trace!("creating temp for return destination"); + let dest = Rvalue::Ref( + tcx.lifetimes.re_erased, + BorrowKind::Mut { kind: MutBorrowKind::Default }, + destination, + ); + let dest_ty = dest.ty(caller_body, tcx); + let temp = Place::from(new_call_temp(caller_body, callsite, dest_ty, return_block)); caller_body[callsite.block].statements.push(Statement { source_info: callsite.source_info, - kind: StatementKind::StorageLive(local), + kind: StatementKind::Assign(Box::new((temp, dest))), }); + tcx.mk_place_deref(temp) + } else { + destination + }; + + // Always create a local to hold the destination, as `RETURN_PLACE` may appear + // where a full `Place` is not allowed. + let (remap_destination, destination_local) = if let Some(d) = dest.as_local() { + (false, d) + } else { + ( + true, + new_call_temp(caller_body, callsite, destination.ty(caller_body, tcx).ty, return_block), + ) + }; + + // Copy the arguments if needed. + let args = make_call_args(inliner, args, callsite, caller_body, &callee_body, return_block); + + let mut integrator = Integrator { + args: &args, + new_locals: Local::new(caller_body.local_decls.len()).., + new_scopes: SourceScope::new(caller_body.source_scopes.len()).., + new_blocks: BasicBlock::new(caller_body.basic_blocks.len()).., + destination: destination_local, + callsite_scope: caller_body.source_scopes[callsite.source_info.scope].clone(), + callsite, + cleanup_block: unwind, + in_cleanup_block: false, + return_block, + tcx, + always_live_locals: DenseBitSet::new_filled(callee_body.local_decls.len()), + }; + + // Map all `Local`s, `SourceScope`s and `BasicBlock`s to new ones + // (or existing ones, in a few special cases) in the caller. + integrator.visit_body(&mut callee_body); - if let Some(block) = return_block { - caller_body[block].statements.insert(0, Statement { + // If there are any locals without storage markers, give them storage only for the + // duration of the call. + for local in callee_body.vars_and_temps_iter() { + if integrator.always_live_locals.contains(local) { + let new_local = integrator.map_local(local); + caller_body[callsite.block].statements.push(Statement { source_info: callsite.source_info, - kind: StatementKind::StorageDead(local), + kind: StatementKind::StorageLive(new_local), + }); + } + } + if let Some(block) = return_block { + // To avoid repeated O(n) insert, push any new statements to the end and rotate + // the slice once. + let mut n = 0; + if remap_destination { + caller_body[block].statements.push(Statement { + source_info: callsite.source_info, + kind: StatementKind::Assign(Box::new(( + dest, + Rvalue::Use(Operand::Move(destination_local.into())), + ))), }); + n += 1; + } + for local in callee_body.vars_and_temps_iter().rev() { + if integrator.always_live_locals.contains(local) { + let new_local = integrator.map_local(local); + caller_body[block].statements.push(Statement { + source_info: callsite.source_info, + kind: StatementKind::StorageDead(new_local), + }); + n += 1; + } } + caller_body[block].statements.rotate_right(n); + } + + // Insert all of the (mapped) parts of the callee body into the caller. + caller_body.local_decls.extend(callee_body.drain_vars_and_temps()); + caller_body.source_scopes.append(&mut callee_body.source_scopes); + if tcx + .sess + .opts + .unstable_opts + .inline_mir_preserve_debug + .unwrap_or(tcx.sess.opts.debuginfo != DebugInfo::None) + { + // Note that we need to preserve these in the standard library so that + // people working on rust can build with or without debuginfo while + // still getting consistent results from the mir-opt tests. + caller_body.var_debug_info.append(&mut callee_body.var_debug_info); + } + caller_body.basic_blocks_mut().append(callee_body.basic_blocks_mut()); + + caller_body[callsite.block].terminator = Some(Terminator { + source_info: callsite.source_info, + kind: TerminatorKind::Goto { target: integrator.map_block(START_BLOCK) }, + }); + + // Copy required constants from the callee_body into the caller_body. Although we are only + // pushing unevaluated consts to `required_consts`, here they may have been evaluated + // because we are calling `instantiate_and_normalize_erasing_regions` -- so we filter again. + caller_body.required_consts.as_mut().unwrap().extend( + callee_body.required_consts().into_iter().filter(|ct| ct.const_.is_required_const()), + ); + // Now that we incorporated the callee's `required_consts`, we can remove the callee from + // `mentioned_items` -- but we have to take their `mentioned_items` in return. This does + // some extra work here to save the monomorphization collector work later. It helps a lot, + // since monomorphization can avoid a lot of work when the "mentioned items" are similar to + // the actually used items. By doing this we can entirely avoid visiting the callee! + // We need to reconstruct the `required_item` for the callee so that we can find and + // remove it. + let callee_item = MentionedItem::Fn(func.ty(caller_body, tcx)); + let caller_mentioned_items = caller_body.mentioned_items.as_mut().unwrap(); + if let Some(idx) = caller_mentioned_items.iter().position(|item| item.node == callee_item) { + // We found the callee, so remove it and add its items instead. + caller_mentioned_items.remove(idx); + caller_mentioned_items.extend(callee_body.mentioned_items()); + } else { + // If we can't find the callee, there's no point in adding its items. Probably it + // already got removed by being inlined elsewhere in the same function, so we already + // took its items. + } +} + +fn make_call_args<'tcx, I: Inliner<'tcx>>( + inliner: &I, + args: Box<[Spanned>]>, + callsite: &CallSite<'tcx>, + caller_body: &mut Body<'tcx>, + callee_body: &Body<'tcx>, + return_block: Option, +) -> Box<[Local]> { + let tcx = inliner.tcx(); + + // There is a bit of a mismatch between the *caller* of a closure and the *callee*. + // The caller provides the arguments wrapped up in a tuple: + // + // tuple_tmp = (a, b, c) + // Fn::call(closure_ref, tuple_tmp) + // + // meanwhile the closure body expects the arguments (here, `a`, `b`, and `c`) + // as distinct arguments. (This is the "rust-call" ABI hack.) Normally, codegen has + // the job of unpacking this tuple. But here, we are codegen. =) So we want to create + // a vector like + // + // [closure_ref, tuple_tmp.0, tuple_tmp.1, tuple_tmp.2] + // + // Except for one tiny wrinkle: we don't actually want `tuple_tmp.0`. It's more convenient + // if we "spill" that into *another* temporary, so that we can map the argument + // variable in the callee MIR directly to an argument variable on our side. + // So we introduce temporaries like: + // + // tmp0 = tuple_tmp.0 + // tmp1 = tuple_tmp.1 + // tmp2 = tuple_tmp.2 + // + // and the vector is `[closure_ref, tmp0, tmp1, tmp2]`. + if callsite.fn_sig.abi() == ExternAbi::RustCall && callee_body.spread_arg.is_none() { + // FIXME(edition_2024): switch back to a normal method call. + let mut args = <_>::into_iter(args); + let self_ = create_temp_if_necessary( + inliner, + args.next().unwrap().node, + callsite, + caller_body, + return_block, + ); + let tuple = create_temp_if_necessary( + inliner, + args.next().unwrap().node, + callsite, + caller_body, + return_block, + ); + assert!(args.next().is_none()); + + let tuple = Place::from(tuple); + let ty::Tuple(tuple_tys) = tuple.ty(caller_body, tcx).ty.kind() else { + bug!("Closure arguments are not passed as a tuple"); + }; + + // The `closure_ref` in our example above. + let closure_ref_arg = iter::once(self_); + + // The `tmp0`, `tmp1`, and `tmp2` in our example above. + let tuple_tmp_args = tuple_tys.iter().enumerate().map(|(i, ty)| { + // This is e.g., `tuple_tmp.0` in our example above. + let tuple_field = Operand::Move(tcx.mk_place_field(tuple, FieldIdx::new(i), ty)); + + // Spill to a local to make e.g., `tmp0`. + create_temp_if_necessary(inliner, tuple_field, callsite, caller_body, return_block) + }); + + closure_ref_arg.chain(tuple_tmp_args).collect() + } else { + // FIXME(edition_2024): switch back to a normal method call. + <_>::into_iter(args) + .map(|a| create_temp_if_necessary(inliner, a.node, callsite, caller_body, return_block)) + .collect() + } +} - local +/// If `arg` is already a temporary, returns it. Otherwise, introduces a fresh temporary `T` and an +/// instruction `T = arg`, and returns `T`. +fn create_temp_if_necessary<'tcx, I: Inliner<'tcx>>( + inliner: &I, + arg: Operand<'tcx>, + callsite: &CallSite<'tcx>, + caller_body: &mut Body<'tcx>, + return_block: Option, +) -> Local { + // Reuse the operand if it is a moved temporary. + if let Operand::Move(place) = &arg + && let Some(local) = place.as_local() + && caller_body.local_kind(local) == LocalKind::Temp + { + return local; + } + + // Otherwise, create a temporary for the argument. + trace!("creating temp for argument {:?}", arg); + let arg_ty = arg.ty(caller_body, inliner.tcx()); + let local = new_call_temp(caller_body, callsite, arg_ty, return_block); + caller_body[callsite.block].statements.push(Statement { + source_info: callsite.source_info, + kind: StatementKind::Assign(Box::new((Place::from(local), Rvalue::Use(arg)))), + }); + local +} + +/// Introduces a new temporary into the caller body that is live for the duration of the call. +fn new_call_temp<'tcx>( + caller_body: &mut Body<'tcx>, + callsite: &CallSite<'tcx>, + ty: Ty<'tcx>, + return_block: Option, +) -> Local { + let local = caller_body.local_decls.push(LocalDecl::new(ty, callsite.source_info.span)); + + caller_body[callsite.block].statements.push(Statement { + source_info: callsite.source_info, + kind: StatementKind::StorageLive(local), + }); + + if let Some(block) = return_block { + caller_body[block].statements.insert( + 0, + Statement { + source_info: callsite.source_info, + kind: StatementKind::StorageDead(local), + }, + ); } + + local } /** @@ -893,7 +1141,7 @@ struct Integrator<'a, 'tcx> { in_cleanup_block: bool, return_block: Option, tcx: TyCtxt<'tcx>, - always_live_locals: BitSet, + always_live_locals: DenseBitSet, } impl Integrator<'_, '_> { @@ -1008,6 +1256,8 @@ impl<'tcx> MutVisitor<'tcx> for Integrator<'_, 'tcx> { // replaced down below anyways). if !matches!(terminator.kind, TerminatorKind::Return) { self.super_terminator(terminator, loc); + } else { + self.visit_source_info(&mut terminator.source_info); } match terminator.kind { diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index 1a65affe8121a..3dc4edaaa5ae4 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -48,6 +48,8 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify { ctx.simplify_ref_deref(rvalue); ctx.simplify_ptr_aggregate(rvalue); ctx.simplify_cast(rvalue); + ctx.simplify_repeated_aggregate(rvalue); + ctx.simplify_repeat_once(rvalue); } _ => {} } @@ -59,6 +61,10 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify { simplify_duplicate_switch_targets(block.terminator.as_mut().unwrap()); } } + + fn is_required(&self) -> bool { + false + } } struct InstSimplifyContext<'a, 'tcx> { @@ -68,6 +74,35 @@ struct InstSimplifyContext<'a, 'tcx> { } impl<'tcx> InstSimplifyContext<'_, 'tcx> { + /// Transform aggregates like [0, 0, 0, 0, 0] into [0; 5]. + /// GVN can also do this optimization, but GVN is only run at mir-opt-level 2 so having this in + /// InstSimplify helps unoptimized builds. + fn simplify_repeated_aggregate(&self, rvalue: &mut Rvalue<'tcx>) { + let Rvalue::Aggregate(box AggregateKind::Array(_), fields) = rvalue else { + return; + }; + if fields.len() < 5 { + return; + } + let first = &fields[rustc_abi::FieldIdx::ZERO]; + let Operand::Constant(first) = first else { + return; + }; + let Ok(first_val) = first.const_.eval(self.tcx, self.typing_env, first.span) else { + return; + }; + if fields.iter().all(|field| { + let Operand::Constant(field) = field else { + return false; + }; + let field = field.const_.eval(self.tcx, self.typing_env, field.span); + field == Ok(first_val) + }) { + let len = ty::Const::from_target_usize(self.tcx, fields.len().try_into().unwrap()); + *rvalue = Rvalue::Repeat(Operand::Constant(first.clone()), len); + } + } + /// Transform boolean comparisons into logical operations. fn simplify_bool_cmp(&self, rvalue: &mut Rvalue<'tcx>) { match rvalue { @@ -173,33 +208,22 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { *kind = CastKind::IntToInt; return; } - - // Transmuting a transparent struct/union to a field's type is a projection - if let ty::Adt(adt_def, args) = operand_ty.kind() - && adt_def.repr().transparent() - && (adt_def.is_struct() || adt_def.is_union()) - && let Some(place) = operand.place() - { - let variant = adt_def.non_enum_variant(); - for (i, field) in variant.fields.iter_enumerated() { - let field_ty = field.ty(self.tcx, args); - if field_ty == *cast_ty { - let place = place - .project_deeper(&[ProjectionElem::Field(i, *cast_ty)], self.tcx); - let operand = if operand.is_move() { - Operand::Move(place) - } else { - Operand::Copy(place) - }; - *rvalue = Rvalue::Use(operand); - return; - } - } - } } } } + /// Simplify `[x; 1]` to just `[x]`. + fn simplify_repeat_once(&self, rvalue: &mut Rvalue<'tcx>) { + if let Rvalue::Repeat(operand, count) = rvalue + && let Some(1) = count.try_to_target_usize(self.tcx) + { + *rvalue = Rvalue::Aggregate( + Box::new(AggregateKind::Array(operand.ty(self.local_decls, self.tcx))), + [operand.clone()].into(), + ); + } + } + fn simplify_primitive_clone( &self, terminator: &mut Terminator<'tcx>, diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index 8feb90ff7a068..17084eca6e388 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -40,7 +40,7 @@ use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable}; use rustc_data_structures::fx::FxHashSet; use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::bug; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::visit::Visitor; @@ -105,6 +105,10 @@ impl<'tcx> crate::MirPass<'tcx> for JumpThreading { } OpportunitySet::new(body, opportunities).apply(body); } + + fn is_required(&self) -> bool { + false + } } #[derive(Debug)] @@ -121,7 +125,7 @@ struct TOFinder<'a, 'tcx> { ecx: InterpCx<'tcx, DummyMachine>, body: &'a Body<'tcx>, map: Map<'tcx>, - loop_headers: BitSet, + loop_headers: DenseBitSet, /// We use an arena to avoid cloning the slices when cloning `state`. arena: &'a DroplessArena, opportunities: Vec, @@ -832,8 +836,8 @@ enum Update { /// at least a predecessor which it dominates. This definition is only correct for reducible CFGs. /// But if the CFG is already irreducible, there is no point in trying much harder. /// is already irreducible. -fn loop_headers(body: &Body<'_>) -> BitSet { - let mut loop_headers = BitSet::new_empty(body.basic_blocks.len()); +fn loop_headers(body: &Body<'_>) -> DenseBitSet { + let mut loop_headers = DenseBitSet::new_empty(body.basic_blocks.len()); let dominators = body.basic_blocks.dominators(); // Only visit reachable blocks. for (bb, bbdata) in traversal::preorder(body) { diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index f1705d0c831c2..59de6ca84a71b 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -13,7 +13,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::HirId; use rustc_hir::def::DefKind; use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::bug; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -67,7 +67,7 @@ struct ConstPropagator<'mir, 'tcx> { tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, worklist: Vec, - visited_blocks: BitSet, + visited_blocks: DenseBitSet, locals: IndexVec>, body: &'mir Body<'tcx>, written_only_inside_own_block_locals: FxHashSet, @@ -190,7 +190,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { tcx, typing_env, worklist: vec![START_BLOCK], - visited_blocks: BitSet::new_empty(body.basic_blocks.len()), + visited_blocks: DenseBitSet::new_empty(body.basic_blocks.len()), locals: IndexVec::from_elem_n(Value::Uninit, body.local_decls.len()), body, can_const_prop, @@ -296,11 +296,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let source_info = self.body.source_info(location); if let Some(lint_root) = self.lint_root(*source_info) { let span = source_info.span; - self.tcx.emit_node_span_lint(lint_kind.lint(), lint_root, span, AssertLint { + self.tcx.emit_node_span_lint( + lint_kind.lint(), + lint_root, span, - assert_kind, - lint_kind, - }); + AssertLint { span, assert_kind, lint_kind }, + ); } } @@ -440,10 +441,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { | Rvalue::Use(..) | Rvalue::CopyForDeref(..) | Rvalue::Repeat(..) + | Rvalue::Len(..) | Rvalue::Cast(..) | Rvalue::ShallowInitBox(..) | Rvalue::Discriminant(..) - | Rvalue::NullaryOp(..) => {} + | Rvalue::NullaryOp(..) + | Rvalue::WrapUnsafeBinder(..) => {} } // FIXME we need to revisit this for #67176 @@ -545,7 +548,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let val: Value<'_> = match *rvalue { ThreadLocalRef(_) => return None, - Use(ref operand) => self.eval_operand(operand)?.into(), + Use(ref operand) | WrapUnsafeBinder(ref operand, _) => { + self.eval_operand(operand)?.into() + } CopyForDeref(place) => self.eval_place(place)?.into(), @@ -599,6 +604,20 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { return None; } + Len(place) => { + let len = if let ty::Array(_, n) = place.ty(self.local_decls(), self.tcx).ty.kind() + { + n.try_to_target_usize(self.tcx)? + } else { + match self.get_const(place)? { + Value::Immediate(src) => src.len(&self.ecx).discard_err()?, + Value::Aggregate { fields, .. } => fields.len() as u64, + Value::Uninit => return None, + } + }; + ImmTy::from_scalar(Scalar::from_target_usize(len, self), layout).into() + } + Ref(..) | RawPtr(..) => return None, NullaryOp(ref null_op, ty) => { @@ -611,6 +630,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { .offset_of_subfield(self.typing_env, op_layout, fields.iter()) .bytes(), NullOp::UbChecks => return None, + NullOp::ContractChecks => return None, }; ImmTy::from_scalar(Scalar::from_target_usize(val, self), layout).into() } @@ -852,7 +872,7 @@ enum ConstPropMode { struct CanConstProp { can_const_prop: IndexVec, // False at the beginning. Once set, no more assignments are allowed to that local. - found_assignment: BitSet, + found_assignment: DenseBitSet, } impl CanConstProp { @@ -864,7 +884,7 @@ impl CanConstProp { ) -> IndexVec { let mut cpv = CanConstProp { can_const_prop: IndexVec::from_elem(ConstPropMode::FullConstProp, &body.local_decls), - found_assignment: BitSet::new_empty(body.local_decls.len()), + found_assignment: DenseBitSet::new_empty(body.local_decls.len()), }; for (local, val) in cpv.can_const_prop.iter_enumerated_mut() { let ty = body.local_decls[local].ty; diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs index 490e7dd8f7e07..1e546bfbeb303 100644 --- a/compiler/rustc_mir_transform/src/large_enums.rs +++ b/compiler/rustc_mir_transform/src/large_enums.rs @@ -125,7 +125,7 @@ impl<'tcx> crate::MirPass<'tcx> for EnumSizeOpt { source_info, kind: StatementKind::Assign(Box::new(( dst, - Rvalue::RawPtr(Mutability::Mut, *lhs), + Rvalue::RawPtr(RawPtrKind::Mut, *lhs), ))), }; @@ -146,7 +146,7 @@ impl<'tcx> crate::MirPass<'tcx> for EnumSizeOpt { source_info, kind: StatementKind::Assign(Box::new(( src, - Rvalue::RawPtr(Mutability::Not, *rhs), + Rvalue::RawPtr(RawPtrKind::Const, *rhs), ))), }; @@ -200,6 +200,10 @@ impl<'tcx> crate::MirPass<'tcx> for EnumSizeOpt { }); } } + + fn is_required(&self) -> bool { + false + } } impl EnumSizeOpt { diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index e1fba9be5bb6d..46abdcb2a8709 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -1,4 +1,5 @@ // tidy-alphabetical-start +#![feature(array_windows)] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(const_type_name)] @@ -32,10 +33,10 @@ use rustc_middle::mir::{ use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_middle::util::Providers; use rustc_middle::{bug, query, span_bug}; +use rustc_mir_build::builder::build_mir; use rustc_span::source_map::Spanned; use rustc_span::{DUMMY_SP, sym}; -use rustc_trait_selection::traits; -use tracing::{debug, trace}; +use tracing::debug; #[macro_use] mod pass_manager; @@ -44,13 +45,16 @@ use std::sync::LazyLock; use pass_manager::{self as pm, Lint, MirLint, MirPass, WithMinOptLevel}; +mod check_pointers; mod cost_checker; mod cross_crate_inline; mod deduce_param_attrs; +mod elaborate_drop; mod errors; mod ffi_unwind_calls; mod lint; mod lint_tail_expr_drop_order; +mod patch; mod shim; mod ssa; @@ -114,8 +118,11 @@ declare_passes! { mod add_moves_for_packed_drops : AddMovesForPackedDrops; mod add_retag : AddRetag; mod add_subtyping_projections : Subtyper; + mod check_inline : CheckForceInline; + mod check_call_recursion : CheckCallRecursion, CheckDropRecursion; mod check_alignment : CheckAlignment; mod check_const_item_mutation : CheckConstItemMutation; + mod check_null : CheckNull; mod check_packed_ref : CheckPackedRef; mod check_undefined_transmutes : CheckUndefinedTransmutes; // This pass is public to allow external drivers to perform MIR cleanup @@ -130,7 +137,6 @@ declare_passes! { Initial, Final }; - mod deduplicate_blocks : DeduplicateBlocks; mod deref_separator : Derefer; mod dest_prop : DestinationPropagation; pub mod dump_mir : Marker; @@ -141,7 +147,8 @@ declare_passes! { mod gvn : GVN; // Made public so that `mir_drops_elaborated_and_const_checked` can be overridden // by custom rustc drivers, running all the steps by themselves. See #114628. - pub mod inline : Inline; + pub mod inline : Inline, ForceInline; + mod impossible_predicates : ImpossiblePredicates; mod instsimplify : InstSimplify { BeforeInline, AfterSimplifyCfg }; mod jump_threading : JumpThreading; mod known_panics_lint : KnownPanicsLint; @@ -365,7 +372,7 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def: LocalDefId) -> ConstQualifs { } fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal> { - let mut body = tcx.build_mir(def); + let mut body = build_mir(tcx, def); pass_manager::dump_mir_for_phase_change(tcx, &body); @@ -374,6 +381,8 @@ fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal> { &mut body, &[ // MIR-level lints. + &Lint(check_inline::CheckForceInline), + &Lint(check_call_recursion::CheckCallRecursion), &Lint(check_packed_ref::CheckPackedRef), &Lint(check_const_item_mutation::CheckConstItemMutation), &Lint(function_item_references::FunctionItemReferences), @@ -383,6 +392,7 @@ fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal> { &Lint(sanity_check::SanityCheck), ], None, + pm::Optimizations::Allowed, ); tcx.alloc_steal_mir(body) } @@ -412,11 +422,11 @@ fn mir_promoted( }; // the `has_ffi_unwind_calls` query uses the raw mir, so make sure it is run. - tcx.ensure_with_value().has_ffi_unwind_calls(def); + tcx.ensure_done().has_ffi_unwind_calls(def); // the `by_move_body` query uses the raw mir, so make sure it is run. if tcx.needs_coroutine_by_move_body_def_id(def.to_def_id()) { - tcx.ensure_with_value().coroutine_by_move_body_def_id(def); + tcx.ensure_done().coroutine_by_move_body_def_id(def); } let mut body = tcx.mir_built(def).steal(); @@ -435,6 +445,7 @@ fn mir_promoted( &mut body, &[&promote_pass, &simplify::SimplifyCfg::PromoteConsts, &coverage::InstrumentCoverage], Some(MirPhase::Analysis(AnalysisPhase::Initial)), + pm::Optimizations::Allowed, ); lint_tail_expr_drop_order::run_lint(tcx, def, &body); @@ -468,7 +479,7 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: LocalDefId) -> Body<'_> { }; let mut body = remap_mir_for_const_eval_select(tcx, body, hir::Constness::Const); - pm::run_passes(tcx, &mut body, &[&ctfe_limit::CtfeLimit], None); + pm::run_passes(tcx, &mut body, &[&ctfe_limit::CtfeLimit], None, pm::Optimizations::Allowed); body } @@ -478,7 +489,7 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: LocalDefId) -> Body<'_> { /// end up missing the source MIR due to stealing happening. fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal> { if tcx.is_coroutine(def.to_def_id()) { - tcx.ensure_with_value().mir_coroutine_witnesses(def); + tcx.ensure_done().mir_coroutine_witnesses(def); } // We only need to borrowck non-synthetic MIR. @@ -488,8 +499,10 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> & let is_fn_like = tcx.def_kind(def).is_fn_like(); if is_fn_like { // Do not compute the mir call graph without said call graph actually being used. - if pm::should_run_pass(tcx, &inline::Inline) { - tcx.ensure_with_value().mir_inliner_callees(ty::InstanceKind::Item(def.to_def_id())); + if pm::should_run_pass(tcx, &inline::Inline, pm::Optimizations::Allowed) + || inline::ForceInline::should_run_pass_for_callee(tcx, def.to_def_id()) + { + tcx.ensure_done().mir_inliner_callees(ty::InstanceKind::Item(def.to_def_id())); } } @@ -500,56 +513,8 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> & body.tainted_by_errors = Some(error_reported); } - // Check if it's even possible to satisfy the 'where' clauses - // for this item. - // - // This branch will never be taken for any normal function. - // However, it's possible to `#!feature(trivial_bounds)]` to write - // a function with impossible to satisfy clauses, e.g.: - // `fn foo() where String: Copy {}` - // - // We don't usually need to worry about this kind of case, - // since we would get a compilation error if the user tried - // to call it. However, since we optimize even without any - // calls to the function, we need to make sure that it even - // makes sense to try to evaluate the body. - // - // If there are unsatisfiable where clauses, then all bets are - // off, and we just give up. - // - // We manually filter the predicates, skipping anything that's not - // "global". We are in a potentially generic context - // (e.g. we are evaluating a function without instantiating generic - // parameters, so this filtering serves two purposes: - // - // 1. We skip evaluating any predicates that we would - // never be able prove are unsatisfiable (e.g. `` - // 2. We avoid trying to normalize predicates involving generic - // parameters (e.g. `::MyItem`). This can confuse - // the normalization code (leading to cycle errors), since - // it's usually never invoked in this way. - let predicates = tcx - .predicates_of(body.source.def_id()) - .predicates - .iter() - .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None }); - if traits::impossible_predicates(tcx, traits::elaborate(tcx, predicates).collect()) { - trace!("found unsatisfiable predicates for {:?}", body.source); - // Clear the body to only contain a single `unreachable` statement. - let bbs = body.basic_blocks.as_mut(); - bbs.raw.truncate(1); - bbs[START_BLOCK].statements.clear(); - bbs[START_BLOCK].terminator_mut().kind = TerminatorKind::Unreachable; - body.var_debug_info.clear(); - body.local_decls.raw.truncate(body.arg_count + 1); - } - run_analysis_to_runtime_passes(tcx, &mut body); - // Now that drop elaboration has been performed, we can check for - // unconditional drop recursion. - rustc_mir_build::lints::check_drop_recursion(tcx, &body); - tcx.alloc_steal_mir(body) } @@ -574,6 +539,7 @@ pub fn run_analysis_to_runtime_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<' &Lint(post_drop_elaboration::CheckLiveDrops), ], None, + pm::Optimizations::Allowed, ); } @@ -591,13 +557,20 @@ pub fn run_analysis_to_runtime_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<' /// After this series of passes, no lifetime analysis based on borrowing can be done. fn run_analysis_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let passes: &[&dyn MirPass<'tcx>] = &[ + &impossible_predicates::ImpossiblePredicates, &cleanup_post_borrowck::CleanupPostBorrowck, &remove_noop_landing_pads::RemoveNoopLandingPads, &simplify::SimplifyCfg::PostAnalysis, &deref_separator::Derefer, ]; - pm::run_passes(tcx, body, passes, Some(MirPhase::Analysis(AnalysisPhase::PostCleanup))); + pm::run_passes( + tcx, + body, + passes, + Some(MirPhase::Analysis(AnalysisPhase::PostCleanup)), + pm::Optimizations::Allowed, + ); } /// Returns the sequence of passes that lowers analysis to runtime MIR. @@ -610,6 +583,8 @@ fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // Calling this after `PostAnalysisNormalize` ensures that we don't deal with opaque types. &add_subtyping_projections::Subtyper, &elaborate_drops::ElaborateDrops, + // Needs to happen after drop elaboration. + &Lint(check_call_recursion::CheckDropRecursion), // This will remove extraneous landing pads which are no longer // necessary as well as forcing any call in a non-unwinding // function calling a possibly-unwinding function to abort the process. @@ -635,7 +610,13 @@ fn run_runtime_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &simplify::SimplifyCfg::PreOptimizations, ]; - pm::run_passes(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::PostCleanup))); + pm::run_passes( + tcx, + body, + passes, + Some(MirPhase::Runtime(RuntimePhase::PostCleanup)), + pm::Optimizations::Allowed, + ); // Clear this by anticipation. Optimizations and runtime MIR have no reason to look // into this information, which is meant for borrowck diagnostics. @@ -649,6 +630,15 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { WithMinOptLevel(1, x) } + let def_id = body.source.def_id(); + let optimizations = if tcx.def_kind(def_id).has_codegen_attrs() + && tcx.codegen_fn_attrs(def_id).optimize.do_not_optimize() + { + pm::Optimizations::Suppressed + } else { + pm::Optimizations::Allowed + }; + // The main optimizations that we do on MIR. pm::run_passes( tcx, @@ -656,6 +646,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &[ // Add some UB checks before any UB gets optimized away. &check_alignment::CheckAlignment, + &check_null::CheckNull, // Before inlining: trim down MIR with passes to reduce inlining work. // Has to be done before inlining, otherwise actual call will be almost always inlined. @@ -664,6 +655,8 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // Perform instsimplify before inline to eliminate some trivial calls (like clone // shims). &instsimplify::InstSimplify::BeforeInline, + // Perform inlining of `#[rustc_force_inline]`-annotated callees. + &inline::ForceInline, // Perform inlining, which may add a lot of code. &inline::Inline, // Code from other crates may have storage markers, so this needs to happen after @@ -708,7 +701,6 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &nrvo::RenameReturnPlace, &simplify::SimplifyLocals::Final, &multiple_return_terminators::MultipleReturnTerminators, - &deduplicate_blocks::DeduplicateBlocks, &large_enums::EnumSizeOpt { discrepancy: 128 }, // Some cleanup necessary at least for LLVM and potentially other codegen backends. &add_call_guards::CriticalCallEdges, @@ -719,6 +711,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &dump_mir::Marker("PreCodegen"), ], Some(MirPhase::Runtime(RuntimePhase::Optimized)), + optimizations, ); } @@ -740,7 +733,7 @@ fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> { // Run the `mir_for_ctfe` query, which depends on `mir_drops_elaborated_and_const_checked` // which we are going to steal below. Thus we need to run `mir_for_ctfe` first, so it // computes and caches its result. - Some(hir::ConstContext::ConstFn) => tcx.ensure_with_value().mir_for_ctfe(did), + Some(hir::ConstContext::ConstFn) => tcx.ensure_done().mir_for_ctfe(did), None => {} Some(other) => panic!("do not use `optimized_mir` for constants: {other:?}"), } @@ -779,7 +772,7 @@ fn promoted_mir(tcx: TyCtxt<'_>, def: LocalDefId) -> &IndexVec { when: String, body: &'a Body<'tcx>, is_fn_like: bool, - always_live_locals: &'a BitSet, + always_live_locals: &'a DenseBitSet, maybe_storage_live: ResultsCursor<'a, 'tcx, MaybeStorageLive<'a>>, maybe_storage_dead: ResultsCursor<'a, 'tcx, MaybeStorageDead<'a>>, places: FxHashSet>, diff --git a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs index e5a183bc75ce5..50d10883d2cad 100644 --- a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs +++ b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs @@ -351,6 +351,11 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body< { return; } + + // FIXME(typing_env): This should be able to reveal the opaques local to the + // body using the typeck results. + let typing_env = ty::TypingEnv::non_body_analysis(tcx, def_id); + // ## About BIDs in blocks ## // Track the set of blocks that contain a backwards-incompatible drop (BID) // and, for each block, the vector of locations. @@ -358,7 +363,7 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body< // We group them per-block because they tend to scheduled in the same drop ladder block. let mut bid_per_block = IndexMap::default(); let mut bid_places = UnordSet::new(); - let typing_env = ty::TypingEnv::post_analysis(tcx, def_id); + let mut ty_dropped_components = UnordMap::default(); for (block, data) in body.basic_blocks.iter_enumerated() { for (statement_index, stmt) in data.statements.iter().enumerate() { @@ -686,7 +691,7 @@ impl Subdiagnostic for LocalLabel<'_> { for dtor in self.destructors { dtor.add_to_diag_with(diag, f); } - let msg = f(diag, crate::fluent_generated::mir_transform_label_local_epilogue.into()); + let msg = f(diag, crate::fluent_generated::mir_transform_label_local_epilogue); diag.span_label(self.span, msg); } } diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 942c6144ea69c..9c21bcfc0d26a 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -34,6 +34,17 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { }); terminator.kind = TerminatorKind::Goto { target }; } + sym::contract_checks => { + let target = target.unwrap(); + block.statements.push(Statement { + source_info: terminator.source_info, + kind: StatementKind::Assign(Box::new(( + *destination, + Rvalue::NullaryOp(NullOp::ContractChecks, tcx.types.bool), + ))), + }); + terminator.kind = TerminatorKind::Goto { target }; + } sym::forget => { let target = target.unwrap(); block.statements.push(Statement { @@ -325,4 +336,8 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { } } } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs index 420661f29c8cf..aca80e36e339d 100644 --- a/compiler/rustc_mir_transform/src/lower_slice_len.rs +++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs @@ -26,6 +26,10 @@ impl<'tcx> crate::MirPass<'tcx> for LowerSliceLenCalls { lower_slice_len_call(block, slice_len_fn_item_def_id); } } + + fn is_required(&self) -> bool { + false + } } fn lower_slice_len_call<'tcx>(block: &mut BasicBlockData<'tcx>, slice_len_fn_item_def_id: DefId) { diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index 534ba99178017..9db37bf5a0721 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -2,7 +2,6 @@ use std::iter; use rustc_abi::Integer; use rustc_index::IndexSlice; -use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::*; use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; @@ -10,6 +9,7 @@ use rustc_type_ir::TyKind::*; use tracing::instrument; use super::simplify::simplify_cfg; +use crate::patch::MirPatch; pub(super) struct MatchBranchSimplification; @@ -49,6 +49,10 @@ impl<'tcx> crate::MirPass<'tcx> for MatchBranchSimplification { simplify_cfg(body); } } + + fn is_required(&self) -> bool { + false + } } trait SimplifyMatch<'tcx> { diff --git a/compiler/rustc_mir_transform/src/mentioned_items.rs b/compiler/rustc_mir_transform/src/mentioned_items.rs index cf5c5f85a9ff6..f5c57418467a1 100644 --- a/compiler/rustc_mir_transform/src/mentioned_items.rs +++ b/compiler/rustc_mir_transform/src/mentioned_items.rs @@ -27,6 +27,10 @@ impl<'tcx> crate::MirPass<'tcx> for MentionedItems { visitor.visit_body(body); body.set_mentioned_items(visitor.mentioned_items); } + + fn is_required(&self) -> bool { + true + } } // This visitor is carefully in sync with the one in `rustc_monomorphize::collector`. We are diff --git a/compiler/rustc_mir_transform/src/multiple_return_terminators.rs b/compiler/rustc_mir_transform/src/multiple_return_terminators.rs index a9227524ce5e5..c63bfdcee8559 100644 --- a/compiler/rustc_mir_transform/src/multiple_return_terminators.rs +++ b/compiler/rustc_mir_transform/src/multiple_return_terminators.rs @@ -1,7 +1,7 @@ //! This pass removes jumps to basic blocks containing only a return, and replaces them with a //! return instead. -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; @@ -16,7 +16,7 @@ impl<'tcx> crate::MirPass<'tcx> for MultipleReturnTerminators { fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // find basic blocks with no statement and a return terminator - let mut bbs_simple_returns = BitSet::new_empty(body.basic_blocks.len()); + let mut bbs_simple_returns = DenseBitSet::new_empty(body.basic_blocks.len()); let bbs = body.basic_blocks_mut(); for idx in bbs.indices() { if bbs[idx].statements.is_empty() @@ -36,4 +36,8 @@ impl<'tcx> crate::MirPass<'tcx> for MultipleReturnTerminators { simplify::remove_dead_blocks(body) } + + fn is_required(&self) -> bool { + false + } } diff --git a/compiler/rustc_mir_transform/src/nrvo.rs b/compiler/rustc_mir_transform/src/nrvo.rs index cd026ed68069f..965002aae04c7 100644 --- a/compiler/rustc_mir_transform/src/nrvo.rs +++ b/compiler/rustc_mir_transform/src/nrvo.rs @@ -1,7 +1,7 @@ //! See the docs for [`RenameReturnPlace`]. use rustc_hir::Mutability; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::bug; use rustc_middle::mir::visit::{MutVisitor, NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{self, BasicBlock, Local, Location}; @@ -71,6 +71,10 @@ impl<'tcx> crate::MirPass<'tcx> for RenameReturnPlace { // The return place is always mutable. ret_decl.mutability = Mutability::Mut; } + + fn is_required(&self) -> bool { + false + } } /// MIR that is eligible for the NRVO must fulfill two conditions: @@ -116,7 +120,7 @@ fn local_eligible_for_nrvo(body: &mir::Body<'_>) -> Option { fn find_local_assigned_to_return_place(start: BasicBlock, body: &mir::Body<'_>) -> Option { let mut block = start; - let mut seen = BitSet::new_empty(body.basic_blocks.len()); + let mut seen = DenseBitSet::new_empty(body.basic_blocks.len()); // Iterate as long as `block` has exactly one predecessor that we have not yet visited. while seen.insert(block) { diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 8a45ce0762d59..7a8d3ba1ff1fa 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -79,11 +79,22 @@ pub(super) trait MirPass<'tcx> { true } + /// Returns `true` if this pass can be overridden by `-Zenable-mir-passes`. This should be + /// true for basically every pass other than those that are necessary for correctness. + fn can_be_overridden(&self) -> bool { + true + } + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>); fn is_mir_dump_enabled(&self) -> bool { true } + + /// Returns `true` if this pass must be run (i.e. it is required for soundness). + /// For passes which are strictly optimizations, this should return `false`. + /// If this is `false`, `#[optimize(none)]` will disable the pass. + fn is_required(&self) -> bool; } /// Just like `MirPass`, except it cannot mutate `Body`, and MIR dumping is @@ -128,6 +139,10 @@ where fn is_mir_dump_enabled(&self) -> bool { false } + + fn is_required(&self) -> bool { + true + } } pub(super) struct WithMinOptLevel(pub u32, pub T); @@ -147,6 +162,19 @@ where fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { self.1.run_pass(tcx, body) } + + fn is_required(&self) -> bool { + self.1.is_required() + } +} + +/// Whether to allow non-[required] optimizations +/// +/// [required]: MirPass::is_required +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum Optimizations { + Suppressed, + Allowed, } /// Run the sequence of passes without validating the MIR after each pass. The MIR is still @@ -157,7 +185,7 @@ pub(super) fn run_passes_no_validate<'tcx>( passes: &[&dyn MirPass<'tcx>], phase_change: Option, ) { - run_passes_inner(tcx, body, passes, phase_change, false); + run_passes_inner(tcx, body, passes, phase_change, false, Optimizations::Allowed); } /// The optional `phase_change` is applied after executing all the passes, if present @@ -166,16 +194,25 @@ pub(super) fn run_passes<'tcx>( body: &mut Body<'tcx>, passes: &[&dyn MirPass<'tcx>], phase_change: Option, + optimizations: Optimizations, ) { - run_passes_inner(tcx, body, passes, phase_change, true); + run_passes_inner(tcx, body, passes, phase_change, true, optimizations); } -pub(super) fn should_run_pass<'tcx, P>(tcx: TyCtxt<'tcx>, pass: &P) -> bool +pub(super) fn should_run_pass<'tcx, P>( + tcx: TyCtxt<'tcx>, + pass: &P, + optimizations: Optimizations, +) -> bool where P: MirPass<'tcx> + ?Sized, { let name = pass.name(); + if !pass.can_be_overridden() { + return pass.is_enabled(tcx.sess); + } + let overridden_passes = &tcx.sess.opts.unstable_opts.mir_enable_passes; let overridden = overridden_passes.iter().rev().find(|(s, _)| s == &*name).map(|(_name, polarity)| { @@ -186,7 +223,8 @@ where ); *polarity }); - overridden.unwrap_or_else(|| pass.is_enabled(tcx.sess)) + let suppressed = !pass.is_required() && matches!(optimizations, Optimizations::Suppressed); + overridden.unwrap_or_else(|| !suppressed && pass.is_enabled(tcx.sess)) } fn run_passes_inner<'tcx>( @@ -195,6 +233,7 @@ fn run_passes_inner<'tcx>( passes: &[&dyn MirPass<'tcx>], phase_change: Option, validate_each: bool, + optimizations: Optimizations, ) { let overridden_passes = &tcx.sess.opts.unstable_opts.mir_enable_passes; trace!(?overridden_passes); @@ -233,7 +272,7 @@ fn run_passes_inner<'tcx>( for pass in passes { let name = pass.name(); - if !should_run_pass(tcx, *pass) { + if !should_run_pass(tcx, *pass, optimizations) { continue; }; diff --git a/compiler/rustc_middle/src/mir/patch.rs b/compiler/rustc_mir_transform/src/patch.rs similarity index 84% rename from compiler/rustc_middle/src/mir/patch.rs rename to compiler/rustc_mir_transform/src/patch.rs index 18c48d99b81ce..72cd9c224f648 100644 --- a/compiler/rustc_middle/src/mir/patch.rs +++ b/compiler/rustc_mir_transform/src/patch.rs @@ -1,10 +1,13 @@ +use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::*; +use rustc_middle::ty::Ty; +use rustc_span::Span; use tracing::debug; /// This struct represents a patch to MIR, which can add /// new statements and basic blocks and patch over block /// terminators. -pub struct MirPatch<'tcx> { +pub(crate) struct MirPatch<'tcx> { patch_map: IndexVec>>, new_blocks: Vec>, new_statements: Vec<(Location, StatementKind<'tcx>)>, @@ -21,7 +24,7 @@ pub struct MirPatch<'tcx> { } impl<'tcx> MirPatch<'tcx> { - pub fn new(body: &Body<'tcx>) -> Self { + pub(crate) fn new(body: &Body<'tcx>) -> Self { let mut result = MirPatch { patch_map: IndexVec::from_elem(None, &body.basic_blocks), new_blocks: vec![], @@ -68,7 +71,7 @@ impl<'tcx> MirPatch<'tcx> { result } - pub fn resume_block(&mut self) -> BasicBlock { + pub(crate) fn resume_block(&mut self) -> BasicBlock { if let Some(bb) = self.resume_block { return bb; } @@ -85,7 +88,7 @@ impl<'tcx> MirPatch<'tcx> { bb } - pub fn unreachable_cleanup_block(&mut self) -> BasicBlock { + pub(crate) fn unreachable_cleanup_block(&mut self) -> BasicBlock { if let Some(bb) = self.unreachable_cleanup_block { return bb; } @@ -102,7 +105,7 @@ impl<'tcx> MirPatch<'tcx> { bb } - pub fn unreachable_no_cleanup_block(&mut self) -> BasicBlock { + pub(crate) fn unreachable_no_cleanup_block(&mut self) -> BasicBlock { if let Some(bb) = self.unreachable_no_cleanup_block { return bb; } @@ -119,7 +122,7 @@ impl<'tcx> MirPatch<'tcx> { bb } - pub fn terminate_block(&mut self, reason: UnwindTerminateReason) -> BasicBlock { + pub(crate) fn terminate_block(&mut self, reason: UnwindTerminateReason) -> BasicBlock { if let Some((cached_bb, cached_reason)) = self.terminate_block && reason == cached_reason { @@ -138,19 +141,11 @@ impl<'tcx> MirPatch<'tcx> { bb } - pub fn is_patched(&self, bb: BasicBlock) -> bool { + pub(crate) fn is_patched(&self, bb: BasicBlock) -> bool { self.patch_map[bb].is_some() } - pub fn terminator_loc(&self, body: &Body<'tcx>, bb: BasicBlock) -> Location { - let offset = match bb.index().checked_sub(body.basic_blocks.len()) { - Some(index) => self.new_blocks[index].statements.len(), - None => body[bb].statements.len(), - }; - Location { block: bb, statement_index: offset } - } - - pub fn new_local_with_info( + pub(crate) fn new_local_with_info( &mut self, ty: Ty<'tcx>, span: Span, @@ -164,14 +159,14 @@ impl<'tcx> MirPatch<'tcx> { Local::new(index) } - pub fn new_temp(&mut self, ty: Ty<'tcx>, span: Span) -> Local { + pub(crate) fn new_temp(&mut self, ty: Ty<'tcx>, span: Span) -> Local { let index = self.next_local; self.next_local += 1; self.new_locals.push(LocalDecl::new(ty, span)); Local::new(index) } - pub fn new_block(&mut self, data: BasicBlockData<'tcx>) -> BasicBlock { + pub(crate) fn new_block(&mut self, data: BasicBlockData<'tcx>) -> BasicBlock { let block = BasicBlock::new(self.patch_map.len()); debug!("MirPatch: new_block: {:?}: {:?}", block, data); self.new_blocks.push(data); @@ -179,22 +174,22 @@ impl<'tcx> MirPatch<'tcx> { block } - pub fn patch_terminator(&mut self, block: BasicBlock, new: TerminatorKind<'tcx>) { + pub(crate) fn patch_terminator(&mut self, block: BasicBlock, new: TerminatorKind<'tcx>) { assert!(self.patch_map[block].is_none()); debug!("MirPatch: patch_terminator({:?}, {:?})", block, new); self.patch_map[block] = Some(new); } - pub fn add_statement(&mut self, loc: Location, stmt: StatementKind<'tcx>) { + pub(crate) fn add_statement(&mut self, loc: Location, stmt: StatementKind<'tcx>) { debug!("MirPatch: add_statement({:?}, {:?})", loc, stmt); self.new_statements.push((loc, stmt)); } - pub fn add_assign(&mut self, loc: Location, place: Place<'tcx>, rv: Rvalue<'tcx>) { + pub(crate) fn add_assign(&mut self, loc: Location, place: Place<'tcx>, rv: Rvalue<'tcx>) { self.add_statement(loc, StatementKind::Assign(Box::new((place, rv)))); } - pub fn apply(self, body: &mut Body<'tcx>) { + pub(crate) fn apply(self, body: &mut Body<'tcx>) { debug!( "MirPatch: {:?} new temps, starting from index {}: {:?}", self.new_locals.len(), @@ -240,14 +235,14 @@ impl<'tcx> MirPatch<'tcx> { } } - pub fn source_info_for_index(data: &BasicBlockData<'_>, loc: Location) -> SourceInfo { + fn source_info_for_index(data: &BasicBlockData<'_>, loc: Location) -> SourceInfo { match data.statements.get(loc.statement_index) { Some(stmt) => stmt.source_info, None => data.terminator().source_info, } } - pub fn source_info_for_location(&self, body: &Body<'tcx>, loc: Location) -> SourceInfo { + pub(crate) fn source_info_for_location(&self, body: &Body<'tcx>, loc: Location) -> SourceInfo { let data = match loc.block.index().checked_sub(body.basic_blocks.len()) { Some(new) => &self.new_blocks[new], None => &body[loc.block], diff --git a/compiler/rustc_mir_transform/src/post_analysis_normalize.rs b/compiler/rustc_mir_transform/src/post_analysis_normalize.rs index 3eecf79a7eae7..76c2f082c0bfc 100644 --- a/compiler/rustc_mir_transform/src/post_analysis_normalize.rs +++ b/compiler/rustc_mir_transform/src/post_analysis_normalize.rs @@ -15,6 +15,10 @@ impl<'tcx> crate::MirPass<'tcx> for PostAnalysisNormalize { let typing_env = ty::TypingEnv::post_analysis(tcx, body.source.def_id()); PostAnalysisNormalizeVisitor { tcx, typing_env }.visit_body_preserves_cfg(body); } + + fn is_required(&self) -> bool { + true + } } struct PostAnalysisNormalizeVisitor<'tcx> { diff --git a/compiler/rustc_mir_transform/src/prettify.rs b/compiler/rustc_mir_transform/src/prettify.rs index 937c207776b35..8ccfbe2f194b4 100644 --- a/compiler/rustc_mir_transform/src/prettify.rs +++ b/compiler/rustc_mir_transform/src/prettify.rs @@ -4,7 +4,7 @@ //! (`-Zmir-enable-passes=+ReorderBasicBlocks,+ReorderLocals`) //! to make the MIR easier to read for humans. -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -35,6 +35,10 @@ impl<'tcx> crate::MirPass<'tcx> for ReorderBasicBlocks { permute(body.basic_blocks.as_mut(), &updater.map); } + + fn is_required(&self) -> bool { + false + } } /// Rearranges the locals into *use* order. @@ -51,8 +55,10 @@ impl<'tcx> crate::MirPass<'tcx> for ReorderLocals { } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let mut finder = - LocalFinder { map: IndexVec::new(), seen: BitSet::new_empty(body.local_decls.len()) }; + let mut finder = LocalFinder { + map: IndexVec::new(), + seen: DenseBitSet::new_empty(body.local_decls.len()), + }; // We can't reorder the return place or the arguments for local in (0..=body.arg_count).map(Local::from_usize) { @@ -83,6 +89,10 @@ impl<'tcx> crate::MirPass<'tcx> for ReorderLocals { permute(&mut body.local_decls, &updater.map); } + + fn is_required(&self) -> bool { + false + } } fn permute(data: &mut IndexVec, map: &IndexSlice) { @@ -113,7 +123,7 @@ impl<'tcx> MutVisitor<'tcx> for BasicBlockUpdater<'tcx> { struct LocalFinder { map: IndexVec, - seen: BitSet, + seen: DenseBitSet, } impl LocalFinder { diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 7451f4193042b..c8d8dc147e94f 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -61,6 +61,10 @@ impl<'tcx> crate::MirPass<'tcx> for PromoteTemps<'tcx> { let promoted = promote_candidates(body, tcx, temps, promotable_candidates); self.promoted_fragments.set(promoted); } + + fn is_required(&self) -> bool { + true + } } /// State of a temporary during collection and promotion. @@ -289,7 +293,8 @@ impl<'tcx> Validator<'_, 'tcx> { // Recurse directly. ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subtype(_) - | ProjectionElem::Subslice { .. } => {} + | ProjectionElem::Subslice { .. } + | ProjectionElem::UnwrapUnsafeBinder(_) => {} // Never recurse. ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) => { @@ -422,7 +427,9 @@ impl<'tcx> Validator<'_, 'tcx> { fn validate_rvalue(&mut self, rvalue: &Rvalue<'tcx>) -> Result<(), Unpromotable> { match rvalue { - Rvalue::Use(operand) | Rvalue::Repeat(operand, _) => { + Rvalue::Use(operand) + | Rvalue::Repeat(operand, _) + | Rvalue::WrapUnsafeBinder(operand, _) => { self.validate_operand(operand)?; } Rvalue::CopyForDeref(place) => { @@ -430,7 +437,9 @@ impl<'tcx> Validator<'_, 'tcx> { self.validate_operand(op)? } - Rvalue::Discriminant(place) => self.validate_place(place.as_ref())?, + Rvalue::Discriminant(place) | Rvalue::Len(place) => { + self.validate_place(place.as_ref())? + } Rvalue::ThreadLocalRef(_) => return Err(Unpromotable), @@ -448,6 +457,7 @@ impl<'tcx> Validator<'_, 'tcx> { NullOp::AlignOf => {} NullOp::OffsetOf(_) => {} NullOp::UbChecks => {} + NullOp::ContractChecks => {} }, Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable), @@ -910,19 +920,23 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { self.extra_statements.push((loc, promoted_ref_statement)); ( - Rvalue::Ref(tcx.lifetimes.re_erased, *borrow_kind, Place { - local: mem::replace(&mut place.local, promoted_ref), - projection: List::empty(), - }), + Rvalue::Ref( + tcx.lifetimes.re_erased, + *borrow_kind, + Place { + local: mem::replace(&mut place.local, promoted_ref), + projection: List::empty(), + }, + ), promoted_operand, ) }; assert_eq!(self.new_block(), START_BLOCK); - self.visit_rvalue(&mut rvalue, Location { - block: START_BLOCK, - statement_index: usize::MAX, - }); + self.visit_rvalue( + &mut rvalue, + Location { block: START_BLOCK, statement_index: usize::MAX }, + ); let span = self.promoted.span; self.assign(RETURN_PLACE, rvalue, span); diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs index 96bcdfa6fac47..368d5340ac34f 100644 --- a/compiler/rustc_mir_transform/src/ref_prop.rs +++ b/compiler/rustc_mir_transform/src/ref_prop.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use rustc_data_structures::fx::FxHashSet; use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::bug; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; @@ -81,6 +81,10 @@ impl<'tcx> crate::MirPass<'tcx> for ReferencePropagation { debug!(def_id = ?body.source.def_id()); while propagate_ssa(tcx, body) {} } + + fn is_required(&self) -> bool { + false + } } fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool { @@ -132,7 +136,7 @@ fn compute_replacement<'tcx>( let mut targets = IndexVec::from_elem(Value::Unknown, &body.local_decls); // Set of locals for which we will remove their storage statement. This is useful for // reborrowed references. - let mut storage_to_remove = BitSet::new_empty(body.local_decls.len()); + let mut storage_to_remove = DenseBitSet::new_empty(body.local_decls.len()); let fully_replacable_locals = fully_replacable_locals(ssa); @@ -324,8 +328,8 @@ fn compute_replacement<'tcx>( /// /// We consider a local to be replacable iff it's only used in a `Deref` projection `*_local` or /// non-use position (like storage statements and debuginfo). -fn fully_replacable_locals(ssa: &SsaLocals) -> BitSet { - let mut replacable = BitSet::new_empty(ssa.num_locals()); +fn fully_replacable_locals(ssa: &SsaLocals) -> DenseBitSet { + let mut replacable = DenseBitSet::new_empty(ssa.num_locals()); // First pass: for each local, whether its uses can be fully replaced. for local in ssa.locals() { @@ -344,7 +348,7 @@ fn fully_replacable_locals(ssa: &SsaLocals) -> BitSet { struct Replacer<'tcx> { tcx: TyCtxt<'tcx>, targets: IndexVec>, - storage_to_remove: BitSet, + storage_to_remove: DenseBitSet, allowed_replacements: FxHashSet<(Local, Location)>, any_replacement: bool, } diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index fd49e956f433b..1dd34005d6641 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -1,10 +1,11 @@ -use rustc_index::bit_set::BitSet; -use rustc_middle::mir::patch::MirPatch; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use rustc_target::spec::PanicStrategy; use tracing::debug; +use crate::patch::MirPatch; + /// A pass that removes noop landing pads and replaces jumps to them with /// `UnwindAction::Continue`. This is important because otherwise LLVM generates /// terrible code for these. @@ -40,7 +41,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { let mut jumps_folded = 0; let mut landing_pads_removed = 0; - let mut nop_landing_pads = BitSet::new_empty(body.basic_blocks.len()); + let mut nop_landing_pads = DenseBitSet::new_empty(body.basic_blocks.len()); // This is a post-order traversal, so that if A post-dominates B // then A will be visited before B. @@ -74,6 +75,10 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { debug!("removed {:?} jumps and {:?} landing pads", jumps_folded, landing_pads_removed); } + + fn is_required(&self) -> bool { + true + } } impl RemoveNoopLandingPads { @@ -81,7 +86,7 @@ impl RemoveNoopLandingPads { &self, bb: BasicBlock, body: &Body<'_>, - nop_landing_pads: &BitSet, + nop_landing_pads: &DenseBitSet, ) -> bool { for stmt in &body[bb].statements { match &stmt.kind { diff --git a/compiler/rustc_mir_transform/src/remove_place_mention.rs b/compiler/rustc_mir_transform/src/remove_place_mention.rs index 71399eb72f004..15fe77d53195a 100644 --- a/compiler/rustc_mir_transform/src/remove_place_mention.rs +++ b/compiler/rustc_mir_transform/src/remove_place_mention.rs @@ -20,4 +20,8 @@ impl<'tcx> crate::MirPass<'tcx> for RemovePlaceMention { }) } } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/remove_storage_markers.rs b/compiler/rustc_mir_transform/src/remove_storage_markers.rs index 3ecb4a8994f5f..1ae33c0096875 100644 --- a/compiler/rustc_mir_transform/src/remove_storage_markers.rs +++ b/compiler/rustc_mir_transform/src/remove_storage_markers.rs @@ -22,4 +22,8 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveStorageMarkers { }) } } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs index e955d8277a458..9044a88295cc4 100644 --- a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs @@ -62,6 +62,10 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveUninitDrops { block.terminator_mut().kind = TerminatorKind::Goto { target: *target }; } } + + fn is_required(&self) -> bool { + true + } } fn is_needs_drop_and_init<'tcx>( diff --git a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs index e335051d65644..8a8cdafc69070 100644 --- a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs @@ -38,4 +38,8 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveUnneededDrops { simplify_cfg(body); } } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs index 64e183bcbc010..78d94a038671d 100644 --- a/compiler/rustc_mir_transform/src/remove_zsts.rs +++ b/compiler/rustc_mir_transform/src/remove_zsts.rs @@ -27,6 +27,10 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveZsts { replacer.visit_basic_block_data(bb, data); } } + + fn is_required(&self) -> bool { + true + } } struct Replacer<'a, 'tcx> { @@ -36,31 +40,39 @@ struct Replacer<'a, 'tcx> { } /// A cheap, approximate check to avoid unnecessary `layout_of` calls. -fn maybe_zst(ty: Ty<'_>) -> bool { +/// +/// `Some(true)` is definitely ZST; `Some(false)` is definitely *not* ZST. +/// +/// `None` may or may not be, and must check `layout_of` to be sure. +fn trivially_zst<'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option { match ty.kind() { - // maybe ZST (could be more precise) - ty::Adt(..) - | ty::Array(..) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Tuple(..) - | ty::Alias(ty::Opaque, ..) => true, // definitely ZST - ty::FnDef(..) | ty::Never => true, - // unreachable or can't be ZST - _ => false, + ty::FnDef(..) | ty::Never => Some(true), + ty::Tuple(fields) if fields.is_empty() => Some(true), + ty::Array(_ty, len) if let Some(0) = len.try_to_target_usize(tcx) => Some(true), + // clearly not ZST + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnPtr(..) => Some(false), + // check `layout_of` to see (including unreachable things we won't actually see) + _ => None, } } impl<'tcx> Replacer<'_, 'tcx> { fn known_to_be_zst(&self, ty: Ty<'tcx>) -> bool { - if !maybe_zst(ty) { - return false; + if let Some(is_zst) = trivially_zst(ty, self.tcx) { + is_zst + } else { + self.tcx + .layout_of(self.typing_env.as_query_input(ty)) + .is_ok_and(|layout| layout.is_zst()) } - let Ok(layout) = self.tcx.layout_of(self.typing_env.as_query_input(ty)) else { - return false; - }; - layout.is_zst() } fn make_zst(&self, ty: Ty<'tcx>) -> ConstOperand<'tcx> { diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 722da3c420dc9..e8d86bad98735 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -6,7 +6,6 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_index::{Idx, IndexVec}; -use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::*; use rustc_middle::query::Providers; use rustc_middle::ty::adjustment::PointerCoercion; @@ -14,13 +13,14 @@ use rustc_middle::ty::{ self, CoroutineArgs, CoroutineArgsExt, EarlyBinder, GenericArgs, Ty, TyCtxt, }; use rustc_middle::{bug, span_bug}; -use rustc_mir_dataflow::elaborate_drops::{self, DropElaborator, DropFlagMode, DropStyle}; use rustc_span::source_map::Spanned; use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; +use crate::elaborate_drop::{DropElaborator, DropFlagMode, DropStyle, Unwind, elaborate_drop}; +use crate::patch::MirPatch; use crate::{ - abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator, + abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator, inline, instsimplify, mentioned_items, pass_manager as pm, remove_noop_landing_pads, simplify, }; @@ -119,6 +119,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body< &add_call_guards::CriticalCallEdges, ], Some(MirPhase::Runtime(RuntimePhase::Optimized)), + pm::Optimizations::Allowed, ); return body; @@ -155,6 +156,8 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body< &remove_noop_landing_pads::RemoveNoopLandingPads, &simplify::SimplifyCfg::MakeShim, &instsimplify::InstSimplify::BeforeInline, + // Perform inlining of `#[rustc_force_inline]`-annotated callees. + &inline::ForceInline, &abort_unwinding_calls::AbortUnwindingCalls, &add_call_guards::CriticalCallEdges, ], @@ -280,13 +283,13 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option>) DropShimElaborator { body: &body, patch: MirPatch::new(&body), tcx, typing_env }; let dropee = tcx.mk_place_deref(dropee_ptr); let resume_block = elaborator.patch.resume_block(); - elaborate_drops::elaborate_drop( + elaborate_drop( &mut elaborator, source_info, dropee, (), return_block, - elaborate_drops::Unwind::To(resume_block), + Unwind::To(resume_block), START_BLOCK, ); elaborator.patch diff --git a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs index f01bab75c4a1d..94b1b4b1855b5 100644 --- a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs +++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs @@ -2,17 +2,11 @@ use std::iter; use itertools::Itertools; use rustc_abi::{FieldIdx, VariantIdx}; -use rustc_ast::Mutability; use rustc_const_eval::interpret; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_index::{Idx, IndexVec}; -use rustc_middle::mir::{ - BasicBlock, BasicBlockData, Body, CallSource, CastKind, CoercionSource, Const, ConstOperand, - ConstValue, Local, LocalDecl, MirSource, Operand, Place, PlaceElem, RETURN_PLACE, Rvalue, - SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, UnwindAction, - UnwindTerminateReason, -}; +use rustc_middle::mir::*; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::util::{AsyncDropGlueMorphology, Discr}; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -345,7 +339,7 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> { .tcx .mk_place_elems(&[PlaceElem::Deref, PlaceElem::Field(field, field_ty)]), }; - self.put_temp_rvalue(Rvalue::RawPtr(Mutability::Mut, place)) + self.put_temp_rvalue(Rvalue::RawPtr(RawPtrKind::Mut, place)) } /// If given Self is an enum puts `to_drop: *mut FieldTy` on top of @@ -365,7 +359,7 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> { PlaceElem::Field(field, field_ty), ]), }; - self.put_temp_rvalue(Rvalue::RawPtr(Mutability::Mut, place)) + self.put_temp_rvalue(Rvalue::RawPtr(RawPtrKind::Mut, place)) } /// If given Self is an enum puts `to_drop: *mut FieldTy` on top of @@ -453,17 +447,19 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> { } fn combine_sync_surface(&mut self) -> Ty<'tcx> { - self.apply_combinator(1, LangItem::AsyncDropSurfaceDropInPlace, &[self - .self_ty - .unwrap() - .into()]) + self.apply_combinator( + 1, + LangItem::AsyncDropSurfaceDropInPlace, + &[self.self_ty.unwrap().into()], + ) } fn combine_deferred_drop_in_place(&mut self) -> Ty<'tcx> { - self.apply_combinator(1, LangItem::AsyncDropDeferredDropInPlace, &[self - .self_ty - .unwrap() - .into()]) + self.apply_combinator( + 1, + LangItem::AsyncDropDeferredDropInPlace, + &[self.self_ty.unwrap().into()], + ) } fn combine_fuse(&mut self, inner_future_ty: Ty<'tcx>) -> Ty<'tcx> { @@ -483,11 +479,11 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> { } fn combine_either(&mut self, other: Ty<'tcx>, matched: Ty<'tcx>) -> Ty<'tcx> { - self.apply_combinator(4, LangItem::AsyncDropEither, &[ - other.into(), - matched.into(), - self.self_ty.unwrap().into(), - ]) + self.apply_combinator( + 4, + LangItem::AsyncDropEither, + &[other.into(), matched.into(), self.self_ty.unwrap().into()], + ) } fn return_(mut self) -> Body<'tcx> { diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 4f312ed2aaabc..67070f03dedbd 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -83,6 +83,10 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyCfg { debug!("SimplifyCfg({:?}) - simplifying {:?}", self.name(), body.source); simplify_cfg(body); } + + fn is_required(&self) -> bool { + false + } } struct CfgSimplifier<'a, 'tcx> { @@ -405,6 +409,10 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyLocals { body.local_decls.shrink_to_fit(); } } + + fn is_required(&self) -> bool { + false + } } pub(super) fn remove_unused_definitions<'tcx>(body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/simplify_branches.rs b/compiler/rustc_mir_transform/src/simplify_branches.rs index bea3d0d8557a7..12c3503879fb9 100644 --- a/compiler/rustc_mir_transform/src/simplify_branches.rs +++ b/compiler/rustc_mir_transform/src/simplify_branches.rs @@ -60,4 +60,8 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyConstCondition { }; } } + + fn is_required(&self) -> bool { + false + } } diff --git a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs index b6d8017308662..21bc51ecca14e 100644 --- a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs +++ b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs @@ -113,10 +113,13 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyComparisonIntegral { // if we have StorageDeads to remove then make sure to insert them at the top of // each target for bb_idx in new_targets.all_targets() { - storage_deads_to_insert.push((*bb_idx, Statement { - source_info: terminator.source_info, - kind: StatementKind::StorageDead(opt.to_switch_on.local), - })); + storage_deads_to_insert.push(( + *bb_idx, + Statement { + source_info: terminator.source_info, + kind: StatementKind::StorageDead(opt.to_switch_on.local), + }, + )); } } @@ -140,6 +143,10 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyComparisonIntegral { body.basic_blocks_mut()[idx].statements.insert(0, stmt); } } + + fn is_required(&self) -> bool { + false + } } struct OptimizationFinder<'a, 'tcx> { diff --git a/compiler/rustc_mir_transform/src/single_use_consts.rs b/compiler/rustc_mir_transform/src/single_use_consts.rs index 277a33c031152..c5e951eb8b2c2 100644 --- a/compiler/rustc_mir_transform/src/single_use_consts.rs +++ b/compiler/rustc_mir_transform/src/single_use_consts.rs @@ -1,5 +1,5 @@ use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::bug; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -28,9 +28,9 @@ impl<'tcx> crate::MirPass<'tcx> for SingleUseConsts { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let mut finder = SingleUseConstsFinder { - ineligible_locals: BitSet::new_empty(body.local_decls.len()), + ineligible_locals: DenseBitSet::new_empty(body.local_decls.len()), locations: IndexVec::from_elem(LocationPair::new(), &body.local_decls), - locals_in_debug_info: BitSet::new_empty(body.local_decls.len()), + locals_in_debug_info: DenseBitSet::new_empty(body.local_decls.len()), }; finder.ineligible_locals.insert_range(..=Local::from_usize(body.arg_count)); @@ -81,6 +81,10 @@ impl<'tcx> crate::MirPass<'tcx> for SingleUseConsts { } } } + + fn is_required(&self) -> bool { + true + } } #[derive(Copy, Clone, Debug)] @@ -96,9 +100,9 @@ impl LocationPair { } struct SingleUseConstsFinder { - ineligible_locals: BitSet, + ineligible_locals: DenseBitSet, locations: IndexVec, - locals_in_debug_info: BitSet, + locals_in_debug_info: DenseBitSet, } impl<'tcx> Visitor<'tcx> for SingleUseConstsFinder { diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs index 52b9ec1e0a357..28de99f9193d9 100644 --- a/compiler/rustc_mir_transform/src/sroa.rs +++ b/compiler/rustc_mir_transform/src/sroa.rs @@ -2,15 +2,16 @@ use rustc_abi::{FIRST_VARIANT, FieldIdx}; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_hir::LangItem; use rustc_index::IndexVec; -use rustc_index::bit_set::{BitSet, GrowableBitSet}; +use rustc_index::bit_set::{DenseBitSet, GrowableBitSet}; use rustc_middle::bug; -use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_mir_dataflow::value_analysis::{excluded_locals, iter_fields}; use tracing::{debug, instrument}; +use crate::patch::MirPatch; + pub(super) struct ScalarReplacementOfAggregates; impl<'tcx> crate::MirPass<'tcx> for ScalarReplacementOfAggregates { @@ -48,6 +49,10 @@ impl<'tcx> crate::MirPass<'tcx> for ScalarReplacementOfAggregates { } } } + + fn is_required(&self) -> bool { + false + } } /// Identify all locals that are not eligible for SROA. @@ -60,9 +65,9 @@ impl<'tcx> crate::MirPass<'tcx> for ScalarReplacementOfAggregates { fn escaping_locals<'tcx>( tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, - excluded: &BitSet, + excluded: &DenseBitSet, body: &Body<'tcx>, -) -> BitSet { +) -> DenseBitSet { let is_excluded_ty = |ty: Ty<'tcx>| { if ty.is_union() || ty.is_enum() { return true; @@ -97,7 +102,7 @@ fn escaping_locals<'tcx>( false }; - let mut set = BitSet::new_empty(body.local_decls.len()); + let mut set = DenseBitSet::new_empty(body.local_decls.len()); set.insert_range(RETURN_PLACE..=Local::from_usize(body.arg_count)); for (local, decl) in body.local_decls().iter_enumerated() { if excluded.contains(local) || is_excluded_ty(decl.ty) { @@ -109,7 +114,7 @@ fn escaping_locals<'tcx>( return visitor.set; struct EscapeVisitor { - set: BitSet, + set: DenseBitSet, } impl<'tcx> Visitor<'tcx> for EscapeVisitor { @@ -198,7 +203,7 @@ fn compute_flattening<'tcx>( tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, body: &mut Body<'tcx>, - escaping: BitSet, + escaping: DenseBitSet, ) -> ReplacementMap<'tcx> { let mut fragments = IndexVec::from_elem(None, &body.local_decls); @@ -226,8 +231,8 @@ fn replace_flattened_locals<'tcx>( tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, replacements: ReplacementMap<'tcx>, -) -> BitSet { - let mut all_dead_locals = BitSet::new_empty(replacements.fragments.len()); +) -> DenseBitSet { + let mut all_dead_locals = DenseBitSet::new_empty(replacements.fragments.len()); for (local, replacements) in replacements.fragments.iter_enumerated() { if replacements.is_some() { all_dead_locals.insert(local); @@ -267,7 +272,7 @@ struct ReplacementVisitor<'tcx, 'll> { /// Work to do. replacements: &'ll ReplacementMap<'tcx>, /// This is used to check that we are not leaving references to replaced locals behind. - all_dead_locals: BitSet, + all_dead_locals: DenseBitSet, patch: MirPatch<'tcx>, } diff --git a/compiler/rustc_mir_transform/src/ssa.rs b/compiler/rustc_mir_transform/src/ssa.rs index 5653aef0aae06..9f769077e77e2 100644 --- a/compiler/rustc_mir_transform/src/ssa.rs +++ b/compiler/rustc_mir_transform/src/ssa.rs @@ -7,7 +7,7 @@ //! of a `Freeze` local. Those can still be considered to be SSA. use rustc_data_structures::graph::dominators::Dominators; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::bug; use rustc_middle::middle::resolve_bound_vars::Set1; @@ -29,7 +29,7 @@ pub(super) struct SsaLocals { /// We ignore non-uses (Storage statements, debuginfo). direct_uses: IndexVec, /// Set of SSA locals that are immutably borrowed. - borrowed_locals: BitSet, + borrowed_locals: DenseBitSet, } pub(super) enum AssignedValue<'a, 'tcx> { @@ -50,7 +50,7 @@ impl SsaLocals { let dominators = body.basic_blocks.dominators(); let direct_uses = IndexVec::from_elem(0, &body.local_decls); - let borrowed_locals = BitSet::new_empty(body.local_decls.len()); + let borrowed_locals = DenseBitSet::new_empty(body.local_decls.len()); let mut visitor = SsaVisitor { body, assignments, @@ -159,10 +159,11 @@ impl SsaLocals { ) { for &local in &self.assignment_order { match self.assignments[local] { - Set1::One(DefLocation::Argument) => f(local, AssignedValue::Arg, Location { - block: START_BLOCK, - statement_index: 0, - }), + Set1::One(DefLocation::Argument) => f( + local, + AssignedValue::Arg, + Location { block: START_BLOCK, statement_index: 0 }, + ), Set1::One(DefLocation::Assignment(loc)) => { let bb = &mut basic_blocks[loc.block]; // `loc` must point to a direct assignment to `local`. @@ -202,12 +203,12 @@ impl SsaLocals { } /// Set of SSA locals that are immutably borrowed. - pub(super) fn borrowed_locals(&self) -> &BitSet { + pub(super) fn borrowed_locals(&self) -> &DenseBitSet { &self.borrowed_locals } /// Make a property uniform on a copy equivalence class by removing elements. - pub(super) fn meet_copy_equivalence(&self, property: &mut BitSet) { + pub(super) fn meet_copy_equivalence(&self, property: &mut DenseBitSet) { // Consolidate to have a local iff all its copies are. // // `copy_classes` defines equivalence classes between locals. The `local`s that recursively @@ -241,7 +242,7 @@ struct SsaVisitor<'a, 'tcx> { assignment_order: Vec, direct_uses: IndexVec, // Track locals that are immutably borrowed, so we can check their type is `Freeze` later. - borrowed_locals: BitSet, + borrowed_locals: DenseBitSet, } impl SsaVisitor<'_, '_> { @@ -396,7 +397,7 @@ pub(crate) struct StorageLiveLocals { impl StorageLiveLocals { pub(crate) fn new( body: &Body<'_>, - always_storage_live_locals: &BitSet, + always_storage_live_locals: &DenseBitSet, ) -> StorageLiveLocals { let mut storage_live = IndexVec::from_elem(Set1::Empty, &body.local_decls); for local in always_storage_live_locals.iter() { diff --git a/compiler/rustc_mir_transform/src/strip_debuginfo.rs b/compiler/rustc_mir_transform/src/strip_debuginfo.rs index 438c75726bb90..9ede8aa79c44a 100644 --- a/compiler/rustc_mir_transform/src/strip_debuginfo.rs +++ b/compiler/rustc_mir_transform/src/strip_debuginfo.rs @@ -31,4 +31,8 @@ impl<'tcx> crate::MirPass<'tcx> for StripDebugInfo { ) }); } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs b/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs index 55dcad0680a96..6ccec5b6f2129 100644 --- a/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs +++ b/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs @@ -3,7 +3,6 @@ use rustc_abi::Variants; use rustc_data_structures::fx::FxHashSet; use rustc_middle::bug; -use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::{ BasicBlock, BasicBlockData, BasicBlocks, Body, Local, Operand, Rvalue, StatementKind, TerminatorKind, @@ -12,6 +11,8 @@ use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{Ty, TyCtxt}; use tracing::trace; +use crate::patch::MirPatch; + pub(super) struct UnreachableEnumBranching; fn get_discriminant_local(terminator: &TerminatorKind<'_>) -> Option { @@ -208,4 +209,8 @@ impl<'tcx> crate::MirPass<'tcx> for UnreachableEnumBranching { patch.apply(body); } + + fn is_required(&self) -> bool { + false + } } diff --git a/compiler/rustc_mir_transform/src/unreachable_prop.rs b/compiler/rustc_mir_transform/src/unreachable_prop.rs index 734703ec78b55..13fb5b3e56f2f 100644 --- a/compiler/rustc_mir_transform/src/unreachable_prop.rs +++ b/compiler/rustc_mir_transform/src/unreachable_prop.rs @@ -6,10 +6,11 @@ use rustc_abi::Size; use rustc_data_structures::fx::FxHashSet; use rustc_middle::bug; use rustc_middle::mir::interpret::Scalar; -use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::*; use rustc_middle::ty::{self, TyCtxt}; +use crate::patch::MirPatch; + pub(super) struct UnreachablePropagation; impl crate::MirPass<'_> for UnreachablePropagation { @@ -52,6 +53,10 @@ impl crate::MirPass<'_> for UnreachablePropagation { body.basic_blocks_mut()[bb].statements.clear(); } } + + fn is_required(&self) -> bool { + false + } } /// Return whether the current terminator is fully unreachable. diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index a670da94fcc9e..4ac3a268c9c33 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1,10 +1,11 @@ //! Validates the MIR to ensure that invariants are upheld. use rustc_abi::{ExternAbi, FIRST_VARIANT, Size}; +use rustc_attr_parsing::InlineAttr; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::LangItem; use rustc_index::IndexVec; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{Obligation, ObligationCause}; use rustc_middle::mir::coverage::CoverageKind; @@ -90,14 +91,24 @@ impl<'tcx> crate::MirPass<'tcx> for Validator { } } } + + fn is_required(&self) -> bool { + true + } } +/// This checker covers basic properties of the control-flow graph, (dis)allowed statements and terminators. +/// Everything checked here must be stable under substitution of generic parameters. In other words, +/// this is about the *structure* of the MIR, not the *contents*. +/// +/// Everything that depends on types, or otherwise can be affected by generic parameters, +/// must be checked in `TypeChecker`. struct CfgChecker<'a, 'tcx> { when: &'a str, body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, unwind_edge_count: usize, - reachable_blocks: BitSet, + reachable_blocks: DenseBitSet, value_cache: FxHashSet, // If `false`, then the MIR must not contain `UnwindAction::Continue` or // `TerminatorKind::Resume`. @@ -366,7 +377,8 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { self.check_edge(location, *target, EdgeKind::Normal); self.check_unwind_edge(location, *unwind); } - TerminatorKind::Call { args, .. } | TerminatorKind::TailCall { args, .. } => { + TerminatorKind::Call { func, args, .. } + | TerminatorKind::TailCall { func, args, .. } => { // FIXME(explicit_tail_calls): refactor this & add tail-call specific checks if let TerminatorKind::Call { target, unwind, destination, .. } = terminator.kind { if let Some(target) = target { @@ -419,6 +431,13 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { } } } + + if let ty::FnDef(did, ..) = func.ty(&self.body.local_decls, self.tcx).kind() + && self.body.phase >= MirPhase::Runtime(RuntimePhase::Optimized) + && matches!(self.tcx.codegen_fn_attrs(did).inline, InlineAttr::Force { .. }) + { + self.fail(location, "`#[rustc_force_inline]`-annotated function not inlined"); + } } TerminatorKind::Assert { target, unwind, .. } => { self.check_edge(location, *target, EdgeKind::Normal); @@ -794,6 +813,25 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ) } } + ProjectionElem::UnwrapUnsafeBinder(unwrapped_ty) => { + let binder_ty = place_ref.ty(&self.body.local_decls, self.tcx); + let ty::UnsafeBinder(binder_ty) = *binder_ty.ty.kind() else { + self.fail( + location, + format!("WrapUnsafeBinder does not produce a ty::UnsafeBinder"), + ); + return; + }; + let binder_inner_ty = self.tcx.instantiate_bound_regions_with_erased(*binder_ty); + if !self.mir_assign_valid_types(unwrapped_ty, binder_inner_ty) { + self.fail( + location, + format!( + "Cannot unwrap unsafe binder {binder_ty:?} into type {unwrapped_ty:?}" + ), + ); + } + } _ => {} } self.super_projection_elem(place_ref, elem, context, location); @@ -1009,6 +1047,14 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } Rvalue::Ref(..) => {} + Rvalue::Len(p) => { + let pty = p.ty(&self.body.local_decls, self.tcx).ty; + check_kinds!( + pty, + "Cannot compute length of non-array type {:?}", + ty::Array(..) | ty::Slice(..) + ); + } Rvalue::BinaryOp(op, vals) => { use BinOp::*; let a = vals.0.ty(&self.body.local_decls, self.tcx); @@ -1339,8 +1385,29 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { Rvalue::Repeat(_, _) | Rvalue::ThreadLocalRef(_) | Rvalue::RawPtr(_, _) - | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::UbChecks, _) + | Rvalue::NullaryOp( + NullOp::SizeOf | NullOp::AlignOf | NullOp::UbChecks | NullOp::ContractChecks, + _, + ) | Rvalue::Discriminant(_) => {} + + Rvalue::WrapUnsafeBinder(op, ty) => { + let unwrapped_ty = op.ty(self.body, self.tcx); + let ty::UnsafeBinder(binder_ty) = *ty.kind() else { + self.fail( + location, + format!("WrapUnsafeBinder does not produce a ty::UnsafeBinder"), + ); + return; + }; + let binder_inner_ty = self.tcx.instantiate_bound_regions_with_erased(*binder_ty); + if !self.mir_assign_valid_types(unwrapped_ty, binder_inner_ty) { + self.fail( + location, + format!("Cannot wrap {unwrapped_ty:?} into unsafe binder {binder_ty:?}"), + ); + } + } } self.super_rvalue(rvalue, location); } diff --git a/compiler/rustc_monomorphize/Cargo.toml b/compiler/rustc_monomorphize/Cargo.toml index 9bdaeb015cd5f..5462105e5e8e4 100644 --- a/compiler/rustc_monomorphize/Cargo.toml +++ b/compiler/rustc_monomorphize/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start rustc_abi = { path = "../rustc_abi" } +rustc_ast = { path = "../rustc_ast" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } @@ -15,6 +16,7 @@ rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } +rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } rustc_target = { path = "../rustc_target" } serde = "1" serde_json = "1" diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 05d34715314f1..e4a733fcbce88 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -207,6 +207,7 @@ use std::path::PathBuf; +use rustc_attr_parsing::InlineAttr; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::{LRef, MTLock, par_for_each_in}; use rustc_data_structures::unord::{UnordMap, UnordSet}; @@ -224,15 +225,15 @@ use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCoercion}; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::print::{shrunk_instance_name, with_no_trimmed_paths}; use rustc_middle::ty::{ - self, GenericArgs, GenericParamDefKind, Instance, InstanceKind, Ty, TyCtxt, TypeFoldable, - TypeVisitableExt, VtblEntry, + self, GenericArgs, GenericParamDefKind, Instance, InstanceKind, Interner, Ty, TyCtxt, + TypeFoldable, TypeVisitableExt, VtblEntry, }; use rustc_middle::util::Providers; use rustc_middle::{bug, span_bug}; use rustc_session::Limit; use rustc_session::config::EntryFnType; use rustc_span::source_map::{Spanned, dummy_spanned, respan}; -use rustc_span::{DUMMY_SP, Span, sym}; +use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument, trace}; use crate::errors::{self, EncounteredErrorWhileInstantiating, NoOptimizedMir, RecursionLimit}; @@ -256,7 +257,7 @@ struct SharedState<'tcx> { pub(crate) struct UsageMap<'tcx> { // Maps every mono item to the mono items used by it. - used_map: UnordMap, Vec>>, + pub used_map: UnordMap, Vec>>, // Maps every mono item to the mono items that use it. user_map: UnordMap, Vec>>, @@ -813,6 +814,9 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { mir::AssertKind::MisalignedPointerDereference { .. } => { push_mono_lang_item(self, LangItem::PanicMisalignedPointerDereference); } + mir::AssertKind::NullPointerDereference => { + push_mono_lang_item(self, LangItem::PanicNullPointerDereference); + } _ => { push_mono_lang_item(self, msg.panic_function()); } @@ -894,9 +898,8 @@ fn visit_instance_use<'tcx>( if !tcx.should_codegen_locally(instance) { return; } - if let ty::InstanceKind::Intrinsic(def_id) = instance.def { - let name = tcx.item_name(def_id); - if let Some(_requirement) = ValidityRequirement::from_intrinsic(name) { + if let Some(intrinsic) = tcx.intrinsic(instance.def_id()) { + if let Some(_requirement) = ValidityRequirement::from_intrinsic(intrinsic.name) { // The intrinsics assert_inhabited, assert_zero_valid, and assert_mem_uninitialized_valid will // be lowered in codegen to nothing or a call to panic_nounwind. So if we encounter any // of those intrinsics, we need to include a mono item for panic_nounwind, else we may try to @@ -906,11 +909,12 @@ fn visit_instance_use<'tcx>( if tcx.should_codegen_locally(panic_instance) { output.push(create_fn_mono_item(tcx, panic_instance, source)); } - } else if tcx.has_attr(def_id, sym::rustc_intrinsic) - && !tcx.has_attr(def_id, sym::rustc_intrinsic_must_be_overridden) - { - // Codegen the fallback body of intrinsics with fallback bodies - let instance = ty::Instance::new(def_id, instance.args); + } else if !intrinsic.must_be_overridden { + // Codegen the fallback body of intrinsics with fallback bodies. + // We explicitly skip this otherwise to ensure we get a linker error + // if anyone tries to call this intrinsic and the codegen backend did not + // override the implementation. + let instance = ty::Instance::new(instance.def_id(), instance.args); if tcx.should_codegen_locally(instance) { output.push(create_fn_mono_item(tcx, instance, source)); } @@ -949,7 +953,7 @@ fn visit_instance_use<'tcx>( /// Returns `true` if we should codegen an instance in the local crate, or returns `false` if we /// can just link to the upstream crate and therefore don't need a mono item. -fn should_codegen_locally<'tcx>(tcx: TyCtxtAt<'tcx>, instance: Instance<'tcx>) -> bool { +fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> bool { let Some(def_id) = instance.def.def_id_if_not_guaranteed_local_codegen() else { return true; }; @@ -959,12 +963,20 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxtAt<'tcx>, instance: Instance<'tcx>) - return false; } + if tcx.def_kind(def_id).has_codegen_attrs() + && matches!(tcx.codegen_fn_attrs(def_id).inline, InlineAttr::Force { .. }) + { + // `#[rustc_force_inline]` items should never be codegened. This should be caught by + // the MIR validator. + tcx.delay_bug("attempt to codegen `#[rustc_force_inline]` item"); + } + if def_id.is_local() { // Local items cannot be referred to locally without monomorphizing them locally. return true; } - if tcx.is_reachable_non_generic(def_id) || instance.upstream_monomorphization(*tcx).is_some() { + if tcx.is_reachable_non_generic(def_id) || instance.upstream_monomorphization(tcx).is_some() { // We can link to the item in question, no instance needed in this crate. return false; } @@ -1129,11 +1141,12 @@ fn create_mono_items_for_vtable_methods<'tcx>( bug!("create_mono_items_for_vtable_methods: {trait_ty:?} not a trait type"); }; if let Some(principal) = trait_ty.principal() { - let poly_trait_ref = principal.with_self_ty(tcx, impl_ty); - assert!(!poly_trait_ref.has_escaping_bound_vars()); + let trait_ref = + tcx.instantiate_bound_regions_with_erased(principal.with_self_ty(tcx, impl_ty)); + assert!(!trait_ref.has_escaping_bound_vars()); // Walk all methods of the trait, including those of its supertraits - let entries = tcx.vtable_entries(poly_trait_ref); + let entries = tcx.vtable_entries(trait_ref); debug!(?entries); let methods = entries .iter() @@ -1188,7 +1201,12 @@ fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIt } } GlobalAlloc::VTable(ty, dyn_ty) => { - let alloc_id = tcx.vtable_allocation((ty, dyn_ty.principal())); + let alloc_id = tcx.vtable_allocation(( + ty, + dyn_ty + .principal() + .map(|principal| tcx.instantiate_bound_regions_with_erased(principal)), + )); collect_alloc(tcx, alloc_id, output) } } @@ -1204,7 +1222,7 @@ fn collect_items_of_instance<'tcx>( mode: CollectionMode, ) -> (MonoItems<'tcx>, MonoItems<'tcx>) { // This item is getting monomorphized, do mono-time checks. - tcx.ensure().check_mono_item(instance); + tcx.ensure_ok().check_mono_item(instance); let body = tcx.instance_mir(instance.def); // Naively, in "used" collection mode, all functions get added to *both* `used_items` and @@ -1368,6 +1386,10 @@ fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionStrategy) -> Vec RootCollector<'_, 'v> { match self.tcx.def_kind(id.owner_id) { DefKind::Enum | DefKind::Struct | DefKind::Union => { if self.strategy == MonoItemCollectionStrategy::Eager - && self.tcx.generics_of(id.owner_id).is_empty() + && !self.tcx.generics_of(id.owner_id).requires_monomorphization(self.tcx) { debug!("RootCollector: ADT drop-glue for `{id:?}`",); + let id_args = + ty::GenericArgs::for_item(self.tcx, id.owner_id.to_def_id(), |param, _| { + match param.kind { + GenericParamDefKind::Lifetime => { + self.tcx.lifetimes.re_erased.into() + } + GenericParamDefKind::Type { .. } + | GenericParamDefKind::Const { .. } => { + unreachable!( + "`own_requires_monomorphization` check means that \ + we should have no type/const params" + ) + } + } + }); // This type is impossible to instantiate, so we should not try to // generate a `drop_in_place` instance for it. if self.tcx.instantiate_and_check_impossible_predicates(( id.owner_id.to_def_id(), - ty::List::empty(), + id_args, )) { return; } - let ty = self.tcx.type_of(id.owner_id.to_def_id()).no_bound_vars().unwrap(); + let ty = + self.tcx.type_of(id.owner_id.to_def_id()).instantiate(self.tcx, id_args); + assert!(!ty.has_non_region_param()); visit_drop_use(self.tcx, ty, true, DUMMY_SP, self.output); } } @@ -1424,11 +1463,14 @@ impl<'v> RootCollector<'_, 'v> { self.output.push(dummy_spanned(MonoItem::Static(def_id))); } DefKind::Const => { - // const items only generate mono items if they are - // actually used somewhere. Just declaring them is insufficient. + // Const items only generate mono items if they are actually used somewhere. + // Just declaring them is insufficient. - // but even just declaring them must collect the items they refer to - if let Ok(val) = self.tcx.const_eval_poly(id.owner_id.to_def_id()) { + // But even just declaring them must collect the items they refer to + // unless their generics require monomorphization. + if !self.tcx.generics_of(id.owner_id).requires_monomorphization(self.tcx) + && let Ok(val) = self.tcx.const_eval_poly(id.owner_id.to_def_id()) + { collect_const_value(self.tcx, val, self.output); } } @@ -1450,10 +1492,46 @@ impl<'v> RootCollector<'_, 'v> { } } + fn process_nested_body(&mut self, def_id: LocalDefId) { + match self.tcx.def_kind(def_id) { + DefKind::Closure => { + if self.strategy == MonoItemCollectionStrategy::Eager + && !self + .tcx + .generics_of(self.tcx.typeck_root_def_id(def_id.to_def_id())) + .requires_monomorphization(self.tcx) + { + let instance = match *self.tcx.type_of(def_id).instantiate_identity().kind() { + ty::Closure(def_id, args) + | ty::Coroutine(def_id, args) + | ty::CoroutineClosure(def_id, args) => { + Instance::new(def_id, self.tcx.erase_regions(args)) + } + _ => unreachable!(), + }; + let Ok(instance) = self.tcx.try_normalize_erasing_regions( + ty::TypingEnv::fully_monomorphized(), + instance, + ) else { + // Don't ICE on an impossible-to-normalize closure. + return; + }; + let mono_item = create_fn_mono_item(self.tcx, instance, DUMMY_SP); + if mono_item.node.is_instantiable(self.tcx) { + self.output.push(mono_item); + } + } + } + _ => {} + } + } + fn is_root(&self, def_id: LocalDefId) -> bool { !self.tcx.generics_of(def_id).requires_monomorphization(self.tcx) && match self.strategy { - MonoItemCollectionStrategy::Eager => true, + MonoItemCollectionStrategy::Eager => { + !matches!(self.tcx.codegen_fn_attrs(def_id).inline, InlineAttr::Force { .. }) + } MonoItemCollectionStrategy::Lazy => { self.entry_fn.and_then(|(id, _)| id.as_local()) == Some(def_id) || self.tcx.is_reachable_non_generic(def_id) diff --git a/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs b/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs index 30e634d825276..a0be7f11d709d 100644 --- a/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs @@ -1,5 +1,6 @@ //! This module ensures that if a function's ABI requires a particular target feature, //! that target feature is enabled both on the callee and all callers. +use rustc_abi::{BackendRepr, RegKind}; use rustc_hir::CRATE_HIR_ID; use rustc_middle::mir::{self, traversal}; use rustc_middle::ty::inherent::*; @@ -7,8 +8,7 @@ use rustc_middle::ty::{self, Instance, InstanceKind, Ty, TyCtxt}; use rustc_session::lint::builtin::ABI_UNSUPPORTED_VECTOR_TYPES; use rustc_span::def_id::DefId; use rustc_span::{DUMMY_SP, Span, Symbol}; -use rustc_target::abi::call::{FnAbi, PassMode}; -use rustc_target::abi::{BackendRepr, RegKind}; +use rustc_target::callconv::{FnAbi, PassMode}; use crate::errors::{ AbiErrorDisabledVectorTypeCall, AbiErrorDisabledVectorTypeDef, diff --git a/compiler/rustc_monomorphize/src/mono_checks/move_check.rs b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs index 02b9397f9a995..838bfdab1ea59 100644 --- a/compiler/rustc_monomorphize/src/mono_checks/move_check.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs @@ -162,11 +162,12 @@ impl<'tcx> MoveCheckVisitor<'tcx> { // but correct span? This would make the lint at least accept crate-level lint attributes. return; }; - self.tcx.emit_node_span_lint(LARGE_ASSIGNMENTS, lint_root, span, LargeAssignmentsLint { + self.tcx.emit_node_span_lint( + LARGE_ASSIGNMENTS, + lint_root, span, - size: too_large_size.bytes(), - limit: limit as u64, - }); + LargeAssignmentsLint { span, size: too_large_size.bytes(), limit: limit as u64 }, + ); self.move_size_spans.push(span); } } diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 7b17966343084..d826d03918e08 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -92,6 +92,8 @@ //! source-level module, functions from the same module will be available for //! inlining, even when they are not marked `#[inline]`. +mod autodiff; + use std::cmp; use std::collections::hash_map::Entry; use std::fs::{self, File}; @@ -110,7 +112,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel}; use rustc_middle::mir::mono::{ CodegenUnit, CodegenUnitNameBuilder, InstantiationMode, Linkage, MonoItem, MonoItemData, - Visibility, + MonoItemPartitions, Visibility, }; use rustc_middle::ty::print::{characteristic_def_id_of_type, with_no_trimmed_paths}; use rustc_middle::ty::{self, InstanceKind, TyCtxt}; @@ -251,17 +253,23 @@ where can_export_generics, always_export_generics, ); - if visibility == Visibility::Hidden && can_be_internalized { + + // We can't differentiate something that got inlined. + let autodiff_active = cfg!(llvm_enzyme) + && cx + .tcx + .codegen_fn_attrs(mono_item.def_id()) + .autodiff_item + .as_ref() + .is_some_and(|ad| ad.is_active()); + + if !autodiff_active && visibility == Visibility::Hidden && can_be_internalized { internalization_candidates.insert(mono_item); } let size_estimate = mono_item.size_estimate(cx.tcx); - cgu.items_mut().insert(mono_item, MonoItemData { - inlined: false, - linkage, - visibility, - size_estimate, - }); + cgu.items_mut() + .insert(mono_item, MonoItemData { inlined: false, linkage, visibility, size_estimate }); // Get all inlined items that are reachable from `mono_item` without // going via another root item. This includes drop-glue, functions from @@ -1114,7 +1122,7 @@ where } } -fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[CodegenUnit<'_>]) { +fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> MonoItemPartitions<'_> { let collection_strategy = match tcx.sess.opts.unstable_opts.print_mono_items { Some(ref s) => { let mode = s.to_lowercase(); @@ -1167,15 +1175,27 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[Co } } + #[cfg(not(llvm_enzyme))] + let autodiff_mono_items: Vec<_> = vec![]; + #[cfg(llvm_enzyme)] + let mut autodiff_mono_items: Vec<_> = vec![]; let mono_items: DefIdSet = items .iter() .filter_map(|mono_item| match *mono_item { - MonoItem::Fn(ref instance) => Some(instance.def_id()), + MonoItem::Fn(ref instance) => { + #[cfg(llvm_enzyme)] + autodiff_mono_items.push((mono_item, instance)); + Some(instance.def_id()) + } MonoItem::Static(def_id) => Some(def_id), _ => None, }) .collect(); + let autodiff_items = + autodiff::find_autodiff_source_functions(tcx, &usage_map, autodiff_mono_items); + let autodiff_items = tcx.arena.alloc_from_iter(autodiff_items); + // Output monomorphization stats per def_id if let SwitchWithOptPath::Enabled(ref path) = tcx.sess.opts.unstable_opts.dump_mono_stats { if let Err(err) = @@ -1214,9 +1234,7 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[Co Linkage::LinkOnceODR => "OnceODR", Linkage::WeakAny => "WeakAny", Linkage::WeakODR => "WeakODR", - Linkage::Appending => "Appending", Linkage::Internal => "Internal", - Linkage::Private => "Private", Linkage::ExternalWeak => "ExternalWeak", Linkage::Common => "Common", }; @@ -1236,7 +1254,11 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[Co } } - (tcx.arena.alloc(mono_items), codegen_units) + MonoItemPartitions { + all_mono_items: tcx.arena.alloc(mono_items), + codegen_units, + autodiff_items, + } } /// Outputs stats about instantiation counts and estimated size, per `MonoItem`'s @@ -1319,14 +1341,13 @@ fn dump_mono_items_stats<'tcx>( pub(crate) fn provide(providers: &mut Providers) { providers.collect_and_partition_mono_items = collect_and_partition_mono_items; - providers.is_codegened_item = |tcx, def_id| { - let (all_mono_items, _) = tcx.collect_and_partition_mono_items(()); - all_mono_items.contains(&def_id) - }; + providers.is_codegened_item = + |tcx, def_id| tcx.collect_and_partition_mono_items(()).all_mono_items.contains(&def_id); providers.codegen_unit = |tcx, name| { - let (_, all) = tcx.collect_and_partition_mono_items(()); - all.iter() + tcx.collect_and_partition_mono_items(()) + .codegen_units + .iter() .find(|cgu| cgu.name() == name) .unwrap_or_else(|| panic!("failed to find cgu with name {name:?}")) }; diff --git a/compiler/rustc_monomorphize/src/partitioning/autodiff.rs b/compiler/rustc_monomorphize/src/partitioning/autodiff.rs new file mode 100644 index 0000000000000..bce31bf0748e0 --- /dev/null +++ b/compiler/rustc_monomorphize/src/partitioning/autodiff.rs @@ -0,0 +1,121 @@ +use rustc_ast::expand::autodiff_attrs::{AutoDiffItem, DiffActivity}; +use rustc_hir::def_id::LOCAL_CRATE; +use rustc_middle::bug; +use rustc_middle::mir::mono::MonoItem; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; +use rustc_symbol_mangling::symbol_name_for_instance_in_crate; +use tracing::{debug, trace}; + +use crate::partitioning::UsageMap; + +fn adjust_activity_to_abi<'tcx>(tcx: TyCtxt<'tcx>, fn_ty: Ty<'tcx>, da: &mut Vec) { + if !matches!(fn_ty.kind(), ty::FnDef(..)) { + bug!("expected fn def for autodiff, got {:?}", fn_ty); + } + let fnc_binder: ty::Binder<'_, ty::FnSig<'_>> = fn_ty.fn_sig(tcx); + + // If rustc compiles the unmodified primal, we know that this copy of the function + // also has correct lifetimes. We know that Enzyme won't free the shadow too early + // (or actually at all), so let's strip lifetimes when computing the layout. + let x = tcx.instantiate_bound_regions_with_erased(fnc_binder); + let mut new_activities = vec![]; + let mut new_positions = vec![]; + for (i, ty) in x.inputs().iter().enumerate() { + if let Some(inner_ty) = ty.builtin_deref(true) { + if ty.is_fn_ptr() { + // FIXME(ZuseZ4): add a nicer error, or just figure out how to support them, + // since Enzyme itself can handle them. + tcx.dcx().err("function pointers are currently not supported in autodiff"); + } + if inner_ty.is_slice() { + // We know that the length will be passed as extra arg. + if !da.is_empty() { + // We are looking at a slice. The length of that slice will become an + // extra integer on llvm level. Integers are always const. + // However, if the slice get's duplicated, we want to know to later check the + // size. So we mark the new size argument as FakeActivitySize. + let activity = match da[i] { + DiffActivity::DualOnly + | DiffActivity::Dual + | DiffActivity::DuplicatedOnly + | DiffActivity::Duplicated => DiffActivity::FakeActivitySize, + DiffActivity::Const => DiffActivity::Const, + _ => bug!("unexpected activity for ptr/ref"), + }; + new_activities.push(activity); + new_positions.push(i + 1); + } + continue; + } + } + } + // now add the extra activities coming from slices + // Reverse order to not invalidate the indices + for _ in 0..new_activities.len() { + let pos = new_positions.pop().unwrap(); + let activity = new_activities.pop().unwrap(); + da.insert(pos, activity); + } +} + +pub(crate) fn find_autodiff_source_functions<'tcx>( + tcx: TyCtxt<'tcx>, + usage_map: &UsageMap<'tcx>, + autodiff_mono_items: Vec<(&MonoItem<'tcx>, &Instance<'tcx>)>, +) -> Vec { + let mut autodiff_items: Vec = vec![]; + for (item, instance) in autodiff_mono_items { + let target_id = instance.def_id(); + let cg_fn_attr = tcx.codegen_fn_attrs(target_id).autodiff_item.clone(); + let Some(target_attrs) = cg_fn_attr else { + continue; + }; + let mut input_activities: Vec = target_attrs.input_activity.clone(); + if target_attrs.is_source() { + trace!("source found: {:?}", target_id); + } + if !target_attrs.apply_autodiff() { + continue; + } + + let target_symbol = symbol_name_for_instance_in_crate(tcx, instance.clone(), LOCAL_CRATE); + + let source = + usage_map.used_map.get(&item).unwrap().into_iter().find_map(|item| match *item { + MonoItem::Fn(ref instance_s) => { + let source_id = instance_s.def_id(); + if let Some(ad) = &tcx.codegen_fn_attrs(source_id).autodiff_item + && ad.is_active() + { + return Some(instance_s); + } + None + } + _ => None, + }); + let inst = match source { + Some(source) => source, + None => continue, + }; + + debug!("source_id: {:?}", inst.def_id()); + let fn_ty = inst.ty(tcx, ty::TypingEnv::fully_monomorphized()); + assert!(fn_ty.is_fn()); + adjust_activity_to_abi(tcx, fn_ty, &mut input_activities); + let symb = symbol_name_for_instance_in_crate(tcx, inst.clone(), LOCAL_CRATE); + + let mut new_target_attrs = target_attrs.clone(); + new_target_attrs.input_activity = input_activities; + let itm = new_target_attrs.into_item(symb, target_symbol); + autodiff_items.push(itm); + } + + if !autodiff_items.is_empty() { + trace!("AUTODIFF ITEMS EXIST"); + for item in &mut *autodiff_items { + trace!("{}", &item); + } + } + + autodiff_items +} diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 8a54a4ece983e..7eeed721d5a0f 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -273,7 +273,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { // // For this we set `next_orig_uv` to the next smallest, not yet compressed, // universe of the input. - if next_orig_uv.map_or(true, |curr_next_uv| uv.cannot_name(curr_next_uv)) { + if next_orig_uv.is_none_or(|curr_next_uv| uv.cannot_name(curr_next_uv)) { next_orig_uv = Some(uv); } } @@ -522,7 +522,7 @@ impl, I: Interner> TypeFolder for Canonicaliz // FIXME: See comment above -- we could fold the region separately or something. ty::ConstKind::Bound(_, _) | ty::ConstKind::Unevaluated(_) - | ty::ConstKind::Value(_, _) + | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) | ty::ConstKind::Expr(_) => return c.super_fold_with(self), }; diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 4ba54a2e0bf23..a64941cce3afd 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -4,15 +4,13 @@ use rustc_type_ir::fold::TypeFoldable; use rustc_type_ir::solve::{Certainty, Goal, NoSolution}; use rustc_type_ir::{self as ty, InferCtxtLike, Interner}; -pub trait SolverDelegate: Deref::Infcx> + Sized { - type Infcx: InferCtxtLike::Interner>; +pub trait SolverDelegate: Deref + Sized { + type Infcx: InferCtxtLike; type Interner: Interner; fn cx(&self) -> Self::Interner { (**self).cx() } - type Span: Copy; - fn build_with_canonical( cx: Self::Interner, canonical: &ty::CanonicalQueryInput, @@ -23,7 +21,7 @@ pub trait SolverDelegate: Deref::Infcx> + Size fn fresh_var_for_kind_with_span( &self, arg: ::GenericArg, - span: Self::Span, + span: ::Span, ) -> ::GenericArg; // FIXME: Uplift the leak check into this crate. @@ -61,6 +59,7 @@ pub trait SolverDelegate: Deref::Infcx> + Size fn instantiate_canonical_var_with_infer( &self, cv_info: ty::CanonicalVarInfo, + span: ::Span, universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> ::GenericArg; @@ -86,6 +85,7 @@ pub trait SolverDelegate: Deref::Infcx> + Size &self, key: ty::OpaqueTypeKey, hidden_ty: ::Ty, + span: ::Span, ); fn reset_opaque_types(&self); @@ -95,7 +95,10 @@ pub trait SolverDelegate: Deref::Infcx> + Size goal_trait_ref: ty::TraitRef, trait_assoc_def_id: ::DefId, impl_def_id: ::DefId, - ) -> Result::DefId>, NoSolution>; + ) -> Result< + Option<::DefId>, + ::ErrorGuaranteed, + >; fn is_transmutable( &self, diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 63432dc199b98..b0f59ed1474c2 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -6,7 +6,6 @@ use derive_where::derive_where; use rustc_type_ir::fold::TypeFoldable; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; -use rustc_type_ir::solve::inspect; use rustc_type_ir::visit::TypeVisitableExt as _; use rustc_type_ir::{self as ty, Interner, TypingMode, Upcast as _, elaborate}; use tracing::{debug, instrument}; @@ -19,6 +18,11 @@ use crate::solve::{ MaybeCause, NoSolution, QueryResult, }; +enum AliasBoundKind { + SelfBounds, + NonSelfBounds, +} + /// A candidate is a possible way to prove a goal. /// /// It consists of both the `source`, which describes how that goal would be proven, @@ -262,6 +266,11 @@ where goal: Goal, ) -> Result, NoSolution>; + fn consider_builtin_bikeshed_guaranteed_no_drop_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolution>; + /// Consider (possibly several) candidates to upcast or unsize a type to another /// type, excluding the coercion of a sized type into a `dyn Trait`. /// @@ -287,25 +296,6 @@ where let Ok(normalized_self_ty) = self.structurally_normalize_ty(goal.param_env, goal.predicate.self_ty()) else { - // FIXME: We register a fake candidate when normalization fails so that - // we can point at the reason for *why*. I'm tempted to say that this - // is the wrong way to do this, though. - let result = - self.probe(|&result| inspect::ProbeKind::RigidAlias { result }).enter(|this| { - let normalized_ty = this.next_ty_infer(); - let alias_relate_goal = Goal::new( - this.cx(), - goal.param_env, - ty::PredicateKind::AliasRelate( - goal.predicate.self_ty().into(), - normalized_ty.into(), - ty::AliasRelationDirection::Equate, - ), - ); - this.add_goal(GoalSource::AliasWellFormed, alias_relate_goal); - this.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) - }); - assert_eq!(result, Err(NoSolution)); return vec![]; }; @@ -473,6 +463,9 @@ where Some(TraitSolverLangItem::TransmuteTrait) => { G::consider_builtin_transmute_candidate(self, goal) } + Some(TraitSolverLangItem::BikeshedGuaranteedNoDrop) => { + G::consider_builtin_bikeshed_guaranteed_no_drop_candidate(self, goal) + } _ => Err(NoSolution), } }; @@ -510,7 +503,12 @@ where candidates: &mut Vec>, ) { let () = self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| { - ecx.assemble_alias_bound_candidates_recur(goal.predicate.self_ty(), goal, candidates); + ecx.assemble_alias_bound_candidates_recur( + goal.predicate.self_ty(), + goal, + candidates, + AliasBoundKind::SelfBounds, + ); }); } @@ -528,6 +526,7 @@ where self_ty: I::Ty, goal: Goal, candidates: &mut Vec>, + consider_self_bounds: AliasBoundKind, ) { let (kind, alias_ty) = match self_ty.kind() { ty::Bool @@ -580,16 +579,37 @@ where } }; - for assumption in - self.cx().item_bounds(alias_ty.def_id).iter_instantiated(self.cx(), alias_ty.args) - { - candidates.extend(G::probe_and_consider_implied_clause( - self, - CandidateSource::AliasBound, - goal, - assumption, - [], - )); + match consider_self_bounds { + AliasBoundKind::SelfBounds => { + for assumption in self + .cx() + .item_self_bounds(alias_ty.def_id) + .iter_instantiated(self.cx(), alias_ty.args) + { + candidates.extend(G::probe_and_consider_implied_clause( + self, + CandidateSource::AliasBound, + goal, + assumption, + [], + )); + } + } + AliasBoundKind::NonSelfBounds => { + for assumption in self + .cx() + .item_non_self_bounds(alias_ty.def_id) + .iter_instantiated(self.cx(), alias_ty.args) + { + candidates.extend(G::probe_and_consider_implied_clause( + self, + CandidateSource::AliasBound, + goal, + assumption, + [], + )); + } + } } candidates.extend(G::consider_additional_alias_assumptions(self, goal, alias_ty)); @@ -600,9 +620,12 @@ where // Recurse on the self type of the projection. match self.structurally_normalize_ty(goal.param_env, alias_ty.self_ty()) { - Ok(next_self_ty) => { - self.assemble_alias_bound_candidates_recur(next_self_ty, goal, candidates) - } + Ok(next_self_ty) => self.assemble_alias_bound_candidates_recur( + next_self_ty, + goal, + candidates, + AliasBoundKind::NonSelfBounds, + ), Err(NoSolution) => {} } } @@ -754,11 +777,12 @@ where /// treat the alias as rigid. /// /// See trait-system-refactor-initiative#124 for more details. - #[instrument(level = "debug", skip(self), ret)] + #[instrument(level = "debug", skip(self, inject_normalize_to_rigid_candidate), ret)] pub(super) fn merge_candidates( &mut self, proven_via: Option, candidates: Vec>, + inject_normalize_to_rigid_candidate: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult, ) -> QueryResult { let Some(proven_via) = proven_via else { // We don't care about overflow. If proving the trait goal overflowed, then @@ -775,13 +799,27 @@ where // FIXME(const_trait_impl): should this behavior also be used by // constness checking. Doing so is *at least theoretically* breaking, // see github.com/rust-lang/rust/issues/133044#issuecomment-2500709754 - TraitGoalProvenVia::ParamEnv | TraitGoalProvenVia::AliasBound => candidates - .iter() - .filter(|c| { - matches!(c.source, CandidateSource::AliasBound | CandidateSource::ParamEnv(_)) - }) - .map(|c| c.result) - .collect(), + TraitGoalProvenVia::ParamEnv | TraitGoalProvenVia::AliasBound => { + let mut candidates_from_env: Vec<_> = candidates + .iter() + .filter(|c| { + matches!( + c.source, + CandidateSource::AliasBound | CandidateSource::ParamEnv(_) + ) + }) + .map(|c| c.result) + .collect(); + + // If the trait goal has been proven by using the environment, we want to treat + // aliases as rigid if there are no applicable projection bounds in the environment. + if candidates_from_env.is_empty() { + if let Ok(response) = inject_normalize_to_rigid_candidate(self) { + candidates_from_env.push(response); + } + } + candidates_from_env + } TraitGoalProvenVia::Misc => candidates.iter().map(|c| c.result).collect(), }; diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 7da4f5e010754..082c356cc5fbd 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -514,10 +514,11 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable( sig: ty::CoroutineClosureSignature, ) -> I::Ty { let upvars_projection_def_id = cx.require_lang_item(TraitSolverLangItem::AsyncFnKindUpvars); - let tupled_upvars_ty = Ty::new_projection(cx, upvars_projection_def_id, [ - I::GenericArg::from(args.kind_ty()), - Ty::from_closure_kind(cx, goal_kind).into(), - goal_region.into(), - sig.tupled_inputs_ty.into(), - args.tupled_upvars_ty().into(), - args.coroutine_captures_by_ref_ty().into(), - ]); + let tupled_upvars_ty = Ty::new_projection( + cx, + upvars_projection_def_id, + [ + I::GenericArg::from(args.kind_ty()), + Ty::from_closure_kind(cx, goal_kind).into(), + goal_region.into(), + sig.tupled_inputs_ty.into(), + args.tupled_upvars_ty().into(), + args.coroutine_captures_by_ref_ty().into(), + ], + ); sig.to_coroutine( cx, args.parent_args(), @@ -712,6 +717,8 @@ pub(in crate::solve) fn extract_fn_def_from_const_callable( } } +// NOTE: Keep this in sync with `evaluate_host_effect_for_destruct_goal` in +// the old solver, for as long as that exists. pub(in crate::solve) fn const_conditions_for_destruct( cx: I, self_ty: I::Ty, diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index ce7552e30f0fe..0b61c368d8e8d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -103,7 +103,7 @@ where |ecx| { // Const conditions must hold for the implied const bound to hold. ecx.add_goals( - GoalSource::Misc, + GoalSource::AliasBoundConstCondition, cx.const_conditions(alias_ty.def_id) .iter_instantiated(cx, alias_ty.args) .map(|trait_ref| { @@ -248,10 +248,11 @@ where let pred = inputs_and_output .map_bound(|(inputs, _)| { - ty::TraitRef::new(cx, goal.predicate.def_id(), [ - goal.predicate.self_ty(), - Ty::new_tup(cx, inputs.as_slice()), - ]) + ty::TraitRef::new( + cx, + goal.predicate.def_id(), + [goal.predicate.self_ty(), Ty::new_tup(cx, inputs.as_slice())], + ) }) .to_host_effect_clause(cx, goal.predicate.constness); @@ -353,7 +354,7 @@ where ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { ecx.add_goals( - GoalSource::Misc, + GoalSource::AliasBoundConstCondition, const_conditions.into_iter().map(|trait_ref| { goal.with( cx, @@ -373,6 +374,13 @@ where unreachable!("TransmuteFrom is not const") } + fn consider_builtin_bikeshed_guaranteed_no_drop_candidate( + _ecx: &mut EvalCtxt<'_, D>, + _goal: Goal, + ) -> Result, NoSolution> { + unreachable!("BikeshedGuaranteedNoDrop is not const"); + } + fn consider_structural_builtin_unsize_candidates( _ecx: &mut EvalCtxt<'_, D>, _goal: Goal, @@ -397,6 +405,6 @@ where goal.with(ecx.cx(), goal.predicate.trait_ref); ecx.compute_trait_goal(trait_goal) })?; - self.merge_candidates(proven_via, candidates) + self.merge_candidates(proven_via, candidates, |_ecx| Err(NoSolution)) } } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index e99cd3d272700..2491f09a0e288 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -60,13 +60,16 @@ where (goal, opaque_types).fold_with(&mut EagerResolver::new(self.delegate)); let mut orig_values = Default::default(); - let canonical = - Canonicalizer::canonicalize_input(self.delegate, &mut orig_values, QueryInput { + let canonical = Canonicalizer::canonicalize_input( + self.delegate, + &mut orig_values, + QueryInput { goal, predefined_opaques_in_body: self .cx() .mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }), - }); + }, + ); let query_input = ty::CanonicalQueryInput { canonical, typing_mode: self.typing_mode() }; (orig_values, query_input) } @@ -142,7 +145,7 @@ where // Remove any trivial region constraints once we've resolved regions external_constraints .region_constraints - .retain(|outlives| outlives.0.as_region().map_or(true, |re| re != outlives.1)); + .retain(|outlives| outlives.0.as_region().is_none_or(|re| re != outlives.1)); let canonical = Canonicalizer::canonicalize_response( self.delegate, @@ -255,20 +258,29 @@ where self.delegate, &original_values, &response, + self.origin_span, ); let Response { var_values, external_constraints, certainty } = self.delegate.instantiate_canonical(response, instantiation); - Self::unify_query_var_values(self.delegate, param_env, &original_values, var_values); + Self::unify_query_var_values( + self.delegate, + param_env, + &original_values, + var_values, + self.origin_span, + ); let ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals, } = &*external_constraints; + self.register_region_constraints(region_constraints); self.register_new_opaque_types(opaque_types); + (normalization_nested_goals.clone(), certainty) } @@ -279,6 +291,7 @@ where delegate: &D, original_values: &[I::GenericArg], response: &Canonical, + span: I::Span, ) -> CanonicalVarValues { // FIXME: Longterm canonical queries should deal with all placeholders // created inside of the query directly instead of returning them to the @@ -331,7 +344,7 @@ where // A variable from inside a binder of the query. While ideally these shouldn't // exist at all (see the FIXME at the start of this method), we have to deal with // them for now. - delegate.instantiate_canonical_var_with_infer(info, |idx| { + delegate.instantiate_canonical_var_with_infer(info, span, |idx| { ty::UniverseIndex::from(prev_universe.index() + idx.index()) }) } else if info.is_existential() { @@ -345,7 +358,7 @@ where if let Some(v) = opt_values[ty::BoundVar::from_usize(index)] { v } else { - delegate.instantiate_canonical_var_with_infer(info, |_| prev_universe) + delegate.instantiate_canonical_var_with_infer(info, span, |_| prev_universe) } } else { // For placeholders which were already part of the input, we simply map this @@ -376,12 +389,13 @@ where param_env: I::ParamEnv, original_values: &[I::GenericArg], var_values: CanonicalVarValues, + span: I::Span, ) { assert_eq!(original_values.len(), var_values.len()); for (&orig, response) in iter::zip(original_values, var_values.var_values.iter()) { let goals = - delegate.eq_structurally_relating_aliases(param_env, orig, response).unwrap(); + delegate.eq_structurally_relating_aliases(param_env, orig, response, span).unwrap(); assert!(goals.is_empty()); } } @@ -401,7 +415,7 @@ where fn register_new_opaque_types(&mut self, opaque_types: &[(ty::OpaqueTypeKey, I::Ty)]) { for &(key, ty) in opaque_types { - self.delegate.inject_new_hidden_type_unchecked(key, ty); + self.delegate.inject_new_hidden_type_unchecked(key, ty, self.origin_span); } } } @@ -431,7 +445,7 @@ where // `rustc_trait_selection::solve::inspect::analyse`. pub fn instantiate_canonical_state>( delegate: &D, - span: D::Span, + span: I::Span, param_env: I::ParamEnv, orig_values: &mut Vec, state: inspect::CanonicalState, @@ -442,19 +456,17 @@ where { // In case any fresh inference variables have been created between `state` // and the previous instantiation, extend `orig_values` for it. - assert!(orig_values.len() <= state.value.var_values.len()); - for &arg in &state.value.var_values.var_values.as_slice() - [orig_values.len()..state.value.var_values.len()] - { - let unconstrained = delegate.fresh_var_for_kind_with_span(arg, span); - orig_values.push(unconstrained); - } + orig_values.extend( + state.value.var_values.var_values.as_slice()[orig_values.len()..] + .iter() + .map(|&arg| delegate.fresh_var_for_kind_with_span(arg, span)), + ); let instantiation = - EvalCtxt::compute_query_response_instantiation_values(delegate, orig_values, &state); + EvalCtxt::compute_query_response_instantiation_values(delegate, orig_values, &state, span); let inspect::State { var_values, data } = delegate.instantiate_canonical(state, instantiation); - EvalCtxt::unify_query_var_values(delegate, param_env, orig_values, var_values); + EvalCtxt::unify_query_var_values(delegate, param_env, orig_values, var_values, span); data } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 8c74490e0e0ee..b0a34b9ce7560 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -78,6 +78,8 @@ where nested_goals: NestedGoals, + pub(super) origin_span: I::Span, + // Has this `EvalCtxt` errored out with `NoSolution` in `try_evaluate_added_goals`? // // If so, then it can no longer be used to make a canonical query response, @@ -134,6 +136,7 @@ pub trait SolverDelegateEvalExt: SolverDelegate { &self, goal: Goal::Predicate>, generate_proof_tree: GenerateProofTree, + span: ::Span, ) -> ( Result<(HasChanged, Certainty), NoSolution>, Option>, @@ -174,8 +177,9 @@ where &self, goal: Goal, generate_proof_tree: GenerateProofTree, + span: I::Span, ) -> (Result<(HasChanged, Certainty), NoSolution>, Option>) { - EvalCtxt::enter_root(self, self.cx().recursion_limit(), generate_proof_tree, |ecx| { + EvalCtxt::enter_root(self, self.cx().recursion_limit(), generate_proof_tree, span, |ecx| { ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal) }) } @@ -186,7 +190,7 @@ where goal: Goal::Predicate>, ) -> bool { self.probe(|| { - EvalCtxt::enter_root(self, root_depth, GenerateProofTree::No, |ecx| { + EvalCtxt::enter_root(self, root_depth, GenerateProofTree::No, I::Span::dummy(), |ecx| { ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal) }) .0 @@ -203,9 +207,13 @@ where Result<(NestedNormalizationGoals, HasChanged, Certainty), NoSolution>, Option>, ) { - EvalCtxt::enter_root(self, self.cx().recursion_limit(), generate_proof_tree, |ecx| { - ecx.evaluate_goal_raw(GoalEvaluationKind::Root, GoalSource::Misc, goal) - }) + EvalCtxt::enter_root( + self, + self.cx().recursion_limit(), + generate_proof_tree, + I::Span::dummy(), + |ecx| ecx.evaluate_goal_raw(GoalEvaluationKind::Root, GoalSource::Misc, goal), + ) } } @@ -229,6 +237,7 @@ where delegate: &D, root_depth: usize, generate_proof_tree: GenerateProofTree, + origin_span: I::Span, f: impl FnOnce(&mut EvalCtxt<'_, D>) -> R, ) -> (R, Option>) { let mut search_graph = SearchGraph::new(root_depth); @@ -248,6 +257,7 @@ where variables: Default::default(), var_values: CanonicalVarValues::dummy(), is_normalizes_to_goal: false, + origin_span, tainted: Ok(()), }; let result = f(&mut ecx); @@ -289,12 +299,13 @@ where max_input_universe: canonical_input.canonical.max_universe, search_graph, nested_goals: NestedGoals::new(), + origin_span: I::Span::dummy(), tainted: Ok(()), inspect: canonical_goal_evaluation.new_goal_evaluation_step(var_values), }; for &(key, ty) in &input.predefined_opaques_in_body.opaque_types { - ecx.delegate.inject_new_hidden_type_unchecked(key, ty); + ecx.delegate.inject_new_hidden_type_unchecked(key, ty, ecx.origin_span); } if !ecx.nested_goals.is_empty() { @@ -537,10 +548,10 @@ where // Replace the goal with an unconstrained infer var, so the // RHS does not affect projection candidate assembly. let unconstrained_rhs = self.next_term_infer_of_kind(goal.predicate.term); - let unconstrained_goal = goal.with(cx, ty::NormalizesTo { - alias: goal.predicate.alias, - term: unconstrained_rhs, - }); + let unconstrained_goal = goal.with( + cx, + ty::NormalizesTo { alias: goal.predicate.alias, term: unconstrained_rhs }, + ); let (NestedNormalizationGoals(nested_goals), _, certainty) = self.evaluate_goal_raw( GoalEvaluationKind::Nested, @@ -822,8 +833,12 @@ where let identity_args = self.fresh_args_for_item(alias.def_id); let rigid_ctor = ty::AliasTerm::new_from_args(cx, alias.def_id, identity_args); let ctor_term = rigid_ctor.to_term(cx); - let obligations = - self.delegate.eq_structurally_relating_aliases(param_env, term, ctor_term)?; + let obligations = self.delegate.eq_structurally_relating_aliases( + param_env, + term, + ctor_term, + self.origin_span, + )?; debug_assert!(obligations.is_empty()); self.relate(param_env, alias, variance, rigid_ctor) } else { @@ -841,7 +856,12 @@ where lhs: T, rhs: T, ) -> Result<(), NoSolution> { - let result = self.delegate.eq_structurally_relating_aliases(param_env, lhs, rhs)?; + let result = self.delegate.eq_structurally_relating_aliases( + param_env, + lhs, + rhs, + self.origin_span, + )?; assert_eq!(result, vec![]); Ok(()) } @@ -864,7 +884,7 @@ where variance: ty::Variance, rhs: T, ) -> Result<(), NoSolution> { - let goals = self.delegate.relate(param_env, lhs, variance, rhs)?; + let goals = self.delegate.relate(param_env, lhs, variance, rhs, self.origin_span)?; self.add_goals(GoalSource::Misc, goals); Ok(()) } @@ -881,7 +901,7 @@ where lhs: T, rhs: T, ) -> Result>, NoSolution> { - Ok(self.delegate.relate(param_env, lhs, ty::Variance::Invariant, rhs)?) + Ok(self.delegate.relate(param_env, lhs, ty::Variance::Invariant, rhs, self.origin_span)?) } pub(super) fn instantiate_binder_with_infer + Copy>( @@ -917,12 +937,12 @@ where } pub(super) fn register_ty_outlives(&self, ty: I::Ty, lt: I::Region) { - self.delegate.register_ty_outlives(ty, lt); + self.delegate.register_ty_outlives(ty, lt, self.origin_span); } pub(super) fn register_region_outlives(&self, a: I::Region, b: I::Region) { // `b : a` ==> `a <= b` - self.delegate.sub_regions(b, a); + self.delegate.sub_regions(b, a, self.origin_span); } /// Computes the list of goals required for `arg` to be well-formed @@ -950,7 +970,7 @@ where goal_trait_ref: ty::TraitRef, trait_assoc_def_id: I::DefId, impl_def_id: I::DefId, - ) -> Result, NoSolution> { + ) -> Result, I::ErrorGuaranteed> { self.delegate.fetch_eligible_assoc_item(goal_trait_ref, trait_assoc_def_id, impl_def_id) } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs index be69a6e84ea13..add96a1fdf7a0 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs @@ -39,6 +39,7 @@ where max_input_universe, search_graph: outer_ecx.search_graph, nested_goals: outer_ecx.nested_goals.clone(), + origin_span: outer_ecx.origin_span, tainted: outer_ecx.tainted, inspect: outer_ecx.inspect.take_and_enter_probe(), }; diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 37678bfd8805f..1fa35b60304cf 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -160,9 +160,7 @@ where ty::ConstKind::Infer(_) => { self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } - ty::ConstKind::Placeholder(_) - | ty::ConstKind::Value(_, _) - | ty::ConstKind::Error(_) => { + ty::ConstKind::Placeholder(_) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) => { self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } // We can freely ICE here as: @@ -199,7 +197,7 @@ where unreachable!("`ConstKind::Param` should have been canonicalized to `Placeholder`") } ty::ConstKind::Bound(_, _) => panic!("escaping bound vars in {:?}", ct), - ty::ConstKind::Value(ty, _) => ty, + ty::ConstKind::Value(cv) => cv.ty(), ty::ConstKind::Placeholder(placeholder) => { self.cx().find_const_ty_from_env(goal.param_env, placeholder) } @@ -277,23 +275,7 @@ where param_env: I::ParamEnv, ty: I::Ty, ) -> Result { - if let ty::Alias(..) = ty.kind() { - let normalized_ty = self.next_ty_infer(); - let alias_relate_goal = Goal::new( - self.cx(), - param_env, - ty::PredicateKind::AliasRelate( - ty.into(), - normalized_ty.into(), - ty::AliasRelationDirection::Equate, - ), - ); - self.add_goal(GoalSource::Misc, alias_relate_goal); - self.try_evaluate_added_goals()?; - Ok(self.resolve_vars_if_possible(normalized_ty)) - } else { - Ok(ty) - } + self.structurally_normalize_term(param_env, ty.into()).map(|term| term.expect_ty()) } /// Normalize a const for when it is structurally matched on, or more likely @@ -308,22 +290,34 @@ where param_env: I::ParamEnv, ct: I::Const, ) -> Result { - if let ty::ConstKind::Unevaluated(..) = ct.kind() { - let normalized_ct = self.next_const_infer(); + self.structurally_normalize_term(param_env, ct.into()).map(|term| term.expect_const()) + } + + /// Normalize a term for when it is structurally matched on. + /// + /// This function is necessary in nearly all cases before matching on a ty/const. + /// Not doing so is likely to be incomplete and therefore unsound during coherence. + fn structurally_normalize_term( + &mut self, + param_env: I::ParamEnv, + term: I::Term, + ) -> Result { + if let Some(_) = term.to_alias_term() { + let normalized_term = self.next_term_infer_of_kind(term); let alias_relate_goal = Goal::new( self.cx(), param_env, ty::PredicateKind::AliasRelate( - ct.into(), - normalized_ct.into(), + term, + normalized_term, ty::AliasRelationDirection::Equate, ), ); self.add_goal(GoalSource::Misc, alias_relate_goal); self.try_evaluate_added_goals()?; - Ok(self.resolve_vars_if_possible(normalized_ct)) + Ok(self.resolve_vars_if_possible(normalized_term)) } else { - Ok(ct) + Ok(term) } } diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index f5ecfea540858..88002e1a88a87 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -30,75 +30,26 @@ where ) -> QueryResult { self.set_is_normalizes_to_goal(); debug_assert!(self.term_is_fully_unconstrained(goal)); - let normalize_result = self - .probe(|&result| ProbeKind::TryNormalizeNonRigid { result }) - .enter(|this| this.normalize_at_least_one_step(goal)); - - match normalize_result { - Ok(res) => Ok(res), - Err(NoSolution) => { - self.probe(|&result| ProbeKind::RigidAlias { result }).enter(|this| { - let Goal { param_env, predicate: NormalizesTo { alias, term } } = goal; - this.add_rigid_constraints(param_env, alias)?; - this.relate_rigid_alias_non_alias(param_env, alias, ty::Invariant, term)?; - this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - } - } - } - - /// Register any obligations that are used to validate that an alias should be - /// treated as rigid. - /// - /// An alias may be considered rigid if it fails normalization, but we also don't - /// want to consider aliases that are not well-formed to be rigid simply because - /// they fail normalization. - /// - /// For example, some `::Assoc` where `T: Trait` does not hold, or an - /// opaque type whose hidden type doesn't actually satisfy the opaque item bounds. - fn add_rigid_constraints( - &mut self, - param_env: I::ParamEnv, - rigid_alias: ty::AliasTerm, - ) -> Result<(), NoSolution> { - let cx = self.cx(); - match rigid_alias.kind(cx) { - // Projections are rigid only if their trait ref holds, - // and the GAT where-clauses hold. - ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => { - let trait_ref = rigid_alias.trait_ref(cx); - self.add_goal(GoalSource::AliasWellFormed, Goal::new(cx, param_env, trait_ref)); - Ok(()) - } - ty::AliasTermKind::OpaqueTy => { - if self.opaque_type_is_rigid(rigid_alias.def_id) { - Ok(()) - } else { - Err(NoSolution) - } - } - // FIXME(generic_const_exprs): we would need to support generic consts here - ty::AliasTermKind::UnevaluatedConst => Err(NoSolution), - // Inherent and weak types are never rigid. This type must not be well-formed. - ty::AliasTermKind::WeakTy | ty::AliasTermKind::InherentTy => Err(NoSolution), - } - } - - /// Normalize the given alias by at least one step. If the alias is rigid, this - /// returns `NoSolution`. - #[instrument(level = "trace", skip(self), ret)] - fn normalize_at_least_one_step(&mut self, goal: Goal>) -> QueryResult { let cx = self.cx(); match goal.predicate.alias.kind(cx) { ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => { let candidates = self.assemble_and_evaluate_candidates(goal); + let trait_ref = goal.predicate.alias.trait_ref(cx); let (_, proven_via) = self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| { - let trait_goal: Goal> = - goal.with(cx, goal.predicate.alias.trait_ref(cx)); + let trait_goal: Goal> = goal.with(cx, trait_ref); ecx.compute_trait_goal(trait_goal) })?; - self.merge_candidates(proven_via, candidates) + self.merge_candidates(proven_via, candidates, |ecx| { + ecx.probe(|&result| ProbeKind::RigidAlias { result }).enter(|this| { + this.structurally_instantiate_normalizes_to_term( + goal, + goal.predicate.alias, + ); + this.add_goal(GoalSource::AliasWellFormed, goal.with(cx, trait_ref)); + this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + }) } ty::AliasTermKind::InherentTy => self.normalize_inherent_associated_type(goal), ty::AliasTermKind::OpaqueTy => self.normalize_opaque_type(goal), @@ -120,6 +71,17 @@ where self.eq(goal.param_env, goal.predicate.term, term) .expect("expected goal term to be fully unconstrained"); } + + /// Unlike `instantiate_normalizes_to_term` this instantiates the expected term + /// with a rigid alias. Using this is pretty much always wrong. + pub fn structurally_instantiate_normalizes_to_term( + &mut self, + goal: Goal>, + term: ty::AliasTerm, + ) { + self.relate_rigid_alias_non_alias(goal.param_env, term, ty::Invariant, goal.predicate.term) + .expect("expected goal term to be fully unconstrained"); + } } impl assembly::GoalKind for NormalizesTo @@ -244,20 +206,7 @@ where .map(|pred| goal.with(cx, pred)), ); - // In case the associated item is hidden due to specialization, we have to - // return ambiguity this would otherwise be incomplete, resulting in - // unsoundness during coherence (#105782). - let Some(target_item_def_id) = ecx.fetch_eligible_assoc_item( - goal_trait_ref, - goal.predicate.def_id(), - impl_def_id, - )? - else { - return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); - }; - - let error_response = |ecx: &mut EvalCtxt<'_, D>, msg: &str| { - let guar = cx.delay_bug(msg); + let error_response = |ecx: &mut EvalCtxt<'_, D>, guar| { let error_term = match goal.predicate.alias.kind(cx) { ty::AliasTermKind::ProjectionTy => Ty::new_error(cx, guar).into(), ty::AliasTermKind::ProjectionConst => Const::new_error(cx, guar).into(), @@ -267,8 +216,24 @@ where ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }; + // In case the associated item is hidden due to specialization, we have to + // return ambiguity this would otherwise be incomplete, resulting in + // unsoundness during coherence (#105782). + let target_item_def_id = match ecx.fetch_eligible_assoc_item( + goal_trait_ref, + goal.predicate.def_id(), + impl_def_id, + ) { + Ok(Some(target_item_def_id)) => target_item_def_id, + Ok(None) => { + return ecx + .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); + } + Err(guar) => return error_response(ecx, guar), + }; + if !cx.has_item_definition(target_item_def_id) { - return error_response(ecx, "missing item"); + return error_response(ecx, cx.delay_bug("missing item")); } let target_container_def_id = cx.parent(target_item_def_id); @@ -292,7 +257,10 @@ where )?; if !cx.check_args_compatible(target_item_def_id, target_args) { - return error_response(ecx, "associated item has mismatched arguments"); + return error_response( + ecx, + cx.delay_bug("associated item has mismatched arguments"), + ); } // Finally we construct the actual value of the associated type. @@ -392,10 +360,11 @@ where let pred = tupled_inputs_and_output .map_bound(|(inputs, output)| ty::ProjectionPredicate { - projection_term: ty::AliasTerm::new(cx, goal.predicate.def_id(), [ - goal.predicate.self_ty(), - inputs, - ]), + projection_term: ty::AliasTerm::new( + cx, + goal.predicate.def_id(), + [goal.predicate.self_ty(), inputs], + ), term: output.into(), }) .upcast(cx); @@ -448,21 +417,26 @@ where .is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CallOnceFuture) { ( - ty::AliasTerm::new(cx, goal.predicate.def_id(), [ - goal.predicate.self_ty(), - tupled_inputs_ty, - ]), + ty::AliasTerm::new( + cx, + goal.predicate.def_id(), + [goal.predicate.self_ty(), tupled_inputs_ty], + ), output_coroutine_ty.into(), ) } else if cx .is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CallRefFuture) { ( - ty::AliasTerm::new(cx, goal.predicate.def_id(), [ - I::GenericArg::from(goal.predicate.self_ty()), - tupled_inputs_ty.into(), - env_region.into(), - ]), + ty::AliasTerm::new( + cx, + goal.predicate.def_id(), + [ + I::GenericArg::from(goal.predicate.self_ty()), + tupled_inputs_ty.into(), + env_region.into(), + ], + ), output_coroutine_ty.into(), ) } else if cx.is_lang_item( @@ -470,10 +444,14 @@ where TraitSolverLangItem::AsyncFnOnceOutput, ) { ( - ty::AliasTerm::new(cx, goal.predicate.def_id(), [ - I::GenericArg::from(goal.predicate.self_ty()), - tupled_inputs_ty.into(), - ]), + ty::AliasTerm::new( + cx, + goal.predicate.def_id(), + [ + I::GenericArg::from(goal.predicate.self_ty()), + tupled_inputs_ty.into(), + ], + ), coroutine_return_ty.into(), ) } else { @@ -560,79 +538,92 @@ where let cx = ecx.cx(); let metadata_def_id = cx.require_lang_item(TraitSolverLangItem::Metadata); assert_eq!(metadata_def_id, goal.predicate.def_id()); - ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { - let metadata_ty = match goal.predicate.self_ty().kind() { - ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::Array(..) - | ty::Pat(..) - | ty::RawPtr(..) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Infer(ty::IntVar(..) | ty::FloatVar(..)) - | ty::Coroutine(..) - | ty::CoroutineWitness(..) - | ty::Never - | ty::Foreign(..) - | ty::Dynamic(_, _, ty::DynStar) => Ty::new_unit(cx), - - ty::Error(e) => Ty::new_error(cx, e), - - ty::Str | ty::Slice(_) => Ty::new_usize(cx), - - ty::Dynamic(_, _, ty::Dyn) => { - let dyn_metadata = cx.require_lang_item(TraitSolverLangItem::DynMetadata); - cx.type_of(dyn_metadata) - .instantiate(cx, &[I::GenericArg::from(goal.predicate.self_ty())]) - } + let metadata_ty = match goal.predicate.self_ty().kind() { + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Array(..) + | ty::Pat(..) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Infer(ty::IntVar(..) | ty::FloatVar(..)) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Foreign(..) + | ty::Dynamic(_, _, ty::DynStar) => Ty::new_unit(cx), - ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => { - // This is the "fallback impl" for type parameters, unnormalizable projections - // and opaque types: If the `self_ty` is `Sized`, then the metadata is `()`. - // FIXME(ptr_metadata): This impl overlaps with the other impls and shouldn't - // exist. Instead, `Pointee` should be a supertrait of `Sized`. - let sized_predicate = - ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Sized), [ - I::GenericArg::from(goal.predicate.self_ty()), - ]); - // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`? - ecx.add_goal(GoalSource::Misc, goal.with(cx, sized_predicate)); - Ty::new_unit(cx) - } + ty::Error(e) => Ty::new_error(cx, e), - ty::Adt(def, args) if def.is_struct() => match def.struct_tail_ty(cx) { - None => Ty::new_unit(cx), - Some(tail_ty) => { - Ty::new_projection(cx, metadata_def_id, [tail_ty.instantiate(cx, args)]) - } - }, - ty::Adt(_, _) => Ty::new_unit(cx), + ty::Str | ty::Slice(_) => Ty::new_usize(cx), - ty::Tuple(elements) => match elements.last() { - None => Ty::new_unit(cx), - Some(tail_ty) => Ty::new_projection(cx, metadata_def_id, [tail_ty]), - }, + ty::Dynamic(_, _, ty::Dyn) => { + let dyn_metadata = cx.require_lang_item(TraitSolverLangItem::DynMetadata); + cx.type_of(dyn_metadata) + .instantiate(cx, &[I::GenericArg::from(goal.predicate.self_ty())]) + } + + ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => { + // This is the "fallback impl" for type parameters, unnormalizable projections + // and opaque types: If the `self_ty` is `Sized`, then the metadata is `()`. + // FIXME(ptr_metadata): This impl overlaps with the other impls and shouldn't + // exist. Instead, `Pointee` should be a supertrait of `Sized`. + let alias_bound_result = + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + let sized_predicate = ty::TraitRef::new( + cx, + cx.require_lang_item(TraitSolverLangItem::Sized), + [I::GenericArg::from(goal.predicate.self_ty())], + ); + ecx.add_goal(GoalSource::Misc, goal.with(cx, sized_predicate)); + ecx.instantiate_normalizes_to_term(goal, Ty::new_unit(cx).into()); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }); + // In case the dummy alias-bound candidate does not apply, we instead treat this projection + // as rigid. + return alias_bound_result.or_else(|NoSolution| { + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|this| { + this.structurally_instantiate_normalizes_to_term( + goal, + goal.predicate.alias, + ); + this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + }); + } - ty::UnsafeBinder(_) => { - // FIXME(unsafe_binder): Figure out how to handle pointee for unsafe binders. - todo!() + ty::Adt(def, args) if def.is_struct() => match def.struct_tail_ty(cx) { + None => Ty::new_unit(cx), + Some(tail_ty) => { + Ty::new_projection(cx, metadata_def_id, [tail_ty.instantiate(cx, args)]) } + }, + ty::Adt(_, _) => Ty::new_unit(cx), - ty::Infer( - ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_), - ) - | ty::Bound(..) => panic!( - "unexpected self ty `{:?}` when normalizing `::Metadata`", - goal.predicate.self_ty() - ), - }; + ty::Tuple(elements) => match elements.last() { + None => Ty::new_unit(cx), + Some(tail_ty) => Ty::new_projection(cx, metadata_def_id, [tail_ty]), + }, + + ty::UnsafeBinder(_) => { + // FIXME(unsafe_binder): Figure out how to handle pointee for unsafe binders. + todo!() + } + + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) + | ty::Bound(..) => panic!( + "unexpected self ty `{:?}` when normalizing `::Metadata`", + goal.predicate.self_ty() + ), + }; + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { ecx.instantiate_normalizes_to_term(goal, metadata_ty.into()); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) @@ -776,10 +767,11 @@ where CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), goal, ty::ProjectionPredicate { - projection_term: ty::AliasTerm::new(ecx.cx(), goal.predicate.def_id(), [ - self_ty, - coroutine.resume_ty(), - ]), + projection_term: ty::AliasTerm::new( + ecx.cx(), + goal.predicate.def_id(), + [self_ty, coroutine.resume_ty()], + ), term, } .upcast(cx), @@ -832,12 +824,14 @@ where todo!("discr subgoal...") } - // We do not call `Ty::discriminant_ty` on alias, param, or placeholder - // types, which return `::Discriminant` - // (or ICE in the case of placeholders). Projecting a type to itself - // is never really productive. + // Given an alias, parameter, or placeholder we add an impl candidate normalizing to a rigid + // alias. In case there's a where-bound further constraining this alias it is preferred over + // this impl candidate anyways. It's still a bit scuffed. ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => { - return Err(NoSolution); + return ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }); } ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) @@ -884,12 +878,14 @@ where todo!() } - // We do not call `Ty::async_destructor_ty` on alias, param, or placeholder - // types, which return `::AsyncDestructor` - // (or ICE in the case of placeholders). Projecting a type to itself - // is never really productive. + // Given an alias, parameter, or placeholder we add an impl candidate normalizing to a rigid + // alias. In case there's a where-bound further constraining this alias it is preferred over + // this impl candidate anyways. It's still a bit scuffed. ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => { - return Err(NoSolution); + return ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }); } ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) @@ -924,6 +920,13 @@ where ) -> Result, NoSolution> { panic!("`TransmuteFrom` does not have an associated type: {:?}", goal) } + + fn consider_builtin_bikeshed_guaranteed_no_drop_candidate( + _ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolution> { + unreachable!("`BikeshedGuaranteedNoDrop` does not have an associated type: {:?}", goal) + } } impl EvalCtxt<'_, D> diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs index 26a8a22d77ebe..60c20762a30ab 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs @@ -35,14 +35,15 @@ where self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } TypingMode::Analysis { defining_opaque_types } => { - let Some(def_id) = opaque_ty.def_id.as_local() else { - return Err(NoSolution); + let Some(def_id) = opaque_ty + .def_id + .as_local() + .filter(|&def_id| defining_opaque_types.contains(&def_id)) + else { + self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); + return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); }; - if !defining_opaque_types.contains(&def_id) { - return Err(NoSolution); - } - // FIXME: This may have issues when the args contain aliases... match uses_unique_placeholders_ignoring_regions(self.cx(), opaque_ty.args) { Err(NotUniqueParam::NotParam(param)) if param.is_non_region_infer() => { @@ -97,15 +98,16 @@ where self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } TypingMode::PostBorrowckAnalysis { defined_opaque_types } => { - let Some(def_id) = opaque_ty.def_id.as_local() else { - return Err(NoSolution); + let Some(def_id) = opaque_ty + .def_id + .as_local() + .filter(|&def_id| defined_opaque_types.contains(&def_id)) + else { + self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); + return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); }; - if !defined_opaque_types.contains(&def_id) { - return Err(NoSolution); - } - - let actual = cx.type_of(opaque_ty.def_id).instantiate(cx, opaque_ty.args); + let actual = cx.type_of(def_id.into()).instantiate(cx, opaque_ty.args); // FIXME: Actually use a proper binder here instead of relying on `ReErased`. // // This is also probably unsound or sth :shrug: diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index d68fca608299a..dabfa5cc04c8d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -225,7 +225,7 @@ where } ecx.probe_and_evaluate_goal_for_constituent_tys( - CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), + CandidateSource::BuiltinImpl(BuiltinImplSource::Trivial), goal, structural_traits::instantiate_constituent_tys_for_sized_trait, ) @@ -342,18 +342,21 @@ where // (FIXME: technically we only need to check this if the type is a fn ptr...) let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound( |AsyncCallableRelevantTypes { output_coroutine_ty, .. }| { - ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Sized), [ - output_coroutine_ty, - ]) + ty::TraitRef::new( + cx, + cx.require_lang_item(TraitSolverLangItem::Sized), + [output_coroutine_ty], + ) }, ); let pred = tupled_inputs_and_output_and_coroutine .map_bound(|AsyncCallableRelevantTypes { tupled_inputs_ty, .. }| { - ty::TraitRef::new(cx, goal.predicate.def_id(), [ - goal.predicate.self_ty(), - tupled_inputs_ty, - ]) + ty::TraitRef::new( + cx, + goal.predicate.def_id(), + [goal.predicate.self_ty(), tupled_inputs_ty], + ) }) .upcast(cx); Self::probe_and_consider_implied_clause( @@ -622,6 +625,101 @@ where }) } + /// NOTE: This is implemented as a built-in goal and not a set of impls like: + /// + /// ```rust,ignore (illustrative) + /// impl BikeshedGuaranteedNoDrop for T where T: Copy {} + /// impl BikeshedGuaranteedNoDrop for ManuallyDrop {} + /// ``` + /// + /// because these impls overlap, and I'd rather not build a coherence hack for + /// this harmless overlap. + fn consider_builtin_bikeshed_guaranteed_no_drop_candidate( + ecx: &mut EvalCtxt<'_, D>, + goal: Goal, + ) -> Result, NoSolution> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { + return Err(NoSolution); + } + + let cx = ecx.cx(); + ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { + let ty = goal.predicate.self_ty(); + match ty.kind() { + // `&mut T` and `&T` always implement `BikeshedGuaranteedNoDrop`. + ty::Ref(..) => {} + // `ManuallyDrop` always implements `BikeshedGuaranteedNoDrop`. + ty::Adt(def, _) if def.is_manually_drop() => {} + // Arrays and tuples implement `BikeshedGuaranteedNoDrop` only if + // their constituent types implement `BikeshedGuaranteedNoDrop`. + ty::Tuple(tys) => { + ecx.add_goals( + GoalSource::ImplWhereBound, + tys.iter().map(|elem_ty| { + goal.with(cx, ty::TraitRef::new(cx, goal.predicate.def_id(), [elem_ty])) + }), + ); + } + ty::Array(elem_ty, _) => { + ecx.add_goal( + GoalSource::ImplWhereBound, + goal.with(cx, ty::TraitRef::new(cx, goal.predicate.def_id(), [elem_ty])), + ); + } + + // All other types implement `BikeshedGuaranteedNoDrop` only if + // they implement `Copy`. We could be smart here and short-circuit + // some trivially `Copy`/`!Copy` types, but there's no benefit. + ty::FnDef(..) + | ty::FnPtr(..) + | ty::Error(_) + | ty::Uint(_) + | ty::Int(_) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Pat(..) + | ty::Dynamic(..) + | ty::Str + | ty::Slice(_) + | ty::Foreign(..) + | ty::Adt(..) + | ty::Alias(..) + | ty::Param(_) + | ty::Placeholder(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::UnsafeBinder(_) + | ty::CoroutineWitness(..) => { + ecx.add_goal( + GoalSource::ImplWhereBound, + goal.with( + cx, + ty::TraitRef::new( + cx, + cx.require_lang_item(TraitSolverLangItem::Copy), + [ty], + ), + ), + ); + } + + ty::Bound(..) + | ty::Infer( + ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_), + ) => { + panic!("unexpected type `{ty:?}`") + } + } + + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + /// ```ignore (builtin impl example) /// trait Trait { /// fn foo(&self); @@ -741,12 +839,14 @@ where a_data.principal(), )); } else if let Some(a_principal) = a_data.principal() { - for new_a_principal in - elaborate::supertraits(self.cx(), a_principal.with_self_ty(cx, a_ty)).skip(1) + for (idx, new_a_principal) in + elaborate::supertraits(self.cx(), a_principal.with_self_ty(cx, a_ty)) + .enumerate() + .skip(1) { responses.extend(self.consider_builtin_upcast_to_principal( goal, - CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting), + CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting(idx)), a_data, a_region, b_data, @@ -973,9 +1073,11 @@ where GoalSource::ImplWhereBound, goal.with( cx, - ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Unsize), [ - a_tail_ty, b_tail_ty, - ]), + ty::TraitRef::new( + cx, + cx.require_lang_item(TraitSolverLangItem::Unsize), + [a_tail_ty, b_tail_ty], + ), ), ); self.probe_builtin_trait_candidate(BuiltinImplSource::Misc) @@ -1013,9 +1115,11 @@ where GoalSource::ImplWhereBound, goal.with( cx, - ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Unsize), [ - a_last_ty, b_last_ty, - ]), + ty::TraitRef::new( + cx, + cx.require_lang_item(TraitSolverLangItem::Unsize), + [a_last_ty, b_last_ty], + ), ), ); self.probe_builtin_trait_candidate(BuiltinImplSource::TupleUnsizing) @@ -1192,14 +1296,46 @@ where }; } - // FIXME: prefer trivial builtin impls + // We prefer trivial builtin candidates, i.e. builtin impls without any + // nested requirements, over all others. This is a fix for #53123 and + // prevents where-bounds from accidentally extending the lifetime of a + // variable. + let mut trivial_builtin_impls = candidates.iter().filter(|c| { + matches!(c.source, CandidateSource::BuiltinImpl(BuiltinImplSource::Trivial)) + }); + if let Some(candidate) = trivial_builtin_impls.next() { + // There should only ever be a single trivial builtin candidate + // as they would otherwise overlap. + assert!(trivial_builtin_impls.next().is_none()); + return Ok((candidate.result, Some(TraitGoalProvenVia::Misc))); + } // If there are non-global where-bounds, prefer where-bounds // (including global ones) over everything else. let has_non_global_where_bounds = candidates.iter().any(|c| match c.source { CandidateSource::ParamEnv(idx) => { - let where_bound = goal.param_env.caller_bounds().get(idx); - where_bound.has_bound_vars() || !where_bound.is_global() + let where_bound = goal.param_env.caller_bounds().get(idx).unwrap(); + let ty::ClauseKind::Trait(trait_pred) = where_bound.kind().skip_binder() else { + unreachable!("expected trait-bound: {where_bound:?}"); + }; + + if trait_pred.has_bound_vars() || !trait_pred.is_global() { + return true; + } + + // We don't consider a trait-bound global if it has a projection bound. + // + // See ui/traits/next-solver/normalization-shadowing/global-trait-with-project.rs + // for an example where this is necessary. + for p in goal.param_env.caller_bounds().iter() { + if let ty::ClauseKind::Projection(proj) = p.kind().skip_binder() { + if proj.projection_term.trait_ref(self.cx()) == trait_pred.trait_ref { + return true; + } + } + } + + false } _ => false, }); diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index f78d9dc2bfc28..8f0e29c27695b 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1121,23 +1121,29 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for ExpectedIdentifier { let token_descr = TokenDescription::from_token(&self.token); let mut add_token = true; - let mut diag = Diag::new(dcx, level, match token_descr { - Some(TokenDescription::ReservedIdentifier) => { - fluent::parse_expected_identifier_found_reserved_identifier_str - } - Some(TokenDescription::Keyword) => fluent::parse_expected_identifier_found_keyword_str, - Some(TokenDescription::ReservedKeyword) => { - fluent::parse_expected_identifier_found_reserved_keyword_str - } - Some(TokenDescription::DocComment) => { - fluent::parse_expected_identifier_found_doc_comment_str - } - Some(TokenDescription::MetaVar(_)) => { - add_token = false; - fluent::parse_expected_identifier_found_metavar_str - } - None => fluent::parse_expected_identifier_found_str, - }); + let mut diag = Diag::new( + dcx, + level, + match token_descr { + Some(TokenDescription::ReservedIdentifier) => { + fluent::parse_expected_identifier_found_reserved_identifier_str + } + Some(TokenDescription::Keyword) => { + fluent::parse_expected_identifier_found_keyword_str + } + Some(TokenDescription::ReservedKeyword) => { + fluent::parse_expected_identifier_found_reserved_keyword_str + } + Some(TokenDescription::DocComment) => { + fluent::parse_expected_identifier_found_doc_comment_str + } + Some(TokenDescription::MetaVar(_)) => { + add_token = false; + fluent::parse_expected_identifier_found_metavar_str + } + None => fluent::parse_expected_identifier_found_str, + }, + ); diag.span(self.span); if add_token { diag.arg("token", self.token); @@ -1182,21 +1188,27 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for ExpectedSemi { let token_descr = TokenDescription::from_token(&self.token); let mut add_token = true; - let mut diag = Diag::new(dcx, level, match token_descr { - Some(TokenDescription::ReservedIdentifier) => { - fluent::parse_expected_semi_found_reserved_identifier_str - } - Some(TokenDescription::Keyword) => fluent::parse_expected_semi_found_keyword_str, - Some(TokenDescription::ReservedKeyword) => { - fluent::parse_expected_semi_found_reserved_keyword_str - } - Some(TokenDescription::DocComment) => fluent::parse_expected_semi_found_doc_comment_str, - Some(TokenDescription::MetaVar(_)) => { - add_token = false; - fluent::parse_expected_semi_found_metavar_str - } - None => fluent::parse_expected_semi_found_str, - }); + let mut diag = Diag::new( + dcx, + level, + match token_descr { + Some(TokenDescription::ReservedIdentifier) => { + fluent::parse_expected_semi_found_reserved_identifier_str + } + Some(TokenDescription::Keyword) => fluent::parse_expected_semi_found_keyword_str, + Some(TokenDescription::ReservedKeyword) => { + fluent::parse_expected_semi_found_reserved_keyword_str + } + Some(TokenDescription::DocComment) => { + fluent::parse_expected_semi_found_doc_comment_str + } + Some(TokenDescription::MetaVar(_)) => { + add_token = false; + fluent::parse_expected_semi_found_metavar_str + } + None => fluent::parse_expected_semi_found_str, + }, + ); diag.span(self.span); if add_token { diag.arg("token", self.token); @@ -2830,9 +2842,10 @@ pub(crate) struct DynAfterMut { pub(crate) struct FnPointerCannotBeConst { #[primary_span] pub span: Span, - #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")] #[label] pub qualifier: Span, + #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")] + pub suggestion: Span, } #[derive(Diagnostic)] @@ -2840,9 +2853,10 @@ pub(crate) struct FnPointerCannotBeConst { pub(crate) struct FnPointerCannotBeAsync { #[primary_span] pub span: Span, - #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")] #[label] pub qualifier: Span, + #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")] + pub suggestion: Span, } #[derive(Diagnostic)] @@ -3233,7 +3247,7 @@ pub(crate) struct MalformedCfgAttr { pub(crate) struct UnknownBuiltinConstruct { #[primary_span] pub span: Span, - pub name: Symbol, + pub name: Ident, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs index ef8d0f96b6102..648a352efd9bd 100644 --- a/compiler/rustc_parse/src/lexer/unicode_chars.rs +++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs @@ -8,7 +8,7 @@ use crate::errors::TokenSubstitution; use crate::token::{self, Delimiter}; #[rustfmt::skip] // for line breaks -pub(super) const UNICODE_ARRAY: &[(char, &str, &str)] = &[ +pub(super) static UNICODE_ARRAY: &[(char, &str, &str)] = &[ ('
', "Line Separator", " "), ('
', "Paragraph Separator", " "), (' ', "Ogham Space mark", " "), diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index f963a424a7fd6..1a104ff5e3375 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -11,18 +11,21 @@ #![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(let_chains)] +#![feature(string_from_utf8_lossy_owned)] #![warn(unreachable_pub)] // tidy-alphabetical-end -use std::path::Path; +use std::path::{Path, PathBuf}; +use std::str::Utf8Error; +use std::sync::Arc; use rustc_ast as ast; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{AttrItem, Attribute, MetaItemInner, token}; use rustc_ast_pretty::pprust; -use rustc_data_structures::sync::Lrc; -use rustc_errors::{Diag, FatalError, PResult}; +use rustc_errors::{Diag, EmissionGuarantee, FatalError, PResult, pluralize}; use rustc_session::parse::ParseSess; +use rustc_span::source_map::SourceMap; use rustc_span::{FileName, SourceFile, Span}; pub use unicode_normalization::UNICODE_VERSION as UNICODE_NORMALIZATION_VERSION; @@ -73,9 +76,22 @@ pub fn new_parser_from_file<'a>( path: &Path, sp: Option, ) -> Result, Vec>> { - let source_file = psess.source_map().load_file(path).unwrap_or_else(|e| { - let msg = format!("couldn't read {}: {}", path.display(), e); + let sm = psess.source_map(); + let source_file = sm.load_file(path).unwrap_or_else(|e| { + let msg = format!("couldn't read `{}`: {}", path.display(), e); let mut err = psess.dcx().struct_fatal(msg); + if let Ok(contents) = std::fs::read(path) + && let Err(utf8err) = String::from_utf8(contents.clone()) + { + utf8_error( + sm, + &path.display().to_string(), + sp, + &mut err, + utf8err.utf8_error(), + &contents, + ); + } if let Some(sp) = sp { err.span(sp); } @@ -84,11 +100,54 @@ pub fn new_parser_from_file<'a>( new_parser_from_source_file(psess, source_file) } +pub fn utf8_error( + sm: &SourceMap, + path: &str, + sp: Option, + err: &mut Diag<'_, E>, + utf8err: Utf8Error, + contents: &[u8], +) { + // The file exists, but it wasn't valid UTF-8. + let start = utf8err.valid_up_to(); + let note = format!("invalid utf-8 at byte `{start}`"); + let msg = if let Some(len) = utf8err.error_len() { + format!( + "byte{s} `{bytes}` {are} not valid utf-8", + bytes = if len == 1 { + format!("{:?}", contents[start]) + } else { + format!("{:?}", &contents[start..start + len]) + }, + s = pluralize!(len), + are = if len == 1 { "is" } else { "are" }, + ) + } else { + note.clone() + }; + let contents = String::from_utf8_lossy(contents).to_string(); + let source = sm.new_source_file(PathBuf::from(path).into(), contents); + let span = Span::with_root_ctxt( + source.normalized_byte_pos(start as u32), + source.normalized_byte_pos(start as u32), + ); + if span.is_dummy() { + err.note(note); + } else { + if sp.is_some() { + err.span_note(span, msg); + } else { + err.span(span); + err.span_label(span, msg); + } + } +} + /// Given a session and a `source_file`, return a parser. Returns any buffered errors from lexing /// the initial token stream. fn new_parser_from_source_file( psess: &ParseSess, - source_file: Lrc, + source_file: Arc, ) -> Result, Vec>> { let end_pos = source_file.end_position(); let stream = source_file_to_stream(psess, source_file, None)?; @@ -113,7 +172,7 @@ pub fn source_str_to_stream( /// parsing the token stream. fn source_file_to_stream<'psess>( psess: &'psess ParseSess, - source_file: Lrc, + source_file: Arc, override_span: Option, ) -> Result>> { let src = source_file.src.as_ref().unwrap_or_else(|| { diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 434f71beac243..cff998fa13790 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -502,10 +502,10 @@ fn make_attr_token_stream( for flat_token in iter { match flat_token { FlatToken::Token((Token { kind: TokenKind::OpenDelim(delim), span }, spacing)) => { - stack_rest.push(mem::replace(&mut stack_top, FrameData { - open_delim_sp: Some((delim, span, spacing)), - inner: vec![], - })); + stack_rest.push(mem::replace( + &mut stack_top, + FrameData { open_delim_sp: Some((delim, span, spacing)), inner: vec![] }, + )); } FlatToken::Token((Token { kind: TokenKind::CloseDelim(delim), span }, spacing)) => { let frame_data = mem::replace(&mut stack_top, stack_rest.pop().unwrap()); diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 9ee6c2fae1ac0..72aebb5d12144 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1,5 +1,6 @@ use std::mem::take; use std::ops::{Deref, DerefMut}; +use std::sync::Arc; use ast::token::IdentIsRaw; use rustc_ast as ast; @@ -14,7 +15,6 @@ use rustc_ast::{ }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::sync::Lrc; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, PResult, Subdiagnostic, Suggestions, pluralize, @@ -684,12 +684,15 @@ impl<'a> Parser<'a> { let span = self.token.span.with_lo(pos).with_hi(pos); err.span_suggestion_verbose( span, - format!("add a space before {} to write a regular comment", match (kind, style) { - (token::CommentKind::Line, ast::AttrStyle::Inner) => "`!`", - (token::CommentKind::Block, ast::AttrStyle::Inner) => "`!`", - (token::CommentKind::Line, ast::AttrStyle::Outer) => "the last `/`", - (token::CommentKind::Block, ast::AttrStyle::Outer) => "the last `*`", - },), + format!( + "add a space before {} to write a regular comment", + match (kind, style) { + (token::CommentKind::Line, ast::AttrStyle::Inner) => "`!`", + (token::CommentKind::Block, ast::AttrStyle::Inner) => "`!`", + (token::CommentKind::Line, ast::AttrStyle::Outer) => "the last `/`", + (token::CommentKind::Block, ast::AttrStyle::Outer) => "the last `*`", + }, + ), " ".to_string(), Applicability::MachineApplicable, ); @@ -1894,13 +1897,14 @@ impl<'a> Parser<'a> { (token::Eof, None) => (self.prev_token.span, self.token.span), _ => (self.prev_token.span.shrink_to_hi(), self.token.span), }; - let msg = format!("expected `{}`, found {}", token_str, match ( - &self.token.kind, - self.subparser_name - ) { - (token::Eof, Some(origin)) => format!("end of {origin}"), - _ => this_token_str, - },); + let msg = format!( + "expected `{}`, found {}", + token_str, + match (&self.token.kind, self.subparser_name) { + (token::Eof, Some(origin)) => format!("end of {origin}"), + _ => this_token_str, + }, + ); let mut err = self.dcx().struct_span_err(sp, msg); let label_exp = format!("expected `{token_str}`"); let sm = self.psess.source_map(); @@ -2403,7 +2407,7 @@ impl<'a> Parser<'a> { let mut labels = vec![]; while let TokenKind::Interpolated(nt) = &tok.kind { let tokens = nt.tokens(); - labels.push(Lrc::clone(nt)); + labels.push(Arc::clone(nt)); if let Some(tokens) = tokens && let tokens = tokens.to_attr_token_stream() && let tokens = tokens.0.deref() @@ -2826,25 +2830,27 @@ impl<'a> Parser<'a> { PatKind::Ident(BindingMode::NONE, ident, None) => { match &first_pat.kind { PatKind::Ident(_, old_ident, _) => { - let path = PatKind::Path(None, Path { - span: new_span, - segments: thin_vec![ - PathSegment::from_ident(*old_ident), - PathSegment::from_ident(*ident), - ], - tokens: None, - }); + let path = PatKind::Path( + None, + Path { + span: new_span, + segments: thin_vec![ + PathSegment::from_ident(*old_ident), + PathSegment::from_ident(*ident), + ], + tokens: None, + }, + ); first_pat = self.mk_pat(new_span, path); show_sugg = true; } PatKind::Path(old_qself, old_path) => { let mut segments = old_path.segments.clone(); segments.push(PathSegment::from_ident(*ident)); - let path = PatKind::Path(old_qself.clone(), Path { - span: new_span, - segments, - tokens: None, - }); + let path = PatKind::Path( + old_qself.clone(), + Path { span: new_span, segments, tokens: None }, + ); first_pat = self.mk_pat(new_span, path); show_sugg = true; } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 7533e75ffe261..e0e6c2177da54 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -807,17 +807,20 @@ impl<'a> Parser<'a> { // Check if an illegal postfix operator has been added after the cast. // If the resulting expression is not a cast, it is an illegal postfix operator. if !matches!(with_postfix.kind, ExprKind::Cast(_, _)) { - let msg = format!("cast cannot be followed by {}", match with_postfix.kind { - ExprKind::Index(..) => "indexing", - ExprKind::Try(_) => "`?`", - ExprKind::Field(_, _) => "a field access", - ExprKind::MethodCall(_) => "a method call", - ExprKind::Call(_, _) => "a function call", - ExprKind::Await(_, _) => "`.await`", - ExprKind::Match(_, _, MatchKind::Postfix) => "a postfix match", - ExprKind::Err(_) => return Ok(with_postfix), - _ => unreachable!("parse_dot_or_call_expr_with_ shouldn't produce this"), - }); + let msg = format!( + "cast cannot be followed by {}", + match with_postfix.kind { + ExprKind::Index(..) => "indexing", + ExprKind::Try(_) => "`?`", + ExprKind::Field(_, _) => "a field access", + ExprKind::MethodCall(_) => "a method call", + ExprKind::Call(_, _) => "a function call", + ExprKind::Await(_, _) => "`.await`", + ExprKind::Match(_, _, MatchKind::Postfix) => "a postfix match", + ExprKind::Err(_) => return Ok(with_postfix), + _ => unreachable!("parse_dot_or_call_expr_with_ shouldn't produce this"), + } + ); let mut err = self.dcx().struct_span_err(span, msg); let suggest_parens = |err: &mut Diag<'_>| { @@ -1958,7 +1961,7 @@ impl<'a> Parser<'a> { } else { let err = self.dcx().create_err(errors::UnknownBuiltinConstruct { span: lo.to(ident.span), - name: ident.name, + name: ident, }); return Err(err); }; @@ -2095,7 +2098,7 @@ impl<'a> Parser<'a> { // point literal here, since there's no use of the exponent // syntax that also constitutes a valid integer, so we need // not check for that. - if suffix.map_or(true, |s| s == sym::f32 || s == sym::f64) + if suffix.is_none_or(|s| s == sym::f32 || s == sym::f64) && symbol.as_str().chars().all(|c| c.is_numeric() || c == '_') && self.token.span.hi() == next_token.span.lo() { @@ -2862,13 +2865,10 @@ impl<'a> Parser<'a> { .emit_err(errors::MissingExpressionInForLoop { span: expr.span.shrink_to_lo() }); let err_expr = self.mk_expr(expr.span, ExprKind::Err(guar)); let block = self.mk_block(thin_vec![], BlockCheckMode::Default, self.prev_token.span); - return Ok(self.mk_expr(lo.to(self.prev_token.span), ExprKind::ForLoop { - pat, - iter: err_expr, - body: block, - label: opt_label, - kind, - })); + return Ok(self.mk_expr( + lo.to(self.prev_token.span), + ExprKind::ForLoop { pat, iter: err_expr, body: block, label: opt_label, kind }, + )); } let (attrs, loop_block) = self.parse_inner_attrs_and_block()?; @@ -3114,9 +3114,8 @@ impl<'a> Parser<'a> { let span_before_body = this.prev_token.span; let arm_body; let is_fat_arrow = this.check(exp!(FatArrow)); - let is_almost_fat_arrow = TokenKind::FatArrow - .similar_tokens() - .is_some_and(|similar_tokens| similar_tokens.contains(&this.token.kind)); + let is_almost_fat_arrow = + TokenKind::FatArrow.similar_tokens().contains(&this.token.kind); // this avoids the compiler saying that a `,` or `}` was expected even though // the pattern isn't a never pattern (and thus an arm body is required) diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index b1b84b0b7011e..11f0e579de5a1 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -4,7 +4,7 @@ use rustc_ast::{ WhereClause, token, }; use rustc_errors::{Applicability, PResult}; -use rustc_span::{Ident, Span, kw}; +use rustc_span::{Ident, Span, kw, sym}; use thin_vec::ThinVec; use super::{ForceCollect, Parser, Trailing, UsePreAttrPos}; @@ -297,6 +297,32 @@ impl<'a> Parser<'a> { }) } + /// Parses an experimental fn contract + /// (`contract_requires(WWW) contract_ensures(ZZZ)`) + pub(super) fn parse_contract( + &mut self, + ) -> PResult<'a, Option>> { + let requires = if self.eat_keyword_noexpect(exp!(ContractRequires).kw) { + self.psess.gated_spans.gate(sym::contracts_internals, self.prev_token.span); + let precond = self.parse_expr()?; + Some(precond) + } else { + None + }; + let ensures = if self.eat_keyword_noexpect(exp!(ContractEnsures).kw) { + self.psess.gated_spans.gate(sym::contracts_internals, self.prev_token.span); + let postcond = self.parse_expr()?; + Some(postcond) + } else { + None + }; + if requires.is_none() && ensures.is_none() { + Ok(None) + } else { + Ok(Some(rustc_ast::ptr::P(ast::FnContract { requires, ensures }))) + } + } + /// Parses an optional where-clause. /// /// ```ignore (only-for-syntax-highlight) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index f3e56be9f6e8b..637ed2774a293 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -213,9 +213,12 @@ impl<'a> Parser<'a> { self.parse_use_item()? } else if self.check_fn_front_matter(check_pub, case) { // FUNCTION ITEM - let (ident, sig, generics, body) = + let (ident, sig, generics, contract, body) = self.parse_fn(attrs, fn_parse_mode, lo, vis, case)?; - (ident, ItemKind::Fn(Box::new(Fn { defaultness: def_(), sig, generics, body }))) + ( + ident, + ItemKind::Fn(Box::new(Fn { defaultness: def_(), sig, generics, contract, body })), + ) } else if self.eat_keyword(exp!(Extern)) { if self.eat_keyword(exp!(Crate)) { // EXTERN CRATE @@ -1951,10 +1954,10 @@ impl<'a> Parser<'a> { // Try to recover extra trailing angle brackets if let TyKind::Path(_, Path { segments, .. }) = &a_var.ty.kind { if let Some(last_segment) = segments.last() { - let guar = self.check_trailing_angle_brackets(last_segment, &[ - exp!(Comma), - exp!(CloseBrace), - ]); + let guar = self.check_trailing_angle_brackets( + last_segment, + &[exp!(Comma), exp!(CloseBrace)], + ); if let Some(_guar) = guar { // Handle a case like `Vec>,` where we can continue parsing fields // after the comma @@ -2372,7 +2375,7 @@ impl<'a> Parser<'a> { sig_lo: Span, vis: &Visibility, case: Case, - ) -> PResult<'a, (Ident, FnSig, Generics, Option>)> { + ) -> PResult<'a, (Ident, FnSig, Generics, Option>, Option>)> { let fn_span = self.token.span; let header = self.parse_fn_front_matter(vis, case)?; // `const ... fn` let ident = self.parse_ident()?; // `foo` @@ -2398,6 +2401,8 @@ impl<'a> Parser<'a> { // inside `parse_fn_body()`. let fn_params_end = self.prev_token.span.shrink_to_hi(); + let contract = self.parse_contract()?; + generics.where_clause = self.parse_where_clause()?; // `where T: Ord` // `fn_params_end` is needed only when it's followed by a where clause. @@ -2409,7 +2414,7 @@ impl<'a> Parser<'a> { let body = self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body, fn_params_end)?; let fn_sig_span = sig_lo.to(sig_hi); - Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, body)) + Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, contract, body)) } /// Provide diagnostics when function body is not found diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 2637ea268c85b..ea464fc8ebbb5 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -13,6 +13,7 @@ mod ty; use std::assert_matches::debug_assert_matches; use std::ops::Range; +use std::sync::Arc; use std::{fmt, mem, slice}; use attr_wrapper::{AttrWrapper, UsePreAttrPos}; @@ -34,7 +35,6 @@ use rustc_ast::{ }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, Diag, FatalError, MultiSpan, PResult}; use rustc_index::interval::IntervalSet; use rustc_session::parse::ParseSess; @@ -189,8 +189,9 @@ pub struct Parser<'a> { } // This type is used a lot, e.g. it's cloned when matching many declarative macro rules with -// nonterminals. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_pointer_width = "64", not(target_arch = "s390x")))] +// nonterminals. Make sure it doesn't unintentionally get bigger. We only check a few arches +// though, because `TokenTypeSet(u128)` alignment varies on others, changing the total size. +#[cfg(all(target_pointer_width = "64", any(target_arch = "aarch64", target_arch = "x86_64")))] rustc_data_structures::static_assert_size!(Parser<'_>, 288); /// Stores span information about a closure. @@ -655,9 +656,9 @@ impl<'a> Parser<'a> { fn check_keyword_case(&mut self, exp: ExpKeywordPair, case: Case) -> bool { if self.check_keyword(exp) { true - // Do an ASCII case-insensitive match, because all keywords are ASCII. } else if case == Case::Insensitive && let Some((ident, IdentIsRaw::No)) = self.token.ident() + // Do an ASCII case-insensitive match, because all keywords are ASCII. && ident.as_str().eq_ignore_ascii_case(exp.kw.as_str()) { true @@ -689,7 +690,8 @@ impl<'a> Parser<'a> { true } else if case == Case::Insensitive && let Some((ident, IdentIsRaw::No)) = self.token.ident() - && ident.as_str().to_lowercase() == exp.kw.as_str().to_lowercase() + // Do an ASCII case-insensitive match, because all keywords are ASCII. + && ident.as_str().eq_ignore_ascii_case(exp.kw.as_str()) { self.dcx().emit_err(errors::KwBadCase { span: ident.span, kw: exp.kw.as_str() }); self.bump(); @@ -922,10 +924,8 @@ impl<'a> Parser<'a> { _ => { // Attempt to keep parsing if it was a similar separator. - if let Some(tokens) = exp.tok.similar_tokens() { - if tokens.contains(&self.token.kind) { - self.bump(); - } + if exp.tok.similar_tokens().contains(&self.token.kind) { + self.bump(); } } } @@ -1596,45 +1596,35 @@ impl<'a> Parser<'a> { // Only used when debugging. #[allow(unused)] pub(crate) fn debug_lookahead(&self, lookahead: usize) -> impl fmt::Debug + '_ { - struct DebugParser<'dbg> { - parser: &'dbg Parser<'dbg>, - lookahead: usize, - } - - impl fmt::Debug for DebugParser<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { parser, lookahead } = self; - let mut dbg_fmt = f.debug_struct("Parser"); // or at least, one view of - - // we don't need N spans, but we want at least one, so print all of prev_token - dbg_fmt.field("prev_token", &parser.prev_token); - let mut tokens = vec![]; - for i in 0..*lookahead { - let tok = parser.look_ahead(i, |tok| tok.kind.clone()); - let is_eof = tok == TokenKind::Eof; - tokens.push(tok); - if is_eof { - // Don't look ahead past EOF. - break; - } - } - dbg_fmt.field_with("tokens", |field| field.debug_list().entries(tokens).finish()); - dbg_fmt.field("approx_token_stream_pos", &parser.num_bump_calls); - - // some fields are interesting for certain values, as they relate to macro parsing - if let Some(subparser) = parser.subparser_name { - dbg_fmt.field("subparser_name", &subparser); - } - if let Recovery::Forbidden = parser.recovery { - dbg_fmt.field("recovery", &parser.recovery); + fmt::from_fn(move |f| { + let mut dbg_fmt = f.debug_struct("Parser"); // or at least, one view of + + // we don't need N spans, but we want at least one, so print all of prev_token + dbg_fmt.field("prev_token", &self.prev_token); + let mut tokens = vec![]; + for i in 0..lookahead { + let tok = self.look_ahead(i, |tok| tok.kind.clone()); + let is_eof = tok == TokenKind::Eof; + tokens.push(tok); + if is_eof { + // Don't look ahead past EOF. + break; } + } + dbg_fmt.field_with("tokens", |field| field.debug_list().entries(tokens).finish()); + dbg_fmt.field("approx_token_stream_pos", &self.num_bump_calls); - // imply there's "more to know" than this view - dbg_fmt.finish_non_exhaustive() + // some fields are interesting for certain values, as they relate to macro parsing + if let Some(subparser) = self.subparser_name { + dbg_fmt.field("subparser_name", &subparser); + } + if let Recovery::Forbidden = self.recovery { + dbg_fmt.field("recovery", &self.recovery); } - } - DebugParser { parser: self, lookahead } + // imply there's "more to know" than this view + dbg_fmt.finish_non_exhaustive() + }) } pub fn clear_expected_token_types(&mut self) { @@ -1695,5 +1685,5 @@ pub enum ParseNtResult { Lifetime(Ident, IdentIsRaw), /// This case will eventually be removed, along with `Token::Interpolate`. - Nt(Lrc), + Nt(Arc), } diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 67cabb757e9ef..eefdb641da22a 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use rustc_ast::HasTokens; use rustc_ast::ptr::P; use rustc_ast::token::Nonterminal::*; @@ -7,7 +9,6 @@ use rustc_ast::token::{ self, Delimiter, InvisibleOrigin, MetaVarKind, Nonterminal, NonterminalKind, Token, }; use rustc_ast_pretty::pprust; -use rustc_data_structures::sync::Lrc; use rustc_errors::PResult; use rustc_span::{Ident, kw}; @@ -235,7 +236,7 @@ impl<'a> Parser<'a> { ); } - Ok(ParseNtResult::Nt(Lrc::new(nt))) + Ok(ParseNtResult::Nt(Arc::new(nt))) } } diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 5ad3da2196f8f..64bcb1a5a36cc 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -656,14 +656,14 @@ impl<'a> Parser<'a> { fn visit_pat(&mut self, p: &'a Pat) -> Self::Result { match &p.kind { // Base expression - PatKind::Err(_) | PatKind::Lit(_) => { + PatKind::Err(_) | PatKind::Expr(_) => { self.maybe_add_suggestions_then_emit(p.span, p.span, false) } // Sub-patterns // FIXME: this doesn't work with recursive subpats (`&mut &mut `) PatKind::Box(subpat) | PatKind::Ref(subpat, _) - if matches!(subpat.kind, PatKind::Err(_) | PatKind::Lit(_)) => + if matches!(subpat.kind, PatKind::Err(_) | PatKind::Expr(_)) => { self.maybe_add_suggestions_then_emit(subpat.span, p.span, false) } @@ -766,7 +766,7 @@ impl<'a> Parser<'a> { if let Some(re) = self.parse_range_end() { self.parse_pat_range_begin_with(const_expr, re)? } else { - PatKind::Lit(const_expr) + PatKind::Expr(const_expr) } } else if self.is_builtin() { self.parse_pat_builtin()? @@ -833,7 +833,7 @@ impl<'a> Parser<'a> { .struct_span_err(self_.token.span, msg) .with_span_label(self_.token.span, format!("expected {expected}")) }); - PatKind::Lit(self.mk_expr(lo, ExprKind::Lit(lit))) + PatKind::Expr(self.mk_expr(lo, ExprKind::Lit(lit))) } else { // Try to parse everything else as literal with optional minus match self.parse_literal_maybe_minus() { @@ -845,7 +845,7 @@ impl<'a> Parser<'a> { match self.parse_range_end() { Some(form) => self.parse_pat_range_begin_with(begin, form)?, - None => PatKind::Lit(begin), + None => PatKind::Expr(begin), } } Err(err) => return self.fatal_unexpected_non_pat(err, expected), @@ -989,7 +989,7 @@ impl<'a> Parser<'a> { match &pat.kind { // recover ranges with parentheses around the `(start)..` - PatKind::Lit(begin) + PatKind::Expr(begin) if self.may_recover() && let Some(form) = self.parse_range_end() => { diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 39737b9e13798..576711e66777e 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -106,11 +106,10 @@ impl<'a> Parser<'a> { self.parse_path_segments(&mut path.segments, style, None)?; } - Ok((qself, Path { - segments: path.segments, - span: lo.to(self.prev_token.span), - tokens: None, - })) + Ok(( + qself, + Path { segments: path.segments, span: lo.to(self.prev_token.span), tokens: None }, + )) } /// Recover from an invalid single colon, when the user likely meant a qualified path. @@ -485,13 +484,16 @@ impl<'a> Parser<'a> { error.span_suggestion_verbose( prev_token_before_parsing.span, - format!("consider removing the `::` here to {}", match style { - PathStyle::Expr => "call the expression", - PathStyle::Pat => "turn this into a tuple struct pattern", - _ => { - return; + format!( + "consider removing the `::` here to {}", + match style { + PathStyle::Expr => "call the expression", + PathStyle::Pat => "turn this into a tuple struct pattern", + _ => { + return; + } } - }), + ), "", Applicability::MaybeIncorrect, ); diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 1ddb5fc0a1169..a2699b077fcff 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -417,14 +417,20 @@ impl<'a> Parser<'a> { fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) { if let Some(trailing) = classify::expr_trailing_brace(init) { let (span, sugg) = match trailing { - TrailingBrace::MacCall(mac) => (mac.span(), errors::WrapInParentheses::MacroArgs { - left: mac.args.dspan.open, - right: mac.args.dspan.close, - }), - TrailingBrace::Expr(expr) => (expr.span, errors::WrapInParentheses::Expression { - left: expr.span.shrink_to_lo(), - right: expr.span.shrink_to_hi(), - }), + TrailingBrace::MacCall(mac) => ( + mac.span(), + errors::WrapInParentheses::MacroArgs { + left: mac.args.dspan.open, + right: mac.args.dspan.close, + }, + ), + TrailingBrace::Expr(expr) => ( + expr.span, + errors::WrapInParentheses::Expression { + left: expr.span.shrink_to_lo(), + right: expr.span.shrink_to_hi(), + }, + ), }; self.dcx().emit_err(errors::InvalidCurlyInLetElse { span: span.with_lo(span.hi() - BytePos(1)), diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs index 3f8d66c2c9580..8b8c81a77a01a 100644 --- a/compiler/rustc_parse/src/parser/tests.rs +++ b/compiler/rustc_parse/src/parser/tests.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] +#![allow(rustc::symbol_intern_string_literal)] use std::assert_matches::assert_matches; use std::io::prelude::*; @@ -13,7 +13,6 @@ use rustc_ast::token::{self, Delimiter, Token}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_ast::{self as ast, PatKind, visit}; use rustc_ast_pretty::pprust::item_to_string; -use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::{HumanEmitter, OutputTheme}; use rustc_errors::{DiagCtxt, MultiSpan, PResult}; use rustc_session::parse::ParseSess; @@ -27,7 +26,7 @@ use crate::parser::{ForceCollect, Parser}; use crate::{new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal}; fn psess() -> ParseSess { - ParseSess::new(vec![crate::DEFAULT_LOCALE_RESOURCE, crate::DEFAULT_LOCALE_RESOURCE]) + ParseSess::new(vec![crate::DEFAULT_LOCALE_RESOURCE]) } /// Map string to parser (via tts). @@ -39,13 +38,11 @@ fn string_to_parser(psess: &ParseSess, source_str: String) -> Parser<'_> { )) } -fn create_test_handler(theme: OutputTheme) -> (DiagCtxt, Lrc, Arc>>) { +fn create_test_handler(theme: OutputTheme) -> (DiagCtxt, Arc, Arc>>) { let output = Arc::new(Mutex::new(Vec::new())); - let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fallback_bundle = rustc_errors::fallback_fluent_bundle( - vec![crate::DEFAULT_LOCALE_RESOURCE, crate::DEFAULT_LOCALE_RESOURCE], - false, - ); + let source_map = Arc::new(SourceMap::new(FilePathMapping::empty())); + let fallback_bundle = + rustc_errors::fallback_fluent_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false); let mut emitter = HumanEmitter::new(Box::new(Shared { data: output.clone() }), fallback_bundle) .sm(Some(source_map.clone())) .diagnostic_width(Some(140)); @@ -2366,8 +2363,7 @@ fn string_to_tts_1() { token::Ident(sym::i32, IdentIsRaw::No), sp(8, 11), ), - ]) - .into(), + ]), ), TokenTree::Delimited( DelimSpan::from_pair(sp(13, 14), sp(18, 19)), @@ -2383,8 +2379,7 @@ fn string_to_tts_1() { ), // `Alone` because the `;` is followed by whitespace. TokenTree::token_alone(token::Semi, sp(16, 17)), - ]) - .into(), + ]), ), ]); diff --git a/compiler/rustc_parse/src/parser/token_type.rs b/compiler/rustc_parse/src/parser/token_type.rs index 73f3ac001c8ae..40631d9154d37 100644 --- a/compiler/rustc_parse/src/parser/token_type.rs +++ b/compiler/rustc_parse/src/parser/token_type.rs @@ -83,6 +83,8 @@ pub enum TokenType { KwCatch, KwConst, KwContinue, + KwContractEnsures, + KwContractRequires, KwCrate, KwDefault, KwDyn, @@ -217,6 +219,8 @@ impl TokenType { KwCatch, KwConst, KwContinue, + KwContractEnsures, + KwContractRequires, KwCrate, KwDefault, KwDyn, @@ -289,6 +293,8 @@ impl TokenType { TokenType::KwCatch => Some(kw::Catch), TokenType::KwConst => Some(kw::Const), TokenType::KwContinue => Some(kw::Continue), + TokenType::KwContractEnsures => Some(kw::ContractEnsures), + TokenType::KwContractRequires => Some(kw::ContractRequires), TokenType::KwCrate => Some(kw::Crate), TokenType::KwDefault => Some(kw::Default), TokenType::KwDyn => Some(kw::Dyn), @@ -519,6 +525,8 @@ macro_rules! exp { (Catch) => { exp!(@kw, Catch, KwCatch) }; (Const) => { exp!(@kw, Const, KwConst) }; (Continue) => { exp!(@kw, Continue, KwContinue) }; + (ContractEnsures) => { exp!(@kw, ContractEnsures, KwContractEnsures) }; + (ContractRequires) => { exp!(@kw, ContractRequires, KwContractRequires) }; (Crate) => { exp!(@kw, Crate, KwCrate) }; (Default) => { exp!(@kw, Default, KwDefault) }; (Dyn) => { exp!(@kw, Dyn, KwDyn) }; diff --git a/compiler/rustc_parse/src/parser/tokenstream/tests.rs b/compiler/rustc_parse/src/parser/tokenstream/tests.rs index 037b5b1a9de5d..aac75323ff3d2 100644 --- a/compiler/rustc_parse/src/parser/tokenstream/tests.rs +++ b/compiler/rustc_parse/src/parser/tokenstream/tests.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] +#![allow(rustc::symbol_intern_string_literal)] use rustc_ast::token::{self, IdentIsRaw}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 6497d19a173ca..dc5919b3630ce 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -609,16 +609,58 @@ impl<'a> Parser<'a> { let span_start = self.token.span; let ast::FnHeader { ext, safety, constness, coroutine_kind } = self.parse_fn_front_matter(&inherited_vis, Case::Sensitive)?; + let fn_start_lo = self.prev_token.span.lo(); if self.may_recover() && self.token == TokenKind::Lt { self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?; } let decl = self.parse_fn_decl(|_| false, AllowPlus::No, recover_return_sign)?; let whole_span = lo.to(self.prev_token.span); - if let ast::Const::Yes(span) = constness { - self.dcx().emit_err(FnPointerCannotBeConst { span: whole_span, qualifier: span }); + + // Order/parsing of "front matter" follows: + // ` fn()` + // ^ ^ ^ ^ ^ + // | | | | fn_start_lo + // | | | ext_sp.lo + // | | safety_sp.lo + // | coroutine_sp.lo + // const_sp.lo + if let ast::Const::Yes(const_span) = constness { + let next_token_lo = if let Some( + ast::CoroutineKind::Async { span, .. } + | ast::CoroutineKind::Gen { span, .. } + | ast::CoroutineKind::AsyncGen { span, .. }, + ) = coroutine_kind + { + span.lo() + } else if let ast::Safety::Unsafe(span) | ast::Safety::Safe(span) = safety { + span.lo() + } else if let ast::Extern::Implicit(span) | ast::Extern::Explicit(_, span) = ext { + span.lo() + } else { + fn_start_lo + }; + let sugg_span = const_span.with_hi(next_token_lo); + self.dcx().emit_err(FnPointerCannotBeConst { + span: whole_span, + qualifier: const_span, + suggestion: sugg_span, + }); } - if let Some(ast::CoroutineKind::Async { span, .. }) = coroutine_kind { - self.dcx().emit_err(FnPointerCannotBeAsync { span: whole_span, qualifier: span }); + if let Some(ast::CoroutineKind::Async { span: async_span, .. }) = coroutine_kind { + let next_token_lo = if let ast::Safety::Unsafe(span) | ast::Safety::Safe(span) = safety + { + span.lo() + } else if let ast::Extern::Implicit(span) | ast::Extern::Explicit(_, span) = ext { + span.lo() + } else { + fn_start_lo + }; + let sugg_span = async_span.with_hi(next_token_lo); + self.dcx().emit_err(FnPointerCannotBeAsync { + span: whole_span, + qualifier: async_span, + suggestion: sugg_span, + }); } // FIXME(gen_blocks): emit a similar error for `gen fn()` let decl_span = span_start.to(self.prev_token.span); diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 8b6b37c0f8f52..86f673c100c19 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -11,7 +11,7 @@ use rustc_session::errors::report_lit_error; use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{ILL_FORMED_ATTRIBUTE_INPUT, UNSAFE_ATTR_OUTSIDE_UNSAFE}; use rustc_session::parse::ParseSess; -use rustc_span::{BytePos, Span, Symbol, sym}; +use rustc_span::{Span, Symbol, sym}; use crate::{errors, parse_in}; @@ -164,11 +164,7 @@ pub fn check_attribute_safety(psess: &ParseSess, safety: AttributeSafety, attr: // wrapping it in `unsafe(...)`. Otherwise, we suggest putting the // `unsafe(`, `)` right after and right before the opening and closing // square bracket respectively. - let diag_span = if attr_item.span().can_be_used_for_suggestions() { - attr_item.span() - } else { - attr.span.with_lo(attr.span.lo() + BytePos(2)).with_hi(attr.span.hi() - BytePos(1)) - }; + let diag_span = attr_item.span(); if attr.span.at_least_rust_2024() { psess.dcx().emit_err(errors::UnsafeAttrOutsideUnsafe { diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 9eb335cb34cc2..3b985621b5772 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -16,11 +16,8 @@ #![warn(unreachable_pub)] // tidy-alphabetical-end -use std::{iter, str, string}; - pub use Alignment::*; pub use Count::*; -pub use Piece::*; pub use Position::*; use rustc_lexer::unescape; @@ -86,7 +83,7 @@ impl InnerOffset { #[derive(Clone, Debug, PartialEq)] pub enum Piece<'a> { /// A literal string which should directly be emitted - String(&'a str), + Lit(&'a str), /// This describes that formatting should process the next argument (as /// specified inside) for emission. NextArgument(Box>), @@ -205,11 +202,11 @@ pub enum Count<'a> { } pub struct ParseError { - pub description: string::String, - pub note: Option, - pub label: string::String, + pub description: String, + pub note: Option, + pub label: String, pub span: InnerSpan, - pub secondary_label: Option<(string::String, InnerSpan)>, + pub secondary_label: Option<(String, InnerSpan)>, pub suggestion: Suggestion, } @@ -225,7 +222,7 @@ pub enum Suggestion { /// `format!("{foo:?#}")` -> `format!("{foo:#?}")` /// `format!("{foo:?x}")` -> `format!("{foo:x?}")` /// `format!("{foo:?X}")` -> `format!("{foo:X?}")` - ReorderFormatParameter(InnerSpan, string::String), + ReorderFormatParameter(InnerSpan, String), } /// The parser structure for interpreting the input format string. This is @@ -237,7 +234,7 @@ pub enum Suggestion { pub struct Parser<'a> { mode: ParseMode, input: &'a str, - cur: iter::Peekable>, + cur: std::iter::Peekable>, /// Error messages accumulated during parsing pub errors: Vec, /// Current position of implicit positional argument pointer @@ -278,7 +275,7 @@ impl<'a> Iterator for Parser<'a> { if self.consume('{') { self.last_opening_brace = curr_last_brace; - Some(String(self.string(pos + 1))) + Some(Piece::Lit(self.string(pos + 1))) } else { let arg = self.argument(lbrace_end); if let Some(rbrace_pos) = self.consume_closing_brace(&arg) { @@ -299,13 +296,13 @@ impl<'a> Iterator for Parser<'a> { _ => self.suggest_positional_arg_instead_of_captured_arg(arg), } } - Some(NextArgument(Box::new(arg))) + Some(Piece::NextArgument(Box::new(arg))) } } '}' => { self.cur.next(); if self.consume('}') { - Some(String(self.string(pos + 1))) + Some(Piece::Lit(self.string(pos + 1))) } else { let err_pos = self.to_span_index(pos); self.err_with_note( @@ -317,7 +314,7 @@ impl<'a> Iterator for Parser<'a> { None } } - _ => Some(String(self.string(pos))), + _ => Some(Piece::Lit(self.string(pos))), } } else { if self.is_source_literal { @@ -336,7 +333,7 @@ impl<'a> Parser<'a> { pub fn new( s: &'a str, style: Option, - snippet: Option, + snippet: Option, append_newline: bool, mode: ParseMode, ) -> Parser<'a> { @@ -366,12 +363,7 @@ impl<'a> Parser<'a> { /// Notifies of an error. The message doesn't actually need to be of type /// String, but I think it does when this eventually uses conditions so it /// might as well start using it now. - fn err, S2: Into>( - &mut self, - description: S1, - label: S2, - span: InnerSpan, - ) { + fn err(&mut self, description: impl Into, label: impl Into, span: InnerSpan) { self.errors.push(ParseError { description: description.into(), note: None, @@ -385,15 +377,11 @@ impl<'a> Parser<'a> { /// Notifies of an error. The message doesn't actually need to be of type /// String, but I think it does when this eventually uses conditions so it /// might as well start using it now. - fn err_with_note< - S1: Into, - S2: Into, - S3: Into, - >( + fn err_with_note( &mut self, - description: S1, - label: S2, - note: S3, + description: impl Into, + label: impl Into, + note: impl Into, span: InnerSpan, ) { self.errors.push(ParseError { @@ -514,13 +502,7 @@ impl<'a> Parser<'a> { /// Consumes all whitespace characters until the first non-whitespace character fn ws(&mut self) { - while let Some(&(_, c)) = self.cur.peek() { - if c.is_whitespace() { - self.cur.next(); - } else { - break; - } - } + while let Some(_) = self.cur.next_if(|&(_, c)| c.is_whitespace()) {} } /// Parses all of a string which is to be considered a "raw literal" in a @@ -545,7 +527,7 @@ impl<'a> Parser<'a> { } } } - &self.input[start..self.input.len()] + &self.input[start..] } /// Parses an `Argument` structure, or what's contained within braces inside the format string. @@ -881,28 +863,34 @@ impl<'a> Parser<'a> { if let (Some(pos), Some(_)) = (self.consume_pos('?'), self.consume_pos(':')) { let word = self.word(); let pos = self.to_span_index(pos); - self.errors.insert(0, ParseError { - description: "expected format parameter to occur after `:`".to_owned(), - note: Some(format!("`?` comes after `:`, try `{}:{}` instead", word, "?")), - label: "expected `?` to occur after `:`".to_owned(), - span: pos.to(pos), - secondary_label: None, - suggestion: Suggestion::None, - }); + self.errors.insert( + 0, + ParseError { + description: "expected format parameter to occur after `:`".to_owned(), + note: Some(format!("`?` comes after `:`, try `{}:{}` instead", word, "?")), + label: "expected `?` to occur after `:`".to_owned(), + span: pos.to(pos), + secondary_label: None, + suggestion: Suggestion::None, + }, + ); } } fn suggest_format_align(&mut self, alignment: char) { if let Some(pos) = self.consume_pos(alignment) { let pos = self.to_span_index(pos); - self.errors.insert(0, ParseError { - description: "expected format parameter to occur after `:`".to_owned(), - note: None, - label: format!("expected `{}` to occur after `:`", alignment), - span: pos.to(pos), - secondary_label: None, - suggestion: Suggestion::None, - }); + self.errors.insert( + 0, + ParseError { + description: "expected format parameter to occur after `:`".to_owned(), + note: None, + label: format!("expected `{}` to occur after `:`", alignment), + span: pos.to(pos), + secondary_label: None, + suggestion: Suggestion::None, + }, + ); } } @@ -919,24 +907,36 @@ impl<'a> Parser<'a> { if let ArgumentNamed(_) = arg.position { match field.position { ArgumentNamed(_) => { - self.errors.insert(0, ParseError { - description: "field access isn't supported".to_string(), - note: None, - label: "not supported".to_string(), - span: InnerSpan::new(arg.position_span.start, field.position_span.end), - secondary_label: None, - suggestion: Suggestion::UsePositional, - }); + self.errors.insert( + 0, + ParseError { + description: "field access isn't supported".to_string(), + note: None, + label: "not supported".to_string(), + span: InnerSpan::new( + arg.position_span.start, + field.position_span.end, + ), + secondary_label: None, + suggestion: Suggestion::UsePositional, + }, + ); } ArgumentIs(_) => { - self.errors.insert(0, ParseError { - description: "tuple index access isn't supported".to_string(), - note: None, - label: "not supported".to_string(), - span: InnerSpan::new(arg.position_span.start, field.position_span.end), - secondary_label: None, - suggestion: Suggestion::UsePositional, - }); + self.errors.insert( + 0, + ParseError { + description: "tuple index access isn't supported".to_string(), + note: None, + label: "not supported".to_string(), + span: InnerSpan::new( + arg.position_span.start, + field.position_span.end, + ), + secondary_label: None, + suggestion: Suggestion::UsePositional, + }, + ); } _ => {} }; @@ -958,14 +958,17 @@ impl<'a> Parser<'a> { let span = self.span(pos - 1, pos + 1); let pos = self.to_span_index(pos); - self.errors.insert(0, ParseError { - description: format!("expected `}}`, found `{c}`"), - note: None, - label: "expected `'}'`".into(), - span: pos.to(pos), - secondary_label: None, - suggestion: Suggestion::ReorderFormatParameter(span, format!("{replacement}")), - }) + self.errors.insert( + 0, + ParseError { + description: format!("expected `}}`, found `{c}`"), + note: None, + label: "expected `'}'`".into(), + span: pos.to(pos), + secondary_label: None, + suggestion: Suggestion::ReorderFormatParameter(span, format!("{replacement}")), + }, + ) } } @@ -974,7 +977,7 @@ impl<'a> Parser<'a> { /// in order to properly synthesise the intra-string `Span`s for error diagnostics. fn find_width_map_from_snippet( input: &str, - snippet: Option, + snippet: Option, str_style: Option, ) -> InputStringKind { let snippet = match snippet { @@ -1089,8 +1092,8 @@ fn find_width_map_from_snippet( InputStringKind::Literal { width_mappings } } -fn unescape_string(string: &str) -> Option { - let mut buf = string::String::new(); +fn unescape_string(string: &str) -> Option { + let mut buf = String::new(); let mut ok = true; unescape::unescape_unicode(string, unescape::Mode::Str, &mut |_, unescaped_char| { match unescaped_char { diff --git a/compiler/rustc_parse_format/src/tests.rs b/compiler/rustc_parse_format/src/tests.rs index 81e5bca0ba9fa..cc8a0069c4ecd 100644 --- a/compiler/rustc_parse_format/src/tests.rs +++ b/compiler/rustc_parse_format/src/tests.rs @@ -1,3 +1,5 @@ +use Piece::*; + use super::*; #[track_caller] @@ -32,12 +34,12 @@ fn musterr(s: &str) { #[test] fn simple() { - same("asdf", &[String("asdf")]); - same("a{{b", &[String("a"), String("{b")]); - same("a}}b", &[String("a"), String("}b")]); - same("a}}", &[String("a"), String("}")]); - same("}}", &[String("}")]); - same("\\}}", &[String("\\"), String("}")]); + same("asdf", &[Lit("asdf")]); + same("a{{b", &[Lit("a"), Lit("{b")]); + same("a}}b", &[Lit("a"), Lit("}b")]); + same("a}}", &[Lit("a"), Lit("}")]); + same("}}", &[Lit("}")]); + same("\\}}", &[Lit("\\"), Lit("}")]); } #[test] @@ -78,307 +80,311 @@ fn invalid_precision() { #[test] fn format_nothing() { - same("{}", &[NextArgument(Box::new(Argument { - position: ArgumentImplicitlyIs(0), - position_span: InnerSpan { start: 2, end: 2 }, - format: fmtdflt(), - }))]); + same( + "{}", + &[NextArgument(Box::new(Argument { + position: ArgumentImplicitlyIs(0), + position_span: InnerSpan { start: 2, end: 2 }, + format: fmtdflt(), + }))], + ); } #[test] fn format_position() { - same("{3}", &[NextArgument(Box::new(Argument { - position: ArgumentIs(3), - position_span: InnerSpan { start: 2, end: 3 }, - format: fmtdflt(), - }))]); + same( + "{3}", + &[NextArgument(Box::new(Argument { + position: ArgumentIs(3), + position_span: InnerSpan { start: 2, end: 3 }, + format: fmtdflt(), + }))], + ); } #[test] fn format_position_nothing_else() { - same("{3:}", &[NextArgument(Box::new(Argument { - position: ArgumentIs(3), - position_span: InnerSpan { start: 2, end: 3 }, - format: fmtdflt(), - }))]); + same( + "{3:}", + &[NextArgument(Box::new(Argument { + position: ArgumentIs(3), + position_span: InnerSpan { start: 2, end: 3 }, + format: fmtdflt(), + }))], + ); } #[test] fn format_named() { - same("{name}", &[NextArgument(Box::new(Argument { - position: ArgumentNamed("name"), - position_span: InnerSpan { start: 2, end: 6 }, - format: fmtdflt(), - }))]) + same( + "{name}", + &[NextArgument(Box::new(Argument { + position: ArgumentNamed("name"), + position_span: InnerSpan { start: 2, end: 6 }, + format: fmtdflt(), + }))], + ) } #[test] fn format_type() { - same("{3:x}", &[NextArgument(Box::new(Argument { - position: ArgumentIs(3), - position_span: InnerSpan { start: 2, end: 3 }, - format: FormatSpec { - fill: None, - fill_span: None, - align: AlignUnknown, - sign: None, - alternate: false, - zero_pad: false, - debug_hex: None, - precision: CountImplied, - width: CountImplied, - precision_span: None, - width_span: None, - ty: "x", - ty_span: None, - }, - }))]); + same( + "{3:x}", + &[NextArgument(Box::new(Argument { + position: ArgumentIs(3), + position_span: InnerSpan { start: 2, end: 3 }, + format: FormatSpec { + fill: None, + fill_span: None, + align: AlignUnknown, + sign: None, + alternate: false, + zero_pad: false, + debug_hex: None, + precision: CountImplied, + width: CountImplied, + precision_span: None, + width_span: None, + ty: "x", + ty_span: None, + }, + }))], + ); } #[test] fn format_align_fill() { - same("{3:>}", &[NextArgument(Box::new(Argument { - position: ArgumentIs(3), - position_span: InnerSpan { start: 2, end: 3 }, - format: FormatSpec { - fill: None, - fill_span: None, - align: AlignRight, - sign: None, - alternate: false, - zero_pad: false, - debug_hex: None, - precision: CountImplied, - width: CountImplied, - precision_span: None, - width_span: None, - ty: "", - ty_span: None, - }, - }))]); - same("{3:0<}", &[NextArgument(Box::new(Argument { - position: ArgumentIs(3), - position_span: InnerSpan { start: 2, end: 3 }, - format: FormatSpec { - fill: Some('0'), - fill_span: Some(InnerSpan::new(4, 5)), - align: AlignLeft, - sign: None, - alternate: false, - zero_pad: false, - debug_hex: None, - precision: CountImplied, - width: CountImplied, - precision_span: None, - width_span: None, - ty: "", - ty_span: None, - }, - }))]); - same("{3:*}", + &[NextArgument(Box::new(Argument { + position: ArgumentIs(3), + position_span: InnerSpan { start: 2, end: 3 }, + format: FormatSpec { + fill: None, + fill_span: None, + align: AlignRight, + sign: None, + alternate: false, + zero_pad: false, + debug_hex: None, + precision: CountImplied, + width: CountImplied, + precision_span: None, + width_span: None, + ty: "", + ty_span: None, + }, + }))], + ); + same( + "{3:0<}", + &[NextArgument(Box::new(Argument { + position: ArgumentIs(3), + position_span: InnerSpan { start: 2, end: 3 }, + format: FormatSpec { + fill: Some('0'), + fill_span: Some(InnerSpan::new(4, 5)), + align: AlignLeft, + sign: None, + alternate: false, + zero_pad: false, + debug_hex: None, + precision: CountImplied, + width: CountImplied, + precision_span: None, + width_span: None, + ty: "", + ty_span: None, + }, + }))], + ); + same( + "{3:*( span: tcx.def_span(item_def_id), }); } - Err(FnAbiError::AdjustForForeignAbi(e)) => { - // Sadly there seems to be no `into_diagnostic` for this case... and I am not sure if - // this can even be reached. Anyway this is a perma-unstable debug attribute, an ICE - // isn't the worst thing. Also this matches what codegen does. - span_bug!( - tcx.def_span(item_def_id), - "error computing fn_abi_of_instance, cannot adjust for foreign ABI: {e:?}", - ) - } } } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 12f715a0fe4a4..f578708b40cd3 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -7,6 +7,7 @@ use std::cell::Cell; use std::collections::hash_map::Entry; +use rustc_abi::{ExternAbi, Size}; use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, MetaItemLit, ast}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey}; @@ -25,14 +26,13 @@ use rustc_middle::traits::ObligationCause; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, TyCtxt, TypingMode}; use rustc_middle::{bug, span_bug}; +use rustc_session::config::CrateType; use rustc_session::lint::builtin::{ CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS, UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, }; use rustc_session::parse::feature_err; use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, kw, sym}; -use rustc_target::abi::Size; -use rustc_target::spec::abi::Abi; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs}; use rustc_trait_selection::traits::ObligationCtxt; @@ -247,7 +247,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_coroutine(attr, target); } [sym::linkage, ..] => self.check_linkage(attr, span, target), - [sym::rustc_pub_transparent, ..] => self.check_rustc_pub_transparent( attr.span, span, attrs), + [sym::rustc_pub_transparent, ..] => self.check_rustc_pub_transparent(attr.span, span, attrs), [ // ok sym::allow @@ -275,7 +275,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::lang | sym::needs_allocator | sym::default_lib_allocator - | sym::start | sym::custom_mir, .. ] => {} @@ -332,6 +331,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_repr(attrs, span, target, item, hir_id); self.check_used(attrs, target, span); + self.check_rustc_force_inline(hir_id, attrs, span, target); } fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) { @@ -344,8 +344,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) { - self.tcx - .emit_node_span_lint(UNUSED_ATTRIBUTES, hir_id, attr.span, errors::IgnoredAttr { sym }); + self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::IgnoredAttr { sym }, + ); } /// Checks if `#[diagnostic::do_not_recommend]` is applied on a trait impl. @@ -1505,10 +1509,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> { _ => { // FIXME: #[cold] was previously allowed on non-functions and some crates used // this, so only emit a warning. - self.tcx.emit_node_span_lint(UNUSED_ATTRIBUTES, hir_id, attr.span, errors::Cold { - span, - on_crate: hir_id == CRATE_HIR_ID, - }); + self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::Cold { span, on_crate: hir_id == CRATE_HIR_ID }, + ); } } } @@ -1518,14 +1524,17 @@ impl<'tcx> CheckAttrVisitor<'tcx> { if target == Target::ForeignMod && let hir::Node::Item(item) = self.tcx.hir_node(hir_id) && let Item { kind: ItemKind::ForeignMod { abi, .. }, .. } = item - && !matches!(abi, Abi::Rust | Abi::RustIntrinsic) + && !matches!(abi, ExternAbi::Rust | ExternAbi::RustIntrinsic) { return; } - self.tcx.emit_node_span_lint(UNUSED_ATTRIBUTES, hir_id, attr.span, errors::Link { - span: (target != Target::ForeignMod).then_some(span), - }); + self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::Link { span: (target != Target::ForeignMod).then_some(span) }, + ); } /// Checks if `#[link_name]` is applied to an item other than a foreign function or static. @@ -1646,7 +1655,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { }; let Some(ItemLike::Item(Item { - kind: ItemKind::Fn(FnSig { decl, .. }, generics, _), .. + kind: ItemKind::Fn { sig: FnSig { decl, .. }, generics, .. }, + .. })) = item else { bug!("should be a function item"); @@ -1849,6 +1859,34 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let mut is_simd = false; let mut is_transparent = false; + // catch `repr()` with no arguments, applied to an item (i.e. not `#![repr()]`) + if hints.is_empty() && item.is_some() { + for attr in attrs.iter().filter(|attr| attr.has_name(sym::repr)) { + match target { + Target::Struct | Target::Union | Target::Enum => {} + Target::Fn | Target::Method(_) => { + feature_err( + &self.tcx.sess, + sym::fn_align, + attr.span, + fluent::passes_repr_align_function, + ) + .emit(); + } + _ => { + self.dcx().emit_err( + errors::AttrApplication::StructEnumFunctionMethodUnion { + hint_span: attr.span, + span, + }, + ); + } + } + } + + return; + } + for hint in &hints { if !hint.is_meta_item() { self.dcx().emit_err(errors::ReprIdent { span: hint.span() }); @@ -1881,24 +1919,19 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } sym::align => { - if let (Target::Fn | Target::Method(MethodKind::Inherent), false) = - (target, self.tcx.features().fn_align()) - { - feature_err( - &self.tcx.sess, - sym::fn_align, - hint.span(), - fluent::passes_repr_align_function, - ) - .emit(); - } - match target { - Target::Struct - | Target::Union - | Target::Enum - | Target::Fn - | Target::Method(_) => {} + Target::Struct | Target::Union | Target::Enum => {} + Target::Fn | Target::Method(_) => { + if !self.tcx.features().fn_align() { + feature_err( + &self.tcx.sess, + sym::fn_align, + hint.span(), + fluent::passes_repr_align_function, + ) + .emit(); + } + } _ => { self.dcx().emit_err( errors::AttrApplication::StructEnumFunctionMethodUnion { @@ -2326,16 +2359,54 @@ impl<'tcx> CheckAttrVisitor<'tcx> { && item.path == sym::reason { errors::UnusedNote::NoLints { name: attr.name_or_empty() } + } else if matches!( + attr.name_or_empty(), + sym::allow | sym::warn | sym::deny | sym::forbid | sym::expect + ) && let Some(meta) = attr.meta_item_list() + && meta.iter().any(|meta| { + meta.meta_item().map_or(false, |item| item.path == sym::linker_messages) + }) + { + if hir_id != CRATE_HIR_ID { + match attr.style { + ast::AttrStyle::Outer => self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::OuterCrateLevelAttr, + ), + ast::AttrStyle::Inner => self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::InnerCrateLevelAttr, + ), + }; + return; + } else { + let never_needs_link = self + .tcx + .crate_types() + .iter() + .all(|kind| matches!(kind, CrateType::Rlib | CrateType::Staticlib)); + if never_needs_link { + errors::UnusedNote::LinkerWarningsBinaryCrateOnly + } else { + return; + } + } } else if attr.name_or_empty() == sym::default_method_body_is_const { errors::UnusedNote::DefaultMethodBodyConst } else { return; }; - self.tcx.emit_node_span_lint(UNUSED_ATTRIBUTES, hir_id, attr.span, errors::Unused { - attr_span: attr.span, - note, - }); + self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::Unused { attr_span: attr.span, note }, + ); } /// A best effort attempt to create an error for a mismatching proc macro signature. @@ -2384,7 +2455,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { token_stream, false, Safety::Safe, - Abi::Rust, + ExternAbi::Rust, ); if let Err(terr) = ocx.eq(&cause, param_env, expected_sig, sig) { @@ -2433,6 +2504,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { }))), terr, false, + None, ); diag.emit(); self.abort.set(true); @@ -2479,6 +2551,45 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } + fn check_rustc_force_inline( + &self, + hir_id: HirId, + attrs: &[Attribute], + span: Span, + target: Target, + ) { + let force_inline_attr = attrs.iter().find(|attr| attr.has_name(sym::rustc_force_inline)); + match (target, force_inline_attr) { + (Target::Closure, None) => { + let is_coro = matches!( + self.tcx.hir().expect_expr(hir_id).kind, + hir::ExprKind::Closure(hir::Closure { + kind: hir::ClosureKind::Coroutine(..) + | hir::ClosureKind::CoroutineClosure(..), + .. + }) + ); + let parent_did = self.tcx.hir().get_parent_item(hir_id).to_def_id(); + let parent_span = self.tcx.def_span(parent_did); + let parent_force_inline_attr = + self.tcx.get_attr(parent_did, sym::rustc_force_inline); + if let Some(attr) = parent_force_inline_attr + && is_coro + { + self.dcx().emit_err(errors::RustcForceInlineCoro { + attr_span: attr.span, + span: parent_span, + }); + } + } + (Target::Fn, _) => (), + (_, Some(attr)) => { + self.dcx().emit_err(errors::RustcForceInline { attr_span: attr.span, span }); + } + (_, None) => (), + } + } + /// Checks if `#[autodiff]` is applied to an item other than a function item. fn check_autodiff(&self, _hir_id: HirId, _attr: &Attribute, span: Span, target: Target) { debug!("check_autodiff"); @@ -2614,7 +2725,6 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { sym::repr, sym::path, sym::automatically_derived, - sym::start, sym::rustc_main, sym::derive, sym::test, @@ -2769,7 +2879,7 @@ fn doc_fake_variadic_is_allowed_self_ty(self_ty: &hir::Ty<'_>) -> bool { && let Some(&[hir::GenericArg::Type(ty)]) = path.segments.last().map(|last| last.args().args) { - doc_fake_variadic_is_allowed_self_ty(ty) + doc_fake_variadic_is_allowed_self_ty(ty.as_unambig_ty()) } else { false }) diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index c5dc8e68fe847..95f18eaa7ef1b 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -10,11 +10,10 @@ use hir::def_id::{LocalDefIdMap, LocalDefIdSet}; use rustc_abi::FieldIdx; use rustc_data_structures::unord::UnordSet; use rustc_errors::MultiSpan; -use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Node, PatKind, TyKind}; +use rustc_hir::{self as hir, Node, PatKind, TyKind}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::privacy::Level; use rustc_middle::query::Providers; @@ -460,7 +459,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } // mark self_ty live - intravisit::walk_ty(self, impl_ref.self_ty); + intravisit::walk_unambig_ty(self, impl_ref.self_ty); if let Some(&impl_item_id) = self.tcx.impl_item_implementor_ids(impl_id).get(&trait_item_id) { @@ -637,10 +636,6 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { let res = self.typeck_results().qpath_res(path, pat.hir_id); self.handle_field_pattern_match(pat, res, fields); } - PatKind::Path(ref qpath) => { - let res = self.typeck_results().qpath_res(qpath, pat.hir_id); - self.handle_res(res); - } PatKind::TupleStruct(ref qpath, fields, dotdot) => { let res = self.typeck_results().qpath_res(qpath, pat.hir_id); self.handle_tuple_field_pattern_match(pat, res, fields, dotdot); @@ -652,6 +647,17 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { self.in_pat = false; } + fn visit_pat_expr(&mut self, expr: &'tcx rustc_hir::PatExpr<'tcx>) { + match &expr.kind { + rustc_hir::PatExprKind::Path(qpath) => { + let res = self.typeck_results().qpath_res(qpath, expr.hir_id); + self.handle_res(res); + } + _ => {} + } + intravisit::walk_pat_expr(self, expr); + } + fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) { self.handle_res(path.res); intravisit::walk_path(self, path); @@ -1231,7 +1237,7 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { continue; } - let is_positional = variant.fields.raw.first().map_or(false, |field| { + let is_positional = variant.fields.raw.first().is_some_and(|field| { field.name.as_str().starts_with(|c: char| c.is_ascii_digit()) }); let report_on = diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs index 7b02aecdfae1f..323b414cca04c 100644 --- a/compiler/rustc_passes/src/diagnostic_items.rs +++ b/compiler/rustc_passes/src/diagnostic_items.rs @@ -79,8 +79,14 @@ fn all_diagnostic_items(tcx: TyCtxt<'_>, (): ()) -> DiagnosticItems { // Initialize the collector. let mut items = DiagnosticItems::default(); - // Collect diagnostic items in other crates. - for &cnum in tcx.crates(()).iter().chain(std::iter::once(&LOCAL_CRATE)) { + // Collect diagnostic items in visible crates. + for cnum in tcx + .crates(()) + .iter() + .copied() + .filter(|cnum| tcx.is_user_visible_dep(*cnum)) + .chain(std::iter::once(LOCAL_CRATE)) + { for (&name, &def_id) in &tcx.diagnostic_items(cnum).name_to_id { collect_item(tcx, &mut items, name, def_id); } diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index 4949a4316a7b0..22291c9282db7 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -10,9 +10,7 @@ use rustc_session::RemapFileNameExt; use rustc_session::config::{CrateType, EntryFnType, RemapPathScopeComponents, sigpipe}; use rustc_span::{Span, Symbol, sym}; -use crate::errors::{ - AttrOnlyInFunctions, ExternMain, MultipleRustcMain, MultipleStartFunctions, NoMainErr, -}; +use crate::errors::{AttrOnlyInFunctions, ExternMain, MultipleRustcMain, NoMainErr}; struct EntryContext<'tcx> { tcx: TyCtxt<'tcx>, @@ -20,9 +18,6 @@ struct EntryContext<'tcx> { /// The function has the `#[rustc_main]` attribute. rustc_main_fn: Option<(LocalDefId, Span)>, - /// The function that has the attribute `#[start]` on it. - start_fn: Option<(LocalDefId, Span)>, - /// The functions that one might think are `main` but aren't, e.g. /// main functions not defined at the top level. For diagnostics. non_main_fns: Vec, @@ -40,8 +35,7 @@ fn entry_fn(tcx: TyCtxt<'_>, (): ()) -> Option<(DefId, EntryFnType)> { return None; } - let mut ctxt = - EntryContext { tcx, rustc_main_fn: None, start_fn: None, non_main_fns: Vec::new() }; + let mut ctxt = EntryContext { tcx, rustc_main_fn: None, non_main_fns: Vec::new() }; for id in tcx.hir().items() { check_and_search_item(id, &mut ctxt); @@ -57,7 +51,7 @@ fn attr_span_by_symbol(ctxt: &EntryContext<'_>, id: ItemId, sym: Symbol) -> Opti fn check_and_search_item(id: ItemId, ctxt: &mut EntryContext<'_>) { if !matches!(ctxt.tcx.def_kind(id.owner_id), DefKind::Fn) { - for attr in [sym::start, sym::rustc_main] { + for attr in [sym::rustc_main] { if let Some(span) = attr_span_by_symbol(ctxt, id, attr) { ctxt.tcx.dcx().emit_err(AttrOnlyInFunctions { span, attr }); } @@ -91,24 +85,11 @@ fn check_and_search_item(id: ItemId, ctxt: &mut EntryContext<'_>) { }); } } - EntryPointType::Start => { - if ctxt.start_fn.is_none() { - ctxt.start_fn = Some((id.owner_id.def_id, ctxt.tcx.def_span(id.owner_id))); - } else { - ctxt.tcx.dcx().emit_err(MultipleStartFunctions { - span: ctxt.tcx.def_span(id.owner_id), - labeled: ctxt.tcx.def_span(id.owner_id.to_def_id()), - previous: ctxt.start_fn.unwrap().1, - }); - } - } } } fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId, EntryFnType)> { - if let Some((def_id, _)) = visitor.start_fn { - Some((def_id.to_def_id(), EntryFnType::Start)) - } else if let Some((local_def_id, _)) = visitor.rustc_main_fn { + if let Some((local_def_id, _)) = visitor.rustc_main_fn { let def_id = local_def_id.to_def_id(); Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx) })) } else { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index d95fa5db0ceee..9bcdd2385470c 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -688,6 +688,24 @@ pub(crate) struct RustcPubTransparent { pub span: Span, } +#[derive(Diagnostic)] +#[diag(passes_rustc_force_inline)] +pub(crate) struct RustcForceInline { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(passes_rustc_force_inline_coro)] +pub(crate) struct RustcForceInlineCoro { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(passes_link_ordinal)] pub(crate) struct LinkOrdinal { @@ -784,6 +802,8 @@ pub(crate) enum UnusedNote { NoLints { name: Symbol }, #[note(passes_unused_default_method_body_const_note)] DefaultMethodBodyConst, + #[note(passes_unused_linker_warnings_note)] + LinkerWarningsBinaryCrateOnly, } #[derive(LintDiagnostic)] @@ -1295,17 +1315,6 @@ pub(crate) struct MultipleRustcMain { pub additional: Span, } -#[derive(Diagnostic)] -#[diag(passes_multiple_start_functions, code = E0138)] -pub(crate) struct MultipleStartFunctions { - #[primary_span] - pub span: Span, - #[label] - pub labeled: Span, - #[label(passes_previous)] - pub previous: Span, -} - #[derive(Diagnostic)] #[diag(passes_extern_main)] pub(crate) struct ExternMain { @@ -1385,11 +1394,15 @@ pub(crate) struct DuplicateLangItem { impl Diagnostic<'_, G> for DuplicateLangItem { #[track_caller] fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { - let mut diag = Diag::new(dcx, level, match self.duplicate { - Duplicate::Plain => fluent::passes_duplicate_lang_item, - Duplicate::Crate => fluent::passes_duplicate_lang_item_crate, - Duplicate::CrateDepends => fluent::passes_duplicate_lang_item_crate_depends, - }); + let mut diag = Diag::new( + dcx, + level, + match self.duplicate { + Duplicate::Plain => fluent::passes_duplicate_lang_item, + Duplicate::Crate => fluent::passes_duplicate_lang_item_crate, + Duplicate::CrateDepends => fluent::passes_duplicate_lang_item_crate_depends, + }, + ); diag.code(E0152); diag.arg("lang_item_name", self.lang_item_name); diag.arg("crate_name", self.crate_name); @@ -1769,9 +1782,26 @@ pub(crate) struct IneffectiveUnstableImpl; #[derive(LintDiagnostic)] #[diag(passes_unused_assign)] -#[help] pub(crate) struct UnusedAssign { pub name: String, + #[subdiagnostic] + pub suggestion: Option, + #[help] + pub help: bool, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(passes_unused_assign_suggestion, applicability = "maybe-incorrect")] +pub(crate) struct UnusedAssignSuggestion { + pub pre: &'static str, + #[suggestion_part(code = "{pre}mut ")] + pub ty_span: Option, + #[suggestion_part(code = "")] + pub ty_ref_span: Span, + #[suggestion_part(code = "*")] + pub ident_span: Span, + #[suggestion_part(code = "")] + pub expr_ref_span: Span, } #[derive(LintDiagnostic)] diff --git a/compiler/rustc_passes/src/hir_id_validator.rs b/compiler/rustc_passes/src/hir_id_validator.rs index f6b70d9389bb9..74038b24dccd3 100644 --- a/compiler/rustc_passes/src/hir_id_validator.rs +++ b/compiler/rustc_passes/src/hir_id_validator.rs @@ -162,7 +162,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirIdValidator<'a, 'hir> { inner_visitor.check(i.owner_id, |this| intravisit::walk_impl_item(this, i)); } - fn visit_pattern_type_pattern(&mut self, p: &'hir hir::Pat<'hir>) { - self.visit_pat(p) + fn visit_pattern_type_pattern(&mut self, p: &'hir hir::TyPat<'hir>) { + intravisit::walk_ty_pat(self, p) } } diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index f9cb8c9b92716..f7cae89852e11 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -5,11 +5,10 @@ use rustc_ast::visit::BoundKind; use rustc_ast::{self as ast, NodeId, visit as ast_visit}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir as hir; -use rustc_hir::{HirId, intravisit as hir_visit}; +use rustc_data_structures::thousands::format_with_underscores; +use rustc_hir::{self as hir, AmbigArg, HirId, intravisit as hir_visit}; use rustc_middle::hir::map::Map; use rustc_middle::ty::TyCtxt; -use rustc_middle::util::common::to_readable_str; use rustc_span::Span; use rustc_span::def_id::LocalDefId; @@ -145,10 +144,10 @@ impl<'k> StatCollector<'k> { "{} {:<18}{:>10} ({:4.1}%){:>14}{:>14}", prefix, label, - to_readable_str(size), + format_with_underscores(size), percent(size, total_size), - to_readable_str(node.stats.count), - to_readable_str(node.stats.size) + format_with_underscores(node.stats.count), + format_with_underscores(node.stats.size) ); if !node.subnodes.is_empty() { // We will soon sort, so the initial order does not matter. @@ -164,9 +163,9 @@ impl<'k> StatCollector<'k> { "{} - {:<18}{:>10} ({:4.1}%){:>14}", prefix, label, - to_readable_str(size), + format_with_underscores(size), percent(size, total_size), - to_readable_str(subnode.count), + format_with_underscores(subnode.count), ); } } @@ -176,8 +175,8 @@ impl<'k> StatCollector<'k> { "{} {:<18}{:>10} {:>14}", prefix, "Total", - to_readable_str(total_size), - to_readable_str(total_count), + format_with_underscores(total_size), + format_with_underscores(total_count), ); eprintln!("{prefix}"); } @@ -231,24 +230,27 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { } fn visit_item(&mut self, i: &'v hir::Item<'v>) { - record_variants!((self, i, i.kind, Some(i.hir_id()), hir, Item, ItemKind), [ - ExternCrate, - Use, - Static, - Const, - Fn, - Macro, - Mod, - ForeignMod, - GlobalAsm, - TyAlias, - Enum, - Struct, - Union, - Trait, - TraitAlias, - Impl - ]); + record_variants!( + (self, i, i.kind, Some(i.hir_id()), hir, Item, ItemKind), + [ + ExternCrate, + Use, + Static, + Const, + Fn, + Macro, + Mod, + ForeignMod, + GlobalAsm, + TyAlias, + Enum, + Struct, + Union, + Trait, + TraitAlias, + Impl + ] + ); hir_visit::walk_item(self, i) } @@ -263,9 +265,10 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { } fn visit_foreign_item(&mut self, i: &'v hir::ForeignItem<'v>) { - record_variants!((self, i, i.kind, Some(i.hir_id()), hir, ForeignItem, ForeignItemKind), [ - Fn, Static, Type - ]); + record_variants!( + (self, i, i.kind, Some(i.hir_id()), hir, ForeignItem, ForeignItemKind), + [Fn, Static, Type] + ); hir_visit::walk_foreign_item(self, i) } @@ -280,9 +283,10 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { } fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) { - record_variants!((self, s, s.kind, Some(s.hir_id), hir, Stmt, StmtKind), [ - Let, Item, Expr, Semi - ]); + record_variants!( + (self, s, s.kind, Some(s.hir_id), hir, Stmt, StmtKind), + [Let, Item, Expr, Semi] + ); hir_visit::walk_stmt(self, s) } @@ -292,23 +296,26 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { } fn visit_pat(&mut self, p: &'v hir::Pat<'v>) { - record_variants!((self, p, p.kind, Some(p.hir_id), hir, Pat, PatKind), [ - Wild, - Binding, - Struct, - TupleStruct, - Or, - Never, - Path, - Tuple, - Box, - Deref, - Ref, - Lit, - Range, - Slice, - Err - ]); + record_variants!( + (self, p, p.kind, Some(p.hir_id), hir, Pat, PatKind), + [ + Wild, + Binding, + Struct, + TupleStruct, + Or, + Never, + Tuple, + Box, + Deref, + Ref, + Expr, + Guard, + Range, + Slice, + Err + ] + ); hir_visit::walk_pat(self, p) } @@ -318,42 +325,45 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { } fn visit_expr(&mut self, e: &'v hir::Expr<'v>) { - record_variants!((self, e, e.kind, Some(e.hir_id), hir, Expr, ExprKind), [ - ConstBlock, - Array, - Call, - MethodCall, - Tup, - Binary, - Unary, - Lit, - Cast, - Type, - DropTemps, - Let, - If, - Loop, - Match, - Closure, - Block, - Assign, - AssignOp, - Field, - Index, - Path, - AddrOf, - Break, - Continue, - Ret, - Become, - InlineAsm, - OffsetOf, - Struct, - Repeat, - Yield, - UnsafeBinderCast, - Err - ]); + record_variants!( + (self, e, e.kind, Some(e.hir_id), hir, Expr, ExprKind), + [ + ConstBlock, + Array, + Call, + MethodCall, + Tup, + Binary, + Unary, + Lit, + Cast, + Type, + DropTemps, + Let, + If, + Loop, + Match, + Closure, + Block, + Assign, + AssignOp, + Field, + Index, + Path, + AddrOf, + Break, + Continue, + Ret, + Become, + InlineAsm, + OffsetOf, + Struct, + Repeat, + Yield, + UnsafeBinderCast, + Err + ] + ); hir_visit::walk_expr(self, e) } @@ -362,26 +372,29 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { hir_visit::walk_expr_field(self, f) } - fn visit_ty(&mut self, t: &'v hir::Ty<'v>) { - record_variants!((self, t, t.kind, Some(t.hir_id), hir, Ty, TyKind), [ - InferDelegation, - Slice, - Array, - Ptr, - Ref, - BareFn, - UnsafeBinder, - Never, - Tup, - Path, - OpaqueDef, - TraitAscription, - TraitObject, - Typeof, - Infer, - Pat, - Err - ]); + fn visit_ty(&mut self, t: &'v hir::Ty<'v, AmbigArg>) { + record_variants!( + (self, t, t.kind, Some(t.hir_id), hir, Ty, TyKind), + [ + InferDelegation, + Slice, + Array, + Ptr, + Ref, + BareFn, + UnsafeBinder, + Never, + Tup, + Path, + OpaqueDef, + TraitAscription, + TraitObject, + Typeof, + Infer, + Pat, + Err + ] + ); hir_visit::walk_ty(self, t) } @@ -422,9 +435,10 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { } fn visit_trait_item(&mut self, ti: &'v hir::TraitItem<'v>) { - record_variants!((self, ti, ti.kind, Some(ti.hir_id()), hir, TraitItem, TraitItemKind), [ - Const, Fn, Type - ]); + record_variants!( + (self, ti, ti.kind, Some(ti.hir_id()), hir, TraitItem, TraitItemKind), + [Const, Fn, Type] + ); hir_visit::walk_trait_item(self, ti) } @@ -434,9 +448,10 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { } fn visit_impl_item(&mut self, ii: &'v hir::ImplItem<'v>) { - record_variants!((self, ii, ii.kind, Some(ii.hir_id()), hir, ImplItem, ImplItemKind), [ - Const, Fn, Type - ]); + record_variants!( + (self, ii, ii.kind, Some(ii.hir_id()), hir, ImplItem, ImplItemKind), + [Const, Fn, Type] + ); hir_visit::walk_impl_item(self, ii) } @@ -451,9 +466,10 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { } fn visit_param_bound(&mut self, b: &'v hir::GenericBound<'v>) { - record_variants!((self, b, b, None, hir, GenericBound, GenericBound), [ - Trait, Outlives, Use - ]); + record_variants!( + (self, b, b, None, hir, GenericBound, GenericBound), + [Trait, Outlives, Use] + ); hir_visit::walk_param_bound(self, b) } @@ -468,14 +484,15 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { } fn visit_generic_arg(&mut self, ga: &'v hir::GenericArg<'v>) { - record_variants!((self, ga, ga, Some(ga.hir_id()), hir, GenericArg, GenericArg), [ - Lifetime, Type, Const, Infer - ]); + record_variants!( + (self, ga, ga, Some(ga.hir_id()), hir, GenericArg, GenericArg), + [Lifetime, Type, Const, Infer] + ); match ga { hir::GenericArg::Lifetime(lt) => self.visit_lifetime(lt), hir::GenericArg::Type(ty) => self.visit_ty(ty), hir::GenericArg::Const(ct) => self.visit_const_arg(ct), - hir::GenericArg::Infer(inf) => self.visit_infer(inf), + hir::GenericArg::Infer(inf) => self.visit_id(inf.hir_id), } } @@ -516,34 +533,38 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { fn visit_foreign_item(&mut self, i: &'v ast::ForeignItem) { - record_variants!((self, i, i.kind, None, ast, ForeignItem, ForeignItemKind), [ - Static, Fn, TyAlias, MacCall - ]); + record_variants!( + (self, i, i.kind, None, ast, ForeignItem, ForeignItemKind), + [Static, Fn, TyAlias, MacCall] + ); ast_visit::walk_item(self, i) } fn visit_item(&mut self, i: &'v ast::Item) { - record_variants!((self, i, i.kind, None, ast, Item, ItemKind), [ - ExternCrate, - Use, - Static, - Const, - Fn, - Mod, - ForeignMod, - GlobalAsm, - TyAlias, - Enum, - Struct, - Union, - Trait, - TraitAlias, - Impl, - MacCall, - MacroDef, - Delegation, - DelegationMac - ]); + record_variants!( + (self, i, i.kind, None, ast, Item, ItemKind), + [ + ExternCrate, + Use, + Static, + Const, + Fn, + Mod, + ForeignMod, + GlobalAsm, + TyAlias, + Enum, + Struct, + Union, + Trait, + TraitAlias, + Impl, + MacCall, + MacroDef, + Delegation, + DelegationMac + ] + ); ast_visit::walk_item(self, i) } @@ -558,9 +579,10 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { } fn visit_stmt(&mut self, s: &'v ast::Stmt) { - record_variants!((self, s, s.kind, None, ast, Stmt, StmtKind), [ - Let, Item, Expr, Semi, Empty, MacCall - ]); + record_variants!( + (self, s, s.kind, None, ast, Stmt, StmtKind), + [Let, Item, Expr, Semi, Empty, MacCall] + ); ast_visit::walk_stmt(self, s) } @@ -575,27 +597,30 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { } fn visit_pat(&mut self, p: &'v ast::Pat) { - record_variants!((self, p, p.kind, None, ast, Pat, PatKind), [ - Wild, - Ident, - Struct, - TupleStruct, - Or, - Path, - Tuple, - Box, - Deref, - Ref, - Lit, - Range, - Slice, - Rest, - Never, - Guard, - Paren, - MacCall, - Err - ]); + record_variants!( + (self, p, p.kind, None, ast, Pat, PatKind), + [ + Wild, + Ident, + Struct, + TupleStruct, + Or, + Path, + Tuple, + Box, + Deref, + Ref, + Expr, + Range, + Slice, + Rest, + Never, + Guard, + Paren, + MacCall, + Err + ] + ); ast_visit::walk_pat(self, p) } @@ -615,29 +640,32 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { } fn visit_ty(&mut self, t: &'v ast::Ty) { - record_variants!((self, t, t.kind, None, ast, Ty, TyKind), [ - Slice, - Array, - Ptr, - Ref, - PinnedRef, - BareFn, - UnsafeBinder, - Never, - Tup, - Path, - Pat, - TraitObject, - ImplTrait, - Paren, - Typeof, - Infer, - ImplicitSelf, - MacCall, - CVarArgs, - Dummy, - Err - ]); + record_variants!( + (self, t, t.kind, None, ast, Ty, TyKind), + [ + Slice, + Array, + Ptr, + Ref, + PinnedRef, + BareFn, + UnsafeBinder, + Never, + Tup, + Path, + Pat, + TraitObject, + ImplTrait, + Paren, + Typeof, + Infer, + ImplicitSelf, + MacCall, + CVarArgs, + Dummy, + Err + ] + ); ast_visit::walk_ty(self, t) } @@ -648,11 +676,10 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { } fn visit_where_predicate(&mut self, p: &'v ast::WherePredicate) { - record_variants!((self, p, &p.kind, None, ast, WherePredicate, WherePredicateKind), [ - BoundPredicate, - RegionPredicate, - EqPredicate - ]); + record_variants!( + (self, p, &p.kind, None, ast, WherePredicate, WherePredicateKind), + [BoundPredicate, RegionPredicate, EqPredicate] + ); ast_visit::walk_where_predicate(self, p) } @@ -662,21 +689,18 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { } fn visit_assoc_item(&mut self, i: &'v ast::AssocItem, ctxt: ast_visit::AssocCtxt) { - record_variants!((self, i, i.kind, None, ast, AssocItem, AssocItemKind), [ - Const, - Fn, - Type, - MacCall, - Delegation, - DelegationMac - ]); + record_variants!( + (self, i, i.kind, None, ast, AssocItem, AssocItemKind), + [Const, Fn, Type, MacCall, Delegation, DelegationMac] + ); ast_visit::walk_assoc_item(self, i, ctxt); } fn visit_param_bound(&mut self, b: &'v ast::GenericBound, _ctxt: BoundKind) { - record_variants!((self, b, b, None, ast, GenericBound, GenericBound), [ - Trait, Outlives, Use - ]); + record_variants!( + (self, b, b, None, ast, GenericBound, GenericBound), + [Trait, Outlives, Use] + ); ast_visit::walk_param_bound(self, b) } @@ -709,18 +733,18 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { // common, so we implement `visit_generic_args` and tolerate the double // counting in the former case. fn visit_generic_args(&mut self, g: &'v ast::GenericArgs) { - record_variants!((self, g, g, None, ast, GenericArgs, GenericArgs), [ - AngleBracketed, - Parenthesized, - ParenthesizedElided - ]); + record_variants!( + (self, g, g, None, ast, GenericArgs, GenericArgs), + [AngleBracketed, Parenthesized, ParenthesizedElided] + ); ast_visit::walk_generic_args(self, g) } fn visit_attribute(&mut self, attr: &'v ast::Attribute) { - record_variants!((self, attr, attr.kind, None, ast, Attribute, AttrKind), [ - Normal, DocComment - ]); + record_variants!( + (self, attr, attr.kind, None, ast, Attribute, AttrKind), + [Normal, DocComment] + ); ast_visit::walk_attribute(self, attr) } diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index b85a987c64119..60f7616a5fbad 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -1360,7 +1360,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> { fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) { self.check_unused_vars_in_pat(local.pat, None, None, |spans, hir_id, ln, var| { if local.init.is_some() { - self.warn_about_dead_assign(spans, hir_id, ln, var); + self.warn_about_dead_assign(spans, hir_id, ln, var, None); } }); @@ -1460,7 +1460,8 @@ impl<'tcx> Liveness<'_, 'tcx> { // as being used. let ln = self.live_node(expr.hir_id, expr.span); let var = self.variable(var_hid, expr.span); - self.warn_about_dead_assign(vec![expr.span], expr.hir_id, ln, var); + let sugg = self.annotate_mut_binding_to_immutable_binding(var_hid, expr); + self.warn_about_dead_assign(vec![expr.span], expr.hir_id, ln, var, sugg); } } _ => { @@ -1521,6 +1522,14 @@ impl<'tcx> Liveness<'_, 'tcx> { } fn warn_about_unused_args(&self, body: &hir::Body<'_>, entry_ln: LiveNode) { + if let Some(intrinsic) = + self.ir.tcx.intrinsic(self.ir.tcx.hir().body_owner_def_id(body.id())) + { + if intrinsic.must_be_overridden { + return; + } + } + for p in body.params { self.check_unused_vars_in_pat( p.pat, @@ -1585,6 +1594,74 @@ impl<'tcx> Liveness<'_, 'tcx> { } } + /// Detect the following case + /// + /// ```text + /// fn change_object(mut a: &Ty) { + /// let a = Ty::new(); + /// b = &a; + /// } + /// ``` + /// + /// where the user likely meant to modify the value behind there reference, use `a` as an out + /// parameter, instead of mutating the local binding. When encountering this we suggest: + /// + /// ```text + /// fn change_object(a: &'_ mut Ty) { + /// let a = Ty::new(); + /// *b = a; + /// } + /// ``` + fn annotate_mut_binding_to_immutable_binding( + &self, + var_hid: HirId, + expr: &'tcx Expr<'tcx>, + ) -> Option { + if let hir::Node::Expr(parent) = self.ir.tcx.parent_hir_node(expr.hir_id) + && let hir::ExprKind::Assign(_, rhs, _) = parent.kind + && let hir::ExprKind::AddrOf(borrow_kind, _mut, inner) = rhs.kind + && let hir::BorrowKind::Ref = borrow_kind + && let hir::Node::Pat(pat) = self.ir.tcx.hir_node(var_hid) + && let hir::Node::Param(hir::Param { ty_span, .. }) = + self.ir.tcx.parent_hir_node(pat.hir_id) + && let item_id = self.ir.tcx.hir().get_parent_item(pat.hir_id) + && let item = self.ir.tcx.hir_owner_node(item_id) + && let Some(fn_decl) = item.fn_decl() + && let hir::PatKind::Binding(hir::BindingMode::MUT, _hir_id, ident, _) = pat.kind + && let Some((lt, mut_ty)) = fn_decl + .inputs + .iter() + .filter_map(|ty| { + if ty.span == *ty_span + && let hir::TyKind::Ref(lt, mut_ty) = ty.kind + { + Some((lt, mut_ty)) + } else { + None + } + }) + .next() + { + let ty_span = if mut_ty.mutbl.is_mut() { + // Leave `&'name mut Ty` and `&mut Ty` as they are (#136028). + None + } else { + // `&'name Ty` -> `&'name mut Ty` or `&Ty` -> `&mut Ty` + Some(mut_ty.ty.span.shrink_to_lo()) + }; + let pre = if lt.ident.span.lo() == lt.ident.span.hi() { "" } else { " " }; + Some(errors::UnusedAssignSuggestion { + ty_span, + pre, + ty_ref_span: pat.span.until(ident.span), + ident_span: expr.span.shrink_to_lo(), + expr_ref_span: rhs.span.until(inner.span), + }) + } else { + None + } + } + #[instrument(skip(self), level = "INFO")] fn report_unused( &self, @@ -1738,15 +1815,23 @@ impl<'tcx> Liveness<'_, 'tcx> { suggs } - fn warn_about_dead_assign(&self, spans: Vec, hir_id: HirId, ln: LiveNode, var: Variable) { + fn warn_about_dead_assign( + &self, + spans: Vec, + hir_id: HirId, + ln: LiveNode, + var: Variable, + suggestion: Option, + ) { if !self.live_on_exit(ln, var) && let Some(name) = self.should_warn(var) { + let help = suggestion.is_none(); self.ir.tcx.emit_node_span_lint( lint::builtin::UNUSED_ASSIGNMENTS, hir_id, spans, - errors::UnusedAssign { name }, + errors::UnusedAssign { name, suggestion, help }, ); } } diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs index cad488bd71d9d..875b6edb58cbc 100644 --- a/compiler/rustc_passes/src/naked_functions.rs +++ b/compiler/rustc_passes/src/naked_functions.rs @@ -1,5 +1,6 @@ //! Checks validity of naked functions. +use rustc_abi::ExternAbi; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{LocalDefId, LocalModDefId}; @@ -11,7 +12,6 @@ use rustc_middle::span_bug; use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::UNDEFINED_NAKED_FUNCTION_ABI; use rustc_span::{Span, sym}; -use rustc_target::spec::abi::Abi; use crate::errors::{ NakedAsmOutsideNakedFn, NakedFunctionsAsmBlock, NakedFunctionsMustNakedAsm, NoPatterns, @@ -30,7 +30,10 @@ fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { } let (fn_header, body_id) = match tcx.hir_node_by_def_id(def_id) { - hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn { sig, body: body_id, .. }, + .. + }) | hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), .. @@ -58,8 +61,8 @@ fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { } /// Checks that function uses non-Rust ABI. -fn check_abi(tcx: TyCtxt<'_>, def_id: LocalDefId, abi: Abi) { - if abi == Abi::Rust { +fn check_abi(tcx: TyCtxt<'_>, def_id: LocalDefId, abi: ExternAbi) { + if abi == ExternAbi::Rust { let hir_id = tcx.local_def_id_to_hir_id(def_id); let span = tcx.def_span(def_id); tcx.emit_node_span_lint( diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index 0ec151ceb4542..7788adb6e17da 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -141,7 +141,7 @@ impl<'tcx> ReachableContext<'tcx> { match self.tcx.hir_node_by_def_id(def_id) { Node::Item(item) => match item.kind { - hir::ItemKind::Fn(..) => recursively_reachable(self.tcx, def_id.into()), + hir::ItemKind::Fn { .. } => recursively_reachable(self.tcx, def_id.into()), _ => false, }, Node::TraitItem(trait_method) => match trait_method.kind { @@ -200,7 +200,7 @@ impl<'tcx> ReachableContext<'tcx> { match *node { Node::Item(item) => { match item.kind { - hir::ItemKind::Fn(.., body) => { + hir::ItemKind::Fn { body, .. } => { if recursively_reachable(self.tcx, item.owner_id.into()) { self.visit_nested_body(body); } diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index d3637dac6b7a5..fd30d0d4867fe 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -4,6 +4,7 @@ use std::mem::replace; use std::num::NonZero; +use rustc_ast_lowering::stability::extern_abi_stability; use rustc_attr_parsing::{ self as attr, ConstStability, DeprecatedSince, Stability, StabilityLevel, StableSince, UnstableReason, VERSION_PLACEHOLDER, @@ -11,20 +12,24 @@ use rustc_attr_parsing::{ use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet}; use rustc_feature::{ACCEPTED_LANG_FEATURES, EnabledLangFeature, EnabledLibFeature}; -use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalModDefId}; use rustc_hir::hir_id::CRATE_HIR_ID; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant}; +use rustc_hir::intravisit::{self, Visitor, VisitorExt}; +use rustc_hir::{self as hir, AmbigArg, FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant}; use rustc_middle::hir::nested_filter; use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures}; use rustc_middle::middle::privacy::EffectiveVisibilities; -use rustc_middle::middle::stability::{AllowUnstable, DeprecationEntry, Index}; +use rustc_middle::middle::stability::{ + AllowUnstable, Deprecated, DeprecationEntry, EvalResult, Index, +}; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_session::lint; -use rustc_session::lint::builtin::{INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED}; +use rustc_session::lint::builtin::{ + DEPRECATED, INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED, +}; use rustc_span::{Span, Symbol, sym}; use tracing::{debug, info}; @@ -428,7 +433,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { ) } } - hir::ItemKind::Fn(ref item_fn_sig, _, _) => { + hir::ItemKind::Fn { sig: ref item_fn_sig, .. } => { fn_sig = Some(item_fn_sig); } _ => {} @@ -593,9 +598,11 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> { } fn check_missing_const_stability(&self, def_id: LocalDefId, span: Span) { - let is_const = self.tcx.is_const_fn(def_id.to_def_id()); + let is_const = self.tcx.is_const_fn(def_id.to_def_id()) + || (self.tcx.def_kind(def_id.to_def_id()) == DefKind::Trait + && self.tcx.is_const_trait(def_id.to_def_id())); - // Reachable const fn must have a stability attribute. + // Reachable const fn/trait must have a stability attribute. if is_const && self.effective_visibilities.is_reachable(def_id) && self.tcx.lookup_const_stability(def_id).is_none() @@ -772,7 +779,13 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { // For implementations of traits, check the stability of each item // individually as it's possible to have a stable trait with unstable // items. - hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref t), self_ty, items, .. }) => { + hir::ItemKind::Impl(hir::Impl { + of_trait: Some(ref t), + self_ty, + items, + constness, + .. + }) => { let features = self.tcx.features(); if features.staged_api() { let attrs = self.tcx.hir().attrs(item.hir_id()); @@ -789,7 +802,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { )) = stab { let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true }; - c.visit_ty(self_ty); + c.visit_ty_unambig(self_ty); c.visit_trait_ref(t); // do not lint when the trait isn't resolved, since resolution error should @@ -814,6 +827,16 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { } } + match constness { + rustc_hir::Constness::Const => { + if let Some(def_id) = t.trait_def_id() { + // FIXME(const_trait_impl): Improve the span here. + self.tcx.check_const_stability(def_id, t.path.span, t.path.span); + } + } + rustc_hir::Constness::NotConst => {} + } + for impl_item_ref in *items { let impl_item = self.tcx.associated_item(impl_item_ref.id.owner_id); @@ -829,6 +852,18 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { intravisit::walk_item(self, item); } + fn visit_poly_trait_ref(&mut self, t: &'tcx hir::PolyTraitRef<'tcx>) { + match t.modifiers.constness { + hir::BoundConstness::Always(span) | hir::BoundConstness::Maybe(span) => { + if let Some(def_id) = t.trait_ref.trait_def_id() { + self.tcx.check_const_stability(def_id, t.trait_ref.path.span, span); + } + } + hir::BoundConstness::Never => {} + } + intravisit::walk_poly_trait_ref(self, t); + } + fn visit_path(&mut self, path: &hir::Path<'tcx>, id: hir::HirId) { if let Some(def_id) = path.res.opt_def_id() { let method_span = path.segments.last().map(|s| s.ident.span); @@ -844,18 +879,18 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { }, ); - let is_allowed_through_unstable_modules = |def_id| { - self.tcx.lookup_stability(def_id).is_some_and(|stab| match stab.level { - StabilityLevel::Stable { allowed_through_unstable_modules, .. } => { - allowed_through_unstable_modules - } - _ => false, - }) - }; + if item_is_allowed { + // The item itself is allowed; check whether the path there is also allowed. + let is_allowed_through_unstable_modules: Option = + self.tcx.lookup_stability(def_id).and_then(|stab| match stab.level { + StabilityLevel::Stable { allowed_through_unstable_modules, .. } => { + allowed_through_unstable_modules + } + _ => None, + }); - if item_is_allowed && !is_allowed_through_unstable_modules(def_id) { // Check parent modules stability as well if the item the path refers to is itself - // stable. We only emit warnings for unstable path segments if the item is stable + // stable. We only emit errors for unstable path segments if the item is stable // or allowed because stability is often inherited, so the most common case is that // both the segments and the item are unstable behind the same feature flag. // @@ -868,18 +903,67 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { let parents = path.segments.iter().rev().skip(1); for path_segment in parents { if let Some(def_id) = path_segment.res.opt_def_id() { - // use `None` for id to prevent deprecation check - self.tcx.check_stability_allow_unstable( - def_id, - None, - path.span, - None, - if is_unstable_reexport(self.tcx, id) { - AllowUnstable::Yes - } else { - AllowUnstable::No - }, - ); + match is_allowed_through_unstable_modules { + None => { + // Emit a hard stability error if this path is not stable. + + // use `None` for id to prevent deprecation check + self.tcx.check_stability_allow_unstable( + def_id, + None, + path.span, + None, + if is_unstable_reexport(self.tcx, id) { + AllowUnstable::Yes + } else { + AllowUnstable::No + }, + ); + } + Some(deprecation) => { + // Call the stability check directly so that we can control which + // diagnostic is emitted. + let eval_result = self.tcx.eval_stability_allow_unstable( + def_id, + None, + path.span, + None, + if is_unstable_reexport(self.tcx, id) { + AllowUnstable::Yes + } else { + AllowUnstable::No + }, + ); + let is_allowed = matches!(eval_result, EvalResult::Allow); + if !is_allowed { + // Calculating message for lint involves calling `self.def_path_str`, + // which will by default invoke the expensive `visible_parent_map` query. + // Skip all that work if the lint is allowed anyway. + if self.tcx.lint_level_at_node(DEPRECATED, id).0 + == lint::Level::Allow + { + return; + } + // Show a deprecation message. + let def_path = + with_no_trimmed_paths!(self.tcx.def_path_str(def_id)); + let def_kind = self.tcx.def_descr(def_id); + let diag = Deprecated { + sub: None, + kind: def_kind.to_owned(), + path: def_path, + note: Some(deprecation), + since_kind: lint::DeprecatedSinceKind::InEffect, + }; + self.tcx.emit_node_span_lint( + DEPRECATED, + id, + method_span.unwrap_or(path.span), + diag, + ); + } + } + } } } } @@ -940,12 +1024,12 @@ impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> { intravisit::walk_trait_ref(self, t) } - fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) { + fn visit_ty(&mut self, t: &'tcx Ty<'tcx, AmbigArg>) { if let TyKind::Never = t.kind { self.fully_stable = false; } - if let TyKind::BareFn(f) = t.kind { - if rustc_target::spec::abi::is_stable(f.abi.name()).is_err() { + if let TyKind::BareFn(function) = t.kind { + if extern_abi_stability(function.abi).is_err() { self.fully_stable = false; } } @@ -954,12 +1038,12 @@ impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> { fn visit_fn_decl(&mut self, fd: &'tcx hir::FnDecl<'tcx>) { for ty in fd.inputs { - self.visit_ty(ty) + self.visit_ty_unambig(ty) } if let hir::FnRetTy::Return(output_ty) = fd.output { match output_ty.kind { TyKind::Never => {} // `-> !` is stable - _ => self.visit_ty(output_ty), + _ => self.visit_ty_unambig(output_ty), } } } diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs index 020128f29c59b..701f500e4f60b 100644 --- a/compiler/rustc_passes/src/weak_lang_items.rs +++ b/compiler/rustc_passes/src/weak_lang_items.rs @@ -26,7 +26,10 @@ pub(crate) fn check_crate( if items.eh_personality().is_none() { items.missing.push(LangItem::EhPersonality); } - if tcx.sess.target.os == "emscripten" && items.eh_catch_typeinfo().is_none() { + if tcx.sess.target.os == "emscripten" + && items.eh_catch_typeinfo().is_none() + && !tcx.sess.opts.unstable_opts.emscripten_wasm_eh + { items.missing.push(LangItem::EhCatchTypeinfo); } diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 8fce42663453a..4ce868f014f42 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -182,7 +182,7 @@ use std::iter::once; use rustc_apfloat::ieee::{DoubleS, HalfS, IeeeFloat, QuadS, SingleS}; use rustc_index::IndexVec; -use rustc_index::bit_set::{BitSet, GrowableBitSet}; +use rustc_index::bit_set::{DenseBitSet, GrowableBitSet}; use smallvec::SmallVec; use self::Constructor::*; @@ -1072,7 +1072,7 @@ impl ConstructorSet { } } ConstructorSet::Variants { variants, non_exhaustive } => { - let mut seen_set = BitSet::new_empty(variants.len()); + let mut seen_set = DenseBitSet::new_empty(variants.len()); for idx in seen.iter().filter_map(|c| c.as_variant()) { seen_set.insert(idx); } diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index ae991e3ce4030..2694cf472f480 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -794,9 +794,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { // fictitious values after `{u,i}size::MAX` (see [`IntRange::split`] for why we do // this). We show this to the user as `usize::MAX..` which is slightly incorrect but // probably clear enough. - let c = ty.numeric_max_val(cx.tcx).unwrap(); - let value = mir::Const::from_ty_const(c, ty.0, cx.tcx); - lo = PatRangeBoundary::Finite(value); + lo = PatRangeBoundary::Finite(ty.numeric_max_val(cx.tcx).unwrap()); } let hi = if let Some(hi) = range.hi.minus_one() { hi diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 99261eaa95cc1..cc09cd491af19 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -712,7 +712,7 @@ use std::fmt; #[cfg(feature = "rustc")] use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hash::{FxHashMap, FxHashSet}; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; @@ -1129,7 +1129,7 @@ struct MatrixRow<'p, Cx: PatCx> { /// ``` /// Here the `(true, true)` case is irrelevant. Since we skip it, we will not detect that row 0 /// intersects rows 1 and 2. - intersects_at_least: BitSet, + intersects_at_least: DenseBitSet, /// Whether the head pattern is a branch (see definition of "branch pattern" at /// [`BranchPatUsefulness`]) head_is_branch: bool, @@ -1142,7 +1142,7 @@ impl<'p, Cx: PatCx> MatrixRow<'p, Cx> { parent_row: arm_id, is_under_guard: arm.has_guard, useful: false, - intersects_at_least: BitSet::new_empty(0), // Initialized in `Matrix::push`. + intersects_at_least: DenseBitSet::new_empty(0), // Initialized in `Matrix::push`. // This pattern is a branch because it comes from a match arm. head_is_branch: true, } @@ -1171,7 +1171,7 @@ impl<'p, Cx: PatCx> MatrixRow<'p, Cx> { parent_row, is_under_guard: self.is_under_guard, useful: false, - intersects_at_least: BitSet::new_empty(0), // Initialized in `Matrix::push`. + intersects_at_least: DenseBitSet::new_empty(0), // Initialized in `Matrix::push`. head_is_branch: is_or_pat, }) } @@ -1191,7 +1191,7 @@ impl<'p, Cx: PatCx> MatrixRow<'p, Cx> { parent_row, is_under_guard: self.is_under_guard, useful: false, - intersects_at_least: BitSet::new_empty(0), // Initialized in `Matrix::push`. + intersects_at_least: DenseBitSet::new_empty(0), // Initialized in `Matrix::push`. head_is_branch: false, }) } @@ -1230,7 +1230,7 @@ struct Matrix<'p, Cx: PatCx> { impl<'p, Cx: PatCx> Matrix<'p, Cx> { /// Pushes a new row to the matrix. Internal method, prefer [`Matrix::new`]. fn push(&mut self, mut row: MatrixRow<'p, Cx>) { - row.intersects_at_least = BitSet::new_empty(self.rows.len()); + row.intersects_at_least = DenseBitSet::new_empty(self.rows.len()); self.rows.push(row); } @@ -1824,7 +1824,7 @@ pub struct UsefulnessReport<'p, Cx: PatCx> { pub non_exhaustiveness_witnesses: Vec>, /// For each arm, a set of indices of arms above it that have non-empty intersection, i.e. there /// is a value matched by both arms. This may miss real intersections. - pub arm_intersections: Vec>, + pub arm_intersections: Vec>, } /// Computes whether a match is exhaustive and which of its arms are useful. diff --git a/compiler/rustc_privacy/messages.ftl b/compiler/rustc_privacy/messages.ftl index 7785f1a7f81fb..43c34a109d721 100644 --- a/compiler/rustc_privacy/messages.ftl +++ b/compiler/rustc_privacy/messages.ftl @@ -1,5 +1,19 @@ -privacy_field_is_private = field `{$field_name}` of {$variant_descr} `{$def_path_str}` is private -privacy_field_is_private_is_update_syntax_label = field `{$field_name}` is private +privacy_field_is_private = + {$len -> + [1] field + *[other] fields + } {$field_names} of {$variant_descr} `{$def_path_str}` {$len -> + [1] is + *[other] are + } private + .label = in this type +privacy_field_is_private_is_update_syntax_label = {$rest_len -> + [1] field + *[other] fields + } {$rest_field_names} {$rest_len -> + [1] is + *[other] are + } private privacy_field_is_private_label = private field privacy_from_private_dep_in_public_interface = diff --git a/compiler/rustc_privacy/src/errors.rs b/compiler/rustc_privacy/src/errors.rs index f5e641eb64243..4d1d58c08528c 100644 --- a/compiler/rustc_privacy/src/errors.rs +++ b/compiler/rustc_privacy/src/errors.rs @@ -1,5 +1,5 @@ -use rustc_errors::DiagArgFromDisplay; use rustc_errors::codes::*; +use rustc_errors::{DiagArgFromDisplay, MultiSpan}; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; @@ -7,12 +7,15 @@ use rustc_span::{Span, Symbol}; #[diag(privacy_field_is_private, code = E0451)] pub(crate) struct FieldIsPrivate { #[primary_span] - pub span: Span, - pub field_name: Symbol, + pub span: MultiSpan, + #[label] + pub struct_span: Option, + pub field_names: String, pub variant_descr: &'static str, pub def_path_str: String, #[subdiagnostic] - pub label: FieldIsPrivateLabel, + pub labels: Vec, + pub len: usize, } #[derive(Subdiagnostic)] @@ -21,7 +24,8 @@ pub(crate) enum FieldIsPrivateLabel { IsUpdateSyntax { #[primary_span] span: Span, - field_name: Symbol, + rest_field_names: String, + rest_len: usize, }, #[label(privacy_field_is_private_label)] Other { diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 9ae2d981ab060..3842b7035e537 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -24,10 +24,11 @@ use rustc_ast::MacroDef; use rustc_ast::visit::{VisitorResult, try_visit}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::intern::Interned; +use rustc_errors::{MultiSpan, listify}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId, LocalModDefId}; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{AssocItemKind, ForeignItemKind, ItemId, ItemKind, PatKind}; +use rustc_hir::intravisit::{self, InferKind, Visitor}; +use rustc_hir::{AmbigArg, AssocItemKind, ForeignItemKind, ItemId, ItemKind, PatKind}; use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility, Level}; use rustc_middle::query::Providers; use rustc_middle::ty::print::PrintTraitRefExt as _; @@ -38,7 +39,7 @@ use rustc_middle::ty::{ use rustc_middle::{bug, span_bug}; use rustc_session::lint; use rustc_span::hygiene::Transparency; -use rustc_span::{Ident, Span, kw, sym}; +use rustc_span::{Ident, Span, Symbol, kw, sym}; use tracing::debug; use {rustc_attr_parsing as attr, rustc_hir as hir}; @@ -654,7 +655,7 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { } hir::ItemKind::Const(..) | hir::ItemKind::Static(..) - | hir::ItemKind::Fn(..) + | hir::ItemKind::Fn { .. } | hir::ItemKind::TyAlias(..) => { if let Some(item_ev) = item_ev { self.reach(item.owner_id.def_id, item_ev).generics().predicates().ty(); @@ -921,31 +922,81 @@ impl<'tcx> NamePrivacyVisitor<'tcx> { &mut self, hir_id: hir::HirId, // ID of the field use use_ctxt: Span, // syntax context of the field name at the use site - span: Span, // span of the field pattern, e.g., `x: 0` def: ty::AdtDef<'tcx>, // definition of the struct or enum field: &'tcx ty::FieldDef, - in_update_syntax: bool, - ) { + ) -> bool { if def.is_enum() { - return; + return true; } // definition of the field let ident = Ident::new(kw::Empty, use_ctxt); let def_id = self.tcx.adjust_ident_and_get_scope(ident, def.did(), hir_id).1; - if !field.vis.is_accessible_from(def_id, self.tcx) { - self.tcx.dcx().emit_err(FieldIsPrivate { - span, - field_name: field.name, - variant_descr: def.variant_descr(), - def_path_str: self.tcx.def_path_str(def.did()), - label: if in_update_syntax { - FieldIsPrivateLabel::IsUpdateSyntax { span, field_name: field.name } - } else { - FieldIsPrivateLabel::Other { span } - }, - }); + !field.vis.is_accessible_from(def_id, self.tcx) + } + + // Checks that a field in a struct constructor (expression or pattern) is accessible. + fn emit_unreachable_field_error( + &mut self, + fields: Vec<(Symbol, Span, bool /* field is present */)>, + def: ty::AdtDef<'tcx>, // definition of the struct or enum + update_syntax: Option, + struct_span: Span, + ) { + if def.is_enum() || fields.is_empty() { + return; } + + // error[E0451]: fields `beta` and `gamma` of struct `Alpha` are private + // --> $DIR/visibility.rs:18:13 + // | + // LL | let _x = Alpha { + // | ----- in this type # from `def` + // LL | beta: 0, + // | ^^^^^^^ private field # `fields.2` is `true` + // LL | .. + // | ^^ field `gamma` is private # `fields.2` is `false` + + // Get the list of all private fields for the main message. + let Some(field_names) = listify(&fields[..], |(n, _, _)| format!("`{n}`")) else { return }; + let span: MultiSpan = fields.iter().map(|(_, span, _)| *span).collect::>().into(); + + // Get the list of all private fields when pointing at the `..rest`. + let rest_field_names: Vec<_> = + fields.iter().filter(|(_, _, is_present)| !is_present).map(|(n, _, _)| n).collect(); + let rest_len = rest_field_names.len(); + let rest_field_names = + listify(&rest_field_names[..], |n| format!("`{n}`")).unwrap_or_default(); + // Get all the labels for each field or `..rest` in the primary MultiSpan. + let labels = fields + .iter() + .filter(|(_, _, is_present)| *is_present) + .map(|(_, span, _)| FieldIsPrivateLabel::Other { span: *span }) + .chain(update_syntax.iter().map(|span| FieldIsPrivateLabel::IsUpdateSyntax { + span: *span, + rest_field_names: rest_field_names.clone(), + rest_len, + })) + .collect(); + + self.tcx.dcx().emit_err(FieldIsPrivate { + span, + struct_span: if self + .tcx + .sess + .source_map() + .is_multiline(fields[0].1.between(struct_span)) + { + Some(struct_span) + } else { + None + }, + field_names, + variant_descr: def.variant_descr(), + def_path_str: self.tcx.def_path_str(def.did()), + labels, + len: fields.len(), + }); } fn check_expanded_fields( @@ -955,7 +1006,9 @@ impl<'tcx> NamePrivacyVisitor<'tcx> { fields: &[hir::ExprField<'tcx>], hir_id: hir::HirId, span: Span, + struct_span: Span, ) { + let mut failed_fields = vec![]; for (vf_index, variant_field) in variant.fields.iter_enumerated() { let field = fields.iter().find(|f| self.typeck_results().field_index(f.hir_id) == vf_index); @@ -963,8 +1016,15 @@ impl<'tcx> NamePrivacyVisitor<'tcx> { Some(field) => (field.hir_id, field.ident.span, field.span), None => (hir_id, span, span), }; - self.check_field(hir_id, use_ctxt, span, adt, variant_field, true); + if self.check_field(hir_id, use_ctxt, adt, variant_field) { + let name = match field { + Some(field) => field.ident.name, + None => variant_field.name, + }; + failed_fields.push((name, span, field.is_some())); + } } + self.emit_unreachable_field_error(failed_fields, adt, Some(span), struct_span); } } @@ -990,24 +1050,35 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> { // If the expression uses FRU we need to make sure all the unmentioned fields // are checked for privacy (RFC 736). Rather than computing the set of // unmentioned fields, just check them all. - self.check_expanded_fields(adt, variant, fields, base.hir_id, base.span); + self.check_expanded_fields( + adt, + variant, + fields, + base.hir_id, + base.span, + qpath.span(), + ); } hir::StructTailExpr::DefaultFields(span) => { - self.check_expanded_fields(adt, variant, fields, expr.hir_id, span); + self.check_expanded_fields( + adt, + variant, + fields, + expr.hir_id, + span, + qpath.span(), + ); } hir::StructTailExpr::None => { + let mut failed_fields = vec![]; for field in fields { - let (hir_id, use_ctxt, span) = (field.hir_id, field.ident.span, field.span); + let (hir_id, use_ctxt) = (field.hir_id, field.ident.span); let index = self.typeck_results().field_index(field.hir_id); - self.check_field( - hir_id, - use_ctxt, - span, - adt, - &variant.fields[index], - false, - ); + if self.check_field(hir_id, use_ctxt, adt, &variant.fields[index]) { + failed_fields.push((field.ident.name, field.ident.span, true)); + } } + self.emit_unreachable_field_error(failed_fields, adt, None, qpath.span()); } } } @@ -1020,11 +1091,15 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> { let res = self.typeck_results().qpath_res(qpath, pat.hir_id); let adt = self.typeck_results().pat_ty(pat).ty_adt_def().unwrap(); let variant = adt.variant_of_res(res); + let mut failed_fields = vec![]; for field in fields { - let (hir_id, use_ctxt, span) = (field.hir_id, field.ident.span, field.span); + let (hir_id, use_ctxt) = (field.hir_id, field.ident.span); let index = self.typeck_results().field_index(field.hir_id); - self.check_field(hir_id, use_ctxt, span, adt, &variant.fields[index], false); + if self.check_field(hir_id, use_ctxt, adt, &variant.fields[index]) { + failed_fields.push((field.ident.name, field.ident.span, true)); + } } + self.emit_unreachable_field_error(failed_fields, adt, None, qpath.span()); } intravisit::walk_pat(self, pat); @@ -1090,7 +1165,7 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { self.maybe_typeck_results = old_maybe_typeck_results; } - fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx, AmbigArg>) { self.span = hir_ty.span; if self .visit( @@ -1106,12 +1181,17 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { intravisit::walk_ty(self, hir_ty); } - fn visit_infer(&mut self, inf: &'tcx hir::InferArg) { - self.span = inf.span; + fn visit_infer( + &mut self, + inf_id: rustc_hir::HirId, + inf_span: Span, + _kind: InferKind<'tcx>, + ) -> Self::Result { + self.span = inf_span; if let Some(ty) = self .maybe_typeck_results - .unwrap_or_else(|| span_bug!(inf.span, "`hir::InferArg` outside of a body")) - .node_type_opt(inf.hir_id) + .unwrap_or_else(|| span_bug!(inf_span, "Inference variable outside of a body")) + .node_type_opt(inf_id) { if self.visit(ty).is_break() { return; @@ -1119,7 +1199,8 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { } else { // FIXME: check types of const infers here. } - intravisit::walk_inf(self, inf); + + self.visit_id(inf_id) } // Check types of expressions diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml index 6e8fd32610bc1..8b0cc9d726b80 100644 --- a/compiler/rustc_query_impl/Cargo.toml +++ b/compiler/rustc_query_impl/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -field-offset = "0.3.5" measureme = "11" rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index d2bb0b3f27719..73c205fdb17d1 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -11,7 +11,6 @@ #![warn(unreachable_pub)] // tidy-alphabetical-end -use field_offset::offset_of; use rustc_data_structures::stable_hasher::HashStable; use rustc_data_structures::sync::AtomicU64; use rustc_middle::arena::Arena; @@ -89,7 +88,13 @@ where where QueryCtxt<'tcx>: 'a, { - self.dynamic.query_state.apply(&qcx.tcx.query_system.states) + // Safety: + // This is just manually doing the subfield referencing through pointer math. + unsafe { + &*(&qcx.tcx.query_system.states as *const QueryStates<'tcx>) + .byte_add(self.dynamic.query_state) + .cast::>() + } } #[inline(always)] @@ -97,7 +102,13 @@ where where 'tcx: 'a, { - self.dynamic.query_cache.apply(&qcx.tcx.query_system.caches) + // Safety: + // This is just manually doing the subfield referencing through pointer math. + unsafe { + &*(&qcx.tcx.query_system.caches as *const QueryCaches<'tcx>) + .byte_add(self.dynamic.query_cache) + .cast::() + } } #[inline(always)] @@ -224,7 +235,6 @@ pub fn query_system<'a>( rustc_middle::rustc_query_append! { define_queries! } pub fn provide(providers: &mut rustc_middle::util::Providers) { - providers.hooks.alloc_self_profile_query_strings = - |tcx| alloc_self_profile_query_strings(tcx.tcx); - providers.hooks.query_key_hash_verify_all = |tcx| query_key_hash_verify_all(tcx.tcx); + providers.hooks.alloc_self_profile_query_strings = alloc_self_profile_query_strings; + providers.hooks.query_key_hash_verify_all = query_key_hash_verify_all; } diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 1b12af62ea5a9..e95c186f6b680 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -27,7 +27,7 @@ use rustc_query_system::query::{ QueryCache, QueryConfig, QueryContext, QueryJobId, QueryMap, QuerySideEffects, QueryStackFrame, force_query, }; -use rustc_query_system::{LayoutOfDepth, QueryOverflow}; +use rustc_query_system::{QueryOverflow, QueryOverflowNote}; use rustc_serialize::{Decodable, Encodable}; use rustc_session::Limit; use rustc_span::def_id::LOCAL_CRATE; @@ -153,14 +153,7 @@ impl QueryContext for QueryCtxt<'_> { } fn depth_limit_error(self, job: QueryJobId) { - let mut span = None; - let mut layout_of_depth = None; - if let Some((info, depth)) = - job.try_find_layout_root(self.collect_active_jobs(), dep_kinds::layout_of) - { - span = Some(info.job.span); - layout_of_depth = Some(LayoutOfDepth { desc: info.query.description, depth }); - } + let (info, depth) = job.find_dep_kind_root(self.collect_active_jobs()); let suggested_limit = match self.recursion_limit() { Limit(0) => Limit(2), @@ -168,8 +161,8 @@ impl QueryContext for QueryCtxt<'_> { }; self.sess.dcx().emit_fatal(QueryOverflow { - span, - layout_of_depth, + span: info.job.span, + note: QueryOverflowNote { desc: info.query.description, depth }, suggested_limit, crate_name: self.crate_name(LOCAL_CRATE), }); @@ -612,8 +605,8 @@ macro_rules! define_queries { eval_always: is_eval_always!([$($modifiers)*]), dep_kind: dep_graph::dep_kinds::$name, handle_cycle_error: handle_cycle_error!([$($modifiers)*]), - query_state: offset_of!(QueryStates<'tcx> => $name), - query_cache: offset_of!(QueryCaches<'tcx> => $name), + query_state: std::mem::offset_of!(QueryStates<'tcx>, $name), + query_cache: std::mem::offset_of!(QueryCaches<'tcx>, $name), cache_on_disk: |tcx, key| ::rustc_middle::query::cached::$name(tcx, key), execute_query: |tcx, key| erase(tcx.$name(key)), compute: |tcx, key| { diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml index 96b210accdb37..a42329b4614f0 100644 --- a/compiler/rustc_query_system/Cargo.toml +++ b/compiler/rustc_query_system/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" # tidy-alphabetical-start parking_lot = "0.12" rustc-rayon-core = { version = "0.5.0" } +rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } @@ -18,7 +19,6 @@ rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } -rustc_target = { path = "../rustc_target" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } thin-vec = "0.2.12" tracing = "0.1" diff --git a/compiler/rustc_query_system/messages.ftl b/compiler/rustc_query_system/messages.ftl index d7ab755751118..f48dc60afa0e7 100644 --- a/compiler/rustc_query_system/messages.ftl +++ b/compiler/rustc_query_system/messages.ftl @@ -21,7 +21,7 @@ query_system_increment_compilation = internal compiler error: encountered increm query_system_increment_compilation_note1 = Please follow the instructions below to create a bug report with the provided information query_system_increment_compilation_note2 = See for more information -query_system_layout_of_depth = query depth increased by {$depth} when {$desc} +query_system_overflow_note = query depth increased by {$depth} when {$desc} query_system_query_overflow = queries overflow the depth limit! .help = consider increasing the recursion limit by adding a `#![recursion_limit = "{$suggested_limit}"]` attribute to your crate (`{$crate_name}`) diff --git a/compiler/rustc_query_system/src/dep_graph/dep_node.rs b/compiler/rustc_query_system/src/dep_graph/dep_node.rs index 2902d17d68730..9d3368607a21c 100644 --- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs +++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs @@ -1,19 +1,20 @@ -//! This module defines the `DepNode` type which the compiler uses to represent -//! nodes in the dependency graph. A `DepNode` consists of a `DepKind` (which -//! specifies the kind of thing it represents, like a piece of HIR, MIR, etc) -//! and a `Fingerprint`, a 128 bit hash value the exact meaning of which +//! This module defines the [`DepNode`] type which the compiler uses to represent +//! nodes in the [dependency graph]. A `DepNode` consists of a [`DepKind`] (which +//! specifies the kind of thing it represents, like a piece of HIR, MIR, etc.) +//! and a [`Fingerprint`], a 128-bit hash value, the exact meaning of which //! depends on the node's `DepKind`. Together, the kind and the fingerprint //! fully identify a dependency node, even across multiple compilation sessions. //! In other words, the value of the fingerprint does not depend on anything //! that is specific to a given compilation session, like an unpredictable -//! interning key (e.g., NodeId, DefId, Symbol) or the numeric value of a +//! interning key (e.g., `NodeId`, `DefId`, `Symbol`) or the numeric value of a //! pointer. The concept behind this could be compared to how git commit hashes -//! uniquely identify a given commit and has a few advantages: +//! uniquely identify a given commit. The fingerprinting approach has +//! a few advantages: //! //! * A `DepNode` can simply be serialized to disk and loaded in another session -//! without the need to do any "rebasing (like we have to do for Spans and -//! NodeIds) or "retracing" like we had to do for `DefId` in earlier -//! implementations of the dependency graph. +//! without the need to do any "rebasing" (like we have to do for Spans and +//! NodeIds) or "retracing" (like we had to do for `DefId` in earlier +//! implementations of the dependency graph). //! * A `Fingerprint` is just a bunch of bits, which allows `DepNode` to //! implement `Copy`, `Sync`, `Send`, `Freeze`, etc. //! * Since we just have a bit pattern, `DepNode` can be mapped from disk into @@ -26,10 +27,12 @@ //! could not be instantiated because the current compilation session //! contained no `DefId` for thing that had been removed. //! -//! `DepNode` definition happens in `rustc_middle` with the `define_dep_nodes!()` macro. -//! This macro defines the `DepKind` enum and a corresponding `DepConstructor` enum. The -//! `DepConstructor` enum links a `DepKind` to the parameters that are needed at runtime in order -//! to construct a valid `DepNode` fingerprint. +//! `DepNode` definition happens in `rustc_middle` with the +//! `define_dep_nodes!()` macro. This macro defines the `DepKind` enum. Each +//! `DepKind` has its own parameters that are needed at runtime in order to +//! construct a valid `DepNode` fingerprint. However, only `CompileCodegenUnit` +//! and `CompileMonoItem` are constructed explicitly (with +//! `make_compile_codegen_unit` and `make_compile_mono_item`). //! //! Because the macro sees what parameters a given `DepKind` requires, it can //! "infer" some properties for each kind of `DepNode`: @@ -41,6 +44,16 @@ //! in which case it is possible to map the node's fingerprint back to the //! `DefId` it was computed from. In other cases, too much information gets //! lost during fingerprint computation. +//! +//! `make_compile_codegen_unit` and `make_compile_mono_items`, together with +//! `DepNode::new()`, ensure that only valid `DepNode` instances can be +//! constructed. For example, the API does not allow for constructing +//! parameterless `DepNode`s with anything other than a zeroed out fingerprint. +//! More generally speaking, it relieves the user of the `DepNode` API of +//! having to know how to compute the expected fingerprint for a given set of +//! node parameters. +//! +//! [dependency graph]: https://rustc-dev-guide.rust-lang.org/query.html use std::fmt; use std::hash::Hash; diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 4b47ce8389c3d..5e6bee1dbd5a1 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -4,14 +4,14 @@ use std::fmt::Debug; use std::hash::Hash; use std::marker::PhantomData; use std::sync::Arc; -use std::sync::atomic::Ordering; +use std::sync::atomic::{AtomicU32, Ordering}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::profiling::{QueryInvocationId, SelfProfilerRef}; use rustc_data_structures::sharded::{self, Sharded}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc}; +use rustc_data_structures::sync::{AtomicU64, Lock}; use rustc_data_structures::unord::UnordMap; use rustc_index::IndexVec; use rustc_macros::{Decodable, Encodable}; @@ -29,13 +29,13 @@ use crate::query::{QueryContext, QuerySideEffects}; #[derive(Clone)] pub struct DepGraph { - data: Option>>, + data: Option>>, /// This field is used for assigning DepNodeIndices when running in /// non-incremental mode. Even in non-incremental mode we make sure that /// each task has a `DepNodeIndex` that uniquely identifies it. This unique /// ID is used for self-profiling. - virtual_dep_node_index: Lrc, + virtual_dep_node_index: Arc, } rustc_index::newtype_index! { @@ -171,7 +171,7 @@ impl DepGraph { } DepGraph { - data: Some(Lrc::new(DepGraphData { + data: Some(Arc::new(DepGraphData { previous_work_products: prev_work_products, dep_node_debug: Default::default(), current, @@ -180,12 +180,12 @@ impl DepGraph { colors, debug_loaded_from_disk: Default::default(), })), - virtual_dep_node_index: Lrc::new(AtomicU32::new(0)), + virtual_dep_node_index: Arc::new(AtomicU32::new(0)), } } pub fn new_disabled() -> DepGraph { - DepGraph { data: None, virtual_dep_node_index: Lrc::new(AtomicU32::new(0)) } + DepGraph { data: None, virtual_dep_node_index: Arc::new(AtomicU32::new(0)) } } #[inline] @@ -376,25 +376,8 @@ impl DepGraphData { }; let dcx = cx.dep_context(); - let hashing_timer = dcx.profiler().incr_result_hashing(); - let current_fingerprint = - hash_result.map(|f| dcx.with_stable_hashing_context(|mut hcx| f(&mut hcx, &result))); - - // Intern the new `DepNode`. - let (dep_node_index, prev_and_color) = - self.current.intern_node(&self.previous, key, edges, current_fingerprint); - - hashing_timer.finish_with_query_invocation_id(dep_node_index.into()); - - if let Some((prev_index, color)) = prev_and_color { - debug_assert!( - self.colors.get(prev_index).is_none(), - "DepGraph::with_task() - Duplicate DepNodeColor \ - insertion for {key:?}" - ); - - self.colors.insert(prev_index, color); - } + let dep_node_index = + self.hash_result_and_intern_node(dcx, key, edges, &result, hash_result); (result, dep_node_index) } @@ -462,6 +445,38 @@ impl DepGraphData { (result, dep_node_index) } + + /// Intern the new `DepNode` with the dependencies up-to-now. + fn hash_result_and_intern_node, R>( + &self, + cx: &Ctxt, + node: DepNode, + edges: EdgesVec, + result: &R, + hash_result: Option, &R) -> Fingerprint>, + ) -> DepNodeIndex { + let hashing_timer = cx.profiler().incr_result_hashing(); + let current_fingerprint = hash_result.map(|hash_result| { + cx.with_stable_hashing_context(|mut hcx| hash_result(&mut hcx, result)) + }); + + // Intern the new `DepNode` with the dependencies up-to-now. + let (dep_node_index, prev_and_color) = + self.current.intern_node(&self.previous, node, edges, current_fingerprint); + + hashing_timer.finish_with_query_invocation_id(dep_node_index.into()); + + if let Some((prev_index, color)) = prev_and_color { + debug_assert!( + self.colors.get(prev_index).is_none(), + "DepGraph::with_task() - Duplicate DepNodeColor insertion for {node:?}", + ); + + self.colors.insert(prev_index, color); + } + + dep_node_index + } } impl DepGraph { @@ -536,11 +551,10 @@ impl DepGraph { /// FIXME: If the code is changed enough for this node to be marked before requiring the /// caller's node, we suppose that those changes will be enough to mark this node red and /// force a recomputation using the "normal" way. - pub fn with_feed_task, A: Debug, R: Debug>( + pub fn with_feed_task, R: Debug>( &self, node: DepNode, cx: Ctxt, - key: A, result: &R, hash_result: Option, &R) -> Fingerprint>, ) -> DepNodeIndex { @@ -588,27 +602,7 @@ impl DepGraph { } }); - let hashing_timer = cx.profiler().incr_result_hashing(); - let current_fingerprint = hash_result.map(|hash_result| { - cx.with_stable_hashing_context(|mut hcx| hash_result(&mut hcx, result)) - }); - - // Intern the new `DepNode` with the dependencies up-to-now. - let (dep_node_index, prev_and_color) = - data.current.intern_node(&data.previous, node, edges, current_fingerprint); - - hashing_timer.finish_with_query_invocation_id(dep_node_index.into()); - - if let Some((prev_index, color)) = prev_and_color { - debug_assert!( - data.colors.get(prev_index).is_none(), - "DepGraph::with_task() - Duplicate DepNodeColor insertion for {key:?}", - ); - - data.colors.insert(prev_index, color); - } - - dep_node_index + data.hash_result_and_intern_node(&cx, node, edges, result, hash_result) } else { // Incremental compilation is turned off. We just execute the task // without tracking. We still provide a dep-node index that uniquely diff --git a/compiler/rustc_query_system/src/error.rs b/compiler/rustc_query_system/src/error.rs index 860f2e66915f3..5108ecaeea3a1 100644 --- a/compiler/rustc_query_system/src/error.rs +++ b/compiler/rustc_query_system/src/error.rs @@ -82,16 +82,16 @@ pub(crate) struct IncrementCompilation { #[diag(query_system_query_overflow)] pub struct QueryOverflow { #[primary_span] - pub span: Option, + pub span: Span, #[subdiagnostic] - pub layout_of_depth: Option, + pub note: QueryOverflowNote, pub suggested_limit: Limit, pub crate_name: Symbol, } #[derive(Subdiagnostic)] -#[note(query_system_layout_of_depth)] -pub struct LayoutOfDepth { +#[note(query_system_overflow_note)] +pub struct QueryOverflowNote { pub desc: String, pub depth: usize, } diff --git a/compiler/rustc_query_system/src/ich/hcx.rs b/compiler/rustc_query_system/src/ich/hcx.rs index 6b737ceb50a68..cf50e61e72ba1 100644 --- a/compiler/rustc_query_system/src/ich/hcx.rs +++ b/compiler/rustc_query_system/src/ich/hcx.rs @@ -1,6 +1,7 @@ +use std::sync::Arc; + use rustc_ast as ast; use rustc_data_structures::stable_hasher::{HashStable, HashingControls, StableHasher}; -use rustc_data_structures::sync::Lrc; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::definitions::DefPathHash; use rustc_session::Session; @@ -117,7 +118,7 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> { fn span_data_to_lines_and_cols( &mut self, span: &SpanData, - ) -> Option<(Lrc, usize, BytePos, usize, BytePos)> { + ) -> Option<(Arc, usize, BytePos, usize, BytePos)> { self.source_map().span_data_to_lines_and_cols(span) } diff --git a/compiler/rustc_query_system/src/ich/impls_syntax.rs b/compiler/rustc_query_system/src/ich/impls_syntax.rs index 480fd49772833..7d508b8201bdf 100644 --- a/compiler/rustc_query_system/src/ich/impls_syntax.rs +++ b/compiler/rustc_query_system/src/ich/impls_syntax.rs @@ -8,7 +8,7 @@ use smallvec::SmallVec; use crate::ich::StableHashingContext; -impl<'ctx> rustc_target::HashStableContext for StableHashingContext<'ctx> {} +impl<'ctx> rustc_abi::HashStableContext for StableHashingContext<'ctx> {} impl<'ctx> rustc_ast::HashStableContext for StableHashingContext<'ctx> {} impl<'a> HashStable> for [hir::Attribute] { diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index a85e8a55a21a9..ee984095ad84b 100644 --- a/compiler/rustc_query_system/src/lib.rs +++ b/compiler/rustc_query_system/src/lib.rs @@ -16,7 +16,7 @@ pub mod ich; pub mod query; mod values; -pub use error::{HandleCycleError, LayoutOfDepth, QueryOverflow}; +pub use error::{HandleCycleError, QueryOverflow, QueryOverflowNote}; pub use values::Value; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_query_system/src/query/caches.rs b/compiler/rustc_query_system/src/query/caches.rs index e6f3d97742d31..e11123dff26a7 100644 --- a/compiler/rustc_query_system/src/query/caches.rs +++ b/compiler/rustc_query_system/src/query/caches.rs @@ -11,18 +11,30 @@ use rustc_span::def_id::{DefId, DefIndex}; use crate::dep_graph::DepNodeIndex; +/// Trait for types that serve as an in-memory cache for query results, +/// for a given key (argument) type and value (return) type. +/// +/// Types implementing this trait are associated with actual key/value types +/// by the `Cache` associated type of the `rustc_middle::query::Key` trait. pub trait QueryCache: Sized { type Key: Hash + Eq + Copy + Debug; type Value: Copy; - /// Checks if the query is already computed and in the cache. + /// Returns the cached value (and other information) associated with the + /// given key, if it is present in the cache. fn lookup(&self, key: &Self::Key) -> Option<(Self::Value, DepNodeIndex)>; + /// Adds a key/value entry to this cache. + /// + /// Called by some part of the query system, after having obtained the + /// value by executing the query or loading a cached value from disk. fn complete(&self, key: Self::Key, value: Self::Value, index: DepNodeIndex); fn iter(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)); } +/// In-memory cache for queries whose keys aren't suitable for any of the +/// more specialized kinds of cache. Backed by a sharded hashmap. pub struct DefaultCache { cache: Sharded>, } @@ -67,6 +79,8 @@ where } } +/// In-memory cache for queries whose key type only has one value (e.g. `()`). +/// The cache therefore only needs to store one query return value. pub struct SingleCache { cache: OnceLock<(V, DepNodeIndex)>, } @@ -101,6 +115,10 @@ where } } +/// In-memory cache for queries whose key is a [`DefId`]. +/// +/// Selects between one of two internal caches, depending on whether the key +/// is a local ID or foreign-crate ID. pub struct DefIdCache { /// Stores the local DefIds in a dense map. Local queries are much more often dense, so this is /// a win over hashing query keys at marginal memory cost (~5% at most) compared to FxHashMap. diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 2a7d759ab3576..a8c2aa98cd083 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -15,7 +15,7 @@ use rustc_span::{DUMMY_SP, Span}; use crate::dep_graph::DepContext; use crate::error::CycleStack; use crate::query::plumbing::CycleError; -use crate::query::{DepKind, QueryContext, QueryStackFrame}; +use crate::query::{QueryContext, QueryStackFrame}; /// Represents a span and a query key. #[derive(Clone, Debug)] @@ -136,20 +136,18 @@ impl QueryJobId { #[cold] #[inline(never)] - pub fn try_find_layout_root( - &self, - query_map: QueryMap, - layout_of_kind: DepKind, - ) -> Option<(QueryJobInfo, usize)> { - let mut last_layout = None; - let mut current_id = Some(*self); - let mut depth = 0; + pub fn find_dep_kind_root(&self, query_map: QueryMap) -> (QueryJobInfo, usize) { + let mut depth = 1; + let info = query_map.get(&self).unwrap(); + let dep_kind = info.query.dep_kind; + let mut current_id = info.job.parent; + let mut last_layout = (info.clone(), depth); while let Some(id) = current_id { let info = query_map.get(&id).unwrap(); - if info.query.dep_kind == layout_of_kind { + if info.query.dep_kind == dep_kind { depth += 1; - last_layout = Some((info.clone(), depth)); + last_layout = (info.clone(), depth); } current_id = info.job.parent; } @@ -569,7 +567,7 @@ pub fn print_query_stack( qcx: Qcx, mut current_query: Option, dcx: DiagCtxtHandle<'_>, - num_frames: Option, + limit_frames: Option, mut file: Option, ) -> usize { // Be careful relying on global state here: this code is called from @@ -586,7 +584,7 @@ pub fn print_query_stack( let Some(query_info) = query_map.get(&query) else { break; }; - if Some(count_printed) < num_frames || num_frames.is_none() { + if Some(count_printed) < limit_frames || limit_frames.is_none() { // Only print to stderr as many stack frames as `num_frames` when present. // FIXME: needs translation #[allow(rustc::diagnostic_outside_of_impl)] @@ -617,5 +615,5 @@ pub fn print_query_stack( if let Some(ref mut file) = file { let _ = writeln!(file, "end of query stack"); } - count_printed + count_total } diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 6fb5e37d2d066..18aae8c00b247 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -222,10 +222,10 @@ pub struct CycleError { pub cycle: Vec, } -/// Checks if the query is already computed and in the cache. -/// It returns the shard index and a lock guard to the shard, -/// which will be used if the query is not in the cache and we need -/// to compute it. +/// Checks whether there is already a value for this key in the in-memory +/// query cache, returning that value if present. +/// +/// (Also performs some associated bookkeeping, if a value was found.) #[inline(always)] pub fn try_get_cached(tcx: Tcx, cache: &C, key: &C::Key) -> Option where diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 3fe1eed243f0a..c6781ecc56681 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -6,6 +6,7 @@ //! Imports are also considered items and placed into modules here, but not resolved yet. use std::cell::Cell; +use std::sync::Arc; use rustc_ast::visit::{self, AssocCtxt, Visitor, WalkItemKind}; use rustc_ast::{ @@ -13,7 +14,6 @@ use rustc_ast::{ ItemKind, MetaItemKind, NodeId, StmtKind, }; use rustc_attr_parsing as attr; -use rustc_data_structures::sync::Lrc; use rustc_expand::base::ResolverExpand; use rustc_expand::expand::AstFragment; use rustc_hir::def::{self, *}; @@ -179,7 +179,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { LoadedMacro::MacroDef { def, ident, attrs, span, edition } => { self.compile_macro(&def, ident, &attrs, span, ast::DUMMY_NODE_ID, edition) } - LoadedMacro::ProcMacro(ext) => MacroData::new(Lrc::new(ext)), + LoadedMacro::ProcMacro(ext) => MacroData::new(Arc::new(ext)), }; self.macro_map.entry(def_id).or_insert(macro_data) @@ -567,10 +567,13 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { Some(rename) => source.ident.span.to(rename.span), None => source.ident.span, }; - self.r.report_error(span, ResolutionError::SelfImportsOnlyAllowedWithin { - root: parent.is_none(), - span_with_rename, - }); + self.r.report_error( + span, + ResolutionError::SelfImportsOnlyAllowedWithin { + root: parent.is_none(), + span_with_rename, + }, + ); // Error recovery: replace `use foo::self;` with `use foo;` if let Some(parent) = module_path.pop() { @@ -645,10 +648,10 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { let self_spans = items .iter() .filter_map(|(use_tree, _)| { - if let ast::UseTreeKind::Simple(..) = use_tree.kind { - if use_tree.ident().name == kw::SelfLower { - return Some(use_tree.span); - } + if let ast::UseTreeKind::Simple(..) = use_tree.kind + && use_tree.ident().name == kw::SelfLower + { + return Some(use_tree.span); } None @@ -947,19 +950,19 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { let imported_binding = self.r.import(binding, import); if parent == self.r.graph_root { let ident = ident.normalize_to_macros_2_0(); - if let Some(entry) = self.r.extern_prelude.get(&ident) { - if expansion != LocalExpnId::ROOT && orig_name.is_some() && !entry.is_import() { - self.r.dcx().emit_err( - errors::MacroExpandedExternCrateCannotShadowExternArguments { - span: item.span, - }, - ); - // `return` is intended to discard this binding because it's an - // unregistered ambiguity error which would result in a panic - // caused by inconsistency `path_res` - // more details: https://github.com/rust-lang/rust/pull/111761 - return; - } + if let Some(entry) = self.r.extern_prelude.get(&ident) + && expansion != LocalExpnId::ROOT + && orig_name.is_some() + && !entry.is_import() + { + self.r.dcx().emit_err( + errors::MacroExpandedExternCrateCannotShadowExternArguments { span: item.span }, + ); + // `return` is intended to discard this binding because it's an + // unregistered ambiguity error which would result in a panic + // caused by inconsistency `path_res` + // more details: https://github.com/rust-lang/rust/pull/111761 + return; } let entry = self .r @@ -1040,10 +1043,10 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { span: item.span, }); } - if let ItemKind::ExternCrate(Some(orig_name)) = item.kind { - if orig_name == kw::SelfLower { - self.r.dcx().emit_err(errors::MacroUseExternCrateSelf { span: attr.span }); - } + if let ItemKind::ExternCrate(Some(orig_name)) = item.kind + && orig_name == kw::SelfLower + { + self.r.dcx().emit_err(errors::MacroUseExternCrateSelf { span: attr.span }); } let ill_formed = |span| { self.r.dcx().emit_err(errors::BadMacroImport { span }); @@ -1179,14 +1182,12 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { return Some((MacroKind::Bang, item.ident, item.span)); } else if ast::attr::contains_name(&item.attrs, sym::proc_macro_attribute) { return Some((MacroKind::Attr, item.ident, item.span)); - } else if let Some(attr) = ast::attr::find_by_name(&item.attrs, sym::proc_macro_derive) { - if let Some(meta_item_inner) = + } else if let Some(attr) = ast::attr::find_by_name(&item.attrs, sym::proc_macro_derive) + && let Some(meta_item_inner) = attr.meta_item_list().and_then(|list| list.get(0).cloned()) - { - if let Some(ident) = meta_item_inner.ident() { - return Some((MacroKind::Derive, ident, ident.span)); - } - } + && let Some(ident) = meta_item_inner.ident() + { + return Some((MacroKind::Derive, ident, ident.span)); } None } diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 5a605df88c207..1c1e8494ffc7c 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -397,7 +397,7 @@ impl Resolver<'_, '_> { } ImportKind::ExternCrate { id, .. } => { let def_id = self.local_def_id(id); - if self.extern_crate_map.get(&def_id).map_or(true, |&cnum| { + if self.extern_crate_map.get(&def_id).is_none_or(|&cnum| { !tcx.is_compiler_builtins(cnum) && !tcx.is_panic_runtime(cnum) && !tcx.has_global_allocator(cnum) diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 87024c487df71..5eb8e420fa4ed 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -95,11 +95,14 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { fn visit_macro_invoc(&mut self, id: NodeId) { let id = id.placeholder_to_expn_id(); - let old_parent = self.resolver.invocation_parents.insert(id, InvocationParent { - parent_def: self.parent_def, - impl_trait_context: self.impl_trait_context, - in_attr: self.in_attr, - }); + let old_parent = self.resolver.invocation_parents.insert( + id, + InvocationParent { + parent_def: self.parent_def, + impl_trait_context: self.impl_trait_context, + in_attr: self.in_attr, + }, + ); assert!(old_parent.is_none(), "parent `LocalDefId` is reset for an invocation"); } } @@ -170,11 +173,17 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) { match fn_kind { - FnKind::Fn(_ctxt, _ident, FnSig { header, decl, span: _ }, _vis, generics, body) - if let Some(coroutine_kind) = header.coroutine_kind => - { + FnKind::Fn( + _ctxt, + _ident, + _vis, + Fn { sig: FnSig { header, decl, span: _ }, generics, contract, body, .. }, + ) if let Some(coroutine_kind) = header.coroutine_kind => { self.visit_fn_header(header); self.visit_generics(generics); + if let Some(contract) = contract { + self.visit_contract(contract); + } // For async functions, we need to create their inner defs inside of a // closure to match their desugared representation. Besides that, diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 9795299ed6d50..5db6f83f3ee51 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -24,13 +24,14 @@ use rustc_session::lint::builtin::{ MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, }; use rustc_session::lint::{AmbiguityErrorDiag, BuiltinLintDiag}; +use rustc_session::utils::was_invoked_from_cargo; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::hygiene::MacroKind; use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, Ident, Span, Symbol, SyntaxContext, kw, sym}; use thin_vec::{ThinVec, thin_vec}; -use tracing::debug; +use tracing::{debug, instrument}; use crate::errors::{ self, AddedMacroUse, ChangeImportBinding, ChangeImportBindingSuggestion, ConsiderAddingADerive, @@ -225,10 +226,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let (name, span) = (ident.name, self.tcx.sess.source_map().guess_head_span(new_binding.span)); - if let Some(s) = self.name_already_seen.get(&name) { - if s == &span { - return; - } + if self.name_already_seen.get(&name) == Some(&span) { + return; } let old_kind = match (ns, old_binding.module()) { @@ -322,7 +321,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let duplicate = new_binding.res().opt_def_id() == old_binding.res().opt_def_id(); let has_dummy_span = new_binding.span.is_dummy() || old_binding.span.is_dummy(); let from_item = - self.extern_prelude.get(&ident).map_or(true, |entry| entry.introduced_by_item); + self.extern_prelude.get(&ident).is_none_or(|entry| entry.introduced_by_item); // Only suggest removing an import if both bindings are to the same def, if both spans // aren't dummy spans. Further, if both bindings are imports, then the ident must have // been introduced by an item. @@ -380,20 +379,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { suggestion = Some(format!("self as {suggested_name}")) } ImportKind::Single { source, .. } => { - if let Some(pos) = - source.span.hi().0.checked_sub(binding_span.lo().0).map(|pos| pos as usize) + if let Some(pos) = source.span.hi().0.checked_sub(binding_span.lo().0) + && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(binding_span) + && pos as usize <= snippet.len() { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(binding_span) { - if pos <= snippet.len() { - span = binding_span - .with_lo(binding_span.lo() + BytePos(pos as u32)) - .with_hi( - binding_span.hi() - - BytePos(if snippet.ends_with(';') { 1 } else { 0 }), - ); - suggestion = Some(format!(" as {suggested_name}")); - } - } + span = binding_span.with_lo(binding_span.lo() + BytePos(pos)).with_hi( + binding_span.hi() - BytePos(if snippet.ends_with(';') { 1 } else { 0 }), + ); + suggestion = Some(format!(" as {suggested_name}")); } } ImportKind::ExternCrate { source, target, .. } => { @@ -510,13 +503,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // If the first element of our path was actually resolved to an // `ExternCrate` (also used for `crate::...`) then no need to issue a // warning, this looks all good! - if let Some(binding) = second_binding { - if let NameBindingKind::Import { import, .. } = binding.kind { - // Careful: we still want to rewrite paths from renamed extern crates. - if let ImportKind::ExternCrate { source: None, .. } = import.kind { - return; - } - } + if let Some(binding) = second_binding + && let NameBindingKind::Import { import, .. } = binding.kind + // Careful: we still want to rewrite paths from renamed extern crates. + && let ImportKind::ExternCrate { source: None, .. } = import.kind + { + return; } let diag = BuiltinLintDiag::AbsPathWithModule(root_span); @@ -537,7 +529,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) { module.for_each_child(self, |_this, ident, _ns, binding| { let res = binding.res(); - if filter_fn(res) && ctxt.map_or(true, |ctxt| ctxt == ident.span.ctxt()) { + if filter_fn(res) && ctxt.is_none_or(|ctxt| ctxt == ident.span.ctxt()) { names.push(TypoSuggestion::typo_from_ident(ident, res)); } }); @@ -685,7 +677,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } if could_be_path { let import_suggestions = self.lookup_import_candidates( - Ident::with_dummy_span(name), + name, Namespace::ValueNS, &parent_scope, &|res: Res| { @@ -809,7 +801,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } err.multipart_suggestion(msg, suggestions, applicability); } - if let Some(ModuleOrUniformRoot::Module(module)) = module && let Some(module) = module.opt_def_id() && let Some(segment) = segment @@ -999,14 +990,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { VisResolutionError::AncestorOnly(span) => { self.dcx().create_err(errs::AncestorOnly(span)) } - VisResolutionError::FailedToResolve(span, label, suggestion) => { - self.into_struct_error(span, ResolutionError::FailedToResolve { - segment: None, - label, - suggestion, - module: None, - }) - } + VisResolutionError::FailedToResolve(span, label, suggestion) => self.into_struct_error( + span, + ResolutionError::FailedToResolve { segment: None, label, suggestion, module: None }, + ), VisResolutionError::ExpectedFound(span, path_str, res) => { self.dcx().create_err(errs::ExpectedModuleFound { span, res, path_str }) } @@ -1047,20 +1034,21 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if filter_fn(res) { for derive in parent_scope.derives { let parent_scope = &ParentScope { derives: &[], ..*parent_scope }; - if let Ok((Some(ext), _)) = this.resolve_macro_path( + let Ok((Some(ext), _)) = this.resolve_macro_path( derive, Some(MacroKind::Derive), parent_scope, false, false, None, - ) { - suggestions.extend( - ext.helper_attrs - .iter() - .map(|name| TypoSuggestion::typo_from_name(*name, res)), - ); - } + ) else { + continue; + }; + suggestions.extend( + ext.helper_attrs + .iter() + .map(|name| TypoSuggestion::typo_from_name(*name, res)), + ); } } } @@ -1139,7 +1127,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }); // Make sure error reporting is deterministic. - suggestions.sort_by(|a, b| a.candidate.as_str().partial_cmp(b.candidate.as_str()).unwrap()); + suggestions.sort_by(|a, b| a.candidate.as_str().cmp(b.candidate.as_str())); match find_best_match_for_name( &suggestions.iter().map(|suggestion| suggestion.candidate).collect::>(), @@ -1183,7 +1171,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let in_module_is_extern = !in_module.def_id().is_local(); in_module.for_each_child(self, |this, ident, ns, name_binding| { // avoid non-importable candidates - if !name_binding.is_importable() { + if !name_binding.is_importable() + // FIXME(import_trait_associated_functions): remove this when `import_trait_associated_functions` is stable + || name_binding.is_assoc_const_or_fn() + && !this.tcx.features().import_trait_associated_functions() + { return; } @@ -1211,12 +1203,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } // #90113: Do not count an inaccessible reexported item as a candidate. - if let NameBindingKind::Import { binding, .. } = name_binding.kind { - if this.is_accessible_from(binding.vis, parent_scope.module) - && !this.is_accessible_from(name_binding.vis, parent_scope.module) - { - return; - } + if let NameBindingKind::Import { binding, .. } = name_binding.kind + && this.is_accessible_from(binding.vis, parent_scope.module) + && !this.is_accessible_from(name_binding.vis, parent_scope.module) + { + return; } let res = name_binding.res(); @@ -1225,7 +1216,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { _ => res.opt_def_id(), }; let child_doc_visible = doc_visible - && (did.map_or(true, |did| did.is_local() || !this.tcx.is_doc_hidden(did))); + && did.is_none_or(|did| did.is_local() || !this.tcx.is_doc_hidden(did)); // collect results based on the filter function // avoid suggesting anything from the same module in which we are resolving @@ -1249,14 +1240,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { segms.push(ast::PathSegment::from_ident(ident)); let path = Path { span: name_binding.span, segments: segms, tokens: None }; - if child_accessible { + if child_accessible // Remove invisible match if exists - if let Some(idx) = candidates + && let Some(idx) = candidates .iter() .position(|v: &ImportSuggestion| v.did == did && !v.accessible) - { - candidates.remove(idx); - } + { + candidates.remove(idx); } if candidates.iter().all(|v: &ImportSuggestion| v.did != did) { @@ -1369,48 +1359,50 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // otherwise cause duplicate suggestions. continue; } - let crate_id = self.crate_loader(|c| c.maybe_process_path_extern(ident.name)); - if let Some(crate_id) = crate_id { - let crate_def_id = crate_id.as_def_id(); - let crate_root = self.expect_module(crate_def_id); - - // Check if there's already an item in scope with the same name as the crate. - // If so, we have to disambiguate the potential import suggestions by making - // the paths *global* (i.e., by prefixing them with `::`). - let needs_disambiguation = - self.resolutions(parent_scope.module).borrow().iter().any( - |(key, name_resolution)| { - if key.ns == TypeNS - && key.ident == ident - && let Some(binding) = name_resolution.borrow().binding - { - match binding.res() { - // No disambiguation needed if the identically named item we - // found in scope actually refers to the crate in question. - Res::Def(_, def_id) => def_id != crate_def_id, - Res::PrimTy(_) => true, - _ => false, - } - } else { - false - } - }, - ); - let mut crate_path = ThinVec::new(); - if needs_disambiguation { - crate_path.push(ast::PathSegment::path_root(rustc_span::DUMMY_SP)); - } - crate_path.push(ast::PathSegment::from_ident(ident)); + let Some(crate_id) = self.crate_loader(|c| c.maybe_process_path_extern(ident.name)) + else { + continue; + }; - suggestions.extend(self.lookup_import_candidates_from_module( - lookup_ident, - namespace, - parent_scope, - crate_root, - crate_path, - &filter_fn, - )); + let crate_def_id = crate_id.as_def_id(); + let crate_root = self.expect_module(crate_def_id); + + // Check if there's already an item in scope with the same name as the crate. + // If so, we have to disambiguate the potential import suggestions by making + // the paths *global* (i.e., by prefixing them with `::`). + let needs_disambiguation = + self.resolutions(parent_scope.module).borrow().iter().any( + |(key, name_resolution)| { + if key.ns == TypeNS + && key.ident == ident + && let Some(binding) = name_resolution.borrow().binding + { + match binding.res() { + // No disambiguation needed if the identically named item we + // found in scope actually refers to the crate in question. + Res::Def(_, def_id) => def_id != crate_def_id, + Res::PrimTy(_) => true, + _ => false, + } + } else { + false + } + }, + ); + let mut crate_path = ThinVec::new(); + if needs_disambiguation { + crate_path.push(ast::PathSegment::path_root(rustc_span::DUMMY_SP)); } + crate_path.push(ast::PathSegment::from_ident(ident)); + + suggestions.extend(self.lookup_import_candidates_from_module( + lookup_ident, + namespace, + parent_scope, + crate_root, + crate_path, + &filter_fn, + )); } } @@ -1507,7 +1499,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }); } for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] { - if let Ok(binding) = self.early_resolve_ident_in_lexical_scope( + let Ok(binding) = self.early_resolve_ident_in_lexical_scope( ident, ScopeSet::All(ns), parent_scope, @@ -1515,53 +1507,53 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { false, None, None, - ) { - let desc = match binding.res() { - Res::Def(DefKind::Macro(MacroKind::Bang), _) => { - "a function-like macro".to_string() - } - Res::Def(DefKind::Macro(MacroKind::Attr), _) | Res::NonMacroAttr(..) => { - format!("an attribute: `#[{ident}]`") - } - Res::Def(DefKind::Macro(MacroKind::Derive), _) => { - format!("a derive macro: `#[derive({ident})]`") - } - Res::ToolMod => { - // Don't confuse the user with tool modules. - continue; - } - Res::Def(DefKind::Trait, _) if macro_kind == MacroKind::Derive => { - "only a trait, without a derive macro".to_string() - } - res => format!( - "{} {}, not {} {}", - res.article(), - res.descr(), - macro_kind.article(), - macro_kind.descr_expected(), - ), - }; - if let crate::NameBindingKind::Import { import, .. } = binding.kind { - if !import.span.is_dummy() { - let note = errors::IdentImporterHereButItIsDesc { - span: import.span, - imported_ident: ident, - imported_ident_desc: &desc, - }; - err.subdiagnostic(note); - // Silence the 'unused import' warning we might get, - // since this diagnostic already covers that import. - self.record_use(ident, binding, Used::Other); - return; - } + ) else { + continue; + }; + + let desc = match binding.res() { + Res::Def(DefKind::Macro(MacroKind::Bang), _) => "a function-like macro".to_string(), + Res::Def(DefKind::Macro(MacroKind::Attr), _) | Res::NonMacroAttr(..) => { + format!("an attribute: `#[{ident}]`") + } + Res::Def(DefKind::Macro(MacroKind::Derive), _) => { + format!("a derive macro: `#[derive({ident})]`") + } + Res::ToolMod => { + // Don't confuse the user with tool modules. + continue; + } + Res::Def(DefKind::Trait, _) if macro_kind == MacroKind::Derive => { + "only a trait, without a derive macro".to_string() } - let note = errors::IdentInScopeButItIsDesc { + res => format!( + "{} {}, not {} {}", + res.article(), + res.descr(), + macro_kind.article(), + macro_kind.descr_expected(), + ), + }; + if let crate::NameBindingKind::Import { import, .. } = binding.kind + && !import.span.is_dummy() + { + let note = errors::IdentImporterHereButItIsDesc { + span: import.span, imported_ident: ident, imported_ident_desc: &desc, }; err.subdiagnostic(note); + // Silence the 'unused import' warning we might get, + // since this diagnostic already covers that import. + self.record_use(ident, binding, Used::Other); return; } + let note = errors::IdentInScopeButItIsDesc { + imported_ident: ident, + imported_ident_desc: &desc, + }; + err.subdiagnostic(note); + return; } } @@ -1745,15 +1737,16 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// If the binding refers to a tuple struct constructor with fields, /// returns the span of its fields. fn ctor_fields_span(&self, binding: NameBinding<'_>) -> Option { - if let NameBindingKind::Res(Res::Def( + let NameBindingKind::Res(Res::Def( DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), ctor_def_id, )) = binding.kind - { - let def_id = self.tcx.parent(ctor_def_id); - return self.field_idents(def_id)?.iter().map(|&f| f.span).reduce(Span::to); // None for `struct Foo()` - } - None + else { + return None; + }; + + let def_id = self.tcx.parent(ctor_def_id); + self.field_idents(def_id)?.iter().map(|&f| f.span).reduce(Span::to) // None for `struct Foo()` } fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'ra>) { @@ -1979,10 +1972,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .collect::>(); candidates.sort(); candidates.dedup(); - match find_best_match_for_name(&candidates, ident, None) { - Some(sugg) if sugg == ident => None, - sugg => sugg, - } + find_best_match_for_name(&candidates, ident, None).filter(|sugg| *sugg != ident) } pub(crate) fn report_path_resolution_error( @@ -2040,13 +2030,23 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { (format!("`_` is not a valid crate or module name"), None) } else if self.tcx.sess.is_rust_2015() { ( - format!("you might be missing crate `{ident}`"), + format!("use of unresolved module or unlinked crate `{ident}`"), Some(( vec![( self.current_crate_outer_attr_insert_span, format!("extern crate {ident};\n"), )], - format!("consider importing the `{ident}` crate"), + if was_invoked_from_cargo() { + format!( + "if you wanted to use a crate named `{ident}`, use `cargo add {ident}` \ + to add it to your `Cargo.toml` and import it in your code", + ) + } else { + format!( + "you might be missing a crate named `{ident}`, add it to your \ + project and import it in your code", + ) + }, Applicability::MaybeIncorrect, )), ) @@ -2225,28 +2225,46 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let descr = binding.res().descr(); (format!("{descr} `{ident}` is not a crate or module"), suggestion) } else { - (format!("use of undeclared crate or module `{ident}`"), suggestion) + let suggestion = if suggestion.is_some() { + suggestion + } else if was_invoked_from_cargo() { + Some(( + vec![], + format!( + "if you wanted to use a crate named `{ident}`, use `cargo add {ident}` \ + to add it to your `Cargo.toml`", + ), + Applicability::MaybeIncorrect, + )) + } else { + Some(( + vec![], + format!("you might be missing a crate named `{ident}`",), + Applicability::MaybeIncorrect, + )) + }; + (format!("use of unresolved module or unlinked crate `{ident}`"), suggestion) } } } /// Adds suggestions for a path that cannot be resolved. + #[instrument(level = "debug", skip(self, parent_scope))] pub(crate) fn make_path_suggestion( &mut self, span: Span, mut path: Vec, parent_scope: &ParentScope<'ra>, ) -> Option<(Vec, Option)> { - debug!("make_path_suggestion: span={:?} path={:?}", span, path); - - match (path.get(0), path.get(1)) { + match path[..] { // `{{root}}::ident::...` on both editions. // On 2015 `{{root}}` is usually added implicitly. - (Some(fst), Some(snd)) - if fst.ident.name == kw::PathRoot && !snd.ident.is_path_segment_keyword() => {} + [first, second, ..] + if first.ident.name == kw::PathRoot && !second.ident.is_path_segment_keyword() => {} // `ident::...` on 2018. - (Some(fst), _) - if fst.ident.span.at_least_rust_2018() && !fst.ident.is_path_segment_keyword() => + [first, ..] + if first.ident.span.at_least_rust_2018() + && !first.ident.is_path_segment_keyword() => { // Insert a placeholder that's later replaced by `self`/`super`/etc. path.insert(0, Segment::from_ident(Ident::empty())); @@ -2267,6 +2285,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// LL | use foo::Bar; /// | ^^^ did you mean `self::foo`? /// ``` + #[instrument(level = "debug", skip(self, parent_scope))] fn make_missing_self_suggestion( &mut self, mut path: Vec, @@ -2275,7 +2294,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Replace first ident with `self` and check if that is valid. path[0].ident.name = kw::SelfLower; let result = self.maybe_resolve_path(&path, None, parent_scope, None); - debug!("make_missing_self_suggestion: path={:?} result={:?}", path, result); + debug!(?path, ?result); if let PathResult::Module(..) = result { Some((path, None)) } else { None } } @@ -2286,6 +2305,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// LL | use foo::Bar; /// | ^^^ did you mean `crate::foo`? /// ``` + #[instrument(level = "debug", skip(self, parent_scope))] fn make_missing_crate_suggestion( &mut self, mut path: Vec, @@ -2294,7 +2314,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Replace first ident with `crate` and check if that is valid. path[0].ident.name = kw::Crate; let result = self.maybe_resolve_path(&path, None, parent_scope, None); - debug!("make_missing_crate_suggestion: path={:?} result={:?}", path, result); + debug!(?path, ?result); if let PathResult::Module(..) = result { Some(( path, @@ -2317,6 +2337,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// LL | use foo::Bar; /// | ^^^ did you mean `super::foo`? /// ``` + #[instrument(level = "debug", skip(self, parent_scope))] fn make_missing_super_suggestion( &mut self, mut path: Vec, @@ -2325,7 +2346,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Replace first ident with `crate` and check if that is valid. path[0].ident.name = kw::Super; let result = self.maybe_resolve_path(&path, None, parent_scope, None); - debug!("make_missing_super_suggestion: path={:?} result={:?}", path, result); + debug!(?path, ?result); if let PathResult::Module(..) = result { Some((path, None)) } else { None } } @@ -2339,6 +2360,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// /// Used when importing a submodule of an external crate but missing that crate's /// name as the first part of path. + #[instrument(level = "debug", skip(self, parent_scope))] fn make_external_crate_suggestion( &mut self, mut path: Vec, @@ -2353,16 +2375,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // 2) `std` suggestions before `core` suggestions. let mut extern_crate_names = self.extern_prelude.keys().map(|ident| ident.name).collect::>(); - extern_crate_names.sort_by(|a, b| b.as_str().partial_cmp(a.as_str()).unwrap()); + extern_crate_names.sort_by(|a, b| b.as_str().cmp(a.as_str())); for name in extern_crate_names.into_iter() { // Replace first ident with a crate name and check if that is valid. path[0].ident.name = name; let result = self.maybe_resolve_path(&path, None, parent_scope, None); - debug!( - "make_external_crate_suggestion: name={:?} path={:?} result={:?}", - name, path, result - ); + debug!(?path, ?name, ?result); if let PathResult::Module(..) = result { return Some((path, None)); } @@ -2406,121 +2425,115 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let binding_key = BindingKey::new(ident, MacroNS); let resolution = resolutions.get(&binding_key)?; let binding = resolution.borrow().binding()?; - if let Res::Def(DefKind::Macro(MacroKind::Bang), _) = binding.res() { - let module_name = crate_module.kind.name().unwrap(); - let import_snippet = match import.kind { - ImportKind::Single { source, target, .. } if source != target => { - format!("{source} as {target}") - } - _ => format!("{ident}"), - }; + let Res::Def(DefKind::Macro(MacroKind::Bang), _) = binding.res() else { + return None; + }; + let module_name = crate_module.kind.name().unwrap(); + let import_snippet = match import.kind { + ImportKind::Single { source, target, .. } if source != target => { + format!("{source} as {target}") + } + _ => format!("{ident}"), + }; - let mut corrections: Vec<(Span, String)> = Vec::new(); - if !import.is_nested() { - // Assume this is the easy case of `use issue_59764::foo::makro;` and just remove - // intermediate segments. - corrections.push((import.span, format!("{module_name}::{import_snippet}"))); - } else { - // Find the binding span (and any trailing commas and spaces). - // ie. `use a::b::{c, d, e};` - // ^^^ - let (found_closing_brace, binding_span) = find_span_of_binding_until_next_binding( - self.tcx.sess, - import.span, - import.use_span, - ); - debug!( - "check_for_module_export_macro: found_closing_brace={:?} binding_span={:?}", - found_closing_brace, binding_span - ); + let mut corrections: Vec<(Span, String)> = Vec::new(); + if !import.is_nested() { + // Assume this is the easy case of `use issue_59764::foo::makro;` and just remove + // intermediate segments. + corrections.push((import.span, format!("{module_name}::{import_snippet}"))); + } else { + // Find the binding span (and any trailing commas and spaces). + // ie. `use a::b::{c, d, e};` + // ^^^ + let (found_closing_brace, binding_span) = find_span_of_binding_until_next_binding( + self.tcx.sess, + import.span, + import.use_span, + ); + debug!(found_closing_brace, ?binding_span); + + let mut removal_span = binding_span; + + // If the binding span ended with a closing brace, as in the below example: + // ie. `use a::b::{c, d};` + // ^ + // Then expand the span of characters to remove to include the previous + // binding's trailing comma. + // ie. `use a::b::{c, d};` + // ^^^ + if found_closing_brace + && let Some(previous_span) = + extend_span_to_previous_binding(self.tcx.sess, binding_span) + { + debug!(?previous_span); + removal_span = removal_span.with_lo(previous_span.lo()); + } + debug!(?removal_span); - let mut removal_span = binding_span; - if found_closing_brace { - // If the binding span ended with a closing brace, as in the below example: - // ie. `use a::b::{c, d};` - // ^ - // Then expand the span of characters to remove to include the previous - // binding's trailing comma. - // ie. `use a::b::{c, d};` - // ^^^ - if let Some(previous_span) = - extend_span_to_previous_binding(self.tcx.sess, binding_span) - { - debug!("check_for_module_export_macro: previous_span={:?}", previous_span); - removal_span = removal_span.with_lo(previous_span.lo()); - } - } - debug!("check_for_module_export_macro: removal_span={:?}", removal_span); - - // Remove the `removal_span`. - corrections.push((removal_span, "".to_string())); - - // Find the span after the crate name and if it has nested imports immediately - // after the crate name already. - // ie. `use a::b::{c, d};` - // ^^^^^^^^^ - // or `use a::{b, c, d}};` - // ^^^^^^^^^^^ - let (has_nested, after_crate_name) = find_span_immediately_after_crate_name( - self.tcx.sess, - module_name, - import.use_span, - ); - debug!( - "check_for_module_export_macro: has_nested={:?} after_crate_name={:?}", - has_nested, after_crate_name - ); + // Remove the `removal_span`. + corrections.push((removal_span, "".to_string())); - let source_map = self.tcx.sess.source_map(); + // Find the span after the crate name and if it has nested imports immediately + // after the crate name already. + // ie. `use a::b::{c, d};` + // ^^^^^^^^^ + // or `use a::{b, c, d}};` + // ^^^^^^^^^^^ + let (has_nested, after_crate_name) = + find_span_immediately_after_crate_name(self.tcx.sess, module_name, import.use_span); + debug!(has_nested, ?after_crate_name); - // Make sure this is actually crate-relative. - let is_definitely_crate = import - .module_path - .first() - .is_some_and(|f| f.ident.name != kw::SelfLower && f.ident.name != kw::Super); + let source_map = self.tcx.sess.source_map(); - // Add the import to the start, with a `{` if required. - let start_point = source_map.start_point(after_crate_name); - if is_definitely_crate - && let Ok(start_snippet) = source_map.span_to_snippet(start_point) - { - corrections.push(( - start_point, - if has_nested { - // In this case, `start_snippet` must equal '{'. - format!("{start_snippet}{import_snippet}, ") - } else { - // In this case, add a `{`, then the moved import, then whatever - // was there before. - format!("{{{import_snippet}, {start_snippet}") - }, - )); + // Make sure this is actually crate-relative. + let is_definitely_crate = import + .module_path + .first() + .is_some_and(|f| f.ident.name != kw::SelfLower && f.ident.name != kw::Super); - // Add a `};` to the end if nested, matching the `{` added at the start. - if !has_nested { - corrections - .push((source_map.end_point(after_crate_name), "};".to_string())); - } - } else { - // If the root import is module-relative, add the import separately - corrections.push(( - import.use_span.shrink_to_lo(), - format!("use {module_name}::{import_snippet};\n"), - )); + // Add the import to the start, with a `{` if required. + let start_point = source_map.start_point(after_crate_name); + if is_definitely_crate + && let Ok(start_snippet) = source_map.span_to_snippet(start_point) + { + corrections.push(( + start_point, + if has_nested { + // In this case, `start_snippet` must equal '{'. + format!("{start_snippet}{import_snippet}, ") + } else { + // In this case, add a `{`, then the moved import, then whatever + // was there before. + format!("{{{import_snippet}, {start_snippet}") + }, + )); + + // Add a `};` to the end if nested, matching the `{` added at the start. + if !has_nested { + corrections.push((source_map.end_point(after_crate_name), "};".to_string())); } + } else { + // If the root import is module-relative, add the import separately + corrections.push(( + import.use_span.shrink_to_lo(), + format!("use {module_name}::{import_snippet};\n"), + )); } + } - let suggestion = Some(( - corrections, - String::from("a macro with this name exists at the root of the crate"), - Applicability::MaybeIncorrect, - )); - Some((suggestion, Some("this could be because a macro annotated with `#[macro_export]` will be exported \ + let suggestion = Some(( + corrections, + String::from("a macro with this name exists at the root of the crate"), + Applicability::MaybeIncorrect, + )); + Some(( + suggestion, + Some( + "this could be because a macro annotated with `#[macro_export]` will be exported \ at the root of the crate instead of the module where it is defined" - .to_string()))) - } else { - None - } + .to_string(), + ), + )) } /// Finds a cfg-ed out item inside `module` with the matching name. @@ -2673,15 +2686,12 @@ fn extend_span_to_previous_binding(sess: &Session, binding_span: Span) -> Option /// use foo::{a, b::{c, d}}; /// // ^^^^^^^^^^^^^^^ -- true /// ``` +#[instrument(level = "debug", skip(sess))] fn find_span_immediately_after_crate_name( sess: &Session, module_name: Symbol, use_span: Span, ) -> (bool, Span) { - debug!( - "find_span_immediately_after_crate_name: module_name={:?} use_span={:?}", - module_name, use_span - ); let source_map = sess.source_map(); // Using `use issue_59764::foo::{baz, makro};` as an example throughout.. @@ -3047,7 +3057,6 @@ impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder { self.first_legal_span = Some(inject); } self.first_use_span = search_for_any_use_in_items(&c.items); - return; } else { visit::walk_crate(self, c); } @@ -3061,7 +3070,6 @@ impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder { self.first_legal_span = Some(inject); } self.first_use_span = search_for_any_use_in_items(items); - return; } } else { visit::walk_item(self, item); @@ -3071,16 +3079,16 @@ impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder { fn search_for_any_use_in_items(items: &[P]) -> Option { for item in items { - if let ItemKind::Use(..) = item.kind { - if is_span_suitable_for_use_injection(item.span) { - let mut lo = item.span.lo(); - for attr in &item.attrs { - if attr.span.eq_ctxt(item.span) { - lo = std::cmp::min(lo, attr.span.lo()); - } + if let ItemKind::Use(..) = item.kind + && is_span_suitable_for_use_injection(item.span) + { + let mut lo = item.span.lo(); + for attr in &item.attrs { + if attr.span.eq_ctxt(item.span) { + lo = std::cmp::min(lo, attr.span.lo()); } - return Some(Span::new(lo, lo, item.span.ctxt(), item.span.parent())); } + return Some(Span::new(lo, lo, item.span.ctxt(), item.span.parent())); } } None diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index e279d14704e05..6ef4aa407253d 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -118,37 +118,38 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { let resolutions = self.r.resolutions(module); for (_, name_resolution) in resolutions.borrow().iter() { - if let Some(mut binding) = name_resolution.borrow().binding() { - // Set the given effective visibility level to `Level::Direct` and - // sets the rest of the `use` chain to `Level::Reexported` until - // we hit the actual exported item. - // - // If the binding is ambiguous, put the root ambiguity binding and all reexports - // leading to it into the table. They are used by the `ambiguous_glob_reexports` - // lint. For all bindings added to the table this way `is_ambiguity` returns true. - let is_ambiguity = - |binding: NameBinding<'ra>, warn: bool| binding.ambiguity.is_some() && !warn; - let mut parent_id = ParentId::Def(module_id); - let mut warn_ambiguity = binding.warn_ambiguity; - while let NameBindingKind::Import { binding: nested_binding, .. } = binding.kind { - self.update_import(binding, parent_id); + let Some(mut binding) = name_resolution.borrow().binding() else { + continue; + }; + // Set the given effective visibility level to `Level::Direct` and + // sets the rest of the `use` chain to `Level::Reexported` until + // we hit the actual exported item. + // + // If the binding is ambiguous, put the root ambiguity binding and all reexports + // leading to it into the table. They are used by the `ambiguous_glob_reexports` + // lint. For all bindings added to the table this way `is_ambiguity` returns true. + let is_ambiguity = + |binding: NameBinding<'ra>, warn: bool| binding.ambiguity.is_some() && !warn; + let mut parent_id = ParentId::Def(module_id); + let mut warn_ambiguity = binding.warn_ambiguity; + while let NameBindingKind::Import { binding: nested_binding, .. } = binding.kind { + self.update_import(binding, parent_id); - if is_ambiguity(binding, warn_ambiguity) { - // Stop at the root ambiguity, further bindings in the chain should not - // be reexported because the root ambiguity blocks any access to them. - // (Those further bindings are most likely not ambiguities themselves.) - break; - } - - parent_id = ParentId::Import(binding); - binding = nested_binding; - warn_ambiguity |= nested_binding.warn_ambiguity; - } - if !is_ambiguity(binding, warn_ambiguity) - && let Some(def_id) = binding.res().opt_def_id().and_then(|id| id.as_local()) - { - self.update_def(def_id, binding.vis.expect_local(), parent_id); + if is_ambiguity(binding, warn_ambiguity) { + // Stop at the root ambiguity, further bindings in the chain should not + // be reexported because the root ambiguity blocks any access to them. + // (Those further bindings are most likely not ambiguities themselves.) + break; } + + parent_id = ParentId::Import(binding); + binding = nested_binding; + warn_ambiguity |= nested_binding.warn_ambiguity; + } + if !is_ambiguity(binding, warn_ambiguity) + && let Some(def_id) = binding.res().opt_def_id().and_then(|id| id.as_local()) + { + self.update_def(def_id, binding.vis.expect_local(), parent_id); } } } diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 3bfe98f7091b4..7eb795034b030 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -59,7 +59,7 @@ pub(crate) struct NameAlreadyUsedInParameterList { pub(crate) span: Span, #[label(resolve_first_use_of_name)] pub(crate) first_use_span: Span, - pub(crate) name: Symbol, + pub(crate) name: Ident, } #[derive(Diagnostic)] @@ -142,7 +142,7 @@ pub(crate) struct VariableBoundWithDifferentMode { pub(crate) span: Span, #[label(resolve_first_binding_span)] pub(crate) first_binding_span: Span, - pub(crate) variable_name: Symbol, + pub(crate) variable_name: Ident, } #[derive(Diagnostic)] @@ -151,7 +151,7 @@ pub(crate) struct IdentifierBoundMoreThanOnceInParameterList { #[primary_span] #[label] pub(crate) span: Span, - pub(crate) identifier: Symbol, + pub(crate) identifier: Ident, } #[derive(Diagnostic)] @@ -160,7 +160,7 @@ pub(crate) struct IdentifierBoundMoreThanOnceInSamePattern { #[primary_span] #[label] pub(crate) span: Span, - pub(crate) identifier: Symbol, + pub(crate) identifier: Ident, } #[derive(Diagnostic)] @@ -478,7 +478,7 @@ pub(crate) struct TraitImplDuplicate { pub(crate) old_span: Span, #[label(resolve_trait_item_span)] pub(crate) trait_item_span: Span, - pub(crate) name: Symbol, + pub(crate) name: Ident, } #[derive(Diagnostic)] @@ -976,7 +976,7 @@ pub(crate) struct AttemptToDefineBuiltinMacroTwice { pub(crate) struct VariableIsNotBoundInAllPatterns { #[primary_span] pub(crate) multispan: MultiSpan, - pub(crate) name: Symbol, + pub(crate) name: Ident, } #[derive(Subdiagnostic, Debug, Clone)] @@ -984,7 +984,7 @@ pub(crate) struct VariableIsNotBoundInAllPatterns { pub(crate) struct PatternDoesntBindName { #[primary_span] pub(crate) span: Span, - pub(crate) name: Symbol, + pub(crate) name: Ident, } #[derive(Subdiagnostic, Debug, Clone)] @@ -1260,7 +1260,7 @@ pub(crate) struct TraitImplMismatch { #[primary_span] #[label] pub(crate) span: Span, - pub(crate) name: Symbol, + pub(crate) name: Ident, pub(crate) kind: &'static str, pub(crate) trait_path: String, #[label(resolve_trait_impl_mismatch_label_item)] diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 45e87edc53e96..499b9bca4d2ad 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -246,23 +246,21 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // ---- end // ``` // So we have to fall back to the module's parent during lexical resolution in this case. - if derive_fallback_lint_id.is_some() { - if let Some(parent) = module.parent { - // Inner module is inside the macro, parent module is outside of the macro. - if module.expansion != parent.expansion - && module.expansion.is_descendant_of(parent.expansion) - { - // The macro is a proc macro derive - if let Some(def_id) = module.expansion.expn_data().macro_def_id { - let ext = &self.get_macro_by_def_id(def_id).ext; - if ext.builtin_name.is_none() - && ext.macro_kind() == MacroKind::Derive - && parent.expansion.outer_expn_is_descendant_of(*ctxt) - { - return Some((parent, derive_fallback_lint_id)); - } - } - } + if derive_fallback_lint_id.is_some() + && let Some(parent) = module.parent + // Inner module is inside the macro + && module.expansion != parent.expansion + // Parent module is outside of the macro + && module.expansion.is_descendant_of(parent.expansion) + // The macro is a proc macro derive + && let Some(def_id) = module.expansion.expn_data().macro_def_id + { + let ext = &self.get_macro_by_def_id(def_id).ext; + if ext.builtin_name.is_none() + && ext.macro_kind() == MacroKind::Derive + && parent.expansion.outer_expn_is_descendant_of(*ctxt) + { + return Some((parent, derive_fallback_lint_id)); } } @@ -593,8 +591,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }, Scope::StdLibPrelude => { let mut result = Err(Determinacy::Determined); - if let Some(prelude) = this.prelude { - if let Ok(binding) = this.resolve_ident_in_module_unadjusted( + if let Some(prelude) = this.prelude + && let Ok(binding) = this.resolve_ident_in_module_unadjusted( ModuleOrUniformRoot::Module(prelude), ident, ns, @@ -603,14 +601,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { None, ignore_binding, ignore_import, - ) { - if matches!(use_prelude, UsePrelude::Yes) - || this.is_builtin_macro(binding.res()) - { - result = Ok((binding, Flags::MISC_FROM_PRELUDE)); - } - } + ) + && (matches!(use_prelude, UsePrelude::Yes) + || this.is_builtin_macro(binding.res())) + { + result = Ok((binding, Flags::MISC_FROM_PRELUDE)); } + result } Scope::BuiltinTypes => match this.builtin_types_bindings.get(&ident.name) { @@ -939,10 +936,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }; // Items and single imports are not shadowable, if we have one, then it's determined. - if let Some(binding) = binding { - if !binding.is_glob_import() { - return check_usable(self, binding); - } + if let Some(binding) = binding + && !binding.is_glob_import() + { + return check_usable(self, binding); } // --- From now on we either have a glob resolution or no resolution. --- @@ -1184,21 +1181,25 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } Some(_) => None, }; - (rib_ident.span, AttemptToUseNonConstantValueInConstant { - ident: original_rib_ident_def, - suggestion: "const", - current: "let", - type_span, - }) + ( + rib_ident.span, + AttemptToUseNonConstantValueInConstant { + ident: original_rib_ident_def, + suggestion: "const", + current: "let", + type_span, + }, + ) } - Some((ident, kind)) => { - (span, AttemptToUseNonConstantValueInConstant { + Some((ident, kind)) => ( + span, + AttemptToUseNonConstantValueInConstant { ident, suggestion: "let", current: kind.as_str(), type_span: None, - }) - } + }, + ), }; self.report_error(span, resolution_error); } @@ -1206,10 +1207,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } RibKind::ConstParamTy => { if let Some(span) = finalize { - self.report_error(span, ParamInTyOfConstParam { - name: rib_ident.name, - param_kind: None, - }); + self.report_error( + span, + ParamInTyOfConstParam { + name: rib_ident.name, + param_kind: None, + }, + ); } return Res::Err; } @@ -1290,10 +1294,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } RibKind::ConstParamTy => { if let Some(span) = finalize { - self.report_error(span, ResolutionError::ParamInTyOfConstParam { - name: rib_ident.name, - param_kind: Some(errors::ParamKindInTyOfConstParam::Type), - }); + self.report_error( + span, + ResolutionError::ParamInTyOfConstParam { + name: rib_ident.name, + param_kind: Some(errors::ParamKindInTyOfConstParam::Type), + }, + ); } return Res::Err; } @@ -1356,10 +1363,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } RibKind::ConstParamTy => { if let Some(span) = finalize { - self.report_error(span, ResolutionError::ParamInTyOfConstParam { - name: rib_ident.name, - param_kind: Some(errors::ParamKindInTyOfConstParam::Const), - }); + self.report_error( + span, + ResolutionError::ParamInTyOfConstParam { + name: rib_ident.name, + param_kind: Some(errors::ParamKindInTyOfConstParam::Const), + }, + ); } return Res::Err; } @@ -1437,13 +1447,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { for (segment_idx, &Segment { ident, id, .. }) in path.iter().enumerate() { debug!("resolve_path ident {} {:?} {:?}", segment_idx, ident, id); let record_segment_res = |this: &mut Self, res| { - if finalize.is_some() { - if let Some(id) = id { - if !this.partial_res_map.contains_key(&id) { - assert!(id != ast::DUMMY_NODE_ID, "Trying to resolve dummy id"); - this.record_partial_res(id, PartialRes::new(res)); - } - } + if finalize.is_some() + && let Some(id) = id + && !this.partial_res_map.contains_key(&id) + { + assert!(id != ast::DUMMY_NODE_ID, "Trying to resolve dummy id"); + this.record_partial_res(id, PartialRes::new(res)); } }; @@ -1463,13 +1472,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { _ => None, }, }; - if let Some(self_module) = self_module { - if let Some(parent) = self_module.parent { - module = Some(ModuleOrUniformRoot::Module( - self.resolve_self(&mut ctxt, parent), - )); - continue; - } + if let Some(self_module) = self_module + && let Some(parent) = self_module.parent + { + module = + Some(ModuleOrUniformRoot::Module(self.resolve_self(&mut ctxt, parent))); + continue; } return PathResult::failed( ident, @@ -1644,13 +1652,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } Err(Undetermined) => return PathResult::Indeterminate, Err(Determined) => { - if let Some(ModuleOrUniformRoot::Module(module)) = module { - if opt_ns.is_some() && !module.is_normal() { - return PathResult::NonModule(PartialRes::with_unresolved_segments( - module.res().unwrap(), - path.len() - segment_idx, - )); - } + if let Some(ModuleOrUniformRoot::Module(module)) = module + && opt_ns.is_some() + && !module.is_normal() + { + return PathResult::NonModule(PartialRes::with_unresolved_segments( + module.res().unwrap(), + path.len() - segment_idx, + )); } return PathResult::failed( diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 5b1d8d622bdd6..b1b234eb75737 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -17,9 +17,10 @@ use rustc_session::lint::builtin::{ AMBIGUOUS_GLOB_REEXPORTS, HIDDEN_GLOB_REEXPORTS, PUB_USE_OF_PRIVATE_EXTERN_CRATE, REDUNDANT_IMPORTS, UNUSED_IMPORTS, }; +use rustc_session::parse::feature_err; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::hygiene::LocalExpnId; -use rustc_span::{Ident, Span, Symbol, kw}; +use rustc_span::{Ident, Span, Symbol, kw, sym}; use smallvec::SmallVec; use tracing::debug; @@ -286,12 +287,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { binding.vis }; - if let ImportKind::Glob { ref max_vis, .. } = import.kind { - if vis == import_vis - || max_vis.get().map_or(true, |max_vis| vis.is_at_least(max_vis, self.tcx)) - { - max_vis.set(Some(vis.expect_local())) - } + if let ImportKind::Glob { ref max_vis, .. } = import.kind + && (vis == import_vis + || max_vis.get().is_none_or(|max_vis| vis.is_at_least(max_vis, self.tcx))) + { + max_vis.set(Some(vis.expect_local())) } self.arenas.alloc_name_binding(NameBindingData { @@ -542,31 +542,28 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // resolution for it so that later resolve stages won't complain. self.import_dummy_binding(*import, is_indeterminate); - if let Some(err) = unresolved_import_error { - glob_error |= import.is_glob(); + let Some(err) = unresolved_import_error else { continue }; - if let ImportKind::Single { source, ref source_bindings, .. } = import.kind { - if source.name == kw::SelfLower { - // Silence `unresolved import` error if E0429 is already emitted - if let Err(Determined) = source_bindings.value_ns.get() { - continue; - } - } - } + glob_error |= import.is_glob(); - if prev_root_id != NodeId::ZERO - && prev_root_id != import.root_id - && !errors.is_empty() - { - // In the case of a new import line, throw a diagnostic message - // for the previous line. - self.throw_unresolved_import_error(errors, glob_error); - errors = vec![]; - } - if seen_spans.insert(err.span) { - errors.push((*import, err)); - prev_root_id = import.root_id; - } + if let ImportKind::Single { source, ref source_bindings, .. } = import.kind + && source.name == kw::SelfLower + // Silence `unresolved import` error if E0429 is already emitted + && let Err(Determined) = source_bindings.value_ns.get() + { + continue; + } + + if prev_root_id != NodeId::ZERO && prev_root_id != import.root_id && !errors.is_empty() + { + // In the case of a new import line, throw a diagnostic message + // for the previous line. + self.throw_unresolved_import_error(errors, glob_error); + errors = vec![]; + } + if seen_spans.insert(err.span) { + errors.push((*import, err)); + prev_root_id = import.root_id; } } @@ -608,60 +605,60 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { for (key, resolution) in self.resolutions(*module).borrow().iter() { let resolution = resolution.borrow(); - if let Some(binding) = resolution.binding { - if let NameBindingKind::Import { import, .. } = binding.kind - && let Some((amb_binding, _)) = binding.ambiguity - && binding.res() != Res::Err - && exported_ambiguities.contains(&binding) + let Some(binding) = resolution.binding else { continue }; + + if let NameBindingKind::Import { import, .. } = binding.kind + && let Some((amb_binding, _)) = binding.ambiguity + && binding.res() != Res::Err + && exported_ambiguities.contains(&binding) + { + self.lint_buffer.buffer_lint( + AMBIGUOUS_GLOB_REEXPORTS, + import.root_id, + import.root_span, + BuiltinLintDiag::AmbiguousGlobReexports { + name: key.ident.to_string(), + namespace: key.ns.descr().to_string(), + first_reexport_span: import.root_span, + duplicate_reexport_span: amb_binding.span, + }, + ); + } + + if let Some(glob_binding) = resolution.shadowed_glob { + let binding_id = match binding.kind { + NameBindingKind::Res(res) => { + Some(self.def_id_to_node_id[res.def_id().expect_local()]) + } + NameBindingKind::Module(module) => { + Some(self.def_id_to_node_id[module.def_id().expect_local()]) + } + NameBindingKind::Import { import, .. } => import.id(), + }; + + if binding.res() != Res::Err + && glob_binding.res() != Res::Err + && let NameBindingKind::Import { import: glob_import, .. } = + glob_binding.kind + && let Some(binding_id) = binding_id + && let Some(glob_import_id) = glob_import.id() + && let glob_import_def_id = self.local_def_id(glob_import_id) + && self.effective_visibilities.is_exported(glob_import_def_id) + && glob_binding.vis.is_public() + && !binding.vis.is_public() { self.lint_buffer.buffer_lint( - AMBIGUOUS_GLOB_REEXPORTS, - import.root_id, - import.root_span, - BuiltinLintDiag::AmbiguousGlobReexports { - name: key.ident.to_string(), - namespace: key.ns.descr().to_string(), - first_reexport_span: import.root_span, - duplicate_reexport_span: amb_binding.span, + HIDDEN_GLOB_REEXPORTS, + binding_id, + binding.span, + BuiltinLintDiag::HiddenGlobReexports { + name: key.ident.name.to_string(), + namespace: key.ns.descr().to_owned(), + glob_reexport_span: glob_binding.span, + private_item_span: binding.span, }, ); } - - if let Some(glob_binding) = resolution.shadowed_glob { - let binding_id = match binding.kind { - NameBindingKind::Res(res) => { - Some(self.def_id_to_node_id[res.def_id().expect_local()]) - } - NameBindingKind::Module(module) => { - Some(self.def_id_to_node_id[module.def_id().expect_local()]) - } - NameBindingKind::Import { import, .. } => import.id(), - }; - - if binding.res() != Res::Err - && glob_binding.res() != Res::Err - && let NameBindingKind::Import { import: glob_import, .. } = - glob_binding.kind - && let Some(binding_id) = binding_id - && let Some(glob_import_id) = glob_import.id() - && let glob_import_def_id = self.local_def_id(glob_import_id) - && self.effective_visibilities.is_exported(glob_import_def_id) - && glob_binding.vis.is_public() - && !binding.vis.is_public() - { - self.lint_buffer.buffer_lint( - HIDDEN_GLOB_REEXPORTS, - binding_id, - binding.span, - BuiltinLintDiag::HiddenGlobReexports { - name: key.ident.name.to_string(), - namespace: key.ns.descr().to_owned(), - glob_reexport_span: glob_binding.span, - private_item_span: binding.span, - }, - ); - } - } } } } @@ -829,6 +826,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Don't update the resolution, because it was never added. Err(Determined) if target.name == kw::Underscore => {} Ok(binding) if binding.is_importable() => { + if binding.is_assoc_const_or_fn() + && !this.tcx.features().import_trait_associated_functions() + { + feature_err( + this.tcx.sess, + sym::import_trait_associated_functions, + import.span, + "`use` associated items of traits is unstable", + ) + .emit(); + } let imported_binding = this.import(binding, import); target_bindings[ns].set(Some(imported_binding)); this.define(parent, target, ns, imported_binding); @@ -906,12 +914,15 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } => { if no_ambiguity { assert!(import.imported_module.get().is_none()); - self.report_error(span, ResolutionError::FailedToResolve { - segment: Some(segment_name), - label, - suggestion, - module, - }); + self.report_error( + span, + ResolutionError::FailedToResolve { + segment: Some(segment_name), + label, + suggestion, + module, + }, + ); } return None; } @@ -994,21 +1005,19 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.lint_if_path_starts_with_module(Some(finalize), &full_path, None); } - if let ModuleOrUniformRoot::Module(module) = module { - if module == import.parent_scope.module { - // Importing a module into itself is not allowed. - return Some(UnresolvedImportError { - span: import.span, - label: Some(String::from( - "cannot glob-import a module into itself", - )), - note: None, - suggestion: None, - candidates: None, - segment: None, - module: None, - }); - } + if let ModuleOrUniformRoot::Module(module) = module + && module == import.parent_scope.module + { + // Importing a module into itself is not allowed. + return Some(UnresolvedImportError { + span: import.span, + label: Some(String::from("cannot glob-import a module into itself")), + note: None, + suggestion: None, + candidates: None, + segment: None, + module: None, + }); } if !is_prelude && let Some(max_vis) = max_vis.get() @@ -1069,18 +1078,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Consistency checks, analogous to `finalize_macro_resolutions`. let initial_res = source_bindings[ns].get().map(|initial_binding| { all_ns_err = false; - if let Some(target_binding) = target_bindings[ns].get() { - if target.name == kw::Underscore - && initial_binding.is_extern_crate() - && !initial_binding.is_import() - { - let used = if import.module_path.is_empty() { - Used::Scope - } else { - Used::Other - }; - this.record_use(ident, target_binding, used); - } + if let Some(target_binding) = target_bindings[ns].get() + && target.name == kw::Underscore + && initial_binding.is_extern_crate() + && !initial_binding.is_import() + { + let used = if import.module_path.is_empty() { + Used::Scope + } else { + Used::Other + }; + this.record_use(ident, target_binding, used); } initial_binding.res() }); @@ -1235,17 +1243,19 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let mut any_successful_reexport = false; let mut crate_private_reexport = false; self.per_ns(|this, ns| { - if let Ok(binding) = source_bindings[ns].get() { - if !binding.vis.is_at_least(import.vis, this.tcx) { - reexport_error = Some((ns, binding)); - if let ty::Visibility::Restricted(binding_def_id) = binding.vis { - if binding_def_id.is_top_level_module() { - crate_private_reexport = true; - } - } - } else { - any_successful_reexport = true; + let Ok(binding) = source_bindings[ns].get() else { + return; + }; + + if !binding.vis.is_at_least(import.vis, this.tcx) { + reexport_error = Some((ns, binding)); + if let ty::Visibility::Restricted(binding_def_id) = binding.vis + && binding_def_id.is_top_level_module() + { + crate_private_reexport = true; } + } else { + any_successful_reexport = true; } }); @@ -1462,28 +1472,28 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Since import resolution is finished, globs will not define any more names. *module.globs.borrow_mut() = Vec::new(); - if let Some(def_id) = module.opt_def_id() { - let mut children = Vec::new(); - - module.for_each_child(self, |this, ident, _, binding| { - let res = binding.res().expect_non_local(); - let error_ambiguity = binding.is_ambiguity_recursive() && !binding.warn_ambiguity; - if res != def::Res::Err && !error_ambiguity { - let mut reexport_chain = SmallVec::new(); - let mut next_binding = binding; - while let NameBindingKind::Import { binding, import, .. } = next_binding.kind { - reexport_chain.push(import.simplify(this)); - next_binding = binding; - } + let Some(def_id) = module.opt_def_id() else { return }; + + let mut children = Vec::new(); - children.push(ModChild { ident, res, vis: binding.vis, reexport_chain }); + module.for_each_child(self, |this, ident, _, binding| { + let res = binding.res().expect_non_local(); + let error_ambiguity = binding.is_ambiguity_recursive() && !binding.warn_ambiguity; + if res != def::Res::Err && !error_ambiguity { + let mut reexport_chain = SmallVec::new(); + let mut next_binding = binding; + while let NameBindingKind::Import { binding, import, .. } = next_binding.kind { + reexport_chain.push(import.simplify(this)); + next_binding = binding; } - }); - if !children.is_empty() { - // Should be fine because this code is only called for local modules. - self.module_children.insert(def_id.expect_local(), children); + children.push(ModChild { ident, res, vis: binding.vis, reexport_chain }); } + }); + + if !children.is_empty() { + // Should be fine because this code is only called for local modules. + self.module_children.insert(def_id.expect_local(), children); } } } @@ -1493,7 +1503,7 @@ fn import_path_to_string(names: &[Ident], import_kind: &ImportKind<'_>, span: Sp let global = !names.is_empty() && names[0].name == kw::PathRoot; if let Some(pos) = pos { let names = if global { &names[1..pos + 1] } else { &names[..pos + 1] }; - names_to_string(&names.iter().map(|ident| ident.name).collect::>()) + names_to_string(names.iter().map(|ident| ident.name)) } else { let names = if global { &names[1..] } else { names }; if names.is_empty() { @@ -1501,7 +1511,7 @@ fn import_path_to_string(names: &[Ident], import_kind: &ImportKind<'_>, span: Sp } else { format!( "{}::{}", - names_to_string(&names.iter().map(|ident| ident.name).collect::>()), + names_to_string(names.iter().map(|ident| ident.name)), import_kind_to_string(import_kind), ) } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 7324d3fe7862d..03aeb8720ca62 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -468,16 +468,12 @@ impl<'a> PathSource<'a> { { "external crate" } - ExprKind::Path(_, path) => { - let mut msg = "function"; - if let Some(segment) = path.segments.iter().last() { - if let Some(c) = segment.ident.to_string().chars().next() { - if c.is_uppercase() { - msg = "function, tuple struct or tuple variant"; - } - } - } - msg + ExprKind::Path(_, path) + if let Some(segment) = path.segments.last() + && let Some(c) = segment.ident.to_string().chars().next() + && c.is_uppercase() => + { + "function, tuple struct or tuple variant" } _ => "function", }, @@ -612,7 +608,7 @@ impl MaybeExported<'_> { return vis.kind.is_pub(); } }; - def_id.map_or(true, |def_id| r.effective_visibilities.is_exported(def_id)) + def_id.is_none_or(|def_id| r.effective_visibilities.is_exported(def_id)) } } @@ -927,6 +923,21 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r self.diag_metadata.current_trait_object = prev; self.diag_metadata.current_type_path = prev_ty; } + + fn visit_ty_pat(&mut self, t: &'ast TyPat) -> Self::Result { + match &t.kind { + TyPatKind::Range(start, end, _) => { + if let Some(start) = start { + self.resolve_anon_const(start, AnonConstKind::ConstArg(IsRepeatExpr::No)); + } + if let Some(end) = end { + self.resolve_anon_const(end, AnonConstKind::ConstArg(IsRepeatExpr::No)); + } + } + TyPatKind::Err(_) => {} + } + } + fn visit_poly_trait_ref(&mut self, tref: &'ast PolyTraitRef) { let span = tref.span.shrink_to_lo().to(tref.trait_ref.path.span.shrink_to_lo()); self.with_generic_param_rib( @@ -990,8 +1001,8 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r match fn_kind { // Bail if the function is foreign, and thus cannot validly have // a body, or if there's no body for some other reason. - FnKind::Fn(FnCtxt::Foreign, _, sig, _, generics, _) - | FnKind::Fn(_, _, sig, _, generics, None) => { + FnKind::Fn(FnCtxt::Foreign, _, _, Fn { sig, generics, .. }) + | FnKind::Fn(_, _, _, Fn { sig, generics, body: None, .. }) => { self.visit_fn_header(&sig.header); self.visit_generics(generics); self.with_lifetime_rib( @@ -1023,7 +1034,7 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r // Create a label rib for the function. this.with_label_rib(RibKind::FnOrCoroutine, |this| { match fn_kind { - FnKind::Fn(_, _, sig, _, generics, body) => { + FnKind::Fn(_, _, _, Fn { sig, generics, contract, body, .. }) => { this.visit_generics(generics); let declaration = &sig.decl; @@ -1050,6 +1061,10 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r }, ); + if let Some(contract) = contract { + this.visit_contract(contract); + } + if let Some(body) = body { // Ignore errors in function bodies if this is rustdoc // Be sure not to set this until the function signature has been resolved. @@ -1182,32 +1197,27 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r // namespace first, and if that fails we try again in the value namespace. If // resolution in the value namespace succeeds, we have an generic const argument on // our hands. - if let TyKind::Path(None, ref path) = ty.kind { + if let TyKind::Path(None, ref path) = ty.kind // We cannot disambiguate multi-segment paths right now as that requires type // checking. - if path.is_potential_trivial_const_arg() { - let mut check_ns = |ns| { - self.maybe_resolve_ident_in_lexical_scope(path.segments[0].ident, ns) - .is_some() - }; - if !check_ns(TypeNS) && check_ns(ValueNS) { - self.resolve_anon_const_manual( - true, - AnonConstKind::ConstArg(IsRepeatExpr::No), - |this| { - this.smart_resolve_path( - ty.id, - &None, - path, - PathSource::Expr(None), - ); - this.visit_path(path, ty.id); - }, - ); + && path.is_potential_trivial_const_arg() + { + let mut check_ns = |ns| { + self.maybe_resolve_ident_in_lexical_scope(path.segments[0].ident, ns) + .is_some() + }; + if !check_ns(TypeNS) && check_ns(ValueNS) { + self.resolve_anon_const_manual( + true, + AnonConstKind::ConstArg(IsRepeatExpr::No), + |this| { + this.smart_resolve_path(ty.id, &None, path, PathSource::Expr(None)); + this.visit_path(path, ty.id); + }, + ); - self.diag_metadata.currently_processing_generic_args = prev; - return; - } + self.diag_metadata.currently_processing_generic_args = prev; + return; } } @@ -1243,54 +1253,56 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r } fn visit_path_segment(&mut self, path_segment: &'ast PathSegment) { - if let Some(ref args) = path_segment.args { - match &**args { - GenericArgs::AngleBracketed(..) => visit::walk_generic_args(self, args), - GenericArgs::Parenthesized(p_args) => { - // Probe the lifetime ribs to know how to behave. - for rib in self.lifetime_ribs.iter().rev() { - match rib.kind { - // We are inside a `PolyTraitRef`. The lifetimes are - // to be introduced in that (maybe implicit) `for<>` binder. - LifetimeRibKind::Generics { - binder, - kind: LifetimeBinderKind::PolyTrait, - .. - } => { - self.with_lifetime_rib( - LifetimeRibKind::AnonymousCreateParameter { + let Some(ref args) = path_segment.args else { + return; + }; + + match &**args { + GenericArgs::AngleBracketed(..) => visit::walk_generic_args(self, args), + GenericArgs::Parenthesized(p_args) => { + // Probe the lifetime ribs to know how to behave. + for rib in self.lifetime_ribs.iter().rev() { + match rib.kind { + // We are inside a `PolyTraitRef`. The lifetimes are + // to be introduced in that (maybe implicit) `for<>` binder. + LifetimeRibKind::Generics { + binder, + kind: LifetimeBinderKind::PolyTrait, + .. + } => { + self.with_lifetime_rib( + LifetimeRibKind::AnonymousCreateParameter { + binder, + report_in_path: false, + }, + |this| { + this.resolve_fn_signature( binder, - report_in_path: false, - }, - |this| { - this.resolve_fn_signature( - binder, - false, - p_args.inputs.iter().map(|ty| (None, &**ty)), - &p_args.output, - ) - }, - ); - break; - } - // We have nowhere to introduce generics. Code is malformed, - // so use regular lifetime resolution to avoid spurious errors. - LifetimeRibKind::Item | LifetimeRibKind::Generics { .. } => { - visit::walk_generic_args(self, args); - break; - } - LifetimeRibKind::AnonymousCreateParameter { .. } - | LifetimeRibKind::AnonymousReportError - | LifetimeRibKind::StaticIfNoLifetimeInScope { .. } - | LifetimeRibKind::Elided(_) - | LifetimeRibKind::ElisionFailure - | LifetimeRibKind::ConcreteAnonConst(_) - | LifetimeRibKind::ConstParamTy => {} + false, + p_args.inputs.iter().map(|ty| (None, &**ty)), + &p_args.output, + ) + }, + ); + break; + } + // We have nowhere to introduce generics. Code is malformed, + // so use regular lifetime resolution to avoid spurious errors. + LifetimeRibKind::Item | LifetimeRibKind::Generics { .. } => { + visit::walk_generic_args(self, args); + break; } + LifetimeRibKind::AnonymousCreateParameter { .. } + | LifetimeRibKind::AnonymousReportError + | LifetimeRibKind::StaticIfNoLifetimeInScope { .. } + | LifetimeRibKind::Elided(_) + | LifetimeRibKind::ElisionFailure + | LifetimeRibKind::ConcreteAnonConst(_) + | LifetimeRibKind::ConstParamTy => {} } } - GenericArgs::ParenthesizedElided(_) => {} } + GenericArgs::ParenthesizedElided(_) => {} } } @@ -1735,13 +1747,8 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } let normalized_ident = ident.normalize_to_macros_2_0(); - let mut outer_res = None; - for rib in lifetime_rib_iter { - if let Some((&outer, _)) = rib.bindings.get_key_value(&normalized_ident) { - outer_res = Some(outer); - break; - } - } + let outer_res = lifetime_rib_iter + .find_map(|rib| rib.bindings.get_key_value(&normalized_ident).map(|(&outer, _)| outer)); self.emit_undeclared_lifetime_error(lifetime, outer_res); self.record_lifetime_res(lifetime.id, LifetimeRes::Error, LifetimeElisionCandidate::Named); @@ -1808,23 +1815,21 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } LifetimeRibKind::AnonymousReportError => { if elided { - let mut suggestion = None; - for rib in self.lifetime_ribs[i..].iter().rev() { + let suggestion = self.lifetime_ribs[i..].iter().rev().find_map(|rib| { if let LifetimeRibKind::Generics { span, kind: LifetimeBinderKind::PolyTrait | LifetimeBinderKind::WhereBound, .. - } = &rib.kind + } = rib.kind { - suggestion = - Some(errors::ElidedAnonymousLivetimeReportErrorSuggestion { - lo: span.shrink_to_lo(), - hi: lifetime.ident.span.shrink_to_hi(), - }); - break; + Some(errors::ElidedAnonymousLivetimeReportErrorSuggestion { + lo: span.shrink_to_lo(), + hi: lifetime.ident.span.shrink_to_hi(), + }) + } else { + None } - } - + }); // are we trying to use an anonymous lifetime // on a non GAT associated trait type? if !self.in_func_body @@ -1833,9 +1838,11 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { && Some(true) == self.diag_metadata.in_non_gat_assoc_type && let crate::ModuleKind::Def(DefKind::Trait, trait_id, _) = module.kind { - if def_id_matches_path(self.r.tcx, trait_id, &[ - "core", "iter", "traits", "iterator", "Iterator", - ]) { + if def_id_matches_path( + self.r.tcx, + trait_id, + &["core", "iter", "traits", "iterator", "Iterator"], + ) { self.r.dcx().emit_err(errors::LendingIteratorReportError { lifetime: lifetime.ident.span, ty: ty.span, @@ -2460,12 +2467,12 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { for i in (0..self.label_ribs.len()).rev() { let rib = &self.label_ribs[i]; - if let RibKind::MacroDefinition(def) = rib.kind { + if let RibKind::MacroDefinition(def) = rib.kind // If an invocation of this macro created `ident`, give up on `ident` // and switch to `ident`'s source from the macro definition. - if def == self.r.macro_def(label.span.ctxt()) { - label.span.remove_mark(); - } + && def == self.r.macro_def(label.span.ctxt()) + { + label.span.remove_mark(); } let ident = label.normalize_to_macro_rules(); @@ -2493,14 +2500,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { /// Determine whether or not a label from the `rib_index`th label rib is reachable. fn is_label_valid_from_rib(&self, rib_index: usize) -> bool { let ribs = &self.label_ribs[rib_index + 1..]; - - for rib in ribs { - if rib.kind.is_label_barrier() { - return false; - } - } - - true + ribs.iter().all(|rib| !rib.kind.is_label_barrier()) } fn resolve_adt(&mut self, item: &'ast Item, generics: &'ast Generics) { @@ -2856,7 +2856,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { match seen_bindings.entry(ident) { Entry::Occupied(entry) => { let span = *entry.get(); - let err = ResolutionError::NameAlreadyUsedInParameterList(ident.name, span); + let err = ResolutionError::NameAlreadyUsedInParameterList(ident, span); self.report_error(param.ident.span, err); let rib = match param.kind { GenericParamKind::Lifetime => { @@ -3442,11 +3442,14 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { match seen_trait_items.entry(id_in_trait) { Entry::Occupied(entry) => { - self.report_error(span, ResolutionError::TraitImplDuplicate { - name: ident.name, - old_span: *entry.get(), - trait_item_span: binding.span, - }); + self.report_error( + span, + ResolutionError::TraitImplDuplicate { + name: ident, + old_span: *entry.get(), + trait_item_span: binding.span, + }, + ); return; } Entry::Vacant(entry) => { @@ -3477,13 +3480,16 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } }; let trait_path = path_names_to_string(path); - self.report_error(span, ResolutionError::TraitImplMismatch { - name: ident.name, - kind, - code, - trait_path, - trait_item_span: binding.span, - }); + self.report_error( + span, + ResolutionError::TraitImplMismatch { + name: ident, + kind, + code, + trait_path, + trait_item_span: binding.span, + }, + ); } fn resolve_const_body(&mut self, expr: &'ast Expr, item: Option<(Ident, ConstantItemKind)>) { @@ -3505,21 +3511,20 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.visit_ty(&qself.ty); } self.visit_path(&delegation.path, delegation.id); - if let Some(body) = &delegation.body { - self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| { - // `PatBoundCtx` is not necessary in this context - let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; - - let span = delegation.path.segments.last().unwrap().ident.span; - this.fresh_binding( - Ident::new(kw::SelfLower, span), - delegation.id, - PatternSource::FnParam, - &mut bindings, - ); - this.visit_block(body); - }); - } + let Some(body) = &delegation.body else { return }; + self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| { + // `PatBoundCtx` is not necessary in this context + let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; + + let span = delegation.path.segments.last().unwrap().ident.span; + this.fresh_binding( + Ident::new(kw::SelfLower, span), + delegation.id, + PatternSource::FnParam, + &mut bindings, + ); + this.visit_block(body); + }); } fn resolve_params(&mut self, params: &'ast [Param]) { @@ -3662,9 +3667,8 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { .filter(|(_, pat)| pat.id != pat_outer.id) .flat_map(|(map, _)| map); - for (key, binding_inner) in inners { - let name = key.name; - match map_outer.get(key) { + for (&name, binding_inner) in inners { + match map_outer.get(&name) { None => { // The inner binding is missing in the outer. let binding_error = @@ -3864,10 +3868,13 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { .patterns_with_skipped_bindings .entry(def_id) .or_default() - .push((pat.span, match rest { - ast::PatFieldsRest::Recovered(guar) => Err(*guar), - _ => Ok(()), - })); + .push(( + pat.span, + match rest { + ast::PatFieldsRest::Recovered(guar) => Err(*guar), + _ => Ok(()), + }, + )); } } ast::PatFieldsRest::None => {} @@ -3902,7 +3909,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // `Variant(a, a)`: _ => IdentifierBoundMoreThanOnceInSamePattern, }; - self.report_error(ident.span, error(ident.name)); + self.report_error(ident.span, error(ident)); } // Record as bound if it's valid: @@ -3960,7 +3967,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { match res { Res::SelfCtor(_) // See #70549. | Res::Def( - DefKind::Ctor(_, CtorKind::Const) | DefKind::Const | DefKind::ConstParam, + DefKind::Ctor(_, CtorKind::Const) | DefKind::Const | DefKind::AssocConst | DefKind::ConstParam, _, ) if is_syntactic_ambiguity => { // Disambiguate in favor of a unit struct/variant or constant pattern. @@ -3969,7 +3976,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } Some(res) } - Res::Def(DefKind::Ctor(..) | DefKind::Const | DefKind::Static { .. }, _) => { + Res::Def(DefKind::Ctor(..) | DefKind::Const | DefKind::AssocConst | DefKind::Static { .. }, _) => { // This is unambiguously a fresh binding, either syntactically // (e.g., `IDENT @ PAT` or `ref IDENT`) or because `IDENT` resolves // to something unusable as a pattern (e.g., constructor function), @@ -4005,7 +4012,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ); None } - Res::Def(DefKind::Fn, _) | Res::Local(..) | Res::Err => { + Res::Def(DefKind::Fn | DefKind::AssocFn, _) | Res::Local(..) | Res::Err => { // These entities are explicitly allowed to be shadowed by fresh bindings. None } @@ -4504,12 +4511,15 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { segment_name, error_implied_by_parse_error: _, } => { - return Err(respan(span, ResolutionError::FailedToResolve { - segment: Some(segment_name), - label, - suggestion, - module, - })); + return Err(respan( + span, + ResolutionError::FailedToResolve { + segment: Some(segment_name), + label, + suggestion, + module, + }, + )); } PathResult::Module(..) | PathResult::Failed { .. } => return Ok(None), PathResult::Indeterminate => bug!("indeterminate path result in resolve_qpath"), @@ -5019,12 +5029,13 @@ struct ItemInfoCollector<'a, 'ra, 'tcx> { } impl ItemInfoCollector<'_, '_, '_> { - fn collect_fn_info(&mut self, sig: &FnSig, id: NodeId) { + fn collect_fn_info(&mut self, sig: &FnSig, id: NodeId, attrs: &[Attribute]) { let sig = DelegationFnSig { header: sig.header, param_count: sig.decl.inputs.len(), has_self: sig.decl.has_self(), c_variadic: sig.decl.c_variadic(), + target_feature: attrs.iter().any(|attr| attr.has_name(sym::target_feature)), }; self.r.delegation_fn_sigs.insert(self.r.local_def_id(id), sig); } @@ -5043,7 +5054,7 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { | ItemKind::Trait(box Trait { ref generics, .. }) | ItemKind::TraitAlias(ref generics, _) => { if let ItemKind::Fn(box Fn { ref sig, .. }) = &item.kind { - self.collect_fn_info(sig, item.id); + self.collect_fn_info(sig, item.id, &item.attrs); } let def_id = self.r.local_def_id(item.id); @@ -5076,7 +5087,7 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { fn visit_assoc_item(&mut self, item: &'ast AssocItem, ctxt: AssocCtxt) { if let AssocItemKind::Fn(box Fn { ref sig, .. }) = &item.kind { - self.collect_fn_info(sig, item.id); + self.collect_fn_info(sig, item.id, &item.attrs); } visit::walk_assoc_item(self, item, ctxt); } @@ -5103,7 +5114,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn def_id_matches_path(tcx: TyCtxt<'_>, mut def_id: DefId, expected_path: &[&str]) -> bool { let mut path = expected_path.iter().rev(); while let (Some(parent), Some(next_step)) = (tcx.opt_parent(def_id), path.next()) { - if !tcx.opt_item_name(def_id).map_or(false, |n| n.as_str() == *next_step) { + if !tcx.opt_item_name(def_id).is_some_and(|n| n.as_str() == *next_step) { return false; } def_id = parent; diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 6ee02e9f47f15..0962865e7f190 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -224,7 +224,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let suggestion = if self.current_trait_ref.is_none() && let Some((fn_kind, _)) = self.diag_metadata.current_function && let Some(FnCtxt::Assoc(_)) = fn_kind.ctxt() - && let FnKind::Fn(_, _, sig, ..) = fn_kind + && let FnKind::Fn(_, _, _, ast::Fn { sig, .. }) = fn_kind && let Some(items) = self.diag_metadata.current_impl_items && let Some(item) = items.iter().find(|i| { i.ident.name == item_str.name @@ -560,7 +560,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { Applicability::MaybeIncorrect, ); if !self.self_value_is_available(path[0].ident.span) { - if let Some((FnKind::Fn(_, _, sig, ..), fn_span)) = + if let Some((FnKind::Fn(_, _, _, ast::Fn { sig, .. }), fn_span)) = &self.diag_metadata.current_function { let (span, sugg) = if let Some(param) = sig.decl.inputs.get(0) { @@ -1048,12 +1048,15 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { debug!("smart_resolve_path_fragment: E0424, source={:?}", source); err.code(E0424); - err.span_label(span, match source { - PathSource::Pat => { - "`self` value is a keyword and may not be bound to variables or shadowed" - } - _ => "`self` value is a keyword only available in methods with a `self` parameter", - }); + err.span_label( + span, + match source { + PathSource::Pat => { + "`self` value is a keyword and may not be bound to variables or shadowed" + } + _ => "`self` value is a keyword only available in methods with a `self` parameter", + }, + ); let is_assoc_fn = self.self_type_is_available(); let self_from_macro = "a `self` parameter, but a macro invocation can only \ access identifiers it receives from parameters"; @@ -1130,7 +1133,9 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let None = following_seg else { return }; for rib in self.ribs[ValueNS].iter().rev() { for (def_id, spans) in &rib.patterns_with_skipped_bindings { - if let Some(fields) = self.r.field_idents(*def_id) { + if let DefKind::Struct | DefKind::Variant = self.r.tcx.def_kind(*def_id) + && let Some(fields) = self.r.field_idents(*def_id) + { for field in fields { if field.name == segment.ident.name { if spans.iter().all(|(_, had_error)| had_error.is_err()) { @@ -1634,13 +1639,12 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { .enumerate() .map(|(idx, new)| (new, old_fields.get(idx))) .map(|(new, old)| { - let new = new.name.to_ident_string(); if let Some(Some(old)) = old - && new != *old + && new.as_str() != old { format!("{new}: {old}") } else { - new + new.to_string() } }) .collect::>() @@ -1694,7 +1698,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { ) => { // Don't suggest macro if it's unstable. let suggestable = def_id.is_local() - || self.r.tcx.lookup_stability(def_id).map_or(true, |s| s.is_stable()); + || self.r.tcx.lookup_stability(def_id).is_none_or(|s| s.is_stable()); err.span_label(span, fallback_label.to_string()); @@ -2133,7 +2137,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { .r .delegation_fn_sigs .get(&self.r.local_def_id(assoc_item.id)) - .map_or(false, |sig| sig.has_self) => + .is_some_and(|sig| sig.has_self) => { AssocSuggestion::MethodWithSelf { called } } @@ -2164,7 +2168,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { .r .delegation_fn_sigs .get(&def_id) - .map_or(false, |sig| sig.has_self), + .is_some_and(|sig| sig.has_self), None => self .r .tcx @@ -2343,9 +2347,14 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // try to give a suggestion for this pattern: `name = blah`, which is common in other languages // suggest `let name = blah` to introduce a new binding fn let_binding_suggestion(&mut self, err: &mut Diag<'_>, ident_span: Span) -> bool { + if ident_span.from_expansion() { + return false; + } + + // only suggest when the code is a assignment without prefix code if let Some(Expr { kind: ExprKind::Assign(lhs, ..), .. }) = self.diag_metadata.in_assignment && let ast::ExprKind::Path(None, ref path) = lhs.kind - && !ident_span.from_expansion() + && self.r.tcx.sess.source_map().is_line_before_span_empty(ident_span) { let (span, text) = match path.segments.first() { Some(seg) if let Some(name) = seg.ident.as_str().strip_prefix("let") => { @@ -2364,6 +2373,22 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { ); return true; } + + // a special case for #133713 + // '=' maybe a typo of `:`, which is a type annotation instead of assignment + if err.code == Some(E0423) + && let Some((let_span, None, Some(val_span))) = self.diag_metadata.current_let_binding + && val_span.contains(ident_span) + && val_span.lo() == ident_span.lo() + { + err.span_suggestion_verbose( + let_span.shrink_to_hi().to(val_span.shrink_to_lo()), + "you might have meant to use `:` for type annotation", + ": ", + Applicability::MaybeIncorrect, + ); + return true; + } false } @@ -2398,15 +2423,18 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { if module_def_id == def_id { let path = Path { span: name_binding.span, segments: path_segments, tokens: None }; - result = Some((module, ImportSuggestion { - did: Some(def_id), - descr: "module", - path, - accessible: true, - doc_visible, - note: None, - via_import: false, - })); + result = Some(( + module, + ImportSuggestion { + did: Some(def_id), + descr: "module", + path, + accessible: true, + doc_visible, + note: None, + via_import: false, + }, + )); } else { // add the module to the lookup if seen_modules.insert(module_def_id) { @@ -3155,7 +3183,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } } - #[cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] + #[allow(rustc::symbol_intern_string_literal)] let existing_name = match &in_scope_lifetimes[..] { [] => Symbol::intern("'a"), [(existing, _)] => existing.name, @@ -3248,7 +3276,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { { let pre = if lt.kind == MissingLifetimeKind::Ampersand && let Some((kind, _span)) = self.diag_metadata.current_function - && let FnKind::Fn(_, _, sig, _, _, _) = kind + && let FnKind::Fn(_, _, _, ast::Fn { sig, .. }) = kind && !sig.decl.inputs.is_empty() && let sugg = sig .decl @@ -3289,7 +3317,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } else if (lt.kind == MissingLifetimeKind::Ampersand || lt.kind == MissingLifetimeKind::Underscore) && let Some((kind, _span)) = self.diag_metadata.current_function - && let FnKind::Fn(_, _, sig, _, _, _) = kind + && let FnKind::Fn(_, _, _, ast::Fn { sig, .. }) = kind && let ast::FnRetTy::Ty(ret_ty) = &sig.decl.output && !sig.decl.inputs.is_empty() && let arg_refs = sig @@ -3349,7 +3377,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let mut owned_sugg = lt.kind == MissingLifetimeKind::Ampersand; let mut sugg = vec![(lt.span, String::new())]; if let Some((kind, _span)) = self.diag_metadata.current_function - && let FnKind::Fn(_, _, sig, _, _, _) = kind + && let FnKind::Fn(_, _, _, ast::Fn { sig, .. }) = kind && let ast::FnRetTy::Ty(ty) = &sig.decl.output { let mut lt_finder = diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index cc9ed566edac5..90191b7776f32 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -27,6 +27,7 @@ use std::cell::{Cell, RefCell}; use std::collections::BTreeSet; use std::fmt; +use std::sync::Arc; use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion}; use effective_visibilities::EffectiveVisibilitiesVisitor; @@ -46,7 +47,7 @@ use rustc_ast::{ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::intern::Interned; use rustc_data_structures::steal::Steal; -use rustc_data_structures::sync::{FreezeReadGuard, Lrc}; +use rustc_data_structures::sync::FreezeReadGuard; use rustc_errors::{Applicability, Diag, ErrCode, ErrorGuaranteed}; use rustc_expand::base::{DeriveResolution, SyntaxExtension, SyntaxExtensionKind}; use rustc_feature::BUILTIN_ATTRIBUTES; @@ -214,7 +215,7 @@ enum Used { #[derive(Debug)] struct BindingError { - name: Symbol, + name: Ident, origin: BTreeSet, target: BTreeSet, could_be_path: bool, @@ -226,7 +227,7 @@ enum ResolutionError<'ra> { GenericParamsFromOuterItem(Res, HasGenericParams, DefKind), /// Error E0403: the name is already used for a type or const parameter in this generic /// parameter list. - NameAlreadyUsedInParameterList(Symbol, Span), + NameAlreadyUsedInParameterList(Ident, Span), /// Error E0407: method is not a member of trait. MethodNotMemberOfTrait(Ident, String, Option), /// Error E0437: type is not a member of trait. @@ -236,11 +237,11 @@ enum ResolutionError<'ra> { /// Error E0408: variable `{}` is not bound in all patterns. VariableNotBoundInPattern(BindingError, ParentScope<'ra>), /// Error E0409: variable `{}` is bound in inconsistent ways within the same match arm. - VariableBoundWithDifferentMode(Symbol, Span), + VariableBoundWithDifferentMode(Ident, Span), /// Error E0415: identifier is bound more than once in this parameter list. - IdentifierBoundMoreThanOnceInParameterList(Symbol), + IdentifierBoundMoreThanOnceInParameterList(Ident), /// Error E0416: identifier is bound more than once in the same pattern. - IdentifierBoundMoreThanOnceInSamePattern(Symbol), + IdentifierBoundMoreThanOnceInSamePattern(Ident), /// Error E0426: use of undeclared label. UndeclaredLabel { name: Symbol, suggestion: Option }, /// Error E0429: `self` imports are only allowed within a `{ }` list. @@ -292,14 +293,14 @@ enum ResolutionError<'ra> { UnreachableLabel { name: Symbol, definition_span: Span, suggestion: Option }, /// Error E0323, E0324, E0325: mismatch between trait item and impl item. TraitImplMismatch { - name: Symbol, + name: Ident, kind: &'static str, trait_path: String, trait_item_span: Span, code: ErrCode, }, /// Error E0201: multiple impl items for the same trait item. - TraitImplDuplicate { name: Symbol, trait_item_span: Span, old_span: Span }, + TraitImplDuplicate { name: Ident, trait_item_span: Span, old_span: Span }, /// Inline asm `sym` operand must refer to a `fn` or `static`. InvalidAsmSym, /// `self` used instead of `Self` in a generic parameter @@ -357,7 +358,7 @@ impl Segment { } fn names_to_string(segments: &[Segment]) -> String { - names_to_string(&segments.iter().map(|seg| seg.ident.name).collect::>()) + names_to_string(segments.iter().map(|seg| seg.ident.name)) } } @@ -920,10 +921,13 @@ impl<'ra> NameBindingData<'ra> { } fn is_importable(&self) -> bool { - !matches!( - self.res(), - Res::Def(DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy, _) - ) + !matches!(self.res(), Res::Def(DefKind::AssocTy, _)) + } + + // FIXME(import_trait_associated_functions): associate `const` or `fn` are not importable unless + // the feature `import_trait_associated_functions` is enable + fn is_assoc_const_or_fn(&self) -> bool { + matches!(self.res(), Res::Def(DefKind::AssocConst | DefKind::AssocFn, _)) } fn macro_kind(&self) -> Option { @@ -992,13 +996,13 @@ struct DeriveData { } struct MacroData { - ext: Lrc, + ext: Arc, rule_spans: Vec<(usize, Span)>, macro_rules: bool, } impl MacroData { - fn new(ext: Lrc) -> MacroData { + fn new(ext: Arc) -> MacroData { MacroData { ext, rule_spans: Vec::new(), macro_rules: false } } } @@ -1107,8 +1111,8 @@ pub struct Resolver<'ra, 'tcx> { registered_tools: &'tcx RegisteredTools, macro_use_prelude: FxHashMap>, macro_map: FxHashMap, - dummy_ext_bang: Lrc, - dummy_ext_derive: Lrc, + dummy_ext_bang: Arc, + dummy_ext_derive: Arc, non_macro_attr: MacroData, local_macro_def_scopes: FxHashMap>, ast_transform_scopes: FxHashMap>, @@ -1239,7 +1243,7 @@ impl<'ra> ResolverArenas<'ra> { no_implicit_prelude, )))); let def_id = module.opt_def_id(); - if def_id.map_or(true, |def_id| def_id.is_local()) { + if def_id.is_none_or(|def_id| def_id.is_local()) { self.local_modules.borrow_mut().push(module); } if let Some(def_id) = def_id { @@ -1507,9 +1511,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { registered_tools, macro_use_prelude: FxHashMap::default(), macro_map: FxHashMap::default(), - dummy_ext_bang: Lrc::new(SyntaxExtension::dummy_bang(edition)), - dummy_ext_derive: Lrc::new(SyntaxExtension::dummy_derive(edition)), - non_macro_attr: MacroData::new(Lrc::new(SyntaxExtension::non_macro_attr(edition))), + dummy_ext_bang: Arc::new(SyntaxExtension::dummy_bang(edition)), + dummy_ext_derive: Arc::new(SyntaxExtension::dummy_derive(edition)), + non_macro_attr: MacroData::new(Arc::new(SyntaxExtension::non_macro_attr(edition))), invocation_parent_scopes: Default::default(), output_macro_rules_scopes: Default::default(), macro_rules_scopes: Default::default(), @@ -1685,11 +1689,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { CStore::from_tcx(self.tcx) } - fn dummy_ext(&self, macro_kind: MacroKind) -> Lrc { + fn dummy_ext(&self, macro_kind: MacroKind) -> Arc { match macro_kind { - MacroKind::Bang => Lrc::clone(&self.dummy_ext_bang), - MacroKind::Derive => Lrc::clone(&self.dummy_ext_derive), - MacroKind::Attr => Lrc::clone(&self.non_macro_attr.ext), + MacroKind::Bang => Arc::clone(&self.dummy_ext_bang), + MacroKind::Derive => Arc::clone(&self.dummy_ext_derive), + MacroKind::Attr => Arc::clone(&self.non_macro_attr.ext), } } @@ -2237,13 +2241,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } -fn names_to_string(names: &[Symbol]) -> String { +fn names_to_string(names: impl Iterator) -> String { let mut result = String::new(); - for (i, name) in names.iter().filter(|name| **name != kw::PathRoot).enumerate() { + for (i, name) in names.filter(|name| *name != kw::PathRoot).enumerate() { if i > 0 { result.push_str("::"); } - if Ident::with_dummy_span(*name).is_raw_guess() { + if Ident::with_dummy_span(name).is_raw_guess() { result.push_str("r#"); } result.push_str(name.as_str()); @@ -2252,31 +2256,32 @@ fn names_to_string(names: &[Symbol]) -> String { } fn path_names_to_string(path: &Path) -> String { - names_to_string(&path.segments.iter().map(|seg| seg.ident.name).collect::>()) + names_to_string(path.segments.iter().map(|seg| seg.ident.name)) } /// A somewhat inefficient routine to obtain the name of a module. -fn module_to_string(module: Module<'_>) -> Option { +fn module_to_string(mut module: Module<'_>) -> Option { let mut names = Vec::new(); - - fn collect_mod(names: &mut Vec, module: Module<'_>) { + loop { if let ModuleKind::Def(.., name) = module.kind { if let Some(parent) = module.parent { names.push(name); - collect_mod(names, parent); + module = parent + } else { + break; } } else { names.push(sym::opaque_module_name_placeholder); - collect_mod(names, module.parent.unwrap()); + let Some(parent) = module.parent else { + return None; + }; + module = parent; } } - collect_mod(&mut names, module); - if names.is_empty() { return None; } - names.reverse(); - Some(names_to_string(&names)) + Some(names_to_string(names.iter().rev().copied())) } #[derive(Copy, Clone, Debug)] diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index b24e343c58df8..cca01a01e9877 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -3,18 +3,17 @@ use std::cell::Cell; use std::mem; +use std::sync::Arc; use rustc_ast::attr::AttributeExt; use rustc_ast::expand::StrippedCfgItem; -use rustc_ast::{self as ast, Crate, Inline, ItemKind, ModKind, NodeId, attr}; +use rustc_ast::{self as ast, Crate, NodeId, attr}; use rustc_ast_pretty::pprust; use rustc_attr_parsing::StabilityLevel; use rustc_data_structures::intern::Interned; -use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, StashKey}; use rustc_expand::base::{ - Annotatable, DeriveResolution, Indeterminate, ResolverExpand, SyntaxExtension, - SyntaxExtensionKind, + DeriveResolution, Indeterminate, ResolverExpand, SyntaxExtension, SyntaxExtensionKind, }; use rustc_expand::compile_declarative_macro; use rustc_expand::expand::{ @@ -26,8 +25,8 @@ use rustc_middle::middle::stability; use rustc_middle::ty::{RegisteredTools, TyCtxt, Visibility}; use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{ - LEGACY_DERIVE_HELPERS, OUT_OF_SCOPE_MACRO_CALLS, SOFT_UNSTABLE, - UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNUSED_MACRO_RULES, UNUSED_MACROS, + LEGACY_DERIVE_HELPERS, OUT_OF_SCOPE_MACRO_CALLS, UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + UNUSED_MACRO_RULES, UNUSED_MACROS, }; use rustc_session::parse::feature_err; use rustc_span::edit_distance::edit_distance; @@ -157,26 +156,6 @@ pub(crate) fn registered_tools(tcx: TyCtxt<'_>, (): ()) -> RegisteredTools { registered_tools } -// Some feature gates for inner attributes are reported as lints for backward compatibility. -fn soft_custom_inner_attributes_gate(path: &ast::Path, invoc: &Invocation) -> bool { - match &path.segments[..] { - // `#![test]` - [seg] if seg.ident.name == sym::test => return true, - // `#![rustfmt::skip]` on out-of-line modules - [seg1, seg2] if seg1.ident.name == sym::rustfmt && seg2.ident.name == sym::skip => { - if let InvocationKind::Attr { item, .. } = &invoc.kind { - if let Annotatable::Item(item) = item { - if let ItemKind::Mod(_, ModKind::Loaded(_, Inline::No, _, _)) = item.kind { - return true; - } - } - } - } - _ => {} - } - false -} - impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { fn next_node_id(&mut self) -> NodeId { self.next_node_id() @@ -260,7 +239,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { invoc: &Invocation, eager_expansion_root: LocalExpnId, force: bool, - ) -> Result, Indeterminate> { + ) -> Result, Indeterminate> { let invoc_id = invoc.expansion_data.id; let parent_scope = match self.invocation_parent_scopes.get(&invoc_id) { Some(parent_scope) => *parent_scope, @@ -317,7 +296,6 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { parent_scope, node_id, force, - soft_custom_inner_attributes_gate(path, invoc), deleg_impl, looks_like_invoc_in_mod_inert_attr, )?; @@ -549,10 +527,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { parent_scope: &ParentScope<'ra>, node_id: NodeId, force: bool, - soft_custom_inner_attributes_gate: bool, deleg_impl: Option, invoc_in_mod_inert_attr: Option, - ) -> Result<(Lrc, Res), Indeterminate> { + ) -> Result<(Arc, Res), Indeterminate> { let (ext, res) = match self.resolve_macro_or_delegation_path( path, Some(kind), @@ -667,22 +644,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Res::NonMacroAttr(..) => false, _ => unreachable!(), }; - if soft_custom_inner_attributes_gate { - self.tcx.sess.psess.buffer_lint( - SOFT_UNSTABLE, - path.span, - node_id, - BuiltinLintDiag::InnerAttributeUnstable { is_macro }, - ); + let msg = if is_macro { + "inner macro attributes are unstable" } else { - // FIXME: deduplicate with rustc_lint (`BuiltinLintDiag::InnerAttributeUnstable`) - let msg = if is_macro { - "inner macro attributes are unstable" - } else { - "custom inner attributes are unstable" - }; - feature_err(&self.tcx.sess, sym::custom_inner_attributes, path.span, msg).emit(); - } + "custom inner attributes are unstable" + }; + feature_err(&self.tcx.sess, sym::custom_inner_attributes, path.span, msg).emit(); } if res == Res::NonMacroAttr(NonMacroAttrKind::Tool) @@ -715,7 +682,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { trace: bool, force: bool, ignore_import: Option>, - ) -> Result<(Option>, Res), Determinacy> { + ) -> Result<(Option>, Res), Determinacy> { self.resolve_macro_or_delegation_path( path, kind, @@ -738,7 +705,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { deleg_impl: Option, invoc_in_mod_inert_attr: Option<(LocalDefId, NodeId)>, ignore_import: Option>, - ) -> Result<(Option>, Res), Determinacy> { + ) -> Result<(Option>, Res), Determinacy> { let path_span = ast_path.span; let mut path = Segment::from_path(ast_path); @@ -821,11 +788,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Some(impl_def_id) => match res { def::Res::Def(DefKind::Trait, def_id) => { let edition = self.tcx.sess.edition(); - Some(Lrc::new(SyntaxExtension::glob_delegation(def_id, impl_def_id, edition))) + Some(Arc::new(SyntaxExtension::glob_delegation(def_id, impl_def_id, edition))) } _ => None, }, - None => self.get_macro(res).map(|macro_data| Lrc::clone(¯o_data.ext)), + None => self.get_macro(res).map(|macro_data| Arc::clone(¯o_data.ext)), }; Ok((ext, res)) } @@ -921,12 +888,15 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { None, ) }; - self.report_error(span, ResolutionError::FailedToResolve { - segment: path.last().map(|segment| segment.ident.name), - label, - suggestion, - module, - }); + self.report_error( + span, + ResolutionError::FailedToResolve { + segment: path.last().map(|segment| segment.ident.name), + label, + suggestion, + module, + }, + ); } PathResult::Module(..) | PathResult::Indeterminate => unreachable!(), } @@ -1031,6 +1001,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { is_soft, span, soft_handler, + stability::UnstableKind::Regular, ); } } @@ -1054,7 +1025,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { span: Span, ) { if let Some(Res::NonMacroAttr(kind)) = res { - if kind != NonMacroAttrKind::Tool && binding.map_or(true, |b| b.is_import()) { + if kind != NonMacroAttrKind::Tool && binding.is_none_or(|b| b.is_import()) { let binding_span = binding.map(|binding| binding.span); self.dcx().emit_err(errors::CannotUseThroughAnImport { span, @@ -1162,7 +1133,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } - MacroData { ext: Lrc::new(ext), rule_spans, macro_rules: macro_def.macro_rules } + MacroData { ext: Arc::new(ext), rule_spans, macro_rules: macro_def.macro_rules } } fn path_accessible( diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index 84e43d0e01660..fecb973501933 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -347,13 +347,10 @@ pub fn strip_generics_from_path(path_str: &str) -> Result, MalformedGen /// Returns whether the first doc-comment is an inner attribute. /// -//// If there are no doc-comments, return true. +/// If there are no doc-comments, return true. /// FIXME(#78591): Support both inner and outer attributes on the same item. pub fn inner_docs(attrs: &[impl AttributeExt]) -> bool { - attrs - .iter() - .find(|a| a.doc_str().is_some()) - .map_or(true, |a| a.style() == ast::AttrStyle::Inner) + attrs.iter().find(|a| a.doc_str().is_some()).is_none_or(|a| a.style() == ast::AttrStyle::Inner) } /// Has `#[rustc_doc_primitive]` or `#[doc(keyword)]`. diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index 895259d52a7fd..5f0c1afdf6420 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -103,7 +103,7 @@ fn encode_args<'tcx>( /// ). fn encode_const<'tcx>( tcx: TyCtxt<'tcx>, - c: Const<'tcx>, + ct: Const<'tcx>, ct_ty: Ty<'tcx>, dict: &mut FxHashMap, usize>, options: EncodeTyOptions, @@ -111,7 +111,7 @@ fn encode_const<'tcx>( // L[n][]E as literal argument let mut s = String::from('L'); - match c.kind() { + match ct.kind() { // Const parameters ty::ConstKind::Param(..) => { // LE as literal argument @@ -121,18 +121,18 @@ fn encode_const<'tcx>( } // Literal arguments - ty::ConstKind::Value(ct_ty, ..) => { + ty::ConstKind::Value(cv) => { // L[n]E as literal argument // Element type - s.push_str(&encode_ty(tcx, ct_ty, dict, options)); + s.push_str(&encode_ty(tcx, cv.ty, dict, options)); // The only allowed types of const values are bool, u8, u16, u32, // u64, u128, usize i8, i16, i32, i64, i128, isize, and char. The // bool value false is encoded as 0 and true as 1. - match ct_ty.kind() { + match cv.ty.kind() { ty::Int(ity) => { - let bits = c + let bits = cv .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized()) .expect("expected monomorphic const in cfi"); let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128; @@ -142,30 +142,30 @@ fn encode_const<'tcx>( let _ = write!(s, "{val}"); } ty::Uint(_) => { - let val = c + let val = cv .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized()) .expect("expected monomorphic const in cfi"); let _ = write!(s, "{val}"); } ty::Bool => { - let val = c.try_to_bool().expect("expected monomorphic const in cfi"); + let val = cv.try_to_bool().expect("expected monomorphic const in cfi"); let _ = write!(s, "{val}"); } _ => { - bug!("encode_const: unexpected type `{:?}`", ct_ty); + bug!("encode_const: unexpected type `{:?}`", cv.ty); } } } _ => { - bug!("encode_const: unexpected kind `{:?}`", c.kind()); + bug!("encode_const: unexpected kind `{:?}`", ct.kind()); } } // Close the "L..E" pair s.push('E'); - compress(dict, DictKey::Const(c), &mut s); + compress(dict, DictKey::Const(ct), &mut s); s } @@ -448,10 +448,9 @@ pub(crate) fn encode_ty<'tcx>( if let Some(cfi_encoding) = tcx.get_attr(def_id, sym::cfi_encoding) { // Use user-defined CFI encoding for type if let Some(value_str) = cfi_encoding.value_str() { - let value_str = value_str.to_string(); - let str = value_str.trim(); - if !str.is_empty() { - s.push_str(str); + let value_str = value_str.as_str().trim(); + if !value_str.is_empty() { + s.push_str(value_str); // Don't compress user-defined builtin types (see // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-builtin and // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression). @@ -459,7 +458,7 @@ pub(crate) fn encode_ty<'tcx>( "v", "w", "b", "c", "a", "h", "s", "t", "i", "j", "l", "m", "x", "y", "n", "o", "f", "d", "e", "g", "z", "Dh", ]; - if !builtin_types.contains(&str) { + if !builtin_types.contains(&value_str) { compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); } } else { diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 9c6186d688284..b711c238d594b 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -51,8 +51,7 @@ impl<'tcx> TypeFolder> for TransformTy<'tcx> { // Transforms a ty:Ty for being encoded and used in the substitution dictionary. fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { match t.kind() { - ty::Array(..) - | ty::Closure(..) + ty::Closure(..) | ty::Coroutine(..) | ty::CoroutineClosure(..) | ty::CoroutineWitness(..) @@ -67,6 +66,13 @@ impl<'tcx> TypeFolder> for TransformTy<'tcx> { | ty::Tuple(..) | ty::UnsafeBinder(_) => t.super_fold_with(self), + // Don't transform the type of the array length and keep it as `usize`. + // This is required for `try_to_target_usize` to work correctly. + &ty::Array(inner, len) => { + let inner = self.fold_ty(inner); + Ty::new_array_with_const_len(self.tcx, inner, len) + } + ty::Bool => { if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { // Note: on all platforms that Rust's currently supports, its size and alignment diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index e9983699609da..b9c535df4bd09 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -31,7 +31,7 @@ libc = "0.2" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] -version = "0.57.0" +version = "0.59.0" features = [ "Win32_Foundation", "Win32_System_LibraryLoader", diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index eb14b78a0030b..e5fba8cc5a2d2 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -133,7 +133,8 @@ session_unstable_virtual_function_elimination = `-Zvirtual-function-elimination` session_unsupported_crate_type_for_target = dropping unsupported crate type `{$crate_type}` for target `{$target_triple}` -session_unsupported_dwarf_version = requested DWARF version {$dwarf_version} is greater than 5 +session_unsupported_dwarf_version = requested DWARF version {$dwarf_version} is not supported +session_unsupported_dwarf_version_help = supported DWARF versions are 2, 3, 4 and 5 session_unsupported_reg_struct_return_arch = `-Zreg-struct-return` is only supported on x86 session_unsupported_regparm = `-Zregparm={$regparm}` is unsupported (valid values 0-3) diff --git a/compiler/rustc_session/src/code_stats.rs b/compiler/rustc_session/src/code_stats.rs index f3c21992784c0..b4597ae2515d4 100644 --- a/compiler/rustc_session/src/code_stats.rs +++ b/compiler/rustc_session/src/code_stats.rs @@ -1,10 +1,9 @@ use std::cmp; use rustc_abi::{Align, Size}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lock; use rustc_span::Symbol; -use rustc_span::def_id::DefId; #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub struct VariantInfo { @@ -71,29 +70,9 @@ pub struct TypeSizeInfo { pub variants: Vec, } -pub struct VTableSizeInfo { - pub trait_name: String, - - /// Number of entries in a vtable with the current algorithm - /// (i.e. with upcasting). - pub entries: usize, - - /// Number of entries in a vtable, as-if we did not have trait upcasting. - pub entries_ignoring_upcasting: usize, - - /// Number of entries in a vtable needed solely for upcasting - /// (i.e. `entries - entries_ignoring_upcasting`). - pub entries_for_upcasting: usize, - - /// Cost of having upcasting in % relative to the number of entries without - /// upcasting (i.e. `entries_for_upcasting / entries_ignoring_upcasting * 100%`). - pub upcasting_cost_percent: f64, -} - #[derive(Default)] pub struct CodeStats { type_sizes: Lock>, - vtable_sizes: Lock>, } impl CodeStats { @@ -127,14 +106,6 @@ impl CodeStats { self.type_sizes.borrow_mut().insert(info); } - pub fn record_vtable_size(&self, trait_did: DefId, trait_name: &str, info: VTableSizeInfo) { - let prev = self.vtable_sizes.lock().insert(trait_did, info); - assert!( - prev.is_none(), - "size of vtable for `{trait_name}` ({trait_did:?}) is already recorded" - ); - } - pub fn print_type_sizes(&self) { let type_sizes = self.type_sizes.borrow(); // We will soon sort, so the initial order does not matter. @@ -238,33 +209,4 @@ impl CodeStats { } } } - - pub fn print_vtable_sizes(&self, crate_name: Symbol) { - // We will soon sort, so the initial order does not matter. - #[allow(rustc::potential_query_instability)] - let mut infos = - std::mem::take(&mut *self.vtable_sizes.lock()).into_values().collect::>(); - - // Primary sort: cost % in reverse order (from largest to smallest) - // Secondary sort: trait_name - infos.sort_by(|a, b| { - a.upcasting_cost_percent - .total_cmp(&b.upcasting_cost_percent) - .reverse() - .then_with(|| a.trait_name.cmp(&b.trait_name)) - }); - - for VTableSizeInfo { - trait_name, - entries, - entries_ignoring_upcasting, - entries_for_upcasting, - upcasting_cost_percent, - } in infos - { - println!( - r#"print-vtable-sizes {{ "crate_name": "{crate_name}", "trait_name": "{trait_name}", "entries": "{entries}", "entries_ignoring_upcasting": "{entries_ignoring_upcasting}", "entries_for_upcasting": "{entries_for_upcasting}", "upcasting_cost_percent": "{upcasting_cost_percent}" }}"# - ); - } - } } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 5c36c98649020..7d473b86ff58a 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -86,12 +86,18 @@ pub enum CFProtection { #[derive(Clone, Copy, Debug, PartialEq, Hash, HashStable_Generic)] pub enum OptLevel { - No, // -O0 - Less, // -O1 - Default, // -O2 - Aggressive, // -O3 - Size, // -Os - SizeMin, // -Oz + /// `-Copt-level=0` + No, + /// `-Copt-level=1` + Less, + /// `-Copt-level=2` + More, + /// `-Copt-level=3` / `-O` + Aggressive, + /// `-Copt-level=s` + Size, + /// `-Copt-level=z` + SizeMin, } /// This is what the `LtoCli` values get mapped to after resolving defaults and @@ -132,13 +138,6 @@ pub enum LtoCli { } /// The different settings that the `-C instrument-coverage` flag can have. -/// -/// Coverage instrumentation now supports combining `-C instrument-coverage` -/// with compiler and linker optimization (enabled with `-O` or `-C opt-level=1` -/// and higher). Nevertheless, there are many variables, depending on options -/// selected, code structure, and enabled attributes. If errors are encountered, -/// either while compiling or when generating `llvm-cov show` reports, consider -/// lowering the optimization level, or including/excluding `-C link-dead-code`. #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum InstrumentCoverage { /// `-C instrument-coverage=no` (or `off`, `false` etc.) @@ -196,6 +195,39 @@ pub enum CoverageLevel { Mcdc, } +/// The different settings that the `-Z autodiff` flag can have. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum AutoDiff { + /// Print TypeAnalysis information + PrintTA, + /// Print ActivityAnalysis Information + PrintAA, + /// Print Performance Warnings from Enzyme + PrintPerf, + /// Combines the three print flags above. + Print, + /// Print the whole module, before running opts. + PrintModBefore, + /// Print the whole module just before we pass it to Enzyme. + /// For Debug purpose, prefer the OPT flag below + PrintModAfterOpts, + /// Print the module after Enzyme differentiated everything. + PrintModAfterEnzyme, + + /// Enzyme's loose type debug helper (can cause incorrect gradients) + LooseTypes, + + /// More flags + NoModOptAfter, + /// Tell Enzyme to run LLVM Opts on each function it generated. By default off, + /// since we already optimize the whole module after Enzyme is done. + EnableFncOpt, + NoVecUnroll, + RuntimeActivity, + /// Runs Enzyme specific Inlining + Inline, +} + /// Settings for `-Z instrument-xray` flag. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] pub struct InstrumentXRay { @@ -682,7 +714,7 @@ impl OutputTypes { /// Returns `true` if user specified a name and not just produced type pub fn contains_explicit_name(&self, key: &OutputType) -> bool { - self.0.get(key).map_or(false, |f| f.is_some()) + matches!(self.0.get(key), Some(Some(..))) } pub fn iter(&self) -> BTreeMapIter<'_, OutputType, Option> { @@ -895,7 +927,7 @@ impl Input { Input::File(file) => Some(file), Input::Str { name, .. } => match name { FileName::Real(real) => real.local_path(), - FileName::QuoteExpansion(_) => None, + FileName::CfgSpec(_) => None, FileName::Anon(_) => None, FileName::MacroExpansion(_) => None, FileName::ProcMacroSourceCode(_) => None, @@ -1198,6 +1230,7 @@ impl Default for Options { color: ColorConfig::Auto, logical_env: FxIndexMap::default(), verbose: false, + target_modifiers: BTreeMap::default(), } } } @@ -1226,7 +1259,7 @@ impl Options { Some(setting) => setting, None => match self.optimize { OptLevel::No | OptLevel::Less | OptLevel::Size | OptLevel::SizeMin => true, - OptLevel::Default | OptLevel::Aggressive => false, + OptLevel::More | OptLevel::Aggressive => false, }, } } @@ -1275,7 +1308,6 @@ pub enum EntryFnType { /// and an `include!()`. sigpipe: u8, }, - Start, } #[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)] @@ -1353,8 +1385,12 @@ pub fn build_configuration(sess: &Session, mut user_cfg: Cfg) -> Cfg { user_cfg } -pub fn build_target_config(early_dcx: &EarlyDiagCtxt, opts: &Options, sysroot: &Path) -> Target { - match Target::search(&opts.target_triple, sysroot) { +pub fn build_target_config( + early_dcx: &EarlyDiagCtxt, + target: &TargetTuple, + sysroot: &Path, +) -> Target { + match Target::search(target, sysroot) { Ok((target, warnings)) => { for warning in warnings.warning_messages() { early_dcx.early_warn(warning) @@ -1542,7 +1578,7 @@ pub fn rustc_optgroups() -> Vec { stack-protector-strategies|link-args|deployment-target]", ), opt(Stable, FlagMulti, "g", "", "Equivalent to -C debuginfo=2", ""), - opt(Stable, FlagMulti, "O", "", "Equivalent to -C opt-level=2", ""), + opt(Stable, FlagMulti, "O", "", "Equivalent to -C opt-level=3", ""), opt(Stable, Opt, "o", "", "Write output to ", "FILENAME"), opt(Stable, Opt, "", "out-dir", "Write output to compiler-chosen filename in ", "DIR"), opt( @@ -1790,7 +1826,7 @@ pub fn parse_error_format( ErrorOutputType::HumanReadable(HumanReadableErrorType::Unicode, color) } Some(arg) => { - early_dcx.abort_if_error_and_set_error_format(ErrorOutputType::HumanReadable( + early_dcx.set_error_format(ErrorOutputType::HumanReadable( HumanReadableErrorType::Default, color, )); @@ -1958,7 +1994,7 @@ fn collect_print_requests( matches: &getopts::Matches, ) -> Vec { let mut prints = Vec::::new(); - if cg.target_cpu.as_ref().is_some_and(|s| s == "help") { + if cg.target_cpu.as_deref() == Some("help") { prints.push(PrintRequest { kind: PrintKind::TargetCPUs, out: OutFileName::Stdout }); cg.target_cpu = None; }; @@ -2097,12 +2133,12 @@ fn parse_opt_level( }) .max(); if max_o > max_c { - OptLevel::Default + OptLevel::Aggressive } else { match cg.opt_level.as_ref() { "0" => OptLevel::No, "1" => OptLevel::Less, - "2" => OptLevel::Default, + "2" => OptLevel::More, "3" => OptLevel::Aggressive, "s" => OptLevel::Size, "z" => OptLevel::SizeMin, @@ -2331,7 +2367,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M let error_format = parse_error_format(early_dcx, matches, color, json_color, json_rendered); - early_dcx.abort_if_error_and_set_error_format(error_format); + early_dcx.set_error_format(error_format); let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_else(|_| { early_dcx.early_fatal("`--diagnostic-width` must be an positive integer"); @@ -2341,14 +2377,16 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M let crate_types = parse_crate_types_from_list(unparsed_crate_types) .unwrap_or_else(|e| early_dcx.early_fatal(e)); - let mut unstable_opts = UnstableOptions::build(early_dcx, matches); + let mut target_modifiers = BTreeMap::::new(); + + let mut unstable_opts = UnstableOptions::build(early_dcx, matches, &mut target_modifiers); let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(early_dcx, matches); check_error_format_stability(early_dcx, &unstable_opts, error_format); let output_types = parse_output_types(early_dcx, &unstable_opts, matches); - let mut cg = CodegenOptions::build(early_dcx, matches); + let mut cg = CodegenOptions::build(early_dcx, matches, &mut target_modifiers); let (disable_local_thinlto, codegen_units) = should_override_cgus_and_disable_thinlto( early_dcx, &output_types, @@ -2619,6 +2657,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M color, logical_env, verbose, + target_modifiers, } } @@ -2741,6 +2780,7 @@ pub mod nightly_options { "the option `{}` is only accepted on the nightly compiler", opt.name ); + // The non-zero nightly_options_on_stable will force an early_fatal eventually. let _ = early_dcx.early_err(msg); } OptionStability::Stable => {} @@ -2892,6 +2932,7 @@ pub(crate) mod dep_tracking { use std::num::NonZero; use std::path::PathBuf; + use rustc_abi::Align; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stable_hasher::Hash64; use rustc_errors::LanguageIdentifier; @@ -2905,7 +2946,7 @@ pub(crate) mod dep_tracking { }; use super::{ - BranchProtection, CFGuard, CFProtection, CollapseMacroDebuginfo, CoverageOptions, + AutoDiff, BranchProtection, CFGuard, CFProtection, CollapseMacroDebuginfo, CoverageOptions, CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FmtDebug, FunctionReturn, InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail, LtoCli, MirStripDebugInfo, NextSolverConfig, OomStrategy, OptLevel, OutFileName, @@ -2953,6 +2994,7 @@ pub(crate) mod dep_tracking { } impl_dep_tracking_hash_via_hash!( + AutoDiff, bool, usize, NonZero, @@ -3012,6 +3054,7 @@ pub(crate) mod dep_tracking { InliningThreshold, FunctionReturn, WasmCAbi, + Align, ); impl DepTrackingHash for (T1, T2) diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index b68dfeffa3451..aa9ebdd9cead4 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -9,7 +9,7 @@ //! //! ## Adding a new cfg //! -//! Adding a new feature requires two new symbols one for the cfg it-self +//! Adding a new feature requires two new symbols one for the cfg itself //! and the second one for the unstable feature gate, those are defined in //! `rustc_span::symbol`. //! @@ -119,6 +119,7 @@ pub(crate) fn disallow_cfgs(sess: &Session, user_cfgs: &Cfg) { (sym::overflow_checks, None) => disallow(cfg, "-C overflow-checks"), (sym::debug_assertions, None) => disallow(cfg, "-C debug-assertions"), (sym::ub_checks, None) => disallow(cfg, "-Z ub-checks"), + (sym::contract_checks, None) => disallow(cfg, "-Z contract-checks"), (sym::sanitize, None | Some(_)) => disallow(cfg, "-Z sanitizer"), ( sym::sanitizer_cfi_generalize_pointers | sym::sanitizer_cfi_normalize_integers, @@ -143,6 +144,7 @@ pub(crate) fn disallow_cfgs(sess: &Session, user_cfgs: &Cfg) { | (sym::target_has_atomic_load_store, Some(_)) | (sym::target_thread_local, None) => disallow(cfg, "--target"), (sym::fmt_debug, None | Some(_)) => disallow(cfg, "-Z fmt-debug"), + (sym::emscripten_wasm_eh, None | Some(_)) => disallow(cfg, "-Z emscripten_wasm_eh"), _ => {} } } @@ -295,6 +297,15 @@ pub(crate) fn default_configuration(sess: &Session) -> Cfg { ins_none!(sym::ub_checks); } + // Nightly-only implementation detail for the `panic_unwind` and `unwind` crates. + if sess.is_nightly_build() && sess.opts.unstable_opts.emscripten_wasm_eh { + ins_none!(sym::emscripten_wasm_eh); + } + + if sess.contract_checks() { + ins_none!(sym::contract_checks); + } + ret } @@ -416,7 +427,7 @@ impl CheckCfg { Some(values_target_os), Some(values_target_pointer_width), Some(values_target_vendor), - ] = self.expecteds.get_many_mut(VALUES) + ] = self.expecteds.get_disjoint_mut(VALUES) else { panic!("unable to get all the check-cfg values buckets"); }; @@ -459,6 +470,7 @@ impl CheckCfg { ins!(sym::target_thread_local, no_values); ins!(sym::ub_checks, no_values); + ins!(sym::contract_checks, no_values); ins!(sym::unix, no_values); ins!(sym::windows, no_values); diff --git a/compiler/rustc_session/src/config/native_libs/tests.rs b/compiler/rustc_session/src/config/native_libs/tests.rs index 3bcab93ef4b8f..287c98fe7ebe6 100644 --- a/compiler/rustc_session/src/config/native_libs/tests.rs +++ b/compiler/rustc_session/src/config/native_libs/tests.rs @@ -14,30 +14,22 @@ fn split() { (":bar", P { kind: None, modifiers: None, name: "", new_name: Some("bar") }), ("kind=foo", P { kind: Some("kind"), modifiers: None, name: "foo", new_name: None }), (":mods=foo", P { kind: Some(""), modifiers: Some("mods"), name: "foo", new_name: None }), - (":mods=:bar", P { - kind: Some(""), - modifiers: Some("mods"), - name: "", - new_name: Some("bar"), - }), - ("kind=foo:bar", P { - kind: Some("kind"), - modifiers: None, - name: "foo", - new_name: Some("bar"), - }), - ("kind:mods=foo", P { - kind: Some("kind"), - modifiers: Some("mods"), - name: "foo", - new_name: None, - }), - ("kind:mods=foo:bar", P { - kind: Some("kind"), - modifiers: Some("mods"), - name: "foo", - new_name: Some("bar"), - }), + ( + ":mods=:bar", + P { kind: Some(""), modifiers: Some("mods"), name: "", new_name: Some("bar") }, + ), + ( + "kind=foo:bar", + P { kind: Some("kind"), modifiers: None, name: "foo", new_name: Some("bar") }, + ), + ( + "kind:mods=foo", + P { kind: Some("kind"), modifiers: Some("mods"), name: "foo", new_name: None }, + ), + ( + "kind:mods=foo:bar", + P { kind: Some("kind"), modifiers: Some("mods"), name: "foo", new_name: Some("bar") }, + ), ("::==::", P { kind: Some(""), modifiers: Some(":"), name: "=", new_name: Some(":") }), ("==::==", P { kind: Some(""), modifiers: None, name: "=", new_name: Some(":==") }), ]; diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 6c26a78148719..75c3b2c7a8592 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -161,6 +161,7 @@ pub(crate) struct UnstableVirtualFunctionElimination; #[derive(Diagnostic)] #[diag(session_unsupported_dwarf_version)] +#[help(session_unsupported_dwarf_version_help)] pub(crate) struct UnsupportedDwarfVersion { pub(crate) dwarf_version: u32, } diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index 4be013fd6fd9c..ec83761da4a92 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -145,7 +145,7 @@ fn current_dll_path() -> Result { .map_err(|e| e.to_string())?; let mut filename = vec![0; 1024]; - let n = unsafe { GetModuleFileNameW(module, &mut filename) } as usize; + let n = unsafe { GetModuleFileNameW(Some(module), &mut filename) } as usize; if n == 0 { return Err(format!("GetModuleFileNameW failed: {}", io::Error::last_os_error())); } diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index dcf86d1a4089f..112adde3740bc 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -2,8 +2,10 @@ #![allow(internal_features)] #![feature(iter_intersperse)] #![feature(let_chains)] -#![feature(map_many_mut)] #![feature(rustc_attrs)] +// To generate CodegenOptionsTargetModifiers and UnstableOptionsTargetModifiers enums +// with macro_rules, it is necessary to use recursive mechanic ("Incremental TT Munchers"). +#![recursion_limit = "256"] #![warn(unreachable_pub)] // tidy-alphabetical-end diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 3772a4a08af01..35819f896c5bd 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -4,11 +4,13 @@ use std::num::{IntErrorKind, NonZero}; use std::path::PathBuf; use std::str; +use rustc_abi::Align; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::profiling::TimePassesFormat; use rustc_data_structures::stable_hasher::Hash64; use rustc_errors::{ColorConfig, LanguageIdentifier, TerminalUrl}; use rustc_feature::UnstableFeatures; +use rustc_macros::{Decodable, Encodable}; use rustc_span::edition::Edition; use rustc_span::{RealFileName, SourceFileHashAlgorithm}; use rustc_target::spec::{ @@ -58,18 +60,194 @@ macro_rules! hash_substruct { }; } +/// Extended target modifier info. +/// For example, when external target modifier is '-Zregparm=2': +/// Target modifier enum value + user value ('2') from external crate +/// is converted into description: prefix ('Z'), name ('regparm'), tech value ('Some(2)'). +pub struct ExtendedTargetModifierInfo { + /// Flag prefix (usually, 'C' for codegen flags or 'Z' for unstable flags) + pub prefix: String, + /// Flag name + pub name: String, + /// Flag parsed technical value + pub tech_value: String, +} + +/// A recorded -Zopt_name=opt_value (or -Copt_name=opt_value) +/// which alter the ABI or effectiveness of exploit mitigations. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Encodable, Decodable)] +pub struct TargetModifier { + /// Option enum value + pub opt: OptionsTargetModifiers, + /// User-provided option value (before parsing) + pub value_name: String, +} + +impl TargetModifier { + pub fn extend(&self) -> ExtendedTargetModifierInfo { + self.opt.reparse(&self.value_name) + } +} + +fn tmod_push_impl( + opt: OptionsTargetModifiers, + tmod_vals: &BTreeMap, + tmods: &mut Vec, +) { + tmods.push(TargetModifier { opt, value_name: tmod_vals.get(&opt).cloned().unwrap_or_default() }) +} + +macro_rules! tmod_push { + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $mods:expr, $tmod_vals:expr) => { + tmod_push_impl( + OptionsTargetModifiers::$struct_name($tmod_enum_name::$opt_name), + $tmod_vals, + $mods, + ); + }; +} + +macro_rules! gather_tmods { + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + [SUBSTRUCT], [TARGET_MODIFIER]) => { + compile_error!("SUBSTRUCT can't be target modifier"); + }; + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + [UNTRACKED], [TARGET_MODIFIER]) => { + tmod_push!($struct_name, $tmod_enum_name, $opt_name, $mods, $tmod_vals) + }; + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + [TRACKED], [TARGET_MODIFIER]) => { + tmod_push!($struct_name, $tmod_enum_name, $opt_name, $mods, $tmod_vals) + }; + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + [TRACKED_NO_CRATE_HASH], [TARGET_MODIFIER]) => { + tmod_push!($struct_name, $tmod_enum_name, $opt_name, $mods, $tmod_vals) + }; + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + [SUBSTRUCT], []) => { + $opt_expr.gather_target_modifiers($mods, $tmod_vals); + }; + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + [UNTRACKED], []) => {{}}; + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + [TRACKED], []) => {{}}; + ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, + [TRACKED_NO_CRATE_HASH], []) => {{}}; +} + +macro_rules! gather_tmods_top_level { + ($_opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, [SUBSTRUCT $substruct_enum:ident]) => { + $opt_expr.gather_target_modifiers($mods, $tmod_vals); + }; + ($opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, [$non_substruct:ident TARGET_MODIFIER]) => { + compile_error!("Top level option can't be target modifier"); + }; + ($opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, [$non_substruct:ident]) => {}; +} + +/// Macro for generating OptionsTargetsModifiers top-level enum with impl. +/// Will generate something like: +/// ```rust,ignore (illustrative) +/// pub enum OptionsTargetModifiers { +/// CodegenOptions(CodegenOptionsTargetModifiers), +/// UnstableOptions(UnstableOptionsTargetModifiers), +/// } +/// impl OptionsTargetModifiers { +/// pub fn reparse(&self, user_value: &str) -> ExtendedTargetModifierInfo { +/// match self { +/// Self::CodegenOptions(v) => v.reparse(user_value), +/// Self::UnstableOptions(v) => v.reparse(user_value), +/// } +/// } +/// pub fn is_target_modifier(flag_name: &str) -> bool { +/// CodegenOptionsTargetModifiers::is_target_modifier(flag_name) || +/// UnstableOptionsTargetModifiers::is_target_modifier(flag_name) +/// } +/// } +/// ``` +macro_rules! top_level_tmod_enum { + ($( {$($optinfo:tt)*} ),* $(,)*) => { + top_level_tmod_enum! { @parse {}, (user_value){}; $($($optinfo)*|)* } + }; + // Termination + ( + @parse + {$($variant:tt($substruct_enum:tt))*}, + ($user_value:ident){$($pout:tt)*}; + ) => { + #[allow(non_camel_case_types)] + #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone, Encodable, Decodable)] + pub enum OptionsTargetModifiers { + $($variant($substruct_enum)),* + } + impl OptionsTargetModifiers { + #[allow(unused_variables)] + pub fn reparse(&self, $user_value: &str) -> ExtendedTargetModifierInfo { + #[allow(unreachable_patterns)] + match self { + $($pout)* + _ => panic!("unknown target modifier option: {:?}", *self) + } + } + pub fn is_target_modifier(flag_name: &str) -> bool { + $($substruct_enum::is_target_modifier(flag_name))||* + } + } + }; + // Adding SUBSTRUCT option group into $eout + ( + @parse {$($eout:tt)*}, ($puser_value:ident){$($pout:tt)*}; + [SUBSTRUCT $substruct_enum:ident $variant:ident] | + $($tail:tt)* + ) => { + top_level_tmod_enum! { + @parse + { + $($eout)* + $variant($substruct_enum) + }, + ($puser_value){ + $($pout)* + Self::$variant(v) => v.reparse($puser_value), + }; + $($tail)* + } + }; + // Skipping non-target-modifier and non-substruct + ( + @parse {$($eout:tt)*}, ($puser_value:ident){$($pout:tt)*}; + [$non_substruct:ident] | + $($tail:tt)* + ) => { + top_level_tmod_enum! { + @parse + { + $($eout)* + }, + ($puser_value){ + $($pout)* + }; + $($tail)* + } + }; +} + macro_rules! top_level_options { ( $( #[$top_level_attr:meta] )* pub struct Options { $( $( #[$attr:meta] )* - $opt:ident : $t:ty [$dep_tracking_marker:ident], + $opt:ident : $t:ty [$dep_tracking_marker:ident $( $tmod:ident $variant:ident )?], )* } ) => ( + top_level_tmod_enum!( {$([$dep_tracking_marker $($tmod $variant),*])|*} ); + #[derive(Clone)] $( #[$top_level_attr] )* pub struct Options { $( $( #[$attr] )* pub $opt: $t - ),* + ),*, + pub target_modifiers: BTreeMap, } impl Options { @@ -97,6 +275,17 @@ macro_rules! top_level_options { })* hasher.finish() } + + pub fn gather_target_modifiers(&self) -> Vec { + let mut mods = Vec::::new(); + $({ + gather_tmods_top_level!($opt, + &self.$opt, &mut mods, &self.target_modifiers, + [$dep_tracking_marker $($tmod),*]); + })* + mods.sort_by(|a, b| a.opt.cmp(&b.opt)); + mods + } } ); } @@ -164,9 +353,9 @@ top_level_options!( #[rustc_lint_opt_deny_field_access("should only be used via `Config::hash_untracked_state`")] untracked_state_hash: Hash64 [TRACKED_NO_CRATE_HASH], - unstable_opts: UnstableOptions [SUBSTRUCT], + unstable_opts: UnstableOptions [SUBSTRUCT UnstableOptionsTargetModifiers UnstableOptions], prints: Vec [UNTRACKED], - cg: CodegenOptions [SUBSTRUCT], + cg: CodegenOptions [SUBSTRUCT CodegenOptionsTargetModifiers CodegenOptions], externs: Externs [UNTRACKED], crate_name: Option [TRACKED], /// Indicates how the compiler should treat unstable features. @@ -225,6 +414,98 @@ top_level_options!( } ); +macro_rules! tmod_enum_opt { + ($struct_name:ident, $tmod_enum_name:ident, $opt:ident, $v:ident) => { + Some(OptionsTargetModifiers::$struct_name($tmod_enum_name::$opt)) + }; + ($struct_name:ident, $tmod_enum_name:ident, $opt:ident, ) => { + None + }; +} + +macro_rules! tmod_enum { + ($tmod_enum_name:ident, $prefix:expr, $( {$($optinfo:tt)*} ),* $(,)*) => { + tmod_enum! { $tmod_enum_name, $prefix, @parse {}, (user_value){}; $($($optinfo)*|)* } + }; + // Termination + ( + $tmod_enum_name:ident, $prefix:expr, + @parse + {$($eout:tt)*}, + ($user_value:ident){$($pout:tt)*}; + ) => { + #[allow(non_camel_case_types)] + #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone, Encodable, Decodable)] + pub enum $tmod_enum_name { + $($eout),* + } + impl $tmod_enum_name { + #[allow(unused_variables)] + pub fn reparse(&self, $user_value: &str) -> ExtendedTargetModifierInfo { + #[allow(unreachable_patterns)] + match self { + $($pout)* + _ => panic!("unknown target modifier option: {:?}", *self) + } + } + pub fn is_target_modifier(flag_name: &str) -> bool { + match flag_name.replace('-', "_").as_str() { + $(stringify!($eout) => true,)* + _ => false, + } + } + } + }; + // Adding target-modifier option into $eout + ( + $tmod_enum_name:ident, $prefix:expr, + @parse {$($eout:tt)*}, ($puser_value:ident){$($pout:tt)*}; + $opt:ident, $parse:ident, $t:ty, [TARGET_MODIFIER] | + $($tail:tt)* + ) => { + tmod_enum! { + $tmod_enum_name, $prefix, + @parse + { + $($eout)* + $opt + }, + ($puser_value){ + $($pout)* + Self::$opt => { + let mut parsed : $t = Default::default(); + parse::$parse(&mut parsed, Some($puser_value)); + ExtendedTargetModifierInfo { + prefix: $prefix.to_string(), + name: stringify!($opt).to_string().replace('_', "-"), + tech_value: format!("{:?}", parsed), + } + }, + }; + $($tail)* + } + }; + // Skipping non-target-modifier + ( + $tmod_enum_name:ident, $prefix:expr, + @parse {$($eout:tt)*}, ($puser_value:ident){$($pout:tt)*}; + $opt:ident, $parse:ident, $t:ty, [] | + $($tail:tt)* + ) => { + tmod_enum! { + $tmod_enum_name, $prefix, + @parse + { + $($eout)* + }, + ($puser_value){ + $($pout)* + }; + $($tail)* + } + }; +} + /// Defines all `CodegenOptions`/`DebuggingOptions` fields and parsers all at once. The goal of this /// macro is to define an interface that can be programmatically used by the option parser /// to initialize the struct without hardcoding field names all over the place. @@ -234,18 +515,21 @@ top_level_options!( /// generated code to parse an option into its respective field in the struct. There are a few /// hand-written parsers for parsing specific types of values in this module. macro_rules! options { - ($struct_name:ident, $stat:ident, $optmod:ident, $prefix:expr, $outputname:expr, + ($struct_name:ident, $tmod_enum_name:ident, $stat:ident, $optmod:ident, $prefix:expr, $outputname:expr, $($( #[$attr:meta] )* $opt:ident : $t:ty = ( $init:expr, $parse:ident, - [$dep_tracking_marker:ident], - $desc:expr) + [$dep_tracking_marker:ident $( $tmod:ident )?], + $desc:expr + $(, deprecated_do_nothing: $dnn:literal )?) ),* ,) => ( #[derive(Clone)] #[rustc_lint_opt_ty] pub struct $struct_name { $( $( #[$attr] )* pub $opt: $t),* } + tmod_enum!( $tmod_enum_name, $prefix, {$($opt, $parse, $t, [$($tmod),*])|*} ); + impl Default for $struct_name { fn default() -> $struct_name { $struct_name { $($opt: $init),* } @@ -256,8 +540,9 @@ macro_rules! options { pub fn build( early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches, + target_modifiers: &mut BTreeMap, ) -> $struct_name { - build_options(early_dcx, matches, $stat, $prefix, $outputname) + build_options(early_dcx, matches, target_modifiers, $stat, $prefix, $outputname) } fn dep_tracking_hash(&self, for_crate_hash: bool, error_format: ErrorOutputType) -> u64 { @@ -277,10 +562,23 @@ macro_rules! options { ); hasher.finish() } + + pub fn gather_target_modifiers( + &self, + _mods: &mut Vec, + _tmod_vals: &BTreeMap, + ) { + $({ + gather_tmods!($struct_name, $tmod_enum_name, $opt, &self.$opt, _mods, _tmod_vals, + [$dep_tracking_marker], [$($tmod),*]); + })* + } } pub const $stat: OptionDescrs<$struct_name> = - &[ $( (stringify!($opt), $optmod::$opt, desc::$parse, $desc) ),* ]; + &[ $( OptionDesc{ name: stringify!($opt), setter: $optmod::$opt, + type_desc: desc::$parse, desc: $desc, is_deprecated_and_do_nothing: false $( || $dnn )?, + tmod: tmod_enum_opt!($struct_name, $tmod_enum_name, $opt, $($tmod),*) } ),* ]; mod $optmod { $( @@ -315,12 +613,34 @@ macro_rules! redirect_field { } type OptionSetter = fn(&mut O, v: Option<&str>) -> bool; -type OptionDescrs = &'static [(&'static str, OptionSetter, &'static str, &'static str)]; +type OptionDescrs = &'static [OptionDesc]; + +pub struct OptionDesc { + name: &'static str, + setter: OptionSetter, + // description for return value/type from mod desc + type_desc: &'static str, + // description for option from options table + desc: &'static str, + is_deprecated_and_do_nothing: bool, + tmod: Option, +} + +impl OptionDesc { + pub fn name(&self) -> &'static str { + self.name + } + + pub fn desc(&self) -> &'static str { + self.desc + } +} #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable fn build_options( early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches, + target_modifiers: &mut BTreeMap, descrs: OptionDescrs, prefix: &str, outputname: &str, @@ -333,8 +653,20 @@ fn build_options( }; let option_to_lookup = key.replace('-', "_"); - match descrs.iter().find(|(name, ..)| *name == option_to_lookup) { - Some((_, setter, type_desc, _)) => { + match descrs.iter().find(|opt_desc| opt_desc.name == option_to_lookup) { + Some(OptionDesc { + name: _, + setter, + type_desc, + desc, + is_deprecated_and_do_nothing, + tmod, + }) => { + if *is_deprecated_and_do_nothing { + // deprecation works for prefixed options only + assert!(!prefix.is_empty()); + early_dcx.early_warn(format!("`-{prefix} {key}`: {desc}")); + } if !setter(&mut op, value) { match value { None => early_dcx.early_fatal( @@ -349,6 +681,11 @@ fn build_options( ), } } + if let Some(tmod) = *tmod + && let Some(value) = value + { + target_modifiers.insert(tmod, value.to_string()); + } } None => early_dcx.early_fatal(format!("unknown {outputname} option: `{key}`")), } @@ -370,6 +707,7 @@ mod desc { pub(crate) const parse_list: &str = "a space-separated list of strings"; pub(crate) const parse_list_with_polarity: &str = "a comma-separated list of strings, with elements beginning with + or -"; + pub(crate) const parse_autodiff: &str = "a comma separated list of settings: `Print`, `PrintTA`, `PrintAA`, `PrintPerf`, `PrintModBefore`, `PrintModAfterOpts`, `PrintModAfterEnzyme`, `LooseTypes`, `NoModOptAfter`, `EnableFncOpt`, `NoVecUnroll`, `Inline`"; pub(crate) const parse_comma_list: &str = "a comma-separated list of strings"; pub(crate) const parse_opt_comma_list: &str = parse_comma_list; pub(crate) const parse_number: &str = "a number"; @@ -455,6 +793,7 @@ mod desc { pub(crate) const parse_wasm_c_abi: &str = "`legacy` or `spec`"; pub(crate) const parse_mir_include_spans: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or `nll` (default: `nll`)"; + pub(crate) const parse_align: &str = "a number that is a power of 2 between 1 and 2^29"; } pub mod parse { @@ -1000,6 +1339,38 @@ pub mod parse { } } + pub(crate) fn parse_autodiff(slot: &mut Vec, v: Option<&str>) -> bool { + let Some(v) = v else { + *slot = vec![]; + return true; + }; + let mut v: Vec<&str> = v.split(",").collect(); + v.sort_unstable(); + for &val in v.iter() { + let variant = match val { + "PrintTA" => AutoDiff::PrintTA, + "PrintAA" => AutoDiff::PrintAA, + "PrintPerf" => AutoDiff::PrintPerf, + "Print" => AutoDiff::Print, + "PrintModBefore" => AutoDiff::PrintModBefore, + "PrintModAfterOpts" => AutoDiff::PrintModAfterOpts, + "PrintModAfterEnzyme" => AutoDiff::PrintModAfterEnzyme, + "LooseTypes" => AutoDiff::LooseTypes, + "NoModOptAfter" => AutoDiff::NoModOptAfter, + "EnableFncOpt" => AutoDiff::EnableFncOpt, + "NoVecUnroll" => AutoDiff::NoVecUnroll, + "Inline" => AutoDiff::Inline, + _ => { + // FIXME(ZuseZ4): print an error saying which value is not recognized + return false; + } + }; + slot.push(variant); + } + + true + } + pub(crate) fn parse_instrument_coverage( slot: &mut InstrumentCoverage, v: Option<&str>, @@ -1534,10 +1905,25 @@ pub mod parse { true } + + pub(crate) fn parse_align(slot: &mut Option, v: Option<&str>) -> bool { + let mut bytes = 0u64; + if !parse_number(&mut bytes, v) { + return false; + } + + let Ok(align) = Align::from_bytes(bytes) else { + return false; + }; + + *slot = Some(align); + + true + } } options! { - CodegenOptions, CG_OPTIONS, cgopts, "C", "codegen", + CodegenOptions, CodegenOptionsTargetModifiers, CG_OPTIONS, cgopts, "C", "codegen", // If you add a new option, please update: // - compiler/rustc_interface/src/tests.rs @@ -1546,7 +1932,8 @@ options! { // tidy-alphabetical-start #[rustc_lint_opt_deny_field_access("documented to do nothing")] ar: String = (String::new(), parse_string, [UNTRACKED], - "this option is deprecated and does nothing"), + "this option is deprecated and does nothing", + deprecated_do_nothing: true), #[rustc_lint_opt_deny_field_access("use `Session::code_model` instead of this field")] code_model: Option = (None, parse_code_model, [TRACKED], "choose the code model to use (`rustc --print code-models` for details)"), @@ -1578,9 +1965,10 @@ options! { incremental: Option = (None, parse_opt_string, [UNTRACKED], "enable incremental compilation"), #[rustc_lint_opt_deny_field_access("documented to do nothing")] - inline_threshold: Option = (None, parse_opt_number, [TRACKED], + inline_threshold: Option = (None, parse_opt_number, [UNTRACKED], "this option is deprecated and does nothing \ - (consider using `-Cllvm-args=--inline-threshold=...`)"), + (consider using `-Cllvm-args=--inline-threshold=...`)", + deprecated_do_nothing: true), #[rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field")] instrument_coverage: InstrumentCoverage = (InstrumentCoverage::No, parse_instrument_coverage, [TRACKED], "instrument the generated code to support LLVM source-based code coverage reports \ @@ -1592,7 +1980,7 @@ options! { "extra arguments to append to the linker invocation (space separated)"), #[rustc_lint_opt_deny_field_access("use `Session::link_dead_code` instead of this field")] link_dead_code: Option = (None, parse_opt_bool, [TRACKED], - "keep dead code at link time (useful for code coverage) (default: no)"), + "try to generate and link dead code (default: no)"), link_self_contained: LinkSelfContained = (LinkSelfContained::default(), parse_link_self_contained, [UNTRACKED], "control whether to link Rust provided C objects/libraries or rely \ on a C toolchain or linker installed in the system"), @@ -1616,7 +2004,8 @@ options! { "disable the use of the redzone"), #[rustc_lint_opt_deny_field_access("documented to do nothing")] no_stack_check: bool = (false, parse_no_value, [UNTRACKED], - "this option is deprecated and does nothing"), + "this option is deprecated and does nothing", + deprecated_do_nothing: true), no_vectorize_loops: bool = (false, parse_no_value, [TRACKED], "disable loop vectorization optimization passes"), no_vectorize_slp: bool = (false, parse_no_value, [TRACKED], @@ -1665,6 +2054,8 @@ options! { target_feature: String = (String::new(), parse_target_feature, [TRACKED], "target specific attributes. (`rustc --print target-features` for details). \ This feature is unsafe."), + unsafe_allow_abi_mismatch: Vec = (Vec::new(), parse_comma_list, [UNTRACKED], + "Allow incompatible target modifiers in dependency crates (comma separated list)"), // tidy-alphabetical-end // If you add a new option, please update: @@ -1673,7 +2064,7 @@ options! { } options! { - UnstableOptions, Z_OPTIONS, dbopts, "Z", "unstable", + UnstableOptions, UnstableOptionsTargetModifiers, Z_OPTIONS, dbopts, "Z", "unstable", // If you add a new option, please update: // - compiler/rustc_interface/src/tests.rs @@ -1689,6 +2080,22 @@ options! { either `loaded` or `not-loaded`."), assume_incomplete_release: bool = (false, parse_bool, [TRACKED], "make cfg(version) treat the current version as incomplete (default: no)"), + autodiff: Vec = (Vec::new(), parse_autodiff, [TRACKED], + "a list of optional autodiff flags to enable + Optional extra settings: + `=PrintTA` + `=PrintAA` + `=PrintPerf` + `=Print` + `=PrintModBefore` + `=PrintModAfterOpts` + `=PrintModAfterEnzyme` + `=LooseTypes` + `=NoModOptAfter` + `=EnableFncOpt` + `=NoVecUnroll` + `=Inline` + Multiple options can be combined with commas."), #[rustc_lint_opt_deny_field_access("use `Session::binary_dep_depinfo` instead of this field")] binary_dep_depinfo: bool = (false, parse_bool, [TRACKED], "include artifacts (sysroot, crate dependencies) used during compilation in dep-info \ @@ -1707,6 +2114,8 @@ options! { "the backend to use"), combine_cgu: bool = (false, parse_bool, [TRACKED], "combine CGUs into a single one"), + contract_checks: Option = (None, parse_opt_bool, [TRACKED], + "emit runtime checks for contract pre- and post-conditions (default: no)"), coverage_options: CoverageOptions = (CoverageOptions::default(), parse_coverage_options, [TRACKED], "control details of coverage instrumentation"), crate_attr: Vec = (Vec::new(), parse_string_push, [TRACKED], @@ -1756,6 +2165,7 @@ options! { "output statistics about monomorphization collection"), dump_mono_stats_format: DumpMonoStatsFormat = (DumpMonoStatsFormat::Markdown, parse_dump_mono_stats, [UNTRACKED], "the format to use for -Z dump-mono-stats (`markdown` (default) or `json`)"), + #[rustc_lint_opt_deny_field_access("use `Session::dwarf_version` instead of this field")] dwarf_version: Option = (None, parse_opt_number, [TRACKED], "version of DWARF debug information to emit (default: 2 or 4, depending on platform)"), dylib_lto: bool = (false, parse_bool, [UNTRACKED], @@ -1771,6 +2181,8 @@ options! { "emit a section containing stack size metadata (default: no)"), emit_thin_lto: bool = (true, parse_bool, [TRACKED], "emit the bc module with thin LTO info (default: yes)"), + emscripten_wasm_eh: bool = (false, parse_bool, [TRACKED], + "Use WebAssembly error handling for wasm32-unknown-emscripten"), enforce_type_length_limit: bool = (false, parse_bool, [TRACKED], "enforce the type length limit when monomorphizing instances in codegen"), export_executable_symbols: bool = (false, parse_bool, [TRACKED], @@ -1821,8 +2233,6 @@ options! { "verify extended properties for incr. comp. (default: no): - hashes of green query instances - hash collisions of query keys"), - inline_in_all_cgus: Option = (None, parse_opt_bool, [TRACKED], - "control whether `#[inline]` functions are in all CGUs"), inline_llvm: bool = (true, parse_bool, [TRACKED], "enable LLVM inlining (default: yes)"), inline_mir: Option = (None, parse_opt_bool, [TRACKED], @@ -1889,6 +2299,8 @@ options! { "gather metadata statistics (default: no)"), metrics_dir: Option = (None, parse_opt_pathbuf, [UNTRACKED], "the directory metrics emitted by rustc are dumped into (implicitly enables default set of metrics)"), + min_function_alignment: Option = (None, parse_align, [TRACKED], + "align all functions to at least this many bytes. Must be a power of 2"), mir_emit_retag: bool = (false, parse_bool, [TRACKED], "emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \ (default: no)"), @@ -1984,8 +2396,6 @@ options! { Note that this overwrites the effect `-Clink-dead-code` has on collection!"), print_type_sizes: bool = (false, parse_bool, [UNTRACKED], "print layout information for each type encountered (default: no)"), - print_vtable_sizes: bool = (false, parse_bool, [UNTRACKED], - "print size comparison between old and new vtable layouts (default: no)"), proc_macro_backtrace: bool = (false, parse_bool, [UNTRACKED], "show backtraces for panics during proc-macro execution (default: no)"), proc_macro_execution_strategy: ProcMacroExecutionStrategy = (ProcMacroExecutionStrategy::SameThread, @@ -2001,10 +2411,10 @@ options! { "enable queries of the dependency graph for regression testing (default: no)"), randomize_layout: bool = (false, parse_bool, [TRACKED], "randomize the layout of types (default: no)"), - reg_struct_return: bool = (false, parse_bool, [TRACKED], + reg_struct_return: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER], "On x86-32 targets, it overrides the default ABI to return small structs in registers. It is UNSOUND to link together crates that use different values for this flag!"), - regparm: Option = (None, parse_opt_number, [TRACKED], + regparm: Option = (None, parse_opt_number, [TRACKED TARGET_MODIFIER], "On x86-32 targets, setting this to N causes the compiler to pass N arguments \ in registers EAX, EDX, and ECX instead of on the stack for\ \"C\", \"cdecl\", and \"stdcall\" fn.\ diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 90361efed844b..e0405a71f65af 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -2,11 +2,12 @@ //! It also serves as an input to the parser itself. use std::str; +use std::sync::Arc; use rustc_ast::attr::AttrIdGenerator; use rustc_ast::node_id::NodeId; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; -use rustc_data_structures::sync::{AppendOnlyVec, Lock, Lrc}; +use rustc_data_structures::sync::{AppendOnlyVec, Lock}; use rustc_errors::emitter::{HumanEmitter, SilentEmitter, stderr_destination}; use rustc_errors::{ ColorConfig, Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, EmissionGuarantee, MultiSpan, @@ -214,7 +215,7 @@ pub struct ParseSess { /// should be. Useful to avoid bad tokenization when encountering emoji. We group them to /// provide a single error per unique incorrect identifier. pub bad_unicode_identifiers: Lock>>, - source_map: Lrc, + source_map: Arc, pub buffered_lints: Lock>, /// Contains the spans of block expressions that could have been incomplete based on the /// operation token that followed it, but that the parser cannot identify without further @@ -239,16 +240,16 @@ impl ParseSess { /// Used for testing. pub fn new(locale_resources: Vec<&'static str>) -> Self { let fallback_bundle = fallback_fluent_bundle(locale_resources, false); - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let emitter = Box::new( HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle) - .sm(Some(Lrc::clone(&sm))), + .sm(Some(Arc::clone(&sm))), ); let dcx = DiagCtxt::new(emitter); ParseSess::with_dcx(dcx, sm) } - pub fn with_dcx(dcx: DiagCtxt, source_map: Lrc) -> Self { + pub fn with_dcx(dcx: DiagCtxt, source_map: Arc) -> Self { Self { dcx, unstable_features: UnstableFeatures::from_environment(None), @@ -276,15 +277,11 @@ impl ParseSess { emit_fatal_diagnostic: bool, ) -> Self { let fallback_bundle = fallback_fluent_bundle(locale_resources, false); - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let emitter = Box::new(HumanEmitter::new( - stderr_destination(ColorConfig::Auto), - Lrc::clone(&fallback_bundle), - )); - let fatal_dcx = DiagCtxt::new(emitter); + let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); + let fatal_emitter = + Box::new(HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle)); let dcx = DiagCtxt::new(Box::new(SilentEmitter { - fallback_bundle, - fatal_dcx, + fatal_emitter, fatal_note: Some(fatal_note), emit_fatal_diagnostic, })) @@ -297,8 +294,8 @@ impl ParseSess { &self.source_map } - pub fn clone_source_map(&self) -> Lrc { - Lrc::clone(&self.source_map) + pub fn clone_source_map(&self) -> Arc { + Arc::clone(&self.source_map) } pub fn buffer_lint( @@ -341,8 +338,4 @@ impl ParseSess { pub fn dcx(&self) -> DiagCtxtHandle<'_> { self.dcx.handle() } - - pub fn set_dcx(&mut self, dcx: DiagCtxt) { - self.dcx = dcx; - } } diff --git a/compiler/rustc_session/src/search_paths.rs b/compiler/rustc_session/src/search_paths.rs index 78473fccd2de0..b750d870cb6f7 100644 --- a/compiler/rustc_session/src/search_paths.rs +++ b/compiler/rustc_session/src/search_paths.rs @@ -140,10 +140,10 @@ impl SearchPath { e.ok().and_then(|e| { e.file_name().to_str().map(|s| { let file_name_str: Arc = s.into(); - (Arc::clone(&file_name_str), SearchPathFile { - path: e.path().into(), - file_name_str, - }) + ( + Arc::clone(&file_name_str), + SearchPathFile { path: e.path().into(), file_name_str }, + ) }) }) }) diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 60f1154dc6d02..f795ad1ee17d7 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -9,9 +9,7 @@ use std::{env, fmt, io}; use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef}; -use rustc_data_structures::sync::{ - DynSend, DynSync, Lock, Lrc, MappedReadGuard, ReadGuard, RwLock, -}; +use rustc_data_structures::sync::{DynSend, DynSync, Lock, MappedReadGuard, ReadGuard, RwLock}; use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter; use rustc_errors::codes::*; use rustc_errors::emitter::{ @@ -138,8 +136,8 @@ pub struct Session { pub target: Target, pub host: Target, pub opts: config::Options, - pub host_tlib_path: Lrc, - pub target_tlib_path: Lrc, + pub host_tlib_path: Arc, + pub target_tlib_path: Arc, pub psess: ParseSess, pub sysroot: PathBuf, /// Input, input file path and output file path to this compilation process. @@ -154,7 +152,7 @@ pub struct Session { pub code_stats: CodeStats, /// This only ever stores a `LintStore` but we don't want a dependency on that type here. - pub lint_store: Option>, + pub lint_store: Option>, /// Cap lint level specified by a driver specifically. pub driver_lint_caps: FxHashMap, @@ -189,7 +187,7 @@ pub struct Session { /// enabled. Makes it so that "please report a bug" is hidden, as ICEs with /// internal features are wontfix, and they are usually the cause of the ICEs. /// None signifies that this is not tracked. - pub using_internal_features: Arc, + pub using_internal_features: &'static AtomicBool, /// All commandline args used to invoke the compiler, with @file args fully expanded. /// This will only be used within debug info, e.g. in the pdb file on windows @@ -709,6 +707,10 @@ impl Session { self.opts.unstable_opts.ub_checks.unwrap_or(self.opts.debug_assertions) } + pub fn contract_checks(&self) -> bool { + self.opts.unstable_opts.contract_checks.unwrap_or(false) + } + pub fn relocation_model(&self) -> RelocModel { self.opts.cg.relocation_model.unwrap_or(self.target.relocation_model) } @@ -732,6 +734,11 @@ impl Session { self.opts.cg.split_debuginfo.unwrap_or(self.target.split_debuginfo) } + /// Returns the DWARF version passed on the CLI or the default for the target. + pub fn dwarf_version(&self) -> u32 { + self.opts.unstable_opts.dwarf_version.unwrap_or(self.target.default_dwarf_version) + } + pub fn stack_protector(&self) -> StackProtector { if self.target.options.supports_stack_protector { self.opts.unstable_opts.stack_protector @@ -876,8 +883,8 @@ impl Session { #[allow(rustc::bad_opt_access)] fn default_emitter( sopts: &config::Options, - source_map: Lrc, - bundle: Option>, + source_map: Arc, + bundle: Option>, fallback_bundle: LazyFallbackBundle, ) -> Box { let macro_backtrace = sopts.unstable_opts.macro_backtrace; @@ -895,13 +902,16 @@ fn default_emitter( } t => t, }; + + let source_map = if sopts.unstable_opts.link_only { None } else { Some(source_map) }; + match sopts.error_format { config::ErrorOutputType::HumanReadable(kind, color_config) => { let short = kind.short(); if let HumanReadableErrorType::AnnotateSnippet = kind { let emitter = AnnotateSnippetEmitter::new( - Some(source_map), + source_map, bundle, fallback_bundle, short, @@ -911,7 +921,7 @@ fn default_emitter( } else { let emitter = HumanEmitter::new(stderr_destination(color_config), fallback_bundle) .fluent_bundle(bundle) - .sm(Some(source_map)) + .sm(source_map) .short_message(short) .teach(sopts.unstable_opts.teach) .diagnostic_width(sopts.diagnostic_width) @@ -955,10 +965,9 @@ fn default_emitter( #[allow(rustc::bad_opt_access)] #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable pub fn build_session( - early_dcx: EarlyDiagCtxt, sopts: config::Options, io: CompilerIO, - bundle: Option>, + bundle: Option>, registry: rustc_errors::registry::Registry, fluent_resources: Vec<&'static str>, driver_lint_caps: FxHashMap, @@ -966,7 +975,7 @@ pub fn build_session( sysroot: PathBuf, cfg_version: &'static str, ice_file: Option, - using_internal_features: Arc, + using_internal_features: &'static AtomicBool, expanded_args: Vec, ) -> Session { // FIXME: This is not general enough to make the warning lint completely override @@ -980,20 +989,12 @@ pub fn build_session( let cap_lints_allow = sopts.lint_cap.is_some_and(|cap| cap == lint::Allow); let can_emit_warnings = !(warnings_allow || cap_lints_allow); - let host_triple = TargetTuple::from_tuple(config::host_tuple()); - let (host, target_warnings) = Target::search(&host_triple, &sysroot).unwrap_or_else(|e| { - early_dcx.early_fatal(format!("Error loading host specification: {e}")) - }); - for warning in target_warnings.warning_messages() { - early_dcx.early_warn(warning) - } - let fallback_bundle = fallback_fluent_bundle( fluent_resources, sopts.unstable_opts.translate_directionality_markers, ); let source_map = rustc_span::source_map::get_source_map().unwrap(); - let emitter = default_emitter(&sopts, Lrc::clone(&source_map), bundle, fallback_bundle); + let emitter = default_emitter(&sopts, Arc::clone(&source_map), bundle, fallback_bundle); let mut dcx = DiagCtxt::new(emitter) .with_flags(sopts.unstable_opts.dcx_flags(can_emit_warnings)) @@ -1002,9 +1003,12 @@ pub fn build_session( dcx = dcx.with_ice_file(ice_file); } - // Now that the proper handler has been constructed, drop early_dcx to - // prevent accidental use. - drop(early_dcx); + let host_triple = TargetTuple::from_tuple(config::host_tuple()); + let (host, target_warnings) = Target::search(&host_triple, &sysroot) + .unwrap_or_else(|e| dcx.handle().fatal(format!("Error loading host specification: {e}"))); + for warning in target_warnings.warning_messages() { + dcx.handle().warn(warning) + } let self_profiler = if let SwitchWithOptPath::Enabled(ref d) = sopts.unstable_opts.self_profile { @@ -1033,13 +1037,13 @@ pub fn build_session( let host_triple = config::host_tuple(); let target_triple = sopts.target_triple.tuple(); - let host_tlib_path = Lrc::new(SearchPath::from_sysroot_and_triple(&sysroot, host_triple)); + let host_tlib_path = Arc::new(SearchPath::from_sysroot_and_triple(&sysroot, host_triple)); let target_tlib_path = if host_triple == target_triple { // Use the same `SearchPath` if host and target triple are identical to avoid unnecessary // rescanning of the target lib path and an unnecessary allocation. - Lrc::clone(&host_tlib_path) + Arc::clone(&host_tlib_path) } else { - Lrc::new(SearchPath::from_sysroot_and_triple(&sysroot, target_triple)) + Arc::new(SearchPath::from_sysroot_and_triple(&sysroot, target_triple)) }; let prof = SelfProfilerRef::new( @@ -1247,7 +1251,8 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } if let Some(dwarf_version) = sess.opts.unstable_opts.dwarf_version { - if dwarf_version > 5 { + // DWARF 1 is not supported by LLVM and DWARF 6 is not yet finalized. + if dwarf_version < 2 || dwarf_version > 5 { sess.dcx().emit_err(errors::UnsupportedDwarfVersion { dwarf_version }); } } @@ -1260,8 +1265,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } if sess.opts.unstable_opts.embed_source { - let dwarf_version = - sess.opts.unstable_opts.dwarf_version.unwrap_or(sess.target.default_dwarf_version); + let dwarf_version = sess.dwarf_version(); if dwarf_version < 5 { sess.dcx().emit_warn(errors::EmbedSourceInsufficientDwarfVersion { dwarf_version }); @@ -1355,12 +1359,6 @@ pub struct EarlyDiagCtxt { dcx: DiagCtxt, } -impl Default for EarlyDiagCtxt { - fn default() -> Self { - Self::new(ErrorOutputType::default()) - } -} - impl EarlyDiagCtxt { pub fn new(output: ErrorOutputType) -> Self { let emitter = mk_emitter(output); @@ -1368,10 +1366,9 @@ impl EarlyDiagCtxt { } /// Swap out the underlying dcx once we acquire the user's preference on error emission - /// format. Any errors prior to that will cause an abort and all stashed diagnostics of the - /// previous dcx will be emitted. - pub fn abort_if_error_and_set_error_format(&mut self, output: ErrorOutputType) { - self.dcx.handle().abort_if_errors(); + /// format. If `early_err` was previously called this will panic. + pub fn set_error_format(&mut self, output: ErrorOutputType) { + assert!(self.dcx.handle().has_errors().is_none()); let emitter = mk_emitter(output); self.dcx = DiagCtxt::new(emitter); @@ -1391,7 +1388,7 @@ impl EarlyDiagCtxt { #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] - #[must_use = "ErrorGuaranteed must be returned from `run_compiler` in order to exit with a non-zero status code"] + #[must_use = "raise_fatal must be called on the returned ErrorGuaranteed in order to exit with a non-zero status code"] pub fn early_err(&self, msg: impl Into) -> ErrorGuaranteed { self.dcx.handle().err(msg) } @@ -1442,7 +1439,7 @@ fn mk_emitter(output: ErrorOutputType) -> Box { config::ErrorOutputType::Json { pretty, json_rendered, color_config } => { Box::new(JsonEmitter::new( Box::new(io::BufWriter::new(io::stderr())), - Lrc::new(SourceMap::new(FilePathMapping::empty())), + Some(Arc::new(SourceMap::new(FilePathMapping::empty()))), fallback_bundle, pretty, json_rendered, diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index c465367b6b9e0..50cf605ba2a82 100644 --- a/compiler/rustc_smir/src/rustc_internal/internal.rs +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -10,7 +10,7 @@ use rustc_span::Symbol; use stable_mir::abi::Layout; use stable_mir::mir::alloc::AllocId; use stable_mir::mir::mono::{Instance, MonoItem, StaticDef}; -use stable_mir::mir::{BinOp, Mutability, Place, ProjectionElem, Safety, UnOp}; +use stable_mir::mir::{BinOp, Mutability, Place, ProjectionElem, RawPtrKind, Safety, UnOp}; use stable_mir::ty::{ Abi, AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, DynKind, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FloatTy, FnSig, @@ -226,6 +226,18 @@ impl RustcInternal for Movability { } } +impl RustcInternal for RawPtrKind { + type T<'tcx> = rustc_middle::mir::RawPtrKind; + + fn internal<'tcx>(&self, _tables: &mut Tables<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> { + match self { + RawPtrKind::Mut => rustc_middle::mir::RawPtrKind::Mut, + RawPtrKind::Const => rustc_middle::mir::RawPtrKind::Const, + RawPtrKind::FakeForPtrMetadata => rustc_middle::mir::RawPtrKind::FakeForPtrMetadata, + } + } +} + impl RustcInternal for FnSig { type T<'tcx> = rustc_ty::FnSig<'tcx>; @@ -472,6 +484,7 @@ impl RustcInternal for Abi { Abi::PtxKernel => rustc_abi::ExternAbi::PtxKernel, Abi::Msp430Interrupt => rustc_abi::ExternAbi::Msp430Interrupt, Abi::X86Interrupt => rustc_abi::ExternAbi::X86Interrupt, + Abi::GpuKernel => rustc_abi::ExternAbi::GpuKernel, Abi::EfiApi => rustc_abi::ExternAbi::EfiApi, Abi::AvrInterrupt => rustc_abi::ExternAbi::AvrInterrupt, Abi::AvrNonBlockingInterrupt => rustc_abi::ExternAbi::AvrNonBlockingInterrupt, diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index dc63ea1999e5b..ad38ea228bf53 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -316,7 +316,7 @@ macro_rules! optional { #[doc(hidden)] macro_rules! run_driver { ($args:expr, $callback:expr $(, $with_tcx:ident)?) => {{ - use rustc_driver::{Callbacks, Compilation, RunCompiler}; + use rustc_driver::{Callbacks, Compilation, run_compiler}; use rustc_middle::ty::TyCtxt; use rustc_interface::interface; use stable_mir::CompilerError; @@ -347,7 +347,7 @@ macro_rules! run_driver { /// Runs the compiler against given target and tests it with `test_function` pub fn run(&mut self) -> Result> { let compiler_result = rustc_driver::catch_fatal_errors(|| -> interface::Result::<()> { - RunCompiler::new(&self.args.clone(), self).run(); + run_compiler(&self.args.clone(), self); Ok(()) }); match (compiler_result, self.result.take()) { diff --git a/compiler/rustc_smir/src/rustc_smir/alloc.rs b/compiler/rustc_smir/src/rustc_smir/alloc.rs index 4e8db6096d4f6..52c5b425c14f6 100644 --- a/compiler/rustc_smir/src/rustc_smir/alloc.rs +++ b/compiler/rustc_smir/src/rustc_smir/alloc.rs @@ -1,6 +1,6 @@ use rustc_abi::{Align, Size}; use rustc_middle::mir::ConstValue; -use rustc_middle::mir::interpret::{AllocRange, Pointer, alloc_range}; +use rustc_middle::mir::interpret::{AllocInit, AllocRange, Pointer, alloc_range}; use stable_mir::Error; use stable_mir::mir::Mutability; use stable_mir::ty::{Allocation, ProvenanceMap}; @@ -44,7 +44,8 @@ pub(crate) fn try_new_allocation<'tcx>( .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty)) .map_err(|e| e.stable(tables))? .align; - let mut allocation = rustc_middle::mir::interpret::Allocation::uninit(size, align.abi); + let mut allocation = + rustc_middle::mir::interpret::Allocation::new(size, align.abi, AllocInit::Uninit); allocation .write_scalar(&tables.tcx, alloc_range(Size::ZERO, size), scalar) .map_err(|e| e.stable(tables))?; @@ -68,8 +69,11 @@ pub(crate) fn try_new_allocation<'tcx>( .tcx .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty)) .map_err(|e| e.stable(tables))?; - let mut allocation = - rustc_middle::mir::interpret::Allocation::uninit(layout.size, layout.align.abi); + let mut allocation = rustc_middle::mir::interpret::Allocation::new( + layout.size, + layout.align.abi, + AllocInit::Uninit, + ); allocation .write_scalar( &tables.tcx, diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index 9793a4d416239..71e7b9c04ca16 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -450,17 +450,15 @@ impl<'tcx> Context for TablesWrapper<'tcx> { let tcx = tables.tcx; let ty = ty::Ty::new_static_str(tcx); let bytes = value.as_bytes(); - let val_tree = ty::ValTree::from_raw_bytes(tcx, bytes); - - let ct = ty::Const::new_value(tcx, val_tree, ty); - super::convert::mir_const_from_ty_const(&mut *tables, ct, ty) + let valtree = ty::ValTree::from_raw_bytes(tcx, bytes); + let cv = ty::Value { ty, valtree }; + let val = tcx.valtree_to_const_val(cv); + mir::Const::from_value(val, ty).stable(&mut tables) } fn new_const_bool(&self, value: bool) -> MirConst { let mut tables = self.0.borrow_mut(); - let ct = ty::Const::from_bool(tables.tcx, value); - let ty = tables.tcx.types.bool; - super::convert::mir_const_from_ty_const(&mut *tables, ct, ty) + mir::Const::from_bool(tables.tcx, value).stable(&mut tables) } fn try_new_const_uint(&self, value: u128, uint_ty: UintTy) -> Result { @@ -472,13 +470,11 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)) .unwrap() .size; - - // We don't use Const::from_bits since it doesn't have any error checking. let scalar = ScalarInt::try_from_uint(value, size).ok_or_else(|| { Error::new(format!("Value overflow: cannot convert `{value}` to `{ty}`.")) })?; - let ct = ty::Const::new_value(tables.tcx, ValTree::from_scalar_int(scalar), ty); - Ok(super::convert::mir_const_from_ty_const(&mut *tables, ct, ty)) + Ok(mir::Const::from_scalar(tcx, mir::interpret::Scalar::Int(scalar), ty) + .stable(&mut tables)) } fn try_new_ty_const_uint( &self, @@ -498,7 +494,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { let scalar = ScalarInt::try_from_uint(value, size).ok_or_else(|| { Error::new(format!("Value overflow: cannot convert `{value}` to `{ty}`.")) })?; - Ok(ty::Const::new_value(tables.tcx, ValTree::from_scalar_int(scalar), ty) + Ok(ty::Const::new_value(tcx, ValTree::from_scalar_int(tcx, scalar), ty) .stable(&mut *tables)) } @@ -751,7 +747,9 @@ impl<'tcx> Context for TablesWrapper<'tcx> { let tcx = tables.tcx; let alloc_id = tables.tcx.vtable_allocation(( ty.internal(&mut *tables, tcx), - trait_ref.internal(&mut *tables, tcx), + trait_ref + .internal(&mut *tables, tcx) + .map(|principal| tcx.instantiate_bound_regions_with_erased(principal)), )); Some(alloc_id.stable(&mut *tables)) } diff --git a/compiler/rustc_smir/src/rustc_smir/convert/abi.rs b/compiler/rustc_smir/src/rustc_smir/convert/abi.rs index b39a15a863326..fb2e838cdc998 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/abi.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/abi.rs @@ -105,7 +105,6 @@ impl<'tcx> Stable<'tcx> for callconv::Conv { Conv::CCmseNonSecureCall => CallConvention::CCmseNonSecureCall, Conv::CCmseNonSecureEntry => CallConvention::CCmseNonSecureEntry, Conv::Msp430Intr => CallConvention::Msp430Intr, - Conv::PtxKernel => CallConvention::PtxKernel, Conv::X86Fastcall => CallConvention::X86Fastcall, Conv::X86Intr => CallConvention::X86Intr, Conv::X86Stdcall => CallConvention::X86Stdcall, @@ -113,6 +112,7 @@ impl<'tcx> Stable<'tcx> for callconv::Conv { Conv::X86VectorCall => CallConvention::X86VectorCall, Conv::X86_64SysV => CallConvention::X86_64SysV, Conv::X86_64Win64 => CallConvention::X86_64Win64, + Conv::GpuKernel => CallConvention::GpuKernel, Conv::AvrInterrupt => CallConvention::AvrInterrupt, Conv::AvrNonBlockingInterrupt => CallConvention::AvrNonBlockingInterrupt, Conv::RiscvInterrupt { .. } => CallConvention::RiscvInterrupt, diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index de933952c6a7b..bdd6e16a7c171 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -181,6 +181,7 @@ impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> { RawPtr(mutability, place) => { stable_mir::mir::Rvalue::AddressOf(mutability.stable(tables), place.stable(tables)) } + Len(place) => stable_mir::mir::Rvalue::Len(place.stable(tables)), Cast(cast_kind, op, ty) => stable_mir::mir::Rvalue::Cast( cast_kind.stable(tables), op.stable(tables), @@ -216,6 +217,7 @@ impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> { stable_mir::mir::Rvalue::ShallowInitBox(op.stable(tables), ty.stable(tables)) } CopyForDeref(place) => stable_mir::mir::Rvalue::CopyForDeref(place.stable(tables)), + WrapUnsafeBinder(..) => todo!("FIXME(unsafe_binders):"), } } } @@ -231,6 +233,18 @@ impl<'tcx> Stable<'tcx> for mir::Mutability { } } +impl<'tcx> Stable<'tcx> for mir::RawPtrKind { + type T = stable_mir::mir::RawPtrKind; + fn stable(&self, _: &mut Tables<'_>) -> Self::T { + use mir::RawPtrKind::*; + match *self { + Const => stable_mir::mir::RawPtrKind::Const, + Mut => stable_mir::mir::RawPtrKind::Mut, + FakeForPtrMetadata => stable_mir::mir::RawPtrKind::FakeForPtrMetadata, + } + } +} + impl<'tcx> Stable<'tcx> for mir::BorrowKind { type T = stable_mir::mir::BorrowKind; fn stable(&self, tables: &mut Tables<'_>) -> Self::T { @@ -277,6 +291,7 @@ impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> { indices.iter().map(|idx| idx.stable(tables)).collect(), ), UbChecks => stable_mir::mir::NullOp::UbChecks, + ContractChecks => stable_mir::mir::NullOp::ContractChecks, } } } @@ -382,6 +397,7 @@ impl<'tcx> Stable<'tcx> for mir::PlaceElem<'tcx> { Downcast(_, idx) => stable_mir::mir::ProjectionElem::Downcast(idx.stable(tables)), OpaqueCast(ty) => stable_mir::mir::ProjectionElem::OpaqueCast(ty.stable(tables)), Subtype(ty) => stable_mir::mir::ProjectionElem::Subtype(ty.stable(tables)), + UnwrapUnsafeBinder(..) => todo!("FIXME(unsafe_binders):"), } } } @@ -483,6 +499,9 @@ impl<'tcx> Stable<'tcx> for mir::AssertMessage<'tcx> { found: found.stable(tables), } } + AssertKind::NullPointerDereference => { + stable_mir::mir::AssertMessage::NullPointerDereference + } } } } diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs index 4f8da08eff916..a3da563af50d4 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs @@ -9,8 +9,6 @@ mod error; mod mir; mod ty; -pub(crate) use ty::mir_const_from_ty_const; - impl<'tcx> Stable<'tcx> for rustc_hir::Safety { type T = stable_mir::mir::Safety; fn stable(&self, _: &mut Tables<'_>) -> Self::T { diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index e15dad78c693f..a0faf20c79a67 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -414,69 +414,20 @@ impl<'tcx> Stable<'tcx> for ty::Pattern<'tcx> { } } -pub(crate) fn mir_const_from_ty_const<'tcx>( - tables: &mut Tables<'tcx>, - ty_const: ty::Const<'tcx>, - ty: Ty<'tcx>, -) -> stable_mir::ty::MirConst { - let kind = match ty_const.kind() { - ty::ConstKind::Value(ty, val) => { - let val = match val { - ty::ValTree::Leaf(scalar) => ty::ValTree::Leaf(scalar), - ty::ValTree::Branch(branch) => { - ty::ValTree::Branch(tables.tcx.lift(branch).unwrap()) - } - }; - let ty = tables.tcx.lift(ty).unwrap(); - let const_val = tables.tcx.valtree_to_const_val((ty, val)); - if matches!(const_val, mir::ConstValue::ZeroSized) { - stable_mir::ty::ConstantKind::ZeroSized - } else { - stable_mir::ty::ConstantKind::Allocated(alloc::new_allocation( - ty, const_val, tables, - )) - } - } - ty::ConstKind::Param(param) => stable_mir::ty::ConstantKind::Param(param.stable(tables)), - ty::ConstKind::Error(_) => unreachable!(), - ty::ConstKind::Infer(_) => unreachable!(), - ty::ConstKind::Bound(_, _) => unimplemented!(), - ty::ConstKind::Placeholder(_) => unimplemented!(), - ty::ConstKind::Unevaluated(uv) => { - stable_mir::ty::ConstantKind::Unevaluated(stable_mir::ty::UnevaluatedConst { - def: tables.const_def(uv.def), - args: uv.args.stable(tables), - promoted: None, - }) - } - ty::ConstKind::Expr(_) => unimplemented!(), - }; - let stable_ty = tables.intern_ty(ty); - let id = tables.intern_mir_const(mir::Const::Ty(ty, ty_const)); - stable_mir::ty::MirConst::new(kind, stable_ty, id) -} - impl<'tcx> Stable<'tcx> for ty::Const<'tcx> { type T = stable_mir::ty::TyConst; fn stable(&self, tables: &mut Tables<'_>) -> Self::T { - let kind = match self.kind() { - ty::ConstKind::Value(ty, val) => { - let val = match val { - ty::ValTree::Leaf(scalar) => ty::ValTree::Leaf(scalar), - ty::ValTree::Branch(branch) => { - ty::ValTree::Branch(tables.tcx.lift(branch).unwrap()) - } - }; - - let ty = tables.tcx.lift(ty).unwrap(); - let const_val = tables.tcx.valtree_to_const_val((ty, val)); + let ct = tables.tcx.lift(*self).unwrap(); + let kind = match ct.kind() { + ty::ConstKind::Value(cv) => { + let const_val = tables.tcx.valtree_to_const_val(cv); if matches!(const_val, mir::ConstValue::ZeroSized) { - stable_mir::ty::TyConstKind::ZSTValue(ty.stable(tables)) + stable_mir::ty::TyConstKind::ZSTValue(cv.ty.stable(tables)) } else { stable_mir::ty::TyConstKind::Value( - ty.stable(tables), - alloc::new_allocation(ty, const_val, tables), + cv.ty.stable(tables), + alloc::new_allocation(cv.ty, const_val, tables), ) } } @@ -491,7 +442,7 @@ impl<'tcx> Stable<'tcx> for ty::Const<'tcx> { ty::ConstKind::Placeholder(_) => unimplemented!(), ty::ConstKind::Expr(_) => unimplemented!(), }; - let id = tables.intern_ty_const(tables.tcx.lift(*self).unwrap()); + let id = tables.intern_ty_const(ct); stable_mir::ty::TyConst::new(kind, id) } } @@ -820,12 +771,10 @@ impl<'tcx> Stable<'tcx> for ty::RegionKind<'tcx> { index: early_reg.index, name: early_reg.name.to_string(), }), - ty::ReBound(db_index, bound_reg) => { - RegionKind::ReBound(db_index.as_u32(), BoundRegion { - var: bound_reg.var.as_u32(), - kind: bound_reg.kind.stable(tables), - }) - } + ty::ReBound(db_index, bound_reg) => RegionKind::ReBound( + db_index.as_u32(), + BoundRegion { var: bound_reg.var.as_u32(), kind: bound_reg.kind.stable(tables) }, + ), ty::ReStatic => RegionKind::ReStatic, ty::RePlaceholder(place_holder) => { RegionKind::RePlaceholder(stable_mir::ty::Placeholder { @@ -911,6 +860,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::ExternAbi { ExternAbi::Win64 { unwind } => Abi::Win64 { unwind }, ExternAbi::SysV64 { unwind } => Abi::SysV64 { unwind }, ExternAbi::PtxKernel => Abi::PtxKernel, + ExternAbi::GpuKernel => Abi::GpuKernel, ExternAbi::Msp430Interrupt => Abi::Msp430Interrupt, ExternAbi::X86Interrupt => Abi::X86Interrupt, ExternAbi::EfiApi => Abi::EfiApi, diff --git a/compiler/rustc_span/src/analyze_source_file.rs b/compiler/rustc_span/src/analyze_source_file.rs index 28ce883daee9a..47cc16b623d10 100644 --- a/compiler/rustc_span/src/analyze_source_file.rs +++ b/compiler/rustc_span/src/analyze_source_file.rs @@ -29,6 +29,7 @@ pub(crate) fn analyze_source_file(src: &str) -> (Vec, Vec { fn analyze_source_file_dispatch( @@ -94,66 +95,158 @@ cfg_match! { if multibyte_mask == 0 { assert!(intra_chunk_offset == 0); - // Check if there are any control characters in the chunk. All - // control characters that we can encounter at this point have a - // byte value less than 32 or ... - let control_char_test0 = unsafe { _mm_cmplt_epi8(chunk, _mm_set1_epi8(32)) }; - let control_char_mask0 = unsafe { _mm_movemask_epi8(control_char_test0) }; - - // ... it's the ASCII 'DEL' character with a value of 127. - let control_char_test1 = unsafe { _mm_cmpeq_epi8(chunk, _mm_set1_epi8(127)) }; - let control_char_mask1 = unsafe { _mm_movemask_epi8(control_char_test1) }; - - let control_char_mask = control_char_mask0 | control_char_mask1; - - if control_char_mask != 0 { - // Check for newlines in the chunk - let newlines_test = unsafe { _mm_cmpeq_epi8(chunk, _mm_set1_epi8(b'\n' as i8)) }; - let newlines_mask = unsafe { _mm_movemask_epi8(newlines_test) }; - - if control_char_mask == newlines_mask { - // All control characters are newlines, record them - let mut newlines_mask = 0xFFFF0000 | newlines_mask as u32; - let output_offset = RelativeBytePos::from_usize(chunk_index * CHUNK_SIZE + 1); - - loop { - let index = newlines_mask.trailing_zeros(); - - if index >= CHUNK_SIZE as u32 { - // We have arrived at the end of the chunk. - break; - } - - lines.push(RelativeBytePos(index) + output_offset); - - // Clear the bit, so we can find the next one. - newlines_mask &= (!1) << index; - } - - // We are done for this chunk. All control characters were - // newlines and we took care of those. - continue; - } else { - // Some of the control characters are not newlines, - // fall through to the slow path below. - } - } else { - // No control characters, nothing to record for this chunk - continue; + // Check for newlines in the chunk + let newlines_test = unsafe { _mm_cmpeq_epi8(chunk, _mm_set1_epi8(b'\n' as i8)) }; + let mut newlines_mask = unsafe { _mm_movemask_epi8(newlines_test) }; + + let output_offset = RelativeBytePos::from_usize(chunk_index * CHUNK_SIZE + 1); + + while newlines_mask != 0 { + let index = newlines_mask.trailing_zeros(); + + lines.push(RelativeBytePos(index) + output_offset); + + // Clear the bit, so we can find the next one. + newlines_mask &= newlines_mask - 1; } + } else { + // The slow path. + // There are multibyte chars in here, fallback to generic decoding. + let scan_start = chunk_index * CHUNK_SIZE + intra_chunk_offset; + intra_chunk_offset = analyze_source_file_generic( + &src[scan_start..], + CHUNK_SIZE - intra_chunk_offset, + RelativeBytePos::from_usize(scan_start), + lines, + multi_byte_chars, + ); } + } - // The slow path. - // There are control chars in here, fallback to generic decoding. - let scan_start = chunk_index * CHUNK_SIZE + intra_chunk_offset; - intra_chunk_offset = analyze_source_file_generic( - &src[scan_start..], - CHUNK_SIZE - intra_chunk_offset, - RelativeBytePos::from_usize(scan_start), + // There might still be a tail left to analyze + let tail_start = chunk_count * CHUNK_SIZE + intra_chunk_offset; + if tail_start < src.len() { + analyze_source_file_generic( + &src[tail_start..], + src.len() - tail_start, + RelativeBytePos::from_usize(tail_start), lines, multi_byte_chars, ); } + } + } + _ => { + // The target (or compiler version) does not support SSE2 ... + fn analyze_source_file_dispatch( + src: &str, + lines: &mut Vec, + multi_byte_chars: &mut Vec, + ) { + analyze_source_file_generic( + src, + src.len(), + RelativeBytePos::from_u32(0), + lines, + multi_byte_chars, + ); + } + } +} + +#[cfg(not(bootstrap))] +cfg_match! { + any(target_arch = "x86", target_arch = "x86_64") => { + fn analyze_source_file_dispatch( + src: &str, + lines: &mut Vec, + multi_byte_chars: &mut Vec, + ) { + if is_x86_feature_detected!("sse2") { + unsafe { + analyze_source_file_sse2(src, lines, multi_byte_chars); + } + } else { + analyze_source_file_generic( + src, + src.len(), + RelativeBytePos::from_u32(0), + lines, + multi_byte_chars, + ); + } + } + + /// Checks 16 byte chunks of text at a time. If the chunk contains + /// something other than printable ASCII characters and newlines, the + /// function falls back to the generic implementation. Otherwise it uses + /// SSE2 intrinsics to quickly find all newlines. + #[target_feature(enable = "sse2")] + unsafe fn analyze_source_file_sse2( + src: &str, + lines: &mut Vec, + multi_byte_chars: &mut Vec, + ) { + #[cfg(target_arch = "x86")] + use std::arch::x86::*; + #[cfg(target_arch = "x86_64")] + use std::arch::x86_64::*; + + const CHUNK_SIZE: usize = 16; + + let src_bytes = src.as_bytes(); + + let chunk_count = src.len() / CHUNK_SIZE; + + // This variable keeps track of where we should start decoding a + // chunk. If a multi-byte character spans across chunk boundaries, + // we need to skip that part in the next chunk because we already + // handled it. + let mut intra_chunk_offset = 0; + + for chunk_index in 0..chunk_count { + let ptr = src_bytes.as_ptr() as *const __m128i; + // We don't know if the pointer is aligned to 16 bytes, so we + // use `loadu`, which supports unaligned loading. + let chunk = unsafe { _mm_loadu_si128(ptr.add(chunk_index)) }; + + // For character in the chunk, see if its byte value is < 0, which + // indicates that it's part of a UTF-8 char. + let multibyte_test = unsafe { _mm_cmplt_epi8(chunk, _mm_set1_epi8(0)) }; + // Create a bit mask from the comparison results. + let multibyte_mask = unsafe { _mm_movemask_epi8(multibyte_test) }; + + // If the bit mask is all zero, we only have ASCII chars here: + if multibyte_mask == 0 { + assert!(intra_chunk_offset == 0); + + // Check for newlines in the chunk + let newlines_test = unsafe { _mm_cmpeq_epi8(chunk, _mm_set1_epi8(b'\n' as i8)) }; + let mut newlines_mask = unsafe { _mm_movemask_epi8(newlines_test) }; + + let output_offset = RelativeBytePos::from_usize(chunk_index * CHUNK_SIZE + 1); + + while newlines_mask != 0 { + let index = newlines_mask.trailing_zeros(); + + lines.push(RelativeBytePos(index) + output_offset); + + // Clear the bit, so we can find the next one. + newlines_mask &= newlines_mask - 1; + } + } else { + // The slow path. + // There are multibyte chars in here, fallback to generic decoding. + let scan_start = chunk_index * CHUNK_SIZE + intra_chunk_offset; + intra_chunk_offset = analyze_source_file_generic( + &src[scan_start..], + CHUNK_SIZE - intra_chunk_offset, + RelativeBytePos::from_usize(scan_start), + lines, + multi_byte_chars, + ); + } + } // There might still be a tail left to analyze let tail_start = chunk_count * CHUNK_SIZE + intra_chunk_offset; @@ -185,6 +278,7 @@ cfg_match! { } } } + // `scan_len` determines the number of bytes in `src` to scan. Note that the // function can read past `scan_len` if a multi-byte character start within the // range but extends past it. The overflow is returned by the function. @@ -209,29 +303,18 @@ fn analyze_source_file_generic( // string. let mut char_len = 1; - if byte < 32 { - // This is an ASCII control character, it could be one of the cases - // that are interesting to us. - + if byte == b'\n' { let pos = RelativeBytePos::from_usize(i) + output_offset; - - if let b'\n' = byte { - lines.push(pos + RelativeBytePos(1)); - } - } else if byte >= 127 { - // The slow path: - // This is either ASCII control character "DEL" or the beginning of - // a multibyte char. Just decode to `char`. + lines.push(pos + RelativeBytePos(1)); + } else if byte >= 128 { + // This is the beginning of a multibyte char. Just decode to `char`. let c = src[i..].chars().next().unwrap(); char_len = c.len_utf8(); let pos = RelativeBytePos::from_usize(i) + output_offset; - - if char_len > 1 { - assert!((2..=4).contains(&char_len)); - let mbc = MultiByteChar { pos, bytes: char_len as u8 }; - multi_byte_chars.push(mbc); - } + assert!((2..=4).contains(&char_len)); + let mbc = MultiByteChar { pos, bytes: char_len as u8 }; + multi_byte_chars.push(mbc); } i += char_len; diff --git a/compiler/rustc_span/src/caching_source_map_view.rs b/compiler/rustc_span/src/caching_source_map_view.rs index 9f977b98b82b6..d8a4cc2f2e24f 100644 --- a/compiler/rustc_span/src/caching_source_map_view.rs +++ b/compiler/rustc_span/src/caching_source_map_view.rs @@ -1,6 +1,5 @@ use std::ops::Range; - -use rustc_data_structures::sync::Lrc; +use std::sync::Arc; use crate::source_map::SourceMap; use crate::{BytePos, Pos, RelativeBytePos, SourceFile, SpanData}; @@ -22,7 +21,7 @@ struct CacheEntry { // misses for these rare positions. A line lookup for the position via `SourceMap::lookup_line` // after a cache miss will produce the last line number, as desired. line: Range, - file: Lrc, + file: Arc, file_index: usize, } @@ -30,7 +29,7 @@ impl CacheEntry { #[inline] fn update( &mut self, - new_file_and_idx: Option<(Lrc, usize)>, + new_file_and_idx: Option<(Arc, usize)>, pos: BytePos, time_stamp: usize, ) { @@ -63,7 +62,7 @@ pub struct CachingSourceMapView<'sm> { impl<'sm> CachingSourceMapView<'sm> { pub fn new(source_map: &'sm SourceMap) -> CachingSourceMapView<'sm> { let files = source_map.files(); - let first_file = Lrc::clone(&files[0]); + let first_file = Arc::clone(&files[0]); let entry = CacheEntry { time_stamp: 0, line_number: 0, @@ -82,7 +81,7 @@ impl<'sm> CachingSourceMapView<'sm> { pub fn byte_pos_to_line_and_col( &mut self, pos: BytePos, - ) -> Option<(Lrc, usize, RelativeBytePos)> { + ) -> Option<(Arc, usize, RelativeBytePos)> { self.time_stamp += 1; // Check if the position is in one of the cached lines @@ -92,7 +91,7 @@ impl<'sm> CachingSourceMapView<'sm> { cache_entry.touch(self.time_stamp); let col = RelativeBytePos(pos.to_u32() - cache_entry.line.start.to_u32()); - return Some((Lrc::clone(&cache_entry.file), cache_entry.line_number, col)); + return Some((Arc::clone(&cache_entry.file), cache_entry.line_number, col)); } // No cache hit ... @@ -109,13 +108,13 @@ impl<'sm> CachingSourceMapView<'sm> { cache_entry.update(new_file_and_idx, pos, self.time_stamp); let col = RelativeBytePos(pos.to_u32() - cache_entry.line.start.to_u32()); - Some((Lrc::clone(&cache_entry.file), cache_entry.line_number, col)) + Some((Arc::clone(&cache_entry.file), cache_entry.line_number, col)) } pub fn span_data_to_lines_and_cols( &mut self, span_data: &SpanData, - ) -> Option<(Lrc, usize, BytePos, usize, BytePos)> { + ) -> Option<(Arc, usize, BytePos, usize, BytePos)> { self.time_stamp += 1; // Check if lo and hi are in the cached lines. @@ -133,7 +132,7 @@ impl<'sm> CachingSourceMapView<'sm> { } ( - Lrc::clone(&lo.file), + Arc::clone(&lo.file), lo.line_number, span_data.lo - lo.line.start, hi.line_number, @@ -181,7 +180,7 @@ impl<'sm> CachingSourceMapView<'sm> { lo.update(new_file_and_idx, span_data.lo, self.time_stamp); if !lo.line.contains(&span_data.hi) { - let new_file_and_idx = Some((Lrc::clone(&lo.file), lo.file_index)); + let new_file_and_idx = Some((Arc::clone(&lo.file), lo.file_index)); let next_oldest = self.oldest_cache_entry_index_avoid(oldest); let hi = &mut self.line_cache[next_oldest]; hi.update(new_file_and_idx, span_data.hi, self.time_stamp); @@ -227,7 +226,7 @@ impl<'sm> CachingSourceMapView<'sm> { assert_eq!(lo.file_index, hi.file_index); Some(( - Lrc::clone(&lo.file), + Arc::clone(&lo.file), lo.line_number, span_data.lo - lo.line.start, hi.line_number, @@ -271,13 +270,13 @@ impl<'sm> CachingSourceMapView<'sm> { oldest } - fn file_for_position(&self, pos: BytePos) -> Option<(Lrc, usize)> { + fn file_for_position(&self, pos: BytePos) -> Option<(Arc, usize)> { if !self.source_map.files().is_empty() { let file_idx = self.source_map.lookup_source_file_idx(pos); let file = &self.source_map.files()[file_idx]; if file_contains(file, pos) { - return Some((Lrc::clone(file), file_idx)); + return Some((Arc::clone(file), file_idx)); } } diff --git a/compiler/rustc_span/src/edit_distance/tests.rs b/compiler/rustc_span/src/edit_distance/tests.rs index 9540f934d7ebe..8372856c0d304 100644 --- a/compiler/rustc_span/src/edit_distance/tests.rs +++ b/compiler/rustc_span/src/edit_distance/tests.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] +#![allow(rustc::symbol_intern_string_literal)] use super::*; diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 3cdae437b7d46..2910bcdf51d94 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -29,11 +29,12 @@ use std::collections::hash_map::Entry; use std::collections::hash_set::Entry as SetEntry; use std::fmt; use std::hash::Hash; +use std::sync::Arc; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::{Hash64, HashStable, HashingControls, StableHasher}; -use rustc_data_structures::sync::{Lock, Lrc, WorkerLocal}; +use rustc_data_structures::sync::{Lock, WorkerLocal}; use rustc_data_structures::unhash::UnhashMap; use rustc_index::IndexVec; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; @@ -904,7 +905,7 @@ impl Span { /// allowed inside this span. pub fn mark_with_reason( self, - allow_internal_unstable: Option>, + allow_internal_unstable: Option>, reason: DesugaringKind, edition: Edition, ctx: impl HashStableContext, @@ -959,7 +960,7 @@ pub struct ExpnData { /// List of `#[unstable]`/feature-gated features that the macro is allowed to use /// internally without forcing the whole crate to opt-in /// to them. - pub allow_internal_unstable: Option>, + pub allow_internal_unstable: Option>, /// Edition of the crate in which the macro is defined. pub edition: Edition, /// The `DefId` of the macro being invoked, @@ -985,7 +986,7 @@ impl ExpnData { parent: ExpnId, call_site: Span, def_site: Span, - allow_internal_unstable: Option>, + allow_internal_unstable: Option>, edition: Edition, macro_def_id: Option, parent_module: Option, @@ -1037,7 +1038,7 @@ impl ExpnData { kind: ExpnKind, call_site: Span, edition: Edition, - allow_internal_unstable: Lrc<[Symbol]>, + allow_internal_unstable: Arc<[Symbol]>, macro_def_id: Option, parent_module: Option, ) -> ExpnData { @@ -1163,9 +1164,8 @@ pub enum DesugaringKind { WhileLoop, /// `async Fn()` bound modifier BoundModifier, - /// Marks a `&raw const *_1` needed as part of getting the length of a mutable - /// slice for the bounds check, so that MIRI's retag handling can recognize it. - IndexBoundsCheckReborrow, + /// Calls to contract checks (`#[requires]` to precond, `#[ensures]` to postcond) + Contract, } impl DesugaringKind { @@ -1182,7 +1182,7 @@ impl DesugaringKind { DesugaringKind::ForLoop => "`for` loop", DesugaringKind::WhileLoop => "`while` loop", DesugaringKind::BoundModifier => "trait bound modifier", - DesugaringKind::IndexBoundsCheckReborrow => "slice indexing", + DesugaringKind::Contract => "contract check", } } } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index d5c2a337b4c19..0e146baef3751 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -25,6 +25,7 @@ #![feature(hash_set_entry)] #![feature(if_let_guard)] #![feature(let_chains)] +#![feature(map_try_insert)] #![feature(negative_impls)] #![feature(read_buf)] #![feature(round_char_boundary)] @@ -67,7 +68,7 @@ mod span_encoding; pub use span_encoding::{DUMMY_SP, Span}; pub mod symbol; -pub use symbol::{Ident, MacroRulesNormalizedIdent, Symbol, kw, sym}; +pub use symbol::{Ident, MacroRulesNormalizedIdent, STDLIB_STABLE_CRATES, Symbol, kw, sym}; mod analyze_source_file; pub mod fatal_error; @@ -82,12 +83,13 @@ use std::io::{self, Read}; use std::ops::{Add, Range, Sub}; use std::path::{Path, PathBuf}; use std::str::FromStr; +use std::sync::Arc; use std::{fmt, iter}; use md5::{Digest, Md5}; -use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{Hash64, Hash128, HashStable, StableHasher}; -use rustc_data_structures::sync::{FreezeLock, FreezeWriteGuard, Lock, Lrc}; +use rustc_data_structures::sync::{FreezeLock, FreezeWriteGuard, Lock}; +use rustc_data_structures::unord::UnordMap; use sha1::Sha1; use sha2::Sha256; @@ -103,13 +105,13 @@ pub struct SessionGlobals { span_interner: Lock, /// Maps a macro argument token into use of the corresponding metavariable in the macro body. /// Collisions are possible and processed in `maybe_use_metavar_location` on best effort basis. - metavar_spans: Lock>, + metavar_spans: MetavarSpansMap, hygiene_data: Lock, /// The session's source map, if there is one. This field should only be /// used in places where the `Session` is truly not available, such as /// `::fmt`. - source_map: Option>, + source_map: Option>, } impl SessionGlobals { @@ -119,7 +121,7 @@ impl SessionGlobals { span_interner: Lock::new(span_encoding::SpanInterner::default()), metavar_spans: Default::default(), hygiene_data: Lock::new(hygiene::HygieneData::new(edition)), - source_map: sm_inputs.map(|inputs| Lrc::new(SourceMap::with_inputs(inputs))), + source_map: sm_inputs.map(|inputs| Arc::new(SourceMap::with_inputs(inputs))), } } } @@ -177,9 +179,42 @@ pub fn create_default_session_globals_then(f: impl FnOnce() -> R) -> R { // deserialization. scoped_tls::scoped_thread_local!(static SESSION_GLOBALS: SessionGlobals); +#[derive(Default)] +pub struct MetavarSpansMap(FreezeLock>); + +impl MetavarSpansMap { + pub fn insert(&self, span: Span, var_span: Span) -> bool { + match self.0.write().try_insert(span, (var_span, false)) { + Ok(_) => true, + Err(entry) => entry.entry.get().0 == var_span, + } + } + + /// Read a span and record that it was read. + pub fn get(&self, span: Span) -> Option { + if let Some(mut mspans) = self.0.try_write() { + if let Some((var_span, read)) = mspans.get_mut(&span) { + *read = true; + Some(*var_span) + } else { + None + } + } else { + if let Some((span, true)) = self.0.read().get(&span) { Some(*span) } else { None } + } + } + + /// Freeze the set, and return the spans which have been read. + /// + /// After this is frozen, no spans that have not been read can be read. + pub fn freeze_and_get_read_spans(&self) -> UnordMap { + self.0.freeze().items().filter(|(_, (_, b))| *b).map(|(s1, (s2, _))| (*s1, *s2)).collect() + } +} + #[inline] -pub fn with_metavar_spans(f: impl FnOnce(&mut FxHashMap) -> R) -> R { - with_session_globals(|session_globals| f(&mut session_globals.metavar_spans.lock())) +pub fn with_metavar_spans(f: impl FnOnce(&MetavarSpansMap) -> R) -> R { + with_session_globals(|session_globals| f(&session_globals.metavar_spans)) } // FIXME: We should use this enum or something like it to get rid of the @@ -305,8 +340,8 @@ impl RealFileName { #[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash, Decodable, Encodable)] pub enum FileName { Real(RealFileName), - /// Call to `quote!`. - QuoteExpansion(Hash64), + /// Strings provided as `--cfg [cfgspec]`. + CfgSpec(Hash64), /// Command line. Anon(Hash64), /// Hack in `src/librustc_ast/parse.rs`. @@ -353,7 +388,7 @@ impl fmt::Display for FileNameDisplay<'_> { Real(ref name) => { write!(fmt, "{}", name.to_string_lossy(self.display_pref)) } - QuoteExpansion(_) => write!(fmt, ""), + CfgSpec(_) => write!(fmt, ""), MacroExpansion(_) => write!(fmt, ""), Anon(_) => write!(fmt, ""), ProcMacroSourceCode(_) => write!(fmt, ""), @@ -384,7 +419,7 @@ impl FileName { | ProcMacroSourceCode(_) | CliCrateAttr(_) | Custom(_) - | QuoteExpansion(_) + | CfgSpec(_) | DocTest(_, _) | InlineAsm(_) => false, } @@ -425,7 +460,7 @@ impl FileName { pub fn cfg_spec_source_code(src: &str) -> FileName { let mut hasher = StableHasher::new(); src.hash(&mut hasher); - FileName::QuoteExpansion(hasher.finish()) + FileName::CfgSpec(hasher.finish()) } pub fn cli_crate_attr_source_code(src: &str) -> FileName { @@ -566,11 +601,43 @@ impl Span { !self.is_dummy() && sm.is_span_accessible(self) } + /// Returns whether `span` originates in a foreign crate's external macro. + /// + /// This is used to test whether a lint should not even begin to figure out whether it should + /// be reported on the current node. + pub fn in_external_macro(self, sm: &SourceMap) -> bool { + let expn_data = self.ctxt().outer_expn_data(); + match expn_data.kind { + ExpnKind::Root + | ExpnKind::Desugaring( + DesugaringKind::ForLoop + | DesugaringKind::WhileLoop + | DesugaringKind::OpaqueTy + | DesugaringKind::Async + | DesugaringKind::Await, + ) => false, + ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => true, // well, it's "external" + ExpnKind::Macro(MacroKind::Bang, _) => { + // Dummy span for the `def_site` means it's an external macro. + expn_data.def_site.is_dummy() || sm.is_imported(expn_data.def_site) + } + ExpnKind::Macro { .. } => true, // definitely a plugin + } + } + /// Returns `true` if `span` originates in a derive-macro's expansion. pub fn in_derive_expansion(self) -> bool { matches!(self.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Derive, _)) } + /// Return whether `span` is generated by `async` or `await`. + pub fn is_from_async_await(self) -> bool { + matches!( + self.ctxt().outer_expn_data().kind, + ExpnKind::Desugaring(DesugaringKind::Async | DesugaringKind::Await), + ) + } + /// Gate suggestions that would not be appropriate in a context the user didn't write. pub fn can_be_used_for_suggestions(self) -> bool { !self.from_expansion() @@ -872,8 +939,7 @@ impl Span { /// Check if you can select metavar spans for the given spans to get matching contexts. fn try_metavars(a: SpanData, b: SpanData, a_orig: Span, b_orig: Span) -> (SpanData, SpanData) { - let get = |mspans: &FxHashMap<_, _>, s| mspans.get(&s).copied(); - match with_metavar_spans(|mspans| (get(mspans, a_orig), get(mspans, b_orig))) { + match with_metavar_spans(|mspans| (mspans.get(a_orig), mspans.get(b_orig))) { (None, None) => {} (Some(meta_a), None) => { let meta_a = meta_a.data(); @@ -1365,7 +1431,7 @@ pub enum ExternalSource { #[derive(PartialEq, Eq, Clone, Debug)] pub enum ExternalSourceKind { /// The external source has been loaded already. - Present(Lrc), + Present(Arc), /// No attempt has been made to load the external source. AbsentOk, /// A failed attempt has been made to load the external source. @@ -1605,7 +1671,7 @@ pub struct SourceFile { /// (e.g., ``). pub name: FileName, /// The complete source code. - pub src: Option>, + pub src: Option>, /// The source code's hash. pub src_hash: SourceFileHash, /// Used to enable cargo to use checksums to check if a crate is fresh rather @@ -1866,7 +1932,7 @@ impl SourceFile { Ok(SourceFile { name, - src: Some(Lrc::new(src)), + src: Some(Arc::new(src)), src_hash, checksum_hash, external_src: FreezeLock::frozen(ExternalSource::Unneeded), @@ -1985,7 +2051,7 @@ impl SourceFile { } = &mut *external_src { *src_kind = if let Some(src) = src { - ExternalSourceKind::Present(Lrc::new(src)) + ExternalSourceKind::Present(Arc::new(src)) } else { ExternalSourceKind::AbsentErr }; @@ -2425,7 +2491,7 @@ impl Decodable for RelativeBytePos { #[derive(Debug, Clone)] pub struct Loc { /// Information about the original source. - pub file: Lrc, + pub file: Arc, /// The (1-based) line number. pub line: usize, /// The (0-based) column offset. @@ -2437,13 +2503,13 @@ pub struct Loc { // Used to be structural records. #[derive(Debug)] pub struct SourceFileAndLine { - pub sf: Lrc, + pub sf: Arc, /// Index of line, starting from 0. pub line: usize, } #[derive(Debug)] pub struct SourceFileAndBytePos { - pub sf: Lrc, + pub sf: Arc, pub pos: BytePos, } @@ -2460,7 +2526,7 @@ pub struct LineInfo { } pub struct FileLines { - pub file: Lrc, + pub file: Arc, pub lines: Vec, } @@ -2526,7 +2592,7 @@ pub trait HashStableContext { fn span_data_to_lines_and_cols( &mut self, span: &SpanData, - ) -> Option<(Lrc, usize, BytePos, usize, BytePos)>; + ) -> Option<(Arc, usize, BytePos, usize, BytePos)>; fn hashing_controls(&self) -> HashingControls; } diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 55e106b661b28..6fdf8e46fec65 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -102,8 +102,8 @@ pub trait FileLoader { fn read_file(&self, path: &Path) -> io::Result; /// Read the contents of a potentially non-UTF-8 file into memory. - /// We don't normalize binary files, so we can start in an Lrc. - fn read_binary_file(&self, path: &Path) -> io::Result>; + /// We don't normalize binary files, so we can start in an Arc. + fn read_binary_file(&self, path: &Path) -> io::Result>; } /// A FileLoader that uses std::fs to load real files. @@ -124,12 +124,12 @@ impl FileLoader for RealFileLoader { fs::read_to_string(path) } - fn read_binary_file(&self, path: &Path) -> io::Result> { + fn read_binary_file(&self, path: &Path) -> io::Result> { let mut file = fs::File::open(path)?; let len = file.metadata()?.len(); - let mut bytes = Lrc::new_uninit_slice(len as usize); - let mut buf = BorrowedBuf::from(Lrc::get_mut(&mut bytes).unwrap()); + let mut bytes = Arc::new_uninit_slice(len as usize); + let mut buf = BorrowedBuf::from(Arc::get_mut(&mut bytes).unwrap()); match file.read_buf_exact(buf.unfilled()) { Ok(()) => {} Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => { @@ -146,9 +146,9 @@ impl FileLoader for RealFileLoader { // But we are not guaranteed to be at the end of the file, because we did not attempt to do // a read with a non-zero-sized buffer and get Ok(0). // So we do small read to a fixed-size buffer. If the read returns no bytes then we're - // already done, and we just return the Lrc we built above. + // already done, and we just return the Arc we built above. // If the read returns bytes however, we just fall back to reading into a Vec then turning - // that into an Lrc, losing our nice peak memory behavior. This fallback code path should + // that into an Arc, losing our nice peak memory behavior. This fallback code path should // be rarely exercised. let mut probe = [0u8; 32]; @@ -172,8 +172,8 @@ impl FileLoader for RealFileLoader { #[derive(Default)] struct SourceMapFiles { - source_files: monotonic::MonotonicVec>, - stable_id_to_source_file: UnhashMap>, + source_files: monotonic::MonotonicVec>, + stable_id_to_source_file: UnhashMap>, } /// Used to construct a `SourceMap` with `SourceMap::with_inputs`. @@ -232,7 +232,7 @@ impl SourceMap { self.file_loader.file_exists(path) } - pub fn load_file(&self, path: &Path) -> io::Result> { + pub fn load_file(&self, path: &Path) -> io::Result> { let src = self.file_loader.read_file(path)?; let filename = path.to_owned().into(); Ok(self.new_source_file(filename, src)) @@ -242,7 +242,7 @@ impl SourceMap { /// /// Unlike `load_file`, guarantees that no normalization like BOM-removal /// takes place. - pub fn load_binary_file(&self, path: &Path) -> io::Result<(Lrc<[u8]>, Span)> { + pub fn load_binary_file(&self, path: &Path) -> io::Result<(Arc<[u8]>, Span)> { let bytes = self.file_loader.read_binary_file(path)?; // We need to add file to the `SourceMap`, so that it is present @@ -265,14 +265,14 @@ impl SourceMap { // By returning a `MonotonicVec`, we ensure that consumers cannot invalidate // any existing indices pointing into `files`. - pub fn files(&self) -> MappedReadGuard<'_, monotonic::MonotonicVec>> { + pub fn files(&self) -> MappedReadGuard<'_, monotonic::MonotonicVec>> { ReadGuard::map(self.files.borrow(), |files| &files.source_files) } pub fn source_file_by_stable_id( &self, stable_id: StableSourceFileId, - ) -> Option> { + ) -> Option> { self.files.borrow().stable_id_to_source_file.get(&stable_id).cloned() } @@ -280,7 +280,7 @@ impl SourceMap { &self, file_id: StableSourceFileId, mut file: SourceFile, - ) -> Result, OffsetOverflowError> { + ) -> Result, OffsetOverflowError> { let mut files = self.files.borrow_mut(); file.start_pos = BytePos(if let Some(last_file) = files.source_files.last() { @@ -291,9 +291,9 @@ impl SourceMap { 0 }); - let file = Lrc::new(file); - files.source_files.push(Lrc::clone(&file)); - files.stable_id_to_source_file.insert(file_id, Lrc::clone(&file)); + let file = Arc::new(file); + files.source_files.push(Arc::clone(&file)); + files.stable_id_to_source_file.insert(file_id, Arc::clone(&file)); Ok(file) } @@ -301,7 +301,7 @@ impl SourceMap { /// Creates a new `SourceFile`. /// If a file already exists in the `SourceMap` with the same ID, that file is returned /// unmodified. - pub fn new_source_file(&self, filename: FileName, src: String) -> Lrc { + pub fn new_source_file(&self, filename: FileName, src: String) -> Arc { self.try_new_source_file(filename, src).unwrap_or_else(|OffsetOverflowError| { eprintln!( "fatal error: rustc does not support text files larger than {} bytes", @@ -315,7 +315,7 @@ impl SourceMap { &self, filename: FileName, src: String, - ) -> Result, OffsetOverflowError> { + ) -> Result, OffsetOverflowError> { // Note that filename may not be a valid path, eg it may be `` etc, // but this is okay because the directory determined by `path.pop()` will // be empty, so the working directory will be used. @@ -353,7 +353,7 @@ impl SourceMap { multibyte_chars: Vec, normalized_pos: Vec, metadata_index: u32, - ) -> Lrc { + ) -> Arc { let source_len = RelativeBytePos::from_u32(source_len); let source_file = SourceFile { @@ -393,9 +393,9 @@ impl SourceMap { } /// Return the SourceFile that contains the given `BytePos` - pub fn lookup_source_file(&self, pos: BytePos) -> Lrc { + pub fn lookup_source_file(&self, pos: BytePos) -> Arc { let idx = self.lookup_source_file_idx(pos); - Lrc::clone(&(*self.files.borrow().source_files)[idx]) + Arc::clone(&(*self.files.borrow().source_files)[idx]) } /// Looks up source information about a `BytePos`. @@ -406,7 +406,7 @@ impl SourceMap { } /// If the corresponding `SourceFile` is empty, does not return a line number. - pub fn lookup_line(&self, pos: BytePos) -> Result> { + pub fn lookup_line(&self, pos: BytePos) -> Result> { let f = self.lookup_source_file(pos); let pos = f.relative_position(pos); @@ -441,7 +441,7 @@ impl SourceMap { pub fn span_to_location_info( &self, sp: Span, - ) -> (Option>, usize, usize, usize, usize) { + ) -> (Option>, usize, usize, usize, usize) { if self.files.borrow().source_files.is_empty() || sp.is_dummy() { return (None, 0, 0, 0, 0); } @@ -477,7 +477,7 @@ impl SourceMap { if lo != hi { return true; } - let f = Lrc::clone(&(*self.files.borrow().source_files)[lo]); + let f = Arc::clone(&(*self.files.borrow().source_files)[lo]); let lo = f.relative_position(sp.lo()); let hi = f.relative_position(sp.hi()); f.lookup_line(lo) != f.lookup_line(hi) @@ -998,12 +998,12 @@ impl SourceMap { } } - pub fn get_source_file(&self, filename: &FileName) -> Option> { + pub fn get_source_file(&self, filename: &FileName) -> Option> { // Remap filename before lookup let filename = self.path_mapping().map_filename_prefix(filename).0; for sf in self.files.borrow().source_files.iter() { if filename == sf.name { - return Some(Lrc::clone(&sf)); + return Some(Arc::clone(&sf)); } } None @@ -1012,7 +1012,7 @@ impl SourceMap { /// For a global `BytePos`, computes the local offset within the containing `SourceFile`. pub fn lookup_byte_offset(&self, bpos: BytePos) -> SourceFileAndBytePos { let idx = self.lookup_source_file_idx(bpos); - let sf = Lrc::clone(&(*self.files.borrow().source_files)[idx]); + let sf = Arc::clone(&(*self.files.borrow().source_files)[idx]); let offset = bpos - sf.start_pos; SourceFileAndBytePos { sf, pos: offset } } @@ -1082,7 +1082,7 @@ impl SourceMap { } } -pub fn get_source_map() -> Option> { +pub fn get_source_map() -> Option> { with_session_globals(|session_globals| session_globals.source_map.clone()) } diff --git a/compiler/rustc_span/src/source_map/tests.rs b/compiler/rustc_span/src/source_map/tests.rs index 5b39706f3ade1..957f55e39138e 100644 --- a/compiler/rustc_span/src/source_map/tests.rs +++ b/compiler/rustc_span/src/source_map/tests.rs @@ -538,7 +538,7 @@ fn test_next_point() { #[cfg(target_os = "linux")] #[test] fn read_binary_file_handles_lying_stat() { - // read_binary_file tries to read the contents of a file into an Lrc<[u8]> while + // read_binary_file tries to read the contents of a file into an Arc<[u8]> while // never having two copies of the data in memory at once. This is an optimization // to support include_bytes! with large files. But since Rust allocators are // sensitive to alignment, our implementation can't be bootstrapped off calling diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 4ecc4201f89d5..62723e385cf64 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -118,6 +118,8 @@ symbols! { MacroRules: "macro_rules", Raw: "raw", Reuse: "reuse", + ContractEnsures: "contract_ensures", + ContractRequires: "contract_requires", Safe: "safe", Union: "union", Yeet: "yeet", @@ -191,6 +193,7 @@ symbols! { Cleanup, Clone, CoercePointee, + CoercePointeeValidated, CoerceUnsized, Command, ConstParamTy, @@ -247,6 +250,7 @@ symbols! { Into, IntoFuture, IntoIterator, + IoBufRead, IoLines, IoRead, IoSeek, @@ -294,9 +298,12 @@ symbols! { ProceduralMasqueradeDummyType, Range, RangeBounds, + RangeCopy, RangeFrom, + RangeFromCopy, RangeFull, RangeInclusive, + RangeInclusiveCopy, RangeTo, RangeToInclusive, Rc, @@ -314,8 +321,6 @@ symbols! { Right, Rust, RustaceansAreAwesome, - RustcDecodable, - RustcEncodable, RwLock, RwLockReadGuard, RwLockWriteGuard, @@ -379,6 +384,7 @@ symbols! { abi_avr_interrupt, abi_c_cmse_nonsecure_call, abi_efiapi, + abi_gpu_kernel, abi_msp430_interrupt, abi_ptx, abi_riscv_interrupt, @@ -501,7 +507,6 @@ symbols! { augmented_assignments, auto_traits, autodiff, - autodiff_fallback, automatically_derived, avx, avx512_target_feature, @@ -511,6 +516,7 @@ symbols! { bang, begin_panic, bench, + bikeshed_guaranteed_no_drop, bin, binaryheap_iter, bind_by_move_pattern_guards, @@ -567,9 +573,10 @@ symbols! { cfg_accessible, cfg_attr, cfg_attr_multi, - cfg_autodiff_fallback, cfg_boolean_literals, + cfg_contract_checks, cfg_doctest, + cfg_emscripten_wasm_eh, cfg_eval, cfg_fmt_debug, cfg_hide, @@ -615,6 +622,7 @@ symbols! { cmp_partialord_lt, cmpxchg16b_target_feature, cmse_nonsecure_entry, + coerce_pointee_validated, coerce_unsized, cold, cold_path, @@ -677,6 +685,14 @@ symbols! { const_ty_placeholder: "", constant, constructor, + contract_build_check_ensures, + contract_check_ensures, + contract_check_requires, + contract_checks, + contracts, + contracts_ensures, + contracts_internals, + contracts_requires, convert_identity, copy, copy_closures, @@ -705,7 +721,6 @@ symbols! { coverage, coverage_attribute, cr, - crate_id, crate_in_paths, crate_local, crate_name, @@ -777,6 +792,7 @@ symbols! { discriminant_kind, discriminant_type, discriminant_value, + disjoint_bitor, dispatch_from_dyn, div, div_assign, @@ -823,6 +839,7 @@ symbols! { emit_enum_variant_arg, emit_struct, emit_struct_field, + emscripten_wasm_eh, enable, encode, end, @@ -864,6 +881,7 @@ symbols! { extern_crate_self, extern_in_paths, extern_prelude, + extern_system_varargs, extern_types, external, external_doc, @@ -1011,6 +1029,7 @@ symbols! { generic_const_exprs, generic_const_items, generic_param_attrs, + generic_pattern_types, get_context, global_alloc_ty, global_allocator, @@ -1093,6 +1112,7 @@ symbols! { import, import_name_type, import_shadowing, + import_trait_associated_functions, imported_main, in_band_lifetimes, include, @@ -1149,6 +1169,7 @@ symbols! { iterator, iterator_collect_fn, kcfi, + keylocker_x86, keyword, kind, kreg, @@ -1185,6 +1206,7 @@ symbols! { link_section, linkage, linker, + linker_messages, lint_reasons, literal, load, @@ -1283,6 +1305,7 @@ symbols! { mir_drop, mir_field, mir_goto, + mir_len, mir_make_place, mir_move, mir_offset, @@ -1362,6 +1385,7 @@ symbols! { new_lower_hex, new_octal, new_pointer, + new_range, new_unchecked, new_upper_exp, new_upper_hex, @@ -1384,7 +1408,6 @@ symbols! { no_mangle, no_sanitize, no_stack_check, - no_start, no_std, nomem, non_ascii_idents, @@ -1472,6 +1495,7 @@ symbols! { panic_location, panic_misaligned_pointer_dereference, panic_nounwind, + panic_null_pointer_dereference, panic_runtime, panic_str_2015, panic_unwind, @@ -1696,7 +1720,6 @@ symbols! { rustc_as_ptr, rustc_attrs, rustc_autodiff, - rustc_box, rustc_builtin_macro, rustc_capture_analysis, rustc_clean, @@ -1730,6 +1753,7 @@ symbols! { rustc_error, rustc_evaluate_where_clauses, rustc_expected_cgu_reuse, + rustc_force_inline, rustc_has_incoherent_inherent_impls, rustc_hidden_type_of_opaques, rustc_if_this_changed, @@ -1973,6 +1997,7 @@ symbols! { sub_assign, sub_with_overflow, suggestion, + supertrait_item_shadowing, surface_async_drop_in_place, sym, sync, @@ -2149,7 +2174,6 @@ symbols! { unwrap, unwrap_binder, unwrap_or, - unwrap_unsafe_binder, use_extern_macros, use_nested_groups, used, @@ -2180,8 +2204,10 @@ symbols! { vec_macro, vec_new, vec_pop, + vec_reserve, vec_with_capacity, vecdeque_iter, + vecdeque_reserve, vector, version, vfp2, @@ -2239,6 +2265,10 @@ symbols! { } } +/// Symbols for crates that are part of the stable standard library: `std`, `core`, `alloc`, and +/// `proc_macro`. +pub const STDLIB_STABLE_CRATES: &[Symbol] = &[sym::std, sym::core, sym::alloc, sym::proc_macro]; + #[derive(Copy, Clone, Eq, HashStable_Generic, Encodable, Decodable)] pub struct Ident { pub name: Symbol, diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index 0d6d8488a23ca..8ae35572d012d 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -19,15 +19,15 @@ pub(super) fn mangle<'tcx>( let def_id = instance.def_id(); // We want to compute the "type" of this item. Unfortunately, some - // kinds of items (e.g., closures) don't have an entry in the - // item-type array. So walk back up the find the closest parent - // that DOES have an entry. + // kinds of items (e.g., synthetic static allocations from const eval) + // don't have a proper implementation for the `type_of` query. So walk + // back up the find the closest parent that DOES have a type. let mut ty_def_id = def_id; let instance_ty; loop { let key = tcx.def_key(ty_def_id); match key.disambiguated_data.data { - DefPathData::TypeNs(_) | DefPathData::ValueNs(_) => { + DefPathData::TypeNs(_) | DefPathData::ValueNs(_) | DefPathData::Closure => { instance_ty = tcx.type_of(ty_def_id).instantiate_identity(); debug!(?instance_ty); break; @@ -274,14 +274,15 @@ impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> { fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> { // only print integers match ct.kind() { - ty::ConstKind::Value(ty, ty::ValTree::Leaf(scalar)) if ty.is_integral() => { + ty::ConstKind::Value(cv) if cv.ty.is_integral() => { // The `pretty_print_const` formatting depends on -Zverbose-internals // flag, so we cannot reuse it here. - let signed = matches!(ty.kind(), ty::Int(_)); + let scalar = cv.valtree.unwrap_leaf(); + let signed = matches!(cv.ty.kind(), ty::Int(_)); write!( self, "{:#?}", - ty::ConstInt::new(scalar, signed, ty.is_ptr_sized_integral()) + ty::ConstInt::new(scalar, signed, cv.ty.is_ptr_sized_integral()) )?; } _ => self.write_str("_")?, @@ -383,14 +384,47 @@ impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> { &mut self, impl_def_id: DefId, args: &'tcx [GenericArg<'tcx>], - mut self_ty: Ty<'tcx>, - mut impl_trait_ref: Option>, ) -> Result<(), PrintError> { - let mut typing_env = ty::TypingEnv::post_analysis(self.tcx, impl_def_id); - if !args.is_empty() { - typing_env.param_env = - ty::EarlyBinder::bind(typing_env.param_env).instantiate(self.tcx, args); - } + let self_ty = self.tcx.type_of(impl_def_id); + let impl_trait_ref = self.tcx.impl_trait_ref(impl_def_id); + let generics = self.tcx.generics_of(impl_def_id); + // We have two cases to worry about here: + // 1. We're printing a nested item inside of an impl item, like an inner + // function inside of a method. Due to the way that def path printing works, + // we'll render this something like `::method::inner_fn` + // but we have no substs for this impl since it's not really inheriting + // generics from the outer item. We need to use the identity substs, and + // to normalize we need to use the correct param-env too. + // 2. We're mangling an item with identity substs. This seems to only happen + // when generating coverage, since we try to generate coverage for unused + // items too, and if something isn't monomorphized then we necessarily don't + // have anything to substitute the instance with. + // NOTE: We don't support mangling partially substituted but still polymorphic + // instances, like `impl Tr for ()` where `A` is substituted w/ `(T,)`. + let (typing_env, mut self_ty, mut impl_trait_ref) = if generics.count() > args.len() + || &args[..generics.count()] + == self + .tcx + .erase_regions(ty::GenericArgs::identity_for_item(self.tcx, impl_def_id)) + .as_slice() + { + ( + ty::TypingEnv::post_analysis(self.tcx, impl_def_id), + self_ty.instantiate_identity(), + impl_trait_ref.map(|impl_trait_ref| impl_trait_ref.instantiate_identity()), + ) + } else { + assert!( + !args.has_non_region_param(), + "should not be mangling partially substituted \ + polymorphic instance: {impl_def_id:?} {args:?}" + ); + ( + ty::TypingEnv::fully_monomorphized(), + self_ty.instantiate(self.tcx, args), + impl_trait_ref.map(|impl_trait_ref| impl_trait_ref.instantiate(self.tcx, args)), + ) + }; match &mut impl_trait_ref { Some(impl_trait_ref) => { @@ -403,7 +437,7 @@ impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> { } } - self.default_print_impl_path(impl_def_id, args, self_ty, impl_trait_ref) + self.default_print_impl_path(impl_def_id, self_ty, impl_trait_ref) } } diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 5c5ab435dbd79..4312c82815c16 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -151,7 +151,7 @@ fn symbol_name_provider<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> ty pub fn typeid_for_trait_ref<'tcx>( tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyExistentialTraitRef<'tcx>, + trait_ref: ty::ExistentialTraitRef<'tcx>, ) -> String { v0::mangle_typeid_for_trait_ref(tcx, trait_ref) } diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 0ca47eba5e81e..4fafd1ac3509a 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -14,8 +14,8 @@ use rustc_middle::bug; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::print::{Print, PrintError, Printer}; use rustc_middle::ty::{ - self, EarlyBinder, FloatTy, GenericArg, GenericArgKind, Instance, IntTy, ReifyReason, Ty, - TyCtxt, TypeVisitable, TypeVisitableExt, UintTy, + self, FloatTy, GenericArg, GenericArgKind, Instance, IntTy, ReifyReason, Ty, TyCtxt, + TypeVisitable, TypeVisitableExt, UintTy, }; use rustc_span::kw; @@ -72,7 +72,7 @@ pub(super) fn mangle<'tcx>( pub(super) fn mangle_typeid_for_trait_ref<'tcx>( tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyExistentialTraitRef<'tcx>, + trait_ref: ty::ExistentialTraitRef<'tcx>, ) -> String { // FIXME(flip1995): See comment in `mangle_typeid_for_fnabi`. let mut cx = SymbolMangler { @@ -84,7 +84,7 @@ pub(super) fn mangle_typeid_for_trait_ref<'tcx>( binders: vec![], out: String::new(), }; - cx.print_def_path(trait_ref.def_id(), &[]).unwrap(); + cx.print_def_path(trait_ref.def_id, &[]).unwrap(); std::mem::take(&mut cx.out) } @@ -227,17 +227,50 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { &mut self, impl_def_id: DefId, args: &'tcx [GenericArg<'tcx>], - mut self_ty: Ty<'tcx>, - mut impl_trait_ref: Option>, ) -> Result<(), PrintError> { let key = self.tcx.def_key(impl_def_id); let parent_def_id = DefId { index: key.parent.unwrap(), ..impl_def_id }; - let mut typing_env = ty::TypingEnv::post_analysis(self.tcx, impl_def_id); - if !args.is_empty() { - typing_env.param_env = - EarlyBinder::bind(typing_env.param_env).instantiate(self.tcx, args); - } + let self_ty = self.tcx.type_of(impl_def_id); + let impl_trait_ref = self.tcx.impl_trait_ref(impl_def_id); + let generics = self.tcx.generics_of(impl_def_id); + // We have two cases to worry about here: + // 1. We're printing a nested item inside of an impl item, like an inner + // function inside of a method. Due to the way that def path printing works, + // we'll render this something like `::method::inner_fn` + // but we have no substs for this impl since it's not really inheriting + // generics from the outer item. We need to use the identity substs, and + // to normalize we need to use the correct param-env too. + // 2. We're mangling an item with identity substs. This seems to only happen + // when generating coverage, since we try to generate coverage for unused + // items too, and if something isn't monomorphized then we necessarily don't + // have anything to substitute the instance with. + // NOTE: We don't support mangling partially substituted but still polymorphic + // instances, like `impl Tr for ()` where `A` is substituted w/ `(T,)`. + let (typing_env, mut self_ty, mut impl_trait_ref) = if generics.count() > args.len() + || &args[..generics.count()] + == self + .tcx + .erase_regions(ty::GenericArgs::identity_for_item(self.tcx, impl_def_id)) + .as_slice() + { + ( + ty::TypingEnv::post_analysis(self.tcx, impl_def_id), + self_ty.instantiate_identity(), + impl_trait_ref.map(|impl_trait_ref| impl_trait_ref.instantiate_identity()), + ) + } else { + assert!( + !args.has_non_region_param(), + "should not be mangling partially substituted \ + polymorphic instance: {impl_def_id:?} {args:?}" + ); + ( + ty::TypingEnv::fully_monomorphized(), + self_ty.instantiate(self.tcx, args), + impl_trait_ref.map(|impl_trait_ref| impl_trait_ref.instantiate(self.tcx, args)), + ) + }; match &mut impl_trait_ref { Some(impl_trait_ref) => { @@ -447,7 +480,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { ExternAbi::C { unwind: false } => cx.push("KC"), abi => { cx.push("K"); - let name = abi.name(); + let name = abi.as_str(); if name.contains('-') { cx.push_ident(&name.replace('-', "_")); } else { @@ -557,8 +590,8 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> { // We only mangle a typed value if the const can be evaluated. - let (ct_ty, valtree) = match ct.kind() { - ty::ConstKind::Value(ty, val) => (ty, val), + let cv = match ct.kind() { + ty::ConstKind::Value(cv) => cv, // Should only be encountered within the identity-substituted // impl header of an item nested within an impl item. @@ -586,13 +619,14 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { return Ok(()); } + let ty::Value { ty: ct_ty, valtree } = cv; let start = self.out.len(); match ct_ty.kind() { ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char => { ct_ty.print(self)?; - let mut bits = ct + let mut bits = cv .try_to_bits(self.tcx, ty::TypingEnv::fully_monomorphized()) .expect("expected const to be monomorphic"); @@ -615,7 +649,8 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { // HACK(jaic1): hide the `str` type behind a reference // for the following transformation from valtree to raw bytes let ref_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, ct_ty); - let slice = valtree.try_to_raw_bytes(tcx, ref_ty).unwrap_or_else(|| { + let cv = ty::Value { ty: ref_ty, valtree }; + let slice = cv.try_to_raw_bytes(tcx).unwrap_or_else(|| { bug!("expected to get raw bytes from valtree {:?} for type {:}", valtree, ct_ty) }); let s = std::str::from_utf8(slice).expect("non utf8 str from MIR interpreter"); diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index 1292f46f0c913..9f791603c723c 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -1,11 +1,11 @@ use std::fmt; use std::str::FromStr; +use rustc_abi::Size; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::Symbol; -use crate::abi::Size; use crate::spec::{RelocModel, Target}; pub struct ModifierInfo { @@ -39,12 +39,12 @@ macro_rules! def_reg_class { } } - pub fn parse(name: rustc_span::Symbol) -> Result { + pub fn parse(name: rustc_span::Symbol) -> Result { match name { $( rustc_span::sym::$class => Ok(Self::$class), )* - _ => Err("unknown register class"), + _ => Err(&[$(rustc_span::sym::$class),*]), } } } @@ -635,7 +635,7 @@ impl InlineAsmRegClass { } } - pub fn parse(arch: InlineAsmArch, name: Symbol) -> Result { + pub fn parse(arch: InlineAsmArch, name: Symbol) -> Result { Ok(match arch { InlineAsmArch::X86 | InlineAsmArch::X86_64 => { Self::X86(X86InlineAsmRegClass::parse(name)?) @@ -934,6 +934,7 @@ pub enum InlineAsmClobberAbi { LoongArch, PowerPC, S390x, + Bpf, Msp430, } @@ -1003,6 +1004,10 @@ impl InlineAsmClobberAbi { "C" | "system" => Ok(InlineAsmClobberAbi::S390x), _ => Err(&["C", "system"]), }, + InlineAsmArch::Bpf => match name { + "C" | "system" => Ok(InlineAsmClobberAbi::Bpf), + _ => Err(&["C", "system"]), + }, InlineAsmArch::Msp430 => match name { "C" | "system" => Ok(InlineAsmClobberAbi::Msp430), _ => Err(&["C", "system"]), @@ -1278,6 +1283,14 @@ impl InlineAsmClobberAbi { a8, a9, a10, a11, a12, a13, a14, a15, } }, + InlineAsmClobberAbi::Bpf => clobbered_regs! { + Bpf BpfInlineAsmReg { + // Refs: Section 1.1 "Registers and calling convention" in BPF ABI Recommended Conventions and Guidelines v1.0 + // https://www.kernel.org/doc/html/latest/bpf/standardization/abi.html#registers-and-calling-convention + + r0, r1, r2, r3, r4, r5, + } + }, InlineAsmClobberAbi::Msp430 => clobbered_regs! { Msp430 Msp430InlineAsmReg { r11, r12, r13, r14, r15, diff --git a/compiler/rustc_target/src/callconv/aarch64.rs b/compiler/rustc_target/src/callconv/aarch64.rs index 67345f0d47b71..5c23e7036b043 100644 --- a/compiler/rustc_target/src/callconv/aarch64.rs +++ b/compiler/rustc_target/src/callconv/aarch64.rs @@ -1,9 +1,8 @@ use std::iter; -use rustc_abi::{BackendRepr, Primitive}; +use rustc_abi::{BackendRepr, HasDataLayout, Primitive, TyAbiInterface}; -use crate::abi::call::{ArgAbi, FnAbi, Reg, RegKind, Uniform}; -use crate::abi::{HasDataLayout, TyAbiInterface}; +use crate::callconv::{ArgAbi, FnAbi, Reg, RegKind, Uniform}; use crate::spec::{HasTargetSpec, Target}; /// Indicates the variant of the AArch64 ABI we are compiling for. diff --git a/compiler/rustc_target/src/callconv/amdgpu.rs b/compiler/rustc_target/src/callconv/amdgpu.rs index 3007a729a8b8a..3ec6d0b3da20b 100644 --- a/compiler/rustc_target/src/callconv/amdgpu.rs +++ b/compiler/rustc_target/src/callconv/amdgpu.rs @@ -1,5 +1,6 @@ -use crate::abi::call::{ArgAbi, FnAbi}; -use crate::abi::{HasDataLayout, TyAbiInterface}; +use rustc_abi::{HasDataLayout, TyAbiInterface}; + +use crate::callconv::{ArgAbi, FnAbi}; fn classify_ret<'a, Ty, C>(_cx: &C, ret: &mut ArgAbi<'a, Ty>) where diff --git a/compiler/rustc_target/src/callconv/arm.rs b/compiler/rustc_target/src/callconv/arm.rs index bd6f781fb8120..0a5dcc6634759 100644 --- a/compiler/rustc_target/src/callconv/arm.rs +++ b/compiler/rustc_target/src/callconv/arm.rs @@ -1,5 +1,6 @@ -use crate::abi::call::{ArgAbi, Conv, FnAbi, Reg, RegKind, Uniform}; -use crate::abi::{HasDataLayout, TyAbiInterface}; +use rustc_abi::{HasDataLayout, TyAbiInterface}; + +use crate::callconv::{ArgAbi, Conv, FnAbi, Reg, RegKind, Uniform}; use crate::spec::HasTargetSpec; fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Option diff --git a/compiler/rustc_target/src/callconv/avr.rs b/compiler/rustc_target/src/callconv/avr.rs index dfc991e09543a..0d690eff68dcf 100644 --- a/compiler/rustc_target/src/callconv/avr.rs +++ b/compiler/rustc_target/src/callconv/avr.rs @@ -30,7 +30,7 @@ //! compatible with AVR-GCC - Rust and AVR-GCC only differ in the small amount //! of compiler frontend specific calling convention logic implemented here. -use crate::abi::call::{ArgAbi, FnAbi}; +use crate::callconv::{ArgAbi, FnAbi}; fn classify_ret_ty(ret: &mut ArgAbi<'_, Ty>) { if ret.layout.is_aggregate() { diff --git a/compiler/rustc_target/src/callconv/bpf.rs b/compiler/rustc_target/src/callconv/bpf.rs index f19772ac70918..f958aeb9bb654 100644 --- a/compiler/rustc_target/src/callconv/bpf.rs +++ b/compiler/rustc_target/src/callconv/bpf.rs @@ -1,5 +1,5 @@ // see https://github.com/llvm/llvm-project/blob/main/llvm/lib/Target/BPF/BPFCallingConv.td -use crate::abi::call::{ArgAbi, FnAbi}; +use crate::callconv::{ArgAbi, FnAbi}; fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { if ret.layout.is_aggregate() || ret.layout.size.bits() > 64 { diff --git a/compiler/rustc_target/src/callconv/csky.rs b/compiler/rustc_target/src/callconv/csky.rs index b1c1ae814a7ed..cf289e639eac4 100644 --- a/compiler/rustc_target/src/callconv/csky.rs +++ b/compiler/rustc_target/src/callconv/csky.rs @@ -4,7 +4,7 @@ // Reference: Clang CSKY lowering code // https://github.com/llvm/llvm-project/blob/4a074f32a6914f2a8d7215d78758c24942dddc3d/clang/lib/CodeGen/Targets/CSKY.cpp#L76-L162 -use crate::abi::call::{ArgAbi, FnAbi, Reg, Uniform}; +use crate::callconv::{ArgAbi, FnAbi, Reg, Uniform}; fn classify_ret(arg: &mut ArgAbi<'_, Ty>) { if !arg.layout.is_sized() { diff --git a/compiler/rustc_target/src/callconv/hexagon.rs b/compiler/rustc_target/src/callconv/hexagon.rs index 0a0688880c019..d4f6dd0a5b4d3 100644 --- a/compiler/rustc_target/src/callconv/hexagon.rs +++ b/compiler/rustc_target/src/callconv/hexagon.rs @@ -1,4 +1,4 @@ -use crate::abi::call::{ArgAbi, FnAbi}; +use crate::callconv::{ArgAbi, FnAbi}; fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { if ret.layout.is_aggregate() && ret.layout.size.bits() > 64 { diff --git a/compiler/rustc_target/src/callconv/loongarch.rs b/compiler/rustc_target/src/callconv/loongarch.rs index 8bf61cb133766..47566bde6b4a4 100644 --- a/compiler/rustc_target/src/callconv/loongarch.rs +++ b/compiler/rustc_target/src/callconv/loongarch.rs @@ -1,9 +1,10 @@ -use crate::abi::call::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Reg, RegKind, Uniform}; -use crate::abi::{ - self, BackendRepr, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout, +use rustc_abi::{ + BackendRepr, ExternAbi, FieldsShape, HasDataLayout, Primitive, Reg, RegKind, Size, + TyAbiInterface, TyAndLayout, Variants, }; + +use crate::callconv::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Uniform}; use crate::spec::HasTargetSpec; -use crate::spec::abi::Abi as SpecAbi; #[derive(Copy, Clone)] enum RegPassKind { @@ -42,7 +43,7 @@ where { match arg_layout.backend_repr { BackendRepr::Scalar(scalar) => match scalar.primitive() { - abi::Int(..) | abi::Pointer(_) => { + Primitive::Int(..) | Primitive::Pointer(_) => { if arg_layout.size.bits() > xlen { return Err(CannotUseFpConv); } @@ -62,7 +63,7 @@ where _ => return Err(CannotUseFpConv), } } - abi::Float(_) => { + Primitive::Float(_) => { if arg_layout.size.bits() > flen { return Err(CannotUseFpConv); } @@ -115,8 +116,8 @@ where } FieldsShape::Arbitrary { .. } => { match arg_layout.variants { - abi::Variants::Multiple { .. } => return Err(CannotUseFpConv), - abi::Variants::Single { .. } | abi::Variants::Empty => (), + Variants::Multiple { .. } => return Err(CannotUseFpConv), + Variants::Single { .. } | Variants::Empty => (), } for i in arg_layout.fields.index_by_increasing_offset() { let field = arg_layout.field(cx, i); @@ -314,7 +315,7 @@ fn classify_arg<'a, Ty, C>( fn extend_integer_width(arg: &mut ArgAbi<'_, Ty>, xlen: u64) { if let BackendRepr::Scalar(scalar) = arg.layout.backend_repr { - if let abi::Int(i, _) = scalar.primitive() { + if let Primitive::Int(i, _) = scalar.primitive() { // 32-bit integers are always sign-extended if i.size().bits() == 32 && xlen > 32 { if let PassMode::Direct(ref mut attrs) = arg.mode { @@ -363,12 +364,12 @@ where } } -pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, abi: SpecAbi) +pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, abi: ExternAbi) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout + HasTargetSpec, { - if abi == SpecAbi::RustIntrinsic { + if abi == ExternAbi::RustIntrinsic { return; } diff --git a/compiler/rustc_target/src/callconv/m68k.rs b/compiler/rustc_target/src/callconv/m68k.rs index 82fe81f8c52ae..0b637e1e27a5b 100644 --- a/compiler/rustc_target/src/callconv/m68k.rs +++ b/compiler/rustc_target/src/callconv/m68k.rs @@ -1,4 +1,4 @@ -use crate::abi::call::{ArgAbi, FnAbi}; +use crate::callconv::{ArgAbi, FnAbi}; fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { if ret.layout.is_aggregate() { diff --git a/compiler/rustc_target/src/callconv/mips.rs b/compiler/rustc_target/src/callconv/mips.rs index 37980a91c7601..6162267a0d064 100644 --- a/compiler/rustc_target/src/callconv/mips.rs +++ b/compiler/rustc_target/src/callconv/mips.rs @@ -1,5 +1,6 @@ -use crate::abi::call::{ArgAbi, FnAbi, Reg, Uniform}; -use crate::abi::{HasDataLayout, Size}; +use rustc_abi::{HasDataLayout, Size}; + +use crate::callconv::{ArgAbi, FnAbi, Reg, Uniform}; fn classify_ret(cx: &C, ret: &mut ArgAbi<'_, Ty>, offset: &mut Size) where diff --git a/compiler/rustc_target/src/callconv/mips64.rs b/compiler/rustc_target/src/callconv/mips64.rs index 5bdf4c2ad77f0..89f324bc31308 100644 --- a/compiler/rustc_target/src/callconv/mips64.rs +++ b/compiler/rustc_target/src/callconv/mips64.rs @@ -1,12 +1,15 @@ -use crate::abi::call::{ - ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, PassMode, Reg, Uniform, +use rustc_abi::{ + BackendRepr, FieldsShape, Float, HasDataLayout, Primitive, Reg, Size, TyAbiInterface, +}; + +use crate::callconv::{ + ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, PassMode, Uniform, }; -use crate::abi::{self, HasDataLayout, Size, TyAbiInterface}; fn extend_integer_width_mips(arg: &mut ArgAbi<'_, Ty>, bits: u64) { // Always sign extend u32 values on 64-bit mips - if let abi::BackendRepr::Scalar(scalar) = arg.layout.backend_repr { - if let abi::Int(i, signed) = scalar.primitive() { + if let BackendRepr::Scalar(scalar) = arg.layout.backend_repr { + if let Primitive::Int(i, signed) = scalar.primitive() { if !signed && i.size().bits() == 32 { if let PassMode::Direct(ref mut attrs) = arg.mode { attrs.ext(ArgExtension::Sext); @@ -25,9 +28,9 @@ where C: HasDataLayout, { match ret.layout.field(cx, i).backend_repr { - abi::BackendRepr::Scalar(scalar) => match scalar.primitive() { - abi::Float(abi::F32) => Some(Reg::f32()), - abi::Float(abi::F64) => Some(Reg::f64()), + BackendRepr::Scalar(scalar) => match scalar.primitive() { + Primitive::Float(Float::F32) => Some(Reg::f32()), + Primitive::Float(Float::F64) => Some(Reg::f64()), _ => None, }, _ => None, @@ -51,7 +54,7 @@ where // use of float registers to structures (not unions) containing exactly one or two // float fields. - if let abi::FieldsShape::Arbitrary { .. } = ret.layout.fields { + if let FieldsShape::Arbitrary { .. } = ret.layout.fields { if ret.layout.fields.count() == 1 { if let Some(reg) = float_reg(cx, ret, 0) { ret.cast_to(reg); @@ -90,16 +93,16 @@ where let mut prefix_index = 0; match arg.layout.fields { - abi::FieldsShape::Primitive => unreachable!(), - abi::FieldsShape::Array { .. } => { + FieldsShape::Primitive => unreachable!(), + FieldsShape::Array { .. } => { // Arrays are passed indirectly arg.make_indirect(); return; } - abi::FieldsShape::Union(_) => { + FieldsShape::Union(_) => { // Unions and are always treated as a series of 64-bit integer chunks } - abi::FieldsShape::Arbitrary { .. } => { + FieldsShape::Arbitrary { .. } => { // Structures are split up into a series of 64-bit integer chunks, but any aligned // doubles not part of another aggregate are passed as floats. let mut last_offset = Size::ZERO; @@ -109,8 +112,8 @@ where let offset = arg.layout.fields.offset(i); // We only care about aligned doubles - if let abi::BackendRepr::Scalar(scalar) = field.backend_repr { - if scalar.primitive() == abi::Float(abi::F64) { + if let BackendRepr::Scalar(scalar) = field.backend_repr { + if scalar.primitive() == Primitive::Float(Float::F64) { if offset.is_aligned(dl.f64_align.abi) { // Insert enough integers to cover [last_offset, offset) assert!(last_offset.is_aligned(dl.f64_align.abi)); diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 746e81738076f..2bde105562218 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -1,16 +1,13 @@ use std::str::FromStr; use std::{fmt, iter}; -pub use rustc_abi::{Reg, RegKind}; +use rustc_abi::{ + AddressSpace, Align, BackendRepr, ExternAbi, HasDataLayout, Primitive, Reg, RegKind, Scalar, + Size, TyAbiInterface, TyAndLayout, +}; use rustc_macros::HashStable_Generic; -use rustc_span::Symbol; -use crate::abi::{ - self, AddressSpace, Align, BackendRepr, HasDataLayout, Pointer, Size, TyAbiInterface, - TyAndLayout, -}; -use crate::spec::abi::Abi as SpecAbi; -use crate::spec::{self, HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, WasmCAbi}; +use crate::spec::{HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, WasmCAbi}; mod aarch64; mod amdgpu; @@ -350,7 +347,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> { pub fn new( cx: &impl HasDataLayout, layout: TyAndLayout<'a, Ty>, - scalar_attrs: impl Fn(&TyAndLayout<'a, Ty>, abi::Scalar, Size) -> ArgAttributes, + scalar_attrs: impl Fn(&TyAndLayout<'a, Ty>, Scalar, Size) -> ArgAttributes, ) -> Self { let mode = match layout.backend_repr { BackendRepr::Uninhabited => PassMode::Ignore, @@ -465,7 +462,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> { pub fn extend_integer_width_to(&mut self, bits: u64) { // Only integers have signedness if let BackendRepr::Scalar(scalar) = self.layout.backend_repr { - if let abi::Int(i, signed) = scalar.primitive() { + if let Primitive::Int(i, signed) = scalar.primitive() { if i.size().bits() < bits { if let PassMode::Direct(ref mut attrs) = self.mode { if signed { @@ -545,7 +542,7 @@ pub enum Conv { Msp430Intr, - PtxKernel, + GpuKernel, X86Fastcall, X86Intr, @@ -623,40 +620,27 @@ impl<'a, Ty: fmt::Display> fmt::Debug for FnAbi<'a, Ty> { } } -/// Error produced by attempting to adjust a `FnAbi`, for a "foreign" ABI. -#[derive(Copy, Clone, Debug, HashStable_Generic)] -pub enum AdjustForForeignAbiError { - /// Target architecture doesn't support "foreign" (i.e. non-Rust) ABIs. - Unsupported { arch: Symbol, abi: spec::abi::Abi }, -} - impl<'a, Ty> FnAbi<'a, Ty> { - pub fn adjust_for_foreign_abi( - &mut self, - cx: &C, - abi: spec::abi::Abi, - ) -> Result<(), AdjustForForeignAbiError> + pub fn adjust_for_foreign_abi(&mut self, cx: &C, abi: ExternAbi) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout + HasTargetSpec + HasWasmCAbiOpt + HasX86AbiOpt, { - if abi == spec::abi::Abi::X86Interrupt { + if abi == ExternAbi::X86Interrupt { if let Some(arg) = self.args.first_mut() { arg.pass_by_stack_offset(None); } - return Ok(()); + return; } let spec = cx.target_spec(); match &spec.arch[..] { "x86" => { let (flavor, regparm) = match abi { - spec::abi::Abi::Fastcall { .. } | spec::abi::Abi::Vectorcall { .. } => { + ExternAbi::Fastcall { .. } | ExternAbi::Vectorcall { .. } => { (x86::Flavor::FastcallOrVectorcall, None) } - spec::abi::Abi::C { .. } - | spec::abi::Abi::Cdecl { .. } - | spec::abi::Abi::Stdcall { .. } => { + ExternAbi::C { .. } | ExternAbi::Cdecl { .. } | ExternAbi::Stdcall { .. } => { (x86::Flavor::General, cx.x86_abi_opt().regparm) } _ => (x86::Flavor::General, None), @@ -666,8 +650,10 @@ impl<'a, Ty> FnAbi<'a, Ty> { x86::compute_abi_info(cx, self, opts); } "x86_64" => match abi { - spec::abi::Abi::SysV64 { .. } => x86_64::compute_abi_info(cx, self), - spec::abi::Abi::Win64 { .. } => x86_win64::compute_abi_info(cx, self), + ExternAbi::SysV64 { .. } => x86_64::compute_abi_info(cx, self), + ExternAbi::Win64 { .. } | ExternAbi::Vectorcall { .. } => { + x86_win64::compute_abi_info(cx, self) + } _ => { if cx.target_spec().is_like_windows { x86_win64::compute_abi_info(cx, self) @@ -701,7 +687,8 @@ impl<'a, Ty> FnAbi<'a, Ty> { "sparc" => sparc::compute_abi_info(cx, self), "sparc64" => sparc64::compute_abi_info(cx, self), "nvptx64" => { - if cx.target_spec().adjust_abi(abi, self.c_variadic) == spec::abi::Abi::PtxKernel { + let abi = cx.target_spec().adjust_abi(abi, self.c_variadic); + if abi == ExternAbi::PtxKernel || abi == ExternAbi::GpuKernel { nvptx64::compute_ptx_kernel_abi_info(cx, self) } else { nvptx64::compute_abi_info(self) @@ -719,18 +706,11 @@ impl<'a, Ty> FnAbi<'a, Ty> { } "wasm64" => wasm::compute_c_abi_info(cx, self), "bpf" => bpf::compute_abi_info(self), - arch => { - return Err(AdjustForForeignAbiError::Unsupported { - arch: Symbol::intern(arch), - abi, - }); - } + arch => panic!("no lowering implemented for {arch}"), } - - Ok(()) } - pub fn adjust_for_rust_abi(&mut self, cx: &C, abi: SpecAbi) + pub fn adjust_for_rust_abi(&mut self, cx: &C, abi: ExternAbi) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout + HasTargetSpec, @@ -755,7 +735,9 @@ impl<'a, Ty> FnAbi<'a, Ty> { continue; } - if arg_idx.is_none() && arg.layout.size > Pointer(AddressSpace::DATA).size(cx) * 2 { + if arg_idx.is_none() + && arg.layout.size > Primitive::Pointer(AddressSpace::DATA).size(cx) * 2 + { // Return values larger than 2 registers using a return area // pointer. LLVM and Cranelift disagree about how to return // values that don't fit in the registers designated for return @@ -821,7 +803,7 @@ impl<'a, Ty> FnAbi<'a, Ty> { // that's how we connect up to LLVM and it's unstable // anyway, we control all calls to it in libstd. BackendRepr::Vector { .. } - if abi != SpecAbi::RustIntrinsic && spec.simd_types_indirect => + if abi != ExternAbi::RustIntrinsic && spec.simd_types_indirect => { arg.make_indirect(); continue; @@ -836,7 +818,7 @@ impl<'a, Ty> FnAbi<'a, Ty> { assert!(is_indirect_not_on_stack); let size = arg.layout.size; - if !arg.layout.is_unsized() && size <= Pointer(AddressSpace::DATA).size(cx) { + if !arg.layout.is_unsized() && size <= Primitive::Pointer(AddressSpace::DATA).size(cx) { // We want to pass small aggregates as immediates, but using // an LLVM aggregate type for this leads to bad optimizations, // so we pick an appropriately sized integer type instead. @@ -858,7 +840,6 @@ impl FromStr for Conv { "CCmseNonSecureCall" => Ok(Conv::CCmseNonSecureCall), "CCmseNonSecureEntry" => Ok(Conv::CCmseNonSecureEntry), "Msp430Intr" => Ok(Conv::Msp430Intr), - "PtxKernel" => Ok(Conv::PtxKernel), "X86Fastcall" => Ok(Conv::X86Fastcall), "X86Intr" => Ok(Conv::X86Intr), "X86Stdcall" => Ok(Conv::X86Stdcall), @@ -866,6 +847,7 @@ impl FromStr for Conv { "X86VectorCall" => Ok(Conv::X86VectorCall), "X86_64SysV" => Ok(Conv::X86_64SysV), "X86_64Win64" => Ok(Conv::X86_64Win64), + "GpuKernel" => Ok(Conv::GpuKernel), "AvrInterrupt" => Ok(Conv::AvrInterrupt), "AvrNonBlockingInterrupt" => Ok(Conv::AvrNonBlockingInterrupt), "RiscvInterrupt(machine)" => { diff --git a/compiler/rustc_target/src/callconv/msp430.rs b/compiler/rustc_target/src/callconv/msp430.rs index 4f613aa6c1549..3b53d183ddcda 100644 --- a/compiler/rustc_target/src/callconv/msp430.rs +++ b/compiler/rustc_target/src/callconv/msp430.rs @@ -1,7 +1,7 @@ // Reference: MSP430 Embedded Application Binary Interface // https://www.ti.com/lit/an/slaa534a/slaa534a.pdf -use crate::abi::call::{ArgAbi, FnAbi}; +use crate::callconv::{ArgAbi, FnAbi}; // 3.5 Structures or Unions Passed and Returned by Reference // diff --git a/compiler/rustc_target/src/callconv/nvptx64.rs b/compiler/rustc_target/src/callconv/nvptx64.rs index 2e8b16d3a938e..a26e7dac5bafb 100644 --- a/compiler/rustc_target/src/callconv/nvptx64.rs +++ b/compiler/rustc_target/src/callconv/nvptx64.rs @@ -1,6 +1,7 @@ +use rustc_abi::{HasDataLayout, Reg, Size, TyAbiInterface}; + use super::{ArgAttribute, ArgAttributes, ArgExtension, CastTarget}; -use crate::abi::call::{ArgAbi, FnAbi, PassMode, Reg, Size, Uniform}; -use crate::abi::{HasDataLayout, TyAbiInterface}; +use crate::callconv::{ArgAbi, FnAbi, Uniform}; fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { if ret.layout.is_aggregate() && ret.layout.is_sized() { @@ -53,21 +54,37 @@ where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { - if matches!(arg.mode, PassMode::Pair(..)) && (arg.layout.is_adt() || arg.layout.is_tuple()) { - let align_bytes = arg.layout.align.abi.bytes(); + match arg.mode { + super::PassMode::Ignore | super::PassMode::Direct(_) => return, + super::PassMode::Pair(_, _) => {} + super::PassMode::Cast { .. } => unreachable!(), + super::PassMode::Indirect { .. } => {} + } - let unit = match align_bytes { - 1 => Reg::i8(), - 2 => Reg::i16(), - 4 => Reg::i32(), - 8 => Reg::i64(), - 16 => Reg::i128(), - _ => unreachable!("Align is given as power of 2 no larger than 16 bytes"), - }; - arg.cast_to(Uniform::new(unit, Size::from_bytes(2 * align_bytes))); + // FIXME only allow structs and wide pointers here + // panic!( + // "`extern \"ptx-kernel\"` doesn't allow passing types other than primitives and structs" + // ); + + let align_bytes = arg.layout.align.abi.bytes(); + + let unit = match align_bytes { + 1 => Reg::i8(), + 2 => Reg::i16(), + 4 => Reg::i32(), + 8 => Reg::i64(), + 16 => Reg::i128(), + _ => unreachable!("Align is given as power of 2 no larger than 16 bytes"), + }; + if arg.layout.size.bytes() / align_bytes == 1 { + // Make sure we pass the struct as array at the LLVM IR level and not as a single integer. + arg.cast_to(CastTarget { + prefix: [Some(unit), None, None, None, None, None, None, None], + rest: Uniform::new(unit, Size::ZERO), + attrs: ArgAttributes::new(), + }); } else { - // FIXME: find a better way to do this. See https://github.com/rust-lang/rust/issues/117271. - arg.make_direct_deprecated(); + arg.cast_to(Uniform::new(unit, arg.layout.size)); } } diff --git a/compiler/rustc_target/src/callconv/powerpc.rs b/compiler/rustc_target/src/callconv/powerpc.rs index f3b05c4817301..2f6129626b812 100644 --- a/compiler/rustc_target/src/callconv/powerpc.rs +++ b/compiler/rustc_target/src/callconv/powerpc.rs @@ -1,4 +1,4 @@ -use crate::abi::call::{ArgAbi, FnAbi}; +use crate::callconv::{ArgAbi, FnAbi}; use crate::spec::HasTargetSpec; fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { diff --git a/compiler/rustc_target/src/callconv/powerpc64.rs b/compiler/rustc_target/src/callconv/powerpc64.rs index 3a71592cbe090..89ec85e4b6664 100644 --- a/compiler/rustc_target/src/callconv/powerpc64.rs +++ b/compiler/rustc_target/src/callconv/powerpc64.rs @@ -2,8 +2,9 @@ // Alignment of 128 bit types is not currently handled, this will // need to be fixed when PowerPC vector support is added. -use crate::abi::call::{Align, ArgAbi, FnAbi, Reg, RegKind, Uniform}; -use crate::abi::{Endian, HasDataLayout, TyAbiInterface}; +use rustc_abi::{Endian, HasDataLayout, TyAbiInterface}; + +use crate::callconv::{Align, ArgAbi, FnAbi, Reg, RegKind, Uniform}; use crate::spec::HasTargetSpec; #[derive(Debug, Clone, Copy, PartialEq)] @@ -58,8 +59,10 @@ where // The AIX ABI expect byval for aggregates // See https://github.com/llvm/llvm-project/blob/main/clang/lib/CodeGen/Targets/PPC.cpp. + // The incoming parameter is represented as a pointer in the IR, + // the alignment is associated with the size of the register. (align 8 for 64bit) if !is_ret && abi == AIX { - arg.pass_by_stack_offset(None); + arg.pass_by_stack_offset(Some(Align::from_bytes(8).unwrap())); return; } diff --git a/compiler/rustc_target/src/callconv/riscv.rs b/compiler/rustc_target/src/callconv/riscv.rs index 4d858392c979a..265ae20f9919d 100644 --- a/compiler/rustc_target/src/callconv/riscv.rs +++ b/compiler/rustc_target/src/callconv/riscv.rs @@ -4,12 +4,13 @@ // Reference: Clang RISC-V ELF psABI lowering code // https://github.com/llvm/llvm-project/blob/8e780252a7284be45cf1ba224cabd884847e8e92/clang/lib/CodeGen/TargetInfo.cpp#L9311-L9773 -use rustc_abi::{BackendRepr, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout}; +use rustc_abi::{ + BackendRepr, ExternAbi, FieldsShape, HasDataLayout, Primitive, Reg, RegKind, Size, + TyAbiInterface, TyAndLayout, Variants, +}; -use crate::abi; -use crate::abi::call::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Reg, RegKind, Uniform}; +use crate::callconv::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Uniform}; use crate::spec::HasTargetSpec; -use crate::spec::abi::Abi as SpecAbi; #[derive(Copy, Clone)] enum RegPassKind { @@ -48,7 +49,7 @@ where { match arg_layout.backend_repr { BackendRepr::Scalar(scalar) => match scalar.primitive() { - abi::Int(..) | abi::Pointer(_) => { + Primitive::Int(..) | Primitive::Pointer(_) => { if arg_layout.size.bits() > xlen { return Err(CannotUseFpConv); } @@ -68,7 +69,7 @@ where _ => return Err(CannotUseFpConv), } } - abi::Float(_) => { + Primitive::Float(_) => { if arg_layout.size.bits() > flen { return Err(CannotUseFpConv); } @@ -121,8 +122,8 @@ where } FieldsShape::Arbitrary { .. } => { match arg_layout.variants { - abi::Variants::Multiple { .. } => return Err(CannotUseFpConv), - abi::Variants::Single { .. } | abi::Variants::Empty => (), + Variants::Multiple { .. } => return Err(CannotUseFpConv), + Variants::Single { .. } | Variants::Empty => (), } for i in arg_layout.fields.index_by_increasing_offset() { let field = arg_layout.field(cx, i); @@ -320,7 +321,7 @@ fn classify_arg<'a, Ty, C>( fn extend_integer_width(arg: &mut ArgAbi<'_, Ty>, xlen: u64) { if let BackendRepr::Scalar(scalar) = arg.layout.backend_repr { - if let abi::Int(i, _) = scalar.primitive() { + if let Primitive::Int(i, _) = scalar.primitive() { // 32-bit integers are always sign-extended if i.size().bits() == 32 && xlen > 32 { if let PassMode::Direct(ref mut attrs) = arg.mode { @@ -369,12 +370,12 @@ where } } -pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, abi: SpecAbi) +pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, abi: ExternAbi) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout + HasTargetSpec, { - if abi == SpecAbi::RustIntrinsic { + if abi == ExternAbi::RustIntrinsic { return; } diff --git a/compiler/rustc_target/src/callconv/s390x.rs b/compiler/rustc_target/src/callconv/s390x.rs index c99eb9226eff1..4f69e32b2e3df 100644 --- a/compiler/rustc_target/src/callconv/s390x.rs +++ b/compiler/rustc_target/src/callconv/s390x.rs @@ -1,8 +1,9 @@ // Reference: ELF Application Binary Interface s390x Supplement // https://github.com/IBM/s390x-abi -use crate::abi::call::{ArgAbi, FnAbi, Reg, RegKind}; -use crate::abi::{BackendRepr, HasDataLayout, TyAbiInterface}; +use rustc_abi::{BackendRepr, HasDataLayout, TyAbiInterface}; + +use crate::callconv::{ArgAbi, FnAbi, Reg, RegKind}; use crate::spec::HasTargetSpec; fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { @@ -38,9 +39,17 @@ where } let size = arg.layout.size; - if size.bits() <= 128 && arg.layout.is_single_vector_element(cx, size) { - arg.cast_to(Reg { kind: RegKind::Vector, size }); - return; + if size.bits() <= 128 { + if let BackendRepr::Vector { .. } = arg.layout.backend_repr { + // pass non-wrapped vector types using `PassMode::Direct` + return; + } + + if arg.layout.is_single_vector_element(cx, size) { + // pass non-transparant wrappers around a vector as `PassMode::Cast` + arg.cast_to(Reg { kind: RegKind::Vector, size }); + return; + } } if !arg.layout.is_aggregate() && size.bits() <= 64 { arg.extend_integer_width_to(64); diff --git a/compiler/rustc_target/src/callconv/sparc.rs b/compiler/rustc_target/src/callconv/sparc.rs index 37980a91c7601..6162267a0d064 100644 --- a/compiler/rustc_target/src/callconv/sparc.rs +++ b/compiler/rustc_target/src/callconv/sparc.rs @@ -1,5 +1,6 @@ -use crate::abi::call::{ArgAbi, FnAbi, Reg, Uniform}; -use crate::abi::{HasDataLayout, Size}; +use rustc_abi::{HasDataLayout, Size}; + +use crate::callconv::{ArgAbi, FnAbi, Reg, Uniform}; fn classify_ret(cx: &C, ret: &mut ArgAbi<'_, Ty>, offset: &mut Size) where diff --git a/compiler/rustc_target/src/callconv/sparc64.rs b/compiler/rustc_target/src/callconv/sparc64.rs index 313d8730399b2..7ca0031fc59c0 100644 --- a/compiler/rustc_target/src/callconv/sparc64.rs +++ b/compiler/rustc_target/src/callconv/sparc64.rs @@ -1,9 +1,13 @@ // FIXME: This needs an audit for correctness and completeness. -use crate::abi::call::{ - ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, Reg, Uniform, +use rustc_abi::{ + BackendRepr, FieldsShape, Float, HasDataLayout, Primitive, Reg, Scalar, Size, TyAbiInterface, + TyAndLayout, +}; + +use crate::callconv::{ + ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, Uniform, }; -use crate::abi::{self, HasDataLayout, Scalar, Size, TyAbiInterface, TyAndLayout}; use crate::spec::HasTargetSpec; #[derive(Clone, Debug)] @@ -21,7 +25,7 @@ where { let dl = cx.data_layout(); - if !matches!(scalar.primitive(), abi::Float(abi::F32 | abi::F64)) { + if !matches!(scalar.primitive(), Primitive::Float(Float::F32 | Float::F64)) { return data; } @@ -57,7 +61,7 @@ where return data; } - if scalar.primitive() == abi::Float(abi::F32) { + if scalar.primitive() == Primitive::Float(Float::F32) { data.arg_attribute = ArgAttribute::InReg; data.prefix[data.prefix_index] = Some(Reg::f32()); data.last_offset = offset + Reg::f32().size; @@ -81,14 +85,16 @@ where { data = arg_scalar(cx, scalar1, offset, data); match (scalar1.primitive(), scalar2.primitive()) { - (abi::Float(abi::F32), _) => offset += Reg::f32().size, - (_, abi::Float(abi::F64)) => offset += Reg::f64().size, - (abi::Int(i, _signed), _) => offset += i.size(), - (abi::Pointer(_), _) => offset += Reg::i64().size, + (Primitive::Float(Float::F32), _) => offset += Reg::f32().size, + (_, Primitive::Float(Float::F64)) => offset += Reg::f64().size, + (Primitive::Int(i, _signed), _) => offset += i.size(), + (Primitive::Pointer(_), _) => offset += Reg::i64().size, _ => {} } - if (offset.bytes() % 4) != 0 && matches!(scalar2.primitive(), abi::Float(abi::F32 | abi::F64)) { + if (offset.bytes() % 4) != 0 + && matches!(scalar2.primitive(), Primitive::Float(Float::F32 | Float::F64)) + { offset += Size::from_bytes(4 - (offset.bytes() % 4)); } data = arg_scalar(cx, scalar2, offset, data); @@ -105,15 +111,15 @@ where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { - if let abi::FieldsShape::Union(_) = layout.fields { + if let FieldsShape::Union(_) = layout.fields { return data; } match layout.backend_repr { - abi::BackendRepr::Scalar(scalar) => { + BackendRepr::Scalar(scalar) => { data = arg_scalar(cx, &scalar, offset, data); } - abi::BackendRepr::Memory { .. } => { + BackendRepr::Memory { .. } => { for i in 0..layout.fields.count() { if offset < layout.fields.offset(i) { offset = layout.fields.offset(i); @@ -122,7 +128,7 @@ where } } _ => { - if let abi::BackendRepr::ScalarPair(scalar1, scalar2) = &layout.backend_repr { + if let BackendRepr::ScalarPair(scalar1, scalar2) = &layout.backend_repr { data = arg_scalar_pair(cx, scalar1, scalar2, offset, data); } } @@ -148,16 +154,16 @@ where } match arg.layout.fields { - abi::FieldsShape::Primitive => unreachable!(), - abi::FieldsShape::Array { .. } => { + FieldsShape::Primitive => unreachable!(), + FieldsShape::Array { .. } => { // Arrays are passed indirectly arg.make_indirect(); return; } - abi::FieldsShape::Union(_) => { + FieldsShape::Union(_) => { // Unions and are always treated as a series of 64-bit integer chunks } - abi::FieldsShape::Arbitrary { .. } => { + FieldsShape::Arbitrary { .. } => { // Structures with floating point numbers need special care. let mut data = parse_structure( diff --git a/compiler/rustc_target/src/callconv/wasm.rs b/compiler/rustc_target/src/callconv/wasm.rs index 3c4cd76a75464..364a655113114 100644 --- a/compiler/rustc_target/src/callconv/wasm.rs +++ b/compiler/rustc_target/src/callconv/wasm.rs @@ -1,5 +1,6 @@ -use crate::abi::call::{ArgAbi, FnAbi}; -use crate::abi::{HasDataLayout, TyAbiInterface}; +use rustc_abi::{BackendRepr, Float, HasDataLayout, Integer, Primitive, TyAbiInterface}; + +use crate::callconv::{ArgAbi, FnAbi}; fn unwrap_trivial_aggregate<'a, Ty, C>(cx: &C, val: &mut ArgAbi<'a, Ty>) -> bool where @@ -27,6 +28,16 @@ where if ret.layout.is_aggregate() && !unwrap_trivial_aggregate(cx, ret) { ret.make_indirect(); } + + // `long double`, `__int128_t` and `__uint128_t` use an indirect return + if let BackendRepr::Scalar(scalar) = ret.layout.backend_repr { + match scalar.primitive() { + Primitive::Int(Integer::I128, _) | Primitive::Float(Float::F128) => { + ret.make_indirect(); + } + _ => {} + } + } } fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) diff --git a/compiler/rustc_target/src/callconv/x86.rs b/compiler/rustc_target/src/callconv/x86.rs index cd8465c09ca98..5b9414536d8ca 100644 --- a/compiler/rustc_target/src/callconv/x86.rs +++ b/compiler/rustc_target/src/callconv/x86.rs @@ -1,9 +1,10 @@ -use crate::abi::call::{ArgAttribute, FnAbi, PassMode, Reg, RegKind}; -use crate::abi::{ - AddressSpace, Align, BackendRepr, Float, HasDataLayout, Pointer, TyAbiInterface, TyAndLayout, +use rustc_abi::{ + AddressSpace, Align, BackendRepr, ExternAbi, HasDataLayout, Primitive, Reg, RegKind, + TyAbiInterface, TyAndLayout, }; + +use crate::callconv::{ArgAttribute, FnAbi, PassMode}; use crate::spec::HasTargetSpec; -use crate::spec::abi::Abi as SpecAbi; #[derive(PartialEq)] pub(crate) enum Flavor { @@ -214,7 +215,7 @@ pub(crate) fn fill_inregs<'a, Ty, C>( } } -pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, abi: SpecAbi) +pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, abi: ExternAbi) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout + HasTargetSpec, @@ -223,18 +224,19 @@ where // registers will quiet signalling NaNs. Also avoid using SSE registers since they // are not always available (depending on target features). if !fn_abi.ret.is_ignore() - // Intrinsics themselves are not actual "real" functions, so theres no need to change their ABIs. - && abi != SpecAbi::RustIntrinsic + // Intrinsics themselves are not "real" functions, so theres no need to change their ABIs. + && abi != ExternAbi::RustIntrinsic { let has_float = match fn_abi.ret.layout.backend_repr { - BackendRepr::Scalar(s) => matches!(s.primitive(), Float(_)), + BackendRepr::Scalar(s) => matches!(s.primitive(), Primitive::Float(_)), BackendRepr::ScalarPair(s1, s2) => { - matches!(s1.primitive(), Float(_)) || matches!(s2.primitive(), Float(_)) + matches!(s1.primitive(), Primitive::Float(_)) + || matches!(s2.primitive(), Primitive::Float(_)) } _ => false, // anyway not passed via registers on x86 }; if has_float { - if fn_abi.ret.layout.size <= Pointer(AddressSpace::DATA).size(cx) { + if fn_abi.ret.layout.size <= Primitive::Pointer(AddressSpace::DATA).size(cx) { // Same size or smaller than pointer, return in a register. fn_abi.ret.cast_to(Reg { kind: RegKind::Integer, size: fn_abi.ret.layout.size }); } else { diff --git a/compiler/rustc_target/src/callconv/x86_64.rs b/compiler/rustc_target/src/callconv/x86_64.rs index 37aecf323a182..b15d82c26dac2 100644 --- a/compiler/rustc_target/src/callconv/x86_64.rs +++ b/compiler/rustc_target/src/callconv/x86_64.rs @@ -1,10 +1,12 @@ // The classification code for the x86_64 ABI is taken from the clay language // https://github.com/jckarter/clay/blob/db0bd2702ab0b6e48965cd85f8859bbd5f60e48e/compiler/externals.cpp -use rustc_abi::{BackendRepr, HasDataLayout, Size, TyAbiInterface, TyAndLayout}; +use rustc_abi::{ + BackendRepr, HasDataLayout, Primitive, Reg, RegKind, Size, TyAbiInterface, TyAndLayout, + Variants, +}; -use crate::abi; -use crate::abi::call::{ArgAbi, CastTarget, FnAbi, Reg, RegKind}; +use crate::callconv::{ArgAbi, CastTarget, FnAbi}; /// Classification of "eightbyte" components. // N.B., the order of the variants is from general to specific, @@ -52,8 +54,8 @@ where BackendRepr::Uninhabited => return Ok(()), BackendRepr::Scalar(scalar) => match scalar.primitive() { - abi::Int(..) | abi::Pointer(_) => Class::Int, - abi::Float(_) => Class::Sse, + Primitive::Int(..) | Primitive::Pointer(_) => Class::Int, + Primitive::Float(_) => Class::Sse, }, BackendRepr::Vector { .. } => Class::Sse, @@ -65,8 +67,8 @@ where } match &layout.variants { - abi::Variants::Single { .. } | abi::Variants::Empty => {} - abi::Variants::Multiple { variants, .. } => { + Variants::Single { .. } | Variants::Empty => {} + Variants::Multiple { variants, .. } => { // Treat enum variants like union members. for variant_idx in variants.indices() { classify(cx, layout.for_variant(cx, variant_idx), cls, off)?; diff --git a/compiler/rustc_target/src/callconv/x86_win64.rs b/compiler/rustc_target/src/callconv/x86_win64.rs index 83d94cb11bafd..23ef2cf828409 100644 --- a/compiler/rustc_target/src/callconv/x86_win64.rs +++ b/compiler/rustc_target/src/callconv/x86_win64.rs @@ -1,12 +1,12 @@ -use rustc_abi::{BackendRepr, Float, Primitive}; +use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind, Size}; -use crate::abi::call::{ArgAbi, FnAbi, Reg}; +use crate::callconv::{ArgAbi, FnAbi, Reg}; use crate::spec::HasTargetSpec; // Win64 ABI: https://docs.microsoft.com/en-us/cpp/build/parameter-passing -pub(crate) fn compute_abi_info(cx: &impl HasTargetSpec, fn_abi: &mut FnAbi<'_, Ty>) { - let fixup = |a: &mut ArgAbi<'_, Ty>| { +pub(crate) fn compute_abi_info(_cx: &impl HasTargetSpec, fn_abi: &mut FnAbi<'_, Ty>) { + let fixup = |a: &mut ArgAbi<'_, Ty>, is_ret: bool| { match a.layout.backend_repr { BackendRepr::Uninhabited | BackendRepr::Memory { sized: false } => {} BackendRepr::ScalarPair(..) | BackendRepr::Memory { sized: true } => { @@ -23,11 +23,16 @@ pub(crate) fn compute_abi_info(cx: &impl HasTargetSpec, fn_abi: &mut FnAbi<' // (probably what clang calls "illegal vectors"). } BackendRepr::Scalar(scalar) => { - // Match what LLVM does for `f128` so that `compiler-builtins` builtins match up - // with what LLVM expects. - if a.layout.size.bytes() > 8 + if is_ret && matches!(scalar.primitive(), Primitive::Int(Integer::I128, _)) { + // `i128` is returned in xmm0 by Clang and GCC + // FIXME(#134288): This may change for the `-msvc` targets in the future. + let reg = Reg { kind: RegKind::Vector, size: Size::from_bits(128) }; + a.cast_to(reg); + } else if a.layout.size.bytes() > 8 && !matches!(scalar.primitive(), Primitive::Float(Float::F128)) { + // Match what LLVM does for `f128` so that `compiler-builtins` builtins match up + // with what LLVM expects. a.make_indirect(); } else { a.extend_integer_width_to(32); @@ -37,19 +42,22 @@ pub(crate) fn compute_abi_info(cx: &impl HasTargetSpec, fn_abi: &mut FnAbi<' }; if !fn_abi.ret.is_ignore() { - fixup(&mut fn_abi.ret); + fixup(&mut fn_abi.ret, true); } + for arg in fn_abi.args.iter_mut() { - if arg.is_ignore() { - // x86_64-pc-windows-gnu doesn't ignore ZSTs. - if cx.target_spec().os == "windows" - && cx.target_spec().env == "gnu" - && arg.layout.is_zst() - { - arg.make_indirect_from_ignore(); - } + if arg.is_ignore() && arg.layout.is_zst() { + // Windows ABIs do not talk about ZST since such types do not exist in MSVC. + // In that sense we can do whatever we want here, and maybe we should throw an error + // (but of course that would be a massive breaking change now). + // We try to match clang and gcc (which allow ZST is their windows-gnu targets), so we + // pass ZST via pointer indirection. + arg.make_indirect_from_ignore(); continue; } - fixup(arg); + fixup(arg, false); } + // FIXME: We should likely also do something about ZST return types, similar to above. + // However, that's non-trivial due to `()`. + // See . } diff --git a/compiler/rustc_target/src/callconv/xtensa.rs b/compiler/rustc_target/src/callconv/xtensa.rs index 9d313d1650032..6c030cb3bf770 100644 --- a/compiler/rustc_target/src/callconv/xtensa.rs +++ b/compiler/rustc_target/src/callconv/xtensa.rs @@ -5,8 +5,9 @@ //! Section 8.1.4 & 8.1.5 of the Xtensa ISA reference manual, as well as snippets from //! Section 2.3 from the Xtensa programmers guide. -use crate::abi::call::{ArgAbi, FnAbi, Reg, Uniform}; -use crate::abi::{BackendRepr, HasDataLayout, Size, TyAbiInterface}; +use rustc_abi::{BackendRepr, HasDataLayout, Size, TyAbiInterface}; + +use crate::callconv::{ArgAbi, FnAbi, Reg, Uniform}; use crate::spec::HasTargetSpec; const NUM_ARG_GPRS: u64 = 6; diff --git a/compiler/rustc_target/src/json.rs b/compiler/rustc_target/src/json.rs index b09d8d724efd4..8d6f8f4c6f67b 100644 --- a/compiler/rustc_target/src/json.rs +++ b/compiler/rustc_target/src/json.rs @@ -92,7 +92,7 @@ impl ToJson for Option { } } -impl ToJson for crate::abi::call::Conv { +impl ToJson for crate::callconv::Conv { fn to_json(&self) -> Json { let buf: String; let s = match self { @@ -105,7 +105,6 @@ impl ToJson for crate::abi::call::Conv { Self::CCmseNonSecureCall => "CCmseNonSecureCall", Self::CCmseNonSecureEntry => "CCmseNonSecureEntry", Self::Msp430Intr => "Msp430Intr", - Self::PtxKernel => "PtxKernel", Self::X86Fastcall => "X86Fastcall", Self::X86Intr => "X86Intr", Self::X86Stdcall => "X86Stdcall", @@ -113,6 +112,7 @@ impl ToJson for crate::abi::call::Conv { Self::X86VectorCall => "X86VectorCall", Self::X86_64SysV => "X86_64SysV", Self::X86_64Win64 => "X86_64Win64", + Self::GpuKernel => "GpuKernel", Self::AvrInterrupt => "AvrInterrupt", Self::AvrNonBlockingInterrupt => "AvrNonBlockingInterrupt", Self::RiscvInterrupt { kind } => { diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index 50679ab8cc81c..7ebe96960ed0f 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -30,16 +30,7 @@ pub mod target_features; #[cfg(test)] mod tests; -pub mod abi { - pub(crate) use Float::*; - pub(crate) use Primitive::*; - // Explicitly import `Float` to avoid ambiguity with `Primitive::Float`. - pub use rustc_abi::{Float, *}; - - pub use crate::callconv as call; -} - -pub use rustc_abi::HashStableContext; +use rustc_abi::HashStableContext; /// The name of rustc's own place to organize libraries. /// diff --git a/compiler/rustc_target/src/spec/base/aix.rs b/compiler/rustc_target/src/spec/base/aix.rs index fe37d313294d2..a92d104f9108e 100644 --- a/compiler/rustc_target/src/spec/base/aix.rs +++ b/compiler/rustc_target/src/spec/base/aix.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Cc, CodeModel, LinkOutputKind, LinkerFlavor, TargetOptions, crt_objects, cvs}; pub(crate) fn opts() -> TargetOptions { diff --git a/compiler/rustc_target/src/spec/base/apple/mod.rs b/compiler/rustc_target/src/spec/base/apple/mod.rs index 497994a5998cb..1b56143545f61 100644 --- a/compiler/rustc_target/src/spec/base/apple/mod.rs +++ b/compiler/rustc_target/src/spec/base/apple/mod.rs @@ -2,8 +2,8 @@ use std::borrow::Cow; use std::env; use crate::spec::{ - Cc, DebuginfoKind, FloatAbi, FramePointer, LinkerFlavor, Lld, SplitDebuginfo, StackProbeType, - StaticCow, TargetOptions, cvs, + Cc, DebuginfoKind, FloatAbi, FramePointer, LinkerFlavor, Lld, RustcAbi, SplitDebuginfo, + StackProbeType, StaticCow, TargetOptions, cvs, }; #[cfg(test)] @@ -103,7 +103,7 @@ pub(crate) fn base( arch: Arch, abi: TargetAbi, ) -> (TargetOptions, StaticCow, StaticCow) { - let opts = TargetOptions { + let mut opts = TargetOptions { abi: abi.target_abi().into(), llvm_floatabi: Some(FloatAbi::Hard), os: os.into(), @@ -154,6 +154,10 @@ pub(crate) fn base( ..Default::default() }; + if matches!(arch, Arch::I386 | Arch::I686) { + // All Apple x86-32 targets have SSE2. + opts.rustc_abi = Some(RustcAbi::X86Sse2); + } (opts, unversioned_llvm_target(os, arch, abi), arch.target_arch()) } diff --git a/compiler/rustc_target/src/spec/base/apple/tests.rs b/compiler/rustc_target/src/spec/base/apple/tests.rs index a7335c9bcaed4..7a985ad4dc056 100644 --- a/compiler/rustc_target/src/spec/base/apple/tests.rs +++ b/compiler/rustc_target/src/spec/base/apple/tests.rs @@ -32,10 +32,13 @@ fn macos_link_environment_unmodified() { for target in all_macos_targets { // macOS targets should only remove information for cross-compiling, but never // for the host. - assert_eq!(target.link_env_remove, crate::spec::cvs![ - "IPHONEOS_DEPLOYMENT_TARGET", - "TVOS_DEPLOYMENT_TARGET", - "XROS_DEPLOYMENT_TARGET" - ],); + assert_eq!( + target.link_env_remove, + crate::spec::cvs![ + "IPHONEOS_DEPLOYMENT_TARGET", + "TVOS_DEPLOYMENT_TARGET", + "XROS_DEPLOYMENT_TARGET" + ], + ); } } diff --git a/compiler/rustc_target/src/spec/base/avr_gnu.rs b/compiler/rustc_target/src/spec/base/avr_gnu.rs index 4f348af21ad99..3554dcfcb4a13 100644 --- a/compiler/rustc_target/src/spec/base/avr_gnu.rs +++ b/compiler/rustc_target/src/spec/base/avr_gnu.rs @@ -28,9 +28,10 @@ pub(crate) fn target(target_cpu: &'static str, mmcu: &'static str) -> Target { linker: Some("avr-gcc".into()), eh_frame_header: false, pre_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &[mmcu]), - late_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &[ - "-lgcc", - ]), + late_link_args: TargetOptions::link_args( + LinkerFlavor::Gnu(Cc::Yes, Lld::No), + &["-lgcc"], + ), max_atomic_width: Some(16), atomic_cas: false, relocation_model: RelocModel::Static, diff --git a/compiler/rustc_target/src/spec/base/bpf.rs b/compiler/rustc_target/src/spec/base/bpf.rs index 17d5e75ef6d01..7c0e2e165b649 100644 --- a/compiler/rustc_target/src/spec/base/bpf.rs +++ b/compiler/rustc_target/src/spec/base/bpf.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, TargetOptions}; pub(crate) fn opts(endian: Endian) -> TargetOptions { diff --git a/compiler/rustc_target/src/spec/base/cygwin.rs b/compiler/rustc_target/src/spec/base/cygwin.rs new file mode 100644 index 0000000000000..fe3efb3f46ba0 --- /dev/null +++ b/compiler/rustc_target/src/spec/base/cygwin.rs @@ -0,0 +1,46 @@ +use std::borrow::Cow; + +use crate::spec::{Cc, DebuginfoKind, LinkerFlavor, Lld, SplitDebuginfo, TargetOptions, cvs}; + +pub(crate) fn opts() -> TargetOptions { + let mut pre_link_args = TargetOptions::link_args( + LinkerFlavor::Gnu(Cc::No, Lld::No), + &["--disable-dynamicbase", "--enable-auto-image-base"], + ); + crate::spec::add_link_args( + &mut pre_link_args, + LinkerFlavor::Gnu(Cc::Yes, Lld::No), + &["-Wl,--disable-dynamicbase", "-Wl,--enable-auto-image-base"], + ); + let cygwin_libs = &["-lcygwin", "-lgcc", "-lcygwin", "-luser32", "-lkernel32", "-lgcc_s"]; + let mut late_link_args = + TargetOptions::link_args(LinkerFlavor::Gnu(Cc::No, Lld::No), cygwin_libs); + crate::spec::add_link_args( + &mut late_link_args, + LinkerFlavor::Gnu(Cc::Yes, Lld::No), + cygwin_libs, + ); + TargetOptions { + os: "cygwin".into(), + vendor: "pc".into(), + // FIXME(#13846) this should be enabled for cygwin + function_sections: false, + linker: Some("gcc".into()), + dynamic_linking: true, + dll_prefix: "".into(), + dll_suffix: ".dll".into(), + exe_suffix: ".exe".into(), + families: cvs!["unix"], + is_like_windows: true, + allows_weak_linkage: false, + pre_link_args, + late_link_args, + abi_return_struct_as_int: true, + emit_debug_gdb_scripts: false, + requires_uwtable: true, + eh_frame_header: false, + debuginfo_kind: DebuginfoKind::Dwarf, + supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]), + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/base/fuchsia.rs b/compiler/rustc_target/src/spec/base/fuchsia.rs index 910ee328368db..9deafcac27f15 100644 --- a/compiler/rustc_target/src/spec/base/fuchsia.rs +++ b/compiler/rustc_target/src/spec/base/fuchsia.rs @@ -8,19 +8,22 @@ pub(crate) fn opts() -> TargetOptions { // so we only list them for ld/lld. // // https://github.com/llvm/llvm-project/blob/db9322b2066c55254e7691efeab863f43bfcc084/clang/lib/Driver/ToolChains/Fuchsia.cpp#L31 - let pre_link_args = TargetOptions::link_args(LinkerFlavor::Gnu(Cc::No, Lld::No), &[ - "--build-id", - "--hash-style=gnu", - "-z", - "max-page-size=4096", - "-z", - "now", - "-z", - "rodynamic", - "-z", - "separate-loadable-segments", - "--pack-dyn-relocs=relr", - ]); + let pre_link_args = TargetOptions::link_args( + LinkerFlavor::Gnu(Cc::No, Lld::No), + &[ + "--build-id", + "--hash-style=gnu", + "-z", + "max-page-size=4096", + "-z", + "now", + "-z", + "rodynamic", + "-z", + "separate-loadable-segments", + "--pack-dyn-relocs=relr", + ], + ); TargetOptions { os: "fuchsia".into(), diff --git a/compiler/rustc_target/src/spec/base/illumos.rs b/compiler/rustc_target/src/spec/base/illumos.rs index bb7dfafc53eb3..2391b229e5ba0 100644 --- a/compiler/rustc_target/src/spec/base/illumos.rs +++ b/compiler/rustc_target/src/spec/base/illumos.rs @@ -1,25 +1,28 @@ use crate::spec::{Cc, FramePointer, LinkerFlavor, TargetOptions, cvs}; pub(crate) fn opts() -> TargetOptions { - let late_link_args = TargetOptions::link_args(LinkerFlavor::Unix(Cc::Yes), &[ - // The illumos libc contains a stack unwinding implementation, as - // does libgcc_s. The latter implementation includes several - // additional symbols that are not always in base libc. To force - // the consistent use of just one unwinder, we ensure libc appears - // after libgcc_s in the NEEDED list for the resultant binary by - // ignoring any attempts to add it as a dynamic dependency until the - // very end. - // FIXME: This should be replaced by a more complete and generic - // mechanism for controlling the order of library arguments passed - // to the linker. - "-lc", - // LLVM will insert calls to the stack protector functions - // "__stack_chk_fail" and "__stack_chk_guard" into code in native - // object files. Some platforms include these symbols directly in - // libc, but at least historically these have been provided in - // libssp.so on illumos and Solaris systems. - "-lssp", - ]); + let late_link_args = TargetOptions::link_args( + LinkerFlavor::Unix(Cc::Yes), + &[ + // The illumos libc contains a stack unwinding implementation, as + // does libgcc_s. The latter implementation includes several + // additional symbols that are not always in base libc. To force + // the consistent use of just one unwinder, we ensure libc appears + // after libgcc_s in the NEEDED list for the resultant binary by + // ignoring any attempts to add it as a dynamic dependency until the + // very end. + // FIXME: This should be replaced by a more complete and generic + // mechanism for controlling the order of library arguments passed + // to the linker. + "-lc", + // LLVM will insert calls to the stack protector functions + // "__stack_chk_fail" and "__stack_chk_guard" into code in native + // object files. Some platforms include these symbols directly in + // libc, but at least historically these have been provided in + // libssp.so on illumos and Solaris systems. + "-lssp", + ], + ); TargetOptions { os: "illumos".into(), diff --git a/compiler/rustc_target/src/spec/base/mod.rs b/compiler/rustc_target/src/spec/base/mod.rs index 28d10dcf2ff3a..b9139c8452c5f 100644 --- a/compiler/rustc_target/src/spec/base/mod.rs +++ b/compiler/rustc_target/src/spec/base/mod.rs @@ -3,6 +3,7 @@ pub(crate) mod android; pub(crate) mod apple; pub(crate) mod avr_gnu; pub(crate) mod bpf; +pub(crate) mod cygwin; pub(crate) mod dragonfly; pub(crate) mod freebsd; pub(crate) mod fuchsia; diff --git a/compiler/rustc_target/src/spec/base/nto_qnx.rs b/compiler/rustc_target/src/spec/base/nto_qnx.rs index 65475d068d7e9..3f35b8b801ab9 100644 --- a/compiler/rustc_target/src/spec/base/nto_qnx.rs +++ b/compiler/rustc_target/src/spec/base/nto_qnx.rs @@ -1,4 +1,6 @@ -use crate::spec::{RelroLevel, TargetOptions, cvs}; +use crate::spec::{ + Cc, LinkArgs, LinkerFlavor, Lld, RelroLevel, Target, TargetMetadata, TargetOptions, cvs, +}; pub(crate) fn opts() -> TargetOptions { TargetOptions { @@ -16,3 +18,96 @@ pub(crate) fn opts() -> TargetOptions { ..Default::default() } } + +pub(crate) fn meta() -> TargetMetadata { + TargetMetadata { description: None, tier: Some(3), host_tools: Some(false), std: Some(true) } +} + +pub(crate) fn aarch64() -> Target { + Target { + llvm_target: "aarch64-unknown-unknown".into(), + metadata: meta(), + pointer_width: 64, + // from: https://llvm.org/docs/LangRef.html#data-layout + // e = little endian + // m:e = ELF mangling: Private symbols get a .L prefix + // i8:8:32 = 8-bit-integer, minimum_alignment=8, preferred_alignment=32 + // i16:16:32 = 16-bit-integer, minimum_alignment=16, preferred_alignment=32 + // i64:64 = 64-bit-integer, minimum_alignment=64, preferred_alignment=64 + // i128:128 = 128-bit-integer, minimum_alignment=128, preferred_alignment=128 + // n32:64 = 32 and 64 are native integer widths; Elements of this set are considered to support most general arithmetic operations efficiently. + // S128 = 128 bits are the natural alignment of the stack in bits. + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), + arch: "aarch64".into(), + options: TargetOptions { + features: "+v8a".into(), + max_atomic_width: Some(128), + ..opts() + } + } +} + +pub(crate) fn x86_64() -> Target { + Target { + llvm_target: "x86_64-pc-unknown".into(), + metadata: meta(), + pointer_width: 64, + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), + arch: "x86_64".into(), + options: TargetOptions { + cpu: "x86-64".into(), + plt_by_default: false, + max_atomic_width: Some(64), + vendor: "pc".into(), + ..opts() + }, + } +} + +pub(crate) fn pre_link_args(api_var: ApiVariant, arch: Arch) -> LinkArgs { + let (qcc_arg, arch_lib_dir) = match arch { + Arch::Aarch64 => ("-Vgcc_ntoaarch64le_cxx", "aarch64le"), + Arch::I586 => { + ("-Vgcc_ntox86_cxx", "notSupportedByQnx_compiler/rustc_target/src/spec/base/nto_qnx.rs") + } + Arch::X86_64 => ("-Vgcc_ntox86_64_cxx", "x86_64"), + }; + match api_var { + ApiVariant::Default => { + TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &[qcc_arg]) + } + ApiVariant::IoSock => TargetOptions::link_args( + LinkerFlavor::Gnu(Cc::Yes, Lld::No), + &[qcc_arg, get_iosock_param(arch_lib_dir)], + ), + } +} + +pub(crate) enum ApiVariant { + Default, + IoSock, +} + +pub(crate) enum Arch { + Aarch64, + I586, + X86_64, +} + +// When using `io-sock` on QNX, we must add a search path for the linker so +// that it prefers the io-sock version. +// The path depends on the host, i.e. we cannot hard-code it here, but have +// to determine it when the compiler runs. +// When using the QNX toolchain, the environment variable QNX_TARGET is always set. +// More information: +// https://www.qnx.com/developers/docs/7.1/index.html#com.qnx.doc.neutrino.io_sock/topic/migrate_app.html +fn get_iosock_param(arch_lib_dir: &str) -> &'static str { + let target_dir = std::env::var("QNX_TARGET") + .unwrap_or_else(|_| "QNX_TARGET_not_set_please_source_qnxsdp-env.sh".into()); + let linker_param = format!("-L{target_dir}/{arch_lib_dir}/io-sock/lib"); + + // FIXME: leaking this is kind of weird: we're feeding these into something that expects an + // `AsRef`, but often converts to `OsString` anyways, so shouldn't we just demand an `OsString`? + linker_param.leak() +} diff --git a/compiler/rustc_target/src/spec/base/uefi_msvc.rs b/compiler/rustc_target/src/spec/base/uefi_msvc.rs index b0232ecbe61e7..4766eab7fd7c1 100644 --- a/compiler/rustc_target/src/spec/base/uefi_msvc.rs +++ b/compiler/rustc_target/src/spec/base/uefi_msvc.rs @@ -14,22 +14,25 @@ use crate::spec::{LinkerFlavor, Lld, PanicStrategy, StackProbeType, TargetOption pub(crate) fn opts() -> TargetOptions { let mut base = base::msvc::opts(); - base.add_pre_link_args(LinkerFlavor::Msvc(Lld::No), &[ - // Non-standard subsystems have no default entry-point in PE+ files. We have to define - // one. "efi_main" seems to be a common choice amongst other implementations and the - // spec. - "/entry:efi_main", - // COFF images have a "Subsystem" field in their header, which defines what kind of - // program it is. UEFI has 3 fields reserved, which are EFI_APPLICATION, - // EFI_BOOT_SERVICE_DRIVER, and EFI_RUNTIME_DRIVER. We default to EFI_APPLICATION, - // which is very likely the most common option. Individual projects can override this - // with custom linker flags. - // The subsystem-type only has minor effects on the application. It defines the memory - // regions the application is loaded into (runtime-drivers need to be put into - // reserved areas), as well as whether a return from the entry-point is treated as - // exit (default for applications). - "/subsystem:efi_application", - ]); + base.add_pre_link_args( + LinkerFlavor::Msvc(Lld::No), + &[ + // Non-standard subsystems have no default entry-point in PE+ files. We have to define + // one. "efi_main" seems to be a common choice amongst other implementations and the + // spec. + "/entry:efi_main", + // COFF images have a "Subsystem" field in their header, which defines what kind of + // program it is. UEFI has 3 fields reserved, which are EFI_APPLICATION, + // EFI_BOOT_SERVICE_DRIVER, and EFI_RUNTIME_DRIVER. We default to EFI_APPLICATION, + // which is very likely the most common option. Individual projects can override this + // with custom linker flags. + // The subsystem-type only has minor effects on the application. It defines the memory + // regions the application is loaded into (runtime-drivers need to be put into + // reserved areas), as well as whether a return from the entry-point is treated as + // exit (default for applications). + "/subsystem:efi_application", + ], + ); TargetOptions { os: "uefi".into(), diff --git a/compiler/rustc_target/src/spec/base/windows_gnu.rs b/compiler/rustc_target/src/spec/base/windows_gnu.rs index e975102e2389a..024b10f2faad7 100644 --- a/compiler/rustc_target/src/spec/base/windows_gnu.rs +++ b/compiler/rustc_target/src/spec/base/windows_gnu.rs @@ -6,19 +6,26 @@ use crate::spec::{ }; pub(crate) fn opts() -> TargetOptions { - let mut pre_link_args = TargetOptions::link_args(LinkerFlavor::Gnu(Cc::No, Lld::No), &[ - // Enable ASLR - "--dynamicbase", - // ASLR will rebase it anyway so leaving that option enabled only leads to confusion - "--disable-auto-image-base", - ]); - add_link_args(&mut pre_link_args, LinkerFlavor::Gnu(Cc::Yes, Lld::No), &[ - // Tell GCC to avoid linker plugins, because we are not bundling - // them with Windows installer, and Rust does its own LTO anyways. - "-fno-use-linker-plugin", - "-Wl,--dynamicbase", - "-Wl,--disable-auto-image-base", - ]); + let mut pre_link_args = TargetOptions::link_args( + LinkerFlavor::Gnu(Cc::No, Lld::No), + &[ + // Enable ASLR + "--dynamicbase", + // ASLR will rebase it anyway so leaving that option enabled only leads to confusion + "--disable-auto-image-base", + ], + ); + add_link_args( + &mut pre_link_args, + LinkerFlavor::Gnu(Cc::Yes, Lld::No), + &[ + // Tell GCC to avoid linker plugins, because we are not bundling + // them with Windows installer, and Rust does its own LTO anyways. + "-fno-use-linker-plugin", + "-Wl,--dynamicbase", + "-Wl,--disable-auto-image-base", + ], + ); // Order of `late_link_args*` was found through trial and error to work with various // mingw-w64 versions (not tested on the CI). It's expected to change from time to time. @@ -97,9 +104,9 @@ pub(crate) fn opts() -> TargetOptions { emit_debug_gdb_scripts: false, requires_uwtable: true, eh_frame_header: false, + debuginfo_kind: DebuginfoKind::Dwarf, // FIXME(davidtwco): Support Split DWARF on Windows GNU - may require LLVM changes to // output DWO, despite using DWARF, doesn't use ELF.. - debuginfo_kind: DebuginfoKind::Pdb, supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]), ..Default::default() } diff --git a/compiler/rustc_target/src/spec/base/windows_gnullvm.rs b/compiler/rustc_target/src/spec/base/windows_gnullvm.rs index 4f370ec8bd032..4a6e7c75200f6 100644 --- a/compiler/rustc_target/src/spec/base/windows_gnullvm.rs +++ b/compiler/rustc_target/src/spec/base/windows_gnullvm.rs @@ -7,18 +7,15 @@ pub(crate) fn opts() -> TargetOptions { // as a path since it's not added to linker search path by the default. // There were attempts to make it behave like libgcc (so one can just use -l) // but LLVM maintainers rejected it: https://reviews.llvm.org/D51440 - let pre_link_args = TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &[ - "-nolibc", - "--unwindlib=none", - ]); + let pre_link_args = TargetOptions::link_args( + LinkerFlavor::Gnu(Cc::Yes, Lld::No), + &["-nolibc", "--unwindlib=none"], + ); // Order of `late_link_args*` does not matter with LLD. - let late_link_args = TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &[ - "-lmingw32", - "-lmingwex", - "-lmsvcrt", - "-lkernel32", - "-luser32", - ]); + let late_link_args = TargetOptions::link_args( + LinkerFlavor::Gnu(Cc::Yes, Lld::No), + &["-lmingw32", "-lmingwex", "-lmsvcrt", "-lkernel32", "-luser32"], + ); TargetOptions { os: "windows".into(), @@ -44,9 +41,9 @@ pub(crate) fn opts() -> TargetOptions { has_thread_local: true, crt_static_allows_dylibs: true, crt_static_respected: true, + debuginfo_kind: DebuginfoKind::Dwarf, // FIXME(davidtwco): Support Split DWARF on Windows GNU - may require LLVM changes to // output DWO, despite using DWARF, doesn't use ELF.. - debuginfo_kind: DebuginfoKind::Pdb, supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]), ..Default::default() } diff --git a/compiler/rustc_target/src/spec/base/xtensa.rs b/compiler/rustc_target/src/spec/base/xtensa.rs index 280dd16e26453..47a532dfdd48d 100644 --- a/compiler/rustc_target/src/spec/base/xtensa.rs +++ b/compiler/rustc_target/src/spec/base/xtensa.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, TargetOptions}; pub(crate) fn opts() -> TargetOptions { diff --git a/compiler/rustc_target/src/spec/json.rs b/compiler/rustc_target/src/spec/json.rs index 9cdc0801b1f00..f703132e51f0a 100644 --- a/compiler/rustc_target/src/spec/json.rs +++ b/compiler/rustc_target/src/spec/json.rs @@ -128,6 +128,19 @@ impl Target { Some(Ok(())) })).unwrap_or(Ok(())) } ); + ($key_name:ident, RustcAbi) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { + match s.parse::() { + Ok(rustc_abi) => base.$key_name = Some(rustc_abi), + _ => return Some(Err(format!( + "'{s}' is not a valid value for rustc-abi. \ + Use 'x86-softfloat' or leave the field unset." + ))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); ($key_name:ident, RelocModel) => ( { let name = (stringify!($key_name)).replace("_", "-"); obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { @@ -546,6 +559,7 @@ impl Target { key!(link_env_remove, list); key!(asm_args, list); key!(cpu); + key!(need_explicit_cpu, bool); key!(features); key!(dynamic_linking, bool); key!(direct_access_external_data, Option); @@ -611,6 +625,7 @@ impl Target { key!(llvm_mcount_intrinsic, optional); key!(llvm_abiname); key!(llvm_floatabi, FloatAbi)?; + key!(rustc_abi, RustcAbi)?; key!(relax_elf_relocations, bool); key!(llvm_args, list); key!(use_ctors_section, bool); @@ -632,10 +647,10 @@ impl Target { // Each field should have been read using `Json::remove` so any keys remaining are unused. let remaining_keys = obj.keys(); - Ok((base, TargetWarnings { - unused_fields: remaining_keys.cloned().collect(), - incorrect_type, - })) + Ok(( + base, + TargetWarnings { unused_fields: remaining_keys.cloned().collect(), incorrect_type }, + )) } } @@ -720,6 +735,7 @@ impl ToJson for Target { target_option_val!(link_env_remove); target_option_val!(asm_args); target_option_val!(cpu); + target_option_val!(need_explicit_cpu); target_option_val!(features); target_option_val!(dynamic_linking); target_option_val!(direct_access_external_data); @@ -786,6 +802,7 @@ impl ToJson for Target { target_option_val!(llvm_mcount_intrinsic); target_option_val!(llvm_abiname); target_option_val!(llvm_floatabi); + target_option_val!(rustc_abi); target_option_val!(relax_elf_relocations); target_option_val!(llvm_args); target_option_val!(use_ctors_section); diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 02962d55a60ed..794d6457cb78b 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -42,6 +42,7 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use std::{fmt, io}; +use rustc_abi::{Endian, ExternAbi, Integer, Size, TargetDataLayout, TargetDataLayoutErrors}; use rustc_data_structures::fx::FxHashSet; use rustc_fs_util::try_canonicalize; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; @@ -50,21 +51,12 @@ use rustc_span::{Symbol, kw, sym}; use serde_json::Value; use tracing::debug; -use crate::abi::call::Conv; -use crate::abi::{Endian, Integer, Size, TargetDataLayout, TargetDataLayoutErrors}; +use crate::callconv::Conv; use crate::json::{Json, ToJson}; -use crate::spec::abi::Abi; use crate::spec::crt_objects::CrtObjects; pub mod crt_objects; -pub mod abi { - pub use rustc_abi::{ - AbiDisabled, AbiUnsupported, ExternAbi as Abi, all_names, enabled_names, is_enabled, - is_stable, lookup, - }; -} - mod base; mod json; @@ -1114,6 +1106,37 @@ impl ToJson for FloatAbi { } } +/// The Rustc-specific variant of the ABI used for this target. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum RustcAbi { + /// On x86-32 only: make use of SSE and SSE2 for ABI purposes. + X86Sse2, + /// On x86-32/64 only: do not use any FPU or SIMD registers for the ABI. + X86Softfloat, +} + +impl FromStr for RustcAbi { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + "x86-sse2" => RustcAbi::X86Sse2, + "x86-softfloat" => RustcAbi::X86Softfloat, + _ => return Err(()), + }) + } +} + +impl ToJson for RustcAbi { + fn to_json(&self) -> Json { + match *self { + RustcAbi::X86Sse2 => "x86-sse2", + RustcAbi::X86Softfloat => "x86-softfloat", + } + .to_json() + } +} + #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum TlsModel { GeneralDynamic, @@ -1624,7 +1647,7 @@ macro_rules! supported_targets { } /// List of supported targets - pub const TARGETS: &[&str] = &[$($tuple),+]; + pub static TARGETS: &[&str] = &[$($tuple),+]; fn load_builtin(target: &str) -> Option { let t = match target { @@ -1656,6 +1679,7 @@ supported_targets! { ("loongarch64-unknown-linux-gnu", loongarch64_unknown_linux_gnu), ("loongarch64-unknown-linux-musl", loongarch64_unknown_linux_musl), ("m68k-unknown-linux-gnu", m68k_unknown_linux_gnu), + ("m68k-unknown-none-elf", m68k_unknown_none_elf), ("csky-unknown-linux-gnuabiv2", csky_unknown_linux_gnuabiv2), ("csky-unknown-linux-gnuabiv2hf", csky_unknown_linux_gnuabiv2hf), ("mips-unknown-linux-gnu", mips_unknown_linux_gnu), @@ -1770,7 +1794,7 @@ supported_targets! { ("x86_64-unknown-l4re-uclibc", x86_64_unknown_l4re_uclibc), ("aarch64-unknown-redox", aarch64_unknown_redox), - ("i686-unknown-redox", i686_unknown_redox), + ("i586-unknown-redox", i586_unknown_redox), ("x86_64-unknown-redox", x86_64_unknown_redox), ("i386-apple-ios", i386_apple_ios), @@ -1811,9 +1835,11 @@ supported_targets! { ("aarch64-unknown-illumos", aarch64_unknown_illumos), ("x86_64-pc-windows-gnu", x86_64_pc_windows_gnu), + ("x86_64-uwp-windows-gnu", x86_64_uwp_windows_gnu), + ("x86_64-win7-windows-gnu", x86_64_win7_windows_gnu), ("i686-pc-windows-gnu", i686_pc_windows_gnu), ("i686-uwp-windows-gnu", i686_uwp_windows_gnu), - ("x86_64-uwp-windows-gnu", x86_64_uwp_windows_gnu), + ("i686-win7-windows-gnu", i686_win7_windows_gnu), ("aarch64-pc-windows-gnullvm", aarch64_pc_windows_gnullvm), ("i686-pc-windows-gnullvm", i686_pc_windows_gnullvm), @@ -1850,6 +1876,8 @@ supported_targets! { ("armv7a-none-eabi", armv7a_none_eabi), ("armv7a-none-eabihf", armv7a_none_eabihf), + ("armv7a-nuttx-eabi", armv7a_nuttx_eabi), + ("armv7a-nuttx-eabihf", armv7a_nuttx_eabihf), ("msp430-none-elf", msp430_none_elf), @@ -1893,6 +1921,7 @@ supported_targets! { ("aarch64-unknown-none", aarch64_unknown_none), ("aarch64-unknown-none-softfloat", aarch64_unknown_none_softfloat), + ("aarch64-unknown-nuttx", aarch64_unknown_nuttx), ("x86_64-fortanix-unknown-sgx", x86_64_fortanix_unknown_sgx), @@ -1902,6 +1931,8 @@ supported_targets! { ("nvptx64-nvidia-cuda", nvptx64_nvidia_cuda), + ("amdgcn-amd-amdhsa", amdgcn_amd_amdhsa), + ("xtensa-esp32-none-elf", xtensa_esp32_none_elf), ("xtensa-esp32-espidf", xtensa_esp32_espidf), ("xtensa-esp32s2-none-elf", xtensa_esp32s2_none_elf), @@ -1926,6 +1957,8 @@ supported_targets! { ("mipsel-sony-psp", mipsel_sony_psp), ("mipsel-sony-psx", mipsel_sony_psx), ("mipsel-unknown-none", mipsel_unknown_none), + ("mips-mti-none-elf", mips_mti_none_elf), + ("mipsel-mti-none-elf", mipsel_mti_none_elf), ("thumbv4t-none-eabi", thumbv4t_none_eabi), ("armv4t-none-eabi", armv4t_none_eabi), ("thumbv5te-none-eabi", thumbv5te_none_eabi), @@ -1955,7 +1988,11 @@ supported_targets! { ("aarch64-unknown-nto-qnx700", aarch64_unknown_nto_qnx700), ("aarch64-unknown-nto-qnx710", aarch64_unknown_nto_qnx710), + ("aarch64-unknown-nto-qnx710_iosock", aarch64_unknown_nto_qnx710_iosock), + ("aarch64-unknown-nto-qnx800", aarch64_unknown_nto_qnx800), ("x86_64-pc-nto-qnx710", x86_64_pc_nto_qnx710), + ("x86_64-pc-nto-qnx710_iosock", x86_64_pc_nto_qnx710_iosock), + ("x86_64-pc-nto-qnx800", x86_64_pc_nto_qnx800), ("i586-pc-nto-qnx700", i586_pc_nto_qnx700), ("aarch64-unknown-linux-ohos", aarch64_unknown_linux_ohos), @@ -1966,6 +2003,8 @@ supported_targets! { ("x86_64-unknown-linux-none", x86_64_unknown_linux_none), ("thumbv6m-nuttx-eabi", thumbv6m_nuttx_eabi), + ("thumbv7a-nuttx-eabi", thumbv7a_nuttx_eabi), + ("thumbv7a-nuttx-eabihf", thumbv7a_nuttx_eabihf), ("thumbv7m-nuttx-eabi", thumbv7m_nuttx_eabi), ("thumbv7em-nuttx-eabi", thumbv7em_nuttx_eabi), ("thumbv7em-nuttx-eabihf", thumbv7em_nuttx_eabihf), @@ -1978,6 +2017,7 @@ supported_targets! { ("riscv64imac-unknown-nuttx-elf", riscv64imac_unknown_nuttx_elf), ("riscv64gc-unknown-nuttx-elf", riscv64gc_unknown_nuttx_elf), + ("x86_64-pc-cygwin", x86_64_pc_cygwin), } /// Cow-Vec-Str: Cow<'static, [Cow<'static, str>]> @@ -2240,6 +2280,9 @@ pub struct TargetOptions { /// Default CPU to pass to LLVM. Corresponds to `llc -mcpu=$cpu`. Defaults /// to "generic". pub cpu: StaticCow, + /// Whether a cpu needs to be explicitly set. + /// Set to true if there is no default cpu. Defaults to false. + pub need_explicit_cpu: bool, /// Default target features to pass to LLVM. These features overwrite /// `-Ctarget-cpu` but can be overwritten with `-Ctarget-features`. /// Corresponds to `llc -mattr=$features`. @@ -2488,6 +2531,12 @@ pub struct TargetOptions { /// If not provided, LLVM will infer the float ABI from the target triple (`llvm_target`). pub llvm_floatabi: Option, + /// Picks a specific ABI for this target. This is *not* just for "Rust" ABI functions, + /// it can also affect "C" ABI functions; the point is that this flag is interpreted by + /// rustc and not forwarded to LLVM. + /// So far, this is only used on x86. + pub rustc_abi: Option, + /// Whether or not RelaxElfRelocation flag will be passed to the linker pub relax_elf_relocations: bool, @@ -2647,14 +2696,6 @@ impl TargetOptions { .collect(); } } - - pub(crate) fn has_feature(&self, search_feature: &str) -> bool { - self.features.split(',').any(|f| f.strip_prefix('+').is_some_and(|f| f == search_feature)) - } - - pub(crate) fn has_neg_feature(&self, search_feature: &str) -> bool { - self.features.split(',').any(|f| f.strip_prefix('-').is_some_and(|f| f == search_feature)) - } } impl Default for TargetOptions { @@ -2676,6 +2717,7 @@ impl Default for TargetOptions { link_script: None, asm_args: cvs![], cpu: "generic".into(), + need_explicit_cpu: false, features: "".into(), direct_access_external_data: None, dynamic_linking: false, @@ -2760,6 +2802,7 @@ impl Default for TargetOptions { llvm_mcount_intrinsic: None, llvm_abiname: "".into(), llvm_floatabi: None, + rustc_abi: None, relax_elf_relocations: false, llvm_args: cvs![], use_ctors_section: false, @@ -2801,39 +2844,40 @@ impl DerefMut for Target { impl Target { /// Given a function ABI, turn it into the correct ABI for this target. - pub fn adjust_abi(&self, abi: Abi, c_variadic: bool) -> Abi { + pub fn adjust_abi(&self, abi: ExternAbi, c_variadic: bool) -> ExternAbi { + use ExternAbi::*; match abi { // On Windows, `extern "system"` behaves like msvc's `__stdcall`. // `__stdcall` only applies on x86 and on non-variadic functions: // https://learn.microsoft.com/en-us/cpp/cpp/stdcall?view=msvc-170 - Abi::System { unwind } if self.is_like_windows && self.arch == "x86" && !c_variadic => { - Abi::Stdcall { unwind } + System { unwind } if self.is_like_windows && self.arch == "x86" && !c_variadic => { + Stdcall { unwind } } - Abi::System { unwind } => Abi::C { unwind }, - Abi::EfiApi if self.arch == "arm" => Abi::Aapcs { unwind: false }, - Abi::EfiApi if self.arch == "x86_64" => Abi::Win64 { unwind: false }, - Abi::EfiApi => Abi::C { unwind: false }, + System { unwind } => C { unwind }, + EfiApi if self.arch == "arm" => Aapcs { unwind: false }, + EfiApi if self.arch == "x86_64" => Win64 { unwind: false }, + EfiApi => C { unwind: false }, // See commentary in `is_abi_supported`. - Abi::Stdcall { .. } | Abi::Thiscall { .. } if self.arch == "x86" => abi, - Abi::Stdcall { unwind } | Abi::Thiscall { unwind } => Abi::C { unwind }, - Abi::Fastcall { .. } if self.arch == "x86" => abi, - Abi::Vectorcall { .. } if ["x86", "x86_64"].contains(&&self.arch[..]) => abi, - Abi::Fastcall { unwind } | Abi::Vectorcall { unwind } => Abi::C { unwind }, + Stdcall { .. } | Thiscall { .. } if self.arch == "x86" => abi, + Stdcall { unwind } | Thiscall { unwind } => C { unwind }, + Fastcall { .. } if self.arch == "x86" => abi, + Vectorcall { .. } if ["x86", "x86_64"].contains(&&self.arch[..]) => abi, + Fastcall { unwind } | Vectorcall { unwind } => C { unwind }, // The Windows x64 calling convention we use for `extern "Rust"` // // expects the callee to save `xmm6` through `xmm15`, but `PreserveMost` // (that we use by default for `extern "rust-cold"`) doesn't save any of those. // So to avoid bloating callers, just use the Rust convention here. - Abi::RustCold if self.is_like_windows && self.arch == "x86_64" => Abi::Rust, + RustCold if self.is_like_windows && self.arch == "x86_64" => Rust, abi => abi, } } - pub fn is_abi_supported(&self, abi: Abi) -> bool { - use Abi::*; + pub fn is_abi_supported(&self, abi: ExternAbi) -> bool { + use ExternAbi::*; match abi { Rust | C { .. } @@ -2854,6 +2898,7 @@ impl Target { } Win64 { .. } | SysV64 { .. } => self.arch == "x86_64", PtxKernel => self.arch == "nvptx64", + GpuKernel => ["amdgpu", "nvptx64"].contains(&&self.arch[..]), Msp430Interrupt => self.arch == "msp430", RiscvInterruptM | RiscvInterruptS => ["riscv32", "riscv64"].contains(&&self.arch[..]), AvrInterrupt | AvrNonBlockingInterrupt => self.arch == "avr", @@ -2954,8 +2999,8 @@ impl Target { ); check_eq!( self.is_like_windows, - self.os == "windows" || self.os == "uefi", - "`is_like_windows` must be set if and only if `os` is `windows` or `uefi`" + self.os == "windows" || self.os == "uefi" || self.os == "cygwin", + "`is_like_windows` must be set if and only if `os` is `windows`, `uefi` or `cygwin`" ); check_eq!( self.is_like_wasm, @@ -3201,7 +3246,8 @@ impl Target { check_matches!( &*self.llvm_abiname, "ilp32" | "ilp32f" | "ilp32d" | "ilp32e", - "invalid RISC-V ABI name" + "invalid RISC-V ABI name: {}", + self.llvm_abiname, ); } "riscv64" => { @@ -3209,7 +3255,8 @@ impl Target { check_matches!( &*self.llvm_abiname, "lp64" | "lp64f" | "lp64d" | "lp64e", - "invalid RISC-V ABI name" + "invalid RISC-V ABI name: {}", + self.llvm_abiname, ); } "arm" => { @@ -3218,6 +3265,22 @@ impl Target { _ => {} } + // Check consistency of Rust ABI declaration. + if let Some(rust_abi) = self.rustc_abi { + match rust_abi { + RustcAbi::X86Sse2 => check_matches!( + &*self.arch, + "x86", + "`x86-sse2` ABI is only valid for x86-32 targets" + ), + RustcAbi::X86Softfloat => check_matches!( + &*self.arch, + "x86" | "x86_64", + "`x86-softfloat` ABI is only valid for x86 targets" + ), + } + } + // Check that the given target-features string makes some basic sense. if !self.features.is_empty() { let mut features_enabled = FxHashSet::default(); @@ -3243,6 +3306,26 @@ impl Target { )); } } + // Check that we don't mis-set any of the ABI-relevant features. + let abi_feature_constraints = self.abi_required_features(); + for feat in abi_feature_constraints.required { + // The feature might be enabled by default so we can't *require* it to show up. + // But it must not be *disabled*. + if features_disabled.contains(feat) { + return Err(format!( + "target feature `{feat}` is required by the ABI but gets disabled in target spec" + )); + } + } + for feat in abi_feature_constraints.incompatible { + // The feature might be disabled by default so we can't *require* it to show up. + // But it must not be *enabled*. + if features_enabled.contains(feat) { + return Err(format!( + "target feature `{feat}` is incompatible with the ABI but gets enabled in target spec" + )); + } + } } Ok(()) diff --git a/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_linux_gnu.rs index 1511c0ccaad90..de6fe991460a0 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_linux_gnu.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{StackProbeType, Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_linux_gnu_ilp32.rs b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_linux_gnu_ilp32.rs index e7070778794c2..8fdaff822fe79 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_linux_gnu_ilp32.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_linux_gnu_ilp32.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{StackProbeType, Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_netbsd.rs b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_netbsd.rs index f0f6e49eedd5f..4e1e95ab7516e 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_netbsd.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{StackProbeType, Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none.rs index 05fe32734a7e4..27dd713cc5338 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none.rs @@ -16,9 +16,10 @@ pub(crate) fn target() -> Target { linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), // Enable the Cortex-A53 errata 843419 mitigation by default - pre_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::No, Lld::No), &[ - "--fix-cortex-a53-843419", - ]), + pre_link_args: TargetOptions::link_args( + LinkerFlavor::Gnu(Cc::No, Lld::No), + &["--fix-cortex-a53-843419"], + ), features: "+v8a,+strict-align,+neon,+fp-armv8".into(), supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS, relocation_model: RelocModel::Static, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs index d6b77ffd091a9..3b719ebaf07e8 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs @@ -7,7 +7,8 @@ // For example, `-C target-cpu=cortex-a53`. use crate::spec::{ - Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, StackProbeType, Target, TargetOptions, + Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, Target, + TargetOptions, }; pub(crate) fn target() -> Target { @@ -19,6 +20,7 @@ pub(crate) fn target() -> Target { relocation_model: RelocModel::Static, disable_redzone: true, max_atomic_width: Some(128), + supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS, stack_probes: StackProbeType::Inline, panic_strategy: PanicStrategy::Abort, ..Default::default() diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx700.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx700.rs index 441ed7cf1535e..b26e6f19e1ab5 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx700.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx700.rs @@ -1,37 +1,11 @@ -use crate::spec::{Cc, LinkerFlavor, Lld, Target, TargetOptions, base}; +use crate::spec::Target; +use crate::spec::base::nto_qnx; pub(crate) fn target() -> Target { - // In QNX, libc does not provide a compatible ABI between versions. - // To distinguish between QNX versions, we needed a stable conditional compilation switch, - // which is why we needed to implement different targets in the compiler. - Target { - llvm_target: "aarch64-unknown-unknown".into(), - metadata: crate::spec::TargetMetadata { - description: Some("ARM64 QNX Neutrino 7.0 RTOS".into()), - tier: Some(3), - host_tools: Some(false), - std: Some(true), - }, - pointer_width: 64, - // from: https://llvm.org/docs/LangRef.html#data-layout - // e = little endian - // m:e = ELF mangling: Private symbols get a .L prefix - // i8:8:32 = 8-bit-integer, minimum_alignment=8, preferred_alignment=32 - // i16:16:32 = 16-bit-integer, minimum_alignment=16, preferred_alignment=32 - // i64:64 = 64-bit-integer, minimum_alignment=64, preferred_alignment=64 - // i128:128 = 128-bit-integer, minimum_alignment=128, preferred_alignment=128 - // n32:64 = 32 and 64 are native integer widths; Elements of this set are considered to support most general arithmetic operations efficiently. - // S128 = 128 bits are the natural alignment of the stack in bits. - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), - arch: "aarch64".into(), - options: TargetOptions { - features: "+v8a".into(), - max_atomic_width: Some(128), - pre_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &[ - "-Vgcc_ntoaarch64le_cxx", - ]), - env: "nto70".into(), - ..base::nto_qnx::opts() - }, - } + let mut target = nto_qnx::aarch64(); + target.metadata.description = Some("ARM64 QNX Neutrino 7.0 RTOS".into()); + target.options.pre_link_args = + nto_qnx::pre_link_args(nto_qnx::ApiVariant::Default, nto_qnx::Arch::Aarch64); + target.options.env = "nto70".into(); + target } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710.rs index 5b5a21fca9527..3a78952c36c4e 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710.rs @@ -1,8 +1,12 @@ use crate::spec::Target; +use crate::spec::base::nto_qnx; pub(crate) fn target() -> Target { - let mut base = super::aarch64_unknown_nto_qnx700::target(); - base.metadata.description = Some("ARM64 QNX Neutrino 7.1 RTOS".into()); - base.options.env = "nto71".into(); - base + let mut target = nto_qnx::aarch64(); + target.metadata.description = + Some("ARM64 QNX Neutrino 7.1 RTOS with io-pkt network stack".into()); + target.options.pre_link_args = + nto_qnx::pre_link_args(nto_qnx::ApiVariant::Default, nto_qnx::Arch::Aarch64); + target.options.env = "nto71".into(); + target } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710_iosock.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710_iosock.rs new file mode 100644 index 0000000000000..4964f4078f5cf --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710_iosock.rs @@ -0,0 +1,12 @@ +use crate::spec::Target; +use crate::spec::base::nto_qnx; + +pub(crate) fn target() -> Target { + let mut target = nto_qnx::aarch64(); + target.metadata.description = + Some("ARM64 QNX Neutrino 7.1 RTOS with io-sock network stack".into()); + target.options.pre_link_args = + nto_qnx::pre_link_args(nto_qnx::ApiVariant::IoSock, nto_qnx::Arch::Aarch64); + target.options.env = "nto71_iosock".into(); + target +} diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx800.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx800.rs new file mode 100644 index 0000000000000..5b820681efe99 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx800.rs @@ -0,0 +1,11 @@ +use crate::spec::Target; +use crate::spec::base::nto_qnx; + +pub(crate) fn target() -> Target { + let mut target = nto_qnx::aarch64(); + target.metadata.description = Some("ARM64 QNX Neutrino 8.0 RTOS".into()); + target.options.pre_link_args = + nto_qnx::pre_link_args(nto_qnx::ApiVariant::Default, nto_qnx::Arch::Aarch64); + target.options.env = "nto80".into(); + target +} diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nuttx.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nuttx.rs new file mode 100644 index 0000000000000..f7f8dc1e1efb4 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nuttx.rs @@ -0,0 +1,47 @@ +// Generic AArch64 target for NuttX OS +// +// Can be used in conjunction with the `target-feature` and +// `target-cpu` compiler flags to opt-in more hardware-specific +// features. +// +// For example, `-C target-cpu=cortex-a53`. + +use crate::spec::{ + Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, Target, + TargetOptions, cvs, +}; + +pub(crate) fn target() -> Target { + let opts = TargetOptions { + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + // Enable the Cortex-A53 errata 843419 mitigation by default + pre_link_args: TargetOptions::link_args( + LinkerFlavor::Gnu(Cc::No, Lld::No), + &["--fix-cortex-a53-843419"], + ), + features: "+v8a,+strict-align,+neon,+fp-armv8".into(), + supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS, + relocation_model: RelocModel::Static, + disable_redzone: true, + max_atomic_width: Some(128), + stack_probes: StackProbeType::Inline, + panic_strategy: PanicStrategy::Abort, + families: cvs!["unix"], + os: "nuttx".into(), + ..Default::default() + }; + Target { + llvm_target: "aarch64-unknown-none".into(), + metadata: crate::spec::TargetMetadata { + description: Some("AArch64 NuttX".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }, + pointer_width: 64, + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), + arch: "aarch64".into(), + options: opts, + } +} diff --git a/compiler/rustc_target/src/spec/targets/aarch64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/aarch64_wrs_vxworks.rs index d5e78d0307606..ac53cbaecceb1 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_wrs_vxworks.rs @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), arch: "aarch64".into(), options: TargetOptions { - features: "+v8a".into(), + features: "+v8a,+reserve-x18".into(), max_atomic_width: Some(128), stack_probes: StackProbeType::Inline, ..base::vxworks::opts() diff --git a/compiler/rustc_target/src/spec/targets/amdgcn_amd_amdhsa.rs b/compiler/rustc_target/src/spec/targets/amdgcn_amd_amdhsa.rs new file mode 100644 index 0000000000000..bb488c350c23e --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/amdgcn_amd_amdhsa.rs @@ -0,0 +1,51 @@ +use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, Target, TargetOptions}; + +pub(crate) fn target() -> Target { + Target { + arch: "amdgpu".into(), + data_layout: "e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-p7:160:256:256:32-p8:128:128-p9:192:256:256:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7:8:9".into(), + llvm_target: "amdgcn-amd-amdhsa".into(), + metadata: crate::spec::TargetMetadata { + description: Some("AMD GPU".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(false), + }, + pointer_width: 64, + + options: TargetOptions { + os: "amdhsa".into(), + vendor: "amd".into(), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + + // There are many CPUs, one for each hardware generation. + // Require to set one explicitly as there is no good default. + need_explicit_cpu: true, + + max_atomic_width: Some(64), + + // Unwinding on GPUs is not useful. + panic_strategy: PanicStrategy::Abort, + + // amdgpu backend does not support libcalls. + no_builtins: true, + simd_types_indirect: false, + + // Allow `cdylib` crate type. + dynamic_linking: true, + only_cdylib: true, + executables: false, + dll_prefix: "".into(), + dll_suffix: ".elf".into(), + + // The LLVM backend does not support stack canaries for this target + supports_stack_protector: false, + + // Force LTO, object linking does not yet work with amdgpu. + requires_lto: true, + + ..Default::default() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs index 03c96fbfdb0c3..5026c52429e50 100644 --- a/compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs @@ -4,10 +4,11 @@ pub(crate) fn target() -> Target { let mut base = base::windows_msvc::opts(); base.max_atomic_width = Some(128); base.features = "+v8a,+neon,+fp-armv8".into(); - add_link_args(&mut base.late_link_args, LinkerFlavor::Msvc(Lld::No), &[ - "/machine:arm64ec", - "softintrin.lib", - ]); + add_link_args( + &mut base.late_link_args, + LinkerFlavor::Msvc(Lld::No), + &["/machine:arm64ec", "softintrin.lib"], + ); Target { llvm_target: "arm64ec-pc-windows-msvc".into(), diff --git a/compiler/rustc_target/src/spec/targets/armeb_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/targets/armeb_unknown_linux_gnueabi.rs index bac9b39bbf2ae..60ec4edbb0d21 100644 --- a/compiler/rustc_target/src/spec/targets/armeb_unknown_linux_gnueabi.rs +++ b/compiler/rustc_target/src/spec/targets/armeb_unknown_linux_gnueabi.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{FloatAbi, Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/armebv7r_none_eabi.rs b/compiler/rustc_target/src/spec/targets/armebv7r_none_eabi.rs index db774323e7be6..18b93f6cbc4f5 100644 --- a/compiler/rustc_target/src/spec/targets/armebv7r_none_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/armebv7r_none_eabi.rs @@ -1,6 +1,7 @@ // Targets the Big endian Cortex-R4/R5 processor (ARMv7-R) -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{ Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions, }; diff --git a/compiler/rustc_target/src/spec/targets/armebv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/targets/armebv7r_none_eabihf.rs index de006a92668fb..6c22cd34fc3a1 100644 --- a/compiler/rustc_target/src/spec/targets/armebv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armebv7r_none_eabihf.rs @@ -1,6 +1,7 @@ // Targets the Cortex-R4F/R5F processor (ARMv7-R) -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{ Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions, }; diff --git a/compiler/rustc_target/src/spec/targets/armv6k_nintendo_3ds.rs b/compiler/rustc_target/src/spec/targets/armv6k_nintendo_3ds.rs index b323d2f86558e..5438811803b4a 100644 --- a/compiler/rustc_target/src/spec/targets/armv6k_nintendo_3ds.rs +++ b/compiler/rustc_target/src/spec/targets/armv6k_nintendo_3ds.rs @@ -5,12 +5,10 @@ use crate::spec::{Cc, FloatAbi, LinkerFlavor, Lld, RelocModel, Target, TargetOpt /// Requires the devkitARM toolchain for 3DS targets on the host system. pub(crate) fn target() -> Target { - let pre_link_args = TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &[ - "-specs=3dsx.specs", - "-mtune=mpcore", - "-mfloat-abi=hard", - "-mtp=soft", - ]); + let pre_link_args = TargetOptions::link_args( + LinkerFlavor::Gnu(Cc::Yes, Lld::No), + &["-specs=3dsx.specs", "-mtune=mpcore", "-mfloat-abi=hard", "-mtp=soft"], + ); Target { llvm_target: "armv6k-none-eabihf".into(), diff --git a/compiler/rustc_target/src/spec/targets/armv7_rtems_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv7_rtems_eabihf.rs index 8c3bff2a90638..4e8a9d55f9a97 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_rtems_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_rtems_eabihf.rs @@ -23,7 +23,7 @@ pub(crate) fn target() -> Target { linker_flavor: LinkerFlavor::Gnu(Cc::Yes, Lld::No), linker: None, relocation_model: RelocModel::Static, - panic_strategy: PanicStrategy::Abort, + panic_strategy: PanicStrategy::Unwind, features: "+thumb2,+neon,+vfp3".into(), max_atomic_width: Some(64), emit_debug_gdb_scripts: false, diff --git a/compiler/rustc_target/src/spec/targets/armv7_sony_vita_newlibeabihf.rs b/compiler/rustc_target/src/spec/targets/armv7_sony_vita_newlibeabihf.rs index 18d834597d87e..4b2ab8b8f20b1 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_sony_vita_newlibeabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_sony_vita_newlibeabihf.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Cc, FloatAbi, LinkerFlavor, Lld, RelocModel, Target, TargetOptions, cvs}; /// A base target for PlayStation Vita devices using the VITASDK toolchain (using newlib). @@ -6,10 +7,10 @@ use crate::spec::{Cc, FloatAbi, LinkerFlavor, Lld, RelocModel, Target, TargetOpt /// Requires the VITASDK toolchain on the host system. pub(crate) fn target() -> Target { - let pre_link_args = TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &[ - "-Wl,-q", - "-Wl,--pic-veneer", - ]); + let pre_link_args = TargetOptions::link_args( + LinkerFlavor::Gnu(Cc::Yes, Lld::No), + &["-Wl,-q", "-Wl,--pic-veneer"], + ); Target { llvm_target: "thumbv7a-vita-eabihf".into(), diff --git a/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabi.rs new file mode 100644 index 0000000000000..08cbfc743968e --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabi.rs @@ -0,0 +1,41 @@ +// Targets Cortex-A7/A8/A9 processors (ARMv7-A) running NuttX +// +// This target assumes that the device does NOT have a FPU (Floating Point Unit) +// and will use software floating point operations. This matches the NuttX EABI +// configuration without hardware floating point support. + +use crate::spec::{ + Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions, cvs, +}; + +pub(crate) fn target() -> Target { + let opts = TargetOptions { + abi: "eabi".into(), + llvm_floatabi: Some(FloatAbi::Soft), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + features: "+v7,+thumb2,+soft-float,-neon,+strict-align".into(), + relocation_model: RelocModel::Static, + disable_redzone: true, + max_atomic_width: Some(64), + panic_strategy: PanicStrategy::Abort, + emit_debug_gdb_scripts: false, + c_enum_min_bits: Some(8), + families: cvs!["unix"], + os: "nuttx".into(), + ..Default::default() + }; + Target { + llvm_target: "armv7a-none-eabi".into(), + metadata: crate::spec::TargetMetadata { + description: Some("ARMv7-A Cortex-A with NuttX".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }, + pointer_width: 32, + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), + arch: "arm".into(), + options: opts, + } +} diff --git a/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabihf.rs new file mode 100644 index 0000000000000..f68c11a9c687e --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabihf.rs @@ -0,0 +1,41 @@ +// Targets Cortex-A7/A8/A9 processors (ARMv7-A) running NuttX with hardware floating point +// +// This target assumes that the device has a FPU (Floating Point Unit) +// and will use hardware floating point operations. This matches the NuttX EABI +// configuration with hardware floating point support. + +use crate::spec::{ + Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions, cvs, +}; + +pub(crate) fn target() -> Target { + let opts = TargetOptions { + abi: "eabihf".into(), + llvm_floatabi: Some(FloatAbi::Hard), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + features: "+v7,+thumb2,+vfp3,+neon,+strict-align".into(), + relocation_model: RelocModel::Static, + disable_redzone: true, + max_atomic_width: Some(64), + panic_strategy: PanicStrategy::Abort, + emit_debug_gdb_scripts: false, + c_enum_min_bits: Some(8), + families: cvs!["unix"], + os: "nuttx".into(), + ..Default::default() + }; + Target { + llvm_target: "armv7a-none-eabihf".into(), + metadata: crate::spec::TargetMetadata { + description: Some("ARMv7-A Cortex-A with NuttX (hard float)".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }, + pointer_width: 32, + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), + arch: "arm".into(), + options: opts, + } +} diff --git a/compiler/rustc_target/src/spec/targets/bpfeb_unknown_none.rs b/compiler/rustc_target/src/spec/targets/bpfeb_unknown_none.rs index df0f29ceab23b..b95f93c3553cd 100644 --- a/compiler/rustc_target/src/spec/targets/bpfeb_unknown_none.rs +++ b/compiler/rustc_target/src/spec/targets/bpfeb_unknown_none.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Target, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/bpfel_unknown_none.rs b/compiler/rustc_target/src/spec/targets/bpfel_unknown_none.rs index 691acf89d641f..711bbb04b1950 100644 --- a/compiler/rustc_target/src/spec/targets/bpfel_unknown_none.rs +++ b/compiler/rustc_target/src/spec/targets/bpfel_unknown_none.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Target, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/i586_pc_nto_qnx700.rs b/compiler/rustc_target/src/spec/targets/i586_pc_nto_qnx700.rs index 7648f81fd4d9a..6a98a763b364d 100644 --- a/compiler/rustc_target/src/spec/targets/i586_pc_nto_qnx700.rs +++ b/compiler/rustc_target/src/spec/targets/i586_pc_nto_qnx700.rs @@ -1,25 +1,26 @@ -use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions, base}; +use crate::spec::base::nto_qnx; +use crate::spec::{RustcAbi, StackProbeType, Target, TargetOptions, base}; pub(crate) fn target() -> Target { + let mut meta = nto_qnx::meta(); + meta.description = Some("32-bit x86 QNX Neutrino 7.0 RTOS".into()); + meta.std = Some(false); Target { llvm_target: "i586-pc-unknown".into(), - metadata: crate::spec::TargetMetadata { - description: Some("32-bit x86 QNX Neutrino 7.0 RTOS".into()), - tier: Some(3), - host_tools: Some(false), - std: Some(false), - }, + metadata: meta, pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ i128:128-f64:32:64-f80:32-n8:16:32-S128" .into(), arch: "x86".into(), options: TargetOptions { + rustc_abi: Some(RustcAbi::X86Sse2), cpu: "pentium4".into(), max_atomic_width: Some(64), - pre_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &[ - "-Vgcc_ntox86_cxx", - ]), + pre_link_args: nto_qnx::pre_link_args( + nto_qnx::ApiVariant::Default, + nto_qnx::Arch::I586, + ), env: "nto70".into(), vendor: "pc".into(), stack_probes: StackProbeType::Inline, diff --git a/compiler/rustc_target/src/spec/targets/i586_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/i586_pc_windows_msvc.rs index dd38f86bced2e..394e6f9e6bf65 100644 --- a/compiler/rustc_target/src/spec/targets/i586_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/i586_pc_windows_msvc.rs @@ -2,6 +2,7 @@ use crate::spec::Target; pub(crate) fn target() -> Target { let mut base = super::i686_pc_windows_msvc::target(); + base.rustc_abi = None; // overwrite the SSE2 ABI set by the base target base.cpu = "pentium".into(); base.llvm_target = "i586-pc-windows-msvc".into(); base diff --git a/compiler/rustc_target/src/spec/targets/i586_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/i586_unknown_linux_gnu.rs index e481e967ebfa2..f04e3c2c2a5b0 100644 --- a/compiler/rustc_target/src/spec/targets/i586_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/i586_unknown_linux_gnu.rs @@ -2,6 +2,7 @@ use crate::spec::Target; pub(crate) fn target() -> Target { let mut base = super::i686_unknown_linux_gnu::target(); + base.rustc_abi = None; // overwrite the SSE2 ABI set by the base target base.cpu = "pentium".into(); base.llvm_target = "i586-unknown-linux-gnu".into(); base diff --git a/compiler/rustc_target/src/spec/targets/i586_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/i586_unknown_linux_musl.rs index 8ad93496f3a8c..42babb90da7cc 100644 --- a/compiler/rustc_target/src/spec/targets/i586_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/i586_unknown_linux_musl.rs @@ -2,6 +2,7 @@ use crate::spec::Target; pub(crate) fn target() -> Target { let mut base = super::i686_unknown_linux_musl::target(); + base.rustc_abi = None; // overwrite the SSE2 ABI set by the base target base.cpu = "pentium".into(); base.llvm_target = "i586-unknown-linux-musl".into(); // FIXME(compiler-team#422): musl targets should be dynamically linked by default. diff --git a/compiler/rustc_target/src/spec/targets/i586_unknown_redox.rs b/compiler/rustc_target/src/spec/targets/i586_unknown_redox.rs new file mode 100644 index 0000000000000..29ef8b883a118 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/i586_unknown_redox.rs @@ -0,0 +1,27 @@ +use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, base}; + +pub(crate) fn target() -> Target { + let mut base = base::redox::opts(); + base.cpu = "pentiumpro".into(); + base.plt_by_default = false; + base.max_atomic_width = Some(64); + base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m32"]); + // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved + base.stack_probes = StackProbeType::Call; + + Target { + llvm_target: "i586-unknown-redox".into(), + metadata: crate::spec::TargetMetadata { + description: None, + tier: None, + host_tools: None, + std: None, + }, + pointer_width: 32, + data_layout: + "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128" + .into(), + arch: "x86".into(), + options: base, + } +} diff --git a/compiler/rustc_target/src/spec/targets/i686_linux_android.rs b/compiler/rustc_target/src/spec/targets/i686_linux_android.rs index dcf9b6b4460f3..7d61d5ca257c1 100644 --- a/compiler/rustc_target/src/spec/targets/i686_linux_android.rs +++ b/compiler/rustc_target/src/spec/targets/i686_linux_android.rs @@ -1,4 +1,4 @@ -use crate::spec::{SanitizerSet, StackProbeType, Target, TargetOptions, base}; +use crate::spec::{RustcAbi, SanitizerSet, StackProbeType, Target, TargetOptions, base}; // See https://developer.android.com/ndk/guides/abis.html#x86 // for target ABI requirements. @@ -8,8 +8,9 @@ pub(crate) fn target() -> Target { base.max_atomic_width = Some(64); + base.rustc_abi = Some(RustcAbi::X86Sse2); // https://developer.android.com/ndk/guides/abis.html#x86 - base.cpu = "pentiumpro".into(); + base.cpu = "pentium4".into(); base.features = "+mmx,+sse,+sse2,+sse3,+ssse3".into(); base.stack_probes = StackProbeType::Inline; diff --git a/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs index 9ef8e120e0de4..06dbf1e3a1657 100644 --- a/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs @@ -1,7 +1,8 @@ -use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, Target, base}; +use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, RustcAbi, Target, base}; pub(crate) fn target() -> Target { let mut base = base::windows_gnu::opts(); + base.rustc_abi = Some(RustcAbi::X86Sse2); base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); base.frame_pointer = FramePointer::Always; // Required for backtraces @@ -9,11 +10,10 @@ pub(crate) fn target() -> Target { // Mark all dynamic libraries and executables as compatible with the larger 4GiB address // space available to x86 Windows binaries on x86_64. - base.add_pre_link_args(LinkerFlavor::Gnu(Cc::No, Lld::No), &[ - "-m", - "i386pe", - "--large-address-aware", - ]); + base.add_pre_link_args( + LinkerFlavor::Gnu(Cc::No, Lld::No), + &["-m", "i386pe", "--large-address-aware"], + ); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-Wl,--large-address-aware"]); Target { diff --git a/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnullvm.rs b/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnullvm.rs index 13d3c5532ffd4..05d848e039315 100644 --- a/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnullvm.rs +++ b/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnullvm.rs @@ -1,7 +1,8 @@ -use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, Target, base}; +use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, RustcAbi, Target, base}; pub(crate) fn target() -> Target { let mut base = base::windows_gnullvm::opts(); + base.rustc_abi = Some(RustcAbi::X86Sse2); base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); base.frame_pointer = FramePointer::Always; // Required for backtraces @@ -9,11 +10,10 @@ pub(crate) fn target() -> Target { // Mark all dynamic libraries and executables as compatible with the larger 4GiB address // space available to x86 Windows binaries on x86_64. - base.add_pre_link_args(LinkerFlavor::Gnu(Cc::No, Lld::No), &[ - "-m", - "i386pe", - "--large-address-aware", - ]); + base.add_pre_link_args( + LinkerFlavor::Gnu(Cc::No, Lld::No), + &["-m", "i386pe", "--large-address-aware"], + ); Target { llvm_target: "i686-pc-windows-gnu".into(), diff --git a/compiler/rustc_target/src/spec/targets/i686_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/i686_pc_windows_msvc.rs index aacad26686315..e0e471130874c 100644 --- a/compiler/rustc_target/src/spec/targets/i686_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/i686_pc_windows_msvc.rs @@ -1,20 +1,24 @@ -use crate::spec::{LinkerFlavor, Lld, SanitizerSet, Target, base}; +use crate::spec::{LinkerFlavor, Lld, RustcAbi, SanitizerSet, Target, base}; pub(crate) fn target() -> Target { let mut base = base::windows_msvc::opts(); + base.rustc_abi = Some(RustcAbi::X86Sse2); base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); base.supported_sanitizers = SanitizerSet::ADDRESS; - base.add_pre_link_args(LinkerFlavor::Msvc(Lld::No), &[ - // Mark all dynamic libraries and executables as compatible with the larger 4GiB address - // space available to x86 Windows binaries on x86_64. - "/LARGEADDRESSAWARE", - // Ensure the linker will only produce an image if it can also produce a table of - // the image's safe exception handlers. - // https://docs.microsoft.com/en-us/cpp/build/reference/safeseh-image-has-safe-exception-handlers - "/SAFESEH", - ]); + base.add_pre_link_args( + LinkerFlavor::Msvc(Lld::No), + &[ + // Mark all dynamic libraries and executables as compatible with the larger 4GiB address + // space available to x86 Windows binaries on x86_64. + "/LARGEADDRESSAWARE", + // Ensure the linker will only produce an image if it can also produce a table of + // the image's safe exception handlers. + // https://docs.microsoft.com/en-us/cpp/build/reference/safeseh-image-has-safe-exception-handlers + "/SAFESEH", + ], + ); Target { llvm_target: "i686-pc-windows-msvc".into(), diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_freebsd.rs index 85ab87cc07f77..71c26041a50a4 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_freebsd.rs @@ -1,7 +1,8 @@ -use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, base}; +use crate::spec::{Cc, LinkerFlavor, Lld, RustcAbi, StackProbeType, Target, base}; pub(crate) fn target() -> Target { let mut base = base::freebsd::opts(); + base.rustc_abi = Some(RustcAbi::X86Sse2); base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m32", "-Wl,-znotext"]); diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_haiku.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_haiku.rs index 1ab493c787c2a..464ac46b07c0e 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_haiku.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_haiku.rs @@ -1,7 +1,8 @@ -use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, base}; +use crate::spec::{Cc, LinkerFlavor, Lld, RustcAbi, StackProbeType, Target, base}; pub(crate) fn target() -> Target { let mut base = base::haiku::opts(); + base.rustc_abi = Some(RustcAbi::X86Sse2); base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m32"]); diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_hurd_gnu.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_hurd_gnu.rs index 3961656d1b61c..2f93e97d9fc33 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_hurd_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_hurd_gnu.rs @@ -2,7 +2,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, base}; pub(crate) fn target() -> Target { let mut base = base::hurd_gnu::opts(); - base.cpu = "pentiumpro".into(); + base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m32"]); base.stack_probes = StackProbeType::Inline; diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_linux_gnu.rs index c95cb308d7f5c..fe699dbcb5109 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_linux_gnu.rs @@ -1,7 +1,19 @@ -use crate::spec::{Cc, LinkerFlavor, Lld, SanitizerSet, StackProbeType, Target, base}; +use crate::spec::{Cc, LinkerFlavor, Lld, RustcAbi, SanitizerSet, StackProbeType, Target, base}; pub(crate) fn target() -> Target { let mut base = base::linux_gnu::opts(); + base.rustc_abi = Some(RustcAbi::X86Sse2); + // Dear distribution packager, if you are changing the base CPU model with the goal of removing + // the SSE2 requirement, make sure to also set the `rustc_abi` to `None` above or else the compiler + // will complain that the chosen ABI cannot be realized with the given CPU features. + // Also note that x86 without SSE2 is *not* considered a Tier 1 target by the Rust project, and + // it has some known floating-point correctness issues mostly caused by a lack of people caring + // for LLVM's x87 support (double-rounding, value truncation; see + // for details). This can lead to incorrect + // math (Rust generally promises exact math, so this can break code in unexpected ways) and it + // can lead to memory safety violations if floating-point values are used e.g. to access an + // array. If users run into such issues and report bugs upstream and then it turns out that the + // bugs are caused by distribution patches, that leads to confusion and frustration. base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); base.supported_sanitizers = SanitizerSet::ADDRESS; diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs index 6ba87c732b72d..3d25c951e813f 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_linux_musl.rs @@ -1,7 +1,10 @@ -use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, StackProbeType, Target, base}; +use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, RustcAbi, StackProbeType, Target, base}; pub(crate) fn target() -> Target { let mut base = base::linux_musl::opts(); + base.rustc_abi = Some(RustcAbi::X86Sse2); + // If you want to change the base CPU, please see `i686_unknown_linux_gnu.rs` + // for an important comment. base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m32", "-Wl,-melf_i386"]); diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_netbsd.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_netbsd.rs index 092ba5c7baa16..4bc6eee1ac272 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_netbsd.rs @@ -1,7 +1,8 @@ -use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions, base}; +use crate::spec::{Cc, LinkerFlavor, Lld, RustcAbi, StackProbeType, Target, TargetOptions, base}; pub(crate) fn target() -> Target { let mut base = base::netbsd::opts(); + base.rustc_abi = Some(RustcAbi::X86Sse2); base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m32"]); diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_openbsd.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_openbsd.rs index 7ae9189e23393..a3bdf66a14d89 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_openbsd.rs @@ -1,7 +1,8 @@ -use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, base}; +use crate::spec::{Cc, LinkerFlavor, Lld, RustcAbi, StackProbeType, Target, base}; pub(crate) fn target() -> Target { let mut base = base::openbsd::opts(); + base.rustc_abi = Some(RustcAbi::X86Sse2); base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m32", "-fuse-ld=lld"]); diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_redox.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_redox.rs deleted file mode 100644 index bfe52a330d30d..0000000000000 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_redox.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, base}; - -pub(crate) fn target() -> Target { - let mut base = base::redox::opts(); - base.cpu = "pentiumpro".into(); - base.plt_by_default = false; - base.max_atomic_width = Some(64); - base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m32"]); - // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved - base.stack_probes = StackProbeType::Call; - - Target { - llvm_target: "i686-unknown-redox".into(), - metadata: crate::spec::TargetMetadata { - description: None, - tier: None, - host_tools: None, - std: None, - }, - pointer_width: 32, - data_layout: - "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128" - .into(), - arch: "x86".into(), - options: base, - } -} diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_uefi.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_uefi.rs index 39d3a5a46330f..736b91310224c 100644 --- a/compiler/rustc_target/src/spec/targets/i686_unknown_uefi.rs +++ b/compiler/rustc_target/src/spec/targets/i686_unknown_uefi.rs @@ -5,7 +5,7 @@ // The cdecl ABI is used. It differs from the stdcall or fastcall ABI. // "i686-unknown-windows" is used to get the minimal subset of windows-specific features. -use crate::spec::{Target, base}; +use crate::spec::{LinkerFlavor, Lld, RustcAbi, Target, add_link_args, base}; pub(crate) fn target() -> Target { let mut base = base::uefi_msvc::opts(); @@ -22,6 +22,14 @@ pub(crate) fn target() -> Target { // If you initialize FP units yourself, you can override these flags with custom linker // arguments, thus giving you access to full MMX/SSE acceleration. base.features = "-mmx,-sse,+soft-float".into(); + base.rustc_abi = Some(RustcAbi::X86Softfloat); + + // Turn off DWARF. This fixes an lld warning, "section name .debug_frame is longer than 8 + // characters and will use a non-standard string table". That section will not be created if + // DWARF is disabled. + // + // This is only needed in the i686 target due to using the `-gnu` LLVM target (see below). + add_link_args(&mut base.post_link_args, LinkerFlavor::Msvc(Lld::No), &["/DEBUG:NODWARF"]); // Use -GNU here, because of the reason below: // Background and Problem: diff --git a/compiler/rustc_target/src/spec/targets/i686_uwp_windows_gnu.rs b/compiler/rustc_target/src/spec/targets/i686_uwp_windows_gnu.rs index ffb9926e9444b..0e1b65ef598c0 100644 --- a/compiler/rustc_target/src/spec/targets/i686_uwp_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/i686_uwp_windows_gnu.rs @@ -1,18 +1,18 @@ -use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, Target, base}; +use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, RustcAbi, Target, base}; pub(crate) fn target() -> Target { let mut base = base::windows_uwp_gnu::opts(); + base.rustc_abi = Some(RustcAbi::X86Sse2); base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); base.frame_pointer = FramePointer::Always; // Required for backtraces // Mark all dynamic libraries and executables as compatible with the larger 4GiB address // space available to x86 Windows binaries on x86_64. - base.add_pre_link_args(LinkerFlavor::Gnu(Cc::No, Lld::No), &[ - "-m", - "i386pe", - "--large-address-aware", - ]); + base.add_pre_link_args( + LinkerFlavor::Gnu(Cc::No, Lld::No), + &["-m", "i386pe", "--large-address-aware"], + ); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-Wl,--large-address-aware"]); Target { diff --git a/compiler/rustc_target/src/spec/targets/i686_uwp_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/i686_uwp_windows_msvc.rs index e0de9b9af45f2..44cf9b1adda14 100644 --- a/compiler/rustc_target/src/spec/targets/i686_uwp_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/i686_uwp_windows_msvc.rs @@ -1,7 +1,8 @@ -use crate::spec::{Target, base}; +use crate::spec::{RustcAbi, Target, base}; pub(crate) fn target() -> Target { let mut base = base::windows_uwp_msvc::opts(); + base.rustc_abi = Some(RustcAbi::X86Sse2); base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); diff --git a/compiler/rustc_target/src/spec/targets/i686_win7_windows_gnu.rs b/compiler/rustc_target/src/spec/targets/i686_win7_windows_gnu.rs new file mode 100644 index 0000000000000..e9100da14cba2 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/i686_win7_windows_gnu.rs @@ -0,0 +1,35 @@ +use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, RustcAbi, Target, base}; + +pub(crate) fn target() -> Target { + let mut base = base::windows_gnu::opts(); + base.vendor = "win7".into(); + base.rustc_abi = Some(RustcAbi::X86Sse2); + base.cpu = "pentium4".into(); + base.max_atomic_width = Some(64); + base.frame_pointer = FramePointer::Always; // Required for backtraces + base.linker = Some("i686-w64-mingw32-gcc".into()); + + // Mark all dynamic libraries and executables as compatible with the larger 4GiB address + // space available to x86 Windows binaries on x86_64. + base.add_pre_link_args( + LinkerFlavor::Gnu(Cc::No, Lld::No), + &["-m", "i386pe", "--large-address-aware"], + ); + base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-Wl,--large-address-aware"]); + + Target { + llvm_target: "i686-pc-windows-gnu".into(), + metadata: crate::spec::TargetMetadata { + description: Some("32-bit MinGW (Windows 7+)".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }, + pointer_width: 32, + data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ + i64:64-i128:128-f80:32-n8:16:32-a:0:32-S32" + .into(), + arch: "x86".into(), + options: base, + } +} diff --git a/compiler/rustc_target/src/spec/targets/i686_win7_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/i686_win7_windows_msvc.rs index 173977b77bd13..94284a2fe72b2 100644 --- a/compiler/rustc_target/src/spec/targets/i686_win7_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/i686_win7_windows_msvc.rs @@ -1,21 +1,25 @@ -use crate::spec::{LinkerFlavor, Lld, SanitizerSet, Target, base}; +use crate::spec::{LinkerFlavor, Lld, RustcAbi, SanitizerSet, Target, base}; pub(crate) fn target() -> Target { let mut base = base::windows_msvc::opts(); base.vendor = "win7".into(); + base.rustc_abi = Some(RustcAbi::X86Sse2); base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); base.supported_sanitizers = SanitizerSet::ADDRESS; - base.add_pre_link_args(LinkerFlavor::Msvc(Lld::No), &[ - // Mark all dynamic libraries and executables as compatible with the larger 4GiB address - // space available to x86 Windows binaries on x86_64. - "/LARGEADDRESSAWARE", - // Ensure the linker will only produce an image if it can also produce a table of - // the image's safe exception handlers. - // https://docs.microsoft.com/en-us/cpp/build/reference/safeseh-image-has-safe-exception-handlers - "/SAFESEH", - ]); + base.add_pre_link_args( + LinkerFlavor::Msvc(Lld::No), + &[ + // Mark all dynamic libraries and executables as compatible with the larger 4GiB address + // space available to x86 Windows binaries on x86_64. + "/LARGEADDRESSAWARE", + // Ensure the linker will only produce an image if it can also produce a table of + // the image's safe exception handlers. + // https://docs.microsoft.com/en-us/cpp/build/reference/safeseh-image-has-safe-exception-handlers + "/SAFESEH", + ], + ); Target { llvm_target: "i686-pc-windows-msvc".into(), diff --git a/compiler/rustc_target/src/spec/targets/i686_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/i686_wrs_vxworks.rs index dfffa6e138f85..4e801955d0cb1 100644 --- a/compiler/rustc_target/src/spec/targets/i686_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/targets/i686_wrs_vxworks.rs @@ -1,7 +1,8 @@ -use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, base}; +use crate::spec::{Cc, LinkerFlavor, Lld, RustcAbi, StackProbeType, Target, base}; pub(crate) fn target() -> Target { let mut base = base::vxworks::opts(); + base.rustc_abi = Some(RustcAbi::X86Sse2); base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m32"]); diff --git a/compiler/rustc_target/src/spec/targets/m68k_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/m68k_unknown_linux_gnu.rs index 47c6d80548359..d3584a1be743a 100644 --- a/compiler/rustc_target/src/spec/targets/m68k_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/m68k_unknown_linux_gnu.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{LinkSelfContainedDefault, Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/m68k_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/m68k_unknown_none_elf.rs new file mode 100644 index 0000000000000..8b8693b55c5cc --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/m68k_unknown_none_elf.rs @@ -0,0 +1,34 @@ +use rustc_abi::Endian; + +use crate::spec::{CodeModel, PanicStrategy, RelocModel, Target, TargetOptions}; + +pub(crate) fn target() -> Target { + let options = TargetOptions { + cpu: "M68010".into(), + max_atomic_width: None, + endian: Endian::Big, + // LLD currently does not have support for M68k + linker: Some("m68k-linux-gnu-ld".into()), + panic_strategy: PanicStrategy::Abort, + code_model: Some(CodeModel::Medium), + has_rpath: false, + // should be soft-float + llvm_floatabi: None, + relocation_model: RelocModel::Static, + ..Default::default() + }; + + Target { + llvm_target: "m68k".into(), + metadata: crate::spec::TargetMetadata { + description: Some("Motorola 680x0".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(false), + }, + pointer_width: 32, + data_layout: "E-m:e-p:32:16:32-i8:8:8-i16:16:16-i32:16:32-n8:16:32-a:0:16-S16".into(), + arch: "m68k".into(), + options, + } +} diff --git a/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs b/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs index bddcc457498a4..4880e993722f4 100644 --- a/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs @@ -1,6 +1,7 @@ //! A target tuple for OpenWrt MIPS64 targets. -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_gnuabi64.rs index 75da4abc6b603..daf6d5de4c031 100644 --- a/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_gnuabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_gnuabi64.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs index 32f5c79d653d2..03c8fa9245046 100644 --- a/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/mips_mti_none_elf.rs b/compiler/rustc_target/src/spec/targets/mips_mti_none_elf.rs new file mode 100644 index 0000000000000..2815d995c315b --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/mips_mti_none_elf.rs @@ -0,0 +1,37 @@ +use rustc_abi::Endian; + +use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; + +pub(crate) fn target() -> Target { + Target { + data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".into(), + llvm_target: "mips".into(), + metadata: crate::spec::TargetMetadata { + description: Some("MIPS32r2 BE Baremetal Softfloat".into()), + tier: Some(3), + host_tools: Some(false), + std: None, // ? + }, + pointer_width: 32, + arch: "mips".into(), + + options: TargetOptions { + vendor: "mti".into(), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + endian: Endian::Big, + cpu: "mips32r2".into(), + + max_atomic_width: Some(32), + + features: "+mips32r2,+soft-float,+noabicalls".into(), + executables: true, + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + emit_debug_gdb_scripts: false, + eh_frame_header: false, + singlethread: true, + ..Default::default() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/mips_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/mips_unknown_linux_gnu.rs index b9dc0d4a6ceb0..19cf62d19f6e5 100644 --- a/compiler/rustc_target/src/spec/targets/mips_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/mips_unknown_linux_gnu.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/mips_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/mips_unknown_linux_musl.rs index 5076ae345a97e..dbca90ba569c0 100644 --- a/compiler/rustc_target/src/spec/targets/mips_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/mips_unknown_linux_musl.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/mips_unknown_linux_uclibc.rs b/compiler/rustc_target/src/spec/targets/mips_unknown_linux_uclibc.rs index 36e61d55fa066..c26c7867ec7aa 100644 --- a/compiler/rustc_target/src/spec/targets/mips_unknown_linux_uclibc.rs +++ b/compiler/rustc_target/src/spec/targets/mips_unknown_linux_uclibc.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/mipsel_mti_none_elf.rs b/compiler/rustc_target/src/spec/targets/mipsel_mti_none_elf.rs new file mode 100644 index 0000000000000..f532643b56aee --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/mipsel_mti_none_elf.rs @@ -0,0 +1,37 @@ +use rustc_abi::Endian; + +use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; + +pub(crate) fn target() -> Target { + Target { + data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".into(), + llvm_target: "mipsel".into(), + metadata: crate::spec::TargetMetadata { + description: Some("MIPS32r2 LE Baremetal Softfloat".into()), + tier: Some(3), + host_tools: Some(false), + std: None, // ? + }, + pointer_width: 32, + arch: "mips".into(), + + options: TargetOptions { + vendor: "mti".into(), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + endian: Endian::Little, + cpu: "mips32r2".into(), + + max_atomic_width: Some(32), + + features: "+mips32r2,+soft-float,+noabicalls".into(), + executables: true, + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + emit_debug_gdb_scripts: false, + eh_frame_header: false, + singlethread: true, + ..Default::default() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/mipsel_sony_psp.rs b/compiler/rustc_target/src/spec/targets/mipsel_sony_psp.rs index bf51d86437212..2c63b0f2f3845 100644 --- a/compiler/rustc_target/src/spec/targets/mipsel_sony_psp.rs +++ b/compiler/rustc_target/src/spec/targets/mipsel_sony_psp.rs @@ -4,10 +4,10 @@ use crate::spec::{Cc, LinkerFlavor, Lld, RelocModel, Target, TargetOptions, cvs} const LINKER_SCRIPT: &str = include_str!("./mipsel_sony_psp_linker_script.ld"); pub(crate) fn target() -> Target { - let pre_link_args = TargetOptions::link_args(LinkerFlavor::Gnu(Cc::No, Lld::No), &[ - "--emit-relocs", - "--nmagic", - ]); + let pre_link_args = TargetOptions::link_args( + LinkerFlavor::Gnu(Cc::No, Lld::No), + &["--emit-relocs", "--nmagic"], + ); Target { llvm_target: "mipsel-sony-psp".into(), diff --git a/compiler/rustc_target/src/spec/targets/mipsel_unknown_netbsd.rs b/compiler/rustc_target/src/spec/targets/mipsel_unknown_netbsd.rs index e1adeb98ec213..400e05f147837 100644 --- a/compiler/rustc_target/src/spec/targets/mipsel_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/targets/mipsel_unknown_netbsd.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/mipsisa32r6_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/mipsisa32r6_unknown_linux_gnu.rs index 3ef0988d23570..c7b0a05d8899c 100644 --- a/compiler/rustc_target/src/spec/targets/mipsisa32r6_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/mipsisa32r6_unknown_linux_gnu.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/mipsisa64r6_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/targets/mipsisa64r6_unknown_linux_gnuabi64.rs index d0d0ed1acc3f0..49bec90022c54 100644 --- a/compiler/rustc_target/src/spec/targets/mipsisa64r6_unknown_linux_gnuabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mipsisa64r6_unknown_linux_gnuabi64.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_ibm_aix.rs b/compiler/rustc_target/src/spec/targets/powerpc64_ibm_aix.rs index edabbbf5f005b..2125c95e2664c 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_ibm_aix.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_ibm_aix.rs @@ -3,12 +3,10 @@ use crate::spec::{Cc, LinkerFlavor, Target, base}; pub(crate) fn target() -> Target { let mut base = base::aix::opts(); base.max_atomic_width = Some(64); - base.add_pre_link_args(LinkerFlavor::Unix(Cc::No), &[ - "-b64", - "-bpT:0x100000000", - "-bpD:0x110000000", - "-bcdtors:all:0:s", - ]); + base.add_pre_link_args( + LinkerFlavor::Unix(Cc::No), + &["-b64", "-bpT:0x100000000", "-bpD:0x110000000", "-bcdtors:all:0:s"], + ); Target { llvm_target: "powerpc64-ibm-aix".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs index 4ccb3ee466405..201ab0d534f50 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnu.rs index 351ffa65eba8e..1a2f212594295 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnu.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs index a54b17c87a7db..417c8b63c86ba 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs index 31fbb14152407..15947fdb0ac2d 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs index c37aa8d502ad7..d48c2f5e744b4 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/powerpc_unknown_freebsd.rs index 9cfc2153ac170..d6a84f55f5eea 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_unknown_freebsd.rs @@ -1,13 +1,14 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions, base}; pub(crate) fn target() -> Target { let mut base = base::freebsd::opts(); // Extra hint to linker that we are generating secure-PLT code. - base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &[ - "-m32", - "--target=powerpc-unknown-freebsd13.0", - ]); + base.add_pre_link_args( + LinkerFlavor::Gnu(Cc::Yes, Lld::No), + &["-m32", "--target=powerpc-unknown-freebsd13.0"], + ); base.max_atomic_width = Some(32); base.stack_probes = StackProbeType::Inline; diff --git a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnu.rs index 1ae879d01bd46..6f3a2baf40564 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnu.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions, base}; pub(crate) fn target() -> Target { @@ -18,6 +19,11 @@ pub(crate) fn target() -> Target { pointer_width: 32, data_layout: "E-m:e-p:32:32-Fn32-i64:64-n32".into(), arch: "powerpc".into(), - options: TargetOptions { endian: Endian::Big, mcount: "_mcount".into(), ..base }, + options: TargetOptions { + endian: Endian::Big, + features: "+secure-plt".into(), + mcount: "_mcount".into(), + ..base + }, } } diff --git a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnuspe.rs b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnuspe.rs index 4a5ab375c7307..c4d894823e6af 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnuspe.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnuspe.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions, base}; pub(crate) fn target() -> Target { @@ -21,6 +22,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { abi: "spe".into(), endian: Endian::Big, + features: "+secure-plt".into(), mcount: "_mcount".into(), ..base }, diff --git a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs index 0cd0ea96ad377..5b5fea666bb9e 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_musl.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_muslspe.rs b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_muslspe.rs index b86c3c2e8e042..dfd99635ddd0e 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_muslspe.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_muslspe.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc_unknown_netbsd.rs b/compiler/rustc_target/src/spec/targets/powerpc_unknown_netbsd.rs index 142443aa5b159..7492077bb88bd 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_unknown_netbsd.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc_unknown_openbsd.rs b/compiler/rustc_target/src/spec/targets/powerpc_unknown_openbsd.rs index 7aedc2d0665cb..dd82a6a71cda1 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_unknown_openbsd.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{StackProbeType, Target, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/powerpc_wrs_vxworks.rs index 22b38208b108a..c4f93c60d3ea6 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_wrs_vxworks.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc_wrs_vxworks_spe.rs b/compiler/rustc_target/src/spec/targets/powerpc_wrs_vxworks_spe.rs index ee61a7da41c9c..0284c50bd6501 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_wrs_vxworks_spe.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_wrs_vxworks_spe.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs index b1f52973c107f..771ffac7d804b 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs @@ -1,6 +1,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; pub(crate) fn target() -> Target { + let abi = "ilp32e"; Target { // The below `data_layout` is explicitly specified by the ilp32e ABI in LLVM. See also // `options.llvm_abiname`. @@ -16,11 +17,12 @@ pub(crate) fn target() -> Target { arch: "riscv32".into(), options: TargetOptions { + abi: abi.into(), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), cpu: "generic-rv32".into(), // The ilp32e ABI specifies the `data_layout` - llvm_abiname: "ilp32e".into(), + llvm_abiname: abi.into(), max_atomic_width: Some(32), atomic_cas: false, features: "+e,+forced-atomics".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32em_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32em_unknown_none_elf.rs index feeaa48778d43..3b81c278d3afd 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32em_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32em_unknown_none_elf.rs @@ -1,6 +1,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; pub(crate) fn target() -> Target { + let abi = "ilp32e"; Target { // The below `data_layout` is explicitly specified by the ilp32e ABI in LLVM. See also // `options.llvm_abiname`. @@ -16,11 +17,12 @@ pub(crate) fn target() -> Target { arch: "riscv32".into(), options: TargetOptions { + abi: abi.into(), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), cpu: "generic-rv32".into(), // The ilp32e ABI specifies the `data_layout` - llvm_abiname: "ilp32e".into(), + llvm_abiname: abi.into(), max_atomic_width: Some(32), atomic_cas: false, features: "+e,+m,+forced-atomics".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32emc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32emc_unknown_none_elf.rs index 45d73c1323371..c18b51ad46e87 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32emc_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32emc_unknown_none_elf.rs @@ -1,6 +1,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; pub(crate) fn target() -> Target { + let abi = "ilp32e"; Target { // The below `data_layout` is explicitly specified by the ilp32e ABI in LLVM. See also // `options.llvm_abiname`. @@ -16,11 +17,12 @@ pub(crate) fn target() -> Target { arch: "riscv32".into(), options: TargetOptions { + abi: abi.into(), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), cpu: "generic-rv32".into(), // The ilp32e ABI specifies the `data_layout` - llvm_abiname: "ilp32e".into(), + llvm_abiname: abi.into(), max_atomic_width: Some(32), atomic_cas: false, features: "+e,+m,+c,+forced-atomics".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_nuttx_elf.rs index 31c9180c509ce..3eb3d18faf44b 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_nuttx_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_nuttx_elf.rs @@ -6,9 +6,9 @@ pub(crate) fn target() -> Target { llvm_target: "riscv32".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, arch: "riscv32".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32imafc_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32imafc_unknown_nuttx_elf.rs index 08dd3cc2a09de..7864f7f8f9a36 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32imafc_unknown_nuttx_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32imafc_unknown_nuttx_elf.rs @@ -6,9 +6,9 @@ pub(crate) fn target() -> Target { llvm_target: "riscv32".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, arch: "riscv32".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_nuttx_elf.rs index e86549806ddbf..60d8ec576af69 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_nuttx_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_nuttx_elf.rs @@ -6,9 +6,9 @@ pub(crate) fn target() -> Target { llvm_target: "riscv32".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, arch: "riscv32".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs index c389759aecd2c..2cbb8c19b849e 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs @@ -8,9 +8,9 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, llvm_target: "riscv64".into(), pointer_width: 64, diff --git a/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_nuttx_elf.rs index 9c18166558115..306b23d278761 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_nuttx_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_nuttx_elf.rs @@ -8,9 +8,9 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, llvm_target: "riscv64".into(), pointer_width: 64, diff --git a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs index a84a18a433ffc..41aa400f3e094 100644 --- a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{SanitizerSet, StackProbeType, Target, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs index fbe8c48eca72c..61c01eba70d40 100644 --- a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{SanitizerSet, StackProbeType, Target, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/sparc64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/sparc64_unknown_linux_gnu.rs index ac2141f9b086c..3317fe51c334c 100644 --- a/compiler/rustc_target/src/spec/targets/sparc64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/sparc64_unknown_linux_gnu.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Target, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/sparc64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/targets/sparc64_unknown_netbsd.rs index d16b3776cfb14..4c2b971c0f138 100644 --- a/compiler/rustc_target/src/spec/targets/sparc64_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/targets/sparc64_unknown_netbsd.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Cc, LinkerFlavor, Lld, Target, TargetOptions, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/sparc64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/targets/sparc64_unknown_openbsd.rs index 91e64061020ff..81d7bf9a62842 100644 --- a/compiler/rustc_target/src/spec/targets/sparc64_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/targets/sparc64_unknown_openbsd.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Cc, LinkerFlavor, Lld, Target, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/sparc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/sparc_unknown_linux_gnu.rs index 45941cd7c0164..6abef79be96a0 100644 --- a/compiler/rustc_target/src/spec/targets/sparc_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/sparc_unknown_linux_gnu.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Cc, LinkerFlavor, Lld, Target, TargetOptions, base}; pub(crate) fn target() -> Target { @@ -17,9 +18,10 @@ pub(crate) fn target() -> Target { features: "+v8plus".into(), cpu: "v9".into(), endian: Endian::Big, - late_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &[ - "-mcpu=v9", "-m32", - ]), + late_link_args: TargetOptions::link_args( + LinkerFlavor::Gnu(Cc::Yes, Lld::No), + &["-mcpu=v9", "-m32"], + ), max_atomic_width: Some(32), ..base::linux_gnu::opts() }, diff --git a/compiler/rustc_target/src/spec/targets/sparc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/sparc_unknown_none_elf.rs index 987f69429c069..00cd7438f7ec4 100644 --- a/compiler/rustc_target/src/spec/targets/sparc_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/sparc_unknown_none_elf.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/sparcv9_sun_solaris.rs b/compiler/rustc_target/src/spec/targets/sparcv9_sun_solaris.rs index 770da60da9e00..5b7d560a8e09c 100644 --- a/compiler/rustc_target/src/spec/targets/sparcv9_sun_solaris.rs +++ b/compiler/rustc_target/src/spec/targets/sparcv9_sun_solaris.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::{Cc, LinkerFlavor, Target, base}; pub(crate) fn target() -> Target { diff --git a/compiler/rustc_target/src/spec/targets/thumbv6m_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv6m_nuttx_eabi.rs index 5799bbf551f8b..dcf98acc41f8a 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv6m_nuttx_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv6m_nuttx_eabi.rs @@ -7,9 +7,9 @@ pub(crate) fn target() -> Target { llvm_target: "thumbv6m-none-eabi".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), @@ -22,12 +22,9 @@ pub(crate) fn target() -> Target { llvm_floatabi: Some(FloatAbi::Soft), // The ARMv6-M architecture doesn't support unaligned loads/stores so we disable them // with +strict-align. - // Also force-enable 32-bit atomics, which allows the use of atomic load/store only. - // The resulting atomics are ABI incompatible with atomics backed by libatomic. - features: "+strict-align,+atomics-32".into(), - // There are no atomic CAS instructions available in the instruction set of the ARMv6-M - // architecture - atomic_cas: false, + // The ARMv6-M doesn't support hardware atomic operations, use atomic builtins instead. + features: "+strict-align".into(), + max_atomic_width: Some(32), ..base::thumb::opts() }, } diff --git a/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabi.rs new file mode 100644 index 0000000000000..b5cb393f4b001 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabi.rs @@ -0,0 +1,33 @@ +// Targets Cortex-A7/A8/A9 processors (ARMv7-A) +// +// This target assumes that the device does NOT have a FPU (Floating Point Unit) +// and will use software floating point operations. This matches the NuttX EABI +// configuration without hardware floating point support. + +use crate::spec::{FloatAbi, Target, TargetOptions, base, cvs}; + +pub(crate) fn target() -> Target { + Target { + llvm_target: "thumbv7a-none-eabi".into(), + metadata: crate::spec::TargetMetadata { + description: None, + tier: Some(3), + host_tools: None, + std: Some(true), + }, + pointer_width: 32, + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), + arch: "arm".into(), + + options: TargetOptions { + families: cvs!["unix"], + os: "nuttx".into(), + abi: "eabi".into(), + llvm_floatabi: Some(FloatAbi::Soft), + // Cortex-A7/A8/A9 with software floating point + features: "+soft-float,-neon".into(), + max_atomic_width: Some(64), + ..base::thumb::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabihf.rs new file mode 100644 index 0000000000000..1aa44a8cc939f --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabihf.rs @@ -0,0 +1,37 @@ +// Targets Cortex-A7/A8/A9 processors (ARMv7-A) +// +// This target assumes that the device has a FPU (Floating Point Unit) and lowers all (single +// precision) floating point operations to hardware instructions. Cortex-A7/A8/A9 processors +// support VFPv3-D32 or VFPv4-D32 floating point units with optional double-precision support. +// +// This target uses the "hard" floating convention (ABI) where floating point values +// are passed to/from subroutines via FPU registers (S0, S1, D0, D1, etc.). + +use crate::spec::{FloatAbi, Target, TargetOptions, base, cvs}; + +pub(crate) fn target() -> Target { + Target { + llvm_target: "thumbv7a-none-eabihf".into(), + metadata: crate::spec::TargetMetadata { + description: None, + tier: Some(3), + host_tools: None, + std: Some(true), + }, + pointer_width: 32, + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), + arch: "arm".into(), + + options: TargetOptions { + families: cvs!["unix"], + os: "nuttx".into(), + abi: "eabihf".into(), + llvm_floatabi: Some(FloatAbi::Hard), + // Cortex-A7/A8/A9 support VFPv3-D32/VFPv4-D32 with optional double-precision + // and NEON SIMD instructions + features: "+vfp3,+neon".into(), + max_atomic_width: Some(64), + ..base::thumb::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabi.rs index 536d128590fb4..a3bc4013e530a 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabi.rs @@ -16,9 +16,9 @@ pub(crate) fn target() -> Target { llvm_target: "thumbv7em-none-eabi".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabihf.rs index 35e92b81d87da..14bbe38257d3f 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabihf.rs @@ -15,9 +15,9 @@ pub(crate) fn target() -> Target { llvm_target: "thumbv7em-none-eabihf".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/thumbv7m_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv7m_nuttx_eabi.rs index 320867444ad29..2a77f48a9cd34 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7m_nuttx_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7m_nuttx_eabi.rs @@ -7,9 +7,9 @@ pub(crate) fn target() -> Target { llvm_target: "thumbv7m-none-eabi".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/thumbv8m_base_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv8m_base_nuttx_eabi.rs index 1af01b97666fe..25a100e9c7e56 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv8m_base_nuttx_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv8m_base_nuttx_eabi.rs @@ -7,9 +7,9 @@ pub(crate) fn target() -> Target { llvm_target: "thumbv8m.base-none-eabi".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabi.rs index 661d74217adf6..0bfe2b32ad4a5 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabi.rs @@ -8,9 +8,9 @@ pub(crate) fn target() -> Target { llvm_target: "thumbv8m.main-none-eabi".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabihf.rs index 484d35bfc2028..9f75f23aa93e1 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabihf.rs @@ -8,9 +8,9 @@ pub(crate) fn target() -> Target { llvm_target: "thumbv8m.main-none-eabihf".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/wasm32_unknown_unknown.rs b/compiler/rustc_target/src/spec/targets/wasm32_unknown_unknown.rs index 96237f208917c..bcf7b69fe74a1 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_unknown_unknown.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_unknown_unknown.rs @@ -16,17 +16,23 @@ pub(crate) fn target() -> Target { let mut options = base::wasm::options(); options.os = "unknown".into(); - options.add_pre_link_args(LinkerFlavor::WasmLld(Cc::No), &[ - // For now this target just never has an entry symbol no matter the output - // type, so unconditionally pass this. - "--no-entry", - ]); - options.add_pre_link_args(LinkerFlavor::WasmLld(Cc::Yes), &[ - // Make sure clang uses LLD as its linker and is configured appropriately - // otherwise - "--target=wasm32-unknown-unknown", - "-Wl,--no-entry", - ]); + options.add_pre_link_args( + LinkerFlavor::WasmLld(Cc::No), + &[ + // For now this target just never has an entry symbol no matter the output + // type, so unconditionally pass this. + "--no-entry", + ], + ); + options.add_pre_link_args( + LinkerFlavor::WasmLld(Cc::Yes), + &[ + // Make sure clang uses LLD as its linker and is configured appropriately + // otherwise + "--target=wasm32-unknown-unknown", + "-Wl,--no-entry", + ], + ); Target { llvm_target: "wasm32-unknown-unknown".into(), diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs b/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs index 0c2e2bfeda6d6..2411d386f52f3 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs @@ -15,17 +15,19 @@ pub(crate) fn target() -> Target { options.os = "wasi".into(); options.env = "p1".into(); - options.add_pre_link_args(LinkerFlavor::WasmLld(Cc::No), &[ - "--import-memory", - "--export-memory", - "--shared-memory", - ]); - options.add_pre_link_args(LinkerFlavor::WasmLld(Cc::Yes), &[ - "--target=wasm32-wasip1-threads", - "-Wl,--import-memory", - "-Wl,--export-memory,", - "-Wl,--shared-memory", - ]); + options.add_pre_link_args( + LinkerFlavor::WasmLld(Cc::No), + &["--import-memory", "--export-memory", "--shared-memory"], + ); + options.add_pre_link_args( + LinkerFlavor::WasmLld(Cc::Yes), + &[ + "--target=wasm32-wasip1-threads", + "-Wl,--import-memory", + "-Wl,--export-memory,", + "-Wl,--shared-memory", + ], + ); options.pre_link_objects_self_contained = crt_objects::pre_wasi_self_contained(); options.post_link_objects_self_contained = crt_objects::post_wasi_self_contained(); diff --git a/compiler/rustc_target/src/spec/targets/wasm32v1_none.rs b/compiler/rustc_target/src/spec/targets/wasm32v1_none.rs index 5c35e9c21d3c1..ab1dc21d125f4 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32v1_none.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32v1_none.rs @@ -23,17 +23,23 @@ pub(crate) fn target() -> Target { options.cpu = "mvp".into(); options.features = "+mutable-globals".into(); - options.add_pre_link_args(LinkerFlavor::WasmLld(Cc::No), &[ - // For now this target just never has an entry symbol no matter the output - // type, so unconditionally pass this. - "--no-entry", - ]); - options.add_pre_link_args(LinkerFlavor::WasmLld(Cc::Yes), &[ - // Make sure clang uses LLD as its linker and is configured appropriately - // otherwise - "--target=wasm32-unknown-unknown", - "-Wl,--no-entry", - ]); + options.add_pre_link_args( + LinkerFlavor::WasmLld(Cc::No), + &[ + // For now this target just never has an entry symbol no matter the output + // type, so unconditionally pass this. + "--no-entry", + ], + ); + options.add_pre_link_args( + LinkerFlavor::WasmLld(Cc::Yes), + &[ + // Make sure clang uses LLD as its linker and is configured appropriately + // otherwise + "--target=wasm32-unknown-unknown", + "-Wl,--no-entry", + ], + ); Target { llvm_target: "wasm32-unknown-unknown".into(), diff --git a/compiler/rustc_target/src/spec/targets/wasm64_unknown_unknown.rs b/compiler/rustc_target/src/spec/targets/wasm64_unknown_unknown.rs index 22fa26d3cdbd9..596e26d1e9da1 100644 --- a/compiler/rustc_target/src/spec/targets/wasm64_unknown_unknown.rs +++ b/compiler/rustc_target/src/spec/targets/wasm64_unknown_unknown.rs @@ -13,18 +13,24 @@ pub(crate) fn target() -> Target { let mut options = base::wasm::options(); options.os = "unknown".into(); - options.add_pre_link_args(LinkerFlavor::WasmLld(Cc::No), &[ - // For now this target just never has an entry symbol no matter the output - // type, so unconditionally pass this. - "--no-entry", - "-mwasm64", - ]); - options.add_pre_link_args(LinkerFlavor::WasmLld(Cc::Yes), &[ - // Make sure clang uses LLD as its linker and is configured appropriately - // otherwise - "--target=wasm64-unknown-unknown", - "-Wl,--no-entry", - ]); + options.add_pre_link_args( + LinkerFlavor::WasmLld(Cc::No), + &[ + // For now this target just never has an entry symbol no matter the output + // type, so unconditionally pass this. + "--no-entry", + "-mwasm64", + ], + ); + options.add_pre_link_args( + LinkerFlavor::WasmLld(Cc::Yes), + &[ + // Make sure clang uses LLD as its linker and is configured appropriately + // otherwise + "--target=wasm64-unknown-unknown", + "-Wl,--no-entry", + ], + ); // Any engine that implements wasm64 will surely implement the rest of these // features since they were all merged into the official spec by the time diff --git a/compiler/rustc_target/src/spec/targets/x86_64_fortanix_unknown_sgx.rs b/compiler/rustc_target/src/spec/targets/x86_64_fortanix_unknown_sgx.rs index 65c3163463093..60d078371bb67 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_fortanix_unknown_sgx.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_fortanix_unknown_sgx.rs @@ -3,37 +3,40 @@ use std::borrow::Cow; use crate::spec::{Cc, LinkerFlavor, Lld, Target, TargetOptions, cvs}; pub(crate) fn target() -> Target { - let pre_link_args = TargetOptions::link_args(LinkerFlavor::Gnu(Cc::No, Lld::No), &[ - "-e", - "elf_entry", - "-Bstatic", - "--gc-sections", - "-z", - "text", - "-z", - "norelro", - "--no-undefined", - "--error-unresolved-symbols", - "--no-undefined-version", - "-Bsymbolic", - "--export-dynamic", - // The following symbols are needed by libunwind, which is linked after - // libstd. Make sure they're included in the link. - "-u", - "__rust_abort", - "-u", - "__rust_c_alloc", - "-u", - "__rust_c_dealloc", - "-u", - "__rust_print_err", - "-u", - "__rust_rwlock_rdlock", - "-u", - "__rust_rwlock_unlock", - "-u", - "__rust_rwlock_wrlock", - ]); + let pre_link_args = TargetOptions::link_args( + LinkerFlavor::Gnu(Cc::No, Lld::No), + &[ + "-e", + "elf_entry", + "-Bstatic", + "--gc-sections", + "-z", + "text", + "-z", + "norelro", + "--no-undefined", + "--error-unresolved-symbols", + "--no-undefined-version", + "-Bsymbolic", + "--export-dynamic", + // The following symbols are needed by libunwind, which is linked after + // libstd. Make sure they're included in the link. + "-u", + "__rust_abort", + "-u", + "__rust_c_alloc", + "-u", + "__rust_c_dealloc", + "-u", + "__rust_print_err", + "-u", + "__rust_rwlock_rdlock", + "-u", + "__rust_rwlock_unlock", + "-u", + "__rust_rwlock_wrlock", + ], + ); const EXPORT_SYMBOLS: &[&str] = &[ "sgx_entry", diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_cygwin.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_cygwin.rs new file mode 100644 index 0000000000000..8da4fe6b8b152 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_cygwin.rs @@ -0,0 +1,24 @@ +use crate::spec::{Cc, LinkerFlavor, Lld, Target, base}; + +pub(crate) fn target() -> Target { + let mut base = base::cygwin::opts(); + base.cpu = "x86-64".into(); + base.add_pre_link_args(LinkerFlavor::Gnu(Cc::No, Lld::No), &["-m", "i386pep"]); + base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); + base.max_atomic_width = Some(64); + base.linker = Some("x86_64-pc-cygwin-gcc".into()); + Target { + llvm_target: "x86_64-pc-cygwin".into(), + pointer_width: 64, + data_layout: + "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), + arch: "x86_64".into(), + options: base, + metadata: crate::spec::TargetMetadata { + description: Some("64-bit x86 Cygwin".into()), + tier: Some(3), + host_tools: Some(false), + std: None, + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710.rs index 245a5f0676547..248aa91862c92 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710.rs @@ -1,28 +1,12 @@ -use crate::spec::{Cc, LinkerFlavor, Lld, Target, TargetOptions, base}; +use crate::spec::Target; +use crate::spec::base::nto_qnx; pub(crate) fn target() -> Target { - Target { - llvm_target: "x86_64-pc-unknown".into(), - metadata: crate::spec::TargetMetadata { - description: Some("x86 64-bit QNX Neutrino 7.1 RTOS".into()), - tier: Some(3), - host_tools: Some(false), - std: Some(true), - }, - pointer_width: 64, - data_layout: - "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), - arch: "x86_64".into(), - options: TargetOptions { - cpu: "x86-64".into(), - plt_by_default: false, - max_atomic_width: Some(64), - pre_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &[ - "-Vgcc_ntox86_64_cxx", - ]), - env: "nto71".into(), - vendor: "pc".into(), - ..base::nto_qnx::opts() - }, - } + let mut target = nto_qnx::x86_64(); + target.metadata.description = + Some("x86 64-bit QNX Neutrino 7.1 RTOS with io-pkt network stack".into()); + target.options.pre_link_args = + nto_qnx::pre_link_args(nto_qnx::ApiVariant::Default, nto_qnx::Arch::X86_64); + target.options.env = "nto71".into(); + target } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710_iosock.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710_iosock.rs new file mode 100644 index 0000000000000..8f4c4924a295d --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710_iosock.rs @@ -0,0 +1,12 @@ +use crate::spec::Target; +use crate::spec::base::nto_qnx; + +pub(crate) fn target() -> Target { + let mut target = nto_qnx::x86_64(); + target.metadata.description = + Some("x86 64-bit QNX Neutrino 7.1 RTOS with io-sock network stack".into()); + target.options.pre_link_args = + nto_qnx::pre_link_args(nto_qnx::ApiVariant::IoSock, nto_qnx::Arch::X86_64); + target.options.env = "nto71_iosock".into(); + target +} diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx800.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx800.rs new file mode 100644 index 0000000000000..d91a94a2ba55a --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx800.rs @@ -0,0 +1,11 @@ +use crate::spec::Target; +use crate::spec::base::nto_qnx; + +pub(crate) fn target() -> Target { + let mut target = nto_qnx::x86_64(); + target.metadata.description = Some("x86 64-bit QNX Neutrino 8.0 RTOS".into()); + target.options.pre_link_args = + nto_qnx::pre_link_args(nto_qnx::ApiVariant::Default, nto_qnx::Arch::X86_64); + target.options.env = "nto80".into(); + target +} diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnu.rs index e0ffcaf5a72b6..b10d4478d5ecf 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnu.rs @@ -6,11 +6,10 @@ pub(crate) fn target() -> Target { base.features = "+cx16,+sse3,+sahf".into(); base.plt_by_default = false; // Use high-entropy 64 bit address space for ASLR - base.add_pre_link_args(LinkerFlavor::Gnu(Cc::No, Lld::No), &[ - "-m", - "i386pep", - "--high-entropy-va", - ]); + base.add_pre_link_args( + LinkerFlavor::Gnu(Cc::No, Lld::No), + &["-m", "i386pep", "--high-entropy-va"], + ); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64", "-Wl,--high-entropy-va"]); base.max_atomic_width = Some(128); base.linker = Some("x86_64-w64-mingw32-gcc".into()); diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_none.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_none.rs index cfefb1de99387..e14a367358944 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_none.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_none.rs @@ -5,8 +5,8 @@ // features. use crate::spec::{ - Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy, RelroLevel, SanitizerSet, StackProbeType, - Target, TargetOptions, + Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy, RelroLevel, RustcAbi, SanitizerSet, + StackProbeType, Target, TargetOptions, }; pub(crate) fn target() -> Target { @@ -20,6 +20,7 @@ pub(crate) fn target() -> Target { relro_level: RelroLevel::Full, linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), + rustc_abi: Some(RustcAbi::X86Softfloat), features: "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,+soft-float".into(), supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS, disable_redzone: true, diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_uefi.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_uefi.rs index a11a79ff41a8e..bce6aa0ebc6bc 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_uefi.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_uefi.rs @@ -5,8 +5,8 @@ // The win64 ABI is used. It differs from the sysv64 ABI, so we must use a windows target with // LLVM. "x86_64-unknown-windows" is used to get the minimal subset of windows-specific features. -use crate::abi::call::Conv; -use crate::spec::{Target, base}; +use crate::callconv::Conv; +use crate::spec::{RustcAbi, Target, base}; pub(crate) fn target() -> Target { let mut base = base::uefi_msvc::opts(); @@ -26,6 +26,7 @@ pub(crate) fn target() -> Target { // If you initialize FP units yourself, you can override these flags with custom linker // arguments, thus giving you access to full MMX/SSE acceleration. base.features = "-mmx,-sse,+soft-float".into(); + base.rustc_abi = Some(RustcAbi::X86Softfloat); Target { llvm_target: "x86_64-unknown-windows".into(), diff --git a/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_gnu.rs b/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_gnu.rs index fd10bc087efe7..0ef41d315af12 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_uwp_windows_gnu.rs @@ -6,11 +6,10 @@ pub(crate) fn target() -> Target { base.features = "+cx16,+sse3,+sahf".into(); base.plt_by_default = false; // Use high-entropy 64 bit address space for ASLR - base.add_pre_link_args(LinkerFlavor::Gnu(Cc::No, Lld::No), &[ - "-m", - "i386pep", - "--high-entropy-va", - ]); + base.add_pre_link_args( + LinkerFlavor::Gnu(Cc::No, Lld::No), + &["-m", "i386pep", "--high-entropy-va"], + ); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64", "-Wl,--high-entropy-va"]); base.max_atomic_width = Some(128); diff --git a/compiler/rustc_target/src/spec/targets/x86_64_win7_windows_gnu.rs b/compiler/rustc_target/src/spec/targets/x86_64_win7_windows_gnu.rs new file mode 100644 index 0000000000000..8f903934a90fb --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/x86_64_win7_windows_gnu.rs @@ -0,0 +1,31 @@ +use crate::spec::{Cc, LinkerFlavor, Lld, Target, base}; + +pub(crate) fn target() -> Target { + let mut base = base::windows_gnu::opts(); + base.vendor = "win7".into(); + base.cpu = "x86-64".into(); + base.plt_by_default = false; + // Use high-entropy 64 bit address space for ASLR + base.add_pre_link_args( + LinkerFlavor::Gnu(Cc::No, Lld::No), + &["-m", "i386pep", "--high-entropy-va"], + ); + base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64", "-Wl,--high-entropy-va"]); + base.max_atomic_width = Some(64); + base.linker = Some("x86_64-w64-mingw32-gcc".into()); + + Target { + llvm_target: "x86_64-pc-windows-gnu".into(), + metadata: crate::spec::TargetMetadata { + description: Some("64-bit MinGW (Windows 7+)".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }, + pointer_width: 64, + data_layout: + "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), + arch: "x86_64".into(), + options: base, + } +} diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32_espidf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32_espidf.rs index 442523aa39717..b2a7c8551e4ff 100644 --- a/compiler/rustc_target/src/spec/targets/xtensa_esp32_espidf.rs +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32_espidf.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::base::xtensa; use crate::spec::{Target, TargetOptions, cvs}; diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_espidf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_espidf.rs index 353034df6f1b5..4fab2bac8e2d9 100644 --- a/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_espidf.rs +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_espidf.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::base::xtensa; use crate::spec::{Target, TargetOptions, cvs}; diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_espidf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_espidf.rs index a242f451c342c..45d409a509f39 100644 --- a/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_espidf.rs +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_espidf.rs @@ -1,4 +1,5 @@ -use crate::abi::Endian; +use rustc_abi::Endian; + use crate::spec::base::xtensa; use crate::spec::{Target, TargetOptions, cvs}; diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 5372437b0d2ab..bb41d03e87f58 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -5,7 +5,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_span::{Symbol, sym}; -use crate::spec::Target; +use crate::spec::{FloatAbi, RustcAbi, Target}; /// Features that control behaviour of rustc, rather than the codegen. /// These exist globally and are not in the target-specific lists below. @@ -17,60 +17,34 @@ pub const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"]; pub const RUSTC_SPECIAL_FEATURES: &[&str] = &["backchain"]; /// Stability information for target features. -/// `Toggleability` is the type storing whether (un)stable features can be toggled: -/// this is initially a function since it can depend on `Target`, but for stable hashing -/// it needs to be something hashable to we have to make the type generic. -#[derive(Debug, Clone)] -pub enum Stability { +#[derive(Debug, Copy, Clone)] +pub enum Stability { /// This target feature is stable, it can be used in `#[target_feature]` and /// `#[cfg(target_feature)]`. - Stable { - /// When enabling/disabling the feature via `-Ctarget-feature` or `#[target_feature]`, - /// determine if that is allowed. - allow_toggle: Toggleability, - }, + Stable, /// This target feature is unstable. It is only present in `#[cfg(target_feature)]` on /// nightly and using it in `#[target_feature]` requires enabling the given nightly feature. - Unstable { + Unstable( /// This must be a *language* feature, or else rustc will ICE when reporting a missing /// feature gate! - nightly_feature: Symbol, - /// See `Stable::allow_toggle` comment above. - allow_toggle: Toggleability, - }, + Symbol, + ), /// This feature can not be set via `-Ctarget-feature` or `#[target_feature]`, it can only be /// set in the target spec. It is never set in `cfg(target_feature)`. Used in - /// particular for features that change the floating-point ABI. + /// particular for features are actually ABI configuration flags (not all targets are as nice as + /// RISC-V and have an explicit way to set the ABI separate from target features). Forbidden { reason: &'static str }, } +use Stability::*; -/// Returns `Ok` if the toggle is allowed, `Err` with an explanation of not. -/// The `bool` indicates whether the feature is being enabled (`true`) or disabled. -pub type AllowToggleUncomputed = fn(&Target, bool) -> Result<(), &'static str>; - -/// The computed result of whether a feature can be enabled/disabled on the current target. -#[derive(Debug, Clone)] -pub struct AllowToggleComputed { - enable: Result<(), &'static str>, - disable: Result<(), &'static str>, -} - -/// `Stability` where `allow_toggle` has not been computed yet. -pub type StabilityUncomputed = Stability; -/// `Stability` where `allow_toggle` has already been computed. -pub type StabilityComputed = Stability; - -impl> HashStable for Stability { +impl HashStable for Stability { #[inline] fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { std::mem::discriminant(self).hash_stable(hcx, hasher); match self { - Stability::Stable { allow_toggle } => { - allow_toggle.hash_stable(hcx, hasher); - } - Stability::Unstable { nightly_feature, allow_toggle } => { + Stability::Stable => {} + Stability::Unstable(nightly_feature) => { nightly_feature.hash_stable(hcx, hasher); - allow_toggle.hash_stable(hcx, hasher); } Stability::Forbidden { reason } => { reason.hash_stable(hcx, hasher); @@ -79,16 +53,7 @@ impl> HashStable for Stability HashStable for AllowToggleComputed { - #[inline] - fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { - let AllowToggleComputed { enable, disable } = self; - enable.hash_stable(hcx, hasher); - disable.hash_stable(hcx, hasher); - } -} - -impl Stability { +impl Stability { /// Returns whether the feature can be used in `cfg(target_feature)` ever. /// (It might still be nightly-only even if this returns `true`, so make sure to also check /// `requires_nightly`.) @@ -106,59 +71,23 @@ impl Stability { /// - for `cfg(target_feature)`, check `in_cfg` pub fn requires_nightly(&self) -> Option { match *self { - Stability::Unstable { nightly_feature, .. } => Some(nightly_feature), + Stability::Unstable(nightly_feature) => Some(nightly_feature), Stability::Stable { .. } => None, Stability::Forbidden { .. } => panic!("forbidden features should not reach this far"), } } -} -impl StabilityUncomputed { - pub fn compute_toggleability(&self, target: &Target) -> StabilityComputed { - use Stability::*; - let compute = |f: AllowToggleUncomputed| AllowToggleComputed { - enable: f(target, true), - disable: f(target, false), - }; - match *self { - Stable { allow_toggle } => Stable { allow_toggle: compute(allow_toggle) }, - Unstable { nightly_feature, allow_toggle } => { - Unstable { nightly_feature, allow_toggle: compute(allow_toggle) } - } - Forbidden { reason } => Forbidden { reason }, - } - } - - pub fn toggle_allowed(&self, target: &Target, enable: bool) -> Result<(), &'static str> { - use Stability::*; - match *self { - Stable { allow_toggle } => allow_toggle(target, enable), - Unstable { allow_toggle, .. } => allow_toggle(target, enable), - Forbidden { reason } => Err(reason), - } - } -} - -impl StabilityComputed { /// Returns whether the feature may be toggled via `#[target_feature]` or `-Ctarget-feature`. /// (It might still be nightly-only even if this returns `true`, so make sure to also check /// `requires_nightly`.) - pub fn toggle_allowed(&self, enable: bool) -> Result<(), &'static str> { - let allow_toggle = match self { - Stability::Stable { allow_toggle } => allow_toggle, - Stability::Unstable { allow_toggle, .. } => allow_toggle, - Stability::Forbidden { reason } => return Err(reason), - }; - if enable { allow_toggle.enable } else { allow_toggle.disable } + pub fn toggle_allowed(&self) -> Result<(), &'static str> { + match self { + Stability::Forbidden { reason } => Err(reason), + _ => Ok(()), + } } } -// Constructors for the list below, defaulting to "always allow toggle". -const STABLE: StabilityUncomputed = Stability::Stable { allow_toggle: |_target, _enable| Ok(()) }; -const fn unstable(nightly_feature: Symbol) -> StabilityUncomputed { - Stability::Unstable { nightly_feature, allow_toggle: |_target, _enable| Ok(()) } -} - // Here we list target features that rustc "understands": they can be used in `#[target_feature]` // and `#[cfg(target_feature)]`. They also do not trigger any warnings when used with // `-Ctarget-feature`. @@ -179,21 +108,19 @@ const fn unstable(nightly_feature: Symbol) -> StabilityUncomputed { // per-function level, since we would then allow safe calls from functions with `+soft-float` to // functions without that feature! // -// It is important for soundness that features allowed here do *not* change the function call ABI. -// For example, disabling the `x87` feature on x86 changes how scalar floats are passed as -// arguments, so enabling toggling that feature would be unsound. In fact, since `-Ctarget-feature` -// will just allow unknown features (with a warning), we have to explicitly list features that change -// the ABI as `Forbidden` to ensure using them causes an error. Note that this is only effective if -// such features can never be toggled via `-Ctarget-cpu`! If that is ever a possibility, we will need -// extra checks ensuring that the LLVM-computed target features for a CPU did not (un)set a -// `Forbidden` feature. See https://github.com/rust-lang/rust/issues/116344 for some more context. -// FIXME: add such "forbidden" features for non-x86 targets. +// It is important for soundness to consider the interaction of targets features and the function +// call ABI. For example, disabling the `x87` feature on x86 changes how scalar floats are passed as +// arguments, so letting people toggle that feature would be unsound. To this end, the +// `abi_required_features` function computes which target features must and must not be enabled for +// any given target, and individual features can also be marked as `Forbidden`. +// See https://github.com/rust-lang/rust/issues/116344 for some more context. // // The one exception to features that change the ABI is features that enable larger vector -// registers. Those are permitted to be listed here. This is currently unsound (see -// https://github.com/rust-lang/rust/issues/116558); in the future we will have to ensure that -// functions can only use such vectors as arguments/return types if the corresponding target feature -// is enabled. +// registers. Those are permitted to be listed here. The `*_FOR_CORRECT_VECTOR_ABI` arrays store +// information about which target feature is ABI-required for which vector size; this is used to +// ensure that vectors can only be passed via `extern "C"` when the right feature is enabled. (For +// the "Rust" ABI we generally pass vectors by-ref exactly to avoid these issues.) +// Also see https://github.com/rust-lang/rust/issues/116558. // // Stabilizing a target feature requires t-lang approval. @@ -204,218 +131,193 @@ const fn unstable(nightly_feature: Symbol) -> StabilityUncomputed { // Both of these are also applied transitively. type ImpliedFeatures = &'static [&'static str]; -const ARM_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +static ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("aclass", unstable(sym::arm_target_feature), &[]), - ("aes", unstable(sym::arm_target_feature), &["neon"]), - ("crc", unstable(sym::arm_target_feature), &[]), - ("d32", unstable(sym::arm_target_feature), &[]), - ("dotprod", unstable(sym::arm_target_feature), &["neon"]), - ("dsp", unstable(sym::arm_target_feature), &[]), - ("fp-armv8", unstable(sym::arm_target_feature), &["vfp4"]), + ("aclass", Unstable(sym::arm_target_feature), &[]), + ("aes", Unstable(sym::arm_target_feature), &["neon"]), ( - "fpregs", - Stability::Unstable { - nightly_feature: sym::arm_target_feature, - allow_toggle: |target: &Target, _enable| { - // Only allow toggling this if the target has `soft-float` set. With `soft-float`, - // `fpregs` isn't needed so changing it cannot affect the ABI. - if target.has_feature("soft-float") { - Ok(()) - } else { - Err("unsound on hard-float targets because it changes float ABI") - } - }, - }, + "atomics-32", + Stability::Forbidden { reason: "unsound because it changes the ABI of atomic operations" }, &[], ), - ("i8mm", unstable(sym::arm_target_feature), &["neon"]), - ("mclass", unstable(sym::arm_target_feature), &[]), - ("neon", unstable(sym::arm_target_feature), &["vfp3"]), - ("rclass", unstable(sym::arm_target_feature), &[]), - ("sha2", unstable(sym::arm_target_feature), &["neon"]), - ("soft-float", Stability::Forbidden { reason: "unsound because it changes float ABI" }, &[]), + ("crc", Unstable(sym::arm_target_feature), &[]), + ("d32", Unstable(sym::arm_target_feature), &[]), + ("dotprod", Unstable(sym::arm_target_feature), &["neon"]), + ("dsp", Unstable(sym::arm_target_feature), &[]), + ("fp-armv8", Unstable(sym::arm_target_feature), &["vfp4"]), + ("fp16", Unstable(sym::arm_target_feature), &["neon"]), + ("fpregs", Unstable(sym::arm_target_feature), &[]), + ("i8mm", Unstable(sym::arm_target_feature), &["neon"]), + ("mclass", Unstable(sym::arm_target_feature), &[]), + ("neon", Unstable(sym::arm_target_feature), &["vfp3"]), + ("rclass", Unstable(sym::arm_target_feature), &[]), + ("sha2", Unstable(sym::arm_target_feature), &["neon"]), + // This can be *disabled* on non-`hf` targets to enable the use + // of hardfloats while keeping the softfloat ABI. + // FIXME before stabilization: Should we expose this as a `hard-float` target feature instead of + // matching the odd negative feature LLVM uses? + ("soft-float", Unstable(sym::arm_target_feature), &[]), // This is needed for inline assembly, but shouldn't be stabilized as-is // since it should be enabled per-function using #[instruction_set], not // #[target_feature]. - ("thumb-mode", unstable(sym::arm_target_feature), &[]), - ("thumb2", unstable(sym::arm_target_feature), &[]), - ("trustzone", unstable(sym::arm_target_feature), &[]), - ("v5te", unstable(sym::arm_target_feature), &[]), - ("v6", unstable(sym::arm_target_feature), &["v5te"]), - ("v6k", unstable(sym::arm_target_feature), &["v6"]), - ("v6t2", unstable(sym::arm_target_feature), &["v6k", "thumb2"]), - ("v7", unstable(sym::arm_target_feature), &["v6t2"]), - ("v8", unstable(sym::arm_target_feature), &["v7"]), - ("vfp2", unstable(sym::arm_target_feature), &[]), - ("vfp3", unstable(sym::arm_target_feature), &["vfp2", "d32"]), - ("vfp4", unstable(sym::arm_target_feature), &["vfp3"]), - ("virtualization", unstable(sym::arm_target_feature), &[]), + ("thumb-mode", Unstable(sym::arm_target_feature), &[]), + ("thumb2", Unstable(sym::arm_target_feature), &[]), + ("trustzone", Unstable(sym::arm_target_feature), &[]), + ("v5te", Unstable(sym::arm_target_feature), &[]), + ("v6", Unstable(sym::arm_target_feature), &["v5te"]), + ("v6k", Unstable(sym::arm_target_feature), &["v6"]), + ("v6t2", Unstable(sym::arm_target_feature), &["v6k", "thumb2"]), + ("v7", Unstable(sym::arm_target_feature), &["v6t2"]), + ("v8", Unstable(sym::arm_target_feature), &["v7"]), + ("vfp2", Unstable(sym::arm_target_feature), &[]), + ("vfp3", Unstable(sym::arm_target_feature), &["vfp2", "d32"]), + ("vfp4", Unstable(sym::arm_target_feature), &["vfp3"]), + ("virtualization", Unstable(sym::arm_target_feature), &[]), // tidy-alphabetical-end ]; -const AARCH64_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +static AARCH64_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start // FEAT_AES & FEAT_PMULL - ("aes", STABLE, &["neon"]), + ("aes", Stable, &["neon"]), // FEAT_BF16 - ("bf16", STABLE, &[]), + ("bf16", Stable, &[]), // FEAT_BTI - ("bti", STABLE, &[]), + ("bti", Stable, &[]), // FEAT_CRC - ("crc", STABLE, &[]), + ("crc", Stable, &[]), // FEAT_CSSC - ("cssc", unstable(sym::aarch64_unstable_target_feature), &[]), + ("cssc", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_DIT - ("dit", STABLE, &[]), + ("dit", Stable, &[]), // FEAT_DotProd - ("dotprod", STABLE, &["neon"]), + ("dotprod", Stable, &["neon"]), // FEAT_DPB - ("dpb", STABLE, &[]), + ("dpb", Stable, &[]), // FEAT_DPB2 - ("dpb2", STABLE, &["dpb"]), + ("dpb2", Stable, &["dpb"]), // FEAT_ECV - ("ecv", unstable(sym::aarch64_unstable_target_feature), &[]), + ("ecv", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_F32MM - ("f32mm", STABLE, &["sve"]), + ("f32mm", Stable, &["sve"]), // FEAT_F64MM - ("f64mm", STABLE, &["sve"]), + ("f64mm", Stable, &["sve"]), // FEAT_FAMINMAX - ("faminmax", unstable(sym::aarch64_unstable_target_feature), &[]), + ("faminmax", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_FCMA - ("fcma", STABLE, &["neon"]), + ("fcma", Stable, &["neon"]), // FEAT_FHM - ("fhm", STABLE, &["fp16"]), + ("fhm", Stable, &["fp16"]), // FEAT_FLAGM - ("flagm", STABLE, &[]), + ("flagm", Stable, &[]), // FEAT_FLAGM2 - ("flagm2", unstable(sym::aarch64_unstable_target_feature), &[]), + ("flagm2", Unstable(sym::aarch64_unstable_target_feature), &[]), + // We forbid directly toggling just `fp-armv8`; it must be toggled with `neon`. ("fp-armv8", Stability::Forbidden { reason: "Rust ties `fp-armv8` to `neon`" }, &[]), // FEAT_FP16 // Rust ties FP and Neon: https://github.com/rust-lang/rust/pull/91608 - ("fp16", STABLE, &["neon"]), + ("fp16", Stable, &["neon"]), // FEAT_FP8 - ("fp8", unstable(sym::aarch64_unstable_target_feature), &["faminmax", "lut", "bf16"]), + ("fp8", Unstable(sym::aarch64_unstable_target_feature), &["faminmax", "lut", "bf16"]), // FEAT_FP8DOT2 - ("fp8dot2", unstable(sym::aarch64_unstable_target_feature), &["fp8dot4"]), + ("fp8dot2", Unstable(sym::aarch64_unstable_target_feature), &["fp8dot4"]), // FEAT_FP8DOT4 - ("fp8dot4", unstable(sym::aarch64_unstable_target_feature), &["fp8fma"]), + ("fp8dot4", Unstable(sym::aarch64_unstable_target_feature), &["fp8fma"]), // FEAT_FP8FMA - ("fp8fma", unstable(sym::aarch64_unstable_target_feature), &["fp8"]), + ("fp8fma", Unstable(sym::aarch64_unstable_target_feature), &["fp8"]), // FEAT_FRINTTS - ("frintts", STABLE, &[]), + ("frintts", Stable, &[]), // FEAT_HBC - ("hbc", unstable(sym::aarch64_unstable_target_feature), &[]), + ("hbc", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_I8MM - ("i8mm", STABLE, &[]), + ("i8mm", Stable, &[]), // FEAT_JSCVT // Rust ties FP and Neon: https://github.com/rust-lang/rust/pull/91608 - ("jsconv", STABLE, &["neon"]), + ("jsconv", Stable, &["neon"]), // FEAT_LOR - ("lor", STABLE, &[]), + ("lor", Stable, &[]), // FEAT_LSE - ("lse", STABLE, &[]), + ("lse", Stable, &[]), // FEAT_LSE128 - ("lse128", unstable(sym::aarch64_unstable_target_feature), &["lse"]), + ("lse128", Unstable(sym::aarch64_unstable_target_feature), &["lse"]), // FEAT_LSE2 - ("lse2", unstable(sym::aarch64_unstable_target_feature), &[]), + ("lse2", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_LUT - ("lut", unstable(sym::aarch64_unstable_target_feature), &[]), + ("lut", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_MOPS - ("mops", unstable(sym::aarch64_unstable_target_feature), &[]), + ("mops", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_MTE & FEAT_MTE2 - ("mte", STABLE, &[]), + ("mte", Stable, &[]), // FEAT_AdvSimd & FEAT_FP - ( - "neon", - Stability::Stable { - allow_toggle: |target, enable| { - if target.abi == "softfloat" { - // `neon` has no ABI implications for softfloat targets, we can allow this. - Ok(()) - } else if enable - && !target.has_neg_feature("fp-armv8") - && !target.has_neg_feature("neon") - { - // neon is enabled by default, and has not been disabled, so enabling it again - // is redundant and we can permit it. Forbidding this would be a breaking change - // since this feature is stable. - Ok(()) - } else { - Err("unsound on hard-float targets because it changes float ABI") - } - }, - }, - &[], - ), + ("neon", Stable, &[]), // FEAT_PAUTH (address authentication) - ("paca", STABLE, &[]), + ("paca", Stable, &[]), // FEAT_PAUTH (generic authentication) - ("pacg", STABLE, &[]), + ("pacg", Stable, &[]), // FEAT_PAN - ("pan", STABLE, &[]), + ("pan", Stable, &[]), // FEAT_PAuth_LR - ("pauth-lr", unstable(sym::aarch64_unstable_target_feature), &[]), + ("pauth-lr", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_PMUv3 - ("pmuv3", STABLE, &[]), + ("pmuv3", Stable, &[]), // FEAT_RNG - ("rand", STABLE, &[]), + ("rand", Stable, &[]), // FEAT_RAS & FEAT_RASv1p1 - ("ras", STABLE, &[]), + ("ras", Stable, &[]), // FEAT_LRCPC - ("rcpc", STABLE, &[]), + ("rcpc", Stable, &[]), // FEAT_LRCPC2 - ("rcpc2", STABLE, &["rcpc"]), + ("rcpc2", Stable, &["rcpc"]), // FEAT_LRCPC3 - ("rcpc3", unstable(sym::aarch64_unstable_target_feature), &["rcpc2"]), + ("rcpc3", Unstable(sym::aarch64_unstable_target_feature), &["rcpc2"]), // FEAT_RDM - ("rdm", STABLE, &["neon"]), + ("rdm", Stable, &["neon"]), // This is needed for inline assembly, but shouldn't be stabilized as-is // since it should be enabled globally using -Zfixed-x18, not // #[target_feature]. // Note that cfg(target_feature = "reserve-x18") is currently not set for // targets that reserve x18 by default. - ("reserve-x18", unstable(sym::aarch64_unstable_target_feature), &[]), + ("reserve-x18", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_SB - ("sb", STABLE, &[]), + ("sb", Stable, &[]), // FEAT_SHA1 & FEAT_SHA256 - ("sha2", STABLE, &["neon"]), + ("sha2", Stable, &["neon"]), // FEAT_SHA512 & FEAT_SHA3 - ("sha3", STABLE, &["sha2"]), + ("sha3", Stable, &["sha2"]), // FEAT_SM3 & FEAT_SM4 - ("sm4", STABLE, &["neon"]), + ("sm4", Stable, &["neon"]), // FEAT_SME - ("sme", unstable(sym::aarch64_unstable_target_feature), &["bf16"]), + ("sme", Unstable(sym::aarch64_unstable_target_feature), &["bf16"]), // FEAT_SME_B16B16 - ("sme-b16b16", unstable(sym::aarch64_unstable_target_feature), &["bf16", "sme2", "sve-b16b16"]), + ("sme-b16b16", Unstable(sym::aarch64_unstable_target_feature), &["bf16", "sme2", "sve-b16b16"]), // FEAT_SME_F16F16 - ("sme-f16f16", unstable(sym::aarch64_unstable_target_feature), &["sme2"]), + ("sme-f16f16", Unstable(sym::aarch64_unstable_target_feature), &["sme2"]), // FEAT_SME_F64F64 - ("sme-f64f64", unstable(sym::aarch64_unstable_target_feature), &["sme"]), + ("sme-f64f64", Unstable(sym::aarch64_unstable_target_feature), &["sme"]), // FEAT_SME_F8F16 - ("sme-f8f16", unstable(sym::aarch64_unstable_target_feature), &["sme-f8f32"]), + ("sme-f8f16", Unstable(sym::aarch64_unstable_target_feature), &["sme-f8f32"]), // FEAT_SME_F8F32 - ("sme-f8f32", unstable(sym::aarch64_unstable_target_feature), &["sme2", "fp8"]), + ("sme-f8f32", Unstable(sym::aarch64_unstable_target_feature), &["sme2", "fp8"]), // FEAT_SME_FA64 - ("sme-fa64", unstable(sym::aarch64_unstable_target_feature), &["sme", "sve2"]), + ("sme-fa64", Unstable(sym::aarch64_unstable_target_feature), &["sme", "sve2"]), // FEAT_SME_I16I64 - ("sme-i16i64", unstable(sym::aarch64_unstable_target_feature), &["sme"]), + ("sme-i16i64", Unstable(sym::aarch64_unstable_target_feature), &["sme"]), // FEAT_SME_LUTv2 - ("sme-lutv2", unstable(sym::aarch64_unstable_target_feature), &[]), + ("sme-lutv2", Unstable(sym::aarch64_unstable_target_feature), &[]), // FEAT_SME2 - ("sme2", unstable(sym::aarch64_unstable_target_feature), &["sme"]), + ("sme2", Unstable(sym::aarch64_unstable_target_feature), &["sme"]), // FEAT_SME2p1 - ("sme2p1", unstable(sym::aarch64_unstable_target_feature), &["sme2"]), + ("sme2p1", Unstable(sym::aarch64_unstable_target_feature), &["sme2"]), // FEAT_SPE - ("spe", STABLE, &[]), + ("spe", Stable, &[]), // FEAT_SSBS & FEAT_SSBS2 - ("ssbs", STABLE, &[]), + ("ssbs", Stable, &[]), // FEAT_SSVE_FP8FDOT2 - ("ssve-fp8dot2", unstable(sym::aarch64_unstable_target_feature), &["ssve-fp8dot4"]), + ("ssve-fp8dot2", Unstable(sym::aarch64_unstable_target_feature), &["ssve-fp8dot4"]), // FEAT_SSVE_FP8FDOT4 - ("ssve-fp8dot4", unstable(sym::aarch64_unstable_target_feature), &["ssve-fp8fma"]), + ("ssve-fp8dot4", Unstable(sym::aarch64_unstable_target_feature), &["ssve-fp8fma"]), // FEAT_SSVE_FP8FMA - ("ssve-fp8fma", unstable(sym::aarch64_unstable_target_feature), &["sme2", "fp8"]), + ("ssve-fp8fma", Unstable(sym::aarch64_unstable_target_feature), &["sme2", "fp8"]), // FEAT_SVE // It was decided that SVE requires Neon: https://github.com/rust-lang/rust/pull/91608 // @@ -423,46 +325,50 @@ const AARCH64_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ // exist together: https://developer.arm.com/documentation/102340/0100/New-features-in-SVE2 // // "For backwards compatibility, Neon and VFP are required in the latest architectures." - ("sve", STABLE, &["neon"]), + ("sve", Stable, &["neon"]), // FEAT_SVE_B16B16 (SVE or SME Z-targeting instructions) - ("sve-b16b16", unstable(sym::aarch64_unstable_target_feature), &["bf16"]), + ("sve-b16b16", Unstable(sym::aarch64_unstable_target_feature), &["bf16"]), // FEAT_SVE2 - ("sve2", STABLE, &["sve"]), + ("sve2", Stable, &["sve"]), // FEAT_SVE_AES & FEAT_SVE_PMULL128 - ("sve2-aes", STABLE, &["sve2", "aes"]), + ("sve2-aes", Stable, &["sve2", "aes"]), // FEAT_SVE2_BitPerm - ("sve2-bitperm", STABLE, &["sve2"]), + ("sve2-bitperm", Stable, &["sve2"]), // FEAT_SVE2_SHA3 - ("sve2-sha3", STABLE, &["sve2", "sha3"]), + ("sve2-sha3", Stable, &["sve2", "sha3"]), // FEAT_SVE2_SM4 - ("sve2-sm4", STABLE, &["sve2", "sm4"]), + ("sve2-sm4", Stable, &["sve2", "sm4"]), // FEAT_SVE2p1 - ("sve2p1", unstable(sym::aarch64_unstable_target_feature), &["sve2"]), + ("sve2p1", Unstable(sym::aarch64_unstable_target_feature), &["sve2"]), // FEAT_TME - ("tme", STABLE, &[]), - ("v8.1a", unstable(sym::aarch64_ver_target_feature), &[ - "crc", "lse", "rdm", "pan", "lor", "vh", - ]), - ("v8.2a", unstable(sym::aarch64_ver_target_feature), &["v8.1a", "ras", "dpb"]), - ("v8.3a", unstable(sym::aarch64_ver_target_feature), &[ - "v8.2a", "rcpc", "paca", "pacg", "jsconv", - ]), - ("v8.4a", unstable(sym::aarch64_ver_target_feature), &["v8.3a", "dotprod", "dit", "flagm"]), - ("v8.5a", unstable(sym::aarch64_ver_target_feature), &["v8.4a", "ssbs", "sb", "dpb2", "bti"]), - ("v8.6a", unstable(sym::aarch64_ver_target_feature), &["v8.5a", "bf16", "i8mm"]), - ("v8.7a", unstable(sym::aarch64_ver_target_feature), &["v8.6a", "wfxt"]), - ("v8.8a", unstable(sym::aarch64_ver_target_feature), &["v8.7a", "hbc", "mops"]), - ("v8.9a", unstable(sym::aarch64_ver_target_feature), &["v8.8a", "cssc"]), - ("v9.1a", unstable(sym::aarch64_ver_target_feature), &["v9a", "v8.6a"]), - ("v9.2a", unstable(sym::aarch64_ver_target_feature), &["v9.1a", "v8.7a"]), - ("v9.3a", unstable(sym::aarch64_ver_target_feature), &["v9.2a", "v8.8a"]), - ("v9.4a", unstable(sym::aarch64_ver_target_feature), &["v9.3a", "v8.9a"]), - ("v9.5a", unstable(sym::aarch64_ver_target_feature), &["v9.4a"]), - ("v9a", unstable(sym::aarch64_ver_target_feature), &["v8.5a", "sve2"]), + ("tme", Stable, &[]), + ( + "v8.1a", + Unstable(sym::aarch64_ver_target_feature), + &["crc", "lse", "rdm", "pan", "lor", "vh"], + ), + ("v8.2a", Unstable(sym::aarch64_ver_target_feature), &["v8.1a", "ras", "dpb"]), + ( + "v8.3a", + Unstable(sym::aarch64_ver_target_feature), + &["v8.2a", "rcpc", "paca", "pacg", "jsconv"], + ), + ("v8.4a", Unstable(sym::aarch64_ver_target_feature), &["v8.3a", "dotprod", "dit", "flagm"]), + ("v8.5a", Unstable(sym::aarch64_ver_target_feature), &["v8.4a", "ssbs", "sb", "dpb2", "bti"]), + ("v8.6a", Unstable(sym::aarch64_ver_target_feature), &["v8.5a", "bf16", "i8mm"]), + ("v8.7a", Unstable(sym::aarch64_ver_target_feature), &["v8.6a", "wfxt"]), + ("v8.8a", Unstable(sym::aarch64_ver_target_feature), &["v8.7a", "hbc", "mops"]), + ("v8.9a", Unstable(sym::aarch64_ver_target_feature), &["v8.8a", "cssc"]), + ("v9.1a", Unstable(sym::aarch64_ver_target_feature), &["v9a", "v8.6a"]), + ("v9.2a", Unstable(sym::aarch64_ver_target_feature), &["v9.1a", "v8.7a"]), + ("v9.3a", Unstable(sym::aarch64_ver_target_feature), &["v9.2a", "v8.8a"]), + ("v9.4a", Unstable(sym::aarch64_ver_target_feature), &["v9.3a", "v8.9a"]), + ("v9.5a", Unstable(sym::aarch64_ver_target_feature), &["v9.4a"]), + ("v9a", Unstable(sym::aarch64_ver_target_feature), &["v8.5a", "sve2"]), // FEAT_VHE - ("vh", STABLE, &[]), + ("vh", Stable, &[]), // FEAT_WFxT - ("wfxt", unstable(sym::aarch64_unstable_target_feature), &[]), + ("wfxt", Unstable(sym::aarch64_unstable_target_feature), &[]), // tidy-alphabetical-end ]; @@ -470,337 +376,264 @@ const AARCH64_TIED_FEATURES: &[&[&str]] = &[ &["paca", "pacg"], // Together these represent `pauth` in LLVM ]; -const X86_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("adx", STABLE, &[]), - ("aes", STABLE, &["sse2"]), - ("amx-bf16", unstable(sym::x86_amx_intrinsics), &["amx-tile"]), - ("amx-complex", unstable(sym::x86_amx_intrinsics), &["amx-tile"]), - ("amx-fp16", unstable(sym::x86_amx_intrinsics), &["amx-tile"]), - ("amx-int8", unstable(sym::x86_amx_intrinsics), &["amx-tile"]), - ("amx-tile", unstable(sym::x86_amx_intrinsics), &[]), - ("avx", STABLE, &["sse4.2"]), - ("avx2", STABLE, &["avx"]), - ("avx512bf16", unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512bitalg", unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512bw", unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512cd", unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512dq", unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512f", unstable(sym::avx512_target_feature), &["avx2", "fma", "f16c"]), - ("avx512fp16", unstable(sym::avx512_target_feature), &["avx512bw", "avx512vl", "avx512dq"]), - ("avx512ifma", unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vbmi", unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512vbmi2", unstable(sym::avx512_target_feature), &["avx512bw"]), - ("avx512vl", unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vnni", unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vp2intersect", unstable(sym::avx512_target_feature), &["avx512f"]), - ("avx512vpopcntdq", unstable(sym::avx512_target_feature), &["avx512f"]), - ("avxifma", unstable(sym::avx512_target_feature), &["avx2"]), - ("avxneconvert", unstable(sym::avx512_target_feature), &["avx2"]), - ("avxvnni", unstable(sym::avx512_target_feature), &["avx2"]), - ("avxvnniint16", unstable(sym::avx512_target_feature), &["avx2"]), - ("avxvnniint8", unstable(sym::avx512_target_feature), &["avx2"]), - ("bmi1", STABLE, &[]), - ("bmi2", STABLE, &[]), - ("cmpxchg16b", STABLE, &[]), - ("ermsb", unstable(sym::ermsb_target_feature), &[]), - ("f16c", STABLE, &["avx"]), - ("fma", STABLE, &["avx"]), - ("fxsr", STABLE, &[]), - ("gfni", unstable(sym::avx512_target_feature), &["sse2"]), - ("lahfsahf", unstable(sym::lahfsahf_target_feature), &[]), - ("lzcnt", STABLE, &[]), - ("movbe", STABLE, &[]), - ("pclmulqdq", STABLE, &["sse2"]), - ("popcnt", STABLE, &[]), - ("prfchw", unstable(sym::prfchw_target_feature), &[]), - ("rdrand", STABLE, &[]), - ("rdseed", STABLE, &[]), - ("rtm", unstable(sym::rtm_target_feature), &[]), - ("sha", STABLE, &["sse2"]), - ("sha512", unstable(sym::sha512_sm_x86), &["avx2"]), - ("sm3", unstable(sym::sha512_sm_x86), &["avx"]), - ("sm4", unstable(sym::sha512_sm_x86), &["avx2"]), - ("soft-float", Stability::Forbidden { reason: "unsound because it changes float ABI" }, &[]), - ("sse", STABLE, &[]), - ("sse2", STABLE, &["sse"]), - ("sse3", STABLE, &["sse2"]), - ("sse4.1", STABLE, &["ssse3"]), - ("sse4.2", STABLE, &["sse4.1"]), - ("sse4a", unstable(sym::sse4a_target_feature), &["sse3"]), - ("ssse3", STABLE, &["sse3"]), - ("tbm", unstable(sym::tbm_target_feature), &[]), - ("vaes", unstable(sym::avx512_target_feature), &["avx2", "aes"]), - ("vpclmulqdq", unstable(sym::avx512_target_feature), &["avx", "pclmulqdq"]), - ( - "x87", - Stability::Unstable { - nightly_feature: sym::x87_target_feature, - allow_toggle: |target: &Target, _enable| { - // Only allow toggling this if the target has `soft-float` set. With `soft-float`, - // `fpregs` isn't needed so changing it cannot affect the ABI. - if target.has_feature("soft-float") { - Ok(()) - } else { - Err("unsound on hard-float targets because it changes float ABI") - } - }, - }, - &[], - ), - ("xop", unstable(sym::xop_target_feature), &[/*"fma4", */ "avx", "sse4a"]), - ("xsave", STABLE, &[]), - ("xsavec", STABLE, &["xsave"]), - ("xsaveopt", STABLE, &["xsave"]), - ("xsaves", STABLE, &["xsave"]), + ("adx", Stable, &[]), + ("aes", Stable, &["sse2"]), + ("amx-bf16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-complex", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-fp16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-int8", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-tile", Unstable(sym::x86_amx_intrinsics), &[]), + ("avx", Stable, &["sse4.2"]), + ("avx2", Stable, &["avx"]), + ("avx512bf16", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512bitalg", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512bw", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512cd", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512dq", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512f", Unstable(sym::avx512_target_feature), &["avx2", "fma", "f16c"]), + ("avx512fp16", Unstable(sym::avx512_target_feature), &["avx512bw", "avx512vl", "avx512dq"]), + ("avx512ifma", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vbmi", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512vbmi2", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512vl", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vnni", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vp2intersect", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vpopcntdq", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avxifma", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxneconvert", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxvnni", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxvnniint16", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxvnniint8", Unstable(sym::avx512_target_feature), &["avx2"]), + ("bmi1", Stable, &[]), + ("bmi2", Stable, &[]), + ("cmpxchg16b", Stable, &[]), + ("ermsb", Unstable(sym::ermsb_target_feature), &[]), + ("f16c", Stable, &["avx"]), + ("fma", Stable, &["avx"]), + ("fxsr", Stable, &[]), + ("gfni", Unstable(sym::avx512_target_feature), &["sse2"]), + ("kl", Unstable(sym::keylocker_x86), &["sse2"]), + ("lahfsahf", Unstable(sym::lahfsahf_target_feature), &[]), + ("lzcnt", Stable, &[]), + ("movbe", Stable, &[]), + ("pclmulqdq", Stable, &["sse2"]), + ("popcnt", Stable, &[]), + ("prfchw", Unstable(sym::prfchw_target_feature), &[]), + ("rdrand", Stable, &[]), + ("rdseed", Stable, &[]), + ("rtm", Unstable(sym::rtm_target_feature), &[]), + ("sha", Stable, &["sse2"]), + ("sha512", Unstable(sym::sha512_sm_x86), &["avx2"]), + ("sm3", Unstable(sym::sha512_sm_x86), &["avx"]), + ("sm4", Unstable(sym::sha512_sm_x86), &["avx2"]), + // This cannot actually be toggled, the ABI always fixes it, so it'd make little sense to + // stabilize. It must be in this list for the ABI check to be able to use it. + ("soft-float", Stability::Unstable(sym::x87_target_feature), &[]), + ("sse", Stable, &[]), + ("sse2", Stable, &["sse"]), + ("sse3", Stable, &["sse2"]), + ("sse4.1", Stable, &["ssse3"]), + ("sse4.2", Stable, &["sse4.1"]), + ("sse4a", Unstable(sym::sse4a_target_feature), &["sse3"]), + ("ssse3", Stable, &["sse3"]), + ("tbm", Unstable(sym::tbm_target_feature), &[]), + ("vaes", Unstable(sym::avx512_target_feature), &["avx2", "aes"]), + ("vpclmulqdq", Unstable(sym::avx512_target_feature), &["avx", "pclmulqdq"]), + ("widekl", Unstable(sym::keylocker_x86), &["kl"]), + ("x87", Unstable(sym::x87_target_feature), &[]), + ("xop", Unstable(sym::xop_target_feature), &[/*"fma4", */ "avx", "sse4a"]), + ("xsave", Stable, &[]), + ("xsavec", Stable, &["xsave"]), + ("xsaveopt", Stable, &["xsave"]), + ("xsaves", Stable, &["xsave"]), // tidy-alphabetical-end ]; -const HEXAGON_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const HEXAGON_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("hvx", unstable(sym::hexagon_target_feature), &[]), - ("hvx-length128b", unstable(sym::hexagon_target_feature), &["hvx"]), + ("hvx", Unstable(sym::hexagon_target_feature), &[]), + ("hvx-length128b", Unstable(sym::hexagon_target_feature), &["hvx"]), // tidy-alphabetical-end ]; -const POWERPC_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +static POWERPC_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("altivec", unstable(sym::powerpc_target_feature), &[]), - ("partword-atomics", unstable(sym::powerpc_target_feature), &[]), - ("power10-vector", unstable(sym::powerpc_target_feature), &["power9-vector"]), - ("power8-altivec", unstable(sym::powerpc_target_feature), &["altivec"]), - ("power8-crypto", unstable(sym::powerpc_target_feature), &["power8-altivec"]), - ("power8-vector", unstable(sym::powerpc_target_feature), &["vsx", "power8-altivec"]), - ("power9-altivec", unstable(sym::powerpc_target_feature), &["power8-altivec"]), - ("power9-vector", unstable(sym::powerpc_target_feature), &["power8-vector", "power9-altivec"]), - ("quadword-atomics", unstable(sym::powerpc_target_feature), &[]), - ("vsx", unstable(sym::powerpc_target_feature), &["altivec"]), + ("altivec", Unstable(sym::powerpc_target_feature), &[]), + ("partword-atomics", Unstable(sym::powerpc_target_feature), &[]), + ("power10-vector", Unstable(sym::powerpc_target_feature), &["power9-vector"]), + ("power8-altivec", Unstable(sym::powerpc_target_feature), &["altivec"]), + ("power8-crypto", Unstable(sym::powerpc_target_feature), &["power8-altivec"]), + ("power8-vector", Unstable(sym::powerpc_target_feature), &["vsx", "power8-altivec"]), + ("power9-altivec", Unstable(sym::powerpc_target_feature), &["power8-altivec"]), + ("power9-vector", Unstable(sym::powerpc_target_feature), &["power8-vector", "power9-altivec"]), + ("quadword-atomics", Unstable(sym::powerpc_target_feature), &[]), + ("vsx", Unstable(sym::powerpc_target_feature), &["altivec"]), // tidy-alphabetical-end ]; -const MIPS_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const MIPS_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("fp64", unstable(sym::mips_target_feature), &[]), - ("msa", unstable(sym::mips_target_feature), &[]), - ("virt", unstable(sym::mips_target_feature), &[]), + ("fp64", Unstable(sym::mips_target_feature), &[]), + ("msa", Unstable(sym::mips_target_feature), &[]), + ("virt", Unstable(sym::mips_target_feature), &[]), // tidy-alphabetical-end ]; -const RISCV_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("a", STABLE, &["zaamo", "zalrsc"]), - ("c", STABLE, &[]), - ( - "d", - Stability::Unstable { - nightly_feature: sym::riscv_target_feature, - allow_toggle: |target, enable| match &*target.llvm_abiname { - "ilp32d" | "lp64d" if !enable => { - // The ABI requires the `d` feature, so it cannot be disabled. - Err("feature is required by ABI") - } - "ilp32e" if enable => { - // ilp32e is incompatible with features that need aligned load/stores > 32 bits, - // like `d`. - Err("feature is incompatible with ABI") - } - _ => Ok(()), - }, - }, - &["f"], - ), - ( - "e", - Stability::Unstable { - // Given that this is a negative feature, consider this before stabilizing: - // does it really make sense to enable this feature in an individual - // function with `#[target_feature]`? - nightly_feature: sym::riscv_target_feature, - allow_toggle: |target, enable| { - match &*target.llvm_abiname { - _ if !enable => { - // Disabling this feature means we can use more registers (x16-x31). - // The "e" ABIs treat them as caller-save, so it is safe to use them only - // in some parts of a program while the rest doesn't know they even exist. - // On other ABIs, the feature is already disabled anyway. - Ok(()) - } - "ilp32e" | "lp64e" => { - // Embedded ABIs should already have the feature anyway, it's fine to enable - // it again from an ABI perspective. - Ok(()) - } - _ => { - // *Not* an embedded ABI. Enabling `e` is invalid. - Err("feature is incompatible with ABI") - } - } - }, - }, - &[], - ), - ( - "f", - Stability::Unstable { - nightly_feature: sym::riscv_target_feature, - allow_toggle: |target, enable| { - match &*target.llvm_abiname { - "ilp32f" | "ilp32d" | "lp64f" | "lp64d" if !enable => { - // The ABI requires the `f` feature, so it cannot be disabled. - Err("feature is required by ABI") - } - _ => Ok(()), - } - }, - }, - &[], - ), + ("a", Stable, &["zaamo", "zalrsc"]), + ("c", Stable, &[]), + ("d", Unstable(sym::riscv_target_feature), &["f"]), + ("e", Unstable(sym::riscv_target_feature), &[]), + ("f", Unstable(sym::riscv_target_feature), &[]), ( "forced-atomics", Stability::Forbidden { reason: "unsound because it changes the ABI of atomic operations" }, &[], ), - ("m", STABLE, &[]), - ("relax", unstable(sym::riscv_target_feature), &[]), - ("unaligned-scalar-mem", unstable(sym::riscv_target_feature), &[]), - ("v", unstable(sym::riscv_target_feature), &[]), - ("zaamo", unstable(sym::riscv_target_feature), &[]), - ("zabha", unstable(sym::riscv_target_feature), &["zaamo"]), - ("zalrsc", unstable(sym::riscv_target_feature), &[]), - ("zba", STABLE, &[]), - ("zbb", STABLE, &[]), - ("zbc", STABLE, &[]), - ("zbkb", STABLE, &[]), - ("zbkc", STABLE, &[]), - ("zbkx", STABLE, &[]), - ("zbs", STABLE, &[]), - ("zdinx", unstable(sym::riscv_target_feature), &["zfinx"]), - ("zfh", unstable(sym::riscv_target_feature), &["zfhmin"]), - ("zfhmin", unstable(sym::riscv_target_feature), &["f"]), - ("zfinx", unstable(sym::riscv_target_feature), &[]), - ("zhinx", unstable(sym::riscv_target_feature), &["zhinxmin"]), - ("zhinxmin", unstable(sym::riscv_target_feature), &["zfinx"]), - ("zk", STABLE, &["zkn", "zkr", "zkt"]), - ("zkn", STABLE, &["zbkb", "zbkc", "zbkx", "zkne", "zknd", "zknh"]), - ("zknd", STABLE, &[]), - ("zkne", STABLE, &[]), - ("zknh", STABLE, &[]), - ("zkr", STABLE, &[]), - ("zks", STABLE, &["zbkb", "zbkc", "zbkx", "zksed", "zksh"]), - ("zksed", STABLE, &[]), - ("zksh", STABLE, &[]), - ("zkt", STABLE, &[]), + ("m", Stable, &[]), + ("relax", Unstable(sym::riscv_target_feature), &[]), + ("unaligned-scalar-mem", Unstable(sym::riscv_target_feature), &[]), + ("v", Unstable(sym::riscv_target_feature), &[]), + ("zaamo", Unstable(sym::riscv_target_feature), &[]), + ("zabha", Unstable(sym::riscv_target_feature), &["zaamo"]), + ("zalrsc", Unstable(sym::riscv_target_feature), &[]), + ("zba", Stable, &[]), + ("zbb", Stable, &[]), + ("zbc", Stable, &[]), + ("zbkb", Stable, &[]), + ("zbkc", Stable, &[]), + ("zbkx", Stable, &[]), + ("zbs", Stable, &[]), + ("zdinx", Unstable(sym::riscv_target_feature), &["zfinx"]), + ("zfh", Unstable(sym::riscv_target_feature), &["zfhmin"]), + ("zfhmin", Unstable(sym::riscv_target_feature), &["f"]), + ("zfinx", Unstable(sym::riscv_target_feature), &[]), + ("zhinx", Unstable(sym::riscv_target_feature), &["zhinxmin"]), + ("zhinxmin", Unstable(sym::riscv_target_feature), &["zfinx"]), + ("zk", Stable, &["zkn", "zkr", "zkt"]), + ("zkn", Stable, &["zbkb", "zbkc", "zbkx", "zkne", "zknd", "zknh"]), + ("zknd", Stable, &[]), + ("zkne", Stable, &[]), + ("zknh", Stable, &[]), + ("zkr", Stable, &[]), + ("zks", Stable, &["zbkb", "zbkc", "zbkx", "zksed", "zksh"]), + ("zksed", Stable, &[]), + ("zksh", Stable, &[]), + ("zkt", Stable, &[]), // tidy-alphabetical-end ]; -const WASM_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +static WASM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("atomics", unstable(sym::wasm_target_feature), &[]), - ("bulk-memory", STABLE, &[]), - ("exception-handling", unstable(sym::wasm_target_feature), &[]), - ("extended-const", STABLE, &[]), - ("multivalue", STABLE, &[]), - ("mutable-globals", STABLE, &[]), - ("nontrapping-fptoint", STABLE, &[]), - ("reference-types", STABLE, &[]), - ("relaxed-simd", STABLE, &["simd128"]), - ("sign-ext", STABLE, &[]), - ("simd128", STABLE, &[]), - ("tail-call", STABLE, &[]), - ("wide-arithmetic", unstable(sym::wasm_target_feature), &[]), + ("atomics", Unstable(sym::wasm_target_feature), &[]), + ("bulk-memory", Stable, &[]), + ("exception-handling", Unstable(sym::wasm_target_feature), &[]), + ("extended-const", Stable, &[]), + ("multivalue", Stable, &[]), + ("mutable-globals", Stable, &[]), + ("nontrapping-fptoint", Stable, &[]), + ("reference-types", Stable, &[]), + ("relaxed-simd", Stable, &["simd128"]), + ("sign-ext", Stable, &[]), + ("simd128", Stable, &[]), + ("tail-call", Stable, &[]), + ("wide-arithmetic", Unstable(sym::wasm_target_feature), &[]), // tidy-alphabetical-end ]; -const BPF_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = - &[("alu32", unstable(sym::bpf_target_feature), &[])]; +const BPF_FEATURES: &[(&str, Stability, ImpliedFeatures)] = + &[("alu32", Unstable(sym::bpf_target_feature), &[])]; -const CSKY_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +static CSKY_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("10e60", unstable(sym::csky_target_feature), &["7e10"]), - ("2e3", unstable(sym::csky_target_feature), &["e2"]), - ("3e3r1", unstable(sym::csky_target_feature), &[]), - ("3e3r2", unstable(sym::csky_target_feature), &["3e3r1", "doloop"]), - ("3e3r3", unstable(sym::csky_target_feature), &["doloop"]), - ("3e7", unstable(sym::csky_target_feature), &["2e3"]), - ("7e10", unstable(sym::csky_target_feature), &["3e7"]), - ("cache", unstable(sym::csky_target_feature), &[]), - ("doloop", unstable(sym::csky_target_feature), &[]), - ("dsp1e2", unstable(sym::csky_target_feature), &[]), - ("dspe60", unstable(sym::csky_target_feature), &[]), - ("e1", unstable(sym::csky_target_feature), &["elrw"]), - ("e2", unstable(sym::csky_target_feature), &["e2"]), - ("edsp", unstable(sym::csky_target_feature), &[]), - ("elrw", unstable(sym::csky_target_feature), &[]), - ("float1e2", unstable(sym::csky_target_feature), &[]), - ("float1e3", unstable(sym::csky_target_feature), &[]), - ("float3e4", unstable(sym::csky_target_feature), &[]), - ("float7e60", unstable(sym::csky_target_feature), &[]), - ("floate1", unstable(sym::csky_target_feature), &[]), - ("hard-tp", unstable(sym::csky_target_feature), &[]), - ("high-registers", unstable(sym::csky_target_feature), &[]), - ("hwdiv", unstable(sym::csky_target_feature), &[]), - ("mp", unstable(sym::csky_target_feature), &["2e3"]), - ("mp1e2", unstable(sym::csky_target_feature), &["3e7"]), - ("nvic", unstable(sym::csky_target_feature), &[]), - ("trust", unstable(sym::csky_target_feature), &[]), - ("vdsp2e60f", unstable(sym::csky_target_feature), &[]), - ("vdspv1", unstable(sym::csky_target_feature), &[]), - ("vdspv2", unstable(sym::csky_target_feature), &[]), + ("10e60", Unstable(sym::csky_target_feature), &["7e10"]), + ("2e3", Unstable(sym::csky_target_feature), &["e2"]), + ("3e3r1", Unstable(sym::csky_target_feature), &[]), + ("3e3r2", Unstable(sym::csky_target_feature), &["3e3r1", "doloop"]), + ("3e3r3", Unstable(sym::csky_target_feature), &["doloop"]), + ("3e7", Unstable(sym::csky_target_feature), &["2e3"]), + ("7e10", Unstable(sym::csky_target_feature), &["3e7"]), + ("cache", Unstable(sym::csky_target_feature), &[]), + ("doloop", Unstable(sym::csky_target_feature), &[]), + ("dsp1e2", Unstable(sym::csky_target_feature), &[]), + ("dspe60", Unstable(sym::csky_target_feature), &[]), + ("e1", Unstable(sym::csky_target_feature), &["elrw"]), + ("e2", Unstable(sym::csky_target_feature), &["e2"]), + ("edsp", Unstable(sym::csky_target_feature), &[]), + ("elrw", Unstable(sym::csky_target_feature), &[]), + ("float1e2", Unstable(sym::csky_target_feature), &[]), + ("float1e3", Unstable(sym::csky_target_feature), &[]), + ("float3e4", Unstable(sym::csky_target_feature), &[]), + ("float7e60", Unstable(sym::csky_target_feature), &[]), + ("floate1", Unstable(sym::csky_target_feature), &[]), + ("hard-tp", Unstable(sym::csky_target_feature), &[]), + ("high-registers", Unstable(sym::csky_target_feature), &[]), + ("hwdiv", Unstable(sym::csky_target_feature), &[]), + ("mp", Unstable(sym::csky_target_feature), &["2e3"]), + ("mp1e2", Unstable(sym::csky_target_feature), &["3e7"]), + ("nvic", Unstable(sym::csky_target_feature), &[]), + ("trust", Unstable(sym::csky_target_feature), &[]), + ("vdsp2e60f", Unstable(sym::csky_target_feature), &[]), + ("vdspv1", Unstable(sym::csky_target_feature), &[]), + ("vdspv2", Unstable(sym::csky_target_feature), &[]), // tidy-alphabetical-end //fpu // tidy-alphabetical-start - ("fdivdu", unstable(sym::csky_target_feature), &[]), - ("fpuv2_df", unstable(sym::csky_target_feature), &[]), - ("fpuv2_sf", unstable(sym::csky_target_feature), &[]), - ("fpuv3_df", unstable(sym::csky_target_feature), &[]), - ("fpuv3_hf", unstable(sym::csky_target_feature), &[]), - ("fpuv3_hi", unstable(sym::csky_target_feature), &[]), - ("fpuv3_sf", unstable(sym::csky_target_feature), &[]), - ("hard-float", unstable(sym::csky_target_feature), &[]), - ("hard-float-abi", unstable(sym::csky_target_feature), &[]), + ("fdivdu", Unstable(sym::csky_target_feature), &[]), + ("fpuv2_df", Unstable(sym::csky_target_feature), &[]), + ("fpuv2_sf", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_df", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_hf", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_hi", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_sf", Unstable(sym::csky_target_feature), &[]), + ("hard-float", Unstable(sym::csky_target_feature), &[]), + ("hard-float-abi", Unstable(sym::csky_target_feature), &[]), // tidy-alphabetical-end ]; -const LOONGARCH_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +static LOONGARCH_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("d", unstable(sym::loongarch_target_feature), &["f"]), - ("f", unstable(sym::loongarch_target_feature), &[]), - ("frecipe", unstable(sym::loongarch_target_feature), &[]), - ("lasx", unstable(sym::loongarch_target_feature), &["lsx"]), - ("lbt", unstable(sym::loongarch_target_feature), &[]), - ("lsx", unstable(sym::loongarch_target_feature), &["d"]), - ("lvz", unstable(sym::loongarch_target_feature), &[]), - ("relax", unstable(sym::loongarch_target_feature), &[]), - ("ual", unstable(sym::loongarch_target_feature), &[]), + ("d", Unstable(sym::loongarch_target_feature), &["f"]), + ("f", Unstable(sym::loongarch_target_feature), &[]), + ("frecipe", Unstable(sym::loongarch_target_feature), &[]), + ("lasx", Unstable(sym::loongarch_target_feature), &["lsx"]), + ("lbt", Unstable(sym::loongarch_target_feature), &[]), + ("lsx", Unstable(sym::loongarch_target_feature), &["d"]), + ("lvz", Unstable(sym::loongarch_target_feature), &[]), + ("relax", Unstable(sym::loongarch_target_feature), &[]), + ("ual", Unstable(sym::loongarch_target_feature), &[]), // tidy-alphabetical-end ]; -const IBMZ_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const IBMZ_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("backchain", unstable(sym::s390x_target_feature), &[]), - ("vector", unstable(sym::s390x_target_feature), &[]), + ("backchain", Unstable(sym::s390x_target_feature), &[]), + ("vector", Unstable(sym::s390x_target_feature), &[]), // tidy-alphabetical-end ]; -const SPARC_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +const SPARC_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("leoncasa", unstable(sym::sparc_target_feature), &[]), - ("v8plus", unstable(sym::sparc_target_feature), &[]), - ("v9", unstable(sym::sparc_target_feature), &[]), + ("leoncasa", Unstable(sym::sparc_target_feature), &[]), + ("v8plus", Unstable(sym::sparc_target_feature), &[]), + ("v9", Unstable(sym::sparc_target_feature), &[]), // tidy-alphabetical-end ]; -const M68K_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ +static M68K_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("isa-68000", unstable(sym::m68k_target_feature), &[]), - ("isa-68010", unstable(sym::m68k_target_feature), &["isa-68000"]), - ("isa-68020", unstable(sym::m68k_target_feature), &["isa-68010"]), - ("isa-68030", unstable(sym::m68k_target_feature), &["isa-68020"]), - ("isa-68040", unstable(sym::m68k_target_feature), &["isa-68030", "isa-68882"]), - ("isa-68060", unstable(sym::m68k_target_feature), &["isa-68040"]), + ("isa-68000", Unstable(sym::m68k_target_feature), &[]), + ("isa-68010", Unstable(sym::m68k_target_feature), &["isa-68000"]), + ("isa-68020", Unstable(sym::m68k_target_feature), &["isa-68010"]), + ("isa-68030", Unstable(sym::m68k_target_feature), &["isa-68020"]), + ("isa-68040", Unstable(sym::m68k_target_feature), &["isa-68030", "isa-68882"]), + ("isa-68060", Unstable(sym::m68k_target_feature), &["isa-68040"]), // FPU - ("isa-68881", unstable(sym::m68k_target_feature), &[]), - ("isa-68882", unstable(sym::m68k_target_feature), &["isa-68881"]), + ("isa-68881", Unstable(sym::m68k_target_feature), &[]), + ("isa-68882", Unstable(sym::m68k_target_feature), &["isa-68881"]), // tidy-alphabetical-end ]; @@ -808,7 +641,7 @@ const M68K_FEATURES: &[(&str, StabilityUncomputed, ImpliedFeatures)] = &[ /// primitives may be documented. /// /// IMPORTANT: If you're adding another feature list above, make sure to add it to this iterator! -pub fn all_rust_features() -> impl Iterator { +pub fn all_rust_features() -> impl Iterator { std::iter::empty() .chain(ARM_FEATURES.iter()) .chain(AARCH64_FEATURES.iter()) @@ -853,10 +686,16 @@ const CSKY_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[( const LOONGARCH_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "lsx"), (256, "lasx")]; +#[derive(Copy, Clone, Debug)] +pub struct FeatureConstraints { + /// Features that must be enabled. + pub required: &'static [&'static str], + /// Features that must be disabled. + pub incompatible: &'static [&'static str], +} + impl Target { - pub fn rust_target_features( - &self, - ) -> &'static [(&'static str, StabilityUncomputed, ImpliedFeatures)] { + pub fn rust_target_features(&self) -> &'static [(&'static str, Stability, ImpliedFeatures)] { match &*self.arch { "arm" => ARM_FEATURES, "aarch64" | "arm64ec" => AARCH64_FEATURES, @@ -904,27 +743,159 @@ impl Target { } } - pub fn implied_target_features( + pub fn implied_target_features<'a>( &self, - base_features: impl Iterator, - ) -> FxHashSet { - let implied_features = self - .rust_target_features() - .iter() - .map(|(f, _, i)| (Symbol::intern(f), i)) - .collect::>(); + base_features: impl Iterator, + ) -> FxHashSet<&'a str> { + let implied_features = + self.rust_target_features().iter().map(|(f, _, i)| (f, i)).collect::>(); // implied target features have their own implied target features, so we traverse the // map until there are no more features to add let mut features = FxHashSet::default(); - let mut new_features = base_features.collect::>(); + let mut new_features = base_features.collect::>(); while let Some(new_feature) = new_features.pop() { if features.insert(new_feature) { if let Some(implied_features) = implied_features.get(&new_feature) { - new_features.extend(implied_features.iter().copied().map(Symbol::intern)) + new_features.extend(implied_features.iter().copied()) } } } features } + + /// Returns two lists of features: + /// the first list contains target features that must be enabled for ABI reasons, + /// and the second list contains target feature that must be disabled for ABI reasons. + /// + /// These features are automatically appended to whatever the target spec sats as default + /// features for the target. + /// + /// All features enabled/disabled via `-Ctarget-features` and `#[target_features]` are checked + /// against this. We also check any implied features, based on the information above. If LLVM + /// implicitly enables more implied features than we do, that could bypass this check! + pub fn abi_required_features(&self) -> FeatureConstraints { + const NOTHING: FeatureConstraints = FeatureConstraints { required: &[], incompatible: &[] }; + // Some architectures don't have a clean explicit ABI designation; instead, the ABI is + // defined by target features. When that is the case, those target features must be + // "forbidden" in the list above to ensure that there is a consistent answer to the + // questions "which ABI is used". + match &*self.arch { + "x86" => { + // We use our own ABI indicator here; LLVM does not have anything native. + // Every case should require or forbid `soft-float`! + match self.rustc_abi { + None => { + // Default hardfloat ABI. + // x87 must be enabled, soft-float must be disabled. + FeatureConstraints { required: &["x87"], incompatible: &["soft-float"] } + } + Some(RustcAbi::X86Sse2) => { + // Extended hardfloat ABI. x87 and SSE2 must be enabled, soft-float must be disabled. + FeatureConstraints { + required: &["x87", "sse2"], + incompatible: &["soft-float"], + } + } + Some(RustcAbi::X86Softfloat) => { + // Softfloat ABI, requires corresponding target feature. That feature trumps + // `x87` and all other FPU features so those do not matter. + // Note that this one requirement is the entire implementation of the ABI! + // LLVM handles the rest. + FeatureConstraints { required: &["soft-float"], incompatible: &[] } + } + } + } + "x86_64" => { + // We use our own ABI indicator here; LLVM does not have anything native. + // Every case should require or forbid `soft-float`! + match self.rustc_abi { + None => { + // Default hardfloat ABI. On x86-64, this always includes SSE2. + FeatureConstraints { + required: &["x87", "sse2"], + incompatible: &["soft-float"], + } + } + Some(RustcAbi::X86Softfloat) => { + // Softfloat ABI, requires corresponding target feature. That feature trumps + // `x87` and all other FPU features so those do not matter. + // Note that this one requirement is the entire implementation of the ABI! + // LLVM handles the rest. + FeatureConstraints { required: &["soft-float"], incompatible: &[] } + } + Some(r) => panic!("invalid Rust ABI for x86_64: {r:?}"), + } + } + "arm" => { + // On ARM, ABI handling is reasonably sane; we use `llvm_floatabi` to indicate + // to LLVM which ABI we are going for. + match self.llvm_floatabi.unwrap() { + FloatAbi::Soft => { + // Nothing special required, will use soft-float ABI throughout. + // We can even allow `-soft-float` here; in fact that is useful as it lets + // people use FPU instructions with a softfloat ABI (corresponds to + // `-mfloat-abi=softfp` in GCC/clang). + NOTHING + } + FloatAbi::Hard => { + // Must have `fpregs` and must not have `soft-float`. + FeatureConstraints { required: &["fpregs"], incompatible: &["soft-float"] } + } + } + } + "aarch64" | "arm64ec" => { + // Aarch64 has no sane ABI specifier, and LLVM doesn't even have a way to force + // the use of soft-float, so all we can do here is some crude hacks. + match &*self.abi { + "softfloat" => { + // This is not fully correct, LLVM actually doesn't let us enforce the softfloat + // ABI properly... see . + // FIXME: should we forbid "neon" here? But that would be a breaking change. + NOTHING + } + _ => { + // Everything else is assumed to use a hardfloat ABI. neon and fp-armv8 must be enabled. + // These are Rust feature names and we use "neon" to control both of them. + FeatureConstraints { required: &["neon"], incompatible: &[] } + } + } + } + "riscv32" | "riscv64" => { + // RISC-V handles ABI in a very sane way, being fully explicit via `llvm_abiname` + // about what the intended ABI is. + match &*self.llvm_abiname { + "ilp32d" | "lp64d" => { + // Requires d (which implies f), incompatible with e. + FeatureConstraints { required: &["d"], incompatible: &["e"] } + } + "ilp32f" | "lp64f" => { + // Requires f, incompatible with e. + FeatureConstraints { required: &["f"], incompatible: &["e"] } + } + "ilp32" | "lp64" => { + // Requires nothing, incompatible with e. + FeatureConstraints { required: &[], incompatible: &["e"] } + } + "ilp32e" => { + // ilp32e is documented to be incompatible with features that need aligned + // load/stores > 32 bits, like `d`. (One could also just generate more + // complicated code to align the stack when needed, but the RISCV + // architecture manual just explicitly rules out this combination so we + // might as well.) + // Note that the `e` feature is not required: the ABI treats the extra + // registers as caller-save, so it is safe to use them only in some parts of + // a program while the rest doesn't know they even exist. + FeatureConstraints { required: &[], incompatible: &["d"] } + } + "lp64e" => { + // As above, `e` is not required. + NOTHING + } + _ => unreachable!(), + } + } + _ => NOTHING, + } + } } diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index 1ab89ecde7a44..055a3edcc3293 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -148,8 +148,6 @@ trait_selection_dtcs_has_req_note = the used `impl` has a `'static` requirement trait_selection_dtcs_introduces_requirement = calling this method introduces the `impl`'s `'static` requirement trait_selection_dtcs_suggestion = consider relaxing the implicit `'static` requirement -trait_selection_dump_vtable_entries = vtable entries for `{$trait_ref}`: {$entries} - trait_selection_empty_on_clause_in_rustc_on_unimplemented = empty `on`-clause in `#[rustc_on_unimplemented]` .label = empty on-clause here @@ -165,6 +163,8 @@ trait_selection_explicit_lifetime_required_with_param_type = explicit lifetime r trait_selection_fn_consider_casting = consider casting the fn item to a fn pointer: `{$casting}` +trait_selection_fn_consider_casting_both = consider casting both fn items to fn pointers using `as {$sig}` + trait_selection_fn_uniq_types = different fn items have unique types, even if their signatures are the same trait_selection_fps_cast = consider casting to a fn pointer trait_selection_fps_cast_both = consider casting both fn items to fn pointers using `as {$expected_sig}` @@ -251,7 +251,9 @@ trait_selection_no_value_in_rustc_on_unimplemented = this attribute must have a trait_selection_nothing = {""} -trait_selection_oc_cant_coerce = cannot coerce intrinsics to function pointers +trait_selection_oc_cant_coerce_force_inline = + cannot coerce functions which must be inlined to function pointers +trait_selection_oc_cant_coerce_intrinsic = cannot coerce intrinsics to function pointers trait_selection_oc_closure_selfref = closure/coroutine type that references itself trait_selection_oc_const_compat = const not compatible with trait trait_selection_oc_fn_lang_correct_type = {$lang_item_name -> @@ -259,7 +261,6 @@ trait_selection_oc_fn_lang_correct_type = {$lang_item_name -> *[lang_item_name] lang item `{$lang_item_name}` } function has wrong type trait_selection_oc_fn_main_correct_type = `main` function has wrong type -trait_selection_oc_fn_start_correct_type = `#[start]` function has wrong type trait_selection_oc_generic = mismatched types trait_selection_oc_if_else_different = `if` and `else` have incompatible types @@ -394,7 +395,6 @@ trait_selection_subtype = ...so that the {$requirement -> [if_else_different] `if` and `else` have incompatible types [no_else] `if` missing an `else` returns `()` [fn_main_correct_type] `main` function has the correct type - [fn_start_correct_type] `#[start]` function has the correct type [fn_lang_correct_type] lang item function has the correct type [intrinsic_correct_type] intrinsic has the correct type [method_correct_type] method receiver has the correct type @@ -408,7 +408,6 @@ trait_selection_subtype_2 = ...so that {$requirement -> [if_else_different] `if` and `else` have incompatible types [no_else] `if` missing an `else` returns `()` [fn_main_correct_type] `main` function has the correct type - [fn_start_correct_type] `#[start]` function has the correct type [fn_lang_correct_type] lang item function has the correct type [intrinsic_correct_type] intrinsic has the correct type [method_correct_type] method receiver has the correct type diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index ee5ce19cb4df6..091773009e98d 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -196,7 +196,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0]; self.tcx - .explicit_item_super_predicates(def_id) + .explicit_item_self_bounds(def_id) .iter_instantiated_copied(self.tcx, args) .find_map(|(predicate, _)| { predicate @@ -457,6 +457,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { format!("this is an iterator with items of type `{}`", args.type_at(0)), ); } else { + let expected_ty = self.tcx.short_string(expected_ty, err.long_ty_path()); err.span_label(span, format!("this expression has type `{expected_ty}`")); } } @@ -620,6 +621,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }) => { let then_span = self.find_block_span_from_hir_id(then_id); let else_span = self.find_block_span_from_hir_id(else_id); + if let hir::Node::Expr(e) = self.tcx.hir_node(else_id) + && let hir::ExprKind::If(_cond, _then, None) = e.kind + && else_ty.is_unit() + { + // Account for `let x = if a { 1 } else if b { 2 };` + err.note("`if` expressions without `else` evaluate to `()`"); + err.note("consider adding an `else` block that evaluates to the expected type"); + } err.span_label(then_span, "expected because of this"); if let Some(sp) = outer_span { err.span_label(sp, "`if` and `else` have incompatible types"); @@ -709,53 +718,47 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { value: &mut DiagStyledString, other_value: &mut DiagStyledString, name: String, - sub: ty::GenericArgsRef<'tcx>, + args: &[ty::GenericArg<'tcx>], pos: usize, other_ty: Ty<'tcx>, ) { // `value` and `other_value` hold two incomplete type representation for display. // `name` is the path of both types being compared. `sub` value.push_highlighted(name); - let len = sub.len(); - if len > 0 { - value.push_highlighted("<"); - } - // Output the lifetimes for the first type - let lifetimes = sub - .regions() - .map(|lifetime| { - let s = lifetime.to_string(); - if s.is_empty() { "'_".to_string() } else { s } - }) - .collect::>() - .join(", "); - if !lifetimes.is_empty() { - if sub.regions().count() < len { - value.push_normal(lifetimes + ", "); - } else { - value.push_normal(lifetimes); - } + if args.is_empty() { + return; } + value.push_highlighted("<"); - // Highlight all the type arguments that aren't at `pos` and compare the type argument at - // `pos` and `other_ty`. - for (i, type_arg) in sub.types().enumerate() { - if i == pos { - let values = self.cmp(type_arg, other_ty); - value.0.extend((values.0).0); - other_value.0.extend((values.1).0); - } else { - value.push_highlighted(type_arg.to_string()); + for (i, arg) in args.iter().enumerate() { + if i > 0 { + value.push_normal(", "); } - if len > 0 && i != len - 1 { - value.push_normal(", "); + match arg.unpack() { + ty::GenericArgKind::Lifetime(lt) => { + let s = lt.to_string(); + value.push_normal(if s.is_empty() { "'_" } else { &s }); + } + ty::GenericArgKind::Const(ct) => { + value.push_normal(ct.to_string()); + } + // Highlight all the type arguments that aren't at `pos` and compare + // the type argument at `pos` and `other_ty`. + ty::GenericArgKind::Type(type_arg) => { + if i == pos { + let values = self.cmp(type_arg, other_ty); + value.0.extend((values.0).0); + other_value.0.extend((values.1).0); + } else { + value.push_highlighted(type_arg.to_string()); + } + } } } - if len > 0 { - value.push_highlighted(">"); - } + + value.push_highlighted(">"); } /// If `other_ty` is the same as a type argument present in `sub`, highlight `path` in `t1_out`, @@ -783,27 +786,26 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { t1_out: &mut DiagStyledString, t2_out: &mut DiagStyledString, path: String, - sub: &'tcx [ty::GenericArg<'tcx>], + args: &'tcx [ty::GenericArg<'tcx>], other_path: String, other_ty: Ty<'tcx>, - ) -> Option<()> { - // FIXME/HACK: Go back to `GenericArgsRef` to use its inherent methods, - // ideally that shouldn't be necessary. - let sub = self.tcx.mk_args(sub); - for (i, ta) in sub.types().enumerate() { - if ta == other_ty { - self.highlight_outer(t1_out, t2_out, path, sub, i, other_ty); - return Some(()); - } - if let ty::Adt(def, _) = ta.kind() { - let path_ = self.tcx.def_path_str(def.did()); - if path_ == other_path { - self.highlight_outer(t1_out, t2_out, path, sub, i, other_ty); - return Some(()); + ) -> bool { + for (i, arg) in args.iter().enumerate() { + if let Some(ta) = arg.as_type() { + if ta == other_ty { + self.highlight_outer(t1_out, t2_out, path, args, i, other_ty); + return true; + } + if let ty::Adt(def, _) = ta.kind() { + let path_ = self.tcx.def_path_str(def.did()); + if path_ == other_path { + self.highlight_outer(t1_out, t2_out, path, args, i, other_ty); + return true; + } } } } - None + false } /// Adds a `,` to the type representation only if it is appropriate. @@ -811,10 +813,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &self, value: &mut DiagStyledString, other_value: &mut DiagStyledString, - len: usize, pos: usize, ) { - if len > 0 && pos != len - 1 { + if pos > 0 { value.push_normal(", "); other_value.push_normal(", "); } @@ -824,9 +825,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn cmp_fn_sig( &self, sig1: &ty::PolyFnSig<'tcx>, - fn_def1: Option<(DefId, &'tcx [ty::GenericArg<'tcx>])>, + fn_def1: Option<(DefId, Option<&'tcx [ty::GenericArg<'tcx>]>)>, sig2: &ty::PolyFnSig<'tcx>, - fn_def2: Option<(DefId, &'tcx [ty::GenericArg<'tcx>])>, + fn_def2: Option<(DefId, Option<&'tcx [ty::GenericArg<'tcx>]>)>, ) -> (DiagStyledString, DiagStyledString) { let sig1 = &(self.normalize_fn_sig)(*sig1); let sig2 = &(self.normalize_fn_sig)(*sig2); @@ -850,8 +851,20 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // unsafe extern "C" for<'a> fn(&'a T) -> &'a T // ^^^^^^ - values.0.push(sig1.safety.prefix_str(), sig1.safety != sig2.safety); - values.1.push(sig2.safety.prefix_str(), sig1.safety != sig2.safety); + let safety = |fn_def, sig: ty::FnSig<'_>| match fn_def { + None => sig.safety.prefix_str(), + Some((did, _)) => { + if self.tcx.codegen_fn_attrs(did).safe_target_features { + "#[target_features] " + } else { + sig.safety.prefix_str() + } + } + }; + let safety1 = safety(fn_def1, sig1); + let safety2 = safety(fn_def2, sig2); + values.0.push(safety1, safety1 != safety2); + values.1.push(safety2, safety1 != safety2); // unsafe extern "C" for<'a> fn(&'a T) -> &'a T // ^^^^^^^^^^ @@ -879,10 +892,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let len2 = sig2.inputs().len(); if len1 == len2 { for (i, (l, r)) in iter::zip(sig1.inputs(), sig2.inputs()).enumerate() { + self.push_comma(&mut values.0, &mut values.1, i); let (x1, x2) = self.cmp(*l, *r); (values.0).0.extend(x1.0); (values.1).0.extend(x2.0); - self.push_comma(&mut values.0, &mut values.1, len1, i); } } else { for (i, l) in sig1.inputs().iter().enumerate() { @@ -932,23 +945,23 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { (values.1).0.extend(x2.0); } - let fmt = |(did, args)| format!(" {{{}}}", self.tcx.def_path_str_with_args(did, args)); + let fmt = |did, args| format!(" {{{}}}", self.tcx.def_path_str_with_args(did, args)); match (fn_def1, fn_def2) { - (None, None) => {} - (Some(fn_def1), Some(fn_def2)) => { - let path1 = fmt(fn_def1); - let path2 = fmt(fn_def2); + (Some((fn_def1, Some(fn_args1))), Some((fn_def2, Some(fn_args2)))) => { + let path1 = fmt(fn_def1, fn_args1); + let path2 = fmt(fn_def2, fn_args2); let same_path = path1 == path2; values.0.push(path1, !same_path); values.1.push(path2, !same_path); } - (Some(fn_def1), None) => { - values.0.push_highlighted(fmt(fn_def1)); + (Some((fn_def1, Some(fn_args1))), None) => { + values.0.push_highlighted(fmt(fn_def1, fn_args1)); } - (None, Some(fn_def2)) => { - values.1.push_highlighted(fmt(fn_def2)); + (None, Some((fn_def2, Some(fn_args2)))) => { + values.1.push_highlighted(fmt(fn_def2, fn_args2)); } + _ => {} } values @@ -1130,14 +1143,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let len1 = sub_no_defaults_1.len(); let len2 = sub_no_defaults_2.len(); let common_len = cmp::min(len1, len2); - let remainder1: Vec<_> = sub1.types().skip(common_len).collect(); - let remainder2: Vec<_> = sub2.types().skip(common_len).collect(); + let remainder1 = &sub1[common_len..]; + let remainder2 = &sub2[common_len..]; let common_default_params = iter::zip(remainder1.iter().rev(), remainder2.iter().rev()) .filter(|(a, b)| a == b) .count(); let len = sub1.len() - common_default_params; - let consts_offset = len - sub1.consts().count(); // Only draw `<...>` if there are lifetime/type arguments. if len > 0 { @@ -1149,70 +1161,68 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let s = lifetime.to_string(); if s.is_empty() { "'_".to_string() } else { s } } - // At one point we'd like to elide all lifetimes here, they are irrelevant for - // all diagnostics that use this output - // - // Foo<'x, '_, Bar> - // Foo<'y, '_, Qux> - // ^^ ^^ --- type arguments are not elided - // | | - // | elided as they were the same - // not elided, they were different, but irrelevant - // - // For bound lifetimes, keep the names of the lifetimes, - // even if they are the same so that it's clear what's happening - // if we have something like - // - // for<'r, 's> fn(Inv<'r>, Inv<'s>) - // for<'r> fn(Inv<'r>, Inv<'r>) - let lifetimes = sub1.regions().zip(sub2.regions()); - for (i, lifetimes) in lifetimes.enumerate() { - let l1 = lifetime_display(lifetimes.0); - let l2 = lifetime_display(lifetimes.1); - if lifetimes.0 != lifetimes.1 { - values.0.push_highlighted(l1); - values.1.push_highlighted(l2); - } else if lifetimes.0.is_bound() || self.tcx.sess.opts.verbose { - values.0.push_normal(l1); - values.1.push_normal(l2); - } else { - values.0.push_normal("'_"); - values.1.push_normal("'_"); - } - self.push_comma(&mut values.0, &mut values.1, len, i); - } - // We're comparing two types with the same path, so we compare the type - // arguments for both. If they are the same, do not highlight and elide from the - // output. - // Foo<_, Bar> - // Foo<_, Qux> - // ^ elided type as this type argument was the same in both sides - let type_arguments = sub1.types().zip(sub2.types()); - let regions_len = sub1.regions().count(); - let num_display_types = consts_offset - regions_len; - for (i, (ta1, ta2)) in type_arguments.take(num_display_types).enumerate() { - let i = i + regions_len; - if ta1 == ta2 && !self.tcx.sess.opts.verbose { - values.0.push_normal("_"); - values.1.push_normal("_"); - } else { - recurse(ta1, ta2, &mut values); + for (i, (arg1, arg2)) in sub1.iter().zip(sub2).enumerate().take(len) { + self.push_comma(&mut values.0, &mut values.1, i); + match arg1.unpack() { + // At one point we'd like to elide all lifetimes here, they are + // irrelevant for all diagnostics that use this output. + // + // Foo<'x, '_, Bar> + // Foo<'y, '_, Qux> + // ^^ ^^ --- type arguments are not elided + // | | + // | elided as they were the same + // not elided, they were different, but irrelevant + // + // For bound lifetimes, keep the names of the lifetimes, + // even if they are the same so that it's clear what's happening + // if we have something like + // + // for<'r, 's> fn(Inv<'r>, Inv<'s>) + // for<'r> fn(Inv<'r>, Inv<'r>) + ty::GenericArgKind::Lifetime(l1) => { + let l1_str = lifetime_display(l1); + let l2 = arg2.expect_region(); + let l2_str = lifetime_display(l2); + if l1 != l2 { + values.0.push_highlighted(l1_str); + values.1.push_highlighted(l2_str); + } else if l1.is_bound() || self.tcx.sess.opts.verbose { + values.0.push_normal(l1_str); + values.1.push_normal(l2_str); + } else { + values.0.push_normal("'_"); + values.1.push_normal("'_"); + } + } + ty::GenericArgKind::Type(ta1) => { + let ta2 = arg2.expect_ty(); + if ta1 == ta2 && !self.tcx.sess.opts.verbose { + values.0.push_normal("_"); + values.1.push_normal("_"); + } else { + recurse(ta1, ta2, &mut values); + } + } + // We're comparing two types with the same path, so we compare the type + // arguments for both. If they are the same, do not highlight and elide + // from the output. + // Foo<_, Bar> + // Foo<_, Qux> + // ^ elided type as this type argument was the same in both sides + + // Do the same for const arguments, if they are equal, do not highlight and + // elide them from the output. + ty::GenericArgKind::Const(ca1) => { + let ca2 = arg2.expect_const(); + maybe_highlight(ca1, ca2, &mut values, self.tcx); + } } - self.push_comma(&mut values.0, &mut values.1, len, i); - } - - // Do the same for const arguments, if they are equal, do not highlight and - // elide them from the output. - let const_arguments = sub1.consts().zip(sub2.consts()); - for (i, (ca1, ca2)) in const_arguments.enumerate() { - let i = i + consts_offset; - maybe_highlight(ca1, ca2, &mut values, self.tcx); - self.push_comma(&mut values.0, &mut values.1, len, i); } // Close the type argument bracket. - // Only draw `<...>` if there are lifetime/type arguments. + // Only draw `<...>` if there are arguments. if len > 0 { values.0.push_normal(">"); values.1.push_normal(">"); @@ -1224,17 +1234,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Foo // ------- this type argument is exactly the same as the other type // Bar - if self - .cmp_type_arg( - &mut values.0, - &mut values.1, - path1.clone(), - sub_no_defaults_1, - path2.clone(), - t2, - ) - .is_some() - { + if self.cmp_type_arg( + &mut values.0, + &mut values.1, + path1.clone(), + sub_no_defaults_1, + path2.clone(), + t2, + ) { return values; } // Check for case: @@ -1242,17 +1249,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Bar // Foo> // ------- this type argument is exactly the same as the other type - if self - .cmp_type_arg( - &mut values.1, - &mut values.0, - path2, - sub_no_defaults_2, - path1, - t1, - ) - .is_some() - { + if self.cmp_type_arg( + &mut values.1, + &mut values.0, + path2, + sub_no_defaults_2, + path1, + t1, + ) { return values; } @@ -1323,8 +1327,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let mut values = (DiagStyledString::normal("("), DiagStyledString::normal("(")); let len = args1.len(); for (i, (left, right)) in args1.iter().zip(args2).enumerate() { + self.push_comma(&mut values.0, &mut values.1, i); recurse(left, right, &mut values); - self.push_comma(&mut values.0, &mut values.1, len, i); } if len == 1 { // Keep the output for single element tuples as `(ty,)`. @@ -1339,17 +1343,22 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { (ty::FnDef(did1, args1), ty::FnDef(did2, args2)) => { let sig1 = self.tcx.fn_sig(*did1).instantiate(self.tcx, args1); let sig2 = self.tcx.fn_sig(*did2).instantiate(self.tcx, args2); - self.cmp_fn_sig(&sig1, Some((*did1, args1)), &sig2, Some((*did2, args2))) + self.cmp_fn_sig( + &sig1, + Some((*did1, Some(args1))), + &sig2, + Some((*did2, Some(args2))), + ) } (ty::FnDef(did1, args1), ty::FnPtr(sig_tys2, hdr2)) => { let sig1 = self.tcx.fn_sig(*did1).instantiate(self.tcx, args1); - self.cmp_fn_sig(&sig1, Some((*did1, args1)), &sig_tys2.with(*hdr2), None) + self.cmp_fn_sig(&sig1, Some((*did1, Some(args1))), &sig_tys2.with(*hdr2), None) } (ty::FnPtr(sig_tys1, hdr1), ty::FnDef(did2, args2)) => { let sig2 = self.tcx.fn_sig(*did2).instantiate(self.tcx, args2); - self.cmp_fn_sig(&sig_tys1.with(*hdr1), None, &sig2, Some((*did2, args2))) + self.cmp_fn_sig(&sig_tys1.with(*hdr1), None, &sig2, Some((*did2, Some(args2)))) } (ty::FnPtr(sig_tys1, hdr1), ty::FnPtr(sig_tys2, hdr2)) => { @@ -1382,9 +1391,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { mut values: Option>>, terr: TypeError<'tcx>, prefer_label: bool, + override_span: Option, ) { - let span = cause.span; - + // We use `override_span` when we want the error to point at a `Span` other than + // `cause.span`. This is used in E0271, when a closure is passed in where the return type + // isn't what was expected. We want to point at the closure's return type (or expression), + // instead of the expression where the closure is passed as call argument. + let span = override_span.unwrap_or(cause.span); // For some types of errors, expected-found does not make // sense, so just ignore the values we were given. if let TypeError::CyclicTy(_) = terr { @@ -1496,8 +1509,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ValuePairs::Terms(ExpectedFound { expected, found }) => { match (expected.unpack(), found.unpack()) { (ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => { - let is_simple_err = expected.is_simple_text(self.tcx) - && found.is_simple_text(self.tcx); + let is_simple_err = + expected.is_simple_text() && found.is_simple_text(); OpaqueTypesVisitor::visit_expected_found( self.tcx, expected, found, span, ) @@ -1531,7 +1544,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { (false, Mismatch::Fixed("existential projection")) } }; - let Some(vals) = self.values_str(values) else { + let Some(vals) = self.values_str(values, cause, diag.long_ty_path()) else { // Derived error. Cancel the emitter. // NOTE(eddyb) this was `.cancel()`, but `diag` // is borrowed, so we can't fully defuse it. @@ -1586,7 +1599,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return; } - if let Some((expected, found, path)) = expected_found { + if let Some((expected, found)) = expected_found { let (expected_label, found_label, exp_found) = match exp_found { Mismatch::Variable(ef) => ( ef.expected.prefix_string(self.tcx), @@ -1709,35 +1722,44 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } TypeError::Sorts(values) => { - let extra = expected == found; + let extra = expected == found + // Ensure that we don't ever say something like + // expected `impl Trait` (opaque type `impl Trait`) + // found `impl Trait` (opaque type `impl Trait`) + && values.expected.sort_string(self.tcx) + != values.found.sort_string(self.tcx); let sort_string = |ty: Ty<'tcx>| match (extra, ty.kind()) { (true, ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) => { let sm = self.tcx.sess.source_map(); let pos = sm.lookup_char_pos(self.tcx.def_span(*def_id).lo()); - format!( + DiagStyledString::normal(format!( " (opaque type at <{}:{}:{}>)", sm.filename_for_diagnostics(&pos.file.name), pos.line, pos.col.to_usize() + 1, - ) + )) } (true, ty::Alias(ty::Projection, proj)) if self.tcx.is_impl_trait_in_trait(proj.def_id) => { let sm = self.tcx.sess.source_map(); let pos = sm.lookup_char_pos(self.tcx.def_span(proj.def_id).lo()); - format!( + DiagStyledString::normal(format!( " (trait associated opaque type at <{}:{}:{}>)", sm.filename_for_diagnostics(&pos.file.name), pos.line, pos.col.to_usize() + 1, - ) + )) } - (true, _) => format!(" ({})", ty.sort_string(self.tcx)), - (false, _) => "".to_string(), + (true, _) => { + let mut s = DiagStyledString::normal(" ("); + s.push_highlighted(ty.sort_string(self.tcx)); + s.push_normal(")"); + s + } + (false, _) => DiagStyledString::normal(""), }; - if !(values.expected.is_simple_text(self.tcx) - && values.found.is_simple_text(self.tcx)) + if !(values.expected.is_simple_text() && values.found.is_simple_text()) || (exp_found.is_some_and(|ef| { // This happens when the type error is a subset of the expectation, // like when you have two references but one is `usize` and the other @@ -1752,30 +1774,23 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } })) { - if let Some(ExpectedFound { found: found_ty, .. }) = exp_found { + if let Some(ExpectedFound { found: found_ty, .. }) = exp_found + && !self.tcx.ty_is_opaque_future(found_ty) + { // `Future` is a special opaque type that the compiler // will try to hide in some case such as `async fn`, so // to make an error more use friendly we will // avoid to suggest a mismatch type with a // type that the user usually are not using // directly such as `impl Future`. - if !self.tcx.ty_is_opaque_future(found_ty) { - diag.note_expected_found_extra( - &expected_label, - expected, - &found_label, - found, - &sort_string(values.expected), - &sort_string(values.found), - ); - if let Some(path) = path { - diag.note(format!( - "the full type name has been written to '{}'", - path.display(), - )); - diag.note("consider using `--verbose` to print the full type name to the console"); - } - } + diag.note_expected_found_extra( + &expected_label, + expected, + &found_label, + found, + sort_string(values.expected), + sort_string(values.found), + ); } } } @@ -1840,7 +1855,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.suggest_tuple_pattern(cause, &exp_found, diag); self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag); self.suggest_await_on_expect_found(cause, span, &exp_found, diag); - self.suggest_function_pointers(cause, span, &exp_found, diag); + self.suggest_function_pointers(cause, span, &exp_found, terr, diag); self.suggest_turning_stmt_into_expr(cause, &exp_found, diag); } } @@ -1879,6 +1894,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &self, trace: &TypeTrace<'tcx>, terr: TypeError<'tcx>, + path: &mut Option, ) -> Vec { let mut suggestions = Vec::new(); let span = trace.cause.span; @@ -1957,7 +1973,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }) | ObligationCauseCode::BlockTailExpression(.., source)) = code && let hir::MatchSource::TryDesugar(_) = source - && let Some((expected_ty, found_ty, _)) = self.values_str(trace.values) + && let Some((expected_ty, found_ty)) = self.values_str(trace.values, &trace.cause, path) { suggestions.push(TypeErrorAdditionalDiags::TryCannotConvert { found: found_ty.content(), @@ -1978,7 +1994,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return None; }; let tykind = match self.tcx.hir_node_by_def_id(trace.cause.body_id) { - hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) => { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn { body: body_id, .. }, .. + }) => { let body = hir.body(*body_id); struct LetVisitor { span: Span, @@ -2013,14 +2031,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { _ => None, }; if let Some(tykind) = tykind - && let hir::TyKind::Array(_, length) = tykind - && let Some((scalar, ty)) = sz.found.try_to_scalar() - && ty == self.tcx.types.usize + && let hir::TyKind::Array(_, length_arg) = tykind + && let Some(length_val) = sz.found.try_to_target_usize(self.tcx) { - let span = length.span(); Some(TypeErrorAdditionalDiags::ConsiderSpecifyingLength { - span, - length: scalar.to_target_usize(&self.tcx).unwrap(), + span: length_arg.span(), + length: length_val, }) } else { None @@ -2036,12 +2052,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { debug!("report_and_explain_type_error(trace={:?}, terr={:?})", trace, terr); let span = trace.cause.span; + let mut path = None; let failure_code = trace.cause.as_failure_code_diag( terr, span, - self.type_error_additional_suggestions(&trace, terr), + self.type_error_additional_suggestions(&trace, terr, &mut path), ); let mut diag = self.dcx().create_err(failure_code); + *diag.long_ty_path() = path; self.note_type_err( &mut diag, &trace.cause, @@ -2049,6 +2067,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { Some(param_env.and(trace.values)), terr, false, + None, ); diag } @@ -2084,10 +2103,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn values_str( &self, values: ValuePairs<'tcx>, - ) -> Option<(DiagStyledString, DiagStyledString, Option)> { + cause: &ObligationCause<'tcx>, + file: &mut Option, + ) -> Option<(DiagStyledString, DiagStyledString)> { match values { ValuePairs::Regions(exp_found) => self.expected_found_str(exp_found), - ValuePairs::Terms(exp_found) => self.expected_found_str_term(exp_found), + ValuePairs::Terms(exp_found) => self.expected_found_str_term(exp_found, file), ValuePairs::Aliases(exp_found) => self.expected_found_str(exp_found), ValuePairs::ExistentialTraitRef(exp_found) => self.expected_found_str(exp_found), ValuePairs::ExistentialProjection(exp_found) => self.expected_found_str(exp_found), @@ -2097,7 +2118,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { found: exp_found.found.print_trait_sugared(), }; match self.expected_found_str(pretty_exp_found) { - Some((expected, found, _)) if expected == found => { + Some((expected, found)) if expected == found => { self.expected_found_str(exp_found) } ret => ret, @@ -2108,8 +2129,18 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if exp_found.references_error() { return None; } - let (exp, fnd) = self.cmp_fn_sig(&exp_found.expected, None, &exp_found.found, None); - Some((exp, fnd, None)) + let (fn_def1, fn_def2) = if let ObligationCauseCode::CompareImplItem { + impl_item_def_id, + trait_item_def_id, + .. + } = *cause.code() + { + (Some((trait_item_def_id, None)), Some((impl_item_def_id.to_def_id(), None))) + } else { + (None, None) + }; + + Some(self.cmp_fn_sig(&exp_found.expected, fn_def1, &exp_found.found, fn_def2)) } } } @@ -2117,7 +2148,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn expected_found_str_term( &self, exp_found: ty::error::ExpectedFound>, - ) -> Option<(DiagStyledString, DiagStyledString, Option)> { + path: &mut Option, + ) -> Option<(DiagStyledString, DiagStyledString)> { let exp_found = self.resolve_vars_if_possible(exp_found); if exp_found.references_error() { return None; @@ -2132,21 +2164,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let len = self.tcx.sess().diagnostic_width() + 40; let exp_s = exp.content(); let fnd_s = fnd.content(); - let mut path = None; if exp_s.len() > len { - let exp_s = self.tcx.short_ty_string(expected, &mut path); + let exp_s = self.tcx.short_string(expected, path); exp = DiagStyledString::highlighted(exp_s); } if fnd_s.len() > len { - let fnd_s = self.tcx.short_ty_string(found, &mut path); + let fnd_s = self.tcx.short_string(found, path); fnd = DiagStyledString::highlighted(fnd_s); } - (exp, fnd, path) + (exp, fnd) } _ => ( DiagStyledString::highlighted(exp_found.expected.to_string()), DiagStyledString::highlighted(exp_found.found.to_string()), - None, ), }) } @@ -2155,7 +2185,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn expected_found_str>>( &self, exp_found: ty::error::ExpectedFound, - ) -> Option<(DiagStyledString, DiagStyledString, Option)> { + ) -> Option<(DiagStyledString, DiagStyledString)> { let exp_found = self.resolve_vars_if_possible(exp_found); if exp_found.references_error() { return None; @@ -2164,7 +2194,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { Some(( DiagStyledString::highlighted(exp_found.expected.to_string()), DiagStyledString::highlighted(exp_found.found.to_string()), - None, )) } @@ -2279,7 +2308,6 @@ impl<'tcx> ObligationCause<'tcx> { | ObligationCauseCode::MatchExpressionArm(_) | ObligationCauseCode::IfExpression { .. } | ObligationCauseCode::LetElse - | ObligationCauseCode::StartFunctionType | ObligationCauseCode::LangFunctionType(_) | ObligationCauseCode::IntrinsicType | ObligationCauseCode::MethodReceiver => FailureCode::Error0308, @@ -2293,7 +2321,7 @@ impl<'tcx> ObligationCause<'tcx> { { FailureCode::Error0644 } - TypeError::IntrinsicCast => FailureCode::Error0308, + TypeError::IntrinsicCast | TypeError::ForceInlineCast => FailureCode::Error0308, _ => FailureCode::Error0308, }, } @@ -2337,9 +2365,6 @@ impl<'tcx> ObligationCause<'tcx> { ObligationCauseCode::MainFunctionType => { ObligationCauseFailureCode::FnMainCorrectType { span } } - ObligationCauseCode::StartFunctionType => { - ObligationCauseFailureCode::FnStartCorrectType { span, subdiags } - } &ObligationCauseCode::LangFunctionType(lang_item_name) => { ObligationCauseFailureCode::FnLangCorrectType { span, subdiags, lang_item_name } } @@ -2359,8 +2384,11 @@ impl<'tcx> ObligationCause<'tcx> { { ObligationCauseFailureCode::ClosureSelfref { span } } + TypeError::ForceInlineCast => { + ObligationCauseFailureCode::CantCoerceForceInline { span, subdiags } + } TypeError::IntrinsicCast => { - ObligationCauseFailureCode::CantCoerce { span, subdiags } + ObligationCauseFailureCode::CantCoerceIntrinsic { span, subdiags } } _ => ObligationCauseFailureCode::Generic { span, subdiags }, }, @@ -2379,7 +2407,6 @@ impl<'tcx> ObligationCause<'tcx> { "const is compatible with trait" } ObligationCauseCode::MainFunctionType => "`main` function has the correct type", - ObligationCauseCode::StartFunctionType => "`#[start]` function has the correct type", ObligationCauseCode::LangFunctionType(_) => "lang item function has the correct type", ObligationCauseCode::IntrinsicType => "intrinsic has the correct type", ObligationCauseCode::MethodReceiver => "method receiver has the correct type", @@ -2400,7 +2427,6 @@ impl IntoDiagArg for ObligationCauseAsDiagArg<'_> { "const_compat" } ObligationCauseCode::MainFunctionType => "fn_main_correct_type", - ObligationCauseCode::StartFunctionType => "fn_start_correct_type", ObligationCauseCode::LangFunctionType(_) => "fn_lang_correct_type", ObligationCauseCode::IntrinsicType => "intrinsic_correct_type", ObligationCauseCode::MethodReceiver => "method_correct_type", diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index 68a5338123049..9e7e96dddd7c1 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -18,6 +18,8 @@ use rustc_middle::ty::{ TypeFoldable, TypeFolder, TypeSuperFoldable, TypeckResults, }; use rustc_span::{BytePos, DUMMY_SP, FileName, Ident, Span, sym}; +use rustc_type_ir::inherent::*; +use rustc_type_ir::visit::TypeVisitableExt; use tracing::{debug, instrument, warn}; use super::nice_region_error::placeholder_error::Highlighted; @@ -155,27 +157,92 @@ impl UnderspecifiedArgKind { } } -struct ClosureEraser<'tcx> { - tcx: TyCtxt<'tcx>, +struct ClosureEraser<'a, 'tcx> { + infcx: &'a InferCtxt<'tcx>, } -impl<'tcx> TypeFolder> for ClosureEraser<'tcx> { +impl<'a, 'tcx> ClosureEraser<'a, 'tcx> { + fn new_infer(&mut self) -> Ty<'tcx> { + self.infcx.next_ty_var(DUMMY_SP) + } +} + +impl<'a, 'tcx> TypeFolder> for ClosureEraser<'a, 'tcx> { fn cx(&self) -> TyCtxt<'tcx> { - self.tcx + self.infcx.tcx } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { match ty.kind() { ty::Closure(_, args) => { + // For a closure type, we turn it into a function pointer so that it gets rendered + // as `fn(args) -> Ret`. let closure_sig = args.as_closure().sig(); Ty::new_fn_ptr( - self.tcx, - self.tcx.signature_unclosure(closure_sig, hir::Safety::Safe), + self.cx(), + self.cx().signature_unclosure(closure_sig, hir::Safety::Safe), ) } - _ => ty.super_fold_with(self), + ty::Adt(_, args) if !args.iter().any(|a| a.has_infer()) => { + // We have a type that doesn't have any inference variables, so we replace + // the whole thing with `_`. The type system already knows about this type in + // its entirety and it is redundant to specify it for the user. The user only + // needs to specify the type parameters that we *couldn't* figure out. + self.new_infer() + } + ty::Adt(def, args) => { + let generics = self.cx().generics_of(def.did()); + let generics: Vec = generics + .own_params + .iter() + .map(|param| param.default_value(self.cx()).is_some()) + .collect(); + let ty = Ty::new_adt( + self.cx(), + *def, + self.cx().mk_args_from_iter(generics.into_iter().zip(args.iter()).map( + |(has_default, arg)| { + if arg.has_infer() { + // This param has an unsubstituted type variable, meaning that this + // type has a (potentially deeply nested) type parameter from the + // corresponding type's definition. We have explicitly asked this + // type to not be hidden. In either case, we keep the type and don't + // substitute with `_` just yet. + arg.fold_with(self) + } else if has_default { + // We have a type param that has a default type, like the allocator + // in Vec. We decided to show `Vec` itself, because it hasn't yet + // been replaced by an `_` `Infer`, but we want to ensure that the + // type parameter with default types does *not* get replaced with + // `_` because then we'd end up with `Vec<_, _>`, instead of + // `Vec<_>`. + arg + } else if let GenericArgKind::Type(_) = arg.kind() { + // We don't replace lifetime or const params, only type params. + self.new_infer().into() + } else { + arg.fold_with(self) + } + }, + )), + ); + ty + } + _ if ty.has_infer() => { + // This type has a (potentially nested) type parameter that we couldn't figure out. + // We will print this depth of type, so at least the type name and at least one of + // its type parameters. + ty.super_fold_with(self) + } + // We don't have an unknown type parameter anywhere, replace with `_`. + _ => self.new_infer(), } } + + fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { + // Avoid accidentally erasing the type of the const. + c + } } fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'tcx>, ns: Namespace) -> FmtPrinter<'a, 'tcx> { @@ -219,9 +286,9 @@ fn ty_to_string<'tcx>( ) -> String { let mut printer = fmt_printer(infcx, Namespace::TypeNS); let ty = infcx.resolve_vars_if_possible(ty); - // We use `fn` ptr syntax for closures, but this only works when the closure - // does not capture anything. - let ty = ty.fold_with(&mut ClosureEraser { tcx: infcx.tcx }); + // We use `fn` ptr syntax for closures, but this only works when the closure does not capture + // anything. We also remove all type parameters that are fully known to the type system. + let ty = ty.fold_with(&mut ClosureEraser { infcx }); match (ty.kind(), called_method_def_id) { // We don't want the regular output for `fn`s because it includes its path in @@ -713,7 +780,7 @@ impl<'tcx> InferSourceKind<'tcx> { if ty.is_closure() { ("closure", closure_as_fn_str(infcx, ty), path) } else if !ty.is_ty_or_numeric_infer() { - ("normal", infcx.tcx.short_ty_string(ty, &mut path), path) + ("normal", infcx.tcx.short_string(ty, &mut path), path) } else { ("other", String::new(), path) } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs index 2a487a48a8e87..b9f3abc2534ad 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs @@ -1,8 +1,8 @@ use core::ops::ControlFlow; -use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::intravisit::{self, Visitor, VisitorExt}; +use rustc_hir::{self as hir, AmbigArg}; use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_bound_vars as rbv; @@ -48,7 +48,7 @@ fn find_component_for_bound_region<'tcx>( region_def_id: DefId, ) -> Option<&'tcx hir::Ty<'tcx>> { FindNestedTypeVisitor { tcx, region_def_id, current_index: ty::INNERMOST } - .visit_ty(arg) + .visit_ty_unambig(arg) .break_value() } @@ -74,7 +74,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { self.tcx.hir() } - fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) -> Self::Result { + fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result { match arg.kind { hir::TyKind::BareFn(_) => { self.current_index.shift_in(1); @@ -101,7 +101,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { Some(rbv::ResolvedArg::EarlyBound(id)) => { debug!("EarlyBound id={:?}", id); if id.to_def_id() == self.region_def_id { - return ControlFlow::Break(arg); + return ControlFlow::Break(arg.as_unambig_ty()); } } @@ -117,7 +117,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { if debruijn_index == self.current_index && id.to_def_id() == self.region_def_id { - return ControlFlow::Break(arg); + return ControlFlow::Break(arg.as_unambig_ty()); } } @@ -147,7 +147,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { ) .is_break() { - ControlFlow::Break(arg) + ControlFlow::Break(arg.as_unambig_ty()) } else { ControlFlow::Continue(()) }; @@ -210,7 +210,7 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { ControlFlow::Continue(()) } - fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) -> Self::Result { + fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result { // ignore nested types // // If you have a type like `Foo<'a, &Ty>` we diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs index 54d8a9e25ca7f..886581bc35fd3 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs @@ -4,7 +4,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::VisitorExt; use rustc_middle::bug; use rustc_middle::ty::TypeVisitor; use tracing::debug; @@ -87,7 +87,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { for matching_def_id in v.0 { let mut hir_v = super::static_impl_trait::HirTraitObjectVisitor(&mut traits, matching_def_id); - hir_v.visit_ty(impl_self_ty); + hir_v.visit_ty_unambig(impl_self_ty); } if traits.is_empty() { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs index 3416a17624e91..039e21cb5562d 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs @@ -3,9 +3,9 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, Subdiagnostic}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{Visitor, walk_ty}; +use rustc_hir::intravisit::{Visitor, VisitorExt, walk_ty}; use rustc_hir::{ - self as hir, GenericBound, GenericParam, GenericParamKind, Item, ItemKind, Lifetime, + self as hir, AmbigArg, GenericBound, GenericParam, GenericParamKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, MissingLifetimeKind, Node, TyKind, }; use rustc_middle::ty::{ @@ -153,7 +153,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let mut add_label = true; if let hir::FnRetTy::Return(ty) = fn_decl.output { let mut v = StaticLifetimeVisitor(vec![], tcx.hir()); - v.visit_ty(ty); + v.visit_ty_unambig(ty); if !v.0.is_empty() { span = v.0.clone().into(); spans = v.0; @@ -323,9 +323,12 @@ pub fn suggest_new_region_bound( .params .iter() .filter(|p| { - matches!(p.kind, GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Explicit - }) + matches!( + p.kind, + GenericParamKind::Lifetime { + kind: hir::LifetimeParamKind::Explicit + } + ) }) .map(|p| { if let hir::ParamName::Plain(name) = p.name { @@ -374,7 +377,7 @@ pub fn suggest_new_region_bound( } } } - TyKind::TraitObject(_, lt, _) => { + TyKind::TraitObject(_, lt) => { if let LifetimeName::ImplicitObjectLifetimeDefault = lt.res { err.span_suggestion_verbose( fn_return.span.shrink_to_hi(), @@ -420,8 +423,8 @@ fn make_elided_region_spans_suggs<'a>( let mut process_consecutive_brackets = |span: Option, spans_suggs: &mut Vec<(Span, String)>| { - if span - .is_some_and(|span| bracket_span.map_or(true, |bracket_span| span == bracket_span)) + if let Some(span) = span + && bracket_span.is_none_or(|bracket_span| span == bracket_span) { consecutive_brackets += 1; } else if let Some(bracket_span) = bracket_span.take() { @@ -500,7 +503,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // In that case, only the first one will get suggestions. let mut traits = vec![]; let mut hir_v = HirTraitObjectVisitor(&mut traits, *did); - hir_v.visit_ty(self_ty); + hir_v.visit_ty_unambig(self_ty); !traits.is_empty() }) { @@ -560,7 +563,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { for found_did in found_dids { let mut traits = vec![]; let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did); - hir_v.visit_ty(self_ty); + hir_v.visit_ty_unambig(self_ty); for &span in &traits { let subdiag = DynTraitConstraintSuggestion { span, ident }; subdiag.add_to_diag(err); @@ -591,12 +594,10 @@ impl<'tcx> TypeVisitor> for TraitObjectVisitor { pub struct HirTraitObjectVisitor<'a>(pub &'a mut Vec, pub DefId); impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> { - fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) { - if let TyKind::TraitObject( - poly_trait_refs, - Lifetime { res: LifetimeName::ImplicitObjectLifetimeDefault, .. }, - _, - ) = t.kind + fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) { + if let TyKind::TraitObject(poly_trait_refs, lifetime_ptr) = t.kind + && let Lifetime { res: LifetimeName::ImplicitObjectLifetimeDefault, .. } = + lifetime_ptr.pointer() { for ptr in poly_trait_refs { if Some(self.1) == ptr.trait_ref.trait_def_id() { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs index 95dd1b28a3994..bbcd28c08351f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs @@ -1,10 +1,10 @@ //! Error Reporting for `impl` items that do not match the obligations from their `trait`. use rustc_errors::ErrorGuaranteed; -use rustc_hir as hir; use rustc_hir::def::{Namespace, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::{Visitor, walk_ty}; +use rustc_hir::{self as hir, AmbigArg}; use rustc_middle::hir::nested_filter; use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::error::ExpectedFound; @@ -137,11 +137,13 @@ impl<'tcx> Visitor<'tcx> for TypeParamSpanVisitor<'tcx> { self.tcx.hir() } - fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx, AmbigArg>) { match arg.kind { hir::TyKind::Ref(_, ref mut_ty) => { // We don't want to suggest looking into borrowing `&T` or `&Self`. - hir::intravisit::walk_ty(self, mut_ty.ty); + if let Some(ambig_ty) = mut_ty.ty.try_as_ambig_ty() { + walk_ty(self, ambig_ty); + } return; } hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note.rs deleted file mode 100644 index beae9962f7f28..0000000000000 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note.rs +++ /dev/null @@ -1,422 +0,0 @@ -use rustc_errors::{Diag, Subdiagnostic}; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::traits::ObligationCauseCode; -use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::{self, IsSuggestable, Region, Ty}; -use rustc_span::kw; -use tracing::debug; - -use super::ObligationCauseAsDiagArg; -use crate::error_reporting::infer::{TypeErrCtxt, note_and_explain_region}; -use crate::errors::{ - FulfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, OutlivesContent, RefLongerThanData, - RegionOriginNote, WhereClauseSuggestions, note_and_explain, -}; -use crate::fluent_generated as fluent; -use crate::infer::{self, SubregionOrigin}; - -impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { - pub(super) fn note_region_origin(&self, err: &mut Diag<'_>, origin: &SubregionOrigin<'tcx>) { - match *origin { - infer::Subtype(ref trace) => RegionOriginNote::WithRequirement { - span: trace.cause.span, - requirement: ObligationCauseAsDiagArg(trace.cause.clone()), - expected_found: self.values_str(trace.values).map(|(e, f, _)| (e, f)), - } - .add_to_diag(err), - infer::Reborrow(span) => { - RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diag(err) - } - infer::RelateObjectBound(span) => { - RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound } - .add_to_diag(err); - } - infer::ReferenceOutlivesReferent(ty, span) => { - RegionOriginNote::WithName { - span, - msg: fluent::infer_reference_outlives_referent, - name: &self.ty_to_string(ty), - continues: false, - } - .add_to_diag(err); - } - infer::RelateParamBound(span, ty, opt_span) => { - RegionOriginNote::WithName { - span, - msg: fluent::infer_relate_param_bound, - name: &self.ty_to_string(ty), - continues: opt_span.is_some(), - } - .add_to_diag(err); - if let Some(span) = opt_span { - RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 } - .add_to_diag(err); - } - } - infer::RelateRegionParamBound(span, _) => { - RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound } - .add_to_diag(err); - } - infer::CompareImplItemObligation { span, .. } => { - RegionOriginNote::Plain { span, msg: fluent::infer_compare_impl_item_obligation } - .add_to_diag(err); - } - infer::CheckAssociatedTypeBounds { ref parent, .. } => { - self.note_region_origin(err, parent); - } - infer::AscribeUserTypeProvePredicate(span) => { - RegionOriginNote::Plain { - span, - msg: fluent::infer_ascribe_user_type_prove_predicate, - } - .add_to_diag(err); - } - } - } - - pub(super) fn report_concrete_failure( - &self, - generic_param_scope: LocalDefId, - origin: SubregionOrigin<'tcx>, - sub: Region<'tcx>, - sup: Region<'tcx>, - ) -> Diag<'a> { - let mut err = match origin { - infer::Subtype(box trace) => { - let terr = TypeError::RegionsDoesNotOutlive(sup, sub); - let mut err = self.report_and_explain_type_error(trace, terr); - match (*sub, *sup) { - (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {} - (ty::RePlaceholder(_), _) => { - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "", - sup, - " doesn't meet the lifetime requirements", - None, - ); - } - (_, ty::RePlaceholder(_)) => { - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "the required lifetime does not necessarily outlive ", - sub, - "", - None, - ); - } - _ => { - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "", - sup, - "...", - None, - ); - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "...does not necessarily outlive ", - sub, - "", - None, - ); - } - } - err - } - infer::Reborrow(span) => { - let reference_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::RefValidFor, - note_and_explain::SuffixKind::Continues, - ); - let content_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::ContentValidFor, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(OutlivesContent { - span, - notes: reference_valid.into_iter().chain(content_valid).collect(), - }) - } - infer::RelateObjectBound(span) => { - let object_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::TypeObjValidFor, - note_and_explain::SuffixKind::Empty, - ); - let pointer_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::SourcePointerValidFor, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(OutlivesBound { - span, - notes: object_valid.into_iter().chain(pointer_valid).collect(), - }) - } - infer::RelateParamBound(span, ty, opt_span) => { - let prefix = match *sub { - ty::ReStatic => note_and_explain::PrefixKind::TypeSatisfy, - _ => note_and_explain::PrefixKind::TypeOutlive, - }; - let suffix = if opt_span.is_some() { - note_and_explain::SuffixKind::ReqByBinding - } else { - note_and_explain::SuffixKind::Empty - }; - let note = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - opt_span, - prefix, - suffix, - ); - self.dcx().create_err(FulfillReqLifetime { - span, - ty: self.resolve_vars_if_possible(ty), - note, - }) - } - infer::RelateRegionParamBound(span, _) => { - let param_instantiated = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::LfParamInstantiatedWith, - note_and_explain::SuffixKind::Empty, - ); - let param_must_outlive = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::LfParamMustOutlive, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(LfBoundNotSatisfied { - span, - notes: param_instantiated.into_iter().chain(param_must_outlive).collect(), - }) - } - infer::ReferenceOutlivesReferent(ty, span) => { - let pointer_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::PointerValidFor, - note_and_explain::SuffixKind::Empty, - ); - let data_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::DataValidFor, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(RefLongerThanData { - span, - ty: self.resolve_vars_if_possible(ty), - notes: pointer_valid.into_iter().chain(data_valid).collect(), - }) - } - infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => { - let mut err = self.infcx.report_extra_impl_obligation( - span, - impl_item_def_id, - trait_item_def_id, - &format!("`{sup}: {sub}`"), - ); - // We should only suggest rewriting the `where` clause if the predicate is within that `where` clause - if let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) - && generics.where_clause_span.contains(span) - { - self.suggest_copy_trait_method_bounds( - trait_item_def_id, - impl_item_def_id, - &mut err, - ); - } - err - } - infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => { - let mut err = self.report_concrete_failure(generic_param_scope, *parent, sub, sup); - - // Don't mention the item name if it's an RPITIT, since that'll just confuse - // folks. - if !self.tcx.is_impl_trait_in_trait(impl_item_def_id.to_def_id()) { - let trait_item_span = self.tcx.def_span(trait_item_def_id); - let item_name = self.tcx.item_name(impl_item_def_id.to_def_id()); - err.span_label( - trait_item_span, - format!("definition of `{item_name}` from trait"), - ); - } - - self.suggest_copy_trait_method_bounds( - trait_item_def_id, - impl_item_def_id, - &mut err, - ); - err - } - infer::AscribeUserTypeProvePredicate(span) => { - let instantiated = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::LfInstantiatedWith, - note_and_explain::SuffixKind::Empty, - ); - let must_outlive = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::LfMustOutlive, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(LfBoundNotSatisfied { - span, - notes: instantiated.into_iter().chain(must_outlive).collect(), - }) - } - }; - if sub.is_error() || sup.is_error() { - err.downgrade_to_delayed_bug(); - } - err - } - - pub fn suggest_copy_trait_method_bounds( - &self, - trait_item_def_id: DefId, - impl_item_def_id: LocalDefId, - err: &mut Diag<'_>, - ) { - // FIXME(compiler-errors): Right now this is only being used for region - // predicate mismatches. Ideally, we'd use it for *all* predicate mismatches, - // but right now it's not really very smart when it comes to implicit `Sized` - // predicates and bounds on the trait itself. - - let Some(impl_def_id) = self.tcx.associated_item(impl_item_def_id).impl_container(self.tcx) - else { - return; - }; - let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) else { - return; - }; - let trait_args = trait_ref - .instantiate_identity() - // Replace the explicit self type with `Self` for better suggestion rendering - .with_self_ty(self.tcx, Ty::new_param(self.tcx, 0, kw::SelfUpper)) - .args; - let trait_item_args = ty::GenericArgs::identity_for_item(self.tcx, impl_item_def_id) - .rebase_onto(self.tcx, impl_def_id, trait_args); - - let Ok(trait_predicates) = - self.tcx - .explicit_predicates_of(trait_item_def_id) - .instantiate_own(self.tcx, trait_item_args) - .map(|(pred, _)| { - if pred.is_suggestable(self.tcx, false) { - Ok(pred.to_string()) - } else { - Err(()) - } - }) - .collect::, ()>>() - else { - return; - }; - - let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) else { - return; - }; - - let suggestion = if trait_predicates.is_empty() { - WhereClauseSuggestions::Remove { span: generics.where_clause_span } - } else { - let space = if generics.where_clause_span.is_empty() { " " } else { "" }; - WhereClauseSuggestions::CopyPredicates { - span: generics.where_clause_span, - space, - trait_predicates: trait_predicates.join(", "), - } - }; - err.subdiagnostic(suggestion); - } - - pub(super) fn report_placeholder_failure( - &self, - generic_param_scope: LocalDefId, - placeholder_origin: SubregionOrigin<'tcx>, - sub: Region<'tcx>, - sup: Region<'tcx>, - ) -> Diag<'a> { - // I can't think how to do better than this right now. -nikomatsakis - debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure"); - match placeholder_origin { - infer::Subtype(box ref trace) - if matches!( - &trace.cause.code().peel_derives(), - ObligationCauseCode::WhereClause(..) - | ObligationCauseCode::WhereClauseInExpr(..) - ) => - { - // Hack to get around the borrow checker because trace.cause has an `Rc`. - if let ObligationCauseCode::WhereClause(_, span) - | ObligationCauseCode::WhereClauseInExpr(_, span, ..) = - &trace.cause.code().peel_derives() - && !span.is_dummy() - { - let span = *span; - self.report_concrete_failure(generic_param_scope, placeholder_origin, sub, sup) - .with_span_note(span, "the lifetime requirement is introduced here") - } else { - unreachable!( - "control flow ensures we have a `BindingObligation` or `WhereClauseInExpr` here..." - ) - } - } - infer::Subtype(box trace) => { - let terr = TypeError::RegionsPlaceholderMismatch; - return self.report_and_explain_type_error(trace, terr); - } - _ => { - return self.report_concrete_failure( - generic_param_scope, - placeholder_origin, - sub, - sup, - ); - } - } - } -} diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index b97f3dc303ba1..e8d14b89d698e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -293,7 +293,7 @@ impl Trait for X { (ty::Dynamic(t, _, ty::DynKind::Dyn), ty::Alias(ty::Opaque, alias)) if let Some(def_id) = t.principal_def_id() && tcx - .explicit_item_super_predicates(alias.def_id) + .explicit_item_self_bounds(alias.def_id) .skip_binder() .iter() .any(|(pred, _span)| match pred.kind().skip_binder() { @@ -422,7 +422,7 @@ impl Trait for X { ty::Alias(..) => values.expected, _ => values.found, }; - let preds = tcx.explicit_item_super_predicates(opaque_ty.def_id); + let preds = tcx.explicit_item_self_bounds(opaque_ty.def_id); for (pred, _span) in preds.skip_binder() { let ty::ClauseKind::Trait(trait_predicate) = pred.kind().skip_binder() else { @@ -461,9 +461,11 @@ impl Trait for X { (ty::FnPtr(_, hdr), ty::FnDef(def_id, _)) | (ty::FnDef(def_id, _), ty::FnPtr(_, hdr)) => { if tcx.fn_sig(def_id).skip_binder().safety() < hdr.safety { - diag.note( + if !tcx.codegen_fn_attrs(def_id).safe_target_features { + diag.note( "unsafe functions cannot be coerced into safe function pointers", - ); + ); + } } } (ty::Adt(_, _), ty::Adt(def, args)) @@ -630,7 +632,7 @@ impl Trait for X { let callable_scope = matches!( body_owner, Some( - hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }) + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { .. }, .. }) | hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }), ) diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index 98b5fb2052f11..f35a5349ecb3c 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -221,7 +221,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { infer::Subtype(ref trace) => RegionOriginNote::WithRequirement { span: trace.cause.span, requirement: ObligationCauseAsDiagArg(trace.cause.clone()), - expected_found: self.values_str(trace.values).map(|(e, f, _)| (e, f)), + expected_found: self.values_str(trace.values, &trace.cause, err.long_ty_path()), } .add_to_diag(err), infer::Reborrow(span) => { @@ -946,8 +946,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let infer::Subtype(ref sup_trace) = sup_origin && let infer::Subtype(ref sub_trace) = sub_origin - && let Some((sup_expected, sup_found, _)) = self.values_str(sup_trace.values) - && let Some((sub_expected, sub_found, _)) = self.values_str(sub_trace.values) + && let Some((sup_expected, sup_found)) = + self.values_str(sup_trace.values, &sup_trace.cause, err.long_ty_path()) + && let Some((sub_expected, sub_found)) = + self.values_str(sub_trace.values, &sup_trace.cause, err.long_ty_path()) && sub_expected == sup_expected && sub_found == sup_found { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs index 08775df5ac99a..562000e28aca1 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs @@ -12,6 +12,7 @@ use rustc_middle::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, StatementAsExpression, }; +use rustc_middle::ty::error::TypeError; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt}; use rustc_span::{Span, sym}; @@ -20,7 +21,7 @@ use tracing::debug; use crate::error_reporting::TypeErrCtxt; use crate::error_reporting::infer::hir::Path; use crate::errors::{ - ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes, + ConsiderAddingAwait, FnConsiderCasting, FnConsiderCastingBoth, FnItemsAreDistinct, FnUniqTypes, FunctionPointerSuggestion, SuggestAccessingField, SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany, SuggestTuplePatternOne, TypeErrorAdditionalDiags, }; @@ -167,6 +168,18 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { exp_span, exp_found.expected, exp_found.found, ); + match self.tcx.coroutine_kind(cause.body_id) { + Some(hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::Async | hir::CoroutineDesugaring::AsyncGen, + _, + )) => (), + None + | Some( + hir::CoroutineKind::Coroutine(_) + | hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _), + ) => return, + } + if let ObligationCauseCode::CompareImplItem { .. } = cause.code() { return; } @@ -369,14 +382,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - pub(super) fn suggest_function_pointers( + pub fn suggest_function_pointers_impl( &self, - cause: &ObligationCause<'tcx>, - span: Span, + span: Option, exp_found: &ty::error::ExpectedFound>, diag: &mut Diag<'_>, ) { - debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found); let ty::error::ExpectedFound { expected, found } = exp_found; let expected_inner = expected.peel_refs(); let found_inner = found.peel_refs(); @@ -399,8 +410,17 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { return; } + let Some(span) = span else { + let casting = format!("{fn_name} as {sig}"); + diag.subdiagnostic(FnItemsAreDistinct); + diag.subdiagnostic(FnConsiderCasting { casting }); + return; + }; + let sugg = match (expected.is_ref(), found.is_ref()) { - (true, false) => FunctionPointerSuggestion::UseRef { span, fn_name }, + (true, false) => { + FunctionPointerSuggestion::UseRef { span: span.shrink_to_lo() } + } (false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name }, (true, true) => { diag.subdiagnostic(FnItemsAreDistinct); @@ -408,7 +428,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } (false, false) => { diag.subdiagnostic(FnItemsAreDistinct); - FunctionPointerSuggestion::Cast { span, fn_name, sig } + FunctionPointerSuggestion::Cast { span: span.shrink_to_hi(), sig } } }; diag.subdiagnostic(sugg); @@ -433,6 +453,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } let fn_name = self.tcx.def_path_str_with_args(*did2, args2); + + let Some(span) = span else { + diag.subdiagnostic(FnConsiderCastingBoth { sig: *expected_sig }); + return; + }; + let sug = if found.is_ref() { FunctionPointerSuggestion::CastBothRef { span, @@ -442,8 +468,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } else { FunctionPointerSuggestion::CastBoth { - span, - fn_name, + span: span.shrink_to_hi(), found_sig: *found_sig, expected_sig: *expected_sig, } @@ -476,6 +501,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }; } + pub(super) fn suggest_function_pointers( + &self, + cause: &ObligationCause<'tcx>, + span: Span, + exp_found: &ty::error::ExpectedFound>, + terr: TypeError<'tcx>, + diag: &mut Diag<'_>, + ) { + debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found); + + if exp_found.expected.peel_refs().is_fn() && exp_found.found.peel_refs().is_fn() { + self.suggest_function_pointers_impl(Some(span), exp_found, diag); + } else if let TypeError::Sorts(exp_found) = terr { + self.suggest_function_pointers_impl(None, &exp_found, diag); + } + } + pub fn should_suggest_as_ref_kind( &self, expected: Ty<'tcx>, @@ -643,7 +685,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { && let ty::Ref(found_region, _, _) = found.kind() && expected_region.is_bound() && !found_region.is_bound() - && let hir::TyKind::Infer = arg_hir.kind + && let hir::TyKind::Infer(()) = arg_hir.kind { // If the expected region is late bound, the found region is not, and users are asking compiler // to infer the type, we can suggest adding `: &_`. @@ -817,7 +859,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { pat.walk(&mut find_compatible_candidates); } - hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. }) + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { body, .. }, .. }) | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body), .. }) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index fc0de13aeab09..6beb108bc3aee 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -163,6 +163,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let predicate = self.resolve_vars_if_possible(obligation.predicate); let span = obligation.cause.span; + let mut file = None; debug!(?predicate, obligation.cause.code = ?obligation.cause.code()); @@ -172,14 +173,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let bound_predicate = predicate.kind(); let mut err = match bound_predicate.skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { - let trait_ref = bound_predicate.rebind(data.trait_ref); - debug!(?trait_ref); + let trait_pred = bound_predicate.rebind(data); + debug!(?trait_pred); if let Err(e) = predicate.error_reported() { return e; } - if let Err(guar) = self.tcx.ensure().coherent_trait(trait_ref.def_id()) { + if let Err(guar) = self.tcx.ensure_ok().coherent_trait(trait_pred.def_id()) { // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case // other `Foo` impls are incoherent. return guar; @@ -200,13 +201,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // avoid inundating the user with unnecessary errors, but we now // check upstream for type errors and don't add the obligations to // begin with in those cases. - if self.tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) { + if self.tcx.is_lang_item(trait_pred.def_id(), LangItem::Sized) { match self.tainted_by_errors() { None => { let err = self.emit_inference_failure_err( obligation.cause.body_id, span, - trait_ref.self_ty().skip_binder().into(), + trait_pred.self_ty().skip_binder().into(), TypeAnnotationNeeded::E0282, false, ); @@ -245,16 +246,20 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { span, E0283, "type annotations needed: cannot satisfy `{}`", - predicate, + self.tcx.short_string(predicate, &mut file), ) }; let mut ambiguities = compute_applicable_impls_for_diagnostics( self.infcx, - &obligation.with(self.tcx, trait_ref), + &obligation.with(self.tcx, trait_pred), ); - let has_non_region_infer = - trait_ref.skip_binder().args.types().any(|t| !t.is_ty_or_numeric_infer()); + let has_non_region_infer = trait_pred + .skip_binder() + .trait_ref + .args + .types() + .any(|t| !t.is_ty_or_numeric_infer()); // It doesn't make sense to talk about applicable impls if there are more than a // handful of them. If there are a lot of them, but only a few of them have no type // params, we only show those, as they are more likely to be useful/intended. @@ -288,13 +293,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.cancel(); return e; } - err.note(format!("cannot satisfy `{predicate}`")); + let pred = self.tcx.short_string(predicate, &mut file); + err.note(format!("cannot satisfy `{pred}`")); let impl_candidates = self.find_similar_impl_candidates(predicate.as_trait_clause().unwrap()); if impl_candidates.len() < 40 { self.report_similar_impl_candidates( impl_candidates.as_slice(), - trait_ref, + trait_pred, obligation.cause.body_id, &mut err, false, @@ -306,7 +312,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let ObligationCauseCode::WhereClause(def_id, _) | ObligationCauseCode::WhereClauseInExpr(def_id, ..) = *obligation.cause.code() { - self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id()); + self.suggest_fully_qualified_path(&mut err, def_id, span, trait_pred.def_id()); } if let Some(ty::GenericArgKind::Type(_)) = arg.map(|arg| arg.unpack()) @@ -507,8 +513,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return e; } - if let Err(guar) = - self.tcx.ensure().coherent_trait(self.tcx.parent(data.projection_term.def_id)) + if let Err(guar) = self + .tcx + .ensure_ok() + .coherent_trait(self.tcx.parent(data.projection_term.def_id)) { // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case // other `Foo` impls are incoherent. @@ -520,6 +528,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .iter() .chain(Some(data.term.into_arg())) .find(|g| g.has_non_region_infer()); + let predicate = self.tcx.short_string(predicate, &mut file); if let Some(arg) = arg { self.emit_inference_failure_err( obligation.cause.body_id, @@ -535,8 +544,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.dcx(), span, E0284, - "type annotations needed: cannot satisfy `{}`", - predicate, + "type annotations needed: cannot satisfy `{predicate}`", ) .with_span_label(span, format!("cannot satisfy `{predicate}`")) } @@ -561,12 +569,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err } else { // If we can't find a generic parameter, just print a generic error + let predicate = self.tcx.short_string(predicate, &mut file); struct_span_code_err!( self.dcx(), span, E0284, - "type annotations needed: cannot satisfy `{}`", - predicate, + "type annotations needed: cannot satisfy `{predicate}`", ) .with_span_label(span, format!("cannot satisfy `{predicate}`")) } @@ -586,6 +594,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let Some(e) = self.tainted_by_errors() { return e; } + let alias = self.tcx.short_string(alias, &mut file); struct_span_code_err!( self.dcx(), span, @@ -599,16 +608,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let Some(e) = self.tainted_by_errors() { return e; } + let predicate = self.tcx.short_string(predicate, &mut file); struct_span_code_err!( self.dcx(), span, E0284, - "type annotations needed: cannot satisfy `{}`", - predicate, + "type annotations needed: cannot satisfy `{predicate}`", ) .with_span_label(span, format!("cannot satisfy `{predicate}`")) } }; + *err.long_ty_path() = file; self.note_obligation_cause(&mut err, obligation); err.emit() } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs new file mode 100644 index 0000000000000..1c3e570b67695 --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs @@ -0,0 +1,155 @@ +//! Common logic for borrowck use-after-move errors when moved into a `fn(self)`, +//! as well as errors when attempting to call a non-const function in a const +//! context. + +use rustc_hir::def::DefKind; +use rustc_hir::def_id::DefId; +use rustc_hir::{LangItem, lang_items}; +use rustc_middle::ty::{AssocItemContainer, GenericArgsRef, Instance, Ty, TyCtxt, TypingEnv}; +use rustc_span::{DesugaringKind, Ident, Span, sym}; +use tracing::debug; + +use crate::traits::specialization_graph; + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum CallDesugaringKind { + /// for _ in x {} calls x.into_iter() + ForLoopIntoIter, + /// for _ in x {} calls iter.next() + ForLoopNext, + /// x? calls x.branch() + QuestionBranch, + /// x? calls type_of(x)::from_residual() + QuestionFromResidual, + /// try { ..; x } calls type_of(x)::from_output(x) + TryBlockFromOutput, + /// `.await` calls `IntoFuture::into_future` + Await, +} + +impl CallDesugaringKind { + pub fn trait_def_id(self, tcx: TyCtxt<'_>) -> DefId { + match self { + Self::ForLoopIntoIter => tcx.get_diagnostic_item(sym::IntoIterator).unwrap(), + Self::ForLoopNext => tcx.require_lang_item(LangItem::Iterator, None), + Self::QuestionBranch | Self::TryBlockFromOutput => { + tcx.require_lang_item(LangItem::Try, None) + } + Self::QuestionFromResidual => tcx.get_diagnostic_item(sym::FromResidual).unwrap(), + Self::Await => tcx.get_diagnostic_item(sym::IntoFuture).unwrap(), + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum CallKind<'tcx> { + /// A normal method call of the form `receiver.foo(a, b, c)` + Normal { + self_arg: Option, + desugaring: Option<(CallDesugaringKind, Ty<'tcx>)>, + method_did: DefId, + method_args: GenericArgsRef<'tcx>, + }, + /// A call to `Fn(..)::call(..)`, desugared from `my_closure(a, b, c)` + FnCall { fn_trait_id: DefId, self_ty: Ty<'tcx> }, + /// A call to an operator trait, desugared from operator syntax (e.g. `a << b`) + Operator { self_arg: Option, trait_id: DefId, self_ty: Ty<'tcx> }, + DerefCoercion { + /// The `Span` of the `Target` associated type + /// in the `Deref` impl we are using. + deref_target_span: Option, + /// The type `T::Deref` we are dereferencing to + deref_target_ty: Ty<'tcx>, + self_ty: Ty<'tcx>, + }, +} + +pub fn call_kind<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: TypingEnv<'tcx>, + method_did: DefId, + method_args: GenericArgsRef<'tcx>, + fn_call_span: Span, + from_hir_call: bool, + self_arg: Option, +) -> CallKind<'tcx> { + let parent = tcx.opt_associated_item(method_did).and_then(|assoc| { + let container_id = assoc.container_id(tcx); + match assoc.container { + AssocItemContainer::Impl => tcx.trait_id_of_impl(container_id), + AssocItemContainer::Trait => Some(container_id), + } + }); + + let fn_call = parent.and_then(|p| { + lang_items::FN_TRAITS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p) + }); + + let operator = if !from_hir_call && let Some(p) = parent { + lang_items::OPERATORS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p) + } else { + None + }; + + // Check for a 'special' use of 'self' - + // an FnOnce call, an operator (e.g. `<<`), or a + // deref coercion. + if let Some(trait_id) = fn_call { + return CallKind::FnCall { fn_trait_id: trait_id, self_ty: method_args.type_at(0) }; + } else if let Some(trait_id) = operator { + return CallKind::Operator { self_arg, trait_id, self_ty: method_args.type_at(0) }; + } else if !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did) { + let deref_target_def_id = + tcx.get_diagnostic_item(sym::deref_target).expect("deref method but no deref target"); + let deref_target_ty = tcx.normalize_erasing_regions( + typing_env, + Ty::new_projection(tcx, deref_target_def_id, method_args), + ); + let deref_target_span = if let Ok(Some(instance)) = + Instance::try_resolve(tcx, typing_env, method_did, method_args) + && let instance_parent_def_id = tcx.parent(instance.def_id()) + && matches!(tcx.def_kind(instance_parent_def_id), DefKind::Impl { .. }) + && let Ok(instance) = + specialization_graph::assoc_def(tcx, instance_parent_def_id, deref_target_def_id) + && instance.is_final() + { + Some(tcx.def_span(instance.item.def_id)) + } else { + None + }; + return CallKind::DerefCoercion { + deref_target_ty, + deref_target_span, + self_ty: method_args.type_at(0), + }; + } + + // This isn't a 'special' use of `self` + debug!(?method_did, ?fn_call_span); + let desugaring = if tcx.is_lang_item(method_did, LangItem::IntoIterIntoIter) + && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop) + { + Some((CallDesugaringKind::ForLoopIntoIter, method_args.type_at(0))) + } else if tcx.is_lang_item(method_did, LangItem::IteratorNext) + && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop) + { + Some((CallDesugaringKind::ForLoopNext, method_args.type_at(0))) + } else if fn_call_span.desugaring_kind() == Some(DesugaringKind::QuestionMark) { + if tcx.is_lang_item(method_did, LangItem::TryTraitBranch) { + Some((CallDesugaringKind::QuestionBranch, method_args.type_at(0))) + } else if tcx.is_lang_item(method_did, LangItem::TryTraitFromResidual) { + Some((CallDesugaringKind::QuestionFromResidual, method_args.type_at(0))) + } else { + None + } + } else if tcx.is_lang_item(method_did, LangItem::TryTraitFromOutput) + && fn_call_span.desugaring_kind() == Some(DesugaringKind::TryBlock) + { + Some((CallDesugaringKind::TryBlockFromOutput, method_args.type_at(0))) + } else if fn_call_span.is_desugaring(DesugaringKind::Await) { + Some((CallDesugaringKind::Await, method_args.type_at(0))) + } else { + None + }; + CallKind::Normal { self_arg, desugaring, method_did, method_args } +} diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 885b606326c54..57440d60de1f4 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1,5 +1,6 @@ use core::ops::ControlFlow; use std::borrow::Cow; +use std::path::PathBuf; use rustc_ast::TraitObjectSyntax; use rustc_data_structures::fx::FxHashMap; @@ -9,7 +10,6 @@ use rustc_errors::{ Applicability, Diag, ErrorGuaranteed, Level, MultiSpan, StashKey, StringPart, Suggestions, pluralize, struct_span_code_err, }; -use rustc_hir::def::Namespace; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, LangItem, Node}; @@ -20,14 +20,12 @@ use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::print::{ - FmtPrinter, Print, PrintPolyTraitPredicateExt, PrintTraitPredicateExt as _, - PrintTraitRefExt as _, with_forced_trimmed_paths, -}; -use rustc_middle::ty::{ - self, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast, + PrintPolyTraitPredicateExt, PrintTraitPredicateExt as _, PrintTraitRefExt as _, + with_forced_trimmed_paths, }; +use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast}; use rustc_middle::{bug, span_bug}; -use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, sym}; +use rustc_span::{BytePos, DUMMY_SP, STDLIB_STABLE_CRATES, Span, Symbol, sym}; use tracing::{debug, instrument}; use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote}; @@ -62,6 +60,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) -> ErrorGuaranteed { let tcx = self.tcx; let mut span = obligation.cause.span; + let mut long_ty_file = None; let mut err = match *error { SelectionError::Unimplemented => { @@ -114,7 +113,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // // We rely on a few heuristics to identify cases where this root // obligation is more important than the leaf obligation: - let (main_trait_predicate, o) = if let ty::PredicateKind::Clause( + let (main_trait_predicate, main_obligation) = if let ty::PredicateKind::Clause( ty::ClauseKind::Trait(root_pred) ) = root_obligation.predicate.kind().skip_binder() && !leaf_trait_predicate.self_ty().skip_binder().has_escaping_bound_vars() @@ -155,12 +154,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { (leaf_trait_predicate, &obligation) }; - let main_trait_ref = main_trait_predicate.to_poly_trait_ref(); - let leaf_trait_ref = leaf_trait_predicate.to_poly_trait_ref(); - if let Some(guar) = self.emit_specialized_closure_kind_error( &obligation, - leaf_trait_ref, + leaf_trait_predicate, ) { return guar; } @@ -174,11 +170,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let Err(guar) = self.fn_arg_obligation(&obligation) { return guar; } - let mut file = None; let (post_message, pre_message, type_def) = self .get_parent_trait_ref(obligation.cause.code()) .map(|(t, s)| { - let t = self.tcx.short_ty_string(t, &mut file); + let t = self.tcx.short_string(t, &mut long_ty_file); ( format!(" in `{t}`"), format!("within `{t}`, "), @@ -186,12 +181,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) }) .unwrap_or_default(); - let file_note = file.as_ref().map(|file| format!( - "the full trait has been written to '{}'", - file.display(), - )); - - let mut long_ty_file = None; let OnUnimplementedNote { message, @@ -199,17 +188,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { notes, parent_label, append_const_msg, - } = self.on_unimplemented_note(main_trait_predicate, o, &mut long_ty_file); + } = self.on_unimplemented_note(main_trait_predicate, main_obligation, &mut long_ty_file); let have_alt_message = message.is_some() || label.is_some(); - let is_try_conversion = self.is_try_conversion(span, main_trait_ref.def_id()); + let is_try_conversion = self.is_try_conversion(span, main_trait_predicate.def_id()); let is_unsize = - self.tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Unsize); + self.tcx.is_lang_item(leaf_trait_predicate.def_id(), LangItem::Unsize); let (message, notes, append_const_msg) = if is_try_conversion { ( Some(format!( "`?` couldn't convert the error to `{}`", - main_trait_ref.skip_binder().self_ty(), + main_trait_predicate.skip_binder().self_ty(), )), vec![ "the question mark operation (`?`) implicitly performs a \ @@ -228,14 +217,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { None, append_const_msg, post_message, + &mut long_ty_file, ); - let (err_msg, safe_transmute_explanation) = if self.tcx.is_lang_item(main_trait_ref.def_id(), LangItem::TransmuteTrait) + let (err_msg, safe_transmute_explanation) = if self.tcx.is_lang_item(main_trait_predicate.def_id(), LangItem::TransmuteTrait) { // Recompute the safe transmute reason and use that for the error reporting match self.get_safe_transmute_error_and_reason( obligation.clone(), - main_trait_ref, + main_trait_predicate, span, ) { GetSafeTransmuteErrorAndReason::Silent => { @@ -256,17 +246,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; let mut err = struct_span_code_err!(self.dcx(), span, E0277, "{}", err_msg); + *err.long_ty_path() = long_ty_file; - if let Some(long_ty_file) = long_ty_file { - err.note(format!( - "the full name for the type has been written to '{}'", - long_ty_file.display(), - )); - err.note("consider using `--verbose` to print the full type name to the console"); - } let mut suggested = false; if is_try_conversion { - suggested = self.try_conversion_context(&obligation, main_trait_ref.skip_binder(), &mut err); + suggested = self.try_conversion_context(&obligation, main_trait_predicate, &mut err); } if is_try_conversion && let Some(ret_span) = self.return_type_span(&obligation) { @@ -274,12 +258,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ret_span, format!( "expected `{}` because of this", - main_trait_ref.skip_binder().self_ty() + main_trait_predicate.skip_binder().self_ty() ), ); } - if tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Tuple) { + if tcx.is_lang_item(leaf_trait_predicate.def_id(), LangItem::Tuple) { self.add_tuple_trait_message( obligation.cause.code().peel_derives(), &mut err, @@ -314,12 +298,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return err.emit(); } - file_note.map(|note| err.note(note)); if let Some(s) = label { // If it has a custom `#[rustc_on_unimplemented]` // error message, let's display it as the label! err.span_label(span, s); - if !matches!(leaf_trait_ref.skip_binder().self_ty().kind(), ty::Param(_)) { + if !matches!(leaf_trait_predicate.skip_binder().self_ty().kind(), ty::Param(_)) { // When the self type is a type param We don't need to "the trait // `std::marker::Sized` is not implemented for `T`" as we will point // at the type param with a label to suggest constraining it. @@ -339,7 +322,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let ObligationCauseCode::Coercion { source, target } = *obligation.cause.code().peel_derives() { - if self.tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Sized) { + if self.tcx.is_lang_item(leaf_trait_predicate.def_id(), LangItem::Sized) { self.suggest_borrowing_for_object_cast( &mut err, root_obligation, @@ -368,7 +351,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.span_label(tcx.def_span(body), s); } - self.suggest_floating_point_literal(&obligation, &mut err, leaf_trait_ref); + self.suggest_floating_point_literal(&obligation, &mut err, leaf_trait_predicate); self.suggest_dereferencing_index(&obligation, &mut err, leaf_trait_predicate); suggested |= self.suggest_dereferences(&obligation, &mut err, leaf_trait_predicate); suggested |= self.suggest_fn_call(&obligation, &mut err, leaf_trait_predicate); @@ -376,7 +359,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { suggested = if let &[cand] = &impl_candidates[..] { let cand = cand.trait_ref; if let (ty::FnPtr(..), ty::FnDef(..)) = - (cand.self_ty().kind(), main_trait_ref.self_ty().skip_binder().kind()) + (cand.self_ty().kind(), main_trait_predicate.self_ty().skip_binder().kind()) { // Wrap method receivers and `&`-references in parens let suggestion = if self.tcx.sess.source_map().span_look_ahead(span, ".", Some(50)).is_some() { @@ -423,11 +406,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { span, leaf_trait_predicate, ); - self.note_version_mismatch(&mut err, leaf_trait_ref); + self.note_version_mismatch(&mut err, leaf_trait_predicate); self.suggest_remove_await(&obligation, &mut err); self.suggest_derive(&obligation, &mut err, leaf_trait_predicate); - if tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Try) { + if tcx.is_lang_item(leaf_trait_predicate.def_id(), LangItem::Try) { self.suggest_await_before_try( &mut err, &obligation, @@ -455,9 +438,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); } - let is_fn_trait = tcx.is_fn_trait(leaf_trait_ref.def_id()); + let is_fn_trait = tcx.is_fn_trait(leaf_trait_predicate.def_id()); let is_target_feature_fn = if let ty::FnDef(def_id, _) = - *leaf_trait_ref.skip_binder().self_ty().kind() + *leaf_trait_predicate.skip_binder().self_ty().kind() { !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() } else { @@ -467,6 +450,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.note( "`#[target_feature]` functions do not implement the `Fn` traits", ); + err.note( + "try casting the function to a `fn` pointer or wrapping it in a closure", + ); } self.try_to_add_help_message( @@ -509,7 +495,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } self.explain_hrtb_projection(&mut err, leaf_trait_predicate, obligation.param_env, &obligation.cause); - self.suggest_desugaring_async_fn_in_trait(&mut err, main_trait_ref); + self.suggest_desugaring_async_fn_in_trait(&mut err, main_trait_predicate); // Return early if the trait is Debug or Display and the invocation // originates within a standard library macro, because the output @@ -520,14 +506,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { match obligation.cause.span.ctxt().outer_expn_data().macro_def_id { Some(macro_def_id) => { let crate_name = tcx.crate_name(macro_def_id.krate); - crate_name == sym::std || crate_name == sym::core + STDLIB_STABLE_CRATES.contains(&crate_name) } None => false, }; if in_std_macro && matches!( - self.tcx.get_diagnostic_name(leaf_trait_ref.def_id()), + self.tcx.get_diagnostic_name(leaf_trait_predicate.def_id()), Some(sym::Debug | sym::Display) ) { @@ -538,23 +524,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(predicate)) => { - // FIXME(const_trait_impl): We should recompute the predicate with `~const` - // if it's `const`, and if it holds, explain that this bound only - // *conditionally* holds. If that fails, we should also do selection - // to drill this down to an impl or built-in source, so we can - // point at it and explain that while the trait *is* implemented, - // that implementation is not const. - let err_msg = self.get_standard_error_message( - bound_predicate.rebind(ty::TraitPredicate { - trait_ref: predicate.trait_ref, - polarity: ty::PredicatePolarity::Positive, - }), - None, - Some(predicate.constness), - None, - String::new(), - ); - struct_span_code_err!(self.dcx(), span, E0277, "{}", err_msg) + self.report_host_effect_error(bound_predicate.rebind(predicate), obligation.param_env, span) } ty::PredicateKind::Subtype(predicate) => { @@ -601,8 +571,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.tcx.hir_node_by_def_id(obligation.cause.body_id) && let hir::ItemKind::Impl(impl_) = item.kind && let None = impl_.of_trait - && let hir::TyKind::TraitObject(_, _, syntax) = impl_.self_ty.kind - && let TraitObjectSyntax::None = syntax + && let hir::TyKind::TraitObject(_, tagged_ptr) = impl_.self_ty.kind + && let TraitObjectSyntax::None = tagged_ptr.tag() && impl_.self_ty.span.edition().at_least_rust_2021() { // Silence the dyn-compatibility error in favor of the missing dyn on @@ -615,6 +585,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(ty)) => { let ty = self.resolve_vars_if_possible(ty); if self.next_trait_solver() { + if let Err(guar) = ty.error_reported() { + return guar; + } + // FIXME: we'll need a better message which takes into account // which bounds actually failed to hold. self.dcx().struct_span_err( @@ -726,6 +700,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { None, TypeError::Sorts(ty::error::ExpectedFound::new(expected_ty, ct_ty)), false, + None, ); diag } @@ -753,7 +728,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { applied_do_not_recommend = true; } } - if let Some((parent_cause, _parent_pred)) = base_cause.parent() { + if let Some(parent_cause) = base_cause.parent() { base_cause = parent_cause.clone(); } else { break; @@ -763,24 +738,63 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { applied_do_not_recommend } + fn report_host_effect_error( + &self, + predicate: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>, + param_env: ty::ParamEnv<'tcx>, + span: Span, + ) -> Diag<'a> { + // FIXME(const_trait_impl): We should recompute the predicate with `~const` + // if it's `const`, and if it holds, explain that this bound only + // *conditionally* holds. If that fails, we should also do selection + // to drill this down to an impl or built-in source, so we can + // point at it and explain that while the trait *is* implemented, + // that implementation is not const. + let trait_ref = predicate.map_bound(|predicate| ty::TraitPredicate { + trait_ref: predicate.trait_ref, + polarity: ty::PredicatePolarity::Positive, + }); + let mut file = None; + let err_msg = self.get_standard_error_message( + trait_ref, + None, + Some(predicate.constness()), + None, + String::new(), + &mut file, + ); + let mut diag = struct_span_code_err!(self.dcx(), span, E0277, "{}", err_msg); + *diag.long_ty_path() = file; + if !self.predicate_may_hold(&Obligation::new( + self.tcx, + ObligationCause::dummy(), + param_env, + trait_ref, + )) { + diag.downgrade_to_delayed_bug(); + } + diag + } + fn emit_specialized_closure_kind_error( &self, obligation: &PredicateObligation<'tcx>, - mut trait_ref: ty::PolyTraitRef<'tcx>, + mut trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> Option { // If `AsyncFnKindHelper` is not implemented, that means that the closure kind // doesn't extend the goal kind. This is worth reporting, but we can only do so // if we actually know which closure this goal comes from, so look at the cause // to see if we can extract that information. - if self.tcx.is_lang_item(trait_ref.def_id(), LangItem::AsyncFnKindHelper) - && let Some(found_kind) = trait_ref.skip_binder().args.type_at(0).to_opt_closure_kind() + if self.tcx.is_lang_item(trait_pred.def_id(), LangItem::AsyncFnKindHelper) + && let Some(found_kind) = + trait_pred.skip_binder().trait_ref.args.type_at(0).to_opt_closure_kind() && let Some(expected_kind) = - trait_ref.skip_binder().args.type_at(1).to_opt_closure_kind() + trait_pred.skip_binder().trait_ref.args.type_at(1).to_opt_closure_kind() && !found_kind.extends(expected_kind) { - if let Some((_, Some(parent))) = obligation.cause.code().parent() { + if let Some((_, Some(parent))) = obligation.cause.code().parent_with_predicate() { // If we have a derived obligation, then the parent will be a `AsyncFn*` goal. - trait_ref = parent.to_poly_trait_ref(); + trait_pred = parent; } else if let &ObligationCauseCode::FunctionArg { arg_hir_id, .. } = obligation.cause.code() && let Some(typeck_results) = &self.typeck_results @@ -801,9 +815,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - let self_ty = trait_ref.self_ty().skip_binder(); + let self_ty = trait_pred.self_ty().skip_binder(); - if let Some(expected_kind) = self.tcx.fn_trait_kind_from_def_id(trait_ref.def_id()) { + if let Some(expected_kind) = self.tcx.fn_trait_kind_from_def_id(trait_pred.def_id()) { let (closure_def_id, found_args, by_ref_captures) = match *self_ty.kind() { ty::Closure(def_id, args) => { (def_id, args.as_closure().sig().map_bound(|sig| sig.inputs()[0]), None) @@ -818,7 +832,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { _ => return None, }; - let expected_args = trait_ref.map_bound(|trait_ref| trait_ref.args.type_at(1)); + let expected_args = + trait_pred.map_bound(|trait_pred| trait_pred.trait_ref.args.type_at(1)); // Verify that the arguments are compatible. If the signature is // mismatched, then we have a totally different error to report. @@ -890,7 +905,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn try_conversion_context( &self, obligation: &PredicateObligation<'tcx>, - trait_ref: ty::TraitRef<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, err: &mut Diag<'_>, ) -> bool { let span = obligation.cause.span; @@ -912,27 +927,25 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } let hir_id = self.tcx.local_def_id_to_hir_id(obligation.cause.body_id); - let body_id = match self.tcx.hir_node(hir_id) { - hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) => body_id, - _ => return false, - }; - let ControlFlow::Break(expr) = (FindMethodSubexprOfTry { search_span: span }) - .visit_body(self.tcx.hir().body(*body_id)) + let Some(body_id) = self.tcx.hir_node(hir_id).body_id() else { return false }; + let ControlFlow::Break(expr) = + (FindMethodSubexprOfTry { search_span: span }).visit_body(self.tcx.hir().body(body_id)) else { return false; }; let Some(typeck) = &self.typeck_results else { return false; }; - let Some((ObligationCauseCode::QuestionMark, Some(y))) = obligation.cause.code().parent() + let Some((ObligationCauseCode::QuestionMark, Some(y))) = + obligation.cause.code().parent_with_predicate() else { return false; }; if !self.tcx.is_diagnostic_item(sym::FromResidual, y.def_id()) { return false; } - let self_ty = trait_ref.self_ty(); - let found_ty = trait_ref.args.get(1).and_then(|a| a.as_type()); + let self_ty = trait_pred.skip_binder().self_ty(); + let found_ty = trait_pred.skip_binder().trait_ref.args.get(1).and_then(|a| a.as_type()); let mut prev_ty = self.resolve_vars_if_possible( typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)), @@ -1177,7 +1190,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let mut code = obligation.cause.code(); let mut pred = obligation.predicate.as_trait_clause(); - while let Some((next_code, next_pred)) = code.parent() { + while let Some((next_code, next_pred)) = code.parent_with_predicate() { if let Some(pred) = pred { self.enter_forall(pred, |pred| { diag.note(format!( @@ -1201,18 +1214,18 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { goal: ty::TraitPredicate<'tcx>, assumption: ty::PolyTraitPredicate<'tcx>, ) -> bool { + // Fast path if goal.polarity != assumption.polarity() { return false; } - let trait_goal = goal.trait_ref; let trait_assumption = self.instantiate_binder_with_fresh_vars( DUMMY_SP, infer::BoundRegionConversionTime::HigherRankedType, - assumption.to_poly_trait_ref(), + assumption, ); - self.can_eq(ty::ParamEnv::empty(), trait_goal, trait_assumption) + self.can_eq(ty::ParamEnv::empty(), goal.trait_ref, trait_assumption.trait_ref) } fn can_match_projection( @@ -1319,20 +1332,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let derive_better_type_error = |alias_term: ty::AliasTerm<'tcx>, expected_term: ty::Term<'tcx>| { let ocx = ObligationCtxt::new(self); - let normalized_term = match expected_term.unpack() { - ty::TermKind::Ty(_) => self.next_ty_var(DUMMY_SP).into(), - ty::TermKind::Const(_) => self.next_const_var(DUMMY_SP).into(), - }; - ocx.register_obligation(Obligation::new( - self.tcx, - ObligationCause::dummy(), + + let Ok(normalized_term) = ocx.structurally_normalize_term( + &ObligationCause::dummy(), obligation.param_env, - ty::PredicateKind::NormalizesTo(ty::NormalizesTo { - alias: alias_term, - term: normalized_term, - }), - )); - let _ = ocx.select_where_possible(); + alias_term.to_term(self.tcx), + ) else { + return None; + }; + if let Err(terr) = ocx.eq( &ObligationCause::dummy(), obligation.param_env, @@ -1368,22 +1376,53 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { _ => (None, error.err), }; - let msg = values + let mut file = None; + let (msg, span, closure_span) = values .and_then(|(predicate, normalized_term, expected_term)| { - self.maybe_detailed_projection_msg(predicate, normalized_term, expected_term) + self.maybe_detailed_projection_msg( + obligation.cause.span, + predicate, + normalized_term, + expected_term, + &mut file, + ) }) .unwrap_or_else(|| { - let mut cx = FmtPrinter::new_with_limit( - self.tcx, - Namespace::TypeNS, - rustc_session::Limit(10), - ); - with_forced_trimmed_paths!(format!("type mismatch resolving `{}`", { - self.resolve_vars_if_possible(predicate).print(&mut cx).unwrap(); - cx.into_buffer() - })) + ( + with_forced_trimmed_paths!(format!( + "type mismatch resolving `{}`", + self.tcx + .short_string(self.resolve_vars_if_possible(predicate), &mut file), + )), + obligation.cause.span, + None, + ) }); - let mut diag = struct_span_code_err!(self.dcx(), obligation.cause.span, E0271, "{msg}"); + let mut diag = struct_span_code_err!(self.dcx(), span, E0271, "{msg}"); + *diag.long_ty_path() = file; + if let Some(span) = closure_span { + // Mark the closure decl so that it is seen even if we are pointing at the return + // type or expression. + // + // error[E0271]: expected `{closure@foo.rs:41:16}` to be a closure that returns + // `Unit3`, but it returns `Unit4` + // --> $DIR/foo.rs:43:17 + // | + // LL | let v = Unit2.m( + // | - required by a bound introduced by this call + // ... + // LL | f: |x| { + // | --- /* this span */ + // LL | drop(x); + // LL | Unit4 + // | ^^^^^ expected `Unit3`, found `Unit4` + // | + diag.span_label(span, "this closure"); + if !span.overlaps(obligation.cause.span) { + // Point at the binding corresponding to the closure where it is used. + diag.span_label(obligation.cause.span, "closure used here"); + } + } let secondary_span = (|| { let ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) = @@ -1426,15 +1465,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ty.span, with_forced_trimmed_paths!(Cow::from(format!( "type mismatch resolving `{}`", - { - let mut cx = FmtPrinter::new_with_limit( - self.tcx, - Namespace::TypeNS, - rustc_session::Limit(5), - ); - self.resolve_vars_if_possible(predicate).print(&mut cx).unwrap(); - cx.into_buffer() - } + self.tcx.short_string( + self.resolve_vars_if_possible(predicate), + diag.long_ty_path() + ), ))), true, )), @@ -1454,6 +1488,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }), err, false, + Some(span), ); self.note_obligation_cause(&mut diag, obligation); diag.emit() @@ -1462,34 +1497,65 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn maybe_detailed_projection_msg( &self, + mut span: Span, projection_term: ty::AliasTerm<'tcx>, normalized_ty: ty::Term<'tcx>, expected_ty: ty::Term<'tcx>, - ) -> Option { + file: &mut Option, + ) -> Option<(String, Span, Option)> { let trait_def_id = projection_term.trait_def_id(self.tcx); let self_ty = projection_term.self_ty(); with_forced_trimmed_paths! { if self.tcx.is_lang_item(projection_term.def_id, LangItem::FnOnceOutput) { - let fn_kind = self_ty.prefix_string(self.tcx); + let (span, closure_span) = if let ty::Closure(def_id, _) = self_ty.kind() { + let def_span = self.tcx.def_span(def_id); + if let Some(local_def_id) = def_id.as_local() + && let node = self.tcx.hir_node_by_def_id(local_def_id) + && let Some(fn_decl) = node.fn_decl() + && let Some(id) = node.body_id() + { + span = match fn_decl.output { + hir::FnRetTy::Return(ty) => ty.span, + hir::FnRetTy::DefaultReturn(_) => { + let body = self.tcx.hir().body(id); + match body.value.kind { + hir::ExprKind::Block( + hir::Block { expr: Some(expr), .. }, + _, + ) => expr.span, + hir::ExprKind::Block( + hir::Block { + expr: None, stmts: [.., last], .. + }, + _, + ) => last.span, + _ => body.value.span, + } + } + }; + } + (span, Some(def_span)) + } else { + (span, None) + }; let item = match self_ty.kind() { ty::FnDef(def, _) => self.tcx.item_name(*def).to_string(), - _ => self_ty.to_string(), + _ => self.tcx.short_string(self_ty, file), }; - Some(format!( - "expected `{item}` to be a {fn_kind} that returns `{expected_ty}`, but it \ - returns `{normalized_ty}`", - )) + Some((format!( + "expected `{item}` to return `{expected_ty}`, but it returns `{normalized_ty}`", + ), span, closure_span)) } else if self.tcx.is_lang_item(trait_def_id, LangItem::Future) { - Some(format!( + Some((format!( "expected `{self_ty}` to be a future that resolves to `{expected_ty}`, but it \ resolves to `{normalized_ty}`" - )) + ), span, None)) } else if Some(trait_def_id) == self.tcx.get_diagnostic_item(sym::Iterator) { - Some(format!( + Some((format!( "expected `{self_ty}` to be an iterator that yields `{expected_ty}`, but it \ yields `{normalized_ty}`" - )) + ), span, None)) } else { None } @@ -1660,7 +1726,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { pub(super) fn report_similar_impl_candidates( &self, impl_candidates: &[ImplCandidate<'tcx>], - trait_ref: ty::PolyTraitRef<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, body_def_id: LocalDefId, err: &mut Diag<'_>, other: bool, @@ -1705,7 +1771,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // We'll check for the case where the reason for the mismatch is that the trait comes from // one crate version and the type comes from another crate version, even though they both // are from the same crate. - let trait_def_id = trait_ref.def_id(); + let trait_def_id = trait_pred.def_id(); let trait_name = self.tcx.item_name(trait_def_id); let crate_name = self.tcx.crate_name(trait_def_id.krate); if let Some(other_trait_def_id) = self.tcx.all_traits().find(|def_id| { @@ -1717,7 +1783,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // different crate `DefId`. We highlight the traits. let found_type = - if let ty::Adt(def, _) = trait_ref.self_ty().skip_binder().peel_refs().kind() { + if let ty::Adt(def, _) = trait_pred.self_ty().skip_binder().peel_refs().kind() { Some(def.did()) } else { None @@ -1779,13 +1845,16 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } span.push_span_label(self.tcx.def_span(other_trait_def_id), "this is the found trait"); - err.highlighted_span_note(span, vec![ - StringPart::normal("there are ".to_string()), - StringPart::highlighted("multiple different versions".to_string()), - StringPart::normal(" of crate `".to_string()), - StringPart::highlighted(format!("{crate_name}")), - StringPart::normal("` in the dependency graph\n".to_string()), - ]); + err.highlighted_span_note( + span, + vec![ + StringPart::normal("there are ".to_string()), + StringPart::highlighted("multiple different versions".to_string()), + StringPart::normal(" of crate `".to_string()), + StringPart::highlighted(format!("{crate_name}")), + StringPart::normal("` in the dependency graph\n".to_string()), + ], + ); if points_at_type { // We only clarify that the same type from different crate versions are not the // same when we *find* the same type coming from different crate versions, otherwise @@ -1814,7 +1883,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if self.probe(|_| { let ocx = ObligationCtxt::new(self); - self.enter_forall(trait_ref, |obligation_trait_ref| { + self.enter_forall(trait_pred, |obligation_trait_ref| { let impl_args = self.fresh_args_for_item(DUMMY_SP, single.impl_def_id); let impl_trait_ref = ocx.normalize( &ObligationCause::dummy(), @@ -1842,7 +1911,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let mut terrs = vec![]; for (obligation_arg, impl_arg) in - std::iter::zip(obligation_trait_ref.args, impl_trait_ref.args) + std::iter::zip(obligation_trait_ref.trait_ref.args, impl_trait_ref.args) { if (obligation_arg, impl_arg).references_error() { return false; @@ -1884,8 +1953,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } let traits = self.cmp_traits( - obligation_trait_ref.def_id, - &obligation_trait_ref.args[1..], + obligation_trait_ref.def_id(), + &obligation_trait_ref.trait_ref.args[1..], impl_trait_ref.def_id, &impl_trait_ref.args[1..], ); @@ -1906,8 +1975,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { StringPart::normal(" implemented for `"), ]); if types_content.0 == types_content.1 { - let ty = - self.tcx.short_ty_string(obligation_trait_ref.self_ty(), &mut None); + let ty = self + .tcx + .short_string(obligation_trait_ref.self_ty(), err.long_ty_path()); msg.push(StringPart::normal(ty)); } else { msg.extend(types.0.0); @@ -1952,6 +2022,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { StringPart::highlighted(exp_found.found.to_string()), StringPart::normal("`"), ]); + self.suggest_function_pointers_impl(None, &exp_found, err); } true @@ -1969,7 +2040,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } if let &[cand] = &candidates[..] { let (desc, mention_castable) = - match (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind()) { + match (cand.self_ty().kind(), trait_pred.self_ty().skip_binder().kind()) { (ty::FnPtr(..), ty::FnDef(..)) => { (" implemented for fn pointer `", ", cast using `as`") } @@ -2033,7 +2104,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .filter(|cand| !self.tcx.do_not_recommend_impl(cand.impl_def_id)) .collect::>(); - let def_id = trait_ref.def_id(); + let def_id = trait_pred.def_id(); if impl_candidates.is_empty() { if self.tcx.trait_is_auto(def_id) || self.tcx.lang_items().iter().any(|(_, id)| id == def_id) @@ -2093,7 +2164,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let mut code = obligation.cause.code(); let mut trait_pred = trait_predicate; let mut peeled = false; - while let Some((parent_code, parent_trait_pred)) = code.parent() { + while let Some((parent_code, parent_trait_pred)) = code.parent_with_predicate() { code = parent_code; if let Some(parent_trait_pred) = parent_trait_pred { trait_pred = parent_trait_pred; @@ -2110,11 +2181,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && !self.tcx.trait_is_auto(def_id) && !self.tcx.lang_items().iter().any(|(_, id)| id == def_id) { - let trait_ref = trait_pred.to_poly_trait_ref(); let impl_candidates = self.find_similar_impl_candidates(trait_pred); self.report_similar_impl_candidates( &impl_candidates, - trait_ref, + trait_pred, body_def_id, err, true, @@ -2151,12 +2221,16 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait /// with the same path as `trait_ref`, a help message about /// a probable version mismatch is added to `err` - fn note_version_mismatch(&self, err: &mut Diag<'_>, trait_ref: ty::PolyTraitRef<'tcx>) -> bool { + fn note_version_mismatch( + &self, + err: &mut Diag<'_>, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> bool { let get_trait_impls = |trait_def_id| { let mut trait_impls = vec![]; self.tcx.for_each_relevant_impl( trait_def_id, - trait_ref.skip_binder().self_ty(), + trait_pred.skip_binder().self_ty(), |impl_def_id| { trait_impls.push(impl_def_id); }, @@ -2164,11 +2238,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_impls }; - let required_trait_path = self.tcx.def_path_str(trait_ref.def_id()); + let required_trait_path = self.tcx.def_path_str(trait_pred.def_id()); let traits_with_same_path: UnordSet<_> = self .tcx - .all_traits() - .filter(|trait_def_id| *trait_def_id != trait_ref.def_id()) + .visible_traits() + .filter(|trait_def_id| *trait_def_id != trait_pred.def_id()) .map(|trait_def_id| (self.tcx.def_path_str(trait_def_id), trait_def_id)) .filter(|(p, _)| *p == required_trait_path) .collect(); @@ -2260,7 +2334,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // First, attempt to add note to this error with an async-await-specific // message, and fall back to regular note otherwise. if !self.maybe_note_obligation_cause_for_async_await(err, obligation) { - let mut long_ty_file = None; self.note_obligation_cause_code( obligation.cause.body_id, err, @@ -2269,15 +2342,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { obligation.cause.code(), &mut vec![], &mut Default::default(), - &mut long_ty_file, ); - if let Some(file) = long_ty_file { - err.note(format!( - "the full name for the type has been written to '{}'", - file.display(), - )); - err.note("consider using `--verbose` to print the full type name to the console"); - } self.suggest_unsized_bound_if_applicable(err, obligation); if let Some(span) = err.span.primary_span() && let Some(mut diag) = @@ -2321,6 +2386,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { predicate_constness: Option, append_const_msg: Option, post_message: String, + long_ty_file: &mut Option, ) -> String { message .and_then(|cannot_do_this| { @@ -2344,7 +2410,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .unwrap_or_else(|| { format!( "the trait bound `{}` is not satisfied{post_message}", - trait_predicate.print_with_bound_constness(predicate_constness) + self.tcx.short_string( + trait_predicate.print_with_bound_constness(predicate_constness), + long_ty_file, + ), ) }) } @@ -2352,7 +2421,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn get_safe_transmute_error_and_reason( &self, obligation: PredicateObligation<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, span: Span, ) -> GetSafeTransmuteErrorAndReason { use rustc_transmute::Answer; @@ -2364,19 +2433,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } // Erase regions because layout code doesn't particularly care about regions. - let trait_ref = - self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_ref)); + let trait_pred = + self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_pred)); let src_and_dst = rustc_transmute::Types { - dst: trait_ref.args.type_at(0), - src: trait_ref.args.type_at(1), + dst: trait_pred.trait_ref.args.type_at(0), + src: trait_pred.trait_ref.args.type_at(1), }; let ocx = ObligationCtxt::new(self); let Ok(assume) = ocx.structurally_normalize_const( &obligation.cause, obligation.param_env, - trait_ref.args.const_at(2), + trait_pred.trait_ref.args.const_at(2), ) else { self.dcx().span_delayed_bug( span, @@ -2395,8 +2464,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return GetSafeTransmuteErrorAndReason::Silent; }; - let dst = trait_ref.args.type_at(0); - let src = trait_ref.args.type_at(1); + let dst = trait_pred.trait_ref.args.type_at(0); + let src = trait_pred.trait_ref.args.type_at(1); let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`"); match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable( @@ -2544,12 +2613,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_predicate.skip_binder().polarity, ) { - self.add_help_message_for_fn_trait( - trait_predicate.to_poly_trait_ref(), - err, - implemented_kind, - params, - ); + self.add_help_message_for_fn_trait(trait_predicate, err, implemented_kind, params); } else if !trait_predicate.has_non_region_infer() && self.predicate_can_apply(obligation.param_env, trait_predicate) { @@ -2584,7 +2648,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let impl_candidates = self.find_similar_impl_candidates(trait_predicate); if !self.report_similar_impl_candidates( &impl_candidates, - trait_predicate.to_poly_trait_ref(), + trait_predicate, body_def_id, err, true, @@ -2601,7 +2665,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.suggest_convert_to_slice( err, obligation, - trait_predicate.to_poly_trait_ref(), + trait_predicate, impl_candidates.as_slice(), span, ); @@ -2612,7 +2676,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn add_help_message_for_fn_trait( &self, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, err: &mut Diag<'_>, implemented_kind: ty::ClosureKind, params: ty::Binder<'tcx, Ty<'tcx>>, @@ -2625,12 +2689,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // to implement. let selected_kind = self .tcx - .fn_trait_kind_from_def_id(trait_ref.def_id()) + .fn_trait_kind_from_def_id(trait_pred.def_id()) .expect("expected to map DefId to ClosureKind"); if !implemented_kind.extends(selected_kind) { err.note(format!( "`{}` implements `{}`, but it must implement `{}`, which is more general", - trait_ref.skip_binder().self_ty(), + trait_pred.skip_binder().self_ty(), implemented_kind, selected_kind )); @@ -2638,7 +2702,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Note any argument mismatches let given_ty = params.skip_binder(); - let expected_ty = trait_ref.skip_binder().args.type_at(1); + let expected_ty = trait_pred.skip_binder().trait_ref.args.type_at(1); if let ty::Tuple(given) = given_ty.kind() && let ty::Tuple(expected) = expected_ty.kind() { @@ -2910,7 +2974,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }) .collect::>>()?, ), - Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref sig, ..), .. }) + Node::Item(&hir::Item { kind: hir::ItemKind::Fn { ref sig, .. }, .. }) | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref sig, _), .. }) | Node::TraitItem(&hir::TraitItem { kind: hir::TraitItemKind::Fn(ref sig, _), .. @@ -2925,12 +2989,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .inputs .iter() .map(|arg| match arg.kind { - hir::TyKind::Tup(tys) => { - ArgKind::Tuple(Some(arg.span), vec![ - ("_".to_owned(), "_".to_owned()); - tys.len() - ]) - } + hir::TyKind::Tup(tys) => ArgKind::Tuple( + Some(arg.span), + vec![("_".to_owned(), "_".to_owned()); tys.len()], + ), _ => ArgKind::empty(), }) .collect::>(), diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index b108a9352a53d..e4f250ca4f540 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -1,4 +1,5 @@ pub mod ambiguity; +pub mod call_kind; mod fulfillment_errors; pub mod on_unimplemented; mod overflow; @@ -10,7 +11,7 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{Applicability, Diag, E0038, E0276, MultiSpan, struct_span_code_err}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; -use rustc_hir::{self as hir, LangItem}; +use rustc_hir::{self as hir, AmbigArg, LangItem}; use rustc_infer::traits::{ DynCompatibilityViolation, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, SelectionError, @@ -86,9 +87,9 @@ impl<'v> Visitor<'v> for FindExprBySpan<'v> { } } - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) { if self.span == ty.span { - self.ty_result = Some(ty); + self.ty_result = Some(ty.as_unambig_ty()); } else { hir::intravisit::walk_ty(self, ty); } @@ -171,8 +172,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { { 1 } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => 3, ty::PredicateKind::Coerce(_) => 2, + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => 3, _ => 0, }); @@ -305,7 +306,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let ObligationCauseCode::WhereClause(..) | ObligationCauseCode::WhereClauseInExpr(..) = code { - let mut long_ty_file = None; self.note_obligation_cause_code( error.obligation.cause.body_id, &mut diag, @@ -314,17 +314,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { code, &mut vec![], &mut Default::default(), - &mut long_ty_file, ); - if let Some(file) = long_ty_file { - diag.note(format!( - "the full name for the type has been written to '{}'", - file.display(), - )); - diag.note( - "consider using `--verbose` to print the full type name to the console", - ); - } } diag.emit() } @@ -432,33 +422,19 @@ pub fn report_dyn_incompatibility<'tcx>( hir::Node::Item(item) => Some(item.ident.span), _ => None, }); + let mut err = struct_span_code_err!( tcx.dcx(), span, E0038, - "the trait `{}` cannot be made into an object", + "the {} `{}` is not dyn compatible", + tcx.def_descr(trait_def_id), trait_str ); - err.span_label(span, format!("`{trait_str}` cannot be made into an object")); - - if let Some(hir_id) = hir_id - && let hir::Node::Ty(ty) = tcx.hir_node(hir_id) - && let hir::TyKind::TraitObject([trait_ref, ..], ..) = ty.kind - { - let mut hir_id = hir_id; - while let hir::Node::Ty(ty) = tcx.parent_hir_node(hir_id) { - hir_id = ty.hir_id; - } - if tcx.parent_hir_node(hir_id).fn_sig().is_some() { - // Do not suggest `impl Trait` when dealing with things like super-traits. - err.span_suggestion_verbose( - ty.span.until(trait_ref.span), - "consider using an opaque type instead", - "impl ", - Applicability::MaybeIncorrect, - ); - } - } + err.span_label(span, format!("`{trait_str}` is not dyn compatible")); + + attempt_dyn_to_impl_suggestion(tcx, hir_id, &mut err); + let mut reported_violations = FxIndexSet::default(); let mut multi_span = vec![]; let mut messages = vec![]; @@ -473,7 +449,7 @@ pub fn report_dyn_incompatibility<'tcx>( if reported_violations.insert(violation.clone()) { let spans = violation.spans(); let msg = if trait_span.is_none() || spans.is_empty() { - format!("the trait cannot be made into an object because {}", violation.error_msg()) + format!("the trait is not dyn compatible because {}", violation.error_msg()) } else { format!("...because {}", violation.error_msg()) }; @@ -490,24 +466,19 @@ pub fn report_dyn_incompatibility<'tcx>( let has_multi_span = !multi_span.is_empty(); let mut note_span = MultiSpan::from_spans(multi_span.clone()); if let (Some(trait_span), true) = (trait_span, has_multi_span) { - note_span.push_span_label(trait_span, "this trait cannot be made into an object..."); + note_span.push_span_label(trait_span, "this trait is not dyn compatible..."); } for (span, msg) in iter::zip(multi_span, messages) { note_span.push_span_label(span, msg); } - // FIXME(dyn_compat_renaming): Update the URL. err.span_note( note_span, - "for a trait to be \"dyn-compatible\" it needs to allow building a vtable to allow the call \ - to be resolvable dynamically; for more information visit \ - ", + "for a trait to be dyn compatible it needs to allow building a vtable\n\ + for more information, visit ", ); // Only provide the help if its a local trait, otherwise it's not actionable. if trait_span.is_some() { - let mut reported_violations: Vec<_> = reported_violations.into_iter().collect(); - reported_violations.sort(); - let mut potential_solutions: Vec<_> = reported_violations.into_iter().map(|violation| violation.solution()).collect(); potential_solutions.sort(); @@ -518,68 +489,116 @@ pub fn report_dyn_incompatibility<'tcx>( } } + attempt_dyn_to_enum_suggestion(tcx, trait_def_id, &*trait_str, &mut err); + + err +} + +/// Attempt to suggest converting the `dyn Trait` argument to an enumeration +/// over the types that implement `Trait`. +fn attempt_dyn_to_enum_suggestion( + tcx: TyCtxt<'_>, + trait_def_id: DefId, + trait_str: &str, + err: &mut Diag<'_>, +) { let impls_of = tcx.trait_impls_of(trait_def_id); - let impls = if impls_of.blanket_impls().is_empty() { - impls_of - .non_blanket_impls() - .values() - .flatten() - .filter(|def_id| { - !matches!(tcx.type_of(*def_id).instantiate_identity().kind(), ty::Dynamic(..)) - }) - .collect::>() - } else { - vec![] - }; - let externally_visible = if !impls.is_empty() - && let Some(def_id) = trait_def_id.as_local() + + if !impls_of.blanket_impls().is_empty() { + return; + } + + let concrete_impls: Option>> = impls_of + .non_blanket_impls() + .values() + .flatten() + .map(|impl_id| { + // Don't suggest conversion to enum if the impl types have type parameters. + // It's unlikely the user wants to define a generic enum. + let Some(impl_type) = tcx.type_of(*impl_id).no_bound_vars() else { return None }; + + // Obviously unsized impl types won't be usable in an enum. + // Note: this doesn't use `Ty::is_trivially_sized` because that function + // defaults to assuming that things are *not* sized, whereas we want to + // fall back to assuming that things may be sized. + match impl_type.kind() { + ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::DynKind::Dyn) => { + return None; + } + _ => {} + } + Some(impl_type) + }) + .collect(); + let Some(concrete_impls) = concrete_impls else { return }; + + const MAX_IMPLS_TO_SUGGEST_CONVERTING_TO_ENUM: usize = 9; + if concrete_impls.is_empty() || concrete_impls.len() > MAX_IMPLS_TO_SUGGEST_CONVERTING_TO_ENUM { + return; + } + + let externally_visible = if let Some(def_id) = trait_def_id.as_local() { // We may be executing this during typeck, which would result in cycle // if we used effective_visibilities query, which looks into opaque types // (and therefore calls typeck). - && tcx.resolutions(()).effective_visibilities.is_exported(def_id) - { - true + tcx.resolutions(()).effective_visibilities.is_exported(def_id) } else { false }; - match &impls[..] { - [] => {} - _ if impls.len() > 9 => {} - [only] if externally_visible => { - err.help(with_no_trimmed_paths!(format!( - "only type `{}` is seen to implement the trait in this crate, consider using it \ - directly instead", - tcx.type_of(*only).instantiate_identity(), - ))); - } - [only] => { - err.help(with_no_trimmed_paths!(format!( - "only type `{}` implements the trait, consider using it directly instead", - tcx.type_of(*only).instantiate_identity(), - ))); - } - impls => { - let types = impls - .iter() - .map(|t| { - with_no_trimmed_paths!(format!(" {}", tcx.type_of(*t).instantiate_identity(),)) - }) - .collect::>(); - err.help(format!( - "the following types implement the trait, consider defining an enum where each \ - variant holds one of these types, implementing `{}` for this new enum and using \ - it instead:\n{}", - trait_str, - types.join("\n"), - )); - } + + if let [only_impl] = &concrete_impls[..] { + let within = if externally_visible { " within this crate" } else { "" }; + err.help(with_no_trimmed_paths!(format!( + "only type `{only_impl}` implements `{trait_str}`{within}; \ + consider using it directly instead." + ))); + } else { + let types = concrete_impls + .iter() + .map(|t| with_no_trimmed_paths!(format!(" {}", t))) + .collect::>() + .join("\n"); + + err.help(format!( + "the following types implement `{trait_str}`:\n\ + {types}\n\ + consider defining an enum where each variant holds one of these types,\n\ + implementing `{trait_str}` for this new enum and using it instead", + )); } + if externally_visible { err.note(format!( - "`{trait_str}` can be implemented in other crates; if you want to support your users \ + "`{trait_str}` may be implemented in other crates; if you want to support your users \ passing their own types here, you can't refer to a specific type", )); } +} - err +/// Attempt to suggest that a `dyn Trait` argument or return type be converted +/// to use `impl Trait`. +fn attempt_dyn_to_impl_suggestion(tcx: TyCtxt<'_>, hir_id: Option, err: &mut Diag<'_>) { + let Some(hir_id) = hir_id else { return }; + let hir::Node::Ty(ty) = tcx.hir_node(hir_id) else { return }; + let hir::TyKind::TraitObject([trait_ref, ..], ..) = ty.kind else { return }; + + // Only suggest converting `dyn` to `impl` if we're in a function signature. + // This ensures that we don't suggest converting e.g. + // `type Alias = Box;` to + // `type Alias = Box;` + let Some((_id, first_non_type_parent_node)) = + tcx.hir().parent_iter(hir_id).find(|(_id, node)| !matches!(node, hir::Node::Ty(_))) + else { + return; + }; + if first_non_type_parent_node.fn_sig().is_none() { + return; + } + + err.span_suggestion_verbose( + ty.span.until(trait_ref.span), + "consider using an opaque type instead", + "impl ", + Applicability::MaybeIncorrect, + ); } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index a401fcf35057d..518323f6526a6 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -10,10 +10,10 @@ use rustc_hir::{AttrArgs, AttrKind, Attribute}; use rustc_macros::LintDiagnostic; use rustc_middle::bug; use rustc_middle::ty::print::PrintTraitRefExt as _; -use rustc_middle::ty::{self, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, TyCtxt}; +use rustc_middle::ty::{self, GenericArgsRef, GenericParamDefKind, TyCtxt}; use rustc_parse_format::{ParseMode, Parser, Piece, Position}; use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES; -use rustc_span::{Span, Symbol, kw, sym}; +use rustc_span::{Ident, Span, Symbol, kw, sym}; use tracing::{debug, info}; use {rustc_attr_parsing as attr, rustc_hir as hir}; @@ -42,18 +42,18 @@ static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[ impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn impl_similar_to( &self, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, obligation: &PredicateObligation<'tcx>, ) -> Option<(DefId, GenericArgsRef<'tcx>)> { let tcx = self.tcx; let param_env = obligation.param_env; - self.enter_forall(trait_ref, |trait_ref| { - let trait_self_ty = trait_ref.self_ty(); + self.enter_forall(trait_pred, |trait_pred| { + let trait_self_ty = trait_pred.self_ty(); let mut self_match_impls = vec![]; let mut fuzzy_match_impls = vec![]; - self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| { + self.tcx.for_each_relevant_impl(trait_pred.def_id(), trait_self_ty, |def_id| { let impl_args = self.fresh_args_for_item(obligation.cause.span, def_id); let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().instantiate(tcx, impl_args); @@ -64,7 +64,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { self_match_impls.push((def_id, impl_args)); if iter::zip( - trait_ref.args.types().skip(1), + trait_pred.trait_ref.args.types().skip(1), impl_trait_ref.args.types().skip(1), ) .all(|(u, v)| self.fuzzy_match_tys(u, v, false).is_some()) @@ -91,7 +91,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { /// to be the enclosing (async) block/function/closure fn describe_enclosure(&self, def_id: LocalDefId) -> Option<&'static str> { match self.tcx.hir_node_by_def_id(def_id) { - hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }) => Some("a function"), + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { .. }, .. }) => Some("a function"), hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) => { Some("a trait method") } @@ -117,7 +117,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } let (def_id, args) = self - .impl_similar_to(trait_pred.to_poly_trait_ref(), obligation) + .impl_similar_to(trait_pred, obligation) .unwrap_or_else(|| (trait_pred.def_id(), trait_pred.skip_binder().trait_ref.args)); let trait_pred = trait_pred.skip_binder(); @@ -205,9 +205,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if self_ty.is_fn() { let fn_sig = self_ty.fn_sig(self.tcx); - let shortname = match fn_sig.safety() { - hir::Safety::Safe => "fn", - hir::Safety::Unsafe => "unsafe fn", + let shortname = if let ty::FnDef(def_id, _) = self_ty.kind() + && self.tcx.codegen_fn_attrs(def_id).safe_target_features + { + "#[target_feature] fn" + } else { + match fn_sig.safety() { + hir::Safety::Safe => "fn", + hir::Safety::Unsafe => "unsafe fn", + } }; flags.push((sym::_Self, Some(shortname.to_owned()))); } @@ -369,7 +375,7 @@ impl IgnoredDiagnosticOption { #[help] pub struct UnknownFormatParameterForOnUnimplementedAttr { argument_name: Symbol, - trait_name: Symbol, + trait_name: Ident, } #[derive(LintDiagnostic)] @@ -786,14 +792,14 @@ impl<'tcx> OnUnimplementedFormatString { tcx.trait_id_of_impl(item_def_id) .expect("expected `on_unimplemented` to correspond to a trait") }; - let trait_name = tcx.item_name(trait_def_id); + let trait_name = tcx.item_ident(trait_def_id); let generics = tcx.generics_of(item_def_id); let s = self.symbol.as_str(); let mut parser = Parser::new(s, None, None, false, ParseMode::Format); let mut result = Ok(()); for token in &mut parser { match token { - Piece::String(_) => (), // Normal string, no need to check it + Piece::Lit(_) => (), // Normal string, no need to check it Piece::NextArgument(a) => { let format_spec = a.format; if self.is_diagnostic_namespace_variant @@ -815,7 +821,11 @@ impl<'tcx> OnUnimplementedFormatString { Position::ArgumentNamed(s) => { match Symbol::intern(s) { // `{ThisTraitsName}` is allowed - s if s == trait_name && !self.is_diagnostic_namespace_variant => (), + s if s == trait_name.name + && !self.is_diagnostic_namespace_variant => + { + () + } s if ALLOWED_FORMAT_SYMBOLS.contains(&s) && !self.is_diagnostic_namespace_variant => { @@ -922,7 +932,7 @@ impl<'tcx> OnUnimplementedFormatString { let value = match param.kind { GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { if let Some(ty) = trait_ref.args[param.index as usize].as_type() { - tcx.short_ty_string(ty, long_ty_file) + tcx.short_string(ty, long_ty_file) } else { trait_ref.args[param.index as usize].to_string() } @@ -940,7 +950,7 @@ impl<'tcx> OnUnimplementedFormatString { let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string); let constructed_message = (&mut parser) .map(|p| match p { - Piece::String(s) => s.to_owned(), + Piece::Lit(s) => s.to_owned(), Piece::NextArgument(a) => match a.position { Position::ArgumentNamed(arg) => { let s = Symbol::intern(arg); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs index d82acc4e0543f..fad03b5e9bf5a 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs @@ -141,7 +141,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { obligation.cause.span, suggest_increasing_limit, |err| { - let mut long_ty_file = None; self.note_obligation_cause_code( obligation.cause.body_id, err, @@ -150,17 +149,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { obligation.cause.code(), &mut vec![], &mut Default::default(), - &mut long_ty_file, ); - if let Some(file) = long_ty_file { - err.note(format!( - "the full name for the type has been written to '{}'", - file.display(), - )); - err.note( - "consider using `--verbose` to print the full type name to the console", - ); - } }, ); } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 6c0676270ece2..21564c147a35d 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3,7 +3,6 @@ use std::assert_matches::debug_assert_matches; use std::borrow::Cow; use std::iter; -use std::path::PathBuf; use itertools::{EitherOrBoth, Itertools}; use rustc_abi::ExternAbi; @@ -14,14 +13,13 @@ use rustc_errors::{ Applicability, Diag, EmissionGuarantee, MultiSpan, Style, SuggestionStyle, pluralize, struct_span_code_err, }; -use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::{Visitor, VisitorExt}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ - CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node, expr_needs_parens, - is_range_literal, + self as hir, AmbigArg, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node, + expr_needs_parens, is_range_literal, }; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk}; use rustc_middle::hir::map; @@ -32,9 +30,9 @@ use rustc_middle::ty::print::{ with_forced_trimmed_paths, with_no_trimmed_paths, }; use rustc_middle::ty::{ - self, AdtKind, GenericArgs, InferTy, IsSuggestable, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable, - TypeFolder, TypeSuperFoldable, TypeVisitableExt, TypeckResults, Upcast, - suggest_arbitrary_trait_bound, suggest_constraining_type_param, + self, AdtKind, GenericArgs, InferTy, IsSuggestable, Ty, TyCtxt, TypeFoldable, TypeFolder, + TypeSuperFoldable, TypeVisitableExt, TypeckResults, Upcast, suggest_arbitrary_trait_bound, + suggest_constraining_type_param, }; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::LocalDefId; @@ -179,7 +177,7 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>( let mut ty_spans = vec![]; for input in fn_sig.decl.inputs { ReplaceImplTraitVisitor { ty_spans: &mut ty_spans, param_did: param.def_id } - .visit_ty(input); + .visit_ty_unambig(input); } // The type param `T: Trait` we will suggest to introduce. let type_param = format!("{type_param_name}: {bound_str}"); @@ -218,15 +216,15 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>( (_, None) => predicate_constraint(hir_generics, trait_pred.upcast(tcx)), (None, Some((ident, []))) => ( ident.span.shrink_to_hi(), - format!(": {}", trait_pred.to_poly_trait_ref().print_trait_sugared()), + format!(": {}", trait_pred.print_modifiers_and_trait_path()), ), (_, Some((_, [.., bounds]))) => ( bounds.span().shrink_to_hi(), - format!(" + {}", trait_pred.to_poly_trait_ref().print_trait_sugared()), + format!(" + {}", trait_pred.print_modifiers_and_trait_path()), ), (Some(_), Some((_, []))) => ( hir_generics.span.shrink_to_hi(), - format!(": {}", trait_pred.to_poly_trait_ref().print_trait_sugared()), + format!(": {}", trait_pred.print_modifiers_and_trait_path()), ), }; @@ -311,7 +309,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .. }) | hir::Node::Item(hir::Item { - kind: hir::ItemKind::Fn(fn_sig, generics, _), .. + kind: hir::ItemKind::Fn { sig: fn_sig, generics, .. }, + .. }) if projection.is_some() => { // Missing restriction on associated type of type parameter (unmet projection). suggest_restriction( @@ -355,7 +354,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { | hir::ItemKind::Union(_, generics) | hir::ItemKind::Trait(_, _, generics, ..) | hir::ItemKind::Impl(hir::Impl { generics, .. }) - | hir::ItemKind::Fn(_, generics, _) + | hir::ItemKind::Fn { generics, .. } | hir::ItemKind::TyAlias(_, generics) | hir::ItemKind::Const(_, generics, _) | hir::ItemKind::TraitAlias(generics, _), @@ -420,7 +419,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { | hir::ItemKind::Union(_, generics) | hir::ItemKind::Trait(_, _, generics, ..) | hir::ItemKind::Impl(hir::Impl { generics, .. }) - | hir::ItemKind::Fn(_, generics, _) + | hir::ItemKind::Fn { generics, .. } | hir::ItemKind::TyAlias(_, generics) | hir::ItemKind::Const(_, generics, _) | hir::ItemKind::TraitAlias(generics, _), @@ -463,7 +462,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Get the root obligation, since the leaf obligation we have may be unhelpful (#87437) let mut real_trait_pred = trait_pred; - while let Some((parent_code, parent_trait_pred)) = code.parent() { + while let Some((parent_code, parent_trait_pred)) = code.parent_with_predicate() { code = parent_code; if let Some(parent_trait_pred) = parent_trait_pred { real_trait_pred = parent_trait_pred; @@ -641,10 +640,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } // Empty suggestions with empty spans ICE with debug assertions if steps == 0 { - return (msg.trim_end_matches(" and dereferencing instead"), vec![( - prefix_span, - String::new(), - )]); + return ( + msg.trim_end_matches(" and dereferencing instead"), + vec![(prefix_span, String::new())], + ); } let derefs = "*".repeat(steps); let needs_parens = steps > 0 @@ -846,7 +845,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; name.to_string() } - Some(hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(..), .. })) => { + Some(hir::Node::Item(hir::Item { + ident, kind: hir::ItemKind::Fn { .. }, .. + })) => { err.span_label(ident.span, "consider calling this function"); ident.to_string() } @@ -1085,28 +1086,27 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { sig_parts.map_bound(|sig| sig.tupled_inputs_ty.tuple_fields().as_slice()), )) } - ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => self - .tcx - .item_super_predicates(def_id) - .instantiate(self.tcx, args) - .iter() - .find_map(|pred| { - if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder() + ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { + self.tcx.item_self_bounds(def_id).instantiate(self.tcx, args).iter().find_map( + |pred| { + if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder() && self .tcx .is_lang_item(proj.projection_term.def_id, LangItem::FnOnceOutput) // args tuple will always be args[1] && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind() - { - Some(( - DefIdOrName::DefId(def_id), - pred.kind().rebind(proj.term.expect_type()), - pred.kind().rebind(args.as_slice()), - )) - } else { - None - } - }), + { + Some(( + DefIdOrName::DefId(def_id), + pred.kind().rebind(proj.term.expect_type()), + pred.kind().rebind(args.as_slice()), + )) + } else { + None + } + }, + ) + } ty::Dynamic(data, _, ty::Dyn) => data.iter().find_map(|pred| { if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder() && self.tcx.is_lang_item(proj.def_id, LangItem::FnOnceOutput) @@ -1296,30 +1296,24 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Because of this, we modify the error to refer to the original obligation and // return early in the caller. - let msg = format!("the trait bound `{old_pred}` is not satisfied"); + let msg = format!( + "the trait bound `{}` is not satisfied", + self.tcx.short_string(old_pred, err.long_ty_path()), + ); + let self_ty_str = + self.tcx.short_string(old_pred.self_ty().skip_binder(), err.long_ty_path()); if has_custom_message { err.note(msg); } else { err.messages = vec![(rustc_errors::DiagMessage::from(msg), Style::NoStyle)]; } - let mut file = None; err.span_label( span, format!( - "the trait `{}` is not implemented for `{}`", - old_pred.print_modifiers_and_trait_path(), - self.tcx.short_ty_string(old_pred.self_ty().skip_binder(), &mut file), + "the trait `{}` is not implemented for `{self_ty_str}`", + old_pred.print_modifiers_and_trait_path() ), ); - if let Some(file) = file { - err.note(format!( - "the full type name has been written to '{}'", - file.display() - )); - err.note( - "consider using `--verbose` to print full type name to the console", - ); - } if imm_ref_self_ty_satisfies_pred && mut_ref_self_ty_satisfies_pred { err.span_suggestions( @@ -1444,7 +1438,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let mut span = obligation.cause.span; let mut trait_pred = trait_pred; let mut code = obligation.cause.code(); - while let Some((c, Some(parent_trait_pred))) = code.parent() { + while let Some((c, Some(parent_trait_pred))) = code.parent_with_predicate() { // We want the root obligation, in order to detect properly handle // `for _ in &mut &mut vec![] {}`. code = c; @@ -1711,7 +1705,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) -> bool { let hir = self.tcx.hir(); let node = self.tcx.hir_node_by_def_id(obligation.cause.body_id); - if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) = node + if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn {sig, body: body_id, .. }, .. }) = node && let hir::ExprKind::Block(blk, _) = &hir.body(*body_id).value.kind && sig.decl.output.span().overlaps(span) && blk.expr.is_none() @@ -1745,7 +1739,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } pub(super) fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option { - let hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, ..), .. }) = + let hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { sig, .. }, .. }) = self.tcx.hir_node_by_def_id(obligation.cause.body_id) else { return None; @@ -1859,7 +1853,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let hir = self.tcx.hir(); let node = self.tcx.hir_node_by_def_id(obligation.cause.body_id); - if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) = node { + if let hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn { body: body_id, .. }, .. + }) = node + { let body = hir.body(*body_id); // Point at all the `return`s in the function as they have failed trait bounds. let mut visitor = ReturnsVisitor::default(); @@ -2685,7 +2682,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Add a note for the item obligation that remains - normally a note pointing to the // bound that introduced the obligation (e.g. `T: Send`). debug!(?next_code); - let mut long_ty_file = None; self.note_obligation_cause_code( obligation.cause.body_id, err, @@ -2694,7 +2690,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { next_code.unwrap(), &mut Vec::new(), &mut Default::default(), - &mut long_ty_file, ); } @@ -2707,7 +2702,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { cause_code: &ObligationCauseCode<'tcx>, obligated_types: &mut Vec>, seen_requirements: &mut FxHashSet, - long_ty_file: &mut Option, ) where T: Upcast, ty::Predicate<'tcx>>, { @@ -2734,7 +2728,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { | ObligationCauseCode::IfExpression { .. } | ObligationCauseCode::IfExpressionWithNoElse | ObligationCauseCode::MainFunctionType - | ObligationCauseCode::StartFunctionType | ObligationCauseCode::LangFunctionType(_) | ObligationCauseCode::IntrinsicType | ObligationCauseCode::MethodReceiver @@ -2766,6 +2759,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ObligationCauseCode::SliceOrArrayElem => { err.note("slice and array elements must have `Sized` type"); } + ObligationCauseCode::ArrayLen(array_ty) => { + err.note(format!("the length of array `{array_ty}` must be type `usize`")); + } ObligationCauseCode::TupleElem => { err.note("only the last element of a tuple may have a dynamically sized type"); } @@ -2959,9 +2955,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } ObligationCauseCode::Coercion { source, target } => { let source = - tcx.short_ty_string(self.resolve_vars_if_possible(source), long_ty_file); + tcx.short_string(self.resolve_vars_if_possible(source), err.long_ty_path()); let target = - tcx.short_ty_string(self.resolve_vars_if_possible(target), long_ty_file); + tcx.short_string(self.resolve_vars_if_possible(target), err.long_ty_path()); err.note(with_forced_trimmed_paths!(format!( "required for the cast from `{source}` to `{target}`", ))); @@ -3069,7 +3065,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } if let Some(ty) = ty { match ty.kind { - hir::TyKind::TraitObject(traits, _, _) => { + hir::TyKind::TraitObject(traits, _) => { let (span, kw) = match traits { [first, ..] if first.span.lo() == ty.span.lo() => { // Missing `dyn` in front of trait object. @@ -3246,7 +3242,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; if !is_upvar_tys_infer_tuple { - let ty_str = tcx.short_ty_string(ty, long_ty_file); + let ty_str = tcx.short_string(ty, err.long_ty_path()); let msg = format!("required because it appears within the type `{ty_str}`"); match ty.kind() { ty::Adt(def, _) => match tcx.opt_item_ident(def.did()) { @@ -3324,7 +3320,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &data.parent_code, obligated_types, seen_requirements, - long_ty_file, ) }); } else { @@ -3337,7 +3332,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { cause_code.peel_derives(), obligated_types, seen_requirements, - long_ty_file, ) }); } @@ -3347,7 +3341,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.resolve_vars_if_possible(data.derived.parent_trait_pred); let parent_def_id = parent_trait_pred.def_id(); let self_ty_str = - tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty(), long_ty_file); + tcx.short_string(parent_trait_pred.skip_binder().self_ty(), err.long_ty_path()); let trait_name = parent_trait_pred.print_modifiers_and_trait_path().to_string(); let msg = format!("required for `{self_ty_str}` to implement `{trait_name}`"); let mut is_auto_trait = false; @@ -3443,8 +3437,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { count, pluralize!(count) )); - let self_ty = tcx - .short_ty_string(parent_trait_pred.skip_binder().self_ty(), long_ty_file); + let self_ty = tcx.short_string( + parent_trait_pred.skip_binder().self_ty(), + err.long_ty_path(), + ); err.note(format!( "required for `{self_ty}` to implement `{}`", parent_trait_pred.print_modifiers_and_trait_path() @@ -3460,7 +3456,57 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &data.parent_code, obligated_types, seen_requirements, - long_ty_file, + ) + }); + } + ObligationCauseCode::ImplDerivedHost(ref data) => { + let self_ty = + self.resolve_vars_if_possible(data.derived.parent_host_pred.self_ty()); + let msg = format!( + "required for `{self_ty}` to implement `{} {}`", + data.derived.parent_host_pred.skip_binder().constness, + data.derived + .parent_host_pred + .map_bound(|pred| pred.trait_ref) + .print_only_trait_path(), + ); + match tcx.hir().get_if_local(data.impl_def_id) { + Some(Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }), + .. + })) => { + let mut spans = vec![self_ty.span]; + spans.extend(of_trait.as_ref().map(|t| t.path.span)); + let mut spans: MultiSpan = spans.into(); + spans.push_span_label(data.span, "unsatisfied trait bound introduced here"); + err.span_note(spans, msg); + } + _ => { + err.note(msg); + } + } + ensure_sufficient_stack(|| { + self.note_obligation_cause_code( + body_id, + err, + data.derived.parent_host_pred, + param_env, + &data.derived.parent_code, + obligated_types, + seen_requirements, + ) + }); + } + ObligationCauseCode::BuiltinDerivedHost(ref data) => { + ensure_sufficient_stack(|| { + self.note_obligation_cause_code( + body_id, + err, + data.parent_host_pred, + param_env, + &data.parent_code, + obligated_types, + seen_requirements, ) }); } @@ -3477,7 +3523,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &data.parent_code, obligated_types, seen_requirements, - long_ty_file, ) }); } @@ -3492,7 +3537,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { nested, obligated_types, seen_requirements, - long_ty_file, ) }); let mut multispan = MultiSpan::from(span); @@ -3523,7 +3567,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { parent_code, obligated_types, seen_requirements, - long_ty_file, ) }); } @@ -3556,14 +3599,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } ObligationCauseCode::TrivialBound => { err.help("see issue #48214"); - tcx.disabled_nightly_features(err, Some(tcx.local_def_id_to_hir_id(body_id)), [( - String::new(), - sym::trivial_bounds, - )]); + tcx.disabled_nightly_features( + err, + Some(tcx.local_def_id_to_hir_id(body_id)), + [(String::new(), sym::trivial_bounds)], + ); } ObligationCauseCode::OpaqueReturnType(expr_info) => { let (expr_ty, expr) = if let Some((expr_ty, hir_id)) = expr_info { - let expr_ty = tcx.short_ty_string(expr_ty, long_ty_file); + let expr_ty = tcx.short_string(expr_ty, err.long_ty_path()); let expr = tcx.hir().expect_expr(hir_id); (expr_ty, expr) } else if let Some(body_id) = tcx.hir_node_by_def_id(body_id).body_id() @@ -3578,7 +3622,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && let ty::ClauseKind::Trait(pred) = pred.kind().skip_binder() && self.can_eq(param_env, pred.self_ty(), expr_ty) { - let expr_ty = tcx.short_ty_string(expr_ty, long_ty_file); + let expr_ty = tcx.short_string(expr_ty, err.long_ty_path()); (expr_ty, expr) } else { return; @@ -3670,7 +3714,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, ) { let rhs_span = match obligation.cause.code() { ObligationCauseCode::BinOp { rhs_span: Some(span), rhs_is_lit, .. } if *rhs_is_lit => { @@ -3678,8 +3722,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } _ => return, }; - if let ty::Float(_) = trait_ref.skip_binder().self_ty().kind() - && let ty::Infer(InferTy::IntVar(_)) = trait_ref.skip_binder().args.type_at(1).kind() + if let ty::Float(_) = trait_pred.skip_binder().self_ty().kind() + && let ty::Infer(InferTy::IntVar(_)) = + trait_pred.skip_binder().trait_ref.args.type_at(1).kind() { err.span_suggestion_verbose( rhs_span.shrink_to_hi(), @@ -4389,7 +4434,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &self, err: &mut Diag<'_>, obligation: &PredicateObligation<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, candidate_impls: &[ImplCandidate<'tcx>], span: Span, ) { @@ -4405,7 +4450,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // 1. `[T; _]` (array of T) // 2. `&[T; _]` (reference to array of T) // 3. `&mut [T; _]` (mutable reference to array of T) - let (element_ty, mut mutability) = match *trait_ref.skip_binder().self_ty().kind() { + let (element_ty, mut mutability) = match *trait_pred.skip_binder().self_ty().kind() { ty::Array(element_ty, _) => (element_ty, None), ty::Ref(_, pointee_ty, mutability) => match *pointee_ty.kind() { @@ -4561,14 +4606,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { pub(super) fn suggest_desugaring_async_fn_in_trait( &self, err: &mut Diag<'_>, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, ) { // Don't suggest if RTN is active -- we should prefer a where-clause bound instead. if self.tcx.features().return_type_notation() { return; } - let trait_def_id = trait_ref.def_id(); + let trait_def_id = trait_pred.def_id(); // Only suggest specifying auto traits if !self.tcx.trait_is_auto(trait_def_id) { @@ -4576,7 +4621,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } // Look for an RPITIT - let ty::Alias(ty::Projection, alias_ty) = trait_ref.self_ty().skip_binder().kind() else { + let ty::Alias(ty::Projection, alias_ty) = trait_pred.self_ty().skip_binder().kind() else { return; }; let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, opaque_def_id }) = @@ -4732,7 +4777,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { node: hir::Node<'hir>, ) -> Option<(&'hir hir::FnDecl<'hir>, hir::BodyId)> { match node { - hir::Node::Item(item) if let hir::ItemKind::Fn(sig, _, body_id) = item.kind => { + hir::Node::Item(item) + if let hir::ItemKind::Fn { sig, body: body_id, .. } = item.kind => + { Some((sig.decl, body_id)) } hir::Node::ImplItem(item) @@ -5004,7 +5051,7 @@ pub struct SelfVisitor<'v> { } impl<'v> Visitor<'v> for SelfVisitor<'v> { - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) { if let hir::TyKind::Path(path) = ty.kind && let hir::QPath::TypeRelative(inner_ty, segment) = path && (Some(segment.ident.name) == self.name || self.name.is_none()) @@ -5012,7 +5059,7 @@ impl<'v> Visitor<'v> for SelfVisitor<'v> { && let hir::QPath::Resolved(None, inner_path) = inner_path && let Res::SelfTyAlias { .. } = inner_path.res { - self.paths.push(ty); + self.paths.push(ty.as_unambig_ty()); } hir::intravisit::walk_ty(self, ty); } @@ -5126,7 +5173,7 @@ struct ReplaceImplTraitVisitor<'a> { } impl<'a, 'hir> hir::intravisit::Visitor<'hir> for ReplaceImplTraitVisitor<'a> { - fn visit_ty(&mut self, t: &'hir hir::Ty<'hir>) { + fn visit_ty(&mut self, t: &'hir hir::Ty<'hir, AmbigArg>) { if let hir::TyKind::Path(hir::QPath::Resolved( None, hir::Path { res: Res::Def(_, segment_did), .. }, @@ -5169,7 +5216,7 @@ pub(super) fn get_explanation_based_on_obligation<'tcx>( format!( "{pre_message}the trait `{}` is not implemented for{desc} `{}`", trait_predicate.print_modifiers_and_trait_path(), - tcx.short_ty_string(trait_predicate.self_ty().skip_binder(), &mut None), + tcx.short_string(trait_predicate.self_ty().skip_binder(), &mut None), ) } else { // "the trait bound `T: !Send` is not satisfied" reads better than "`!Send` is @@ -5419,7 +5466,7 @@ impl<'v> Visitor<'v> for FindTypeParam { // Skip where-clauses, to avoid suggesting indirection for type parameters found there. } - fn visit_ty(&mut self, ty: &hir::Ty<'_>) { + fn visit_ty(&mut self, ty: &hir::Ty<'_, AmbigArg>) { // We collect the spans of all uses of the "bare" type param, like in `field: T` or // `field: (T, T)` where we could make `T: ?Sized` while skipping cases that are known to be // valid like `field: &'a T` or `field: *mut T` and cases that *might* have further `Sized` diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 700c79a706534..62cac5b17bd04 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -6,14 +6,13 @@ use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, DiagMessage, DiagStyledString, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, MultiSpan, SubdiagMessageOp, Subdiagnostic, }; -use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::{Visitor, walk_ty}; -use rustc_hir::{FnRetTy, GenericParamKind, Node}; +use rustc_hir::intravisit::{Visitor, VisitorExt, walk_ty}; +use rustc_hir::{self as hir, AmbigArg, FnRetTy, GenericParamKind, Node}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::print::{PrintTraitRefExt as _, TraitRefPrintOnlyTraitPath}; -use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, PolyTraitRef, Region, Ty, TyCtxt}; +use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, Region, Ty, TyCtxt}; use rustc_span::{BytePos, Ident, Span, Symbol, kw}; use crate::error_reporting::infer::ObligationCauseAsDiagArg; @@ -23,15 +22,6 @@ use crate::fluent_generated as fluent; pub mod note_and_explain; -#[derive(Diagnostic)] -#[diag(trait_selection_dump_vtable_entries)] -pub struct DumpVTableEntries<'a> { - #[primary_span] - pub span: Span, - pub trait_ref: PolyTraitRef<'a>, - pub entries: String, -} - #[derive(Diagnostic)] #[diag(trait_selection_unable_to_construct_constant_value)] pub struct UnableToConstructConstantValue<'a> { @@ -520,7 +510,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { let is_impl = matches!(&node, hir::Node::ImplItem(_)); let (generics, parent_generics) = match node { hir::Node::Item(&hir::Item { - kind: hir::ItemKind::Fn(_, ref generics, ..), + kind: hir::ItemKind::Fn { ref generics, .. }, .. }) | hir::Node::TraitItem(&hir::TraitItem { ref generics, .. }) @@ -579,7 +569,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { } impl<'v> Visitor<'v> for ImplicitLifetimeFinder { - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) { let make_suggestion = |ident: Ident| { if ident.name == kw::Empty && ident.span.is_empty() { format!("{}, ", self.suggestion_param_name) @@ -642,16 +632,16 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { if let Some(fn_decl) = node.fn_decl() && let hir::FnRetTy::Return(ty) = fn_decl.output { - visitor.visit_ty(ty); + visitor.visit_ty_unambig(ty); } if visitor.suggestions.is_empty() { // Do not suggest constraining the `&self` param, but rather the return type. // If that is wrong (because it is not sufficient), a follow up error will tell the // user to fix it. This way we lower the chances of *over* constraining, but still // get the cake of "correctly" contrained in two steps. - visitor.visit_ty(self.ty_sup); + visitor.visit_ty_unambig(self.ty_sup); } - visitor.visit_ty(self.ty_sub); + visitor.visit_ty_unambig(self.ty_sub); if visitor.suggestions.is_empty() { return false; } @@ -1401,15 +1391,13 @@ pub struct OpaqueCapturesLifetime<'tcx> { pub enum FunctionPointerSuggestion<'a> { #[suggestion( trait_selection_fps_use_ref, - code = "&{fn_name}", + code = "&", style = "verbose", applicability = "maybe-incorrect" )] UseRef { #[primary_span] span: Span, - #[skip_arg] - fn_name: String, }, #[suggestion( trait_selection_fps_remove_ref, @@ -1439,7 +1427,7 @@ pub enum FunctionPointerSuggestion<'a> { }, #[suggestion( trait_selection_fps_cast, - code = "{fn_name} as {sig}", + code = " as {sig}", style = "verbose", applicability = "maybe-incorrect" )] @@ -1447,13 +1435,11 @@ pub enum FunctionPointerSuggestion<'a> { #[primary_span] span: Span, #[skip_arg] - fn_name: String, - #[skip_arg] sig: Binder<'a, FnSig<'a>>, }, #[suggestion( trait_selection_fps_cast_both, - code = "{fn_name} as {found_sig}", + code = " as {found_sig}", style = "hidden", applicability = "maybe-incorrect" )] @@ -1461,8 +1447,6 @@ pub enum FunctionPointerSuggestion<'a> { #[primary_span] span: Span, #[skip_arg] - fn_name: String, - #[skip_arg] found_sig: Binder<'a, FnSig<'a>>, expected_sig: Binder<'a, FnSig<'a>>, }, @@ -1497,6 +1481,12 @@ pub struct FnConsiderCasting { pub casting: String, } +#[derive(Subdiagnostic)] +#[help(trait_selection_fn_consider_casting_both)] +pub struct FnConsiderCastingBoth<'a> { + pub sig: Binder<'a, FnSig<'a>>, +} + #[derive(Subdiagnostic)] pub enum SuggestAccessingField<'a> { #[suggestion( @@ -1695,13 +1685,6 @@ pub enum ObligationCauseFailureCode { #[primary_span] span: Span, }, - #[diag(trait_selection_oc_fn_start_correct_type, code = E0308)] - FnStartCorrectType { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec, - }, #[diag(trait_selection_oc_fn_lang_correct_type, code = E0308)] FnLangCorrectType { #[primary_span] @@ -1729,8 +1712,15 @@ pub enum ObligationCauseFailureCode { #[primary_span] span: Span, }, - #[diag(trait_selection_oc_cant_coerce, code = E0308)] - CantCoerce { + #[diag(trait_selection_oc_cant_coerce_force_inline, code = E0308)] + CantCoerceForceInline { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec, + }, + #[diag(trait_selection_oc_cant_coerce_intrinsic, code = E0308)] + CantCoerceIntrinsic { #[primary_span] span: Span, #[subdiagnostic] diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index ee708564a8046..f373706b2960c 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -47,6 +47,12 @@ impl<'tcx> InferCtxt<'tcx> { traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id) } + fn type_is_clone_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { + let ty = self.resolve_vars_if_possible(ty); + let clone_def_id = self.tcx.require_lang_item(LangItem::Clone, None); + traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, clone_def_id) + } + fn type_is_sized_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { let lang_item = self.tcx.require_lang_item(LangItem::Sized, None); traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, lang_item) diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs index 863b6e293ff89..5517175461876 100644 --- a/compiler/rustc_trait_selection/src/regions.rs +++ b/compiler/rustc_trait_selection/src/regions.rs @@ -1,10 +1,73 @@ +use rustc_hir::def_id::LocalDefId; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{InferCtxt, RegionResolutionError}; use rustc_macros::extension; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::query::NoSolution; +use rustc_middle::ty::{self, Ty}; use crate::traits::ScrubbedTraitError; +use crate::traits::outlives_bounds::InferCtxtExt; + +#[extension(pub trait OutlivesEnvironmentBuildExt<'tcx>)] +impl<'tcx> OutlivesEnvironment<'tcx> { + fn new( + infcx: &InferCtxt<'tcx>, + body_id: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + assumed_wf_tys: impl IntoIterator>, + ) -> Self { + Self::new_with_implied_bounds_compat( + infcx, + body_id, + param_env, + assumed_wf_tys, + !infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat, + ) + } + + fn new_with_implied_bounds_compat( + infcx: &InferCtxt<'tcx>, + body_id: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + assumed_wf_tys: impl IntoIterator>, + implied_bounds_compat: bool, + ) -> Self { + let mut bounds = vec![]; + + for bound in param_env.caller_bounds() { + if let Some(mut type_outlives) = bound.as_type_outlives_clause() { + if infcx.next_trait_solver() { + match crate::solve::deeply_normalize::<_, ScrubbedTraitError<'tcx>>( + infcx.at(&ObligationCause::dummy(), param_env), + type_outlives, + ) { + Ok(new) => type_outlives = new, + Err(_) => { + infcx.dcx().delayed_bug(format!("could not normalize `{bound}`")); + } + } + } + bounds.push(type_outlives); + } + } + + // FIXME: This needs to be modified so that we normalize the known type + // outlives obligations then elaborate them into their region/type components. + // Otherwise, ` as Mirror>::Assoc: 'b` will not imply `'a: 'b` even + // if we can normalize `'a`. + OutlivesEnvironment::from_normalized_bounds( + param_env, + bounds, + infcx.implied_bounds_tys_with_compat( + body_id, + param_env, + assumed_wf_tys, + implied_bounds_compat, + ), + ) + } +} #[extension(pub trait InferCtxtRegionExt<'tcx>)] impl<'tcx> InferCtxt<'tcx> { @@ -15,10 +78,25 @@ impl<'tcx> InferCtxt<'tcx> { /// Prefer this method over `resolve_regions_with_normalize`, unless you are /// doing something specific for normalization. fn resolve_regions( + &self, + body_id: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + assumed_wf_tys: impl IntoIterator>, + ) -> Vec> { + self.resolve_regions_with_outlives_env(&OutlivesEnvironment::new( + self, + body_id, + param_env, + assumed_wf_tys, + )) + } + + /// Don't call this directly unless you know what you're doing. + fn resolve_regions_with_outlives_env( &self, outlives_env: &OutlivesEnvironment<'tcx>, ) -> Vec> { - self.resolve_regions_with_normalize(outlives_env, |ty, origin| { + self.resolve_regions_with_normalize(&outlives_env, |ty, origin| { let ty = self.resolve_vars_if_possible(ty); if self.next_trait_solver() { diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 9b8c9ff6bb834..58d8a3a62547e 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -1,7 +1,7 @@ use std::ops::Deref; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; use rustc_infer::infer::canonical::{ Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarInfo, CanonicalVarValues, @@ -43,8 +43,6 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< self.0.tcx } - type Span = Span; - fn build_with_canonical( interner: TyCtxt<'tcx>, canonical: &CanonicalQueryInput<'tcx, V>, @@ -98,9 +96,10 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< param_env: ty::ParamEnv<'tcx>, arg: ty::GenericArg<'tcx>, ) -> Option>>> { - crate::traits::wf::unnormalized_obligations(&self.0, param_env, arg).map(|obligations| { - obligations.into_iter().map(|obligation| obligation.into()).collect() - }) + crate::traits::wf::unnormalized_obligations(&self.0, param_env, arg, DUMMY_SP, CRATE_DEF_ID) + .map(|obligations| { + obligations.into_iter().map(|obligation| obligation.into()).collect() + }) } fn clone_opaque_types_for_query_response(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> { @@ -146,9 +145,10 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< fn instantiate_canonical_var_with_infer( &self, cv_info: CanonicalVarInfo<'tcx>, + span: Span, universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> ty::GenericArg<'tcx> { - self.0.instantiate_canonical_var(DUMMY_SP, cv_info, universe_map) + self.0.instantiate_canonical_var(span, cv_info, universe_map) } fn insert_hidden_type( @@ -174,11 +174,13 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< self.0.add_item_bounds_for_hidden_type(def_id, args, param_env, hidden_ty, goals); } - fn inject_new_hidden_type_unchecked(&self, key: ty::OpaqueTypeKey<'tcx>, hidden_ty: Ty<'tcx>) { - self.0.inject_new_hidden_type_unchecked(key, ty::OpaqueHiddenType { - ty: hidden_ty, - span: DUMMY_SP, - }) + fn inject_new_hidden_type_unchecked( + &self, + key: ty::OpaqueTypeKey<'tcx>, + hidden_ty: Ty<'tcx>, + span: Span, + ) { + self.0.inject_new_hidden_type_unchecked(key, ty::OpaqueHiddenType { ty: hidden_ty, span }) } fn reset_opaque_types(&self) { @@ -190,9 +192,8 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< goal_trait_ref: ty::TraitRef<'tcx>, trait_assoc_def_id: DefId, impl_def_id: DefId, - ) -> Result, NoSolution> { - let node_item = specialization_graph::assoc_def(self.tcx, impl_def_id, trait_assoc_def_id) - .map_err(|ErrorGuaranteed { .. }| NoSolution)?; + ) -> Result, ErrorGuaranteed> { + let node_item = specialization_graph::assoc_def(self.tcx, impl_def_id, trait_assoc_def_id)?; let eligible = if node_item.is_final() { // Non-specializable items are always projectable. diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 2b2623a050ec9..c238e708ab8fa 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -1,25 +1,21 @@ use std::marker::PhantomData; use std::mem; -use std::ops::ControlFlow; use rustc_data_structures::thinvec::ExtractIf; use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; -use rustc_infer::traits::solve::{CandidateSource, GoalSource, MaybeCause}; use rustc_infer::traits::{ - self, FromSolverError, MismatchedProjectionTypes, Obligation, ObligationCause, - ObligationCauseCode, PredicateObligation, PredicateObligations, SelectionError, TraitEngine, + FromSolverError, PredicateObligation, PredicateObligations, TraitEngine, }; -use rustc_middle::bug; -use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::{self, TyCtxt}; use rustc_next_trait_solver::solve::{GenerateProofTree, HasChanged, SolverDelegateEvalExt as _}; -use tracing::{instrument, trace}; +use tracing::instrument; +use self::derive_errors::*; use super::Certainty; use super::delegate::SolverDelegate; -use super::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor}; -use crate::traits::{FulfillmentError, FulfillmentErrorCode, ScrubbedTraitError}; +use crate::traits::{FulfillmentError, ScrubbedTraitError}; + +mod derive_errors; /// A trait engine using the new trait solver. /// @@ -86,7 +82,7 @@ impl<'tcx> ObligationStorage<'tcx> { self.overflowed.extend(ExtractIf::new(&mut self.pending, |o| { let goal = o.clone().into(); let result = <&SolverDelegate<'tcx>>::from(infcx) - .evaluate_root_goal(goal, GenerateProofTree::No) + .evaluate_root_goal(goal, GenerateProofTree::No, o.cause.span) .0; matches!(result, Ok((HasChanged::Yes, _))) })); @@ -167,7 +163,7 @@ where for obligation in self.obligations.unstalled_for_select() { let goal = obligation.clone().into(); let result = <&SolverDelegate<'tcx>>::from(infcx) - .evaluate_root_goal(goal, GenerateProofTree::No) + .evaluate_root_goal(goal, GenerateProofTree::No, obligation.cause.span) .0; self.inspect_evaluated_obligation(infcx, &obligation, &result); let (changed, certainty) = match result { @@ -244,375 +240,3 @@ impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for ScrubbedTraitError<' } } } - -fn fulfillment_error_for_no_solution<'tcx>( - infcx: &InferCtxt<'tcx>, - root_obligation: PredicateObligation<'tcx>, -) -> FulfillmentError<'tcx> { - let obligation = find_best_leaf_obligation(infcx, &root_obligation, false); - - let code = match obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => { - FulfillmentErrorCode::Project( - // FIXME: This could be a `Sorts` if the term is a type - MismatchedProjectionTypes { err: TypeError::Mismatch }, - ) - } - ty::PredicateKind::NormalizesTo(..) => { - FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch }) - } - ty::PredicateKind::AliasRelate(_, _, _) => { - FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch }) - } - ty::PredicateKind::Subtype(pred) => { - let (a, b) = infcx.enter_forall_and_leak_universe( - obligation.predicate.kind().rebind((pred.a, pred.b)), - ); - let expected_found = ExpectedFound::new(a, b); - FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found)) - } - ty::PredicateKind::Coerce(pred) => { - let (a, b) = infcx.enter_forall_and_leak_universe( - obligation.predicate.kind().rebind((pred.a, pred.b)), - ); - let expected_found = ExpectedFound::new(b, a); - FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found)) - } - ty::PredicateKind::Clause(_) - | ty::PredicateKind::DynCompatible(_) - | ty::PredicateKind::Ambiguous => { - FulfillmentErrorCode::Select(SelectionError::Unimplemented) - } - ty::PredicateKind::ConstEquate(..) => { - bug!("unexpected goal: {obligation:?}") - } - }; - - FulfillmentError { obligation, code, root_obligation } -} - -fn fulfillment_error_for_stalled<'tcx>( - infcx: &InferCtxt<'tcx>, - root_obligation: PredicateObligation<'tcx>, -) -> FulfillmentError<'tcx> { - let (code, refine_obligation) = infcx.probe(|_| { - match <&SolverDelegate<'tcx>>::from(infcx) - .evaluate_root_goal(root_obligation.clone().into(), GenerateProofTree::No) - .0 - { - Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => { - (FulfillmentErrorCode::Ambiguity { overflow: None }, true) - } - Ok((_, Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit }))) => ( - FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) }, - // Don't look into overflows because we treat overflows weirdly anyways. - // We discard the inference constraints from overflowing goals, so - // recomputing the goal again during `find_best_leaf_obligation` may apply - // inference guidance that makes other goals go from ambig -> pass, for example. - // - // FIXME: We should probably just look into overflows here. - false, - ), - Ok((_, Certainty::Yes)) => { - bug!("did not expect successful goal when collecting ambiguity errors") - } - Err(_) => { - bug!("did not expect selection error when collecting ambiguity errors") - } - } - }); - - FulfillmentError { - obligation: if refine_obligation { - find_best_leaf_obligation(infcx, &root_obligation, true) - } else { - root_obligation.clone() - }, - code, - root_obligation, - } -} - -fn fulfillment_error_for_overflow<'tcx>( - infcx: &InferCtxt<'tcx>, - root_obligation: PredicateObligation<'tcx>, -) -> FulfillmentError<'tcx> { - FulfillmentError { - obligation: find_best_leaf_obligation(infcx, &root_obligation, true), - code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) }, - root_obligation, - } -} - -fn find_best_leaf_obligation<'tcx>( - infcx: &InferCtxt<'tcx>, - obligation: &PredicateObligation<'tcx>, - consider_ambiguities: bool, -) -> PredicateObligation<'tcx> { - let obligation = infcx.resolve_vars_if_possible(obligation.clone()); - // FIXME: we use a probe here as the `BestObligation` visitor does not - // check whether it uses candidates which get shadowed by where-bounds. - // - // We should probably fix the visitor to not do so instead, as this also - // means the leaf obligation may be incorrect. - infcx - .fudge_inference_if_ok(|| { - infcx - .visit_proof_tree(obligation.clone().into(), &mut BestObligation { - obligation: obligation.clone(), - consider_ambiguities, - }) - .break_value() - .ok_or(()) - }) - .unwrap_or(obligation) -} - -struct BestObligation<'tcx> { - obligation: PredicateObligation<'tcx>, - consider_ambiguities: bool, -} - -impl<'tcx> BestObligation<'tcx> { - fn with_derived_obligation( - &mut self, - derived_obligation: PredicateObligation<'tcx>, - and_then: impl FnOnce(&mut Self) -> >::Result, - ) -> >::Result { - let old_obligation = std::mem::replace(&mut self.obligation, derived_obligation); - let res = and_then(self); - self.obligation = old_obligation; - res - } - - /// Filter out the candidates that aren't interesting to visit for the - /// purposes of reporting errors. For ambiguities, we only consider - /// candidates that may hold. For errors, we only consider candidates that - /// *don't* hold and which have impl-where clauses that also don't hold. - fn non_trivial_candidates<'a>( - &self, - goal: &'a inspect::InspectGoal<'a, 'tcx>, - ) -> Vec> { - let mut candidates = goal.candidates(); - match self.consider_ambiguities { - true => { - // If we have an ambiguous obligation, we must consider *all* candidates - // that hold, or else we may guide inference causing other goals to go - // from ambig -> pass/fail. - candidates.retain(|candidate| candidate.result().is_ok()); - } - false => { - // If we have >1 candidate, one may still be due to "boring" reasons, like - // an alias-relate that failed to hold when deeply evaluated. We really - // don't care about reasons like this. - if candidates.len() > 1 { - candidates.retain(|candidate| { - goal.infcx().probe(|_| { - candidate.instantiate_nested_goals(self.span()).iter().any( - |nested_goal| { - matches!( - nested_goal.source(), - GoalSource::ImplWhereBound - | GoalSource::InstantiateHigherRanked - | GoalSource::AliasWellFormed - ) && match self.consider_ambiguities { - true => { - matches!( - nested_goal.result(), - Ok(Certainty::Maybe(MaybeCause::Ambiguity)) - ) - } - false => matches!(nested_goal.result(), Err(_)), - } - }, - ) - }) - }); - } - - // Prefer a non-rigid candidate if there is one. - if candidates.len() > 1 { - candidates.retain(|candidate| { - !matches!(candidate.kind(), inspect::ProbeKind::RigidAlias { .. }) - }); - } - } - } - - candidates - } -} - -impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { - type Result = ControlFlow>; - - fn span(&self) -> rustc_span::Span { - self.obligation.cause.span - } - - #[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))] - fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result { - let candidates = self.non_trivial_candidates(goal); - trace!(candidates = ?candidates.iter().map(|c| c.kind()).collect::>()); - - let [candidate] = candidates.as_slice() else { - return ControlFlow::Break(self.obligation.clone()); - }; - - // Don't walk into impls that have `do_not_recommend`. - if let inspect::ProbeKind::TraitCandidate { - source: CandidateSource::Impl(impl_def_id), - result: _, - } = candidate.kind() - && goal.infcx().tcx.do_not_recommend_impl(impl_def_id) - { - return ControlFlow::Break(self.obligation.clone()); - } - - let tcx = goal.infcx().tcx; - // FIXME: Also, what about considering >1 layer up the stack? May be necessary - // for normalizes-to. - let pred_kind = goal.goal().predicate.kind(); - let child_mode = match pred_kind.skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Trait(parent_trait_pred)) => { - ChildMode::Trait(pred_kind.rebind(parent_trait_pred)) - } - ty::PredicateKind::NormalizesTo(normalizes_to) - if matches!( - normalizes_to.alias.kind(tcx), - ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst - ) => - { - ChildMode::Trait(pred_kind.rebind(ty::TraitPredicate { - trait_ref: normalizes_to.alias.trait_ref(tcx), - polarity: ty::PredicatePolarity::Positive, - })) - } - _ => ChildMode::PassThrough, - }; - - let mut impl_where_bound_count = 0; - for nested_goal in candidate.instantiate_nested_goals(self.span()) { - trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result())); - - let make_obligation = |cause| Obligation { - cause, - param_env: nested_goal.goal().param_env, - predicate: nested_goal.goal().predicate, - recursion_depth: self.obligation.recursion_depth + 1, - }; - - let obligation; - match (child_mode, nested_goal.source()) { - (ChildMode::Trait(_), GoalSource::Misc) => { - continue; - } - (ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => { - obligation = make_obligation(derive_cause( - tcx, - candidate.kind(), - self.obligation.cause.clone(), - impl_where_bound_count, - parent_trait_pred, - )); - impl_where_bound_count += 1; - } - // Skip over a higher-ranked predicate. - (_, GoalSource::InstantiateHigherRanked) => { - obligation = self.obligation.clone(); - } - (ChildMode::PassThrough, _) | (_, GoalSource::AliasWellFormed) => { - obligation = make_obligation(self.obligation.cause.clone()); - } - } - - // Skip nested goals that aren't the *reason* for our goal's failure. - match self.consider_ambiguities { - true if matches!( - nested_goal.result(), - Ok(Certainty::Maybe(MaybeCause::Ambiguity)) - ) => {} - false if matches!(nested_goal.result(), Err(_)) => {} - _ => continue, - } - - self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?; - } - - // alias-relate may fail because the lhs or rhs can't be normalized, - // and therefore is treated as rigid. - if let Some(ty::PredicateKind::AliasRelate(lhs, rhs, _)) = pred_kind.no_bound_vars() { - if let Some(obligation) = goal - .infcx() - .visit_proof_tree_at_depth( - goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(lhs.into())), - goal.depth() + 1, - self, - ) - .break_value() - { - return ControlFlow::Break(obligation); - } else if let Some(obligation) = goal - .infcx() - .visit_proof_tree_at_depth( - goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(rhs.into())), - goal.depth() + 1, - self, - ) - .break_value() - { - return ControlFlow::Break(obligation); - } - } - - ControlFlow::Break(self.obligation.clone()) - } -} - -#[derive(Copy, Clone)] -enum ChildMode<'tcx> { - // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`, - // and skip all `GoalSource::Misc`, which represent useless obligations - // such as alias-eq which may not hold. - Trait(ty::PolyTraitPredicate<'tcx>), - // Skip trying to derive an `ObligationCause` from this obligation, and - // report *all* sub-obligations as if they came directly from the parent - // obligation. - PassThrough, -} - -fn derive_cause<'tcx>( - tcx: TyCtxt<'tcx>, - candidate_kind: inspect::ProbeKind>, - mut cause: ObligationCause<'tcx>, - idx: usize, - parent_trait_pred: ty::PolyTraitPredicate<'tcx>, -) -> ObligationCause<'tcx> { - match candidate_kind { - inspect::ProbeKind::TraitCandidate { - source: CandidateSource::Impl(impl_def_id), - result: _, - } => { - if let Some((_, span)) = - tcx.predicates_of(impl_def_id).instantiate_identity(tcx).iter().nth(idx) - { - cause = cause.derived_cause(parent_trait_pred, |derived| { - ObligationCauseCode::ImplDerived(Box::new(traits::ImplDerivedCause { - derived, - impl_or_alias_def_id: impl_def_id, - impl_def_predicate_index: Some(idx), - span, - })) - }) - } - } - inspect::ProbeKind::TraitCandidate { - source: CandidateSource::BuiltinImpl(..), - result: _, - } => { - cause = cause.derived_cause(parent_trait_pred, ObligationCauseCode::BuiltinDerived); - } - _ => {} - }; - cause -} diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs new file mode 100644 index 0000000000000..982782bc57c18 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -0,0 +1,609 @@ +use std::ops::ControlFlow; + +use rustc_infer::infer::InferCtxt; +use rustc_infer::traits::solve::{CandidateSource, GoalSource, MaybeCause}; +use rustc_infer::traits::{ + self, MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode, + PredicateObligation, SelectionError, +}; +use rustc_middle::ty::error::{ExpectedFound, TypeError}; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::{bug, span_bug}; +use rustc_next_trait_solver::solve::{GenerateProofTree, SolverDelegateEvalExt as _}; +use rustc_type_ir::solve::{Goal, NoSolution}; +use tracing::{instrument, trace}; + +use crate::solve::Certainty; +use crate::solve::delegate::SolverDelegate; +use crate::solve::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor}; +use crate::traits::{FulfillmentError, FulfillmentErrorCode, wf}; + +pub(super) fn fulfillment_error_for_no_solution<'tcx>( + infcx: &InferCtxt<'tcx>, + root_obligation: PredicateObligation<'tcx>, +) -> FulfillmentError<'tcx> { + let obligation = find_best_leaf_obligation(infcx, &root_obligation, false); + + let code = match obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => { + FulfillmentErrorCode::Project( + // FIXME: This could be a `Sorts` if the term is a type + MismatchedProjectionTypes { err: TypeError::Mismatch }, + ) + } + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, expected_ty)) => { + let ct_ty = match ct.kind() { + ty::ConstKind::Unevaluated(uv) => { + infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args) + } + ty::ConstKind::Param(param_ct) => param_ct.find_ty_from_env(obligation.param_env), + ty::ConstKind::Value(cv) => cv.ty, + kind => span_bug!( + obligation.cause.span, + "ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}" + ), + }; + FulfillmentErrorCode::Select(SelectionError::ConstArgHasWrongType { + ct, + ct_ty, + expected_ty, + }) + } + ty::PredicateKind::NormalizesTo(..) => { + FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch }) + } + ty::PredicateKind::AliasRelate(_, _, _) => { + FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch }) + } + ty::PredicateKind::Subtype(pred) => { + let (a, b) = infcx.enter_forall_and_leak_universe( + obligation.predicate.kind().rebind((pred.a, pred.b)), + ); + let expected_found = ExpectedFound::new(a, b); + FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found)) + } + ty::PredicateKind::Coerce(pred) => { + let (a, b) = infcx.enter_forall_and_leak_universe( + obligation.predicate.kind().rebind((pred.a, pred.b)), + ); + let expected_found = ExpectedFound::new(b, a); + FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found)) + } + ty::PredicateKind::Clause(_) + | ty::PredicateKind::DynCompatible(_) + | ty::PredicateKind::Ambiguous => { + FulfillmentErrorCode::Select(SelectionError::Unimplemented) + } + ty::PredicateKind::ConstEquate(..) => { + bug!("unexpected goal: {obligation:?}") + } + }; + + FulfillmentError { obligation, code, root_obligation } +} + +pub(super) fn fulfillment_error_for_stalled<'tcx>( + infcx: &InferCtxt<'tcx>, + root_obligation: PredicateObligation<'tcx>, +) -> FulfillmentError<'tcx> { + let (code, refine_obligation) = infcx.probe(|_| { + match <&SolverDelegate<'tcx>>::from(infcx) + .evaluate_root_goal( + root_obligation.clone().into(), + GenerateProofTree::No, + root_obligation.cause.span, + ) + .0 + { + Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => { + (FulfillmentErrorCode::Ambiguity { overflow: None }, true) + } + Ok((_, Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit }))) => ( + FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) }, + // Don't look into overflows because we treat overflows weirdly anyways. + // We discard the inference constraints from overflowing goals, so + // recomputing the goal again during `find_best_leaf_obligation` may apply + // inference guidance that makes other goals go from ambig -> pass, for example. + // + // FIXME: We should probably just look into overflows here. + false, + ), + Ok((_, Certainty::Yes)) => { + bug!("did not expect successful goal when collecting ambiguity errors") + } + Err(_) => { + bug!("did not expect selection error when collecting ambiguity errors") + } + } + }); + + FulfillmentError { + obligation: if refine_obligation { + find_best_leaf_obligation(infcx, &root_obligation, true) + } else { + root_obligation.clone() + }, + code, + root_obligation, + } +} + +pub(super) fn fulfillment_error_for_overflow<'tcx>( + infcx: &InferCtxt<'tcx>, + root_obligation: PredicateObligation<'tcx>, +) -> FulfillmentError<'tcx> { + FulfillmentError { + obligation: find_best_leaf_obligation(infcx, &root_obligation, true), + code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) }, + root_obligation, + } +} + +#[instrument(level = "debug", skip(infcx), ret)] +fn find_best_leaf_obligation<'tcx>( + infcx: &InferCtxt<'tcx>, + obligation: &PredicateObligation<'tcx>, + consider_ambiguities: bool, +) -> PredicateObligation<'tcx> { + let obligation = infcx.resolve_vars_if_possible(obligation.clone()); + // FIXME: we use a probe here as the `BestObligation` visitor does not + // check whether it uses candidates which get shadowed by where-bounds. + // + // We should probably fix the visitor to not do so instead, as this also + // means the leaf obligation may be incorrect. + infcx + .fudge_inference_if_ok(|| { + infcx + .visit_proof_tree( + obligation.clone().into(), + &mut BestObligation { obligation: obligation.clone(), consider_ambiguities }, + ) + .break_value() + .ok_or(()) + }) + .unwrap_or(obligation) +} + +struct BestObligation<'tcx> { + obligation: PredicateObligation<'tcx>, + consider_ambiguities: bool, +} + +impl<'tcx> BestObligation<'tcx> { + fn with_derived_obligation( + &mut self, + derived_obligation: PredicateObligation<'tcx>, + and_then: impl FnOnce(&mut Self) -> >::Result, + ) -> >::Result { + let old_obligation = std::mem::replace(&mut self.obligation, derived_obligation); + let res = and_then(self); + self.obligation = old_obligation; + res + } + + /// Filter out the candidates that aren't interesting to visit for the + /// purposes of reporting errors. For ambiguities, we only consider + /// candidates that may hold. For errors, we only consider candidates that + /// *don't* hold and which have impl-where clauses that also don't hold. + fn non_trivial_candidates<'a>( + &self, + goal: &'a inspect::InspectGoal<'a, 'tcx>, + ) -> Vec> { + let mut candidates = goal.candidates(); + match self.consider_ambiguities { + true => { + // If we have an ambiguous obligation, we must consider *all* candidates + // that hold, or else we may guide inference causing other goals to go + // from ambig -> pass/fail. + candidates.retain(|candidate| candidate.result().is_ok()); + } + false => { + // We always handle rigid alias candidates separately as we may not add them for + // aliases whose trait bound doesn't hold. + candidates.retain(|c| !matches!(c.kind(), inspect::ProbeKind::RigidAlias { .. })); + // If we have >1 candidate, one may still be due to "boring" reasons, like + // an alias-relate that failed to hold when deeply evaluated. We really + // don't care about reasons like this. + if candidates.len() > 1 { + candidates.retain(|candidate| { + goal.infcx().probe(|_| { + candidate.instantiate_nested_goals(self.span()).iter().any( + |nested_goal| { + matches!( + nested_goal.source(), + GoalSource::ImplWhereBound + | GoalSource::AliasBoundConstCondition + | GoalSource::InstantiateHigherRanked + | GoalSource::AliasWellFormed + ) && nested_goal.result().is_err() + }, + ) + }) + }); + } + } + } + + candidates + } + + /// HACK: We walk the nested obligations for a well-formed arg manually, + /// since there's nontrivial logic in `wf.rs` to set up an obligation cause. + /// Ideally we'd be able to track this better. + fn visit_well_formed_goal( + &mut self, + candidate: &inspect::InspectCandidate<'_, 'tcx>, + arg: ty::GenericArg<'tcx>, + ) -> ControlFlow> { + let infcx = candidate.goal().infcx(); + let param_env = candidate.goal().goal().param_env; + let body_id = self.obligation.cause.body_id; + + for obligation in wf::unnormalized_obligations(infcx, param_env, arg, self.span(), body_id) + .into_iter() + .flatten() + { + let nested_goal = candidate.instantiate_proof_tree_for_nested_goal( + GoalSource::Misc, + Goal::new(infcx.tcx, obligation.param_env, obligation.predicate), + self.span(), + ); + // Skip nested goals that aren't the *reason* for our goal's failure. + match (self.consider_ambiguities, nested_goal.result()) { + (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {} + _ => continue, + } + + self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?; + } + + ControlFlow::Break(self.obligation.clone()) + } + + /// If a normalization of an associated item or a trait goal fails without trying any + /// candidates it's likely that normalizing its self type failed. We manually detect + /// such cases here. + fn detect_error_in_self_ty_normalization( + &mut self, + goal: &inspect::InspectGoal<'_, 'tcx>, + self_ty: Ty<'tcx>, + ) -> ControlFlow> { + assert!(!self.consider_ambiguities); + let tcx = goal.infcx().tcx; + if let ty::Alias(..) = self_ty.kind() { + let infer_term = goal.infcx().next_ty_var(self.obligation.cause.span); + let pred = ty::PredicateKind::AliasRelate( + self_ty.into(), + infer_term.into(), + ty::AliasRelationDirection::Equate, + ); + let obligation = + Obligation::new(tcx, self.obligation.cause.clone(), goal.goal().param_env, pred); + self.with_derived_obligation(obligation, |this| { + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(tcx, pred), + goal.depth() + 1, + this, + ) + }) + } else { + ControlFlow::Continue(()) + } + } + + /// It is likely that `NormalizesTo` failed without any applicable candidates + /// because the alias is not well-formed. + /// + /// As we only enter `RigidAlias` candidates if the trait bound of the associated type + /// holds, we discard these candidates in `non_trivial_candidates` and always manually + /// check this here. + fn detect_non_well_formed_assoc_item( + &mut self, + goal: &inspect::InspectGoal<'_, 'tcx>, + alias: ty::AliasTerm<'tcx>, + ) -> ControlFlow> { + let tcx = goal.infcx().tcx; + let obligation = Obligation::new( + tcx, + self.obligation.cause.clone(), + goal.goal().param_env, + alias.trait_ref(tcx), + ); + self.with_derived_obligation(obligation, |this| { + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(tcx, alias.trait_ref(tcx)), + goal.depth() + 1, + this, + ) + }) + } + + /// If we have no candidates, then it's likely that there is a + /// non-well-formed alias in the goal. + fn detect_error_from_empty_candidates( + &mut self, + goal: &inspect::InspectGoal<'_, 'tcx>, + ) -> ControlFlow> { + let tcx = goal.infcx().tcx; + let pred_kind = goal.goal().predicate.kind(); + + match pred_kind.no_bound_vars() { + Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred))) => { + self.detect_error_in_self_ty_normalization(goal, pred.self_ty())?; + } + Some(ty::PredicateKind::NormalizesTo(pred)) + if let ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst = + pred.alias.kind(tcx) => + { + self.detect_error_in_self_ty_normalization(goal, pred.alias.self_ty())?; + self.detect_non_well_formed_assoc_item(goal, pred.alias)?; + } + Some(_) | None => {} + } + + ControlFlow::Break(self.obligation.clone()) + } +} + +impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { + type Result = ControlFlow>; + + fn span(&self) -> rustc_span::Span { + self.obligation.cause.span + } + + #[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))] + fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result { + let tcx = goal.infcx().tcx; + // Skip goals that aren't the *reason* for our goal's failure. + match (self.consider_ambiguities, goal.result()) { + (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {} + _ => return ControlFlow::Continue(()), + } + let pred_kind = goal.goal().predicate.kind(); + + let candidates = self.non_trivial_candidates(goal); + let candidate = match candidates.as_slice() { + [candidate] => candidate, + [] => return self.detect_error_from_empty_candidates(goal), + _ => return ControlFlow::Break(self.obligation.clone()), + }; + + // Don't walk into impls that have `do_not_recommend`. + if let inspect::ProbeKind::TraitCandidate { + source: CandidateSource::Impl(impl_def_id), + result: _, + } = candidate.kind() + && goal.infcx().tcx.do_not_recommend_impl(impl_def_id) + { + trace!("#[do_not_recommend] -> exit"); + return ControlFlow::Break(self.obligation.clone()); + } + + // FIXME: Also, what about considering >1 layer up the stack? May be necessary + // for normalizes-to. + let child_mode = match pred_kind.skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => { + ChildMode::Trait(pred_kind.rebind(pred)) + } + ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(pred)) => { + ChildMode::Host(pred_kind.rebind(pred)) + } + ty::PredicateKind::NormalizesTo(normalizes_to) + if matches!( + normalizes_to.alias.kind(tcx), + ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst + ) => + { + ChildMode::Trait(pred_kind.rebind(ty::TraitPredicate { + trait_ref: normalizes_to.alias.trait_ref(tcx), + polarity: ty::PredicatePolarity::Positive, + })) + } + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { + return self.visit_well_formed_goal(candidate, arg); + } + _ => ChildMode::PassThrough, + }; + + let nested_goals = candidate.instantiate_nested_goals(self.span()); + + // If the candidate requires some `T: FnPtr` bound which does not hold should not be treated as + // an actual candidate, instead we should treat them as if the impl was never considered to + // have potentially applied. As if `impl Trait for for<..> fn(..A) -> R` was written + // instead of `impl Trait for T`. + // + // We do this as a separate loop so that we do not choose to tell the user about some nested + // goal before we encounter a `T: FnPtr` nested goal. + for nested_goal in &nested_goals { + if let Some(fn_ptr_trait) = tcx.lang_items().fn_ptr_trait() + && let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause() + && poly_trait_pred.def_id() == fn_ptr_trait + && let Err(NoSolution) = nested_goal.result() + { + return ControlFlow::Break(self.obligation.clone()); + } + } + + let mut impl_where_bound_count = 0; + for nested_goal in nested_goals { + trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result())); + + let make_obligation = |cause| Obligation { + cause, + param_env: nested_goal.goal().param_env, + predicate: nested_goal.goal().predicate, + recursion_depth: self.obligation.recursion_depth + 1, + }; + + let obligation; + match (child_mode, nested_goal.source()) { + (ChildMode::Trait(_) | ChildMode::Host(_), GoalSource::Misc) => { + continue; + } + (ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => { + obligation = make_obligation(derive_cause( + tcx, + candidate.kind(), + self.obligation.cause.clone(), + impl_where_bound_count, + parent_trait_pred, + )); + impl_where_bound_count += 1; + } + ( + ChildMode::Host(parent_host_pred), + GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition, + ) => { + obligation = make_obligation(derive_host_cause( + tcx, + candidate.kind(), + self.obligation.cause.clone(), + impl_where_bound_count, + parent_host_pred, + )); + impl_where_bound_count += 1; + } + // Skip over a higher-ranked predicate. + (_, GoalSource::InstantiateHigherRanked) => { + obligation = self.obligation.clone(); + } + (ChildMode::PassThrough, _) + | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => { + obligation = make_obligation(self.obligation.cause.clone()); + } + } + + self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?; + } + + // alias-relate may fail because the lhs or rhs can't be normalized, + // and therefore is treated as rigid. + if let Some(ty::PredicateKind::AliasRelate(lhs, rhs, _)) = pred_kind.no_bound_vars() { + if let Some(obligation) = goal + .infcx() + .visit_proof_tree_at_depth( + goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(lhs.into())), + goal.depth() + 1, + self, + ) + .break_value() + { + return ControlFlow::Break(obligation); + } else if let Some(obligation) = goal + .infcx() + .visit_proof_tree_at_depth( + goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(rhs.into())), + goal.depth() + 1, + self, + ) + .break_value() + { + return ControlFlow::Break(obligation); + } + } + + ControlFlow::Break(self.obligation.clone()) + } +} + +#[derive(Debug, Copy, Clone)] +enum ChildMode<'tcx> { + // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`, + // and skip all `GoalSource::Misc`, which represent useless obligations + // such as alias-eq which may not hold. + Trait(ty::PolyTraitPredicate<'tcx>), + // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`, + // and skip all `GoalSource::Misc`, which represent useless obligations + // such as alias-eq which may not hold. + Host(ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>), + // Skip trying to derive an `ObligationCause` from this obligation, and + // report *all* sub-obligations as if they came directly from the parent + // obligation. + PassThrough, +} + +fn derive_cause<'tcx>( + tcx: TyCtxt<'tcx>, + candidate_kind: inspect::ProbeKind>, + mut cause: ObligationCause<'tcx>, + idx: usize, + parent_trait_pred: ty::PolyTraitPredicate<'tcx>, +) -> ObligationCause<'tcx> { + match candidate_kind { + inspect::ProbeKind::TraitCandidate { + source: CandidateSource::Impl(impl_def_id), + result: _, + } => { + if let Some((_, span)) = + tcx.predicates_of(impl_def_id).instantiate_identity(tcx).iter().nth(idx) + { + cause = cause.derived_cause(parent_trait_pred, |derived| { + ObligationCauseCode::ImplDerived(Box::new(traits::ImplDerivedCause { + derived, + impl_or_alias_def_id: impl_def_id, + impl_def_predicate_index: Some(idx), + span, + })) + }) + } + } + inspect::ProbeKind::TraitCandidate { + source: CandidateSource::BuiltinImpl(..), + result: _, + } => { + cause = cause.derived_cause(parent_trait_pred, ObligationCauseCode::BuiltinDerived); + } + _ => {} + }; + cause +} + +fn derive_host_cause<'tcx>( + tcx: TyCtxt<'tcx>, + candidate_kind: inspect::ProbeKind>, + mut cause: ObligationCause<'tcx>, + idx: usize, + parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>, +) -> ObligationCause<'tcx> { + match candidate_kind { + inspect::ProbeKind::TraitCandidate { + source: CandidateSource::Impl(impl_def_id), + result: _, + } => { + if let Some((_, span)) = tcx + .predicates_of(impl_def_id) + .instantiate_identity(tcx) + .into_iter() + .chain(tcx.const_conditions(impl_def_id).instantiate_identity(tcx).into_iter().map( + |(trait_ref, span)| { + ( + trait_ref.to_host_effect_clause( + tcx, + parent_host_pred.skip_binder().constness, + ), + span, + ) + }, + )) + .nth(idx) + { + cause = + cause.derived_host_cause(parent_host_pred, |derived| { + ObligationCauseCode::ImplDerivedHost(Box::new( + traits::ImplDerivedHostCause { derived, impl_def_id, span }, + )) + }) + } + } + inspect::ProbeKind::TraitCandidate { + source: CandidateSource::BuiltinImpl(..), + result: _, + } => { + cause = + cause.derived_host_cause(parent_host_pred, ObligationCauseCode::BuiltinDerivedHost); + } + _ => {} + }; + cause +} diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index e735020a63e66..9fbc1d64d7429 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -194,47 +194,57 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { let goals = instantiated_goals .into_iter() - .map(|(source, goal)| match goal.predicate.kind().no_bound_vars() { - Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => { - let unconstrained_term = match term.unpack() { - ty::TermKind::Ty(_) => infcx.next_ty_var(span).into(), - ty::TermKind::Const(_) => infcx.next_const_var(span).into(), - }; - let goal = - goal.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term }); - // We have to use a `probe` here as evaluating a `NormalizesTo` can constrain the - // expected term. This means that candidates which only fail due to nested goals - // and which normalize to a different term then the final result could ICE: when - // building their proof tree, the expected term was unconstrained, but when - // instantiating the candidate it is already constrained to the result of another - // candidate. - let proof_tree = infcx - .probe(|_| infcx.evaluate_root_goal_raw(goal, GenerateProofTree::Yes).1); - InspectGoal::new( - infcx, - self.goal.depth + 1, - proof_tree.unwrap(), - Some(NormalizesToTermHack { term, unconstrained_term }), - source, - ) - } - _ => { - // We're using a probe here as evaluating a goal could constrain - // inference variables by choosing one candidate. If we then recurse - // into another candidate who ends up with different inference - // constraints, we get an ICE if we already applied the constraints - // from the chosen candidate. - let proof_tree = infcx - .probe(|_| infcx.evaluate_root_goal(goal, GenerateProofTree::Yes).1) - .unwrap(); - InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source) - } - }) + .map(|(source, goal)| self.instantiate_proof_tree_for_nested_goal(source, goal, span)) .collect(); (goals, opt_impl_args) } + pub fn instantiate_proof_tree_for_nested_goal( + &self, + source: GoalSource, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + span: Span, + ) -> InspectGoal<'a, 'tcx> { + let infcx = self.goal.infcx; + match goal.predicate.kind().no_bound_vars() { + Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => { + let unconstrained_term = match term.unpack() { + ty::TermKind::Ty(_) => infcx.next_ty_var(span).into(), + ty::TermKind::Const(_) => infcx.next_const_var(span).into(), + }; + let goal = + goal.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term }); + // We have to use a `probe` here as evaluating a `NormalizesTo` can constrain the + // expected term. This means that candidates which only fail due to nested goals + // and which normalize to a different term then the final result could ICE: when + // building their proof tree, the expected term was unconstrained, but when + // instantiating the candidate it is already constrained to the result of another + // candidate. + let proof_tree = + infcx.probe(|_| infcx.evaluate_root_goal_raw(goal, GenerateProofTree::Yes).1); + InspectGoal::new( + infcx, + self.goal.depth + 1, + proof_tree.unwrap(), + Some(NormalizesToTermHack { term, unconstrained_term }), + source, + ) + } + _ => { + // We're using a probe here as evaluating a goal could constrain + // inference variables by choosing one candidate. If we then recurse + // into another candidate who ends up with different inference + // constraints, we get an ICE if we already applied the constraints + // from the chosen candidate. + let proof_tree = infcx + .probe(|_| infcx.evaluate_root_goal(goal, GenerateProofTree::Yes, span).1) + .unwrap(); + InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source) + } + } + } + /// Visit all nested goals of this candidate, rolling back /// all inference constraints. pub fn visit_nested_in_probe>(&self, visitor: &mut V) -> V::Result { @@ -290,7 +300,6 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { inspect::ProbeKind::NormalizedSelfTyAssembly | inspect::ProbeKind::UnsizeAssembly | inspect::ProbeKind::Root { .. } - | inspect::ProbeKind::TryNormalizeNonRigid { .. } | inspect::ProbeKind::TraitCandidate { .. } | inspect::ProbeKind::OpaqueTypeStorageLookup { .. } | inspect::ProbeKind::RigidAlias { .. } => { @@ -315,7 +324,6 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { // We add a candidate even for the root evaluation if there // is only one way to prove a given goal, e.g. for `WellFormed`. inspect::ProbeKind::Root { result } - | inspect::ProbeKind::TryNormalizeNonRigid { result } | inspect::ProbeKind::TraitCandidate { source: _, result } | inspect::ProbeKind::OpaqueTypeStorageLookup { result } | inspect::ProbeKind::RigidAlias { result } => { @@ -430,8 +438,11 @@ impl<'tcx> InferCtxt<'tcx> { depth: usize, visitor: &mut V, ) -> V::Result { - let (_, proof_tree) = - <&SolverDelegate<'tcx>>::from(self).evaluate_root_goal(goal, GenerateProofTree::Yes); + let (_, proof_tree) = <&SolverDelegate<'tcx>>::from(self).evaluate_root_goal( + goal, + GenerateProofTree::Yes, + visitor.span(), + ); let proof_tree = proof_tree.unwrap(); visitor.visit_goal(&InspectGoal::new(self, depth, proof_tree, None, GoalSource::Misc)) } diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index a724705ffe912..232357dc71a0d 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -130,11 +130,12 @@ where self.depth += 1; let new_infer_ct = infcx.next_const_var(self.at.cause.span); - let obligation = - Obligation::new(tcx, self.at.cause.clone(), self.at.param_env, ty::NormalizesTo { - alias: uv.into(), - term: new_infer_ct.into(), - }); + let obligation = Obligation::new( + tcx, + self.at.cause.clone(), + self.at.param_env, + ty::NormalizesTo { alias: uv.into(), term: new_infer_ct.into() }, + ); let result = if infcx.predicate_may_hold(&obligation) { self.fulfill_cx.register_predicate_obligation(infcx, obligation); @@ -252,20 +253,20 @@ impl<'tcx> TypeFolder> for DeeplyNormalizeForDiagnosticsFolder<'_, } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - deeply_normalize_with_skipped_universes(self.at, ty, vec![ - None; - ty.outer_exclusive_binder() - .as_usize() - ]) + deeply_normalize_with_skipped_universes( + self.at, + ty, + vec![None; ty.outer_exclusive_binder().as_usize()], + ) .unwrap_or_else(|_: Vec>| ty.super_fold_with(self)) } fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - deeply_normalize_with_skipped_universes(self.at, ct, vec![ - None; - ct.outer_exclusive_binder() - .as_usize() - ]) + deeply_normalize_with_skipped_universes( + self.at, + ct, + vec![None; ct.outer_exclusive_binder().as_usize()], + ) .unwrap_or_else(|_: Vec>| ct.super_fold_with(self)) } } diff --git a/compiler/rustc_trait_selection/src/solve/select.rs b/compiler/rustc_trait_selection/src/solve/select.rs index 1661852903c48..4437fc5b0295f 100644 --- a/compiler/rustc_trait_selection/src/solve/select.rs +++ b/compiler/rustc_trait_selection/src/solve/select.rs @@ -117,6 +117,10 @@ fn candidate_should_be_dropped_in_favor_of<'tcx>( CandidateSource::BuiltinImpl(BuiltinImplSource::Object(a)), CandidateSource::BuiltinImpl(BuiltinImplSource::Object(b)), ) => a >= b, + ( + CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting(a)), + CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting(b)), + ) => a >= b, // Prefer dyn candidates over non-dyn candidates. This is necessary to // handle the unsoundness between `impl Any for T` and `dyn Any: Any`. ( @@ -171,8 +175,7 @@ fn to_selection<'tcx>( span_bug!(span, "didn't expect to select an unknowable candidate") } }, - ProbeKind::TryNormalizeNonRigid { result: _ } - | ProbeKind::NormalizedSelfTyAssembly + ProbeKind::NormalizedSelfTyAssembly | ProbeKind::UnsizeAssembly | ProbeKind::UpcastProjectionCompatibility | ProbeKind::OpaqueTypeStorageLookup { result: _ } diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 9a53e8a5d5190..1fca2f4da7eee 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -6,6 +6,7 @@ use std::iter; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry}; use rustc_data_structures::unord::UnordSet; +use rustc_hir::def_id::CRATE_DEF_ID; use rustc_infer::infer::DefineOpaqueTypes; use rustc_middle::ty::{Region, RegionVid}; use tracing::debug; @@ -13,6 +14,7 @@ use tracing::debug; use super::*; use crate::errors::UnableToConstructConstantValue; use crate::infer::region_constraints::{Constraint, RegionConstraintData}; +use crate::regions::OutlivesEnvironmentBuildExt; use crate::traits::project::ProjectAndUnifyResult; // FIXME(twk): this is obviously not nice to duplicate like that @@ -158,7 +160,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { panic!("Unable to fulfill trait {trait_did:?} for '{ty:?}': {errors:?}"); } - let outlives_env = OutlivesEnvironment::new(full_env); + let outlives_env = OutlivesEnvironment::new(&infcx, CRATE_DEF_ID, full_env, []); let _ = infcx.process_registered_region_obligations(&outlives_env, |ty, _| Ok(ty)); let region_data = infcx.inner.borrow_mut().unwrap_region_constraints().data().clone(); diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 971d3a8110254..3b40882ef572d 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -6,10 +6,10 @@ use std::fmt::Debug; -use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::{Diag, EmissionGuarantee}; use rustc_hir::def::DefKind; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::PredicateObligations; use rustc_middle::bug; @@ -27,7 +27,6 @@ use tracing::{debug, instrument, warn}; use super::ObligationCtxt; use crate::error_reporting::traits::suggest_new_overflow_limit; use crate::infer::InferOk; -use crate::infer::outlives::env::OutlivesEnvironment; use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor}; use crate::solve::{SolverDelegate, deeply_normalize_for_diagnostics, inspect}; use crate::traits::query::evaluate_obligation::InferCtxtExt; @@ -116,28 +115,39 @@ pub fn overlapping_impls( return None; } - let _overlap_with_bad_diagnostics = overlap( - tcx, - TrackAmbiguityCauses::No, - skip_leak_check, - impl1_def_id, - impl2_def_id, - overlap_mode, - )?; - - // In the case where we detect an error, run the check again, but - // this time tracking intercrate ambiguity causes for better - // diagnostics. (These take time and can lead to false errors.) - let overlap = overlap( - tcx, - TrackAmbiguityCauses::Yes, - skip_leak_check, - impl1_def_id, - impl2_def_id, - overlap_mode, - ) - .unwrap(); - Some(overlap) + if tcx.next_trait_solver_in_coherence() { + overlap( + tcx, + TrackAmbiguityCauses::Yes, + skip_leak_check, + impl1_def_id, + impl2_def_id, + overlap_mode, + ) + } else { + let _overlap_with_bad_diagnostics = overlap( + tcx, + TrackAmbiguityCauses::No, + skip_leak_check, + impl1_def_id, + impl2_def_id, + overlap_mode, + )?; + + // In the case where we detect an error, run the check again, but + // this time tracking intercrate ambiguity causes for better + // diagnostics. (These take time and can lead to false errors.) + let overlap = overlap( + tcx, + TrackAmbiguityCauses::Yes, + skip_leak_check, + impl1_def_id, + impl2_def_id, + overlap_mode, + ) + .unwrap(); + Some(overlap) + } } fn fresh_impl_header<'tcx>(infcx: &InferCtxt<'tcx>, impl_def_id: DefId) -> ty::ImplHeader<'tcx> { @@ -358,9 +368,10 @@ fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>( overflowing_predicates: ambiguities .into_iter() .filter(|error| { - matches!(error.code, FulfillmentErrorCode::Ambiguity { - overflow: Some(true) - }) + matches!( + error.code, + FulfillmentErrorCode::Ambiguity { overflow: Some(true) } + ) }) .map(|e| infcx.resolve_vars_if_possible(e.obligation.predicate)) .collect(), @@ -481,13 +492,16 @@ fn plug_infer_with_placeholders<'tcx>( // Comparing against a type variable never registers hidden types anyway DefineOpaqueTypes::Yes, ty, - Ty::new_placeholder(self.infcx.tcx, ty::Placeholder { - universe: self.universe, - bound: ty::BoundTy { - var: self.next_var(), - kind: ty::BoundTyKind::Anon, + Ty::new_placeholder( + self.infcx.tcx, + ty::Placeholder { + universe: self.universe, + bound: ty::BoundTy { + var: self.next_var(), + kind: ty::BoundTyKind::Anon, + }, }, - }), + ), ) else { bug!("we always expect to be able to plug an infer var with placeholder") @@ -507,10 +521,10 @@ fn plug_infer_with_placeholders<'tcx>( // registration happening anyway. DefineOpaqueTypes::Yes, ct, - ty::Const::new_placeholder(self.infcx.tcx, ty::Placeholder { - universe: self.universe, - bound: self.next_var(), - }), + ty::Const::new_placeholder( + self.infcx.tcx, + ty::Placeholder { universe: self.universe, bound: self.next_var() }, + ), ) else { bug!("we always expect to be able to plug an infer var with placeholder") @@ -535,13 +549,16 @@ fn plug_infer_with_placeholders<'tcx>( // Lifetimes don't contain opaque types (or any types for that matter). DefineOpaqueTypes::Yes, r, - ty::Region::new_placeholder(self.infcx.tcx, ty::Placeholder { - universe: self.universe, - bound: ty::BoundRegion { - var: self.next_var(), - kind: ty::BoundRegionKind::Anon, + ty::Region::new_placeholder( + self.infcx.tcx, + ty::Placeholder { + universe: self.universe, + bound: ty::BoundRegion { + var: self.next_var(), + kind: ty::BoundRegionKind::Anon, + }, }, - }), + ), ) else { bug!("we always expect to be able to plug an infer var with placeholder") @@ -585,8 +602,7 @@ fn try_prove_negated_where_clause<'tcx>( // FIXME: We could use the assumed_wf_types from both impls, I think, // if that wasn't implemented just for LocalDefId, and we'd need to do // the normalization ourselves since this is totally fallible... - let outlives_env = OutlivesEnvironment::new(param_env); - let errors = ocx.resolve_regions(&outlives_env); + let errors = ocx.resolve_regions(CRATE_DEF_ID, param_env, []); if !errors.is_empty() { return false; } @@ -615,6 +631,7 @@ fn compute_intercrate_ambiguity_causes<'tcx>( } struct AmbiguityCausesVisitor<'a, 'tcx> { + cache: FxHashSet>>, causes: &'a mut FxIndexSet>, } @@ -624,6 +641,10 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> { } fn visit_goal(&mut self, goal: &InspectGoal<'_, 'tcx>) { + if !self.cache.insert(goal.goal()) { + return; + } + let infcx = goal.infcx(); for cand in goal.candidates() { cand.visit_nested_in_probe(self); @@ -693,7 +714,7 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> { if matches!(ty.kind(), ty::Alias(..)) { let ocx = ObligationCtxt::new(infcx); ty = ocx - .structurally_normalize(&ObligationCause::dummy(), param_env, ty) + .structurally_normalize_ty(&ObligationCause::dummy(), param_env, ty) .map_err(|_| ())?; if !ocx.select_where_possible().is_empty() { return Err(()); @@ -748,5 +769,10 @@ fn search_ambiguity_causes<'tcx>( goal: Goal<'tcx, ty::Predicate<'tcx>>, causes: &mut FxIndexSet>, ) { - infcx.probe(|_| infcx.visit_proof_tree(goal, &mut AmbiguityCausesVisitor { causes })); + infcx.probe(|_| { + infcx.visit_proof_tree( + goal, + &mut AmbiguityCausesVisitor { cache: Default::default(), causes }, + ) + }); } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 15eb5d74cbfa5..bdee6ca2afe54 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -35,7 +35,7 @@ pub fn is_const_evaluatable<'tcx>( ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Placeholder(_) - | ty::ConstKind::Value(_, _) + | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) => return Ok(()), ty::ConstKind::Infer(_) => return Err(NotConstEvaluatable::MentionsInfer), }; @@ -79,7 +79,7 @@ pub fn is_const_evaluatable<'tcx>( Err( EvaluateConstErr::EvaluationFailure(e) | EvaluateConstErr::InvalidConstParamTy(e), - ) => Err(NotConstEvaluatable::Error(e.into())), + ) => Err(NotConstEvaluatable::Error(e)), Ok(_) => Ok(()), } } @@ -140,7 +140,7 @@ pub fn is_const_evaluatable<'tcx>( } Err( EvaluateConstErr::EvaluationFailure(e) | EvaluateConstErr::InvalidConstParamTy(e), - ) => Err(NotConstEvaluatable::Error(e.into())), + ) => Err(NotConstEvaluatable::Error(e)), Ok(_) => Ok(()), } } diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index d2abd881c4591..617bc87a9d23c 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -4,21 +4,19 @@ //! //! [^1]: Formerly known as "object safety". -use std::iter; use std::ops::ControlFlow; -use rustc_abi::BackendRepr; use rustc_errors::FatalError; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::ty::{ - self, EarlyBinder, ExistentialPredicateStableCmpExt as _, GenericArgs, Ty, TyCtxt, - TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt, TypeVisitor, TypingMode, Upcast, + self, EarlyBinder, GenericArgs, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Upcast, }; use rustc_span::Span; +use rustc_type_ir::elaborate; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -39,7 +37,7 @@ pub fn hir_ty_lowering_dyn_compatibility_violations( trait_def_id: DefId, ) -> Vec { debug_assert!(tcx.generics_of(trait_def_id).has_self); - tcx.supertrait_def_ids(trait_def_id) + elaborate::supertrait_def_ids(tcx, trait_def_id) .map(|def_id| predicates_reference_self(tcx, def_id, true)) .filter(|spans| !spans.is_empty()) .map(DynCompatibilityViolation::SupertraitSelf) @@ -52,9 +50,8 @@ fn dyn_compatibility_violations( ) -> &'_ [DynCompatibilityViolation] { debug_assert!(tcx.generics_of(trait_def_id).has_self); debug!("dyn_compatibility_violations: {:?}", trait_def_id); - tcx.arena.alloc_from_iter( - tcx.supertrait_def_ids(trait_def_id) + elaborate::supertrait_def_ids(tcx, trait_def_id) .flat_map(|def_id| dyn_compatibility_violations_for_trait(tcx, def_id)), ) } @@ -64,7 +61,7 @@ fn is_dyn_compatible(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool { } /// We say a method is *vtable safe* if it can be invoked on a trait -/// object. Note that object-safe traits can have some +/// object. Note that dyn-compatible traits can have some /// non-vtable-safe methods, so long as they require `Self: Sized` or /// otherwise ensure that they cannot be used when `Self = Trait`. pub fn is_vtable_safe_method(tcx: TyCtxt<'_>, trait_def_id: DefId, method: ty::AssocItem) -> bool { @@ -109,14 +106,6 @@ fn dyn_compatibility_violations_for_trait( violations.push(DynCompatibilityViolation::SupertraitNonLifetimeBinder(spans)); } - if violations.is_empty() { - for item in tcx.associated_items(trait_def_id).in_definition_order() { - if let ty::AssocKind::Fn = item.kind { - check_receiver_correct(tcx, trait_def_id, *item); - } - } - } - violations } @@ -421,7 +410,7 @@ fn virtual_call_violations_for_method<'tcx>( let receiver_ty = tcx.liberate_late_bound_regions(method.def_id, sig.input(0)); // Until `unsized_locals` is fully implemented, `self: Self` can't be dispatched on. - // However, this is already considered object-safe. We allow it as a special case here. + // However, this is already considered dyn compatible. We allow it as a special case here. // FIXME(mikeyhew) get rid of this `if` statement once `receiver_is_dispatchable` allows // `Receiver: Unsize dyn Trait]>`. if receiver_ty != tcx.types.self_param { @@ -499,55 +488,6 @@ fn virtual_call_violations_for_method<'tcx>( errors } -/// This code checks that `receiver_is_dispatchable` is correctly implemented. -/// -/// This check is outlined from the dyn-compatibility check to avoid cycles with -/// layout computation, which relies on knowing whether methods are dyn-compatible. -fn check_receiver_correct<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, method: ty::AssocItem) { - if !is_vtable_safe_method(tcx, trait_def_id, method) { - return; - } - - let method_def_id = method.def_id; - let sig = tcx.fn_sig(method_def_id).instantiate_identity(); - let typing_env = ty::TypingEnv::non_body_analysis(tcx, method_def_id); - let receiver_ty = tcx.liberate_late_bound_regions(method_def_id, sig.input(0)); - - if receiver_ty == tcx.types.self_param { - // Assumed OK, may change later if unsized_locals permits `self: Self` as dispatchable. - return; - } - - // e.g., `Rc<()>` - let unit_receiver_ty = receiver_for_self_ty(tcx, receiver_ty, tcx.types.unit, method_def_id); - match tcx.layout_of(typing_env.as_query_input(unit_receiver_ty)).map(|l| l.backend_repr) { - Ok(BackendRepr::Scalar(..)) => (), - abi => { - tcx.dcx().span_delayed_bug( - tcx.def_span(method_def_id), - format!("receiver {unit_receiver_ty:?} when `Self = ()` should have a Scalar ABI; found {abi:?}"), - ); - } - } - - let trait_object_ty = object_ty_for_trait(tcx, trait_def_id, tcx.lifetimes.re_static); - - // e.g., `Rc` - let trait_object_receiver = - receiver_for_self_ty(tcx, receiver_ty, trait_object_ty, method_def_id); - match tcx.layout_of(typing_env.as_query_input(trait_object_receiver)).map(|l| l.backend_repr) { - Ok(BackendRepr::ScalarPair(..)) => (), - abi => { - tcx.dcx().span_delayed_bug( - tcx.def_span(method_def_id), - format!( - "receiver {trait_object_receiver:?} when `Self = {trait_object_ty}` should have a ScalarPair ABI; found {abi:?}" - ), - ); - } - } -} - /// Performs a type instantiation to produce the version of `receiver_ty` when `Self = self_ty`. /// For example, for `receiver_ty = Rc` and `self_ty = Foo`, returns `Rc`. fn receiver_for_self_ty<'tcx>( @@ -569,49 +509,6 @@ fn receiver_for_self_ty<'tcx>( result } -/// Creates the object type for the current trait. For example, -/// if the current trait is `Deref`, then this will be -/// `dyn Deref + 'static`. -#[instrument(level = "trace", skip(tcx), ret)] -fn object_ty_for_trait<'tcx>( - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, - lifetime: ty::Region<'tcx>, -) -> Ty<'tcx> { - let trait_ref = ty::TraitRef::identity(tcx, trait_def_id); - debug!(?trait_ref); - - let trait_predicate = ty::Binder::dummy(ty::ExistentialPredicate::Trait( - ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref), - )); - debug!(?trait_predicate); - - let pred: ty::Predicate<'tcx> = trait_ref.upcast(tcx); - let mut elaborated_predicates: Vec<_> = elaborate(tcx, [pred]) - .filter_map(|pred| { - debug!(?pred); - let pred = pred.as_projection_clause()?; - Some(pred.map_bound(|p| { - ty::ExistentialPredicate::Projection(ty::ExistentialProjection::erase_self_ty( - tcx, p, - )) - })) - }) - .collect(); - // NOTE: Since #37965, the existential predicates list has depended on the - // list of predicates to be sorted. This is mostly to enforce that the primary - // predicate comes first. - elaborated_predicates.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); - elaborated_predicates.dedup(); - - let existential_predicates = tcx.mk_poly_existential_predicates_from_iter( - iter::once(trait_predicate).chain(elaborated_predicates), - ); - debug!(?existential_predicates); - - Ty::new_dynamic(tcx, existential_predicates, lifetime, ty::Dyn) -} - /// Checks the method's receiver (the `self` argument) can be dispatched on when `Self` is a /// trait object. We require that `DispatchableFromDyn` be implemented for the receiver type /// in the following way: @@ -631,7 +528,7 @@ fn object_ty_for_trait<'tcx>( /// - `self: Pin>` requires `Pin>: DispatchFromDyn>>`. /// /// The only case where the receiver is not dispatchable, but is still a valid receiver -/// type (just not object-safe), is when there is more than one level of pointer indirection. +/// type (just not dyn compatible), is when there is more than one level of pointer indirection. /// E.g., `self: &&Self`, `self: &Rc`, `self: Box>`. In these cases, there /// is no way, or at least no inexpensive way, to coerce the receiver from the version where /// `Self = dyn Trait` to the version where `Self = T`, where `T` is the unknown erased type diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs index 91484ef99dbb8..b32909efe0be7 100644 --- a/compiler/rustc_trait_selection/src/traits/effects.rs +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -1,6 +1,8 @@ -use rustc_hir as hir; +use rustc_hir::{self as hir, LangItem}; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes}; -use rustc_infer::traits::{ImplSource, Obligation, PredicateObligation}; +use rustc_infer::traits::{ + ImplDerivedHostCause, ImplSource, Obligation, ObligationCauseCode, PredicateObligation, +}; use rustc_middle::span_bug; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::{self, TypingMode}; @@ -46,6 +48,12 @@ pub fn evaluate_host_effect_obligation<'tcx>( Err(EvaluationFailure::NoSolution) => {} } + match evaluate_host_effect_from_builtin_impls(selcx, obligation) { + Ok(result) => return Ok(result), + Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous), + Err(EvaluationFailure::NoSolution) => {} + } + match evaluate_host_effect_from_selection_candiate(selcx, obligation) { Ok(result) => return Ok(result), Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous), @@ -226,6 +234,104 @@ fn evaluate_host_effect_from_item_bounds<'tcx>( } } +fn evaluate_host_effect_from_builtin_impls<'tcx>( + selcx: &mut SelectionContext<'_, 'tcx>, + obligation: &HostEffectObligation<'tcx>, +) -> Result>, EvaluationFailure> { + match selcx.tcx().as_lang_item(obligation.predicate.def_id()) { + Some(LangItem::Destruct) => evaluate_host_effect_for_destruct_goal(selcx, obligation), + _ => Err(EvaluationFailure::NoSolution), + } +} + +// NOTE: Keep this in sync with `const_conditions_for_destruct` in the new solver. +fn evaluate_host_effect_for_destruct_goal<'tcx>( + selcx: &mut SelectionContext<'_, 'tcx>, + obligation: &HostEffectObligation<'tcx>, +) -> Result>, EvaluationFailure> { + let tcx = selcx.tcx(); + let destruct_def_id = tcx.require_lang_item(LangItem::Destruct, None); + let self_ty = obligation.predicate.self_ty(); + + let const_conditions = match *self_ty.kind() { + // An ADT is `~const Destruct` only if all of the fields are, + // *and* if there is a `Drop` impl, that `Drop` impl is also `~const`. + ty::Adt(adt_def, args) => { + let mut const_conditions: ThinVec<_> = adt_def + .all_fields() + .map(|field| ty::TraitRef::new(tcx, destruct_def_id, [field.ty(tcx, args)])) + .collect(); + match adt_def.destructor(tcx).map(|dtor| dtor.constness) { + // `Drop` impl exists, but it's not const. Type cannot be `~const Destruct`. + Some(hir::Constness::NotConst) => return Err(EvaluationFailure::NoSolution), + // `Drop` impl exists, and it's const. Require `Ty: ~const Drop` to hold. + Some(hir::Constness::Const) => { + let drop_def_id = tcx.require_lang_item(LangItem::Drop, None); + let drop_trait_ref = ty::TraitRef::new(tcx, drop_def_id, [self_ty]); + const_conditions.push(drop_trait_ref); + } + // No `Drop` impl, no need to require anything else. + None => {} + } + const_conditions + } + + ty::Array(ty, _) | ty::Pat(ty, _) | ty::Slice(ty) => { + thin_vec![ty::TraitRef::new(tcx, destruct_def_id, [ty])] + } + + ty::Tuple(tys) => { + tys.iter().map(|field_ty| ty::TraitRef::new(tcx, destruct_def_id, [field_ty])).collect() + } + + // Trivially implement `~const Destruct` + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Str + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Never + | ty::Infer(ty::InferTy::FloatVar(_) | ty::InferTy::IntVar(_)) + | ty::Error(_) => thin_vec![], + + // Coroutines and closures could implement `~const Drop`, + // but they don't really need to right now. + ty::Closure(_, _) + | ty::CoroutineClosure(_, _) + | ty::Coroutine(_, _) + | ty::CoroutineWitness(_, _) => return Err(EvaluationFailure::NoSolution), + + // FIXME(unsafe_binders): Unsafe binders could implement `~const Drop` + // if their inner type implements it. + ty::UnsafeBinder(_) => return Err(EvaluationFailure::NoSolution), + + ty::Dynamic(..) | ty::Param(_) | ty::Alias(..) | ty::Placeholder(_) | ty::Foreign(_) => { + return Err(EvaluationFailure::NoSolution); + } + + ty::Bound(..) + | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + panic!("unexpected type `{self_ty:?}`") + } + }; + + Ok(const_conditions + .into_iter() + .map(|trait_ref| { + obligation.with( + tcx, + ty::Binder::dummy(trait_ref) + .to_host_effect_clause(tcx, obligation.predicate.constness), + ) + }) + .collect()) +} + fn evaluate_host_effect_from_selection_candiate<'tcx>( selcx: &mut SelectionContext<'_, 'tcx>, obligation: &HostEffectObligation<'tcx>, @@ -248,9 +354,22 @@ fn evaluate_host_effect_from_selection_candiate<'tcx>( tcx.const_conditions(impl_.impl_def_id) .instantiate(tcx, impl_.args) .into_iter() - .map(|(trait_ref, _)| { - obligation.with( + .map(|(trait_ref, span)| { + Obligation::new( tcx, + obligation.cause.clone().derived_host_cause( + ty::Binder::dummy(obligation.predicate), + |derived| { + ObligationCauseCode::ImplDerivedHost(Box::new( + ImplDerivedHostCause { + derived, + impl_def_id: impl_.impl_def_id, + span, + }, + )) + }, + ), + obligation.param_env, trait_ref .to_host_effect_clause(tcx, obligation.predicate.constness), ) diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index 5e270b62b0081..9f3178f887927 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -8,7 +8,6 @@ use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::canonical::{ Canonical, CanonicalQueryResponse, CanonicalVarValues, QueryResponse, }; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, RegionResolutionError, TypeTrace}; use rustc_infer::traits::PredicateObligations; use rustc_macros::extension; @@ -217,14 +216,15 @@ where /// will result in region constraints getting ignored. pub fn resolve_regions_and_report_errors( self, - generic_param_scope: LocalDefId, - outlives_env: &OutlivesEnvironment<'tcx>, + body_id: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + assumed_wf_tys: impl IntoIterator>, ) -> Result<(), ErrorGuaranteed> { - let errors = self.infcx.resolve_regions(outlives_env); + let errors = self.infcx.resolve_regions(body_id, param_env, assumed_wf_tys); if errors.is_empty() { Ok(()) } else { - Err(self.infcx.err_ctxt().report_region_errors(generic_param_scope, &errors)) + Err(self.infcx.err_ctxt().report_region_errors(body_id, &errors)) } } @@ -235,9 +235,11 @@ where #[must_use] pub fn resolve_regions( self, - outlives_env: &OutlivesEnvironment<'tcx>, + body_id: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + assumed_wf_tys: impl IntoIterator>, ) -> Vec> { - self.infcx.resolve_regions(outlives_env) + self.infcx.resolve_regions(body_id, param_env, assumed_wf_tys) } } @@ -319,7 +321,7 @@ where self.infcx.at(cause, param_env).deeply_normalize(value, &mut **self.engine.borrow_mut()) } - pub fn structurally_normalize( + pub fn structurally_normalize_ty( &self, cause: &ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -327,7 +329,7 @@ where ) -> Result, Vec> { self.infcx .at(cause, param_env) - .structurally_normalize(value, &mut **self.engine.borrow_mut()) + .structurally_normalize_ty(value, &mut **self.engine.borrow_mut()) } pub fn structurally_normalize_const( @@ -340,4 +342,15 @@ where .at(cause, param_env) .structurally_normalize_const(value, &mut **self.engine.borrow_mut()) } + + pub fn structurally_normalize_term( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + value: ty::Term<'tcx>, + ) -> Result, Vec> { + self.infcx + .at(cause, param_env) + .structurally_normalize_term(value, &mut **self.engine.borrow_mut()) + } } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 7529ee128f5e4..f2d2dd2f3ce59 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -479,7 +479,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ty::ConstKind::Error(_) => { return ProcessResult::Changed(PendingPredicateObligations::new()); } - ty::ConstKind::Value(ty, _) => ty, + ty::ConstKind::Value(cv) => cv.ty, ty::ConstKind::Unevaluated(uv) => { infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args) } diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index 7a67b943e9494..79e178150de3a 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -4,13 +4,10 @@ use std::assert_matches::assert_matches; use hir::LangItem; use rustc_ast::Mutability; -use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt}; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeVisitableExt, TypingMode}; -use super::outlives_bounds::InferCtxtExt; use crate::regions::InferCtxtRegionExt; use crate::traits::{self, FulfillmentError, ObligationCause}; @@ -170,15 +167,7 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>( } // Check regions assuming the self type of the impl is WF - let outlives_env = OutlivesEnvironment::with_bounds( - param_env, - infcx.implied_bounds_tys( - param_env, - parent_cause.body_id, - &FxIndexSet::from_iter([self_type]), - ), - ); - let errors = infcx.resolve_regions(&outlives_env); + let errors = infcx.resolve_regions(parent_cause.body_id, param_env, [self_type]); if !errors.is_empty() { infringing_inner_tys.push((inner_ty, InfringingFieldsReason::Regions(errors))); continue; @@ -261,15 +250,7 @@ pub fn all_fields_implement_trait<'tcx>( } // Check regions assuming the self type of the impl is WF - let outlives_env = OutlivesEnvironment::with_bounds( - param_env, - infcx.implied_bounds_tys( - param_env, - parent_cause.body_id, - &FxIndexSet::from_iter([self_type]), - ), - ); - let errors = infcx.resolve_regions(&outlives_env); + let errors = infcx.resolve_regions(parent_cause.body_id, param_env, [self_type]); if !errors.is_empty() { infringing.push((field, ty, InfringingFieldsReason::Regions(errors))); } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 1dcd0d0dfb830..105cb9175717d 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -66,9 +66,9 @@ pub use self::specialize::{ }; pub use self::structural_normalize::StructurallyNormalizeExt; pub use self::util::{ - BoundVarReplacer, PlaceholderReplacer, TraitAliasExpander, TraitAliasExpansionInfo, elaborate, - expand_trait_aliases, impl_item_is_final, supertraits, - transitive_bounds_that_define_assoc_item, upcast_choices, with_replaced_escaping_bound_vars, + BoundVarReplacer, PlaceholderReplacer, elaborate, expand_trait_aliases, impl_item_is_final, + supertrait_def_ids, supertraits, transitive_bounds_that_define_assoc_item, upcast_choices, + with_replaced_escaping_bound_vars, }; use crate::error_reporting::InferCtxtErrorExt; use crate::infer::outlives::env::OutlivesEnvironment; @@ -76,6 +76,7 @@ use crate::infer::{InferCtxt, TyCtxtInferExt}; use crate::regions::InferCtxtRegionExt; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; +#[derive(Debug)] pub struct FulfillmentError<'tcx> { pub obligation: PredicateObligation<'tcx>, pub code: FulfillmentErrorCode<'tcx>, @@ -107,12 +108,6 @@ impl<'tcx> FulfillmentError<'tcx> { } } -impl<'tcx> Debug for FulfillmentError<'tcx> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "FulfillmentError({:?},{:?})", self.obligation, self.code) - } -} - #[derive(Clone)] pub enum FulfillmentErrorCode<'tcx> { /// Inherently impossible to fulfill; this trait is implemented if and only @@ -290,12 +285,10 @@ fn do_normalize_predicates<'tcx>( // We can use the `elaborated_env` here; the region code only // cares about declarations like `'a: 'b`. - let outlives_env = OutlivesEnvironment::new(elaborated_env); - // FIXME: It's very weird that we ignore region obligations but apparently // still need to use `resolve_regions` as we need the resolved regions in // the normalized predicates. - let errors = infcx.resolve_regions(&outlives_env); + let errors = infcx.resolve_regions(cause.body_id, elaborated_env, []); if !errors.is_empty() { tcx.dcx().span_delayed_bug( span, @@ -667,13 +660,16 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>( self.idx += 1; idx }; - Ty::new_placeholder(self.tcx, ty::PlaceholderType { - universe: ty::UniverseIndex::ROOT, - bound: ty::BoundTy { - var: ty::BoundVar::from_u32(idx), - kind: ty::BoundTyKind::Anon, + Ty::new_placeholder( + self.tcx, + ty::PlaceholderType { + universe: ty::UniverseIndex::ROOT, + bound: ty::BoundTy { + var: ty::BoundVar::from_u32(idx), + kind: ty::BoundTyKind::Anon, + }, }, - }) + ) } else { t.super_fold_with(self) } @@ -681,14 +677,17 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>( fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { if let ty::ConstKind::Infer(_) = c.kind() { - ty::Const::new_placeholder(self.tcx, ty::PlaceholderConst { - universe: ty::UniverseIndex::ROOT, - bound: ty::BoundVar::from_u32({ - let idx = self.idx; - self.idx += 1; - idx - }), - }) + ty::Const::new_placeholder( + self.tcx, + ty::PlaceholderConst { + universe: ty::UniverseIndex::ROOT, + bound: ty::BoundVar::from_u32({ + let idx = self.idx; + self.idx += 1; + idx + }), + }, + ) } else { c.super_fold_with(self) } @@ -714,9 +713,18 @@ pub fn impossible_predicates<'tcx>(tcx: TyCtxt<'tcx>, predicates: Vec( diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 23dabe32ff2e3..189326958078d 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -1,4 +1,3 @@ -use rustc_data_structures::fx::FxIndexSet; use rustc_infer::infer::InferOk; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; @@ -12,9 +11,6 @@ use tracing::instrument; use crate::infer::InferCtxt; use crate::traits::{ObligationCause, ObligationCtxt}; -pub type BoundsCompat<'a, 'tcx: 'a> = impl Iterator> + 'a; -pub type Bounds<'a, 'tcx: 'a> = impl Iterator> + 'a; - /// Implied bounds are region relationships that we deduce /// automatically. The idea is that (e.g.) a caller must check that a /// function's argument types are well-formed immediately before @@ -110,36 +106,18 @@ fn implied_outlives_bounds<'a, 'tcx>( bounds } -#[extension(pub trait InferCtxtExt<'a, 'tcx>)] -impl<'a, 'tcx: 'a> InferCtxt<'tcx> { - /// Do *NOT* call this directly. - fn implied_bounds_tys_compat( - &'a self, - param_env: ParamEnv<'tcx>, +#[extension(pub trait InferCtxtExt<'tcx>)] +impl<'tcx> InferCtxt<'tcx> { + /// Do *NOT* call this directly. You probably want to construct a `OutlivesEnvironment` + /// instead if you're interested in the implied bounds for a given signature. + fn implied_bounds_tys_with_compat>>( + &self, body_id: LocalDefId, - tys: &'a FxIndexSet>, - compat: bool, - ) -> BoundsCompat<'a, 'tcx> { - tys.iter() - .flat_map(move |ty| implied_outlives_bounds(self, param_env, body_id, *ty, compat)) - } - - /// If `-Z no-implied-bounds-compat` is set, calls `implied_bounds_tys_compat` - /// with `compat` set to `true`, otherwise `false`. - fn implied_bounds_tys( - &'a self, param_env: ParamEnv<'tcx>, - body_id: LocalDefId, - tys: &'a FxIndexSet>, - ) -> Bounds<'a, 'tcx> { - tys.iter().flat_map(move |ty| { - implied_outlives_bounds( - self, - param_env, - body_id, - *ty, - !self.tcx.sess.opts.unstable_opts.no_implied_bounds_compat, - ) - }) + tys: Tys, + compat: bool, + ) -> impl Iterator> { + tys.into_iter() + .flat_map(move |ty| implied_outlives_bounds(self, param_env, body_id, ty, compat)) } } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 54407d17dcf0f..de5aaa5bd9729 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -18,6 +18,7 @@ use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, Term, Ty, TyCtxt, TypingMode, Upcast}; use rustc_middle::{bug, span_bug}; use rustc_span::sym; +use rustc_type_ir::elaborate; use thin_vec::thin_vec; use tracing::{debug, instrument}; @@ -836,8 +837,7 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>( if tcx.is_impl_trait_in_trait(obligation.predicate.def_id) && let Some(out_trait_def_id) = data.principal_def_id() && let rpitit_trait_def_id = tcx.parent(obligation.predicate.def_id) - && tcx - .supertrait_def_ids(out_trait_def_id) + && elaborate::supertrait_def_ids(tcx, out_trait_def_id) .any(|trait_def_id| trait_def_id == rpitit_trait_def_id) { candidate_set.push_candidate(ProjectionCandidate::ObjectRpitit); @@ -950,42 +950,48 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // // NOTE: This should be kept in sync with the similar code in // `rustc_ty_utils::instance::resolve_associated_item()`. - let node_item = specialization_graph::assoc_def( + match specialization_graph::assoc_def( selcx.tcx(), impl_data.impl_def_id, obligation.predicate.def_id, - ) - .map_err(|ErrorGuaranteed { .. }| ())?; - - if node_item.is_final() { - // Non-specializable items are always projectable. - true - } else { - // Only reveal a specializable default if we're past type-checking - // and the obligation is monomorphic, otherwise passes such as - // transmute checking and polymorphic MIR optimizations could - // get a result which isn't correct for all monomorphizations. - match selcx.infcx.typing_mode() { - TypingMode::Coherence - | TypingMode::Analysis { .. } - | TypingMode::PostBorrowckAnalysis { .. } => { - debug!( - assoc_ty = ?selcx.tcx().def_path_str(node_item.item.def_id), - ?obligation.predicate, - "assemble_candidates_from_impls: not eligible due to default", - ); - false - } - TypingMode::PostAnalysis => { - // NOTE(eddyb) inference variables can resolve to parameters, so - // assume `poly_trait_ref` isn't monomorphic, if it contains any. - let poly_trait_ref = selcx.infcx.resolve_vars_if_possible(trait_ref); - !poly_trait_ref.still_further_specializable() + ) { + Ok(node_item) => { + if node_item.is_final() { + // Non-specializable items are always projectable. + true + } else { + // Only reveal a specializable default if we're past type-checking + // and the obligation is monomorphic, otherwise passes such as + // transmute checking and polymorphic MIR optimizations could + // get a result which isn't correct for all monomorphizations. + match selcx.infcx.typing_mode() { + TypingMode::Coherence + | TypingMode::Analysis { .. } + | TypingMode::PostBorrowckAnalysis { .. } => { + debug!( + assoc_ty = ?selcx.tcx().def_path_str(node_item.item.def_id), + ?obligation.predicate, + "not eligible due to default", + ); + false + } + TypingMode::PostAnalysis => { + // NOTE(eddyb) inference variables can resolve to parameters, so + // assume `poly_trait_ref` isn't monomorphic, if it contains any. + let poly_trait_ref = + selcx.infcx.resolve_vars_if_possible(trait_ref); + !poly_trait_ref.still_further_specializable() + } + } } } + // Always project `ErrorGuaranteed`, since this will just help + // us propagate `TyKind::Error` around which suppresses ICEs + // and spurious, unrelated inference errors. + Err(ErrorGuaranteed { .. }) => true, } } - ImplSource::Builtin(BuiltinImplSource::Misc, _) => { + ImplSource::Builtin(BuiltinImplSource::Misc | BuiltinImplSource::Trivial, _) => { // While a builtin impl may be known to exist, the associated type may not yet // be known. Any type with multiple potential associated types is therefore // not eligible. @@ -1142,7 +1148,9 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // If returned by `struct_tail` this is the empty tuple. | ty::Tuple(..) // Integers and floats are always Sized, and so have unit type metadata. - | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true, + | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) + // This happens if we reach the recursion limit when finding the struct tail. + | ty::Error(..) => true, // We normalize from `Wrapper::Metadata` to `Tail::Metadata` if able. // Otherwise, type parameters, opaques, and unnormalized projections have @@ -1173,8 +1181,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Alias(..) | ty::Bound(..) | ty::Placeholder(..) - | ty::Infer(..) - | ty::Error(_) => { + | ty::Infer(..) => { if tail.has_infer_types() { candidate_set.mark_ambiguous(); } @@ -1289,7 +1296,7 @@ fn confirm_select_candidate<'cx, 'tcx>( ) -> Progress<'tcx> { match impl_source { ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data), - ImplSource::Builtin(BuiltinImplSource::Misc, data) => { + ImplSource::Builtin(BuiltinImplSource::Misc | BuiltinImplSource::Trivial, data) => { let tcx = selcx.tcx(); let trait_def_id = obligation.predicate.trait_def_id(tcx); if tcx.is_lang_item(trait_def_id, LangItem::Coroutine) { @@ -1663,14 +1670,18 @@ fn confirm_closure_candidate<'cx, 'tcx>( } else { let upvars_projection_def_id = tcx.require_lang_item(LangItem::AsyncFnKindUpvars, None); - let tupled_upvars_ty = Ty::new_projection(tcx, upvars_projection_def_id, [ - ty::GenericArg::from(kind_ty), - Ty::from_closure_kind(tcx, ty::ClosureKind::FnOnce).into(), - tcx.lifetimes.re_static.into(), - sig.tupled_inputs_ty.into(), - args.tupled_upvars_ty().into(), - args.coroutine_captures_by_ref_ty().into(), - ]); + let tupled_upvars_ty = Ty::new_projection( + tcx, + upvars_projection_def_id, + [ + ty::GenericArg::from(kind_ty), + Ty::from_closure_kind(tcx, ty::ClosureKind::FnOnce).into(), + tcx.lifetimes.re_static.into(), + sig.tupled_inputs_ty.into(), + args.tupled_upvars_ty().into(), + args.coroutine_captures_by_ref_ty().into(), + ], + ); sig.to_coroutine( tcx, args.parent_args(), @@ -1788,14 +1799,18 @@ fn confirm_async_closure_candidate<'cx, 'tcx>( // will project to the right upvars for the generator, appending the inputs and // coroutine upvars respecting the closure kind. // N.B. No need to register a `AsyncFnKindHelper` goal here, it's already in `nested`. - let tupled_upvars_ty = Ty::new_projection(tcx, upvars_projection_def_id, [ - ty::GenericArg::from(kind_ty), - Ty::from_closure_kind(tcx, goal_kind).into(), - env_region.into(), - sig.tupled_inputs_ty.into(), - args.tupled_upvars_ty().into(), - args.coroutine_captures_by_ref_ty().into(), - ]); + let tupled_upvars_ty = Ty::new_projection( + tcx, + upvars_projection_def_id, + [ + ty::GenericArg::from(kind_ty), + Ty::from_closure_kind(tcx, goal_kind).into(), + env_region.into(), + sig.tupled_inputs_ty.into(), + args.tupled_upvars_ty().into(), + args.coroutine_captures_by_ref_ty().into(), + ], + ); sig.to_coroutine( tcx, args.parent_args(), @@ -1809,17 +1824,16 @@ fn confirm_async_closure_candidate<'cx, 'tcx>( name => bug!("no such associated type: {name}"), }; let projection_term = match item_name { - sym::CallOnceFuture | sym::Output => { - ty::AliasTerm::new(tcx, obligation.predicate.def_id, [ - self_ty, - sig.tupled_inputs_ty, - ]) - } - sym::CallRefFuture => ty::AliasTerm::new(tcx, obligation.predicate.def_id, [ - ty::GenericArg::from(self_ty), - sig.tupled_inputs_ty.into(), - env_region.into(), - ]), + sym::CallOnceFuture | sym::Output => ty::AliasTerm::new( + tcx, + obligation.predicate.def_id, + [self_ty, sig.tupled_inputs_ty], + ), + sym::CallRefFuture => ty::AliasTerm::new( + tcx, + obligation.predicate.def_id, + [ty::GenericArg::from(self_ty), sig.tupled_inputs_ty.into(), env_region.into()], + ), name => bug!("no such associated type: {name}"), }; @@ -1839,17 +1853,20 @@ fn confirm_async_closure_candidate<'cx, 'tcx>( name => bug!("no such associated type: {name}"), }; let projection_term = match item_name { - sym::CallOnceFuture | sym::Output => { - ty::AliasTerm::new(tcx, obligation.predicate.def_id, [ - self_ty, - Ty::new_tup(tcx, sig.inputs()), - ]) - } - sym::CallRefFuture => ty::AliasTerm::new(tcx, obligation.predicate.def_id, [ - ty::GenericArg::from(self_ty), - Ty::new_tup(tcx, sig.inputs()).into(), - env_region.into(), - ]), + sym::CallOnceFuture | sym::Output => ty::AliasTerm::new( + tcx, + obligation.predicate.def_id, + [self_ty, Ty::new_tup(tcx, sig.inputs())], + ), + sym::CallRefFuture => ty::AliasTerm::new( + tcx, + obligation.predicate.def_id, + [ + ty::GenericArg::from(self_ty), + Ty::new_tup(tcx, sig.inputs()).into(), + env_region.into(), + ], + ), name => bug!("no such associated type: {name}"), }; @@ -1872,11 +1889,11 @@ fn confirm_async_closure_candidate<'cx, 'tcx>( sym::CallOnceFuture | sym::Output => { ty::AliasTerm::new(tcx, obligation.predicate.def_id, [self_ty, sig.inputs()[0]]) } - sym::CallRefFuture => ty::AliasTerm::new(tcx, obligation.predicate.def_id, [ - ty::GenericArg::from(self_ty), - sig.inputs()[0].into(), - env_region.into(), - ]), + sym::CallRefFuture => ty::AliasTerm::new( + tcx, + obligation.predicate.def_id, + [ty::GenericArg::from(self_ty), sig.inputs()[0].into(), env_region.into()], + ), name => bug!("no such associated type: {name}"), }; @@ -2014,7 +2031,6 @@ fn confirm_impl_candidate<'cx, 'tcx>( Ok(assoc_ty) => assoc_ty, Err(guar) => return Progress::error(tcx, guar), }; - if !assoc_ty.item.defaultness(tcx).has_value() { // This means that the impl is missing a definition for the // associated type. This error will be reported by the type diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index 4004e354dc189..92098e204487a 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -91,6 +91,7 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { pub fn compute_dropck_outlives_inner<'tcx>( ocx: &ObligationCtxt<'_, 'tcx>, goal: ParamEnvAnd<'tcx, DropckOutlives<'tcx>>, + span: Span, ) -> Result, NoSolution> { let tcx = ocx.infcx.tcx; let ParamEnvAnd { param_env, value: DropckOutlives { dropped_ty } } = goal; @@ -136,7 +137,7 @@ pub fn compute_dropck_outlives_inner<'tcx>( // Set used to detect infinite recursion. let mut ty_set = FxHashSet::default(); - let cause = ObligationCause::dummy(); + let cause = ObligationCause::dummy_with_span(span); let mut constraints = DropckConstraint::empty(); while let Some((ty, depth)) = ty_stack.pop() { debug!( diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs index 254dee794f1bc..4eecde00eaa1e 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs @@ -30,8 +30,9 @@ impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> { fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, + span: Span, ) -> Result { - type_op_ascribe_user_type_with_span(ocx, key, None) + type_op_ascribe_user_type_with_span(ocx, key, span) } } @@ -41,11 +42,10 @@ impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> { pub fn type_op_ascribe_user_type_with_span<'tcx>( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, AscribeUserType<'tcx>>, - span: Option, + span: Span, ) -> Result<(), NoSolution> { let (param_env, AscribeUserType { mir_ty, user_ty }) = key.into_parts(); debug!("type_op_ascribe_user_type: mir_ty={:?} user_ty={:?}", mir_ty, user_ty); - let span = span.unwrap_or(DUMMY_SP); match user_ty.kind { UserTypeKind::Ty(user_ty) => relate_mir_and_user_ty(ocx, param_env, span, mir_ty, user_ty)?, UserTypeKind::TypeOf(def_id, user_args) => { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index fe47e837dfb53..ec0b790339691 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -5,8 +5,8 @@ use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; use rustc_middle::infer::canonical::CanonicalQueryResponse; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFolder, TypeVisitableExt}; -use rustc_span::DUMMY_SP; use rustc_span::def_id::CRATE_DEF_ID; +use rustc_span::{DUMMY_SP, Span}; use rustc_type_ir::outlives::{Component, push_outlives_components}; use smallvec::{SmallVec, smallvec}; use tracing::debug; @@ -45,11 +45,12 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, + span: Span, ) -> Result { if ocx.infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat { - compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty) + compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty, span) } else { - compute_implied_outlives_bounds_compat_inner(ocx, key.param_env, key.value.ty) + compute_implied_outlives_bounds_compat_inner(ocx, key.param_env, key.value.ty, span) } } } @@ -58,13 +59,14 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( ocx: &ObligationCtxt<'_, 'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, + span: Span, ) -> Result>, NoSolution> { let normalize_op = |ty| -> Result<_, NoSolution> { // We must normalize the type so we can compute the right outlives components. // for example, if we have some constrained param type like `T: Trait`, // and we know that `&'a T::Out` is WF, then we want to imply `U: 'a`. let ty = ocx - .deeply_normalize(&ObligationCause::dummy(), param_env, ty) + .deeply_normalize(&ObligationCause::dummy_with_span(span), param_env, ty) .map_err(|_| NoSolution)?; if !ocx.select_all_or_error().is_empty() { return Err(NoSolution); @@ -90,7 +92,9 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( // From the full set of obligations, just filter down to the region relationships. for obligation in - wf::unnormalized_obligations(ocx.infcx, param_env, arg).into_iter().flatten() + wf::unnormalized_obligations(ocx.infcx, param_env, arg, DUMMY_SP, CRATE_DEF_ID) + .into_iter() + .flatten() { assert!(!obligation.has_escaping_bound_vars()); let Some(pred) = obligation.predicate.kind().no_bound_vars() else { @@ -142,6 +146,7 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>( ocx: &ObligationCtxt<'_, 'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, + span: Span, ) -> Result>, NoSolution> { let tcx = ocx.infcx.tcx; @@ -171,8 +176,8 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>( // FIXME(@lcnr): It's not really "always fine", having fewer implied // bounds can be backward incompatible, e.g. #101951 was caused by // us not dealing with inference vars in `TypeOutlives` predicates. - let obligations = wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, DUMMY_SP) - .unwrap_or_default(); + let obligations = + wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, span).unwrap_or_default(); for obligation in obligations { debug!(?obligation); @@ -255,7 +260,7 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>( // Need to manually normalize in the new solver as `wf::obligations` does not. if ocx.infcx.next_trait_solver() { ty_a = ocx - .deeply_normalize(&ObligationCause::dummy(), param_env, ty_a) + .deeply_normalize(&ObligationCause::dummy_with_span(span), param_env, ty_a) .map_err(|_| NoSolution)?; } let mut components = smallvec![]; diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 54fce914bb658..68feb19c55b89 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -92,6 +92,7 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable> + 't fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, + span: Span, ) -> Result; fn fully_perform_into( @@ -152,7 +153,7 @@ where if infcx.next_trait_solver() { return Ok(scrape_region_constraints( infcx, - |ocx| QueryTypeOp::perform_locally_with_next_solver(ocx, self), + |ocx| QueryTypeOp::perform_locally_with_next_solver(ocx, self, span), "query type op", span, )? diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs index 94df222932efe..2f6bbd7f4cf48 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs @@ -2,9 +2,10 @@ use std::fmt; use rustc_middle::traits::ObligationCause; use rustc_middle::traits::query::NoSolution; -pub use rustc_middle::traits::query::type_op::Normalize; +pub use rustc_middle::traits::query::type_op::{DeeplyNormalize, Normalize}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{self, Lift, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt}; +use rustc_span::Span; use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; use crate::traits::ObligationCtxt; @@ -26,12 +27,54 @@ where T::type_op_method(tcx, canonicalized) } + fn perform_locally_with_next_solver( + _ocx: &ObligationCtxt<'_, 'tcx>, + key: ParamEnvAnd<'tcx, Self>, + _span: Span, + ) -> Result { + Ok(key.value.value) + } +} + +impl<'tcx, T> super::QueryTypeOp<'tcx> for DeeplyNormalize +where + T: Normalizable<'tcx> + 'tcx, +{ + type QueryResponse = T; + + fn try_fast_path(_tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option { + if !key.value.value.has_aliases() { Some(key.value.value) } else { None } + } + + fn perform_query( + tcx: TyCtxt<'tcx>, + canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Result, NoSolution> { + T::type_op_method( + tcx, + CanonicalQueryInput { + typing_mode: canonicalized.typing_mode, + canonical: canonicalized.canonical.unchecked_map( + |ty::ParamEnvAnd { param_env, value }| ty::ParamEnvAnd { + param_env, + value: Normalize { value: value.value }, + }, + ), + }, + ) + } + fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, + span: Span, ) -> Result { - // FIXME(-Znext-solver): shouldn't be using old normalizer - Ok(ocx.normalize(&ObligationCause::dummy(), key.param_env, key.value.value)) + ocx.deeply_normalize( + &ObligationCause::dummy_with_span(span), + key.param_env, + key.value.value, + ) + .map_err(|_| NoSolution) } } @@ -79,3 +122,14 @@ impl<'tcx> Normalizable<'tcx> for ty::FnSig<'tcx> { tcx.type_op_normalize_fn_sig(canonicalized) } } + +/// This impl is not needed, since we never normalize type outlives predicates +/// in the old solver, but is required by trait bounds to be happy. +impl<'tcx> Normalizable<'tcx> for ty::PolyTypeOutlivesPredicate<'tcx> { + fn type_op_method( + _tcx: TyCtxt<'tcx>, + _canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Normalize>>, + ) -> Result, NoSolution> { + unreachable!("we never normalize PolyTypeOutlivesPredicate") + } +} diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs index fa05f901f663d..99a2779aa8212 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs @@ -1,5 +1,6 @@ use rustc_middle::traits::query::{DropckOutlivesResult, NoSolution}; use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; +use rustc_span::Span; use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; use crate::traits::ObligationCtxt; @@ -28,7 +29,8 @@ impl<'tcx> super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> { fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, + span: Span, ) -> Result { - compute_dropck_outlives_inner(ocx, key.param_env.and(key.value)) + compute_dropck_outlives_inner(ocx, key.param_env.and(key.value), span) } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs index b2dab379262f5..4f9e2e79d624c 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs @@ -4,6 +4,7 @@ use rustc_middle::traits::ObligationCause; use rustc_middle::traits::query::NoSolution; pub use rustc_middle::traits::query::type_op::ProvePredicate; use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt}; +use rustc_span::Span; use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; use crate::traits::ObligationCtxt; @@ -57,10 +58,11 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, + span: Span, ) -> Result { ocx.register_obligation(Obligation::new( ocx.infcx.tcx, - ObligationCause::dummy(), + ObligationCause::dummy_with_span(span), key.param_env, key.value.predicate, )); diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 968dc631e50e5..5b362b2356e82 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -12,19 +12,15 @@ use hir::LangItem; use hir::def_id::DefId; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_hir as hir; -use rustc_infer::traits::{ - Obligation, ObligationCause, PolyTraitObligation, PredicateObligations, SelectionError, -}; +use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError}; use rustc_middle::ty::fast_reject::DeepRejectCtxt; -use rustc_middle::ty::{self, ToPolyTraitRef, Ty, TypeVisitableExt, TypingMode}; +use rustc_middle::ty::{self, Ty, TypeVisitableExt, TypingMode}; use rustc_middle::{bug, span_bug}; -use rustc_type_ir::Interner; +use rustc_type_ir::{Interner, elaborate}; use tracing::{debug, instrument, trace}; use super::SelectionCandidate::*; use super::{BuiltinImplConditions, SelectionCandidateSet, SelectionContext, TraitObligationStack}; -use crate::traits; -use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::util; impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { @@ -106,6 +102,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.assemble_candidate_for_tuple(obligation, &mut candidates); } else if tcx.is_lang_item(def_id, LangItem::FnPtrTrait) { self.assemble_candidates_for_fn_ptr_trait(obligation, &mut candidates); + } else if tcx.is_lang_item(def_id, LangItem::BikeshedGuaranteedNoDrop) { + self.assemble_candidates_for_bikeshed_guaranteed_no_drop_trait( + obligation, + &mut candidates, + ); } else { if tcx.is_lang_item(def_id, LangItem::Clone) { // Same builtin conditions as `Copy`, i.e., every type which has builtin support @@ -186,10 +187,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } selcx.infcx.probe(|_| { + // We checked the polarity already match selcx.match_normalize_trait_ref( obligation, placeholder_trait_predicate.trait_ref, - bound.to_poly_trait_ref(), + bound.map_bound(|pred| pred.trait_ref), ) { Ok(None) => { candidates.vec.push(ProjectionCandidate(idx)); @@ -903,54 +905,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }) } - /// Temporary migration for #89190 - fn need_migrate_deref_output_trait_object( - &mut self, - ty: Ty<'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: &ObligationCause<'tcx>, - ) -> Option> { - // Don't drop any candidates in intercrate mode, as it's incomplete. - // (Not that it matters, since `Unsize` is not a stable trait.) - // - // FIXME(@lcnr): This should probably only trigger during analysis, - // disabling candidates during codegen is also questionable. - if let TypingMode::Coherence = self.infcx.typing_mode() { - return None; - } - - let tcx = self.tcx(); - if tcx.features().trait_upcasting() { - return None; - } - - // - let trait_ref = ty::TraitRef::new(tcx, tcx.lang_items().deref_trait()?, [ty]); - - let obligation = - traits::Obligation::new(tcx, cause.clone(), param_env, ty::Binder::dummy(trait_ref)); - if !self.infcx.predicate_may_hold(&obligation) { - return None; - } - - self.infcx.probe(|_| { - let ty = traits::normalize_projection_ty( - self, - param_env, - ty::AliasTy::new_from_args(tcx, tcx.lang_items().deref_target()?, trait_ref.args), - cause.clone(), - 0, - // We're *intentionally* throwing these away, - // since we don't actually use them. - &mut PredicateObligations::new(), - ) - .as_type() - .unwrap(); - - if let ty::Dynamic(data, ..) = ty.kind() { data.principal() } else { None } - }) - } - /// Searches for unsizing that might apply to `obligation`. fn assemble_candidates_for_unsizing( &mut self, @@ -971,11 +925,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // T: Trait // so it seems ok if we (conservatively) fail to accept that `Unsize` // obligation above. Should be possible to extend this in the future. - let Some(source) = obligation.self_ty().no_bound_vars() else { + let Some(trait_pred) = obligation.predicate.no_bound_vars() else { // Don't add any candidates if there are bound regions. return; }; - let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1); + let source = trait_pred.self_ty(); + let target = trait_pred.trait_ref.args.type_at(1); debug!(?source, ?target, "assemble_candidates_for_unsizing"); @@ -1002,8 +957,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let a_auto_traits: FxIndexSet = a_data .auto_traits() .chain(principal_def_id_a.into_iter().flat_map(|principal_def_id| { - self.tcx() - .supertrait_def_ids(principal_def_id) + elaborate::supertrait_def_ids(self.tcx(), principal_def_id) .filter(|def_id| self.tcx().trait_is_auto(*def_id)) })) .collect(); @@ -1019,15 +973,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let principal_a = a_data.principal().unwrap(); let target_trait_did = principal_def_id_b.unwrap(); let source_trait_ref = principal_a.with_self_ty(self.tcx(), source); - if let Some(deref_trait_ref) = self.need_migrate_deref_output_trait_object( - source, - obligation.param_env, - &obligation.cause, - ) { - if deref_trait_ref.def_id() == target_trait_did { - return; - } - } for (idx, upcast_trait_ref) in util::supertraits(self.tcx(), source_trait_ref).enumerate() @@ -1244,4 +1189,48 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } } + + fn assemble_candidates_for_bikeshed_guaranteed_no_drop_trait( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + match obligation.predicate.self_ty().skip_binder().kind() { + ty::Ref(..) + | ty::Adt(..) + | ty::Tuple(_) + | ty::Array(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Error(_) + | ty::Uint(_) + | ty::Int(_) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Pat(..) + | ty::Dynamic(..) + | ty::Str + | ty::Slice(_) + | ty::Foreign(..) + | ty::Alias(..) + | ty::Param(_) + | ty::Placeholder(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::UnsafeBinder(_) + | ty::CoroutineWitness(..) + | ty::Bound(..) => { + candidates.vec.push(BikeshedGuaranteedNoDropCandidate); + } + + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + candidates.ambiguous = true; + } + } + } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 3619d16cde248..32cbb97e314d6 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -16,10 +16,11 @@ use rustc_hir::lang_items::LangItem; use rustc_infer::infer::{DefineOpaqueTypes, HigherRankedType, InferOk}; use rustc_infer::traits::ObligationCauseCode; use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData}; -use rustc_middle::ty::{self, GenericArgsRef, ToPolyTraitRef, Ty, TyCtxt, Upcast}; +use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, Upcast}; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::DefId; use rustc_type_ir::elaborate; +use thin_vec::thin_vec; use tracing::{debug, instrument}; use super::SelectionCandidate::{self, *}; @@ -130,6 +131,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { TraitUpcastingUnsizeCandidate(idx) => { self.confirm_trait_upcasting_unsize_candidate(obligation, idx)? } + + BikeshedGuaranteedNoDropCandidate => { + self.confirm_bikeshed_guaranteed_no_drop_candidate(obligation) + } }; // The obligations returned by confirmation are recursively evaluated @@ -301,11 +306,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let make_transmute_obl = |src, dst| { let transmute_trait = obligation.predicate.def_id(); let assume = obligation.predicate.skip_binder().trait_ref.args.const_at(2); - let trait_ref = ty::TraitRef::new(tcx, transmute_trait, [ - ty::GenericArg::from(dst), - ty::GenericArg::from(src), - ty::GenericArg::from(assume), - ]); + let trait_ref = ty::TraitRef::new( + tcx, + transmute_trait, + [ + ty::GenericArg::from(dst), + ty::GenericArg::from(src), + ty::GenericArg::from(assume), + ], + ); Obligation::with_depth( tcx, obligation.cause.clone(), @@ -316,10 +325,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; let make_freeze_obl = |ty| { - let trait_ref = - ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Freeze, None), [ - ty::GenericArg::from(ty), - ]); + let trait_ref = ty::TraitRef::new( + tcx, + tcx.require_lang_item(LangItem::Freeze, None), + [ty::GenericArg::from(ty)], + ); Obligation::with_depth( tcx, obligation.cause.clone(), @@ -458,8 +468,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ensure_sufficient_stack(|| { let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived); - let poly_trait_ref = obligation.predicate.to_poly_trait_ref(); - let trait_ref = self.infcx.enter_forall_and_leak_universe(poly_trait_ref); + assert_eq!(obligation.predicate.polarity(), ty::PredicatePolarity::Positive); + let trait_ref = + self.infcx.enter_forall_and_leak_universe(obligation.predicate).trait_ref; let trait_obligations = self.impl_or_trait_obligations( &cause, obligation.recursion_depth + 1, @@ -862,10 +873,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::CoroutineClosure(_, args) => { args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| { - ty::TraitRef::new(self.tcx(), obligation.predicate.def_id(), [ - self_ty, - sig.tupled_inputs_ty, - ]) + ty::TraitRef::new( + self.tcx(), + obligation.predicate.def_id(), + [self_ty, sig.tupled_inputs_ty], + ) }) } _ => { @@ -891,10 +903,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::CoroutineClosure(_, args) => { let args = args.as_coroutine_closure(); let trait_ref = args.coroutine_closure_sig().map_bound(|sig| { - ty::TraitRef::new(self.tcx(), obligation.predicate.def_id(), [ - self_ty, - sig.tupled_inputs_ty, - ]) + ty::TraitRef::new( + self.tcx(), + obligation.predicate.def_id(), + [self_ty, sig.tupled_inputs_ty], + ) }); // Note that unlike below, we don't need to check `Future + Sized` for @@ -905,10 +918,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::FnDef(..) | ty::FnPtr(..) => { let sig = self_ty.fn_sig(tcx); let trait_ref = sig.map_bound(|sig| { - ty::TraitRef::new(self.tcx(), obligation.predicate.def_id(), [ - self_ty, - Ty::new_tup(tcx, sig.inputs()), - ]) + ty::TraitRef::new( + self.tcx(), + obligation.predicate.def_id(), + [self_ty, Ty::new_tup(tcx, sig.inputs())], + ) }); // We must additionally check that the return type impls `Future + Sized`. @@ -933,10 +947,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let args = args.as_closure(); let sig = args.sig(); let trait_ref = sig.map_bound(|sig| { - ty::TraitRef::new(self.tcx(), obligation.predicate.def_id(), [ - self_ty, - sig.inputs()[0], - ]) + ty::TraitRef::new( + self.tcx(), + obligation.predicate.def_id(), + [self_ty, sig.inputs()[0]], + ) }); // We must additionally check that the return type impls `Future + Sized`. @@ -1090,7 +1105,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { )? .expect("did not expect ambiguity during confirmation"); - Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting, nested)) + Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting(idx), nested)) } fn confirm_builtin_unsize_candidate( @@ -1294,10 +1309,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Construct the nested `TailField: Unsize>` predicate. let tail_unsize_obligation = obligation.with( tcx, - ty::TraitRef::new(tcx, obligation.predicate.def_id(), [ - source_tail, - target_tail, - ]), + ty::TraitRef::new( + tcx, + obligation.predicate.def_id(), + [source_tail, target_tail], + ), ); nested.push(tail_unsize_obligation); @@ -1335,6 +1351,93 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => bug!("source: {source}, target: {target}"), }) } + + fn confirm_bikeshed_guaranteed_no_drop_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + ) -> ImplSource<'tcx, PredicateObligation<'tcx>> { + let mut obligations = thin_vec![]; + + let tcx = self.tcx(); + let self_ty = obligation.predicate.self_ty(); + match *self_ty.skip_binder().kind() { + // `&mut T` and `&T` always implement `BikeshedGuaranteedNoDrop`. + ty::Ref(..) => {} + // `ManuallyDrop` always implements `BikeshedGuaranteedNoDrop`. + ty::Adt(def, _) if def.is_manually_drop() => {} + // Arrays and tuples implement `BikeshedGuaranteedNoDrop` only if + // their constituent types implement `BikeshedGuaranteedNoDrop`. + ty::Tuple(tys) => { + obligations.extend(tys.iter().map(|elem_ty| { + obligation.with( + tcx, + self_ty.rebind(ty::TraitRef::new( + tcx, + obligation.predicate.def_id(), + [elem_ty], + )), + ) + })); + } + ty::Array(elem_ty, _) => { + obligations.push(obligation.with( + tcx, + self_ty.rebind(ty::TraitRef::new( + tcx, + obligation.predicate.def_id(), + [elem_ty], + )), + )); + } + + // All other types implement `BikeshedGuaranteedNoDrop` only if + // they implement `Copy`. We could be smart here and short-circuit + // some trivially `Copy`/`!Copy` types, but there's no benefit. + ty::FnDef(..) + | ty::FnPtr(..) + | ty::Error(_) + | ty::Uint(_) + | ty::Int(_) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Pat(..) + | ty::Dynamic(..) + | ty::Str + | ty::Slice(_) + | ty::Foreign(..) + | ty::Adt(..) + | ty::Alias(..) + | ty::Param(_) + | ty::Placeholder(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::UnsafeBinder(_) + | ty::CoroutineWitness(..) + | ty::Bound(..) => { + obligations.push(obligation.with( + tcx, + self_ty.map_bound(|ty| { + ty::TraitRef::new( + tcx, + tcx.require_lang_item(LangItem::Copy, Some(obligation.cause.span)), + [ty], + ) + }), + )); + } + + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + panic!("unexpected type `{self_ty:?}`") + } + } + + ImplSource::Builtin(BuiltinImplSource::Misc, obligations) + } } /// Compute a goal that some RPITIT (right now, only RPITITs corresponding to Futures) @@ -1358,10 +1461,11 @@ fn pointer_like_goal_for_rpitit<'tcx>( ty::GenericParamDefKind::Lifetime => { let kind = ty::BoundRegionKind::Named(arg.def_id, tcx.item_name(arg.def_id)); bound_vars.push(ty::BoundVariableKind::Region(kind)); - ty::Region::new_bound(tcx, ty::INNERMOST, ty::BoundRegion { - var: ty::BoundVar::from_usize(bound_vars.len() - 1), - kind, - }) + ty::Region::new_bound( + tcx, + ty::INNERMOST, + ty::BoundRegion { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind }, + ) .into() } ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Const { .. } => { @@ -1370,9 +1474,11 @@ fn pointer_like_goal_for_rpitit<'tcx>( }); ty::Binder::bind_with_vars( - ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::PointerLike, Some(cause.span)), [ - Ty::new_projection_from_args(tcx, rpitit_item, args), - ]), + ty::TraitRef::new( + tcx, + tcx.require_lang_item(LangItem::PointerLike, Some(cause.span)), + [Ty::new_projection_from_args(tcx, rpitit_item, args)], + ), tcx.mk_bound_variable_kinds(&bound_vars), ) } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 9e7da5eb3689c..436ce3dddd9f0 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -32,6 +32,7 @@ use rustc_middle::ty::{ TypingMode, Upcast, }; use rustc_span::{Symbol, sym}; +use rustc_type_ir::elaborate; use tracing::{debug, instrument, trace}; use self::EvaluationResult::*; @@ -961,7 +962,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return Ok(EvaluatedToAmbig); } ty::ConstKind::Error(_) => return Ok(EvaluatedToOk), - ty::ConstKind::Value(ty, _) => ty, + ty::ConstKind::Value(cv) => cv.ty, ty::ConstKind::Unevaluated(uv) => { self.tcx().type_of(uv.def).instantiate(self.tcx(), uv.args) } @@ -1619,9 +1620,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // projections, we will never be able to equate, e.g. `::A` // with `<::A as Tr>::A`. let relevant_bounds = if in_parent_alias_type { - self.tcx().item_non_self_assumptions(alias_ty.def_id) + self.tcx().item_non_self_bounds(alias_ty.def_id) } else { - self.tcx().item_super_predicates(alias_ty.def_id) + self.tcx().item_self_bounds(alias_ty.def_id) }; for bound in relevant_bounds.instantiate(self.tcx(), alias_ty.args) { @@ -1895,6 +1896,18 @@ impl<'tcx> SelectionContext<'_, 'tcx> { Some(None) => {} None => return None, } + // Same for upcasting. + let upcast_bound = candidates + .iter() + .filter_map(|c| { + if let TraitUpcastingUnsizeCandidate(i) = c.candidate { Some(i) } else { None } + }) + .try_reduce(|c1, c2| if has_non_region_infer { None } else { Some(c1.min(c2)) }); + match upcast_bound { + Some(Some(index)) => return Some(TraitUpcastingUnsizeCandidate(index)), + Some(None) => {} + None => return None, + } // Finally, handle overlapping user-written impls. let impls = candidates.iter().filter_map(|c| { @@ -1936,7 +1949,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | TraitAliasCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinObjectCandidate - | BuiltinUnsizeCandidate => false, + | BuiltinUnsizeCandidate + | BikeshedGuaranteedNoDropCandidate => false, // Non-global param candidates have already been handled, global // where-bounds get ignored. ParamCandidate(_) | ImplCandidate(_) => true, @@ -2409,9 +2423,11 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } else { // If this is an ill-formed auto/built-in trait, then synthesize // new error args for the missing generics. - let err_args = ty::GenericArgs::extend_with_error(tcx, trait_def_id, &[ - normalized_ty.into(), - ]); + let err_args = ty::GenericArgs::extend_with_error( + tcx, + trait_def_id, + &[normalized_ty.into()], + ); ty::TraitRef::new_from_args(tcx, trait_def_id, err_args) }; @@ -2519,7 +2535,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> { let a_auto_traits: FxIndexSet = a_data .auto_traits() .chain(a_data.principal_def_id().into_iter().flat_map(|principal_def_id| { - tcx.supertrait_def_ids(principal_def_id).filter(|def_id| tcx.trait_is_auto(*def_id)) + elaborate::supertrait_def_ids(tcx, principal_def_id) + .filter(|def_id| tcx.trait_is_auto(*def_id)) })) .collect(); diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 401b41c796dad..cb3e81f5477fc 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -575,7 +575,7 @@ fn report_conflicting_impls<'tcx>( match used_to_be_allowed { None => { let reported = if overlap.with_impl.is_local() - || tcx.ensure().orphan_check_impl(impl_def_id).is_ok() + || tcx.ensure_ok().orphan_check_impl(impl_def_id).is_ok() { let mut err = tcx.dcx().struct_span_err(impl_span, msg()); err.code(E0119); diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs index 13620f4b8d9cc..f39c611d19fe0 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -376,6 +376,12 @@ pub(crate) fn assoc_def( // If there is no such item in that impl, this function will fail with a // cycle error if the specialization graph is currently being built. if let Some(&impl_item_id) = tcx.impl_item_implementor_ids(impl_def_id).get(&assoc_def_id) { + // Ensure that the impl is constrained, otherwise projection may give us + // bad unconstrained infer vars. + if let Some(impl_def_id) = impl_def_id.as_local() { + tcx.ensure_ok().enforce_impl_non_lifetime_params_are_constrained(impl_def_id)?; + } + let item = tcx.associated_item(impl_item_id); let impl_node = Node::Impl(impl_def_id); return Ok(LeafDef { @@ -391,6 +397,14 @@ pub(crate) fn assoc_def( let ancestors = trait_def.ancestors(tcx, impl_def_id)?; if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_def_id) { + // Ensure that the impl is constrained, otherwise projection may give us + // bad unconstrained infer vars. + if assoc_item.item.container == ty::AssocItemContainer::Impl + && let Some(impl_def_id) = assoc_item.item.container_id(tcx).as_local() + { + tcx.ensure_ok().enforce_impl_non_lifetime_params_are_constrained(impl_def_id)?; + } + Ok(assoc_item) } else { // This is saying that neither the trait nor diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs index f8e8f2176c182..e6d5d336b8d55 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs @@ -7,44 +7,12 @@ use crate::traits::{NormalizeExt, Obligation}; #[extension(pub trait StructurallyNormalizeExt<'tcx>)] impl<'tcx> At<'_, 'tcx> { - fn structurally_normalize( + fn structurally_normalize_ty( &self, ty: Ty<'tcx>, fulfill_cx: &mut dyn TraitEngine<'tcx, E>, ) -> Result, Vec> { - assert!(!ty.is_ty_var(), "should have resolved vars before calling"); - - if self.infcx.next_trait_solver() { - let ty::Alias(..) = *ty.kind() else { - return Ok(ty); - }; - - let new_infer_ty = self.infcx.next_ty_var(self.cause.span); - - // We simply emit an `alias-eq` goal here, since that will take care of - // normalizing the LHS of the projection until it is a rigid projection - // (or a not-yet-defined opaque in scope). - let obligation = Obligation::new( - self.infcx.tcx, - self.cause.clone(), - self.param_env, - ty::PredicateKind::AliasRelate( - ty.into(), - new_infer_ty.into(), - ty::AliasRelationDirection::Equate, - ), - ); - - fulfill_cx.register_predicate_obligation(self.infcx, obligation); - let errors = fulfill_cx.select_where_possible(self.infcx); - if !errors.is_empty() { - return Err(errors); - } - - Ok(self.infcx.resolve_vars_if_possible(new_infer_ty)) - } else { - Ok(self.normalize(ty).into_value_registering_obligations(self.infcx, fulfill_cx)) - } + self.structurally_normalize_term(ty.into(), fulfill_cx).map(|term| term.expect_type()) } fn structurally_normalize_const( @@ -52,14 +20,29 @@ impl<'tcx> At<'_, 'tcx> { ct: ty::Const<'tcx>, fulfill_cx: &mut dyn TraitEngine<'tcx, E>, ) -> Result, Vec> { - assert!(!ct.is_ct_infer(), "should have resolved vars before calling"); + if self.infcx.tcx.features().generic_const_exprs() { + return Ok(super::evaluate_const(&self.infcx, ct, self.param_env)); + } + + self.structurally_normalize_term(ct.into(), fulfill_cx).map(|term| term.expect_const()) + } + + fn structurally_normalize_term( + &self, + term: ty::Term<'tcx>, + fulfill_cx: &mut dyn TraitEngine<'tcx, E>, + ) -> Result, Vec> { + assert!(!term.is_infer(), "should have resolved vars before calling"); if self.infcx.next_trait_solver() { - let ty::ConstKind::Unevaluated(..) = ct.kind() else { - return Ok(ct); - }; + if let None = term.to_alias_term() { + return Ok(term); + } - let new_infer_ct = self.infcx.next_const_var(self.cause.span); + let new_infer = match term.unpack() { + ty::TermKind::Ty(_) => self.infcx.next_ty_var(self.cause.span).into(), + ty::TermKind::Const(_) => self.infcx.next_const_var(self.cause.span).into(), + }; // We simply emit an `alias-eq` goal here, since that will take care of // normalizing the LHS of the projection until it is a rigid projection @@ -68,11 +51,7 @@ impl<'tcx> At<'_, 'tcx> { self.infcx.tcx, self.cause.clone(), self.param_env, - ty::PredicateKind::AliasRelate( - ct.into(), - new_infer_ct.into(), - ty::AliasRelationDirection::Equate, - ), + ty::PredicateKind::AliasRelate(term, new_infer, ty::AliasRelationDirection::Equate), ); fulfill_cx.register_predicate_obligation(self.infcx, obligation); @@ -81,11 +60,9 @@ impl<'tcx> At<'_, 'tcx> { return Err(errors); } - Ok(self.infcx.resolve_vars_if_possible(new_infer_ct)) - } else if self.infcx.tcx.features().generic_const_exprs() { - Ok(super::evaluate_const(&self.infcx, ct, self.param_env)) + Ok(self.infcx.resolve_vars_if_possible(new_infer)) } else { - Ok(self.normalize(ct).into_value_registering_obligations(self.infcx, fulfill_cx)) + Ok(self.normalize(term).into_value_registering_obligations(self.infcx, fulfill_cx)) } } } diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index da1045b664afc..15f5cf916a48b 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -1,162 +1,85 @@ -use std::collections::BTreeMap; +use std::collections::{BTreeMap, VecDeque}; -use rustc_data_structures::fx::FxIndexMap; -use rustc_errors::Diag; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_hir::def_id::DefId; use rustc_infer::infer::InferCtxt; pub use rustc_infer::traits::util::*; use rustc_middle::bug; use rustc_middle::ty::{ - self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, Upcast, + self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; use rustc_span::Span; use smallvec::{SmallVec, smallvec}; use tracing::debug; -/////////////////////////////////////////////////////////////////////////// -// `TraitAliasExpander` iterator -/////////////////////////////////////////////////////////////////////////// - -/// "Trait alias expansion" is the process of expanding a sequence of trait -/// references into another sequence by transitively following all trait -/// aliases. e.g. If you have bounds like `Foo + Send`, a trait alias -/// `trait Foo = Bar + Sync;`, and another trait alias -/// `trait Bar = Read + Write`, then the bounds would expand to -/// `Read + Write + Sync + Send`. -/// Expansion is done via a DFS (depth-first search), and the `visited` field -/// is used to avoid cycles. -pub struct TraitAliasExpander<'tcx> { - tcx: TyCtxt<'tcx>, - stack: Vec>, -} - -/// Stores information about the expansion of a trait via a path of zero or more trait aliases. -#[derive(Debug, Clone)] -pub struct TraitAliasExpansionInfo<'tcx> { - pub path: SmallVec<[(ty::PolyTraitRef<'tcx>, Span); 4]>, -} - -impl<'tcx> TraitAliasExpansionInfo<'tcx> { - fn new(trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> Self { - Self { path: smallvec![(trait_ref, span)] } - } - - /// Adds diagnostic labels to `diag` for the expansion path of a trait through all intermediate - /// trait aliases. - pub fn label_with_exp_info( - &self, - diag: &mut Diag<'_>, - top_label: &'static str, - use_desc: &str, - ) { - diag.span_label(self.top().1, top_label); - if self.path.len() > 1 { - for (_, sp) in self.path.iter().rev().skip(1).take(self.path.len() - 2) { - diag.span_label(*sp, format!("referenced here ({use_desc})")); - } - } - if self.top().1 != self.bottom().1 { - // When the trait object is in a return type these two spans match, we don't want - // redundant labels. - diag.span_label( - self.bottom().1, - format!("trait alias used in trait object type ({use_desc})"), - ); - } - } - - pub fn trait_ref(&self) -> ty::PolyTraitRef<'tcx> { - self.top().0 - } - - pub fn top(&self) -> &(ty::PolyTraitRef<'tcx>, Span) { - self.path.last().unwrap() - } - - pub fn bottom(&self) -> &(ty::PolyTraitRef<'tcx>, Span) { - self.path.first().unwrap() - } - - fn clone_and_push(&self, trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> Self { - let mut path = self.path.clone(); - path.push((trait_ref, span)); - - Self { path } - } -} - +/// Return the trait and projection predicates that come from eagerly expanding the +/// trait aliases in the list of clauses. For each trait predicate, record a stack +/// of spans that trace from the user-written trait alias bound. For projection predicates, +/// just record the span of the projection itself. +/// +/// For trait aliases, we don't deduplicte the predicates, since we currently do not +/// consider duplicated traits as a single trait for the purposes of our "one trait principal" +/// restriction; however, for projections we do deduplicate them. +/// +/// ```rust,ignore (fails) +/// trait Bar {} +/// trait Foo = Bar + Bar; +/// +/// let dyn_incompatible: dyn Foo; // bad, two `Bar` principals. +/// ``` pub fn expand_trait_aliases<'tcx>( tcx: TyCtxt<'tcx>, - trait_refs: impl Iterator, Span)>, -) -> TraitAliasExpander<'tcx> { - let items: Vec<_> = - trait_refs.map(|(trait_ref, span)| TraitAliasExpansionInfo::new(trait_ref, span)).collect(); - TraitAliasExpander { tcx, stack: items } -} - -impl<'tcx> TraitAliasExpander<'tcx> { - /// If `item` is a trait alias and its predicate has not yet been visited, then expands `item` - /// to the definition, pushes the resulting expansion onto `self.stack`, and returns `false`. - /// Otherwise, immediately returns `true` if `item` is a regular trait, or `false` if it is a - /// trait alias. - /// The return value indicates whether `item` should be yielded to the user. - fn expand(&mut self, item: &TraitAliasExpansionInfo<'tcx>) -> bool { - let tcx = self.tcx; - let trait_ref = item.trait_ref(); - let pred = trait_ref.upcast(tcx); - - debug!("expand_trait_aliases: trait_ref={:?}", trait_ref); - - // Don't recurse if this bound is not a trait alias. - let is_alias = tcx.is_trait_alias(trait_ref.def_id()); - if !is_alias { - return true; - } - - // Don't recurse if this trait alias is already on the stack for the DFS search. - let anon_pred = anonymize_predicate(tcx, pred); - if item - .path - .iter() - .rev() - .skip(1) - .any(|&(tr, _)| anonymize_predicate(tcx, tr.upcast(tcx)) == anon_pred) - { - return false; - } - - // Get components of trait alias. - let predicates = tcx.explicit_super_predicates_of(trait_ref.def_id()); - debug!(?predicates); - - let items = predicates.skip_binder().iter().rev().filter_map(|(pred, span)| { - pred.instantiate_supertrait(tcx, trait_ref) - .as_trait_clause() - .map(|trait_ref| item.clone_and_push(trait_ref.map_bound(|t| t.trait_ref), *span)) - }); - debug!("expand_trait_aliases: items={:?}", items.clone().collect::>()); - - self.stack.extend(items); - - false - } -} - -impl<'tcx> Iterator for TraitAliasExpander<'tcx> { - type Item = TraitAliasExpansionInfo<'tcx>; - - fn size_hint(&self) -> (usize, Option) { - (self.stack.len(), None) - } - - fn next(&mut self) -> Option> { - while let Some(item) = self.stack.pop() { - if self.expand(&item) { - return Some(item); + clauses: impl IntoIterator, Span)>, +) -> ( + Vec<(ty::PolyTraitPredicate<'tcx>, SmallVec<[Span; 1]>)>, + Vec<(ty::PolyProjectionPredicate<'tcx>, Span)>, +) { + let mut trait_preds = vec![]; + let mut projection_preds = vec![]; + let mut seen_projection_preds = FxHashSet::default(); + + let mut queue: VecDeque<_> = clauses.into_iter().map(|(p, s)| (p, smallvec![s])).collect(); + + while let Some((clause, spans)) = queue.pop_front() { + match clause.kind().skip_binder() { + ty::ClauseKind::Trait(trait_pred) => { + if tcx.is_trait_alias(trait_pred.def_id()) { + queue.extend( + tcx.explicit_super_predicates_of(trait_pred.def_id()) + .iter_identity_copied() + .map(|(super_clause, span)| { + let mut spans = spans.clone(); + spans.push(span); + ( + super_clause.instantiate_supertrait( + tcx, + clause.kind().rebind(trait_pred.trait_ref), + ), + spans, + ) + }), + ); + } else { + trait_preds.push((clause.kind().rebind(trait_pred), spans)); + } } + ty::ClauseKind::Projection(projection_pred) => { + let projection_pred = clause.kind().rebind(projection_pred); + if !seen_projection_preds.insert(tcx.anonymize_bound_vars(projection_pred)) { + continue; + } + projection_preds.push((projection_pred, *spans.last().unwrap())); + } + ty::ClauseKind::RegionOutlives(..) + | ty::ClauseKind::TypeOutlives(..) + | ty::ClauseKind::ConstArgHasType(_, _) + | ty::ClauseKind::WellFormed(_) + | ty::ClauseKind::ConstEvaluatable(_) + | ty::ClauseKind::HostEffect(..) => {} } - None } + + (trait_preds, projection_preds) } /////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index b5bc8364c7b69..0024316b877c5 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -2,26 +2,22 @@ use std::fmt::Debug; use std::ops::ControlFlow; use rustc_hir::def_id::DefId; -use rustc_infer::infer::at::ToTrace; -use rustc_infer::infer::{BoundRegionConversionTime, TyCtxtInferExt}; -use rustc_infer::traits::ObligationCause; use rustc_infer::traits::util::PredicateSet; use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::ty::{ self, GenericArgs, GenericParamDefKind, Ty, TyCtxt, TypeVisitableExt, Upcast, VtblEntry, }; -use rustc_span::{DUMMY_SP, Span, sym}; +use rustc_span::DUMMY_SP; use smallvec::{SmallVec, smallvec}; use tracing::debug; -use crate::errors::DumpVTableEntries; -use crate::traits::{ObligationCtxt, impossible_predicates, is_vtable_safe_method}; +use crate::traits::{impossible_predicates, is_vtable_safe_method}; #[derive(Clone, Debug)] pub enum VtblSegment<'tcx> { MetadataDSA, - TraitOwnEntries { trait_ref: ty::PolyTraitRef<'tcx>, emit_vptr: bool }, + TraitOwnEntries { trait_ref: ty::TraitRef<'tcx>, emit_vptr: bool }, } /// Prepare the segments for a vtable @@ -29,7 +25,7 @@ pub enum VtblSegment<'tcx> { // about our `Self` type here. pub fn prepare_vtable_segments<'tcx, T>( tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_ref: ty::TraitRef<'tcx>, segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow, ) -> Option { prepare_vtable_segments_inner(tcx, trait_ref, segment_visitor).break_value() @@ -39,7 +35,7 @@ pub fn prepare_vtable_segments<'tcx, T>( /// such that we can use `?` in the body. fn prepare_vtable_segments_inner<'tcx, T>( tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_ref: ty::TraitRef<'tcx>, mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow, ) -> ControlFlow { // The following constraints holds for the final arrangement. @@ -92,7 +88,7 @@ fn prepare_vtable_segments_inner<'tcx, T>( let mut emit_vptr_on_new_entry = false; let mut visited = PredicateSet::new(tcx); let predicate = trait_ref.upcast(tcx); - let mut stack: SmallVec<[(ty::PolyTraitRef<'tcx>, _, _); 5]> = + let mut stack: SmallVec<[(ty::TraitRef<'tcx>, _, _); 5]> = smallvec![(trait_ref, emit_vptr_on_new_entry, maybe_iter(None))]; visited.insert(predicate); @@ -125,10 +121,18 @@ fn prepare_vtable_segments_inner<'tcx, T>( let &(inner_most_trait_ref, _, _) = stack.last().unwrap(); let mut direct_super_traits_iter = tcx - .explicit_super_predicates_of(inner_most_trait_ref.def_id()) + .explicit_super_predicates_of(inner_most_trait_ref.def_id) .iter_identity_copied() .filter_map(move |(pred, _)| { - pred.instantiate_supertrait(tcx, inner_most_trait_ref).as_trait_clause() + pred.instantiate_supertrait(tcx, ty::Binder::dummy(inner_most_trait_ref)) + .as_trait_clause() + }) + .map(move |pred| { + tcx.normalize_erasing_late_bound_regions( + ty::TypingEnv::fully_monomorphized(), + pred, + ) + .trait_ref }); // Find an unvisited supertrait @@ -136,16 +140,11 @@ fn prepare_vtable_segments_inner<'tcx, T>( .find(|&super_trait| visited.insert(super_trait.upcast(tcx))) { // Push it to the stack for the next iteration of 'diving_in to pick up - Some(unvisited_super_trait) => { - // We're throwing away potential constness of super traits here. - // FIXME: handle ~const super traits - let next_super_trait = unvisited_super_trait.map_bound(|t| t.trait_ref); - stack.push(( - next_super_trait, - emit_vptr_on_new_entry, - maybe_iter(Some(direct_super_traits_iter)), - )) - } + Some(next_super_trait) => stack.push(( + next_super_trait, + emit_vptr_on_new_entry, + maybe_iter(Some(direct_super_traits_iter)), + )), // There are no more unvisited direct super traits, dive-in finished None => break 'diving_in, @@ -154,8 +153,7 @@ fn prepare_vtable_segments_inner<'tcx, T>( // emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level. while let Some((inner_most_trait_ref, emit_vptr, mut siblings)) = stack.pop() { - let has_entries = - has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id()); + let has_entries = has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id); segment_visitor(VtblSegment::TraitOwnEntries { trait_ref: inner_most_trait_ref, @@ -169,11 +167,6 @@ fn prepare_vtable_segments_inner<'tcx, T>( if let Some(next_inner_most_trait_ref) = siblings.find(|&sibling| visited.insert(sibling.upcast(tcx))) { - // We're throwing away potential constness of super traits here. - // FIXME: handle ~const super traits - let next_inner_most_trait_ref = - next_inner_most_trait_ref.map_bound(|t| t.trait_ref); - stack.push((next_inner_most_trait_ref, emit_vptr_on_new_entry, siblings)); // just pushed a new trait onto the stack, so we need to go through its super traits @@ -192,15 +185,6 @@ fn maybe_iter(i: Option) -> impl Iterator { i.into_iter().flatten() } -fn dump_vtable_entries<'tcx>( - tcx: TyCtxt<'tcx>, - sp: Span, - trait_ref: ty::PolyTraitRef<'tcx>, - entries: &[VtblEntry<'tcx>], -) { - tcx.dcx().emit_err(DumpVTableEntries { span: sp, trait_ref, entries: format!("{entries:#?}") }); -} - fn has_own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool { own_existential_vtable_entries_iter(tcx, trait_def_id).next().is_some() } @@ -239,8 +223,15 @@ fn own_existential_vtable_entries_iter( /// that come from `trait_ref`, including its supertraits. fn vtable_entries<'tcx>( tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, + trait_ref: ty::TraitRef<'tcx>, ) -> &'tcx [VtblEntry<'tcx>] { + debug_assert!(!trait_ref.has_non_region_infer() && !trait_ref.has_non_region_param()); + debug_assert_eq!( + tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref), + trait_ref, + "vtable trait ref should be normalized" + ); + debug!("vtable_entries({:?})", trait_ref); let mut entries = vec![]; @@ -251,33 +242,26 @@ fn vtable_entries<'tcx>( entries.extend(TyCtxt::COMMON_VTABLE_ENTRIES); } VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { - let existential_trait_ref = trait_ref - .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); + let existential_trait_ref = ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref); // Lookup the shape of vtable for the trait. let own_existential_entries = - tcx.own_existential_vtable_entries(existential_trait_ref.def_id()); + tcx.own_existential_vtable_entries(existential_trait_ref.def_id); let own_entries = own_existential_entries.iter().copied().map(|def_id| { debug!("vtable_entries: trait_method={:?}", def_id); // The method may have some early-bound lifetimes; add regions for those. - let args = trait_ref.map_bound(|trait_ref| { + // FIXME: Is this normalize needed? + let args = tcx.normalize_erasing_regions( + ty::TypingEnv::fully_monomorphized(), GenericArgs::for_item(tcx, def_id, |param, _| match param.kind { GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { trait_ref.args[param.index as usize] } - }) - }); - - // The trait type may have higher-ranked lifetimes in it; - // erase them if they appear, so that we get the type - // at some particular call site. - let args = tcx.normalize_erasing_late_bound_regions( - ty::TypingEnv::fully_monomorphized(), - args, + }), ); // It's possible that the method relies on where-clauses that @@ -317,11 +301,6 @@ fn vtable_entries<'tcx>( let _ = prepare_vtable_segments(tcx, trait_ref, vtable_segment_callback); - if tcx.has_attr(trait_ref.def_id(), sym::rustc_dump_vtable) { - let sp = tcx.def_span(trait_ref.def_id()); - dump_vtable_entries(tcx, sp, trait_ref, &entries); - } - tcx.arena.alloc_from_iter(entries) } @@ -329,14 +308,28 @@ fn vtable_entries<'tcx>( // for `Supertrait`'s methods in the vtable of `Subtrait`. pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRef<'tcx>) -> usize { debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param()); + debug_assert_eq!( + tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), key), + key, + "vtable trait ref should be normalized" + ); let ty::Dynamic(source, _, _) = *key.self_ty().kind() else { bug!(); }; - let source_principal = - source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self); + let source_principal = tcx.instantiate_bound_regions_with_erased( + source.principal().unwrap().with_self_ty(tcx, key.self_ty()), + ); + + // We're monomorphizing a call to a dyn trait object that can never be constructed. + if tcx.instantiate_and_check_impossible_predicates(( + source_principal.def_id, + source_principal.args, + )) { + return 0; + } - let target_principal = ty::Binder::dummy(ty::ExistentialTraitRef::erase_self_ty(tcx, key)); + let target_principal = ty::ExistentialTraitRef::erase_self_ty(tcx, key); let vtable_segment_callback = { let mut vptr_offset = 0; @@ -346,17 +339,14 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len(); } VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => { - if trait_refs_are_compatible( - tcx, - vtable_principal - .map_bound(|t| ty::ExistentialTraitRef::erase_self_ty(tcx, t)), - target_principal, - ) { + if ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal) + == target_principal + { return ControlFlow::Break(vptr_offset); } vptr_offset += - tcx.own_existential_vtable_entries(vtable_principal.def_id()).len(); + tcx.own_existential_vtable_entries(vtable_principal.def_id).len(); if emit_vptr { vptr_offset += 1; @@ -382,20 +372,35 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( ), ) -> Option { debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param()); + debug_assert_eq!( + tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), key), + key, + "upcasting trait refs should be normalized" + ); + let (source, target) = key; // If the target principal is `None`, we can just return `None`. - let ty::Dynamic(target, _, _) = *target.kind() else { + let ty::Dynamic(target_data, _, _) = *target.kind() else { bug!(); }; - let target_principal = target.principal()?; + let target_principal = tcx.instantiate_bound_regions_with_erased(target_data.principal()?); // Given that we have a target principal, it is a bug for there not to be a source principal. - let ty::Dynamic(source, _, _) = *source.kind() else { + let ty::Dynamic(source_data, _, _) = *source.kind() else { bug!(); }; - let source_principal = - source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self); + let source_principal = tcx.instantiate_bound_regions_with_erased( + source_data.principal().unwrap().with_self_ty(tcx, source), + ); + + // We're monomorphizing a dyn trait object upcast that can never be constructed. + if tcx.instantiate_and_check_impossible_predicates(( + source_principal.def_id, + source_principal.args, + )) { + return None; + } let vtable_segment_callback = { let mut vptr_offset = 0; @@ -406,13 +411,10 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( } VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => { vptr_offset += - tcx.own_existential_vtable_entries(vtable_principal.def_id()).len(); - if trait_refs_are_compatible( - tcx, - vtable_principal - .map_bound(|t| ty::ExistentialTraitRef::erase_self_ty(tcx, t)), - target_principal, - ) { + tcx.own_existential_vtable_entries(vtable_principal.def_id).len(); + if ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal) + == target_principal + { if emit_vptr { return ControlFlow::Break(Some(vptr_offset)); } else { @@ -432,41 +434,6 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap() } -fn trait_refs_are_compatible<'tcx>( - tcx: TyCtxt<'tcx>, - hr_vtable_principal: ty::PolyExistentialTraitRef<'tcx>, - hr_target_principal: ty::PolyExistentialTraitRef<'tcx>, -) -> bool { - if hr_vtable_principal.def_id() != hr_target_principal.def_id() { - return false; - } - - let (infcx, param_env) = - tcx.infer_ctxt().build_with_typing_env(ty::TypingEnv::fully_monomorphized()); - let ocx = ObligationCtxt::new(&infcx); - let hr_source_principal = - ocx.normalize(&ObligationCause::dummy(), param_env, hr_vtable_principal); - let hr_target_principal = - ocx.normalize(&ObligationCause::dummy(), param_env, hr_target_principal); - infcx.enter_forall(hr_target_principal, |target_principal| { - let source_principal = infcx.instantiate_binder_with_fresh_vars( - DUMMY_SP, - BoundRegionConversionTime::HigherRankedType, - hr_source_principal, - ); - let Ok(()) = ocx.eq_trace( - &ObligationCause::dummy(), - param_env, - ToTrace::to_trace(&ObligationCause::dummy(), hr_target_principal, hr_source_principal), - target_principal, - source_principal, - ) else { - return false; - }; - ocx.select_all_or_error().is_empty() - }) -} - pub(super) fn provide(providers: &mut Providers) { *providers = Providers { own_existential_vtable_entries, diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 9d32eb0538606..2f72b44f6b62f 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -8,8 +8,9 @@ use rustc_middle::ty::{ self, GenericArg, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; -use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; -use rustc_span::{DUMMY_SP, Span}; +use rustc_session::parse::feature_err; +use rustc_span::def_id::{DefId, LocalDefId}; +use rustc_span::{Span, sym}; use tracing::{debug, instrument, trace}; use crate::infer::InferCtxt; @@ -89,6 +90,8 @@ pub fn unnormalized_obligations<'tcx>( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, arg: GenericArg<'tcx>, + span: Span, + body_id: LocalDefId, ) -> Option> { debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg)); @@ -106,8 +109,8 @@ pub fn unnormalized_obligations<'tcx>( let mut wf = WfPredicates { infcx, param_env, - body_id: CRATE_DEF_ID, - span: DUMMY_SP, + body_id, + span, out: PredicateObligations::new(), recursion_depth: 0, item: None, @@ -689,7 +692,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { self.require_sized(subty, ObligationCauseCode::SliceOrArrayElem); // Note that the len being WF is implicitly checked while visiting. // Here we just check that it's of type usize. - let cause = self.cause(ObligationCauseCode::Misc); + let cause = self.cause(ObligationCauseCode::ArrayLen(t)); self.out.push(traits::Obligation::with_depth( tcx, cause, @@ -702,8 +705,47 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { )); } - ty::Pat(subty, _) => { + ty::Pat(subty, pat) => { self.require_sized(subty, ObligationCauseCode::Misc); + match *pat { + ty::PatternKind::Range { start, end, include_end: _ } => { + let mut check = |c| { + let cause = self.cause(ObligationCauseCode::Misc); + self.out.push(traits::Obligation::with_depth( + tcx, + cause.clone(), + self.recursion_depth, + self.param_env, + ty::Binder::dummy(ty::PredicateKind::Clause( + ty::ClauseKind::ConstArgHasType(c, subty), + )), + )); + if !tcx.features().generic_pattern_types() { + if c.has_param() { + if self.span.is_dummy() { + self.tcx().dcx().delayed_bug( + "feature error should be reported elsewhere, too", + ); + } else { + feature_err( + &self.tcx().sess, + sym::generic_pattern_types, + self.span, + "wraparound pattern type ranges cause monomorphization time errors", + ) + .emit(); + } + } + } + }; + if let Some(start) = start { + check(start) + } + if let Some(end) = end { + check(end) + } + } + } } ty::Tuple(tys) => { @@ -828,8 +870,28 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { // Let the visitor iterate into the argument/return // types appearing in the fn signature. } - ty::UnsafeBinder(_) => { - // FIXME(unsafe_binders): We should also recurse into the binder here. + ty::UnsafeBinder(ty) => { + // FIXME(unsafe_binders): For now, we have no way to express + // that a type must be `ManuallyDrop` OR `Copy` (or a pointer). + if !ty.has_escaping_bound_vars() { + self.out.push(traits::Obligation::new( + self.tcx(), + self.cause(ObligationCauseCode::Misc), + self.param_env, + ty.map_bound(|ty| { + ty::TraitRef::new( + self.tcx(), + self.tcx().require_lang_item( + LangItem::BikeshedGuaranteedNoDrop, + Some(self.span), + ), + [ty], + ) + }), + )); + } + + // We recurse into the binder below. } ty::Dynamic(data, r, _) => { diff --git a/compiler/rustc_traits/src/dropck_outlives.rs b/compiler/rustc_traits/src/dropck_outlives.rs index 51e4dbe81b3d0..b3377e15aa791 100644 --- a/compiler/rustc_traits/src/dropck_outlives.rs +++ b/compiler/rustc_traits/src/dropck_outlives.rs @@ -6,6 +6,7 @@ use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::traits::query::{DropckConstraint, DropckOutlivesResult}; use rustc_middle::ty::{self, GenericArgs, TyCtxt}; +use rustc_span::DUMMY_SP; use rustc_trait_selection::infer::InferCtxtBuilderExt; use rustc_trait_selection::traits::query::dropck_outlives::{ compute_dropck_outlives_inner, dtorck_constraint_for_ty_inner, @@ -24,7 +25,7 @@ fn dropck_outlives<'tcx>( debug!("dropck_outlives(goal={:#?})", canonical_goal); tcx.infer_ctxt().enter_canonical_trait_query(&canonical_goal, |ocx, goal| { - compute_dropck_outlives_inner(ocx, goal) + compute_dropck_outlives_inner(ocx, goal, DUMMY_SP) }) } diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index a51eefd908cc7..5f75e242a50fb 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -8,6 +8,7 @@ use rustc_infer::traits::query::OutlivesBound; use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; +use rustc_span::DUMMY_SP; use rustc_trait_selection::infer::InferCtxtBuilderExt; use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::{ compute_implied_outlives_bounds_compat_inner, compute_implied_outlives_bounds_inner, @@ -28,7 +29,7 @@ fn implied_outlives_bounds_compat<'tcx>( > { tcx.infer_ctxt().enter_canonical_trait_query(&goal, |ocx, key| { let (param_env, ImpliedOutlivesBounds { ty }) = key.into_parts(); - compute_implied_outlives_bounds_compat_inner(ocx, param_env, ty) + compute_implied_outlives_bounds_compat_inner(ocx, param_env, ty, DUMMY_SP) }) } @@ -41,6 +42,6 @@ fn implied_outlives_bounds<'tcx>( > { tcx.infer_ctxt().enter_canonical_trait_query(&goal, |ocx, key| { let (param_env, ImpliedOutlivesBounds { ty }) = key.into_parts(); - compute_implied_outlives_bounds_inner(ocx, param_env, ty) + compute_implied_outlives_bounds_inner(ocx, param_env, ty, DUMMY_SP) }) } diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs index 71088a598bb93..e58ad639d206d 100644 --- a/compiler/rustc_traits/src/type_op.rs +++ b/compiler/rustc_traits/src/type_op.rs @@ -5,6 +5,7 @@ use rustc_infer::infer::canonical::{Canonical, CanonicalQueryInput, QueryRespons use rustc_middle::query::Providers; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::{Clause, FnSig, ParamEnvAnd, PolyFnSig, Ty, TyCtxt, TypeFoldable}; +use rustc_span::DUMMY_SP; use rustc_trait_selection::infer::InferCtxtBuilderExt; use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; use rustc_trait_selection::traits::query::type_op::ascribe_user_type::{ @@ -31,7 +32,7 @@ fn type_op_ascribe_user_type<'tcx>( canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, AscribeUserType<'tcx>>>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, NoSolution> { tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, |ocx, key| { - type_op_ascribe_user_type_with_span(ocx, key, None) + type_op_ascribe_user_type_with_span(ocx, key, DUMMY_SP) }) } diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index 6ce9969aefe90..b00585d292212 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -197,6 +197,7 @@ pub(crate) mod rustc { match err { LayoutError::Unknown(..) | LayoutError::ReferencesError(..) + | LayoutError::TooGeneric(..) | LayoutError::NormalizationFailure(..) => Self::UnknownLayout, LayoutError::SizeOverflow(..) => Self::SizeOverflow, LayoutError::Cycle(err) => Self::TypeError(*err), diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs index ad86813c87e29..f9537f708ef8d 100644 --- a/compiler/rustc_transmute/src/lib.rs +++ b/compiler/rustc_transmute/src/lib.rs @@ -84,7 +84,7 @@ mod rustc { use rustc_infer::infer::InferCtxt; use rustc_macros::TypeVisitable; use rustc_middle::traits::ObligationCause; - use rustc_middle::ty::{Const, ParamEnv, Ty, TyCtxt, ValTree}; + use rustc_middle::ty::{Const, ParamEnv, Ty, TyCtxt}; use super::*; @@ -128,36 +128,32 @@ mod rustc { pub fn from_const<'tcx>( tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, - c: Const<'tcx>, + ct: Const<'tcx>, ) -> Option { use rustc_middle::ty::ScalarInt; use rustc_span::sym; - let Some((cv, ty)) = c.try_to_valtree() else { + let Some(cv) = ct.try_to_value() else { return None; }; - let adt_def = ty.ty_adt_def()?; - - assert_eq!( - tcx.require_lang_item(LangItem::TransmuteOpts, None), - adt_def.did(), - "The given `Const` was not marked with the `{}` lang item.", - LangItem::TransmuteOpts.name(), - ); + let adt_def = cv.ty.ty_adt_def()?; + + if !tcx.is_lang_item(adt_def.did(), LangItem::TransmuteOpts) { + tcx.dcx().delayed_bug(format!( + "The given `const` was not marked with the `{}` lang item.", + LangItem::TransmuteOpts.name() + )); + return Some(Self { + alignment: true, + lifetimes: true, + safety: true, + validity: true, + }); + } let variant = adt_def.non_enum_variant(); - let fields = match cv { - ValTree::Branch(branch) => branch, - _ => { - return Some(Self { - alignment: true, - lifetimes: true, - safety: true, - validity: true, - }); - } - }; + let fields = cv.valtree.unwrap_branch(); let get_field = |name| { let (field_idx, _) = variant diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index b63534880d1c4..606f0b5396ee2 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -290,9 +290,10 @@ fn conv_from_spec_abi(tcx: TyCtxt<'_>, abi: ExternAbi, c_variadic: bool) -> Conv Aapcs { .. } => Conv::ArmAapcs, CCmseNonSecureCall => Conv::CCmseNonSecureCall, CCmseNonSecureEntry => Conv::CCmseNonSecureEntry, - PtxKernel => Conv::PtxKernel, + PtxKernel => Conv::GpuKernel, Msp430Interrupt => Conv::Msp430Intr, X86Interrupt => Conv::X86Intr, + GpuKernel => Conv::GpuKernel, AvrInterrupt => Conv::AvrInterrupt, AvrNonBlockingInterrupt => Conv::AvrNonBlockingInterrupt, RiscvInterruptM => Conv::RiscvInterrupt { kind: RiscvInterruptKind::Machine }, @@ -308,15 +309,11 @@ fn fn_abi_of_fn_ptr<'tcx>( query: ty::PseudoCanonicalInput<'tcx, (ty::PolyFnSig<'tcx>, &'tcx ty::List>)>, ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { let ty::PseudoCanonicalInput { typing_env, value: (sig, extra_args) } = query; - - let cx = LayoutCx::new(tcx, typing_env); fn_abi_new_uncached( - &cx, + &LayoutCx::new(tcx, typing_env), tcx.instantiate_bound_regions_with_erased(sig), extra_args, None, - None, - false, ) } @@ -325,19 +322,11 @@ fn fn_abi_of_instance<'tcx>( query: ty::PseudoCanonicalInput<'tcx, (ty::Instance<'tcx>, &'tcx ty::List>)>, ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { let ty::PseudoCanonicalInput { typing_env, value: (instance, extra_args) } = query; - - let sig = fn_sig_for_fn_abi(tcx, instance, typing_env); - - let caller_location = - instance.def.requires_caller_location(tcx).then(|| tcx.caller_location_ty()); - fn_abi_new_uncached( &LayoutCx::new(tcx, typing_env), - sig, + fn_sig_for_fn_abi(tcx, instance, typing_env), extra_args, - caller_location, - Some(instance.def_id()), - matches!(instance.def, ty::InstanceKind::Virtual(..)), + Some(instance), ) } @@ -488,21 +477,16 @@ fn fn_abi_sanity_check<'tcx>( // have to allow it -- but we absolutely shouldn't let any more targets do // that. (Also see .) // - // The unstable abi `PtxKernel` also uses Direct for now. - // It needs to switch to something else before stabilization can happen. - // (See issue: https://github.com/rust-lang/rust/issues/117271) - // - // And finally the unadjusted ABI is ill specified and uses Direct for all - // args, but unfortunately we need it for calling certain LLVM intrinsics. + // The unadjusted ABI also uses Direct for all args and is ill-specified, + // but unfortunately we need it for calling certain LLVM intrinsics. match spec_abi { ExternAbi::Unadjusted => {} - ExternAbi::PtxKernel => {} ExternAbi::C { unwind: _ } if matches!(&*tcx.sess.target.arch, "wasm32" | "wasm64") => {} _ => { panic!( - "`PassMode::Direct` for aggregates only allowed for \"unadjusted\" and \"ptx-kernel\" functions and on wasm\n\ + "`PassMode::Direct` for aggregates only allowed for \"unadjusted\" functions and on wasm\n\ Problematic type: {:#?}", arg.layout, ); @@ -553,19 +537,25 @@ fn fn_abi_sanity_check<'tcx>( fn_arg_sanity_check(cx, fn_abi, spec_abi, &fn_abi.ret); } -// FIXME(eddyb) perhaps group the signature/type-containing (or all of them?) -// arguments of this method, into a separate `struct`. -#[tracing::instrument(level = "debug", skip(cx, caller_location, fn_def_id, force_thin_self_ptr))] +#[tracing::instrument(level = "debug", skip(cx, instance))] fn fn_abi_new_uncached<'tcx>( cx: &LayoutCx<'tcx>, sig: ty::FnSig<'tcx>, extra_args: &[Ty<'tcx>], - caller_location: Option>, - fn_def_id: Option, - // FIXME(eddyb) replace this with something typed, like an `enum`. - force_thin_self_ptr: bool, + instance: Option>, ) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> { let tcx = cx.tcx(); + let (caller_location, determined_fn_def_id, is_virtual_call) = if let Some(instance) = instance + { + let is_virtual_call = matches!(instance.def, ty::InstanceKind::Virtual(..)); + ( + instance.def.requires_caller_location(tcx).then(|| tcx.caller_location_ty()), + if is_virtual_call { None } else { Some(instance.def_id()) }, + is_virtual_call, + ) + } else { + (None, None, false) + }; let sig = tcx.normalize_erasing_regions(cx.typing_env, sig); let conv = conv_from_spec_abi(cx.tcx(), sig.abi, sig.c_variadic); @@ -574,16 +564,11 @@ fn fn_abi_new_uncached<'tcx>( let extra_args = if sig.abi == ExternAbi::RustCall { assert!(!sig.c_variadic && extra_args.is_empty()); - if let Some(input) = sig.inputs().last() { - if let ty::Tuple(tupled_arguments) = input.kind() { - inputs = &sig.inputs()[0..sig.inputs().len() - 1]; - tupled_arguments - } else { - bug!( - "argument to function with \"rust-call\" ABI \ - is not a tuple" - ); - } + if let Some(input) = sig.inputs().last() + && let ty::Tuple(tupled_arguments) = input.kind() + { + inputs = &sig.inputs()[0..sig.inputs().len() - 1]; + tupled_arguments } else { bug!( "argument to function with \"rust-call\" ABI \ @@ -596,7 +581,7 @@ fn fn_abi_new_uncached<'tcx>( }; let is_drop_in_place = - fn_def_id.is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::DropInPlace)); + determined_fn_def_id.is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::DropInPlace)); let arg_of = |ty: Ty<'tcx>, arg_idx: Option| -> Result<_, &'tcx FnAbiError<'tcx>> { let span = tracing::debug_span!("arg_of"); @@ -609,7 +594,7 @@ fn fn_abi_new_uncached<'tcx>( }); let layout = cx.layout_of(ty).map_err(|err| &*tcx.arena.alloc(FnAbiError::Layout(*err)))?; - let layout = if force_thin_self_ptr && arg_idx == Some(0) { + let layout = if is_virtual_call && arg_idx == Some(0) { // Don't pass the vtable, it's not an argument of the virtual fn. // Instead, pass just the data pointer, but give it the type `*const/mut dyn Trait` // or `&/&mut dyn Trait` because this is special-cased elsewhere in codegen @@ -652,9 +637,22 @@ fn fn_abi_new_uncached<'tcx>( c_variadic: sig.c_variadic, fixed_count: inputs.len() as u32, conv, - can_unwind: fn_can_unwind(cx.tcx(), fn_def_id, sig.abi), + can_unwind: fn_can_unwind( + tcx, + // Since `#[rustc_nounwind]` can change unwinding, we cannot infer unwinding by `fn_def_id` for a virtual call. + determined_fn_def_id, + sig.abi, + ), }; - fn_abi_adjust_for_abi(cx, &mut fn_abi, sig.abi, fn_def_id)?; + fn_abi_adjust_for_abi( + cx, + &mut fn_abi, + sig.abi, + // If this is a virtual call, we cannot pass the `fn_def_id`, as it might call other + // functions from vtable. Internally, `deduced_param_attrs` attempts to infer attributes by + // visit the function body. + determined_fn_def_id, + ); debug!("fn_abi_new_uncached = {:?}", fn_abi); fn_abi_sanity_check(cx, &fn_abi, sig.abi); Ok(tcx.arena.alloc(fn_abi)) @@ -666,7 +664,7 @@ fn fn_abi_adjust_for_abi<'tcx>( fn_abi: &mut FnAbi<'tcx, Ty<'tcx>>, abi: ExternAbi, fn_def_id: Option, -) -> Result<(), &'tcx FnAbiError<'tcx>> { +) { if abi == ExternAbi::Unadjusted { // The "unadjusted" ABI passes aggregates in "direct" mode. That's fragile but needed for // some LLVM intrinsics. @@ -686,7 +684,7 @@ fn fn_abi_adjust_for_abi<'tcx>( for arg in fn_abi.args.iter_mut() { unadjust(arg); } - return Ok(()); + return; } let tcx = cx.tcx(); @@ -727,12 +725,8 @@ fn fn_abi_adjust_for_abi<'tcx>( } } } else { - fn_abi - .adjust_for_foreign_abi(cx, abi) - .map_err(|err| &*tcx.arena.alloc(FnAbiError::AdjustForForeignAbi(err)))?; + fn_abi.adjust_for_foreign_abi(cx, abi); } - - Ok(()) } #[tracing::instrument(level = "debug", skip(cx))] @@ -757,7 +751,7 @@ fn make_thin_self_ptr<'tcx>( // To get the type `*mut RcInner`, we just keep unwrapping newtypes until we // get a built-in pointer type let mut wide_pointer_layout = layout; - while !wide_pointer_layout.ty.is_unsafe_ptr() && !wide_pointer_layout.ty.is_ref() { + while !wide_pointer_layout.ty.is_raw_ptr() && !wide_pointer_layout.ty.is_ref() { wide_pointer_layout = wide_pointer_layout .non_1zst_field(cx) .expect("not exactly one non-1-ZST field in a `DispatchFromDyn` type") diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index 8f9f66db1bdd2..f71b924b177a5 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -1,8 +1,8 @@ use rustc_data_structures::fx::FxIndexSet; -use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::{self as hir, AmbigArg}; use rustc_middle::query::Providers; use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt}; use rustc_middle::{bug, span_bug}; @@ -187,7 +187,7 @@ fn associated_types_for_impl_traits_in_associated_fn( } impl<'tcx> Visitor<'tcx> for RPITVisitor { - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { if let hir::TyKind::OpaqueDef(opaq) = ty.kind && self.rpits.insert(opaq.def_id) { diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 637e239a57010..ece796b3c71ce 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -4,7 +4,7 @@ use rustc_abi::{FIRST_VARIANT, VariantIdx}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; -use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; +use rustc_middle::mir::interpret::LitToConstInput; use rustc_middle::query::Providers; use rustc_middle::thir::visit; use rustc_middle::thir::visit::Visitor; @@ -22,16 +22,13 @@ fn destructure_const<'tcx>( tcx: TyCtxt<'tcx>, const_: ty::Const<'tcx>, ) -> ty::DestructuredConst<'tcx> { - let ty::ConstKind::Value(ct_ty, valtree) = const_.kind() else { + let ty::ConstKind::Value(cv) = const_.kind() else { bug!("cannot destructure constant {:?}", const_) }; - let branches = match valtree { - ty::ValTree::Branch(b) => b, - _ => bug!("cannot destructure constant {:?}", const_), - }; + let branches = cv.valtree.unwrap_branch(); - let (fields, variant) = match ct_ty.kind() { + let (fields, variant) = match cv.ty.kind() { ty::Array(inner_ty, _) | ty::Slice(inner_ty) => { // construct the consts for the elements of the array/slice let field_consts = branches @@ -116,24 +113,20 @@ fn recurse_build<'tcx>( | &ExprKind::ValueTypeAscription { source, .. } => { recurse_build(tcx, body, source, root_span)? } + &ExprKind::PlaceUnwrapUnsafeBinder { .. } + | &ExprKind::ValueUnwrapUnsafeBinder { .. } + | &ExprKind::WrapUnsafeBinder { .. } => { + todo!("FIXME(unsafe_binders)") + } &ExprKind::Literal { lit, neg } => { let sp = node.span; - match tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) { - Ok(c) => c, - Err(LitToConstError::Reported(guar)) => ty::Const::new_error(tcx, guar), - Err(LitToConstError::TypeError) => { - bug!("encountered type error in lit_to_const") - } - } + tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) } &ExprKind::NonHirLiteral { lit, user_ty: _ } => { - let val = ty::ValTree::from_scalar_int(lit); - ty::Const::new_value(tcx, val, node.ty) - } - &ExprKind::ZstLiteral { user_ty: _ } => { - let val = ty::ValTree::zst(); + let val = ty::ValTree::from_scalar_int(tcx, lit); ty::Const::new_value(tcx, val, node.ty) } + &ExprKind::ZstLiteral { user_ty: _ } => ty::Const::zero_sized(tcx, node.ty), &ExprKind::NamedConst { def_id, args, user_ty: _ } => { let uneval = ty::UnevaluatedConst::new(def_id, args); ty::Const::new_unevaluated(tcx, uneval) @@ -353,6 +346,9 @@ impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> { | thir::ExprKind::Adt(_) | thir::ExprKind::PlaceTypeAscription { .. } | thir::ExprKind::ValueTypeAscription { .. } + | thir::ExprKind::PlaceUnwrapUnsafeBinder { .. } + | thir::ExprKind::ValueUnwrapUnsafeBinder { .. } + | thir::ExprKind::WrapUnsafeBinder { .. } | thir::ExprKind::Closure(_) | thir::ExprKind::Literal { .. } | thir::ExprKind::NonHirLiteral { .. } @@ -371,7 +367,8 @@ impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> { match pat.kind { thir::PatKind::Constant { value } => value.has_non_region_param(), - thir::PatKind::Range(box thir::PatRange { lo, hi, .. }) => { + thir::PatKind::Range(ref range) => { + let &thir::PatRange { lo, hi, .. } = range.as_ref(); lo.has_non_region_param() || hi.has_non_region_param() } _ => false, diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs index c4637f1293cb7..cc7baf8daac54 100644 --- a/compiler/rustc_ty_utils/src/implied_bounds.rs +++ b/compiler/rustc_ty_utils/src/implied_bounds.rs @@ -79,10 +79,10 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<' if matches!(*orig_lt, ty::ReLateParam(..)) { mapping.insert( orig_lt, - ty::Region::new_early_param(tcx, ty::EarlyParamRegion { - index: param.index, - name: param.name, - }), + ty::Region::new_early_param( + tcx, + ty::EarlyParamRegion { index: param.index, name: param.name }, + ), ); } } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index fc76a86f79772..8c6e3c86bf5c8 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -225,7 +225,7 @@ fn resolve_associated_item<'tcx>( if trait_item_id != leaf_def.item.def_id && let Some(leaf_def_item) = leaf_def.item.def_id.as_local() { - tcx.ensure().compare_impl_item(leaf_def_item)?; + tcx.ensure_ok().compare_impl_item(leaf_def_item)?; } Some(ty::Instance::new(leaf_def.item.def_id, args)) @@ -248,7 +248,7 @@ fn resolve_associated_item<'tcx>( }) } } - traits::ImplSource::Builtin(BuiltinImplSource::Misc, _) => { + traits::ImplSource::Builtin(BuiltinImplSource::Misc | BuiltinImplSource::Trivial, _) => { if tcx.is_lang_item(trait_ref.def_id, LangItem::Clone) { // FIXME(eddyb) use lang items for methods instead of names. let name = tcx.item_name(trait_item_id); diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 9f138cf1275d2..ee271048fc171 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -9,7 +9,7 @@ use rustc_abi::{ HasDataLayout, Layout, LayoutCalculatorError, LayoutData, Niche, ReprOptions, Scalar, Size, StructKind, TagEncoding, VariantIdx, Variants, WrappingRange, }; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::bug; use rustc_middle::mir::{CoroutineLayout, CoroutineSavedLocal}; @@ -104,7 +104,7 @@ fn map_error<'tcx>( // This is sometimes not a compile error if there are trivially false where clauses. // See `tests/ui/layout/trivial-bounds-sized.rs` for an example. assert!(field.layout.is_unsized(), "invalid layout error {err:#?}"); - if !field.ty.is_sized(cx.tcx(), cx.typing_env) { + if cx.typing_env.param_env.caller_bounds().is_empty() { cx.tcx().dcx().delayed_bug(format!( "encountered unexpected unsized field in layout of {ty:?}: {field:#?}" )); @@ -113,13 +113,17 @@ fn map_error<'tcx>( } LayoutCalculatorError::EmptyUnion => { // This is always a compile error. - cx.tcx().dcx().delayed_bug(format!("computed layout of empty union: {ty:?}")); - LayoutError::Unknown(ty) + let guar = + cx.tcx().dcx().delayed_bug(format!("computed layout of empty union: {ty:?}")); + LayoutError::ReferencesError(guar) } LayoutCalculatorError::ReprConflict => { // packed enums are the only known trigger of this, but others might arise - cx.tcx().dcx().delayed_bug(format!("computed impossible repr (packed enum?): {ty:?}")); - LayoutError::Unknown(ty) + let guar = cx + .tcx() + .dcx() + .delayed_bug(format!("computed impossible repr (packed enum?): {ty:?}")); + LayoutError::ReferencesError(guar) } }; error(cx, err) @@ -140,6 +144,35 @@ fn univariant_uninterned<'tcx>( cx.calc.univariant(fields, repr, kind).map_err(|err| map_error(cx, ty, err)) } +fn extract_const_value<'tcx>( + const_: ty::Const<'tcx>, + ty: Ty<'tcx>, + cx: &LayoutCx<'tcx>, +) -> Result, &'tcx LayoutError<'tcx>> { + match const_.kind() { + ty::ConstKind::Value(cv) => Ok(cv), + ty::ConstKind::Error(guar) => { + return Err(error(cx, LayoutError::ReferencesError(guar))); + } + ty::ConstKind::Param(_) | ty::ConstKind::Expr(_) => { + if !const_.has_param() { + bug!("no generic type found in the type: {ty:?}"); + } + return Err(error(cx, LayoutError::TooGeneric(ty))); + } + ty::ConstKind::Unevaluated(_) => { + if !const_.has_param() { + return Err(error(cx, LayoutError::Unknown(ty))); + } else { + return Err(error(cx, LayoutError::TooGeneric(ty))); + } + } + ty::ConstKind::Infer(_) | ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(_) => { + bug!("unexpected type: {ty:?}"); + } + } +} + fn layout_of_uncached<'tcx>( cx: &LayoutCx<'tcx>, ty: Ty<'tcx>, @@ -176,12 +209,12 @@ fn layout_of_uncached<'tcx>( &mut layout.backend_repr { if let Some(start) = start { - scalar.valid_range_mut().start = start + scalar.valid_range_mut().start = extract_const_value(start, ty, cx)? .try_to_bits(tcx, cx.typing_env) .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; } if let Some(end) = end { - let mut end = end + let mut end = extract_const_value(end, ty, cx)? .try_to_bits(tcx, cx.typing_env) .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; if !include_end { @@ -207,14 +240,20 @@ fn layout_of_uncached<'tcx>( } // Basic scalars. - ty::Bool => tcx.mk_layout(LayoutData::scalar(cx, Scalar::Initialized { - value: Int(I8, false), - valid_range: WrappingRange { start: 0, end: 1 }, - })), - ty::Char => tcx.mk_layout(LayoutData::scalar(cx, Scalar::Initialized { - value: Int(I32, false), - valid_range: WrappingRange { start: 0, end: 0x10FFFF }, - })), + ty::Bool => tcx.mk_layout(LayoutData::scalar( + cx, + Scalar::Initialized { + value: Int(I8, false), + valid_range: WrappingRange { start: 0, end: 1 }, + }, + )), + ty::Char => tcx.mk_layout(LayoutData::scalar( + cx, + Scalar::Initialized { + value: Int(I32, false), + valid_range: WrappingRange { start: 0, end: 0x10FFFF }, + }, + )), ty::Int(ity) => scalar(Int(abi::Integer::from_int_ty(dl, ity), true)), ty::Uint(ity) => scalar(Int(abi::Integer::from_uint_ty(dl, ity), false)), ty::Float(fty) => scalar(Float(abi::Float::from_float_ty(fty))), @@ -230,7 +269,7 @@ fn layout_of_uncached<'tcx>( // Potentially-wide pointers. ty::Ref(_, pointee, _) | ty::RawPtr(pointee, _) => { let mut data_ptr = scalar_unit(Pointer(AddressSpace::DATA)); - if !ty.is_unsafe_ptr() { + if !ty.is_raw_ptr() { data_ptr.valid_range_mut().start = 1; } @@ -313,17 +352,11 @@ fn layout_of_uncached<'tcx>( } // Arrays and slices. - ty::Array(element, mut count) => { - if count.has_aliases() { - count = tcx.normalize_erasing_regions(cx.typing_env, count); - if count.has_aliases() { - return Err(error(cx, LayoutError::Unknown(ty))); - } - } - - let count = count + ty::Array(element, count) => { + let count = extract_const_value(count, ty, cx)? .try_to_target_usize(tcx) .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; + let element = cx.layout_of(element)?; let size = element .size @@ -347,6 +380,7 @@ fn layout_of_uncached<'tcx>( size, max_repr_align: None, unadjusted_abi_align: element.align.abi, + randomization_seed: element.randomization_seed.wrapping_add(count), }) } ty::Slice(element) => { @@ -360,6 +394,8 @@ fn layout_of_uncached<'tcx>( size: Size::ZERO, max_repr_align: None, unadjusted_abi_align: element.align.abi, + // adding a randomly chosen value to distinguish slices + randomization_seed: element.randomization_seed.wrapping_add(0x2dcba99c39784102), }) } ty::Str => tcx.mk_layout(LayoutData { @@ -371,6 +407,8 @@ fn layout_of_uncached<'tcx>( size: Size::ZERO, max_repr_align: None, unadjusted_abi_align: dl.i8_align.abi, + // another random value + randomization_seed: 0xc1325f37d127be22, }), // Odd unit types. @@ -427,8 +465,10 @@ fn layout_of_uncached<'tcx>( ty::Adt(def, args) if def.repr().simd() => { if !def.is_struct() { // Should have yielded E0517 by now. - tcx.dcx().delayed_bug("#[repr(simd)] was applied to an ADT that is not a struct"); - return Err(error(cx, LayoutError::Unknown(ty))); + let guar = tcx + .dcx() + .delayed_bug("#[repr(simd)] was applied to an ADT that is not a struct"); + return Err(error(cx, LayoutError::ReferencesError(guar))); } let fields = &def.non_enum_variant().fields; @@ -454,10 +494,10 @@ fn layout_of_uncached<'tcx>( // (should be caught by typeck) for fi in fields { if fi.ty(tcx, args) != f0_ty { - tcx.dcx().delayed_bug( + let guar = tcx.dcx().delayed_bug( "#[repr(simd)] was applied to an ADT with heterogeneous field type", ); - return Err(error(cx, LayoutError::Unknown(ty))); + return Err(error(cx, LayoutError::ReferencesError(guar))); } } @@ -517,10 +557,13 @@ fn layout_of_uncached<'tcx>( // Non-power-of-two vectors have padding up to the next power-of-two. // If we're a packed repr, remove the padding while keeping the alignment as close // to a vector as possible. - (BackendRepr::Memory { sized: true }, AbiAndPrefAlign { - abi: Align::max_for_offset(size), - pref: dl.vector_align(size).pref, - }) + ( + BackendRepr::Memory { sized: true }, + AbiAndPrefAlign { + abi: Align::max_for_offset(size), + pref: dl.vector_align(size).pref, + }, + ) } else { (BackendRepr::Vector { element: e_abi, count: e_len }, dl.vector_align(size)) }; @@ -542,6 +585,7 @@ fn layout_of_uncached<'tcx>( align, max_repr_align: None, unadjusted_abi_align: align.abi, + randomization_seed: e_ly.randomization_seed.wrapping_add(e_len), }) } @@ -561,11 +605,11 @@ fn layout_of_uncached<'tcx>( if def.is_union() { if def.repr().pack.is_some() && def.repr().align.is_some() { - tcx.dcx().span_delayed_bug( + let guar = tcx.dcx().span_delayed_bug( tcx.def_span(def.did()), "union cannot be packed and aligned", ); - return Err(error(cx, LayoutError::Unknown(ty))); + return Err(error(cx, LayoutError::ReferencesError(guar))); } return Ok(tcx.mk_layout( @@ -673,6 +717,9 @@ fn layout_of_uncached<'tcx>( // Types with no meaningful known layout. ty::Alias(..) => { + if ty.has_param() { + return Err(error(cx, LayoutError::TooGeneric(ty))); + } // NOTE(eddyb) `layout_of` query should've normalized these away, // if that was possible, so there's no reason to try again here. return Err(error(cx, LayoutError::Unknown(ty))); @@ -682,7 +729,11 @@ fn layout_of_uncached<'tcx>( bug!("Layout::compute: unexpected type `{}`", ty) } - ty::Placeholder(..) | ty::Param(_) => { + ty::Param(_) => { + return Err(error(cx, LayoutError::TooGeneric(ty))); + } + + ty::Placeholder(..) => { return Err(error(cx, LayoutError::Unknown(ty))); } }) @@ -718,7 +769,7 @@ enum SavedLocalEligibility { /// Compute the eligibility and assignment of each local. fn coroutine_saved_local_eligibility( info: &CoroutineLayout<'_>, -) -> (BitSet, IndexVec) { +) -> (DenseBitSet, IndexVec) { use SavedLocalEligibility::*; let mut assignments: IndexVec = @@ -726,7 +777,7 @@ fn coroutine_saved_local_eligibility( // The saved locals not eligible for overlap. These will get // "promoted" to the prefix of our coroutine. - let mut ineligible_locals = BitSet::new_empty(info.field_tys.len()); + let mut ineligible_locals = DenseBitSet::new_empty(info.field_tys.len()); // Figure out which of our saved locals are fields in only // one variant. The rest are deemed ineligible for overlap. @@ -786,7 +837,7 @@ fn coroutine_saved_local_eligibility( // lay them out with the other locals in the prefix and eliminate // unnecessary padding bytes. { - let mut used_variants = BitSet::new_empty(info.variant_fields.len()); + let mut used_variants = DenseBitSet::new_empty(info.variant_fields.len()); for assignment in &assignments { if let Assigned(idx) = assignment { used_variants.insert(*idx); @@ -999,6 +1050,9 @@ fn coroutine_layout<'tcx>( BackendRepr::Memory { sized: true } }; + // this is similar to how ReprOptions populates its field_shuffle_seed + let def_hash = tcx.def_path_hash(def_id).0.to_smaller_hash().as_u64(); + let layout = tcx.mk_layout(LayoutData { variants: Variants::Multiple { tag, @@ -1019,6 +1073,7 @@ fn coroutine_layout<'tcx>( align, max_repr_align: None, unadjusted_abi_align: align.abi, + randomization_seed: def_hash, }); debug!("coroutine layout ({:?}): {:#?}", ty, layout); Ok(layout) @@ -1133,10 +1188,13 @@ fn variant_info_for_adt<'tcx>( }) .collect(); - (variant_infos, match tag_encoding { - TagEncoding::Direct => Some(tag.size(cx)), - _ => None, - }) + ( + variant_infos, + match tag_encoding { + TagEncoding::Direct => Some(tag.size(cx)), + _ => None, + }, + ) } } } @@ -1256,8 +1314,11 @@ fn variant_info_for_coroutine<'tcx>( let end_states: Vec<_> = end_states.collect(); variant_infos.extend(end_states); - (variant_infos, match tag_encoding { - TagEncoding::Direct => Some(tag.size(cx)), - _ => None, - }) + ( + variant_infos, + match tag_encoding { + TagEncoding::Direct => Some(tag.size(cx)), + _ => None, + }, + ) } diff --git a/compiler/rustc_ty_utils/src/representability.rs b/compiler/rustc_ty_utils/src/representability.rs index 0ffb7f624965e..98b1550e1a3d6 100644 --- a/compiler/rustc_ty_utils/src/representability.rs +++ b/compiler/rustc_ty_utils/src/representability.rs @@ -1,5 +1,5 @@ use rustc_hir::def::DefKind; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::ty::{self, Representability, Ty, TyCtxt}; @@ -83,10 +83,10 @@ fn representability_adt_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Representab Representability::Representable } -fn params_in_repr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> BitSet { +fn params_in_repr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> DenseBitSet { let adt_def = tcx.adt_def(def_id); let generics = tcx.generics_of(def_id); - let mut params_in_repr = BitSet::new_empty(generics.own_params.len()); + let mut params_in_repr = DenseBitSet::new_empty(generics.own_params.len()); for variant in adt_def.variants() { for field in variant.fields.iter() { params_in_repr_ty( @@ -99,7 +99,7 @@ fn params_in_repr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> BitSet { params_in_repr } -fn params_in_repr_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, params_in_repr: &mut BitSet) { +fn params_in_repr_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, params_in_repr: &mut DenseBitSet) { match *ty.kind() { ty::Adt(adt, args) => { let inner_params_in_repr = tcx.params_in_repr(adt.did()); diff --git a/compiler/rustc_ty_utils/src/sig_types.rs b/compiler/rustc_ty_utils/src/sig_types.rs index 6f1cbb0fee700..64e5a609b2f6d 100644 --- a/compiler/rustc_ty_utils/src/sig_types.rs +++ b/compiler/rustc_ty_utils/src/sig_types.rs @@ -31,7 +31,7 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( // If the type of the item uses `_`, we're gonna error out anyway, but // typeck (which type_of invokes below), will call back into opaque_types_defined_by // causing a cycle. So we just bail out in this case. - if hir_sig.output.get_infer_ret_ty().is_some() { + if hir_sig.output.is_suggestable_infer_ty().is_some() { return V::Result::output(); } let ty_sig = tcx.fn_sig(item).instantiate_identity(); diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 7eed32e3a3366..8ed45b4e5415b 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -2,7 +2,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::LangItem; use rustc_hir::def::DefKind; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::ty::fold::fold_regions; @@ -317,7 +317,7 @@ fn asyncness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Asyncness { }) } -fn unsizing_params_for_adt<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> BitSet { +fn unsizing_params_for_adt<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> DenseBitSet { let def = tcx.adt_def(def_id); let num_params = tcx.generics_of(def_id).count(); @@ -338,10 +338,10 @@ fn unsizing_params_for_adt<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> BitSet, ty::TraitRef, ty::ExistentialTraitRef, + ty::HostEffectPredicate, } impl Binder @@ -803,7 +804,7 @@ impl<'a, I: Interner> ArgFolder<'a, I> { #[inline(never)] fn region_param_out_of_range(&self, ebr: I::EarlyParamRegion, r: I::Region) -> ! { panic!( - "const parameter `{:?}` ({:?}/{}) out of range when instantiating args={:?}", + "region parameter `{:?}` ({:?}/{}) out of range when instantiating args={:?}", ebr, r, ebr.index(), diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index 03dfe547cedd4..aafc0907ae156 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -31,7 +31,7 @@ pub enum ConstKind { Unevaluated(ty::UnevaluatedConst), /// Used to hold computed value. - Value(I::Ty, I::ValueConst), + Value(I::ValueConst), /// A placeholder for a const which could not be computed; this is /// propagated to avoid useless error messages. @@ -52,7 +52,7 @@ impl fmt::Debug for ConstKind { Bound(debruijn, var) => crate::debug_bound_var(f, *debruijn, var), Placeholder(placeholder) => write!(f, "{placeholder:?}"), Unevaluated(uv) => write!(f, "{uv:?}"), - Value(ty, valtree) => write!(f, "({valtree:?}: {ty:?})"), + Value(val) => write!(f, "{val:?}"), Error(_) => write!(f, "{{const error}}"), Expr(expr) => write!(f, "{expr:?}"), } diff --git a/compiler/rustc_type_ir/src/data_structures/mod.rs b/compiler/rustc_type_ir/src/data_structures/mod.rs index d9766d91845cb..30c67d10d0e86 100644 --- a/compiler/rustc_type_ir/src/data_structures/mod.rs +++ b/compiler/rustc_type_ir/src/data_structures/mod.rs @@ -12,13 +12,11 @@ mod delayed_map; mod impl_ { pub use rustc_data_structures::sso::{SsoHashMap, SsoHashSet}; pub use rustc_data_structures::stack::ensure_sufficient_stack; - pub use rustc_data_structures::sync::Lrc; } #[cfg(not(feature = "nightly"))] mod impl_ { pub use std::collections::{HashMap as SsoHashMap, HashSet as SsoHashSet}; - pub use std::sync::Arc as Lrc; #[inline] pub fn ensure_sufficient_stack(f: impl FnOnce() -> R) -> R { diff --git a/compiler/rustc_type_ir/src/error.rs b/compiler/rustc_type_ir/src/error.rs index 55671b84dbc4f..68b11489ae7c1 100644 --- a/compiler/rustc_type_ir/src/error.rs +++ b/compiler/rustc_type_ir/src/error.rs @@ -51,6 +51,9 @@ pub enum TypeError { ConstMismatch(ExpectedFound), IntrinsicCast, + /// `#[rustc_force_inline]` functions must be inlined and must not be codegened independently, + /// so casting to a function pointer must be prohibited. + ForceInlineCast, /// Safe `#[target_feature]` functions are not assignable to safe function pointers. TargetFeatureCast(I::DefId), } @@ -83,6 +86,7 @@ impl TypeError { | ProjectionMismatched(_) | ExistentialMismatch(_) | ConstMismatch(_) + | ForceInlineCast | IntrinsicCast => true, } } diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs index 9b3ff14d507ac..9955e92b55a5c 100644 --- a/compiler/rustc_type_ir/src/fast_reject.rs +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -483,8 +483,8 @@ impl match rhs.kind() { - ty::ConstKind::Value(_, rhs_val) => lhs_val == rhs_val, + ty::ConstKind::Value(lhs_val) => match rhs.kind() { + ty::ConstKind::Value(rhs_val) => lhs_val.valtree() == rhs_val.valtree(), _ => false, }, diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index d337a1a8ad9b7..0bc8b94bbf4cc 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -46,12 +46,12 @@ //! ``` use std::mem; +use std::sync::Arc; use rustc_index::{Idx, IndexVec}; use thin_vec::ThinVec; use tracing::{debug, instrument}; -use crate::data_structures::Lrc; use crate::inherent::*; use crate::visit::{TypeVisitable, TypeVisitableExt as _}; use crate::{self as ty, Interner}; @@ -73,7 +73,7 @@ type Never = std::convert::Infallible; /// which means in practice almost every foldable type needs to also be /// visitable. (However, there are some types that are visitable without being /// foldable.) -pub trait TypeFoldable: TypeVisitable { +pub trait TypeFoldable: TypeVisitable + Clone { /// The entry point for folding. To fold a value `t` with a folder `f` /// call: `t.try_fold_with(f)`. /// @@ -273,28 +273,28 @@ impl, E: TypeFoldable> TypeFoldable for Re } } -impl> TypeFoldable for Lrc { +impl> TypeFoldable for Arc { fn try_fold_with>(mut self, folder: &mut F) -> Result { // We merely want to replace the contained `T`, if at all possible, - // so that we don't needlessly allocate a new `Lrc` or indeed clone + // so that we don't needlessly allocate a new `Arc` or indeed clone // the contained type. unsafe { // First step is to ensure that we have a unique reference to - // the contained type, which `Lrc::make_mut` will accomplish (by - // allocating a new `Lrc` and cloning the `T` only if required). - // This is done *before* casting to `Lrc>` so that + // the contained type, which `Arc::make_mut` will accomplish (by + // allocating a new `Arc` and cloning the `T` only if required). + // This is done *before* casting to `Arc>` so that // panicking during `make_mut` does not leak the `T`. - Lrc::make_mut(&mut self); + Arc::make_mut(&mut self); - // Casting to `Lrc>` is safe because `ManuallyDrop` + // Casting to `Arc>` is safe because `ManuallyDrop` // is `repr(transparent)`. - let ptr = Lrc::into_raw(self).cast::>(); - let mut unique = Lrc::from_raw(ptr); + let ptr = Arc::into_raw(self).cast::>(); + let mut unique = Arc::from_raw(ptr); - // Call to `Lrc::make_mut` above guarantees that `unique` is the + // Call to `Arc::make_mut` above guarantees that `unique` is the // sole reference to the contained value, so we can avoid doing // a checked `get_mut` here. - let slot = Lrc::get_mut(&mut unique).unwrap_unchecked(); + let slot = Arc::get_mut(&mut unique).unwrap_unchecked(); // Semantically move the contained type out from `unique`, fold // it, then move the folded value back into `unique`. Should @@ -304,8 +304,8 @@ impl> TypeFoldable for Lrc { let folded = owned.try_fold_with(folder)?; *slot = mem::ManuallyDrop::new(folded); - // Cast back to `Lrc`. - Ok(Lrc::from_raw(Lrc::into_raw(unique).cast())) + // Cast back to `Arc`. + Ok(Arc::from_raw(Arc::into_raw(unique).cast())) } } } diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index a892b88c2c636..c9b636132f807 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -201,17 +201,20 @@ pub trait InferCtxtLike: Sized { &self, sub: ::Region, sup: ::Region, + span: ::Span, ); fn equate_regions( &self, a: ::Region, b: ::Region, + span: ::Span, ); fn register_ty_outlives( &self, ty: ::Ty, r: ::Region, + span: ::Span, ); } diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 872cf6680185b..6924216bd26e6 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -286,6 +286,11 @@ pub trait Const>: } } +pub trait ValueConst>: Copy + Debug + Hash + Eq { + fn ty(self) -> I::Ty; + fn valtree(self) -> I::ValTree; +} + pub trait ExprConst>: Copy + Debug + Hash + Eq + Relate { fn args(self) -> I::GenericArgs; } @@ -535,6 +540,8 @@ pub trait AdtDef: Copy + Debug + Hash + Eq { fn is_phantom_data(self) -> bool; + fn is_manually_drop(self) -> bool; + // FIXME: perhaps use `all_fields` and expose `FieldDef`. fn all_field_tys(self, interner: I) -> ty::EarlyBinder>; diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 025ec7ae896dd..aae2d2e96b96f 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -3,7 +3,7 @@ use std::hash::Hash; use std::ops::Deref; use rustc_ast_ir::Movability; -use rustc_index::bit_set::BitSet; +use rustc_index::bit_set::DenseBitSet; use smallvec::SmallVec; use crate::fold::TypeFoldable; @@ -112,8 +112,9 @@ pub trait Interner: type PlaceholderConst: PlaceholderLike; type ParamConst: Copy + Debug + Hash + Eq + ParamLike; type BoundConst: Copy + Debug + Hash + Eq + BoundVarLike; - type ValueConst: Copy + Debug + Hash + Eq; + type ValueConst: ValueConst; type ExprConst: ExprConst; + type ValTree: Copy + Debug + Hash + Eq; // Kinds of regions type Region: Region; @@ -140,6 +141,12 @@ pub trait Interner: type VariancesOf: Copy + Debug + SliceLike; fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf; + fn opt_alias_variances( + self, + kind: impl Into, + def_id: Self::DefId, + ) -> Option; + fn type_of(self, def_id: Self::DefId) -> ty::EarlyBinder; type AdtDef: AdtDef; @@ -203,6 +210,16 @@ pub trait Interner: def_id: Self::DefId, ) -> ty::EarlyBinder>; + fn item_self_bounds( + self, + def_id: Self::DefId, + ) -> ty::EarlyBinder>; + + fn item_non_self_bounds( + self, + def_id: Self::DefId, + ) -> ty::EarlyBinder>; + fn predicates_of( self, def_id: Self::DefId, @@ -282,7 +299,7 @@ pub trait Interner: fn coroutine_is_gen(self, coroutine_def_id: Self::DefId) -> bool; fn coroutine_is_async_gen(self, coroutine_def_id: Self::DefId) -> bool; - type UnsizingParams: Deref>; + type UnsizingParams: Deref>; fn unsizing_params_for_adt(self, adt_def_id: Self::DefId) -> Self::UnsizingParams; fn find_const_ty_from_env( diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index eeb80bc3ab420..65f7cdf8f922b 100644 --- a/compiler/rustc_type_ir/src/lang_items.rs +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -10,6 +10,7 @@ pub enum TraitSolverLangItem { AsyncFnOnce, AsyncFnOnceOutput, AsyncIterator, + BikeshedGuaranteedNoDrop, CallOnceFuture, CallRefFuture, Clone, diff --git a/compiler/rustc_type_ir/src/macros.rs b/compiler/rustc_type_ir/src/macros.rs index aae5aeb5fb363..fab1a11304d57 100644 --- a/compiler/rustc_type_ir/src/macros.rs +++ b/compiler/rustc_type_ir/src/macros.rs @@ -47,23 +47,16 @@ TrivialTypeTraversalImpls! { u16, u32, u64, - String, + // tidy-alphabetical-start crate::AliasRelationDirection, - crate::AliasTyKind, crate::BoundConstness, crate::DebruijnIndex, - crate::FloatTy, - crate::InferTy, - crate::IntVarValue, crate::PredicatePolarity, - crate::RegionVid, crate::solve::BuiltinImplSource, crate::solve::Certainty, crate::solve::GoalSource, - crate::solve::MaybeCause, - crate::solve::NoSolution, crate::UniverseIndex, crate::Variance, - rustc_ast_ir::Movability, rustc_ast_ir::Mutability, + // tidy-alphabetical-end } diff --git a/compiler/rustc_type_ir/src/outlives.rs b/compiler/rustc_type_ir/src/outlives.rs index c26e211a79402..b09a378d34143 100644 --- a/compiler/rustc_type_ir/src/outlives.rs +++ b/compiler/rustc_type_ir/src/outlives.rs @@ -148,7 +148,7 @@ impl TypeVisitor for OutlivesCollector<'_, I> { // trait-ref. Therefore, if we see any higher-ranked regions, // we simply fallback to the most restrictive rule, which // requires that `Pi: 'a` for all `i`. - ty::Alias(_, alias_ty) => { + ty::Alias(kind, alias_ty) => { if !alias_ty.has_escaping_bound_vars() { // best case: no escaping regions, so push the // projection and skip the subtree (thus generating no @@ -162,7 +162,7 @@ impl TypeVisitor for OutlivesCollector<'_, I> { // OutlivesProjectionComponents. Continue walking // through and constrain Pi. let mut subcomponents = smallvec![]; - compute_alias_components_recursive(self.cx, ty, &mut subcomponents); + compute_alias_components_recursive(self.cx, kind, alias_ty, &mut subcomponents); self.out.push(Component::EscapingAlias(subcomponents.into_iter().collect())); } } @@ -217,21 +217,17 @@ impl TypeVisitor for OutlivesCollector<'_, I> { } } -/// Collect [Component]s for *all* the args of `parent`. +/// Collect [Component]s for *all* the args of `alias_ty`. /// -/// This should not be used to get the components of `parent` itself. +/// This should not be used to get the components of `alias_ty` itself. /// Use [push_outlives_components] instead. pub fn compute_alias_components_recursive( cx: I, - alias_ty: I::Ty, + kind: ty::AliasTyKind, + alias_ty: ty::AliasTy, out: &mut SmallVec<[Component; 4]>, ) { - let ty::Alias(kind, alias_ty) = alias_ty.kind() else { - unreachable!("can only call `compute_alias_components_recursive` on an alias type") - }; - - let opt_variances = - if kind == ty::Opaque { Some(cx.variances_of(alias_ty.def_id)) } else { None }; + let opt_variances = cx.opt_alias_variances(kind, alias_ty.def_id); let mut visitor = OutlivesCollector { cx, out, visited: Default::default() }; diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 9a80f97e274d2..51790b13ec776 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -469,6 +469,17 @@ impl AliasTermKind { } } +impl From for AliasTermKind { + fn from(value: ty::AliasTyKind) -> Self { + match value { + ty::Projection => AliasTermKind::ProjectionTy, + ty::Opaque => AliasTermKind::OpaqueTy, + ty::Weak => AliasTermKind::WeakTy, + ty::Inherent => AliasTermKind::InherentTy, + } + } +} + /// Represents the unprojected term of a projection goal. /// /// * For a projection, this would be `>::N<...>`. @@ -541,35 +552,29 @@ impl AliasTerm { pub fn to_term(self, interner: I) -> I::Term { match self.kind(interner) { - AliasTermKind::ProjectionTy => { - Ty::new_alias(interner, ty::AliasTyKind::Projection, ty::AliasTy { - def_id: self.def_id, - args: self.args, - _use_alias_ty_new_instead: (), - }) - .into() - } - AliasTermKind::InherentTy => { - Ty::new_alias(interner, ty::AliasTyKind::Inherent, ty::AliasTy { - def_id: self.def_id, - args: self.args, - _use_alias_ty_new_instead: (), - }) - .into() - } - AliasTermKind::OpaqueTy => { - Ty::new_alias(interner, ty::AliasTyKind::Opaque, ty::AliasTy { - def_id: self.def_id, - args: self.args, - _use_alias_ty_new_instead: (), - }) - .into() - } - AliasTermKind::WeakTy => Ty::new_alias(interner, ty::AliasTyKind::Weak, ty::AliasTy { - def_id: self.def_id, - args: self.args, - _use_alias_ty_new_instead: (), - }) + AliasTermKind::ProjectionTy => Ty::new_alias( + interner, + ty::AliasTyKind::Projection, + ty::AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () }, + ) + .into(), + AliasTermKind::InherentTy => Ty::new_alias( + interner, + ty::AliasTyKind::Inherent, + ty::AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () }, + ) + .into(), + AliasTermKind::OpaqueTy => Ty::new_alias( + interner, + ty::AliasTyKind::Opaque, + ty::AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () }, + ) + .into(), + AliasTermKind::WeakTy => Ty::new_alias( + interner, + ty::AliasTyKind::Weak, + ty::AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () }, + ) .into(), AliasTermKind::UnevaluatedConst | AliasTermKind::ProjectionConst => { I::Const::new_unevaluated( diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index e628b66d2f086..d065384b58e23 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -236,28 +236,14 @@ impl Relate for ty::AliasTy { ExpectedFound::new(a, b) })) } else { - let args = match a.kind(relation.cx()) { - ty::Opaque => relate_args_with_variances( - relation, - a.def_id, - relation.cx().variances_of(a.def_id), - a.args, - b.args, + let cx = relation.cx(); + let args = if let Some(variances) = cx.opt_alias_variances(a.kind(cx), a.def_id) { + relate_args_with_variances( + relation, a.def_id, variances, a.args, b.args, false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle - )?, - ty::Projection if relation.cx().is_impl_trait_in_trait(a.def_id) => { - relate_args_with_variances( - relation, - a.def_id, - relation.cx().variances_of(a.def_id), - a.args, - b.args, - false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle - )? - } - ty::Projection | ty::Weak | ty::Inherent => { - relate_args_invariantly(relation, a.args, b.args)? - } + )? + } else { + relate_args_invariantly(relation, a.args, b.args)? }; Ok(ty::AliasTy::new_from_args(relation.cx(), a.def_id, args)) } @@ -606,7 +592,9 @@ pub fn structurally_relate_consts>( true } (ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2, - (ty::ConstKind::Value(_, a_val), ty::ConstKind::Value(_, b_val)) => a_val == b_val, + (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => { + a_val.valtree() == b_val.valtree() + } // While this is slightly incorrect, it shouldn't matter for `min_const_generics` // and is the better alternative to waiting until `generic_const_exprs` can diff --git a/compiler/rustc_type_ir/src/relate/solver_relating.rs b/compiler/rustc_type_ir/src/relate/solver_relating.rs index ad10ad5af9caf..e42639c68075a 100644 --- a/compiler/rustc_type_ir/src/relate/solver_relating.rs +++ b/compiler/rustc_type_ir/src/relate/solver_relating.rs @@ -1,10 +1,10 @@ -pub use rustc_type_ir::relate::*; -use rustc_type_ir::solve::Goal; -use rustc_type_ir::{self as ty, InferCtxtLike, Interner}; use tracing::{debug, instrument}; use self::combine::{PredicateEmittingRelation, super_combine_consts, super_combine_tys}; use crate::data_structures::DelayedSet; +pub use crate::relate::*; +use crate::solve::Goal; +use crate::{self as ty, InferCtxtLike, Interner}; pub trait RelateExt: InferCtxtLike { fn relate>( @@ -13,6 +13,7 @@ pub trait RelateExt: InferCtxtLike { lhs: T, variance: ty::Variance, rhs: T, + span: ::Span, ) -> Result< Vec::Predicate>>, TypeError, @@ -23,6 +24,7 @@ pub trait RelateExt: InferCtxtLike { param_env: ::ParamEnv, lhs: T, rhs: T, + span: ::Span, ) -> Result< Vec::Predicate>>, TypeError, @@ -36,12 +38,13 @@ impl RelateExt for Infcx { lhs: T, variance: ty::Variance, rhs: T, + span: ::Span, ) -> Result< Vec::Predicate>>, TypeError, > { let mut relate = - SolverRelating::new(self, StructurallyRelateAliases::No, variance, param_env); + SolverRelating::new(self, StructurallyRelateAliases::No, variance, param_env, span); relate.relate(lhs, rhs)?; Ok(relate.goals) } @@ -51,12 +54,18 @@ impl RelateExt for Infcx { param_env: ::ParamEnv, lhs: T, rhs: T, + span: ::Span, ) -> Result< Vec::Predicate>>, TypeError, > { - let mut relate = - SolverRelating::new(self, StructurallyRelateAliases::Yes, ty::Invariant, param_env); + let mut relate = SolverRelating::new( + self, + StructurallyRelateAliases::Yes, + ty::Invariant, + param_env, + span, + ); relate.relate(lhs, rhs)?; Ok(relate.goals) } @@ -68,6 +77,7 @@ pub struct SolverRelating<'infcx, Infcx, I: Interner> { // Immutable fields. structurally_relate_aliases: StructurallyRelateAliases, param_env: I::ParamEnv, + span: I::Span, // Mutable fields. ambient_variance: ty::Variance, goals: Vec>, @@ -106,10 +116,12 @@ where structurally_relate_aliases: StructurallyRelateAliases, ambient_variance: ty::Variance, param_env: I::ParamEnv, + span: I::Span, ) -> Self { SolverRelating { infcx, structurally_relate_aliases, + span, ambient_variance, param_env, goals: vec![], @@ -241,10 +253,10 @@ where fn regions(&mut self, a: I::Region, b: I::Region) -> RelateResult { match self.ambient_variance { // Subtype(&'a u8, &'b u8) => Outlives('a: 'b) => SubRegion('b, 'a) - ty::Covariant => self.infcx.sub_regions(b, a), + ty::Covariant => self.infcx.sub_regions(b, a, self.span), // Suptype(&'a u8, &'b u8) => Outlives('b: 'a) => SubRegion('a, 'b) - ty::Contravariant => self.infcx.sub_regions(a, b), - ty::Invariant => self.infcx.equate_regions(a, b), + ty::Contravariant => self.infcx.sub_regions(a, b, self.span), + ty::Invariant => self.infcx.equate_regions(a, b, self.span), ty::Bivariant => { unreachable!("Expected bivariance to be handled in relate_with_variance") } diff --git a/compiler/rustc_type_ir/src/solve/inspect.rs b/compiler/rustc_type_ir/src/solve/inspect.rs index d0e618dc6f96d..18fb71dd290e7 100644 --- a/compiler/rustc_type_ir/src/solve/inspect.rs +++ b/compiler/rustc_type_ir/src/solve/inspect.rs @@ -111,8 +111,6 @@ pub enum ProbeStep { pub enum ProbeKind { /// The root inference context while proving a goal. Root { result: QueryResult }, - /// Trying to normalize an alias by at least one step in `NormalizesTo`. - TryNormalizeNonRigid { result: QueryResult }, /// Probe entered when normalizing the self ty during candidate assembly NormalizedSelfTyAssembly, /// A candidate for proving a trait or alias-relate goal. diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 8fe512026e5d9..a562b751d8a9c 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -68,6 +68,10 @@ pub enum GoalSource { /// FIXME(-Znext-solver=coinductive): Explain how and why this /// changes whether cycles are coinductive. ImplWhereBound, + /// Const conditions that need to hold for `~const` alias bounds to hold. + /// + /// FIXME(-Znext-solver=coinductive): Are these even coinductive? + AliasBoundConstCondition, /// Instantiating a higher-ranked goal and re-proving it. InstantiateHigherRanked, /// Predicate required for an alias projection to be well-formed. @@ -165,6 +169,9 @@ pub enum CandidateSource { #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext, TyEncodable, TyDecodable))] pub enum BuiltinImplSource { + /// A built-in impl that is considered trivial, without any nested requirements. They + /// are preferred over where-clauses, and we want to track them explicitly. + Trivial, /// Some built-in impl we don't need to differentiate. This should be used /// unless more specific information is necessary. Misc, @@ -172,9 +179,8 @@ pub enum BuiltinImplSource { Object(usize), /// A built-in implementation of `Upcast` for trait objects to other trait objects. /// - /// This can be removed when `feature(dyn_upcasting)` is stabilized, since we only - /// use it to detect when upcasting traits in hir typeck. - TraitUpcasting, + /// The index is only used for winnowing. + TraitUpcasting(usize), /// Unsizing a tuple like `(A, B, ..., X)` to `(A, B, ..., Y)` if `X` unsizes to `Y`. /// /// This can be removed when `feature(tuple_unsizing)` is stabilized, since we only diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs index 10b164eae027b..d1ca9bdb7fbd1 100644 --- a/compiler/rustc_type_ir/src/ty_kind/closure.rs +++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs @@ -3,9 +3,10 @@ use std::ops::ControlFlow; use derive_where::derive_where; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; +use crate::data_structures::DelayedMap; use crate::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable, shift_region}; use crate::inherent::*; -use crate::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; +use crate::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; use crate::{self as ty, Interner}; /// A closure can be modeled as a struct that looks like: @@ -398,15 +399,18 @@ impl CoroutineClosureSignature { coroutine_def_id: I::DefId, tupled_upvars_ty: I::Ty, ) -> I::Ty { - let coroutine_args = ty::CoroutineArgs::new(cx, ty::CoroutineArgsParts { - parent_args, - kind_ty: coroutine_kind_ty, - resume_ty: self.resume_ty, - yield_ty: self.yield_ty, - return_ty: self.return_ty, - witness: self.interior, - tupled_upvars_ty, - }); + let coroutine_args = ty::CoroutineArgs::new( + cx, + ty::CoroutineArgsParts { + parent_args, + kind_ty: coroutine_kind_ty, + resume_ty: self.resume_ty, + yield_ty: self.yield_ty, + return_ty: self.return_ty, + witness: self.interior, + tupled_upvars_ty, + }, + ); Ty::new_coroutine(cx, coroutine_def_id, coroutine_args.args) } @@ -471,6 +475,7 @@ impl CoroutineClosureSignature { interner: cx, region: env_region, debruijn: ty::INNERMOST, + cache: Default::default(), }); Ty::new_tup_from_iter( cx, @@ -498,6 +503,10 @@ struct FoldEscapingRegions { interner: I, debruijn: ty::DebruijnIndex, region: I::Region, + + // Depends on `debruijn` because we may have types with regions of different + // debruijn depths depending on the binders we've entered. + cache: DelayedMap<(ty::DebruijnIndex, I::Ty), I::Ty>, } impl TypeFolder for FoldEscapingRegions { @@ -505,6 +514,18 @@ impl TypeFolder for FoldEscapingRegions { self.interner } + fn fold_ty(&mut self, t: I::Ty) -> I::Ty { + if !t.has_vars_bound_at_or_above(self.debruijn) { + t + } else if let Some(&t) = self.cache.get(&(self.debruijn, t)) { + t + } else { + let res = t.super_fold_with(self); + assert!(self.cache.insert((self.debruijn, t), res)); + res + } + } + fn fold_binder(&mut self, t: ty::Binder) -> ty::Binder where T: TypeFoldable, diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 71c3646498b9f..a12e9856304a9 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -43,13 +43,14 @@ use std::fmt; use std::ops::ControlFlow; +use std::sync::Arc; use rustc_ast_ir::visit::VisitorResult; use rustc_ast_ir::{try_visit, walk_visitable_list}; use rustc_index::{Idx, IndexVec}; +use smallvec::SmallVec; use thin_vec::ThinVec; -use crate::data_structures::Lrc; use crate::inherent::*; use crate::{self as ty, Interner, TypeFlags}; @@ -58,7 +59,7 @@ use crate::{self as ty, Interner, TypeFlags}; /// /// To implement this conveniently, use the derive macro located in /// `rustc_macros`. -pub trait TypeVisitable: fmt::Debug + Clone { +pub trait TypeVisitable: fmt::Debug { /// The entry point for visiting. To visit a value `t` with a visitor `v` /// call: `t.visit_with(v)`. /// @@ -166,7 +167,7 @@ impl, E: TypeVisitable> TypeVisitable for } } -impl> TypeVisitable for Lrc { +impl> TypeVisitable for Arc { fn visit_with>(&self, visitor: &mut V) -> V::Result { (**self).visit_with(visitor) } @@ -192,6 +193,13 @@ impl> TypeVisitable for ThinVec { } } +impl, const N: usize> TypeVisitable for SmallVec<[T; N]> { + fn visit_with>(&self, visitor: &mut V) -> V::Result { + walk_visitable_list!(visitor, self.iter()); + V::Result::output() + } +} + // `TypeFoldable` isn't impl'd for `&[T]`. It doesn't make sense in the general // case, because we can't return a new slice. But note that there are a couple // of trivial impls of `TypeFoldable` for specific slice types elsewhere. diff --git a/compiler/rustc_type_ir_macros/src/lib.rs b/compiler/rustc_type_ir_macros/src/lib.rs index aaf69e2648d5c..ede6dcd469cce 100644 --- a/compiler/rustc_type_ir_macros/src/lib.rs +++ b/compiler/rustc_type_ir_macros/src/lib.rs @@ -57,15 +57,18 @@ fn type_visitable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Tok }); s.bind_with(|_| synstructure::BindStyle::Move); - s.bound_impl(quote!(::rustc_type_ir::visit::TypeVisitable), quote! { - fn visit_with<__V: ::rustc_type_ir::visit::TypeVisitor>( - &self, - __visitor: &mut __V - ) -> __V::Result { - match *self { #body_visit } - <__V::Result as ::rustc_ast_ir::visit::VisitorResult>::output() - } - }) + s.bound_impl( + quote!(::rustc_type_ir::visit::TypeVisitable), + quote! { + fn visit_with<__V: ::rustc_type_ir::visit::TypeVisitor>( + &self, + __visitor: &mut __V + ) -> __V::Result { + match *self { #body_visit } + <__V::Result as ::rustc_ast_ir::visit::VisitorResult>::output() + } + }, + ) } fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { @@ -101,14 +104,17 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke // to generate code for them. s.filter(|bi| !has_ignore_attr(&bi.ast().attrs, "type_foldable", "identity")); s.add_bounds(synstructure::AddBounds::Fields); - s.bound_impl(quote!(::rustc_type_ir::fold::TypeFoldable), quote! { - fn try_fold_with<__F: ::rustc_type_ir::fold::FallibleTypeFolder>( - self, - __folder: &mut __F - ) -> Result { - Ok(match self { #body_fold }) - } - }) + s.bound_impl( + quote!(::rustc_type_ir::fold::TypeFoldable), + quote! { + fn try_fold_with<__F: ::rustc_type_ir::fold::FallibleTypeFolder>( + self, + __folder: &mut __F + ) -> Result { + Ok(match self { #body_fold }) + } + }, + ) } fn lift_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { @@ -148,16 +154,19 @@ fn lift_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { let self_ty: syn::Type = parse_quote! { #name #ty_generics }; let lifted_ty = lift(self_ty); - s.bound_impl(quote!(::rustc_type_ir::lift::Lift), quote! { - type Lifted = #lifted_ty; + s.bound_impl( + quote!(::rustc_type_ir::lift::Lift), + quote! { + type Lifted = #lifted_ty; - fn lift_to_interner( - self, - interner: J, - ) -> Option { - Some(match self { #body_fold }) - } - }) + fn lift_to_interner( + self, + interner: J, + ) -> Option { + Some(match self { #body_fold }) + } + }, + ) } fn lift(mut ty: syn::Type) -> syn::Type { diff --git a/compiler/stable_mir/src/abi.rs b/compiler/stable_mir/src/abi.rs index 17e6a852022d7..861b6692b5367 100644 --- a/compiler/stable_mir/src/abi.rs +++ b/compiler/stable_mir/src/abi.rs @@ -442,6 +442,8 @@ pub enum CallConvention { PtxKernel, + GpuKernel, + X86Fastcall, X86Intr, X86Stdcall, diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index dfd090b39563f..bc2e427f50cb0 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -251,6 +251,7 @@ pub enum AssertMessage { ResumedAfterReturn(CoroutineKind), ResumedAfterPanic(CoroutineKind), MisalignedPointerDereference { required: Operand, found: Operand }, + NullPointerDereference, } impl AssertMessage { @@ -306,6 +307,7 @@ impl AssertMessage { AssertMessage::MisalignedPointerDereference { .. } => { Ok("misaligned pointer dereference") } + AssertMessage::NullPointerDereference => Ok("null pointer dereference occurred"), } } } @@ -457,7 +459,7 @@ pub enum Rvalue { /// /// This is generated by pointer casts like `&v as *const _` or raw address of expressions like /// `&raw v` or `addr_of!(v)`. - AddressOf(Mutability, Place), + AddressOf(RawPtrKind, Place), /// Creates an aggregate value, like a tuple or struct. /// @@ -577,7 +579,7 @@ impl Rvalue { } Rvalue::AddressOf(mutability, place) => { let place_ty = place.ty(locals)?; - Ok(Ty::new_ptr(place_ty, *mutability)) + Ok(Ty::new_ptr(place_ty, mutability.to_mutable_lossy())) } Rvalue::Len(..) => Ok(Ty::usize_ty()), Rvalue::Cast(.., ty) => Ok(*ty), @@ -606,7 +608,8 @@ impl Rvalue { Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { Ok(Ty::usize_ty()) } - Rvalue::NullaryOp(NullOp::UbChecks, _) => Ok(Ty::bool_ty()), + Rvalue::NullaryOp(NullOp::ContractChecks, _) + | Rvalue::NullaryOp(NullOp::UbChecks, _) => Ok(Ty::bool_ty()), Rvalue::Aggregate(ak, ops) => match *ak { AggregateKind::Array(ty) => Ty::try_new_array(ty, ops.len() as u64), AggregateKind::Tuple => Ok(Ty::new_tuple( @@ -903,6 +906,24 @@ impl BorrowKind { } } +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum RawPtrKind { + Mut, + Const, + FakeForPtrMetadata, +} + +impl RawPtrKind { + pub fn to_mutable_lossy(self) -> Mutability { + match self { + RawPtrKind::Mut { .. } => Mutability::Mut, + RawPtrKind::Const => Mutability::Not, + // FIXME: There's no type corresponding to a shallow borrow, so use `&` as an approximation. + RawPtrKind::FakeForPtrMetadata => Mutability::Not, + } + } +} + #[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] pub enum MutBorrowKind { Default, @@ -987,6 +1008,8 @@ pub enum NullOp { OffsetOf(Vec<(VariantIdx, FieldIdx)>), /// cfg!(ub_checks), but at codegen time UbChecks, + /// cfg!(contract_checks), but at codegen time + ContractChecks, } impl Operand { diff --git a/compiler/stable_mir/src/mir/pretty.rs b/compiler/stable_mir/src/mir/pretty.rs index 93ed32e258a8b..8278afb7a2f17 100644 --- a/compiler/stable_mir/src/mir/pretty.rs +++ b/compiler/stable_mir/src/mir/pretty.rs @@ -6,7 +6,9 @@ use std::{fmt, io, iter}; use fmt::{Display, Formatter}; use super::{AggregateKind, AssertMessage, BinOp, BorrowKind, FakeBorrowKind, TerminatorKind}; -use crate::mir::{Operand, Place, Rvalue, StatementKind, UnwindAction, VarDebugInfoContents}; +use crate::mir::{ + Operand, Place, RawPtrKind, Rvalue, StatementKind, UnwindAction, VarDebugInfoContents, +}; use crate::ty::{AdtKind, IndexedVal, MirConst, Ty, TyConst}; use crate::{Body, CrateDef, Mutability, with}; @@ -296,6 +298,9 @@ fn pretty_assert_message(writer: &mut W, msg: &AssertMessage) -> io::R "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\",{pretty_required}, {pretty_found}" ) } + AssertMessage::NullPointerDereference => { + write!(writer, "\"null pointer dereference occurred\"") + } AssertMessage::ResumedAfterReturn(_) | AssertMessage::ResumedAfterPanic(_) => { write!(writer, "{}", msg.description().unwrap()) } @@ -325,7 +330,7 @@ fn pretty_ty_const(ct: &TyConst) -> String { fn pretty_rvalue(writer: &mut W, rval: &Rvalue) -> io::Result<()> { match rval { Rvalue::AddressOf(mutability, place) => { - write!(writer, "&raw {} {:?}", pretty_mut(*mutability), place) + write!(writer, "&raw {} {:?}", pretty_raw_ptr_kind(*mutability), place) } Rvalue::Aggregate(aggregate_kind, operands) => { // FIXME: Add pretty_aggregate function that returns a pretty string @@ -437,3 +442,11 @@ fn pretty_mut(mutability: Mutability) -> &'static str { Mutability::Mut => "mut ", } } + +fn pretty_raw_ptr_kind(kind: RawPtrKind) -> &'static str { + match kind { + RawPtrKind::Const => "const", + RawPtrKind::Mut => "mut", + RawPtrKind::FakeForPtrMetadata => "const (fake)", + } +} diff --git a/compiler/stable_mir/src/mir/visit.rs b/compiler/stable_mir/src/mir/visit.rs index d73e79c48937e..d985a98fcbf01 100644 --- a/compiler/stable_mir/src/mir/visit.rs +++ b/compiler/stable_mir/src/mir/visit.rs @@ -308,7 +308,7 @@ pub trait MirVisitor { fn super_rvalue(&mut self, rvalue: &Rvalue, location: Location) { match rvalue { Rvalue::AddressOf(mutability, place) => { - let pcx = PlaceContext { is_mut: *mutability == Mutability::Mut }; + let pcx = PlaceContext { is_mut: *mutability == RawPtrKind::Mut }; self.visit_place(place, pcx, location); } Rvalue::Aggregate(_, operands) => { @@ -426,7 +426,10 @@ pub trait MirVisitor { | AssertMessage::RemainderByZero(op) => { self.visit_operand(op, location); } - AssertMessage::ResumedAfterReturn(_) | AssertMessage::ResumedAfterPanic(_) => { //nothing to visit + AssertMessage::ResumedAfterReturn(_) + | AssertMessage::ResumedAfterPanic(_) + | AssertMessage::NullPointerDereference => { + //nothing to visit } AssertMessage::MisalignedPointerDereference { required, found } => { self.visit_operand(required, location); diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index d7eb435e13f40..9ef80edb82a6e 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -489,7 +489,7 @@ impl TyKind { /// Returns the type and mutability of `*ty` for builtin types. /// /// The parameter `explicit` indicates if this is an *explicit* dereference. - /// Some types -- notably unsafe ptrs -- can only be dereferenced explicitly. + /// Some types -- notably raw ptrs -- can only be dereferenced explicitly. pub fn builtin_deref(&self, explicit: bool) -> Option { match self.rigid()? { RigidTy::Adt(def, args) if def.is_box() => { @@ -1077,6 +1077,7 @@ pub enum Abi { PtxKernel, Msp430Interrupt, X86Interrupt, + GpuKernel, EfiApi, AvrInterrupt, AvrNonBlockingInterrupt, diff --git a/config.example.toml b/config.example.toml index 5ea6774ce035c..f5395375afe4c 100644 --- a/config.example.toml +++ b/config.example.toml @@ -87,10 +87,6 @@ # Whether to build LLVM with support for it's gpu offload runtime. #offload = false -# Indicates whether ccache is used when building LLVM. Set to `true` to use the first `ccache` in -# PATH, or set an absolute path to use a specific version. -#ccache = false - # When true, link libstdc++ statically into the rustc_llvm. # This is useful if you don't want to use the dynamic version of that # library provided by LLVM. @@ -108,7 +104,7 @@ # the resulting rustc being unable to compile for the disabled architectures. # # To add support for new targets, see https://rustc-dev-guide.rust-lang.org/building/new-target.html. -#targets = "AArch64;ARM;BPF;Hexagon;LoongArch;MSP430;Mips;NVPTX;PowerPC;RISCV;Sparc;SystemZ;WebAssembly;X86" +#targets = "AArch64;AMDGPU;ARM;BPF;Hexagon;LoongArch;MSP430;Mips;NVPTX;PowerPC;RISCV;Sparc;SystemZ;WebAssembly;X86" # LLVM experimental targets to build support for. These targets are specified in # the same format as above, but since these targets are experimental, they are @@ -323,7 +319,7 @@ #full-bootstrap = false # Set the bootstrap/download cache path. It is useful when building rust -# repeatedly in a CI invironment. +# repeatedly in a CI environment. #bootstrap-cache-path = /path/to/shared/cache # Enable a build of the extended Rust tool set which is not only the compiler @@ -424,6 +420,11 @@ # What custom diff tool to use for displaying compiletest tests. #compiletest-diff-tool = +# Indicates whether ccache is used when building certain artifacts (e.g. LLVM). +# Set to `true` to use the first `ccache` in PATH, or set an absolute path to use +# a specific version. +#ccache = false + # ============================================================================= # General install configuration options # ============================================================================= @@ -595,11 +596,6 @@ # Whether to always use incremental compilation when building rustc #incremental = false -# Build a multi-threaded rustc. This allows users to use parallel rustc -# via the unstable option `-Z threads=n`. -# This option is deprecated and always true. -#parallel-compiler = true - # The default linker that will be hard-coded into the generated # compiler for targets that don't specify a default linker explicitly # in their target specifications. Note that this is not the linker @@ -922,6 +918,15 @@ # argument as the test binary. #runner = (string) +# Use the optimized LLVM C intrinsics for `compiler_builtins`, rather than Rust intrinsics +# on this target. +# Requires the LLVM submodule to be managed by bootstrap (i.e. not external) so that `compiler-rt` +# sources are available. +# +# Setting this to `false` generates slower code, but removes the requirement for a C toolchain in +# order to run `x check`. +#optimized-compiler-builtins = build.optimized-compiler-builtins (bool) + # ============================================================================= # Distribution options # diff --git a/library/Cargo.lock b/library/Cargo.lock index 40edd2c211cd3..0be2f9a154939 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -4,21 +4,21 @@ version = 4 [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "compiler_builtins", - "gimli 0.29.0", + "gimli", "rustc-std-workspace-alloc", "rustc-std-workspace-core", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" dependencies = [ "compiler_builtins", "rustc-std-workspace-core", @@ -61,9 +61,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.140" +version = "0.1.146" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df14d41c5d172a886df3753d54238eefb0f61c96cbd8b363c33ccc92c457bee3" +checksum = "a97117b1434b79833f39a5fabdf82f890bd98c1988334dea1cb67f7e627fa311" dependencies = [ "cc", "rustc-std-workspace-core", @@ -72,6 +72,10 @@ dependencies = [ [[package]] name = "core" version = "0.0.0" + +[[package]] +name = "coretests" +version = "0.0.0" dependencies = [ "rand", "rand_xorshift", @@ -111,17 +115,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "gimli" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] - [[package]] name = "gimli" version = "0.31.1" @@ -177,11 +170,11 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ - "adler", + "adler2", "compiler_builtins", "rustc-std-workspace-alloc", "rustc-std-workspace-core", @@ -222,6 +215,15 @@ dependencies = [ "unwind", ] +[[package]] +name = "proc-macro2" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +dependencies = [ + "unicode-ident", +] + [[package]] name = "proc_macro" version = "0.0.0" @@ -237,6 +239,15 @@ dependencies = [ "cc", ] +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + [[package]] name = "r-efi" version = "4.5.0" @@ -260,24 +271,28 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.5" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" dependencies = [ "rand_core", + "zerocopy", ] [[package]] name = "rand_core" -version = "0.6.4" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff" +dependencies = [ + "zerocopy", +] [[package]] name = "rand_xorshift" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" dependencies = [ "rand_core", ] @@ -359,6 +374,17 @@ dependencies = [ "rustc-std-workspace-core", ] +[[package]] +name = "syn" +version = "2.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "sysroot" version = "0.0.0" @@ -379,6 +405,12 @@ dependencies = [ "std", ] +[[package]] +name = "unicode-ident" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" + [[package]] name = "unicode-width" version = "0.1.14" @@ -408,7 +440,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51f06a05848f650946acef3bf525fe96612226b61f74ae23ffa4e98bfbb8ab3c" dependencies = [ "compiler_builtins", - "gimli 0.31.1", + "gimli", "rustc-std-workspace-core", ] @@ -499,3 +531,23 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zerocopy" +version = "0.8.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa91407dacce3a68c56de03abe2760159582b846c6a4acd2f456618087f12713" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06718a168365cad3d5ff0bb133aad346959a2074bd4a85c121255a11304a8626" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/library/Cargo.toml b/library/Cargo.toml index e744cfe5e0f57..1205f7c9ed6b5 100644 --- a/library/Cargo.toml +++ b/library/Cargo.toml @@ -3,6 +3,7 @@ resolver = "1" members = [ "std", "sysroot", + "coretests", ] exclude = [ @@ -32,7 +33,7 @@ codegen-units = 10000 [profile.release.package] addr2line.debug = 0 addr2line.opt-level = "s" -adler.debug = 0 +adler2.debug = 0 gimli.debug = 0 gimli.opt-level = "s" miniz_oxide.debug = 0 diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index 07596fa16f982..c1d7f324f1770 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -10,11 +10,11 @@ edition = "2021" [dependencies] core = { path = "../core" } -compiler_builtins = { version = "=0.1.140", features = ['rustc-dep-of-std'] } +compiler_builtins = { version = "=0.1.146", features = ['rustc-dep-of-std'] } [dev-dependencies] -rand = { version = "0.8.5", default-features = false, features = ["alloc"] } -rand_xorshift = "0.3.0" +rand = { version = "0.9.0", default-features = false, features = ["alloc"] } +rand_xorshift = "0.4.0" [[test]] name = "alloctests" diff --git a/library/alloc/benches/btree/map.rs b/library/alloc/benches/btree/map.rs index b8119c9f0ebf4..20f02dc3a968a 100644 --- a/library/alloc/benches/btree/map.rs +++ b/library/alloc/benches/btree/map.rs @@ -9,19 +9,19 @@ macro_rules! map_insert_rand_bench { ($name: ident, $n: expr, $map: ident) => { #[bench] pub fn $name(b: &mut Bencher) { - let n: usize = $n; + let n: u32 = $n; let mut map = $map::new(); // setup let mut rng = crate::bench_rng(); for _ in 0..n { - let i = rng.gen::() % n; + let i = rng.random::() % n; map.insert(i, i); } // measure b.iter(|| { - let k = rng.gen::() % n; + let k = rng.random::() % n; map.insert(k, k); map.remove(&k); }); @@ -57,13 +57,13 @@ macro_rules! map_from_iter_rand_bench { ($name: ident, $n: expr, $map: ident) => { #[bench] pub fn $name(b: &mut Bencher) { - let n: usize = $n; + let n: u32 = $n; // setup let mut rng = crate::bench_rng(); - let mut vec = Vec::with_capacity(n); + let mut vec = Vec::with_capacity(n as usize); for _ in 0..n { - let i = rng.gen::() % n; + let i = rng.random::() % n; vec.push((i, i)); } @@ -102,11 +102,11 @@ macro_rules! map_find_rand_bench { #[bench] pub fn $name(b: &mut Bencher) { let mut map = $map::new(); - let n: usize = $n; + let n: u32 = $n; // setup let mut rng = crate::bench_rng(); - let mut keys: Vec<_> = (0..n).map(|_| rng.gen::() % n).collect(); + let mut keys: Vec<_> = (0..n).map(|_| rng.random::() % n).collect(); for &k in &keys { map.insert(k, k); @@ -115,9 +115,9 @@ macro_rules! map_find_rand_bench { keys.shuffle(&mut rng); // measure - let mut i = 0; + let mut i = 0u32; b.iter(|| { - let t = map.get(&keys[i]); + let t = map.get(&keys[i as usize]); i = (i + 1) % n; black_box(t); }) @@ -171,7 +171,7 @@ fn bench_iteration(b: &mut Bencher, size: i32) { let mut rng = crate::bench_rng(); for _ in 0..size { - map.insert(rng.gen(), rng.gen()); + map.insert(rng.random(), rng.random()); } b.iter(|| { @@ -201,7 +201,7 @@ fn bench_iteration_mut(b: &mut Bencher, size: i32) { let mut rng = crate::bench_rng(); for _ in 0..size { - map.insert(rng.gen(), rng.gen()); + map.insert(rng.random(), rng.random()); } b.iter(|| { @@ -353,6 +353,7 @@ pub fn iter_10k(b: &mut Bencher) { } #[bench] +#[cfg_attr(target_os = "emscripten", ignore)] // hits an OOM pub fn iter_1m(b: &mut Bencher) { bench_iter(b, 1_000, 1_000_000); } diff --git a/library/alloc/benches/btree/set.rs b/library/alloc/benches/btree/set.rs index 09d72c7206469..5aa395b4d52ac 100644 --- a/library/alloc/benches/btree/set.rs +++ b/library/alloc/benches/btree/set.rs @@ -3,13 +3,13 @@ use std::collections::BTreeSet; use rand::Rng; use test::Bencher; -fn random(n: usize) -> BTreeSet { +fn random(n: u32) -> BTreeSet { let mut rng = crate::bench_rng(); let mut set = BTreeSet::new(); - while set.len() < n { - set.insert(rng.gen()); + while set.len() < n as usize { + set.insert(rng.random()); } - assert_eq!(set.len(), n); + assert_eq!(set.len(), n as usize); set } diff --git a/library/alloc/benches/slice.rs b/library/alloc/benches/slice.rs index 48c74c4491dc8..c6b46e6a2a188 100644 --- a/library/alloc/benches/slice.rs +++ b/library/alloc/benches/slice.rs @@ -1,7 +1,7 @@ use std::{mem, ptr}; use rand::Rng; -use rand::distributions::{Alphanumeric, DistString, Standard}; +use rand::distr::{Alphanumeric, SampleString, StandardUniform}; use test::{Bencher, black_box}; #[bench] @@ -156,7 +156,7 @@ fn random_inserts(b: &mut Bencher) { let mut v = vec![(0, 0); 30]; for _ in 0..100 { let l = v.len(); - v.insert(rng.gen::() % (l + 1), (1, 1)); + v.insert(rng.random::() as usize % (l + 1), (1, 1)); } }) } @@ -168,7 +168,7 @@ fn random_removes(b: &mut Bencher) { let mut v = vec![(0, 0); 130]; for _ in 0..100 { let l = v.len(); - v.remove(rng.gen::() % l); + v.remove(rng.random::() as usize % l); } }) } @@ -183,20 +183,20 @@ fn gen_descending(len: usize) -> Vec { fn gen_random(len: usize) -> Vec { let mut rng = crate::bench_rng(); - (&mut rng).sample_iter(&Standard).take(len).collect() + (&mut rng).sample_iter(&StandardUniform).take(len).collect() } fn gen_random_bytes(len: usize) -> Vec { let mut rng = crate::bench_rng(); - (&mut rng).sample_iter(&Standard).take(len).collect() + (&mut rng).sample_iter(&StandardUniform).take(len).collect() } fn gen_mostly_ascending(len: usize) -> Vec { let mut rng = crate::bench_rng(); let mut v = gen_ascending(len); for _ in (0usize..).take_while(|x| x * x <= len) { - let x = rng.gen::() % len; - let y = rng.gen::() % len; + let x = rng.random::() as usize % len; + let y = rng.random::() as usize % len; v.swap(x, y); } v @@ -206,8 +206,8 @@ fn gen_mostly_descending(len: usize) -> Vec { let mut rng = crate::bench_rng(); let mut v = gen_descending(len); for _ in (0usize..).take_while(|x| x * x <= len) { - let x = rng.gen::() % len; - let y = rng.gen::() % len; + let x = rng.random::() as usize % len; + let y = rng.random::() as usize % len; v.swap(x, y); } v @@ -217,15 +217,15 @@ fn gen_strings(len: usize) -> Vec { let mut rng = crate::bench_rng(); let mut v = vec![]; for _ in 0..len { - let n = rng.gen::() % 20 + 1; - v.push(Alphanumeric.sample_string(&mut rng, n)); + let n = rng.random::() % 20 + 1; + v.push(Alphanumeric.sample_string(&mut rng, n as usize)); } v } fn gen_big_random(len: usize) -> Vec<[u64; 16]> { let mut rng = crate::bench_rng(); - (&mut rng).sample_iter(&Standard).map(|x| [x; 16]).take(len).collect() + (&mut rng).sample_iter(&StandardUniform).map(|x| [x; 16]).take(len).collect() } macro_rules! sort { @@ -366,14 +366,25 @@ rotate!(rotate_medium_half, gen_random, 9158, 9158 / 2); rotate!(rotate_medium_half_plus_one, gen_random, 9158, 9158 / 2 + 1); // Intended to use more RAM than the machine has cache +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by1, gen_random, 5 * 1024 * 1024, 1); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by9199_u64, gen_random, 5 * 1024 * 1024, 9199); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by9199_bytes, gen_random_bytes, 5 * 1024 * 1024, 9199); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by9199_strings, gen_strings, 5 * 1024 * 1024, 9199); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by9199_big, gen_big_random, 5 * 1024 * 1024, 9199); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by1234577_u64, gen_random, 5 * 1024 * 1024, 1234577); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by1234577_bytes, gen_random_bytes, 5 * 1024 * 1024, 1234577); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by1234577_strings, gen_strings, 5 * 1024 * 1024, 1234577); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_by1234577_big, gen_big_random, 5 * 1024 * 1024, 1234577); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_half, gen_random, 5 * 1024 * 1024, 5 * 1024 * 1024 / 2); +#[cfg(not(target_os = "emscripten"))] // hits an OOM rotate!(rotate_huge_half_plus_one, gen_random, 5 * 1024 * 1024, 5 * 1024 * 1024 / 2 + 1); diff --git a/library/alloc/benches/vec.rs b/library/alloc/benches/vec.rs index d29ffae9d70b1..a725ad6894b9c 100644 --- a/library/alloc/benches/vec.rs +++ b/library/alloc/benches/vec.rs @@ -547,6 +547,11 @@ fn bench_in_place_collect_droppable(b: &mut Bencher) { }) } +// node.js gives out of memory error to use with length 1_100_000 +#[cfg(target_os = "emscripten")] +const LEN: usize = 4096; + +#[cfg(not(target_os = "emscripten"))] const LEN: usize = 16384; #[bench] diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index ae34fc653260e..e686a02f29b0c 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -10,7 +10,7 @@ use core::hint; #[cfg(not(test))] use core::ptr::{self, NonNull}; -extern "Rust" { +unsafe extern "Rust" { // These are the magic symbols to call the global allocator. rustc generates // them to call `__rg_alloc` etc. if there is a `#[global_allocator]` attribute // (the code expanding that attribute macro generates those functions), or to call @@ -339,7 +339,7 @@ unsafe impl Allocator for Global { } } -/// The allocator for unique pointers. +/// The allocator for `Box`. #[cfg(all(not(no_global_oom_handling), not(test)))] #[lang = "exchange_malloc"] #[inline] @@ -355,7 +355,7 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { // # Allocation error handler #[cfg(not(no_global_oom_handling))] -extern "Rust" { +unsafe extern "Rust" { // This is the magic symbol to call the global alloc error handler. rustc generates // it to call `__rg_oom` if there is a `#[alloc_error_handler]`, or to call the // default implementations below (`__rdl_oom`) otherwise. @@ -426,7 +426,7 @@ pub mod __alloc_error_handler { // `#[alloc_error_handler]`. #[rustc_std_internal_symbol] pub unsafe fn __rdl_oom(size: usize, _align: usize) -> ! { - extern "Rust" { + unsafe extern "Rust" { // This symbol is emitted by rustc next to __rust_alloc_error_handler. // Its value depends on the -Zoom={panic,abort} compiler option. static __rust_alloc_error_handler_should_panic: u8; diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 05e5d712a2737..1ea7b731461e7 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -24,7 +24,7 @@ //! Creating a recursive data structure: //! //! ``` -//! ##[allow(dead_code)] +//! # #[allow(dead_code)] //! #[derive(Debug)] //! enum List { //! Cons(T, Box>), @@ -97,12 +97,12 @@ //! #[repr(C)] //! pub struct Foo; //! -//! #[no_mangle] +//! #[unsafe(no_mangle)] //! pub extern "C" fn foo_new() -> Box { //! Box::new(Foo) //! } //! -//! #[no_mangle] +//! #[unsafe(no_mangle)] //! pub extern "C" fn foo_delete(_: Option>) {} //! ``` //! @@ -233,6 +233,27 @@ pub struct Box< #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, >(Unique, A); +/// Constructs a `Box` by calling the `exchange_malloc` lang item and moving the argument into +/// the newly allocated memory. This is an intrinsic to avoid unnecessary copies. +/// +/// This is the surface syntax for `box ` expressions. +#[cfg(not(bootstrap))] +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[unstable(feature = "liballoc_internals", issue = "none")] +pub fn box_new(_x: T) -> Box { + unreachable!() +} + +/// Transition function for the next bootstrap bump. +#[cfg(bootstrap)] +#[unstable(feature = "liballoc_internals", issue = "none")] +#[inline(always)] +pub fn box_new(x: T) -> Box { + #[rustc_box] + Box::new(x) +} + impl Box { /// Allocates memory on the heap and then places `x` into it. /// @@ -250,8 +271,7 @@ impl Box { #[rustc_diagnostic_item = "box_new"] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn new(x: T) -> Self { - #[rustc_box] - Box::new(x) + return box_new(x); } /// Constructs a new box with uninitialized contents. @@ -260,13 +280,9 @@ impl Box { /// /// ``` /// let mut five = Box::::new_uninit(); - /// - /// let five = unsafe { - /// // Deferred initialization: - /// five.as_mut_ptr().write(5); - /// - /// five.assume_init() - /// }; + /// // Deferred initialization: + /// five.write(5); + /// let five = unsafe { five.assume_init() }; /// /// assert_eq!(*five, 5) /// ``` @@ -347,13 +363,9 @@ impl Box { /// #![feature(allocator_api)] /// /// let mut five = Box::::try_new_uninit()?; - /// - /// let five = unsafe { - /// // Deferred initialization: - /// five.as_mut_ptr().write(5); - /// - /// five.assume_init() - /// }; + /// // Deferred initialization: + /// five.write(5); + /// let five = unsafe { five.assume_init() }; /// /// assert_eq!(*five, 5); /// # Ok::<(), std::alloc::AllocError>(()) @@ -415,10 +427,8 @@ impl Box { A: Allocator, { let mut boxed = Self::new_uninit_in(alloc); - unsafe { - boxed.as_mut_ptr().write(x); - boxed.assume_init() - } + boxed.write(x); + unsafe { boxed.assume_init() } } /// Allocates memory in the given allocator then places `x` into it, @@ -443,10 +453,8 @@ impl Box { A: Allocator, { let mut boxed = Self::try_new_uninit_in(alloc)?; - unsafe { - boxed.as_mut_ptr().write(x); - Ok(boxed.assume_init()) - } + boxed.write(x); + unsafe { Ok(boxed.assume_init()) } } /// Constructs a new box with uninitialized contents in the provided allocator. @@ -459,13 +467,9 @@ impl Box { /// use std::alloc::System; /// /// let mut five = Box::::new_uninit_in(System); - /// - /// let five = unsafe { - /// // Deferred initialization: - /// five.as_mut_ptr().write(5); - /// - /// five.assume_init() - /// }; + /// // Deferred initialization: + /// five.write(5); + /// let five = unsafe { five.assume_init() }; /// /// assert_eq!(*five, 5) /// ``` @@ -497,13 +501,9 @@ impl Box { /// use std::alloc::System; /// /// let mut five = Box::::try_new_uninit_in(System)?; - /// - /// let five = unsafe { - /// // Deferred initialization: - /// five.as_mut_ptr().write(5); - /// - /// five.assume_init() - /// }; + /// // Deferred initialization: + /// five.write(5); + /// let five = unsafe { five.assume_init() }; /// /// assert_eq!(*five, 5); /// # Ok::<(), std::alloc::AllocError>(()) @@ -649,15 +649,11 @@ impl Box<[T]> { /// /// ``` /// let mut values = Box::<[u32]>::new_uninit_slice(3); - /// - /// let values = unsafe { - /// // Deferred initialization: - /// values[0].as_mut_ptr().write(1); - /// values[1].as_mut_ptr().write(2); - /// values[2].as_mut_ptr().write(3); - /// - /// values.assume_init() - /// }; + /// // Deferred initialization: + /// values[0].write(1); + /// values[1].write(2); + /// values[2].write(3); + /// let values = unsafe {values.assume_init() }; /// /// assert_eq!(*values, [1, 2, 3]) /// ``` @@ -702,13 +698,11 @@ impl Box<[T]> { /// #![feature(allocator_api)] /// /// let mut values = Box::<[u32]>::try_new_uninit_slice(3)?; - /// let values = unsafe { - /// // Deferred initialization: - /// values[0].as_mut_ptr().write(1); - /// values[1].as_mut_ptr().write(2); - /// values[2].as_mut_ptr().write(3); - /// values.assume_init() - /// }; + /// // Deferred initialization: + /// values[0].write(1); + /// values[1].write(2); + /// values[2].write(3); + /// let values = unsafe { values.assume_init() }; /// /// assert_eq!(*values, [1, 2, 3]); /// # Ok::<(), std::alloc::AllocError>(()) @@ -794,15 +788,11 @@ impl Box<[T], A> { /// use std::alloc::System; /// /// let mut values = Box::<[u32], _>::new_uninit_slice_in(3, System); - /// - /// let values = unsafe { - /// // Deferred initialization: - /// values[0].as_mut_ptr().write(1); - /// values[1].as_mut_ptr().write(2); - /// values[2].as_mut_ptr().write(3); - /// - /// values.assume_init() - /// }; + /// // Deferred initialization: + /// values[0].write(1); + /// values[1].write(2); + /// values[2].write(3); + /// let values = unsafe { values.assume_init() }; /// /// assert_eq!(*values, [1, 2, 3]) /// ``` @@ -853,13 +843,11 @@ impl Box<[T], A> { /// use std::alloc::System; /// /// let mut values = Box::<[u32], _>::try_new_uninit_slice_in(3, System)?; - /// let values = unsafe { - /// // Deferred initialization: - /// values[0].as_mut_ptr().write(1); - /// values[1].as_mut_ptr().write(2); - /// values[2].as_mut_ptr().write(3); - /// values.assume_init() - /// }; + /// // Deferred initialization: + /// values[0].write(1); + /// values[1].write(2); + /// values[2].write(3); + /// let values = unsafe { values.assume_init() }; /// /// assert_eq!(*values, [1, 2, 3]); /// # Ok::<(), std::alloc::AllocError>(()) @@ -939,13 +927,9 @@ impl Box, A> { /// /// ``` /// let mut five = Box::::new_uninit(); - /// - /// let five: Box = unsafe { - /// // Deferred initialization: - /// five.as_mut_ptr().write(5); - /// - /// five.assume_init() - /// }; + /// // Deferred initialization: + /// five.write(5); + /// let five: Box = unsafe { five.assume_init() }; /// /// assert_eq!(*five, 5) /// ``` @@ -1010,15 +994,11 @@ impl Box<[mem::MaybeUninit], A> { /// /// ``` /// let mut values = Box::<[u32]>::new_uninit_slice(3); - /// - /// let values = unsafe { - /// // Deferred initialization: - /// values[0].as_mut_ptr().write(1); - /// values[1].as_mut_ptr().write(2); - /// values[2].as_mut_ptr().write(3); - /// - /// values.assume_init() - /// }; + /// // Deferred initialization: + /// values[0].write(1); + /// values[1].write(2); + /// values[2].write(3); + /// let values = unsafe { values.assume_init() }; /// /// assert_eq!(*values, [1, 2, 3]) /// ``` @@ -1095,6 +1075,8 @@ impl Box { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same `NonNull` pointer. /// + /// The non-null pointer must point to a block of memory allocated by the global allocator. + /// /// The safety conditions are described in the [memory layout] section. /// /// # Examples @@ -1150,7 +1132,7 @@ impl Box { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same raw pointer. /// - /// The raw pointer must point to a block of memory allocated by `alloc` + /// The raw pointer must point to a block of memory allocated by `alloc`. /// /// # Examples /// @@ -1205,6 +1187,7 @@ impl Box { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same raw pointer. /// + /// The non-null pointer must point to a block of memory allocated by `alloc`. /// /// # Examples /// @@ -2008,8 +1991,7 @@ impl + ?Sized, A: Allocator> Fn for Box { } } -#[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] -#[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] +#[stable(feature = "async_closure", since = "1.85.0")] impl + ?Sized, A: Allocator> AsyncFnOnce for Box { type Output = F::Output; type CallOnceFuture = F::CallOnceFuture; @@ -2019,8 +2001,7 @@ impl + ?Sized, A: Allocator> AsyncFnOnce } } -#[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] -#[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] +#[stable(feature = "async_closure", since = "1.85.0")] impl + ?Sized, A: Allocator> AsyncFnMut for Box { type CallRefFuture<'a> = F::CallRefFuture<'a> @@ -2032,8 +2013,7 @@ impl + ?Sized, A: Allocator> AsyncFnMut f } } -#[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] -#[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] +#[stable(feature = "async_closure", since = "1.85.0")] impl + ?Sized, A: Allocator> AsyncFn for Box { extern "rust-call" fn async_call(&self, args: Args) -> Self::CallRefFuture<'_> { F::async_call(self, args) diff --git a/library/alloc/src/bstr.rs b/library/alloc/src/bstr.rs new file mode 100644 index 0000000000000..61e61019b508c --- /dev/null +++ b/library/alloc/src/bstr.rs @@ -0,0 +1,702 @@ +//! The `ByteStr` and `ByteString` types and trait implementations. + +// This could be more fine-grained. +#![cfg(not(no_global_oom_handling))] + +use core::borrow::{Borrow, BorrowMut}; +#[unstable(feature = "bstr", issue = "134915")] +pub use core::bstr::ByteStr; +use core::bstr::{impl_partial_eq, impl_partial_eq_n, impl_partial_eq_ord}; +use core::cmp::Ordering; +use core::ops::{ + Deref, DerefMut, DerefPure, Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, + RangeTo, RangeToInclusive, +}; +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +use core::str::FromStr; +use core::{fmt, hash}; + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +use crate::borrow::{Cow, ToOwned}; +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +use crate::boxed::Box; +#[cfg(not(no_rc))] +use crate::rc::Rc; +use crate::string::String; +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +use crate::sync::Arc; +use crate::vec::Vec; + +/// A wrapper for `Vec` representing a human-readable string that's conventionally, but not +/// always, UTF-8. +/// +/// Unlike `String`, this type permits non-UTF-8 contents, making it suitable for user input, +/// non-native filenames (as `Path` only supports native filenames), and other applications that +/// need to round-trip whatever data the user provides. +/// +/// A `ByteString` owns its contents and can grow and shrink, like a `Vec` or `String`. For a +/// borrowed byte string, see [`ByteStr`](../../std/bstr/struct.ByteStr.html). +/// +/// `ByteString` implements `Deref` to `&Vec`, so all methods available on `&Vec` are +/// available on `ByteString`. Similarly, `ByteString` implements `DerefMut` to `&mut Vec`, +/// so you can modify a `ByteString` using any method available on `&mut Vec`. +/// +/// The `Debug` and `Display` implementations for `ByteString` are the same as those for `ByteStr`, +/// showing invalid UTF-8 as hex escapes or the Unicode replacement character, respectively. +#[unstable(feature = "bstr", issue = "134915")] +#[repr(transparent)] +#[derive(Clone)] +#[doc(alias = "BString")] +pub struct ByteString(pub Vec); + +impl ByteString { + #[inline] + pub(crate) fn as_bytes(&self) -> &[u8] { + &self.0 + } + + #[inline] + pub(crate) fn as_bytestr(&self) -> &ByteStr { + ByteStr::new(&self.0) + } + + #[inline] + pub(crate) fn as_mut_bytestr(&mut self) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Deref for ByteString { + type Target = Vec; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl DerefMut for ByteString { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +#[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl DerefPure for ByteString {} + +#[unstable(feature = "bstr", issue = "134915")] +impl fmt::Debug for ByteString { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self.as_bytestr(), f) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl fmt::Display for ByteString { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self.as_bytestr(), f) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl AsRef<[u8]> for ByteString { + #[inline] + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl AsRef for ByteString { + #[inline] + fn as_ref(&self) -> &ByteStr { + self.as_bytestr() + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl AsMut<[u8]> for ByteString { + #[inline] + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl AsMut for ByteString { + #[inline] + fn as_mut(&mut self) -> &mut ByteStr { + self.as_mut_bytestr() + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Borrow<[u8]> for ByteString { + #[inline] + fn borrow(&self) -> &[u8] { + &self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Borrow for ByteString { + #[inline] + fn borrow(&self) -> &ByteStr { + self.as_bytestr() + } +} + +// `impl Borrow for Vec` omitted to avoid inference failures +// `impl Borrow for String` omitted to avoid inference failures + +#[unstable(feature = "bstr", issue = "134915")] +impl BorrowMut<[u8]> for ByteString { + #[inline] + fn borrow_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl BorrowMut for ByteString { + #[inline] + fn borrow_mut(&mut self) -> &mut ByteStr { + self.as_mut_bytestr() + } +} + +// `impl BorrowMut for Vec` omitted to avoid inference failures + +#[unstable(feature = "bstr", issue = "134915")] +impl Default for ByteString { + fn default() -> Self { + ByteString(Vec::new()) + } +} + +// Omitted due to inference failures +// +// #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a, const N: usize> From<&'a [u8; N]> for ByteString { +// #[inline] +// fn from(s: &'a [u8; N]) -> Self { +// ByteString(s.as_slice().to_vec()) +// } +// } +// +// #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +// #[unstable(feature = "bstr", issue = "134915")] +// impl From<[u8; N]> for ByteString { +// #[inline] +// fn from(s: [u8; N]) -> Self { +// ByteString(s.as_slice().to_vec()) +// } +// } +// +// #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a> From<&'a [u8]> for ByteString { +// #[inline] +// fn from(s: &'a [u8]) -> Self { +// ByteString(s.to_vec()) +// } +// } +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl From> for ByteString { +// #[inline] +// fn from(s: Vec) -> Self { +// ByteString(s) +// } +// } + +#[unstable(feature = "bstr", issue = "134915")] +impl From for Vec { + #[inline] + fn from(s: ByteString) -> Self { + s.0 + } +} + +// Omitted due to inference failures +// +// #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a> From<&'a str> for ByteString { +// #[inline] +// fn from(s: &'a str) -> Self { +// ByteString(s.as_bytes().to_vec()) +// } +// } +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl From for ByteString { +// #[inline] +// fn from(s: String) -> Self { +// ByteString(s.into_bytes()) +// } +// } + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> From<&'a ByteStr> for ByteString { + #[inline] + fn from(s: &'a ByteStr) -> Self { + ByteString(s.0.to_vec()) + } +} + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> From for Cow<'a, ByteStr> { + #[inline] + fn from(s: ByteString) -> Self { + Cow::Owned(s) + } +} + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> From<&'a ByteString> for Cow<'a, ByteStr> { + #[inline] + fn from(s: &'a ByteString) -> Self { + Cow::Borrowed(s.as_bytestr()) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl FromIterator for ByteString { + #[inline] + fn from_iter>(iter: T) -> Self { + ByteString(iter.into_iter().collect::().into_bytes()) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl FromIterator for ByteString { + #[inline] + fn from_iter>(iter: T) -> Self { + ByteString(iter.into_iter().collect()) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> FromIterator<&'a str> for ByteString { + #[inline] + fn from_iter>(iter: T) -> Self { + ByteString(iter.into_iter().collect::().into_bytes()) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> FromIterator<&'a [u8]> for ByteString { + #[inline] + fn from_iter>(iter: T) -> Self { + let mut buf = Vec::new(); + for b in iter { + buf.extend_from_slice(b); + } + ByteString(buf) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> FromIterator<&'a ByteStr> for ByteString { + #[inline] + fn from_iter>(iter: T) -> Self { + let mut buf = Vec::new(); + for b in iter { + buf.extend_from_slice(&b.0); + } + ByteString(buf) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl FromIterator for ByteString { + #[inline] + fn from_iter>(iter: T) -> Self { + let mut buf = Vec::new(); + for mut b in iter { + buf.append(&mut b.0); + } + ByteString(buf) + } +} + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl FromStr for ByteString { + type Err = core::convert::Infallible; + + #[inline] + fn from_str(s: &str) -> Result { + Ok(ByteString(s.as_bytes().to_vec())) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index for ByteString { + type Output = u8; + + #[inline] + fn index(&self, idx: usize) -> &u8 { + &self.0[idx] + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index for ByteString { + type Output = ByteStr; + + #[inline] + fn index(&self, _: RangeFull) -> &ByteStr { + self.as_bytestr() + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index> for ByteString { + type Output = ByteStr; + + #[inline] + fn index(&self, r: Range) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index> for ByteString { + type Output = ByteStr; + + #[inline] + fn index(&self, r: RangeInclusive) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index> for ByteString { + type Output = ByteStr; + + #[inline] + fn index(&self, r: RangeFrom) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index> for ByteString { + type Output = ByteStr; + + #[inline] + fn index(&self, r: RangeTo) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index> for ByteString { + type Output = ByteStr; + + #[inline] + fn index(&self, r: RangeToInclusive) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut for ByteString { + #[inline] + fn index_mut(&mut self, idx: usize) -> &mut u8 { + &mut self.0[idx] + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut for ByteString { + #[inline] + fn index_mut(&mut self, _: RangeFull) -> &mut ByteStr { + self.as_mut_bytestr() + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut> for ByteString { + #[inline] + fn index_mut(&mut self, r: Range) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut> for ByteString { + #[inline] + fn index_mut(&mut self, r: RangeInclusive) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut> for ByteString { + #[inline] + fn index_mut(&mut self, r: RangeFrom) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut> for ByteString { + #[inline] + fn index_mut(&mut self, r: RangeTo) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut> for ByteString { + #[inline] + fn index_mut(&mut self, r: RangeToInclusive) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl hash::Hash for ByteString { + #[inline] + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Eq for ByteString {} + +#[unstable(feature = "bstr", issue = "134915")] +impl PartialEq for ByteString { + #[inline] + fn eq(&self, other: &ByteString) -> bool { + self.0 == other.0 + } +} + +macro_rules! impl_partial_eq_ord_cow { + ($lhs:ty, $rhs:ty) => { + #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl<'a> PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + let other: &[u8] = (&**other).as_ref(); + PartialEq::eq(self.as_bytes(), other) + } + } + + #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl<'a> PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + let this: &[u8] = (&**self).as_ref(); + PartialEq::eq(this, other.as_bytes()) + } + } + + #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl<'a> PartialOrd<$rhs> for $lhs { + #[inline] + fn partial_cmp(&self, other: &$rhs) -> Option { + let other: &[u8] = (&**other).as_ref(); + PartialOrd::partial_cmp(self.as_bytes(), other) + } + } + + #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl<'a> PartialOrd<$lhs> for $rhs { + #[inline] + fn partial_cmp(&self, other: &$lhs) -> Option { + let this: &[u8] = (&**self).as_ref(); + PartialOrd::partial_cmp(this, other.as_bytes()) + } + } + }; +} + +// PartialOrd with `Vec` omitted to avoid inference failures +impl_partial_eq!(ByteString, Vec); +// PartialOrd with `[u8]` omitted to avoid inference failures +impl_partial_eq!(ByteString, [u8]); +// PartialOrd with `&[u8]` omitted to avoid inference failures +impl_partial_eq!(ByteString, &[u8]); +// PartialOrd with `String` omitted to avoid inference failures +impl_partial_eq!(ByteString, String); +// PartialOrd with `str` omitted to avoid inference failures +impl_partial_eq!(ByteString, str); +// PartialOrd with `&str` omitted to avoid inference failures +impl_partial_eq!(ByteString, &str); +impl_partial_eq_ord!(ByteString, ByteStr); +impl_partial_eq_ord!(ByteString, &ByteStr); +// PartialOrd with `[u8; N]` omitted to avoid inference failures +impl_partial_eq_n!(ByteString, [u8; N]); +// PartialOrd with `&[u8; N]` omitted to avoid inference failures +impl_partial_eq_n!(ByteString, &[u8; N]); +impl_partial_eq_ord_cow!(ByteString, Cow<'_, ByteStr>); +impl_partial_eq_ord_cow!(ByteString, Cow<'_, str>); +impl_partial_eq_ord_cow!(ByteString, Cow<'_, [u8]>); + +#[unstable(feature = "bstr", issue = "134915")] +impl Ord for ByteString { + #[inline] + fn cmp(&self, other: &ByteString) -> Ordering { + Ord::cmp(&self.0, &other.0) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl PartialOrd for ByteString { + #[inline] + fn partial_cmp(&self, other: &ByteString) -> Option { + PartialOrd::partial_cmp(&self.0, &other.0) + } +} + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl ToOwned for ByteStr { + type Owned = ByteString; + + #[inline] + fn to_owned(&self) -> ByteString { + ByteString(self.0.to_vec()) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl TryFrom for String { + type Error = crate::string::FromUtf8Error; + + #[inline] + fn try_from(s: ByteString) -> Result { + String::from_utf8(s.0) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> TryFrom<&'a ByteString> for &'a str { + type Error = crate::str::Utf8Error; + + #[inline] + fn try_from(s: &'a ByteString) -> Result { + crate::str::from_utf8(s.0.as_slice()) + } +} + +// Additional impls for `ByteStr` that require types from `alloc`: + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl Clone for Box { + #[inline] + fn clone(&self) -> Self { + Self::from(Box::<[u8]>::from(&self.0)) + } +} + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> From<&'a ByteStr> for Cow<'a, ByteStr> { + #[inline] + fn from(s: &'a ByteStr) -> Self { + Cow::Borrowed(s) + } +} + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl From> for Box { + #[inline] + fn from(s: Box<[u8]>) -> Box { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`. + unsafe { Box::from_raw(Box::into_raw(s) as _) } + } +} + +#[cfg(not(test))] // https://github.com/rust-lang/rust/issues/135100 +#[unstable(feature = "bstr", issue = "134915")] +impl From> for Box<[u8]> { + #[inline] + fn from(s: Box) -> Box<[u8]> { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`. + unsafe { Box::from_raw(Box::into_raw(s) as _) } + } +} + +#[unstable(feature = "bstr", issue = "134915")] +#[cfg(not(no_rc))] +impl From> for Rc { + #[inline] + fn from(s: Rc<[u8]>) -> Rc { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`. + unsafe { Rc::from_raw(Rc::into_raw(s) as _) } + } +} + +#[unstable(feature = "bstr", issue = "134915")] +#[cfg(not(no_rc))] +impl From> for Rc<[u8]> { + #[inline] + fn from(s: Rc) -> Rc<[u8]> { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`. + unsafe { Rc::from_raw(Rc::into_raw(s) as _) } + } +} + +#[unstable(feature = "bstr", issue = "134915")] +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +impl From> for Arc { + #[inline] + fn from(s: Arc<[u8]>) -> Arc { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`. + unsafe { Arc::from_raw(Arc::into_raw(s) as _) } + } +} + +#[unstable(feature = "bstr", issue = "134915")] +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +impl From> for Arc<[u8]> { + #[inline] + fn from(s: Arc) -> Arc<[u8]> { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`. + unsafe { Arc::from_raw(Arc::into_raw(s) as _) } + } +} + +// PartialOrd with `Vec` omitted to avoid inference failures +impl_partial_eq!(ByteStr, Vec); +// PartialOrd with `String` omitted to avoid inference failures +impl_partial_eq!(ByteStr, String); +impl_partial_eq_ord_cow!(&'a ByteStr, Cow<'a, ByteStr>); +impl_partial_eq_ord_cow!(&'a ByteStr, Cow<'a, str>); +impl_partial_eq_ord_cow!(&'a ByteStr, Cow<'a, [u8]>); + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> TryFrom<&'a ByteStr> for String { + type Error = core::str::Utf8Error; + + #[inline] + fn try_from(s: &'a ByteStr) -> Result { + Ok(core::str::from_utf8(&s.0)?.into()) + } +} diff --git a/library/alloc/src/collections/btree/append.rs b/library/alloc/src/collections/btree/append.rs index d137d2721ee4f..091376d5d685b 100644 --- a/library/alloc/src/collections/btree/append.rs +++ b/library/alloc/src/collections/btree/append.rs @@ -16,7 +16,7 @@ impl Root { /// a `BTreeMap`, both iterators should produce keys in strictly ascending /// order, each greater than all keys in the tree, including any keys /// already in the tree upon entry. - pub fn append_from_sorted_iters( + pub(super) fn append_from_sorted_iters( &mut self, left: I, right: I, @@ -36,8 +36,12 @@ impl Root { /// Pushes all key-value pairs to the end of the tree, incrementing a /// `length` variable along the way. The latter makes it easier for the /// caller to avoid a leak when the iterator panicks. - pub fn bulk_push(&mut self, iter: I, length: &mut usize, alloc: A) - where + pub(super) fn bulk_push( + &mut self, + iter: I, + length: &mut usize, + alloc: A, + ) where I: Iterator, { let mut cur_node = self.borrow_mut().last_leaf_edge().into_node(); diff --git a/library/alloc/src/collections/btree/borrow.rs b/library/alloc/src/collections/btree/borrow.rs index 000b9bd0fab42..e848ac3f2d192 100644 --- a/library/alloc/src/collections/btree/borrow.rs +++ b/library/alloc/src/collections/btree/borrow.rs @@ -11,7 +11,7 @@ use core::ptr::NonNull; /// the compiler to follow. A `DormantMutRef` allows you to check borrowing /// yourself, while still expressing its stacked nature, and encapsulating /// the raw pointer code needed to do this without undefined behavior. -pub struct DormantMutRef<'a, T> { +pub(super) struct DormantMutRef<'a, T> { ptr: NonNull, _marker: PhantomData<&'a mut T>, } @@ -23,7 +23,7 @@ impl<'a, T> DormantMutRef<'a, T> { /// Capture a unique borrow, and immediately reborrow it. For the compiler, /// the lifetime of the new reference is the same as the lifetime of the /// original reference, but you promise to use it for a shorter period. - pub fn new(t: &'a mut T) -> (&'a mut T, Self) { + pub(super) fn new(t: &'a mut T) -> (&'a mut T, Self) { let ptr = NonNull::from(t); // SAFETY: we hold the borrow throughout 'a via `_marker`, and we expose // only this reference, so it is unique. @@ -37,7 +37,7 @@ impl<'a, T> DormantMutRef<'a, T> { /// /// The reborrow must have ended, i.e., the reference returned by `new` and /// all pointers and references derived from it, must not be used anymore. - pub unsafe fn awaken(self) -> &'a mut T { + pub(super) unsafe fn awaken(self) -> &'a mut T { // SAFETY: our own safety conditions imply this reference is again unique. unsafe { &mut *self.ptr.as_ptr() } } @@ -48,7 +48,7 @@ impl<'a, T> DormantMutRef<'a, T> { /// /// The reborrow must have ended, i.e., the reference returned by `new` and /// all pointers and references derived from it, must not be used anymore. - pub unsafe fn reborrow(&mut self) -> &'a mut T { + pub(super) unsafe fn reborrow(&mut self) -> &'a mut T { // SAFETY: our own safety conditions imply this reference is again unique. unsafe { &mut *self.ptr.as_ptr() } } @@ -59,7 +59,7 @@ impl<'a, T> DormantMutRef<'a, T> { /// /// The reborrow must have ended, i.e., the reference returned by `new` and /// all pointers and references derived from it, must not be used anymore. - pub unsafe fn reborrow_shared(&self) -> &'a T { + pub(super) unsafe fn reborrow_shared(&self) -> &'a T { // SAFETY: our own safety conditions imply this reference is again unique. unsafe { &*self.ptr.as_ptr() } } diff --git a/library/alloc/src/collections/btree/dedup_sorted_iter.rs b/library/alloc/src/collections/btree/dedup_sorted_iter.rs index cd6a88f329125..6bcf0bca519af 100644 --- a/library/alloc/src/collections/btree/dedup_sorted_iter.rs +++ b/library/alloc/src/collections/btree/dedup_sorted_iter.rs @@ -6,7 +6,7 @@ use core::iter::Peekable; /// Used by [`BTreeMap::bulk_build_from_sorted_iter`][1]. /// /// [1]: crate::collections::BTreeMap::bulk_build_from_sorted_iter -pub struct DedupSortedIter +pub(super) struct DedupSortedIter where I: Iterator, { @@ -17,7 +17,7 @@ impl DedupSortedIter where I: Iterator, { - pub fn new(iter: I) -> Self { + pub(super) fn new(iter: I) -> Self { Self { iter: iter.peekable() } } } diff --git a/library/alloc/src/collections/btree/fix.rs b/library/alloc/src/collections/btree/fix.rs index 09edea3555ad5..b0c6759794691 100644 --- a/library/alloc/src/collections/btree/fix.rs +++ b/library/alloc/src/collections/btree/fix.rs @@ -57,7 +57,10 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { /// /// This method does not expect ancestors to already be underfull upon entry /// and panics if it encounters an empty ancestor. - pub fn fix_node_and_affected_ancestors(mut self, alloc: A) -> bool { + pub(super) fn fix_node_and_affected_ancestors( + mut self, + alloc: A, + ) -> bool { loop { match self.fix_node_through_parent(alloc.clone()) { Ok(Some(parent)) => self = parent.forget_type(), @@ -70,7 +73,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { impl Root { /// Removes empty levels on the top, but keeps an empty leaf if the entire tree is empty. - pub fn fix_top(&mut self, alloc: A) { + pub(super) fn fix_top(&mut self, alloc: A) { while self.height() > 0 && self.len() == 0 { self.pop_internal_level(alloc.clone()); } @@ -79,7 +82,7 @@ impl Root { /// Stocks up or merge away any underfull nodes on the right border of the /// tree. The other nodes, those that are not the root nor a rightmost edge, /// must already have at least MIN_LEN elements. - pub fn fix_right_border(&mut self, alloc: A) { + pub(super) fn fix_right_border(&mut self, alloc: A) { self.fix_top(alloc.clone()); if self.len() > 0 { self.borrow_mut().last_kv().fix_right_border_of_right_edge(alloc.clone()); @@ -88,7 +91,7 @@ impl Root { } /// The symmetric clone of `fix_right_border`. - pub fn fix_left_border(&mut self, alloc: A) { + pub(super) fn fix_left_border(&mut self, alloc: A) { self.fix_top(alloc.clone()); if self.len() > 0 { self.borrow_mut().first_kv().fix_left_border_of_left_edge(alloc.clone()); @@ -99,7 +102,7 @@ impl Root { /// Stocks up any underfull nodes on the right border of the tree. /// The other nodes, those that are neither the root nor a rightmost edge, /// must be prepared to have up to MIN_LEN elements stolen. - pub fn fix_right_border_of_plentiful(&mut self) { + pub(super) fn fix_right_border_of_plentiful(&mut self) { let mut cur_node = self.borrow_mut(); while let Internal(internal) = cur_node.force() { // Check if rightmost child is underfull. diff --git a/library/alloc/src/collections/btree/mem.rs b/library/alloc/src/collections/btree/mem.rs index d738c5c47b4cc..4643c4133d55d 100644 --- a/library/alloc/src/collections/btree/mem.rs +++ b/library/alloc/src/collections/btree/mem.rs @@ -6,7 +6,7 @@ use core::{intrinsics, mem, ptr}; /// If a panic occurs in the `change` closure, the entire process will be aborted. #[allow(dead_code)] // keep as illustration and for future use #[inline] -pub fn take_mut(v: &mut T, change: impl FnOnce(T) -> T) { +pub(super) fn take_mut(v: &mut T, change: impl FnOnce(T) -> T) { replace(v, |value| (change(value), ())) } @@ -15,7 +15,7 @@ pub fn take_mut(v: &mut T, change: impl FnOnce(T) -> T) { /// /// If a panic occurs in the `change` closure, the entire process will be aborted. #[inline] -pub fn replace(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R { +pub(super) fn replace(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R { struct PanicGuard; impl Drop for PanicGuard { fn drop(&mut self) { diff --git a/library/alloc/src/collections/btree/merge_iter.rs b/library/alloc/src/collections/btree/merge_iter.rs index 7f23d93b990f5..5077062e25d87 100644 --- a/library/alloc/src/collections/btree/merge_iter.rs +++ b/library/alloc/src/collections/btree/merge_iter.rs @@ -4,7 +4,7 @@ use core::iter::FusedIterator; /// Core of an iterator that merges the output of two strictly ascending iterators, /// for instance a union or a symmetric difference. -pub struct MergeIterInner { +pub(super) struct MergeIterInner { a: I, b: I, peeked: Option>, @@ -40,7 +40,7 @@ where impl MergeIterInner { /// Creates a new core for an iterator merging a pair of sources. - pub fn new(a: I, b: I) -> Self { + pub(super) fn new(a: I, b: I) -> Self { MergeIterInner { a, b, peeked: None } } @@ -51,7 +51,7 @@ impl MergeIterInner { /// the sources are not strictly ascending). If neither returned option /// contains a value, iteration has finished and subsequent calls will /// return the same empty pair. - pub fn nexts Ordering>( + pub(super) fn nexts Ordering>( &mut self, cmp: Cmp, ) -> (Option, Option) @@ -74,7 +74,7 @@ impl MergeIterInner { b_next = self.b.next(); } } - if let (Some(ref a1), Some(ref b1)) = (&a_next, &b_next) { + if let (Some(a1), Some(b1)) = (&a_next, &b_next) { match cmp(a1, b1) { Ordering::Less => self.peeked = b_next.take().map(Peeked::B), Ordering::Greater => self.peeked = a_next.take().map(Peeked::A), @@ -85,7 +85,7 @@ impl MergeIterInner { } /// Returns a pair of upper bounds for the `size_hint` of the final iterator. - pub fn lens(&self) -> (usize, usize) + pub(super) fn lens(&self) -> (usize, usize) where I: ExactSizeIterator, { diff --git a/library/alloc/src/collections/btree/mod.rs b/library/alloc/src/collections/btree/mod.rs index b8667d09c33b3..6651480667391 100644 --- a/library/alloc/src/collections/btree/mod.rs +++ b/library/alloc/src/collections/btree/mod.rs @@ -2,13 +2,13 @@ mod append; mod borrow; mod dedup_sorted_iter; mod fix; -pub mod map; +pub(super) mod map; mod mem; mod merge_iter; mod navigate; mod node; mod remove; mod search; -pub mod set; +pub(super) mod set; mod set_val; mod split; diff --git a/library/alloc/src/collections/btree/navigate.rs b/library/alloc/src/collections/btree/navigate.rs index 14b7d4ad71f86..b2a7de74875d9 100644 --- a/library/alloc/src/collections/btree/navigate.rs +++ b/library/alloc/src/collections/btree/navigate.rs @@ -7,7 +7,7 @@ use super::node::{Handle, NodeRef, marker}; use super::search::SearchBound; use crate::alloc::Allocator; // `front` and `back` are always both `None` or both `Some`. -pub struct LeafRange { +pub(super) struct LeafRange { front: Option, marker::Edge>>, back: Option, marker::Edge>>, } @@ -25,7 +25,7 @@ impl Default for LeafRange { } impl LeafRange { - pub fn none() -> Self { + pub(super) fn none() -> Self { LeafRange { front: None, back: None } } @@ -34,7 +34,7 @@ impl LeafRange { } /// Temporarily takes out another, immutable equivalent of the same range. - pub fn reborrow(&self) -> LeafRange, K, V> { + pub(super) fn reborrow(&self) -> LeafRange, K, V> { LeafRange { front: self.front.as_ref().map(|f| f.reborrow()), back: self.back.as_ref().map(|b| b.reborrow()), @@ -44,24 +44,24 @@ impl LeafRange { impl<'a, K, V> LeafRange, K, V> { #[inline] - pub fn next_checked(&mut self) -> Option<(&'a K, &'a V)> { + pub(super) fn next_checked(&mut self) -> Option<(&'a K, &'a V)> { self.perform_next_checked(|kv| kv.into_kv()) } #[inline] - pub fn next_back_checked(&mut self) -> Option<(&'a K, &'a V)> { + pub(super) fn next_back_checked(&mut self) -> Option<(&'a K, &'a V)> { self.perform_next_back_checked(|kv| kv.into_kv()) } } impl<'a, K, V> LeafRange, K, V> { #[inline] - pub fn next_checked(&mut self) -> Option<(&'a K, &'a mut V)> { + pub(super) fn next_checked(&mut self) -> Option<(&'a K, &'a mut V)> { self.perform_next_checked(|kv| unsafe { ptr::read(kv) }.into_kv_valmut()) } #[inline] - pub fn next_back_checked(&mut self) -> Option<(&'a K, &'a mut V)> { + pub(super) fn next_back_checked(&mut self) -> Option<(&'a K, &'a mut V)> { self.perform_next_back_checked(|kv| unsafe { ptr::read(kv) }.into_kv_valmut()) } } @@ -124,7 +124,7 @@ impl LazyLeafHandle { } // `front` and `back` are always both `None` or both `Some`. -pub struct LazyLeafRange { +pub(super) struct LazyLeafRange { front: Option>, back: Option>, } @@ -142,12 +142,12 @@ impl<'a, K: 'a, V: 'a> Clone for LazyLeafRange, K, V> { } impl LazyLeafRange { - pub fn none() -> Self { + pub(super) fn none() -> Self { LazyLeafRange { front: None, back: None } } /// Temporarily takes out another, immutable equivalent of the same range. - pub fn reborrow(&self) -> LazyLeafRange, K, V> { + pub(super) fn reborrow(&self) -> LazyLeafRange, K, V> { LazyLeafRange { front: self.front.as_ref().map(|f| f.reborrow()), back: self.back.as_ref().map(|b| b.reborrow()), @@ -157,24 +157,24 @@ impl LazyLeafRange { impl<'a, K, V> LazyLeafRange, K, V> { #[inline] - pub unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) { + pub(super) unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) { unsafe { self.init_front().unwrap().next_unchecked() } } #[inline] - pub unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) { + pub(super) unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) { unsafe { self.init_back().unwrap().next_back_unchecked() } } } impl<'a, K, V> LazyLeafRange, K, V> { #[inline] - pub unsafe fn next_unchecked(&mut self) -> (&'a K, &'a mut V) { + pub(super) unsafe fn next_unchecked(&mut self) -> (&'a K, &'a mut V) { unsafe { self.init_front().unwrap().next_unchecked() } } #[inline] - pub unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a mut V) { + pub(super) unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a mut V) { unsafe { self.init_back().unwrap().next_back_unchecked() } } } @@ -190,7 +190,7 @@ impl LazyLeafRange { } #[inline] - pub unsafe fn deallocating_next_unchecked( + pub(super) unsafe fn deallocating_next_unchecked( &mut self, alloc: A, ) -> Handle, marker::KV> { @@ -200,7 +200,7 @@ impl LazyLeafRange { } #[inline] - pub unsafe fn deallocating_next_back_unchecked( + pub(super) unsafe fn deallocating_next_back_unchecked( &mut self, alloc: A, ) -> Handle, marker::KV> { @@ -210,7 +210,7 @@ impl LazyLeafRange { } #[inline] - pub fn deallocating_end(&mut self, alloc: A) { + pub(super) fn deallocating_end(&mut self, alloc: A) { if let Some(front) = self.take_front() { front.deallocating_end(alloc) } @@ -313,7 +313,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> /// /// The result is meaningful only if the tree is ordered by key, like the tree /// in a `BTreeMap` is. - pub fn range_search(self, range: R) -> LeafRange, K, V> + pub(super) fn range_search(self, range: R) -> LeafRange, K, V> where Q: ?Sized + Ord, K: Borrow, @@ -324,7 +324,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> } /// Finds the pair of leaf edges delimiting an entire tree. - pub fn full_range(self) -> LazyLeafRange, K, V> { + pub(super) fn full_range(self) -> LazyLeafRange, K, V> { full_range(self, self) } } @@ -339,7 +339,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> /// /// # Safety /// Do not use the duplicate handles to visit the same KV twice. - pub fn range_search(self, range: R) -> LeafRange, K, V> + pub(super) fn range_search(self, range: R) -> LeafRange, K, V> where Q: ?Sized + Ord, K: Borrow, @@ -351,7 +351,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> /// Splits a unique reference into a pair of leaf edges delimiting the full range of the tree. /// The results are non-unique references allowing mutation (of values only), so must be used /// with care. - pub fn full_range(self) -> LazyLeafRange, K, V> { + pub(super) fn full_range(self) -> LazyLeafRange, K, V> { // We duplicate the root NodeRef here -- we will never visit the same KV // twice, and never end up with overlapping value references. let self2 = unsafe { ptr::read(&self) }; @@ -363,7 +363,7 @@ impl NodeRef { /// Splits a unique reference into a pair of leaf edges delimiting the full range of the tree. /// The results are non-unique references allowing massively destructive mutation, so must be /// used with the utmost care. - pub fn full_range(self) -> LazyLeafRange { + pub(super) fn full_range(self) -> LazyLeafRange { // We duplicate the root NodeRef here -- we will never access it in a way // that overlaps references obtained from the root. let self2 = unsafe { ptr::read(&self) }; @@ -377,7 +377,7 @@ impl /// Given a leaf edge handle, returns [`Result::Ok`] with a handle to the neighboring KV /// on the right side, which is either in the same leaf node or in an ancestor node. /// If the leaf edge is the last one in the tree, returns [`Result::Err`] with the root node. - pub fn next_kv( + pub(super) fn next_kv( self, ) -> Result< Handle, marker::KV>, @@ -398,7 +398,7 @@ impl /// Given a leaf edge handle, returns [`Result::Ok`] with a handle to the neighboring KV /// on the left side, which is either in the same leaf node or in an ancestor node. /// If the leaf edge is the first one in the tree, returns [`Result::Err`] with the root node. - pub fn next_back_kv( + pub(super) fn next_back_kv( self, ) -> Result< Handle, marker::KV>, @@ -627,7 +627,9 @@ impl NodeRef Handle, marker::Edge> { + pub(super) fn first_leaf_edge( + self, + ) -> Handle, marker::Edge> { let mut node = self; loop { match node.force() { @@ -640,7 +642,9 @@ impl NodeRef Handle, marker::Edge> { + pub(super) fn last_leaf_edge( + self, + ) -> Handle, marker::Edge> { let mut node = self; loop { match node.force() { @@ -651,7 +655,7 @@ impl NodeRef { +pub(super) enum Position { Leaf(NodeRef), Internal(NodeRef), InternalKV, @@ -661,7 +665,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> /// Visits leaf nodes and internal KVs in order of ascending keys, and also /// visits internal nodes as a whole in a depth first order, meaning that /// internal nodes precede their individual KVs and their child nodes. - pub fn visit_nodes_in_order(self, mut visit: F) + pub(super) fn visit_nodes_in_order(self, mut visit: F) where F: FnMut(Position, K, V>), { @@ -693,7 +697,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> } /// Calculates the number of elements in a (sub)tree. - pub fn calc_length(self) -> usize { + pub(super) fn calc_length(self) -> usize { let mut result = 0; self.visit_nodes_in_order(|pos| match pos { Position::Leaf(node) => result += node.len(), @@ -708,7 +712,9 @@ impl Handle, marker::KV> { /// Returns the leaf edge closest to a KV for forward navigation. - pub fn next_leaf_edge(self) -> Handle, marker::Edge> { + pub(super) fn next_leaf_edge( + self, + ) -> Handle, marker::Edge> { match self.force() { Leaf(leaf_kv) => leaf_kv.right_edge(), Internal(internal_kv) => { @@ -719,7 +725,7 @@ impl } /// Returns the leaf edge closest to a KV for backward navigation. - pub fn next_back_leaf_edge( + pub(super) fn next_back_leaf_edge( self, ) -> Handle, marker::Edge> { match self.force() { @@ -735,7 +741,7 @@ impl impl NodeRef { /// Returns the leaf edge corresponding to the first point at which the /// given bound is true. - pub fn lower_bound( + pub(super) fn lower_bound( self, mut bound: SearchBound<&Q>, ) -> Handle, marker::Edge> @@ -758,7 +764,7 @@ impl NodeRef( + pub(super) fn upper_bound( self, mut bound: SearchBound<&Q>, ) -> Handle, marker::Edge> diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index 0c93eff0d20fa..37f784a322cad 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -40,8 +40,8 @@ use crate::alloc::{Allocator, Layout}; use crate::boxed::Box; const B: usize = 6; -pub const CAPACITY: usize = 2 * B - 1; -pub const MIN_LEN_AFTER_SPLIT: usize = B - 1; +pub(super) const CAPACITY: usize = 2 * B - 1; +pub(super) const MIN_LEN_AFTER_SPLIT: usize = B - 1; const KV_IDX_CENTER: usize = B - 1; const EDGE_IDX_LEFT_OF_CENTER: usize = B - 1; const EDGE_IDX_RIGHT_OF_CENTER: usize = B; @@ -179,7 +179,7 @@ type BoxedNode = NonNull>; /// as the returned reference is used. /// The methods supporting insert bend this rule by returning a raw pointer, /// i.e., a reference without any lifetime. -pub struct NodeRef { +pub(super) struct NodeRef { /// The number of levels that the node and the level of leaves are apart, a /// constant of the node that cannot be entirely described by `Type`, and that /// the node itself does not store. We only need to store the height of the root @@ -195,7 +195,7 @@ pub struct NodeRef { /// The root node of an owned tree. /// /// Note that this does not have a destructor, and must be cleaned up manually. -pub type Root = NodeRef; +pub(super) type Root = NodeRef; impl<'a, K: 'a, V: 'a, Type> Copy for NodeRef, K, V, Type> {} impl<'a, K: 'a, V: 'a, Type> Clone for NodeRef, K, V, Type> { @@ -213,7 +213,7 @@ unsafe impl Send for NodeRef unsafe impl Send for NodeRef {} impl NodeRef { - pub fn new_leaf(alloc: A) -> Self { + pub(super) fn new_leaf(alloc: A) -> Self { Self::from_new_leaf(LeafNode::new(alloc)) } @@ -274,7 +274,7 @@ impl NodeRef { /// The number of edges is `len() + 1`. /// Note that, despite being safe, calling this function can have the side effect /// of invalidating mutable references that unsafe code has created. - pub fn len(&self) -> usize { + pub(super) fn len(&self) -> usize { // Crucially, we only access the `len` field here. If BorrowType is marker::ValMut, // there might be outstanding mutable references to values that we must not invalidate. unsafe { usize::from((*Self::as_leaf_ptr(self)).len) } @@ -285,12 +285,12 @@ impl NodeRef { /// root on top, the number says at which elevation the node appears. /// If you picture trees with leaves on top, the number says how high /// the tree extends above the node. - pub fn height(&self) -> usize { + pub(super) fn height(&self) -> usize { self.height } /// Temporarily takes out another, immutable reference to the same node. - pub fn reborrow(&self) -> NodeRef, K, V, Type> { + pub(super) fn reborrow(&self) -> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } @@ -315,7 +315,7 @@ impl NodeRef /// /// `edge.descend().ascend().unwrap()` and `node.ascend().unwrap().descend()` should /// both, upon success, do nothing. - pub fn ascend( + pub(super) fn ascend( self, ) -> Result, marker::Edge>, Self> { const { @@ -335,24 +335,24 @@ impl NodeRef .ok_or(self) } - pub fn first_edge(self) -> Handle { + pub(super) fn first_edge(self) -> Handle { unsafe { Handle::new_edge(self, 0) } } - pub fn last_edge(self) -> Handle { + pub(super) fn last_edge(self) -> Handle { let len = self.len(); unsafe { Handle::new_edge(self, len) } } /// Note that `self` must be nonempty. - pub fn first_kv(self) -> Handle { + pub(super) fn first_kv(self) -> Handle { let len = self.len(); assert!(len > 0); unsafe { Handle::new_kv(self, 0) } } /// Note that `self` must be nonempty. - pub fn last_kv(self) -> Handle { + pub(super) fn last_kv(self) -> Handle { let len = self.len(); assert!(len > 0); unsafe { Handle::new_kv(self, len - 1) } @@ -381,11 +381,9 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { } /// Borrows a view into the keys stored in the node. - pub fn keys(&self) -> &[K] { + pub(super) fn keys(&self) -> &[K] { let leaf = self.into_leaf(); - unsafe { - MaybeUninit::slice_assume_init_ref(leaf.keys.get_unchecked(..usize::from(leaf.len))) - } + unsafe { leaf.keys.get_unchecked(..usize::from(leaf.len)).assume_init_ref() } } } @@ -393,7 +391,7 @@ impl NodeRef { /// Similar to `ascend`, gets a reference to a node's parent node, but also /// deallocates the current node in the process. This is unsafe because the /// current node will still be accessible despite being deallocated. - pub unsafe fn deallocate_and_ascend( + pub(super) unsafe fn deallocate_and_ascend( self, alloc: A, ) -> Option, marker::Edge>> { @@ -445,7 +443,7 @@ impl<'a, K, V, Type> NodeRef, K, V, Type> { /// Returns a dormant copy of this node with its lifetime erased which can /// be reawakened later. - pub fn dormant(&self) -> NodeRef { + pub(super) fn dormant(&self) -> NodeRef { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } @@ -457,7 +455,7 @@ impl NodeRef { /// /// The reborrow must have ended, i.e., the reference returned by `new` and /// all pointers and references derived from it, must not be used anymore. - pub unsafe fn awaken<'a>(self) -> NodeRef, K, V, Type> { + pub(super) unsafe fn awaken<'a>(self) -> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } @@ -538,7 +536,7 @@ impl<'a, K, V, Type> NodeRef, K, V, Type> { impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { /// Borrows exclusive access to the length of the node. - pub fn len_mut(&mut self) -> &mut u16 { + pub(super) fn len_mut(&mut self) -> &mut u16 { &mut self.as_leaf_mut().len } } @@ -580,14 +578,14 @@ impl NodeRef { impl NodeRef { /// Returns a new owned tree, with its own root node that is initially empty. - pub fn new(alloc: A) -> Self { + pub(super) fn new(alloc: A) -> Self { NodeRef::new_leaf(alloc).forget_type() } /// Adds a new internal node with a single edge pointing to the previous root node, /// make that new node the root node, and return it. This increases the height by 1 /// and is the opposite of `pop_internal_level`. - pub fn push_internal_level( + pub(super) fn push_internal_level( &mut self, alloc: A, ) -> NodeRef, K, V, marker::Internal> { @@ -602,11 +600,11 @@ impl NodeRef { /// no cleanup is done on any of the keys, values and other children. /// This decreases the height by 1 and is the opposite of `push_internal_level`. /// - /// Requires exclusive access to the `NodeRef` object but not to the root node; - /// it will not invalidate other handles or references to the root node. + /// Does not invalidate any handles or references pointing into the subtree + /// rooted at the first child of `self`. /// /// Panics if there is no internal level, i.e., if the root node is a leaf. - pub fn pop_internal_level(&mut self, alloc: A) { + pub(super) fn pop_internal_level(&mut self, alloc: A) { assert!(self.height > 0); let top = self.node; @@ -630,18 +628,18 @@ impl NodeRef { /// Mutably borrows the owned root node. Unlike `reborrow_mut`, this is safe /// because the return value cannot be used to destroy the root, and there /// cannot be other references to the tree. - pub fn borrow_mut(&mut self) -> NodeRef, K, V, Type> { + pub(super) fn borrow_mut(&mut self) -> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } /// Slightly mutably borrows the owned root node. - pub fn borrow_valmut(&mut self) -> NodeRef, K, V, Type> { + pub(super) fn borrow_valmut(&mut self) -> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } /// Irreversibly transitions to a reference that permits traversal and offers /// destructive methods and little else. - pub fn into_dying(self) -> NodeRef { + pub(super) fn into_dying(self) -> NodeRef { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } @@ -653,7 +651,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Leaf> { /// # Safety /// /// The returned handle has an unbound lifetime. - pub unsafe fn push_with_handle<'b>( + pub(super) unsafe fn push_with_handle<'b>( &mut self, key: K, val: V, @@ -674,7 +672,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Leaf> { /// Adds a key-value pair to the end of the node, and returns /// the mutable reference of the inserted value. - pub fn push(&mut self, key: K, val: V) -> *mut V { + pub(super) fn push(&mut self, key: K, val: V) -> *mut V { // SAFETY: The unbound handle is no longer accessible. unsafe { self.push_with_handle(key, val).into_val_mut() } } @@ -683,7 +681,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Leaf> { impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { /// Adds a key-value pair, and an edge to go to the right of that pair, /// to the end of the node. - pub fn push(&mut self, key: K, val: V, edge: Root) { + pub(super) fn push(&mut self, key: K, val: V, edge: Root) { assert!(edge.height == self.height - 1); let len = self.len_mut(); @@ -701,21 +699,21 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { impl NodeRef { /// Removes any static information asserting that this node is a `Leaf` node. - pub fn forget_type(self) -> NodeRef { + pub(super) fn forget_type(self) -> NodeRef { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } impl NodeRef { /// Removes any static information asserting that this node is an `Internal` node. - pub fn forget_type(self) -> NodeRef { + pub(super) fn forget_type(self) -> NodeRef { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } impl NodeRef { /// Checks whether a node is an `Internal` node or a `Leaf` node. - pub fn force( + pub(super) fn force( self, ) -> ForceResult< NodeRef, @@ -739,7 +737,9 @@ impl NodeRef { impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { /// Unsafely asserts to the compiler the static information that this node is a `Leaf`. - pub unsafe fn cast_to_leaf_unchecked(self) -> NodeRef, K, V, marker::Leaf> { + pub(super) unsafe fn cast_to_leaf_unchecked( + self, + ) -> NodeRef, K, V, marker::Leaf> { debug_assert!(self.height == 0); NodeRef { height: self.height, node: self.node, _marker: PhantomData } } @@ -759,7 +759,7 @@ impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { /// a child node, these represent the spaces where child pointers would go between the key-value /// pairs. For example, in a node with length 2, there would be 3 possible edge locations - one /// to the left of the node, one between the two pairs, and one at the right of the node. -pub struct Handle { +pub(super) struct Handle { node: Node, idx: usize, _marker: PhantomData, @@ -776,12 +776,12 @@ impl Clone for Handle { impl Handle { /// Retrieves the node that contains the edge or key-value pair this handle points to. - pub fn into_node(self) -> Node { + pub(super) fn into_node(self) -> Node { self.node } /// Returns the position of this handle in the node. - pub fn idx(&self) -> usize { + pub(super) fn idx(&self) -> usize { self.idx } } @@ -789,17 +789,17 @@ impl Handle { impl Handle, marker::KV> { /// Creates a new handle to a key-value pair in `node`. /// Unsafe because the caller must ensure that `idx < node.len()`. - pub unsafe fn new_kv(node: NodeRef, idx: usize) -> Self { + pub(super) unsafe fn new_kv(node: NodeRef, idx: usize) -> Self { debug_assert!(idx < node.len()); Handle { node, idx, _marker: PhantomData } } - pub fn left_edge(self) -> Handle, marker::Edge> { + pub(super) fn left_edge(self) -> Handle, marker::Edge> { unsafe { Handle::new_edge(self.node, self.idx) } } - pub fn right_edge(self) -> Handle, marker::Edge> { + pub(super) fn right_edge(self) -> Handle, marker::Edge> { unsafe { Handle::new_edge(self.node, self.idx + 1) } } } @@ -817,7 +817,9 @@ impl Handle, HandleType> { /// Temporarily takes out another immutable handle on the same location. - pub fn reborrow(&self) -> Handle, K, V, NodeType>, HandleType> { + pub(super) fn reborrow( + &self, + ) -> Handle, K, V, NodeType>, HandleType> { // We can't use Handle::new_kv or Handle::new_edge because we don't know our type Handle { node: self.node.reborrow(), idx: self.idx, _marker: PhantomData } } @@ -829,7 +831,7 @@ impl<'a, K, V, NodeType, HandleType> Handle, K, V, NodeT /// dangerous. /// /// For details, see `NodeRef::reborrow_mut`. - pub unsafe fn reborrow_mut( + pub(super) unsafe fn reborrow_mut( &mut self, ) -> Handle, K, V, NodeType>, HandleType> { // We can't use Handle::new_kv or Handle::new_edge because we don't know our type @@ -839,7 +841,9 @@ impl<'a, K, V, NodeType, HandleType> Handle, K, V, NodeT /// Returns a dormant copy of this handle which can be reawakened later. /// /// See `DormantMutRef` for more details. - pub fn dormant(&self) -> Handle, HandleType> { + pub(super) fn dormant( + &self, + ) -> Handle, HandleType> { Handle { node: self.node.dormant(), idx: self.idx, _marker: PhantomData } } } @@ -851,7 +855,9 @@ impl Handle(self) -> Handle, K, V, NodeType>, HandleType> { + pub(super) unsafe fn awaken<'a>( + self, + ) -> Handle, K, V, NodeType>, HandleType> { Handle { node: unsafe { self.node.awaken() }, idx: self.idx, _marker: PhantomData } } } @@ -859,13 +865,15 @@ impl Handle Handle, marker::Edge> { /// Creates a new handle to an edge in `node`. /// Unsafe because the caller must ensure that `idx <= node.len()`. - pub unsafe fn new_edge(node: NodeRef, idx: usize) -> Self { + pub(super) unsafe fn new_edge(node: NodeRef, idx: usize) -> Self { debug_assert!(idx <= node.len()); Handle { node, idx, _marker: PhantomData } } - pub fn left_kv(self) -> Result, marker::KV>, Self> { + pub(super) fn left_kv( + self, + ) -> Result, marker::KV>, Self> { if self.idx > 0 { Ok(unsafe { Handle::new_kv(self.node, self.idx - 1) }) } else { @@ -873,7 +881,9 @@ impl Handle, mar } } - pub fn right_kv(self) -> Result, marker::KV>, Self> { + pub(super) fn right_kv( + self, + ) -> Result, marker::KV>, Self> { if self.idx < self.node.len() { Ok(unsafe { Handle::new_kv(self.node, self.idx) }) } else { @@ -882,7 +892,7 @@ impl Handle, mar } } -pub enum LeftOrRight { +pub(super) enum LeftOrRight { Left(T), Right(T), } @@ -1036,7 +1046,7 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark /// If the returned result is some `SplitResult`, the `left` field will be the root node. /// The returned pointer points to the inserted value, which in the case of `SplitResult` /// is in the `left` or `right` tree. - pub fn insert_recursing( + pub(super) fn insert_recursing( self, key: K, value: V, @@ -1080,7 +1090,7 @@ impl /// /// `edge.descend().ascend().unwrap()` and `node.ascend().unwrap().descend()` should /// both, upon success, do nothing. - pub fn descend(self) -> NodeRef { + pub(super) fn descend(self) -> NodeRef { const { assert!(BorrowType::TRAVERSAL_PERMIT); } @@ -1099,7 +1109,7 @@ impl } impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { - pub fn into_kv(self) -> (&'a K, &'a V) { + pub(super) fn into_kv(self) -> (&'a K, &'a V) { debug_assert!(self.idx < self.node.len()); let leaf = self.node.into_leaf(); let k = unsafe { leaf.keys.get_unchecked(self.idx).assume_init_ref() }; @@ -1109,17 +1119,17 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeTyp } impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { - pub fn key_mut(&mut self) -> &mut K { + pub(super) fn key_mut(&mut self) -> &mut K { unsafe { self.node.key_area_mut(self.idx).assume_init_mut() } } - pub fn into_val_mut(self) -> &'a mut V { + pub(super) fn into_val_mut(self) -> &'a mut V { debug_assert!(self.idx < self.node.len()); let leaf = self.node.into_leaf_mut(); unsafe { leaf.vals.get_unchecked_mut(self.idx).assume_init_mut() } } - pub fn into_kv_mut(self) -> (&'a mut K, &'a mut V) { + pub(super) fn into_kv_mut(self) -> (&'a mut K, &'a mut V) { debug_assert!(self.idx < self.node.len()); let leaf = self.node.into_leaf_mut(); let k = unsafe { leaf.keys.get_unchecked_mut(self.idx).assume_init_mut() }; @@ -1129,13 +1139,13 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType> } impl<'a, K, V, NodeType> Handle, K, V, NodeType>, marker::KV> { - pub fn into_kv_valmut(self) -> (&'a K, &'a mut V) { + pub(super) fn into_kv_valmut(self) -> (&'a K, &'a mut V) { unsafe { self.node.into_key_val_mut_at(self.idx) } } } impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { - pub fn kv_mut(&mut self) -> (&mut K, &mut V) { + pub(super) fn kv_mut(&mut self) -> (&mut K, &mut V) { debug_assert!(self.idx < self.node.len()); // We cannot call separate key and value methods, because calling the second one // invalidates the reference returned by the first. @@ -1148,7 +1158,7 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType> } /// Replaces the key and value that the KV handle refers to. - pub fn replace_kv(&mut self, k: K, v: V) -> (K, V) { + pub(super) fn replace_kv(&mut self, k: K, v: V) -> (K, V) { let (key, val) = self.kv_mut(); (mem::replace(key, k), mem::replace(val, v)) } @@ -1158,7 +1168,7 @@ impl Handle, marker::KV> /// Extracts the key and value that the KV handle refers to. /// # Safety /// The node that the handle refers to must not yet have been deallocated. - pub unsafe fn into_key_val(mut self) -> (K, V) { + pub(super) unsafe fn into_key_val(mut self) -> (K, V) { debug_assert!(self.idx < self.node.len()); let leaf = self.node.as_leaf_dying(); unsafe { @@ -1172,7 +1182,7 @@ impl Handle, marker::KV> /// # Safety /// The node that the handle refers to must not yet have been deallocated. #[inline] - pub unsafe fn drop_key_val(mut self) { + pub(super) unsafe fn drop_key_val(mut self) { // Run the destructor of the value even if the destructor of the key panics. struct Dropper<'a, T>(&'a mut MaybeUninit); impl Drop for Dropper<'_, T> { @@ -1231,7 +1241,10 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark /// - The key and value pointed to by this handle are extracted. /// - All the key-value pairs to the right of this handle are put into a newly /// allocated node. - pub fn split(mut self, alloc: A) -> SplitResult<'a, K, V, marker::Leaf> { + pub(super) fn split( + mut self, + alloc: A, + ) -> SplitResult<'a, K, V, marker::Leaf> { let mut new_node = LeafNode::new(alloc); let kv = self.split_leaf_data(&mut new_node); @@ -1242,7 +1255,7 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark /// Removes the key-value pair pointed to by this handle and returns it, along with the edge /// that the key-value pair collapsed into. - pub fn remove( + pub(super) fn remove( mut self, ) -> ((K, V), Handle, K, V, marker::Leaf>, marker::Edge>) { let old_len = self.node.len(); @@ -1263,7 +1276,7 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, /// - The key and value pointed to by this handle are extracted. /// - All the edges and key-value pairs to the right of this handle are put into /// a newly allocated node. - pub fn split( + pub(super) fn split( mut self, alloc: A, ) -> SplitResult<'a, K, V, marker::Internal> { @@ -1287,14 +1300,14 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, /// Represents a session for evaluating and performing a balancing operation /// around an internal key-value pair. -pub struct BalancingContext<'a, K, V> { +pub(super) struct BalancingContext<'a, K, V> { parent: Handle, K, V, marker::Internal>, marker::KV>, left_child: NodeRef, K, V, marker::LeafOrInternal>, right_child: NodeRef, K, V, marker::LeafOrInternal>, } impl<'a, K, V> Handle, K, V, marker::Internal>, marker::KV> { - pub fn consider_for_balancing(self) -> BalancingContext<'a, K, V> { + pub(super) fn consider_for_balancing(self) -> BalancingContext<'a, K, V> { let self1 = unsafe { ptr::read(&self) }; let self2 = unsafe { ptr::read(&self) }; BalancingContext { @@ -1320,7 +1333,7 @@ impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { /// typically faster, since we only need to shift the node's N elements to /// the right, instead of shifting at least N of the sibling's elements to /// the left. - pub fn choose_parent_kv(self) -> Result>, Self> { + pub(super) fn choose_parent_kv(self) -> Result>, Self> { match unsafe { ptr::read(&self) }.ascend() { Ok(parent_edge) => match parent_edge.left_kv() { Ok(left_parent_kv) => Ok(LeftOrRight::Left(BalancingContext { @@ -1343,25 +1356,25 @@ impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { } impl<'a, K, V> BalancingContext<'a, K, V> { - pub fn left_child_len(&self) -> usize { + pub(super) fn left_child_len(&self) -> usize { self.left_child.len() } - pub fn right_child_len(&self) -> usize { + pub(super) fn right_child_len(&self) -> usize { self.right_child.len() } - pub fn into_left_child(self) -> NodeRef, K, V, marker::LeafOrInternal> { + pub(super) fn into_left_child(self) -> NodeRef, K, V, marker::LeafOrInternal> { self.left_child } - pub fn into_right_child(self) -> NodeRef, K, V, marker::LeafOrInternal> { + pub(super) fn into_right_child(self) -> NodeRef, K, V, marker::LeafOrInternal> { self.right_child } /// Returns whether merging is possible, i.e., whether there is enough room /// in a node to combine the central KV with both adjacent child nodes. - pub fn can_merge(&self) -> bool { + pub(super) fn can_merge(&self) -> bool { self.left_child.len() + 1 + self.right_child.len() <= CAPACITY } } @@ -1435,7 +1448,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// the left child node and returns the shrunk parent node. /// /// Panics unless we `.can_merge()`. - pub fn merge_tracking_parent( + pub(super) fn merge_tracking_parent( self, alloc: A, ) -> NodeRef, K, V, marker::Internal> { @@ -1446,7 +1459,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// the left child node and returns that child node. /// /// Panics unless we `.can_merge()`. - pub fn merge_tracking_child( + pub(super) fn merge_tracking_child( self, alloc: A, ) -> NodeRef, K, V, marker::LeafOrInternal> { @@ -1458,7 +1471,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// where the tracked child edge ended up, /// /// Panics unless we `.can_merge()`. - pub fn merge_tracking_child_edge( + pub(super) fn merge_tracking_child_edge( self, track_edge_idx: LeftOrRight, alloc: A, @@ -1481,7 +1494,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// of the parent, while pushing the old parent key-value pair into the right child. /// Returns a handle to the edge in the right child corresponding to where the original /// edge specified by `track_right_edge_idx` ended up. - pub fn steal_left( + pub(super) fn steal_left( mut self, track_right_edge_idx: usize, ) -> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { @@ -1493,7 +1506,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// of the parent, while pushing the old parent key-value pair onto the left child. /// Returns a handle to the edge in the left child specified by `track_left_edge_idx`, /// which didn't move. - pub fn steal_right( + pub(super) fn steal_right( mut self, track_left_edge_idx: usize, ) -> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { @@ -1502,7 +1515,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { } /// This does stealing similar to `steal_left` but steals multiple elements at once. - pub fn bulk_steal_left(&mut self, count: usize) { + pub(super) fn bulk_steal_left(&mut self, count: usize) { assert!(count > 0); unsafe { let left_node = &mut self.left_child; @@ -1565,7 +1578,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { } /// The symmetric clone of `bulk_steal_left`. - pub fn bulk_steal_right(&mut self, count: usize) { + pub(super) fn bulk_steal_right(&mut self, count: usize) { assert!(count > 0); unsafe { let left_node = &mut self.left_child; @@ -1630,7 +1643,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { } impl Handle, marker::Edge> { - pub fn forget_node_type( + pub(super) fn forget_node_type( self, ) -> Handle, marker::Edge> { unsafe { Handle::new_edge(self.node.forget_type(), self.idx) } @@ -1638,7 +1651,7 @@ impl Handle, marker::E } impl Handle, marker::Edge> { - pub fn forget_node_type( + pub(super) fn forget_node_type( self, ) -> Handle, marker::Edge> { unsafe { Handle::new_edge(self.node.forget_type(), self.idx) } @@ -1646,7 +1659,7 @@ impl Handle, marke } impl Handle, marker::KV> { - pub fn forget_node_type( + pub(super) fn forget_node_type( self, ) -> Handle, marker::KV> { unsafe { Handle::new_kv(self.node.forget_type(), self.idx) } @@ -1655,7 +1668,7 @@ impl Handle, marker::K impl Handle, Type> { /// Checks whether the underlying node is an `Internal` node or a `Leaf` node. - pub fn force( + pub(super) fn force( self, ) -> ForceResult< Handle, Type>, @@ -1674,7 +1687,7 @@ impl Handle Handle, K, V, marker::LeafOrInternal>, Type> { /// Unsafely asserts to the compiler the static information that the handle's node is a `Leaf`. - pub unsafe fn cast_to_leaf_unchecked( + pub(super) unsafe fn cast_to_leaf_unchecked( self, ) -> Handle, K, V, marker::Leaf>, Type> { let node = unsafe { self.node.cast_to_leaf_unchecked() }; @@ -1685,7 +1698,7 @@ impl<'a, K, V, Type> Handle, K, V, marker::LeafOrInterna impl<'a, K, V> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { /// Move the suffix after `self` from one node to another one. `right` must be empty. /// The first edge of `right` remains unchanged. - pub fn move_suffix( + pub(super) fn move_suffix( &mut self, right: &mut NodeRef, K, V, marker::LeafOrInternal>, ) { @@ -1728,13 +1741,13 @@ impl<'a, K, V> Handle, K, V, marker::LeafOrInternal>, ma } } -pub enum ForceResult { +pub(super) enum ForceResult { Leaf(Leaf), Internal(Internal), } /// Result of insertion, when a node needed to expand beyond its capacity. -pub struct SplitResult<'a, K, V, NodeType> { +pub(super) struct SplitResult<'a, K, V, NodeType> { // Altered node in existing tree with elements and edges that belong to the left of `kv`. pub left: NodeRef, K, V, NodeType>, // Some key and value that existed before and were split off, to be inserted elsewhere. @@ -1744,32 +1757,32 @@ pub struct SplitResult<'a, K, V, NodeType> { } impl<'a, K, V> SplitResult<'a, K, V, marker::Leaf> { - pub fn forget_node_type(self) -> SplitResult<'a, K, V, marker::LeafOrInternal> { + pub(super) fn forget_node_type(self) -> SplitResult<'a, K, V, marker::LeafOrInternal> { SplitResult { left: self.left.forget_type(), kv: self.kv, right: self.right.forget_type() } } } impl<'a, K, V> SplitResult<'a, K, V, marker::Internal> { - pub fn forget_node_type(self) -> SplitResult<'a, K, V, marker::LeafOrInternal> { + pub(super) fn forget_node_type(self) -> SplitResult<'a, K, V, marker::LeafOrInternal> { SplitResult { left: self.left.forget_type(), kv: self.kv, right: self.right.forget_type() } } } -pub mod marker { +pub(super) mod marker { use core::marker::PhantomData; - pub enum Leaf {} - pub enum Internal {} - pub enum LeafOrInternal {} + pub(crate) enum Leaf {} + pub(crate) enum Internal {} + pub(crate) enum LeafOrInternal {} - pub enum Owned {} - pub enum Dying {} - pub enum DormantMut {} - pub struct Immut<'a>(PhantomData<&'a ()>); - pub struct Mut<'a>(PhantomData<&'a mut ()>); - pub struct ValMut<'a>(PhantomData<&'a mut ()>); + pub(crate) enum Owned {} + pub(crate) enum Dying {} + pub(crate) enum DormantMut {} + pub(crate) struct Immut<'a>(PhantomData<&'a ()>); + pub(crate) struct Mut<'a>(PhantomData<&'a mut ()>); + pub(crate) struct ValMut<'a>(PhantomData<&'a mut ()>); - pub trait BorrowType { + pub(crate) trait BorrowType { /// If node references of this borrow type allow traversing to other /// nodes in the tree, this constant is set to `true`. It can be used /// for a compile-time assertion. @@ -1788,8 +1801,8 @@ pub mod marker { impl<'a> BorrowType for ValMut<'a> {} impl BorrowType for DormantMut {} - pub enum KV {} - pub enum Edge {} + pub(crate) enum KV {} + pub(crate) enum Edge {} } /// Inserts a value into a slice of initialized elements followed by one uninitialized element. diff --git a/library/alloc/src/collections/btree/node/tests.rs b/library/alloc/src/collections/btree/node/tests.rs index 4d2fa0f094171..ecd009f11c71a 100644 --- a/library/alloc/src/collections/btree/node/tests.rs +++ b/library/alloc/src/collections/btree/node/tests.rs @@ -6,7 +6,7 @@ use crate::string::String; impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { // Asserts that the back pointer in each reachable node points to its parent. - pub fn assert_back_pointers(self) { + pub(crate) fn assert_back_pointers(self) { if let ForceResult::Internal(node) = self.force() { for idx in 0..=node.len() { let edge = unsafe { Handle::new_edge(node, idx) }; @@ -20,7 +20,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> // Renders a multi-line display of the keys in order and in tree hierarchy, // picturing the tree growing sideways from its root on the left to its // leaves on the right. - pub fn dump_keys(self) -> String + pub(crate) fn dump_keys(self) -> String where K: Debug, { diff --git a/library/alloc/src/collections/btree/remove.rs b/library/alloc/src/collections/btree/remove.rs index 56f2824b782bd..9d870b86f34a0 100644 --- a/library/alloc/src/collections/btree/remove.rs +++ b/library/alloc/src/collections/btree/remove.rs @@ -10,7 +10,7 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::LeafOrInter /// the leaf edge corresponding to that former pair. It's possible this empties /// a root node that is internal, which the caller should pop from the map /// holding the tree. The caller should also decrement the map's length. - pub fn remove_kv_tracking( + pub(super) fn remove_kv_tracking( self, handle_emptied_internal_root: F, alloc: A, diff --git a/library/alloc/src/collections/btree/search.rs b/library/alloc/src/collections/btree/search.rs index 22e015edac3d2..96e5bf108024b 100644 --- a/library/alloc/src/collections/btree/search.rs +++ b/library/alloc/src/collections/btree/search.rs @@ -8,7 +8,7 @@ use SearchResult::*; use super::node::ForceResult::*; use super::node::{Handle, NodeRef, marker}; -pub enum SearchBound { +pub(super) enum SearchBound { /// An inclusive bound to look for, just like `Bound::Included(T)`. Included(T), /// An exclusive bound to look for, just like `Bound::Excluded(T)`. @@ -20,7 +20,7 @@ pub enum SearchBound { } impl SearchBound { - pub fn from_range(range_bound: Bound) -> Self { + pub(super) fn from_range(range_bound: Bound) -> Self { match range_bound { Bound::Included(t) => Included(t), Bound::Excluded(t) => Excluded(t), @@ -29,12 +29,12 @@ impl SearchBound { } } -pub enum SearchResult { +pub(super) enum SearchResult { Found(Handle, marker::KV>), GoDown(Handle, marker::Edge>), } -pub enum IndexResult { +pub(super) enum IndexResult { KV(usize), Edge(usize), } @@ -46,7 +46,7 @@ impl NodeRef( + pub(super) fn search_tree( mut self, key: &Q, ) -> SearchResult @@ -80,7 +80,7 @@ impl NodeRef( + pub(super) fn search_tree_for_bifurcation<'r, Q: ?Sized, R>( mut self, range: &'r R, ) -> Result< @@ -156,7 +156,7 @@ impl NodeRef( + pub(super) fn find_lower_bound_edge<'r, Q>( self, bound: SearchBound<&'r Q>, ) -> (Handle, SearchBound<&'r Q>) @@ -170,7 +170,7 @@ impl NodeRef( + pub(super) fn find_upper_bound_edge<'r, Q>( self, bound: SearchBound<&'r Q>, ) -> (Handle, SearchBound<&'r Q>) @@ -192,7 +192,10 @@ impl NodeRef { /// /// The result is meaningful only if the tree is ordered by key, like the tree /// in a `BTreeMap` is. - pub fn search_node(self, key: &Q) -> SearchResult + pub(super) fn search_node( + self, + key: &Q, + ) -> SearchResult where Q: Ord, K: Borrow, diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index 9660023d6945e..041f80c1f2c52 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -1442,20 +1442,20 @@ impl BTreeSet { /// /// let mut set = BTreeSet::from([1, 2, 3, 4]); /// - /// let mut cursor = unsafe { set.upper_bound_mut(Bound::Included(&3)) }; + /// let mut cursor = set.upper_bound_mut(Bound::Included(&3)); /// assert_eq!(cursor.peek_prev(), Some(&3)); /// assert_eq!(cursor.peek_next(), Some(&4)); /// - /// let mut cursor = unsafe { set.upper_bound_mut(Bound::Excluded(&3)) }; + /// let mut cursor = set.upper_bound_mut(Bound::Excluded(&3)); /// assert_eq!(cursor.peek_prev(), Some(&2)); /// assert_eq!(cursor.peek_next(), Some(&3)); /// - /// let mut cursor = unsafe { set.upper_bound_mut(Bound::Unbounded) }; + /// let mut cursor = set.upper_bound_mut(Bound::Unbounded); /// assert_eq!(cursor.peek_prev(), Some(&4)); /// assert_eq!(cursor.peek_next(), None); /// ``` #[unstable(feature = "btree_cursors", issue = "107540")] - pub unsafe fn upper_bound_mut(&mut self, bound: Bound<&Q>) -> CursorMut<'_, T, A> + pub fn upper_bound_mut(&mut self, bound: Bound<&Q>) -> CursorMut<'_, T, A> where T: Borrow + Ord, Q: Ord, diff --git a/library/alloc/src/collections/btree/set/tests.rs b/library/alloc/src/collections/btree/set/tests.rs index 990044e069f64..d538ef707eb93 100644 --- a/library/alloc/src/collections/btree/set/tests.rs +++ b/library/alloc/src/collections/btree/set/tests.rs @@ -132,9 +132,11 @@ fn test_difference() { check_difference(&[1, 3, 5, 9, 11], &[3, 6, 9], &[1, 5, 11]); check_difference(&[1, 3, 5, 9, 11], &[0, 1], &[3, 5, 9, 11]); check_difference(&[1, 3, 5, 9, 11], &[11, 12], &[1, 3, 5, 9]); - check_difference(&[-5, 11, 22, 33, 40, 42], &[-12, -5, 14, 23, 34, 38, 39, 50], &[ - 11, 22, 33, 40, 42, - ]); + check_difference( + &[-5, 11, 22, 33, 40, 42], + &[-12, -5, 14, 23, 34, 38, 39, 50], + &[11, 22, 33, 40, 42], + ); if cfg!(miri) { // Miri is too slow @@ -250,9 +252,11 @@ fn test_union() { check_union(&[], &[], &[]); check_union(&[1, 2, 3], &[2], &[1, 2, 3]); check_union(&[2], &[1, 2, 3], &[1, 2, 3]); - check_union(&[1, 3, 5, 9, 11, 16, 19, 24], &[-2, 1, 5, 9, 13, 19], &[ - -2, 1, 3, 5, 9, 11, 13, 16, 19, 24, - ]); + check_union( + &[1, 3, 5, 9, 11, 16, 19, 24], + &[-2, 1, 5, 9, 13, 19], + &[-2, 1, 3, 5, 9, 11, 13, 16, 19, 24], + ); } #[test] diff --git a/library/alloc/src/collections/btree/split.rs b/library/alloc/src/collections/btree/split.rs index c188ed1da6113..87a79e6cf3f93 100644 --- a/library/alloc/src/collections/btree/split.rs +++ b/library/alloc/src/collections/btree/split.rs @@ -8,7 +8,7 @@ use super::search::SearchResult::*; impl Root { /// Calculates the length of both trees that result from splitting up /// a given number of distinct key-value pairs. - pub fn calc_split_length( + pub(super) fn calc_split_length( total_num: usize, root_a: &Root, root_b: &Root, @@ -31,7 +31,11 @@ impl Root { /// and if the ordering of `Q` corresponds to that of `K`. /// If `self` respects all `BTreeMap` tree invariants, then both /// `self` and the returned tree will respect those invariants. - pub fn split_off(&mut self, key: &Q, alloc: A) -> Self + pub(super) fn split_off( + &mut self, + key: &Q, + alloc: A, + ) -> Self where K: Borrow, { diff --git a/library/alloc/src/collections/linked_list/tests.rs b/library/alloc/src/collections/linked_list/tests.rs index b7d4f8512a0f2..812fe229a0fc5 100644 --- a/library/alloc/src/collections/linked_list/tests.rs +++ b/library/alloc/src/collections/linked_list/tests.rs @@ -58,7 +58,7 @@ fn list_from(v: &[T]) -> LinkedList { v.iter().cloned().collect() } -pub fn check_links(list: &LinkedList) { +fn check_links(list: &LinkedList) { unsafe { let mut len = 0; let mut last_ptr: Option<&Node> = None; @@ -696,9 +696,10 @@ fn test_cursor_mut_insert() { cursor.splice_after(p); cursor.splice_before(q); check_links(&m); - assert_eq!(m.iter().cloned().collect::>(), &[ - 200, 201, 202, 203, 1, 100, 101, 102, 103, 8, 2, 3, 4, 5, 6 - ]); + assert_eq!( + m.iter().cloned().collect::>(), + &[200, 201, 202, 203, 1, 100, 101, 102, 103, 8, 2, 3, 4, 5, 6] + ); let mut cursor = m.cursor_front_mut(); cursor.move_prev(); let tmp = cursor.split_before(); @@ -915,9 +916,10 @@ fn extract_if_complex() { assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); assert_eq!(list.len(), 14); - assert_eq!(list.into_iter().collect::>(), vec![ - 1, 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39 - ]); + assert_eq!( + list.into_iter().collect::>(), + vec![1, 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39] + ); } { @@ -932,9 +934,10 @@ fn extract_if_complex() { assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); assert_eq!(list.len(), 13); - assert_eq!(list.into_iter().collect::>(), vec![ - 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39 - ]); + assert_eq!( + list.into_iter().collect::>(), + vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39] + ); } { @@ -949,9 +952,10 @@ fn extract_if_complex() { assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]); assert_eq!(list.len(), 11); - assert_eq!(list.into_iter().collect::>(), vec![ - 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35 - ]); + assert_eq!( + list.into_iter().collect::>(), + vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35] + ); } { diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 0b6a55297e1ab..299c8b8679e3d 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -823,6 +823,7 @@ impl VecDeque { /// assert!(buf.capacity() >= 11); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "vecdeque_reserve")] #[track_caller] pub fn reserve(&mut self, additional: usize) { let new_cap = self.len.checked_add(additional).expect("capacity overflow"); @@ -1735,6 +1736,52 @@ impl VecDeque { } } + /// Removes and returns the first element from the deque if the predicate + /// returns `true`, or [`None`] if the predicate returns false or the deque + /// is empty (the predicate will not be called in that case). + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_deque_pop_if)] + /// use std::collections::VecDeque; + /// + /// let mut deque: VecDeque = vec![0, 1, 2, 3, 4].into(); + /// let pred = |x: &mut i32| *x % 2 == 0; + /// + /// assert_eq!(deque.pop_front_if(pred), Some(0)); + /// assert_eq!(deque, [1, 2, 3, 4]); + /// assert_eq!(deque.pop_front_if(pred), None); + /// ``` + #[unstable(feature = "vec_deque_pop_if", issue = "135889")] + pub fn pop_front_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option { + let first = self.front_mut()?; + if predicate(first) { self.pop_front() } else { None } + } + + /// Removes and returns the last element from the deque if the predicate + /// returns `true`, or [`None`] if the predicate returns false or the deque + /// is empty (the predicate will not be called in that case). + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_deque_pop_if)] + /// use std::collections::VecDeque; + /// + /// let mut deque: VecDeque = vec![0, 1, 2, 3, 4].into(); + /// let pred = |x: &mut i32| *x % 2 == 0; + /// + /// assert_eq!(deque.pop_back_if(pred), Some(4)); + /// assert_eq!(deque, [0, 1, 2, 3]); + /// assert_eq!(deque.pop_back_if(pred), None); + /// ``` + #[unstable(feature = "vec_deque_pop_if", issue = "135889")] + pub fn pop_back_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option { + let first = self.back_mut()?; + if predicate(first) { self.pop_back() } else { None } + } + /// Prepends an element to the deque. /// /// # Examples diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs index 6328b3b4db867..c90679f179775 100644 --- a/library/alloc/src/collections/vec_deque/tests.rs +++ b/library/alloc/src/collections/vec_deque/tests.rs @@ -562,9 +562,10 @@ fn make_contiguous_head_to_end() { tester.push_front(i as char); } - assert_eq!(tester, [ - 'P', 'O', 'N', 'M', 'L', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K' - ]); + assert_eq!( + tester, + ['P', 'O', 'N', 'M', 'L', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K'] + ); // ABCDEFGHIJKPONML let expected_start = 0; diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index c7d6d8a55c2e3..fd93045a5ac4d 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -397,7 +397,7 @@ impl CString { // information about the size of the allocation is correct on Rust's // side. unsafe { - extern "C" { + unsafe extern "C" { /// Provided by libc or compiler_builtins. fn strlen(s: *const c_char) -> usize; } @@ -965,8 +965,9 @@ impl Default for Rc { /// This may or may not share an allocation with other Rcs on the same thread. #[inline] fn default() -> Self { - let c_str: &CStr = Default::default(); - Rc::from(c_str) + let rc = Rc::<[u8]>::from(*b"\0"); + // `[u8]` has the same layout as `CStr`, and it is `NUL` terminated. + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const CStr) } } } diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 40759cb0ba83c..1bb0f76106472 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -88,10 +88,10 @@ #![allow(rustdoc::redundant_explicit_links)] #![warn(rustdoc::unescaped_backticks)] #![deny(ffi_unwind_calls)] +#![warn(unreachable_pub)] // // Library features: // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(async_closure))] #![cfg_attr(test, feature(str_as_str))] #![feature(alloc_layout_extra)] #![feature(allocator_api)] @@ -103,6 +103,8 @@ #![feature(async_fn_traits)] #![feature(async_iterator)] #![feature(box_uninit_write)] +#![feature(bstr)] +#![feature(bstr_internals)] #![feature(clone_to_uninit)] #![feature(coerce_unsized)] #![feature(const_eval_select)] @@ -127,6 +129,7 @@ #![feature(local_waker)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array_transpose)] +#![feature(nonnull_provenance)] #![feature(panic_internals)] #![feature(pattern)] #![feature(pin_coerce_unsized_trait)] @@ -143,6 +146,7 @@ #![feature(slice_range)] #![feature(std_internals)] #![feature(str_internals)] +#![feature(temporary_niche_types)] #![feature(trusted_fused)] #![feature(trusted_len)] #![feature(trusted_random_access)] @@ -152,7 +156,6 @@ #![feature(unicode_internals)] #![feature(unsize)] #![feature(unwrap_infallible)] -#![feature(vec_pop_if)] // tidy-alphabetical-end // // Language features: @@ -168,6 +171,7 @@ #![feature(dropck_eyepatch)] #![feature(fundamental)] #![feature(hashmap_internals)] +#![feature(intrinsics)] #![feature(lang_items)] #![feature(min_specialization)] #![feature(multiple_supertrait_upcastable)] @@ -223,9 +227,11 @@ pub mod alloc; pub mod boxed; #[cfg(test)] mod boxed { - pub use std::boxed::Box; + pub(crate) use std::boxed::Box; } pub mod borrow; +#[unstable(feature = "bstr", issue = "134915")] +pub mod bstr; pub mod collections; #[cfg(all(not(no_rc), not(no_sync), not(no_global_oom_handling)))] pub mod ffi; diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs index 8c6a367869ce0..c000fd6f4efa7 100644 --- a/library/alloc/src/macros.rs +++ b/library/alloc/src/macros.rs @@ -48,10 +48,9 @@ macro_rules! vec { ); ($($x:expr),+ $(,)?) => ( <[_]>::into_vec( - // This rustc_box is not required, but it produces a dramatic improvement in compile - // time when constructing arrays with many elements. - #[rustc_box] - $crate::boxed::Box::new([$($x),+]) + // Using the intrinsic produces a dramatic improvement in stack usage for + // unoptimized programs using this code path to construct large Vecs. + $crate::boxed::box_new([$($x),+]) ) ); } diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index e93ff2f902378..b80d1fc788947 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -33,21 +33,15 @@ enum AllocInit { Zeroed, } -#[repr(transparent)] -#[cfg_attr(target_pointer_width = "16", rustc_layout_scalar_valid_range_end(0x7fff))] -#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0x7fff_ffff))] -#[cfg_attr(target_pointer_width = "64", rustc_layout_scalar_valid_range_end(0x7fff_ffff_ffff_ffff))] -struct Cap(usize); +type Cap = core::num::niche_types::UsizeNoHighBit; -impl Cap { - const ZERO: Cap = unsafe { Cap(0) }; +const ZERO_CAP: Cap = unsafe { Cap::new_unchecked(0) }; - /// `Cap(cap)`, except if `T` is a ZST then `Cap::ZERO`. - /// - /// # Safety: cap must be <= `isize::MAX`. - unsafe fn new(cap: usize) -> Self { - if T::IS_ZST { Cap::ZERO } else { unsafe { Self(cap) } } - } +/// `Cap(cap)`, except if `T` is a ZST then `Cap::ZERO`. +/// +/// # Safety: cap must be <= `isize::MAX`. +unsafe fn new_cap(cap: usize) -> Cap { + if T::IS_ZST { ZERO_CAP } else { unsafe { Cap::new_unchecked(cap) } } } /// A low-level utility for more ergonomically allocating, reallocating, and deallocating @@ -103,7 +97,7 @@ impl RawVec { /// `RawVec` with capacity `usize::MAX`. Useful for implementing /// delayed allocation. #[must_use] - pub const fn new() -> Self { + pub(crate) const fn new() -> Self { Self::new_in(Global) } @@ -126,7 +120,7 @@ impl RawVec { #[must_use] #[inline] #[track_caller] - pub fn with_capacity(capacity: usize) -> Self { + pub(crate) fn with_capacity(capacity: usize) -> Self { Self { inner: RawVecInner::with_capacity(capacity, T::LAYOUT), _marker: PhantomData } } @@ -135,7 +129,7 @@ impl RawVec { #[must_use] #[inline] #[track_caller] - pub fn with_capacity_zeroed(capacity: usize) -> Self { + pub(crate) fn with_capacity_zeroed(capacity: usize) -> Self { Self { inner: RawVecInner::with_capacity_zeroed_in(capacity, Global, T::LAYOUT), _marker: PhantomData, @@ -178,7 +172,7 @@ impl RawVec { /// Like `new`, but parameterized over the choice of allocator for /// the returned `RawVec`. #[inline] - pub const fn new_in(alloc: A) -> Self { + pub(crate) const fn new_in(alloc: A) -> Self { Self { inner: RawVecInner::new_in(alloc, align_of::()), _marker: PhantomData } } @@ -187,7 +181,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] #[track_caller] - pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { + pub(crate) fn with_capacity_in(capacity: usize, alloc: A) -> Self { Self { inner: RawVecInner::with_capacity_in(capacity, alloc, T::LAYOUT), _marker: PhantomData, @@ -197,7 +191,7 @@ impl RawVec { /// Like `try_with_capacity`, but parameterized over the choice of /// allocator for the returned `RawVec`. #[inline] - pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result { + pub(crate) fn try_with_capacity_in(capacity: usize, alloc: A) -> Result { match RawVecInner::try_with_capacity_in(capacity, alloc, T::LAYOUT) { Ok(inner) => Ok(Self { inner, _marker: PhantomData }), Err(e) => Err(e), @@ -209,7 +203,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] #[track_caller] - pub fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self { + pub(crate) fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self { Self { inner: RawVecInner::with_capacity_zeroed_in(capacity, alloc, T::LAYOUT), _marker: PhantomData, @@ -228,7 +222,7 @@ impl RawVec { /// /// Note, that the requested capacity and `self.capacity()` could differ, as /// an allocator could overallocate and return a greater memory block than requested. - pub unsafe fn into_box(self, len: usize) -> Box<[MaybeUninit], A> { + pub(crate) unsafe fn into_box(self, len: usize) -> Box<[MaybeUninit], A> { // Sanity-check one half of the safety requirement (we cannot check the other half). debug_assert!( len <= self.capacity(), @@ -253,11 +247,11 @@ impl RawVec { /// If the `ptr` and `capacity` come from a `RawVec` created via `alloc`, then this is /// guaranteed. #[inline] - pub unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, alloc: A) -> Self { + pub(crate) unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, alloc: A) -> Self { // SAFETY: Precondition passed to the caller unsafe { let ptr = ptr.cast(); - let capacity = Cap::new::(capacity); + let capacity = new_cap::(capacity); Self { inner: RawVecInner::from_raw_parts_in(ptr, capacity, alloc), _marker: PhantomData, @@ -271,11 +265,11 @@ impl RawVec { /// /// See [`RawVec::from_raw_parts_in`]. #[inline] - pub unsafe fn from_nonnull_in(ptr: NonNull, capacity: usize, alloc: A) -> Self { + pub(crate) unsafe fn from_nonnull_in(ptr: NonNull, capacity: usize, alloc: A) -> Self { // SAFETY: Precondition passed to the caller unsafe { let ptr = ptr.cast(); - let capacity = Cap::new::(capacity); + let capacity = new_cap::(capacity); Self { inner: RawVecInner::from_nonnull_in(ptr, capacity, alloc), _marker: PhantomData } } } @@ -284,12 +278,12 @@ impl RawVec { /// `Unique::dangling()` if `capacity == 0` or `T` is zero-sized. In the former case, you must /// be careful. #[inline] - pub const fn ptr(&self) -> *mut T { + pub(crate) const fn ptr(&self) -> *mut T { self.inner.ptr() } #[inline] - pub fn non_null(&self) -> NonNull { + pub(crate) fn non_null(&self) -> NonNull { self.inner.non_null() } @@ -297,13 +291,13 @@ impl RawVec { /// /// This will always be `usize::MAX` if `T` is zero-sized. #[inline] - pub const fn capacity(&self) -> usize { + pub(crate) const fn capacity(&self) -> usize { self.inner.capacity(size_of::()) } /// Returns a shared reference to the allocator backing this `RawVec`. #[inline] - pub fn allocator(&self) -> &A { + pub(crate) fn allocator(&self) -> &A { self.inner.allocator() } @@ -329,7 +323,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] #[track_caller] - pub fn reserve(&mut self, len: usize, additional: usize) { + pub(crate) fn reserve(&mut self, len: usize, additional: usize) { self.inner.reserve(len, additional, T::LAYOUT) } @@ -338,12 +332,16 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline(never)] #[track_caller] - pub fn grow_one(&mut self) { + pub(crate) fn grow_one(&mut self) { self.inner.grow_one(T::LAYOUT) } /// The same as `reserve`, but returns on errors instead of panicking or aborting. - pub fn try_reserve(&mut self, len: usize, additional: usize) -> Result<(), TryReserveError> { + pub(crate) fn try_reserve( + &mut self, + len: usize, + additional: usize, + ) -> Result<(), TryReserveError> { self.inner.try_reserve(len, additional, T::LAYOUT) } @@ -366,12 +364,12 @@ impl RawVec { /// Aborts on OOM. #[cfg(not(no_global_oom_handling))] #[track_caller] - pub fn reserve_exact(&mut self, len: usize, additional: usize) { + pub(crate) fn reserve_exact(&mut self, len: usize, additional: usize) { self.inner.reserve_exact(len, additional, T::LAYOUT) } /// The same as `reserve_exact`, but returns on errors instead of panicking or aborting. - pub fn try_reserve_exact( + pub(crate) fn try_reserve_exact( &mut self, len: usize, additional: usize, @@ -392,7 +390,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[track_caller] #[inline] - pub fn shrink_to_fit(&mut self, cap: usize) { + pub(crate) fn shrink_to_fit(&mut self, cap: usize) { self.inner.shrink_to_fit(cap, T::LAYOUT) } } @@ -410,7 +408,7 @@ impl RawVecInner { const fn new_in(alloc: A, align: usize) -> Self { let ptr = unsafe { core::mem::transmute(align) }; // `cap: 0` means "unallocated". zero-sized types are ignored. - Self { ptr, cap: Cap::ZERO, alloc } + Self { ptr, cap: ZERO_CAP, alloc } } #[cfg(not(no_global_oom_handling))] @@ -483,7 +481,11 @@ impl RawVecInner { // Allocators currently return a `NonNull<[u8]>` whose length // matches the size requested. If that ever changes, the capacity // here should change to `ptr.len() / mem::size_of::()`. - Ok(Self { ptr: Unique::from(ptr.cast()), cap: unsafe { Cap(capacity) }, alloc }) + Ok(Self { + ptr: Unique::from(ptr.cast()), + cap: unsafe { Cap::new_unchecked(capacity) }, + alloc, + }) } #[inline] @@ -508,7 +510,7 @@ impl RawVecInner { #[inline] const fn capacity(&self, elem_size: usize) -> usize { - if elem_size == 0 { usize::MAX } else { self.cap.0 } + if elem_size == 0 { usize::MAX } else { self.cap.as_inner() } } #[inline] @@ -518,7 +520,7 @@ impl RawVecInner { #[inline] fn current_memory(&self, elem_layout: Layout) -> Option<(NonNull, Layout)> { - if elem_layout.size() == 0 || self.cap.0 == 0 { + if elem_layout.size() == 0 || self.cap.as_inner() == 0 { None } else { // We could use Layout::array here which ensures the absence of isize and usize overflows @@ -526,7 +528,7 @@ impl RawVecInner { // has already been allocated so we know it can't overflow and currently Rust does not // support such types. So we can do better by skipping some checks and avoid an unwrap. unsafe { - let alloc_size = elem_layout.size().unchecked_mul(self.cap.0); + let alloc_size = elem_layout.size().unchecked_mul(self.cap.as_inner()); let layout = Layout::from_size_align_unchecked(alloc_size, elem_layout.align()); Some((self.ptr.into(), layout)) } @@ -562,7 +564,7 @@ impl RawVecInner { #[inline] #[track_caller] fn grow_one(&mut self, elem_layout: Layout) { - if let Err(err) = self.grow_amortized(self.cap.0, 1, elem_layout) { + if let Err(err) = self.grow_amortized(self.cap.as_inner(), 1, elem_layout) { handle_error(err); } } @@ -627,7 +629,7 @@ impl RawVecInner { // the size requested. If that ever changes, the capacity here should // change to `ptr.len() / mem::size_of::()`. self.ptr = Unique::from(ptr.cast()); - self.cap = unsafe { Cap(cap) }; + self.cap = unsafe { Cap::new_unchecked(cap) }; } fn grow_amortized( @@ -650,7 +652,7 @@ impl RawVecInner { // This guarantees exponential growth. The doubling cannot overflow // because `cap <= isize::MAX` and the type of `cap` is `usize`. - let cap = cmp::max(self.cap.0 * 2, required_cap); + let cap = cmp::max(self.cap.as_inner() * 2, required_cap); let cap = cmp::max(min_non_zero_cap(elem_layout.size()), cap); let new_layout = layout_array(cap, elem_layout)?; @@ -719,7 +721,7 @@ impl RawVecInner { unsafe { self.alloc.deallocate(ptr, layout) }; self.ptr = unsafe { Unique::new_unchecked(ptr::without_provenance_mut(elem_layout.align())) }; - self.cap = Cap::ZERO; + self.cap = ZERO_CAP; } else { let ptr = unsafe { // Layout cannot overflow here because it would have diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index e014404eff35b..09206c2f8b290 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -252,6 +252,7 @@ use core::intrinsics::abort; use core::iter; use core::marker::{PhantomData, Unsize}; use core::mem::{self, ManuallyDrop, align_of_val_raw}; +use core::num::NonZeroUsize; use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, LegacyReceiver}; use core::panic::{RefUnwindSafe, UnwindSafe}; #[cfg(not(no_global_oom_handling))] @@ -1461,18 +1462,18 @@ impl Rc { /// Provides a raw pointer to the data. /// /// The counts are not affected in any way and the `Rc` is not consumed. The pointer is valid - /// for as long there are strong counts in the `Rc`. + /// for as long as there are strong counts in the `Rc`. /// /// # Examples /// /// ``` /// use std::rc::Rc; /// - /// let x = Rc::new("hello".to_owned()); + /// let x = Rc::new(0); /// let y = Rc::clone(&x); /// let x_ptr = Rc::as_ptr(&x); /// assert_eq!(x_ptr, Rc::as_ptr(&y)); - /// assert_eq!(unsafe { &*x_ptr }, "hello"); + /// assert_eq!(unsafe { *x_ptr }, 0); /// ``` #[stable(feature = "weak_into_raw", since = "1.45.0")] #[rustc_never_returns_null_ptr] @@ -1789,7 +1790,7 @@ impl Rc { /// let x: Rc<&str> = Rc::new("Hello, world!"); /// { /// let s = String::from("Oh, no!"); - /// let mut y: Rc<&str> = x.clone().into(); + /// let mut y: Rc<&str> = x.clone(); /// unsafe { /// // this is Undefined Behavior, because x's inner type /// // is &'long str, not &'short str @@ -2349,11 +2350,10 @@ impl Default for Rc { fn default() -> Rc { unsafe { Self::from_inner( - Box::leak(Box::write(Box::new_uninit(), RcInner { - strong: Cell::new(1), - weak: Cell::new(1), - value: T::default(), - })) + Box::leak(Box::write( + Box::new_uninit(), + RcInner { strong: Cell::new(1), weak: Cell::new(1), value: T::default() }, + )) .into(), ) } @@ -2368,7 +2368,9 @@ impl Default for Rc { /// This may or may not share an allocation with other Rcs on the same thread. #[inline] fn default() -> Self { - Rc::from("") + let rc = Rc::<[u8]>::default(); + // `[u8]` has the same layout as `str`. + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const str) } } } @@ -3027,12 +3029,7 @@ impl Weak { #[rustc_const_stable(feature = "const_weak_new", since = "1.73.0")] #[must_use] pub const fn new() -> Weak { - Weak { - ptr: unsafe { - NonNull::new_unchecked(ptr::without_provenance_mut::>(usize::MAX)) - }, - alloc: Global, - } + Weak { ptr: NonNull::without_provenance(NonZeroUsize::MAX), alloc: Global } } } @@ -3054,12 +3051,7 @@ impl Weak { #[inline] #[unstable(feature = "allocator_api", issue = "32838")] pub fn new_in(alloc: A) -> Weak { - Weak { - ptr: unsafe { - NonNull::new_unchecked(ptr::without_provenance_mut::>(usize::MAX)) - }, - alloc, - } + Weak { ptr: NonNull::without_provenance(NonZeroUsize::MAX), alloc } } } @@ -3717,7 +3709,11 @@ pub struct UniqueRc< #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, > { ptr: NonNull>, - phantom: PhantomData>, + // Define the ownership of `RcInner` for drop-check + _marker: PhantomData>, + // Invariance is necessary for soundness: once other `Weak` + // references exist, we already have a form of shared mutability! + _marker2: PhantomData<*mut T>, alloc: A, } @@ -4003,7 +3999,7 @@ impl UniqueRc { }, alloc, )); - Self { ptr: ptr.into(), phantom: PhantomData, alloc } + Self { ptr: ptr.into(), _marker: PhantomData, _marker2: PhantomData, alloc } } } diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index edc8d99f2f990..dcd95ddf00ff5 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -27,8 +27,8 @@ pub use core::slice::ArrayChunksMut; pub use core::slice::ArrayWindows; #[stable(feature = "inherent_ascii_escape", since = "1.60.0")] pub use core::slice::EscapeAscii; -#[unstable(feature = "get_many_mut", issue = "104642")] -pub use core::slice::GetManyMutError; +#[stable(feature = "get_many_mut", since = "1.86.0")] +pub use core::slice::GetDisjointMutError; #[stable(feature = "slice_get_slice", since = "1.28.0")] pub use core::slice::SliceIndex; #[cfg(not(no_global_oom_handling))] @@ -85,6 +85,7 @@ use crate::vec::Vec; // functions are actually methods that are in `impl [T]` but not in // `core::slice::SliceExt` - we need to supply these functions for the // `test_permutations` test +#[allow(unreachable_pub)] // cfg(test) pub above pub(crate) mod hack { use core::alloc::Allocator; diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 23d060d2158cd..154da69107884 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -62,10 +62,10 @@ use crate::alloc::Allocator; use crate::borrow::{Cow, ToOwned}; use crate::boxed::Box; use crate::collections::TryReserveError; -use crate::str::{self, Chars, Utf8Error, from_utf8_unchecked_mut}; +use crate::str::{self, CharIndices, Chars, Utf8Error, from_utf8_unchecked_mut}; #[cfg(not(no_global_oom_handling))] use crate::str::{FromStr, from_boxed_utf8_unchecked}; -use crate::vec::Vec; +use crate::vec::{self, Vec}; /// A UTF-8–encoded, growable string. /// @@ -712,8 +712,8 @@ impl String { } } - /// Decode a UTF-16–encoded vector `v` into a `String`, returning [`Err`] - /// if `v` contains any invalid data. + /// Decode a native endian UTF-16–encoded vector `v` into a `String`, + /// returning [`Err`] if `v` contains any invalid data. /// /// # Examples /// @@ -745,8 +745,8 @@ impl String { Ok(ret) } - /// Decode a UTF-16–encoded slice `v` into a `String`, replacing - /// invalid data with [the replacement character (`U+FFFD`)][U+FFFD]. + /// Decode a native endian UTF-16–encoded slice `v` into a `String`, + /// replacing invalid data with [the replacement character (`U+FFFD`)][U+FFFD]. /// /// Unlike [`from_utf8_lossy`] which returns a [`Cow<'a, str>`], /// `from_utf16_lossy` returns a `String` since the UTF-16 to UTF-8 @@ -777,8 +777,8 @@ impl String { .collect() } - /// Decode a UTF-16LE–encoded vector `v` into a `String`, returning [`Err`] - /// if `v` contains any invalid data. + /// Decode a UTF-16LE–encoded vector `v` into a `String`, + /// returning [`Err`] if `v` contains any invalid data. /// /// # Examples /// @@ -852,8 +852,8 @@ impl String { } } - /// Decode a UTF-16BE–encoded vector `v` into a `String`, returning [`Err`] - /// if `v` contains any invalid data. + /// Decode a UTF-16BE–encoded vector `v` into a `String`, + /// returning [`Err`] if `v` contains any invalid data. /// /// # Examples /// @@ -1952,6 +1952,61 @@ impl String { Drain { start, end, iter: chars_iter, string: self_ptr } } + /// Converts a `String` into an iterator over the [`char`]s of the string. + /// + /// As a string consists of valid UTF-8, we can iterate through a string + /// by [`char`]. This method returns such an iterator. + /// + /// It's important to remember that [`char`] represents a Unicode Scalar + /// Value, and might not match your idea of what a 'character' is. Iteration + /// over grapheme clusters may be what you actually want. That functionality + /// is not provided by Rust's standard library, check crates.io instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(string_into_chars)] + /// + /// let word = String::from("goodbye"); + /// + /// let mut chars = word.into_chars(); + /// + /// assert_eq!(Some('g'), chars.next()); + /// assert_eq!(Some('o'), chars.next()); + /// assert_eq!(Some('o'), chars.next()); + /// assert_eq!(Some('d'), chars.next()); + /// assert_eq!(Some('b'), chars.next()); + /// assert_eq!(Some('y'), chars.next()); + /// assert_eq!(Some('e'), chars.next()); + /// + /// assert_eq!(None, chars.next()); + /// ``` + /// + /// Remember, [`char`]s might not match your intuition about characters: + /// + /// ``` + /// #![feature(string_into_chars)] + /// + /// let y = String::from("y̆"); + /// + /// let mut chars = y.into_chars(); + /// + /// assert_eq!(Some('y'), chars.next()); // not 'y̆' + /// assert_eq!(Some('\u{0306}'), chars.next()); + /// + /// assert_eq!(None, chars.next()); + /// ``` + /// + /// [`char`]: prim@char + #[inline] + #[must_use = "`self` will be dropped if the result is not used"] + #[unstable(feature = "string_into_chars", issue = "133125")] + pub fn into_chars(self) -> IntoChars { + IntoChars { bytes: self.into_bytes().into_iter() } + } + /// Removes the specified range in the string, /// and replaces it with the given string. /// The given string doesn't need to be the same length as the range. @@ -2387,6 +2442,32 @@ impl<'a> Extend> for String { } } +#[cfg(not(no_global_oom_handling))] +#[unstable(feature = "ascii_char", issue = "110998")] +impl Extend for String { + fn extend>(&mut self, iter: I) { + self.vec.extend(iter.into_iter().map(|c| c.to_u8())); + } + + #[inline] + fn extend_one(&mut self, c: core::ascii::Char) { + self.vec.push(c.to_u8()); + } +} + +#[cfg(not(no_global_oom_handling))] +#[unstable(feature = "ascii_char", issue = "110998")] +impl<'a> Extend<&'a core::ascii::Char> for String { + fn extend>(&mut self, iter: I) { + self.extend(iter.into_iter().cloned()); + } + + #[inline] + fn extend_one(&mut self, c: &'a core::ascii::Char) { + self.vec.push(c.to_u8()); + } +} + /// A convenience impl that delegates to the impl for `&str`. /// /// # Examples @@ -3090,6 +3171,134 @@ impl fmt::Write for String { } } +/// An iterator over the [`char`]s of a string. +/// +/// This struct is created by the [`into_chars`] method on [`String`]. +/// See its documentation for more. +/// +/// [`char`]: prim@char +/// [`into_chars`]: String::into_chars +#[cfg_attr(not(no_global_oom_handling), derive(Clone))] +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[unstable(feature = "string_into_chars", issue = "133125")] +pub struct IntoChars { + bytes: vec::IntoIter, +} + +#[unstable(feature = "string_into_chars", issue = "133125")] +impl fmt::Debug for IntoChars { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("IntoChars").field(&self.as_str()).finish() + } +} + +impl IntoChars { + /// Views the underlying data as a subslice of the original data. + /// + /// # Examples + /// + /// ``` + /// #![feature(string_into_chars)] + /// + /// let mut chars = String::from("abc").into_chars(); + /// + /// assert_eq!(chars.as_str(), "abc"); + /// chars.next(); + /// assert_eq!(chars.as_str(), "bc"); + /// chars.next(); + /// chars.next(); + /// assert_eq!(chars.as_str(), ""); + /// ``` + #[unstable(feature = "string_into_chars", issue = "133125")] + #[must_use] + #[inline] + pub fn as_str(&self) -> &str { + // SAFETY: `bytes` is a valid UTF-8 string. + unsafe { str::from_utf8_unchecked(self.bytes.as_slice()) } + } + + /// Consumes the `IntoChars`, returning the remaining string. + /// + /// # Examples + /// + /// ``` + /// #![feature(string_into_chars)] + /// + /// let chars = String::from("abc").into_chars(); + /// assert_eq!(chars.into_string(), "abc"); + /// + /// let mut chars = String::from("def").into_chars(); + /// chars.next(); + /// assert_eq!(chars.into_string(), "ef"); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "string_into_chars", issue = "133125")] + #[inline] + pub fn into_string(self) -> String { + // Safety: `bytes` are kept in UTF-8 form, only removing whole `char`s at a time. + unsafe { String::from_utf8_unchecked(self.bytes.collect()) } + } + + #[inline] + fn iter(&self) -> CharIndices<'_> { + self.as_str().char_indices() + } +} + +#[unstable(feature = "string_into_chars", issue = "133125")] +impl Iterator for IntoChars { + type Item = char; + + #[inline] + fn next(&mut self) -> Option { + let mut iter = self.iter(); + match iter.next() { + None => None, + Some((_, ch)) => { + let offset = iter.offset(); + // `offset` is a valid index. + let _ = self.bytes.advance_by(offset); + Some(ch) + } + } + } + + #[inline] + fn count(self) -> usize { + self.iter().count() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter().size_hint() + } + + #[inline] + fn last(mut self) -> Option { + self.next_back() + } +} + +#[unstable(feature = "string_into_chars", issue = "133125")] +impl DoubleEndedIterator for IntoChars { + #[inline] + fn next_back(&mut self) -> Option { + let len = self.as_str().len(); + let mut iter = self.iter(); + match iter.next_back() { + None => None, + Some((idx, ch)) => { + // `idx` is a valid index. + let _ = self.bytes.advance_back_by(len - idx); + Some(ch) + } + } + } +} + +#[unstable(feature = "string_into_chars", issue = "133125")] +impl FusedIterator for IntoChars {} + /// A draining iterator for `String`. /// /// This struct is created by the [`drain`] method on [`String`]. See its diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index b34a6d3f660c8..dba1449347ac0 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -18,6 +18,7 @@ use core::intrinsics::abort; use core::iter; use core::marker::{PhantomData, Unsize}; use core::mem::{self, ManuallyDrop, align_of_val_raw}; +use core::num::NonZeroUsize; use core::ops::{CoerceUnsized, Deref, DerefPure, DispatchFromDyn, LegacyReceiver}; use core::panic::{RefUnwindSafe, UnwindSafe}; use core::pin::{Pin, PinCoerceUnsized}; @@ -1396,6 +1397,8 @@ impl Arc { /// different types. See [`mem::transmute`][transmute] for more information /// on what restrictions apply in this case. /// + /// The raw pointer must point to a block of memory allocated by the global allocator. + /// /// The user of `from_raw` has to make sure a specific value of `T` is only /// dropped once. /// @@ -1451,7 +1454,8 @@ impl Arc { /// /// The pointer must have been obtained through `Arc::into_raw`, and the /// associated `Arc` instance must be valid (i.e. the strong count must be at - /// least 1) for the duration of this method. + /// least 1) for the duration of this method, and `ptr` must point to a block of memory + /// allocated by the global allocator. /// /// # Examples /// @@ -1485,7 +1489,8 @@ impl Arc { /// /// The pointer must have been obtained through `Arc::into_raw`, and the /// associated `Arc` instance must be valid (i.e. the strong count must be at - /// least 1) when invoking this method. This method can be used to release the final + /// least 1) when invoking this method, and `ptr` must point to a block of memory + /// allocated by the global allocator. This method can be used to release the final /// `Arc` and backing storage, but **should not** be called after the final `Arc` has been /// released. /// @@ -2468,7 +2473,7 @@ impl Arc { /// let x: Arc<&str> = Arc::new("Hello, world!"); /// { /// let s = String::from("Oh, no!"); - /// let mut y: Arc<&str> = x.clone().into(); + /// let mut y: Arc<&str> = x.clone(); /// unsafe { /// // this is Undefined Behavior, because x's inner type /// // is &'long str, not &'short str @@ -2687,12 +2692,7 @@ impl Weak { #[rustc_const_stable(feature = "const_weak_new", since = "1.73.0")] #[must_use] pub const fn new() -> Weak { - Weak { - ptr: unsafe { - NonNull::new_unchecked(ptr::without_provenance_mut::>(usize::MAX)) - }, - alloc: Global, - } + Weak { ptr: NonNull::without_provenance(NonZeroUsize::MAX), alloc: Global } } } @@ -2717,12 +2717,7 @@ impl Weak { #[inline] #[unstable(feature = "allocator_api", issue = "32838")] pub fn new_in(alloc: A) -> Weak { - Weak { - ptr: unsafe { - NonNull::new_unchecked(ptr::without_provenance_mut::>(usize::MAX)) - }, - alloc, - } + Weak { ptr: NonNull::without_provenance(NonZeroUsize::MAX), alloc } } } @@ -2745,7 +2740,7 @@ impl Weak { /// # Safety /// /// The pointer must have originated from the [`into_raw`] and must still own its potential - /// weak reference. + /// weak reference, and must point to a block of memory allocated by global allocator. /// /// It is allowed for the strong count to be 0 at the time of calling this. Nevertheless, this /// takes ownership of one weak reference currently represented as a raw pointer (the weak @@ -3471,11 +3466,14 @@ impl Default for Arc { fn default() -> Arc { unsafe { Self::from_inner( - Box::leak(Box::write(Box::new_uninit(), ArcInner { - strong: atomic::AtomicUsize::new(1), - weak: atomic::AtomicUsize::new(1), - data: T::default(), - })) + Box::leak(Box::write( + Box::new_uninit(), + ArcInner { + strong: atomic::AtomicUsize::new(1), + weak: atomic::AtomicUsize::new(1), + data: T::default(), + }, + )) .into(), ) } diff --git a/library/alloc/src/testing/crash_test.rs b/library/alloc/src/testing/crash_test.rs index 684bac60d9a86..8e00e4f41e5d6 100644 --- a/library/alloc/src/testing/crash_test.rs +++ b/library/alloc/src/testing/crash_test.rs @@ -11,7 +11,7 @@ use crate::fmt::Debug; // the `Debug` trait is the only thing we use from `crate /// Crash test dummies are identified and ordered by an id, so they can be used /// as keys in a BTreeMap. #[derive(Debug)] -pub struct CrashTestDummy { +pub(crate) struct CrashTestDummy { pub id: usize, cloned: AtomicUsize, dropped: AtomicUsize, @@ -20,7 +20,7 @@ pub struct CrashTestDummy { impl CrashTestDummy { /// Creates a crash test dummy design. The `id` determines order and equality of instances. - pub fn new(id: usize) -> CrashTestDummy { + pub(crate) fn new(id: usize) -> CrashTestDummy { CrashTestDummy { id, cloned: AtomicUsize::new(0), @@ -31,34 +31,34 @@ impl CrashTestDummy { /// Creates an instance of a crash test dummy that records what events it experiences /// and optionally panics. - pub fn spawn(&self, panic: Panic) -> Instance<'_> { + pub(crate) fn spawn(&self, panic: Panic) -> Instance<'_> { Instance { origin: self, panic } } /// Returns how many times instances of the dummy have been cloned. - pub fn cloned(&self) -> usize { + pub(crate) fn cloned(&self) -> usize { self.cloned.load(SeqCst) } /// Returns how many times instances of the dummy have been dropped. - pub fn dropped(&self) -> usize { + pub(crate) fn dropped(&self) -> usize { self.dropped.load(SeqCst) } /// Returns how many times instances of the dummy have had their `query` member invoked. - pub fn queried(&self) -> usize { + pub(crate) fn queried(&self) -> usize { self.queried.load(SeqCst) } } #[derive(Debug)] -pub struct Instance<'a> { +pub(crate) struct Instance<'a> { origin: &'a CrashTestDummy, panic: Panic, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Panic { +pub(crate) enum Panic { Never, InClone, InDrop, @@ -66,12 +66,12 @@ pub enum Panic { } impl Instance<'_> { - pub fn id(&self) -> usize { + pub(crate) fn id(&self) -> usize { self.origin.id } /// Some anonymous query, the result of which is already given. - pub fn query(&self, result: R) -> R { + pub(crate) fn query(&self, result: R) -> R { self.origin.queried.fetch_add(1, SeqCst); if self.panic == Panic::InQuery { panic!("panic in `query`"); diff --git a/library/alloc/src/testing/mod.rs b/library/alloc/src/testing/mod.rs index 7a094f8a59522..c8457daf93e5a 100644 --- a/library/alloc/src/testing/mod.rs +++ b/library/alloc/src/testing/mod.rs @@ -1,3 +1,3 @@ -pub mod crash_test; -pub mod ord_chaos; -pub mod rng; +pub(crate) mod crash_test; +pub(crate) mod ord_chaos; +pub(crate) mod rng; diff --git a/library/alloc/src/testing/ord_chaos.rs b/library/alloc/src/testing/ord_chaos.rs index 96ce7c1579046..55e1ae5e3deaa 100644 --- a/library/alloc/src/testing/ord_chaos.rs +++ b/library/alloc/src/testing/ord_chaos.rs @@ -4,7 +4,7 @@ use std::ptr; // Minimal type with an `Ord` implementation violating transitivity. #[derive(Debug)] -pub enum Cyclic3 { +pub(crate) enum Cyclic3 { A, B, C, @@ -37,16 +37,16 @@ impl Eq for Cyclic3 {} // Controls the ordering of values wrapped by `Governed`. #[derive(Debug)] -pub struct Governor { +pub(crate) struct Governor { flipped: Cell, } impl Governor { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Governor { flipped: Cell::new(false) } } - pub fn flip(&self) { + pub(crate) fn flip(&self) { self.flipped.set(!self.flipped.get()); } } @@ -55,7 +55,7 @@ impl Governor { // (assuming that `T` respects total order), but can suddenly be made to invert // that total order. #[derive(Debug)] -pub struct Governed<'a, T>(pub T, pub &'a Governor); +pub(crate) struct Governed<'a, T>(pub T, pub &'a Governor); impl PartialOrd for Governed<'_, T> { fn partial_cmp(&self, other: &Self) -> Option { diff --git a/library/alloc/src/testing/rng.rs b/library/alloc/src/testing/rng.rs index ecf543bee035a..77d3348f38a5d 100644 --- a/library/alloc/src/testing/rng.rs +++ b/library/alloc/src/testing/rng.rs @@ -1,5 +1,5 @@ /// XorShiftRng -pub struct DeterministicRng { +pub(crate) struct DeterministicRng { count: usize, x: u32, y: u32, @@ -8,12 +8,12 @@ pub struct DeterministicRng { } impl DeterministicRng { - pub fn new() -> Self { + pub(crate) fn new() -> Self { DeterministicRng { count: 0, x: 0x193a6754, y: 0xa8a7d469, z: 0x97830e05, w: 0x113ba7bb } } /// Guarantees that each returned number is unique. - pub fn next(&mut self) -> u32 { + pub(crate) fn next(&mut self) -> u32 { self.count += 1; assert!(self.count <= 70029); let x = self.x; diff --git a/library/alloc/src/vec/is_zero.rs b/library/alloc/src/vec/is_zero.rs index ba57d940d8c99..a3ddd6f6e230e 100644 --- a/library/alloc/src/vec/is_zero.rs +++ b/library/alloc/src/vec/is_zero.rs @@ -40,19 +40,8 @@ impl_is_zero!(char, |x| x == '\0'); impl_is_zero!(f32, |x: f32| x.to_bits() == 0); impl_is_zero!(f64, |x: f64| x.to_bits() == 0); -unsafe impl IsZero for *const T { - #[inline] - fn is_zero(&self) -> bool { - (*self).is_null() - } -} - -unsafe impl IsZero for *mut T { - #[inline] - fn is_zero(&self) -> bool { - (*self).is_null() - } -} +// `IsZero` cannot be soundly implemented for pointers because of provenance +// (see #135338). unsafe impl IsZero for [T; N] { #[inline] diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 3a706d5f36b7f..a84bb724473a1 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1267,6 +1267,7 @@ impl Vec { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[track_caller] + #[cfg_attr(not(test), rustc_diagnostic_item = "vec_reserve")] pub fn reserve(&mut self, additional: usize) { self.buf.reserve(self.len, additional); } @@ -1836,7 +1837,7 @@ impl Vec { /// # // don't use this as a starting point for a real library. /// # pub struct StreamWrapper { strm: *mut std::ffi::c_void } /// # const Z_OK: i32 = 0; - /// # extern "C" { + /// # unsafe extern "C" { /// # fn deflateGetDictionary( /// # strm: *mut std::ffi::c_void, /// # dictionary: *mut u8, @@ -2511,15 +2512,13 @@ impl Vec { } } - /// Removes and returns the last element in a vector if the predicate + /// Removes and returns the last element from a vector if the predicate /// returns `true`, or [`None`] if the predicate returns false or the vector - /// is empty. + /// is empty (the predicate will not be called in that case). /// /// # Examples /// /// ``` - /// #![feature(vec_pop_if)] - /// /// let mut vec = vec![1, 2, 3, 4]; /// let pred = |x: &mut i32| *x % 2 == 0; /// @@ -2527,13 +2526,10 @@ impl Vec { /// assert_eq!(vec, [1, 2, 3]); /// assert_eq!(vec.pop_if(pred), None); /// ``` - #[unstable(feature = "vec_pop_if", issue = "122741")] - pub fn pop_if(&mut self, f: F) -> Option - where - F: FnOnce(&mut T) -> bool, - { + #[stable(feature = "vec_pop_if", since = "1.86.0")] + pub fn pop_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option { let last = self.last_mut()?; - if f(last) { self.pop() } else { None } + if predicate(last) { self.pop() } else { None } } /// Moves all the elements of `other` into `self`, leaving `other` empty. @@ -2574,9 +2570,11 @@ impl Vec { self.len += count; } - /// Removes the specified range from the vector in bulk, returning all - /// removed elements as an iterator. If the iterator is dropped before - /// being fully consumed, it drops the remaining removed elements. + /// Removes the subslice indicated by the given range from the vector, + /// returning a double-ended iterator over the removed subslice. + /// + /// If the iterator is dropped before being fully consumed, + /// it drops the remaining removed elements. /// /// The returned iterator keeps a mutable borrow on the vector to optimize /// its implementation. @@ -3016,10 +3014,9 @@ impl Vec { /// Iterates over the slice `other`, clones each element, and then appends /// it to this `Vec`. The `other` slice is traversed in-order. /// - /// Note that this function is same as [`extend`] except that it is - /// specialized to work with slices instead. If and when Rust gets - /// specialization this function will likely be deprecated (but still - /// available). + /// Note that this function is the same as [`extend`], + /// except that it also works with slice elements that are Clone but not Copy. + /// If Rust gets specialization this function may be deprecated. /// /// # Examples /// @@ -3587,7 +3584,7 @@ impl Vec { /// with the given `replace_with` iterator and yields the removed items. /// `replace_with` does not need to be the same length as `range`. /// - /// `range` is removed even if the iterator is not consumed until the end. + /// `range` is removed even if the `Splice` iterator is not consumed before it is dropped. /// /// It is unspecified how many elements are removed from the vector /// if the `Splice` value is leaked. @@ -3613,8 +3610,18 @@ impl Vec { /// let mut v = vec![1, 2, 3, 4]; /// let new = [7, 8, 9]; /// let u: Vec<_> = v.splice(1..3, new).collect(); - /// assert_eq!(v, &[1, 7, 8, 9, 4]); - /// assert_eq!(u, &[2, 3]); + /// assert_eq!(v, [1, 7, 8, 9, 4]); + /// assert_eq!(u, [2, 3]); + /// ``` + /// + /// Using `splice` to insert new items into a vector efficiently at a specific position + /// indicated by an empty range: + /// + /// ``` + /// let mut v = vec![1, 5]; + /// let new = [2, 3, 4]; + /// v.splice(1..1, new); + /// assert_eq!(v, [1, 2, 3, 4, 5]); /// ``` #[cfg(not(no_global_oom_handling))] #[inline] diff --git a/library/alloc/tests/collections/binary_heap.rs b/library/alloc/tests/collections/binary_heap.rs index 55405ffe8c4f6..95f4c3e614f5e 100644 --- a/library/alloc/tests/collections/binary_heap.rs +++ b/library/alloc/tests/collections/binary_heap.rs @@ -502,9 +502,7 @@ fn test_retain_catch_unwind() { // even if the order might not be correct. // // Destructors must be called exactly once per element. -// FIXME: re-enable emscripten once it can unwind again #[test] -#[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn panic_safe() { use std::cmp; diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index 393bdfe48b741..1bcec4037cdd6 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -37,7 +37,7 @@ #![feature(local_waker)] #![feature(str_as_str)] #![feature(strict_provenance_lints)] -#![feature(vec_pop_if)] +#![feature(vec_deque_pop_if)] #![feature(unique_rc_arc)] #![feature(macro_metavar_expr_concat)] #![allow(internal_features)] @@ -93,9 +93,6 @@ fn test_rng() -> rand_xorshift::XorShiftRng { rand::SeedableRng::from_seed(seed) } -// FIXME: Instantiated functions with i128 in the signature is not supported in Emscripten. -// See https://github.com/kripken/emscripten-fastcomp/issues/169 -#[cfg(not(target_os = "emscripten"))] #[test] fn test_boxed_hasher() { let ordinary_hash = hash(&5u32); diff --git a/library/alloc/tests/slice.rs b/library/alloc/tests/slice.rs index 9625e3d2b5e08..f990a41b679fa 100644 --- a/library/alloc/tests/slice.rs +++ b/library/alloc/tests/slice.rs @@ -1414,7 +1414,6 @@ fn test_box_slice_clone() { #[test] #[allow(unused_must_use)] // here, we care about the side effects of `.clone()` -#[cfg_attr(target_os = "emscripten", ignore)] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_box_slice_clone_panics() { use std::sync::Arc; diff --git a/library/alloc/tests/sort/patterns.rs b/library/alloc/tests/sort/patterns.rs index e5d31d868b251..0f1ec664d3d4f 100644 --- a/library/alloc/tests/sort/patterns.rs +++ b/library/alloc/tests/sort/patterns.rs @@ -1,8 +1,8 @@ use std::env; -use std::hash::Hash; use std::str::FromStr; use std::sync::OnceLock; +use rand::distr::Uniform; use rand::prelude::*; use rand_xorshift::XorShiftRng; @@ -23,14 +23,14 @@ pub fn random(len: usize) -> Vec { pub fn random_uniform(len: usize, range: R) -> Vec where - R: Into> + Hash, + Uniform: TryFrom, { // :.:.:.:: let mut rng: XorShiftRng = rand::SeedableRng::seed_from_u64(get_or_init_rand_seed()); // Abstracting over ranges in Rust :( - let dist: rand::distributions::Uniform = range.into(); + let dist = Uniform::try_from(range).unwrap(); (0..len).map(|_| dist.sample(&mut rng)).collect() } @@ -207,5 +207,5 @@ fn rand_root_seed() -> u64 { fn random_vec(len: usize) -> Vec { let mut rng: XorShiftRng = rand::SeedableRng::seed_from_u64(get_or_init_rand_seed()); - (0..len).map(|_| rng.gen::()).collect() + (0..len).map(|_| rng.random::()).collect() } diff --git a/library/alloc/tests/sort/tests.rs b/library/alloc/tests/sort/tests.rs index 4cc79010e8fed..d321f8df51898 100644 --- a/library/alloc/tests/sort/tests.rs +++ b/library/alloc/tests/sort/tests.rs @@ -11,7 +11,14 @@ use crate::sort::{Sort, known_good_stable_sort, patterns}; #[cfg(miri)] const TEST_LENGTHS: &[usize] = &[2, 3, 4, 7, 10, 15, 20, 24, 33, 50, 100, 171, 300]; -#[cfg(not(miri))] +// node.js gives out of memory error to use with length 1_100_000 +#[cfg(all(not(miri), target_os = "emscripten"))] +const TEST_LENGTHS: &[usize] = &[ + 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 16, 17, 20, 24, 30, 32, 33, 35, 50, 100, 200, 500, 1_000, + 2_048, 5_000, 10_000, 100_000, +]; + +#[cfg(all(not(miri), not(target_os = "emscripten")))] const TEST_LENGTHS: &[usize] = &[ 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 16, 17, 20, 24, 30, 32, 33, 35, 50, 100, 200, 500, 1_000, 2_048, 5_000, 10_000, 100_000, 1_100_000, diff --git a/library/alloc/tests/sort/zipf.rs b/library/alloc/tests/sort/zipf.rs index cc774ee5c43bf..3dad2db521f4b 100644 --- a/library/alloc/tests/sort/zipf.rs +++ b/library/alloc/tests/sort/zipf.rs @@ -80,7 +80,7 @@ impl ZipfDistribution { loop { use std::cmp; - let u: f64 = hnum + rng.gen::() * (self.h_integral_x1 - hnum); + let u: f64 = hnum + rng.random::() * (self.h_integral_x1 - hnum); // u is uniformly distributed in (h_integral_x1, h_integral_num_elements] let x: f64 = ZipfDistribution::h_integral_inv(u, self.exponent); @@ -145,7 +145,7 @@ impl ZipfDistribution { } } -impl rand::distributions::Distribution for ZipfDistribution { +impl rand::distr::Distribution for ZipfDistribution { fn sample(&self, rng: &mut R) -> usize { self.next(rng) } diff --git a/library/alloc/tests/str.rs b/library/alloc/tests/str.rs index 6f930ab08535c..23a0e5e525643 100644 --- a/library/alloc/tests/str.rs +++ b/library/alloc/tests/str.rs @@ -1524,18 +1524,14 @@ fn test_lines() { t("bare\r", &["bare\r"]); t("bare\rcr", &["bare\rcr"]); t("Text\n\r", &["Text", "\r"]); - t("\nMäry häd ä little lämb\n\r\nLittle lämb\n", &[ - "", - "Märy häd ä little lämb", - "", - "Little lämb", - ]); - t("\r\nMäry häd ä little lämb\n\nLittle lämb", &[ - "", - "Märy häd ä little lämb", - "", - "Little lämb", - ]); + t( + "\nMäry häd ä little lämb\n\r\nLittle lämb\n", + &["", "Märy häd ä little lämb", "", "Little lämb"], + ); + t( + "\r\nMäry häd ä little lämb\n\nLittle lämb", + &["", "Märy häd ä little lämb", "", "Little lämb"], + ); } #[test] @@ -1978,73 +1974,88 @@ mod pattern { assert_eq!(v, right); } - make_test!(str_searcher_ascii_haystack, "bb", "abbcbbd", [ - Reject(0, 1), - Match(1, 3), - Reject(3, 4), - Match(4, 6), - Reject(6, 7), - ]); - make_test!(str_searcher_ascii_haystack_seq, "bb", "abbcbbbbd", [ - Reject(0, 1), - Match(1, 3), - Reject(3, 4), - Match(4, 6), - Match(6, 8), - Reject(8, 9), - ]); - make_test!(str_searcher_empty_needle_ascii_haystack, "", "abbcbbd", [ - Match(0, 0), - Reject(0, 1), - Match(1, 1), - Reject(1, 2), - Match(2, 2), - Reject(2, 3), - Match(3, 3), - Reject(3, 4), - Match(4, 4), - Reject(4, 5), - Match(5, 5), - Reject(5, 6), - Match(6, 6), - Reject(6, 7), - Match(7, 7), - ]); - make_test!(str_searcher_multibyte_haystack, " ", "├──", [ - Reject(0, 3), - Reject(3, 6), - Reject(6, 9), - ]); - make_test!(str_searcher_empty_needle_multibyte_haystack, "", "├──", [ - Match(0, 0), - Reject(0, 3), - Match(3, 3), - Reject(3, 6), - Match(6, 6), - Reject(6, 9), - Match(9, 9), - ]); + make_test!( + str_searcher_ascii_haystack, + "bb", + "abbcbbd", + [Reject(0, 1), Match(1, 3), Reject(3, 4), Match(4, 6), Reject(6, 7),] + ); + make_test!( + str_searcher_ascii_haystack_seq, + "bb", + "abbcbbbbd", + [Reject(0, 1), Match(1, 3), Reject(3, 4), Match(4, 6), Match(6, 8), Reject(8, 9),] + ); + make_test!( + str_searcher_empty_needle_ascii_haystack, + "", + "abbcbbd", + [ + Match(0, 0), + Reject(0, 1), + Match(1, 1), + Reject(1, 2), + Match(2, 2), + Reject(2, 3), + Match(3, 3), + Reject(3, 4), + Match(4, 4), + Reject(4, 5), + Match(5, 5), + Reject(5, 6), + Match(6, 6), + Reject(6, 7), + Match(7, 7), + ] + ); + make_test!( + str_searcher_multibyte_haystack, + " ", + "├──", + [Reject(0, 3), Reject(3, 6), Reject(6, 9),] + ); + make_test!( + str_searcher_empty_needle_multibyte_haystack, + "", + "├──", + [ + Match(0, 0), + Reject(0, 3), + Match(3, 3), + Reject(3, 6), + Match(6, 6), + Reject(6, 9), + Match(9, 9), + ] + ); make_test!(str_searcher_empty_needle_empty_haystack, "", "", [Match(0, 0),]); make_test!(str_searcher_nonempty_needle_empty_haystack, "├", "", []); - make_test!(char_searcher_ascii_haystack, 'b', "abbcbbd", [ - Reject(0, 1), - Match(1, 2), - Match(2, 3), - Reject(3, 4), - Match(4, 5), - Match(5, 6), - Reject(6, 7), - ]); - make_test!(char_searcher_multibyte_haystack, ' ', "├──", [ - Reject(0, 3), - Reject(3, 6), - Reject(6, 9), - ]); - make_test!(char_searcher_short_haystack, '\u{1F4A9}', "* \t", [ - Reject(0, 1), - Reject(1, 2), - Reject(2, 3), - ]); + make_test!( + char_searcher_ascii_haystack, + 'b', + "abbcbbd", + [ + Reject(0, 1), + Match(1, 2), + Match(2, 3), + Reject(3, 4), + Match(4, 5), + Match(5, 6), + Reject(6, 7), + ] + ); + make_test!( + char_searcher_multibyte_haystack, + ' ', + "├──", + [Reject(0, 3), Reject(3, 6), Reject(6, 9),] + ); + make_test!( + char_searcher_short_haystack, + '\u{1F4A9}', + "* \t", + [Reject(0, 1), Reject(1, 2), Reject(2, 3),] + ); // See #85462 #[test] @@ -2297,21 +2308,21 @@ fn utf8_chars() { assert_eq!(schs.len(), 4); assert_eq!(schs.iter().cloned().collect::(), s); - assert!((from_utf8(s.as_bytes()).is_ok())); + assert!(from_utf8(s.as_bytes()).is_ok()); // invalid prefix - assert!((!from_utf8(&[0x80]).is_ok())); + assert!(!from_utf8(&[0x80]).is_ok()); // invalid 2 byte prefix - assert!((!from_utf8(&[0xc0]).is_ok())); - assert!((!from_utf8(&[0xc0, 0x10]).is_ok())); + assert!(!from_utf8(&[0xc0]).is_ok()); + assert!(!from_utf8(&[0xc0, 0x10]).is_ok()); // invalid 3 byte prefix - assert!((!from_utf8(&[0xe0]).is_ok())); - assert!((!from_utf8(&[0xe0, 0x10]).is_ok())); - assert!((!from_utf8(&[0xe0, 0xff, 0x10]).is_ok())); + assert!(!from_utf8(&[0xe0]).is_ok()); + assert!(!from_utf8(&[0xe0, 0x10]).is_ok()); + assert!(!from_utf8(&[0xe0, 0xff, 0x10]).is_ok()); // invalid 4 byte prefix - assert!((!from_utf8(&[0xf0]).is_ok())); - assert!((!from_utf8(&[0xf0, 0x10]).is_ok())); - assert!((!from_utf8(&[0xf0, 0xff, 0x10]).is_ok())); - assert!((!from_utf8(&[0xf0, 0xff, 0xff, 0x10]).is_ok())); + assert!(!from_utf8(&[0xf0]).is_ok()); + assert!(!from_utf8(&[0xf0, 0x10]).is_ok()); + assert!(!from_utf8(&[0xf0, 0xff, 0x10]).is_ok()); + assert!(!from_utf8(&[0xf0, 0xff, 0xff, 0x10]).is_ok()); } #[test] diff --git a/library/alloc/tests/string.rs b/library/alloc/tests/string.rs index 1c8bff1564db2..d996c55f94660 100644 --- a/library/alloc/tests/string.rs +++ b/library/alloc/tests/string.rs @@ -154,19 +154,28 @@ fn test_fromutf8error_into_lossy() { #[test] fn test_from_utf16() { let pairs = [ - (String::from("𐍅𐌿𐌻𐍆𐌹𐌻𐌰\n"), vec![ - 0xd800, 0xdf45, 0xd800, 0xdf3f, 0xd800, 0xdf3b, 0xd800, 0xdf46, 0xd800, 0xdf39, 0xd800, - 0xdf3b, 0xd800, 0xdf30, 0x000a, - ]), - (String::from("𐐒𐑉𐐮𐑀𐐲𐑋 𐐏𐐲𐑍\n"), vec![ - 0xd801, 0xdc12, 0xd801, 0xdc49, 0xd801, 0xdc2e, 0xd801, 0xdc40, 0xd801, 0xdc32, 0xd801, - 0xdc4b, 0x0020, 0xd801, 0xdc0f, 0xd801, 0xdc32, 0xd801, 0xdc4d, 0x000a, - ]), - (String::from("𐌀𐌖𐌋𐌄𐌑𐌉·𐌌𐌄𐌕𐌄𐌋𐌉𐌑\n"), vec![ - 0xd800, 0xdf00, 0xd800, 0xdf16, 0xd800, 0xdf0b, 0xd800, 0xdf04, 0xd800, 0xdf11, 0xd800, - 0xdf09, 0x00b7, 0xd800, 0xdf0c, 0xd800, 0xdf04, 0xd800, 0xdf15, 0xd800, 0xdf04, 0xd800, - 0xdf0b, 0xd800, 0xdf09, 0xd800, 0xdf11, 0x000a, - ]), + ( + String::from("𐍅𐌿𐌻𐍆𐌹𐌻𐌰\n"), + vec![ + 0xd800, 0xdf45, 0xd800, 0xdf3f, 0xd800, 0xdf3b, 0xd800, 0xdf46, 0xd800, 0xdf39, + 0xd800, 0xdf3b, 0xd800, 0xdf30, 0x000a, + ], + ), + ( + String::from("𐐒𐑉𐐮𐑀𐐲𐑋 𐐏𐐲𐑍\n"), + vec![ + 0xd801, 0xdc12, 0xd801, 0xdc49, 0xd801, 0xdc2e, 0xd801, 0xdc40, 0xd801, 0xdc32, + 0xd801, 0xdc4b, 0x0020, 0xd801, 0xdc0f, 0xd801, 0xdc32, 0xd801, 0xdc4d, 0x000a, + ], + ), + ( + String::from("𐌀𐌖𐌋𐌄𐌑𐌉·𐌌𐌄𐌕𐌄𐌋𐌉𐌑\n"), + vec![ + 0xd800, 0xdf00, 0xd800, 0xdf16, 0xd800, 0xdf0b, 0xd800, 0xdf04, 0xd800, 0xdf11, + 0xd800, 0xdf09, 0x00b7, 0xd800, 0xdf0c, 0xd800, 0xdf04, 0xd800, 0xdf15, 0xd800, + 0xdf04, 0xd800, 0xdf0b, 0xd800, 0xdf09, 0xd800, 0xdf11, 0x000a, + ], + ), ( String::from("𐒋𐒘𐒈𐒑𐒛𐒒 𐒕𐒓 𐒈𐒚𐒍 𐒏𐒜𐒒𐒖𐒆 𐒕𐒆\n"), vec![ diff --git a/library/alloc/tests/sync.rs b/library/alloc/tests/sync.rs index 7a9a4abfdc672..6d3ab1b1d11e1 100644 --- a/library/alloc/tests/sync.rs +++ b/library/alloc/tests/sync.rs @@ -128,6 +128,7 @@ fn try_unwrap() { } #[test] +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads fn into_inner() { for _ in 0..100 // ^ Increase chances of hitting potential race conditions diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index 84679827ba1c0..fe1db56414e0c 100644 --- a/library/alloc/tests/vec.rs +++ b/library/alloc/tests/vec.rs @@ -1204,22 +1204,16 @@ fn test_from_iter_specialization_with_iterator_adapters() { #[test] fn test_in_place_specialization_step_up_down() { fn assert_in_place_trait(_: &T) {} - let src = vec![[0u8; 4]; 256]; - let srcptr = src.as_ptr(); - let src_cap = src.capacity(); - let iter = src.into_iter().flatten(); - assert_in_place_trait(&iter); - let sink = iter.collect::>(); - let sinkptr = sink.as_ptr(); - assert_eq!(srcptr as *const u8, sinkptr); - assert_eq!(src_cap * 4, sink.capacity()); - let iter = sink.into_iter().array_chunks::<4>(); + let src = vec![0u8; 1024]; + let srcptr = src.as_ptr(); + let src_bytes = src.capacity(); + let iter = src.into_iter().array_chunks::<4>(); assert_in_place_trait(&iter); let sink = iter.collect::>(); let sinkptr = sink.as_ptr(); - assert_eq!(srcptr, sinkptr); - assert_eq!(src_cap, sink.capacity()); + assert_eq!(srcptr.addr(), sinkptr.addr()); + assert_eq!(src_bytes, sink.capacity() * 4); let mut src: Vec = Vec::with_capacity(17); let src_bytes = src.capacity(); @@ -1236,13 +1230,6 @@ fn test_in_place_specialization_step_up_down() { let sink: Vec<[u8; 2]> = iter.collect(); assert_eq!(sink.len(), 8); assert!(sink.capacity() <= 25); - - let src = vec![[0u8; 4]; 256]; - let srcptr = src.as_ptr(); - let iter = src.into_iter().flat_map(|a| a.into_iter().map(|b| b.wrapping_add(1))); - assert_in_place_trait(&iter); - let sink = iter.collect::>(); - assert_eq!(srcptr as *const u8, sink.as_ptr()); } #[test] @@ -1350,6 +1337,20 @@ fn test_collect_after_iterator_clone() { assert_eq!(v, [1, 1, 1, 1, 1]); assert!(v.len() <= v.capacity()); } + +// regression test for #135103, similar to the one above Flatten/FlatMap had an unsound InPlaceIterable +// implementation. +#[test] +fn test_flatten_clone() { + const S: String = String::new(); + + let v = vec![[S, "Hello World!".into()], [S, S]]; + let mut i = v.into_iter().flatten(); + let _ = i.next(); + let result: Vec = i.clone().collect(); + assert_eq!(result, ["Hello World!", "", ""]); +} + #[test] fn test_cow_from() { let borrowed: &[_] = &["borrowed", "(slice)"]; @@ -1586,9 +1587,7 @@ fn extract_if_complex() { } } -// FIXME: re-enable emscripten once it can unwind again #[test] -#[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn extract_if_consumed_panic() { use std::rc::Rc; @@ -1639,9 +1638,7 @@ fn extract_if_consumed_panic() { } } -// FIXME: Re-enable emscripten once it can catch panics #[test] -#[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn extract_if_unconsumed_panic() { use std::rc::Rc; @@ -2741,3 +2738,13 @@ fn max_swap_remove() { let mut v = vec![0]; v.swap_remove(usize::MAX); } + +// Regression test for #135338 +#[test] +fn vec_null_ptr_roundtrip() { + let ptr = std::ptr::from_ref(&42); + let zero = ptr.with_addr(0); + let roundtripped = vec![zero; 1].pop().unwrap(); + let new = roundtripped.with_addr(ptr.addr()); + unsafe { new.read() }; +} diff --git a/library/alloc/tests/vec_deque.rs b/library/alloc/tests/vec_deque.rs index 4b8d3c735f72b..1b03c29e5bda1 100644 --- a/library/alloc/tests/vec_deque.rs +++ b/library/alloc/tests/vec_deque.rs @@ -80,6 +80,45 @@ fn test_parameterized(a: T, b: T, c: T, d: T) { assert_eq!(deq[3].clone(), d.clone()); } +#[test] +fn test_pop_if() { + let mut deq: VecDeque<_> = vec![0, 1, 2, 3, 4].into(); + let pred = |x: &mut i32| *x % 2 == 0; + + assert_eq!(deq.pop_front_if(pred), Some(0)); + assert_eq!(deq, [1, 2, 3, 4]); + + assert_eq!(deq.pop_front_if(pred), None); + assert_eq!(deq, [1, 2, 3, 4]); + + assert_eq!(deq.pop_back_if(pred), Some(4)); + assert_eq!(deq, [1, 2, 3]); + + assert_eq!(deq.pop_back_if(pred), None); + assert_eq!(deq, [1, 2, 3]); +} + +#[test] +fn test_pop_if_empty() { + let mut deq = VecDeque::::new(); + assert_eq!(deq.pop_front_if(|_| true), None); + assert_eq!(deq.pop_back_if(|_| true), None); + assert!(deq.is_empty()); +} + +#[test] +fn test_pop_if_mutates() { + let mut v: VecDeque<_> = vec![-1, 1].into(); + let pred = |x: &mut i32| { + *x *= 2; + false + }; + assert_eq!(v.pop_front_if(pred), None); + assert_eq!(v, [-2, 1]); + assert_eq!(v.pop_back_if(pred), None); + assert_eq!(v, [-2, 2]); +} + #[test] fn test_push_front_grow() { let mut deq = VecDeque::new(); diff --git a/library/backtrace b/library/backtrace index 4d7906bb24ae9..9d2c34e7e63af 160000 --- a/library/backtrace +++ b/library/backtrace @@ -1 +1 @@ -Subproject commit 4d7906bb24ae91ee6587127020d360f5298f9e7e +Subproject commit 9d2c34e7e63afe1e71c333b247065e3b7ba4d883 diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index 46c55c437cce5..b7c6db6c78dde 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -15,19 +15,6 @@ edition = "2021" test = false bench = false -[[test]] -name = "coretests" -path = "tests/lib.rs" - -[[bench]] -name = "corebenches" -path = "benches/lib.rs" -test = true - -[dev-dependencies] -rand = { version = "0.8.5", default-features = false } -rand_xorshift = { version = "0.3.0", default-features = false } - [features] # Make panics and failed asserts immediately abort without formatting any message panic_immediate_abort = [] diff --git a/library/core/benches/num/int_log/mod.rs b/library/core/benches/num/int_log/mod.rs deleted file mode 100644 index e5874ddf03b5b..0000000000000 --- a/library/core/benches/num/int_log/mod.rs +++ /dev/null @@ -1,125 +0,0 @@ -use rand::Rng; -use test::{Bencher, black_box}; - -macro_rules! int_log10_bench { - ($t:ty, $predictable:ident, $random:ident, $random_small:ident) => { - #[bench] - fn $predictable(bench: &mut Bencher) { - bench.iter(|| { - for n in 0..(<$t>::BITS / 8) { - for i in 1..=(100 as $t) { - let x = black_box(i << (n * 8)); - black_box(x.ilog10()); - } - } - }); - } - - #[bench] - fn $random(bench: &mut Bencher) { - let mut rng = crate::bench_rng(); - /* Exponentially distributed random numbers from the whole range of the type. */ - let numbers: Vec<$t> = (0..256) - .map(|_| { - let x = rng.gen::<$t>() >> rng.gen_range(0..<$t>::BITS); - if x != 0 { x } else { 1 } - }) - .collect(); - bench.iter(|| { - for x in &numbers { - black_box(black_box(x).ilog10()); - } - }); - } - - #[bench] - fn $random_small(bench: &mut Bencher) { - let mut rng = crate::bench_rng(); - /* Exponentially distributed random numbers from the range 0..256. */ - let numbers: Vec<$t> = (0..256) - .map(|_| { - let x = (rng.gen::() >> rng.gen_range(0..u8::BITS)) as $t; - if x != 0 { x } else { 1 } - }) - .collect(); - bench.iter(|| { - for x in &numbers { - black_box(black_box(x).ilog10()); - } - }); - } - }; -} - -int_log10_bench! {u8, u8_log10_predictable, u8_log10_random, u8_log10_random_small} -int_log10_bench! {u16, u16_log10_predictable, u16_log10_random, u16_log10_random_small} -int_log10_bench! {u32, u32_log10_predictable, u32_log10_random, u32_log10_random_small} -int_log10_bench! {u64, u64_log10_predictable, u64_log10_random, u64_log10_random_small} -int_log10_bench! {u128, u128_log10_predictable, u128_log10_random, u128_log10_random_small} - -macro_rules! int_log_bench { - ($t:ty, $random:ident, $random_small:ident, $geometric:ident) => { - #[bench] - fn $random(bench: &mut Bencher) { - let mut rng = crate::bench_rng(); - /* Exponentially distributed random numbers from the whole range of the type. */ - let numbers: Vec<$t> = (0..256) - .map(|_| { - let x = rng.gen::<$t>() >> rng.gen_range(0..<$t>::BITS); - if x >= 2 { x } else { 2 } - }) - .collect(); - bench.iter(|| { - for &b in &numbers { - for &x in &numbers { - black_box(black_box(x).ilog(b)); - } - } - }); - } - - #[bench] - fn $random_small(bench: &mut Bencher) { - let mut rng = crate::bench_rng(); - /* Exponentially distributed random numbers from the range 0..256. */ - let numbers: Vec<$t> = (0..256) - .map(|_| { - let x = (rng.gen::() >> rng.gen_range(0..u8::BITS)) as $t; - if x >= 2 { x } else { 2 } - }) - .collect(); - bench.iter(|| { - for &b in &numbers { - for &x in &numbers { - black_box(black_box(x).ilog(b)); - } - } - }); - } - - #[bench] - fn $geometric(bench: &mut Bencher) { - let bases: [$t; 16] = [2, 3, 4, 5, 7, 8, 9, 15, 16, 17, 31, 32, 33, 63, 64, 65]; - let base_and_numbers: Vec<($t, Vec<$t>)> = bases - .iter() - .map(|&b| { - let numbers = (0..=<$t>::MAX.ilog(b)).map(|exp| b.pow(exp)).collect(); - (b, numbers) - }) - .collect(); - bench.iter(|| { - for (b, numbers) in &base_and_numbers { - for &x in numbers { - black_box(black_box(x).ilog(black_box(*b))); - } - } - }); - } - }; -} - -int_log_bench! {u8, u8_log_random, u8_log_random_small, u8_log_geometric} -int_log_bench! {u16, u16_log_random, u16_log_random_small, u16_log_geometric} -int_log_bench! {u32, u32_log_random, u32_log_random_small, u32_log_geometric} -int_log_bench! {u64, u64_log_random, u64_log_random_small, u64_log_geometric} -int_log_bench! {u128, u128_log_random, u128_log_random_small, u128_log_geometric} diff --git a/library/core/benches/num/int_pow/mod.rs b/library/core/benches/num/int_pow/mod.rs deleted file mode 100644 index 46f47028d56e6..0000000000000 --- a/library/core/benches/num/int_pow/mod.rs +++ /dev/null @@ -1,99 +0,0 @@ -use rand::Rng; -use test::{Bencher, black_box}; - -const ITERATIONS: usize = 128; // Uses an ITERATIONS * 20 Byte stack allocation -type IntType = i128; // Hardest native type to multiply -const EXPONENT_MAX: u32 = 31; -const MAX_BASE: IntType = 17; // +-17 ** 31 <= IntType::MAX - -macro_rules! pow_bench_template { - ($name:ident, $inner_macro:ident, $base_macro:ident) => { - #[bench] - fn $name(bench: &mut Bencher) { - // Frequent black_box calls can add latency and prevent optimizations, so for - // variable parameters we premake an array and pass the - // reference through black_box outside of the loop. - let mut rng = crate::bench_rng(); - let base_array: [IntType; ITERATIONS] = - core::array::from_fn(|_| rng.gen_range((-MAX_BASE..=MAX_BASE))); - let exp_array: [u32; ITERATIONS] = - core::array::from_fn(|_| rng.gen_range((0..=EXPONENT_MAX))); - - bench.iter(|| { - #[allow(unused, unused_mut)] - let mut base_iter = black_box(&base_array).into_iter(); - let mut exp_iter = black_box(&exp_array).into_iter(); - - (0..ITERATIONS).fold((0 as IntType, false), |acc, _| { - // Sometimes constants don't propagate all the way to the - // inside of the loop, so we call a custom expression every cycle - // rather than iter::repeat(CONST) - let base: IntType = $base_macro!(base_iter); - let exp: u32 = *exp_iter.next().unwrap(); - - let r: (IntType, bool) = $inner_macro!(base, exp); - (acc.0 ^ r.0, acc.1 ^ r.1) - }) - }); - } - }; -} - -// This may panic if it overflows. -macro_rules! inner_pow { - ($base:ident, $exp:ident) => { - ($base.pow($exp), false) - }; -} - -macro_rules! inner_wrapping { - ($base:ident, $exp:ident) => { - ($base.wrapping_pow($exp), false) - }; -} - -macro_rules! inner_overflowing { - ($base:ident, $exp:ident) => { - $base.overflowing_pow($exp) - }; -} - -// This will panic if it overflows. -macro_rules! inner_checked_unwrapped { - ($base:ident, $exp:ident) => { - ($base.checked_pow($exp).unwrap(), false) - }; -} - -macro_rules! inner_saturating { - ($base:ident, $exp:ident) => { - ($base.saturating_pow($exp), false) - }; -} - -macro_rules! make_const_base { - ($name:ident, $x:literal) => { - macro_rules! $name { - ($iter:ident) => { - $x - }; - } - }; -} - -make_const_base!(const_base_m7, -7); -make_const_base!(const_base_m8, -8); - -macro_rules! variable_base { - ($iter:ident) => { - *$iter.next().unwrap() - }; -} - -pow_bench_template!(pow_variable, inner_pow, variable_base); -pow_bench_template!(wrapping_pow_variable, inner_wrapping, variable_base); -pow_bench_template!(overflowing_pow_variable, inner_overflowing, variable_base); -pow_bench_template!(checked_pow_variable, inner_checked_unwrapped, variable_base); -pow_bench_template!(saturating_pow_variable, inner_saturating, variable_base); -pow_bench_template!(pow_m7, inner_pow, const_base_m7); -pow_bench_template!(pow_m8, inner_pow, const_base_m8); diff --git a/library/core/benches/num/int_sqrt/mod.rs b/library/core/benches/num/int_sqrt/mod.rs deleted file mode 100644 index e47b92e866eff..0000000000000 --- a/library/core/benches/num/int_sqrt/mod.rs +++ /dev/null @@ -1,62 +0,0 @@ -use rand::Rng; -use test::{Bencher, black_box}; - -macro_rules! int_sqrt_bench { - ($t:ty, $predictable:ident, $random:ident, $random_small:ident, $random_uniform:ident) => { - #[bench] - fn $predictable(bench: &mut Bencher) { - bench.iter(|| { - for n in 0..(<$t>::BITS / 8) { - for i in 1..=(100 as $t) { - let x = black_box(i << (n * 8)); - black_box(x.isqrt()); - } - } - }); - } - - #[bench] - fn $random(bench: &mut Bencher) { - let mut rng = crate::bench_rng(); - /* Exponentially distributed random numbers from the whole range of the type. */ - let numbers: Vec<$t> = - (0..256).map(|_| rng.gen::<$t>() >> rng.gen_range(0..<$t>::BITS)).collect(); - bench.iter(|| { - for x in &numbers { - black_box(black_box(x).isqrt()); - } - }); - } - - #[bench] - fn $random_small(bench: &mut Bencher) { - let mut rng = crate::bench_rng(); - /* Exponentially distributed random numbers from the range 0..256. */ - let numbers: Vec<$t> = - (0..256).map(|_| (rng.gen::() >> rng.gen_range(0..u8::BITS)) as $t).collect(); - bench.iter(|| { - for x in &numbers { - black_box(black_box(x).isqrt()); - } - }); - } - - #[bench] - fn $random_uniform(bench: &mut Bencher) { - let mut rng = crate::bench_rng(); - /* Exponentially distributed random numbers from the whole range of the type. */ - let numbers: Vec<$t> = (0..256).map(|_| rng.gen::<$t>()).collect(); - bench.iter(|| { - for x in &numbers { - black_box(black_box(x).isqrt()); - } - }); - } - }; -} - -int_sqrt_bench! {u8, u8_sqrt_predictable, u8_sqrt_random, u8_sqrt_random_small, u8_sqrt_uniform} -int_sqrt_bench! {u16, u16_sqrt_predictable, u16_sqrt_random, u16_sqrt_random_small, u16_sqrt_uniform} -int_sqrt_bench! {u32, u32_sqrt_predictable, u32_sqrt_random, u32_sqrt_random_small, u32_sqrt_uniform} -int_sqrt_bench! {u64, u64_sqrt_predictable, u64_sqrt_random, u64_sqrt_random_small, u64_sqrt_uniform} -int_sqrt_bench! {u128, u128_sqrt_predictable, u128_sqrt_random, u128_sqrt_random_small, u128_sqrt_uniform} diff --git a/library/core/benches/slice.rs b/library/core/benches/slice.rs deleted file mode 100644 index 29a66b6219976..0000000000000 --- a/library/core/benches/slice.rs +++ /dev/null @@ -1,173 +0,0 @@ -use core::ptr::NonNull; - -use test::{Bencher, black_box}; - -enum Cache { - L1, - L2, - L3, -} - -impl Cache { - fn size(&self) -> usize { - match self { - Cache::L1 => 1000, // 8kb - Cache::L2 => 10_000, // 80kb - Cache::L3 => 1_000_000, // 8Mb - } - } -} - -fn binary_search(b: &mut Bencher, cache: Cache, mapper: F) -where - F: Fn(usize) -> usize, -{ - let size = cache.size(); - let v = (0..size).map(&mapper).collect::>(); - let mut r = 0usize; - b.iter(move || { - // LCG constants from https://en.wikipedia.org/wiki/Numerical_Recipes. - r = r.wrapping_mul(1664525).wrapping_add(1013904223); - // Lookup the whole range to get 50% hits and 50% misses. - let i = mapper(r % size); - black_box(v.binary_search(&i).is_ok()); - }); -} - -fn binary_search_worst_case(b: &mut Bencher, cache: Cache) { - let size = cache.size(); - - let mut v = vec![0; size]; - let i = 1; - v[size - 1] = i; - b.iter(move || { - black_box(v.binary_search(&i).is_ok()); - }); -} - -#[bench] -fn binary_search_l1(b: &mut Bencher) { - binary_search(b, Cache::L1, |i| i * 2); -} - -#[bench] -fn binary_search_l2(b: &mut Bencher) { - binary_search(b, Cache::L2, |i| i * 2); -} - -#[bench] -fn binary_search_l3(b: &mut Bencher) { - binary_search(b, Cache::L3, |i| i * 2); -} - -#[bench] -fn binary_search_l1_with_dups(b: &mut Bencher) { - binary_search(b, Cache::L1, |i| i / 16 * 16); -} - -#[bench] -fn binary_search_l2_with_dups(b: &mut Bencher) { - binary_search(b, Cache::L2, |i| i / 16 * 16); -} - -#[bench] -fn binary_search_l3_with_dups(b: &mut Bencher) { - binary_search(b, Cache::L3, |i| i / 16 * 16); -} - -#[bench] -fn binary_search_l1_worst_case(b: &mut Bencher) { - binary_search_worst_case(b, Cache::L1); -} - -#[bench] -fn binary_search_l2_worst_case(b: &mut Bencher) { - binary_search_worst_case(b, Cache::L2); -} - -#[bench] -fn binary_search_l3_worst_case(b: &mut Bencher) { - binary_search_worst_case(b, Cache::L3); -} - -#[derive(Clone)] -struct Rgb(#[allow(dead_code)] u8, #[allow(dead_code)] u8, #[allow(dead_code)] u8); - -impl Rgb { - fn gen(i: usize) -> Self { - Rgb(i as u8, (i as u8).wrapping_add(7), (i as u8).wrapping_add(42)) - } -} - -macro_rules! rotate { - ($fn:ident, $n:expr, $mapper:expr) => { - #[bench] - fn $fn(b: &mut Bencher) { - let mut x = (0usize..$n).map(&$mapper).collect::>(); - b.iter(|| { - for s in 0..x.len() { - x[..].rotate_right(s); - } - black_box(x[0].clone()) - }) - } - }; -} - -rotate!(rotate_u8, 32, |i| i as u8); -rotate!(rotate_rgb, 32, Rgb::gen); -rotate!(rotate_usize, 32, |i| i); -rotate!(rotate_16_usize_4, 16, |i| [i; 4]); -rotate!(rotate_16_usize_5, 16, |i| [i; 5]); -rotate!(rotate_64_usize_4, 64, |i| [i; 4]); -rotate!(rotate_64_usize_5, 64, |i| [i; 5]); - -macro_rules! swap_with_slice { - ($fn:ident, $n:expr, $mapper:expr) => { - #[bench] - fn $fn(b: &mut Bencher) { - let mut x = (0usize..$n).map(&$mapper).collect::>(); - let mut y = ($n..($n * 2)).map(&$mapper).collect::>(); - let mut skip = 0; - b.iter(|| { - for _ in 0..32 { - x[skip..].swap_with_slice(&mut y[..($n - skip)]); - skip = black_box(skip + 1) % 8; - } - black_box((x[$n / 3].clone(), y[$n * 2 / 3].clone())) - }) - } - }; -} - -swap_with_slice!(swap_with_slice_u8_30, 30, |i| i as u8); -swap_with_slice!(swap_with_slice_u8_3000, 3000, |i| i as u8); -swap_with_slice!(swap_with_slice_rgb_30, 30, Rgb::gen); -swap_with_slice!(swap_with_slice_rgb_3000, 3000, Rgb::gen); -swap_with_slice!(swap_with_slice_usize_30, 30, |i| i); -swap_with_slice!(swap_with_slice_usize_3000, 3000, |i| i); -swap_with_slice!(swap_with_slice_4x_usize_30, 30, |i| [i; 4]); -swap_with_slice!(swap_with_slice_4x_usize_3000, 3000, |i| [i; 4]); -swap_with_slice!(swap_with_slice_5x_usize_30, 30, |i| [i; 5]); -swap_with_slice!(swap_with_slice_5x_usize_3000, 3000, |i| [i; 5]); - -#[bench] -fn fill_byte_sized(b: &mut Bencher) { - #[derive(Copy, Clone)] - struct NewType(#[allow(dead_code)] u8); - - let mut ary = [NewType(0); 1024]; - - b.iter(|| { - let slice = &mut ary[..]; - black_box(slice.fill(black_box(NewType(42)))); - }); -} - -// Tests the ability of the compiler to recognize that only the last slice item is needed -// based on issue #106288 -#[bench] -fn fold_to_last(b: &mut Bencher) { - let slice: &[i32] = &[0; 1024]; - b.iter(|| black_box(slice).iter().fold(None, |_, r| Some(NonNull::from(r)))); -} diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs index e6f39db9dced3..17f4d68867e1e 100644 --- a/library/core/src/alloc/layout.rs +++ b/library/core/src/alloc/layout.rs @@ -178,7 +178,7 @@ impl Layout { /// allocate backing structure for `T` (which could be a trait /// or other unsized type like a slice). #[stable(feature = "alloc_layout", since = "1.28.0")] - #[rustc_const_stable(feature = "const_alloc_layout", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")] #[must_use] #[inline] pub const fn for_value(t: &T) -> Self { @@ -233,8 +233,7 @@ impl Layout { #[must_use] #[inline] pub const fn dangling(&self) -> NonNull { - // SAFETY: align is guaranteed to be non-zero - unsafe { NonNull::new_unchecked(crate::ptr::without_provenance_mut::(self.align())) } + NonNull::without_provenance(self.align.as_nonzero()) } /// Creates a layout describing the record that can hold a value @@ -252,7 +251,7 @@ impl Layout { /// Returns an error if the combination of `self.size()` and the given /// `align` violates the conditions listed in [`Layout::from_size_align`]. #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] - #[rustc_const_stable(feature = "const_alloc_layout", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")] #[inline] pub const fn align_to(&self, align: usize) -> Result { if let Some(align) = Alignment::new(align) { @@ -327,7 +326,7 @@ impl Layout { /// This is equivalent to adding the result of `padding_needed_for` /// to the layout's current size. #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] - #[rustc_const_stable(feature = "const_alloc_layout", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")] #[must_use = "this returns a new `Layout`, \ without modifying the original"] #[inline] @@ -426,7 +425,7 @@ impl Layout { /// # assert_eq!(repr_c(&[u64, u32, u16, u32]), Ok((s, vec![0, 8, 12, 16]))); /// ``` #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] - #[rustc_const_stable(feature = "const_alloc_layout", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")] #[inline] pub const fn extend(&self, next: Self) -> Result<(Self, usize), LayoutError> { let new_align = Alignment::max(self.align, next.align); @@ -489,7 +488,7 @@ impl Layout { /// On arithmetic overflow or when the total size would exceed /// `isize::MAX`, returns `LayoutError`. #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] - #[rustc_const_stable(feature = "const_alloc_layout", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_alloc_layout", since = "1.85.0")] #[inline] pub const fn array(n: usize) -> Result { // Reduce the amount of code we need to monomorphize per `T`. diff --git a/library/core/src/alloc/mod.rs b/library/core/src/alloc/mod.rs index aa841db045ce7..9805cee1c331e 100644 --- a/library/core/src/alloc/mod.rs +++ b/library/core/src/alloc/mod.rs @@ -49,26 +49,26 @@ impl fmt::Display for AllocError { /// An implementation of `Allocator` can allocate, grow, shrink, and deallocate arbitrary blocks of /// data described via [`Layout`][]. /// -/// `Allocator` is designed to be implemented on ZSTs, references, or smart pointers because having -/// an allocator like `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the +/// `Allocator` is designed to be implemented on ZSTs, references, or smart pointers. +/// An allocator for `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the /// allocated memory. /// -/// Unlike [`GlobalAlloc`][], zero-sized allocations are allowed in `Allocator`. If an underlying -/// allocator does not support this (like jemalloc) or return a null pointer (such as -/// `libc::malloc`), this must be caught by the implementation. +/// In contrast to [`GlobalAlloc`][], `Allocator` allows zero-sized allocations. If an underlying +/// allocator does not support this (like jemalloc) or responds by returning a null pointer +/// (such as `libc::malloc`), this must be caught by the implementation. /// /// ### Currently allocated memory /// -/// Some of the methods require that a memory block be *currently allocated* via an allocator. This -/// means that: +/// Some of the methods require that a memory block is *currently allocated* by an allocator. +/// This means that: +/// * the starting address for that memory block was previously +/// returned by [`allocate`], [`grow`], or [`shrink`], and +/// * the memory block has not subsequently been deallocated. /// -/// * the starting address for that memory block was previously returned by [`allocate`], [`grow`], or -/// [`shrink`], and -/// -/// * the memory block has not been subsequently deallocated, where blocks are either deallocated -/// directly by being passed to [`deallocate`] or were changed by being passed to [`grow`] or -/// [`shrink`] that returns `Ok`. If `grow` or `shrink` have returned `Err`, the passed pointer -/// remains valid. +/// A memory block is deallocated by a call to [`deallocate`], +/// or by a call to [`grow`] or [`shrink`] that returns `Ok`. +/// A call to `grow` or `shrink` that returns `Err`, +/// does not deallocate the memory block passed to it. /// /// [`allocate`]: Allocator::allocate /// [`grow`]: Allocator::grow @@ -77,32 +77,28 @@ impl fmt::Display for AllocError { /// /// ### Memory fitting /// -/// Some of the methods require that a layout *fit* a memory block. What it means for a layout to -/// "fit" a memory block means (or equivalently, for a memory block to "fit" a layout) is that the +/// Some of the methods require that a `layout` *fit* a memory block or vice versa. This means that the /// following conditions must hold: -/// -/// * The block must be allocated with the same alignment as [`layout.align()`], and -/// -/// * The provided [`layout.size()`] must fall in the range `min ..= max`, where: -/// - `min` is the size of the layout most recently used to allocate the block, and -/// - `max` is the latest actual size returned from [`allocate`], [`grow`], or [`shrink`]. +/// * the memory block must be *currently allocated* with alignment of [`layout.align()`], and +/// * [`layout.size()`] must fall in the range `min ..= max`, where: +/// - `min` is the size of the layout used to allocate the block, and +/// - `max` is the actual size returned from [`allocate`], [`grow`], or [`shrink`]. /// /// [`layout.align()`]: Layout::align /// [`layout.size()`]: Layout::size /// /// # Safety /// -/// * Memory blocks returned from an allocator that are [*currently allocated*] must point to -/// valid memory and retain their validity while they are [*currently allocated*] and the shorter -/// of: -/// - the borrow-checker lifetime of the allocator type itself. -/// - as long as at least one of the instance and all of its clones has not been dropped. +/// Memory blocks that are [*currently allocated*] by an allocator, +/// must point to valid memory, and retain their validity while until either: +/// - the memory block is deallocated, or +/// - the allocator is dropped. /// -/// * copying, cloning, or moving the allocator must not invalidate memory blocks returned from this -/// allocator. A copied or cloned allocator must behave like the same allocator, and +/// Copying, cloning, or moving the allocator must not invalidate memory blocks returned from it. +/// A copied or cloned allocator must behave like the original allocator. /// -/// * any pointer to a memory block which is [*currently allocated*] may be passed to any other -/// method of the allocator. +/// A memory block which is [*currently allocated*] may be passed to +/// any method of the allocator that accepts such an argument. /// /// [*currently allocated*]: #currently-allocated-memory #[unstable(feature = "allocator_api", issue = "32838")] diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 17d9455592787..f90de1f5ced40 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -610,6 +610,101 @@ impl dyn Any + Send + Sync { /// While `TypeId` implements `Hash`, `PartialOrd`, and `Ord`, it is worth /// noting that the hashes and ordering will vary between Rust releases. Beware /// of relying on them inside of your code! +/// +/// # Danger of Improper Variance +/// +/// You might think that subtyping is impossible between two static types, +/// but this is false; there exists a static type with a static subtype. +/// To wit, `fn(&str)`, which is short for `for<'any> fn(&'any str)`, and +/// `fn(&'static str)`, are two distinct, static types, and yet, +/// `fn(&str)` is a subtype of `fn(&'static str)`, since any value of type +/// `fn(&str)` can be used where a value of type `fn(&'static str)` is needed. +/// +/// This means that abstractions around `TypeId`, despite its +/// `'static` bound on arguments, still need to worry about unnecessary +/// and improper variance: it is advisable to strive for invariance +/// first. The usability impact will be negligible, while the reduction +/// in the risk of unsoundness will be most welcome. +/// +/// ## Examples +/// +/// Suppose `SubType` is a subtype of `SuperType`, that is, +/// a value of type `SubType` can be used wherever +/// a value of type `SuperType` is expected. +/// Suppose also that `CoVar` is a generic type, which is covariant over `T` +/// (like many other types, including `PhantomData` and `Vec`). +/// +/// Then, by covariance, `CoVar` is a subtype of `CoVar`, +/// that is, a value of type `CoVar` can be used wherever +/// a value of type `CoVar` is expected. +/// +/// Then if `CoVar` relies on `TypeId::of::()` to uphold any invariants, +/// those invariants may be broken because a value of type `CoVar` can be created +/// without going through any of its methods, like so: +/// ``` +/// type SubType = fn(&()); +/// type SuperType = fn(&'static ()); +/// type CoVar = Vec; // imagine something more complicated +/// +/// let sub: CoVar = CoVar::new(); +/// // we have a `CoVar` instance without +/// // *ever* having called `CoVar::::new()`! +/// let fake_super: CoVar = sub; +/// ``` +/// +/// The following is an example program that tries to use `TypeId::of` to +/// implement a generic type `Unique` that guarantees unique instances for each `Unique`, +/// that is, and for each type `T` there can be at most one value of type `Unique` at any time. +/// +/// ``` +/// mod unique { +/// use std::any::TypeId; +/// use std::collections::BTreeSet; +/// use std::marker::PhantomData; +/// use std::sync::Mutex; +/// +/// static ID_SET: Mutex> = Mutex::new(BTreeSet::new()); +/// +/// // TypeId has only covariant uses, which makes Unique covariant over TypeAsId 🚨 +/// #[derive(Debug, PartialEq)] +/// pub struct Unique( +/// // private field prevents creation without `new` outside this module +/// PhantomData, +/// ); +/// +/// impl Unique { +/// pub fn new() -> Option { +/// let mut set = ID_SET.lock().unwrap(); +/// (set.insert(TypeId::of::())).then(|| Self(PhantomData)) +/// } +/// } +/// +/// impl Drop for Unique { +/// fn drop(&mut self) { +/// let mut set = ID_SET.lock().unwrap(); +/// (!set.remove(&TypeId::of::())).then(|| panic!("duplicity detected")); +/// } +/// } +/// } +/// +/// use unique::Unique; +/// +/// // `OtherRing` is a subtype of `TheOneRing`. Both are 'static, and thus have a TypeId. +/// type TheOneRing = fn(&'static ()); +/// type OtherRing = fn(&()); +/// +/// fn main() { +/// let the_one_ring: Unique = Unique::new().unwrap(); +/// assert_eq!(Unique::::new(), None); +/// +/// let other_ring: Unique = Unique::new().unwrap(); +/// // Use that `Unique` is a subtype of `Unique` 🚨 +/// let fake_one_ring: Unique = other_ring; +/// assert_eq!(fake_one_ring, the_one_ring); +/// +/// std::mem::forget(fake_one_ring); +/// } +/// ``` #[derive(Clone, Copy, Eq, PartialOrd, Ord)] #[stable(feature = "rust1", since = "1.0.0")] pub struct TypeId { @@ -627,8 +722,7 @@ impl PartialEq for TypeId { } impl TypeId { - /// Returns the `TypeId` of the type this generic function has been - /// instantiated with. + /// Returns the `TypeId` of the generic type parameter. /// /// # Examples /// diff --git a/library/core/src/arch.rs b/library/core/src/arch.rs index 95d88c7f67991..81d828a971c80 100644 --- a/library/core/src/arch.rs +++ b/library/core/src/arch.rs @@ -1,6 +1,14 @@ #![doc = include_str!("../../stdarch/crates/core_arch/src/core_arch_docs.md")] -#[allow(unused_imports)] +#[allow( + // some targets don't have anything to reexport, which + // makes the `pub use` unused and unreachable, allow + // both lints as to not have `#[cfg]`s + // + // cf. https://github.com/rust-lang/rust/pull/116033#issuecomment-1760085575 + unused_imports, + unreachable_pub +)] #[stable(feature = "simd_arch", since = "1.27.0")] pub use crate::core_arch::arch::*; @@ -65,7 +73,6 @@ pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))? // When stabilizing this, update the comment on `core::intrinsics::breakpoint`. #[unstable(feature = "breakpoint", issue = "133724")] #[inline(always)] -#[cfg(not(bootstrap))] pub fn breakpoint() { core::intrinsics::breakpoint(); } diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index 9ce0eb61e0814..1edade41597f7 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -214,7 +214,7 @@ impl IntoIter { // SAFETY: We know that all elements within `alive` are properly initialized. unsafe { let slice = self.data.get_unchecked(self.alive.clone()); - MaybeUninit::slice_assume_init_ref(slice) + slice.assume_init_ref() } } @@ -224,7 +224,7 @@ impl IntoIter { // SAFETY: We know that all elements within `alive` are properly initialized. unsafe { let slice = self.data.get_unchecked_mut(self.alive.clone()); - MaybeUninit::slice_assume_init_mut(slice) + slice.assume_init_mut() } } } @@ -285,7 +285,7 @@ impl Iterator for IntoIter { // SAFETY: These elements are currently initialized, so it's fine to drop them. unsafe { let slice = self.data.get_unchecked_mut(range_to_drop); - ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(slice)); + slice.assume_init_drop(); } NonZero::new(remaining).map_or(Ok(()), Err) @@ -340,7 +340,7 @@ impl DoubleEndedIterator for IntoIter { // SAFETY: These elements are currently initialized, so it's fine to drop them. unsafe { let slice = self.data.get_unchecked_mut(range_to_drop); - ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(slice)); + slice.assume_init_drop(); } NonZero::new(remaining).map_or(Ok(()), Err) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 95c1eb460cd94..28329bb090845 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -156,7 +156,6 @@ pub const fn from_mut(s: &mut T) -> &mut [T; 1] { /// The error type returned when a conversion from a slice to an array fails. #[stable(feature = "try_from", since = "1.34.0")] -#[rustc_allowed_through_unstable_modules] #[derive(Debug, Copy, Clone)] pub struct TryFromSliceError(()); @@ -893,7 +892,7 @@ impl Guard<'_, T> { /// /// No more than N elements must be initialized. #[inline] - pub unsafe fn push_unchecked(&mut self, item: T) { + pub(crate) unsafe fn push_unchecked(&mut self, item: T) { // SAFETY: If `initialized` was correct before and the caller does not // invoke this method more than N times then writes will be in-bounds // and slots will not be initialized more than once. @@ -911,9 +910,7 @@ impl Drop for Guard<'_, T> { // SAFETY: this slice will contain only initialized objects. unsafe { - crate::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut( - self.array_mut.get_unchecked_mut(..self.initialized), - )); + self.array_mut.get_unchecked_mut(..self.initialized).assume_init_drop(); } } } diff --git a/library/core/src/bool.rs b/library/core/src/bool.rs index 1590b9f29fcae..3c589ca5dfa7e 100644 --- a/library/core/src/bool.rs +++ b/library/core/src/bool.rs @@ -61,4 +61,52 @@ impl bool { pub fn then T>(self, f: F) -> Option { if self { Some(f()) } else { None } } + + /// Returns either `true_val` or `false_val` depending on the value of + /// `self`, with a hint to the compiler that `self` is unlikely + /// to be correctly predicted by a CPU’s branch predictor. + /// + /// This method is functionally equivalent to + /// ```ignore (this is just for illustrative purposes) + /// fn select_unpredictable(b: bool, true_val: T, false_val: T) -> T { + /// if b { true_val } else { false_val } + /// } + /// ``` + /// but might generate different assembly. In particular, on platforms with + /// a conditional move or select instruction (like `cmov` on x86 or `csel` + /// on ARM) the optimizer might use these instructions to avoid branches, + /// which can benefit performance if the branch predictor is struggling + /// with predicting `condition`, such as in an implementation of binary + /// search. + /// + /// Note however that this lowering is not guaranteed (on any platform) and + /// should not be relied upon when trying to write constant-time code. Also + /// be aware that this lowering might *decrease* performance if `condition` + /// is well-predictable. It is advisable to perform benchmarks to tell if + /// this function is useful. + /// + /// # Examples + /// + /// Distribute values evenly between two buckets: + /// ``` + /// #![feature(select_unpredictable)] + /// + /// use std::hash::BuildHasher; + /// + /// fn append(hasher: &H, v: i32, bucket_one: &mut Vec, bucket_two: &mut Vec) { + /// let hash = hasher.hash_one(&v); + /// let bucket = (hash % 2 == 0).select_unpredictable(bucket_one, bucket_two); + /// bucket.push(v); + /// } + /// # let hasher = std::collections::hash_map::RandomState::new(); + /// # let mut bucket_one = Vec::new(); + /// # let mut bucket_two = Vec::new(); + /// # append(&hasher, 42, &mut bucket_one, &mut bucket_two); + /// # assert_eq!(bucket_one.len() + bucket_two.len(), 1); + /// ``` + #[inline(always)] + #[unstable(feature = "select_unpredictable", issue = "133962")] + pub fn select_unpredictable(self, true_val: T, false_val: T) -> T { + crate::intrinsics::select_unpredictable(self, true_val, false_val) + } } diff --git a/library/core/src/bstr.rs b/library/core/src/bstr.rs new file mode 100644 index 0000000000000..74e07f3d242cd --- /dev/null +++ b/library/core/src/bstr.rs @@ -0,0 +1,581 @@ +//! The `ByteStr` type and trait implementations. + +use crate::borrow::{Borrow, BorrowMut}; +use crate::cmp::Ordering; +use crate::ops::{ + Deref, DerefMut, DerefPure, Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, + RangeTo, RangeToInclusive, +}; +use crate::{fmt, hash}; + +/// A wrapper for `&[u8]` representing a human-readable string that's conventionally, but not +/// always, UTF-8. +/// +/// Unlike `&str`, this type permits non-UTF-8 contents, making it suitable for user input, +/// non-native filenames (as `Path` only supports native filenames), and other applications that +/// need to round-trip whatever data the user provides. +/// +/// For an owned, growable byte string buffer, use +/// [`ByteString`](../../std/bstr/struct.ByteString.html). +/// +/// `ByteStr` implements `Deref` to `[u8]`, so all methods available on `[u8]` are available on +/// `ByteStr`. +/// +/// # Representation +/// +/// A `&ByteStr` has the same representation as a `&str`. That is, a `&ByteStr` is a wide pointer +/// which includes a pointer to some bytes and a length. +/// +/// # Trait implementations +/// +/// The `ByteStr` type has a number of trait implementations, and in particular, defines equality +/// and comparisons between `&ByteStr`, `&str`, and `&[u8]`, for convenience. +/// +/// The `Debug` implementation for `ByteStr` shows its bytes as a normal string, with invalid UTF-8 +/// presented as hex escape sequences. +/// +/// The `Display` implementation behaves as if the `ByteStr` were first lossily converted to a +/// `str`, with invalid UTF-8 presented as the Unicode replacement character: � +/// +#[unstable(feature = "bstr", issue = "134915")] +#[repr(transparent)] +#[doc(alias = "BStr")] +pub struct ByteStr(pub [u8]); + +impl ByteStr { + /// Creates a `ByteStr` slice from anything that can be converted to a byte slice. + /// + /// This is a zero-cost conversion. + /// + /// # Example + /// + /// You can create a `ByteStr` from a byte array, a byte slice or a string slice: + /// + /// ``` + /// # #![feature(bstr)] + /// # use std::bstr::ByteStr; + /// let a = ByteStr::new(b"abc"); + /// let b = ByteStr::new(&b"abc"[..]); + /// let c = ByteStr::new("abc"); + /// + /// assert_eq!(a, b); + /// assert_eq!(a, c); + /// ``` + #[inline] + #[unstable(feature = "bstr", issue = "134915")] + pub fn new>(bytes: &B) -> &Self { + ByteStr::from_bytes(bytes.as_ref()) + } + + #[doc(hidden)] + #[unstable(feature = "bstr_internals", issue = "none")] + #[inline] + pub fn from_bytes(slice: &[u8]) -> &Self { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`, so we can turn a reference to + // the wrapped type into a reference to the wrapper type. + unsafe { &*(slice as *const [u8] as *const Self) } + } + + #[doc(hidden)] + #[unstable(feature = "bstr_internals", issue = "none")] + #[inline] + pub fn from_bytes_mut(slice: &mut [u8]) -> &mut Self { + // SAFETY: `ByteStr` is a transparent wrapper around `[u8]`, so we can turn a reference to + // the wrapped type into a reference to the wrapper type. + unsafe { &mut *(slice as *mut [u8] as *mut Self) } + } + + #[doc(hidden)] + #[unstable(feature = "bstr_internals", issue = "none")] + #[inline] + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Deref for ByteStr { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + &self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl DerefMut for ByteStr { + #[inline] + fn deref_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +#[unstable(feature = "deref_pure_trait", issue = "87121")] +unsafe impl DerefPure for ByteStr {} + +#[unstable(feature = "bstr", issue = "134915")] +impl fmt::Debug for ByteStr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "\"")?; + for chunk in self.utf8_chunks() { + for c in chunk.valid().chars() { + match c { + '\0' => write!(f, "\\0")?, + '\x01'..='\x7f' => write!(f, "{}", (c as u8).escape_ascii())?, + _ => write!(f, "{}", c.escape_debug())?, + } + } + write!(f, "{}", chunk.invalid().escape_ascii())?; + } + write!(f, "\"")?; + Ok(()) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl fmt::Display for ByteStr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt_nopad(this: &ByteStr, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for chunk in this.utf8_chunks() { + f.write_str(chunk.valid())?; + if !chunk.invalid().is_empty() { + f.write_str("\u{FFFD}")?; + } + } + Ok(()) + } + + let Some(align) = f.align() else { + return fmt_nopad(self, f); + }; + let nchars: usize = self + .utf8_chunks() + .map(|chunk| chunk.valid().len() + if chunk.invalid().is_empty() { 0 } else { 1 }) + .sum(); + let padding = f.width().unwrap_or(0).saturating_sub(nchars); + let fill = f.fill(); + let (lpad, rpad) = match align { + fmt::Alignment::Left => (0, padding), + fmt::Alignment::Right => (padding, 0), + fmt::Alignment::Center => { + let half = padding / 2; + (half, half + padding % 2) + } + }; + for _ in 0..lpad { + write!(f, "{fill}")?; + } + fmt_nopad(self, f)?; + for _ in 0..rpad { + write!(f, "{fill}")?; + } + + Ok(()) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl AsRef<[u8]> for ByteStr { + #[inline] + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl AsRef for ByteStr { + #[inline] + fn as_ref(&self) -> &ByteStr { + self + } +} + +// `impl AsRef for [u8]` omitted to avoid widespread inference failures + +#[unstable(feature = "bstr", issue = "134915")] +impl AsRef for str { + #[inline] + fn as_ref(&self) -> &ByteStr { + ByteStr::new(self) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl AsMut<[u8]> for ByteStr { + #[inline] + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +// `impl AsMut for [u8]` omitted to avoid widespread inference failures + +// `impl Borrow for [u8]` omitted to avoid widespread inference failures + +// `impl Borrow for str` omitted to avoid widespread inference failures + +#[unstable(feature = "bstr", issue = "134915")] +impl Borrow<[u8]> for ByteStr { + #[inline] + fn borrow(&self) -> &[u8] { + &self.0 + } +} + +// `impl BorrowMut for [u8]` omitted to avoid widespread inference failures + +#[unstable(feature = "bstr", issue = "134915")] +impl BorrowMut<[u8]> for ByteStr { + #[inline] + fn borrow_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> Default for &'a ByteStr { + fn default() -> Self { + ByteStr::from_bytes(b"") + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> Default for &'a mut ByteStr { + fn default() -> Self { + ByteStr::from_bytes_mut(&mut []) + } +} + +// Omitted due to inference failures +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a, const N: usize> From<&'a [u8; N]> for &'a ByteStr { +// #[inline] +// fn from(s: &'a [u8; N]) -> Self { +// ByteStr::from_bytes(s) +// } +// } +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a> From<&'a [u8]> for &'a ByteStr { +// #[inline] +// fn from(s: &'a [u8]) -> Self { +// ByteStr::from_bytes(s) +// } +// } + +// Omitted due to slice-from-array-issue-113238: +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a> From<&'a ByteStr> for &'a [u8] { +// #[inline] +// fn from(s: &'a ByteStr) -> Self { +// &s.0 +// } +// } +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a> From<&'a mut ByteStr> for &'a mut [u8] { +// #[inline] +// fn from(s: &'a mut ByteStr) -> Self { +// &mut s.0 +// } +// } + +// Omitted due to inference failures +// +// #[unstable(feature = "bstr", issue = "134915")] +// impl<'a> From<&'a str> for &'a ByteStr { +// #[inline] +// fn from(s: &'a str) -> Self { +// ByteStr::from_bytes(s.as_bytes()) +// } +// } + +#[unstable(feature = "bstr", issue = "134915")] +impl hash::Hash for ByteStr { + #[inline] + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index for ByteStr { + type Output = u8; + + #[inline] + fn index(&self, idx: usize) -> &u8 { + &self.0[idx] + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index for ByteStr { + type Output = ByteStr; + + #[inline] + fn index(&self, _: RangeFull) -> &ByteStr { + self + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index> for ByteStr { + type Output = ByteStr; + + #[inline] + fn index(&self, r: Range) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index> for ByteStr { + type Output = ByteStr; + + #[inline] + fn index(&self, r: RangeInclusive) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index> for ByteStr { + type Output = ByteStr; + + #[inline] + fn index(&self, r: RangeFrom) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index> for ByteStr { + type Output = ByteStr; + + #[inline] + fn index(&self, r: RangeTo) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Index> for ByteStr { + type Output = ByteStr; + + #[inline] + fn index(&self, r: RangeToInclusive) -> &ByteStr { + ByteStr::from_bytes(&self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut for ByteStr { + #[inline] + fn index_mut(&mut self, idx: usize) -> &mut u8 { + &mut self.0[idx] + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut for ByteStr { + #[inline] + fn index_mut(&mut self, _: RangeFull) -> &mut ByteStr { + self + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut> for ByteStr { + #[inline] + fn index_mut(&mut self, r: Range) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut> for ByteStr { + #[inline] + fn index_mut(&mut self, r: RangeInclusive) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut> for ByteStr { + #[inline] + fn index_mut(&mut self, r: RangeFrom) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut> for ByteStr { + #[inline] + fn index_mut(&mut self, r: RangeTo) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl IndexMut> for ByteStr { + #[inline] + fn index_mut(&mut self, r: RangeToInclusive) -> &mut ByteStr { + ByteStr::from_bytes_mut(&mut self.0[r]) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl Eq for ByteStr {} + +#[unstable(feature = "bstr", issue = "134915")] +impl PartialEq for ByteStr { + #[inline] + fn eq(&self, other: &ByteStr) -> bool { + &self.0 == &other.0 + } +} + +#[doc(hidden)] +#[macro_export] +#[unstable(feature = "bstr_internals", issue = "none")] +macro_rules! impl_partial_eq { + ($lhs:ty, $rhs:ty) => { + #[allow(unused_lifetimes)] + impl<'a> PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + let other: &[u8] = other.as_ref(); + PartialEq::eq(self.as_bytes(), other) + } + } + + #[allow(unused_lifetimes)] + impl<'a> PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + let this: &[u8] = self.as_ref(); + PartialEq::eq(this, other.as_bytes()) + } + } + }; +} + +#[doc(hidden)] +#[unstable(feature = "bstr_internals", issue = "none")] +pub use impl_partial_eq; + +#[doc(hidden)] +#[macro_export] +#[unstable(feature = "bstr_internals", issue = "none")] +macro_rules! impl_partial_eq_ord { + ($lhs:ty, $rhs:ty) => { + $crate::bstr::impl_partial_eq!($lhs, $rhs); + + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl<'a> PartialOrd<$rhs> for $lhs { + #[inline] + fn partial_cmp(&self, other: &$rhs) -> Option { + let other: &[u8] = other.as_ref(); + PartialOrd::partial_cmp(self.as_bytes(), other) + } + } + + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl<'a> PartialOrd<$lhs> for $rhs { + #[inline] + fn partial_cmp(&self, other: &$lhs) -> Option { + let this: &[u8] = self.as_ref(); + PartialOrd::partial_cmp(this, other.as_bytes()) + } + } + }; +} + +#[doc(hidden)] +#[unstable(feature = "bstr_internals", issue = "none")] +pub use impl_partial_eq_ord; + +#[doc(hidden)] +#[macro_export] +#[unstable(feature = "bstr_internals", issue = "none")] +macro_rules! impl_partial_eq_n { + ($lhs:ty, $rhs:ty) => { + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl PartialEq<$rhs> for $lhs { + #[inline] + fn eq(&self, other: &$rhs) -> bool { + let other: &[u8] = other.as_ref(); + PartialEq::eq(self.as_bytes(), other) + } + } + + #[allow(unused_lifetimes)] + #[unstable(feature = "bstr", issue = "134915")] + impl PartialEq<$lhs> for $rhs { + #[inline] + fn eq(&self, other: &$lhs) -> bool { + let this: &[u8] = self.as_ref(); + PartialEq::eq(this, other.as_bytes()) + } + } + }; +} + +#[doc(hidden)] +#[unstable(feature = "bstr_internals", issue = "none")] +pub use impl_partial_eq_n; + +// PartialOrd with `[u8]` omitted to avoid inference failures +impl_partial_eq!(ByteStr, [u8]); +// PartialOrd with `&[u8]` omitted to avoid inference failures +impl_partial_eq!(ByteStr, &[u8]); +// PartialOrd with `str` omitted to avoid inference failures +impl_partial_eq!(ByteStr, str); +// PartialOrd with `&str` omitted to avoid inference failures +impl_partial_eq!(ByteStr, &str); +// PartialOrd with `[u8; N]` omitted to avoid inference failures +impl_partial_eq_n!(ByteStr, [u8; N]); +// PartialOrd with `[u8; N]` omitted to avoid inference failures +impl_partial_eq_n!(ByteStr, &[u8; N]); + +#[unstable(feature = "bstr", issue = "134915")] +impl Ord for ByteStr { + #[inline] + fn cmp(&self, other: &ByteStr) -> Ordering { + Ord::cmp(&self.0, &other.0) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl PartialOrd for ByteStr { + #[inline] + fn partial_cmp(&self, other: &ByteStr) -> Option { + PartialOrd::partial_cmp(&self.0, &other.0) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> TryFrom<&'a ByteStr> for &'a str { + type Error = crate::str::Utf8Error; + + #[inline] + fn try_from(s: &'a ByteStr) -> Result { + crate::str::from_utf8(&s.0) + } +} + +#[unstable(feature = "bstr", issue = "134915")] +impl<'a> TryFrom<&'a mut ByteStr> for &'a mut str { + type Error = crate::str::Utf8Error; + + #[inline] + fn try_from(s: &'a mut ByteStr) -> Result { + crate::str::from_utf8_mut(&mut s.0) + } +} diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 306d565a77e66..cbf00106c5173 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -22,8 +22,8 @@ //! (mutable via `&T`), in contrast with typical Rust types that exhibit 'inherited mutability' //! (mutable only via `&mut T`). //! -//! Cell types come in three flavors: `Cell`, `RefCell`, and `OnceCell`. Each provides -//! a different way of providing safe interior mutability. +//! Cell types come in four flavors: `Cell`, `RefCell`, `OnceCell`, and `LazyCell`. +//! Each provides a different way of providing safe interior mutability. //! //! ## `Cell` //! @@ -1590,10 +1590,10 @@ impl<'b, T: ?Sized> Ref<'b, T> { { let (a, b) = f(&*orig); let borrow = orig.borrow.clone(); - (Ref { value: NonNull::from(a), borrow }, Ref { - value: NonNull::from(b), - borrow: orig.borrow, - }) + ( + Ref { value: NonNull::from(a), borrow }, + Ref { value: NonNull::from(b), borrow: orig.borrow }, + ) } /// Converts into a reference to the underlying data. @@ -1758,11 +1758,10 @@ impl<'b, T: ?Sized> RefMut<'b, T> { { let borrow = orig.borrow.clone(); let (a, b) = f(&mut *orig); - (RefMut { value: NonNull::from(a), borrow, marker: PhantomData }, RefMut { - value: NonNull::from(b), - borrow: orig.borrow, - marker: PhantomData, - }) + ( + RefMut { value: NonNull::from(a), borrow, marker: PhantomData }, + RefMut { value: NonNull::from(b), borrow: orig.borrow, marker: PhantomData }, + ) } /// Converts into a mutable reference to the underlying data. @@ -2118,6 +2117,35 @@ impl UnsafeCell { pub const fn into_inner(self) -> T { self.value } + + /// Replace the value in this `UnsafeCell` and return the old value. + /// + /// # Safety + /// + /// The caller must take care to avoid aliasing and data races. + /// + /// - It is Undefined Behavior to allow calls to race with + /// any other access to the wrapped value. + /// - It is Undefined Behavior to call this while any other + /// reference(s) to the wrapped value are alive. + /// + /// # Examples + /// + /// ``` + /// #![feature(unsafe_cell_access)] + /// use std::cell::UnsafeCell; + /// + /// let uc = UnsafeCell::new(5); + /// + /// let old = unsafe { uc.replace(10) }; + /// assert_eq!(old, 5); + /// ``` + #[inline] + #[unstable(feature = "unsafe_cell_access", issue = "136327")] + pub const unsafe fn replace(&self, value: T) -> T { + // SAFETY: pointer comes from `&self` so naturally satisfies invariants. + unsafe { ptr::replace(self.get(), value) } + } } impl UnsafeCell { @@ -2230,6 +2258,61 @@ impl UnsafeCell { // no guarantee for user code that this will work in future versions of the compiler! this as *const T as *mut T } + + /// Get a shared reference to the value within the `UnsafeCell`. + /// + /// # Safety + /// + /// - It is Undefined Behavior to call this while any mutable + /// reference to the wrapped value is alive. + /// - Mutating the wrapped value while the returned + /// reference is alive is Undefined Behavior. + /// + /// # Examples + /// + /// ``` + /// #![feature(unsafe_cell_access)] + /// use std::cell::UnsafeCell; + /// + /// let uc = UnsafeCell::new(5); + /// + /// let val = unsafe { uc.as_ref_unchecked() }; + /// assert_eq!(val, &5); + /// ``` + #[inline] + #[unstable(feature = "unsafe_cell_access", issue = "136327")] + pub const unsafe fn as_ref_unchecked(&self) -> &T { + // SAFETY: pointer comes from `&self` so naturally satisfies ptr-to-ref invariants. + unsafe { self.get().as_ref_unchecked() } + } + + /// Get an exclusive reference to the value within the `UnsafeCell`. + /// + /// # Safety + /// + /// - It is Undefined Behavior to call this while any other + /// reference(s) to the wrapped value are alive. + /// - Mutating the wrapped value through other means while the + /// returned reference is alive is Undefined Behavior. + /// + /// # Examples + /// + /// ``` + /// #![feature(unsafe_cell_access)] + /// use std::cell::UnsafeCell; + /// + /// let uc = UnsafeCell::new(5); + /// + /// unsafe { *uc.as_mut_unchecked() += 1; } + /// assert_eq!(uc.into_inner(), 6); + /// ``` + #[inline] + #[unstable(feature = "unsafe_cell_access", issue = "136327")] + #[allow(clippy::mut_from_ref)] + pub const unsafe fn as_mut_unchecked(&self) -> &mut T { + // SAFETY: pointer comes from `&self` so naturally satisfies ptr-to-ref invariants. + unsafe { self.get().as_mut_unchecked() } + } } #[stable(feature = "unsafe_cell_default", since = "1.10.0")] diff --git a/library/core/src/cell/once.rs b/library/core/src/cell/once.rs index 6a85791916a61..c6c96571d33c9 100644 --- a/library/core/src/cell/once.rs +++ b/library/core/src/cell/once.rs @@ -8,6 +8,9 @@ use crate::{fmt, mem}; /// only immutable references can be obtained unless one has a mutable reference to the cell /// itself. In the same vein, the cell can only be re-initialized with such a mutable reference. /// +/// A `OnceCell` can be thought of as a safe abstraction over uninitialized data that becomes +/// initialized once written. +/// /// For a thread-safe version of this struct, see [`std::sync::OnceLock`]. /// /// [`RefCell`]: crate::cell::RefCell @@ -35,7 +38,7 @@ pub struct OnceCell { } impl OnceCell { - /// Creates a new empty cell. + /// Creates a new uninitialized cell. #[inline] #[must_use] #[stable(feature = "once_cell", since = "1.70.0")] @@ -46,7 +49,7 @@ impl OnceCell { /// Gets the reference to the underlying value. /// - /// Returns `None` if the cell is empty. + /// Returns `None` if the cell is uninitialized. #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn get(&self) -> Option<&T> { @@ -56,19 +59,19 @@ impl OnceCell { /// Gets the mutable reference to the underlying value. /// - /// Returns `None` if the cell is empty. + /// Returns `None` if the cell is uninitialized. #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn get_mut(&mut self) -> Option<&mut T> { self.inner.get_mut().as_mut() } - /// Sets the contents of the cell to `value`. + /// Initializes the contents of the cell to `value`. /// /// # Errors /// - /// This method returns `Ok(())` if the cell was empty and `Err(value)` if - /// it was full. + /// This method returns `Ok(())` if the cell was uninitialized + /// and `Err(value)` if it was already initialized. /// /// # Examples /// @@ -92,13 +95,13 @@ impl OnceCell { } } - /// Sets the contents of the cell to `value` if the cell was empty, then - /// returns a reference to it. + /// Initializes the contents of the cell to `value` if the cell was + /// uninitialized, then returns a reference to it. /// /// # Errors /// - /// This method returns `Ok(&value)` if the cell was empty and - /// `Err(¤t_value, value)` if it was full. + /// This method returns `Ok(&value)` if the cell was uninitialized + /// and `Err((¤t_value, value))` if it was already initialized. /// /// # Examples /// @@ -130,12 +133,12 @@ impl OnceCell { Ok(slot.insert(value)) } - /// Gets the contents of the cell, initializing it with `f` - /// if the cell was empty. + /// Gets the contents of the cell, initializing it to `f()` + /// if the cell was uninitialized. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// It is an error to reentrantly initialize the cell from `f`. Doing @@ -164,11 +167,11 @@ impl OnceCell { } /// Gets the mutable reference of the contents of the cell, - /// initializing it with `f` if the cell was empty. + /// initializing it to `f()` if the cell was uninitialized. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// # Examples @@ -199,13 +202,13 @@ impl OnceCell { } } - /// Gets the contents of the cell, initializing it with `f` if - /// the cell was empty. If the cell was empty and `f` failed, an - /// error is returned. + /// Gets the contents of the cell, initializing it to `f()` if + /// the cell was uninitialized. If the cell was uninitialized + /// and `f()` failed, an error is returned. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// It is an error to reentrantly initialize the cell from `f`. Doing @@ -239,12 +242,12 @@ impl OnceCell { } /// Gets the mutable reference of the contents of the cell, initializing - /// it with `f` if the cell was empty. If the cell was empty and `f` failed, - /// an error is returned. + /// it to `f()` if the cell was uninitialized. If the cell was uninitialized + /// and `f()` failed, an error is returned. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// # Examples @@ -256,7 +259,7 @@ impl OnceCell { /// /// let mut cell: OnceCell = OnceCell::new(); /// - /// // Failed initializers do not change the value + /// // Failed attempts to initialize the cell do not change its contents /// assert!(cell.get_mut_or_try_init(|| "not a number!".parse()).is_err()); /// assert!(cell.get().is_none()); /// @@ -295,7 +298,7 @@ impl OnceCell { /// Consumes the cell, returning the wrapped value. /// - /// Returns `None` if the cell was empty. + /// Returns `None` if the cell was uninitialized. /// /// # Examples /// @@ -321,7 +324,7 @@ impl OnceCell { /// Takes the value out of this `OnceCell`, moving it back to an uninitialized state. /// - /// Has no effect and returns `None` if the `OnceCell` hasn't been initialized. + /// Has no effect and returns `None` if the `OnceCell` is uninitialized. /// /// Safety is guaranteed by requiring a mutable reference. /// diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index fb8a740aced13..ccfdbf0eb704d 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -92,7 +92,7 @@ impl char { #[stable(feature = "assoc_char_consts", since = "1.52.0")] pub const UNICODE_VERSION: (u8, u8, u8) = crate::unicode::UNICODE_VERSION; - /// Creates an iterator over the UTF-16 encoded code points in `iter`, + /// Creates an iterator over the native endian UTF-16 encoded code points in `iter`, /// returning unpaired surrogates as `Err`s. /// /// # Examples @@ -704,7 +704,7 @@ impl char { unsafe { from_utf8_unchecked_mut(encode_utf8_raw(self as u32, dst)) } } - /// Encodes this character as UTF-16 into the provided `u16` buffer, + /// Encodes this character as native endian UTF-16 into the provided `u16` buffer, /// and then returns the subslice of the buffer that contains the encoded character. /// /// # Panics @@ -1828,7 +1828,7 @@ pub const fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] { unsafe { slice::from_raw_parts_mut(dst.as_mut_ptr(), len) } } -/// Encodes a raw `u32` value as UTF-16 into the provided `u16` buffer, +/// Encodes a raw `u32` value as native endian UTF-16 into the provided `u16` buffer, /// and then returns the subslice of the buffer that contains the encoded character. /// /// Unlike `char::encode_utf16`, this method also handles codepoints in the surrogate range. diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index ec1aed53eaf72..00300328b64c1 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -311,6 +311,16 @@ unsafe impl CloneToUninit for crate::ffi::CStr { } } +#[unstable(feature = "bstr", issue = "134915")] +unsafe impl CloneToUninit for crate::bstr::ByteStr { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + unsafe fn clone_to_uninit(&self, dst: *mut u8) { + // SAFETY: ByteStr is a `#[repr(transparent)]` wrapper around `[u8]` + unsafe { self.as_bytes().clone_to_uninit(dst) } + } +} + /// Implementations of `Clone` for primitive types. /// /// Implementations that cannot be described in Rust diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 97974d195fec6..594236cf1d96f 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -973,6 +973,24 @@ pub trait Ord: Eq + PartialOrd { /// assert_eq!(1.max(2), 2); /// assert_eq!(2.max(2), 2); /// ``` + /// ``` + /// use std::cmp::Ordering; + /// + /// #[derive(Eq)] + /// struct Equal(&'static str); + /// + /// impl PartialEq for Equal { + /// fn eq(&self, other: &Self) -> bool { true } + /// } + /// impl PartialOrd for Equal { + /// fn partial_cmp(&self, other: &Self) -> Option { Some(Ordering::Equal) } + /// } + /// impl Ord for Equal { + /// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal } + /// } + /// + /// assert_eq!(Equal("self").max(Equal("other")).0, "other"); + /// ``` #[stable(feature = "ord_max_min", since = "1.21.0")] #[inline] #[must_use] @@ -981,7 +999,7 @@ pub trait Ord: Eq + PartialOrd { where Self: Sized, { - max_by(self, other, Ord::cmp) + if other < self { self } else { other } } /// Compares and returns the minimum of two values. @@ -994,6 +1012,24 @@ pub trait Ord: Eq + PartialOrd { /// assert_eq!(1.min(2), 1); /// assert_eq!(2.min(2), 2); /// ``` + /// ``` + /// use std::cmp::Ordering; + /// + /// #[derive(Eq)] + /// struct Equal(&'static str); + /// + /// impl PartialEq for Equal { + /// fn eq(&self, other: &Self) -> bool { true } + /// } + /// impl PartialOrd for Equal { + /// fn partial_cmp(&self, other: &Self) -> Option { Some(Ordering::Equal) } + /// } + /// impl Ord for Equal { + /// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal } + /// } + /// + /// assert_eq!(Equal("self").min(Equal("other")).0, "self"); + /// ``` #[stable(feature = "ord_max_min", since = "1.21.0")] #[inline] #[must_use] @@ -1002,7 +1038,7 @@ pub trait Ord: Eq + PartialOrd { where Self: Sized, { - min_by(self, other, Ord::cmp) + if other < self { other } else { self } } /// Restrict a value to a certain interval. @@ -1414,6 +1450,24 @@ pub macro PartialOrd($item:item) { /// assert_eq!(cmp::min(1, 2), 1); /// assert_eq!(cmp::min(2, 2), 2); /// ``` +/// ``` +/// use std::cmp::{self, Ordering}; +/// +/// #[derive(Eq)] +/// struct Equal(&'static str); +/// +/// impl PartialEq for Equal { +/// fn eq(&self, other: &Self) -> bool { true } +/// } +/// impl PartialOrd for Equal { +/// fn partial_cmp(&self, other: &Self) -> Option { Some(Ordering::Equal) } +/// } +/// impl Ord for Equal { +/// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal } +/// } +/// +/// assert_eq!(cmp::min(Equal("v1"), Equal("v2")).0, "v1"); +/// ``` #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] @@ -1431,20 +1485,22 @@ pub fn min(v1: T, v2: T) -> T { /// ``` /// use std::cmp; /// -/// let result = cmp::min_by(-2, 1, |x: &i32, y: &i32| x.abs().cmp(&y.abs())); -/// assert_eq!(result, 1); +/// let abs_cmp = |x: &i32, y: &i32| x.abs().cmp(&y.abs()); /// -/// let result = cmp::min_by(-2, 3, |x: &i32, y: &i32| x.abs().cmp(&y.abs())); -/// assert_eq!(result, -2); +/// let result = cmp::min_by(2, -1, abs_cmp); +/// assert_eq!(result, -1); +/// +/// let result = cmp::min_by(2, -3, abs_cmp); +/// assert_eq!(result, 2); +/// +/// let result = cmp::min_by(1, -1, abs_cmp); +/// assert_eq!(result, 1); /// ``` #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn min_by Ordering>(v1: T, v2: T, compare: F) -> T { - match compare(&v1, &v2) { - Ordering::Less | Ordering::Equal => v1, - Ordering::Greater => v2, - } + if compare(&v2, &v1).is_lt() { v2 } else { v1 } } /// Returns the element that gives the minimum value from the specified function. @@ -1456,17 +1512,20 @@ pub fn min_by Ordering>(v1: T, v2: T, compare: F) -> T { /// ``` /// use std::cmp; /// -/// let result = cmp::min_by_key(-2, 1, |x: &i32| x.abs()); -/// assert_eq!(result, 1); +/// let result = cmp::min_by_key(2, -1, |x: &i32| x.abs()); +/// assert_eq!(result, -1); /// -/// let result = cmp::min_by_key(-2, 2, |x: &i32| x.abs()); -/// assert_eq!(result, -2); +/// let result = cmp::min_by_key(2, -3, |x: &i32| x.abs()); +/// assert_eq!(result, 2); +/// +/// let result = cmp::min_by_key(1, -1, |x: &i32| x.abs()); +/// assert_eq!(result, 1); /// ``` #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn min_by_key K, K: Ord>(v1: T, v2: T, mut f: F) -> T { - min_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2))) + if f(&v2) < f(&v1) { v2 } else { v1 } } /// Compares and returns the maximum of two values. @@ -1483,6 +1542,24 @@ pub fn min_by_key K, K: Ord>(v1: T, v2: T, mut f: F) -> T { /// assert_eq!(cmp::max(1, 2), 2); /// assert_eq!(cmp::max(2, 2), 2); /// ``` +/// ``` +/// use std::cmp::{self, Ordering}; +/// +/// #[derive(Eq)] +/// struct Equal(&'static str); +/// +/// impl PartialEq for Equal { +/// fn eq(&self, other: &Self) -> bool { true } +/// } +/// impl PartialOrd for Equal { +/// fn partial_cmp(&self, other: &Self) -> Option { Some(Ordering::Equal) } +/// } +/// impl Ord for Equal { +/// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal } +/// } +/// +/// assert_eq!(cmp::max(Equal("v1"), Equal("v2")).0, "v2"); +/// ``` #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] @@ -1500,20 +1577,22 @@ pub fn max(v1: T, v2: T) -> T { /// ``` /// use std::cmp; /// -/// let result = cmp::max_by(-2, 1, |x: &i32, y: &i32| x.abs().cmp(&y.abs())); +/// let abs_cmp = |x: &i32, y: &i32| x.abs().cmp(&y.abs()); +/// +/// let result = cmp::max_by(3, -2, abs_cmp) ; +/// assert_eq!(result, 3); +/// +/// let result = cmp::max_by(1, -2, abs_cmp); /// assert_eq!(result, -2); /// -/// let result = cmp::max_by(-2, 2, |x: &i32, y: &i32| x.abs().cmp(&y.abs())) ; -/// assert_eq!(result, 2); +/// let result = cmp::max_by(1, -1, abs_cmp); +/// assert_eq!(result, -1); /// ``` #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn max_by Ordering>(v1: T, v2: T, compare: F) -> T { - match compare(&v1, &v2) { - Ordering::Less | Ordering::Equal => v2, - Ordering::Greater => v1, - } + if compare(&v2, &v1).is_lt() { v1 } else { v2 } } /// Returns the element that gives the maximum value from the specified function. @@ -1525,17 +1604,20 @@ pub fn max_by Ordering>(v1: T, v2: T, compare: F) -> T { /// ``` /// use std::cmp; /// -/// let result = cmp::max_by_key(-2, 1, |x: &i32| x.abs()); +/// let result = cmp::max_by_key(3, -2, |x: &i32| x.abs()); +/// assert_eq!(result, 3); +/// +/// let result = cmp::max_by_key(1, -2, |x: &i32| x.abs()); /// assert_eq!(result, -2); /// -/// let result = cmp::max_by_key(-2, 2, |x: &i32| x.abs()); -/// assert_eq!(result, 2); +/// let result = cmp::max_by_key(1, -1, |x: &i32| x.abs()); +/// assert_eq!(result, -1); /// ``` #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] pub fn max_by_key K, K: Ord>(v1: T, v2: T, mut f: F) -> T { - max_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2))) + if f(&v2) < f(&v1) { v1 } else { v2 } } /// Compares and sorts two values, returning minimum and maximum. @@ -1549,13 +1631,32 @@ pub fn max_by_key K, K: Ord>(v1: T, v2: T, mut f: F) -> T { /// use std::cmp; /// /// assert_eq!(cmp::minmax(1, 2), [1, 2]); -/// assert_eq!(cmp::minmax(2, 2), [2, 2]); +/// assert_eq!(cmp::minmax(2, 1), [1, 2]); /// /// // You can destructure the result using array patterns /// let [min, max] = cmp::minmax(42, 17); /// assert_eq!(min, 17); /// assert_eq!(max, 42); /// ``` +/// ``` +/// #![feature(cmp_minmax)] +/// use std::cmp::{self, Ordering}; +/// +/// #[derive(Eq)] +/// struct Equal(&'static str); +/// +/// impl PartialEq for Equal { +/// fn eq(&self, other: &Self) -> bool { true } +/// } +/// impl PartialOrd for Equal { +/// fn partial_cmp(&self, other: &Self) -> Option { Some(Ordering::Equal) } +/// } +/// impl Ord for Equal { +/// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal } +/// } +/// +/// assert_eq!(cmp::minmax(Equal("v1"), Equal("v2")).map(|v| v.0), ["v1", "v2"]); +/// ``` #[inline] #[must_use] #[unstable(feature = "cmp_minmax", issue = "115939")] @@ -1563,7 +1664,7 @@ pub fn minmax(v1: T, v2: T) -> [T; 2] where T: Ord, { - if v1 <= v2 { [v1, v2] } else { [v2, v1] } + if v2 < v1 { [v2, v1] } else { [v1, v2] } } /// Returns minimum and maximum values with respect to the specified comparison function. @@ -1576,11 +1677,14 @@ where /// #![feature(cmp_minmax)] /// use std::cmp; /// -/// assert_eq!(cmp::minmax_by(-2, 1, |x: &i32, y: &i32| x.abs().cmp(&y.abs())), [1, -2]); -/// assert_eq!(cmp::minmax_by(-2, 2, |x: &i32, y: &i32| x.abs().cmp(&y.abs())), [-2, 2]); +/// let abs_cmp = |x: &i32, y: &i32| x.abs().cmp(&y.abs()); +/// +/// assert_eq!(cmp::minmax_by(-2, 1, abs_cmp), [1, -2]); +/// assert_eq!(cmp::minmax_by(-1, 2, abs_cmp), [-1, 2]); +/// assert_eq!(cmp::minmax_by(-2, 2, abs_cmp), [-2, 2]); /// /// // You can destructure the result using array patterns -/// let [min, max] = cmp::minmax_by(-42, 17, |x: &i32, y: &i32| x.abs().cmp(&y.abs())); +/// let [min, max] = cmp::minmax_by(-42, 17, abs_cmp); /// assert_eq!(min, 17); /// assert_eq!(max, -42); /// ``` @@ -1591,7 +1695,7 @@ pub fn minmax_by(v1: T, v2: T, compare: F) -> [T; 2] where F: FnOnce(&T, &T) -> Ordering, { - if compare(&v1, &v2).is_le() { [v1, v2] } else { [v2, v1] } + if compare(&v2, &v1).is_lt() { [v2, v1] } else { [v1, v2] } } /// Returns minimum and maximum values with respect to the specified key function. @@ -1620,7 +1724,7 @@ where F: FnMut(&T) -> K, K: Ord, { - minmax_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2))) + if f(&v2) < f(&v1) { [v2, v1] } else { [v1, v2] } } // Implementation of PartialEq, Eq, PartialOrd and Ord for primitive types diff --git a/library/core/src/contracts.rs b/library/core/src/contracts.rs new file mode 100644 index 0000000000000..c769e219e4d49 --- /dev/null +++ b/library/core/src/contracts.rs @@ -0,0 +1,21 @@ +//! Unstable module containing the unstable contracts lang items and attribute macros. +#![cfg(not(bootstrap))] + +pub use crate::macros::builtin::{contracts_ensures as ensures, contracts_requires as requires}; + +/// Emitted by rustc as a desugaring of `#[ensures(PRED)] fn foo() -> R { ... [return R;] ... }` +/// into: `fn foo() { let _check = build_check_ensures(|ret| PRED) ... [return _check(R);] ... }` +/// (including the implicit return of the tail expression, if any). +#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] +#[lang = "contract_build_check_ensures"] +#[track_caller] +pub fn build_check_ensures(cond: C) -> impl (Fn(Ret) -> Ret) + Copy +where + C: for<'a> Fn(&'a Ret) -> bool + Copy + 'static, +{ + #[track_caller] + move |ret| { + crate::intrinsics::contract_check_ensures(&ret, cond); + ret + } +} diff --git a/library/core/src/error.rs b/library/core/src/error.rs index 9dbea57fa1f86..62fce527b2fe2 100644 --- a/library/core/src/error.rs +++ b/library/core/src/error.rs @@ -1075,5 +1075,5 @@ impl Error for crate::time::TryFromFloatSecsError {} #[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")] impl Error for crate::ffi::FromBytesUntilNulError {} -#[unstable(feature = "get_many_mut", issue = "104642")] -impl Error for crate::slice::GetManyMutError {} +#[stable(feature = "get_many_mut", since = "1.86.0")] +impl Error for crate::slice::GetDisjointMutError {} diff --git a/library/core/src/escape.rs b/library/core/src/escape.rs index 0685f525dca83..0c3329f676eeb 100644 --- a/library/core/src/escape.rs +++ b/library/core/src/escape.rs @@ -163,28 +163,28 @@ pub(crate) struct EscapeIterInner { } impl EscapeIterInner { - pub const fn backslash(c: ascii::Char) -> Self { + pub(crate) const fn backslash(c: ascii::Char) -> Self { let (data, range) = backslash(c); Self { data, alive: range } } - pub const fn ascii(c: u8) -> Self { + pub(crate) const fn ascii(c: u8) -> Self { let (data, range) = escape_ascii(c); Self { data, alive: range } } - pub const fn unicode(c: char) -> Self { + pub(crate) const fn unicode(c: char) -> Self { let (data, range) = escape_unicode(c); Self { data, alive: range } } #[inline] - pub const fn empty() -> Self { + pub(crate) const fn empty() -> Self { Self { data: [ascii::Char::Null; N], alive: 0..0 } } #[inline] - pub fn as_ascii(&self) -> &[ascii::Char] { + pub(crate) fn as_ascii(&self) -> &[ascii::Char] { // SAFETY: `self.alive` is guaranteed to be a valid range for indexing `self.data`. unsafe { self.data.get_unchecked(usize::from(self.alive.start)..usize::from(self.alive.end)) @@ -192,34 +192,34 @@ impl EscapeIterInner { } #[inline] - pub fn as_str(&self) -> &str { + pub(crate) fn as_str(&self) -> &str { self.as_ascii().as_str() } #[inline] - pub fn len(&self) -> usize { + pub(crate) fn len(&self) -> usize { usize::from(self.alive.end - self.alive.start) } - pub fn next(&mut self) -> Option { + pub(crate) fn next(&mut self) -> Option { let i = self.alive.next()?; // SAFETY: `i` is guaranteed to be a valid index for `self.data`. unsafe { Some(self.data.get_unchecked(usize::from(i)).to_u8()) } } - pub fn next_back(&mut self) -> Option { + pub(crate) fn next_back(&mut self) -> Option { let i = self.alive.next_back()?; // SAFETY: `i` is guaranteed to be a valid index for `self.data`. unsafe { Some(self.data.get_unchecked(usize::from(i)).to_u8()) } } - pub fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { + pub(crate) fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { self.alive.advance_by(n) } - pub fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + pub(crate) fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { self.alive.advance_back_by(n) } } diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 8831443a10f17..75338e492ee0a 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -124,37 +124,25 @@ pub struct CStr { /// /// let _: FromBytesWithNulError = CStr::from_bytes_with_nul(b"f\0oo").unwrap_err(); /// ``` -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] #[stable(feature = "core_c_str", since = "1.64.0")] -pub struct FromBytesWithNulError { - kind: FromBytesWithNulErrorKind, -} - -#[derive(Clone, PartialEq, Eq, Debug)] -enum FromBytesWithNulErrorKind { - InteriorNul(usize), +pub enum FromBytesWithNulError { + /// Data provided contains an interior nul byte at byte `position`. + InteriorNul { + /// The position of the interior nul byte. + position: usize, + }, + /// Data provided is not nul terminated. NotNulTerminated, } -// FIXME: const stability attributes should not be required here, I think -impl FromBytesWithNulError { - const fn interior_nul(pos: usize) -> FromBytesWithNulError { - FromBytesWithNulError { kind: FromBytesWithNulErrorKind::InteriorNul(pos) } - } - const fn not_nul_terminated() -> FromBytesWithNulError { - FromBytesWithNulError { kind: FromBytesWithNulErrorKind::NotNulTerminated } - } -} - #[stable(feature = "frombyteswithnulerror_impls", since = "1.17.0")] impl Error for FromBytesWithNulError { #[allow(deprecated)] fn description(&self) -> &str { - match self.kind { - FromBytesWithNulErrorKind::InteriorNul(..) => { - "data provided contains an interior nul byte" - } - FromBytesWithNulErrorKind::NotNulTerminated => "data provided is not nul terminated", + match self { + Self::InteriorNul { .. } => "data provided contains an interior nul byte", + Self::NotNulTerminated => "data provided is not nul terminated", } } } @@ -199,8 +187,8 @@ impl fmt::Display for FromBytesWithNulError { #[allow(deprecated, deprecated_in_future)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.description())?; - if let FromBytesWithNulErrorKind::InteriorNul(pos) = self.kind { - write!(f, " at byte pos {pos}")?; + if let Self::InteriorNul { position } = self { + write!(f, " at byte pos {position}")?; } Ok(()) } @@ -349,25 +337,25 @@ impl CStr { /// use std::ffi::CStr; /// /// let cstr = CStr::from_bytes_with_nul(b"hello\0"); - /// assert!(cstr.is_ok()); + /// assert_eq!(cstr, Ok(c"hello")); /// ``` /// /// Creating a `CStr` without a trailing nul terminator is an error: /// /// ``` - /// use std::ffi::CStr; + /// use std::ffi::{CStr, FromBytesWithNulError}; /// /// let cstr = CStr::from_bytes_with_nul(b"hello"); - /// assert!(cstr.is_err()); + /// assert_eq!(cstr, Err(FromBytesWithNulError::NotNulTerminated)); /// ``` /// /// Creating a `CStr` with an interior nul byte is an error: /// /// ``` - /// use std::ffi::CStr; + /// use std::ffi::{CStr, FromBytesWithNulError}; /// /// let cstr = CStr::from_bytes_with_nul(b"he\0llo\0"); - /// assert!(cstr.is_err()); + /// assert_eq!(cstr, Err(FromBytesWithNulError::InteriorNul { position: 2 })); /// ``` #[stable(feature = "cstr_from_bytes", since = "1.10.0")] #[rustc_const_stable(feature = "const_cstr_methods", since = "1.72.0")] @@ -379,8 +367,8 @@ impl CStr { // of the byte slice. Ok(unsafe { Self::from_bytes_with_nul_unchecked(bytes) }) } - Some(nul_pos) => Err(FromBytesWithNulError::interior_nul(nul_pos)), - None => Err(FromBytesWithNulError::not_nul_terminated()), + Some(position) => Err(FromBytesWithNulError::InteriorNul { position }), + None => Err(FromBytesWithNulError::NotNulTerminated), } } @@ -743,7 +731,7 @@ const unsafe fn strlen(ptr: *const c_char) -> usize { len } else { - extern "C" { + unsafe extern "C" { /// Provided by libc or compiler_builtins. fn strlen(s: *const c_char) -> usize; } diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 5f32775822be6..9bae5fd466a18 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -37,173 +37,14 @@ pub use self::va_list::{VaList, VaListImpl}; )] pub mod va_list; -macro_rules! type_alias { - { - $Docfile:tt, $Alias:ident = $Real:ty; - $( $Cfg:tt )* - } => { - #[doc = include_str!($Docfile)] - $( $Cfg )* - #[stable(feature = "core_ffi_c", since = "1.64.0")] - pub type $Alias = $Real; - } -} - -type_alias! { "c_char.md", c_char = c_char_definition::c_char; #[doc(cfg(all()))] } - -type_alias! { "c_schar.md", c_schar = i8; } -type_alias! { "c_uchar.md", c_uchar = u8; } -type_alias! { "c_short.md", c_short = i16; } -type_alias! { "c_ushort.md", c_ushort = u16; } - -type_alias! { "c_int.md", c_int = c_int_definition::c_int; #[doc(cfg(all()))] } -type_alias! { "c_uint.md", c_uint = c_int_definition::c_uint; #[doc(cfg(all()))] } - -type_alias! { "c_long.md", c_long = c_long_definition::c_long; #[doc(cfg(all()))] } -type_alias! { "c_ulong.md", c_ulong = c_long_definition::c_ulong; #[doc(cfg(all()))] } - -type_alias! { "c_longlong.md", c_longlong = i64; } -type_alias! { "c_ulonglong.md", c_ulonglong = u64; } - -type_alias! { "c_float.md", c_float = f32; } -type_alias! { "c_double.md", c_double = f64; } - -/// Equivalent to C's `size_t` type, from `stddef.h` (or `cstddef` for C++). -/// -/// This type is currently always [`usize`], however in the future there may be -/// platforms where this is not the case. -#[unstable(feature = "c_size_t", issue = "88345")] -pub type c_size_t = usize; - -/// Equivalent to C's `ptrdiff_t` type, from `stddef.h` (or `cstddef` for C++). -/// -/// This type is currently always [`isize`], however in the future there may be -/// platforms where this is not the case. +mod primitives; +#[stable(feature = "core_ffi_c", since = "1.64.0")] +pub use self::primitives::{ + c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, + c_ulong, c_ulonglong, c_ushort, +}; #[unstable(feature = "c_size_t", issue = "88345")] -pub type c_ptrdiff_t = isize; - -/// Equivalent to C's `ssize_t` (on POSIX) or `SSIZE_T` (on Windows) type. -/// -/// This type is currently always [`isize`], however in the future there may be -/// platforms where this is not the case. -#[unstable(feature = "c_size_t", issue = "88345")] -pub type c_ssize_t = isize; - -mod c_char_definition { - cfg_if! { - // These are the targets on which c_char is unsigned. Usually the - // signedness is the same for all target_os values on a given architecture - // but there are some exceptions (see isSignedCharDefault() in clang). - // - // aarch64: - // Section 10 "Arm C and C++ language mappings" in Procedure Call Standard for the Arm® - // 64-bit Architecture (AArch64) says C/C++ char is unsigned byte. - // https://github.com/ARM-software/abi-aa/blob/2024Q3/aapcs64/aapcs64.rst#arm-c-and-c-language-mappings - // arm: - // Section 8 "Arm C and C++ Language Mappings" in Procedure Call Standard for the Arm® - // Architecture says C/C++ char is unsigned byte. - // https://github.com/ARM-software/abi-aa/blob/2024Q3/aapcs32/aapcs32.rst#arm-c-and-c-language-mappings - // csky: - // Section 2.1.2 "Primary Data Type" in C-SKY V2 CPU Applications Binary Interface - // Standards Manual says ANSI C char is unsigned byte. - // https://github.com/c-sky/csky-doc/blob/9f7121f7d40970ba5cc0f15716da033db2bb9d07/C-SKY_V2_CPU_Applications_Binary_Interface_Standards_Manual.pdf - // Note: this doesn't seem to match Clang's default (https://github.com/rust-lang/rust/issues/129945). - // hexagon: - // Section 3.1 "Basic data type" in Qualcomm Hexagon™ Application - // Binary Interface User Guide says "By default, the `char` data type is unsigned." - // https://docs.qualcomm.com/bundle/publicresource/80-N2040-23_REV_K_Qualcomm_Hexagon_Application_Binary_Interface_User_Guide.pdf - // msp430: - // Section 2.1 "Basic Types" in MSP430 Embedded Application Binary - // Interface says "The char type is unsigned by default". - // https://www.ti.com/lit/an/slaa534a/slaa534a.pdf - // Note: this doesn't seem to match Clang's default (https://github.com/rust-lang/rust/issues/129945). - // powerpc/powerpc64: - // - PPC32 SysV: "Table 3-1 Scalar Types" in System V Application Binary Interface PowerPC - // Processor Supplement says ANSI C char is unsigned byte - // https://refspecs.linuxfoundation.org/elf/elfspec_ppc.pdf - // - PPC64 ELFv1: Section 3.1.4 "Fundamental Types" in 64-bit PowerPC ELF Application - // Binary Interface Supplement 1.9 says ANSI C is unsigned byte - // https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#FUND-TYPE - // - PPC64 ELFv2: Section 2.1.2.2 "Fundamental Types" in 64-Bit ELF V2 ABI Specification - // says char is unsigned byte - // https://openpowerfoundation.org/specifications/64bitelfabi/ - // - AIX: XL C for AIX Language Reference says "By default, char behaves like an unsigned char." - // https://www.ibm.com/docs/en/xl-c-aix/13.1.3?topic=specifiers-character-types - // riscv32/riscv64: - // C/C++ type representations section in RISC-V Calling Conventions - // page in RISC-V ELF psABI Document says "char is unsigned." - // https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/draft-20240829-13bfa9f54634cb60d86b9b333e109f077805b4b3/riscv-cc.adoc#cc-type-representations - // s390x: - // - ELF: "Table 1.1.: Scalar types" in ELF Application Binary Interface s390x Supplement - // Version 1.6.1 categorize ISO C char in unsigned integer - // https://github.com/IBM/s390x-abi/releases/tag/v1.6.1 - // - z/OS: XL C/C++ Language Reference says: "By default, char behaves like an unsigned char." - // https://www.ibm.com/docs/en/zos/3.1.0?topic=specifiers-character-types - // Xtensa: - // - "The char type is unsigned by default for Xtensa processors." - // - // On the following operating systems, c_char is signed by default, regardless of architecture. - // Darwin (macOS, iOS, etc.): - // Apple targets' c_char is signed by default even on arm - // https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Handle-data-types-and-data-alignment-properly - // Windows: - // Windows MSVC C++ Language Reference says "Microsoft-specific: Variables of type char - // are promoted to int as if from type signed char by default, unless the /J compilation - // option is used." - // https://learn.microsoft.com/en-us/cpp/cpp/fundamental-types-cpp?view=msvc-170#character-types) - // L4RE: - // The kernel builds with -funsigned-char on all targets (but useserspace follows the - // architecture defaults). As we only have a target for userspace apps so there are no - // special cases for L4RE below. - if #[cfg(all( - not(windows), - not(target_vendor = "apple"), - any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "csky", - target_arch = "hexagon", - target_arch = "msp430", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "riscv64", - target_arch = "riscv32", - target_arch = "s390x", - target_arch = "xtensa", - ) - ))] { - pub type c_char = u8; - } else { - // On every other target, c_char is signed. - pub type c_char = i8; - } - } -} - -mod c_int_definition { - cfg_if! { - if #[cfg(any(target_arch = "avr", target_arch = "msp430"))] { - pub type c_int = i16; - pub type c_uint = u16; - } else { - pub type c_int = i32; - pub type c_uint = u32; - } - } -} - -mod c_long_definition { - cfg_if! { - if #[cfg(all(target_pointer_width = "64", not(windows)))] { - pub type c_long = i64; - pub type c_ulong = u64; - } else { - // The minimal size of `long` in the C standard is 32 bits - pub type c_long = i32; - pub type c_ulong = u32; - } - } -} +pub use self::primitives::{c_ptrdiff_t, c_size_t, c_ssize_t}; // N.B., for LLVM to recognize the void pointer type and by extension // functions like malloc(), we need to have it represented as i8* in @@ -249,4 +90,4 @@ impl fmt::Debug for c_void { cfg(not(target_feature = "crt-static")) )] #[link(name = "/defaultlib:libcmt", modifiers = "+verbatim", cfg(target_feature = "crt-static"))] -extern "C" {} +unsafe extern "C" {} diff --git a/library/core/src/ffi/primitives.rs b/library/core/src/ffi/primitives.rs new file mode 100644 index 0000000000000..ece3c7538dabb --- /dev/null +++ b/library/core/src/ffi/primitives.rs @@ -0,0 +1,174 @@ +//! Defines primitive types that match C's type definitions for FFI compatibility. +//! +//! This module is intentionally standalone to facilitate parsing when retrieving +//! core C types. + +macro_rules! type_alias { + { + $Docfile:tt, $Alias:ident = $Real:ty; + $( $Cfg:tt )* + } => { + #[doc = include_str!($Docfile)] + $( $Cfg )* + #[stable(feature = "core_ffi_c", since = "1.64.0")] + pub type $Alias = $Real; + } +} + +type_alias! { "c_char.md", c_char = c_char_definition::c_char; #[doc(cfg(all()))] } + +type_alias! { "c_schar.md", c_schar = i8; } +type_alias! { "c_uchar.md", c_uchar = u8; } +type_alias! { "c_short.md", c_short = i16; } +type_alias! { "c_ushort.md", c_ushort = u16; } + +type_alias! { "c_int.md", c_int = c_int_definition::c_int; #[doc(cfg(all()))] } +type_alias! { "c_uint.md", c_uint = c_int_definition::c_uint; #[doc(cfg(all()))] } + +type_alias! { "c_long.md", c_long = c_long_definition::c_long; #[doc(cfg(all()))] } +type_alias! { "c_ulong.md", c_ulong = c_long_definition::c_ulong; #[doc(cfg(all()))] } + +type_alias! { "c_longlong.md", c_longlong = i64; } +type_alias! { "c_ulonglong.md", c_ulonglong = u64; } + +type_alias! { "c_float.md", c_float = f32; } +type_alias! { "c_double.md", c_double = f64; } + +mod c_char_definition { + cfg_if! { + // These are the targets on which c_char is unsigned. Usually the + // signedness is the same for all target_os values on a given architecture + // but there are some exceptions (see isSignedCharDefault() in clang). + // + // aarch64: + // Section 10 "Arm C and C++ language mappings" in Procedure Call Standard for the Arm® + // 64-bit Architecture (AArch64) says C/C++ char is unsigned byte. + // https://github.com/ARM-software/abi-aa/blob/2024Q3/aapcs64/aapcs64.rst#arm-c-and-c-language-mappings + // arm: + // Section 8 "Arm C and C++ Language Mappings" in Procedure Call Standard for the Arm® + // Architecture says C/C++ char is unsigned byte. + // https://github.com/ARM-software/abi-aa/blob/2024Q3/aapcs32/aapcs32.rst#arm-c-and-c-language-mappings + // csky: + // Section 2.1.2 "Primary Data Type" in C-SKY V2 CPU Applications Binary Interface + // Standards Manual says ANSI C char is unsigned byte. + // https://github.com/c-sky/csky-doc/blob/9f7121f7d40970ba5cc0f15716da033db2bb9d07/C-SKY_V2_CPU_Applications_Binary_Interface_Standards_Manual.pdf + // Note: this doesn't seem to match Clang's default (https://github.com/rust-lang/rust/issues/129945). + // hexagon: + // Section 3.1 "Basic data type" in Qualcomm Hexagon™ Application + // Binary Interface User Guide says "By default, the `char` data type is unsigned." + // https://docs.qualcomm.com/bundle/publicresource/80-N2040-23_REV_K_Qualcomm_Hexagon_Application_Binary_Interface_User_Guide.pdf + // msp430: + // Section 2.1 "Basic Types" in MSP430 Embedded Application Binary + // Interface says "The char type is unsigned by default". + // https://www.ti.com/lit/an/slaa534a/slaa534a.pdf + // powerpc/powerpc64: + // - PPC32 SysV: "Table 3-1 Scalar Types" in System V Application Binary Interface PowerPC + // Processor Supplement says ANSI C char is unsigned byte + // https://refspecs.linuxfoundation.org/elf/elfspec_ppc.pdf + // - PPC64 ELFv1: Section 3.1.4 "Fundamental Types" in 64-bit PowerPC ELF Application + // Binary Interface Supplement 1.9 says ANSI C is unsigned byte + // https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#FUND-TYPE + // - PPC64 ELFv2: Section 2.1.2.2 "Fundamental Types" in 64-Bit ELF V2 ABI Specification + // says char is unsigned byte + // https://openpowerfoundation.org/specifications/64bitelfabi/ + // - AIX: XL C for AIX Language Reference says "By default, char behaves like an unsigned char." + // https://www.ibm.com/docs/en/xl-c-aix/13.1.3?topic=specifiers-character-types + // riscv32/riscv64: + // C/C++ type representations section in RISC-V Calling Conventions + // page in RISC-V ELF psABI Document says "char is unsigned." + // https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/draft-20240829-13bfa9f54634cb60d86b9b333e109f077805b4b3/riscv-cc.adoc#cc-type-representations + // s390x: + // - ELF: "Table 1.1.: Scalar types" in ELF Application Binary Interface s390x Supplement + // Version 1.6.1 categorize ISO C char in unsigned integer + // https://github.com/IBM/s390x-abi/releases/tag/v1.6.1 + // - z/OS: XL C/C++ Language Reference says: "By default, char behaves like an unsigned char." + // https://www.ibm.com/docs/en/zos/3.1.0?topic=specifiers-character-types + // xtensa: + // Section 2.17.1 "Data Types and Alignment" of Xtensa LX Microprocessor Overview handbook + // says "`char` type is unsigned by default". + // https://loboris.eu/ESP32/Xtensa_lx%20Overview%20handbook.pdf + // + // On the following operating systems, c_char is signed by default, regardless of architecture. + // Darwin (macOS, iOS, etc.): + // Apple targets' c_char is signed by default even on arm + // https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Handle-data-types-and-data-alignment-properly + // Windows: + // Windows MSVC C++ Language Reference says "Microsoft-specific: Variables of type char + // are promoted to int as if from type signed char by default, unless the /J compilation + // option is used." + // https://learn.microsoft.com/en-us/cpp/cpp/fundamental-types-cpp?view=msvc-170#character-types + // L4Re: + // The kernel builds with -funsigned-char on all targets (but useserspace follows the + // architecture defaults). As we only have a target for userspace apps so there are no + // special cases for L4Re below. + // https://github.com/rust-lang/rust/pull/132975#issuecomment-2484645240 + if #[cfg(all( + not(windows), + not(target_vendor = "apple"), + any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "csky", + target_arch = "hexagon", + target_arch = "msp430", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "riscv32", + target_arch = "riscv64", + target_arch = "s390x", + target_arch = "xtensa", + ) + ))] { + pub(super) type c_char = u8; + } else { + // On every other target, c_char is signed. + pub(super) type c_char = i8; + } + } +} + +mod c_long_definition { + cfg_if! { + if #[cfg(all(target_pointer_width = "64", not(windows)))] { + pub(super) type c_long = i64; + pub(super) type c_ulong = u64; + } else { + // The minimal size of `long` in the C standard is 32 bits + pub(super) type c_long = i32; + pub(super) type c_ulong = u32; + } + } +} + +/// Equivalent to C's `size_t` type, from `stddef.h` (or `cstddef` for C++). +/// +/// This type is currently always [`usize`], however in the future there may be +/// platforms where this is not the case. +#[unstable(feature = "c_size_t", issue = "88345")] +pub type c_size_t = usize; + +/// Equivalent to C's `ptrdiff_t` type, from `stddef.h` (or `cstddef` for C++). +/// +/// This type is currently always [`isize`], however in the future there may be +/// platforms where this is not the case. +#[unstable(feature = "c_size_t", issue = "88345")] +pub type c_ptrdiff_t = isize; + +/// Equivalent to C's `ssize_t` (on POSIX) or `SSIZE_T` (on Windows) type. +/// +/// This type is currently always [`isize`], however in the future there may be +/// platforms where this is not the case. +#[unstable(feature = "c_size_t", issue = "88345")] +pub type c_ssize_t = isize; + +mod c_int_definition { + cfg_if! { + if #[cfg(any(target_arch = "avr", target_arch = "msp430"))] { + pub(super) type c_int = i16; + pub(super) type c_uint = u16; + } else { + pub(super) type c_int = i32; + pub(super) type c_uint = u32; + } + } +} diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index f67c592d8d8f7..cceb186b31e79 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -302,18 +302,28 @@ impl<'f> Drop for VaListImpl<'f> { } } -extern "rust-intrinsic" { - /// Destroy the arglist `ap` after initialization with `va_start` or - /// `va_copy`. - #[rustc_nounwind] - fn va_end(ap: &mut VaListImpl<'_>); +/// Destroy the arglist `ap` after initialization with `va_start` or +/// `va_copy`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +unsafe fn va_end(_ap: &mut VaListImpl<'_>) { + unreachable!() +} - /// Copies the current location of arglist `src` to the arglist `dst`. - #[rustc_nounwind] - fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>); +/// Copies the current location of arglist `src` to the arglist `dst`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +unsafe fn va_copy<'f>(_dest: *mut VaListImpl<'f>, _src: &VaListImpl<'f>) { + unreachable!() +} - /// Loads an argument of type `T` from the `va_list` `ap` and increment the - /// argument `ap` points to. - #[rustc_nounwind] - fn va_arg(ap: &mut VaListImpl<'_>) -> T; +/// Loads an argument of type `T` from the `va_list` `ap` and increment the +/// argument `ap` points to. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +unsafe fn va_arg(_ap: &mut VaListImpl<'_>) -> T { + unreachable!() } diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index 1862be0e86c5d..665b05b12ec07 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -1228,6 +1228,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// assert_eq!(format!("{:?}", wrapped), "'a'"); /// ``` #[unstable(feature = "debug_closure_helpers", issue = "117729")] +#[must_use = "returns a type implementing Debug and Display, which do not have any effects unless they are used"] pub fn from_fn) -> fmt::Result>(f: F) -> FromFn { FromFn(f) } diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index c2c78dd9c67eb..a1bf3a4d7a706 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -288,7 +288,7 @@ pub enum DebugAsHex { /// /// `FormattingOptions` is a [`Formatter`] without an attached [`Write`] trait. /// It is mainly used to construct `Formatter` instances. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[unstable(feature = "formatting_options", issue = "118117")] pub struct FormattingOptions { flags: u32, @@ -508,6 +508,15 @@ impl FormattingOptions { } } +#[unstable(feature = "formatting_options", issue = "118117")] +impl Default for FormattingOptions { + /// Same as [`FormattingOptions::new()`]. + fn default() -> Self { + // The `#[derive(Default)]` implementation would set `fill` to `\0` instead of space. + Self::new() + } +} + /// Configuration for formatting. /// /// A `Formatter` represents various options related to formatting. Users do not @@ -596,7 +605,7 @@ impl<'a> Arguments<'a> { /// When using the format_args!() macro, this function is used to generate the /// Arguments structure. #[inline] - pub fn new_v1( + pub const fn new_v1( pieces: &'a [&'static str; P], args: &'a [rt::Argument<'a>; A], ) -> Arguments<'a> { @@ -612,7 +621,7 @@ impl<'a> Arguments<'a> { /// 2. Every `rt::Placeholder::position` value within `fmt` must be a valid index of `args`. /// 3. Every `rt::Count::Param` within `fmt` must contain a valid index of `args`. #[inline] - pub fn new_v1_formatted( + pub const fn new_v1_formatted( pieces: &'a [&'static str], args: &'a [rt::Argument<'a>], fmt: &'a [rt::Placeholder], diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index 683e45b35f70a..4467b37bd4510 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -192,7 +192,8 @@ macro_rules! impl_Debug { } // 2 digit decimal look up table -static DEC_DIGITS_LUT: &[u8; 200] = b"0001020304050607080910111213141516171819\ +static DEC_DIGITS_LUT: &[u8; 200] = b"\ + 0001020304050607080910111213141516171819\ 2021222324252627282930313233343536373839\ 4041424344454647484950515253545556575859\ 6061626364656667686970717273747576777879\ @@ -232,83 +233,89 @@ macro_rules! impl_Display { #[cfg(not(feature = "optimize_for_size"))] impl $unsigned { - fn _fmt(mut self, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { - const SIZE: usize = $unsigned::MAX.ilog(10) as usize + 1; - let mut buf = [MaybeUninit::::uninit(); SIZE]; - let mut curr = SIZE; - let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); - let lut_ptr = DEC_DIGITS_LUT.as_ptr(); - - // SAFETY: Since `d1` and `d2` are always less than or equal to `198`, we - // can copy from `lut_ptr[d1..d1 + 1]` and `lut_ptr[d2..d2 + 1]`. To show - // that it's OK to copy into `buf_ptr`, notice that at the beginning - // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at - // each step this is kept the same as `n` is divided. Since `n` is always - // non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]` - // is safe to access. - unsafe { - // need at least 16 bits for the 4-characters-at-a-time to work. - #[allow(overflowing_literals)] - #[allow(unused_comparisons)] - // This block will be removed for smaller types at compile time and in the worst - // case, it will prevent to have the `10000` literal to overflow for `i8` and `u8`. - if core::mem::size_of::<$unsigned>() >= 2 { - // eagerly decode 4 characters at a time - while self >= 10000 { - let rem = (self % 10000) as usize; - self /= 10000; - - let d1 = (rem / 100) << 1; - let d2 = (rem % 100) << 1; - curr -= 4; - - // We are allowed to copy to `buf_ptr[curr..curr + 3]` here since - // otherwise `curr < 0`. But then `n` was originally at least `10000^10` - // which is `10^40 > 2^128 > n`. - ptr::copy_nonoverlapping(lut_ptr.add(d1 as usize), buf_ptr.add(curr), 2); - ptr::copy_nonoverlapping(lut_ptr.add(d2 as usize), buf_ptr.add(curr + 2), 2); - } - } - - // if we reach here numbers are <= 9999, so at most 4 chars long - let mut n = self as usize; // possibly reduce 64bit math + fn _fmt(self, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { + const MAX_DEC_N: usize = $unsigned::MAX.ilog(10) as usize + 1; + // Buffer decimals for $unsigned with right alignment. + let mut buf = [MaybeUninit::::uninit(); MAX_DEC_N]; + // Count the number of bytes in buf that are not initialized. + let mut offset = buf.len(); + // Consume the least-significant decimals from a working copy. + let mut remain = self; + + // Format per four digits from the lookup table. + // Four digits need a 16-bit $unsigned or wider. + while size_of::() > 1 && remain > 999.try_into().expect("branch is not hit for types that cannot fit 999 (u8)") { + // SAFETY: All of the decimals fit in buf due to MAX_DEC_N + // and the while condition ensures at least 4 more decimals. + unsafe { core::hint::assert_unchecked(offset >= 4) } + // SAFETY: The offset counts down from its initial buf.len() + // without underflow due to the previous precondition. + unsafe { core::hint::assert_unchecked(offset <= buf.len()) } + offset -= 4; + + // pull two pairs + let scale: Self = 1_00_00.try_into().expect("branch is not hit for types that cannot fit 1E4 (u8)"); + let quad = remain % scale; + remain /= scale; + let pair1 = (quad / 100) as usize; + let pair2 = (quad % 100) as usize; + buf[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]); + buf[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]); + buf[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]); + buf[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]); + } - // decode 2 more chars, if > 2 chars - if n >= 100 { - let d1 = (n % 100) << 1; - n /= 100; - curr -= 2; - ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2); - } + // Format per two digits from the lookup table. + if remain > 9 { + // SAFETY: All of the decimals fit in buf due to MAX_DEC_N + // and the while condition ensures at least 2 more decimals. + unsafe { core::hint::assert_unchecked(offset >= 2) } + // SAFETY: The offset counts down from its initial buf.len() + // without underflow due to the previous precondition. + unsafe { core::hint::assert_unchecked(offset <= buf.len()) } + offset -= 2; + + let pair = (remain % 100) as usize; + remain /= 100; + buf[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]); + buf[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]); + } - // if we reach here numbers are <= 100, so at most 2 chars long - // The biggest it can be is 99, and 99 << 1 == 198, so a `u8` is enough. - // decode last 1 or 2 chars - if n < 10 { - curr -= 1; - *buf_ptr.add(curr) = (n as u8) + b'0'; - } else { - let d1 = n << 1; - curr -= 2; - ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2); - } + // Format the last remaining digit, if any. + if remain != 0 || self == 0 { + // SAFETY: All of the decimals fit in buf due to MAX_DEC_N + // and the if condition ensures (at least) 1 more decimals. + unsafe { core::hint::assert_unchecked(offset >= 1) } + // SAFETY: The offset counts down from its initial buf.len() + // without underflow due to the previous precondition. + unsafe { core::hint::assert_unchecked(offset <= buf.len()) } + offset -= 1; + + // Either the compiler sees that remain < 10, or it prevents + // a boundary check up next. + let last = (remain & 15) as usize; + buf[offset].write(DEC_DIGITS_LUT[last * 2 + 1]); + // not used: remain = 0; } - // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid - // UTF-8 since `DEC_DIGITS_LUT` is - let buf_slice = unsafe { - str::from_utf8_unchecked( - slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr)) + // SAFETY: All buf content since offset is set. + let written = unsafe { buf.get_unchecked(offset..) }; + // SAFETY: Writes use ASCII from the lookup table exclusively. + let as_str = unsafe { + str::from_utf8_unchecked(slice::from_raw_parts( + MaybeUninit::slice_as_ptr(written), + written.len(), + )) }; - f.pad_integral(is_nonnegative, "", buf_slice) + f.pad_integral(is_nonnegative, "", as_str) } })* #[cfg(feature = "optimize_for_size")] fn $gen_name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { - const SIZE: usize = $u::MAX.ilog(10) as usize + 1; - let mut buf = [MaybeUninit::::uninit(); SIZE]; - let mut curr = buf.len(); + const MAX_DEC_N: usize = $u::MAX.ilog(10) as usize + 1; + let mut buf = [MaybeUninit::::uninit(); MAX_DEC_N]; + let mut curr = MAX_DEC_N; let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); // SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning @@ -726,28 +733,9 @@ fn udiv_1e19(n: u128) -> (u128, u64) { let quot = if n < 1 << 83 { ((n >> 19) as u64 / (DIV >> 19)) as u128 } else { - u128_mulhi(n, FACTOR) >> 62 + n.widening_mul(FACTOR).1 >> 62 }; let rem = (n - quot * DIV as u128) as u64; (quot, rem) } - -/// Multiply unsigned 128 bit integers, return upper 128 bits of the result -#[inline] -fn u128_mulhi(x: u128, y: u128) -> u128 { - let x_lo = x as u64; - let x_hi = (x >> 64) as u64; - let y_lo = y as u64; - let y_hi = (y >> 64) as u64; - - // handle possibility of overflow - let carry = (x_lo as u128 * y_lo as u128) >> 64; - let m = x_lo as u128 * y_hi as u128 + carry; - let high1 = m >> 64; - - let m_lo = m as u64; - let high2 = (x_hi as u128 * y_lo as u128 + m_lo as u128) >> 64; - - x_hi as u128 * y_hi as u128 + high1 + high2 -} diff --git a/library/core/src/fmt/rt.rs b/library/core/src/fmt/rt.rs index 94341a4da66cd..85d089a079082 100644 --- a/library/core/src/fmt/rt.rs +++ b/library/core/src/fmt/rt.rs @@ -96,12 +96,12 @@ pub struct Argument<'a> { #[rustc_diagnostic_item = "ArgumentMethods"] impl Argument<'_> { #[inline] - fn new<'a, T>(x: &'a T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'a> { + const fn new<'a, T>(x: &'a T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'a> { Argument { // INVARIANT: this creates an `ArgumentType<'a>` from a `&'a T` and // a `fn(&T, ...)`, so the invariant is maintained. ty: ArgumentType::Placeholder { - value: NonNull::from(x).cast(), + value: NonNull::from_ref(x).cast(), // SAFETY: function pointers always have the same layout. formatter: unsafe { mem::transmute(f) }, _lifetime: PhantomData, @@ -150,7 +150,7 @@ impl Argument<'_> { Self::new(x, UpperExp::fmt) } #[inline] - pub fn from_usize(x: &usize) -> Argument<'_> { + pub const fn from_usize(x: &usize) -> Argument<'_> { Argument { ty: ArgumentType::Count(*x) } } @@ -181,7 +181,7 @@ impl Argument<'_> { } #[inline] - pub(super) fn as_usize(&self) -> Option { + pub(super) const fn as_usize(&self) -> Option { match self.ty { ArgumentType::Count(count) => Some(count), ArgumentType::Placeholder { .. } => None, @@ -199,7 +199,7 @@ impl Argument<'_> { /// println!("{f}"); /// ``` #[inline] - pub fn none() -> [Self; 0] { + pub const fn none() -> [Self; 0] { [] } } @@ -216,7 +216,7 @@ impl UnsafeArg { /// See documentation where `UnsafeArg` is required to know when it is safe to /// create and use `UnsafeArg`. #[inline] - pub unsafe fn new() -> Self { + pub const unsafe fn new() -> Self { Self { _private: () } } } diff --git a/library/core/src/future/async_drop.rs b/library/core/src/future/async_drop.rs index ea6d3b800e64d..f1778a4d782af 100644 --- a/library/core/src/future/async_drop.rs +++ b/library/core/src/future/async_drop.rs @@ -133,9 +133,8 @@ pub trait AsyncDrop { } #[lang = "async_destruct"] -#[cfg_attr(bootstrap, rustc_deny_explicit_impl(implement_via_object = false))] -#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)] -#[cfg_attr(not(bootstrap), rustc_do_not_implement_via_object)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] trait AsyncDestruct { type AsyncDestructor: Future; } diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs index 84bbf985e8be4..7a6630c82d0da 100644 --- a/library/core/src/hash/mod.rs +++ b/library/core/src/hash/mod.rs @@ -752,11 +752,8 @@ pub struct BuildHasherDefault(marker::PhantomData H>); impl BuildHasherDefault { /// Creates a new BuildHasherDefault for Hasher `H`. - #[stable(feature = "build_hasher_default_const_new", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable( - feature = "build_hasher_default_const_new", - since = "CURRENT_RUSTC_VERSION" - )] + #[stable(feature = "build_hasher_default_const_new", since = "1.85.0")] + #[rustc_const_stable(feature = "build_hasher_default_const_new", since = "1.85.0")] pub const fn new() -> Self { BuildHasherDefault(marker::PhantomData) } diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 9c054b99a27ac..32b25808542cf 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -468,9 +468,11 @@ pub fn spin_loop() { /// // No assumptions can be made about either operand, so the multiplication is not optimized out. /// let y = black_box(5) * black_box(10); /// ``` +/// +/// During constant evaluation, `black_box` is treated as a no-op. #[inline] #[stable(feature = "bench_black_box", since = "1.66.0")] -#[rustc_const_unstable(feature = "const_black_box", issue = "none")] +#[rustc_const_stable(feature = "const_black_box", since = "1.86.0")] pub const fn black_box(dummy: T) -> T { crate::intrinsics::black_box(dummy) } @@ -597,3 +599,138 @@ pub const fn black_box(dummy: T) -> T { pub const fn must_use(value: T) -> T { value } + +/// Hints to the compiler that a branch condition is likely to be true. +/// Returns the value passed to it. +/// +/// It can be used with `if` or boolean `match` expressions. +/// +/// When used outside of a branch condition, it may still influence a nearby branch, but +/// probably will not have any effect. +/// +/// It can also be applied to parts of expressions, such as `likely(a) && unlikely(b)`, or to +/// compound expressions, such as `likely(a && b)`. When applied to compound expressions, it has +/// the following effect: +/// ```text +/// likely(!a) => !unlikely(a) +/// likely(a && b) => likely(a) && likely(b) +/// likely(a || b) => a || likely(b) +/// ``` +/// +/// See also the function [`cold_path()`] which may be more appropriate for idiomatic Rust code. +/// +/// # Examples +/// +/// ``` +/// #![feature(likely_unlikely)] +/// use core::hint::likely; +/// +/// fn foo(x: i32) { +/// if likely(x > 0) { +/// println!("this branch is likely to be taken"); +/// } else { +/// println!("this branch is unlikely to be taken"); +/// } +/// +/// match likely(x > 0) { +/// true => println!("this branch is likely to be taken"), +/// false => println!("this branch is unlikely to be taken"), +/// } +/// +/// // Use outside of a branch condition may still influence a nearby branch +/// let cond = likely(x != 0); +/// if cond { +/// println!("this branch is likely to be taken"); +/// } +/// } +/// ``` +/// +/// +#[unstable(feature = "likely_unlikely", issue = "136873")] +#[inline(always)] +pub const fn likely(b: bool) -> bool { + crate::intrinsics::likely(b) +} + +/// Hints to the compiler that a branch condition is unlikely to be true. +/// Returns the value passed to it. +/// +/// It can be used with `if` or boolean `match` expressions. +/// +/// When used outside of a branch condition, it may still influence a nearby branch, but +/// probably will not have any effect. +/// +/// It can also be applied to parts of expressions, such as `likely(a) && unlikely(b)`, or to +/// compound expressions, such as `unlikely(a && b)`. When applied to compound expressions, it has +/// the following effect: +/// ```text +/// unlikely(!a) => !likely(a) +/// unlikely(a && b) => a && unlikely(b) +/// unlikely(a || b) => unlikely(a) || unlikely(b) +/// ``` +/// +/// See also the function [`cold_path()`] which may be more appropriate for idiomatic Rust code. +/// +/// # Examples +/// +/// ``` +/// #![feature(likely_unlikely)] +/// use core::hint::unlikely; +/// +/// fn foo(x: i32) { +/// if unlikely(x > 0) { +/// println!("this branch is unlikely to be taken"); +/// } else { +/// println!("this branch is likely to be taken"); +/// } +/// +/// match unlikely(x > 0) { +/// true => println!("this branch is unlikely to be taken"), +/// false => println!("this branch is likely to be taken"), +/// } +/// +/// // Use outside of a branch condition may still influence a nearby branch +/// let cond = unlikely(x != 0); +/// if cond { +/// println!("this branch is likely to be taken"); +/// } +/// } +/// ``` +#[unstable(feature = "likely_unlikely", issue = "136873")] +#[inline(always)] +pub const fn unlikely(b: bool) -> bool { + crate::intrinsics::unlikely(b) +} + +/// Hints to the compiler that given path is cold, i.e., unlikely to be taken. The compiler may +/// choose to optimize paths that are not cold at the expense of paths that are cold. +/// +/// # Examples +/// +/// ``` +/// #![feature(cold_path)] +/// use core::hint::cold_path; +/// +/// fn foo(x: &[i32]) { +/// if let Some(first) = x.get(0) { +/// // this is the fast path +/// } else { +/// // this path is unlikely +/// cold_path(); +/// } +/// } +/// +/// fn bar(x: i32) -> i32 { +/// match x { +/// 1 => 10, +/// 2 => 100, +/// 3 => { cold_path(); 1000 }, // this branch is unlikely +/// _ => { cold_path(); 10000 }, // this is also unlikely +/// } +/// } +/// ``` +#[unstable(feature = "cold_path", issue = "136873")] +#[inline(always)] +pub const fn cold_path() { + crate::intrinsics::cold_path() +} diff --git a/library/core/src/intrinsics/fallback.rs b/library/core/src/intrinsics/fallback.rs index 1779126b180ea..eec5c4d646d07 100644 --- a/library/core/src/intrinsics/fallback.rs +++ b/library/core/src/intrinsics/fallback.rs @@ -8,6 +8,7 @@ #![allow(missing_docs)] #[const_trait] +#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] pub trait CarryingMulAdd: Copy + 'static { type Unsigned: Copy + 'static; fn carrying_mul_add( @@ -109,3 +110,41 @@ impl const CarryingMulAdd for i128 { (low, high) } } + +#[const_trait] +#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] +pub trait DisjointBitOr: Copy + 'static { + /// See [`super::disjoint_bitor`]; we just need the trait indirection to handle + /// different types since calling intrinsics with generics doesn't work. + unsafe fn disjoint_bitor(self, other: Self) -> Self; +} +macro_rules! zero { + (bool) => { + false + }; + ($t:ident) => { + 0 + }; +} +macro_rules! impl_disjoint_bitor { + ($($t:ident,)+) => {$( + #[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] + impl const DisjointBitOr for $t { + #[cfg_attr(miri, track_caller)] + #[inline] + unsafe fn disjoint_bitor(self, other: Self) -> Self { + // Note that the assume here is required for UB detection in Miri! + + // SAFETY: our precondition is that there are no bits in common, + // so this is just telling that to the backend. + unsafe { super::assume((self & other) == zero!($t)) }; + self | other + } + } + )+}; +} +impl_disjoint_bitor! { + bool, + u8, u16, u32, u64, u128, usize, + i8, i16, i32, i64, i128, isize, +} diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs index 834f44c7790d9..55dcf7cd47e97 100644 --- a/library/core/src/intrinsics/mir.rs +++ b/library/core/src/intrinsics/mir.rs @@ -233,7 +233,7 @@ //! //! - Operands implicitly convert to `Use` rvalues. //! - `&`, `&mut`, `addr_of!`, and `addr_of_mut!` all work to create their associated rvalue. -//! - [`Discriminant`] and [`CopyForDeref`] have associated functions. +//! - [`Discriminant`], [`Len`], and [`CopyForDeref`] have associated functions. //! - Unary and binary operations use their normal Rust syntax - `a * b`, `!c`, etc. //! - The binary operation `Offset` can be created via [`Offset`]. //! - Checked binary operations are represented by wrapping the associated binop in [`Checked`]. @@ -401,6 +401,7 @@ define!("mir_storage_dead", fn StorageDead(local: T)); define!("mir_assume", fn Assume(operand: bool)); define!("mir_deinit", fn Deinit(place: T)); define!("mir_checked", fn Checked(binop: T) -> (T, bool)); +define!("mir_len", fn Len(place: T) -> usize); define!( "mir_ptr_metadata", fn PtrMetadata(place: *const P) ->

::Metadata diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index b5c31d824677d..6c9c6d0edc235 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -78,7 +78,11 @@ pub mod simd; use crate::sync::atomic::{self, AtomicBool, AtomicI32, AtomicIsize, AtomicU32, Ordering}; #[stable(feature = "drop_in_place", since = "1.8.0")] -#[rustc_allowed_through_unstable_modules] +#[cfg_attr(bootstrap, rustc_allowed_through_unstable_modules)] +#[cfg_attr( + not(bootstrap), + rustc_allowed_through_unstable_modules = "import this function via `std::ptr` instead" +)] #[deprecated(note = "no longer an intrinsic - use `ptr::drop_in_place` directly", since = "1.52.0")] #[inline] pub unsafe fn drop_in_place(to_drop: *mut T) { @@ -1382,22 +1386,10 @@ pub unsafe fn prefetch_write_instruction(_data: *const T, _locality: i32) { #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] #[rustc_nounwind] -#[cfg(not(bootstrap))] pub fn breakpoint() { unreachable!() } -/// Executes a breakpoint trap, for inspection by a debugger. -/// -/// This intrinsic does not have a stable counterpart. -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -#[rustc_nounwind] -#[cfg(bootstrap)] -pub unsafe fn breakpoint() { - unreachable!() -} - /// Magic intrinsic that derives its meaning from attributes /// attached to the function. /// @@ -1545,7 +1537,7 @@ pub const fn unlikely(b: bool) -> bool { /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// This intrinsic does not have a stable counterpart. +/// The public form of this instrinsic is [`bool::select_unpredictable`]. #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] #[rustc_nounwind] @@ -1909,7 +1901,11 @@ pub const fn forget(_: T) { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_allowed_through_unstable_modules] +#[cfg_attr(bootstrap, rustc_allowed_through_unstable_modules)] +#[cfg_attr( + not(bootstrap), + rustc_allowed_through_unstable_modules = "import this function via `std::mem` instead" +)] #[rustc_const_stable(feature = "const_transmute", since = "1.56.0")] #[rustc_diagnostic_item = "transmute"] #[rustc_nounwind] @@ -3252,6 +3248,26 @@ pub const fn three_way_compare(_lhs: T, _rhss: T) -> crate::cmp::Orderi unimplemented!() } +/// Combine two values which have no bits in common. +/// +/// This allows the backend to implement it as `a + b` *or* `a | b`, +/// depending which is easier to implement on a specific target. +/// +/// # Safety +/// +/// Requires that `(a & b) == 0`, or equivalently that `(a | b) == (a + b)`. +/// +/// Otherwise it's immediate UB. +#[rustc_const_unstable(feature = "disjoint_bitor", issue = "135758")] +#[rustc_nounwind] +#[cfg_attr(not(bootstrap), rustc_intrinsic)] +#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces +#[miri::intrinsic_fallback_is_spec] // the fallbacks all `assume` to tell Miri +pub const unsafe fn disjoint_bitor(a: T, b: T) -> T { + // SAFETY: same preconditions as this function. + unsafe { fallback::DisjointBitOr::disjoint_bitor(a, b) } +} + /// Performs checked integer addition. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -3323,8 +3339,8 @@ pub const fn mul_with_overflow(_x: T, _y: T) -> (T, bool) { #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_const_unstable(feature = "const_carrying_mul_add", issue = "85532")] #[rustc_nounwind] -#[cfg_attr(not(bootstrap), rustc_intrinsic)] -#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)] +#[rustc_intrinsic] +#[miri::intrinsic_fallback_is_spec] pub const fn carrying_mul_add, U>( multiplier: T, multiplicand: T, @@ -3733,6 +3749,7 @@ pub const unsafe fn compare_bytes(_left: *const u8, _right: *const u8, _bytes: u #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_must_be_overridden] +#[rustc_intrinsic_const_stable_indirect] pub const fn black_box(_dummy: T) -> T { unimplemented!() } @@ -3969,21 +3986,6 @@ pub const fn is_val_statically_known(_arg: T) -> bool { false } -#[rustc_nounwind] -#[inline] -#[rustc_intrinsic] -#[rustc_intrinsic_const_stable_indirect] -#[rustc_allow_const_fn_unstable(const_swap_nonoverlapping)] // this is anyway not called since CTFE implements the intrinsic -#[cfg(bootstrap)] -pub const unsafe fn typed_swap(x: *mut T, y: *mut T) { - // SAFETY: The caller provided single non-overlapping items behind - // pointers, so swapping them with `count: 1` is fine. - unsafe { ptr::swap_nonoverlapping(x, y, 1) }; -} - -#[cfg(bootstrap)] -pub use typed_swap as typed_swap_nonoverlapping; - /// Non-overlapping *typed* swap of a single value. /// /// The codegen backends will replace this with a better implementation when @@ -3999,7 +4001,6 @@ pub use typed_swap as typed_swap_nonoverlapping; #[rustc_intrinsic] #[rustc_intrinsic_const_stable_indirect] #[rustc_allow_const_fn_unstable(const_swap_nonoverlapping)] // this is anyway not called since CTFE implements the intrinsic -#[cfg(not(bootstrap))] pub const unsafe fn typed_swap_nonoverlapping(x: *mut T, y: *mut T) { // SAFETY: The caller provided single non-overlapping items behind // pointers, so swapping them with `count: 1` is fine. @@ -4063,6 +4064,52 @@ pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) // Runtime NOP } +/// Returns whether we should perform contract-checking at runtime. +/// +/// This is meant to be similar to the ub_checks intrinsic, in terms +/// of not prematurely commiting at compile-time to whether contract +/// checking is turned on, so that we can specify contracts in libstd +/// and let an end user opt into turning them on. +#[cfg(not(bootstrap))] +#[rustc_const_unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] +#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] +#[inline(always)] +#[rustc_intrinsic] +pub const fn contract_checks() -> bool { + // FIXME: should this be `false` or `cfg!(contract_checks)`? + + // cfg!(contract_checks) + false +} + +/// Check if the pre-condition `cond` has been met. +/// +/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition +/// returns false. +#[cfg(not(bootstrap))] +#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] +#[lang = "contract_check_requires"] +#[rustc_intrinsic] +pub fn contract_check_requires bool>(cond: C) { + if contract_checks() && !cond() { + // Emit no unwind panic in case this was a safety requirement. + crate::panicking::panic_nounwind("failed requires check"); + } +} + +/// Check if the post-condition `cond` has been met. +/// +/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition +/// returns false. +#[cfg(not(bootstrap))] +#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)] +#[rustc_intrinsic] +pub fn contract_check_ensures<'a, Ret, C: Fn(&'a Ret) -> bool>(ret: &'a Ret, cond: C) { + if contract_checks() && !cond(ret) { + crate::panicking::panic_nounwind("failed ensures check"); + } +} + /// The intrinsic will return the size stored in that vtable. /// /// # Safety @@ -4353,7 +4400,11 @@ pub const fn ptr_metadata + ?Sized, M>(_ptr: *cons /// [`Vec::append`]: ../../std/vec/struct.Vec.html#method.append #[doc(alias = "memcpy")] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_allowed_through_unstable_modules] +#[cfg_attr(bootstrap, rustc_allowed_through_unstable_modules)] +#[cfg_attr( + not(bootstrap), + rustc_allowed_through_unstable_modules = "import this function via `std::mem` instead" +)] #[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces @@ -4457,7 +4508,11 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us /// ``` #[doc(alias = "memmove")] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_allowed_through_unstable_modules] +#[cfg_attr(bootstrap, rustc_allowed_through_unstable_modules)] +#[cfg_attr( + not(bootstrap), + rustc_allowed_through_unstable_modules = "import this function via `std::mem` instead" +)] #[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces @@ -4540,7 +4595,11 @@ pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { /// ``` #[doc(alias = "memset")] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_allowed_through_unstable_modules] +#[cfg_attr(bootstrap, rustc_allowed_through_unstable_modules)] +#[cfg_attr( + not(bootstrap), + rustc_allowed_through_unstable_modules = "import this function via `std::mem` instead" +)] #[rustc_const_stable(feature = "const_ptr_write", since = "1.83.0")] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces @@ -4796,7 +4855,7 @@ pub const unsafe fn copysignf128(_x: f128, _y: f128) -> f128 { #[cfg(miri)] #[rustc_allow_const_fn_unstable(const_eval_select)] pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize) { - extern "Rust" { + unsafe extern "Rust" { /// Miri-provided extern function to promise that a given pointer is properly aligned for /// "symbolic" alignment checks. Will fail if the pointer is not actually aligned or `align` is /// not a power of two. Has no effect when alignment checks are concrete (which is the default). diff --git a/library/core/src/intrinsics/simd.rs b/library/core/src/intrinsics/simd.rs index f80a60d471c04..e59d3aff37999 100644 --- a/library/core/src/intrinsics/simd.rs +++ b/library/core/src/intrinsics/simd.rs @@ -2,670 +2,939 @@ //! //! In this module, a "vector" is any `repr(simd)` type. -extern "rust-intrinsic" { - /// Inserts an element into a vector, returning the updated vector. - /// - /// `T` must be a vector with element type `U`. - /// - /// # Safety - /// - /// `idx` must be in-bounds of the vector. - #[rustc_nounwind] - pub fn simd_insert(x: T, idx: u32, val: U) -> T; - - /// Extracts an element from a vector. - /// - /// `T` must be a vector with element type `U`. - /// - /// # Safety - /// - /// `idx` must be in-bounds of the vector. - #[rustc_nounwind] - pub fn simd_extract(x: T, idx: u32) -> U; - - /// Adds two simd vectors elementwise. - /// - /// `T` must be a vector of integer or floating point primitive types. - #[rustc_nounwind] - pub fn simd_add(x: T, y: T) -> T; - - /// Subtracts `rhs` from `lhs` elementwise. - /// - /// `T` must be a vector of integer or floating point primitive types. - #[rustc_nounwind] - pub fn simd_sub(lhs: T, rhs: T) -> T; - - /// Multiplies two simd vectors elementwise. - /// - /// `T` must be a vector of integer or floating point primitive types. - #[rustc_nounwind] - pub fn simd_mul(x: T, y: T) -> T; - - /// Divides `lhs` by `rhs` elementwise. - /// - /// `T` must be a vector of integer or floating point primitive types. - /// - /// # Safety - /// For integers, `rhs` must not contain any zero elements. - /// Additionally for signed integers, `::MIN / -1` is undefined behavior. - #[rustc_nounwind] - pub fn simd_div(lhs: T, rhs: T) -> T; - - /// Returns remainder of two vectors elementwise. - /// - /// `T` must be a vector of integer or floating point primitive types. - /// - /// # Safety - /// For integers, `rhs` must not contain any zero elements. - /// Additionally for signed integers, `::MIN / -1` is undefined behavior. - #[rustc_nounwind] - pub fn simd_rem(lhs: T, rhs: T) -> T; - - /// Shifts vector left elementwise, with UB on overflow. - /// - /// Shifts `lhs` left by `rhs`, shifting in sign bits for signed types. - /// - /// `T` must be a vector of integer primitive types. - /// - /// # Safety - /// - /// Each element of `rhs` must be less than `::BITS`. - #[rustc_nounwind] - pub fn simd_shl(lhs: T, rhs: T) -> T; - - /// Shifts vector right elementwise, with UB on overflow. - /// - /// `T` must be a vector of integer primitive types. - /// - /// Shifts `lhs` right by `rhs`, shifting in sign bits for signed types. - /// - /// # Safety - /// - /// Each element of `rhs` must be less than `::BITS`. - #[rustc_nounwind] - pub fn simd_shr(lhs: T, rhs: T) -> T; - - /// "Ands" vectors elementwise. - /// - /// `T` must be a vector of integer primitive types. - #[rustc_nounwind] - pub fn simd_and(x: T, y: T) -> T; - - /// "Ors" vectors elementwise. - /// - /// `T` must be a vector of integer primitive types. - #[rustc_nounwind] - pub fn simd_or(x: T, y: T) -> T; - - /// "Exclusive ors" vectors elementwise. - /// - /// `T` must be a vector of integer primitive types. - #[rustc_nounwind] - pub fn simd_xor(x: T, y: T) -> T; - - /// Numerically casts a vector, elementwise. - /// - /// `T` and `U` must be vectors of integer or floating point primitive types, and must have the - /// same length. - /// - /// When casting floats to integers, the result is truncated. Out-of-bounds result lead to UB. - /// When casting integers to floats, the result is rounded. - /// Otherwise, truncates or extends the value, maintaining the sign for signed integers. - /// - /// # Safety - /// Casting from integer types is always safe. - /// Casting between two float types is also always safe. - /// - /// Casting floats to integers truncates, following the same rules as `to_int_unchecked`. - /// Specifically, each element must: - /// * Not be `NaN` - /// * Not be infinite - /// * Be representable in the return type, after truncating off its fractional part - #[rustc_nounwind] - pub fn simd_cast(x: T) -> U; - - /// Numerically casts a vector, elementwise. - /// - /// `T` and `U` be a vectors of integer or floating point primitive types, and must have the - /// same length. - /// - /// Like `simd_cast`, but saturates float-to-integer conversions (NaN becomes 0). - /// This matches regular `as` and is always safe. - /// - /// When casting floats to integers, the result is truncated. - /// When casting integers to floats, the result is rounded. - /// Otherwise, truncates or extends the value, maintaining the sign for signed integers. - #[rustc_nounwind] - pub fn simd_as(x: T) -> U; - - /// Negates a vector elementwise. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// Rust panics for `-::Min` due to overflow, but it is not UB with this intrinsic. - #[rustc_nounwind] - pub fn simd_neg(x: T) -> T; - - /// Returns absolute value of a vector, elementwise. - /// - /// `T` must be a vector of floating-point primitive types. - #[rustc_nounwind] - pub fn simd_fabs(x: T) -> T; - - /// Returns the minimum of two vectors, elementwise. - /// - /// `T` must be a vector of floating-point primitive types. - /// - /// Follows IEEE-754 `minNum` semantics. - #[rustc_nounwind] - pub fn simd_fmin(x: T, y: T) -> T; - - /// Returns the maximum of two vectors, elementwise. - /// - /// `T` must be a vector of floating-point primitive types. - /// - /// Follows IEEE-754 `maxNum` semantics. - #[rustc_nounwind] - pub fn simd_fmax(x: T, y: T) -> T; - - /// Tests elementwise equality of two vectors. - /// - /// `T` must be a vector of floating-point primitive types. - /// - /// `U` must be a vector of integers with the same number of elements and element size as `T`. - /// - /// Returns `0` for false and `!0` for true. - #[rustc_nounwind] - pub fn simd_eq(x: T, y: T) -> U; - - /// Tests elementwise inequality equality of two vectors. - /// - /// `T` must be a vector of floating-point primitive types. - /// - /// `U` must be a vector of integers with the same number of elements and element size as `T`. - /// - /// Returns `0` for false and `!0` for true. - #[rustc_nounwind] - pub fn simd_ne(x: T, y: T) -> U; - - /// Tests if `x` is less than `y`, elementwise. - /// - /// `T` must be a vector of floating-point primitive types. - /// - /// `U` must be a vector of integers with the same number of elements and element size as `T`. - /// - /// Returns `0` for false and `!0` for true. - #[rustc_nounwind] - pub fn simd_lt(x: T, y: T) -> U; - - /// Tests if `x` is less than or equal to `y`, elementwise. - /// - /// `T` must be a vector of floating-point primitive types. - /// - /// `U` must be a vector of integers with the same number of elements and element size as `T`. - /// - /// Returns `0` for false and `!0` for true. - #[rustc_nounwind] - pub fn simd_le(x: T, y: T) -> U; - - /// Tests if `x` is greater than `y`, elementwise. - /// - /// `T` must be a vector of floating-point primitive types. - /// - /// `U` must be a vector of integers with the same number of elements and element size as `T`. - /// - /// Returns `0` for false and `!0` for true. - #[rustc_nounwind] - pub fn simd_gt(x: T, y: T) -> U; - - /// Tests if `x` is greater than or equal to `y`, elementwise. - /// - /// `T` must be a vector of floating-point primitive types. - /// - /// `U` must be a vector of integers with the same number of elements and element size as `T`. - /// - /// Returns `0` for false and `!0` for true. - #[rustc_nounwind] - pub fn simd_ge(x: T, y: T) -> U; - - /// Shuffles two vectors by const indices. - /// - /// `T` must be a vector. - /// - /// `U` must be a **const** vector of `u32`s. This means it must either refer to a named - /// const or be given as an inline const expression (`const { ... }`). - /// - /// `V` must be a vector with the same element type as `T` and the same length as `U`. - /// - /// Returns a new vector such that element `i` is selected from `xy[idx[i]]`, where `xy` - /// is the concatenation of `x` and `y`. It is a compile-time error if `idx[i]` is out-of-bounds - /// of `xy`. - #[rustc_nounwind] - pub fn simd_shuffle(x: T, y: T, idx: U) -> V; - - /// Reads a vector of pointers. - /// - /// `T` must be a vector. - /// - /// `U` must be a vector of pointers to the element type of `T`, with the same length as `T`. - /// - /// `V` must be a vector of integers with the same length as `T` (but any element size). - /// - /// For each pointer in `ptr`, if the corresponding value in `mask` is `!0`, read the pointer. - /// Otherwise if the corresponding value in `mask` is `0`, return the corresponding value from - /// `val`. - /// - /// # Safety - /// Unmasked values in `T` must be readable as if by `::read` (e.g. aligned to the element - /// type). - /// - /// `mask` must only contain `0` or `!0` values. - #[rustc_nounwind] - pub fn simd_gather(val: T, ptr: U, mask: V) -> T; - - /// Writes to a vector of pointers. - /// - /// `T` must be a vector. - /// - /// `U` must be a vector of pointers to the element type of `T`, with the same length as `T`. - /// - /// `V` must be a vector of integers with the same length as `T` (but any element size). - /// - /// For each pointer in `ptr`, if the corresponding value in `mask` is `!0`, write the - /// corresponding value in `val` to the pointer. - /// Otherwise if the corresponding value in `mask` is `0`, do nothing. - /// - /// The stores happen in left-to-right order. - /// (This is relevant in case two of the stores overlap.) - /// - /// # Safety - /// Unmasked values in `T` must be writeable as if by `::write` (e.g. aligned to the element - /// type). - /// - /// `mask` must only contain `0` or `!0` values. - #[rustc_nounwind] - pub fn simd_scatter(val: T, ptr: U, mask: V); - - /// Reads a vector of pointers. - /// - /// `T` must be a vector. - /// - /// `U` must be a pointer to the element type of `T` - /// - /// `V` must be a vector of integers with the same length as `T` (but any element size). - /// - /// For each element, if the corresponding value in `mask` is `!0`, read the corresponding - /// pointer offset from `ptr`. - /// The first element is loaded from `ptr`, the second from `ptr.wrapping_offset(1)` and so on. - /// Otherwise if the corresponding value in `mask` is `0`, return the corresponding value from - /// `val`. - /// - /// # Safety - /// Unmasked values in `T` must be readable as if by `::read` (e.g. aligned to the element - /// type). - /// - /// `mask` must only contain `0` or `!0` values. - #[rustc_nounwind] - pub fn simd_masked_load(mask: V, ptr: U, val: T) -> T; - - /// Writes to a vector of pointers. - /// - /// `T` must be a vector. - /// - /// `U` must be a pointer to the element type of `T` - /// - /// `V` must be a vector of integers with the same length as `T` (but any element size). - /// - /// For each element, if the corresponding value in `mask` is `!0`, write the corresponding - /// value in `val` to the pointer offset from `ptr`. - /// The first element is written to `ptr`, the second to `ptr.wrapping_offset(1)` and so on. - /// Otherwise if the corresponding value in `mask` is `0`, do nothing. - /// - /// # Safety - /// Unmasked values in `T` must be writeable as if by `::write` (e.g. aligned to the element - /// type). - /// - /// `mask` must only contain `0` or `!0` values. - #[rustc_nounwind] - pub fn simd_masked_store(mask: V, ptr: U, val: T); - - /// Adds two simd vectors elementwise, with saturation. - /// - /// `T` must be a vector of integer primitive types. - #[rustc_nounwind] - pub fn simd_saturating_add(x: T, y: T) -> T; - - /// Subtracts two simd vectors elementwise, with saturation. - /// - /// `T` must be a vector of integer primitive types. - /// - /// Subtract `rhs` from `lhs`. - #[rustc_nounwind] - pub fn simd_saturating_sub(lhs: T, rhs: T) -> T; - - /// Adds elements within a vector from left to right. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - /// - /// Starting with the value `y`, add the elements of `x` and accumulate. - #[rustc_nounwind] - pub fn simd_reduce_add_ordered(x: T, y: U) -> U; - - /// Adds elements within a vector in arbitrary order. May also be re-associated with - /// unordered additions on the inputs/outputs. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - #[rustc_nounwind] - pub fn simd_reduce_add_unordered(x: T) -> U; - - /// Multiplies elements within a vector from left to right. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - /// - /// Starting with the value `y`, multiply the elements of `x` and accumulate. - #[rustc_nounwind] - pub fn simd_reduce_mul_ordered(x: T, y: U) -> U; - - /// Multiplies elements within a vector in arbitrary order. May also be re-associated with - /// unordered additions on the inputs/outputs. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - #[rustc_nounwind] - pub fn simd_reduce_mul_unordered(x: T) -> U; - - /// Checks if all mask values are true. - /// - /// `T` must be a vector of integer primitive types. - /// - /// # Safety - /// `x` must contain only `0` or `!0`. - #[rustc_nounwind] - pub fn simd_reduce_all(x: T) -> bool; - - /// Checks if any mask value is true. - /// - /// `T` must be a vector of integer primitive types. - /// - /// # Safety - /// `x` must contain only `0` or `!0`. - #[rustc_nounwind] - pub fn simd_reduce_any(x: T) -> bool; - - /// Returns the maximum element of a vector. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - /// - /// For floating-point values, uses IEEE-754 `maxNum`. - #[rustc_nounwind] - pub fn simd_reduce_max(x: T) -> U; - - /// Returns the minimum element of a vector. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - /// - /// For floating-point values, uses IEEE-754 `minNum`. - #[rustc_nounwind] - pub fn simd_reduce_min(x: T) -> U; - - /// Logical "ands" all elements together. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - #[rustc_nounwind] - pub fn simd_reduce_and(x: T) -> U; - - /// Logical "ors" all elements together. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - #[rustc_nounwind] - pub fn simd_reduce_or(x: T) -> U; - - /// Logical "exclusive ors" all elements together. - /// - /// `T` must be a vector of integer or floating-point primitive types. - /// - /// `U` must be the element type of `T`. - #[rustc_nounwind] - pub fn simd_reduce_xor(x: T) -> U; - - /// Truncates an integer vector to a bitmask. - /// - /// `T` must be an integer vector. - /// - /// `U` must be either the smallest unsigned integer with at least as many bits as the length - /// of `T`, or the smallest array of `u8` with at least as many bits as the length of `T`. - /// - /// Each element is truncated to a single bit and packed into the result. - /// - /// No matter whether the output is an array or an unsigned integer, it is treated as a single - /// contiguous list of bits. The bitmask is always packed on the least-significant side of the - /// output, and padded with 0s in the most-significant bits. The order of the bits depends on - /// endianness: - /// - /// * On little endian, the least significant bit corresponds to the first vector element. - /// * On big endian, the least significant bit corresponds to the last vector element. - /// - /// For example, `[!0, 0, !0, !0]` packs to - /// - `0b1101u8` or `[0b1101]` on little endian, and - /// - `0b1011u8` or `[0b1011]` on big endian. - /// - /// To consider a larger example, - /// `[!0, 0, 0, 0, 0, 0, 0, 0, !0, !0, 0, 0, 0, 0, !0, 0]` packs to - /// - `0b0100001100000001u16` or `[0b00000001, 0b01000011]` on little endian, and - /// - `0b1000000011000010u16` or `[0b10000000, 0b11000010]` on big endian. - /// - /// And finally, a non-power-of-2 example with multiple bytes: - /// `[!0, !0, 0, !0, 0, 0, !0, 0, !0, 0]` packs to - /// - `0b0101001011u16` or `[0b01001011, 0b01]` on little endian, and - /// - `0b1101001010u16` or `[0b11, 0b01001010]` on big endian. - /// - /// # Safety - /// `x` must contain only `0` and `!0`. - #[rustc_nounwind] - pub fn simd_bitmask(x: T) -> U; - - /// Selects elements from a mask. - /// - /// `M` must be an integer vector. - /// - /// `T` must be a vector with the same number of elements as `M`. - /// - /// For each element, if the corresponding value in `mask` is `!0`, select the element from - /// `if_true`. If the corresponding value in `mask` is `0`, select the element from - /// `if_false`. - /// - /// # Safety - /// `mask` must only contain `0` and `!0`. - #[rustc_nounwind] - pub fn simd_select(mask: M, if_true: T, if_false: T) -> T; - - /// Selects elements from a bitmask. - /// - /// `M` must be an unsigned integer or array of `u8`, matching `simd_bitmask`. - /// - /// `T` must be a vector. - /// - /// For each element, if the bit in `mask` is `1`, select the element from - /// `if_true`. If the corresponding bit in `mask` is `0`, select the element from - /// `if_false`. - /// - /// The bitmask bit order matches `simd_bitmask`. - /// - /// # Safety - /// Padding bits must be all zero. - #[rustc_nounwind] - pub fn simd_select_bitmask(m: M, yes: T, no: T) -> T; - - /// Calculates the offset from a pointer vector elementwise, potentially - /// wrapping. - /// - /// `T` must be a vector of pointers. - /// - /// `U` must be a vector of `isize` or `usize` with the same number of elements as `T`. - /// - /// Operates as if by `::wrapping_offset`. - #[rustc_nounwind] - pub fn simd_arith_offset(ptr: T, offset: U) -> T; - - /// Casts a vector of pointers. - /// - /// `T` and `U` must be vectors of pointers with the same number of elements. - #[rustc_nounwind] - pub fn simd_cast_ptr(ptr: T) -> U; - - /// Exposes a vector of pointers as a vector of addresses. - /// - /// `T` must be a vector of pointers. - /// - /// `U` must be a vector of `usize` with the same length as `T`. - #[rustc_nounwind] - pub fn simd_expose_provenance(ptr: T) -> U; - - /// Creates a vector of pointers from a vector of addresses. - /// - /// `T` must be a vector of `usize`. - /// - /// `U` must be a vector of pointers, with the same length as `T`. - #[rustc_nounwind] - pub fn simd_with_exposed_provenance(addr: T) -> U; - - /// Swaps bytes of each element. - /// - /// `T` must be a vector of integers. - #[rustc_nounwind] - pub fn simd_bswap(x: T) -> T; - - /// Reverses bits of each element. - /// - /// `T` must be a vector of integers. - #[rustc_nounwind] - pub fn simd_bitreverse(x: T) -> T; - - /// Counts the leading zeros of each element. - /// - /// `T` must be a vector of integers. - #[rustc_nounwind] - pub fn simd_ctlz(x: T) -> T; - - /// Counts the number of ones in each element. - /// - /// `T` must be a vector of integers. - #[rustc_nounwind] - pub fn simd_ctpop(x: T) -> T; - - /// Counts the trailing zeros of each element. - /// - /// `T` must be a vector of integers. - #[rustc_nounwind] - pub fn simd_cttz(x: T) -> T; - - /// Rounds up each element to the next highest integer-valued float. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_ceil(x: T) -> T; - - /// Rounds down each element to the next lowest integer-valued float. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_floor(x: T) -> T; - - /// Rounds each element to the closest integer-valued float. - /// Ties are resolved by rounding away from 0. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_round(x: T) -> T; - - /// Returns the integer part of each element as an integer-valued float. - /// In other words, non-integer values are truncated towards zero. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_trunc(x: T) -> T; - - /// Takes the square root of each element. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_fsqrt(x: T) -> T; - - /// Computes `(x*y) + z` for each element, but without any intermediate rounding. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_fma(x: T, y: T, z: T) -> T; - - /// Computes `(x*y) + z` for each element, non-deterministically executing either - /// a fused multiply-add or two operations with rounding of the intermediate result. - /// - /// The operation is fused if the code generator determines that target instruction - /// set has support for a fused operation, and that the fused operation is more efficient - /// than the equivalent, separate pair of mul and add instructions. It is unspecified - /// whether or not a fused operation is selected, and that may depend on optimization - /// level and context, for example. It may even be the case that some SIMD lanes get fused - /// and others do not. - /// - /// `T` must be a vector of floats. - #[cfg(not(bootstrap))] - #[rustc_nounwind] - pub fn simd_relaxed_fma(x: T, y: T, z: T) -> T; - - // Computes the sine of each element. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_fsin(a: T) -> T; - - // Computes the cosine of each element. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_fcos(a: T) -> T; - - // Computes the exponential function of each element. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_fexp(a: T) -> T; - - // Computes 2 raised to the power of each element. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_fexp2(a: T) -> T; - - // Computes the base 10 logarithm of each element. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_flog10(a: T) -> T; - - // Computes the base 2 logarithm of each element. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_flog2(a: T) -> T; - - // Computes the natural logarithm of each element. - /// - /// `T` must be a vector of floats. - #[rustc_nounwind] - pub fn simd_flog(a: T) -> T; +/// Inserts an element into a vector, returning the updated vector. +/// +/// `T` must be a vector with element type `U`. +/// +/// # Safety +/// +/// `idx` must be in-bounds of the vector. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_insert(_x: T, _idx: u32, _val: U) -> T { + unreachable!() +} + +/// Extracts an element from a vector. +/// +/// `T` must be a vector with element type `U`. +/// +/// # Safety +/// +/// `idx` must be in-bounds of the vector. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_extract(_x: T, _idx: u32) -> U { + unreachable!() +} + +/// Adds two simd vectors elementwise. +/// +/// `T` must be a vector of integer or floating point primitive types. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_add(_x: T, _y: T) -> T { + unreachable!() +} + +/// Subtracts `rhs` from `lhs` elementwise. +/// +/// `T` must be a vector of integer or floating point primitive types. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_sub(_lhs: T, _rhs: T) -> T { + unreachable!() +} + +/// Multiplies two simd vectors elementwise. +/// +/// `T` must be a vector of integer or floating point primitive types. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_mul(_x: T, _y: T) -> T { + unreachable!() +} + +/// Divides `lhs` by `rhs` elementwise. +/// +/// `T` must be a vector of integer or floating point primitive types. +/// +/// # Safety +/// For integers, `rhs` must not contain any zero elements. +/// Additionally for signed integers, `::MIN / -1` is undefined behavior. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_div(_lhs: T, _rhs: T) -> T { + unreachable!() +} + +/// Returns remainder of two vectors elementwise. +/// +/// `T` must be a vector of integer or floating point primitive types. +/// +/// # Safety +/// For integers, `rhs` must not contain any zero elements. +/// Additionally for signed integers, `::MIN / -1` is undefined behavior. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_rem(_lhs: T, _rhs: T) -> T { + unreachable!() +} + +/// Shifts vector left elementwise, with UB on overflow. +/// +/// Shifts `lhs` left by `rhs`, shifting in sign bits for signed types. +/// +/// `T` must be a vector of integer primitive types. +/// +/// # Safety +/// +/// Each element of `rhs` must be less than `::BITS`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_shl(_lhs: T, _rhs: T) -> T { + unreachable!() +} + +/// Shifts vector right elementwise, with UB on overflow. +/// +/// `T` must be a vector of integer primitive types. +/// +/// Shifts `lhs` right by `rhs`, shifting in sign bits for signed types. +/// +/// # Safety +/// +/// Each element of `rhs` must be less than `::BITS`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_shr(_lhs: T, _rhs: T) -> T { + unreachable!() +} + +/// "Ands" vectors elementwise. +/// +/// `T` must be a vector of integer primitive types. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_and(_x: T, _y: T) -> T { + unreachable!() +} + +/// "Ors" vectors elementwise. +/// +/// `T` must be a vector of integer primitive types. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_or(_x: T, _y: T) -> T { + unreachable!() +} + +/// "Exclusive ors" vectors elementwise. +/// +/// `T` must be a vector of integer primitive types. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_xor(_x: T, _y: T) -> T { + unreachable!() +} + +/// Numerically casts a vector, elementwise. +/// +/// `T` and `U` must be vectors of integer or floating point primitive types, and must have the +/// same length. +/// +/// When casting floats to integers, the result is truncated. Out-of-bounds result lead to UB. +/// When casting integers to floats, the result is rounded. +/// Otherwise, truncates or extends the value, maintaining the sign for signed integers. +/// +/// # Safety +/// Casting from integer types is always safe. +/// Casting between two float types is also always safe. +/// +/// Casting floats to integers truncates, following the same rules as `to_int_unchecked`. +/// Specifically, each element must: +/// * Not be `NaN` +/// * Not be infinite +/// * Be representable in the return type, after truncating off its fractional part +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_cast(_x: T) -> U { + unreachable!() +} + +/// Numerically casts a vector, elementwise. +/// +/// `T` and `U` be a vectors of integer or floating point primitive types, and must have the +/// same length. +/// +/// Like `simd_cast`, but saturates float-to-integer conversions (NaN becomes 0). +/// This matches regular `as` and is always safe. +/// +/// When casting floats to integers, the result is truncated. +/// When casting integers to floats, the result is rounded. +/// Otherwise, truncates or extends the value, maintaining the sign for signed integers. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_as(_x: T) -> U { + unreachable!() +} + +/// Negates a vector elementwise. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// Rust panics for `-::Min` due to overflow, but it is not UB with this intrinsic. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_neg(_x: T) -> T { + unreachable!() +} + +/// Returns absolute value of a vector, elementwise. +/// +/// `T` must be a vector of floating-point primitive types. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fabs(_x: T) -> T { + unreachable!() +} + +/// Returns the minimum of two vectors, elementwise. +/// +/// `T` must be a vector of floating-point primitive types. +/// +/// Follows IEEE-754 `minNum` semantics. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fmin(_x: T, _y: T) -> T { + unreachable!() +} + +/// Returns the maximum of two vectors, elementwise. +/// +/// `T` must be a vector of floating-point primitive types. +/// +/// Follows IEEE-754 `maxNum` semantics. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fmax(_x: T, _y: T) -> T { + unreachable!() +} + +/// Tests elementwise equality of two vectors. +/// +/// `T` must be a vector of floating-point primitive types. +/// +/// `U` must be a vector of integers with the same number of elements and element size as `T`. +/// +/// Returns `0` for false and `!0` for true. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_eq(_x: T, _y: T) -> U { + unreachable!() +} + +/// Tests elementwise inequality equality of two vectors. +/// +/// `T` must be a vector of floating-point primitive types. +/// +/// `U` must be a vector of integers with the same number of elements and element size as `T`. +/// +/// Returns `0` for false and `!0` for true. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_ne(_x: T, _y: T) -> U { + unreachable!() +} + +/// Tests if `x` is less than `y`, elementwise. +/// +/// `T` must be a vector of floating-point primitive types. +/// +/// `U` must be a vector of integers with the same number of elements and element size as `T`. +/// +/// Returns `0` for false and `!0` for true. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_lt(_x: T, _y: T) -> U { + unreachable!() +} + +/// Tests if `x` is less than or equal to `y`, elementwise. +/// +/// `T` must be a vector of floating-point primitive types. +/// +/// `U` must be a vector of integers with the same number of elements and element size as `T`. +/// +/// Returns `0` for false and `!0` for true. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_le(_x: T, _y: T) -> U { + unreachable!() +} + +/// Tests if `x` is greater than `y`, elementwise. +/// +/// `T` must be a vector of floating-point primitive types. +/// +/// `U` must be a vector of integers with the same number of elements and element size as `T`. +/// +/// Returns `0` for false and `!0` for true. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_gt(_x: T, _y: T) -> U { + unreachable!() +} + +/// Tests if `x` is greater than or equal to `y`, elementwise. +/// +/// `T` must be a vector of floating-point primitive types. +/// +/// `U` must be a vector of integers with the same number of elements and element size as `T`. +/// +/// Returns `0` for false and `!0` for true. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_ge(_x: T, _y: T) -> U { + unreachable!() +} + +/// Shuffles two vectors by const indices. +/// +/// `T` must be a vector. +/// +/// `U` must be a **const** vector of `u32`s. This means it must either refer to a named +/// const or be given as an inline const expression (`const { ... }`). +/// +/// `V` must be a vector with the same element type as `T` and the same length as `U`. +/// +/// Returns a new vector such that element `i` is selected from `xy[idx[i]]`, where `xy` +/// is the concatenation of `x` and `y`. It is a compile-time error if `idx[i]` is out-of-bounds +/// of `xy`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_shuffle(_x: T, _y: T, _idx: U) -> V { + unreachable!() +} + +/// Reads a vector of pointers. +/// +/// `T` must be a vector. +/// +/// `U` must be a vector of pointers to the element type of `T`, with the same length as `T`. +/// +/// `V` must be a vector of integers with the same length as `T` (but any element size). +/// +/// For each pointer in `ptr`, if the corresponding value in `mask` is `!0`, read the pointer. +/// Otherwise if the corresponding value in `mask` is `0`, return the corresponding value from +/// `val`. +/// +/// # Safety +/// Unmasked values in `T` must be readable as if by `::read` (e.g. aligned to the element +/// type). +/// +/// `mask` must only contain `0` or `!0` values. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_gather(_val: T, _ptr: U, _mask: V) -> T { + unreachable!() +} + +/// Writes to a vector of pointers. +/// +/// `T` must be a vector. +/// +/// `U` must be a vector of pointers to the element type of `T`, with the same length as `T`. +/// +/// `V` must be a vector of integers with the same length as `T` (but any element size). +/// +/// For each pointer in `ptr`, if the corresponding value in `mask` is `!0`, write the +/// corresponding value in `val` to the pointer. +/// Otherwise if the corresponding value in `mask` is `0`, do nothing. +/// +/// The stores happen in left-to-right order. +/// (This is relevant in case two of the stores overlap.) +/// +/// # Safety +/// Unmasked values in `T` must be writeable as if by `::write` (e.g. aligned to the element +/// type). +/// +/// `mask` must only contain `0` or `!0` values. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_scatter(_val: T, _ptr: U, _mask: V) { + unreachable!() +} + +/// Reads a vector of pointers. +/// +/// `T` must be a vector. +/// +/// `U` must be a pointer to the element type of `T` +/// +/// `V` must be a vector of integers with the same length as `T` (but any element size). +/// +/// For each element, if the corresponding value in `mask` is `!0`, read the corresponding +/// pointer offset from `ptr`. +/// The first element is loaded from `ptr`, the second from `ptr.wrapping_offset(1)` and so on. +/// Otherwise if the corresponding value in `mask` is `0`, return the corresponding value from +/// `val`. +/// +/// # Safety +/// Unmasked values in `T` must be readable as if by `::read` (e.g. aligned to the element +/// type). +/// +/// `mask` must only contain `0` or `!0` values. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_masked_load(_mask: V, _ptr: U, _val: T) -> T { + unreachable!() +} + +/// Writes to a vector of pointers. +/// +/// `T` must be a vector. +/// +/// `U` must be a pointer to the element type of `T` +/// +/// `V` must be a vector of integers with the same length as `T` (but any element size). +/// +/// For each element, if the corresponding value in `mask` is `!0`, write the corresponding +/// value in `val` to the pointer offset from `ptr`. +/// The first element is written to `ptr`, the second to `ptr.wrapping_offset(1)` and so on. +/// Otherwise if the corresponding value in `mask` is `0`, do nothing. +/// +/// # Safety +/// Unmasked values in `T` must be writeable as if by `::write` (e.g. aligned to the element +/// type). +/// +/// `mask` must only contain `0` or `!0` values. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_masked_store(_mask: V, _ptr: U, _val: T) { + unreachable!() +} + +/// Adds two simd vectors elementwise, with saturation. +/// +/// `T` must be a vector of integer primitive types. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_saturating_add(_x: T, _y: T) -> T { + unreachable!() +} + +/// Subtracts two simd vectors elementwise, with saturation. +/// +/// `T` must be a vector of integer primitive types. +/// +/// Subtract `rhs` from `lhs`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_saturating_sub(_lhs: T, _rhs: T) -> T { + unreachable!() +} + +/// Adds elements within a vector from left to right. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +/// +/// Starting with the value `y`, add the elements of `x` and accumulate. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_add_ordered(_x: T, _y: U) -> U { + unreachable!() +} + +/// Adds elements within a vector in arbitrary order. May also be re-associated with +/// unordered additions on the inputs/outputs. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_add_unordered(_x: T) -> U { + unreachable!() +} + +/// Multiplies elements within a vector from left to right. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +/// +/// Starting with the value `y`, multiply the elements of `x` and accumulate. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_mul_ordered(_x: T, _y: U) -> U { + unreachable!() +} + +/// Multiplies elements within a vector in arbitrary order. May also be re-associated with +/// unordered additions on the inputs/outputs. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_mul_unordered(_x: T) -> U { + unreachable!() +} + +/// Checks if all mask values are true. +/// +/// `T` must be a vector of integer primitive types. +/// +/// # Safety +/// `x` must contain only `0` or `!0`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_all(_x: T) -> bool { + unreachable!() +} + +/// Checks if any mask value is true. +/// +/// `T` must be a vector of integer primitive types. +/// +/// # Safety +/// `x` must contain only `0` or `!0`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_any(_x: T) -> bool { + unreachable!() +} + +/// Returns the maximum element of a vector. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +/// +/// For floating-point values, uses IEEE-754 `maxNum`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_max(_x: T) -> U { + unreachable!() +} + +/// Returns the minimum element of a vector. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +/// +/// For floating-point values, uses IEEE-754 `minNum`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_min(_x: T) -> U { + unreachable!() +} + +/// Logical "ands" all elements together. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_and(_x: T) -> U { + unreachable!() +} + +/// Logical "ors" all elements together. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_or(_x: T) -> U { + unreachable!() +} + +/// Logical "exclusive ors" all elements together. +/// +/// `T` must be a vector of integer or floating-point primitive types. +/// +/// `U` must be the element type of `T`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_reduce_xor(_x: T) -> U { + unreachable!() +} + +/// Truncates an integer vector to a bitmask. +/// +/// `T` must be an integer vector. +/// +/// `U` must be either the smallest unsigned integer with at least as many bits as the length +/// of `T`, or the smallest array of `u8` with at least as many bits as the length of `T`. +/// +/// Each element is truncated to a single bit and packed into the result. +/// +/// No matter whether the output is an array or an unsigned integer, it is treated as a single +/// contiguous list of bits. The bitmask is always packed on the least-significant side of the +/// output, and padded with 0s in the most-significant bits. The order of the bits depends on +/// endianness: +/// +/// * On little endian, the least significant bit corresponds to the first vector element. +/// * On big endian, the least significant bit corresponds to the last vector element. +/// +/// For example, `[!0, 0, !0, !0]` packs to +/// - `0b1101u8` or `[0b1101]` on little endian, and +/// - `0b1011u8` or `[0b1011]` on big endian. +/// +/// To consider a larger example, +/// `[!0, 0, 0, 0, 0, 0, 0, 0, !0, !0, 0, 0, 0, 0, !0, 0]` packs to +/// - `0b0100001100000001u16` or `[0b00000001, 0b01000011]` on little endian, and +/// - `0b1000000011000010u16` or `[0b10000000, 0b11000010]` on big endian. +/// +/// And finally, a non-power-of-2 example with multiple bytes: +/// `[!0, !0, 0, !0, 0, 0, !0, 0, !0, 0]` packs to +/// - `0b0101001011u16` or `[0b01001011, 0b01]` on little endian, and +/// - `0b1101001010u16` or `[0b11, 0b01001010]` on big endian. +/// +/// # Safety +/// `x` must contain only `0` and `!0`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_bitmask(_x: T) -> U { + unreachable!() +} + +/// Selects elements from a mask. +/// +/// `M` must be an integer vector. +/// +/// `T` must be a vector with the same number of elements as `M`. +/// +/// For each element, if the corresponding value in `mask` is `!0`, select the element from +/// `if_true`. If the corresponding value in `mask` is `0`, select the element from +/// `if_false`. +/// +/// # Safety +/// `mask` must only contain `0` and `!0`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_select(_mask: M, _if_true: T, _if_false: T) -> T { + unreachable!() +} + +/// Selects elements from a bitmask. +/// +/// `M` must be an unsigned integer or array of `u8`, matching `simd_bitmask`. +/// +/// `T` must be a vector. +/// +/// For each element, if the bit in `mask` is `1`, select the element from +/// `if_true`. If the corresponding bit in `mask` is `0`, select the element from +/// `if_false`. +/// +/// The bitmask bit order matches `simd_bitmask`. +/// +/// # Safety +/// Padding bits must be all zero. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_select_bitmask(_m: M, _yes: T, _no: T) -> T { + unreachable!() +} + +/// Calculates the offset from a pointer vector elementwise, potentially +/// wrapping. +/// +/// `T` must be a vector of pointers. +/// +/// `U` must be a vector of `isize` or `usize` with the same number of elements as `T`. +/// +/// Operates as if by `::wrapping_offset`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_arith_offset(_ptr: T, _offset: U) -> T { + unreachable!() +} + +/// Casts a vector of pointers. +/// +/// `T` and `U` must be vectors of pointers with the same number of elements. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_cast_ptr(_ptr: T) -> U { + unreachable!() +} + +/// Exposes a vector of pointers as a vector of addresses. +/// +/// `T` must be a vector of pointers. +/// +/// `U` must be a vector of `usize` with the same length as `T`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_expose_provenance(_ptr: T) -> U { + unreachable!() +} + +/// Creates a vector of pointers from a vector of addresses. +/// +/// `T` must be a vector of `usize`. +/// +/// `U` must be a vector of pointers, with the same length as `T`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_with_exposed_provenance(_addr: T) -> U { + unreachable!() +} + +/// Swaps bytes of each element. +/// +/// `T` must be a vector of integers. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_bswap(_x: T) -> T { + unreachable!() +} + +/// Reverses bits of each element. +/// +/// `T` must be a vector of integers. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_bitreverse(_x: T) -> T { + unreachable!() +} + +/// Counts the leading zeros of each element. +/// +/// `T` must be a vector of integers. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_ctlz(_x: T) -> T { + unreachable!() +} + +/// Counts the number of ones in each element. +/// +/// `T` must be a vector of integers. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_ctpop(_x: T) -> T { + unreachable!() +} + +/// Counts the trailing zeros of each element. +/// +/// `T` must be a vector of integers. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_cttz(_x: T) -> T { + unreachable!() +} + +/// Rounds up each element to the next highest integer-valued float. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_ceil(_x: T) -> T { + unreachable!() +} + +/// Rounds down each element to the next lowest integer-valued float. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_floor(_x: T) -> T { + unreachable!() +} + +/// Rounds each element to the closest integer-valued float. +/// Ties are resolved by rounding away from 0. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_round(_x: T) -> T { + unreachable!() +} + +/// Returns the integer part of each element as an integer-valued float. +/// In other words, non-integer values are truncated towards zero. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_trunc(_x: T) -> T { + unreachable!() +} + +/// Takes the square root of each element. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fsqrt(_x: T) -> T { + unreachable!() +} + +/// Computes `(x*y) + z` for each element, but without any intermediate rounding. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fma(_x: T, _y: T, _z: T) -> T { + unreachable!() +} + +/// Computes `(x*y) + z` for each element, non-deterministically executing either +/// a fused multiply-add or two operations with rounding of the intermediate result. +/// +/// The operation is fused if the code generator determines that target instruction +/// set has support for a fused operation, and that the fused operation is more efficient +/// than the equivalent, separate pair of mul and add instructions. It is unspecified +/// whether or not a fused operation is selected, and that may depend on optimization +/// level and context, for example. It may even be the case that some SIMD lanes get fused +/// and others do not. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_relaxed_fma(_x: T, _y: T, _z: T) -> T { + unreachable!() +} + +// Computes the sine of each element. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fsin(_a: T) -> T { + unreachable!() +} + +// Computes the cosine of each element. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fcos(_a: T) -> T { + unreachable!() +} + +// Computes the exponential function of each element. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fexp(_a: T) -> T { + unreachable!() +} + +// Computes 2 raised to the power of each element. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_fexp2(_a: T) -> T { + unreachable!() +} + +// Computes the base 10 logarithm of each element. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_flog10(_a: T) -> T { + unreachable!() +} + +// Computes the base 2 logarithm of each element. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_flog2(_a: T) -> T { + unreachable!() +} + +// Computes the natural logarithm of each element. +/// +/// `T` must be a vector of floats. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn simd_flog(_a: T) -> T { + unreachable!() } diff --git a/library/core/src/io/borrowed_buf.rs b/library/core/src/io/borrowed_buf.rs index 4227e503ba7ba..f86abf7f1e91c 100644 --- a/library/core/src/io/borrowed_buf.rs +++ b/library/core/src/io/borrowed_buf.rs @@ -94,7 +94,7 @@ impl<'data> BorrowedBuf<'data> { // SAFETY: We only slice the filled part of the buffer, which is always valid unsafe { let buf = self.buf.get_unchecked(..self.filled); - MaybeUninit::slice_assume_init_ref(buf) + buf.assume_init_ref() } } @@ -104,7 +104,7 @@ impl<'data> BorrowedBuf<'data> { // SAFETY: We only slice the filled part of the buffer, which is always valid unsafe { let buf = self.buf.get_unchecked_mut(..self.filled); - MaybeUninit::slice_assume_init_mut(buf) + buf.assume_init_mut() } } @@ -114,7 +114,7 @@ impl<'data> BorrowedBuf<'data> { // SAFETY: We only slice the filled part of the buffer, which is always valid unsafe { let buf = self.buf.get_unchecked(..self.filled); - MaybeUninit::slice_assume_init_ref(buf) + buf.assume_init_ref() } } @@ -124,7 +124,7 @@ impl<'data> BorrowedBuf<'data> { // SAFETY: We only slice the filled part of the buffer, which is always valid unsafe { let buf = self.buf.get_unchecked_mut(..self.filled); - MaybeUninit::slice_assume_init_mut(buf) + buf.assume_init_mut() } } @@ -233,7 +233,7 @@ impl<'a> BorrowedCursor<'a> { // SAFETY: We only slice the initialized part of the buffer, which is always valid unsafe { let buf = self.buf.buf.get_unchecked(self.buf.filled..self.buf.init); - MaybeUninit::slice_assume_init_ref(buf) + buf.assume_init_ref() } } @@ -243,7 +243,7 @@ impl<'a> BorrowedCursor<'a> { // SAFETY: We only slice the initialized part of the buffer, which is always valid unsafe { let buf = self.buf.buf.get_unchecked_mut(self.buf.filled..self.buf.init); - MaybeUninit::slice_assume_init_mut(buf) + buf.assume_init_mut() } } @@ -344,7 +344,7 @@ impl<'a> BorrowedCursor<'a> { // SAFETY: we do not de-initialize any of the elements of the slice unsafe { - MaybeUninit::copy_from_slice(&mut self.as_mut()[..buf.len()], buf); + self.as_mut()[..buf.len()].write_copy_of_slice(buf); } // SAFETY: We just added the entire contents of buf to the filled section. diff --git a/library/core/src/iter/adapters/filter_map.rs b/library/core/src/iter/adapters/filter_map.rs index cc64ceb13f766..24ec6b1741ce1 100644 --- a/library/core/src/iter/adapters/filter_map.rs +++ b/library/core/src/iter/adapters/filter_map.rs @@ -81,9 +81,7 @@ where if const { crate::mem::needs_drop::() } { // SAFETY: self.initialized is always <= N, which also is the length of the array. unsafe { - core::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut( - self.array.get_unchecked_mut(..self.initialized), - )); + self.array.get_unchecked_mut(..self.initialized).assume_init_drop(); } } } diff --git a/library/core/src/iter/adapters/flatten.rs b/library/core/src/iter/adapters/flatten.rs index 0023b46031f12..9b9353b800a98 100644 --- a/library/core/src/iter/adapters/flatten.rs +++ b/library/core/src/iter/adapters/flatten.rs @@ -1,7 +1,7 @@ use crate::iter::adapters::SourceIter; use crate::iter::{ - Cloned, Copied, Empty, Filter, FilterMap, Fuse, FusedIterator, InPlaceIterable, Map, Once, - OnceWith, TrustedFused, TrustedLen, + Cloned, Copied, Empty, Filter, FilterMap, Fuse, FusedIterator, Map, Once, OnceWith, + TrustedFused, TrustedLen, }; use crate::num::NonZero; use crate::ops::{ControlFlow, Try}; @@ -157,21 +157,6 @@ where { } -#[unstable(issue = "none", feature = "inplace_iteration")] -unsafe impl InPlaceIterable for FlatMap -where - I: InPlaceIterable, - U: BoundedSize + IntoIterator, -{ - const EXPAND_BY: Option> = const { - match (I::EXPAND_BY, U::UPPER_BOUND) { - (Some(m), Some(n)) => m.checked_mul(n), - _ => None, - } - }; - const MERGE_BY: Option> = I::MERGE_BY; -} - #[unstable(issue = "none", feature = "inplace_iteration")] unsafe impl SourceIter for FlatMap where @@ -386,21 +371,6 @@ where { } -#[unstable(issue = "none", feature = "inplace_iteration")] -unsafe impl InPlaceIterable for Flatten -where - I: InPlaceIterable + Iterator, - ::Item: IntoIterator + BoundedSize, -{ - const EXPAND_BY: Option> = const { - match (I::EXPAND_BY, I::Item::UPPER_BOUND) { - (Some(m), Some(n)) => m.checked_mul(n), - _ => None, - } - }; - const MERGE_BY: Option> = I::MERGE_BY; -} - #[unstable(issue = "none", feature = "inplace_iteration")] unsafe impl SourceIter for Flatten where diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index 635e14e769a84..d62a445d704ae 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -420,14 +420,14 @@ pub use self::adapters::{Intersperse, IntersperseWith}; issue = "42168" )] pub use self::range::Step; +#[stable(feature = "iter_empty", since = "1.2.0")] +pub use self::sources::{Empty, empty}; #[unstable( feature = "iter_from_coroutine", issue = "43122", reason = "coroutines are unstable" )] -pub use self::sources::from_coroutine; -#[stable(feature = "iter_empty", since = "1.2.0")] -pub use self::sources::{Empty, empty}; +pub use self::sources::{FromCoroutine, from_coroutine}; #[stable(feature = "iter_from_fn", since = "1.34.0")] pub use self::sources::{FromFn, from_fn}; #[stable(feature = "iter_once", since = "1.2.0")] diff --git a/library/core/src/iter/sources.rs b/library/core/src/iter/sources.rs index c635992dfbd1b..1eb4367b18372 100644 --- a/library/core/src/iter/sources.rs +++ b/library/core/src/iter/sources.rs @@ -15,7 +15,7 @@ pub use self::empty::{Empty, empty}; issue = "43122", reason = "coroutines are unstable" )] -pub use self::from_coroutine::from_coroutine; +pub use self::from_coroutine::{FromCoroutine, from_coroutine}; #[stable(feature = "iter_from_fn", since = "1.34.0")] pub use self::from_fn::{FromFn, from_fn}; #[stable(feature = "iter_once", since = "1.2.0")] diff --git a/library/core/src/iter/sources/from_fn.rs b/library/core/src/iter/sources/from_fn.rs index 3cd3830471cfe..1c7e1b30a2f88 100644 --- a/library/core/src/iter/sources/from_fn.rs +++ b/library/core/src/iter/sources/from_fn.rs @@ -1,7 +1,9 @@ use crate::fmt; -/// Creates a new iterator where each iteration calls the provided closure -/// `F: FnMut() -> Option`. +/// Creates an iterator with the provided closure +/// `F: FnMut() -> Option` as its [`next`](Iterator::next) method. +/// +/// The iterator will yield the `T`s returned from the closure. /// /// This allows creating a custom iterator with any behavior /// without using the more verbose syntax of creating a dedicated type diff --git a/library/core/src/iter/sources/once_with.rs b/library/core/src/iter/sources/once_with.rs index 8b31ab2ff90c0..c9698b4fd431b 100644 --- a/library/core/src/iter/sources/once_with.rs +++ b/library/core/src/iter/sources/once_with.rs @@ -58,8 +58,8 @@ use crate::iter::{FusedIterator, TrustedLen}; /// ``` #[inline] #[stable(feature = "iter_once_with", since = "1.43.0")] -pub fn once_with A>(gen: F) -> OnceWith { - OnceWith { gen: Some(gen) } +pub fn once_with A>(make: F) -> OnceWith { + OnceWith { make: Some(make) } } /// An iterator that yields a single element of type `A` by @@ -70,13 +70,13 @@ pub fn once_with A>(gen: F) -> OnceWith { #[derive(Clone)] #[stable(feature = "iter_once_with", since = "1.43.0")] pub struct OnceWith { - gen: Option, + make: Option, } #[stable(feature = "iter_once_with_debug", since = "1.68.0")] impl fmt::Debug for OnceWith { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.gen.is_some() { + if self.make.is_some() { f.write_str("OnceWith(Some(_))") } else { f.write_str("OnceWith(None)") @@ -90,13 +90,13 @@ impl A> Iterator for OnceWith { #[inline] fn next(&mut self) -> Option { - let f = self.gen.take()?; + let f = self.make.take()?; Some(f()) } #[inline] fn size_hint(&self) -> (usize, Option) { - self.gen.iter().size_hint() + self.make.iter().size_hint() } } @@ -110,7 +110,7 @@ impl A> DoubleEndedIterator for OnceWith { #[stable(feature = "iter_once_with", since = "1.43.0")] impl A> ExactSizeIterator for OnceWith { fn len(&self) -> usize { - self.gen.iter().len() + self.make.iter().len() } } diff --git a/library/core/src/iter/sources/successors.rs b/library/core/src/iter/sources/successors.rs index 36bc4035039e6..e14c9235e5562 100644 --- a/library/core/src/iter/sources/successors.rs +++ b/library/core/src/iter/sources/successors.rs @@ -5,6 +5,7 @@ use crate::iter::FusedIterator; /// /// The iterator starts with the given first item (if any) /// and calls the given `FnMut(&T) -> Option` closure to compute each item’s successor. +/// The iterator will yield the `T`s returned from the closure. /// /// ``` /// use std::iter::successors; diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index 73e6d93106096..97bb21c8a36e8 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -472,7 +472,7 @@ macro_rules! spec_tuple_impl { #[doc(fake_variadic)] #[doc = "This trait is implemented for tuples up to twelve items long. The `impl`s for \ 1- and 3- through 12-ary tuples were stabilized after 2-tuples, in \ - CURRENT_RUSTC_VERSION."] + 1.85.0."] => ($ty_name, $var_name, $extend_ty_name, $cnt), ); }; diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 91c3a4b29b539..42886e90f997d 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -3493,7 +3493,8 @@ pub trait Iterator { /// /// Takes each element, adds them together, and returns the result. /// - /// An empty iterator returns the zero value of the type. + /// An empty iterator returns the *additive identity* ("zero") of the type, + /// which is `0` for integers and `-0.0` for floats. /// /// `sum()` can be used to sum any type implementing [`Sum`][`core::iter::Sum`], /// including [`Option`][`Option::sum`] and [`Result`][`Result::sum`]. @@ -3511,6 +3512,10 @@ pub trait Iterator { /// let sum: i32 = a.iter().sum(); /// /// assert_eq!(sum, 6); + /// + /// let b: Vec = vec![]; + /// let sum: f32 = b.iter().sum(); + /// assert_eq!(sum, -0.0_f32); /// ``` #[stable(feature = "iter_arith", since = "1.11.0")] fn sum(self) -> S diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index e31957a1fa703..99d5af9f0ef95 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -44,7 +44,7 @@ //! called. The `lang` attribute is called `eh_personality`. // Since core defines many fundamental lang items, all tests live in a -// separate crate, libcoretest (library/core/tests), to avoid bizarre issues. +// separate crate, coretests (library/coretests), to avoid bizarre issues. // // Here we explicitly #[cfg]-out this whole crate when testing. If we don't do // this, both the generated test artifact and the linked libtest (which @@ -101,20 +101,24 @@ #![warn(multiple_supertrait_upcastable)] #![allow(internal_features)] #![deny(ffi_unwind_calls)] +#![warn(unreachable_pub)] // Do not check link redundancy on bootstraping phase #![allow(rustdoc::redundant_explicit_links)] #![warn(rustdoc::unescaped_backticks)] // // Library features: // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(do_not_recommend))] #![feature(array_ptr_get)] #![feature(asm_experimental_arch)] #![feature(bigint_helper_methods)] +#![feature(bstr)] +#![feature(bstr_internals)] +#![feature(closure_track_caller)] #![feature(const_carrying_mul_add)] #![feature(const_eval_select)] #![feature(core_intrinsics)] #![feature(coverage_attribute)] +#![feature(disjoint_bitor)] #![feature(internal_impls_macro)] #![feature(ip)] #![feature(is_ascii_octdigit)] @@ -188,7 +192,6 @@ #![feature(staged_api)] #![feature(stmt_expr_attributes)] #![feature(strict_provenance_lints)] -#![feature(target_feature_11)] #![feature(trait_alias)] #![feature(transparent_unions)] #![feature(try_blocks)] @@ -244,6 +247,10 @@ pub mod autodiff { pub use crate::macros::builtin::autodiff; } +#[cfg(not(bootstrap))] +#[unstable(feature = "contracts", issue = "128044")] +pub mod contracts; + #[unstable(feature = "cfg_match", issue = "115585")] pub use crate::macros::cfg_match; @@ -336,6 +343,8 @@ pub mod ascii; pub mod asserting; #[unstable(feature = "async_iterator", issue = "79024")] pub mod async_iter; +#[unstable(feature = "bstr", issue = "134915")] +pub mod bstr; pub mod cell; pub mod char; pub mod ffi; @@ -397,7 +406,8 @@ pub mod primitive; unused_imports, unsafe_op_in_unsafe_fn, ambiguous_glob_reexports, - deprecated_in_future + deprecated_in_future, + unreachable_pub )] #[allow(rustdoc::bare_urls)] mod core_arch; diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 402b436d28e68..4c6fd196bd31c 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -224,6 +224,7 @@ pub macro assert_matches { /// } /// } /// ``` +#[cfg(bootstrap)] #[unstable(feature = "cfg_match", issue = "115585")] #[rustc_diagnostic_item = "cfg_match"] pub macro cfg_match { @@ -284,6 +285,68 @@ pub macro cfg_match { } } +/// A macro for defining `#[cfg]` match-like statements. +/// +/// It is similar to the `if/elif` C preprocessor macro by allowing definition of a cascade of +/// `#[cfg]` cases, emitting the implementation which matches first. +/// +/// This allows you to conveniently provide a long list `#[cfg]`'d blocks of code +/// without having to rewrite each clause multiple times. +/// +/// Trailing `_` wildcard match arms are **optional** and they indicate a fallback branch when +/// all previous declarations do not evaluate to true. +/// +/// # Example +/// +/// ``` +/// #![feature(cfg_match)] +/// +/// cfg_match! { +/// unix => { +/// fn foo() { /* unix specific functionality */ } +/// } +/// target_pointer_width = "32" => { +/// fn foo() { /* non-unix, 32-bit functionality */ } +/// } +/// _ => { +/// fn foo() { /* fallback implementation */ } +/// } +/// } +/// ``` +/// +/// If desired, it is possible to return expressions through the use of surrounding braces: +/// +/// ``` +/// #![feature(cfg_match)] +/// +/// let _some_string = cfg_match! {{ +/// unix => { "With great power comes great electricity bills" } +/// _ => { "Behind every successful diet is an unwatched pizza" } +/// }}; +/// ``` +#[cfg(not(bootstrap))] +#[unstable(feature = "cfg_match", issue = "115585")] +#[rustc_diagnostic_item = "cfg_match"] +pub macro cfg_match { + ({ $($tt:tt)* }) => {{ + cfg_match! { $($tt)* } + }}, + (_ => { $($output:tt)* }) => { + $($output)* + }, + ( + $cfg:meta => $output:tt + $($( $rest:tt )+)? + ) => { + #[cfg($cfg)] + cfg_match! { _ => $output } + $( + #[cfg(not($cfg))] + cfg_match! { $($rest)+ } + )? + }, +} + /// Asserts that a boolean expression is `true` at runtime. /// /// This will invoke the [`panic!`] macro if the provided expression cannot be @@ -1714,6 +1777,32 @@ pub(crate) mod builtin { /* compiler built-in */ } + /// Attribute macro applied to a function to give it a post-condition. + /// + /// The attribute carries an argument token-tree which is + /// eventually parsed as a unary closure expression that is + /// invoked on a reference to the return value. + #[cfg(not(bootstrap))] + #[unstable(feature = "contracts", issue = "128044")] + #[allow_internal_unstable(contracts_internals)] + #[rustc_builtin_macro] + pub macro contracts_ensures($item:item) { + /* compiler built-in */ + } + + /// Attribute macro applied to a function to give it a precondition. + /// + /// The attribute carries an argument token-tree which is + /// eventually parsed as an boolean expression with access to the + /// function's formal parameters + #[cfg(not(bootstrap))] + #[unstable(feature = "contracts", issue = "128044")] + #[allow_internal_unstable(contracts_internals)] + #[rustc_builtin_macro] + pub macro contracts_requires($item:item) { + /* compiler built-in */ + } + /// Attribute macro applied to a function to register it as a handler for allocation failure. /// /// See also [`std::alloc::handle_alloc_error`](../../../std/alloc/fn.handle_alloc_error.html). @@ -1768,32 +1857,4 @@ pub(crate) mod builtin { pub macro deref($pat:pat) { builtin # deref($pat) } - - /// Derive macro for `rustc-serialize`. Should not be used in new code. - #[rustc_builtin_macro] - #[unstable( - feature = "rustc_encodable_decodable", - issue = "none", - soft, - reason = "derive macro for `rustc-serialize`; should not be used in new code" - )] - #[deprecated(since = "1.52.0", note = "rustc-serialize is deprecated and no longer supported")] - #[doc(hidden)] // While technically stable, using it is unstable, and deprecated. Hide it. - pub macro RustcDecodable($item:item) { - /* compiler built-in */ - } - - /// Derive macro for `rustc-serialize`. Should not be used in new code. - #[rustc_builtin_macro] - #[unstable( - feature = "rustc_encodable_decodable", - issue = "none", - soft, - reason = "derive macro for `rustc-serialize`; should not be used in new code" - )] - #[deprecated(since = "1.52.0", note = "rustc-serialize is deprecated and no longer supported")] - #[doc(hidden)] // While technically stable, using it is unstable, and deprecated. Hide it. - pub macro RustcEncodable($item:item) { - /* compiler built-in */ - } } diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 29fc01d37fe3f..042ee419d57e4 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -6,6 +6,13 @@ #![stable(feature = "rust1", since = "1.0.0")] +mod variance; + +#[unstable(feature = "phantom_variance_markers", issue = "135806")] +pub use self::variance::{ + PhantomContravariant, PhantomContravariantLifetime, PhantomCovariant, PhantomCovariantLifetime, + PhantomInvariant, PhantomInvariantLifetime, Variance, variance, +}; use crate::cell::UnsafeCell; use crate::cmp; use crate::fmt::Debug; @@ -141,9 +148,8 @@ unsafe impl Send for &T {} )] #[fundamental] // for Default, for example, which requires that `[T]: !Default` be evaluatable #[rustc_specialization_trait] -#[cfg_attr(bootstrap, rustc_deny_explicit_impl(implement_via_object = false))] -#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)] -#[cfg_attr(not(bootstrap), rustc_do_not_implement_via_object)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] #[rustc_coinductive] pub trait Sized { // Empty. @@ -183,33 +189,27 @@ pub trait Sized { /// [^1]: Formerly known as *object safe*. #[unstable(feature = "unsize", issue = "18598")] #[lang = "unsize"] -#[cfg_attr(bootstrap, rustc_deny_explicit_impl(implement_via_object = false))] -#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)] -#[cfg_attr(not(bootstrap), rustc_do_not_implement_via_object)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] pub trait Unsize { // Empty. } /// Required trait for constants used in pattern matches. /// -/// Any type that derives `PartialEq` automatically implements this trait, -/// *regardless* of whether its type-parameters implement `PartialEq`. -/// -/// If a `const` item contains some type that does not implement this trait, -/// then that type either (1.) does not implement `PartialEq` (which means the -/// constant will not provide that comparison method, which code generation -/// assumes is available), or (2.) it implements *its own* version of -/// `PartialEq` (which we assume does not conform to a structural-equality -/// comparison). -/// -/// In either of the two scenarios above, we reject usage of such a constant in -/// a pattern match. +/// Constants are only allowed as patterns if (a) their type implements +/// `PartialEq`, and (b) interpreting the value of the constant as a pattern +/// is equialent to calling `PartialEq`. This ensures that constants used as +/// patterns cannot expose implementation details in an unexpected way or +/// cause semver hazards. /// -/// See also the [structural match RFC][RFC1445], and [issue 63438] which -/// motivated migrating from an attribute-based design to this trait. +/// This trait ensures point (b). +/// Any type that derives `PartialEq` automatically implements this trait. /// -/// [RFC1445]: https://github.com/rust-lang/rfcs/blob/master/text/1445-restrict-constants-in-patterns.md -/// [issue 63438]: https://github.com/rust-lang/rust/issues/63438 +/// Implementing this trait (which is unstable) is a way for type authors to explicitly allow +/// comparing const values of this type; that operation will recursively compare all fields +/// (including private fields), even if that behavior differs from `PartialEq`. This can make it +/// semver-breaking to add further private fields to a type. #[unstable(feature = "structural_match", issue = "31434")] #[diagnostic::on_unimplemented(message = "the type `{Self}` does not `#[derive(PartialEq)]`")] #[lang = "structural_peq"] @@ -453,6 +453,23 @@ impl Copy for ! {} #[stable(feature = "rust1", since = "1.0.0")] impl Copy for &T {} +/// Marker trait for the types that are allowed in union fields, unsafe fields, +/// and unsafe binder types. +/// +/// Implemented for: +/// * `&T`, `&mut T` for all `T`, +/// * `ManuallyDrop` for all `T`, +/// * tuples and arrays whose elements implement `BikeshedGuaranteedNoDrop`, +/// * or otherwise, all types that are `Copy`. +/// +/// Notably, this doesn't include all trivially-destructible types for semver +/// reasons. +/// +/// Bikeshed name for now. +#[unstable(feature = "bikeshed_guaranteed_no_drop", issue = "none")] +#[cfg_attr(not(bootstrap), lang = "bikeshed_guaranteed_no_drop")] +pub trait BikeshedGuaranteedNoDrop {} + /// Types for which it is safe to share references between threads. /// /// This trait is automatically implemented when the compiler determines @@ -819,9 +836,8 @@ impl StructuralPartialEq for PhantomData {} reason = "this trait is unlikely to ever be stabilized, use `mem::discriminant` instead" )] #[lang = "discriminant_kind"] -#[cfg_attr(bootstrap, rustc_deny_explicit_impl(implement_via_object = false))] -#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)] -#[cfg_attr(not(bootstrap), rustc_do_not_implement_via_object)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] pub trait DiscriminantKind { /// The type of the discriminant, which must satisfy the trait /// bounds required by `mem::Discriminant`. @@ -960,12 +976,12 @@ marker_impls! { /// This should be used for `~const` bounds, /// as non-const bounds will always hold for every type. #[unstable(feature = "const_destruct", issue = "133214")] +#[rustc_const_unstable(feature = "const_destruct", issue = "133214")] #[lang = "destruct"] #[rustc_on_unimplemented(message = "can't drop `{Self}`", append_const_msg)] -#[cfg_attr(bootstrap, rustc_deny_explicit_impl(implement_via_object = false))] -#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)] -#[cfg_attr(not(bootstrap), rustc_do_not_implement_via_object)] -#[cfg_attr(not(bootstrap), const_trait)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] +#[const_trait] pub trait Destruct {} /// A marker for tuple types. @@ -975,9 +991,8 @@ pub trait Destruct {} #[unstable(feature = "tuple_trait", issue = "none")] #[lang = "tuple_trait"] #[diagnostic::on_unimplemented(message = "`{Self}` is not a tuple")] -#[cfg_attr(bootstrap, rustc_deny_explicit_impl(implement_via_object = false))] -#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)] -#[cfg_attr(not(bootstrap), rustc_do_not_implement_via_object)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] pub trait Tuple {} /// A marker for pointer-like types. @@ -996,10 +1011,9 @@ pub trait Tuple {} message = "`{Self}` needs to have the same ABI as a pointer", label = "`{Self}` needs to be a pointer-like type" )] -#[cfg_attr(not(bootstrap), rustc_do_not_implement_via_object)] +#[rustc_do_not_implement_via_object] pub trait PointerLike {} -#[cfg(not(bootstrap))] marker_impls! { #[unstable(feature = "pointer_like_trait", issue = "none")] PointerLike for @@ -1080,26 +1094,229 @@ marker_impls! { } /// A common trait implemented by all function pointers. +// +// Note that while the trait is internal and unstable it is nevertheless +// exposed as a public bound of the stable `core::ptr::fn_addr_eq` function. #[unstable( feature = "fn_ptr_trait", issue = "none", reason = "internal trait for implementing various traits for all function pointers" )] #[lang = "fn_ptr_trait"] -#[cfg_attr(bootstrap, rustc_deny_explicit_impl(implement_via_object = false))] -#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)] -#[cfg_attr(not(bootstrap), rustc_do_not_implement_via_object)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] pub trait FnPtr: Copy + Clone { /// Returns the address of the function pointer. #[lang = "fn_ptr_addr"] fn addr(self) -> *const (); } -/// Derive macro generating impls of traits related to smart pointers. +/// Derive macro that makes a smart pointer usable with trait objects. +/// +/// # What this macro does +/// +/// This macro is intended to be used with user-defined pointer types, and makes it possible to +/// perform coercions on the pointee of the user-defined pointer. There are two aspects to this: +/// +/// ## Unsizing coercions of the pointee +/// +/// By using the macro, the following example will compile: +/// ``` +/// #![feature(derive_coerce_pointee)] +/// use std::marker::CoercePointee; +/// use std::ops::Deref; +/// +/// #[derive(CoercePointee)] +/// #[repr(transparent)] +/// struct MySmartPointer(Box); +/// +/// impl Deref for MySmartPointer { +/// type Target = T; +/// fn deref(&self) -> &T { +/// &self.0 +/// } +/// } +/// +/// trait MyTrait {} +/// +/// impl MyTrait for i32 {} +/// +/// fn main() { +/// let ptr: MySmartPointer = MySmartPointer(Box::new(4)); +/// +/// // This coercion would be an error without the derive. +/// let ptr: MySmartPointer = ptr; +/// } +/// ``` +/// Without the `#[derive(CoercePointee)]` macro, this example would fail with the following error: +/// ```text +/// error[E0308]: mismatched types +/// --> src/main.rs:11:44 +/// | +/// 11 | let ptr: MySmartPointer = ptr; +/// | --------------------------- ^^^ expected `MySmartPointer`, found `MySmartPointer` +/// | | +/// | expected due to this +/// | +/// = note: expected struct `MySmartPointer` +/// found struct `MySmartPointer` +/// = help: `i32` implements `MyTrait` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well +/// ``` +/// +/// ## Dyn compatibility +/// +/// This macro allows you to dispatch on the user-defined pointer type. That is, traits using the +/// type as a receiver are dyn-compatible. For example, this compiles: +/// +/// ``` +/// #![feature(arbitrary_self_types, derive_coerce_pointee)] +/// use std::marker::CoercePointee; +/// use std::ops::Deref; +/// +/// #[derive(CoercePointee)] +/// #[repr(transparent)] +/// struct MySmartPointer(Box); +/// +/// impl Deref for MySmartPointer { +/// type Target = T; +/// fn deref(&self) -> &T { +/// &self.0 +/// } +/// } +/// +/// // You can always define this trait. (as long as you have #![feature(arbitrary_self_types)]) +/// trait MyTrait { +/// fn func(self: MySmartPointer); +/// } +/// +/// // But using `dyn MyTrait` requires #[derive(CoercePointee)]. +/// fn call_func(value: MySmartPointer) { +/// value.func(); +/// } +/// ``` +/// If you remove the `#[derive(CoercePointee)]` annotation from the struct, then the above example +/// will fail with this error message: +/// ```text +/// error[E0038]: the trait `MyTrait` is not dyn compatible +/// --> src/lib.rs:21:36 +/// | +/// 17 | fn func(self: MySmartPointer); +/// | -------------------- help: consider changing method `func`'s `self` parameter to be `&self`: `&Self` +/// ... +/// 21 | fn call_func(value: MySmartPointer) { +/// | ^^^^^^^^^^^ `MyTrait` is not dyn compatible +/// | +/// note: for a trait to be dyn compatible it needs to allow building a vtable +/// for more information, visit +/// --> src/lib.rs:17:19 +/// | +/// 16 | trait MyTrait { +/// | ------- this trait is not dyn compatible... +/// 17 | fn func(self: MySmartPointer); +/// | ^^^^^^^^^^^^^^^^^^^^ ...because method `func`'s `self` parameter cannot be dispatched on +/// ``` +/// +/// # Requirements for using the macro +/// +/// This macro can only be used if: +/// * The type is a `#[repr(transparent)]` struct. +/// * The type of its non-zero-sized field must either be a standard library pointer type +/// (reference, raw pointer, `NonNull`, `Box`, `Rc`, `Arc`, etc.) or another user-defined type +/// also using the `#[derive(CoercePointee)]` macro. +/// * Zero-sized fields must not mention any generic parameters unless the zero-sized field has +/// type [`PhantomData`]. +/// +/// ## Multiple type parameters +/// +/// If the type has multiple type parameters, then you must explicitly specify which one should be +/// used for dynamic dispatch. For example: +/// ``` +/// # #![feature(derive_coerce_pointee)] +/// # use std::marker::{CoercePointee, PhantomData}; +/// #[derive(CoercePointee)] +/// #[repr(transparent)] +/// struct MySmartPointer<#[pointee] T: ?Sized, U> { +/// ptr: Box, +/// _phantom: PhantomData, +/// } +/// ``` +/// Specifying `#[pointee]` when the struct has only one type parameter is allowed, but not required. +/// +/// # Examples +/// +/// A custom implementation of the `Rc` type: +/// ``` +/// #![feature(derive_coerce_pointee)] +/// use std::marker::CoercePointee; +/// use std::ops::Deref; +/// use std::ptr::NonNull; +/// +/// #[derive(CoercePointee)] +/// #[repr(transparent)] +/// pub struct Rc { +/// inner: NonNull>, +/// } +/// +/// struct RcInner { +/// refcount: usize, +/// value: T, +/// } +/// +/// impl Deref for Rc { +/// type Target = T; +/// fn deref(&self) -> &T { +/// let ptr = self.inner.as_ptr(); +/// unsafe { &(*ptr).value } +/// } +/// } +/// +/// impl Rc { +/// pub fn new(value: T) -> Self { +/// let inner = Box::new(RcInner { +/// refcount: 1, +/// value, +/// }); +/// Self { +/// inner: NonNull::from(Box::leak(inner)), +/// } +/// } +/// } +/// +/// impl Clone for Rc { +/// fn clone(&self) -> Self { +/// // A real implementation would handle overflow here. +/// unsafe { (*self.inner.as_ptr()).refcount += 1 }; +/// Self { inner: self.inner } +/// } +/// } +/// +/// impl Drop for Rc { +/// fn drop(&mut self) { +/// let ptr = self.inner.as_ptr(); +/// unsafe { (*ptr).refcount -= 1 }; +/// if unsafe { (*ptr).refcount } == 0 { +/// drop(unsafe { Box::from_raw(ptr) }); +/// } +/// } +/// } +/// ``` #[rustc_builtin_macro(CoercePointee, attributes(pointee))] -#[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize)] +#[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize, coerce_pointee_validated)] #[unstable(feature = "derive_coerce_pointee", issue = "123430")] -#[cfg(not(bootstrap))] pub macro CoercePointee($item:item) { /* compiler built-in */ } + +/// A trait that is implemented for ADTs with `derive(CoercePointee)` so that +/// the compiler can enforce the derive impls are valid post-expansion, since +/// the derive has stricter requirements than if the impls were written by hand. +/// +/// This trait is not intended to be implemented by users or used other than +/// validation, so it should never be stabilized. +#[cfg(not(bootstrap))] +#[lang = "coerce_pointee_validated"] +#[unstable(feature = "coerce_pointee_validated", issue = "none")] +#[doc(hidden)] +pub trait CoercePointeeValidated { + /* compiler built-in */ +} diff --git a/library/core/src/marker/variance.rs b/library/core/src/marker/variance.rs new file mode 100644 index 0000000000000..23334e6575ddf --- /dev/null +++ b/library/core/src/marker/variance.rs @@ -0,0 +1,260 @@ +#![unstable(feature = "phantom_variance_markers", issue = "135806")] + +use super::PhantomData; +use crate::any::type_name; +use crate::cmp::Ordering; +use crate::fmt; +use crate::hash::{Hash, Hasher}; + +macro_rules! first_token { + ($first:tt $($rest:tt)*) => { + $first + }; +} + +macro_rules! phantom_type { + ($( + $(#[$attr:meta])* + pub struct $name:ident <$t:ident> ($($inner:tt)*); + )*) => {$( + $(#[$attr])* + pub struct $name<$t>($($inner)*) where T: ?Sized; + + impl $name + where T: ?Sized + { + /// Constructs a new instance of the variance marker. + pub const fn new() -> Self { + Self(PhantomData) + } + } + + impl self::sealed::Sealed for $name where T: ?Sized { + const VALUE: Self = Self::new(); + } + impl Variance for $name where T: ?Sized {} + + impl Default for $name + where T: ?Sized + { + fn default() -> Self { + Self(PhantomData) + } + } + + impl fmt::Debug for $name + where T: ?Sized + { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}<{}>", stringify!($name), type_name::()) + } + } + + impl Clone for $name + where T: ?Sized + { + fn clone(&self) -> Self { + *self + } + } + + impl Copy for $name where T: ?Sized {} + + impl PartialEq for $name + where T: ?Sized + { + fn eq(&self, _: &Self) -> bool { + true + } + } + + impl Eq for $name where T: ?Sized {} + + impl PartialOrd for $name + where T: ?Sized + { + fn partial_cmp(&self, _: &Self) -> Option { + Some(Ordering::Equal) + } + } + + impl Ord for $name + where T: ?Sized + { + fn cmp(&self, _: &Self) -> Ordering { + Ordering::Equal + } + } + + impl Hash for $name + where T: ?Sized + { + fn hash(&self, _: &mut H) {} + } + )*}; +} + +macro_rules! phantom_lifetime { + ($( + $(#[$attr:meta])* + pub struct $name:ident <$lt:lifetime> ($($inner:tt)*); + )*) => {$( + $(#[$attr])* + #[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct $name<$lt>($($inner)*); + + impl $name<'_> { + /// Constructs a new instance of the variance marker. + pub const fn new() -> Self { + Self(first_token!($($inner)*)(PhantomData)) + } + } + + impl self::sealed::Sealed for $name<'_> { + const VALUE: Self = Self::new(); + } + impl Variance for $name<'_> {} + + impl fmt::Debug for $name<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", stringify!($name)) + } + } + )*}; +} + +phantom_lifetime! { + /// Zero-sized type used to mark a lifetime as covariant. + /// + /// Covariant lifetimes must live at least as long as declared. See [the reference][1] for more + /// information. + /// + /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance + /// + /// ## Layout + /// + /// For all `'a`, the following are guaranteed: + /// * `size_of::>() == 0` + /// * `align_of::>() == 1` + pub struct PhantomCovariantLifetime<'a>(PhantomCovariant<&'a ()>); + /// Zero-sized type used to mark a lifetime as contravariant. + /// + /// Contravariant lifetimes must live at most as long as declared. See [the reference][1] for + /// more information. + /// + /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance + /// + /// ## Layout + /// + /// For all `'a`, the following are guaranteed: + /// * `size_of::>() == 0` + /// * `align_of::>() == 1` + pub struct PhantomContravariantLifetime<'a>(PhantomContravariant<&'a ()>); + /// Zero-sized type used to mark a lifetime as invariant. + /// + /// Invariant lifetimes must be live for the exact length declared, neither shorter nor longer. + /// See [the reference][1] for more information. + /// + /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance + /// + /// ## Layout + /// + /// For all `'a`, the following are guaranteed: + /// * `size_of::>() == 0` + /// * `align_of::>() == 1` + pub struct PhantomInvariantLifetime<'a>(PhantomInvariant<&'a ()>); +} + +phantom_type! { + /// Zero-sized type used to mark a type parameter as covariant. + /// + /// Types used as part of the return value from a function are covariant. If the type is _also_ + /// passed as a parameter then it is [invariant][PhantomInvariant]. See [the reference][1] for + /// more information. + /// + /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance + /// + /// ## Layout + /// + /// For all `T`, the following are guaranteed: + /// * `size_of::>() == 0` + /// * `align_of::>() == 1` + pub struct PhantomCovariant(PhantomData T>); + /// Zero-sized type used to mark a type parameter as contravariant. + /// + /// Types passed as arguments to a function are contravariant. If the type is _also_ part of the + /// return value from a function then it is [invariant][PhantomInvariant]. See [the + /// reference][1] for more information. + /// + /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance + /// + /// ## Layout + /// + /// For all `T`, the following are guaranteed: + /// * `size_of::>() == 0` + /// * `align_of::>() == 1` + pub struct PhantomContravariant(PhantomData); + /// Zero-sized type used to mark a type parameter as invariant. + /// + /// Types that are both passed as an argument _and_ used as part of the return value from a + /// function are invariant. See [the reference][1] for more information. + /// + /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance + /// + /// ## Layout + /// + /// For all `T`, the following are guaranteed: + /// * `size_of::>() == 0` + /// * `align_of::>() == 1` + pub struct PhantomInvariant(PhantomData T>); +} + +mod sealed { + pub trait Sealed { + const VALUE: Self; + } +} + +/// A marker trait for phantom variance types. +pub trait Variance: sealed::Sealed + Default {} + +/// Construct a variance marker; equivalent to [`Default::default`]. +/// +/// This type can be any of the following. You generally should not need to explicitly name the +/// type, however. +/// +/// - [`PhantomCovariant`] +/// - [`PhantomContravariant`] +/// - [`PhantomInvariant`] +/// - [`PhantomCovariantLifetime`] +/// - [`PhantomContravariantLifetime`] +/// - [`PhantomInvariantLifetime`] +/// +/// # Example +/// +/// ```rust +/// #![feature(phantom_variance_markers)] +/// +/// use core::marker::{PhantomCovariant, variance}; +/// +/// struct BoundFn +/// where +/// F: Fn(P) -> R, +/// { +/// function: F, +/// parameter: P, +/// return_value: PhantomCovariant, +/// } +/// +/// let bound_fn = BoundFn { +/// function: core::convert::identity, +/// parameter: 5u8, +/// return_value: variance(), +/// }; +/// ``` +pub const fn variance() -> T +where + T: Variance, +{ + T::VALUE +} diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 58fb5be5812c8..2c7f1d86341ad 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -1,5 +1,5 @@ use crate::any::type_name; -use crate::mem::{self, ManuallyDrop}; +use crate::mem::ManuallyDrop; use crate::{fmt, intrinsics, ptr, slice}; /// A wrapper type to construct uninitialized instances of `T`. @@ -276,10 +276,9 @@ impl Clone for MaybeUninit { impl fmt::Debug for MaybeUninit { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // NB: there is no `.pad_fmt` so we can't use a simpler `format_args!("MaybeUninit<{..}>"). - // This needs to be adjusted if `MaybeUninit` moves modules. let full_name = type_name::(); - let short_name = full_name.split_once("mem::maybe_uninit::").unwrap().1; - f.pad(short_name) + let prefix_len = full_name.find("MaybeUninit").unwrap(); + f.pad(&full_name[prefix_len..]) } } @@ -346,7 +345,7 @@ impl MaybeUninit { /// /// use std::mem::MaybeUninit; /// - /// extern "C" { + /// unsafe extern "C" { /// fn read_into_buffer(ptr: *mut u8, max_len: usize) -> usize; /// } /// @@ -354,7 +353,7 @@ impl MaybeUninit { /// fn read(buf: &mut [MaybeUninit]) -> &[u8] { /// unsafe { /// let len = read_into_buffer(buf.as_mut_ptr() as *mut u8, buf.len()); - /// MaybeUninit::slice_assume_init_ref(&buf[..len]) + /// buf[..len].assume_init_ref() /// } /// } /// @@ -507,7 +506,7 @@ impl MaybeUninit { /// ``` #[inline(always)] #[stable(feature = "maybe_uninit_write", since = "1.55.0")] - #[rustc_const_stable(feature = "const_maybe_uninit_write", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_maybe_uninit_write", since = "1.85.0")] pub const fn write(&mut self, val: T) -> &mut T { *self = MaybeUninit::new(val); // SAFETY: We just initialized this value. @@ -740,7 +739,7 @@ impl MaybeUninit { /// /// On top of that, all additional invariants of the type `T` must be /// satisfied, as the `Drop` implementation of `T` (or its members) may - /// rely on this. For example, setting a [`Vec`] to an invalid but + /// rely on this. For example, setting a `Vec` to an invalid but /// non-null address makes it initialized (under the current implementation; /// this does not constitute a stable guarantee), because the only /// requirement the compiler knows about it is that the data pointer must be @@ -748,7 +747,6 @@ impl MaybeUninit { /// behavior. /// /// [`assume_init`]: MaybeUninit::assume_init - /// [`Vec`]: ../../std/vec/struct.Vec.html #[stable(feature = "maybe_uninit_extra", since = "1.60.0")] pub unsafe fn assume_init_drop(&mut self) { // SAFETY: the caller must guarantee that `self` is initialized and @@ -982,44 +980,87 @@ impl MaybeUninit { } } - /// Assuming all the elements are initialized, get a slice to them. + /// Returns the contents of this `MaybeUninit` as a slice of potentially uninitialized bytes. /// - /// # Safety + /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still + /// contain padding bytes which are left uninitialized. /// - /// It is up to the caller to guarantee that the `MaybeUninit` elements - /// really are in an initialized state. - /// Calling this when the content is not yet fully initialized causes undefined behavior. + /// # Examples /// - /// See [`assume_init_ref`] for more details and examples. + /// ``` + /// #![feature(maybe_uninit_as_bytes, maybe_uninit_slice)] + /// use std::mem::MaybeUninit; /// - /// [`assume_init_ref`]: MaybeUninit::assume_init_ref - #[unstable(feature = "maybe_uninit_slice", issue = "63569")] - #[inline(always)] - pub const unsafe fn slice_assume_init_ref(slice: &[Self]) -> &[T] { - // SAFETY: casting `slice` to a `*const [T]` is safe since the caller guarantees that - // `slice` is initialized, and `MaybeUninit` is guaranteed to have the same layout as `T`. - // The pointer obtained is valid since it refers to memory owned by `slice` which is a - // reference and thus guaranteed to be valid for reads. - unsafe { &*(slice as *const [Self] as *const [T]) } + /// let val = 0x12345678_i32; + /// let uninit = MaybeUninit::new(val); + /// let uninit_bytes = uninit.as_bytes(); + /// let bytes = unsafe { uninit_bytes.assume_init_ref() }; + /// assert_eq!(bytes, val.to_ne_bytes()); + /// ``` + #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] + pub const fn as_bytes(&self) -> &[MaybeUninit] { + // SAFETY: MaybeUninit is always valid, even for padding bytes + unsafe { + slice::from_raw_parts(self.as_ptr().cast::>(), super::size_of::()) + } } - /// Assuming all the elements are initialized, get a mutable slice to them. + /// Returns the contents of this `MaybeUninit` as a mutable slice of potentially uninitialized + /// bytes. /// - /// # Safety + /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still + /// contain padding bytes which are left uninitialized. /// - /// It is up to the caller to guarantee that the `MaybeUninit` elements - /// really are in an initialized state. - /// Calling this when the content is not yet fully initialized causes undefined behavior. + /// # Examples /// - /// See [`assume_init_mut`] for more details and examples. + /// ``` + /// #![feature(maybe_uninit_as_bytes)] + /// use std::mem::MaybeUninit; /// - /// [`assume_init_mut`]: MaybeUninit::assume_init_mut + /// let val = 0x12345678_i32; + /// let mut uninit = MaybeUninit::new(val); + /// let uninit_bytes = uninit.as_bytes_mut(); + /// if cfg!(target_endian = "little") { + /// uninit_bytes[0].write(0xcd); + /// } else { + /// uninit_bytes[3].write(0xcd); + /// } + /// let val2 = unsafe { uninit.assume_init() }; + /// assert_eq!(val2, 0x123456cd); + /// ``` + #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] + pub const fn as_bytes_mut(&mut self) -> &mut [MaybeUninit] { + // SAFETY: MaybeUninit is always valid, even for padding bytes + unsafe { + slice::from_raw_parts_mut( + self.as_mut_ptr().cast::>(), + super::size_of::(), + ) + } + } + + /// Deprecated version of [`slice::assume_init_ref`]. #[unstable(feature = "maybe_uninit_slice", issue = "63569")] - #[inline(always)] + #[rustc_const_unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[deprecated( + note = "replaced by inherent assume_init_ref method; will eventually be removed", + since = "1.83.0" + )] + pub const unsafe fn slice_assume_init_ref(slice: &[Self]) -> &[T] { + // SAFETY: Same for both methods. + unsafe { slice.assume_init_ref() } + } + + /// Deprecated version of [`slice::assume_init_mut`]. + #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[rustc_const_unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[deprecated( + note = "replaced by inherent assume_init_mut method; will eventually be removed", + since = "1.83.0" + )] pub const unsafe fn slice_assume_init_mut(slice: &mut [Self]) -> &mut [T] { - // SAFETY: similar to safety notes for `slice_get_ref`, but we have a - // mutable reference which is also guaranteed to be valid for writes. - unsafe { &mut *(slice as *mut [Self] as *mut [T]) } + // SAFETY: Same for both methods. + unsafe { slice.assume_init_mut() } } /// Gets a pointer to the first element of the array. @@ -1036,142 +1077,34 @@ impl MaybeUninit { this.as_mut_ptr() as *mut T } - /// Copies the elements from `src` to `this`, returning a mutable reference to the now initialized contents of `this`. - /// - /// If `T` does not implement `Copy`, use [`clone_from_slice`] - /// - /// This is similar to [`slice::copy_from_slice`]. - /// - /// # Panics - /// - /// This function will panic if the two slices have different lengths. - /// - /// # Examples - /// - /// ``` - /// #![feature(maybe_uninit_write_slice)] - /// use std::mem::MaybeUninit; - /// - /// let mut dst = [MaybeUninit::uninit(); 32]; - /// let src = [0; 32]; - /// - /// let init = MaybeUninit::copy_from_slice(&mut dst, &src); - /// - /// assert_eq!(init, src); - /// ``` - /// - /// ``` - /// #![feature(maybe_uninit_write_slice)] - /// use std::mem::MaybeUninit; - /// - /// let mut vec = Vec::with_capacity(32); - /// let src = [0; 16]; - /// - /// MaybeUninit::copy_from_slice(&mut vec.spare_capacity_mut()[..src.len()], &src); - /// - /// // SAFETY: we have just copied all the elements of len into the spare capacity - /// // the first src.len() elements of the vec are valid now. - /// unsafe { - /// vec.set_len(src.len()); - /// } - /// - /// assert_eq!(vec, src); - /// ``` - /// - /// [`clone_from_slice`]: MaybeUninit::clone_from_slice + /// Deprecated version of [`slice::write_copy_of_slice`]. #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + #[deprecated( + note = "replaced by inherent write_copy_of_slice method; will eventually be removed", + since = "1.83.0" + )] pub fn copy_from_slice<'a>(this: &'a mut [MaybeUninit], src: &[T]) -> &'a mut [T] where T: Copy, { - // SAFETY: &[T] and &[MaybeUninit] have the same layout - let uninit_src: &[MaybeUninit] = unsafe { super::transmute(src) }; - - this.copy_from_slice(uninit_src); - - // SAFETY: Valid elements have just been copied into `this` so it is initialized - unsafe { MaybeUninit::slice_assume_init_mut(this) } + this.write_copy_of_slice(src) } - /// Clones the elements from `src` to `this`, returning a mutable reference to the now initialized contents of `this`. - /// Any already initialized elements will not be dropped. - /// - /// If `T` implements `Copy`, use [`copy_from_slice`] - /// - /// This is similar to [`slice::clone_from_slice`] but does not drop existing elements. - /// - /// # Panics - /// - /// This function will panic if the two slices have different lengths, or if the implementation of `Clone` panics. - /// - /// If there is a panic, the already cloned elements will be dropped. - /// - /// # Examples - /// - /// ``` - /// #![feature(maybe_uninit_write_slice)] - /// use std::mem::MaybeUninit; - /// - /// let mut dst = [MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit(), MaybeUninit::uninit()]; - /// let src = ["wibbly".to_string(), "wobbly".to_string(), "timey".to_string(), "wimey".to_string(), "stuff".to_string()]; - /// - /// let init = MaybeUninit::clone_from_slice(&mut dst, &src); - /// - /// assert_eq!(init, src); - /// # // Prevent leaks for Miri - /// # unsafe { std::ptr::drop_in_place(init); } - /// ``` - /// - /// ``` - /// #![feature(maybe_uninit_write_slice)] - /// use std::mem::MaybeUninit; - /// - /// let mut vec = Vec::with_capacity(32); - /// let src = ["rust", "is", "a", "pretty", "cool", "language"]; - /// - /// MaybeUninit::clone_from_slice(&mut vec.spare_capacity_mut()[..src.len()], &src); - /// - /// // SAFETY: we have just cloned all the elements of len into the spare capacity - /// // the first src.len() elements of the vec are valid now. - /// unsafe { - /// vec.set_len(src.len()); - /// } - /// - /// assert_eq!(vec, src); - /// ``` - /// - /// [`copy_from_slice`]: MaybeUninit::copy_from_slice + /// Deprecated version of [`slice::write_clone_of_slice`]. #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + #[deprecated( + note = "replaced by inherent write_clone_of_slice method; will eventually be removed", + since = "1.83.0" + )] pub fn clone_from_slice<'a>(this: &'a mut [MaybeUninit], src: &[T]) -> &'a mut [T] where T: Clone, { - // unlike copy_from_slice this does not call clone_from_slice on the slice - // this is because `MaybeUninit` does not implement Clone. - - assert_eq!(this.len(), src.len(), "destination and source slices have different lengths"); - // NOTE: We need to explicitly slice them to the same length - // for bounds checking to be elided, and the optimizer will - // generate memcpy for simple cases (for example T = u8). - let len = this.len(); - let src = &src[..len]; - - // guard is needed b/c panic might happen during a clone - let mut guard = Guard { slice: this, initialized: 0 }; - - for i in 0..len { - guard.slice[i].write(src[i].clone()); - guard.initialized += 1; - } - - super::forget(guard); - - // SAFETY: Valid elements have just been written into `this` so it is initialized - unsafe { MaybeUninit::slice_assume_init_mut(this) } + this.write_clone_of_slice(src) } - /// Fills `this` with elements by cloning `value`, returning a mutable reference to the now - /// initialized contents of `this`. + /// Fills a slice with elements by cloning `value`, returning a mutable reference to the now + /// initialized contents of the slice. /// Any previously initialized elements will not be dropped. /// /// This is similar to [`slice::fill`]. @@ -1185,27 +1118,26 @@ impl MaybeUninit { /// /// # Examples /// - /// Fill an uninit vec with 1. /// ``` /// #![feature(maybe_uninit_fill)] /// use std::mem::MaybeUninit; /// - /// let mut buf = vec![MaybeUninit::uninit(); 10]; - /// let initialized = MaybeUninit::fill(buf.as_mut_slice(), 1); + /// let mut buf = [const { MaybeUninit::uninit() }; 10]; + /// let initialized = MaybeUninit::fill(&mut buf, 1); /// assert_eq!(initialized, &mut [1; 10]); /// ``` #[doc(alias = "memset")] #[unstable(feature = "maybe_uninit_fill", issue = "117428")] - pub fn fill<'a>(this: &'a mut [MaybeUninit], value: T) -> &'a mut [T] + pub fn fill(this: &mut [MaybeUninit], value: T) -> &mut [T] where T: Clone, { SpecFill::spec_fill(this, value); // SAFETY: Valid elements have just been filled into `this` so it is initialized - unsafe { MaybeUninit::slice_assume_init_mut(this) } + unsafe { this.assume_init_mut() } } - /// Fills `this` with elements returned by calling a closure repeatedly. + /// Fills a slice with elements returned by calling a closure repeatedly. /// /// This method uses a closure to create new values. If you'd rather `Clone` a given value, use /// [`MaybeUninit::fill`]. If you want to use the `Default` trait to generate values, you can @@ -1220,17 +1152,16 @@ impl MaybeUninit { /// /// # Examples /// - /// Fill an uninit vec with the default value. /// ``` /// #![feature(maybe_uninit_fill)] /// use std::mem::MaybeUninit; /// - /// let mut buf = vec![MaybeUninit::::uninit(); 10]; - /// let initialized = MaybeUninit::fill_with(buf.as_mut_slice(), Default::default); + /// let mut buf = [const { MaybeUninit::::uninit() }; 10]; + /// let initialized = MaybeUninit::fill_with(&mut buf, Default::default); /// assert_eq!(initialized, &mut [0; 10]); /// ``` #[unstable(feature = "maybe_uninit_fill", issue = "117428")] - pub fn fill_with<'a, F>(this: &'a mut [MaybeUninit], mut f: F) -> &'a mut [T] + pub fn fill_with(this: &mut [MaybeUninit], mut f: F) -> &mut [T] where F: FnMut() -> T, { @@ -1244,13 +1175,13 @@ impl MaybeUninit { super::forget(guard); // SAFETY: Valid elements have just been written into `this` so it is initialized - unsafe { MaybeUninit::slice_assume_init_mut(this) } + unsafe { this.assume_init_mut() } } - /// Fills `this` with elements yielded by an iterator until either all elements have been + /// Fills a slice with elements yielded by an iterator until either all elements have been /// initialized or the iterator is empty. /// - /// Returns two slices. The first slice contains the initialized portion of the original slice. + /// Returns two slices. The first slice contains the initialized portion of the original slice. /// The second slice is the still-uninitialized remainder of the original slice. /// /// # Panics @@ -1262,37 +1193,51 @@ impl MaybeUninit { /// /// # Examples /// - /// Fill an uninit vec with a cycling iterator. + /// Completely filling the slice: + /// /// ``` /// #![feature(maybe_uninit_fill)] /// use std::mem::MaybeUninit; /// - /// let mut buf = vec![MaybeUninit::uninit(); 5]; + /// let mut buf = [const { MaybeUninit::uninit() }; 5]; /// /// let iter = [1, 2, 3].into_iter().cycle(); /// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter); /// /// assert_eq!(initialized, &mut [1, 2, 3, 1, 2]); - /// assert_eq!(0, remainder.len()); + /// assert_eq!(remainder.len(), 0); /// ``` /// - /// Fill an uninit vec, but not completely. + /// Partially filling the slice: + /// /// ``` /// #![feature(maybe_uninit_fill)] /// use std::mem::MaybeUninit; /// - /// let mut buf = vec![MaybeUninit::uninit(); 5]; + /// let mut buf = [const { MaybeUninit::uninit() }; 5]; /// let iter = [1, 2]; /// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter); /// /// assert_eq!(initialized, &mut [1, 2]); /// assert_eq!(remainder.len(), 3); /// ``` + /// + /// Checking an iterator after filling a slice: + /// + /// ``` + /// #![feature(maybe_uninit_fill)] + /// use std::mem::MaybeUninit; + /// + /// let mut buf = [const { MaybeUninit::uninit() }; 3]; + /// let mut iter = [1, 2, 3, 4, 5].into_iter(); + /// let (initialized, remainder) = MaybeUninit::fill_from(&mut buf, iter.by_ref()); + /// + /// assert_eq!(initialized, &mut [1, 2, 3]); + /// assert_eq!(remainder.len(), 0); + /// assert_eq!(iter.as_slice(), &[4, 5]); + /// ``` #[unstable(feature = "maybe_uninit_fill", issue = "117428")] - pub fn fill_from<'a, I>( - this: &'a mut [MaybeUninit], - it: I, - ) -> (&'a mut [T], &'a mut [MaybeUninit]) + pub fn fill_from(this: &mut [MaybeUninit], it: I) -> (&mut [T], &mut [MaybeUninit]) where I: IntoIterator, { @@ -1312,70 +1257,169 @@ impl MaybeUninit { // SAFETY: Valid elements have just been written into `init`, so that portion // of `this` is initialized. - (unsafe { MaybeUninit::slice_assume_init_mut(initted) }, remainder) + (unsafe { initted.assume_init_mut() }, remainder) } - /// Returns the contents of this `MaybeUninit` as a slice of potentially uninitialized bytes. + /// Deprecated version of [`slice::as_bytes`]. + #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] + #[deprecated( + note = "replaced by inherent as_bytes method; will eventually be removed", + since = "1.83.0" + )] + pub fn slice_as_bytes(this: &[MaybeUninit]) -> &[MaybeUninit] { + this.as_bytes() + } + + /// Deprecated version of [`slice::as_bytes_mut`]. + #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] + #[deprecated( + note = "replaced by inherent as_bytes_mut method; will eventually be removed", + since = "1.83.0" + )] + pub fn slice_as_bytes_mut(this: &mut [MaybeUninit]) -> &mut [MaybeUninit] { + this.as_bytes_mut() + } +} + +impl [MaybeUninit] { + /// Copies the elements from `src` to `self`, + /// returning a mutable reference to the now initialized contents of `self`. /// - /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still - /// contain padding bytes which are left uninitialized. + /// If `T` does not implement `Copy`, use [`write_clone_of_slice`] instead. + /// + /// This is similar to [`slice::copy_from_slice`]. + /// + /// # Panics + /// + /// This function will panic if the two slices have different lengths. /// /// # Examples /// /// ``` - /// #![feature(maybe_uninit_as_bytes, maybe_uninit_slice)] + /// #![feature(maybe_uninit_write_slice)] /// use std::mem::MaybeUninit; /// - /// let val = 0x12345678_i32; - /// let uninit = MaybeUninit::new(val); - /// let uninit_bytes = uninit.as_bytes(); - /// let bytes = unsafe { MaybeUninit::slice_assume_init_ref(uninit_bytes) }; - /// assert_eq!(bytes, val.to_ne_bytes()); + /// let mut dst = [MaybeUninit::uninit(); 32]; + /// let src = [0; 32]; + /// + /// let init = dst.write_copy_of_slice(&src); + /// + /// assert_eq!(init, src); /// ``` - #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] - pub fn as_bytes(&self) -> &[MaybeUninit] { - // SAFETY: MaybeUninit is always valid, even for padding bytes - unsafe { - slice::from_raw_parts(self.as_ptr() as *const MaybeUninit, mem::size_of::()) - } + /// + /// ``` + /// #![feature(maybe_uninit_write_slice)] + /// + /// let mut vec = Vec::with_capacity(32); + /// let src = [0; 16]; + /// + /// vec.spare_capacity_mut()[..src.len()].write_copy_of_slice(&src); + /// + /// // SAFETY: we have just copied all the elements of len into the spare capacity + /// // the first src.len() elements of the vec are valid now. + /// unsafe { + /// vec.set_len(src.len()); + /// } + /// + /// assert_eq!(vec, src); + /// ``` + /// + /// [`write_clone_of_slice`]: slice::write_clone_of_slice + #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + #[rustc_const_unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + pub const fn write_copy_of_slice(&mut self, src: &[T]) -> &mut [T] + where + T: Copy, + { + // SAFETY: &[T] and &[MaybeUninit] have the same layout + let uninit_src: &[MaybeUninit] = unsafe { super::transmute(src) }; + + self.copy_from_slice(uninit_src); + + // SAFETY: Valid elements have just been copied into `self` so it is initialized + unsafe { self.assume_init_mut() } } - /// Returns the contents of this `MaybeUninit` as a mutable slice of potentially uninitialized - /// bytes. + /// Clones the elements from `src` to `self`, + /// returning a mutable reference to the now initialized contents of `self`. + /// Any already initialized elements will not be dropped. /// - /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still - /// contain padding bytes which are left uninitialized. + /// If `T` implements `Copy`, use [`write_copy_of_slice`] instead. + /// + /// This is similar to [`slice::clone_from_slice`] but does not drop existing elements. + /// + /// # Panics + /// + /// This function will panic if the two slices have different lengths, or if the implementation of `Clone` panics. + /// + /// If there is a panic, the already cloned elements will be dropped. /// /// # Examples /// /// ``` - /// #![feature(maybe_uninit_as_bytes)] + /// #![feature(maybe_uninit_write_slice)] /// use std::mem::MaybeUninit; /// - /// let val = 0x12345678_i32; - /// let mut uninit = MaybeUninit::new(val); - /// let uninit_bytes = uninit.as_bytes_mut(); - /// if cfg!(target_endian = "little") { - /// uninit_bytes[0].write(0xcd); - /// } else { - /// uninit_bytes[3].write(0xcd); + /// let mut dst = [const { MaybeUninit::uninit() }; 5]; + /// let src = ["wibbly", "wobbly", "timey", "wimey", "stuff"].map(|s| s.to_string()); + /// + /// let init = dst.write_clone_of_slice(&src); + /// + /// assert_eq!(init, src); + /// + /// # // Prevent leaks for Miri + /// # unsafe { std::ptr::drop_in_place(init); } + /// ``` + /// + /// ``` + /// #![feature(maybe_uninit_write_slice)] + /// + /// let mut vec = Vec::with_capacity(32); + /// let src = ["rust", "is", "a", "pretty", "cool", "language"].map(|s| s.to_string()); + /// + /// vec.spare_capacity_mut()[..src.len()].write_clone_of_slice(&src); + /// + /// // SAFETY: we have just cloned all the elements of len into the spare capacity + /// // the first src.len() elements of the vec are valid now. + /// unsafe { + /// vec.set_len(src.len()); /// } - /// let val2 = unsafe { uninit.assume_init() }; - /// assert_eq!(val2, 0x123456cd); + /// + /// assert_eq!(vec, src); /// ``` - #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] - pub fn as_bytes_mut(&mut self) -> &mut [MaybeUninit] { - // SAFETY: MaybeUninit is always valid, even for padding bytes - unsafe { - slice::from_raw_parts_mut( - self.as_mut_ptr() as *mut MaybeUninit, - mem::size_of::(), - ) + /// + /// [`write_copy_of_slice`]: slice::write_copy_of_slice + #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + pub fn write_clone_of_slice(&mut self, src: &[T]) -> &mut [T] + where + T: Clone, + { + // unlike copy_from_slice this does not call clone_from_slice on the slice + // this is because `MaybeUninit` does not implement Clone. + + assert_eq!(self.len(), src.len(), "destination and source slices have different lengths"); + + // NOTE: We need to explicitly slice them to the same length + // for bounds checking to be elided, and the optimizer will + // generate memcpy for simple cases (for example T = u8). + let len = self.len(); + let src = &src[..len]; + + // guard is needed b/c panic might happen during a clone + let mut guard = Guard { slice: self, initialized: 0 }; + + for i in 0..len { + guard.slice[i].write(src[i].clone()); + guard.initialized += 1; } + + super::forget(guard); + + // SAFETY: Valid elements have just been written into `self` so it is initialized + unsafe { self.assume_init_mut() } } - /// Returns the contents of this slice of `MaybeUninit` as a slice of potentially uninitialized - /// bytes. + /// Returns the contents of this `MaybeUninit` as a slice of potentially uninitialized bytes. /// /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still /// contain padding bytes which are left uninitialized. @@ -1387,21 +1431,22 @@ impl MaybeUninit { /// use std::mem::MaybeUninit; /// /// let uninit = [MaybeUninit::new(0x1234u16), MaybeUninit::new(0x5678u16)]; - /// let uninit_bytes = MaybeUninit::slice_as_bytes(&uninit); - /// let bytes = unsafe { MaybeUninit::slice_assume_init_ref(&uninit_bytes) }; + /// let uninit_bytes = uninit.as_bytes(); + /// let bytes = unsafe { uninit_bytes.assume_init_ref() }; /// let val1 = u16::from_ne_bytes(bytes[0..2].try_into().unwrap()); /// let val2 = u16::from_ne_bytes(bytes[2..4].try_into().unwrap()); /// assert_eq!(&[val1, val2], &[0x1234u16, 0x5678u16]); /// ``` #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] - pub fn slice_as_bytes(this: &[MaybeUninit]) -> &[MaybeUninit] { - let bytes = mem::size_of_val(this); + pub const fn as_bytes(&self) -> &[MaybeUninit] { // SAFETY: MaybeUninit is always valid, even for padding bytes - unsafe { slice::from_raw_parts(this.as_ptr() as *const MaybeUninit, bytes) } + unsafe { + slice::from_raw_parts(self.as_ptr().cast::>(), super::size_of_val(self)) + } } - /// Returns the contents of this mutable slice of `MaybeUninit` as a mutable slice of - /// potentially uninitialized bytes. + /// Returns the contents of this `MaybeUninit` slice as a mutable slice of potentially + /// uninitialized bytes. /// /// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still /// contain padding bytes which are left uninitialized. @@ -1414,8 +1459,8 @@ impl MaybeUninit { /// /// let mut uninit = [MaybeUninit::::uninit(), MaybeUninit::::uninit()]; /// let uninit_bytes = MaybeUninit::slice_as_bytes_mut(&mut uninit); - /// MaybeUninit::copy_from_slice(uninit_bytes, &[0x12, 0x34, 0x56, 0x78]); - /// let vals = unsafe { MaybeUninit::slice_assume_init_ref(&uninit) }; + /// uninit_bytes.write_copy_of_slice(&[0x12, 0x34, 0x56, 0x78]); + /// let vals = unsafe { uninit.assume_init_ref() }; /// if cfg!(target_endian = "little") { /// assert_eq!(vals, &[0x3412u16, 0x7856u16]); /// } else { @@ -1423,10 +1468,74 @@ impl MaybeUninit { /// } /// ``` #[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")] - pub fn slice_as_bytes_mut(this: &mut [MaybeUninit]) -> &mut [MaybeUninit] { - let bytes = mem::size_of_val(this); + pub const fn as_bytes_mut(&mut self) -> &mut [MaybeUninit] { // SAFETY: MaybeUninit is always valid, even for padding bytes - unsafe { slice::from_raw_parts_mut(this.as_mut_ptr() as *mut MaybeUninit, bytes) } + unsafe { + slice::from_raw_parts_mut( + self.as_mut_ptr() as *mut MaybeUninit, + super::size_of_val(self), + ) + } + } + + /// Drops the contained values in place. + /// + /// # Safety + /// + /// It is up to the caller to guarantee that every `MaybeUninit` in the slice + /// really is in an initialized state. Calling this when the content is not yet + /// fully initialized causes undefined behavior. + /// + /// On top of that, all additional invariants of the type `T` must be + /// satisfied, as the `Drop` implementation of `T` (or its members) may + /// rely on this. For example, setting a `Vec` to an invalid but + /// non-null address makes it initialized (under the current implementation; + /// this does not constitute a stable guarantee), because the only + /// requirement the compiler knows about it is that the data pointer must be + /// non-null. Dropping such a `Vec` however will cause undefined + /// behaviour. + #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[inline(always)] + pub unsafe fn assume_init_drop(&mut self) { + if !self.is_empty() { + // SAFETY: the caller must guarantee that every element of `self` + // is initialized and satisfies all invariants of `T`. + // Dropping the value in place is safe if that is the case. + unsafe { ptr::drop_in_place(self as *mut [MaybeUninit] as *mut [T]) } + } + } + + /// Gets a shared reference to the contained value. + /// + /// # Safety + /// + /// Calling this when the content is not yet fully initialized causes undefined + /// behavior: it is up to the caller to guarantee that every `MaybeUninit` in + /// the slice really is in an initialized state. + #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[inline(always)] + pub const unsafe fn assume_init_ref(&self) -> &[T] { + // SAFETY: casting `slice` to a `*const [T]` is safe since the caller guarantees that + // `slice` is initialized, and `MaybeUninit` is guaranteed to have the same layout as `T`. + // The pointer obtained is valid since it refers to memory owned by `slice` which is a + // reference and thus guaranteed to be valid for reads. + unsafe { &*(self as *const Self as *const [T]) } + } + + /// Gets a mutable (unique) reference to the contained value. + /// + /// # Safety + /// + /// Calling this when the content is not yet fully initialized causes undefined + /// behavior: it is up to the caller to guarantee that every `MaybeUninit` in the + /// slice really is in an initialized state. For instance, `.assume_init_mut()` cannot + /// be used to initialize a `MaybeUninit` slice. + #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[inline(always)] + pub const unsafe fn assume_init_mut(&mut self) -> &mut [T] { + // SAFETY: similar to safety notes for `slice_get_ref`, but we have a + // mutable reference which is also guaranteed to be valid for writes. + unsafe { &mut *(self as *mut Self as *mut [T]) } } } @@ -1479,7 +1588,7 @@ impl<'a, T> Drop for Guard<'a, T> { let initialized_part = &mut self.slice[..self.initialized]; // SAFETY: this raw sub-slice will contain only initialized objects. unsafe { - crate::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(initialized_part)); + initialized_part.assume_init_drop(); } } } diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 2d66e5c2f2a7a..b9bb6d6a13f7f 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -333,7 +333,7 @@ pub const fn size_of() -> usize { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_stable(feature = "const_size_of_val", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "const_size_of_val", since = "1.85.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "mem_size_of_val")] pub const fn size_of_val(val: &T) -> usize { // SAFETY: `val` is a reference, so it's a valid raw pointer @@ -484,7 +484,7 @@ pub const fn align_of() -> usize { #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_stable(feature = "const_align_of_val", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "const_align_of_val", since = "1.85.0")] #[allow(deprecated)] pub const fn align_of_val(val: &T) -> usize { // SAFETY: val is a reference, so it's a valid raw pointer @@ -725,7 +725,7 @@ pub unsafe fn uninitialized() -> T { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "const_swap", since = "1.85.0")] #[rustc_diagnostic_item = "mem_swap"] pub const fn swap(x: &mut T, y: &mut T) { // SAFETY: `&mut` guarantees these are typed readable and writable diff --git a/library/core/src/mem/transmutability.rs b/library/core/src/mem/transmutability.rs index 9cf587650d949..6a4f84c849cb1 100644 --- a/library/core/src/mem/transmutability.rs +++ b/library/core/src/mem/transmutability.rs @@ -84,9 +84,8 @@ use crate::marker::{ConstParamTy_, UnsizedConstParamTy}; /// `usize` is stable, but not portable. #[unstable(feature = "transmutability", issue = "99571")] #[lang = "transmute_trait"] -#[cfg_attr(bootstrap, rustc_deny_explicit_impl(implement_via_object = false))] -#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)] -#[cfg_attr(not(bootstrap), rustc_do_not_implement_via_object)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] #[rustc_coinductive] pub unsafe trait TransmuteFrom where diff --git a/library/core/src/net/display_buffer.rs b/library/core/src/net/display_buffer.rs index bab84a97308b3..625ad5401f5c0 100644 --- a/library/core/src/net/display_buffer.rs +++ b/library/core/src/net/display_buffer.rs @@ -2,23 +2,23 @@ use crate::mem::MaybeUninit; use crate::{fmt, str}; /// Used for slow path in `Display` implementations when alignment is required. -pub struct DisplayBuffer { +pub(super) struct DisplayBuffer { buf: [MaybeUninit; SIZE], len: usize, } impl DisplayBuffer { #[inline] - pub const fn new() -> Self { + pub(super) const fn new() -> Self { Self { buf: [MaybeUninit::uninit(); SIZE], len: 0 } } #[inline] - pub fn as_str(&self) -> &str { + pub(super) fn as_str(&self) -> &str { // SAFETY: `buf` is only written to by the `fmt::Write::write_str` implementation // which writes a valid UTF-8 string to `buf` and correctly sets `len`. unsafe { - let s = MaybeUninit::slice_assume_init_ref(&self.buf[..self.len]); + let s = self.buf[..self.len].assume_init_ref(); str::from_utf8_unchecked(s) } } @@ -29,7 +29,7 @@ impl fmt::Write for DisplayBuffer { let bytes = s.as_bytes(); if let Some(buf) = self.buf.get_mut(self.len..(self.len + bytes.len())) { - MaybeUninit::copy_from_slice(buf, bytes); + buf.write_copy_of_slice(bytes); self.len += bytes.len(); Ok(()) } else { diff --git a/library/core/src/net/ip_addr.rs b/library/core/src/net/ip_addr.rs index 7dd5c21401264..b11ba05685352 100644 --- a/library/core/src/net/ip_addr.rs +++ b/library/core/src/net/ip_addr.rs @@ -1539,8 +1539,9 @@ impl Ipv6Addr { /// // Addresses reserved for benchmarking (`2001:2::/48`) /// assert_eq!(Ipv6Addr::new(0x2001, 2, 0, 0, 0, 0, 0, 1,).is_global(), false); /// - /// // Addresses reserved for documentation (`2001:db8::/32`) + /// // Addresses reserved for documentation (`2001:db8::/32` and `3fff::/20`) /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1).is_global(), false); + /// assert_eq!(Ipv6Addr::new(0x3fff, 0, 0, 0, 0, 0, 0, 0).is_global(), false); /// /// // Unique local addresses (`fc00::/7`) /// assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 1).is_global(), false); @@ -1686,11 +1687,12 @@ impl Ipv6Addr { } /// Returns [`true`] if this is an address reserved for documentation - /// (`2001:db8::/32`). + /// (`2001:db8::/32` and `3fff::/20`). /// - /// This property is defined in [IETF RFC 3849]. + /// This property is defined by [IETF RFC 3849] and [IETF RFC 9637]. /// /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849 + /// [IETF RFC 9637]: https://tools.ietf.org/html/rfc9637 /// /// # Examples /// @@ -1701,12 +1703,13 @@ impl Ipv6Addr { /// /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_documentation(), false); /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_documentation(), true); + /// assert_eq!(Ipv6Addr::new(0x3fff, 0, 0, 0, 0, 0, 0, 0).is_documentation(), true); /// ``` #[unstable(feature = "ip", issue = "27709")] #[must_use] #[inline] pub const fn is_documentation(&self) -> bool { - (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) + matches!(self.segments(), [0x2001, 0xdb8, ..] | [0x3fff, 0..=0x0fff, ..]) } /// Returns [`true`] if this is an address reserved for benchmarking (`2001:2::/48`). diff --git a/library/core/src/num/dec2flt/decimal.rs b/library/core/src/num/dec2flt/decimal.rs index be9c0eccd5eb8..b37724ba62d5e 100644 --- a/library/core/src/num/dec2flt/decimal.rs +++ b/library/core/src/num/dec2flt/decimal.rs @@ -12,7 +12,7 @@ use crate::num::dec2flt::common::{ByteSlice, is_8digits}; #[derive(Clone)] -pub struct Decimal { +pub(super) struct Decimal { /// The number of significant digits in the decimal. pub num_digits: usize, /// The offset of the decimal point in the significant digits. @@ -55,13 +55,13 @@ impl Decimal { /// /// In Python: /// `-emin + p2 + math.floor((emin+ 1)*math.log(2, b)-math.log(1-2**(-p2), b))` - pub const MAX_DIGITS: usize = 768; + pub(super) const MAX_DIGITS: usize = 768; /// The max digits that can be exactly represented in a 64-bit integer. - pub const MAX_DIGITS_WITHOUT_OVERFLOW: usize = 19; - pub const DECIMAL_POINT_RANGE: i32 = 2047; + pub(super) const MAX_DIGITS_WITHOUT_OVERFLOW: usize = 19; + pub(super) const DECIMAL_POINT_RANGE: i32 = 2047; /// Append a digit to the buffer. - pub fn try_add_digit(&mut self, digit: u8) { + pub(super) fn try_add_digit(&mut self, digit: u8) { if self.num_digits < Self::MAX_DIGITS { self.digits[self.num_digits] = digit; } @@ -69,7 +69,7 @@ impl Decimal { } /// Trim trailing zeros from the buffer. - pub fn trim(&mut self) { + pub(super) fn trim(&mut self) { // All of the following calls to `Decimal::trim` can't panic because: // // 1. `parse_decimal` sets `num_digits` to a max of `Decimal::MAX_DIGITS`. @@ -83,7 +83,7 @@ impl Decimal { } } - pub fn round(&self) -> u64 { + pub(super) fn round(&self) -> u64 { if self.num_digits == 0 || self.decimal_point < 0 { return 0; } else if self.decimal_point > 18 { @@ -111,7 +111,7 @@ impl Decimal { } /// Computes decimal * 2^shift. - pub fn left_shift(&mut self, shift: usize) { + pub(super) fn left_shift(&mut self, shift: usize) { if self.num_digits == 0 { return; } @@ -152,7 +152,7 @@ impl Decimal { } /// Computes decimal * 2^-shift. - pub fn right_shift(&mut self, shift: usize) { + pub(super) fn right_shift(&mut self, shift: usize) { let mut read_index = 0; let mut write_index = 0; let mut n = 0_u64; @@ -202,7 +202,7 @@ impl Decimal { } /// Parse a big integer representation of the float as a decimal. -pub fn parse_decimal(mut s: &[u8]) -> Decimal { +pub(super) fn parse_decimal(mut s: &[u8]) -> Decimal { let mut d = Decimal::default(); let start = s; diff --git a/library/core/src/num/dec2flt/fpu.rs b/library/core/src/num/dec2flt/fpu.rs index 8d62684f8d383..daeee1755b0b5 100644 --- a/library/core/src/num/dec2flt/fpu.rs +++ b/library/core/src/num/dec2flt/fpu.rs @@ -1,7 +1,7 @@ //! Platform-specific, assembly instructions to avoid //! intermediate rounding on architectures with FPUs. -pub use fpu_precision::set_precision; +pub(super) use fpu_precision::set_precision; // On x86, the x87 FPU is used for float operations if the SSE/SSE2 extensions are not available. // The x87 FPU operates with 80 bits of precision by default, which means that operations will @@ -42,7 +42,7 @@ mod fpu_precision { /// - 0b10, double precision i.e., 64-bits /// - 0b11, double extended precision i.e., 80-bits (default state) /// The 0b01 value is reserved and should not be used. - pub struct FPUControlWord(u16); + pub(crate) struct FPUControlWord(u16); fn set_cw(cw: u16) { // SAFETY: the `fldcw` instruction has been audited to be able to work correctly with @@ -57,7 +57,7 @@ mod fpu_precision { } /// Sets the precision field of the FPU to `T` and returns a `FPUControlWord`. - pub fn set_precision() -> FPUControlWord { + pub(crate) fn set_precision() -> FPUControlWord { let mut cw = 0_u16; // Compute the value for the Precision Control field that is appropriate for `T`. @@ -97,5 +97,5 @@ mod fpu_precision { // precision of the computation is determined on a per-operation basis. #[cfg(any(not(target_arch = "x86"), target_feature = "sse2"))] mod fpu_precision { - pub fn set_precision() {} + pub(crate) fn set_precision() {} } diff --git a/library/core/src/num/dec2flt/table.rs b/library/core/src/num/dec2flt/table.rs index 4856074a62bd0..942c2eacfd276 100644 --- a/library/core/src/num/dec2flt/table.rs +++ b/library/core/src/num/dec2flt/table.rs @@ -6,16 +6,17 @@ //! //! DO NOT MODIFY: Generated by `src/etc/dec2flt_table.py` -pub const SMALLEST_POWER_OF_FIVE: i32 = -342; -pub const LARGEST_POWER_OF_FIVE: i32 = 308; -pub const N_POWERS_OF_FIVE: usize = (LARGEST_POWER_OF_FIVE - SMALLEST_POWER_OF_FIVE + 1) as usize; +pub(super) const SMALLEST_POWER_OF_FIVE: i32 = -342; +pub(super) const LARGEST_POWER_OF_FIVE: i32 = 308; +pub(super) const N_POWERS_OF_FIVE: usize = + (LARGEST_POWER_OF_FIVE - SMALLEST_POWER_OF_FIVE + 1) as usize; // Use static to avoid long compile times: Rust compiler errors // can have the entire table compiled multiple times, and then // emit code multiple times, even if it's stripped out in // the final binary. #[rustfmt::skip] -pub static POWER_OF_FIVE_128: [(u64, u64); N_POWERS_OF_FIVE] = [ +pub(super) static POWER_OF_FIVE_128: [(u64, u64); N_POWERS_OF_FIVE] = [ (0xeef453d6923bd65a, 0x113faa2906a13b3f), // 5^-342 (0x9558b4661b6565f8, 0x4ac7ca59a424c507), // 5^-341 (0xbaaee17fa23ebf76, 0x5d79bcf00d2df649), // 5^-340 diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 4ebeaf046114a..5e45974b3d422 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -504,7 +504,6 @@ impl f128 { /// /// ```rust /// #![feature(f128)] - /// #![feature(float_next_up_down)] /// # // FIXME(f16_f128): remove when `eqtf2` is available /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// @@ -516,13 +515,15 @@ impl f128 { /// # } /// ``` /// + /// This operation corresponds to IEEE-754 `nextUp`. + /// /// [`NEG_INFINITY`]: Self::NEG_INFINITY /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] + #[doc(alias = "nextUp")] #[unstable(feature = "f128", issue = "116909")] - // #[unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_up(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -558,7 +559,6 @@ impl f128 { /// /// ```rust /// #![feature(f128)] - /// #![feature(float_next_up_down)] /// # // FIXME(f16_f128): remove when `eqtf2` is available /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// @@ -570,13 +570,15 @@ impl f128 { /// # } /// ``` /// + /// This operation corresponds to IEEE-754 `nextDown`. + /// /// [`NEG_INFINITY`]: Self::NEG_INFINITY /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] + #[doc(alias = "nextDown")] #[unstable(feature = "f128", issue = "116909")] - // #[unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_down(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -668,7 +670,8 @@ impl f128 { /// If one of the arguments is NaN, then the other argument is returned. /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. - /// This also matches the behavior of libm’s fmax. + /// This also matches the behavior of libm’s fmax. In particular, if the inputs compare equal + /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// ``` /// #![feature(f128)] @@ -694,7 +697,8 @@ impl f128 { /// If one of the arguments is NaN, then the other argument is returned. /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; /// this function handles all NaNs the same way and avoids minNum's problems with associativity. - /// This also matches the behavior of libm’s fmin. + /// This also matches the behavior of libm’s fmin. In particular, if the inputs compare equal + /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// ``` /// #![feature(f128)] diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index c82f0d7cd4ad5..e3176cd168852 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -497,7 +497,6 @@ impl f16 { /// /// ```rust /// #![feature(f16)] - /// #![feature(float_next_up_down)] /// # // FIXME(f16_f128): ABI issues on MSVC /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// @@ -509,13 +508,15 @@ impl f16 { /// # } /// ``` /// + /// This operation corresponds to IEEE-754 `nextUp`. + /// /// [`NEG_INFINITY`]: Self::NEG_INFINITY /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] + #[doc(alias = "nextUp")] #[unstable(feature = "f16", issue = "116909")] - // #[unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_up(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -551,7 +552,6 @@ impl f16 { /// /// ```rust /// #![feature(f16)] - /// #![feature(float_next_up_down)] /// # // FIXME(f16_f128): ABI issues on MSVC /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// @@ -563,13 +563,15 @@ impl f16 { /// # } /// ``` /// + /// This operation corresponds to IEEE-754 `nextDown`. + /// /// [`NEG_INFINITY`]: Self::NEG_INFINITY /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] + #[doc(alias = "nextDown")] #[unstable(feature = "f16", issue = "116909")] - // #[unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_down(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -660,7 +662,8 @@ impl f16 { /// If one of the arguments is NaN, then the other argument is returned. /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. - /// This also matches the behavior of libm’s fmax. + /// This also matches the behavior of libm’s fmax. In particular, if the inputs compare equal + /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// ``` /// #![feature(f16)] @@ -685,7 +688,8 @@ impl f16 { /// If one of the arguments is NaN, then the other argument is returned. /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; /// this function handles all NaNs the same way and avoids minNum's problems with associativity. - /// This also matches the behavior of libm’s fmin. + /// This also matches the behavior of libm’s fmin. In particular, if the inputs compare equal + /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// ``` /// #![feature(f16)] diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 4c0d95f95e562..a200fd5318669 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -726,7 +726,6 @@ impl f32 { /// is finite `x == x.next_up().next_down()` also holds. /// /// ```rust - /// #![feature(float_next_up_down)] /// // f32::EPSILON is the difference between 1.0 and the next number up. /// assert_eq!(1.0f32.next_up(), 1.0 + f32::EPSILON); /// // But not for most numbers. @@ -734,12 +733,16 @@ impl f32 { /// assert_eq!(16777216f32.next_up(), 16777218.0); /// ``` /// + /// This operation corresponds to IEEE-754 `nextUp`. + /// /// [`NEG_INFINITY`]: Self::NEG_INFINITY /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] - #[unstable(feature = "float_next_up_down", issue = "91399")] + #[doc(alias = "nextUp")] + #[stable(feature = "float_next_up_down", since = "1.86.0")] + #[rustc_const_stable(feature = "float_next_up_down", since = "1.86.0")] pub const fn next_up(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -774,7 +777,6 @@ impl f32 { /// is finite `x == x.next_down().next_up()` also holds. /// /// ```rust - /// #![feature(float_next_up_down)] /// let x = 1.0f32; /// // Clamp value into range [0, 1). /// let clamped = x.clamp(0.0, 1.0f32.next_down()); @@ -782,12 +784,16 @@ impl f32 { /// assert_eq!(clamped.next_up(), 1.0); /// ``` /// + /// This operation corresponds to IEEE-754 `nextDown`. + /// /// [`NEG_INFINITY`]: Self::NEG_INFINITY /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] - #[unstable(feature = "float_next_up_down", issue = "91399")] + #[doc(alias = "nextDown")] + #[stable(feature = "float_next_up_down", since = "1.86.0")] + #[rustc_const_stable(feature = "float_next_up_down", since = "1.86.0")] pub const fn next_down(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -818,7 +824,7 @@ impl f32 { /// ``` #[must_use = "this returns the result of the operation, without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn recip(self) -> f32 { 1.0 / self @@ -836,7 +842,7 @@ impl f32 { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[stable(feature = "f32_deg_rad_conversions", since = "1.7.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn to_degrees(self) -> f32 { // Use a constant for better precision. @@ -856,7 +862,7 @@ impl f32 { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[stable(feature = "f32_deg_rad_conversions", since = "1.7.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn to_radians(self) -> f32 { const RADS_PER_DEG: f32 = consts::PI / 180.0; @@ -868,7 +874,8 @@ impl f32 { /// If one of the arguments is NaN, then the other argument is returned. /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. - /// This also matches the behavior of libm’s fmax. + /// This also matches the behavior of libm’s fmax. In particular, if the inputs compare equal + /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// ``` /// let x = 1.0f32; @@ -878,7 +885,7 @@ impl f32 { /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn max(self, other: f32) -> f32 { intrinsics::maxnumf32(self, other) @@ -889,7 +896,8 @@ impl f32 { /// If one of the arguments is NaN, then the other argument is returned. /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; /// this function handles all NaNs the same way and avoids minNum's problems with associativity. - /// This also matches the behavior of libm’s fmin. + /// This also matches the behavior of libm’s fmin. In particular, if the inputs compare equal + /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// ``` /// let x = 1.0f32; @@ -899,7 +907,7 @@ impl f32 { /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn min(self, other: f32) -> f32 { intrinsics::minnumf32(self, other) @@ -988,8 +996,8 @@ impl f32 { /// assert_eq!((-5.5f32).midpoint(8.0), 1.25); /// ``` #[inline] - #[stable(feature = "num_midpoint", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "num_midpoint", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "num_midpoint", since = "1.85.0")] + #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] pub const fn midpoint(self, other: f32) -> f32 { cfg_if! { // Allow faster implementation that have known good 64-bit float @@ -1396,7 +1404,7 @@ impl f32 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "clamp", since = "1.50.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn clamp(mut self, min: f32, max: f32) -> f32 { const_assert!( @@ -1433,7 +1441,7 @@ impl f32 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn abs(self) -> f32 { // SAFETY: this is actually a safe intrinsic @@ -1458,7 +1466,7 @@ impl f32 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn signum(self) -> f32 { if self.is_nan() { Self::NAN } else { 1.0_f32.copysign(self) } @@ -1493,7 +1501,7 @@ impl f32 { #[must_use = "method returns a new number and does not mutate the original value"] #[inline] #[stable(feature = "copysign", since = "1.35.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] pub const fn copysign(self, sign: f32) -> f32 { // SAFETY: this is actually a safe intrinsic unsafe { intrinsics::copysignf32(self, sign) } diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 77ca56df06705..de63a462b61ac 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -743,7 +743,6 @@ impl f64 { /// is finite `x == x.next_up().next_down()` also holds. /// /// ```rust - /// #![feature(float_next_up_down)] /// // f64::EPSILON is the difference between 1.0 and the next number up. /// assert_eq!(1.0f64.next_up(), 1.0 + f64::EPSILON); /// // But not for most numbers. @@ -751,12 +750,16 @@ impl f64 { /// assert_eq!(9007199254740992f64.next_up(), 9007199254740994.0); /// ``` /// + /// This operation corresponds to IEEE-754 `nextUp`. + /// /// [`NEG_INFINITY`]: Self::NEG_INFINITY /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] - #[unstable(feature = "float_next_up_down", issue = "91399")] + #[doc(alias = "nextUp")] + #[stable(feature = "float_next_up_down", since = "1.86.0")] + #[rustc_const_stable(feature = "float_next_up_down", since = "1.86.0")] pub const fn next_up(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -791,7 +794,6 @@ impl f64 { /// is finite `x == x.next_down().next_up()` also holds. /// /// ```rust - /// #![feature(float_next_up_down)] /// let x = 1.0f64; /// // Clamp value into range [0, 1). /// let clamped = x.clamp(0.0, 1.0f64.next_down()); @@ -799,12 +801,16 @@ impl f64 { /// assert_eq!(clamped.next_up(), 1.0); /// ``` /// + /// This operation corresponds to IEEE-754 `nextDown`. + /// /// [`NEG_INFINITY`]: Self::NEG_INFINITY /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX #[inline] - #[unstable(feature = "float_next_up_down", issue = "91399")] + #[doc(alias = "nextDown")] + #[stable(feature = "float_next_up_down", since = "1.86.0")] + #[rustc_const_stable(feature = "float_next_up_down", since = "1.86.0")] pub const fn next_down(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -835,7 +841,7 @@ impl f64 { /// ``` #[must_use = "this returns the result of the operation, without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn recip(self) -> f64 { 1.0 / self @@ -853,7 +859,7 @@ impl f64 { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn to_degrees(self) -> f64 { // The division here is correctly rounded with respect to the true @@ -874,7 +880,7 @@ impl f64 { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn to_radians(self) -> f64 { const RADS_PER_DEG: f64 = consts::PI / 180.0; @@ -886,7 +892,8 @@ impl f64 { /// If one of the arguments is NaN, then the other argument is returned. /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. - /// This also matches the behavior of libm’s fmax. + /// This also matches the behavior of libm’s fmax. In particular, if the inputs compare equal + /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// ``` /// let x = 1.0_f64; @@ -896,7 +903,7 @@ impl f64 { /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn max(self, other: f64) -> f64 { intrinsics::maxnumf64(self, other) @@ -907,7 +914,8 @@ impl f64 { /// If one of the arguments is NaN, then the other argument is returned. /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; /// this function handles all NaNs the same way and avoids minNum's problems with associativity. - /// This also matches the behavior of libm’s fmin. + /// This also matches the behavior of libm’s fmin. In particular, if the inputs compare equal + /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. /// /// ``` /// let x = 1.0_f64; @@ -917,7 +925,7 @@ impl f64 { /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn min(self, other: f64) -> f64 { intrinsics::minnumf64(self, other) @@ -1006,8 +1014,8 @@ impl f64 { /// assert_eq!((-5.5f64).midpoint(8.0), 1.25); /// ``` #[inline] - #[stable(feature = "num_midpoint", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "num_midpoint", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "num_midpoint", since = "1.85.0")] + #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] pub const fn midpoint(self, other: f64) -> f64 { const LO: f64 = f64::MIN_POSITIVE * 2.; const HI: f64 = f64::MAX / 2.; @@ -1396,7 +1404,7 @@ impl f64 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "clamp", since = "1.50.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn clamp(mut self, min: f64, max: f64) -> f64 { const_assert!( @@ -1433,7 +1441,7 @@ impl f64 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn abs(self) -> f64 { // SAFETY: this is actually a safe intrinsic @@ -1458,7 +1466,7 @@ impl f64 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn signum(self) -> f64 { if self.is_nan() { Self::NAN } else { 1.0_f64.copysign(self) } @@ -1492,7 +1500,7 @@ impl f64 { /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "copysign", since = "1.35.0")] - #[rustc_const_stable(feature = "const_float_methods", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_float_methods", since = "1.85.0")] #[inline] pub const fn copysign(self, sign: f64) -> f64 { // SAFETY: this is actually a safe intrinsic diff --git a/library/core/src/num/flt2dec/mod.rs b/library/core/src/num/flt2dec/mod.rs index d6413fadc3381..7601e3e2c58a2 100644 --- a/library/core/src/num/flt2dec/mod.rs +++ b/library/core/src/num/flt2dec/mod.rs @@ -210,10 +210,10 @@ fn digits_to_dec_str<'a>( if frac_digits > buf.len() && frac_digits - buf.len() > minus_exp { parts[3] = MaybeUninit::new(Part::Zero((frac_digits - buf.len()) - minus_exp)); // SAFETY: we just initialized the elements `..4`. - unsafe { MaybeUninit::slice_assume_init_ref(&parts[..4]) } + unsafe { parts[..4].assume_init_ref() } } else { // SAFETY: we just initialized the elements `..3`. - unsafe { MaybeUninit::slice_assume_init_ref(&parts[..3]) } + unsafe { parts[..3].assume_init_ref() } } } else { let exp = exp as usize; @@ -225,10 +225,10 @@ fn digits_to_dec_str<'a>( if frac_digits > buf.len() - exp { parts[3] = MaybeUninit::new(Part::Zero(frac_digits - (buf.len() - exp))); // SAFETY: we just initialized the elements `..4`. - unsafe { MaybeUninit::slice_assume_init_ref(&parts[..4]) } + unsafe { parts[..4].assume_init_ref() } } else { // SAFETY: we just initialized the elements `..3`. - unsafe { MaybeUninit::slice_assume_init_ref(&parts[..3]) } + unsafe { parts[..3].assume_init_ref() } } } else { // the decimal point is after rendered digits: [1234][____0000] or [1234][__][.][__]. @@ -238,10 +238,10 @@ fn digits_to_dec_str<'a>( parts[2] = MaybeUninit::new(Part::Copy(b".")); parts[3] = MaybeUninit::new(Part::Zero(frac_digits)); // SAFETY: we just initialized the elements `..4`. - unsafe { MaybeUninit::slice_assume_init_ref(&parts[..4]) } + unsafe { parts[..4].assume_init_ref() } } else { // SAFETY: we just initialized the elements `..2`. - unsafe { MaybeUninit::slice_assume_init_ref(&parts[..2]) } + unsafe { parts[..2].assume_init_ref() } } } } @@ -292,7 +292,7 @@ fn digits_to_exp_str<'a>( parts[n + 1] = MaybeUninit::new(Part::Num(exp as u16)); } // SAFETY: we just initialized the elements `..n + 2`. - unsafe { MaybeUninit::slice_assume_init_ref(&parts[..n + 2]) } + unsafe { parts[..n + 2].assume_init_ref() } } /// Sign formatting options. @@ -366,12 +366,12 @@ where FullDecoded::Nan => { parts[0] = MaybeUninit::new(Part::Copy(b"NaN")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Infinite => { parts[0] = MaybeUninit::new(Part::Copy(b"inf")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Zero => { if frac_digits > 0 { @@ -381,14 +381,14 @@ where Formatted { sign, // SAFETY: we just initialized the elements `..2`. - parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..2]) }, + parts: unsafe { parts[..2].assume_init_ref() }, } } else { parts[0] = MaybeUninit::new(Part::Copy(b"0")); Formatted { sign, // SAFETY: we just initialized the elements `..1`. - parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) }, + parts: unsafe { parts[..1].assume_init_ref() }, } } } @@ -442,12 +442,12 @@ where FullDecoded::Nan => { parts[0] = MaybeUninit::new(Part::Copy(b"NaN")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Infinite => { parts[0] = MaybeUninit::new(Part::Copy(b"inf")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Zero => { parts[0] = if dec_bounds.0 <= 0 && 0 < dec_bounds.1 { @@ -456,7 +456,7 @@ where MaybeUninit::new(Part::Copy(if upper { b"0E0" } else { b"0e0" })) }; // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Finite(ref decoded) => { let (buf, exp) = format_shortest(decoded, buf); @@ -533,12 +533,12 @@ where FullDecoded::Nan => { parts[0] = MaybeUninit::new(Part::Copy(b"NaN")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Infinite => { parts[0] = MaybeUninit::new(Part::Copy(b"inf")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Zero => { if ndigits > 1 { @@ -549,14 +549,14 @@ where Formatted { sign, // SAFETY: we just initialized the elements `..3`. - parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..3]) }, + parts: unsafe { parts[..3].assume_init_ref() }, } } else { parts[0] = MaybeUninit::new(Part::Copy(if upper { b"0E0" } else { b"0e0" })); Formatted { sign, // SAFETY: we just initialized the elements `..1`. - parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) }, + parts: unsafe { parts[..1].assume_init_ref() }, } } } @@ -607,12 +607,12 @@ where FullDecoded::Nan => { parts[0] = MaybeUninit::new(Part::Copy(b"NaN")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Infinite => { parts[0] = MaybeUninit::new(Part::Copy(b"inf")); // SAFETY: we just initialized the elements `..1`. - Formatted { sign, parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) } } + Formatted { sign, parts: unsafe { parts[..1].assume_init_ref() } } } FullDecoded::Zero => { if frac_digits > 0 { @@ -622,14 +622,14 @@ where Formatted { sign, // SAFETY: we just initialized the elements `..2`. - parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..2]) }, + parts: unsafe { parts[..2].assume_init_ref() }, } } else { parts[0] = MaybeUninit::new(Part::Copy(b"0")); Formatted { sign, // SAFETY: we just initialized the elements `..1`. - parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) }, + parts: unsafe { parts[..1].assume_init_ref() }, } } } @@ -654,14 +654,14 @@ where Formatted { sign, // SAFETY: we just initialized the elements `..2`. - parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..2]) }, + parts: unsafe { parts[..2].assume_init_ref() }, } } else { parts[0] = MaybeUninit::new(Part::Copy(b"0")); Formatted { sign, // SAFETY: we just initialized the elements `..1`. - parts: unsafe { MaybeUninit::slice_assume_init_ref(&parts[..1]) }, + parts: unsafe { parts[..1].assume_init_ref() }, } } } else { diff --git a/library/core/src/num/flt2dec/strategy/dragon.rs b/library/core/src/num/flt2dec/strategy/dragon.rs index e801f07b3bc0e..dd73e4b4846d5 100644 --- a/library/core/src/num/flt2dec/strategy/dragon.rs +++ b/library/core/src/num/flt2dec/strategy/dragon.rs @@ -247,7 +247,7 @@ pub fn format_shortest<'a>( // it seems that this condition is very hard to satisfy (possibly impossible), // but we are just being safe and consistent here. // SAFETY: we initialized that memory above. - if let Some(c) = round_up(unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..i]) }) { + if let Some(c) = round_up(unsafe { buf[..i].assume_init_mut() }) { buf[i] = MaybeUninit::new(c); i += 1; k += 1; @@ -255,7 +255,7 @@ pub fn format_shortest<'a>( } // SAFETY: we initialized that memory above. - (unsafe { MaybeUninit::slice_assume_init_ref(&buf[..i]) }, k) + (unsafe { buf[..i].assume_init_ref() }, k) } /// The exact and fixed mode implementation for Dragon. @@ -333,7 +333,7 @@ pub fn format_exact<'a>( *c = MaybeUninit::new(b'0'); } // SAFETY: we initialized that memory above. - return (unsafe { MaybeUninit::slice_assume_init_ref(&buf[..len]) }, k); + return (unsafe { buf[..len].assume_init_ref() }, k); } let mut d = 0; @@ -372,7 +372,7 @@ pub fn format_exact<'a>( // if rounding up changes the length, the exponent should also change. // but we've been requested a fixed number of digits, so do not alter the buffer... // SAFETY: we initialized that memory above. - if let Some(c) = round_up(unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..len]) }) { + if let Some(c) = round_up(unsafe { buf[..len].assume_init_mut() }) { // ...unless we've been requested the fixed precision instead. // we also need to check that, if the original buffer was empty, // the additional digit can only be added when `k == limit` (edge case). @@ -385,5 +385,5 @@ pub fn format_exact<'a>( } // SAFETY: we initialized that memory above. - (unsafe { MaybeUninit::slice_assume_init_ref(&buf[..len]) }, k) + (unsafe { buf[..len].assume_init_ref() }, k) } diff --git a/library/core/src/num/flt2dec/strategy/grisu.rs b/library/core/src/num/flt2dec/strategy/grisu.rs index bdf544a4133bb..2816de4c63339 100644 --- a/library/core/src/num/flt2dec/strategy/grisu.rs +++ b/library/core/src/num/flt2dec/strategy/grisu.rs @@ -275,7 +275,7 @@ pub fn format_shortest_opt<'a>( let ten_kappa = (ten_kappa as u64) << e; // scale 10^kappa back to the shared exponent return round_and_weed( // SAFETY: we initialized that memory above. - unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..i]) }, + unsafe { buf[..i].assume_init_mut() }, exp, plus1rem, delta1, @@ -324,7 +324,7 @@ pub fn format_shortest_opt<'a>( let ten_kappa = 1 << e; // implicit divisor return round_and_weed( // SAFETY: we initialized that memory above. - unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..i]) }, + unsafe { buf[..i].assume_init_mut() }, exp, r, threshold, @@ -713,7 +713,7 @@ pub fn format_exact_opt<'a>( // `10^kappa` did not overflow after all, the second check is fine. if ten_kappa - remainder > remainder && ten_kappa - 2 * remainder >= 2 * ulp { // SAFETY: our caller initialized that memory. - return Some((unsafe { MaybeUninit::slice_assume_init_ref(&buf[..len]) }, exp)); + return Some((unsafe { buf[..len].assume_init_ref() }, exp)); } // :<------- remainder ------>| : @@ -736,7 +736,7 @@ pub fn format_exact_opt<'a>( if remainder > ulp && ten_kappa - (remainder - ulp) <= remainder - ulp { if let Some(c) = // SAFETY: our caller must have initialized that memory. - round_up(unsafe { MaybeUninit::slice_assume_init_mut(&mut buf[..len]) }) + round_up(unsafe { buf[..len].assume_init_mut() }) { // only add an additional digit when we've been requested the fixed precision. // we also need to check that, if the original buffer was empty, @@ -748,7 +748,7 @@ pub fn format_exact_opt<'a>( } } // SAFETY: we and our caller initialized that memory. - return Some((unsafe { MaybeUninit::slice_assume_init_ref(&buf[..len]) }, exp)); + return Some((unsafe { buf[..len].assume_init_ref() }, exp)); } // otherwise we are doomed (i.e., some values between `v - 1 ulp` and `v + 1 ulp` are diff --git a/library/core/src/num/int_log10.rs b/library/core/src/num/int_log10.rs index 0ce31b40a3845..28a3f5d880ad7 100644 --- a/library/core/src/num/int_log10.rs +++ b/library/core/src/num/int_log10.rs @@ -3,7 +3,7 @@ // 0 < val <= u8::MAX #[inline] -pub const fn u8(val: u8) -> u32 { +pub(super) const fn u8(val: u8) -> u32 { let val = val as u32; // For better performance, avoid branches by assembling the solution @@ -45,13 +45,13 @@ const fn less_than_5(val: u32) -> u32 { // 0 < val <= u16::MAX #[inline] -pub const fn u16(val: u16) -> u32 { +pub(super) const fn u16(val: u16) -> u32 { less_than_5(val as u32) } // 0 < val <= u32::MAX #[inline] -pub const fn u32(mut val: u32) -> u32 { +pub(super) const fn u32(mut val: u32) -> u32 { let mut log = 0; if val >= 100_000 { val /= 100_000; @@ -62,7 +62,7 @@ pub const fn u32(mut val: u32) -> u32 { // 0 < val <= u64::MAX #[inline] -pub const fn u64(mut val: u64) -> u32 { +pub(super) const fn u64(mut val: u64) -> u32 { let mut log = 0; if val >= 10_000_000_000 { val /= 10_000_000_000; @@ -77,7 +77,7 @@ pub const fn u64(mut val: u64) -> u32 { // 0 < val <= u128::MAX #[inline] -pub const fn u128(mut val: u128) -> u32 { +pub(super) const fn u128(mut val: u128) -> u32 { let mut log = 0; if val >= 100_000_000_000_000_000_000_000_000_000_000 { val /= 100_000_000_000_000_000_000_000_000_000_000; @@ -93,49 +93,49 @@ pub const fn u128(mut val: u128) -> u32 { #[cfg(target_pointer_width = "16")] #[inline] -pub const fn usize(val: usize) -> u32 { +pub(super) const fn usize(val: usize) -> u32 { u16(val as _) } #[cfg(target_pointer_width = "32")] #[inline] -pub const fn usize(val: usize) -> u32 { +pub(super) const fn usize(val: usize) -> u32 { u32(val as _) } #[cfg(target_pointer_width = "64")] #[inline] -pub const fn usize(val: usize) -> u32 { +pub(super) const fn usize(val: usize) -> u32 { u64(val as _) } // 0 < val <= i8::MAX #[inline] -pub const fn i8(val: i8) -> u32 { +pub(super) const fn i8(val: i8) -> u32 { u8(val as u8) } // 0 < val <= i16::MAX #[inline] -pub const fn i16(val: i16) -> u32 { +pub(super) const fn i16(val: i16) -> u32 { u16(val as u16) } // 0 < val <= i32::MAX #[inline] -pub const fn i32(val: i32) -> u32 { +pub(super) const fn i32(val: i32) -> u32 { u32(val as u32) } // 0 < val <= i64::MAX #[inline] -pub const fn i64(val: i64) -> u32 { +pub(super) const fn i64(val: i64) -> u32 { u64(val as u64) } // 0 < val <= i128::MAX #[inline] -pub const fn i128(val: i128) -> u32 { +pub(super) const fn i128(val: i128) -> u32 { u128(val as u128) } @@ -143,6 +143,6 @@ pub const fn i128(val: i128) -> u32 { /// on every single primitive type. #[cold] #[track_caller] -pub const fn panic_for_nonpositive_argument() -> ! { +pub(super) const fn panic_for_nonpositive_argument() -> ! { panic!("argument of integer logarithm must be positive") } diff --git a/library/core/src/num/int_sqrt.rs b/library/core/src/num/int_sqrt.rs index 601e81f69930f..c7a322c08c139 100644 --- a/library/core/src/num/int_sqrt.rs +++ b/library/core/src/num/int_sqrt.rs @@ -37,7 +37,7 @@ const U8_ISQRT_WITH_REMAINDER: [(u8, u8); 256] = { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] -pub const fn u8(n: u8) -> u8 { +pub(super) const fn u8(n: u8) -> u8 { U8_ISQRT_WITH_REMAINDER[n as usize].0 } @@ -58,7 +58,7 @@ macro_rules! signed_fn { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const unsafe fn $SignedT(n: $SignedT) -> $SignedT { + pub(super) const unsafe fn $SignedT(n: $SignedT) -> $SignedT { debug_assert!(n >= 0, "Negative input inside `isqrt`."); $UnsignedT(n as $UnsignedT) as $SignedT } @@ -83,7 +83,7 @@ macro_rules! unsigned_fn { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn $UnsignedT(mut n: $UnsignedT) -> $UnsignedT { + pub(super) const fn $UnsignedT(mut n: $UnsignedT) -> $UnsignedT { if n <= <$HalfBitsT>::MAX as $UnsignedT { $HalfBitsT(n as $HalfBitsT) as $UnsignedT } else { @@ -311,6 +311,6 @@ unsigned_fn!(u128, u64, u128_stages); /// on every single primitive type. #[cold] #[track_caller] -pub const fn panic_for_negative_argument() -> ! { +pub(super) const fn panic_for_negative_argument() -> ! { panic!("argument of integer square root cannot be negative") } diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 1f5b8ce6c6e22..55f4ccd958e77 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -51,6 +51,10 @@ mod overflow_panic; mod saturating; mod wrapping; +/// 100% perma-unstable +#[doc(hidden)] +pub mod niche_types; + #[stable(feature = "rust1", since = "1.0.0")] #[cfg(not(no_fp_fmt_parse))] pub use dec2flt::ParseFloatError; @@ -138,8 +142,8 @@ macro_rules! midpoint_impl { #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(4), 2);")] #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".midpoint(4), 2);")] /// ``` - #[stable(feature = "num_midpoint", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "num_midpoint", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "num_midpoint", since = "1.85.0")] + #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -192,8 +196,8 @@ macro_rules! midpoint_impl { #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".midpoint(4), 2);")] #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".midpoint(4), 2);")] /// ``` - #[stable(feature = "num_midpoint", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "num_midpoint", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "num_midpoint", since = "1.85.0")] + #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1318,20 +1322,6 @@ pub enum FpCategory { Normal, } -macro_rules! from_str_radix_int_impl { - ($($t:ty)*) => {$( - #[stable(feature = "rust1", since = "1.0.0")] - impl FromStr for $t { - type Err = ParseIntError; - #[inline] - fn from_str(src: &str) -> Result { - <$t>::from_str_radix(src, 10) - } - } - )*} -} -from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 } - /// Determines if a string of text of that length of that radix could be guaranteed to be /// stored in the given type T. /// Note that if the radix is known to the compiler, it is just the check of digits.len that @@ -1347,18 +1337,58 @@ pub const fn can_not_overflow(radix: u32, is_signed_ty: bool, digits: &[u8]) #[cfg_attr(feature = "panic_immediate_abort", inline)] #[cold] #[track_caller] -const fn from_str_radix_panic(radix: u32) -> ! { +const fn from_ascii_radix_panic(radix: u32) -> ! { const_panic!( - "from_str_radix_int: must lie in the range `[2, 36]`", - "from_str_radix_int: must lie in the range `[2, 36]` - found {radix}", + "from_ascii_radix: radix must lie in the range `[2, 36]`", + "from_ascii_radix: radix must lie in the range `[2, 36]` - found {radix}", radix: u32 = radix, ) } -macro_rules! from_str_radix { +macro_rules! from_str_int_impl { ($signedness:ident $($int_ty:ty)+) => {$( + #[stable(feature = "rust1", since = "1.0.0")] + impl FromStr for $int_ty { + type Err = ParseIntError; + + /// Parses an integer from a string slice with decimal digits. + /// + /// The characters are expected to be an optional + #[doc = sign_dependent_expr!{ + $signedness ? + if signed { + " `+` or `-` " + } + if unsigned { + " `+` " + } + }] + /// sign followed by only digits. Leading and trailing non-digit characters (including + /// whitespace) represent an error. Underscores (which are accepted in Rust literals) + /// also represent an error. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// use std::str::FromStr; + /// + #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_str(\"+10\"), Ok(10));")] + /// ``` + /// Trailing space returns error: + /// ``` + /// # use std::str::FromStr; + /// # + #[doc = concat!("assert!(", stringify!($int_ty), "::from_str(\"1 \").is_err());")] + /// ``` + #[inline] + fn from_str(src: &str) -> Result<$int_ty, ParseIntError> { + <$int_ty>::from_str_radix(src, 10) + } + } + impl $int_ty { - /// Converts a string slice in a given base to an integer. + /// Parses an integer from a string slice with digits in a given base. /// /// The string is expected to be an optional #[doc = sign_dependent_expr!{ @@ -1371,7 +1401,7 @@ macro_rules! from_str_radix { } }] /// sign followed by only digits. Leading and trailing non-digit characters (including - /// whitespace) represent an error. Underscores (which are accepted in rust literals) + /// whitespace) represent an error. Underscores (which are accepted in Rust literals) /// also represent an error. /// /// Digits are a subset of these characters, depending on `radix`: @@ -1397,11 +1427,92 @@ macro_rules! from_str_radix { #[rustc_const_stable(feature = "const_int_from_str", since = "1.82.0")] #[inline] pub const fn from_str_radix(src: &str, radix: u32) -> Result<$int_ty, ParseIntError> { + <$int_ty>::from_ascii_radix(src.as_bytes(), radix) + } + + /// Parses an integer from an ASCII-byte slice with decimal digits. + /// + /// The characters are expected to be an optional + #[doc = sign_dependent_expr!{ + $signedness ? + if signed { + " `+` or `-` " + } + if unsigned { + " `+` " + } + }] + /// sign followed by only digits. Leading and trailing non-digit characters (including + /// whitespace) represent an error. Underscores (which are accepted in Rust literals) + /// also represent an error. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// #![feature(int_from_ascii)] + /// + #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_ascii(b\"+10\"), Ok(10));")] + /// ``` + /// Trailing space returns error: + /// ``` + /// # #![feature(int_from_ascii)] + /// # + #[doc = concat!("assert!(", stringify!($int_ty), "::from_ascii(b\"1 \").is_err());")] + /// ``` + #[unstable(feature = "int_from_ascii", issue = "134821")] + #[inline] + pub const fn from_ascii(src: &[u8]) -> Result<$int_ty, ParseIntError> { + <$int_ty>::from_ascii_radix(src, 10) + } + + /// Parses an integer from an ASCII-byte slice with digits in a given base. + /// + /// The characters are expected to be an optional + #[doc = sign_dependent_expr!{ + $signedness ? + if signed { + " `+` or `-` " + } + if unsigned { + " `+` " + } + }] + /// sign followed by only digits. Leading and trailing non-digit characters (including + /// whitespace) represent an error. Underscores (which are accepted in Rust literals) + /// also represent an error. + /// + /// Digits are a subset of these characters, depending on `radix`: + /// * `0-9` + /// * `a-z` + /// * `A-Z` + /// + /// # Panics + /// + /// This function panics if `radix` is not in the range from 2 to 36. + /// + /// # Examples + /// + /// Basic usage: + /// ``` + /// #![feature(int_from_ascii)] + /// + #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_ascii_radix(b\"A\", 16), Ok(10));")] + /// ``` + /// Trailing space returns error: + /// ``` + /// # #![feature(int_from_ascii)] + /// # + #[doc = concat!("assert!(", stringify!($int_ty), "::from_ascii_radix(b\"1 \", 10).is_err());")] + /// ``` + #[unstable(feature = "int_from_ascii", issue = "134821")] + #[inline] + pub const fn from_ascii_radix(src: &[u8], radix: u32) -> Result<$int_ty, ParseIntError> { use self::IntErrorKind::*; use self::ParseIntError as PIE; if 2 > radix || radix > 36 { - from_str_radix_panic(radix); + from_ascii_radix_panic(radix); } if src.is_empty() { @@ -1411,12 +1522,6 @@ macro_rules! from_str_radix { #[allow(unused_comparisons)] let is_signed_ty = 0 > <$int_ty>::MIN; - // all valid digits are ascii, so we will just iterate over the utf8 bytes - // and cast them to chars. .to_digit() will safely return None for anything - // other than a valid ascii digit for the given radix, including the first-byte - // of multi-byte sequences - let src = src.as_bytes(); - let (is_positive, mut digits) = match src { [b'+' | b'-'] => { return Err(PIE { kind: InvalidDigit }); @@ -1492,67 +1597,8 @@ macro_rules! from_str_radix { Ok(result) } } - )+} -} - -from_str_radix! { unsigned u8 u16 u32 u64 u128 } -from_str_radix! { signed i8 i16 i32 i64 i128 } - -// Re-use the relevant implementation of from_str_radix for isize and usize to avoid outputting two -// identical functions. -macro_rules! from_str_radix_size_impl { - ($($signedness:ident $t:ident $size:ty),*) => {$( - impl $size { - /// Converts a string slice in a given base to an integer. - /// - /// The string is expected to be an optional - #[doc = sign_dependent_expr!{ - $signedness ? - if signed { - " `+` or `-` " - } - if unsigned { - " `+` " - } - }] - /// sign followed by only digits. Leading and trailing non-digit characters (including - /// whitespace) represent an error. Underscores (which are accepted in rust literals) - /// also represent an error. - /// - /// Digits are a subset of these characters, depending on `radix`: - /// * `0-9` - /// * `a-z` - /// * `A-Z` - /// - /// # Panics - /// - /// This function panics if `radix` is not in the range from 2 to 36. - /// - /// # Examples - /// - /// Basic usage: - /// ``` - #[doc = concat!("assert_eq!(", stringify!($size), "::from_str_radix(\"A\", 16), Ok(10));")] - /// ``` - /// Trailing space returns error: - /// ``` - #[doc = concat!("assert!(", stringify!($size), "::from_str_radix(\"1 \", 10).is_err());")] - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_int_from_str", since = "1.82.0")] - #[inline] - pub const fn from_str_radix(src: &str, radix: u32) -> Result<$size, ParseIntError> { - match <$t>::from_str_radix(src, radix) { - Ok(x) => Ok(x as $size), - Err(e) => Err(e), - } - } - })*} + )*} } -#[cfg(target_pointer_width = "16")] -from_str_radix_size_impl! { signed i16 isize, unsigned u16 usize } -#[cfg(target_pointer_width = "32")] -from_str_radix_size_impl! { signed i32 isize, unsigned u32 usize } -#[cfg(target_pointer_width = "64")] -from_str_radix_size_impl! { signed i64 isize, unsigned u64 usize } +from_str_int_impl! { signed isize i8 i16 i32 i64 i128 } +from_str_int_impl! { unsigned usize u8 u16 u32 u64 u128 } diff --git a/library/core/src/num/niche_types.rs b/library/core/src/num/niche_types.rs new file mode 100644 index 0000000000000..47ff4254e533b --- /dev/null +++ b/library/core/src/num/niche_types.rs @@ -0,0 +1,178 @@ +#![unstable( + feature = "temporary_niche_types", + issue = "none", + reason = "for core, alloc, and std internals until pattern types are further along" +)] + +use crate::cmp::Ordering; +use crate::fmt; +use crate::hash::{Hash, Hasher}; +use crate::marker::StructuralPartialEq; + +macro_rules! define_valid_range_type { + ($( + $(#[$m:meta])* + $vis:vis struct $name:ident($int:ident as $uint:ident in $low:literal..=$high:literal); + )+) => {$( + #[derive(Clone, Copy, Eq)] + #[repr(transparent)] + #[rustc_layout_scalar_valid_range_start($low)] + #[rustc_layout_scalar_valid_range_end($high)] + $(#[$m])* + $vis struct $name($int); + + const _: () = { + // With the `valid_range` attributes, it's always specified as unsigned + assert!(<$uint>::MIN == 0); + let ulow: $uint = $low; + let uhigh: $uint = $high; + assert!(ulow <= uhigh); + + assert!(size_of::<$int>() == size_of::<$uint>()); + }; + + impl $name { + #[inline] + pub const fn new(val: $int) -> Option { + if (val as $uint) >= ($low as $uint) && (val as $uint) <= ($high as $uint) { + // SAFETY: just checked the inclusive range + Some(unsafe { $name(val) }) + } else { + None + } + } + + /// Constructs an instance of this type from the underlying integer + /// primitive without checking whether its zero. + /// + /// # Safety + /// Immediate language UB if `val == 0`, as it violates the validity + /// invariant of this type. + #[inline] + pub const unsafe fn new_unchecked(val: $int) -> Self { + // SAFETY: Caller promised that `val` is non-zero. + unsafe { $name(val) } + } + + #[inline] + pub const fn as_inner(self) -> $int { + // SAFETY: This is a transparent wrapper, so unwrapping it is sound + // (Not using `.0` due to MCP#807.) + unsafe { crate::mem::transmute(self) } + } + } + + // This is required to allow matching a constant. We don't get it from a derive + // because the derived `PartialEq` would do a field projection, which is banned + // by . + impl StructuralPartialEq for $name {} + + impl PartialEq for $name { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.as_inner() == other.as_inner() + } + } + + impl Ord for $name { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + Ord::cmp(&self.as_inner(), &other.as_inner()) + } + } + + impl PartialOrd for $name { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some(Ord::cmp(self, other)) + } + } + + impl Hash for $name { + // Required method + fn hash(&self, state: &mut H) { + Hash::hash(&self.as_inner(), state); + } + } + + impl fmt::Debug for $name { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + <$int as fmt::Debug>::fmt(&self.as_inner(), f) + } + } + )+}; +} + +define_valid_range_type! { + pub struct Nanoseconds(u32 as u32 in 0..=999_999_999); +} + +impl Nanoseconds { + // SAFETY: 0 is within the valid range + pub const ZERO: Self = unsafe { Nanoseconds::new_unchecked(0) }; +} + +impl Default for Nanoseconds { + #[inline] + fn default() -> Self { + Self::ZERO + } +} + +define_valid_range_type! { + pub struct NonZeroU8Inner(u8 as u8 in 1..=0xff); + pub struct NonZeroU16Inner(u16 as u16 in 1..=0xff_ff); + pub struct NonZeroU32Inner(u32 as u32 in 1..=0xffff_ffff); + pub struct NonZeroU64Inner(u64 as u64 in 1..=0xffffffff_ffffffff); + pub struct NonZeroU128Inner(u128 as u128 in 1..=0xffffffffffffffff_ffffffffffffffff); + + pub struct NonZeroI8Inner(i8 as u8 in 1..=0xff); + pub struct NonZeroI16Inner(i16 as u16 in 1..=0xff_ff); + pub struct NonZeroI32Inner(i32 as u32 in 1..=0xffff_ffff); + pub struct NonZeroI64Inner(i64 as u64 in 1..=0xffffffff_ffffffff); + pub struct NonZeroI128Inner(i128 as u128 in 1..=0xffffffffffffffff_ffffffffffffffff); +} + +#[cfg(target_pointer_width = "16")] +define_valid_range_type! { + pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff); + pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff); + pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff); +} +#[cfg(target_pointer_width = "32")] +define_valid_range_type! { + pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff_ffff); + pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff_ffff); + pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff_ffff); +} +#[cfg(target_pointer_width = "64")] +define_valid_range_type! { + pub struct UsizeNoHighBit(usize as usize in 0..=0x7fff_ffff_ffff_ffff); + pub struct NonZeroUsizeInner(usize as usize in 1..=0xffff_ffff_ffff_ffff); + pub struct NonZeroIsizeInner(isize as usize in 1..=0xffff_ffff_ffff_ffff); +} + +define_valid_range_type! { + pub struct U32NotAllOnes(u32 as u32 in 0..=0xffff_fffe); + pub struct I32NotAllOnes(i32 as u32 in 0..=0xffff_fffe); + + pub struct U64NotAllOnes(u64 as u64 in 0..=0xffff_ffff_ffff_fffe); + pub struct I64NotAllOnes(i64 as u64 in 0..=0xffff_ffff_ffff_fffe); +} + +pub trait NotAllOnesHelper { + type Type; +} +pub type NotAllOnes = ::Type; +impl NotAllOnesHelper for u32 { + type Type = U32NotAllOnes; +} +impl NotAllOnesHelper for i32 { + type Type = I32NotAllOnes; +} +impl NotAllOnesHelper for u64 { + type Type = U64NotAllOnes; +} +impl NotAllOnesHelper for i64 { + type Type = I64NotAllOnes; +} diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index a9294306b1b61..332f77bf53e70 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -37,41 +37,12 @@ pub unsafe trait ZeroablePrimitive: Sized + Copy + private::Sealed { macro_rules! impl_zeroable_primitive { ($($NonZeroInner:ident ( $primitive:ty )),+ $(,)?) => { mod private { - use super::*; - #[unstable( feature = "nonzero_internals", reason = "implementation detail which may disappear or be replaced at any time", issue = "none" )] pub trait Sealed {} - - $( - // This inner type is never shown directly, so intentionally does not have Debug - #[expect(missing_debug_implementations)] - // Since this struct is non-generic and derives Copy, - // the derived Clone is `*self` and thus doesn't field-project. - #[derive(Clone, Copy)] - #[repr(transparent)] - #[rustc_layout_scalar_valid_range_start(1)] - #[rustc_nonnull_optimization_guaranteed] - #[unstable( - feature = "nonzero_internals", - reason = "implementation detail which may disappear or be replaced at any time", - issue = "none" - )] - pub struct $NonZeroInner($primitive); - - // This is required to allow matching a constant. We don't get it from a derive - // because the derived `PartialEq` would do a field projection, which is banned - // by . - #[unstable( - feature = "nonzero_internals", - reason = "implementation detail which may disappear or be replaced at any time", - issue = "none" - )] - impl StructuralPartialEq for $NonZeroInner {} - )+ } $( @@ -88,7 +59,7 @@ macro_rules! impl_zeroable_primitive { issue = "none" )] unsafe impl ZeroablePrimitive for $primitive { - type NonZeroInner = private::$NonZeroInner; + type NonZeroInner = super::niche_types::$NonZeroInner; } )+ }; @@ -119,6 +90,26 @@ impl_zeroable_primitive!( /// /// assert_eq!(size_of::>>(), size_of::()); /// ``` +/// +/// # Layout +/// +/// `NonZero` is guaranteed to have the same layout and bit validity as `T` +/// with the exception that the all-zero bit pattern is invalid. +/// `Option>` is guaranteed to be compatible with `T`, including in +/// FFI. +/// +/// Thanks to the [null pointer optimization], `NonZero` and +/// `Option>` are guaranteed to have the same size and alignment: +/// +/// ``` +/// # use std::mem::{size_of, align_of}; +/// use std::num::NonZero; +/// +/// assert_eq!(size_of::>(), size_of::>>()); +/// assert_eq!(align_of::>(), align_of::>>()); +/// ``` +/// +/// [null pointer optimization]: crate::option#representation #[stable(feature = "generic_nonzero", since = "1.79.0")] #[repr(transparent)] #[rustc_nonnull_optimization_guaranteed] @@ -483,6 +474,7 @@ macro_rules! nonzero_integer { #[$stability:meta] Self = $Ty:ident, Primitive = $signedness:ident $Int:ident, + SignedPrimitive = $Sint:ty, UnsignedPrimitive = $Uint:ty, // Used in doc comments. @@ -620,8 +612,6 @@ macro_rules! nonzero_integer { /// Basic usage: /// /// ``` - /// #![feature(non_zero_count_ones)] - /// /// # use std::num::NonZero; /// # /// # fn main() { test().unwrap(); } @@ -635,7 +625,8 @@ macro_rules! nonzero_integer { /// # } /// ``` /// - #[unstable(feature = "non_zero_count_ones", issue = "120287")] + #[stable(feature = "non_zero_count_ones", since = "1.86.0")] + #[rustc_const_stable(feature = "non_zero_count_ones", since = "1.86.0")] #[doc(alias = "popcount")] #[doc(alias = "popcnt")] #[must_use = "this returns the result of the operation, \ @@ -914,6 +905,7 @@ macro_rules! nonzero_integer { nonzero_integer_signedness_dependent_methods! { Primitive = $signedness $Int, + SignedPrimitive = $Sint, UnsignedPrimitive = $Uint, } @@ -1137,6 +1129,7 @@ macro_rules! nonzero_integer { ( Self = $Ty:ident, Primitive = unsigned $Int:ident, + SignedPrimitive = $Sint:ident, rot = $rot:literal, rot_op = $rot_op:literal, rot_result = $rot_result:literal, @@ -1149,6 +1142,7 @@ macro_rules! nonzero_integer { #[stable(feature = "nonzero", since = "1.28.0")] Self = $Ty, Primitive = unsigned $Int, + SignedPrimitive = $Sint, UnsignedPrimitive = $Int, rot = $rot, rot_op = $rot_op, @@ -1163,7 +1157,7 @@ macro_rules! nonzero_integer { ( Self = $Ty:ident, Primitive = signed $Int:ident, - UnsignedPrimitive = $UInt:ident, + UnsignedPrimitive = $Uint:ident, rot = $rot:literal, rot_op = $rot_op:literal, rot_result = $rot_result:literal, @@ -1175,7 +1169,8 @@ macro_rules! nonzero_integer { #[stable(feature = "signed_nonzero", since = "1.34.0")] Self = $Ty, Primitive = signed $Int, - UnsignedPrimitive = $UInt, + SignedPrimitive = $Int, + UnsignedPrimitive = $Uint, rot = $rot, rot_op = $rot_op, rot_result = $rot_result, @@ -1194,8 +1189,12 @@ macro_rules! nonzero_integer_signedness_dependent_impls { impl Div> for $Int { type Output = $Int; + /// Same as `self / other.get()`, but because `other` is a `NonZero<_>`, + /// there's never a runtime check for division-by-zero. + /// /// This operation rounds towards zero, truncating any fractional /// part of the exact result, and cannot panic. + #[doc(alias = "unchecked_div")] #[inline] fn div(self, other: NonZero<$Int>) -> $Int { // SAFETY: Division by zero is checked because `other` is non-zero, @@ -1206,6 +1205,9 @@ macro_rules! nonzero_integer_signedness_dependent_impls { #[stable(feature = "nonzero_div_assign", since = "1.79.0")] impl DivAssign> for $Int { + /// Same as `self /= other.get()`, but because `other` is a `NonZero<_>`, + /// there's never a runtime check for division-by-zero. + /// /// This operation rounds towards zero, truncating any fractional /// part of the exact result, and cannot panic. #[inline] @@ -1288,6 +1290,7 @@ macro_rules! nonzero_integer_signedness_dependent_methods { // Associated items for unsigned nonzero types only. ( Primitive = unsigned $Int:ident, + SignedPrimitive = $Sint:ty, UnsignedPrimitive = $Uint:ty, ) => { /// The smallest value that can be represented by this non-zero @@ -1543,8 +1546,8 @@ macro_rules! nonzero_integer_signedness_dependent_methods { /// # Some(()) /// # } /// ``` - #[stable(feature = "num_midpoint", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "num_midpoint", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "num_midpoint", since = "1.85.0")] + #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -1622,11 +1625,35 @@ macro_rules! nonzero_integer_signedness_dependent_methods { // results will be sqrt(1), which is 1, so a result can't be zero. unsafe { Self::new_unchecked(result) } } + + /// Returns the bit pattern of `self` reinterpreted as a signed integer of the same size. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(integer_sign_cast)] + /// # use std::num::NonZero; + /// + #[doc = concat!("let n = NonZero::<", stringify!($Int), ">::MAX;")] + /// + #[doc = concat!("assert_eq!(n.cast_signed(), NonZero::new(-1", stringify!($Sint), ").unwrap());")] + /// ``` + #[unstable(feature = "integer_sign_cast", issue = "125882")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub const fn cast_signed(self) -> NonZero<$Sint> { + // SAFETY: `self.get()` can't be zero + unsafe { NonZero::new_unchecked(self.get().cast_signed()) } + } }; // Associated items for signed nonzero types only. ( Primitive = signed $Int:ident, + SignedPrimitive = $Sint:ty, UnsignedPrimitive = $Uint:ty, ) => { /// The smallest value that can be represented by this non-zero @@ -2037,12 +2064,37 @@ macro_rules! nonzero_integer_signedness_dependent_methods { // SAFETY: negation of nonzero cannot yield zero values. unsafe { Self::new_unchecked(result) } } + + /// Returns the bit pattern of `self` reinterpreted as an unsigned integer of the same size. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(integer_sign_cast)] + /// # use std::num::NonZero; + /// + #[doc = concat!("let n = NonZero::new(-1", stringify!($Int), ").unwrap();")] + /// + #[doc = concat!("assert_eq!(n.cast_unsigned(), NonZero::<", stringify!($Uint), ">::MAX);")] + /// ``` + #[unstable(feature = "integer_sign_cast", issue = "125882")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline(always)] + pub const fn cast_unsigned(self) -> NonZero<$Uint> { + // SAFETY: `self.get()` can't be zero + unsafe { NonZero::new_unchecked(self.get().cast_unsigned()) } + } + }; } nonzero_integer! { Self = NonZeroU8, Primitive = unsigned u8, + SignedPrimitive = i8, rot = 2, rot_op = "0x82", rot_result = "0xa", @@ -2054,6 +2106,7 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroU16, Primitive = unsigned u16, + SignedPrimitive = i16, rot = 4, rot_op = "0xa003", rot_result = "0x3a", @@ -2065,6 +2118,7 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroU32, Primitive = unsigned u32, + SignedPrimitive = i32, rot = 8, rot_op = "0x10000b3", rot_result = "0xb301", @@ -2076,6 +2130,7 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroU64, Primitive = unsigned u64, + SignedPrimitive = i64, rot = 12, rot_op = "0xaa00000000006e1", rot_result = "0x6e10aa", @@ -2087,6 +2142,7 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroU128, Primitive = unsigned u128, + SignedPrimitive = i128, rot = 16, rot_op = "0x13f40000000000000000000000004f76", rot_result = "0x4f7613f4", @@ -2099,6 +2155,7 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroUsize, Primitive = unsigned usize, + SignedPrimitive = isize, rot = 4, rot_op = "0xa003", rot_result = "0x3a", @@ -2111,6 +2168,7 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroUsize, Primitive = unsigned usize, + SignedPrimitive = isize, rot = 8, rot_op = "0x10000b3", rot_result = "0xb301", @@ -2123,6 +2181,7 @@ nonzero_integer! { nonzero_integer! { Self = NonZeroUsize, Primitive = unsigned usize, + SignedPrimitive = isize, rot = 12, rot_op = "0xaa00000000006e1", rot_result = "0x6e10aa", diff --git a/library/core/src/num/overflow_panic.rs b/library/core/src/num/overflow_panic.rs index 203037ffb43ea..e30573dd3f392 100644 --- a/library/core/src/num/overflow_panic.rs +++ b/library/core/src/num/overflow_panic.rs @@ -4,48 +4,48 @@ #[cold] #[track_caller] -pub const fn add() -> ! { +pub(super) const fn add() -> ! { panic!("attempt to add with overflow") } #[cold] #[track_caller] -pub const fn sub() -> ! { +pub(super) const fn sub() -> ! { panic!("attempt to subtract with overflow") } #[cold] #[track_caller] -pub const fn mul() -> ! { +pub(super) const fn mul() -> ! { panic!("attempt to multiply with overflow") } #[cold] #[track_caller] -pub const fn div() -> ! { +pub(super) const fn div() -> ! { panic!("attempt to divide with overflow") } #[cold] #[track_caller] -pub const fn rem() -> ! { +pub(super) const fn rem() -> ! { panic!("attempt to calculate the remainder with overflow") } #[cold] #[track_caller] -pub const fn neg() -> ! { +pub(super) const fn neg() -> ! { panic!("attempt to negate with overflow") } #[cold] #[track_caller] -pub const fn shr() -> ! { +pub(super) const fn shr() -> ! { panic!("attempt to shift right with overflow") } #[cold] #[track_caller] -pub const fn shl() -> ! { +pub(super) const fn shl() -> ! { panic!("attempt to shift left with overflow") } diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 404e4bcffd379..29f6791ee6ad2 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1187,6 +1187,50 @@ macro_rules! uint_impl { self % rhs } + /// Same value as `self | other`, but UB if any bit position is set in both inputs. + /// + /// This is a situational micro-optimization for places where you'd rather + /// use addition on some platforms and bitwise or on other platforms, based + /// on exactly which instructions combine better with whatever else you're + /// doing. Note that there's no reason to bother using this for places + /// where it's clear from the operations involved that they can't overlap. + /// For example, if you're combining `u16`s into a `u32` with + /// `((a as u32) << 16) | (b as u32)`, that's fine, as the backend will + /// know those sides of the `|` are disjoint without needing help. + /// + /// # Examples + /// + /// ``` + /// #![feature(disjoint_bitor)] + /// + /// // SAFETY: `1` and `4` have no bits in common. + /// unsafe { + #[doc = concat!(" assert_eq!(1_", stringify!($SelfT), ".unchecked_disjoint_bitor(4), 5);")] + /// } + /// ``` + /// + /// # Safety + /// + /// Requires that `(self & other) == 0`, otherwise it's immediate UB. + /// + /// Equivalently, requires that `(self | other) == (self + other)`. + #[unstable(feature = "disjoint_bitor", issue = "135758")] + #[rustc_const_unstable(feature = "disjoint_bitor", issue = "135758")] + #[inline] + pub const unsafe fn unchecked_disjoint_bitor(self, other: Self) -> Self { + assert_unsafe_precondition!( + check_language_ub, + concat!(stringify!($SelfT), "::unchecked_disjoint_bitor cannot have overlapping bits"), + ( + lhs: $SelfT = self, + rhs: $SelfT = other, + ) => (lhs & rhs) == 0, + ); + + // SAFETY: Same precondition + unsafe { intrinsics::disjoint_bitor(self, other) } + } + /// Returns the logarithm of the number with respect to an arbitrary base, /// rounded down. /// @@ -2346,15 +2390,22 @@ macro_rules! uint_impl { /// assert_eq!((sum1, sum0), (9, 6)); /// ``` #[unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn carrying_add(self, rhs: Self, carry: bool) -> (Self, bool) { // note: longer-term this should be done via an intrinsic, but this has been shown // to generate optimal code for now, and LLVM doesn't have an equivalent intrinsic - let (a, b) = self.overflowing_add(rhs); - let (c, d) = a.overflowing_add(carry as $SelfT); - (c, b | d) + let (a, c1) = self.overflowing_add(rhs); + let (b, c2) = a.overflowing_add(carry as $SelfT); + // Ideally LLVM would know this is disjoint without us telling them, + // but it doesn't + // SAFETY: Only one of `c1` and `c2` can be set. + // For c1 to be set we need to have overflowed, but if we did then + // `a` is at most `MAX-1`, which means that `c2` cannot possibly + // overflow because it's adding at most `1` (since it came from `bool`) + (b, unsafe { intrinsics::disjoint_bitor(c1, c2) }) } /// Calculates `self` + `rhs` with a signed `rhs`. @@ -2663,8 +2714,8 @@ macro_rules! uint_impl { /// /// Basic usage: /// - /// Please note that this example is shared between integer types. - /// Which explains why `u32` is used here. + /// Please note that this example is shared between integer types, + /// which explains why `u32` is used here. /// /// ``` /// #![feature(bigint_helper_methods)] @@ -2677,6 +2728,35 @@ macro_rules! uint_impl { "(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX));" )] /// ``` + /// + /// This is the core per-digit operation for "grade school" O(n²) multiplication. + /// + /// Please note that this example is shared between integer types, + /// using `u8` for simplicity of the demonstration. + /// + /// ``` + /// #![feature(bigint_helper_methods)] + /// + /// fn quadratic_mul(a: [u8; N], b: [u8; N]) -> [u8; N] { + /// let mut out = [0; N]; + /// for j in 0..N { + /// let mut carry = 0; + /// for i in 0..(N - j) { + /// (out[j + i], carry) = u8::carrying_mul_add(a[i], b[j], out[j + i], carry); + /// } + /// } + /// out + /// } + /// + /// // -1 * -1 == 1 + /// assert_eq!(quadratic_mul([0xFF; 3], [0xFF; 3]), [1, 0, 0]); + /// + /// assert_eq!(u32::wrapping_mul(0x9e3779b9, 0x7f4a7c15), 0xCFFC982D); + /// assert_eq!( + /// quadratic_mul(u32::to_le_bytes(0x9e3779b9), u32::to_le_bytes(0x7f4a7c15)), + /// u32::to_le_bytes(0xCFFC982D) + /// ); + /// ``` #[unstable(feature = "bigint_helper_methods", issue = "85532")] #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] #[must_use = "this returns the result of the operation, \ diff --git a/library/core/src/num/wrapping.rs b/library/core/src/num/wrapping.rs index 1156b389e2867..55fa91d0b9f49 100644 --- a/library/core/src/num/wrapping.rs +++ b/library/core/src/num/wrapping.rs @@ -1058,33 +1058,33 @@ mod shift_max { #[cfg(target_pointer_width = "16")] mod platform { - pub const usize: u32 = super::u16; - pub const isize: u32 = super::i16; + pub(crate) const usize: u32 = super::u16; + pub(crate) const isize: u32 = super::i16; } #[cfg(target_pointer_width = "32")] mod platform { - pub const usize: u32 = super::u32; - pub const isize: u32 = super::i32; + pub(crate) const usize: u32 = super::u32; + pub(crate) const isize: u32 = super::i32; } #[cfg(target_pointer_width = "64")] mod platform { - pub const usize: u32 = super::u64; - pub const isize: u32 = super::i64; + pub(crate) const usize: u32 = super::u64; + pub(crate) const isize: u32 = super::i64; } - pub const i8: u32 = (1 << 3) - 1; - pub const i16: u32 = (1 << 4) - 1; - pub const i32: u32 = (1 << 5) - 1; - pub const i64: u32 = (1 << 6) - 1; - pub const i128: u32 = (1 << 7) - 1; - pub use self::platform::isize; - - pub const u8: u32 = i8; - pub const u16: u32 = i16; - pub const u32: u32 = i32; - pub const u64: u32 = i64; - pub const u128: u32 = i128; - pub use self::platform::usize; + pub(super) const i8: u32 = (1 << 3) - 1; + pub(super) const i16: u32 = (1 << 4) - 1; + pub(super) const i32: u32 = (1 << 5) - 1; + pub(super) const i64: u32 = (1 << 6) - 1; + pub(super) const i128: u32 = (1 << 7) - 1; + pub(super) use self::platform::isize; + + pub(super) const u8: u32 = i8; + pub(super) const u16: u32 = i16; + pub(super) const u32: u32 = i32; + pub(super) const u64: u32 = i64; + pub(super) const u128: u32 = i128; + pub(super) use self::platform::usize; } diff --git a/library/core/src/ops/arith.rs b/library/core/src/ops/arith.rs index 565bccf589826..fe7ff2d9ede6a 100644 --- a/library/core/src/ops/arith.rs +++ b/library/core/src/ops/arith.rs @@ -65,6 +65,7 @@ /// ``` #[lang = "add"] #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_ops", issue = "90080")] #[rustc_on_unimplemented( on(all(_Self = "{integer}", Rhs = "{float}"), message = "cannot add a float to an integer",), on(all(_Self = "{float}", Rhs = "{integer}"), message = "cannot add an integer to a float",), @@ -73,7 +74,7 @@ append_const_msg )] #[doc(alias = "+")] -#[cfg_attr(not(bootstrap), const_trait)] +#[const_trait] pub trait Add { /// The resulting type after applying the `+` operator. #[stable(feature = "rust1", since = "1.0.0")] @@ -95,18 +96,6 @@ pub trait Add { macro_rules! add_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(bootstrap)] - impl Add for $t { - type Output = $t; - - #[inline] - #[track_caller] - #[rustc_inherit_overflow_checks] - fn add(self, other: $t) -> $t { self + other } - } - - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(not(bootstrap))] impl const Add for $t { type Output = $t; diff --git a/library/core/src/ops/async_function.rs b/library/core/src/ops/async_function.rs index 0073afd496070..6be42ca7d32fe 100644 --- a/library/core/src/ops/async_function.rs +++ b/library/core/src/ops/async_function.rs @@ -4,10 +4,8 @@ use crate::marker::Tuple; /// An async-aware version of the [`Fn`](crate::ops::Fn) trait. /// /// All `async fn` and functions returning futures implement this trait. -#[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] -#[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] +#[stable(feature = "async_closure", since = "1.85.0")] #[rustc_paren_sugar] -#[fundamental] #[must_use = "async closures are lazy and do nothing unless called"] #[lang = "async_fn"] pub trait AsyncFn: AsyncFnMut { @@ -19,10 +17,8 @@ pub trait AsyncFn: AsyncFnMut { /// An async-aware version of the [`FnMut`](crate::ops::FnMut) trait. /// /// All `async fn` and functions returning futures implement this trait. -#[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] -#[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] +#[stable(feature = "async_closure", since = "1.85.0")] #[rustc_paren_sugar] -#[fundamental] #[must_use = "async closures are lazy and do nothing unless called"] #[lang = "async_fn_mut"] pub trait AsyncFnMut: AsyncFnOnce { @@ -41,10 +37,8 @@ pub trait AsyncFnMut: AsyncFnOnce { /// An async-aware version of the [`FnOnce`](crate::ops::FnOnce) trait. /// /// All `async fn` and functions returning futures implement this trait. -#[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] -#[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] +#[stable(feature = "async_closure", since = "1.85.0")] #[rustc_paren_sugar] -#[fundamental] #[must_use = "async closures are lazy and do nothing unless called"] #[lang = "async_fn_once"] pub trait AsyncFnOnce { @@ -67,8 +61,7 @@ mod impls { use super::{AsyncFn, AsyncFnMut, AsyncFnOnce}; use crate::marker::Tuple; - #[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] - #[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] + #[stable(feature = "async_closure", since = "1.85.0")] impl AsyncFn for &F where F: AsyncFn, @@ -78,8 +71,7 @@ mod impls { } } - #[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] - #[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] + #[stable(feature = "async_closure", since = "1.85.0")] impl AsyncFnMut for &F where F: AsyncFn, @@ -94,8 +86,7 @@ mod impls { } } - #[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] - #[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] + #[stable(feature = "async_closure", since = "1.85.0")] impl<'a, A: Tuple, F: ?Sized> AsyncFnOnce for &'a F where F: AsyncFn, @@ -108,8 +99,7 @@ mod impls { } } - #[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] - #[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] + #[stable(feature = "async_closure", since = "1.85.0")] impl AsyncFnMut for &mut F where F: AsyncFnMut, @@ -124,8 +114,7 @@ mod impls { } } - #[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] - #[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] + #[stable(feature = "async_closure", since = "1.85.0")] impl<'a, A: Tuple, F: ?Sized> AsyncFnOnce for &'a mut F where F: AsyncFnMut, diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs index c36d55fe2a8c1..11490ea2bfcb4 100644 --- a/library/core/src/ops/deref.rs +++ b/library/core/src/ops/deref.rs @@ -133,7 +133,8 @@ #[doc(alias = "&*")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "Deref"] -#[cfg_attr(not(bootstrap), const_trait)] +#[const_trait] +#[rustc_const_unstable(feature = "const_deref", issue = "88955")] pub trait Deref { /// The resulting type after dereferencing. #[stable(feature = "rust1", since = "1.0.0")] @@ -148,18 +149,6 @@ pub trait Deref { fn deref(&self) -> &Self::Target; } -#[cfg(bootstrap)] -#[stable(feature = "rust1", since = "1.0.0")] -impl Deref for &T { - type Target = T; - - #[rustc_diagnostic_item = "noop_method_deref"] - fn deref(&self) -> &T { - *self - } -} - -#[cfg(not(bootstrap))] #[stable(feature = "rust1", since = "1.0.0")] impl const Deref for &T { type Target = T; @@ -173,17 +162,6 @@ impl const Deref for &T { #[stable(feature = "rust1", since = "1.0.0")] impl !DerefMut for &T {} -#[cfg(bootstrap)] -#[stable(feature = "rust1", since = "1.0.0")] -impl Deref for &mut T { - type Target = T; - - fn deref(&self) -> &T { - *self - } -} - -#[cfg(not(bootstrap))] #[stable(feature = "rust1", since = "1.0.0")] impl const Deref for &mut T { type Target = T; @@ -282,11 +260,11 @@ impl const Deref for &mut T { /// *x = 'b'; /// assert_eq!('b', x.value); /// ``` -#[cfg(not(bootstrap))] #[lang = "deref_mut"] #[doc(alias = "*")] #[stable(feature = "rust1", since = "1.0.0")] #[const_trait] +#[rustc_const_unstable(feature = "const_deref", issue = "88955")] pub trait DerefMut: ~const Deref { /// Mutably dereferences the value. #[stable(feature = "rust1", since = "1.0.0")] @@ -294,27 +272,6 @@ pub trait DerefMut: ~const Deref { fn deref_mut(&mut self) -> &mut Self::Target; } -/// Bootstrap -#[lang = "deref_mut"] -#[doc(alias = "*")] -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg(bootstrap)] -pub trait DerefMut: Deref { - /// Mutably dereferences the value. - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_diagnostic_item = "deref_mut_method"] - fn deref_mut(&mut self) -> &mut Self::Target; -} - -#[cfg(bootstrap)] -#[stable(feature = "rust1", since = "1.0.0")] -impl DerefMut for &mut T { - fn deref_mut(&mut self) -> &mut T { - *self - } -} - -#[cfg(not(bootstrap))] #[stable(feature = "rust1", since = "1.0.0")] impl const DerefMut for &mut T { fn deref_mut(&mut self) -> &mut T { diff --git a/library/core/src/ops/drop.rs b/library/core/src/ops/drop.rs index f3314364e5428..e024b7fb4d301 100644 --- a/library/core/src/ops/drop.rs +++ b/library/core/src/ops/drop.rs @@ -203,7 +203,8 @@ /// [nomicon]: ../../nomicon/phantom-data.html#an-exception-the-special-case-of-the-standard-library-and-its-unstable-may_dangle #[lang = "drop"] #[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(bootstrap), const_trait)] +#[const_trait] +#[rustc_const_unstable(feature = "const_destruct", issue = "133214")] pub trait Drop { /// Executes the destructor for this type. /// diff --git a/library/core/src/ops/index_range.rs b/library/core/src/ops/index_range.rs index dce3514a1595b..b82184b15b2f5 100644 --- a/library/core/src/ops/index_range.rs +++ b/library/core/src/ops/index_range.rs @@ -18,7 +18,7 @@ impl IndexRange { /// # Safety /// - `start <= end` #[inline] - pub const unsafe fn new_unchecked(start: usize, end: usize) -> Self { + pub(crate) const unsafe fn new_unchecked(start: usize, end: usize) -> Self { ub_checks::assert_unsafe_precondition!( check_library_ub, "IndexRange::new_unchecked requires `start <= end`", @@ -28,22 +28,22 @@ impl IndexRange { } #[inline] - pub const fn zero_to(end: usize) -> Self { + pub(crate) const fn zero_to(end: usize) -> Self { IndexRange { start: 0, end } } #[inline] - pub const fn start(&self) -> usize { + pub(crate) const fn start(&self) -> usize { self.start } #[inline] - pub const fn end(&self) -> usize { + pub(crate) const fn end(&self) -> usize { self.end } #[inline] - pub const fn len(&self) -> usize { + pub(crate) const fn len(&self) -> usize { // SAFETY: By invariant, this cannot wrap // Using the intrinsic because a UB check here impedes LLVM optimization. (#131563) unsafe { crate::intrinsics::unchecked_sub(self.end, self.start) } @@ -79,7 +79,7 @@ impl IndexRange { /// /// This is designed to help implement `Iterator::advance_by`. #[inline] - pub fn take_prefix(&mut self, n: usize) -> Self { + pub(crate) fn take_prefix(&mut self, n: usize) -> Self { let mid = if n <= self.len() { // SAFETY: We just checked that this will be between start and end, // and thus the addition cannot overflow. @@ -99,7 +99,7 @@ impl IndexRange { /// /// This is designed to help implement `Iterator::advance_back_by`. #[inline] - pub fn take_suffix(&mut self, n: usize) -> Self { + pub(crate) fn take_suffix(&mut self, n: usize) -> Self { let mid = if n <= self.len() { // SAFETY: We just checked that this will be between start and end, // and thus the subtraction cannot overflow. diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs index 40526f9583e64..627a875d9f724 100644 --- a/library/core/src/ops/mod.rs +++ b/library/core/src/ops/mod.rs @@ -182,10 +182,12 @@ pub use self::function::{Fn, FnMut, FnOnce}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::index::{Index, IndexMut}; pub(crate) use self::index_range::IndexRange; -#[unstable(feature = "one_sided_range", issue = "69780")] -pub use self::range::OneSidedRange; +#[unstable(feature = "range_into_bounds", issue = "136903")] +pub use self::range::IntoBounds; #[stable(feature = "inclusive_range", since = "1.26.0")] pub use self::range::{Bound, RangeBounds, RangeInclusive, RangeToInclusive}; +#[unstable(feature = "one_sided_range", issue = "69780")] +pub use self::range::{OneSidedRange, OneSidedRangeBound}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::range::{Range, RangeFrom, RangeFull, RangeTo}; #[unstable(feature = "try_trait_v2_residual", issue = "91285")] diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index 727a22e454d3d..5580faefacc0d 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -831,6 +831,30 @@ pub trait RangeBounds { } } +/// Used to convert a range into start and end bounds, consuming the +/// range by value. +/// +/// `IntoBounds` is implemented by Rust’s built-in range types, produced +/// by range syntax like `..`, `a..`, `..b`, `..=c`, `d..e`, or `f..=g`. +#[unstable(feature = "range_into_bounds", issue = "136903")] +pub trait IntoBounds: RangeBounds { + /// Convert this range into the start and end bounds. + /// Returns `(start_bound, end_bound)`. + /// + /// # Examples + /// + /// ``` + /// #![feature(range_into_bounds)] + /// + /// use std::ops::Bound::*; + /// use std::ops::IntoBounds; + /// + /// assert_eq!((0..5).into_bounds(), (Included(0), Excluded(5))); + /// assert_eq!((..=7).into_bounds(), (Unbounded, Included(7))); + /// ``` + fn into_bounds(self) -> (Bound, Bound); +} + use self::Bound::{Excluded, Included, Unbounded}; #[stable(feature = "collections_range", since = "1.28.0")] @@ -843,6 +867,13 @@ impl RangeBounds for RangeFull { } } +#[unstable(feature = "range_into_bounds", issue = "136903")] +impl IntoBounds for RangeFull { + fn into_bounds(self) -> (Bound, Bound) { + (Unbounded, Unbounded) + } +} + #[stable(feature = "collections_range", since = "1.28.0")] impl RangeBounds for RangeFrom { fn start_bound(&self) -> Bound<&T> { @@ -853,6 +884,13 @@ impl RangeBounds for RangeFrom { } } +#[unstable(feature = "range_into_bounds", issue = "136903")] +impl IntoBounds for RangeFrom { + fn into_bounds(self) -> (Bound, Bound) { + (Included(self.start), Unbounded) + } +} + #[stable(feature = "collections_range", since = "1.28.0")] impl RangeBounds for RangeTo { fn start_bound(&self) -> Bound<&T> { @@ -863,6 +901,13 @@ impl RangeBounds for RangeTo { } } +#[unstable(feature = "range_into_bounds", issue = "136903")] +impl IntoBounds for RangeTo { + fn into_bounds(self) -> (Bound, Bound) { + (Unbounded, Excluded(self.end)) + } +} + #[stable(feature = "collections_range", since = "1.28.0")] impl RangeBounds for Range { fn start_bound(&self) -> Bound<&T> { @@ -873,6 +918,13 @@ impl RangeBounds for Range { } } +#[unstable(feature = "range_into_bounds", issue = "136903")] +impl IntoBounds for Range { + fn into_bounds(self) -> (Bound, Bound) { + (Included(self.start), Excluded(self.end)) + } +} + #[stable(feature = "collections_range", since = "1.28.0")] impl RangeBounds for RangeInclusive { fn start_bound(&self) -> Bound<&T> { @@ -889,6 +941,22 @@ impl RangeBounds for RangeInclusive { } } +#[unstable(feature = "range_into_bounds", issue = "136903")] +impl IntoBounds for RangeInclusive { + fn into_bounds(self) -> (Bound, Bound) { + ( + Included(self.start), + if self.exhausted { + // When the iterator is exhausted, we usually have start == end, + // but we want the range to appear empty, containing nothing. + Excluded(self.end) + } else { + Included(self.end) + }, + ) + } +} + #[stable(feature = "collections_range", since = "1.28.0")] impl RangeBounds for RangeToInclusive { fn start_bound(&self) -> Bound<&T> { @@ -899,6 +967,13 @@ impl RangeBounds for RangeToInclusive { } } +#[unstable(feature = "range_into_bounds", issue = "136903")] +impl IntoBounds for RangeToInclusive { + fn into_bounds(self) -> (Bound, Bound) { + (Unbounded, Included(self.end)) + } +} + #[stable(feature = "collections_range", since = "1.28.0")] impl RangeBounds for (Bound, Bound) { fn start_bound(&self) -> Bound<&T> { @@ -918,6 +993,13 @@ impl RangeBounds for (Bound, Bound) { } } +#[unstable(feature = "range_into_bounds", issue = "136903")] +impl IntoBounds for (Bound, Bound) { + fn into_bounds(self) -> (Bound, Bound) { + self + } +} + #[stable(feature = "collections_range", since = "1.28.0")] impl<'a, T: ?Sized + 'a> RangeBounds for (Bound<&'a T>, Bound<&'a T>) { fn start_bound(&self) -> Bound<&T> { @@ -979,6 +1061,19 @@ impl RangeBounds for RangeToInclusive<&T> { } } +/// An internal helper for `split_off` functions indicating +/// which end a `OneSidedRange` is bounded on. +#[unstable(feature = "one_sided_range", issue = "69780")] +#[allow(missing_debug_implementations)] +pub enum OneSidedRangeBound { + /// The range is bounded inclusively from below and is unbounded above. + StartInclusive, + /// The range is bounded exclusively from above and is unbounded below. + End, + /// The range is bounded inclusively from above and is unbounded below. + EndInclusive, +} + /// `OneSidedRange` is implemented for built-in range types that are unbounded /// on one side. For example, `a..`, `..b` and `..=c` implement `OneSidedRange`, /// but `..`, `d..e`, and `f..=g` do not. @@ -986,13 +1081,38 @@ impl RangeBounds for RangeToInclusive<&T> { /// Types that implement `OneSidedRange` must return `Bound::Unbounded` /// from one of `RangeBounds::start_bound` or `RangeBounds::end_bound`. #[unstable(feature = "one_sided_range", issue = "69780")] -pub trait OneSidedRange: RangeBounds {} +pub trait OneSidedRange: RangeBounds { + /// An internal-only helper function for `split_off` and + /// `split_off_mut` that returns the bound of the one-sided range. + fn bound(self) -> (OneSidedRangeBound, T); +} #[unstable(feature = "one_sided_range", issue = "69780")] -impl OneSidedRange for RangeTo where Self: RangeBounds {} +impl OneSidedRange for RangeTo +where + Self: RangeBounds, +{ + fn bound(self) -> (OneSidedRangeBound, T) { + (OneSidedRangeBound::End, self.end) + } +} #[unstable(feature = "one_sided_range", issue = "69780")] -impl OneSidedRange for RangeFrom where Self: RangeBounds {} +impl OneSidedRange for RangeFrom +where + Self: RangeBounds, +{ + fn bound(self) -> (OneSidedRangeBound, T) { + (OneSidedRangeBound::StartInclusive, self.start) + } +} #[unstable(feature = "one_sided_range", issue = "69780")] -impl OneSidedRange for RangeToInclusive where Self: RangeBounds {} +impl OneSidedRange for RangeToInclusive +where + Self: RangeBounds, +{ + fn bound(self) -> (OneSidedRangeBound, T) { + (OneSidedRangeBound::EndInclusive, self.end) + } +} diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs index cd444c86ed06e..3ba2957526f9c 100644 --- a/library/core/src/ops/try_trait.rs +++ b/library/core/src/ops/try_trait.rs @@ -338,6 +338,7 @@ pub trait FromResidual::Residual> { #[inline] #[track_caller] // because `Result::from_residual` has it #[lang = "from_yeet"] +#[allow(unreachable_pub)] // not-exposed but still used via lang-item pub fn from_yeet(yeeted: Y) -> T where T: FromResidual>, @@ -383,12 +384,14 @@ impl NeverShortCircuit { /// This is useful for implementing infallible functions in terms of the `try_` ones, /// without accidentally capturing extra generic parameters in a closure. #[inline] - pub fn wrap_mut_1(mut f: impl FnMut(A) -> T) -> impl FnMut(A) -> NeverShortCircuit { + pub(crate) fn wrap_mut_1( + mut f: impl FnMut(A) -> T, + ) -> impl FnMut(A) -> NeverShortCircuit { move |a| NeverShortCircuit(f(a)) } #[inline] - pub fn wrap_mut_2(mut f: impl FnMut(A, B) -> T) -> impl FnMut(A, B) -> Self { + pub(crate) fn wrap_mut_2(mut f: impl FnMut(A, B) -> T) -> impl FnMut(A, B) -> Self { move |a, b| NeverShortCircuit(f(a, b)) } } diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 53e2b238bae69..d36e677d21a18 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -59,7 +59,7 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call // that gets resolved to the `#[panic_handler]` function. - extern "Rust" { + unsafe extern "Rust" { #[lang = "panic_impl"] fn panic_impl(pi: &PanicInfo<'_>) -> !; } @@ -100,7 +100,7 @@ pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: boo // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call // that gets resolved to the `#[panic_handler]` function. - extern "Rust" { + unsafe extern "Rust" { #[lang = "panic_impl"] fn panic_impl(pi: &PanicInfo<'_>) -> !; } @@ -291,6 +291,22 @@ fn panic_misaligned_pointer_dereference(required: usize, found: usize) -> ! { ) } +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))] +#[cfg_attr(feature = "panic_immediate_abort", inline)] +#[track_caller] +#[cfg_attr(not(bootstrap), lang = "panic_null_pointer_dereference")] // needed by codegen for panic on null pointer deref +#[rustc_nounwind] // `CheckNull` MIR pass requires this function to never unwind +fn panic_null_pointer_dereference() -> ! { + if cfg!(feature = "panic_immediate_abort") { + super::intrinsics::abort() + } + + panic_nounwind_fmt( + format_args!("null pointer dereference occurred"), + /* force_no_backtrace */ false, + ) +} + /// Panics because we cannot unwind out of a function. /// /// This is a separate function to avoid the codesize impact of each crate containing the string to diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 83730285636fb..2a0bf89fcf7a9 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -156,8 +156,8 @@ //! //! In order to implement the second option, we must in some way enforce its key invariant, //! *i.e.* prevent the value from being *moved* or otherwise invalidated (you may notice this -//! sounds an awful lot like the definition of *pinning* a value). There a few ways one might be -//! able to enforce this invariant in Rust: +//! sounds an awful lot like the definition of *pinning* a value). There are a few ways one might +//! be able to enforce this invariant in Rust: //! //! 1. Offer a wholly `unsafe` API to interact with the object, thus requiring every caller to //! uphold the invariant themselves @@ -331,7 +331,7 @@ //! //! Note that this invariant is enforced by simply making it impossible to call code that would //! perform a move on the pinned value. This is the case since the only way to access that pinned -//! value is through the pinning [Pin]<[&mut] T>>, which in turn restricts our access. +//! value is through the pinning [Pin]<[&mut] T>, which in turn restricts our access. //! //! ## [`Unpin`] //! @@ -379,7 +379,7 @@ //! //! Exposing access to the inner field which you want to remain pinned must then be carefully //! considered as well! Remember, exposing a method that gives access to a -//! [Pin]<[&mut] InnerT>> where InnerT: [Unpin] would allow safe code to +//! [Pin]<[&mut] InnerT> where InnerT: [Unpin] would allow safe code to //! trivially move the inner value out of that pinning pointer, which is precisely what you're //! seeking to prevent! Exposing a field of a pinned value through a pinning pointer is called //! "projecting" a pin, and the more general case of deciding in which cases a pin should be able diff --git a/library/core/src/prelude/common.rs b/library/core/src/prelude/common.rs deleted file mode 100644 index e38ef1e147c76..0000000000000 --- a/library/core/src/prelude/common.rs +++ /dev/null @@ -1,108 +0,0 @@ -//! Items common to the prelude of all editions. -//! -//! See the [module-level documentation](super) for more. - -// No formatting: this file is nothing but re-exports, and their order is worth preserving. -#![cfg_attr(rustfmt, rustfmt::skip)] - -// Re-exported core operators -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -pub use crate::marker::{Copy, Send, Sized, Sync, Unpin}; -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -pub use crate::ops::{Drop, Fn, FnMut, FnOnce}; - -// Re-exported functions -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -pub use crate::mem::drop; -#[stable(feature = "size_of_prelude", since = "1.80.0")] -#[doc(no_inline)] -pub use crate::mem::{align_of, align_of_val, size_of, size_of_val}; - -// Re-exported types and traits -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -pub use crate::clone::Clone; -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -pub use crate::cmp::{Eq, Ord, PartialEq, PartialOrd}; -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -pub use crate::convert::{AsMut, AsRef, From, Into}; -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -pub use crate::default::Default; -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -pub use crate::iter::{DoubleEndedIterator, ExactSizeIterator, Extend, IntoIterator, Iterator}; -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -pub use crate::option::Option::{self, None, Some}; -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -pub use crate::result::Result::{self, Err, Ok}; - -// Re-exported built-in macros -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[doc(no_inline)] -pub use crate::fmt::macros::Debug; -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[doc(no_inline)] -pub use crate::hash::macros::Hash; - -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[allow(deprecated)] -#[doc(no_inline)] -pub use crate::{ - assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args, - format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env, - stringify, trace_macros, -}; - -#[unstable( - feature = "concat_bytes", - issue = "87555", - reason = "`concat_bytes` is not stable enough for use and is subject to change" -)] -#[doc(no_inline)] -pub use crate::concat_bytes; - -// Do not `doc(no_inline)` so that they become doc items on their own -// (no public module for them to be re-exported from). -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -pub use crate::macros::builtin::{ - alloc_error_handler, bench, derive, global_allocator, test, test_case, -}; - -#[unstable(feature = "derive_const", issue = "none")] -pub use crate::macros::builtin::derive_const; - -#[unstable( - feature = "cfg_accessible", - issue = "64797", - reason = "`cfg_accessible` is not fully implemented" -)] -pub use crate::macros::builtin::cfg_accessible; - -#[unstable( - feature = "cfg_eval", - issue = "82679", - reason = "`cfg_eval` is a recently implemented feature" -)] -pub use crate::macros::builtin::cfg_eval; - -#[unstable( - feature = "type_ascription", - issue = "23416", - reason = "placeholder syntax for type ascription" -)] -pub use crate::macros::builtin::type_ascribe; - -#[unstable( - feature = "deref_patterns", - issue = "87121", - reason = "placeholder syntax for deref patterns" -)] -pub use crate::macros::builtin::deref; diff --git a/library/core/src/prelude/mod.rs b/library/core/src/prelude/mod.rs index 9b23874bcf7ba..590ffd64b5bff 100644 --- a/library/core/src/prelude/mod.rs +++ b/library/core/src/prelude/mod.rs @@ -9,26 +9,7 @@ #![stable(feature = "core_prelude", since = "1.4.0")] -mod common; - -/// The first version of the prelude of The Rust Standard Library. -/// -/// See the [module-level documentation](self) for more. -#[stable(feature = "rust1", since = "1.0.0")] -pub mod v1 { - #[stable(feature = "rust1", since = "1.0.0")] - pub use super::common::*; - - // Do not `doc(inline)` these `doc(hidden)` items. - #[unstable( - feature = "rustc_encodable_decodable", - issue = "none", - soft, - reason = "derive macro for `rustc-serialize`; should not be used in new code" - )] - #[allow(deprecated)] - pub use crate::macros::builtin::{RustcDecodable, RustcEncodable}; -} +pub mod v1; /// The 2015 version of the core prelude. /// @@ -71,10 +52,11 @@ pub mod rust_2021 { /// The 2024 version of the core prelude. /// /// See the [module-level documentation](self) for more. -#[stable(feature = "prelude_2024", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "prelude_2024", since = "1.85.0")] pub mod rust_2024 { #[stable(feature = "rust1", since = "1.0.0")] - pub use super::common::*; + #[doc(no_inline)] + pub use super::v1::*; #[stable(feature = "prelude_2021", since = "1.55.0")] #[doc(no_inline)] @@ -84,7 +66,7 @@ pub mod rust_2024 { #[doc(no_inline)] pub use crate::convert::{TryFrom, TryInto}; - #[stable(feature = "prelude_2024", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "prelude_2024", since = "1.85.0")] #[doc(no_inline)] pub use crate::future::{Future, IntoFuture}; } diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs new file mode 100644 index 0000000000000..50fd67e839557 --- /dev/null +++ b/library/core/src/prelude/v1.rs @@ -0,0 +1,113 @@ +//! The first version of the core prelude. +//! +//! See the [module-level documentation](super) for more. + +#![stable(feature = "core_prelude", since = "1.4.0")] + +// No formatting: this file is nothing but re-exports, and their order is worth preserving. +#![cfg_attr(rustfmt, rustfmt::skip)] + +// Re-exported core operators +#[stable(feature = "core_prelude", since = "1.4.0")] +#[doc(no_inline)] +pub use crate::marker::{Copy, Send, Sized, Sync, Unpin}; +#[stable(feature = "core_prelude", since = "1.4.0")] +#[doc(no_inline)] +pub use crate::ops::{Drop, Fn, FnMut, FnOnce}; +#[stable(feature = "async_closure", since = "1.85.0")] +#[doc(no_inline)] +pub use crate::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}; + +// Re-exported functions +#[stable(feature = "core_prelude", since = "1.4.0")] +#[doc(no_inline)] +pub use crate::mem::drop; +#[stable(feature = "size_of_prelude", since = "1.80.0")] +#[doc(no_inline)] +pub use crate::mem::{align_of, align_of_val, size_of, size_of_val}; + +// Re-exported types and traits +#[stable(feature = "core_prelude", since = "1.4.0")] +#[doc(no_inline)] +pub use crate::clone::Clone; +#[stable(feature = "core_prelude", since = "1.4.0")] +#[doc(no_inline)] +pub use crate::cmp::{Eq, Ord, PartialEq, PartialOrd}; +#[stable(feature = "core_prelude", since = "1.4.0")] +#[doc(no_inline)] +pub use crate::convert::{AsMut, AsRef, From, Into}; +#[stable(feature = "core_prelude", since = "1.4.0")] +#[doc(no_inline)] +pub use crate::default::Default; +#[stable(feature = "core_prelude", since = "1.4.0")] +#[doc(no_inline)] +pub use crate::iter::{DoubleEndedIterator, ExactSizeIterator, Extend, IntoIterator, Iterator}; +#[stable(feature = "core_prelude", since = "1.4.0")] +#[doc(no_inline)] +pub use crate::option::Option::{self, None, Some}; +#[stable(feature = "core_prelude", since = "1.4.0")] +#[doc(no_inline)] +pub use crate::result::Result::{self, Err, Ok}; + +// Re-exported built-in macros +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[doc(no_inline)] +pub use crate::fmt::macros::Debug; +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[doc(no_inline)] +pub use crate::hash::macros::Hash; + +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[allow(deprecated)] +#[doc(no_inline)] +pub use crate::{ + assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args, + format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env, + stringify, trace_macros, +}; + +#[unstable( + feature = "concat_bytes", + issue = "87555", + reason = "`concat_bytes` is not stable enough for use and is subject to change" +)] +#[doc(no_inline)] +pub use crate::concat_bytes; + +// Do not `doc(no_inline)` so that they become doc items on their own +// (no public module for them to be re-exported from). +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +pub use crate::macros::builtin::{ + alloc_error_handler, bench, derive, global_allocator, test, test_case, +}; + +#[unstable(feature = "derive_const", issue = "none")] +pub use crate::macros::builtin::derive_const; + +#[unstable( + feature = "cfg_accessible", + issue = "64797", + reason = "`cfg_accessible` is not fully implemented" +)] +pub use crate::macros::builtin::cfg_accessible; + +#[unstable( + feature = "cfg_eval", + issue = "82679", + reason = "`cfg_eval` is a recently implemented feature" +)] +pub use crate::macros::builtin::cfg_eval; + +#[unstable( + feature = "type_ascription", + issue = "23416", + reason = "placeholder syntax for type ascription" +)] +pub use crate::macros::builtin::type_ascribe; + +#[unstable( + feature = "deref_patterns", + issue = "87121", + reason = "placeholder syntax for deref patterns" +)] +pub use crate::macros::builtin::deref; diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index c5f029363e589..bbf5939fe1b05 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -1160,9 +1160,9 @@ impl (T,) {} /// /// Note that most common platforms will not support `f16` in hardware without enabling extra target /// features, with the notable exception of Apple Silicon (also known as M1, M2, etc.) processors. -/// Hardware support on x86-64 requires the avx512fp16 feature, while RISC-V requires Zhf. -/// Usually the fallback implementation will be to use `f32` hardware if it exists, and convert -/// between `f16` and `f32` when performing math. +/// Hardware support on x86/x86-64 requires the avx512fp16 or avx10.1 features, while RISC-V requires +/// Zfh, and Arm/AArch64 requires FEAT_FP16. Usually the fallback implementation will be to use `f32` +/// hardware if it exists, and convert between `f16` and `f32` when performing math. /// /// *[See also the `std::f16::consts` module](crate::f16::consts).* /// @@ -1344,10 +1344,10 @@ mod prim_f64 {} /// quad-precision values][wikipedia] for more information. /// /// Note that no platforms have hardware support for `f128` without enabling target specific features, -/// as for all instruction set architectures `f128` is considered an optional feature. -/// Only Power ISA ("PowerPC") and RISC-V specify it, and only certain microarchitectures -/// actually implement it. For x86-64 and AArch64, ISA support is not even specified, -/// so it will always be a software implementation significantly slower than `f64`. +/// as for all instruction set architectures `f128` is considered an optional feature. Only Power ISA +/// ("PowerPC") and RISC-V (via the Q extension) specify it, and only certain microarchitectures +/// actually implement it. For x86-64 and AArch64, ISA support is not even specified, so it will always +/// be a software implementation significantly slower than `f64`. /// /// _Note: `f128` support is incomplete. Many platforms will not be able to link math functions. On /// x86 in particular, these functions do link but their results are always incorrect._ diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs index 74a1d40f4e734..2da94e72566e9 100644 --- a/library/core/src/ptr/alignment.rs +++ b/library/core/src/ptr/alignment.rs @@ -42,9 +42,10 @@ impl Alignment { /// but in an `Alignment` instead of a `usize`. #[unstable(feature = "ptr_alignment_type", issue = "102070")] #[inline] + #[must_use] pub const fn of() -> Self { - // SAFETY: rustc ensures that type alignment is always a power of two. - unsafe { Alignment::new_unchecked(mem::align_of::()) } + // This can't actually panic since type alignment is always a power of two. + const { Alignment::new(mem::align_of::()).unwrap() } } /// Creates an `Alignment` from a `usize`, or returns `None` if it's @@ -95,8 +96,13 @@ impl Alignment { #[unstable(feature = "ptr_alignment_type", issue = "102070")] #[inline] pub const fn as_nonzero(self) -> NonZero { + // This transmutes directly to avoid the UbCheck in `NonZero::new_unchecked` + // since there's no way for the user to trip that check anyway -- the + // validity invariant of the type would have to have been broken earlier -- + // and emitting it in an otherwise simple method is bad for compile time. + // SAFETY: All the discriminants are non-zero. - unsafe { NonZero::new_unchecked(self.as_usize()) } + unsafe { mem::transmute::>(self) } } /// Returns the base-2 logarithm of the alignment. diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index ec569291853a5..0c6eaf60d0480 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -1681,7 +1681,7 @@ impl *const [T; N] { } } -// Equality for pointers +/// Pointer equality is by address, as produced by the [`<*const T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for *const T { #[inline] @@ -1691,10 +1691,11 @@ impl PartialEq for *const T { } } +/// Pointer equality is an equivalence relation. #[stable(feature = "rust1", since = "1.0.0")] impl Eq for *const T {} -// Comparison for pointers +/// Pointer comparison is by address, as produced by the `[`<*const T>::addr`](pointer::addr)` method. #[stable(feature = "rust1", since = "1.0.0")] impl Ord for *const T { #[inline] @@ -1710,6 +1711,7 @@ impl Ord for *const T { } } +/// Pointer comparison is by address, as produced by the `[`<*const T>::addr`](pointer::addr)` method. #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for *const T { #[inline] diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index b1d5e1fa3a059..9eee29d485f41 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -53,9 +53,8 @@ use crate::ptr::NonNull; /// /// [`to_raw_parts`]: *const::to_raw_parts #[lang = "pointee_trait"] -#[cfg_attr(bootstrap, rustc_deny_explicit_impl(implement_via_object = false))] -#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)] -#[cfg_attr(not(bootstrap), rustc_do_not_implement_via_object)] +#[rustc_deny_explicit_impl] +#[rustc_do_not_implement_via_object] pub trait Pointee { /// The type for metadata in pointers and references to `Self`. #[lang = "metadata_type"] @@ -156,7 +155,7 @@ pub struct DynMetadata { _phantom: crate::marker::PhantomData, } -extern "C" { +unsafe extern "C" { /// Opaque type for accessing vtables. /// /// Private implementation detail of `DynMetadata::size_of` etc. diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index a70af793004b2..eb99be817a2ca 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -596,12 +596,7 @@ pub const fn null_mut() -> *mut T { #[stable(feature = "strict_provenance", since = "1.84.0")] #[rustc_const_stable(feature = "strict_provenance", since = "1.84.0")] pub const fn without_provenance(addr: usize) -> *const T { - // An int-to-pointer transmute currently has exactly the intended semantics: it creates a - // pointer without provenance. Note that this is *not* a stable guarantee about transmute - // semantics, it relies on sysroot crates having special status. - // SAFETY: every valid integer is also a valid pointer (as long as you don't dereference that - // pointer). - unsafe { mem::transmute(addr) } + without_provenance_mut(addr) } /// Creates a new pointer that is dangling, but non-null and well-aligned. @@ -618,7 +613,7 @@ pub const fn without_provenance(addr: usize) -> *const T { #[stable(feature = "strict_provenance", since = "1.84.0")] #[rustc_const_stable(feature = "strict_provenance", since = "1.84.0")] pub const fn dangling() -> *const T { - without_provenance(mem::align_of::()) + dangling_mut() } /// Creates a pointer with the given address and no [provenance][crate::ptr#provenance]. @@ -661,7 +656,7 @@ pub const fn without_provenance_mut(addr: usize) -> *mut T { #[stable(feature = "strict_provenance", since = "1.84.0")] #[rustc_const_stable(feature = "strict_provenance", since = "1.84.0")] pub const fn dangling_mut() -> *mut T { - without_provenance_mut(mem::align_of::()) + NonNull::dangling().as_ptr() } /// Converts an address back to a pointer, picking up some previously 'exposed' @@ -777,7 +772,7 @@ pub fn with_exposed_provenance_mut(addr: usize) -> *mut T { /// # type T = i32; /// # fn foo() -> T { 42 } /// // The temporary holding the return value of `foo` does *not* have its lifetime extended, -/// // because the surrounding expression involves no function call. +/// // because the surrounding expression involves a function call. /// let p = ptr::from_ref(&foo()); /// unsafe { p.read() }; // UB! Reading from a dangling pointer ⚠️ /// ``` @@ -828,7 +823,7 @@ pub const fn from_ref(r: &T) -> *const T { /// # type T = i32; /// # fn foo() -> T { 42 } /// // The temporary holding the return value of `foo` does *not* have its lifetime extended, -/// // because the surrounding expression involves no function call. +/// // because the surrounding expression involves a function call. /// let p = ptr::from_mut(&mut foo()); /// unsafe { p.write(T::default()) }; // UB! Writing to a dangling pointer ⚠️ /// ``` @@ -1009,7 +1004,7 @@ pub const fn slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T] { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "const_swap", since = "1.85.0")] #[rustc_diagnostic_item = "ptr_swap"] pub const unsafe fn swap(x: *mut T, y: *mut T) { // Give ourselves some scratch space to work with. @@ -1075,7 +1070,7 @@ pub const unsafe fn swap(x: *mut T, y: *mut T) { #[rustc_diagnostic_item = "ptr_swap_nonoverlapping"] pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { ub_checks::assert_unsafe_precondition!( - check_language_ub, + check_library_ub, "ptr::swap_nonoverlapping requires that both pointer arguments are aligned and non-null \ and the specified memory ranges do not overlap", ( @@ -2150,7 +2145,7 @@ pub fn addr_eq(p: *const T, q: *const U) -> bool { /// ``` /// /// [subtype]: https://doc.rust-lang.org/reference/subtyping.html -#[stable(feature = "ptr_fn_addr_eq", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "ptr_fn_addr_eq", since = "1.85.0")] #[inline(always)] #[must_use = "function pointer comparison produces a value"] pub fn fn_addr_eq(f: T, g: U) -> bool { diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index d75d570a969f8..d1b0104c0fa92 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -1594,7 +1594,7 @@ impl *mut T { /// /// [`ptr::swap`]: crate::ptr::swap() #[stable(feature = "pointer_methods", since = "1.26.0")] - #[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_swap", since = "1.85.0")] #[inline(always)] pub const unsafe fn swap(self, with: *mut T) where @@ -2097,7 +2097,7 @@ impl *mut [T; N] { } } -// Equality for pointers +/// Pointer equality is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for *mut T { #[inline(always)] @@ -2107,9 +2107,11 @@ impl PartialEq for *mut T { } } +/// Pointer equality is an equivalence relation. #[stable(feature = "rust1", since = "1.0.0")] impl Eq for *mut T {} +/// Pointer comparison is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] impl Ord for *mut T { #[inline] @@ -2125,6 +2127,7 @@ impl Ord for *mut T { } } +/// Pointer comparison is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for *mut T { #[inline(always)] diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 1058fa42cc1e3..d93069d384edd 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -85,6 +85,20 @@ impl !Send for NonNull {} impl !Sync for NonNull {} impl NonNull { + /// Creates a pointer with the given address and no [provenance][crate::ptr#provenance]. + /// + /// For more details, see the equivalent method on a raw pointer, [`ptr::without_provenance_mut`]. + /// + /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. + #[unstable(feature = "nonnull_provenance", issue = "135243")] + #[must_use] + #[inline] + pub const fn without_provenance(addr: NonZero) -> Self { + let pointer = crate::ptr::without_provenance(addr.get()); + // SAFETY: we know `addr` is non-zero. + unsafe { NonNull { pointer } } + } + /// Creates a new `NonNull` that is dangling, but well-aligned. /// /// This is useful for initializing types which lazily allocate, like @@ -109,9 +123,22 @@ impl NonNull { #[must_use] #[inline] pub const fn dangling() -> Self { - // SAFETY: ptr::dangling_mut() returns a non-null well-aligned pointer. + let align = crate::ptr::Alignment::of::(); + NonNull::without_provenance(align.as_nonzero()) + } + + /// Converts an address back to a mutable pointer, picking up some previously 'exposed' + /// [provenance][crate::ptr#provenance]. + /// + /// For more details, see the equivalent method on a raw pointer, [`ptr::with_exposed_provenance_mut`]. + /// + /// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API. + #[unstable(feature = "nonnull_provenance", issue = "135243")] + #[inline] + pub fn with_exposed_provenance(addr: NonZero) -> Self { + // SAFETY: we know `addr` is non-zero. unsafe { - let ptr = crate::ptr::dangling_mut::(); + let ptr = crate::ptr::with_exposed_provenance_mut(addr.get()); NonNull::new_unchecked(ptr) } } @@ -224,7 +251,7 @@ impl NonNull { /// } /// ``` #[stable(feature = "nonnull", since = "1.25.0")] - #[rustc_const_stable(feature = "const_nonnull_new", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_nonnull_new", since = "1.85.0")] #[inline] pub const fn new(ptr: *mut T) -> Option { if !ptr.is_null() { @@ -282,7 +309,7 @@ impl NonNull { /// Gets the "address" portion of the pointer. /// - /// For more details see the equivalent method on a raw pointer, [`pointer::addr`]. + /// For more details, see the equivalent method on a raw pointer, [`pointer::addr`]. /// /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] @@ -294,10 +321,23 @@ impl NonNull { unsafe { NonZero::new_unchecked(self.as_ptr().addr()) } } + /// Exposes the ["provenance"][crate::ptr#provenance] part of the pointer for future use in + /// [`with_exposed_provenance`][NonNull::with_exposed_provenance] and returns the "address" portion. + /// + /// For more details, see the equivalent method on a raw pointer, [`pointer::expose_provenance`]. + /// + /// This is an [Exposed Provenance][crate::ptr#exposed-provenance] API. + #[unstable(feature = "nonnull_provenance", issue = "135243")] + pub fn expose_provenance(self) -> NonZero { + // SAFETY: The pointer is guaranteed by the type to be non-null, + // meaning that the address will be non-zero. + unsafe { NonZero::new_unchecked(self.as_ptr().expose_provenance()) } + } + /// Creates a new pointer with the given address and the [provenance][crate::ptr#provenance] of /// `self`. /// - /// For more details see the equivalent method on a raw pointer, [`pointer::with_addr`]. + /// For more details, see the equivalent method on a raw pointer, [`pointer::with_addr`]. /// /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] @@ -311,7 +351,7 @@ impl NonNull { /// Creates a new pointer by mapping `self`'s address to a new one, preserving the /// [provenance][crate::ptr#provenance] of `self`. /// - /// For more details see the equivalent method on a raw pointer, [`pointer::map_addr`]. + /// For more details, see the equivalent method on a raw pointer, [`pointer::map_addr`]. /// /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. #[must_use] @@ -1146,7 +1186,7 @@ impl NonNull { /// [`ptr::swap`]: crate::ptr::swap() #[inline(always)] #[stable(feature = "non_null_convenience", since = "1.80.0")] - #[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_swap", since = "1.85.0")] pub const unsafe fn swap(self, with: NonNull) where T: Sized, @@ -1556,7 +1596,6 @@ impl DispatchFromDyn> for NonNull where T: U unsafe impl PinCoerceUnsized for NonNull {} #[unstable(feature = "pointer_like_trait", issue = "none")] -#[cfg(not(bootstrap))] impl core::marker::PointerLike for NonNull {} #[stable(feature = "nonnull", since = "1.25.0")] diff --git a/library/core/src/range.rs b/library/core/src/range.rs index 427526fd14b91..e94499065ac9a 100644 --- a/library/core/src/range.rs +++ b/library/core/src/range.rs @@ -31,7 +31,9 @@ pub use iter::{IterRange, IterRangeFrom, IterRangeInclusive}; #[doc(inline)] pub use crate::iter::Step; #[doc(inline)] -pub use crate::ops::{Bound, OneSidedRange, RangeBounds, RangeFull, RangeTo, RangeToInclusive}; +pub use crate::ops::{ + Bound, IntoBounds, OneSidedRange, RangeBounds, RangeFull, RangeTo, RangeToInclusive, +}; /// A (half-open) range bounded inclusively below and exclusively above /// (`start..end` in a future edition). @@ -48,6 +50,7 @@ pub use crate::ops::{Bound, OneSidedRange, RangeBounds, RangeFull, RangeTo, Rang /// assert_eq!(Range::from(3..5), Range { start: 3, end: 5 }); /// assert_eq!(3 + 4 + 5, Range::from(3..6).into_iter().sum()); /// ``` +#[cfg_attr(not(bootstrap), lang = "RangeCopy")] #[derive(Clone, Copy, Default, PartialEq, Eq, Hash)] #[unstable(feature = "new_range_api", issue = "125687")] pub struct Range { @@ -174,6 +177,14 @@ impl RangeBounds for Range<&T> { } } +// #[unstable(feature = "range_into_bounds", issue = "136903")] +#[unstable(feature = "new_range_api", issue = "125687")] +impl IntoBounds for Range { + fn into_bounds(self) -> (Bound, Bound) { + (Included(self.start), Excluded(self.end)) + } +} + #[unstable(feature = "new_range_api", issue = "125687")] impl From> for legacy::Range { #[inline] @@ -205,6 +216,7 @@ impl From> for Range { /// assert_eq!(RangeInclusive::from(3..=5), RangeInclusive { start: 3, end: 5 }); /// assert_eq!(3 + 4 + 5, RangeInclusive::from(3..=5).into_iter().sum()); /// ``` +#[cfg_attr(not(bootstrap), lang = "RangeInclusiveCopy")] #[derive(Clone, Copy, PartialEq, Eq, Hash)] #[unstable(feature = "new_range_api", issue = "125687")] pub struct RangeInclusive { @@ -341,6 +353,14 @@ impl RangeBounds for RangeInclusive<&T> { } } +// #[unstable(feature = "range_into_bounds", issue = "136903")] +#[unstable(feature = "new_range_api", issue = "125687")] +impl IntoBounds for RangeInclusive { + fn into_bounds(self) -> (Bound, Bound) { + (Included(self.start), Included(self.end)) + } +} + #[unstable(feature = "new_range_api", issue = "125687")] impl From> for legacy::RangeInclusive { #[inline] @@ -388,6 +408,7 @@ impl From> for RangeInclusive { /// assert_eq!(RangeFrom::from(2..), core::range::RangeFrom { start: 2 }); /// assert_eq!(2 + 3 + 4, RangeFrom::from(2..).into_iter().take(3).sum()); /// ``` +#[cfg_attr(not(bootstrap), lang = "RangeFromCopy")] #[derive(Clone, Copy, PartialEq, Eq, Hash)] #[unstable(feature = "new_range_api", issue = "125687")] pub struct RangeFrom { @@ -476,6 +497,14 @@ impl RangeBounds for RangeFrom<&T> { } } +// #[unstable(feature = "range_into_bounds", issue = "136903")] +#[unstable(feature = "new_range_api", issue = "125687")] +impl IntoBounds for RangeFrom { + fn into_bounds(self) -> (Bound, Bound) { + (Included(self.start), Unbounded) + } +} + #[unstable(feature = "new_range_api", issue = "125687")] impl From> for legacy::RangeFrom { #[inline] diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index d2842f69008e2..a687ed7129dc8 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -49,7 +49,7 @@ impl<'a, T> IntoIterator for &'a mut [T] { /// // First, we need a slice to call the `iter` method on: /// let slice = &[1, 2, 3]; /// -/// // Then we call `iter` on the slice to get the `Iter` struct, +/// // Then we call `iter` on the slice to get the `Iter` iterator, /// // and iterate over it: /// for element in slice.iter() { /// println!("{element}"); @@ -107,24 +107,20 @@ impl<'a, T> Iter<'a, T> { /// Views the underlying data as a subslice of the original data. /// - /// This has the same lifetime as the original slice, and so the - /// iterator can continue to be used while this exists. - /// /// # Examples /// /// Basic usage: /// /// ``` /// // First, we need a slice to call the `iter` method on: - /// // struct (`&[usize]` here): /// let slice = &[1, 2, 3]; /// - /// // Then we call `iter` on the slice to get the `Iter` struct: + /// // Then we call `iter` on the slice to get the `Iter` iterator: /// let mut iter = slice.iter(); /// // Here `as_slice` still returns the whole slice, so this prints "[1, 2, 3]": /// println!("{:?}", iter.as_slice()); /// - /// // Now, we call the `next` method to remove the first element of the iterator: + /// // Now, we call the `next` method to remove the first element from the iterator: /// iter.next(); /// // Here the iterator does not contain the first element of the slice any more, /// // so `as_slice` only returns the last two elements of the slice, @@ -181,7 +177,7 @@ impl AsRef<[T]> for Iter<'_, T> { /// // First, we need a slice to call the `iter_mut` method on: /// let slice = &mut [1, 2, 3]; /// -/// // Then we call `iter_mut` on the slice to get the `IterMut` struct, +/// // Then we call `iter_mut` on the slice to get the `IterMut` iterator, /// // iterate over it and increment each element value: /// for element in slice.iter_mut() { /// *element += 1; @@ -286,25 +282,30 @@ impl<'a, T> IterMut<'a, T> { /// Views the underlying data as a subslice of the original data. /// - /// To avoid creating `&mut [T]` references that alias, the returned slice - /// borrows its lifetime from the iterator the method is applied on. - /// /// # Examples /// /// Basic usage: /// /// ``` - /// let mut slice: &mut [usize] = &mut [1, 2, 3]; + /// // First, we need a slice to call the `iter_mut` method on: + /// let slice = &mut [1, 2, 3]; /// - /// // First, we get the iterator: + /// // Then we call `iter_mut` on the slice to get the `IterMut` iterator: /// let mut iter = slice.iter_mut(); - /// // So if we check what the `as_slice` method returns here, we have "[1, 2, 3]": - /// assert_eq!(iter.as_slice(), &[1, 2, 3]); + /// // Here `as_slice` still returns the whole slice, so this prints "[1, 2, 3]": + /// println!("{:?}", iter.as_slice()); /// - /// // Next, we move to the second element of the slice: - /// iter.next(); - /// // Now `as_slice` returns "[2, 3]": - /// assert_eq!(iter.as_slice(), &[2, 3]); + /// // Now, we call the `next` method to remove the first element from the iterator + /// // and increment its value: + /// *iter.next().unwrap() += 1; + /// // Here the iterator does not contain the first element of the slice any more, + /// // so `as_slice` only returns the last two elements of the slice, + /// // and so this prints "[2, 3]": + /// println!("{:?}", iter.as_slice()); + /// + /// // The underlying slice still contains three elements, but its first element + /// // was increased by 1, so this prints "[2, 2, 3]": + /// println!("{:?}", slice); /// ``` #[must_use] #[stable(feature = "slice_iter_mut_as_slice", since = "1.53.0")] @@ -315,9 +316,6 @@ impl<'a, T> IterMut<'a, T> { /// Views the underlying data as a mutable subslice of the original data. /// - /// To avoid creating `&mut [T]` references that alias, the returned slice - /// borrows its lifetime from the iterator the method is applied on. - /// /// # Examples /// /// Basic usage: diff --git a/library/core/src/slice/iter/macros.rs b/library/core/src/slice/iter/macros.rs index 830debe02ea2b..45e320e66bc1a 100644 --- a/library/core/src/slice/iter/macros.rs +++ b/library/core/src/slice/iter/macros.rs @@ -30,7 +30,7 @@ macro_rules! if_zst { $zst_body } else { // SAFETY: for non-ZSTs, the type invariant ensures it cannot be null - let $end = unsafe { *(&raw const $this.end_or_len).cast::>() }; + let $end = unsafe { mem::transmute::<*const T, NonNull>($this.end_or_len) }; $other_body } }}; diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index df9720698d32f..cc290f75f92d4 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -7,10 +7,10 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::cmp::Ordering::{self, Equal, Greater, Less}; -use crate::intrinsics::{exact_div, select_unpredictable, unchecked_sub}; +use crate::intrinsics::{exact_div, unchecked_sub}; use crate::mem::{self, SizedTypeProperties}; use crate::num::NonZero; -use crate::ops::{Bound, OneSidedRange, Range, RangeBounds, RangeInclusive}; +use crate::ops::{OneSidedRange, OneSidedRangeBound, Range, RangeBounds, RangeInclusive}; use crate::panic::const_panic; use crate::simd::{self, Simd}; use crate::ub_checks::assert_unsafe_precondition; @@ -83,14 +83,12 @@ pub use raw::{from_raw_parts, from_raw_parts_mut}; /// which to split. Returns `None` if the split index would overflow. #[inline] fn split_point_of(range: impl OneSidedRange) -> Option<(Direction, usize)> { - use Bound::*; - - Some(match (range.start_bound(), range.end_bound()) { - (Unbounded, Excluded(i)) => (Direction::Front, *i), - (Unbounded, Included(i)) => (Direction::Front, i.checked_add(1)?), - (Excluded(i), Unbounded) => (Direction::Back, i.checked_add(1)?), - (Included(i), Unbounded) => (Direction::Back, *i), - _ => unreachable!(), + use OneSidedRangeBound::{End, EndInclusive, StartInclusive}; + + Some(match range.bound() { + (StartInclusive, i) => (Direction::Back, i), + (End, i) => (Direction::Front, i), + (EndInclusive, i) => (Direction::Front, i.checked_add(1)?), }) } @@ -913,7 +911,7 @@ impl [T] { /// assert!(v == ["a", "b", "e", "d", "c"]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "const_swap", since = "1.85.0")] #[inline] #[track_caller] pub const fn swap(&mut self, a: usize, b: usize) { @@ -987,8 +985,9 @@ impl [T] { /// assert!(v == [3, 2, 1]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_slice_reverse", issue = "135120")] #[inline] - pub fn reverse(&mut self) { + pub const fn reverse(&mut self) { let half_len = self.len() / 2; let Range { start, end } = self.as_mut_ptr_range(); @@ -1011,7 +1010,7 @@ impl [T] { revswap(front_half, back_half, half_len); #[inline] - fn revswap(a: &mut [T], b: &mut [T], n: usize) { + const fn revswap(a: &mut [T], b: &mut [T], n: usize) { debug_assert!(a.len() == n); debug_assert!(b.len() == n); @@ -1019,7 +1018,8 @@ impl [T] { // this check tells LLVM that the indexing below is // in-bounds. Then after inlining -- once the actual // lengths of the slices are known -- it's removed. - let (a, b) = (&mut a[..n], &mut b[..n]); + let (a, _) = a.split_at_mut(n); + let (b, _) = b.split_at_mut(n); let mut i = 0; while i < n { @@ -1097,10 +1097,15 @@ impl [T] { /// assert!(iter.next().is_none()); /// ``` /// - /// There's no `windows_mut`, as that existing would let safe code violate the - /// "only one `&mut` at a time to the same thing" rule. However, you can sometimes - /// use [`Cell::as_slice_of_cells`](crate::cell::Cell::as_slice_of_cells) in - /// conjunction with `windows` to accomplish something similar: + /// Because the [Iterator] trait cannot represent the required lifetimes, + /// there is no `windows_mut` analog to `windows`; + /// `[0,1,2].windows_mut(2).collect()` would violate [the rules of references] + /// (though a [LendingIterator] analog is possible). You can sometimes use + /// [`Cell::as_slice_of_cells`](crate::cell::Cell::as_slice_of_cells) in + /// conjunction with `windows` instead: + /// + /// [the rules of references]: https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#the-rules-of-references + /// [LendingIterator]: https://blog.rust-lang.org/2022/10/28/gats-stabilization.html /// ``` /// use std::cell::Cell; /// @@ -2835,7 +2840,7 @@ impl [T] { // Binary search interacts poorly with branch prediction, so force // the compiler to use conditional moves if supported by the target // architecture. - base = select_unpredictable(cmp == Greater, base, mid); + base = (cmp == Greater).select_unpredictable(base, mid); // This is imprecise in the case where `size` is odd and the // comparison returns Greater: the mid element still gets included @@ -3069,19 +3074,21 @@ impl [T] { sort::unstable::sort(self, &mut |a, b| f(a).lt(&f(b))); } - /// Reorders the slice such that the element at `index` after the reordering is at its final - /// sorted position. + /// Reorders the slice such that the element at `index` is at a sort-order position. All + /// elements before `index` will be `<=` to this value, and all elements after will be `>=` to + /// it. + /// + /// This reordering is unstable (i.e. any element that compares equal to the nth element may end + /// up at that position), in-place (i.e. does not allocate), and runs in *O*(*n*) time. This + /// function is also known as "kth element" in other libraries. + /// + /// Returns a triple that partitions the reordered slice: /// - /// This reordering has the additional property that any value at position `i < index` will be - /// less than or equal to any value at a position `j > index`. Additionally, this reordering is - /// unstable (i.e. any number of equal elements may end up at position `index`), in-place (i.e. - /// does not allocate), and runs in *O*(*n*) time. This function is also known as "kth element" - /// in other libraries. + /// * The unsorted subslice before `index`, whose elements all satisfy `x <= self[index]`. /// - /// It returns a triplet of the following from the reordered slice: the subslice prior to - /// `index`, the element at `index`, and the subslice after `index`; accordingly, the values in - /// those two subslices will respectively all be less-than-or-equal-to and - /// greater-than-or-equal-to the value of the element at `index`. + /// * The element at `index`. + /// + /// * The unsorted subslice after `index`, whose elements all satisfy `x >= self[index]`. /// /// # Current implementation /// @@ -3094,7 +3101,7 @@ impl [T] { /// /// # Panics /// - /// Panics when `index >= len()`, meaning it always panics on empty slices. + /// Panics when `index >= len()`, and so always panics on empty slices. /// /// May panic if the implementation of [`Ord`] for `T` does not implement a [total order]. /// @@ -3103,8 +3110,7 @@ impl [T] { /// ``` /// let mut v = [-5i32, 4, 2, -3, 1]; /// - /// // Find the items less than or equal to the median, the median, and greater than or equal to - /// // the median. + /// // Find the items `<=` to the median, the median itself, and the items `>=` to it. /// let (lesser, median, greater) = v.select_nth_unstable(2); /// /// assert!(lesser == [-3, -5] || lesser == [-5, -3]); @@ -3130,19 +3136,23 @@ impl [T] { sort::select::partition_at_index(self, index, T::lt) } - /// Reorders the slice with a comparator function such that the element at `index` after the - /// reordering is at its final sorted position. + /// Reorders the slice with a comparator function such that the element at `index` is at a + /// sort-order position. All elements before `index` will be `<=` to this value, and all + /// elements after will be `>=` to it, according to the comparator function. /// - /// This reordering has the additional property that any value at position `i < index` will be - /// less than or equal to any value at a position `j > index` using the comparator function. - /// Additionally, this reordering is unstable (i.e. any number of equal elements may end up at - /// position `index`), in-place (i.e. does not allocate), and runs in *O*(*n*) time. This + /// This reordering is unstable (i.e. any element that compares equal to the nth element may end + /// up at that position), in-place (i.e. does not allocate), and runs in *O*(*n*) time. This /// function is also known as "kth element" in other libraries. /// - /// It returns a triplet of the following from the slice reordered according to the provided - /// comparator function: the subslice prior to `index`, the element at `index`, and the subslice - /// after `index`; accordingly, the values in those two subslices will respectively all be - /// less-than-or-equal-to and greater-than-or-equal-to the value of the element at `index`. + /// Returns a triple partitioning the reordered slice: + /// + /// * The unsorted subslice before `index`, whose elements all satisfy + /// `compare(x, self[index]).is_le()`. + /// + /// * The element at `index`. + /// + /// * The unsorted subslice after `index`, whose elements all satisfy + /// `compare(x, self[index]).is_ge()`. /// /// # Current implementation /// @@ -3155,7 +3165,7 @@ impl [T] { /// /// # Panics /// - /// Panics when `index >= len()`, meaning it always panics on empty slices. + /// Panics when `index >= len()`, and so always panics on empty slices. /// /// May panic if `compare` does not implement a [total order]. /// @@ -3164,13 +3174,13 @@ impl [T] { /// ``` /// let mut v = [-5i32, 4, 2, -3, 1]; /// - /// // Find the items less than or equal to the median, the median, and greater than or equal to - /// // the median as if the slice were sorted in descending order. - /// let (lesser, median, greater) = v.select_nth_unstable_by(2, |a, b| b.cmp(a)); + /// // Find the items `>=` to the median, the median itself, and the items `<=` to it, by using + /// // a reversed comparator. + /// let (before, median, after) = v.select_nth_unstable_by(2, |a, b| b.cmp(a)); /// - /// assert!(lesser == [4, 2] || lesser == [2, 4]); + /// assert!(before == [4, 2] || before == [2, 4]); /// assert_eq!(median, &mut 1); - /// assert!(greater == [-3, -5] || greater == [-5, -3]); + /// assert!(after == [-3, -5] || after == [-5, -3]); /// /// // We are only guaranteed the slice will be one of the following, based on the way we sort /// // about the specified index. @@ -3195,19 +3205,21 @@ impl [T] { sort::select::partition_at_index(self, index, |a: &T, b: &T| compare(a, b) == Less) } - /// Reorders the slice with a key extraction function such that the element at `index` after the - /// reordering is at its final sorted position. + /// Reorders the slice with a key extraction function such that the element at `index` is at a + /// sort-order position. All elements before `index` will have keys `<=` to the key at `index`, + /// and all elements after will have keys `>=` to it. /// - /// This reordering has the additional property that any value at position `i < index` will be - /// less than or equal to any value at a position `j > index` using the key extraction function. - /// Additionally, this reordering is unstable (i.e. any number of equal elements may end up at - /// position `index`), in-place (i.e. does not allocate), and runs in *O*(*n*) time. This + /// This reordering is unstable (i.e. any element that compares equal to the nth element may end + /// up at that position), in-place (i.e. does not allocate), and runs in *O*(*n*) time. This /// function is also known as "kth element" in other libraries. /// - /// It returns a triplet of the following from the slice reordered according to the provided key - /// extraction function: the subslice prior to `index`, the element at `index`, and the subslice - /// after `index`; accordingly, the values in those two subslices will respectively all be - /// less-than-or-equal-to and greater-than-or-equal-to the value of the element at `index`. + /// Returns a triple partitioning the reordered slice: + /// + /// * The unsorted subslice before `index`, whose elements all satisfy `f(x) <= f(self[index])`. + /// + /// * The element at `index`. + /// + /// * The unsorted subslice after `index`, whose elements all satisfy `f(x) >= f(self[index])`. /// /// # Current implementation /// @@ -3229,8 +3241,8 @@ impl [T] { /// ``` /// let mut v = [-5i32, 4, 1, -3, 2]; /// - /// // Find the items less than or equal to the median, the median, and greater than or equal to - /// // the median as if the slice were sorted according to absolute value. + /// // Find the items `<=` to the absolute median, the absolute median itself, and the items + /// // `>=` to it. /// let (lesser, median, greater) = v.select_nth_unstable_by_key(2, |a| a.abs()); /// /// assert!(lesser == [1, 2] || lesser == [2, 1]); @@ -3701,6 +3713,7 @@ impl [T] { /// [`clone_from_slice`]: slice::clone_from_slice /// [`split_at_mut`]: slice::split_at_mut #[doc(alias = "memcpy")] + #[inline] #[stable(feature = "copy_from_slice", since = "1.9.0")] #[rustc_const_unstable(feature = "const_copy_from_slice", issue = "131415")] #[track_caller] @@ -4279,25 +4292,25 @@ impl [T] { /// /// # Examples /// - /// Taking the first three elements of a slice: + /// Splitting off the first three elements of a slice: /// /// ``` /// #![feature(slice_take)] /// /// let mut slice: &[_] = &['a', 'b', 'c', 'd']; - /// let mut first_three = slice.take(..3).unwrap(); + /// let mut first_three = slice.split_off(..3).unwrap(); /// /// assert_eq!(slice, &['d']); /// assert_eq!(first_three, &['a', 'b', 'c']); /// ``` /// - /// Taking the last two elements of a slice: + /// Splitting off the last two elements of a slice: /// /// ``` /// #![feature(slice_take)] /// /// let mut slice: &[_] = &['a', 'b', 'c', 'd']; - /// let mut tail = slice.take(2..).unwrap(); + /// let mut tail = slice.split_off(2..).unwrap(); /// /// assert_eq!(slice, &['a', 'b']); /// assert_eq!(tail, &['c', 'd']); @@ -4310,16 +4323,19 @@ impl [T] { /// /// let mut slice: &[_] = &['a', 'b', 'c', 'd']; /// - /// assert_eq!(None, slice.take(5..)); - /// assert_eq!(None, slice.take(..5)); - /// assert_eq!(None, slice.take(..=4)); + /// assert_eq!(None, slice.split_off(5..)); + /// assert_eq!(None, slice.split_off(..5)); + /// assert_eq!(None, slice.split_off(..=4)); /// let expected: &[char] = &['a', 'b', 'c', 'd']; - /// assert_eq!(Some(expected), slice.take(..4)); + /// assert_eq!(Some(expected), slice.split_off(..4)); /// ``` #[inline] #[must_use = "method does not modify the slice if the range is out of bounds"] #[unstable(feature = "slice_take", issue = "62280")] - pub fn take<'a, R: OneSidedRange>(self: &mut &'a Self, range: R) -> Option<&'a Self> { + pub fn split_off<'a, R: OneSidedRange>( + self: &mut &'a Self, + range: R, + ) -> Option<&'a Self> { let (direction, split_index) = split_point_of(range)?; if split_index > self.len() { return None; @@ -4348,13 +4364,13 @@ impl [T] { /// /// # Examples /// - /// Taking the first three elements of a slice: + /// Splitting off the first three elements of a slice: /// /// ``` /// #![feature(slice_take)] /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd']; - /// let mut first_three = slice.take_mut(..3).unwrap(); + /// let mut first_three = slice.split_off_mut(..3).unwrap(); /// /// assert_eq!(slice, &mut ['d']); /// assert_eq!(first_three, &mut ['a', 'b', 'c']); @@ -4366,7 +4382,7 @@ impl [T] { /// #![feature(slice_take)] /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd']; - /// let mut tail = slice.take_mut(2..).unwrap(); + /// let mut tail = slice.split_off_mut(2..).unwrap(); /// /// assert_eq!(slice, &mut ['a', 'b']); /// assert_eq!(tail, &mut ['c', 'd']); @@ -4379,16 +4395,16 @@ impl [T] { /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd']; /// - /// assert_eq!(None, slice.take_mut(5..)); - /// assert_eq!(None, slice.take_mut(..5)); - /// assert_eq!(None, slice.take_mut(..=4)); + /// assert_eq!(None, slice.split_off_mut(5..)); + /// assert_eq!(None, slice.split_off_mut(..5)); + /// assert_eq!(None, slice.split_off_mut(..=4)); /// let expected: &mut [_] = &mut ['a', 'b', 'c', 'd']; - /// assert_eq!(Some(expected), slice.take_mut(..4)); + /// assert_eq!(Some(expected), slice.split_off_mut(..4)); /// ``` #[inline] #[must_use = "method does not modify the slice if the range is out of bounds"] #[unstable(feature = "slice_take", issue = "62280")] - pub fn take_mut<'a, R: OneSidedRange>( + pub fn split_off_mut<'a, R: OneSidedRange>( self: &mut &'a mut Self, range: R, ) -> Option<&'a mut Self> { @@ -4420,14 +4436,14 @@ impl [T] { /// #![feature(slice_take)] /// /// let mut slice: &[_] = &['a', 'b', 'c']; - /// let first = slice.take_first().unwrap(); + /// let first = slice.split_off_first().unwrap(); /// /// assert_eq!(slice, &['b', 'c']); /// assert_eq!(first, &'a'); /// ``` #[inline] #[unstable(feature = "slice_take", issue = "62280")] - pub fn take_first<'a>(self: &mut &'a Self) -> Option<&'a T> { + pub fn split_off_first<'a>(self: &mut &'a Self) -> Option<&'a T> { let (first, rem) = self.split_first()?; *self = rem; Some(first) @@ -4444,7 +4460,7 @@ impl [T] { /// #![feature(slice_take)] /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c']; - /// let first = slice.take_first_mut().unwrap(); + /// let first = slice.split_off_first_mut().unwrap(); /// *first = 'd'; /// /// assert_eq!(slice, &['b', 'c']); @@ -4452,7 +4468,7 @@ impl [T] { /// ``` #[inline] #[unstable(feature = "slice_take", issue = "62280")] - pub fn take_first_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> { + pub fn split_off_first_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> { let (first, rem) = mem::take(self).split_first_mut()?; *self = rem; Some(first) @@ -4469,14 +4485,14 @@ impl [T] { /// #![feature(slice_take)] /// /// let mut slice: &[_] = &['a', 'b', 'c']; - /// let last = slice.take_last().unwrap(); + /// let last = slice.split_off_last().unwrap(); /// /// assert_eq!(slice, &['a', 'b']); /// assert_eq!(last, &'c'); /// ``` #[inline] #[unstable(feature = "slice_take", issue = "62280")] - pub fn take_last<'a>(self: &mut &'a Self) -> Option<&'a T> { + pub fn split_off_last<'a>(self: &mut &'a Self) -> Option<&'a T> { let (last, rem) = self.split_last()?; *self = rem; Some(last) @@ -4493,7 +4509,7 @@ impl [T] { /// #![feature(slice_take)] /// /// let mut slice: &mut [_] = &mut ['a', 'b', 'c']; - /// let last = slice.take_last_mut().unwrap(); + /// let last = slice.split_off_last_mut().unwrap(); /// *last = 'd'; /// /// assert_eq!(slice, &['a', 'b']); @@ -4501,7 +4517,7 @@ impl [T] { /// ``` #[inline] #[unstable(feature = "slice_take", issue = "62280")] - pub fn take_last_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> { + pub fn split_off_last_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> { let (last, rem) = mem::take(self).split_last_mut()?; *self = rem; Some(last) @@ -4515,7 +4531,7 @@ impl [T] { /// to single elements, while if passed an array of ranges it gives back an array of /// mutable references to slices. /// - /// For a safe alternative see [`get_many_mut`]. + /// For a safe alternative see [`get_disjoint_mut`]. /// /// # Safety /// @@ -4525,19 +4541,17 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(get_many_mut)] - /// /// let x = &mut [1, 2, 4]; /// /// unsafe { - /// let [a, b] = x.get_many_unchecked_mut([0, 2]); + /// let [a, b] = x.get_disjoint_unchecked_mut([0, 2]); /// *a *= 10; /// *b *= 100; /// } /// assert_eq!(x, &[10, 2, 400]); /// /// unsafe { - /// let [a, b] = x.get_many_unchecked_mut([0..1, 1..3]); + /// let [a, b] = x.get_disjoint_unchecked_mut([0..1, 1..3]); /// a[0] = 8; /// b[0] = 88; /// b[1] = 888; @@ -4545,7 +4559,7 @@ impl [T] { /// assert_eq!(x, &[8, 88, 888]); /// /// unsafe { - /// let [a, b] = x.get_many_unchecked_mut([1..=2, 0..=0]); + /// let [a, b] = x.get_disjoint_unchecked_mut([1..=2, 0..=0]); /// a[0] = 11; /// a[1] = 111; /// b[0] = 1; @@ -4553,16 +4567,16 @@ impl [T] { /// assert_eq!(x, &[1, 11, 111]); /// ``` /// - /// [`get_many_mut`]: slice::get_many_mut + /// [`get_disjoint_mut`]: slice::get_disjoint_mut /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html - #[unstable(feature = "get_many_mut", issue = "104642")] + #[stable(feature = "get_many_mut", since = "1.86.0")] #[inline] - pub unsafe fn get_many_unchecked_mut( + pub unsafe fn get_disjoint_unchecked_mut( &mut self, indices: [I; N], ) -> [&mut I::Output; N] where - I: GetManyMutIndex + SliceIndex, + I: GetDisjointMutIndex + SliceIndex, { // NB: This implementation is written as it is because any variation of // `indices.map(|i| self.get_unchecked_mut(i))` would make miri unhappy, @@ -4601,42 +4615,40 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(get_many_mut)] - /// /// let v = &mut [1, 2, 3]; - /// if let Ok([a, b]) = v.get_many_mut([0, 2]) { + /// if let Ok([a, b]) = v.get_disjoint_mut([0, 2]) { /// *a = 413; /// *b = 612; /// } /// assert_eq!(v, &[413, 2, 612]); /// - /// if let Ok([a, b]) = v.get_many_mut([0..1, 1..3]) { + /// if let Ok([a, b]) = v.get_disjoint_mut([0..1, 1..3]) { /// a[0] = 8; /// b[0] = 88; /// b[1] = 888; /// } /// assert_eq!(v, &[8, 88, 888]); /// - /// if let Ok([a, b]) = v.get_many_mut([1..=2, 0..=0]) { + /// if let Ok([a, b]) = v.get_disjoint_mut([1..=2, 0..=0]) { /// a[0] = 11; /// a[1] = 111; /// b[0] = 1; /// } /// assert_eq!(v, &[1, 11, 111]); /// ``` - #[unstable(feature = "get_many_mut", issue = "104642")] + #[stable(feature = "get_many_mut", since = "1.86.0")] #[inline] - pub fn get_many_mut( + pub fn get_disjoint_mut( &mut self, indices: [I; N], - ) -> Result<[&mut I::Output; N], GetManyMutError> + ) -> Result<[&mut I::Output; N], GetDisjointMutError> where - I: GetManyMutIndex + SliceIndex, + I: GetDisjointMutIndex + SliceIndex, { - get_many_check_valid(&indices, self.len())?; - // SAFETY: The `get_many_check_valid()` call checked that all indices + get_disjoint_check_valid(&indices, self.len())?; + // SAFETY: The `get_disjoint_check_valid()` call checked that all indices // are disjunct and in bounds. - unsafe { Ok(self.get_many_unchecked_mut(indices)) } + unsafe { Ok(self.get_disjoint_unchecked_mut(indices)) } } /// Returns the index that an element reference points to. @@ -4978,26 +4990,26 @@ impl SlicePattern for [T; N] { /// This will do `binomial(N + 1, 2) = N * (N + 1) / 2 = 0, 1, 3, 6, 10, ..` /// comparison operations. #[inline] -fn get_many_check_valid( +fn get_disjoint_check_valid( indices: &[I; N], len: usize, -) -> Result<(), GetManyMutError> { +) -> Result<(), GetDisjointMutError> { // NB: The optimizer should inline the loops into a sequence // of instructions without additional branching. for (i, idx) in indices.iter().enumerate() { if !idx.is_in_bounds(len) { - return Err(GetManyMutError::IndexOutOfBounds); + return Err(GetDisjointMutError::IndexOutOfBounds); } for idx2 in &indices[..i] { if idx.is_overlapping(idx2) { - return Err(GetManyMutError::OverlappingIndices); + return Err(GetDisjointMutError::OverlappingIndices); } } } Ok(()) } -/// The error type returned by [`get_many_mut`][`slice::get_many_mut`]. +/// The error type returned by [`get_disjoint_mut`][`slice::get_disjoint_mut`]. /// /// It indicates one of two possible errors: /// - An index is out-of-bounds. @@ -5007,74 +5019,75 @@ fn get_many_check_valid( /// # Examples /// /// ``` -/// #![feature(get_many_mut)] -/// use std::slice::GetManyMutError; +/// use std::slice::GetDisjointMutError; /// /// let v = &mut [1, 2, 3]; -/// assert_eq!(v.get_many_mut([0, 999]), Err(GetManyMutError::IndexOutOfBounds)); -/// assert_eq!(v.get_many_mut([1, 1]), Err(GetManyMutError::OverlappingIndices)); +/// assert_eq!(v.get_disjoint_mut([0, 999]), Err(GetDisjointMutError::IndexOutOfBounds)); +/// assert_eq!(v.get_disjoint_mut([1, 1]), Err(GetDisjointMutError::OverlappingIndices)); /// ``` -#[unstable(feature = "get_many_mut", issue = "104642")] +#[stable(feature = "get_many_mut", since = "1.86.0")] #[derive(Debug, Clone, PartialEq, Eq)] -pub enum GetManyMutError { +pub enum GetDisjointMutError { /// An index provided was out-of-bounds for the slice. IndexOutOfBounds, /// Two indices provided were overlapping. OverlappingIndices, } -#[unstable(feature = "get_many_mut", issue = "104642")] -impl fmt::Display for GetManyMutError { +#[stable(feature = "get_many_mut", since = "1.86.0")] +impl fmt::Display for GetDisjointMutError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let msg = match self { - GetManyMutError::IndexOutOfBounds => "an index is out of bounds", - GetManyMutError::OverlappingIndices => "there were overlapping indices", + GetDisjointMutError::IndexOutOfBounds => "an index is out of bounds", + GetDisjointMutError::OverlappingIndices => "there were overlapping indices", }; fmt::Display::fmt(msg, f) } } -mod private_get_many_mut_index { +mod private_get_disjoint_mut_index { use super::{Range, RangeInclusive, range}; - #[unstable(feature = "get_many_mut_helpers", issue = "none")] + #[unstable(feature = "get_disjoint_mut_helpers", issue = "none")] pub trait Sealed {} - #[unstable(feature = "get_many_mut_helpers", issue = "none")] + #[unstable(feature = "get_disjoint_mut_helpers", issue = "none")] impl Sealed for usize {} - #[unstable(feature = "get_many_mut_helpers", issue = "none")] + #[unstable(feature = "get_disjoint_mut_helpers", issue = "none")] impl Sealed for Range {} - #[unstable(feature = "get_many_mut_helpers", issue = "none")] + #[unstable(feature = "get_disjoint_mut_helpers", issue = "none")] impl Sealed for RangeInclusive {} - #[unstable(feature = "get_many_mut_helpers", issue = "none")] + #[unstable(feature = "get_disjoint_mut_helpers", issue = "none")] impl Sealed for range::Range {} - #[unstable(feature = "get_many_mut_helpers", issue = "none")] + #[unstable(feature = "get_disjoint_mut_helpers", issue = "none")] impl Sealed for range::RangeInclusive {} } -/// A helper trait for `<[T]>::get_many_mut()`. +/// A helper trait for `<[T]>::get_disjoint_mut()`. /// /// # Safety /// /// If `is_in_bounds()` returns `true` and `is_overlapping()` returns `false`, /// it must be safe to index the slice with the indices. -#[unstable(feature = "get_many_mut_helpers", issue = "none")] -pub unsafe trait GetManyMutIndex: Clone + private_get_many_mut_index::Sealed { +#[unstable(feature = "get_disjoint_mut_helpers", issue = "none")] +pub unsafe trait GetDisjointMutIndex: + Clone + private_get_disjoint_mut_index::Sealed +{ /// Returns `true` if `self` is in bounds for `len` slice elements. - #[unstable(feature = "get_many_mut_helpers", issue = "none")] + #[unstable(feature = "get_disjoint_mut_helpers", issue = "none")] fn is_in_bounds(&self, len: usize) -> bool; /// Returns `true` if `self` overlaps with `other`. /// /// Note that we don't consider zero-length ranges to overlap at the beginning or the end, /// but do consider them to overlap in the middle. - #[unstable(feature = "get_many_mut_helpers", issue = "none")] + #[unstable(feature = "get_disjoint_mut_helpers", issue = "none")] fn is_overlapping(&self, other: &Self) -> bool; } -#[unstable(feature = "get_many_mut_helpers", issue = "none")] +#[unstable(feature = "get_disjoint_mut_helpers", issue = "none")] // SAFETY: We implement `is_in_bounds()` and `is_overlapping()` correctly. -unsafe impl GetManyMutIndex for usize { +unsafe impl GetDisjointMutIndex for usize { #[inline] fn is_in_bounds(&self, len: usize) -> bool { *self < len @@ -5086,9 +5099,9 @@ unsafe impl GetManyMutIndex for usize { } } -#[unstable(feature = "get_many_mut_helpers", issue = "none")] +#[unstable(feature = "get_disjoint_mut_helpers", issue = "none")] // SAFETY: We implement `is_in_bounds()` and `is_overlapping()` correctly. -unsafe impl GetManyMutIndex for Range { +unsafe impl GetDisjointMutIndex for Range { #[inline] fn is_in_bounds(&self, len: usize) -> bool { (self.start <= self.end) & (self.end <= len) @@ -5100,9 +5113,9 @@ unsafe impl GetManyMutIndex for Range { } } -#[unstable(feature = "get_many_mut_helpers", issue = "none")] +#[unstable(feature = "get_disjoint_mut_helpers", issue = "none")] // SAFETY: We implement `is_in_bounds()` and `is_overlapping()` correctly. -unsafe impl GetManyMutIndex for RangeInclusive { +unsafe impl GetDisjointMutIndex for RangeInclusive { #[inline] fn is_in_bounds(&self, len: usize) -> bool { (self.start <= self.end) & (self.end < len) @@ -5114,9 +5127,9 @@ unsafe impl GetManyMutIndex for RangeInclusive { } } -#[unstable(feature = "get_many_mut_helpers", issue = "none")] +#[unstable(feature = "get_disjoint_mut_helpers", issue = "none")] // SAFETY: We implement `is_in_bounds()` and `is_overlapping()` correctly. -unsafe impl GetManyMutIndex for range::Range { +unsafe impl GetDisjointMutIndex for range::Range { #[inline] fn is_in_bounds(&self, len: usize) -> bool { Range::from(*self).is_in_bounds(len) @@ -5128,9 +5141,9 @@ unsafe impl GetManyMutIndex for range::Range { } } -#[unstable(feature = "get_many_mut_helpers", issue = "none")] +#[unstable(feature = "get_disjoint_mut_helpers", issue = "none")] // SAFETY: We implement `is_in_bounds()` and `is_overlapping()` correctly. -unsafe impl GetManyMutIndex for range::RangeInclusive { +unsafe impl GetDisjointMutIndex for range::RangeInclusive { #[inline] fn is_in_bounds(&self, len: usize) -> bool { RangeInclusive::from(*self).is_in_bounds(len) diff --git a/library/core/src/slice/rotate.rs b/library/core/src/slice/rotate.rs index 1e4865a7caad9..5d5ee4c7b6240 100644 --- a/library/core/src/slice/rotate.rs +++ b/library/core/src/slice/rotate.rs @@ -1,6 +1,8 @@ use crate::mem::{self, MaybeUninit, SizedTypeProperties}; use crate::{cmp, ptr}; +type BufType = [usize; 32]; + /// Rotates the range `[mid-left, mid+right)` such that the element at `mid` becomes the first /// element. Equivalently, rotates the range `left` elements to the left or `right` elements to the /// right. @@ -8,14 +10,82 @@ use crate::{cmp, ptr}; /// # Safety /// /// The specified range must be valid for reading and writing. +#[inline] +pub(super) unsafe fn ptr_rotate(left: usize, mid: *mut T, right: usize) { + if T::IS_ZST { + return; + } + // abort early if the rotate is a no-op + if (left == 0) || (right == 0) { + return; + } + // `T` is not a zero-sized type, so it's okay to divide by its size. + if !cfg!(feature = "optimize_for_size") + && cmp::min(left, right) <= mem::size_of::() / mem::size_of::() + { + // SAFETY: guaranteed by the caller + unsafe { ptr_rotate_memmove(left, mid, right) }; + } else if !cfg!(feature = "optimize_for_size") + && ((left + right < 24) || (mem::size_of::() > mem::size_of::<[usize; 4]>())) + { + // SAFETY: guaranteed by the caller + unsafe { ptr_rotate_gcd(left, mid, right) } + } else { + // SAFETY: guaranteed by the caller + unsafe { ptr_rotate_swap(left, mid, right) } + } +} + +/// Algorithm 1 is used if `min(left, right)` is small enough to fit onto a stack buffer. The +/// `min(left, right)` elements are copied onto the buffer, `memmove` is applied to the others, and +/// the ones on the buffer are moved back into the hole on the opposite side of where they +/// originated. /// -/// # Algorithm +/// # Safety /// -/// Algorithm 1 is used for small values of `left + right` or for large `T`. The elements are moved -/// into their final positions one at a time starting at `mid - left` and advancing by `right` steps -/// modulo `left + right`, such that only one temporary is needed. Eventually, we arrive back at -/// `mid - left`. However, if `gcd(left + right, right)` is not 1, the above steps skipped over -/// elements. For example: +/// The specified range must be valid for reading and writing. +#[inline] +unsafe fn ptr_rotate_memmove(left: usize, mid: *mut T, right: usize) { + // The `[T; 0]` here is to ensure this is appropriately aligned for T + let mut rawarray = MaybeUninit::<(BufType, [T; 0])>::uninit(); + let buf = rawarray.as_mut_ptr() as *mut T; + // SAFETY: `mid-left <= mid-left+right < mid+right` + let dim = unsafe { mid.sub(left).add(right) }; + if left <= right { + // SAFETY: + // + // 1) The `if` condition about the sizes ensures `[mid-left; left]` will fit in + // `buf` without overflow and `buf` was created just above and so cannot be + // overlapped with any value of `[mid-left; left]` + // 2) [mid-left, mid+right) are all valid for reading and writing and we don't care + // about overlaps here. + // 3) The `if` condition about `left <= right` ensures writing `left` elements to + // `dim = mid-left+right` is valid because: + // - `buf` is valid and `left` elements were written in it in 1) + // - `dim+left = mid-left+right+left = mid+right` and we write `[dim, dim+left)` + unsafe { + // 1) + ptr::copy_nonoverlapping(mid.sub(left), buf, left); + // 2) + ptr::copy(mid, mid.sub(left), right); + // 3) + ptr::copy_nonoverlapping(buf, dim, left); + } + } else { + // SAFETY: same reasoning as above but with `left` and `right` reversed + unsafe { + ptr::copy_nonoverlapping(mid, buf, right); + ptr::copy(mid.sub(left), dim, left); + ptr::copy_nonoverlapping(buf, mid.sub(left), right); + } + } +} + +/// Algorithm 2 is used for small values of `left + right` or for large `T`. The elements +/// are moved into their final positions one at a time starting at `mid - left` and advancing by +/// `right` steps modulo `left + right`, such that only one temporary is needed. Eventually, we +/// arrive back at `mid - left`. However, if `gcd(left + right, right)` is not 1, the above steps +/// skipped over elements. For example: /// ```text /// left = 10, right = 6 /// the `^` indicates an element in its final place @@ -39,17 +109,104 @@ use crate::{cmp, ptr}; /// `gcd(left + right, right)` value). The end result is that all elements are finalized once and /// only once. /// -/// Algorithm 2 is used if `left + right` is large but `min(left, right)` is small enough to -/// fit onto a stack buffer. The `min(left, right)` elements are copied onto the buffer, `memmove` -/// is applied to the others, and the ones on the buffer are moved back into the hole on the -/// opposite side of where they originated. -/// -/// Algorithms that can be vectorized outperform the above once `left + right` becomes large enough. -/// Algorithm 1 can be vectorized by chunking and performing many rounds at once, but there are too +/// Algorithm 2 can be vectorized by chunking and performing many rounds at once, but there are too /// few rounds on average until `left + right` is enormous, and the worst case of a single -/// round is always there. Instead, algorithm 3 utilizes repeated swapping of -/// `min(left, right)` elements until a smaller rotate problem is left. +/// round is always there. +/// +/// # Safety +/// +/// The specified range must be valid for reading and writing. +#[inline] +unsafe fn ptr_rotate_gcd(left: usize, mid: *mut T, right: usize) { + // Algorithm 2 + // Microbenchmarks indicate that the average performance for random shifts is better all + // the way until about `left + right == 32`, but the worst case performance breaks even + // around 16. 24 was chosen as middle ground. If the size of `T` is larger than 4 + // `usize`s, this algorithm also outperforms other algorithms. + // SAFETY: callers must ensure `mid - left` is valid for reading and writing. + let x = unsafe { mid.sub(left) }; + // beginning of first round + // SAFETY: see previous comment. + let mut tmp: T = unsafe { x.read() }; + let mut i = right; + // `gcd` can be found before hand by calculating `gcd(left + right, right)`, + // but it is faster to do one loop which calculates the gcd as a side effect, then + // doing the rest of the chunk + let mut gcd = right; + // benchmarks reveal that it is faster to swap temporaries all the way through instead + // of reading one temporary once, copying backwards, and then writing that temporary at + // the very end. This is possibly due to the fact that swapping or replacing temporaries + // uses only one memory address in the loop instead of needing to manage two. + loop { + // [long-safety-expl] + // SAFETY: callers must ensure `[left, left+mid+right)` are all valid for reading and + // writing. + // + // - `i` start with `right` so `mid-left <= x+i = x+right = mid-left+right < mid+right` + // - `i <= left+right-1` is always true + // - if `i < left`, `right` is added so `i < left+right` and on the next + // iteration `left` is removed from `i` so it doesn't go further + // - if `i >= left`, `left` is removed immediately and so it doesn't go further. + // - overflows cannot happen for `i` since the function's safety contract ask for + // `mid+right-1 = x+left+right` to be valid for writing + // - underflows cannot happen because `i` must be bigger or equal to `left` for + // a subtraction of `left` to happen. + // + // So `x+i` is valid for reading and writing if the caller respected the contract + tmp = unsafe { x.add(i).replace(tmp) }; + // instead of incrementing `i` and then checking if it is outside the bounds, we + // check if `i` will go outside the bounds on the next increment. This prevents + // any wrapping of pointers or `usize`. + if i >= left { + i -= left; + if i == 0 { + // end of first round + // SAFETY: tmp has been read from a valid source and x is valid for writing + // according to the caller. + unsafe { x.write(tmp) }; + break; + } + // this conditional must be here if `left + right >= 15` + if i < gcd { + gcd = i; + } + } else { + i += right; + } + } + // finish the chunk with more rounds + for start in 1..gcd { + // SAFETY: `gcd` is at most equal to `right` so all values in `1..gcd` are valid for + // reading and writing as per the function's safety contract, see [long-safety-expl] + // above + tmp = unsafe { x.add(start).read() }; + // [safety-expl-addition] + // + // Here `start < gcd` so `start < right` so `i < right+right`: `right` being the + // greatest common divisor of `(left+right, right)` means that `left = right` so + // `i < left+right` so `x+i = mid-left+i` is always valid for reading and writing + // according to the function's safety contract. + i = start + right; + loop { + // SAFETY: see [long-safety-expl] and [safety-expl-addition] + tmp = unsafe { x.add(i).replace(tmp) }; + if i >= left { + i -= left; + if i == start { + // SAFETY: see [long-safety-expl] and [safety-expl-addition] + unsafe { x.add(start).write(tmp) }; + break; + } + } else { + i += right; + } + } + } +} + +/// Algorithm 3 utilizes repeated swapping of `min(left, right)` elements. /// +/// /// /// ```text /// left = 11, right = 4 /// [4 5 6 7 8 9 10 11 12 13 14 . 0 1 2 3] @@ -60,144 +217,14 @@ use crate::{cmp, ptr}; /// we cannot swap any more, but a smaller rotation problem is left to solve /// ``` /// when `left < right` the swapping happens from the left instead. -pub unsafe fn ptr_rotate(mut left: usize, mut mid: *mut T, mut right: usize) { - type BufType = [usize; 32]; - if T::IS_ZST { - return; - } +/// +/// # Safety +/// +/// The specified range must be valid for reading and writing. +#[inline] +unsafe fn ptr_rotate_swap(mut left: usize, mut mid: *mut T, mut right: usize) { loop { - // N.B. the below algorithms can fail if these cases are not checked - if (right == 0) || (left == 0) { - return; - } - if !cfg!(feature = "optimize_for_size") - && ((left + right < 24) || (mem::size_of::() > mem::size_of::<[usize; 4]>())) - { - // Algorithm 1 - // Microbenchmarks indicate that the average performance for random shifts is better all - // the way until about `left + right == 32`, but the worst case performance breaks even - // around 16. 24 was chosen as middle ground. If the size of `T` is larger than 4 - // `usize`s, this algorithm also outperforms other algorithms. - // SAFETY: callers must ensure `mid - left` is valid for reading and writing. - let x = unsafe { mid.sub(left) }; - // beginning of first round - // SAFETY: see previous comment. - let mut tmp: T = unsafe { x.read() }; - let mut i = right; - // `gcd` can be found before hand by calculating `gcd(left + right, right)`, - // but it is faster to do one loop which calculates the gcd as a side effect, then - // doing the rest of the chunk - let mut gcd = right; - // benchmarks reveal that it is faster to swap temporaries all the way through instead - // of reading one temporary once, copying backwards, and then writing that temporary at - // the very end. This is possibly due to the fact that swapping or replacing temporaries - // uses only one memory address in the loop instead of needing to manage two. - loop { - // [long-safety-expl] - // SAFETY: callers must ensure `[left, left+mid+right)` are all valid for reading and - // writing. - // - // - `i` start with `right` so `mid-left <= x+i = x+right = mid-left+right < mid+right` - // - `i <= left+right-1` is always true - // - if `i < left`, `right` is added so `i < left+right` and on the next - // iteration `left` is removed from `i` so it doesn't go further - // - if `i >= left`, `left` is removed immediately and so it doesn't go further. - // - overflows cannot happen for `i` since the function's safety contract ask for - // `mid+right-1 = x+left+right` to be valid for writing - // - underflows cannot happen because `i` must be bigger or equal to `left` for - // a subtraction of `left` to happen. - // - // So `x+i` is valid for reading and writing if the caller respected the contract - tmp = unsafe { x.add(i).replace(tmp) }; - // instead of incrementing `i` and then checking if it is outside the bounds, we - // check if `i` will go outside the bounds on the next increment. This prevents - // any wrapping of pointers or `usize`. - if i >= left { - i -= left; - if i == 0 { - // end of first round - // SAFETY: tmp has been read from a valid source and x is valid for writing - // according to the caller. - unsafe { x.write(tmp) }; - break; - } - // this conditional must be here if `left + right >= 15` - if i < gcd { - gcd = i; - } - } else { - i += right; - } - } - // finish the chunk with more rounds - for start in 1..gcd { - // SAFETY: `gcd` is at most equal to `right` so all values in `1..gcd` are valid for - // reading and writing as per the function's safety contract, see [long-safety-expl] - // above - tmp = unsafe { x.add(start).read() }; - // [safety-expl-addition] - // - // Here `start < gcd` so `start < right` so `i < right+right`: `right` being the - // greatest common divisor of `(left+right, right)` means that `left = right` so - // `i < left+right` so `x+i = mid-left+i` is always valid for reading and writing - // according to the function's safety contract. - i = start + right; - loop { - // SAFETY: see [long-safety-expl] and [safety-expl-addition] - tmp = unsafe { x.add(i).replace(tmp) }; - if i >= left { - i -= left; - if i == start { - // SAFETY: see [long-safety-expl] and [safety-expl-addition] - unsafe { x.add(start).write(tmp) }; - break; - } - } else { - i += right; - } - } - } - return; - // `T` is not a zero-sized type, so it's okay to divide by its size. - } else if !cfg!(feature = "optimize_for_size") - && cmp::min(left, right) <= mem::size_of::() / mem::size_of::() - { - // Algorithm 2 - // The `[T; 0]` here is to ensure this is appropriately aligned for T - let mut rawarray = MaybeUninit::<(BufType, [T; 0])>::uninit(); - let buf = rawarray.as_mut_ptr() as *mut T; - // SAFETY: `mid-left <= mid-left+right < mid+right` - let dim = unsafe { mid.sub(left).add(right) }; - if left <= right { - // SAFETY: - // - // 1) The `else if` condition about the sizes ensures `[mid-left; left]` will fit in - // `buf` without overflow and `buf` was created just above and so cannot be - // overlapped with any value of `[mid-left; left]` - // 2) [mid-left, mid+right) are all valid for reading and writing and we don't care - // about overlaps here. - // 3) The `if` condition about `left <= right` ensures writing `left` elements to - // `dim = mid-left+right` is valid because: - // - `buf` is valid and `left` elements were written in it in 1) - // - `dim+left = mid-left+right+left = mid+right` and we write `[dim, dim+left)` - unsafe { - // 1) - ptr::copy_nonoverlapping(mid.sub(left), buf, left); - // 2) - ptr::copy(mid, mid.sub(left), right); - // 3) - ptr::copy_nonoverlapping(buf, dim, left); - } - } else { - // SAFETY: same reasoning as above but with `left` and `right` reversed - unsafe { - ptr::copy_nonoverlapping(mid, buf, right); - ptr::copy(mid.sub(left), dim, left); - ptr::copy_nonoverlapping(buf, mid.sub(left), right); - } - } - return; - } else if left >= right { + if left >= right { // Algorithm 3 // There is an alternate way of swapping that involves finding where the last swap // of this algorithm would be, and swapping using that last chunk instead of swapping @@ -233,5 +260,8 @@ pub unsafe fn ptr_rotate(mut left: usize, mut mid: *mut T, mut right: usize) } } } + if (right == 0) || (left == 0) { + return; + } } } diff --git a/library/core/src/slice/sort/stable/drift.rs b/library/core/src/slice/sort/stable/drift.rs index 644e75a4581e9..cf1df1e91a50d 100644 --- a/library/core/src/slice/sort/stable/drift.rs +++ b/library/core/src/slice/sort/stable/drift.rs @@ -10,8 +10,8 @@ use crate::{cmp, intrinsics}; /// Sorts `v` based on comparison function `is_less`. If `eager_sort` is true, /// it will only do small-sorts and physical merges, ensuring O(N * log(N)) -/// worst-case complexity. `scratch.len()` must be at least `max(v.len() / 2, -/// MIN_SMALL_SORT_SCRATCH_LEN)` otherwise the implementation may abort. +/// worst-case complexity. `scratch.len()` must be at least +/// `max(v.len() - v.len() / 2, SMALL_SORT_GENERAL_SCRATCH_LEN)` otherwise the implementation may abort. /// Fully ascending and descending inputs will be sorted with exactly N - 1 /// comparisons. /// diff --git a/library/core/src/slice/sort/stable/mod.rs b/library/core/src/slice/sort/stable/mod.rs index 7adcc83b818d1..3ff2e71fd05bc 100644 --- a/library/core/src/slice/sort/stable/mod.rs +++ b/library/core/src/slice/sort/stable/mod.rs @@ -41,6 +41,8 @@ pub fn sort bool, BufT: BufGuard>(v: &mut [T], is_less cfg_if! { if #[cfg(any(feature = "optimize_for_size", target_pointer_width = "16"))] { + // Unlike driftsort, mergesort only requires len / 2, + // not len - len / 2. let alloc_len = len / 2; cfg_if! { @@ -91,16 +93,26 @@ fn driftsort_main bool, BufT: BufGuard>(v: &mut [T], i // By allocating n elements of memory we can ensure the entire input can // be sorted using stable quicksort, which allows better performance on // random and low-cardinality distributions. However, we still want to - // reduce our memory usage to n / 2 for large inputs. We do this by scaling - // our allocation as max(n / 2, min(n, 8MB)), ensuring we scale like n for - // small inputs and n / 2 for large inputs, without a sudden drop off. We - // also need to ensure our alloc >= MIN_SMALL_SORT_SCRATCH_LEN, as the + // reduce our memory usage to n - n / 2 for large inputs. We do this by scaling + // our allocation as max(n - n / 2, min(n, 8MB)), ensuring we scale like n for + // small inputs and n - n / 2 for large inputs, without a sudden drop off. We + // also need to ensure our alloc >= SMALL_SORT_GENERAL_SCRATCH_LEN, as the // small-sort always needs this much memory. + // + // driftsort will produce unsorted runs of up to min_good_run_len, which + // is at most len - len / 2. + // Unsorted runs need to be processed by quicksort, which requires as much + // scratch space as the run length, therefore the scratch space must be at + // least len - len / 2. + // If min_good_run_len is ever modified, this code must be updated to allocate + // the correct scratch size for it. const MAX_FULL_ALLOC_BYTES: usize = 8_000_000; // 8MB let max_full_alloc = MAX_FULL_ALLOC_BYTES / mem::size_of::(); let len = v.len(); - let alloc_len = - cmp::max(cmp::max(len / 2, cmp::min(len, max_full_alloc)), SMALL_SORT_GENERAL_SCRATCH_LEN); + let alloc_len = cmp::max( + cmp::max(len - len / 2, cmp::min(len, max_full_alloc)), + SMALL_SORT_GENERAL_SCRATCH_LEN, + ); // For small inputs 4KiB of stack storage suffices, which allows us to avoid // calling the (de-)allocator. Benchmarks showed this was quite beneficial. diff --git a/library/core/src/slice/sort/stable/quicksort.rs b/library/core/src/slice/sort/stable/quicksort.rs index 0c8308bfce00e..630c6ff907703 100644 --- a/library/core/src/slice/sort/stable/quicksort.rs +++ b/library/core/src/slice/sort/stable/quicksort.rs @@ -7,6 +7,8 @@ use crate::slice::sort::shared::smallsort::StableSmallSortTypeImpl; use crate::{intrinsics, ptr}; /// Sorts `v` recursively using quicksort. +/// `scratch.len()` must be at least `max(v.len() - v.len() / 2, SMALL_SORT_GENERAL_SCRATCH_LEN)` +/// otherwise the implementation may abort. /// /// `limit` when initialized with `c*log(v.len())` for some c ensures we do not /// overflow the stack or go quadratic. diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 8a473b398bb5f..9800888efc37b 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -160,6 +160,182 @@ impl str { self.len() == 0 } + /// Converts a slice of bytes to a string slice. + /// + /// A string slice ([`&str`]) is made of bytes ([`u8`]), and a byte slice + /// ([`&[u8]`][byteslice]) is made of bytes, so this function converts between + /// the two. Not all byte slices are valid string slices, however: [`&str`] requires + /// that it is valid UTF-8. `from_utf8()` checks to ensure that the bytes are valid + /// UTF-8, and then does the conversion. + /// + /// [`&str`]: str + /// [byteslice]: prim@slice + /// + /// If you are sure that the byte slice is valid UTF-8, and you don't want to + /// incur the overhead of the validity check, there is an unsafe version of + /// this function, [`from_utf8_unchecked`], which has the same + /// behavior but skips the check. + /// + /// If you need a `String` instead of a `&str`, consider + /// [`String::from_utf8`][string]. + /// + /// [string]: ../std/string/struct.String.html#method.from_utf8 + /// + /// Because you can stack-allocate a `[u8; N]`, and you can take a + /// [`&[u8]`][byteslice] of it, this function is one way to have a + /// stack-allocated string. There is an example of this in the + /// examples section below. + /// + /// [byteslice]: slice + /// + /// # Errors + /// + /// Returns `Err` if the slice is not UTF-8 with a description as to why the + /// provided slice is not UTF-8. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::str; + /// + /// // some bytes, in a vector + /// let sparkle_heart = vec![240, 159, 146, 150]; + /// + /// // We can use the ? (try) operator to check if the bytes are valid + /// let sparkle_heart = str::from_utf8(&sparkle_heart)?; + /// + /// assert_eq!("💖", sparkle_heart); + /// # Ok::<_, str::Utf8Error>(()) + /// ``` + /// + /// Incorrect bytes: + /// + /// ``` + /// use std::str; + /// + /// // some invalid bytes, in a vector + /// let sparkle_heart = vec![0, 159, 146, 150]; + /// + /// assert!(str::from_utf8(&sparkle_heart).is_err()); + /// ``` + /// + /// See the docs for [`Utf8Error`] for more details on the kinds of + /// errors that can be returned. + /// + /// A "stack allocated string": + /// + /// ``` + /// use std::str; + /// + /// // some bytes, in a stack-allocated array + /// let sparkle_heart = [240, 159, 146, 150]; + /// + /// // We know these bytes are valid, so just use `unwrap()`. + /// let sparkle_heart: &str = str::from_utf8(&sparkle_heart).unwrap(); + /// + /// assert_eq!("💖", sparkle_heart); + /// ``` + #[unstable(feature = "inherent_str_constructors", issue = "131114")] + pub const fn from_utf8(v: &[u8]) -> Result<&str, Utf8Error> { + converts::from_utf8(v) + } + + /// Converts a mutable slice of bytes to a mutable string slice. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::str; + /// + /// // "Hello, Rust!" as a mutable vector + /// let mut hellorust = vec![72, 101, 108, 108, 111, 44, 32, 82, 117, 115, 116, 33]; + /// + /// // As we know these bytes are valid, we can use `unwrap()` + /// let outstr = str::from_utf8_mut(&mut hellorust).unwrap(); + /// + /// assert_eq!("Hello, Rust!", outstr); + /// ``` + /// + /// Incorrect bytes: + /// + /// ``` + /// use std::str; + /// + /// // Some invalid bytes in a mutable vector + /// let mut invalid = vec![128, 223]; + /// + /// assert!(str::from_utf8_mut(&mut invalid).is_err()); + /// ``` + /// See the docs for [`Utf8Error`] for more details on the kinds of + /// errors that can be returned. + #[unstable(feature = "inherent_str_constructors", issue = "131114")] + #[rustc_const_unstable(feature = "const_str_from_utf8", issue = "91006")] + pub const fn from_utf8_mut(v: &mut [u8]) -> Result<&mut str, Utf8Error> { + converts::from_utf8_mut(v) + } + + /// Converts a slice of bytes to a string slice without checking + /// that the string contains valid UTF-8. + /// + /// See the safe version, [`from_utf8`], for more information. + /// + /// # Safety + /// + /// The bytes passed in must be valid UTF-8. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::str; + /// + /// // some bytes, in a vector + /// let sparkle_heart = vec![240, 159, 146, 150]; + /// + /// let sparkle_heart = unsafe { + /// str::from_utf8_unchecked(&sparkle_heart) + /// }; + /// + /// assert_eq!("💖", sparkle_heart); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "inherent_str_constructors", issue = "131114")] + pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str { + // SAFETY: converts::from_utf8_unchecked has the same safety requirements as this function. + unsafe { converts::from_utf8_unchecked(v) } + } + + /// Converts a slice of bytes to a string slice without checking + /// that the string contains valid UTF-8; mutable version. + /// + /// See the immutable version, [`from_utf8_unchecked()`] for more information. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::str; + /// + /// let mut heart = vec![240, 159, 146, 150]; + /// let heart = unsafe { str::from_utf8_unchecked_mut(&mut heart) }; + /// + /// assert_eq!("💖", heart); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "inherent_str_constructors", issue = "131114")] + pub const unsafe fn from_utf8_unchecked_mut(v: &mut [u8]) -> &mut str { + // SAFETY: converts::from_utf8_unchecked_mut has the same safety requirements as this function. + unsafe { converts::from_utf8_unchecked_mut(v) } + } + /// Checks that `index`-th byte is the first byte in a UTF-8 code point /// sequence or the end of the string. /// @@ -185,7 +361,7 @@ impl str { /// ``` #[must_use] #[stable(feature = "is_char_boundary", since = "1.9.0")] - #[rustc_const_unstable(feature = "const_is_char_boundary", issue = "131516")] + #[rustc_const_stable(feature = "const_is_char_boundary", since = "1.86.0")] #[inline] pub const fn is_char_boundary(&self, index: usize) -> bool { // 0 is always ok. @@ -642,7 +818,7 @@ impl str { #[inline] #[must_use] #[stable(feature = "str_split_at", since = "1.4.0")] - #[rustc_const_unstable(feature = "const_str_split_at", issue = "131518")] + #[rustc_const_stable(feature = "const_str_split_at", since = "1.86.0")] pub const fn split_at(&self, mid: usize) -> (&str, &str) { match self.split_at_checked(mid) { None => slice_error_fail(self, 0, mid), @@ -683,7 +859,7 @@ impl str { #[inline] #[must_use] #[stable(feature = "str_split_at", since = "1.4.0")] - #[rustc_const_unstable(feature = "const_str_split_at", issue = "131518")] + #[rustc_const_stable(feature = "const_str_split_at", since = "1.86.0")] pub const fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str) { // is_char_boundary checks that the index is in [0, .len()] if self.is_char_boundary(mid) { @@ -723,7 +899,7 @@ impl str { #[inline] #[must_use] #[stable(feature = "split_at_checked", since = "1.80.0")] - #[rustc_const_unstable(feature = "const_str_split_at", issue = "131518")] + #[rustc_const_stable(feature = "const_str_split_at", since = "1.86.0")] pub const fn split_at_checked(&self, mid: usize) -> Option<(&str, &str)> { // is_char_boundary checks that the index is in [0, .len()] if self.is_char_boundary(mid) { @@ -764,7 +940,7 @@ impl str { #[inline] #[must_use] #[stable(feature = "split_at_checked", since = "1.80.0")] - #[rustc_const_unstable(feature = "const_str_split_at", issue = "131518")] + #[rustc_const_stable(feature = "const_str_split_at", since = "1.86.0")] pub const fn split_at_mut_checked(&mut self, mid: usize) -> Option<(&mut str, &mut str)> { // is_char_boundary checks that the index is in [0, .len()] if self.is_char_boundary(mid) { @@ -1108,7 +1284,8 @@ impl str { LinesAny(self.lines()) } - /// Returns an iterator of `u16` over the string encoded as UTF-16. + /// Returns an iterator of `u16` over the string encoded + /// as native endian UTF-16 (without byte-order mark). /// /// # Examples /// diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index fda26a672990a..73180bde54aa9 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -716,6 +716,12 @@ impl AtomicBool { /// AcqRel | AcqRel | Acquire /// SeqCst | SeqCst | SeqCst /// + /// `compare_and_swap` and `compare_exchange` also differ in their return type. You can use + /// `compare_exchange(...).unwrap_or_else(|x| x)` to recover the behavior of `compare_and_swap`, + /// but in most cases it is more idiomatic to check whether the return value is `Ok` or `Err` + /// rather than to infer success vs failure based on the value that was read. + /// + /// During migration, consider whether it makes sense to use `compare_exchange_weak` instead. /// `compare_exchange_weak` is allowed to fail spuriously even when the comparison succeeds, /// which allows the compiler to generate better assembly code when the compare and swap /// is used in a loop. @@ -1164,7 +1170,7 @@ impl AtomicBool { /// /// # Considerations /// - /// This method is not magic; it is not provided by the hardware. + /// This method is not magic; it is not provided by the hardware. /// It is implemented in terms of [`AtomicBool::compare_exchange_weak`], and suffers from the same drawbacks. /// In particular, this method will not circumvent the [ABA Problem]. /// @@ -1203,6 +1209,125 @@ impl AtomicBool { } Err(prev) } + + /// Fetches the value, and applies a function to it that returns an optional + /// new value. Returns a `Result` of `Ok(previous_value)` if the function + /// returned `Some(_)`, else `Err(previous_value)`. + /// + /// See also: [`update`](`AtomicBool::update`). + /// + /// Note: This may call the function multiple times if the value has been + /// changed from other threads in the meantime, as long as the function + /// returns `Some(_)`, but the function will have been applied only once to + /// the stored value. + /// + /// `try_update` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. The first describes the required ordering for + /// when the operation finally succeeds while the second describes the + /// required ordering for loads. These correspond to the success and failure + /// orderings of [`AtomicBool::compare_exchange`] respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part of this + /// operation [`Relaxed`], and using [`Release`] makes the final successful + /// load [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], + /// [`Acquire`] or [`Relaxed`]. + /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on `u8`. + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of [`AtomicBool::compare_exchange_weak`], and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + /// use std::sync::atomic::{AtomicBool, Ordering}; + /// + /// let x = AtomicBool::new(false); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(false)); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(!x)), Ok(false)); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(!x)), Ok(true)); + /// assert_eq!(x.load(Ordering::SeqCst), false); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[cfg(target_has_atomic = "8")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn try_update( + &self, + set_order: Ordering, + fetch_order: Ordering, + f: impl FnMut(bool) -> Option, + ) -> Result { + // FIXME(atomic_try_update): this is currently an unstable alias to `fetch_update`; + // when stabilizing, turn `fetch_update` into a deprecated alias to `try_update`. + self.fetch_update(set_order, fetch_order, f) + } + + /// Fetches the value, applies a function to it that it return a new value. + /// The new value is stored and the old value is returned. + /// + /// See also: [`try_update`](`AtomicBool::try_update`). + /// + /// Note: This may call the function multiple times if the value has been changed from other threads in + /// the meantime, but the function will have been applied only once to the stored value. + /// + /// `update` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. The first describes the required ordering for + /// when the operation finally succeeds while the second describes the + /// required ordering for loads. These correspond to the success and failure + /// orderings of [`AtomicBool::compare_exchange`] respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the final successful load + /// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`]. + /// + /// **Note:** This method is only available on platforms that support atomic operations on `u8`. + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of [`AtomicBool::compare_exchange_weak`], and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + /// + /// use std::sync::atomic::{AtomicBool, Ordering}; + /// + /// let x = AtomicBool::new(false); + /// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |x| !x), false); + /// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |x| !x), true); + /// assert_eq!(x.load(Ordering::SeqCst), false); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[cfg(target_has_atomic = "8")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn update( + &self, + set_order: Ordering, + fetch_order: Ordering, + mut f: impl FnMut(bool) -> bool, + ) -> bool { + let mut prev = self.load(fetch_order); + loop { + match self.compare_exchange_weak(prev, f(prev), set_order, fetch_order) { + Ok(x) => break x, + Err(next_prev) => prev = next_prev, + } + } + } } #[cfg(target_has_atomic_load_store = "ptr")] @@ -1532,6 +1657,12 @@ impl AtomicPtr { /// AcqRel | AcqRel | Acquire /// SeqCst | SeqCst | SeqCst /// + /// `compare_and_swap` and `compare_exchange` also differ in their return type. You can use + /// `compare_exchange(...).unwrap_or_else(|x| x)` to recover the behavior of `compare_and_swap`, + /// but in most cases it is more idiomatic to check whether the return value is `Ok` or `Err` + /// rather than to infer success vs failure based on the value that was read. + /// + /// During migration, consider whether it makes sense to use `compare_exchange_weak` instead. /// `compare_exchange_weak` is allowed to fail spuriously even when the comparison succeeds, /// which allows the compiler to generate better assembly code when the compare and swap /// is used in a loop. @@ -1684,7 +1815,7 @@ impl AtomicPtr { /// /// # Considerations /// - /// This method is not magic; it is not provided by the hardware. + /// This method is not magic; it is not provided by the hardware. /// It is implemented in terms of [`AtomicPtr::compare_exchange_weak`], and suffers from the same drawbacks. /// In particular, this method will not circumvent the [ABA Problem]. /// @@ -1732,6 +1863,137 @@ impl AtomicPtr { } Err(prev) } + /// Fetches the value, and applies a function to it that returns an optional + /// new value. Returns a `Result` of `Ok(previous_value)` if the function + /// returned `Some(_)`, else `Err(previous_value)`. + /// + /// See also: [`update`](`AtomicPtr::update`). + /// + /// Note: This may call the function multiple times if the value has been + /// changed from other threads in the meantime, as long as the function + /// returns `Some(_)`, but the function will have been applied only once to + /// the stored value. + /// + /// `try_update` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. The first describes the required ordering for + /// when the operation finally succeeds while the second describes the + /// required ordering for loads. These correspond to the success and failure + /// orderings of [`AtomicPtr::compare_exchange`] respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part of this + /// operation [`Relaxed`], and using [`Release`] makes the final successful + /// load [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], + /// [`Acquire`] or [`Relaxed`]. + /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on pointers. + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of [`AtomicPtr::compare_exchange_weak`], and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + /// use std::sync::atomic::{AtomicPtr, Ordering}; + /// + /// let ptr: *mut _ = &mut 5; + /// let some_ptr = AtomicPtr::new(ptr); + /// + /// let new: *mut _ = &mut 10; + /// assert_eq!(some_ptr.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(ptr)); + /// let result = some_ptr.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| { + /// if x == ptr { + /// Some(new) + /// } else { + /// None + /// } + /// }); + /// assert_eq!(result, Ok(ptr)); + /// assert_eq!(some_ptr.load(Ordering::SeqCst), new); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[cfg(target_has_atomic = "ptr")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn try_update( + &self, + set_order: Ordering, + fetch_order: Ordering, + f: impl FnMut(*mut T) -> Option<*mut T>, + ) -> Result<*mut T, *mut T> { + // FIXME(atomic_try_update): this is currently an unstable alias to `fetch_update`; + // when stabilizing, turn `fetch_update` into a deprecated alias to `try_update`. + self.fetch_update(set_order, fetch_order, f) + } + + /// Fetches the value, applies a function to it that it return a new value. + /// The new value is stored and the old value is returned. + /// + /// See also: [`try_update`](`AtomicPtr::try_update`). + /// + /// Note: This may call the function multiple times if the value has been changed from other threads in + /// the meantime, but the function will have been applied only once to the stored value. + /// + /// `update` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. The first describes the required ordering for + /// when the operation finally succeeds while the second describes the + /// required ordering for loads. These correspond to the success and failure + /// orderings of [`AtomicPtr::compare_exchange`] respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the final successful load + /// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`]. + /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on pointers. + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of [`AtomicPtr::compare_exchange_weak`], and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + /// + /// use std::sync::atomic::{AtomicPtr, Ordering}; + /// + /// let ptr: *mut _ = &mut 5; + /// let some_ptr = AtomicPtr::new(ptr); + /// + /// let new: *mut _ = &mut 10; + /// let result = some_ptr.update(Ordering::SeqCst, Ordering::SeqCst, |_| new); + /// assert_eq!(result, ptr); + /// assert_eq!(some_ptr.load(Ordering::SeqCst), new); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[cfg(target_has_atomic = "8")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn update( + &self, + set_order: Ordering, + fetch_order: Ordering, + mut f: impl FnMut(*mut T) -> *mut T, + ) -> *mut T { + let mut prev = self.load(fetch_order); + loop { + match self.compare_exchange_weak(prev, f(prev), set_order, fetch_order) { + Ok(x) => break x, + Err(next_prev) => prev = next_prev, + } + } + } /// Offsets the pointer's address by adding `val` (in units of `T`), /// returning the previous pointer. @@ -2297,7 +2559,7 @@ macro_rules! atomic_int { $int_type, no = [ "**Note:** This function is only available on targets where `", - stringify!($int_type), "` has an alignment of ", $align, " bytes." + stringify!($atomic_type), "` has the same alignment as `", stringify!($int_type), "`." ], }] /// @@ -2521,6 +2783,12 @@ macro_rules! atomic_int { /// AcqRel | AcqRel | Acquire /// SeqCst | SeqCst | SeqCst /// + /// `compare_and_swap` and `compare_exchange` also differ in their return type. You can use + /// `compare_exchange(...).unwrap_or_else(|x| x)` to recover the behavior of `compare_and_swap`, + /// but in most cases it is more idiomatic to check whether the return value is `Ok` or `Err` + /// rather than to infer success vs failure based on the value that was read. + /// + /// During migration, consider whether it makes sense to use `compare_exchange_weak` instead. /// `compare_exchange_weak` is allowed to fail spuriously even when the comparison succeeds, /// which allows the compiler to generate better assembly code when the compare and swap /// is used in a loop. @@ -2875,7 +3143,7 @@ macro_rules! atomic_int { /// /// # Considerations /// - /// This method is not magic; it is not provided by the hardware. + /// This method is not magic; it is not provided by the hardware. /// It is implemented in terms of #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange_weak`],")] /// and suffers from the same drawbacks. @@ -2913,6 +3181,127 @@ macro_rules! atomic_int { Err(prev) } + /// Fetches the value, and applies a function to it that returns an optional + /// new value. Returns a `Result` of `Ok(previous_value)` if the function returned `Some(_)`, else + /// `Err(previous_value)`. + /// + #[doc = concat!("See also: [`update`](`", stringify!($atomic_type), "::update`).")] + /// + /// Note: This may call the function multiple times if the value has been changed from other threads in + /// the meantime, as long as the function returns `Some(_)`, but the function will have been applied + /// only once to the stored value. + /// + /// `try_update` takes two [`Ordering`] arguments to describe the memory ordering of this operation. + /// The first describes the required ordering for when the operation finally succeeds while the second + /// describes the required ordering for loads. These correspond to the success and failure orderings of + #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange`]")] + /// respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the final successful load + /// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`].")] + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of + #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange_weak`],")] + /// and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let x = ", stringify!($atomic_type), "::new(7);")] + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(7)); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(7)); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(8)); + /// assert_eq!(x.load(Ordering::SeqCst), 9); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[$cfg_cas] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn try_update( + &self, + set_order: Ordering, + fetch_order: Ordering, + f: impl FnMut($int_type) -> Option<$int_type>, + ) -> Result<$int_type, $int_type> { + // FIXME(atomic_try_update): this is currently an unstable alias to `fetch_update`; + // when stabilizing, turn `fetch_update` into a deprecated alias to `try_update`. + self.fetch_update(set_order, fetch_order, f) + } + + /// Fetches the value, applies a function to it that it return a new value. + /// The new value is stored and the old value is returned. + /// + #[doc = concat!("See also: [`try_update`](`", stringify!($atomic_type), "::try_update`).")] + /// + /// Note: This may call the function multiple times if the value has been changed from other threads in + /// the meantime, but the function will have been applied only once to the stored value. + /// + /// `update` takes two [`Ordering`] arguments to describe the memory ordering of this operation. + /// The first describes the required ordering for when the operation finally succeeds while the second + /// describes the required ordering for loads. These correspond to the success and failure orderings of + #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange`]")] + /// respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the final successful load + /// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`].")] + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of + #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange_weak`],")] + /// and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let x = ", stringify!($atomic_type), "::new(7);")] + /// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |x| x + 1), 7); + /// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |x| x + 1), 8); + /// assert_eq!(x.load(Ordering::SeqCst), 9); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[$cfg_cas] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn update( + &self, + set_order: Ordering, + fetch_order: Ordering, + mut f: impl FnMut($int_type) -> $int_type, + ) -> $int_type { + let mut prev = self.load(fetch_order); + loop { + match self.compare_exchange_weak(prev, f(prev), set_order, fetch_order) { + Ok(x) => break x, + Err(next_prev) => prev = next_prev, + } + } + } + /// Maximum with the current value. /// /// Finds the maximum of the current value and the argument `val`, and @@ -3727,33 +4116,33 @@ pub fn fence(order: Ordering) { /// /// # Examples /// -/// Without `compiler_fence`, the `assert_eq!` in following code -/// is *not* guaranteed to succeed, despite everything happening in a single thread. -/// To see why, remember that the compiler is free to swap the stores to -/// `IMPORTANT_VARIABLE` and `IS_READY` since they are both -/// `Ordering::Relaxed`. If it does, and the signal handler is invoked right -/// after `IS_READY` is updated, then the signal handler will see -/// `IS_READY=1`, but `IMPORTANT_VARIABLE=0`. -/// Using a `compiler_fence` remedies this situation. +/// Without the two `compiler_fence` calls, the read of `IMPORTANT_VARIABLE` in `signal_handler` +/// is *undefined behavior* due to a data race, despite everything happening in a single thread. +/// This is because the signal handler is considered to run concurrently with its associated +/// thread, and explicit synchronization is required to pass data between a thread and its +/// signal handler. The code below uses two `compiler_fence` calls to establish the usual +/// release-acquire synchronization pattern (see [`fence`] for an image). /// /// ``` -/// use std::sync::atomic::{AtomicBool, AtomicUsize}; +/// use std::sync::atomic::AtomicBool; /// use std::sync::atomic::Ordering; /// use std::sync::atomic::compiler_fence; /// -/// static IMPORTANT_VARIABLE: AtomicUsize = AtomicUsize::new(0); +/// static mut IMPORTANT_VARIABLE: usize = 0; /// static IS_READY: AtomicBool = AtomicBool::new(false); /// /// fn main() { -/// IMPORTANT_VARIABLE.store(42, Ordering::Relaxed); -/// // prevent earlier writes from being moved beyond this point +/// unsafe { IMPORTANT_VARIABLE = 42 }; +/// // Marks earlier writes as being released with future relaxed stores. /// compiler_fence(Ordering::Release); /// IS_READY.store(true, Ordering::Relaxed); /// } /// /// fn signal_handler() { /// if IS_READY.load(Ordering::Relaxed) { -/// assert_eq!(IMPORTANT_VARIABLE.load(Ordering::Relaxed), 42); +/// // Acquires writes that were released with relaxed stores that we read from. +/// compiler_fence(Ordering::Acquire); +/// assert_eq!(unsafe { IMPORTANT_VARIABLE }, 42); /// } /// } /// ``` diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index ba429005fab3d..4c51ca0a5e437 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -60,7 +60,7 @@ impl RawWaker { RawWaker { data, vtable } } - #[stable(feature = "noop_waker", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "noop_waker", since = "1.85.0")] const NOOP: RawWaker = { const VTABLE: RawWakerVTable = RawWakerVTable::new( // Cloning just returns a new no-op raw waker @@ -564,8 +564,8 @@ impl Waker { /// ``` #[inline] #[must_use] - #[stable(feature = "noop_waker", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "noop_waker", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "noop_waker", since = "1.85.0")] + #[rustc_const_stable(feature = "noop_waker", since = "1.85.0")] pub const fn noop() -> &'static Waker { const WAKER: &Waker = &Waker { waker: RawWaker::NOOP }; WAKER diff --git a/library/core/src/time.rs b/library/core/src/time.rs index 2d93148bac09f..22bd46c567eaa 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -21,6 +21,7 @@ use crate::fmt; use crate::iter::Sum; +use crate::num::niche_types::Nanoseconds; use crate::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; const NANOS_PER_SEC: u32 = 1_000_000_000; @@ -37,24 +38,6 @@ const HOURS_PER_DAY: u64 = 24; #[unstable(feature = "duration_units", issue = "120301")] const DAYS_PER_WEEK: u64 = 7; -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -#[rustc_layout_scalar_valid_range_end(999_999_999)] -struct Nanoseconds(u32); - -impl Nanoseconds { - // SAFETY: 0 is within the valid range - const ZERO: Self = unsafe { Nanoseconds(0) }; -} - -impl Default for Nanoseconds { - #[inline] - fn default() -> Self { - Self::ZERO - } -} - /// A `Duration` type to represent a span of time, typically used for system /// timeouts. /// @@ -211,14 +194,14 @@ impl Duration { pub const fn new(secs: u64, nanos: u32) -> Duration { if nanos < NANOS_PER_SEC { // SAFETY: nanos < NANOS_PER_SEC, therefore nanos is within the valid range - Duration { secs, nanos: unsafe { Nanoseconds(nanos) } } + Duration { secs, nanos: unsafe { Nanoseconds::new_unchecked(nanos) } } } else { let secs = secs .checked_add((nanos / NANOS_PER_SEC) as u64) .expect("overflow in Duration::new"); let nanos = nanos % NANOS_PER_SEC; // SAFETY: nanos % NANOS_PER_SEC < NANOS_PER_SEC, therefore nanos is within the valid range - Duration { secs, nanos: unsafe { Nanoseconds(nanos) } } + Duration { secs, nanos: unsafe { Nanoseconds::new_unchecked(nanos) } } } } @@ -263,7 +246,7 @@ impl Duration { let subsec_millis = (millis % MILLIS_PER_SEC) as u32; // SAFETY: (x % 1_000) * 1_000_000 < 1_000_000_000 // => x % 1_000 < 1_000 - let subsec_nanos = unsafe { Nanoseconds(subsec_millis * NANOS_PER_MILLI) }; + let subsec_nanos = unsafe { Nanoseconds::new_unchecked(subsec_millis * NANOS_PER_MILLI) }; Duration { secs, nanos: subsec_nanos } } @@ -289,7 +272,7 @@ impl Duration { let subsec_micros = (micros % MICROS_PER_SEC) as u32; // SAFETY: (x % 1_000_000) * 1_000 < 1_000_000_000 // => x % 1_000_000 < 1_000_000 - let subsec_nanos = unsafe { Nanoseconds(subsec_micros * NANOS_PER_MICRO) }; + let subsec_nanos = unsafe { Nanoseconds::new_unchecked(subsec_micros * NANOS_PER_MICRO) }; Duration { secs, nanos: subsec_nanos } } @@ -320,7 +303,7 @@ impl Duration { let secs = nanos / NANOS_PER_SEC; let subsec_nanos = (nanos % NANOS_PER_SEC) as u32; // SAFETY: x % 1_000_000_000 < 1_000_000_000 - let subsec_nanos = unsafe { Nanoseconds(subsec_nanos) }; + let subsec_nanos = unsafe { Nanoseconds::new_unchecked(subsec_nanos) }; Duration { secs, nanos: subsec_nanos } } @@ -458,7 +441,7 @@ impl Duration { #[rustc_const_stable(feature = "duration_zero", since = "1.53.0")] #[inline] pub const fn is_zero(&self) -> bool { - self.secs == 0 && self.nanos.0 == 0 + self.secs == 0 && self.nanos.as_inner() == 0 } /// Returns the number of _whole_ seconds contained by this `Duration`. @@ -509,7 +492,7 @@ impl Duration { #[must_use] #[inline] pub const fn subsec_millis(&self) -> u32 { - self.nanos.0 / NANOS_PER_MILLI + self.nanos.as_inner() / NANOS_PER_MILLI } /// Returns the fractional part of this `Duration`, in whole microseconds. @@ -532,7 +515,7 @@ impl Duration { #[must_use] #[inline] pub const fn subsec_micros(&self) -> u32 { - self.nanos.0 / NANOS_PER_MICRO + self.nanos.as_inner() / NANOS_PER_MICRO } /// Returns the fractional part of this `Duration`, in nanoseconds. @@ -555,7 +538,7 @@ impl Duration { #[must_use] #[inline] pub const fn subsec_nanos(&self) -> u32 { - self.nanos.0 + self.nanos.as_inner() } /// Returns the total number of whole milliseconds contained by this `Duration`. @@ -573,7 +556,8 @@ impl Duration { #[must_use] #[inline] pub const fn as_millis(&self) -> u128 { - self.secs as u128 * MILLIS_PER_SEC as u128 + (self.nanos.0 / NANOS_PER_MILLI) as u128 + self.secs as u128 * MILLIS_PER_SEC as u128 + + (self.nanos.as_inner() / NANOS_PER_MILLI) as u128 } /// Returns the total number of whole microseconds contained by this `Duration`. @@ -591,7 +575,8 @@ impl Duration { #[must_use] #[inline] pub const fn as_micros(&self) -> u128 { - self.secs as u128 * MICROS_PER_SEC as u128 + (self.nanos.0 / NANOS_PER_MICRO) as u128 + self.secs as u128 * MICROS_PER_SEC as u128 + + (self.nanos.as_inner() / NANOS_PER_MICRO) as u128 } /// Returns the total number of nanoseconds contained by this `Duration`. @@ -609,7 +594,7 @@ impl Duration { #[must_use] #[inline] pub const fn as_nanos(&self) -> u128 { - self.secs as u128 * NANOS_PER_SEC as u128 + self.nanos.0 as u128 + self.secs as u128 * NANOS_PER_SEC as u128 + self.nanos.as_inner() as u128 } /// Computes the absolute difference between `self` and `other`. @@ -649,7 +634,7 @@ impl Duration { #[rustc_const_stable(feature = "duration_consts_2", since = "1.58.0")] pub const fn checked_add(self, rhs: Duration) -> Option { if let Some(mut secs) = self.secs.checked_add(rhs.secs) { - let mut nanos = self.nanos.0 + rhs.nanos.0; + let mut nanos = self.nanos.as_inner() + rhs.nanos.as_inner(); if nanos >= NANOS_PER_SEC { nanos -= NANOS_PER_SEC; if let Some(new_secs) = secs.checked_add(1) { @@ -707,11 +692,11 @@ impl Duration { #[rustc_const_stable(feature = "duration_consts_2", since = "1.58.0")] pub const fn checked_sub(self, rhs: Duration) -> Option { if let Some(mut secs) = self.secs.checked_sub(rhs.secs) { - let nanos = if self.nanos.0 >= rhs.nanos.0 { - self.nanos.0 - rhs.nanos.0 + let nanos = if self.nanos.as_inner() >= rhs.nanos.as_inner() { + self.nanos.as_inner() - rhs.nanos.as_inner() } else if let Some(sub_secs) = secs.checked_sub(1) { secs = sub_secs; - self.nanos.0 + NANOS_PER_SEC - rhs.nanos.0 + self.nanos.as_inner() + NANOS_PER_SEC - rhs.nanos.as_inner() } else { return None; }; @@ -763,7 +748,7 @@ impl Duration { #[rustc_const_stable(feature = "duration_consts_2", since = "1.58.0")] pub const fn checked_mul(self, rhs: u32) -> Option { // Multiply nanoseconds as u64, because it cannot overflow that way. - let total_nanos = self.nanos.0 as u64 * rhs as u64; + let total_nanos = self.nanos.as_inner() as u64 * rhs as u64; let extra_secs = total_nanos / (NANOS_PER_SEC as u64); let nanos = (total_nanos % (NANOS_PER_SEC as u64)) as u32; // FIXME(const-hack): use `and_then` once that is possible. @@ -820,7 +805,8 @@ impl Duration { pub const fn checked_div(self, rhs: u32) -> Option { if rhs != 0 { let (secs, extra_secs) = (self.secs / (rhs as u64), self.secs % (rhs as u64)); - let (mut nanos, extra_nanos) = (self.nanos.0 / rhs, self.nanos.0 % rhs); + let (mut nanos, extra_nanos) = + (self.nanos.as_inner() / rhs, self.nanos.as_inner() % rhs); nanos += ((extra_secs * (NANOS_PER_SEC as u64) + extra_nanos as u64) / (rhs as u64)) as u32; debug_assert!(nanos < NANOS_PER_SEC); @@ -846,7 +832,7 @@ impl Duration { #[inline] #[rustc_const_stable(feature = "duration_consts_float", since = "1.83.0")] pub const fn as_secs_f64(&self) -> f64 { - (self.secs as f64) + (self.nanos.0 as f64) / (NANOS_PER_SEC as f64) + (self.secs as f64) + (self.nanos.as_inner() as f64) / (NANOS_PER_SEC as f64) } /// Returns the number of seconds contained by this `Duration` as `f32`. @@ -865,7 +851,7 @@ impl Duration { #[inline] #[rustc_const_stable(feature = "duration_consts_float", since = "1.83.0")] pub const fn as_secs_f32(&self) -> f32 { - (self.secs as f32) + (self.nanos.0 as f32) / (NANOS_PER_SEC as f32) + (self.secs as f32) + (self.nanos.as_inner() as f32) / (NANOS_PER_SEC as f32) } /// Returns the number of milliseconds contained by this `Duration` as `f64`. @@ -885,7 +871,7 @@ impl Duration { #[inline] pub const fn as_millis_f64(&self) -> f64 { (self.secs as f64) * (MILLIS_PER_SEC as f64) - + (self.nanos.0 as f64) / (NANOS_PER_MILLI as f64) + + (self.nanos.as_inner() as f64) / (NANOS_PER_MILLI as f64) } /// Returns the number of milliseconds contained by this `Duration` as `f32`. @@ -905,7 +891,7 @@ impl Duration { #[inline] pub const fn as_millis_f32(&self) -> f32 { (self.secs as f32) * (MILLIS_PER_SEC as f32) - + (self.nanos.0 as f32) / (NANOS_PER_MILLI as f32) + + (self.nanos.as_inner() as f32) / (NANOS_PER_MILLI as f32) } /// Creates a new `Duration` from the specified number of seconds represented @@ -1084,8 +1070,9 @@ impl Duration { #[inline] #[rustc_const_stable(feature = "duration_consts_float", since = "1.83.0")] pub const fn div_duration_f64(self, rhs: Duration) -> f64 { - let self_nanos = (self.secs as f64) * (NANOS_PER_SEC as f64) + (self.nanos.0 as f64); - let rhs_nanos = (rhs.secs as f64) * (NANOS_PER_SEC as f64) + (rhs.nanos.0 as f64); + let self_nanos = + (self.secs as f64) * (NANOS_PER_SEC as f64) + (self.nanos.as_inner() as f64); + let rhs_nanos = (rhs.secs as f64) * (NANOS_PER_SEC as f64) + (rhs.nanos.as_inner() as f64); self_nanos / rhs_nanos } @@ -1105,8 +1092,9 @@ impl Duration { #[inline] #[rustc_const_stable(feature = "duration_consts_float", since = "1.83.0")] pub const fn div_duration_f32(self, rhs: Duration) -> f32 { - let self_nanos = (self.secs as f32) * (NANOS_PER_SEC as f32) + (self.nanos.0 as f32); - let rhs_nanos = (rhs.secs as f32) * (NANOS_PER_SEC as f32) + (rhs.nanos.0 as f32); + let self_nanos = + (self.secs as f32) * (NANOS_PER_SEC as f32) + (self.nanos.as_inner() as f32); + let rhs_nanos = (rhs.secs as f32) * (NANOS_PER_SEC as f32) + (rhs.nanos.as_inner() as f32); self_nanos / rhs_nanos } } @@ -1201,13 +1189,13 @@ macro_rules! sum_durations { for entry in $iter { total_secs = total_secs.checked_add(entry.secs).expect("overflow in iter::sum over durations"); - total_nanos = match total_nanos.checked_add(entry.nanos.0 as u64) { + total_nanos = match total_nanos.checked_add(entry.nanos.as_inner() as u64) { Some(n) => n, None => { total_secs = total_secs .checked_add(total_nanos / NANOS_PER_SEC as u64) .expect("overflow in iter::sum over durations"); - (total_nanos % NANOS_PER_SEC as u64) + entry.nanos.0 as u64 + (total_nanos % NANOS_PER_SEC as u64) + entry.nanos.as_inner() as u64 } }; } @@ -1399,27 +1387,27 @@ impl fmt::Debug for Duration { let prefix = if f.sign_plus() { "+" } else { "" }; if self.secs > 0 { - fmt_decimal(f, self.secs, self.nanos.0, NANOS_PER_SEC / 10, prefix, "s") - } else if self.nanos.0 >= NANOS_PER_MILLI { + fmt_decimal(f, self.secs, self.nanos.as_inner(), NANOS_PER_SEC / 10, prefix, "s") + } else if self.nanos.as_inner() >= NANOS_PER_MILLI { fmt_decimal( f, - (self.nanos.0 / NANOS_PER_MILLI) as u64, - self.nanos.0 % NANOS_PER_MILLI, + (self.nanos.as_inner() / NANOS_PER_MILLI) as u64, + self.nanos.as_inner() % NANOS_PER_MILLI, NANOS_PER_MILLI / 10, prefix, "ms", ) - } else if self.nanos.0 >= NANOS_PER_MICRO { + } else if self.nanos.as_inner() >= NANOS_PER_MICRO { fmt_decimal( f, - (self.nanos.0 / NANOS_PER_MICRO) as u64, - self.nanos.0 % NANOS_PER_MICRO, + (self.nanos.as_inner() / NANOS_PER_MICRO) as u64, + self.nanos.as_inner() % NANOS_PER_MICRO, NANOS_PER_MICRO / 10, prefix, "µs", ) } else { - fmt_decimal(f, self.nanos.0 as u64, 0, 1, prefix, "ns") + fmt_decimal(f, self.nanos.as_inner() as u64, 0, 1, prefix, "ns") } } } diff --git a/library/core/src/unicode/mod.rs b/library/core/src/unicode/mod.rs index 6066aa9921607..49dbdeb1a6d1c 100644 --- a/library/core/src/unicode/mod.rs +++ b/library/core/src/unicode/mod.rs @@ -17,6 +17,8 @@ pub(crate) use unicode_data::uppercase::lookup as Uppercase; pub(crate) use unicode_data::white_space::lookup as White_Space; pub(crate) mod printable; + +#[allow(unreachable_pub)] mod unicode_data; /// The version of [Unicode](https://www.unicode.org/) that the Unicode parts of diff --git a/library/core/tests/ascii_char.rs b/library/core/tests/ascii_char.rs deleted file mode 100644 index 75b5fd4b9e61d..0000000000000 --- a/library/core/tests/ascii_char.rs +++ /dev/null @@ -1,28 +0,0 @@ -use core::ascii::Char; -use core::fmt::Write; - -/// Tests Display implementation for ascii::Char. -#[test] -fn test_display() { - let want = (0..128u8).map(|b| b as char).collect::(); - let mut got = String::with_capacity(128); - for byte in 0..128 { - write!(&mut got, "{}", Char::from_u8(byte).unwrap()).unwrap(); - } - assert_eq!(want, got); -} - -/// Tests Debug implementation for ascii::Char. -#[test] -fn test_debug_control() { - for byte in 0..128u8 { - let mut want = format!("{:?}", byte as char); - // `char` uses `'\u{#}'` representation where ascii::char uses `'\x##'`. - // Transform former into the latter. - if let Some(rest) = want.strip_prefix("'\\u{") { - want = format!("'\\x{:0>2}'", rest.strip_suffix("}'").unwrap()); - } - let chr = core::ascii::Char::from_u8(byte).unwrap(); - assert_eq!(want, format!("{chr:?}"), "byte: {byte}"); - } -} diff --git a/library/core/tests/fmt/mod.rs b/library/core/tests/fmt/mod.rs deleted file mode 100644 index 2c93a9bc80db9..0000000000000 --- a/library/core/tests/fmt/mod.rs +++ /dev/null @@ -1,76 +0,0 @@ -mod builders; -mod float; -mod num; - -#[test] -fn test_format_flags() { - // No residual flags left by pointer formatting - let p = "".as_ptr(); - assert_eq!(format!("{:p} {:x}", p, 16), format!("{p:p} 10")); - - assert_eq!(format!("{: >3}", 'a'), " a"); -} - -#[test] -fn test_pointer_formats_data_pointer() { - let b: &[u8] = b""; - let s: &str = ""; - assert_eq!(format!("{s:p}"), format!("{:p}", s.as_ptr())); - assert_eq!(format!("{b:p}"), format!("{:p}", b.as_ptr())); -} - -#[test] -fn test_estimated_capacity() { - assert_eq!(format_args!("").estimated_capacity(), 0); - assert_eq!(format_args!("{}", { "" }).estimated_capacity(), 0); - assert_eq!(format_args!("Hello").estimated_capacity(), 5); - assert_eq!(format_args!("Hello, {}!", { "" }).estimated_capacity(), 16); - assert_eq!(format_args!("{}, hello!", { "World" }).estimated_capacity(), 0); - assert_eq!(format_args!("{}. 16-bytes piece", { "World" }).estimated_capacity(), 32); -} - -#[test] -fn pad_integral_resets() { - struct Bar; - - impl core::fmt::Display for Bar { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - "1".fmt(f)?; - f.pad_integral(true, "", "5")?; - "1".fmt(f) - } - } - - assert_eq!(format!("{Bar:<03}"), "1 0051 "); -} - -#[test] -fn test_maybe_uninit_short() { - // Ensure that the trimmed `MaybeUninit` Debug implementation doesn't break - let x = core::mem::MaybeUninit::new(0u32); - assert_eq!(format!("{x:?}"), "MaybeUninit"); -} - -#[test] -fn formatting_options_flags() { - use core::fmt::*; - for sign in [None, Some(Sign::Plus), Some(Sign::Minus)] { - for alternate in [true, false] { - for sign_aware_zero_pad in [true, false] { - for debug_as_hex in [None, Some(DebugAsHex::Lower), Some(DebugAsHex::Upper)] { - let mut formatting_options = FormattingOptions::new(); - formatting_options - .sign(sign) - .sign_aware_zero_pad(sign_aware_zero_pad) - .alternate(alternate) - .debug_as_hex(debug_as_hex); - - assert_eq!(formatting_options.get_sign(), sign); - assert_eq!(formatting_options.get_alternate(), alternate); - assert_eq!(formatting_options.get_sign_aware_zero_pad(), sign_aware_zero_pad); - assert_eq!(formatting_options.get_debug_as_hex(), debug_as_hex); - } - } - } - } -} diff --git a/library/core/tests/hash/mod.rs b/library/core/tests/hash/mod.rs deleted file mode 100644 index 9f14995f73fe2..0000000000000 --- a/library/core/tests/hash/mod.rs +++ /dev/null @@ -1,170 +0,0 @@ -mod sip; - -use std::hash::{BuildHasher, Hash, Hasher}; -use std::ptr; -use std::rc::Rc; - -#[derive(Default)] -struct MyHasher { - hash: u64, -} - -impl Hasher for MyHasher { - fn write(&mut self, buf: &[u8]) { - for byte in buf { - self.hash += *byte as u64; - } - } - fn write_str(&mut self, s: &str) { - self.write(s.as_bytes()); - self.write_u8(0xFF); - } - fn finish(&self) -> u64 { - self.hash - } -} - -#[test] -fn test_writer_hasher() { - // FIXME(#110395) - /* const */ - fn hash(t: &T) -> u64 { - let mut s = MyHasher { hash: 0 }; - t.hash(&mut s); - s.finish() - } - - /* const { - // FIXME(fee1-dead): assert_eq - assert!(hash(&()) == 0); - assert!(hash(&5_u8) == 5); - assert!(hash(&5_u16) == 5); - assert!(hash(&5_u32) == 5); - - assert!(hash(&'a') == 97); - - let s: &str = "a"; - assert!(hash(&s) == 97 + 0xFF); - }; */ - - assert_eq!(hash(&()), 0); - - assert_eq!(hash(&5_u8), 5); - assert_eq!(hash(&5_u16), 5); - assert_eq!(hash(&5_u32), 5); - assert_eq!(hash(&5_u64), 5); - assert_eq!(hash(&5_usize), 5); - - assert_eq!(hash(&5_i8), 5); - assert_eq!(hash(&5_i16), 5); - assert_eq!(hash(&5_i32), 5); - assert_eq!(hash(&5_i64), 5); - assert_eq!(hash(&5_isize), 5); - - assert_eq!(hash(&false), 0); - assert_eq!(hash(&true), 1); - - assert_eq!(hash(&'a'), 97); - - let s: &str = "a"; - assert_eq!(hash(&s), 97 + 0xFF); - let s: Box = String::from("a").into_boxed_str(); - assert_eq!(hash(&s), 97 + 0xFF); - let s: Rc<&str> = Rc::new("a"); - assert_eq!(hash(&s), 97 + 0xFF); - let cs: &[u8] = &[1, 2, 3]; - assert_eq!(hash(&cs), 9); - let cs: Box<[u8]> = Box::new([1, 2, 3]); - assert_eq!(hash(&cs), 9); - let cs: Rc<[u8]> = Rc::new([1, 2, 3]); - assert_eq!(hash(&cs), 9); - - let ptr = ptr::without_provenance::(5_usize); - assert_eq!(hash(&ptr), 5); - - let ptr = ptr::without_provenance_mut::(5_usize); - assert_eq!(hash(&ptr), 5); - - if cfg!(miri) { - // Miri cannot hash pointers - return; - } - - let cs: &mut [u8] = &mut [1, 2, 3]; - let ptr = cs.as_ptr(); - let slice_ptr = cs as *const [u8]; - assert_eq!(hash(&slice_ptr), hash(&ptr) + cs.len() as u64); - - let slice_ptr = cs as *mut [u8]; - assert_eq!(hash(&slice_ptr), hash(&ptr) + cs.len() as u64); -} - -struct Custom { - hash: u64, -} - -#[derive(Default)] -struct CustomHasher { - output: u64, -} - -impl Hasher for CustomHasher { - fn finish(&self) -> u64 { - self.output - } - fn write(&mut self, _: &[u8]) { - panic!() - } - fn write_u64(&mut self, data: u64) { - self.output = data; - } -} - -impl Hash for Custom { - fn hash(&self, state: &mut H) { - state.write_u64(self.hash); - } -} - -#[test] -fn test_custom_state() { - // FIXME(#110395) - /* const */ - fn hash(t: &T) -> u64 { - let mut c = CustomHasher { output: 0 }; - t.hash(&mut c); - c.finish() - } - - assert_eq!(hash(&Custom { hash: 5 }), 5); - - // const { assert!(hash(&Custom { hash: 6 }) == 6) }; -} - -// FIXME: Instantiated functions with i128 in the signature is not supported in Emscripten. -// See https://github.com/kripken/emscripten-fastcomp/issues/169 -#[cfg(not(target_os = "emscripten"))] -#[test] -fn test_indirect_hasher() { - let mut hasher = MyHasher { hash: 0 }; - { - let mut indirect_hasher: &mut dyn Hasher = &mut hasher; - 5u32.hash(&mut indirect_hasher); - } - assert_eq!(hasher.hash, 5); -} - -#[test] -fn test_build_hasher_dyn_compatible() { - use std::hash::{DefaultHasher, RandomState}; - - let _: &dyn BuildHasher = &RandomState::new(); -} - -// just tests by whether or not this compiles -fn _build_hasher_default_impl_all_auto_traits() { - use std::panic::{RefUnwindSafe, UnwindSafe}; - fn all_auto_traits() {} - - all_auto_traits::>(); -} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs deleted file mode 100644 index 18feee9fb2545..0000000000000 --- a/library/core/tests/lib.rs +++ /dev/null @@ -1,191 +0,0 @@ -// tidy-alphabetical-start -#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] -#![cfg_attr(test, feature(cfg_match))] -#![feature(alloc_layout_extra)] -#![feature(array_chunks)] -#![feature(array_ptr_get)] -#![feature(array_try_from_fn)] -#![feature(array_windows)] -#![feature(ascii_char)] -#![feature(ascii_char_variants)] -#![feature(async_iter_from_iter)] -#![feature(async_iterator)] -#![feature(bigint_helper_methods)] -#![feature(cell_update)] -#![feature(clone_to_uninit)] -#![feature(const_black_box)] -#![feature(const_eval_select)] -#![feature(const_swap_nonoverlapping)] -#![feature(const_trait_impl)] -#![feature(core_intrinsics)] -#![feature(core_intrinsics_fallbacks)] -#![feature(core_io_borrowed_buf)] -#![feature(core_private_bignum)] -#![feature(core_private_diy_float)] -#![feature(dec2flt)] -#![feature(duration_constants)] -#![feature(duration_constructors)] -#![feature(error_generic_member_access)] -#![feature(exact_size_is_empty)] -#![feature(extern_types)] -#![feature(float_minimum_maximum)] -#![feature(flt2dec)] -#![feature(fmt_internals)] -#![feature(formatting_options)] -#![feature(freeze)] -#![feature(future_join)] -#![feature(generic_assert_internals)] -#![feature(get_many_mut)] -#![feature(hasher_prefixfree_extras)] -#![feature(hashmap_internals)] -#![feature(inline_const_pat)] -#![feature(int_roundings)] -#![feature(ip)] -#![feature(ip_from)] -#![feature(is_ascii_octdigit)] -#![feature(iter_advance_by)] -#![feature(iter_array_chunks)] -#![feature(iter_chain)] -#![feature(iter_collect_into)] -#![feature(iter_intersperse)] -#![feature(iter_is_partitioned)] -#![feature(iter_map_windows)] -#![feature(iter_next_chunk)] -#![feature(iter_order_by)] -#![feature(iter_partition_in_place)] -#![feature(iterator_try_collect)] -#![feature(iterator_try_reduce)] -#![feature(layout_for_ptr)] -#![feature(lazy_get)] -#![feature(maybe_uninit_fill)] -#![feature(maybe_uninit_uninit_array_transpose)] -#![feature(maybe_uninit_write_slice)] -#![feature(min_specialization)] -#![feature(never_type)] -#![feature(num_midpoint_signed)] -#![feature(numfmt)] -#![feature(pattern)] -#![feature(pointer_is_aligned_to)] -#![feature(portable_simd)] -#![feature(ptr_metadata)] -#![feature(slice_from_ptr_range)] -#![feature(slice_internals)] -#![feature(slice_partition_dedup)] -#![feature(slice_split_once)] -#![feature(slice_take)] -#![feature(split_array)] -#![feature(split_as_slice)] -#![feature(std_internals)] -#![feature(step_trait)] -#![feature(str_internals)] -#![feature(strict_provenance_atomic_ptr)] -#![feature(strict_provenance_lints)] -#![feature(test)] -#![feature(trait_upcasting)] -#![feature(trusted_len)] -#![feature(trusted_random_access)] -#![feature(try_blocks)] -#![feature(try_find)] -#![feature(try_trait_v2)] -#![feature(unsigned_is_multiple_of)] -#![feature(unsize)] -#![feature(unsized_tuple_coercion)] -#![feature(unwrap_infallible)] -// tidy-alphabetical-end -#![allow(internal_features)] -#![deny(fuzzy_provenance_casts)] -#![deny(unsafe_op_in_unsafe_fn)] - -/// Version of `assert_matches` that ignores fancy runtime printing in const context and uses structural equality. -macro_rules! assert_eq_const_safe { - ($left:expr, $right:expr) => { - assert_eq_const_safe!($left, $right, concat!(stringify!($left), " == ", stringify!($right))); - }; - ($left:expr, $right:expr$(, $($arg:tt)+)?) => { - { - fn runtime() { - assert_eq!($left, $right, $($($arg)*),*); - } - const fn compiletime() { - assert!(matches!($left, const { $right })); - } - core::intrinsics::const_eval_select((), compiletime, runtime) - } - }; -} - -/// Creates a test for runtime and a test for constant-time. -macro_rules! test_runtime_and_compiletime { - ($( - $(#[$attr:meta])* - fn $test:ident() $block:block - )*) => { - $( - $(#[$attr])* - #[test] - fn $test() $block - $(#[$attr])* - const _: () = $block; - )* - } -} - -mod alloc; -mod any; -mod array; -mod ascii; -mod ascii_char; -mod asserting; -mod async_iter; -mod atomic; -mod bool; -mod cell; -mod char; -mod clone; -mod cmp; -mod const_ptr; -mod convert; -mod ffi; -mod fmt; -mod future; -mod hash; -mod intrinsics; -mod io; -mod iter; -mod lazy; -mod macros; -mod manually_drop; -mod mem; -mod net; -mod nonzero; -mod num; -mod ops; -mod option; -mod panic; -mod pattern; -mod pin; -mod pin_macro; -mod ptr; -mod result; -mod simd; -mod slice; -mod str; -mod str_lossy; -mod task; -mod time; -mod tuple; -mod unicode; -mod waker; - -/// Copied from `std::test_helpers::test_rng`, see that function for rationale. -#[track_caller] -#[allow(dead_code)] // Not used in all configurations. -pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { - use core::hash::{BuildHasher, Hash, Hasher}; - let mut hasher = std::hash::RandomState::new().build_hasher(); - core::panic::Location::caller().hash(&mut hasher); - let hc64 = hasher.finish(); - let seed_vec = hc64.to_le_bytes().into_iter().chain(0u8..8).collect::>(); - let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap(); - rand::SeedableRng::from_seed(seed) -} diff --git a/library/core/tests/macros.rs b/library/core/tests/macros.rs deleted file mode 100644 index fdb4ea2941285..0000000000000 --- a/library/core/tests/macros.rs +++ /dev/null @@ -1,195 +0,0 @@ -#![allow(unused_must_use)] - -#[allow(dead_code)] -trait Trait { - fn blah(&self); -} - -#[allow(dead_code)] -struct Struct; - -impl Trait for Struct { - cfg_match! { - cfg(feature = "blah") => { - fn blah(&self) { - unimplemented!(); - } - } - _ => { - fn blah(&self) { - unimplemented!(); - } - } - } -} - -#[test] -fn assert_eq_trailing_comma() { - assert_eq!(1, 1,); -} - -#[test] -fn assert_escape() { - assert!(r#"☃\backslash"#.contains("\\")); -} - -#[test] -fn assert_ne_trailing_comma() { - assert_ne!(1, 2,); -} - -#[rustfmt::skip] -#[test] -fn matches_leading_pipe() { - matches!(1, | 1 | 2 | 3); -} - -#[test] -fn cfg_match_basic() { - cfg_match! { - cfg(target_pointer_width = "64") => { fn f0_() -> bool { true }} - } - - cfg_match! { - cfg(unix) => { fn f1_() -> bool { true }} - cfg(any(target_os = "macos", target_os = "linux")) => { fn f1_() -> bool { false }} - } - - cfg_match! { - cfg(target_pointer_width = "32") => { fn f2_() -> bool { false }} - cfg(target_pointer_width = "64") => { fn f2_() -> bool { true }} - } - - cfg_match! { - cfg(target_pointer_width = "16") => { fn f3_() -> i32 { 1 }} - _ => { fn f3_() -> i32 { 2 }} - } - - #[cfg(target_pointer_width = "64")] - assert!(f0_()); - - #[cfg(unix)] - assert!(f1_()); - - #[cfg(target_pointer_width = "32")] - assert!(!f2_()); - #[cfg(target_pointer_width = "64")] - assert!(f2_()); - - #[cfg(not(target_pointer_width = "16"))] - assert_eq!(f3_(), 2); -} - -#[test] -fn cfg_match_debug_assertions() { - cfg_match! { - cfg(debug_assertions) => { - assert!(cfg!(debug_assertions)); - assert_eq!(4, 2+2); - } - _ => { - assert!(cfg!(not(debug_assertions))); - assert_eq!(10, 5+5); - } - } -} - -#[cfg(target_pointer_width = "64")] -#[test] -fn cfg_match_no_duplication_on_64() { - cfg_match! { - cfg(windows) => { - fn foo() {} - } - cfg(unix) => { - fn foo() {} - } - cfg(target_pointer_width = "64") => { - fn foo() {} - } - } - foo(); -} - -#[test] -fn cfg_match_options() { - cfg_match! { - cfg(test) => { - use core::option::Option as Option2; - fn works1() -> Option2 { Some(1) } - } - _ => { fn works1() -> Option { None } } - } - - cfg_match! { - cfg(feature = "foo") => { fn works2() -> bool { false } } - cfg(test) => { fn works2() -> bool { true } } - _ => { fn works2() -> bool { false } } - } - - cfg_match! { - cfg(feature = "foo") => { fn works3() -> bool { false } } - _ => { fn works3() -> bool { true } } - } - - cfg_match! { - cfg(test) => { - use core::option::Option as Option3; - fn works4() -> Option3 { Some(1) } - } - } - - cfg_match! { - cfg(feature = "foo") => { fn works5() -> bool { false } } - cfg(test) => { fn works5() -> bool { true } } - } - - assert!(works1().is_some()); - assert!(works2()); - assert!(works3()); - assert!(works4().is_some()); - assert!(works5()); -} - -#[test] -fn cfg_match_two_functions() { - cfg_match! { - cfg(target_pointer_width = "64") => { - fn foo1() {} - fn bar1() {} - } - _ => { - fn foo2() {} - fn bar2() {} - } - } - - #[cfg(target_pointer_width = "64")] - { - foo1(); - bar1(); - } - #[cfg(not(target_pointer_width = "64"))] - { - foo2(); - bar2(); - } -} - -fn _accepts_expressions() -> i32 { - cfg_match! { - cfg(unix) => { 1 } - _ => { 2 } - } -} - -// The current implementation expands to a macro call, which allows the use of expression -// statements. -fn _allows_stmt_expr_attributes() { - let one = 1; - let two = 2; - cfg_match! { - cfg(unix) => { one * two; } - _ => { one + two; } - } -} diff --git a/library/core/tests/num/flt2dec/mod.rs b/library/core/tests/num/flt2dec/mod.rs deleted file mode 100644 index 3d82522481316..0000000000000 --- a/library/core/tests/num/flt2dec/mod.rs +++ /dev/null @@ -1,1168 +0,0 @@ -use core::num::flt2dec::{ - DecodableFloat, Decoded, FullDecoded, MAX_SIG_DIGITS, Sign, decode, round_up, to_exact_exp_str, - to_exact_fixed_str, to_shortest_exp_str, to_shortest_str, -}; -use core::num::fmt::{Formatted, Part}; -use std::mem::MaybeUninit; -use std::{fmt, str}; - -mod estimator; -mod strategy { - mod dragon; - mod grisu; -} -mod random; - -pub fn decode_finite(v: T) -> Decoded { - match decode(v).1 { - FullDecoded::Finite(decoded) => decoded, - full_decoded => panic!("expected finite, got {full_decoded:?} instead"), - } -} - -macro_rules! check_shortest { - ($f:ident($v:expr) => $buf:expr, $exp:expr) => ( - check_shortest!($f($v) => $buf, $exp; - "shortest mismatch for v={v}: actual {actual:?}, expected {expected:?}", - v = stringify!($v)) - ); - - ($f:ident{$($k:ident: $v:expr),+} => $buf:expr, $exp:expr) => ( - check_shortest!($f{$($k: $v),+} => $buf, $exp; - "shortest mismatch for {v:?}: actual {actual:?}, expected {expected:?}", - v = Decoded { $($k: $v),+ }) - ); - - ($f:ident($v:expr) => $buf:expr, $exp:expr; $fmt:expr, $($key:ident = $val:expr),*) => ({ - let mut buf = [MaybeUninit::new(b'_'); MAX_SIG_DIGITS]; - let (buf, k) = $f(&decode_finite($v), &mut buf); - assert!((buf, k) == ($buf, $exp), - $fmt, actual = (str::from_utf8(buf).unwrap(), k), - expected = (str::from_utf8($buf).unwrap(), $exp), - $($key = $val),*); - }); - - ($f:ident{$($k:ident: $v:expr),+} => $buf:expr, $exp:expr; - $fmt:expr, $($key:ident = $val:expr),*) => ({ - let mut buf = [MaybeUninit::new(b'_'); MAX_SIG_DIGITS]; - let (buf, k) = $f(&Decoded { $($k: $v),+ }, &mut buf); - assert!((buf, k) == ($buf, $exp), - $fmt, actual = (str::from_utf8(buf).unwrap(), k), - expected = (str::from_utf8($buf).unwrap(), $exp), - $($key = $val),*); - }) -} - -macro_rules! try_exact { - ($f:ident($decoded:expr) => $buf:expr, $expected:expr, $expectedk:expr; - $fmt:expr, $($key:ident = $val:expr),*) => ({ - let (buf, k) = $f($decoded, &mut $buf[..$expected.len()], i16::MIN); - assert!((buf, k) == ($expected, $expectedk), - $fmt, actual = (str::from_utf8(buf).unwrap(), k), - expected = (str::from_utf8($expected).unwrap(), $expectedk), - $($key = $val),*); - }) -} - -macro_rules! try_fixed { - ($f:ident($decoded:expr) => $buf:expr, $request:expr, $expected:expr, $expectedk:expr; - $fmt:expr, $($key:ident = $val:expr),*) => ({ - let (buf, k) = $f($decoded, &mut $buf[..], $request); - assert!((buf, k) == ($expected, $expectedk), - $fmt, actual = (str::from_utf8(buf).unwrap(), k), - expected = (str::from_utf8($expected).unwrap(), $expectedk), - $($key = $val),*); - }) -} - -fn ldexp_f32(a: f32, b: i32) -> f32 { - ldexp_f64(a as f64, b) as f32 -} - -fn ldexp_f64(a: f64, b: i32) -> f64 { - extern "C" { - fn ldexp(x: f64, n: i32) -> f64; - } - // SAFETY: assuming a correct `ldexp` has been supplied, the given arguments cannot possibly - // cause undefined behavior - unsafe { ldexp(a, b) } -} - -fn check_exact(mut f: F, v: T, vstr: &str, expected: &[u8], expectedk: i16) -where - T: DecodableFloat, - F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), -{ - // use a large enough buffer - let mut buf = [MaybeUninit::new(b'_'); 1024]; - let mut expected_ = [b'_'; 1024]; - - let decoded = decode_finite(v); - let cut = expected.iter().position(|&c| c == b' '); - - // check significant digits - for i in 1..cut.unwrap_or(expected.len() - 1) { - expected_[..i].copy_from_slice(&expected[..i]); - let mut expectedk_ = expectedk; - if expected[i] >= b'5' { - // check if this is a rounding-to-even case. - // we avoid rounding ...x5000... (with infinite zeroes) to ...(x+1) when x is even. - if !(i + 1 < expected.len() - && expected[i - 1] & 1 == 0 - && expected[i] == b'5' - && expected[i + 1] == b' ') - { - // if this returns true, expected_[..i] is all `9`s and being rounded up. - // we should always return `100..00` (`i` digits) instead, since that's - // what we can came up with `i` digits anyway. `round_up` assumes that - // the adjustment to the length is done by caller, which we simply ignore. - if let Some(_) = round_up(&mut expected_[..i]) { - expectedk_ += 1; - } - } - } - - try_exact!(f(&decoded) => &mut buf, &expected_[..i], expectedk_; - "exact sigdigit mismatch for v={v}, i={i}: \ - actual {actual:?}, expected {expected:?}", - v = vstr, i = i); - try_fixed!(f(&decoded) => &mut buf, expectedk_ - i as i16, &expected_[..i], expectedk_; - "fixed sigdigit mismatch for v={v}, i={i}: \ - actual {actual:?}, expected {expected:?}", - v = vstr, i = i); - } - - // check exact rounding for zero- and negative-width cases - let start; - if expected[0] > b'5' { - try_fixed!(f(&decoded) => &mut buf, expectedk, b"1", expectedk + 1; - "zero-width rounding-up mismatch for v={v}: \ - actual {actual:?}, expected {expected:?}", - v = vstr); - start = 1; - } else { - start = 0; - } - for i in start..-10 { - try_fixed!(f(&decoded) => &mut buf, expectedk - i, b"", expectedk; - "rounding-down mismatch for v={v}, i={i}: \ - actual {actual:?}, expected {expected:?}", - v = vstr, i = -i); - } - - // check infinite zero digits - if let Some(cut) = cut { - for i in cut..expected.len() - 1 { - expected_[..cut].copy_from_slice(&expected[..cut]); - for c in &mut expected_[cut..i] { - *c = b'0'; - } - - try_exact!(f(&decoded) => &mut buf, &expected_[..i], expectedk; - "exact infzero mismatch for v={v}, i={i}: \ - actual {actual:?}, expected {expected:?}", - v = vstr, i = i); - try_fixed!(f(&decoded) => &mut buf, expectedk - i as i16, &expected_[..i], expectedk; - "fixed infzero mismatch for v={v}, i={i}: \ - actual {actual:?}, expected {expected:?}", - v = vstr, i = i); - } - } -} - -trait TestableFloat: DecodableFloat + fmt::Display { - /// Returns `x * 2^exp`. Almost same to `std::{f32,f64}::ldexp`. - /// This is used for testing. - fn ldexpi(f: i64, exp: isize) -> Self; -} - -impl TestableFloat for f32 { - fn ldexpi(f: i64, exp: isize) -> Self { - f as Self * (exp as Self).exp2() - } -} - -impl TestableFloat for f64 { - fn ldexpi(f: i64, exp: isize) -> Self { - f as Self * (exp as Self).exp2() - } -} - -fn check_exact_one(mut f: F, x: i64, e: isize, tstr: &str, expected: &[u8], expectedk: i16) -where - T: TestableFloat, - F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), -{ - // use a large enough buffer - let mut buf = [MaybeUninit::new(b'_'); 1024]; - let v: T = TestableFloat::ldexpi(x, e); - let decoded = decode_finite(v); - - try_exact!(f(&decoded) => &mut buf, &expected, expectedk; - "exact mismatch for v={x}p{e}{t}: actual {actual:?}, expected {expected:?}", - x = x, e = e, t = tstr); - try_fixed!(f(&decoded) => &mut buf, expectedk - expected.len() as i16, &expected, expectedk; - "fixed mismatch for v={x}p{e}{t}: actual {actual:?}, expected {expected:?}", - x = x, e = e, t = tstr); -} - -macro_rules! check_exact { - ($f:ident($v:expr) => $buf:expr, $exp:expr) => { - check_exact(|d, b, k| $f(d, b, k), $v, stringify!($v), $buf, $exp) - }; -} - -macro_rules! check_exact_one { - ($f:ident($x:expr, $e:expr; $t:ty) => $buf:expr, $exp:expr) => { - check_exact_one::<_, $t>(|d, b, k| $f(d, b, k), $x, $e, stringify!($t), $buf, $exp) - }; -} - -// in the following comments, three numbers are spaced by 1 ulp apart, -// and the second one is being formatted. -// -// some tests are derived from [1]. -// -// [1] Vern Paxson, A Program for Testing IEEE Decimal-Binary Conversion -// ftp://ftp.ee.lbl.gov/testbase-report.ps.Z - -pub fn f32_shortest_sanity_test(mut f: F) -where - F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), -{ - // 0.0999999940395355224609375 - // 0.100000001490116119384765625 - // 0.10000000894069671630859375 - check_shortest!(f(0.1f32) => b"1", 0); - - // 0.333333313465118408203125 - // 0.3333333432674407958984375 (1/3 in the default rounding) - // 0.33333337306976318359375 - check_shortest!(f(1.0f32/3.0) => b"33333334", 0); - - // 10^1 * 0.31415917873382568359375 - // 10^1 * 0.31415920257568359375 - // 10^1 * 0.31415922641754150390625 - check_shortest!(f(3.141592f32) => b"3141592", 1); - - // 10^18 * 0.31415916243714048 - // 10^18 * 0.314159196796878848 - // 10^18 * 0.314159231156617216 - check_shortest!(f(3.141592e17f32) => b"3141592", 18); - - // regression test for decoders - // 10^8 * 0.3355443 - // 10^8 * 0.33554432 - // 10^8 * 0.33554436 - check_shortest!(f(ldexp_f32(1.0, 25)) => b"33554432", 8); - - // 10^39 * 0.340282326356119256160033759537265639424 - // 10^39 * 0.34028234663852885981170418348451692544 - // 10^39 * 0.340282366920938463463374607431768211456 - check_shortest!(f(f32::MAX) => b"34028235", 39); - - // 10^-37 * 0.1175494210692441075487029444849287348827... - // 10^-37 * 0.1175494350822287507968736537222245677818... - // 10^-37 * 0.1175494490952133940450443629595204006810... - check_shortest!(f(f32::MIN_POSITIVE) => b"11754944", -37); - - // 10^-44 * 0 - // 10^-44 * 0.1401298464324817070923729583289916131280... - // 10^-44 * 0.2802596928649634141847459166579832262560... - let minf32 = ldexp_f32(1.0, -149); - check_shortest!(f(minf32) => b"1", -44); -} - -pub fn f32_exact_sanity_test(mut f: F) -where - F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), -{ - let minf32 = ldexp_f32(1.0, -149); - - check_exact!(f(0.1f32) => b"100000001490116119384765625 ", 0); - check_exact!(f(0.5f32) => b"5 ", 0); - check_exact!(f(1.0f32/3.0) => b"3333333432674407958984375 ", 0); - check_exact!(f(3.141592f32) => b"31415920257568359375 ", 1); - check_exact!(f(3.141592e17f32) => b"314159196796878848 ", 18); - check_exact!(f(f32::MAX) => b"34028234663852885981170418348451692544 ", 39); - check_exact!(f(f32::MIN_POSITIVE) => b"1175494350822287507968736537222245677818", -37); - check_exact!(f(minf32) => b"1401298464324817070923729583289916131280", -44); - - // [1], Table 16: Stress Inputs for Converting 24-bit Binary to Decimal, < 1/2 ULP - check_exact_one!(f(12676506, -102; f32) => b"2", -23); - check_exact_one!(f(12676506, -103; f32) => b"12", -23); - check_exact_one!(f(15445013, 86; f32) => b"119", 34); - check_exact_one!(f(13734123, -138; f32) => b"3941", -34); - check_exact_one!(f(12428269, -130; f32) => b"91308", -32); - check_exact_one!(f(15334037, -146; f32) => b"171900", -36); - check_exact_one!(f(11518287, -41; f32) => b"5237910", -5); - check_exact_one!(f(12584953, -145; f32) => b"28216440", -36); - check_exact_one!(f(15961084, -125; f32) => b"375243281", -30); - check_exact_one!(f(14915817, -146; f32) => b"1672120916", -36); - check_exact_one!(f(10845484, -102; f32) => b"21388945814", -23); - check_exact_one!(f(16431059, -61; f32) => b"712583594561", -11); - - // [1], Table 17: Stress Inputs for Converting 24-bit Binary to Decimal, > 1/2 ULP - check_exact_one!(f(16093626, 69; f32) => b"1", 29); - check_exact_one!(f( 9983778, 25; f32) => b"34", 15); - check_exact_one!(f(12745034, 104; f32) => b"259", 39); - check_exact_one!(f(12706553, 72; f32) => b"6001", 29); - check_exact_one!(f(11005028, 45; f32) => b"38721", 21); - check_exact_one!(f(15059547, 71; f32) => b"355584", 29); - check_exact_one!(f(16015691, -99; f32) => b"2526831", -22); - check_exact_one!(f( 8667859, 56; f32) => b"62458507", 24); - check_exact_one!(f(14855922, -82; f32) => b"307213267", -17); - check_exact_one!(f(14855922, -83; f32) => b"1536066333", -17); - check_exact_one!(f(10144164, -110; f32) => b"78147796834", -26); - check_exact_one!(f(13248074, 95; f32) => b"524810279937", 36); -} - -pub fn f64_shortest_sanity_test(mut f: F) -where - F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), -{ - // 0.0999999999999999777955395074968691915273... - // 0.1000000000000000055511151231257827021181... - // 0.1000000000000000333066907387546962127089... - check_shortest!(f(0.1f64) => b"1", 0); - - // this example is explicitly mentioned in the paper. - // 10^3 * 0.0999999999999999857891452847979962825775... - // 10^3 * 0.1 (exact) - // 10^3 * 0.1000000000000000142108547152020037174224... - check_shortest!(f(100.0f64) => b"1", 3); - - // 0.3333333333333332593184650249895639717578... - // 0.3333333333333333148296162562473909929394... (1/3 in the default rounding) - // 0.3333333333333333703407674875052180141210... - check_shortest!(f(1.0f64/3.0) => b"3333333333333333", 0); - - // explicit test case for equally closest representations. - // Dragon has its own tie-breaking rule; Grisu should fall back. - // 10^1 * 0.1000007629394531027955395074968691915273... - // 10^1 * 0.100000762939453125 (exact) - // 10^1 * 0.1000007629394531472044604925031308084726... - check_shortest!(f(1.00000762939453125f64) => b"10000076293945313", 1); - - // 10^1 * 0.3141591999999999718085064159822650253772... - // 10^1 * 0.3141592000000000162174274009885266423225... - // 10^1 * 0.3141592000000000606263483859947882592678... - check_shortest!(f(3.141592f64) => b"3141592", 1); - - // 10^18 * 0.314159199999999936 - // 10^18 * 0.3141592 (exact) - // 10^18 * 0.314159200000000064 - check_shortest!(f(3.141592e17f64) => b"3141592", 18); - - // regression test for decoders - // 10^20 * 0.18446744073709549568 - // 10^20 * 0.18446744073709551616 - // 10^20 * 0.18446744073709555712 - check_shortest!(f(ldexp_f64(1.0, 64)) => b"18446744073709552", 20); - - // pathological case: high = 10^23 (exact). tie breaking should always prefer that. - // 10^24 * 0.099999999999999974834176 - // 10^24 * 0.099999999999999991611392 - // 10^24 * 0.100000000000000008388608 - check_shortest!(f(1.0e23f64) => b"1", 24); - - // 10^309 * 0.1797693134862315508561243283845062402343... - // 10^309 * 0.1797693134862315708145274237317043567980... - // 10^309 * 0.1797693134862315907729305190789024733617... - check_shortest!(f(f64::MAX) => b"17976931348623157", 309); - - // 10^-307 * 0.2225073858507200889024586876085859887650... - // 10^-307 * 0.2225073858507201383090232717332404064219... - // 10^-307 * 0.2225073858507201877155878558578948240788... - check_shortest!(f(f64::MIN_POSITIVE) => b"22250738585072014", -307); - - // 10^-323 * 0 - // 10^-323 * 0.4940656458412465441765687928682213723650... - // 10^-323 * 0.9881312916824930883531375857364427447301... - let minf64 = ldexp_f64(1.0, -1074); - check_shortest!(f(minf64) => b"5", -323); -} - -pub fn f64_exact_sanity_test(mut f: F) -where - F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), -{ - let minf64 = ldexp_f64(1.0, -1074); - - check_exact!(f(0.1f64) => b"1000000000000000055511151231257827021181", 0); - check_exact!(f(0.45f64) => b"4500000000000000111022302462515654042363", 0); - check_exact!(f(0.5f64) => b"5 ", 0); - check_exact!(f(0.95f64) => b"9499999999999999555910790149937383830547", 0); - check_exact!(f(100.0f64) => b"1 ", 3); - check_exact!(f(999.5f64) => b"9995000000000000000000000000000000000000", 3); - check_exact!(f(1.0f64/3.0) => b"3333333333333333148296162562473909929394", 0); - check_exact!(f(3.141592f64) => b"3141592000000000162174274009885266423225", 1); - check_exact!(f(3.141592e17f64) => b"3141592 ", 18); - check_exact!(f(1.0e23f64) => b"99999999999999991611392 ", 23); - check_exact!(f(f64::MAX) => b"1797693134862315708145274237317043567980", 309); - check_exact!(f(f64::MIN_POSITIVE) => b"2225073858507201383090232717332404064219", -307); - check_exact!(f(minf64) => b"4940656458412465441765687928682213723650\ - 5980261432476442558568250067550727020875\ - 1865299836361635992379796564695445717730\ - 9266567103559397963987747960107818781263\ - 0071319031140452784581716784898210368871\ - 8636056998730723050006387409153564984387\ - 3124733972731696151400317153853980741262\ - 3856559117102665855668676818703956031062\ - 4931945271591492455329305456544401127480\ - 1297099995419319894090804165633245247571\ - 4786901472678015935523861155013480352649\ - 3472019379026810710749170333222684475333\ - 5720832431936092382893458368060106011506\ - 1698097530783422773183292479049825247307\ - 7637592724787465608477820373446969953364\ - 7017972677717585125660551199131504891101\ - 4510378627381672509558373897335989936648\ - 0994116420570263709027924276754456522908\ - 7538682506419718265533447265625 ", -323); - - // [1], Table 3: Stress Inputs for Converting 53-bit Binary to Decimal, < 1/2 ULP - check_exact_one!(f(8511030020275656, -342; f64) => b"9", -87); - check_exact_one!(f(5201988407066741, -824; f64) => b"46", -232); - check_exact_one!(f(6406892948269899, 237; f64) => b"141", 88); - check_exact_one!(f(8431154198732492, 72; f64) => b"3981", 38); - check_exact_one!(f(6475049196144587, 99; f64) => b"41040", 46); - check_exact_one!(f(8274307542972842, 726; f64) => b"292084", 235); - check_exact_one!(f(5381065484265332, -456; f64) => b"2891946", -121); - check_exact_one!(f(6761728585499734, -1057; f64) => b"43787718", -302); - check_exact_one!(f(7976538478610756, 376; f64) => b"122770163", 130); - check_exact_one!(f(5982403858958067, 377; f64) => b"1841552452", 130); - check_exact_one!(f(5536995190630837, 93; f64) => b"54835744350", 44); - check_exact_one!(f(7225450889282194, 710; f64) => b"389190181146", 230); - check_exact_one!(f(7225450889282194, 709; f64) => b"1945950905732", 230); - check_exact_one!(f(8703372741147379, 117; f64) => b"14460958381605", 52); - check_exact_one!(f(8944262675275217, -1001; f64) => b"417367747458531", -285); - check_exact_one!(f(7459803696087692, -707; f64) => b"1107950772878888", -196); - check_exact_one!(f(6080469016670379, -381; f64) => b"12345501366327440", -98); - check_exact_one!(f(8385515147034757, 721; f64) => b"925031711960365024", 233); - check_exact_one!(f(7514216811389786, -828; f64) => b"4198047150284889840", -233); - check_exact_one!(f(8397297803260511, -345; f64) => b"11716315319786511046", -87); - check_exact_one!(f(6733459239310543, 202; f64) => b"432810072844612493629", 77); - check_exact_one!(f(8091450587292794, -473; f64) => b"3317710118160031081518", -126); - - // [1], Table 4: Stress Inputs for Converting 53-bit Binary to Decimal, > 1/2 ULP - check_exact_one!(f(6567258882077402, 952; f64) => b"3", 303); - check_exact_one!(f(6712731423444934, 535; f64) => b"76", 177); - check_exact_one!(f(6712731423444934, 534; f64) => b"378", 177); - check_exact_one!(f(5298405411573037, -957; f64) => b"4350", -272); - check_exact_one!(f(5137311167659507, -144; f64) => b"23037", -27); - check_exact_one!(f(6722280709661868, 363; f64) => b"126301", 126); - check_exact_one!(f(5344436398034927, -169; f64) => b"7142211", -35); - check_exact_one!(f(8369123604277281, -853; f64) => b"13934574", -240); - check_exact_one!(f(8995822108487663, -780; f64) => b"141463449", -218); - check_exact_one!(f(8942832835564782, -383; f64) => b"4539277920", -99); - check_exact_one!(f(8942832835564782, -384; f64) => b"22696389598", -99); - check_exact_one!(f(8942832835564782, -385; f64) => b"113481947988", -99); - check_exact_one!(f(6965949469487146, -249; f64) => b"7700366561890", -59); - check_exact_one!(f(6965949469487146, -250; f64) => b"38501832809448", -59); - check_exact_one!(f(6965949469487146, -251; f64) => b"192509164047238", -59); - check_exact_one!(f(7487252720986826, 548; f64) => b"6898586531774201", 181); - check_exact_one!(f(5592117679628511, 164; f64) => b"13076622631878654", 66); - check_exact_one!(f(8887055249355788, 665; f64) => b"136052020756121240", 217); - check_exact_one!(f(6994187472632449, 690; f64) => b"3592810217475959676", 224); - check_exact_one!(f(8797576579012143, 588; f64) => b"89125197712484551899", 193); - check_exact_one!(f(7363326733505337, 272; f64) => b"558769757362301140950", 98); - check_exact_one!(f(8549497411294502, -448; f64) => b"1176257830728540379990", -118); -} - -pub fn more_shortest_sanity_test(mut f: F) -where - F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), -{ - check_shortest!(f{mant: 99_999_999_999_999_999, minus: 1, plus: 1, - exp: 0, inclusive: true} => b"1", 18); - check_shortest!(f{mant: 99_999_999_999_999_999, minus: 1, plus: 1, - exp: 0, inclusive: false} => b"99999999999999999", 17); -} - -fn to_string_with_parts(mut f: F) -> String -where - F: for<'a> FnMut(&'a mut [MaybeUninit], &'a mut [MaybeUninit>]) -> Formatted<'a>, -{ - let mut buf = [MaybeUninit::new(0); 1024]; - let mut parts = [MaybeUninit::new(Part::Zero(0)); 16]; - let formatted = f(&mut buf, &mut parts); - let mut ret = vec![0; formatted.len()]; - assert_eq!(formatted.write(&mut ret), Some(ret.len())); - String::from_utf8(ret).unwrap() -} - -pub fn to_shortest_str_test(mut f_: F) -where - F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), -{ - use core::num::flt2dec::Sign::*; - - fn to_string(f: &mut F, v: T, sign: Sign, frac_digits: usize) -> String - where - T: DecodableFloat, - F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), - { - to_string_with_parts(|buf, parts| { - to_shortest_str(|d, b| f(d, b), v, sign, frac_digits, buf, parts) - }) - } - - let f = &mut f_; - - assert_eq!(to_string(f, 0.0, Minus, 0), "0"); - assert_eq!(to_string(f, 0.0, Minus, 0), "0"); - assert_eq!(to_string(f, 0.0, MinusPlus, 0), "+0"); - assert_eq!(to_string(f, -0.0, Minus, 0), "-0"); - assert_eq!(to_string(f, -0.0, MinusPlus, 0), "-0"); - assert_eq!(to_string(f, 0.0, Minus, 1), "0.0"); - assert_eq!(to_string(f, 0.0, Minus, 1), "0.0"); - assert_eq!(to_string(f, 0.0, MinusPlus, 1), "+0.0"); - assert_eq!(to_string(f, -0.0, Minus, 8), "-0.00000000"); - assert_eq!(to_string(f, -0.0, MinusPlus, 8), "-0.00000000"); - - assert_eq!(to_string(f, 1.0 / 0.0, Minus, 0), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, Minus, 0), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, 0), "+inf"); - assert_eq!(to_string(f, 0.0 / 0.0, Minus, 0), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, Minus, 1), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 64), "NaN"); - assert_eq!(to_string(f, -1.0 / 0.0, Minus, 0), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, Minus, 1), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 64), "-inf"); - - assert_eq!(to_string(f, 3.14, Minus, 0), "3.14"); - assert_eq!(to_string(f, 3.14, Minus, 0), "3.14"); - assert_eq!(to_string(f, 3.14, MinusPlus, 0), "+3.14"); - assert_eq!(to_string(f, -3.14, Minus, 0), "-3.14"); - assert_eq!(to_string(f, -3.14, Minus, 0), "-3.14"); - assert_eq!(to_string(f, -3.14, MinusPlus, 0), "-3.14"); - assert_eq!(to_string(f, 3.14, Minus, 1), "3.14"); - assert_eq!(to_string(f, 3.14, Minus, 2), "3.14"); - assert_eq!(to_string(f, 3.14, MinusPlus, 4), "+3.1400"); - assert_eq!(to_string(f, -3.14, Minus, 8), "-3.14000000"); - assert_eq!(to_string(f, -3.14, Minus, 8), "-3.14000000"); - assert_eq!(to_string(f, -3.14, MinusPlus, 8), "-3.14000000"); - - assert_eq!(to_string(f, 7.5e-11, Minus, 0), "0.000000000075"); - assert_eq!(to_string(f, 7.5e-11, Minus, 3), "0.000000000075"); - assert_eq!(to_string(f, 7.5e-11, Minus, 12), "0.000000000075"); - assert_eq!(to_string(f, 7.5e-11, Minus, 13), "0.0000000000750"); - - assert_eq!(to_string(f, 1.9971e20, Minus, 0), "199710000000000000000"); - assert_eq!(to_string(f, 1.9971e20, Minus, 1), "199710000000000000000.0"); - assert_eq!(to_string(f, 1.9971e20, Minus, 8), "199710000000000000000.00000000"); - - assert_eq!(to_string(f, f32::MAX, Minus, 0), format!("34028235{:0>31}", "")); - assert_eq!(to_string(f, f32::MAX, Minus, 1), format!("34028235{:0>31}.0", "")); - assert_eq!(to_string(f, f32::MAX, Minus, 8), format!("34028235{:0>31}.00000000", "")); - - let minf32 = ldexp_f32(1.0, -149); - assert_eq!(to_string(f, minf32, Minus, 0), format!("0.{:0>44}1", "")); - assert_eq!(to_string(f, minf32, Minus, 45), format!("0.{:0>44}1", "")); - assert_eq!(to_string(f, minf32, Minus, 46), format!("0.{:0>44}10", "")); - - assert_eq!(to_string(f, f64::MAX, Minus, 0), format!("17976931348623157{:0>292}", "")); - assert_eq!(to_string(f, f64::MAX, Minus, 1), format!("17976931348623157{:0>292}.0", "")); - assert_eq!(to_string(f, f64::MAX, Minus, 8), format!("17976931348623157{:0>292}.00000000", "")); - - let minf64 = ldexp_f64(1.0, -1074); - assert_eq!(to_string(f, minf64, Minus, 0), format!("0.{:0>323}5", "")); - assert_eq!(to_string(f, minf64, Minus, 324), format!("0.{:0>323}5", "")); - assert_eq!(to_string(f, minf64, Minus, 325), format!("0.{:0>323}50", "")); - - if cfg!(miri) { - // Miri is too slow - return; - } - - // very large output - assert_eq!(to_string(f, 1.1, Minus, 80000), format!("1.1{:0>79999}", "")); -} - -pub fn to_shortest_exp_str_test(mut f_: F) -where - F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), -{ - use core::num::flt2dec::Sign::*; - - fn to_string(f: &mut F, v: T, sign: Sign, exp_bounds: (i16, i16), upper: bool) -> String - where - T: DecodableFloat, - F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), - { - to_string_with_parts(|buf, parts| { - to_shortest_exp_str(|d, b| f(d, b), v, sign, exp_bounds, upper, buf, parts) - }) - } - - let f = &mut f_; - - assert_eq!(to_string(f, 0.0, Minus, (-4, 16), false), "0"); - assert_eq!(to_string(f, 0.0, Minus, (-4, 16), false), "0"); - assert_eq!(to_string(f, 0.0, MinusPlus, (-4, 16), false), "+0"); - assert_eq!(to_string(f, -0.0, Minus, (-4, 16), false), "-0"); - assert_eq!(to_string(f, -0.0, MinusPlus, (-4, 16), false), "-0"); - assert_eq!(to_string(f, 0.0, Minus, (0, 0), true), "0E0"); - assert_eq!(to_string(f, 0.0, Minus, (0, 0), false), "0e0"); - assert_eq!(to_string(f, 0.0, MinusPlus, (5, 9), false), "+0e0"); - assert_eq!(to_string(f, -0.0, Minus, (0, 0), true), "-0E0"); - assert_eq!(to_string(f, -0.0, MinusPlus, (5, 9), false), "-0e0"); - - assert_eq!(to_string(f, 1.0 / 0.0, Minus, (-4, 16), false), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, Minus, (-4, 16), true), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, (-4, 16), true), "+inf"); - assert_eq!(to_string(f, 0.0 / 0.0, Minus, (0, 0), false), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, Minus, (0, 0), true), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, (5, 9), true), "NaN"); - assert_eq!(to_string(f, -1.0 / 0.0, Minus, (0, 0), false), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, Minus, (0, 0), true), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, (5, 9), true), "-inf"); - - assert_eq!(to_string(f, 3.14, Minus, (-4, 16), false), "3.14"); - assert_eq!(to_string(f, 3.14, MinusPlus, (-4, 16), false), "+3.14"); - assert_eq!(to_string(f, -3.14, Minus, (-4, 16), false), "-3.14"); - assert_eq!(to_string(f, -3.14, MinusPlus, (-4, 16), false), "-3.14"); - assert_eq!(to_string(f, 3.14, Minus, (0, 0), true), "3.14E0"); - assert_eq!(to_string(f, 3.14, Minus, (0, 0), false), "3.14e0"); - assert_eq!(to_string(f, 3.14, MinusPlus, (5, 9), false), "+3.14e0"); - assert_eq!(to_string(f, -3.14, Minus, (0, 0), true), "-3.14E0"); - assert_eq!(to_string(f, -3.14, Minus, (0, 0), false), "-3.14e0"); - assert_eq!(to_string(f, -3.14, MinusPlus, (5, 9), false), "-3.14e0"); - - assert_eq!(to_string(f, 0.1, Minus, (-4, 16), false), "0.1"); - assert_eq!(to_string(f, 0.1, Minus, (-4, 16), false), "0.1"); - assert_eq!(to_string(f, 0.1, MinusPlus, (-4, 16), false), "+0.1"); - assert_eq!(to_string(f, -0.1, Minus, (-4, 16), false), "-0.1"); - assert_eq!(to_string(f, -0.1, MinusPlus, (-4, 16), false), "-0.1"); - assert_eq!(to_string(f, 0.1, Minus, (0, 0), true), "1E-1"); - assert_eq!(to_string(f, 0.1, Minus, (0, 0), false), "1e-1"); - assert_eq!(to_string(f, 0.1, MinusPlus, (5, 9), false), "+1e-1"); - assert_eq!(to_string(f, -0.1, Minus, (0, 0), true), "-1E-1"); - assert_eq!(to_string(f, -0.1, Minus, (0, 0), false), "-1e-1"); - assert_eq!(to_string(f, -0.1, MinusPlus, (5, 9), false), "-1e-1"); - - assert_eq!(to_string(f, 7.5e-11, Minus, (-4, 16), false), "7.5e-11"); - assert_eq!(to_string(f, 7.5e-11, Minus, (-11, 10), false), "0.000000000075"); - assert_eq!(to_string(f, 7.5e-11, Minus, (-10, 11), false), "7.5e-11"); - - assert_eq!(to_string(f, 1.9971e20, Minus, (-4, 16), false), "1.9971e20"); - assert_eq!(to_string(f, 1.9971e20, Minus, (-20, 21), false), "199710000000000000000"); - assert_eq!(to_string(f, 1.9971e20, Minus, (-21, 20), false), "1.9971e20"); - - // the true value of 1.0e23f64 is less than 10^23, but that shouldn't matter here - assert_eq!(to_string(f, 1.0e23, Minus, (22, 23), false), "1e23"); - assert_eq!(to_string(f, 1.0e23, Minus, (23, 24), false), "100000000000000000000000"); - assert_eq!(to_string(f, 1.0e23, Minus, (24, 25), false), "1e23"); - - assert_eq!(to_string(f, f32::MAX, Minus, (-4, 16), false), "3.4028235e38"); - assert_eq!(to_string(f, f32::MAX, Minus, (-39, 38), false), "3.4028235e38"); - assert_eq!(to_string(f, f32::MAX, Minus, (-38, 39), false), format!("34028235{:0>31}", "")); - - let minf32 = ldexp_f32(1.0, -149); - assert_eq!(to_string(f, minf32, Minus, (-4, 16), false), "1e-45"); - assert_eq!(to_string(f, minf32, Minus, (-44, 45), false), "1e-45"); - assert_eq!(to_string(f, minf32, Minus, (-45, 44), false), format!("0.{:0>44}1", "")); - - assert_eq!(to_string(f, f64::MAX, Minus, (-4, 16), false), "1.7976931348623157e308"); - assert_eq!( - to_string(f, f64::MAX, Minus, (-308, 309), false), - format!("17976931348623157{:0>292}", "") - ); - assert_eq!(to_string(f, f64::MAX, Minus, (-309, 308), false), "1.7976931348623157e308"); - - let minf64 = ldexp_f64(1.0, -1074); - assert_eq!(to_string(f, minf64, Minus, (-4, 16), false), "5e-324"); - assert_eq!(to_string(f, minf64, Minus, (-324, 323), false), format!("0.{:0>323}5", "")); - assert_eq!(to_string(f, minf64, Minus, (-323, 324), false), "5e-324"); - - assert_eq!(to_string(f, 1.1, Minus, (i16::MIN, i16::MAX), false), "1.1"); -} - -pub fn to_exact_exp_str_test(mut f_: F) -where - F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), -{ - use core::num::flt2dec::Sign::*; - - fn to_string(f: &mut F, v: T, sign: Sign, ndigits: usize, upper: bool) -> String - where - T: DecodableFloat, - F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), - { - to_string_with_parts(|buf, parts| { - to_exact_exp_str(|d, b, l| f(d, b, l), v, sign, ndigits, upper, buf, parts) - }) - } - - let f = &mut f_; - - assert_eq!(to_string(f, 0.0, Minus, 1, true), "0E0"); - assert_eq!(to_string(f, 0.0, Minus, 1, false), "0e0"); - assert_eq!(to_string(f, 0.0, MinusPlus, 1, false), "+0e0"); - assert_eq!(to_string(f, -0.0, Minus, 1, true), "-0E0"); - assert_eq!(to_string(f, -0.0, MinusPlus, 1, false), "-0e0"); - assert_eq!(to_string(f, 0.0, Minus, 2, true), "0.0E0"); - assert_eq!(to_string(f, 0.0, Minus, 2, false), "0.0e0"); - assert_eq!(to_string(f, 0.0, MinusPlus, 2, false), "+0.0e0"); - assert_eq!(to_string(f, -0.0, Minus, 8, false), "-0.0000000e0"); - assert_eq!(to_string(f, -0.0, MinusPlus, 8, false), "-0.0000000e0"); - - assert_eq!(to_string(f, 1.0 / 0.0, Minus, 1, false), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, Minus, 1, true), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, 1, true), "+inf"); - assert_eq!(to_string(f, 0.0 / 0.0, Minus, 8, false), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, Minus, 8, true), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 8, true), "NaN"); - assert_eq!(to_string(f, -1.0 / 0.0, Minus, 64, false), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, Minus, 64, true), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 64, true), "-inf"); - - assert_eq!(to_string(f, 3.14, Minus, 1, true), "3E0"); - assert_eq!(to_string(f, 3.14, Minus, 1, false), "3e0"); - assert_eq!(to_string(f, 3.14, MinusPlus, 1, false), "+3e0"); - assert_eq!(to_string(f, -3.14, Minus, 2, true), "-3.1E0"); - assert_eq!(to_string(f, -3.14, Minus, 2, false), "-3.1e0"); - assert_eq!(to_string(f, -3.14, MinusPlus, 2, false), "-3.1e0"); - assert_eq!(to_string(f, 3.14, Minus, 3, true), "3.14E0"); - assert_eq!(to_string(f, 3.14, Minus, 3, false), "3.14e0"); - assert_eq!(to_string(f, 3.14, MinusPlus, 3, false), "+3.14e0"); - assert_eq!(to_string(f, -3.14, Minus, 4, true), "-3.140E0"); - assert_eq!(to_string(f, -3.14, Minus, 4, false), "-3.140e0"); - assert_eq!(to_string(f, -3.14, MinusPlus, 4, false), "-3.140e0"); - - assert_eq!(to_string(f, 0.195, Minus, 1, false), "2e-1"); - assert_eq!(to_string(f, 0.195, Minus, 1, true), "2E-1"); - assert_eq!(to_string(f, 0.195, MinusPlus, 1, true), "+2E-1"); - assert_eq!(to_string(f, -0.195, Minus, 2, false), "-2.0e-1"); - assert_eq!(to_string(f, -0.195, Minus, 2, true), "-2.0E-1"); - assert_eq!(to_string(f, -0.195, MinusPlus, 2, true), "-2.0E-1"); - assert_eq!(to_string(f, 0.195, Minus, 3, false), "1.95e-1"); - assert_eq!(to_string(f, 0.195, Minus, 3, true), "1.95E-1"); - assert_eq!(to_string(f, 0.195, MinusPlus, 3, true), "+1.95E-1"); - assert_eq!(to_string(f, -0.195, Minus, 4, false), "-1.950e-1"); - assert_eq!(to_string(f, -0.195, Minus, 4, true), "-1.950E-1"); - assert_eq!(to_string(f, -0.195, MinusPlus, 4, true), "-1.950E-1"); - - assert_eq!(to_string(f, 9.5, Minus, 1, false), "1e1"); - assert_eq!(to_string(f, 9.5, Minus, 2, false), "9.5e0"); - assert_eq!(to_string(f, 9.5, Minus, 3, false), "9.50e0"); - assert_eq!(to_string(f, 9.5, Minus, 30, false), "9.50000000000000000000000000000e0"); - - assert_eq!(to_string(f, 1.0e25, Minus, 1, false), "1e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 2, false), "1.0e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 15, false), "1.00000000000000e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 16, false), "1.000000000000000e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 17, false), "1.0000000000000001e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 18, false), "1.00000000000000009e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 19, false), "1.000000000000000091e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 20, false), "1.0000000000000000906e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 21, false), "1.00000000000000009060e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 22, false), "1.000000000000000090597e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 23, false), "1.0000000000000000905970e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 24, false), "1.00000000000000009059697e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 25, false), "1.000000000000000090596966e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 26, false), "1.0000000000000000905969664e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 27, false), "1.00000000000000009059696640e25"); - assert_eq!(to_string(f, 1.0e25, Minus, 30, false), "1.00000000000000009059696640000e25"); - - assert_eq!(to_string(f, 1.0e-6, Minus, 1, false), "1e-6"); - assert_eq!(to_string(f, 1.0e-6, Minus, 2, false), "1.0e-6"); - assert_eq!(to_string(f, 1.0e-6, Minus, 16, false), "1.000000000000000e-6"); - assert_eq!(to_string(f, 1.0e-6, Minus, 17, false), "9.9999999999999995e-7"); - assert_eq!(to_string(f, 1.0e-6, Minus, 18, false), "9.99999999999999955e-7"); - assert_eq!(to_string(f, 1.0e-6, Minus, 19, false), "9.999999999999999547e-7"); - assert_eq!(to_string(f, 1.0e-6, Minus, 20, false), "9.9999999999999995475e-7"); - assert_eq!(to_string(f, 1.0e-6, Minus, 30, false), "9.99999999999999954748111825886e-7"); - assert_eq!( - to_string(f, 1.0e-6, Minus, 40, false), - "9.999999999999999547481118258862586856139e-7" - ); - assert_eq!( - to_string(f, 1.0e-6, Minus, 50, false), - "9.9999999999999995474811182588625868561393872369081e-7" - ); - assert_eq!( - to_string(f, 1.0e-6, Minus, 60, false), - "9.99999999999999954748111825886258685613938723690807819366455e-7" - ); - assert_eq!( - to_string(f, 1.0e-6, Minus, 70, false), - "9.999999999999999547481118258862586856139387236908078193664550781250000e-7" - ); - - assert_eq!(to_string(f, f32::MAX, Minus, 1, false), "3e38"); - assert_eq!(to_string(f, f32::MAX, Minus, 2, false), "3.4e38"); - assert_eq!(to_string(f, f32::MAX, Minus, 4, false), "3.403e38"); - assert_eq!(to_string(f, f32::MAX, Minus, 8, false), "3.4028235e38"); - assert_eq!(to_string(f, f32::MAX, Minus, 16, false), "3.402823466385289e38"); - assert_eq!(to_string(f, f32::MAX, Minus, 32, false), "3.4028234663852885981170418348452e38"); - assert_eq!( - to_string(f, f32::MAX, Minus, 64, false), - "3.402823466385288598117041834845169254400000000000000000000000000e38" - ); - - let minf32 = ldexp_f32(1.0, -149); - assert_eq!(to_string(f, minf32, Minus, 1, false), "1e-45"); - assert_eq!(to_string(f, minf32, Minus, 2, false), "1.4e-45"); - assert_eq!(to_string(f, minf32, Minus, 4, false), "1.401e-45"); - assert_eq!(to_string(f, minf32, Minus, 8, false), "1.4012985e-45"); - assert_eq!(to_string(f, minf32, Minus, 16, false), "1.401298464324817e-45"); - assert_eq!(to_string(f, minf32, Minus, 32, false), "1.4012984643248170709237295832899e-45"); - assert_eq!( - to_string(f, minf32, Minus, 64, false), - "1.401298464324817070923729583289916131280261941876515771757068284e-45" - ); - assert_eq!( - to_string(f, minf32, Minus, 128, false), - "1.401298464324817070923729583289916131280261941876515771757068283\ - 8897910826858606014866381883621215820312500000000000000000000000e-45" - ); - - if cfg!(miri) { - // Miri is too slow - return; - } - - assert_eq!(to_string(f, f64::MAX, Minus, 1, false), "2e308"); - assert_eq!(to_string(f, f64::MAX, Minus, 2, false), "1.8e308"); - assert_eq!(to_string(f, f64::MAX, Minus, 4, false), "1.798e308"); - assert_eq!(to_string(f, f64::MAX, Minus, 8, false), "1.7976931e308"); - assert_eq!(to_string(f, f64::MAX, Minus, 16, false), "1.797693134862316e308"); - assert_eq!(to_string(f, f64::MAX, Minus, 32, false), "1.7976931348623157081452742373170e308"); - assert_eq!( - to_string(f, f64::MAX, Minus, 64, false), - "1.797693134862315708145274237317043567980705675258449965989174768e308" - ); - assert_eq!( - to_string(f, f64::MAX, Minus, 128, false), - "1.797693134862315708145274237317043567980705675258449965989174768\ - 0315726078002853876058955863276687817154045895351438246423432133e308" - ); - assert_eq!( - to_string(f, f64::MAX, Minus, 256, false), - "1.797693134862315708145274237317043567980705675258449965989174768\ - 0315726078002853876058955863276687817154045895351438246423432132\ - 6889464182768467546703537516986049910576551282076245490090389328\ - 9440758685084551339423045832369032229481658085593321233482747978e308" - ); - assert_eq!( - to_string(f, f64::MAX, Minus, 512, false), - "1.797693134862315708145274237317043567980705675258449965989174768\ - 0315726078002853876058955863276687817154045895351438246423432132\ - 6889464182768467546703537516986049910576551282076245490090389328\ - 9440758685084551339423045832369032229481658085593321233482747978\ - 2620414472316873817718091929988125040402618412485836800000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000e308" - ); - - // okay, this is becoming tough. fortunately for us, this is almost the worst case. - let minf64 = ldexp_f64(1.0, -1074); - assert_eq!(to_string(f, minf64, Minus, 1, false), "5e-324"); - assert_eq!(to_string(f, minf64, Minus, 2, false), "4.9e-324"); - assert_eq!(to_string(f, minf64, Minus, 4, false), "4.941e-324"); - assert_eq!(to_string(f, minf64, Minus, 8, false), "4.9406565e-324"); - assert_eq!(to_string(f, minf64, Minus, 16, false), "4.940656458412465e-324"); - assert_eq!(to_string(f, minf64, Minus, 32, false), "4.9406564584124654417656879286822e-324"); - assert_eq!( - to_string(f, minf64, Minus, 64, false), - "4.940656458412465441765687928682213723650598026143247644255856825e-324" - ); - assert_eq!( - to_string(f, minf64, Minus, 128, false), - "4.940656458412465441765687928682213723650598026143247644255856825\ - 0067550727020875186529983636163599237979656469544571773092665671e-324" - ); - assert_eq!( - to_string(f, minf64, Minus, 256, false), - "4.940656458412465441765687928682213723650598026143247644255856825\ - 0067550727020875186529983636163599237979656469544571773092665671\ - 0355939796398774796010781878126300713190311404527845817167848982\ - 1036887186360569987307230500063874091535649843873124733972731696e-324" - ); - assert_eq!( - to_string(f, minf64, Minus, 512, false), - "4.940656458412465441765687928682213723650598026143247644255856825\ - 0067550727020875186529983636163599237979656469544571773092665671\ - 0355939796398774796010781878126300713190311404527845817167848982\ - 1036887186360569987307230500063874091535649843873124733972731696\ - 1514003171538539807412623856559117102665855668676818703956031062\ - 4931945271591492455329305456544401127480129709999541931989409080\ - 4165633245247571478690147267801593552386115501348035264934720193\ - 7902681071074917033322268447533357208324319360923828934583680601e-324" - ); - assert_eq!( - to_string(f, minf64, Minus, 1024, false), - "4.940656458412465441765687928682213723650598026143247644255856825\ - 0067550727020875186529983636163599237979656469544571773092665671\ - 0355939796398774796010781878126300713190311404527845817167848982\ - 1036887186360569987307230500063874091535649843873124733972731696\ - 1514003171538539807412623856559117102665855668676818703956031062\ - 4931945271591492455329305456544401127480129709999541931989409080\ - 4165633245247571478690147267801593552386115501348035264934720193\ - 7902681071074917033322268447533357208324319360923828934583680601\ - 0601150616980975307834227731832924790498252473077637592724787465\ - 6084778203734469699533647017972677717585125660551199131504891101\ - 4510378627381672509558373897335989936648099411642057026370902792\ - 4276754456522908753868250641971826553344726562500000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000e-324" - ); - - // very large output - assert_eq!(to_string(f, 0.0, Minus, 80000, false), format!("0.{:0>79999}e0", "")); - assert_eq!(to_string(f, 1.0e1, Minus, 80000, false), format!("1.{:0>79999}e1", "")); - assert_eq!(to_string(f, 1.0e0, Minus, 80000, false), format!("1.{:0>79999}e0", "")); - assert_eq!( - to_string(f, 1.0e-1, Minus, 80000, false), - format!( - "1.000000000000000055511151231257827021181583404541015625{:0>79945}\ - e-1", - "" - ) - ); - assert_eq!( - to_string(f, 1.0e-20, Minus, 80000, false), - format!( - "9.999999999999999451532714542095716517295037027873924471077157760\ - 66783064379706047475337982177734375{:0>79901}e-21", - "" - ) - ); -} - -pub fn to_exact_fixed_str_test(mut f_: F) -where - F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), -{ - use core::num::flt2dec::Sign::*; - - fn to_string(f: &mut F, v: T, sign: Sign, frac_digits: usize) -> String - where - T: DecodableFloat, - F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), - { - to_string_with_parts(|buf, parts| { - to_exact_fixed_str(|d, b, l| f(d, b, l), v, sign, frac_digits, buf, parts) - }) - } - - let f = &mut f_; - - assert_eq!(to_string(f, 0.0, Minus, 0), "0"); - assert_eq!(to_string(f, 0.0, MinusPlus, 0), "+0"); - assert_eq!(to_string(f, -0.0, Minus, 0), "-0"); - assert_eq!(to_string(f, -0.0, MinusPlus, 0), "-0"); - assert_eq!(to_string(f, 0.0, Minus, 1), "0.0"); - assert_eq!(to_string(f, 0.0, MinusPlus, 1), "+0.0"); - assert_eq!(to_string(f, -0.0, Minus, 8), "-0.00000000"); - assert_eq!(to_string(f, -0.0, MinusPlus, 8), "-0.00000000"); - - assert_eq!(to_string(f, 1.0 / 0.0, Minus, 0), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, Minus, 1), "inf"); - assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, 64), "+inf"); - assert_eq!(to_string(f, 0.0 / 0.0, Minus, 0), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, Minus, 1), "NaN"); - assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 64), "NaN"); - assert_eq!(to_string(f, -1.0 / 0.0, Minus, 0), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, Minus, 1), "-inf"); - assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 64), "-inf"); - - assert_eq!(to_string(f, 3.14, Minus, 0), "3"); - assert_eq!(to_string(f, 3.14, Minus, 0), "3"); - assert_eq!(to_string(f, 3.14, MinusPlus, 0), "+3"); - assert_eq!(to_string(f, -3.14, Minus, 0), "-3"); - assert_eq!(to_string(f, -3.14, Minus, 0), "-3"); - assert_eq!(to_string(f, -3.14, MinusPlus, 0), "-3"); - assert_eq!(to_string(f, 3.14, Minus, 1), "3.1"); - assert_eq!(to_string(f, 3.14, Minus, 2), "3.14"); - assert_eq!(to_string(f, 3.14, MinusPlus, 4), "+3.1400"); - assert_eq!(to_string(f, -3.14, Minus, 8), "-3.14000000"); - assert_eq!(to_string(f, -3.14, Minus, 8), "-3.14000000"); - assert_eq!(to_string(f, -3.14, MinusPlus, 8), "-3.14000000"); - - assert_eq!(to_string(f, 0.195, Minus, 0), "0"); - assert_eq!(to_string(f, 0.195, MinusPlus, 0), "+0"); - assert_eq!(to_string(f, -0.195, Minus, 0), "-0"); - assert_eq!(to_string(f, -0.195, Minus, 0), "-0"); - assert_eq!(to_string(f, -0.195, MinusPlus, 0), "-0"); - assert_eq!(to_string(f, 0.195, Minus, 1), "0.2"); - assert_eq!(to_string(f, 0.195, Minus, 2), "0.20"); - assert_eq!(to_string(f, 0.195, MinusPlus, 4), "+0.1950"); - assert_eq!(to_string(f, -0.195, Minus, 5), "-0.19500"); - assert_eq!(to_string(f, -0.195, Minus, 6), "-0.195000"); - assert_eq!(to_string(f, -0.195, MinusPlus, 8), "-0.19500000"); - - assert_eq!(to_string(f, 999.5, Minus, 0), "1000"); - assert_eq!(to_string(f, 999.5, Minus, 1), "999.5"); - assert_eq!(to_string(f, 999.5, Minus, 2), "999.50"); - assert_eq!(to_string(f, 999.5, Minus, 3), "999.500"); - assert_eq!(to_string(f, 999.5, Minus, 30), "999.500000000000000000000000000000"); - - assert_eq!(to_string(f, 0.5, Minus, 0), "0"); - assert_eq!(to_string(f, 0.5, Minus, 1), "0.5"); - assert_eq!(to_string(f, 0.5, Minus, 2), "0.50"); - assert_eq!(to_string(f, 0.5, Minus, 3), "0.500"); - - assert_eq!(to_string(f, 0.95, Minus, 0), "1"); - assert_eq!(to_string(f, 0.95, Minus, 1), "0.9"); // because it really is less than 0.95 - assert_eq!(to_string(f, 0.95, Minus, 2), "0.95"); - assert_eq!(to_string(f, 0.95, Minus, 3), "0.950"); - assert_eq!(to_string(f, 0.95, Minus, 10), "0.9500000000"); - assert_eq!(to_string(f, 0.95, Minus, 30), "0.949999999999999955591079014994"); - - assert_eq!(to_string(f, 0.095, Minus, 0), "0"); - assert_eq!(to_string(f, 0.095, Minus, 1), "0.1"); - assert_eq!(to_string(f, 0.095, Minus, 2), "0.10"); - assert_eq!(to_string(f, 0.095, Minus, 3), "0.095"); - assert_eq!(to_string(f, 0.095, Minus, 4), "0.0950"); - assert_eq!(to_string(f, 0.095, Minus, 10), "0.0950000000"); - assert_eq!(to_string(f, 0.095, Minus, 30), "0.095000000000000001110223024625"); - - assert_eq!(to_string(f, 0.0095, Minus, 0), "0"); - assert_eq!(to_string(f, 0.0095, Minus, 1), "0.0"); - assert_eq!(to_string(f, 0.0095, Minus, 2), "0.01"); - assert_eq!(to_string(f, 0.0095, Minus, 3), "0.009"); // really is less than 0.0095 - assert_eq!(to_string(f, 0.0095, Minus, 4), "0.0095"); - assert_eq!(to_string(f, 0.0095, Minus, 5), "0.00950"); - assert_eq!(to_string(f, 0.0095, Minus, 10), "0.0095000000"); - assert_eq!(to_string(f, 0.0095, Minus, 30), "0.009499999999999999764077607267"); - - assert_eq!(to_string(f, 7.5e-11, Minus, 0), "0"); - assert_eq!(to_string(f, 7.5e-11, Minus, 3), "0.000"); - assert_eq!(to_string(f, 7.5e-11, Minus, 10), "0.0000000001"); - assert_eq!(to_string(f, 7.5e-11, Minus, 11), "0.00000000007"); // ditto - assert_eq!(to_string(f, 7.5e-11, Minus, 12), "0.000000000075"); - assert_eq!(to_string(f, 7.5e-11, Minus, 13), "0.0000000000750"); - assert_eq!(to_string(f, 7.5e-11, Minus, 20), "0.00000000007500000000"); - assert_eq!(to_string(f, 7.5e-11, Minus, 30), "0.000000000074999999999999999501"); - - assert_eq!(to_string(f, 1.0e25, Minus, 0), "10000000000000000905969664"); - assert_eq!(to_string(f, 1.0e25, Minus, 1), "10000000000000000905969664.0"); - assert_eq!(to_string(f, 1.0e25, Minus, 3), "10000000000000000905969664.000"); - - assert_eq!(to_string(f, 1.0e-6, Minus, 0), "0"); - assert_eq!(to_string(f, 1.0e-6, Minus, 3), "0.000"); - assert_eq!(to_string(f, 1.0e-6, Minus, 6), "0.000001"); - assert_eq!(to_string(f, 1.0e-6, Minus, 9), "0.000001000"); - assert_eq!(to_string(f, 1.0e-6, Minus, 12), "0.000001000000"); - assert_eq!(to_string(f, 1.0e-6, Minus, 22), "0.0000010000000000000000"); - assert_eq!(to_string(f, 1.0e-6, Minus, 23), "0.00000099999999999999995"); - assert_eq!(to_string(f, 1.0e-6, Minus, 24), "0.000000999999999999999955"); - assert_eq!(to_string(f, 1.0e-6, Minus, 25), "0.0000009999999999999999547"); - assert_eq!(to_string(f, 1.0e-6, Minus, 35), "0.00000099999999999999995474811182589"); - assert_eq!(to_string(f, 1.0e-6, Minus, 45), "0.000000999999999999999954748111825886258685614"); - assert_eq!( - to_string(f, 1.0e-6, Minus, 55), - "0.0000009999999999999999547481118258862586856139387236908" - ); - assert_eq!( - to_string(f, 1.0e-6, Minus, 65), - "0.00000099999999999999995474811182588625868561393872369080781936646" - ); - assert_eq!( - to_string(f, 1.0e-6, Minus, 75), - "0.000000999999999999999954748111825886258685613938723690807819366455078125000" - ); - - assert_eq!(to_string(f, f32::MAX, Minus, 0), "340282346638528859811704183484516925440"); - assert_eq!(to_string(f, f32::MAX, Minus, 1), "340282346638528859811704183484516925440.0"); - assert_eq!(to_string(f, f32::MAX, Minus, 2), "340282346638528859811704183484516925440.00"); - - if cfg!(miri) { - // Miri is too slow - return; - } - - let minf32 = ldexp_f32(1.0, -149); - assert_eq!(to_string(f, minf32, Minus, 0), "0"); - assert_eq!(to_string(f, minf32, Minus, 1), "0.0"); - assert_eq!(to_string(f, minf32, Minus, 2), "0.00"); - assert_eq!(to_string(f, minf32, Minus, 4), "0.0000"); - assert_eq!(to_string(f, minf32, Minus, 8), "0.00000000"); - assert_eq!(to_string(f, minf32, Minus, 16), "0.0000000000000000"); - assert_eq!(to_string(f, minf32, Minus, 32), "0.00000000000000000000000000000000"); - assert_eq!( - to_string(f, minf32, Minus, 64), - "0.0000000000000000000000000000000000000000000014012984643248170709" - ); - assert_eq!( - to_string(f, minf32, Minus, 128), - "0.0000000000000000000000000000000000000000000014012984643248170709\ - 2372958328991613128026194187651577175706828388979108268586060149" - ); - assert_eq!( - to_string(f, minf32, Minus, 256), - "0.0000000000000000000000000000000000000000000014012984643248170709\ - 2372958328991613128026194187651577175706828388979108268586060148\ - 6638188362121582031250000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000" - ); - - assert_eq!( - to_string(f, f64::MAX, Minus, 0), - "1797693134862315708145274237317043567980705675258449965989174768\ - 0315726078002853876058955863276687817154045895351438246423432132\ - 6889464182768467546703537516986049910576551282076245490090389328\ - 9440758685084551339423045832369032229481658085593321233482747978\ - 26204144723168738177180919299881250404026184124858368" - ); - assert_eq!( - to_string(f, f64::MAX, Minus, 10), - "1797693134862315708145274237317043567980705675258449965989174768\ - 0315726078002853876058955863276687817154045895351438246423432132\ - 6889464182768467546703537516986049910576551282076245490090389328\ - 9440758685084551339423045832369032229481658085593321233482747978\ - 26204144723168738177180919299881250404026184124858368.0000000000" - ); - - let minf64 = ldexp_f64(1.0, -1074); - assert_eq!(to_string(f, minf64, Minus, 0), "0"); - assert_eq!(to_string(f, minf64, Minus, 1), "0.0"); - assert_eq!(to_string(f, minf64, Minus, 10), "0.0000000000"); - assert_eq!( - to_string(f, minf64, Minus, 100), - "0.0000000000000000000000000000000000000000000000000000000000000000\ - 000000000000000000000000000000000000" - ); - assert_eq!( - to_string(f, minf64, Minus, 1000), - "0.0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0004940656458412465441765687928682213723650598026143247644255856\ - 8250067550727020875186529983636163599237979656469544571773092665\ - 6710355939796398774796010781878126300713190311404527845817167848\ - 9821036887186360569987307230500063874091535649843873124733972731\ - 6961514003171538539807412623856559117102665855668676818703956031\ - 0624931945271591492455329305456544401127480129709999541931989409\ - 0804165633245247571478690147267801593552386115501348035264934720\ - 1937902681071074917033322268447533357208324319360923828934583680\ - 6010601150616980975307834227731832924790498252473077637592724787\ - 4656084778203734469699533647017972677717585125660551199131504891\ - 1014510378627381672509558373897335989937" - ); - - // very large output - assert_eq!(to_string(f, 0.0, Minus, 80000), format!("0.{:0>80000}", "")); - assert_eq!(to_string(f, 1.0e1, Minus, 80000), format!("10.{:0>80000}", "")); - assert_eq!(to_string(f, 1.0e0, Minus, 80000), format!("1.{:0>80000}", "")); - assert_eq!( - to_string(f, 1.0e-1, Minus, 80000), - format!("0.1000000000000000055511151231257827021181583404541015625{:0>79945}", "") - ); - assert_eq!( - to_string(f, 1.0e-20, Minus, 80000), - format!( - "0.0000000000000000000099999999999999994515327145420957165172950370\ - 2787392447107715776066783064379706047475337982177734375{:0>79881}", - "" - ) - ); -} diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs deleted file mode 100644 index 510dd4967c961..0000000000000 --- a/library/core/tests/slice.rs +++ /dev/null @@ -1,2688 +0,0 @@ -use core::cell::Cell; -use core::cmp::Ordering; -use core::mem::MaybeUninit; -use core::num::NonZero; -use core::ops::{Range, RangeInclusive}; -use core::slice; - -#[test] -fn test_position() { - let b = [1, 2, 3, 5, 5]; - assert_eq!(b.iter().position(|&v| v == 9), None); - assert_eq!(b.iter().position(|&v| v == 5), Some(3)); - assert_eq!(b.iter().position(|&v| v == 3), Some(2)); - assert_eq!(b.iter().position(|&v| v == 0), None); -} - -#[test] -fn test_rposition() { - let b = [1, 2, 3, 5, 5]; - assert_eq!(b.iter().rposition(|&v| v == 9), None); - assert_eq!(b.iter().rposition(|&v| v == 5), Some(4)); - assert_eq!(b.iter().rposition(|&v| v == 3), Some(2)); - assert_eq!(b.iter().rposition(|&v| v == 0), None); -} - -#[test] -fn test_binary_search() { - let b: [i32; 0] = []; - assert_eq!(b.binary_search(&5), Err(0)); - - let b = [4]; - assert_eq!(b.binary_search(&3), Err(0)); - assert_eq!(b.binary_search(&4), Ok(0)); - assert_eq!(b.binary_search(&5), Err(1)); - - let b = [1, 2, 4, 6, 8, 9]; - assert_eq!(b.binary_search(&5), Err(3)); - assert_eq!(b.binary_search(&6), Ok(3)); - assert_eq!(b.binary_search(&7), Err(4)); - assert_eq!(b.binary_search(&8), Ok(4)); - - let b = [1, 2, 4, 5, 6, 8]; - assert_eq!(b.binary_search(&9), Err(6)); - - let b = [1, 2, 4, 6, 7, 8, 9]; - assert_eq!(b.binary_search(&6), Ok(3)); - assert_eq!(b.binary_search(&5), Err(3)); - assert_eq!(b.binary_search(&8), Ok(5)); - - let b = [1, 2, 4, 5, 6, 8, 9]; - assert_eq!(b.binary_search(&7), Err(5)); - assert_eq!(b.binary_search(&0), Err(0)); - - let b = [1, 3, 3, 3, 7]; - assert_eq!(b.binary_search(&0), Err(0)); - assert_eq!(b.binary_search(&1), Ok(0)); - assert_eq!(b.binary_search(&2), Err(1)); - assert!(match b.binary_search(&3) { - Ok(1..=3) => true, - _ => false, - }); - assert!(match b.binary_search(&3) { - Ok(1..=3) => true, - _ => false, - }); - assert_eq!(b.binary_search(&4), Err(4)); - assert_eq!(b.binary_search(&5), Err(4)); - assert_eq!(b.binary_search(&6), Err(4)); - assert_eq!(b.binary_search(&7), Ok(4)); - assert_eq!(b.binary_search(&8), Err(5)); - - let b = [(); usize::MAX]; - assert_eq!(b.binary_search(&()), Ok(usize::MAX - 1)); -} - -#[test] -fn test_binary_search_by_overflow() { - let b = [(); usize::MAX]; - assert_eq!(b.binary_search_by(|_| Ordering::Equal), Ok(usize::MAX - 1)); - assert_eq!(b.binary_search_by(|_| Ordering::Greater), Err(0)); - assert_eq!(b.binary_search_by(|_| Ordering::Less), Err(usize::MAX)); -} - -#[test] -// Test implementation specific behavior when finding equivalent elements. -// It is ok to break this test but when you do a crater run is highly advisable. -fn test_binary_search_implementation_details() { - let b = [1, 1, 2, 2, 3, 3, 3]; - assert_eq!(b.binary_search(&1), Ok(1)); - assert_eq!(b.binary_search(&2), Ok(3)); - assert_eq!(b.binary_search(&3), Ok(6)); - let b = [1, 1, 1, 1, 1, 3, 3, 3, 3]; - assert_eq!(b.binary_search(&1), Ok(4)); - assert_eq!(b.binary_search(&3), Ok(8)); - let b = [1, 1, 1, 1, 3, 3, 3, 3, 3]; - assert_eq!(b.binary_search(&1), Ok(3)); - assert_eq!(b.binary_search(&3), Ok(8)); -} - -#[test] -fn test_partition_point() { - let b: [i32; 0] = []; - assert_eq!(b.partition_point(|&x| x < 5), 0); - - let b = [4]; - assert_eq!(b.partition_point(|&x| x < 3), 0); - assert_eq!(b.partition_point(|&x| x < 4), 0); - assert_eq!(b.partition_point(|&x| x < 5), 1); - - let b = [1, 2, 4, 6, 8, 9]; - assert_eq!(b.partition_point(|&x| x < 5), 3); - assert_eq!(b.partition_point(|&x| x < 6), 3); - assert_eq!(b.partition_point(|&x| x < 7), 4); - assert_eq!(b.partition_point(|&x| x < 8), 4); - - let b = [1, 2, 4, 5, 6, 8]; - assert_eq!(b.partition_point(|&x| x < 9), 6); - - let b = [1, 2, 4, 6, 7, 8, 9]; - assert_eq!(b.partition_point(|&x| x < 6), 3); - assert_eq!(b.partition_point(|&x| x < 5), 3); - assert_eq!(b.partition_point(|&x| x < 8), 5); - - let b = [1, 2, 4, 5, 6, 8, 9]; - assert_eq!(b.partition_point(|&x| x < 7), 5); - assert_eq!(b.partition_point(|&x| x < 0), 0); - - let b = [1, 3, 3, 3, 7]; - assert_eq!(b.partition_point(|&x| x < 0), 0); - assert_eq!(b.partition_point(|&x| x < 1), 0); - assert_eq!(b.partition_point(|&x| x < 2), 1); - assert_eq!(b.partition_point(|&x| x < 3), 1); - assert_eq!(b.partition_point(|&x| x < 4), 4); - assert_eq!(b.partition_point(|&x| x < 5), 4); - assert_eq!(b.partition_point(|&x| x < 6), 4); - assert_eq!(b.partition_point(|&x| x < 7), 4); - assert_eq!(b.partition_point(|&x| x < 8), 5); -} - -#[test] -fn test_iterator_advance_by() { - let v = &[0, 1, 2, 3, 4]; - - for i in 0..=v.len() { - let mut iter = v.iter(); - assert_eq!(iter.advance_by(i), Ok(())); - assert_eq!(iter.as_slice(), &v[i..]); - } - - let mut iter = v.iter(); - assert_eq!(iter.advance_by(v.len() + 1), Err(NonZero::new(1).unwrap())); - assert_eq!(iter.as_slice(), &[]); - - let mut iter = v.iter(); - assert_eq!(iter.advance_by(3), Ok(())); - assert_eq!(iter.as_slice(), &v[3..]); - assert_eq!(iter.advance_by(2), Ok(())); - assert_eq!(iter.as_slice(), &[]); - assert_eq!(iter.advance_by(0), Ok(())); -} - -#[test] -fn test_iterator_advance_back_by() { - let v = &[0, 1, 2, 3, 4]; - - for i in 0..=v.len() { - let mut iter = v.iter(); - assert_eq!(iter.advance_back_by(i), Ok(())); - assert_eq!(iter.as_slice(), &v[..v.len() - i]); - } - - let mut iter = v.iter(); - assert_eq!(iter.advance_back_by(v.len() + 1), Err(NonZero::new(1).unwrap())); - assert_eq!(iter.as_slice(), &[]); - - let mut iter = v.iter(); - assert_eq!(iter.advance_back_by(3), Ok(())); - assert_eq!(iter.as_slice(), &v[..v.len() - 3]); - assert_eq!(iter.advance_back_by(2), Ok(())); - assert_eq!(iter.as_slice(), &[]); - assert_eq!(iter.advance_back_by(0), Ok(())); -} - -#[test] -fn test_iterator_nth() { - let v: &[_] = &[0, 1, 2, 3, 4]; - for i in 0..v.len() { - assert_eq!(v.iter().nth(i).unwrap(), &v[i]); - } - assert_eq!(v.iter().nth(v.len()), None); - - let mut iter = v.iter(); - assert_eq!(iter.nth(2).unwrap(), &v[2]); - assert_eq!(iter.nth(1).unwrap(), &v[4]); -} - -#[test] -fn test_iterator_nth_back() { - let v: &[_] = &[0, 1, 2, 3, 4]; - for i in 0..v.len() { - assert_eq!(v.iter().nth_back(i).unwrap(), &v[v.len() - i - 1]); - } - assert_eq!(v.iter().nth_back(v.len()), None); - - let mut iter = v.iter(); - assert_eq!(iter.nth_back(2).unwrap(), &v[2]); - assert_eq!(iter.nth_back(1).unwrap(), &v[0]); -} - -#[test] -fn test_iterator_last() { - let v: &[_] = &[0, 1, 2, 3, 4]; - assert_eq!(v.iter().last().unwrap(), &4); - assert_eq!(v[..1].iter().last().unwrap(), &0); -} - -#[test] -fn test_iterator_count() { - let v: &[_] = &[0, 1, 2, 3, 4]; - assert_eq!(v.iter().count(), 5); - - let mut iter2 = v.iter(); - iter2.next(); - iter2.next(); - assert_eq!(iter2.count(), 3); -} - -#[test] -fn test_chunks_count() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.chunks(3); - assert_eq!(c.count(), 2); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.chunks(2); - assert_eq!(c2.count(), 3); - - let v3: &[i32] = &[]; - let c3 = v3.chunks(2); - assert_eq!(c3.count(), 0); -} - -#[test] -fn test_chunks_nth() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.chunks(2); - assert_eq!(c.nth(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[4, 5]); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let mut c2 = v2.chunks(3); - assert_eq!(c2.nth(1).unwrap(), &[3, 4]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_chunks_next() { - let v = [0, 1, 2, 3, 4, 5]; - let mut c = v.chunks(2); - assert_eq!(c.next().unwrap(), &[0, 1]); - assert_eq!(c.next().unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[4, 5]); - assert_eq!(c.next(), None); - - let v = [0, 1, 2, 3, 4, 5, 6, 7]; - let mut c = v.chunks(3); - assert_eq!(c.next().unwrap(), &[0, 1, 2]); - assert_eq!(c.next().unwrap(), &[3, 4, 5]); - assert_eq!(c.next().unwrap(), &[6, 7]); - assert_eq!(c.next(), None); -} - -#[test] -fn test_chunks_next_back() { - let v = [0, 1, 2, 3, 4, 5]; - let mut c = v.chunks(2); - assert_eq!(c.next_back().unwrap(), &[4, 5]); - assert_eq!(c.next_back().unwrap(), &[2, 3]); - assert_eq!(c.next_back().unwrap(), &[0, 1]); - assert_eq!(c.next_back(), None); - - let v = [0, 1, 2, 3, 4, 5, 6, 7]; - let mut c = v.chunks(3); - assert_eq!(c.next_back().unwrap(), &[6, 7]); - assert_eq!(c.next_back().unwrap(), &[3, 4, 5]); - assert_eq!(c.next_back().unwrap(), &[0, 1, 2]); - assert_eq!(c.next_back(), None); -} - -#[test] -fn test_chunks_nth_back() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.chunks(2); - assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[0, 1]); - assert_eq!(c.next(), None); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let mut c2 = v2.chunks(3); - assert_eq!(c2.nth_back(1).unwrap(), &[0, 1, 2]); - assert_eq!(c2.next(), None); - assert_eq!(c2.next_back(), None); - - let v3: &[i32] = &[0, 1, 2, 3, 4]; - let mut c3 = v3.chunks(10); - assert_eq!(c3.nth_back(0).unwrap(), &[0, 1, 2, 3, 4]); - assert_eq!(c3.next(), None); - - let v4: &[i32] = &[0, 1, 2]; - let mut c4 = v4.chunks(10); - assert_eq!(c4.nth_back(1_000_000_000usize), None); -} - -#[test] -fn test_chunks_last() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.chunks(2); - assert_eq!(c.last().unwrap()[1], 5); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.chunks(2); - assert_eq!(c2.last().unwrap()[0], 4); -} - -#[test] -fn test_chunks_zip() { - let v1: &[i32] = &[0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - let res = v1 - .chunks(2) - .zip(v2.chunks(2)) - .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) - .collect::>(); - assert_eq!(res, vec![14, 22, 14]); -} - -#[test] -fn test_chunks_mut_count() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let c = v.chunks_mut(3); - assert_eq!(c.count(), 2); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c2 = v2.chunks_mut(2); - assert_eq!(c2.count(), 3); - - let v3: &mut [i32] = &mut []; - let c3 = v3.chunks_mut(2); - assert_eq!(c3.count(), 0); -} - -#[test] -fn test_chunks_mut_nth() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let mut c = v.chunks_mut(2); - assert_eq!(c.nth(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[4, 5]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let mut c2 = v2.chunks_mut(3); - assert_eq!(c2.nth(1).unwrap(), &[3, 4]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_chunks_mut_nth_back() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let mut c = v.chunks_mut(2); - assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[0, 1]); - - let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let mut c1 = v1.chunks_mut(3); - assert_eq!(c1.nth_back(1).unwrap(), &[0, 1, 2]); - assert_eq!(c1.next(), None); - - let v3: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let mut c3 = v3.chunks_mut(10); - assert_eq!(c3.nth_back(0).unwrap(), &[0, 1, 2, 3, 4]); - assert_eq!(c3.next(), None); - - let v4: &mut [i32] = &mut [0, 1, 2]; - let mut c4 = v4.chunks_mut(10); - assert_eq!(c4.nth_back(1_000_000_000usize), None); -} - -#[test] -fn test_chunks_mut_last() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let c = v.chunks_mut(2); - assert_eq!(c.last().unwrap(), &[4, 5]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c2 = v2.chunks_mut(2); - assert_eq!(c2.last().unwrap(), &[4]); -} - -#[test] -fn test_chunks_mut_zip() { - let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - for (a, b) in v1.chunks_mut(2).zip(v2.chunks(2)) { - let sum = b.iter().sum::(); - for v in a { - *v += sum; - } - } - assert_eq!(v1, [13, 14, 19, 20, 14]); -} - -#[test] -fn test_chunks_mut_zip_aliasing() { - let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - let mut it = v1.chunks_mut(2).zip(v2.chunks(2)); - let first = it.next().unwrap(); - let _ = it.next().unwrap(); - assert_eq!(first, (&mut [0, 1][..], &[6, 7][..])); -} - -#[test] -fn test_chunks_exact_mut_zip_aliasing() { - let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - let mut it = v1.chunks_exact_mut(2).zip(v2.chunks(2)); - let first = it.next().unwrap(); - let _ = it.next().unwrap(); - assert_eq!(first, (&mut [0, 1][..], &[6, 7][..])); -} - -#[test] -fn test_rchunks_mut_zip_aliasing() { - let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - let mut it = v1.rchunks_mut(2).zip(v2.chunks(2)); - let first = it.next().unwrap(); - let _ = it.next().unwrap(); - assert_eq!(first, (&mut [3, 4][..], &[6, 7][..])); -} - -#[test] -fn test_rchunks_exact_mut_zip_aliasing() { - let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - let mut it = v1.rchunks_exact_mut(2).zip(v2.chunks(2)); - let first = it.next().unwrap(); - let _ = it.next().unwrap(); - assert_eq!(first, (&mut [3, 4][..], &[6, 7][..])); -} - -#[test] -fn test_chunks_exact_count() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.chunks_exact(3); - assert_eq!(c.count(), 2); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.chunks_exact(2); - assert_eq!(c2.count(), 2); - - let v3: &[i32] = &[]; - let c3 = v3.chunks_exact(2); - assert_eq!(c3.count(), 0); -} - -#[test] -fn test_chunks_exact_nth() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.chunks_exact(2); - assert_eq!(c.nth(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[4, 5]); - - let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; - let mut c2 = v2.chunks_exact(3); - assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_chunks_exact_nth_back() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.chunks_exact(2); - assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[0, 1]); - assert_eq!(c.next(), None); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let mut c2 = v2.chunks_exact(3); - assert_eq!(c2.nth_back(0).unwrap(), &[0, 1, 2]); - assert_eq!(c2.next(), None); - assert_eq!(c2.next_back(), None); - - let v3: &[i32] = &[0, 1, 2, 3, 4]; - let mut c3 = v3.chunks_exact(10); - assert_eq!(c3.nth_back(0), None); -} - -#[test] -fn test_chunks_exact_last() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.chunks_exact(2); - assert_eq!(c.last().unwrap(), &[4, 5]); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.chunks_exact(2); - assert_eq!(c2.last().unwrap(), &[2, 3]); -} - -#[test] -fn test_chunks_exact_remainder() { - let v: &[i32] = &[0, 1, 2, 3, 4]; - let c = v.chunks_exact(2); - assert_eq!(c.remainder(), &[4]); -} - -#[test] -fn test_chunks_exact_zip() { - let v1: &[i32] = &[0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - let res = v1 - .chunks_exact(2) - .zip(v2.chunks_exact(2)) - .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) - .collect::>(); - assert_eq!(res, vec![14, 22]); -} - -#[test] -fn test_chunks_exact_mut_count() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let c = v.chunks_exact_mut(3); - assert_eq!(c.count(), 2); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c2 = v2.chunks_exact_mut(2); - assert_eq!(c2.count(), 2); - - let v3: &mut [i32] = &mut []; - let c3 = v3.chunks_exact_mut(2); - assert_eq!(c3.count(), 0); -} - -#[test] -fn test_chunks_exact_mut_nth() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let mut c = v.chunks_exact_mut(2); - assert_eq!(c.nth(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[4, 5]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; - let mut c2 = v2.chunks_exact_mut(3); - assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_chunks_exact_mut_nth_back() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let mut c = v.chunks_exact_mut(2); - assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[0, 1]); - assert_eq!(c.next(), None); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let mut c2 = v2.chunks_exact_mut(3); - assert_eq!(c2.nth_back(0).unwrap(), &[0, 1, 2]); - assert_eq!(c2.next(), None); - assert_eq!(c2.next_back(), None); - - let v3: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let mut c3 = v3.chunks_exact_mut(10); - assert_eq!(c3.nth_back(0), None); -} - -#[test] -fn test_chunks_exact_mut_last() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let c = v.chunks_exact_mut(2); - assert_eq!(c.last().unwrap(), &[4, 5]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c2 = v2.chunks_exact_mut(2); - assert_eq!(c2.last().unwrap(), &[2, 3]); -} - -#[test] -fn test_chunks_exact_mut_remainder() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c = v.chunks_exact_mut(2); - assert_eq!(c.into_remainder(), &[4]); -} - -#[test] -fn test_chunks_exact_mut_zip() { - let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - for (a, b) in v1.chunks_exact_mut(2).zip(v2.chunks_exact(2)) { - let sum = b.iter().sum::(); - for v in a { - *v += sum; - } - } - assert_eq!(v1, [13, 14, 19, 20, 4]); -} - -#[test] -fn test_array_chunks_infer() { - let v: &[i32] = &[0, 1, 2, 3, 4, -4]; - let c = v.array_chunks(); - for &[a, b, c] in c { - assert_eq!(a + b + c, 3); - } - - let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; - let total = v2.array_chunks().map(|&[a, b]| a * b).sum::(); - assert_eq!(total, 2 * 3 + 4 * 5); -} - -#[test] -fn test_array_chunks_count() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.array_chunks::<3>(); - assert_eq!(c.count(), 2); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.array_chunks::<2>(); - assert_eq!(c2.count(), 2); - - let v3: &[i32] = &[]; - let c3 = v3.array_chunks::<2>(); - assert_eq!(c3.count(), 0); -} - -#[test] -fn test_array_chunks_nth() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.array_chunks::<2>(); - assert_eq!(c.nth(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[4, 5]); - - let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; - let mut c2 = v2.array_chunks::<3>(); - assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_array_chunks_nth_back() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.array_chunks::<2>(); - assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[0, 1]); - assert_eq!(c.next(), None); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let mut c2 = v2.array_chunks::<3>(); - assert_eq!(c2.nth_back(0).unwrap(), &[0, 1, 2]); - assert_eq!(c2.next(), None); - assert_eq!(c2.next_back(), None); - - let v3: &[i32] = &[0, 1, 2, 3, 4]; - let mut c3 = v3.array_chunks::<10>(); - assert_eq!(c3.nth_back(0), None); -} - -#[test] -fn test_array_chunks_last() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.array_chunks::<2>(); - assert_eq!(c.last().unwrap(), &[4, 5]); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.array_chunks::<2>(); - assert_eq!(c2.last().unwrap(), &[2, 3]); -} - -#[test] -fn test_array_chunks_remainder() { - let v: &[i32] = &[0, 1, 2, 3, 4]; - let c = v.array_chunks::<2>(); - assert_eq!(c.remainder(), &[4]); -} - -#[test] -fn test_array_chunks_zip() { - let v1: &[i32] = &[0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - let res = v1 - .array_chunks::<2>() - .zip(v2.array_chunks::<2>()) - .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) - .collect::>(); - assert_eq!(res, vec![14, 22]); -} - -#[test] -fn test_array_chunks_mut_infer() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; - for a in v.array_chunks_mut() { - let sum = a.iter().sum::(); - *a = [sum; 3]; - } - assert_eq!(v, &[3, 3, 3, 12, 12, 12, 6]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; - v2.array_chunks_mut().for_each(|[a, b]| core::mem::swap(a, b)); - assert_eq!(v2, &[1, 0, 3, 2, 5, 4, 6]); -} - -#[test] -fn test_array_chunks_mut_count() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let c = v.array_chunks_mut::<3>(); - assert_eq!(c.count(), 2); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c2 = v2.array_chunks_mut::<2>(); - assert_eq!(c2.count(), 2); - - let v3: &mut [i32] = &mut []; - let c3 = v3.array_chunks_mut::<2>(); - assert_eq!(c3.count(), 0); -} - -#[test] -fn test_array_chunks_mut_nth() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let mut c = v.array_chunks_mut::<2>(); - assert_eq!(c.nth(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[4, 5]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; - let mut c2 = v2.array_chunks_mut::<3>(); - assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_array_chunks_mut_nth_back() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let mut c = v.array_chunks_mut::<2>(); - assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[0, 1]); - assert_eq!(c.next(), None); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let mut c2 = v2.array_chunks_mut::<3>(); - assert_eq!(c2.nth_back(0).unwrap(), &[0, 1, 2]); - assert_eq!(c2.next(), None); - assert_eq!(c2.next_back(), None); - - let v3: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let mut c3 = v3.array_chunks_mut::<10>(); - assert_eq!(c3.nth_back(0), None); -} - -#[test] -fn test_array_chunks_mut_last() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let c = v.array_chunks_mut::<2>(); - assert_eq!(c.last().unwrap(), &[4, 5]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c2 = v2.array_chunks_mut::<2>(); - assert_eq!(c2.last().unwrap(), &[2, 3]); -} - -#[test] -fn test_array_chunks_mut_remainder() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c = v.array_chunks_mut::<2>(); - assert_eq!(c.into_remainder(), &[4]); -} - -#[test] -fn test_array_chunks_mut_zip() { - let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - for (a, b) in v1.array_chunks_mut::<2>().zip(v2.array_chunks::<2>()) { - let sum = b.iter().sum::(); - for v in a { - *v += sum; - } - } - assert_eq!(v1, [13, 14, 19, 20, 4]); -} - -#[test] -fn test_array_windows_infer() { - let v: &[i32] = &[0, 1, 0, 1]; - assert_eq!(v.array_windows::<2>().count(), 3); - let c = v.array_windows(); - for &[a, b] in c { - assert_eq!(a + b, 1); - } - - let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; - let total = v2.array_windows().map(|&[a, b, c]| a + b + c).sum::(); - assert_eq!(total, 3 + 6 + 9 + 12 + 15); -} - -#[test] -fn test_array_windows_count() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.array_windows::<3>(); - assert_eq!(c.count(), 4); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.array_windows::<6>(); - assert_eq!(c2.count(), 0); - - let v3: &[i32] = &[]; - let c3 = v3.array_windows::<2>(); - assert_eq!(c3.count(), 0); - - let v4: &[()] = &[(); usize::MAX]; - let c4 = v4.array_windows::<1>(); - assert_eq!(c4.count(), usize::MAX); -} - -#[test] -fn test_array_windows_nth() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let snd = v.array_windows::<4>().nth(1); - assert_eq!(snd, Some(&[1, 2, 3, 4])); - let mut arr_windows = v.array_windows::<2>(); - assert_ne!(arr_windows.nth(0), arr_windows.nth(0)); - let last = v.array_windows::<3>().last(); - assert_eq!(last, Some(&[3, 4, 5])); -} - -#[test] -fn test_array_windows_nth_back() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let snd = v.array_windows::<4>().nth_back(1); - assert_eq!(snd, Some(&[1, 2, 3, 4])); - let mut arr_windows = v.array_windows::<2>(); - assert_ne!(arr_windows.nth_back(0), arr_windows.nth_back(0)); -} - -#[test] -fn test_rchunks_count() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.rchunks(3); - assert_eq!(c.count(), 2); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.rchunks(2); - assert_eq!(c2.count(), 3); - - let v3: &[i32] = &[]; - let c3 = v3.rchunks(2); - assert_eq!(c3.count(), 0); -} - -#[test] -fn test_rchunks_nth() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.rchunks(2); - assert_eq!(c.nth(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[0, 1]); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let mut c2 = v2.rchunks(3); - assert_eq!(c2.nth(1).unwrap(), &[0, 1]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_rchunks_nth_back() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.rchunks(2); - assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); - assert_eq!(c.next_back().unwrap(), &[4, 5]); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let mut c2 = v2.rchunks(3); - assert_eq!(c2.nth_back(1).unwrap(), &[2, 3, 4]); - assert_eq!(c2.next_back(), None); -} - -#[test] -fn test_rchunks_next() { - let v = [0, 1, 2, 3, 4, 5]; - let mut c = v.rchunks(2); - assert_eq!(c.next().unwrap(), &[4, 5]); - assert_eq!(c.next().unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[0, 1]); - assert_eq!(c.next(), None); - - let v = [0, 1, 2, 3, 4, 5, 6, 7]; - let mut c = v.rchunks(3); - assert_eq!(c.next().unwrap(), &[5, 6, 7]); - assert_eq!(c.next().unwrap(), &[2, 3, 4]); - assert_eq!(c.next().unwrap(), &[0, 1]); - assert_eq!(c.next(), None); -} - -#[test] -fn test_rchunks_next_back() { - let v = [0, 1, 2, 3, 4, 5]; - let mut c = v.rchunks(2); - assert_eq!(c.next_back().unwrap(), &[0, 1]); - assert_eq!(c.next_back().unwrap(), &[2, 3]); - assert_eq!(c.next_back().unwrap(), &[4, 5]); - assert_eq!(c.next_back(), None); - - let v = [0, 1, 2, 3, 4, 5, 6, 7]; - let mut c = v.rchunks(3); - assert_eq!(c.next_back().unwrap(), &[0, 1]); - assert_eq!(c.next_back().unwrap(), &[2, 3, 4]); - assert_eq!(c.next_back().unwrap(), &[5, 6, 7]); - assert_eq!(c.next_back(), None); -} - -#[test] -fn test_rchunks_last() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.rchunks(2); - assert_eq!(c.last().unwrap()[1], 1); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.rchunks(2); - assert_eq!(c2.last().unwrap()[0], 0); -} - -#[test] -fn test_rchunks_zip() { - let v1: &[i32] = &[0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - let res = v1 - .rchunks(2) - .zip(v2.rchunks(2)) - .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) - .collect::>(); - assert_eq!(res, vec![26, 18, 6]); -} - -#[test] -fn test_rchunks_mut_count() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let c = v.rchunks_mut(3); - assert_eq!(c.count(), 2); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c2 = v2.rchunks_mut(2); - assert_eq!(c2.count(), 3); - - let v3: &mut [i32] = &mut []; - let c3 = v3.rchunks_mut(2); - assert_eq!(c3.count(), 0); -} - -#[test] -fn test_rchunks_mut_nth() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let mut c = v.rchunks_mut(2); - assert_eq!(c.nth(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[0, 1]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let mut c2 = v2.rchunks_mut(3); - assert_eq!(c2.nth(1).unwrap(), &[0, 1]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_rchunks_mut_nth_back() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let mut c = v.rchunks_mut(2); - assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); - assert_eq!(c.next_back().unwrap(), &[4, 5]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let mut c2 = v2.rchunks_mut(3); - assert_eq!(c2.nth_back(1).unwrap(), &[2, 3, 4]); - assert_eq!(c2.next_back(), None); -} - -#[test] -fn test_rchunks_mut_next() { - let mut v = [0, 1, 2, 3, 4, 5]; - let mut c = v.rchunks_mut(2); - assert_eq!(c.next().unwrap(), &mut [4, 5]); - assert_eq!(c.next().unwrap(), &mut [2, 3]); - assert_eq!(c.next().unwrap(), &mut [0, 1]); - assert_eq!(c.next(), None); - - let mut v = [0, 1, 2, 3, 4, 5, 6, 7]; - let mut c = v.rchunks_mut(3); - assert_eq!(c.next().unwrap(), &mut [5, 6, 7]); - assert_eq!(c.next().unwrap(), &mut [2, 3, 4]); - assert_eq!(c.next().unwrap(), &mut [0, 1]); - assert_eq!(c.next(), None); -} - -#[test] -fn test_rchunks_mut_next_back() { - let mut v = [0, 1, 2, 3, 4, 5]; - let mut c = v.rchunks_mut(2); - assert_eq!(c.next_back().unwrap(), &mut [0, 1]); - assert_eq!(c.next_back().unwrap(), &mut [2, 3]); - assert_eq!(c.next_back().unwrap(), &mut [4, 5]); - assert_eq!(c.next_back(), None); - - let mut v = [0, 1, 2, 3, 4, 5, 6, 7]; - let mut c = v.rchunks_mut(3); - assert_eq!(c.next_back().unwrap(), &mut [0, 1]); - assert_eq!(c.next_back().unwrap(), &mut [2, 3, 4]); - assert_eq!(c.next_back().unwrap(), &mut [5, 6, 7]); - assert_eq!(c.next_back(), None); -} - -#[test] -fn test_rchunks_mut_last() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let c = v.rchunks_mut(2); - assert_eq!(c.last().unwrap(), &[0, 1]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c2 = v2.rchunks_mut(2); - assert_eq!(c2.last().unwrap(), &[0]); -} - -#[test] -fn test_rchunks_mut_zip() { - let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - for (a, b) in v1.rchunks_mut(2).zip(v2.rchunks(2)) { - let sum = b.iter().sum::(); - for v in a { - *v += sum; - } - } - assert_eq!(v1, [6, 16, 17, 22, 23]); -} - -#[test] -fn test_rchunks_exact_count() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.rchunks_exact(3); - assert_eq!(c.count(), 2); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.rchunks_exact(2); - assert_eq!(c2.count(), 2); - - let v3: &[i32] = &[]; - let c3 = v3.rchunks_exact(2); - assert_eq!(c3.count(), 0); -} - -#[test] -fn test_rchunks_exact_nth() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.rchunks_exact(2); - assert_eq!(c.nth(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[0, 1]); - - let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; - let mut c2 = v2.rchunks_exact(3); - assert_eq!(c2.nth(1).unwrap(), &[1, 2, 3]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_rchunks_exact_nth_back() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.rchunks_exact(2); - assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); - assert_eq!(c.next_back().unwrap(), &[4, 5]); - - let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; - let mut c2 = v2.rchunks_exact(3); - assert_eq!(c2.nth_back(1).unwrap(), &[4, 5, 6]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_rchunks_exact_last() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.rchunks_exact(2); - assert_eq!(c.last().unwrap(), &[0, 1]); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.rchunks_exact(2); - assert_eq!(c2.last().unwrap(), &[1, 2]); -} - -#[test] -fn test_rchunks_exact_remainder() { - let v: &[i32] = &[0, 1, 2, 3, 4]; - let c = v.rchunks_exact(2); - assert_eq!(c.remainder(), &[0]); -} - -#[test] -fn test_rchunks_exact_zip() { - let v1: &[i32] = &[0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - let res = v1 - .rchunks_exact(2) - .zip(v2.rchunks_exact(2)) - .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) - .collect::>(); - assert_eq!(res, vec![26, 18]); -} - -#[test] -fn test_rchunks_exact_mut_count() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let c = v.rchunks_exact_mut(3); - assert_eq!(c.count(), 2); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c2 = v2.rchunks_exact_mut(2); - assert_eq!(c2.count(), 2); - - let v3: &mut [i32] = &mut []; - let c3 = v3.rchunks_exact_mut(2); - assert_eq!(c3.count(), 0); -} - -#[test] -fn test_rchunks_exact_mut_nth() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let mut c = v.rchunks_exact_mut(2); - assert_eq!(c.nth(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[0, 1]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; - let mut c2 = v2.rchunks_exact_mut(3); - assert_eq!(c2.nth(1).unwrap(), &[1, 2, 3]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_rchunks_exact_mut_nth_back() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let mut c = v.rchunks_exact_mut(2); - assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); - assert_eq!(c.next_back().unwrap(), &[4, 5]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; - let mut c2 = v2.rchunks_exact_mut(3); - assert_eq!(c2.nth_back(1).unwrap(), &[4, 5, 6]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_rchunks_exact_mut_last() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let c = v.rchunks_exact_mut(2); - assert_eq!(c.last().unwrap(), &[0, 1]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c2 = v2.rchunks_exact_mut(2); - assert_eq!(c2.last().unwrap(), &[1, 2]); -} - -#[test] -fn test_rchunks_exact_mut_remainder() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c = v.rchunks_exact_mut(2); - assert_eq!(c.into_remainder(), &[0]); -} - -#[test] -fn test_rchunks_exact_mut_zip() { - let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - for (a, b) in v1.rchunks_exact_mut(2).zip(v2.rchunks_exact(2)) { - let sum = b.iter().sum::(); - for v in a { - *v += sum; - } - } - assert_eq!(v1, [0, 16, 17, 22, 23]); -} - -#[test] -fn chunks_mut_are_send_and_sync() { - use std::cell::Cell; - use std::slice::{ChunksExactMut, ChunksMut, RChunksExactMut, RChunksMut}; - use std::sync::MutexGuard; - - fn assert_send_and_sync() - where - ChunksMut<'static, Cell>: Send, - ChunksMut<'static, MutexGuard<'static, u32>>: Sync, - ChunksExactMut<'static, Cell>: Send, - ChunksExactMut<'static, MutexGuard<'static, u32>>: Sync, - RChunksMut<'static, Cell>: Send, - RChunksMut<'static, MutexGuard<'static, u32>>: Sync, - RChunksExactMut<'static, Cell>: Send, - RChunksExactMut<'static, MutexGuard<'static, u32>>: Sync, - { - } - - assert_send_and_sync(); -} - -#[test] -fn test_windows_count() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.windows(3); - assert_eq!(c.count(), 4); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.windows(6); - assert_eq!(c2.count(), 0); - - let v3: &[i32] = &[]; - let c3 = v3.windows(2); - assert_eq!(c3.count(), 0); - - let v4 = &[(); usize::MAX]; - let c4 = v4.windows(1); - assert_eq!(c4.count(), usize::MAX); -} - -#[test] -fn test_windows_nth() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.windows(2); - assert_eq!(c.nth(2).unwrap()[1], 3); - assert_eq!(c.next().unwrap()[0], 3); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let mut c2 = v2.windows(4); - assert_eq!(c2.nth(1).unwrap()[1], 2); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_windows_nth_back() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.windows(2); - assert_eq!(c.nth_back(2).unwrap()[0], 2); - assert_eq!(c.next_back().unwrap()[1], 2); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let mut c2 = v2.windows(4); - assert_eq!(c2.nth_back(1).unwrap()[1], 1); - assert_eq!(c2.next_back(), None); -} - -#[test] -fn test_windows_last() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.windows(2); - assert_eq!(c.last().unwrap()[1], 5); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.windows(2); - assert_eq!(c2.last().unwrap()[0], 3); -} - -#[test] -fn test_windows_zip() { - let v1: &[i32] = &[0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - let res = v1 - .windows(2) - .zip(v2.windows(2)) - .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) - .collect::>(); - - assert_eq!(res, [14, 18, 22, 26]); -} - -#[test] -fn test_iter_ref_consistency() { - use std::fmt::Debug; - - fn test(x: T) { - let v: &[T] = &[x, x, x]; - let v_ptrs: [*const T; 3] = match v { - [ref v1, ref v2, ref v3] => [v1 as *const _, v2 as *const _, v3 as *const _], - _ => unreachable!(), - }; - let len = v.len(); - - // nth(i) - for i in 0..len { - assert_eq!(&v[i] as *const _, v_ptrs[i]); // check the v_ptrs array, just to be sure - let nth = v.iter().nth(i).unwrap(); - assert_eq!(nth as *const _, v_ptrs[i]); - } - assert_eq!(v.iter().nth(len), None, "nth(len) should return None"); - - // stepping through with nth(0) - { - let mut it = v.iter(); - for i in 0..len { - let next = it.nth(0).unwrap(); - assert_eq!(next as *const _, v_ptrs[i]); - } - assert_eq!(it.nth(0), None); - } - - // next() - { - let mut it = v.iter(); - for i in 0..len { - let remaining = len - i; - assert_eq!(it.size_hint(), (remaining, Some(remaining))); - - let next = it.next().unwrap(); - assert_eq!(next as *const _, v_ptrs[i]); - } - assert_eq!(it.size_hint(), (0, Some(0))); - assert_eq!(it.next(), None, "The final call to next() should return None"); - } - - // next_back() - { - let mut it = v.iter(); - for i in 0..len { - let remaining = len - i; - assert_eq!(it.size_hint(), (remaining, Some(remaining))); - - let prev = it.next_back().unwrap(); - assert_eq!(prev as *const _, v_ptrs[remaining - 1]); - } - assert_eq!(it.size_hint(), (0, Some(0))); - assert_eq!(it.next_back(), None, "The final call to next_back() should return None"); - } - } - - fn test_mut(x: T) { - let v: &mut [T] = &mut [x, x, x]; - let v_ptrs: [*mut T; 3] = match v { - [ref v1, ref v2, ref v3] => { - [v1 as *const _ as *mut _, v2 as *const _ as *mut _, v3 as *const _ as *mut _] - } - _ => unreachable!(), - }; - let len = v.len(); - - // nth(i) - for i in 0..len { - assert_eq!(&mut v[i] as *mut _, v_ptrs[i]); // check the v_ptrs array, just to be sure - let nth = v.iter_mut().nth(i).unwrap(); - assert_eq!(nth as *mut _, v_ptrs[i]); - } - assert_eq!(v.iter().nth(len), None, "nth(len) should return None"); - - // stepping through with nth(0) - { - let mut it = v.iter(); - for i in 0..len { - let next = it.nth(0).unwrap(); - assert_eq!(next as *const _, v_ptrs[i]); - } - assert_eq!(it.nth(0), None); - } - - // next() - { - let mut it = v.iter_mut(); - for i in 0..len { - let remaining = len - i; - assert_eq!(it.size_hint(), (remaining, Some(remaining))); - - let next = it.next().unwrap(); - assert_eq!(next as *mut _, v_ptrs[i]); - } - assert_eq!(it.size_hint(), (0, Some(0))); - assert_eq!(it.next(), None, "The final call to next() should return None"); - } - - // next_back() - { - let mut it = v.iter_mut(); - for i in 0..len { - let remaining = len - i; - assert_eq!(it.size_hint(), (remaining, Some(remaining))); - - let prev = it.next_back().unwrap(); - assert_eq!(prev as *mut _, v_ptrs[remaining - 1]); - } - assert_eq!(it.size_hint(), (0, Some(0))); - assert_eq!(it.next_back(), None, "The final call to next_back() should return None"); - } - } - - // Make sure iterators and slice patterns yield consistent addresses for various types, - // including ZSTs. - test(0u32); - test(()); - test([0u32; 0]); // ZST with alignment > 0 - test_mut(0u32); - test_mut(()); - test_mut([0u32; 0]); // ZST with alignment > 0 -} - -// The current implementation of SliceIndex fails to handle methods -// orthogonally from range types; therefore, it is worth testing -// all of the indexing operations on each input. -mod slice_index { - // This checks all six indexing methods, given an input range that - // should succeed. (it is NOT suitable for testing invalid inputs) - macro_rules! assert_range_eq { - ($arr:expr, $range:expr, $expected:expr) => { - let mut arr = $arr; - let mut expected = $expected; - { - let s: &[_] = &arr; - let expected: &[_] = &expected; - - assert_eq!(&s[$range], expected, "(in assertion for: index)"); - assert_eq!(s.get($range), Some(expected), "(in assertion for: get)"); - unsafe { - assert_eq!( - s.get_unchecked($range), - expected, - "(in assertion for: get_unchecked)", - ); - } - } - { - let s: &mut [_] = &mut arr; - let expected: &mut [_] = &mut expected; - - assert_eq!(&mut s[$range], expected, "(in assertion for: index_mut)",); - assert_eq!( - s.get_mut($range), - Some(&mut expected[..]), - "(in assertion for: get_mut)", - ); - unsafe { - assert_eq!( - s.get_unchecked_mut($range), - expected, - "(in assertion for: get_unchecked_mut)", - ); - } - } - }; - } - - // Make sure the macro can actually detect bugs, - // because if it can't, then what are we even doing here? - // - // (Be aware this only demonstrates the ability to detect bugs - // in the FIRST method that panics, as the macro is not designed - // to be used in `should_panic`) - #[test] - #[should_panic(expected = "out of range")] - fn assert_range_eq_can_fail_by_panic() { - assert_range_eq!([0, 1, 2], 0..5, [0, 1, 2]); - } - - // (Be aware this only demonstrates the ability to detect bugs - // in the FIRST method it calls, as the macro is not designed - // to be used in `should_panic`) - #[test] - #[should_panic(expected = "==")] - fn assert_range_eq_can_fail_by_inequality() { - assert_range_eq!([0, 1, 2], 0..2, [0, 1, 2]); - } - - // Test cases for bad index operations. - // - // This generates `should_panic` test cases for Index/IndexMut - // and `None` test cases for get/get_mut. - macro_rules! panic_cases { - ($( - // each test case needs a unique name to namespace the tests - in mod $case_name:ident { - data: $data:expr; - - // optional: - // - // one or more similar inputs for which data[input] succeeds, - // and the corresponding output as an array. This helps validate - // "critical points" where an input range straddles the boundary - // between valid and invalid. - // (such as the input `len..len`, which is just barely valid) - $( - good: data[$good:expr] == $output:expr; - )* - - bad: data[$bad:expr]; - message: $expect_msg:expr; - } - )*) => {$( - mod $case_name { - #[allow(unused_imports)] - use core::ops::Bound; - - #[test] - fn pass() { - let mut v = $data; - - $( assert_range_eq!($data, $good, $output); )* - - { - let v: &[_] = &v; - assert_eq!(v.get($bad), None, "(in None assertion for get)"); - } - - { - let v: &mut [_] = &mut v; - assert_eq!(v.get_mut($bad), None, "(in None assertion for get_mut)"); - } - } - - #[test] - #[should_panic(expected = $expect_msg)] - fn index_fail() { - let v = $data; - let v: &[_] = &v; - let _v = &v[$bad]; - } - - #[test] - #[should_panic(expected = $expect_msg)] - fn index_mut_fail() { - let mut v = $data; - let v: &mut [_] = &mut v; - let _v = &mut v[$bad]; - } - } - )*}; - } - - #[test] - fn simple() { - let v = [0, 1, 2, 3, 4, 5]; - - assert_range_eq!(v, .., [0, 1, 2, 3, 4, 5]); - assert_range_eq!(v, ..2, [0, 1]); - assert_range_eq!(v, ..=1, [0, 1]); - assert_range_eq!(v, 2.., [2, 3, 4, 5]); - assert_range_eq!(v, 1..4, [1, 2, 3]); - assert_range_eq!(v, 1..=3, [1, 2, 3]); - } - - panic_cases! { - in mod rangefrom_len { - data: [0, 1, 2, 3, 4, 5]; - - good: data[6..] == []; - bad: data[7..]; - message: "out of range"; - } - - in mod rangeto_len { - data: [0, 1, 2, 3, 4, 5]; - - good: data[..6] == [0, 1, 2, 3, 4, 5]; - bad: data[..7]; - message: "out of range"; - } - - in mod rangetoinclusive_len { - data: [0, 1, 2, 3, 4, 5]; - - good: data[..=5] == [0, 1, 2, 3, 4, 5]; - bad: data[..=6]; - message: "out of range"; - } - - in mod rangeinclusive_len { - data: [0, 1, 2, 3, 4, 5]; - - good: data[0..=5] == [0, 1, 2, 3, 4, 5]; - bad: data[0..=6]; - message: "out of range"; - } - - in mod range_len_len { - data: [0, 1, 2, 3, 4, 5]; - - good: data[6..6] == []; - bad: data[7..7]; - message: "out of range"; - } - - in mod rangeinclusive_len_len { - data: [0, 1, 2, 3, 4, 5]; - - good: data[6..=5] == []; - bad: data[7..=6]; - message: "out of range"; - } - - in mod boundpair_len { - data: [0, 1, 2, 3, 4, 5]; - - good: data[(Bound::Included(6), Bound::Unbounded)] == []; - good: data[(Bound::Unbounded, Bound::Included(5))] == [0, 1, 2, 3, 4, 5]; - good: data[(Bound::Unbounded, Bound::Excluded(6))] == [0, 1, 2, 3, 4, 5]; - good: data[(Bound::Included(0), Bound::Included(5))] == [0, 1, 2, 3, 4, 5]; - good: data[(Bound::Included(0), Bound::Excluded(6))] == [0, 1, 2, 3, 4, 5]; - good: data[(Bound::Included(2), Bound::Excluded(4))] == [2, 3]; - good: data[(Bound::Excluded(1), Bound::Included(4))] == [2, 3, 4]; - good: data[(Bound::Excluded(5), Bound::Excluded(6))] == []; - good: data[(Bound::Included(6), Bound::Excluded(6))] == []; - good: data[(Bound::Excluded(5), Bound::Included(5))] == []; - good: data[(Bound::Included(6), Bound::Included(5))] == []; - bad: data[(Bound::Unbounded, Bound::Included(6))]; - message: "out of range"; - } - } - - panic_cases! { - in mod rangeinclusive_exhausted { - data: [0, 1, 2, 3, 4, 5]; - - good: data[0..=5] == [0, 1, 2, 3, 4, 5]; - good: data[{ - let mut iter = 0..=5; - iter.by_ref().count(); // exhaust it - iter - }] == []; - - // 0..=6 is out of range before exhaustion, so it - // stands to reason that it still would be after. - bad: data[{ - let mut iter = 0..=6; - iter.by_ref().count(); // exhaust it - iter - }]; - message: "out of range"; - } - } - - panic_cases! { - in mod range_neg_width { - data: [0, 1, 2, 3, 4, 5]; - - good: data[4..4] == []; - bad: data[4..3]; - message: "but ends at"; - } - - in mod rangeinclusive_neg_width { - data: [0, 1, 2, 3, 4, 5]; - - good: data[4..=3] == []; - bad: data[4..=2]; - message: "but ends at"; - } - - in mod boundpair_neg_width { - data: [0, 1, 2, 3, 4, 5]; - - good: data[(Bound::Included(4), Bound::Excluded(4))] == []; - bad: data[(Bound::Included(4), Bound::Excluded(3))]; - message: "but ends at"; - } - } - - panic_cases! { - in mod rangeinclusive_overflow { - data: [0, 1]; - - // note: using 0 specifically ensures that the result of overflowing is 0..0, - // so that `get` doesn't simply return None for the wrong reason. - bad: data[0 ..= usize::MAX]; - message: "maximum usize"; - } - - in mod rangetoinclusive_overflow { - data: [0, 1]; - - bad: data[..= usize::MAX]; - message: "maximum usize"; - } - - in mod boundpair_overflow_end { - data: [0; 1]; - - bad: data[(Bound::Unbounded, Bound::Included(usize::MAX))]; - message: "maximum usize"; - } - - in mod boundpair_overflow_start { - data: [0; 1]; - - bad: data[(Bound::Excluded(usize::MAX), Bound::Unbounded)]; - message: "maximum usize"; - } - } // panic_cases! -} - -#[test] -fn test_find_rfind() { - let v = [0, 1, 2, 3, 4, 5]; - let mut iter = v.iter(); - let mut i = v.len(); - while let Some(&elt) = iter.rfind(|_| true) { - i -= 1; - assert_eq!(elt, v[i]); - } - assert_eq!(i, 0); - assert_eq!(v.iter().rfind(|&&x| x <= 3), Some(&3)); -} - -#[test] -fn test_iter_folds() { - let a = [1, 2, 3, 4, 5]; // len>4 so the unroll is used - assert_eq!(a.iter().fold(0, |acc, &x| 2 * acc + x), 57); - assert_eq!(a.iter().rfold(0, |acc, &x| 2 * acc + x), 129); - let fold = |acc: i32, &x| acc.checked_mul(2)?.checked_add(x); - assert_eq!(a.iter().try_fold(0, &fold), Some(57)); - assert_eq!(a.iter().try_rfold(0, &fold), Some(129)); - - // short-circuiting try_fold, through other methods - let a = [0, 1, 2, 3, 5, 5, 5, 7, 8, 9]; - let mut iter = a.iter(); - assert_eq!(iter.position(|&x| x == 3), Some(3)); - assert_eq!(iter.rfind(|&&x| x == 5), Some(&5)); - assert_eq!(iter.len(), 2); -} - -#[test] -fn test_rotate_left() { - const N: usize = 600; - let a: &mut [_] = &mut [0; N]; - for i in 0..N { - a[i] = i; - } - - a.rotate_left(42); - let k = N - 42; - - for i in 0..N { - assert_eq!(a[(i + k) % N], i); - } -} - -#[test] -fn test_rotate_right() { - const N: usize = 600; - let a: &mut [_] = &mut [0; N]; - for i in 0..N { - a[i] = i; - } - - a.rotate_right(42); - - for i in 0..N { - assert_eq!(a[(i + 42) % N], i); - } -} - -#[test] -#[cfg_attr(miri, ignore)] // Miri is too slow -fn brute_force_rotate_test_0() { - // In case of edge cases involving multiple algorithms - let n = 300; - for len in 0..n { - for s in 0..len { - let mut v = Vec::with_capacity(len); - for i in 0..len { - v.push(i); - } - v[..].rotate_right(s); - for i in 0..v.len() { - assert_eq!(v[i], v.len().wrapping_add(i.wrapping_sub(s)) % v.len()); - } - } - } -} - -#[test] -fn brute_force_rotate_test_1() { - // `ptr_rotate` covers so many kinds of pointer usage, that this is just a good test for - // pointers in general. This uses a `[usize; 4]` to hit all algorithms without overwhelming miri - let n = 30; - for len in 0..n { - for s in 0..len { - let mut v: Vec<[usize; 4]> = Vec::with_capacity(len); - for i in 0..len { - v.push([i, 0, 0, 0]); - } - v[..].rotate_right(s); - for i in 0..v.len() { - assert_eq!(v[i][0], v.len().wrapping_add(i.wrapping_sub(s)) % v.len()); - } - } - } -} - -#[test] -#[cfg(not(target_arch = "wasm32"))] -#[cfg_attr(miri, ignore)] // Miri is too slow -fn select_nth_unstable() { - use core::cmp::Ordering::{Equal, Greater, Less}; - - use rand::Rng; - use rand::seq::SliceRandom; - - let mut rng = crate::test_rng(); - - for len in (2..21).chain(500..501) { - let mut orig = vec![0; len]; - - for &modulus in &[5, 10, 1000] { - for _ in 0..10 { - for i in 0..len { - orig[i] = rng.gen::() % modulus; - } - - let v_sorted = { - let mut v = orig.clone(); - v.sort(); - v - }; - - // Sort in default order. - for pivot in 0..len { - let mut v = orig.clone(); - v.select_nth_unstable(pivot); - - assert_eq!(v_sorted[pivot], v[pivot]); - for i in 0..pivot { - for j in pivot..len { - assert!(v[i] <= v[j]); - } - } - } - - // Sort in ascending order. - for pivot in 0..len { - let mut v = orig.clone(); - let (left, pivot, right) = v.select_nth_unstable_by(pivot, |a, b| a.cmp(b)); - - assert_eq!(left.len() + right.len(), len - 1); - - for l in left { - assert!(l <= pivot); - for r in right.iter_mut() { - assert!(l <= r); - assert!(pivot <= r); - } - } - } - - // Sort in descending order. - let sort_descending_comparator = |a: &i32, b: &i32| b.cmp(a); - let v_sorted_descending = { - let mut v = orig.clone(); - v.sort_by(sort_descending_comparator); - v - }; - - for pivot in 0..len { - let mut v = orig.clone(); - v.select_nth_unstable_by(pivot, sort_descending_comparator); - - assert_eq!(v_sorted_descending[pivot], v[pivot]); - for i in 0..pivot { - for j in pivot..len { - assert!(v[j] <= v[i]); - } - } - } - } - } - } - - // Sort at index using a completely random comparison function. - // This will reorder the elements *somehow*, but won't panic. - let mut v = [0; 500]; - for i in 0..v.len() { - v[i] = i as i32; - } - - for pivot in 0..v.len() { - v.select_nth_unstable_by(pivot, |_, _| *[Less, Equal, Greater].choose(&mut rng).unwrap()); - v.sort(); - for i in 0..v.len() { - assert_eq!(v[i], i as i32); - } - } - - // Should not panic. - [(); 10].select_nth_unstable(0); - [(); 10].select_nth_unstable(5); - [(); 10].select_nth_unstable(9); - [(); 100].select_nth_unstable(0); - [(); 100].select_nth_unstable(50); - [(); 100].select_nth_unstable(99); - - let mut v = [0xDEADBEEFu64]; - v.select_nth_unstable(0); - assert!(v == [0xDEADBEEF]); -} - -#[test] -#[should_panic(expected = "index 0 greater than length of slice")] -fn select_nth_unstable_zero_length() { - [0i32; 0].select_nth_unstable(0); -} - -#[test] -#[should_panic(expected = "index 20 greater than length of slice")] -fn select_nth_unstable_past_length() { - [0i32; 10].select_nth_unstable(20); -} - -pub mod memchr { - use core::slice::memchr::{memchr, memrchr}; - - // test fallback implementations on all platforms - #[test] - fn matches_one() { - assert_eq!(Some(0), memchr(b'a', b"a")); - } - - #[test] - fn matches_begin() { - assert_eq!(Some(0), memchr(b'a', b"aaaa")); - } - - #[test] - fn matches_end() { - assert_eq!(Some(4), memchr(b'z', b"aaaaz")); - } - - #[test] - fn matches_nul() { - assert_eq!(Some(4), memchr(b'\x00', b"aaaa\x00")); - } - - #[test] - fn matches_past_nul() { - assert_eq!(Some(5), memchr(b'z', b"aaaa\x00z")); - } - - #[test] - fn no_match_empty() { - assert_eq!(None, memchr(b'a', b"")); - } - - #[test] - fn no_match() { - assert_eq!(None, memchr(b'a', b"xyz")); - } - - #[test] - fn matches_one_reversed() { - assert_eq!(Some(0), memrchr(b'a', b"a")); - } - - #[test] - fn matches_begin_reversed() { - assert_eq!(Some(3), memrchr(b'a', b"aaaa")); - } - - #[test] - fn matches_end_reversed() { - assert_eq!(Some(0), memrchr(b'z', b"zaaaa")); - } - - #[test] - fn matches_nul_reversed() { - assert_eq!(Some(4), memrchr(b'\x00', b"aaaa\x00")); - } - - #[test] - fn matches_past_nul_reversed() { - assert_eq!(Some(0), memrchr(b'z', b"z\x00aaaa")); - } - - #[test] - fn no_match_empty_reversed() { - assert_eq!(None, memrchr(b'a', b"")); - } - - #[test] - fn no_match_reversed() { - assert_eq!(None, memrchr(b'a', b"xyz")); - } - - #[test] - fn each_alignment_reversed() { - let mut data = [1u8; 64]; - let needle = 2; - let pos = 40; - data[pos] = needle; - for start in 0..16 { - assert_eq!(Some(pos - start), memrchr(needle, &data[start..])); - } - } -} - -#[test] -fn test_align_to_simple() { - let bytes = [1u8, 2, 3, 4, 5, 6, 7]; - let (prefix, aligned, suffix) = unsafe { bytes.align_to::() }; - assert_eq!(aligned.len(), 3); - assert!(prefix == [1] || suffix == [7]); - let expect1 = [1 << 8 | 2, 3 << 8 | 4, 5 << 8 | 6]; - let expect2 = [1 | 2 << 8, 3 | 4 << 8, 5 | 6 << 8]; - let expect3 = [2 << 8 | 3, 4 << 8 | 5, 6 << 8 | 7]; - let expect4 = [2 | 3 << 8, 4 | 5 << 8, 6 | 7 << 8]; - assert!( - aligned == expect1 || aligned == expect2 || aligned == expect3 || aligned == expect4, - "aligned={:?} expected={:?} || {:?} || {:?} || {:?}", - aligned, - expect1, - expect2, - expect3, - expect4 - ); -} - -#[test] -fn test_align_to_zst() { - let bytes = [1, 2, 3, 4, 5, 6, 7]; - let (prefix, aligned, suffix) = unsafe { bytes.align_to::<()>() }; - assert_eq!(aligned.len(), 0); - assert!(prefix == [1, 2, 3, 4, 5, 6, 7] || suffix == [1, 2, 3, 4, 5, 6, 7]); -} - -#[test] -fn test_align_to_non_trivial() { - #[repr(align(8))] - struct U64(#[allow(dead_code)] u64, #[allow(dead_code)] u64); - #[repr(align(8))] - struct U64U64U32(#[allow(dead_code)] u64, #[allow(dead_code)] u64, #[allow(dead_code)] u32); - let data = [ - U64(1, 2), - U64(3, 4), - U64(5, 6), - U64(7, 8), - U64(9, 10), - U64(11, 12), - U64(13, 14), - U64(15, 16), - ]; - let (prefix, aligned, suffix) = unsafe { data.align_to::() }; - assert_eq!(aligned.len(), 4); - assert_eq!(prefix.len() + suffix.len(), 2); -} - -#[test] -fn test_align_to_empty_mid() { - use core::mem; - - // Make sure that we do not create empty unaligned slices for the mid part, even when the - // overall slice is too short to contain an aligned address. - let bytes = [1, 2, 3, 4, 5, 6, 7]; - type Chunk = u32; - for offset in 0..4 { - let (_, mid, _) = unsafe { bytes[offset..offset + 1].align_to::() }; - assert_eq!(mid.as_ptr() as usize % mem::align_of::(), 0); - } -} - -#[test] -fn test_align_to_mut_aliasing() { - let mut val = [1u8, 2, 3, 4, 5]; - // `align_to_mut` used to create `mid` in a way that there was some intermediate - // incorrect aliasing, invalidating the resulting `mid` slice. - let (begin, mid, end) = unsafe { val.align_to_mut::<[u8; 2]>() }; - assert!(begin.len() == 0); - assert!(end.len() == 1); - mid[0] = mid[1]; - assert_eq!(val, [3, 4, 3, 4, 5]) -} - -#[test] -fn test_slice_partition_dedup_by() { - let mut slice: [i32; 9] = [1, -1, 2, 3, 1, -5, 5, -2, 2]; - - let (dedup, duplicates) = slice.partition_dedup_by(|a, b| a.abs() == b.abs()); - - assert_eq!(dedup, [1, 2, 3, 1, -5, -2]); - assert_eq!(duplicates, [5, -1, 2]); -} - -#[test] -fn test_slice_partition_dedup_empty() { - let mut slice: [i32; 0] = []; - - let (dedup, duplicates) = slice.partition_dedup(); - - assert_eq!(dedup, []); - assert_eq!(duplicates, []); -} - -#[test] -fn test_slice_partition_dedup_one() { - let mut slice = [12]; - - let (dedup, duplicates) = slice.partition_dedup(); - - assert_eq!(dedup, [12]); - assert_eq!(duplicates, []); -} - -#[test] -fn test_slice_partition_dedup_multiple_ident() { - let mut slice = [12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11]; - - let (dedup, duplicates) = slice.partition_dedup(); - - assert_eq!(dedup, [12, 11]); - assert_eq!(duplicates, [12, 12, 12, 12, 11, 11, 11, 11, 11]); -} - -#[test] -fn test_slice_partition_dedup_partialeq() { - #[derive(Debug)] - struct Foo(i32, #[allow(dead_code)] i32); - - impl PartialEq for Foo { - fn eq(&self, other: &Foo) -> bool { - self.0 == other.0 - } - } - - let mut slice = [Foo(0, 1), Foo(0, 5), Foo(1, 7), Foo(1, 9)]; - - let (dedup, duplicates) = slice.partition_dedup(); - - assert_eq!(dedup, [Foo(0, 1), Foo(1, 7)]); - assert_eq!(duplicates, [Foo(0, 5), Foo(1, 9)]); -} - -#[test] -fn test_copy_within() { - // Start to end, with a RangeTo. - let mut bytes = *b"Hello, World!"; - bytes.copy_within(..3, 10); - assert_eq!(&bytes, b"Hello, WorHel"); - - // End to start, with a RangeFrom. - let mut bytes = *b"Hello, World!"; - bytes.copy_within(10.., 0); - assert_eq!(&bytes, b"ld!lo, World!"); - - // Overlapping, with a RangeInclusive. - let mut bytes = *b"Hello, World!"; - bytes.copy_within(0..=11, 1); - assert_eq!(&bytes, b"HHello, World"); - - // Whole slice, with a RangeFull. - let mut bytes = *b"Hello, World!"; - bytes.copy_within(.., 0); - assert_eq!(&bytes, b"Hello, World!"); - - // Ensure that copying at the end of slice won't cause UB. - let mut bytes = *b"Hello, World!"; - bytes.copy_within(13..13, 5); - assert_eq!(&bytes, b"Hello, World!"); - bytes.copy_within(5..5, 13); - assert_eq!(&bytes, b"Hello, World!"); -} - -#[test] -#[should_panic(expected = "range end index 14 out of range for slice of length 13")] -fn test_copy_within_panics_src_too_long() { - let mut bytes = *b"Hello, World!"; - // The length is only 13, so 14 is out of bounds. - bytes.copy_within(10..14, 0); -} - -#[test] -#[should_panic(expected = "dest is out of bounds")] -fn test_copy_within_panics_dest_too_long() { - let mut bytes = *b"Hello, World!"; - // The length is only 13, so a slice of length 4 starting at index 10 is out of bounds. - bytes.copy_within(0..4, 10); -} - -#[test] -#[should_panic(expected = "slice index starts at 2 but ends at 1")] -fn test_copy_within_panics_src_inverted() { - let mut bytes = *b"Hello, World!"; - // 2 is greater than 1, so this range is invalid. - bytes.copy_within(2..1, 0); -} -#[test] -#[should_panic(expected = "attempted to index slice up to maximum usize")] -fn test_copy_within_panics_src_out_of_bounds() { - let mut bytes = *b"Hello, World!"; - // an inclusive range ending at usize::MAX would make src_end overflow - bytes.copy_within(usize::MAX..=usize::MAX, 0); -} - -#[test] -fn test_is_sorted() { - let empty: [i32; 0] = []; - - // Tests on integers - assert!([1, 2, 2, 9].is_sorted()); - assert!(![1, 3, 2].is_sorted()); - assert!([0].is_sorted()); - assert!([0, 0].is_sorted()); - assert!(empty.is_sorted()); - - // Tests on floats - assert!([1.0f32, 2.0, 2.0, 9.0].is_sorted()); - assert!(![1.0f32, 3.0f32, 2.0f32].is_sorted()); - assert!([0.0f32].is_sorted()); - assert!([0.0f32, 0.0f32].is_sorted()); - // Test cases with NaNs - assert!([f32::NAN].is_sorted()); - assert!(![f32::NAN, f32::NAN].is_sorted()); - assert!(![0.0, 1.0, f32::NAN].is_sorted()); - // Tests from - assert!(![f32::NAN, f32::NAN, f32::NAN].is_sorted()); - assert!(![1.0, f32::NAN, 2.0].is_sorted()); - assert!(![2.0, f32::NAN, 1.0].is_sorted()); - assert!(![2.0, f32::NAN, 1.0, 7.0].is_sorted()); - assert!(![2.0, f32::NAN, 1.0, 0.0].is_sorted()); - assert!(![-f32::NAN, -1.0, 0.0, 1.0, f32::NAN].is_sorted()); - assert!(![f32::NAN, -f32::NAN, -1.0, 0.0, 1.0].is_sorted()); - assert!(![1.0, f32::NAN, -f32::NAN, -1.0, 0.0].is_sorted()); - assert!(![0.0, 1.0, f32::NAN, -f32::NAN, -1.0].is_sorted()); - assert!(![-1.0, 0.0, 1.0, f32::NAN, -f32::NAN].is_sorted()); - - // Tests for is_sorted_by - assert!(![6, 2, 8, 5, 1, -60, 1337].is_sorted()); - assert!([6, 2, 8, 5, 1, -60, 1337].is_sorted_by(|_, _| true)); - - // Tests for is_sorted_by_key - assert!([-2, -1, 0, 3].is_sorted()); - assert!(![-2i32, -1, 0, 3].is_sorted_by_key(|n| n.abs())); - assert!(!["c", "bb", "aaa"].is_sorted()); - assert!(["c", "bb", "aaa"].is_sorted_by_key(|s| s.len())); -} - -#[test] -fn test_slice_run_destructors() { - // Make sure that destructors get run on slice literals - struct Foo<'a> { - x: &'a Cell, - } - - impl<'a> Drop for Foo<'a> { - fn drop(&mut self) { - self.x.set(self.x.get() + 1); - } - } - - fn foo(x: &Cell) -> Foo<'_> { - Foo { x } - } - - let x = &Cell::new(0); - - { - let l = &[foo(x)]; - assert_eq!(l[0].x.get(), 0); - } - - assert_eq!(x.get(), 1); -} - -#[test] -fn test_const_from_ref() { - const VALUE: &i32 = &1; - const SLICE: &[i32] = core::slice::from_ref(VALUE); - - assert!(core::ptr::eq(VALUE, &SLICE[0])) -} - -#[test] -fn test_slice_fill_with_uninit() { - // This should not UB. See #87891 - let mut a = [MaybeUninit::::uninit(); 10]; - a.fill(MaybeUninit::uninit()); -} - -#[test] -fn test_swap() { - let mut x = ["a", "b", "c", "d"]; - x.swap(1, 3); - assert_eq!(x, ["a", "d", "c", "b"]); - x.swap(0, 3); - assert_eq!(x, ["b", "d", "c", "a"]); -} - -mod swap_panics { - #[test] - #[should_panic(expected = "index out of bounds: the len is 4 but the index is 4")] - fn index_a_equals_len() { - let mut x = ["a", "b", "c", "d"]; - x.swap(4, 2); - } - - #[test] - #[should_panic(expected = "index out of bounds: the len is 4 but the index is 4")] - fn index_b_equals_len() { - let mut x = ["a", "b", "c", "d"]; - x.swap(2, 4); - } - - #[test] - #[should_panic(expected = "index out of bounds: the len is 4 but the index is 5")] - fn index_a_greater_than_len() { - let mut x = ["a", "b", "c", "d"]; - x.swap(5, 2); - } - - #[test] - #[should_panic(expected = "index out of bounds: the len is 4 but the index is 5")] - fn index_b_greater_than_len() { - let mut x = ["a", "b", "c", "d"]; - x.swap(2, 5); - } -} - -#[test] -fn slice_split_first_chunk_mut() { - let v = &mut [1, 2, 3, 4, 5, 6][..]; - - { - let (left, right) = v.split_first_chunk_mut::<0>().unwrap(); - assert_eq!(left, &mut []); - assert_eq!(right, [1, 2, 3, 4, 5, 6]); - } - - { - let (left, right) = v.split_first_chunk_mut::<6>().unwrap(); - assert_eq!(left, &mut [1, 2, 3, 4, 5, 6]); - assert_eq!(right, []); - } - - { - assert!(v.split_first_chunk_mut::<7>().is_none()); - } -} - -#[test] -fn slice_split_last_chunk_mut() { - let v = &mut [1, 2, 3, 4, 5, 6][..]; - - { - let (left, right) = v.split_last_chunk_mut::<0>().unwrap(); - assert_eq!(left, [1, 2, 3, 4, 5, 6]); - assert_eq!(right, &mut []); - } - - { - let (left, right) = v.split_last_chunk_mut::<6>().unwrap(); - assert_eq!(left, []); - assert_eq!(right, &mut [1, 2, 3, 4, 5, 6]); - } - - { - assert!(v.split_last_chunk_mut::<7>().is_none()); - } -} - -#[test] -fn split_as_slice() { - let arr = [1, 2, 3, 4, 5, 6]; - let mut split = arr.split(|v| v % 2 == 0); - assert_eq!(split.as_slice(), &[1, 2, 3, 4, 5, 6]); - assert!(split.next().is_some()); - assert_eq!(split.as_slice(), &[3, 4, 5, 6]); - assert!(split.next().is_some()); - assert!(split.next().is_some()); - assert_eq!(split.as_slice(), &[]); -} - -#[test] -fn slice_split_once() { - let v = &[1, 2, 3, 2, 4][..]; - - assert_eq!(v.split_once(|&x| x == 2), Some((&[1][..], &[3, 2, 4][..]))); - assert_eq!(v.split_once(|&x| x == 1), Some((&[][..], &[2, 3, 2, 4][..]))); - assert_eq!(v.split_once(|&x| x == 4), Some((&[1, 2, 3, 2][..], &[][..]))); - assert_eq!(v.split_once(|&x| x == 0), None); -} - -#[test] -fn slice_rsplit_once() { - let v = &[1, 2, 3, 2, 4][..]; - - assert_eq!(v.rsplit_once(|&x| x == 2), Some((&[1, 2, 3][..], &[4][..]))); - assert_eq!(v.rsplit_once(|&x| x == 1), Some((&[][..], &[2, 3, 2, 4][..]))); - assert_eq!(v.rsplit_once(|&x| x == 4), Some((&[1, 2, 3, 2][..], &[][..]))); - assert_eq!(v.rsplit_once(|&x| x == 0), None); -} - -macro_rules! take_tests { - (slice: &[], $($tts:tt)*) => { - take_tests!(ty: &[()], slice: &[], $($tts)*); - }; - (slice: &mut [], $($tts:tt)*) => { - take_tests!(ty: &mut [()], slice: &mut [], $($tts)*); - }; - (slice: &$slice:expr, $($tts:tt)*) => { - take_tests!(ty: &[_], slice: &$slice, $($tts)*); - }; - (slice: &mut $slice:expr, $($tts:tt)*) => { - take_tests!(ty: &mut [_], slice: &mut $slice, $($tts)*); - }; - (ty: $ty:ty, slice: $slice:expr, method: $method:ident, $(($test_name:ident, ($($args:expr),*), $output:expr, $remaining:expr),)*) => { - $( - #[test] - fn $test_name() { - let mut slice: $ty = $slice; - assert_eq!($output, slice.$method($($args)*)); - let remaining: $ty = $remaining; - assert_eq!(remaining, slice); - } - )* - }; -} - -take_tests! { - slice: &[0, 1, 2, 3], method: take, - (take_in_bounds_range_to, (..1), Some(&[0] as _), &[1, 2, 3]), - (take_in_bounds_range_to_inclusive, (..=0), Some(&[0] as _), &[1, 2, 3]), - (take_in_bounds_range_from, (2..), Some(&[2, 3] as _), &[0, 1]), - (take_oob_range_to, (..5), None, &[0, 1, 2, 3]), - (take_oob_range_to_inclusive, (..=4), None, &[0, 1, 2, 3]), - (take_oob_range_from, (5..), None, &[0, 1, 2, 3]), -} - -take_tests! { - slice: &mut [0, 1, 2, 3], method: take_mut, - (take_mut_in_bounds_range_to, (..1), Some(&mut [0] as _), &mut [1, 2, 3]), - (take_mut_in_bounds_range_to_inclusive, (..=0), Some(&mut [0] as _), &mut [1, 2, 3]), - (take_mut_in_bounds_range_from, (2..), Some(&mut [2, 3] as _), &mut [0, 1]), - (take_mut_oob_range_to, (..5), None, &mut [0, 1, 2, 3]), - (take_mut_oob_range_to_inclusive, (..=4), None, &mut [0, 1, 2, 3]), - (take_mut_oob_range_from, (5..), None, &mut [0, 1, 2, 3]), -} - -take_tests! { - slice: &[1, 2], method: take_first, - (take_first_nonempty, (), Some(&1), &[2]), -} - -take_tests! { - slice: &mut [1, 2], method: take_first_mut, - (take_first_mut_nonempty, (), Some(&mut 1), &mut [2]), -} - -take_tests! { - slice: &[1, 2], method: take_last, - (take_last_nonempty, (), Some(&2), &[1]), -} - -take_tests! { - slice: &mut [1, 2], method: take_last_mut, - (take_last_mut_nonempty, (), Some(&mut 2), &mut [1]), -} - -take_tests! { - slice: &[], method: take_first, - (take_first_empty, (), None, &[]), -} - -take_tests! { - slice: &mut [], method: take_first_mut, - (take_first_mut_empty, (), None, &mut []), -} - -take_tests! { - slice: &[], method: take_last, - (take_last_empty, (), None, &[]), -} - -take_tests! { - slice: &mut [], method: take_last_mut, - (take_last_mut_empty, (), None, &mut []), -} - -#[cfg(not(miri))] // unused in Miri -const EMPTY_MAX: &'static [()] = &[(); usize::MAX]; - -// can't be a constant due to const mutability rules -#[cfg(not(miri))] // unused in Miri -macro_rules! empty_max_mut { - () => { - &mut [(); usize::MAX] as _ - }; -} - -#[cfg(not(miri))] // Comparing usize::MAX many elements takes forever in Miri (and in rustc without optimizations) -take_tests! { - slice: &[(); usize::MAX], method: take, - (take_in_bounds_max_range_to, (..usize::MAX), Some(EMPTY_MAX), &[(); 0]), - (take_oob_max_range_to_inclusive, (..=usize::MAX), None, EMPTY_MAX), - (take_in_bounds_max_range_from, (usize::MAX..), Some(&[] as _), EMPTY_MAX), -} - -#[cfg(not(miri))] // Comparing usize::MAX many elements takes forever in Miri (and in rustc without optimizations) -take_tests! { - slice: &mut [(); usize::MAX], method: take_mut, - (take_mut_in_bounds_max_range_to, (..usize::MAX), Some(empty_max_mut!()), &mut [(); 0]), - (take_mut_oob_max_range_to_inclusive, (..=usize::MAX), None, empty_max_mut!()), - (take_mut_in_bounds_max_range_from, (usize::MAX..), Some(&mut [] as _), empty_max_mut!()), -} - -#[test] -fn test_slice_from_ptr_range() { - let arr = ["foo".to_owned(), "bar".to_owned()]; - let range = arr.as_ptr_range(); - unsafe { - assert_eq!(slice::from_ptr_range(range), &arr); - } - - let mut arr = [1, 2, 3]; - let range = arr.as_mut_ptr_range(); - unsafe { - assert_eq!(slice::from_mut_ptr_range(range), &mut [1, 2, 3]); - } - - let arr: [Vec; 0] = []; - let range = arr.as_ptr_range(); - unsafe { - assert_eq!(slice::from_ptr_range(range), &arr); - } -} - -#[test] -#[should_panic = "slice len overflow"] -fn test_flatten_size_overflow() { - let x = &[[(); usize::MAX]; 2][..]; - let _ = x.as_flattened(); -} - -#[test] -#[should_panic = "slice len overflow"] -fn test_flatten_mut_size_overflow() { - let x = &mut [[(); usize::MAX]; 2][..]; - let _ = x.as_flattened_mut(); -} - -#[test] -fn test_get_many_mut_normal_2() { - let mut v = vec![1, 2, 3, 4, 5]; - let [a, b] = v.get_many_mut([3, 0]).unwrap(); - *a += 10; - *b += 100; - assert_eq!(v, vec![101, 2, 3, 14, 5]); - - let [a, b] = v.get_many_mut([0..=1, 2..=2]).unwrap(); - assert_eq!(a, &mut [101, 2][..]); - assert_eq!(b, &mut [3][..]); - a[0] += 10; - a[1] += 20; - b[0] += 100; - assert_eq!(v, vec![111, 22, 103, 14, 5]); -} - -#[test] -fn test_get_many_mut_normal_3() { - let mut v = vec![1, 2, 3, 4, 5]; - let [a, b, c] = v.get_many_mut([0, 4, 2]).unwrap(); - *a += 10; - *b += 100; - *c += 1000; - assert_eq!(v, vec![11, 2, 1003, 4, 105]); - - let [a, b, c] = v.get_many_mut([0..1, 4..5, 1..4]).unwrap(); - assert_eq!(a, &mut [11][..]); - assert_eq!(b, &mut [105][..]); - assert_eq!(c, &mut [2, 1003, 4][..]); - a[0] += 10; - b[0] += 100; - c[0] += 1000; - assert_eq!(v, vec![21, 1002, 1003, 4, 205]); -} - -#[test] -fn test_get_many_mut_empty() { - let mut v = vec![1, 2, 3, 4, 5]; - let [] = v.get_many_mut::([]).unwrap(); - let [] = v.get_many_mut::, 0>([]).unwrap(); - let [] = v.get_many_mut::, 0>([]).unwrap(); - assert_eq!(v, vec![1, 2, 3, 4, 5]); -} - -#[test] -fn test_get_many_mut_single_first() { - let mut v = vec![1, 2, 3, 4, 5]; - let [a] = v.get_many_mut([0]).unwrap(); - *a += 10; - assert_eq!(v, vec![11, 2, 3, 4, 5]); -} - -#[test] -fn test_get_many_mut_single_last() { - let mut v = vec![1, 2, 3, 4, 5]; - let [a] = v.get_many_mut([4]).unwrap(); - *a += 10; - assert_eq!(v, vec![1, 2, 3, 4, 15]); -} - -#[test] -fn test_get_many_mut_oob_nonempty() { - let mut v = vec![1, 2, 3, 4, 5]; - assert!(v.get_many_mut([5]).is_err()); -} - -#[test] -fn test_get_many_mut_oob_empty() { - let mut v: Vec = vec![]; - assert!(v.get_many_mut([0]).is_err()); -} - -#[test] -fn test_get_many_mut_duplicate() { - let mut v = vec![1, 2, 3, 4, 5]; - assert!(v.get_many_mut([1, 3, 3, 4]).is_err()); -} - -#[test] -fn test_get_many_mut_range_oob() { - let mut v = vec![1, 2, 3, 4, 5]; - assert!(v.get_many_mut([0..6]).is_err()); - assert!(v.get_many_mut([5..6]).is_err()); - assert!(v.get_many_mut([6..6]).is_err()); - assert!(v.get_many_mut([0..=5]).is_err()); - assert!(v.get_many_mut([0..=6]).is_err()); - assert!(v.get_many_mut([5..=5]).is_err()); -} - -#[test] -fn test_get_many_mut_range_overlapping() { - let mut v = vec![1, 2, 3, 4, 5]; - assert!(v.get_many_mut([0..1, 0..2]).is_err()); - assert!(v.get_many_mut([0..1, 1..2, 0..1]).is_err()); - assert!(v.get_many_mut([0..3, 1..1]).is_err()); - assert!(v.get_many_mut([0..3, 1..2]).is_err()); - assert!(v.get_many_mut([0..=0, 2..=2, 0..=1]).is_err()); - assert!(v.get_many_mut([0..=4, 0..=0]).is_err()); - assert!(v.get_many_mut([4..=4, 0..=0, 3..=4]).is_err()); -} - -#[test] -fn test_get_many_mut_range_empty_at_edge() { - let mut v = vec![1, 2, 3, 4, 5]; - assert_eq!( - v.get_many_mut([0..0, 0..5, 5..5]), - Ok([&mut [][..], &mut [1, 2, 3, 4, 5], &mut []]), - ); - assert_eq!( - v.get_many_mut([0..0, 0..1, 1..1, 1..2, 2..2, 2..3, 3..3, 3..4, 4..4, 4..5, 5..5]), - Ok([ - &mut [][..], - &mut [1], - &mut [], - &mut [2], - &mut [], - &mut [3], - &mut [], - &mut [4], - &mut [], - &mut [5], - &mut [], - ]), - ); -} - -#[test] -fn test_slice_from_raw_parts_in_const() { - static FANCY: i32 = 4; - static FANCY_SLICE: &[i32] = unsafe { std::slice::from_raw_parts(&FANCY, 1) }; - assert_eq!(FANCY_SLICE.as_ptr(), std::ptr::addr_of!(FANCY)); - assert_eq!(FANCY_SLICE.len(), 1); - - const EMPTY_SLICE: &[i32] = - unsafe { std::slice::from_raw_parts(std::ptr::without_provenance(123456), 0) }; - assert_eq!(EMPTY_SLICE.as_ptr().addr(), 123456); - assert_eq!(EMPTY_SLICE.len(), 0); -} diff --git a/library/coretests/Cargo.toml b/library/coretests/Cargo.toml new file mode 100644 index 0000000000000..e44f01d347b3d --- /dev/null +++ b/library/coretests/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "coretests" +version = "0.0.0" +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-lang/rust.git" +description = "Tests for the Rust Core Library" +autotests = false +autobenches = false +edition = "2024" + +[lib] +path = "lib.rs" +test = false +bench = false + +[[test]] +name = "coretests" +path = "tests/lib.rs" + +[[bench]] +name = "corebenches" +path = "benches/lib.rs" +test = true + +[dev-dependencies] +rand = { version = "0.9.0", default-features = false } +rand_xorshift = { version = "0.4.0", default-features = false } diff --git a/library/core/benches/any.rs b/library/coretests/benches/any.rs similarity index 100% rename from library/core/benches/any.rs rename to library/coretests/benches/any.rs diff --git a/library/core/benches/array.rs b/library/coretests/benches/array.rs similarity index 100% rename from library/core/benches/array.rs rename to library/coretests/benches/array.rs diff --git a/library/core/benches/ascii.rs b/library/coretests/benches/ascii.rs similarity index 100% rename from library/core/benches/ascii.rs rename to library/coretests/benches/ascii.rs diff --git a/library/core/benches/ascii/is_ascii.rs b/library/coretests/benches/ascii/is_ascii.rs similarity index 100% rename from library/core/benches/ascii/is_ascii.rs rename to library/coretests/benches/ascii/is_ascii.rs diff --git a/library/core/benches/char/methods.rs b/library/coretests/benches/char/methods.rs similarity index 100% rename from library/core/benches/char/methods.rs rename to library/coretests/benches/char/methods.rs diff --git a/library/core/benches/char/mod.rs b/library/coretests/benches/char/mod.rs similarity index 100% rename from library/core/benches/char/mod.rs rename to library/coretests/benches/char/mod.rs diff --git a/library/core/benches/fmt.rs b/library/coretests/benches/fmt.rs similarity index 90% rename from library/core/benches/fmt.rs rename to library/coretests/benches/fmt.rs index ed478b0f1e055..ee8e981b46b97 100644 --- a/library/core/benches/fmt.rs +++ b/library/coretests/benches/fmt.rs @@ -124,42 +124,41 @@ fn write_str_macro_debug_ascii(bh: &mut Bencher) { #[bench] fn write_u128_max(bh: &mut Bencher) { bh.iter(|| { - test::black_box(format!("{}", u128::MAX)); + black_box(format!("{}", black_box(u128::MAX))); }); } #[bench] fn write_u128_min(bh: &mut Bencher) { bh.iter(|| { - let s = format!("{}", 0u128); - test::black_box(s); + black_box(format!("{}", black_box(u128::MIN))); }); } #[bench] fn write_u64_max(bh: &mut Bencher) { bh.iter(|| { - test::black_box(format!("{}", u64::MAX)); + black_box(format!("{}", black_box(u64::MAX))); }); } #[bench] fn write_u64_min(bh: &mut Bencher) { bh.iter(|| { - test::black_box(format!("{}", 0u64)); + black_box(format!("{}", black_box(u64::MIN))); }); } #[bench] fn write_u8_max(bh: &mut Bencher) { bh.iter(|| { - test::black_box(format!("{}", u8::MAX)); + black_box(format!("{}", black_box(u8::MAX))); }); } #[bench] fn write_u8_min(bh: &mut Bencher) { bh.iter(|| { - test::black_box(format!("{}", 0u8)); + black_box(format!("{}", black_box(u8::MIN))); }); } diff --git a/library/core/benches/hash/mod.rs b/library/coretests/benches/hash/mod.rs similarity index 100% rename from library/core/benches/hash/mod.rs rename to library/coretests/benches/hash/mod.rs diff --git a/library/core/benches/hash/sip.rs b/library/coretests/benches/hash/sip.rs similarity index 100% rename from library/core/benches/hash/sip.rs rename to library/coretests/benches/hash/sip.rs diff --git a/library/core/benches/iter.rs b/library/coretests/benches/iter.rs similarity index 100% rename from library/core/benches/iter.rs rename to library/coretests/benches/iter.rs diff --git a/library/core/benches/lib.rs b/library/coretests/benches/lib.rs similarity index 100% rename from library/core/benches/lib.rs rename to library/coretests/benches/lib.rs diff --git a/library/core/benches/net/addr_parser.rs b/library/coretests/benches/net/addr_parser.rs similarity index 100% rename from library/core/benches/net/addr_parser.rs rename to library/coretests/benches/net/addr_parser.rs diff --git a/library/core/benches/net/mod.rs b/library/coretests/benches/net/mod.rs similarity index 100% rename from library/core/benches/net/mod.rs rename to library/coretests/benches/net/mod.rs diff --git a/library/core/benches/num/dec2flt/mod.rs b/library/coretests/benches/num/dec2flt/mod.rs similarity index 100% rename from library/core/benches/num/dec2flt/mod.rs rename to library/coretests/benches/num/dec2flt/mod.rs diff --git a/library/core/benches/num/flt2dec/mod.rs b/library/coretests/benches/num/flt2dec/mod.rs similarity index 100% rename from library/core/benches/num/flt2dec/mod.rs rename to library/coretests/benches/num/flt2dec/mod.rs diff --git a/library/core/benches/num/flt2dec/strategy/dragon.rs b/library/coretests/benches/num/flt2dec/strategy/dragon.rs similarity index 100% rename from library/core/benches/num/flt2dec/strategy/dragon.rs rename to library/coretests/benches/num/flt2dec/strategy/dragon.rs diff --git a/library/core/benches/num/flt2dec/strategy/grisu.rs b/library/coretests/benches/num/flt2dec/strategy/grisu.rs similarity index 100% rename from library/core/benches/num/flt2dec/strategy/grisu.rs rename to library/coretests/benches/num/flt2dec/strategy/grisu.rs diff --git a/library/coretests/benches/num/int_log/mod.rs b/library/coretests/benches/num/int_log/mod.rs new file mode 100644 index 0000000000000..171d7e31cdb1a --- /dev/null +++ b/library/coretests/benches/num/int_log/mod.rs @@ -0,0 +1,125 @@ +use rand::Rng; +use test::{Bencher, black_box}; + +macro_rules! int_log10_bench { + ($t:ty, $predictable:ident, $random:ident, $random_small:ident) => { + #[bench] + fn $predictable(bench: &mut Bencher) { + bench.iter(|| { + for n in 0..(<$t>::BITS / 8) { + for i in 1..=(100 as $t) { + let x = black_box(i << (n * 8)); + black_box(x.ilog10()); + } + } + }); + } + + #[bench] + fn $random(bench: &mut Bencher) { + let mut rng = crate::bench_rng(); + /* Exponentially distributed random numbers from the whole range of the type. */ + let numbers: Vec<$t> = (0..256) + .map(|_| { + let x = rng.random::<$t>() >> rng.random_range(0..<$t>::BITS); + if x != 0 { x } else { 1 } + }) + .collect(); + bench.iter(|| { + for x in &numbers { + black_box(black_box(x).ilog10()); + } + }); + } + + #[bench] + fn $random_small(bench: &mut Bencher) { + let mut rng = crate::bench_rng(); + /* Exponentially distributed random numbers from the range 0..256. */ + let numbers: Vec<$t> = (0..256) + .map(|_| { + let x = (rng.random::() >> rng.random_range(0..u8::BITS)) as $t; + if x != 0 { x } else { 1 } + }) + .collect(); + bench.iter(|| { + for x in &numbers { + black_box(black_box(x).ilog10()); + } + }); + } + }; +} + +int_log10_bench! {u8, u8_log10_predictable, u8_log10_random, u8_log10_random_small} +int_log10_bench! {u16, u16_log10_predictable, u16_log10_random, u16_log10_random_small} +int_log10_bench! {u32, u32_log10_predictable, u32_log10_random, u32_log10_random_small} +int_log10_bench! {u64, u64_log10_predictable, u64_log10_random, u64_log10_random_small} +int_log10_bench! {u128, u128_log10_predictable, u128_log10_random, u128_log10_random_small} + +macro_rules! int_log_bench { + ($t:ty, $random:ident, $random_small:ident, $geometric:ident) => { + #[bench] + fn $random(bench: &mut Bencher) { + let mut rng = crate::bench_rng(); + /* Exponentially distributed random numbers from the whole range of the type. */ + let numbers: Vec<$t> = (0..256) + .map(|_| { + let x = rng.random::<$t>() >> rng.random_range(0..<$t>::BITS); + if x >= 2 { x } else { 2 } + }) + .collect(); + bench.iter(|| { + for &b in &numbers { + for &x in &numbers { + black_box(black_box(x).ilog(b)); + } + } + }); + } + + #[bench] + fn $random_small(bench: &mut Bencher) { + let mut rng = crate::bench_rng(); + /* Exponentially distributed random numbers from the range 0..256. */ + let numbers: Vec<$t> = (0..256) + .map(|_| { + let x = (rng.random::() >> rng.random_range(0..u8::BITS)) as $t; + if x >= 2 { x } else { 2 } + }) + .collect(); + bench.iter(|| { + for &b in &numbers { + for &x in &numbers { + black_box(black_box(x).ilog(b)); + } + } + }); + } + + #[bench] + fn $geometric(bench: &mut Bencher) { + let bases: [$t; 16] = [2, 3, 4, 5, 7, 8, 9, 15, 16, 17, 31, 32, 33, 63, 64, 65]; + let base_and_numbers: Vec<($t, Vec<$t>)> = bases + .iter() + .map(|&b| { + let numbers = (0..=<$t>::MAX.ilog(b)).map(|exp| b.pow(exp)).collect(); + (b, numbers) + }) + .collect(); + bench.iter(|| { + for (b, numbers) in &base_and_numbers { + for &x in numbers { + black_box(black_box(x).ilog(black_box(*b))); + } + } + }); + } + }; +} + +int_log_bench! {u8, u8_log_random, u8_log_random_small, u8_log_geometric} +int_log_bench! {u16, u16_log_random, u16_log_random_small, u16_log_geometric} +int_log_bench! {u32, u32_log_random, u32_log_random_small, u32_log_geometric} +int_log_bench! {u64, u64_log_random, u64_log_random_small, u64_log_geometric} +int_log_bench! {u128, u128_log_random, u128_log_random_small, u128_log_geometric} diff --git a/library/coretests/benches/num/int_pow/mod.rs b/library/coretests/benches/num/int_pow/mod.rs new file mode 100644 index 0000000000000..6b603d2f7b3b4 --- /dev/null +++ b/library/coretests/benches/num/int_pow/mod.rs @@ -0,0 +1,99 @@ +use rand::Rng; +use test::{Bencher, black_box}; + +const ITERATIONS: usize = 128; // Uses an ITERATIONS * 20 Byte stack allocation +type IntType = i128; // Hardest native type to multiply +const EXPONENT_MAX: u32 = 31; +const MAX_BASE: IntType = 17; // +-17 ** 31 <= IntType::MAX + +macro_rules! pow_bench_template { + ($name:ident, $inner_macro:ident, $base_macro:ident) => { + #[bench] + fn $name(bench: &mut Bencher) { + // Frequent black_box calls can add latency and prevent optimizations, so for + // variable parameters we premake an array and pass the + // reference through black_box outside of the loop. + let mut rng = crate::bench_rng(); + let base_array: [IntType; ITERATIONS] = + core::array::from_fn(|_| rng.random_range((-MAX_BASE..=MAX_BASE))); + let exp_array: [u32; ITERATIONS] = + core::array::from_fn(|_| rng.random_range((0..=EXPONENT_MAX))); + + bench.iter(|| { + #[allow(unused, unused_mut)] + let mut base_iter = black_box(&base_array).into_iter(); + let mut exp_iter = black_box(&exp_array).into_iter(); + + (0..ITERATIONS).fold((0 as IntType, false), |acc, _| { + // Sometimes constants don't propagate all the way to the + // inside of the loop, so we call a custom expression every cycle + // rather than iter::repeat(CONST) + let base: IntType = $base_macro!(base_iter); + let exp: u32 = *exp_iter.next().unwrap(); + + let r: (IntType, bool) = $inner_macro!(base, exp); + (acc.0 ^ r.0, acc.1 ^ r.1) + }) + }); + } + }; +} + +// This may panic if it overflows. +macro_rules! inner_pow { + ($base:ident, $exp:ident) => { + ($base.pow($exp), false) + }; +} + +macro_rules! inner_wrapping { + ($base:ident, $exp:ident) => { + ($base.wrapping_pow($exp), false) + }; +} + +macro_rules! inner_overflowing { + ($base:ident, $exp:ident) => { + $base.overflowing_pow($exp) + }; +} + +// This will panic if it overflows. +macro_rules! inner_checked_unwrapped { + ($base:ident, $exp:ident) => { + ($base.checked_pow($exp).unwrap(), false) + }; +} + +macro_rules! inner_saturating { + ($base:ident, $exp:ident) => { + ($base.saturating_pow($exp), false) + }; +} + +macro_rules! make_const_base { + ($name:ident, $x:literal) => { + macro_rules! $name { + ($iter:ident) => { + $x + }; + } + }; +} + +make_const_base!(const_base_m7, -7); +make_const_base!(const_base_m8, -8); + +macro_rules! variable_base { + ($iter:ident) => { + *$iter.next().unwrap() + }; +} + +pow_bench_template!(pow_variable, inner_pow, variable_base); +pow_bench_template!(wrapping_pow_variable, inner_wrapping, variable_base); +pow_bench_template!(overflowing_pow_variable, inner_overflowing, variable_base); +pow_bench_template!(checked_pow_variable, inner_checked_unwrapped, variable_base); +pow_bench_template!(saturating_pow_variable, inner_saturating, variable_base); +pow_bench_template!(pow_m7, inner_pow, const_base_m7); +pow_bench_template!(pow_m8, inner_pow, const_base_m8); diff --git a/library/coretests/benches/num/int_sqrt/mod.rs b/library/coretests/benches/num/int_sqrt/mod.rs new file mode 100644 index 0000000000000..05cb3c5383b27 --- /dev/null +++ b/library/coretests/benches/num/int_sqrt/mod.rs @@ -0,0 +1,63 @@ +use rand::Rng; +use test::{Bencher, black_box}; + +macro_rules! int_sqrt_bench { + ($t:ty, $predictable:ident, $random:ident, $random_small:ident, $random_uniform:ident) => { + #[bench] + fn $predictable(bench: &mut Bencher) { + bench.iter(|| { + for n in 0..(<$t>::BITS / 8) { + for i in 1..=(100 as $t) { + let x = black_box(i << (n * 8)); + black_box(x.isqrt()); + } + } + }); + } + + #[bench] + fn $random(bench: &mut Bencher) { + let mut rng = crate::bench_rng(); + /* Exponentially distributed random numbers from the whole range of the type. */ + let numbers: Vec<$t> = + (0..256).map(|_| rng.random::<$t>() >> rng.random_range(0..<$t>::BITS)).collect(); + bench.iter(|| { + for x in &numbers { + black_box(black_box(x).isqrt()); + } + }); + } + + #[bench] + fn $random_small(bench: &mut Bencher) { + let mut rng = crate::bench_rng(); + /* Exponentially distributed random numbers from the range 0..256. */ + let numbers: Vec<$t> = (0..256) + .map(|_| (rng.random::() >> rng.random_range(0..u8::BITS)) as $t) + .collect(); + bench.iter(|| { + for x in &numbers { + black_box(black_box(x).isqrt()); + } + }); + } + + #[bench] + fn $random_uniform(bench: &mut Bencher) { + let mut rng = crate::bench_rng(); + /* Exponentially distributed random numbers from the whole range of the type. */ + let numbers: Vec<$t> = (0..256).map(|_| rng.random::<$t>()).collect(); + bench.iter(|| { + for x in &numbers { + black_box(black_box(x).isqrt()); + } + }); + } + }; +} + +int_sqrt_bench! {u8, u8_sqrt_predictable, u8_sqrt_random, u8_sqrt_random_small, u8_sqrt_uniform} +int_sqrt_bench! {u16, u16_sqrt_predictable, u16_sqrt_random, u16_sqrt_random_small, u16_sqrt_uniform} +int_sqrt_bench! {u32, u32_sqrt_predictable, u32_sqrt_random, u32_sqrt_random_small, u32_sqrt_uniform} +int_sqrt_bench! {u64, u64_sqrt_predictable, u64_sqrt_random, u64_sqrt_random_small, u64_sqrt_uniform} +int_sqrt_bench! {u128, u128_sqrt_predictable, u128_sqrt_random, u128_sqrt_random_small, u128_sqrt_uniform} diff --git a/library/core/benches/num/mod.rs b/library/coretests/benches/num/mod.rs similarity index 100% rename from library/core/benches/num/mod.rs rename to library/coretests/benches/num/mod.rs diff --git a/library/core/benches/ops.rs b/library/coretests/benches/ops.rs similarity index 100% rename from library/core/benches/ops.rs rename to library/coretests/benches/ops.rs diff --git a/library/core/benches/pattern.rs b/library/coretests/benches/pattern.rs similarity index 100% rename from library/core/benches/pattern.rs rename to library/coretests/benches/pattern.rs diff --git a/library/coretests/benches/slice.rs b/library/coretests/benches/slice.rs new file mode 100644 index 0000000000000..71027981d94a1 --- /dev/null +++ b/library/coretests/benches/slice.rs @@ -0,0 +1,173 @@ +use core::ptr::NonNull; + +use test::{Bencher, black_box}; + +enum Cache { + L1, + L2, + L3, +} + +impl Cache { + fn size(&self) -> usize { + match self { + Cache::L1 => 1000, // 8kb + Cache::L2 => 10_000, // 80kb + Cache::L3 => 1_000_000, // 8Mb + } + } +} + +fn binary_search(b: &mut Bencher, cache: Cache, mapper: F) +where + F: Fn(usize) -> usize, +{ + let size = cache.size(); + let v = (0..size).map(&mapper).collect::>(); + let mut r = 0usize; + b.iter(move || { + // LCG constants from https://en.wikipedia.org/wiki/Numerical_Recipes. + r = r.wrapping_mul(1664525).wrapping_add(1013904223); + // Lookup the whole range to get 50% hits and 50% misses. + let i = mapper(r % size); + black_box(v.binary_search(&i).is_ok()); + }); +} + +fn binary_search_worst_case(b: &mut Bencher, cache: Cache) { + let size = cache.size(); + + let mut v = vec![0; size]; + let i = 1; + v[size - 1] = i; + b.iter(move || { + black_box(v.binary_search(&i).is_ok()); + }); +} + +#[bench] +fn binary_search_l1(b: &mut Bencher) { + binary_search(b, Cache::L1, |i| i * 2); +} + +#[bench] +fn binary_search_l2(b: &mut Bencher) { + binary_search(b, Cache::L2, |i| i * 2); +} + +#[bench] +fn binary_search_l3(b: &mut Bencher) { + binary_search(b, Cache::L3, |i| i * 2); +} + +#[bench] +fn binary_search_l1_with_dups(b: &mut Bencher) { + binary_search(b, Cache::L1, |i| i / 16 * 16); +} + +#[bench] +fn binary_search_l2_with_dups(b: &mut Bencher) { + binary_search(b, Cache::L2, |i| i / 16 * 16); +} + +#[bench] +fn binary_search_l3_with_dups(b: &mut Bencher) { + binary_search(b, Cache::L3, |i| i / 16 * 16); +} + +#[bench] +fn binary_search_l1_worst_case(b: &mut Bencher) { + binary_search_worst_case(b, Cache::L1); +} + +#[bench] +fn binary_search_l2_worst_case(b: &mut Bencher) { + binary_search_worst_case(b, Cache::L2); +} + +#[bench] +fn binary_search_l3_worst_case(b: &mut Bencher) { + binary_search_worst_case(b, Cache::L3); +} + +#[derive(Clone)] +struct Rgb(#[allow(dead_code)] u8, #[allow(dead_code)] u8, #[allow(dead_code)] u8); + +impl Rgb { + fn new(i: usize) -> Self { + Rgb(i as u8, (i as u8).wrapping_add(7), (i as u8).wrapping_add(42)) + } +} + +macro_rules! rotate { + ($fn:ident, $n:expr, $mapper:expr) => { + #[bench] + fn $fn(b: &mut Bencher) { + let mut x = (0usize..$n).map(&$mapper).collect::>(); + b.iter(|| { + for s in 0..x.len() { + x[..].rotate_right(s); + } + black_box(x[0].clone()) + }) + } + }; +} + +rotate!(rotate_u8, 32, |i| i as u8); +rotate!(rotate_rgb, 32, Rgb::new); +rotate!(rotate_usize, 32, |i| i); +rotate!(rotate_16_usize_4, 16, |i| [i; 4]); +rotate!(rotate_16_usize_5, 16, |i| [i; 5]); +rotate!(rotate_64_usize_4, 64, |i| [i; 4]); +rotate!(rotate_64_usize_5, 64, |i| [i; 5]); + +macro_rules! swap_with_slice { + ($fn:ident, $n:expr, $mapper:expr) => { + #[bench] + fn $fn(b: &mut Bencher) { + let mut x = (0usize..$n).map(&$mapper).collect::>(); + let mut y = ($n..($n * 2)).map(&$mapper).collect::>(); + let mut skip = 0; + b.iter(|| { + for _ in 0..32 { + x[skip..].swap_with_slice(&mut y[..($n - skip)]); + skip = black_box(skip + 1) % 8; + } + black_box((x[$n / 3].clone(), y[$n * 2 / 3].clone())) + }) + } + }; +} + +swap_with_slice!(swap_with_slice_u8_30, 30, |i| i as u8); +swap_with_slice!(swap_with_slice_u8_3000, 3000, |i| i as u8); +swap_with_slice!(swap_with_slice_rgb_30, 30, Rgb::new); +swap_with_slice!(swap_with_slice_rgb_3000, 3000, Rgb::new); +swap_with_slice!(swap_with_slice_usize_30, 30, |i| i); +swap_with_slice!(swap_with_slice_usize_3000, 3000, |i| i); +swap_with_slice!(swap_with_slice_4x_usize_30, 30, |i| [i; 4]); +swap_with_slice!(swap_with_slice_4x_usize_3000, 3000, |i| [i; 4]); +swap_with_slice!(swap_with_slice_5x_usize_30, 30, |i| [i; 5]); +swap_with_slice!(swap_with_slice_5x_usize_3000, 3000, |i| [i; 5]); + +#[bench] +fn fill_byte_sized(b: &mut Bencher) { + #[derive(Copy, Clone)] + struct NewType(#[allow(dead_code)] u8); + + let mut ary = [NewType(0); 1024]; + + b.iter(|| { + let slice = &mut ary[..]; + black_box(slice.fill(black_box(NewType(42)))); + }); +} + +// Tests the ability of the compiler to recognize that only the last slice item is needed +// based on issue #106288 +#[bench] +fn fold_to_last(b: &mut Bencher) { + let slice: &[i32] = &[0; 1024]; + b.iter(|| black_box(slice).iter().fold(None, |_, r| Some(NonNull::from(r)))); +} diff --git a/library/core/benches/str.rs b/library/coretests/benches/str.rs similarity index 100% rename from library/core/benches/str.rs rename to library/coretests/benches/str.rs diff --git a/library/core/benches/str/char_count.rs b/library/coretests/benches/str/char_count.rs similarity index 100% rename from library/core/benches/str/char_count.rs rename to library/coretests/benches/str/char_count.rs diff --git a/library/core/benches/str/corpora.rs b/library/coretests/benches/str/corpora.rs similarity index 100% rename from library/core/benches/str/corpora.rs rename to library/coretests/benches/str/corpora.rs diff --git a/library/core/benches/str/debug.rs b/library/coretests/benches/str/debug.rs similarity index 100% rename from library/core/benches/str/debug.rs rename to library/coretests/benches/str/debug.rs diff --git a/library/core/benches/str/iter.rs b/library/coretests/benches/str/iter.rs similarity index 100% rename from library/core/benches/str/iter.rs rename to library/coretests/benches/str/iter.rs diff --git a/library/core/benches/tuple.rs b/library/coretests/benches/tuple.rs similarity index 100% rename from library/core/benches/tuple.rs rename to library/coretests/benches/tuple.rs diff --git a/library/coretests/lib.rs b/library/coretests/lib.rs new file mode 100644 index 0000000000000..b49208cd4eb3a --- /dev/null +++ b/library/coretests/lib.rs @@ -0,0 +1 @@ +// Intentionally left empty. diff --git a/library/core/tests/alloc.rs b/library/coretests/tests/alloc.rs similarity index 100% rename from library/core/tests/alloc.rs rename to library/coretests/tests/alloc.rs diff --git a/library/core/tests/any.rs b/library/coretests/tests/any.rs similarity index 100% rename from library/core/tests/any.rs rename to library/coretests/tests/any.rs diff --git a/library/core/tests/array.rs b/library/coretests/tests/array.rs similarity index 100% rename from library/core/tests/array.rs rename to library/coretests/tests/array.rs diff --git a/library/core/tests/ascii.rs b/library/coretests/tests/ascii.rs similarity index 100% rename from library/core/tests/ascii.rs rename to library/coretests/tests/ascii.rs diff --git a/library/coretests/tests/ascii_char.rs b/library/coretests/tests/ascii_char.rs new file mode 100644 index 0000000000000..f5a15a9469f3f --- /dev/null +++ b/library/coretests/tests/ascii_char.rs @@ -0,0 +1,40 @@ +use core::ascii::Char; +use core::fmt::Write; + +/// Tests Display implementation for ascii::Char. +#[test] +fn test_display() { + let want = (0..128u8).map(|b| b as char).collect::(); + let mut got = String::with_capacity(128); + for byte in 0..128 { + write!(&mut got, "{}", Char::from_u8(byte).unwrap()).unwrap(); + } + assert_eq!(want, got); +} + +/// Tests Debug implementation for ascii::Char. +#[test] +fn test_debug_control() { + for byte in 0..128u8 { + let mut want = format!("{:?}", byte as char); + // `char` uses `'\u{#}'` representation where ascii::char uses `'\x##'`. + // Transform former into the latter. + if let Some(rest) = want.strip_prefix("'\\u{") { + want = format!("'\\x{:0>2}'", rest.strip_suffix("}'").unwrap()); + } + let chr = core::ascii::Char::from_u8(byte).unwrap(); + assert_eq!(want, format!("{chr:?}"), "byte: {byte}"); + } +} + +/// Tests Extend implementation for ascii::Char. +#[test] +fn test_extend() { + let mut s = String::from("abc"); + s.extend_one(Char::SmallD); + assert_eq!(s, String::from("abcd")); + + let mut s = String::from("abc"); + s.extend(Char::CapitalA..=Char::CapitalC); + assert_eq!(s, String::from("abcABC")); +} diff --git a/library/core/tests/asserting.rs b/library/coretests/tests/asserting.rs similarity index 100% rename from library/core/tests/asserting.rs rename to library/coretests/tests/asserting.rs diff --git a/library/core/tests/async_iter/mod.rs b/library/coretests/tests/async_iter/mod.rs similarity index 100% rename from library/core/tests/async_iter/mod.rs rename to library/coretests/tests/async_iter/mod.rs diff --git a/library/core/tests/atomic.rs b/library/coretests/tests/atomic.rs similarity index 100% rename from library/core/tests/atomic.rs rename to library/coretests/tests/atomic.rs diff --git a/library/core/tests/bool.rs b/library/coretests/tests/bool.rs similarity index 96% rename from library/core/tests/bool.rs rename to library/coretests/tests/bool.rs index 47f6459915b3e..bcd6dc2abac6c 100644 --- a/library/core/tests/bool.rs +++ b/library/coretests/tests/bool.rs @@ -71,14 +71,14 @@ fn test_bool() { #[test] pub fn test_bool_not() { if !false { - assert!((true)); + assert!(true); } else { - assert!((false)); + assert!(false); } if !true { - assert!((false)); + assert!(false); } else { - assert!((true)); + assert!(true); } } diff --git a/library/coretests/tests/bstr.rs b/library/coretests/tests/bstr.rs new file mode 100644 index 0000000000000..cd4d69d6b337d --- /dev/null +++ b/library/coretests/tests/bstr.rs @@ -0,0 +1,52 @@ +use core::bstr::ByteStr; + +#[test] +fn test_debug() { + assert_eq!( + r#""\0\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x11\x12\r\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f \x7f\x80\x81\xfe\xff""#, + format!("{:?}", ByteStr::new(b"\0\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x11\x12\r\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f \x7f\x80\x81\xfe\xff")), + ); +} + +#[test] +fn test_display() { + let b1 = ByteStr::new("abc"); + let b2 = ByteStr::new(b"\xf0\x28\x8c\xbc"); + + assert_eq!(&format!("{b1}"), "abc"); + assert_eq!(&format!("{b2}"), "�(��"); + + assert_eq!(&format!("{b1:<7}!"), "abc !"); + assert_eq!(&format!("{b1:>7}!"), " abc!"); + assert_eq!(&format!("{b1:^7}!"), " abc !"); + assert_eq!(&format!("{b1:^6}!"), " abc !"); + assert_eq!(&format!("{b1:-<7}!"), "abc----!"); + assert_eq!(&format!("{b1:->7}!"), "----abc!"); + assert_eq!(&format!("{b1:-^7}!"), "--abc--!"); + assert_eq!(&format!("{b1:-^6}!"), "-abc--!"); + + assert_eq!(&format!("{b2:<7}!"), "�(�� !"); + assert_eq!(&format!("{b2:>7}!"), " �(��!"); + assert_eq!(&format!("{b2:^7}!"), " �(�� !"); + assert_eq!(&format!("{b2:^6}!"), " �(�� !"); + assert_eq!(&format!("{b2:-<7}!"), "�(��---!"); + assert_eq!(&format!("{b2:->7}!"), "---�(��!"); + assert_eq!(&format!("{b2:-^7}!"), "-�(��--!"); + assert_eq!(&format!("{b2:-^6}!"), "-�(��-!"); + + assert_eq!(&format!("{b1:<2}!"), "abc!"); + assert_eq!(&format!("{b1:>2}!"), "abc!"); + assert_eq!(&format!("{b1:^2}!"), "abc!"); + assert_eq!(&format!("{b1:-<2}!"), "abc!"); + assert_eq!(&format!("{b1:->2}!"), "abc!"); + assert_eq!(&format!("{b1:-^2}!"), "abc!"); + + assert_eq!(&format!("{b2:<3}!"), "�(��!"); + assert_eq!(&format!("{b2:>3}!"), "�(��!"); + assert_eq!(&format!("{b2:^3}!"), "�(��!"); + assert_eq!(&format!("{b2:^2}!"), "�(��!"); + assert_eq!(&format!("{b2:-<3}!"), "�(��!"); + assert_eq!(&format!("{b2:->3}!"), "�(��!"); + assert_eq!(&format!("{b2:-^3}!"), "�(��!"); + assert_eq!(&format!("{b2:-^2}!"), "�(��!"); +} diff --git a/library/core/tests/cell.rs b/library/coretests/tests/cell.rs similarity index 100% rename from library/core/tests/cell.rs rename to library/coretests/tests/cell.rs diff --git a/library/core/tests/char.rs b/library/coretests/tests/char.rs similarity index 100% rename from library/core/tests/char.rs rename to library/coretests/tests/char.rs diff --git a/library/core/tests/clone.rs b/library/coretests/tests/clone.rs similarity index 100% rename from library/core/tests/clone.rs rename to library/coretests/tests/clone.rs diff --git a/library/core/tests/cmp.rs b/library/coretests/tests/cmp.rs similarity index 100% rename from library/core/tests/cmp.rs rename to library/coretests/tests/cmp.rs diff --git a/library/core/tests/const_ptr.rs b/library/coretests/tests/const_ptr.rs similarity index 100% rename from library/core/tests/const_ptr.rs rename to library/coretests/tests/const_ptr.rs diff --git a/library/core/tests/convert.rs b/library/coretests/tests/convert.rs similarity index 100% rename from library/core/tests/convert.rs rename to library/coretests/tests/convert.rs diff --git a/library/core/tests/error.rs b/library/coretests/tests/error.rs similarity index 100% rename from library/core/tests/error.rs rename to library/coretests/tests/error.rs diff --git a/library/core/tests/ffi.rs b/library/coretests/tests/ffi.rs similarity index 100% rename from library/core/tests/ffi.rs rename to library/coretests/tests/ffi.rs diff --git a/library/core/tests/ffi/cstr.rs b/library/coretests/tests/ffi/cstr.rs similarity index 100% rename from library/core/tests/ffi/cstr.rs rename to library/coretests/tests/ffi/cstr.rs diff --git a/library/core/tests/fmt/builders.rs b/library/coretests/tests/fmt/builders.rs similarity index 100% rename from library/core/tests/fmt/builders.rs rename to library/coretests/tests/fmt/builders.rs diff --git a/library/core/tests/fmt/float.rs b/library/coretests/tests/fmt/float.rs similarity index 100% rename from library/core/tests/fmt/float.rs rename to library/coretests/tests/fmt/float.rs diff --git a/library/coretests/tests/fmt/mod.rs b/library/coretests/tests/fmt/mod.rs new file mode 100644 index 0000000000000..025c69c4f6236 --- /dev/null +++ b/library/coretests/tests/fmt/mod.rs @@ -0,0 +1,82 @@ +mod builders; +mod float; +mod num; + +#[test] +fn test_format_flags() { + // No residual flags left by pointer formatting + let p = "".as_ptr(); + assert_eq!(format!("{:p} {:x}", p, 16), format!("{p:p} 10")); + + assert_eq!(format!("{: >3}", 'a'), " a"); +} + +#[test] +fn test_pointer_formats_data_pointer() { + let b: &[u8] = b""; + let s: &str = ""; + assert_eq!(format!("{s:p}"), format!("{:p}", s.as_ptr())); + assert_eq!(format!("{b:p}"), format!("{:p}", b.as_ptr())); +} + +#[test] +fn test_estimated_capacity() { + assert_eq!(format_args!("").estimated_capacity(), 0); + assert_eq!(format_args!("{}", { "" }).estimated_capacity(), 0); + assert_eq!(format_args!("Hello").estimated_capacity(), 5); + assert_eq!(format_args!("Hello, {}!", { "" }).estimated_capacity(), 16); + assert_eq!(format_args!("{}, hello!", { "World" }).estimated_capacity(), 0); + assert_eq!(format_args!("{}. 16-bytes piece", { "World" }).estimated_capacity(), 32); +} + +#[test] +fn pad_integral_resets() { + struct Bar; + + impl core::fmt::Display for Bar { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + "1".fmt(f)?; + f.pad_integral(true, "", "5")?; + "1".fmt(f) + } + } + + assert_eq!(format!("{Bar:<03}"), "1 0051 "); +} + +#[test] +fn test_maybe_uninit_short() { + // Ensure that the trimmed `MaybeUninit` Debug implementation doesn't break + let x = core::mem::MaybeUninit::new(0u32); + assert_eq!(format!("{x:?}"), "MaybeUninit"); +} + +#[test] +fn formatting_options_ctor() { + use core::fmt::FormattingOptions; + assert_eq!(FormattingOptions::new(), FormattingOptions::default()); +} + +#[test] +fn formatting_options_flags() { + use core::fmt::*; + for sign in [None, Some(Sign::Plus), Some(Sign::Minus)] { + for alternate in [true, false] { + for sign_aware_zero_pad in [true, false] { + for debug_as_hex in [None, Some(DebugAsHex::Lower), Some(DebugAsHex::Upper)] { + let mut formatting_options = FormattingOptions::new(); + formatting_options + .sign(sign) + .sign_aware_zero_pad(sign_aware_zero_pad) + .alternate(alternate) + .debug_as_hex(debug_as_hex); + + assert_eq!(formatting_options.get_sign(), sign); + assert_eq!(formatting_options.get_alternate(), alternate); + assert_eq!(formatting_options.get_sign_aware_zero_pad(), sign_aware_zero_pad); + assert_eq!(formatting_options.get_debug_as_hex(), debug_as_hex); + } + } + } + } +} diff --git a/library/core/tests/fmt/num.rs b/library/coretests/tests/fmt/num.rs similarity index 100% rename from library/core/tests/fmt/num.rs rename to library/coretests/tests/fmt/num.rs diff --git a/library/core/tests/future.rs b/library/coretests/tests/future.rs similarity index 100% rename from library/core/tests/future.rs rename to library/coretests/tests/future.rs diff --git a/library/coretests/tests/hash/mod.rs b/library/coretests/tests/hash/mod.rs new file mode 100644 index 0000000000000..1f10a4733b053 --- /dev/null +++ b/library/coretests/tests/hash/mod.rs @@ -0,0 +1,167 @@ +mod sip; + +use std::hash::{BuildHasher, Hash, Hasher}; +use std::ptr; +use std::rc::Rc; + +#[derive(Default)] +struct MyHasher { + hash: u64, +} + +impl Hasher for MyHasher { + fn write(&mut self, buf: &[u8]) { + for byte in buf { + self.hash += *byte as u64; + } + } + fn write_str(&mut self, s: &str) { + self.write(s.as_bytes()); + self.write_u8(0xFF); + } + fn finish(&self) -> u64 { + self.hash + } +} + +#[test] +fn test_writer_hasher() { + // FIXME(#110395) + /* const */ + fn hash(t: &T) -> u64 { + let mut s = MyHasher { hash: 0 }; + t.hash(&mut s); + s.finish() + } + + /* const { + // FIXME(fee1-dead): assert_eq + assert!(hash(&()) == 0); + assert!(hash(&5_u8) == 5); + assert!(hash(&5_u16) == 5); + assert!(hash(&5_u32) == 5); + + assert!(hash(&'a') == 97); + + let s: &str = "a"; + assert!(hash(&s) == 97 + 0xFF); + }; */ + + assert_eq!(hash(&()), 0); + + assert_eq!(hash(&5_u8), 5); + assert_eq!(hash(&5_u16), 5); + assert_eq!(hash(&5_u32), 5); + assert_eq!(hash(&5_u64), 5); + assert_eq!(hash(&5_usize), 5); + + assert_eq!(hash(&5_i8), 5); + assert_eq!(hash(&5_i16), 5); + assert_eq!(hash(&5_i32), 5); + assert_eq!(hash(&5_i64), 5); + assert_eq!(hash(&5_isize), 5); + + assert_eq!(hash(&false), 0); + assert_eq!(hash(&true), 1); + + assert_eq!(hash(&'a'), 97); + + let s: &str = "a"; + assert_eq!(hash(&s), 97 + 0xFF); + let s: Box = String::from("a").into_boxed_str(); + assert_eq!(hash(&s), 97 + 0xFF); + let s: Rc<&str> = Rc::new("a"); + assert_eq!(hash(&s), 97 + 0xFF); + let cs: &[u8] = &[1, 2, 3]; + assert_eq!(hash(&cs), 9); + let cs: Box<[u8]> = Box::new([1, 2, 3]); + assert_eq!(hash(&cs), 9); + let cs: Rc<[u8]> = Rc::new([1, 2, 3]); + assert_eq!(hash(&cs), 9); + + let ptr = ptr::without_provenance::(5_usize); + assert_eq!(hash(&ptr), 5); + + let ptr = ptr::without_provenance_mut::(5_usize); + assert_eq!(hash(&ptr), 5); + + if cfg!(miri) { + // Miri cannot hash pointers + return; + } + + let cs: &mut [u8] = &mut [1, 2, 3]; + let ptr = cs.as_ptr(); + let slice_ptr = cs as *const [u8]; + assert_eq!(hash(&slice_ptr), hash(&ptr) + cs.len() as u64); + + let slice_ptr = cs as *mut [u8]; + assert_eq!(hash(&slice_ptr), hash(&ptr) + cs.len() as u64); +} + +struct Custom { + hash: u64, +} + +#[derive(Default)] +struct CustomHasher { + output: u64, +} + +impl Hasher for CustomHasher { + fn finish(&self) -> u64 { + self.output + } + fn write(&mut self, _: &[u8]) { + panic!() + } + fn write_u64(&mut self, data: u64) { + self.output = data; + } +} + +impl Hash for Custom { + fn hash(&self, state: &mut H) { + state.write_u64(self.hash); + } +} + +#[test] +fn test_custom_state() { + // FIXME(#110395) + /* const */ + fn hash(t: &T) -> u64 { + let mut c = CustomHasher { output: 0 }; + t.hash(&mut c); + c.finish() + } + + assert_eq!(hash(&Custom { hash: 5 }), 5); + + // const { assert!(hash(&Custom { hash: 6 }) == 6) }; +} + +#[test] +fn test_indirect_hasher() { + let mut hasher = MyHasher { hash: 0 }; + { + let mut indirect_hasher: &mut dyn Hasher = &mut hasher; + 5u32.hash(&mut indirect_hasher); + } + assert_eq!(hasher.hash, 5); +} + +#[test] +fn test_build_hasher_dyn_compatible() { + use std::hash::{DefaultHasher, RandomState}; + + let _: &dyn BuildHasher = &RandomState::new(); +} + +// just tests by whether or not this compiles +fn _build_hasher_default_impl_all_auto_traits() { + use std::panic::{RefUnwindSafe, UnwindSafe}; + fn all_auto_traits() {} + + all_auto_traits::>(); +} diff --git a/library/core/tests/hash/sip.rs b/library/coretests/tests/hash/sip.rs similarity index 100% rename from library/core/tests/hash/sip.rs rename to library/coretests/tests/hash/sip.rs diff --git a/library/core/tests/intrinsics.rs b/library/coretests/tests/intrinsics.rs similarity index 100% rename from library/core/tests/intrinsics.rs rename to library/coretests/tests/intrinsics.rs diff --git a/library/core/tests/io/borrowed_buf.rs b/library/coretests/tests/io/borrowed_buf.rs similarity index 96% rename from library/core/tests/io/borrowed_buf.rs rename to library/coretests/tests/io/borrowed_buf.rs index a5dd4e525777a..fbd3864dcac14 100644 --- a/library/core/tests/io/borrowed_buf.rs +++ b/library/coretests/tests/io/borrowed_buf.rs @@ -145,7 +145,7 @@ fn cursor_set_init() { assert_eq!(rbuf.unfilled().init_ref().len(), 8); assert_eq!(rbuf.unfilled().init_mut().len(), 8); assert_eq!(rbuf.unfilled().uninit_mut().len(), 8); - assert_eq!(unsafe { rbuf.unfilled().as_mut() }.len(), 16); + assert_eq!(unsafe { rbuf.unfilled().as_mut().len() }, 16); rbuf.unfilled().advance(4); @@ -163,5 +163,5 @@ fn cursor_set_init() { assert_eq!(rbuf.unfilled().init_ref().len(), 8); assert_eq!(rbuf.unfilled().init_mut().len(), 8); assert_eq!(rbuf.unfilled().uninit_mut().len(), 4); - assert_eq!(unsafe { rbuf.unfilled().as_mut() }.len(), 12); + assert_eq!(unsafe { rbuf.unfilled().as_mut().len() }, 12); } diff --git a/library/core/tests/io/mod.rs b/library/coretests/tests/io/mod.rs similarity index 100% rename from library/core/tests/io/mod.rs rename to library/coretests/tests/io/mod.rs diff --git a/library/core/tests/iter/adapters/array_chunks.rs b/library/coretests/tests/iter/adapters/array_chunks.rs similarity index 100% rename from library/core/tests/iter/adapters/array_chunks.rs rename to library/coretests/tests/iter/adapters/array_chunks.rs diff --git a/library/core/tests/iter/adapters/by_ref_sized.rs b/library/coretests/tests/iter/adapters/by_ref_sized.rs similarity index 100% rename from library/core/tests/iter/adapters/by_ref_sized.rs rename to library/coretests/tests/iter/adapters/by_ref_sized.rs diff --git a/library/core/tests/iter/adapters/chain.rs b/library/coretests/tests/iter/adapters/chain.rs similarity index 100% rename from library/core/tests/iter/adapters/chain.rs rename to library/coretests/tests/iter/adapters/chain.rs diff --git a/library/core/tests/iter/adapters/cloned.rs b/library/coretests/tests/iter/adapters/cloned.rs similarity index 100% rename from library/core/tests/iter/adapters/cloned.rs rename to library/coretests/tests/iter/adapters/cloned.rs diff --git a/library/core/tests/iter/adapters/copied.rs b/library/coretests/tests/iter/adapters/copied.rs similarity index 100% rename from library/core/tests/iter/adapters/copied.rs rename to library/coretests/tests/iter/adapters/copied.rs diff --git a/library/core/tests/iter/adapters/cycle.rs b/library/coretests/tests/iter/adapters/cycle.rs similarity index 100% rename from library/core/tests/iter/adapters/cycle.rs rename to library/coretests/tests/iter/adapters/cycle.rs diff --git a/library/core/tests/iter/adapters/enumerate.rs b/library/coretests/tests/iter/adapters/enumerate.rs similarity index 100% rename from library/core/tests/iter/adapters/enumerate.rs rename to library/coretests/tests/iter/adapters/enumerate.rs diff --git a/library/core/tests/iter/adapters/filter.rs b/library/coretests/tests/iter/adapters/filter.rs similarity index 100% rename from library/core/tests/iter/adapters/filter.rs rename to library/coretests/tests/iter/adapters/filter.rs diff --git a/library/core/tests/iter/adapters/filter_map.rs b/library/coretests/tests/iter/adapters/filter_map.rs similarity index 100% rename from library/core/tests/iter/adapters/filter_map.rs rename to library/coretests/tests/iter/adapters/filter_map.rs diff --git a/library/core/tests/iter/adapters/flat_map.rs b/library/coretests/tests/iter/adapters/flat_map.rs similarity index 100% rename from library/core/tests/iter/adapters/flat_map.rs rename to library/coretests/tests/iter/adapters/flat_map.rs diff --git a/library/core/tests/iter/adapters/flatten.rs b/library/coretests/tests/iter/adapters/flatten.rs similarity index 100% rename from library/core/tests/iter/adapters/flatten.rs rename to library/coretests/tests/iter/adapters/flatten.rs diff --git a/library/core/tests/iter/adapters/fuse.rs b/library/coretests/tests/iter/adapters/fuse.rs similarity index 100% rename from library/core/tests/iter/adapters/fuse.rs rename to library/coretests/tests/iter/adapters/fuse.rs diff --git a/library/core/tests/iter/adapters/inspect.rs b/library/coretests/tests/iter/adapters/inspect.rs similarity index 100% rename from library/core/tests/iter/adapters/inspect.rs rename to library/coretests/tests/iter/adapters/inspect.rs diff --git a/library/core/tests/iter/adapters/intersperse.rs b/library/coretests/tests/iter/adapters/intersperse.rs similarity index 100% rename from library/core/tests/iter/adapters/intersperse.rs rename to library/coretests/tests/iter/adapters/intersperse.rs diff --git a/library/core/tests/iter/adapters/map.rs b/library/coretests/tests/iter/adapters/map.rs similarity index 100% rename from library/core/tests/iter/adapters/map.rs rename to library/coretests/tests/iter/adapters/map.rs diff --git a/library/core/tests/iter/adapters/map_windows.rs b/library/coretests/tests/iter/adapters/map_windows.rs similarity index 98% rename from library/core/tests/iter/adapters/map_windows.rs rename to library/coretests/tests/iter/adapters/map_windows.rs index b677f1cfd55e7..01cebc9b27fd8 100644 --- a/library/core/tests/iter/adapters/map_windows.rs +++ b/library/coretests/tests/iter/adapters/map_windows.rs @@ -159,11 +159,10 @@ fn output_n2() { >::new(), ); assert_eq!("ab".chars().map_windows(|a: &[_; 2]| *a).collect::>(), vec![['a', 'b']]); - assert_eq!("abcd".chars().map_windows(|a: &[_; 2]| *a).collect::>(), vec![ - ['a', 'b'], - ['b', 'c'], - ['c', 'd'] - ],); + assert_eq!( + "abcd".chars().map_windows(|a: &[_; 2]| *a).collect::>(), + vec![['a', 'b'], ['b', 'c'], ['c', 'd']], + ); } #[test] diff --git a/library/core/tests/iter/adapters/mod.rs b/library/coretests/tests/iter/adapters/mod.rs similarity index 100% rename from library/core/tests/iter/adapters/mod.rs rename to library/coretests/tests/iter/adapters/mod.rs diff --git a/library/core/tests/iter/adapters/peekable.rs b/library/coretests/tests/iter/adapters/peekable.rs similarity index 100% rename from library/core/tests/iter/adapters/peekable.rs rename to library/coretests/tests/iter/adapters/peekable.rs diff --git a/library/core/tests/iter/adapters/scan.rs b/library/coretests/tests/iter/adapters/scan.rs similarity index 100% rename from library/core/tests/iter/adapters/scan.rs rename to library/coretests/tests/iter/adapters/scan.rs diff --git a/library/core/tests/iter/adapters/skip.rs b/library/coretests/tests/iter/adapters/skip.rs similarity index 100% rename from library/core/tests/iter/adapters/skip.rs rename to library/coretests/tests/iter/adapters/skip.rs diff --git a/library/core/tests/iter/adapters/skip_while.rs b/library/coretests/tests/iter/adapters/skip_while.rs similarity index 100% rename from library/core/tests/iter/adapters/skip_while.rs rename to library/coretests/tests/iter/adapters/skip_while.rs diff --git a/library/core/tests/iter/adapters/step_by.rs b/library/coretests/tests/iter/adapters/step_by.rs similarity index 100% rename from library/core/tests/iter/adapters/step_by.rs rename to library/coretests/tests/iter/adapters/step_by.rs diff --git a/library/core/tests/iter/adapters/take.rs b/library/coretests/tests/iter/adapters/take.rs similarity index 100% rename from library/core/tests/iter/adapters/take.rs rename to library/coretests/tests/iter/adapters/take.rs diff --git a/library/core/tests/iter/adapters/take_while.rs b/library/coretests/tests/iter/adapters/take_while.rs similarity index 100% rename from library/core/tests/iter/adapters/take_while.rs rename to library/coretests/tests/iter/adapters/take_while.rs diff --git a/library/core/tests/iter/adapters/zip.rs b/library/coretests/tests/iter/adapters/zip.rs similarity index 100% rename from library/core/tests/iter/adapters/zip.rs rename to library/coretests/tests/iter/adapters/zip.rs diff --git a/library/core/tests/iter/mod.rs b/library/coretests/tests/iter/mod.rs similarity index 100% rename from library/core/tests/iter/mod.rs rename to library/coretests/tests/iter/mod.rs diff --git a/library/core/tests/iter/range.rs b/library/coretests/tests/iter/range.rs similarity index 100% rename from library/core/tests/iter/range.rs rename to library/coretests/tests/iter/range.rs diff --git a/library/core/tests/iter/sources.rs b/library/coretests/tests/iter/sources.rs similarity index 100% rename from library/core/tests/iter/sources.rs rename to library/coretests/tests/iter/sources.rs diff --git a/library/core/tests/iter/traits/accum.rs b/library/coretests/tests/iter/traits/accum.rs similarity index 100% rename from library/core/tests/iter/traits/accum.rs rename to library/coretests/tests/iter/traits/accum.rs diff --git a/library/core/tests/iter/traits/double_ended.rs b/library/coretests/tests/iter/traits/double_ended.rs similarity index 100% rename from library/core/tests/iter/traits/double_ended.rs rename to library/coretests/tests/iter/traits/double_ended.rs diff --git a/library/core/tests/iter/traits/iterator.rs b/library/coretests/tests/iter/traits/iterator.rs similarity index 100% rename from library/core/tests/iter/traits/iterator.rs rename to library/coretests/tests/iter/traits/iterator.rs diff --git a/library/core/tests/iter/traits/mod.rs b/library/coretests/tests/iter/traits/mod.rs similarity index 100% rename from library/core/tests/iter/traits/mod.rs rename to library/coretests/tests/iter/traits/mod.rs diff --git a/library/core/tests/iter/traits/step.rs b/library/coretests/tests/iter/traits/step.rs similarity index 100% rename from library/core/tests/iter/traits/step.rs rename to library/coretests/tests/iter/traits/step.rs diff --git a/library/core/tests/lazy.rs b/library/coretests/tests/lazy.rs similarity index 100% rename from library/core/tests/lazy.rs rename to library/coretests/tests/lazy.rs diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs new file mode 100644 index 0000000000000..1e1ff29e16151 --- /dev/null +++ b/library/coretests/tests/lib.rs @@ -0,0 +1,194 @@ +// tidy-alphabetical-start +#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] +#![cfg_attr(test, feature(cfg_match))] +#![feature(alloc_layout_extra)] +#![feature(array_chunks)] +#![feature(array_ptr_get)] +#![feature(array_try_from_fn)] +#![feature(array_windows)] +#![feature(ascii_char)] +#![feature(ascii_char_variants)] +#![feature(async_iter_from_iter)] +#![feature(async_iterator)] +#![feature(bigint_helper_methods)] +#![feature(bstr)] +#![feature(cell_update)] +#![feature(clone_to_uninit)] +#![feature(const_eval_select)] +#![feature(const_swap_nonoverlapping)] +#![feature(const_trait_impl)] +#![feature(core_intrinsics)] +#![feature(core_intrinsics_fallbacks)] +#![feature(core_io_borrowed_buf)] +#![feature(core_private_bignum)] +#![feature(core_private_diy_float)] +#![feature(dec2flt)] +#![feature(duration_constants)] +#![feature(duration_constructors)] +#![feature(error_generic_member_access)] +#![feature(exact_size_is_empty)] +#![feature(extend_one)] +#![feature(extern_types)] +#![feature(float_minimum_maximum)] +#![feature(flt2dec)] +#![feature(fmt_internals)] +#![feature(formatting_options)] +#![feature(freeze)] +#![feature(future_join)] +#![feature(generic_assert_internals)] +#![feature(hasher_prefixfree_extras)] +#![feature(hashmap_internals)] +#![feature(inline_const_pat)] +#![feature(int_roundings)] +#![feature(ip)] +#![feature(ip_from)] +#![feature(is_ascii_octdigit)] +#![feature(iter_advance_by)] +#![feature(iter_array_chunks)] +#![feature(iter_chain)] +#![feature(iter_collect_into)] +#![feature(iter_intersperse)] +#![feature(iter_is_partitioned)] +#![feature(iter_map_windows)] +#![feature(iter_next_chunk)] +#![feature(iter_order_by)] +#![feature(iter_partition_in_place)] +#![feature(iterator_try_collect)] +#![feature(iterator_try_reduce)] +#![feature(layout_for_ptr)] +#![feature(lazy_get)] +#![feature(maybe_uninit_fill)] +#![feature(maybe_uninit_uninit_array_transpose)] +#![feature(maybe_uninit_write_slice)] +#![feature(min_specialization)] +#![feature(never_type)] +#![feature(num_midpoint_signed)] +#![feature(numfmt)] +#![feature(pattern)] +#![feature(pointer_is_aligned_to)] +#![feature(portable_simd)] +#![feature(ptr_metadata)] +#![feature(slice_from_ptr_range)] +#![feature(slice_internals)] +#![feature(slice_partition_dedup)] +#![feature(slice_split_once)] +#![feature(slice_take)] +#![feature(split_array)] +#![feature(split_as_slice)] +#![feature(std_internals)] +#![feature(step_trait)] +#![feature(str_internals)] +#![feature(strict_provenance_atomic_ptr)] +#![feature(strict_provenance_lints)] +#![feature(test)] +#![feature(trusted_len)] +#![feature(trusted_random_access)] +#![feature(try_blocks)] +#![feature(try_find)] +#![feature(try_trait_v2)] +#![feature(unsigned_is_multiple_of)] +#![feature(unsize)] +#![feature(unsized_tuple_coercion)] +#![feature(unwrap_infallible)] +// tidy-alphabetical-end +#![allow(internal_features)] +#![deny(fuzzy_provenance_casts)] +#![deny(unsafe_op_in_unsafe_fn)] + +/// Version of `assert_matches` that ignores fancy runtime printing in const context and uses structural equality. +macro_rules! assert_eq_const_safe { + ($left:expr, $right:expr) => { + assert_eq_const_safe!($left, $right, concat!(stringify!($left), " == ", stringify!($right))); + }; + ($left:expr, $right:expr$(, $($arg:tt)+)?) => { + { + fn runtime() { + assert_eq!($left, $right, $($($arg)*),*); + } + const fn compiletime() { + assert!(matches!($left, const { $right })); + } + core::intrinsics::const_eval_select((), compiletime, runtime) + } + }; +} + +/// Creates a test for runtime and a test for constant-time. +macro_rules! test_runtime_and_compiletime { + ($( + $(#[$attr:meta])* + fn $test:ident() $block:block + )*) => { + $( + $(#[$attr])* + #[test] + fn $test() $block + $(#[$attr])* + const _: () = $block; + )* + } +} + +mod alloc; +mod any; +mod array; +mod ascii; +mod ascii_char; +mod asserting; +mod async_iter; +mod atomic; +mod bool; +mod bstr; +mod cell; +mod char; +mod clone; +mod cmp; +mod const_ptr; +mod convert; +mod ffi; +mod fmt; +mod future; +mod hash; +mod intrinsics; +mod io; +mod iter; +mod lazy; +#[cfg(not(bootstrap))] +mod macros; +#[cfg(bootstrap)] +mod macros_bootstrap; +mod manually_drop; +mod mem; +mod net; +mod nonzero; +mod num; +mod ops; +mod option; +mod panic; +mod pattern; +mod pin; +mod pin_macro; +mod ptr; +mod result; +mod simd; +mod slice; +mod str; +mod str_lossy; +mod task; +mod time; +mod tuple; +mod unicode; +mod waker; + +/// Copied from `std::test_helpers::test_rng`, see that function for rationale. +#[track_caller] +#[allow(dead_code)] // Not used in all configurations. +pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { + use core::hash::{BuildHasher, Hash, Hasher}; + let mut hasher = std::hash::RandomState::new().build_hasher(); + core::panic::Location::caller().hash(&mut hasher); + let hc64 = hasher.finish(); + let seed_vec = hc64.to_le_bytes().into_iter().chain(0u8..8).collect::>(); + let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap(); + rand::SeedableRng::from_seed(seed) +} diff --git a/library/coretests/tests/macros.rs b/library/coretests/tests/macros.rs new file mode 100644 index 0000000000000..b30a40b7df28e --- /dev/null +++ b/library/coretests/tests/macros.rs @@ -0,0 +1,206 @@ +#![allow(unused_must_use)] + +#[allow(dead_code)] +trait Trait { + fn blah(&self); +} + +#[allow(dead_code)] +struct Struct; + +impl Trait for Struct { + cfg_match! { + feature = "blah" => { + fn blah(&self) { + unimplemented!(); + } + } + _ => { + fn blah(&self) { + unimplemented!(); + } + } + } +} + +#[test] +fn assert_eq_trailing_comma() { + assert_eq!(1, 1,); +} + +#[test] +fn assert_escape() { + assert!(r#"☃\backslash"#.contains("\\")); +} + +#[test] +fn assert_ne_trailing_comma() { + assert_ne!(1, 2,); +} + +#[rustfmt::skip] +#[test] +fn matches_leading_pipe() { + matches!(1, | 1 | 2 | 3); +} + +#[test] +fn cfg_match_basic() { + cfg_match! { + target_pointer_width = "64" => { fn f0_() -> bool { true }} + } + + cfg_match! { + unix => { fn f1_() -> bool { true } } + any(target_os = "macos", target_os = "linux") => { fn f1_() -> bool { false }} + } + + cfg_match! { + target_pointer_width = "32" => { fn f2_() -> bool { false } } + target_pointer_width = "64" => { fn f2_() -> bool { true } } + } + + cfg_match! { + target_pointer_width = "16" => { fn f3_() -> i32 { 1 } } + _ => { fn f3_() -> i32 { 2 }} + } + + #[cfg(target_pointer_width = "64")] + assert!(f0_()); + + #[cfg(unix)] + assert!(f1_()); + + #[cfg(target_pointer_width = "32")] + assert!(!f2_()); + #[cfg(target_pointer_width = "64")] + assert!(f2_()); + + #[cfg(not(target_pointer_width = "16"))] + assert_eq!(f3_(), 2); +} + +#[test] +fn cfg_match_debug_assertions() { + cfg_match! { + debug_assertions => { + assert!(cfg!(debug_assertions)); + assert_eq!(4, 2+2); + } + _ => { + assert!(cfg!(not(debug_assertions))); + assert_eq!(10, 5+5); + } + } +} + +#[cfg(target_pointer_width = "64")] +#[test] +fn cfg_match_no_duplication_on_64() { + cfg_match! { + windows => { + fn foo() {} + } + unix => { + fn foo() {} + } + target_pointer_width = "64" => { + fn foo() {} + } + } + foo(); +} + +#[test] +fn cfg_match_options() { + cfg_match! { + test => { + use core::option::Option as Option2; + fn works1() -> Option2 { Some(1) } + } + _ => { fn works1() -> Option { None } } + } + + cfg_match! { + feature = "foo" => { fn works2() -> bool { false } } + test => { fn works2() -> bool { true } } + _ => { fn works2() -> bool { false } } + } + + cfg_match! { + feature = "foo" => { fn works3() -> bool { false } } + _ => { fn works3() -> bool { true } } + } + + cfg_match! { + test => { + use core::option::Option as Option3; + fn works4() -> Option3 { Some(1) } + } + } + + cfg_match! { + feature = "foo" => { fn works5() -> bool { false } } + test => { fn works5() -> bool { true } } + } + + assert!(works1().is_some()); + assert!(works2()); + assert!(works3()); + assert!(works4().is_some()); + assert!(works5()); +} + +#[test] +fn cfg_match_two_functions() { + cfg_match! { + target_pointer_width = "64" => { + fn foo1() {} + fn bar1() {} + } + _ => { + fn foo2() {} + fn bar2() {} + } + } + + #[cfg(target_pointer_width = "64")] + { + foo1(); + bar1(); + } + #[cfg(not(target_pointer_width = "64"))] + { + foo2(); + bar2(); + } +} + +fn _accepts_expressions() -> i32 { + cfg_match! { + unix => { 1 } + _ => { 2 } + } +} + +// The current implementation expands to a macro call, which allows the use of expression +// statements. +fn _allows_stmt_expr_attributes() { + let one = 1; + let two = 2; + cfg_match! { + unix => { one * two; } + _ => { one + two; } + } +} + +fn _expression() { + let _ = cfg_match!({ + windows => { + " XP" + } + _ => { + "" + } + }); +} diff --git a/library/coretests/tests/macros_bootstrap.rs b/library/coretests/tests/macros_bootstrap.rs new file mode 100644 index 0000000000000..f10ef862c5dd9 --- /dev/null +++ b/library/coretests/tests/macros_bootstrap.rs @@ -0,0 +1,193 @@ +#![allow(unused_must_use)] + +#[allow(dead_code)] +trait Trait { + fn blah(&self); +} + +#[allow(dead_code)] +struct Struct; + +impl Trait for Struct { + cfg_match! { + cfg(feature = "blah") => { + fn blah(&self) { + unimplemented!(); + } + } + _ => { + fn blah(&self) { + unimplemented!(); + } + } + } +} + +#[test] +fn assert_eq_trailing_comma() { + assert_eq!(1, 1,); +} + +#[test] +fn assert_escape() { + assert!(r#"☃\backslash"#.contains("\\")); +} + +#[test] +fn assert_ne_trailing_comma() { + assert_ne!(1, 2,); +} + +#[rustfmt::skip] +#[test] +fn matches_leading_pipe() { + matches!(1, | 1 | 2 | 3); +} + +#[test] +fn cfg_match_basic() { + cfg_match! { + cfg(target_pointer_width = "64") => { fn f0_() -> bool { true }} + } + + cfg_match! { + cfg(unix) => { fn f1_() -> bool { true }} + cfg(any(target_os = "macos", target_os = "linux")) => { fn f1_() -> bool { false }} + } + + cfg_match! { + cfg(target_pointer_width = "32") => { fn f2_() -> bool { false }} + cfg(target_pointer_width = "64") => { fn f2_() -> bool { true }} + } + + cfg_match! { + cfg(target_pointer_width = "16") => { fn f3_() -> i32 { 1 }} + _ => { fn f3_() -> i32 { 2 }} + } + + #[cfg(target_pointer_width = "64")] + assert!(f0_()); + + #[cfg(unix)] + assert!(f1_()); + + #[cfg(target_pointer_width = "32")] + assert!(!f2_()); + #[cfg(target_pointer_width = "64")] + assert!(f2_()); + + #[cfg(not(target_pointer_width = "16"))] + assert_eq!(f3_(), 2); +} + +#[test] +fn cfg_match_debug_assertions() { + cfg_match! { + cfg(debug_assertions) => { + assert!(cfg!(debug_assertions)); + assert_eq!(4, 2+2); + } + _ => { + assert!(cfg!(not(debug_assertions))); + assert_eq!(10, 5+5); + } + } +} + +#[cfg(target_pointer_width = "64")] +#[test] +fn cfg_match_no_duplication_on_64() { + cfg_match! { + cfg(windows) => { + fn foo() {} + } + cfg(unix) => { + fn foo() {} + } + cfg(target_pointer_width = "64") => { + fn foo() {} + } + } + foo(); +} + +#[test] +fn cfg_match_options() { + cfg_match! { + cfg(test) => { + use core::option::Option as Option2; + fn works1() -> Option2 { Some(1) } + } + _ => { fn works1() -> Option { None } } + } + + cfg_match! { + cfg(feature = "foo") => { fn works2() -> bool { false } } + cfg(test) => { fn works2() -> bool { true } } + _ => { fn works2() -> bool { false } } + } + + cfg_match! { + cfg(feature = "foo") => { fn works3() -> bool { false } } + _ => { fn works3() -> bool { true } } + } + + cfg_match! { + cfg(test) => { + use core::option::Option as Option3; + fn works4() -> Option3 { Some(1) } + } + } + + cfg_match! { + cfg(feature = "foo") => { fn works5() -> bool { false } } + cfg(test) => { fn works5() -> bool { true } } + } + + assert!(works1().is_some()); + assert!(works2()); + assert!(works3()); + assert!(works4().is_some()); + assert!(works5()); +} + +#[test] +fn cfg_match_two_functions() { + cfg_match! { + cfg(target_pointer_width = "64") => { + fn foo1() {} + fn bar1() {} + } + _ => { + fn foo2() {} + fn bar2() {} + } + } + + #[cfg(target_pointer_width = "64")] + { + foo1(); + bar1(); + } + #[cfg(not(target_pointer_width = "64"))] + { + foo2(); + bar2(); + } +} + +fn _accepts_expressions() -> i32 { + cfg_match! { + cfg(unix) => { 1 } + _ => { 2 } + } +} + +fn _allows_stmt_expr_attributes() { + let one = 1; + let two = 2; + cfg_match! { + cfg(unix) => { one * two; } + _ => { one + two; } + } +} diff --git a/library/core/tests/manually_drop.rs b/library/coretests/tests/manually_drop.rs similarity index 100% rename from library/core/tests/manually_drop.rs rename to library/coretests/tests/manually_drop.rs diff --git a/library/core/tests/mem.rs b/library/coretests/tests/mem.rs similarity index 95% rename from library/core/tests/mem.rs rename to library/coretests/tests/mem.rs index f3b4387f6a898..9cb94ca3b0ff0 100644 --- a/library/core/tests/mem.rs +++ b/library/coretests/tests/mem.rs @@ -200,60 +200,60 @@ fn uninit_array_assume_init() { } #[test] -fn uninit_write_slice() { +fn uninit_write_copy_of_slice() { let mut dst = [MaybeUninit::new(255); 64]; let src = [0; 64]; - assert_eq!(MaybeUninit::copy_from_slice(&mut dst, &src), &src); + assert_eq!(dst.write_copy_of_slice(&src), &src); } #[test] #[should_panic(expected = "source slice length (32) does not match destination slice length (64)")] -fn uninit_write_slice_panic_lt() { +fn uninit_write_copy_of_slice_panic_lt() { let mut dst = [MaybeUninit::uninit(); 64]; let src = [0; 32]; - MaybeUninit::copy_from_slice(&mut dst, &src); + dst.write_copy_of_slice(&src); } #[test] #[should_panic(expected = "source slice length (128) does not match destination slice length (64)")] -fn uninit_write_slice_panic_gt() { +fn uninit_write_copy_of_slice_panic_gt() { let mut dst = [MaybeUninit::uninit(); 64]; let src = [0; 128]; - MaybeUninit::copy_from_slice(&mut dst, &src); + dst.write_copy_of_slice(&src); } #[test] -fn uninit_clone_from_slice() { +fn uninit_write_clone_of_slice() { let mut dst = [MaybeUninit::new(255); 64]; let src = [0; 64]; - assert_eq!(MaybeUninit::clone_from_slice(&mut dst, &src), &src); + assert_eq!(dst.write_clone_of_slice(&src), &src); } #[test] #[should_panic(expected = "destination and source slices have different lengths")] -fn uninit_write_slice_cloned_panic_lt() { +fn uninit_write_clone_of_slice_panic_lt() { let mut dst = [MaybeUninit::uninit(); 64]; let src = [0; 32]; - MaybeUninit::clone_from_slice(&mut dst, &src); + dst.write_clone_of_slice(&src); } #[test] #[should_panic(expected = "destination and source slices have different lengths")] -fn uninit_write_slice_cloned_panic_gt() { +fn uninit_write_clone_of_slice_panic_gt() { let mut dst = [MaybeUninit::uninit(); 64]; let src = [0; 128]; - MaybeUninit::clone_from_slice(&mut dst, &src); + dst.write_clone_of_slice(&src); } #[test] #[cfg(panic = "unwind")] -fn uninit_write_slice_cloned_mid_panic() { +fn uninit_write_clone_of_slice_mid_panic() { use std::panic; enum IncrementOrPanic { @@ -289,7 +289,7 @@ fn uninit_write_slice_cloned_mid_panic() { ]; let err = panic::catch_unwind(panic::AssertUnwindSafe(|| { - MaybeUninit::clone_from_slice(&mut dst, &src); + dst.write_clone_of_slice(&src); })); drop(src); @@ -317,11 +317,11 @@ impl Drop for Bomb { } #[test] -fn uninit_write_slice_cloned_no_drop() { +fn uninit_write_clone_of_slice_no_drop() { let mut dst = [MaybeUninit::uninit()]; let src = [Bomb]; - MaybeUninit::clone_from_slice(&mut dst, &src); + dst.write_clone_of_slice(&src); forget(src); } @@ -648,7 +648,7 @@ fn offset_of_dst() { z: dyn Trait, } - extern "C" { + unsafe extern "C" { type Extern; } diff --git a/library/core/tests/net/ip_addr.rs b/library/coretests/tests/net/ip_addr.rs similarity index 99% rename from library/core/tests/net/ip_addr.rs rename to library/coretests/tests/net/ip_addr.rs index 707f9a160e127..f01b43282ec42 100644 --- a/library/core/tests/net/ip_addr.rs +++ b/library/coretests/tests/net/ip_addr.rs @@ -332,6 +332,7 @@ fn ip_properties() { check!("ff08::", global | multicast); check!("ff0e::", global | multicast); check!("2001:db8:85a3::8a2e:370:7334", doc); + check!("3fff:fff:ffff:ffff:ffff:ffff:ffff:ffff", doc); check!("2001:2::ac32:23ff:21", benchmarking); check!("102:304:506:708:90a:b0c:d0e:f10", global); } @@ -790,6 +791,15 @@ fn ipv6_properties() { documentation ); + check!( + "3fff:fff:ffff:ffff:ffff:ffff:ffff:ffff", + &[ + 0x3f, 0xff, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff + ], + documentation + ); + check!( "2001:2::ac32:23ff:21", &[0x20, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0xac, 0x32, 0x23, 0xff, 0, 0x21], diff --git a/library/core/tests/net/mod.rs b/library/coretests/tests/net/mod.rs similarity index 100% rename from library/core/tests/net/mod.rs rename to library/coretests/tests/net/mod.rs diff --git a/library/core/tests/net/parser.rs b/library/coretests/tests/net/parser.rs similarity index 100% rename from library/core/tests/net/parser.rs rename to library/coretests/tests/net/parser.rs diff --git a/library/core/tests/net/socket_addr.rs b/library/coretests/tests/net/socket_addr.rs similarity index 100% rename from library/core/tests/net/socket_addr.rs rename to library/coretests/tests/net/socket_addr.rs diff --git a/library/core/tests/nonzero.rs b/library/coretests/tests/nonzero.rs similarity index 100% rename from library/core/tests/nonzero.rs rename to library/coretests/tests/nonzero.rs diff --git a/library/core/tests/num/bignum.rs b/library/coretests/tests/num/bignum.rs similarity index 100% rename from library/core/tests/num/bignum.rs rename to library/coretests/tests/num/bignum.rs diff --git a/library/core/tests/num/const_from.rs b/library/coretests/tests/num/const_from.rs similarity index 100% rename from library/core/tests/num/const_from.rs rename to library/coretests/tests/num/const_from.rs diff --git a/library/core/tests/num/dec2flt/float.rs b/library/coretests/tests/num/dec2flt/float.rs similarity index 100% rename from library/core/tests/num/dec2flt/float.rs rename to library/coretests/tests/num/dec2flt/float.rs diff --git a/library/core/tests/num/dec2flt/lemire.rs b/library/coretests/tests/num/dec2flt/lemire.rs similarity index 100% rename from library/core/tests/num/dec2flt/lemire.rs rename to library/coretests/tests/num/dec2flt/lemire.rs diff --git a/library/core/tests/num/dec2flt/mod.rs b/library/coretests/tests/num/dec2flt/mod.rs similarity index 100% rename from library/core/tests/num/dec2flt/mod.rs rename to library/coretests/tests/num/dec2flt/mod.rs diff --git a/library/core/tests/num/dec2flt/parse.rs b/library/coretests/tests/num/dec2flt/parse.rs similarity index 100% rename from library/core/tests/num/dec2flt/parse.rs rename to library/coretests/tests/num/dec2flt/parse.rs diff --git a/library/core/tests/num/float_iter_sum_identity.rs b/library/coretests/tests/num/float_iter_sum_identity.rs similarity index 100% rename from library/core/tests/num/float_iter_sum_identity.rs rename to library/coretests/tests/num/float_iter_sum_identity.rs diff --git a/library/core/tests/num/flt2dec/estimator.rs b/library/coretests/tests/num/flt2dec/estimator.rs similarity index 100% rename from library/core/tests/num/flt2dec/estimator.rs rename to library/coretests/tests/num/flt2dec/estimator.rs diff --git a/library/coretests/tests/num/flt2dec/mod.rs b/library/coretests/tests/num/flt2dec/mod.rs new file mode 100644 index 0000000000000..6041923117c2a --- /dev/null +++ b/library/coretests/tests/num/flt2dec/mod.rs @@ -0,0 +1,1168 @@ +use core::num::flt2dec::{ + DecodableFloat, Decoded, FullDecoded, MAX_SIG_DIGITS, Sign, decode, round_up, to_exact_exp_str, + to_exact_fixed_str, to_shortest_exp_str, to_shortest_str, +}; +use core::num::fmt::{Formatted, Part}; +use std::mem::MaybeUninit; +use std::{fmt, str}; + +mod estimator; +mod strategy { + mod dragon; + mod grisu; +} +mod random; + +pub fn decode_finite(v: T) -> Decoded { + match decode(v).1 { + FullDecoded::Finite(decoded) => decoded, + full_decoded => panic!("expected finite, got {full_decoded:?} instead"), + } +} + +macro_rules! check_shortest { + ($f:ident($v:expr) => $buf:expr, $exp:expr) => ( + check_shortest!($f($v) => $buf, $exp; + "shortest mismatch for v={v}: actual {actual:?}, expected {expected:?}", + v = stringify!($v)) + ); + + ($f:ident{$($k:ident: $v:expr),+} => $buf:expr, $exp:expr) => ( + check_shortest!($f{$($k: $v),+} => $buf, $exp; + "shortest mismatch for {v:?}: actual {actual:?}, expected {expected:?}", + v = Decoded { $($k: $v),+ }) + ); + + ($f:ident($v:expr) => $buf:expr, $exp:expr; $fmt:expr, $($key:ident = $val:expr),*) => ({ + let mut buf = [MaybeUninit::new(b'_'); MAX_SIG_DIGITS]; + let (buf, k) = $f(&decode_finite($v), &mut buf); + assert!((buf, k) == ($buf, $exp), + $fmt, actual = (str::from_utf8(buf).unwrap(), k), + expected = (str::from_utf8($buf).unwrap(), $exp), + $($key = $val),*); + }); + + ($f:ident{$($k:ident: $v:expr),+} => $buf:expr, $exp:expr; + $fmt:expr, $($key:ident = $val:expr),*) => ({ + let mut buf = [MaybeUninit::new(b'_'); MAX_SIG_DIGITS]; + let (buf, k) = $f(&Decoded { $($k: $v),+ }, &mut buf); + assert!((buf, k) == ($buf, $exp), + $fmt, actual = (str::from_utf8(buf).unwrap(), k), + expected = (str::from_utf8($buf).unwrap(), $exp), + $($key = $val),*); + }) +} + +macro_rules! try_exact { + ($f:ident($decoded:expr) => $buf:expr, $expected:expr, $expectedk:expr; + $fmt:expr, $($key:ident = $val:expr),*) => ({ + let (buf, k) = $f($decoded, &mut $buf[..$expected.len()], i16::MIN); + assert!((buf, k) == ($expected, $expectedk), + $fmt, actual = (str::from_utf8(buf).unwrap(), k), + expected = (str::from_utf8($expected).unwrap(), $expectedk), + $($key = $val),*); + }) +} + +macro_rules! try_fixed { + ($f:ident($decoded:expr) => $buf:expr, $request:expr, $expected:expr, $expectedk:expr; + $fmt:expr, $($key:ident = $val:expr),*) => ({ + let (buf, k) = $f($decoded, &mut $buf[..], $request); + assert!((buf, k) == ($expected, $expectedk), + $fmt, actual = (str::from_utf8(buf).unwrap(), k), + expected = (str::from_utf8($expected).unwrap(), $expectedk), + $($key = $val),*); + }) +} + +fn ldexp_f32(a: f32, b: i32) -> f32 { + ldexp_f64(a as f64, b) as f32 +} + +fn ldexp_f64(a: f64, b: i32) -> f64 { + unsafe extern "C" { + fn ldexp(x: f64, n: i32) -> f64; + } + // SAFETY: assuming a correct `ldexp` has been supplied, the given arguments cannot possibly + // cause undefined behavior + unsafe { ldexp(a, b) } +} + +fn check_exact(mut f: F, v: T, vstr: &str, expected: &[u8], expectedk: i16) +where + T: DecodableFloat, + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), +{ + // use a large enough buffer + let mut buf = [MaybeUninit::new(b'_'); 1024]; + let mut expected_ = [b'_'; 1024]; + + let decoded = decode_finite(v); + let cut = expected.iter().position(|&c| c == b' '); + + // check significant digits + for i in 1..cut.unwrap_or(expected.len() - 1) { + expected_[..i].copy_from_slice(&expected[..i]); + let mut expectedk_ = expectedk; + if expected[i] >= b'5' { + // check if this is a rounding-to-even case. + // we avoid rounding ...x5000... (with infinite zeroes) to ...(x+1) when x is even. + if !(i + 1 < expected.len() + && expected[i - 1] & 1 == 0 + && expected[i] == b'5' + && expected[i + 1] == b' ') + { + // if this returns true, expected_[..i] is all `9`s and being rounded up. + // we should always return `100..00` (`i` digits) instead, since that's + // what we can came up with `i` digits anyway. `round_up` assumes that + // the adjustment to the length is done by caller, which we simply ignore. + if let Some(_) = round_up(&mut expected_[..i]) { + expectedk_ += 1; + } + } + } + + try_exact!(f(&decoded) => &mut buf, &expected_[..i], expectedk_; + "exact sigdigit mismatch for v={v}, i={i}: \ + actual {actual:?}, expected {expected:?}", + v = vstr, i = i); + try_fixed!(f(&decoded) => &mut buf, expectedk_ - i as i16, &expected_[..i], expectedk_; + "fixed sigdigit mismatch for v={v}, i={i}: \ + actual {actual:?}, expected {expected:?}", + v = vstr, i = i); + } + + // check exact rounding for zero- and negative-width cases + let start; + if expected[0] > b'5' { + try_fixed!(f(&decoded) => &mut buf, expectedk, b"1", expectedk + 1; + "zero-width rounding-up mismatch for v={v}: \ + actual {actual:?}, expected {expected:?}", + v = vstr); + start = 1; + } else { + start = 0; + } + for i in start..-10 { + try_fixed!(f(&decoded) => &mut buf, expectedk - i, b"", expectedk; + "rounding-down mismatch for v={v}, i={i}: \ + actual {actual:?}, expected {expected:?}", + v = vstr, i = -i); + } + + // check infinite zero digits + if let Some(cut) = cut { + for i in cut..expected.len() - 1 { + expected_[..cut].copy_from_slice(&expected[..cut]); + for c in &mut expected_[cut..i] { + *c = b'0'; + } + + try_exact!(f(&decoded) => &mut buf, &expected_[..i], expectedk; + "exact infzero mismatch for v={v}, i={i}: \ + actual {actual:?}, expected {expected:?}", + v = vstr, i = i); + try_fixed!(f(&decoded) => &mut buf, expectedk - i as i16, &expected_[..i], expectedk; + "fixed infzero mismatch for v={v}, i={i}: \ + actual {actual:?}, expected {expected:?}", + v = vstr, i = i); + } + } +} + +trait TestableFloat: DecodableFloat + fmt::Display { + /// Returns `x * 2^exp`. Almost same to `std::{f32,f64}::ldexp`. + /// This is used for testing. + fn ldexpi(f: i64, exp: isize) -> Self; +} + +impl TestableFloat for f32 { + fn ldexpi(f: i64, exp: isize) -> Self { + f as Self * (exp as Self).exp2() + } +} + +impl TestableFloat for f64 { + fn ldexpi(f: i64, exp: isize) -> Self { + f as Self * (exp as Self).exp2() + } +} + +fn check_exact_one(mut f: F, x: i64, e: isize, tstr: &str, expected: &[u8], expectedk: i16) +where + T: TestableFloat, + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), +{ + // use a large enough buffer + let mut buf = [MaybeUninit::new(b'_'); 1024]; + let v: T = TestableFloat::ldexpi(x, e); + let decoded = decode_finite(v); + + try_exact!(f(&decoded) => &mut buf, &expected, expectedk; + "exact mismatch for v={x}p{e}{t}: actual {actual:?}, expected {expected:?}", + x = x, e = e, t = tstr); + try_fixed!(f(&decoded) => &mut buf, expectedk - expected.len() as i16, &expected, expectedk; + "fixed mismatch for v={x}p{e}{t}: actual {actual:?}, expected {expected:?}", + x = x, e = e, t = tstr); +} + +macro_rules! check_exact { + ($f:ident($v:expr) => $buf:expr, $exp:expr) => { + check_exact(|d, b, k| $f(d, b, k), $v, stringify!($v), $buf, $exp) + }; +} + +macro_rules! check_exact_one { + ($f:ident($x:expr, $e:expr; $t:ty) => $buf:expr, $exp:expr) => { + check_exact_one::<_, $t>(|d, b, k| $f(d, b, k), $x, $e, stringify!($t), $buf, $exp) + }; +} + +// in the following comments, three numbers are spaced by 1 ulp apart, +// and the second one is being formatted. +// +// some tests are derived from [1]. +// +// [1] Vern Paxson, A Program for Testing IEEE Decimal-Binary Conversion +// ftp://ftp.ee.lbl.gov/testbase-report.ps.Z + +pub fn f32_shortest_sanity_test(mut f: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +{ + // 0.0999999940395355224609375 + // 0.100000001490116119384765625 + // 0.10000000894069671630859375 + check_shortest!(f(0.1f32) => b"1", 0); + + // 0.333333313465118408203125 + // 0.3333333432674407958984375 (1/3 in the default rounding) + // 0.33333337306976318359375 + check_shortest!(f(1.0f32/3.0) => b"33333334", 0); + + // 10^1 * 0.31415917873382568359375 + // 10^1 * 0.31415920257568359375 + // 10^1 * 0.31415922641754150390625 + check_shortest!(f(3.141592f32) => b"3141592", 1); + + // 10^18 * 0.31415916243714048 + // 10^18 * 0.314159196796878848 + // 10^18 * 0.314159231156617216 + check_shortest!(f(3.141592e17f32) => b"3141592", 18); + + // regression test for decoders + // 10^8 * 0.3355443 + // 10^8 * 0.33554432 + // 10^8 * 0.33554436 + check_shortest!(f(ldexp_f32(1.0, 25)) => b"33554432", 8); + + // 10^39 * 0.340282326356119256160033759537265639424 + // 10^39 * 0.34028234663852885981170418348451692544 + // 10^39 * 0.340282366920938463463374607431768211456 + check_shortest!(f(f32::MAX) => b"34028235", 39); + + // 10^-37 * 0.1175494210692441075487029444849287348827... + // 10^-37 * 0.1175494350822287507968736537222245677818... + // 10^-37 * 0.1175494490952133940450443629595204006810... + check_shortest!(f(f32::MIN_POSITIVE) => b"11754944", -37); + + // 10^-44 * 0 + // 10^-44 * 0.1401298464324817070923729583289916131280... + // 10^-44 * 0.2802596928649634141847459166579832262560... + let minf32 = ldexp_f32(1.0, -149); + check_shortest!(f(minf32) => b"1", -44); +} + +pub fn f32_exact_sanity_test(mut f: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), +{ + let minf32 = ldexp_f32(1.0, -149); + + check_exact!(f(0.1f32) => b"100000001490116119384765625 ", 0); + check_exact!(f(0.5f32) => b"5 ", 0); + check_exact!(f(1.0f32/3.0) => b"3333333432674407958984375 ", 0); + check_exact!(f(3.141592f32) => b"31415920257568359375 ", 1); + check_exact!(f(3.141592e17f32) => b"314159196796878848 ", 18); + check_exact!(f(f32::MAX) => b"34028234663852885981170418348451692544 ", 39); + check_exact!(f(f32::MIN_POSITIVE) => b"1175494350822287507968736537222245677818", -37); + check_exact!(f(minf32) => b"1401298464324817070923729583289916131280", -44); + + // [1], Table 16: Stress Inputs for Converting 24-bit Binary to Decimal, < 1/2 ULP + check_exact_one!(f(12676506, -102; f32) => b"2", -23); + check_exact_one!(f(12676506, -103; f32) => b"12", -23); + check_exact_one!(f(15445013, 86; f32) => b"119", 34); + check_exact_one!(f(13734123, -138; f32) => b"3941", -34); + check_exact_one!(f(12428269, -130; f32) => b"91308", -32); + check_exact_one!(f(15334037, -146; f32) => b"171900", -36); + check_exact_one!(f(11518287, -41; f32) => b"5237910", -5); + check_exact_one!(f(12584953, -145; f32) => b"28216440", -36); + check_exact_one!(f(15961084, -125; f32) => b"375243281", -30); + check_exact_one!(f(14915817, -146; f32) => b"1672120916", -36); + check_exact_one!(f(10845484, -102; f32) => b"21388945814", -23); + check_exact_one!(f(16431059, -61; f32) => b"712583594561", -11); + + // [1], Table 17: Stress Inputs for Converting 24-bit Binary to Decimal, > 1/2 ULP + check_exact_one!(f(16093626, 69; f32) => b"1", 29); + check_exact_one!(f( 9983778, 25; f32) => b"34", 15); + check_exact_one!(f(12745034, 104; f32) => b"259", 39); + check_exact_one!(f(12706553, 72; f32) => b"6001", 29); + check_exact_one!(f(11005028, 45; f32) => b"38721", 21); + check_exact_one!(f(15059547, 71; f32) => b"355584", 29); + check_exact_one!(f(16015691, -99; f32) => b"2526831", -22); + check_exact_one!(f( 8667859, 56; f32) => b"62458507", 24); + check_exact_one!(f(14855922, -82; f32) => b"307213267", -17); + check_exact_one!(f(14855922, -83; f32) => b"1536066333", -17); + check_exact_one!(f(10144164, -110; f32) => b"78147796834", -26); + check_exact_one!(f(13248074, 95; f32) => b"524810279937", 36); +} + +pub fn f64_shortest_sanity_test(mut f: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +{ + // 0.0999999999999999777955395074968691915273... + // 0.1000000000000000055511151231257827021181... + // 0.1000000000000000333066907387546962127089... + check_shortest!(f(0.1f64) => b"1", 0); + + // this example is explicitly mentioned in the paper. + // 10^3 * 0.0999999999999999857891452847979962825775... + // 10^3 * 0.1 (exact) + // 10^3 * 0.1000000000000000142108547152020037174224... + check_shortest!(f(100.0f64) => b"1", 3); + + // 0.3333333333333332593184650249895639717578... + // 0.3333333333333333148296162562473909929394... (1/3 in the default rounding) + // 0.3333333333333333703407674875052180141210... + check_shortest!(f(1.0f64/3.0) => b"3333333333333333", 0); + + // explicit test case for equally closest representations. + // Dragon has its own tie-breaking rule; Grisu should fall back. + // 10^1 * 0.1000007629394531027955395074968691915273... + // 10^1 * 0.100000762939453125 (exact) + // 10^1 * 0.1000007629394531472044604925031308084726... + check_shortest!(f(1.00000762939453125f64) => b"10000076293945313", 1); + + // 10^1 * 0.3141591999999999718085064159822650253772... + // 10^1 * 0.3141592000000000162174274009885266423225... + // 10^1 * 0.3141592000000000606263483859947882592678... + check_shortest!(f(3.141592f64) => b"3141592", 1); + + // 10^18 * 0.314159199999999936 + // 10^18 * 0.3141592 (exact) + // 10^18 * 0.314159200000000064 + check_shortest!(f(3.141592e17f64) => b"3141592", 18); + + // regression test for decoders + // 10^20 * 0.18446744073709549568 + // 10^20 * 0.18446744073709551616 + // 10^20 * 0.18446744073709555712 + check_shortest!(f(ldexp_f64(1.0, 64)) => b"18446744073709552", 20); + + // pathological case: high = 10^23 (exact). tie breaking should always prefer that. + // 10^24 * 0.099999999999999974834176 + // 10^24 * 0.099999999999999991611392 + // 10^24 * 0.100000000000000008388608 + check_shortest!(f(1.0e23f64) => b"1", 24); + + // 10^309 * 0.1797693134862315508561243283845062402343... + // 10^309 * 0.1797693134862315708145274237317043567980... + // 10^309 * 0.1797693134862315907729305190789024733617... + check_shortest!(f(f64::MAX) => b"17976931348623157", 309); + + // 10^-307 * 0.2225073858507200889024586876085859887650... + // 10^-307 * 0.2225073858507201383090232717332404064219... + // 10^-307 * 0.2225073858507201877155878558578948240788... + check_shortest!(f(f64::MIN_POSITIVE) => b"22250738585072014", -307); + + // 10^-323 * 0 + // 10^-323 * 0.4940656458412465441765687928682213723650... + // 10^-323 * 0.9881312916824930883531375857364427447301... + let minf64 = ldexp_f64(1.0, -1074); + check_shortest!(f(minf64) => b"5", -323); +} + +pub fn f64_exact_sanity_test(mut f: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), +{ + let minf64 = ldexp_f64(1.0, -1074); + + check_exact!(f(0.1f64) => b"1000000000000000055511151231257827021181", 0); + check_exact!(f(0.45f64) => b"4500000000000000111022302462515654042363", 0); + check_exact!(f(0.5f64) => b"5 ", 0); + check_exact!(f(0.95f64) => b"9499999999999999555910790149937383830547", 0); + check_exact!(f(100.0f64) => b"1 ", 3); + check_exact!(f(999.5f64) => b"9995000000000000000000000000000000000000", 3); + check_exact!(f(1.0f64/3.0) => b"3333333333333333148296162562473909929394", 0); + check_exact!(f(3.141592f64) => b"3141592000000000162174274009885266423225", 1); + check_exact!(f(3.141592e17f64) => b"3141592 ", 18); + check_exact!(f(1.0e23f64) => b"99999999999999991611392 ", 23); + check_exact!(f(f64::MAX) => b"1797693134862315708145274237317043567980", 309); + check_exact!(f(f64::MIN_POSITIVE) => b"2225073858507201383090232717332404064219", -307); + check_exact!(f(minf64) => b"4940656458412465441765687928682213723650\ + 5980261432476442558568250067550727020875\ + 1865299836361635992379796564695445717730\ + 9266567103559397963987747960107818781263\ + 0071319031140452784581716784898210368871\ + 8636056998730723050006387409153564984387\ + 3124733972731696151400317153853980741262\ + 3856559117102665855668676818703956031062\ + 4931945271591492455329305456544401127480\ + 1297099995419319894090804165633245247571\ + 4786901472678015935523861155013480352649\ + 3472019379026810710749170333222684475333\ + 5720832431936092382893458368060106011506\ + 1698097530783422773183292479049825247307\ + 7637592724787465608477820373446969953364\ + 7017972677717585125660551199131504891101\ + 4510378627381672509558373897335989936648\ + 0994116420570263709027924276754456522908\ + 7538682506419718265533447265625 ", -323); + + // [1], Table 3: Stress Inputs for Converting 53-bit Binary to Decimal, < 1/2 ULP + check_exact_one!(f(8511030020275656, -342; f64) => b"9", -87); + check_exact_one!(f(5201988407066741, -824; f64) => b"46", -232); + check_exact_one!(f(6406892948269899, 237; f64) => b"141", 88); + check_exact_one!(f(8431154198732492, 72; f64) => b"3981", 38); + check_exact_one!(f(6475049196144587, 99; f64) => b"41040", 46); + check_exact_one!(f(8274307542972842, 726; f64) => b"292084", 235); + check_exact_one!(f(5381065484265332, -456; f64) => b"2891946", -121); + check_exact_one!(f(6761728585499734, -1057; f64) => b"43787718", -302); + check_exact_one!(f(7976538478610756, 376; f64) => b"122770163", 130); + check_exact_one!(f(5982403858958067, 377; f64) => b"1841552452", 130); + check_exact_one!(f(5536995190630837, 93; f64) => b"54835744350", 44); + check_exact_one!(f(7225450889282194, 710; f64) => b"389190181146", 230); + check_exact_one!(f(7225450889282194, 709; f64) => b"1945950905732", 230); + check_exact_one!(f(8703372741147379, 117; f64) => b"14460958381605", 52); + check_exact_one!(f(8944262675275217, -1001; f64) => b"417367747458531", -285); + check_exact_one!(f(7459803696087692, -707; f64) => b"1107950772878888", -196); + check_exact_one!(f(6080469016670379, -381; f64) => b"12345501366327440", -98); + check_exact_one!(f(8385515147034757, 721; f64) => b"925031711960365024", 233); + check_exact_one!(f(7514216811389786, -828; f64) => b"4198047150284889840", -233); + check_exact_one!(f(8397297803260511, -345; f64) => b"11716315319786511046", -87); + check_exact_one!(f(6733459239310543, 202; f64) => b"432810072844612493629", 77); + check_exact_one!(f(8091450587292794, -473; f64) => b"3317710118160031081518", -126); + + // [1], Table 4: Stress Inputs for Converting 53-bit Binary to Decimal, > 1/2 ULP + check_exact_one!(f(6567258882077402, 952; f64) => b"3", 303); + check_exact_one!(f(6712731423444934, 535; f64) => b"76", 177); + check_exact_one!(f(6712731423444934, 534; f64) => b"378", 177); + check_exact_one!(f(5298405411573037, -957; f64) => b"4350", -272); + check_exact_one!(f(5137311167659507, -144; f64) => b"23037", -27); + check_exact_one!(f(6722280709661868, 363; f64) => b"126301", 126); + check_exact_one!(f(5344436398034927, -169; f64) => b"7142211", -35); + check_exact_one!(f(8369123604277281, -853; f64) => b"13934574", -240); + check_exact_one!(f(8995822108487663, -780; f64) => b"141463449", -218); + check_exact_one!(f(8942832835564782, -383; f64) => b"4539277920", -99); + check_exact_one!(f(8942832835564782, -384; f64) => b"22696389598", -99); + check_exact_one!(f(8942832835564782, -385; f64) => b"113481947988", -99); + check_exact_one!(f(6965949469487146, -249; f64) => b"7700366561890", -59); + check_exact_one!(f(6965949469487146, -250; f64) => b"38501832809448", -59); + check_exact_one!(f(6965949469487146, -251; f64) => b"192509164047238", -59); + check_exact_one!(f(7487252720986826, 548; f64) => b"6898586531774201", 181); + check_exact_one!(f(5592117679628511, 164; f64) => b"13076622631878654", 66); + check_exact_one!(f(8887055249355788, 665; f64) => b"136052020756121240", 217); + check_exact_one!(f(6994187472632449, 690; f64) => b"3592810217475959676", 224); + check_exact_one!(f(8797576579012143, 588; f64) => b"89125197712484551899", 193); + check_exact_one!(f(7363326733505337, 272; f64) => b"558769757362301140950", 98); + check_exact_one!(f(8549497411294502, -448; f64) => b"1176257830728540379990", -118); +} + +pub fn more_shortest_sanity_test(mut f: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +{ + check_shortest!(f{mant: 99_999_999_999_999_999, minus: 1, plus: 1, + exp: 0, inclusive: true} => b"1", 18); + check_shortest!(f{mant: 99_999_999_999_999_999, minus: 1, plus: 1, + exp: 0, inclusive: false} => b"99999999999999999", 17); +} + +fn to_string_with_parts(mut f: F) -> String +where + F: for<'a> FnMut(&'a mut [MaybeUninit], &'a mut [MaybeUninit>]) -> Formatted<'a>, +{ + let mut buf = [MaybeUninit::new(0); 1024]; + let mut parts = [MaybeUninit::new(Part::Zero(0)); 16]; + let formatted = f(&mut buf, &mut parts); + let mut ret = vec![0; formatted.len()]; + assert_eq!(formatted.write(&mut ret), Some(ret.len())); + String::from_utf8(ret).unwrap() +} + +pub fn to_shortest_str_test(mut f_: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +{ + use core::num::flt2dec::Sign::*; + + fn to_string(f: &mut F, v: T, sign: Sign, frac_digits: usize) -> String + where + T: DecodableFloat, + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), + { + to_string_with_parts(|buf, parts| { + to_shortest_str(|d, b| f(d, b), v, sign, frac_digits, buf, parts) + }) + } + + let f = &mut f_; + + assert_eq!(to_string(f, 0.0, Minus, 0), "0"); + assert_eq!(to_string(f, 0.0, Minus, 0), "0"); + assert_eq!(to_string(f, 0.0, MinusPlus, 0), "+0"); + assert_eq!(to_string(f, -0.0, Minus, 0), "-0"); + assert_eq!(to_string(f, -0.0, MinusPlus, 0), "-0"); + assert_eq!(to_string(f, 0.0, Minus, 1), "0.0"); + assert_eq!(to_string(f, 0.0, Minus, 1), "0.0"); + assert_eq!(to_string(f, 0.0, MinusPlus, 1), "+0.0"); + assert_eq!(to_string(f, -0.0, Minus, 8), "-0.00000000"); + assert_eq!(to_string(f, -0.0, MinusPlus, 8), "-0.00000000"); + + assert_eq!(to_string(f, 1.0 / 0.0, Minus, 0), "inf"); + assert_eq!(to_string(f, 1.0 / 0.0, Minus, 0), "inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, 0), "+inf"); + assert_eq!(to_string(f, 0.0 / 0.0, Minus, 0), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, Minus, 1), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 64), "NaN"); + assert_eq!(to_string(f, -1.0 / 0.0, Minus, 0), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, Minus, 1), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 64), "-inf"); + + assert_eq!(to_string(f, 3.14, Minus, 0), "3.14"); + assert_eq!(to_string(f, 3.14, Minus, 0), "3.14"); + assert_eq!(to_string(f, 3.14, MinusPlus, 0), "+3.14"); + assert_eq!(to_string(f, -3.14, Minus, 0), "-3.14"); + assert_eq!(to_string(f, -3.14, Minus, 0), "-3.14"); + assert_eq!(to_string(f, -3.14, MinusPlus, 0), "-3.14"); + assert_eq!(to_string(f, 3.14, Minus, 1), "3.14"); + assert_eq!(to_string(f, 3.14, Minus, 2), "3.14"); + assert_eq!(to_string(f, 3.14, MinusPlus, 4), "+3.1400"); + assert_eq!(to_string(f, -3.14, Minus, 8), "-3.14000000"); + assert_eq!(to_string(f, -3.14, Minus, 8), "-3.14000000"); + assert_eq!(to_string(f, -3.14, MinusPlus, 8), "-3.14000000"); + + assert_eq!(to_string(f, 7.5e-11, Minus, 0), "0.000000000075"); + assert_eq!(to_string(f, 7.5e-11, Minus, 3), "0.000000000075"); + assert_eq!(to_string(f, 7.5e-11, Minus, 12), "0.000000000075"); + assert_eq!(to_string(f, 7.5e-11, Minus, 13), "0.0000000000750"); + + assert_eq!(to_string(f, 1.9971e20, Minus, 0), "199710000000000000000"); + assert_eq!(to_string(f, 1.9971e20, Minus, 1), "199710000000000000000.0"); + assert_eq!(to_string(f, 1.9971e20, Minus, 8), "199710000000000000000.00000000"); + + assert_eq!(to_string(f, f32::MAX, Minus, 0), format!("34028235{:0>31}", "")); + assert_eq!(to_string(f, f32::MAX, Minus, 1), format!("34028235{:0>31}.0", "")); + assert_eq!(to_string(f, f32::MAX, Minus, 8), format!("34028235{:0>31}.00000000", "")); + + let minf32 = ldexp_f32(1.0, -149); + assert_eq!(to_string(f, minf32, Minus, 0), format!("0.{:0>44}1", "")); + assert_eq!(to_string(f, minf32, Minus, 45), format!("0.{:0>44}1", "")); + assert_eq!(to_string(f, minf32, Minus, 46), format!("0.{:0>44}10", "")); + + assert_eq!(to_string(f, f64::MAX, Minus, 0), format!("17976931348623157{:0>292}", "")); + assert_eq!(to_string(f, f64::MAX, Minus, 1), format!("17976931348623157{:0>292}.0", "")); + assert_eq!(to_string(f, f64::MAX, Minus, 8), format!("17976931348623157{:0>292}.00000000", "")); + + let minf64 = ldexp_f64(1.0, -1074); + assert_eq!(to_string(f, minf64, Minus, 0), format!("0.{:0>323}5", "")); + assert_eq!(to_string(f, minf64, Minus, 324), format!("0.{:0>323}5", "")); + assert_eq!(to_string(f, minf64, Minus, 325), format!("0.{:0>323}50", "")); + + if cfg!(miri) { + // Miri is too slow + return; + } + + // very large output + assert_eq!(to_string(f, 1.1, Minus, 80000), format!("1.1{:0>79999}", "")); +} + +pub fn to_shortest_exp_str_test(mut f_: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), +{ + use core::num::flt2dec::Sign::*; + + fn to_string(f: &mut F, v: T, sign: Sign, exp_bounds: (i16, i16), upper: bool) -> String + where + T: DecodableFloat, + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), + { + to_string_with_parts(|buf, parts| { + to_shortest_exp_str(|d, b| f(d, b), v, sign, exp_bounds, upper, buf, parts) + }) + } + + let f = &mut f_; + + assert_eq!(to_string(f, 0.0, Minus, (-4, 16), false), "0"); + assert_eq!(to_string(f, 0.0, Minus, (-4, 16), false), "0"); + assert_eq!(to_string(f, 0.0, MinusPlus, (-4, 16), false), "+0"); + assert_eq!(to_string(f, -0.0, Minus, (-4, 16), false), "-0"); + assert_eq!(to_string(f, -0.0, MinusPlus, (-4, 16), false), "-0"); + assert_eq!(to_string(f, 0.0, Minus, (0, 0), true), "0E0"); + assert_eq!(to_string(f, 0.0, Minus, (0, 0), false), "0e0"); + assert_eq!(to_string(f, 0.0, MinusPlus, (5, 9), false), "+0e0"); + assert_eq!(to_string(f, -0.0, Minus, (0, 0), true), "-0E0"); + assert_eq!(to_string(f, -0.0, MinusPlus, (5, 9), false), "-0e0"); + + assert_eq!(to_string(f, 1.0 / 0.0, Minus, (-4, 16), false), "inf"); + assert_eq!(to_string(f, 1.0 / 0.0, Minus, (-4, 16), true), "inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, (-4, 16), true), "+inf"); + assert_eq!(to_string(f, 0.0 / 0.0, Minus, (0, 0), false), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, Minus, (0, 0), true), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, (5, 9), true), "NaN"); + assert_eq!(to_string(f, -1.0 / 0.0, Minus, (0, 0), false), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, Minus, (0, 0), true), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, (5, 9), true), "-inf"); + + assert_eq!(to_string(f, 3.14, Minus, (-4, 16), false), "3.14"); + assert_eq!(to_string(f, 3.14, MinusPlus, (-4, 16), false), "+3.14"); + assert_eq!(to_string(f, -3.14, Minus, (-4, 16), false), "-3.14"); + assert_eq!(to_string(f, -3.14, MinusPlus, (-4, 16), false), "-3.14"); + assert_eq!(to_string(f, 3.14, Minus, (0, 0), true), "3.14E0"); + assert_eq!(to_string(f, 3.14, Minus, (0, 0), false), "3.14e0"); + assert_eq!(to_string(f, 3.14, MinusPlus, (5, 9), false), "+3.14e0"); + assert_eq!(to_string(f, -3.14, Minus, (0, 0), true), "-3.14E0"); + assert_eq!(to_string(f, -3.14, Minus, (0, 0), false), "-3.14e0"); + assert_eq!(to_string(f, -3.14, MinusPlus, (5, 9), false), "-3.14e0"); + + assert_eq!(to_string(f, 0.1, Minus, (-4, 16), false), "0.1"); + assert_eq!(to_string(f, 0.1, Minus, (-4, 16), false), "0.1"); + assert_eq!(to_string(f, 0.1, MinusPlus, (-4, 16), false), "+0.1"); + assert_eq!(to_string(f, -0.1, Minus, (-4, 16), false), "-0.1"); + assert_eq!(to_string(f, -0.1, MinusPlus, (-4, 16), false), "-0.1"); + assert_eq!(to_string(f, 0.1, Minus, (0, 0), true), "1E-1"); + assert_eq!(to_string(f, 0.1, Minus, (0, 0), false), "1e-1"); + assert_eq!(to_string(f, 0.1, MinusPlus, (5, 9), false), "+1e-1"); + assert_eq!(to_string(f, -0.1, Minus, (0, 0), true), "-1E-1"); + assert_eq!(to_string(f, -0.1, Minus, (0, 0), false), "-1e-1"); + assert_eq!(to_string(f, -0.1, MinusPlus, (5, 9), false), "-1e-1"); + + assert_eq!(to_string(f, 7.5e-11, Minus, (-4, 16), false), "7.5e-11"); + assert_eq!(to_string(f, 7.5e-11, Minus, (-11, 10), false), "0.000000000075"); + assert_eq!(to_string(f, 7.5e-11, Minus, (-10, 11), false), "7.5e-11"); + + assert_eq!(to_string(f, 1.9971e20, Minus, (-4, 16), false), "1.9971e20"); + assert_eq!(to_string(f, 1.9971e20, Minus, (-20, 21), false), "199710000000000000000"); + assert_eq!(to_string(f, 1.9971e20, Minus, (-21, 20), false), "1.9971e20"); + + // the true value of 1.0e23f64 is less than 10^23, but that shouldn't matter here + assert_eq!(to_string(f, 1.0e23, Minus, (22, 23), false), "1e23"); + assert_eq!(to_string(f, 1.0e23, Minus, (23, 24), false), "100000000000000000000000"); + assert_eq!(to_string(f, 1.0e23, Minus, (24, 25), false), "1e23"); + + assert_eq!(to_string(f, f32::MAX, Minus, (-4, 16), false), "3.4028235e38"); + assert_eq!(to_string(f, f32::MAX, Minus, (-39, 38), false), "3.4028235e38"); + assert_eq!(to_string(f, f32::MAX, Minus, (-38, 39), false), format!("34028235{:0>31}", "")); + + let minf32 = ldexp_f32(1.0, -149); + assert_eq!(to_string(f, minf32, Minus, (-4, 16), false), "1e-45"); + assert_eq!(to_string(f, minf32, Minus, (-44, 45), false), "1e-45"); + assert_eq!(to_string(f, minf32, Minus, (-45, 44), false), format!("0.{:0>44}1", "")); + + assert_eq!(to_string(f, f64::MAX, Minus, (-4, 16), false), "1.7976931348623157e308"); + assert_eq!( + to_string(f, f64::MAX, Minus, (-308, 309), false), + format!("17976931348623157{:0>292}", "") + ); + assert_eq!(to_string(f, f64::MAX, Minus, (-309, 308), false), "1.7976931348623157e308"); + + let minf64 = ldexp_f64(1.0, -1074); + assert_eq!(to_string(f, minf64, Minus, (-4, 16), false), "5e-324"); + assert_eq!(to_string(f, minf64, Minus, (-324, 323), false), format!("0.{:0>323}5", "")); + assert_eq!(to_string(f, minf64, Minus, (-323, 324), false), "5e-324"); + + assert_eq!(to_string(f, 1.1, Minus, (i16::MIN, i16::MAX), false), "1.1"); +} + +pub fn to_exact_exp_str_test(mut f_: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), +{ + use core::num::flt2dec::Sign::*; + + fn to_string(f: &mut F, v: T, sign: Sign, ndigits: usize, upper: bool) -> String + where + T: DecodableFloat, + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), + { + to_string_with_parts(|buf, parts| { + to_exact_exp_str(|d, b, l| f(d, b, l), v, sign, ndigits, upper, buf, parts) + }) + } + + let f = &mut f_; + + assert_eq!(to_string(f, 0.0, Minus, 1, true), "0E0"); + assert_eq!(to_string(f, 0.0, Minus, 1, false), "0e0"); + assert_eq!(to_string(f, 0.0, MinusPlus, 1, false), "+0e0"); + assert_eq!(to_string(f, -0.0, Minus, 1, true), "-0E0"); + assert_eq!(to_string(f, -0.0, MinusPlus, 1, false), "-0e0"); + assert_eq!(to_string(f, 0.0, Minus, 2, true), "0.0E0"); + assert_eq!(to_string(f, 0.0, Minus, 2, false), "0.0e0"); + assert_eq!(to_string(f, 0.0, MinusPlus, 2, false), "+0.0e0"); + assert_eq!(to_string(f, -0.0, Minus, 8, false), "-0.0000000e0"); + assert_eq!(to_string(f, -0.0, MinusPlus, 8, false), "-0.0000000e0"); + + assert_eq!(to_string(f, 1.0 / 0.0, Minus, 1, false), "inf"); + assert_eq!(to_string(f, 1.0 / 0.0, Minus, 1, true), "inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, 1, true), "+inf"); + assert_eq!(to_string(f, 0.0 / 0.0, Minus, 8, false), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, Minus, 8, true), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 8, true), "NaN"); + assert_eq!(to_string(f, -1.0 / 0.0, Minus, 64, false), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, Minus, 64, true), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 64, true), "-inf"); + + assert_eq!(to_string(f, 3.14, Minus, 1, true), "3E0"); + assert_eq!(to_string(f, 3.14, Minus, 1, false), "3e0"); + assert_eq!(to_string(f, 3.14, MinusPlus, 1, false), "+3e0"); + assert_eq!(to_string(f, -3.14, Minus, 2, true), "-3.1E0"); + assert_eq!(to_string(f, -3.14, Minus, 2, false), "-3.1e0"); + assert_eq!(to_string(f, -3.14, MinusPlus, 2, false), "-3.1e0"); + assert_eq!(to_string(f, 3.14, Minus, 3, true), "3.14E0"); + assert_eq!(to_string(f, 3.14, Minus, 3, false), "3.14e0"); + assert_eq!(to_string(f, 3.14, MinusPlus, 3, false), "+3.14e0"); + assert_eq!(to_string(f, -3.14, Minus, 4, true), "-3.140E0"); + assert_eq!(to_string(f, -3.14, Minus, 4, false), "-3.140e0"); + assert_eq!(to_string(f, -3.14, MinusPlus, 4, false), "-3.140e0"); + + assert_eq!(to_string(f, 0.195, Minus, 1, false), "2e-1"); + assert_eq!(to_string(f, 0.195, Minus, 1, true), "2E-1"); + assert_eq!(to_string(f, 0.195, MinusPlus, 1, true), "+2E-1"); + assert_eq!(to_string(f, -0.195, Minus, 2, false), "-2.0e-1"); + assert_eq!(to_string(f, -0.195, Minus, 2, true), "-2.0E-1"); + assert_eq!(to_string(f, -0.195, MinusPlus, 2, true), "-2.0E-1"); + assert_eq!(to_string(f, 0.195, Minus, 3, false), "1.95e-1"); + assert_eq!(to_string(f, 0.195, Minus, 3, true), "1.95E-1"); + assert_eq!(to_string(f, 0.195, MinusPlus, 3, true), "+1.95E-1"); + assert_eq!(to_string(f, -0.195, Minus, 4, false), "-1.950e-1"); + assert_eq!(to_string(f, -0.195, Minus, 4, true), "-1.950E-1"); + assert_eq!(to_string(f, -0.195, MinusPlus, 4, true), "-1.950E-1"); + + assert_eq!(to_string(f, 9.5, Minus, 1, false), "1e1"); + assert_eq!(to_string(f, 9.5, Minus, 2, false), "9.5e0"); + assert_eq!(to_string(f, 9.5, Minus, 3, false), "9.50e0"); + assert_eq!(to_string(f, 9.5, Minus, 30, false), "9.50000000000000000000000000000e0"); + + assert_eq!(to_string(f, 1.0e25, Minus, 1, false), "1e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 2, false), "1.0e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 15, false), "1.00000000000000e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 16, false), "1.000000000000000e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 17, false), "1.0000000000000001e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 18, false), "1.00000000000000009e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 19, false), "1.000000000000000091e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 20, false), "1.0000000000000000906e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 21, false), "1.00000000000000009060e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 22, false), "1.000000000000000090597e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 23, false), "1.0000000000000000905970e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 24, false), "1.00000000000000009059697e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 25, false), "1.000000000000000090596966e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 26, false), "1.0000000000000000905969664e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 27, false), "1.00000000000000009059696640e25"); + assert_eq!(to_string(f, 1.0e25, Minus, 30, false), "1.00000000000000009059696640000e25"); + + assert_eq!(to_string(f, 1.0e-6, Minus, 1, false), "1e-6"); + assert_eq!(to_string(f, 1.0e-6, Minus, 2, false), "1.0e-6"); + assert_eq!(to_string(f, 1.0e-6, Minus, 16, false), "1.000000000000000e-6"); + assert_eq!(to_string(f, 1.0e-6, Minus, 17, false), "9.9999999999999995e-7"); + assert_eq!(to_string(f, 1.0e-6, Minus, 18, false), "9.99999999999999955e-7"); + assert_eq!(to_string(f, 1.0e-6, Minus, 19, false), "9.999999999999999547e-7"); + assert_eq!(to_string(f, 1.0e-6, Minus, 20, false), "9.9999999999999995475e-7"); + assert_eq!(to_string(f, 1.0e-6, Minus, 30, false), "9.99999999999999954748111825886e-7"); + assert_eq!( + to_string(f, 1.0e-6, Minus, 40, false), + "9.999999999999999547481118258862586856139e-7" + ); + assert_eq!( + to_string(f, 1.0e-6, Minus, 50, false), + "9.9999999999999995474811182588625868561393872369081e-7" + ); + assert_eq!( + to_string(f, 1.0e-6, Minus, 60, false), + "9.99999999999999954748111825886258685613938723690807819366455e-7" + ); + assert_eq!( + to_string(f, 1.0e-6, Minus, 70, false), + "9.999999999999999547481118258862586856139387236908078193664550781250000e-7" + ); + + assert_eq!(to_string(f, f32::MAX, Minus, 1, false), "3e38"); + assert_eq!(to_string(f, f32::MAX, Minus, 2, false), "3.4e38"); + assert_eq!(to_string(f, f32::MAX, Minus, 4, false), "3.403e38"); + assert_eq!(to_string(f, f32::MAX, Minus, 8, false), "3.4028235e38"); + assert_eq!(to_string(f, f32::MAX, Minus, 16, false), "3.402823466385289e38"); + assert_eq!(to_string(f, f32::MAX, Minus, 32, false), "3.4028234663852885981170418348452e38"); + assert_eq!( + to_string(f, f32::MAX, Minus, 64, false), + "3.402823466385288598117041834845169254400000000000000000000000000e38" + ); + + let minf32 = ldexp_f32(1.0, -149); + assert_eq!(to_string(f, minf32, Minus, 1, false), "1e-45"); + assert_eq!(to_string(f, minf32, Minus, 2, false), "1.4e-45"); + assert_eq!(to_string(f, minf32, Minus, 4, false), "1.401e-45"); + assert_eq!(to_string(f, minf32, Minus, 8, false), "1.4012985e-45"); + assert_eq!(to_string(f, minf32, Minus, 16, false), "1.401298464324817e-45"); + assert_eq!(to_string(f, minf32, Minus, 32, false), "1.4012984643248170709237295832899e-45"); + assert_eq!( + to_string(f, minf32, Minus, 64, false), + "1.401298464324817070923729583289916131280261941876515771757068284e-45" + ); + assert_eq!( + to_string(f, minf32, Minus, 128, false), + "1.401298464324817070923729583289916131280261941876515771757068283\ + 8897910826858606014866381883621215820312500000000000000000000000e-45" + ); + + if cfg!(miri) { + // Miri is too slow + return; + } + + assert_eq!(to_string(f, f64::MAX, Minus, 1, false), "2e308"); + assert_eq!(to_string(f, f64::MAX, Minus, 2, false), "1.8e308"); + assert_eq!(to_string(f, f64::MAX, Minus, 4, false), "1.798e308"); + assert_eq!(to_string(f, f64::MAX, Minus, 8, false), "1.7976931e308"); + assert_eq!(to_string(f, f64::MAX, Minus, 16, false), "1.797693134862316e308"); + assert_eq!(to_string(f, f64::MAX, Minus, 32, false), "1.7976931348623157081452742373170e308"); + assert_eq!( + to_string(f, f64::MAX, Minus, 64, false), + "1.797693134862315708145274237317043567980705675258449965989174768e308" + ); + assert_eq!( + to_string(f, f64::MAX, Minus, 128, false), + "1.797693134862315708145274237317043567980705675258449965989174768\ + 0315726078002853876058955863276687817154045895351438246423432133e308" + ); + assert_eq!( + to_string(f, f64::MAX, Minus, 256, false), + "1.797693134862315708145274237317043567980705675258449965989174768\ + 0315726078002853876058955863276687817154045895351438246423432132\ + 6889464182768467546703537516986049910576551282076245490090389328\ + 9440758685084551339423045832369032229481658085593321233482747978e308" + ); + assert_eq!( + to_string(f, f64::MAX, Minus, 512, false), + "1.797693134862315708145274237317043567980705675258449965989174768\ + 0315726078002853876058955863276687817154045895351438246423432132\ + 6889464182768467546703537516986049910576551282076245490090389328\ + 9440758685084551339423045832369032229481658085593321233482747978\ + 2620414472316873817718091929988125040402618412485836800000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000e308" + ); + + // okay, this is becoming tough. fortunately for us, this is almost the worst case. + let minf64 = ldexp_f64(1.0, -1074); + assert_eq!(to_string(f, minf64, Minus, 1, false), "5e-324"); + assert_eq!(to_string(f, minf64, Minus, 2, false), "4.9e-324"); + assert_eq!(to_string(f, minf64, Minus, 4, false), "4.941e-324"); + assert_eq!(to_string(f, minf64, Minus, 8, false), "4.9406565e-324"); + assert_eq!(to_string(f, minf64, Minus, 16, false), "4.940656458412465e-324"); + assert_eq!(to_string(f, minf64, Minus, 32, false), "4.9406564584124654417656879286822e-324"); + assert_eq!( + to_string(f, minf64, Minus, 64, false), + "4.940656458412465441765687928682213723650598026143247644255856825e-324" + ); + assert_eq!( + to_string(f, minf64, Minus, 128, false), + "4.940656458412465441765687928682213723650598026143247644255856825\ + 0067550727020875186529983636163599237979656469544571773092665671e-324" + ); + assert_eq!( + to_string(f, minf64, Minus, 256, false), + "4.940656458412465441765687928682213723650598026143247644255856825\ + 0067550727020875186529983636163599237979656469544571773092665671\ + 0355939796398774796010781878126300713190311404527845817167848982\ + 1036887186360569987307230500063874091535649843873124733972731696e-324" + ); + assert_eq!( + to_string(f, minf64, Minus, 512, false), + "4.940656458412465441765687928682213723650598026143247644255856825\ + 0067550727020875186529983636163599237979656469544571773092665671\ + 0355939796398774796010781878126300713190311404527845817167848982\ + 1036887186360569987307230500063874091535649843873124733972731696\ + 1514003171538539807412623856559117102665855668676818703956031062\ + 4931945271591492455329305456544401127480129709999541931989409080\ + 4165633245247571478690147267801593552386115501348035264934720193\ + 7902681071074917033322268447533357208324319360923828934583680601e-324" + ); + assert_eq!( + to_string(f, minf64, Minus, 1024, false), + "4.940656458412465441765687928682213723650598026143247644255856825\ + 0067550727020875186529983636163599237979656469544571773092665671\ + 0355939796398774796010781878126300713190311404527845817167848982\ + 1036887186360569987307230500063874091535649843873124733972731696\ + 1514003171538539807412623856559117102665855668676818703956031062\ + 4931945271591492455329305456544401127480129709999541931989409080\ + 4165633245247571478690147267801593552386115501348035264934720193\ + 7902681071074917033322268447533357208324319360923828934583680601\ + 0601150616980975307834227731832924790498252473077637592724787465\ + 6084778203734469699533647017972677717585125660551199131504891101\ + 4510378627381672509558373897335989936648099411642057026370902792\ + 4276754456522908753868250641971826553344726562500000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000e-324" + ); + + // very large output + assert_eq!(to_string(f, 0.0, Minus, 80000, false), format!("0.{:0>79999}e0", "")); + assert_eq!(to_string(f, 1.0e1, Minus, 80000, false), format!("1.{:0>79999}e1", "")); + assert_eq!(to_string(f, 1.0e0, Minus, 80000, false), format!("1.{:0>79999}e0", "")); + assert_eq!( + to_string(f, 1.0e-1, Minus, 80000, false), + format!( + "1.000000000000000055511151231257827021181583404541015625{:0>79945}\ + e-1", + "" + ) + ); + assert_eq!( + to_string(f, 1.0e-20, Minus, 80000, false), + format!( + "9.999999999999999451532714542095716517295037027873924471077157760\ + 66783064379706047475337982177734375{:0>79901}e-21", + "" + ) + ); +} + +pub fn to_exact_fixed_str_test(mut f_: F) +where + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), +{ + use core::num::flt2dec::Sign::*; + + fn to_string(f: &mut F, v: T, sign: Sign, frac_digits: usize) -> String + where + T: DecodableFloat, + F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit], i16) -> (&'a [u8], i16), + { + to_string_with_parts(|buf, parts| { + to_exact_fixed_str(|d, b, l| f(d, b, l), v, sign, frac_digits, buf, parts) + }) + } + + let f = &mut f_; + + assert_eq!(to_string(f, 0.0, Minus, 0), "0"); + assert_eq!(to_string(f, 0.0, MinusPlus, 0), "+0"); + assert_eq!(to_string(f, -0.0, Minus, 0), "-0"); + assert_eq!(to_string(f, -0.0, MinusPlus, 0), "-0"); + assert_eq!(to_string(f, 0.0, Minus, 1), "0.0"); + assert_eq!(to_string(f, 0.0, MinusPlus, 1), "+0.0"); + assert_eq!(to_string(f, -0.0, Minus, 8), "-0.00000000"); + assert_eq!(to_string(f, -0.0, MinusPlus, 8), "-0.00000000"); + + assert_eq!(to_string(f, 1.0 / 0.0, Minus, 0), "inf"); + assert_eq!(to_string(f, 1.0 / 0.0, Minus, 1), "inf"); + assert_eq!(to_string(f, 1.0 / 0.0, MinusPlus, 64), "+inf"); + assert_eq!(to_string(f, 0.0 / 0.0, Minus, 0), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, Minus, 1), "NaN"); + assert_eq!(to_string(f, 0.0 / 0.0, MinusPlus, 64), "NaN"); + assert_eq!(to_string(f, -1.0 / 0.0, Minus, 0), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, Minus, 1), "-inf"); + assert_eq!(to_string(f, -1.0 / 0.0, MinusPlus, 64), "-inf"); + + assert_eq!(to_string(f, 3.14, Minus, 0), "3"); + assert_eq!(to_string(f, 3.14, Minus, 0), "3"); + assert_eq!(to_string(f, 3.14, MinusPlus, 0), "+3"); + assert_eq!(to_string(f, -3.14, Minus, 0), "-3"); + assert_eq!(to_string(f, -3.14, Minus, 0), "-3"); + assert_eq!(to_string(f, -3.14, MinusPlus, 0), "-3"); + assert_eq!(to_string(f, 3.14, Minus, 1), "3.1"); + assert_eq!(to_string(f, 3.14, Minus, 2), "3.14"); + assert_eq!(to_string(f, 3.14, MinusPlus, 4), "+3.1400"); + assert_eq!(to_string(f, -3.14, Minus, 8), "-3.14000000"); + assert_eq!(to_string(f, -3.14, Minus, 8), "-3.14000000"); + assert_eq!(to_string(f, -3.14, MinusPlus, 8), "-3.14000000"); + + assert_eq!(to_string(f, 0.195, Minus, 0), "0"); + assert_eq!(to_string(f, 0.195, MinusPlus, 0), "+0"); + assert_eq!(to_string(f, -0.195, Minus, 0), "-0"); + assert_eq!(to_string(f, -0.195, Minus, 0), "-0"); + assert_eq!(to_string(f, -0.195, MinusPlus, 0), "-0"); + assert_eq!(to_string(f, 0.195, Minus, 1), "0.2"); + assert_eq!(to_string(f, 0.195, Minus, 2), "0.20"); + assert_eq!(to_string(f, 0.195, MinusPlus, 4), "+0.1950"); + assert_eq!(to_string(f, -0.195, Minus, 5), "-0.19500"); + assert_eq!(to_string(f, -0.195, Minus, 6), "-0.195000"); + assert_eq!(to_string(f, -0.195, MinusPlus, 8), "-0.19500000"); + + assert_eq!(to_string(f, 999.5, Minus, 0), "1000"); + assert_eq!(to_string(f, 999.5, Minus, 1), "999.5"); + assert_eq!(to_string(f, 999.5, Minus, 2), "999.50"); + assert_eq!(to_string(f, 999.5, Minus, 3), "999.500"); + assert_eq!(to_string(f, 999.5, Minus, 30), "999.500000000000000000000000000000"); + + assert_eq!(to_string(f, 0.5, Minus, 0), "0"); + assert_eq!(to_string(f, 0.5, Minus, 1), "0.5"); + assert_eq!(to_string(f, 0.5, Minus, 2), "0.50"); + assert_eq!(to_string(f, 0.5, Minus, 3), "0.500"); + + assert_eq!(to_string(f, 0.95, Minus, 0), "1"); + assert_eq!(to_string(f, 0.95, Minus, 1), "0.9"); // because it really is less than 0.95 + assert_eq!(to_string(f, 0.95, Minus, 2), "0.95"); + assert_eq!(to_string(f, 0.95, Minus, 3), "0.950"); + assert_eq!(to_string(f, 0.95, Minus, 10), "0.9500000000"); + assert_eq!(to_string(f, 0.95, Minus, 30), "0.949999999999999955591079014994"); + + assert_eq!(to_string(f, 0.095, Minus, 0), "0"); + assert_eq!(to_string(f, 0.095, Minus, 1), "0.1"); + assert_eq!(to_string(f, 0.095, Minus, 2), "0.10"); + assert_eq!(to_string(f, 0.095, Minus, 3), "0.095"); + assert_eq!(to_string(f, 0.095, Minus, 4), "0.0950"); + assert_eq!(to_string(f, 0.095, Minus, 10), "0.0950000000"); + assert_eq!(to_string(f, 0.095, Minus, 30), "0.095000000000000001110223024625"); + + assert_eq!(to_string(f, 0.0095, Minus, 0), "0"); + assert_eq!(to_string(f, 0.0095, Minus, 1), "0.0"); + assert_eq!(to_string(f, 0.0095, Minus, 2), "0.01"); + assert_eq!(to_string(f, 0.0095, Minus, 3), "0.009"); // really is less than 0.0095 + assert_eq!(to_string(f, 0.0095, Minus, 4), "0.0095"); + assert_eq!(to_string(f, 0.0095, Minus, 5), "0.00950"); + assert_eq!(to_string(f, 0.0095, Minus, 10), "0.0095000000"); + assert_eq!(to_string(f, 0.0095, Minus, 30), "0.009499999999999999764077607267"); + + assert_eq!(to_string(f, 7.5e-11, Minus, 0), "0"); + assert_eq!(to_string(f, 7.5e-11, Minus, 3), "0.000"); + assert_eq!(to_string(f, 7.5e-11, Minus, 10), "0.0000000001"); + assert_eq!(to_string(f, 7.5e-11, Minus, 11), "0.00000000007"); // ditto + assert_eq!(to_string(f, 7.5e-11, Minus, 12), "0.000000000075"); + assert_eq!(to_string(f, 7.5e-11, Minus, 13), "0.0000000000750"); + assert_eq!(to_string(f, 7.5e-11, Minus, 20), "0.00000000007500000000"); + assert_eq!(to_string(f, 7.5e-11, Minus, 30), "0.000000000074999999999999999501"); + + assert_eq!(to_string(f, 1.0e25, Minus, 0), "10000000000000000905969664"); + assert_eq!(to_string(f, 1.0e25, Minus, 1), "10000000000000000905969664.0"); + assert_eq!(to_string(f, 1.0e25, Minus, 3), "10000000000000000905969664.000"); + + assert_eq!(to_string(f, 1.0e-6, Minus, 0), "0"); + assert_eq!(to_string(f, 1.0e-6, Minus, 3), "0.000"); + assert_eq!(to_string(f, 1.0e-6, Minus, 6), "0.000001"); + assert_eq!(to_string(f, 1.0e-6, Minus, 9), "0.000001000"); + assert_eq!(to_string(f, 1.0e-6, Minus, 12), "0.000001000000"); + assert_eq!(to_string(f, 1.0e-6, Minus, 22), "0.0000010000000000000000"); + assert_eq!(to_string(f, 1.0e-6, Minus, 23), "0.00000099999999999999995"); + assert_eq!(to_string(f, 1.0e-6, Minus, 24), "0.000000999999999999999955"); + assert_eq!(to_string(f, 1.0e-6, Minus, 25), "0.0000009999999999999999547"); + assert_eq!(to_string(f, 1.0e-6, Minus, 35), "0.00000099999999999999995474811182589"); + assert_eq!(to_string(f, 1.0e-6, Minus, 45), "0.000000999999999999999954748111825886258685614"); + assert_eq!( + to_string(f, 1.0e-6, Minus, 55), + "0.0000009999999999999999547481118258862586856139387236908" + ); + assert_eq!( + to_string(f, 1.0e-6, Minus, 65), + "0.00000099999999999999995474811182588625868561393872369080781936646" + ); + assert_eq!( + to_string(f, 1.0e-6, Minus, 75), + "0.000000999999999999999954748111825886258685613938723690807819366455078125000" + ); + + assert_eq!(to_string(f, f32::MAX, Minus, 0), "340282346638528859811704183484516925440"); + assert_eq!(to_string(f, f32::MAX, Minus, 1), "340282346638528859811704183484516925440.0"); + assert_eq!(to_string(f, f32::MAX, Minus, 2), "340282346638528859811704183484516925440.00"); + + if cfg!(miri) { + // Miri is too slow + return; + } + + let minf32 = ldexp_f32(1.0, -149); + assert_eq!(to_string(f, minf32, Minus, 0), "0"); + assert_eq!(to_string(f, minf32, Minus, 1), "0.0"); + assert_eq!(to_string(f, minf32, Minus, 2), "0.00"); + assert_eq!(to_string(f, minf32, Minus, 4), "0.0000"); + assert_eq!(to_string(f, minf32, Minus, 8), "0.00000000"); + assert_eq!(to_string(f, minf32, Minus, 16), "0.0000000000000000"); + assert_eq!(to_string(f, minf32, Minus, 32), "0.00000000000000000000000000000000"); + assert_eq!( + to_string(f, minf32, Minus, 64), + "0.0000000000000000000000000000000000000000000014012984643248170709" + ); + assert_eq!( + to_string(f, minf32, Minus, 128), + "0.0000000000000000000000000000000000000000000014012984643248170709\ + 2372958328991613128026194187651577175706828388979108268586060149" + ); + assert_eq!( + to_string(f, minf32, Minus, 256), + "0.0000000000000000000000000000000000000000000014012984643248170709\ + 2372958328991613128026194187651577175706828388979108268586060148\ + 6638188362121582031250000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000" + ); + + assert_eq!( + to_string(f, f64::MAX, Minus, 0), + "1797693134862315708145274237317043567980705675258449965989174768\ + 0315726078002853876058955863276687817154045895351438246423432132\ + 6889464182768467546703537516986049910576551282076245490090389328\ + 9440758685084551339423045832369032229481658085593321233482747978\ + 26204144723168738177180919299881250404026184124858368" + ); + assert_eq!( + to_string(f, f64::MAX, Minus, 10), + "1797693134862315708145274237317043567980705675258449965989174768\ + 0315726078002853876058955863276687817154045895351438246423432132\ + 6889464182768467546703537516986049910576551282076245490090389328\ + 9440758685084551339423045832369032229481658085593321233482747978\ + 26204144723168738177180919299881250404026184124858368.0000000000" + ); + + let minf64 = ldexp_f64(1.0, -1074); + assert_eq!(to_string(f, minf64, Minus, 0), "0"); + assert_eq!(to_string(f, minf64, Minus, 1), "0.0"); + assert_eq!(to_string(f, minf64, Minus, 10), "0.0000000000"); + assert_eq!( + to_string(f, minf64, Minus, 100), + "0.0000000000000000000000000000000000000000000000000000000000000000\ + 000000000000000000000000000000000000" + ); + assert_eq!( + to_string(f, minf64, Minus, 1000), + "0.0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0004940656458412465441765687928682213723650598026143247644255856\ + 8250067550727020875186529983636163599237979656469544571773092665\ + 6710355939796398774796010781878126300713190311404527845817167848\ + 9821036887186360569987307230500063874091535649843873124733972731\ + 6961514003171538539807412623856559117102665855668676818703956031\ + 0624931945271591492455329305456544401127480129709999541931989409\ + 0804165633245247571478690147267801593552386115501348035264934720\ + 1937902681071074917033322268447533357208324319360923828934583680\ + 6010601150616980975307834227731832924790498252473077637592724787\ + 4656084778203734469699533647017972677717585125660551199131504891\ + 1014510378627381672509558373897335989937" + ); + + // very large output + assert_eq!(to_string(f, 0.0, Minus, 80000), format!("0.{:0>80000}", "")); + assert_eq!(to_string(f, 1.0e1, Minus, 80000), format!("10.{:0>80000}", "")); + assert_eq!(to_string(f, 1.0e0, Minus, 80000), format!("1.{:0>80000}", "")); + assert_eq!( + to_string(f, 1.0e-1, Minus, 80000), + format!("0.1000000000000000055511151231257827021181583404541015625{:0>79945}", "") + ); + assert_eq!( + to_string(f, 1.0e-20, Minus, 80000), + format!( + "0.0000000000000000000099999999999999994515327145420957165172950370\ + 2787392447107715776066783064379706047475337982177734375{:0>79881}", + "" + ) + ); +} diff --git a/library/core/tests/num/flt2dec/random.rs b/library/coretests/tests/num/flt2dec/random.rs similarity index 94% rename from library/core/tests/num/flt2dec/random.rs rename to library/coretests/tests/num/flt2dec/random.rs index 99fc23af7ea9d..586b49df7d9b2 100644 --- a/library/core/tests/num/flt2dec/random.rs +++ b/library/coretests/tests/num/flt2dec/random.rs @@ -5,7 +5,7 @@ use core::num::flt2dec::{DecodableFloat, Decoded, FullDecoded, MAX_SIG_DIGITS, d use std::mem::MaybeUninit; use std::str; -use rand::distributions::{Distribution, Uniform}; +use rand::distr::{Distribution, Uniform}; pub fn decode_finite(v: T) -> Decoded { match decode(v).1 { @@ -84,11 +84,8 @@ where F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), { - if cfg!(target_os = "emscripten") { - return; // using rng pulls in i128 support, which doesn't work - } let mut rng = crate::test_rng(); - let f32_range = Uniform::new(0x0000_0001u32, 0x7f80_0000); + let f32_range = Uniform::new(0x0000_0001u32, 0x7f80_0000).unwrap(); iterate("f32_random_equivalence_test", k, n, f, g, |_| { let x = f32::from_bits(f32_range.sample(&mut rng)); decode_finite(x) @@ -100,11 +97,8 @@ where F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> Option<(&'a [u8], i16)>, G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit]) -> (&'a [u8], i16), { - if cfg!(target_os = "emscripten") { - return; // using rng pulls in i128 support, which doesn't work - } let mut rng = crate::test_rng(); - let f64_range = Uniform::new(0x0000_0000_0000_0001u64, 0x7ff0_0000_0000_0000); + let f64_range = Uniform::new(0x0000_0000_0000_0001u64, 0x7ff0_0000_0000_0000).unwrap(); iterate("f64_random_equivalence_test", k, n, f, g, |_| { let x = f64::from_bits(f64_range.sample(&mut rng)); decode_finite(x) diff --git a/library/core/tests/num/flt2dec/strategy/dragon.rs b/library/coretests/tests/num/flt2dec/strategy/dragon.rs similarity index 100% rename from library/core/tests/num/flt2dec/strategy/dragon.rs rename to library/coretests/tests/num/flt2dec/strategy/dragon.rs diff --git a/library/core/tests/num/flt2dec/strategy/grisu.rs b/library/coretests/tests/num/flt2dec/strategy/grisu.rs similarity index 100% rename from library/core/tests/num/flt2dec/strategy/grisu.rs rename to library/coretests/tests/num/flt2dec/strategy/grisu.rs diff --git a/library/core/tests/num/i128.rs b/library/coretests/tests/num/i128.rs similarity index 100% rename from library/core/tests/num/i128.rs rename to library/coretests/tests/num/i128.rs diff --git a/library/core/tests/num/i16.rs b/library/coretests/tests/num/i16.rs similarity index 100% rename from library/core/tests/num/i16.rs rename to library/coretests/tests/num/i16.rs diff --git a/library/core/tests/num/i32.rs b/library/coretests/tests/num/i32.rs similarity index 100% rename from library/core/tests/num/i32.rs rename to library/coretests/tests/num/i32.rs diff --git a/library/core/tests/num/i64.rs b/library/coretests/tests/num/i64.rs similarity index 100% rename from library/core/tests/num/i64.rs rename to library/coretests/tests/num/i64.rs diff --git a/library/core/tests/num/i8.rs b/library/coretests/tests/num/i8.rs similarity index 100% rename from library/core/tests/num/i8.rs rename to library/coretests/tests/num/i8.rs diff --git a/library/core/tests/num/ieee754.rs b/library/coretests/tests/num/ieee754.rs similarity index 100% rename from library/core/tests/num/ieee754.rs rename to library/coretests/tests/num/ieee754.rs diff --git a/library/core/tests/num/int_log.rs b/library/coretests/tests/num/int_log.rs similarity index 100% rename from library/core/tests/num/int_log.rs rename to library/coretests/tests/num/int_log.rs diff --git a/library/core/tests/num/int_macros.rs b/library/coretests/tests/num/int_macros.rs similarity index 100% rename from library/core/tests/num/int_macros.rs rename to library/coretests/tests/num/int_macros.rs diff --git a/library/core/tests/num/int_sqrt.rs b/library/coretests/tests/num/int_sqrt.rs similarity index 100% rename from library/core/tests/num/int_sqrt.rs rename to library/coretests/tests/num/int_sqrt.rs diff --git a/library/core/tests/num/midpoint.rs b/library/coretests/tests/num/midpoint.rs similarity index 100% rename from library/core/tests/num/midpoint.rs rename to library/coretests/tests/num/midpoint.rs diff --git a/library/core/tests/num/mod.rs b/library/coretests/tests/num/mod.rs similarity index 100% rename from library/core/tests/num/mod.rs rename to library/coretests/tests/num/mod.rs diff --git a/library/core/tests/num/nan.rs b/library/coretests/tests/num/nan.rs similarity index 100% rename from library/core/tests/num/nan.rs rename to library/coretests/tests/num/nan.rs diff --git a/library/core/tests/num/ops.rs b/library/coretests/tests/num/ops.rs similarity index 81% rename from library/core/tests/num/ops.rs rename to library/coretests/tests/num/ops.rs index ae8b938250ec9..7b2aad4897808 100644 --- a/library/core/tests/num/ops.rs +++ b/library/coretests/tests/num/ops.rs @@ -51,9 +51,7 @@ macro_rules! test_op { }; } -test_op!(test_neg_defined, Neg::neg(0), 0, i8, i16, i32, i64, f32, f64); -#[cfg(not(target_os = "emscripten"))] -test_op!(test_neg_defined_128, Neg::neg(0), 0, i128); +test_op!(test_neg_defined, Neg::neg(0), 0, i8, i16, i32, i64, i128, f32, f64); test_op!(test_not_defined_bool, Not::not(true), false, bool); @@ -69,17 +67,17 @@ macro_rules! test_arith_op { i16, i32, i64, + i128, isize, u8, u16, u32, u64, + u128, usize, f32, f64 ); - #[cfg(not(target_os = "emscripten"))] - impls_defined!($op, $method($lhs, $rhs), 0, i128, u128); } }; ($fn_name:ident, $op:ident::$method:ident(&mut $lhs:literal, $rhs:literal)) => { @@ -93,17 +91,17 @@ macro_rules! test_arith_op { i16, i32, i64, + i128, isize, u8, u16, u32, u64, + u128, usize, f32, f64 ); - #[cfg(not(target_os = "emscripten"))] - impls_defined!($op, $method(&mut $lhs, $rhs), 0, i128, u128); } }; } @@ -131,15 +129,15 @@ macro_rules! test_bitop { i16, i32, i64, + i128, isize, u8, u16, u32, u64, + u128, usize ); - #[cfg(not(target_os = "emscripten"))] - impls_defined!($op, $method(0, 0), 0, i128, u128); impls_defined!($op, $method(false, false), false, bool); } }; @@ -156,15 +154,15 @@ macro_rules! test_bitop_assign { i16, i32, i64, + i128, isize, u8, u16, u32, u64, + u128, usize ); - #[cfg(not(target_os = "emscripten"))] - impls_defined!($op, $method(&mut 0, 0), 0, i128, u128); impls_defined!($op, $method(&mut false, false), false, bool); } }; @@ -182,9 +180,11 @@ macro_rules! test_shift_inner { $(impl_defined!($op, $method(0,0), 0, $lt, $rt);)+ }; ($op:ident::$method:ident, $lt:ty) => { - test_shift_inner!($op::$method, $lt, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize); - #[cfg(not(target_os = "emscripten"))] - test_shift_inner!($op::$method, $lt, i128, u128); + test_shift_inner!( + $op::$method, $lt, + i8, i16, i32, i64, i128, isize, + u8, u16, u32, u64, u128, usize + ); }; } @@ -195,9 +195,11 @@ macro_rules! test_shift { ($test_name:ident, $op:ident::$method:ident) => { #[test] fn $test_name() { - test_shift!($op::$method, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize); - #[cfg(not(target_os = "emscripten"))] - test_shift!($op::$method, i128, u128); + test_shift!( + $op::$method, + i8, i16, i32, i64, i128, isize, + u8, u16, u32, u64, u128, usize + ); } }; } @@ -207,9 +209,11 @@ macro_rules! test_shift_assign_inner { $(impl_defined!($op, $method(&mut 0,0), 0, $lt, $rt);)+ }; ($op:ident::$method:ident, $lt:ty) => { - test_shift_assign_inner!($op::$method, $lt, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize); - #[cfg(not(target_os = "emscripten"))] - test_shift_assign_inner!($op::$method, $lt, i128, u128); + test_shift_assign_inner!( + $op::$method, $lt, + i8, i16, i32, i64, i128, isize, + u8, u16, u32, u64, u128, usize + ); }; } @@ -220,9 +224,11 @@ macro_rules! test_shift_assign { ($test_name:ident, $op:ident::$method:ident) => { #[test] fn $test_name() { - test_shift_assign!($op::$method, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize); - #[cfg(not(target_os = "emscripten"))] - test_shift_assign!($op::$method, i128, u128); + test_shift_assign!( + $op::$method, + i8, i16, i32, i64, i128, isize, + u8, u16, u32, u64, u128, usize + ); } }; } diff --git a/library/core/tests/num/u128.rs b/library/coretests/tests/num/u128.rs similarity index 100% rename from library/core/tests/num/u128.rs rename to library/coretests/tests/num/u128.rs diff --git a/library/core/tests/num/u16.rs b/library/coretests/tests/num/u16.rs similarity index 100% rename from library/core/tests/num/u16.rs rename to library/coretests/tests/num/u16.rs diff --git a/library/core/tests/num/u32.rs b/library/coretests/tests/num/u32.rs similarity index 100% rename from library/core/tests/num/u32.rs rename to library/coretests/tests/num/u32.rs diff --git a/library/core/tests/num/u64.rs b/library/coretests/tests/num/u64.rs similarity index 100% rename from library/core/tests/num/u64.rs rename to library/coretests/tests/num/u64.rs diff --git a/library/core/tests/num/u8.rs b/library/coretests/tests/num/u8.rs similarity index 100% rename from library/core/tests/num/u8.rs rename to library/coretests/tests/num/u8.rs diff --git a/library/core/tests/num/uint_macros.rs b/library/coretests/tests/num/uint_macros.rs similarity index 100% rename from library/core/tests/num/uint_macros.rs rename to library/coretests/tests/num/uint_macros.rs diff --git a/library/core/tests/num/wrapping.rs b/library/coretests/tests/num/wrapping.rs similarity index 99% rename from library/core/tests/num/wrapping.rs rename to library/coretests/tests/num/wrapping.rs index c5a7198839517..0b9fca8455b81 100644 --- a/library/core/tests/num/wrapping.rs +++ b/library/coretests/tests/num/wrapping.rs @@ -64,14 +64,12 @@ wrapping_test!(test_wrapping_i8, i8, i8::MIN, i8::MAX); wrapping_test!(test_wrapping_i16, i16, i16::MIN, i16::MAX); wrapping_test!(test_wrapping_i32, i32, i32::MIN, i32::MAX); wrapping_test!(test_wrapping_i64, i64, i64::MIN, i64::MAX); -#[cfg(not(target_os = "emscripten"))] wrapping_test!(test_wrapping_i128, i128, i128::MIN, i128::MAX); wrapping_test!(test_wrapping_isize, isize, isize::MIN, isize::MAX); wrapping_test!(test_wrapping_u8, u8, u8::MIN, u8::MAX); wrapping_test!(test_wrapping_u16, u16, u16::MIN, u16::MAX); wrapping_test!(test_wrapping_u32, u32, u32::MIN, u32::MAX); wrapping_test!(test_wrapping_u64, u64, u64::MIN, u64::MAX); -#[cfg(not(target_os = "emscripten"))] wrapping_test!(test_wrapping_u128, u128, u128::MIN, u128::MAX); wrapping_test!(test_wrapping_usize, usize, usize::MIN, usize::MAX); diff --git a/library/core/tests/ops.rs b/library/coretests/tests/ops.rs similarity index 100% rename from library/core/tests/ops.rs rename to library/coretests/tests/ops.rs diff --git a/library/core/tests/ops/control_flow.rs b/library/coretests/tests/ops/control_flow.rs similarity index 100% rename from library/core/tests/ops/control_flow.rs rename to library/coretests/tests/ops/control_flow.rs diff --git a/library/core/tests/ops/from_residual.rs b/library/coretests/tests/ops/from_residual.rs similarity index 100% rename from library/core/tests/ops/from_residual.rs rename to library/coretests/tests/ops/from_residual.rs diff --git a/library/core/tests/option.rs b/library/coretests/tests/option.rs similarity index 100% rename from library/core/tests/option.rs rename to library/coretests/tests/option.rs diff --git a/library/core/tests/panic.rs b/library/coretests/tests/panic.rs similarity index 100% rename from library/core/tests/panic.rs rename to library/coretests/tests/panic.rs diff --git a/library/core/tests/panic/location.rs b/library/coretests/tests/panic/location.rs similarity index 100% rename from library/core/tests/panic/location.rs rename to library/coretests/tests/panic/location.rs diff --git a/library/core/tests/pattern.rs b/library/coretests/tests/pattern.rs similarity index 100% rename from library/core/tests/pattern.rs rename to library/coretests/tests/pattern.rs diff --git a/library/core/tests/pin.rs b/library/coretests/tests/pin.rs similarity index 96% rename from library/core/tests/pin.rs rename to library/coretests/tests/pin.rs index 026d2ca8de26a..b3fb06e710d44 100644 --- a/library/core/tests/pin.rs +++ b/library/coretests/tests/pin.rs @@ -28,7 +28,9 @@ fn pin_const() { const fn pin_mut_const() { let _ = Pin::new(&mut 2).into_ref(); let _ = Pin::new(&mut 2).get_mut(); - let _ = unsafe { Pin::new(&mut 2).get_unchecked_mut() }; + unsafe { + let _ = Pin::new(&mut 2).get_unchecked_mut(); + } } pin_mut_const(); diff --git a/library/core/tests/pin_macro.rs b/library/coretests/tests/pin_macro.rs similarity index 100% rename from library/core/tests/pin_macro.rs rename to library/coretests/tests/pin_macro.rs diff --git a/library/core/tests/ptr.rs b/library/coretests/tests/ptr.rs similarity index 98% rename from library/core/tests/ptr.rs rename to library/coretests/tests/ptr.rs index e6825d8e39e2c..0c9f9b338b0c1 100644 --- a/library/core/tests/ptr.rs +++ b/library/coretests/tests/ptr.rs @@ -42,11 +42,11 @@ fn test() { let mut v1 = vec![0u16, 0u16, 0u16]; copy(v0.as_ptr().offset(1), v1.as_mut_ptr().offset(1), 1); - assert!((v1[0] == 0u16 && v1[1] == 32001u16 && v1[2] == 0u16)); + assert!(v1[0] == 0u16 && v1[1] == 32001u16 && v1[2] == 0u16); copy(v0.as_ptr().offset(2), v1.as_mut_ptr(), 1); - assert!((v1[0] == 32002u16 && v1[1] == 32001u16 && v1[2] == 0u16)); + assert!(v1[0] == 32002u16 && v1[1] == 32001u16 && v1[2] == 0u16); copy(v0.as_ptr(), v1.as_mut_ptr().offset(2), 1); - assert!((v1[0] == 32002u16 && v1[1] == 32001u16 && v1[2] == 32000u16)); + assert!(v1[0] == 32002u16 && v1[1] == 32001u16 && v1[2] == 32000u16); } } @@ -97,7 +97,7 @@ fn test_is_null() { let nmi: *mut dyn ToString = null_mut::(); assert!(nmi.is_null()); - extern "C" { + unsafe extern "C" { type Extern; } let ec: *const Extern = null::(); @@ -304,11 +304,11 @@ fn test_const_nonnull_new() { #[test] #[cfg(unix)] // printf may not be available on other platforms #[allow(deprecated)] // For SipHasher -#[cfg_attr(not(bootstrap), allow(unpredictable_function_pointer_comparisons))] +#[allow(unpredictable_function_pointer_comparisons)] pub fn test_variadic_fnptr() { use core::ffi; use core::hash::{Hash, SipHasher}; - extern "C" { + unsafe extern "C" { // This needs to use the correct function signature even though it isn't called as some // codegen backends make it UB to declare a function with multiple conflicting signatures // (like LLVM) while others straight up return an error (like Cranelift). @@ -506,7 +506,7 @@ fn offset_from() { fn ptr_metadata() { struct Unit; struct Pair(A, B); - extern "C" { + unsafe extern "C" { type Extern; } let () = metadata(&()); diff --git a/library/core/tests/result.rs b/library/coretests/tests/result.rs similarity index 100% rename from library/core/tests/result.rs rename to library/coretests/tests/result.rs diff --git a/library/core/tests/simd.rs b/library/coretests/tests/simd.rs similarity index 100% rename from library/core/tests/simd.rs rename to library/coretests/tests/simd.rs diff --git a/library/coretests/tests/slice.rs b/library/coretests/tests/slice.rs new file mode 100644 index 0000000000000..1c5c8a9ebf258 --- /dev/null +++ b/library/coretests/tests/slice.rs @@ -0,0 +1,2689 @@ +use core::cell::Cell; +use core::cmp::Ordering; +use core::mem::MaybeUninit; +use core::num::NonZero; +use core::ops::{Range, RangeInclusive}; +use core::slice; + +use rand::seq::IndexedRandom; + +#[test] +fn test_position() { + let b = [1, 2, 3, 5, 5]; + assert_eq!(b.iter().position(|&v| v == 9), None); + assert_eq!(b.iter().position(|&v| v == 5), Some(3)); + assert_eq!(b.iter().position(|&v| v == 3), Some(2)); + assert_eq!(b.iter().position(|&v| v == 0), None); +} + +#[test] +fn test_rposition() { + let b = [1, 2, 3, 5, 5]; + assert_eq!(b.iter().rposition(|&v| v == 9), None); + assert_eq!(b.iter().rposition(|&v| v == 5), Some(4)); + assert_eq!(b.iter().rposition(|&v| v == 3), Some(2)); + assert_eq!(b.iter().rposition(|&v| v == 0), None); +} + +#[test] +fn test_binary_search() { + let b: [i32; 0] = []; + assert_eq!(b.binary_search(&5), Err(0)); + + let b = [4]; + assert_eq!(b.binary_search(&3), Err(0)); + assert_eq!(b.binary_search(&4), Ok(0)); + assert_eq!(b.binary_search(&5), Err(1)); + + let b = [1, 2, 4, 6, 8, 9]; + assert_eq!(b.binary_search(&5), Err(3)); + assert_eq!(b.binary_search(&6), Ok(3)); + assert_eq!(b.binary_search(&7), Err(4)); + assert_eq!(b.binary_search(&8), Ok(4)); + + let b = [1, 2, 4, 5, 6, 8]; + assert_eq!(b.binary_search(&9), Err(6)); + + let b = [1, 2, 4, 6, 7, 8, 9]; + assert_eq!(b.binary_search(&6), Ok(3)); + assert_eq!(b.binary_search(&5), Err(3)); + assert_eq!(b.binary_search(&8), Ok(5)); + + let b = [1, 2, 4, 5, 6, 8, 9]; + assert_eq!(b.binary_search(&7), Err(5)); + assert_eq!(b.binary_search(&0), Err(0)); + + let b = [1, 3, 3, 3, 7]; + assert_eq!(b.binary_search(&0), Err(0)); + assert_eq!(b.binary_search(&1), Ok(0)); + assert_eq!(b.binary_search(&2), Err(1)); + assert!(match b.binary_search(&3) { + Ok(1..=3) => true, + _ => false, + }); + assert!(match b.binary_search(&3) { + Ok(1..=3) => true, + _ => false, + }); + assert_eq!(b.binary_search(&4), Err(4)); + assert_eq!(b.binary_search(&5), Err(4)); + assert_eq!(b.binary_search(&6), Err(4)); + assert_eq!(b.binary_search(&7), Ok(4)); + assert_eq!(b.binary_search(&8), Err(5)); + + let b = [(); usize::MAX]; + assert_eq!(b.binary_search(&()), Ok(usize::MAX - 1)); +} + +#[test] +fn test_binary_search_by_overflow() { + let b = [(); usize::MAX]; + assert_eq!(b.binary_search_by(|_| Ordering::Equal), Ok(usize::MAX - 1)); + assert_eq!(b.binary_search_by(|_| Ordering::Greater), Err(0)); + assert_eq!(b.binary_search_by(|_| Ordering::Less), Err(usize::MAX)); +} + +#[test] +// Test implementation specific behavior when finding equivalent elements. +// It is ok to break this test but when you do a crater run is highly advisable. +fn test_binary_search_implementation_details() { + let b = [1, 1, 2, 2, 3, 3, 3]; + assert_eq!(b.binary_search(&1), Ok(1)); + assert_eq!(b.binary_search(&2), Ok(3)); + assert_eq!(b.binary_search(&3), Ok(6)); + let b = [1, 1, 1, 1, 1, 3, 3, 3, 3]; + assert_eq!(b.binary_search(&1), Ok(4)); + assert_eq!(b.binary_search(&3), Ok(8)); + let b = [1, 1, 1, 1, 3, 3, 3, 3, 3]; + assert_eq!(b.binary_search(&1), Ok(3)); + assert_eq!(b.binary_search(&3), Ok(8)); +} + +#[test] +fn test_partition_point() { + let b: [i32; 0] = []; + assert_eq!(b.partition_point(|&x| x < 5), 0); + + let b = [4]; + assert_eq!(b.partition_point(|&x| x < 3), 0); + assert_eq!(b.partition_point(|&x| x < 4), 0); + assert_eq!(b.partition_point(|&x| x < 5), 1); + + let b = [1, 2, 4, 6, 8, 9]; + assert_eq!(b.partition_point(|&x| x < 5), 3); + assert_eq!(b.partition_point(|&x| x < 6), 3); + assert_eq!(b.partition_point(|&x| x < 7), 4); + assert_eq!(b.partition_point(|&x| x < 8), 4); + + let b = [1, 2, 4, 5, 6, 8]; + assert_eq!(b.partition_point(|&x| x < 9), 6); + + let b = [1, 2, 4, 6, 7, 8, 9]; + assert_eq!(b.partition_point(|&x| x < 6), 3); + assert_eq!(b.partition_point(|&x| x < 5), 3); + assert_eq!(b.partition_point(|&x| x < 8), 5); + + let b = [1, 2, 4, 5, 6, 8, 9]; + assert_eq!(b.partition_point(|&x| x < 7), 5); + assert_eq!(b.partition_point(|&x| x < 0), 0); + + let b = [1, 3, 3, 3, 7]; + assert_eq!(b.partition_point(|&x| x < 0), 0); + assert_eq!(b.partition_point(|&x| x < 1), 0); + assert_eq!(b.partition_point(|&x| x < 2), 1); + assert_eq!(b.partition_point(|&x| x < 3), 1); + assert_eq!(b.partition_point(|&x| x < 4), 4); + assert_eq!(b.partition_point(|&x| x < 5), 4); + assert_eq!(b.partition_point(|&x| x < 6), 4); + assert_eq!(b.partition_point(|&x| x < 7), 4); + assert_eq!(b.partition_point(|&x| x < 8), 5); +} + +#[test] +fn test_iterator_advance_by() { + let v = &[0, 1, 2, 3, 4]; + + for i in 0..=v.len() { + let mut iter = v.iter(); + assert_eq!(iter.advance_by(i), Ok(())); + assert_eq!(iter.as_slice(), &v[i..]); + } + + let mut iter = v.iter(); + assert_eq!(iter.advance_by(v.len() + 1), Err(NonZero::new(1).unwrap())); + assert_eq!(iter.as_slice(), &[]); + + let mut iter = v.iter(); + assert_eq!(iter.advance_by(3), Ok(())); + assert_eq!(iter.as_slice(), &v[3..]); + assert_eq!(iter.advance_by(2), Ok(())); + assert_eq!(iter.as_slice(), &[]); + assert_eq!(iter.advance_by(0), Ok(())); +} + +#[test] +fn test_iterator_advance_back_by() { + let v = &[0, 1, 2, 3, 4]; + + for i in 0..=v.len() { + let mut iter = v.iter(); + assert_eq!(iter.advance_back_by(i), Ok(())); + assert_eq!(iter.as_slice(), &v[..v.len() - i]); + } + + let mut iter = v.iter(); + assert_eq!(iter.advance_back_by(v.len() + 1), Err(NonZero::new(1).unwrap())); + assert_eq!(iter.as_slice(), &[]); + + let mut iter = v.iter(); + assert_eq!(iter.advance_back_by(3), Ok(())); + assert_eq!(iter.as_slice(), &v[..v.len() - 3]); + assert_eq!(iter.advance_back_by(2), Ok(())); + assert_eq!(iter.as_slice(), &[]); + assert_eq!(iter.advance_back_by(0), Ok(())); +} + +#[test] +fn test_iterator_nth() { + let v: &[_] = &[0, 1, 2, 3, 4]; + for i in 0..v.len() { + assert_eq!(v.iter().nth(i).unwrap(), &v[i]); + } + assert_eq!(v.iter().nth(v.len()), None); + + let mut iter = v.iter(); + assert_eq!(iter.nth(2).unwrap(), &v[2]); + assert_eq!(iter.nth(1).unwrap(), &v[4]); +} + +#[test] +fn test_iterator_nth_back() { + let v: &[_] = &[0, 1, 2, 3, 4]; + for i in 0..v.len() { + assert_eq!(v.iter().nth_back(i).unwrap(), &v[v.len() - i - 1]); + } + assert_eq!(v.iter().nth_back(v.len()), None); + + let mut iter = v.iter(); + assert_eq!(iter.nth_back(2).unwrap(), &v[2]); + assert_eq!(iter.nth_back(1).unwrap(), &v[0]); +} + +#[test] +fn test_iterator_last() { + let v: &[_] = &[0, 1, 2, 3, 4]; + assert_eq!(v.iter().last().unwrap(), &4); + assert_eq!(v[..1].iter().last().unwrap(), &0); +} + +#[test] +fn test_iterator_count() { + let v: &[_] = &[0, 1, 2, 3, 4]; + assert_eq!(v.iter().count(), 5); + + let mut iter2 = v.iter(); + iter2.next(); + iter2.next(); + assert_eq!(iter2.count(), 3); +} + +#[test] +fn test_chunks_count() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.chunks(3); + assert_eq!(c.count(), 2); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.chunks(2); + assert_eq!(c2.count(), 3); + + let v3: &[i32] = &[]; + let c3 = v3.chunks(2); + assert_eq!(c3.count(), 0); +} + +#[test] +fn test_chunks_nth() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.chunks(2); + assert_eq!(c.nth(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[4, 5]); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let mut c2 = v2.chunks(3); + assert_eq!(c2.nth(1).unwrap(), &[3, 4]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_chunks_next() { + let v = [0, 1, 2, 3, 4, 5]; + let mut c = v.chunks(2); + assert_eq!(c.next().unwrap(), &[0, 1]); + assert_eq!(c.next().unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[4, 5]); + assert_eq!(c.next(), None); + + let v = [0, 1, 2, 3, 4, 5, 6, 7]; + let mut c = v.chunks(3); + assert_eq!(c.next().unwrap(), &[0, 1, 2]); + assert_eq!(c.next().unwrap(), &[3, 4, 5]); + assert_eq!(c.next().unwrap(), &[6, 7]); + assert_eq!(c.next(), None); +} + +#[test] +fn test_chunks_next_back() { + let v = [0, 1, 2, 3, 4, 5]; + let mut c = v.chunks(2); + assert_eq!(c.next_back().unwrap(), &[4, 5]); + assert_eq!(c.next_back().unwrap(), &[2, 3]); + assert_eq!(c.next_back().unwrap(), &[0, 1]); + assert_eq!(c.next_back(), None); + + let v = [0, 1, 2, 3, 4, 5, 6, 7]; + let mut c = v.chunks(3); + assert_eq!(c.next_back().unwrap(), &[6, 7]); + assert_eq!(c.next_back().unwrap(), &[3, 4, 5]); + assert_eq!(c.next_back().unwrap(), &[0, 1, 2]); + assert_eq!(c.next_back(), None); +} + +#[test] +fn test_chunks_nth_back() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.chunks(2); + assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[0, 1]); + assert_eq!(c.next(), None); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let mut c2 = v2.chunks(3); + assert_eq!(c2.nth_back(1).unwrap(), &[0, 1, 2]); + assert_eq!(c2.next(), None); + assert_eq!(c2.next_back(), None); + + let v3: &[i32] = &[0, 1, 2, 3, 4]; + let mut c3 = v3.chunks(10); + assert_eq!(c3.nth_back(0).unwrap(), &[0, 1, 2, 3, 4]); + assert_eq!(c3.next(), None); + + let v4: &[i32] = &[0, 1, 2]; + let mut c4 = v4.chunks(10); + assert_eq!(c4.nth_back(1_000_000_000usize), None); +} + +#[test] +fn test_chunks_last() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.chunks(2); + assert_eq!(c.last().unwrap()[1], 5); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.chunks(2); + assert_eq!(c2.last().unwrap()[0], 4); +} + +#[test] +fn test_chunks_zip() { + let v1: &[i32] = &[0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let res = v1 + .chunks(2) + .zip(v2.chunks(2)) + .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) + .collect::>(); + assert_eq!(res, vec![14, 22, 14]); +} + +#[test] +fn test_chunks_mut_count() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let c = v.chunks_mut(3); + assert_eq!(c.count(), 2); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c2 = v2.chunks_mut(2); + assert_eq!(c2.count(), 3); + + let v3: &mut [i32] = &mut []; + let c3 = v3.chunks_mut(2); + assert_eq!(c3.count(), 0); +} + +#[test] +fn test_chunks_mut_nth() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let mut c = v.chunks_mut(2); + assert_eq!(c.nth(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[4, 5]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let mut c2 = v2.chunks_mut(3); + assert_eq!(c2.nth(1).unwrap(), &[3, 4]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_chunks_mut_nth_back() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let mut c = v.chunks_mut(2); + assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[0, 1]); + + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let mut c1 = v1.chunks_mut(3); + assert_eq!(c1.nth_back(1).unwrap(), &[0, 1, 2]); + assert_eq!(c1.next(), None); + + let v3: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let mut c3 = v3.chunks_mut(10); + assert_eq!(c3.nth_back(0).unwrap(), &[0, 1, 2, 3, 4]); + assert_eq!(c3.next(), None); + + let v4: &mut [i32] = &mut [0, 1, 2]; + let mut c4 = v4.chunks_mut(10); + assert_eq!(c4.nth_back(1_000_000_000usize), None); +} + +#[test] +fn test_chunks_mut_last() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let c = v.chunks_mut(2); + assert_eq!(c.last().unwrap(), &[4, 5]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c2 = v2.chunks_mut(2); + assert_eq!(c2.last().unwrap(), &[4]); +} + +#[test] +fn test_chunks_mut_zip() { + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + for (a, b) in v1.chunks_mut(2).zip(v2.chunks(2)) { + let sum = b.iter().sum::(); + for v in a { + *v += sum; + } + } + assert_eq!(v1, [13, 14, 19, 20, 14]); +} + +#[test] +fn test_chunks_mut_zip_aliasing() { + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let mut it = v1.chunks_mut(2).zip(v2.chunks(2)); + let first = it.next().unwrap(); + let _ = it.next().unwrap(); + assert_eq!(first, (&mut [0, 1][..], &[6, 7][..])); +} + +#[test] +fn test_chunks_exact_mut_zip_aliasing() { + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let mut it = v1.chunks_exact_mut(2).zip(v2.chunks(2)); + let first = it.next().unwrap(); + let _ = it.next().unwrap(); + assert_eq!(first, (&mut [0, 1][..], &[6, 7][..])); +} + +#[test] +fn test_rchunks_mut_zip_aliasing() { + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let mut it = v1.rchunks_mut(2).zip(v2.chunks(2)); + let first = it.next().unwrap(); + let _ = it.next().unwrap(); + assert_eq!(first, (&mut [3, 4][..], &[6, 7][..])); +} + +#[test] +fn test_rchunks_exact_mut_zip_aliasing() { + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let mut it = v1.rchunks_exact_mut(2).zip(v2.chunks(2)); + let first = it.next().unwrap(); + let _ = it.next().unwrap(); + assert_eq!(first, (&mut [3, 4][..], &[6, 7][..])); +} + +#[test] +fn test_chunks_exact_count() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.chunks_exact(3); + assert_eq!(c.count(), 2); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.chunks_exact(2); + assert_eq!(c2.count(), 2); + + let v3: &[i32] = &[]; + let c3 = v3.chunks_exact(2); + assert_eq!(c3.count(), 0); +} + +#[test] +fn test_chunks_exact_nth() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.chunks_exact(2); + assert_eq!(c.nth(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[4, 5]); + + let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; + let mut c2 = v2.chunks_exact(3); + assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_chunks_exact_nth_back() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.chunks_exact(2); + assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[0, 1]); + assert_eq!(c.next(), None); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let mut c2 = v2.chunks_exact(3); + assert_eq!(c2.nth_back(0).unwrap(), &[0, 1, 2]); + assert_eq!(c2.next(), None); + assert_eq!(c2.next_back(), None); + + let v3: &[i32] = &[0, 1, 2, 3, 4]; + let mut c3 = v3.chunks_exact(10); + assert_eq!(c3.nth_back(0), None); +} + +#[test] +fn test_chunks_exact_last() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.chunks_exact(2); + assert_eq!(c.last().unwrap(), &[4, 5]); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.chunks_exact(2); + assert_eq!(c2.last().unwrap(), &[2, 3]); +} + +#[test] +fn test_chunks_exact_remainder() { + let v: &[i32] = &[0, 1, 2, 3, 4]; + let c = v.chunks_exact(2); + assert_eq!(c.remainder(), &[4]); +} + +#[test] +fn test_chunks_exact_zip() { + let v1: &[i32] = &[0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let res = v1 + .chunks_exact(2) + .zip(v2.chunks_exact(2)) + .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) + .collect::>(); + assert_eq!(res, vec![14, 22]); +} + +#[test] +fn test_chunks_exact_mut_count() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let c = v.chunks_exact_mut(3); + assert_eq!(c.count(), 2); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c2 = v2.chunks_exact_mut(2); + assert_eq!(c2.count(), 2); + + let v3: &mut [i32] = &mut []; + let c3 = v3.chunks_exact_mut(2); + assert_eq!(c3.count(), 0); +} + +#[test] +fn test_chunks_exact_mut_nth() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let mut c = v.chunks_exact_mut(2); + assert_eq!(c.nth(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[4, 5]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; + let mut c2 = v2.chunks_exact_mut(3); + assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_chunks_exact_mut_nth_back() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let mut c = v.chunks_exact_mut(2); + assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[0, 1]); + assert_eq!(c.next(), None); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let mut c2 = v2.chunks_exact_mut(3); + assert_eq!(c2.nth_back(0).unwrap(), &[0, 1, 2]); + assert_eq!(c2.next(), None); + assert_eq!(c2.next_back(), None); + + let v3: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let mut c3 = v3.chunks_exact_mut(10); + assert_eq!(c3.nth_back(0), None); +} + +#[test] +fn test_chunks_exact_mut_last() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let c = v.chunks_exact_mut(2); + assert_eq!(c.last().unwrap(), &[4, 5]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c2 = v2.chunks_exact_mut(2); + assert_eq!(c2.last().unwrap(), &[2, 3]); +} + +#[test] +fn test_chunks_exact_mut_remainder() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c = v.chunks_exact_mut(2); + assert_eq!(c.into_remainder(), &[4]); +} + +#[test] +fn test_chunks_exact_mut_zip() { + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + for (a, b) in v1.chunks_exact_mut(2).zip(v2.chunks_exact(2)) { + let sum = b.iter().sum::(); + for v in a { + *v += sum; + } + } + assert_eq!(v1, [13, 14, 19, 20, 4]); +} + +#[test] +fn test_array_chunks_infer() { + let v: &[i32] = &[0, 1, 2, 3, 4, -4]; + let c = v.array_chunks(); + for &[a, b, c] in c { + assert_eq!(a + b + c, 3); + } + + let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; + let total = v2.array_chunks().map(|&[a, b]| a * b).sum::(); + assert_eq!(total, 2 * 3 + 4 * 5); +} + +#[test] +fn test_array_chunks_count() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.array_chunks::<3>(); + assert_eq!(c.count(), 2); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.array_chunks::<2>(); + assert_eq!(c2.count(), 2); + + let v3: &[i32] = &[]; + let c3 = v3.array_chunks::<2>(); + assert_eq!(c3.count(), 0); +} + +#[test] +fn test_array_chunks_nth() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.array_chunks::<2>(); + assert_eq!(c.nth(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[4, 5]); + + let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; + let mut c2 = v2.array_chunks::<3>(); + assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_array_chunks_nth_back() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.array_chunks::<2>(); + assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[0, 1]); + assert_eq!(c.next(), None); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let mut c2 = v2.array_chunks::<3>(); + assert_eq!(c2.nth_back(0).unwrap(), &[0, 1, 2]); + assert_eq!(c2.next(), None); + assert_eq!(c2.next_back(), None); + + let v3: &[i32] = &[0, 1, 2, 3, 4]; + let mut c3 = v3.array_chunks::<10>(); + assert_eq!(c3.nth_back(0), None); +} + +#[test] +fn test_array_chunks_last() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.array_chunks::<2>(); + assert_eq!(c.last().unwrap(), &[4, 5]); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.array_chunks::<2>(); + assert_eq!(c2.last().unwrap(), &[2, 3]); +} + +#[test] +fn test_array_chunks_remainder() { + let v: &[i32] = &[0, 1, 2, 3, 4]; + let c = v.array_chunks::<2>(); + assert_eq!(c.remainder(), &[4]); +} + +#[test] +fn test_array_chunks_zip() { + let v1: &[i32] = &[0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let res = v1 + .array_chunks::<2>() + .zip(v2.array_chunks::<2>()) + .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) + .collect::>(); + assert_eq!(res, vec![14, 22]); +} + +#[test] +fn test_array_chunks_mut_infer() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; + for a in v.array_chunks_mut() { + let sum = a.iter().sum::(); + *a = [sum; 3]; + } + assert_eq!(v, &[3, 3, 3, 12, 12, 12, 6]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; + v2.array_chunks_mut().for_each(|[a, b]| core::mem::swap(a, b)); + assert_eq!(v2, &[1, 0, 3, 2, 5, 4, 6]); +} + +#[test] +fn test_array_chunks_mut_count() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let c = v.array_chunks_mut::<3>(); + assert_eq!(c.count(), 2); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c2 = v2.array_chunks_mut::<2>(); + assert_eq!(c2.count(), 2); + + let v3: &mut [i32] = &mut []; + let c3 = v3.array_chunks_mut::<2>(); + assert_eq!(c3.count(), 0); +} + +#[test] +fn test_array_chunks_mut_nth() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let mut c = v.array_chunks_mut::<2>(); + assert_eq!(c.nth(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[4, 5]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; + let mut c2 = v2.array_chunks_mut::<3>(); + assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_array_chunks_mut_nth_back() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let mut c = v.array_chunks_mut::<2>(); + assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[0, 1]); + assert_eq!(c.next(), None); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let mut c2 = v2.array_chunks_mut::<3>(); + assert_eq!(c2.nth_back(0).unwrap(), &[0, 1, 2]); + assert_eq!(c2.next(), None); + assert_eq!(c2.next_back(), None); + + let v3: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let mut c3 = v3.array_chunks_mut::<10>(); + assert_eq!(c3.nth_back(0), None); +} + +#[test] +fn test_array_chunks_mut_last() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let c = v.array_chunks_mut::<2>(); + assert_eq!(c.last().unwrap(), &[4, 5]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c2 = v2.array_chunks_mut::<2>(); + assert_eq!(c2.last().unwrap(), &[2, 3]); +} + +#[test] +fn test_array_chunks_mut_remainder() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c = v.array_chunks_mut::<2>(); + assert_eq!(c.into_remainder(), &[4]); +} + +#[test] +fn test_array_chunks_mut_zip() { + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + for (a, b) in v1.array_chunks_mut::<2>().zip(v2.array_chunks::<2>()) { + let sum = b.iter().sum::(); + for v in a { + *v += sum; + } + } + assert_eq!(v1, [13, 14, 19, 20, 4]); +} + +#[test] +fn test_array_windows_infer() { + let v: &[i32] = &[0, 1, 0, 1]; + assert_eq!(v.array_windows::<2>().count(), 3); + let c = v.array_windows(); + for &[a, b] in c { + assert_eq!(a + b, 1); + } + + let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; + let total = v2.array_windows().map(|&[a, b, c]| a + b + c).sum::(); + assert_eq!(total, 3 + 6 + 9 + 12 + 15); +} + +#[test] +fn test_array_windows_count() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.array_windows::<3>(); + assert_eq!(c.count(), 4); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.array_windows::<6>(); + assert_eq!(c2.count(), 0); + + let v3: &[i32] = &[]; + let c3 = v3.array_windows::<2>(); + assert_eq!(c3.count(), 0); + + let v4: &[()] = &[(); usize::MAX]; + let c4 = v4.array_windows::<1>(); + assert_eq!(c4.count(), usize::MAX); +} + +#[test] +fn test_array_windows_nth() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let snd = v.array_windows::<4>().nth(1); + assert_eq!(snd, Some(&[1, 2, 3, 4])); + let mut arr_windows = v.array_windows::<2>(); + assert_ne!(arr_windows.nth(0), arr_windows.nth(0)); + let last = v.array_windows::<3>().last(); + assert_eq!(last, Some(&[3, 4, 5])); +} + +#[test] +fn test_array_windows_nth_back() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let snd = v.array_windows::<4>().nth_back(1); + assert_eq!(snd, Some(&[1, 2, 3, 4])); + let mut arr_windows = v.array_windows::<2>(); + assert_ne!(arr_windows.nth_back(0), arr_windows.nth_back(0)); +} + +#[test] +fn test_rchunks_count() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.rchunks(3); + assert_eq!(c.count(), 2); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.rchunks(2); + assert_eq!(c2.count(), 3); + + let v3: &[i32] = &[]; + let c3 = v3.rchunks(2); + assert_eq!(c3.count(), 0); +} + +#[test] +fn test_rchunks_nth() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.rchunks(2); + assert_eq!(c.nth(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[0, 1]); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let mut c2 = v2.rchunks(3); + assert_eq!(c2.nth(1).unwrap(), &[0, 1]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_rchunks_nth_back() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.rchunks(2); + assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); + assert_eq!(c.next_back().unwrap(), &[4, 5]); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let mut c2 = v2.rchunks(3); + assert_eq!(c2.nth_back(1).unwrap(), &[2, 3, 4]); + assert_eq!(c2.next_back(), None); +} + +#[test] +fn test_rchunks_next() { + let v = [0, 1, 2, 3, 4, 5]; + let mut c = v.rchunks(2); + assert_eq!(c.next().unwrap(), &[4, 5]); + assert_eq!(c.next().unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[0, 1]); + assert_eq!(c.next(), None); + + let v = [0, 1, 2, 3, 4, 5, 6, 7]; + let mut c = v.rchunks(3); + assert_eq!(c.next().unwrap(), &[5, 6, 7]); + assert_eq!(c.next().unwrap(), &[2, 3, 4]); + assert_eq!(c.next().unwrap(), &[0, 1]); + assert_eq!(c.next(), None); +} + +#[test] +fn test_rchunks_next_back() { + let v = [0, 1, 2, 3, 4, 5]; + let mut c = v.rchunks(2); + assert_eq!(c.next_back().unwrap(), &[0, 1]); + assert_eq!(c.next_back().unwrap(), &[2, 3]); + assert_eq!(c.next_back().unwrap(), &[4, 5]); + assert_eq!(c.next_back(), None); + + let v = [0, 1, 2, 3, 4, 5, 6, 7]; + let mut c = v.rchunks(3); + assert_eq!(c.next_back().unwrap(), &[0, 1]); + assert_eq!(c.next_back().unwrap(), &[2, 3, 4]); + assert_eq!(c.next_back().unwrap(), &[5, 6, 7]); + assert_eq!(c.next_back(), None); +} + +#[test] +fn test_rchunks_last() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.rchunks(2); + assert_eq!(c.last().unwrap()[1], 1); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.rchunks(2); + assert_eq!(c2.last().unwrap()[0], 0); +} + +#[test] +fn test_rchunks_zip() { + let v1: &[i32] = &[0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let res = v1 + .rchunks(2) + .zip(v2.rchunks(2)) + .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) + .collect::>(); + assert_eq!(res, vec![26, 18, 6]); +} + +#[test] +fn test_rchunks_mut_count() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let c = v.rchunks_mut(3); + assert_eq!(c.count(), 2); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c2 = v2.rchunks_mut(2); + assert_eq!(c2.count(), 3); + + let v3: &mut [i32] = &mut []; + let c3 = v3.rchunks_mut(2); + assert_eq!(c3.count(), 0); +} + +#[test] +fn test_rchunks_mut_nth() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let mut c = v.rchunks_mut(2); + assert_eq!(c.nth(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[0, 1]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let mut c2 = v2.rchunks_mut(3); + assert_eq!(c2.nth(1).unwrap(), &[0, 1]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_rchunks_mut_nth_back() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let mut c = v.rchunks_mut(2); + assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); + assert_eq!(c.next_back().unwrap(), &[4, 5]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let mut c2 = v2.rchunks_mut(3); + assert_eq!(c2.nth_back(1).unwrap(), &[2, 3, 4]); + assert_eq!(c2.next_back(), None); +} + +#[test] +fn test_rchunks_mut_next() { + let mut v = [0, 1, 2, 3, 4, 5]; + let mut c = v.rchunks_mut(2); + assert_eq!(c.next().unwrap(), &mut [4, 5]); + assert_eq!(c.next().unwrap(), &mut [2, 3]); + assert_eq!(c.next().unwrap(), &mut [0, 1]); + assert_eq!(c.next(), None); + + let mut v = [0, 1, 2, 3, 4, 5, 6, 7]; + let mut c = v.rchunks_mut(3); + assert_eq!(c.next().unwrap(), &mut [5, 6, 7]); + assert_eq!(c.next().unwrap(), &mut [2, 3, 4]); + assert_eq!(c.next().unwrap(), &mut [0, 1]); + assert_eq!(c.next(), None); +} + +#[test] +fn test_rchunks_mut_next_back() { + let mut v = [0, 1, 2, 3, 4, 5]; + let mut c = v.rchunks_mut(2); + assert_eq!(c.next_back().unwrap(), &mut [0, 1]); + assert_eq!(c.next_back().unwrap(), &mut [2, 3]); + assert_eq!(c.next_back().unwrap(), &mut [4, 5]); + assert_eq!(c.next_back(), None); + + let mut v = [0, 1, 2, 3, 4, 5, 6, 7]; + let mut c = v.rchunks_mut(3); + assert_eq!(c.next_back().unwrap(), &mut [0, 1]); + assert_eq!(c.next_back().unwrap(), &mut [2, 3, 4]); + assert_eq!(c.next_back().unwrap(), &mut [5, 6, 7]); + assert_eq!(c.next_back(), None); +} + +#[test] +fn test_rchunks_mut_last() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let c = v.rchunks_mut(2); + assert_eq!(c.last().unwrap(), &[0, 1]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c2 = v2.rchunks_mut(2); + assert_eq!(c2.last().unwrap(), &[0]); +} + +#[test] +fn test_rchunks_mut_zip() { + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + for (a, b) in v1.rchunks_mut(2).zip(v2.rchunks(2)) { + let sum = b.iter().sum::(); + for v in a { + *v += sum; + } + } + assert_eq!(v1, [6, 16, 17, 22, 23]); +} + +#[test] +fn test_rchunks_exact_count() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.rchunks_exact(3); + assert_eq!(c.count(), 2); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.rchunks_exact(2); + assert_eq!(c2.count(), 2); + + let v3: &[i32] = &[]; + let c3 = v3.rchunks_exact(2); + assert_eq!(c3.count(), 0); +} + +#[test] +fn test_rchunks_exact_nth() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.rchunks_exact(2); + assert_eq!(c.nth(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[0, 1]); + + let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; + let mut c2 = v2.rchunks_exact(3); + assert_eq!(c2.nth(1).unwrap(), &[1, 2, 3]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_rchunks_exact_nth_back() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.rchunks_exact(2); + assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); + assert_eq!(c.next_back().unwrap(), &[4, 5]); + + let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; + let mut c2 = v2.rchunks_exact(3); + assert_eq!(c2.nth_back(1).unwrap(), &[4, 5, 6]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_rchunks_exact_last() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.rchunks_exact(2); + assert_eq!(c.last().unwrap(), &[0, 1]); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.rchunks_exact(2); + assert_eq!(c2.last().unwrap(), &[1, 2]); +} + +#[test] +fn test_rchunks_exact_remainder() { + let v: &[i32] = &[0, 1, 2, 3, 4]; + let c = v.rchunks_exact(2); + assert_eq!(c.remainder(), &[0]); +} + +#[test] +fn test_rchunks_exact_zip() { + let v1: &[i32] = &[0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let res = v1 + .rchunks_exact(2) + .zip(v2.rchunks_exact(2)) + .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) + .collect::>(); + assert_eq!(res, vec![26, 18]); +} + +#[test] +fn test_rchunks_exact_mut_count() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let c = v.rchunks_exact_mut(3); + assert_eq!(c.count(), 2); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c2 = v2.rchunks_exact_mut(2); + assert_eq!(c2.count(), 2); + + let v3: &mut [i32] = &mut []; + let c3 = v3.rchunks_exact_mut(2); + assert_eq!(c3.count(), 0); +} + +#[test] +fn test_rchunks_exact_mut_nth() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let mut c = v.rchunks_exact_mut(2); + assert_eq!(c.nth(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[0, 1]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; + let mut c2 = v2.rchunks_exact_mut(3); + assert_eq!(c2.nth(1).unwrap(), &[1, 2, 3]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_rchunks_exact_mut_nth_back() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let mut c = v.rchunks_exact_mut(2); + assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); + assert_eq!(c.next_back().unwrap(), &[4, 5]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; + let mut c2 = v2.rchunks_exact_mut(3); + assert_eq!(c2.nth_back(1).unwrap(), &[4, 5, 6]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_rchunks_exact_mut_last() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let c = v.rchunks_exact_mut(2); + assert_eq!(c.last().unwrap(), &[0, 1]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c2 = v2.rchunks_exact_mut(2); + assert_eq!(c2.last().unwrap(), &[1, 2]); +} + +#[test] +fn test_rchunks_exact_mut_remainder() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c = v.rchunks_exact_mut(2); + assert_eq!(c.into_remainder(), &[0]); +} + +#[test] +fn test_rchunks_exact_mut_zip() { + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + for (a, b) in v1.rchunks_exact_mut(2).zip(v2.rchunks_exact(2)) { + let sum = b.iter().sum::(); + for v in a { + *v += sum; + } + } + assert_eq!(v1, [0, 16, 17, 22, 23]); +} + +#[test] +fn chunks_mut_are_send_and_sync() { + use std::cell::Cell; + use std::slice::{ChunksExactMut, ChunksMut, RChunksExactMut, RChunksMut}; + use std::sync::MutexGuard; + + fn assert_send_and_sync() + where + ChunksMut<'static, Cell>: Send, + ChunksMut<'static, MutexGuard<'static, u32>>: Sync, + ChunksExactMut<'static, Cell>: Send, + ChunksExactMut<'static, MutexGuard<'static, u32>>: Sync, + RChunksMut<'static, Cell>: Send, + RChunksMut<'static, MutexGuard<'static, u32>>: Sync, + RChunksExactMut<'static, Cell>: Send, + RChunksExactMut<'static, MutexGuard<'static, u32>>: Sync, + { + } + + assert_send_and_sync(); +} + +#[test] +fn test_windows_count() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.windows(3); + assert_eq!(c.count(), 4); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.windows(6); + assert_eq!(c2.count(), 0); + + let v3: &[i32] = &[]; + let c3 = v3.windows(2); + assert_eq!(c3.count(), 0); + + let v4 = &[(); usize::MAX]; + let c4 = v4.windows(1); + assert_eq!(c4.count(), usize::MAX); +} + +#[test] +fn test_windows_nth() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.windows(2); + assert_eq!(c.nth(2).unwrap()[1], 3); + assert_eq!(c.next().unwrap()[0], 3); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let mut c2 = v2.windows(4); + assert_eq!(c2.nth(1).unwrap()[1], 2); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_windows_nth_back() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.windows(2); + assert_eq!(c.nth_back(2).unwrap()[0], 2); + assert_eq!(c.next_back().unwrap()[1], 2); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let mut c2 = v2.windows(4); + assert_eq!(c2.nth_back(1).unwrap()[1], 1); + assert_eq!(c2.next_back(), None); +} + +#[test] +fn test_windows_last() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.windows(2); + assert_eq!(c.last().unwrap()[1], 5); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.windows(2); + assert_eq!(c2.last().unwrap()[0], 3); +} + +#[test] +fn test_windows_zip() { + let v1: &[i32] = &[0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let res = v1 + .windows(2) + .zip(v2.windows(2)) + .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) + .collect::>(); + + assert_eq!(res, [14, 18, 22, 26]); +} + +#[test] +fn test_iter_ref_consistency() { + use std::fmt::Debug; + + fn test(x: T) { + let v: &[T] = &[x, x, x]; + let v_ptrs: [*const T; 3] = match v { + [v1, v2, v3] => [v1 as *const _, v2 as *const _, v3 as *const _], + _ => unreachable!(), + }; + let len = v.len(); + + // nth(i) + for i in 0..len { + assert_eq!(&v[i] as *const _, v_ptrs[i]); // check the v_ptrs array, just to be sure + let nth = v.iter().nth(i).unwrap(); + assert_eq!(nth as *const _, v_ptrs[i]); + } + assert_eq!(v.iter().nth(len), None, "nth(len) should return None"); + + // stepping through with nth(0) + { + let mut it = v.iter(); + for i in 0..len { + let next = it.nth(0).unwrap(); + assert_eq!(next as *const _, v_ptrs[i]); + } + assert_eq!(it.nth(0), None); + } + + // next() + { + let mut it = v.iter(); + for i in 0..len { + let remaining = len - i; + assert_eq!(it.size_hint(), (remaining, Some(remaining))); + + let next = it.next().unwrap(); + assert_eq!(next as *const _, v_ptrs[i]); + } + assert_eq!(it.size_hint(), (0, Some(0))); + assert_eq!(it.next(), None, "The final call to next() should return None"); + } + + // next_back() + { + let mut it = v.iter(); + for i in 0..len { + let remaining = len - i; + assert_eq!(it.size_hint(), (remaining, Some(remaining))); + + let prev = it.next_back().unwrap(); + assert_eq!(prev as *const _, v_ptrs[remaining - 1]); + } + assert_eq!(it.size_hint(), (0, Some(0))); + assert_eq!(it.next_back(), None, "The final call to next_back() should return None"); + } + } + + fn test_mut(x: T) { + let v: &mut [T] = &mut [x, x, x]; + let v_ptrs: [*mut T; 3] = match v { + &mut [ref v1, ref v2, ref v3] => { + [v1 as *const _ as *mut _, v2 as *const _ as *mut _, v3 as *const _ as *mut _] + } + _ => unreachable!(), + }; + let len = v.len(); + + // nth(i) + for i in 0..len { + assert_eq!(&mut v[i] as *mut _, v_ptrs[i]); // check the v_ptrs array, just to be sure + let nth = v.iter_mut().nth(i).unwrap(); + assert_eq!(nth as *mut _, v_ptrs[i]); + } + assert_eq!(v.iter().nth(len), None, "nth(len) should return None"); + + // stepping through with nth(0) + { + let mut it = v.iter(); + for i in 0..len { + let next = it.nth(0).unwrap(); + assert_eq!(next as *const _, v_ptrs[i]); + } + assert_eq!(it.nth(0), None); + } + + // next() + { + let mut it = v.iter_mut(); + for i in 0..len { + let remaining = len - i; + assert_eq!(it.size_hint(), (remaining, Some(remaining))); + + let next = it.next().unwrap(); + assert_eq!(next as *mut _, v_ptrs[i]); + } + assert_eq!(it.size_hint(), (0, Some(0))); + assert_eq!(it.next(), None, "The final call to next() should return None"); + } + + // next_back() + { + let mut it = v.iter_mut(); + for i in 0..len { + let remaining = len - i; + assert_eq!(it.size_hint(), (remaining, Some(remaining))); + + let prev = it.next_back().unwrap(); + assert_eq!(prev as *mut _, v_ptrs[remaining - 1]); + } + assert_eq!(it.size_hint(), (0, Some(0))); + assert_eq!(it.next_back(), None, "The final call to next_back() should return None"); + } + } + + // Make sure iterators and slice patterns yield consistent addresses for various types, + // including ZSTs. + test(0u32); + test(()); + test([0u32; 0]); // ZST with alignment > 0 + test_mut(0u32); + test_mut(()); + test_mut([0u32; 0]); // ZST with alignment > 0 +} + +// The current implementation of SliceIndex fails to handle methods +// orthogonally from range types; therefore, it is worth testing +// all of the indexing operations on each input. +mod slice_index { + // This checks all six indexing methods, given an input range that + // should succeed. (it is NOT suitable for testing invalid inputs) + macro_rules! assert_range_eq { + ($arr:expr, $range:expr, $expected:expr) => { + let mut arr = $arr; + let mut expected = $expected; + { + let s: &[_] = &arr; + let expected: &[_] = &expected; + + assert_eq!(&s[$range], expected, "(in assertion for: index)"); + assert_eq!(s.get($range), Some(expected), "(in assertion for: get)"); + unsafe { + assert_eq!( + s.get_unchecked($range), + expected, + "(in assertion for: get_unchecked)", + ); + } + } + { + let s: &mut [_] = &mut arr; + let expected: &mut [_] = &mut expected; + + assert_eq!(&mut s[$range], expected, "(in assertion for: index_mut)",); + assert_eq!( + s.get_mut($range), + Some(&mut expected[..]), + "(in assertion for: get_mut)", + ); + unsafe { + assert_eq!( + s.get_unchecked_mut($range), + expected, + "(in assertion for: get_unchecked_mut)", + ); + } + } + }; + } + + // Make sure the macro can actually detect bugs, + // because if it can't, then what are we even doing here? + // + // (Be aware this only demonstrates the ability to detect bugs + // in the FIRST method that panics, as the macro is not designed + // to be used in `should_panic`) + #[test] + #[should_panic(expected = "out of range")] + fn assert_range_eq_can_fail_by_panic() { + assert_range_eq!([0, 1, 2], 0..5, [0, 1, 2]); + } + + // (Be aware this only demonstrates the ability to detect bugs + // in the FIRST method it calls, as the macro is not designed + // to be used in `should_panic`) + #[test] + #[should_panic(expected = "==")] + fn assert_range_eq_can_fail_by_inequality() { + assert_range_eq!([0, 1, 2], 0..2, [0, 1, 2]); + } + + // Test cases for bad index operations. + // + // This generates `should_panic` test cases for Index/IndexMut + // and `None` test cases for get/get_mut. + macro_rules! panic_cases { + ($( + // each test case needs a unique name to namespace the tests + in mod $case_name:ident { + data: $data:expr; + + // optional: + // + // one or more similar inputs for which data[input] succeeds, + // and the corresponding output as an array. This helps validate + // "critical points" where an input range straddles the boundary + // between valid and invalid. + // (such as the input `len..len`, which is just barely valid) + $( + good: data[$good:expr] == $output:expr; + )* + + bad: data[$bad:expr]; + message: $expect_msg:expr; + } + )*) => {$( + mod $case_name { + #[allow(unused_imports)] + use core::ops::Bound; + + #[test] + fn pass() { + let mut v = $data; + + $( assert_range_eq!($data, $good, $output); )* + + { + let v: &[_] = &v; + assert_eq!(v.get($bad), None, "(in None assertion for get)"); + } + + { + let v: &mut [_] = &mut v; + assert_eq!(v.get_mut($bad), None, "(in None assertion for get_mut)"); + } + } + + #[test] + #[should_panic(expected = $expect_msg)] + fn index_fail() { + let v = $data; + let v: &[_] = &v; + let _v = &v[$bad]; + } + + #[test] + #[should_panic(expected = $expect_msg)] + fn index_mut_fail() { + let mut v = $data; + let v: &mut [_] = &mut v; + let _v = &mut v[$bad]; + } + } + )*}; + } + + #[test] + fn simple() { + let v = [0, 1, 2, 3, 4, 5]; + + assert_range_eq!(v, .., [0, 1, 2, 3, 4, 5]); + assert_range_eq!(v, ..2, [0, 1]); + assert_range_eq!(v, ..=1, [0, 1]); + assert_range_eq!(v, 2.., [2, 3, 4, 5]); + assert_range_eq!(v, 1..4, [1, 2, 3]); + assert_range_eq!(v, 1..=3, [1, 2, 3]); + } + + panic_cases! { + in mod rangefrom_len { + data: [0, 1, 2, 3, 4, 5]; + + good: data[6..] == []; + bad: data[7..]; + message: "out of range"; + } + + in mod rangeto_len { + data: [0, 1, 2, 3, 4, 5]; + + good: data[..6] == [0, 1, 2, 3, 4, 5]; + bad: data[..7]; + message: "out of range"; + } + + in mod rangetoinclusive_len { + data: [0, 1, 2, 3, 4, 5]; + + good: data[..=5] == [0, 1, 2, 3, 4, 5]; + bad: data[..=6]; + message: "out of range"; + } + + in mod rangeinclusive_len { + data: [0, 1, 2, 3, 4, 5]; + + good: data[0..=5] == [0, 1, 2, 3, 4, 5]; + bad: data[0..=6]; + message: "out of range"; + } + + in mod range_len_len { + data: [0, 1, 2, 3, 4, 5]; + + good: data[6..6] == []; + bad: data[7..7]; + message: "out of range"; + } + + in mod rangeinclusive_len_len { + data: [0, 1, 2, 3, 4, 5]; + + good: data[6..=5] == []; + bad: data[7..=6]; + message: "out of range"; + } + + in mod boundpair_len { + data: [0, 1, 2, 3, 4, 5]; + + good: data[(Bound::Included(6), Bound::Unbounded)] == []; + good: data[(Bound::Unbounded, Bound::Included(5))] == [0, 1, 2, 3, 4, 5]; + good: data[(Bound::Unbounded, Bound::Excluded(6))] == [0, 1, 2, 3, 4, 5]; + good: data[(Bound::Included(0), Bound::Included(5))] == [0, 1, 2, 3, 4, 5]; + good: data[(Bound::Included(0), Bound::Excluded(6))] == [0, 1, 2, 3, 4, 5]; + good: data[(Bound::Included(2), Bound::Excluded(4))] == [2, 3]; + good: data[(Bound::Excluded(1), Bound::Included(4))] == [2, 3, 4]; + good: data[(Bound::Excluded(5), Bound::Excluded(6))] == []; + good: data[(Bound::Included(6), Bound::Excluded(6))] == []; + good: data[(Bound::Excluded(5), Bound::Included(5))] == []; + good: data[(Bound::Included(6), Bound::Included(5))] == []; + bad: data[(Bound::Unbounded, Bound::Included(6))]; + message: "out of range"; + } + } + + panic_cases! { + in mod rangeinclusive_exhausted { + data: [0, 1, 2, 3, 4, 5]; + + good: data[0..=5] == [0, 1, 2, 3, 4, 5]; + good: data[{ + let mut iter = 0..=5; + iter.by_ref().count(); // exhaust it + iter + }] == []; + + // 0..=6 is out of range before exhaustion, so it + // stands to reason that it still would be after. + bad: data[{ + let mut iter = 0..=6; + iter.by_ref().count(); // exhaust it + iter + }]; + message: "out of range"; + } + } + + panic_cases! { + in mod range_neg_width { + data: [0, 1, 2, 3, 4, 5]; + + good: data[4..4] == []; + bad: data[4..3]; + message: "but ends at"; + } + + in mod rangeinclusive_neg_width { + data: [0, 1, 2, 3, 4, 5]; + + good: data[4..=3] == []; + bad: data[4..=2]; + message: "but ends at"; + } + + in mod boundpair_neg_width { + data: [0, 1, 2, 3, 4, 5]; + + good: data[(Bound::Included(4), Bound::Excluded(4))] == []; + bad: data[(Bound::Included(4), Bound::Excluded(3))]; + message: "but ends at"; + } + } + + panic_cases! { + in mod rangeinclusive_overflow { + data: [0, 1]; + + // note: using 0 specifically ensures that the result of overflowing is 0..0, + // so that `get` doesn't simply return None for the wrong reason. + bad: data[0 ..= usize::MAX]; + message: "maximum usize"; + } + + in mod rangetoinclusive_overflow { + data: [0, 1]; + + bad: data[..= usize::MAX]; + message: "maximum usize"; + } + + in mod boundpair_overflow_end { + data: [0; 1]; + + bad: data[(Bound::Unbounded, Bound::Included(usize::MAX))]; + message: "maximum usize"; + } + + in mod boundpair_overflow_start { + data: [0; 1]; + + bad: data[(Bound::Excluded(usize::MAX), Bound::Unbounded)]; + message: "maximum usize"; + } + } // panic_cases! +} + +#[test] +fn test_find_rfind() { + let v = [0, 1, 2, 3, 4, 5]; + let mut iter = v.iter(); + let mut i = v.len(); + while let Some(&elt) = iter.rfind(|_| true) { + i -= 1; + assert_eq!(elt, v[i]); + } + assert_eq!(i, 0); + assert_eq!(v.iter().rfind(|&&x| x <= 3), Some(&3)); +} + +#[test] +fn test_iter_folds() { + let a = [1, 2, 3, 4, 5]; // len>4 so the unroll is used + assert_eq!(a.iter().fold(0, |acc, &x| 2 * acc + x), 57); + assert_eq!(a.iter().rfold(0, |acc, &x| 2 * acc + x), 129); + let fold = |acc: i32, &x| acc.checked_mul(2)?.checked_add(x); + assert_eq!(a.iter().try_fold(0, &fold), Some(57)); + assert_eq!(a.iter().try_rfold(0, &fold), Some(129)); + + // short-circuiting try_fold, through other methods + let a = [0, 1, 2, 3, 5, 5, 5, 7, 8, 9]; + let mut iter = a.iter(); + assert_eq!(iter.position(|&x| x == 3), Some(3)); + assert_eq!(iter.rfind(|&&x| x == 5), Some(&5)); + assert_eq!(iter.len(), 2); +} + +#[test] +fn test_rotate_left() { + const N: usize = 600; + let a: &mut [_] = &mut [0; N]; + for i in 0..N { + a[i] = i; + } + + a.rotate_left(42); + let k = N - 42; + + for i in 0..N { + assert_eq!(a[(i + k) % N], i); + } +} + +#[test] +fn test_rotate_right() { + const N: usize = 600; + let a: &mut [_] = &mut [0; N]; + for i in 0..N { + a[i] = i; + } + + a.rotate_right(42); + + for i in 0..N { + assert_eq!(a[(i + 42) % N], i); + } +} + +#[test] +#[cfg_attr(miri, ignore)] // Miri is too slow +fn brute_force_rotate_test_0() { + // In case of edge cases involving multiple algorithms + let n = 300; + for len in 0..n { + for s in 0..len { + let mut v = Vec::with_capacity(len); + for i in 0..len { + v.push(i); + } + v[..].rotate_right(s); + for i in 0..v.len() { + assert_eq!(v[i], v.len().wrapping_add(i.wrapping_sub(s)) % v.len()); + } + } + } +} + +#[test] +fn brute_force_rotate_test_1() { + // `ptr_rotate` covers so many kinds of pointer usage, that this is just a good test for + // pointers in general. This uses a `[usize; 4]` to hit all algorithms without overwhelming miri + let n = 30; + for len in 0..n { + for s in 0..len { + let mut v: Vec<[usize; 4]> = Vec::with_capacity(len); + for i in 0..len { + v.push([i, 0, 0, 0]); + } + v[..].rotate_right(s); + for i in 0..v.len() { + assert_eq!(v[i][0], v.len().wrapping_add(i.wrapping_sub(s)) % v.len()); + } + } + } +} + +#[test] +#[cfg(not(target_arch = "wasm32"))] +#[cfg_attr(miri, ignore)] // Miri is too slow +fn select_nth_unstable() { + use core::cmp::Ordering::{Equal, Greater, Less}; + + use rand::Rng; + + let mut rng = crate::test_rng(); + + for len in (2..21).chain(500..501) { + let mut orig = vec![0; len]; + + for &modulus in &[5, 10, 1000] { + for _ in 0..10 { + for i in 0..len { + orig[i] = rng.random::() % modulus; + } + + let v_sorted = { + let mut v = orig.clone(); + v.sort(); + v + }; + + // Sort in default order. + for pivot in 0..len { + let mut v = orig.clone(); + v.select_nth_unstable(pivot); + + assert_eq!(v_sorted[pivot], v[pivot]); + for i in 0..pivot { + for j in pivot..len { + assert!(v[i] <= v[j]); + } + } + } + + // Sort in ascending order. + for pivot in 0..len { + let mut v = orig.clone(); + let (left, pivot, right) = v.select_nth_unstable_by(pivot, |a, b| a.cmp(b)); + + assert_eq!(left.len() + right.len(), len - 1); + + for l in left { + assert!(l <= pivot); + for r in right.iter_mut() { + assert!(l <= r); + assert!(pivot <= r); + } + } + } + + // Sort in descending order. + let sort_descending_comparator = |a: &i32, b: &i32| b.cmp(a); + let v_sorted_descending = { + let mut v = orig.clone(); + v.sort_by(sort_descending_comparator); + v + }; + + for pivot in 0..len { + let mut v = orig.clone(); + v.select_nth_unstable_by(pivot, sort_descending_comparator); + + assert_eq!(v_sorted_descending[pivot], v[pivot]); + for i in 0..pivot { + for j in pivot..len { + assert!(v[j] <= v[i]); + } + } + } + } + } + } + + // Sort at index using a completely random comparison function. + // This will reorder the elements *somehow*, but won't panic. + let mut v = [0; 500]; + for i in 0..v.len() { + v[i] = i as i32; + } + + for pivot in 0..v.len() { + v.select_nth_unstable_by(pivot, |_, _| *[Less, Equal, Greater].choose(&mut rng).unwrap()); + v.sort(); + for i in 0..v.len() { + assert_eq!(v[i], i as i32); + } + } + + // Should not panic. + [(); 10].select_nth_unstable(0); + [(); 10].select_nth_unstable(5); + [(); 10].select_nth_unstable(9); + [(); 100].select_nth_unstable(0); + [(); 100].select_nth_unstable(50); + [(); 100].select_nth_unstable(99); + + let mut v = [0xDEADBEEFu64]; + v.select_nth_unstable(0); + assert!(v == [0xDEADBEEF]); +} + +#[test] +#[should_panic(expected = "index 0 greater than length of slice")] +fn select_nth_unstable_zero_length() { + [0i32; 0].select_nth_unstable(0); +} + +#[test] +#[should_panic(expected = "index 20 greater than length of slice")] +fn select_nth_unstable_past_length() { + [0i32; 10].select_nth_unstable(20); +} + +pub mod memchr { + use core::slice::memchr::{memchr, memrchr}; + + // test fallback implementations on all platforms + #[test] + fn matches_one() { + assert_eq!(Some(0), memchr(b'a', b"a")); + } + + #[test] + fn matches_begin() { + assert_eq!(Some(0), memchr(b'a', b"aaaa")); + } + + #[test] + fn matches_end() { + assert_eq!(Some(4), memchr(b'z', b"aaaaz")); + } + + #[test] + fn matches_nul() { + assert_eq!(Some(4), memchr(b'\x00', b"aaaa\x00")); + } + + #[test] + fn matches_past_nul() { + assert_eq!(Some(5), memchr(b'z', b"aaaa\x00z")); + } + + #[test] + fn no_match_empty() { + assert_eq!(None, memchr(b'a', b"")); + } + + #[test] + fn no_match() { + assert_eq!(None, memchr(b'a', b"xyz")); + } + + #[test] + fn matches_one_reversed() { + assert_eq!(Some(0), memrchr(b'a', b"a")); + } + + #[test] + fn matches_begin_reversed() { + assert_eq!(Some(3), memrchr(b'a', b"aaaa")); + } + + #[test] + fn matches_end_reversed() { + assert_eq!(Some(0), memrchr(b'z', b"zaaaa")); + } + + #[test] + fn matches_nul_reversed() { + assert_eq!(Some(4), memrchr(b'\x00', b"aaaa\x00")); + } + + #[test] + fn matches_past_nul_reversed() { + assert_eq!(Some(0), memrchr(b'z', b"z\x00aaaa")); + } + + #[test] + fn no_match_empty_reversed() { + assert_eq!(None, memrchr(b'a', b"")); + } + + #[test] + fn no_match_reversed() { + assert_eq!(None, memrchr(b'a', b"xyz")); + } + + #[test] + fn each_alignment_reversed() { + let mut data = [1u8; 64]; + let needle = 2; + let pos = 40; + data[pos] = needle; + for start in 0..16 { + assert_eq!(Some(pos - start), memrchr(needle, &data[start..])); + } + } +} + +#[test] +fn test_align_to_simple() { + let bytes = [1u8, 2, 3, 4, 5, 6, 7]; + let (prefix, aligned, suffix) = unsafe { bytes.align_to::() }; + assert_eq!(aligned.len(), 3); + assert!(prefix == [1] || suffix == [7]); + let expect1 = [1 << 8 | 2, 3 << 8 | 4, 5 << 8 | 6]; + let expect2 = [1 | 2 << 8, 3 | 4 << 8, 5 | 6 << 8]; + let expect3 = [2 << 8 | 3, 4 << 8 | 5, 6 << 8 | 7]; + let expect4 = [2 | 3 << 8, 4 | 5 << 8, 6 | 7 << 8]; + assert!( + aligned == expect1 || aligned == expect2 || aligned == expect3 || aligned == expect4, + "aligned={:?} expected={:?} || {:?} || {:?} || {:?}", + aligned, + expect1, + expect2, + expect3, + expect4 + ); +} + +#[test] +fn test_align_to_zst() { + let bytes = [1, 2, 3, 4, 5, 6, 7]; + let (prefix, aligned, suffix) = unsafe { bytes.align_to::<()>() }; + assert_eq!(aligned.len(), 0); + assert!(prefix == [1, 2, 3, 4, 5, 6, 7] || suffix == [1, 2, 3, 4, 5, 6, 7]); +} + +#[test] +fn test_align_to_non_trivial() { + #[repr(align(8))] + struct U64(#[allow(dead_code)] u64, #[allow(dead_code)] u64); + #[repr(align(8))] + struct U64U64U32(#[allow(dead_code)] u64, #[allow(dead_code)] u64, #[allow(dead_code)] u32); + let data = [ + U64(1, 2), + U64(3, 4), + U64(5, 6), + U64(7, 8), + U64(9, 10), + U64(11, 12), + U64(13, 14), + U64(15, 16), + ]; + let (prefix, aligned, suffix) = unsafe { data.align_to::() }; + assert_eq!(aligned.len(), 4); + assert_eq!(prefix.len() + suffix.len(), 2); +} + +#[test] +fn test_align_to_empty_mid() { + use core::mem; + + // Make sure that we do not create empty unaligned slices for the mid part, even when the + // overall slice is too short to contain an aligned address. + let bytes = [1, 2, 3, 4, 5, 6, 7]; + type Chunk = u32; + for offset in 0..4 { + let (_, mid, _) = unsafe { bytes[offset..offset + 1].align_to::() }; + assert_eq!(mid.as_ptr() as usize % mem::align_of::(), 0); + } +} + +#[test] +fn test_align_to_mut_aliasing() { + let mut val = [1u8, 2, 3, 4, 5]; + // `align_to_mut` used to create `mid` in a way that there was some intermediate + // incorrect aliasing, invalidating the resulting `mid` slice. + let (begin, mid, end) = unsafe { val.align_to_mut::<[u8; 2]>() }; + assert!(begin.len() == 0); + assert!(end.len() == 1); + mid[0] = mid[1]; + assert_eq!(val, [3, 4, 3, 4, 5]) +} + +#[test] +fn test_slice_partition_dedup_by() { + let mut slice: [i32; 9] = [1, -1, 2, 3, 1, -5, 5, -2, 2]; + + let (dedup, duplicates) = slice.partition_dedup_by(|a, b| a.abs() == b.abs()); + + assert_eq!(dedup, [1, 2, 3, 1, -5, -2]); + assert_eq!(duplicates, [5, -1, 2]); +} + +#[test] +fn test_slice_partition_dedup_empty() { + let mut slice: [i32; 0] = []; + + let (dedup, duplicates) = slice.partition_dedup(); + + assert_eq!(dedup, []); + assert_eq!(duplicates, []); +} + +#[test] +fn test_slice_partition_dedup_one() { + let mut slice = [12]; + + let (dedup, duplicates) = slice.partition_dedup(); + + assert_eq!(dedup, [12]); + assert_eq!(duplicates, []); +} + +#[test] +fn test_slice_partition_dedup_multiple_ident() { + let mut slice = [12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11]; + + let (dedup, duplicates) = slice.partition_dedup(); + + assert_eq!(dedup, [12, 11]); + assert_eq!(duplicates, [12, 12, 12, 12, 11, 11, 11, 11, 11]); +} + +#[test] +fn test_slice_partition_dedup_partialeq() { + #[derive(Debug)] + struct Foo(i32, #[allow(dead_code)] i32); + + impl PartialEq for Foo { + fn eq(&self, other: &Foo) -> bool { + self.0 == other.0 + } + } + + let mut slice = [Foo(0, 1), Foo(0, 5), Foo(1, 7), Foo(1, 9)]; + + let (dedup, duplicates) = slice.partition_dedup(); + + assert_eq!(dedup, [Foo(0, 1), Foo(1, 7)]); + assert_eq!(duplicates, [Foo(0, 5), Foo(1, 9)]); +} + +#[test] +fn test_copy_within() { + // Start to end, with a RangeTo. + let mut bytes = *b"Hello, World!"; + bytes.copy_within(..3, 10); + assert_eq!(&bytes, b"Hello, WorHel"); + + // End to start, with a RangeFrom. + let mut bytes = *b"Hello, World!"; + bytes.copy_within(10.., 0); + assert_eq!(&bytes, b"ld!lo, World!"); + + // Overlapping, with a RangeInclusive. + let mut bytes = *b"Hello, World!"; + bytes.copy_within(0..=11, 1); + assert_eq!(&bytes, b"HHello, World"); + + // Whole slice, with a RangeFull. + let mut bytes = *b"Hello, World!"; + bytes.copy_within(.., 0); + assert_eq!(&bytes, b"Hello, World!"); + + // Ensure that copying at the end of slice won't cause UB. + let mut bytes = *b"Hello, World!"; + bytes.copy_within(13..13, 5); + assert_eq!(&bytes, b"Hello, World!"); + bytes.copy_within(5..5, 13); + assert_eq!(&bytes, b"Hello, World!"); +} + +#[test] +#[should_panic(expected = "range end index 14 out of range for slice of length 13")] +fn test_copy_within_panics_src_too_long() { + let mut bytes = *b"Hello, World!"; + // The length is only 13, so 14 is out of bounds. + bytes.copy_within(10..14, 0); +} + +#[test] +#[should_panic(expected = "dest is out of bounds")] +fn test_copy_within_panics_dest_too_long() { + let mut bytes = *b"Hello, World!"; + // The length is only 13, so a slice of length 4 starting at index 10 is out of bounds. + bytes.copy_within(0..4, 10); +} + +#[test] +#[should_panic(expected = "slice index starts at 2 but ends at 1")] +fn test_copy_within_panics_src_inverted() { + let mut bytes = *b"Hello, World!"; + // 2 is greater than 1, so this range is invalid. + bytes.copy_within(2..1, 0); +} +#[test] +#[should_panic(expected = "attempted to index slice up to maximum usize")] +fn test_copy_within_panics_src_out_of_bounds() { + let mut bytes = *b"Hello, World!"; + // an inclusive range ending at usize::MAX would make src_end overflow + bytes.copy_within(usize::MAX..=usize::MAX, 0); +} + +#[test] +fn test_is_sorted() { + let empty: [i32; 0] = []; + + // Tests on integers + assert!([1, 2, 2, 9].is_sorted()); + assert!(![1, 3, 2].is_sorted()); + assert!([0].is_sorted()); + assert!([0, 0].is_sorted()); + assert!(empty.is_sorted()); + + // Tests on floats + assert!([1.0f32, 2.0, 2.0, 9.0].is_sorted()); + assert!(![1.0f32, 3.0f32, 2.0f32].is_sorted()); + assert!([0.0f32].is_sorted()); + assert!([0.0f32, 0.0f32].is_sorted()); + // Test cases with NaNs + assert!([f32::NAN].is_sorted()); + assert!(![f32::NAN, f32::NAN].is_sorted()); + assert!(![0.0, 1.0, f32::NAN].is_sorted()); + // Tests from + assert!(![f32::NAN, f32::NAN, f32::NAN].is_sorted()); + assert!(![1.0, f32::NAN, 2.0].is_sorted()); + assert!(![2.0, f32::NAN, 1.0].is_sorted()); + assert!(![2.0, f32::NAN, 1.0, 7.0].is_sorted()); + assert!(![2.0, f32::NAN, 1.0, 0.0].is_sorted()); + assert!(![-f32::NAN, -1.0, 0.0, 1.0, f32::NAN].is_sorted()); + assert!(![f32::NAN, -f32::NAN, -1.0, 0.0, 1.0].is_sorted()); + assert!(![1.0, f32::NAN, -f32::NAN, -1.0, 0.0].is_sorted()); + assert!(![0.0, 1.0, f32::NAN, -f32::NAN, -1.0].is_sorted()); + assert!(![-1.0, 0.0, 1.0, f32::NAN, -f32::NAN].is_sorted()); + + // Tests for is_sorted_by + assert!(![6, 2, 8, 5, 1, -60, 1337].is_sorted()); + assert!([6, 2, 8, 5, 1, -60, 1337].is_sorted_by(|_, _| true)); + + // Tests for is_sorted_by_key + assert!([-2, -1, 0, 3].is_sorted()); + assert!(![-2i32, -1, 0, 3].is_sorted_by_key(|n| n.abs())); + assert!(!["c", "bb", "aaa"].is_sorted()); + assert!(["c", "bb", "aaa"].is_sorted_by_key(|s| s.len())); +} + +#[test] +fn test_slice_run_destructors() { + // Make sure that destructors get run on slice literals + struct Foo<'a> { + x: &'a Cell, + } + + impl<'a> Drop for Foo<'a> { + fn drop(&mut self) { + self.x.set(self.x.get() + 1); + } + } + + fn foo(x: &Cell) -> Foo<'_> { + Foo { x } + } + + let x = &Cell::new(0); + + { + let l = &[foo(x)]; + assert_eq!(l[0].x.get(), 0); + } + + assert_eq!(x.get(), 1); +} + +#[test] +fn test_const_from_ref() { + const VALUE: &i32 = &1; + const SLICE: &[i32] = core::slice::from_ref(VALUE); + + assert!(core::ptr::eq(VALUE, &SLICE[0])) +} + +#[test] +fn test_slice_fill_with_uninit() { + // This should not UB. See #87891 + let mut a = [MaybeUninit::::uninit(); 10]; + a.fill(MaybeUninit::uninit()); +} + +#[test] +fn test_swap() { + let mut x = ["a", "b", "c", "d"]; + x.swap(1, 3); + assert_eq!(x, ["a", "d", "c", "b"]); + x.swap(0, 3); + assert_eq!(x, ["b", "d", "c", "a"]); +} + +mod swap_panics { + #[test] + #[should_panic(expected = "index out of bounds: the len is 4 but the index is 4")] + fn index_a_equals_len() { + let mut x = ["a", "b", "c", "d"]; + x.swap(4, 2); + } + + #[test] + #[should_panic(expected = "index out of bounds: the len is 4 but the index is 4")] + fn index_b_equals_len() { + let mut x = ["a", "b", "c", "d"]; + x.swap(2, 4); + } + + #[test] + #[should_panic(expected = "index out of bounds: the len is 4 but the index is 5")] + fn index_a_greater_than_len() { + let mut x = ["a", "b", "c", "d"]; + x.swap(5, 2); + } + + #[test] + #[should_panic(expected = "index out of bounds: the len is 4 but the index is 5")] + fn index_b_greater_than_len() { + let mut x = ["a", "b", "c", "d"]; + x.swap(2, 5); + } +} + +#[test] +fn slice_split_first_chunk_mut() { + let v = &mut [1, 2, 3, 4, 5, 6][..]; + + { + let (left, right) = v.split_first_chunk_mut::<0>().unwrap(); + assert_eq!(left, &mut []); + assert_eq!(right, [1, 2, 3, 4, 5, 6]); + } + + { + let (left, right) = v.split_first_chunk_mut::<6>().unwrap(); + assert_eq!(left, &mut [1, 2, 3, 4, 5, 6]); + assert_eq!(right, []); + } + + { + assert!(v.split_first_chunk_mut::<7>().is_none()); + } +} + +#[test] +fn slice_split_last_chunk_mut() { + let v = &mut [1, 2, 3, 4, 5, 6][..]; + + { + let (left, right) = v.split_last_chunk_mut::<0>().unwrap(); + assert_eq!(left, [1, 2, 3, 4, 5, 6]); + assert_eq!(right, &mut []); + } + + { + let (left, right) = v.split_last_chunk_mut::<6>().unwrap(); + assert_eq!(left, []); + assert_eq!(right, &mut [1, 2, 3, 4, 5, 6]); + } + + { + assert!(v.split_last_chunk_mut::<7>().is_none()); + } +} + +#[test] +fn split_as_slice() { + let arr = [1, 2, 3, 4, 5, 6]; + let mut split = arr.split(|v| v % 2 == 0); + assert_eq!(split.as_slice(), &[1, 2, 3, 4, 5, 6]); + assert!(split.next().is_some()); + assert_eq!(split.as_slice(), &[3, 4, 5, 6]); + assert!(split.next().is_some()); + assert!(split.next().is_some()); + assert_eq!(split.as_slice(), &[]); +} + +#[test] +fn slice_split_once() { + let v = &[1, 2, 3, 2, 4][..]; + + assert_eq!(v.split_once(|&x| x == 2), Some((&[1][..], &[3, 2, 4][..]))); + assert_eq!(v.split_once(|&x| x == 1), Some((&[][..], &[2, 3, 2, 4][..]))); + assert_eq!(v.split_once(|&x| x == 4), Some((&[1, 2, 3, 2][..], &[][..]))); + assert_eq!(v.split_once(|&x| x == 0), None); +} + +#[test] +fn slice_rsplit_once() { + let v = &[1, 2, 3, 2, 4][..]; + + assert_eq!(v.rsplit_once(|&x| x == 2), Some((&[1, 2, 3][..], &[4][..]))); + assert_eq!(v.rsplit_once(|&x| x == 1), Some((&[][..], &[2, 3, 2, 4][..]))); + assert_eq!(v.rsplit_once(|&x| x == 4), Some((&[1, 2, 3, 2][..], &[][..]))); + assert_eq!(v.rsplit_once(|&x| x == 0), None); +} + +macro_rules! split_off_tests { + (slice: &[], $($tts:tt)*) => { + split_off_tests!(ty: &[()], slice: &[], $($tts)*); + }; + (slice: &mut [], $($tts:tt)*) => { + split_off_tests!(ty: &mut [()], slice: &mut [], $($tts)*); + }; + (slice: &$slice:expr, $($tts:tt)*) => { + split_off_tests!(ty: &[_], slice: &$slice, $($tts)*); + }; + (slice: &mut $slice:expr, $($tts:tt)*) => { + split_off_tests!(ty: &mut [_], slice: &mut $slice, $($tts)*); + }; + (ty: $ty:ty, slice: $slice:expr, method: $method:ident, $(($test_name:ident, ($($args:expr),*), $output:expr, $remaining:expr),)*) => { + $( + #[test] + fn $test_name() { + let mut slice: $ty = $slice; + assert_eq!($output, slice.$method($($args)*)); + let remaining: $ty = $remaining; + assert_eq!(remaining, slice); + } + )* + }; +} + +split_off_tests! { + slice: &[0, 1, 2, 3], method: split_off, + (split_off_in_bounds_range_to, (..1), Some(&[0] as _), &[1, 2, 3]), + (split_off_in_bounds_range_to_inclusive, (..=0), Some(&[0] as _), &[1, 2, 3]), + (split_off_in_bounds_range_from, (2..), Some(&[2, 3] as _), &[0, 1]), + (split_off_oob_range_to, (..5), None, &[0, 1, 2, 3]), + (split_off_oob_range_to_inclusive, (..=4), None, &[0, 1, 2, 3]), + (split_off_oob_range_from, (5..), None, &[0, 1, 2, 3]), +} + +split_off_tests! { + slice: &mut [0, 1, 2, 3], method: split_off_mut, + (split_off_mut_in_bounds_range_to, (..1), Some(&mut [0] as _), &mut [1, 2, 3]), + (split_off_mut_in_bounds_range_to_inclusive, (..=0), Some(&mut [0] as _), &mut [1, 2, 3]), + (split_off_mut_in_bounds_range_from, (2..), Some(&mut [2, 3] as _), &mut [0, 1]), + (split_off_mut_oob_range_to, (..5), None, &mut [0, 1, 2, 3]), + (split_off_mut_oob_range_to_inclusive, (..=4), None, &mut [0, 1, 2, 3]), + (split_off_mut_oob_range_from, (5..), None, &mut [0, 1, 2, 3]), +} + +split_off_tests! { + slice: &[1, 2], method: split_off_first, + (split_off_first_nonempty, (), Some(&1), &[2]), +} + +split_off_tests! { + slice: &mut [1, 2], method: split_off_first_mut, + (split_off_first_mut_nonempty, (), Some(&mut 1), &mut [2]), +} + +split_off_tests! { + slice: &[1, 2], method: split_off_last, + (split_off_last_nonempty, (), Some(&2), &[1]), +} + +split_off_tests! { + slice: &mut [1, 2], method: split_off_last_mut, + (split_off_last_mut_nonempty, (), Some(&mut 2), &mut [1]), +} + +split_off_tests! { + slice: &[], method: split_off_first, + (split_off_first_empty, (), None, &[]), +} + +split_off_tests! { + slice: &mut [], method: split_off_first_mut, + (split_off_first_mut_empty, (), None, &mut []), +} + +split_off_tests! { + slice: &[], method: split_off_last, + (split_off_last_empty, (), None, &[]), +} + +split_off_tests! { + slice: &mut [], method: split_off_last_mut, + (split_off_last_mut_empty, (), None, &mut []), +} + +#[cfg(not(miri))] // unused in Miri +const EMPTY_MAX: &'static [()] = &[(); usize::MAX]; + +// can't be a constant due to const mutability rules +#[cfg(not(miri))] // unused in Miri +macro_rules! empty_max_mut { + () => { + &mut [(); usize::MAX] as _ + }; +} + +#[cfg(not(miri))] // Comparing usize::MAX many elements takes forever in Miri (and in rustc without optimizations) +split_off_tests! { + slice: &[(); usize::MAX], method: split_off, + (split_off_in_bounds_max_range_to, (..usize::MAX), Some(EMPTY_MAX), &[(); 0]), + (split_off_oob_max_range_to_inclusive, (..=usize::MAX), None, EMPTY_MAX), + (split_off_in_bounds_max_range_from, (usize::MAX..), Some(&[] as _), EMPTY_MAX), +} + +#[cfg(not(miri))] // Comparing usize::MAX many elements takes forever in Miri (and in rustc without optimizations) +split_off_tests! { + slice: &mut [(); usize::MAX], method: split_off_mut, + (split_off_mut_in_bounds_max_range_to, (..usize::MAX), Some(empty_max_mut!()), &mut [(); 0]), + (split_off_mut_oob_max_range_to_inclusive, (..=usize::MAX), None, empty_max_mut!()), + (split_off_mut_in_bounds_max_range_from, (usize::MAX..), Some(&mut [] as _), empty_max_mut!()), +} + +#[test] +fn test_slice_from_ptr_range() { + let arr = ["foo".to_owned(), "bar".to_owned()]; + let range = arr.as_ptr_range(); + unsafe { + assert_eq!(slice::from_ptr_range(range), &arr); + } + + let mut arr = [1, 2, 3]; + let range = arr.as_mut_ptr_range(); + unsafe { + assert_eq!(slice::from_mut_ptr_range(range), &mut [1, 2, 3]); + } + + let arr: [Vec; 0] = []; + let range = arr.as_ptr_range(); + unsafe { + assert_eq!(slice::from_ptr_range(range), &arr); + } +} + +#[test] +#[should_panic = "slice len overflow"] +fn test_flatten_size_overflow() { + let x = &[[(); usize::MAX]; 2][..]; + let _ = x.as_flattened(); +} + +#[test] +#[should_panic = "slice len overflow"] +fn test_flatten_mut_size_overflow() { + let x = &mut [[(); usize::MAX]; 2][..]; + let _ = x.as_flattened_mut(); +} + +#[test] +fn test_get_disjoint_mut_normal_2() { + let mut v = vec![1, 2, 3, 4, 5]; + let [a, b] = v.get_disjoint_mut([3, 0]).unwrap(); + *a += 10; + *b += 100; + assert_eq!(v, vec![101, 2, 3, 14, 5]); + + let [a, b] = v.get_disjoint_mut([0..=1, 2..=2]).unwrap(); + assert_eq!(a, &mut [101, 2][..]); + assert_eq!(b, &mut [3][..]); + a[0] += 10; + a[1] += 20; + b[0] += 100; + assert_eq!(v, vec![111, 22, 103, 14, 5]); +} + +#[test] +fn test_get_disjoint_mut_normal_3() { + let mut v = vec![1, 2, 3, 4, 5]; + let [a, b, c] = v.get_disjoint_mut([0, 4, 2]).unwrap(); + *a += 10; + *b += 100; + *c += 1000; + assert_eq!(v, vec![11, 2, 1003, 4, 105]); + + let [a, b, c] = v.get_disjoint_mut([0..1, 4..5, 1..4]).unwrap(); + assert_eq!(a, &mut [11][..]); + assert_eq!(b, &mut [105][..]); + assert_eq!(c, &mut [2, 1003, 4][..]); + a[0] += 10; + b[0] += 100; + c[0] += 1000; + assert_eq!(v, vec![21, 1002, 1003, 4, 205]); +} + +#[test] +fn test_get_disjoint_mut_empty() { + let mut v = vec![1, 2, 3, 4, 5]; + let [] = v.get_disjoint_mut::([]).unwrap(); + let [] = v.get_disjoint_mut::, 0>([]).unwrap(); + let [] = v.get_disjoint_mut::, 0>([]).unwrap(); + assert_eq!(v, vec![1, 2, 3, 4, 5]); +} + +#[test] +fn test_get_disjoint_mut_single_first() { + let mut v = vec![1, 2, 3, 4, 5]; + let [a] = v.get_disjoint_mut([0]).unwrap(); + *a += 10; + assert_eq!(v, vec![11, 2, 3, 4, 5]); +} + +#[test] +fn test_get_disjoint_mut_single_last() { + let mut v = vec![1, 2, 3, 4, 5]; + let [a] = v.get_disjoint_mut([4]).unwrap(); + *a += 10; + assert_eq!(v, vec![1, 2, 3, 4, 15]); +} + +#[test] +fn test_get_disjoint_mut_oob_nonempty() { + let mut v = vec![1, 2, 3, 4, 5]; + assert!(v.get_disjoint_mut([5]).is_err()); +} + +#[test] +fn test_get_disjoint_mut_oob_empty() { + let mut v: Vec = vec![]; + assert!(v.get_disjoint_mut([0]).is_err()); +} + +#[test] +fn test_get_disjoint_mut_duplicate() { + let mut v = vec![1, 2, 3, 4, 5]; + assert!(v.get_disjoint_mut([1, 3, 3, 4]).is_err()); +} + +#[test] +fn test_get_disjoint_mut_range_oob() { + let mut v = vec![1, 2, 3, 4, 5]; + assert!(v.get_disjoint_mut([0..6]).is_err()); + assert!(v.get_disjoint_mut([5..6]).is_err()); + assert!(v.get_disjoint_mut([6..6]).is_err()); + assert!(v.get_disjoint_mut([0..=5]).is_err()); + assert!(v.get_disjoint_mut([0..=6]).is_err()); + assert!(v.get_disjoint_mut([5..=5]).is_err()); +} + +#[test] +fn test_get_disjoint_mut_range_overlapping() { + let mut v = vec![1, 2, 3, 4, 5]; + assert!(v.get_disjoint_mut([0..1, 0..2]).is_err()); + assert!(v.get_disjoint_mut([0..1, 1..2, 0..1]).is_err()); + assert!(v.get_disjoint_mut([0..3, 1..1]).is_err()); + assert!(v.get_disjoint_mut([0..3, 1..2]).is_err()); + assert!(v.get_disjoint_mut([0..=0, 2..=2, 0..=1]).is_err()); + assert!(v.get_disjoint_mut([0..=4, 0..=0]).is_err()); + assert!(v.get_disjoint_mut([4..=4, 0..=0, 3..=4]).is_err()); +} + +#[test] +fn test_get_disjoint_mut_range_empty_at_edge() { + let mut v = vec![1, 2, 3, 4, 5]; + assert_eq!( + v.get_disjoint_mut([0..0, 0..5, 5..5]), + Ok([&mut [][..], &mut [1, 2, 3, 4, 5], &mut []]), + ); + assert_eq!( + v.get_disjoint_mut([0..0, 0..1, 1..1, 1..2, 2..2, 2..3, 3..3, 3..4, 4..4, 4..5, 5..5]), + Ok([ + &mut [][..], + &mut [1], + &mut [], + &mut [2], + &mut [], + &mut [3], + &mut [], + &mut [4], + &mut [], + &mut [5], + &mut [], + ]), + ); +} + +#[test] +fn test_slice_from_raw_parts_in_const() { + static FANCY: i32 = 4; + static FANCY_SLICE: &[i32] = unsafe { std::slice::from_raw_parts(&FANCY, 1) }; + assert_eq!(FANCY_SLICE.as_ptr(), std::ptr::addr_of!(FANCY)); + assert_eq!(FANCY_SLICE.len(), 1); + + const EMPTY_SLICE: &[i32] = + unsafe { std::slice::from_raw_parts(std::ptr::without_provenance(123456), 0) }; + assert_eq!(EMPTY_SLICE.as_ptr().addr(), 123456); + assert_eq!(EMPTY_SLICE.len(), 0); +} diff --git a/library/core/tests/str.rs b/library/coretests/tests/str.rs similarity index 100% rename from library/core/tests/str.rs rename to library/coretests/tests/str.rs diff --git a/library/core/tests/str_lossy.rs b/library/coretests/tests/str_lossy.rs similarity index 100% rename from library/core/tests/str_lossy.rs rename to library/coretests/tests/str_lossy.rs diff --git a/library/core/tests/task.rs b/library/coretests/tests/task.rs similarity index 100% rename from library/core/tests/task.rs rename to library/coretests/tests/task.rs diff --git a/library/core/tests/time.rs b/library/coretests/tests/time.rs similarity index 100% rename from library/core/tests/time.rs rename to library/coretests/tests/time.rs diff --git a/library/core/tests/tuple.rs b/library/coretests/tests/tuple.rs similarity index 100% rename from library/core/tests/tuple.rs rename to library/coretests/tests/tuple.rs diff --git a/library/core/tests/unicode.rs b/library/coretests/tests/unicode.rs similarity index 100% rename from library/core/tests/unicode.rs rename to library/coretests/tests/unicode.rs diff --git a/library/core/tests/waker.rs b/library/coretests/tests/waker.rs similarity index 100% rename from library/core/tests/waker.rs rename to library/coretests/tests/waker.rs diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs index dc2b42bb90ae8..7718d68aef8e5 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -54,7 +54,7 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn PanicPayload) -> u32 { ))] { unsafe fn abort() -> ! { // call std::sys::abort_internal - extern "C" { + unsafe extern "C" { pub fn __rust_abort() -> !; } __rust_abort(); @@ -87,7 +87,7 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn PanicPayload) -> u32 { } } else if #[cfg(target_os = "teeos")] { mod teeos { - extern "C" { + unsafe extern "C" { pub fn TEE_Panic(code: u32) -> !; } } diff --git a/library/panic_abort/src/zkvm.rs b/library/panic_abort/src/zkvm.rs index a6a02abf10976..11150eafd0b80 100644 --- a/library/panic_abort/src/zkvm.rs +++ b/library/panic_abort/src/zkvm.rs @@ -16,7 +16,7 @@ pub(crate) unsafe fn zkvm_set_abort_message(payload: &mut dyn PanicPayload) { return; } - extern "C" { + unsafe extern "C" { fn sys_panic(msg_ptr: *const u8, len: usize) -> !; } diff --git a/library/panic_unwind/Cargo.toml b/library/panic_unwind/Cargo.toml index 252f118fecfbb..c2abb79192e9f 100644 --- a/library/panic_unwind/Cargo.toml +++ b/library/panic_unwind/Cargo.toml @@ -23,4 +23,4 @@ libc = { version = "0.2", default-features = false } [lints.rust.unexpected_cfgs] level = "warn" -check-cfg = [] +check-cfg = ['cfg(emscripten_wasm_eh)'] diff --git a/library/panic_unwind/src/dummy.rs b/library/panic_unwind/src/dummy.rs index a4bcd216c60f0..a0d6876691833 100644 --- a/library/panic_unwind/src/dummy.rs +++ b/library/panic_unwind/src/dummy.rs @@ -6,10 +6,10 @@ use alloc::boxed::Box; use core::any::Any; use core::intrinsics; -pub unsafe fn cleanup(_ptr: *mut u8) -> Box { +pub(crate) unsafe fn cleanup(_ptr: *mut u8) -> Box { intrinsics::abort() } -pub unsafe fn panic(_data: Box) -> u32 { +pub(crate) unsafe fn panic(_data: Box) -> u32 { intrinsics::abort() } diff --git a/library/panic_unwind/src/emcc.rs b/library/panic_unwind/src/emcc.rs index b986fc1c2a829..4140b004ad13e 100644 --- a/library/panic_unwind/src/emcc.rs +++ b/library/panic_unwind/src/emcc.rs @@ -21,7 +21,7 @@ struct TypeInfo { } unsafe impl Sync for TypeInfo {} -extern "C" { +unsafe extern "C" { // The leading `\x01` byte here is actually a magical signal to LLVM to // *not* apply any other mangling like prefixing with a `_` character. // @@ -64,7 +64,7 @@ struct Exception { data: Option>, } -pub unsafe fn cleanup(ptr: *mut u8) -> Box { +pub(crate) unsafe fn cleanup(ptr: *mut u8) -> Box { // intrinsics::try actually gives us a pointer to this structure. #[repr(C)] struct CatchData { @@ -93,16 +93,19 @@ pub unsafe fn cleanup(ptr: *mut u8) -> Box { out } -pub unsafe fn panic(data: Box) -> u32 { +pub(crate) unsafe fn panic(data: Box) -> u32 { let exception = __cxa_allocate_exception(mem::size_of::()) as *mut Exception; if exception.is_null() { return uw::_URC_FATAL_PHASE1_ERROR as u32; } - ptr::write(exception, Exception { - canary: &EXCEPTION_TYPE_INFO, - caught: AtomicBool::new(false), - data: Some(data), - }); + ptr::write( + exception, + Exception { + canary: &EXCEPTION_TYPE_INFO, + caught: AtomicBool::new(false), + data: Some(data), + }, + ); __cxa_throw(exception as *mut _, &EXCEPTION_TYPE_INFO, exception_cleanup); } @@ -116,7 +119,7 @@ extern "C" fn exception_cleanup(ptr: *mut libc::c_void) -> *mut libc::c_void { } } -extern "C" { +unsafe extern "C" { fn __cxa_allocate_exception(thrown_size: libc::size_t) -> *mut libc::c_void; fn __cxa_begin_catch(thrown_exception: *mut libc::c_void) -> *mut libc::c_void; fn __cxa_end_catch(); diff --git a/library/panic_unwind/src/gcc.rs b/library/panic_unwind/src/gcc.rs index b2389078afd0f..e478f6c5fc86c 100644 --- a/library/panic_unwind/src/gcc.rs +++ b/library/panic_unwind/src/gcc.rs @@ -58,7 +58,7 @@ struct Exception { cause: Box, } -pub unsafe fn panic(data: Box) -> u32 { +pub(crate) unsafe fn panic(data: Box) -> u32 { let exception = Box::new(Exception { _uwe: uw::_Unwind_Exception { exception_class: RUST_EXCEPTION_CLASS, @@ -82,7 +82,7 @@ pub unsafe fn panic(data: Box) -> u32 { } } -pub unsafe fn cleanup(ptr: *mut u8) -> Box { +pub(crate) unsafe fn cleanup(ptr: *mut u8) -> Box { let exception = ptr as *mut uw::_Unwind_Exception; if (*exception).exception_class != RUST_EXCEPTION_CLASS { uw::_Unwind_DeleteException(exception); diff --git a/library/panic_unwind/src/hermit.rs b/library/panic_unwind/src/hermit.rs index 69b9edb77c564..9719c13341520 100644 --- a/library/panic_unwind/src/hermit.rs +++ b/library/panic_unwind/src/hermit.rs @@ -5,16 +5,16 @@ use alloc::boxed::Box; use core::any::Any; -pub unsafe fn cleanup(_ptr: *mut u8) -> Box { - extern "C" { - pub fn __rust_abort() -> !; +pub(crate) unsafe fn cleanup(_ptr: *mut u8) -> Box { + unsafe extern "C" { + fn __rust_abort() -> !; } __rust_abort(); } -pub unsafe fn panic(_data: Box) -> u32 { - extern "C" { - pub fn __rust_abort() -> !; +pub(crate) unsafe fn panic(_data: Box) -> u32 { + unsafe extern "C" { + fn __rust_abort() -> !; } __rust_abort(); } diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index 1981675f40922..45e2a466b4df6 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -25,13 +25,15 @@ // `real_imp` is unused with Miri, so silence warnings. #![cfg_attr(miri, allow(dead_code))] #![allow(internal_features)] +#![cfg_attr(not(bootstrap), feature(cfg_emscripten_wasm_eh))] +#![warn(unreachable_pub)] use alloc::boxed::Box; use core::any::Any; use core::panic::PanicPayload; cfg_if::cfg_if! { - if #[cfg(target_os = "emscripten")] { + if #[cfg(all(target_os = "emscripten", not(emscripten_wasm_eh)))] { #[path = "emcc.rs"] mod imp; } else if #[cfg(target_os = "hermit")] { @@ -46,7 +48,7 @@ cfg_if::cfg_if! { target_os = "psp", target_os = "xous", target_os = "solid_asp3", - all(target_family = "unix", not(any(target_os = "espidf", target_os = "rtems", target_os = "nuttx"))), + all(target_family = "unix", not(any(target_os = "espidf", target_os = "nuttx"))), all(target_vendor = "fortanix", target_env = "sgx"), target_family = "wasm", ))] { @@ -73,7 +75,7 @@ cfg_if::cfg_if! { } } -extern "C" { +unsafe extern "C" { /// Handler in std called when a panic object is dropped outside of /// `catch_unwind`. fn __rust_drop_panic() -> !; diff --git a/library/panic_unwind/src/miri.rs b/library/panic_unwind/src/miri.rs index 695adadd59b55..ec48b1105ab47 100644 --- a/library/panic_unwind/src/miri.rs +++ b/library/panic_unwind/src/miri.rs @@ -7,19 +7,19 @@ use core::any::Any; // Must be pointer-sized. type Payload = Box>; -extern "Rust" { +unsafe extern "Rust" { /// Miri-provided extern function to begin unwinding. fn miri_start_unwind(payload: *mut u8) -> !; } -pub unsafe fn panic(payload: Box) -> u32 { +pub(crate) unsafe fn panic(payload: Box) -> u32 { // The payload we pass to `miri_start_unwind` will be exactly the argument we get // in `cleanup` below. So we just box it up once, to get something pointer-sized. let payload_box: Payload = Box::new(payload); miri_start_unwind(Box::into_raw(payload_box) as *mut u8) } -pub unsafe fn cleanup(payload_box: *mut u8) -> Box { +pub(crate) unsafe fn cleanup(payload_box: *mut u8) -> Box { // Recover the underlying `Box`. let payload_box: Payload = Box::from_raw(payload_box as *mut _); *payload_box diff --git a/library/panic_unwind/src/seh.rs b/library/panic_unwind/src/seh.rs index 5afa0a1975612..c8dfddf821e68 100644 --- a/library/panic_unwind/src/seh.rs +++ b/library/panic_unwind/src/seh.rs @@ -111,18 +111,18 @@ struct Exception { mod imp { #[repr(transparent)] #[derive(Copy, Clone)] - pub struct ptr_t(*mut u8); + pub(super) struct ptr_t(*mut u8); impl ptr_t { - pub const fn null() -> Self { + pub(super) const fn null() -> Self { Self(core::ptr::null_mut()) } - pub const fn new(ptr: *mut u8) -> Self { + pub(super) const fn new(ptr: *mut u8) -> Self { Self(ptr) } - pub const fn raw(self) -> *mut u8 { + pub(super) const fn raw(self) -> *mut u8 { self.0 } } @@ -133,18 +133,18 @@ mod imp { // On 64-bit systems, SEH represents pointers as 32-bit offsets from `__ImageBase`. #[repr(transparent)] #[derive(Copy, Clone)] - pub struct ptr_t(u32); + pub(super) struct ptr_t(u32); - extern "C" { - pub static __ImageBase: u8; + unsafe extern "C" { + static __ImageBase: u8; } impl ptr_t { - pub const fn null() -> Self { + pub(super) const fn null() -> Self { Self(0) } - pub fn new(ptr: *mut u8) -> Self { + pub(super) fn new(ptr: *mut u8) -> Self { // We need to expose the provenance of the pointer because it is not carried by // the `u32`, while the FFI needs to have this provenance to excess our statics. // @@ -159,7 +159,7 @@ mod imp { Self(offset as u32) } - pub const fn raw(self) -> u32 { + pub(super) const fn raw(self) -> u32 { self.0 } } @@ -168,7 +168,7 @@ mod imp { use imp::ptr_t; #[repr(C)] -pub struct _ThrowInfo { +struct _ThrowInfo { pub attributes: c_uint, pub pmfnUnwind: ptr_t, pub pForwardCompat: ptr_t, @@ -176,13 +176,13 @@ pub struct _ThrowInfo { } #[repr(C)] -pub struct _CatchableTypeArray { +struct _CatchableTypeArray { pub nCatchableTypes: c_int, pub arrayOfCatchableTypes: [ptr_t; 1], } #[repr(C)] -pub struct _CatchableType { +struct _CatchableType { pub properties: c_uint, pub pType: ptr_t, pub thisDisplacement: _PMD, @@ -191,14 +191,14 @@ pub struct _CatchableType { } #[repr(C)] -pub struct _PMD { +struct _PMD { pub mdisp: c_int, pub pdisp: c_int, pub vdisp: c_int, } #[repr(C)] -pub struct _TypeDescriptor { +struct _TypeDescriptor { pub pVFTable: *const u8, pub spare: *mut u8, pub name: [u8; 11], @@ -229,7 +229,7 @@ static mut CATCHABLE_TYPE: _CatchableType = _CatchableType { copyFunction: ptr_t::null(), }; -extern "C" { +unsafe extern "C" { // The leading `\x01` byte here is actually a magical signal to LLVM to // *not* apply any other mangling like prefixing with a `_` character. // @@ -288,7 +288,7 @@ cfg_if::cfg_if! { } } -pub unsafe fn panic(data: Box) -> u32 { +pub(crate) unsafe fn panic(data: Box) -> u32 { use core::intrinsics::atomic_store_seqcst; // _CxxThrowException executes entirely on this stack frame, so there's no @@ -343,14 +343,14 @@ pub unsafe fn panic(data: Box) -> u32 { ptr_t::new(exception_copy as *mut u8).raw(), ); - extern "system-unwind" { + unsafe extern "system-unwind" { fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8) -> !; } _CxxThrowException(throw_ptr, (&raw mut THROW_INFO) as *mut _); } -pub unsafe fn cleanup(payload: *mut u8) -> Box { +pub(crate) unsafe fn cleanup(payload: *mut u8) -> Box { // A null payload here means that we got here from the catch (...) of // __rust_try. This happens when a non-Rust foreign exception is caught. if payload.is_null() { diff --git a/library/portable-simd/.github/workflows/ci.yml b/library/portable-simd/.github/workflows/ci.yml index b292be2d6f999..3984d8f0d8d99 100644 --- a/library/portable-simd/.github/workflows/ci.yml +++ b/library/portable-simd/.github/workflows/ci.yml @@ -9,6 +9,7 @@ on: env: CARGO_NET_RETRY: 10 RUSTUP_MAX_RETRIES: 10 + PROPTEST_CASES: 64 jobs: rustfmt: @@ -16,12 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly - rustup component add rustfmt + - uses: actions/checkout@v4 - name: Run rustfmt run: cargo fmt --all -- --check @@ -37,7 +33,9 @@ jobs: - i686-unknown-linux-gnu - i586-unknown-linux-gnu - aarch64-unknown-linux-gnu + - arm64ec-pc-windows-msvc - armv7-unknown-linux-gnueabihf + - loongarch64-unknown-linux-gnu # non-nightly since https://github.com/rust-lang/rust/pull/113274 # - mips-unknown-linux-gnu # - mips64-unknown-linux-gnuabi64 @@ -49,13 +47,9 @@ jobs: - wasm32-unknown-unknown steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly - rustup target add ${{ matrix.target }} - rustup component add clippy + run: rustup target add ${{ matrix.target }} - name: Run Clippy run: cargo clippy --all-targets --target ${{ matrix.target }} @@ -65,26 +59,19 @@ jobs: strategy: fail-fast: false matrix: - target: [x86_64-pc-windows-msvc, i686-pc-windows-msvc, i586-pc-windows-msvc, x86_64-unknown-linux-gnu, x86_64-apple-darwin] + target: [x86_64-pc-windows-msvc, i686-pc-windows-msvc, i586-pc-windows-msvc, x86_64-unknown-linux-gnu] # `default` means we use the default target config for the target, # `native` means we run with `-Ctarget-cpu=native`, and anything else is # an arg to `-Ctarget-feature` target_feature: [default, native, +sse3, +ssse3, +sse4.1, +sse4.2, +avx, +avx2] exclude: - # The macos runners seem to only reliably support up to `avx`. - - { target: x86_64-apple-darwin, target_feature: +avx2 } - # These features are statically known to be present for all 64 bit - # macs, and thus are covered by the `default` test - - { target: x86_64-apple-darwin, target_feature: +sse3 } - - { target: x86_64-apple-darwin, target_feature: +ssse3 } # -Ctarget-cpu=native sounds like bad-news if target != host - { target: i686-pc-windows-msvc, target_feature: native } - { target: i586-pc-windows-msvc, target_feature: native } include: # Populate the `matrix.os` field - - { target: x86_64-apple-darwin, os: macos-latest } - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest } - { target: x86_64-pc-windows-msvc, os: windows-latest } - { target: i686-pc-windows-msvc, os: windows-latest } @@ -98,12 +85,9 @@ jobs: # avx512vl, but occasionally doesn't. Maybe one day we can enable it. steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly - rustup target add ${{ matrix.target }} + run: rustup target add ${{ matrix.target }} - name: Configure RUSTFLAGS shell: bash @@ -145,6 +129,35 @@ jobs: run: cargo doc --verbose --target=${{ matrix.target }} env: RUSTDOCFLAGS: -Dwarnings + + macos-tests: + name: ${{ matrix.target }} + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + target: + - aarch64-apple-darwin + - x86_64-apple-darwin + steps: + - uses: actions/checkout@v4 + - name: Setup Rust + run: rustup target add ${{ matrix.target }} + + - name: Configure RUSTFLAGS + shell: bash + run: echo "RUSTFLAGS=-Dwarnings" >> $GITHUB_ENV + + - name: Test (debug) + run: cargo test --verbose --target=${{ matrix.target }} + + - name: Test (release) + run: cargo test --verbose --target=${{ matrix.target }} --release + + - name: Generate docs + run: cargo doc --verbose --target=${{ matrix.target }} + env: + RUSTDOCFLAGS: -Dwarnings wasm-tests: name: "wasm (firefox, ${{ matrix.name }})" @@ -155,11 +168,7 @@ jobs: - { name: default, RUSTFLAGS: "" } - { name: simd128, RUSTFLAGS: "-C target-feature=+simd128" } steps: - - uses: actions/checkout@v2 - - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly + - uses: actions/checkout@v4 - name: Install wasm-pack run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh - name: Test (debug) @@ -174,6 +183,8 @@ jobs: cross-tests: name: "${{ matrix.target_feature }} on ${{ matrix.target }} (via cross)" runs-on: ubuntu-latest + env: + PROPTEST_CASES: 16 strategy: fail-fast: false @@ -185,6 +196,7 @@ jobs: - powerpc-unknown-linux-gnu - powerpc64le-unknown-linux-gnu # includes altivec by default - riscv64gc-unknown-linux-gnu + - loongarch64-unknown-linux-gnu # MIPS uses a nonstandard binary representation for NaNs which makes it worth testing # non-nightly since https://github.com/rust-lang/rust/pull/113274 # - mips-unknown-linux-gnu @@ -201,24 +213,14 @@ jobs: # - { target: riscv64gc-unknown-linux-gnu, target_feature: "+v,+zvl128b" } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly - rustup target add ${{ matrix.target }} - rustup component add rust-src + run: rustup target add ${{ matrix.target }} - name: Install Cross - # Equivalent to `cargo install cross`, but downloading a prebuilt - # binary. Ideally we wouldn't hardcode a version, but the version number - # being part of the tarball means we can't just use the download/latest - # URL :( + # Install the latest git version for newer targets. run: | - CROSS_URL=https://github.com/cross-rs/cross/releases/download/v0.2.5/cross-x86_64-unknown-linux-gnu.tar.gz - mkdir -p "$HOME/.bin" - curl -sfSL --retry-delay 10 --retry 5 "${CROSS_URL}" | tar zxf - -C "$HOME/.bin" - echo "$HOME/.bin" >> $GITHUB_PATH + cargo install cross --git https://github.com/cross-rs/cross --rev 4090beca3cfffa44371a5bba524de3a578aa46c3 - name: Configure Emulated CPUs run: | @@ -242,34 +244,11 @@ jobs: - name: Test (release) run: cross test --verbose --target=${{ matrix.target }} --release - features: - name: "Test cargo features (${{ matrix.simd }} × ${{ matrix.features }})" + miri: runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - simd: - - "" - - "avx512" - features: - - "" - - "--features std" - - "--features all_lane_counts" - - "--all-features" - + env: + PROPTEST_CASES: 16 steps: - - uses: actions/checkout@v2 - - name: Setup Rust - run: | - rustup update nightly --no-self-update - rustup default nightly - - name: Detect AVX512 - run: echo "CPU_FEATURE=$(lscpu | grep -o avx512[a-z]* | sed s/avx/+avx/ | tr '\n' ',' )" >> $GITHUB_ENV - - name: Check build - if: ${{ matrix.simd == '' }} - run: RUSTFLAGS="-Dwarnings" cargo test --all-targets --no-default-features ${{ matrix.features }} - - name: Check AVX - if: ${{ matrix.simd == 'avx512' && contains(env.CPU_FEATURE, 'avx512') }} - run: | - echo "Found AVX features: $CPU_FEATURE" - RUSTFLAGS="-Dwarnings -Ctarget-feature=$CPU_FEATURE" cargo test --all-targets --no-default-features ${{ matrix.features }} + - uses: actions/checkout@v4 + - name: Test (Miri) + run: cargo miri test diff --git a/library/portable-simd/.github/workflows/doc.yml b/library/portable-simd/.github/workflows/doc.yml index 9d1fa66ccb595..22c2cb3f67f1b 100644 --- a/library/portable-simd/.github/workflows/doc.yml +++ b/library/portable-simd/.github/workflows/doc.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout Repository - uses: actions/checkout@v1 + uses: actions/checkout@v4 - name: Setup Rust run: | diff --git a/library/portable-simd/.gitignore b/library/portable-simd/.gitignore index ea8c4bf7f35f6..9673e52dcadba 100644 --- a/library/portable-simd/.gitignore +++ b/library/portable-simd/.gitignore @@ -1 +1,2 @@ /target +git-subtree.sh diff --git a/library/portable-simd/Cargo.toml b/library/portable-simd/Cargo.toml index d1732aaec2f92..21d4584a9f4d9 100644 --- a/library/portable-simd/Cargo.toml +++ b/library/portable-simd/Cargo.toml @@ -5,3 +5,9 @@ members = [ "crates/std_float", "crates/test_helpers", ] + +[profile.test.package."*"] +opt-level = 2 + +[profile.test.package.test_helpers] +opt-level = 2 diff --git a/library/portable-simd/Cross.toml b/library/portable-simd/Cross.toml new file mode 100644 index 0000000000000..d21e76b92dd1a --- /dev/null +++ b/library/portable-simd/Cross.toml @@ -0,0 +1,2 @@ +[build.env] +passthrough = ["PROPTEST_CASES"] diff --git a/library/portable-simd/crates/core_simd/Cargo.toml b/library/portable-simd/crates/core_simd/Cargo.toml index b4a8fd70f4c0e..a7a6d43b11d3c 100644 --- a/library/portable-simd/crates/core_simd/Cargo.toml +++ b/library/portable-simd/crates/core_simd/Cargo.toml @@ -9,10 +9,9 @@ categories = ["hardware-support", "no-std"] license = "MIT OR Apache-2.0" [features] -default = ["as_crate"] +default = ["as_crate", "std"] as_crate = [] std = [] -all_lane_counts = [] [target.'cfg(target_arch = "wasm32")'.dev-dependencies] wasm-bindgen = "0.2" diff --git a/library/portable-simd/crates/core_simd/src/lane_count.rs b/library/portable-simd/crates/core_simd/src/lane_count.rs index 4cd7265ed671e..280b27bc9bc6f 100644 --- a/library/portable-simd/crates/core_simd/src/lane_count.rs +++ b/library/portable-simd/crates/core_simd/src/lane_count.rs @@ -33,10 +33,8 @@ macro_rules! supported_lane_count { }; } -supported_lane_count!(1, 2, 4, 8, 16, 32, 64); -#[cfg(feature = "all_lane_counts")] supported_lane_count!( - 3, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, - 31, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63 + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 ); diff --git a/library/portable-simd/crates/core_simd/src/lib.rs b/library/portable-simd/crates/core_simd/src/lib.rs index 992a7705e3c52..7f57847c9c234 100644 --- a/library/portable-simd/crates/core_simd/src/lib.rs +++ b/library/portable-simd/crates/core_simd/src/lib.rs @@ -1,7 +1,6 @@ #![no_std] #![feature( - const_refs_to_cell, - const_mut_refs, + const_eval_select, convert_float_to_int, core_intrinsics, decl_macro, @@ -26,6 +25,7 @@ all(target_arch = "arm", target_feature = "v7"), feature(stdarch_arm_neon_intrinsics) )] +#![cfg_attr(target_arch = "loongarch64", feature(stdarch_loongarch))] #![cfg_attr( any(target_arch = "powerpc", target_arch = "powerpc64"), feature(stdarch_powerpc) diff --git a/library/portable-simd/crates/core_simd/src/masks.rs b/library/portable-simd/crates/core_simd/src/masks.rs index 04de3a968276d..b763a7c75a5a6 100644 --- a/library/portable-simd/crates/core_simd/src/masks.rs +++ b/library/portable-simd/crates/core_simd/src/masks.rs @@ -308,48 +308,6 @@ where Self(mask_impl::Mask::from_bitmask_integer(bitmask)) } - /// Creates a bitmask vector from a mask. - /// - /// Each bit is set if the corresponding element in the mask is `true`. - /// The remaining bits are unset. - /// - /// The bits are packed into the first N bits of the vector: - /// ``` - /// # #![feature(portable_simd)] - /// # #[cfg(feature = "as_crate")] use core_simd::simd; - /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::mask32x8; - /// let mask = mask32x8::from_array([true, false, true, false, false, false, true, false]); - /// assert_eq!(mask.to_bitmask_vector()[0], 0b01000101); - /// ``` - #[inline] - #[must_use = "method returns a new integer and does not mutate the original value"] - pub fn to_bitmask_vector(self) -> Simd { - self.0.to_bitmask_vector() - } - - /// Creates a mask from a bitmask vector. - /// - /// For each bit, if it is set, the corresponding element in the mask is set to `true`. - /// - /// The bits are packed into the first N bits of the vector: - /// ``` - /// # #![feature(portable_simd)] - /// # #[cfg(feature = "as_crate")] use core_simd::simd; - /// # #[cfg(not(feature = "as_crate"))] use core::simd; - /// # use simd::{mask32x8, u8x8}; - /// let bitmask = u8x8::from_array([0b01000101, 0, 0, 0, 0, 0, 0, 0]); - /// assert_eq!( - /// mask32x8::from_bitmask_vector(bitmask), - /// mask32x8::from_array([true, false, true, false, false, false, true, false]), - /// ); - /// ``` - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn from_bitmask_vector(bitmask: Simd) -> Self { - Self(mask_impl::Mask::from_bitmask_vector(bitmask)) - } - /// Finds the index of the first set element. /// /// ``` diff --git a/library/portable-simd/crates/core_simd/src/masks/bitmask.rs b/library/portable-simd/crates/core_simd/src/masks/bitmask.rs index 96c553426ee74..db4312d5bf88a 100644 --- a/library/portable-simd/crates/core_simd/src/masks/bitmask.rs +++ b/library/portable-simd/crates/core_simd/src/masks/bitmask.rs @@ -122,23 +122,6 @@ where unsafe { Self(core::intrinsics::simd::simd_bitmask(value), PhantomData) } } - #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn to_bitmask_vector(self) -> Simd { - let mut bitmask = Simd::splat(0); - bitmask.as_mut_array()[..self.0.as_ref().len()].copy_from_slice(self.0.as_ref()); - bitmask - } - - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn from_bitmask_vector(bitmask: Simd) -> Self { - let mut bytes = as SupportedLaneCount>::BitMask::default(); - let len = bytes.as_ref().len(); - bytes.as_mut().copy_from_slice(&bitmask.as_array()[..len]); - Self(bytes, PhantomData) - } - #[inline] pub fn to_bitmask_integer(self) -> u64 { let mut bitmask = [0u8; 8]; diff --git a/library/portable-simd/crates/core_simd/src/masks/full_masks.rs b/library/portable-simd/crates/core_simd/src/masks/full_masks.rs index 87f031a9f367a..2d01946b5747c 100644 --- a/library/portable-simd/crates/core_simd/src/masks/full_masks.rs +++ b/library/portable-simd/crates/core_simd/src/masks/full_masks.rs @@ -140,62 +140,6 @@ where unsafe { Mask(core::intrinsics::simd::simd_cast(self.0)) } } - #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn to_bitmask_vector(self) -> Simd { - let mut bitmask = Simd::splat(0); - - // Safety: Bytes is the right size array - unsafe { - // Compute the bitmask - let mut bytes: as SupportedLaneCount>::BitMask = - core::intrinsics::simd::simd_bitmask(self.0); - - // LLVM assumes bit order should match endianness - if cfg!(target_endian = "big") { - for x in bytes.as_mut() { - *x = x.reverse_bits() - } - if N % 8 > 0 { - bytes.as_mut()[N / 8] >>= 8 - N % 8; - } - } - - bitmask.as_mut_array()[..bytes.as_ref().len()].copy_from_slice(bytes.as_ref()); - } - - bitmask - } - - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn from_bitmask_vector(bitmask: Simd) -> Self { - let mut bytes = as SupportedLaneCount>::BitMask::default(); - - // Safety: Bytes is the right size array - unsafe { - let len = bytes.as_ref().len(); - bytes.as_mut().copy_from_slice(&bitmask.as_array()[..len]); - - // LLVM assumes bit order should match endianness - if cfg!(target_endian = "big") { - for x in bytes.as_mut() { - *x = x.reverse_bits(); - } - if N % 8 > 0 { - bytes.as_mut()[N / 8] >>= 8 - N % 8; - } - } - - // Compute the regular mask - Self::from_int_unchecked(core::intrinsics::simd::simd_select_bitmask( - bytes, - Self::splat(true).to_int(), - Self::splat(false).to_int(), - )) - } - } - #[inline] unsafe fn to_bitmask_impl(self) -> U where @@ -283,7 +227,7 @@ where } #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] + #[must_use = "method returns a new bool and does not mutate the original value"] pub fn all(self) -> bool { // Safety: use `self` as an integer vector unsafe { core::intrinsics::simd::simd_reduce_all(self.to_int()) } diff --git a/library/portable-simd/crates/core_simd/src/ops.rs b/library/portable-simd/crates/core_simd/src/ops.rs index dd7303a97b197..d3bd14a340278 100644 --- a/library/portable-simd/crates/core_simd/src/ops.rs +++ b/library/portable-simd/crates/core_simd/src/ops.rs @@ -77,7 +77,7 @@ macro_rules! int_divrem_guard { ( $lhs:ident, $rhs:ident, { const PANIC_ZERO: &'static str = $zero:literal; - $simd_call:ident + $simd_call:ident, $op:tt }, $int:ident ) => { if $rhs.simd_eq(Simd::splat(0 as _)).any() { @@ -96,8 +96,23 @@ macro_rules! int_divrem_guard { // Nice base case to make it easy to const-fold away the other branch. $rhs }; - // Safety: $lhs and rhs are vectors - unsafe { core::intrinsics::simd::$simd_call($lhs, rhs) } + + // aarch64 div fails for arbitrary `v % 0`, mod fails when rhs is MIN, for non-powers-of-two + // these operations aren't vectorized on aarch64 anyway + #[cfg(target_arch = "aarch64")] + { + let mut out = Simd::splat(0 as _); + for i in 0..Self::LEN { + out[i] = $lhs[i] $op rhs[i]; + } + out + } + + #[cfg(not(target_arch = "aarch64"))] + { + // Safety: $lhs and rhs are vectors + unsafe { core::intrinsics::simd::$simd_call($lhs, rhs) } + } } }; } @@ -205,14 +220,14 @@ for_base_ops! { impl Div::div { int_divrem_guard { const PANIC_ZERO: &'static str = "attempt to divide by zero"; - simd_div + simd_div, / } } impl Rem::rem { int_divrem_guard { const PANIC_ZERO: &'static str = "attempt to calculate the remainder with a divisor of zero"; - simd_rem + simd_rem, % } } diff --git a/library/portable-simd/crates/core_simd/src/simd/cmp/eq.rs b/library/portable-simd/crates/core_simd/src/simd/cmp/eq.rs index 5b4615ce51d79..93989ce91b89d 100644 --- a/library/portable-simd/crates/core_simd/src/simd/cmp/eq.rs +++ b/library/portable-simd/crates/core_simd/src/simd/cmp/eq.rs @@ -12,7 +12,7 @@ pub trait SimdPartialEq { #[must_use = "method returns a new mask and does not mutate the original value"] fn simd_eq(self, other: Self) -> Self::Mask; - /// Test if each element is equal to the corresponding element in `other`. + /// Test if each element is not equal to the corresponding element in `other`. #[must_use = "method returns a new mask and does not mutate the original value"] fn simd_ne(self, other: Self) -> Self::Mask; } diff --git a/library/portable-simd/crates/core_simd/src/simd/num/float.rs b/library/portable-simd/crates/core_simd/src/simd/num/float.rs index 59e43851ea8da..79954b937b397 100644 --- a/library/portable-simd/crates/core_simd/src/simd/num/float.rs +++ b/library/portable-simd/crates/core_simd/src/simd/num/float.rs @@ -255,6 +255,7 @@ macro_rules! impl_trait { type Bits = Simd<$bits_ty, N>; type Cast = Simd; + #[cfg(not(target_arch = "aarch64"))] #[inline] fn cast(self) -> Self::Cast { @@ -262,6 +263,33 @@ macro_rules! impl_trait { unsafe { core::intrinsics::simd::simd_as(self) } } + // https://github.com/llvm/llvm-project/issues/94694 + #[cfg(target_arch = "aarch64")] + #[inline] + fn cast(self) -> Self::Cast + { + const { assert!(N <= 64) }; + if N <= 2 || N == 4 || N == 8 || N == 16 || N == 32 || N == 64 { + // Safety: supported types are guaranteed by SimdCast + unsafe { core::intrinsics::simd::simd_as(self) } + } else if N < 4 { + let x = self.resize::<4>(Default::default()).cast(); + x.resize::(x[0]) + } else if N < 8 { + let x = self.resize::<8>(Default::default()).cast(); + x.resize::(x[0]) + } else if N < 16 { + let x = self.resize::<16>(Default::default()).cast(); + x.resize::(x[0]) + } else if N < 32 { + let x = self.resize::<32>(Default::default()).cast(); + x.resize::(x[0]) + } else { + let x = self.resize::<64>(Default::default()).cast(); + x.resize::(x[0]) + } + } + #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces unsafe fn to_int_unchecked(self) -> Self::Cast @@ -391,7 +419,7 @@ macro_rules! impl_trait { self.as_array().iter().sum() } else { // Safety: `self` is a float vector - unsafe { core::intrinsics::simd::simd_reduce_add_ordered(self, 0.) } + unsafe { core::intrinsics::simd::simd_reduce_add_ordered(self, -0.) } } } diff --git a/library/portable-simd/crates/core_simd/src/simd/num/int.rs b/library/portable-simd/crates/core_simd/src/simd/num/int.rs index d7598d9ceaf92..3a51235ff954e 100644 --- a/library/portable-simd/crates/core_simd/src/simd/num/int.rs +++ b/library/portable-simd/crates/core_simd/src/simd/num/int.rs @@ -1,6 +1,6 @@ use super::sealed::Sealed; use crate::simd::{ - cmp::SimdPartialOrd, num::SimdUint, LaneCount, Mask, Simd, SimdCast, SimdElement, + cmp::SimdOrd, cmp::SimdPartialOrd, num::SimdUint, LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount, }; @@ -70,11 +70,27 @@ pub trait SimdInt: Copy + Sealed { /// # #[cfg(not(feature = "as_crate"))] use core::simd; /// # use simd::prelude::*; /// use core::i32::{MIN, MAX}; - /// let xs = Simd::from_array([MIN, MIN +1, -5, 0]); + /// let xs = Simd::from_array([MIN, MIN + 1, -5, 0]); /// assert_eq!(xs.abs(), Simd::from_array([MIN, MAX, 5, 0])); /// ``` fn abs(self) -> Self; + /// Lanewise absolute difference. + /// Every element becomes the absolute difference of `self` and `second`. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::prelude::*; + /// use core::i32::{MIN, MAX}; + /// let a = Simd::from_array([MIN, MAX, 100, -100]); + /// let b = Simd::from_array([MAX, MIN, -80, -120]); + /// assert_eq!(a.abs_diff(b), Simd::from_array([u32::MAX, u32::MAX, 180, 20])); + /// ``` + fn abs_diff(self, second: Self) -> Self::Unsigned; + /// Lanewise saturating absolute value, implemented in Rust. /// As abs(), except the MIN value becomes MAX instead of itself. /// @@ -203,6 +219,12 @@ pub trait SimdInt: Copy + Sealed { /// The least significant bit becomes the most significant bit, second least-significant bit becomes second most-significant bit, etc. fn reverse_bits(self) -> Self; + /// Returns the number of ones in the binary representation of each element. + fn count_ones(self) -> Self::Unsigned; + + /// Returns the number of zeros in the binary representation of each element. + fn count_zeros(self) -> Self::Unsigned; + /// Returns the number of leading zeros in the binary representation of each element. fn leading_zeros(self) -> Self::Unsigned; @@ -259,6 +281,13 @@ macro_rules! impl_trait { (self^m) - m } + #[inline] + fn abs_diff(self, second: Self) -> Self::Unsigned { + let max = self.simd_max(second); + let min = self.simd_min(second); + (max - min).cast() + } + #[inline] fn saturating_abs(self) -> Self { // arith shift for -1 or 0 mask based on sign bit, giving 2s complement @@ -344,6 +373,16 @@ macro_rules! impl_trait { unsafe { core::intrinsics::simd::simd_bitreverse(self) } } + #[inline] + fn count_ones(self) -> Self::Unsigned { + self.cast::<$unsigned>().count_ones() + } + + #[inline] + fn count_zeros(self) -> Self::Unsigned { + self.cast::<$unsigned>().count_zeros() + } + #[inline] fn leading_zeros(self) -> Self::Unsigned { self.cast::<$unsigned>().leading_zeros() diff --git a/library/portable-simd/crates/core_simd/src/simd/num/uint.rs b/library/portable-simd/crates/core_simd/src/simd/num/uint.rs index 53dd97f501c63..1ab2d8c7b7316 100644 --- a/library/portable-simd/crates/core_simd/src/simd/num/uint.rs +++ b/library/portable-simd/crates/core_simd/src/simd/num/uint.rs @@ -1,5 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount}; +use crate::simd::{cmp::SimdOrd, LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount}; /// Operations on SIMD vectors of unsigned integers. pub trait SimdUint: Copy + Sealed { @@ -57,6 +57,22 @@ pub trait SimdUint: Copy + Sealed { /// assert_eq!(sat, Simd::splat(0)); fn saturating_sub(self, second: Self) -> Self; + /// Lanewise absolute difference. + /// Every element becomes the absolute difference of `self` and `second`. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::prelude::*; + /// use core::u32::MAX; + /// let a = Simd::from_array([0, MAX, 100, 20]); + /// let b = Simd::from_array([MAX, 0, 80, 200]); + /// assert_eq!(a.abs_diff(b), Simd::from_array([MAX, MAX, 20, 180])); + /// ``` + fn abs_diff(self, second: Self) -> Self; + /// Returns the sum of the elements of the vector, with wrapping addition. fn reduce_sum(self) -> Self::Scalar; @@ -85,6 +101,12 @@ pub trait SimdUint: Copy + Sealed { /// The least significant bit becomes the most significant bit, second least-significant bit becomes second most-significant bit, etc. fn reverse_bits(self) -> Self; + /// Returns the number of ones in the binary representation of each element. + fn count_ones(self) -> Self; + + /// Returns the number of zeros in the binary representation of each element. + fn count_zeros(self) -> Self; + /// Returns the number of leading zeros in the binary representation of each element. fn leading_zeros(self) -> Self; @@ -138,6 +160,13 @@ macro_rules! impl_trait { unsafe { core::intrinsics::simd::simd_saturating_sub(self, second) } } + #[inline] + fn abs_diff(self, second: Self) -> Self { + let max = self.simd_max(second); + let min = self.simd_min(second); + max - min + } + #[inline] fn reduce_sum(self) -> Self::Scalar { // Safety: `self` is an integer vector @@ -192,6 +221,17 @@ macro_rules! impl_trait { unsafe { core::intrinsics::simd::simd_bitreverse(self) } } + #[inline] + fn count_ones(self) -> Self { + // Safety: `self` is an integer vector + unsafe { core::intrinsics::simd::simd_ctpop(self) } + } + + #[inline] + fn count_zeros(self) -> Self { + (!self).count_ones() + } + #[inline] fn leading_zeros(self) -> Self { // Safety: `self` is an integer vector diff --git a/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs b/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs index be635ea640b86..47383809ffbae 100644 --- a/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs +++ b/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -42,6 +42,19 @@ pub trait SimdConstPtr: Copy + Sealed { /// Equivalent to calling [`pointer::addr`] on each element. fn addr(self) -> Self::Usize; + /// Converts an address to a pointer without giving it any provenance. + /// + /// Without provenance, this pointer is not associated with any actual allocation. Such a + /// no-provenance pointer may be used for zero-sized memory accesses (if suitably aligned), but + /// non-zero-sized memory accesses with a no-provenance pointer are UB. No-provenance pointers + /// are little more than a usize address in disguise. + /// + /// This is different from [`Self::with_exposed_provenance`], which creates a pointer that picks up a + /// previously exposed provenance. + /// + /// Equivalent to calling [`core::ptr::without_provenance`] on each element. + fn without_provenance(addr: Self::Usize) -> Self; + /// Creates a new pointer with the given address. /// /// This performs the same operation as a cast, but copies the *address-space* and @@ -118,6 +131,14 @@ where unsafe { core::mem::transmute_copy(&self) } } + #[inline] + fn without_provenance(addr: Self::Usize) -> Self { + // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. + // SAFETY: Integer-to-pointer transmutes are valid (if you are okay with not getting any + // provenance). + unsafe { core::mem::transmute_copy(&addr) } + } + #[inline] fn with_addr(self, addr: Self::Usize) -> Self { // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. diff --git a/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs b/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs index f6823a949e32a..3f20eef21a312 100644 --- a/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs +++ b/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -39,6 +39,19 @@ pub trait SimdMutPtr: Copy + Sealed { /// Equivalent to calling [`pointer::addr`] on each element. fn addr(self) -> Self::Usize; + /// Converts an address to a pointer without giving it any provenance. + /// + /// Without provenance, this pointer is not associated with any actual allocation. Such a + /// no-provenance pointer may be used for zero-sized memory accesses (if suitably aligned), but + /// non-zero-sized memory accesses with a no-provenance pointer are UB. No-provenance pointers + /// are little more than a usize address in disguise. + /// + /// This is different from [`Self::with_exposed_provenance`], which creates a pointer that picks up a + /// previously exposed provenance. + /// + /// Equivalent to calling [`core::ptr::without_provenance`] on each element. + fn without_provenance(addr: Self::Usize) -> Self; + /// Creates a new pointer with the given address. /// /// This performs the same operation as a cast, but copies the *address-space* and @@ -115,6 +128,14 @@ where unsafe { core::mem::transmute_copy(&self) } } + #[inline] + fn without_provenance(addr: Self::Usize) -> Self { + // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. + // SAFETY: Integer-to-pointer transmutes are valid (if you are okay with not getting any + // provenance). + unsafe { core::mem::transmute_copy(&addr) } + } + #[inline] fn with_addr(self, addr: Self::Usize) -> Self { // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. diff --git a/library/portable-simd/crates/core_simd/src/swizzle.rs b/library/portable-simd/crates/core_simd/src/swizzle.rs index d62642fb9061b..42425ef37e50b 100644 --- a/library/portable-simd/crates/core_simd/src/swizzle.rs +++ b/library/portable-simd/crates/core_simd/src/swizzle.rs @@ -155,8 +155,7 @@ pub trait Swizzle { /// Creates a new mask from the elements of `mask`. /// - /// Element `i` of the output is `concat[Self::INDEX[i]]`, where `concat` is the concatenation of - /// `first` and `second`. + /// Element `i` of the output is `mask[Self::INDEX[i]]`. #[inline] #[must_use = "method returns a new mask and does not mutate the original inputs"] fn swizzle_mask(mask: Mask) -> Mask @@ -260,6 +259,50 @@ where Rotate::::swizzle(self) } + /// Shifts the vector elements to the left by `OFFSET`, filling in with + /// `padding` from the right. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn shift_elements_left(self, padding: T) -> Self { + struct Shift; + + impl Swizzle for Shift { + const INDEX: [usize; N] = const { + let mut index = [N; N]; + let mut i = 0; + while i + OFFSET < N { + index[i] = i + OFFSET; + i += 1; + } + index + }; + } + + Shift::::concat_swizzle(self, Simd::splat(padding)) + } + + /// Shifts the vector elements to the right by `OFFSET`, filling in with + /// `padding` from the left. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn shift_elements_right(self, padding: T) -> Self { + struct Shift; + + impl Swizzle for Shift { + const INDEX: [usize; N] = const { + let mut index = [N; N]; + let mut i = OFFSET; + while i < N { + index[i] = i - OFFSET; + i += 1; + } + index + }; + } + + Shift::::concat_swizzle(self, Simd::splat(padding)) + } + /// Interleave two vectors. /// /// The resulting vectors contain elements taken alternatively from `self` and `other`, first @@ -320,7 +363,9 @@ where /// /// ``` /// # #![feature(portable_simd)] - /// # use core::simd::Simd; + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::Simd; /// let a = Simd::from_array([0, 4, 1, 5]); /// let b = Simd::from_array([2, 6, 3, 7]); /// let (x, y) = a.deinterleave(b); @@ -391,4 +436,210 @@ where } Resize::::concat_swizzle(self, Simd::splat(value)) } + + /// Extract a vector from another vector. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::u32x4; + /// let x = u32x4::from_array([0, 1, 2, 3]); + /// assert_eq!(x.extract::<1, 2>().to_array(), [1, 2]); + /// ``` + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn extract(self) -> Simd + where + LaneCount: SupportedLaneCount, + { + struct Extract; + impl Swizzle for Extract { + const INDEX: [usize; LEN] = const { + assert!(START + LEN <= N, "index out of bounds"); + let mut index = [0; LEN]; + let mut i = 0; + while i < LEN { + index[i] = START + i; + i += 1; + } + index + }; + } + Extract::::swizzle(self) + } +} + +impl Mask +where + T: MaskElement, + LaneCount: SupportedLaneCount, +{ + /// Reverse the order of the elements in the mask. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn reverse(self) -> Self { + // Safety: swizzles are safe for masks + unsafe { Self::from_int_unchecked(self.to_int().reverse()) } + } + + /// Rotates the mask such that the first `OFFSET` elements of the slice move to the end + /// while the last `self.len() - OFFSET` elements move to the front. After calling `rotate_elements_left`, + /// the element previously at index `OFFSET` will become the first element in the slice. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn rotate_elements_left(self) -> Self { + // Safety: swizzles are safe for masks + unsafe { Self::from_int_unchecked(self.to_int().rotate_elements_left::()) } + } + + /// Rotates the mask such that the first `self.len() - OFFSET` elements of the mask move to + /// the end while the last `OFFSET` elements move to the front. After calling `rotate_elements_right`, + /// the element previously at index `self.len() - OFFSET` will become the first element in the slice. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn rotate_elements_right(self) -> Self { + // Safety: swizzles are safe for masks + unsafe { Self::from_int_unchecked(self.to_int().rotate_elements_right::()) } + } + + /// Shifts the mask elements to the left by `OFFSET`, filling in with + /// `padding` from the right. + #[inline] + #[must_use = "method returns a new mask and does not mutate the original inputs"] + pub fn shift_elements_left(self, padding: bool) -> Self { + // Safety: swizzles are safe for masks + unsafe { + Self::from_int_unchecked(self.to_int().shift_elements_left::(if padding { + T::TRUE + } else { + T::FALSE + })) + } + } + + /// Shifts the mask elements to the right by `OFFSET`, filling in with + /// `padding` from the left. + #[inline] + #[must_use = "method returns a new mask and does not mutate the original inputs"] + pub fn shift_elements_right(self, padding: bool) -> Self { + // Safety: swizzles are safe for masks + unsafe { + Self::from_int_unchecked(self.to_int().shift_elements_right::(if padding { + T::TRUE + } else { + T::FALSE + })) + } + } + + /// Interleave two masks. + /// + /// The resulting masks contain elements taken alternatively from `self` and `other`, first + /// filling the first result, and then the second. + /// + /// The reverse of this operation is [`Mask::deinterleave`]. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::mask32x4; + /// let a = mask32x4::from_array([false, true, false, true]); + /// let b = mask32x4::from_array([false, false, true, true]); + /// let (x, y) = a.interleave(b); + /// assert_eq!(x.to_array(), [false, false, true, false]); + /// assert_eq!(y.to_array(), [false, true, true, true]); + /// ``` + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn interleave(self, other: Self) -> (Self, Self) { + let (lo, hi) = self.to_int().interleave(other.to_int()); + // Safety: swizzles are safe for masks + unsafe { (Self::from_int_unchecked(lo), Self::from_int_unchecked(hi)) } + } + + /// Deinterleave two masks. + /// + /// The first result takes every other element of `self` and then `other`, starting with + /// the first element. + /// + /// The second result takes every other element of `self` and then `other`, starting with + /// the second element. + /// + /// The reverse of this operation is [`Mask::interleave`]. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::mask32x4; + /// let a = mask32x4::from_array([false, true, false, true]); + /// let b = mask32x4::from_array([false, false, true, true]); + /// let (x, y) = a.deinterleave(b); + /// assert_eq!(x.to_array(), [false, false, false, true]); + /// assert_eq!(y.to_array(), [true, true, false, true]); + /// ``` + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn deinterleave(self, other: Self) -> (Self, Self) { + let (even, odd) = self.to_int().deinterleave(other.to_int()); + // Safety: swizzles are safe for masks + unsafe { + ( + Self::from_int_unchecked(even), + Self::from_int_unchecked(odd), + ) + } + } + + /// Resize a mask. + /// + /// If `M` > `N`, extends the length of a mask, setting the new elements to `value`. + /// If `M` < `N`, truncates the mask to the first `M` elements. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::mask32x4; + /// let x = mask32x4::from_array([false, true, true, false]); + /// assert_eq!(x.resize::<8>(true).to_array(), [false, true, true, false, true, true, true, true]); + /// assert_eq!(x.resize::<2>(true).to_array(), [false, true]); + /// ``` + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn resize(self, value: bool) -> Mask + where + LaneCount: SupportedLaneCount, + { + // Safety: swizzles are safe for masks + unsafe { + Mask::::from_int_unchecked(self.to_int().resize::(if value { + T::TRUE + } else { + T::FALSE + })) + } + } + + /// Extract a vector from another vector. + /// + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::mask32x4; + /// let x = mask32x4::from_array([false, true, true, false]); + /// assert_eq!(x.extract::<1, 2>().to_array(), [true, true]); + /// ``` + #[inline] + #[must_use = "method returns a new vector and does not mutate the original inputs"] + pub fn extract(self) -> Mask + where + LaneCount: SupportedLaneCount, + { + // Safety: swizzles are safe for masks + unsafe { Mask::::from_int_unchecked(self.to_int().extract::()) } + } } diff --git a/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs b/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs index 3b6388d0f2759..773bd028bae09 100644 --- a/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs +++ b/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs @@ -59,15 +59,40 @@ where target_endian = "little" ))] 16 => transize(vqtbl1q_u8, self, idxs), + #[cfg(all( + target_arch = "arm", + target_feature = "v7", + target_feature = "neon", + target_endian = "little" + ))] + 16 => transize(armv7_neon_swizzle_u8x16, self, idxs), #[cfg(all(target_feature = "avx2", not(target_feature = "avx512vbmi")))] 32 => transize(avx2_pshufb, self, idxs), #[cfg(all(target_feature = "avx512vl", target_feature = "avx512vbmi"))] - 32 => transize(x86::_mm256_permutexvar_epi8, zeroing_idxs(idxs), self), - // Notable absence: avx512bw shuffle - // If avx512bw is available, odds of avx512vbmi are good - // FIXME: initial AVX512VBMI variant didn't actually pass muster - // #[cfg(target_feature = "avx512vbmi")] - // 64 => transize(x86::_mm512_permutexvar_epi8, self, idxs), + 32 => { + // Unlike vpshufb, vpermb doesn't zero out values in the result based on the index high bit + let swizzler = |bytes, idxs| { + let mask = x86::_mm256_cmp_epu8_mask::<{ x86::_MM_CMPINT_LT }>( + idxs, + Simd::::splat(N as u8).into(), + ); + x86::_mm256_maskz_permutexvar_epi8(mask, idxs, bytes) + }; + transize(swizzler, self, idxs) + } + // Notable absence: avx512bw pshufb shuffle + #[cfg(all(target_feature = "avx512vl", target_feature = "avx512vbmi"))] + 64 => { + // Unlike vpshufb, vpermb doesn't zero out values in the result based on the index high bit + let swizzler = |bytes, idxs| { + let mask = x86::_mm512_cmp_epu8_mask::<{ x86::_MM_CMPINT_LT }>( + idxs, + Simd::::splat(N as u8).into(), + ); + x86::_mm512_maskz_permutexvar_epi8(mask, idxs, bytes) + }; + transize(swizzler, self, idxs) + } _ => { let mut array = [0; N]; for (i, k) in idxs.to_array().into_iter().enumerate() { @@ -82,6 +107,28 @@ where } } +/// armv7 neon supports swizzling `u8x16` by swizzling two u8x8 blocks +/// with a u8x8x2 lookup table. +/// +/// # Safety +/// This requires armv7 neon to work +#[cfg(all( + target_arch = "arm", + target_feature = "v7", + target_feature = "neon", + target_endian = "little" +))] +unsafe fn armv7_neon_swizzle_u8x16(bytes: Simd, idxs: Simd) -> Simd { + use core::arch::arm::{uint8x8x2_t, vcombine_u8, vget_high_u8, vget_low_u8, vtbl2_u8}; + // SAFETY: Caller promised arm neon support + unsafe { + let bytes = uint8x8x2_t(vget_low_u8(bytes.into()), vget_high_u8(bytes.into())); + let lo = vtbl2_u8(bytes, vget_low_u8(idxs.into())); + let hi = vtbl2_u8(bytes, vget_high_u8(idxs.into())); + vcombine_u8(lo, hi).into() + } +} + /// "vpshufb like it was meant to be" on AVX2 /// /// # Safety diff --git a/library/portable-simd/crates/core_simd/src/vector.rs b/library/portable-simd/crates/core_simd/src/vector.rs index 3e23916914963..9c4dd36c24fe8 100644 --- a/library/portable-simd/crates/core_simd/src/vector.rs +++ b/library/portable-simd/crates/core_simd/src/vector.rs @@ -99,7 +99,7 @@ use crate::simd::{ // directly constructing an instance of the type (i.e. `let vector = Simd(array)`) should be // avoided, as it will likely become illegal on `#[repr(simd)]` structs in the future. It also // causes rustc to emit illegal LLVM IR in some cases. -#[repr(simd)] +#[repr(simd, packed)] pub struct Simd([T; N]) where LaneCount: SupportedLaneCount, @@ -144,14 +144,32 @@ where /// assert_eq!(v.as_array(), &[8, 8, 8, 8]); /// ``` #[inline] - pub fn splat(value: T) -> Self { - // This is preferred over `[value; N]`, since it's explicitly a splat: - // https://github.com/rust-lang/rust/issues/97804 - struct Splat; - impl Swizzle for Splat { - const INDEX: [usize; N] = [0; N]; + #[rustc_const_unstable(feature = "portable_simd", issue = "86656")] + pub const fn splat(value: T) -> Self { + const fn splat_const(value: T) -> Simd + where + T: SimdElement, + LaneCount: SupportedLaneCount, + { + Simd::from_array([value; N]) } - Splat::swizzle::(Simd::::from([value])) + + fn splat_rt(value: T) -> Simd + where + T: SimdElement, + LaneCount: SupportedLaneCount, + { + // This is preferred over `[value; N]`, since it's explicitly a splat: + // https://github.com/rust-lang/rust/issues/97804 + struct Splat; + impl Swizzle for Splat { + const INDEX: [usize; N] = [0; N]; + } + + Splat::swizzle::(Simd::::from([value])) + } + + core::intrinsics::const_eval_select((value,), splat_const, splat_rt) } /// Returns an array reference containing the entire SIMD vector. @@ -425,6 +443,9 @@ where /// /// When the element is disabled, that memory location is not accessed and the corresponding /// value from `or` is passed through. + /// + /// # Safety + /// Enabled loads must not exceed the length of `slice`. #[must_use] #[inline] pub unsafe fn load_select_unchecked( @@ -442,6 +463,9 @@ where /// /// When the element is disabled, that memory location is not accessed and the corresponding /// value from `or` is passed through. + /// + /// # Safety + /// Enabled `ptr` elements must be safe to read as if by `std::ptr::read`. #[must_use] #[inline] pub unsafe fn load_select_ptr( @@ -924,6 +948,7 @@ where } } +/// Lexicographic order. For the SIMD elementwise minimum and maximum, use simd_min and simd_max instead. impl PartialOrd for Simd where LaneCount: SupportedLaneCount, @@ -943,6 +968,7 @@ where { } +/// Lexicographic order. For the SIMD elementwise minimum and maximum, use simd_min and simd_max instead. impl Ord for Simd where LaneCount: SupportedLaneCount, @@ -1195,6 +1221,7 @@ fn lane_indices() -> Simd where LaneCount: SupportedLaneCount, { + #![allow(clippy::needless_range_loop)] let mut index = [0; N]; for i in 0..N { index[i] = i; diff --git a/library/portable-simd/crates/core_simd/src/vendor.rs b/library/portable-simd/crates/core_simd/src/vendor.rs index 1a34a3a8de5c4..57536e4fc77dc 100644 --- a/library/portable-simd/crates/core_simd/src/vendor.rs +++ b/library/portable-simd/crates/core_simd/src/vendor.rs @@ -29,3 +29,6 @@ mod arm; #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] mod powerpc; + +#[cfg(target_arch = "loongarch64")] +mod loongarch64; diff --git a/library/portable-simd/crates/core_simd/src/vendor/loongarch64.rs b/library/portable-simd/crates/core_simd/src/vendor/loongarch64.rs new file mode 100644 index 0000000000000..1290bc166b2b8 --- /dev/null +++ b/library/portable-simd/crates/core_simd/src/vendor/loongarch64.rs @@ -0,0 +1,31 @@ +use crate::simd::*; +use core::arch::loongarch64::*; + +from_transmute! { unsafe u8x16 => v16u8 } +from_transmute! { unsafe u8x32 => v32u8 } +from_transmute! { unsafe i8x16 => v16i8 } +from_transmute! { unsafe i8x32 => v32i8 } + +from_transmute! { unsafe u16x8 => v8u16 } +from_transmute! { unsafe u16x16 => v16u16 } +from_transmute! { unsafe i16x8 => v8i16 } +from_transmute! { unsafe i16x16 => v16i16 } + +from_transmute! { unsafe u32x4 => v4u32 } +from_transmute! { unsafe u32x8 => v8u32 } +from_transmute! { unsafe i32x4 => v4i32 } +from_transmute! { unsafe i32x8 => v8i32 } +from_transmute! { unsafe f32x4 => v4f32 } +from_transmute! { unsafe f32x8 => v8f32 } + +from_transmute! { unsafe u64x2 => v2u64 } +from_transmute! { unsafe u64x4 => v4u64 } +from_transmute! { unsafe i64x2 => v2i64 } +from_transmute! { unsafe i64x4 => v4i64 } +from_transmute! { unsafe f64x2 => v2f64 } +from_transmute! { unsafe f64x4 => v4f64 } + +from_transmute! { unsafe usizex2 => v2u64 } +from_transmute! { unsafe usizex4 => v4u64 } +from_transmute! { unsafe isizex2 => v2i64 } +from_transmute! { unsafe isizex4 => v4i64 } diff --git a/library/portable-simd/crates/core_simd/tests/layout.rs b/library/portable-simd/crates/core_simd/tests/layout.rs new file mode 100644 index 0000000000000..24114c2d261e7 --- /dev/null +++ b/library/portable-simd/crates/core_simd/tests/layout.rs @@ -0,0 +1,35 @@ +#![feature(portable_simd)] + +macro_rules! layout_tests { + { $($mod:ident, $ty:ty,)* } => { + $( + mod $mod { + test_helpers::test_lanes! { + fn no_padding() { + assert_eq!( + core::mem::size_of::>(), + core::mem::size_of::<[$ty; LANES]>(), + ); + } + } + } + )* + } +} + +layout_tests! { + i8, i8, + i16, i16, + i32, i32, + i64, i64, + isize, isize, + u8, u8, + u16, u16, + u32, u32, + u64, u64, + usize, usize, + f32, f32, + f64, f64, + mut_ptr, *mut (), + const_ptr, *const (), +} diff --git a/library/portable-simd/crates/core_simd/tests/masks.rs b/library/portable-simd/crates/core_simd/tests/masks.rs index fc6a3476b7c60..48786d02440b3 100644 --- a/library/portable-simd/crates/core_simd/tests/masks.rs +++ b/library/portable-simd/crates/core_simd/tests/masks.rs @@ -99,7 +99,6 @@ macro_rules! test_mask_api { assert_eq!(Mask::<$type, 2>::from_bitmask(bitmask), mask); } - #[cfg(feature = "all_lane_counts")] #[test] fn roundtrip_bitmask_conversion_odd() { let values = [ @@ -134,48 +133,6 @@ macro_rules! test_mask_api { cast_impl::(); cast_impl::(); } - - #[test] - fn roundtrip_bitmask_vector_conversion() { - use core_simd::simd::ToBytes; - let values = [ - true, false, false, true, false, false, true, false, - true, true, false, false, false, false, false, true, - ]; - let mask = Mask::<$type, 16>::from_array(values); - let bitmask = mask.to_bitmask_vector(); - assert_eq!(bitmask.resize::<2>(0).to_ne_bytes()[..2], [0b01001001, 0b10000011]); - assert_eq!(Mask::<$type, 16>::from_bitmask_vector(bitmask), mask); - } - - // rust-lang/portable-simd#379 - #[test] - fn roundtrip_bitmask_vector_conversion_small() { - use core_simd::simd::ToBytes; - let values = [ - true, false, true, true - ]; - let mask = Mask::<$type, 4>::from_array(values); - let bitmask = mask.to_bitmask_vector(); - assert_eq!(bitmask.resize::<1>(0).to_ne_bytes()[0], 0b00001101); - assert_eq!(Mask::<$type, 4>::from_bitmask_vector(bitmask), mask); - } - - /* FIXME doesn't work with non-powers-of-two, yet - // rust-lang/portable-simd#379 - #[cfg(feature = "all_lane_counts")] - #[test] - fn roundtrip_bitmask_vector_conversion_odd() { - use core_simd::simd::ToBytes; - let values = [ - true, false, true, false, true, true, false, false, false, true, true, - ]; - let mask = Mask::<$type, 11>::from_array(values); - let bitmask = mask.to_bitmask_vector(); - assert_eq!(bitmask.resize::<2>(0).to_ne_bytes()[..2], [0b00110101, 0b00000110]); - assert_eq!(Mask::<$type, 11>::from_bitmask_vector(bitmask), mask); - } - */ } } } diff --git a/library/portable-simd/crates/core_simd/tests/ops_macros.rs b/library/portable-simd/crates/core_simd/tests/ops_macros.rs index aa565a137527e..6de78f51e59df 100644 --- a/library/portable-simd/crates/core_simd/tests/ops_macros.rs +++ b/library/portable-simd/crates/core_simd/tests/ops_macros.rs @@ -216,6 +216,22 @@ macro_rules! impl_common_integer_tests { ) } + fn count_ones() { + test_helpers::test_unary_elementwise( + &$vector::::count_ones, + &|x| x.count_ones() as _, + &|_| true, + ) + } + + fn count_zeros() { + test_helpers::test_unary_elementwise( + &$vector::::count_zeros, + &|x| x.count_zeros() as _, + &|_| true, + ) + } + fn leading_zeros() { test_helpers::test_unary_elementwise( &$vector::::leading_zeros, @@ -307,6 +323,14 @@ macro_rules! impl_signed_tests { assert_eq!(a % b, Vector::::splat(0)); } + fn abs_diff() { + test_helpers::test_binary_elementwise( + &Vector::::abs_diff, + &Scalar::abs_diff, + &|_, _| true, + ) + } + fn simd_min() { use core_simd::simd::cmp::SimdOrd; let a = Vector::::splat(Scalar::MIN); @@ -419,6 +443,14 @@ macro_rules! impl_unsigned_tests { &|_| true, ); } + + fn abs_diff() { + test_helpers::test_binary_elementwise( + &Vector::::abs_diff, + &Scalar::abs_diff, + &|_, _| true, + ) + } } impl_binary_op_test!(Scalar, Add::add, AddAssign::add_assign, Scalar::wrapping_add); @@ -495,6 +527,9 @@ macro_rules! impl_float_tests { } fn is_normal() { + // Arm v7 Neon violates float opsem re: subnormals, see + // https://github.com/rust-lang/portable-simd/issues/439 + #[cfg(not(target_arch = "arm"))] test_helpers::test_unary_mask_elementwise( &Vector::::is_normal, &Scalar::is_normal, @@ -503,6 +538,9 @@ macro_rules! impl_float_tests { } fn is_subnormal() { + // Arm v7 Neon violates float opsem re: subnormals, see + // https://github.com/rust-lang/portable-simd/issues/439 + #[cfg(not(target_arch = "arm"))] test_helpers::test_unary_mask_elementwise( &Vector::::is_subnormal, &Scalar::is_subnormal, diff --git a/library/portable-simd/crates/core_simd/tests/swizzle.rs b/library/portable-simd/crates/core_simd/tests/swizzle.rs index 522d71439b77d..7001e5f6bf87b 100644 --- a/library/portable-simd/crates/core_simd/tests/swizzle.rs +++ b/library/portable-simd/crates/core_simd/tests/swizzle.rs @@ -48,6 +48,24 @@ fn rotate() { assert_eq!(a.rotate_elements_right::<5>().to_array(), [4, 1, 2, 3]); } +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn shift() { + let a = Simd::from_array([1, 2, 3, 4]); + assert_eq!(a.shift_elements_left::<0>(0).to_array(), [1, 2, 3, 4]); + assert_eq!(a.shift_elements_left::<1>(0).to_array(), [2, 3, 4, 0]); + assert_eq!(a.shift_elements_left::<2>(9).to_array(), [3, 4, 9, 9]); + assert_eq!(a.shift_elements_left::<3>(8).to_array(), [4, 8, 8, 8]); + assert_eq!(a.shift_elements_left::<4>(7).to_array(), [7, 7, 7, 7]); + assert_eq!(a.shift_elements_left::<5>(6).to_array(), [6, 6, 6, 6]); + assert_eq!(a.shift_elements_right::<0>(0).to_array(), [1, 2, 3, 4]); + assert_eq!(a.shift_elements_right::<1>(0).to_array(), [0, 1, 2, 3]); + assert_eq!(a.shift_elements_right::<2>(-1).to_array(), [-1, -1, 1, 2]); + assert_eq!(a.shift_elements_right::<3>(-2).to_array(), [-2, -2, -2, 1]); + assert_eq!(a.shift_elements_right::<4>(-3).to_array(), [-3, -3, -3, -3]); + assert_eq!(a.shift_elements_right::<5>(-4).to_array(), [-4, -4, -4, -4]); +} + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn interleave() { diff --git a/library/portable-simd/crates/test_helpers/Cargo.toml b/library/portable-simd/crates/test_helpers/Cargo.toml index 23dae7c93381e..a5359b9abc84d 100644 --- a/library/portable-simd/crates/test_helpers/Cargo.toml +++ b/library/portable-simd/crates/test_helpers/Cargo.toml @@ -6,6 +6,3 @@ publish = false [dependencies] proptest = { version = "0.10", default-features = false, features = ["alloc"] } - -[features] -all_lane_counts = [] diff --git a/library/portable-simd/crates/test_helpers/src/lib.rs b/library/portable-simd/crates/test_helpers/src/lib.rs index 51b860a863560..197c920e11eac 100644 --- a/library/portable-simd/crates/test_helpers/src/lib.rs +++ b/library/portable-simd/crates/test_helpers/src/lib.rs @@ -539,32 +539,22 @@ macro_rules! test_lanes { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; lanes_1 1; lanes_2 2; - lanes_4 4; - ); - - #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow - $crate::test_lanes_helper!( - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; - lanes_8 8; - lanes_16 16; - lanes_32 32; - lanes_64 64; - ); - - #[cfg(feature = "all_lane_counts")] - $crate::test_lanes_helper!( - // test some odd and even non-power-of-2 lengths on miri - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; + // Cover an odd and an even non-power-of-2 length in Miri. + // (Even non-power-of-2 vectors have alignment between element + // and vector size, so we want to cover that case as well.) lanes_3 3; - lanes_5 5; + lanes_6 6; ); - #[cfg(feature = "all_lane_counts")] #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow $crate::test_lanes_helper!( #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]; + lanes_4 4; + lanes_5 5; + lanes_7 7; + lanes_8 8; lanes_9 9; lanes_10 10; lanes_11 11; @@ -572,52 +562,55 @@ macro_rules! test_lanes { lanes_13 13; lanes_14 14; lanes_15 15; + lanes_16 16; lanes_17 17; - lanes_18 18; - lanes_19 19; - lanes_20 20; - lanes_21 21; - lanes_22 22; - lanes_23 23; + //lanes_18 18; + //lanes_19 19; + //lanes_20 20; + //lanes_21 21; + //lanes_22 22; + //lanes_23 23; lanes_24 24; - lanes_25 25; - lanes_26 26; - lanes_27 27; - lanes_28 28; - lanes_29 29; - lanes_30 30; - lanes_31 31; - lanes_33 33; - lanes_34 34; - lanes_35 35; - lanes_36 36; - lanes_37 37; - lanes_38 38; - lanes_39 39; - lanes_40 40; - lanes_41 41; - lanes_42 42; - lanes_43 43; - lanes_44 44; - lanes_45 45; - lanes_46 46; + //lanes_25 25; + //lanes_26 26; + //lanes_27 27; + //lanes_28 28; + //lanes_29 29; + //lanes_30 30; + //lanes_31 31; + lanes_32 32; + //lanes_33 33; + //lanes_34 34; + //lanes_35 35; + //lanes_36 36; + //lanes_37 37; + //lanes_38 38; + //lanes_39 39; + //lanes_40 40; + //lanes_41 41; + //lanes_42 42; + //lanes_43 43; + //lanes_44 44; + //lanes_45 45; + //lanes_46 46; lanes_47 47; - lanes_48 48; - lanes_49 49; - lanes_50 50; - lanes_51 51; - lanes_52 52; - lanes_53 53; - lanes_54 54; - lanes_55 55; + //lanes_48 48; + //lanes_49 49; + //lanes_50 50; + //lanes_51 51; + //lanes_52 52; + //lanes_53 53; + //lanes_54 54; + //lanes_55 55; lanes_56 56; lanes_57 57; - lanes_58 58; - lanes_59 59; - lanes_60 60; - lanes_61 61; - lanes_62 62; + //lanes_58 58; + //lanes_59 59; + //lanes_60 60; + //lanes_61 61; + //lanes_62 62; lanes_63 63; + lanes_64 64; ); } )* @@ -639,36 +632,24 @@ macro_rules! test_lanes_panic { core_simd::simd::LaneCount<$lanes>: core_simd::simd::SupportedLaneCount, $body + // test some odd and even non-power-of-2 lengths on miri $crate::test_lanes_helper!( #[should_panic]; lanes_1 1; lanes_2 2; - lanes_4 4; - ); - - #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow - $crate::test_lanes_helper!( - #[should_panic]; - lanes_8 8; - lanes_16 16; - lanes_32 32; - lanes_64 64; - ); - - #[cfg(feature = "all_lane_counts")] - $crate::test_lanes_helper!( - // test some odd and even non-power-of-2 lengths on miri - #[should_panic]; lanes_3 3; - lanes_5 5; + lanes_6 6; ); - #[cfg(feature = "all_lane_counts")] #[cfg(not(miri))] // Miri intrinsic implementations are uniform and larger tests are sloooow $crate::test_lanes_helper!( #[should_panic]; + lanes_4 4; + lanes_5 5; + lanes_7 7; + lanes_8 8; lanes_9 9; lanes_10 10; lanes_11 11; @@ -676,52 +657,55 @@ macro_rules! test_lanes_panic { lanes_13 13; lanes_14 14; lanes_15 15; + lanes_16 16; lanes_17 17; - lanes_18 18; - lanes_19 19; - lanes_20 20; - lanes_21 21; - lanes_22 22; - lanes_23 23; + //lanes_18 18; + //lanes_19 19; + //lanes_20 20; + //lanes_21 21; + //lanes_22 22; + //lanes_23 23; lanes_24 24; - lanes_25 25; - lanes_26 26; - lanes_27 27; - lanes_28 28; - lanes_29 29; - lanes_30 30; - lanes_31 31; - lanes_33 33; - lanes_34 34; - lanes_35 35; - lanes_36 36; - lanes_37 37; - lanes_38 38; - lanes_39 39; - lanes_40 40; - lanes_41 41; - lanes_42 42; - lanes_43 43; - lanes_44 44; - lanes_45 45; - lanes_46 46; + //lanes_25 25; + //lanes_26 26; + //lanes_27 27; + //lanes_28 28; + //lanes_29 29; + //lanes_30 30; + //lanes_31 31; + lanes_32 32; + //lanes_33 33; + //lanes_34 34; + //lanes_35 35; + //lanes_36 36; + //lanes_37 37; + //lanes_38 38; + //lanes_39 39; + //lanes_40 40; + //lanes_41 41; + //lanes_42 42; + //lanes_43 43; + //lanes_44 44; + //lanes_45 45; + //lanes_46 46; lanes_47 47; - lanes_48 48; - lanes_49 49; - lanes_50 50; - lanes_51 51; - lanes_52 52; - lanes_53 53; - lanes_54 54; - lanes_55 55; + //lanes_48 48; + //lanes_49 49; + //lanes_50 50; + //lanes_51 51; + //lanes_52 52; + //lanes_53 53; + //lanes_54 54; + //lanes_55 55; lanes_56 56; lanes_57 57; - lanes_58 58; - lanes_59 59; - lanes_60 60; - lanes_61 61; - lanes_62 62; + //lanes_58 58; + //lanes_59 59; + //lanes_60 60; + //lanes_61 61; + //lanes_62 62; lanes_63 63; + lanes_64 64; ); } )* diff --git a/library/portable-simd/rust-toolchain.toml b/library/portable-simd/rust-toolchain.toml new file mode 100644 index 0000000000000..d17c6d2e88946 --- /dev/null +++ b/library/portable-simd/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "nightly-2025-01-16" +components = ["rustfmt", "clippy", "miri", "rust-src"] diff --git a/library/portable-simd/subtree-sync.sh b/library/portable-simd/subtree-sync.sh new file mode 100755 index 0000000000000..18360077623b1 --- /dev/null +++ b/library/portable-simd/subtree-sync.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +set -eou pipefail + +git fetch origin +pushd $2 +git fetch origin +popd + +if [ "$(git rev-parse --show-prefix)" != "" ]; then + echo "Run this script from the git root" >&2 + exit 1 +fi + +if [ "$(git rev-parse HEAD)" != "$(git rev-parse origin/master)" ]; then + echo "$(pwd) is not at origin/master" >&2 + exit 1 +fi + +if [ ! -f library/portable-simd/git-subtree.sh ]; then + curl -sS https://raw.githubusercontent.com/bjorn3/git/tqc-subtree-portable/contrib/subtree/git-subtree.sh -o library/portable-simd/git-subtree.sh + chmod +x library/portable-simd/git-subtree.sh +fi + +today=$(date +%Y-%m-%d) + +case $1 in + "push") + upstream=rust-upstream-$today + merge=sync-from-rust-$today + + pushd $2 + git checkout master + git pull + popd + + library/portable-simd/git-subtree.sh push -P library/portable-simd $2 $upstream + + pushd $2 + git checkout -B $merge origin/master + git merge $upstream + popd + echo "Branch \`$merge\` created in \`$2\`. You may need to resolve merge conflicts." + ;; + "pull") + branch=sync-from-portable-simd-$today + + git checkout -B $branch + echo "Creating branch \`$branch\`... You may need to resolve merge conflicts." + library/portable-simd/git-subtree.sh pull -P library/portable-simd $2 origin/master + ;; +esac diff --git a/library/proc_macro/src/bridge/arena.rs b/library/proc_macro/src/bridge/arena.rs index 1d5986093c8a4..29636e793f614 100644 --- a/library/proc_macro/src/bridge/arena.rs +++ b/library/proc_macro/src/bridge/arena.rs @@ -102,7 +102,7 @@ impl Arena { #[allow(clippy::mut_from_ref)] // arena allocator pub(crate) fn alloc_str<'a>(&'a self, string: &str) -> &'a mut str { let alloc = self.alloc_raw(string.len()); - let bytes = MaybeUninit::copy_from_slice(alloc, string.as_bytes()); + let bytes = alloc.write_copy_of_slice(string.as_bytes()); // SAFETY: we convert from `&str` to `&[u8]`, clone it into the arena, // and immediately convert the clone back to `&str`. diff --git a/library/proc_macro/src/bridge/closure.rs b/library/proc_macro/src/bridge/closure.rs index d371ae3cea098..524fdf53d6b7e 100644 --- a/library/proc_macro/src/bridge/closure.rs +++ b/library/proc_macro/src/bridge/closure.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; #[repr(C)] -pub struct Closure<'a, A, R> { +pub(super) struct Closure<'a, A, R> { call: unsafe extern "C" fn(*mut Env, A) -> R, env: *mut Env, // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual way of doing @@ -26,7 +26,7 @@ impl<'a, A, R, F: FnMut(A) -> R> From<&'a mut F> for Closure<'a, A, R> { } impl<'a, A, R> Closure<'a, A, R> { - pub fn call(&mut self, arg: A) -> R { + pub(super) fn call(&mut self, arg: A) -> R { unsafe { (self.call)(self.env, arg) } } } diff --git a/library/proc_macro/src/bridge/fxhash.rs b/library/proc_macro/src/bridge/fxhash.rs index 3345e099a3724..5f6b3d1b929e4 100644 --- a/library/proc_macro/src/bridge/fxhash.rs +++ b/library/proc_macro/src/bridge/fxhash.rs @@ -9,7 +9,7 @@ use std::hash::{BuildHasherDefault, Hasher}; use std::ops::BitXor; /// Type alias for a hashmap using the `fx` hash algorithm. -pub type FxHashMap = HashMap>; +pub(super) type FxHashMap = HashMap>; /// A speedy hash algorithm for use within rustc. The hashmap in alloc by /// default uses SipHash which isn't quite as speedy as we want. In the compiler @@ -23,7 +23,7 @@ pub type FxHashMap = HashMap>; /// similar or slightly worse than FNV, but the speed of the hash function /// itself is much higher because it works on up to 8 bytes at a time. #[derive(Default)] -pub struct FxHasher { +pub(super) struct FxHasher { hash: usize, } diff --git a/library/proc_macro/src/bridge/rpc.rs b/library/proc_macro/src/bridge/rpc.rs index 202a8e04543b2..85fd7d138585c 100644 --- a/library/proc_macro/src/bridge/rpc.rs +++ b/library/proc_macro/src/bridge/rpc.rs @@ -67,7 +67,7 @@ macro_rules! rpc_encode_decode { mod tag { #[repr(u8)] enum Tag { $($variant),* } - $(pub const $variant: u8 = Tag::$variant as u8;)* + $(pub(crate) const $variant: u8 = Tag::$variant as u8;)* } match self { @@ -89,7 +89,7 @@ macro_rules! rpc_encode_decode { mod tag { #[repr(u8)] enum Tag { $($variant),* } - $(pub const $variant: u8 = Tag::$variant as u8;)* + $(pub(crate) const $variant: u8 = Tag::$variant as u8;)* } match u8::decode(r, s) { diff --git a/library/proc_macro/src/bridge/selfless_reify.rs b/library/proc_macro/src/bridge/selfless_reify.rs index 907ad256e4b43..312a79152e23b 100644 --- a/library/proc_macro/src/bridge/selfless_reify.rs +++ b/library/proc_macro/src/bridge/selfless_reify.rs @@ -44,7 +44,7 @@ macro_rules! define_reify_functions { fn $name:ident $(<$($param:ident),*>)? for $(extern $abi:tt)? fn($($arg:ident: $arg_ty:ty),*) -> $ret_ty:ty; )+) => { - $(pub const fn $name< + $(pub(super) const fn $name< $($($param,)*)? F: Fn($($arg_ty),*) -> $ret_ty + Copy >(f: F) -> $(extern $abi)? fn($($arg_ty),*) -> $ret_ty { diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 26a09f0daca02..6611ce30a1b01 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -32,6 +32,7 @@ #![allow(internal_features)] #![deny(ffi_unwind_calls)] #![warn(rustdoc::unescaped_backticks)] +#![warn(unreachable_pub)] #[unstable(feature = "proc_macro_internals", issue = "27812")] #[doc(hidden)] @@ -419,7 +420,7 @@ pub mod token_stream { /// Unquoting is done with `$`, and works by taking the single next ident as the unquoted term. /// To quote `$` itself, use `$$`. #[unstable(feature = "proc_macro_quote", issue = "54722")] -#[allow_internal_unstable(proc_macro_def_site, proc_macro_internals)] +#[allow_internal_unstable(proc_macro_def_site, proc_macro_internals, proc_macro_totokens)] #[rustc_builtin_macro] pub macro quote($($t:tt)*) { /* compiler built-in */ diff --git a/library/proc_macro/src/quote.rs b/library/proc_macro/src/quote.rs index 04fa696d5e6be..bcb15912bb65e 100644 --- a/library/proc_macro/src/quote.rs +++ b/library/proc_macro/src/quote.rs @@ -4,12 +4,14 @@ //! This quasiquoter uses macros 2.0 hygiene to reliably access //! items from `proc_macro`, to build a `proc_macro::TokenStream`. -use crate::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; +use crate::{ + Delimiter, Group, Ident, Literal, Punct, Spacing, Span, ToTokens, TokenStream, TokenTree, +}; -macro_rules! quote_tt { - (($($t:tt)*)) => { Group::new(Delimiter::Parenthesis, quote!($($t)*)) }; - ([$($t:tt)*]) => { Group::new(Delimiter::Bracket, quote!($($t)*)) }; - ({$($t:tt)*}) => { Group::new(Delimiter::Brace, quote!($($t)*)) }; +macro_rules! minimal_quote_tt { + (($($t:tt)*)) => { Group::new(Delimiter::Parenthesis, minimal_quote!($($t)*)) }; + ([$($t:tt)*]) => { Group::new(Delimiter::Bracket, minimal_quote!($($t)*)) }; + ({$($t:tt)*}) => { Group::new(Delimiter::Brace, minimal_quote!($($t)*)) }; (,) => { Punct::new(',', Spacing::Alone) }; (.) => { Punct::new('.', Spacing::Alone) }; (;) => { Punct::new(';', Spacing::Alone) }; @@ -21,21 +23,20 @@ macro_rules! quote_tt { ($i:ident) => { Ident::new(stringify!($i), Span::def_site()) }; } -macro_rules! quote_ts { +macro_rules! minimal_quote_ts { ((@ $($t:tt)*)) => { $($t)* }; (::) => { - [ - TokenTree::from(Punct::new(':', Spacing::Joint)), - TokenTree::from(Punct::new(':', Spacing::Alone)), - ].iter() - .cloned() - .map(|mut x| { - x.set_span(Span::def_site()); - x - }) - .collect::() + { + let mut c = ( + TokenTree::from(Punct::new(':', Spacing::Joint)), + TokenTree::from(Punct::new(':', Spacing::Alone)) + ); + c.0.set_span(Span::def_site()); + c.1.set_span(Span::def_site()); + [c.0, c.1].into_iter().collect::() + } }; - ($t:tt) => { TokenTree::from(quote_tt!($t)) }; + ($t:tt) => { TokenTree::from(minimal_quote_tt!($t)) }; } /// Simpler version of the real `quote!` macro, implemented solely @@ -46,12 +47,14 @@ macro_rules! quote_ts { /// /// Note: supported tokens are a subset of the real `quote!`, but /// unquoting is different: instead of `$x`, this uses `(@ expr)`. -macro_rules! quote { - () => { TokenStream::new() }; +macro_rules! minimal_quote { ($($t:tt)*) => { - [ - $(TokenStream::from(quote_ts!($t)),)* - ].iter().cloned().collect::() + { + #[allow(unused_mut)] // In case the expansion is empty + let mut ts = TokenStream::new(); + $(ToTokens::to_tokens(&minimal_quote_ts!($t), &mut ts);)* + ts + } }; } @@ -62,52 +65,66 @@ macro_rules! quote { #[unstable(feature = "proc_macro_quote", issue = "54722")] pub fn quote(stream: TokenStream) -> TokenStream { if stream.is_empty() { - return quote!(crate::TokenStream::new()); + return minimal_quote!(crate::TokenStream::new()); } - let proc_macro_crate = quote!(crate); + let proc_macro_crate = minimal_quote!(crate); let mut after_dollar = false; - let tokens = stream - .into_iter() - .filter_map(|tree| { - if after_dollar { - after_dollar = false; - match tree { - TokenTree::Ident(_) => { - return Some(quote!(Into::::into( - Clone::clone(&(@ tree))),)); - } - TokenTree::Punct(ref tt) if tt.as_char() == '$' => {} - _ => panic!("`$` must be followed by an ident or `$` in `quote!`"), - } - } else if let TokenTree::Punct(ref tt) = tree { - if tt.as_char() == '$' { - after_dollar = true; - return None; + + let mut tokens = crate::TokenStream::new(); + for tree in stream { + if after_dollar { + after_dollar = false; + match tree { + TokenTree::Ident(_) => { + minimal_quote!(crate::ToTokens::to_tokens(&(@ tree), &mut ts);) + .to_tokens(&mut tokens); + continue; } + TokenTree::Punct(ref tt) if tt.as_char() == '$' => {} + _ => panic!("`$` must be followed by an ident or `$` in `quote!`"), } + } else if let TokenTree::Punct(ref tt) = tree { + if tt.as_char() == '$' { + after_dollar = true; + continue; + } + } - Some(quote!(crate::TokenStream::from((@ match tree { - TokenTree::Punct(tt) => quote!(crate::TokenTree::Punct(crate::Punct::new( + match tree { + TokenTree::Punct(tt) => { + minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Punct(crate::Punct::new( (@ TokenTree::from(Literal::character(tt.as_char()))), (@ match tt.spacing() { - Spacing::Alone => quote!(crate::Spacing::Alone), - Spacing::Joint => quote!(crate::Spacing::Joint), + Spacing::Alone => minimal_quote!(crate::Spacing::Alone), + Spacing::Joint => minimal_quote!(crate::Spacing::Joint), }), - ))), - TokenTree::Group(tt) => quote!(crate::TokenTree::Group(crate::Group::new( + )), &mut ts);) + } + TokenTree::Group(tt) => { + minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Group(crate::Group::new( (@ match tt.delimiter() { - Delimiter::Parenthesis => quote!(crate::Delimiter::Parenthesis), - Delimiter::Brace => quote!(crate::Delimiter::Brace), - Delimiter::Bracket => quote!(crate::Delimiter::Bracket), - Delimiter::None => quote!(crate::Delimiter::None), + Delimiter::Parenthesis => minimal_quote!(crate::Delimiter::Parenthesis), + Delimiter::Brace => minimal_quote!(crate::Delimiter::Brace), + Delimiter::Bracket => minimal_quote!(crate::Delimiter::Bracket), + Delimiter::None => minimal_quote!(crate::Delimiter::None), }), (@ quote(tt.stream())), - ))), - TokenTree::Ident(tt) => quote!(crate::TokenTree::Ident(crate::Ident::new( - (@ TokenTree::from(Literal::string(&tt.to_string()))), + )), &mut ts);) + } + TokenTree::Ident(tt) => { + let literal = tt.to_string(); + let (literal, ctor) = if let Some(stripped) = literal.strip_prefix("r#") { + (stripped, minimal_quote!(crate::Ident::new_raw)) + } else { + (literal.as_str(), minimal_quote!(crate::Ident::new)) + }; + minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Ident((@ ctor)( + (@ TokenTree::from(Literal::string(literal))), (@ quote_span(proc_macro_crate.clone(), tt.span())), - ))), - TokenTree::Literal(tt) => quote!(crate::TokenTree::Literal({ + )), &mut ts);) + } + TokenTree::Literal(tt) => { + minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Literal({ let mut iter = (@ TokenTree::from(Literal::string(&tt.to_string()))) .parse::() .unwrap() @@ -120,16 +137,22 @@ pub fn quote(stream: TokenStream) -> TokenStream { } else { unreachable!() } - })) - })),)) - }) - .collect::(); - + }), &mut ts);) + } + } + .to_tokens(&mut tokens); + } if after_dollar { panic!("unexpected trailing `$` in `quote!`"); } - quote!([(@ tokens)].iter().cloned().collect::()) + minimal_quote! { + { + let mut ts = crate::TokenStream::new(); + (@ tokens) + ts + } + } } /// Quote a `Span` into a `TokenStream`. @@ -137,5 +160,5 @@ pub fn quote(stream: TokenStream) -> TokenStream { #[unstable(feature = "proc_macro_quote", issue = "54722")] pub fn quote_span(proc_macro_crate: TokenStream, span: Span) -> TokenStream { let id = span.save_span(); - quote!((@ proc_macro_crate ) ::Span::recover_proc_macro_span((@ TokenTree::from(Literal::usize_unsuffixed(id))))) + minimal_quote!((@ proc_macro_crate ) ::Span::recover_proc_macro_span((@ TokenTree::from(Literal::usize_unsuffixed(id))))) } diff --git a/library/rtstartup/rsbegin.rs b/library/rtstartup/rsbegin.rs index 9a3d95bd8ddfb..67b09599d9d2b 100644 --- a/library/rtstartup/rsbegin.rs +++ b/library/rtstartup/rsbegin.rs @@ -19,6 +19,7 @@ #![no_core] #![allow(non_camel_case_types)] #![allow(internal_features)] +#![warn(unreachable_pub)] #[lang = "sized"] trait Sized {} @@ -51,7 +52,7 @@ pub unsafe fn drop_in_place(to_drop: *mut T) { #[cfg(all(target_os = "windows", target_arch = "x86", target_env = "gnu"))] pub mod eh_frames { #[no_mangle] - #[link_section = ".eh_frame"] + #[unsafe(link_section = ".eh_frame")] // Marks beginning of the stack frame unwind info section pub static __EH_FRAME_BEGIN__: [u8; 0] = []; @@ -75,7 +76,7 @@ pub mod eh_frames { } // Unwind info registration/deregistration routines. - extern "C" { + unsafe extern "C" { fn __register_frame_info(eh_frame_begin: *const u8, object: *mut u8); fn __deregister_frame_info(eh_frame_begin: *const u8, object: *mut u8); } @@ -100,10 +101,10 @@ pub mod eh_frames { // end of the list. Since constructors are run in reverse order, this ensures that our // callbacks are the first and last ones executed. - #[link_section = ".ctors.65535"] // .ctors.* : C initialization callbacks + #[unsafe(link_section = ".ctors.65535")] // .ctors.* : C initialization callbacks pub static P_INIT: unsafe extern "C" fn() = super::init; - #[link_section = ".dtors.65535"] // .dtors.* : C termination callbacks + #[unsafe(link_section = ".dtors.65535")] // .dtors.* : C termination callbacks pub static P_UNINIT: unsafe extern "C" fn() = super::uninit; } } diff --git a/library/rtstartup/rsend.rs b/library/rtstartup/rsend.rs index 2514eb0034402..a6f7d103356bf 100644 --- a/library/rtstartup/rsend.rs +++ b/library/rtstartup/rsend.rs @@ -6,6 +6,7 @@ #![crate_type = "rlib"] #![no_core] #![allow(internal_features)] +#![warn(unreachable_pub)] #[lang = "sized"] trait Sized {} @@ -31,6 +32,6 @@ pub mod eh_frames { // Terminate the frame unwind info section with a 0 as a sentinel; // this would be the 'length' field in a real FDE. #[no_mangle] - #[link_section = ".eh_frame"] + #[unsafe(link_section = ".eh_frame")] pub static __EH_FRAME_END__: u32 = 0; } diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 6380c941e6ab5..36a0a59d939f3 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -7,6 +7,7 @@ license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust.git" description = "The Rust Standard Library" edition = "2021" +autobenches = false [lib] crate-type = ["dylib", "rlib"] @@ -17,7 +18,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core", public = true } -compiler_builtins = { version = "=0.1.140" } +compiler_builtins = { version = "=0.1.146" } unwind = { path = "../unwind" } hashbrown = { version = "0.15", default-features = false, features = [ 'rustc-dep-of-std', @@ -30,8 +31,8 @@ std_detect = { path = "../stdarch/crates/std_detect", default-features = false, rustc-demangle = { version = "0.1.24", features = ['rustc-dep-of-std'] } [target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies] -miniz_oxide = { version = "0.7.0", optional = true, default-features = false } -addr2line = { version = "0.22.0", optional = true, default-features = false } +miniz_oxide = { version = "0.8.0", optional = true, default-features = false } +addr2line = { version = "0.24.0", optional = true, default-features = false } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] libc = { version = "0.2.169", default-features = false, features = [ @@ -60,8 +61,8 @@ object = { version = "0.36.0", default-features = false, optional = true, featur path = "../windows_targets" [dev-dependencies] -rand = { version = "0.8.5", default-features = false, features = ["alloc"] } -rand_xorshift = "0.3.0" +rand = { version = "0.9.0", default-features = false, features = ["alloc"] } +rand_xorshift = "0.4.0" [target.'cfg(any(all(target_family = "wasm", target_os = "unknown"), target_os = "xous", all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies] dlmalloc = { version = "0.2.4", features = ['rustc-dep-of-std'] } @@ -130,6 +131,18 @@ name = "pipe-subprocess" path = "tests/pipe_subprocess.rs" harness = false +[[test]] +name = "sync" +path = "tests/sync/lib.rs" + +[[test]] +name = "floats" +path = "tests/floats/lib.rs" + +[[test]] +name = "thread_local" +path = "tests/thread_local/lib.rs" + [[bench]] name = "stdbenches" path = "benches/lib.rs" @@ -139,7 +152,8 @@ test = true level = "warn" check-cfg = [ 'cfg(bootstrap)', - 'cfg(target_arch, values("xtensa"))', + 'cfg(target_arch, values("xtensa", "aarch64-unknown-nto-qnx710_iosock", "x86_64-pc-nto-qnx710_iosock", "x86_64-pc-nto-qnx800","aarch64-unknown-nto-qnx800"))', + 'cfg(target_env, values("nto71_iosock", "nto80"))', # std use #[path] imports to portable-simd `std_float` crate # and to the `backtrace` crate which messes-up with Cargo list # of declared features, we therefor expect any feature cfg diff --git a/library/std/benches/lib.rs b/library/std/benches/lib.rs index 1b21c230a0bf2..e749d9c0f7998 100644 --- a/library/std/benches/lib.rs +++ b/library/std/benches/lib.rs @@ -5,3 +5,5 @@ extern crate test; mod hash; +mod path; +mod time; diff --git a/library/std/benches/path.rs b/library/std/benches/path.rs new file mode 100644 index 0000000000000..094c00894a8ee --- /dev/null +++ b/library/std/benches/path.rs @@ -0,0 +1,114 @@ +use core::hint::black_box; +use std::collections::{BTreeSet, HashSet}; +use std::hash::{DefaultHasher, Hash, Hasher}; +use std::path::*; + +#[bench] +#[cfg_attr(miri, ignore)] // Miri isn't fast... +fn bench_path_cmp_fast_path_buf_sort(b: &mut test::Bencher) { + let prefix = "my/home"; + let mut paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); + + paths.sort(); + + b.iter(|| { + black_box(paths.as_mut_slice()).sort_unstable(); + }); +} + +#[bench] +#[cfg_attr(miri, ignore)] // Miri isn't fast... +fn bench_path_cmp_fast_path_long(b: &mut test::Bencher) { + let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; + let paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); + + let mut set = BTreeSet::new(); + + paths.iter().for_each(|p| { + set.insert(p.as_path()); + }); + + b.iter(|| { + set.remove(paths[500].as_path()); + set.insert(paths[500].as_path()); + }); +} + +#[bench] +#[cfg_attr(miri, ignore)] // Miri isn't fast... +fn bench_path_cmp_fast_path_short(b: &mut test::Bencher) { + let prefix = "my/home"; + let paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); + + let mut set = BTreeSet::new(); + + paths.iter().for_each(|p| { + set.insert(p.as_path()); + }); + + b.iter(|| { + set.remove(paths[500].as_path()); + set.insert(paths[500].as_path()); + }); +} + +#[bench] +#[cfg_attr(miri, ignore)] // Miri isn't fast... +fn bench_path_hashset(b: &mut test::Bencher) { + let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; + let paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); + + let mut set = HashSet::new(); + + paths.iter().for_each(|p| { + set.insert(p.as_path()); + }); + + b.iter(|| { + set.remove(paths[500].as_path()); + set.insert(black_box(paths[500].as_path())) + }); +} + +#[bench] +#[cfg_attr(miri, ignore)] // Miri isn't fast... +fn bench_path_hashset_miss(b: &mut test::Bencher) { + let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; + let paths: Vec<_> = + (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); + + let mut set = HashSet::new(); + + paths.iter().for_each(|p| { + set.insert(p.as_path()); + }); + + let probe = PathBuf::from(prefix).join("other"); + + b.iter(|| set.remove(black_box(probe.as_path()))); +} + +#[bench] +fn bench_hash_path_short(b: &mut test::Bencher) { + let mut hasher = DefaultHasher::new(); + let path = Path::new("explorer.exe"); + + b.iter(|| black_box(path).hash(&mut hasher)); + + black_box(hasher.finish()); +} + +#[bench] +fn bench_hash_path_long(b: &mut test::Bencher) { + let mut hasher = DefaultHasher::new(); + let path = + Path::new("/aaaaa/aaaaaa/./../aaaaaaaa/bbbbbbbbbbbbb/ccccccccccc/ddddddddd/eeeeeee.fff"); + + b.iter(|| black_box(path).hash(&mut hasher)); + + black_box(hasher.finish()); +} diff --git a/library/std/benches/time.rs b/library/std/benches/time.rs new file mode 100644 index 0000000000000..dfd886738f984 --- /dev/null +++ b/library/std/benches/time.rs @@ -0,0 +1,46 @@ +#[cfg(not(target_arch = "wasm32"))] +use test::{Bencher, black_box}; + +macro_rules! bench_instant_threaded { + ($bench_name:ident, $thread_count:expr) => { + #[bench] + #[cfg(not(target_arch = "wasm32"))] + fn $bench_name(b: &mut Bencher) -> std::thread::Result<()> { + use std::sync::Arc; + use std::sync::atomic::{AtomicBool, Ordering}; + use std::time::Instant; + + let running = Arc::new(AtomicBool::new(true)); + + let threads: Vec<_> = (0..$thread_count) + .map(|_| { + let flag = Arc::clone(&running); + std::thread::spawn(move || { + while flag.load(Ordering::Relaxed) { + black_box(Instant::now()); + } + }) + }) + .collect(); + + b.iter(|| { + let a = Instant::now(); + let b = Instant::now(); + assert!(b >= a); + }); + + running.store(false, Ordering::Relaxed); + + for t in threads { + t.join()?; + } + Ok(()) + } + }; +} + +bench_instant_threaded!(instant_contention_01_threads, 0); +bench_instant_threaded!(instant_contention_02_threads, 1); +bench_instant_threaded!(instant_contention_04_threads, 3); +bench_instant_threaded!(instant_contention_08_threads, 7); +bench_instant_threaded!(instant_contention_16_threads, 15); diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index 5d51d6a0c78a8..3936ed057e6e2 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -345,7 +345,7 @@ pub fn take_alloc_error_hook() -> fn(Layout) { } fn default_alloc_error_hook(layout: Layout) { - extern "Rust" { + unsafe extern "Rust" { // This symbol is emitted by rustc next to __rust_alloc_error_handler. // Its value depends on the -Zoom={panic,abort} compiler option. static __rust_alloc_error_handler_should_panic: u8; diff --git a/library/std/src/bstr.rs b/library/std/src/bstr.rs new file mode 100644 index 0000000000000..dd49177162833 --- /dev/null +++ b/library/std/src/bstr.rs @@ -0,0 +1,4 @@ +//! The `ByteStr` and `ByteString` types and trait implementations. + +#[unstable(feature = "bstr", issue = "134915")] +pub use alloc::bstr::{ByteStr, ByteString}; diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 56d734ba2fbfb..93d254e34f5eb 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -282,7 +282,7 @@ impl HashMap { /// manually using this function can expose a DoS attack vector. /// /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the HashMap to be useful, see its documentation for details. + /// the `HashMap` to be useful, see its documentation for details. /// /// # Examples /// @@ -296,10 +296,7 @@ impl HashMap { /// ``` #[inline] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - #[rustc_const_stable( - feature = "const_collections_with_hasher", - since = "CURRENT_RUSTC_VERSION" - )] + #[rustc_const_stable(feature = "const_collections_with_hasher", since = "1.85.0")] pub const fn with_hasher(hash_builder: S) -> HashMap { HashMap { base: base::HashMap::with_hasher(hash_builder) } } @@ -317,7 +314,7 @@ impl HashMap { /// manually using this function can expose a DoS attack vector. /// /// The `hasher` passed should implement the [`BuildHasher`] trait for - /// the HashMap to be useful, see its documentation for details. + /// the `HashMap` to be useful, see its documentation for details. /// /// # Examples /// @@ -972,7 +969,6 @@ where /// # Examples /// /// ``` - /// #![feature(map_many_mut)] /// use std::collections::HashMap; /// /// let mut libraries = HashMap::new(); @@ -982,13 +978,13 @@ where /// libraries.insert("Library of Congress".to_string(), 1800); /// /// // Get Athenæum and Bodleian Library - /// let [Some(a), Some(b)] = libraries.get_many_mut([ + /// let [Some(a), Some(b)] = libraries.get_disjoint_mut([ /// "Athenæum", /// "Bodleian Library", /// ]) else { panic!() }; /// /// // Assert values of Athenæum and Library of Congress - /// let got = libraries.get_many_mut([ + /// let got = libraries.get_disjoint_mut([ /// "Athenæum", /// "Library of Congress", /// ]); @@ -1001,7 +997,7 @@ where /// ); /// /// // Missing keys result in None - /// let got = libraries.get_many_mut([ + /// let got = libraries.get_disjoint_mut([ /// "Athenæum", /// "New York Public Library", /// ]); @@ -1015,21 +1011,24 @@ where /// ``` /// /// ```should_panic - /// #![feature(map_many_mut)] /// use std::collections::HashMap; /// /// let mut libraries = HashMap::new(); /// libraries.insert("Athenæum".to_string(), 1807); /// /// // Duplicate keys panic! - /// let got = libraries.get_many_mut([ + /// let got = libraries.get_disjoint_mut([ /// "Athenæum", /// "Athenæum", /// ]); /// ``` #[inline] - #[unstable(feature = "map_many_mut", issue = "97601")] - pub fn get_many_mut(&mut self, ks: [&Q; N]) -> [Option<&'_ mut V>; N] + #[doc(alias = "get_many_mut")] + #[stable(feature = "map_many_mut", since = "1.86.0")] + pub fn get_disjoint_mut( + &mut self, + ks: [&Q; N], + ) -> [Option<&'_ mut V>; N] where K: Borrow, Q: Hash + Eq, @@ -1043,7 +1042,7 @@ where /// Returns an array of length `N` with the results of each query. `None` will be used if /// the key is missing. /// - /// For a safe alternative see [`get_many_mut`](`HashMap::get_many_mut`). + /// For a safe alternative see [`get_disjoint_mut`](`HashMap::get_disjoint_mut`). /// /// # Safety /// @@ -1055,7 +1054,6 @@ where /// # Examples /// /// ``` - /// #![feature(map_many_mut)] /// use std::collections::HashMap; /// /// let mut libraries = HashMap::new(); @@ -1065,13 +1063,13 @@ where /// libraries.insert("Library of Congress".to_string(), 1800); /// /// // SAFETY: The keys do not overlap. - /// let [Some(a), Some(b)] = (unsafe { libraries.get_many_unchecked_mut([ + /// let [Some(a), Some(b)] = (unsafe { libraries.get_disjoint_unchecked_mut([ /// "Athenæum", /// "Bodleian Library", /// ]) }) else { panic!() }; /// /// // SAFETY: The keys do not overlap. - /// let got = unsafe { libraries.get_many_unchecked_mut([ + /// let got = unsafe { libraries.get_disjoint_unchecked_mut([ /// "Athenæum", /// "Library of Congress", /// ]) }; @@ -1084,7 +1082,7 @@ where /// ); /// /// // SAFETY: The keys do not overlap. - /// let got = unsafe { libraries.get_many_unchecked_mut([ + /// let got = unsafe { libraries.get_disjoint_unchecked_mut([ /// "Athenæum", /// "New York Public Library", /// ]) }; @@ -1092,8 +1090,9 @@ where /// assert_eq!(got, [Some(&mut 1807), None]); /// ``` #[inline] - #[unstable(feature = "map_many_mut", issue = "97601")] - pub unsafe fn get_many_unchecked_mut( + #[doc(alias = "get_many_unchecked_mut")] + #[stable(feature = "map_many_mut", since = "1.86.0")] + pub unsafe fn get_disjoint_unchecked_mut( &mut self, ks: [&Q; N], ) -> [Option<&'_ mut V>; N] @@ -1284,7 +1283,7 @@ impl HashMap where S: BuildHasher, { - /// Creates a raw entry builder for the HashMap. + /// Creates a raw entry builder for the `HashMap`. /// /// Raw entries provide the lowest level of control for searching and /// manipulating a map. They must be manually initialized with a hash and @@ -1299,13 +1298,13 @@ where /// * Using custom comparison logic without newtype wrappers /// /// Because raw entries provide much more low-level control, it's much easier - /// to put the HashMap into an inconsistent state which, while memory-safe, + /// to put the `HashMap` into an inconsistent state which, while memory-safe, /// will cause the map to produce seemingly random results. Higher-level and /// more foolproof APIs like `entry` should be preferred when possible. /// /// In particular, the hash used to initialize the raw entry must still be /// consistent with the hash of the key that is ultimately stored in the entry. - /// This is because implementations of HashMap may need to recompute hashes + /// This is because implementations of `HashMap` may need to recompute hashes /// when resizing, at which point only the keys are available. /// /// Raw entries give mutable access to the keys. This must not be used @@ -1321,7 +1320,7 @@ where RawEntryBuilderMut { map: self } } - /// Creates a raw immutable entry builder for the HashMap. + /// Creates a raw immutable entry builder for the `HashMap`. /// /// Raw entries provide the lowest level of control for searching and /// manipulating a map. They must be manually initialized with a hash and diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index bcf6d6b4ccf3a..c265d42d06a9f 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -374,7 +374,7 @@ impl HashSet { /// manually using this function can expose a DoS attack vector. /// /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the HashMap to be useful, see its documentation for details. + /// the `HashSet` to be useful, see its documentation for details. /// /// # Examples /// @@ -388,10 +388,7 @@ impl HashSet { /// ``` #[inline] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - #[rustc_const_stable( - feature = "const_collections_with_hasher", - since = "CURRENT_RUSTC_VERSION" - )] + #[rustc_const_stable(feature = "const_collections_with_hasher", since = "1.85.0")] pub const fn with_hasher(hasher: S) -> HashSet { HashSet { base: base::HashSet::with_hasher(hasher) } } @@ -409,7 +406,7 @@ impl HashSet { /// manually using this function can expose a DoS attack vector. /// /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the HashMap to be useful, see its documentation for details. + /// the `HashSet` to be useful, see its documentation for details. /// /// # Examples /// diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 535236ff89a37..adbd68896241c 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -10,9 +10,6 @@ #![stable(feature = "env", since = "1.0.0")] -#[cfg(test)] -mod tests; - use crate::error::Error; use crate::ffi::{OsStr, OsString}; use crate::path::{Path, PathBuf}; @@ -336,7 +333,10 @@ impl Error for VarError { /// - [Austin Group Bugzilla](https://austingroupbugs.net/view.php?id=188) /// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2) /// +/// To pass an environment variable to a child process, you can instead use [`Command::env`]. +/// /// [`std::net::ToSocketAddrs`]: crate::net::ToSocketAddrs +/// [`Command::env`]: crate::process::Command::env /// /// # Panics /// @@ -396,7 +396,12 @@ pub unsafe fn set_var, V: AsRef>(key: K, value: V) { /// - [Austin Group Bugzilla](https://austingroupbugs.net/view.php?id=188) /// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2) /// +/// To prevent a child process from inheriting an environment variable, you can +/// instead use [`Command::env_remove`] or [`Command::env_clear`]. +/// /// [`std::net::ToSocketAddrs`]: crate::net::ToSocketAddrs +/// [`Command::env_remove`]: crate::process::Command::env_remove +/// [`Command::env_clear`]: crate::process::Command::env_clear /// /// # Panics /// @@ -563,7 +568,7 @@ pub struct JoinPathsError { /// let mut paths = env::split_paths(&path).collect::>(); /// paths.push(PathBuf::from("/home/xyz/bin")); /// let new_path = env::join_paths(paths)?; -/// env::set_var("PATH", &new_path); +/// unsafe { env::set_var("PATH", &new_path); } /// } /// /// Ok(()) @@ -622,7 +627,7 @@ impl Error for JoinPathsError { /// /// In UWP (Universal Windows Platform) targets this function is unimplemented and always returns `None`. /// -/// Before Rust CURRENT_RUSTC_VERSION, this function used to return the value of the 'HOME' environment variable +/// Before Rust 1.85.0, this function used to return the value of the 'HOME' environment variable /// on Windows, which in Cygwin or Mingw environments could return non-standard paths like `/home/you` /// instead of `C:\Users\you`. /// diff --git a/library/std/src/env/tests.rs b/library/std/src/env/tests.rs deleted file mode 100644 index d021726106872..0000000000000 --- a/library/std/src/env/tests.rs +++ /dev/null @@ -1,120 +0,0 @@ -use super::*; - -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi", target_env = "sgx"), ignore)] -fn test_self_exe_path() { - let path = current_exe(); - assert!(path.is_ok()); - let path = path.unwrap(); - - // Hard to test this function - assert!(path.is_absolute()); -} - -#[test] -fn test() { - assert!((!Path::new("test-path").is_absolute())); - - #[cfg(not(target_env = "sgx"))] - current_dir().unwrap(); -} - -#[test] -#[cfg(windows)] -fn split_paths_windows() { - use crate::path::PathBuf; - - fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { - split_paths(unparsed).collect::>() - == parsed.iter().map(|s| PathBuf::from(*s)).collect::>() - } - - assert!(check_parse("", &mut [""])); - assert!(check_parse(r#""""#, &mut [""])); - assert!(check_parse(";;", &mut ["", "", ""])); - assert!(check_parse(r"c:\", &mut [r"c:\"])); - assert!(check_parse(r"c:\;", &mut [r"c:\", ""])); - assert!(check_parse(r"c:\;c:\Program Files\", &mut [r"c:\", r"c:\Program Files\"])); - assert!(check_parse(r#"c:\;c:\"foo"\"#, &mut [r"c:\", r"c:\foo\"])); - assert!(check_parse(r#"c:\;c:\"foo;bar"\;c:\baz"#, &mut [r"c:\", r"c:\foo;bar\", r"c:\baz"])); -} - -#[test] -#[cfg(unix)] -fn split_paths_unix() { - use crate::path::PathBuf; - - fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { - split_paths(unparsed).collect::>() - == parsed.iter().map(|s| PathBuf::from(*s)).collect::>() - } - - assert!(check_parse("", &mut [""])); - assert!(check_parse("::", &mut ["", "", ""])); - assert!(check_parse("/", &mut ["/"])); - assert!(check_parse("/:", &mut ["/", ""])); - assert!(check_parse("/:/usr/local", &mut ["/", "/usr/local"])); -} - -#[test] -#[cfg(unix)] -fn join_paths_unix() { - use crate::ffi::OsStr; - - fn test_eq(input: &[&str], output: &str) -> bool { - &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output) - } - - assert!(test_eq(&[], "")); - assert!(test_eq(&["/bin", "/usr/bin", "/usr/local/bin"], "/bin:/usr/bin:/usr/local/bin")); - assert!(test_eq(&["", "/bin", "", "", "/usr/bin", ""], ":/bin:::/usr/bin:")); - assert!(join_paths(["/te:st"].iter().cloned()).is_err()); -} - -#[test] -#[cfg(windows)] -fn join_paths_windows() { - use crate::ffi::OsStr; - - fn test_eq(input: &[&str], output: &str) -> bool { - &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output) - } - - assert!(test_eq(&[], "")); - assert!(test_eq(&[r"c:\windows", r"c:\"], r"c:\windows;c:\")); - assert!(test_eq(&["", r"c:\windows", "", "", r"c:\", ""], r";c:\windows;;;c:\;")); - assert!(test_eq(&[r"c:\te;st", r"c:\"], r#""c:\te;st";c:\"#)); - assert!(join_paths([r#"c:\te"st"#].iter().cloned()).is_err()); -} - -#[test] -fn args_debug() { - assert_eq!( - format!("Args {{ inner: {:?} }}", args().collect::>()), - format!("{:?}", args()) - ); -} - -#[test] -fn args_os_debug() { - assert_eq!( - format!("ArgsOs {{ inner: {:?} }}", args_os().collect::>()), - format!("{:?}", args_os()) - ); -} - -#[test] -fn vars_debug() { - assert_eq!( - format!("Vars {{ inner: {:?} }}", vars().collect::>()), - format!("{:?}", vars()) - ); -} - -#[test] -fn vars_os_debug() { - assert_eq!( - format!("VarsOs {{ inner: {:?} }}", vars_os().collect::>()), - format!("{:?}", vars_os()) - ); -} diff --git a/library/std/src/error.rs b/library/std/src/error.rs index b3e63aaf1c567..def5f984c88e4 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -1,9 +1,6 @@ #![doc = include_str!("../../core/src/error.md")] #![stable(feature = "rust1", since = "1.0.0")] -#[cfg(test)] -mod tests; - #[stable(feature = "rust1", since = "1.0.0")] pub use core::error::Error; #[unstable(feature = "error_generic_member_access", issue = "99301")] diff --git a/library/std/src/error/tests.rs b/library/std/src/error/tests.rs deleted file mode 100644 index 88a9f33c07908..0000000000000 --- a/library/std/src/error/tests.rs +++ /dev/null @@ -1,444 +0,0 @@ -use core::error::Request; - -use super::Error; -use crate::fmt; - -#[derive(Debug, PartialEq)] -struct A; -#[derive(Debug, PartialEq)] -struct B; - -impl fmt::Display for A { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "A") - } -} -impl fmt::Display for B { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "B") - } -} - -impl Error for A {} -impl Error for B {} - -#[test] -fn downcasting() { - let mut a = A; - let a = &mut a as &mut (dyn Error + 'static); - assert_eq!(a.downcast_ref::(), Some(&A)); - assert_eq!(a.downcast_ref::(), None); - assert_eq!(a.downcast_mut::(), Some(&mut A)); - assert_eq!(a.downcast_mut::(), None); - - let a: Box = Box::new(A); - match a.downcast::() { - Ok(..) => panic!("expected error"), - Err(e) => assert_eq!(*e.downcast::().unwrap(), A), - } -} - -use crate::backtrace::Backtrace; -use crate::error::Report; - -#[derive(Debug)] -struct SuperError { - source: SuperErrorSideKick, -} - -impl fmt::Display for SuperError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "SuperError is here!") - } -} - -impl Error for SuperError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - Some(&self.source) - } -} - -#[derive(Debug)] -struct SuperErrorSideKick; - -impl fmt::Display for SuperErrorSideKick { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "SuperErrorSideKick is here!") - } -} - -impl Error for SuperErrorSideKick {} - -#[test] -fn single_line_formatting() { - let error = SuperError { source: SuperErrorSideKick }; - let report = Report::new(&error); - let actual = report.to_string(); - let expected = String::from("SuperError is here!: SuperErrorSideKick is here!"); - - assert_eq!(expected, actual); -} - -#[test] -fn multi_line_formatting() { - let error = SuperError { source: SuperErrorSideKick }; - let report = Report::new(&error).pretty(true); - let actual = report.to_string(); - let expected = String::from( - "\ -SuperError is here! - -Caused by: - SuperErrorSideKick is here!", - ); - - assert_eq!(expected, actual); -} - -#[test] -fn error_with_no_sources_formats_single_line_correctly() { - let report = Report::new(SuperErrorSideKick); - let actual = report.to_string(); - let expected = String::from("SuperErrorSideKick is here!"); - - assert_eq!(expected, actual); -} - -#[test] -fn error_with_no_sources_formats_multi_line_correctly() { - let report = Report::new(SuperErrorSideKick).pretty(true); - let actual = report.to_string(); - let expected = String::from("SuperErrorSideKick is here!"); - - assert_eq!(expected, actual); -} - -#[test] -fn error_with_backtrace_outputs_correctly_with_one_source() { - let trace = Backtrace::force_capture(); - let expected = format!( - "\ -The source of the error - -Caused by: - Error with backtrace - -Stack backtrace: -{}", - trace - ); - let error = GenericError::new("Error with backtrace"); - let mut error = GenericError::new_with_source("The source of the error", error); - error.backtrace = Some(trace); - let report = Report::new(error).pretty(true).show_backtrace(true); - - println!("Error: {report}"); - assert_eq!(expected.trim_end(), report.to_string()); -} - -#[test] -fn error_with_backtrace_outputs_correctly_with_two_sources() { - let trace = Backtrace::force_capture(); - let expected = format!( - "\ -Error with two sources - -Caused by: - 0: The source of the error - 1: Error with backtrace - -Stack backtrace: -{}", - trace - ); - let mut error = GenericError::new("Error with backtrace"); - error.backtrace = Some(trace); - let error = GenericError::new_with_source("The source of the error", error); - let error = GenericError::new_with_source("Error with two sources", error); - let report = Report::new(error).pretty(true).show_backtrace(true); - - println!("Error: {report}"); - assert_eq!(expected.trim_end(), report.to_string()); -} - -#[derive(Debug)] -struct GenericError { - message: D, - backtrace: Option, - source: Option>, -} - -impl GenericError { - fn new(message: D) -> GenericError { - Self { message, backtrace: None, source: None } - } - - fn new_with_source(message: D, source: E) -> GenericError - where - E: Error + 'static, - { - let source: Box = Box::new(source); - let source = Some(source); - GenericError { message, backtrace: None, source } - } -} - -impl fmt::Display for GenericError -where - D: fmt::Display, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.message, f) - } -} - -impl Error for GenericError -where - D: fmt::Debug + fmt::Display, -{ - fn source(&self) -> Option<&(dyn Error + 'static)> { - self.source.as_deref() - } - - fn provide<'a>(&'a self, req: &mut Request<'a>) { - self.backtrace.as_ref().map(|bt| req.provide_ref::(bt)); - } -} - -#[test] -fn error_formats_single_line_with_rude_display_impl() { - #[derive(Debug)] - struct MyMessage; - - impl fmt::Display for MyMessage { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("line 1\nline 2")?; - f.write_str("\nline 3\nline 4\n")?; - f.write_str("line 5\nline 6")?; - Ok(()) - } - } - - let error = GenericError::new(MyMessage); - let error = GenericError::new_with_source(MyMessage, error); - let error = GenericError::new_with_source(MyMessage, error); - let error = GenericError::new_with_source(MyMessage, error); - let report = Report::new(error); - let expected = "\ -line 1 -line 2 -line 3 -line 4 -line 5 -line 6: line 1 -line 2 -line 3 -line 4 -line 5 -line 6: line 1 -line 2 -line 3 -line 4 -line 5 -line 6: line 1 -line 2 -line 3 -line 4 -line 5 -line 6"; - - let actual = report.to_string(); - assert_eq!(expected, actual); -} - -#[test] -fn error_formats_multi_line_with_rude_display_impl() { - #[derive(Debug)] - struct MyMessage; - - impl fmt::Display for MyMessage { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("line 1\nline 2")?; - f.write_str("\nline 3\nline 4\n")?; - f.write_str("line 5\nline 6")?; - Ok(()) - } - } - - let error = GenericError::new(MyMessage); - let error = GenericError::new_with_source(MyMessage, error); - let error = GenericError::new_with_source(MyMessage, error); - let error = GenericError::new_with_source(MyMessage, error); - let report = Report::new(error).pretty(true); - let expected = "line 1 -line 2 -line 3 -line 4 -line 5 -line 6 - -Caused by: - 0: line 1 - line 2 - line 3 - line 4 - line 5 - line 6 - 1: line 1 - line 2 - line 3 - line 4 - line 5 - line 6 - 2: line 1 - line 2 - line 3 - line 4 - line 5 - line 6"; - - let actual = report.to_string(); - assert_eq!(expected, actual); -} - -#[test] -fn errors_that_start_with_newline_formats_correctly() { - #[derive(Debug)] - struct MyMessage; - - impl fmt::Display for MyMessage { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("\nThe message\n") - } - } - - let error = GenericError::new(MyMessage); - let error = GenericError::new_with_source(MyMessage, error); - let error = GenericError::new_with_source(MyMessage, error); - let report = Report::new(error).pretty(true); - let expected = " -The message - - -Caused by: - 0: \ -\n The message - \ -\n 1: \ -\n The message - "; - - let actual = report.to_string(); - assert_eq!(expected, actual); -} - -#[test] -fn errors_with_multiple_writes_on_same_line_dont_insert_erroneous_newlines() { - #[derive(Debug)] - struct MyMessage; - - impl fmt::Display for MyMessage { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("The message")?; - f.write_str(" goes on")?; - f.write_str(" and on.") - } - } - - let error = GenericError::new(MyMessage); - let error = GenericError::new_with_source(MyMessage, error); - let error = GenericError::new_with_source(MyMessage, error); - let report = Report::new(error).pretty(true); - let expected = "\ -The message goes on and on. - -Caused by: - 0: The message goes on and on. - 1: The message goes on and on."; - - let actual = report.to_string(); - println!("{actual}"); - assert_eq!(expected, actual); -} - -#[test] -fn errors_with_string_interpolation_formats_correctly() { - #[derive(Debug)] - struct MyMessage(usize); - - impl fmt::Display for MyMessage { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Got an error code: ({}). ", self.0)?; - write!(f, "What would you like to do in response?") - } - } - - let error = GenericError::new(MyMessage(10)); - let error = GenericError::new_with_source(MyMessage(20), error); - let report = Report::new(error).pretty(true); - let expected = "\ -Got an error code: (20). What would you like to do in response? - -Caused by: - Got an error code: (10). What would you like to do in response?"; - let actual = report.to_string(); - assert_eq!(expected, actual); -} - -#[test] -fn empty_lines_mid_message() { - #[derive(Debug)] - struct MyMessage; - - impl fmt::Display for MyMessage { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("line 1\n\nline 2") - } - } - - let error = GenericError::new(MyMessage); - let error = GenericError::new_with_source(MyMessage, error); - let error = GenericError::new_with_source(MyMessage, error); - let report = Report::new(error).pretty(true); - let expected = "\ -line 1 - -line 2 - -Caused by: - 0: line 1 - \ -\n line 2 - 1: line 1 - \ -\n line 2"; - - let actual = report.to_string(); - assert_eq!(expected, actual); -} - -#[test] -fn only_one_source() { - #[derive(Debug)] - struct MyMessage; - - impl fmt::Display for MyMessage { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("line 1\nline 2") - } - } - - let error = GenericError::new(MyMessage); - let error = GenericError::new_with_source(MyMessage, error); - let report = Report::new(error).pretty(true); - let expected = "\ -line 1 -line 2 - -Caused by: - line 1 - line 2"; - - let actual = report.to_string(); - assert_eq!(expected, actual); -} diff --git a/library/std/src/f128.rs b/library/std/src/f128.rs index e93e915159e40..14ce2b69ca427 100644 --- a/library/std/src/f128.rs +++ b/library/std/src/f128.rs @@ -1,12 +1,9 @@ -//! Constants for the `f128` double-precision floating point type. +//! Constants for the `f128` quadruple-precision floating point type. //! //! *[See also the `f128` primitive type](primitive@f128).* //! //! Mathematically significant numbers are provided in the `consts` sub-module. -#[cfg(test)] -mod tests; - #[unstable(feature = "f128", issue = "116909")] pub use core::f128::consts; @@ -227,6 +224,7 @@ impl f128 { /// ``` #[inline] #[rustc_allow_incoherent_impl] + #[doc(alias = "fmaf128", alias = "fusedMultiplyAdd")] #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn mul_add(self, a: f128, b: f128) -> f128 { @@ -323,6 +321,20 @@ impl f128 { /// /// The precision of this function is non-deterministic. This means it varies by platform, /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0_f128; + /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f128::EPSILON); + /// + /// assert_eq!(f128::powi(f128::NAN, 0), 1.0); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] @@ -346,8 +358,10 @@ impl f128 { /// /// let x = 2.0_f128; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// /// assert!(abs_difference <= f128::EPSILON); + /// + /// assert_eq!(f128::powf(1.0, f128::NAN), 1.0); + /// assert_eq!(f128::powf(f128::NAN, 0.0), 1.0); /// # } /// ``` #[inline] @@ -384,6 +398,7 @@ impl f128 { /// # } /// ``` #[inline] + #[doc(alias = "squareRoot")] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] @@ -1211,6 +1226,7 @@ impl f128 { #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "float_gamma", issue = "99842")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn gamma(self) -> f128 { unsafe { cmath::tgammaf128(self) } @@ -1245,10 +1261,83 @@ impl f128 { #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "float_gamma", issue = "99842")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn ln_gamma(self) -> (f128, i32) { let mut signgamp: i32 = 0; let x = unsafe { cmath::lgammaf128_r(self, &mut signgamp) }; (x, signgamp) } + + /// Error function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `erff128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_erf)] + /// # #[cfg(reliable_f128_math)] { + /// /// The error function relates what percent of a normal distribution lies + /// /// within `x` standard deviations (scaled by `1/sqrt(2)`). + /// fn within_standard_deviations(x: f128) -> f128 { + /// (x * std::f128::consts::FRAC_1_SQRT_2).erf() * 100.0 + /// } + /// + /// // 68% of a normal distribution is within one standard deviation + /// assert!((within_standard_deviations(1.0) - 68.269).abs() < 0.01); + /// // 95% of a normal distribution is within two standard deviations + /// assert!((within_standard_deviations(2.0) - 95.450).abs() < 0.01); + /// // 99.7% of a normal distribution is within three standard deviations + /// assert!((within_standard_deviations(3.0) - 99.730).abs() < 0.01); + /// # } + /// ``` + #[rustc_allow_incoherent_impl] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "float_erf", issue = "136321")] + #[inline] + pub fn erf(self) -> f128 { + unsafe { cmath::erff128(self) } + } + + /// Complementary error function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `erfcf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_erf)] + /// # #[cfg(reliable_f128_math)] { + /// let x: f128 = 0.123; + /// + /// let one = x.erf() + x.erfc(); + /// let abs_difference = (one - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[rustc_allow_incoherent_impl] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "float_erf", issue = "136321")] + #[inline] + pub fn erfc(self) -> f128 { + unsafe { cmath::erfcf128(self) } + } } diff --git a/library/std/src/f128/tests.rs b/library/std/src/f128/tests.rs deleted file mode 100644 index cbcf9f96239bb..0000000000000 --- a/library/std/src/f128/tests.rs +++ /dev/null @@ -1,987 +0,0 @@ -// FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy -#![cfg(reliable_f128)] - -use crate::f128::consts; -use crate::num::FpCategory as Fp; -#[cfg(reliable_f128_math)] -use crate::ops::Rem; -use crate::ops::{Add, Div, Mul, Sub}; - -// Note these tolerances make sense around zero, but not for more extreme exponents. - -/// For operations that are near exact, usually not involving math of different -/// signs. -const TOL_PRECISE: f128 = 1e-28; - -/// Default tolerances. Works for values that should be near precise but not exact. Roughly -/// the precision carried by `100 * 100`. -const TOL: f128 = 1e-12; - -/// Tolerances for math that is allowed to be imprecise, usually due to multiple chained -/// operations. -#[cfg(reliable_f128_math)] -const TOL_IMPR: f128 = 1e-10; - -/// Smallest number -const TINY_BITS: u128 = 0x1; - -/// Next smallest number -const TINY_UP_BITS: u128 = 0x2; - -/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 -const MAX_DOWN_BITS: u128 = 0x7ffefffffffffffffffffffffffffffe; - -/// Zeroed exponent, full significant -const LARGEST_SUBNORMAL_BITS: u128 = 0x0000ffffffffffffffffffffffffffff; - -/// Exponent = 0b1, zeroed significand -const SMALLEST_NORMAL_BITS: u128 = 0x00010000000000000000000000000000; - -/// First pattern over the mantissa -const NAN_MASK1: u128 = 0x0000aaaaaaaaaaaaaaaaaaaaaaaaaaaa; - -/// Second pattern over the mantissa -const NAN_MASK2: u128 = 0x00005555555555555555555555555555; - -/// Compare by representation -#[allow(unused_macros)] -macro_rules! assert_f128_biteq { - ($a:expr, $b:expr) => { - let (l, r): (&f128, &f128) = (&$a, &$b); - let lb = l.to_bits(); - let rb = r.to_bits(); - assert_eq!(lb, rb, "float {l:?} is not bitequal to {r:?}.\na: {lb:#034x}\nb: {rb:#034x}"); - }; -} - -#[test] -fn test_num_f128() { - // FIXME(f16_f128): replace with a `test_num` call once the required `fmodl`/`fmodf128` - // function is available on all platforms. - let ten = 10f128; - let two = 2f128; - assert_eq!(ten.add(two), ten + two); - assert_eq!(ten.sub(two), ten - two); - assert_eq!(ten.mul(two), ten * two); - assert_eq!(ten.div(two), ten / two); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_num_f128_rem() { - let ten = 10f128; - let two = 2f128; - assert_eq!(ten.rem(two), ten % two); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_min_nan() { - assert_eq!(f128::NAN.min(2.0), 2.0); - assert_eq!(2.0f128.min(f128::NAN), 2.0); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_max_nan() { - assert_eq!(f128::NAN.max(2.0), 2.0); - assert_eq!(2.0f128.max(f128::NAN), 2.0); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_minimum() { - assert!(f128::NAN.minimum(2.0).is_nan()); - assert!(2.0f128.minimum(f128::NAN).is_nan()); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_maximum() { - assert!(f128::NAN.maximum(2.0).is_nan()); - assert!(2.0f128.maximum(f128::NAN).is_nan()); -} - -#[test] -fn test_nan() { - let nan: f128 = f128::NAN; - assert!(nan.is_nan()); - assert!(!nan.is_infinite()); - assert!(!nan.is_finite()); - assert!(nan.is_sign_positive()); - assert!(!nan.is_sign_negative()); - assert!(!nan.is_normal()); - assert_eq!(Fp::Nan, nan.classify()); -} - -#[test] -fn test_infinity() { - let inf: f128 = f128::INFINITY; - assert!(inf.is_infinite()); - assert!(!inf.is_finite()); - assert!(inf.is_sign_positive()); - assert!(!inf.is_sign_negative()); - assert!(!inf.is_nan()); - assert!(!inf.is_normal()); - assert_eq!(Fp::Infinite, inf.classify()); -} - -#[test] -fn test_neg_infinity() { - let neg_inf: f128 = f128::NEG_INFINITY; - assert!(neg_inf.is_infinite()); - assert!(!neg_inf.is_finite()); - assert!(!neg_inf.is_sign_positive()); - assert!(neg_inf.is_sign_negative()); - assert!(!neg_inf.is_nan()); - assert!(!neg_inf.is_normal()); - assert_eq!(Fp::Infinite, neg_inf.classify()); -} - -#[test] -fn test_zero() { - let zero: f128 = 0.0f128; - assert_eq!(0.0, zero); - assert!(!zero.is_infinite()); - assert!(zero.is_finite()); - assert!(zero.is_sign_positive()); - assert!(!zero.is_sign_negative()); - assert!(!zero.is_nan()); - assert!(!zero.is_normal()); - assert_eq!(Fp::Zero, zero.classify()); -} - -#[test] -fn test_neg_zero() { - let neg_zero: f128 = -0.0; - assert_eq!(0.0, neg_zero); - assert!(!neg_zero.is_infinite()); - assert!(neg_zero.is_finite()); - assert!(!neg_zero.is_sign_positive()); - assert!(neg_zero.is_sign_negative()); - assert!(!neg_zero.is_nan()); - assert!(!neg_zero.is_normal()); - assert_eq!(Fp::Zero, neg_zero.classify()); -} - -#[test] -fn test_one() { - let one: f128 = 1.0f128; - assert_eq!(1.0, one); - assert!(!one.is_infinite()); - assert!(one.is_finite()); - assert!(one.is_sign_positive()); - assert!(!one.is_sign_negative()); - assert!(!one.is_nan()); - assert!(one.is_normal()); - assert_eq!(Fp::Normal, one.classify()); -} - -#[test] -fn test_is_nan() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert!(nan.is_nan()); - assert!(!0.0f128.is_nan()); - assert!(!5.3f128.is_nan()); - assert!(!(-10.732f128).is_nan()); - assert!(!inf.is_nan()); - assert!(!neg_inf.is_nan()); -} - -#[test] -fn test_is_infinite() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert!(!nan.is_infinite()); - assert!(inf.is_infinite()); - assert!(neg_inf.is_infinite()); - assert!(!0.0f128.is_infinite()); - assert!(!42.8f128.is_infinite()); - assert!(!(-109.2f128).is_infinite()); -} - -#[test] -fn test_is_finite() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert!(!nan.is_finite()); - assert!(!inf.is_finite()); - assert!(!neg_inf.is_finite()); - assert!(0.0f128.is_finite()); - assert!(42.8f128.is_finite()); - assert!((-109.2f128).is_finite()); -} - -#[test] -fn test_is_normal() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let zero: f128 = 0.0f128; - let neg_zero: f128 = -0.0; - assert!(!nan.is_normal()); - assert!(!inf.is_normal()); - assert!(!neg_inf.is_normal()); - assert!(!zero.is_normal()); - assert!(!neg_zero.is_normal()); - assert!(1f128.is_normal()); - assert!(1e-4931f128.is_normal()); - assert!(!1e-4932f128.is_normal()); -} - -#[test] -fn test_classify() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let zero: f128 = 0.0f128; - let neg_zero: f128 = -0.0; - assert_eq!(nan.classify(), Fp::Nan); - assert_eq!(inf.classify(), Fp::Infinite); - assert_eq!(neg_inf.classify(), Fp::Infinite); - assert_eq!(zero.classify(), Fp::Zero); - assert_eq!(neg_zero.classify(), Fp::Zero); - assert_eq!(1f128.classify(), Fp::Normal); - assert_eq!(1e-4931f128.classify(), Fp::Normal); - assert_eq!(1e-4932f128.classify(), Fp::Subnormal); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_floor() { - assert_approx_eq!(1.0f128.floor(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.3f128.floor(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.5f128.floor(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.7f128.floor(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(0.0f128.floor(), 0.0f128, TOL_PRECISE); - assert_approx_eq!((-0.0f128).floor(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.0f128).floor(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.3f128).floor(), -2.0f128, TOL_PRECISE); - assert_approx_eq!((-1.5f128).floor(), -2.0f128, TOL_PRECISE); - assert_approx_eq!((-1.7f128).floor(), -2.0f128, TOL_PRECISE); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_ceil() { - assert_approx_eq!(1.0f128.ceil(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.3f128.ceil(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(1.5f128.ceil(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(1.7f128.ceil(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(0.0f128.ceil(), 0.0f128, TOL_PRECISE); - assert_approx_eq!((-0.0f128).ceil(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.0f128).ceil(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.3f128).ceil(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.5f128).ceil(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.7f128).ceil(), -1.0f128, TOL_PRECISE); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_round() { - assert_approx_eq!(2.5f128.round(), 3.0f128, TOL_PRECISE); - assert_approx_eq!(1.0f128.round(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.3f128.round(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.5f128.round(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(1.7f128.round(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(0.0f128.round(), 0.0f128, TOL_PRECISE); - assert_approx_eq!((-0.0f128).round(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.0f128).round(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.3f128).round(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.5f128).round(), -2.0f128, TOL_PRECISE); - assert_approx_eq!((-1.7f128).round(), -2.0f128, TOL_PRECISE); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_round_ties_even() { - assert_approx_eq!(2.5f128.round_ties_even(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(1.0f128.round_ties_even(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.3f128.round_ties_even(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.5f128.round_ties_even(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(1.7f128.round_ties_even(), 2.0f128, TOL_PRECISE); - assert_approx_eq!(0.0f128.round_ties_even(), 0.0f128, TOL_PRECISE); - assert_approx_eq!((-0.0f128).round_ties_even(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.0f128).round_ties_even(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.3f128).round_ties_even(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.5f128).round_ties_even(), -2.0f128, TOL_PRECISE); - assert_approx_eq!((-1.7f128).round_ties_even(), -2.0f128, TOL_PRECISE); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_trunc() { - assert_approx_eq!(1.0f128.trunc(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.3f128.trunc(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.5f128.trunc(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(1.7f128.trunc(), 1.0f128, TOL_PRECISE); - assert_approx_eq!(0.0f128.trunc(), 0.0f128, TOL_PRECISE); - assert_approx_eq!((-0.0f128).trunc(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.0f128).trunc(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.3f128).trunc(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.5f128).trunc(), -1.0f128, TOL_PRECISE); - assert_approx_eq!((-1.7f128).trunc(), -1.0f128, TOL_PRECISE); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_fract() { - assert_approx_eq!(1.0f128.fract(), 0.0f128, TOL_PRECISE); - assert_approx_eq!(1.3f128.fract(), 0.3f128, TOL_PRECISE); - assert_approx_eq!(1.5f128.fract(), 0.5f128, TOL_PRECISE); - assert_approx_eq!(1.7f128.fract(), 0.7f128, TOL_PRECISE); - assert_approx_eq!(0.0f128.fract(), 0.0f128, TOL_PRECISE); - assert_approx_eq!((-0.0f128).fract(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.0f128).fract(), -0.0f128, TOL_PRECISE); - assert_approx_eq!((-1.3f128).fract(), -0.3f128, TOL_PRECISE); - assert_approx_eq!((-1.5f128).fract(), -0.5f128, TOL_PRECISE); - assert_approx_eq!((-1.7f128).fract(), -0.7f128, TOL_PRECISE); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_abs() { - assert_eq!(f128::INFINITY.abs(), f128::INFINITY); - assert_eq!(1f128.abs(), 1f128); - assert_eq!(0f128.abs(), 0f128); - assert_eq!((-0f128).abs(), 0f128); - assert_eq!((-1f128).abs(), 1f128); - assert_eq!(f128::NEG_INFINITY.abs(), f128::INFINITY); - assert_eq!((1f128 / f128::NEG_INFINITY).abs(), 0f128); - assert!(f128::NAN.abs().is_nan()); -} - -#[test] -fn test_is_sign_positive() { - assert!(f128::INFINITY.is_sign_positive()); - assert!(1f128.is_sign_positive()); - assert!(0f128.is_sign_positive()); - assert!(!(-0f128).is_sign_positive()); - assert!(!(-1f128).is_sign_positive()); - assert!(!f128::NEG_INFINITY.is_sign_positive()); - assert!(!(1f128 / f128::NEG_INFINITY).is_sign_positive()); - assert!(f128::NAN.is_sign_positive()); - assert!(!(-f128::NAN).is_sign_positive()); -} - -#[test] -fn test_is_sign_negative() { - assert!(!f128::INFINITY.is_sign_negative()); - assert!(!1f128.is_sign_negative()); - assert!(!0f128.is_sign_negative()); - assert!((-0f128).is_sign_negative()); - assert!((-1f128).is_sign_negative()); - assert!(f128::NEG_INFINITY.is_sign_negative()); - assert!((1f128 / f128::NEG_INFINITY).is_sign_negative()); - assert!(!f128::NAN.is_sign_negative()); - assert!((-f128::NAN).is_sign_negative()); -} - -#[test] -fn test_next_up() { - let tiny = f128::from_bits(TINY_BITS); - let tiny_up = f128::from_bits(TINY_UP_BITS); - let max_down = f128::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f128::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f128::from_bits(SMALLEST_NORMAL_BITS); - assert_f128_biteq!(f128::NEG_INFINITY.next_up(), f128::MIN); - assert_f128_biteq!(f128::MIN.next_up(), -max_down); - assert_f128_biteq!((-1.0 - f128::EPSILON).next_up(), -1.0); - assert_f128_biteq!((-smallest_normal).next_up(), -largest_subnormal); - assert_f128_biteq!((-tiny_up).next_up(), -tiny); - assert_f128_biteq!((-tiny).next_up(), -0.0f128); - assert_f128_biteq!((-0.0f128).next_up(), tiny); - assert_f128_biteq!(0.0f128.next_up(), tiny); - assert_f128_biteq!(tiny.next_up(), tiny_up); - assert_f128_biteq!(largest_subnormal.next_up(), smallest_normal); - assert_f128_biteq!(1.0f128.next_up(), 1.0 + f128::EPSILON); - assert_f128_biteq!(f128::MAX.next_up(), f128::INFINITY); - assert_f128_biteq!(f128::INFINITY.next_up(), f128::INFINITY); - - // Check that NaNs roundtrip. - let nan0 = f128::NAN; - let nan1 = f128::from_bits(f128::NAN.to_bits() ^ 0x002a_aaaa); - let nan2 = f128::from_bits(f128::NAN.to_bits() ^ 0x0055_5555); - assert_f128_biteq!(nan0.next_up(), nan0); - assert_f128_biteq!(nan1.next_up(), nan1); - assert_f128_biteq!(nan2.next_up(), nan2); -} - -#[test] -fn test_next_down() { - let tiny = f128::from_bits(TINY_BITS); - let tiny_up = f128::from_bits(TINY_UP_BITS); - let max_down = f128::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f128::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f128::from_bits(SMALLEST_NORMAL_BITS); - assert_f128_biteq!(f128::NEG_INFINITY.next_down(), f128::NEG_INFINITY); - assert_f128_biteq!(f128::MIN.next_down(), f128::NEG_INFINITY); - assert_f128_biteq!((-max_down).next_down(), f128::MIN); - assert_f128_biteq!((-1.0f128).next_down(), -1.0 - f128::EPSILON); - assert_f128_biteq!((-largest_subnormal).next_down(), -smallest_normal); - assert_f128_biteq!((-tiny).next_down(), -tiny_up); - assert_f128_biteq!((-0.0f128).next_down(), -tiny); - assert_f128_biteq!((0.0f128).next_down(), -tiny); - assert_f128_biteq!(tiny.next_down(), 0.0f128); - assert_f128_biteq!(tiny_up.next_down(), tiny); - assert_f128_biteq!(smallest_normal.next_down(), largest_subnormal); - assert_f128_biteq!((1.0 + f128::EPSILON).next_down(), 1.0f128); - assert_f128_biteq!(f128::MAX.next_down(), max_down); - assert_f128_biteq!(f128::INFINITY.next_down(), f128::MAX); - - // Check that NaNs roundtrip. - let nan0 = f128::NAN; - let nan1 = f128::from_bits(f128::NAN.to_bits() ^ 0x002a_aaaa); - let nan2 = f128::from_bits(f128::NAN.to_bits() ^ 0x0055_5555); - assert_f128_biteq!(nan0.next_down(), nan0); - assert_f128_biteq!(nan1.next_down(), nan1); - assert_f128_biteq!(nan2.next_down(), nan2); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_mul_add() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_approx_eq!(12.3f128.mul_add(4.5, 6.7), 62.05, TOL_PRECISE); - assert_approx_eq!((-12.3f128).mul_add(-4.5, -6.7), 48.65, TOL_PRECISE); - assert_approx_eq!(0.0f128.mul_add(8.9, 1.2), 1.2, TOL_PRECISE); - assert_approx_eq!(3.4f128.mul_add(-0.0, 5.6), 5.6, TOL_PRECISE); - assert!(nan.mul_add(7.8, 9.0).is_nan()); - assert_eq!(inf.mul_add(7.8, 9.0), inf); - assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); - assert_eq!(8.9f128.mul_add(inf, 3.2), inf); - assert_eq!((-3.2f128).mul_add(2.4, neg_inf), neg_inf); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_recip() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(1.0f128.recip(), 1.0); - assert_eq!(2.0f128.recip(), 0.5); - assert_eq!((-0.4f128).recip(), -2.5); - assert_eq!(0.0f128.recip(), inf); - assert_approx_eq!( - f128::MAX.recip(), - 8.40525785778023376565669454330438228902076605e-4933, - 1e-4900 - ); - assert!(nan.recip().is_nan()); - assert_eq!(inf.recip(), 0.0); - assert_eq!(neg_inf.recip(), 0.0); -} - -// Many math functions allow for less accurate results, so the next tolerance up is used - -#[test] -#[cfg(reliable_f128_math)] -fn test_powi() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(1.0f128.powi(1), 1.0); - assert_approx_eq!((-3.1f128).powi(2), 9.6100000000000005506706202140776519387, TOL); - assert_approx_eq!(5.9f128.powi(-2), 0.028727377190462507313100483690639638451, TOL); - assert_eq!(8.3f128.powi(0), 1.0); - assert!(nan.powi(2).is_nan()); - assert_eq!(inf.powi(3), inf); - assert_eq!(neg_inf.powi(2), inf); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_powf() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(1.0f128.powf(1.0), 1.0); - assert_approx_eq!(3.4f128.powf(4.5), 246.40818323761892815995637964326426756, TOL_IMPR); - assert_approx_eq!(2.7f128.powf(-3.2), 0.041652009108526178281070304373500889273, TOL_IMPR); - assert_approx_eq!((-3.1f128).powf(2.0), 9.6100000000000005506706202140776519387, TOL_IMPR); - assert_approx_eq!(5.9f128.powf(-2.0), 0.028727377190462507313100483690639638451, TOL_IMPR); - assert_eq!(8.3f128.powf(0.0), 1.0); - assert!(nan.powf(2.0).is_nan()); - assert_eq!(inf.powf(2.0), inf); - assert_eq!(neg_inf.powf(3.0), neg_inf); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_sqrt_domain() { - assert!(f128::NAN.sqrt().is_nan()); - assert!(f128::NEG_INFINITY.sqrt().is_nan()); - assert!((-1.0f128).sqrt().is_nan()); - assert_eq!((-0.0f128).sqrt(), -0.0); - assert_eq!(0.0f128.sqrt(), 0.0); - assert_eq!(1.0f128.sqrt(), 1.0); - assert_eq!(f128::INFINITY.sqrt(), f128::INFINITY); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_exp() { - assert_eq!(1.0, 0.0f128.exp()); - assert_approx_eq!(consts::E, 1.0f128.exp(), TOL); - assert_approx_eq!(148.41315910257660342111558004055227962348775, 5.0f128.exp(), TOL); - - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let nan: f128 = f128::NAN; - assert_eq!(inf, inf.exp()); - assert_eq!(0.0, neg_inf.exp()); - assert!(nan.exp().is_nan()); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_exp2() { - assert_eq!(32.0, 5.0f128.exp2()); - assert_eq!(1.0, 0.0f128.exp2()); - - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let nan: f128 = f128::NAN; - assert_eq!(inf, inf.exp2()); - assert_eq!(0.0, neg_inf.exp2()); - assert!(nan.exp2().is_nan()); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_ln() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_approx_eq!(1.0f128.exp().ln(), 1.0, TOL); - assert!(nan.ln().is_nan()); - assert_eq!(inf.ln(), inf); - assert!(neg_inf.ln().is_nan()); - assert!((-2.3f128).ln().is_nan()); - assert_eq!((-0.0f128).ln(), neg_inf); - assert_eq!(0.0f128.ln(), neg_inf); - assert_approx_eq!(4.0f128.ln(), 1.3862943611198906188344642429163531366, TOL); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_log() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(10.0f128.log(10.0), 1.0); - assert_approx_eq!(2.3f128.log(3.5), 0.66485771361478710036766645911922010272, TOL); - assert_eq!(1.0f128.exp().log(1.0f128.exp()), 1.0); - assert!(1.0f128.log(1.0).is_nan()); - assert!(1.0f128.log(-13.9).is_nan()); - assert!(nan.log(2.3).is_nan()); - assert_eq!(inf.log(10.0), inf); - assert!(neg_inf.log(8.8).is_nan()); - assert!((-2.3f128).log(0.1).is_nan()); - assert_eq!((-0.0f128).log(2.0), neg_inf); - assert_eq!(0.0f128.log(7.0), neg_inf); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_log2() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_approx_eq!(10.0f128.log2(), 3.32192809488736234787031942948939017, TOL); - assert_approx_eq!(2.3f128.log2(), 1.2016338611696504130002982471978765921, TOL); - assert_approx_eq!(1.0f128.exp().log2(), 1.4426950408889634073599246810018921381, TOL); - assert!(nan.log2().is_nan()); - assert_eq!(inf.log2(), inf); - assert!(neg_inf.log2().is_nan()); - assert!((-2.3f128).log2().is_nan()); - assert_eq!((-0.0f128).log2(), neg_inf); - assert_eq!(0.0f128.log2(), neg_inf); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_log10() { - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(10.0f128.log10(), 1.0); - assert_approx_eq!(2.3f128.log10(), 0.36172783601759284532595218865859309898, TOL); - assert_approx_eq!(1.0f128.exp().log10(), 0.43429448190325182765112891891660508222, TOL); - assert_eq!(1.0f128.log10(), 0.0); - assert!(nan.log10().is_nan()); - assert_eq!(inf.log10(), inf); - assert!(neg_inf.log10().is_nan()); - assert!((-2.3f128).log10().is_nan()); - assert_eq!((-0.0f128).log10(), neg_inf); - assert_eq!(0.0f128.log10(), neg_inf); -} - -#[test] -fn test_to_degrees() { - let pi: f128 = consts::PI; - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(0.0f128.to_degrees(), 0.0); - assert_approx_eq!((-5.8f128).to_degrees(), -332.31552117587745090765431723855668471, TOL); - assert_approx_eq!(pi.to_degrees(), 180.0, TOL); - assert!(nan.to_degrees().is_nan()); - assert_eq!(inf.to_degrees(), inf); - assert_eq!(neg_inf.to_degrees(), neg_inf); - assert_eq!(1_f128.to_degrees(), 57.2957795130823208767981548141051703); -} - -#[test] -fn test_to_radians() { - let pi: f128 = consts::PI; - let nan: f128 = f128::NAN; - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - assert_eq!(0.0f128.to_radians(), 0.0); - assert_approx_eq!(154.6f128.to_radians(), 2.6982790235832334267135442069489767804, TOL); - assert_approx_eq!((-332.31f128).to_radians(), -5.7999036373023566567593094812182763013, TOL); - // check approx rather than exact because round trip for pi doesn't fall on an exactly - // representable value (unlike `f32` and `f64`). - assert_approx_eq!(180.0f128.to_radians(), pi, TOL_PRECISE); - assert!(nan.to_radians().is_nan()); - assert_eq!(inf.to_radians(), inf); - assert_eq!(neg_inf.to_radians(), neg_inf); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_asinh() { - // Lower accuracy results are allowed, use increased tolerances - assert_eq!(0.0f128.asinh(), 0.0f128); - assert_eq!((-0.0f128).asinh(), -0.0f128); - - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let nan: f128 = f128::NAN; - assert_eq!(inf.asinh(), inf); - assert_eq!(neg_inf.asinh(), neg_inf); - assert!(nan.asinh().is_nan()); - assert!((-0.0f128).asinh().is_sign_negative()); - - // issue 63271 - assert_approx_eq!(2.0f128.asinh(), 1.443635475178810342493276740273105f128, TOL_IMPR); - assert_approx_eq!((-2.0f128).asinh(), -1.443635475178810342493276740273105f128, TOL_IMPR); - // regression test for the catastrophic cancellation fixed in 72486 - assert_approx_eq!( - (-67452098.07139316f128).asinh(), - -18.720075426274544393985484294000831757220, - TOL_IMPR - ); - - // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f128, 60.0f128.sinh().asinh(), TOL_IMPR); - // mul needed for approximate comparison to be meaningful - assert_approx_eq!(1.0f128, 1e-15f128.sinh().asinh() * 1e15f128, TOL_IMPR); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_acosh() { - assert_eq!(1.0f128.acosh(), 0.0f128); - assert!(0.999f128.acosh().is_nan()); - - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let nan: f128 = f128::NAN; - assert_eq!(inf.acosh(), inf); - assert!(neg_inf.acosh().is_nan()); - assert!(nan.acosh().is_nan()); - assert_approx_eq!(2.0f128.acosh(), 1.31695789692481670862504634730796844f128, TOL_IMPR); - assert_approx_eq!(3.0f128.acosh(), 1.76274717403908605046521864995958461f128, TOL_IMPR); - - // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f128, 60.0f128.cosh().acosh(), TOL_IMPR); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_atanh() { - assert_eq!(0.0f128.atanh(), 0.0f128); - assert_eq!((-0.0f128).atanh(), -0.0f128); - - let inf: f128 = f128::INFINITY; - let neg_inf: f128 = f128::NEG_INFINITY; - let nan: f128 = f128::NAN; - assert_eq!(1.0f128.atanh(), inf); - assert_eq!((-1.0f128).atanh(), neg_inf); - assert!(2f128.atanh().atanh().is_nan()); - assert!((-2f128).atanh().atanh().is_nan()); - assert!(inf.atanh().is_nan()); - assert!(neg_inf.atanh().is_nan()); - assert!(nan.atanh().is_nan()); - assert_approx_eq!(0.5f128.atanh(), 0.54930614433405484569762261846126285f128, TOL_IMPR); - assert_approx_eq!((-0.5f128).atanh(), -0.54930614433405484569762261846126285f128, TOL_IMPR); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_gamma() { - // precision can differ among platforms - assert_approx_eq!(1.0f128.gamma(), 1.0f128, TOL_IMPR); - assert_approx_eq!(2.0f128.gamma(), 1.0f128, TOL_IMPR); - assert_approx_eq!(3.0f128.gamma(), 2.0f128, TOL_IMPR); - assert_approx_eq!(4.0f128.gamma(), 6.0f128, TOL_IMPR); - assert_approx_eq!(5.0f128.gamma(), 24.0f128, TOL_IMPR); - assert_approx_eq!(0.5f128.gamma(), consts::PI.sqrt(), TOL_IMPR); - assert_approx_eq!((-0.5f128).gamma(), -2.0 * consts::PI.sqrt(), TOL_IMPR); - assert_eq!(0.0f128.gamma(), f128::INFINITY); - assert_eq!((-0.0f128).gamma(), f128::NEG_INFINITY); - assert!((-1.0f128).gamma().is_nan()); - assert!((-2.0f128).gamma().is_nan()); - assert!(f128::NAN.gamma().is_nan()); - assert!(f128::NEG_INFINITY.gamma().is_nan()); - assert_eq!(f128::INFINITY.gamma(), f128::INFINITY); - assert_eq!(1760.9f128.gamma(), f128::INFINITY); -} - -#[test] -#[cfg(reliable_f128_math)] -fn test_ln_gamma() { - assert_approx_eq!(1.0f128.ln_gamma().0, 0.0f128, TOL_IMPR); - assert_eq!(1.0f128.ln_gamma().1, 1); - assert_approx_eq!(2.0f128.ln_gamma().0, 0.0f128, TOL_IMPR); - assert_eq!(2.0f128.ln_gamma().1, 1); - assert_approx_eq!(3.0f128.ln_gamma().0, 2.0f128.ln(), TOL_IMPR); - assert_eq!(3.0f128.ln_gamma().1, 1); - assert_approx_eq!((-0.5f128).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), TOL_IMPR); - assert_eq!((-0.5f128).ln_gamma().1, -1); -} - -#[test] -fn test_real_consts() { - use super::consts; - - let pi: f128 = consts::PI; - let frac_pi_2: f128 = consts::FRAC_PI_2; - let frac_pi_3: f128 = consts::FRAC_PI_3; - let frac_pi_4: f128 = consts::FRAC_PI_4; - let frac_pi_6: f128 = consts::FRAC_PI_6; - let frac_pi_8: f128 = consts::FRAC_PI_8; - let frac_1_pi: f128 = consts::FRAC_1_PI; - let frac_2_pi: f128 = consts::FRAC_2_PI; - - assert_approx_eq!(frac_pi_2, pi / 2f128, TOL_PRECISE); - assert_approx_eq!(frac_pi_3, pi / 3f128, TOL_PRECISE); - assert_approx_eq!(frac_pi_4, pi / 4f128, TOL_PRECISE); - assert_approx_eq!(frac_pi_6, pi / 6f128, TOL_PRECISE); - assert_approx_eq!(frac_pi_8, pi / 8f128, TOL_PRECISE); - assert_approx_eq!(frac_1_pi, 1f128 / pi, TOL_PRECISE); - assert_approx_eq!(frac_2_pi, 2f128 / pi, TOL_PRECISE); - - #[cfg(reliable_f128_math)] - { - let frac_2_sqrtpi: f128 = consts::FRAC_2_SQRT_PI; - let sqrt2: f128 = consts::SQRT_2; - let frac_1_sqrt2: f128 = consts::FRAC_1_SQRT_2; - let e: f128 = consts::E; - let log2_e: f128 = consts::LOG2_E; - let log10_e: f128 = consts::LOG10_E; - let ln_2: f128 = consts::LN_2; - let ln_10: f128 = consts::LN_10; - - assert_approx_eq!(frac_2_sqrtpi, 2f128 / pi.sqrt(), TOL_PRECISE); - assert_approx_eq!(sqrt2, 2f128.sqrt(), TOL_PRECISE); - assert_approx_eq!(frac_1_sqrt2, 1f128 / 2f128.sqrt(), TOL_PRECISE); - assert_approx_eq!(log2_e, e.log2(), TOL_PRECISE); - assert_approx_eq!(log10_e, e.log10(), TOL_PRECISE); - assert_approx_eq!(ln_2, 2f128.ln(), TOL_PRECISE); - assert_approx_eq!(ln_10, 10f128.ln(), TOL_PRECISE); - } -} - -#[test] -fn test_float_bits_conv() { - assert_eq!((1f128).to_bits(), 0x3fff0000000000000000000000000000); - assert_eq!((12.5f128).to_bits(), 0x40029000000000000000000000000000); - assert_eq!((1337f128).to_bits(), 0x40094e40000000000000000000000000); - assert_eq!((-14.25f128).to_bits(), 0xc002c800000000000000000000000000); - assert_approx_eq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0, TOL_PRECISE); - assert_approx_eq!(f128::from_bits(0x40029000000000000000000000000000), 12.5, TOL_PRECISE); - assert_approx_eq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0, TOL_PRECISE); - assert_approx_eq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25, TOL_PRECISE); - - // Check that NaNs roundtrip their bits regardless of signaling-ness - // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits - let masked_nan1 = f128::NAN.to_bits() ^ NAN_MASK1; - let masked_nan2 = f128::NAN.to_bits() ^ NAN_MASK2; - assert!(f128::from_bits(masked_nan1).is_nan()); - assert!(f128::from_bits(masked_nan2).is_nan()); - - assert_eq!(f128::from_bits(masked_nan1).to_bits(), masked_nan1); - assert_eq!(f128::from_bits(masked_nan2).to_bits(), masked_nan2); -} - -#[test] -#[should_panic] -fn test_clamp_min_greater_than_max() { - let _ = 1.0f128.clamp(3.0, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_min_is_nan() { - let _ = 1.0f128.clamp(f128::NAN, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_max_is_nan() { - let _ = 1.0f128.clamp(3.0, f128::NAN); -} - -#[test] -fn test_total_cmp() { - use core::cmp::Ordering; - - fn quiet_bit_mask() -> u128 { - 1 << (f128::MANTISSA_DIGITS - 2) - } - - // FIXME(f16_f128): test subnormals when powf is available - // fn min_subnorm() -> f128 { - // f128::MIN_POSITIVE / f128::powf(2.0, f128::MANTISSA_DIGITS as f128 - 1.0) - // } - - // fn max_subnorm() -> f128 { - // f128::MIN_POSITIVE - min_subnorm() - // } - - fn q_nan() -> f128 { - f128::from_bits(f128::NAN.to_bits() | quiet_bit_mask()) - } - - fn s_nan() -> f128 { - f128::from_bits((f128::NAN.to_bits() & !quiet_bit_mask()) + 42) - } - - assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Equal, (-f128::INFINITY).total_cmp(&-f128::INFINITY)); - assert_eq!(Ordering::Equal, (-f128::MAX).total_cmp(&-f128::MAX)); - assert_eq!(Ordering::Equal, (-2.5_f128).total_cmp(&-2.5)); - assert_eq!(Ordering::Equal, (-1.0_f128).total_cmp(&-1.0)); - assert_eq!(Ordering::Equal, (-1.5_f128).total_cmp(&-1.5)); - assert_eq!(Ordering::Equal, (-0.5_f128).total_cmp(&-0.5)); - assert_eq!(Ordering::Equal, (-f128::MIN_POSITIVE).total_cmp(&-f128::MIN_POSITIVE)); - // assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Equal, (-0.0_f128).total_cmp(&-0.0)); - assert_eq!(Ordering::Equal, 0.0_f128.total_cmp(&0.0)); - // assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Equal, f128::MIN_POSITIVE.total_cmp(&f128::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, 0.5_f128.total_cmp(&0.5)); - assert_eq!(Ordering::Equal, 1.0_f128.total_cmp(&1.0)); - assert_eq!(Ordering::Equal, 1.5_f128.total_cmp(&1.5)); - assert_eq!(Ordering::Equal, 2.5_f128.total_cmp(&2.5)); - assert_eq!(Ordering::Equal, f128::MAX.total_cmp(&f128::MAX)); - assert_eq!(Ordering::Equal, f128::INFINITY.total_cmp(&f128::INFINITY)); - assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); - assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::INFINITY)); - assert_eq!(Ordering::Less, (-f128::INFINITY).total_cmp(&-f128::MAX)); - assert_eq!(Ordering::Less, (-f128::MAX).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-2.5_f128).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-1.5_f128).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-1.0_f128).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-0.5_f128).total_cmp(&-f128::MIN_POSITIVE)); - // assert_eq!(Ordering::Less, (-f128::MIN_POSITIVE).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); - // assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-0.0_f128).total_cmp(&0.0)); - // assert_eq!(Ordering::Less, 0.0_f128.total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); - // assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f128::MIN_POSITIVE)); - assert_eq!(Ordering::Less, f128::MIN_POSITIVE.total_cmp(&0.5)); - assert_eq!(Ordering::Less, 0.5_f128.total_cmp(&1.0)); - assert_eq!(Ordering::Less, 1.0_f128.total_cmp(&1.5)); - assert_eq!(Ordering::Less, 1.5_f128.total_cmp(&2.5)); - assert_eq!(Ordering::Less, 2.5_f128.total_cmp(&f128::MAX)); - assert_eq!(Ordering::Less, f128::MAX.total_cmp(&f128::INFINITY)); - assert_eq!(Ordering::Less, f128::INFINITY.total_cmp(&s_nan())); - assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Greater, (-f128::INFINITY).total_cmp(&-s_nan())); - assert_eq!(Ordering::Greater, (-f128::MAX).total_cmp(&-f128::INFINITY)); - assert_eq!(Ordering::Greater, (-2.5_f128).total_cmp(&-f128::MAX)); - assert_eq!(Ordering::Greater, (-1.5_f128).total_cmp(&-2.5)); - assert_eq!(Ordering::Greater, (-1.0_f128).total_cmp(&-1.5)); - assert_eq!(Ordering::Greater, (-0.5_f128).total_cmp(&-1.0)); - assert_eq!(Ordering::Greater, (-f128::MIN_POSITIVE).total_cmp(&-0.5)); - // assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f128::MIN_POSITIVE)); - // assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Greater, (-0.0_f128).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Greater, 0.0_f128.total_cmp(&-0.0)); - // assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); - // assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Greater, f128::MIN_POSITIVE.total_cmp(&max_subnorm())); - assert_eq!(Ordering::Greater, 0.5_f128.total_cmp(&f128::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, 1.0_f128.total_cmp(&0.5)); - assert_eq!(Ordering::Greater, 1.5_f128.total_cmp(&1.0)); - assert_eq!(Ordering::Greater, 2.5_f128.total_cmp(&1.5)); - assert_eq!(Ordering::Greater, f128::MAX.total_cmp(&2.5)); - assert_eq!(Ordering::Greater, f128::INFINITY.total_cmp(&f128::MAX)); - assert_eq!(Ordering::Greater, s_nan().total_cmp(&f128::INFINITY)); - assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::MIN_POSITIVE)); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::MIN_POSITIVE)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); -} diff --git a/library/std/src/f16.rs b/library/std/src/f16.rs index 5b7fcaa28e064..bdbe3e2199466 100644 --- a/library/std/src/f16.rs +++ b/library/std/src/f16.rs @@ -1,12 +1,9 @@ -//! Constants for the `f16` double-precision floating point type. +//! Constants for the `f16` half-precision floating point type. //! //! *[See also the `f16` primitive type](primitive@f16).* //! //! Mathematically significant numbers are provided in the `consts` sub-module. -#[cfg(test)] -mod tests; - #[unstable(feature = "f16", issue = "116909")] pub use core::f16::consts; @@ -228,6 +225,7 @@ impl f16 { #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] + #[doc(alias = "fmaf16", alias = "fusedMultiplyAdd")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn mul_add(self, a: f16, b: f16) -> f16 { unsafe { intrinsics::fmaf16(self, a, b) } @@ -323,6 +321,20 @@ impl f16 { /// /// The precision of this function is non-deterministic. This means it varies by platform, /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0_f16; + /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f16::EPSILON); + /// + /// assert_eq!(f16::powi(f16::NAN, 0), 1.0); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] @@ -346,8 +358,10 @@ impl f16 { /// /// let x = 2.0_f16; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// /// assert!(abs_difference <= f16::EPSILON); + /// + /// assert_eq!(f16::powf(1.0, f16::NAN), 1.0); + /// assert_eq!(f16::powf(f16::NAN, 0.0), 1.0); /// # } /// ``` #[inline] @@ -384,6 +398,7 @@ impl f16 { /// # } /// ``` #[inline] + #[doc(alias = "squareRoot")] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] @@ -1209,6 +1224,7 @@ impl f16 { #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "float_gamma", issue = "99842")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn gamma(self) -> f16 { (unsafe { cmath::tgammaf(self as f32) }) as f16 @@ -1243,10 +1259,81 @@ impl f16 { #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "float_gamma", issue = "99842")] #[must_use = "method returns a new number and does not mutate the original value"] pub fn ln_gamma(self) -> (f16, i32) { let mut signgamp: i32 = 0; let x = (unsafe { cmath::lgammaf_r(self as f32, &mut signgamp) }) as f16; (x, signgamp) } + + /// Error function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `erff` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_erf)] + /// # #[cfg(reliable_f16_math)] { + /// /// The error function relates what percent of a normal distribution lies + /// /// within `x` standard deviations (scaled by `1/sqrt(2)`). + /// fn within_standard_deviations(x: f16) -> f16 { + /// (x * std::f16::consts::FRAC_1_SQRT_2).erf() * 100.0 + /// } + /// + /// // 68% of a normal distribution is within one standard deviation + /// assert!((within_standard_deviations(1.0) - 68.269).abs() < 0.1); + /// // 95% of a normal distribution is within two standard deviations + /// assert!((within_standard_deviations(2.0) - 95.450).abs() < 0.1); + /// // 99.7% of a normal distribution is within three standard deviations + /// assert!((within_standard_deviations(3.0) - 99.730).abs() < 0.1); + /// # } + /// ``` + #[rustc_allow_incoherent_impl] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "float_erf", issue = "136321")] + #[inline] + pub fn erf(self) -> f16 { + (unsafe { cmath::erff(self as f32) }) as f16 + } + + /// Complementary error function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `erfcf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_erf)] + /// let x: f16 = 0.123; + /// + /// let one = x.erf() + x.erfc(); + /// let abs_difference = (one - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// ``` + #[rustc_allow_incoherent_impl] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "float_erf", issue = "136321")] + #[inline] + pub fn erfc(self) -> f16 { + (unsafe { cmath::erfcf(self as f32) }) as f16 + } } diff --git a/library/std/src/f16/tests.rs b/library/std/src/f16/tests.rs deleted file mode 100644 index 684ee3f3855b8..0000000000000 --- a/library/std/src/f16/tests.rs +++ /dev/null @@ -1,958 +0,0 @@ -// FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy -#![cfg(reliable_f16)] - -use crate::f16::consts; -use crate::num::{FpCategory as Fp, *}; - -/// Tolerance for results on the order of 10.0e-2 -#[allow(unused)] -const TOL_N2: f16 = 0.0001; - -/// Tolerance for results on the order of 10.0e+0 -#[allow(unused)] -const TOL_0: f16 = 0.01; - -/// Tolerance for results on the order of 10.0e+2 -#[allow(unused)] -const TOL_P2: f16 = 0.5; - -/// Tolerance for results on the order of 10.0e+4 -#[allow(unused)] -const TOL_P4: f16 = 10.0; - -/// Smallest number -const TINY_BITS: u16 = 0x1; - -/// Next smallest number -const TINY_UP_BITS: u16 = 0x2; - -/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 -const MAX_DOWN_BITS: u16 = 0x7bfe; - -/// Zeroed exponent, full significant -const LARGEST_SUBNORMAL_BITS: u16 = 0x03ff; - -/// Exponent = 0b1, zeroed significand -const SMALLEST_NORMAL_BITS: u16 = 0x0400; - -/// First pattern over the mantissa -const NAN_MASK1: u16 = 0x02aa; - -/// Second pattern over the mantissa -const NAN_MASK2: u16 = 0x0155; - -/// Compare by representation -#[allow(unused_macros)] -macro_rules! assert_f16_biteq { - ($a:expr, $b:expr) => { - let (l, r): (&f16, &f16) = (&$a, &$b); - let lb = l.to_bits(); - let rb = r.to_bits(); - assert_eq!(lb, rb, "float {l:?} ({lb:#04x}) is not bitequal to {r:?} ({rb:#04x})"); - }; -} - -#[test] -fn test_num_f16() { - test_num(10f16, 2f16); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_min_nan() { - assert_eq!(f16::NAN.min(2.0), 2.0); - assert_eq!(2.0f16.min(f16::NAN), 2.0); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_max_nan() { - assert_eq!(f16::NAN.max(2.0), 2.0); - assert_eq!(2.0f16.max(f16::NAN), 2.0); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_minimum() { - assert!(f16::NAN.minimum(2.0).is_nan()); - assert!(2.0f16.minimum(f16::NAN).is_nan()); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_maximum() { - assert!(f16::NAN.maximum(2.0).is_nan()); - assert!(2.0f16.maximum(f16::NAN).is_nan()); -} - -#[test] -fn test_nan() { - let nan: f16 = f16::NAN; - assert!(nan.is_nan()); - assert!(!nan.is_infinite()); - assert!(!nan.is_finite()); - assert!(nan.is_sign_positive()); - assert!(!nan.is_sign_negative()); - assert!(!nan.is_normal()); - assert_eq!(Fp::Nan, nan.classify()); -} - -#[test] -fn test_infinity() { - let inf: f16 = f16::INFINITY; - assert!(inf.is_infinite()); - assert!(!inf.is_finite()); - assert!(inf.is_sign_positive()); - assert!(!inf.is_sign_negative()); - assert!(!inf.is_nan()); - assert!(!inf.is_normal()); - assert_eq!(Fp::Infinite, inf.classify()); -} - -#[test] -fn test_neg_infinity() { - let neg_inf: f16 = f16::NEG_INFINITY; - assert!(neg_inf.is_infinite()); - assert!(!neg_inf.is_finite()); - assert!(!neg_inf.is_sign_positive()); - assert!(neg_inf.is_sign_negative()); - assert!(!neg_inf.is_nan()); - assert!(!neg_inf.is_normal()); - assert_eq!(Fp::Infinite, neg_inf.classify()); -} - -#[test] -fn test_zero() { - let zero: f16 = 0.0f16; - assert_eq!(0.0, zero); - assert!(!zero.is_infinite()); - assert!(zero.is_finite()); - assert!(zero.is_sign_positive()); - assert!(!zero.is_sign_negative()); - assert!(!zero.is_nan()); - assert!(!zero.is_normal()); - assert_eq!(Fp::Zero, zero.classify()); -} - -#[test] -fn test_neg_zero() { - let neg_zero: f16 = -0.0; - assert_eq!(0.0, neg_zero); - assert!(!neg_zero.is_infinite()); - assert!(neg_zero.is_finite()); - assert!(!neg_zero.is_sign_positive()); - assert!(neg_zero.is_sign_negative()); - assert!(!neg_zero.is_nan()); - assert!(!neg_zero.is_normal()); - assert_eq!(Fp::Zero, neg_zero.classify()); -} - -#[test] -fn test_one() { - let one: f16 = 1.0f16; - assert_eq!(1.0, one); - assert!(!one.is_infinite()); - assert!(one.is_finite()); - assert!(one.is_sign_positive()); - assert!(!one.is_sign_negative()); - assert!(!one.is_nan()); - assert!(one.is_normal()); - assert_eq!(Fp::Normal, one.classify()); -} - -#[test] -fn test_is_nan() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert!(nan.is_nan()); - assert!(!0.0f16.is_nan()); - assert!(!5.3f16.is_nan()); - assert!(!(-10.732f16).is_nan()); - assert!(!inf.is_nan()); - assert!(!neg_inf.is_nan()); -} - -#[test] -fn test_is_infinite() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert!(!nan.is_infinite()); - assert!(inf.is_infinite()); - assert!(neg_inf.is_infinite()); - assert!(!0.0f16.is_infinite()); - assert!(!42.8f16.is_infinite()); - assert!(!(-109.2f16).is_infinite()); -} - -#[test] -fn test_is_finite() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert!(!nan.is_finite()); - assert!(!inf.is_finite()); - assert!(!neg_inf.is_finite()); - assert!(0.0f16.is_finite()); - assert!(42.8f16.is_finite()); - assert!((-109.2f16).is_finite()); -} - -#[test] -fn test_is_normal() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let zero: f16 = 0.0f16; - let neg_zero: f16 = -0.0; - assert!(!nan.is_normal()); - assert!(!inf.is_normal()); - assert!(!neg_inf.is_normal()); - assert!(!zero.is_normal()); - assert!(!neg_zero.is_normal()); - assert!(1f16.is_normal()); - assert!(1e-4f16.is_normal()); - assert!(!1e-5f16.is_normal()); -} - -#[test] -fn test_classify() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let zero: f16 = 0.0f16; - let neg_zero: f16 = -0.0; - assert_eq!(nan.classify(), Fp::Nan); - assert_eq!(inf.classify(), Fp::Infinite); - assert_eq!(neg_inf.classify(), Fp::Infinite); - assert_eq!(zero.classify(), Fp::Zero); - assert_eq!(neg_zero.classify(), Fp::Zero); - assert_eq!(1f16.classify(), Fp::Normal); - assert_eq!(1e-4f16.classify(), Fp::Normal); - assert_eq!(1e-5f16.classify(), Fp::Subnormal); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_floor() { - assert_approx_eq!(1.0f16.floor(), 1.0f16, TOL_0); - assert_approx_eq!(1.3f16.floor(), 1.0f16, TOL_0); - assert_approx_eq!(1.5f16.floor(), 1.0f16, TOL_0); - assert_approx_eq!(1.7f16.floor(), 1.0f16, TOL_0); - assert_approx_eq!(0.0f16.floor(), 0.0f16, TOL_0); - assert_approx_eq!((-0.0f16).floor(), -0.0f16, TOL_0); - assert_approx_eq!((-1.0f16).floor(), -1.0f16, TOL_0); - assert_approx_eq!((-1.3f16).floor(), -2.0f16, TOL_0); - assert_approx_eq!((-1.5f16).floor(), -2.0f16, TOL_0); - assert_approx_eq!((-1.7f16).floor(), -2.0f16, TOL_0); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_ceil() { - assert_approx_eq!(1.0f16.ceil(), 1.0f16, TOL_0); - assert_approx_eq!(1.3f16.ceil(), 2.0f16, TOL_0); - assert_approx_eq!(1.5f16.ceil(), 2.0f16, TOL_0); - assert_approx_eq!(1.7f16.ceil(), 2.0f16, TOL_0); - assert_approx_eq!(0.0f16.ceil(), 0.0f16, TOL_0); - assert_approx_eq!((-0.0f16).ceil(), -0.0f16, TOL_0); - assert_approx_eq!((-1.0f16).ceil(), -1.0f16, TOL_0); - assert_approx_eq!((-1.3f16).ceil(), -1.0f16, TOL_0); - assert_approx_eq!((-1.5f16).ceil(), -1.0f16, TOL_0); - assert_approx_eq!((-1.7f16).ceil(), -1.0f16, TOL_0); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_round() { - assert_approx_eq!(2.5f16.round(), 3.0f16, TOL_0); - assert_approx_eq!(1.0f16.round(), 1.0f16, TOL_0); - assert_approx_eq!(1.3f16.round(), 1.0f16, TOL_0); - assert_approx_eq!(1.5f16.round(), 2.0f16, TOL_0); - assert_approx_eq!(1.7f16.round(), 2.0f16, TOL_0); - assert_approx_eq!(0.0f16.round(), 0.0f16, TOL_0); - assert_approx_eq!((-0.0f16).round(), -0.0f16, TOL_0); - assert_approx_eq!((-1.0f16).round(), -1.0f16, TOL_0); - assert_approx_eq!((-1.3f16).round(), -1.0f16, TOL_0); - assert_approx_eq!((-1.5f16).round(), -2.0f16, TOL_0); - assert_approx_eq!((-1.7f16).round(), -2.0f16, TOL_0); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_round_ties_even() { - assert_approx_eq!(2.5f16.round_ties_even(), 2.0f16, TOL_0); - assert_approx_eq!(1.0f16.round_ties_even(), 1.0f16, TOL_0); - assert_approx_eq!(1.3f16.round_ties_even(), 1.0f16, TOL_0); - assert_approx_eq!(1.5f16.round_ties_even(), 2.0f16, TOL_0); - assert_approx_eq!(1.7f16.round_ties_even(), 2.0f16, TOL_0); - assert_approx_eq!(0.0f16.round_ties_even(), 0.0f16, TOL_0); - assert_approx_eq!((-0.0f16).round_ties_even(), -0.0f16, TOL_0); - assert_approx_eq!((-1.0f16).round_ties_even(), -1.0f16, TOL_0); - assert_approx_eq!((-1.3f16).round_ties_even(), -1.0f16, TOL_0); - assert_approx_eq!((-1.5f16).round_ties_even(), -2.0f16, TOL_0); - assert_approx_eq!((-1.7f16).round_ties_even(), -2.0f16, TOL_0); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_trunc() { - assert_approx_eq!(1.0f16.trunc(), 1.0f16, TOL_0); - assert_approx_eq!(1.3f16.trunc(), 1.0f16, TOL_0); - assert_approx_eq!(1.5f16.trunc(), 1.0f16, TOL_0); - assert_approx_eq!(1.7f16.trunc(), 1.0f16, TOL_0); - assert_approx_eq!(0.0f16.trunc(), 0.0f16, TOL_0); - assert_approx_eq!((-0.0f16).trunc(), -0.0f16, TOL_0); - assert_approx_eq!((-1.0f16).trunc(), -1.0f16, TOL_0); - assert_approx_eq!((-1.3f16).trunc(), -1.0f16, TOL_0); - assert_approx_eq!((-1.5f16).trunc(), -1.0f16, TOL_0); - assert_approx_eq!((-1.7f16).trunc(), -1.0f16, TOL_0); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_fract() { - assert_approx_eq!(1.0f16.fract(), 0.0f16, TOL_0); - assert_approx_eq!(1.3f16.fract(), 0.3f16, TOL_0); - assert_approx_eq!(1.5f16.fract(), 0.5f16, TOL_0); - assert_approx_eq!(1.7f16.fract(), 0.7f16, TOL_0); - assert_approx_eq!(0.0f16.fract(), 0.0f16, TOL_0); - assert_approx_eq!((-0.0f16).fract(), -0.0f16, TOL_0); - assert_approx_eq!((-1.0f16).fract(), -0.0f16, TOL_0); - assert_approx_eq!((-1.3f16).fract(), -0.3f16, TOL_0); - assert_approx_eq!((-1.5f16).fract(), -0.5f16, TOL_0); - assert_approx_eq!((-1.7f16).fract(), -0.7f16, TOL_0); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_abs() { - assert_eq!(f16::INFINITY.abs(), f16::INFINITY); - assert_eq!(1f16.abs(), 1f16); - assert_eq!(0f16.abs(), 0f16); - assert_eq!((-0f16).abs(), 0f16); - assert_eq!((-1f16).abs(), 1f16); - assert_eq!(f16::NEG_INFINITY.abs(), f16::INFINITY); - assert_eq!((1f16 / f16::NEG_INFINITY).abs(), 0f16); - assert!(f16::NAN.abs().is_nan()); -} - -#[test] -fn test_is_sign_positive() { - assert!(f16::INFINITY.is_sign_positive()); - assert!(1f16.is_sign_positive()); - assert!(0f16.is_sign_positive()); - assert!(!(-0f16).is_sign_positive()); - assert!(!(-1f16).is_sign_positive()); - assert!(!f16::NEG_INFINITY.is_sign_positive()); - assert!(!(1f16 / f16::NEG_INFINITY).is_sign_positive()); - assert!(f16::NAN.is_sign_positive()); - assert!(!(-f16::NAN).is_sign_positive()); -} - -#[test] -fn test_is_sign_negative() { - assert!(!f16::INFINITY.is_sign_negative()); - assert!(!1f16.is_sign_negative()); - assert!(!0f16.is_sign_negative()); - assert!((-0f16).is_sign_negative()); - assert!((-1f16).is_sign_negative()); - assert!(f16::NEG_INFINITY.is_sign_negative()); - assert!((1f16 / f16::NEG_INFINITY).is_sign_negative()); - assert!(!f16::NAN.is_sign_negative()); - assert!((-f16::NAN).is_sign_negative()); -} - -#[test] -fn test_next_up() { - let tiny = f16::from_bits(TINY_BITS); - let tiny_up = f16::from_bits(TINY_UP_BITS); - let max_down = f16::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f16::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f16::from_bits(SMALLEST_NORMAL_BITS); - assert_f16_biteq!(f16::NEG_INFINITY.next_up(), f16::MIN); - assert_f16_biteq!(f16::MIN.next_up(), -max_down); - assert_f16_biteq!((-1.0 - f16::EPSILON).next_up(), -1.0); - assert_f16_biteq!((-smallest_normal).next_up(), -largest_subnormal); - assert_f16_biteq!((-tiny_up).next_up(), -tiny); - assert_f16_biteq!((-tiny).next_up(), -0.0f16); - assert_f16_biteq!((-0.0f16).next_up(), tiny); - assert_f16_biteq!(0.0f16.next_up(), tiny); - assert_f16_biteq!(tiny.next_up(), tiny_up); - assert_f16_biteq!(largest_subnormal.next_up(), smallest_normal); - assert_f16_biteq!(1.0f16.next_up(), 1.0 + f16::EPSILON); - assert_f16_biteq!(f16::MAX.next_up(), f16::INFINITY); - assert_f16_biteq!(f16::INFINITY.next_up(), f16::INFINITY); - - // Check that NaNs roundtrip. - let nan0 = f16::NAN; - let nan1 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK1); - let nan2 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK2); - assert_f16_biteq!(nan0.next_up(), nan0); - assert_f16_biteq!(nan1.next_up(), nan1); - assert_f16_biteq!(nan2.next_up(), nan2); -} - -#[test] -fn test_next_down() { - let tiny = f16::from_bits(TINY_BITS); - let tiny_up = f16::from_bits(TINY_UP_BITS); - let max_down = f16::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f16::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f16::from_bits(SMALLEST_NORMAL_BITS); - assert_f16_biteq!(f16::NEG_INFINITY.next_down(), f16::NEG_INFINITY); - assert_f16_biteq!(f16::MIN.next_down(), f16::NEG_INFINITY); - assert_f16_biteq!((-max_down).next_down(), f16::MIN); - assert_f16_biteq!((-1.0f16).next_down(), -1.0 - f16::EPSILON); - assert_f16_biteq!((-largest_subnormal).next_down(), -smallest_normal); - assert_f16_biteq!((-tiny).next_down(), -tiny_up); - assert_f16_biteq!((-0.0f16).next_down(), -tiny); - assert_f16_biteq!((0.0f16).next_down(), -tiny); - assert_f16_biteq!(tiny.next_down(), 0.0f16); - assert_f16_biteq!(tiny_up.next_down(), tiny); - assert_f16_biteq!(smallest_normal.next_down(), largest_subnormal); - assert_f16_biteq!((1.0 + f16::EPSILON).next_down(), 1.0f16); - assert_f16_biteq!(f16::MAX.next_down(), max_down); - assert_f16_biteq!(f16::INFINITY.next_down(), f16::MAX); - - // Check that NaNs roundtrip. - let nan0 = f16::NAN; - let nan1 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK1); - let nan2 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK2); - assert_f16_biteq!(nan0.next_down(), nan0); - assert_f16_biteq!(nan1.next_down(), nan1); - assert_f16_biteq!(nan2.next_down(), nan2); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_mul_add() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_approx_eq!(12.3f16.mul_add(4.5, 6.7), 62.05, TOL_P2); - assert_approx_eq!((-12.3f16).mul_add(-4.5, -6.7), 48.65, TOL_P2); - assert_approx_eq!(0.0f16.mul_add(8.9, 1.2), 1.2, TOL_0); - assert_approx_eq!(3.4f16.mul_add(-0.0, 5.6), 5.6, TOL_0); - assert!(nan.mul_add(7.8, 9.0).is_nan()); - assert_eq!(inf.mul_add(7.8, 9.0), inf); - assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); - assert_eq!(8.9f16.mul_add(inf, 3.2), inf); - assert_eq!((-3.2f16).mul_add(2.4, neg_inf), neg_inf); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_recip() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(1.0f16.recip(), 1.0); - assert_eq!(2.0f16.recip(), 0.5); - assert_eq!((-0.4f16).recip(), -2.5); - assert_eq!(0.0f16.recip(), inf); - assert_approx_eq!(f16::MAX.recip(), 1.526624e-5f16, 1e-4); - assert!(nan.recip().is_nan()); - assert_eq!(inf.recip(), 0.0); - assert_eq!(neg_inf.recip(), 0.0); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_powi() { - // FIXME(llvm19): LLVM misoptimizes `powi.f16` - // - // let nan: f16 = f16::NAN; - // let inf: f16 = f16::INFINITY; - // let neg_inf: f16 = f16::NEG_INFINITY; - // assert_eq!(1.0f16.powi(1), 1.0); - // assert_approx_eq!((-3.1f16).powi(2), 9.61, TOL_0); - // assert_approx_eq!(5.9f16.powi(-2), 0.028727, TOL_N2); - // assert_eq!(8.3f16.powi(0), 1.0); - // assert!(nan.powi(2).is_nan()); - // assert_eq!(inf.powi(3), inf); - // assert_eq!(neg_inf.powi(2), inf); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_powf() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(1.0f16.powf(1.0), 1.0); - assert_approx_eq!(3.4f16.powf(4.5), 246.408183, TOL_P2); - assert_approx_eq!(2.7f16.powf(-3.2), 0.041652, TOL_N2); - assert_approx_eq!((-3.1f16).powf(2.0), 9.61, TOL_P2); - assert_approx_eq!(5.9f16.powf(-2.0), 0.028727, TOL_N2); - assert_eq!(8.3f16.powf(0.0), 1.0); - assert!(nan.powf(2.0).is_nan()); - assert_eq!(inf.powf(2.0), inf); - assert_eq!(neg_inf.powf(3.0), neg_inf); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_sqrt_domain() { - assert!(f16::NAN.sqrt().is_nan()); - assert!(f16::NEG_INFINITY.sqrt().is_nan()); - assert!((-1.0f16).sqrt().is_nan()); - assert_eq!((-0.0f16).sqrt(), -0.0); - assert_eq!(0.0f16.sqrt(), 0.0); - assert_eq!(1.0f16.sqrt(), 1.0); - assert_eq!(f16::INFINITY.sqrt(), f16::INFINITY); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_exp() { - assert_eq!(1.0, 0.0f16.exp()); - assert_approx_eq!(2.718282, 1.0f16.exp(), TOL_0); - assert_approx_eq!(148.413159, 5.0f16.exp(), TOL_0); - - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let nan: f16 = f16::NAN; - assert_eq!(inf, inf.exp()); - assert_eq!(0.0, neg_inf.exp()); - assert!(nan.exp().is_nan()); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_exp2() { - assert_eq!(32.0, 5.0f16.exp2()); - assert_eq!(1.0, 0.0f16.exp2()); - - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let nan: f16 = f16::NAN; - assert_eq!(inf, inf.exp2()); - assert_eq!(0.0, neg_inf.exp2()); - assert!(nan.exp2().is_nan()); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_ln() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_approx_eq!(1.0f16.exp().ln(), 1.0, TOL_0); - assert!(nan.ln().is_nan()); - assert_eq!(inf.ln(), inf); - assert!(neg_inf.ln().is_nan()); - assert!((-2.3f16).ln().is_nan()); - assert_eq!((-0.0f16).ln(), neg_inf); - assert_eq!(0.0f16.ln(), neg_inf); - assert_approx_eq!(4.0f16.ln(), 1.386294, TOL_0); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_log() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(10.0f16.log(10.0), 1.0); - assert_approx_eq!(2.3f16.log(3.5), 0.664858, TOL_0); - assert_eq!(1.0f16.exp().log(1.0f16.exp()), 1.0); - assert!(1.0f16.log(1.0).is_nan()); - assert!(1.0f16.log(-13.9).is_nan()); - assert!(nan.log(2.3).is_nan()); - assert_eq!(inf.log(10.0), inf); - assert!(neg_inf.log(8.8).is_nan()); - assert!((-2.3f16).log(0.1).is_nan()); - assert_eq!((-0.0f16).log(2.0), neg_inf); - assert_eq!(0.0f16.log(7.0), neg_inf); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_log2() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_approx_eq!(10.0f16.log2(), 3.321928, TOL_0); - assert_approx_eq!(2.3f16.log2(), 1.201634, TOL_0); - assert_approx_eq!(1.0f16.exp().log2(), 1.442695, TOL_0); - assert!(nan.log2().is_nan()); - assert_eq!(inf.log2(), inf); - assert!(neg_inf.log2().is_nan()); - assert!((-2.3f16).log2().is_nan()); - assert_eq!((-0.0f16).log2(), neg_inf); - assert_eq!(0.0f16.log2(), neg_inf); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_log10() { - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(10.0f16.log10(), 1.0); - assert_approx_eq!(2.3f16.log10(), 0.361728, TOL_0); - assert_approx_eq!(1.0f16.exp().log10(), 0.434294, TOL_0); - assert_eq!(1.0f16.log10(), 0.0); - assert!(nan.log10().is_nan()); - assert_eq!(inf.log10(), inf); - assert!(neg_inf.log10().is_nan()); - assert!((-2.3f16).log10().is_nan()); - assert_eq!((-0.0f16).log10(), neg_inf); - assert_eq!(0.0f16.log10(), neg_inf); -} - -#[test] -fn test_to_degrees() { - let pi: f16 = consts::PI; - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(0.0f16.to_degrees(), 0.0); - assert_approx_eq!((-5.8f16).to_degrees(), -332.315521, TOL_P2); - assert_approx_eq!(pi.to_degrees(), 180.0, TOL_P2); - assert!(nan.to_degrees().is_nan()); - assert_eq!(inf.to_degrees(), inf); - assert_eq!(neg_inf.to_degrees(), neg_inf); - assert_eq!(1_f16.to_degrees(), 57.2957795130823208767981548141051703); -} - -#[test] -fn test_to_radians() { - let pi: f16 = consts::PI; - let nan: f16 = f16::NAN; - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - assert_eq!(0.0f16.to_radians(), 0.0); - assert_approx_eq!(154.6f16.to_radians(), 2.698279, TOL_0); - assert_approx_eq!((-332.31f16).to_radians(), -5.799903, TOL_0); - assert_approx_eq!(180.0f16.to_radians(), pi, TOL_0); - assert!(nan.to_radians().is_nan()); - assert_eq!(inf.to_radians(), inf); - assert_eq!(neg_inf.to_radians(), neg_inf); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_asinh() { - assert_eq!(0.0f16.asinh(), 0.0f16); - assert_eq!((-0.0f16).asinh(), -0.0f16); - - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let nan: f16 = f16::NAN; - assert_eq!(inf.asinh(), inf); - assert_eq!(neg_inf.asinh(), neg_inf); - assert!(nan.asinh().is_nan()); - assert!((-0.0f16).asinh().is_sign_negative()); - // issue 63271 - assert_approx_eq!(2.0f16.asinh(), 1.443635475178810342493276740273105f16, TOL_0); - assert_approx_eq!((-2.0f16).asinh(), -1.443635475178810342493276740273105f16, TOL_0); - // regression test for the catastrophic cancellation fixed in 72486 - assert_approx_eq!((-200.0f16).asinh(), -5.991470797049389, TOL_0); - - // test for low accuracy from issue 104548 - assert_approx_eq!(10.0f16, 10.0f16.sinh().asinh(), TOL_0); - // mul needed for approximate comparison to be meaningful - assert_approx_eq!(1.0f16, 1e-3f16.sinh().asinh() * 1e3f16, TOL_0); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_acosh() { - assert_eq!(1.0f16.acosh(), 0.0f16); - assert!(0.999f16.acosh().is_nan()); - - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let nan: f16 = f16::NAN; - assert_eq!(inf.acosh(), inf); - assert!(neg_inf.acosh().is_nan()); - assert!(nan.acosh().is_nan()); - assert_approx_eq!(2.0f16.acosh(), 1.31695789692481670862504634730796844f16, TOL_0); - assert_approx_eq!(3.0f16.acosh(), 1.76274717403908605046521864995958461f16, TOL_0); - - // test for low accuracy from issue 104548 - assert_approx_eq!(10.0f16, 10.0f16.cosh().acosh(), TOL_P2); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_atanh() { - assert_eq!(0.0f16.atanh(), 0.0f16); - assert_eq!((-0.0f16).atanh(), -0.0f16); - - let inf: f16 = f16::INFINITY; - let neg_inf: f16 = f16::NEG_INFINITY; - let nan: f16 = f16::NAN; - assert_eq!(1.0f16.atanh(), inf); - assert_eq!((-1.0f16).atanh(), neg_inf); - assert!(2f16.atanh().atanh().is_nan()); - assert!((-2f16).atanh().atanh().is_nan()); - assert!(inf.atanh().is_nan()); - assert!(neg_inf.atanh().is_nan()); - assert!(nan.atanh().is_nan()); - assert_approx_eq!(0.5f16.atanh(), 0.54930614433405484569762261846126285f16, TOL_0); - assert_approx_eq!((-0.5f16).atanh(), -0.54930614433405484569762261846126285f16, TOL_0); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_gamma() { - // precision can differ among platforms - assert_approx_eq!(1.0f16.gamma(), 1.0f16, TOL_0); - assert_approx_eq!(2.0f16.gamma(), 1.0f16, TOL_0); - assert_approx_eq!(3.0f16.gamma(), 2.0f16, TOL_0); - assert_approx_eq!(4.0f16.gamma(), 6.0f16, TOL_0); - assert_approx_eq!(5.0f16.gamma(), 24.0f16, TOL_0); - assert_approx_eq!(0.5f16.gamma(), consts::PI.sqrt(), TOL_0); - assert_approx_eq!((-0.5f16).gamma(), -2.0 * consts::PI.sqrt(), TOL_0); - assert_eq!(0.0f16.gamma(), f16::INFINITY); - assert_eq!((-0.0f16).gamma(), f16::NEG_INFINITY); - assert!((-1.0f16).gamma().is_nan()); - assert!((-2.0f16).gamma().is_nan()); - assert!(f16::NAN.gamma().is_nan()); - assert!(f16::NEG_INFINITY.gamma().is_nan()); - assert_eq!(f16::INFINITY.gamma(), f16::INFINITY); - assert_eq!(171.71f16.gamma(), f16::INFINITY); -} - -#[test] -#[cfg(reliable_f16_math)] -fn test_ln_gamma() { - assert_approx_eq!(1.0f16.ln_gamma().0, 0.0f16, TOL_0); - assert_eq!(1.0f16.ln_gamma().1, 1); - assert_approx_eq!(2.0f16.ln_gamma().0, 0.0f16, TOL_0); - assert_eq!(2.0f16.ln_gamma().1, 1); - assert_approx_eq!(3.0f16.ln_gamma().0, 2.0f16.ln(), TOL_0); - assert_eq!(3.0f16.ln_gamma().1, 1); - assert_approx_eq!((-0.5f16).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), TOL_0); - assert_eq!((-0.5f16).ln_gamma().1, -1); -} - -#[test] -fn test_real_consts() { - // FIXME(f16_f128): add math tests when available - use super::consts; - - let pi: f16 = consts::PI; - let frac_pi_2: f16 = consts::FRAC_PI_2; - let frac_pi_3: f16 = consts::FRAC_PI_3; - let frac_pi_4: f16 = consts::FRAC_PI_4; - let frac_pi_6: f16 = consts::FRAC_PI_6; - let frac_pi_8: f16 = consts::FRAC_PI_8; - let frac_1_pi: f16 = consts::FRAC_1_PI; - let frac_2_pi: f16 = consts::FRAC_2_PI; - - assert_approx_eq!(frac_pi_2, pi / 2f16, TOL_0); - assert_approx_eq!(frac_pi_3, pi / 3f16, TOL_0); - assert_approx_eq!(frac_pi_4, pi / 4f16, TOL_0); - assert_approx_eq!(frac_pi_6, pi / 6f16, TOL_0); - assert_approx_eq!(frac_pi_8, pi / 8f16, TOL_0); - assert_approx_eq!(frac_1_pi, 1f16 / pi, TOL_0); - assert_approx_eq!(frac_2_pi, 2f16 / pi, TOL_0); - - #[cfg(reliable_f16_math)] - { - let frac_2_sqrtpi: f16 = consts::FRAC_2_SQRT_PI; - let sqrt2: f16 = consts::SQRT_2; - let frac_1_sqrt2: f16 = consts::FRAC_1_SQRT_2; - let e: f16 = consts::E; - let log2_e: f16 = consts::LOG2_E; - let log10_e: f16 = consts::LOG10_E; - let ln_2: f16 = consts::LN_2; - let ln_10: f16 = consts::LN_10; - - assert_approx_eq!(frac_2_sqrtpi, 2f16 / pi.sqrt(), TOL_0); - assert_approx_eq!(sqrt2, 2f16.sqrt(), TOL_0); - assert_approx_eq!(frac_1_sqrt2, 1f16 / 2f16.sqrt(), TOL_0); - assert_approx_eq!(log2_e, e.log2(), TOL_0); - assert_approx_eq!(log10_e, e.log10(), TOL_0); - assert_approx_eq!(ln_2, 2f16.ln(), TOL_0); - assert_approx_eq!(ln_10, 10f16.ln(), TOL_0); - } -} - -#[test] -fn test_float_bits_conv() { - assert_eq!((1f16).to_bits(), 0x3c00); - assert_eq!((12.5f16).to_bits(), 0x4a40); - assert_eq!((1337f16).to_bits(), 0x6539); - assert_eq!((-14.25f16).to_bits(), 0xcb20); - assert_approx_eq!(f16::from_bits(0x3c00), 1.0, TOL_0); - assert_approx_eq!(f16::from_bits(0x4a40), 12.5, TOL_0); - assert_approx_eq!(f16::from_bits(0x6539), 1337.0, TOL_P4); - assert_approx_eq!(f16::from_bits(0xcb20), -14.25, TOL_0); - - // Check that NaNs roundtrip their bits regardless of signaling-ness - let masked_nan1 = f16::NAN.to_bits() ^ NAN_MASK1; - let masked_nan2 = f16::NAN.to_bits() ^ NAN_MASK2; - assert!(f16::from_bits(masked_nan1).is_nan()); - assert!(f16::from_bits(masked_nan2).is_nan()); - - assert_eq!(f16::from_bits(masked_nan1).to_bits(), masked_nan1); - assert_eq!(f16::from_bits(masked_nan2).to_bits(), masked_nan2); -} - -#[test] -#[should_panic] -fn test_clamp_min_greater_than_max() { - let _ = 1.0f16.clamp(3.0, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_min_is_nan() { - let _ = 1.0f16.clamp(f16::NAN, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_max_is_nan() { - let _ = 1.0f16.clamp(3.0, f16::NAN); -} - -#[test] -fn test_total_cmp() { - use core::cmp::Ordering; - - fn quiet_bit_mask() -> u16 { - 1 << (f16::MANTISSA_DIGITS - 2) - } - - // FIXME(f16_f128): test subnormals when powf is available - // fn min_subnorm() -> f16 { - // f16::MIN_POSITIVE / f16::powf(2.0, f16::MANTISSA_DIGITS as f16 - 1.0) - // } - - // fn max_subnorm() -> f16 { - // f16::MIN_POSITIVE - min_subnorm() - // } - - fn q_nan() -> f16 { - f16::from_bits(f16::NAN.to_bits() | quiet_bit_mask()) - } - - fn s_nan() -> f16 { - f16::from_bits((f16::NAN.to_bits() & !quiet_bit_mask()) + 42) - } - - assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Equal, (-f16::INFINITY).total_cmp(&-f16::INFINITY)); - assert_eq!(Ordering::Equal, (-f16::MAX).total_cmp(&-f16::MAX)); - assert_eq!(Ordering::Equal, (-2.5_f16).total_cmp(&-2.5)); - assert_eq!(Ordering::Equal, (-1.0_f16).total_cmp(&-1.0)); - assert_eq!(Ordering::Equal, (-1.5_f16).total_cmp(&-1.5)); - assert_eq!(Ordering::Equal, (-0.5_f16).total_cmp(&-0.5)); - assert_eq!(Ordering::Equal, (-f16::MIN_POSITIVE).total_cmp(&-f16::MIN_POSITIVE)); - // assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Equal, (-0.0_f16).total_cmp(&-0.0)); - assert_eq!(Ordering::Equal, 0.0_f16.total_cmp(&0.0)); - // assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Equal, f16::MIN_POSITIVE.total_cmp(&f16::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, 0.5_f16.total_cmp(&0.5)); - assert_eq!(Ordering::Equal, 1.0_f16.total_cmp(&1.0)); - assert_eq!(Ordering::Equal, 1.5_f16.total_cmp(&1.5)); - assert_eq!(Ordering::Equal, 2.5_f16.total_cmp(&2.5)); - assert_eq!(Ordering::Equal, f16::MAX.total_cmp(&f16::MAX)); - assert_eq!(Ordering::Equal, f16::INFINITY.total_cmp(&f16::INFINITY)); - assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); - assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY)); - assert_eq!(Ordering::Less, (-f16::INFINITY).total_cmp(&-f16::MAX)); - assert_eq!(Ordering::Less, (-f16::MAX).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-2.5_f16).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-1.5_f16).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-1.0_f16).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-0.5_f16).total_cmp(&-f16::MIN_POSITIVE)); - // assert_eq!(Ordering::Less, (-f16::MIN_POSITIVE).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); - // assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-0.0_f16).total_cmp(&0.0)); - // assert_eq!(Ordering::Less, 0.0_f16.total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); - // assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f16::MIN_POSITIVE)); - assert_eq!(Ordering::Less, f16::MIN_POSITIVE.total_cmp(&0.5)); - assert_eq!(Ordering::Less, 0.5_f16.total_cmp(&1.0)); - assert_eq!(Ordering::Less, 1.0_f16.total_cmp(&1.5)); - assert_eq!(Ordering::Less, 1.5_f16.total_cmp(&2.5)); - assert_eq!(Ordering::Less, 2.5_f16.total_cmp(&f16::MAX)); - assert_eq!(Ordering::Less, f16::MAX.total_cmp(&f16::INFINITY)); - assert_eq!(Ordering::Less, f16::INFINITY.total_cmp(&s_nan())); - assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Greater, (-f16::INFINITY).total_cmp(&-s_nan())); - assert_eq!(Ordering::Greater, (-f16::MAX).total_cmp(&-f16::INFINITY)); - assert_eq!(Ordering::Greater, (-2.5_f16).total_cmp(&-f16::MAX)); - assert_eq!(Ordering::Greater, (-1.5_f16).total_cmp(&-2.5)); - assert_eq!(Ordering::Greater, (-1.0_f16).total_cmp(&-1.5)); - assert_eq!(Ordering::Greater, (-0.5_f16).total_cmp(&-1.0)); - assert_eq!(Ordering::Greater, (-f16::MIN_POSITIVE).total_cmp(&-0.5)); - // assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f16::MIN_POSITIVE)); - // assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Greater, (-0.0_f16).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Greater, 0.0_f16.total_cmp(&-0.0)); - // assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); - // assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Greater, f16::MIN_POSITIVE.total_cmp(&max_subnorm())); - assert_eq!(Ordering::Greater, 0.5_f16.total_cmp(&f16::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, 1.0_f16.total_cmp(&0.5)); - assert_eq!(Ordering::Greater, 1.5_f16.total_cmp(&1.0)); - assert_eq!(Ordering::Greater, 2.5_f16.total_cmp(&1.5)); - assert_eq!(Ordering::Greater, f16::MAX.total_cmp(&2.5)); - assert_eq!(Ordering::Greater, f16::INFINITY.total_cmp(&f16::MAX)); - assert_eq!(Ordering::Greater, s_nan().total_cmp(&f16::INFINITY)); - assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::MIN_POSITIVE)); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MIN_POSITIVE)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); - // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); -} diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index 7cb285bbff5f7..295eee8700af2 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -12,9 +12,6 @@ #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] -#[cfg(test)] -mod tests; - #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::f32::{ @@ -210,6 +207,7 @@ impl f32 { /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); /// ``` #[rustc_allow_incoherent_impl] + #[doc(alias = "fmaf", alias = "fusedMultiplyAdd")] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -305,8 +303,9 @@ impl f32 { /// ``` /// let x = 2.0_f32; /// let abs_difference = (x.powi(2) - (x * x)).abs(); - /// /// assert!(abs_difference <= f32::EPSILON); + /// + /// assert_eq!(f32::powi(f32::NAN, 0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -328,8 +327,10 @@ impl f32 { /// ``` /// let x = 2.0_f32; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// /// assert!(abs_difference <= f32::EPSILON); + /// + /// assert_eq!(f32::powf(1.0, f32::NAN), 1.0); + /// assert_eq!(f32::powf(f32::NAN, 0.0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -360,6 +361,7 @@ impl f32 { /// assert!(negative.sqrt().is_nan()); /// assert!(negative_zero.sqrt() == negative_zero); /// ``` + #[doc(alias = "squareRoot")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -1149,4 +1151,68 @@ impl f32 { let x = unsafe { cmath::lgammaf_r(self, &mut signgamp) }; (x, signgamp) } + + /// Error function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `erff` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(float_erf)] + /// /// The error function relates what percent of a normal distribution lies + /// /// within `x` standard deviations (scaled by `1/sqrt(2)`). + /// fn within_standard_deviations(x: f32) -> f32 { + /// (x * std::f32::consts::FRAC_1_SQRT_2).erf() * 100.0 + /// } + /// + /// // 68% of a normal distribution is within one standard deviation + /// assert!((within_standard_deviations(1.0) - 68.269).abs() < 0.01); + /// // 95% of a normal distribution is within two standard deviations + /// assert!((within_standard_deviations(2.0) - 95.450).abs() < 0.01); + /// // 99.7% of a normal distribution is within three standard deviations + /// assert!((within_standard_deviations(3.0) - 99.730).abs() < 0.01); + /// ``` + #[rustc_allow_incoherent_impl] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_erf", issue = "136321")] + #[inline] + pub fn erf(self) -> f32 { + unsafe { cmath::erff(self) } + } + + /// Complementary error function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `erfcf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(float_erf)] + /// let x: f32 = 0.123; + /// + /// let one = x.erf() + x.erfc(); + /// let abs_difference = (one - 1.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[rustc_allow_incoherent_impl] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_erf", issue = "136321")] + #[inline] + pub fn erfc(self) -> f32 { + unsafe { cmath::erfcf(self) } + } } diff --git a/library/std/src/f32/tests.rs b/library/std/src/f32/tests.rs deleted file mode 100644 index 99cfcfb231dad..0000000000000 --- a/library/std/src/f32/tests.rs +++ /dev/null @@ -1,919 +0,0 @@ -use crate::f32::consts; -use crate::num::{FpCategory as Fp, *}; - -/// Smallest number -const TINY_BITS: u32 = 0x1; - -/// Next smallest number -const TINY_UP_BITS: u32 = 0x2; - -/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 -const MAX_DOWN_BITS: u32 = 0x7f7f_fffe; - -/// Zeroed exponent, full significant -const LARGEST_SUBNORMAL_BITS: u32 = 0x007f_ffff; - -/// Exponent = 0b1, zeroed significand -const SMALLEST_NORMAL_BITS: u32 = 0x0080_0000; - -/// First pattern over the mantissa -const NAN_MASK1: u32 = 0x002a_aaaa; - -/// Second pattern over the mantissa -const NAN_MASK2: u32 = 0x0055_5555; - -#[allow(unused_macros)] -macro_rules! assert_f32_biteq { - ($left : expr, $right : expr) => { - let l: &f32 = &$left; - let r: &f32 = &$right; - let lb = l.to_bits(); - let rb = r.to_bits(); - assert_eq!(lb, rb, "float {l} ({lb:#010x}) is not bitequal to {r} ({rb:#010x})"); - }; -} - -#[test] -fn test_num_f32() { - test_num(10f32, 2f32); -} - -#[test] -fn test_min_nan() { - assert_eq!(f32::NAN.min(2.0), 2.0); - assert_eq!(2.0f32.min(f32::NAN), 2.0); -} - -#[test] -fn test_max_nan() { - assert_eq!(f32::NAN.max(2.0), 2.0); - assert_eq!(2.0f32.max(f32::NAN), 2.0); -} - -#[test] -fn test_minimum() { - assert!(f32::NAN.minimum(2.0).is_nan()); - assert!(2.0f32.minimum(f32::NAN).is_nan()); -} - -#[test] -fn test_maximum() { - assert!(f32::NAN.maximum(2.0).is_nan()); - assert!(2.0f32.maximum(f32::NAN).is_nan()); -} - -#[test] -fn test_nan() { - let nan: f32 = f32::NAN; - assert!(nan.is_nan()); - assert!(!nan.is_infinite()); - assert!(!nan.is_finite()); - assert!(!nan.is_normal()); - assert!(nan.is_sign_positive()); - assert!(!nan.is_sign_negative()); - assert_eq!(Fp::Nan, nan.classify()); -} - -#[test] -fn test_infinity() { - let inf: f32 = f32::INFINITY; - assert!(inf.is_infinite()); - assert!(!inf.is_finite()); - assert!(inf.is_sign_positive()); - assert!(!inf.is_sign_negative()); - assert!(!inf.is_nan()); - assert!(!inf.is_normal()); - assert_eq!(Fp::Infinite, inf.classify()); -} - -#[test] -fn test_neg_infinity() { - let neg_inf: f32 = f32::NEG_INFINITY; - assert!(neg_inf.is_infinite()); - assert!(!neg_inf.is_finite()); - assert!(!neg_inf.is_sign_positive()); - assert!(neg_inf.is_sign_negative()); - assert!(!neg_inf.is_nan()); - assert!(!neg_inf.is_normal()); - assert_eq!(Fp::Infinite, neg_inf.classify()); -} - -#[test] -fn test_zero() { - let zero: f32 = 0.0f32; - assert_eq!(0.0, zero); - assert!(!zero.is_infinite()); - assert!(zero.is_finite()); - assert!(zero.is_sign_positive()); - assert!(!zero.is_sign_negative()); - assert!(!zero.is_nan()); - assert!(!zero.is_normal()); - assert_eq!(Fp::Zero, zero.classify()); -} - -#[test] -fn test_neg_zero() { - let neg_zero: f32 = -0.0; - assert_eq!(0.0, neg_zero); - assert!(!neg_zero.is_infinite()); - assert!(neg_zero.is_finite()); - assert!(!neg_zero.is_sign_positive()); - assert!(neg_zero.is_sign_negative()); - assert!(!neg_zero.is_nan()); - assert!(!neg_zero.is_normal()); - assert_eq!(Fp::Zero, neg_zero.classify()); -} - -#[test] -fn test_one() { - let one: f32 = 1.0f32; - assert_eq!(1.0, one); - assert!(!one.is_infinite()); - assert!(one.is_finite()); - assert!(one.is_sign_positive()); - assert!(!one.is_sign_negative()); - assert!(!one.is_nan()); - assert!(one.is_normal()); - assert_eq!(Fp::Normal, one.classify()); -} - -#[test] -fn test_is_nan() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert!(nan.is_nan()); - assert!(!0.0f32.is_nan()); - assert!(!5.3f32.is_nan()); - assert!(!(-10.732f32).is_nan()); - assert!(!inf.is_nan()); - assert!(!neg_inf.is_nan()); -} - -#[test] -fn test_is_infinite() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert!(!nan.is_infinite()); - assert!(inf.is_infinite()); - assert!(neg_inf.is_infinite()); - assert!(!0.0f32.is_infinite()); - assert!(!42.8f32.is_infinite()); - assert!(!(-109.2f32).is_infinite()); -} - -#[test] -fn test_is_finite() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert!(!nan.is_finite()); - assert!(!inf.is_finite()); - assert!(!neg_inf.is_finite()); - assert!(0.0f32.is_finite()); - assert!(42.8f32.is_finite()); - assert!((-109.2f32).is_finite()); -} - -#[test] -fn test_is_normal() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let zero: f32 = 0.0f32; - let neg_zero: f32 = -0.0; - assert!(!nan.is_normal()); - assert!(!inf.is_normal()); - assert!(!neg_inf.is_normal()); - assert!(!zero.is_normal()); - assert!(!neg_zero.is_normal()); - assert!(1f32.is_normal()); - assert!(1e-37f32.is_normal()); - assert!(!1e-38f32.is_normal()); -} - -#[test] -fn test_classify() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let zero: f32 = 0.0f32; - let neg_zero: f32 = -0.0; - assert_eq!(nan.classify(), Fp::Nan); - assert_eq!(inf.classify(), Fp::Infinite); - assert_eq!(neg_inf.classify(), Fp::Infinite); - assert_eq!(zero.classify(), Fp::Zero); - assert_eq!(neg_zero.classify(), Fp::Zero); - assert_eq!(1f32.classify(), Fp::Normal); - assert_eq!(1e-37f32.classify(), Fp::Normal); - assert_eq!(1e-38f32.classify(), Fp::Subnormal); -} - -#[test] -fn test_floor() { - assert_approx_eq!(1.0f32.floor(), 1.0f32); - assert_approx_eq!(1.3f32.floor(), 1.0f32); - assert_approx_eq!(1.5f32.floor(), 1.0f32); - assert_approx_eq!(1.7f32.floor(), 1.0f32); - assert_approx_eq!(0.0f32.floor(), 0.0f32); - assert_approx_eq!((-0.0f32).floor(), -0.0f32); - assert_approx_eq!((-1.0f32).floor(), -1.0f32); - assert_approx_eq!((-1.3f32).floor(), -2.0f32); - assert_approx_eq!((-1.5f32).floor(), -2.0f32); - assert_approx_eq!((-1.7f32).floor(), -2.0f32); -} - -#[test] -fn test_ceil() { - assert_approx_eq!(1.0f32.ceil(), 1.0f32); - assert_approx_eq!(1.3f32.ceil(), 2.0f32); - assert_approx_eq!(1.5f32.ceil(), 2.0f32); - assert_approx_eq!(1.7f32.ceil(), 2.0f32); - assert_approx_eq!(0.0f32.ceil(), 0.0f32); - assert_approx_eq!((-0.0f32).ceil(), -0.0f32); - assert_approx_eq!((-1.0f32).ceil(), -1.0f32); - assert_approx_eq!((-1.3f32).ceil(), -1.0f32); - assert_approx_eq!((-1.5f32).ceil(), -1.0f32); - assert_approx_eq!((-1.7f32).ceil(), -1.0f32); -} - -#[test] -fn test_round() { - assert_approx_eq!(2.5f32.round(), 3.0f32); - assert_approx_eq!(1.0f32.round(), 1.0f32); - assert_approx_eq!(1.3f32.round(), 1.0f32); - assert_approx_eq!(1.5f32.round(), 2.0f32); - assert_approx_eq!(1.7f32.round(), 2.0f32); - assert_approx_eq!(0.0f32.round(), 0.0f32); - assert_approx_eq!((-0.0f32).round(), -0.0f32); - assert_approx_eq!((-1.0f32).round(), -1.0f32); - assert_approx_eq!((-1.3f32).round(), -1.0f32); - assert_approx_eq!((-1.5f32).round(), -2.0f32); - assert_approx_eq!((-1.7f32).round(), -2.0f32); -} - -#[test] -fn test_round_ties_even() { - assert_approx_eq!(2.5f32.round_ties_even(), 2.0f32); - assert_approx_eq!(1.0f32.round_ties_even(), 1.0f32); - assert_approx_eq!(1.3f32.round_ties_even(), 1.0f32); - assert_approx_eq!(1.5f32.round_ties_even(), 2.0f32); - assert_approx_eq!(1.7f32.round_ties_even(), 2.0f32); - assert_approx_eq!(0.0f32.round_ties_even(), 0.0f32); - assert_approx_eq!((-0.0f32).round_ties_even(), -0.0f32); - assert_approx_eq!((-1.0f32).round_ties_even(), -1.0f32); - assert_approx_eq!((-1.3f32).round_ties_even(), -1.0f32); - assert_approx_eq!((-1.5f32).round_ties_even(), -2.0f32); - assert_approx_eq!((-1.7f32).round_ties_even(), -2.0f32); -} - -#[test] -fn test_trunc() { - assert_approx_eq!(1.0f32.trunc(), 1.0f32); - assert_approx_eq!(1.3f32.trunc(), 1.0f32); - assert_approx_eq!(1.5f32.trunc(), 1.0f32); - assert_approx_eq!(1.7f32.trunc(), 1.0f32); - assert_approx_eq!(0.0f32.trunc(), 0.0f32); - assert_approx_eq!((-0.0f32).trunc(), -0.0f32); - assert_approx_eq!((-1.0f32).trunc(), -1.0f32); - assert_approx_eq!((-1.3f32).trunc(), -1.0f32); - assert_approx_eq!((-1.5f32).trunc(), -1.0f32); - assert_approx_eq!((-1.7f32).trunc(), -1.0f32); -} - -#[test] -fn test_fract() { - assert_approx_eq!(1.0f32.fract(), 0.0f32); - assert_approx_eq!(1.3f32.fract(), 0.3f32); - assert_approx_eq!(1.5f32.fract(), 0.5f32); - assert_approx_eq!(1.7f32.fract(), 0.7f32); - assert_approx_eq!(0.0f32.fract(), 0.0f32); - assert_approx_eq!((-0.0f32).fract(), -0.0f32); - assert_approx_eq!((-1.0f32).fract(), -0.0f32); - assert_approx_eq!((-1.3f32).fract(), -0.3f32); - assert_approx_eq!((-1.5f32).fract(), -0.5f32); - assert_approx_eq!((-1.7f32).fract(), -0.7f32); -} - -#[test] -fn test_abs() { - assert_eq!(f32::INFINITY.abs(), f32::INFINITY); - assert_eq!(1f32.abs(), 1f32); - assert_eq!(0f32.abs(), 0f32); - assert_eq!((-0f32).abs(), 0f32); - assert_eq!((-1f32).abs(), 1f32); - assert_eq!(f32::NEG_INFINITY.abs(), f32::INFINITY); - assert_eq!((1f32 / f32::NEG_INFINITY).abs(), 0f32); - assert!(f32::NAN.abs().is_nan()); -} - -#[test] -fn test_signum() { - assert_eq!(f32::INFINITY.signum(), 1f32); - assert_eq!(1f32.signum(), 1f32); - assert_eq!(0f32.signum(), 1f32); - assert_eq!((-0f32).signum(), -1f32); - assert_eq!((-1f32).signum(), -1f32); - assert_eq!(f32::NEG_INFINITY.signum(), -1f32); - assert_eq!((1f32 / f32::NEG_INFINITY).signum(), -1f32); - assert!(f32::NAN.signum().is_nan()); -} - -#[test] -fn test_is_sign_positive() { - assert!(f32::INFINITY.is_sign_positive()); - assert!(1f32.is_sign_positive()); - assert!(0f32.is_sign_positive()); - assert!(!(-0f32).is_sign_positive()); - assert!(!(-1f32).is_sign_positive()); - assert!(!f32::NEG_INFINITY.is_sign_positive()); - assert!(!(1f32 / f32::NEG_INFINITY).is_sign_positive()); - assert!(f32::NAN.is_sign_positive()); - assert!(!(-f32::NAN).is_sign_positive()); -} - -#[test] -fn test_is_sign_negative() { - assert!(!f32::INFINITY.is_sign_negative()); - assert!(!1f32.is_sign_negative()); - assert!(!0f32.is_sign_negative()); - assert!((-0f32).is_sign_negative()); - assert!((-1f32).is_sign_negative()); - assert!(f32::NEG_INFINITY.is_sign_negative()); - assert!((1f32 / f32::NEG_INFINITY).is_sign_negative()); - assert!(!f32::NAN.is_sign_negative()); - assert!((-f32::NAN).is_sign_negative()); -} - -#[test] -fn test_next_up() { - let tiny = f32::from_bits(TINY_BITS); - let tiny_up = f32::from_bits(TINY_UP_BITS); - let max_down = f32::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS); - assert_f32_biteq!(f32::NEG_INFINITY.next_up(), f32::MIN); - assert_f32_biteq!(f32::MIN.next_up(), -max_down); - assert_f32_biteq!((-1.0 - f32::EPSILON).next_up(), -1.0); - assert_f32_biteq!((-smallest_normal).next_up(), -largest_subnormal); - assert_f32_biteq!((-tiny_up).next_up(), -tiny); - assert_f32_biteq!((-tiny).next_up(), -0.0f32); - assert_f32_biteq!((-0.0f32).next_up(), tiny); - assert_f32_biteq!(0.0f32.next_up(), tiny); - assert_f32_biteq!(tiny.next_up(), tiny_up); - assert_f32_biteq!(largest_subnormal.next_up(), smallest_normal); - assert_f32_biteq!(1.0f32.next_up(), 1.0 + f32::EPSILON); - assert_f32_biteq!(f32::MAX.next_up(), f32::INFINITY); - assert_f32_biteq!(f32::INFINITY.next_up(), f32::INFINITY); - - // Check that NaNs roundtrip. - let nan0 = f32::NAN; - let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1); - let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2); - assert_f32_biteq!(nan0.next_up(), nan0); - assert_f32_biteq!(nan1.next_up(), nan1); - assert_f32_biteq!(nan2.next_up(), nan2); -} - -#[test] -fn test_next_down() { - let tiny = f32::from_bits(TINY_BITS); - let tiny_up = f32::from_bits(TINY_UP_BITS); - let max_down = f32::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS); - assert_f32_biteq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY); - assert_f32_biteq!(f32::MIN.next_down(), f32::NEG_INFINITY); - assert_f32_biteq!((-max_down).next_down(), f32::MIN); - assert_f32_biteq!((-1.0f32).next_down(), -1.0 - f32::EPSILON); - assert_f32_biteq!((-largest_subnormal).next_down(), -smallest_normal); - assert_f32_biteq!((-tiny).next_down(), -tiny_up); - assert_f32_biteq!((-0.0f32).next_down(), -tiny); - assert_f32_biteq!((0.0f32).next_down(), -tiny); - assert_f32_biteq!(tiny.next_down(), 0.0f32); - assert_f32_biteq!(tiny_up.next_down(), tiny); - assert_f32_biteq!(smallest_normal.next_down(), largest_subnormal); - assert_f32_biteq!((1.0 + f32::EPSILON).next_down(), 1.0f32); - assert_f32_biteq!(f32::MAX.next_down(), max_down); - assert_f32_biteq!(f32::INFINITY.next_down(), f32::MAX); - - // Check that NaNs roundtrip. - let nan0 = f32::NAN; - let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1); - let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2); - assert_f32_biteq!(nan0.next_down(), nan0); - assert_f32_biteq!(nan1.next_down(), nan1); - assert_f32_biteq!(nan2.next_down(), nan2); -} - -#[test] -fn test_mul_add() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_approx_eq!(12.3f32.mul_add(4.5, 6.7), 62.05); - assert_approx_eq!((-12.3f32).mul_add(-4.5, -6.7), 48.65); - assert_approx_eq!(0.0f32.mul_add(8.9, 1.2), 1.2); - assert_approx_eq!(3.4f32.mul_add(-0.0, 5.6), 5.6); - assert!(nan.mul_add(7.8, 9.0).is_nan()); - assert_eq!(inf.mul_add(7.8, 9.0), inf); - assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); - assert_eq!(8.9f32.mul_add(inf, 3.2), inf); - assert_eq!((-3.2f32).mul_add(2.4, neg_inf), neg_inf); -} - -#[test] -fn test_recip() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(1.0f32.recip(), 1.0); - assert_eq!(2.0f32.recip(), 0.5); - assert_eq!((-0.4f32).recip(), -2.5); - assert_eq!(0.0f32.recip(), inf); - assert!(nan.recip().is_nan()); - assert_eq!(inf.recip(), 0.0); - assert_eq!(neg_inf.recip(), 0.0); -} - -#[test] -fn test_powi() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(1.0f32.powi(1), 1.0); - assert_approx_eq!((-3.1f32).powi(2), 9.61); - assert_approx_eq!(5.9f32.powi(-2), 0.028727); - assert_eq!(8.3f32.powi(0), 1.0); - assert!(nan.powi(2).is_nan()); - assert_eq!(inf.powi(3), inf); - assert_eq!(neg_inf.powi(2), inf); -} - -#[test] -fn test_powf() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(1.0f32.powf(1.0), 1.0); - assert_approx_eq!(3.4f32.powf(4.5), 246.408218); - assert_approx_eq!(2.7f32.powf(-3.2), 0.041652); - assert_approx_eq!((-3.1f32).powf(2.0), 9.61); - assert_approx_eq!(5.9f32.powf(-2.0), 0.028727); - assert_eq!(8.3f32.powf(0.0), 1.0); - assert!(nan.powf(2.0).is_nan()); - assert_eq!(inf.powf(2.0), inf); - assert_eq!(neg_inf.powf(3.0), neg_inf); -} - -#[test] -fn test_sqrt_domain() { - assert!(f32::NAN.sqrt().is_nan()); - assert!(f32::NEG_INFINITY.sqrt().is_nan()); - assert!((-1.0f32).sqrt().is_nan()); - assert_eq!((-0.0f32).sqrt(), -0.0); - assert_eq!(0.0f32.sqrt(), 0.0); - assert_eq!(1.0f32.sqrt(), 1.0); - assert_eq!(f32::INFINITY.sqrt(), f32::INFINITY); -} - -#[test] -fn test_exp() { - assert_eq!(1.0, 0.0f32.exp()); - assert_approx_eq!(2.718282, 1.0f32.exp()); - assert_approx_eq!(148.413162, 5.0f32.exp()); - - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let nan: f32 = f32::NAN; - assert_eq!(inf, inf.exp()); - assert_eq!(0.0, neg_inf.exp()); - assert!(nan.exp().is_nan()); -} - -#[test] -fn test_exp2() { - assert_eq!(32.0, 5.0f32.exp2()); - assert_eq!(1.0, 0.0f32.exp2()); - - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let nan: f32 = f32::NAN; - assert_eq!(inf, inf.exp2()); - assert_eq!(0.0, neg_inf.exp2()); - assert!(nan.exp2().is_nan()); -} - -#[test] -fn test_ln() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_approx_eq!(1.0f32.exp().ln(), 1.0); - assert!(nan.ln().is_nan()); - assert_eq!(inf.ln(), inf); - assert!(neg_inf.ln().is_nan()); - assert!((-2.3f32).ln().is_nan()); - assert_eq!((-0.0f32).ln(), neg_inf); - assert_eq!(0.0f32.ln(), neg_inf); - assert_approx_eq!(4.0f32.ln(), 1.386294); -} - -#[test] -fn test_log() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(10.0f32.log(10.0), 1.0); - assert_approx_eq!(2.3f32.log(3.5), 0.664858); - assert_eq!(1.0f32.exp().log(1.0f32.exp()), 1.0); - assert!(1.0f32.log(1.0).is_nan()); - assert!(1.0f32.log(-13.9).is_nan()); - assert!(nan.log(2.3).is_nan()); - assert_eq!(inf.log(10.0), inf); - assert!(neg_inf.log(8.8).is_nan()); - assert!((-2.3f32).log(0.1).is_nan()); - assert_eq!((-0.0f32).log(2.0), neg_inf); - assert_eq!(0.0f32.log(7.0), neg_inf); -} - -#[test] -fn test_log2() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_approx_eq!(10.0f32.log2(), 3.321928); - assert_approx_eq!(2.3f32.log2(), 1.201634); - assert_approx_eq!(1.0f32.exp().log2(), 1.442695); - assert!(nan.log2().is_nan()); - assert_eq!(inf.log2(), inf); - assert!(neg_inf.log2().is_nan()); - assert!((-2.3f32).log2().is_nan()); - assert_eq!((-0.0f32).log2(), neg_inf); - assert_eq!(0.0f32.log2(), neg_inf); -} - -#[test] -fn test_log10() { - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(10.0f32.log10(), 1.0); - assert_approx_eq!(2.3f32.log10(), 0.361728); - assert_approx_eq!(1.0f32.exp().log10(), 0.434294); - assert_eq!(1.0f32.log10(), 0.0); - assert!(nan.log10().is_nan()); - assert_eq!(inf.log10(), inf); - assert!(neg_inf.log10().is_nan()); - assert!((-2.3f32).log10().is_nan()); - assert_eq!((-0.0f32).log10(), neg_inf); - assert_eq!(0.0f32.log10(), neg_inf); -} - -#[test] -fn test_to_degrees() { - let pi: f32 = consts::PI; - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(0.0f32.to_degrees(), 0.0); - assert_approx_eq!((-5.8f32).to_degrees(), -332.315521); - assert_eq!(pi.to_degrees(), 180.0); - assert!(nan.to_degrees().is_nan()); - assert_eq!(inf.to_degrees(), inf); - assert_eq!(neg_inf.to_degrees(), neg_inf); - assert_eq!(1_f32.to_degrees(), 57.2957795130823208767981548141051703); -} - -#[test] -fn test_to_radians() { - let pi: f32 = consts::PI; - let nan: f32 = f32::NAN; - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - assert_eq!(0.0f32.to_radians(), 0.0); - assert_approx_eq!(154.6f32.to_radians(), 2.698279); - assert_approx_eq!((-332.31f32).to_radians(), -5.799903); - assert_eq!(180.0f32.to_radians(), pi); - assert!(nan.to_radians().is_nan()); - assert_eq!(inf.to_radians(), inf); - assert_eq!(neg_inf.to_radians(), neg_inf); -} - -#[test] -fn test_asinh() { - assert_eq!(0.0f32.asinh(), 0.0f32); - assert_eq!((-0.0f32).asinh(), -0.0f32); - - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let nan: f32 = f32::NAN; - assert_eq!(inf.asinh(), inf); - assert_eq!(neg_inf.asinh(), neg_inf); - assert!(nan.asinh().is_nan()); - assert!((-0.0f32).asinh().is_sign_negative()); // issue 63271 - assert_approx_eq!(2.0f32.asinh(), 1.443635475178810342493276740273105f32); - assert_approx_eq!((-2.0f32).asinh(), -1.443635475178810342493276740273105f32); - // regression test for the catastrophic cancellation fixed in 72486 - assert_approx_eq!((-3000.0f32).asinh(), -8.699514775987968673236893537700647f32); - - // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f32, 60.0f32.sinh().asinh()); - // mul needed for approximate comparison to be meaningful - assert_approx_eq!(1.0f32, 1e-15f32.sinh().asinh() * 1e15f32); -} - -#[test] -fn test_acosh() { - assert_eq!(1.0f32.acosh(), 0.0f32); - assert!(0.999f32.acosh().is_nan()); - - let inf: f32 = f32::INFINITY; - let neg_inf: f32 = f32::NEG_INFINITY; - let nan: f32 = f32::NAN; - assert_eq!(inf.acosh(), inf); - assert!(neg_inf.acosh().is_nan()); - assert!(nan.acosh().is_nan()); - assert_approx_eq!(2.0f32.acosh(), 1.31695789692481670862504634730796844f32); - assert_approx_eq!(3.0f32.acosh(), 1.76274717403908605046521864995958461f32); - - // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f32, 60.0f32.cosh().acosh()); -} - -#[test] -fn test_atanh() { - assert_eq!(0.0f32.atanh(), 0.0f32); - assert_eq!((-0.0f32).atanh(), -0.0f32); - - let inf32: f32 = f32::INFINITY; - let neg_inf32: f32 = f32::NEG_INFINITY; - assert_eq!(1.0f32.atanh(), inf32); - assert_eq!((-1.0f32).atanh(), neg_inf32); - - assert!(2f64.atanh().atanh().is_nan()); - assert!((-2f64).atanh().atanh().is_nan()); - - let inf64: f32 = f32::INFINITY; - let neg_inf64: f32 = f32::NEG_INFINITY; - let nan32: f32 = f32::NAN; - assert!(inf64.atanh().is_nan()); - assert!(neg_inf64.atanh().is_nan()); - assert!(nan32.atanh().is_nan()); - - assert_approx_eq!(0.5f32.atanh(), 0.54930614433405484569762261846126285f32); - assert_approx_eq!((-0.5f32).atanh(), -0.54930614433405484569762261846126285f32); -} - -#[test] -fn test_gamma() { - // precision can differ between platforms - assert_approx_eq!(1.0f32.gamma(), 1.0f32); - assert_approx_eq!(2.0f32.gamma(), 1.0f32); - assert_approx_eq!(3.0f32.gamma(), 2.0f32); - assert_approx_eq!(4.0f32.gamma(), 6.0f32); - assert_approx_eq!(5.0f32.gamma(), 24.0f32); - assert_approx_eq!(0.5f32.gamma(), consts::PI.sqrt()); - assert_approx_eq!((-0.5f32).gamma(), -2.0 * consts::PI.sqrt()); - assert_eq!(0.0f32.gamma(), f32::INFINITY); - assert_eq!((-0.0f32).gamma(), f32::NEG_INFINITY); - assert!((-1.0f32).gamma().is_nan()); - assert!((-2.0f32).gamma().is_nan()); - assert!(f32::NAN.gamma().is_nan()); - assert!(f32::NEG_INFINITY.gamma().is_nan()); - assert_eq!(f32::INFINITY.gamma(), f32::INFINITY); - assert_eq!(171.71f32.gamma(), f32::INFINITY); -} - -#[test] -fn test_ln_gamma() { - assert_approx_eq!(1.0f32.ln_gamma().0, 0.0f32); - assert_eq!(1.0f32.ln_gamma().1, 1); - assert_approx_eq!(2.0f32.ln_gamma().0, 0.0f32); - assert_eq!(2.0f32.ln_gamma().1, 1); - assert_approx_eq!(3.0f32.ln_gamma().0, 2.0f32.ln()); - assert_eq!(3.0f32.ln_gamma().1, 1); - assert_approx_eq!((-0.5f32).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln()); - assert_eq!((-0.5f32).ln_gamma().1, -1); -} - -#[test] -fn test_real_consts() { - use super::consts; - - let pi: f32 = consts::PI; - let frac_pi_2: f32 = consts::FRAC_PI_2; - let frac_pi_3: f32 = consts::FRAC_PI_3; - let frac_pi_4: f32 = consts::FRAC_PI_4; - let frac_pi_6: f32 = consts::FRAC_PI_6; - let frac_pi_8: f32 = consts::FRAC_PI_8; - let frac_1_pi: f32 = consts::FRAC_1_PI; - let frac_2_pi: f32 = consts::FRAC_2_PI; - let frac_2_sqrtpi: f32 = consts::FRAC_2_SQRT_PI; - let sqrt2: f32 = consts::SQRT_2; - let frac_1_sqrt2: f32 = consts::FRAC_1_SQRT_2; - let e: f32 = consts::E; - let log2_e: f32 = consts::LOG2_E; - let log10_e: f32 = consts::LOG10_E; - let ln_2: f32 = consts::LN_2; - let ln_10: f32 = consts::LN_10; - - assert_approx_eq!(frac_pi_2, pi / 2f32); - assert_approx_eq!(frac_pi_3, pi / 3f32); - assert_approx_eq!(frac_pi_4, pi / 4f32); - assert_approx_eq!(frac_pi_6, pi / 6f32); - assert_approx_eq!(frac_pi_8, pi / 8f32); - assert_approx_eq!(frac_1_pi, 1f32 / pi); - assert_approx_eq!(frac_2_pi, 2f32 / pi); - assert_approx_eq!(frac_2_sqrtpi, 2f32 / pi.sqrt()); - assert_approx_eq!(sqrt2, 2f32.sqrt()); - assert_approx_eq!(frac_1_sqrt2, 1f32 / 2f32.sqrt()); - assert_approx_eq!(log2_e, e.log2()); - assert_approx_eq!(log10_e, e.log10()); - assert_approx_eq!(ln_2, 2f32.ln()); - assert_approx_eq!(ln_10, 10f32.ln()); -} - -#[test] -fn test_float_bits_conv() { - assert_eq!((1f32).to_bits(), 0x3f800000); - assert_eq!((12.5f32).to_bits(), 0x41480000); - assert_eq!((1337f32).to_bits(), 0x44a72000); - assert_eq!((-14.25f32).to_bits(), 0xc1640000); - assert_approx_eq!(f32::from_bits(0x3f800000), 1.0); - assert_approx_eq!(f32::from_bits(0x41480000), 12.5); - assert_approx_eq!(f32::from_bits(0x44a72000), 1337.0); - assert_approx_eq!(f32::from_bits(0xc1640000), -14.25); - - // Check that NaNs roundtrip their bits regardless of signaling-ness - // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits - let masked_nan1 = f32::NAN.to_bits() ^ NAN_MASK1; - let masked_nan2 = f32::NAN.to_bits() ^ NAN_MASK2; - assert!(f32::from_bits(masked_nan1).is_nan()); - assert!(f32::from_bits(masked_nan2).is_nan()); - - assert_eq!(f32::from_bits(masked_nan1).to_bits(), masked_nan1); - assert_eq!(f32::from_bits(masked_nan2).to_bits(), masked_nan2); -} - -#[test] -#[should_panic] -fn test_clamp_min_greater_than_max() { - let _ = 1.0f32.clamp(3.0, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_min_is_nan() { - let _ = 1.0f32.clamp(f32::NAN, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_max_is_nan() { - let _ = 1.0f32.clamp(3.0, f32::NAN); -} - -#[test] -fn test_total_cmp() { - use core::cmp::Ordering; - - fn quiet_bit_mask() -> u32 { - 1 << (f32::MANTISSA_DIGITS - 2) - } - - fn min_subnorm() -> f32 { - f32::MIN_POSITIVE / f32::powf(2.0, f32::MANTISSA_DIGITS as f32 - 1.0) - } - - fn max_subnorm() -> f32 { - f32::MIN_POSITIVE - min_subnorm() - } - - fn q_nan() -> f32 { - f32::from_bits(f32::NAN.to_bits() | quiet_bit_mask()) - } - - fn s_nan() -> f32 { - f32::from_bits((f32::NAN.to_bits() & !quiet_bit_mask()) + 42) - } - - assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Equal, (-f32::INFINITY).total_cmp(&-f32::INFINITY)); - assert_eq!(Ordering::Equal, (-f32::MAX).total_cmp(&-f32::MAX)); - assert_eq!(Ordering::Equal, (-2.5_f32).total_cmp(&-2.5)); - assert_eq!(Ordering::Equal, (-1.0_f32).total_cmp(&-1.0)); - assert_eq!(Ordering::Equal, (-1.5_f32).total_cmp(&-1.5)); - assert_eq!(Ordering::Equal, (-0.5_f32).total_cmp(&-0.5)); - assert_eq!(Ordering::Equal, (-f32::MIN_POSITIVE).total_cmp(&-f32::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Equal, (-0.0_f32).total_cmp(&-0.0)); - assert_eq!(Ordering::Equal, 0.0_f32.total_cmp(&0.0)); - assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); - assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Equal, f32::MIN_POSITIVE.total_cmp(&f32::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, 0.5_f32.total_cmp(&0.5)); - assert_eq!(Ordering::Equal, 1.0_f32.total_cmp(&1.0)); - assert_eq!(Ordering::Equal, 1.5_f32.total_cmp(&1.5)); - assert_eq!(Ordering::Equal, 2.5_f32.total_cmp(&2.5)); - assert_eq!(Ordering::Equal, f32::MAX.total_cmp(&f32::MAX)); - assert_eq!(Ordering::Equal, f32::INFINITY.total_cmp(&f32::INFINITY)); - assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); - assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY)); - assert_eq!(Ordering::Less, (-f32::INFINITY).total_cmp(&-f32::MAX)); - assert_eq!(Ordering::Less, (-f32::MAX).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-2.5_f32).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-1.5_f32).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-1.0_f32).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-0.5_f32).total_cmp(&-f32::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-f32::MIN_POSITIVE).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-0.0_f32).total_cmp(&0.0)); - assert_eq!(Ordering::Less, 0.0_f32.total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f32::MIN_POSITIVE)); - assert_eq!(Ordering::Less, f32::MIN_POSITIVE.total_cmp(&0.5)); - assert_eq!(Ordering::Less, 0.5_f32.total_cmp(&1.0)); - assert_eq!(Ordering::Less, 1.0_f32.total_cmp(&1.5)); - assert_eq!(Ordering::Less, 1.5_f32.total_cmp(&2.5)); - assert_eq!(Ordering::Less, 2.5_f32.total_cmp(&f32::MAX)); - assert_eq!(Ordering::Less, f32::MAX.total_cmp(&f32::INFINITY)); - assert_eq!(Ordering::Less, f32::INFINITY.total_cmp(&s_nan())); - assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Greater, (-f32::INFINITY).total_cmp(&-s_nan())); - assert_eq!(Ordering::Greater, (-f32::MAX).total_cmp(&-f32::INFINITY)); - assert_eq!(Ordering::Greater, (-2.5_f32).total_cmp(&-f32::MAX)); - assert_eq!(Ordering::Greater, (-1.5_f32).total_cmp(&-2.5)); - assert_eq!(Ordering::Greater, (-1.0_f32).total_cmp(&-1.5)); - assert_eq!(Ordering::Greater, (-0.5_f32).total_cmp(&-1.0)); - assert_eq!(Ordering::Greater, (-f32::MIN_POSITIVE).total_cmp(&-0.5)); - assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f32::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Greater, (-0.0_f32).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Greater, 0.0_f32.total_cmp(&-0.0)); - assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); - assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); - assert_eq!(Ordering::Greater, f32::MIN_POSITIVE.total_cmp(&max_subnorm())); - assert_eq!(Ordering::Greater, 0.5_f32.total_cmp(&f32::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, 1.0_f32.total_cmp(&0.5)); - assert_eq!(Ordering::Greater, 1.5_f32.total_cmp(&1.0)); - assert_eq!(Ordering::Greater, 2.5_f32.total_cmp(&1.5)); - assert_eq!(Ordering::Greater, f32::MAX.total_cmp(&2.5)); - assert_eq!(Ordering::Greater, f32::INFINITY.total_cmp(&f32::MAX)); - assert_eq!(Ordering::Greater, s_nan().total_cmp(&f32::INFINITY)); - assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); -} diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index 47163c272de32..0d713ecbc7312 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -12,9 +12,6 @@ #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] -#[cfg(test)] -mod tests; - #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::f64::{ @@ -210,6 +207,7 @@ impl f64 { /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); /// ``` #[rustc_allow_incoherent_impl] + #[doc(alias = "fma", alias = "fusedMultiplyAdd")] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -305,8 +303,9 @@ impl f64 { /// ``` /// let x = 2.0_f64; /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f64::EPSILON); /// - /// assert!(abs_difference < 1e-10); + /// assert_eq!(f64::powi(f64::NAN, 0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -328,8 +327,10 @@ impl f64 { /// ``` /// let x = 2.0_f64; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); + /// assert!(abs_difference <= f64::EPSILON); /// - /// assert!(abs_difference < 1e-10); + /// assert_eq!(f64::powf(1.0, f64::NAN), 1.0); + /// assert_eq!(f64::powf(f64::NAN, 0.0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -360,6 +361,7 @@ impl f64 { /// assert!(negative.sqrt().is_nan()); /// assert!(negative_zero.sqrt() == negative_zero); /// ``` + #[doc(alias = "squareRoot")] #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -1149,4 +1151,68 @@ impl f64 { let x = unsafe { cmath::lgamma_r(self, &mut signgamp) }; (x, signgamp) } + + /// Error function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `erf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(float_erf)] + /// /// The error function relates what percent of a normal distribution lies + /// /// within `x` standard deviations (scaled by `1/sqrt(2)`). + /// fn within_standard_deviations(x: f64) -> f64 { + /// (x * std::f64::consts::FRAC_1_SQRT_2).erf() * 100.0 + /// } + /// + /// // 68% of a normal distribution is within one standard deviation + /// assert!((within_standard_deviations(1.0) - 68.269).abs() < 0.01); + /// // 95% of a normal distribution is within two standard deviations + /// assert!((within_standard_deviations(2.0) - 95.450).abs() < 0.01); + /// // 99.7% of a normal distribution is within three standard deviations + /// assert!((within_standard_deviations(3.0) - 99.730).abs() < 0.01); + /// ``` + #[rustc_allow_incoherent_impl] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_erf", issue = "136321")] + #[inline] + pub fn erf(self) -> f64 { + unsafe { cmath::erf(self) } + } + + /// Complementary error function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `erfc` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(float_erf)] + /// let x: f64 = 0.123; + /// + /// let one = x.erf() + x.erfc(); + /// let abs_difference = (one - 1.0).abs(); + /// + /// assert!(abs_difference <= f64::EPSILON); + /// ``` + #[rustc_allow_incoherent_impl] + #[must_use = "method returns a new number and does not mutate the original value"] + #[unstable(feature = "float_erf", issue = "136321")] + #[inline] + pub fn erfc(self) -> f64 { + unsafe { cmath::erfc(self) } + } } diff --git a/library/std/src/f64/tests.rs b/library/std/src/f64/tests.rs deleted file mode 100644 index 3fac2efe0d76c..0000000000000 --- a/library/std/src/f64/tests.rs +++ /dev/null @@ -1,900 +0,0 @@ -use crate::f64::consts; -use crate::num::{FpCategory as Fp, *}; - -/// Smallest number -const TINY_BITS: u64 = 0x1; - -/// Next smallest number -const TINY_UP_BITS: u64 = 0x2; - -/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 -const MAX_DOWN_BITS: u64 = 0x7fef_ffff_ffff_fffe; - -/// Zeroed exponent, full significant -const LARGEST_SUBNORMAL_BITS: u64 = 0x000f_ffff_ffff_ffff; - -/// Exponent = 0b1, zeroed significand -const SMALLEST_NORMAL_BITS: u64 = 0x0010_0000_0000_0000; - -/// First pattern over the mantissa -const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa; - -/// Second pattern over the mantissa -const NAN_MASK2: u64 = 0x0005_5555_5555_5555; - -#[allow(unused_macros)] -macro_rules! assert_f64_biteq { - ($left : expr, $right : expr) => { - let l: &f64 = &$left; - let r: &f64 = &$right; - let lb = l.to_bits(); - let rb = r.to_bits(); - assert_eq!(lb, rb, "float {l} ({lb:#018x}) is not bitequal to {r} ({rb:#018x})"); - }; -} - -#[test] -fn test_num_f64() { - test_num(10f64, 2f64); -} - -#[test] -fn test_min_nan() { - assert_eq!(f64::NAN.min(2.0), 2.0); - assert_eq!(2.0f64.min(f64::NAN), 2.0); -} - -#[test] -fn test_max_nan() { - assert_eq!(f64::NAN.max(2.0), 2.0); - assert_eq!(2.0f64.max(f64::NAN), 2.0); -} - -#[test] -fn test_nan() { - let nan: f64 = f64::NAN; - assert!(nan.is_nan()); - assert!(!nan.is_infinite()); - assert!(!nan.is_finite()); - assert!(!nan.is_normal()); - assert!(nan.is_sign_positive()); - assert!(!nan.is_sign_negative()); - assert_eq!(Fp::Nan, nan.classify()); -} - -#[test] -fn test_infinity() { - let inf: f64 = f64::INFINITY; - assert!(inf.is_infinite()); - assert!(!inf.is_finite()); - assert!(inf.is_sign_positive()); - assert!(!inf.is_sign_negative()); - assert!(!inf.is_nan()); - assert!(!inf.is_normal()); - assert_eq!(Fp::Infinite, inf.classify()); -} - -#[test] -fn test_neg_infinity() { - let neg_inf: f64 = f64::NEG_INFINITY; - assert!(neg_inf.is_infinite()); - assert!(!neg_inf.is_finite()); - assert!(!neg_inf.is_sign_positive()); - assert!(neg_inf.is_sign_negative()); - assert!(!neg_inf.is_nan()); - assert!(!neg_inf.is_normal()); - assert_eq!(Fp::Infinite, neg_inf.classify()); -} - -#[test] -fn test_zero() { - let zero: f64 = 0.0f64; - assert_eq!(0.0, zero); - assert!(!zero.is_infinite()); - assert!(zero.is_finite()); - assert!(zero.is_sign_positive()); - assert!(!zero.is_sign_negative()); - assert!(!zero.is_nan()); - assert!(!zero.is_normal()); - assert_eq!(Fp::Zero, zero.classify()); -} - -#[test] -fn test_neg_zero() { - let neg_zero: f64 = -0.0; - assert_eq!(0.0, neg_zero); - assert!(!neg_zero.is_infinite()); - assert!(neg_zero.is_finite()); - assert!(!neg_zero.is_sign_positive()); - assert!(neg_zero.is_sign_negative()); - assert!(!neg_zero.is_nan()); - assert!(!neg_zero.is_normal()); - assert_eq!(Fp::Zero, neg_zero.classify()); -} - -#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 -#[test] -fn test_one() { - let one: f64 = 1.0f64; - assert_eq!(1.0, one); - assert!(!one.is_infinite()); - assert!(one.is_finite()); - assert!(one.is_sign_positive()); - assert!(!one.is_sign_negative()); - assert!(!one.is_nan()); - assert!(one.is_normal()); - assert_eq!(Fp::Normal, one.classify()); -} - -#[test] -fn test_is_nan() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert!(nan.is_nan()); - assert!(!0.0f64.is_nan()); - assert!(!5.3f64.is_nan()); - assert!(!(-10.732f64).is_nan()); - assert!(!inf.is_nan()); - assert!(!neg_inf.is_nan()); -} - -#[test] -fn test_is_infinite() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert!(!nan.is_infinite()); - assert!(inf.is_infinite()); - assert!(neg_inf.is_infinite()); - assert!(!0.0f64.is_infinite()); - assert!(!42.8f64.is_infinite()); - assert!(!(-109.2f64).is_infinite()); -} - -#[test] -fn test_is_finite() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert!(!nan.is_finite()); - assert!(!inf.is_finite()); - assert!(!neg_inf.is_finite()); - assert!(0.0f64.is_finite()); - assert!(42.8f64.is_finite()); - assert!((-109.2f64).is_finite()); -} - -#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 -#[test] -fn test_is_normal() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let zero: f64 = 0.0f64; - let neg_zero: f64 = -0.0; - assert!(!nan.is_normal()); - assert!(!inf.is_normal()); - assert!(!neg_inf.is_normal()); - assert!(!zero.is_normal()); - assert!(!neg_zero.is_normal()); - assert!(1f64.is_normal()); - assert!(1e-307f64.is_normal()); - assert!(!1e-308f64.is_normal()); -} - -#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630 -#[test] -fn test_classify() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let zero: f64 = 0.0f64; - let neg_zero: f64 = -0.0; - assert_eq!(nan.classify(), Fp::Nan); - assert_eq!(inf.classify(), Fp::Infinite); - assert_eq!(neg_inf.classify(), Fp::Infinite); - assert_eq!(zero.classify(), Fp::Zero); - assert_eq!(neg_zero.classify(), Fp::Zero); - assert_eq!(1e-307f64.classify(), Fp::Normal); - assert_eq!(1e-308f64.classify(), Fp::Subnormal); -} - -#[test] -fn test_floor() { - assert_approx_eq!(1.0f64.floor(), 1.0f64); - assert_approx_eq!(1.3f64.floor(), 1.0f64); - assert_approx_eq!(1.5f64.floor(), 1.0f64); - assert_approx_eq!(1.7f64.floor(), 1.0f64); - assert_approx_eq!(0.0f64.floor(), 0.0f64); - assert_approx_eq!((-0.0f64).floor(), -0.0f64); - assert_approx_eq!((-1.0f64).floor(), -1.0f64); - assert_approx_eq!((-1.3f64).floor(), -2.0f64); - assert_approx_eq!((-1.5f64).floor(), -2.0f64); - assert_approx_eq!((-1.7f64).floor(), -2.0f64); -} - -#[test] -fn test_ceil() { - assert_approx_eq!(1.0f64.ceil(), 1.0f64); - assert_approx_eq!(1.3f64.ceil(), 2.0f64); - assert_approx_eq!(1.5f64.ceil(), 2.0f64); - assert_approx_eq!(1.7f64.ceil(), 2.0f64); - assert_approx_eq!(0.0f64.ceil(), 0.0f64); - assert_approx_eq!((-0.0f64).ceil(), -0.0f64); - assert_approx_eq!((-1.0f64).ceil(), -1.0f64); - assert_approx_eq!((-1.3f64).ceil(), -1.0f64); - assert_approx_eq!((-1.5f64).ceil(), -1.0f64); - assert_approx_eq!((-1.7f64).ceil(), -1.0f64); -} - -#[test] -fn test_round() { - assert_approx_eq!(2.5f64.round(), 3.0f64); - assert_approx_eq!(1.0f64.round(), 1.0f64); - assert_approx_eq!(1.3f64.round(), 1.0f64); - assert_approx_eq!(1.5f64.round(), 2.0f64); - assert_approx_eq!(1.7f64.round(), 2.0f64); - assert_approx_eq!(0.0f64.round(), 0.0f64); - assert_approx_eq!((-0.0f64).round(), -0.0f64); - assert_approx_eq!((-1.0f64).round(), -1.0f64); - assert_approx_eq!((-1.3f64).round(), -1.0f64); - assert_approx_eq!((-1.5f64).round(), -2.0f64); - assert_approx_eq!((-1.7f64).round(), -2.0f64); -} - -#[test] -fn test_round_ties_even() { - assert_approx_eq!(2.5f64.round_ties_even(), 2.0f64); - assert_approx_eq!(1.0f64.round_ties_even(), 1.0f64); - assert_approx_eq!(1.3f64.round_ties_even(), 1.0f64); - assert_approx_eq!(1.5f64.round_ties_even(), 2.0f64); - assert_approx_eq!(1.7f64.round_ties_even(), 2.0f64); - assert_approx_eq!(0.0f64.round_ties_even(), 0.0f64); - assert_approx_eq!((-0.0f64).round_ties_even(), -0.0f64); - assert_approx_eq!((-1.0f64).round_ties_even(), -1.0f64); - assert_approx_eq!((-1.3f64).round_ties_even(), -1.0f64); - assert_approx_eq!((-1.5f64).round_ties_even(), -2.0f64); - assert_approx_eq!((-1.7f64).round_ties_even(), -2.0f64); -} - -#[test] -fn test_trunc() { - assert_approx_eq!(1.0f64.trunc(), 1.0f64); - assert_approx_eq!(1.3f64.trunc(), 1.0f64); - assert_approx_eq!(1.5f64.trunc(), 1.0f64); - assert_approx_eq!(1.7f64.trunc(), 1.0f64); - assert_approx_eq!(0.0f64.trunc(), 0.0f64); - assert_approx_eq!((-0.0f64).trunc(), -0.0f64); - assert_approx_eq!((-1.0f64).trunc(), -1.0f64); - assert_approx_eq!((-1.3f64).trunc(), -1.0f64); - assert_approx_eq!((-1.5f64).trunc(), -1.0f64); - assert_approx_eq!((-1.7f64).trunc(), -1.0f64); -} - -#[test] -fn test_fract() { - assert_approx_eq!(1.0f64.fract(), 0.0f64); - assert_approx_eq!(1.3f64.fract(), 0.3f64); - assert_approx_eq!(1.5f64.fract(), 0.5f64); - assert_approx_eq!(1.7f64.fract(), 0.7f64); - assert_approx_eq!(0.0f64.fract(), 0.0f64); - assert_approx_eq!((-0.0f64).fract(), -0.0f64); - assert_approx_eq!((-1.0f64).fract(), -0.0f64); - assert_approx_eq!((-1.3f64).fract(), -0.3f64); - assert_approx_eq!((-1.5f64).fract(), -0.5f64); - assert_approx_eq!((-1.7f64).fract(), -0.7f64); -} - -#[test] -fn test_abs() { - assert_eq!(f64::INFINITY.abs(), f64::INFINITY); - assert_eq!(1f64.abs(), 1f64); - assert_eq!(0f64.abs(), 0f64); - assert_eq!((-0f64).abs(), 0f64); - assert_eq!((-1f64).abs(), 1f64); - assert_eq!(f64::NEG_INFINITY.abs(), f64::INFINITY); - assert_eq!((1f64 / f64::NEG_INFINITY).abs(), 0f64); - assert!(f64::NAN.abs().is_nan()); -} - -#[test] -fn test_signum() { - assert_eq!(f64::INFINITY.signum(), 1f64); - assert_eq!(1f64.signum(), 1f64); - assert_eq!(0f64.signum(), 1f64); - assert_eq!((-0f64).signum(), -1f64); - assert_eq!((-1f64).signum(), -1f64); - assert_eq!(f64::NEG_INFINITY.signum(), -1f64); - assert_eq!((1f64 / f64::NEG_INFINITY).signum(), -1f64); - assert!(f64::NAN.signum().is_nan()); -} - -#[test] -fn test_is_sign_positive() { - assert!(f64::INFINITY.is_sign_positive()); - assert!(1f64.is_sign_positive()); - assert!(0f64.is_sign_positive()); - assert!(!(-0f64).is_sign_positive()); - assert!(!(-1f64).is_sign_positive()); - assert!(!f64::NEG_INFINITY.is_sign_positive()); - assert!(!(1f64 / f64::NEG_INFINITY).is_sign_positive()); - assert!(f64::NAN.is_sign_positive()); - assert!(!(-f64::NAN).is_sign_positive()); -} - -#[test] -fn test_is_sign_negative() { - assert!(!f64::INFINITY.is_sign_negative()); - assert!(!1f64.is_sign_negative()); - assert!(!0f64.is_sign_negative()); - assert!((-0f64).is_sign_negative()); - assert!((-1f64).is_sign_negative()); - assert!(f64::NEG_INFINITY.is_sign_negative()); - assert!((1f64 / f64::NEG_INFINITY).is_sign_negative()); - assert!(!f64::NAN.is_sign_negative()); - assert!((-f64::NAN).is_sign_negative()); -} - -#[test] -fn test_next_up() { - let tiny = f64::from_bits(TINY_BITS); - let tiny_up = f64::from_bits(TINY_UP_BITS); - let max_down = f64::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f64::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f64::from_bits(SMALLEST_NORMAL_BITS); - assert_f64_biteq!(f64::NEG_INFINITY.next_up(), f64::MIN); - assert_f64_biteq!(f64::MIN.next_up(), -max_down); - assert_f64_biteq!((-1.0 - f64::EPSILON).next_up(), -1.0); - assert_f64_biteq!((-smallest_normal).next_up(), -largest_subnormal); - assert_f64_biteq!((-tiny_up).next_up(), -tiny); - assert_f64_biteq!((-tiny).next_up(), -0.0f64); - assert_f64_biteq!((-0.0f64).next_up(), tiny); - assert_f64_biteq!(0.0f64.next_up(), tiny); - assert_f64_biteq!(tiny.next_up(), tiny_up); - assert_f64_biteq!(largest_subnormal.next_up(), smallest_normal); - assert_f64_biteq!(1.0f64.next_up(), 1.0 + f64::EPSILON); - assert_f64_biteq!(f64::MAX.next_up(), f64::INFINITY); - assert_f64_biteq!(f64::INFINITY.next_up(), f64::INFINITY); - - let nan0 = f64::NAN; - let nan1 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK1); - let nan2 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK2); - assert_f64_biteq!(nan0.next_up(), nan0); - assert_f64_biteq!(nan1.next_up(), nan1); - assert_f64_biteq!(nan2.next_up(), nan2); -} - -#[test] -fn test_next_down() { - let tiny = f64::from_bits(TINY_BITS); - let tiny_up = f64::from_bits(TINY_UP_BITS); - let max_down = f64::from_bits(MAX_DOWN_BITS); - let largest_subnormal = f64::from_bits(LARGEST_SUBNORMAL_BITS); - let smallest_normal = f64::from_bits(SMALLEST_NORMAL_BITS); - assert_f64_biteq!(f64::NEG_INFINITY.next_down(), f64::NEG_INFINITY); - assert_f64_biteq!(f64::MIN.next_down(), f64::NEG_INFINITY); - assert_f64_biteq!((-max_down).next_down(), f64::MIN); - assert_f64_biteq!((-1.0f64).next_down(), -1.0 - f64::EPSILON); - assert_f64_biteq!((-largest_subnormal).next_down(), -smallest_normal); - assert_f64_biteq!((-tiny).next_down(), -tiny_up); - assert_f64_biteq!((-0.0f64).next_down(), -tiny); - assert_f64_biteq!((0.0f64).next_down(), -tiny); - assert_f64_biteq!(tiny.next_down(), 0.0f64); - assert_f64_biteq!(tiny_up.next_down(), tiny); - assert_f64_biteq!(smallest_normal.next_down(), largest_subnormal); - assert_f64_biteq!((1.0 + f64::EPSILON).next_down(), 1.0f64); - assert_f64_biteq!(f64::MAX.next_down(), max_down); - assert_f64_biteq!(f64::INFINITY.next_down(), f64::MAX); - - let nan0 = f64::NAN; - let nan1 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK1); - let nan2 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK2); - assert_f64_biteq!(nan0.next_down(), nan0); - assert_f64_biteq!(nan1.next_down(), nan1); - assert_f64_biteq!(nan2.next_down(), nan2); -} - -#[test] -fn test_mul_add() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_approx_eq!(12.3f64.mul_add(4.5, 6.7), 62.05); - assert_approx_eq!((-12.3f64).mul_add(-4.5, -6.7), 48.65); - assert_approx_eq!(0.0f64.mul_add(8.9, 1.2), 1.2); - assert_approx_eq!(3.4f64.mul_add(-0.0, 5.6), 5.6); - assert!(nan.mul_add(7.8, 9.0).is_nan()); - assert_eq!(inf.mul_add(7.8, 9.0), inf); - assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); - assert_eq!(8.9f64.mul_add(inf, 3.2), inf); - assert_eq!((-3.2f64).mul_add(2.4, neg_inf), neg_inf); -} - -#[test] -fn test_recip() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(1.0f64.recip(), 1.0); - assert_eq!(2.0f64.recip(), 0.5); - assert_eq!((-0.4f64).recip(), -2.5); - assert_eq!(0.0f64.recip(), inf); - assert!(nan.recip().is_nan()); - assert_eq!(inf.recip(), 0.0); - assert_eq!(neg_inf.recip(), 0.0); -} - -#[test] -fn test_powi() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(1.0f64.powi(1), 1.0); - assert_approx_eq!((-3.1f64).powi(2), 9.61); - assert_approx_eq!(5.9f64.powi(-2), 0.028727); - assert_eq!(8.3f64.powi(0), 1.0); - assert!(nan.powi(2).is_nan()); - assert_eq!(inf.powi(3), inf); - assert_eq!(neg_inf.powi(2), inf); -} - -#[test] -fn test_powf() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(1.0f64.powf(1.0), 1.0); - assert_approx_eq!(3.4f64.powf(4.5), 246.408183); - assert_approx_eq!(2.7f64.powf(-3.2), 0.041652); - assert_approx_eq!((-3.1f64).powf(2.0), 9.61); - assert_approx_eq!(5.9f64.powf(-2.0), 0.028727); - assert_eq!(8.3f64.powf(0.0), 1.0); - assert!(nan.powf(2.0).is_nan()); - assert_eq!(inf.powf(2.0), inf); - assert_eq!(neg_inf.powf(3.0), neg_inf); -} - -#[test] -fn test_sqrt_domain() { - assert!(f64::NAN.sqrt().is_nan()); - assert!(f64::NEG_INFINITY.sqrt().is_nan()); - assert!((-1.0f64).sqrt().is_nan()); - assert_eq!((-0.0f64).sqrt(), -0.0); - assert_eq!(0.0f64.sqrt(), 0.0); - assert_eq!(1.0f64.sqrt(), 1.0); - assert_eq!(f64::INFINITY.sqrt(), f64::INFINITY); -} - -#[test] -fn test_exp() { - assert_eq!(1.0, 0.0f64.exp()); - assert_approx_eq!(2.718282, 1.0f64.exp()); - assert_approx_eq!(148.413159, 5.0f64.exp()); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(inf, inf.exp()); - assert_eq!(0.0, neg_inf.exp()); - assert!(nan.exp().is_nan()); -} - -#[test] -fn test_exp2() { - assert_eq!(32.0, 5.0f64.exp2()); - assert_eq!(1.0, 0.0f64.exp2()); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(inf, inf.exp2()); - assert_eq!(0.0, neg_inf.exp2()); - assert!(nan.exp2().is_nan()); -} - -#[test] -fn test_ln() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_approx_eq!(1.0f64.exp().ln(), 1.0); - assert!(nan.ln().is_nan()); - assert_eq!(inf.ln(), inf); - assert!(neg_inf.ln().is_nan()); - assert!((-2.3f64).ln().is_nan()); - assert_eq!((-0.0f64).ln(), neg_inf); - assert_eq!(0.0f64.ln(), neg_inf); - assert_approx_eq!(4.0f64.ln(), 1.386294); -} - -#[test] -fn test_log() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(10.0f64.log(10.0), 1.0); - assert_approx_eq!(2.3f64.log(3.5), 0.664858); - assert_eq!(1.0f64.exp().log(1.0f64.exp()), 1.0); - assert!(1.0f64.log(1.0).is_nan()); - assert!(1.0f64.log(-13.9).is_nan()); - assert!(nan.log(2.3).is_nan()); - assert_eq!(inf.log(10.0), inf); - assert!(neg_inf.log(8.8).is_nan()); - assert!((-2.3f64).log(0.1).is_nan()); - assert_eq!((-0.0f64).log(2.0), neg_inf); - assert_eq!(0.0f64.log(7.0), neg_inf); -} - -#[test] -fn test_log2() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_approx_eq!(10.0f64.log2(), 3.321928); - assert_approx_eq!(2.3f64.log2(), 1.201634); - assert_approx_eq!(1.0f64.exp().log2(), 1.442695); - assert!(nan.log2().is_nan()); - assert_eq!(inf.log2(), inf); - assert!(neg_inf.log2().is_nan()); - assert!((-2.3f64).log2().is_nan()); - assert_eq!((-0.0f64).log2(), neg_inf); - assert_eq!(0.0f64.log2(), neg_inf); -} - -#[test] -fn test_log10() { - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(10.0f64.log10(), 1.0); - assert_approx_eq!(2.3f64.log10(), 0.361728); - assert_approx_eq!(1.0f64.exp().log10(), 0.434294); - assert_eq!(1.0f64.log10(), 0.0); - assert!(nan.log10().is_nan()); - assert_eq!(inf.log10(), inf); - assert!(neg_inf.log10().is_nan()); - assert!((-2.3f64).log10().is_nan()); - assert_eq!((-0.0f64).log10(), neg_inf); - assert_eq!(0.0f64.log10(), neg_inf); -} - -#[test] -fn test_to_degrees() { - let pi: f64 = consts::PI; - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(0.0f64.to_degrees(), 0.0); - assert_approx_eq!((-5.8f64).to_degrees(), -332.315521); - assert_eq!(pi.to_degrees(), 180.0); - assert!(nan.to_degrees().is_nan()); - assert_eq!(inf.to_degrees(), inf); - assert_eq!(neg_inf.to_degrees(), neg_inf); -} - -#[test] -fn test_to_radians() { - let pi: f64 = consts::PI; - let nan: f64 = f64::NAN; - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - assert_eq!(0.0f64.to_radians(), 0.0); - assert_approx_eq!(154.6f64.to_radians(), 2.698279); - assert_approx_eq!((-332.31f64).to_radians(), -5.799903); - assert_eq!(180.0f64.to_radians(), pi); - assert!(nan.to_radians().is_nan()); - assert_eq!(inf.to_radians(), inf); - assert_eq!(neg_inf.to_radians(), neg_inf); -} - -#[test] -fn test_asinh() { - assert_eq!(0.0f64.asinh(), 0.0f64); - assert_eq!((-0.0f64).asinh(), -0.0f64); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(inf.asinh(), inf); - assert_eq!(neg_inf.asinh(), neg_inf); - assert!(nan.asinh().is_nan()); - assert!((-0.0f64).asinh().is_sign_negative()); - // issue 63271 - assert_approx_eq!(2.0f64.asinh(), 1.443635475178810342493276740273105f64); - assert_approx_eq!((-2.0f64).asinh(), -1.443635475178810342493276740273105f64); - // regression test for the catastrophic cancellation fixed in 72486 - assert_approx_eq!((-67452098.07139316f64).asinh(), -18.72007542627454439398548429400083); - - // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f64, 60.0f64.sinh().asinh()); - // mul needed for approximate comparison to be meaningful - assert_approx_eq!(1.0f64, 1e-15f64.sinh().asinh() * 1e15f64); -} - -#[test] -fn test_acosh() { - assert_eq!(1.0f64.acosh(), 0.0f64); - assert!(0.999f64.acosh().is_nan()); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(inf.acosh(), inf); - assert!(neg_inf.acosh().is_nan()); - assert!(nan.acosh().is_nan()); - assert_approx_eq!(2.0f64.acosh(), 1.31695789692481670862504634730796844f64); - assert_approx_eq!(3.0f64.acosh(), 1.76274717403908605046521864995958461f64); - - // test for low accuracy from issue 104548 - assert_approx_eq!(60.0f64, 60.0f64.cosh().acosh()); -} - -#[test] -fn test_atanh() { - assert_eq!(0.0f64.atanh(), 0.0f64); - assert_eq!((-0.0f64).atanh(), -0.0f64); - - let inf: f64 = f64::INFINITY; - let neg_inf: f64 = f64::NEG_INFINITY; - let nan: f64 = f64::NAN; - assert_eq!(1.0f64.atanh(), inf); - assert_eq!((-1.0f64).atanh(), neg_inf); - assert!(2f64.atanh().atanh().is_nan()); - assert!((-2f64).atanh().atanh().is_nan()); - assert!(inf.atanh().is_nan()); - assert!(neg_inf.atanh().is_nan()); - assert!(nan.atanh().is_nan()); - assert_approx_eq!(0.5f64.atanh(), 0.54930614433405484569762261846126285f64); - assert_approx_eq!((-0.5f64).atanh(), -0.54930614433405484569762261846126285f64); -} - -#[test] -fn test_gamma() { - // precision can differ between platforms - assert_approx_eq!(1.0f64.gamma(), 1.0f64); - assert_approx_eq!(2.0f64.gamma(), 1.0f64); - assert_approx_eq!(3.0f64.gamma(), 2.0f64); - assert_approx_eq!(4.0f64.gamma(), 6.0f64); - assert_approx_eq!(5.0f64.gamma(), 24.0f64); - assert_approx_eq!(0.5f64.gamma(), consts::PI.sqrt()); - assert_approx_eq!((-0.5f64).gamma(), -2.0 * consts::PI.sqrt()); - assert_eq!(0.0f64.gamma(), f64::INFINITY); - assert_eq!((-0.0f64).gamma(), f64::NEG_INFINITY); - assert!((-1.0f64).gamma().is_nan()); - assert!((-2.0f64).gamma().is_nan()); - assert!(f64::NAN.gamma().is_nan()); - assert!(f64::NEG_INFINITY.gamma().is_nan()); - assert_eq!(f64::INFINITY.gamma(), f64::INFINITY); - assert_eq!(171.71f64.gamma(), f64::INFINITY); -} - -#[test] -fn test_ln_gamma() { - assert_approx_eq!(1.0f64.ln_gamma().0, 0.0f64); - assert_eq!(1.0f64.ln_gamma().1, 1); - assert_approx_eq!(2.0f64.ln_gamma().0, 0.0f64); - assert_eq!(2.0f64.ln_gamma().1, 1); - assert_approx_eq!(3.0f64.ln_gamma().0, 2.0f64.ln()); - assert_eq!(3.0f64.ln_gamma().1, 1); - assert_approx_eq!((-0.5f64).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln()); - assert_eq!((-0.5f64).ln_gamma().1, -1); -} - -#[test] -fn test_real_consts() { - use super::consts; - let pi: f64 = consts::PI; - let frac_pi_2: f64 = consts::FRAC_PI_2; - let frac_pi_3: f64 = consts::FRAC_PI_3; - let frac_pi_4: f64 = consts::FRAC_PI_4; - let frac_pi_6: f64 = consts::FRAC_PI_6; - let frac_pi_8: f64 = consts::FRAC_PI_8; - let frac_1_pi: f64 = consts::FRAC_1_PI; - let frac_2_pi: f64 = consts::FRAC_2_PI; - let frac_2_sqrtpi: f64 = consts::FRAC_2_SQRT_PI; - let sqrt2: f64 = consts::SQRT_2; - let frac_1_sqrt2: f64 = consts::FRAC_1_SQRT_2; - let e: f64 = consts::E; - let log2_e: f64 = consts::LOG2_E; - let log10_e: f64 = consts::LOG10_E; - let ln_2: f64 = consts::LN_2; - let ln_10: f64 = consts::LN_10; - - assert_approx_eq!(frac_pi_2, pi / 2f64); - assert_approx_eq!(frac_pi_3, pi / 3f64); - assert_approx_eq!(frac_pi_4, pi / 4f64); - assert_approx_eq!(frac_pi_6, pi / 6f64); - assert_approx_eq!(frac_pi_8, pi / 8f64); - assert_approx_eq!(frac_1_pi, 1f64 / pi); - assert_approx_eq!(frac_2_pi, 2f64 / pi); - assert_approx_eq!(frac_2_sqrtpi, 2f64 / pi.sqrt()); - assert_approx_eq!(sqrt2, 2f64.sqrt()); - assert_approx_eq!(frac_1_sqrt2, 1f64 / 2f64.sqrt()); - assert_approx_eq!(log2_e, e.log2()); - assert_approx_eq!(log10_e, e.log10()); - assert_approx_eq!(ln_2, 2f64.ln()); - assert_approx_eq!(ln_10, 10f64.ln()); -} - -#[test] -fn test_float_bits_conv() { - assert_eq!((1f64).to_bits(), 0x3ff0000000000000); - assert_eq!((12.5f64).to_bits(), 0x4029000000000000); - assert_eq!((1337f64).to_bits(), 0x4094e40000000000); - assert_eq!((-14.25f64).to_bits(), 0xc02c800000000000); - assert_approx_eq!(f64::from_bits(0x3ff0000000000000), 1.0); - assert_approx_eq!(f64::from_bits(0x4029000000000000), 12.5); - assert_approx_eq!(f64::from_bits(0x4094e40000000000), 1337.0); - assert_approx_eq!(f64::from_bits(0xc02c800000000000), -14.25); - - // Check that NaNs roundtrip their bits regardless of signaling-ness - let masked_nan1 = f64::NAN.to_bits() ^ NAN_MASK1; - let masked_nan2 = f64::NAN.to_bits() ^ NAN_MASK2; - assert!(f64::from_bits(masked_nan1).is_nan()); - assert!(f64::from_bits(masked_nan2).is_nan()); - - assert_eq!(f64::from_bits(masked_nan1).to_bits(), masked_nan1); - assert_eq!(f64::from_bits(masked_nan2).to_bits(), masked_nan2); -} - -#[test] -#[should_panic] -fn test_clamp_min_greater_than_max() { - let _ = 1.0f64.clamp(3.0, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_min_is_nan() { - let _ = 1.0f64.clamp(f64::NAN, 1.0); -} - -#[test] -#[should_panic] -fn test_clamp_max_is_nan() { - let _ = 1.0f64.clamp(3.0, f64::NAN); -} - -#[test] -fn test_total_cmp() { - use core::cmp::Ordering; - - fn quiet_bit_mask() -> u64 { - 1 << (f64::MANTISSA_DIGITS - 2) - } - - fn min_subnorm() -> f64 { - f64::MIN_POSITIVE / f64::powf(2.0, f64::MANTISSA_DIGITS as f64 - 1.0) - } - - fn max_subnorm() -> f64 { - f64::MIN_POSITIVE - min_subnorm() - } - - fn q_nan() -> f64 { - f64::from_bits(f64::NAN.to_bits() | quiet_bit_mask()) - } - - fn s_nan() -> f64 { - f64::from_bits((f64::NAN.to_bits() & !quiet_bit_mask()) + 42) - } - - assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Equal, (-f64::INFINITY).total_cmp(&-f64::INFINITY)); - assert_eq!(Ordering::Equal, (-f64::MAX).total_cmp(&-f64::MAX)); - assert_eq!(Ordering::Equal, (-2.5_f64).total_cmp(&-2.5)); - assert_eq!(Ordering::Equal, (-1.0_f64).total_cmp(&-1.0)); - assert_eq!(Ordering::Equal, (-1.5_f64).total_cmp(&-1.5)); - assert_eq!(Ordering::Equal, (-0.5_f64).total_cmp(&-0.5)); - assert_eq!(Ordering::Equal, (-f64::MIN_POSITIVE).total_cmp(&-f64::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Equal, (-0.0_f64).total_cmp(&-0.0)); - assert_eq!(Ordering::Equal, 0.0_f64.total_cmp(&0.0)); - assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); - assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Equal, f64::MIN_POSITIVE.total_cmp(&f64::MIN_POSITIVE)); - assert_eq!(Ordering::Equal, 0.5_f64.total_cmp(&0.5)); - assert_eq!(Ordering::Equal, 1.0_f64.total_cmp(&1.0)); - assert_eq!(Ordering::Equal, 1.5_f64.total_cmp(&1.5)); - assert_eq!(Ordering::Equal, 2.5_f64.total_cmp(&2.5)); - assert_eq!(Ordering::Equal, f64::MAX.total_cmp(&f64::MAX)); - assert_eq!(Ordering::Equal, f64::INFINITY.total_cmp(&f64::INFINITY)); - assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); - assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY)); - assert_eq!(Ordering::Less, (-f64::INFINITY).total_cmp(&-f64::MAX)); - assert_eq!(Ordering::Less, (-f64::MAX).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-2.5_f64).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-1.5_f64).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-1.0_f64).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-0.5_f64).total_cmp(&-f64::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-f64::MIN_POSITIVE).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-0.0_f64).total_cmp(&0.0)); - assert_eq!(Ordering::Less, 0.0_f64.total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f64::MIN_POSITIVE)); - assert_eq!(Ordering::Less, f64::MIN_POSITIVE.total_cmp(&0.5)); - assert_eq!(Ordering::Less, 0.5_f64.total_cmp(&1.0)); - assert_eq!(Ordering::Less, 1.0_f64.total_cmp(&1.5)); - assert_eq!(Ordering::Less, 1.5_f64.total_cmp(&2.5)); - assert_eq!(Ordering::Less, 2.5_f64.total_cmp(&f64::MAX)); - assert_eq!(Ordering::Less, f64::MAX.total_cmp(&f64::INFINITY)); - assert_eq!(Ordering::Less, f64::INFINITY.total_cmp(&s_nan())); - assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); - - assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); - assert_eq!(Ordering::Greater, (-f64::INFINITY).total_cmp(&-s_nan())); - assert_eq!(Ordering::Greater, (-f64::MAX).total_cmp(&-f64::INFINITY)); - assert_eq!(Ordering::Greater, (-2.5_f64).total_cmp(&-f64::MAX)); - assert_eq!(Ordering::Greater, (-1.5_f64).total_cmp(&-2.5)); - assert_eq!(Ordering::Greater, (-1.0_f64).total_cmp(&-1.5)); - assert_eq!(Ordering::Greater, (-0.5_f64).total_cmp(&-1.0)); - assert_eq!(Ordering::Greater, (-f64::MIN_POSITIVE).total_cmp(&-0.5)); - assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f64::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Greater, (-0.0_f64).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Greater, 0.0_f64.total_cmp(&-0.0)); - assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); - assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); - assert_eq!(Ordering::Greater, f64::MIN_POSITIVE.total_cmp(&max_subnorm())); - assert_eq!(Ordering::Greater, 0.5_f64.total_cmp(&f64::MIN_POSITIVE)); - assert_eq!(Ordering::Greater, 1.0_f64.total_cmp(&0.5)); - assert_eq!(Ordering::Greater, 1.5_f64.total_cmp(&1.0)); - assert_eq!(Ordering::Greater, 2.5_f64.total_cmp(&1.5)); - assert_eq!(Ordering::Greater, f64::MAX.total_cmp(&2.5)); - assert_eq!(Ordering::Greater, f64::INFINITY.total_cmp(&f64::MAX)); - assert_eq!(Ordering::Greater, s_nan().total_cmp(&f64::INFINITY)); - assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MAX)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::INFINITY)); - assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); - - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MIN_POSITIVE)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MAX)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::INFINITY)); - assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); -} diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index fff140f1564fa..c4c8dbccd7a44 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -203,8 +203,8 @@ impl OsString { self } - /// Converts the `OsString` into a byte slice. To convert the byte slice back into an - /// `OsString`, use the [`OsStr::from_encoded_bytes_unchecked`] function. + /// Converts the `OsString` into a byte vector. To convert the byte vector back into an + /// `OsString`, use the [`OsString::from_encoded_bytes_unchecked`] function. /// /// The byte encoding is an unspecified, platform-specific, self-synchronizing superset of UTF-8. /// By being a self-synchronizing superset of UTF-8, this encoding is also a superset of 7-bit @@ -550,7 +550,7 @@ impl OsString { OsStr::from_inner_mut(self.inner.leak()) } - /// Truncate the the `OsString` to the specified length. + /// Truncate the `OsString` to the specified length. /// /// # Panics /// Panics if `len` does not lie on a valid `OsStr` boundary diff --git a/library/std/src/ffi/os_str/tests.rs b/library/std/src/ffi/os_str/tests.rs index cbec44c862646..2572b71fd9ac6 100644 --- a/library/std/src/ffi/os_str/tests.rs +++ b/library/std/src/ffi/os_str/tests.rs @@ -295,7 +295,7 @@ fn clone_to_uninit() { let mut storage = vec![MaybeUninit::::uninit(); size_of_val::(a)]; unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()).cast()) }; - assert_eq!(a.as_encoded_bytes(), unsafe { MaybeUninit::slice_assume_init_ref(&storage) }); + assert_eq!(a.as_encoded_bytes(), unsafe { storage.assume_init_ref() }); let mut b: Box = OsStr::new("world.exe").into(); assert_eq!(size_of_val::(a), size_of_val::(&b)); diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 9b752ed14437c..793946692312b 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -629,9 +629,9 @@ impl File { /// This acquires an exclusive advisory lock; no other file handle to this file may acquire /// another lock. /// - /// If this file handle, or a clone of it, already holds an advisory lock the exact behavior is - /// unspecified and platform dependent, including the possibility that it will deadlock. - /// However, if this method returns, then an exclusive lock is held. + /// If this file handle/descriptor, or a clone of it, already holds an advisory lock the exact + /// behavior is unspecified and platform dependent, including the possibility that it will + /// deadlock. However, if this method returns, then an exclusive lock is held. /// /// If the file not open for writing, it is unspecified whether this function returns an error. /// @@ -639,6 +639,9 @@ impl File { /// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`] /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block. /// + /// The lock will be released when this file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called. + /// /// # Platform-specific behavior /// /// This function currently corresponds to the `flock` function on Unix with the `LOCK_EX` flag, @@ -661,7 +664,7 @@ impl File { /// use std::fs::File; /// /// fn main() -> std::io::Result<()> { - /// let f = File::open("foo.txt")?; + /// let f = File::create("foo.txt")?; /// f.lock()?; /// Ok(()) /// } @@ -671,19 +674,22 @@ impl File { self.inner.lock() } - /// Acquire a shared advisory lock on the file. Blocks until the lock can be acquired. + /// Acquire a shared (non-exclusive) advisory lock on the file. Blocks until the lock can be acquired. /// /// This acquires a shared advisory lock; more than one file handle may hold a shared lock, but - /// none may hold an exclusive lock. + /// none may hold an exclusive lock at the same time. /// - /// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is - /// unspecified and platform dependent, including the possibility that it will deadlock. - /// However, if this method returns, then a shared lock is held. + /// If this file handle/descriptor, or a clone of it, already holds an advisory lock, the exact + /// behavior is unspecified and platform dependent, including the possibility that it will + /// deadlock. However, if this method returns, then a shared lock is held. /// /// Note, this is an advisory lock meant to interact with [`lock`], [`try_lock`], /// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`] /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block. /// + /// The lock will be released when this file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called. + /// /// # Platform-specific behavior /// /// This function currently corresponds to the `flock` function on Unix with the `LOCK_SH` flag, @@ -716,14 +722,18 @@ impl File { self.inner.lock_shared() } - /// Acquire an exclusive advisory lock on the file. Returns `Ok(false)` if the file is locked. + /// Try to acquire an exclusive advisory lock on the file. + /// + /// Returns `Ok(false)` if a different lock is already held on this file (via another + /// handle/descriptor). /// /// This acquires an exclusive advisory lock; no other file handle to this file may acquire /// another lock. /// - /// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is - /// unspecified and platform dependent, including the possibility that it will deadlock. - /// However, if this method returns, then an exclusive lock is held. + /// If this file handle/descriptor, or a clone of it, already holds an advisory lock, the exact + /// behavior is unspecified and platform dependent, including the possibility that it will + /// deadlock. However, if this method returns `Ok(true)`, then it has acquired an exclusive + /// lock. /// /// If the file not open for writing, it is unspecified whether this function returns an error. /// @@ -731,6 +741,9 @@ impl File { /// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`] /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block. /// + /// The lock will be released when this file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called. + /// /// # Platform-specific behavior /// /// This function currently corresponds to the `flock` function on Unix with the `LOCK_EX` and @@ -754,7 +767,7 @@ impl File { /// use std::fs::File; /// /// fn main() -> std::io::Result<()> { - /// let f = File::open("foo.txt")?; + /// let f = File::create("foo.txt")?; /// f.try_lock()?; /// Ok(()) /// } @@ -764,20 +777,25 @@ impl File { self.inner.try_lock() } - /// Acquire a shared advisory lock on the file. - /// Returns `Ok(false)` if the file is exclusively locked. + /// Try to acquire a shared (non-exclusive) advisory lock on the file. + /// + /// Returns `Ok(false)` if an exclusive lock is already held on this file (via another + /// handle/descriptor). /// /// This acquires a shared advisory lock; more than one file handle may hold a shared lock, but - /// none may hold an exclusive lock. + /// none may hold an exclusive lock at the same time. /// /// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is /// unspecified and platform dependent, including the possibility that it will deadlock. - /// However, if this method returns, then a shared lock is held. + /// However, if this method returns `Ok(true)`, then it has acquired a shared lock. /// /// Note, this is an advisory lock meant to interact with [`lock`], [`try_lock`], /// [`try_lock`], and [`unlock`]. Its interactions with other methods, such as [`read`] /// and [`write`] are platform specific, and it may or may not cause non-lockholders to block. /// + /// The lock will be released when this file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called. + /// /// # Platform-specific behavior /// /// This function currently corresponds to the `flock` function on Unix with the `LOCK_SH` and @@ -813,7 +831,12 @@ impl File { /// Release all locks on the file. /// - /// All remaining locks are released when the file handle, and all clones of it, are dropped. + /// All locks are released when the file (along with any other file descriptors/handles + /// duplicated or inherited from it) is closed. This method allows releasing locks without + /// closing the file. + /// + /// If no lock is currently held via this file descriptor/handle, this method may return an + /// error, or may return successfully without taking any action. /// /// # Platform-specific behavior /// @@ -2284,8 +2307,8 @@ impl AsInner for DirEntry { /// /// # Platform-specific behavior /// -/// This function currently corresponds to the `unlink` function on Unix -/// and the `DeleteFile` function on Windows. +/// This function currently corresponds to the `unlink` function on Unix. +/// On Windows, `DeleteFile` is used or `CreateFileW` and `SetInformationByHandle` for readonly files. /// Note that, this [may change in the future][changes]. /// /// [changes]: io#platform-specific-behavior @@ -2402,7 +2425,7 @@ pub fn symlink_metadata>(path: P) -> io::Result { /// # Platform-specific behavior /// /// This function currently corresponds to the `rename` function on Unix -/// and the `SetFileInformationByHandle` function on Windows. +/// and the `MoveFileExW` or `SetFileInformationByHandle` function on Windows. /// /// Because of this, the behavior when both `from` and `to` exist differs. On /// Unix, if `from` is a directory, `to` must also be an (empty) directory. If @@ -2529,6 +2552,7 @@ pub fn copy, Q: AsRef>(from: P, to: Q) -> io::Result { /// limited to just these cases: /// /// * The `original` path is not a file or doesn't exist. +/// * The 'link' path already exists. /// /// # Examples /// diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 28f16da1ed8d2..8e307f57cf9d2 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -14,7 +14,7 @@ use crate::os::unix::fs::symlink as junction_point; use crate::os::windows::fs::{OpenOptionsExt, junction_point, symlink_dir, symlink_file}; use crate::path::Path; use crate::sync::Arc; -use crate::sys_common::io::test::{TempDir, tmpdir}; +use crate::test_helpers::{TempDir, tmpdir}; use crate::time::{Duration, Instant, SystemTime}; use crate::{env, str, thread}; @@ -1384,7 +1384,7 @@ fn file_try_clone() { } #[test] -#[cfg(not(windows))] +#[cfg(not(target_vendor = "win7"))] fn unlink_readonly() { let tmpdir = tmpdir(); let path = tmpdir.join("file"); diff --git a/library/std/src/io/buffered/bufreader/buffer.rs b/library/std/src/io/buffered/bufreader/buffer.rs index 17721090db5de..5251cc302cb49 100644 --- a/library/std/src/io/buffered/bufreader/buffer.rs +++ b/library/std/src/io/buffered/bufreader/buffer.rs @@ -50,7 +50,7 @@ impl Buffer { pub fn buffer(&self) -> &[u8] { // SAFETY: self.pos and self.cap are valid, and self.cap => self.pos, and // that region is initialized because those are all invariants of this type. - unsafe { MaybeUninit::slice_assume_init_ref(self.buf.get_unchecked(self.pos..self.filled)) } + unsafe { self.buf.get_unchecked(self.pos..self.filled).assume_init_ref() } } #[inline] diff --git a/library/std/src/io/copy/tests.rs b/library/std/src/io/copy/tests.rs index 2e0eb6cdce666..25b1ece2745b9 100644 --- a/library/std/src/io/copy/tests.rs +++ b/library/std/src/io/copy/tests.rs @@ -126,6 +126,7 @@ mod io_benches { use crate::io::prelude::*; #[bench] + #[cfg_attr(target_os = "emscripten", ignore)] // no /dev fn bench_copy_buf_reader(b: &mut Bencher) { let mut file_in = File::open("/dev/zero").expect("opening /dev/zero failed"); // use dyn to avoid specializations unrelated to readbuf diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index b2ffeb0f95d0d..08832bbc1e3d1 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -153,7 +153,7 @@ impl Cursor { /// let reference = buff.get_mut(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_mut_cursor", issue = "130801")] + #[rustc_const_stable(feature = "const_mut_cursor", since = "1.86.0")] pub const fn get_mut(&mut self) -> &mut T { &mut self.inner } @@ -201,7 +201,7 @@ impl Cursor { /// assert_eq!(buff.position(), 4); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_mut_cursor", issue = "130801")] + #[rustc_const_stable(feature = "const_mut_cursor", since = "1.86.0")] pub const fn set_position(&mut self, pos: u64) { self.pos = pos; } diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 476c403c21f33..38b723366175f 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -339,7 +339,7 @@ pub enum ErrorKind { #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] NotSeekable, /// Filesystem quota or some other kind of quota was exceeded. - #[stable(feature = "io_error_quota_exceeded", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "io_error_quota_exceeded", since = "1.85.0")] QuotaExceeded, /// File larger than allowed or supported. /// @@ -364,7 +364,7 @@ pub enum ErrorKind { #[stable(feature = "io_error_a_bit_more", since = "1.83.0")] Deadlock, /// Cross-device or cross-filesystem (hard) link or rename. - #[stable(feature = "io_error_crosses_devices", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "io_error_crosses_devices", since = "1.85.0")] CrossesDevices, /// Too many (hard) links to the same filesystem object. /// diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/std/src/io/error/repr_bitpacked.rs index f958a93864603..716da37168d01 100644 --- a/library/std/src/io/error/repr_bitpacked.rs +++ b/library/std/src/io/error/repr_bitpacked.rs @@ -103,7 +103,8 @@ //! the time. use core::marker::PhantomData; -use core::ptr::{self, NonNull}; +use core::num::NonZeroUsize; +use core::ptr::NonNull; use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; @@ -176,7 +177,7 @@ impl Repr { let utagged = ((code as usize) << 32) | TAG_OS; // Safety: `TAG_OS` is not zero, so the result of the `|` is not 0. let res = Self( - unsafe { NonNull::new_unchecked(ptr::without_provenance_mut(utagged)) }, + NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), PhantomData, ); // quickly smoke-check we encoded the right thing (This generally will @@ -193,7 +194,7 @@ impl Repr { let utagged = ((kind as usize) << 32) | TAG_SIMPLE; // Safety: `TAG_SIMPLE` is not zero, so the result of the `|` is not 0. let res = Self( - unsafe { NonNull::new_unchecked(ptr::without_provenance_mut(utagged)) }, + NonNull::without_provenance(unsafe { NonZeroUsize::new_unchecked(utagged) }), PhantomData, ); // quickly smoke-check we encoded the right thing (This generally will diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index b952c85addf65..8239b29884e8e 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -45,6 +45,7 @@ impl Read for &mut R { fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { (**self).read_exact(buf) } + #[inline] fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { (**self).read_buf_exact(cursor) @@ -77,6 +78,11 @@ impl Write for &mut W { (**self).write_all(buf) } + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + (**self).write_all_vectored(bufs) + } + #[inline] fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { (**self).write_fmt(fmt) @@ -89,10 +95,25 @@ impl Seek for &mut S { (**self).seek(pos) } + #[inline] + fn rewind(&mut self) -> io::Result<()> { + (**self).rewind() + } + + #[inline] + fn stream_len(&mut self) -> io::Result { + (**self).stream_len() + } + #[inline] fn stream_position(&mut self) -> io::Result { (**self).stream_position() } + + #[inline] + fn seek_relative(&mut self, offset: i64) -> io::Result<()> { + (**self).seek_relative(offset) + } } #[stable(feature = "rust1", since = "1.0.0")] impl BufRead for &mut B { @@ -106,11 +127,21 @@ impl BufRead for &mut B { (**self).consume(amt) } + #[inline] + fn has_data_left(&mut self) -> io::Result { + (**self).has_data_left() + } + #[inline] fn read_until(&mut self, byte: u8, buf: &mut Vec) -> io::Result { (**self).read_until(byte, buf) } + #[inline] + fn skip_until(&mut self, byte: u8) -> io::Result { + (**self).skip_until(byte) + } + #[inline] fn read_line(&mut self, buf: &mut String) -> io::Result { (**self).read_line(buf) @@ -153,6 +184,7 @@ impl Read for Box { fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { (**self).read_exact(buf) } + #[inline] fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> { (**self).read_buf_exact(cursor) @@ -185,6 +217,11 @@ impl Write for Box { (**self).write_all(buf) } + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + (**self).write_all_vectored(bufs) + } + #[inline] fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { (**self).write_fmt(fmt) @@ -197,10 +234,25 @@ impl Seek for Box { (**self).seek(pos) } + #[inline] + fn rewind(&mut self) -> io::Result<()> { + (**self).rewind() + } + + #[inline] + fn stream_len(&mut self) -> io::Result { + (**self).stream_len() + } + #[inline] fn stream_position(&mut self) -> io::Result { (**self).stream_position() } + + #[inline] + fn seek_relative(&mut self, offset: i64) -> io::Result<()> { + (**self).seek_relative(offset) + } } #[stable(feature = "rust1", since = "1.0.0")] impl BufRead for Box { @@ -214,11 +266,21 @@ impl BufRead for Box { (**self).consume(amt) } + #[inline] + fn has_data_left(&mut self) -> io::Result { + (**self).has_data_left() + } + #[inline] fn read_until(&mut self, byte: u8, buf: &mut Vec) -> io::Result { (**self).read_until(byte, buf) } + #[inline] + fn skip_until(&mut self, byte: u8) -> io::Result { + (**self).skip_until(byte) + } + #[inline] fn read_line(&mut self, buf: &mut String) -> io::Result { (**self).read_line(buf) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 7912f969bbd9f..980ea1478e084 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -310,6 +310,8 @@ pub use self::error::RawOsError; pub use self::error::SimpleMessage; #[unstable(feature = "io_const_error", issue = "133448")] pub use self::error::const_error; +#[unstable(feature = "anonymous_pipe", issue = "127154")] +pub use self::pipe::{PipeReader, PipeWriter, pipe}; #[stable(feature = "is_terminal", since = "1.70.0")] pub use self::stdio::IsTerminal; pub(crate) use self::stdio::attempt_print_to_stderr; @@ -337,11 +339,12 @@ pub(crate) mod copy; mod cursor; mod error; mod impls; +mod pipe; pub mod prelude; mod stdio; mod util; -const DEFAULT_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; +const DEFAULT_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; pub(crate) use stdio::cleanup; @@ -2246,6 +2249,7 @@ fn skip_until(r: &mut R, delim: u8) -> Result { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "IoBufRead")] pub trait BufRead: Read { /// Returns the contents of the internal buffer, filling it with more data /// from the inner reader if it is empty. diff --git a/library/std/src/io/pipe.rs b/library/std/src/io/pipe.rs new file mode 100644 index 0000000000000..266c7bc96389b --- /dev/null +++ b/library/std/src/io/pipe.rs @@ -0,0 +1,260 @@ +use crate::io; +use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner}; + +/// Create an anonymous pipe. +/// +/// # Behavior +/// +/// A pipe is a one-way data channel provided by the OS, which works across processes. A pipe is +/// typically used to communicate between two or more separate processes, as there are better, +/// faster ways to communicate within a single process. +/// +/// In particular: +/// +/// * A read on a [`PipeReader`] blocks until the pipe is non-empty. +/// * A write on a [`PipeWriter`] blocks when the pipe is full. +/// * When all copies of a [`PipeWriter`] are closed, a read on the corresponding [`PipeReader`] +/// returns EOF. +/// * [`PipeWriter`] can be shared, and multiple processes or threads can write to it at once, but +/// writes (above a target-specific threshold) may have their data interleaved. +/// * [`PipeReader`] can be shared, and multiple processes or threads can read it at once. Any +/// given byte will only get consumed by one reader. There are no guarantees about data +/// interleaving. +/// * Portable applications cannot assume any atomicity of messages larger than a single byte. +/// +/// # Platform-specific behavior +/// +/// This function currently corresponds to the `pipe` function on Unix and the +/// `CreatePipe` function on Windows. +/// +/// Note that this [may change in the future][changes]. +/// +/// # Capacity +/// +/// Pipe capacity is platform dependent. To quote the Linux [man page]: +/// +/// > Different implementations have different limits for the pipe capacity. Applications should +/// > not rely on a particular capacity: an application should be designed so that a reading process +/// > consumes data as soon as it is available, so that a writing process does not remain blocked. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(anonymous_pipe)] +/// # #[cfg(miri)] fn main() {} +/// # #[cfg(not(miri))] +/// # fn main() -> std::io::Result<()> { +/// use std::process::Command; +/// use std::io::{pipe, Read, Write}; +/// let (ping_rx, mut ping_tx) = pipe()?; +/// let (mut pong_rx, pong_tx) = pipe()?; +/// +/// // Spawn a process that echoes its input. +/// let mut echo_server = Command::new("cat").stdin(ping_rx).stdout(pong_tx).spawn()?; +/// +/// ping_tx.write_all(b"hello")?; +/// // Close to unblock echo_server's reader. +/// drop(ping_tx); +/// +/// let mut buf = String::new(); +/// // Block until echo_server's writer is closed. +/// pong_rx.read_to_string(&mut buf)?; +/// assert_eq!(&buf, "hello"); +/// +/// echo_server.wait()?; +/// # Ok(()) +/// # } +/// ``` +/// [changes]: io#platform-specific-behavior +/// [man page]: https://man7.org/linux/man-pages/man7/pipe.7.html +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[inline] +pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> { + pipe_inner().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer))) +} + +/// Read end of an anonymous pipe. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[derive(Debug)] +pub struct PipeReader(pub(crate) AnonPipe); + +/// Write end of an anonymous pipe. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[derive(Debug)] +pub struct PipeWriter(pub(crate) AnonPipe); + +impl PipeReader { + /// Create a new [`PipeReader`] instance that shares the same underlying file description. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(anonymous_pipe)] + /// # #[cfg(miri)] fn main() {} + /// # #[cfg(not(miri))] + /// # fn main() -> std::io::Result<()> { + /// use std::fs; + /// use std::io::{pipe, Write}; + /// use std::process::Command; + /// const NUM_SLOT: u8 = 2; + /// const NUM_PROC: u8 = 5; + /// const OUTPUT: &str = "work.txt"; + /// + /// let mut jobs = vec![]; + /// let (reader, mut writer) = pipe()?; + /// + /// // Write NUM_SLOT characters the pipe. + /// writer.write_all(&[b'|'; NUM_SLOT as usize])?; + /// + /// // Spawn several processes that read a character from the pipe, do some work, then + /// // write back to the pipe. When the pipe is empty, the processes block, so only + /// // NUM_SLOT processes can be working at any given time. + /// for _ in 0..NUM_PROC { + /// jobs.push( + /// Command::new("bash") + /// .args(["-c", + /// &format!( + /// "read -n 1\n\ + /// echo -n 'x' >> '{OUTPUT}'\n\ + /// echo -n '|'", + /// ), + /// ]) + /// .stdin(reader.try_clone()?) + /// .stdout(writer.try_clone()?) + /// .spawn()?, + /// ); + /// } + /// + /// // Wait for all jobs to finish. + /// for mut job in jobs { + /// job.wait()?; + /// } + /// + /// // Check our work and clean up. + /// let xs = fs::read_to_string(OUTPUT)?; + /// fs::remove_file(OUTPUT)?; + /// assert_eq!(xs, "x".repeat(NUM_PROC.into())); + /// # Ok(()) + /// # } + /// ``` + #[unstable(feature = "anonymous_pipe", issue = "127154")] + pub fn try_clone(&self) -> io::Result { + self.0.try_clone().map(Self) + } +} + +impl PipeWriter { + /// Create a new [`PipeWriter`] instance that shares the same underlying file description. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(anonymous_pipe)] + /// # #[cfg(miri)] fn main() {} + /// # #[cfg(not(miri))] + /// # fn main() -> std::io::Result<()> { + /// use std::process::Command; + /// use std::io::{pipe, Read}; + /// let (mut reader, writer) = pipe()?; + /// + /// // Spawn a process that writes to stdout and stderr. + /// let mut peer = Command::new("bash") + /// .args([ + /// "-c", + /// "echo -n foo\n\ + /// echo -n bar >&2" + /// ]) + /// .stdout(writer.try_clone()?) + /// .stderr(writer) + /// .spawn()?; + /// + /// // Read and check the result. + /// let mut msg = String::new(); + /// reader.read_to_string(&mut msg)?; + /// assert_eq!(&msg, "foobar"); + /// + /// peer.wait()?; + /// # Ok(()) + /// # } + /// ``` + #[unstable(feature = "anonymous_pipe", issue = "127154")] + pub fn try_clone(&self) -> io::Result { + self.0.try_clone().map(Self) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Read for &PipeReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.0.read_to_end(buf) + } + fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Read for PipeReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.0.read_to_end(buf) + } + fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Write for &PipeWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Write for PipeWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} diff --git a/library/std/src/io/pipe/tests.rs b/library/std/src/io/pipe/tests.rs new file mode 100644 index 0000000000000..f113b157459d3 --- /dev/null +++ b/library/std/src/io/pipe/tests.rs @@ -0,0 +1,18 @@ +use crate::io::{Read, Write, pipe}; + +#[test] +#[cfg(all(any(unix, windows), not(miri)))] +fn pipe_creation_clone_and_rw() { + let (rx, tx) = pipe().unwrap(); + + tx.try_clone().unwrap().write_all(b"12345").unwrap(); + drop(tx); + + let mut rx2 = rx.try_clone().unwrap(); + drop(rx); + + let mut s = String::new(); + rx2.read_to_string(&mut s).unwrap(); + drop(rx2); + assert_eq!(s, "12345"); +} diff --git a/library/std/src/io/stdio/tests.rs b/library/std/src/io/stdio/tests.rs index bf8f3a5adfb6f..e68d8c29fbce2 100644 --- a/library/std/src/io/stdio/tests.rs +++ b/library/std/src/io/stdio/tests.rs @@ -159,7 +159,8 @@ where assert_eq!(rx2.recv().unwrap(), Release2); // release th2 th2.join().unwrap(); th1.join().unwrap(); - assert_eq!(*log.lock().unwrap(), [ - Start1, Acquire1, Start2, Release1, Acquire2, Release2, Acquire1, Release1 - ]); + assert_eq!( + *log.lock().unwrap(), + [Start1, Acquire1, Start2, Release1, Acquire2, Release2, Acquire1, Release1] + ); } diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index 47cbb9614afdd..f64f034cce779 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -7,7 +7,6 @@ use crate::mem::MaybeUninit; use crate::ops::Deref; #[test] -#[cfg_attr(target_os = "emscripten", ignore)] fn read_until() { let mut buf = Cursor::new(&b"12"[..]); let mut v = Vec::new(); @@ -359,7 +358,6 @@ fn chain_zero_length_read_is_not_eof() { } #[bench] -#[cfg_attr(target_os = "emscripten", ignore)] #[cfg_attr(miri, ignore)] // Miri isn't fast... fn bench_read_to_end(b: &mut test::Bencher) { b.iter(|| { diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index b4c4dffc371c1..cb3f864fd4e1e 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -7,6 +7,7 @@ use crate::fmt; use crate::io::{ self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write, }; +use crate::mem::MaybeUninit; /// `Empty` ignores any data written via [`Write`], and will always be empty /// (returning zero bytes) when read via [`Read`]. @@ -182,28 +183,30 @@ pub const fn repeat(byte: u8) -> Repeat { impl Read for Repeat { #[inline] fn read(&mut self, buf: &mut [u8]) -> io::Result { - for slot in &mut *buf { - *slot = self.byte; - } + buf.fill(self.byte); Ok(buf.len()) } - fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { - // SAFETY: No uninit bytes are being written - for slot in unsafe { buf.as_mut() } { - slot.write(self.byte); - } - - let remaining = buf.capacity(); - - // SAFETY: the entire unfilled portion of buf has been initialized - unsafe { - buf.advance_unchecked(remaining); - } + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + buf.fill(self.byte); + Ok(()) + } + #[inline] + fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { + // SAFETY: No uninit bytes are being written. + MaybeUninit::fill(unsafe { buf.as_mut() }, self.byte); + // SAFETY: the entire unfilled portion of buf has been initialized. + unsafe { buf.advance_unchecked(buf.capacity()) }; Ok(()) } + #[inline] + fn read_buf_exact(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.read_buf(buf) + } + /// This function is not supported by `io::Repeat`, because there's no end of its data fn read_to_end(&mut self, _: &mut Vec) -> io::Result { Err(io::Error::from(io::ErrorKind::OutOfMemory)) diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index 0c526eafdf36f..bdd330611de3d 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -398,7 +398,7 @@ mod enum_keyword {} /// The mirror use case of FFI is also done via the `extern` keyword: /// /// ```rust -/// #[no_mangle] +/// #[unsafe(no_mangle)] /// pub extern "C" fn callable_from_c(x: i32) -> bool { /// x % 3 == 0 /// } @@ -651,16 +651,24 @@ mod if_keyword {} #[doc(keyword = "impl")] // -/// Implement some functionality for a type. +/// Implementations of functionality for a type, or a type implementing some functionality. +/// +/// There are two uses of the keyword `impl`: +/// * An `impl` block is an item that is used to implement some functionality for a type. +/// * An `impl Trait` in a type-position can be used to designate a type that implements a trait called `Trait`. +/// +/// # Implementing Functionality for a Type /// /// The `impl` keyword is primarily used to define implementations on types. Inherent /// implementations are standalone, while trait implementations are used to implement traits for /// types, or other traits. /// -/// Functions and consts can both be defined in an implementation. A function defined in an -/// `impl` block can be standalone, meaning it would be called like `Foo::bar()`. If the function +/// An implementation consists of definitions of functions and consts. A function defined in an +/// `impl` block can be standalone, meaning it would be called like `Vec::new()`. If the function /// takes `self`, `&self`, or `&mut self` as its first argument, it can also be called using -/// method-call syntax, a familiar feature to any object oriented programmer, like `foo.bar()`. +/// method-call syntax, a familiar feature to any object-oriented programmer, like `vec.len()`. +/// +/// ## Inherent Implementations /// /// ```rust /// struct Example { @@ -680,6 +688,17 @@ mod if_keyword {} /// self.number /// } /// } +/// ``` +/// +/// It matters little where an inherent implementation is defined; +/// its functionality is in scope wherever its implementing type is. +/// +/// ## Trait Implementations +/// +/// ```rust +/// struct Example { +/// number: i32, +/// } /// /// trait Thingy { /// fn do_thingy(&self); @@ -692,11 +711,19 @@ mod if_keyword {} /// } /// ``` /// +/// It matters little where a trait implementation is defined; +/// its functionality can be brought into scope by importing the trait it implements. +/// /// For more information on implementations, see the [Rust book][book1] or the [Reference]. /// -/// The other use of the `impl` keyword is in `impl Trait` syntax, which can be seen as a shorthand -/// for "a concrete type that implements this trait". Its primary use is working with closures, -/// which have type definitions generated at compile time that can't be simply typed out. +/// # Designating a Type that Implements Some Functionality +/// +/// The other use of the `impl` keyword is in `impl Trait` syntax, which can be understood to mean +/// "any (or some) concrete type that implements Trait". +/// It can be used as the type of a variable declaration, +/// in [argument position](https://rust-lang.github.io/rfcs/1951-expand-impl-trait.html) +/// or in [return position](https://rust-lang.github.io/rfcs/3425-return-position-impl-trait-in-traits.html). +/// One pertinent use case is in working with closures, which have unnameable types. /// /// ```rust /// fn thing_returning_closure() -> impl Fn(i32) -> bool { @@ -1401,7 +1428,7 @@ mod self_upper_keyword {} /// /// ```rust,no_run /// # #![allow(dead_code)] -/// extern "C" { +/// unsafe extern "C" { /// static mut ERROR_MESSAGE: *mut std::os::raw::c_char; /// } /// ``` @@ -1898,7 +1925,7 @@ mod type_keyword {} /// /// unsafe fn unsafe_fn() {} /// -/// extern "C" { +/// unsafe extern "C" { /// fn unsafe_extern_fn(); /// static BAR: *mut u32; /// } @@ -2387,13 +2414,12 @@ mod async_keyword {} /// [`async`]: ../std/keyword.async.html mod await_keyword {} -// FIXME(dyn_compat_renaming): Update URL and link text. #[doc(keyword = "dyn")] // /// `dyn` is a prefix of a [trait object]'s type. /// /// The `dyn` keyword is used to highlight that calls to methods on the associated `Trait` -/// are [dynamically dispatched]. To use the trait this way, it must be 'dyn-compatible'[^1]. +/// are [dynamically dispatched]. To use the trait this way, it must be *dyn compatible*[^1]. /// /// Unlike generic parameters or `impl Trait`, the compiler does not know the concrete type that /// is being passed. That is, the type has been [erased]. @@ -2406,7 +2432,7 @@ mod await_keyword {} /// the function pointer and then that function pointer is called. /// /// See the Reference for more information on [trait objects][ref-trait-obj] -/// and [object safety][ref-obj-safety]. +/// and [dyn compatibility][ref-dyn-compat]. /// /// ## Trade-offs /// @@ -2419,9 +2445,9 @@ mod await_keyword {} /// [trait object]: ../book/ch17-02-trait-objects.html /// [dynamically dispatched]: https://en.wikipedia.org/wiki/Dynamic_dispatch /// [ref-trait-obj]: ../reference/types/trait-object.html -/// [ref-obj-safety]: ../reference/items/traits.html#object-safety +/// [ref-dyn-compat]: ../reference/items/traits.html#dyn-compatibility /// [erased]: https://en.wikipedia.org/wiki/Type_erasure -/// [^1]: Formerly known as 'object safe'. +/// [^1]: Formerly known as *object safe*. mod dyn_keyword {} #[doc(keyword = "union")] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 2f8f5c5c58180..750116c6269dd 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -272,6 +272,9 @@ // // Language features: // tidy-alphabetical-start + +// stabilization was reverted after it hit beta +#![cfg_attr(not(bootstrap), feature(extended_varargs_abi_support))] #![feature(alloc_error_handler)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] @@ -299,6 +302,7 @@ #![feature(link_cfg)] #![feature(linkage)] #![feature(macro_metavar_expr_concat)] +#![feature(maybe_uninit_fill)] #![feature(min_specialization)] #![feature(must_not_suspend)] #![feature(needs_panic_runtime)] @@ -320,6 +324,8 @@ // Library features (core): // tidy-alphabetical-start #![feature(array_chunks)] +#![feature(bstr)] +#![feature(bstr_internals)] #![feature(c_str_module)] #![feature(char_internals)] #![feature(clone_to_uninit)] @@ -333,7 +339,6 @@ #![feature(extend_one)] #![feature(float_gamma)] #![feature(float_minimum_maximum)] -#![feature(float_next_up_down)] #![feature(fmt_internals)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] @@ -342,6 +347,7 @@ #![feature(lazy_get)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_write_slice)] +#![feature(nonnull_provenance)] #![feature(panic_can_unwind)] #![feature(panic_internals)] #![feature(pin_coerce_unsized_trait)] @@ -357,6 +363,7 @@ #![feature(str_internals)] #![feature(strict_provenance_atomic_ptr)] #![feature(sync_unsafe_cell)] +#![feature(temporary_niche_types)] #![feature(ub_checks)] #![feature(used_with_arg)] // tidy-alphabetical-end @@ -398,7 +405,6 @@ #![feature(custom_test_frameworks)] #![feature(edition_panic)] #![feature(format_args_nl)] -#![feature(get_many_mut)] #![feature(log_syntax)] #![feature(test)] #![feature(trace_macros)] @@ -527,6 +533,8 @@ pub use core::option; pub use core::pin; #[stable(feature = "rust1", since = "1.0.0")] pub use core::ptr; +#[unstable(feature = "new_range_api", issue = "125687")] +pub use core::range; #[stable(feature = "rust1", since = "1.0.0")] pub use core::result; #[stable(feature = "rust1", since = "1.0.0")] @@ -580,6 +588,8 @@ pub mod f64; pub mod thread; pub mod ascii; pub mod backtrace; +#[unstable(feature = "bstr", issue = "134915")] +pub mod bstr; pub mod collections; pub mod env; pub mod error; @@ -594,8 +604,6 @@ pub mod panic; #[unstable(feature = "pattern_type_macro", issue = "123646")] pub mod pat; pub mod path; -#[unstable(feature = "anonymous_pipe", issue = "127154")] -pub mod pipe; pub mod process; #[unstable(feature = "random", issue = "130703")] pub mod random; @@ -734,27 +742,4 @@ mod sealed { #[cfg(test)] #[allow(dead_code)] // Not used in all configurations. -pub(crate) mod test_helpers { - /// Test-only replacement for `rand::thread_rng()`, which is unusable for - /// us, as we want to allow running stdlib tests on tier-3 targets which may - /// not have `getrandom` support. - /// - /// Does a bit of a song and dance to ensure that the seed is different on - /// each call (as some tests sadly rely on this), but doesn't try that hard. - /// - /// This is duplicated in the `core`, `alloc` test suites (as well as - /// `std`'s integration tests), but figuring out a mechanism to share these - /// seems far more painful than copy-pasting a 7 line function a couple - /// times, given that even under a perma-unstable feature, I don't think we - /// want to expose types from `rand` from `std`. - #[track_caller] - pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { - use core::hash::{BuildHasher, Hash, Hasher}; - let mut hasher = crate::hash::RandomState::new().build_hasher(); - core::panic::Location::caller().hash(&mut hasher); - let hc64 = hasher.finish(); - let seed_vec = hc64.to_le_bytes().into_iter().chain(0u8..8).collect::>(); - let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap(); - rand::SeedableRng::from_seed(seed) - } -} +pub(crate) mod test_helpers; diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index 1b0d7f3dbf2c9..e0f9f0bb5cee4 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -372,18 +372,3 @@ macro_rules! dbg { ($($crate::dbg!($val)),+,) }; } - -/// Verify that floats are within a tolerance of each other, 1.0e-6 by default. -#[cfg(test)] -macro_rules! assert_approx_eq { - ($a:expr, $b:expr) => {{ assert_approx_eq!($a, $b, 1.0e-6) }}; - ($a:expr, $b:expr, $lim:expr) => {{ - let (a, b) = (&$a, &$b); - let diff = (*a - *b).abs(); - assert!( - diff < $lim, - "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, difference {diff:?})", - lim = $lim - ); - }}; -} diff --git a/library/std/src/net/ip_addr.rs b/library/std/src/net/ip_addr.rs index 4d673a1d66db6..7262899b3bbbe 100644 --- a/library/std/src/net/ip_addr.rs +++ b/library/std/src/net/ip_addr.rs @@ -8,32 +8,3 @@ pub use core::net::IpAddr; pub use core::net::Ipv6MulticastScope; #[stable(feature = "rust1", since = "1.0.0")] pub use core::net::{Ipv4Addr, Ipv6Addr}; - -use crate::sys::net::netc as c; -use crate::sys_common::{FromInner, IntoInner}; - -impl IntoInner for Ipv4Addr { - #[inline] - fn into_inner(self) -> c::in_addr { - // `s_addr` is stored as BE on all machines and the array is in BE order. - // So the native endian conversion method is used so that it's never swapped. - c::in_addr { s_addr: u32::from_ne_bytes(self.octets()) } - } -} -impl FromInner for Ipv4Addr { - fn from_inner(addr: c::in_addr) -> Ipv4Addr { - Ipv4Addr::from(addr.s_addr.to_ne_bytes()) - } -} - -impl IntoInner for Ipv6Addr { - fn into_inner(self) -> c::in6_addr { - c::in6_addr { s6_addr: self.octets() } - } -} -impl FromInner for Ipv6Addr { - #[inline] - fn from_inner(addr: c::in6_addr) -> Ipv6Addr { - Ipv6Addr::from(addr.s6_addr) - } -} diff --git a/library/std/src/net/socket_addr.rs b/library/std/src/net/socket_addr.rs index ba9c948a2e96f..4c8905c0d4609 100644 --- a/library/std/src/net/socket_addr.rs +++ b/library/std/src/net/socket_addr.rs @@ -6,51 +6,8 @@ mod tests; pub use core::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -use crate::sys::net::netc as c; -use crate::sys_common::net::LookupHost; -use crate::sys_common::{FromInner, IntoInner}; -use crate::{io, iter, mem, option, slice, vec}; - -impl FromInner for SocketAddrV4 { - fn from_inner(addr: c::sockaddr_in) -> SocketAddrV4 { - SocketAddrV4::new(Ipv4Addr::from_inner(addr.sin_addr), u16::from_be(addr.sin_port)) - } -} - -impl FromInner for SocketAddrV6 { - fn from_inner(addr: c::sockaddr_in6) -> SocketAddrV6 { - SocketAddrV6::new( - Ipv6Addr::from_inner(addr.sin6_addr), - u16::from_be(addr.sin6_port), - addr.sin6_flowinfo, - addr.sin6_scope_id, - ) - } -} - -impl IntoInner for SocketAddrV4 { - fn into_inner(self) -> c::sockaddr_in { - c::sockaddr_in { - sin_family: c::AF_INET as c::sa_family_t, - sin_port: self.port().to_be(), - sin_addr: self.ip().into_inner(), - ..unsafe { mem::zeroed() } - } - } -} - -impl IntoInner for SocketAddrV6 { - fn into_inner(self) -> c::sockaddr_in6 { - c::sockaddr_in6 { - sin6_family: c::AF_INET6 as c::sa_family_t, - sin6_port: self.port().to_be(), - sin6_addr: self.ip().into_inner(), - sin6_flowinfo: self.flowinfo(), - sin6_scope_id: self.scope_id(), - ..unsafe { mem::zeroed() } - } - } -} +use crate::sys::net::LookupHost; +use crate::{io, iter, option, slice, vec}; /// A trait for objects which can be converted or resolved to one or more /// [`SocketAddr`] values. diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 67a0f7e439d55..9b68f872955c0 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -15,7 +15,8 @@ use crate::io::prelude::*; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::iter::FusedIterator; use crate::net::{Shutdown, SocketAddr, ToSocketAddrs}; -use crate::sys_common::{AsInner, FromInner, IntoInner, net as net_imp}; +use crate::sys::net as net_imp; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; /// A TCP stream between a local and a remote socket. diff --git a/library/std/src/net/test.rs b/library/std/src/net/test.rs index d318d457f3569..a5c3983cd89ec 100644 --- a/library/std/src/net/test.rs +++ b/library/std/src/net/test.rs @@ -5,14 +5,15 @@ use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToS use crate::sync::atomic::{AtomicUsize, Ordering}; static PORT: AtomicUsize = AtomicUsize::new(0); +const BASE_PORT: u16 = 19600; pub fn next_test_ip4() -> SocketAddr { - let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + base_port(); + let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + BASE_PORT; SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port)) } pub fn next_test_ip6() -> SocketAddr { - let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + base_port(); + let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + BASE_PORT; SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), port, 0, 0)) } @@ -30,31 +31,3 @@ pub fn tsa(a: A) -> Result, String> { Err(e) => Err(e.to_string()), } } - -// The bots run multiple builds at the same time, and these builds -// all want to use ports. This function figures out which workspace -// it is running in and assigns a port range based on it. -fn base_port() -> u16 { - let cwd = if cfg!(target_env = "sgx") { - String::from("sgx") - } else { - env::current_dir().unwrap().into_os_string().into_string().unwrap() - }; - let dirs = [ - "32-opt", - "32-nopt", - "musl-64-opt", - "cross-opt", - "64-opt", - "64-nopt", - "64-opt-vg", - "64-debug-opt", - "all-opt", - "snap3", - "dist", - "sgx", - ]; - dirs.iter().enumerate().find(|&(_, dir)| cwd.contains(dir)).map(|p| p.0).unwrap_or(0) as u16 - * 1000 - + 19600 -} diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs index 674c5fb7d6ea3..3eb798ad34aaa 100644 --- a/library/std/src/net/udp.rs +++ b/library/std/src/net/udp.rs @@ -12,7 +12,8 @@ mod tests; use crate::fmt; use crate::io::{self, ErrorKind}; use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; -use crate::sys_common::{AsInner, FromInner, IntoInner, net as net_imp}; +use crate::sys::net as net_imp; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; /// A UDP socket. diff --git a/library/std/src/num.rs b/library/std/src/num.rs index d2f679e7dde54..ffb8789c906ef 100644 --- a/library/std/src/num.rs +++ b/library/std/src/num.rs @@ -6,9 +6,6 @@ #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] -#[cfg(test)] -mod tests; - #[stable(feature = "int_error_matching", since = "1.55.0")] pub use core::num::IntErrorKind; #[stable(feature = "generic_nonzero", since = "1.79.0")] @@ -29,28 +26,3 @@ pub use core::num::{FpCategory, ParseFloatError, ParseIntError, TryFromIntError} pub use core::num::{NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize}; #[stable(feature = "nonzero", since = "1.28.0")] pub use core::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize}; - -#[cfg(test)] -use crate::fmt; -#[cfg(test)] -use crate::ops::{Add, Div, Mul, Rem, Sub}; - -/// Helper function for testing numeric operations -#[cfg(test)] -pub fn test_num(ten: T, two: T) -where - T: PartialEq - + Add - + Sub - + Mul - + Div - + Rem - + fmt::Debug - + Copy, -{ - assert_eq!(ten.add(two), ten + two); - assert_eq!(ten.sub(two), ten - two); - assert_eq!(ten.mul(two), ten * two); - assert_eq!(ten.div(two), ten / two); - assert_eq!(ten.rem(two), ten % two); -} diff --git a/library/std/src/num/tests.rs b/library/std/src/num/tests.rs deleted file mode 100644 index df0df3f23f756..0000000000000 --- a/library/std/src/num/tests.rs +++ /dev/null @@ -1,230 +0,0 @@ -use crate::ops::Mul; - -#[test] -fn test_saturating_add_uint() { - assert_eq!(3_usize.saturating_add(5_usize), 8_usize); - assert_eq!(3_usize.saturating_add(usize::MAX - 1), usize::MAX); - assert_eq!(usize::MAX.saturating_add(usize::MAX), usize::MAX); - assert_eq!((usize::MAX - 2).saturating_add(1), usize::MAX - 1); -} - -#[test] -fn test_saturating_sub_uint() { - assert_eq!(5_usize.saturating_sub(3_usize), 2_usize); - assert_eq!(3_usize.saturating_sub(5_usize), 0_usize); - assert_eq!(0_usize.saturating_sub(1_usize), 0_usize); - assert_eq!((usize::MAX - 1).saturating_sub(usize::MAX), 0); -} - -#[test] -fn test_saturating_add_int() { - assert_eq!(3i32.saturating_add(5), 8); - assert_eq!(3isize.saturating_add(isize::MAX - 1), isize::MAX); - assert_eq!(isize::MAX.saturating_add(isize::MAX), isize::MAX); - assert_eq!((isize::MAX - 2).saturating_add(1), isize::MAX - 1); - assert_eq!(3i32.saturating_add(-5), -2); - assert_eq!(isize::MIN.saturating_add(-1), isize::MIN); - assert_eq!((-2isize).saturating_add(-isize::MAX), isize::MIN); -} - -#[test] -fn test_saturating_sub_int() { - assert_eq!(3i32.saturating_sub(5), -2); - assert_eq!(isize::MIN.saturating_sub(1), isize::MIN); - assert_eq!((-2isize).saturating_sub(isize::MAX), isize::MIN); - assert_eq!(3i32.saturating_sub(-5), 8); - assert_eq!(3isize.saturating_sub(-(isize::MAX - 1)), isize::MAX); - assert_eq!(isize::MAX.saturating_sub(-isize::MAX), isize::MAX); - assert_eq!((isize::MAX - 2).saturating_sub(-1), isize::MAX - 1); -} - -#[test] -fn test_checked_add() { - let five_less = usize::MAX - 5; - assert_eq!(five_less.checked_add(0), Some(usize::MAX - 5)); - assert_eq!(five_less.checked_add(1), Some(usize::MAX - 4)); - assert_eq!(five_less.checked_add(2), Some(usize::MAX - 3)); - assert_eq!(five_less.checked_add(3), Some(usize::MAX - 2)); - assert_eq!(five_less.checked_add(4), Some(usize::MAX - 1)); - assert_eq!(five_less.checked_add(5), Some(usize::MAX)); - assert_eq!(five_less.checked_add(6), None); - assert_eq!(five_less.checked_add(7), None); -} - -#[test] -fn test_checked_sub() { - assert_eq!(5_usize.checked_sub(0), Some(5)); - assert_eq!(5_usize.checked_sub(1), Some(4)); - assert_eq!(5_usize.checked_sub(2), Some(3)); - assert_eq!(5_usize.checked_sub(3), Some(2)); - assert_eq!(5_usize.checked_sub(4), Some(1)); - assert_eq!(5_usize.checked_sub(5), Some(0)); - assert_eq!(5_usize.checked_sub(6), None); - assert_eq!(5_usize.checked_sub(7), None); -} - -#[test] -fn test_checked_mul() { - let third = usize::MAX / 3; - assert_eq!(third.checked_mul(0), Some(0)); - assert_eq!(third.checked_mul(1), Some(third)); - assert_eq!(third.checked_mul(2), Some(third * 2)); - assert_eq!(third.checked_mul(3), Some(third * 3)); - assert_eq!(third.checked_mul(4), None); -} - -macro_rules! test_is_power_of_two { - ($test_name:ident, $T:ident) => { - #[test] - fn $test_name() { - assert_eq!((0 as $T).is_power_of_two(), false); - assert_eq!((1 as $T).is_power_of_two(), true); - assert_eq!((2 as $T).is_power_of_two(), true); - assert_eq!((3 as $T).is_power_of_two(), false); - assert_eq!((4 as $T).is_power_of_two(), true); - assert_eq!((5 as $T).is_power_of_two(), false); - assert_eq!(($T::MAX / 2 + 1).is_power_of_two(), true); - } - }; -} - -test_is_power_of_two! { test_is_power_of_two_u8, u8 } -test_is_power_of_two! { test_is_power_of_two_u16, u16 } -test_is_power_of_two! { test_is_power_of_two_u32, u32 } -test_is_power_of_two! { test_is_power_of_two_u64, u64 } -test_is_power_of_two! { test_is_power_of_two_uint, usize } - -macro_rules! test_next_power_of_two { - ($test_name:ident, $T:ident) => { - #[test] - fn $test_name() { - assert_eq!((0 as $T).next_power_of_two(), 1); - let mut next_power = 1; - for i in 1 as $T..40 { - assert_eq!(i.next_power_of_two(), next_power); - if i == next_power { - next_power *= 2 - } - } - } - }; -} - -test_next_power_of_two! { test_next_power_of_two_u8, u8 } -test_next_power_of_two! { test_next_power_of_two_u16, u16 } -test_next_power_of_two! { test_next_power_of_two_u32, u32 } -test_next_power_of_two! { test_next_power_of_two_u64, u64 } -test_next_power_of_two! { test_next_power_of_two_uint, usize } - -macro_rules! test_checked_next_power_of_two { - ($test_name:ident, $T:ident) => { - #[test] - fn $test_name() { - assert_eq!((0 as $T).checked_next_power_of_two(), Some(1)); - let smax = $T::MAX >> 1; - assert_eq!(smax.checked_next_power_of_two(), Some(smax + 1)); - assert_eq!((smax + 1).checked_next_power_of_two(), Some(smax + 1)); - assert_eq!((smax + 2).checked_next_power_of_two(), None); - assert_eq!(($T::MAX - 1).checked_next_power_of_two(), None); - assert_eq!($T::MAX.checked_next_power_of_two(), None); - let mut next_power = 1; - for i in 1 as $T..40 { - assert_eq!(i.checked_next_power_of_two(), Some(next_power)); - if i == next_power { - next_power *= 2 - } - } - } - }; -} - -test_checked_next_power_of_two! { test_checked_next_power_of_two_u8, u8 } -test_checked_next_power_of_two! { test_checked_next_power_of_two_u16, u16 } -test_checked_next_power_of_two! { test_checked_next_power_of_two_u32, u32 } -test_checked_next_power_of_two! { test_checked_next_power_of_two_u64, u64 } -test_checked_next_power_of_two! { test_checked_next_power_of_two_uint, usize } - -#[test] -fn test_pow() { - fn naive_pow + Copy>(one: T, base: T, exp: usize) -> T { - (0..exp).fold(one, |acc, _| acc * base) - } - macro_rules! assert_pow { - (($num:expr, $exp:expr) => $expected:expr) => {{ - let result = $num.pow($exp); - assert_eq!(result, $expected); - assert_eq!(result, naive_pow(1, $num, $exp)); - }}; - } - assert_pow!((3u32, 0 ) => 1); - assert_pow!((5u32, 1 ) => 5); - assert_pow!((-4i32, 2 ) => 16); - assert_pow!((8u32, 3 ) => 512); - assert_pow!((2u64, 50) => 1125899906842624); -} - -#[test] -fn test_uint_to_str_overflow() { - let mut u8_val: u8 = 255; - assert_eq!(u8_val.to_string(), "255"); - - u8_val = u8_val.wrapping_add(1); - assert_eq!(u8_val.to_string(), "0"); - - let mut u16_val: u16 = 65_535; - assert_eq!(u16_val.to_string(), "65535"); - - u16_val = u16_val.wrapping_add(1); - assert_eq!(u16_val.to_string(), "0"); - - let mut u32_val: u32 = 4_294_967_295; - assert_eq!(u32_val.to_string(), "4294967295"); - - u32_val = u32_val.wrapping_add(1); - assert_eq!(u32_val.to_string(), "0"); - - let mut u64_val: u64 = 18_446_744_073_709_551_615; - assert_eq!(u64_val.to_string(), "18446744073709551615"); - - u64_val = u64_val.wrapping_add(1); - assert_eq!(u64_val.to_string(), "0"); -} - -fn from_str(t: &str) -> Option { - crate::str::FromStr::from_str(t).ok() -} - -#[test] -fn test_uint_from_str_overflow() { - let mut u8_val: u8 = 255; - assert_eq!(from_str::("255"), Some(u8_val)); - assert_eq!(from_str::("256"), None); - - u8_val = u8_val.wrapping_add(1); - assert_eq!(from_str::("0"), Some(u8_val)); - assert_eq!(from_str::("-1"), None); - - let mut u16_val: u16 = 65_535; - assert_eq!(from_str::("65535"), Some(u16_val)); - assert_eq!(from_str::("65536"), None); - - u16_val = u16_val.wrapping_add(1); - assert_eq!(from_str::("0"), Some(u16_val)); - assert_eq!(from_str::("-1"), None); - - let mut u32_val: u32 = 4_294_967_295; - assert_eq!(from_str::("4294967295"), Some(u32_val)); - assert_eq!(from_str::("4294967296"), None); - - u32_val = u32_val.wrapping_add(1); - assert_eq!(from_str::("0"), Some(u32_val)); - assert_eq!(from_str::("-1"), None); - - let mut u64_val: u64 = 18_446_744_073_709_551_615; - assert_eq!(from_str::("18446744073709551615"), Some(u64_val)); - assert_eq!(from_str::("18446744073709551616"), None); - - u64_val = u64_val.wrapping_add(1); - assert_eq!(from_str::("0"), Some(u64_val)); - assert_eq!(from_str::("-1"), None); -} diff --git a/library/std/src/os/fd/net.rs b/library/std/src/os/fd/net.rs index 843f45f7f5f98..34479ca0e190e 100644 --- a/library/std/src/os/fd/net.rs +++ b/library/std/src/os/fd/net.rs @@ -1,6 +1,6 @@ use crate::os::fd::owned::OwnedFd; use crate::os::fd::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -use crate::sys_common::{self, AsInner, FromInner, IntoInner}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::{net, sys}; macro_rules! impl_as_raw_fd { @@ -24,7 +24,7 @@ macro_rules! impl_from_raw_fd { unsafe fn from_raw_fd(fd: RawFd) -> net::$t { unsafe { let socket = sys::net::Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd))); - net::$t::from_inner(sys_common::net::$t::from_inner(socket)) + net::$t::from_inner(sys::net::$t::from_inner(socket)) } } } diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index abb13b75f5087..5cec11ecccf1c 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -11,6 +11,8 @@ use crate::sys::cvt; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::{fmt, fs, io}; +type ValidRawFd = core::num::niche_types::NotAllOnes; + /// A borrowed file descriptor. /// /// This has a lifetime parameter to tie it to the lifetime of something that owns the file @@ -32,15 +34,10 @@ use crate::{fmt, fs, io}; /// instead, but this is not supported on all platforms. #[derive(Copy, Clone)] #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a -// 32-bit c_int. Below is -2, in two's complement, but that only works out -// because c_int is 32 bits. -#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] #[rustc_nonnull_optimization_guaranteed] #[stable(feature = "io_safety", since = "1.63.0")] pub struct BorrowedFd<'fd> { - fd: RawFd, + fd: ValidRawFd, _phantom: PhantomData<&'fd OwnedFd>, } @@ -56,15 +53,10 @@ pub struct BorrowedFd<'fd> { /// /// You can use [`AsFd::as_fd`] to obtain a [`BorrowedFd`]. #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a -// 32-bit c_int. Below is -2, in two's complement, but that only works out -// because c_int is 32 bits. -#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] #[rustc_nonnull_optimization_guaranteed] #[stable(feature = "io_safety", since = "1.63.0")] pub struct OwnedFd { - fd: RawFd, + fd: ValidRawFd, } impl BorrowedFd<'_> { @@ -75,12 +67,11 @@ impl BorrowedFd<'_> { /// The resource pointed to by `fd` must remain open for the duration of /// the returned `BorrowedFd`, and it must not have the value `-1`. #[inline] + #[track_caller] #[rustc_const_stable(feature = "io_safety", since = "1.63.0")] #[stable(feature = "io_safety", since = "1.63.0")] pub const unsafe fn borrow_raw(fd: RawFd) -> Self { - assert!(fd != u32::MAX as RawFd); - // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - unsafe { Self { fd, _phantom: PhantomData } } + Self { fd: ValidRawFd::new(fd).expect("fd != -1"), _phantom: PhantomData } } } @@ -130,7 +121,7 @@ impl BorrowedFd<'_> { impl AsRawFd for BorrowedFd<'_> { #[inline] fn as_raw_fd(&self) -> RawFd { - self.fd + self.fd.as_inner() } } @@ -138,7 +129,7 @@ impl AsRawFd for BorrowedFd<'_> { impl AsRawFd for OwnedFd { #[inline] fn as_raw_fd(&self) -> RawFd { - self.fd + self.fd.as_inner() } } @@ -146,7 +137,7 @@ impl AsRawFd for OwnedFd { impl IntoRawFd for OwnedFd { #[inline] fn into_raw_fd(self) -> RawFd { - ManuallyDrop::new(self).fd + ManuallyDrop::new(self).fd.as_inner() } } @@ -161,10 +152,9 @@ impl FromRawFd for OwnedFd { /// /// [io-safety]: io#io-safety #[inline] + #[track_caller] unsafe fn from_raw_fd(fd: RawFd) -> Self { - assert_ne!(fd, u32::MAX as RawFd); - // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - unsafe { Self { fd } } + Self { fd: ValidRawFd::new(fd).expect("fd != -1") } } } @@ -187,12 +177,12 @@ impl Drop for OwnedFd { #[cfg(not(target_os = "hermit"))] { #[cfg(unix)] - crate::sys::fs::debug_assert_fd_is_open(self.fd); + crate::sys::fs::debug_assert_fd_is_open(self.fd.as_inner()); - let _ = libc::close(self.fd); + let _ = libc::close(self.fd.as_inner()); } #[cfg(target_os = "hermit")] - let _ = hermit_abi::close(self.fd); + let _ = hermit_abi::close(self.fd.as_inner()); } } } diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index 22f5528248a32..03dff94350dad 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -19,11 +19,9 @@ use crate::sys_common::{AsInner, IntoInner}; use crate::{fs, io}; /// Raw file descriptors. -#[rustc_allowed_through_unstable_modules] #[stable(feature = "rust1", since = "1.0.0")] #[cfg(not(target_os = "hermit"))] pub type RawFd = raw::c_int; -#[rustc_allowed_through_unstable_modules] #[stable(feature = "rust1", since = "1.0.0")] #[cfg(target_os = "hermit")] pub type RawFd = i32; @@ -33,7 +31,6 @@ pub type RawFd = i32; /// This is only available on unix and WASI platforms and must be imported in /// order to call the method. Windows platforms have a corresponding /// `AsRawHandle` and `AsRawSocket` set of traits. -#[rustc_allowed_through_unstable_modules] #[stable(feature = "rust1", since = "1.0.0")] pub trait AsRawFd { /// Extracts the raw file descriptor. @@ -67,7 +64,6 @@ pub trait AsRawFd { /// A trait to express the ability to construct an object from a raw file /// descriptor. -#[rustc_allowed_through_unstable_modules] #[stable(feature = "from_raw_os", since = "1.1.0")] pub trait FromRawFd { /// Constructs a new instance of `Self` from the given raw file @@ -112,7 +108,6 @@ pub trait FromRawFd { /// A trait to express the ability to consume an object and acquire ownership of /// its raw file descriptor. -#[rustc_allowed_through_unstable_modules] #[stable(feature = "into_raw_os", since = "1.4.0")] pub trait IntoRawFd { /// Consumes this object, returning the raw underlying file descriptor. diff --git a/library/std/src/os/hermit/io/net.rs b/library/std/src/os/hermit/io/net.rs index 7a774345b231a..233bc885fc733 100644 --- a/library/std/src/os/hermit/io/net.rs +++ b/library/std/src/os/hermit/io/net.rs @@ -23,7 +23,7 @@ macro_rules! impl_from_raw_fd { unsafe fn from_raw_fd(fd: RawFd) -> net::$t { unsafe { let socket = sys::net::Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd))); - net::$t::from_inner(sys_common::net::$t::from_inner(socket)) + net::$t::from_inner(sys::net::$t::from_inner(socket)) } } } diff --git a/library/std/src/os/solid/io.rs b/library/std/src/os/solid/io.rs index 2d18f33961506..c23d842b238b8 100644 --- a/library/std/src/os/solid/io.rs +++ b/library/std/src/os/solid/io.rs @@ -48,12 +48,15 @@ use crate::marker::PhantomData; use crate::mem::ManuallyDrop; -use crate::sys_common::{self, AsInner, FromInner, IntoInner}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::{fmt, net, sys}; /// Raw file descriptors. pub type RawFd = i32; +// The max of this is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`. +type ValidRawFd = core::num::niche_types::NotAllOnes; + /// A borrowed SOLID Sockets file descriptor. /// /// This has a lifetime parameter to tie it to the lifetime of something that @@ -69,12 +72,9 @@ pub type RawFd = i32; /// socket, which is then borrowed under the same lifetime. #[derive(Copy, Clone)] #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -// This is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`. -#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] #[rustc_nonnull_optimization_guaranteed] pub struct BorrowedFd<'socket> { - fd: RawFd, + fd: ValidRawFd, _phantom: PhantomData<&'socket OwnedFd>, } @@ -87,12 +87,9 @@ pub struct BorrowedFd<'socket> { /// an argument, it is not captured or consumed, and it never has the value /// `SOLID_NET_INVALID_FD`. #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -// This is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`. -#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] #[rustc_nonnull_optimization_guaranteed] pub struct OwnedFd { - fd: RawFd, + fd: ValidRawFd, } impl BorrowedFd<'_> { @@ -104,11 +101,9 @@ impl BorrowedFd<'_> { /// the returned `BorrowedFd`, and it must not have the value /// `SOLID_NET_INVALID_FD`. #[inline] + #[track_caller] pub const unsafe fn borrow_raw(fd: RawFd) -> Self { - assert!(fd != -1 as RawFd); - // SAFETY: we just asserted that the value is in the valid range and - // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - unsafe { Self { fd, _phantom: PhantomData } } + Self { fd: ValidRawFd::new(fd).expect("fd != -1"), _phantom: PhantomData } } } @@ -124,7 +119,7 @@ impl BorrowedFd<'_> { /// Creates a new `OwnedFd` instance that shares the same underlying file /// description as the existing `BorrowedFd` instance. pub fn try_clone_to_owned(&self) -> crate::io::Result { - let fd = sys::net::cvt(unsafe { sys::net::netc::dup(self.as_raw_fd()) })?; + let fd = sys::net::cvt(unsafe { crate::sys::abi::sockets::dup(self.as_raw_fd()) })?; Ok(unsafe { OwnedFd::from_raw_fd(fd) }) } } @@ -132,21 +127,21 @@ impl BorrowedFd<'_> { impl AsRawFd for BorrowedFd<'_> { #[inline] fn as_raw_fd(&self) -> RawFd { - self.fd + self.fd.as_inner() } } impl AsRawFd for OwnedFd { #[inline] fn as_raw_fd(&self) -> RawFd { - self.fd + self.fd.as_inner() } } impl IntoRawFd for OwnedFd { #[inline] fn into_raw_fd(self) -> RawFd { - ManuallyDrop::new(self).fd + ManuallyDrop::new(self).fd.as_inner() } } @@ -158,18 +153,16 @@ impl FromRawFd for OwnedFd { /// The resource pointed to by `fd` must be open and suitable for assuming /// ownership. The resource must not require any cleanup other than `close`. #[inline] + #[track_caller] unsafe fn from_raw_fd(fd: RawFd) -> Self { - assert_ne!(fd, -1 as RawFd); - // SAFETY: we just asserted that the value is in the valid range and - // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - unsafe { Self { fd } } + Self { fd: ValidRawFd::new(fd).expect("fd != -1") } } } impl Drop for OwnedFd { #[inline] fn drop(&mut self) { - unsafe { sys::net::netc::close(self.fd) }; + unsafe { crate::sys::abi::sockets::close(self.fd.as_inner()) }; } } @@ -388,7 +381,7 @@ macro_rules! impl_from_raw_fd { #[inline] unsafe fn from_raw_fd(fd: RawFd) -> net::$t { let socket = unsafe { sys::net::Socket::from_raw_fd(fd) }; - net::$t::from_inner(sys_common::net::$t::from_inner(socket)) + net::$t::from_inner(sys::net::$t::from_inner(socket)) } } )*}; diff --git a/library/std/src/os/unix/fs/tests.rs b/library/std/src/os/unix/fs/tests.rs index 67f607bd46837..db9621c8c205c 100644 --- a/library/std/src/os/unix/fs/tests.rs +++ b/library/std/src/os/unix/fs/tests.rs @@ -3,7 +3,7 @@ use super::*; #[test] fn read_vectored_at() { let msg = b"preadv is working!"; - let dir = crate::sys_common::io::test::tmpdir(); + let dir = crate::test_helpers::tmpdir(); let filename = dir.join("preadv.txt"); { @@ -31,7 +31,7 @@ fn read_vectored_at() { #[test] fn write_vectored_at() { let msg = b"pwritev is not working!"; - let dir = crate::sys_common::io::test::tmpdir(); + let dir = crate::test_helpers::tmpdir(); let filename = dir.join("preadv.txt"); { diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index 21e2176185d25..0398a535eb54a 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -7,7 +7,7 @@ use crate::os::android::net::{SocketAddrExt, UnixSocketExt}; use crate::os::linux::net::{SocketAddrExt, UnixSocketExt}; #[cfg(any(target_os = "android", target_os = "linux"))] use crate::os::unix::io::AsRawFd; -use crate::sys_common::io::test::tmpdir; +use crate::test_helpers::tmpdir; use crate::thread; use crate::time::Duration; diff --git a/library/std/src/os/wasi/io/fd.rs b/library/std/src/os/wasi/io/fd.rs deleted file mode 100644 index 930aca887e3c4..0000000000000 --- a/library/std/src/os/wasi/io/fd.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! Owned and borrowed file descriptors. - -#![unstable(feature = "wasi_ext", issue = "71213")] - -// Tests for this module -#[cfg(test)] -mod tests; - -pub use crate::os::fd::owned::*; diff --git a/library/std/src/os/wasi/io/mod.rs b/library/std/src/os/wasi/io/mod.rs index 4e123a1eec8ae..5f9a735db085e 100644 --- a/library/std/src/os/wasi/io/mod.rs +++ b/library/std/src/os/wasi/io/mod.rs @@ -4,3 +4,7 @@ #[stable(feature = "io_safety_wasi", since = "1.65.0")] pub use crate::os::fd::*; + +// Tests for this module +#[cfg(test)] +mod tests; diff --git a/library/std/src/os/wasi/io/raw.rs b/library/std/src/os/wasi/io/raw.rs deleted file mode 100644 index da3b36adad409..0000000000000 --- a/library/std/src/os/wasi/io/raw.rs +++ /dev/null @@ -1,20 +0,0 @@ -//! WASI-specific extensions to general I/O primitives. - -#![unstable(feature = "wasi_ext", issue = "71213")] - -// NOTE: despite the fact that this module is unstable, -// stable Rust had the capability to access the stable -// re-exported items from os::fd::raw through this -// unstable module. -// In PR #95956 the stability checker was changed to check -// all path segments of an item rather than just the last, -// which caused the aforementioned stable usage to regress -// (see issue #99502). -// As a result, the items in os::fd::raw were given the -// rustc_allowed_through_unstable_modules attribute. -// No regression tests were added to ensure this property, -// as CI is not configured to test wasm32-wasi. -// If this module is stabilized, -// you may want to remove those attributes -// (assuming no other unstable modules need them). -pub use crate::os::fd::raw::*; diff --git a/library/std/src/os/wasi/io/fd/tests.rs b/library/std/src/os/wasi/io/tests.rs similarity index 100% rename from library/std/src/os/wasi/io/fd/tests.rs rename to library/std/src/os/wasi/io/tests.rs diff --git a/library/std/src/os/windows/io/raw.rs b/library/std/src/os/windows/io/raw.rs index 6658248d574c9..c0517fab95068 100644 --- a/library/std/src/os/windows/io/raw.rs +++ b/library/std/src/os/windows/io/raw.rs @@ -6,7 +6,7 @@ use crate::os::windows::io::{AsHandle, AsSocket}; use crate::os::windows::io::{OwnedHandle, OwnedSocket}; use crate::os::windows::raw; -use crate::sys_common::{self, AsInner, FromInner, IntoInner}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::{fs, io, net, ptr, sys}; /// Raw HANDLEs. @@ -262,7 +262,7 @@ impl FromRawSocket for net::TcpStream { unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpStream { unsafe { let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); - net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(sock)) + net::TcpStream::from_inner(sys::net::TcpStream::from_inner(sock)) } } } @@ -272,7 +272,7 @@ impl FromRawSocket for net::TcpListener { unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpListener { unsafe { let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); - net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(sock)) + net::TcpListener::from_inner(sys::net::TcpListener::from_inner(sock)) } } } @@ -282,7 +282,7 @@ impl FromRawSocket for net::UdpSocket { unsafe fn from_raw_socket(sock: RawSocket) -> net::UdpSocket { unsafe { let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); - net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(sock)) + net::UdpSocket::from_inner(sys::net::UdpSocket::from_inner(sock)) } } } diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs index c6d7bad944093..2bc6ce222ae5c 100644 --- a/library/std/src/os/windows/io/socket.rs +++ b/library/std/src/os/windows/io/socket.rs @@ -9,6 +9,9 @@ use crate::mem::{self, ManuallyDrop}; use crate::sys::cvt; use crate::{fmt, io, sys}; +// The max here is -2, in two's complement. -1 is `INVALID_SOCKET`. +type ValidRawSocket = core::num::niche_types::NotAllOnes; + /// A borrowed socket. /// /// This has a lifetime parameter to tie it to the lifetime of something that @@ -24,17 +27,10 @@ use crate::{fmt, io, sys}; /// socket, which is then borrowed under the same lifetime. #[derive(Copy, Clone)] #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -// This is -2, in two's complement. -1 is `INVALID_SOCKET`. -#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))] -#[cfg_attr( - target_pointer_width = "64", - rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE) -)] #[rustc_nonnull_optimization_guaranteed] #[stable(feature = "io_safety", since = "1.63.0")] pub struct BorrowedSocket<'socket> { - socket: RawSocket, + socket: ValidRawSocket, _phantom: PhantomData<&'socket OwnedSocket>, } @@ -47,17 +43,10 @@ pub struct BorrowedSocket<'socket> { /// argument or returned as an owned value, and it never has the value /// `INVALID_SOCKET`. #[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -// This is -2, in two's complement. -1 is `INVALID_SOCKET`. -#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))] -#[cfg_attr( - target_pointer_width = "64", - rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE) -)] #[rustc_nonnull_optimization_guaranteed] #[stable(feature = "io_safety", since = "1.63.0")] pub struct OwnedSocket { - socket: RawSocket, + socket: ValidRawSocket, } impl BorrowedSocket<'_> { @@ -69,11 +58,11 @@ impl BorrowedSocket<'_> { /// the returned `BorrowedSocket`, and it must not have the value /// `INVALID_SOCKET`. #[inline] + #[track_caller] #[rustc_const_stable(feature = "io_safety", since = "1.63.0")] #[stable(feature = "io_safety", since = "1.63.0")] pub const unsafe fn borrow_raw(socket: RawSocket) -> Self { - assert!(socket != sys::c::INVALID_SOCKET as RawSocket); - unsafe { Self { socket, _phantom: PhantomData } } + Self { socket: ValidRawSocket::new(socket).expect("socket != -1"), _phantom: PhantomData } } } @@ -172,7 +161,7 @@ fn last_error() -> io::Error { impl AsRawSocket for BorrowedSocket<'_> { #[inline] fn as_raw_socket(&self) -> RawSocket { - self.socket + self.socket.as_inner() } } @@ -180,7 +169,7 @@ impl AsRawSocket for BorrowedSocket<'_> { impl AsRawSocket for OwnedSocket { #[inline] fn as_raw_socket(&self) -> RawSocket { - self.socket + self.socket.as_inner() } } @@ -188,18 +177,16 @@ impl AsRawSocket for OwnedSocket { impl IntoRawSocket for OwnedSocket { #[inline] fn into_raw_socket(self) -> RawSocket { - ManuallyDrop::new(self).socket + ManuallyDrop::new(self).socket.as_inner() } } #[stable(feature = "io_safety", since = "1.63.0")] impl FromRawSocket for OwnedSocket { #[inline] + #[track_caller] unsafe fn from_raw_socket(socket: RawSocket) -> Self { - unsafe { - debug_assert_ne!(socket, sys::c::INVALID_SOCKET as RawSocket); - Self { socket } - } + Self { socket: ValidRawSocket::new(socket).expect("socket != -1") } } } @@ -208,7 +195,7 @@ impl Drop for OwnedSocket { #[inline] fn drop(&mut self) { unsafe { - let _ = sys::c::closesocket(self.socket as sys::c::SOCKET); + let _ = sys::c::closesocket(self.socket.as_inner() as sys::c::SOCKET); } } } diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index 0277b79b8b69c..201274cf03aec 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -590,10 +590,10 @@ impl<'a> ProcThreadAttributeListBuilder<'a> { value_ptr: *const T, value_size: usize, ) -> Self { - self.attributes.insert(attribute, ProcThreadAttributeValue { - ptr: value_ptr.cast::(), - size: value_size, - }); + self.attributes.insert( + attribute, + ProcThreadAttributeValue { ptr: value_ptr.cast::(), size: value_size }, + ); self } diff --git a/library/std/src/os/xous/ffi/definitions.rs b/library/std/src/os/xous/ffi/definitions.rs index 1b16849af03b0..345005bcc78d7 100644 --- a/library/std/src/os/xous/ffi/definitions.rs +++ b/library/std/src/os/xous/ffi/definitions.rs @@ -126,36 +126,42 @@ impl From for Error { #[stable(feature = "rust1", since = "1.0.0")] impl core::fmt::Display for Error { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "{}", match self { - Error::NoError => "no error occurred", - Error::BadAlignment => "memory was not properly aligned", - Error::BadAddress => "an invalid address was supplied", - Error::OutOfMemory => "the process or service has run out of memory", - Error::MemoryInUse => "the requested address is in use", - Error::InterruptNotFound => "the requested interrupt does not exist on this platform", - Error::InterruptInUse => "the requested interrupt is currently in use", - Error::InvalidString => "the specified string was not formatted correctly", - Error::ServerExists => "a server with that address already exists", - Error::ServerNotFound => "the requetsed server could not be found", - Error::ProcessNotFound => "the target process does not exist", - Error::ProcessNotChild => "the requested operation can only be done on child processes", - Error::ProcessTerminated => "the target process has crashed", - Error::Timeout => "the requested operation timed out", - Error::InternalError => "an internal error occurred", - Error::ServerQueueFull => "the server has too many pending messages", - Error::ThreadNotAvailable => "the specified thread does not exist", - Error::UnhandledSyscall => "the kernel did not recognize that syscall", - Error::InvalidSyscall => "the syscall had incorrect parameters", - Error::ShareViolation => "an attempt was made to share memory twice", - Error::InvalidThread => "tried to resume a thread that was not ready", - Error::InvalidPid => "kernel attempted to use a pid that was not valid", - Error::AccessDenied => "no permission to perform the requested operation", - Error::UseBeforeInit => "attempt to use a service before initialization finished", - Error::DoubleFree => "the requested resource was freed twice", - Error::DebugInProgress => "kernel attempted to activate a thread being debugged", - Error::InvalidLimit => "process attempted to adjust an invalid limit", - Error::UnknownError => "an unknown error occurred", - }) + write!( + f, + "{}", + match self { + Error::NoError => "no error occurred", + Error::BadAlignment => "memory was not properly aligned", + Error::BadAddress => "an invalid address was supplied", + Error::OutOfMemory => "the process or service has run out of memory", + Error::MemoryInUse => "the requested address is in use", + Error::InterruptNotFound => + "the requested interrupt does not exist on this platform", + Error::InterruptInUse => "the requested interrupt is currently in use", + Error::InvalidString => "the specified string was not formatted correctly", + Error::ServerExists => "a server with that address already exists", + Error::ServerNotFound => "the requetsed server could not be found", + Error::ProcessNotFound => "the target process does not exist", + Error::ProcessNotChild => + "the requested operation can only be done on child processes", + Error::ProcessTerminated => "the target process has crashed", + Error::Timeout => "the requested operation timed out", + Error::InternalError => "an internal error occurred", + Error::ServerQueueFull => "the server has too many pending messages", + Error::ThreadNotAvailable => "the specified thread does not exist", + Error::UnhandledSyscall => "the kernel did not recognize that syscall", + Error::InvalidSyscall => "the syscall had incorrect parameters", + Error::ShareViolation => "an attempt was made to share memory twice", + Error::InvalidThread => "tried to resume a thread that was not ready", + Error::InvalidPid => "kernel attempted to use a pid that was not valid", + Error::AccessDenied => "no permission to perform the requested operation", + Error::UseBeforeInit => "attempt to use a service before initialization finished", + Error::DoubleFree => "the requested resource was freed twice", + Error::DebugInProgress => "kernel attempted to activate a thread being debugged", + Error::InvalidLimit => "process attempted to adjust an invalid limit", + Error::UnknownError => "an unknown error occurred", + } + ) } } diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index d649357a56d71..61801db072a0e 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -376,7 +376,9 @@ pub fn catch_unwind R + UnwindSafe, R>(f: F) -> Result { /// use std::panic; /// /// let result = panic::catch_unwind(|| { -/// panic!("oh no!"); +/// if 1 != 2 { +/// panic!("oh no!"); +/// } /// }); /// /// if let Err(err) = result { @@ -529,6 +531,3 @@ pub fn get_backtrace_style() -> Option { Err(new) => BacktraceStyle::from_u8(new), } } - -#[cfg(test)] -mod tests; diff --git a/library/std/src/panic/tests.rs b/library/std/src/panic/tests.rs deleted file mode 100644 index b37d74011cc67..0000000000000 --- a/library/std/src/panic/tests.rs +++ /dev/null @@ -1,56 +0,0 @@ -#![allow(dead_code)] - -use crate::cell::RefCell; -use crate::panic::{AssertUnwindSafe, UnwindSafe}; -use crate::rc::Rc; -use crate::sync::{Arc, Mutex, RwLock}; - -struct Foo { - a: i32, -} - -fn assert() {} - -#[test] -fn panic_safety_traits() { - assert::(); - assert::<&i32>(); - assert::<*mut i32>(); - assert::<*const i32>(); - assert::(); - assert::(); - assert::<&str>(); - assert::(); - assert::<&Foo>(); - assert::>(); - assert::(); - assert::>(); - assert::>(); - assert::>(); - assert::>(); - assert::<&Mutex>(); - assert::<&RwLock>(); - assert::>(); - assert::>(); - assert::>(); - - { - trait Trait: UnwindSafe {} - assert::>(); - } - - fn bar() { - assert::>(); - assert::>(); - } - - fn baz() { - assert::>(); - assert::>(); - assert::>(); - assert::>(); - assert::<&AssertUnwindSafe>(); - assert::>>(); - assert::>>(); - } -} diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 3b02254548b1c..b47b41d4bc5b7 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -54,11 +54,11 @@ pub static EMPTY_PANIC: fn(&'static str) -> ! = // One day this may look a little less ad-hoc with the compiler helping out to // hook up these functions, but it is not this day! #[allow(improper_ctypes)] -extern "C" { +unsafe extern "C" { fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static); } -extern "Rust" { +unsafe extern "Rust" { /// `PanicPayload` lazily performs allocation only when needed (this avoids /// allocations when using the "abort" panic runtime). fn __rust_start_panic(payload: &mut dyn PanicPayload) -> u32; @@ -258,31 +258,34 @@ fn default_hook(info: &PanicHookInfo<'_>) { let location = info.location().unwrap(); let msg = payload_as_str(info.payload()); - let thread = thread::try_current(); - let name = thread.as_ref().and_then(|t| t.name()).unwrap_or(""); let write = #[optimize(size)] |err: &mut dyn crate::io::Write| { // Use a lock to prevent mixed output in multithreading context. // Some platforms also require it when printing a backtrace, like `SymFromAddr` on Windows. let mut lock = backtrace::lock(); - // Try to write the panic message to a buffer first to prevent other concurrent outputs - // interleaving with it. - let mut buffer = [0u8; 512]; - let mut cursor = crate::io::Cursor::new(&mut buffer[..]); - - let write_msg = |dst: &mut dyn crate::io::Write| { - // We add a newline to ensure the panic message appears at the start of a line. - writeln!(dst, "\nthread '{name}' panicked at {location}:\n{msg}") - }; - if write_msg(&mut cursor).is_ok() { - let pos = cursor.position() as usize; - let _ = err.write_all(&buffer[0..pos]); - } else { - // The message did not fit into the buffer, write it directly instead. - let _ = write_msg(err); - }; + thread::with_current_name(|name| { + let name = name.unwrap_or(""); + + // Try to write the panic message to a buffer first to prevent other concurrent outputs + // interleaving with it. + let mut buffer = [0u8; 512]; + let mut cursor = crate::io::Cursor::new(&mut buffer[..]); + + let write_msg = |dst: &mut dyn crate::io::Write| { + // We add a newline to ensure the panic message appears at the start of a line. + writeln!(dst, "\nthread '{name}' panicked at {location}:\n{msg}") + }; + + if write_msg(&mut cursor).is_ok() { + let pos = cursor.position() as usize; + let _ = err.write_all(&buffer[0..pos]); + } else { + // The message did not fit into the buffer, write it directly instead. + let _ = write_msg(err); + }; + }); static FIRST_PANIC: AtomicBool = AtomicBool::new(true); diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 35e920ab34476..97e17acadeac7 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -67,9 +67,6 @@ #![stable(feature = "rust1", since = "1.0.0")] #![deny(unsafe_op_in_unsafe_fn)] -#[cfg(test)] -mod tests; - use core::clone::CloneToUninit; use crate::borrow::{Borrow, Cow}; @@ -298,7 +295,7 @@ where } // Detect scheme on Redox -fn has_redox_scheme(s: &[u8]) -> bool { +pub(crate) fn has_redox_scheme(s: &[u8]) -> bool { cfg!(target_os = "redox") && s.contains(&b':') } @@ -2155,7 +2152,7 @@ impl Path { unsafe { Path::new(OsStr::from_encoded_bytes_unchecked(s)) } } // The following (private!) function reveals the byte encoding used for OsStr. - fn as_u8_slice(&self) -> &[u8] { + pub(crate) fn as_u8_slice(&self) -> &[u8] { self.inner.as_encoded_bytes() } @@ -2323,14 +2320,7 @@ impl Path { #[must_use] #[allow(deprecated)] pub fn is_absolute(&self) -> bool { - if cfg!(target_os = "redox") { - // FIXME: Allow Redox prefixes - self.has_root() || has_redox_scheme(self.as_u8_slice()) - } else { - self.has_root() - && (cfg!(any(unix, target_os = "hermit", target_os = "wasi")) - || self.prefix().is_some()) - } + sys::path::is_absolute(self) } /// Returns `true` if the `Path` is relative, i.e., not absolute. @@ -2353,7 +2343,7 @@ impl Path { !self.is_absolute() } - fn prefix(&self) -> Option> { + pub(crate) fn prefix(&self) -> Option> { self.components().prefix } diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs deleted file mode 100644 index ff3f7151bb834..0000000000000 --- a/library/std/src/path/tests.rs +++ /dev/null @@ -1,2081 +0,0 @@ -use core::hint::black_box; - -use super::*; -use crate::collections::{BTreeSet, HashSet}; -use crate::hash::DefaultHasher; -use crate::mem::MaybeUninit; -use crate::ptr; - -#[allow(unknown_lints, unused_macro_rules)] -macro_rules! t ( - ($path:expr, iter: $iter:expr) => ( - { - let path = Path::new($path); - - // Forward iteration - let comps = path.iter() - .map(|p| p.to_string_lossy().into_owned()) - .collect::>(); - let exp: &[&str] = &$iter; - let exps = exp.iter().map(|s| s.to_string()).collect::>(); - assert!(comps == exps, "iter: Expected {:?}, found {:?}", - exps, comps); - - // Reverse iteration - let comps = Path::new($path).iter().rev() - .map(|p| p.to_string_lossy().into_owned()) - .collect::>(); - let exps = exps.into_iter().rev().collect::>(); - assert!(comps == exps, "iter().rev(): Expected {:?}, found {:?}", - exps, comps); - } - ); - - ($path:expr, has_root: $has_root:expr, is_absolute: $is_absolute:expr) => ( - { - let path = Path::new($path); - - let act_root = path.has_root(); - assert!(act_root == $has_root, "has_root: Expected {:?}, found {:?}", - $has_root, act_root); - - let act_abs = path.is_absolute(); - assert!(act_abs == $is_absolute, "is_absolute: Expected {:?}, found {:?}", - $is_absolute, act_abs); - } - ); - - ($path:expr, parent: $parent:expr, file_name: $file:expr) => ( - { - let path = Path::new($path); - - let parent = path.parent().map(|p| p.to_str().unwrap()); - let exp_parent: Option<&str> = $parent; - assert!(parent == exp_parent, "parent: Expected {:?}, found {:?}", - exp_parent, parent); - - let file = path.file_name().map(|p| p.to_str().unwrap()); - let exp_file: Option<&str> = $file; - assert!(file == exp_file, "file_name: Expected {:?}, found {:?}", - exp_file, file); - } - ); - - ($path:expr, file_stem: $file_stem:expr, extension: $extension:expr) => ( - { - let path = Path::new($path); - - let stem = path.file_stem().map(|p| p.to_str().unwrap()); - let exp_stem: Option<&str> = $file_stem; - assert!(stem == exp_stem, "file_stem: Expected {:?}, found {:?}", - exp_stem, stem); - - let ext = path.extension().map(|p| p.to_str().unwrap()); - let exp_ext: Option<&str> = $extension; - assert!(ext == exp_ext, "extension: Expected {:?}, found {:?}", - exp_ext, ext); - } - ); - - ($path:expr, file_prefix: $file_prefix:expr, extension: $extension:expr) => ( - { - let path = Path::new($path); - - let prefix = path.file_prefix().map(|p| p.to_str().unwrap()); - let exp_prefix: Option<&str> = $file_prefix; - assert!(prefix == exp_prefix, "file_prefix: Expected {:?}, found {:?}", - exp_prefix, prefix); - - let ext = path.extension().map(|p| p.to_str().unwrap()); - let exp_ext: Option<&str> = $extension; - assert!(ext == exp_ext, "extension: Expected {:?}, found {:?}", - exp_ext, ext); - } - ); - - ($path:expr, iter: $iter:expr, - has_root: $has_root:expr, is_absolute: $is_absolute:expr, - parent: $parent:expr, file_name: $file:expr, - file_stem: $file_stem:expr, extension: $extension:expr, - file_prefix: $file_prefix:expr) => ( - { - t!($path, iter: $iter); - t!($path, has_root: $has_root, is_absolute: $is_absolute); - t!($path, parent: $parent, file_name: $file); - t!($path, file_stem: $file_stem, extension: $extension); - t!($path, file_prefix: $file_prefix, extension: $extension); - } - ); -); - -#[test] -fn into() { - use crate::borrow::Cow; - - let static_path = Path::new("/home/foo"); - let static_cow_path: Cow<'static, Path> = static_path.into(); - let pathbuf = PathBuf::from("/home/foo"); - - { - let path: &Path = &pathbuf; - let borrowed_cow_path: Cow<'_, Path> = path.into(); - - assert_eq!(static_cow_path, borrowed_cow_path); - } - - let owned_cow_path: Cow<'static, Path> = pathbuf.into(); - - assert_eq!(static_cow_path, owned_cow_path); -} - -#[test] -fn test_pathbuf_leak() { - let string = "/have/a/cake".to_owned(); - let (len, cap) = (string.len(), string.capacity()); - let buf = PathBuf::from(string); - let leaked = buf.leak(); - assert_eq!(leaked.as_os_str().as_encoded_bytes(), b"/have/a/cake"); - unsafe { drop(String::from_raw_parts(leaked.as_mut_os_str() as *mut OsStr as _, len, cap)) } -} - -#[test] -#[cfg(any(unix, target_os = "wasi"))] -pub fn test_decompositions_unix() { - t!("", - iter: [], - has_root: false, - is_absolute: false, - parent: None, - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("foo", - iter: ["foo"], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None, - file_prefix: Some("foo") - ); - - t!("/", - iter: ["/"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("/foo", - iter: ["/", "foo"], - has_root: true, - is_absolute: true, - parent: Some("/"), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None, - file_prefix: Some("foo") - ); - - t!("foo/", - iter: ["foo"], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None, - file_prefix: Some("foo") - ); - - t!("/foo/", - iter: ["/", "foo"], - has_root: true, - is_absolute: true, - parent: Some("/"), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None, - file_prefix: Some("foo") - ); - - t!("foo/bar", - iter: ["foo", "bar"], - has_root: false, - is_absolute: false, - parent: Some("foo"), - file_name: Some("bar"), - file_stem: Some("bar"), - extension: None, - file_prefix: Some("bar") - ); - - t!("/foo/bar", - iter: ["/", "foo", "bar"], - has_root: true, - is_absolute: true, - parent: Some("/foo"), - file_name: Some("bar"), - file_stem: Some("bar"), - extension: None, - file_prefix: Some("bar") - ); - - t!("///foo///", - iter: ["/", "foo"], - has_root: true, - is_absolute: true, - parent: Some("/"), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None, - file_prefix: Some("foo") - ); - - t!("///foo///bar", - iter: ["/", "foo", "bar"], - has_root: true, - is_absolute: true, - parent: Some("///foo"), - file_name: Some("bar"), - file_stem: Some("bar"), - extension: None, - file_prefix: Some("bar") - ); - - t!("./.", - iter: ["."], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("/..", - iter: ["/", ".."], - has_root: true, - is_absolute: true, - parent: Some("/"), - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("../", - iter: [".."], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("foo/.", - iter: ["foo"], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None, - file_prefix: Some("foo") - ); - - t!("foo/..", - iter: ["foo", ".."], - has_root: false, - is_absolute: false, - parent: Some("foo"), - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("foo/./", - iter: ["foo"], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None, - file_prefix: Some("foo") - ); - - t!("foo/./bar", - iter: ["foo", "bar"], - has_root: false, - is_absolute: false, - parent: Some("foo"), - file_name: Some("bar"), - file_stem: Some("bar"), - extension: None, - file_prefix: Some("bar") - ); - - t!("foo/../", - iter: ["foo", ".."], - has_root: false, - is_absolute: false, - parent: Some("foo"), - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("foo/../bar", - iter: ["foo", "..", "bar"], - has_root: false, - is_absolute: false, - parent: Some("foo/.."), - file_name: Some("bar"), - file_stem: Some("bar"), - extension: None, - file_prefix: Some("bar") - ); - - t!("./a", - iter: [".", "a"], - has_root: false, - is_absolute: false, - parent: Some("."), - file_name: Some("a"), - file_stem: Some("a"), - extension: None, - file_prefix: Some("a") - ); - - t!(".", - iter: ["."], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("./", - iter: ["."], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("a/b", - iter: ["a", "b"], - has_root: false, - is_absolute: false, - parent: Some("a"), - file_name: Some("b"), - file_stem: Some("b"), - extension: None, - file_prefix: Some("b") - ); - - t!("a//b", - iter: ["a", "b"], - has_root: false, - is_absolute: false, - parent: Some("a"), - file_name: Some("b"), - file_stem: Some("b"), - extension: None, - file_prefix: Some("b") - ); - - t!("a/./b", - iter: ["a", "b"], - has_root: false, - is_absolute: false, - parent: Some("a"), - file_name: Some("b"), - file_stem: Some("b"), - extension: None, - file_prefix: Some("b") - ); - - t!("a/b/c", - iter: ["a", "b", "c"], - has_root: false, - is_absolute: false, - parent: Some("a/b"), - file_name: Some("c"), - file_stem: Some("c"), - extension: None, - file_prefix: Some("c") - ); - - t!(".foo", - iter: [".foo"], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: Some(".foo"), - file_stem: Some(".foo"), - extension: None, - file_prefix: Some(".foo") - ); - - t!("a/.foo", - iter: ["a", ".foo"], - has_root: false, - is_absolute: false, - parent: Some("a"), - file_name: Some(".foo"), - file_stem: Some(".foo"), - extension: None, - file_prefix: Some(".foo") - ); - - t!("a/.rustfmt.toml", - iter: ["a", ".rustfmt.toml"], - has_root: false, - is_absolute: false, - parent: Some("a"), - file_name: Some(".rustfmt.toml"), - file_stem: Some(".rustfmt"), - extension: Some("toml"), - file_prefix: Some(".rustfmt") - ); - - t!("a/.x.y.z", - iter: ["a", ".x.y.z"], - has_root: false, - is_absolute: false, - parent: Some("a"), - file_name: Some(".x.y.z"), - file_stem: Some(".x.y"), - extension: Some("z"), - file_prefix: Some(".x") - ); -} - -#[test] -#[cfg(windows)] -pub fn test_decompositions_windows() { - t!("", - iter: [], - has_root: false, - is_absolute: false, - parent: None, - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("foo", - iter: ["foo"], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None, - file_prefix: Some("foo") - ); - - t!("/", - iter: ["\\"], - has_root: true, - is_absolute: false, - parent: None, - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("\\", - iter: ["\\"], - has_root: true, - is_absolute: false, - parent: None, - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("c:", - iter: ["c:"], - has_root: false, - is_absolute: false, - parent: None, - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("c:\\", - iter: ["c:", "\\"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("c:/", - iter: ["c:", "\\"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("/foo", - iter: ["\\", "foo"], - has_root: true, - is_absolute: false, - parent: Some("/"), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None, - file_prefix: Some("foo") - ); - - t!("foo/", - iter: ["foo"], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None, - file_prefix: Some("foo") - ); - - t!("/foo/", - iter: ["\\", "foo"], - has_root: true, - is_absolute: false, - parent: Some("/"), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None, - file_prefix: Some("foo") - ); - - t!("foo/bar", - iter: ["foo", "bar"], - has_root: false, - is_absolute: false, - parent: Some("foo"), - file_name: Some("bar"), - file_stem: Some("bar"), - extension: None, - file_prefix: Some("bar") - ); - - t!("/foo/bar", - iter: ["\\", "foo", "bar"], - has_root: true, - is_absolute: false, - parent: Some("/foo"), - file_name: Some("bar"), - file_stem: Some("bar"), - extension: None, - file_prefix: Some("bar") - ); - - t!("///foo///", - iter: ["\\", "foo"], - has_root: true, - is_absolute: false, - parent: Some("/"), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None, - file_prefix: Some("foo") - ); - - t!("///foo///bar", - iter: ["\\", "foo", "bar"], - has_root: true, - is_absolute: false, - parent: Some("///foo"), - file_name: Some("bar"), - file_stem: Some("bar"), - extension: None, - file_prefix: Some("bar") - ); - - t!("./.", - iter: ["."], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("/..", - iter: ["\\", ".."], - has_root: true, - is_absolute: false, - parent: Some("/"), - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("../", - iter: [".."], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("foo/.", - iter: ["foo"], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None, - file_prefix: Some("foo") - ); - - t!("foo/..", - iter: ["foo", ".."], - has_root: false, - is_absolute: false, - parent: Some("foo"), - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("foo/./", - iter: ["foo"], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: Some("foo"), - file_stem: Some("foo"), - extension: None, - file_prefix: Some("foo") - ); - - t!("foo/./bar", - iter: ["foo", "bar"], - has_root: false, - is_absolute: false, - parent: Some("foo"), - file_name: Some("bar"), - file_stem: Some("bar"), - extension: None, - file_prefix: Some("bar") - ); - - t!("foo/../", - iter: ["foo", ".."], - has_root: false, - is_absolute: false, - parent: Some("foo"), - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("foo/../bar", - iter: ["foo", "..", "bar"], - has_root: false, - is_absolute: false, - parent: Some("foo/.."), - file_name: Some("bar"), - file_stem: Some("bar"), - extension: None, - file_prefix: Some("bar") - ); - - t!("./a", - iter: [".", "a"], - has_root: false, - is_absolute: false, - parent: Some("."), - file_name: Some("a"), - file_stem: Some("a"), - extension: None, - file_prefix: Some("a") - ); - - t!(".", - iter: ["."], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("./", - iter: ["."], - has_root: false, - is_absolute: false, - parent: Some(""), - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("a/b", - iter: ["a", "b"], - has_root: false, - is_absolute: false, - parent: Some("a"), - file_name: Some("b"), - file_stem: Some("b"), - extension: None, - file_prefix: Some("b") - ); - - t!("a//b", - iter: ["a", "b"], - has_root: false, - is_absolute: false, - parent: Some("a"), - file_name: Some("b"), - file_stem: Some("b"), - extension: None, - file_prefix: Some("b") - ); - - t!("a/./b", - iter: ["a", "b"], - has_root: false, - is_absolute: false, - parent: Some("a"), - file_name: Some("b"), - file_stem: Some("b"), - extension: None, - file_prefix: Some("b") - ); - - t!("a/b/c", - iter: ["a", "b", "c"], - has_root: false, - is_absolute: false, - parent: Some("a/b"), - file_name: Some("c"), - file_stem: Some("c"), - extension: None, - file_prefix: Some("c") - ); - - t!("a\\b\\c", - iter: ["a", "b", "c"], - has_root: false, - is_absolute: false, - parent: Some("a\\b"), - file_name: Some("c"), - file_stem: Some("c"), - extension: None, - file_prefix: Some("c") - ); - - t!("\\a", - iter: ["\\", "a"], - has_root: true, - is_absolute: false, - parent: Some("\\"), - file_name: Some("a"), - file_stem: Some("a"), - extension: None, - file_prefix: Some("a") - ); - - t!("c:\\foo.txt", - iter: ["c:", "\\", "foo.txt"], - has_root: true, - is_absolute: true, - parent: Some("c:\\"), - file_name: Some("foo.txt"), - file_stem: Some("foo"), - extension: Some("txt"), - file_prefix: Some("foo") - ); - - t!("\\\\server\\share\\foo.txt", - iter: ["\\\\server\\share", "\\", "foo.txt"], - has_root: true, - is_absolute: true, - parent: Some("\\\\server\\share\\"), - file_name: Some("foo.txt"), - file_stem: Some("foo"), - extension: Some("txt"), - file_prefix: Some("foo") - ); - - t!("\\\\server\\share", - iter: ["\\\\server\\share", "\\"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("\\\\server", - iter: ["\\", "server"], - has_root: true, - is_absolute: false, - parent: Some("\\"), - file_name: Some("server"), - file_stem: Some("server"), - extension: None, - file_prefix: Some("server") - ); - - t!("\\\\?\\bar\\foo.txt", - iter: ["\\\\?\\bar", "\\", "foo.txt"], - has_root: true, - is_absolute: true, - parent: Some("\\\\?\\bar\\"), - file_name: Some("foo.txt"), - file_stem: Some("foo"), - extension: Some("txt"), - file_prefix: Some("foo") - ); - - t!("\\\\?\\bar", - iter: ["\\\\?\\bar"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("\\\\?\\", - iter: ["\\\\?\\"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("\\\\?\\UNC\\server\\share\\foo.txt", - iter: ["\\\\?\\UNC\\server\\share", "\\", "foo.txt"], - has_root: true, - is_absolute: true, - parent: Some("\\\\?\\UNC\\server\\share\\"), - file_name: Some("foo.txt"), - file_stem: Some("foo"), - extension: Some("txt"), - file_prefix: Some("foo") - ); - - t!("\\\\?\\UNC\\server", - iter: ["\\\\?\\UNC\\server"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("\\\\?\\UNC\\", - iter: ["\\\\?\\UNC\\"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("\\\\?\\C:\\foo.txt", - iter: ["\\\\?\\C:", "\\", "foo.txt"], - has_root: true, - is_absolute: true, - parent: Some("\\\\?\\C:\\"), - file_name: Some("foo.txt"), - file_stem: Some("foo"), - extension: Some("txt"), - file_prefix: Some("foo") - ); - - t!("\\\\?\\C:\\", - iter: ["\\\\?\\C:", "\\"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("\\\\?\\C:", - iter: ["\\\\?\\C:"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("\\\\?\\foo/bar", - iter: ["\\\\?\\foo/bar"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("\\\\?\\C:/foo/bar", - iter: ["\\\\?\\C:", "\\", "foo/bar"], - has_root: true, - is_absolute: true, - parent: Some("\\\\?\\C:/"), - file_name: Some("foo/bar"), - file_stem: Some("foo/bar"), - extension: None, - file_prefix: Some("foo/bar") - ); - - t!("\\\\.\\foo\\bar", - iter: ["\\\\.\\foo", "\\", "bar"], - has_root: true, - is_absolute: true, - parent: Some("\\\\.\\foo\\"), - file_name: Some("bar"), - file_stem: Some("bar"), - extension: None, - file_prefix: Some("bar") - ); - - t!("\\\\.\\foo", - iter: ["\\\\.\\foo", "\\"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("\\\\.\\foo/bar", - iter: ["\\\\.\\foo", "\\", "bar"], - has_root: true, - is_absolute: true, - parent: Some("\\\\.\\foo/"), - file_name: Some("bar"), - file_stem: Some("bar"), - extension: None, - file_prefix: Some("bar") - ); - - t!("\\\\.\\foo\\bar/baz", - iter: ["\\\\.\\foo", "\\", "bar", "baz"], - has_root: true, - is_absolute: true, - parent: Some("\\\\.\\foo\\bar"), - file_name: Some("baz"), - file_stem: Some("baz"), - extension: None, - file_prefix: Some("baz") - ); - - t!("\\\\.\\", - iter: ["\\\\.\\", "\\"], - has_root: true, - is_absolute: true, - parent: None, - file_name: None, - file_stem: None, - extension: None, - file_prefix: None - ); - - t!("\\\\?\\a\\b\\", - iter: ["\\\\?\\a", "\\", "b"], - has_root: true, - is_absolute: true, - parent: Some("\\\\?\\a\\"), - file_name: Some("b"), - file_stem: Some("b"), - extension: None, - file_prefix: Some("b") - ); - - t!("\\\\?\\C:\\foo.txt.zip", - iter: ["\\\\?\\C:", "\\", "foo.txt.zip"], - has_root: true, - is_absolute: true, - parent: Some("\\\\?\\C:\\"), - file_name: Some("foo.txt.zip"), - file_stem: Some("foo.txt"), - extension: Some("zip"), - file_prefix: Some("foo") - ); - - t!("\\\\?\\C:\\.foo.txt.zip", - iter: ["\\\\?\\C:", "\\", ".foo.txt.zip"], - has_root: true, - is_absolute: true, - parent: Some("\\\\?\\C:\\"), - file_name: Some(".foo.txt.zip"), - file_stem: Some(".foo.txt"), - extension: Some("zip"), - file_prefix: Some(".foo") - ); - - t!("\\\\?\\C:\\.foo", - iter: ["\\\\?\\C:", "\\", ".foo"], - has_root: true, - is_absolute: true, - parent: Some("\\\\?\\C:\\"), - file_name: Some(".foo"), - file_stem: Some(".foo"), - extension: None, - file_prefix: Some(".foo") - ); - - t!("a/.x.y.z", - iter: ["a", ".x.y.z"], - has_root: false, - is_absolute: false, - parent: Some("a"), - file_name: Some(".x.y.z"), - file_stem: Some(".x.y"), - extension: Some("z"), - file_prefix: Some(".x") - ); -} - -#[test] -pub fn test_stem_ext() { - t!("foo", - file_stem: Some("foo"), - extension: None - ); - - t!("foo.", - file_stem: Some("foo"), - extension: Some("") - ); - - t!(".foo", - file_stem: Some(".foo"), - extension: None - ); - - t!("foo.txt", - file_stem: Some("foo"), - extension: Some("txt") - ); - - t!("foo.bar.txt", - file_stem: Some("foo.bar"), - extension: Some("txt") - ); - - t!("foo.bar.", - file_stem: Some("foo.bar"), - extension: Some("") - ); - - t!(".", file_stem: None, extension: None); - - t!("..", file_stem: None, extension: None); - - t!(".x.y.z", file_stem: Some(".x.y"), extension: Some("z")); - - t!("..x.y.z", file_stem: Some("..x.y"), extension: Some("z")); - - t!("", file_stem: None, extension: None); -} - -#[test] -pub fn test_prefix_ext() { - t!("foo", - file_prefix: Some("foo"), - extension: None - ); - - t!("foo.", - file_prefix: Some("foo"), - extension: Some("") - ); - - t!(".foo", - file_prefix: Some(".foo"), - extension: None - ); - - t!("foo.txt", - file_prefix: Some("foo"), - extension: Some("txt") - ); - - t!("foo.bar.txt", - file_prefix: Some("foo"), - extension: Some("txt") - ); - - t!("foo.bar.", - file_prefix: Some("foo"), - extension: Some("") - ); - - t!(".", file_prefix: None, extension: None); - - t!("..", file_prefix: None, extension: None); - - t!(".x.y.z", file_prefix: Some(".x"), extension: Some("z")); - - t!("..x.y.z", file_prefix: Some("."), extension: Some("z")); - - t!("", file_prefix: None, extension: None); -} - -#[test] -pub fn test_push() { - macro_rules! tp ( - ($path:expr, $push:expr, $expected:expr) => ({ - let mut actual = PathBuf::from($path); - actual.push($push); - assert!(actual.to_str() == Some($expected), - "pushing {:?} onto {:?}: Expected {:?}, got {:?}", - $push, $path, $expected, actual.to_str().unwrap()); - }); - ); - - if cfg!(unix) - || cfg!(target_os = "wasi") - || cfg!(all(target_env = "sgx", target_vendor = "fortanix")) - { - tp!("", "foo", "foo"); - tp!("foo", "bar", "foo/bar"); - tp!("foo/", "bar", "foo/bar"); - tp!("foo//", "bar", "foo//bar"); - tp!("foo/.", "bar", "foo/./bar"); - tp!("foo./.", "bar", "foo././bar"); - tp!("foo", "", "foo/"); - tp!("foo", ".", "foo/."); - tp!("foo", "..", "foo/.."); - tp!("foo", "/", "/"); - tp!("/foo/bar", "/", "/"); - tp!("/foo/bar", "/baz", "/baz"); - tp!("/foo/bar", "./baz", "/foo/bar/./baz"); - } else { - tp!("", "foo", "foo"); - tp!("foo", "bar", r"foo\bar"); - tp!("foo/", "bar", r"foo/bar"); - tp!(r"foo\", "bar", r"foo\bar"); - tp!("foo//", "bar", r"foo//bar"); - tp!(r"foo\\", "bar", r"foo\\bar"); - tp!("foo/.", "bar", r"foo/.\bar"); - tp!("foo./.", "bar", r"foo./.\bar"); - tp!(r"foo\.", "bar", r"foo\.\bar"); - tp!(r"foo.\.", "bar", r"foo.\.\bar"); - tp!("foo", "", "foo\\"); - tp!("foo", ".", r"foo\."); - tp!("foo", "..", r"foo\.."); - tp!("foo", "/", "/"); - tp!("foo", r"\", r"\"); - tp!("/foo/bar", "/", "/"); - tp!(r"\foo\bar", r"\", r"\"); - tp!("/foo/bar", "/baz", "/baz"); - tp!("/foo/bar", r"\baz", r"\baz"); - tp!("/foo/bar", "./baz", r"/foo/bar\./baz"); - tp!("/foo/bar", r".\baz", r"/foo/bar\.\baz"); - - tp!("c:\\", "windows", "c:\\windows"); - tp!("c:", "windows", "c:windows"); - - tp!("a\\b\\c", "d", "a\\b\\c\\d"); - tp!("\\a\\b\\c", "d", "\\a\\b\\c\\d"); - tp!("a\\b", "c\\d", "a\\b\\c\\d"); - tp!("a\\b", "\\c\\d", "\\c\\d"); - tp!("a\\b", ".", "a\\b\\."); - tp!("a\\b", "..\\c", "a\\b\\..\\c"); - tp!("a\\b", "C:a.txt", "C:a.txt"); - tp!("a\\b", "C:\\a.txt", "C:\\a.txt"); - tp!("C:\\a", "C:\\b.txt", "C:\\b.txt"); - tp!("C:\\a\\b\\c", "C:d", "C:d"); - tp!("C:a\\b\\c", "C:d", "C:d"); - tp!("C:", r"a\b\c", r"C:a\b\c"); - tp!("C:", r"..\a", r"C:..\a"); - tp!("\\\\server\\share\\foo", "bar", "\\\\server\\share\\foo\\bar"); - tp!("\\\\server\\share\\foo", "C:baz", "C:baz"); - tp!("\\\\?\\C:\\a\\b", "C:c\\d", "C:c\\d"); - tp!("\\\\?\\C:a\\b", "C:c\\d", "C:c\\d"); - tp!("\\\\?\\C:\\a\\b", "C:\\c\\d", "C:\\c\\d"); - tp!("\\\\?\\foo\\bar", "baz", "\\\\?\\foo\\bar\\baz"); - tp!("\\\\?\\UNC\\server\\share\\foo", "bar", "\\\\?\\UNC\\server\\share\\foo\\bar"); - tp!("\\\\?\\UNC\\server\\share", "C:\\a", "C:\\a"); - tp!("\\\\?\\UNC\\server\\share", "C:a", "C:a"); - - // Note: modified from old path API - tp!("\\\\?\\UNC\\server", "foo", "\\\\?\\UNC\\server\\foo"); - - tp!("C:\\a", "\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share"); - tp!("\\\\.\\foo\\bar", "baz", "\\\\.\\foo\\bar\\baz"); - tp!("\\\\.\\foo\\bar", "C:a", "C:a"); - // again, not sure about the following, but I'm assuming \\.\ should be verbatim - tp!("\\\\.\\foo", "..\\bar", "\\\\.\\foo\\..\\bar"); - - tp!("\\\\?\\C:", "foo", "\\\\?\\C:\\foo"); // this is a weird one - - tp!(r"\\?\C:\bar", "../foo", r"\\?\C:\foo"); - tp!(r"\\?\C:\bar", "../../foo", r"\\?\C:\foo"); - tp!(r"\\?\C:\", "../foo", r"\\?\C:\foo"); - tp!(r"\\?\C:", r"D:\foo/./", r"D:\foo/./"); - tp!(r"\\?\C:", r"\\?\D:\foo\.\", r"\\?\D:\foo\.\"); - tp!(r"\\?\A:\x\y", "/foo", r"\\?\A:\foo"); - tp!(r"\\?\A:", r"..\foo\.", r"\\?\A:\foo"); - tp!(r"\\?\A:\x\y", r".\foo\.", r"\\?\A:\x\y\foo"); - tp!(r"\\?\A:\x\y", r"", r"\\?\A:\x\y\"); - } -} - -#[test] -pub fn test_pop() { - macro_rules! tp ( - ($path:expr, $expected:expr, $output:expr) => ({ - let mut actual = PathBuf::from($path); - let output = actual.pop(); - assert!(actual.to_str() == Some($expected) && output == $output, - "popping from {:?}: Expected {:?}/{:?}, got {:?}/{:?}", - $path, $expected, $output, - actual.to_str().unwrap(), output); - }); - ); - - tp!("", "", false); - tp!("/", "/", false); - tp!("foo", "", true); - tp!(".", "", true); - tp!("/foo", "/", true); - tp!("/foo/bar", "/foo", true); - tp!("foo/bar", "foo", true); - tp!("foo/.", "", true); - tp!("foo//bar", "foo", true); - - if cfg!(windows) { - tp!("a\\b\\c", "a\\b", true); - tp!("\\a", "\\", true); - tp!("\\", "\\", false); - - tp!("C:\\a\\b", "C:\\a", true); - tp!("C:\\a", "C:\\", true); - tp!("C:\\", "C:\\", false); - tp!("C:a\\b", "C:a", true); - tp!("C:a", "C:", true); - tp!("C:", "C:", false); - tp!("\\\\server\\share\\a\\b", "\\\\server\\share\\a", true); - tp!("\\\\server\\share\\a", "\\\\server\\share\\", true); - tp!("\\\\server\\share", "\\\\server\\share", false); - tp!("\\\\?\\a\\b\\c", "\\\\?\\a\\b", true); - tp!("\\\\?\\a\\b", "\\\\?\\a\\", true); - tp!("\\\\?\\a", "\\\\?\\a", false); - tp!("\\\\?\\C:\\a\\b", "\\\\?\\C:\\a", true); - tp!("\\\\?\\C:\\a", "\\\\?\\C:\\", true); - tp!("\\\\?\\C:\\", "\\\\?\\C:\\", false); - tp!("\\\\?\\UNC\\server\\share\\a\\b", "\\\\?\\UNC\\server\\share\\a", true); - tp!("\\\\?\\UNC\\server\\share\\a", "\\\\?\\UNC\\server\\share\\", true); - tp!("\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share", false); - tp!("\\\\.\\a\\b\\c", "\\\\.\\a\\b", true); - tp!("\\\\.\\a\\b", "\\\\.\\a\\", true); - tp!("\\\\.\\a", "\\\\.\\a", false); - - tp!("\\\\?\\a\\b\\", "\\\\?\\a\\", true); - } -} - -#[test] -pub fn test_set_file_name() { - macro_rules! tfn ( - ($path:expr, $file:expr, $expected:expr) => ({ - let mut p = PathBuf::from($path); - p.set_file_name($file); - assert!(p.to_str() == Some($expected), - "setting file name of {:?} to {:?}: Expected {:?}, got {:?}", - $path, $file, $expected, - p.to_str().unwrap()); - }); - ); - - tfn!("foo", "foo", "foo"); - tfn!("foo", "bar", "bar"); - tfn!("foo", "", ""); - tfn!("", "foo", "foo"); - if cfg!(unix) - || cfg!(target_os = "wasi") - || cfg!(all(target_env = "sgx", target_vendor = "fortanix")) - { - tfn!(".", "foo", "./foo"); - tfn!("foo/", "bar", "bar"); - tfn!("foo/.", "bar", "bar"); - tfn!("..", "foo", "../foo"); - tfn!("foo/..", "bar", "foo/../bar"); - tfn!("/", "foo", "/foo"); - } else { - tfn!(".", "foo", r".\foo"); - tfn!(r"foo\", "bar", r"bar"); - tfn!(r"foo\.", "bar", r"bar"); - tfn!("..", "foo", r"..\foo"); - tfn!(r"foo\..", "bar", r"foo\..\bar"); - tfn!(r"\", "foo", r"\foo"); - } -} - -#[test] -pub fn test_set_extension() { - macro_rules! tfe ( - ($path:expr, $ext:expr, $expected:expr, $output:expr) => ({ - let mut p = PathBuf::from($path); - let output = p.set_extension($ext); - assert!(p.to_str() == Some($expected) && output == $output, - "setting extension of {:?} to {:?}: Expected {:?}/{:?}, got {:?}/{:?}", - $path, $ext, $expected, $output, - p.to_str().unwrap(), output); - }); - ); - - tfe!("foo", "txt", "foo.txt", true); - tfe!("foo.bar", "txt", "foo.txt", true); - tfe!("foo.bar.baz", "txt", "foo.bar.txt", true); - tfe!(".test", "txt", ".test.txt", true); - tfe!("foo.txt", "", "foo", true); - tfe!("foo", "", "foo", true); - tfe!("", "foo", "", false); - tfe!(".", "foo", ".", false); - tfe!("foo/", "bar", "foo.bar", true); - tfe!("foo/.", "bar", "foo.bar", true); - tfe!("..", "foo", "..", false); - tfe!("foo/..", "bar", "foo/..", false); - tfe!("/", "foo", "/", false); -} - -#[test] -pub fn test_add_extension() { - macro_rules! tfe ( - ($path:expr, $ext:expr, $expected:expr, $output:expr) => ({ - let mut p = PathBuf::from($path); - let output = p.add_extension($ext); - assert!(p.to_str() == Some($expected) && output == $output, - "adding extension of {:?} to {:?}: Expected {:?}/{:?}, got {:?}/{:?}", - $path, $ext, $expected, $output, - p.to_str().unwrap(), output); - }); - ); - - tfe!("foo", "txt", "foo.txt", true); - tfe!("foo.bar", "txt", "foo.bar.txt", true); - tfe!("foo.bar.baz", "txt", "foo.bar.baz.txt", true); - tfe!(".test", "txt", ".test.txt", true); - tfe!("foo.txt", "", "foo.txt", true); - tfe!("foo", "", "foo", true); - tfe!("", "foo", "", false); - tfe!(".", "foo", ".", false); - tfe!("foo/", "bar", "foo.bar", true); - tfe!("foo/.", "bar", "foo.bar", true); - tfe!("..", "foo", "..", false); - tfe!("foo/..", "bar", "foo/..", false); - tfe!("/", "foo", "/", false); - - // edge cases - tfe!("/foo.ext////", "bar", "/foo.ext.bar", true); -} - -#[test] -pub fn test_with_extension() { - macro_rules! twe ( - ($input:expr, $extension:expr, $expected:expr) => ({ - let input = Path::new($input); - let output = input.with_extension($extension); - - assert!( - output.to_str() == Some($expected), - "calling Path::new({:?}).with_extension({:?}): Expected {:?}, got {:?}", - $input, $extension, $expected, output, - ); - }); - ); - - twe!("foo", "txt", "foo.txt"); - twe!("foo.bar", "txt", "foo.txt"); - twe!("foo.bar.baz", "txt", "foo.bar.txt"); - twe!(".test", "txt", ".test.txt"); - twe!("foo.txt", "", "foo"); - twe!("foo", "", "foo"); - twe!("", "foo", ""); - twe!(".", "foo", "."); - twe!("foo/", "bar", "foo.bar"); - twe!("foo/.", "bar", "foo.bar"); - twe!("..", "foo", ".."); - twe!("foo/..", "bar", "foo/.."); - twe!("/", "foo", "/"); - - // New extension is smaller than file name - twe!("aaa_aaa_aaa", "bbb_bbb", "aaa_aaa_aaa.bbb_bbb"); - // New extension is greater than file name - twe!("bbb_bbb", "aaa_aaa_aaa", "bbb_bbb.aaa_aaa_aaa"); - - // New extension is smaller than previous extension - twe!("ccc.aaa_aaa_aaa", "bbb_bbb", "ccc.bbb_bbb"); - // New extension is greater than previous extension - twe!("ccc.bbb_bbb", "aaa_aaa_aaa", "ccc.aaa_aaa_aaa"); -} - -#[test] -pub fn test_with_added_extension() { - macro_rules! twe ( - ($input:expr, $extension:expr, $expected:expr) => ({ - let input = Path::new($input); - let output = input.with_added_extension($extension); - - assert!( - output.to_str() == Some($expected), - "calling Path::new({:?}).with_added_extension({:?}): Expected {:?}, got {:?}", - $input, $extension, $expected, output, - ); - }); - ); - - twe!("foo", "txt", "foo.txt"); - twe!("foo.bar", "txt", "foo.bar.txt"); - twe!("foo.bar.baz", "txt", "foo.bar.baz.txt"); - twe!(".test", "txt", ".test.txt"); - twe!("foo.txt", "", "foo.txt"); - twe!("foo", "", "foo"); - twe!("", "foo", ""); - twe!(".", "foo", "."); - twe!("foo/", "bar", "foo.bar"); - twe!("foo/.", "bar", "foo.bar"); - twe!("..", "foo", ".."); - twe!("foo/..", "bar", "foo/.."); - twe!("/", "foo", "/"); - - // edge cases - twe!("/foo.ext////", "bar", "/foo.ext.bar"); - - // New extension is smaller than file name - twe!("aaa_aaa_aaa", "bbb_bbb", "aaa_aaa_aaa.bbb_bbb"); - // New extension is greater than file name - twe!("bbb_bbb", "aaa_aaa_aaa", "bbb_bbb.aaa_aaa_aaa"); - - // New extension is smaller than previous extension - twe!("ccc.aaa_aaa_aaa", "bbb_bbb", "ccc.aaa_aaa_aaa.bbb_bbb"); - // New extension is greater than previous extension - twe!("ccc.bbb_bbb", "aaa_aaa_aaa", "ccc.bbb_bbb.aaa_aaa_aaa"); -} - -#[test] -fn test_eq_receivers() { - use crate::borrow::Cow; - - let borrowed: &Path = Path::new("foo/bar"); - let mut owned: PathBuf = PathBuf::new(); - owned.push("foo"); - owned.push("bar"); - let borrowed_cow: Cow<'_, Path> = borrowed.into(); - let owned_cow: Cow<'_, Path> = owned.clone().into(); - - macro_rules! t { - ($($current:expr),+) => { - $( - assert_eq!($current, borrowed); - assert_eq!($current, owned); - assert_eq!($current, borrowed_cow); - assert_eq!($current, owned_cow); - )+ - } - } - - t!(borrowed, owned, borrowed_cow, owned_cow); -} - -#[test] -pub fn test_compare() { - use crate::hash::{DefaultHasher, Hash, Hasher}; - - fn hash(t: T) -> u64 { - let mut s = DefaultHasher::new(); - t.hash(&mut s); - s.finish() - } - - macro_rules! tc ( - ($path1:expr, $path2:expr, eq: $eq:expr, - starts_with: $starts_with:expr, ends_with: $ends_with:expr, - relative_from: $relative_from:expr) => ({ - let path1 = Path::new($path1); - let path2 = Path::new($path2); - - let eq = path1 == path2; - assert!(eq == $eq, "{:?} == {:?}, expected {:?}, got {:?}", - $path1, $path2, $eq, eq); - assert!($eq == (hash(path1) == hash(path2)), - "{:?} == {:?}, expected {:?}, got {} and {}", - $path1, $path2, $eq, hash(path1), hash(path2)); - - let starts_with = path1.starts_with(path2); - assert!(starts_with == $starts_with, - "{:?}.starts_with({:?}), expected {:?}, got {:?}", $path1, $path2, - $starts_with, starts_with); - - let ends_with = path1.ends_with(path2); - assert!(ends_with == $ends_with, - "{:?}.ends_with({:?}), expected {:?}, got {:?}", $path1, $path2, - $ends_with, ends_with); - - let relative_from = path1.strip_prefix(path2) - .map(|p| p.to_str().unwrap()) - .ok(); - let exp: Option<&str> = $relative_from; - assert!(relative_from == exp, - "{:?}.strip_prefix({:?}), expected {:?}, got {:?}", - $path1, $path2, exp, relative_from); - }); - ); - - tc!("", "", - eq: true, - starts_with: true, - ends_with: true, - relative_from: Some("") - ); - - tc!("foo", "", - eq: false, - starts_with: true, - ends_with: true, - relative_from: Some("foo") - ); - - tc!("", "foo", - eq: false, - starts_with: false, - ends_with: false, - relative_from: None - ); - - tc!("foo", "foo", - eq: true, - starts_with: true, - ends_with: true, - relative_from: Some("") - ); - - tc!("foo/", "foo", - eq: true, - starts_with: true, - ends_with: true, - relative_from: Some("") - ); - - tc!("foo//", "foo", - eq: true, - starts_with: true, - ends_with: true, - relative_from: Some("") - ); - - tc!("foo///", "foo", - eq: true, - starts_with: true, - ends_with: true, - relative_from: Some("") - ); - - tc!("foo/.", "foo", - eq: true, - starts_with: true, - ends_with: true, - relative_from: Some("") - ); - - tc!("foo/./bar", "foo/bar", - eq: true, - starts_with: true, - ends_with: true, - relative_from: Some("") - ); - - tc!("foo/.//bar", "foo/bar", - eq: true, - starts_with: true, - ends_with: true, - relative_from: Some("") - ); - - tc!("foo//./bar", "foo/bar", - eq: true, - starts_with: true, - ends_with: true, - relative_from: Some("") - ); - - tc!("foo/bar", "foo", - eq: false, - starts_with: true, - ends_with: false, - relative_from: Some("bar") - ); - - tc!("foo/bar", "foobar", - eq: false, - starts_with: false, - ends_with: false, - relative_from: None - ); - - tc!("foo/bar/baz", "foo/bar", - eq: false, - starts_with: true, - ends_with: false, - relative_from: Some("baz") - ); - - tc!("foo/bar", "foo/bar/baz", - eq: false, - starts_with: false, - ends_with: false, - relative_from: None - ); - - tc!("./foo/bar/", ".", - eq: false, - starts_with: true, - ends_with: false, - relative_from: Some("foo/bar") - ); - - if cfg!(windows) { - tc!(r"C:\src\rust\cargo-test\test\Cargo.toml", - r"c:\src\rust\cargo-test\test", - eq: false, - starts_with: true, - ends_with: false, - relative_from: Some("Cargo.toml") - ); - - tc!(r"c:\foo", r"C:\foo", - eq: true, - starts_with: true, - ends_with: true, - relative_from: Some("") - ); - - tc!(r"C:\foo\.\bar.txt", r"C:\foo\bar.txt", - eq: true, - starts_with: true, - ends_with: true, - relative_from: Some("") - ); - - tc!(r"C:\foo\.", r"C:\foo", - eq: true, - starts_with: true, - ends_with: true, - relative_from: Some("") - ); - - tc!(r"\\?\C:\foo\.\bar.txt", r"\\?\C:\foo\bar.txt", - eq: false, - starts_with: false, - ends_with: false, - relative_from: None - ); - } -} - -#[test] -fn test_components_debug() { - let path = Path::new("/tmp"); - - let mut components = path.components(); - - let expected = "Components([RootDir, Normal(\"tmp\")])"; - let actual = format!("{components:?}"); - assert_eq!(expected, actual); - - let _ = components.next().unwrap(); - let expected = "Components([Normal(\"tmp\")])"; - let actual = format!("{components:?}"); - assert_eq!(expected, actual); - - let _ = components.next().unwrap(); - let expected = "Components([])"; - let actual = format!("{components:?}"); - assert_eq!(expected, actual); -} - -#[cfg(any(unix, target_os = "wasi"))] -#[test] -fn test_iter_debug() { - let path = Path::new("/tmp"); - - let mut iter = path.iter(); - - let expected = "Iter([\"/\", \"tmp\"])"; - let actual = format!("{iter:?}"); - assert_eq!(expected, actual); - - let _ = iter.next().unwrap(); - let expected = "Iter([\"tmp\"])"; - let actual = format!("{iter:?}"); - assert_eq!(expected, actual); - - let _ = iter.next().unwrap(); - let expected = "Iter([])"; - let actual = format!("{iter:?}"); - assert_eq!(expected, actual); -} - -#[test] -fn into_boxed() { - let orig: &str = "some/sort/of/path"; - let path = Path::new(orig); - let boxed: Box = Box::from(path); - let path_buf = path.to_owned().into_boxed_path().into_path_buf(); - assert_eq!(path, &*boxed); - assert_eq!(&*boxed, &*path_buf); - assert_eq!(&*path_buf, path); -} - -#[test] -fn test_clone_into() { - let mut path_buf = PathBuf::from("supercalifragilisticexpialidocious"); - let path = Path::new("short"); - path.clone_into(&mut path_buf); - assert_eq!(path, path_buf); - assert!(path_buf.into_os_string().capacity() >= 15); -} - -#[test] -fn display_format_flags() { - assert_eq!(format!("a{:#<5}b", Path::new("").display()), "a#####b"); - assert_eq!(format!("a{:#<5}b", Path::new("a").display()), "aa####b"); -} - -#[test] -fn into_rc() { - let orig = "hello/world"; - let path = Path::new(orig); - let rc: Rc = Rc::from(path); - let arc: Arc = Arc::from(path); - - assert_eq!(&*rc, path); - assert_eq!(&*arc, path); - - let rc2: Rc = Rc::from(path.to_owned()); - let arc2: Arc = Arc::from(path.to_owned()); - - assert_eq!(&*rc2, path); - assert_eq!(&*arc2, path); -} - -#[test] -fn test_ord() { - macro_rules! ord( - ($ord:ident, $left:expr, $right:expr) => ({ - use core::cmp::Ordering; - - let left = Path::new($left); - let right = Path::new($right); - assert_eq!(left.cmp(&right), Ordering::$ord); - if (core::cmp::Ordering::$ord == Ordering::Equal) { - assert_eq!(left, right); - - let mut hasher = DefaultHasher::new(); - left.hash(&mut hasher); - let left_hash = hasher.finish(); - hasher = DefaultHasher::new(); - right.hash(&mut hasher); - let right_hash = hasher.finish(); - - assert_eq!(left_hash, right_hash, "hashes for {:?} and {:?} must match", left, right); - } else { - assert_ne!(left, right); - } - }); - ); - - ord!(Less, "1", "2"); - ord!(Less, "/foo/bar", "/foo./bar"); - ord!(Less, "foo/bar", "foo/bar."); - ord!(Equal, "foo/./bar", "foo/bar/"); - ord!(Equal, "foo/bar", "foo/bar/"); - ord!(Equal, "foo/bar", "foo/bar/."); - ord!(Equal, "foo/bar", "foo/bar//"); -} - -#[test] -#[cfg(any(unix, target_os = "wasi"))] -fn test_unix_absolute() { - use crate::path::absolute; - - assert!(absolute("").is_err()); - - let relative = "a/b"; - let mut expected = crate::env::current_dir().unwrap(); - expected.push(relative); - assert_eq!(absolute(relative).unwrap().as_os_str(), expected.as_os_str()); - - // Test how components are collected. - assert_eq!(absolute("/a/b/c").unwrap().as_os_str(), Path::new("/a/b/c").as_os_str()); - assert_eq!(absolute("/a//b/c").unwrap().as_os_str(), Path::new("/a/b/c").as_os_str()); - assert_eq!(absolute("//a/b/c").unwrap().as_os_str(), Path::new("//a/b/c").as_os_str()); - assert_eq!(absolute("///a/b/c").unwrap().as_os_str(), Path::new("/a/b/c").as_os_str()); - assert_eq!(absolute("/a/b/c/").unwrap().as_os_str(), Path::new("/a/b/c/").as_os_str()); - assert_eq!( - absolute("/a/./b/../c/.././..").unwrap().as_os_str(), - Path::new("/a/b/../c/../..").as_os_str() - ); - - // Test leading `.` and `..` components - let curdir = crate::env::current_dir().unwrap(); - assert_eq!(absolute("./a").unwrap().as_os_str(), curdir.join("a").as_os_str()); - assert_eq!(absolute("../a").unwrap().as_os_str(), curdir.join("../a").as_os_str()); // return /pwd/../a -} - -#[test] -#[cfg(windows)] -fn test_windows_absolute() { - use crate::path::absolute; - // An empty path is an error. - assert!(absolute("").is_err()); - - let relative = r"a\b"; - let mut expected = crate::env::current_dir().unwrap(); - expected.push(relative); - assert_eq!(absolute(relative).unwrap().as_os_str(), expected.as_os_str()); - - macro_rules! unchanged( - ($path:expr) => { - assert_eq!(absolute($path).unwrap().as_os_str(), Path::new($path).as_os_str()); - } - ); - - unchanged!(r"C:\path\to\file"); - unchanged!(r"C:\path\to\file\"); - unchanged!(r"\\server\share\to\file"); - unchanged!(r"\\server.\share.\to\file"); - unchanged!(r"\\.\PIPE\name"); - unchanged!(r"\\.\C:\path\to\COM1"); - unchanged!(r"\\?\C:\path\to\file"); - unchanged!(r"\\?\UNC\server\share\to\file"); - unchanged!(r"\\?\PIPE\name"); - // Verbatim paths are always unchanged, no matter what. - unchanged!(r"\\?\path.\to/file.."); - - assert_eq!( - absolute(r"C:\path..\to.\file.").unwrap().as_os_str(), - Path::new(r"C:\path..\to\file").as_os_str() - ); - assert_eq!(absolute(r"COM1").unwrap().as_os_str(), Path::new(r"\\.\COM1").as_os_str()); -} - -#[test] -#[should_panic = "path separator"] -fn test_extension_path_sep() { - let mut path = PathBuf::from("path/to/file"); - path.set_extension("d/../../../../../etc/passwd"); -} - -#[test] -#[should_panic = "path separator"] -#[cfg(windows)] -fn test_extension_path_sep_alternate() { - let mut path = PathBuf::from("path/to/file"); - path.set_extension("d\\test"); -} - -#[test] -#[cfg(not(windows))] -fn test_extension_path_sep_alternate() { - let mut path = PathBuf::from("path/to/file"); - path.set_extension("d\\test"); - assert_eq!(path, Path::new("path/to/file.d\\test")); -} - -#[bench] -#[cfg_attr(miri, ignore)] // Miri isn't fast... -fn bench_path_cmp_fast_path_buf_sort(b: &mut test::Bencher) { - let prefix = "my/home"; - let mut paths: Vec<_> = - (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); - - paths.sort(); - - b.iter(|| { - black_box(paths.as_mut_slice()).sort_unstable(); - }); -} - -#[bench] -#[cfg_attr(miri, ignore)] // Miri isn't fast... -fn bench_path_cmp_fast_path_long(b: &mut test::Bencher) { - let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; - let paths: Vec<_> = - (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); - - let mut set = BTreeSet::new(); - - paths.iter().for_each(|p| { - set.insert(p.as_path()); - }); - - b.iter(|| { - set.remove(paths[500].as_path()); - set.insert(paths[500].as_path()); - }); -} - -#[bench] -#[cfg_attr(miri, ignore)] // Miri isn't fast... -fn bench_path_cmp_fast_path_short(b: &mut test::Bencher) { - let prefix = "my/home"; - let paths: Vec<_> = - (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); - - let mut set = BTreeSet::new(); - - paths.iter().for_each(|p| { - set.insert(p.as_path()); - }); - - b.iter(|| { - set.remove(paths[500].as_path()); - set.insert(paths[500].as_path()); - }); -} - -#[bench] -#[cfg_attr(miri, ignore)] // Miri isn't fast... -fn bench_path_hashset(b: &mut test::Bencher) { - let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; - let paths: Vec<_> = - (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); - - let mut set = HashSet::new(); - - paths.iter().for_each(|p| { - set.insert(p.as_path()); - }); - - b.iter(|| { - set.remove(paths[500].as_path()); - set.insert(black_box(paths[500].as_path())) - }); -} - -#[bench] -#[cfg_attr(miri, ignore)] // Miri isn't fast... -fn bench_path_hashset_miss(b: &mut test::Bencher) { - let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/"; - let paths: Vec<_> = - (0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect(); - - let mut set = HashSet::new(); - - paths.iter().for_each(|p| { - set.insert(p.as_path()); - }); - - let probe = PathBuf::from(prefix).join("other"); - - b.iter(|| set.remove(black_box(probe.as_path()))); -} - -#[bench] -fn bench_hash_path_short(b: &mut test::Bencher) { - let mut hasher = DefaultHasher::new(); - let path = Path::new("explorer.exe"); - - b.iter(|| black_box(path).hash(&mut hasher)); - - black_box(hasher.finish()); -} - -#[bench] -fn bench_hash_path_long(b: &mut test::Bencher) { - let mut hasher = DefaultHasher::new(); - let path = - Path::new("/aaaaa/aaaaaa/./../aaaaaaaa/bbbbbbbbbbbbb/ccccccccccc/ddddddddd/eeeeeee.fff"); - - b.iter(|| black_box(path).hash(&mut hasher)); - - black_box(hasher.finish()); -} - -#[test] -fn clone_to_uninit() { - let a = Path::new("hello.txt"); - - let mut storage = vec![MaybeUninit::::uninit(); size_of_val::(a)]; - unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()).cast()) }; - assert_eq!(a.as_os_str().as_encoded_bytes(), unsafe { - MaybeUninit::slice_assume_init_ref(&storage) - }); - - let mut b: Box = Path::new("world.exe").into(); - assert_eq!(size_of_val::(a), size_of_val::(&b)); - assert_ne!(a, &*b); - unsafe { a.clone_to_uninit(ptr::from_mut::(&mut b).cast()) }; - assert_eq!(a, &*b); -} diff --git a/library/std/src/pipe.rs b/library/std/src/pipe.rs deleted file mode 100644 index 06f3fd9fdffe8..0000000000000 --- a/library/std/src/pipe.rs +++ /dev/null @@ -1,258 +0,0 @@ -//! A cross-platform anonymous pipe. -//! -//! This module provides support for anonymous OS pipes, like [pipe] on Linux or [CreatePipe] on -//! Windows. -//! -//! # Behavior -//! -//! A pipe is a synchronous, unidirectional data channel between two or more processes, like an -//! interprocess [`mpsc`](crate::sync::mpsc) provided by the OS. In particular: -//! -//! * A read on a [`PipeReader`] blocks until the pipe is non-empty. -//! * A write on a [`PipeWriter`] blocks when the pipe is full. -//! * When all copies of a [`PipeWriter`] are closed, a read on the corresponding [`PipeReader`] -//! returns EOF. -//! * [`PipeReader`] can be shared, but only one process will consume the data in the pipe. -//! -//! # Capacity -//! -//! Pipe capacity is platform dependent. To quote the Linux [man page]: -//! -//! > Different implementations have different limits for the pipe capacity. Applications should -//! > not rely on a particular capacity: an application should be designed so that a reading process -//! > consumes data as soon as it is available, so that a writing process does not remain blocked. -//! -//! # Examples -//! -//! ```no_run -//! #![feature(anonymous_pipe)] -//! # #[cfg(miri)] fn main() {} -//! # #[cfg(not(miri))] -//! # fn main() -> std::io::Result<()> { -//! # use std::process::Command; -//! # use std::io::{Read, Write}; -//! let (ping_rx, mut ping_tx) = std::pipe::pipe()?; -//! let (mut pong_rx, pong_tx) = std::pipe::pipe()?; -//! -//! // Spawn a process that echoes its input. -//! let mut echo_server = Command::new("cat").stdin(ping_rx).stdout(pong_tx).spawn()?; -//! -//! ping_tx.write_all(b"hello")?; -//! // Close to unblock echo_server's reader. -//! drop(ping_tx); -//! -//! let mut buf = String::new(); -//! // Block until echo_server's writer is closed. -//! pong_rx.read_to_string(&mut buf)?; -//! assert_eq!(&buf, "hello"); -//! -//! echo_server.wait()?; -//! # Ok(()) -//! # } -//! ``` -//! [pipe]: https://man7.org/linux/man-pages/man2/pipe.2.html -//! [CreatePipe]: https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe -//! [man page]: https://man7.org/linux/man-pages/man7/pipe.7.html -use crate::io; -use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner}; - -/// Create anonymous pipe that is close-on-exec and blocking. -/// -/// # Examples -/// -/// See the [module-level](crate::pipe) documentation for examples. -#[unstable(feature = "anonymous_pipe", issue = "127154")] -#[inline] -pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> { - pipe_inner().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer))) -} - -/// Read end of the anonymous pipe. -#[unstable(feature = "anonymous_pipe", issue = "127154")] -#[derive(Debug)] -pub struct PipeReader(pub(crate) AnonPipe); - -/// Write end of the anonymous pipe. -#[unstable(feature = "anonymous_pipe", issue = "127154")] -#[derive(Debug)] -pub struct PipeWriter(pub(crate) AnonPipe); - -impl PipeReader { - /// Create a new [`PipeReader`] instance that shares the same underlying file description. - /// - /// # Examples - /// - /// ```no_run - /// #![feature(anonymous_pipe)] - /// # #[cfg(miri)] fn main() {} - /// # #[cfg(not(miri))] - /// # fn main() -> std::io::Result<()> { - /// # use std::fs; - /// # use std::io::Write; - /// # use std::process::Command; - /// const NUM_SLOT: u8 = 2; - /// const NUM_PROC: u8 = 5; - /// const OUTPUT: &str = "work.txt"; - /// - /// let mut jobs = vec![]; - /// let (reader, mut writer) = std::pipe::pipe()?; - /// - /// // Write NUM_SLOT characters the the pipe. - /// writer.write_all(&[b'|'; NUM_SLOT as usize])?; - /// - /// // Spawn several processes that read a character from the pipe, do some work, then - /// // write back to the pipe. When the pipe is empty, the processes block, so only - /// // NUM_SLOT processes can be working at any given time. - /// for _ in 0..NUM_PROC { - /// jobs.push( - /// Command::new("bash") - /// .args(["-c", - /// &format!( - /// "read -n 1\n\ - /// echo -n 'x' >> '{OUTPUT}'\n\ - /// echo -n '|'", - /// ), - /// ]) - /// .stdin(reader.try_clone()?) - /// .stdout(writer.try_clone()?) - /// .spawn()?, - /// ); - /// } - /// - /// // Wait for all jobs to finish. - /// for mut job in jobs { - /// job.wait()?; - /// } - /// - /// // Check our work and clean up. - /// let xs = fs::read_to_string(OUTPUT)?; - /// fs::remove_file(OUTPUT)?; - /// assert_eq!(xs, "x".repeat(NUM_PROC.into())); - /// # Ok(()) - /// # } - /// ``` - #[unstable(feature = "anonymous_pipe", issue = "127154")] - pub fn try_clone(&self) -> io::Result { - self.0.try_clone().map(Self) - } -} - -impl PipeWriter { - /// Create a new [`PipeWriter`] instance that shares the same underlying file description. - /// - /// # Examples - /// - /// ```no_run - /// #![feature(anonymous_pipe)] - /// # #[cfg(miri)] fn main() {} - /// # #[cfg(not(miri))] - /// # fn main() -> std::io::Result<()> { - /// # use std::process::Command; - /// # use std::io::Read; - /// let (mut reader, writer) = std::pipe::pipe()?; - /// - /// // Spawn a process that writes to stdout and stderr. - /// let mut peer = Command::new("bash") - /// .args([ - /// "-c", - /// "echo -n foo\n\ - /// echo -n bar >&2" - /// ]) - /// .stdout(writer.try_clone()?) - /// .stderr(writer) - /// .spawn()?; - /// - /// // Read and check the result. - /// let mut msg = String::new(); - /// reader.read_to_string(&mut msg)?; - /// assert_eq!(&msg, "foobar"); - /// - /// peer.wait()?; - /// # Ok(()) - /// # } - /// ``` - #[unstable(feature = "anonymous_pipe", issue = "127154")] - pub fn try_clone(&self) -> io::Result { - self.0.try_clone().map(Self) - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl io::Read for &PipeReader { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } - fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - #[inline] - fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - self.0.read_to_end(buf) - } - fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { - self.0.read_buf(buf) - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl io::Read for PipeReader { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } - fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - #[inline] - fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { - self.0.read_to_end(buf) - } - fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { - self.0.read_buf(buf) - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl io::Write for &PipeWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - - fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } -} - -#[unstable(feature = "anonymous_pipe", issue = "127154")] -impl io::Write for PipeWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - - fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } -} diff --git a/library/std/src/pipe/tests.rs b/library/std/src/pipe/tests.rs deleted file mode 100644 index 9c38e10678752..0000000000000 --- a/library/std/src/pipe/tests.rs +++ /dev/null @@ -1,19 +0,0 @@ -use crate::io::{Read, Write}; -use crate::pipe::pipe; - -#[test] -#[cfg(all(windows, unix, not(miri)))] -fn pipe_creation_clone_and_rw() { - let (rx, tx) = pipe().unwrap(); - - tx.try_clone().unwrap().write_all(b"12345").unwrap(); - drop(tx); - - let mut rx2 = rx.try_clone().unwrap(); - drop(rx); - - let mut s = String::new(); - rx2.read_to_string(&mut s).unwrap(); - drop(rx2); - assert_eq!(s, "12345"); -} diff --git a/library/std/src/prelude/common.rs b/library/std/src/prelude/common.rs deleted file mode 100644 index 22a364074c52d..0000000000000 --- a/library/std/src/prelude/common.rs +++ /dev/null @@ -1,121 +0,0 @@ -//! Items common to the prelude of all editions. -//! -//! See the [module-level documentation](super) for more. - -// No formatting: this file is nothing but re-exports, and their order is worth preserving. -#![cfg_attr(rustfmt, rustfmt::skip)] - -// Re-exported core operators -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::marker::{Send, Sized, Sync, Unpin}; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::ops::{Drop, Fn, FnMut, FnOnce}; -#[cfg_attr(bootstrap, unstable(feature = "async_closure", issue = "62290"))] -#[cfg_attr(not(bootstrap), stable(feature = "async_closure", since = "CURRENT_RUSTC_VERSION"))] -#[doc(no_inline)] -pub use crate::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}; - -// Re-exported functions -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::mem::drop; -#[stable(feature = "size_of_prelude", since = "1.80.0")] -#[doc(no_inline)] -pub use crate::mem::{align_of, align_of_val, size_of, size_of_val}; - -// Re-exported types and traits -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::convert::{AsMut, AsRef, From, Into}; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::iter::{DoubleEndedIterator, ExactSizeIterator}; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::iter::{Extend, IntoIterator, Iterator}; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::option::Option::{self, None, Some}; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::result::Result::{self, Err, Ok}; - -// Re-exported built-in macros -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[allow(deprecated)] -#[doc(no_inline)] -pub use core::prelude::v1::{ - assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args, - format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env, - stringify, trace_macros, Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, -}; - -#[unstable( - feature = "concat_bytes", - issue = "87555", - reason = "`concat_bytes` is not stable enough for use and is subject to change" -)] -#[doc(no_inline)] -pub use core::prelude::v1::concat_bytes; - -// Do not `doc(no_inline)` so that they become doc items on their own -// (no public module for them to be re-exported from). -#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -pub use core::prelude::v1::{ - alloc_error_handler, bench, derive, global_allocator, test, test_case, -}; - -#[unstable(feature = "derive_const", issue = "none")] -pub use core::prelude::v1::derive_const; - -// Do not `doc(no_inline)` either. -#[unstable( - feature = "cfg_accessible", - issue = "64797", - reason = "`cfg_accessible` is not fully implemented" -)] -pub use core::prelude::v1::cfg_accessible; - -// Do not `doc(no_inline)` either. -#[unstable( - feature = "cfg_eval", - issue = "82679", - reason = "`cfg_eval` is a recently implemented feature" -)] -pub use core::prelude::v1::cfg_eval; - -// Do not `doc(no_inline)` either. -#[unstable( - feature = "type_ascription", - issue = "23416", - reason = "placeholder syntax for type ascription" -)] -pub use core::prelude::v1::type_ascribe; - -// Do not `doc(no_inline)` either. -#[unstable( - feature = "deref_patterns", - issue = "87121", - reason = "placeholder syntax for deref patterns" -)] -pub use core::prelude::v1::deref; - -// The file so far is equivalent to core/src/prelude/v1.rs. It is duplicated -// rather than glob imported because we want docs to show these re-exports as -// pointing to within `std`. -// Below are the items from the alloc crate. - -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::borrow::ToOwned; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::boxed::Box; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::string::{String, ToString}; -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(no_inline)] -pub use crate::vec::Vec; diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs index 64349987fcfb8..992a9207a7206 100644 --- a/library/std/src/prelude/mod.rs +++ b/library/std/src/prelude/mod.rs @@ -111,26 +111,7 @@ #![stable(feature = "rust1", since = "1.0.0")] -mod common; - -/// The first version of the prelude of The Rust Standard Library. -/// -/// See the [module-level documentation](self) for more. -#[stable(feature = "rust1", since = "1.0.0")] -pub mod v1 { - #[stable(feature = "rust1", since = "1.0.0")] - pub use super::common::*; - - // Do not `doc(inline)` these `doc(hidden)` items. - #[unstable( - feature = "rustc_encodable_decodable", - issue = "none", - soft, - reason = "derive macro for `rustc-serialize`; should not be used in new code" - )] - #[allow(deprecated)] - pub use core::prelude::v1::{RustcDecodable, RustcEncodable}; -} +pub mod v1; /// The 2015 version of the prelude of The Rust Standard Library. /// @@ -169,12 +150,13 @@ pub mod rust_2021 { /// The 2024 version of the prelude of The Rust Standard Library. /// /// See the [module-level documentation](self) for more. -#[stable(feature = "prelude_2024", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "prelude_2024", since = "1.85.0")] pub mod rust_2024 { #[stable(feature = "rust1", since = "1.0.0")] - pub use super::common::*; + #[doc(no_inline)] + pub use super::v1::*; - #[stable(feature = "prelude_2024", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "prelude_2024", since = "1.85.0")] #[doc(no_inline)] pub use core::prelude::rust_2024::*; } diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs new file mode 100644 index 0000000000000..5b324b2e91671 --- /dev/null +++ b/library/std/src/prelude/v1.rs @@ -0,0 +1,122 @@ +//! The first version of the prelude of The Rust Standard Library. +//! +//! See the [module-level documentation](super) for more. + +#![stable(feature = "rust1", since = "1.0.0")] + +// No formatting: this file is nothing but re-exports, and their order is worth preserving. +#![cfg_attr(rustfmt, rustfmt::skip)] + +// Re-exported core operators +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::marker::{Send, Sized, Sync, Unpin}; +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::ops::{Drop, Fn, FnMut, FnOnce}; +#[stable(feature = "async_closure", since = "1.85.0")] +#[doc(no_inline)] +pub use crate::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}; + +// Re-exported functions +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::mem::drop; +#[stable(feature = "size_of_prelude", since = "1.80.0")] +#[doc(no_inline)] +pub use crate::mem::{align_of, align_of_val, size_of, size_of_val}; + +// Re-exported types and traits +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::convert::{AsMut, AsRef, From, Into}; +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::iter::{DoubleEndedIterator, ExactSizeIterator}; +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::iter::{Extend, IntoIterator, Iterator}; +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::option::Option::{self, None, Some}; +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::result::Result::{self, Err, Ok}; + +// Re-exported built-in macros +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[allow(deprecated)] +#[doc(no_inline)] +pub use core::prelude::v1::{ + assert, cfg, column, compile_error, concat, concat_idents, env, file, format_args, + format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env, + stringify, trace_macros, Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, +}; + +#[unstable( + feature = "concat_bytes", + issue = "87555", + reason = "`concat_bytes` is not stable enough for use and is subject to change" +)] +#[doc(no_inline)] +pub use core::prelude::v1::concat_bytes; + +// Do not `doc(no_inline)` so that they become doc items on their own +// (no public module for them to be re-exported from). +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +pub use core::prelude::v1::{ + alloc_error_handler, bench, derive, global_allocator, test, test_case, +}; + +#[unstable(feature = "derive_const", issue = "none")] +pub use core::prelude::v1::derive_const; + +// Do not `doc(no_inline)` either. +#[unstable( + feature = "cfg_accessible", + issue = "64797", + reason = "`cfg_accessible` is not fully implemented" +)] +pub use core::prelude::v1::cfg_accessible; + +// Do not `doc(no_inline)` either. +#[unstable( + feature = "cfg_eval", + issue = "82679", + reason = "`cfg_eval` is a recently implemented feature" +)] +pub use core::prelude::v1::cfg_eval; + +// Do not `doc(no_inline)` either. +#[unstable( + feature = "type_ascription", + issue = "23416", + reason = "placeholder syntax for type ascription" +)] +pub use core::prelude::v1::type_ascribe; + +// Do not `doc(no_inline)` either. +#[unstable( + feature = "deref_patterns", + issue = "87121", + reason = "placeholder syntax for deref patterns" +)] +pub use core::prelude::v1::deref; + +// The file so far is equivalent to core/src/prelude/v1.rs. It is duplicated +// rather than glob imported because we want docs to show these re-exports as +// pointing to within `std`. +// Below are the items from the alloc crate. + +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::borrow::ToOwned; +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::boxed::Box; +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::string::{String, ToString}; +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use crate::vec::Vec; diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 929d2b57afe5c..fd0fd1cb755e0 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -868,13 +868,17 @@ impl Command { /// /// # Examples /// + /// Prevent any inherited `GIT_DIR` variable from changing the target of the `git` command, + /// while allowing all other variables, like `GIT_AUTHOR_NAME`. + /// /// ```no_run /// use std::process::Command; /// - /// Command::new("ls") - /// .env_remove("PATH") - /// .spawn() - /// .expect("ls command failed to start"); + /// Command::new("git") + /// .arg("commit") + /// .env_remove("GIT_DIR") + /// .spawn()?; + /// # std::io::Result::Ok(()) /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn env_remove>(&mut self, key: K) -> &mut Command { @@ -896,13 +900,17 @@ impl Command { /// /// # Examples /// + /// The behavior of `sort` is affected by `LANG` and `LC_*` environment variables. + /// Clearing the environment makes `sort`'s behavior independent of the parent processes' language. + /// /// ```no_run /// use std::process::Command; /// - /// Command::new("ls") + /// Command::new("sort") + /// .arg("file.txt") /// .env_clear() - /// .spawn() - /// .expect("ls command failed to start"); + /// .spawn()?; + /// # std::io::Result::Ok(()) /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn env_clear(&mut self) -> &mut Command { @@ -1283,13 +1291,13 @@ impl fmt::Debug for Output { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let stdout_utf8 = str::from_utf8(&self.stdout); let stdout_debug: &dyn fmt::Debug = match stdout_utf8 { - Ok(ref str) => str, + Ok(ref s) => s, Err(_) => &self.stdout, }; let stderr_utf8 = str::from_utf8(&self.stderr); let stderr_debug: &dyn fmt::Debug = match stderr_utf8 { - Ok(ref str) => str, + Ok(ref s) => s, Err(_) => &self.stderr, }; @@ -2310,14 +2318,10 @@ pub fn exit(code: i32) -> ! { /// Terminates the process in an abnormal fashion. /// /// The function will never return and will immediately terminate the current -/// process in a platform specific "abnormal" manner. -/// -/// Note that because this function never returns, and that it terminates the -/// process, no destructors on the current stack or any other thread's stack -/// will be run. -/// -/// Rust IO buffers (eg, from `BufWriter`) will not be flushed. -/// Likewise, C stdio buffers will (on most platforms) not be flushed. +/// process in a platform specific "abnormal" manner. As a consequence, +/// no destructors on the current stack or any other thread's stack +/// will be run, Rust IO buffers (eg, from `BufWriter`) will not be flushed, +/// and C stdio buffers will (on most platforms) not be flushed. /// /// This is in contrast to the default behavior of [`panic!`] which unwinds /// the current thread's stack and calls all destructors. diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs index e8cbfe337bccf..5879914ca206a 100644 --- a/library/std/src/process/tests.rs +++ b/library/std/src/process/tests.rs @@ -323,9 +323,13 @@ fn test_capture_env_at_spawn() { // This variable will not be present if the environment has already // been captured above. - env::set_var("RUN_TEST_NEW_ENV2", "456"); + unsafe { + env::set_var("RUN_TEST_NEW_ENV2", "456"); + } let result = cmd.output().unwrap(); - env::remove_var("RUN_TEST_NEW_ENV2"); + unsafe { + env::remove_var("RUN_TEST_NEW_ENV2"); + } let output = String::from_utf8_lossy(&result.stdout).to_string(); @@ -391,145 +395,6 @@ fn test_interior_nul_in_env_value_is_error() { } } -/// Tests that process creation flags work by debugging a process. -/// Other creation flags make it hard or impossible to detect -/// behavioral changes in the process. -#[test] -#[cfg(windows)] -fn test_creation_flags() { - use crate::os::windows::process::CommandExt; - use crate::sys::c::{BOOL, INFINITE}; - #[repr(C)] - struct DEBUG_EVENT { - pub event_code: u32, - pub process_id: u32, - pub thread_id: u32, - // This is a union in the real struct, but we don't - // need this data for the purposes of this test. - pub _junk: [u8; 164], - } - - extern "system" { - fn WaitForDebugEvent(lpDebugEvent: *mut DEBUG_EVENT, dwMilliseconds: u32) -> BOOL; - fn ContinueDebugEvent(dwProcessId: u32, dwThreadId: u32, dwContinueStatus: u32) -> BOOL; - } - - const DEBUG_PROCESS: u32 = 1; - const EXIT_PROCESS_DEBUG_EVENT: u32 = 5; - const DBG_EXCEPTION_NOT_HANDLED: u32 = 0x80010001; - - let mut child = - Command::new("cmd").creation_flags(DEBUG_PROCESS).stdin(Stdio::piped()).spawn().unwrap(); - child.stdin.take().unwrap().write_all(b"exit\r\n").unwrap(); - let mut events = 0; - let mut event = DEBUG_EVENT { event_code: 0, process_id: 0, thread_id: 0, _junk: [0; 164] }; - loop { - if unsafe { WaitForDebugEvent(&mut event as *mut DEBUG_EVENT, INFINITE) } == 0 { - panic!("WaitForDebugEvent failed!"); - } - events += 1; - - if event.event_code == EXIT_PROCESS_DEBUG_EVENT { - break; - } - - if unsafe { - ContinueDebugEvent(event.process_id, event.thread_id, DBG_EXCEPTION_NOT_HANDLED) - } == 0 - { - panic!("ContinueDebugEvent failed!"); - } - } - assert!(events > 0); -} - -/// Tests proc thread attributes by spawning a process with a custom parent process, -/// then comparing the parent process ID with the expected parent process ID. -#[test] -#[cfg(windows)] -fn test_proc_thread_attributes() { - use crate::mem; - use crate::os::windows::io::AsRawHandle; - use crate::os::windows::process::{CommandExt, ProcThreadAttributeList}; - use crate::sys::c::{BOOL, CloseHandle, HANDLE}; - use crate::sys::cvt; - - #[repr(C)] - #[allow(non_snake_case)] - struct PROCESSENTRY32W { - dwSize: u32, - cntUsage: u32, - th32ProcessID: u32, - th32DefaultHeapID: usize, - th32ModuleID: u32, - cntThreads: u32, - th32ParentProcessID: u32, - pcPriClassBase: i32, - dwFlags: u32, - szExeFile: [u16; 260], - } - - extern "system" { - fn CreateToolhelp32Snapshot(dwflags: u32, th32processid: u32) -> HANDLE; - fn Process32First(hsnapshot: HANDLE, lppe: *mut PROCESSENTRY32W) -> BOOL; - fn Process32Next(hsnapshot: HANDLE, lppe: *mut PROCESSENTRY32W) -> BOOL; - } - - const PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: usize = 0x00020000; - const TH32CS_SNAPPROCESS: u32 = 0x00000002; - - struct ProcessDropGuard(crate::process::Child); - - impl Drop for ProcessDropGuard { - fn drop(&mut self) { - let _ = self.0.kill(); - } - } - - let parent = ProcessDropGuard(Command::new("cmd").spawn().unwrap()); - - let mut child_cmd = Command::new("cmd"); - - let parent_process_handle = parent.0.as_raw_handle(); - - let mut attribute_list = ProcThreadAttributeList::build() - .attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parent_process_handle) - .finish() - .unwrap(); - - let child = ProcessDropGuard(child_cmd.spawn_with_attributes(&mut attribute_list).unwrap()); - - let h_snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) }; - - let mut process_entry = PROCESSENTRY32W { - dwSize: mem::size_of::() as u32, - cntUsage: 0, - th32ProcessID: 0, - th32DefaultHeapID: 0, - th32ModuleID: 0, - cntThreads: 0, - th32ParentProcessID: 0, - pcPriClassBase: 0, - dwFlags: 0, - szExeFile: [0; 260], - }; - - unsafe { cvt(Process32First(h_snapshot, &mut process_entry as *mut _)) }.unwrap(); - - loop { - if child.0.id() == process_entry.th32ProcessID { - break; - } - unsafe { cvt(Process32Next(h_snapshot, &mut process_entry as *mut _)) }.unwrap(); - } - - unsafe { cvt(CloseHandle(h_snapshot)) }.unwrap(); - - assert_eq!(parent.0.id(), process_entry.th32ParentProcessID); - - drop(child) -} - #[test] fn test_command_implements_send_sync() { fn take_send_sync_type(_: T) {} @@ -688,7 +553,7 @@ fn debug_print() { #[test] #[cfg(windows)] fn run_bat_script() { - let tempdir = crate::sys_common::io::test::tmpdir(); + let tempdir = crate::test_helpers::tmpdir(); let script_path = tempdir.join("hello.cmd"); crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap(); @@ -707,7 +572,7 @@ fn run_bat_script() { #[test] #[cfg(windows)] fn run_canonical_bat_script() { - let tempdir = crate::sys_common::io::test::tmpdir(); + let tempdir = crate::test_helpers::tmpdir(); let script_path = tempdir.join("hello.cmd"); crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap(); diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index b2492238bd37b..3a22a16cb165e 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -23,7 +23,7 @@ pub use core::panicking::{panic_display, panic_fmt}; #[rustfmt::skip] use crate::any::Any; use crate::sync::Once; -use crate::thread::{self, Thread}; +use crate::thread::{self, main_thread}; use crate::{mem, panic, sys}; // Prints to the "panic output", depending on the platform this may be: @@ -32,9 +32,14 @@ use crate::{mem, panic, sys}; // - nothing (so this macro is a no-op) macro_rules! rtprintpanic { ($($t:tt)*) => { + #[cfg(not(feature = "panic_immediate_abort"))] if let Some(mut out) = crate::sys::stdio::panic_output() { let _ = crate::io::Write::write_fmt(&mut out, format_args!($($t)*)); } + #[cfg(feature = "panic_immediate_abort")] + { + let _ = format_args!($($t)*); + } } } @@ -67,7 +72,7 @@ macro_rules! rtunwrap { }; } -fn handle_rt_panic(e: Box) { +fn handle_rt_panic(e: Box) -> T { mem::forget(e); rtabort!("initialization or cleanup bug"); } @@ -102,24 +107,9 @@ unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { sys::init(argc, argv, sigpipe) }; - // Set up the current thread handle to give it the right name. - // - // When code running before main uses `ReentrantLock` (for example by - // using `println!`), the thread ID can become initialized before we - // create this handle. Since `set_current` fails when the ID of the - // handle does not match the current ID, we should attempt to use the - // current thread ID here instead of unconditionally creating a new - // one. Also see #130210. - let thread = unsafe { Thread::new_main(thread::current_id()) }; - if let Err(_thread) = thread::set_current(thread) { - // `thread::current` will create a new handle if none has been set yet. - // Thus, if someone uses it before main, this call will fail. That's a - // bad idea though, as we then cannot set the main thread name here. - // - // FIXME: detect the main thread in `thread::current` and use the - // correct name there. - rtabort!("code running before main must not use thread::current"); - } + // Remember the main thread ID to give it the correct name. + // SAFETY: this is the only time and place where we call this function. + unsafe { main_thread::set(thread::current_id()) }; } /// Clean up the thread-local runtime state. This *should* be run after all other @@ -157,7 +147,7 @@ fn lang_start_internal( argc: isize, argv: *const *const u8, sigpipe: u8, -) -> Result { +) -> isize { // Guard against the code called by this function from unwinding outside of the Rust-controlled // code, which is UB. This is a requirement imposed by a combination of how the // `#[lang="start"]` attribute is implemented as well as by the implementation of the panicking @@ -168,19 +158,33 @@ fn lang_start_internal( // panic is a std implementation bug. A quite likely one too, as there isn't any way to // prevent std from accidentally introducing a panic to these functions. Another is from // user code from `main` or, more nefariously, as described in e.g. issue #86030. - // SAFETY: Only called once during runtime initialization. - panic::catch_unwind(move || unsafe { init(argc, argv, sigpipe) }) - .unwrap_or_else(handle_rt_panic); - let ret_code = panic::catch_unwind(move || panic::catch_unwind(main).unwrap_or(101) as isize) - .map_err(move |e| { - mem::forget(e); - rtabort!("drop of the panic payload panicked"); + // + // We use `catch_unwind` with `handle_rt_panic` instead of `abort_unwind` to make the error in + // case of a panic a bit nicer. + panic::catch_unwind(move || { + // SAFETY: Only called once during runtime initialization. + unsafe { init(argc, argv, sigpipe) }; + + let ret_code = panic::catch_unwind(main).unwrap_or_else(move |payload| { + // Carefully dispose of the panic payload. + let payload = panic::AssertUnwindSafe(payload); + panic::catch_unwind(move || drop({ payload }.0)).unwrap_or_else(move |e| { + mem::forget(e); // do *not* drop the 2nd payload + rtabort!("drop of the panic payload panicked"); + }); + // Return error code for panicking programs. + 101 }); - panic::catch_unwind(cleanup).unwrap_or_else(handle_rt_panic); - // Guard against multiple threads calling `libc::exit` concurrently. - // See the documentation for `unique_thread_exit` for more information. - panic::catch_unwind(crate::sys::exit_guard::unique_thread_exit).unwrap_or_else(handle_rt_panic); - ret_code + let ret_code = ret_code as isize; + + cleanup(); + // Guard against multiple threads calling `libc::exit` concurrently. + // See the documentation for `unique_thread_exit` for more information. + crate::sys::exit_guard::unique_thread_exit(); + + ret_code + }) + .unwrap_or_else(handle_rt_panic) } #[cfg(not(any(test, doctest)))] @@ -191,11 +195,10 @@ fn lang_start( argv: *const *const u8, sigpipe: u8, ) -> isize { - let Ok(v) = lang_start_internal( + lang_start_internal( &move || crate::sys::backtrace::__rust_begin_short_backtrace(main).report().to_i32(), argc, argv, sigpipe, - ); - v + ) } diff --git a/library/std/src/sync/barrier.rs b/library/std/src/sync/barrier.rs index 862753e4765dc..067ff66d9af73 100644 --- a/library/std/src/sync/barrier.rs +++ b/library/std/src/sync/barrier.rs @@ -1,6 +1,3 @@ -#[cfg(test)] -mod tests; - use crate::fmt; // FIXME(nonpoison_mutex,nonpoison_condvar): switch to nonpoison versions once they are available use crate::sync::{Condvar, Mutex}; diff --git a/library/std/src/sync/barrier/tests.rs b/library/std/src/sync/barrier/tests.rs deleted file mode 100644 index 0fbcd9988127b..0000000000000 --- a/library/std/src/sync/barrier/tests.rs +++ /dev/null @@ -1,35 +0,0 @@ -use crate::sync::mpsc::{TryRecvError, channel}; -use crate::sync::{Arc, Barrier}; -use crate::thread; - -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads -fn test_barrier() { - const N: usize = 10; - - let barrier = Arc::new(Barrier::new(N)); - let (tx, rx) = channel(); - - for _ in 0..N - 1 { - let c = barrier.clone(); - let tx = tx.clone(); - thread::spawn(move || { - tx.send(c.wait().is_leader()).unwrap(); - }); - } - - // At this point, all spawned threads should be blocked, - // so we shouldn't get anything from the port - assert!(matches!(rx.try_recv(), Err(TryRecvError::Empty))); - - let mut leader_found = barrier.wait().is_leader(); - - // Now, the barrier is cleared and we should get data. - for _ in 0..N - 1 { - if rx.recv().unwrap() { - assert!(!leader_found); - leader_found = true; - } - } - assert!(leader_found); -} diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs index 1e4f9b79e0f4a..78cf8841efefb 100644 --- a/library/std/src/sync/lazy_lock.rs +++ b/library/std/src/sync/lazy_lock.rs @@ -31,7 +31,7 @@ union Data { /// ``` /// use std::sync::LazyLock; /// -/// // n.b. static items do not call [`Drop`] on program termination, so this won't be deallocated. +/// // Note: static items do not call [`Drop`] on program termination, so this won't be deallocated. /// // this is fine, as the OS can deallocate the terminated program faster than we can free memory /// // but tools like valgrind might report "memory leaks" as it isn't obvious this is intentional. /// static DEEP_THOUGHT: LazyLock = LazyLock::new(|| { @@ -350,6 +350,3 @@ unsafe impl Sync for LazyLock {} impl RefUnwindSafe for LazyLock {} #[stable(feature = "lazy_cell", since = "1.80.0")] impl UnwindSafe for LazyLock {} - -#[cfg(test)] -mod tests; diff --git a/library/std/src/sync/lazy_lock/tests.rs b/library/std/src/sync/lazy_lock/tests.rs deleted file mode 100644 index 7d7dde5434990..0000000000000 --- a/library/std/src/sync/lazy_lock/tests.rs +++ /dev/null @@ -1,167 +0,0 @@ -use crate::cell::LazyCell; -use crate::sync::atomic::AtomicUsize; -use crate::sync::atomic::Ordering::SeqCst; -use crate::sync::{LazyLock, Mutex, OnceLock}; -use crate::{panic, thread}; - -fn spawn_and_wait(f: impl FnOnce() -> R + Send + 'static) -> R { - thread::spawn(f).join().unwrap() -} - -#[test] -fn lazy_default() { - static CALLED: AtomicUsize = AtomicUsize::new(0); - - struct Foo(u8); - impl Default for Foo { - fn default() -> Self { - CALLED.fetch_add(1, SeqCst); - Foo(42) - } - } - - let lazy: LazyCell> = <_>::default(); - - assert_eq!(CALLED.load(SeqCst), 0); - - assert_eq!(lazy.lock().unwrap().0, 42); - assert_eq!(CALLED.load(SeqCst), 1); - - lazy.lock().unwrap().0 = 21; - - assert_eq!(lazy.lock().unwrap().0, 21); - assert_eq!(CALLED.load(SeqCst), 1); -} - -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn lazy_poisoning() { - let x: LazyCell = LazyCell::new(|| panic!("kaboom")); - for _ in 0..2 { - let res = panic::catch_unwind(panic::AssertUnwindSafe(|| x.len())); - assert!(res.is_err()); - } -} - -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads -fn sync_lazy_new() { - static CALLED: AtomicUsize = AtomicUsize::new(0); - static SYNC_LAZY: LazyLock = LazyLock::new(|| { - CALLED.fetch_add(1, SeqCst); - 92 - }); - - assert_eq!(CALLED.load(SeqCst), 0); - - spawn_and_wait(|| { - let y = *SYNC_LAZY - 30; - assert_eq!(y, 62); - assert_eq!(CALLED.load(SeqCst), 1); - }); - - let y = *SYNC_LAZY - 30; - assert_eq!(y, 62); - assert_eq!(CALLED.load(SeqCst), 1); -} - -#[test] -fn sync_lazy_default() { - static CALLED: AtomicUsize = AtomicUsize::new(0); - - struct Foo(u8); - impl Default for Foo { - fn default() -> Self { - CALLED.fetch_add(1, SeqCst); - Foo(42) - } - } - - let lazy: LazyLock> = <_>::default(); - - assert_eq!(CALLED.load(SeqCst), 0); - - assert_eq!(lazy.lock().unwrap().0, 42); - assert_eq!(CALLED.load(SeqCst), 1); - - lazy.lock().unwrap().0 = 21; - - assert_eq!(lazy.lock().unwrap().0, 21); - assert_eq!(CALLED.load(SeqCst), 1); -} - -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads -fn static_sync_lazy() { - static XS: LazyLock> = LazyLock::new(|| { - let mut xs = Vec::new(); - xs.push(1); - xs.push(2); - xs.push(3); - xs - }); - - spawn_and_wait(|| { - assert_eq!(&*XS, &vec![1, 2, 3]); - }); - - assert_eq!(&*XS, &vec![1, 2, 3]); -} - -#[test] -fn static_sync_lazy_via_fn() { - fn xs() -> &'static Vec { - static XS: OnceLock> = OnceLock::new(); - XS.get_or_init(|| { - let mut xs = Vec::new(); - xs.push(1); - xs.push(2); - xs.push(3); - xs - }) - } - assert_eq!(xs(), &vec![1, 2, 3]); -} - -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn sync_lazy_poisoning() { - let x: LazyLock = LazyLock::new(|| panic!("kaboom")); - for _ in 0..2 { - let res = panic::catch_unwind(|| x.len()); - assert!(res.is_err()); - } -} - -// Check that we can infer `T` from closure's type. -#[test] -fn lazy_type_inference() { - let _ = LazyCell::new(|| ()); -} - -#[test] -fn is_sync_send() { - fn assert_traits() {} - assert_traits::>(); -} - -#[test] -#[should_panic = "has previously been poisoned"] -fn lazy_force_mut_panic() { - let mut lazy = LazyLock::::new(|| panic!()); - crate::panic::catch_unwind(crate::panic::AssertUnwindSafe(|| { - let _ = LazyLock::force_mut(&mut lazy); - })) - .unwrap_err(); - let _ = &*lazy; -} - -#[test] -fn lazy_force_mut() { - let s = "abc".to_owned(); - let mut lazy = LazyLock::new(move || s); - LazyLock::force_mut(&mut lazy); - let p = LazyLock::force_mut(&mut lazy); - p.clear(); - LazyLock::force_mut(&mut lazy); -} diff --git a/library/std/src/sync/mpmc/mod.rs b/library/std/src/sync/mpmc/mod.rs index 0cf4902d6d59b..8caa2dcfad99c 100644 --- a/library/std/src/sync/mpmc/mod.rs +++ b/library/std/src/sync/mpmc/mod.rs @@ -18,7 +18,7 @@ //! infinite buffer. //! //! 2. A synchronous, bounded channel. The [`sync_channel`] function will -//! return a `(SyncSender, Receiver)` tuple where the storage for pending +//! return a `(Sender, Receiver)` tuple where the storage for pending //! messages is a pre-allocated buffer of a fixed size. All sends will be //! **synchronous** by blocking until there is buffer space available. Note //! that a bound of 0 is allowed, causing the channel to become a "rendezvous" @@ -360,9 +360,17 @@ impl Sender { /// that a return value of [`Err`] means that the data will never be /// received, but a return value of [`Ok`] does *not* mean that the data /// will be received. It is possible for the corresponding receiver to - /// hang up immediately after this function returns [`Ok`]. + /// hang up immediately after this function returns [`Ok`]. However, if + /// the channel is zero-capacity, it acts as a rendezvous channel and a + /// return value of [`Ok`] means that the data has been received. /// - /// This method will never block the current thread. + /// If the channel is full and not disconnected, this call will block until + /// the send operation can proceed. If the channel becomes disconnected, + /// this call will wake up and return an error. The returned error contains + /// the original message. + /// + /// If called on a zero-capacity channel, this method will wait for a receive + /// operation to appear on the other side of the channel. /// /// # Examples /// @@ -608,9 +616,9 @@ impl Sender { #[unstable(feature = "mpmc_channel", issue = "126840")] pub fn same_channel(&self, other: &Sender) -> bool { match (&self.flavor, &other.flavor) { - (SenderFlavor::Array(ref a), SenderFlavor::Array(ref b)) => a == b, - (SenderFlavor::List(ref a), SenderFlavor::List(ref b)) => a == b, - (SenderFlavor::Zero(ref a), SenderFlavor::Zero(ref b)) => a == b, + (SenderFlavor::Array(a), SenderFlavor::Array(b)) => a == b, + (SenderFlavor::List(a), SenderFlavor::List(b)) => a == b, + (SenderFlavor::Zero(a), SenderFlavor::Zero(b)) => a == b, _ => false, } } @@ -650,7 +658,7 @@ impl fmt::Debug for Sender { } /// The receiving half of Rust's [`channel`] (or [`sync_channel`]) type. -/// Different threads can share this [`Sender`] by cloning it. +/// Different threads can share this [`Receiver`] by cloning it. /// /// Messages sent to the channel can be retrieved using [`recv`]. /// diff --git a/library/std/src/sync/mpmc/tests.rs b/library/std/src/sync/mpmc/tests.rs deleted file mode 100644 index ab14050df6c98..0000000000000 --- a/library/std/src/sync/mpmc/tests.rs +++ /dev/null @@ -1,728 +0,0 @@ -use super::*; -use crate::{env, thread}; - -pub fn stress_factor() -> usize { - match env::var("RUST_TEST_STRESS") { - Ok(val) => val.parse().unwrap(), - Err(..) => 1, - } -} - -#[test] -fn smoke() { - let (tx, rx) = channel::(); - tx.send(1).unwrap(); - assert_eq!(rx.recv().unwrap(), 1); -} - -#[test] -fn drop_full() { - let (tx, _rx) = channel::>(); - tx.send(Box::new(1)).unwrap(); -} - -#[test] -fn drop_full_shared() { - let (tx, _rx) = channel::>(); - drop(tx.clone()); - drop(tx.clone()); - tx.send(Box::new(1)).unwrap(); -} - -#[test] -fn smoke_shared() { - let (tx, rx) = channel::(); - tx.send(1).unwrap(); - assert_eq!(rx.recv().unwrap(), 1); - let tx = tx.clone(); - tx.send(1).unwrap(); - assert_eq!(rx.recv().unwrap(), 1); -} - -#[test] -fn smoke_threads() { - let (tx, rx) = channel::(); - let t1 = thread::spawn(move || { - for i in 0..2 { - tx.send(i).unwrap(); - } - }); - let t2 = thread::spawn(move || { - assert_eq!(rx.recv().unwrap(), 0); - assert_eq!(rx.recv().unwrap(), 1); - }); - t1.join().unwrap(); - t2.join().unwrap(); -} - -#[test] -fn smoke_port_gone() { - let (tx, rx) = channel::(); - drop(rx); - assert!(tx.send(1).is_err()); -} - -#[test] -fn smoke_shared_port_gone() { - let (tx, rx) = channel::(); - drop(rx); - assert!(tx.send(1).is_err()) -} - -#[test] -fn smoke_shared_port_gone2() { - let (tx, rx) = channel::(); - drop(rx); - let tx2 = tx.clone(); - drop(tx); - assert!(tx2.send(1).is_err()); -} - -#[test] -fn port_gone_concurrent() { - let (tx, rx) = channel::(); - let _t = thread::spawn(move || { - rx.recv().unwrap(); - }); - while tx.send(1).is_ok() {} -} - -#[test] -fn port_gone_concurrent_shared() { - let (tx, rx) = channel::(); - let tx2 = tx.clone(); - let _t = thread::spawn(move || { - rx.recv().unwrap(); - }); - while tx.send(1).is_ok() && tx2.send(1).is_ok() {} -} - -#[test] -fn smoke_chan_gone() { - let (tx, rx) = channel::(); - drop(tx); - assert!(rx.recv().is_err()); -} - -#[test] -fn smoke_chan_gone_shared() { - let (tx, rx) = channel::<()>(); - let tx2 = tx.clone(); - drop(tx); - drop(tx2); - assert!(rx.recv().is_err()); -} - -#[test] -fn chan_gone_concurrent() { - let (tx, rx) = channel::(); - let _t = thread::spawn(move || { - tx.send(1).unwrap(); - tx.send(1).unwrap(); - }); - while rx.recv().is_ok() {} -} - -#[test] -fn stress() { - let count = if cfg!(miri) { 100 } else { 10000 }; - let (tx, rx) = channel::(); - let t = thread::spawn(move || { - for _ in 0..count { - tx.send(1).unwrap(); - } - }); - for _ in 0..count { - assert_eq!(rx.recv().unwrap(), 1); - } - t.join().ok().expect("thread panicked"); -} - -#[test] -fn stress_shared() { - const AMT: u32 = if cfg!(miri) { 100 } else { 10000 }; - const NTHREADS: u32 = 8; - let (tx, rx) = channel::(); - - let t = thread::spawn(move || { - for _ in 0..AMT * NTHREADS { - assert_eq!(rx.recv().unwrap(), 1); - } - match rx.try_recv() { - Ok(..) => panic!(), - _ => {} - } - }); - - for _ in 0..NTHREADS { - let tx = tx.clone(); - thread::spawn(move || { - for _ in 0..AMT { - tx.send(1).unwrap(); - } - }); - } - drop(tx); - t.join().ok().expect("thread panicked"); -} - -#[test] -fn send_from_outside_runtime() { - let (tx1, rx1) = channel::<()>(); - let (tx2, rx2) = channel::(); - let t1 = thread::spawn(move || { - tx1.send(()).unwrap(); - for _ in 0..40 { - assert_eq!(rx2.recv().unwrap(), 1); - } - }); - rx1.recv().unwrap(); - let t2 = thread::spawn(move || { - for _ in 0..40 { - tx2.send(1).unwrap(); - } - }); - t1.join().ok().expect("thread panicked"); - t2.join().ok().expect("thread panicked"); -} - -#[test] -fn recv_from_outside_runtime() { - let (tx, rx) = channel::(); - let t = thread::spawn(move || { - for _ in 0..40 { - assert_eq!(rx.recv().unwrap(), 1); - } - }); - for _ in 0..40 { - tx.send(1).unwrap(); - } - t.join().ok().expect("thread panicked"); -} - -#[test] -fn no_runtime() { - let (tx1, rx1) = channel::(); - let (tx2, rx2) = channel::(); - let t1 = thread::spawn(move || { - assert_eq!(rx1.recv().unwrap(), 1); - tx2.send(2).unwrap(); - }); - let t2 = thread::spawn(move || { - tx1.send(1).unwrap(); - assert_eq!(rx2.recv().unwrap(), 2); - }); - t1.join().ok().expect("thread panicked"); - t2.join().ok().expect("thread panicked"); -} - -#[test] -fn oneshot_single_thread_close_port_first() { - // Simple test of closing without sending - let (_tx, rx) = channel::(); - drop(rx); -} - -#[test] -fn oneshot_single_thread_close_chan_first() { - // Simple test of closing without sending - let (tx, _rx) = channel::(); - drop(tx); -} - -#[test] -fn oneshot_single_thread_send_port_close() { - // Testing that the sender cleans up the payload if receiver is closed - let (tx, rx) = channel::>(); - drop(rx); - assert!(tx.send(Box::new(0)).is_err()); -} - -#[test] -fn oneshot_single_thread_recv_chan_close() { - // Receiving on a closed chan will panic - let res = thread::spawn(move || { - let (tx, rx) = channel::(); - drop(tx); - rx.recv().unwrap(); - }) - .join(); - // What is our res? - assert!(res.is_err()); -} - -#[test] -fn oneshot_single_thread_send_then_recv() { - let (tx, rx) = channel::>(); - tx.send(Box::new(10)).unwrap(); - assert!(*rx.recv().unwrap() == 10); -} - -#[test] -fn oneshot_single_thread_try_send_open() { - let (tx, rx) = channel::(); - assert!(tx.send(10).is_ok()); - assert!(rx.recv().unwrap() == 10); -} - -#[test] -fn oneshot_single_thread_try_send_closed() { - let (tx, rx) = channel::(); - drop(rx); - assert!(tx.send(10).is_err()); -} - -#[test] -fn oneshot_single_thread_try_recv_open() { - let (tx, rx) = channel::(); - tx.send(10).unwrap(); - assert!(rx.recv() == Ok(10)); -} - -#[test] -fn oneshot_single_thread_try_recv_closed() { - let (tx, rx) = channel::(); - drop(tx); - assert!(rx.recv().is_err()); -} - -#[test] -fn oneshot_single_thread_peek_data() { - let (tx, rx) = channel::(); - assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); - tx.send(10).unwrap(); - assert_eq!(rx.try_recv(), Ok(10)); -} - -#[test] -fn oneshot_single_thread_peek_close() { - let (tx, rx) = channel::(); - drop(tx); - assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); - assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); -} - -#[test] -fn oneshot_single_thread_peek_open() { - let (_tx, rx) = channel::(); - assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); -} - -#[test] -fn oneshot_multi_task_recv_then_send() { - let (tx, rx) = channel::>(); - let _t = thread::spawn(move || { - assert!(*rx.recv().unwrap() == 10); - }); - - tx.send(Box::new(10)).unwrap(); -} - -#[test] -fn oneshot_multi_task_recv_then_close() { - let (tx, rx) = channel::>(); - let _t = thread::spawn(move || { - drop(tx); - }); - let res = thread::spawn(move || { - assert!(*rx.recv().unwrap() == 10); - }) - .join(); - assert!(res.is_err()); -} - -#[test] -fn oneshot_multi_thread_close_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = channel::(); - let _t = thread::spawn(move || { - drop(rx); - }); - drop(tx); - } -} - -#[test] -fn oneshot_multi_thread_send_close_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = channel::(); - let _t = thread::spawn(move || { - drop(rx); - }); - let _ = thread::spawn(move || { - tx.send(1).unwrap(); - }) - .join(); - } -} - -#[test] -fn oneshot_multi_thread_recv_close_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = channel::(); - thread::spawn(move || { - let res = thread::spawn(move || { - rx.recv().unwrap(); - }) - .join(); - assert!(res.is_err()); - }); - let _t = thread::spawn(move || { - thread::spawn(move || { - drop(tx); - }); - }); - } -} - -#[test] -fn oneshot_multi_thread_send_recv_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = channel::>(); - let _t = thread::spawn(move || { - tx.send(Box::new(10)).unwrap(); - }); - assert!(*rx.recv().unwrap() == 10); - } -} - -#[test] -fn stream_send_recv_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = channel(); - - send(tx, 0); - recv(rx, 0); - - fn send(tx: Sender>, i: i32) { - if i == 10 { - return; - } - - thread::spawn(move || { - tx.send(Box::new(i)).unwrap(); - send(tx, i + 1); - }); - } - - fn recv(rx: Receiver>, i: i32) { - if i == 10 { - return; - } - - thread::spawn(move || { - assert!(*rx.recv().unwrap() == i); - recv(rx, i + 1); - }); - } - } -} - -#[test] -fn oneshot_single_thread_recv_timeout() { - let (tx, rx) = channel(); - tx.send(()).unwrap(); - assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); - assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); - tx.send(()).unwrap(); - assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); -} - -#[test] -fn stress_recv_timeout_two_threads() { - let (tx, rx) = channel(); - let stress = stress_factor() + 100; - let timeout = Duration::from_millis(100); - - thread::spawn(move || { - for i in 0..stress { - if i % 2 == 0 { - thread::sleep(timeout * 2); - } - tx.send(1usize).unwrap(); - } - }); - - let mut recv_count = 0; - loop { - match rx.recv_timeout(timeout) { - Ok(n) => { - assert_eq!(n, 1usize); - recv_count += 1; - } - Err(RecvTimeoutError::Timeout) => continue, - Err(RecvTimeoutError::Disconnected) => break, - } - } - - assert_eq!(recv_count, stress); -} - -#[test] -fn recv_timeout_upgrade() { - let (tx, rx) = channel::<()>(); - let timeout = Duration::from_millis(1); - let _tx_clone = tx.clone(); - - let start = Instant::now(); - assert_eq!(rx.recv_timeout(timeout), Err(RecvTimeoutError::Timeout)); - assert!(Instant::now() >= start + timeout); -} - -#[test] -fn stress_recv_timeout_shared() { - let (tx, rx) = channel(); - let stress = stress_factor() + 100; - - for i in 0..stress { - let tx = tx.clone(); - thread::spawn(move || { - thread::sleep(Duration::from_millis(i as u64 * 10)); - tx.send(1usize).unwrap(); - }); - } - - drop(tx); - - let mut recv_count = 0; - loop { - match rx.recv_timeout(Duration::from_millis(10)) { - Ok(n) => { - assert_eq!(n, 1usize); - recv_count += 1; - } - Err(RecvTimeoutError::Timeout) => continue, - Err(RecvTimeoutError::Disconnected) => break, - } - } - - assert_eq!(recv_count, stress); -} - -#[test] -fn very_long_recv_timeout_wont_panic() { - let (tx, rx) = channel::<()>(); - let join_handle = thread::spawn(move || rx.recv_timeout(Duration::from_secs(u64::MAX))); - thread::sleep(Duration::from_secs(1)); - assert!(tx.send(()).is_ok()); - assert_eq!(join_handle.join().unwrap(), Ok(())); -} - -#[test] -fn recv_a_lot() { - let count = if cfg!(miri) { 1000 } else { 10000 }; - // Regression test that we don't run out of stack in scheduler context - let (tx, rx) = channel(); - for _ in 0..count { - tx.send(()).unwrap(); - } - for _ in 0..count { - rx.recv().unwrap(); - } -} - -#[test] -fn shared_recv_timeout() { - let (tx, rx) = channel(); - let total = 5; - for _ in 0..total { - let tx = tx.clone(); - thread::spawn(move || { - tx.send(()).unwrap(); - }); - } - - for _ in 0..total { - rx.recv().unwrap(); - } - - assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); - tx.send(()).unwrap(); - assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); -} - -#[test] -fn shared_chan_stress() { - let (tx, rx) = channel(); - let total = stress_factor() + 100; - for _ in 0..total { - let tx = tx.clone(); - thread::spawn(move || { - tx.send(()).unwrap(); - }); - } - - for _ in 0..total { - rx.recv().unwrap(); - } -} - -#[test] -fn test_nested_recv_iter() { - let (tx, rx) = channel::(); - let (total_tx, total_rx) = channel::(); - - let _t = thread::spawn(move || { - let mut acc = 0; - for x in rx.iter() { - acc += x; - } - total_tx.send(acc).unwrap(); - }); - - tx.send(3).unwrap(); - tx.send(1).unwrap(); - tx.send(2).unwrap(); - drop(tx); - assert_eq!(total_rx.recv().unwrap(), 6); -} - -#[test] -fn test_recv_iter_break() { - let (tx, rx) = channel::(); - let (count_tx, count_rx) = channel(); - - let _t = thread::spawn(move || { - let mut count = 0; - for x in rx.iter() { - if count >= 3 { - break; - } else { - count += x; - } - } - count_tx.send(count).unwrap(); - }); - - tx.send(2).unwrap(); - tx.send(2).unwrap(); - tx.send(2).unwrap(); - let _ = tx.send(2); - drop(tx); - assert_eq!(count_rx.recv().unwrap(), 4); -} - -#[test] -fn test_recv_try_iter() { - let (request_tx, request_rx) = channel(); - let (response_tx, response_rx) = channel(); - - // Request `x`s until we have `6`. - let t = thread::spawn(move || { - let mut count = 0; - loop { - for x in response_rx.try_iter() { - count += x; - if count == 6 { - return count; - } - } - request_tx.send(()).unwrap(); - } - }); - - for _ in request_rx.iter() { - if response_tx.send(2).is_err() { - break; - } - } - - assert_eq!(t.join().unwrap(), 6); -} - -#[test] -fn test_recv_into_iter_owned() { - let mut iter = { - let (tx, rx) = channel::(); - tx.send(1).unwrap(); - tx.send(2).unwrap(); - - rx.into_iter() - }; - assert_eq!(iter.next().unwrap(), 1); - assert_eq!(iter.next().unwrap(), 2); - assert_eq!(iter.next().is_none(), true); -} - -#[test] -fn test_recv_into_iter_borrowed() { - let (tx, rx) = channel::(); - tx.send(1).unwrap(); - tx.send(2).unwrap(); - drop(tx); - let mut iter = (&rx).into_iter(); - assert_eq!(iter.next().unwrap(), 1); - assert_eq!(iter.next().unwrap(), 2); - assert_eq!(iter.next().is_none(), true); -} - -#[test] -fn try_recv_states() { - let (tx1, rx1) = channel::(); - let (tx2, rx2) = channel::<()>(); - let (tx3, rx3) = channel::<()>(); - let _t = thread::spawn(move || { - rx2.recv().unwrap(); - tx1.send(1).unwrap(); - tx3.send(()).unwrap(); - rx2.recv().unwrap(); - drop(tx1); - tx3.send(()).unwrap(); - }); - - assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); - tx2.send(()).unwrap(); - rx3.recv().unwrap(); - assert_eq!(rx1.try_recv(), Ok(1)); - assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); - tx2.send(()).unwrap(); - rx3.recv().unwrap(); - assert_eq!(rx1.try_recv(), Err(TryRecvError::Disconnected)); -} - -// This bug used to end up in a livelock inside of the Receiver destructor -// because the internal state of the Shared packet was corrupted -#[test] -fn destroy_upgraded_shared_port_when_sender_still_active() { - let (tx, rx) = channel(); - let (tx2, rx2) = channel(); - let _t = thread::spawn(move || { - rx.recv().unwrap(); // wait on a oneshot - drop(rx); // destroy a shared - tx2.send(()).unwrap(); - }); - // make sure the other thread has gone to sleep - for _ in 0..5000 { - thread::yield_now(); - } - - // upgrade to a shared chan and send a message - let t = tx.clone(); - drop(tx); - t.send(()).unwrap(); - - // wait for the child thread to exit before we exit - rx2.recv().unwrap(); -} - -#[test] -fn issue_32114() { - let (tx, _) = channel(); - let _ = tx.send(123); - assert_eq!(tx.send(123), Err(SendError(123))); -} - -#[test] -fn issue_39364() { - let (tx, rx) = channel::<()>(); - let t = thread::spawn(move || { - thread::sleep(Duration::from_millis(300)); - let _ = tx.clone(); - // Don't drop; hand back to caller. - tx - }); - - let _ = rx.recv_timeout(Duration::from_millis(500)); - let _tx = t.join().unwrap(); // delay dropping until end of test - let _ = rx.recv_timeout(Duration::from_millis(500)); -} diff --git a/library/std/src/sync/mpsc.rs b/library/std/src/sync/mpsc.rs new file mode 100644 index 0000000000000..f942937c14d11 --- /dev/null +++ b/library/std/src/sync/mpsc.rs @@ -0,0 +1,1242 @@ +//! Multi-producer, single-consumer FIFO queue communication primitives. +//! +//! This module provides message-based communication over channels, concretely +//! defined among three types: +//! +//! * [`Sender`] +//! * [`SyncSender`] +//! * [`Receiver`] +//! +//! A [`Sender`] or [`SyncSender`] is used to send data to a [`Receiver`]. Both +//! senders are clone-able (multi-producer) such that many threads can send +//! simultaneously to one receiver (single-consumer). +//! +//! These channels come in two flavors: +//! +//! 1. An asynchronous, infinitely buffered channel. The [`channel`] function +//! will return a `(Sender, Receiver)` tuple where all sends will be +//! **asynchronous** (they never block). The channel conceptually has an +//! infinite buffer. +//! +//! 2. A synchronous, bounded channel. The [`sync_channel`] function will +//! return a `(SyncSender, Receiver)` tuple where the storage for pending +//! messages is a pre-allocated buffer of a fixed size. All sends will be +//! **synchronous** by blocking until there is buffer space available. Note +//! that a bound of 0 is allowed, causing the channel to become a "rendezvous" +//! channel where each sender atomically hands off a message to a receiver. +//! +//! [`send`]: Sender::send +//! +//! ## Disconnection +//! +//! The send and receive operations on channels will all return a [`Result`] +//! indicating whether the operation succeeded or not. An unsuccessful operation +//! is normally indicative of the other half of a channel having "hung up" by +//! being dropped in its corresponding thread. +//! +//! Once half of a channel has been deallocated, most operations can no longer +//! continue to make progress, so [`Err`] will be returned. Many applications +//! will continue to [`unwrap`] the results returned from this module, +//! instigating a propagation of failure among threads if one unexpectedly dies. +//! +//! [`unwrap`]: Result::unwrap +//! +//! # Examples +//! +//! Simple usage: +//! +//! ``` +//! use std::thread; +//! use std::sync::mpsc::channel; +//! +//! // Create a simple streaming channel +//! let (tx, rx) = channel(); +//! thread::spawn(move || { +//! tx.send(10).unwrap(); +//! }); +//! assert_eq!(rx.recv().unwrap(), 10); +//! ``` +//! +//! Shared usage: +//! +//! ``` +//! use std::thread; +//! use std::sync::mpsc::channel; +//! +//! // Create a shared channel that can be sent along from many threads +//! // where tx is the sending half (tx for transmission), and rx is the receiving +//! // half (rx for receiving). +//! let (tx, rx) = channel(); +//! for i in 0..10 { +//! let tx = tx.clone(); +//! thread::spawn(move || { +//! tx.send(i).unwrap(); +//! }); +//! } +//! +//! for _ in 0..10 { +//! let j = rx.recv().unwrap(); +//! assert!(0 <= j && j < 10); +//! } +//! ``` +//! +//! Propagating panics: +//! +//! ``` +//! use std::sync::mpsc::channel; +//! +//! // The call to recv() will return an error because the channel has already +//! // hung up (or been deallocated) +//! let (tx, rx) = channel::(); +//! drop(tx); +//! assert!(rx.recv().is_err()); +//! ``` +//! +//! Synchronous channels: +//! +//! ``` +//! use std::thread; +//! use std::sync::mpsc::sync_channel; +//! +//! let (tx, rx) = sync_channel::(0); +//! thread::spawn(move || { +//! // This will wait for the parent thread to start receiving +//! tx.send(53).unwrap(); +//! }); +//! rx.recv().unwrap(); +//! ``` +//! +//! Unbounded receive loop: +//! +//! ``` +//! use std::sync::mpsc::sync_channel; +//! use std::thread; +//! +//! let (tx, rx) = sync_channel(3); +//! +//! for _ in 0..3 { +//! // It would be the same without thread and clone here +//! // since there will still be one `tx` left. +//! let tx = tx.clone(); +//! // cloned tx dropped within thread +//! thread::spawn(move || tx.send("ok").unwrap()); +//! } +//! +//! // Drop the last sender to stop `rx` waiting for message. +//! // The program will not complete if we comment this out. +//! // **All** `tx` needs to be dropped for `rx` to have `Err`. +//! drop(tx); +//! +//! // Unbounded receiver waiting for all senders to complete. +//! while let Ok(msg) = rx.recv() { +//! println!("{msg}"); +//! } +//! +//! println!("completed"); +//! ``` + +#![stable(feature = "rust1", since = "1.0.0")] + +// MPSC channels are built as a wrapper around MPMC channels, which +// were ported from the `crossbeam-channel` crate. MPMC channels are +// not exposed publicly, but if you are curious about the implementation, +// that's where everything is. + +use crate::sync::mpmc; +use crate::time::{Duration, Instant}; +use crate::{error, fmt}; + +/// The receiving half of Rust's [`channel`] (or [`sync_channel`]) type. +/// This half can only be owned by one thread. +/// +/// Messages sent to the channel can be retrieved using [`recv`]. +/// +/// [`recv`]: Receiver::recv +/// +/// # Examples +/// +/// ```rust +/// use std::sync::mpsc::channel; +/// use std::thread; +/// use std::time::Duration; +/// +/// let (send, recv) = channel(); +/// +/// thread::spawn(move || { +/// send.send("Hello world!").unwrap(); +/// thread::sleep(Duration::from_secs(2)); // block for two seconds +/// send.send("Delayed for 2 seconds").unwrap(); +/// }); +/// +/// println!("{}", recv.recv().unwrap()); // Received immediately +/// println!("Waiting..."); +/// println!("{}", recv.recv().unwrap()); // Received after 2 seconds +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "Receiver")] +pub struct Receiver { + inner: mpmc::Receiver, +} + +// The receiver port can be sent from place to place, so long as it +// is not used to receive non-sendable things. +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for Receiver {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl !Sync for Receiver {} + +/// An iterator over messages on a [`Receiver`], created by [`iter`]. +/// +/// This iterator will block whenever [`next`] is called, +/// waiting for a new message, and [`None`] will be returned +/// when the corresponding channel has hung up. +/// +/// [`iter`]: Receiver::iter +/// [`next`]: Iterator::next +/// +/// # Examples +/// +/// ```rust +/// use std::sync::mpsc::channel; +/// use std::thread; +/// +/// let (send, recv) = channel(); +/// +/// thread::spawn(move || { +/// send.send(1u8).unwrap(); +/// send.send(2u8).unwrap(); +/// send.send(3u8).unwrap(); +/// }); +/// +/// for x in recv.iter() { +/// println!("Got: {x}"); +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +pub struct Iter<'a, T: 'a> { + rx: &'a Receiver, +} + +/// An iterator that attempts to yield all pending values for a [`Receiver`], +/// created by [`try_iter`]. +/// +/// [`None`] will be returned when there are no pending values remaining or +/// if the corresponding channel has hung up. +/// +/// This iterator will never block the caller in order to wait for data to +/// become available. Instead, it will return [`None`]. +/// +/// [`try_iter`]: Receiver::try_iter +/// +/// # Examples +/// +/// ```rust +/// use std::sync::mpsc::channel; +/// use std::thread; +/// use std::time::Duration; +/// +/// let (sender, receiver) = channel(); +/// +/// // Nothing is in the buffer yet +/// assert!(receiver.try_iter().next().is_none()); +/// println!("Nothing in the buffer..."); +/// +/// thread::spawn(move || { +/// sender.send(1).unwrap(); +/// sender.send(2).unwrap(); +/// sender.send(3).unwrap(); +/// }); +/// +/// println!("Going to sleep..."); +/// thread::sleep(Duration::from_secs(2)); // block for two seconds +/// +/// for x in receiver.try_iter() { +/// println!("Got: {x}"); +/// } +/// ``` +#[stable(feature = "receiver_try_iter", since = "1.15.0")] +#[derive(Debug)] +pub struct TryIter<'a, T: 'a> { + rx: &'a Receiver, +} + +/// An owning iterator over messages on a [`Receiver`], +/// created by [`into_iter`]. +/// +/// This iterator will block whenever [`next`] +/// is called, waiting for a new message, and [`None`] will be +/// returned if the corresponding channel has hung up. +/// +/// [`into_iter`]: Receiver::into_iter +/// [`next`]: Iterator::next +/// +/// # Examples +/// +/// ```rust +/// use std::sync::mpsc::channel; +/// use std::thread; +/// +/// let (send, recv) = channel(); +/// +/// thread::spawn(move || { +/// send.send(1u8).unwrap(); +/// send.send(2u8).unwrap(); +/// send.send(3u8).unwrap(); +/// }); +/// +/// for x in recv.into_iter() { +/// println!("Got: {x}"); +/// } +/// ``` +#[stable(feature = "receiver_into_iter", since = "1.1.0")] +#[derive(Debug)] +pub struct IntoIter { + rx: Receiver, +} + +/// The sending-half of Rust's asynchronous [`channel`] type. +/// +/// Messages can be sent through this channel with [`send`]. +/// +/// Note: all senders (the original and its clones) need to be dropped for the receiver +/// to stop blocking to receive messages with [`Receiver::recv`]. +/// +/// [`send`]: Sender::send +/// +/// # Examples +/// +/// ```rust +/// use std::sync::mpsc::channel; +/// use std::thread; +/// +/// let (sender, receiver) = channel(); +/// let sender2 = sender.clone(); +/// +/// // First thread owns sender +/// thread::spawn(move || { +/// sender.send(1).unwrap(); +/// }); +/// +/// // Second thread owns sender2 +/// thread::spawn(move || { +/// sender2.send(2).unwrap(); +/// }); +/// +/// let msg = receiver.recv().unwrap(); +/// let msg2 = receiver.recv().unwrap(); +/// +/// assert_eq!(3, msg + msg2); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Sender { + inner: mpmc::Sender, +} + +// The send port can be sent from place to place, so long as it +// is not used to send non-sendable things. +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for Sender {} + +#[stable(feature = "mpsc_sender_sync", since = "1.72.0")] +unsafe impl Sync for Sender {} + +/// The sending-half of Rust's synchronous [`sync_channel`] type. +/// +/// Messages can be sent through this channel with [`send`] or [`try_send`]. +/// +/// [`send`] will block if there is no space in the internal buffer. +/// +/// [`send`]: SyncSender::send +/// [`try_send`]: SyncSender::try_send +/// +/// # Examples +/// +/// ```rust +/// use std::sync::mpsc::sync_channel; +/// use std::thread; +/// +/// // Create a sync_channel with buffer size 2 +/// let (sync_sender, receiver) = sync_channel(2); +/// let sync_sender2 = sync_sender.clone(); +/// +/// // First thread owns sync_sender +/// thread::spawn(move || { +/// sync_sender.send(1).unwrap(); +/// sync_sender.send(2).unwrap(); +/// }); +/// +/// // Second thread owns sync_sender2 +/// thread::spawn(move || { +/// sync_sender2.send(3).unwrap(); +/// // thread will now block since the buffer is full +/// println!("Thread unblocked!"); +/// }); +/// +/// let mut msg; +/// +/// msg = receiver.recv().unwrap(); +/// println!("message {msg} received"); +/// +/// // "Thread unblocked!" will be printed now +/// +/// msg = receiver.recv().unwrap(); +/// println!("message {msg} received"); +/// +/// msg = receiver.recv().unwrap(); +/// +/// println!("message {msg} received"); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub struct SyncSender { + inner: mpmc::Sender, +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for SyncSender {} + +/// An error returned from the [`Sender::send`] or [`SyncSender::send`] +/// function on **channel**s. +/// +/// A **send** operation can only fail if the receiving end of a channel is +/// disconnected, implying that the data could never be received. The error +/// contains the data being sent as a payload so it can be recovered. +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(PartialEq, Eq, Clone, Copy)] +pub struct SendError(#[stable(feature = "rust1", since = "1.0.0")] pub T); + +/// An error returned from the [`recv`] function on a [`Receiver`]. +/// +/// The [`recv`] operation can only fail if the sending half of a +/// [`channel`] (or [`sync_channel`]) is disconnected, implying that no further +/// messages will ever be received. +/// +/// [`recv`]: Receiver::recv +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct RecvError; + +/// This enumeration is the list of the possible reasons that [`try_recv`] could +/// not return data when called. This can occur with both a [`channel`] and +/// a [`sync_channel`]. +/// +/// [`try_recv`]: Receiver::try_recv +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub enum TryRecvError { + /// This **channel** is currently empty, but the **Sender**(s) have not yet + /// disconnected, so data may yet become available. + #[stable(feature = "rust1", since = "1.0.0")] + Empty, + + /// The **channel**'s sending half has become disconnected, and there will + /// never be any more data received on it. + #[stable(feature = "rust1", since = "1.0.0")] + Disconnected, +} + +/// This enumeration is the list of possible errors that made [`recv_timeout`] +/// unable to return data when called. This can occur with both a [`channel`] and +/// a [`sync_channel`]. +/// +/// [`recv_timeout`]: Receiver::recv_timeout +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[stable(feature = "mpsc_recv_timeout", since = "1.12.0")] +pub enum RecvTimeoutError { + /// This **channel** is currently empty, but the **Sender**(s) have not yet + /// disconnected, so data may yet become available. + #[stable(feature = "mpsc_recv_timeout", since = "1.12.0")] + Timeout, + /// The **channel**'s sending half has become disconnected, and there will + /// never be any more data received on it. + #[stable(feature = "mpsc_recv_timeout", since = "1.12.0")] + Disconnected, +} + +/// This enumeration is the list of the possible error outcomes for the +/// [`try_send`] method. +/// +/// [`try_send`]: SyncSender::try_send +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum TrySendError { + /// The data could not be sent on the [`sync_channel`] because it would require that + /// the callee block to send the data. + /// + /// If this is a buffered channel, then the buffer is full at this time. If + /// this is not a buffered channel, then there is no [`Receiver`] available to + /// acquire the data. + #[stable(feature = "rust1", since = "1.0.0")] + Full(#[stable(feature = "rust1", since = "1.0.0")] T), + + /// This [`sync_channel`]'s receiving half has disconnected, so the data could not be + /// sent. The data is returned back to the callee in this case. + #[stable(feature = "rust1", since = "1.0.0")] + Disconnected(#[stable(feature = "rust1", since = "1.0.0")] T), +} + +/// Creates a new asynchronous channel, returning the sender/receiver halves. +/// +/// All data sent on the [`Sender`] will become available on the [`Receiver`] in +/// the same order as it was sent, and no [`send`] will block the calling thread +/// (this channel has an "infinite buffer", unlike [`sync_channel`], which will +/// block after its buffer limit is reached). [`recv`] will block until a message +/// is available while there is at least one [`Sender`] alive (including clones). +/// +/// The [`Sender`] can be cloned to [`send`] to the same channel multiple times, but +/// only one [`Receiver`] is supported. +/// +/// If the [`Receiver`] is disconnected while trying to [`send`] with the +/// [`Sender`], the [`send`] method will return a [`SendError`]. Similarly, if the +/// [`Sender`] is disconnected while trying to [`recv`], the [`recv`] method will +/// return a [`RecvError`]. +/// +/// [`send`]: Sender::send +/// [`recv`]: Receiver::recv +/// +/// # Examples +/// +/// ``` +/// use std::sync::mpsc::channel; +/// use std::thread; +/// +/// let (sender, receiver) = channel(); +/// +/// // Spawn off an expensive computation +/// thread::spawn(move || { +/// # fn expensive_computation() {} +/// sender.send(expensive_computation()).unwrap(); +/// }); +/// +/// // Do some useful work for awhile +/// +/// // Let's see what that answer was +/// println!("{:?}", receiver.recv().unwrap()); +/// ``` +#[must_use] +#[stable(feature = "rust1", since = "1.0.0")] +pub fn channel() -> (Sender, Receiver) { + let (tx, rx) = mpmc::channel(); + (Sender { inner: tx }, Receiver { inner: rx }) +} + +/// Creates a new synchronous, bounded channel. +/// +/// All data sent on the [`SyncSender`] will become available on the [`Receiver`] +/// in the same order as it was sent. Like asynchronous [`channel`]s, the +/// [`Receiver`] will block until a message becomes available. `sync_channel` +/// differs greatly in the semantics of the sender, however. +/// +/// This channel has an internal buffer on which messages will be queued. +/// `bound` specifies the buffer size. When the internal buffer becomes full, +/// future sends will *block* waiting for the buffer to open up. Note that a +/// buffer size of 0 is valid, in which case this becomes "rendezvous channel" +/// where each [`send`] will not return until a [`recv`] is paired with it. +/// +/// The [`SyncSender`] can be cloned to [`send`] to the same channel multiple +/// times, but only one [`Receiver`] is supported. +/// +/// Like asynchronous channels, if the [`Receiver`] is disconnected while trying +/// to [`send`] with the [`SyncSender`], the [`send`] method will return a +/// [`SendError`]. Similarly, If the [`SyncSender`] is disconnected while trying +/// to [`recv`], the [`recv`] method will return a [`RecvError`]. +/// +/// [`send`]: SyncSender::send +/// [`recv`]: Receiver::recv +/// +/// # Examples +/// +/// ``` +/// use std::sync::mpsc::sync_channel; +/// use std::thread; +/// +/// let (sender, receiver) = sync_channel(1); +/// +/// // this returns immediately +/// sender.send(1).unwrap(); +/// +/// thread::spawn(move || { +/// // this will block until the previous message has been received +/// sender.send(2).unwrap(); +/// }); +/// +/// assert_eq!(receiver.recv().unwrap(), 1); +/// assert_eq!(receiver.recv().unwrap(), 2); +/// ``` +#[must_use] +#[stable(feature = "rust1", since = "1.0.0")] +pub fn sync_channel(bound: usize) -> (SyncSender, Receiver) { + let (tx, rx) = mpmc::sync_channel(bound); + (SyncSender { inner: tx }, Receiver { inner: rx }) +} + +//////////////////////////////////////////////////////////////////////////////// +// Sender +//////////////////////////////////////////////////////////////////////////////// + +impl Sender { + /// Attempts to send a value on this channel, returning it back if it could + /// not be sent. + /// + /// A successful send occurs when it is determined that the other end of + /// the channel has not hung up already. An unsuccessful send would be one + /// where the corresponding receiver has already been deallocated. Note + /// that a return value of [`Err`] means that the data will never be + /// received, but a return value of [`Ok`] does *not* mean that the data + /// will be received. It is possible for the corresponding receiver to + /// hang up immediately after this function returns [`Ok`]. + /// + /// This method will never block the current thread. + /// + /// # Examples + /// + /// ``` + /// use std::sync::mpsc::channel; + /// + /// let (tx, rx) = channel(); + /// + /// // This send is always successful + /// tx.send(1).unwrap(); + /// + /// // This send will fail because the receiver is gone + /// drop(rx); + /// assert_eq!(tx.send(1).unwrap_err().0, 1); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn send(&self, t: T) -> Result<(), SendError> { + self.inner.send(t) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Sender { + /// Clone a sender to send to other threads. + /// + /// Note, be aware of the lifetime of the sender because all senders + /// (including the original) need to be dropped in order for + /// [`Receiver::recv`] to stop blocking. + fn clone(&self) -> Sender { + Sender { inner: self.inner.clone() } + } +} + +#[stable(feature = "mpsc_debug", since = "1.8.0")] +impl fmt::Debug for Sender { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Sender").finish_non_exhaustive() + } +} + +//////////////////////////////////////////////////////////////////////////////// +// SyncSender +//////////////////////////////////////////////////////////////////////////////// + +impl SyncSender { + /// Sends a value on this synchronous channel. + /// + /// This function will *block* until space in the internal buffer becomes + /// available or a receiver is available to hand off the message to. + /// + /// Note that a successful send does *not* guarantee that the receiver will + /// ever see the data if there is a buffer on this channel. Items may be + /// enqueued in the internal buffer for the receiver to receive at a later + /// time. If the buffer size is 0, however, the channel becomes a rendezvous + /// channel and it guarantees that the receiver has indeed received + /// the data if this function returns success. + /// + /// This function will never panic, but it may return [`Err`] if the + /// [`Receiver`] has disconnected and is no longer able to receive + /// information. + /// + /// # Examples + /// + /// ```rust + /// use std::sync::mpsc::sync_channel; + /// use std::thread; + /// + /// // Create a rendezvous sync_channel with buffer size 0 + /// let (sync_sender, receiver) = sync_channel(0); + /// + /// thread::spawn(move || { + /// println!("sending message..."); + /// sync_sender.send(1).unwrap(); + /// // Thread is now blocked until the message is received + /// + /// println!("...message received!"); + /// }); + /// + /// let msg = receiver.recv().unwrap(); + /// assert_eq!(1, msg); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn send(&self, t: T) -> Result<(), SendError> { + self.inner.send(t) + } + + /// Attempts to send a value on this channel without blocking. + /// + /// This method differs from [`send`] by returning immediately if the + /// channel's buffer is full or no receiver is waiting to acquire some + /// data. Compared with [`send`], this function has two failure cases + /// instead of one (one for disconnection, one for a full buffer). + /// + /// See [`send`] for notes about guarantees of whether the + /// receiver has received the data or not if this function is successful. + /// + /// [`send`]: Self::send + /// + /// # Examples + /// + /// ```rust + /// use std::sync::mpsc::sync_channel; + /// use std::thread; + /// + /// // Create a sync_channel with buffer size 1 + /// let (sync_sender, receiver) = sync_channel(1); + /// let sync_sender2 = sync_sender.clone(); + /// + /// // First thread owns sync_sender + /// thread::spawn(move || { + /// sync_sender.send(1).unwrap(); + /// sync_sender.send(2).unwrap(); + /// // Thread blocked + /// }); + /// + /// // Second thread owns sync_sender2 + /// thread::spawn(move || { + /// // This will return an error and send + /// // no message if the buffer is full + /// let _ = sync_sender2.try_send(3); + /// }); + /// + /// let mut msg; + /// msg = receiver.recv().unwrap(); + /// println!("message {msg} received"); + /// + /// msg = receiver.recv().unwrap(); + /// println!("message {msg} received"); + /// + /// // Third message may have never been sent + /// match receiver.try_recv() { + /// Ok(msg) => println!("message {msg} received"), + /// Err(_) => println!("the third message was never sent"), + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn try_send(&self, t: T) -> Result<(), TrySendError> { + self.inner.try_send(t) + } + + // Attempts to send for a value on this receiver, returning an error if the + // corresponding channel has hung up, or if it waits more than `timeout`. + // + // This method is currently only used for tests. + #[unstable(issue = "none", feature = "std_internals")] + #[doc(hidden)] + pub fn send_timeout(&self, t: T, timeout: Duration) -> Result<(), mpmc::SendTimeoutError> { + self.inner.send_timeout(t, timeout) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for SyncSender { + fn clone(&self) -> SyncSender { + SyncSender { inner: self.inner.clone() } + } +} + +#[stable(feature = "mpsc_debug", since = "1.8.0")] +impl fmt::Debug for SyncSender { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SyncSender").finish_non_exhaustive() + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Receiver +//////////////////////////////////////////////////////////////////////////////// + +impl Receiver { + /// Attempts to return a pending value on this receiver without blocking. + /// + /// This method will never block the caller in order to wait for data to + /// become available. Instead, this will always return immediately with a + /// possible option of pending data on the channel. + /// + /// This is useful for a flavor of "optimistic check" before deciding to + /// block on a receiver. + /// + /// Compared with [`recv`], this function has two failure cases instead of one + /// (one for disconnection, one for an empty buffer). + /// + /// [`recv`]: Self::recv + /// + /// # Examples + /// + /// ```rust + /// use std::sync::mpsc::{Receiver, channel}; + /// + /// let (_, receiver): (_, Receiver) = channel(); + /// + /// assert!(receiver.try_recv().is_err()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn try_recv(&self) -> Result { + self.inner.try_recv() + } + + /// Attempts to wait for a value on this receiver, returning an error if the + /// corresponding channel has hung up. + /// + /// This function will always block the current thread if there is no data + /// available and it's possible for more data to be sent (at least one sender + /// still exists). Once a message is sent to the corresponding [`Sender`] + /// (or [`SyncSender`]), this receiver will wake up and return that + /// message. + /// + /// If the corresponding [`Sender`] has disconnected, or it disconnects while + /// this call is blocking, this call will wake up and return [`Err`] to + /// indicate that no more messages can ever be received on this channel. + /// However, since channels are buffered, messages sent before the disconnect + /// will still be properly received. + /// + /// # Examples + /// + /// ``` + /// use std::sync::mpsc; + /// use std::thread; + /// + /// let (send, recv) = mpsc::channel(); + /// let handle = thread::spawn(move || { + /// send.send(1u8).unwrap(); + /// }); + /// + /// handle.join().unwrap(); + /// + /// assert_eq!(Ok(1), recv.recv()); + /// ``` + /// + /// Buffering behavior: + /// + /// ``` + /// use std::sync::mpsc; + /// use std::thread; + /// use std::sync::mpsc::RecvError; + /// + /// let (send, recv) = mpsc::channel(); + /// let handle = thread::spawn(move || { + /// send.send(1u8).unwrap(); + /// send.send(2).unwrap(); + /// send.send(3).unwrap(); + /// drop(send); + /// }); + /// + /// // wait for the thread to join so we ensure the sender is dropped + /// handle.join().unwrap(); + /// + /// assert_eq!(Ok(1), recv.recv()); + /// assert_eq!(Ok(2), recv.recv()); + /// assert_eq!(Ok(3), recv.recv()); + /// assert_eq!(Err(RecvError), recv.recv()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn recv(&self) -> Result { + self.inner.recv() + } + + /// Attempts to wait for a value on this receiver, returning an error if the + /// corresponding channel has hung up, or if it waits more than `timeout`. + /// + /// This function will always block the current thread if there is no data + /// available and it's possible for more data to be sent (at least one sender + /// still exists). Once a message is sent to the corresponding [`Sender`] + /// (or [`SyncSender`]), this receiver will wake up and return that + /// message. + /// + /// If the corresponding [`Sender`] has disconnected, or it disconnects while + /// this call is blocking, this call will wake up and return [`Err`] to + /// indicate that no more messages can ever be received on this channel. + /// However, since channels are buffered, messages sent before the disconnect + /// will still be properly received. + /// + /// # Examples + /// + /// Successfully receiving value before encountering timeout: + /// + /// ```no_run + /// use std::thread; + /// use std::time::Duration; + /// use std::sync::mpsc; + /// + /// let (send, recv) = mpsc::channel(); + /// + /// thread::spawn(move || { + /// send.send('a').unwrap(); + /// }); + /// + /// assert_eq!( + /// recv.recv_timeout(Duration::from_millis(400)), + /// Ok('a') + /// ); + /// ``` + /// + /// Receiving an error upon reaching timeout: + /// + /// ```no_run + /// use std::thread; + /// use std::time::Duration; + /// use std::sync::mpsc; + /// + /// let (send, recv) = mpsc::channel(); + /// + /// thread::spawn(move || { + /// thread::sleep(Duration::from_millis(800)); + /// send.send('a').unwrap(); + /// }); + /// + /// assert_eq!( + /// recv.recv_timeout(Duration::from_millis(400)), + /// Err(mpsc::RecvTimeoutError::Timeout) + /// ); + /// ``` + #[stable(feature = "mpsc_recv_timeout", since = "1.12.0")] + pub fn recv_timeout(&self, timeout: Duration) -> Result { + self.inner.recv_timeout(timeout) + } + + /// Attempts to wait for a value on this receiver, returning an error if the + /// corresponding channel has hung up, or if `deadline` is reached. + /// + /// This function will always block the current thread if there is no data + /// available and it's possible for more data to be sent. Once a message is + /// sent to the corresponding [`Sender`] (or [`SyncSender`]), then this + /// receiver will wake up and return that message. + /// + /// If the corresponding [`Sender`] has disconnected, or it disconnects while + /// this call is blocking, this call will wake up and return [`Err`] to + /// indicate that no more messages can ever be received on this channel. + /// However, since channels are buffered, messages sent before the disconnect + /// will still be properly received. + /// + /// # Examples + /// + /// Successfully receiving value before reaching deadline: + /// + /// ```no_run + /// #![feature(deadline_api)] + /// use std::thread; + /// use std::time::{Duration, Instant}; + /// use std::sync::mpsc; + /// + /// let (send, recv) = mpsc::channel(); + /// + /// thread::spawn(move || { + /// send.send('a').unwrap(); + /// }); + /// + /// assert_eq!( + /// recv.recv_deadline(Instant::now() + Duration::from_millis(400)), + /// Ok('a') + /// ); + /// ``` + /// + /// Receiving an error upon reaching deadline: + /// + /// ```no_run + /// #![feature(deadline_api)] + /// use std::thread; + /// use std::time::{Duration, Instant}; + /// use std::sync::mpsc; + /// + /// let (send, recv) = mpsc::channel(); + /// + /// thread::spawn(move || { + /// thread::sleep(Duration::from_millis(800)); + /// send.send('a').unwrap(); + /// }); + /// + /// assert_eq!( + /// recv.recv_deadline(Instant::now() + Duration::from_millis(400)), + /// Err(mpsc::RecvTimeoutError::Timeout) + /// ); + /// ``` + #[unstable(feature = "deadline_api", issue = "46316")] + pub fn recv_deadline(&self, deadline: Instant) -> Result { + self.inner.recv_deadline(deadline) + } + + /// Returns an iterator that will block waiting for messages, but never + /// [`panic!`]. It will return [`None`] when the channel has hung up. + /// + /// # Examples + /// + /// ```rust + /// use std::sync::mpsc::channel; + /// use std::thread; + /// + /// let (send, recv) = channel(); + /// + /// thread::spawn(move || { + /// send.send(1).unwrap(); + /// send.send(2).unwrap(); + /// send.send(3).unwrap(); + /// }); + /// + /// let mut iter = recv.iter(); + /// assert_eq!(iter.next(), Some(1)); + /// assert_eq!(iter.next(), Some(2)); + /// assert_eq!(iter.next(), Some(3)); + /// assert_eq!(iter.next(), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter(&self) -> Iter<'_, T> { + Iter { rx: self } + } + + /// Returns an iterator that will attempt to yield all pending values. + /// It will return `None` if there are no more pending values or if the + /// channel has hung up. The iterator will never [`panic!`] or block the + /// user by waiting for values. + /// + /// # Examples + /// + /// ```no_run + /// use std::sync::mpsc::channel; + /// use std::thread; + /// use std::time::Duration; + /// + /// let (sender, receiver) = channel(); + /// + /// // nothing is in the buffer yet + /// assert!(receiver.try_iter().next().is_none()); + /// + /// thread::spawn(move || { + /// thread::sleep(Duration::from_secs(1)); + /// sender.send(1).unwrap(); + /// sender.send(2).unwrap(); + /// sender.send(3).unwrap(); + /// }); + /// + /// // nothing is in the buffer yet + /// assert!(receiver.try_iter().next().is_none()); + /// + /// // block for two seconds + /// thread::sleep(Duration::from_secs(2)); + /// + /// let mut iter = receiver.try_iter(); + /// assert_eq!(iter.next(), Some(1)); + /// assert_eq!(iter.next(), Some(2)); + /// assert_eq!(iter.next(), Some(3)); + /// assert_eq!(iter.next(), None); + /// ``` + #[stable(feature = "receiver_try_iter", since = "1.15.0")] + pub fn try_iter(&self) -> TryIter<'_, T> { + TryIter { rx: self } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for Iter<'a, T> { + type Item = T; + + fn next(&mut self) -> Option { + self.rx.recv().ok() + } +} + +#[stable(feature = "receiver_try_iter", since = "1.15.0")] +impl<'a, T> Iterator for TryIter<'a, T> { + type Item = T; + + fn next(&mut self) -> Option { + self.rx.try_recv().ok() + } +} + +#[stable(feature = "receiver_into_iter", since = "1.1.0")] +impl<'a, T> IntoIterator for &'a Receiver { + type Item = T; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +#[stable(feature = "receiver_into_iter", since = "1.1.0")] +impl Iterator for IntoIter { + type Item = T; + fn next(&mut self) -> Option { + self.rx.recv().ok() + } +} + +#[stable(feature = "receiver_into_iter", since = "1.1.0")] +impl IntoIterator for Receiver { + type Item = T; + type IntoIter = IntoIter; + + fn into_iter(self) -> IntoIter { + IntoIter { rx: self } + } +} + +#[stable(feature = "mpsc_debug", since = "1.8.0")] +impl fmt::Debug for Receiver { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Receiver").finish_non_exhaustive() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for SendError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SendError").finish_non_exhaustive() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for SendError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "sending on a closed channel".fmt(f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl error::Error for SendError { + #[allow(deprecated)] + fn description(&self) -> &str { + "sending on a closed channel" + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for TrySendError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + TrySendError::Full(..) => "Full(..)".fmt(f), + TrySendError::Disconnected(..) => "Disconnected(..)".fmt(f), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for TrySendError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + TrySendError::Full(..) => "sending on a full channel".fmt(f), + TrySendError::Disconnected(..) => "sending on a closed channel".fmt(f), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl error::Error for TrySendError { + #[allow(deprecated)] + fn description(&self) -> &str { + match *self { + TrySendError::Full(..) => "sending on a full channel", + TrySendError::Disconnected(..) => "sending on a closed channel", + } + } +} + +#[stable(feature = "mpsc_error_conversions", since = "1.24.0")] +impl From> for TrySendError { + /// Converts a `SendError` into a `TrySendError`. + /// + /// This conversion always returns a `TrySendError::Disconnected` containing the data in the `SendError`. + /// + /// No data is allocated on the heap. + fn from(err: SendError) -> TrySendError { + match err { + SendError(t) => TrySendError::Disconnected(t), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for RecvError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "receiving on a closed channel".fmt(f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl error::Error for RecvError { + #[allow(deprecated)] + fn description(&self) -> &str { + "receiving on a closed channel" + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for TryRecvError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + TryRecvError::Empty => "receiving on an empty channel".fmt(f), + TryRecvError::Disconnected => "receiving on a closed channel".fmt(f), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl error::Error for TryRecvError { + #[allow(deprecated)] + fn description(&self) -> &str { + match *self { + TryRecvError::Empty => "receiving on an empty channel", + TryRecvError::Disconnected => "receiving on a closed channel", + } + } +} + +#[stable(feature = "mpsc_error_conversions", since = "1.24.0")] +impl From for TryRecvError { + /// Converts a `RecvError` into a `TryRecvError`. + /// + /// This conversion always returns `TryRecvError::Disconnected`. + /// + /// No data is allocated on the heap. + fn from(err: RecvError) -> TryRecvError { + match err { + RecvError => TryRecvError::Disconnected, + } + } +} + +#[stable(feature = "mpsc_recv_timeout_error", since = "1.15.0")] +impl fmt::Display for RecvTimeoutError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + RecvTimeoutError::Timeout => "timed out waiting on channel".fmt(f), + RecvTimeoutError::Disconnected => "channel is empty and sending half is closed".fmt(f), + } + } +} + +#[stable(feature = "mpsc_recv_timeout_error", since = "1.15.0")] +impl error::Error for RecvTimeoutError { + #[allow(deprecated)] + fn description(&self) -> &str { + match *self { + RecvTimeoutError::Timeout => "timed out waiting on channel", + RecvTimeoutError::Disconnected => "channel is empty and sending half is closed", + } + } +} + +#[stable(feature = "mpsc_error_conversions", since = "1.24.0")] +impl From for RecvTimeoutError { + /// Converts a `RecvError` into a `RecvTimeoutError`. + /// + /// This conversion always returns `RecvTimeoutError::Disconnected`. + /// + /// No data is allocated on the heap. + fn from(err: RecvError) -> RecvTimeoutError { + match err { + RecvError => RecvTimeoutError::Disconnected, + } + } +} diff --git a/library/std/src/sync/mpsc/mod.rs b/library/std/src/sync/mpsc/mod.rs deleted file mode 100644 index c86b546e01169..0000000000000 --- a/library/std/src/sync/mpsc/mod.rs +++ /dev/null @@ -1,1247 +0,0 @@ -//! Multi-producer, single-consumer FIFO queue communication primitives. -//! -//! This module provides message-based communication over channels, concretely -//! defined among three types: -//! -//! * [`Sender`] -//! * [`SyncSender`] -//! * [`Receiver`] -//! -//! A [`Sender`] or [`SyncSender`] is used to send data to a [`Receiver`]. Both -//! senders are clone-able (multi-producer) such that many threads can send -//! simultaneously to one receiver (single-consumer). -//! -//! These channels come in two flavors: -//! -//! 1. An asynchronous, infinitely buffered channel. The [`channel`] function -//! will return a `(Sender, Receiver)` tuple where all sends will be -//! **asynchronous** (they never block). The channel conceptually has an -//! infinite buffer. -//! -//! 2. A synchronous, bounded channel. The [`sync_channel`] function will -//! return a `(SyncSender, Receiver)` tuple where the storage for pending -//! messages is a pre-allocated buffer of a fixed size. All sends will be -//! **synchronous** by blocking until there is buffer space available. Note -//! that a bound of 0 is allowed, causing the channel to become a "rendezvous" -//! channel where each sender atomically hands off a message to a receiver. -//! -//! [`send`]: Sender::send -//! -//! ## Disconnection -//! -//! The send and receive operations on channels will all return a [`Result`] -//! indicating whether the operation succeeded or not. An unsuccessful operation -//! is normally indicative of the other half of a channel having "hung up" by -//! being dropped in its corresponding thread. -//! -//! Once half of a channel has been deallocated, most operations can no longer -//! continue to make progress, so [`Err`] will be returned. Many applications -//! will continue to [`unwrap`] the results returned from this module, -//! instigating a propagation of failure among threads if one unexpectedly dies. -//! -//! [`unwrap`]: Result::unwrap -//! -//! # Examples -//! -//! Simple usage: -//! -//! ``` -//! use std::thread; -//! use std::sync::mpsc::channel; -//! -//! // Create a simple streaming channel -//! let (tx, rx) = channel(); -//! thread::spawn(move || { -//! tx.send(10).unwrap(); -//! }); -//! assert_eq!(rx.recv().unwrap(), 10); -//! ``` -//! -//! Shared usage: -//! -//! ``` -//! use std::thread; -//! use std::sync::mpsc::channel; -//! -//! // Create a shared channel that can be sent along from many threads -//! // where tx is the sending half (tx for transmission), and rx is the receiving -//! // half (rx for receiving). -//! let (tx, rx) = channel(); -//! for i in 0..10 { -//! let tx = tx.clone(); -//! thread::spawn(move || { -//! tx.send(i).unwrap(); -//! }); -//! } -//! -//! for _ in 0..10 { -//! let j = rx.recv().unwrap(); -//! assert!(0 <= j && j < 10); -//! } -//! ``` -//! -//! Propagating panics: -//! -//! ``` -//! use std::sync::mpsc::channel; -//! -//! // The call to recv() will return an error because the channel has already -//! // hung up (or been deallocated) -//! let (tx, rx) = channel::(); -//! drop(tx); -//! assert!(rx.recv().is_err()); -//! ``` -//! -//! Synchronous channels: -//! -//! ``` -//! use std::thread; -//! use std::sync::mpsc::sync_channel; -//! -//! let (tx, rx) = sync_channel::(0); -//! thread::spawn(move || { -//! // This will wait for the parent thread to start receiving -//! tx.send(53).unwrap(); -//! }); -//! rx.recv().unwrap(); -//! ``` -//! -//! Unbounded receive loop: -//! -//! ``` -//! use std::sync::mpsc::sync_channel; -//! use std::thread; -//! -//! let (tx, rx) = sync_channel(3); -//! -//! for _ in 0..3 { -//! // It would be the same without thread and clone here -//! // since there will still be one `tx` left. -//! let tx = tx.clone(); -//! // cloned tx dropped within thread -//! thread::spawn(move || tx.send("ok").unwrap()); -//! } -//! -//! // Drop the last sender to stop `rx` waiting for message. -//! // The program will not complete if we comment this out. -//! // **All** `tx` needs to be dropped for `rx` to have `Err`. -//! drop(tx); -//! -//! // Unbounded receiver waiting for all senders to complete. -//! while let Ok(msg) = rx.recv() { -//! println!("{msg}"); -//! } -//! -//! println!("completed"); -//! ``` - -#![stable(feature = "rust1", since = "1.0.0")] - -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod sync_tests; - -// MPSC channels are built as a wrapper around MPMC channels, which -// were ported from the `crossbeam-channel` crate. MPMC channels are -// not exposed publicly, but if you are curious about the implementation, -// that's where everything is. - -use crate::sync::mpmc; -use crate::time::{Duration, Instant}; -use crate::{error, fmt}; - -/// The receiving half of Rust's [`channel`] (or [`sync_channel`]) type. -/// This half can only be owned by one thread. -/// -/// Messages sent to the channel can be retrieved using [`recv`]. -/// -/// [`recv`]: Receiver::recv -/// -/// # Examples -/// -/// ```rust -/// use std::sync::mpsc::channel; -/// use std::thread; -/// use std::time::Duration; -/// -/// let (send, recv) = channel(); -/// -/// thread::spawn(move || { -/// send.send("Hello world!").unwrap(); -/// thread::sleep(Duration::from_secs(2)); // block for two seconds -/// send.send("Delayed for 2 seconds").unwrap(); -/// }); -/// -/// println!("{}", recv.recv().unwrap()); // Received immediately -/// println!("Waiting..."); -/// println!("{}", recv.recv().unwrap()); // Received after 2 seconds -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg_attr(not(test), rustc_diagnostic_item = "Receiver")] -pub struct Receiver { - inner: mpmc::Receiver, -} - -// The receiver port can be sent from place to place, so long as it -// is not used to receive non-sendable things. -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for Receiver {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl !Sync for Receiver {} - -/// An iterator over messages on a [`Receiver`], created by [`iter`]. -/// -/// This iterator will block whenever [`next`] is called, -/// waiting for a new message, and [`None`] will be returned -/// when the corresponding channel has hung up. -/// -/// [`iter`]: Receiver::iter -/// [`next`]: Iterator::next -/// -/// # Examples -/// -/// ```rust -/// use std::sync::mpsc::channel; -/// use std::thread; -/// -/// let (send, recv) = channel(); -/// -/// thread::spawn(move || { -/// send.send(1u8).unwrap(); -/// send.send(2u8).unwrap(); -/// send.send(3u8).unwrap(); -/// }); -/// -/// for x in recv.iter() { -/// println!("Got: {x}"); -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct Iter<'a, T: 'a> { - rx: &'a Receiver, -} - -/// An iterator that attempts to yield all pending values for a [`Receiver`], -/// created by [`try_iter`]. -/// -/// [`None`] will be returned when there are no pending values remaining or -/// if the corresponding channel has hung up. -/// -/// This iterator will never block the caller in order to wait for data to -/// become available. Instead, it will return [`None`]. -/// -/// [`try_iter`]: Receiver::try_iter -/// -/// # Examples -/// -/// ```rust -/// use std::sync::mpsc::channel; -/// use std::thread; -/// use std::time::Duration; -/// -/// let (sender, receiver) = channel(); -/// -/// // Nothing is in the buffer yet -/// assert!(receiver.try_iter().next().is_none()); -/// println!("Nothing in the buffer..."); -/// -/// thread::spawn(move || { -/// sender.send(1).unwrap(); -/// sender.send(2).unwrap(); -/// sender.send(3).unwrap(); -/// }); -/// -/// println!("Going to sleep..."); -/// thread::sleep(Duration::from_secs(2)); // block for two seconds -/// -/// for x in receiver.try_iter() { -/// println!("Got: {x}"); -/// } -/// ``` -#[stable(feature = "receiver_try_iter", since = "1.15.0")] -#[derive(Debug)] -pub struct TryIter<'a, T: 'a> { - rx: &'a Receiver, -} - -/// An owning iterator over messages on a [`Receiver`], -/// created by [`into_iter`]. -/// -/// This iterator will block whenever [`next`] -/// is called, waiting for a new message, and [`None`] will be -/// returned if the corresponding channel has hung up. -/// -/// [`into_iter`]: Receiver::into_iter -/// [`next`]: Iterator::next -/// -/// # Examples -/// -/// ```rust -/// use std::sync::mpsc::channel; -/// use std::thread; -/// -/// let (send, recv) = channel(); -/// -/// thread::spawn(move || { -/// send.send(1u8).unwrap(); -/// send.send(2u8).unwrap(); -/// send.send(3u8).unwrap(); -/// }); -/// -/// for x in recv.into_iter() { -/// println!("Got: {x}"); -/// } -/// ``` -#[stable(feature = "receiver_into_iter", since = "1.1.0")] -#[derive(Debug)] -pub struct IntoIter { - rx: Receiver, -} - -/// The sending-half of Rust's asynchronous [`channel`] type. -/// -/// Messages can be sent through this channel with [`send`]. -/// -/// Note: all senders (the original and its clones) need to be dropped for the receiver -/// to stop blocking to receive messages with [`Receiver::recv`]. -/// -/// [`send`]: Sender::send -/// -/// # Examples -/// -/// ```rust -/// use std::sync::mpsc::channel; -/// use std::thread; -/// -/// let (sender, receiver) = channel(); -/// let sender2 = sender.clone(); -/// -/// // First thread owns sender -/// thread::spawn(move || { -/// sender.send(1).unwrap(); -/// }); -/// -/// // Second thread owns sender2 -/// thread::spawn(move || { -/// sender2.send(2).unwrap(); -/// }); -/// -/// let msg = receiver.recv().unwrap(); -/// let msg2 = receiver.recv().unwrap(); -/// -/// assert_eq!(3, msg + msg2); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Sender { - inner: mpmc::Sender, -} - -// The send port can be sent from place to place, so long as it -// is not used to send non-sendable things. -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for Sender {} - -#[stable(feature = "mpsc_sender_sync", since = "1.72.0")] -unsafe impl Sync for Sender {} - -/// The sending-half of Rust's synchronous [`sync_channel`] type. -/// -/// Messages can be sent through this channel with [`send`] or [`try_send`]. -/// -/// [`send`] will block if there is no space in the internal buffer. -/// -/// [`send`]: SyncSender::send -/// [`try_send`]: SyncSender::try_send -/// -/// # Examples -/// -/// ```rust -/// use std::sync::mpsc::sync_channel; -/// use std::thread; -/// -/// // Create a sync_channel with buffer size 2 -/// let (sync_sender, receiver) = sync_channel(2); -/// let sync_sender2 = sync_sender.clone(); -/// -/// // First thread owns sync_sender -/// thread::spawn(move || { -/// sync_sender.send(1).unwrap(); -/// sync_sender.send(2).unwrap(); -/// }); -/// -/// // Second thread owns sync_sender2 -/// thread::spawn(move || { -/// sync_sender2.send(3).unwrap(); -/// // thread will now block since the buffer is full -/// println!("Thread unblocked!"); -/// }); -/// -/// let mut msg; -/// -/// msg = receiver.recv().unwrap(); -/// println!("message {msg} received"); -/// -/// // "Thread unblocked!" will be printed now -/// -/// msg = receiver.recv().unwrap(); -/// println!("message {msg} received"); -/// -/// msg = receiver.recv().unwrap(); -/// -/// println!("message {msg} received"); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct SyncSender { - inner: mpmc::Sender, -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for SyncSender {} - -/// An error returned from the [`Sender::send`] or [`SyncSender::send`] -/// function on **channel**s. -/// -/// A **send** operation can only fail if the receiving end of a channel is -/// disconnected, implying that the data could never be received. The error -/// contains the data being sent as a payload so it can be recovered. -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(PartialEq, Eq, Clone, Copy)] -pub struct SendError(#[stable(feature = "rust1", since = "1.0.0")] pub T); - -/// An error returned from the [`recv`] function on a [`Receiver`]. -/// -/// The [`recv`] operation can only fail if the sending half of a -/// [`channel`] (or [`sync_channel`]) is disconnected, implying that no further -/// messages will ever be received. -/// -/// [`recv`]: Receiver::recv -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct RecvError; - -/// This enumeration is the list of the possible reasons that [`try_recv`] could -/// not return data when called. This can occur with both a [`channel`] and -/// a [`sync_channel`]. -/// -/// [`try_recv`]: Receiver::try_recv -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub enum TryRecvError { - /// This **channel** is currently empty, but the **Sender**(s) have not yet - /// disconnected, so data may yet become available. - #[stable(feature = "rust1", since = "1.0.0")] - Empty, - - /// The **channel**'s sending half has become disconnected, and there will - /// never be any more data received on it. - #[stable(feature = "rust1", since = "1.0.0")] - Disconnected, -} - -/// This enumeration is the list of possible errors that made [`recv_timeout`] -/// unable to return data when called. This can occur with both a [`channel`] and -/// a [`sync_channel`]. -/// -/// [`recv_timeout`]: Receiver::recv_timeout -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -#[stable(feature = "mpsc_recv_timeout", since = "1.12.0")] -pub enum RecvTimeoutError { - /// This **channel** is currently empty, but the **Sender**(s) have not yet - /// disconnected, so data may yet become available. - #[stable(feature = "mpsc_recv_timeout", since = "1.12.0")] - Timeout, - /// The **channel**'s sending half has become disconnected, and there will - /// never be any more data received on it. - #[stable(feature = "mpsc_recv_timeout", since = "1.12.0")] - Disconnected, -} - -/// This enumeration is the list of the possible error outcomes for the -/// [`try_send`] method. -/// -/// [`try_send`]: SyncSender::try_send -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(PartialEq, Eq, Clone, Copy)] -pub enum TrySendError { - /// The data could not be sent on the [`sync_channel`] because it would require that - /// the callee block to send the data. - /// - /// If this is a buffered channel, then the buffer is full at this time. If - /// this is not a buffered channel, then there is no [`Receiver`] available to - /// acquire the data. - #[stable(feature = "rust1", since = "1.0.0")] - Full(#[stable(feature = "rust1", since = "1.0.0")] T), - - /// This [`sync_channel`]'s receiving half has disconnected, so the data could not be - /// sent. The data is returned back to the callee in this case. - #[stable(feature = "rust1", since = "1.0.0")] - Disconnected(#[stable(feature = "rust1", since = "1.0.0")] T), -} - -/// Creates a new asynchronous channel, returning the sender/receiver halves. -/// -/// All data sent on the [`Sender`] will become available on the [`Receiver`] in -/// the same order as it was sent, and no [`send`] will block the calling thread -/// (this channel has an "infinite buffer", unlike [`sync_channel`], which will -/// block after its buffer limit is reached). [`recv`] will block until a message -/// is available while there is at least one [`Sender`] alive (including clones). -/// -/// The [`Sender`] can be cloned to [`send`] to the same channel multiple times, but -/// only one [`Receiver`] is supported. -/// -/// If the [`Receiver`] is disconnected while trying to [`send`] with the -/// [`Sender`], the [`send`] method will return a [`SendError`]. Similarly, if the -/// [`Sender`] is disconnected while trying to [`recv`], the [`recv`] method will -/// return a [`RecvError`]. -/// -/// [`send`]: Sender::send -/// [`recv`]: Receiver::recv -/// -/// # Examples -/// -/// ``` -/// use std::sync::mpsc::channel; -/// use std::thread; -/// -/// let (sender, receiver) = channel(); -/// -/// // Spawn off an expensive computation -/// thread::spawn(move || { -/// # fn expensive_computation() {} -/// sender.send(expensive_computation()).unwrap(); -/// }); -/// -/// // Do some useful work for awhile -/// -/// // Let's see what that answer was -/// println!("{:?}", receiver.recv().unwrap()); -/// ``` -#[must_use] -#[stable(feature = "rust1", since = "1.0.0")] -pub fn channel() -> (Sender, Receiver) { - let (tx, rx) = mpmc::channel(); - (Sender { inner: tx }, Receiver { inner: rx }) -} - -/// Creates a new synchronous, bounded channel. -/// -/// All data sent on the [`SyncSender`] will become available on the [`Receiver`] -/// in the same order as it was sent. Like asynchronous [`channel`]s, the -/// [`Receiver`] will block until a message becomes available. `sync_channel` -/// differs greatly in the semantics of the sender, however. -/// -/// This channel has an internal buffer on which messages will be queued. -/// `bound` specifies the buffer size. When the internal buffer becomes full, -/// future sends will *block* waiting for the buffer to open up. Note that a -/// buffer size of 0 is valid, in which case this becomes "rendezvous channel" -/// where each [`send`] will not return until a [`recv`] is paired with it. -/// -/// The [`SyncSender`] can be cloned to [`send`] to the same channel multiple -/// times, but only one [`Receiver`] is supported. -/// -/// Like asynchronous channels, if the [`Receiver`] is disconnected while trying -/// to [`send`] with the [`SyncSender`], the [`send`] method will return a -/// [`SendError`]. Similarly, If the [`SyncSender`] is disconnected while trying -/// to [`recv`], the [`recv`] method will return a [`RecvError`]. -/// -/// [`send`]: SyncSender::send -/// [`recv`]: Receiver::recv -/// -/// # Examples -/// -/// ``` -/// use std::sync::mpsc::sync_channel; -/// use std::thread; -/// -/// let (sender, receiver) = sync_channel(1); -/// -/// // this returns immediately -/// sender.send(1).unwrap(); -/// -/// thread::spawn(move || { -/// // this will block until the previous message has been received -/// sender.send(2).unwrap(); -/// }); -/// -/// assert_eq!(receiver.recv().unwrap(), 1); -/// assert_eq!(receiver.recv().unwrap(), 2); -/// ``` -#[must_use] -#[stable(feature = "rust1", since = "1.0.0")] -pub fn sync_channel(bound: usize) -> (SyncSender, Receiver) { - let (tx, rx) = mpmc::sync_channel(bound); - (SyncSender { inner: tx }, Receiver { inner: rx }) -} - -//////////////////////////////////////////////////////////////////////////////// -// Sender -//////////////////////////////////////////////////////////////////////////////// - -impl Sender { - /// Attempts to send a value on this channel, returning it back if it could - /// not be sent. - /// - /// A successful send occurs when it is determined that the other end of - /// the channel has not hung up already. An unsuccessful send would be one - /// where the corresponding receiver has already been deallocated. Note - /// that a return value of [`Err`] means that the data will never be - /// received, but a return value of [`Ok`] does *not* mean that the data - /// will be received. It is possible for the corresponding receiver to - /// hang up immediately after this function returns [`Ok`]. - /// - /// This method will never block the current thread. - /// - /// # Examples - /// - /// ``` - /// use std::sync::mpsc::channel; - /// - /// let (tx, rx) = channel(); - /// - /// // This send is always successful - /// tx.send(1).unwrap(); - /// - /// // This send will fail because the receiver is gone - /// drop(rx); - /// assert_eq!(tx.send(1).unwrap_err().0, 1); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn send(&self, t: T) -> Result<(), SendError> { - self.inner.send(t) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Sender { - /// Clone a sender to send to other threads. - /// - /// Note, be aware of the lifetime of the sender because all senders - /// (including the original) need to be dropped in order for - /// [`Receiver::recv`] to stop blocking. - fn clone(&self) -> Sender { - Sender { inner: self.inner.clone() } - } -} - -#[stable(feature = "mpsc_debug", since = "1.8.0")] -impl fmt::Debug for Sender { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Sender").finish_non_exhaustive() - } -} - -//////////////////////////////////////////////////////////////////////////////// -// SyncSender -//////////////////////////////////////////////////////////////////////////////// - -impl SyncSender { - /// Sends a value on this synchronous channel. - /// - /// This function will *block* until space in the internal buffer becomes - /// available or a receiver is available to hand off the message to. - /// - /// Note that a successful send does *not* guarantee that the receiver will - /// ever see the data if there is a buffer on this channel. Items may be - /// enqueued in the internal buffer for the receiver to receive at a later - /// time. If the buffer size is 0, however, the channel becomes a rendezvous - /// channel and it guarantees that the receiver has indeed received - /// the data if this function returns success. - /// - /// This function will never panic, but it may return [`Err`] if the - /// [`Receiver`] has disconnected and is no longer able to receive - /// information. - /// - /// # Examples - /// - /// ```rust - /// use std::sync::mpsc::sync_channel; - /// use std::thread; - /// - /// // Create a rendezvous sync_channel with buffer size 0 - /// let (sync_sender, receiver) = sync_channel(0); - /// - /// thread::spawn(move || { - /// println!("sending message..."); - /// sync_sender.send(1).unwrap(); - /// // Thread is now blocked until the message is received - /// - /// println!("...message received!"); - /// }); - /// - /// let msg = receiver.recv().unwrap(); - /// assert_eq!(1, msg); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn send(&self, t: T) -> Result<(), SendError> { - self.inner.send(t) - } - - /// Attempts to send a value on this channel without blocking. - /// - /// This method differs from [`send`] by returning immediately if the - /// channel's buffer is full or no receiver is waiting to acquire some - /// data. Compared with [`send`], this function has two failure cases - /// instead of one (one for disconnection, one for a full buffer). - /// - /// See [`send`] for notes about guarantees of whether the - /// receiver has received the data or not if this function is successful. - /// - /// [`send`]: Self::send - /// - /// # Examples - /// - /// ```rust - /// use std::sync::mpsc::sync_channel; - /// use std::thread; - /// - /// // Create a sync_channel with buffer size 1 - /// let (sync_sender, receiver) = sync_channel(1); - /// let sync_sender2 = sync_sender.clone(); - /// - /// // First thread owns sync_sender - /// thread::spawn(move || { - /// sync_sender.send(1).unwrap(); - /// sync_sender.send(2).unwrap(); - /// // Thread blocked - /// }); - /// - /// // Second thread owns sync_sender2 - /// thread::spawn(move || { - /// // This will return an error and send - /// // no message if the buffer is full - /// let _ = sync_sender2.try_send(3); - /// }); - /// - /// let mut msg; - /// msg = receiver.recv().unwrap(); - /// println!("message {msg} received"); - /// - /// msg = receiver.recv().unwrap(); - /// println!("message {msg} received"); - /// - /// // Third message may have never been sent - /// match receiver.try_recv() { - /// Ok(msg) => println!("message {msg} received"), - /// Err(_) => println!("the third message was never sent"), - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn try_send(&self, t: T) -> Result<(), TrySendError> { - self.inner.try_send(t) - } - - // Attempts to send for a value on this receiver, returning an error if the - // corresponding channel has hung up, or if it waits more than `timeout`. - // - // This method is currently private and only used for tests. - #[allow(unused)] - fn send_timeout(&self, t: T, timeout: Duration) -> Result<(), mpmc::SendTimeoutError> { - self.inner.send_timeout(t, timeout) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for SyncSender { - fn clone(&self) -> SyncSender { - SyncSender { inner: self.inner.clone() } - } -} - -#[stable(feature = "mpsc_debug", since = "1.8.0")] -impl fmt::Debug for SyncSender { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SyncSender").finish_non_exhaustive() - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Receiver -//////////////////////////////////////////////////////////////////////////////// - -impl Receiver { - /// Attempts to return a pending value on this receiver without blocking. - /// - /// This method will never block the caller in order to wait for data to - /// become available. Instead, this will always return immediately with a - /// possible option of pending data on the channel. - /// - /// This is useful for a flavor of "optimistic check" before deciding to - /// block on a receiver. - /// - /// Compared with [`recv`], this function has two failure cases instead of one - /// (one for disconnection, one for an empty buffer). - /// - /// [`recv`]: Self::recv - /// - /// # Examples - /// - /// ```rust - /// use std::sync::mpsc::{Receiver, channel}; - /// - /// let (_, receiver): (_, Receiver) = channel(); - /// - /// assert!(receiver.try_recv().is_err()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn try_recv(&self) -> Result { - self.inner.try_recv() - } - - /// Attempts to wait for a value on this receiver, returning an error if the - /// corresponding channel has hung up. - /// - /// This function will always block the current thread if there is no data - /// available and it's possible for more data to be sent (at least one sender - /// still exists). Once a message is sent to the corresponding [`Sender`] - /// (or [`SyncSender`]), this receiver will wake up and return that - /// message. - /// - /// If the corresponding [`Sender`] has disconnected, or it disconnects while - /// this call is blocking, this call will wake up and return [`Err`] to - /// indicate that no more messages can ever be received on this channel. - /// However, since channels are buffered, messages sent before the disconnect - /// will still be properly received. - /// - /// # Examples - /// - /// ``` - /// use std::sync::mpsc; - /// use std::thread; - /// - /// let (send, recv) = mpsc::channel(); - /// let handle = thread::spawn(move || { - /// send.send(1u8).unwrap(); - /// }); - /// - /// handle.join().unwrap(); - /// - /// assert_eq!(Ok(1), recv.recv()); - /// ``` - /// - /// Buffering behavior: - /// - /// ``` - /// use std::sync::mpsc; - /// use std::thread; - /// use std::sync::mpsc::RecvError; - /// - /// let (send, recv) = mpsc::channel(); - /// let handle = thread::spawn(move || { - /// send.send(1u8).unwrap(); - /// send.send(2).unwrap(); - /// send.send(3).unwrap(); - /// drop(send); - /// }); - /// - /// // wait for the thread to join so we ensure the sender is dropped - /// handle.join().unwrap(); - /// - /// assert_eq!(Ok(1), recv.recv()); - /// assert_eq!(Ok(2), recv.recv()); - /// assert_eq!(Ok(3), recv.recv()); - /// assert_eq!(Err(RecvError), recv.recv()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn recv(&self) -> Result { - self.inner.recv() - } - - /// Attempts to wait for a value on this receiver, returning an error if the - /// corresponding channel has hung up, or if it waits more than `timeout`. - /// - /// This function will always block the current thread if there is no data - /// available and it's possible for more data to be sent (at least one sender - /// still exists). Once a message is sent to the corresponding [`Sender`] - /// (or [`SyncSender`]), this receiver will wake up and return that - /// message. - /// - /// If the corresponding [`Sender`] has disconnected, or it disconnects while - /// this call is blocking, this call will wake up and return [`Err`] to - /// indicate that no more messages can ever be received on this channel. - /// However, since channels are buffered, messages sent before the disconnect - /// will still be properly received. - /// - /// # Examples - /// - /// Successfully receiving value before encountering timeout: - /// - /// ```no_run - /// use std::thread; - /// use std::time::Duration; - /// use std::sync::mpsc; - /// - /// let (send, recv) = mpsc::channel(); - /// - /// thread::spawn(move || { - /// send.send('a').unwrap(); - /// }); - /// - /// assert_eq!( - /// recv.recv_timeout(Duration::from_millis(400)), - /// Ok('a') - /// ); - /// ``` - /// - /// Receiving an error upon reaching timeout: - /// - /// ```no_run - /// use std::thread; - /// use std::time::Duration; - /// use std::sync::mpsc; - /// - /// let (send, recv) = mpsc::channel(); - /// - /// thread::spawn(move || { - /// thread::sleep(Duration::from_millis(800)); - /// send.send('a').unwrap(); - /// }); - /// - /// assert_eq!( - /// recv.recv_timeout(Duration::from_millis(400)), - /// Err(mpsc::RecvTimeoutError::Timeout) - /// ); - /// ``` - #[stable(feature = "mpsc_recv_timeout", since = "1.12.0")] - pub fn recv_timeout(&self, timeout: Duration) -> Result { - self.inner.recv_timeout(timeout) - } - - /// Attempts to wait for a value on this receiver, returning an error if the - /// corresponding channel has hung up, or if `deadline` is reached. - /// - /// This function will always block the current thread if there is no data - /// available and it's possible for more data to be sent. Once a message is - /// sent to the corresponding [`Sender`] (or [`SyncSender`]), then this - /// receiver will wake up and return that message. - /// - /// If the corresponding [`Sender`] has disconnected, or it disconnects while - /// this call is blocking, this call will wake up and return [`Err`] to - /// indicate that no more messages can ever be received on this channel. - /// However, since channels are buffered, messages sent before the disconnect - /// will still be properly received. - /// - /// # Examples - /// - /// Successfully receiving value before reaching deadline: - /// - /// ```no_run - /// #![feature(deadline_api)] - /// use std::thread; - /// use std::time::{Duration, Instant}; - /// use std::sync::mpsc; - /// - /// let (send, recv) = mpsc::channel(); - /// - /// thread::spawn(move || { - /// send.send('a').unwrap(); - /// }); - /// - /// assert_eq!( - /// recv.recv_deadline(Instant::now() + Duration::from_millis(400)), - /// Ok('a') - /// ); - /// ``` - /// - /// Receiving an error upon reaching deadline: - /// - /// ```no_run - /// #![feature(deadline_api)] - /// use std::thread; - /// use std::time::{Duration, Instant}; - /// use std::sync::mpsc; - /// - /// let (send, recv) = mpsc::channel(); - /// - /// thread::spawn(move || { - /// thread::sleep(Duration::from_millis(800)); - /// send.send('a').unwrap(); - /// }); - /// - /// assert_eq!( - /// recv.recv_deadline(Instant::now() + Duration::from_millis(400)), - /// Err(mpsc::RecvTimeoutError::Timeout) - /// ); - /// ``` - #[unstable(feature = "deadline_api", issue = "46316")] - pub fn recv_deadline(&self, deadline: Instant) -> Result { - self.inner.recv_deadline(deadline) - } - - /// Returns an iterator that will block waiting for messages, but never - /// [`panic!`]. It will return [`None`] when the channel has hung up. - /// - /// # Examples - /// - /// ```rust - /// use std::sync::mpsc::channel; - /// use std::thread; - /// - /// let (send, recv) = channel(); - /// - /// thread::spawn(move || { - /// send.send(1).unwrap(); - /// send.send(2).unwrap(); - /// send.send(3).unwrap(); - /// }); - /// - /// let mut iter = recv.iter(); - /// assert_eq!(iter.next(), Some(1)); - /// assert_eq!(iter.next(), Some(2)); - /// assert_eq!(iter.next(), Some(3)); - /// assert_eq!(iter.next(), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter(&self) -> Iter<'_, T> { - Iter { rx: self } - } - - /// Returns an iterator that will attempt to yield all pending values. - /// It will return `None` if there are no more pending values or if the - /// channel has hung up. The iterator will never [`panic!`] or block the - /// user by waiting for values. - /// - /// # Examples - /// - /// ```no_run - /// use std::sync::mpsc::channel; - /// use std::thread; - /// use std::time::Duration; - /// - /// let (sender, receiver) = channel(); - /// - /// // nothing is in the buffer yet - /// assert!(receiver.try_iter().next().is_none()); - /// - /// thread::spawn(move || { - /// thread::sleep(Duration::from_secs(1)); - /// sender.send(1).unwrap(); - /// sender.send(2).unwrap(); - /// sender.send(3).unwrap(); - /// }); - /// - /// // nothing is in the buffer yet - /// assert!(receiver.try_iter().next().is_none()); - /// - /// // block for two seconds - /// thread::sleep(Duration::from_secs(2)); - /// - /// let mut iter = receiver.try_iter(); - /// assert_eq!(iter.next(), Some(1)); - /// assert_eq!(iter.next(), Some(2)); - /// assert_eq!(iter.next(), Some(3)); - /// assert_eq!(iter.next(), None); - /// ``` - #[stable(feature = "receiver_try_iter", since = "1.15.0")] - pub fn try_iter(&self) -> TryIter<'_, T> { - TryIter { rx: self } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Iterator for Iter<'a, T> { - type Item = T; - - fn next(&mut self) -> Option { - self.rx.recv().ok() - } -} - -#[stable(feature = "receiver_try_iter", since = "1.15.0")] -impl<'a, T> Iterator for TryIter<'a, T> { - type Item = T; - - fn next(&mut self) -> Option { - self.rx.try_recv().ok() - } -} - -#[stable(feature = "receiver_into_iter", since = "1.1.0")] -impl<'a, T> IntoIterator for &'a Receiver { - type Item = T; - type IntoIter = Iter<'a, T>; - - fn into_iter(self) -> Iter<'a, T> { - self.iter() - } -} - -#[stable(feature = "receiver_into_iter", since = "1.1.0")] -impl Iterator for IntoIter { - type Item = T; - fn next(&mut self) -> Option { - self.rx.recv().ok() - } -} - -#[stable(feature = "receiver_into_iter", since = "1.1.0")] -impl IntoIterator for Receiver { - type Item = T; - type IntoIter = IntoIter; - - fn into_iter(self) -> IntoIter { - IntoIter { rx: self } - } -} - -#[stable(feature = "mpsc_debug", since = "1.8.0")] -impl fmt::Debug for Receiver { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Receiver").finish_non_exhaustive() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for SendError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SendError").finish_non_exhaustive() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for SendError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - "sending on a closed channel".fmt(f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for SendError { - #[allow(deprecated)] - fn description(&self) -> &str { - "sending on a closed channel" - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for TrySendError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - TrySendError::Full(..) => "Full(..)".fmt(f), - TrySendError::Disconnected(..) => "Disconnected(..)".fmt(f), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for TrySendError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - TrySendError::Full(..) => "sending on a full channel".fmt(f), - TrySendError::Disconnected(..) => "sending on a closed channel".fmt(f), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for TrySendError { - #[allow(deprecated)] - fn description(&self) -> &str { - match *self { - TrySendError::Full(..) => "sending on a full channel", - TrySendError::Disconnected(..) => "sending on a closed channel", - } - } -} - -#[stable(feature = "mpsc_error_conversions", since = "1.24.0")] -impl From> for TrySendError { - /// Converts a `SendError` into a `TrySendError`. - /// - /// This conversion always returns a `TrySendError::Disconnected` containing the data in the `SendError`. - /// - /// No data is allocated on the heap. - fn from(err: SendError) -> TrySendError { - match err { - SendError(t) => TrySendError::Disconnected(t), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for RecvError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - "receiving on a closed channel".fmt(f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for RecvError { - #[allow(deprecated)] - fn description(&self) -> &str { - "receiving on a closed channel" - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for TryRecvError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - TryRecvError::Empty => "receiving on an empty channel".fmt(f), - TryRecvError::Disconnected => "receiving on a closed channel".fmt(f), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for TryRecvError { - #[allow(deprecated)] - fn description(&self) -> &str { - match *self { - TryRecvError::Empty => "receiving on an empty channel", - TryRecvError::Disconnected => "receiving on a closed channel", - } - } -} - -#[stable(feature = "mpsc_error_conversions", since = "1.24.0")] -impl From for TryRecvError { - /// Converts a `RecvError` into a `TryRecvError`. - /// - /// This conversion always returns `TryRecvError::Disconnected`. - /// - /// No data is allocated on the heap. - fn from(err: RecvError) -> TryRecvError { - match err { - RecvError => TryRecvError::Disconnected, - } - } -} - -#[stable(feature = "mpsc_recv_timeout_error", since = "1.15.0")] -impl fmt::Display for RecvTimeoutError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - RecvTimeoutError::Timeout => "timed out waiting on channel".fmt(f), - RecvTimeoutError::Disconnected => "channel is empty and sending half is closed".fmt(f), - } - } -} - -#[stable(feature = "mpsc_recv_timeout_error", since = "1.15.0")] -impl error::Error for RecvTimeoutError { - #[allow(deprecated)] - fn description(&self) -> &str { - match *self { - RecvTimeoutError::Timeout => "timed out waiting on channel", - RecvTimeoutError::Disconnected => "channel is empty and sending half is closed", - } - } -} - -#[stable(feature = "mpsc_error_conversions", since = "1.24.0")] -impl From for RecvTimeoutError { - /// Converts a `RecvError` into a `RecvTimeoutError`. - /// - /// This conversion always returns `RecvTimeoutError::Disconnected`. - /// - /// No data is allocated on the heap. - fn from(err: RecvError) -> RecvTimeoutError { - match err { - RecvError => RecvTimeoutError::Disconnected, - } - } -} diff --git a/library/std/src/sync/mpsc/sync_tests.rs b/library/std/src/sync/mpsc/sync_tests.rs deleted file mode 100644 index 49b65c8efe692..0000000000000 --- a/library/std/src/sync/mpsc/sync_tests.rs +++ /dev/null @@ -1,669 +0,0 @@ -use super::*; -use crate::rc::Rc; -use crate::sync::mpmc::SendTimeoutError; -use crate::{env, thread}; - -pub fn stress_factor() -> usize { - match env::var("RUST_TEST_STRESS") { - Ok(val) => val.parse().unwrap(), - Err(..) => 1, - } -} - -#[test] -fn smoke() { - let (tx, rx) = sync_channel::(1); - tx.send(1).unwrap(); - assert_eq!(rx.recv().unwrap(), 1); -} - -#[test] -fn drop_full() { - let (tx, _rx) = sync_channel::>(1); - tx.send(Box::new(1)).unwrap(); -} - -#[test] -fn smoke_shared() { - let (tx, rx) = sync_channel::(1); - tx.send(1).unwrap(); - assert_eq!(rx.recv().unwrap(), 1); - let tx = tx.clone(); - tx.send(1).unwrap(); - assert_eq!(rx.recv().unwrap(), 1); -} - -#[test] -fn recv_timeout() { - let (tx, rx) = sync_channel::(1); - assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); - tx.send(1).unwrap(); - assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(1)); -} - -#[test] -fn send_timeout() { - let (tx, _rx) = sync_channel::(1); - assert_eq!(tx.send_timeout(1, Duration::from_millis(1)), Ok(())); - assert_eq!(tx.send_timeout(1, Duration::from_millis(1)), Err(SendTimeoutError::Timeout(1))); -} - -#[test] -fn smoke_threads() { - let (tx, rx) = sync_channel::(0); - let _t = thread::spawn(move || { - tx.send(1).unwrap(); - }); - assert_eq!(rx.recv().unwrap(), 1); -} - -#[test] -fn smoke_port_gone() { - let (tx, rx) = sync_channel::(0); - drop(rx); - assert!(tx.send(1).is_err()); -} - -#[test] -fn smoke_shared_port_gone2() { - let (tx, rx) = sync_channel::(0); - drop(rx); - let tx2 = tx.clone(); - drop(tx); - assert!(tx2.send(1).is_err()); -} - -#[test] -fn port_gone_concurrent() { - let (tx, rx) = sync_channel::(0); - let _t = thread::spawn(move || { - rx.recv().unwrap(); - }); - while tx.send(1).is_ok() {} -} - -#[test] -fn port_gone_concurrent_shared() { - let (tx, rx) = sync_channel::(0); - let tx2 = tx.clone(); - let _t = thread::spawn(move || { - rx.recv().unwrap(); - }); - while tx.send(1).is_ok() && tx2.send(1).is_ok() {} -} - -#[test] -fn smoke_chan_gone() { - let (tx, rx) = sync_channel::(0); - drop(tx); - assert!(rx.recv().is_err()); -} - -#[test] -fn smoke_chan_gone_shared() { - let (tx, rx) = sync_channel::<()>(0); - let tx2 = tx.clone(); - drop(tx); - drop(tx2); - assert!(rx.recv().is_err()); -} - -#[test] -fn chan_gone_concurrent() { - let (tx, rx) = sync_channel::(0); - thread::spawn(move || { - tx.send(1).unwrap(); - tx.send(1).unwrap(); - }); - while rx.recv().is_ok() {} -} - -#[test] -fn stress() { - let count = if cfg!(miri) { 100 } else { 10000 }; - let (tx, rx) = sync_channel::(0); - thread::spawn(move || { - for _ in 0..count { - tx.send(1).unwrap(); - } - }); - for _ in 0..count { - assert_eq!(rx.recv().unwrap(), 1); - } -} - -#[test] -fn stress_recv_timeout_two_threads() { - let count = if cfg!(miri) { 100 } else { 10000 }; - let (tx, rx) = sync_channel::(0); - - thread::spawn(move || { - for _ in 0..count { - tx.send(1).unwrap(); - } - }); - - let mut recv_count = 0; - loop { - match rx.recv_timeout(Duration::from_millis(1)) { - Ok(v) => { - assert_eq!(v, 1); - recv_count += 1; - } - Err(RecvTimeoutError::Timeout) => continue, - Err(RecvTimeoutError::Disconnected) => break, - } - } - - assert_eq!(recv_count, count); -} - -#[test] -fn stress_recv_timeout_shared() { - const AMT: u32 = if cfg!(miri) { 100 } else { 1000 }; - const NTHREADS: u32 = 8; - let (tx, rx) = sync_channel::(0); - let (dtx, drx) = sync_channel::<()>(0); - - thread::spawn(move || { - let mut recv_count = 0; - loop { - match rx.recv_timeout(Duration::from_millis(10)) { - Ok(v) => { - assert_eq!(v, 1); - recv_count += 1; - } - Err(RecvTimeoutError::Timeout) => continue, - Err(RecvTimeoutError::Disconnected) => break, - } - } - - assert_eq!(recv_count, AMT * NTHREADS); - assert!(rx.try_recv().is_err()); - - dtx.send(()).unwrap(); - }); - - for _ in 0..NTHREADS { - let tx = tx.clone(); - thread::spawn(move || { - for _ in 0..AMT { - tx.send(1).unwrap(); - } - }); - } - - drop(tx); - - drx.recv().unwrap(); -} - -#[test] -fn stress_shared() { - const AMT: u32 = if cfg!(miri) { 100 } else { 1000 }; - const NTHREADS: u32 = 8; - let (tx, rx) = sync_channel::(0); - let (dtx, drx) = sync_channel::<()>(0); - - thread::spawn(move || { - for _ in 0..AMT * NTHREADS { - assert_eq!(rx.recv().unwrap(), 1); - } - match rx.try_recv() { - Ok(..) => panic!(), - _ => {} - } - dtx.send(()).unwrap(); - }); - - for _ in 0..NTHREADS { - let tx = tx.clone(); - thread::spawn(move || { - for _ in 0..AMT { - tx.send(1).unwrap(); - } - }); - } - drop(tx); - drx.recv().unwrap(); -} - -#[test] -fn oneshot_single_thread_close_port_first() { - // Simple test of closing without sending - let (_tx, rx) = sync_channel::(0); - drop(rx); -} - -#[test] -fn oneshot_single_thread_close_chan_first() { - // Simple test of closing without sending - let (tx, _rx) = sync_channel::(0); - drop(tx); -} - -#[test] -fn oneshot_single_thread_send_port_close() { - // Testing that the sender cleans up the payload if receiver is closed - let (tx, rx) = sync_channel::>(0); - drop(rx); - assert!(tx.send(Box::new(0)).is_err()); -} - -#[test] -fn oneshot_single_thread_recv_chan_close() { - // Receiving on a closed chan will panic - let res = thread::spawn(move || { - let (tx, rx) = sync_channel::(0); - drop(tx); - rx.recv().unwrap(); - }) - .join(); - // What is our res? - assert!(res.is_err()); -} - -#[test] -fn oneshot_single_thread_send_then_recv() { - let (tx, rx) = sync_channel::>(1); - tx.send(Box::new(10)).unwrap(); - assert!(*rx.recv().unwrap() == 10); -} - -#[test] -fn oneshot_single_thread_try_send_open() { - let (tx, rx) = sync_channel::(1); - assert_eq!(tx.try_send(10), Ok(())); - assert!(rx.recv().unwrap() == 10); -} - -#[test] -fn oneshot_single_thread_try_send_closed() { - let (tx, rx) = sync_channel::(0); - drop(rx); - assert_eq!(tx.try_send(10), Err(TrySendError::Disconnected(10))); -} - -#[test] -fn oneshot_single_thread_try_send_closed2() { - let (tx, _rx) = sync_channel::(0); - assert_eq!(tx.try_send(10), Err(TrySendError::Full(10))); -} - -#[test] -fn oneshot_single_thread_try_recv_open() { - let (tx, rx) = sync_channel::(1); - tx.send(10).unwrap(); - assert!(rx.recv() == Ok(10)); -} - -#[test] -fn oneshot_single_thread_try_recv_closed() { - let (tx, rx) = sync_channel::(0); - drop(tx); - assert!(rx.recv().is_err()); -} - -#[test] -fn oneshot_single_thread_try_recv_closed_with_data() { - let (tx, rx) = sync_channel::(1); - tx.send(10).unwrap(); - drop(tx); - assert_eq!(rx.try_recv(), Ok(10)); - assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); -} - -#[test] -fn oneshot_single_thread_peek_data() { - let (tx, rx) = sync_channel::(1); - assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); - tx.send(10).unwrap(); - assert_eq!(rx.try_recv(), Ok(10)); -} - -#[test] -fn oneshot_single_thread_peek_close() { - let (tx, rx) = sync_channel::(0); - drop(tx); - assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); - assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); -} - -#[test] -fn oneshot_single_thread_peek_open() { - let (_tx, rx) = sync_channel::(0); - assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); -} - -#[test] -fn oneshot_multi_task_recv_then_send() { - let (tx, rx) = sync_channel::>(0); - let _t = thread::spawn(move || { - assert!(*rx.recv().unwrap() == 10); - }); - - tx.send(Box::new(10)).unwrap(); -} - -#[test] -fn oneshot_multi_task_recv_then_close() { - let (tx, rx) = sync_channel::>(0); - let _t = thread::spawn(move || { - drop(tx); - }); - let res = thread::spawn(move || { - assert!(*rx.recv().unwrap() == 10); - }) - .join(); - assert!(res.is_err()); -} - -#[test] -fn oneshot_multi_thread_close_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = sync_channel::(0); - let _t = thread::spawn(move || { - drop(rx); - }); - drop(tx); - } -} - -#[test] -fn oneshot_multi_thread_send_close_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = sync_channel::(0); - let _t = thread::spawn(move || { - drop(rx); - }); - let _ = thread::spawn(move || { - tx.send(1).unwrap(); - }) - .join(); - } -} - -#[test] -fn oneshot_multi_thread_recv_close_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = sync_channel::(0); - let _t = thread::spawn(move || { - let res = thread::spawn(move || { - rx.recv().unwrap(); - }) - .join(); - assert!(res.is_err()); - }); - let _t = thread::spawn(move || { - thread::spawn(move || { - drop(tx); - }); - }); - } -} - -#[test] -fn oneshot_multi_thread_send_recv_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = sync_channel::>(0); - let _t = thread::spawn(move || { - tx.send(Box::new(10)).unwrap(); - }); - assert!(*rx.recv().unwrap() == 10); - } -} - -#[test] -fn stream_send_recv_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = sync_channel::>(0); - - send(tx, 0); - recv(rx, 0); - - fn send(tx: SyncSender>, i: i32) { - if i == 10 { - return; - } - - thread::spawn(move || { - tx.send(Box::new(i)).unwrap(); - send(tx, i + 1); - }); - } - - fn recv(rx: Receiver>, i: i32) { - if i == 10 { - return; - } - - thread::spawn(move || { - assert!(*rx.recv().unwrap() == i); - recv(rx, i + 1); - }); - } - } -} - -#[test] -fn recv_a_lot() { - let count = if cfg!(miri) { 1000 } else { 10000 }; - // Regression test that we don't run out of stack in scheduler context - let (tx, rx) = sync_channel(count); - for _ in 0..count { - tx.send(()).unwrap(); - } - for _ in 0..count { - rx.recv().unwrap(); - } -} - -#[test] -fn shared_chan_stress() { - let (tx, rx) = sync_channel(0); - let total = stress_factor() + 100; - for _ in 0..total { - let tx = tx.clone(); - thread::spawn(move || { - tx.send(()).unwrap(); - }); - } - - for _ in 0..total { - rx.recv().unwrap(); - } -} - -#[test] -fn test_nested_recv_iter() { - let (tx, rx) = sync_channel::(0); - let (total_tx, total_rx) = sync_channel::(0); - - let _t = thread::spawn(move || { - let mut acc = 0; - for x in rx.iter() { - acc += x; - } - total_tx.send(acc).unwrap(); - }); - - tx.send(3).unwrap(); - tx.send(1).unwrap(); - tx.send(2).unwrap(); - drop(tx); - assert_eq!(total_rx.recv().unwrap(), 6); -} - -#[test] -fn test_recv_iter_break() { - let (tx, rx) = sync_channel::(0); - let (count_tx, count_rx) = sync_channel(0); - - let _t = thread::spawn(move || { - let mut count = 0; - for x in rx.iter() { - if count >= 3 { - break; - } else { - count += x; - } - } - count_tx.send(count).unwrap(); - }); - - tx.send(2).unwrap(); - tx.send(2).unwrap(); - tx.send(2).unwrap(); - let _ = tx.try_send(2); - drop(tx); - assert_eq!(count_rx.recv().unwrap(), 4); -} - -#[test] -fn try_recv_states() { - let (tx1, rx1) = sync_channel::(1); - let (tx2, rx2) = sync_channel::<()>(1); - let (tx3, rx3) = sync_channel::<()>(1); - let _t = thread::spawn(move || { - rx2.recv().unwrap(); - tx1.send(1).unwrap(); - tx3.send(()).unwrap(); - rx2.recv().unwrap(); - drop(tx1); - tx3.send(()).unwrap(); - }); - - assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); - tx2.send(()).unwrap(); - rx3.recv().unwrap(); - assert_eq!(rx1.try_recv(), Ok(1)); - assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); - tx2.send(()).unwrap(); - rx3.recv().unwrap(); - assert_eq!(rx1.try_recv(), Err(TryRecvError::Disconnected)); -} - -// This bug used to end up in a livelock inside of the Receiver destructor -// because the internal state of the Shared packet was corrupted -#[test] -fn destroy_upgraded_shared_port_when_sender_still_active() { - let (tx, rx) = sync_channel::<()>(0); - let (tx2, rx2) = sync_channel::<()>(0); - let _t = thread::spawn(move || { - rx.recv().unwrap(); // wait on a oneshot - drop(rx); // destroy a shared - tx2.send(()).unwrap(); - }); - // make sure the other thread has gone to sleep - for _ in 0..5000 { - thread::yield_now(); - } - - // upgrade to a shared chan and send a message - let t = tx.clone(); - drop(tx); - t.send(()).unwrap(); - - // wait for the child thread to exit before we exit - rx2.recv().unwrap(); -} - -#[test] -fn send1() { - let (tx, rx) = sync_channel::(0); - let _t = thread::spawn(move || { - rx.recv().unwrap(); - }); - assert_eq!(tx.send(1), Ok(())); -} - -#[test] -fn send2() { - let (tx, rx) = sync_channel::(0); - let _t = thread::spawn(move || { - drop(rx); - }); - assert!(tx.send(1).is_err()); -} - -#[test] -fn send3() { - let (tx, rx) = sync_channel::(1); - assert_eq!(tx.send(1), Ok(())); - let _t = thread::spawn(move || { - drop(rx); - }); - assert!(tx.send(1).is_err()); -} - -#[test] -fn send4() { - let (tx, rx) = sync_channel::(0); - let tx2 = tx.clone(); - let (done, donerx) = channel(); - let done2 = done.clone(); - let _t = thread::spawn(move || { - assert!(tx.send(1).is_err()); - done.send(()).unwrap(); - }); - let _t = thread::spawn(move || { - assert!(tx2.send(2).is_err()); - done2.send(()).unwrap(); - }); - drop(rx); - donerx.recv().unwrap(); - donerx.recv().unwrap(); -} - -#[test] -fn try_send1() { - let (tx, _rx) = sync_channel::(0); - assert_eq!(tx.try_send(1), Err(TrySendError::Full(1))); -} - -#[test] -fn try_send2() { - let (tx, _rx) = sync_channel::(1); - assert_eq!(tx.try_send(1), Ok(())); - assert_eq!(tx.try_send(1), Err(TrySendError::Full(1))); -} - -#[test] -fn try_send3() { - let (tx, rx) = sync_channel::(1); - assert_eq!(tx.try_send(1), Ok(())); - drop(rx); - assert_eq!(tx.try_send(1), Err(TrySendError::Disconnected(1))); -} - -#[test] -fn issue_15761() { - fn repro() { - let (tx1, rx1) = sync_channel::<()>(3); - let (tx2, rx2) = sync_channel::<()>(3); - - let _t = thread::spawn(move || { - rx1.recv().unwrap(); - tx2.try_send(()).unwrap(); - }); - - tx1.try_send(()).unwrap(); - rx2.recv().unwrap(); - } - - for _ in 0..100 { - repro() - } -} - -#[test] -fn drop_unreceived() { - let (tx, rx) = sync_channel::>(1); - let msg = Rc::new(()); - let weak = Rc::downgrade(&msg); - assert!(tx.send(msg).is_ok()); - drop(rx); - // Messages should be dropped immediately when the last receiver is destroyed. - assert!(weak.upgrade().is_none()); - drop(tx); -} diff --git a/library/std/src/sync/mpsc/tests.rs b/library/std/src/sync/mpsc/tests.rs deleted file mode 100644 index 13892fa0d18e4..0000000000000 --- a/library/std/src/sync/mpsc/tests.rs +++ /dev/null @@ -1,721 +0,0 @@ -use super::*; -use crate::{env, thread}; - -pub fn stress_factor() -> usize { - match env::var("RUST_TEST_STRESS") { - Ok(val) => val.parse().unwrap(), - Err(..) => 1, - } -} - -#[test] -fn smoke() { - let (tx, rx) = channel::(); - tx.send(1).unwrap(); - assert_eq!(rx.recv().unwrap(), 1); -} - -#[test] -fn drop_full() { - let (tx, _rx) = channel::>(); - tx.send(Box::new(1)).unwrap(); -} - -#[test] -fn drop_full_shared() { - let (tx, _rx) = channel::>(); - drop(tx.clone()); - drop(tx.clone()); - tx.send(Box::new(1)).unwrap(); -} - -#[test] -fn smoke_shared() { - let (tx, rx) = channel::(); - tx.send(1).unwrap(); - assert_eq!(rx.recv().unwrap(), 1); - let tx = tx.clone(); - tx.send(1).unwrap(); - assert_eq!(rx.recv().unwrap(), 1); -} - -#[test] -fn smoke_threads() { - let (tx, rx) = channel::(); - let _t = thread::spawn(move || { - tx.send(1).unwrap(); - }); - assert_eq!(rx.recv().unwrap(), 1); -} - -#[test] -fn smoke_port_gone() { - let (tx, rx) = channel::(); - drop(rx); - assert!(tx.send(1).is_err()); -} - -#[test] -fn smoke_shared_port_gone() { - let (tx, rx) = channel::(); - drop(rx); - assert!(tx.send(1).is_err()) -} - -#[test] -fn smoke_shared_port_gone2() { - let (tx, rx) = channel::(); - drop(rx); - let tx2 = tx.clone(); - drop(tx); - assert!(tx2.send(1).is_err()); -} - -#[test] -fn port_gone_concurrent() { - let (tx, rx) = channel::(); - let _t = thread::spawn(move || { - rx.recv().unwrap(); - }); - while tx.send(1).is_ok() {} -} - -#[test] -fn port_gone_concurrent_shared() { - let (tx, rx) = channel::(); - let tx2 = tx.clone(); - let _t = thread::spawn(move || { - rx.recv().unwrap(); - }); - while tx.send(1).is_ok() && tx2.send(1).is_ok() {} -} - -#[test] -fn smoke_chan_gone() { - let (tx, rx) = channel::(); - drop(tx); - assert!(rx.recv().is_err()); -} - -#[test] -fn smoke_chan_gone_shared() { - let (tx, rx) = channel::<()>(); - let tx2 = tx.clone(); - drop(tx); - drop(tx2); - assert!(rx.recv().is_err()); -} - -#[test] -fn chan_gone_concurrent() { - let (tx, rx) = channel::(); - let _t = thread::spawn(move || { - tx.send(1).unwrap(); - tx.send(1).unwrap(); - }); - while rx.recv().is_ok() {} -} - -#[test] -fn stress() { - let count = if cfg!(miri) { 100 } else { 10000 }; - let (tx, rx) = channel::(); - let t = thread::spawn(move || { - for _ in 0..count { - tx.send(1).unwrap(); - } - }); - for _ in 0..count { - assert_eq!(rx.recv().unwrap(), 1); - } - t.join().ok().expect("thread panicked"); -} - -#[test] -fn stress_shared() { - const AMT: u32 = if cfg!(miri) { 100 } else { 10000 }; - const NTHREADS: u32 = 8; - let (tx, rx) = channel::(); - - let t = thread::spawn(move || { - for _ in 0..AMT * NTHREADS { - assert_eq!(rx.recv().unwrap(), 1); - } - match rx.try_recv() { - Ok(..) => panic!(), - _ => {} - } - }); - - for _ in 0..NTHREADS { - let tx = tx.clone(); - thread::spawn(move || { - for _ in 0..AMT { - tx.send(1).unwrap(); - } - }); - } - drop(tx); - t.join().ok().expect("thread panicked"); -} - -#[test] -fn send_from_outside_runtime() { - let (tx1, rx1) = channel::<()>(); - let (tx2, rx2) = channel::(); - let t1 = thread::spawn(move || { - tx1.send(()).unwrap(); - for _ in 0..40 { - assert_eq!(rx2.recv().unwrap(), 1); - } - }); - rx1.recv().unwrap(); - let t2 = thread::spawn(move || { - for _ in 0..40 { - tx2.send(1).unwrap(); - } - }); - t1.join().ok().expect("thread panicked"); - t2.join().ok().expect("thread panicked"); -} - -#[test] -fn recv_from_outside_runtime() { - let (tx, rx) = channel::(); - let t = thread::spawn(move || { - for _ in 0..40 { - assert_eq!(rx.recv().unwrap(), 1); - } - }); - for _ in 0..40 { - tx.send(1).unwrap(); - } - t.join().ok().expect("thread panicked"); -} - -#[test] -fn no_runtime() { - let (tx1, rx1) = channel::(); - let (tx2, rx2) = channel::(); - let t1 = thread::spawn(move || { - assert_eq!(rx1.recv().unwrap(), 1); - tx2.send(2).unwrap(); - }); - let t2 = thread::spawn(move || { - tx1.send(1).unwrap(); - assert_eq!(rx2.recv().unwrap(), 2); - }); - t1.join().ok().expect("thread panicked"); - t2.join().ok().expect("thread panicked"); -} - -#[test] -fn oneshot_single_thread_close_port_first() { - // Simple test of closing without sending - let (_tx, rx) = channel::(); - drop(rx); -} - -#[test] -fn oneshot_single_thread_close_chan_first() { - // Simple test of closing without sending - let (tx, _rx) = channel::(); - drop(tx); -} - -#[test] -fn oneshot_single_thread_send_port_close() { - // Testing that the sender cleans up the payload if receiver is closed - let (tx, rx) = channel::>(); - drop(rx); - assert!(tx.send(Box::new(0)).is_err()); -} - -#[test] -fn oneshot_single_thread_recv_chan_close() { - // Receiving on a closed chan will panic - let res = thread::spawn(move || { - let (tx, rx) = channel::(); - drop(tx); - rx.recv().unwrap(); - }) - .join(); - // What is our res? - assert!(res.is_err()); -} - -#[test] -fn oneshot_single_thread_send_then_recv() { - let (tx, rx) = channel::>(); - tx.send(Box::new(10)).unwrap(); - assert!(*rx.recv().unwrap() == 10); -} - -#[test] -fn oneshot_single_thread_try_send_open() { - let (tx, rx) = channel::(); - assert!(tx.send(10).is_ok()); - assert!(rx.recv().unwrap() == 10); -} - -#[test] -fn oneshot_single_thread_try_send_closed() { - let (tx, rx) = channel::(); - drop(rx); - assert!(tx.send(10).is_err()); -} - -#[test] -fn oneshot_single_thread_try_recv_open() { - let (tx, rx) = channel::(); - tx.send(10).unwrap(); - assert!(rx.recv() == Ok(10)); -} - -#[test] -fn oneshot_single_thread_try_recv_closed() { - let (tx, rx) = channel::(); - drop(tx); - assert!(rx.recv().is_err()); -} - -#[test] -fn oneshot_single_thread_peek_data() { - let (tx, rx) = channel::(); - assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); - tx.send(10).unwrap(); - assert_eq!(rx.try_recv(), Ok(10)); -} - -#[test] -fn oneshot_single_thread_peek_close() { - let (tx, rx) = channel::(); - drop(tx); - assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); - assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); -} - -#[test] -fn oneshot_single_thread_peek_open() { - let (_tx, rx) = channel::(); - assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); -} - -#[test] -fn oneshot_multi_task_recv_then_send() { - let (tx, rx) = channel::>(); - let _t = thread::spawn(move || { - assert!(*rx.recv().unwrap() == 10); - }); - - tx.send(Box::new(10)).unwrap(); -} - -#[test] -fn oneshot_multi_task_recv_then_close() { - let (tx, rx) = channel::>(); - let _t = thread::spawn(move || { - drop(tx); - }); - let res = thread::spawn(move || { - assert!(*rx.recv().unwrap() == 10); - }) - .join(); - assert!(res.is_err()); -} - -#[test] -fn oneshot_multi_thread_close_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = channel::(); - let _t = thread::spawn(move || { - drop(rx); - }); - drop(tx); - } -} - -#[test] -fn oneshot_multi_thread_send_close_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = channel::(); - let _t = thread::spawn(move || { - drop(rx); - }); - let _ = thread::spawn(move || { - tx.send(1).unwrap(); - }) - .join(); - } -} - -#[test] -fn oneshot_multi_thread_recv_close_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = channel::(); - thread::spawn(move || { - let res = thread::spawn(move || { - rx.recv().unwrap(); - }) - .join(); - assert!(res.is_err()); - }); - let _t = thread::spawn(move || { - thread::spawn(move || { - drop(tx); - }); - }); - } -} - -#[test] -fn oneshot_multi_thread_send_recv_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = channel::>(); - let _t = thread::spawn(move || { - tx.send(Box::new(10)).unwrap(); - }); - assert!(*rx.recv().unwrap() == 10); - } -} - -#[test] -fn stream_send_recv_stress() { - for _ in 0..stress_factor() { - let (tx, rx) = channel(); - - send(tx, 0); - recv(rx, 0); - - fn send(tx: Sender>, i: i32) { - if i == 10 { - return; - } - - thread::spawn(move || { - tx.send(Box::new(i)).unwrap(); - send(tx, i + 1); - }); - } - - fn recv(rx: Receiver>, i: i32) { - if i == 10 { - return; - } - - thread::spawn(move || { - assert!(*rx.recv().unwrap() == i); - recv(rx, i + 1); - }); - } - } -} - -#[test] -fn oneshot_single_thread_recv_timeout() { - let (tx, rx) = channel(); - tx.send(()).unwrap(); - assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); - assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); - tx.send(()).unwrap(); - assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); -} - -#[test] -fn stress_recv_timeout_two_threads() { - let (tx, rx) = channel(); - let stress = stress_factor() + 100; - let timeout = Duration::from_millis(100); - - thread::spawn(move || { - for i in 0..stress { - if i % 2 == 0 { - thread::sleep(timeout * 2); - } - tx.send(1usize).unwrap(); - } - }); - - let mut recv_count = 0; - loop { - match rx.recv_timeout(timeout) { - Ok(n) => { - assert_eq!(n, 1usize); - recv_count += 1; - } - Err(RecvTimeoutError::Timeout) => continue, - Err(RecvTimeoutError::Disconnected) => break, - } - } - - assert_eq!(recv_count, stress); -} - -#[test] -fn recv_timeout_upgrade() { - let (tx, rx) = channel::<()>(); - let timeout = Duration::from_millis(1); - let _tx_clone = tx.clone(); - - let start = Instant::now(); - assert_eq!(rx.recv_timeout(timeout), Err(RecvTimeoutError::Timeout)); - assert!(Instant::now() >= start + timeout); -} - -#[test] -fn stress_recv_timeout_shared() { - let (tx, rx) = channel(); - let stress = stress_factor() + 100; - - for i in 0..stress { - let tx = tx.clone(); - thread::spawn(move || { - thread::sleep(Duration::from_millis(i as u64 * 10)); - tx.send(1usize).unwrap(); - }); - } - - drop(tx); - - let mut recv_count = 0; - loop { - match rx.recv_timeout(Duration::from_millis(10)) { - Ok(n) => { - assert_eq!(n, 1usize); - recv_count += 1; - } - Err(RecvTimeoutError::Timeout) => continue, - Err(RecvTimeoutError::Disconnected) => break, - } - } - - assert_eq!(recv_count, stress); -} - -#[test] -fn very_long_recv_timeout_wont_panic() { - let (tx, rx) = channel::<()>(); - let join_handle = thread::spawn(move || rx.recv_timeout(Duration::from_secs(u64::MAX))); - thread::sleep(Duration::from_secs(1)); - assert!(tx.send(()).is_ok()); - assert_eq!(join_handle.join().unwrap(), Ok(())); -} - -#[test] -fn recv_a_lot() { - let count = if cfg!(miri) { 1000 } else { 10000 }; - // Regression test that we don't run out of stack in scheduler context - let (tx, rx) = channel(); - for _ in 0..count { - tx.send(()).unwrap(); - } - for _ in 0..count { - rx.recv().unwrap(); - } -} - -#[test] -fn shared_recv_timeout() { - let (tx, rx) = channel(); - let total = 5; - for _ in 0..total { - let tx = tx.clone(); - thread::spawn(move || { - tx.send(()).unwrap(); - }); - } - - for _ in 0..total { - rx.recv().unwrap(); - } - - assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); - tx.send(()).unwrap(); - assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); -} - -#[test] -fn shared_chan_stress() { - let (tx, rx) = channel(); - let total = stress_factor() + 100; - for _ in 0..total { - let tx = tx.clone(); - thread::spawn(move || { - tx.send(()).unwrap(); - }); - } - - for _ in 0..total { - rx.recv().unwrap(); - } -} - -#[test] -fn test_nested_recv_iter() { - let (tx, rx) = channel::(); - let (total_tx, total_rx) = channel::(); - - let _t = thread::spawn(move || { - let mut acc = 0; - for x in rx.iter() { - acc += x; - } - total_tx.send(acc).unwrap(); - }); - - tx.send(3).unwrap(); - tx.send(1).unwrap(); - tx.send(2).unwrap(); - drop(tx); - assert_eq!(total_rx.recv().unwrap(), 6); -} - -#[test] -fn test_recv_iter_break() { - let (tx, rx) = channel::(); - let (count_tx, count_rx) = channel(); - - let _t = thread::spawn(move || { - let mut count = 0; - for x in rx.iter() { - if count >= 3 { - break; - } else { - count += x; - } - } - count_tx.send(count).unwrap(); - }); - - tx.send(2).unwrap(); - tx.send(2).unwrap(); - tx.send(2).unwrap(); - let _ = tx.send(2); - drop(tx); - assert_eq!(count_rx.recv().unwrap(), 4); -} - -#[test] -fn test_recv_try_iter() { - let (request_tx, request_rx) = channel(); - let (response_tx, response_rx) = channel(); - - // Request `x`s until we have `6`. - let t = thread::spawn(move || { - let mut count = 0; - loop { - for x in response_rx.try_iter() { - count += x; - if count == 6 { - return count; - } - } - request_tx.send(()).unwrap(); - } - }); - - for _ in request_rx.iter() { - if response_tx.send(2).is_err() { - break; - } - } - - assert_eq!(t.join().unwrap(), 6); -} - -#[test] -fn test_recv_into_iter_owned() { - let mut iter = { - let (tx, rx) = channel::(); - tx.send(1).unwrap(); - tx.send(2).unwrap(); - - rx.into_iter() - }; - assert_eq!(iter.next().unwrap(), 1); - assert_eq!(iter.next().unwrap(), 2); - assert_eq!(iter.next().is_none(), true); -} - -#[test] -fn test_recv_into_iter_borrowed() { - let (tx, rx) = channel::(); - tx.send(1).unwrap(); - tx.send(2).unwrap(); - drop(tx); - let mut iter = (&rx).into_iter(); - assert_eq!(iter.next().unwrap(), 1); - assert_eq!(iter.next().unwrap(), 2); - assert_eq!(iter.next().is_none(), true); -} - -#[test] -fn try_recv_states() { - let (tx1, rx1) = channel::(); - let (tx2, rx2) = channel::<()>(); - let (tx3, rx3) = channel::<()>(); - let _t = thread::spawn(move || { - rx2.recv().unwrap(); - tx1.send(1).unwrap(); - tx3.send(()).unwrap(); - rx2.recv().unwrap(); - drop(tx1); - tx3.send(()).unwrap(); - }); - - assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); - tx2.send(()).unwrap(); - rx3.recv().unwrap(); - assert_eq!(rx1.try_recv(), Ok(1)); - assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); - tx2.send(()).unwrap(); - rx3.recv().unwrap(); - assert_eq!(rx1.try_recv(), Err(TryRecvError::Disconnected)); -} - -// This bug used to end up in a livelock inside of the Receiver destructor -// because the internal state of the Shared packet was corrupted -#[test] -fn destroy_upgraded_shared_port_when_sender_still_active() { - let (tx, rx) = channel(); - let (tx2, rx2) = channel(); - let _t = thread::spawn(move || { - rx.recv().unwrap(); // wait on a oneshot - drop(rx); // destroy a shared - tx2.send(()).unwrap(); - }); - // make sure the other thread has gone to sleep - for _ in 0..5000 { - thread::yield_now(); - } - - // upgrade to a shared chan and send a message - let t = tx.clone(); - drop(tx); - t.send(()).unwrap(); - - // wait for the child thread to exit before we exit - rx2.recv().unwrap(); -} - -#[test] -fn issue_32114() { - let (tx, _) = channel(); - let _ = tx.send(123); - assert_eq!(tx.send(123), Err(SendError(123))); -} - -#[test] -fn issue_39364() { - let (tx, rx) = channel::<()>(); - let t = thread::spawn(move || { - thread::sleep(Duration::from_millis(300)); - let _ = tx.clone(); - // Don't drop; hand back to caller. - tx - }); - - let _ = rx.recv_timeout(Duration::from_millis(500)); - let _tx = t.join().unwrap(); // delay dropping until end of test - let _ = rx.recv_timeout(Duration::from_millis(500)); -} diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index 49f2dafd8fd9c..ffb90b1469584 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -13,6 +13,9 @@ use crate::sync::Once; /// Where OnceLock shines is when LazyLock is too simple to support a given case, as LazyLock /// doesn't allow additional inputs to its function after you call [`LazyLock::new(|| ...)`]. /// +/// A `OnceLock` can be thought of as a safe abstraction over uninitialized data that becomes +/// initialized once written. +/// /// [`OnceCell`]: crate::cell::OnceCell /// [`LazyLock`]: crate::sync::LazyLock /// [`LazyLock::new(|| ...)`]: crate::sync::LazyLock::new @@ -126,7 +129,7 @@ pub struct OnceLock { } impl OnceLock { - /// Creates a new empty cell. + /// Creates a new uninitialized cell. #[inline] #[must_use] #[stable(feature = "once_cell", since = "1.70.0")] @@ -141,8 +144,8 @@ impl OnceLock { /// Gets the reference to the underlying value. /// - /// Returns `None` if the cell is empty, or being initialized. This - /// method never blocks. + /// Returns `None` if the cell is uninitialized, or being initialized. + /// This method never blocks. #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn get(&self) -> Option<&T> { @@ -156,7 +159,8 @@ impl OnceLock { /// Gets the mutable reference to the underlying value. /// - /// Returns `None` if the cell is empty. This method never blocks. + /// Returns `None` if the cell is uninitialized, or being initialized. + /// This method never blocks. #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn get_mut(&mut self) -> Option<&mut T> { @@ -174,8 +178,6 @@ impl OnceLock { /// /// Waiting for a computation on another thread to finish: /// ```rust - /// #![feature(once_wait)] - /// /// use std::thread; /// use std::sync::OnceLock; /// @@ -189,19 +191,20 @@ impl OnceLock { /// }) /// ``` #[inline] - #[unstable(feature = "once_wait", issue = "127527")] + #[stable(feature = "once_wait", since = "1.86.0")] pub fn wait(&self) -> &T { self.once.wait_force(); unsafe { self.get_unchecked() } } - /// Sets the contents of this cell to `value`. + /// Initializes the contents of the cell to `value`. /// /// May block if another thread is currently attempting to initialize the cell. The cell is - /// guaranteed to contain a value when set returns, though not necessarily the one provided. + /// guaranteed to contain a value when `set` returns, though not necessarily the one provided. /// - /// Returns `Ok(())` if the cell's value was set by this call. + /// Returns `Ok(())` if the cell was uninitialized and + /// `Err(value)` if the cell was already initialized. /// /// # Examples /// @@ -230,13 +233,15 @@ impl OnceLock { } } - /// Sets the contents of this cell to `value` if the cell was empty, then - /// returns a reference to it. + /// Initializes the contents of the cell to `value` if the cell was uninitialized, + /// then returns a reference to it. /// /// May block if another thread is currently attempting to initialize the cell. The cell is - /// guaranteed to contain a value when set returns, though not necessarily the one provided. + /// guaranteed to contain a value when `try_insert` returns, though not necessarily the + /// one provided. /// - /// Returns `Ok(&value)` if the cell was empty and `Err(¤t_value, value)` if it was full. + /// Returns `Ok(&value)` if the cell was uninitialized and + /// `Err((¤t_value, value))` if it was already initialized. /// /// # Examples /// @@ -269,8 +274,8 @@ impl OnceLock { } } - /// Gets the contents of the cell, initializing it with `f` if the cell - /// was empty. + /// Gets the contents of the cell, initializing it to `f()` if the cell + /// was uninitialized. /// /// Many threads may call `get_or_init` concurrently with different /// initializing functions, but it is guaranteed that only one function @@ -278,7 +283,7 @@ impl OnceLock { /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// It is an error to reentrantly initialize the cell from `f`. The @@ -308,13 +313,13 @@ impl OnceLock { } /// Gets the mutable reference of the contents of the cell, initializing - /// it with `f` if the cell was empty. + /// it to `f()` if the cell was uninitialized. /// /// This method never blocks. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and the cell + /// If `f()` panics, the panic is propagated to the caller, and the cell /// remains uninitialized. /// /// # Examples @@ -345,13 +350,13 @@ impl OnceLock { } } - /// Gets the contents of the cell, initializing it with `f` if - /// the cell was empty. If the cell was empty and `f` failed, an - /// error is returned. + /// Gets the contents of the cell, initializing it to `f()` if + /// the cell was uninitialized. If the cell was uninitialized + /// and `f()` failed, an error is returned. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and + /// If `f()` panics, the panic is propagated to the caller, and /// the cell remains uninitialized. /// /// It is an error to reentrantly initialize the cell from `f`. @@ -397,14 +402,14 @@ impl OnceLock { } /// Gets the mutable reference of the contents of the cell, initializing - /// it with `f` if the cell was empty. If the cell was empty and `f` failed, - /// an error is returned. + /// it to `f()` if the cell was uninitialized. If the cell was uninitialized + /// and `f()` failed, an error is returned. /// /// This method never blocks. /// /// # Panics /// - /// If `f` panics, the panic is propagated to the caller, and + /// If `f()` panics, the panic is propagated to the caller, and /// the cell remains uninitialized. /// /// # Examples @@ -416,7 +421,7 @@ impl OnceLock { /// /// let mut cell: OnceLock = OnceLock::new(); /// - /// // Failed initializers do not change the value + /// // Failed attempts to initialize the cell do not change its contents /// assert!(cell.get_mut_or_try_init(|| "not a number!".parse()).is_err()); /// assert!(cell.get().is_none()); /// @@ -440,7 +445,7 @@ impl OnceLock { } /// Consumes the `OnceLock`, returning the wrapped value. Returns - /// `None` if the cell was empty. + /// `None` if the cell was uninitialized. /// /// # Examples /// @@ -462,7 +467,7 @@ impl OnceLock { /// Takes the value out of this `OnceLock`, moving it back to an uninitialized state. /// - /// Has no effect and returns `None` if the `OnceLock` hasn't been initialized. + /// Has no effect and returns `None` if the `OnceLock` was uninitialized. /// /// Safety is guaranteed by requiring a mutable reference. /// @@ -528,7 +533,7 @@ impl OnceLock { /// # Safety /// - /// The value must be initialized + /// The cell must be initialized #[inline] unsafe fn get_unchecked(&self) -> &T { debug_assert!(self.is_initialized()); @@ -537,7 +542,7 @@ impl OnceLock { /// # Safety /// - /// The value must be initialized + /// The cell must be initialized #[inline] unsafe fn get_unchecked_mut(&mut self) -> &mut T { debug_assert!(self.is_initialized()); @@ -562,7 +567,7 @@ impl UnwindSafe for OnceLock {} #[stable(feature = "once_cell", since = "1.70.0")] impl Default for OnceLock { - /// Creates a new empty cell. + /// Creates a new uninitialized cell. /// /// # Example /// @@ -676,6 +681,3 @@ unsafe impl<#[may_dangle] T> Drop for OnceLock { } } } - -#[cfg(test)] -mod tests; diff --git a/library/std/src/sync/once_lock/tests.rs b/library/std/src/sync/once_lock/tests.rs deleted file mode 100644 index 5113d436c3c99..0000000000000 --- a/library/std/src/sync/once_lock/tests.rs +++ /dev/null @@ -1,200 +0,0 @@ -use crate::sync::OnceLock; -use crate::sync::atomic::AtomicUsize; -use crate::sync::atomic::Ordering::SeqCst; -use crate::sync::mpsc::channel; -use crate::{panic, thread}; - -fn spawn_and_wait(f: impl FnOnce() -> R + Send + 'static) -> R { - thread::spawn(f).join().unwrap() -} - -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads -fn sync_once_cell() { - static ONCE_CELL: OnceLock = OnceLock::new(); - - assert!(ONCE_CELL.get().is_none()); - - spawn_and_wait(|| { - ONCE_CELL.get_or_init(|| 92); - assert_eq!(ONCE_CELL.get(), Some(&92)); - }); - - ONCE_CELL.get_or_init(|| panic!("Kaboom!")); - assert_eq!(ONCE_CELL.get(), Some(&92)); -} - -#[test] -fn sync_once_cell_get_mut() { - let mut c = OnceLock::new(); - assert!(c.get_mut().is_none()); - c.set(90).unwrap(); - *c.get_mut().unwrap() += 2; - assert_eq!(c.get_mut(), Some(&mut 92)); -} - -#[test] -fn sync_once_cell_get_unchecked() { - let c = OnceLock::new(); - c.set(92).unwrap(); - unsafe { - assert_eq!(c.get_unchecked(), &92); - } -} - -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads -fn sync_once_cell_drop() { - static DROP_CNT: AtomicUsize = AtomicUsize::new(0); - struct Dropper; - impl Drop for Dropper { - fn drop(&mut self) { - DROP_CNT.fetch_add(1, SeqCst); - } - } - - let x = OnceLock::new(); - spawn_and_wait(move || { - x.get_or_init(|| Dropper); - assert_eq!(DROP_CNT.load(SeqCst), 0); - drop(x); - }); - - assert_eq!(DROP_CNT.load(SeqCst), 1); -} - -#[test] -fn sync_once_cell_drop_empty() { - let x = OnceLock::::new(); - drop(x); -} - -#[test] -fn clone() { - let s = OnceLock::new(); - let c = s.clone(); - assert!(c.get().is_none()); - - s.set("hello".to_string()).unwrap(); - let c = s.clone(); - assert_eq!(c.get().map(String::as_str), Some("hello")); -} - -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn get_or_try_init() { - let cell: OnceLock = OnceLock::new(); - assert!(cell.get().is_none()); - - let res = panic::catch_unwind(|| cell.get_or_try_init(|| -> Result<_, ()> { panic!() })); - assert!(res.is_err()); - assert!(!cell.is_initialized()); - assert!(cell.get().is_none()); - - assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); - - assert_eq!(cell.get_or_try_init(|| Ok::<_, ()>("hello".to_string())), Ok(&"hello".to_string())); - assert_eq!(cell.get(), Some(&"hello".to_string())); -} - -#[test] -fn from_impl() { - assert_eq!(OnceLock::from("value").get(), Some(&"value")); - assert_ne!(OnceLock::from("foo").get(), Some(&"bar")); -} - -#[test] -fn partialeq_impl() { - assert!(OnceLock::from("value") == OnceLock::from("value")); - assert!(OnceLock::from("foo") != OnceLock::from("bar")); - - assert!(OnceLock::::new() == OnceLock::new()); - assert!(OnceLock::::new() != OnceLock::from("value".to_owned())); -} - -#[test] -fn into_inner() { - let cell: OnceLock = OnceLock::new(); - assert_eq!(cell.into_inner(), None); - let cell = OnceLock::new(); - cell.set("hello".to_string()).unwrap(); - assert_eq!(cell.into_inner(), Some("hello".to_string())); -} - -#[test] -fn is_sync_send() { - fn assert_traits() {} - assert_traits::>(); -} - -#[test] -fn eval_once_macro() { - macro_rules! eval_once { - (|| -> $ty:ty { - $($body:tt)* - }) => {{ - static ONCE_CELL: OnceLock<$ty> = OnceLock::new(); - fn init() -> $ty { - $($body)* - } - ONCE_CELL.get_or_init(init) - }}; - } - - let fib: &'static Vec = eval_once! { - || -> Vec { - let mut res = vec![1, 1]; - for i in 0..10 { - let next = res[i] + res[i + 1]; - res.push(next); - } - res - } - }; - assert_eq!(fib[5], 8) -} - -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads -fn sync_once_cell_does_not_leak_partially_constructed_boxes() { - static ONCE_CELL: OnceLock = OnceLock::new(); - - let n_readers = 10; - let n_writers = 3; - const MSG: &str = "Hello, World"; - - let (tx, rx) = channel(); - - for _ in 0..n_readers { - let tx = tx.clone(); - thread::spawn(move || { - loop { - if let Some(msg) = ONCE_CELL.get() { - tx.send(msg).unwrap(); - break; - } - #[cfg(target_env = "sgx")] - crate::thread::yield_now(); - } - }); - } - for _ in 0..n_writers { - thread::spawn(move || { - let _ = ONCE_CELL.set(MSG.to_owned()); - }); - } - - for _ in 0..n_readers { - let msg = rx.recv().unwrap(); - assert_eq!(msg, MSG); - } -} - -#[test] -fn dropck() { - let cell = OnceLock::new(); - { - let s = String::new(); - cell.set(&s).unwrap(); - } -} diff --git a/library/std/src/sync/poison/condvar.rs b/library/std/src/sync/poison/condvar.rs index a6e2389c93baf..7f0f3f652bcb7 100644 --- a/library/std/src/sync/poison/condvar.rs +++ b/library/std/src/sync/poison/condvar.rs @@ -1,6 +1,3 @@ -#[cfg(test)] -mod tests; - use crate::fmt; use crate::sync::poison::{self, LockResult, MutexGuard, PoisonError, mutex}; use crate::sys::sync as sys; diff --git a/library/std/src/sync/poison/condvar/tests.rs b/library/std/src/sync/poison/condvar/tests.rs deleted file mode 100644 index f9e9066bc92a2..0000000000000 --- a/library/std/src/sync/poison/condvar/tests.rs +++ /dev/null @@ -1,190 +0,0 @@ -use crate::sync::atomic::{AtomicBool, Ordering}; -use crate::sync::mpsc::channel; -use crate::sync::{Arc, Condvar, Mutex}; -use crate::thread; -use crate::time::Duration; - -#[test] -fn smoke() { - let c = Condvar::new(); - c.notify_one(); - c.notify_all(); -} - -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads -fn notify_one() { - let m = Arc::new(Mutex::new(())); - let m2 = m.clone(); - let c = Arc::new(Condvar::new()); - let c2 = c.clone(); - - let g = m.lock().unwrap(); - let _t = thread::spawn(move || { - let _g = m2.lock().unwrap(); - c2.notify_one(); - }); - let g = c.wait(g).unwrap(); - drop(g); -} - -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads -fn notify_all() { - const N: usize = 10; - - let data = Arc::new((Mutex::new(0), Condvar::new())); - let (tx, rx) = channel(); - for _ in 0..N { - let data = data.clone(); - let tx = tx.clone(); - thread::spawn(move || { - let &(ref lock, ref cond) = &*data; - let mut cnt = lock.lock().unwrap(); - *cnt += 1; - if *cnt == N { - tx.send(()).unwrap(); - } - while *cnt != 0 { - cnt = cond.wait(cnt).unwrap(); - } - tx.send(()).unwrap(); - }); - } - drop(tx); - - let &(ref lock, ref cond) = &*data; - rx.recv().unwrap(); - let mut cnt = lock.lock().unwrap(); - *cnt = 0; - cond.notify_all(); - drop(cnt); - - for _ in 0..N { - rx.recv().unwrap(); - } -} - -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads -fn wait_while() { - let pair = Arc::new((Mutex::new(false), Condvar::new())); - let pair2 = pair.clone(); - - // Inside of our lock, spawn a new thread, and then wait for it to start. - thread::spawn(move || { - let &(ref lock, ref cvar) = &*pair2; - let mut started = lock.lock().unwrap(); - *started = true; - // We notify the condvar that the value has changed. - cvar.notify_one(); - }); - - // Wait for the thread to start up. - let &(ref lock, ref cvar) = &*pair; - let guard = cvar.wait_while(lock.lock().unwrap(), |started| !*started); - assert!(*guard.unwrap()); -} - -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // condvar wait not supported -fn wait_timeout_wait() { - let m = Arc::new(Mutex::new(())); - let c = Arc::new(Condvar::new()); - - loop { - let g = m.lock().unwrap(); - let (_g, no_timeout) = c.wait_timeout(g, Duration::from_millis(1)).unwrap(); - // spurious wakeups mean this isn't necessarily true - // so execute test again, if not timeout - if !no_timeout.timed_out() { - continue; - } - - break; - } -} - -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // condvar wait not supported -fn wait_timeout_while_wait() { - let m = Arc::new(Mutex::new(())); - let c = Arc::new(Condvar::new()); - - let g = m.lock().unwrap(); - let (_g, wait) = c.wait_timeout_while(g, Duration::from_millis(1), |_| true).unwrap(); - // no spurious wakeups. ensure it timed-out - assert!(wait.timed_out()); -} - -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // condvar wait not supported -fn wait_timeout_while_instant_satisfy() { - let m = Arc::new(Mutex::new(())); - let c = Arc::new(Condvar::new()); - - let g = m.lock().unwrap(); - let (_g, wait) = c.wait_timeout_while(g, Duration::from_millis(0), |_| false).unwrap(); - // ensure it didn't time-out even if we were not given any time. - assert!(!wait.timed_out()); -} - -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads -fn wait_timeout_while_wake() { - let pair = Arc::new((Mutex::new(false), Condvar::new())); - let pair_copy = pair.clone(); - - let &(ref m, ref c) = &*pair; - let g = m.lock().unwrap(); - let _t = thread::spawn(move || { - let &(ref lock, ref cvar) = &*pair_copy; - let mut started = lock.lock().unwrap(); - thread::sleep(Duration::from_millis(1)); - *started = true; - cvar.notify_one(); - }); - let (g2, wait) = c - .wait_timeout_while(g, Duration::from_millis(u64::MAX), |&mut notified| !notified) - .unwrap(); - // ensure it didn't time-out even if we were not given any time. - assert!(!wait.timed_out()); - assert!(*g2); -} - -#[test] -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads -fn wait_timeout_wake() { - let m = Arc::new(Mutex::new(())); - let c = Arc::new(Condvar::new()); - - loop { - let g = m.lock().unwrap(); - - let c2 = c.clone(); - let m2 = m.clone(); - - let notified = Arc::new(AtomicBool::new(false)); - let notified_copy = notified.clone(); - - let t = thread::spawn(move || { - let _g = m2.lock().unwrap(); - thread::sleep(Duration::from_millis(1)); - notified_copy.store(true, Ordering::Relaxed); - c2.notify_one(); - }); - let (g, timeout_res) = c.wait_timeout(g, Duration::from_millis(u64::MAX)).unwrap(); - assert!(!timeout_res.timed_out()); - // spurious wakeups mean this isn't necessarily true - // so execute test again, if not notified - if !notified.load(Ordering::Relaxed) { - t.join().unwrap(); - continue; - } - drop(g); - - t.join().unwrap(); - - break; - } -} diff --git a/library/std/src/sync/poison/mutex.rs b/library/std/src/sync/poison/mutex.rs index e28c2090afed7..9362c764173a8 100644 --- a/library/std/src/sync/poison/mutex.rs +++ b/library/std/src/sync/poison/mutex.rs @@ -1,6 +1,3 @@ -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - use crate::cell::UnsafeCell; use crate::fmt; use crate::marker::PhantomData; @@ -181,10 +178,29 @@ pub struct Mutex { data: UnsafeCell, } -// these are the only places where `T: Send` matters; all other -// functionality works fine on a single thread. +/// `T` must be `Send` for a [`Mutex`] to be `Send` because it is possible to acquire +/// the owned `T` from the `Mutex` via [`into_inner`]. +/// +/// [`into_inner`]: Mutex::into_inner #[stable(feature = "rust1", since = "1.0.0")] unsafe impl Send for Mutex {} + +/// `T` must be `Send` for [`Mutex`] to be `Sync`. +/// This ensures that the protected data can be accessed safely from multiple threads +/// without causing data races or other unsafe behavior. +/// +/// [`Mutex`] provides mutable access to `T` to one thread at a time. However, it's essential +/// for `T` to be `Send` because it's not safe for non-`Send` structures to be accessed in +/// this manner. For instance, consider [`Rc`], a non-atomic reference counted smart pointer, +/// which is not `Send`. With `Rc`, we can have multiple copies pointing to the same heap +/// allocation with a non-atomic reference count. If we were to use `Mutex>`, it would +/// only protect one instance of `Rc` from shared access, leaving other copies vulnerable +/// to potential data races. +/// +/// Also note that it is not necessary for `T` to be `Sync` as `&T` is only made available +/// to one thread at a time if `T` is not `Sync`. +/// +/// [`Rc`]: crate::rc::Rc #[stable(feature = "rust1", since = "1.0.0")] unsafe impl Sync for Mutex {} @@ -211,8 +227,17 @@ pub struct MutexGuard<'a, T: ?Sized + 'a> { poison: poison::Guard, } +/// A [`MutexGuard`] is not `Send` to maximize platform portablity. +/// +/// On platforms that use POSIX threads (commonly referred to as pthreads) there is a requirement to +/// release mutex locks on the same thread they were acquired. +/// For this reason, [`MutexGuard`] must not implement `Send` to prevent it being dropped from +/// another thread. #[stable(feature = "rust1", since = "1.0.0")] impl !Send for MutexGuard<'_, T> {} + +/// `T` must be `Sync` for a [`MutexGuard`] to be `Sync` +/// because it is possible to get a `&T` from `&MutexGuard` (via `Deref`). #[stable(feature = "mutexguard", since = "1.19.0")] unsafe impl Sync for MutexGuard<'_, T> {} @@ -534,7 +559,7 @@ impl Mutex { /// # Errors /// /// If another user of this mutex panicked while holding the mutex, then - /// this call will return an error containing the the underlying data + /// this call will return an error containing the underlying data /// instead. /// /// # Examples diff --git a/library/std/src/sync/poison/mutex/tests.rs b/library/std/src/sync/poison/mutex/tests.rs deleted file mode 100644 index 395c8aada089a..0000000000000 --- a/library/std/src/sync/poison/mutex/tests.rs +++ /dev/null @@ -1,442 +0,0 @@ -use crate::fmt::Debug; -use crate::ops::FnMut; -use crate::panic::{self, AssertUnwindSafe}; -use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::sync::mpsc::channel; -use crate::sync::{Arc, Condvar, MappedMutexGuard, Mutex, MutexGuard, TryLockError}; -use crate::{hint, mem, thread}; - -struct Packet(Arc<(Mutex, Condvar)>); - -#[derive(Eq, PartialEq, Debug)] -struct NonCopy(i32); - -#[derive(Eq, PartialEq, Debug)] -struct NonCopyNeedsDrop(i32); - -impl Drop for NonCopyNeedsDrop { - fn drop(&mut self) { - hint::black_box(()); - } -} - -#[test] -fn test_needs_drop() { - assert!(!mem::needs_drop::()); - assert!(mem::needs_drop::()); -} - -#[derive(Clone, Eq, PartialEq, Debug)] -struct Cloneable(i32); - -#[test] -fn smoke() { - let m = Mutex::new(()); - drop(m.lock().unwrap()); - drop(m.lock().unwrap()); -} - -#[test] -fn lots_and_lots() { - const J: u32 = 1000; - const K: u32 = 3; - - let m = Arc::new(Mutex::new(0)); - - fn inc(m: &Mutex) { - for _ in 0..J { - *m.lock().unwrap() += 1; - } - } - - let (tx, rx) = channel(); - for _ in 0..K { - let tx2 = tx.clone(); - let m2 = m.clone(); - thread::spawn(move || { - inc(&m2); - tx2.send(()).unwrap(); - }); - let tx2 = tx.clone(); - let m2 = m.clone(); - thread::spawn(move || { - inc(&m2); - tx2.send(()).unwrap(); - }); - } - - drop(tx); - for _ in 0..2 * K { - rx.recv().unwrap(); - } - assert_eq!(*m.lock().unwrap(), J * K * 2); -} - -#[test] -fn try_lock() { - let m = Mutex::new(()); - *m.try_lock().unwrap() = (); -} - -fn new_poisoned_mutex(value: T) -> Mutex { - let mutex = Mutex::new(value); - - let catch_unwind_result = panic::catch_unwind(AssertUnwindSafe(|| { - let _guard = mutex.lock().unwrap(); - - panic!("test panic to poison mutex"); - })); - - assert!(catch_unwind_result.is_err()); - assert!(mutex.is_poisoned()); - - mutex -} - -#[test] -fn test_into_inner() { - let m = Mutex::new(NonCopy(10)); - assert_eq!(m.into_inner().unwrap(), NonCopy(10)); -} - -#[test] -fn test_into_inner_drop() { - struct Foo(Arc); - impl Drop for Foo { - fn drop(&mut self) { - self.0.fetch_add(1, Ordering::SeqCst); - } - } - let num_drops = Arc::new(AtomicUsize::new(0)); - let m = Mutex::new(Foo(num_drops.clone())); - assert_eq!(num_drops.load(Ordering::SeqCst), 0); - { - let _inner = m.into_inner().unwrap(); - assert_eq!(num_drops.load(Ordering::SeqCst), 0); - } - assert_eq!(num_drops.load(Ordering::SeqCst), 1); -} - -#[test] -fn test_into_inner_poison() { - let m = new_poisoned_mutex(NonCopy(10)); - - match m.into_inner() { - Err(e) => assert_eq!(e.into_inner(), NonCopy(10)), - Ok(x) => panic!("into_inner of poisoned Mutex is Ok: {x:?}"), - } -} - -#[test] -fn test_get_cloned() { - let m = Mutex::new(Cloneable(10)); - - assert_eq!(m.get_cloned().unwrap(), Cloneable(10)); -} - -#[test] -fn test_get_cloned_poison() { - let m = new_poisoned_mutex(Cloneable(10)); - - match m.get_cloned() { - Err(e) => assert_eq!(e.into_inner(), ()), - Ok(x) => panic!("get of poisoned Mutex is Ok: {x:?}"), - } -} - -#[test] -fn test_get_mut() { - let mut m = Mutex::new(NonCopy(10)); - *m.get_mut().unwrap() = NonCopy(20); - assert_eq!(m.into_inner().unwrap(), NonCopy(20)); -} - -#[test] -fn test_get_mut_poison() { - let mut m = new_poisoned_mutex(NonCopy(10)); - - match m.get_mut() { - Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)), - Ok(x) => panic!("get_mut of poisoned Mutex is Ok: {x:?}"), - } -} - -#[test] -fn test_set() { - fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) - where - T: Debug + Eq, - { - let m = Mutex::new(init()); - - assert_eq!(*m.lock().unwrap(), init()); - m.set(value()).unwrap(); - assert_eq!(*m.lock().unwrap(), value()); - } - - inner(|| NonCopy(10), || NonCopy(20)); - inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); -} - -#[test] -fn test_set_poison() { - fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) - where - T: Debug + Eq, - { - let m = new_poisoned_mutex(init()); - - match m.set(value()) { - Err(e) => { - assert_eq!(e.into_inner(), value()); - assert_eq!(m.into_inner().unwrap_err().into_inner(), init()); - } - Ok(x) => panic!("set of poisoned Mutex is Ok: {x:?}"), - } - } - - inner(|| NonCopy(10), || NonCopy(20)); - inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); -} - -#[test] -fn test_replace() { - fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) - where - T: Debug + Eq, - { - let m = Mutex::new(init()); - - assert_eq!(*m.lock().unwrap(), init()); - assert_eq!(m.replace(value()).unwrap(), init()); - assert_eq!(*m.lock().unwrap(), value()); - } - - inner(|| NonCopy(10), || NonCopy(20)); - inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); -} - -#[test] -fn test_replace_poison() { - fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) - where - T: Debug + Eq, - { - let m = new_poisoned_mutex(init()); - - match m.replace(value()) { - Err(e) => { - assert_eq!(e.into_inner(), value()); - assert_eq!(m.into_inner().unwrap_err().into_inner(), init()); - } - Ok(x) => panic!("replace of poisoned Mutex is Ok: {x:?}"), - } - } - - inner(|| NonCopy(10), || NonCopy(20)); - inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); -} - -#[test] -fn test_mutex_arc_condvar() { - let packet = Packet(Arc::new((Mutex::new(false), Condvar::new()))); - let packet2 = Packet(packet.0.clone()); - let (tx, rx) = channel(); - let _t = thread::spawn(move || { - // wait until parent gets in - rx.recv().unwrap(); - let &(ref lock, ref cvar) = &*packet2.0; - let mut lock = lock.lock().unwrap(); - *lock = true; - cvar.notify_one(); - }); - - let &(ref lock, ref cvar) = &*packet.0; - let mut lock = lock.lock().unwrap(); - tx.send(()).unwrap(); - assert!(!*lock); - while !*lock { - lock = cvar.wait(lock).unwrap(); - } -} - -#[test] -fn test_arc_condvar_poison() { - let packet = Packet(Arc::new((Mutex::new(1), Condvar::new()))); - let packet2 = Packet(packet.0.clone()); - let (tx, rx) = channel(); - - let _t = thread::spawn(move || -> () { - rx.recv().unwrap(); - let &(ref lock, ref cvar) = &*packet2.0; - let _g = lock.lock().unwrap(); - cvar.notify_one(); - // Parent should fail when it wakes up. - panic!(); - }); - - let &(ref lock, ref cvar) = &*packet.0; - let mut lock = lock.lock().unwrap(); - tx.send(()).unwrap(); - while *lock == 1 { - match cvar.wait(lock) { - Ok(l) => { - lock = l; - assert_eq!(*lock, 1); - } - Err(..) => break, - } - } -} - -#[test] -fn test_mutex_arc_poison() { - let arc = Arc::new(Mutex::new(1)); - assert!(!arc.is_poisoned()); - let arc2 = arc.clone(); - let _ = thread::spawn(move || { - let lock = arc2.lock().unwrap(); - assert_eq!(*lock, 2); // deliberate assertion failure to poison the mutex - }) - .join(); - assert!(arc.lock().is_err()); - assert!(arc.is_poisoned()); -} - -#[test] -fn test_mutex_arc_poison_mapped() { - let arc = Arc::new(Mutex::new(1)); - assert!(!arc.is_poisoned()); - let arc2 = arc.clone(); - let _ = thread::spawn(move || { - let lock = arc2.lock().unwrap(); - let lock = MutexGuard::map(lock, |val| val); - assert_eq!(*lock, 2); // deliberate assertion failure to poison the mutex - }) - .join(); - assert!(arc.lock().is_err()); - assert!(arc.is_poisoned()); -} - -#[test] -fn test_mutex_arc_nested() { - // Tests nested mutexes and access - // to underlying data. - let arc = Arc::new(Mutex::new(1)); - let arc2 = Arc::new(Mutex::new(arc)); - let (tx, rx) = channel(); - let _t = thread::spawn(move || { - let lock = arc2.lock().unwrap(); - let lock2 = lock.lock().unwrap(); - assert_eq!(*lock2, 1); - tx.send(()).unwrap(); - }); - rx.recv().unwrap(); -} - -#[test] -fn test_mutex_arc_access_in_unwind() { - let arc = Arc::new(Mutex::new(1)); - let arc2 = arc.clone(); - let _ = thread::spawn(move || -> () { - struct Unwinder { - i: Arc>, - } - impl Drop for Unwinder { - fn drop(&mut self) { - *self.i.lock().unwrap() += 1; - } - } - let _u = Unwinder { i: arc2 }; - panic!(); - }) - .join(); - let lock = arc.lock().unwrap(); - assert_eq!(*lock, 2); -} - -#[test] -fn test_mutex_unsized() { - let mutex: &Mutex<[i32]> = &Mutex::new([1, 2, 3]); - { - let b = &mut *mutex.lock().unwrap(); - b[0] = 4; - b[2] = 5; - } - let comp: &[i32] = &[4, 2, 5]; - assert_eq!(&*mutex.lock().unwrap(), comp); -} - -#[test] -fn test_mapping_mapped_guard() { - let arr = [0; 4]; - let mut lock = Mutex::new(arr); - let guard = lock.lock().unwrap(); - let guard = MutexGuard::map(guard, |arr| &mut arr[..2]); - let mut guard = MappedMutexGuard::map(guard, |slice| &mut slice[1..]); - assert_eq!(guard.len(), 1); - guard[0] = 42; - drop(guard); - assert_eq!(*lock.get_mut().unwrap(), [0, 42, 0, 0]); -} - -#[test] -fn panic_while_mapping_unlocked_poison() { - let lock = Mutex::new(()); - - let _ = panic::catch_unwind(|| { - let guard = lock.lock().unwrap(); - let _guard = MutexGuard::map::<(), _>(guard, |_| panic!()); - }); - - match lock.try_lock() { - Ok(_) => panic!("panicking in a MutexGuard::map closure should poison the Mutex"), - Err(TryLockError::WouldBlock) => { - panic!("panicking in a MutexGuard::map closure should unlock the mutex") - } - Err(TryLockError::Poisoned(_)) => {} - } - - let _ = panic::catch_unwind(|| { - let guard = lock.lock().unwrap(); - let _guard = MutexGuard::try_map::<(), _>(guard, |_| panic!()); - }); - - match lock.try_lock() { - Ok(_) => panic!("panicking in a MutexGuard::try_map closure should poison the Mutex"), - Err(TryLockError::WouldBlock) => { - panic!("panicking in a MutexGuard::try_map closure should unlock the mutex") - } - Err(TryLockError::Poisoned(_)) => {} - } - - let _ = panic::catch_unwind(|| { - let guard = lock.lock().unwrap(); - let guard = MutexGuard::map::<(), _>(guard, |val| val); - let _guard = MappedMutexGuard::map::<(), _>(guard, |_| panic!()); - }); - - match lock.try_lock() { - Ok(_) => panic!("panicking in a MappedMutexGuard::map closure should poison the Mutex"), - Err(TryLockError::WouldBlock) => { - panic!("panicking in a MappedMutexGuard::map closure should unlock the mutex") - } - Err(TryLockError::Poisoned(_)) => {} - } - - let _ = panic::catch_unwind(|| { - let guard = lock.lock().unwrap(); - let guard = MutexGuard::map::<(), _>(guard, |val| val); - let _guard = MappedMutexGuard::try_map::<(), _>(guard, |_| panic!()); - }); - - match lock.try_lock() { - Ok(_) => panic!("panicking in a MappedMutexGuard::try_map closure should poison the Mutex"), - Err(TryLockError::WouldBlock) => { - panic!("panicking in a MappedMutexGuard::try_map closure should unlock the mutex") - } - Err(TryLockError::Poisoned(_)) => {} - } - - drop(lock); -} diff --git a/library/std/src/sync/poison/once.rs b/library/std/src/sync/poison/once.rs index 27db4b634fb28..103e519540795 100644 --- a/library/std/src/sync/poison/once.rs +++ b/library/std/src/sync/poison/once.rs @@ -3,9 +3,6 @@ //! This primitive is meant to be used to run one-time initialization. An //! example use case would be for initializing an FFI library. -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - use crate::fmt; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::sys::sync as sys; @@ -269,8 +266,6 @@ impl Once { /// # Example /// /// ```rust - /// #![feature(once_wait)] - /// /// use std::sync::Once; /// use std::thread; /// @@ -289,7 +284,7 @@ impl Once { /// If this [`Once`] has been poisoned because an initialization closure has /// panicked, this method will also panic. Use [`wait_force`](Self::wait_force) /// if this behavior is not desired. - #[unstable(feature = "once_wait", issue = "127527")] + #[stable(feature = "once_wait", since = "1.86.0")] pub fn wait(&self) { if !self.inner.is_completed() { self.inner.wait(false); @@ -298,7 +293,7 @@ impl Once { /// Blocks the current thread until initialization has completed, ignoring /// poisoning. - #[unstable(feature = "once_wait", issue = "127527")] + #[stable(feature = "once_wait", since = "1.86.0")] pub fn wait_force(&self) { if !self.inner.is_completed() { self.inner.wait(true); diff --git a/library/std/src/sync/poison/once/tests.rs b/library/std/src/sync/poison/once/tests.rs deleted file mode 100644 index ce96468aeb6e1..0000000000000 --- a/library/std/src/sync/poison/once/tests.rs +++ /dev/null @@ -1,162 +0,0 @@ -use super::Once; -use crate::sync::atomic::AtomicBool; -use crate::sync::atomic::Ordering::Relaxed; -use crate::sync::mpsc::channel; -use crate::time::Duration; -use crate::{panic, thread}; - -#[test] -fn smoke_once() { - static O: Once = Once::new(); - let mut a = 0; - O.call_once(|| a += 1); - assert_eq!(a, 1); - O.call_once(|| a += 1); - assert_eq!(a, 1); -} - -#[test] -fn stampede_once() { - static O: Once = Once::new(); - static mut RUN: bool = false; - - let (tx, rx) = channel(); - for _ in 0..10 { - let tx = tx.clone(); - thread::spawn(move || { - for _ in 0..4 { - thread::yield_now() - } - unsafe { - O.call_once(|| { - assert!(!RUN); - RUN = true; - }); - assert!(RUN); - } - tx.send(()).unwrap(); - }); - } - - unsafe { - O.call_once(|| { - assert!(!RUN); - RUN = true; - }); - assert!(RUN); - } - - for _ in 0..10 { - rx.recv().unwrap(); - } -} - -#[test] -fn poison_bad() { - static O: Once = Once::new(); - - // poison the once - let t = panic::catch_unwind(|| { - O.call_once(|| panic!()); - }); - assert!(t.is_err()); - - // poisoning propagates - let t = panic::catch_unwind(|| { - O.call_once(|| {}); - }); - assert!(t.is_err()); - - // we can subvert poisoning, however - let mut called = false; - O.call_once_force(|p| { - called = true; - assert!(p.is_poisoned()) - }); - assert!(called); - - // once any success happens, we stop propagating the poison - O.call_once(|| {}); -} - -#[test] -fn wait_for_force_to_finish() { - static O: Once = Once::new(); - - // poison the once - let t = panic::catch_unwind(|| { - O.call_once(|| panic!()); - }); - assert!(t.is_err()); - - // make sure someone's waiting inside the once via a force - let (tx1, rx1) = channel(); - let (tx2, rx2) = channel(); - let t1 = thread::spawn(move || { - O.call_once_force(|p| { - assert!(p.is_poisoned()); - tx1.send(()).unwrap(); - rx2.recv().unwrap(); - }); - }); - - rx1.recv().unwrap(); - - // put another waiter on the once - let t2 = thread::spawn(|| { - let mut called = false; - O.call_once(|| { - called = true; - }); - assert!(!called); - }); - - tx2.send(()).unwrap(); - - assert!(t1.join().is_ok()); - assert!(t2.join().is_ok()); -} - -#[test] -fn wait() { - for _ in 0..50 { - let val = AtomicBool::new(false); - let once = Once::new(); - - thread::scope(|s| { - for _ in 0..4 { - s.spawn(|| { - once.wait(); - assert!(val.load(Relaxed)); - }); - } - - once.call_once(|| val.store(true, Relaxed)); - }); - } -} - -#[test] -fn wait_on_poisoned() { - let once = Once::new(); - - panic::catch_unwind(|| once.call_once(|| panic!())).unwrap_err(); - panic::catch_unwind(|| once.wait()).unwrap_err(); -} - -#[test] -fn wait_force_on_poisoned() { - let once = Once::new(); - - thread::scope(|s| { - panic::catch_unwind(|| once.call_once(|| panic!())).unwrap_err(); - - s.spawn(|| { - thread::sleep(Duration::from_millis(100)); - - once.call_once_force(|_| {}); - }); - - once.wait_force(); - }) -} diff --git a/library/std/src/sync/poison/rwlock.rs b/library/std/src/sync/poison/rwlock.rs index 1519baf99a8fd..f9d9321f5f2d8 100644 --- a/library/std/src/sync/poison/rwlock.rs +++ b/library/std/src/sync/poison/rwlock.rs @@ -1,6 +1,3 @@ -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - use crate::cell::UnsafeCell; use crate::fmt; use crate::marker::PhantomData; diff --git a/library/std/src/sync/poison/rwlock/tests.rs b/library/std/src/sync/poison/rwlock/tests.rs deleted file mode 100644 index 057c2f1a5d7a7..0000000000000 --- a/library/std/src/sync/poison/rwlock/tests.rs +++ /dev/null @@ -1,729 +0,0 @@ -use rand::Rng; - -use crate::fmt::Debug; -use crate::ops::FnMut; -use crate::panic::{self, AssertUnwindSafe}; -use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::sync::mpsc::channel; -use crate::sync::{ - Arc, MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard, RwLockWriteGuard, - TryLockError, -}; -use crate::{hint, mem, thread}; - -#[derive(Eq, PartialEq, Debug)] -struct NonCopy(i32); - -#[derive(Eq, PartialEq, Debug)] -struct NonCopyNeedsDrop(i32); - -impl Drop for NonCopyNeedsDrop { - fn drop(&mut self) { - hint::black_box(()); - } -} - -#[test] -fn test_needs_drop() { - assert!(!mem::needs_drop::()); - assert!(mem::needs_drop::()); -} - -#[derive(Clone, Eq, PartialEq, Debug)] -struct Cloneable(i32); - -#[test] -fn smoke() { - let l = RwLock::new(()); - drop(l.read().unwrap()); - drop(l.write().unwrap()); - drop((l.read().unwrap(), l.read().unwrap())); - drop(l.write().unwrap()); -} - -#[test] -// FIXME: On macOS we use a provenance-incorrect implementation and Miri -// catches that issue with a chance of around 1/1000. -// See for details. -#[cfg_attr(all(miri, target_os = "macos"), ignore)] -fn frob() { - const N: u32 = 10; - const M: usize = if cfg!(miri) { 100 } else { 1000 }; - - let r = Arc::new(RwLock::new(())); - - let (tx, rx) = channel::<()>(); - for _ in 0..N { - let tx = tx.clone(); - let r = r.clone(); - thread::spawn(move || { - let mut rng = crate::test_helpers::test_rng(); - for _ in 0..M { - if rng.gen_bool(1.0 / (N as f64)) { - drop(r.write().unwrap()); - } else { - drop(r.read().unwrap()); - } - } - drop(tx); - }); - } - drop(tx); - let _ = rx.recv(); -} - -#[test] -fn test_rw_arc_poison_wr() { - let arc = Arc::new(RwLock::new(1)); - let arc2 = arc.clone(); - let _: Result<(), _> = thread::spawn(move || { - let _lock = arc2.write().unwrap(); - panic!(); - }) - .join(); - assert!(arc.read().is_err()); -} - -#[test] -fn test_rw_arc_poison_mapped_w_r() { - let arc = Arc::new(RwLock::new(1)); - let arc2 = arc.clone(); - let _: Result<(), _> = thread::spawn(move || { - let lock = arc2.write().unwrap(); - let _lock = RwLockWriteGuard::map(lock, |val| val); - panic!(); - }) - .join(); - assert!(arc.read().is_err()); -} - -#[test] -fn test_rw_arc_poison_ww() { - let arc = Arc::new(RwLock::new(1)); - assert!(!arc.is_poisoned()); - let arc2 = arc.clone(); - let _: Result<(), _> = thread::spawn(move || { - let _lock = arc2.write().unwrap(); - panic!(); - }) - .join(); - assert!(arc.write().is_err()); - assert!(arc.is_poisoned()); -} - -#[test] -fn test_rw_arc_poison_mapped_w_w() { - let arc = Arc::new(RwLock::new(1)); - let arc2 = arc.clone(); - let _: Result<(), _> = thread::spawn(move || { - let lock = arc2.write().unwrap(); - let _lock = RwLockWriteGuard::map(lock, |val| val); - panic!(); - }) - .join(); - assert!(arc.write().is_err()); - assert!(arc.is_poisoned()); -} - -#[test] -fn test_rw_arc_no_poison_rr() { - let arc = Arc::new(RwLock::new(1)); - let arc2 = arc.clone(); - let _: Result<(), _> = thread::spawn(move || { - let _lock = arc2.read().unwrap(); - panic!(); - }) - .join(); - let lock = arc.read().unwrap(); - assert_eq!(*lock, 1); -} - -#[test] -fn test_rw_arc_no_poison_mapped_r_r() { - let arc = Arc::new(RwLock::new(1)); - let arc2 = arc.clone(); - let _: Result<(), _> = thread::spawn(move || { - let lock = arc2.read().unwrap(); - let _lock = RwLockReadGuard::map(lock, |val| val); - panic!(); - }) - .join(); - let lock = arc.read().unwrap(); - assert_eq!(*lock, 1); -} - -#[test] -fn test_rw_arc_no_poison_rw() { - let arc = Arc::new(RwLock::new(1)); - let arc2 = arc.clone(); - let _: Result<(), _> = thread::spawn(move || { - let _lock = arc2.read().unwrap(); - panic!() - }) - .join(); - let lock = arc.write().unwrap(); - assert_eq!(*lock, 1); -} - -#[test] -fn test_rw_arc_no_poison_mapped_r_w() { - let arc = Arc::new(RwLock::new(1)); - let arc2 = arc.clone(); - let _: Result<(), _> = thread::spawn(move || { - let lock = arc2.read().unwrap(); - let _lock = RwLockReadGuard::map(lock, |val| val); - panic!(); - }) - .join(); - let lock = arc.write().unwrap(); - assert_eq!(*lock, 1); -} - -#[test] -fn test_rw_arc() { - let arc = Arc::new(RwLock::new(0)); - let arc2 = arc.clone(); - let (tx, rx) = channel(); - - thread::spawn(move || { - let mut lock = arc2.write().unwrap(); - for _ in 0..10 { - let tmp = *lock; - *lock = -1; - thread::yield_now(); - *lock = tmp + 1; - } - tx.send(()).unwrap(); - }); - - // Readers try to catch the writer in the act - let mut children = Vec::new(); - for _ in 0..5 { - let arc3 = arc.clone(); - children.push(thread::spawn(move || { - let lock = arc3.read().unwrap(); - assert!(*lock >= 0); - })); - } - - // Wait for children to pass their asserts - for r in children { - assert!(r.join().is_ok()); - } - - // Wait for writer to finish - rx.recv().unwrap(); - let lock = arc.read().unwrap(); - assert_eq!(*lock, 10); -} - -#[test] -fn test_rw_arc_access_in_unwind() { - let arc = Arc::new(RwLock::new(1)); - let arc2 = arc.clone(); - let _ = thread::spawn(move || -> () { - struct Unwinder { - i: Arc>, - } - impl Drop for Unwinder { - fn drop(&mut self) { - let mut lock = self.i.write().unwrap(); - *lock += 1; - } - } - let _u = Unwinder { i: arc2 }; - panic!(); - }) - .join(); - let lock = arc.read().unwrap(); - assert_eq!(*lock, 2); -} - -#[test] -fn test_rwlock_unsized() { - let rw: &RwLock<[i32]> = &RwLock::new([1, 2, 3]); - { - let b = &mut *rw.write().unwrap(); - b[0] = 4; - b[2] = 5; - } - let comp: &[i32] = &[4, 2, 5]; - assert_eq!(&*rw.read().unwrap(), comp); -} - -#[test] -fn test_rwlock_try_write() { - let lock = RwLock::new(0isize); - let read_guard = lock.read().unwrap(); - - let write_result = lock.try_write(); - match write_result { - Err(TryLockError::WouldBlock) => (), - Ok(_) => assert!(false, "try_write should not succeed while read_guard is in scope"), - Err(_) => assert!(false, "unexpected error"), - } - - drop(read_guard); - let mapped_read_guard = RwLockReadGuard::map(lock.read().unwrap(), |_| &()); - - let write_result = lock.try_write(); - match write_result { - Err(TryLockError::WouldBlock) => (), - Ok(_) => assert!(false, "try_write should not succeed while mapped_read_guard is in scope"), - Err(_) => assert!(false, "unexpected error"), - } - - drop(mapped_read_guard); -} - -fn new_poisoned_rwlock(value: T) -> RwLock { - let lock = RwLock::new(value); - - let catch_unwind_result = panic::catch_unwind(AssertUnwindSafe(|| { - let _guard = lock.write().unwrap(); - - panic!("test panic to poison RwLock"); - })); - - assert!(catch_unwind_result.is_err()); - assert!(lock.is_poisoned()); - - lock -} - -#[test] -fn test_into_inner() { - let m = RwLock::new(NonCopy(10)); - assert_eq!(m.into_inner().unwrap(), NonCopy(10)); -} - -#[test] -fn test_into_inner_drop() { - struct Foo(Arc); - impl Drop for Foo { - fn drop(&mut self) { - self.0.fetch_add(1, Ordering::SeqCst); - } - } - let num_drops = Arc::new(AtomicUsize::new(0)); - let m = RwLock::new(Foo(num_drops.clone())); - assert_eq!(num_drops.load(Ordering::SeqCst), 0); - { - let _inner = m.into_inner().unwrap(); - assert_eq!(num_drops.load(Ordering::SeqCst), 0); - } - assert_eq!(num_drops.load(Ordering::SeqCst), 1); -} - -#[test] -fn test_into_inner_poison() { - let m = new_poisoned_rwlock(NonCopy(10)); - - match m.into_inner() { - Err(e) => assert_eq!(e.into_inner(), NonCopy(10)), - Ok(x) => panic!("into_inner of poisoned RwLock is Ok: {x:?}"), - } -} - -#[test] -fn test_get_cloned() { - let m = RwLock::new(Cloneable(10)); - - assert_eq!(m.get_cloned().unwrap(), Cloneable(10)); -} - -#[test] -fn test_get_cloned_poison() { - let m = new_poisoned_rwlock(Cloneable(10)); - - match m.get_cloned() { - Err(e) => assert_eq!(e.into_inner(), ()), - Ok(x) => panic!("get of poisoned RwLock is Ok: {x:?}"), - } -} - -#[test] -fn test_get_mut() { - let mut m = RwLock::new(NonCopy(10)); - *m.get_mut().unwrap() = NonCopy(20); - assert_eq!(m.into_inner().unwrap(), NonCopy(20)); -} - -#[test] -fn test_get_mut_poison() { - let mut m = new_poisoned_rwlock(NonCopy(10)); - - match m.get_mut() { - Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)), - Ok(x) => panic!("get_mut of poisoned RwLock is Ok: {x:?}"), - } -} - -#[test] -fn test_set() { - fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) - where - T: Debug + Eq, - { - let m = RwLock::new(init()); - - assert_eq!(*m.read().unwrap(), init()); - m.set(value()).unwrap(); - assert_eq!(*m.read().unwrap(), value()); - } - - inner(|| NonCopy(10), || NonCopy(20)); - inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); -} - -#[test] -fn test_set_poison() { - fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) - where - T: Debug + Eq, - { - let m = new_poisoned_rwlock(init()); - - match m.set(value()) { - Err(e) => { - assert_eq!(e.into_inner(), value()); - assert_eq!(m.into_inner().unwrap_err().into_inner(), init()); - } - Ok(x) => panic!("set of poisoned RwLock is Ok: {x:?}"), - } - } - - inner(|| NonCopy(10), || NonCopy(20)); - inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); -} - -#[test] -fn test_replace() { - fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) - where - T: Debug + Eq, - { - let m = RwLock::new(init()); - - assert_eq!(*m.read().unwrap(), init()); - assert_eq!(m.replace(value()).unwrap(), init()); - assert_eq!(*m.read().unwrap(), value()); - } - - inner(|| NonCopy(10), || NonCopy(20)); - inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); -} - -#[test] -fn test_replace_poison() { - fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) - where - T: Debug + Eq, - { - let m = new_poisoned_rwlock(init()); - - match m.replace(value()) { - Err(e) => { - assert_eq!(e.into_inner(), value()); - assert_eq!(m.into_inner().unwrap_err().into_inner(), init()); - } - Ok(x) => panic!("replace of poisoned RwLock is Ok: {x:?}"), - } - } - - inner(|| NonCopy(10), || NonCopy(20)); - inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); -} - -#[test] -fn test_read_guard_covariance() { - fn do_stuff<'a>(_: RwLockReadGuard<'_, &'a i32>, _: &'a i32) {} - let j: i32 = 5; - let lock = RwLock::new(&j); - { - let i = 6; - do_stuff(lock.read().unwrap(), &i); - } - drop(lock); -} - -#[test] -fn test_mapped_read_guard_covariance() { - fn do_stuff<'a>(_: MappedRwLockReadGuard<'_, &'a i32>, _: &'a i32) {} - let j: i32 = 5; - let lock = RwLock::new((&j, &j)); - { - let i = 6; - let guard = lock.read().unwrap(); - let guard = RwLockReadGuard::map(guard, |(val, _val)| val); - do_stuff(guard, &i); - } - drop(lock); -} - -#[test] -fn test_mapping_mapped_guard() { - let arr = [0; 4]; - let mut lock = RwLock::new(arr); - let guard = lock.write().unwrap(); - let guard = RwLockWriteGuard::map(guard, |arr| &mut arr[..2]); - let mut guard = MappedRwLockWriteGuard::map(guard, |slice| &mut slice[1..]); - assert_eq!(guard.len(), 1); - guard[0] = 42; - drop(guard); - assert_eq!(*lock.get_mut().unwrap(), [0, 42, 0, 0]); - - let guard = lock.read().unwrap(); - let guard = RwLockReadGuard::map(guard, |arr| &arr[..2]); - let guard = MappedRwLockReadGuard::map(guard, |slice| &slice[1..]); - assert_eq!(*guard, [42]); - drop(guard); - assert_eq!(*lock.get_mut().unwrap(), [0, 42, 0, 0]); -} - -#[test] -fn panic_while_mapping_read_unlocked_no_poison() { - let lock = RwLock::new(()); - - let _ = panic::catch_unwind(|| { - let guard = lock.read().unwrap(); - let _guard = RwLockReadGuard::map::<(), _>(guard, |_| panic!()); - }); - - match lock.try_write() { - Ok(_) => {} - Err(TryLockError::WouldBlock) => { - panic!("panicking in a RwLockReadGuard::map closure should release the read lock") - } - Err(TryLockError::Poisoned(_)) => { - panic!("panicking in a RwLockReadGuard::map closure should not poison the RwLock") - } - } - - let _ = panic::catch_unwind(|| { - let guard = lock.read().unwrap(); - let _guard = RwLockReadGuard::try_map::<(), _>(guard, |_| panic!()); - }); - - match lock.try_write() { - Ok(_) => {} - Err(TryLockError::WouldBlock) => { - panic!("panicking in a RwLockReadGuard::try_map closure should release the read lock") - } - Err(TryLockError::Poisoned(_)) => { - panic!("panicking in a RwLockReadGuard::try_map closure should not poison the RwLock") - } - } - - let _ = panic::catch_unwind(|| { - let guard = lock.read().unwrap(); - let guard = RwLockReadGuard::map::<(), _>(guard, |val| val); - let _guard = MappedRwLockReadGuard::map::<(), _>(guard, |_| panic!()); - }); - - match lock.try_write() { - Ok(_) => {} - Err(TryLockError::WouldBlock) => { - panic!("panicking in a MappedRwLockReadGuard::map closure should release the read lock") - } - Err(TryLockError::Poisoned(_)) => { - panic!("panicking in a MappedRwLockReadGuard::map closure should not poison the RwLock") - } - } - - let _ = panic::catch_unwind(|| { - let guard = lock.read().unwrap(); - let guard = RwLockReadGuard::map::<(), _>(guard, |val| val); - let _guard = MappedRwLockReadGuard::try_map::<(), _>(guard, |_| panic!()); - }); - - match lock.try_write() { - Ok(_) => {} - Err(TryLockError::WouldBlock) => panic!( - "panicking in a MappedRwLockReadGuard::try_map closure should release the read lock" - ), - Err(TryLockError::Poisoned(_)) => panic!( - "panicking in a MappedRwLockReadGuard::try_map closure should not poison the RwLock" - ), - } - - drop(lock); -} - -#[test] -fn panic_while_mapping_write_unlocked_poison() { - let lock = RwLock::new(()); - - let _ = panic::catch_unwind(|| { - let guard = lock.write().unwrap(); - let _guard = RwLockWriteGuard::map::<(), _>(guard, |_| panic!()); - }); - - match lock.try_write() { - Ok(_) => panic!("panicking in a RwLockWriteGuard::map closure should poison the RwLock"), - Err(TryLockError::WouldBlock) => { - panic!("panicking in a RwLockWriteGuard::map closure should release the write lock") - } - Err(TryLockError::Poisoned(_)) => {} - } - - let _ = panic::catch_unwind(|| { - let guard = lock.write().unwrap(); - let _guard = RwLockWriteGuard::try_map::<(), _>(guard, |_| panic!()); - }); - - match lock.try_write() { - Ok(_) => { - panic!("panicking in a RwLockWriteGuard::try_map closure should poison the RwLock") - } - Err(TryLockError::WouldBlock) => { - panic!("panicking in a RwLockWriteGuard::try_map closure should release the write lock") - } - Err(TryLockError::Poisoned(_)) => {} - } - - let _ = panic::catch_unwind(|| { - let guard = lock.write().unwrap(); - let guard = RwLockWriteGuard::map::<(), _>(guard, |val| val); - let _guard = MappedRwLockWriteGuard::map::<(), _>(guard, |_| panic!()); - }); - - match lock.try_write() { - Ok(_) => { - panic!("panicking in a MappedRwLockWriteGuard::map closure should poison the RwLock") - } - Err(TryLockError::WouldBlock) => panic!( - "panicking in a MappedRwLockWriteGuard::map closure should release the write lock" - ), - Err(TryLockError::Poisoned(_)) => {} - } - - let _ = panic::catch_unwind(|| { - let guard = lock.write().unwrap(); - let guard = RwLockWriteGuard::map::<(), _>(guard, |val| val); - let _guard = MappedRwLockWriteGuard::try_map::<(), _>(guard, |_| panic!()); - }); - - match lock.try_write() { - Ok(_) => panic!( - "panicking in a MappedRwLockWriteGuard::try_map closure should poison the RwLock" - ), - Err(TryLockError::WouldBlock) => panic!( - "panicking in a MappedRwLockWriteGuard::try_map closure should release the write lock" - ), - Err(TryLockError::Poisoned(_)) => {} - } - - drop(lock); -} - -#[test] -fn test_downgrade_basic() { - let r = RwLock::new(()); - - let write_guard = r.write().unwrap(); - let _read_guard = RwLockWriteGuard::downgrade(write_guard); -} - -#[test] -// FIXME: On macOS we use a provenance-incorrect implementation and Miri catches that issue. -// See for details. -#[cfg_attr(all(miri, target_os = "macos"), ignore)] -fn test_downgrade_observe() { - // Taken from the test `test_rwlock_downgrade` from: - // https://github.com/Amanieu/parking_lot/blob/master/src/rwlock.rs - - const W: usize = 20; - const N: usize = if cfg!(miri) { 40 } else { 100 }; - - // This test spawns `W` writer threads, where each will increment a counter `N` times, ensuring - // that the value they wrote has not changed after downgrading. - - let rw = Arc::new(RwLock::new(0)); - - // Spawn the writers that will do `W * N` operations and checks. - let handles: Vec<_> = (0..W) - .map(|_| { - let rw = rw.clone(); - thread::spawn(move || { - for _ in 0..N { - // Increment the counter. - let mut write_guard = rw.write().unwrap(); - *write_guard += 1; - let cur_val = *write_guard; - - // Downgrade the lock to read mode, where the value protected cannot be modified. - let read_guard = RwLockWriteGuard::downgrade(write_guard); - assert_eq!(cur_val, *read_guard); - } - }) - }) - .collect(); - - for handle in handles { - handle.join().unwrap(); - } - - assert_eq!(*rw.read().unwrap(), W * N); -} - -#[test] -// FIXME: On macOS we use a provenance-incorrect implementation and Miri catches that issue. -// See for details. -#[cfg_attr(all(miri, target_os = "macos"), ignore)] -fn test_downgrade_atomic() { - const NEW_VALUE: i32 = -1; - - // This test checks that `downgrade` is atomic, meaning as soon as a write lock has been - // downgraded, the lock must be in read mode and no other threads can take the write lock to - // modify the protected value. - - // `W` is the number of evil writer threads. - const W: usize = 20; - let rwlock = Arc::new(RwLock::new(0)); - - // Spawns many evil writer threads that will try and write to the locked value before the - // initial writer (who has the exclusive lock) can read after it downgrades. - // If the `RwLock` behaves correctly, then the initial writer should read the value it wrote - // itself as no other thread should be able to mutate the protected value. - - // Put the lock in write mode, causing all future threads trying to access this go to sleep. - let mut main_write_guard = rwlock.write().unwrap(); - - // Spawn all of the evil writer threads. They will each increment the protected value by 1. - let handles: Vec<_> = (0..W) - .map(|_| { - let rwlock = rwlock.clone(); - thread::spawn(move || { - // Will go to sleep since the main thread initially has the write lock. - let mut evil_guard = rwlock.write().unwrap(); - *evil_guard += 1; - }) - }) - .collect(); - - // Wait for a good amount of time so that evil threads go to sleep. - // Note: this is not strictly necessary... - let eternity = crate::time::Duration::from_millis(42); - thread::sleep(eternity); - - // Once everyone is asleep, set the value to `NEW_VALUE`. - *main_write_guard = NEW_VALUE; - - // Atomically downgrade the write guard into a read guard. - let main_read_guard = RwLockWriteGuard::downgrade(main_write_guard); - - // If the above is not atomic, then it would be possible for an evil thread to get in front of - // this read and change the value to be non-negative. - assert_eq!(*main_read_guard, NEW_VALUE, "`downgrade` was not atomic"); - - // Drop the main read guard and allow the evil writer threads to start incrementing. - drop(main_read_guard); - - for handle in handles { - handle.join().unwrap(); - } - - let final_check = rwlock.read().unwrap(); - assert_eq!(*final_check, W as i32 + NEW_VALUE); -} diff --git a/library/std/src/sync/reentrant_lock.rs b/library/std/src/sync/reentrant_lock.rs index 0140e0d21299f..e009eb410efc0 100644 --- a/library/std/src/sync/reentrant_lock.rs +++ b/library/std/src/sync/reentrant_lock.rs @@ -1,6 +1,3 @@ -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - use cfg_if::cfg_if; use crate::cell::UnsafeCell; @@ -324,7 +321,10 @@ impl ReentrantLock { /// Otherwise, an RAII guard is returned. /// /// This function does not block. - pub(crate) fn try_lock(&self) -> Option> { + // FIXME maybe make it a public part of the API? + #[unstable(issue = "none", feature = "std_internals")] + #[doc(hidden)] + pub fn try_lock(&self) -> Option> { let this_thread = current_id(); // Safety: We only touch lock_count when we own the inner mutex. // Additionally, we only call `self.owner.set()` while holding diff --git a/library/std/src/sync/reentrant_lock/tests.rs b/library/std/src/sync/reentrant_lock/tests.rs deleted file mode 100644 index aeef0289d28f8..0000000000000 --- a/library/std/src/sync/reentrant_lock/tests.rs +++ /dev/null @@ -1,53 +0,0 @@ -use super::ReentrantLock; -use crate::cell::RefCell; -use crate::sync::Arc; -use crate::thread; - -#[test] -fn smoke() { - let l = ReentrantLock::new(()); - { - let a = l.lock(); - { - let b = l.lock(); - { - let c = l.lock(); - assert_eq!(*c, ()); - } - assert_eq!(*b, ()); - } - assert_eq!(*a, ()); - } -} - -#[test] -fn is_mutex() { - let l = Arc::new(ReentrantLock::new(RefCell::new(0))); - let l2 = l.clone(); - let lock = l.lock(); - let child = thread::spawn(move || { - let lock = l2.lock(); - assert_eq!(*lock.borrow(), 4950); - }); - for i in 0..100 { - let lock = l.lock(); - *lock.borrow_mut() += i; - } - drop(lock); - child.join().unwrap(); -} - -#[test] -fn trylock_works() { - let l = Arc::new(ReentrantLock::new(())); - let l2 = l.clone(); - let _lock = l.try_lock(); - let _lock2 = l.try_lock(); - thread::spawn(move || { - let lock = l2.try_lock(); - assert!(lock.is_none()); - }) - .join() - .unwrap(); - let _lock3 = l.try_lock(); -} diff --git a/library/std/src/sys/alloc/sgx.rs b/library/std/src/sys/alloc/sgx.rs index fca9d087e5bfc..f5c27688fbc8f 100644 --- a/library/std/src/sys/alloc/sgx.rs +++ b/library/std/src/sys/alloc/sgx.rs @@ -11,7 +11,7 @@ use crate::sys::pal::waitqueue::SpinMutex; // in the rust-lang/rust repository as a submodule. The crate is a port of // dlmalloc.c from C to Rust. #[cfg_attr(test, linkage = "available_externally")] -#[export_name = "_ZN16__rust_internals3std3sys3sgx5alloc8DLMALLOCE"] +#[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx5alloc8DLMALLOCE")] static DLMALLOC: SpinMutex> = SpinMutex::new(dlmalloc::Dlmalloc::new_with_allocator(Sgx {})); @@ -85,13 +85,13 @@ unsafe impl GlobalAlloc for System { // The following functions are needed by libunwind. These symbols are named // in pre-link args for the target specification, so keep that in sync. #[cfg(not(test))] -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn __rust_c_alloc(size: usize, align: usize) -> *mut u8 { unsafe { crate::alloc::alloc(Layout::from_size_align_unchecked(size, align)) } } #[cfg(not(test))] -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn __rust_c_dealloc(ptr: *mut u8, size: usize, align: usize) { unsafe { crate::alloc::dealloc(ptr, Layout::from_size_align_unchecked(size, align)) } } diff --git a/library/std/src/sys/alloc/wasm.rs b/library/std/src/sys/alloc/wasm.rs index a308fafc68b15..53fbc9529e590 100644 --- a/library/std/src/sys/alloc/wasm.rs +++ b/library/std/src/sys/alloc/wasm.rs @@ -1,6 +1,6 @@ //! This is an implementation of a global allocator on wasm targets when -//! emscripten is not in use. In that situation there's no actual runtime for us -//! to lean on for allocation, so instead we provide our own! +//! emscripten or wasi is not in use. In that situation there's no actual runtime +//! for us to lean on for allocation, so instead we provide our own! //! //! The wasm instruction set has two instructions for getting the current //! amount of memory and growing the amount of memory. These instructions are the diff --git a/library/std/src/sys/alloc/xous.rs b/library/std/src/sys/alloc/xous.rs index 321d30e0b11b2..ccaa972c22de3 100644 --- a/library/std/src/sys/alloc/xous.rs +++ b/library/std/src/sys/alloc/xous.rs @@ -4,11 +4,11 @@ use crate::alloc::{GlobalAlloc, Layout, System}; #[cfg(not(test))] -#[export_name = "_ZN16__rust_internals3std3sys4xous5alloc8DLMALLOCE"] +#[unsafe(export_name = "_ZN16__rust_internals3std3sys4xous5alloc8DLMALLOCE")] static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::Dlmalloc::new(); #[cfg(test)] -extern "Rust" { +unsafe extern "Rust" { #[link_name = "_ZN16__rust_internals3std3sys4xous5alloc8DLMALLOCE"] static mut DLMALLOC: dlmalloc::Dlmalloc; } diff --git a/library/std/src/sys/anonymous_pipe/unix.rs b/library/std/src/sys/anonymous_pipe/unix.rs index 9168024730e67..9e398765634b7 100644 --- a/library/std/src/sys/anonymous_pipe/unix.rs +++ b/library/std/src/sys/anonymous_pipe/unix.rs @@ -1,6 +1,5 @@ -use crate::io; +use crate::io::{self, PipeReader, PipeWriter}; use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::pipe::{PipeReader, PipeWriter}; use crate::process::Stdio; use crate::sys::fd::FileDesc; use crate::sys::pipe::anon_pipe; diff --git a/library/std/src/sys/anonymous_pipe/unsupported.rs b/library/std/src/sys/anonymous_pipe/unsupported.rs index dd51e70315e96..4e79ac9c21aad 100644 --- a/library/std/src/sys/anonymous_pipe/unsupported.rs +++ b/library/std/src/sys/anonymous_pipe/unsupported.rs @@ -1,5 +1,4 @@ -use crate::io; -use crate::pipe::{PipeReader, PipeWriter}; +use crate::io::{self, PipeReader, PipeWriter}; use crate::process::Stdio; pub use crate::sys::pipe::AnonPipe; diff --git a/library/std/src/sys/anonymous_pipe/windows.rs b/library/std/src/sys/anonymous_pipe/windows.rs index a48198f8a812b..eb7fa8ec1c9a1 100644 --- a/library/std/src/sys/anonymous_pipe/windows.rs +++ b/library/std/src/sys/anonymous_pipe/windows.rs @@ -1,12 +1,12 @@ +use crate::io::{self, PipeReader, PipeWriter}; use crate::os::windows::io::{ AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, }; -use crate::pipe::{PipeReader, PipeWriter}; use crate::process::Stdio; +use crate::ptr; use crate::sys::c; use crate::sys::handle::Handle; use crate::sys_common::{FromInner, IntoInner}; -use crate::{io, ptr}; pub type AnonPipe = Handle; diff --git a/library/std/src/sys/cmath.rs b/library/std/src/sys/cmath.rs index ee36127cfdf1e..c9969b4e376ea 100644 --- a/library/std/src/sys/cmath.rs +++ b/library/std/src/sys/cmath.rs @@ -2,7 +2,7 @@ // These symbols are all defined by `libm`, // or by `compiler-builtins` on unsupported platforms. -extern "C" { +unsafe extern "C" { pub fn acos(n: f64) -> f64; pub fn asin(n: f64) -> f64; pub fn atan(n: f64) -> f64; @@ -28,6 +28,10 @@ extern "C" { pub fn lgamma_r(n: f64, s: &mut i32) -> f64; #[cfg(not(target_os = "aix"))] pub fn lgammaf_r(n: f32, s: &mut i32) -> f32; + pub fn erf(n: f64) -> f64; + pub fn erff(n: f32) -> f32; + pub fn erfc(n: f64) -> f64; + pub fn erfcf(n: f32) -> f32; pub fn acosf128(n: f128) -> f128; pub fn asinf128(n: f128) -> f128; @@ -43,6 +47,8 @@ extern "C" { pub fn tanhf128(n: f128) -> f128; pub fn tgammaf128(n: f128) -> f128; pub fn lgammaf128_r(n: f128, s: &mut i32) -> f128; + pub fn erff128(n: f128) -> f128; + pub fn erfcf128(n: f128) -> f128; cfg_if::cfg_if! { if #[cfg(not(all(target_os = "windows", target_env = "msvc", target_arch = "x86")))] { diff --git a/library/std/src/sys/io/io_slice/iovec.rs b/library/std/src/sys/io/io_slice/iovec.rs new file mode 100644 index 0000000000000..072191315f7c5 --- /dev/null +++ b/library/std/src/sys/io/io_slice/iovec.rs @@ -0,0 +1,87 @@ +#[cfg(target_os = "hermit")] +use hermit_abi::iovec; +#[cfg(target_family = "unix")] +use libc::iovec; + +use crate::ffi::c_void; +use crate::marker::PhantomData; +use crate::slice; +#[cfg(target_os = "solid_asp3")] +use crate::sys::pal::abi::sockets::iovec; + +#[derive(Copy, Clone)] +#[repr(transparent)] +pub struct IoSlice<'a> { + vec: iovec, + _p: PhantomData<&'a [u8]>, +} + +impl<'a> IoSlice<'a> { + #[inline] + pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + IoSlice { + vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() }, + _p: PhantomData, + } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + if self.vec.iov_len < n { + panic!("advancing IoSlice beyond its length"); + } + + unsafe { + self.vec.iov_len -= n; + self.vec.iov_base = self.vec.iov_base.add(n); + } + } + + #[inline] + pub const fn as_slice(&self) -> &'a [u8] { + unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } + } +} + +#[repr(transparent)] +pub struct IoSliceMut<'a> { + vec: iovec, + _p: PhantomData<&'a mut [u8]>, +} + +impl<'a> IoSliceMut<'a> { + #[inline] + pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + IoSliceMut { + vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() }, + _p: PhantomData, + } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + if self.vec.iov_len < n { + panic!("advancing IoSliceMut beyond its length"); + } + + unsafe { + self.vec.iov_len -= n; + self.vec.iov_base = self.vec.iov_base.add(n); + } + } + + #[inline] + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } + } + + #[inline] + pub const fn into_slice(self) -> &'a mut [u8] { + unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } + } + + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } + } +} diff --git a/library/std/src/sys/io/io_slice/unsupported.rs b/library/std/src/sys/io/io_slice/unsupported.rs new file mode 100644 index 0000000000000..1572cac6cd771 --- /dev/null +++ b/library/std/src/sys/io/io_slice/unsupported.rs @@ -0,0 +1,52 @@ +use crate::mem; + +#[derive(Copy, Clone)] +pub struct IoSlice<'a>(&'a [u8]); + +impl<'a> IoSlice<'a> { + #[inline] + pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + IoSlice(buf) + } + + #[inline] + pub fn advance(&mut self, n: usize) { + self.0 = &self.0[n..] + } + + #[inline] + pub const fn as_slice(&self) -> &'a [u8] { + self.0 + } +} + +pub struct IoSliceMut<'a>(&'a mut [u8]); + +impl<'a> IoSliceMut<'a> { + #[inline] + pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + IoSliceMut(buf) + } + + #[inline] + pub fn advance(&mut self, n: usize) { + let slice = mem::take(&mut self.0); + let (_, remaining) = slice.split_at_mut(n); + self.0 = remaining; + } + + #[inline] + pub fn as_slice(&self) -> &[u8] { + self.0 + } + + #[inline] + pub const fn into_slice(self) -> &'a mut [u8] { + self.0 + } + + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [u8] { + self.0 + } +} diff --git a/library/std/src/sys/io/io_slice/wasi.rs b/library/std/src/sys/io/io_slice/wasi.rs new file mode 100644 index 0000000000000..87acbbd924e56 --- /dev/null +++ b/library/std/src/sys/io/io_slice/wasi.rs @@ -0,0 +1,76 @@ +use crate::marker::PhantomData; +use crate::slice; + +#[derive(Copy, Clone)] +#[repr(transparent)] +pub struct IoSlice<'a> { + vec: wasi::Ciovec, + _p: PhantomData<&'a [u8]>, +} + +impl<'a> IoSlice<'a> { + #[inline] + pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + IoSlice { vec: wasi::Ciovec { buf: buf.as_ptr(), buf_len: buf.len() }, _p: PhantomData } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + if self.vec.buf_len < n { + panic!("advancing IoSlice beyond its length"); + } + + unsafe { + self.vec.buf_len -= n; + self.vec.buf = self.vec.buf.add(n); + } + } + + #[inline] + pub const fn as_slice(&self) -> &'a [u8] { + unsafe { slice::from_raw_parts(self.vec.buf as *const u8, self.vec.buf_len) } + } +} + +#[repr(transparent)] +pub struct IoSliceMut<'a> { + vec: wasi::Iovec, + _p: PhantomData<&'a mut [u8]>, +} + +impl<'a> IoSliceMut<'a> { + #[inline] + pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + IoSliceMut { + vec: wasi::Iovec { buf: buf.as_mut_ptr(), buf_len: buf.len() }, + _p: PhantomData, + } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + if self.vec.buf_len < n { + panic!("advancing IoSlice beyond its length"); + } + + unsafe { + self.vec.buf_len -= n; + self.vec.buf = self.vec.buf.add(n); + } + } + + #[inline] + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.vec.buf as *const u8, self.vec.buf_len) } + } + + #[inline] + pub const fn into_slice(self) -> &'a mut [u8] { + unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.buf_len) } + } + + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.buf_len) } + } +} diff --git a/library/std/src/sys/io/io_slice/windows.rs b/library/std/src/sys/io/io_slice/windows.rs new file mode 100644 index 0000000000000..c3d8ec87c19e3 --- /dev/null +++ b/library/std/src/sys/io/io_slice/windows.rs @@ -0,0 +1,82 @@ +use crate::marker::PhantomData; +use crate::slice; +use crate::sys::c; + +#[derive(Copy, Clone)] +#[repr(transparent)] +pub struct IoSlice<'a> { + vec: c::WSABUF, + _p: PhantomData<&'a [u8]>, +} + +impl<'a> IoSlice<'a> { + #[inline] + pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + assert!(buf.len() <= u32::MAX as usize); + IoSlice { + vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_ptr() as *mut u8 }, + _p: PhantomData, + } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + if (self.vec.len as usize) < n { + panic!("advancing IoSlice beyond its length"); + } + + unsafe { + self.vec.len -= n as u32; + self.vec.buf = self.vec.buf.add(n); + } + } + + #[inline] + pub const fn as_slice(&self) -> &'a [u8] { + unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) } + } +} + +#[repr(transparent)] +pub struct IoSliceMut<'a> { + vec: c::WSABUF, + _p: PhantomData<&'a mut [u8]>, +} + +impl<'a> IoSliceMut<'a> { + #[inline] + pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + assert!(buf.len() <= u32::MAX as usize); + IoSliceMut { + vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_mut_ptr() }, + _p: PhantomData, + } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + if (self.vec.len as usize) < n { + panic!("advancing IoSliceMut beyond its length"); + } + + unsafe { + self.vec.len -= n as u32; + self.vec.buf = self.vec.buf.add(n); + } + } + + #[inline] + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) } + } + + #[inline] + pub const fn into_slice(self) -> &'a mut [u8] { + unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } + } + + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } + } +} diff --git a/library/std/src/sys/io/is_terminal/hermit.rs b/library/std/src/sys/io/is_terminal/hermit.rs new file mode 100644 index 0000000000000..61bdb6f0a5440 --- /dev/null +++ b/library/std/src/sys/io/is_terminal/hermit.rs @@ -0,0 +1,6 @@ +use crate::os::fd::{AsFd, AsRawFd}; + +pub fn is_terminal(fd: &impl AsFd) -> bool { + let fd = fd.as_fd(); + hermit_abi::isatty(fd.as_raw_fd()) +} diff --git a/library/std/src/sys/io/is_terminal/isatty.rs b/library/std/src/sys/io/is_terminal/isatty.rs new file mode 100644 index 0000000000000..6e0b46211b907 --- /dev/null +++ b/library/std/src/sys/io/is_terminal/isatty.rs @@ -0,0 +1,6 @@ +use crate::os::fd::{AsFd, AsRawFd}; + +pub fn is_terminal(fd: &impl AsFd) -> bool { + let fd = fd.as_fd(); + unsafe { libc::isatty(fd.as_raw_fd()) != 0 } +} diff --git a/library/std/src/sys/io/is_terminal/unsupported.rs b/library/std/src/sys/io/is_terminal/unsupported.rs new file mode 100644 index 0000000000000..cee4add32fbfc --- /dev/null +++ b/library/std/src/sys/io/is_terminal/unsupported.rs @@ -0,0 +1,3 @@ +pub fn is_terminal(_: &T) -> bool { + false +} diff --git a/library/std/src/sys/io/is_terminal/windows.rs b/library/std/src/sys/io/is_terminal/windows.rs new file mode 100644 index 0000000000000..3ec18fb47b9de --- /dev/null +++ b/library/std/src/sys/io/is_terminal/windows.rs @@ -0,0 +1,71 @@ +use crate::ffi::c_void; +use crate::mem::size_of; +use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle}; +use crate::sys::c; + +pub fn is_terminal(h: &impl AsHandle) -> bool { + handle_is_console(h.as_handle()) +} + +fn handle_is_console(handle: BorrowedHandle<'_>) -> bool { + // A null handle means the process has no console. + if handle.as_raw_handle().is_null() { + return false; + } + + let mut out = 0; + if unsafe { c::GetConsoleMode(handle.as_raw_handle(), &mut out) != 0 } { + // False positives aren't possible. If we got a console then we definitely have a console. + return true; + } + + // Otherwise, we fall back to an msys hack to see if we can detect the presence of a pty. + msys_tty_on(handle) +} + +fn msys_tty_on(handle: BorrowedHandle<'_>) -> bool { + // Early return if the handle is not a pipe. + if unsafe { c::GetFileType(handle.as_raw_handle()) != c::FILE_TYPE_PIPE } { + return false; + } + + /// Mirrors [`FILE_NAME_INFO`], giving it a fixed length that we can stack + /// allocate + /// + /// [`FILE_NAME_INFO`]: https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_name_info + #[repr(C)] + #[allow(non_snake_case)] + struct FILE_NAME_INFO { + FileNameLength: u32, + FileName: [u16; c::MAX_PATH as usize], + } + let mut name_info = FILE_NAME_INFO { FileNameLength: 0, FileName: [0; c::MAX_PATH as usize] }; + // Safety: buffer length is fixed. + let res = unsafe { + c::GetFileInformationByHandleEx( + handle.as_raw_handle(), + c::FileNameInfo, + (&raw mut name_info) as *mut c_void, + size_of::() as u32, + ) + }; + if res == 0 { + return false; + } + + // Use `get` because `FileNameLength` can be out of range. + let s = match name_info.FileName.get(..name_info.FileNameLength as usize / 2) { + None => return false, + Some(s) => s, + }; + let name = String::from_utf16_lossy(s); + // Get the file name only. + let name = name.rsplit('\\').next().unwrap_or(&name); + // This checks whether 'pty' exists in the file name, which indicates that + // a pseudo-terminal is attached. To mitigate against false positives + // (e.g., an actual file name that contains 'pty'), we also require that + // the file name begins with either the strings 'msys-' or 'cygwin-'.) + let is_msys = name.starts_with("msys-") || name.starts_with("cygwin-"); + let is_pty = name.contains("-pty"); + is_msys && is_pty +} diff --git a/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs new file mode 100644 index 0000000000000..e00b479109f39 --- /dev/null +++ b/library/std/src/sys/io/mod.rs @@ -0,0 +1,44 @@ +#![forbid(unsafe_op_in_unsafe_fn)] + +mod io_slice { + cfg_if::cfg_if! { + if #[cfg(any(target_family = "unix", target_os = "hermit", target_os = "solid_asp3"))] { + mod iovec; + pub use iovec::*; + } else if #[cfg(target_os = "windows")] { + mod windows; + pub use windows::*; + } else if #[cfg(target_os = "wasi")] { + mod wasi; + pub use wasi::*; + } else { + mod unsupported; + pub use unsupported::*; + } + } +} + +mod is_terminal { + cfg_if::cfg_if! { + if #[cfg(any(target_family = "unix", target_os = "wasi"))] { + mod isatty; + pub use isatty::*; + } else if #[cfg(target_os = "windows")] { + mod windows; + pub use windows::*; + } else if #[cfg(target_os = "hermit")] { + mod hermit; + pub use hermit::*; + } else { + mod unsupported; + pub use unsupported::*; + } + } +} + +pub use io_slice::{IoSlice, IoSliceMut}; +pub use is_terminal::is_terminal; + +// Bare metal platforms usually have very small amounts of RAM +// (in the order of hundreds of KB) +pub const DEFAULT_BUF_SIZE: usize = if cfg!(target_os = "espidf") { 512 } else { 8 * 1024 }; diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index f17dd47decedc..1032fcba5e2e1 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -12,6 +12,8 @@ pub mod anonymous_pipe; pub mod backtrace; pub mod cmath; pub mod exit_guard; +pub mod io; +pub mod net; pub mod os_str; pub mod path; pub mod random; diff --git a/library/std/src/sys/net/connection/sgx.rs b/library/std/src/sys/net/connection/sgx.rs new file mode 100644 index 0000000000000..242df10bc3270 --- /dev/null +++ b/library/std/src/sys/net/connection/sgx.rs @@ -0,0 +1,501 @@ +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; +use crate::sync::Arc; +use crate::sys::abi::usercalls; +use crate::sys::fd::FileDesc; +use crate::sys::{AsInner, FromInner, IntoInner, TryIntoInner, sgx_ineffective, unsupported}; +use crate::time::Duration; +use crate::{error, fmt}; + +const DEFAULT_FAKE_TTL: u32 = 64; + +#[derive(Debug, Clone)] +pub struct Socket { + inner: Arc, + local_addr: Option, +} + +impl Socket { + fn new(fd: usercalls::raw::Fd, local_addr: String) -> Socket { + Socket { inner: Arc::new(FileDesc::new(fd)), local_addr: Some(local_addr) } + } +} + +impl AsInner for Socket { + #[inline] + fn as_inner(&self) -> &FileDesc { + &self.inner + } +} + +impl TryIntoInner for Socket { + fn try_into_inner(self) -> Result { + let Socket { inner, local_addr } = self; + Arc::try_unwrap(inner).map_err(|inner| Socket { inner, local_addr }) + } +} + +impl FromInner<(FileDesc, Option)> for Socket { + fn from_inner((inner, local_addr): (FileDesc, Option)) -> Socket { + Socket { inner: Arc::new(inner), local_addr } + } +} + +#[derive(Clone)] +pub struct TcpStream { + inner: Socket, + peer_addr: Option, +} + +impl fmt::Debug for TcpStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut res = f.debug_struct("TcpStream"); + + if let Some(ref addr) = self.inner.local_addr { + res.field("addr", addr); + } + + if let Some(ref peer) = self.peer_addr { + res.field("peer", peer); + } + + res.field("fd", &self.inner.inner.as_inner()).finish() + } +} + +fn io_err_to_addr(result: io::Result<&SocketAddr>) -> io::Result { + match result { + Ok(saddr) => Ok(saddr.to_string()), + // need to downcast twice because io::Error::into_inner doesn't return the original + // value if the conversion fails + Err(e) => { + if e.get_ref().and_then(|e| e.downcast_ref::()).is_some() { + Ok(e.into_inner().unwrap().downcast::().unwrap().host) + } else { + Err(e) + } + } + } +} + +fn addr_to_sockaddr(addr: Option<&str>) -> io::Result { + addr.ok_or(io::ErrorKind::AddrNotAvailable)? + .to_socket_addrs() + // unwrap OK: if an iterator is returned, we're guaranteed to get exactly one entry + .map(|mut it| it.next().unwrap()) +} + +impl TcpStream { + pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result { + let addr = io_err_to_addr(addr)?; + let (fd, local_addr, peer_addr) = usercalls::connect_stream(&addr)?; + Ok(TcpStream { inner: Socket::new(fd, local_addr), peer_addr: Some(peer_addr) }) + } + + pub fn connect_timeout(addr: &SocketAddr, dur: Duration) -> io::Result { + if dur == Duration::default() { + return Err(io::Error::ZERO_TIMEOUT); + } + Self::connect(Ok(addr)) // FIXME: ignoring timeout + } + + pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { + match dur { + Some(dur) if dur == Duration::default() => { + return Err(io::Error::ZERO_TIMEOUT); + } + _ => sgx_ineffective(()), + } + } + + pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { + match dur { + Some(dur) if dur == Duration::default() => { + return Err(io::Error::ZERO_TIMEOUT); + } + _ => sgx_ineffective(()), + } + } + + pub fn read_timeout(&self) -> io::Result> { + sgx_ineffective(None) + } + + pub fn write_timeout(&self) -> io::Result> { + sgx_ineffective(None) + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + Ok(0) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.inner.inner.read(buf) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.inner.inner.read_buf(buf) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.inner.inner.read_vectored(bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.inner.inner.is_read_vectored() + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.inner.inner.write(buf) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + self.inner.inner.write_vectored(bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + self.inner.inner.is_write_vectored() + } + + pub fn peer_addr(&self) -> io::Result { + addr_to_sockaddr(self.peer_addr.as_deref()) + } + + pub fn socket_addr(&self) -> io::Result { + addr_to_sockaddr(self.inner.local_addr.as_deref()) + } + + pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn duplicate(&self) -> io::Result { + Ok(self.clone()) + } + + pub fn set_linger(&self, _: Option) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn linger(&self) -> io::Result> { + sgx_ineffective(None) + } + + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn nodelay(&self) -> io::Result { + sgx_ineffective(false) + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn ttl(&self) -> io::Result { + sgx_ineffective(DEFAULT_FAKE_TTL) + } + + pub fn take_error(&self) -> io::Result> { + Ok(None) + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + sgx_ineffective(()) + } +} + +impl AsInner for TcpStream { + #[inline] + fn as_inner(&self) -> &Socket { + &self.inner + } +} + +// `Inner` includes `peer_addr` so that a `TcpStream` maybe correctly +// reconstructed if `Socket::try_into_inner` fails. +impl IntoInner<(Socket, Option)> for TcpStream { + fn into_inner(self) -> (Socket, Option) { + (self.inner, self.peer_addr) + } +} + +impl FromInner<(Socket, Option)> for TcpStream { + fn from_inner((inner, peer_addr): (Socket, Option)) -> TcpStream { + TcpStream { inner, peer_addr } + } +} + +#[derive(Clone)] +pub struct TcpListener { + inner: Socket, +} + +impl fmt::Debug for TcpListener { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut res = f.debug_struct("TcpListener"); + + if let Some(ref addr) = self.inner.local_addr { + res.field("addr", addr); + } + + res.field("fd", &self.inner.inner.as_inner()).finish() + } +} + +impl TcpListener { + pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result { + let addr = io_err_to_addr(addr)?; + let (fd, local_addr) = usercalls::bind_stream(&addr)?; + Ok(TcpListener { inner: Socket::new(fd, local_addr) }) + } + + pub fn socket_addr(&self) -> io::Result { + addr_to_sockaddr(self.inner.local_addr.as_deref()) + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + let (fd, local_addr, peer_addr) = usercalls::accept_stream(self.inner.inner.raw())?; + let peer_addr = Some(peer_addr); + let ret_peer = + addr_to_sockaddr(peer_addr.as_deref()).unwrap_or_else(|_| ([0; 4], 0).into()); + Ok((TcpStream { inner: Socket::new(fd, local_addr), peer_addr }, ret_peer)) + } + + pub fn duplicate(&self) -> io::Result { + Ok(self.clone()) + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn ttl(&self) -> io::Result { + sgx_ineffective(DEFAULT_FAKE_TTL) + } + + pub fn set_only_v6(&self, _: bool) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn only_v6(&self) -> io::Result { + sgx_ineffective(false) + } + + pub fn take_error(&self) -> io::Result> { + Ok(None) + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + sgx_ineffective(()) + } +} + +impl AsInner for TcpListener { + #[inline] + fn as_inner(&self) -> &Socket { + &self.inner + } +} + +impl IntoInner for TcpListener { + fn into_inner(self) -> Socket { + self.inner + } +} + +impl FromInner for TcpListener { + fn from_inner(inner: Socket) -> TcpListener { + TcpListener { inner } + } +} + +pub struct UdpSocket(!); + +impl UdpSocket { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn peer_addr(&self) -> io::Result { + self.0 + } + + pub fn socket_addr(&self) -> io::Result { + self.0 + } + + pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.0 + } + + pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.0 + } + + pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn read_timeout(&self) -> io::Result> { + self.0 + } + + pub fn write_timeout(&self) -> io::Result> { + self.0 + } + + pub fn set_broadcast(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn broadcast(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn multicast_loop_v4(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn multicast_loop_v6(&self) -> io::Result { + self.0 + } + + pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { + self.0 + } + + pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { + self.0 + } + + pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { + self.0 + } + + pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { + self.0 + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn ttl(&self) -> io::Result { + self.0 + } + + pub fn take_error(&self) -> io::Result> { + self.0 + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn recv(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn send(&self, _: &[u8]) -> io::Result { + self.0 + } + + pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { + self.0 + } +} + +impl fmt::Debug for UdpSocket { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +#[derive(Debug)] +pub struct NonIpSockAddr { + host: String, +} + +impl error::Error for NonIpSockAddr { + #[allow(deprecated)] + fn description(&self) -> &str { + "Failed to convert address to SocketAddr" + } +} + +impl fmt::Display for NonIpSockAddr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Failed to convert address to SocketAddr: {}", self.host) + } +} + +pub struct LookupHost(!); + +impl LookupHost { + fn new(host: String) -> io::Result { + Err(io::Error::new(io::ErrorKind::Uncategorized, NonIpSockAddr { host })) + } + + pub fn port(&self) -> u16 { + self.0 + } +} + +impl Iterator for LookupHost { + type Item = SocketAddr; + fn next(&mut self) -> Option { + self.0 + } +} + +impl TryFrom<&str> for LookupHost { + type Error = io::Error; + + fn try_from(v: &str) -> io::Result { + LookupHost::new(v.to_owned()) + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from((host, port): (&'a str, u16)) -> io::Result { + LookupHost::new(format!("{host}:{port}")) + } +} diff --git a/library/std/src/sys/net/connection/socket.rs b/library/std/src/sys/net/connection/socket.rs new file mode 100644 index 0000000000000..b4f0a7836803e --- /dev/null +++ b/library/std/src/sys/net/connection/socket.rs @@ -0,0 +1,832 @@ +#[cfg(test)] +mod tests; + +use crate::ffi::{c_int, c_void}; +use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; +use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6}; +use crate::sys::common::small_c_string::run_with_cstr; +use crate::sys_common::{AsInner, FromInner}; +use crate::time::Duration; +use crate::{cmp, fmt, mem, ptr}; + +cfg_if::cfg_if! { + if #[cfg(target_os = "hermit")] { + mod hermit; + pub use hermit::*; + } else if #[cfg(target_os = "solid_asp3")] { + mod solid; + pub use solid::*; + } else if #[cfg(target_family = "unix")] { + mod unix; + pub use unix::*; + } else if #[cfg(all(target_os = "wasi", target_env = "p2"))] { + mod wasip2; + pub use wasip2::*; + } else if #[cfg(target_os = "windows")] { + mod windows; + pub use windows::*; + } +} + +use netc as c; + +cfg_if::cfg_if! { + if #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "openbsd", + target_os = "netbsd", + target_os = "illumos", + target_os = "solaris", + target_os = "haiku", + target_os = "l4re", + target_os = "nto", + target_os = "nuttx", + target_vendor = "apple", + ))] { + use c::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; + use c::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; + } else { + use c::IPV6_ADD_MEMBERSHIP; + use c::IPV6_DROP_MEMBERSHIP; + } +} + +cfg_if::cfg_if! { + if #[cfg(any( + target_os = "linux", target_os = "android", + target_os = "hurd", + target_os = "dragonfly", target_os = "freebsd", + target_os = "openbsd", target_os = "netbsd", + target_os = "solaris", target_os = "illumos", + target_os = "haiku", target_os = "nto"))] { + use libc::MSG_NOSIGNAL; + } else { + const MSG_NOSIGNAL: c_int = 0x0; + } +} + +cfg_if::cfg_if! { + if #[cfg(any( + target_os = "dragonfly", target_os = "freebsd", + target_os = "openbsd", target_os = "netbsd", + target_os = "solaris", target_os = "illumos", + target_os = "nto"))] { + use crate::ffi::c_uchar; + type IpV4MultiCastType = c_uchar; + } else { + type IpV4MultiCastType = c_int; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// address conversions +//////////////////////////////////////////////////////////////////////////////// + +fn ip_v4_addr_to_c(addr: &Ipv4Addr) -> c::in_addr { + // `s_addr` is stored as BE on all machines and the array is in BE order. + // So the native endian conversion method is used so that it's never swapped. + c::in_addr { s_addr: u32::from_ne_bytes(addr.octets()) } +} + +fn ip_v6_addr_to_c(addr: &Ipv6Addr) -> c::in6_addr { + c::in6_addr { s6_addr: addr.octets() } +} + +fn ip_v4_addr_from_c(addr: c::in_addr) -> Ipv4Addr { + Ipv4Addr::from(addr.s_addr.to_ne_bytes()) +} + +fn ip_v6_addr_from_c(addr: c::in6_addr) -> Ipv6Addr { + Ipv6Addr::from(addr.s6_addr) +} + +fn socket_addr_v4_to_c(addr: &SocketAddrV4) -> c::sockaddr_in { + c::sockaddr_in { + sin_family: c::AF_INET as c::sa_family_t, + sin_port: addr.port().to_be(), + sin_addr: ip_v4_addr_to_c(addr.ip()), + ..unsafe { mem::zeroed() } + } +} + +fn socket_addr_v6_to_c(addr: &SocketAddrV6) -> c::sockaddr_in6 { + c::sockaddr_in6 { + sin6_family: c::AF_INET6 as c::sa_family_t, + sin6_port: addr.port().to_be(), + sin6_addr: ip_v6_addr_to_c(addr.ip()), + sin6_flowinfo: addr.flowinfo(), + sin6_scope_id: addr.scope_id(), + ..unsafe { mem::zeroed() } + } +} + +fn socket_addr_v4_from_c(addr: c::sockaddr_in) -> SocketAddrV4 { + SocketAddrV4::new(ip_v4_addr_from_c(addr.sin_addr), u16::from_be(addr.sin_port)) +} + +fn socket_addr_v6_from_c(addr: c::sockaddr_in6) -> SocketAddrV6 { + SocketAddrV6::new( + ip_v6_addr_from_c(addr.sin6_addr), + u16::from_be(addr.sin6_port), + addr.sin6_flowinfo, + addr.sin6_scope_id, + ) +} + +/// A type with the same memory layout as `c::sockaddr`. Used in converting Rust level +/// SocketAddr* types into their system representation. The benefit of this specific +/// type over using `c::sockaddr_storage` is that this type is exactly as large as it +/// needs to be and not a lot larger. And it can be initialized more cleanly from Rust. +#[repr(C)] +union SocketAddrCRepr { + v4: c::sockaddr_in, + v6: c::sockaddr_in6, +} + +impl SocketAddrCRepr { + fn as_ptr(&self) -> *const c::sockaddr { + self as *const _ as *const c::sockaddr + } +} + +fn socket_addr_to_c(addr: &SocketAddr) -> (SocketAddrCRepr, c::socklen_t) { + match addr { + SocketAddr::V4(a) => { + let sockaddr = SocketAddrCRepr { v4: socket_addr_v4_to_c(a) }; + (sockaddr, mem::size_of::() as c::socklen_t) + } + SocketAddr::V6(a) => { + let sockaddr = SocketAddrCRepr { v6: socket_addr_v6_to_c(a) }; + (sockaddr, mem::size_of::() as c::socklen_t) + } + } +} + +unsafe fn socket_addr_from_c( + storage: *const c::sockaddr_storage, + len: usize, +) -> io::Result { + match (*storage).ss_family as c_int { + c::AF_INET => { + assert!(len >= mem::size_of::()); + Ok(SocketAddr::V4(socket_addr_v4_from_c(unsafe { + *(storage as *const _ as *const c::sockaddr_in) + }))) + } + c::AF_INET6 => { + assert!(len >= mem::size_of::()); + Ok(SocketAddr::V6(socket_addr_v6_from_c(unsafe { + *(storage as *const _ as *const c::sockaddr_in6) + }))) + } + _ => Err(io::const_error!(ErrorKind::InvalidInput, "invalid argument")), + } +} + +//////////////////////////////////////////////////////////////////////////////// +// sockaddr and misc bindings +//////////////////////////////////////////////////////////////////////////////// + +pub fn setsockopt( + sock: &Socket, + level: c_int, + option_name: c_int, + option_value: T, +) -> io::Result<()> { + unsafe { + cvt(c::setsockopt( + sock.as_raw(), + level, + option_name, + (&raw const option_value) as *const _, + mem::size_of::() as c::socklen_t, + ))?; + Ok(()) + } +} + +pub fn getsockopt(sock: &Socket, level: c_int, option_name: c_int) -> io::Result { + unsafe { + let mut option_value: T = mem::zeroed(); + let mut option_len = mem::size_of::() as c::socklen_t; + cvt(c::getsockopt( + sock.as_raw(), + level, + option_name, + (&raw mut option_value) as *mut _, + &mut option_len, + ))?; + Ok(option_value) + } +} + +fn sockname(f: F) -> io::Result +where + F: FnOnce(*mut c::sockaddr, *mut c::socklen_t) -> c_int, +{ + unsafe { + let mut storage: c::sockaddr_storage = mem::zeroed(); + let mut len = mem::size_of_val(&storage) as c::socklen_t; + cvt(f((&raw mut storage) as *mut _, &mut len))?; + socket_addr_from_c(&storage, len as usize) + } +} + +#[cfg(target_os = "android")] +fn to_ipv6mr_interface(value: u32) -> c_int { + value as c_int +} + +#[cfg(not(target_os = "android"))] +fn to_ipv6mr_interface(value: u32) -> crate::ffi::c_uint { + value as crate::ffi::c_uint +} + +//////////////////////////////////////////////////////////////////////////////// +// get_host_addresses +//////////////////////////////////////////////////////////////////////////////// + +pub struct LookupHost { + original: *mut c::addrinfo, + cur: *mut c::addrinfo, + port: u16, +} + +impl LookupHost { + pub fn port(&self) -> u16 { + self.port + } +} + +impl Iterator for LookupHost { + type Item = SocketAddr; + fn next(&mut self) -> Option { + loop { + unsafe { + let cur = self.cur.as_ref()?; + self.cur = cur.ai_next; + match socket_addr_from_c(cur.ai_addr.cast(), cur.ai_addrlen as usize) { + Ok(addr) => return Some(addr), + Err(_) => continue, + } + } + } + } +} + +unsafe impl Sync for LookupHost {} +unsafe impl Send for LookupHost {} + +impl Drop for LookupHost { + fn drop(&mut self) { + unsafe { c::freeaddrinfo(self.original) } + } +} + +impl TryFrom<&str> for LookupHost { + type Error = io::Error; + + fn try_from(s: &str) -> io::Result { + macro_rules! try_opt { + ($e:expr, $msg:expr) => { + match $e { + Some(r) => r, + None => return Err(io::const_error!(io::ErrorKind::InvalidInput, $msg)), + } + }; + } + + // split the string by ':' and convert the second part to u16 + let (host, port_str) = try_opt!(s.rsplit_once(':'), "invalid socket address"); + let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); + (host, port).try_into() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from((host, port): (&'a str, u16)) -> io::Result { + init(); + + run_with_cstr(host.as_bytes(), &|c_host| { + let mut hints: c::addrinfo = unsafe { mem::zeroed() }; + hints.ai_socktype = c::SOCK_STREAM; + let mut res = ptr::null_mut(); + unsafe { + cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)) + .map(|_| LookupHost { original: res, cur: res, port }) + } + }) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// TCP streams +//////////////////////////////////////////////////////////////////////////////// + +pub struct TcpStream { + inner: Socket, +} + +impl TcpStream { + pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result { + let addr = addr?; + + init(); + + let sock = Socket::new(addr, c::SOCK_STREAM)?; + sock.connect(addr)?; + Ok(TcpStream { inner: sock }) + } + + pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result { + init(); + + let sock = Socket::new(addr, c::SOCK_STREAM)?; + sock.connect_timeout(addr, timeout)?; + Ok(TcpStream { inner: sock }) + } + + #[inline] + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } + + pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { + self.inner.set_timeout(dur, c::SO_RCVTIMEO) + } + + pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { + self.inner.set_timeout(dur, c::SO_SNDTIMEO) + } + + pub fn read_timeout(&self) -> io::Result> { + self.inner.timeout(c::SO_RCVTIMEO) + } + + pub fn write_timeout(&self) -> io::Result> { + self.inner.timeout(c::SO_SNDTIMEO) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + self.inner.peek(buf) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.inner.read(buf) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.inner.read_buf(buf) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.inner.read_vectored(bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.inner.is_read_vectored() + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; + let ret = cvt(unsafe { + c::send(self.inner.as_raw(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL) + })?; + Ok(ret as usize) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + self.inner.write_vectored(bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + self.inner.is_write_vectored() + } + + pub fn peer_addr(&self) -> io::Result { + sockname(|buf, len| unsafe { c::getpeername(self.inner.as_raw(), buf, len) }) + } + + pub fn socket_addr(&self) -> io::Result { + sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) }) + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + self.inner.shutdown(how) + } + + pub fn duplicate(&self) -> io::Result { + self.inner.duplicate().map(|s| TcpStream { inner: s }) + } + + pub fn set_linger(&self, linger: Option) -> io::Result<()> { + self.inner.set_linger(linger) + } + + pub fn linger(&self) -> io::Result> { + self.inner.linger() + } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + self.inner.set_nodelay(nodelay) + } + + pub fn nodelay(&self) -> io::Result { + self.inner.nodelay() + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) + } + + pub fn ttl(&self) -> io::Result { + let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)?; + Ok(raw as u32) + } + + pub fn take_error(&self) -> io::Result> { + self.inner.take_error() + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.inner.set_nonblocking(nonblocking) + } +} + +impl AsInner for TcpStream { + #[inline] + fn as_inner(&self) -> &Socket { + &self.inner + } +} + +impl FromInner for TcpStream { + fn from_inner(socket: Socket) -> TcpStream { + TcpStream { inner: socket } + } +} + +impl fmt::Debug for TcpStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut res = f.debug_struct("TcpStream"); + + if let Ok(addr) = self.socket_addr() { + res.field("addr", &addr); + } + + if let Ok(peer) = self.peer_addr() { + res.field("peer", &peer); + } + + let name = if cfg!(windows) { "socket" } else { "fd" }; + res.field(name, &self.inner.as_raw()).finish() + } +} + +//////////////////////////////////////////////////////////////////////////////// +// TCP listeners +//////////////////////////////////////////////////////////////////////////////// + +pub struct TcpListener { + inner: Socket, +} + +impl TcpListener { + pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result { + let addr = addr?; + + init(); + + let sock = Socket::new(addr, c::SOCK_STREAM)?; + + // On platforms with Berkeley-derived sockets, this allows to quickly + // rebind a socket, without needing to wait for the OS to clean up the + // previous one. + // + // On Windows, this allows rebinding sockets which are actively in use, + // which allows “socket hijacking”, so we explicitly don't set it here. + // https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse + #[cfg(not(windows))] + setsockopt(&sock, c::SOL_SOCKET, c::SO_REUSEADDR, 1 as c_int)?; + + // Bind our new socket + let (addr, len) = socket_addr_to_c(addr); + cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?; + + cfg_if::cfg_if! { + if #[cfg(target_os = "horizon")] { + // The 3DS doesn't support a big connection backlog. Sometimes + // it allows up to about 37, but other times it doesn't even + // accept 32. There may be a global limitation causing this. + let backlog = 20; + } else if #[cfg(target_os = "haiku")] { + // Haiku does not support a queue length > 32 + // https://github.com/haiku/haiku/blob/979a0bc487864675517fb2fab28f87dc8bf43041/headers/posix/sys/socket.h#L81 + let backlog = 32; + } else { + // The default for all other platforms + let backlog = 128; + } + } + + // Start listening + cvt(unsafe { c::listen(sock.as_raw(), backlog) })?; + Ok(TcpListener { inner: sock }) + } + + #[inline] + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } + + pub fn socket_addr(&self) -> io::Result { + sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) }) + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + let mut storage: c::sockaddr_storage = unsafe { mem::zeroed() }; + let mut len = mem::size_of_val(&storage) as c::socklen_t; + let sock = self.inner.accept((&raw mut storage) as *mut _, &mut len)?; + let addr = unsafe { socket_addr_from_c(&storage, len as usize)? }; + Ok((TcpStream { inner: sock }, addr)) + } + + pub fn duplicate(&self) -> io::Result { + self.inner.duplicate().map(|s| TcpListener { inner: s }) + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) + } + + pub fn ttl(&self) -> io::Result { + let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)?; + Ok(raw as u32) + } + + pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY, only_v6 as c_int) + } + + pub fn only_v6(&self) -> io::Result { + let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY)?; + Ok(raw != 0) + } + + pub fn take_error(&self) -> io::Result> { + self.inner.take_error() + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.inner.set_nonblocking(nonblocking) + } +} + +impl FromInner for TcpListener { + fn from_inner(socket: Socket) -> TcpListener { + TcpListener { inner: socket } + } +} + +impl fmt::Debug for TcpListener { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut res = f.debug_struct("TcpListener"); + + if let Ok(addr) = self.socket_addr() { + res.field("addr", &addr); + } + + let name = if cfg!(windows) { "socket" } else { "fd" }; + res.field(name, &self.inner.as_raw()).finish() + } +} + +//////////////////////////////////////////////////////////////////////////////// +// UDP +//////////////////////////////////////////////////////////////////////////////// + +pub struct UdpSocket { + inner: Socket, +} + +impl UdpSocket { + pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result { + let addr = addr?; + + init(); + + let sock = Socket::new(addr, c::SOCK_DGRAM)?; + let (addr, len) = socket_addr_to_c(addr); + cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?; + Ok(UdpSocket { inner: sock }) + } + + #[inline] + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } + + pub fn peer_addr(&self) -> io::Result { + sockname(|buf, len| unsafe { c::getpeername(self.inner.as_raw(), buf, len) }) + } + + pub fn socket_addr(&self) -> io::Result { + sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) }) + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.inner.recv_from(buf) + } + + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.inner.peek_from(buf) + } + + pub fn send_to(&self, buf: &[u8], dst: &SocketAddr) -> io::Result { + let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; + let (dst, dstlen) = socket_addr_to_c(dst); + let ret = cvt(unsafe { + c::sendto( + self.inner.as_raw(), + buf.as_ptr() as *const c_void, + len, + MSG_NOSIGNAL, + dst.as_ptr(), + dstlen, + ) + })?; + Ok(ret as usize) + } + + pub fn duplicate(&self) -> io::Result { + self.inner.duplicate().map(|s| UdpSocket { inner: s }) + } + + pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { + self.inner.set_timeout(dur, c::SO_RCVTIMEO) + } + + pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { + self.inner.set_timeout(dur, c::SO_SNDTIMEO) + } + + pub fn read_timeout(&self) -> io::Result> { + self.inner.timeout(c::SO_RCVTIMEO) + } + + pub fn write_timeout(&self) -> io::Result> { + self.inner.timeout(c::SO_SNDTIMEO) + } + + pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> { + setsockopt(&self.inner, c::SOL_SOCKET, c::SO_BROADCAST, broadcast as c_int) + } + + pub fn broadcast(&self) -> io::Result { + let raw: c_int = getsockopt(&self.inner, c::SOL_SOCKET, c::SO_BROADCAST)?; + Ok(raw != 0) + } + + pub fn set_multicast_loop_v4(&self, multicast_loop_v4: bool) -> io::Result<()> { + setsockopt( + &self.inner, + c::IPPROTO_IP, + c::IP_MULTICAST_LOOP, + multicast_loop_v4 as IpV4MultiCastType, + ) + } + + pub fn multicast_loop_v4(&self) -> io::Result { + let raw: IpV4MultiCastType = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_LOOP)?; + Ok(raw != 0) + } + + pub fn set_multicast_ttl_v4(&self, multicast_ttl_v4: u32) -> io::Result<()> { + setsockopt( + &self.inner, + c::IPPROTO_IP, + c::IP_MULTICAST_TTL, + multicast_ttl_v4 as IpV4MultiCastType, + ) + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + let raw: IpV4MultiCastType = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_TTL)?; + Ok(raw as u32) + } + + pub fn set_multicast_loop_v6(&self, multicast_loop_v6: bool) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP, multicast_loop_v6 as c_int) + } + + pub fn multicast_loop_v6(&self) -> io::Result { + let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP)?; + Ok(raw != 0) + } + + pub fn join_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { + let mreq = c::ip_mreq { + imr_multiaddr: ip_v4_addr_to_c(multiaddr), + imr_interface: ip_v4_addr_to_c(interface), + }; + setsockopt(&self.inner, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreq) + } + + pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { + let mreq = c::ipv6_mreq { + ipv6mr_multiaddr: ip_v6_addr_to_c(multiaddr), + ipv6mr_interface: to_ipv6mr_interface(interface), + }; + setsockopt(&self.inner, c::IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, mreq) + } + + pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { + let mreq = c::ip_mreq { + imr_multiaddr: ip_v4_addr_to_c(multiaddr), + imr_interface: ip_v4_addr_to_c(interface), + }; + setsockopt(&self.inner, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreq) + } + + pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { + let mreq = c::ipv6_mreq { + ipv6mr_multiaddr: ip_v6_addr_to_c(multiaddr), + ipv6mr_interface: to_ipv6mr_interface(interface), + }; + setsockopt(&self.inner, c::IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, mreq) + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) + } + + pub fn ttl(&self) -> io::Result { + let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)?; + Ok(raw as u32) + } + + pub fn take_error(&self) -> io::Result> { + self.inner.take_error() + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.inner.set_nonblocking(nonblocking) + } + + pub fn recv(&self, buf: &mut [u8]) -> io::Result { + self.inner.read(buf) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + self.inner.peek(buf) + } + + pub fn send(&self, buf: &[u8]) -> io::Result { + let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; + let ret = cvt(unsafe { + c::send(self.inner.as_raw(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL) + })?; + Ok(ret as usize) + } + + pub fn connect(&self, addr: io::Result<&SocketAddr>) -> io::Result<()> { + let (addr, len) = socket_addr_to_c(addr?); + cvt_r(|| unsafe { c::connect(self.inner.as_raw(), addr.as_ptr(), len) }).map(drop) + } +} + +impl FromInner for UdpSocket { + fn from_inner(socket: Socket) -> UdpSocket { + UdpSocket { inner: socket } + } +} + +impl fmt::Debug for UdpSocket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut res = f.debug_struct("UdpSocket"); + + if let Ok(addr) = self.socket_addr() { + res.field("addr", &addr); + } + + let name = if cfg!(windows) { "socket" } else { "fd" }; + res.field(name, &self.inner.as_raw()).finish() + } +} diff --git a/library/std/src/sys/net/connection/socket/hermit.rs b/library/std/src/sys/net/connection/socket/hermit.rs new file mode 100644 index 0000000000000..e393342ced9da --- /dev/null +++ b/library/std/src/sys/net/connection/socket/hermit.rs @@ -0,0 +1,346 @@ +#![allow(dead_code)] + +use core::ffi::c_int; + +pub(super) use hermit_abi as netc; + +use super::{getsockopt, setsockopt, socket_addr_from_c, socket_addr_to_c}; +use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::net::{Shutdown, SocketAddr}; +use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd}; +use crate::sys::fd::FileDesc; +use crate::sys::time::Instant; +pub use crate::sys::{cvt, cvt_r}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::time::Duration; +use crate::{cmp, mem}; + +#[expect(non_camel_case_types)] +pub type wrlen_t = usize; + +pub fn cvt_gai(err: i32) -> io::Result<()> { + if err == 0 { + return Ok(()); + } + + let detail = ""; + + Err(io::Error::new( + io::ErrorKind::Uncategorized, + &format!("failed to lookup address information: {detail}")[..], + )) +} + +pub fn init() {} + +#[derive(Debug)] +pub struct Socket(FileDesc); + +impl Socket { + pub fn new(addr: &SocketAddr, ty: i32) -> io::Result { + let fam = match *addr { + SocketAddr::V4(..) => netc::AF_INET, + SocketAddr::V6(..) => netc::AF_INET6, + }; + Socket::new_raw(fam, ty) + } + + pub fn new_raw(fam: i32, ty: i32) -> io::Result { + let fd = cvt(unsafe { netc::socket(fam, ty, 0) })?; + Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) + } + + pub fn new_pair(_fam: i32, _ty: i32) -> io::Result<(Socket, Socket)> { + unimplemented!() + } + + pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = socket_addr_to_c(addr); + cvt_r(|| unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?; + Ok(()) + } + + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { + self.set_nonblocking(true)?; + let r = unsafe { + let (addr, len) = socket_addr_to_c(addr); + cvt(netc::connect(self.as_raw_fd(), addr.as_ptr(), len)) + }; + self.set_nonblocking(false)?; + + match r { + Ok(_) => return Ok(()), + // there's no ErrorKind for EINPROGRESS :( + Err(ref e) if e.raw_os_error() == Some(netc::errno::EINPROGRESS) => {} + Err(e) => return Err(e), + } + + let mut pollfd = netc::pollfd { fd: self.as_raw_fd(), events: netc::POLLOUT, revents: 0 }; + + if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { + return Err(io::Error::ZERO_TIMEOUT); + } + + let start = Instant::now(); + + loop { + let elapsed = start.elapsed(); + if elapsed >= timeout { + return Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")); + } + + let timeout = timeout - elapsed; + let mut timeout = timeout + .as_secs() + .saturating_mul(1_000) + .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); + if timeout == 0 { + timeout = 1; + } + + let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; + + match unsafe { netc::poll(&mut pollfd, 1, timeout) } { + -1 => { + let err = io::Error::last_os_error(); + if !err.is_interrupted() { + return Err(err); + } + } + 0 => {} + _ => { + // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look + // for POLLHUP rather than read readiness + if pollfd.revents & netc::POLLHUP != 0 { + let e = self.take_error()?.unwrap_or_else(|| { + io::const_error!( + io::ErrorKind::Uncategorized, + "no error set after POLLHUP", + ) + }); + return Err(e); + } + + return Ok(()); + } + } + } + } + + pub fn accept( + &self, + storage: *mut netc::sockaddr, + len: *mut netc::socklen_t, + ) -> io::Result { + let fd = cvt(unsafe { netc::accept(self.0.as_raw_fd(), storage, len) })?; + Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) + } + + pub fn duplicate(&self) -> io::Result { + let fd = cvt(unsafe { netc::dup(self.0.as_raw_fd()) })?; + Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) + } + + fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: i32) -> io::Result<()> { + let ret = cvt(unsafe { + netc::recv( + self.0.as_raw_fd(), + buf.as_mut().as_mut_ptr() as *mut u8, + buf.capacity(), + flags, + ) + })?; + unsafe { + buf.advance_unchecked(ret as usize); + } + Ok(()) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), 0)?; + Ok(buf.len()) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), netc::MSG_PEEK)?; + Ok(buf.len()) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.recv_with_flags(buf, 0) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + + fn recv_from_with_flags(&self, buf: &mut [u8], flags: i32) -> io::Result<(usize, SocketAddr)> { + let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() }; + let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t; + + let n = cvt(unsafe { + netc::recvfrom( + self.as_raw_fd(), + buf.as_mut_ptr(), + buf.len(), + flags, + (&raw mut storage) as *mut _, + &mut addrlen, + ) + })?; + Ok((n as usize, unsafe { socket_addr_from_c(&storage, addrlen as usize)? })) + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, 0) + } + + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, netc::MSG_PEEK) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + pub fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + + pub fn set_timeout(&self, dur: Option, kind: i32) -> io::Result<()> { + let timeout = match dur { + Some(dur) => { + if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { + return Err(io::Error::ZERO_TIMEOUT); + } + + let secs = if dur.as_secs() > netc::time_t::MAX as u64 { + netc::time_t::MAX + } else { + dur.as_secs() as netc::time_t + }; + let mut timeout = netc::timeval { + tv_sec: secs, + tv_usec: dur.subsec_micros() as netc::suseconds_t, + }; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + timeout + } + None => netc::timeval { tv_sec: 0, tv_usec: 0 }, + }; + + setsockopt(self, netc::SOL_SOCKET, kind, timeout) + } + + pub fn timeout(&self, kind: i32) -> io::Result> { + let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?; + if raw.tv_sec == 0 && raw.tv_usec == 0 { + Ok(None) + } else { + let sec = raw.tv_sec as u64; + let nsec = (raw.tv_usec as u32) * 1000; + Ok(Some(Duration::new(sec, nsec))) + } + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + let how = match how { + Shutdown::Write => netc::SHUT_WR, + Shutdown::Read => netc::SHUT_RD, + Shutdown::Both => netc::SHUT_RDWR, + }; + cvt(unsafe { netc::shutdown(self.as_raw_fd(), how) })?; + Ok(()) + } + + pub fn set_linger(&self, linger: Option) -> io::Result<()> { + let linger = netc::linger { + l_onoff: linger.is_some() as i32, + l_linger: linger.unwrap_or_default().as_secs() as libc::c_int, + }; + + setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger) + } + + pub fn linger(&self) -> io::Result> { + let val: netc::linger = getsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER)?; + + Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) + } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + let value: i32 = if nodelay { 1 } else { 0 }; + setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, value) + } + + pub fn nodelay(&self) -> io::Result { + let raw: i32 = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?; + Ok(raw != 0) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let mut nonblocking: i32 = if nonblocking { 1 } else { 0 }; + cvt(unsafe { + netc::ioctl( + self.as_raw_fd(), + netc::FIONBIO, + (&raw mut nonblocking) as *mut core::ffi::c_void, + ) + }) + .map(drop) + } + + pub fn take_error(&self) -> io::Result> { + unimplemented!() + } + + // This is used by sys_common code to abstract over Windows and Unix. + pub fn as_raw(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl AsInner for Socket { + #[inline] + fn as_inner(&self) -> &FileDesc { + &self.0 + } +} + +impl IntoInner for Socket { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +impl FromInner for Socket { + fn from_inner(file_desc: FileDesc) -> Self { + Self(file_desc) + } +} + +impl AsFd for Socket { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for Socket { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} diff --git a/library/std/src/sys/net/connection/socket/solid.rs b/library/std/src/sys/net/connection/socket/solid.rs new file mode 100644 index 0000000000000..906bef267b6f0 --- /dev/null +++ b/library/std/src/sys/net/connection/socket/solid.rs @@ -0,0 +1,413 @@ +use libc::{c_int, c_void, size_t}; + +use self::netc::{MSG_PEEK, sockaddr, socklen_t}; +use super::{getsockopt, setsockopt, socket_addr_from_c, socket_addr_to_c}; +use crate::ffi::CStr; +use crate::io::{self, BorrowedBuf, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; +use crate::net::{Shutdown, SocketAddr}; +use crate::os::solid::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}; +use crate::sys::abi; +use crate::sys_common::{FromInner, IntoInner}; +use crate::time::Duration; +use crate::{cmp, mem, ptr, str}; + +pub(super) mod netc { + pub use crate::sys::abi::sockets::*; +} + +#[expect(non_camel_case_types)] +pub type wrlen_t = size_t; + +const fn max_iov() -> usize { + // Judging by the source code, it's unlimited, but specify a lower + // value just in case. + 1024 +} + +#[doc(hidden)] +pub trait IsMinusOne { + fn is_minus_one(&self) -> bool; +} + +macro_rules! impl_is_minus_one { + ($($t:ident)*) => ($(impl IsMinusOne for $t { + fn is_minus_one(&self) -> bool { + *self == -1 + } + })*) +} + +impl_is_minus_one! { i8 i16 i32 i64 isize } + +pub fn cvt(t: T) -> io::Result { + if t.is_minus_one() { Err(last_error()) } else { Ok(t) } +} + +/// A variant of `cvt` for `getaddrinfo` which return 0 for a success. +pub fn cvt_gai(err: c_int) -> io::Result<()> { + if err == 0 { + Ok(()) + } else { + let msg: &dyn crate::fmt::Display = match err { + netc::EAI_NONAME => &"name or service not known", + netc::EAI_SERVICE => &"service not supported", + netc::EAI_FAIL => &"non-recoverable failure in name resolution", + netc::EAI_MEMORY => &"memory allocation failure", + netc::EAI_FAMILY => &"family not supported", + _ => &err, + }; + Err(io::Error::new( + io::ErrorKind::Uncategorized, + &format!("failed to lookup address information: {msg}")[..], + )) + } +} + +/// Just to provide the same interface as sys/pal/unix/net.rs +pub fn cvt_r(mut f: F) -> io::Result +where + T: IsMinusOne, + F: FnMut() -> T, +{ + cvt(f()) +} + +/// Returns the last error from the network subsystem. +fn last_error() -> io::Error { + io::Error::from_raw_os_error(unsafe { netc::SOLID_NET_GetLastError() }) +} + +pub fn error_name(er: abi::ER) -> Option<&'static str> { + unsafe { CStr::from_ptr(netc::strerror(er)) }.to_str().ok() +} + +#[inline] +pub fn is_interrupted(er: abi::ER) -> bool { + er == netc::SOLID_NET_ERR_BASE - libc::EINTR +} + +pub fn decode_error_kind(er: abi::ER) -> ErrorKind { + let errno = netc::SOLID_NET_ERR_BASE - er; + match errno as libc::c_int { + libc::ECONNREFUSED => ErrorKind::ConnectionRefused, + libc::ECONNRESET => ErrorKind::ConnectionReset, + libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied, + libc::EPIPE => ErrorKind::BrokenPipe, + libc::ENOTCONN => ErrorKind::NotConnected, + libc::ECONNABORTED => ErrorKind::ConnectionAborted, + libc::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable, + libc::EADDRINUSE => ErrorKind::AddrInUse, + libc::ENOENT => ErrorKind::NotFound, + libc::EINTR => ErrorKind::Interrupted, + libc::EINVAL => ErrorKind::InvalidInput, + libc::ETIMEDOUT => ErrorKind::TimedOut, + libc::EEXIST => ErrorKind::AlreadyExists, + libc::ENOSYS => ErrorKind::Unsupported, + libc::ENOMEM => ErrorKind::OutOfMemory, + libc::EAGAIN => ErrorKind::WouldBlock, + + _ => ErrorKind::Uncategorized, + } +} + +pub fn init() {} + +pub struct Socket(OwnedFd); + +impl Socket { + pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { + let fam = match *addr { + SocketAddr::V4(..) => netc::AF_INET, + SocketAddr::V6(..) => netc::AF_INET6, + }; + Socket::new_raw(fam, ty) + } + + pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { + unsafe { + let fd = cvt(netc::socket(fam, ty, 0))?; + Ok(Self::from_raw_fd(fd)) + } + } + + pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = socket_addr_to_c(addr); + cvt(unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?; + Ok(()) + } + + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { + self.set_nonblocking(true)?; + let r = self.connect(addr); + self.set_nonblocking(false)?; + + match r { + Ok(_) => return Ok(()), + // there's no ErrorKind for EINPROGRESS + Err(ref e) if e.raw_os_error() == Some(netc::EINPROGRESS) => {} + Err(e) => return Err(e), + } + + if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { + return Err(io::Error::ZERO_TIMEOUT); + } + + let mut timeout = + netc::timeval { tv_sec: timeout.as_secs() as _, tv_usec: timeout.subsec_micros() as _ }; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + + let fds = netc::fd_set { num_fds: 1, fds: [self.as_raw_fd()] }; + + let mut writefds = fds; + let mut errorfds = fds; + + let n = unsafe { + cvt(netc::select( + self.as_raw_fd() + 1, + ptr::null_mut(), + &mut writefds, + &mut errorfds, + &mut timeout, + ))? + }; + + match n { + 0 => Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")), + _ => { + let can_write = writefds.num_fds != 0; + if !can_write { + if let Some(e) = self.take_error()? { + return Err(e); + } + } + Ok(()) + } + } + } + + pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result { + let fd = cvt_r(|| unsafe { netc::accept(self.as_raw_fd(), storage, len) })?; + unsafe { Ok(Self::from_raw_fd(fd)) } + } + + pub fn duplicate(&self) -> io::Result { + Ok(Self(self.0.try_clone()?)) + } + + fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { + let ret = cvt(unsafe { + netc::recv(self.as_raw_fd(), buf.as_mut().as_mut_ptr().cast(), buf.capacity(), flags) + })?; + unsafe { + buf.advance_unchecked(ret as usize); + } + Ok(()) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), 0)?; + Ok(buf.len()) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), MSG_PEEK)?; + Ok(buf.len()) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.recv_with_flags(buf, 0) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + let ret = cvt(unsafe { + netc::readv( + self.as_raw_fd(), + bufs.as_ptr() as *const netc::iovec, + cmp::min(bufs.len(), max_iov()) as c_int, + ) + })?; + Ok(ret as usize) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + true + } + + fn recv_from_with_flags( + &self, + buf: &mut [u8], + flags: c_int, + ) -> io::Result<(usize, SocketAddr)> { + let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() }; + let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t; + + let n = cvt(unsafe { + netc::recvfrom( + self.as_raw_fd(), + buf.as_mut_ptr() as *mut c_void, + buf.len(), + flags, + &mut storage as *mut _ as *mut _, + &mut addrlen, + ) + })?; + Ok((n as usize, unsafe { socket_addr_from_c(&storage, addrlen as usize)? })) + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, 0) + } + + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, MSG_PEEK) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + let ret = cvt(unsafe { + netc::writev( + self.as_raw_fd(), + bufs.as_ptr() as *const netc::iovec, + cmp::min(bufs.len(), max_iov()) as c_int, + ) + })?; + Ok(ret as usize) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + + pub fn set_timeout(&self, dur: Option, kind: c_int) -> io::Result<()> { + let timeout = match dur { + Some(dur) => { + if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { + return Err(io::Error::ZERO_TIMEOUT); + } + + let secs = if dur.as_secs() > netc::c_long::MAX as u64 { + netc::c_long::MAX + } else { + dur.as_secs() as netc::c_long + }; + let mut timeout = netc::timeval { tv_sec: secs, tv_usec: dur.subsec_micros() as _ }; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + timeout + } + None => netc::timeval { tv_sec: 0, tv_usec: 0 }, + }; + setsockopt(self, netc::SOL_SOCKET, kind, timeout) + } + + pub fn timeout(&self, kind: c_int) -> io::Result> { + let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?; + if raw.tv_sec == 0 && raw.tv_usec == 0 { + Ok(None) + } else { + let sec = raw.tv_sec as u64; + let nsec = (raw.tv_usec as u32) * 1000; + Ok(Some(Duration::new(sec, nsec))) + } + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + let how = match how { + Shutdown::Write => netc::SHUT_WR, + Shutdown::Read => netc::SHUT_RD, + Shutdown::Both => netc::SHUT_RDWR, + }; + cvt(unsafe { netc::shutdown(self.as_raw_fd(), how) })?; + Ok(()) + } + + pub fn set_linger(&self, linger: Option) -> io::Result<()> { + let linger = netc::linger { + l_onoff: linger.is_some() as netc::c_int, + l_linger: linger.unwrap_or_default().as_secs() as netc::c_int, + }; + + setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger) + } + + pub fn linger(&self) -> io::Result> { + let val: netc::linger = getsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER)?; + + Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) + } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int) + } + + pub fn nodelay(&self) -> io::Result { + let raw: c_int = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?; + Ok(raw != 0) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let mut nonblocking = nonblocking as c_int; + cvt(unsafe { + netc::ioctl(self.as_raw_fd(), netc::FIONBIO, (&mut nonblocking) as *mut c_int as _) + }) + .map(drop) + } + + pub fn take_error(&self) -> io::Result> { + let raw: c_int = getsockopt(self, netc::SOL_SOCKET, netc::SO_ERROR)?; + if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } + } + + // This method is used by sys_common code to abstract over targets. + pub fn as_raw(&self) -> c_int { + self.as_raw_fd() + } +} + +impl FromInner for Socket { + #[inline] + fn from_inner(sock: OwnedFd) -> Socket { + Socket(sock) + } +} + +impl IntoInner for Socket { + #[inline] + fn into_inner(self) -> OwnedFd { + self.0 + } +} + +impl AsFd for Socket { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for Socket { + #[inline] + fn as_raw_fd(&self) -> c_int { + self.0.as_raw_fd() + } +} + +impl FromRawFd for Socket { + #[inline] + unsafe fn from_raw_fd(fd: c_int) -> Socket { + unsafe { Self(FromRawFd::from_raw_fd(fd)) } + } +} + +impl IntoRawFd for Socket { + #[inline] + fn into_raw_fd(self) -> c_int { + self.0.into_raw_fd() + } +} diff --git a/library/std/src/sys_common/net/tests.rs b/library/std/src/sys/net/connection/socket/tests.rs similarity index 100% rename from library/std/src/sys_common/net/tests.rs rename to library/std/src/sys/net/connection/socket/tests.rs diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs new file mode 100644 index 0000000000000..34ab26bc117af --- /dev/null +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -0,0 +1,652 @@ +use libc::{MSG_PEEK, c_int, c_void, size_t, sockaddr, socklen_t}; + +use crate::ffi::CStr; +use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::net::{Shutdown, SocketAddr}; +use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; +use crate::sys::fd::FileDesc; +use crate::sys::net::{getsockopt, setsockopt}; +use crate::sys::pal::IsMinusOne; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::time::{Duration, Instant}; +use crate::{cmp, mem}; + +cfg_if::cfg_if! { + if #[cfg(target_vendor = "apple")] { + use libc::SO_LINGER_SEC as SO_LINGER; + } else { + use libc::SO_LINGER; + } +} + +pub(super) use libc as netc; + +use super::{socket_addr_from_c, socket_addr_to_c}; +pub use crate::sys::{cvt, cvt_r}; + +#[expect(non_camel_case_types)] +pub type wrlen_t = size_t; + +pub struct Socket(FileDesc); + +pub fn init() {} + +pub fn cvt_gai(err: c_int) -> io::Result<()> { + if err == 0 { + return Ok(()); + } + + // We may need to trigger a glibc workaround. See on_resolver_failure() for details. + on_resolver_failure(); + + #[cfg(not(any(target_os = "espidf", target_os = "nuttx")))] + if err == libc::EAI_SYSTEM { + return Err(io::Error::last_os_error()); + } + + #[cfg(not(any(target_os = "espidf", target_os = "nuttx")))] + let detail = unsafe { + // We can't always expect a UTF-8 environment. When we don't get that luxury, + // it's better to give a low-quality error message than none at all. + CStr::from_ptr(libc::gai_strerror(err)).to_string_lossy() + }; + + #[cfg(any(target_os = "espidf", target_os = "nuttx"))] + let detail = ""; + + Err(io::Error::new( + io::ErrorKind::Uncategorized, + &format!("failed to lookup address information: {detail}")[..], + )) +} + +impl Socket { + pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { + let fam = match *addr { + SocketAddr::V4(..) => libc::AF_INET, + SocketAddr::V6(..) => libc::AF_INET6, + }; + Socket::new_raw(fam, ty) + } + + pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { + unsafe { + cfg_if::cfg_if! { + if #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "nto", + target_os = "solaris", + ))] { + // On platforms that support it we pass the SOCK_CLOEXEC + // flag to atomically create the socket and set it as + // CLOEXEC. On Linux this was added in 2.6.27. + let fd = cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0))?; + let socket = Socket(FileDesc::from_raw_fd(fd)); + + // DragonFlyBSD, FreeBSD and NetBSD use `SO_NOSIGPIPE` as a `setsockopt` + // flag to disable `SIGPIPE` emission on socket. + #[cfg(any(target_os = "freebsd", target_os = "netbsd", target_os = "dragonfly"))] + setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?; + + Ok(socket) + } else { + let fd = cvt(libc::socket(fam, ty, 0))?; + let fd = FileDesc::from_raw_fd(fd); + fd.set_cloexec()?; + let socket = Socket(fd); + + // macOS and iOS use `SO_NOSIGPIPE` as a `setsockopt` + // flag to disable `SIGPIPE` emission on socket. + #[cfg(target_vendor = "apple")] + setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?; + + Ok(socket) + } + } + } + } + + #[cfg(not(target_os = "vxworks"))] + pub fn new_pair(fam: c_int, ty: c_int) -> io::Result<(Socket, Socket)> { + unsafe { + let mut fds = [0, 0]; + + cfg_if::cfg_if! { + if #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "hurd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "nto", + ))] { + // Like above, set cloexec atomically + cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()))?; + Ok((Socket(FileDesc::from_raw_fd(fds[0])), Socket(FileDesc::from_raw_fd(fds[1])))) + } else { + cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?; + let a = FileDesc::from_raw_fd(fds[0]); + let b = FileDesc::from_raw_fd(fds[1]); + a.set_cloexec()?; + b.set_cloexec()?; + Ok((Socket(a), Socket(b))) + } + } + } + } + + #[cfg(target_os = "vxworks")] + pub fn new_pair(_fam: c_int, _ty: c_int) -> io::Result<(Socket, Socket)> { + unimplemented!() + } + + pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = socket_addr_to_c(addr); + loop { + let result = unsafe { libc::connect(self.as_raw_fd(), addr.as_ptr(), len) }; + if result.is_minus_one() { + let err = crate::sys::os::errno(); + match err { + libc::EINTR => continue, + libc::EISCONN => return Ok(()), + _ => return Err(io::Error::from_raw_os_error(err)), + } + } + return Ok(()); + } + } + + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { + self.set_nonblocking(true)?; + let r = unsafe { + let (addr, len) = socket_addr_to_c(addr); + cvt(libc::connect(self.as_raw_fd(), addr.as_ptr(), len)) + }; + self.set_nonblocking(false)?; + + match r { + Ok(_) => return Ok(()), + // there's no ErrorKind for EINPROGRESS :( + Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {} + Err(e) => return Err(e), + } + + let mut pollfd = libc::pollfd { fd: self.as_raw_fd(), events: libc::POLLOUT, revents: 0 }; + + if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { + return Err(io::Error::ZERO_TIMEOUT); + } + + let start = Instant::now(); + + loop { + let elapsed = start.elapsed(); + if elapsed >= timeout { + return Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")); + } + + let timeout = timeout - elapsed; + let mut timeout = timeout + .as_secs() + .saturating_mul(1_000) + .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); + if timeout == 0 { + timeout = 1; + } + + let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; + + match unsafe { libc::poll(&mut pollfd, 1, timeout) } { + -1 => { + let err = io::Error::last_os_error(); + if !err.is_interrupted() { + return Err(err); + } + } + 0 => {} + _ => { + if cfg!(target_os = "vxworks") { + // VxWorks poll does not return POLLHUP or POLLERR in revents. Check if the + // connection actually succeeded and return ok only when the socket is + // ready and no errors were found. + if let Some(e) = self.take_error()? { + return Err(e); + } + } else { + // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look + // for POLLHUP or POLLERR rather than read readiness + if pollfd.revents & (libc::POLLHUP | libc::POLLERR) != 0 { + let e = self.take_error()?.unwrap_or_else(|| { + io::const_error!( + io::ErrorKind::Uncategorized, + "no error set after POLLHUP", + ) + }); + return Err(e); + } + } + + return Ok(()); + } + } + } + } + + pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result { + // Unfortunately the only known way right now to accept a socket and + // atomically set the CLOEXEC flag is to use the `accept4` syscall on + // platforms that support it. On Linux, this was added in 2.6.28, + // glibc 2.10 and musl 0.9.5. + cfg_if::cfg_if! { + if #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "hurd", + target_os = "netbsd", + target_os = "openbsd", + ))] { + unsafe { + let fd = cvt_r(|| libc::accept4(self.as_raw_fd(), storage, len, libc::SOCK_CLOEXEC))?; + Ok(Socket(FileDesc::from_raw_fd(fd))) + } + } else { + unsafe { + let fd = cvt_r(|| libc::accept(self.as_raw_fd(), storage, len))?; + let fd = FileDesc::from_raw_fd(fd); + fd.set_cloexec()?; + Ok(Socket(fd)) + } + } + } + } + + pub fn duplicate(&self) -> io::Result { + self.0.duplicate().map(Socket) + } + + fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { + let ret = cvt(unsafe { + libc::recv( + self.as_raw_fd(), + buf.as_mut().as_mut_ptr() as *mut c_void, + buf.capacity(), + flags, + ) + })?; + unsafe { + buf.advance_unchecked(ret as usize); + } + Ok(()) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), 0)?; + Ok(buf.len()) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), MSG_PEEK)?; + Ok(buf.len()) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.recv_with_flags(buf, 0) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + + fn recv_from_with_flags( + &self, + buf: &mut [u8], + flags: c_int, + ) -> io::Result<(usize, SocketAddr)> { + let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; + let mut addrlen = mem::size_of_val(&storage) as libc::socklen_t; + + let n = cvt(unsafe { + libc::recvfrom( + self.as_raw_fd(), + buf.as_mut_ptr() as *mut c_void, + buf.len(), + flags, + (&raw mut storage) as *mut _, + &mut addrlen, + ) + })?; + Ok((n as usize, unsafe { socket_addr_from_c(&storage, addrlen as usize)? })) + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, 0) + } + + #[cfg(any(target_os = "android", target_os = "linux"))] + pub fn recv_msg(&self, msg: &mut libc::msghdr) -> io::Result { + let n = cvt(unsafe { libc::recvmsg(self.as_raw_fd(), msg, libc::MSG_CMSG_CLOEXEC) })?; + Ok(n as usize) + } + + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, MSG_PEEK) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + + #[cfg(any(target_os = "android", target_os = "linux"))] + pub fn send_msg(&self, msg: &mut libc::msghdr) -> io::Result { + let n = cvt(unsafe { libc::sendmsg(self.as_raw_fd(), msg, 0) })?; + Ok(n as usize) + } + + pub fn set_timeout(&self, dur: Option, kind: libc::c_int) -> io::Result<()> { + let timeout = match dur { + Some(dur) => { + if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { + return Err(io::Error::ZERO_TIMEOUT); + } + + let secs = if dur.as_secs() > libc::time_t::MAX as u64 { + libc::time_t::MAX + } else { + dur.as_secs() as libc::time_t + }; + let mut timeout = libc::timeval { + tv_sec: secs, + tv_usec: dur.subsec_micros() as libc::suseconds_t, + }; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + timeout + } + None => libc::timeval { tv_sec: 0, tv_usec: 0 }, + }; + setsockopt(self, libc::SOL_SOCKET, kind, timeout) + } + + pub fn timeout(&self, kind: libc::c_int) -> io::Result> { + let raw: libc::timeval = getsockopt(self, libc::SOL_SOCKET, kind)?; + if raw.tv_sec == 0 && raw.tv_usec == 0 { + Ok(None) + } else { + let sec = raw.tv_sec as u64; + let nsec = (raw.tv_usec as u32) * 1000; + Ok(Some(Duration::new(sec, nsec))) + } + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + let how = match how { + Shutdown::Write => libc::SHUT_WR, + Shutdown::Read => libc::SHUT_RD, + Shutdown::Both => libc::SHUT_RDWR, + }; + cvt(unsafe { libc::shutdown(self.as_raw_fd(), how) })?; + Ok(()) + } + + pub fn set_linger(&self, linger: Option) -> io::Result<()> { + let linger = libc::linger { + l_onoff: linger.is_some() as libc::c_int, + l_linger: linger.unwrap_or_default().as_secs() as libc::c_int, + }; + + setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger) + } + + pub fn linger(&self) -> io::Result> { + let val: libc::linger = getsockopt(self, libc::SOL_SOCKET, SO_LINGER)?; + + Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) + } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) + } + + pub fn nodelay(&self) -> io::Result { + let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY)?; + Ok(raw != 0) + } + + #[cfg(any(target_os = "android", target_os = "linux",))] + pub fn set_quickack(&self, quickack: bool) -> io::Result<()> { + setsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK, quickack as c_int) + } + + #[cfg(any(target_os = "android", target_os = "linux",))] + pub fn quickack(&self) -> io::Result { + let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK)?; + Ok(raw != 0) + } + + // bionic libc makes no use of this flag + #[cfg(target_os = "linux")] + pub fn set_deferaccept(&self, accept: u32) -> io::Result<()> { + setsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT, accept as c_int) + } + + #[cfg(target_os = "linux")] + pub fn deferaccept(&self) -> io::Result { + let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT)?; + Ok(raw as u32) + } + + #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] + pub fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { + if !name.to_bytes().is_empty() { + const AF_NAME_MAX: usize = 16; + let mut buf = [0; AF_NAME_MAX]; + for (src, dst) in name.to_bytes().iter().zip(&mut buf[..AF_NAME_MAX - 1]) { + *dst = *src as libc::c_char; + } + let mut arg: libc::accept_filter_arg = unsafe { mem::zeroed() }; + arg.af_name = buf; + setsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER, &mut arg) + } else { + setsockopt( + self, + libc::SOL_SOCKET, + libc::SO_ACCEPTFILTER, + core::ptr::null_mut() as *mut c_void, + ) + } + } + + #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] + pub fn acceptfilter(&self) -> io::Result<&CStr> { + let arg: libc::accept_filter_arg = + getsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER)?; + let s: &[u8] = + unsafe { core::slice::from_raw_parts(arg.af_name.as_ptr() as *const u8, 16) }; + let name = CStr::from_bytes_with_nul(s).unwrap(); + Ok(name) + } + + #[cfg(any(target_os = "android", target_os = "linux",))] + pub fn set_passcred(&self, passcred: bool) -> io::Result<()> { + setsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED, passcred as libc::c_int) + } + + #[cfg(any(target_os = "android", target_os = "linux",))] + pub fn passcred(&self) -> io::Result { + let passcred: libc::c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED)?; + Ok(passcred != 0) + } + + #[cfg(target_os = "netbsd")] + pub fn set_local_creds(&self, local_creds: bool) -> io::Result<()> { + setsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS, local_creds as libc::c_int) + } + + #[cfg(target_os = "netbsd")] + pub fn local_creds(&self) -> io::Result { + let local_creds: libc::c_int = getsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS)?; + Ok(local_creds != 0) + } + + #[cfg(target_os = "freebsd")] + pub fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()> { + setsockopt( + self, + libc::AF_LOCAL, + libc::LOCAL_CREDS_PERSISTENT, + local_creds_persistent as libc::c_int, + ) + } + + #[cfg(target_os = "freebsd")] + pub fn local_creds_persistent(&self) -> io::Result { + let local_creds_persistent: libc::c_int = + getsockopt(self, libc::AF_LOCAL, libc::LOCAL_CREDS_PERSISTENT)?; + Ok(local_creds_persistent != 0) + } + + #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "vita")))] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let mut nonblocking = nonblocking as libc::c_int; + cvt(unsafe { libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &mut nonblocking) }).map(drop) + } + + #[cfg(target_os = "vita")] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let option = nonblocking as libc::c_int; + setsockopt(self, libc::SOL_SOCKET, libc::SO_NONBLOCK, option) + } + + #[cfg(any(target_os = "solaris", target_os = "illumos"))] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + // FIONBIO is inadequate for sockets on illumos/Solaris, so use the + // fcntl(F_[GS]ETFL)-based method provided by FileDesc instead. + self.0.set_nonblocking(nonblocking) + } + + #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))] + pub fn set_mark(&self, mark: u32) -> io::Result<()> { + #[cfg(target_os = "linux")] + let option = libc::SO_MARK; + #[cfg(target_os = "freebsd")] + let option = libc::SO_USER_COOKIE; + #[cfg(target_os = "openbsd")] + let option = libc::SO_RTABLE; + setsockopt(self, libc::SOL_SOCKET, option, mark as libc::c_int) + } + + pub fn take_error(&self) -> io::Result> { + let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; + if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } + } + + // This is used by sys_common code to abstract over Windows and Unix. + pub fn as_raw(&self) -> RawFd { + self.as_raw_fd() + } +} + +impl AsInner for Socket { + #[inline] + fn as_inner(&self) -> &FileDesc { + &self.0 + } +} + +impl IntoInner for Socket { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +impl FromInner for Socket { + fn from_inner(file_desc: FileDesc) -> Self { + Self(file_desc) + } +} + +impl AsFd for Socket { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for Socket { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for Socket { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for Socket { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(FromRawFd::from_raw_fd(raw_fd)) + } +} + +// In versions of glibc prior to 2.26, there's a bug where the DNS resolver +// will cache the contents of /etc/resolv.conf, so changes to that file on disk +// can be ignored by a long-running program. That can break DNS lookups on e.g. +// laptops where the network comes and goes. See +// https://sourceware.org/bugzilla/show_bug.cgi?id=984. Note however that some +// distros including Debian have patched glibc to fix this for a long time. +// +// A workaround for this bug is to call the res_init libc function, to clear +// the cached configs. Unfortunately, while we believe glibc's implementation +// of res_init is thread-safe, we know that other implementations are not +// (https://github.com/rust-lang/rust/issues/43592). Code here in std could +// try to synchronize its res_init calls with a Mutex, but that wouldn't +// protect programs that call into libc in other ways. So instead of calling +// res_init unconditionally, we call it only when we detect we're linking +// against glibc version < 2.26. (That is, when we both know its needed and +// believe it's thread-safe). +#[cfg(all(target_os = "linux", target_env = "gnu"))] +fn on_resolver_failure() { + use crate::sys; + + // If the version fails to parse, we treat it the same as "not glibc". + if let Some(version) = sys::os::glibc_version() { + if version < (2, 26) { + unsafe { libc::res_init() }; + } + } +} + +#[cfg(not(all(target_os = "linux", target_env = "gnu")))] +fn on_resolver_failure() {} diff --git a/library/std/src/sys/net/connection/socket/wasip2.rs b/library/std/src/sys/net/connection/socket/wasip2.rs new file mode 100644 index 0000000000000..c5034e73dd704 --- /dev/null +++ b/library/std/src/sys/net/connection/socket/wasip2.rs @@ -0,0 +1,416 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +pub(super) use libc as netc; +use libc::{c_int, c_void, size_t}; + +use super::{getsockopt, setsockopt, socket_addr_from_c, socket_addr_to_c}; +use crate::ffi::CStr; +use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::net::{Shutdown, SocketAddr}; +use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::sys::unsupported; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::time::{Duration, Instant}; +use crate::{cmp, mem, str}; + +#[allow(non_camel_case_types)] +pub type wrlen_t = size_t; + +#[doc(hidden)] +pub trait IsMinusOne { + fn is_minus_one(&self) -> bool; +} + +macro_rules! impl_is_minus_one { + ($($t:ident)*) => ($(impl IsMinusOne for $t { + fn is_minus_one(&self) -> bool { + *self == -1 + } + })*) +} + +impl_is_minus_one! { i8 i16 i32 i64 isize } + +pub fn cvt(t: T) -> crate::io::Result { + if t.is_minus_one() { Err(crate::io::Error::last_os_error()) } else { Ok(t) } +} + +pub fn cvt_r(mut f: F) -> crate::io::Result +where + T: IsMinusOne, + F: FnMut() -> T, +{ + loop { + match cvt(f()) { + Err(ref e) if e.is_interrupted() => {} + other => return other, + } + } +} + +pub fn cvt_gai(err: c_int) -> io::Result<()> { + if err == 0 { + return Ok(()); + } + + if err == netc::EAI_SYSTEM { + return Err(io::Error::last_os_error()); + } + + let detail = unsafe { + str::from_utf8(CStr::from_ptr(netc::gai_strerror(err)).to_bytes()).unwrap().to_owned() + }; + + Err(io::Error::new( + io::ErrorKind::Uncategorized, + &format!("failed to lookup address information: {detail}")[..], + )) +} + +pub fn init() {} + +pub struct WasiSocket(OwnedFd); + +pub struct Socket(WasiSocket); + +impl Socket { + pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { + let fam = match *addr { + SocketAddr::V4(..) => netc::AF_INET, + SocketAddr::V6(..) => netc::AF_INET6, + }; + Socket::new_raw(fam, ty) + } + + pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { + let fd = cvt(unsafe { netc::socket(fam, ty, 0) })?; + Ok(unsafe { Self::from_raw_fd(fd) }) + } + + pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = socket_addr_to_c(addr); + cvt_r(|| unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?; + Ok(()) + } + + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { + self.set_nonblocking(true)?; + let r = self.connect(addr); + self.set_nonblocking(false)?; + + match r { + Ok(_) => return Ok(()), + // there's no ErrorKind for EINPROGRESS + Err(ref e) if e.raw_os_error() == Some(netc::EINPROGRESS) => {} + Err(e) => return Err(e), + } + + let mut pollfd = netc::pollfd { fd: self.as_raw_fd(), events: netc::POLLOUT, revents: 0 }; + + if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { + return Err(io::Error::ZERO_TIMEOUT); + } + + let start = Instant::now(); + + loop { + let elapsed = start.elapsed(); + if elapsed >= timeout { + return Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")); + } + + let timeout = timeout - elapsed; + let mut timeout = timeout + .as_secs() + .saturating_mul(1_000) + .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); + if timeout == 0 { + timeout = 1; + } + + let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; + + match unsafe { netc::poll(&mut pollfd, 1, timeout) } { + -1 => { + let err = io::Error::last_os_error(); + if !err.is_interrupted() { + return Err(err); + } + } + 0 => {} + _ => { + // WASI poll does not return POLLHUP or POLLERR in revents. Check if the + // connnection actually succeeded and return ok only when the socket is + // ready and no errors were found. + if let Some(e) = self.take_error()? { + return Err(e); + } + + return Ok(()); + } + } + } + } + + pub fn accept( + &self, + storage: *mut netc::sockaddr, + len: *mut netc::socklen_t, + ) -> io::Result { + let fd = cvt_r(|| unsafe { netc::accept(self.as_raw_fd(), storage, len) })?; + Ok(unsafe { Self::from_raw_fd(fd) }) + } + + pub fn duplicate(&self) -> io::Result { + unsupported() + } + + fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { + let ret = cvt(unsafe { + netc::recv( + self.as_raw_fd(), + buf.as_mut().as_mut_ptr() as *mut c_void, + buf.capacity(), + flags, + ) + })?; + unsafe { + buf.advance_unchecked(ret as usize); + } + Ok(()) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), 0)?; + Ok(buf.len()) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), netc::MSG_PEEK)?; + Ok(buf.len()) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.recv_with_flags(buf, 0) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + io::default_read_vectored(|b| self.read(b), bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + false + } + + fn recv_from_with_flags( + &self, + buf: &mut [u8], + flags: c_int, + ) -> io::Result<(usize, SocketAddr)> { + let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() }; + let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t; + + let n = cvt(unsafe { + netc::recvfrom( + self.as_raw_fd(), + buf.as_mut_ptr() as *mut c_void, + buf.len(), + flags, + core::ptr::addr_of_mut!(storage) as *mut _, + &mut addrlen, + ) + })?; + Ok((n as usize, unsafe { socket_addr_from_c(&storage, addrlen as usize)? })) + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, 0) + } + + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, netc::MSG_PEEK) + } + + fn write(&self, buf: &[u8]) -> io::Result { + let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; + let ret = cvt(unsafe { + netc::send(self.as_raw(), buf.as_ptr() as *const c_void, len, netc::MSG_NOSIGNAL) + })?; + Ok(ret as usize) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + io::default_write_vectored(|b| self.write(b), bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + false + } + + pub fn set_timeout(&self, dur: Option, kind: c_int) -> io::Result<()> { + let timeout = match dur { + Some(dur) => { + if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { + return Err(io::Error::ZERO_TIMEOUT); + } + + let secs = dur.as_secs().try_into().unwrap_or(netc::time_t::MAX); + let mut timeout = netc::timeval { + tv_sec: secs, + tv_usec: dur.subsec_micros() as netc::suseconds_t, + }; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + timeout + } + None => netc::timeval { tv_sec: 0, tv_usec: 0 }, + }; + setsockopt(self, netc::SOL_SOCKET, kind, timeout) + } + + pub fn timeout(&self, kind: c_int) -> io::Result> { + let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?; + if raw.tv_sec == 0 && raw.tv_usec == 0 { + Ok(None) + } else { + let sec = raw.tv_sec as u64; + let nsec = (raw.tv_usec as u32) * 1000; + Ok(Some(Duration::new(sec, nsec))) + } + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + let how = match how { + Shutdown::Write => netc::SHUT_WR, + Shutdown::Read => netc::SHUT_RD, + Shutdown::Both => netc::SHUT_RDWR, + }; + cvt(unsafe { netc::shutdown(self.as_raw_fd(), how) })?; + Ok(()) + } + + pub fn set_linger(&self, _linger: Option) -> io::Result<()> { + unsupported() + } + + pub fn linger(&self) -> io::Result> { + unsupported() + } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int) + } + + pub fn nodelay(&self) -> io::Result { + let raw: c_int = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?; + Ok(raw != 0) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let mut nonblocking = nonblocking as c_int; + cvt(unsafe { netc::ioctl(self.as_raw_fd(), netc::FIONBIO, &mut nonblocking) }).map(drop) + } + + pub fn take_error(&self) -> io::Result> { + let raw: c_int = getsockopt(self, netc::SOL_SOCKET, netc::SO_ERROR)?; + if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } + } + + // This is used by sys_common code to abstract over Windows and Unix. + pub fn as_raw(&self) -> RawFd { + self.as_raw_fd() + } +} + +impl AsInner for WasiSocket { + #[inline] + fn as_inner(&self) -> &OwnedFd { + &self.0 + } +} + +impl IntoInner for WasiSocket { + fn into_inner(self) -> OwnedFd { + self.0 + } +} + +impl FromInner for WasiSocket { + fn from_inner(owned_fd: OwnedFd) -> Self { + Self(owned_fd) + } +} + +impl AsFd for WasiSocket { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for WasiSocket { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for WasiSocket { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for WasiSocket { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) } + } +} + +impl AsInner for Socket { + #[inline] + fn as_inner(&self) -> &WasiSocket { + &self.0 + } +} + +impl IntoInner for Socket { + fn into_inner(self) -> WasiSocket { + self.0 + } +} + +impl FromInner for Socket { + fn from_inner(sock: WasiSocket) -> Socket { + Socket(sock) + } +} + +impl AsFd for Socket { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for Socket { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for Socket { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for Socket { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) } + } +} diff --git a/library/std/src/sys/net/connection/socket/windows.rs b/library/std/src/sys/net/connection/socket/windows.rs new file mode 100644 index 0000000000000..428f142dabe20 --- /dev/null +++ b/library/std/src/sys/net/connection/socket/windows.rs @@ -0,0 +1,576 @@ +#![unstable(issue = "none", feature = "windows_net")] + +use core::ffi::{c_int, c_long, c_ulong, c_ushort}; + +use super::{getsockopt, setsockopt, socket_addr_from_c, socket_addr_to_c}; +use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut, Read}; +use crate::net::{Shutdown, SocketAddr}; +use crate::os::windows::io::{ + AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket, +}; +use crate::sync::OnceLock; +use crate::sys::c; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::time::Duration; +use crate::{cmp, mem, ptr, sys}; + +#[allow(non_camel_case_types)] +pub type wrlen_t = i32; + +pub(super) mod netc { + //! BSD socket compatibility shim + //! + //! Some Windows API types are not quite what's expected by our cross-platform + //! net code. E.g. naming differences or different pointer types. + + use core::ffi::{c_char, c_int, c_uint, c_ulong, c_ushort, c_void}; + + use crate::sys::c::{self, ADDRESS_FAMILY, ADDRINFOA, SOCKADDR, SOCKET}; + // re-exports from Windows API bindings. + pub use crate::sys::c::{ + ADDRESS_FAMILY as sa_family_t, ADDRINFOA as addrinfo, IP_ADD_MEMBERSHIP, + IP_DROP_MEMBERSHIP, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TTL, IPPROTO_IP, IPPROTO_IPV6, + IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MULTICAST_LOOP, IPV6_V6ONLY, SO_BROADCAST, + SO_RCVTIMEO, SO_SNDTIMEO, SOCK_DGRAM, SOCK_STREAM, SOCKADDR as sockaddr, + SOCKADDR_STORAGE as sockaddr_storage, SOL_SOCKET, bind, connect, freeaddrinfo, getpeername, + getsockname, getsockopt, listen, setsockopt, + }; + + #[allow(non_camel_case_types)] + pub type socklen_t = c_int; + + pub const AF_INET: i32 = c::AF_INET as i32; + pub const AF_INET6: i32 = c::AF_INET6 as i32; + + // The following two structs use a union in the generated bindings but + // our cross-platform code expects a normal field so it's redefined here. + // As a consequence, we also need to redefine other structs that use this struct. + #[repr(C)] + #[derive(Copy, Clone)] + pub struct in_addr { + pub s_addr: u32, + } + + #[repr(C)] + #[derive(Copy, Clone)] + pub struct in6_addr { + pub s6_addr: [u8; 16], + } + + #[repr(C)] + pub struct ip_mreq { + pub imr_multiaddr: in_addr, + pub imr_interface: in_addr, + } + + #[repr(C)] + pub struct ipv6_mreq { + pub ipv6mr_multiaddr: in6_addr, + pub ipv6mr_interface: c_uint, + } + + #[repr(C)] + #[derive(Copy, Clone)] + pub struct sockaddr_in { + pub sin_family: ADDRESS_FAMILY, + pub sin_port: c_ushort, + pub sin_addr: in_addr, + pub sin_zero: [c_char; 8], + } + + #[repr(C)] + #[derive(Copy, Clone)] + pub struct sockaddr_in6 { + pub sin6_family: ADDRESS_FAMILY, + pub sin6_port: c_ushort, + pub sin6_flowinfo: c_ulong, + pub sin6_addr: in6_addr, + pub sin6_scope_id: c_ulong, + } + + pub unsafe fn send(socket: SOCKET, buf: *const c_void, len: c_int, flags: c_int) -> c_int { + unsafe { c::send(socket, buf.cast::(), len, flags) } + } + pub unsafe fn sendto( + socket: SOCKET, + buf: *const c_void, + len: c_int, + flags: c_int, + addr: *const SOCKADDR, + addrlen: c_int, + ) -> c_int { + unsafe { c::sendto(socket, buf.cast::(), len, flags, addr, addrlen) } + } + pub unsafe fn getaddrinfo( + node: *const c_char, + service: *const c_char, + hints: *const ADDRINFOA, + res: *mut *mut ADDRINFOA, + ) -> c_int { + unsafe { c::getaddrinfo(node.cast::(), service.cast::(), hints, res) } + } +} + +#[expect(missing_debug_implementations)] +pub struct Socket(OwnedSocket); + +static WSA_CLEANUP: OnceLock i32> = OnceLock::new(); + +/// Checks whether the Windows socket interface has been started already, and +/// if not, starts it. +pub fn init() { + let _ = WSA_CLEANUP.get_or_init(|| unsafe { + let mut data: c::WSADATA = mem::zeroed(); + let ret = c::WSAStartup( + 0x202, // version 2.2 + &mut data, + ); + assert_eq!(ret, 0); + + // Only register `WSACleanup` if `WSAStartup` is actually ever called. + // Workaround to prevent linking to `WS2_32.dll` when no network functionality is used. + // See issue #85441. + c::WSACleanup + }); +} + +pub fn cleanup() { + // only perform cleanup if network functionality was actually initialized + if let Some(cleanup) = WSA_CLEANUP.get() { + unsafe { + cleanup(); + } + } +} + +/// Returns the last error from the Windows socket interface. +fn last_error() -> io::Error { + io::Error::from_raw_os_error(unsafe { c::WSAGetLastError() }) +} + +#[doc(hidden)] +pub trait IsMinusOne { + fn is_minus_one(&self) -> bool; +} + +macro_rules! impl_is_minus_one { + ($($t:ident)*) => ($(impl IsMinusOne for $t { + fn is_minus_one(&self) -> bool { + *self == -1 + } + })*) +} + +impl_is_minus_one! { i8 i16 i32 i64 isize } + +/// Checks if the signed integer is the Windows constant `SOCKET_ERROR` (-1) +/// and if so, returns the last error from the Windows socket interface. This +/// function must be called before another call to the socket API is made. +pub fn cvt(t: T) -> io::Result { + if t.is_minus_one() { Err(last_error()) } else { Ok(t) } +} + +/// A variant of `cvt` for `getaddrinfo` which return 0 for a success. +pub fn cvt_gai(err: c_int) -> io::Result<()> { + if err == 0 { Ok(()) } else { Err(last_error()) } +} + +/// Just to provide the same interface as sys/pal/unix/net.rs +pub fn cvt_r(mut f: F) -> io::Result +where + T: IsMinusOne, + F: FnMut() -> T, +{ + cvt(f()) +} + +impl Socket { + pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { + let family = match *addr { + SocketAddr::V4(..) => netc::AF_INET, + SocketAddr::V6(..) => netc::AF_INET6, + }; + let socket = unsafe { + c::WSASocketW( + family, + ty, + 0, + ptr::null_mut(), + 0, + c::WSA_FLAG_OVERLAPPED | c::WSA_FLAG_NO_HANDLE_INHERIT, + ) + }; + + if socket != c::INVALID_SOCKET { + unsafe { Ok(Self::from_raw(socket)) } + } else { + let error = unsafe { c::WSAGetLastError() }; + + if error != c::WSAEPROTOTYPE && error != c::WSAEINVAL { + return Err(io::Error::from_raw_os_error(error)); + } + + let socket = + unsafe { c::WSASocketW(family, ty, 0, ptr::null_mut(), 0, c::WSA_FLAG_OVERLAPPED) }; + + if socket == c::INVALID_SOCKET { + return Err(last_error()); + } + + unsafe { + let socket = Self::from_raw(socket); + socket.0.set_no_inherit()?; + Ok(socket) + } + } + } + + pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = socket_addr_to_c(addr); + let result = unsafe { c::connect(self.as_raw(), addr.as_ptr(), len) }; + cvt(result).map(drop) + } + + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { + self.set_nonblocking(true)?; + let result = self.connect(addr); + self.set_nonblocking(false)?; + + match result { + Err(ref error) if error.kind() == io::ErrorKind::WouldBlock => { + if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { + return Err(io::Error::ZERO_TIMEOUT); + } + + let mut timeout = c::TIMEVAL { + tv_sec: cmp::min(timeout.as_secs(), c_long::MAX as u64) as c_long, + tv_usec: timeout.subsec_micros() as c_long, + }; + + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + + let fds = { + let mut fds = unsafe { mem::zeroed::() }; + fds.fd_count = 1; + fds.fd_array[0] = self.as_raw(); + fds + }; + + let mut writefds = fds; + let mut errorfds = fds; + + let count = { + let result = unsafe { + c::select(1, ptr::null_mut(), &mut writefds, &mut errorfds, &timeout) + }; + cvt(result)? + }; + + match count { + 0 => Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")), + _ => { + if writefds.fd_count != 1 { + if let Some(e) = self.take_error()? { + return Err(e); + } + } + + Ok(()) + } + } + } + _ => result, + } + } + + pub fn accept(&self, storage: *mut c::SOCKADDR, len: *mut c_int) -> io::Result { + let socket = unsafe { c::accept(self.as_raw(), storage, len) }; + + match socket { + c::INVALID_SOCKET => Err(last_error()), + _ => unsafe { Ok(Self::from_raw(socket)) }, + } + } + + pub fn duplicate(&self) -> io::Result { + Ok(Self(self.0.try_clone()?)) + } + + fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { + // On unix when a socket is shut down all further reads return 0, so we + // do the same on windows to map a shut down socket to returning EOF. + let length = cmp::min(buf.capacity(), i32::MAX as usize) as i32; + let result = + unsafe { c::recv(self.as_raw(), buf.as_mut().as_mut_ptr() as *mut _, length, flags) }; + + match result { + c::SOCKET_ERROR => { + let error = unsafe { c::WSAGetLastError() }; + + if error == c::WSAESHUTDOWN { + Ok(()) + } else { + Err(io::Error::from_raw_os_error(error)) + } + } + _ => { + unsafe { buf.advance_unchecked(result as usize) }; + Ok(()) + } + } + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), 0)?; + Ok(buf.len()) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.recv_with_flags(buf, 0) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + // On unix when a socket is shut down all further reads return 0, so we + // do the same on windows to map a shut down socket to returning EOF. + let length = cmp::min(bufs.len(), u32::MAX as usize) as u32; + let mut nread = 0; + let mut flags = 0; + let result = unsafe { + c::WSARecv( + self.as_raw(), + bufs.as_mut_ptr() as *mut c::WSABUF, + length, + &mut nread, + &mut flags, + ptr::null_mut(), + None, + ) + }; + + match result { + 0 => Ok(nread as usize), + _ => { + let error = unsafe { c::WSAGetLastError() }; + + if error == c::WSAESHUTDOWN { + Ok(0) + } else { + Err(io::Error::from_raw_os_error(error)) + } + } + } + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + true + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), c::MSG_PEEK)?; + Ok(buf.len()) + } + + fn recv_from_with_flags( + &self, + buf: &mut [u8], + flags: c_int, + ) -> io::Result<(usize, SocketAddr)> { + let mut storage = unsafe { mem::zeroed::() }; + let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t; + let length = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; + + // On unix when a socket is shut down all further reads return 0, so we + // do the same on windows to map a shut down socket to returning EOF. + let result = unsafe { + c::recvfrom( + self.as_raw(), + buf.as_mut_ptr() as *mut _, + length, + flags, + (&raw mut storage) as *mut _, + &mut addrlen, + ) + }; + + match result { + c::SOCKET_ERROR => { + let error = unsafe { c::WSAGetLastError() }; + + if error == c::WSAESHUTDOWN { + Ok((0, unsafe { socket_addr_from_c(&storage, addrlen as usize)? })) + } else { + Err(io::Error::from_raw_os_error(error)) + } + } + _ => Ok((result as usize, unsafe { socket_addr_from_c(&storage, addrlen as usize)? })), + } + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, 0) + } + + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, c::MSG_PEEK) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + let length = cmp::min(bufs.len(), u32::MAX as usize) as u32; + let mut nwritten = 0; + let result = unsafe { + c::WSASend( + self.as_raw(), + bufs.as_ptr() as *const c::WSABUF as *mut _, + length, + &mut nwritten, + 0, + ptr::null_mut(), + None, + ) + }; + cvt(result).map(|_| nwritten as usize) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + + pub fn set_timeout(&self, dur: Option, kind: c_int) -> io::Result<()> { + let timeout = match dur { + Some(dur) => { + let timeout = sys::dur2timeout(dur); + if timeout == 0 { + return Err(io::Error::ZERO_TIMEOUT); + } + timeout + } + None => 0, + }; + setsockopt(self, c::SOL_SOCKET, kind, timeout) + } + + pub fn timeout(&self, kind: c_int) -> io::Result> { + let raw: u32 = getsockopt(self, c::SOL_SOCKET, kind)?; + if raw == 0 { + Ok(None) + } else { + let secs = raw / 1000; + let nsec = (raw % 1000) * 1000000; + Ok(Some(Duration::new(secs as u64, nsec as u32))) + } + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + let how = match how { + Shutdown::Write => c::SD_SEND, + Shutdown::Read => c::SD_RECEIVE, + Shutdown::Both => c::SD_BOTH, + }; + let result = unsafe { c::shutdown(self.as_raw(), how) }; + cvt(result).map(drop) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let mut nonblocking = nonblocking as c_ulong; + let result = + unsafe { c::ioctlsocket(self.as_raw(), c::FIONBIO as c_int, &mut nonblocking) }; + cvt(result).map(drop) + } + + pub fn set_linger(&self, linger: Option) -> io::Result<()> { + let linger = c::LINGER { + l_onoff: linger.is_some() as c_ushort, + l_linger: linger.unwrap_or_default().as_secs() as c_ushort, + }; + + setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger) + } + + pub fn linger(&self) -> io::Result> { + let val: c::LINGER = getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?; + + Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) + } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL) + } + + pub fn nodelay(&self) -> io::Result { + let raw: c::BOOL = getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?; + Ok(raw != 0) + } + + pub fn take_error(&self) -> io::Result> { + let raw: c_int = getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)?; + if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } + } + + // This is used by sys_common code to abstract over Windows and Unix. + pub fn as_raw(&self) -> c::SOCKET { + debug_assert_eq!(mem::size_of::(), mem::size_of::()); + debug_assert_eq!(mem::align_of::(), mem::align_of::()); + self.as_inner().as_raw_socket() as c::SOCKET + } + pub unsafe fn from_raw(raw: c::SOCKET) -> Self { + debug_assert_eq!(mem::size_of::(), mem::size_of::()); + debug_assert_eq!(mem::align_of::(), mem::align_of::()); + unsafe { Self::from_raw_socket(raw as RawSocket) } + } +} + +#[unstable(reason = "not public", issue = "none", feature = "fd_read")] +impl<'a> Read for &'a Socket { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (**self).read(buf) + } +} + +impl AsInner for Socket { + #[inline] + fn as_inner(&self) -> &OwnedSocket { + &self.0 + } +} + +impl FromInner for Socket { + fn from_inner(sock: OwnedSocket) -> Socket { + Socket(sock) + } +} + +impl IntoInner for Socket { + fn into_inner(self) -> OwnedSocket { + self.0 + } +} + +impl AsSocket for Socket { + fn as_socket(&self) -> BorrowedSocket<'_> { + self.0.as_socket() + } +} + +impl AsRawSocket for Socket { + fn as_raw_socket(&self) -> RawSocket { + self.0.as_raw_socket() + } +} + +impl IntoRawSocket for Socket { + fn into_raw_socket(self) -> RawSocket { + self.0.into_raw_socket() + } +} + +impl FromRawSocket for Socket { + unsafe fn from_raw_socket(raw_socket: RawSocket) -> Self { + unsafe { Self(FromRawSocket::from_raw_socket(raw_socket)) } + } +} diff --git a/library/std/src/sys/net/connection/uefi/mod.rs b/library/std/src/sys/net/connection/uefi/mod.rs new file mode 100644 index 0000000000000..da2174396266f --- /dev/null +++ b/library/std/src/sys/net/connection/uefi/mod.rs @@ -0,0 +1,334 @@ +use crate::fmt; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; +use crate::sys::unsupported; +use crate::time::Duration; + +pub struct TcpStream(!); + +impl TcpStream { + pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { + unsupported() + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn read_timeout(&self) -> io::Result> { + self.0 + } + + pub fn write_timeout(&self) -> io::Result> { + self.0 + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn read(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> { + self.0 + } + + pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { + self.0 + } + + pub fn is_read_vectored(&self) -> bool { + self.0 + } + + pub fn write(&self, _: &[u8]) -> io::Result { + self.0 + } + + pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { + self.0 + } + + pub fn is_write_vectored(&self) -> bool { + self.0 + } + + pub fn peer_addr(&self) -> io::Result { + self.0 + } + + pub fn socket_addr(&self) -> io::Result { + self.0 + } + + pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_linger(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn linger(&self) -> io::Result> { + self.0 + } + + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn nodelay(&self) -> io::Result { + self.0 + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn ttl(&self) -> io::Result { + self.0 + } + + pub fn take_error(&self) -> io::Result> { + self.0 + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + self.0 + } +} + +impl fmt::Debug for TcpStream { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +pub struct TcpListener(!); + +impl TcpListener { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result { + self.0 + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn ttl(&self) -> io::Result { + self.0 + } + + pub fn set_only_v6(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn only_v6(&self) -> io::Result { + self.0 + } + + pub fn take_error(&self) -> io::Result> { + self.0 + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + self.0 + } +} + +impl fmt::Debug for TcpListener { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +pub struct UdpSocket(!); + +impl UdpSocket { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn peer_addr(&self) -> io::Result { + self.0 + } + + pub fn socket_addr(&self) -> io::Result { + self.0 + } + + pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.0 + } + + pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.0 + } + + pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn read_timeout(&self) -> io::Result> { + self.0 + } + + pub fn write_timeout(&self) -> io::Result> { + self.0 + } + + pub fn set_broadcast(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn broadcast(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn multicast_loop_v4(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn multicast_loop_v6(&self) -> io::Result { + self.0 + } + + pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { + self.0 + } + + pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { + self.0 + } + + pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { + self.0 + } + + pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { + self.0 + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn ttl(&self) -> io::Result { + self.0 + } + + pub fn take_error(&self) -> io::Result> { + self.0 + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn recv(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn send(&self, _: &[u8]) -> io::Result { + self.0 + } + + pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { + self.0 + } +} + +impl fmt::Debug for UdpSocket { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +pub struct LookupHost(!); + +impl LookupHost { + pub fn port(&self) -> u16 { + self.0 + } +} + +impl Iterator for LookupHost { + type Item = SocketAddr; + fn next(&mut self) -> Option { + self.0 + } +} + +impl TryFrom<&str> for LookupHost { + type Error = io::Error; + + fn try_from(_v: &str) -> io::Result { + unsupported() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(_v: (&'a str, u16)) -> io::Result { + unsupported() + } +} diff --git a/library/std/src/sys/net/connection/unsupported.rs b/library/std/src/sys/net/connection/unsupported.rs new file mode 100644 index 0000000000000..da2174396266f --- /dev/null +++ b/library/std/src/sys/net/connection/unsupported.rs @@ -0,0 +1,334 @@ +use crate::fmt; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; +use crate::sys::unsupported; +use crate::time::Duration; + +pub struct TcpStream(!); + +impl TcpStream { + pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { + unsupported() + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn read_timeout(&self) -> io::Result> { + self.0 + } + + pub fn write_timeout(&self) -> io::Result> { + self.0 + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn read(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> { + self.0 + } + + pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { + self.0 + } + + pub fn is_read_vectored(&self) -> bool { + self.0 + } + + pub fn write(&self, _: &[u8]) -> io::Result { + self.0 + } + + pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { + self.0 + } + + pub fn is_write_vectored(&self) -> bool { + self.0 + } + + pub fn peer_addr(&self) -> io::Result { + self.0 + } + + pub fn socket_addr(&self) -> io::Result { + self.0 + } + + pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_linger(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn linger(&self) -> io::Result> { + self.0 + } + + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn nodelay(&self) -> io::Result { + self.0 + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn ttl(&self) -> io::Result { + self.0 + } + + pub fn take_error(&self) -> io::Result> { + self.0 + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + self.0 + } +} + +impl fmt::Debug for TcpStream { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +pub struct TcpListener(!); + +impl TcpListener { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result { + self.0 + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn ttl(&self) -> io::Result { + self.0 + } + + pub fn set_only_v6(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn only_v6(&self) -> io::Result { + self.0 + } + + pub fn take_error(&self) -> io::Result> { + self.0 + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + self.0 + } +} + +impl fmt::Debug for TcpListener { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +pub struct UdpSocket(!); + +impl UdpSocket { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn peer_addr(&self) -> io::Result { + self.0 + } + + pub fn socket_addr(&self) -> io::Result { + self.0 + } + + pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.0 + } + + pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.0 + } + + pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn read_timeout(&self) -> io::Result> { + self.0 + } + + pub fn write_timeout(&self) -> io::Result> { + self.0 + } + + pub fn set_broadcast(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn broadcast(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn multicast_loop_v4(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn multicast_loop_v6(&self) -> io::Result { + self.0 + } + + pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { + self.0 + } + + pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { + self.0 + } + + pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { + self.0 + } + + pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { + self.0 + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn ttl(&self) -> io::Result { + self.0 + } + + pub fn take_error(&self) -> io::Result> { + self.0 + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn recv(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn send(&self, _: &[u8]) -> io::Result { + self.0 + } + + pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { + self.0 + } +} + +impl fmt::Debug for UdpSocket { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +pub struct LookupHost(!); + +impl LookupHost { + pub fn port(&self) -> u16 { + self.0 + } +} + +impl Iterator for LookupHost { + type Item = SocketAddr; + fn next(&mut self) -> Option { + self.0 + } +} + +impl TryFrom<&str> for LookupHost { + type Error = io::Error; + + fn try_from(_v: &str) -> io::Result { + unsupported() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(_v: (&'a str, u16)) -> io::Result { + unsupported() + } +} diff --git a/library/std/src/sys/net/connection/wasip1.rs b/library/std/src/sys/net/connection/wasip1.rs new file mode 100644 index 0000000000000..951dc65e5b47d --- /dev/null +++ b/library/std/src/sys/net/connection/wasip1.rs @@ -0,0 +1,507 @@ +#![forbid(unsafe_op_in_unsafe_fn)] + +use crate::fmt; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; +use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; +use crate::sys::fd::WasiFd; +use crate::sys::{err2io, unsupported}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::time::Duration; + +pub struct Socket(WasiFd); + +pub struct TcpStream { + inner: Socket, +} + +impl AsInner for Socket { + #[inline] + fn as_inner(&self) -> &WasiFd { + &self.0 + } +} + +impl IntoInner for Socket { + fn into_inner(self) -> WasiFd { + self.0 + } +} + +impl FromInner for Socket { + fn from_inner(inner: WasiFd) -> Socket { + Socket(inner) + } +} + +impl AsFd for Socket { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for Socket { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for Socket { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for Socket { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) } + } +} + +impl TcpStream { + pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { + unsupported() + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + unsupported() + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + unsupported() + } + + pub fn read_timeout(&self) -> io::Result> { + unsupported() + } + + pub fn write_timeout(&self) -> io::Result> { + unsupported() + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + unsupported() + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.read_vectored(&mut [IoSliceMut::new(buf)]) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.socket().as_inner().read_buf(buf) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.socket().as_inner().read(bufs) + } + + pub fn is_read_vectored(&self) -> bool { + true + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.write_vectored(&[IoSlice::new(buf)]) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + self.socket().as_inner().write(bufs) + } + + pub fn is_write_vectored(&self) -> bool { + true + } + + pub fn peer_addr(&self) -> io::Result { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result { + unsupported() + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + let wasi_how = match how { + Shutdown::Read => wasi::SDFLAGS_RD, + Shutdown::Write => wasi::SDFLAGS_WR, + Shutdown::Both => wasi::SDFLAGS_RD | wasi::SDFLAGS_WR, + }; + + unsafe { wasi::sock_shutdown(self.socket().as_raw_fd() as _, wasi_how).map_err(err2io) } + } + + pub fn duplicate(&self) -> io::Result { + unsupported() + } + + pub fn set_linger(&self, _: Option) -> io::Result<()> { + unsupported() + } + + pub fn linger(&self) -> io::Result> { + unsupported() + } + + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn nodelay(&self) -> io::Result { + unsupported() + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn ttl(&self) -> io::Result { + unsupported() + } + + pub fn take_error(&self) -> io::Result> { + unsupported() + } + + pub fn set_nonblocking(&self, state: bool) -> io::Result<()> { + let fdstat = unsafe { + wasi::fd_fdstat_get(self.socket().as_inner().as_raw_fd() as wasi::Fd).map_err(err2io)? + }; + + let mut flags = fdstat.fs_flags; + + if state { + flags |= wasi::FDFLAGS_NONBLOCK; + } else { + flags &= !wasi::FDFLAGS_NONBLOCK; + } + + unsafe { + wasi::fd_fdstat_set_flags(self.socket().as_inner().as_raw_fd() as wasi::Fd, flags) + .map_err(err2io) + } + } + + #[inline] + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } +} + +impl FromInner for TcpStream { + fn from_inner(socket: Socket) -> TcpStream { + TcpStream { inner: socket } + } +} + +impl fmt::Debug for TcpStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TcpStream").field("fd", &self.inner.as_raw_fd()).finish() + } +} + +pub struct TcpListener { + inner: Socket, +} + +impl TcpListener { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result { + unsupported() + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + let fd = unsafe { + wasi::sock_accept(self.as_inner().as_inner().as_raw_fd() as _, 0).map_err(err2io)? + }; + + Ok(( + TcpStream::from_inner(unsafe { Socket::from_raw_fd(fd as _) }), + // WASI has no concept of SocketAddr yet + // return an unspecified IPv4Addr + SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), + )) + } + + pub fn duplicate(&self) -> io::Result { + unsupported() + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn ttl(&self) -> io::Result { + unsupported() + } + + pub fn set_only_v6(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn only_v6(&self) -> io::Result { + unsupported() + } + + pub fn take_error(&self) -> io::Result> { + unsupported() + } + + pub fn set_nonblocking(&self, state: bool) -> io::Result<()> { + let fdstat = unsafe { + wasi::fd_fdstat_get(self.socket().as_inner().as_raw_fd() as wasi::Fd).map_err(err2io)? + }; + + let mut flags = fdstat.fs_flags; + + if state { + flags |= wasi::FDFLAGS_NONBLOCK; + } else { + flags &= !wasi::FDFLAGS_NONBLOCK; + } + + unsafe { + wasi::fd_fdstat_set_flags(self.socket().as_inner().as_raw_fd() as wasi::Fd, flags) + .map_err(err2io) + } + } + + #[inline] + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } +} + +impl AsInner for TcpListener { + #[inline] + fn as_inner(&self) -> &Socket { + &self.inner + } +} + +impl IntoInner for TcpListener { + fn into_inner(self) -> Socket { + self.inner + } +} + +impl FromInner for TcpListener { + fn from_inner(inner: Socket) -> TcpListener { + TcpListener { inner } + } +} + +impl fmt::Debug for TcpListener { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TcpListener").field("fd", &self.inner.as_raw_fd()).finish() + } +} + +pub struct UdpSocket { + inner: Socket, +} + +impl UdpSocket { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn peer_addr(&self) -> io::Result { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result { + unsupported() + } + + pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + unsupported() + } + + pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + unsupported() + } + + pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { + unsupported() + } + + pub fn duplicate(&self) -> io::Result { + unsupported() + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + unsupported() + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + unsupported() + } + + pub fn read_timeout(&self) -> io::Result> { + unsupported() + } + + pub fn write_timeout(&self) -> io::Result> { + unsupported() + } + + pub fn set_broadcast(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn broadcast(&self) -> io::Result { + unsupported() + } + + pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn multicast_loop_v4(&self) -> io::Result { + unsupported() + } + + pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + unsupported() + } + + pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn multicast_loop_v6(&self) -> io::Result { + unsupported() + } + + pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { + unsupported() + } + + pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { + unsupported() + } + + pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn ttl(&self) -> io::Result { + unsupported() + } + + pub fn take_error(&self) -> io::Result> { + unsupported() + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn recv(&self, _: &mut [u8]) -> io::Result { + unsupported() + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + unsupported() + } + + pub fn send(&self, _: &[u8]) -> io::Result { + unsupported() + } + + pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { + unsupported() + } + + #[inline] + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } +} + +impl AsInner for UdpSocket { + #[inline] + fn as_inner(&self) -> &Socket { + &self.inner + } +} + +impl IntoInner for UdpSocket { + fn into_inner(self) -> Socket { + self.inner + } +} + +impl FromInner for UdpSocket { + fn from_inner(inner: Socket) -> UdpSocket { + UdpSocket { inner } + } +} + +impl fmt::Debug for UdpSocket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("UdpSocket").field("fd", &self.inner.as_raw_fd()).finish() + } +} + +pub struct LookupHost(!); + +impl LookupHost { + pub fn port(&self) -> u16 { + self.0 + } +} + +impl Iterator for LookupHost { + type Item = SocketAddr; + fn next(&mut self) -> Option { + self.0 + } +} + +impl<'a> TryFrom<&'a str> for LookupHost { + type Error = io::Error; + + fn try_from(_v: &'a str) -> io::Result { + unsupported() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(_v: (&'a str, u16)) -> io::Result { + unsupported() + } +} diff --git a/library/std/src/sys/pal/xous/net/dns.rs b/library/std/src/sys/net/connection/xous/dns.rs similarity index 100% rename from library/std/src/sys/pal/xous/net/dns.rs rename to library/std/src/sys/net/connection/xous/dns.rs diff --git a/library/std/src/sys/net/connection/xous/mod.rs b/library/std/src/sys/net/connection/xous/mod.rs new file mode 100644 index 0000000000000..e44a375b9e3c5 --- /dev/null +++ b/library/std/src/sys/net/connection/xous/mod.rs @@ -0,0 +1,48 @@ +mod dns; + +mod tcpstream; +pub use tcpstream::*; + +mod tcplistener; +pub use tcplistener::*; + +mod udp; +pub use udp::*; + +// this structure needs to be synchronized with what's in net/src/api.rs +#[repr(C)] +#[derive(Debug)] +enum NetError { + // Ok = 0, + Unaddressable = 1, + SocketInUse = 2, + // AccessDenied = 3, + Invalid = 4, + // Finished = 5, + LibraryError = 6, + // AlreadyUsed = 7, + TimedOut = 8, + WouldBlock = 9, +} + +#[repr(C, align(4096))] +struct ConnectRequest { + raw: [u8; 4096], +} + +#[repr(C, align(4096))] +struct SendData { + raw: [u8; 4096], +} + +#[repr(C, align(4096))] +pub struct ReceiveData { + raw: [u8; 4096], +} + +#[repr(C, align(4096))] +pub struct GetAddress { + raw: [u8; 4096], +} + +pub use dns::LookupHost; diff --git a/library/std/src/sys/pal/xous/net/tcplistener.rs b/library/std/src/sys/net/connection/xous/tcplistener.rs similarity index 100% rename from library/std/src/sys/pal/xous/net/tcplistener.rs rename to library/std/src/sys/net/connection/xous/tcplistener.rs diff --git a/library/std/src/sys/pal/xous/net/tcpstream.rs b/library/std/src/sys/net/connection/xous/tcpstream.rs similarity index 100% rename from library/std/src/sys/pal/xous/net/tcpstream.rs rename to library/std/src/sys/net/connection/xous/tcpstream.rs diff --git a/library/std/src/sys/pal/xous/net/udp.rs b/library/std/src/sys/net/connection/xous/udp.rs similarity index 100% rename from library/std/src/sys/pal/xous/net/udp.rs rename to library/std/src/sys/net/connection/xous/udp.rs diff --git a/library/std/src/sys/net/mod.rs b/library/std/src/sys/net/mod.rs new file mode 100644 index 0000000000000..646679a1cc8b9 --- /dev/null +++ b/library/std/src/sys/net/mod.rs @@ -0,0 +1,41 @@ +cfg_if::cfg_if! { + if #[cfg(any( + all(target_family = "unix", not(target_os = "l4re")), + target_os = "windows", + target_os = "hermit", + all(target_os = "wasi", target_env = "p2"), + target_os = "solid_asp3", + ))] { + mod connection { + mod socket; + pub use socket::*; + } + } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + mod connection { + mod sgx; + pub use sgx::*; + } + } else if #[cfg(all(target_os = "wasi", target_env = "p1"))] { + mod connection { + mod wasip1; + pub use wasip1::*; + } + } else if #[cfg(target_os = "xous")] { + mod connection { + mod xous; + pub use xous::*; + } + } else if #[cfg(target_os = "uefi")] { + mod connection { + mod uefi; + pub use uefi::*; + } + } else { + mod connection { + mod unsupported; + pub use unsupported::*; + } + } +} + +pub use connection::*; diff --git a/library/std/src/sys/pal/hermit/io.rs b/library/std/src/sys/pal/hermit/io.rs deleted file mode 100644 index 0424a1ac55a29..0000000000000 --- a/library/std/src/sys/pal/hermit/io.rs +++ /dev/null @@ -1,87 +0,0 @@ -use hermit_abi::{c_void, iovec}; - -use crate::marker::PhantomData; -use crate::os::hermit::io::{AsFd, AsRawFd}; -use crate::slice; - -#[derive(Copy, Clone)] -#[repr(transparent)] -pub struct IoSlice<'a> { - vec: iovec, - _p: PhantomData<&'a [u8]>, -} - -impl<'a> IoSlice<'a> { - #[inline] - pub fn new(buf: &'a [u8]) -> IoSlice<'a> { - IoSlice { - vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if self.vec.iov_len < n { - panic!("advancing IoSlice beyond its length"); - } - - unsafe { - self.vec.iov_len -= n; - self.vec.iov_base = self.vec.iov_base.add(n); - } - } - - #[inline] - pub const fn as_slice(&self) -> &'a [u8] { - unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } -} - -#[repr(transparent)] -pub struct IoSliceMut<'a> { - vec: iovec, - _p: PhantomData<&'a mut [u8]>, -} - -impl<'a> IoSliceMut<'a> { - #[inline] - pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { - IoSliceMut { - vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if self.vec.iov_len < n { - panic!("advancing IoSliceMut beyond its length"); - } - - unsafe { - self.vec.iov_len -= n; - self.vec.iov_base = self.vec.iov_base.add(n); - } - } - - #[inline] - pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } - - #[inline] - pub const fn into_slice(self) -> &'a mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } - - #[inline] - pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } -} - -pub fn is_terminal(fd: &impl AsFd) -> bool { - let fd = fd.as_fd(); - hermit_abi::isatty(fd.as_raw_fd()) -} diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index d833c9d632c6d..21cbac643bbec 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -23,8 +23,6 @@ pub mod env; pub mod fd; pub mod fs; pub mod futex; -pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -55,7 +53,7 @@ pub fn abort_internal() -> ! { // This function is needed by the panic runtime. The symbol is named in // pre-link args for the target specification, so keep that in sync. #[cfg(not(test))] -#[no_mangle] +#[unsafe(no_mangle)] // NB. used by both libunwind and libpanic_abort pub extern "C" fn __rust_abort() { abort_internal(); @@ -74,13 +72,13 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { pub unsafe fn cleanup() {} #[cfg(not(test))] -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn runtime_entry( argc: i32, argv: *const *const c_char, env: *const *const c_char, ) -> ! { - extern "C" { + unsafe extern "C" { fn main(argc: isize, argv: *const *const c_char) -> i32; } diff --git a/library/std/src/sys/pal/hermit/net.rs b/library/std/src/sys/pal/hermit/net.rs deleted file mode 100644 index 4e12374203e38..0000000000000 --- a/library/std/src/sys/pal/hermit/net.rs +++ /dev/null @@ -1,347 +0,0 @@ -#![allow(dead_code)] - -use core::ffi::c_int; - -use super::fd::FileDesc; -use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{Shutdown, SocketAddr}; -use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd}; -use crate::sys::time::Instant; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::time::Duration; -use crate::{cmp, mem}; - -#[allow(unused_extern_crates)] -pub extern crate hermit_abi as netc; - -pub use crate::sys::{cvt, cvt_r}; - -pub type wrlen_t = usize; - -pub fn cvt_gai(err: i32) -> io::Result<()> { - if err == 0 { - return Ok(()); - } - - let detail = ""; - - Err(io::Error::new( - io::ErrorKind::Uncategorized, - &format!("failed to lookup address information: {detail}")[..], - )) -} - -pub fn init() {} - -#[derive(Debug)] -pub struct Socket(FileDesc); - -impl Socket { - pub fn new(addr: &SocketAddr, ty: i32) -> io::Result { - let fam = match *addr { - SocketAddr::V4(..) => netc::AF_INET, - SocketAddr::V6(..) => netc::AF_INET6, - }; - Socket::new_raw(fam, ty) - } - - pub fn new_raw(fam: i32, ty: i32) -> io::Result { - let fd = cvt(unsafe { netc::socket(fam, ty, 0) })?; - Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) - } - - pub fn new_pair(_fam: i32, _ty: i32) -> io::Result<(Socket, Socket)> { - unimplemented!() - } - - pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { - let (addr, len) = addr.into_inner(); - cvt_r(|| unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?; - Ok(()) - } - - pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { - self.set_nonblocking(true)?; - let r = unsafe { - let (addr, len) = addr.into_inner(); - cvt(netc::connect(self.as_raw_fd(), addr.as_ptr(), len)) - }; - self.set_nonblocking(false)?; - - match r { - Ok(_) => return Ok(()), - // there's no ErrorKind for EINPROGRESS :( - Err(ref e) if e.raw_os_error() == Some(netc::errno::EINPROGRESS) => {} - Err(e) => return Err(e), - } - - let mut pollfd = netc::pollfd { fd: self.as_raw_fd(), events: netc::POLLOUT, revents: 0 }; - - if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { - return Err(io::Error::ZERO_TIMEOUT); - } - - let start = Instant::now(); - - loop { - let elapsed = start.elapsed(); - if elapsed >= timeout { - return Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")); - } - - let timeout = timeout - elapsed; - let mut timeout = timeout - .as_secs() - .saturating_mul(1_000) - .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); - if timeout == 0 { - timeout = 1; - } - - let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; - - match unsafe { netc::poll(&mut pollfd, 1, timeout) } { - -1 => { - let err = io::Error::last_os_error(); - if !err.is_interrupted() { - return Err(err); - } - } - 0 => {} - _ => { - // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look - // for POLLHUP rather than read readiness - if pollfd.revents & netc::POLLHUP != 0 { - let e = self.take_error()?.unwrap_or_else(|| { - io::const_error!( - io::ErrorKind::Uncategorized, - "no error set after POLLHUP", - ) - }); - return Err(e); - } - - return Ok(()); - } - } - } - } - - pub fn accept( - &self, - storage: *mut netc::sockaddr, - len: *mut netc::socklen_t, - ) -> io::Result { - let fd = cvt(unsafe { netc::accept(self.0.as_raw_fd(), storage, len) })?; - Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) - } - - pub fn duplicate(&self) -> io::Result { - let fd = cvt(unsafe { netc::dup(self.0.as_raw_fd()) })?; - Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) - } - - fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: i32) -> io::Result<()> { - let ret = cvt(unsafe { - netc::recv( - self.0.as_raw_fd(), - buf.as_mut().as_mut_ptr() as *mut u8, - buf.capacity(), - flags, - ) - })?; - unsafe { - buf.advance_unchecked(ret as usize); - } - Ok(()) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), 0)?; - Ok(buf.len()) - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), netc::MSG_PEEK)?; - Ok(buf.len()) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.recv_with_flags(buf, 0) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - - fn recv_from_with_flags(&self, buf: &mut [u8], flags: i32) -> io::Result<(usize, SocketAddr)> { - let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() }; - let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t; - - let n = cvt(unsafe { - netc::recvfrom( - self.as_raw_fd(), - buf.as_mut_ptr(), - buf.len(), - flags, - (&raw mut storage) as *mut _, - &mut addrlen, - ) - })?; - Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?)) - } - - pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, 0) - } - - pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, netc::MSG_PEEK) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - pub fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } - - pub fn set_timeout(&self, dur: Option, kind: i32) -> io::Result<()> { - let timeout = match dur { - Some(dur) => { - if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { - return Err(io::Error::ZERO_TIMEOUT); - } - - let secs = if dur.as_secs() > netc::time_t::MAX as u64 { - netc::time_t::MAX - } else { - dur.as_secs() as netc::time_t - }; - let mut timeout = netc::timeval { - tv_sec: secs, - tv_usec: dur.subsec_micros() as netc::suseconds_t, - }; - if timeout.tv_sec == 0 && timeout.tv_usec == 0 { - timeout.tv_usec = 1; - } - timeout - } - None => netc::timeval { tv_sec: 0, tv_usec: 0 }, - }; - - setsockopt(self, netc::SOL_SOCKET, kind, timeout) - } - - pub fn timeout(&self, kind: i32) -> io::Result> { - let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?; - if raw.tv_sec == 0 && raw.tv_usec == 0 { - Ok(None) - } else { - let sec = raw.tv_sec as u64; - let nsec = (raw.tv_usec as u32) * 1000; - Ok(Some(Duration::new(sec, nsec))) - } - } - - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - let how = match how { - Shutdown::Write => netc::SHUT_WR, - Shutdown::Read => netc::SHUT_RD, - Shutdown::Both => netc::SHUT_RDWR, - }; - cvt(unsafe { netc::shutdown(self.as_raw_fd(), how) })?; - Ok(()) - } - - pub fn set_linger(&self, linger: Option) -> io::Result<()> { - let linger = netc::linger { - l_onoff: linger.is_some() as i32, - l_linger: linger.unwrap_or_default().as_secs() as libc::c_int, - }; - - setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger) - } - - pub fn linger(&self) -> io::Result> { - let val: netc::linger = getsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER)?; - - Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) - } - - pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - let value: i32 = if nodelay { 1 } else { 0 }; - setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, value) - } - - pub fn nodelay(&self) -> io::Result { - let raw: i32 = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?; - Ok(raw != 0) - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - let mut nonblocking: i32 = if nonblocking { 1 } else { 0 }; - cvt(unsafe { - netc::ioctl( - self.as_raw_fd(), - netc::FIONBIO, - (&raw mut nonblocking) as *mut core::ffi::c_void, - ) - }) - .map(drop) - } - - pub fn take_error(&self) -> io::Result> { - unimplemented!() - } - - // This is used by sys_common code to abstract over Windows and Unix. - pub fn as_raw(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -impl AsInner for Socket { - #[inline] - fn as_inner(&self) -> &FileDesc { - &self.0 - } -} - -impl IntoInner for Socket { - fn into_inner(self) -> FileDesc { - self.0 - } -} - -impl FromInner for Socket { - fn from_inner(file_desc: FileDesc) -> Self { - Self(file_desc) - } -} - -impl AsFd for Socket { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl AsRawFd for Socket { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} diff --git a/library/std/src/sys/pal/itron/abi.rs b/library/std/src/sys/pal/itron/abi.rs index 5eb14bb7e534b..49b5251fc0eb6 100644 --- a/library/std/src/sys/pal/itron/abi.rs +++ b/library/std/src/sys/pal/itron/abi.rs @@ -132,7 +132,7 @@ pub struct T_CTSK { pub stk: *mut u8, } -extern "C" { +unsafe extern "C" { #[link_name = "__asp3_acre_tsk"] pub fn acre_tsk(pk_ctsk: *const T_CTSK) -> ER_ID; #[link_name = "__asp3_get_tid"] diff --git a/library/std/src/sys/pal/itron/time/tests.rs b/library/std/src/sys/pal/itron/time/tests.rs index 28db4f8b6799f..d14035d9da49f 100644 --- a/library/std/src/sys/pal/itron/time/tests.rs +++ b/library/std/src/sys/pal/itron/time/tests.rs @@ -8,24 +8,26 @@ fn reltim2dur(t: u64) -> Duration { fn test_dur2reltims() { assert_eq!(dur2reltims(reltim2dur(0)).collect::>(), vec![]); assert_eq!(dur2reltims(reltim2dur(42)).collect::>(), vec![42]); - assert_eq!(dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64)).collect::>(), vec![ - abi::TMAX_RELTIM - ]); - assert_eq!(dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::>(), vec![ - abi::TMAX_RELTIM, - 10000 - ]); + assert_eq!( + dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64)).collect::>(), + vec![abi::TMAX_RELTIM] + ); + assert_eq!( + dur2reltims(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::>(), + vec![abi::TMAX_RELTIM, 10000] + ); } #[test] fn test_dur2tmos() { assert_eq!(dur2tmos(reltim2dur(0)).collect::>(), vec![0]); assert_eq!(dur2tmos(reltim2dur(42)).collect::>(), vec![42]); - assert_eq!(dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64)).collect::>(), vec![ - abi::TMAX_RELTIM - ]); - assert_eq!(dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::>(), vec![ - abi::TMAX_RELTIM, - 10000 - ]); + assert_eq!( + dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64)).collect::>(), + vec![abi::TMAX_RELTIM] + ); + assert_eq!( + dur2tmos(reltim2dur(abi::TMAX_RELTIM as u64 + 10000)).collect::>(), + vec![abi::TMAX_RELTIM, 10000] + ); } diff --git a/library/std/src/sys/pal/sgx/abi/mem.rs b/library/std/src/sys/pal/sgx/abi/mem.rs index 18e6d5b3fa24d..e6ce15bed3cfd 100644 --- a/library/std/src/sys/pal/sgx/abi/mem.rs +++ b/library/std/src/sys/pal/sgx/abi/mem.rs @@ -12,7 +12,7 @@ pub(crate) unsafe fn rel_ptr_mut(offset: u64) -> *mut T { (image_base() + offset) as *mut T } -extern "C" { +unsafe extern "C" { static ENCLAVE_SIZE: usize; static HEAP_BASE: u64; static HEAP_SIZE: usize; diff --git a/library/std/src/sys/pal/sgx/abi/mod.rs b/library/std/src/sys/pal/sgx/abi/mod.rs index d8836452e7555..90981bd6a6a04 100644 --- a/library/std/src/sys/pal/sgx/abi/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/mod.rs @@ -23,7 +23,7 @@ global_asm!(include_str!("entry.S"), options(att_syntax)); struct EntryReturn(u64, u64); #[cfg(not(test))] -#[no_mangle] +#[unsafe(no_mangle)] unsafe extern "C" fn tcs_init(secondary: bool) { // Be very careful when changing this code: it runs before the binary has been // relocated. Any indirect accesses to symbols will likely fail. @@ -60,7 +60,7 @@ unsafe extern "C" fn tcs_init(secondary: bool) { // (main function exists). If this is a library, the crate author should be // able to specify this #[cfg(not(test))] -#[no_mangle] +#[unsafe(no_mangle)] extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64) -> EntryReturn { // FIXME: how to support TLS in library mode? let tls = Box::new(tls::Tls::new()); @@ -73,7 +73,7 @@ extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64 EntryReturn(0, 0) } else { - extern "C" { + unsafe extern "C" { fn main(argc: isize, argv: *const *const u8) -> isize; } @@ -103,7 +103,7 @@ pub(super) fn exit_with_code(code: isize) -> ! { } #[cfg(not(test))] -#[no_mangle] +#[unsafe(no_mangle)] extern "C" fn abort_reentry() -> ! { usercalls::exit(false) } diff --git a/library/std/src/sys/pal/sgx/abi/panic.rs b/library/std/src/sys/pal/sgx/abi/panic.rs index c06b97ee3674a..67af062b0fffe 100644 --- a/library/std/src/sys/pal/sgx/abi/panic.rs +++ b/library/std/src/sys/pal/sgx/abi/panic.rs @@ -2,7 +2,7 @@ use super::usercalls::alloc::UserRef; use crate::io::{self, Write}; use crate::{cmp, mem}; -extern "C" { +unsafe extern "C" { fn take_debug_panic_buf_ptr() -> *mut u8; static DEBUG: u8; } diff --git a/library/std/src/sys/pal/sgx/abi/reloc.rs b/library/std/src/sys/pal/sgx/abi/reloc.rs index 02dff0ad29fc3..a4f5e4a0936f4 100644 --- a/library/std/src/sys/pal/sgx/abi/reloc.rs +++ b/library/std/src/sys/pal/sgx/abi/reloc.rs @@ -11,7 +11,7 @@ struct Rela { } pub fn relocate_elf_rela() { - extern "C" { + unsafe extern "C" { static RELA: u64; static RELACOUNT: usize; } diff --git a/library/std/src/sys/pal/sgx/abi/thread.rs b/library/std/src/sys/pal/sgx/abi/thread.rs index 2b23e368cc31a..9b37e2baf3642 100644 --- a/library/std/src/sys/pal/sgx/abi/thread.rs +++ b/library/std/src/sys/pal/sgx/abi/thread.rs @@ -6,7 +6,7 @@ use fortanix_sgx_abi::Tcs; /// is a one-to-one correspondence of the ID to the address of the TCS. #[unstable(feature = "sgx_platform", issue = "56975")] pub fn current() -> Tcs { - extern "C" { + unsafe extern "C" { fn get_tcs_addr() -> *mut u8; } let addr = unsafe { get_tcs_addr() }; diff --git a/library/std/src/sys/pal/sgx/abi/tls/mod.rs b/library/std/src/sys/pal/sgx/abi/tls/mod.rs index 34fc2f20d2214..8e2b271f1c970 100644 --- a/library/std/src/sys/pal/sgx/abi/tls/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/tls/mod.rs @@ -12,17 +12,17 @@ const TLS_KEYS: usize = 128; // Same as POSIX minimum const TLS_KEYS_BITSET_SIZE: usize = (TLS_KEYS + (USIZE_BITS - 1)) / USIZE_BITS; #[cfg_attr(test, linkage = "available_externally")] -#[export_name = "_ZN16__rust_internals3std3sys3sgx3abi3tls14TLS_KEY_IN_USEE"] +#[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx3abi3tls14TLS_KEY_IN_USEE")] static TLS_KEY_IN_USE: SyncBitset = SYNC_BITSET_INIT; macro_rules! dup { ((* $($exp:tt)*) $($val:tt)*) => (dup!( ($($exp)*) $($val)* $($val)* )); (() $($val:tt)*) => ([$($val),*]) } #[cfg_attr(test, linkage = "available_externally")] -#[export_name = "_ZN16__rust_internals3std3sys3sgx3abi3tls14TLS_DESTRUCTORE"] +#[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx3abi3tls14TLS_DESTRUCTORE")] static TLS_DESTRUCTOR: [AtomicUsize; TLS_KEYS] = dup!((* * * * * * *) (AtomicUsize::new(0))); -extern "C" { +unsafe extern "C" { fn get_tls_ptr() -> *const u8; fn set_tls_ptr(tls: *const u8); } diff --git a/library/std/src/sys/pal/sgx/abi/usercalls/raw.rs b/library/std/src/sys/pal/sgx/abi/usercalls/raw.rs index 943b771498f8f..28fbbc3c51816 100644 --- a/library/std/src/sys/pal/sgx/abi/usercalls/raw.rs +++ b/library/std/src/sys/pal/sgx/abi/usercalls/raw.rs @@ -9,7 +9,7 @@ use crate::ptr::NonNull; #[repr(C)] struct UsercallReturn(u64, u64); -extern "C" { +unsafe extern "C" { fn usercall(nr: NonZero, p1: u64, p2: u64, abort: u64, p3: u64, p4: u64) -> UsercallReturn; } diff --git a/library/std/src/sys/pal/sgx/args.rs b/library/std/src/sys/pal/sgx/args.rs index a72a041da6cc9..e62bf383954eb 100644 --- a/library/std/src/sys/pal/sgx/args.rs +++ b/library/std/src/sys/pal/sgx/args.rs @@ -7,7 +7,7 @@ use crate::sys_common::FromInner; use crate::{fmt, slice}; #[cfg_attr(test, linkage = "available_externally")] -#[export_name = "_ZN16__rust_internals3std3sys3sgx4args4ARGSE"] +#[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx4args4ARGSE")] static ARGS: AtomicUsize = AtomicUsize::new(0); type ArgsStore = Vec; diff --git a/library/std/src/sys/pal/sgx/libunwind_integration.rs b/library/std/src/sys/pal/sgx/libunwind_integration.rs index debfd324c864e..6d0d78d1eb9b5 100644 --- a/library/std/src/sys/pal/sgx/libunwind_integration.rs +++ b/library/std/src/sys/pal/sgx/libunwind_integration.rs @@ -15,7 +15,7 @@ const _: () = unsafe { const EINVAL: i32 = 22; -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RwLock) -> i32 { if p.is_null() { return EINVAL; @@ -27,7 +27,7 @@ pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RwLock) -> i32 { return 0; } -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RwLock) -> i32 { if p.is_null() { return EINVAL; @@ -36,7 +36,7 @@ pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RwLock) -> i32 { return 0; } -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RwLock) -> i32 { if p.is_null() { return EINVAL; diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index ce8a2fed4bc6b..37ca6b08c950b 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -14,10 +14,7 @@ pub mod env; pub mod fd; #[path = "../unsupported/fs.rs"] pub mod fs; -#[path = "../unsupported/io.rs"] -pub mod io; mod libunwind_integration; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -126,7 +123,7 @@ pub fn abort_internal() -> ! { // This function is needed by the panic runtime. The symbol is named in // pre-link args for the target specification, so keep that in sync. #[cfg(not(test))] -#[no_mangle] +#[unsafe(no_mangle)] // NB. used by both libunwind and libpanic_abort pub extern "C" fn __rust_abort() { abort_internal(); diff --git a/library/std/src/sys/pal/sgx/net.rs b/library/std/src/sys/pal/sgx/net.rs deleted file mode 100644 index c966886d16344..0000000000000 --- a/library/std/src/sys/pal/sgx/net.rs +++ /dev/null @@ -1,536 +0,0 @@ -use super::abi::usercalls; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; -use crate::sync::Arc; -use crate::sys::fd::FileDesc; -use crate::sys::{AsInner, FromInner, IntoInner, TryIntoInner, sgx_ineffective, unsupported}; -use crate::time::Duration; -use crate::{error, fmt}; - -const DEFAULT_FAKE_TTL: u32 = 64; - -#[derive(Debug, Clone)] -pub struct Socket { - inner: Arc, - local_addr: Option, -} - -impl Socket { - fn new(fd: usercalls::raw::Fd, local_addr: String) -> Socket { - Socket { inner: Arc::new(FileDesc::new(fd)), local_addr: Some(local_addr) } - } -} - -impl AsInner for Socket { - #[inline] - fn as_inner(&self) -> &FileDesc { - &self.inner - } -} - -impl TryIntoInner for Socket { - fn try_into_inner(self) -> Result { - let Socket { inner, local_addr } = self; - Arc::try_unwrap(inner).map_err(|inner| Socket { inner, local_addr }) - } -} - -impl FromInner<(FileDesc, Option)> for Socket { - fn from_inner((inner, local_addr): (FileDesc, Option)) -> Socket { - Socket { inner: Arc::new(inner), local_addr } - } -} - -#[derive(Clone)] -pub struct TcpStream { - inner: Socket, - peer_addr: Option, -} - -impl fmt::Debug for TcpStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut res = f.debug_struct("TcpStream"); - - if let Some(ref addr) = self.inner.local_addr { - res.field("addr", addr); - } - - if let Some(ref peer) = self.peer_addr { - res.field("peer", peer); - } - - res.field("fd", &self.inner.inner.as_inner()).finish() - } -} - -fn io_err_to_addr(result: io::Result<&SocketAddr>) -> io::Result { - match result { - Ok(saddr) => Ok(saddr.to_string()), - // need to downcast twice because io::Error::into_inner doesn't return the original - // value if the conversion fails - Err(e) => { - if e.get_ref().and_then(|e| e.downcast_ref::()).is_some() { - Ok(e.into_inner().unwrap().downcast::().unwrap().host) - } else { - Err(e) - } - } - } -} - -fn addr_to_sockaddr(addr: Option<&str>) -> io::Result { - addr.ok_or(io::ErrorKind::AddrNotAvailable)? - .to_socket_addrs() - // unwrap OK: if an iterator is returned, we're guaranteed to get exactly one entry - .map(|mut it| it.next().unwrap()) -} - -impl TcpStream { - pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result { - let addr = io_err_to_addr(addr)?; - let (fd, local_addr, peer_addr) = usercalls::connect_stream(&addr)?; - Ok(TcpStream { inner: Socket::new(fd, local_addr), peer_addr: Some(peer_addr) }) - } - - pub fn connect_timeout(addr: &SocketAddr, dur: Duration) -> io::Result { - if dur == Duration::default() { - return Err(io::Error::ZERO_TIMEOUT); - } - Self::connect(Ok(addr)) // FIXME: ignoring timeout - } - - pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { - match dur { - Some(dur) if dur == Duration::default() => { - return Err(io::Error::ZERO_TIMEOUT); - } - _ => sgx_ineffective(()), - } - } - - pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { - match dur { - Some(dur) if dur == Duration::default() => { - return Err(io::Error::ZERO_TIMEOUT); - } - _ => sgx_ineffective(()), - } - } - - pub fn read_timeout(&self) -> io::Result> { - sgx_ineffective(None) - } - - pub fn write_timeout(&self) -> io::Result> { - sgx_ineffective(None) - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - Ok(0) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.inner.inner.read(buf) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.inner.inner.read_buf(buf) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.inner.inner.read_vectored(bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - self.inner.inner.is_read_vectored() - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.inner.inner.write(buf) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.inner.inner.write_vectored(bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - self.inner.inner.is_write_vectored() - } - - pub fn peer_addr(&self) -> io::Result { - addr_to_sockaddr(self.peer_addr.as_deref()) - } - - pub fn socket_addr(&self) -> io::Result { - addr_to_sockaddr(self.inner.local_addr.as_deref()) - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - sgx_ineffective(()) - } - - pub fn duplicate(&self) -> io::Result { - Ok(self.clone()) - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - sgx_ineffective(()) - } - - pub fn linger(&self) -> io::Result> { - sgx_ineffective(None) - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - sgx_ineffective(()) - } - - pub fn nodelay(&self) -> io::Result { - sgx_ineffective(false) - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - sgx_ineffective(()) - } - - pub fn ttl(&self) -> io::Result { - sgx_ineffective(DEFAULT_FAKE_TTL) - } - - pub fn take_error(&self) -> io::Result> { - Ok(None) - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - sgx_ineffective(()) - } -} - -impl AsInner for TcpStream { - #[inline] - fn as_inner(&self) -> &Socket { - &self.inner - } -} - -// `Inner` includes `peer_addr` so that a `TcpStream` maybe correctly -// reconstructed if `Socket::try_into_inner` fails. -impl IntoInner<(Socket, Option)> for TcpStream { - fn into_inner(self) -> (Socket, Option) { - (self.inner, self.peer_addr) - } -} - -impl FromInner<(Socket, Option)> for TcpStream { - fn from_inner((inner, peer_addr): (Socket, Option)) -> TcpStream { - TcpStream { inner, peer_addr } - } -} - -#[derive(Clone)] -pub struct TcpListener { - inner: Socket, -} - -impl fmt::Debug for TcpListener { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut res = f.debug_struct("TcpListener"); - - if let Some(ref addr) = self.inner.local_addr { - res.field("addr", addr); - } - - res.field("fd", &self.inner.inner.as_inner()).finish() - } -} - -impl TcpListener { - pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result { - let addr = io_err_to_addr(addr)?; - let (fd, local_addr) = usercalls::bind_stream(&addr)?; - Ok(TcpListener { inner: Socket::new(fd, local_addr) }) - } - - pub fn socket_addr(&self) -> io::Result { - addr_to_sockaddr(self.inner.local_addr.as_deref()) - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - let (fd, local_addr, peer_addr) = usercalls::accept_stream(self.inner.inner.raw())?; - let peer_addr = Some(peer_addr); - let ret_peer = - addr_to_sockaddr(peer_addr.as_deref()).unwrap_or_else(|_| ([0; 4], 0).into()); - Ok((TcpStream { inner: Socket::new(fd, local_addr), peer_addr }, ret_peer)) - } - - pub fn duplicate(&self) -> io::Result { - Ok(self.clone()) - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - sgx_ineffective(()) - } - - pub fn ttl(&self) -> io::Result { - sgx_ineffective(DEFAULT_FAKE_TTL) - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - sgx_ineffective(()) - } - - pub fn only_v6(&self) -> io::Result { - sgx_ineffective(false) - } - - pub fn take_error(&self) -> io::Result> { - Ok(None) - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - sgx_ineffective(()) - } -} - -impl AsInner for TcpListener { - #[inline] - fn as_inner(&self) -> &Socket { - &self.inner - } -} - -impl IntoInner for TcpListener { - fn into_inner(self) -> Socket { - self.inner - } -} - -impl FromInner for TcpListener { - fn from_inner(inner: Socket) -> TcpListener { - TcpListener { inner } - } -} - -pub struct UdpSocket(!); - -impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn peer_addr(&self) -> io::Result { - self.0 - } - - pub fn socket_addr(&self) -> io::Result { - self.0 - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.0 - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.0 - } - - pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn read_timeout(&self) -> io::Result> { - self.0 - } - - pub fn write_timeout(&self) -> io::Result> { - self.0 - } - - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn broadcast(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn multicast_loop_v4(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn multicast_ttl_v4(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn multicast_loop_v6(&self) -> io::Result { - self.0 - } - - pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - self.0 - } - - pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - self.0 - } - - pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - self.0 - } - - pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - self.0 - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn ttl(&self) -> io::Result { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - self.0 - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn recv(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn send(&self, _: &[u8]) -> io::Result { - self.0 - } - - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - self.0 - } -} - -impl fmt::Debug for UdpSocket { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -#[derive(Debug)] -pub struct NonIpSockAddr { - host: String, -} - -impl error::Error for NonIpSockAddr { - #[allow(deprecated)] - fn description(&self) -> &str { - "Failed to convert address to SocketAddr" - } -} - -impl fmt::Display for NonIpSockAddr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Failed to convert address to SocketAddr: {}", self.host) - } -} - -pub struct LookupHost(!); - -impl LookupHost { - fn new(host: String) -> io::Result { - Err(io::Error::new(io::ErrorKind::Uncategorized, NonIpSockAddr { host })) - } - - pub fn port(&self) -> u16 { - self.0 - } -} - -impl Iterator for LookupHost { - type Item = SocketAddr; - fn next(&mut self) -> Option { - self.0 - } -} - -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(v: &str) -> io::Result { - LookupHost::new(v.to_owned()) - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from((host, port): (&'a str, u16)) -> io::Result { - LookupHost::new(format!("{host}:{port}")) - } -} - -#[allow(bad_style)] -pub mod netc { - pub const AF_INET: u8 = 0; - pub const AF_INET6: u8 = 1; - pub type sa_family_t = u8; - - #[derive(Copy, Clone)] - pub struct in_addr { - pub s_addr: u32, - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in { - #[allow(dead_code)] - pub sin_family: sa_family_t, - pub sin_port: u16, - pub sin_addr: in_addr, - } - - #[derive(Copy, Clone)] - pub struct in6_addr { - pub s6_addr: [u8; 16], - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in6 { - #[allow(dead_code)] - pub sin6_family: sa_family_t, - pub sin6_port: u16, - pub sin6_addr: in6_addr, - pub sin6_flowinfo: u32, - pub sin6_scope_id: u32, - } -} diff --git a/library/std/src/sys/pal/sgx/os.rs b/library/std/src/sys/pal/sgx/os.rs index 46af710aa396c..b1ec2afd764e6 100644 --- a/library/std/src/sys/pal/sgx/os.rs +++ b/library/std/src/sys/pal/sgx/os.rs @@ -74,10 +74,10 @@ pub fn current_exe() -> io::Result { } #[cfg_attr(test, linkage = "available_externally")] -#[export_name = "_ZN16__rust_internals3std3sys3sgx2os3ENVE"] +#[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx2os3ENVE")] static ENV: AtomicUsize = AtomicUsize::new(0); #[cfg_attr(test, linkage = "available_externally")] -#[export_name = "_ZN16__rust_internals3std3sys3sgx2os8ENV_INITE"] +#[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx2os8ENV_INITE")] static ENV_INIT: Once = Once::new(); type EnvStore = Mutex>; diff --git a/library/std/src/sys/pal/sgx/stdio.rs b/library/std/src/sys/pal/sgx/stdio.rs index 2e680e740fde3..726a93acae44b 100644 --- a/library/std/src/sys/pal/sgx/stdio.rs +++ b/library/std/src/sys/pal/sgx/stdio.rs @@ -62,7 +62,7 @@ impl io::Write for Stderr { } } -pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; pub fn is_ebadf(err: &io::Error) -> bool { // FIXME: Rust normally maps Unix EBADF to `Uncategorized` @@ -76,7 +76,7 @@ pub fn panic_output() -> Option { // This function is needed by libunwind. The symbol is named in pre-link args // for the target specification, so keep that in sync. #[cfg(not(test))] -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn __rust_print_err(m: *mut u8, s: i32) { if s < 0 { return; diff --git a/library/std/src/sys/pal/sgx/thread.rs b/library/std/src/sys/pal/sgx/thread.rs index cecd53c352c5a..b6932df431f42 100644 --- a/library/std/src/sys/pal/sgx/thread.rs +++ b/library/std/src/sys/pal/sgx/thread.rs @@ -46,7 +46,7 @@ mod task_queue { } #[cfg_attr(test, linkage = "available_externally")] - #[export_name = "_ZN16__rust_internals3std3sys3sgx6thread10TASK_QUEUEE"] + #[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx6thread10TASK_QUEUEE")] static TASK_QUEUE: Mutex> = Mutex::new(Vec::new()); pub(super) fn lock() -> MutexGuard<'static, Vec> { diff --git a/library/std/src/sys/pal/solid/abi/fs.rs b/library/std/src/sys/pal/solid/abi/fs.rs index 6864a3e7745b9..7f2b1f83e8561 100644 --- a/library/std/src/sys/pal/solid/abi/fs.rs +++ b/library/std/src/sys/pal/solid/abi/fs.rs @@ -31,7 +31,7 @@ pub const DT_WHT: c_uchar = 14; pub type S_DIR = c_int; -extern "C" { +unsafe extern "C" { pub fn SOLID_FS_Open(fd: *mut c_int, path: *const c_char, mode: c_int) -> c_int; pub fn SOLID_FS_Close(fd: c_int) -> c_int; pub fn SOLID_FS_Read(fd: c_int, buf: *mut u8, size: usize, result: *mut usize) -> c_int; diff --git a/library/std/src/sys/pal/solid/abi/mod.rs b/library/std/src/sys/pal/solid/abi/mod.rs index 4d09705721748..819f93f407423 100644 --- a/library/std/src/sys/pal/solid/abi/mod.rs +++ b/library/std/src/sys/pal/solid/abi/mod.rs @@ -33,27 +33,27 @@ pub struct SOLID_RTC_TIME { pub tm_wday: c_int, } -extern "C" { +unsafe extern "C" { pub fn SOLID_RTC_ReadTime(time: *mut SOLID_RTC_TIME) -> c_int; } // `solid_log.h` -extern "C" { +unsafe extern "C" { pub fn SOLID_LOG_write(s: *const u8, l: usize); } // `solid_mem.h` -extern "C" { +unsafe extern "C" { pub fn SOLID_TLS_AddDestructor(id: i32, dtor: unsafe extern "C" fn(*mut u8)); } // `solid_rng.h` -extern "C" { +unsafe extern "C" { pub fn SOLID_RNG_SampleRandomBytes(buffer: *mut u8, length: usize) -> c_int; } // `rwlock.h` -extern "C" { +unsafe extern "C" { pub fn rwl_loc_rdl(id: ID) -> ER; pub fn rwl_loc_wrl(id: ID) -> ER; pub fn rwl_ploc_rdl(id: ID) -> ER; diff --git a/library/std/src/sys/pal/solid/abi/sockets.rs b/library/std/src/sys/pal/solid/abi/sockets.rs index 3c9e3f9ffb95d..80802dd42e248 100644 --- a/library/std/src/sys/pal/solid/abi/sockets.rs +++ b/library/std/src/sys/pal/solid/abi/sockets.rs @@ -158,7 +158,7 @@ pub struct fd_set { pub fds: [c_int; SOLID_NET_FD_SETSIZE], } -extern "C" { +unsafe extern "C" { #[link_name = "SOLID_NET_StrError"] pub fn strerror(errnum: c_int) -> *const c_char; diff --git a/library/std/src/sys/pal/solid/error.rs b/library/std/src/sys/pal/solid/error.rs index e092497856d43..b399463c0c280 100644 --- a/library/std/src/sys/pal/solid/error.rs +++ b/library/std/src/sys/pal/solid/error.rs @@ -1,6 +1,7 @@ pub use self::itron::error::{ItronError as SolidError, expect_success}; -use super::{abi, itron, net}; +use super::{abi, itron}; use crate::io::ErrorKind; +use crate::sys::net; /// Describe the specified SOLID error code. Returns `None` if it's an /// undefined error code. diff --git a/library/std/src/sys/pal/solid/fs.rs b/library/std/src/sys/pal/solid/fs.rs index 04dd10ad806d3..cc424141ea80c 100644 --- a/library/std/src/sys/pal/solid/fs.rs +++ b/library/std/src/sys/pal/solid/fs.rs @@ -12,29 +12,24 @@ use crate::sys::unsupported; pub use crate::sys_common::fs::exists; use crate::sys_common::ignore_notfound; +type CIntNotMinusOne = core::num::niche_types::NotAllOnes; + /// A file descriptor. #[derive(Clone, Copy)] -#[rustc_layout_scalar_valid_range_start(0)] -// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a -// 32-bit c_int. Below is -2, in two's complement, but that only works out -// because c_int is 32 bits. -#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] struct FileDesc { - fd: c_int, + fd: CIntNotMinusOne, } impl FileDesc { #[inline] + #[track_caller] fn new(fd: c_int) -> FileDesc { - assert_ne!(fd, -1i32); - // Safety: we just asserted that the value is in the valid range and - // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - unsafe { FileDesc { fd } } + FileDesc { fd: CIntNotMinusOne::new(fd).expect("fd != -1") } } #[inline] fn raw(&self) -> c_int { - self.fd + self.fd.as_inner() } } diff --git a/library/std/src/sys/pal/solid/io.rs b/library/std/src/sys/pal/solid/io.rs deleted file mode 100644 index 9ef4b7049b690..0000000000000 --- a/library/std/src/sys/pal/solid/io.rs +++ /dev/null @@ -1,86 +0,0 @@ -use libc::c_void; - -use super::abi::sockets::iovec; -use crate::marker::PhantomData; -use crate::slice; - -#[derive(Copy, Clone)] -#[repr(transparent)] -pub struct IoSlice<'a> { - vec: iovec, - _p: PhantomData<&'a [u8]>, -} - -impl<'a> IoSlice<'a> { - #[inline] - pub fn new(buf: &'a [u8]) -> IoSlice<'a> { - IoSlice { - vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if self.vec.iov_len < n { - panic!("advancing IoSlice beyond its length"); - } - - unsafe { - self.vec.iov_len -= n; - self.vec.iov_base = self.vec.iov_base.add(n); - } - } - - #[inline] - pub const fn as_slice(&self) -> &'a [u8] { - unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } -} - -#[repr(transparent)] -pub struct IoSliceMut<'a> { - vec: iovec, - _p: PhantomData<&'a mut [u8]>, -} - -impl<'a> IoSliceMut<'a> { - #[inline] - pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { - IoSliceMut { - vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if self.vec.iov_len < n { - panic!("advancing IoSliceMut beyond its length"); - } - - unsafe { - self.vec.iov_len -= n; - self.vec.iov_base = self.vec.iov_base.add(n); - } - } - - #[inline] - pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } - - #[inline] - pub const fn into_slice(self) -> &'a mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } - - #[inline] - pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } -} - -pub fn is_terminal(_: &T) -> bool { - false -} diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index d41042be51844..06af7bfade059 100644 --- a/library/std/src/sys/pal/solid/mod.rs +++ b/library/std/src/sys/pal/solid/mod.rs @@ -23,8 +23,6 @@ pub mod env; // `crate::sys::error` pub(crate) mod error; pub mod fs; -pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -51,7 +49,7 @@ pub fn unsupported_err() -> crate::io::Error { #[inline] pub fn is_interrupted(code: i32) -> bool { - net::is_interrupted(code) + crate::sys::net::is_interrupted(code) } pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { diff --git a/library/std/src/sys/pal/solid/net.rs b/library/std/src/sys/pal/solid/net.rs deleted file mode 100644 index 5f6436807e27e..0000000000000 --- a/library/std/src/sys/pal/solid/net.rs +++ /dev/null @@ -1,425 +0,0 @@ -use libc::{c_int, c_void, size_t}; - -use self::netc::{MSG_PEEK, sockaddr, socklen_t}; -use super::abi; -use crate::ffi::CStr; -use crate::io::{self, BorrowedBuf, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; -use crate::net::{Shutdown, SocketAddr}; -use crate::os::solid::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; -use crate::sys_common::{FromInner, IntoInner}; -use crate::time::Duration; -use crate::{cmp, mem, ptr, str}; - -pub mod netc { - pub use super::super::abi::sockets::*; -} - -pub type wrlen_t = size_t; - -const READ_LIMIT: usize = libc::ssize_t::MAX as usize; - -const fn max_iov() -> usize { - // Judging by the source code, it's unlimited, but specify a lower - // value just in case. - 1024 -} - -#[doc(hidden)] -pub trait IsMinusOne { - fn is_minus_one(&self) -> bool; -} - -macro_rules! impl_is_minus_one { - ($($t:ident)*) => ($(impl IsMinusOne for $t { - fn is_minus_one(&self) -> bool { - *self == -1 - } - })*) -} - -impl_is_minus_one! { i8 i16 i32 i64 isize } - -pub fn cvt(t: T) -> io::Result { - if t.is_minus_one() { Err(last_error()) } else { Ok(t) } -} - -/// A variant of `cvt` for `getaddrinfo` which return 0 for a success. -pub fn cvt_gai(err: c_int) -> io::Result<()> { - if err == 0 { - Ok(()) - } else { - let msg: &dyn crate::fmt::Display = match err { - netc::EAI_NONAME => &"name or service not known", - netc::EAI_SERVICE => &"service not supported", - netc::EAI_FAIL => &"non-recoverable failure in name resolution", - netc::EAI_MEMORY => &"memory allocation failure", - netc::EAI_FAMILY => &"family not supported", - _ => &err, - }; - Err(io::Error::new( - io::ErrorKind::Uncategorized, - &format!("failed to lookup address information: {msg}")[..], - )) - } -} - -/// Just to provide the same interface as sys/pal/unix/net.rs -pub fn cvt_r(mut f: F) -> io::Result -where - T: IsMinusOne, - F: FnMut() -> T, -{ - cvt(f()) -} - -/// Returns the last error from the network subsystem. -fn last_error() -> io::Error { - io::Error::from_raw_os_error(unsafe { netc::SOLID_NET_GetLastError() }) -} - -pub(super) fn error_name(er: abi::ER) -> Option<&'static str> { - unsafe { CStr::from_ptr(netc::strerror(er)) }.to_str().ok() -} - -#[inline] -pub fn is_interrupted(er: abi::ER) -> bool { - er == netc::SOLID_NET_ERR_BASE - libc::EINTR -} - -pub(super) fn decode_error_kind(er: abi::ER) -> ErrorKind { - let errno = netc::SOLID_NET_ERR_BASE - er; - match errno as libc::c_int { - libc::ECONNREFUSED => ErrorKind::ConnectionRefused, - libc::ECONNRESET => ErrorKind::ConnectionReset, - libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied, - libc::EPIPE => ErrorKind::BrokenPipe, - libc::ENOTCONN => ErrorKind::NotConnected, - libc::ECONNABORTED => ErrorKind::ConnectionAborted, - libc::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable, - libc::EADDRINUSE => ErrorKind::AddrInUse, - libc::ENOENT => ErrorKind::NotFound, - libc::EINTR => ErrorKind::Interrupted, - libc::EINVAL => ErrorKind::InvalidInput, - libc::ETIMEDOUT => ErrorKind::TimedOut, - libc::EEXIST => ErrorKind::AlreadyExists, - libc::ENOSYS => ErrorKind::Unsupported, - libc::ENOMEM => ErrorKind::OutOfMemory, - libc::EAGAIN => ErrorKind::WouldBlock, - - _ => ErrorKind::Uncategorized, - } -} - -pub fn init() {} - -pub struct Socket(OwnedFd); - -impl Socket { - pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { - let fam = match *addr { - SocketAddr::V4(..) => netc::AF_INET, - SocketAddr::V6(..) => netc::AF_INET6, - }; - Socket::new_raw(fam, ty) - } - - pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { - unsafe { - let fd = cvt(netc::socket(fam, ty, 0))?; - Ok(Self::from_raw_fd(fd)) - } - } - - pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { - let (addr, len) = addr.into_inner(); - cvt(unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?; - Ok(()) - } - - pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { - self.set_nonblocking(true)?; - let r = self.connect(addr); - self.set_nonblocking(false)?; - - match r { - Ok(_) => return Ok(()), - // there's no ErrorKind for EINPROGRESS - Err(ref e) if e.raw_os_error() == Some(netc::EINPROGRESS) => {} - Err(e) => return Err(e), - } - - if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { - return Err(io::Error::ZERO_TIMEOUT); - } - - let mut timeout = - netc::timeval { tv_sec: timeout.as_secs() as _, tv_usec: timeout.subsec_micros() as _ }; - if timeout.tv_sec == 0 && timeout.tv_usec == 0 { - timeout.tv_usec = 1; - } - - let fds = netc::fd_set { num_fds: 1, fds: [self.as_raw_fd()] }; - - let mut writefds = fds; - let mut errorfds = fds; - - let n = unsafe { - cvt(netc::select( - self.as_raw_fd() + 1, - ptr::null_mut(), - &mut writefds, - &mut errorfds, - &mut timeout, - ))? - }; - - match n { - 0 => Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")), - _ => { - let can_write = writefds.num_fds != 0; - if !can_write { - if let Some(e) = self.take_error()? { - return Err(e); - } - } - Ok(()) - } - } - } - - pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result { - let fd = cvt_r(|| unsafe { netc::accept(self.as_raw_fd(), storage, len) })?; - unsafe { Ok(Self::from_raw_fd(fd)) } - } - - pub fn duplicate(&self) -> io::Result { - Ok(Self(self.0.try_clone()?)) - } - - fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { - let ret = cvt(unsafe { - netc::recv(self.as_raw_fd(), buf.as_mut().as_mut_ptr().cast(), buf.capacity(), flags) - })?; - unsafe { - buf.advance_unchecked(ret as usize); - } - Ok(()) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), 0)?; - Ok(buf.len()) - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), MSG_PEEK)?; - Ok(buf.len()) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.recv_with_flags(buf, 0) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - let ret = cvt(unsafe { - netc::readv( - self.as_raw_fd(), - bufs.as_ptr() as *const netc::iovec, - cmp::min(bufs.len(), max_iov()) as c_int, - ) - })?; - Ok(ret as usize) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - true - } - - fn recv_from_with_flags( - &self, - buf: &mut [u8], - flags: c_int, - ) -> io::Result<(usize, SocketAddr)> { - let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() }; - let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t; - - let n = cvt(unsafe { - netc::recvfrom( - self.as_raw_fd(), - buf.as_mut_ptr() as *mut c_void, - buf.len(), - flags, - &mut storage as *mut _ as *mut _, - &mut addrlen, - ) - })?; - Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?)) - } - - pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, 0) - } - - pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, MSG_PEEK) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - let ret = cvt(unsafe { - netc::write( - self.as_raw_fd(), - buf.as_ptr() as *const c_void, - cmp::min(buf.len(), READ_LIMIT), - ) - })?; - Ok(ret as usize) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - let ret = cvt(unsafe { - netc::writev( - self.as_raw_fd(), - bufs.as_ptr() as *const netc::iovec, - cmp::min(bufs.len(), max_iov()) as c_int, - ) - })?; - Ok(ret as usize) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - true - } - - pub fn set_timeout(&self, dur: Option, kind: c_int) -> io::Result<()> { - let timeout = match dur { - Some(dur) => { - if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { - return Err(io::Error::ZERO_TIMEOUT); - } - - let secs = if dur.as_secs() > netc::c_long::MAX as u64 { - netc::c_long::MAX - } else { - dur.as_secs() as netc::c_long - }; - let mut timeout = netc::timeval { tv_sec: secs, tv_usec: dur.subsec_micros() as _ }; - if timeout.tv_sec == 0 && timeout.tv_usec == 0 { - timeout.tv_usec = 1; - } - timeout - } - None => netc::timeval { tv_sec: 0, tv_usec: 0 }, - }; - setsockopt(self, netc::SOL_SOCKET, kind, timeout) - } - - pub fn timeout(&self, kind: c_int) -> io::Result> { - let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?; - if raw.tv_sec == 0 && raw.tv_usec == 0 { - Ok(None) - } else { - let sec = raw.tv_sec as u64; - let nsec = (raw.tv_usec as u32) * 1000; - Ok(Some(Duration::new(sec, nsec))) - } - } - - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - let how = match how { - Shutdown::Write => netc::SHUT_WR, - Shutdown::Read => netc::SHUT_RD, - Shutdown::Both => netc::SHUT_RDWR, - }; - cvt(unsafe { netc::shutdown(self.as_raw_fd(), how) })?; - Ok(()) - } - - pub fn set_linger(&self, linger: Option) -> io::Result<()> { - let linger = netc::linger { - l_onoff: linger.is_some() as netc::c_int, - l_linger: linger.unwrap_or_default().as_secs() as netc::c_int, - }; - - setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger) - } - - pub fn linger(&self) -> io::Result> { - let val: netc::linger = getsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER)?; - - Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) - } - - pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int) - } - - pub fn nodelay(&self) -> io::Result { - let raw: c_int = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?; - Ok(raw != 0) - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - let mut nonblocking = nonblocking as c_int; - cvt(unsafe { - netc::ioctl(self.as_raw_fd(), netc::FIONBIO, (&mut nonblocking) as *mut c_int as _) - }) - .map(drop) - } - - pub fn take_error(&self) -> io::Result> { - let raw: c_int = getsockopt(self, netc::SOL_SOCKET, netc::SO_ERROR)?; - if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } - } - - // This method is used by sys_common code to abstract over targets. - pub fn as_raw(&self) -> c_int { - self.as_raw_fd() - } -} - -impl FromInner for Socket { - #[inline] - fn from_inner(sock: OwnedFd) -> Socket { - Socket(sock) - } -} - -impl IntoInner for Socket { - #[inline] - fn into_inner(self) -> OwnedFd { - self.0 - } -} - -impl AsFd for Socket { - #[inline] - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl AsRawFd for Socket { - #[inline] - fn as_raw_fd(&self) -> c_int { - self.0.as_raw_fd() - } -} - -impl FromRawFd for Socket { - #[inline] - unsafe fn from_raw_fd(fd: c_int) -> Socket { - unsafe { Self(FromRawFd::from_raw_fd(fd)) } - } -} - -impl IntoRawFd for Socket { - #[inline] - fn into_raw_fd(self) -> c_int { - self.0.into_raw_fd() - } -} diff --git a/library/std/src/sys/pal/solid/os.rs b/library/std/src/sys/pal/solid/os.rs index 57c28aed3b293..e3b2e0aa50f4a 100644 --- a/library/std/src/sys/pal/solid/os.rs +++ b/library/std/src/sys/pal/solid/os.rs @@ -129,7 +129,7 @@ impl Iterator for Env { /// Returns a vector of (variable, value) byte-vector pairs for all the /// environment variables of the current process. pub fn env() -> Env { - extern "C" { + unsafe extern "C" { static mut environ: *const *const c_char; } diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index a9900f55b1926..f850fefc8f22f 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -13,9 +13,6 @@ pub mod env; //pub mod fd; #[path = "../unsupported/fs.rs"] pub mod fs; -#[path = "../unsupported/io.rs"] -pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/teeos/net.rs b/library/std/src/sys/pal/teeos/net.rs deleted file mode 100644 index fed95205027a7..0000000000000 --- a/library/std/src/sys/pal/teeos/net.rs +++ /dev/null @@ -1,369 +0,0 @@ -use crate::fmt; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; -use crate::sys::unsupported; -use crate::time::Duration; - -pub struct TcpStream(!); - -impl TcpStream { - pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { - unsupported() - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn read_timeout(&self) -> io::Result> { - self.0 - } - - pub fn write_timeout(&self) -> io::Result> { - self.0 - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn read(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> { - self.0 - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - self.0 - } - - pub fn is_read_vectored(&self) -> bool { - self.0 - } - - pub fn write(&self, _: &[u8]) -> io::Result { - self.0 - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - self.0 - } - - pub fn is_write_vectored(&self) -> bool { - self.0 - } - - pub fn peer_addr(&self) -> io::Result { - self.0 - } - - pub fn socket_addr(&self) -> io::Result { - self.0 - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn linger(&self) -> io::Result> { - self.0 - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn nodelay(&self) -> io::Result { - self.0 - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn ttl(&self) -> io::Result { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - self.0 - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 - } -} - -impl fmt::Debug for TcpStream { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -pub struct TcpListener(!); - -impl TcpListener { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn socket_addr(&self) -> io::Result { - self.0 - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn ttl(&self) -> io::Result { - self.0 - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn only_v6(&self) -> io::Result { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - self.0 - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 - } -} - -impl fmt::Debug for TcpListener { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -pub struct UdpSocket(!); - -impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn peer_addr(&self) -> io::Result { - self.0 - } - - pub fn socket_addr(&self) -> io::Result { - self.0 - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.0 - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.0 - } - - pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn read_timeout(&self) -> io::Result> { - self.0 - } - - pub fn write_timeout(&self) -> io::Result> { - self.0 - } - - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn broadcast(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn multicast_loop_v4(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn multicast_ttl_v4(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn multicast_loop_v6(&self) -> io::Result { - self.0 - } - - pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - self.0 - } - - pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - self.0 - } - - pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - self.0 - } - - pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - self.0 - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn ttl(&self) -> io::Result { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - self.0 - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn recv(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn send(&self, _: &[u8]) -> io::Result { - self.0 - } - - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - self.0 - } -} - -impl fmt::Debug for UdpSocket { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -pub struct LookupHost(!); - -impl LookupHost { - pub fn port(&self) -> u16 { - self.0 - } -} - -impl Iterator for LookupHost { - type Item = SocketAddr; - fn next(&mut self) -> Option { - self.0 - } -} - -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &str) -> io::Result { - unsupported() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - unsupported() - } -} - -#[allow(nonstandard_style)] -pub mod netc { - pub const AF_INET: u8 = 0; - pub const AF_INET6: u8 = 1; - pub type sa_family_t = u8; - - #[derive(Copy, Clone)] - pub struct in_addr { - pub s_addr: u32, - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in { - pub sin_family: sa_family_t, - pub sin_port: u16, - pub sin_addr: in_addr, - } - - #[derive(Copy, Clone)] - pub struct in6_addr { - pub s6_addr: [u8; 16], - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in6 { - pub sin6_family: sa_family_t, - pub sin6_port: u16, - pub sin6_addr: in6_addr, - pub sin6_flowinfo: u32, - pub sin6_scope_id: u32, - } -} - -pub type Socket = UdpSocket; diff --git a/library/std/src/sys/pal/teeos/thread.rs b/library/std/src/sys/pal/teeos/thread.rs index 15c65240ddd2a..c779c5f3ed89c 100644 --- a/library/std/src/sys/pal/teeos/thread.rs +++ b/library/std/src/sys/pal/teeos/thread.rs @@ -16,7 +16,7 @@ pub struct Thread { unsafe impl Send for Thread {} unsafe impl Sync for Thread {} -extern "C" { +unsafe extern "C" { pub fn TEE_Wait(timeout: u32) -> u32; } diff --git a/library/std/src/sys/pal/uefi/fs.rs b/library/std/src/sys/pal/uefi/fs.rs new file mode 100644 index 0000000000000..9585ec24f687d --- /dev/null +++ b/library/std/src/sys/pal/uefi/fs.rs @@ -0,0 +1,344 @@ +use crate::ffi::OsString; +use crate::fmt; +use crate::hash::{Hash, Hasher}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::path::{Path, PathBuf}; +use crate::sys::time::SystemTime; +use crate::sys::unsupported; + +pub struct File(!); + +pub struct FileAttr(!); + +pub struct ReadDir(!); + +pub struct DirEntry(!); + +#[derive(Clone, Debug)] +pub struct OpenOptions {} + +#[derive(Copy, Clone, Debug, Default)] +pub struct FileTimes {} + +pub struct FilePermissions(!); + +pub struct FileType(!); + +#[derive(Debug)] +pub struct DirBuilder {} + +impl FileAttr { + pub fn size(&self) -> u64 { + self.0 + } + + pub fn perm(&self) -> FilePermissions { + self.0 + } + + pub fn file_type(&self) -> FileType { + self.0 + } + + pub fn modified(&self) -> io::Result { + self.0 + } + + pub fn accessed(&self) -> io::Result { + self.0 + } + + pub fn created(&self) -> io::Result { + self.0 + } +} + +impl Clone for FileAttr { + fn clone(&self) -> FileAttr { + self.0 + } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { + self.0 + } + + pub fn set_readonly(&mut self, _readonly: bool) { + self.0 + } +} + +impl Clone for FilePermissions { + fn clone(&self) -> FilePermissions { + self.0 + } +} + +impl PartialEq for FilePermissions { + fn eq(&self, _other: &FilePermissions) -> bool { + self.0 + } +} + +impl Eq for FilePermissions {} + +impl fmt::Debug for FilePermissions { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl FileTimes { + pub fn set_accessed(&mut self, _t: SystemTime) {} + pub fn set_modified(&mut self, _t: SystemTime) {} +} + +impl FileType { + pub fn is_dir(&self) -> bool { + self.0 + } + + pub fn is_file(&self) -> bool { + self.0 + } + + pub fn is_symlink(&self) -> bool { + self.0 + } +} + +impl Clone for FileType { + fn clone(&self) -> FileType { + self.0 + } +} + +impl Copy for FileType {} + +impl PartialEq for FileType { + fn eq(&self, _other: &FileType) -> bool { + self.0 + } +} + +impl Eq for FileType {} + +impl Hash for FileType { + fn hash(&self, _h: &mut H) { + self.0 + } +} + +impl fmt::Debug for FileType { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl fmt::Debug for ReadDir { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +impl Iterator for ReadDir { + type Item = io::Result; + + fn next(&mut self) -> Option> { + self.0 + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + self.0 + } + + pub fn file_name(&self) -> OsString { + self.0 + } + + pub fn metadata(&self) -> io::Result { + self.0 + } + + pub fn file_type(&self) -> io::Result { + self.0 + } +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + OpenOptions {} + } + + pub fn read(&mut self, _read: bool) {} + pub fn write(&mut self, _write: bool) {} + pub fn append(&mut self, _append: bool) {} + pub fn truncate(&mut self, _truncate: bool) {} + pub fn create(&mut self, _create: bool) {} + pub fn create_new(&mut self, _create_new: bool) {} +} + +impl File { + pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result { + unsupported() + } + + pub fn file_attr(&self) -> io::Result { + self.0 + } + + pub fn fsync(&self) -> io::Result<()> { + self.0 + } + + pub fn datasync(&self) -> io::Result<()> { + self.0 + } + + pub fn lock(&self) -> io::Result<()> { + self.0 + } + + pub fn lock_shared(&self) -> io::Result<()> { + self.0 + } + + pub fn try_lock(&self) -> io::Result { + self.0 + } + + pub fn try_lock_shared(&self) -> io::Result { + self.0 + } + + pub fn unlock(&self) -> io::Result<()> { + self.0 + } + + pub fn truncate(&self, _size: u64) -> io::Result<()> { + self.0 + } + + pub fn read(&self, _buf: &mut [u8]) -> io::Result { + self.0 + } + + pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.0 + } + + pub fn is_read_vectored(&self) -> bool { + self.0 + } + + pub fn read_buf(&self, _cursor: BorrowedCursor<'_>) -> io::Result<()> { + self.0 + } + + pub fn write(&self, _buf: &[u8]) -> io::Result { + self.0 + } + + pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result { + self.0 + } + + pub fn is_write_vectored(&self) -> bool { + self.0 + } + + pub fn flush(&self) -> io::Result<()> { + self.0 + } + + pub fn seek(&self, _pos: SeekFrom) -> io::Result { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { + self.0 + } + + pub fn set_times(&self, _times: FileTimes) -> io::Result<()> { + self.0 + } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder {} + } + + pub fn mkdir(&self, _p: &Path) -> io::Result<()> { + unsupported() + } +} + +impl fmt::Debug for File { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +pub fn readdir(_p: &Path) -> io::Result { + unsupported() +} + +pub fn unlink(_p: &Path) -> io::Result<()> { + unsupported() +} + +pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { + unsupported() +} + +pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { + match perm.0 {} +} + +pub fn rmdir(_p: &Path) -> io::Result<()> { + unsupported() +} + +pub fn remove_dir_all(_path: &Path) -> io::Result<()> { + unsupported() +} + +pub fn exists(_path: &Path) -> io::Result { + unsupported() +} + +pub fn readlink(_p: &Path) -> io::Result { + unsupported() +} + +pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> { + unsupported() +} + +pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { + unsupported() +} + +pub fn stat(_p: &Path) -> io::Result { + unsupported() +} + +pub fn lstat(_p: &Path) -> io::Result { + unsupported() +} + +pub fn canonicalize(_p: &Path) -> io::Result { + unsupported() +} + +pub fn copy(_from: &Path, _to: &Path) -> io::Result { + unsupported() +} diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index 47d9add72b5f0..dccc137d6f561 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -14,10 +14,12 @@ use r_efi::protocols::{device_path, device_path_to_text, shell}; use crate::ffi::{OsStr, OsString}; use crate::io::{self, const_error}; +use crate::marker::PhantomData; use crate::mem::{MaybeUninit, size_of}; use crate::os::uefi::env::boot_services; use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; use crate::os::uefi::{self}; +use crate::path::Path; use crate::ptr::NonNull; use crate::slice; use crate::sync::atomic::{AtomicPtr, Ordering}; @@ -222,14 +224,14 @@ pub(crate) fn runtime_services() -> Option> NonNull::new(runtime_services) } -pub(crate) struct DevicePath(NonNull); +pub(crate) struct OwnedDevicePath(NonNull); -impl DevicePath { +impl OwnedDevicePath { pub(crate) fn from_text(p: &OsStr) -> io::Result { fn inner( p: &OsStr, protocol: NonNull, - ) -> io::Result { + ) -> io::Result { let path_vec = p.encode_wide().chain(Some(0)).collect::>(); if path_vec[..path_vec.len() - 1].contains(&0) { return Err(const_error!( @@ -242,7 +244,7 @@ impl DevicePath { unsafe { ((*protocol.as_ptr()).convert_text_to_device_path)(path_vec.as_ptr()) }; NonNull::new(path) - .map(DevicePath) + .map(OwnedDevicePath) .ok_or_else(|| const_error!(io::ErrorKind::InvalidFilename, "Invalid Device Path")) } @@ -275,12 +277,16 @@ impl DevicePath { )) } - pub(crate) fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol { + pub(crate) const fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol { self.0.as_ptr() } + + pub(crate) const fn borrow<'a>(&'a self) -> BorrowedDevicePath<'a> { + BorrowedDevicePath::new(self.0) + } } -impl Drop for DevicePath { +impl Drop for OwnedDevicePath { fn drop(&mut self) { if let Some(bt) = boot_services() { let bt: NonNull = bt.cast(); @@ -291,6 +297,39 @@ impl Drop for DevicePath { } } +impl crate::fmt::Debug for OwnedDevicePath { + fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { + match self.borrow().to_text() { + Ok(p) => p.fmt(f), + Err(_) => f.debug_struct("OwnedDevicePath").finish_non_exhaustive(), + } + } +} + +pub(crate) struct BorrowedDevicePath<'a> { + protocol: NonNull, + phantom: PhantomData<&'a r_efi::protocols::device_path::Protocol>, +} + +impl<'a> BorrowedDevicePath<'a> { + pub(crate) const fn new(protocol: NonNull) -> Self { + Self { protocol, phantom: PhantomData } + } + + pub(crate) fn to_text(&self) -> io::Result { + device_path_to_text(self.protocol) + } +} + +impl<'a> crate::fmt::Debug for BorrowedDevicePath<'a> { + fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { + match self.to_text() { + Ok(p) => p.fmt(f), + Err(_) => f.debug_struct("BorrowedDevicePath").finish_non_exhaustive(), + } + } +} + pub(crate) struct OwnedProtocol { guid: r_efi::efi::Guid, handle: NonNull, @@ -443,3 +482,21 @@ pub(crate) fn open_shell() -> Option> { None } + +/// Get device path protocol associated with shell mapping. +/// +/// returns None in case no such mapping is exists +pub(crate) fn get_device_path_from_map(map: &Path) -> io::Result> { + let shell = + open_shell().ok_or(io::const_error!(io::ErrorKind::NotFound, "UEFI Shell not found"))?; + let mut path = os_string_to_raw(map.as_os_str()) + .ok_or(io::const_error!(io::ErrorKind::InvalidFilename, "Invalid UEFI shell mapping"))?; + + // The Device Path Protocol pointer returned by UEFI shell is owned by the shell and is not + // freed throughout it's lifetime. So it has a 'static lifetime. + let protocol = unsafe { ((*shell.as_ptr()).get_device_path_from_map)(path.as_mut_ptr()) }; + let protocol = NonNull::new(protocol) + .ok_or(io::const_error!(io::ErrorKind::NotFound, "UEFI Shell mapping not found"))?; + + Ok(BorrowedDevicePath::new(protocol)) +} diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index f29c91f3bfe68..8e6aea452f838 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -15,13 +15,8 @@ pub mod args; pub mod env; -#[path = "../unsupported/fs.rs"] pub mod fs; pub mod helpers; -#[path = "../unsupported/io.rs"] -pub mod io; -#[path = "../unsupported/net.rs"] -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -174,7 +169,7 @@ pub fn abort_internal() -> ! { // This function is needed by the panic runtime. The symbol is named in // pre-link args for the target specification, so keep that in sync. #[cfg(not(test))] -#[no_mangle] +#[unsafe(no_mangle)] pub extern "C" fn __rust_abort() { abort_internal(); } diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index 6d23c72ef2209..e305b8610c9f8 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -17,111 +17,50 @@ pub fn errno() -> RawOsError { pub fn error_string(errno: RawOsError) -> String { // Keep the List in Alphabetical Order // The Messages are taken from UEFI Specification Appendix D - Status Codes - match r_efi::efi::Status::from_usize(errno) { - Status::ABORTED => "The operation was aborted.".to_owned(), - Status::ACCESS_DENIED => "Access was denied.".to_owned(), - Status::ALREADY_STARTED => "The protocol has already been started.".to_owned(), - Status::BAD_BUFFER_SIZE => "The buffer was not the proper size for the request.".to_owned(), - Status::BUFFER_TOO_SMALL => { - "The buffer is not large enough to hold the requested data. The required buffer size is returned in the appropriate parameter when this error occurs.".to_owned() - } - Status::COMPROMISED_DATA => { - "The security status of the data is unknown or compromised and the data must be updated or replaced to restore a valid security status.".to_owned() - } - Status::CONNECTION_FIN => { - "The receiving operation fails because the communication peer has closed the connection and there is no more data in the receive buffer of the instance.".to_owned() - } - Status::CONNECTION_REFUSED => { - "The receiving or transmission operation fails because this connection is refused.".to_owned() - } - Status::CONNECTION_RESET => { - "The connect fails because the connection is reset either by instance itself or the communication peer.".to_owned() - } - Status::CRC_ERROR => "A CRC error was detected.".to_owned(), - Status::DEVICE_ERROR => "The physical device reported an error while attempting the operation.".to_owned() - , - Status::END_OF_FILE => { - "The end of the file was reached.".to_owned() - } - Status::END_OF_MEDIA => { - "Beginning or end of media was reached".to_owned() - } - Status::HOST_UNREACHABLE => { - "The remote host is not reachable.".to_owned() - } - Status::HTTP_ERROR => { - "A HTTP error occurred during the network operation.".to_owned() - } - Status::ICMP_ERROR => { - "An ICMP error occurred during the network operation.".to_owned() - } - Status::INCOMPATIBLE_VERSION => { - "The function encountered an internal version that was incompatible with a version requested by the caller.".to_owned() - } - Status::INVALID_LANGUAGE => { - "The language specified was invalid.".to_owned() - } - Status::INVALID_PARAMETER => { - "A parameter was incorrect.".to_owned() - } - Status::IP_ADDRESS_CONFLICT => { - "There is an address conflict address allocation".to_owned() - } - Status::LOAD_ERROR => { - "The image failed to load.".to_owned() - } - Status::MEDIA_CHANGED => { - "The medium in the device has changed since the last access.".to_owned() - } - Status::NETWORK_UNREACHABLE => { - "The network containing the remote host is not reachable.".to_owned() - } - Status::NO_MAPPING => { - "A mapping to a device does not exist.".to_owned() - } - Status::NO_MEDIA => { - "The device does not contain any medium to perform the operation.".to_owned() - } - Status::NO_RESPONSE => { - "The server was not found or did not respond to the request.".to_owned() - } - Status::NOT_FOUND => "The item was not found.".to_owned(), - Status::NOT_READY => { - "There is no data pending upon return.".to_owned() - } - Status::NOT_STARTED => { - "The protocol has not been started.".to_owned() - } - Status::OUT_OF_RESOURCES => { - "A resource has run out.".to_owned() - } - Status::PROTOCOL_ERROR => { - "A protocol error occurred during the network operation.".to_owned() - } - Status::PROTOCOL_UNREACHABLE => { - "An ICMP protocol unreachable error is received.".to_owned() - } - Status::SECURITY_VIOLATION => { - "The function was not performed due to a security violation.".to_owned() - } - Status::TFTP_ERROR => { - "A TFTP error occurred during the network operation.".to_owned() - } - Status::TIMEOUT => "The timeout time expired.".to_owned(), - Status::UNSUPPORTED => { - "The operation is not supported.".to_owned() - } - Status::VOLUME_FULL => { - "There is no more space on the file system.".to_owned() - } - Status::VOLUME_CORRUPTED => { - "An inconstancy was detected on the file system causing the operating to fail.".to_owned() - } - Status::WRITE_PROTECTED => { - "The device cannot be written to.".to_owned() - } - _ => format!("Status: {}", errno), - } + #[rustfmt::skip] + let msg = match r_efi::efi::Status::from_usize(errno) { + Status::ABORTED => "The operation was aborted.", + Status::ACCESS_DENIED => "Access was denied.", + Status::ALREADY_STARTED => "The protocol has already been started.", + Status::BAD_BUFFER_SIZE => "The buffer was not the proper size for the request.", + Status::BUFFER_TOO_SMALL => "The buffer is not large enough to hold the requested data. The required buffer size is returned in the appropriate parameter when this error occurs.", + Status::COMPROMISED_DATA => "The security status of the data is unknown or compromised and the data must be updated or replaced to restore a valid security status.", + Status::CONNECTION_FIN => "The receiving operation fails because the communication peer has closed the connection and there is no more data in the receive buffer of the instance.", + Status::CONNECTION_REFUSED => "The receiving or transmission operation fails because this connection is refused.", + Status::CONNECTION_RESET => "The connect fails because the connection is reset either by instance itself or the communication peer.", + Status::CRC_ERROR => "A CRC error was detected.", + Status::DEVICE_ERROR => "The physical device reported an error while attempting the operation.", + Status::END_OF_FILE => "The end of the file was reached.", + Status::END_OF_MEDIA => "Beginning or end of media was reached", + Status::HOST_UNREACHABLE => "The remote host is not reachable.", + Status::HTTP_ERROR => "A HTTP error occurred during the network operation.", + Status::ICMP_ERROR => "An ICMP error occurred during the network operation.", + Status::INCOMPATIBLE_VERSION => "The function encountered an internal version that was incompatible with a version requested by the caller.", + Status::INVALID_LANGUAGE => "The language specified was invalid.", + Status::INVALID_PARAMETER => "A parameter was incorrect.", + Status::IP_ADDRESS_CONFLICT => "There is an address conflict address allocation", + Status::LOAD_ERROR => "The image failed to load.", + Status::MEDIA_CHANGED => "The medium in the device has changed since the last access.", + Status::NETWORK_UNREACHABLE => "The network containing the remote host is not reachable.", + Status::NO_MAPPING => "A mapping to a device does not exist.", + Status::NO_MEDIA => "The device does not contain any medium to perform the operation.", + Status::NO_RESPONSE => "The server was not found or did not respond to the request.", + Status::NOT_FOUND => "The item was not found.", + Status::NOT_READY => "There is no data pending upon return.", + Status::NOT_STARTED => "The protocol has not been started.", + Status::OUT_OF_RESOURCES => "A resource has run out.", + Status::PROTOCOL_ERROR => "A protocol error occurred during the network operation.", + Status::PROTOCOL_UNREACHABLE => "An ICMP protocol unreachable error is received.", + Status::SECURITY_VIOLATION => "The function was not performed due to a security violation.", + Status::TFTP_ERROR => "A TFTP error occurred during the network operation.", + Status::TIMEOUT => "The timeout time expired.", + Status::UNSUPPORTED => "The operation is not supported.", + Status::VOLUME_FULL => "There is no more space on the file system.", + Status::VOLUME_CORRUPTED => "An inconstancy was detected on the file system causing the operating to fail.", + Status::WRITE_PROTECTED => "The device cannot be written to.", + _ => return format!("Status: {errno}"), + }; + msg.to_owned() } pub fn getcwd() -> io::Result { @@ -314,7 +253,7 @@ mod uefi_env { let mut start = 0; - // UEFI Shell returns all keys seperated by NULL. + // UEFI Shell returns all keys separated by NULL. // End of string is denoted by two NULLs for i in 0.. { if unsafe { *val.add(i) } == 0 { diff --git a/library/std/src/sys/pal/uefi/process.rs b/library/std/src/sys/pal/uefi/process.rs index 95707ebb7f023..9efe9a314f2e9 100644 --- a/library/std/src/sys/pal/uefi/process.rs +++ b/library/std/src/sys/pal/uefi/process.rs @@ -1,6 +1,7 @@ use r_efi::protocols::simple_text_output; use super::helpers; +use crate::collections::BTreeMap; pub use crate::ffi::OsString as EnvKey; use crate::ffi::{OsStr, OsString}; use crate::num::{NonZero, NonZeroI32}; @@ -21,6 +22,7 @@ pub struct Command { args: Vec, stdout: Option, stderr: Option, + env: CommandEnv, } // passed back to std::process with the pipes connected to the child, if any @@ -40,7 +42,13 @@ pub enum Stdio { impl Command { pub fn new(program: &OsStr) -> Command { - Command { prog: program.to_os_string(), args: Vec::new(), stdout: None, stderr: None } + Command { + prog: program.to_os_string(), + args: Vec::new(), + stdout: None, + stderr: None, + env: Default::default(), + } } pub fn arg(&mut self, arg: &OsStr) { @@ -48,7 +56,7 @@ impl Command { } pub fn env_mut(&mut self) -> &mut CommandEnv { - panic!("unsupported") + &mut self.env } pub fn cwd(&mut self, _dir: &OsStr) { @@ -76,7 +84,7 @@ impl Command { } pub fn get_envs(&self) -> CommandEnvs<'_> { - panic!("unsupported") + self.env.iter() } pub fn get_current_dir(&self) -> Option<&Path> { @@ -140,8 +148,30 @@ impl Command { cmd.stderr_inherit() }; + let env = env_changes(&self.env); + + // Set any new vars + if let Some(e) = &env { + for (k, (_, v)) in e { + match v { + Some(v) => unsafe { crate::env::set_var(k, v) }, + None => unsafe { crate::env::remove_var(k) }, + } + } + } + let stat = cmd.start_image()?; + // Rollback any env changes + if let Some(e) = env { + for (k, (v, _)) in e { + match v { + Some(v) => unsafe { crate::env::set_var(k, v) }, + None => unsafe { crate::env::remove_var(k) }, + } + } + } + let stdout = cmd.stdout()?; let stderr = cmd.stderr()?; @@ -326,7 +356,7 @@ mod uefi_command_internal { impl Image { pub fn load_image(p: &OsStr) -> io::Result { - let path = helpers::DevicePath::from_text(p)?; + let path = helpers::OwnedDevicePath::from_text(p)?; let boot_services: NonNull = boot_services() .ok_or_else(|| const_error!(io::ErrorKind::NotFound, "Boot Services not found"))? .cast(); @@ -460,7 +490,7 @@ mod uefi_command_internal { helpers::open_protocol(self.handle, loaded_image::PROTOCOL_GUID).unwrap(); let len = args.len(); - let args_size: u32 = crate::mem::size_of_val(&args).try_into().unwrap(); + let args_size: u32 = (len * crate::mem::size_of::()).try_into().unwrap(); let ptr = Box::into_raw(args).as_mut_ptr(); unsafe { @@ -706,9 +736,10 @@ mod uefi_command_internal { res.push(QUOTE); res.extend(prog.encode_wide()); res.push(QUOTE); - res.push(SPACE); for arg in args { + res.push(SPACE); + // Wrap the argument in quotes to be treat as single arg res.push(QUOTE); for c in arg.encode_wide() { @@ -719,10 +750,37 @@ mod uefi_command_internal { res.push(c); } res.push(QUOTE); - - res.push(SPACE); } res.into_boxed_slice() } } + +/// Create a map of environment variable changes. Allows efficient setting and rolling back of +/// enviroment variable changes. +/// +/// Entry: (Old Value, New Value) +fn env_changes(env: &CommandEnv) -> Option, Option)>> { + if env.is_unchanged() { + return None; + } + + let mut result = BTreeMap::, Option)>::new(); + + // Check if we want to clear all prior variables + if env.does_clear() { + for (k, v) in crate::env::vars_os() { + result.insert(k.into(), (Some(v), None)); + } + } + + for (k, v) in env.iter() { + let v: Option = v.map(Into::into); + result + .entry(k.into()) + .and_modify(|cur| *cur = (cur.0.clone(), v.clone())) + .or_insert((crate::env::var_os(k), v)); + } + + Some(result) +} diff --git a/library/std/src/sys/pal/unix/args.rs b/library/std/src/sys/pal/unix/args.rs index 8438a61e90feb..1c87a79803c0d 100644 --- a/library/std/src/sys/pal/unix/args.rs +++ b/library/std/src/sys/pal/unix/args.rs @@ -147,7 +147,7 @@ mod imp { /// This allows `std::env::args` to work even in a `cdylib`, as it does on macOS and Windows. #[cfg(all(target_os = "linux", target_env = "gnu"))] #[used] - #[link_section = ".init_array.00099"] + #[unsafe(link_section = ".init_array.00099")] static ARGV_INIT_ARRAY: extern "C" fn( crate::os::raw::c_int, *const *const u8, @@ -204,7 +204,7 @@ mod imp { } pub fn argc_argv() -> (isize, *const *const c_char) { - extern "C" { + unsafe extern "C" { // These functions are in crt_externs.h. fn _NSGetArgc() -> *mut c_int; fn _NSGetArgv() -> *mut *mut *mut c_char; diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs index 54fa31620aced..00cfa7a7fcfda 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -9,9 +9,12 @@ use libc::c_char; #[cfg(any( all(target_os = "linux", not(target_env = "musl")), target_os = "android", + target_os = "fuchsia", target_os = "hurd" ))] use libc::dirfd; +#[cfg(target_os = "fuchsia")] +use libc::fstatat as fstatat64; #[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] use libc::fstatat64; #[cfg(any( @@ -709,7 +712,7 @@ impl Iterator for ReadDir { // thread safety for readdir() as long an individual DIR* is not accessed // concurrently, which is sufficient for Rust. super::os::set_errno(0); - let entry_ptr = readdir64(self.inner.dirp.0); + let entry_ptr: *const dirent64 = readdir64(self.inner.dirp.0); if entry_ptr.is_null() { // We either encountered an error, or reached the end. Either way, // the next call to next() should return None. @@ -735,44 +738,32 @@ impl Iterator for ReadDir { // contents were "simply" partially initialized data. // // Like for uninitialized contents, converting entry_ptr to `&dirent64` - // would not be legal. However, unique to dirent64 is that we don't even - // get to use `&raw const (*entry_ptr).d_name` because that operation - // requires the full extent of *entry_ptr to be in bounds of the same - // allocation, which is not necessarily the case here. - // - // Instead we must access fields individually through their offsets. - macro_rules! offset_ptr { - ($entry_ptr:expr, $field:ident) => {{ - const OFFSET: isize = mem::offset_of!(dirent64, $field) as isize; - if true { - // Cast to the same type determined by the else branch. - $entry_ptr.byte_offset(OFFSET).cast::<_>() - } else { - #[allow(deref_nullptr)] - { - &raw const (*ptr::null::()).$field - } - } - }}; - } + // would not be legal. However, we can use `&raw const (*entry_ptr).d_name` + // to refer the fields individually, because that operation is equivalent + // to `byte_offset` and thus does not require the full extent of `*entry_ptr` + // to be in bounds of the same allocation, only the offset of the field + // being referenced. // d_name is guaranteed to be null-terminated. - let name = CStr::from_ptr(offset_ptr!(entry_ptr, d_name).cast()); + let name = CStr::from_ptr((&raw const (*entry_ptr).d_name).cast()); let name_bytes = name.to_bytes(); if name_bytes == b"." || name_bytes == b".." { continue; } + // When loading from a field, we can skip the `&raw const`; `(*entry_ptr).d_ino` as + // a value expression will do the right thing: `byte_offset` to the field and then + // only access those bytes. #[cfg(not(target_os = "vita"))] let entry = dirent64_min { - d_ino: *offset_ptr!(entry_ptr, d_ino) as u64, + d_ino: (*entry_ptr).d_ino as u64, #[cfg(not(any( target_os = "solaris", target_os = "illumos", target_os = "aix", target_os = "nto", )))] - d_type: *offset_ptr!(entry_ptr, d_type) as u8, + d_type: (*entry_ptr).d_type as u8, }; #[cfg(target_os = "vita")] @@ -860,7 +851,6 @@ impl Drop for Dir { target_os = "vita", target_os = "hurd", target_os = "espidf", - target_os = "fuchsia", target_os = "horizon", target_os = "vxworks", target_os = "rtems", @@ -892,6 +882,7 @@ impl DirEntry { any( all(target_os = "linux", not(target_env = "musl")), target_os = "android", + target_os = "fuchsia", target_os = "hurd" ), not(miri) // no dirfd on Miri @@ -920,6 +911,7 @@ impl DirEntry { not(any( all(target_os = "linux", not(target_env = "musl")), target_os = "android", + target_os = "fuchsia", target_os = "hurd", )), miri @@ -1223,6 +1215,7 @@ impl File { } #[cfg(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "android", target_os = "netbsd", @@ -1235,6 +1228,7 @@ impl File { } #[cfg(not(any( target_os = "android", + target_os = "fuchsia", target_os = "freebsd", target_os = "linux", target_os = "netbsd", @@ -1250,6 +1244,7 @@ impl File { #[cfg(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1261,6 +1256,7 @@ impl File { #[cfg(not(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1271,6 +1267,7 @@ impl File { #[cfg(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1282,6 +1279,7 @@ impl File { #[cfg(not(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1292,6 +1290,7 @@ impl File { #[cfg(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1309,6 +1308,7 @@ impl File { #[cfg(not(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1319,6 +1319,7 @@ impl File { #[cfg(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1336,6 +1337,7 @@ impl File { #[cfg(not(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1346,6 +1348,7 @@ impl File { #[cfg(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", @@ -1357,6 +1360,7 @@ impl File { #[cfg(not(any( target_os = "freebsd", + target_os = "fuchsia", target_os = "linux", target_os = "netbsd", target_vendor = "apple", diff --git a/library/std/src/sys/pal/unix/futex.rs b/library/std/src/sys/pal/unix/futex.rs index 0fc765dc87a5d..d4551dd6a38bb 100644 --- a/library/std/src/sys/pal/unix/futex.rs +++ b/library/std/src/sys/pal/unix/futex.rs @@ -219,7 +219,7 @@ pub fn futex_wake_all(futex: &AtomicU32) { } #[cfg(target_os = "emscripten")] -extern "C" { +unsafe extern "C" { fn emscripten_futex_wake(addr: *const AtomicU32, count: libc::c_int) -> libc::c_int; fn emscripten_futex_wait( addr: *const AtomicU32, @@ -267,7 +267,7 @@ pub mod zircon { pub const ZX_ERR_BAD_STATE: zx_status_t = -20; pub const ZX_ERR_TIMED_OUT: zx_status_t = -21; - extern "C" { + unsafe extern "C" { pub fn zx_clock_get_monotonic() -> zx_time_t; pub fn zx_futex_wait( value_ptr: *const zx_futex_t, diff --git a/library/std/src/sys/pal/unix/io.rs b/library/std/src/sys/pal/unix/io.rs deleted file mode 100644 index 0d5a152dc0dc6..0000000000000 --- a/library/std/src/sys/pal/unix/io.rs +++ /dev/null @@ -1,87 +0,0 @@ -use libc::{c_void, iovec}; - -use crate::marker::PhantomData; -use crate::os::fd::{AsFd, AsRawFd}; -use crate::slice; - -#[derive(Copy, Clone)] -#[repr(transparent)] -pub struct IoSlice<'a> { - vec: iovec, - _p: PhantomData<&'a [u8]>, -} - -impl<'a> IoSlice<'a> { - #[inline] - pub fn new(buf: &'a [u8]) -> IoSlice<'a> { - IoSlice { - vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if self.vec.iov_len < n { - panic!("advancing IoSlice beyond its length"); - } - - unsafe { - self.vec.iov_len -= n; - self.vec.iov_base = self.vec.iov_base.add(n); - } - } - - #[inline] - pub const fn as_slice(&self) -> &'a [u8] { - unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } -} - -#[repr(transparent)] -pub struct IoSliceMut<'a> { - vec: iovec, - _p: PhantomData<&'a mut [u8]>, -} - -impl<'a> IoSliceMut<'a> { - #[inline] - pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { - IoSliceMut { - vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if self.vec.iov_len < n { - panic!("advancing IoSliceMut beyond its length"); - } - - unsafe { - self.vec.iov_len -= n; - self.vec.iov_base = self.vec.iov_base.add(n); - } - } - - #[inline] - pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } - - #[inline] - pub const fn into_slice(self) -> &'a mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } - - #[inline] - pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } -} - -pub fn is_terminal(fd: &impl AsFd) -> bool { - let fd = fd.as_fd(); - unsafe { libc::isatty(fd.as_raw_fd()) != 0 } -} diff --git a/library/std/src/sys/pal/unix/kernel_copy.rs b/library/std/src/sys/pal/unix/kernel_copy.rs index 36823a503b17c..bbf29f3252341 100644 --- a/library/std/src/sys/pal/unix/kernel_copy.rs +++ b/library/std/src/sys/pal/unix/kernel_copy.rs @@ -52,15 +52,14 @@ use crate::cmp::min; use crate::fs::{File, Metadata}; use crate::io::copy::generic_copy; use crate::io::{ - BufRead, BufReader, BufWriter, Error, Read, Result, StderrLock, StdinLock, StdoutLock, Take, - Write, + BufRead, BufReader, BufWriter, Error, PipeReader, PipeWriter, Read, Result, StderrLock, + StdinLock, StdoutLock, Take, Write, }; use crate::mem::ManuallyDrop; use crate::net::TcpStream; use crate::os::unix::fs::FileTypeExt; use crate::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use crate::os::unix::net::UnixStream; -use crate::pipe::{PipeReader, PipeWriter}; use crate::process::{ChildStderr, ChildStdin, ChildStdout}; use crate::ptr; use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering}; diff --git a/library/std/src/sys/pal/unix/kernel_copy/tests.rs b/library/std/src/sys/pal/unix/kernel_copy/tests.rs index 1350d743ff6f3..54d8f8ed2edd4 100644 --- a/library/std/src/sys/pal/unix/kernel_copy/tests.rs +++ b/library/std/src/sys/pal/unix/kernel_copy/tests.rs @@ -2,7 +2,7 @@ use crate::fs::OpenOptions; use crate::io; use crate::io::{BufRead, Read, Result, Seek, SeekFrom, Write}; use crate::os::unix::io::AsRawFd; -use crate::sys_common::io::test::tmpdir; +use crate::test_helpers::tmpdir; #[test] fn copy_specialization() -> Result<()> { diff --git a/library/std/src/sys/pal/unix/l4re.rs b/library/std/src/sys/pal/unix/l4re.rs deleted file mode 100644 index 37dd370c5146c..0000000000000 --- a/library/std/src/sys/pal/unix/l4re.rs +++ /dev/null @@ -1,564 +0,0 @@ -macro_rules! unimpl { - () => { - return Err(io::const_error!( - io::ErrorKind::Unsupported, - "No networking available on L4Re.", - )); - }; -} - -pub mod net { - #![allow(warnings)] - use crate::fmt; - use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; - use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; - use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; - use crate::sys::fd::FileDesc; - use crate::sys_common::{AsInner, FromInner, IntoInner}; - use crate::time::Duration; - - #[allow(unused_extern_crates)] - pub extern crate libc as netc; - - pub struct Socket(FileDesc); - impl Socket { - pub fn new(_: &SocketAddr, _: libc::c_int) -> io::Result { - unimpl!(); - } - - pub fn new_raw(_: libc::c_int, _: libc::c_int) -> io::Result { - unimpl!(); - } - - pub fn new_pair(_: libc::c_int, _: libc::c_int) -> io::Result<(Socket, Socket)> { - unimpl!(); - } - - pub fn connect_timeout(&self, _: &SocketAddr, _: Duration) -> io::Result<()> { - unimpl!(); - } - - pub fn accept( - &self, - _: *mut libc::sockaddr, - _: *mut libc::socklen_t, - ) -> io::Result { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result { - unimpl!(); - } - - pub fn read(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn read_buf(&self, _: BorrowedCursor<'_>) -> io::Result<()> { - unimpl!(); - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - unimpl!(); - } - - pub fn is_read_vectored(&self) -> bool { - false - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn write(&self, _: &[u8]) -> io::Result { - unimpl!(); - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - unimpl!(); - } - - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn set_timeout(&self, _: Option, _: libc::c_int) -> io::Result<()> { - unimpl!(); - } - - pub fn timeout(&self, _: libc::c_int) -> io::Result> { - unimpl!(); - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - unimpl!(); - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn linger(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn nodelay(&self) -> io::Result { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - unimpl!(); - } - - // This is used by sys_common code to abstract over Windows and Unix. - pub fn as_raw(&self) -> RawFd { - self.as_raw_fd() - } - } - - impl AsInner for Socket { - #[inline] - fn as_inner(&self) -> &FileDesc { - &self.0 - } - } - - impl FromInner for Socket { - fn from_inner(file_desc: FileDesc) -> Socket { - Socket(file_desc) - } - } - - impl IntoInner for Socket { - fn into_inner(self) -> FileDesc { - self.0 - } - } - - impl AsFd for Socket { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } - } - - impl AsRawFd for Socket { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } - } - - impl IntoRawFd for Socket { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } - } - - impl FromRawFd for Socket { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - Self(FromRawFd::from_raw_fd(raw_fd)) - } - } - - pub struct TcpStream { - inner: Socket, - } - - impl TcpStream { - pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { - unimpl!(); - } - - pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { - unimpl!(); - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn read_timeout(&self) -> io::Result> { - unimpl!(); - } - - pub fn write_timeout(&self) -> io::Result> { - unimpl!(); - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn read(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn read_buf(&self, _: BorrowedCursor<'_>) -> io::Result<()> { - unimpl!(); - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - unimpl!(); - } - - pub fn is_read_vectored(&self) -> bool { - false - } - - pub fn write(&self, _: &[u8]) -> io::Result { - unimpl!(); - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - unimpl!(); - } - - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn peer_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn socket_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result { - unimpl!(); - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn linger(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn nodelay(&self) -> io::Result { - unimpl!(); - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn ttl(&self) -> io::Result { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - } - - impl FromInner for TcpStream { - fn from_inner(socket: Socket) -> TcpStream { - TcpStream { inner: socket } - } - } - - impl fmt::Debug for TcpStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "No networking support available on L4Re") - } - } - - pub struct TcpListener { - inner: Socket, - } - - impl TcpListener { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unimpl!(); - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn socket_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result { - unimpl!(); - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn ttl(&self) -> io::Result { - unimpl!(); - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn only_v6(&self) -> io::Result { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - } - - impl FromInner for TcpListener { - fn from_inner(socket: Socket) -> TcpListener { - TcpListener { inner: socket } - } - } - - impl fmt::Debug for TcpListener { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "No networking support available on L4Re.") - } - } - - pub struct UdpSocket { - inner: Socket, - } - - impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unimpl!(); - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn peer_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn socket_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result { - unimpl!(); - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn read_timeout(&self) -> io::Result> { - unimpl!(); - } - - pub fn write_timeout(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn broadcast(&self) -> io::Result { - unimpl!(); - } - - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_loop_v4(&self) -> io::Result { - unimpl!(); - } - - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_ttl_v4(&self) -> io::Result { - unimpl!(); - } - - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_loop_v6(&self) -> io::Result { - unimpl!(); - } - - pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - unimpl!(); - } - - pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - unimpl!(); - } - - pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn ttl(&self) -> io::Result { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn recv(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn send(&self, _: &[u8]) -> io::Result { - unimpl!(); - } - - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - unimpl!(); - } - } - - impl FromInner for UdpSocket { - fn from_inner(socket: Socket) -> UdpSocket { - UdpSocket { inner: socket } - } - } - - impl fmt::Debug for UdpSocket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "No networking support on L4Re available.") - } - } - - pub struct LookupHost { - original: *mut libc::addrinfo, - cur: *mut libc::addrinfo, - } - - impl Iterator for LookupHost { - type Item = SocketAddr; - fn next(&mut self) -> Option { - None - } - } - - impl LookupHost { - pub fn port(&self) -> u16 { - 0 // unimplemented - } - } - - unsafe impl Sync for LookupHost {} - unsafe impl Send for LookupHost {} - - impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &str) -> io::Result { - unimpl!(); - } - } - - impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - unimpl!(); - } - } -} diff --git a/library/std/src/sys/pal/unix/linux/pidfd/tests.rs b/library/std/src/sys/pal/unix/linux/pidfd/tests.rs index fb928c76fbd04..17b06bea91278 100644 --- a/library/std/src/sys/pal/unix/linux/pidfd/tests.rs +++ b/library/std/src/sys/pal/unix/linux/pidfd/tests.rs @@ -45,8 +45,8 @@ fn test_command_pidfd() { .expect_err("pidfd should not have been created"); // exercise the fork/exec path since the earlier attempts may have used pidfd_spawnp() - let mut child = - unsafe { Command::new("false").pre_exec(|| Ok(())) }.create_pidfd(true).spawn().unwrap(); + let mut cmd = Command::new("false"); + let mut child = unsafe { cmd.pre_exec(|| Ok(())) }.create_pidfd(true).spawn().unwrap(); assert!(child.id() > 0 && child.id() < -1i32 as u32); diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 3cc1cae8d000e..c0b56d8d2b28a 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -11,17 +11,10 @@ pub mod env; pub mod fd; pub mod fs; pub mod futex; -pub mod io; #[cfg(any(target_os = "linux", target_os = "android"))] pub mod kernel_copy; -#[cfg(target_os = "l4re")] -mod l4re; #[cfg(target_os = "linux")] pub mod linux; -#[cfg(not(target_os = "l4re"))] -pub mod net; -#[cfg(target_os = "l4re")] -pub use self::l4re::net; pub mod os; pub mod pipe; pub mod process; @@ -380,24 +373,24 @@ cfg_if::cfg_if! { cfg(target_feature = "crt-static"))] #[link(name = "dl", cfg(not(target_feature = "crt-static")))] #[link(name = "log", cfg(not(target_feature = "crt-static")))] - extern "C" {} + unsafe extern "C" {} } else if #[cfg(target_os = "freebsd")] { #[link(name = "execinfo")] #[link(name = "pthread")] - extern "C" {} + unsafe extern "C" {} } else if #[cfg(target_os = "netbsd")] { #[link(name = "pthread")] #[link(name = "rt")] - extern "C" {} + unsafe extern "C" {} } else if #[cfg(any(target_os = "dragonfly", target_os = "openbsd"))] { #[link(name = "pthread")] - extern "C" {} + unsafe extern "C" {} } else if #[cfg(target_os = "solaris")] { #[link(name = "socket")] #[link(name = "posix4")] #[link(name = "pthread")] #[link(name = "resolv")] - extern "C" {} + unsafe extern "C" {} } else if #[cfg(target_os = "illumos")] { #[link(name = "socket")] #[link(name = "posix4")] @@ -406,24 +399,24 @@ cfg_if::cfg_if! { #[link(name = "nsl")] // Use libumem for the (malloc-compatible) allocator #[link(name = "umem")] - extern "C" {} + unsafe extern "C" {} } else if #[cfg(target_vendor = "apple")] { // Link to `libSystem.dylib`. // // Don't get confused by the presence of `System.framework`, // it is a deprecated wrapper over the dynamic library. #[link(name = "System")] - extern "C" {} + unsafe extern "C" {} } else if #[cfg(target_os = "fuchsia")] { #[link(name = "zircon")] #[link(name = "fdio")] - extern "C" {} + unsafe extern "C" {} } else if #[cfg(all(target_os = "linux", target_env = "uclibc"))] { #[link(name = "dl")] - extern "C" {} + unsafe extern "C" {} } else if #[cfg(target_os = "vita")] { #[link(name = "pthread", kind = "static", modifiers = "-bundle")] - extern "C" {} + unsafe extern "C" {} } } diff --git a/library/std/src/sys/pal/unix/net.rs b/library/std/src/sys/pal/unix/net.rs deleted file mode 100644 index d73b9fd5eb882..0000000000000 --- a/library/std/src/sys/pal/unix/net.rs +++ /dev/null @@ -1,651 +0,0 @@ -use libc::{MSG_PEEK, c_int, c_void, size_t, sockaddr, socklen_t}; - -use crate::ffi::CStr; -use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{Shutdown, SocketAddr}; -use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; -use crate::sys::fd::FileDesc; -use crate::sys::pal::unix::IsMinusOne; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::time::{Duration, Instant}; -use crate::{cmp, mem}; - -cfg_if::cfg_if! { - if #[cfg(target_vendor = "apple")] { - use libc::SO_LINGER_SEC as SO_LINGER; - } else { - use libc::SO_LINGER; - } -} - -pub use crate::sys::{cvt, cvt_r}; - -#[allow(unused_extern_crates)] -pub extern crate libc as netc; - -pub type wrlen_t = size_t; - -pub struct Socket(FileDesc); - -pub fn init() {} - -pub fn cvt_gai(err: c_int) -> io::Result<()> { - if err == 0 { - return Ok(()); - } - - // We may need to trigger a glibc workaround. See on_resolver_failure() for details. - on_resolver_failure(); - - #[cfg(not(any(target_os = "espidf", target_os = "nuttx")))] - if err == libc::EAI_SYSTEM { - return Err(io::Error::last_os_error()); - } - - #[cfg(not(any(target_os = "espidf", target_os = "nuttx")))] - let detail = unsafe { - // We can't always expect a UTF-8 environment. When we don't get that luxury, - // it's better to give a low-quality error message than none at all. - CStr::from_ptr(libc::gai_strerror(err)).to_string_lossy() - }; - - #[cfg(any(target_os = "espidf", target_os = "nuttx"))] - let detail = ""; - - Err(io::Error::new( - io::ErrorKind::Uncategorized, - &format!("failed to lookup address information: {detail}")[..], - )) -} - -impl Socket { - pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { - let fam = match *addr { - SocketAddr::V4(..) => libc::AF_INET, - SocketAddr::V6(..) => libc::AF_INET6, - }; - Socket::new_raw(fam, ty) - } - - pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { - unsafe { - cfg_if::cfg_if! { - if #[cfg(any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "illumos", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "nto", - target_os = "solaris", - ))] { - // On platforms that support it we pass the SOCK_CLOEXEC - // flag to atomically create the socket and set it as - // CLOEXEC. On Linux this was added in 2.6.27. - let fd = cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0))?; - let socket = Socket(FileDesc::from_raw_fd(fd)); - - // DragonFlyBSD, FreeBSD and NetBSD use `SO_NOSIGPIPE` as a `setsockopt` - // flag to disable `SIGPIPE` emission on socket. - #[cfg(any(target_os = "freebsd", target_os = "netbsd", target_os = "dragonfly"))] - setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?; - - Ok(socket) - } else { - let fd = cvt(libc::socket(fam, ty, 0))?; - let fd = FileDesc::from_raw_fd(fd); - fd.set_cloexec()?; - let socket = Socket(fd); - - // macOS and iOS use `SO_NOSIGPIPE` as a `setsockopt` - // flag to disable `SIGPIPE` emission on socket. - #[cfg(target_vendor = "apple")] - setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?; - - Ok(socket) - } - } - } - } - - #[cfg(not(target_os = "vxworks"))] - pub fn new_pair(fam: c_int, ty: c_int) -> io::Result<(Socket, Socket)> { - unsafe { - let mut fds = [0, 0]; - - cfg_if::cfg_if! { - if #[cfg(any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "illumos", - target_os = "linux", - target_os = "hurd", - target_os = "netbsd", - target_os = "openbsd", - target_os = "nto", - ))] { - // Like above, set cloexec atomically - cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()))?; - Ok((Socket(FileDesc::from_raw_fd(fds[0])), Socket(FileDesc::from_raw_fd(fds[1])))) - } else { - cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?; - let a = FileDesc::from_raw_fd(fds[0]); - let b = FileDesc::from_raw_fd(fds[1]); - a.set_cloexec()?; - b.set_cloexec()?; - Ok((Socket(a), Socket(b))) - } - } - } - } - - #[cfg(target_os = "vxworks")] - pub fn new_pair(_fam: c_int, _ty: c_int) -> io::Result<(Socket, Socket)> { - unimplemented!() - } - - pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { - let (addr, len) = addr.into_inner(); - loop { - let result = unsafe { libc::connect(self.as_raw_fd(), addr.as_ptr(), len) }; - if result.is_minus_one() { - let err = crate::sys::os::errno(); - match err { - libc::EINTR => continue, - libc::EISCONN => return Ok(()), - _ => return Err(io::Error::from_raw_os_error(err)), - } - } - return Ok(()); - } - } - - pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { - self.set_nonblocking(true)?; - let r = unsafe { - let (addr, len) = addr.into_inner(); - cvt(libc::connect(self.as_raw_fd(), addr.as_ptr(), len)) - }; - self.set_nonblocking(false)?; - - match r { - Ok(_) => return Ok(()), - // there's no ErrorKind for EINPROGRESS :( - Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {} - Err(e) => return Err(e), - } - - let mut pollfd = libc::pollfd { fd: self.as_raw_fd(), events: libc::POLLOUT, revents: 0 }; - - if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { - return Err(io::Error::ZERO_TIMEOUT); - } - - let start = Instant::now(); - - loop { - let elapsed = start.elapsed(); - if elapsed >= timeout { - return Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")); - } - - let timeout = timeout - elapsed; - let mut timeout = timeout - .as_secs() - .saturating_mul(1_000) - .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); - if timeout == 0 { - timeout = 1; - } - - let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; - - match unsafe { libc::poll(&mut pollfd, 1, timeout) } { - -1 => { - let err = io::Error::last_os_error(); - if !err.is_interrupted() { - return Err(err); - } - } - 0 => {} - _ => { - if cfg!(target_os = "vxworks") { - // VxWorks poll does not return POLLHUP or POLLERR in revents. Check if the - // connection actually succeeded and return ok only when the socket is - // ready and no errors were found. - if let Some(e) = self.take_error()? { - return Err(e); - } - } else { - // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look - // for POLLHUP or POLLERR rather than read readiness - if pollfd.revents & (libc::POLLHUP | libc::POLLERR) != 0 { - let e = self.take_error()?.unwrap_or_else(|| { - io::const_error!( - io::ErrorKind::Uncategorized, - "no error set after POLLHUP", - ) - }); - return Err(e); - } - } - - return Ok(()); - } - } - } - } - - pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result { - // Unfortunately the only known way right now to accept a socket and - // atomically set the CLOEXEC flag is to use the `accept4` syscall on - // platforms that support it. On Linux, this was added in 2.6.28, - // glibc 2.10 and musl 0.9.5. - cfg_if::cfg_if! { - if #[cfg(any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "illumos", - target_os = "linux", - target_os = "hurd", - target_os = "netbsd", - target_os = "openbsd", - ))] { - unsafe { - let fd = cvt_r(|| libc::accept4(self.as_raw_fd(), storage, len, libc::SOCK_CLOEXEC))?; - Ok(Socket(FileDesc::from_raw_fd(fd))) - } - } else { - unsafe { - let fd = cvt_r(|| libc::accept(self.as_raw_fd(), storage, len))?; - let fd = FileDesc::from_raw_fd(fd); - fd.set_cloexec()?; - Ok(Socket(fd)) - } - } - } - } - - pub fn duplicate(&self) -> io::Result { - self.0.duplicate().map(Socket) - } - - fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { - let ret = cvt(unsafe { - libc::recv( - self.as_raw_fd(), - buf.as_mut().as_mut_ptr() as *mut c_void, - buf.capacity(), - flags, - ) - })?; - unsafe { - buf.advance_unchecked(ret as usize); - } - Ok(()) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), 0)?; - Ok(buf.len()) - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), MSG_PEEK)?; - Ok(buf.len()) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.recv_with_flags(buf, 0) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - - fn recv_from_with_flags( - &self, - buf: &mut [u8], - flags: c_int, - ) -> io::Result<(usize, SocketAddr)> { - let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; - let mut addrlen = mem::size_of_val(&storage) as libc::socklen_t; - - let n = cvt(unsafe { - libc::recvfrom( - self.as_raw_fd(), - buf.as_mut_ptr() as *mut c_void, - buf.len(), - flags, - (&raw mut storage) as *mut _, - &mut addrlen, - ) - })?; - Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?)) - } - - pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, 0) - } - - #[cfg(any(target_os = "android", target_os = "linux"))] - pub fn recv_msg(&self, msg: &mut libc::msghdr) -> io::Result { - let n = cvt(unsafe { libc::recvmsg(self.as_raw_fd(), msg, libc::MSG_CMSG_CLOEXEC) })?; - Ok(n as usize) - } - - pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, MSG_PEEK) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } - - #[cfg(any(target_os = "android", target_os = "linux"))] - pub fn send_msg(&self, msg: &mut libc::msghdr) -> io::Result { - let n = cvt(unsafe { libc::sendmsg(self.as_raw_fd(), msg, 0) })?; - Ok(n as usize) - } - - pub fn set_timeout(&self, dur: Option, kind: libc::c_int) -> io::Result<()> { - let timeout = match dur { - Some(dur) => { - if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { - return Err(io::Error::ZERO_TIMEOUT); - } - - let secs = if dur.as_secs() > libc::time_t::MAX as u64 { - libc::time_t::MAX - } else { - dur.as_secs() as libc::time_t - }; - let mut timeout = libc::timeval { - tv_sec: secs, - tv_usec: dur.subsec_micros() as libc::suseconds_t, - }; - if timeout.tv_sec == 0 && timeout.tv_usec == 0 { - timeout.tv_usec = 1; - } - timeout - } - None => libc::timeval { tv_sec: 0, tv_usec: 0 }, - }; - setsockopt(self, libc::SOL_SOCKET, kind, timeout) - } - - pub fn timeout(&self, kind: libc::c_int) -> io::Result> { - let raw: libc::timeval = getsockopt(self, libc::SOL_SOCKET, kind)?; - if raw.tv_sec == 0 && raw.tv_usec == 0 { - Ok(None) - } else { - let sec = raw.tv_sec as u64; - let nsec = (raw.tv_usec as u32) * 1000; - Ok(Some(Duration::new(sec, nsec))) - } - } - - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - let how = match how { - Shutdown::Write => libc::SHUT_WR, - Shutdown::Read => libc::SHUT_RD, - Shutdown::Both => libc::SHUT_RDWR, - }; - cvt(unsafe { libc::shutdown(self.as_raw_fd(), how) })?; - Ok(()) - } - - pub fn set_linger(&self, linger: Option) -> io::Result<()> { - let linger = libc::linger { - l_onoff: linger.is_some() as libc::c_int, - l_linger: linger.unwrap_or_default().as_secs() as libc::c_int, - }; - - setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger) - } - - pub fn linger(&self) -> io::Result> { - let val: libc::linger = getsockopt(self, libc::SOL_SOCKET, SO_LINGER)?; - - Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) - } - - pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) - } - - pub fn nodelay(&self) -> io::Result { - let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY)?; - Ok(raw != 0) - } - - #[cfg(any(target_os = "android", target_os = "linux",))] - pub fn set_quickack(&self, quickack: bool) -> io::Result<()> { - setsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK, quickack as c_int) - } - - #[cfg(any(target_os = "android", target_os = "linux",))] - pub fn quickack(&self) -> io::Result { - let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK)?; - Ok(raw != 0) - } - - // bionic libc makes no use of this flag - #[cfg(target_os = "linux")] - pub fn set_deferaccept(&self, accept: u32) -> io::Result<()> { - setsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT, accept as c_int) - } - - #[cfg(target_os = "linux")] - pub fn deferaccept(&self) -> io::Result { - let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT)?; - Ok(raw as u32) - } - - #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] - pub fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { - if !name.to_bytes().is_empty() { - const AF_NAME_MAX: usize = 16; - let mut buf = [0; AF_NAME_MAX]; - for (src, dst) in name.to_bytes().iter().zip(&mut buf[..AF_NAME_MAX - 1]) { - *dst = *src as libc::c_char; - } - let mut arg: libc::accept_filter_arg = unsafe { mem::zeroed() }; - arg.af_name = buf; - setsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER, &mut arg) - } else { - setsockopt( - self, - libc::SOL_SOCKET, - libc::SO_ACCEPTFILTER, - core::ptr::null_mut() as *mut c_void, - ) - } - } - - #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] - pub fn acceptfilter(&self) -> io::Result<&CStr> { - let arg: libc::accept_filter_arg = - getsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER)?; - let s: &[u8] = - unsafe { core::slice::from_raw_parts(arg.af_name.as_ptr() as *const u8, 16) }; - let name = CStr::from_bytes_with_nul(s).unwrap(); - Ok(name) - } - - #[cfg(any(target_os = "android", target_os = "linux",))] - pub fn set_passcred(&self, passcred: bool) -> io::Result<()> { - setsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED, passcred as libc::c_int) - } - - #[cfg(any(target_os = "android", target_os = "linux",))] - pub fn passcred(&self) -> io::Result { - let passcred: libc::c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED)?; - Ok(passcred != 0) - } - - #[cfg(target_os = "netbsd")] - pub fn set_local_creds(&self, local_creds: bool) -> io::Result<()> { - setsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS, local_creds as libc::c_int) - } - - #[cfg(target_os = "netbsd")] - pub fn local_creds(&self) -> io::Result { - let local_creds: libc::c_int = getsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS)?; - Ok(local_creds != 0) - } - - #[cfg(target_os = "freebsd")] - pub fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()> { - setsockopt( - self, - libc::AF_LOCAL, - libc::LOCAL_CREDS_PERSISTENT, - local_creds_persistent as libc::c_int, - ) - } - - #[cfg(target_os = "freebsd")] - pub fn local_creds_persistent(&self) -> io::Result { - let local_creds_persistent: libc::c_int = - getsockopt(self, libc::AF_LOCAL, libc::LOCAL_CREDS_PERSISTENT)?; - Ok(local_creds_persistent != 0) - } - - #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "vita")))] - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - let mut nonblocking = nonblocking as libc::c_int; - cvt(unsafe { libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &mut nonblocking) }).map(drop) - } - - #[cfg(target_os = "vita")] - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - let option = nonblocking as libc::c_int; - setsockopt(self, libc::SOL_SOCKET, libc::SO_NONBLOCK, option) - } - - #[cfg(any(target_os = "solaris", target_os = "illumos"))] - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - // FIONBIO is inadequate for sockets on illumos/Solaris, so use the - // fcntl(F_[GS]ETFL)-based method provided by FileDesc instead. - self.0.set_nonblocking(nonblocking) - } - - #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))] - pub fn set_mark(&self, mark: u32) -> io::Result<()> { - #[cfg(target_os = "linux")] - let option = libc::SO_MARK; - #[cfg(target_os = "freebsd")] - let option = libc::SO_USER_COOKIE; - #[cfg(target_os = "openbsd")] - let option = libc::SO_RTABLE; - setsockopt(self, libc::SOL_SOCKET, option, mark as libc::c_int) - } - - pub fn take_error(&self) -> io::Result> { - let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; - if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } - } - - // This is used by sys_common code to abstract over Windows and Unix. - pub fn as_raw(&self) -> RawFd { - self.as_raw_fd() - } -} - -impl AsInner for Socket { - #[inline] - fn as_inner(&self) -> &FileDesc { - &self.0 - } -} - -impl IntoInner for Socket { - fn into_inner(self) -> FileDesc { - self.0 - } -} - -impl FromInner for Socket { - fn from_inner(file_desc: FileDesc) -> Self { - Self(file_desc) - } -} - -impl AsFd for Socket { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl AsRawFd for Socket { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -impl IntoRawFd for Socket { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } -} - -impl FromRawFd for Socket { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - Self(FromRawFd::from_raw_fd(raw_fd)) - } -} - -// In versions of glibc prior to 2.26, there's a bug where the DNS resolver -// will cache the contents of /etc/resolv.conf, so changes to that file on disk -// can be ignored by a long-running program. That can break DNS lookups on e.g. -// laptops where the network comes and goes. See -// https://sourceware.org/bugzilla/show_bug.cgi?id=984. Note however that some -// distros including Debian have patched glibc to fix this for a long time. -// -// A workaround for this bug is to call the res_init libc function, to clear -// the cached configs. Unfortunately, while we believe glibc's implementation -// of res_init is thread-safe, we know that other implementations are not -// (https://github.com/rust-lang/rust/issues/43592). Code here in std could -// try to synchronize its res_init calls with a Mutex, but that wouldn't -// protect programs that call into libc in other ways. So instead of calling -// res_init unconditionally, we call it only when we detect we're linking -// against glibc version < 2.26. (That is, when we both know its needed and -// believe it's thread-safe). -#[cfg(all(target_os = "linux", target_env = "gnu"))] -fn on_resolver_failure() { - use crate::sys; - - // If the version fails to parse, we treat it the same as "not glibc". - if let Some(version) = sys::os::glibc_version() { - if version < (2, 26) { - unsafe { libc::res_init() }; - } - } -} - -#[cfg(not(all(target_os = "linux", target_env = "gnu")))] -fn on_resolver_failure() {} diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index b83772e34c173..04199c563303f 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -30,7 +30,7 @@ cfg_if::cfg_if! { } } -extern "C" { +unsafe extern "C" { #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))] #[cfg_attr( any( @@ -82,7 +82,7 @@ pub fn errno() -> i32 { #[cfg(target_os = "rtems")] pub fn errno() -> i32 { - extern "C" { + unsafe extern "C" { #[thread_local] static _tls_errno: c_int; } @@ -92,7 +92,7 @@ pub fn errno() -> i32 { #[cfg(target_os = "dragonfly")] pub fn errno() -> i32 { - extern "C" { + unsafe extern "C" { #[thread_local] static errno: c_int; } @@ -103,7 +103,7 @@ pub fn errno() -> i32 { #[cfg(target_os = "dragonfly")] #[allow(dead_code)] pub fn set_errno(e: i32) { - extern "C" { + unsafe extern "C" { #[thread_local] static mut errno: c_int; } @@ -115,7 +115,7 @@ pub fn set_errno(e: i32) { /// Gets a detailed string description for the given error number. pub fn error_string(errno: i32) -> String { - extern "C" { + unsafe extern "C" { #[cfg_attr( all( any(target_os = "linux", target_os = "hurd", target_env = "newlib"), @@ -610,7 +610,7 @@ pub unsafe fn environ() -> *mut *const *const c_char { // Use the `environ` static which is part of POSIX. #[cfg(not(target_vendor = "apple"))] pub unsafe fn environ() -> *mut *const *const c_char { - extern "C" { + unsafe extern "C" { static mut environ: *const *const c_char; } &raw mut environ @@ -847,7 +847,7 @@ pub fn getppid() -> u32 { #[cfg(all(target_os = "linux", target_env = "gnu"))] pub fn glibc_version() -> Option<(usize, usize)> { - extern "C" { + unsafe extern "C" { fn gnu_get_libc_version() -> *const libc::c_char; } let version_cstr = unsafe { CStr::from_ptr(gnu_get_libc_version()) }; diff --git a/library/std/src/sys/pal/unix/process/process_unix.rs b/library/std/src/sys/pal/unix/process/process_unix.rs index ec4965c1d7196..2bff192a5bd83 100644 --- a/library/std/src/sys/pal/unix/process/process_unix.rs +++ b/library/std/src/sys/pal/unix/process/process_unix.rs @@ -19,8 +19,7 @@ use crate::sys::process::process_common::*; use crate::{fmt, mem, sys}; cfg_if::cfg_if! { - // This workaround is only needed for QNX 7.0 and 7.1. The bug should have been fixed in 8.0 - if #[cfg(any(target_env = "nto70", target_env = "nto71"))] { + if #[cfg(target_os = "nto")] { use crate::thread; use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t}; use crate::time::Duration; @@ -187,12 +186,7 @@ impl Command { // Attempts to fork the process. If successful, returns Ok((0, -1)) // in the child, and Ok((child_pid, -1)) in the parent. - #[cfg(not(any( - target_os = "watchos", - target_os = "tvos", - target_env = "nto70", - target_env = "nto71" - )))] + #[cfg(not(any(target_os = "watchos", target_os = "tvos", target_os = "nto")))] unsafe fn do_fork(&mut self) -> Result { cvt(libc::fork()) } @@ -201,8 +195,7 @@ impl Command { // or closed a file descriptor while the fork() was occurring". // Documentation says "... or try calling fork() again". This is what we do here. // See also https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html - // This workaround is only needed for QNX 7.0 and 7.1. The bug should have been fixed in 8.0 - #[cfg(any(target_env = "nto70", target_env = "nto71"))] + #[cfg(target_os = "nto")] unsafe fn do_fork(&mut self) -> Result { use crate::sys::os::errno; diff --git a/library/std/src/sys/pal/unix/process/zircon.rs b/library/std/src/sys/pal/unix/process/zircon.rs index 4035e2370a3c6..7932bd26d76c3 100644 --- a/library/std/src/sys/pal/unix/process/zircon.rs +++ b/library/std/src/sys/pal/unix/process/zircon.rs @@ -75,7 +75,7 @@ pub struct zx_info_process_t { pub reserved1: u32, } -extern "C" { +unsafe extern "C" { pub fn zx_job_default() -> zx_handle_t; pub fn zx_task_kill(handle: zx_handle_t) -> zx_status_t; @@ -115,7 +115,7 @@ pub struct fdio_spawn_action_t { pub reserved1: u64, } -extern "C" { +unsafe extern "C" { pub fn fdio_spawn_etc( job: zx_handle_t, flags: u32, diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 69b31da427fcb..db5c6bd3a1c32 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -100,10 +100,11 @@ mod imp { // If the faulting address is within the guard page, then we print a // message saying so and abort. if start <= addr && addr < end { - rtprintpanic!( - "\nthread '{}' has overflowed its stack\n", - thread::current().name().unwrap_or("") - ); + thread::with_current_name(|name| { + let name = name.unwrap_or(""); + rtprintpanic!("\nthread '{name}' has overflowed its stack\n"); + }); + rtabort!("stack overflow"); } else { // Unregister ourselves by reverting back to the default behavior. diff --git a/library/std/src/sys/pal/unix/stdio.rs b/library/std/src/sys/pal/unix/stdio.rs index 97e75f1b5b669..8c2f61a40de3b 100644 --- a/library/std/src/sys/pal/unix/stdio.rs +++ b/library/std/src/sys/pal/unix/stdio.rs @@ -92,7 +92,7 @@ pub fn is_ebadf(err: &io::Error) -> bool { err.raw_os_error() == Some(libc::EBADF as i32) } -pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; pub fn panic_output() -> Option { Some(Stderr::new()) diff --git a/library/std/src/sys/pal/unix/sync/mutex.rs b/library/std/src/sys/pal/unix/sync/mutex.rs index 8ff6c3d3d15da..557e70af94ba7 100644 --- a/library/std/src/sys/pal/unix/sync/mutex.rs +++ b/library/std/src/sys/pal/unix/sync/mutex.rs @@ -111,9 +111,9 @@ impl Drop for Mutex { // `PTHREAD_MUTEX_INITIALIZER`, which is valid at all locations. Thus, // this call always destroys a valid mutex. let r = unsafe { libc::pthread_mutex_destroy(self.raw()) }; - if cfg!(target_os = "dragonfly") { - // On DragonFly pthread_mutex_destroy() returns EINVAL if called on a - // mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER. + if cfg!(any(target_os = "aix", target_os = "dragonfly")) { + // On AIX and DragonFly pthread_mutex_destroy() returns EINVAL if called + // on a mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER. // Once it is used (locked/unlocked) or pthread_mutex_init() is called, // this behaviour no longer occurs. debug_assert!(r == 0 || r == libc::EINVAL); diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index e360ba0f6d7d8..69c99782f0bf6 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -23,7 +23,7 @@ mod zircon { type zx_status_t = i32; pub const ZX_PROP_NAME: u32 = 3; - extern "C" { + unsafe extern "C" { pub fn zx_object_set_property( handle: zx_handle_t, property: u32, @@ -130,25 +130,32 @@ impl Thread { } } - #[cfg(target_os = "linux")] + #[cfg(any( + target_os = "linux", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "nuttx" + ))] pub fn set_name(name: &CStr) { - const TASK_COMM_LEN: usize = 16; - unsafe { - // Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20. - let name = truncate_cstr::<{ TASK_COMM_LEN }>(name); + cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + // Linux limits the allowed length of the name. + const TASK_COMM_LEN: usize = 16; + let name = truncate_cstr::<{ TASK_COMM_LEN }>(name); + } else { + // FreeBSD, DragonFly BSD and NuttX do not enforce length limits. + } + }; + // Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20 for Linux, + // FreeBSD 12.2 and 13.0, and DragonFly BSD 6.0. let res = libc::pthread_setname_np(libc::pthread_self(), name.as_ptr()); // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. debug_assert_eq!(res, 0); } } - #[cfg(any( - target_os = "freebsd", - target_os = "dragonfly", - target_os = "openbsd", - target_os = "nuttx" - ))] + #[cfg(target_os = "openbsd")] pub fn set_name(name: &CStr) { unsafe { libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr()); @@ -223,7 +230,7 @@ impl Thread { #[cfg(target_os = "vxworks")] pub fn set_name(name: &CStr) { // FIXME(libc): adding real STATUS, ERROR type eventually. - extern "C" { + unsafe extern "C" { fn taskNameSet(task_id: libc::TASK_ID, task_name: *mut libc::c_char) -> libc::c_int; } @@ -499,7 +506,7 @@ pub fn available_parallelism() -> io::Result> { } else if #[cfg(target_os = "vxworks")] { // Note: there is also `vxCpuConfiguredGet`, closer to _SC_NPROCESSORS_CONF // expectations than the actual cores availability. - extern "C" { + unsafe extern "C" { fn vxCpuEnabledGet() -> libc::cpuset_t; } diff --git a/library/std/src/sys/pal/unix/thread_parking.rs b/library/std/src/sys/pal/unix/thread_parking.rs index 72dd2031479ee..bef8b4fb36391 100644 --- a/library/std/src/sys/pal/unix/thread_parking.rs +++ b/library/std/src/sys/pal/unix/thread_parking.rs @@ -8,7 +8,7 @@ use crate::ffi::{c_int, c_void}; use crate::ptr; use crate::time::Duration; -extern "C" { +unsafe extern "C" { fn ___lwp_park60( clock_id: clockid_t, flags: c_int, diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs index 343864d0b3fd2..e224980e95f31 100644 --- a/library/std/src/sys/pal/unix/time.rs +++ b/library/std/src/sys/pal/unix/time.rs @@ -1,3 +1,5 @@ +use core::num::niche_types::Nanoseconds; + use crate::time::Duration; use crate::{fmt, io}; @@ -15,12 +17,6 @@ pub(in crate::sys) const TIMESPEC_MAX_CAPPED: libc::timespec = libc::timespec { tv_nsec: (u64::MAX % NSEC_PER_SEC) as i64, }; -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -#[rustc_layout_scalar_valid_range_start(0)] -#[rustc_layout_scalar_valid_range_end(999_999_999)] -struct Nanoseconds(u32); - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SystemTime { pub(crate) t: Timespec, @@ -59,14 +55,14 @@ impl fmt::Debug for SystemTime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SystemTime") .field("tv_sec", &self.t.tv_sec) - .field("tv_nsec", &self.t.tv_nsec.0) + .field("tv_nsec", &self.t.tv_nsec) .finish() } } impl Timespec { const unsafe fn new_unchecked(tv_sec: i64, tv_nsec: i64) -> Timespec { - Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds(tv_nsec as u32) } } + Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds::new_unchecked(tv_nsec as u32) } } } pub const fn zero() -> Timespec { @@ -147,12 +143,15 @@ impl Timespec { // // Ideally this code could be rearranged such that it more // directly expresses the lower-cost behavior we want from it. - let (secs, nsec) = if self.tv_nsec.0 >= other.tv_nsec.0 { - ((self.tv_sec - other.tv_sec) as u64, self.tv_nsec.0 - other.tv_nsec.0) + let (secs, nsec) = if self.tv_nsec.as_inner() >= other.tv_nsec.as_inner() { + ( + (self.tv_sec - other.tv_sec) as u64, + self.tv_nsec.as_inner() - other.tv_nsec.as_inner(), + ) } else { ( (self.tv_sec - other.tv_sec - 1) as u64, - self.tv_nsec.0 + (NSEC_PER_SEC as u32) - other.tv_nsec.0, + self.tv_nsec.as_inner() + (NSEC_PER_SEC as u32) - other.tv_nsec.as_inner(), ) }; @@ -170,7 +169,7 @@ impl Timespec { // Nano calculations can't overflow because nanos are <1B which fit // in a u32. - let mut nsec = other.subsec_nanos() + self.tv_nsec.0; + let mut nsec = other.subsec_nanos() + self.tv_nsec.as_inner(); if nsec >= NSEC_PER_SEC as u32 { nsec -= NSEC_PER_SEC as u32; secs = secs.checked_add(1)?; @@ -182,7 +181,7 @@ impl Timespec { let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?; // Similar to above, nanos can't overflow. - let mut nsec = self.tv_nsec.0 as i32 - other.subsec_nanos() as i32; + let mut nsec = self.tv_nsec.as_inner() as i32 - other.subsec_nanos() as i32; if nsec < 0 { nsec += NSEC_PER_SEC as i32; secs = secs.checked_sub(1)?; @@ -194,7 +193,7 @@ impl Timespec { pub fn to_timespec(&self) -> Option { Some(libc::timespec { tv_sec: self.tv_sec.try_into().ok()?, - tv_nsec: self.tv_nsec.0.try_into().ok()?, + tv_nsec: self.tv_nsec.as_inner().try_into().ok()?, }) } @@ -203,7 +202,7 @@ impl Timespec { #[cfg(target_os = "nto")] pub(in crate::sys) fn to_timespec_capped(&self) -> Option { // Check if timeout in nanoseconds would fit into an u64 - if (self.tv_nsec.0 as u64) + if (self.tv_nsec.as_inner() as u64) .checked_add((self.tv_sec as u64).checked_mul(NSEC_PER_SEC)?) .is_none() { @@ -219,7 +218,7 @@ impl Timespec { not(target_arch = "riscv32") ))] pub fn to_timespec64(&self) -> __timespec64 { - __timespec64::new(self.tv_sec, self.tv_nsec.0 as _) + __timespec64::new(self.tv_sec, self.tv_nsec.as_inner() as _) } } @@ -293,7 +292,7 @@ impl fmt::Debug for Instant { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Instant") .field("tv_sec", &self.t.tv_sec) - .field("tv_nsec", &self.t.tv_nsec.0) + .field("tv_nsec", &self.t.tv_nsec) .finish() } } diff --git a/library/std/src/sys/pal/unix/weak.rs b/library/std/src/sys/pal/unix/weak.rs index 35762f5a53b5b..5a37598f43827 100644 --- a/library/std/src/sys/pal/unix/weak.rs +++ b/library/std/src/sys/pal/unix/weak.rs @@ -31,7 +31,7 @@ use crate::{mem, ptr}; pub(crate) macro weak { (fn $name:ident($($t:ty),*) -> $ret:ty) => ( let ref $name: ExternWeak $ret> = { - extern "C" { + unsafe extern "C" { #[linkage = "extern_weak"] static $name: Option $ret>; } diff --git a/library/std/src/sys/pal/unsupported/io.rs b/library/std/src/sys/pal/unsupported/io.rs deleted file mode 100644 index 604735d32d51a..0000000000000 --- a/library/std/src/sys/pal/unsupported/io.rs +++ /dev/null @@ -1,56 +0,0 @@ -use crate::mem; - -#[derive(Copy, Clone)] -pub struct IoSlice<'a>(&'a [u8]); - -impl<'a> IoSlice<'a> { - #[inline] - pub fn new(buf: &'a [u8]) -> IoSlice<'a> { - IoSlice(buf) - } - - #[inline] - pub fn advance(&mut self, n: usize) { - self.0 = &self.0[n..] - } - - #[inline] - pub const fn as_slice(&self) -> &'a [u8] { - self.0 - } -} - -pub struct IoSliceMut<'a>(&'a mut [u8]); - -impl<'a> IoSliceMut<'a> { - #[inline] - pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { - IoSliceMut(buf) - } - - #[inline] - pub fn advance(&mut self, n: usize) { - let slice = mem::take(&mut self.0); - let (_, remaining) = slice.split_at_mut(n); - self.0 = remaining; - } - - #[inline] - pub fn as_slice(&self) -> &[u8] { - self.0 - } - - #[inline] - pub const fn into_slice(self) -> &'a mut [u8] { - self.0 - } - - #[inline] - pub fn as_mut_slice(&mut self) -> &mut [u8] { - self.0 - } -} - -pub fn is_terminal(_: &T) -> bool { - false -} diff --git a/library/std/src/sys/pal/unsupported/mod.rs b/library/std/src/sys/pal/unsupported/mod.rs index 01d516f7568bf..b1aaeb1b4c814 100644 --- a/library/std/src/sys/pal/unsupported/mod.rs +++ b/library/std/src/sys/pal/unsupported/mod.rs @@ -3,8 +3,6 @@ pub mod args; pub mod env; pub mod fs; -pub mod io; -pub mod net; pub mod os; pub mod pipe; pub mod process; diff --git a/library/std/src/sys/pal/unsupported/net.rs b/library/std/src/sys/pal/unsupported/net.rs deleted file mode 100644 index 87e6106468fdb..0000000000000 --- a/library/std/src/sys/pal/unsupported/net.rs +++ /dev/null @@ -1,369 +0,0 @@ -use crate::fmt; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; -use crate::sys::unsupported; -use crate::time::Duration; - -pub struct TcpStream(!); - -impl TcpStream { - pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { - unsupported() - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn read_timeout(&self) -> io::Result> { - self.0 - } - - pub fn write_timeout(&self) -> io::Result> { - self.0 - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn read(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> { - self.0 - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - self.0 - } - - pub fn is_read_vectored(&self) -> bool { - self.0 - } - - pub fn write(&self, _: &[u8]) -> io::Result { - self.0 - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - self.0 - } - - pub fn is_write_vectored(&self) -> bool { - self.0 - } - - pub fn peer_addr(&self) -> io::Result { - self.0 - } - - pub fn socket_addr(&self) -> io::Result { - self.0 - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn linger(&self) -> io::Result> { - self.0 - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn nodelay(&self) -> io::Result { - self.0 - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn ttl(&self) -> io::Result { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - self.0 - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 - } -} - -impl fmt::Debug for TcpStream { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -pub struct TcpListener(!); - -impl TcpListener { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn socket_addr(&self) -> io::Result { - self.0 - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn ttl(&self) -> io::Result { - self.0 - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn only_v6(&self) -> io::Result { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - self.0 - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 - } -} - -impl fmt::Debug for TcpListener { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -pub struct UdpSocket(!); - -impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn peer_addr(&self) -> io::Result { - self.0 - } - - pub fn socket_addr(&self) -> io::Result { - self.0 - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.0 - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.0 - } - - pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn read_timeout(&self) -> io::Result> { - self.0 - } - - pub fn write_timeout(&self) -> io::Result> { - self.0 - } - - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn broadcast(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn multicast_loop_v4(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn multicast_ttl_v4(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn multicast_loop_v6(&self) -> io::Result { - self.0 - } - - pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - self.0 - } - - pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - self.0 - } - - pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - self.0 - } - - pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - self.0 - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn ttl(&self) -> io::Result { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - self.0 - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn recv(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn send(&self, _: &[u8]) -> io::Result { - self.0 - } - - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - self.0 - } -} - -impl fmt::Debug for UdpSocket { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -pub struct LookupHost(!); - -impl LookupHost { - pub fn port(&self) -> u16 { - self.0 - } -} - -impl Iterator for LookupHost { - type Item = SocketAddr; - fn next(&mut self) -> Option { - self.0 - } -} - -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &str) -> io::Result { - unsupported() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - unsupported() - } -} - -#[allow(nonstandard_style)] -pub mod netc { - pub const AF_INET: u8 = 0; - pub const AF_INET6: u8 = 1; - pub type sa_family_t = u8; - - #[derive(Copy, Clone)] - pub struct in_addr { - pub s_addr: u32, - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in { - #[allow(dead_code)] - pub sin_family: sa_family_t, - pub sin_port: u16, - pub sin_addr: in_addr, - } - - #[derive(Copy, Clone)] - pub struct in6_addr { - pub s6_addr: [u8; 16], - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in6 { - #[allow(dead_code)] - pub sin6_family: sa_family_t, - pub sin6_port: u16, - pub sin6_addr: in6_addr, - pub sin6_flowinfo: u32, - pub sin6_scope_id: u32, - } -} diff --git a/library/std/src/sys/pal/wasi/fs.rs b/library/std/src/sys/pal/wasi/fs.rs index 7779d2b97d7f9..faf3edd5da6ce 100644 --- a/library/std/src/sys/pal/wasi/fs.rs +++ b/library/std/src/sys/pal/wasi/fs.rs @@ -183,7 +183,7 @@ impl Iterator for ReadDir { fn next(&mut self) -> Option> { match &mut self.state { - ReadDirState::FillBuffer { next_read_offset, ref mut buf } => { + ReadDirState::FillBuffer { next_read_offset, buf } => { let result = self.inner.dir.fd.readdir(buf, *next_read_offset); match result { Ok(read_bytes) => { @@ -207,7 +207,7 @@ impl Iterator for ReadDir { } } } - ReadDirState::ProcessEntry { ref mut buf, next_read_offset, offset } => { + ReadDirState::ProcessEntry { buf, next_read_offset, offset } => { let contents = &buf[*offset..]; const DIRENT_SIZE: usize = crate::mem::size_of::(); if contents.len() >= DIRENT_SIZE { @@ -787,7 +787,7 @@ fn open_parent(p: &Path) -> io::Result<(ManuallyDrop, PathBuf)> { } } - extern "C" { + unsafe extern "C" { pub fn __wasilibc_find_relpath( path: *const libc::c_char, abs_prefix: *mut *const libc::c_char, diff --git a/library/std/src/sys/pal/wasi/io.rs b/library/std/src/sys/pal/wasi/io.rs deleted file mode 100644 index 57f81bc6257cd..0000000000000 --- a/library/std/src/sys/pal/wasi/io.rs +++ /dev/null @@ -1,84 +0,0 @@ -#![forbid(unsafe_op_in_unsafe_fn)] - -use crate::marker::PhantomData; -use crate::os::fd::{AsFd, AsRawFd}; -use crate::slice; - -#[derive(Copy, Clone)] -#[repr(transparent)] -pub struct IoSlice<'a> { - vec: wasi::Ciovec, - _p: PhantomData<&'a [u8]>, -} - -impl<'a> IoSlice<'a> { - #[inline] - pub fn new(buf: &'a [u8]) -> IoSlice<'a> { - IoSlice { vec: wasi::Ciovec { buf: buf.as_ptr(), buf_len: buf.len() }, _p: PhantomData } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if self.vec.buf_len < n { - panic!("advancing IoSlice beyond its length"); - } - - unsafe { - self.vec.buf_len -= n; - self.vec.buf = self.vec.buf.add(n); - } - } - - #[inline] - pub const fn as_slice(&self) -> &'a [u8] { - unsafe { slice::from_raw_parts(self.vec.buf as *const u8, self.vec.buf_len) } - } -} - -#[repr(transparent)] -pub struct IoSliceMut<'a> { - vec: wasi::Iovec, - _p: PhantomData<&'a mut [u8]>, -} - -impl<'a> IoSliceMut<'a> { - #[inline] - pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { - IoSliceMut { - vec: wasi::Iovec { buf: buf.as_mut_ptr(), buf_len: buf.len() }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if self.vec.buf_len < n { - panic!("advancing IoSlice beyond its length"); - } - - unsafe { - self.vec.buf_len -= n; - self.vec.buf = self.vec.buf.add(n); - } - } - - #[inline] - pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.vec.buf as *const u8, self.vec.buf_len) } - } - - #[inline] - pub const fn into_slice(self) -> &'a mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.buf_len) } - } - - #[inline] - pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.buf_len) } - } -} - -pub fn is_terminal(fd: &impl AsFd) -> bool { - let fd = fd.as_fd(); - unsafe { libc::isatty(fd.as_raw_fd()) != 0 } -} diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs index 5d54c7903065c..f4588a60ea9a4 100644 --- a/library/std/src/sys/pal/wasi/mod.rs +++ b/library/std/src/sys/pal/wasi/mod.rs @@ -1,8 +1,7 @@ //! System bindings for the wasm/web platform //! //! This module contains the facade (aka platform-specific) implementations of -//! OS level functionality for wasm. Note that this wasm is *not* the emscripten -//! wasm, so we have no runtime here. +//! OS level functionality for wasm. //! //! This is all super highly experimental and not actually intended for //! wide/production use yet, it's still all in the experimental category. This @@ -21,9 +20,7 @@ pub mod fs; #[allow(unused)] #[path = "../wasm/atomics/futex.rs"] pub mod futex; -pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -46,5 +43,4 @@ mod helpers; // import conflict rules. If we glob export `helpers` and `common` together, // then the compiler complains about conflicts. -use helpers::err2io; -pub use helpers::{abort_internal, decode_error_kind, is_interrupted}; +pub(crate) use helpers::{abort_internal, decode_error_kind, err2io, is_interrupted}; diff --git a/library/std/src/sys/pal/wasi/net.rs b/library/std/src/sys/pal/wasi/net.rs deleted file mode 100644 index a648679982812..0000000000000 --- a/library/std/src/sys/pal/wasi/net.rs +++ /dev/null @@ -1,543 +0,0 @@ -#![forbid(unsafe_op_in_unsafe_fn)] - -use super::err2io; -use super::fd::WasiFd; -use crate::fmt; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; -use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; -use crate::sys::unsupported; -use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::time::Duration; - -pub struct Socket(WasiFd); - -pub struct TcpStream { - inner: Socket, -} - -impl AsInner for Socket { - #[inline] - fn as_inner(&self) -> &WasiFd { - &self.0 - } -} - -impl IntoInner for Socket { - fn into_inner(self) -> WasiFd { - self.0 - } -} - -impl FromInner for Socket { - fn from_inner(inner: WasiFd) -> Socket { - Socket(inner) - } -} - -impl AsFd for Socket { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl AsRawFd for Socket { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -impl IntoRawFd for Socket { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } -} - -impl FromRawFd for Socket { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) } - } -} - -impl TcpStream { - pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { - unsupported() - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - unsupported() - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - unsupported() - } - - pub fn read_timeout(&self) -> io::Result> { - unsupported() - } - - pub fn write_timeout(&self) -> io::Result> { - unsupported() - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unsupported() - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.read_vectored(&mut [IoSliceMut::new(buf)]) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.socket().as_inner().read_buf(buf) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.socket().as_inner().read(bufs) - } - - pub fn is_read_vectored(&self) -> bool { - true - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.write_vectored(&[IoSlice::new(buf)]) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.socket().as_inner().write(bufs) - } - - pub fn is_write_vectored(&self) -> bool { - true - } - - pub fn peer_addr(&self) -> io::Result { - unsupported() - } - - pub fn socket_addr(&self) -> io::Result { - unsupported() - } - - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - let wasi_how = match how { - Shutdown::Read => wasi::SDFLAGS_RD, - Shutdown::Write => wasi::SDFLAGS_WR, - Shutdown::Both => wasi::SDFLAGS_RD | wasi::SDFLAGS_WR, - }; - - unsafe { wasi::sock_shutdown(self.socket().as_raw_fd() as _, wasi_how).map_err(err2io) } - } - - pub fn duplicate(&self) -> io::Result { - unsupported() - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - unsupported() - } - - pub fn linger(&self) -> io::Result> { - unsupported() - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn nodelay(&self) -> io::Result { - unsupported() - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unsupported() - } - - pub fn ttl(&self) -> io::Result { - unsupported() - } - - pub fn take_error(&self) -> io::Result> { - unsupported() - } - - pub fn set_nonblocking(&self, state: bool) -> io::Result<()> { - let fdstat = unsafe { - wasi::fd_fdstat_get(self.socket().as_inner().as_raw_fd() as wasi::Fd).map_err(err2io)? - }; - - let mut flags = fdstat.fs_flags; - - if state { - flags |= wasi::FDFLAGS_NONBLOCK; - } else { - flags &= !wasi::FDFLAGS_NONBLOCK; - } - - unsafe { - wasi::fd_fdstat_set_flags(self.socket().as_inner().as_raw_fd() as wasi::Fd, flags) - .map_err(err2io) - } - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } -} - -impl FromInner for TcpStream { - fn from_inner(socket: Socket) -> TcpStream { - TcpStream { inner: socket } - } -} - -impl fmt::Debug for TcpStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TcpStream").field("fd", &self.inner.as_raw_fd()).finish() - } -} - -pub struct TcpListener { - inner: Socket, -} - -impl TcpListener { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn socket_addr(&self) -> io::Result { - unsupported() - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - let fd = unsafe { - wasi::sock_accept(self.as_inner().as_inner().as_raw_fd() as _, 0).map_err(err2io)? - }; - - Ok(( - TcpStream::from_inner(unsafe { Socket::from_raw_fd(fd as _) }), - // WASI has no concept of SocketAddr yet - // return an unspecified IPv4Addr - SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), - )) - } - - pub fn duplicate(&self) -> io::Result { - unsupported() - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unsupported() - } - - pub fn ttl(&self) -> io::Result { - unsupported() - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn only_v6(&self) -> io::Result { - unsupported() - } - - pub fn take_error(&self) -> io::Result> { - unsupported() - } - - pub fn set_nonblocking(&self, state: bool) -> io::Result<()> { - let fdstat = unsafe { - wasi::fd_fdstat_get(self.socket().as_inner().as_raw_fd() as wasi::Fd).map_err(err2io)? - }; - - let mut flags = fdstat.fs_flags; - - if state { - flags |= wasi::FDFLAGS_NONBLOCK; - } else { - flags &= !wasi::FDFLAGS_NONBLOCK; - } - - unsafe { - wasi::fd_fdstat_set_flags(self.socket().as_inner().as_raw_fd() as wasi::Fd, flags) - .map_err(err2io) - } - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } -} - -impl AsInner for TcpListener { - #[inline] - fn as_inner(&self) -> &Socket { - &self.inner - } -} - -impl IntoInner for TcpListener { - fn into_inner(self) -> Socket { - self.inner - } -} - -impl FromInner for TcpListener { - fn from_inner(inner: Socket) -> TcpListener { - TcpListener { inner } - } -} - -impl fmt::Debug for TcpListener { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TcpListener").field("fd", &self.inner.as_raw_fd()).finish() - } -} - -pub struct UdpSocket { - inner: Socket, -} - -impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn peer_addr(&self) -> io::Result { - unsupported() - } - - pub fn socket_addr(&self) -> io::Result { - unsupported() - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unsupported() - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unsupported() - } - - pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { - unsupported() - } - - pub fn duplicate(&self) -> io::Result { - unsupported() - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - unsupported() - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - unsupported() - } - - pub fn read_timeout(&self) -> io::Result> { - unsupported() - } - - pub fn write_timeout(&self) -> io::Result> { - unsupported() - } - - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn broadcast(&self) -> io::Result { - unsupported() - } - - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn multicast_loop_v4(&self) -> io::Result { - unsupported() - } - - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - unsupported() - } - - pub fn multicast_ttl_v4(&self) -> io::Result { - unsupported() - } - - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn multicast_loop_v6(&self) -> io::Result { - unsupported() - } - - pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - unsupported() - } - - pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - unsupported() - } - - pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - unsupported() - } - - pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - unsupported() - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unsupported() - } - - pub fn ttl(&self) -> io::Result { - unsupported() - } - - pub fn take_error(&self) -> io::Result> { - unsupported() - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn recv(&self, _: &mut [u8]) -> io::Result { - unsupported() - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unsupported() - } - - pub fn send(&self, _: &[u8]) -> io::Result { - unsupported() - } - - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - unsupported() - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } -} - -impl AsInner for UdpSocket { - #[inline] - fn as_inner(&self) -> &Socket { - &self.inner - } -} - -impl IntoInner for UdpSocket { - fn into_inner(self) -> Socket { - self.inner - } -} - -impl FromInner for UdpSocket { - fn from_inner(inner: Socket) -> UdpSocket { - UdpSocket { inner } - } -} - -impl fmt::Debug for UdpSocket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("UdpSocket").field("fd", &self.inner.as_raw_fd()).finish() - } -} - -pub struct LookupHost(!); - -impl LookupHost { - pub fn port(&self) -> u16 { - self.0 - } -} - -impl Iterator for LookupHost { - type Item = SocketAddr; - fn next(&mut self) -> Option { - self.0 - } -} - -impl<'a> TryFrom<&'a str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &'a str) -> io::Result { - unsupported() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - unsupported() - } -} - -#[allow(nonstandard_style)] -pub mod netc { - pub const AF_INET: u8 = 0; - pub const AF_INET6: u8 = 1; - pub type sa_family_t = u8; - - #[derive(Copy, Clone)] - pub struct in_addr { - pub s_addr: u32, - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in { - #[allow(dead_code)] - pub sin_family: sa_family_t, - pub sin_port: u16, - pub sin_addr: in_addr, - } - - #[derive(Copy, Clone)] - pub struct in6_addr { - pub s6_addr: [u8; 16], - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in6 { - #[allow(dead_code)] - pub sin6_family: sa_family_t, - pub sin6_port: u16, - pub sin6_addr: in6_addr, - pub sin6_flowinfo: u32, - pub sin6_scope_id: u32, - } -} diff --git a/library/std/src/sys/pal/wasi/os.rs b/library/std/src/sys/pal/wasi/os.rs index f7701360f5a9c..ba2b65a1f40dc 100644 --- a/library/std/src/sys/pal/wasi/os.rs +++ b/library/std/src/sys/pal/wasi/os.rs @@ -16,7 +16,7 @@ use crate::{fmt, io, str, vec}; mod libc { pub use libc::*; - extern "C" { + unsafe extern "C" { pub fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char; pub fn chdir(dir: *const c_char) -> c_int; pub fn __wasilibc_get_environ() -> *mut *mut c_char; @@ -46,7 +46,7 @@ cfg_if::cfg_if! { } pub fn errno() -> i32 { - extern "C" { + unsafe extern "C" { #[thread_local] static errno: libc::c_int; } diff --git a/library/std/src/sys/pal/wasi/stdio.rs b/library/std/src/sys/pal/wasi/stdio.rs index ca49f871e1957..d08b772e5fc7d 100644 --- a/library/std/src/sys/pal/wasi/stdio.rs +++ b/library/std/src/sys/pal/wasi/stdio.rs @@ -101,7 +101,7 @@ impl io::Write for Stderr { } } -pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; pub fn is_ebadf(err: &io::Error) -> bool { err.raw_os_error() == Some(wasi::ERRNO_BADF.raw().into()) diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs index f5e19f26bfe17..0ae0236941061 100644 --- a/library/std/src/sys/pal/wasi/thread.rs +++ b/library/std/src/sys/pal/wasi/thread.rs @@ -35,7 +35,7 @@ cfg_if::cfg_if! { pub const _SC_NPROCESSORS_ONLN: ffi::c_int = 84; - extern "C" { + unsafe extern "C" { pub fn pthread_create( native: *mut pthread_t, attr: *const pthread_attr_t, diff --git a/library/std/src/sys/pal/wasip2/cabi_realloc.rs b/library/std/src/sys/pal/wasip2/cabi_realloc.rs index 820063173d657..78adf9002fd7e 100644 --- a/library/std/src/sys/pal/wasip2/cabi_realloc.rs +++ b/library/std/src/sys/pal/wasip2/cabi_realloc.rs @@ -32,7 +32,7 @@ static FORCE_CODEGEN_OF_CABI_REALLOC: unsafe extern "C" fn( ) -> *mut u8 = cabi_realloc; #[linkage = "weak"] -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn cabi_realloc( old_ptr: *mut u8, old_len: usize, diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs index 320712fdcc9fe..72c9742b2e549 100644 --- a/library/std/src/sys/pal/wasip2/mod.rs +++ b/library/std/src/sys/pal/wasip2/mod.rs @@ -17,10 +17,7 @@ pub mod fs; #[allow(unused)] #[path = "../wasm/atomics/futex.rs"] pub mod futex; -#[path = "../wasi/io.rs"] -pub mod io; -pub mod net; #[path = "../wasi/os.rs"] pub mod os; #[path = "../unsupported/pipe.rs"] diff --git a/library/std/src/sys/pal/wasip2/net.rs b/library/std/src/sys/pal/wasip2/net.rs deleted file mode 100644 index f009a51821f35..0000000000000 --- a/library/std/src/sys/pal/wasip2/net.rs +++ /dev/null @@ -1,417 +0,0 @@ -#![deny(unsafe_op_in_unsafe_fn)] - -use libc::{c_int, c_void, size_t}; - -use crate::ffi::CStr; -use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{Shutdown, SocketAddr}; -use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::sys::unsupported; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::time::{Duration, Instant}; -use crate::{cmp, mem, str}; - -pub extern crate libc as netc; - -#[allow(non_camel_case_types)] -pub type wrlen_t = size_t; - -#[doc(hidden)] -pub trait IsMinusOne { - fn is_minus_one(&self) -> bool; -} - -macro_rules! impl_is_minus_one { - ($($t:ident)*) => ($(impl IsMinusOne for $t { - fn is_minus_one(&self) -> bool { - *self == -1 - } - })*) -} - -impl_is_minus_one! { i8 i16 i32 i64 isize } - -pub fn cvt(t: T) -> crate::io::Result { - if t.is_minus_one() { Err(crate::io::Error::last_os_error()) } else { Ok(t) } -} - -pub fn cvt_r(mut f: F) -> crate::io::Result -where - T: IsMinusOne, - F: FnMut() -> T, -{ - loop { - match cvt(f()) { - Err(ref e) if e.is_interrupted() => {} - other => return other, - } - } -} - -pub fn cvt_gai(err: c_int) -> io::Result<()> { - if err == 0 { - return Ok(()); - } - - if err == netc::EAI_SYSTEM { - return Err(io::Error::last_os_error()); - } - - let detail = unsafe { - str::from_utf8(CStr::from_ptr(netc::gai_strerror(err)).to_bytes()).unwrap().to_owned() - }; - - Err(io::Error::new( - io::ErrorKind::Uncategorized, - &format!("failed to lookup address information: {detail}")[..], - )) -} - -pub fn init() {} - -pub struct WasiSocket(OwnedFd); - -pub struct Socket(WasiSocket); - -impl Socket { - pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { - let fam = match *addr { - SocketAddr::V4(..) => netc::AF_INET, - SocketAddr::V6(..) => netc::AF_INET6, - }; - Socket::new_raw(fam, ty) - } - - pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { - let fd = cvt(unsafe { netc::socket(fam, ty, 0) })?; - Ok(unsafe { Self::from_raw_fd(fd) }) - } - - pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { - let (addr, len) = addr.into_inner(); - cvt_r(|| unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?; - Ok(()) - } - - pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { - self.set_nonblocking(true)?; - let r = self.connect(addr); - self.set_nonblocking(false)?; - - match r { - Ok(_) => return Ok(()), - // there's no ErrorKind for EINPROGRESS - Err(ref e) if e.raw_os_error() == Some(netc::EINPROGRESS) => {} - Err(e) => return Err(e), - } - - let mut pollfd = netc::pollfd { fd: self.as_raw_fd(), events: netc::POLLOUT, revents: 0 }; - - if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { - return Err(io::Error::ZERO_TIMEOUT); - } - - let start = Instant::now(); - - loop { - let elapsed = start.elapsed(); - if elapsed >= timeout { - return Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")); - } - - let timeout = timeout - elapsed; - let mut timeout = timeout - .as_secs() - .saturating_mul(1_000) - .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); - if timeout == 0 { - timeout = 1; - } - - let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; - - match unsafe { netc::poll(&mut pollfd, 1, timeout) } { - -1 => { - let err = io::Error::last_os_error(); - if !err.is_interrupted() { - return Err(err); - } - } - 0 => {} - _ => { - // WASI poll does not return POLLHUP or POLLERR in revents. Check if the - // connnection actually succeeded and return ok only when the socket is - // ready and no errors were found. - if let Some(e) = self.take_error()? { - return Err(e); - } - - return Ok(()); - } - } - } - } - - pub fn accept( - &self, - storage: *mut netc::sockaddr, - len: *mut netc::socklen_t, - ) -> io::Result { - let fd = cvt_r(|| unsafe { netc::accept(self.as_raw_fd(), storage, len) })?; - Ok(unsafe { Self::from_raw_fd(fd) }) - } - - pub fn duplicate(&self) -> io::Result { - unsupported() - } - - fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { - let ret = cvt(unsafe { - netc::recv( - self.as_raw_fd(), - buf.as_mut().as_mut_ptr() as *mut c_void, - buf.capacity(), - flags, - ) - })?; - unsafe { - buf.advance_unchecked(ret as usize); - } - Ok(()) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), 0)?; - Ok(buf.len()) - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), netc::MSG_PEEK)?; - Ok(buf.len()) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.recv_with_flags(buf, 0) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - io::default_read_vectored(|b| self.read(b), bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - false - } - - fn recv_from_with_flags( - &self, - buf: &mut [u8], - flags: c_int, - ) -> io::Result<(usize, SocketAddr)> { - let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() }; - let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t; - - let n = cvt(unsafe { - netc::recvfrom( - self.as_raw_fd(), - buf.as_mut_ptr() as *mut c_void, - buf.len(), - flags, - core::ptr::addr_of_mut!(storage) as *mut _, - &mut addrlen, - ) - })?; - Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?)) - } - - pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, 0) - } - - pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, netc::MSG_PEEK) - } - - fn write(&self, buf: &[u8]) -> io::Result { - let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; - let ret = cvt(unsafe { - netc::send(self.as_raw(), buf.as_ptr() as *const c_void, len, netc::MSG_NOSIGNAL) - })?; - Ok(ret as usize) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - io::default_write_vectored(|b| self.write(b), bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn set_timeout(&self, dur: Option, kind: c_int) -> io::Result<()> { - let timeout = match dur { - Some(dur) => { - if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { - return Err(io::Error::ZERO_TIMEOUT); - } - - let secs = dur.as_secs().try_into().unwrap_or(netc::time_t::MAX); - let mut timeout = netc::timeval { - tv_sec: secs, - tv_usec: dur.subsec_micros() as netc::suseconds_t, - }; - if timeout.tv_sec == 0 && timeout.tv_usec == 0 { - timeout.tv_usec = 1; - } - timeout - } - None => netc::timeval { tv_sec: 0, tv_usec: 0 }, - }; - setsockopt(self, netc::SOL_SOCKET, kind, timeout) - } - - pub fn timeout(&self, kind: c_int) -> io::Result> { - let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?; - if raw.tv_sec == 0 && raw.tv_usec == 0 { - Ok(None) - } else { - let sec = raw.tv_sec as u64; - let nsec = (raw.tv_usec as u32) * 1000; - Ok(Some(Duration::new(sec, nsec))) - } - } - - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - let how = match how { - Shutdown::Write => netc::SHUT_WR, - Shutdown::Read => netc::SHUT_RD, - Shutdown::Both => netc::SHUT_RDWR, - }; - cvt(unsafe { netc::shutdown(self.as_raw_fd(), how) })?; - Ok(()) - } - - pub fn set_linger(&self, _linger: Option) -> io::Result<()> { - unsupported() - } - - pub fn linger(&self) -> io::Result> { - unsupported() - } - - pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int) - } - - pub fn nodelay(&self) -> io::Result { - let raw: c_int = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?; - Ok(raw != 0) - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - let mut nonblocking = nonblocking as c_int; - cvt(unsafe { netc::ioctl(self.as_raw_fd(), netc::FIONBIO, &mut nonblocking) }).map(drop) - } - - pub fn take_error(&self) -> io::Result> { - let raw: c_int = getsockopt(self, netc::SOL_SOCKET, netc::SO_ERROR)?; - if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } - } - - // This is used by sys_common code to abstract over Windows and Unix. - pub fn as_raw(&self) -> RawFd { - self.as_raw_fd() - } -} - -impl AsInner for WasiSocket { - #[inline] - fn as_inner(&self) -> &OwnedFd { - &self.0 - } -} - -impl IntoInner for WasiSocket { - fn into_inner(self) -> OwnedFd { - self.0 - } -} - -impl FromInner for WasiSocket { - fn from_inner(owned_fd: OwnedFd) -> Self { - Self(owned_fd) - } -} - -impl AsFd for WasiSocket { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl AsRawFd for WasiSocket { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -impl IntoRawFd for WasiSocket { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } -} - -impl FromRawFd for WasiSocket { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) } - } -} - -impl AsInner for Socket { - #[inline] - fn as_inner(&self) -> &WasiSocket { - &self.0 - } -} - -impl IntoInner for Socket { - fn into_inner(self) -> WasiSocket { - self.0 - } -} - -impl FromInner for Socket { - fn from_inner(sock: WasiSocket) -> Socket { - Socket(sock) - } -} - -impl AsFd for Socket { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl AsRawFd for Socket { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -impl IntoRawFd for Socket { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } -} - -impl FromRawFd for Socket { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) } - } -} diff --git a/library/std/src/sys/pal/wasm/mod.rs b/library/std/src/sys/pal/wasm/mod.rs index 8141bfac49aad..32d59c4d0f7c4 100644 --- a/library/std/src/sys/pal/wasm/mod.rs +++ b/library/std/src/sys/pal/wasm/mod.rs @@ -2,7 +2,7 @@ //! //! This module contains the facade (aka platform-specific) implementations of //! OS level functionality for wasm. Note that this wasm is *not* the emscripten -//! wasm, so we have no runtime here. +//! or wasi wasm, so we have no runtime here. //! //! This is all super highly experimental and not actually intended for //! wide/production use yet, it's still all in the experimental category. This @@ -21,10 +21,6 @@ pub mod args; pub mod env; #[path = "../unsupported/fs.rs"] pub mod fs; -#[path = "../unsupported/io.rs"] -pub mod io; -#[path = "../unsupported/net.rs"] -pub mod net; #[path = "../unsupported/os.rs"] pub mod os; #[path = "../unsupported/pipe.rs"] diff --git a/library/std/src/sys/pal/windows/args/tests.rs b/library/std/src/sys/pal/windows/args/tests.rs index 6d5c953cbd5c6..484a90ab056df 100644 --- a/library/std/src/sys/pal/windows/args/tests.rs +++ b/library/std/src/sys/pal/windows/args/tests.rs @@ -47,10 +47,10 @@ fn whitespace_behavior() { fn genius_quotes() { chk(r#"EXE "" """#, &["EXE", "", ""]); chk(r#"EXE "" """"#, &["EXE", "", r#"""#]); - chk(r#"EXE "this is """all""" in the same argument""#, &[ - "EXE", - r#"this is "all" in the same argument"#, - ]); + chk( + r#"EXE "this is """all""" in the same argument""#, + &["EXE", r#"this is "all" in the same argument"#], + ); chk(r#"EXE "a"""#, &["EXE", r#"a""#]); chk(r#"EXE "a"" a"#, &["EXE", r#"a" a"#]); // quotes cannot be escaped in command names diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs index 9ce3e912caf1b..4fbdc839939c9 100644 --- a/library/std/src/sys/pal/windows/c.rs +++ b/library/std/src/sys/pal/windows/c.rs @@ -115,7 +115,7 @@ if #[cfg(not(target_vendor = "uwp"))] { link(name = "bcryptprimitives", kind = "raw-dylib", import_name_type = "undecorated") )] #[cfg_attr(not(target_arch = "x86"), link(name = "bcryptprimitives", kind = "raw-dylib"))] -extern "system" { +unsafe extern "system" { pub fn ProcessPrng(pbdata: *mut u8, cbdata: usize) -> BOOL; } @@ -164,7 +164,7 @@ compat_fn_with_fallback! { not(target_arch = "x86"), link(name = "api-ms-win-core-synch-l1-2-0", kind = "raw-dylib") )] -extern "system" { +unsafe extern "system" { pub fn WaitOnAddress( address: *const c_void, compareaddress: *const c_void, @@ -204,7 +204,7 @@ compat_fn_with_fallback! { pub fn NtReleaseKeyedEvent( EventHandle: HANDLE, Key: *const c_void, - Alertable: BOOLEAN, + Alertable: bool, Timeout: *mut i64 ) -> NTSTATUS { panic!("keyed events not available") @@ -213,7 +213,7 @@ compat_fn_with_fallback! { pub fn NtWaitForKeyedEvent( EventHandle: HANDLE, Key: *const c_void, - Alertable: BOOLEAN, + Alertable: bool, Timeout: *mut i64 ) -> NTSTATUS { panic!("keyed events not available") diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index c06f274685c24..e2c2163327968 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -1,2611 +1,2612 @@ --out windows_sys.rs ---config flatten sys +--flat +--sys +--no-core --filter -!Windows.Win32.Foundation.INVALID_HANDLE_VALUE -Windows.Wdk.Storage.FileSystem.FILE_COMPLETE_IF_OPLOCKED -Windows.Wdk.Storage.FileSystem.FILE_CONTAINS_EXTENDED_CREATE_INFORMATION -Windows.Wdk.Storage.FileSystem.FILE_CREATE -Windows.Wdk.Storage.FileSystem.FILE_CREATE_TREE_CONNECTION -Windows.Wdk.Storage.FileSystem.FILE_DELETE_ON_CLOSE -Windows.Wdk.Storage.FileSystem.FILE_DIRECTORY_FILE -Windows.Wdk.Storage.FileSystem.FILE_DISALLOW_EXCLUSIVE +!INVALID_HANDLE_VALUE +ABOVE_NORMAL_PRIORITY_CLASS +accept +AcquireSRWLockExclusive +AcquireSRWLockShared +ADDRESS_FAMILY +ADDRINFOA +AddVectoredExceptionHandler +AF_INET +AF_INET6 +AF_UNIX +AF_UNSPEC +ALL_PROCESSOR_GROUPS +ARM64_NT_NEON128 +BELOW_NORMAL_PRIORITY_CLASS +bind +BOOL +BY_HANDLE_FILE_INFORMATION +CALLBACK_CHUNK_FINISHED +CALLBACK_STREAM_SWITCH +CancelIo +CloseHandle +closesocket +COMPARESTRING_RESULT +CompareStringOrdinal +connect +CONSOLE_MODE +CONSOLE_READCONSOLE_CONTROL +CONTEXT +CopyFileExW +CP_UTF8 +CREATE_ALWAYS +CREATE_BREAKAWAY_FROM_JOB +CREATE_DEFAULT_ERROR_MODE +CREATE_FORCEDOS +CREATE_IGNORE_SYSTEM_DEFAULT +CREATE_NEW +CREATE_NEW_CONSOLE +CREATE_NEW_PROCESS_GROUP +CREATE_NO_WINDOW +CREATE_PRESERVE_CODE_AUTHZ_LEVEL +CREATE_PROTECTED_PROCESS +CREATE_SECURE_PROCESS +CREATE_SEPARATE_WOW_VDM +CREATE_SHARED_WOW_VDM +CREATE_SUSPENDED +CREATE_UNICODE_ENVIRONMENT +CREATE_WAITABLE_TIMER_HIGH_RESOLUTION +CREATE_WAITABLE_TIMER_MANUAL_RESET +CreateDirectoryW +CreateEventW +CreateFileW +CreateHardLinkW +CreateNamedPipeW +CreatePipe +CreateProcessW +CreateSymbolicLinkW +CreateThread +CreateWaitableTimerExW +CSTR_EQUAL +CSTR_GREATER_THAN +CSTR_LESS_THAN +DEBUG_ONLY_THIS_PROCESS +DEBUG_PROCESS +DELETE +DeleteFileW +DeleteProcThreadAttributeList +DETACHED_PROCESS +DeviceIoControl +DISABLE_NEWLINE_AUTO_RETURN +DLL_PROCESS_DETACH +DLL_THREAD_DETACH +DNS_ERROR_ADDRESS_REQUIRED +DNS_ERROR_ALIAS_LOOP +DNS_ERROR_AUTOZONE_ALREADY_EXISTS +DNS_ERROR_AXFR +DNS_ERROR_BACKGROUND_LOADING +DNS_ERROR_BAD_KEYMASTER +DNS_ERROR_BAD_PACKET +DNS_ERROR_CANNOT_FIND_ROOT_HINTS +DNS_ERROR_CLIENT_SUBNET_ALREADY_EXISTS +DNS_ERROR_CLIENT_SUBNET_DOES_NOT_EXIST +DNS_ERROR_CLIENT_SUBNET_IS_ACCESSED +DNS_ERROR_CNAME_COLLISION +DNS_ERROR_CNAME_LOOP +DNS_ERROR_DATAFILE_OPEN_FAILURE +DNS_ERROR_DATAFILE_PARSING +DNS_ERROR_DEFAULT_SCOPE +DNS_ERROR_DEFAULT_VIRTUALIZATION_INSTANCE +DNS_ERROR_DEFAULT_ZONESCOPE +DNS_ERROR_DELEGATION_REQUIRED +DNS_ERROR_DNAME_COLLISION +DNS_ERROR_DNSSEC_IS_DISABLED +DNS_ERROR_DP_ALREADY_ENLISTED +DNS_ERROR_DP_ALREADY_EXISTS +DNS_ERROR_DP_DOES_NOT_EXIST +DNS_ERROR_DP_FSMO_ERROR +DNS_ERROR_DP_NOT_AVAILABLE +DNS_ERROR_DP_NOT_ENLISTED +DNS_ERROR_DS_UNAVAILABLE +DNS_ERROR_DS_ZONE_ALREADY_EXISTS +DNS_ERROR_DWORD_VALUE_TOO_LARGE +DNS_ERROR_DWORD_VALUE_TOO_SMALL +DNS_ERROR_FILE_WRITEBACK_FAILED +DNS_ERROR_FORWARDER_ALREADY_EXISTS +DNS_ERROR_INCONSISTENT_ROOT_HINTS +DNS_ERROR_INVAILD_VIRTUALIZATION_INSTANCE_NAME +DNS_ERROR_INVALID_CLIENT_SUBNET_NAME +DNS_ERROR_INVALID_DATA +DNS_ERROR_INVALID_DATAFILE_NAME +DNS_ERROR_INVALID_INITIAL_ROLLOVER_OFFSET +DNS_ERROR_INVALID_IP_ADDRESS +DNS_ERROR_INVALID_KEY_SIZE +DNS_ERROR_INVALID_NAME +DNS_ERROR_INVALID_NAME_CHAR +DNS_ERROR_INVALID_NSEC3_ITERATION_COUNT +DNS_ERROR_INVALID_POLICY_TABLE +DNS_ERROR_INVALID_PROPERTY +DNS_ERROR_INVALID_ROLLOVER_PERIOD +DNS_ERROR_INVALID_SCOPE_NAME +DNS_ERROR_INVALID_SCOPE_OPERATION +DNS_ERROR_INVALID_SIGNATURE_VALIDITY_PERIOD +DNS_ERROR_INVALID_TYPE +DNS_ERROR_INVALID_XML +DNS_ERROR_INVALID_ZONE_OPERATION +DNS_ERROR_INVALID_ZONE_TYPE +DNS_ERROR_INVALID_ZONESCOPE_NAME +DNS_ERROR_KEYMASTER_REQUIRED +DNS_ERROR_KSP_DOES_NOT_SUPPORT_PROTECTION +DNS_ERROR_KSP_NOT_ACCESSIBLE +DNS_ERROR_LOAD_ZONESCOPE_FAILED +DNS_ERROR_NAME_DOES_NOT_EXIST +DNS_ERROR_NAME_NOT_IN_ZONE +DNS_ERROR_NBSTAT_INIT_FAILED +DNS_ERROR_NEED_SECONDARY_ADDRESSES +DNS_ERROR_NEED_WINS_SERVERS +DNS_ERROR_NO_BOOTFILE_IF_DS_ZONE +DNS_ERROR_NO_CREATE_CACHE_DATA +DNS_ERROR_NO_DNS_SERVERS +DNS_ERROR_NO_MEMORY +DNS_ERROR_NO_PACKET +DNS_ERROR_NO_TCPIP +DNS_ERROR_NO_VALID_TRUST_ANCHORS +DNS_ERROR_NO_ZONE_INFO +DNS_ERROR_NODE_CREATION_FAILED +DNS_ERROR_NODE_IS_CNAME +DNS_ERROR_NODE_IS_DNAME +DNS_ERROR_NON_RFC_NAME +DNS_ERROR_NOT_ALLOWED_ON_ACTIVE_SKD +DNS_ERROR_NOT_ALLOWED_ON_RODC +DNS_ERROR_NOT_ALLOWED_ON_ROOT_SERVER +DNS_ERROR_NOT_ALLOWED_ON_SIGNED_ZONE +DNS_ERROR_NOT_ALLOWED_ON_UNSIGNED_ZONE +DNS_ERROR_NOT_ALLOWED_ON_ZSK +DNS_ERROR_NOT_ALLOWED_UNDER_DELEGATION +DNS_ERROR_NOT_ALLOWED_UNDER_DNAME +DNS_ERROR_NOT_ALLOWED_WITH_ZONESCOPES +DNS_ERROR_NOT_ENOUGH_SIGNING_KEY_DESCRIPTORS +DNS_ERROR_NOT_UNIQUE +DNS_ERROR_NSEC3_INCOMPATIBLE_WITH_RSA_SHA1 +DNS_ERROR_NSEC3_NAME_COLLISION +DNS_ERROR_NSEC_INCOMPATIBLE_WITH_NSEC3_RSA_SHA1 +DNS_ERROR_NUMERIC_NAME +DNS_ERROR_POLICY_ALREADY_EXISTS +DNS_ERROR_POLICY_DOES_NOT_EXIST +DNS_ERROR_POLICY_INVALID_CRITERIA +DNS_ERROR_POLICY_INVALID_CRITERIA_CLIENT_SUBNET +DNS_ERROR_POLICY_INVALID_CRITERIA_FQDN +DNS_ERROR_POLICY_INVALID_CRITERIA_INTERFACE +DNS_ERROR_POLICY_INVALID_CRITERIA_NETWORK_PROTOCOL +DNS_ERROR_POLICY_INVALID_CRITERIA_QUERY_TYPE +DNS_ERROR_POLICY_INVALID_CRITERIA_TIME_OF_DAY +DNS_ERROR_POLICY_INVALID_CRITERIA_TRANSPORT_PROTOCOL +DNS_ERROR_POLICY_INVALID_NAME +DNS_ERROR_POLICY_INVALID_SETTINGS +DNS_ERROR_POLICY_INVALID_WEIGHT +DNS_ERROR_POLICY_LOCKED +DNS_ERROR_POLICY_MISSING_CRITERIA +DNS_ERROR_POLICY_PROCESSING_ORDER_INVALID +DNS_ERROR_POLICY_SCOPE_MISSING +DNS_ERROR_POLICY_SCOPE_NOT_ALLOWED +DNS_ERROR_PRIMARY_REQUIRES_DATAFILE +DNS_ERROR_RCODE +DNS_ERROR_RCODE_BADKEY +DNS_ERROR_RCODE_BADSIG +DNS_ERROR_RCODE_BADTIME +DNS_ERROR_RCODE_FORMAT_ERROR +DNS_ERROR_RCODE_NAME_ERROR +DNS_ERROR_RCODE_NOT_IMPLEMENTED +DNS_ERROR_RCODE_NOTAUTH +DNS_ERROR_RCODE_NOTZONE +DNS_ERROR_RCODE_NXRRSET +DNS_ERROR_RCODE_REFUSED +DNS_ERROR_RCODE_SERVER_FAILURE +DNS_ERROR_RCODE_YXDOMAIN +DNS_ERROR_RCODE_YXRRSET +DNS_ERROR_RECORD_ALREADY_EXISTS +DNS_ERROR_RECORD_DOES_NOT_EXIST +DNS_ERROR_RECORD_FORMAT +DNS_ERROR_RECORD_ONLY_AT_ZONE_ROOT +DNS_ERROR_RECORD_TIMED_OUT +DNS_ERROR_ROLLOVER_ALREADY_QUEUED +DNS_ERROR_ROLLOVER_IN_PROGRESS +DNS_ERROR_ROLLOVER_NOT_POKEABLE +DNS_ERROR_RRL_INVALID_IPV4_PREFIX +DNS_ERROR_RRL_INVALID_IPV6_PREFIX +DNS_ERROR_RRL_INVALID_LEAK_RATE +DNS_ERROR_RRL_INVALID_TC_RATE +DNS_ERROR_RRL_INVALID_WINDOW_SIZE +DNS_ERROR_RRL_LEAK_RATE_LESSTHAN_TC_RATE +DNS_ERROR_RRL_NOT_ENABLED +DNS_ERROR_SCOPE_ALREADY_EXISTS +DNS_ERROR_SCOPE_DOES_NOT_EXIST +DNS_ERROR_SCOPE_LOCKED +DNS_ERROR_SECONDARY_DATA +DNS_ERROR_SECONDARY_REQUIRES_MASTER_IP +DNS_ERROR_SERVERSCOPE_IS_REFERENCED +DNS_ERROR_SIGNING_KEY_NOT_ACCESSIBLE +DNS_ERROR_SOA_DELETE_INVALID +DNS_ERROR_STANDBY_KEY_NOT_PRESENT +DNS_ERROR_SUBNET_ALREADY_EXISTS +DNS_ERROR_SUBNET_DOES_NOT_EXIST +DNS_ERROR_TOO_MANY_SKDS +DNS_ERROR_TRY_AGAIN_LATER +DNS_ERROR_UNEXPECTED_CNG_ERROR +DNS_ERROR_UNEXPECTED_DATA_PROTECTION_ERROR +DNS_ERROR_UNKNOWN_RECORD_TYPE +DNS_ERROR_UNKNOWN_SIGNING_PARAMETER_VERSION +DNS_ERROR_UNSECURE_PACKET +DNS_ERROR_UNSUPPORTED_ALGORITHM +DNS_ERROR_VIRTUALIZATION_INSTANCE_ALREADY_EXISTS +DNS_ERROR_VIRTUALIZATION_INSTANCE_DOES_NOT_EXIST +DNS_ERROR_VIRTUALIZATION_TREE_LOCKED +DNS_ERROR_WINS_INIT_FAILED +DNS_ERROR_ZONE_ALREADY_EXISTS +DNS_ERROR_ZONE_CONFIGURATION_ERROR +DNS_ERROR_ZONE_CREATION_FAILED +DNS_ERROR_ZONE_DOES_NOT_EXIST +DNS_ERROR_ZONE_HAS_NO_NS_RECORDS +DNS_ERROR_ZONE_HAS_NO_SOA_RECORD +DNS_ERROR_ZONE_IS_SHUTDOWN +DNS_ERROR_ZONE_LOCKED +DNS_ERROR_ZONE_LOCKED_FOR_SIGNING +DNS_ERROR_ZONE_NOT_SECONDARY +DNS_ERROR_ZONE_REQUIRES_MASTER_IP +DNS_ERROR_ZONESCOPE_ALREADY_EXISTS +DNS_ERROR_ZONESCOPE_DOES_NOT_EXIST +DNS_ERROR_ZONESCOPE_FILE_WRITEBACK_FAILED +DNS_ERROR_ZONESCOPE_IS_REFERENCED +DUPLICATE_CLOSE_SOURCE +DUPLICATE_HANDLE_OPTIONS +DUPLICATE_SAME_ACCESS +DuplicateHandle +E_NOTIMPL +ENABLE_AUTO_POSITION +ENABLE_ECHO_INPUT +ENABLE_EXTENDED_FLAGS +ENABLE_INSERT_MODE +ENABLE_LINE_INPUT +ENABLE_LVB_GRID_WORLDWIDE +ENABLE_MOUSE_INPUT +ENABLE_PROCESSED_INPUT +ENABLE_PROCESSED_OUTPUT +ENABLE_QUICK_EDIT_MODE +ENABLE_VIRTUAL_TERMINAL_INPUT +ENABLE_VIRTUAL_TERMINAL_PROCESSING +ENABLE_WINDOW_INPUT +ENABLE_WRAP_AT_EOL_OUTPUT +ERROR_ABANDON_HIBERFILE +ERROR_ABANDONED_WAIT_0 +ERROR_ABANDONED_WAIT_63 +ERROR_ABIOS_ERROR +ERROR_ACCESS_AUDIT_BY_POLICY +ERROR_ACCESS_DENIED +ERROR_ACCESS_DENIED_APPDATA +ERROR_ACCESS_DISABLED_BY_POLICY +ERROR_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY +ERROR_ACCESS_DISABLED_WEBBLADE +ERROR_ACCESS_DISABLED_WEBBLADE_TAMPER +ERROR_ACCOUNT_DISABLED +ERROR_ACCOUNT_EXPIRED +ERROR_ACCOUNT_LOCKED_OUT +ERROR_ACCOUNT_RESTRICTION +ERROR_ACPI_ERROR +ERROR_ACTIVE_CONNECTIONS +ERROR_ADAP_HDW_ERR +ERROR_ADDRESS_ALREADY_ASSOCIATED +ERROR_ADDRESS_NOT_ASSOCIATED +ERROR_ALERTED +ERROR_ALIAS_EXISTS +ERROR_ALL_USER_TRUST_QUOTA_EXCEEDED +ERROR_ALLOCATE_BUCKET +ERROR_ALLOTTED_SPACE_EXCEEDED +ERROR_ALREADY_ASSIGNED +ERROR_ALREADY_EXISTS +ERROR_ALREADY_FIBER +ERROR_ALREADY_HAS_STREAM_ID +ERROR_ALREADY_INITIALIZED +ERROR_ALREADY_REGISTERED +ERROR_ALREADY_RUNNING_LKG +ERROR_ALREADY_THREAD +ERROR_ALREADY_WAITING +ERROR_ALREADY_WIN32 +ERROR_API_UNAVAILABLE +ERROR_APP_HANG +ERROR_APP_INIT_FAILURE +ERROR_APP_WRONG_OS +ERROR_APPCONTAINER_REQUIRED +ERROR_APPEXEC_APP_COMPAT_BLOCK +ERROR_APPEXEC_CALLER_WAIT_TIMEOUT +ERROR_APPEXEC_CALLER_WAIT_TIMEOUT_LICENSING +ERROR_APPEXEC_CALLER_WAIT_TIMEOUT_RESOURCES +ERROR_APPEXEC_CALLER_WAIT_TIMEOUT_TERMINATION +ERROR_APPEXEC_CONDITION_NOT_SATISFIED +ERROR_APPEXEC_HANDLE_INVALIDATED +ERROR_APPEXEC_HOST_ID_MISMATCH +ERROR_APPEXEC_INVALID_HOST_GENERATION +ERROR_APPEXEC_INVALID_HOST_STATE +ERROR_APPEXEC_NO_DONOR +ERROR_APPEXEC_UNEXPECTED_PROCESS_REGISTRATION +ERROR_APPEXEC_UNKNOWN_USER +ERROR_APPHELP_BLOCK +ERROR_APPX_FILE_NOT_ENCRYPTED +ERROR_ARBITRATION_UNHANDLED +ERROR_ARENA_TRASHED +ERROR_ARITHMETIC_OVERFLOW +ERROR_ASSERTION_FAILURE +ERROR_ATOMIC_LOCKS_NOT_SUPPORTED +ERROR_AUDIT_FAILED +ERROR_AUTHENTICATION_FIREWALL_FAILED +ERROR_AUTHIP_FAILURE +ERROR_AUTODATASEG_EXCEEDS_64k +ERROR_BACKUP_CONTROLLER +ERROR_BAD_ACCESSOR_FLAGS +ERROR_BAD_ARGUMENTS +ERROR_BAD_COMMAND +ERROR_BAD_COMPRESSION_BUFFER +ERROR_BAD_CONFIGURATION +ERROR_BAD_CURRENT_DIRECTORY +ERROR_BAD_DESCRIPTOR_FORMAT +ERROR_BAD_DEV_TYPE +ERROR_BAD_DEVICE +ERROR_BAD_DEVICE_PATH +ERROR_BAD_DLL_ENTRYPOINT +ERROR_BAD_DRIVER_LEVEL +ERROR_BAD_ENVIRONMENT +ERROR_BAD_EXE_FORMAT +ERROR_BAD_FILE_TYPE +ERROR_BAD_FORMAT +ERROR_BAD_FUNCTION_TABLE +ERROR_BAD_IMPERSONATION_LEVEL +ERROR_BAD_INHERITANCE_ACL +ERROR_BAD_LENGTH +ERROR_BAD_LOGON_SESSION_STATE +ERROR_BAD_MCFG_TABLE +ERROR_BAD_NET_NAME +ERROR_BAD_NET_RESP +ERROR_BAD_NETPATH +ERROR_BAD_PATHNAME +ERROR_BAD_PIPE +ERROR_BAD_PROFILE +ERROR_BAD_PROVIDER +ERROR_BAD_QUERY_SYNTAX +ERROR_BAD_RECOVERY_POLICY +ERROR_BAD_REM_ADAP +ERROR_BAD_SERVICE_ENTRYPOINT +ERROR_BAD_STACK +ERROR_BAD_THREADID_ADDR +ERROR_BAD_TOKEN_TYPE +ERROR_BAD_UNIT +ERROR_BAD_USER_PROFILE +ERROR_BAD_USERNAME +ERROR_BAD_VALIDATION_CLASS +ERROR_BADDB +ERROR_BADKEY +ERROR_BADSTARTPOSITION +ERROR_BEGINNING_OF_MEDIA +ERROR_BEYOND_VDL +ERROR_BIOS_FAILED_TO_CONNECT_INTERRUPT +ERROR_BLOCK_SHARED +ERROR_BLOCK_SOURCE_WEAK_REFERENCE_INVALID +ERROR_BLOCK_TARGET_WEAK_REFERENCE_INVALID +ERROR_BLOCK_TOO_MANY_REFERENCES +ERROR_BLOCK_WEAK_REFERENCE_INVALID +ERROR_BLOCKED_BY_PARENTAL_CONTROLS +ERROR_BOOT_ALREADY_ACCEPTED +ERROR_BROKEN_PIPE +ERROR_BUFFER_ALL_ZEROS +ERROR_BUFFER_OVERFLOW +ERROR_BUS_RESET +ERROR_BUSY +ERROR_BUSY_DRIVE +ERROR_BYPASSIO_FLT_NOT_SUPPORTED +ERROR_CACHE_PAGE_LOCKED +ERROR_CALL_NOT_IMPLEMENTED +ERROR_CALLBACK_INVOKE_INLINE +ERROR_CALLBACK_POP_STACK +ERROR_CALLBACK_SUPPLIED_INVALID_DATA +ERROR_CAN_NOT_COMPLETE +ERROR_CANCEL_VIOLATION +ERROR_CANCELLED +ERROR_CANNOT_BREAK_OPLOCK +ERROR_CANNOT_COPY +ERROR_CANNOT_DETECT_DRIVER_FAILURE +ERROR_CANNOT_DETECT_PROCESS_ABORT +ERROR_CANNOT_FIND_WND_CLASS +ERROR_CANNOT_GRANT_REQUESTED_OPLOCK +ERROR_CANNOT_IMPERSONATE +ERROR_CANNOT_LOAD_REGISTRY_FILE +ERROR_CANNOT_MAKE +ERROR_CANNOT_OPEN_PROFILE +ERROR_CANT_ACCESS_DOMAIN_INFO +ERROR_CANT_ACCESS_FILE +ERROR_CANT_CLEAR_ENCRYPTION_FLAG +ERROR_CANT_DISABLE_MANDATORY +ERROR_CANT_ENABLE_DENY_ONLY +ERROR_CANT_OPEN_ANONYMOUS +ERROR_CANT_RESOLVE_FILENAME +ERROR_CANT_TERMINATE_SELF +ERROR_CANT_WAIT +ERROR_CANTFETCHBACKWARDS +ERROR_CANTOPEN +ERROR_CANTREAD +ERROR_CANTSCROLLBACKWARDS +ERROR_CANTWRITE +ERROR_CAPAUTHZ_CHANGE_TYPE +ERROR_CAPAUTHZ_DB_CORRUPTED +ERROR_CAPAUTHZ_NO_POLICY +ERROR_CAPAUTHZ_NOT_AUTHORIZED +ERROR_CAPAUTHZ_NOT_DEVUNLOCKED +ERROR_CAPAUTHZ_NOT_PROVISIONED +ERROR_CAPAUTHZ_SCCD_DEV_MODE_REQUIRED +ERROR_CAPAUTHZ_SCCD_INVALID_CATALOG +ERROR_CAPAUTHZ_SCCD_NO_AUTH_ENTITY +ERROR_CAPAUTHZ_SCCD_NO_CAPABILITY_MATCH +ERROR_CAPAUTHZ_SCCD_PARSE_ERROR +ERROR_CARDBUS_NOT_SUPPORTED +ERROR_CASE_DIFFERING_NAMES_IN_DIR +ERROR_CASE_SENSITIVE_PATH +ERROR_CERTIFICATE_VALIDATION_PREFERENCE_CONFLICT +ERROR_CHECKING_FILE_SYSTEM +ERROR_CHECKOUT_REQUIRED +ERROR_CHILD_MUST_BE_VOLATILE +ERROR_CHILD_NOT_COMPLETE +ERROR_CHILD_PROCESS_BLOCKED +ERROR_CHILD_WINDOW_MENU +ERROR_CIMFS_IMAGE_CORRUPT +ERROR_CIMFS_IMAGE_VERSION_NOT_SUPPORTED +ERROR_CIRCULAR_DEPENDENCY +ERROR_CLASS_ALREADY_EXISTS +ERROR_CLASS_DOES_NOT_EXIST +ERROR_CLASS_HAS_WINDOWS +ERROR_CLIENT_SERVER_PARAMETERS_INVALID +ERROR_CLIPBOARD_NOT_OPEN +ERROR_CLOUD_FILE_ACCESS_DENIED +ERROR_CLOUD_FILE_ALREADY_CONNECTED +ERROR_CLOUD_FILE_AUTHENTICATION_FAILED +ERROR_CLOUD_FILE_CONNECTED_PROVIDER_ONLY +ERROR_CLOUD_FILE_DEHYDRATION_DISALLOWED +ERROR_CLOUD_FILE_IN_USE +ERROR_CLOUD_FILE_INCOMPATIBLE_HARDLINKS +ERROR_CLOUD_FILE_INSUFFICIENT_RESOURCES +ERROR_CLOUD_FILE_INVALID_REQUEST +ERROR_CLOUD_FILE_METADATA_CORRUPT +ERROR_CLOUD_FILE_METADATA_TOO_LARGE +ERROR_CLOUD_FILE_NETWORK_UNAVAILABLE +ERROR_CLOUD_FILE_NOT_IN_SYNC +ERROR_CLOUD_FILE_NOT_SUPPORTED +ERROR_CLOUD_FILE_NOT_UNDER_SYNC_ROOT +ERROR_CLOUD_FILE_PINNED +ERROR_CLOUD_FILE_PROPERTY_BLOB_CHECKSUM_MISMATCH +ERROR_CLOUD_FILE_PROPERTY_BLOB_TOO_LARGE +ERROR_CLOUD_FILE_PROPERTY_CORRUPT +ERROR_CLOUD_FILE_PROPERTY_LOCK_CONFLICT +ERROR_CLOUD_FILE_PROPERTY_VERSION_NOT_SUPPORTED +ERROR_CLOUD_FILE_PROVIDER_NOT_RUNNING +ERROR_CLOUD_FILE_PROVIDER_TERMINATED +ERROR_CLOUD_FILE_READ_ONLY_VOLUME +ERROR_CLOUD_FILE_REQUEST_ABORTED +ERROR_CLOUD_FILE_REQUEST_CANCELED +ERROR_CLOUD_FILE_REQUEST_TIMEOUT +ERROR_CLOUD_FILE_SYNC_ROOT_METADATA_CORRUPT +ERROR_CLOUD_FILE_TOO_MANY_PROPERTY_BLOBS +ERROR_CLOUD_FILE_UNSUCCESSFUL +ERROR_CLOUD_FILE_US_MESSAGE_TIMEOUT +ERROR_CLOUD_FILE_VALIDATION_FAILED +ERROR_COMMITMENT_LIMIT +ERROR_COMMITMENT_MINIMUM +ERROR_COMPRESSED_FILE_NOT_SUPPORTED +ERROR_COMPRESSION_DISABLED +ERROR_COMPRESSION_NOT_BENEFICIAL +ERROR_CONNECTED_OTHER_PASSWORD +ERROR_CONNECTED_OTHER_PASSWORD_DEFAULT +ERROR_CONNECTION_ABORTED +ERROR_CONNECTION_ACTIVE +ERROR_CONNECTION_COUNT_LIMIT +ERROR_CONNECTION_INVALID +ERROR_CONNECTION_REFUSED +ERROR_CONNECTION_UNAVAIL +ERROR_CONTAINER_ASSIGNED +ERROR_CONTENT_BLOCKED +ERROR_CONTEXT_EXPIRED +ERROR_CONTINUE +ERROR_CONTROL_C_EXIT +ERROR_CONTROL_ID_NOT_FOUND +ERROR_CONVERT_TO_LARGE +ERROR_CORRUPT_LOG_CLEARED +ERROR_CORRUPT_LOG_CORRUPTED +ERROR_CORRUPT_LOG_DELETED_FULL +ERROR_CORRUPT_LOG_OVERFULL +ERROR_CORRUPT_LOG_UNAVAILABLE +ERROR_CORRUPT_SYSTEM_FILE +ERROR_COULD_NOT_INTERPRET +ERROR_COUNTER_TIMEOUT +ERROR_CPU_SET_INVALID +ERROR_CRASH_DUMP +ERROR_CRC +ERROR_CREATE_FAILED +ERROR_CROSS_PARTITION_VIOLATION +ERROR_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE +ERROR_CS_ENCRYPTION_FILE_NOT_CSE +ERROR_CS_ENCRYPTION_INVALID_SERVER_RESPONSE +ERROR_CS_ENCRYPTION_NEW_ENCRYPTED_FILE +ERROR_CS_ENCRYPTION_UNSUPPORTED_SERVER +ERROR_CSCSHARE_OFFLINE +ERROR_CTX_CLIENT_QUERY_TIMEOUT +ERROR_CTX_MODEM_RESPONSE_TIMEOUT +ERROR_CURRENT_DIRECTORY +ERROR_CURRENT_DOMAIN_NOT_ALLOWED +ERROR_DATA_CHECKSUM_ERROR +ERROR_DATA_NOT_ACCEPTED +ERROR_DATABASE_DOES_NOT_EXIST +ERROR_DATATYPE_MISMATCH +ERROR_DAX_MAPPING_EXISTS +ERROR_DBG_COMMAND_EXCEPTION +ERROR_DBG_CONTINUE +ERROR_DBG_CONTROL_BREAK +ERROR_DBG_CONTROL_C +ERROR_DBG_EXCEPTION_HANDLED +ERROR_DBG_EXCEPTION_NOT_HANDLED +ERROR_DBG_PRINTEXCEPTION_C +ERROR_DBG_REPLY_LATER +ERROR_DBG_RIPEXCEPTION +ERROR_DBG_TERMINATE_PROCESS +ERROR_DBG_TERMINATE_THREAD +ERROR_DBG_UNABLE_TO_PROVIDE_HANDLE +ERROR_DC_NOT_FOUND +ERROR_DDE_FAIL +ERROR_DEBUG_ATTACH_FAILED +ERROR_DEBUGGER_INACTIVE +ERROR_DECRYPTION_FAILED +ERROR_DELAY_LOAD_FAILED +ERROR_DELETE_PENDING +ERROR_DEPENDENT_SERVICES_RUNNING +ERROR_DESTINATION_ELEMENT_FULL +ERROR_DESTROY_OBJECT_OF_OTHER_THREAD +ERROR_DEV_NOT_EXIST +ERROR_DEVICE_ALREADY_ATTACHED +ERROR_DEVICE_ALREADY_REMEMBERED +ERROR_DEVICE_DOOR_OPEN +ERROR_DEVICE_ENUMERATION_ERROR +ERROR_DEVICE_FEATURE_NOT_SUPPORTED +ERROR_DEVICE_HARDWARE_ERROR +ERROR_DEVICE_HINT_NAME_BUFFER_TOO_SMALL +ERROR_DEVICE_IN_MAINTENANCE +ERROR_DEVICE_IN_USE +ERROR_DEVICE_NO_RESOURCES +ERROR_DEVICE_NOT_CONNECTED +ERROR_DEVICE_NOT_PARTITIONED +ERROR_DEVICE_REINITIALIZATION_NEEDED +ERROR_DEVICE_REMOVED +ERROR_DEVICE_REQUIRES_CLEANING +ERROR_DEVICE_RESET_REQUIRED +ERROR_DEVICE_SUPPORT_IN_PROGRESS +ERROR_DEVICE_UNREACHABLE +ERROR_DHCP_ADDRESS_CONFLICT +ERROR_DIFFERENT_SERVICE_ACCOUNT +ERROR_DIR_EFS_DISALLOWED +ERROR_DIR_NOT_EMPTY +ERROR_DIR_NOT_ROOT +ERROR_DIRECT_ACCESS_HANDLE +ERROR_DIRECTORY +ERROR_DIRECTORY_NOT_SUPPORTED +ERROR_DISCARDED +ERROR_DISK_CHANGE +ERROR_DISK_CORRUPT +ERROR_DISK_FULL +ERROR_DISK_OPERATION_FAILED +ERROR_DISK_QUOTA_EXCEEDED +ERROR_DISK_RECALIBRATE_FAILED +ERROR_DISK_REPAIR_DISABLED +ERROR_DISK_REPAIR_REDIRECTED +ERROR_DISK_REPAIR_UNSUCCESSFUL +ERROR_DISK_RESET_FAILED +ERROR_DISK_RESOURCES_EXHAUSTED +ERROR_DISK_TOO_FRAGMENTED +ERROR_DLL_INIT_FAILED +ERROR_DLL_INIT_FAILED_LOGOFF +ERROR_DLL_MIGHT_BE_INCOMPATIBLE +ERROR_DLL_MIGHT_BE_INSECURE +ERROR_DLL_NOT_FOUND +ERROR_DLP_POLICY_DENIES_OPERATION +ERROR_DLP_POLICY_SILENTLY_FAIL +ERROR_DLP_POLICY_WARNS_AGAINST_OPERATION +ERROR_DOMAIN_CONTROLLER_EXISTS +ERROR_DOMAIN_CONTROLLER_NOT_FOUND +ERROR_DOMAIN_CTRLR_CONFIG_ERROR +ERROR_DOMAIN_EXISTS +ERROR_DOMAIN_LIMIT_EXCEEDED +ERROR_DOMAIN_SID_SAME_AS_LOCAL_WORKSTATION +ERROR_DOMAIN_TRUST_INCONSISTENT +ERROR_DOWNGRADE_DETECTED +ERROR_DPL_NOT_SUPPORTED_FOR_USER +ERROR_DRIVE_LOCKED +ERROR_DRIVER_BLOCKED +ERROR_DRIVER_CANCEL_TIMEOUT +ERROR_DRIVER_DATABASE_ERROR +ERROR_DRIVER_FAILED_PRIOR_UNLOAD +ERROR_DRIVER_FAILED_SLEEP +ERROR_DRIVER_PROCESS_TERMINATED +ERROR_DRIVERS_LEAKING_LOCKED_PAGES +ERROR_DS_ADD_REPLICA_INHIBITED +ERROR_DS_ADMIN_LIMIT_EXCEEDED +ERROR_DS_AFFECTS_MULTIPLE_DSAS +ERROR_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER +ERROR_DS_ALIAS_DEREF_PROBLEM +ERROR_DS_ALIAS_POINTS_TO_ALIAS +ERROR_DS_ALIAS_PROBLEM +ERROR_DS_ALIASED_OBJ_MISSING +ERROR_DS_ATT_ALREADY_EXISTS +ERROR_DS_ATT_IS_NOT_ON_OBJ +ERROR_DS_ATT_NOT_DEF_FOR_CLASS +ERROR_DS_ATT_NOT_DEF_IN_SCHEMA +ERROR_DS_ATT_SCHEMA_REQ_ID +ERROR_DS_ATT_SCHEMA_REQ_SYNTAX +ERROR_DS_ATT_VAL_ALREADY_EXISTS +ERROR_DS_ATTRIBUTE_OR_VALUE_EXISTS +ERROR_DS_ATTRIBUTE_OWNED_BY_SAM +ERROR_DS_ATTRIBUTE_TYPE_UNDEFINED +ERROR_DS_AUDIT_FAILURE +ERROR_DS_AUTH_METHOD_NOT_SUPPORTED +ERROR_DS_AUTH_UNKNOWN +ERROR_DS_AUTHORIZATION_FAILED +ERROR_DS_AUX_CLS_TEST_FAIL +ERROR_DS_BACKLINK_WITHOUT_LINK +ERROR_DS_BAD_ATT_SCHEMA_SYNTAX +ERROR_DS_BAD_HIERARCHY_FILE +ERROR_DS_BAD_INSTANCE_TYPE +ERROR_DS_BAD_NAME_SYNTAX +ERROR_DS_BAD_RDN_ATT_ID_SYNTAX +ERROR_DS_BUILD_HIERARCHY_TABLE_FAILED +ERROR_DS_BUSY +ERROR_DS_CANT_ACCESS_REMOTE_PART_OF_AD +ERROR_DS_CANT_ADD_ATT_VALUES +ERROR_DS_CANT_ADD_SYSTEM_ONLY +ERROR_DS_CANT_ADD_TO_GC +ERROR_DS_CANT_CACHE_ATT +ERROR_DS_CANT_CACHE_CLASS +ERROR_DS_CANT_CREATE_IN_NONDOMAIN_NC +ERROR_DS_CANT_CREATE_UNDER_SCHEMA +ERROR_DS_CANT_DEL_MASTER_CROSSREF +ERROR_DS_CANT_DELETE +ERROR_DS_CANT_DELETE_DSA_OBJ +ERROR_DS_CANT_DEMOTE_WITH_WRITEABLE_NC +ERROR_DS_CANT_DEREF_ALIAS +ERROR_DS_CANT_DERIVE_SPN_FOR_DELETED_DOMAIN +ERROR_DS_CANT_DERIVE_SPN_WITHOUT_SERVER_REF +ERROR_DS_CANT_FIND_DC_FOR_SRC_DOMAIN +ERROR_DS_CANT_FIND_DSA_OBJ +ERROR_DS_CANT_FIND_EXPECTED_NC +ERROR_DS_CANT_FIND_NC_IN_CACHE +ERROR_DS_CANT_MIX_MASTER_AND_REPS +ERROR_DS_CANT_MOD_OBJ_CLASS +ERROR_DS_CANT_MOD_PRIMARYGROUPID +ERROR_DS_CANT_MOD_SYSTEM_ONLY +ERROR_DS_CANT_MOVE_ACCOUNT_GROUP +ERROR_DS_CANT_MOVE_APP_BASIC_GROUP +ERROR_DS_CANT_MOVE_APP_QUERY_GROUP +ERROR_DS_CANT_MOVE_DELETED_OBJECT +ERROR_DS_CANT_MOVE_RESOURCE_GROUP +ERROR_DS_CANT_ON_NON_LEAF +ERROR_DS_CANT_ON_RDN +ERROR_DS_CANT_REM_MISSING_ATT +ERROR_DS_CANT_REM_MISSING_ATT_VAL +ERROR_DS_CANT_REMOVE_ATT_CACHE +ERROR_DS_CANT_REMOVE_CLASS_CACHE +ERROR_DS_CANT_REPLACE_HIDDEN_REC +ERROR_DS_CANT_RETRIEVE_ATTS +ERROR_DS_CANT_RETRIEVE_CHILD +ERROR_DS_CANT_RETRIEVE_DN +ERROR_DS_CANT_RETRIEVE_INSTANCE +ERROR_DS_CANT_RETRIEVE_SD +ERROR_DS_CANT_START +ERROR_DS_CANT_TREE_DELETE_CRITICAL_OBJ +ERROR_DS_CANT_WITH_ACCT_GROUP_MEMBERSHPS +ERROR_DS_CHILDREN_EXIST +ERROR_DS_CLASS_MUST_BE_CONCRETE +ERROR_DS_CLASS_NOT_DSA +ERROR_DS_CLIENT_LOOP +ERROR_DS_CODE_INCONSISTENCY +ERROR_DS_COMPARE_FALSE +ERROR_DS_COMPARE_TRUE +ERROR_DS_CONFIDENTIALITY_REQUIRED +ERROR_DS_CONFIG_PARAM_MISSING +ERROR_DS_CONSTRAINT_VIOLATION +ERROR_DS_CONSTRUCTED_ATT_MOD +ERROR_DS_CONTROL_NOT_FOUND +ERROR_DS_COULDNT_CONTACT_FSMO +ERROR_DS_COULDNT_IDENTIFY_OBJECTS_FOR_TREE_DELETE +ERROR_DS_COULDNT_LOCK_TREE_FOR_DELETE +ERROR_DS_COULDNT_UPDATE_SPNS +ERROR_DS_COUNTING_AB_INDICES_FAILED +ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE +ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE_V2 +ERROR_DS_CROSS_DOM_MOVE_ERROR +ERROR_DS_CROSS_DOMAIN_CLEANUP_REQD +ERROR_DS_CROSS_NC_DN_RENAME +ERROR_DS_CROSS_REF_BUSY +ERROR_DS_CROSS_REF_EXISTS +ERROR_DS_DATABASE_ERROR +ERROR_DS_DECODING_ERROR +ERROR_DS_DESTINATION_AUDITING_NOT_ENABLED +ERROR_DS_DESTINATION_DOMAIN_NOT_IN_FOREST +ERROR_DS_DIFFERENT_REPL_EPOCHS +ERROR_DS_DISALLOWED_IN_SYSTEM_CONTAINER +ERROR_DS_DISALLOWED_NC_REDIRECT +ERROR_DS_DNS_LOOKUP_FAILURE +ERROR_DS_DOMAIN_NAME_EXISTS_IN_FOREST +ERROR_DS_DOMAIN_RENAME_IN_PROGRESS +ERROR_DS_DOMAIN_VERSION_TOO_HIGH +ERROR_DS_DOMAIN_VERSION_TOO_LOW +ERROR_DS_DRA_ABANDON_SYNC +ERROR_DS_DRA_ACCESS_DENIED +ERROR_DS_DRA_BAD_DN +ERROR_DS_DRA_BAD_INSTANCE_TYPE +ERROR_DS_DRA_BAD_NC +ERROR_DS_DRA_BUSY +ERROR_DS_DRA_CONNECTION_FAILED +ERROR_DS_DRA_CORRUPT_UTD_VECTOR +ERROR_DS_DRA_DB_ERROR +ERROR_DS_DRA_DN_EXISTS +ERROR_DS_DRA_EARLIER_SCHEMA_CONFLICT +ERROR_DS_DRA_EXTN_CONNECTION_FAILED +ERROR_DS_DRA_GENERIC +ERROR_DS_DRA_INCOMPATIBLE_PARTIAL_SET +ERROR_DS_DRA_INCONSISTENT_DIT +ERROR_DS_DRA_INTERNAL_ERROR +ERROR_DS_DRA_INVALID_PARAMETER +ERROR_DS_DRA_MAIL_PROBLEM +ERROR_DS_DRA_MISSING_KRBTGT_SECRET +ERROR_DS_DRA_MISSING_PARENT +ERROR_DS_DRA_NAME_COLLISION +ERROR_DS_DRA_NO_REPLICA +ERROR_DS_DRA_NOT_SUPPORTED +ERROR_DS_DRA_OBJ_IS_REP_SOURCE +ERROR_DS_DRA_OBJ_NC_MISMATCH +ERROR_DS_DRA_OUT_OF_MEM +ERROR_DS_DRA_OUT_SCHEDULE_WINDOW +ERROR_DS_DRA_PREEMPTED +ERROR_DS_DRA_RECYCLED_TARGET +ERROR_DS_DRA_REF_ALREADY_EXISTS +ERROR_DS_DRA_REF_NOT_FOUND +ERROR_DS_DRA_REPL_PENDING +ERROR_DS_DRA_RPC_CANCELLED +ERROR_DS_DRA_SCHEMA_CONFLICT +ERROR_DS_DRA_SCHEMA_INFO_SHIP +ERROR_DS_DRA_SCHEMA_MISMATCH +ERROR_DS_DRA_SECRETS_DENIED +ERROR_DS_DRA_SHUTDOWN +ERROR_DS_DRA_SINK_DISABLED +ERROR_DS_DRA_SOURCE_DISABLED +ERROR_DS_DRA_SOURCE_IS_PARTIAL_REPLICA +ERROR_DS_DRA_SOURCE_REINSTALLED +ERROR_DS_DRS_EXTENSIONS_CHANGED +ERROR_DS_DS_REQUIRED +ERROR_DS_DSA_MUST_BE_INT_MASTER +ERROR_DS_DST_DOMAIN_NOT_NATIVE +ERROR_DS_DST_NC_MISMATCH +ERROR_DS_DUP_LDAP_DISPLAY_NAME +ERROR_DS_DUP_LINK_ID +ERROR_DS_DUP_MAPI_ID +ERROR_DS_DUP_MSDS_INTID +ERROR_DS_DUP_OID +ERROR_DS_DUP_RDN +ERROR_DS_DUP_SCHEMA_ID_GUID +ERROR_DS_DUPLICATE_ID_FOUND +ERROR_DS_ENCODING_ERROR +ERROR_DS_EPOCH_MISMATCH +ERROR_DS_EXISTING_AD_CHILD_NC +ERROR_DS_EXISTS_IN_AUX_CLS +ERROR_DS_EXISTS_IN_MAY_HAVE +ERROR_DS_EXISTS_IN_MUST_HAVE +ERROR_DS_EXISTS_IN_POSS_SUP +ERROR_DS_EXISTS_IN_RDNATTID +ERROR_DS_EXISTS_IN_SUB_CLS +ERROR_DS_FILTER_UNKNOWN +ERROR_DS_FILTER_USES_CONTRUCTED_ATTRS +ERROR_DS_FLAT_NAME_EXISTS_IN_FOREST +ERROR_DS_FOREST_VERSION_TOO_HIGH +ERROR_DS_FOREST_VERSION_TOO_LOW +ERROR_DS_GC_NOT_AVAILABLE +ERROR_DS_GC_REQUIRED +ERROR_DS_GCVERIFY_ERROR +ERROR_DS_GENERIC_ERROR +ERROR_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER +ERROR_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER +ERROR_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER +ERROR_DS_GOVERNSID_MISSING +ERROR_DS_GROUP_CONVERSION_ERROR +ERROR_DS_HAVE_PRIMARY_MEMBERS +ERROR_DS_HIERARCHY_TABLE_MALLOC_FAILED +ERROR_DS_HIERARCHY_TABLE_TOO_DEEP +ERROR_DS_HIGH_ADLDS_FFL +ERROR_DS_HIGH_DSA_VERSION +ERROR_DS_ILLEGAL_BASE_SCHEMA_MOD +ERROR_DS_ILLEGAL_MOD_OPERATION +ERROR_DS_ILLEGAL_SUPERIOR +ERROR_DS_ILLEGAL_XDOM_MOVE_OPERATION +ERROR_DS_INAPPROPRIATE_AUTH +ERROR_DS_INAPPROPRIATE_MATCHING +ERROR_DS_INCOMPATIBLE_CONTROLS_USED +ERROR_DS_INCOMPATIBLE_VERSION +ERROR_DS_INCORRECT_ROLE_OWNER +ERROR_DS_INIT_FAILURE +ERROR_DS_INIT_FAILURE_CONSOLE +ERROR_DS_INSTALL_NO_SCH_VERSION_IN_INIFILE +ERROR_DS_INSTALL_NO_SRC_SCH_VERSION +ERROR_DS_INSTALL_SCHEMA_MISMATCH +ERROR_DS_INSUFF_ACCESS_RIGHTS +ERROR_DS_INSUFFICIENT_ATTR_TO_CREATE_OBJECT +ERROR_DS_INTERNAL_FAILURE +ERROR_DS_INVALID_ATTRIBUTE_SYNTAX +ERROR_DS_INVALID_DMD +ERROR_DS_INVALID_DN_SYNTAX +ERROR_DS_INVALID_GROUP_TYPE +ERROR_DS_INVALID_LDAP_DISPLAY_NAME +ERROR_DS_INVALID_NAME_FOR_SPN +ERROR_DS_INVALID_ROLE_OWNER +ERROR_DS_INVALID_SCRIPT +ERROR_DS_INVALID_SEARCH_FLAG +ERROR_DS_INVALID_SEARCH_FLAG_SUBTREE +ERROR_DS_INVALID_SEARCH_FLAG_TUPLE +ERROR_DS_IS_LEAF +ERROR_DS_KEY_NOT_UNIQUE +ERROR_DS_LDAP_SEND_QUEUE_FULL +ERROR_DS_LINK_ID_NOT_AVAILABLE +ERROR_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER +ERROR_DS_LOCAL_ERROR +ERROR_DS_LOCAL_MEMBER_OF_LOCAL_ONLY +ERROR_DS_LOOP_DETECT +ERROR_DS_LOW_ADLDS_FFL +ERROR_DS_LOW_DSA_VERSION +ERROR_DS_MACHINE_ACCOUNT_CREATED_PRENT4 +ERROR_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED +ERROR_DS_MAPI_ID_NOT_AVAILABLE +ERROR_DS_MASTERDSA_REQUIRED +ERROR_DS_MAX_OBJ_SIZE_EXCEEDED +ERROR_DS_MEMBERSHIP_EVALUATED_LOCALLY +ERROR_DS_MISSING_EXPECTED_ATT +ERROR_DS_MISSING_FOREST_TRUST +ERROR_DS_MISSING_FSMO_SETTINGS +ERROR_DS_MISSING_INFRASTRUCTURE_CONTAINER +ERROR_DS_MISSING_REQUIRED_ATT +ERROR_DS_MISSING_SUPREF +ERROR_DS_MODIFYDN_DISALLOWED_BY_FLAG +ERROR_DS_MODIFYDN_DISALLOWED_BY_INSTANCE_TYPE +ERROR_DS_MODIFYDN_WRONG_GRANDPARENT +ERROR_DS_MUST_BE_RUN_ON_DST_DC +ERROR_DS_NAME_ERROR_DOMAIN_ONLY +ERROR_DS_NAME_ERROR_NO_MAPPING +ERROR_DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING +ERROR_DS_NAME_ERROR_NOT_FOUND +ERROR_DS_NAME_ERROR_NOT_UNIQUE +ERROR_DS_NAME_ERROR_RESOLVING +ERROR_DS_NAME_ERROR_TRUST_REFERRAL +ERROR_DS_NAME_NOT_UNIQUE +ERROR_DS_NAME_REFERENCE_INVALID +ERROR_DS_NAME_TOO_LONG +ERROR_DS_NAME_TOO_MANY_PARTS +ERROR_DS_NAME_TYPE_UNKNOWN +ERROR_DS_NAME_UNPARSEABLE +ERROR_DS_NAME_VALUE_TOO_LONG +ERROR_DS_NAMING_MASTER_GC +ERROR_DS_NAMING_VIOLATION +ERROR_DS_NC_MUST_HAVE_NC_PARENT +ERROR_DS_NC_STILL_HAS_DSAS +ERROR_DS_NCNAME_MISSING_CR_REF +ERROR_DS_NCNAME_MUST_BE_NC +ERROR_DS_NO_ATTRIBUTE_OR_VALUE +ERROR_DS_NO_BEHAVIOR_VERSION_IN_MIXEDDOMAIN +ERROR_DS_NO_CHAINED_EVAL +ERROR_DS_NO_CHAINING +ERROR_DS_NO_CHECKPOINT_WITH_PDC +ERROR_DS_NO_CROSSREF_FOR_NC +ERROR_DS_NO_DELETED_NAME +ERROR_DS_NO_FPO_IN_UNIVERSAL_GROUPS +ERROR_DS_NO_MORE_RIDS +ERROR_DS_NO_MSDS_INTID +ERROR_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN +ERROR_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN +ERROR_DS_NO_NTDSA_OBJECT +ERROR_DS_NO_OBJECT_MOVE_IN_SCHEMA_NC +ERROR_DS_NO_PARENT_OBJECT +ERROR_DS_NO_PKT_PRIVACY_ON_CONNECTION +ERROR_DS_NO_RDN_DEFINED_IN_SCHEMA +ERROR_DS_NO_REF_DOMAIN +ERROR_DS_NO_REQUESTED_ATTS_FOUND +ERROR_DS_NO_RESULTS_RETURNED +ERROR_DS_NO_RIDS_ALLOCATED +ERROR_DS_NO_SERVER_OBJECT +ERROR_DS_NO_SUCH_OBJECT +ERROR_DS_NO_TREE_DELETE_ABOVE_NC +ERROR_DS_NON_ASQ_SEARCH +ERROR_DS_NON_BASE_SEARCH +ERROR_DS_NONEXISTENT_MAY_HAVE +ERROR_DS_NONEXISTENT_MUST_HAVE +ERROR_DS_NONEXISTENT_POSS_SUP +ERROR_DS_NONSAFE_SCHEMA_CHANGE +ERROR_DS_NOT_AN_OBJECT +ERROR_DS_NOT_AUTHORITIVE_FOR_DST_NC +ERROR_DS_NOT_CLOSEST +ERROR_DS_NOT_INSTALLED +ERROR_DS_NOT_ON_BACKLINK +ERROR_DS_NOT_SUPPORTED +ERROR_DS_NOT_SUPPORTED_SORT_ORDER +ERROR_DS_NOTIFY_FILTER_TOO_COMPLEX +ERROR_DS_NTDSCRIPT_PROCESS_ERROR +ERROR_DS_NTDSCRIPT_SYNTAX_ERROR +ERROR_DS_OBJ_CLASS_NOT_DEFINED +ERROR_DS_OBJ_CLASS_NOT_SUBCLASS +ERROR_DS_OBJ_CLASS_VIOLATION +ERROR_DS_OBJ_GUID_EXISTS +ERROR_DS_OBJ_NOT_FOUND +ERROR_DS_OBJ_STRING_NAME_EXISTS +ERROR_DS_OBJ_TOO_LARGE +ERROR_DS_OBJECT_BEING_REMOVED +ERROR_DS_OBJECT_CLASS_REQUIRED +ERROR_DS_OBJECT_RESULTS_TOO_LARGE +ERROR_DS_OFFSET_RANGE_ERROR +ERROR_DS_OID_MAPPED_GROUP_CANT_HAVE_MEMBERS +ERROR_DS_OID_NOT_FOUND +ERROR_DS_OPERATIONS_ERROR +ERROR_DS_OUT_OF_SCOPE +ERROR_DS_OUT_OF_VERSION_STORE +ERROR_DS_PARAM_ERROR +ERROR_DS_PARENT_IS_AN_ALIAS +ERROR_DS_PDC_OPERATION_IN_PROGRESS +ERROR_DS_PER_ATTRIBUTE_AUTHZ_FAILED_DURING_ADD +ERROR_DS_POLICY_NOT_KNOWN +ERROR_DS_PROTOCOL_ERROR +ERROR_DS_RANGE_CONSTRAINT +ERROR_DS_RDN_DOESNT_MATCH_SCHEMA +ERROR_DS_RECALCSCHEMA_FAILED +ERROR_DS_REFERRAL +ERROR_DS_REFERRAL_LIMIT_EXCEEDED +ERROR_DS_REFUSING_FSMO_ROLES +ERROR_DS_REMOTE_CROSSREF_OP_FAILED +ERROR_DS_REPL_LIFETIME_EXCEEDED +ERROR_DS_REPLICA_SET_CHANGE_NOT_ALLOWED_ON_DISABLED_CR +ERROR_DS_REPLICATOR_ONLY +ERROR_DS_RESERVED_LINK_ID +ERROR_DS_RESERVED_MAPI_ID +ERROR_DS_RIDMGR_DISABLED +ERROR_DS_RIDMGR_INIT_ERROR +ERROR_DS_ROLE_NOT_VERIFIED +ERROR_DS_ROOT_CANT_BE_SUBREF +ERROR_DS_ROOT_MUST_BE_NC +ERROR_DS_ROOT_REQUIRES_CLASS_TOP +ERROR_DS_SAM_INIT_FAILURE +ERROR_DS_SAM_INIT_FAILURE_CONSOLE +ERROR_DS_SAM_NEED_BOOTKEY_FLOPPY +ERROR_DS_SAM_NEED_BOOTKEY_PASSWORD +ERROR_DS_SCHEMA_ALLOC_FAILED +ERROR_DS_SCHEMA_NOT_LOADED +ERROR_DS_SCHEMA_UPDATE_DISALLOWED +ERROR_DS_SEC_DESC_INVALID +ERROR_DS_SEC_DESC_TOO_SHORT +ERROR_DS_SECURITY_CHECKING_ERROR +ERROR_DS_SECURITY_ILLEGAL_MODIFY +ERROR_DS_SEMANTIC_ATT_TEST +ERROR_DS_SENSITIVE_GROUP_VIOLATION +ERROR_DS_SERVER_DOWN +ERROR_DS_SHUTTING_DOWN +ERROR_DS_SINGLE_USER_MODE_FAILED +ERROR_DS_SINGLE_VALUE_CONSTRAINT +ERROR_DS_SIZELIMIT_EXCEEDED +ERROR_DS_SORT_CONTROL_MISSING +ERROR_DS_SOURCE_AUDITING_NOT_ENABLED +ERROR_DS_SOURCE_DOMAIN_IN_FOREST +ERROR_DS_SPN_VALUE_NOT_UNIQUE_IN_FOREST +ERROR_DS_SRC_AND_DST_NC_IDENTICAL +ERROR_DS_SRC_AND_DST_OBJECT_CLASS_MISMATCH +ERROR_DS_SRC_DC_MUST_BE_SP4_OR_GREATER +ERROR_DS_SRC_GUID_MISMATCH +ERROR_DS_SRC_NAME_MISMATCH +ERROR_DS_SRC_OBJ_NOT_GROUP_OR_USER +ERROR_DS_SRC_SID_EXISTS_IN_FOREST +ERROR_DS_STRING_SD_CONVERSION_FAILED +ERROR_DS_STRONG_AUTH_REQUIRED +ERROR_DS_SUB_CLS_TEST_FAIL +ERROR_DS_SUBREF_MUST_HAVE_PARENT +ERROR_DS_SUBTREE_NOTIFY_NOT_NC_HEAD +ERROR_DS_SYNTAX_MISMATCH +ERROR_DS_THREAD_LIMIT_EXCEEDED +ERROR_DS_TIMELIMIT_EXCEEDED +ERROR_DS_TREE_DELETE_NOT_FINISHED +ERROR_DS_UNABLE_TO_SURRENDER_ROLES +ERROR_DS_UNAVAILABLE +ERROR_DS_UNAVAILABLE_CRIT_EXTENSION +ERROR_DS_UNDELETE_SAM_VALIDATION_FAILED +ERROR_DS_UNICODEPWD_NOT_IN_QUOTES +ERROR_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER +ERROR_DS_UNKNOWN_ERROR +ERROR_DS_UNKNOWN_OPERATION +ERROR_DS_UNWILLING_TO_PERFORM +ERROR_DS_UPN_VALUE_NOT_UNIQUE_IN_FOREST +ERROR_DS_USER_BUFFER_TO_SMALL +ERROR_DS_VALUE_KEY_NOT_UNIQUE +ERROR_DS_VERSION_CHECK_FAILURE +ERROR_DS_WKO_CONTAINER_CANNOT_BE_SPECIAL +ERROR_DS_WRONG_LINKED_ATT_SYNTAX +ERROR_DS_WRONG_OM_OBJ_CLASS +ERROR_DUP_DOMAINNAME +ERROR_DUP_NAME +ERROR_DUPLICATE_PRIVILEGES +ERROR_DUPLICATE_SERVICE_NAME +ERROR_DYNAMIC_CODE_BLOCKED +ERROR_DYNLINK_FROM_INVALID_RING +ERROR_EA_ACCESS_DENIED +ERROR_EA_FILE_CORRUPT +ERROR_EA_LIST_INCONSISTENT +ERROR_EA_TABLE_FULL +ERROR_EAS_DIDNT_FIT +ERROR_EAS_NOT_SUPPORTED +ERROR_EDP_DPL_POLICY_CANT_BE_SATISFIED +ERROR_EDP_POLICY_DENIES_OPERATION +ERROR_EFS_ALG_BLOB_TOO_BIG +ERROR_EFS_DISABLED +ERROR_EFS_SERVER_NOT_TRUSTED +ERROR_EFS_VERSION_NOT_SUPPORT +ERROR_ELEVATION_REQUIRED +ERROR_ENCLAVE_FAILURE +ERROR_ENCLAVE_NOT_TERMINATED +ERROR_ENCLAVE_VIOLATION +ERROR_ENCRYPTED_FILE_NOT_SUPPORTED +ERROR_ENCRYPTED_IO_NOT_POSSIBLE +ERROR_ENCRYPTING_METADATA_DISALLOWED +ERROR_ENCRYPTION_DISABLED +ERROR_ENCRYPTION_FAILED +ERROR_ENCRYPTION_POLICY_DENIES_OPERATION +ERROR_END_OF_MEDIA +ERROR_ENVVAR_NOT_FOUND +ERROR_EOM_OVERFLOW +ERROR_ERRORS_ENCOUNTERED +ERROR_EVALUATION_EXPIRATION +ERROR_EVENT_DONE +ERROR_EVENT_PENDING +ERROR_EVENTLOG_CANT_START +ERROR_EVENTLOG_FILE_CHANGED +ERROR_EVENTLOG_FILE_CORRUPT +ERROR_EXCEPTION_IN_SERVICE +ERROR_EXCL_SEM_ALREADY_OWNED +ERROR_EXE_CANNOT_MODIFY_SIGNED_BINARY +ERROR_EXE_CANNOT_MODIFY_STRONG_SIGNED_BINARY +ERROR_EXE_MACHINE_TYPE_MISMATCH +ERROR_EXE_MARKED_INVALID +ERROR_EXTENDED_ERROR +ERROR_EXTERNAL_BACKING_PROVIDER_UNKNOWN +ERROR_EXTERNAL_SYSKEY_NOT_SUPPORTED +ERROR_EXTRANEOUS_INFORMATION +ERROR_FAIL_FAST_EXCEPTION +ERROR_FAIL_I24 +ERROR_FAIL_NOACTION_REBOOT +ERROR_FAIL_RESTART +ERROR_FAIL_SHUTDOWN +ERROR_FAILED_DRIVER_ENTRY +ERROR_FAILED_SERVICE_CONTROLLER_CONNECT +ERROR_FATAL_APP_EXIT +ERROR_FILE_CHECKED_OUT +ERROR_FILE_CORRUPT +ERROR_FILE_ENCRYPTED +ERROR_FILE_EXISTS +ERROR_FILE_HANDLE_REVOKED +ERROR_FILE_INVALID +ERROR_FILE_LEVEL_TRIM_NOT_SUPPORTED +ERROR_FILE_METADATA_OPTIMIZATION_IN_PROGRESS +ERROR_FILE_NOT_ENCRYPTED +ERROR_FILE_NOT_FOUND +ERROR_FILE_NOT_SUPPORTED +ERROR_FILE_OFFLINE +ERROR_FILE_PROTECTED_UNDER_DPL +ERROR_FILE_READ_ONLY +ERROR_FILE_SNAP_IN_PROGRESS +ERROR_FILE_SNAP_INVALID_PARAMETER +ERROR_FILE_SNAP_IO_NOT_COORDINATED +ERROR_FILE_SNAP_MODIFY_NOT_SUPPORTED +ERROR_FILE_SNAP_UNEXPECTED_ERROR +ERROR_FILE_SNAP_USER_SECTION_NOT_SUPPORTED +ERROR_FILE_SYSTEM_LIMITATION +ERROR_FILE_SYSTEM_VIRTUALIZATION_BUSY +ERROR_FILE_SYSTEM_VIRTUALIZATION_INVALID_OPERATION +ERROR_FILE_SYSTEM_VIRTUALIZATION_METADATA_CORRUPT +ERROR_FILE_SYSTEM_VIRTUALIZATION_PROVIDER_UNKNOWN +ERROR_FILE_SYSTEM_VIRTUALIZATION_UNAVAILABLE +ERROR_FILE_TOO_LARGE +ERROR_FILEMARK_DETECTED +ERROR_FILENAME_EXCED_RANGE +ERROR_FIRMWARE_UPDATED +ERROR_FLOAT_MULTIPLE_FAULTS +ERROR_FLOAT_MULTIPLE_TRAPS +ERROR_FLOPPY_BAD_REGISTERS +ERROR_FLOPPY_ID_MARK_NOT_FOUND +ERROR_FLOPPY_UNKNOWN_ERROR +ERROR_FLOPPY_VOLUME +ERROR_FLOPPY_WRONG_CYLINDER +ERROR_FORMS_AUTH_REQUIRED +ERROR_FOUND_OUT_OF_SCOPE +ERROR_FS_DRIVER_REQUIRED +ERROR_FS_METADATA_INCONSISTENT +ERROR_FSFILTER_OP_COMPLETED_SUCCESSFULLY +ERROR_FT_DI_SCAN_REQUIRED +ERROR_FT_READ_FAILURE +ERROR_FT_READ_FROM_COPY_FAILURE +ERROR_FT_READ_RECOVERY_FROM_BACKUP +ERROR_FT_WRITE_FAILURE +ERROR_FT_WRITE_RECOVERY +ERROR_FULLSCREEN_MODE +ERROR_FUNCTION_FAILED +ERROR_FUNCTION_NOT_CALLED +ERROR_GDI_HANDLE_LEAK +ERROR_GEN_FAILURE +ERROR_GENERIC_NOT_MAPPED +ERROR_GLOBAL_ONLY_HOOK +ERROR_GRACEFUL_DISCONNECT +ERROR_GROUP_EXISTS +ERROR_GUID_SUBSTITUTION_MADE +ERROR_HANDLE_DISK_FULL +ERROR_HANDLE_EOF +ERROR_HANDLE_REVOKED +ERROR_HANDLES_CLOSED +ERROR_HAS_SYSTEM_CRITICAL_FILES +ERROR_HIBERNATED +ERROR_HIBERNATION_FAILURE +ERROR_HOOK_NEEDS_HMOD +ERROR_HOOK_NOT_INSTALLED +ERROR_HOOK_TYPE_NOT_ALLOWED +ERROR_HOST_DOWN +ERROR_HOST_UNREACHABLE +ERROR_HOTKEY_ALREADY_REGISTERED +ERROR_HOTKEY_NOT_REGISTERED +ERROR_HWNDS_HAVE_DIFF_PARENT +ERROR_ILL_FORMED_PASSWORD +ERROR_ILLEGAL_CHARACTER +ERROR_ILLEGAL_DLL_RELOCATION +ERROR_ILLEGAL_ELEMENT_ADDRESS +ERROR_ILLEGAL_FLOAT_CONTEXT +ERROR_IMAGE_AT_DIFFERENT_BASE +ERROR_IMAGE_MACHINE_TYPE_MISMATCH +ERROR_IMAGE_MACHINE_TYPE_MISMATCH_EXE +ERROR_IMAGE_NOT_AT_BASE +ERROR_IMAGE_SUBSYSTEM_NOT_PRESENT +ERROR_IMPLEMENTATION_LIMIT +ERROR_INCOMPATIBLE_SERVICE_PRIVILEGE +ERROR_INCOMPATIBLE_SERVICE_SID_TYPE +ERROR_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING +ERROR_INCORRECT_ACCOUNT_TYPE +ERROR_INCORRECT_ADDRESS +ERROR_INCORRECT_SIZE +ERROR_INDEX_ABSENT +ERROR_INDEX_OUT_OF_BOUNDS +ERROR_INFLOOP_IN_RELOC_CHAIN +ERROR_INSTALL_ALREADY_RUNNING +ERROR_INSTALL_FAILURE +ERROR_INSTALL_LANGUAGE_UNSUPPORTED +ERROR_INSTALL_LOG_FAILURE +ERROR_INSTALL_NOTUSED +ERROR_INSTALL_PACKAGE_INVALID +ERROR_INSTALL_PACKAGE_OPEN_FAILED +ERROR_INSTALL_PACKAGE_REJECTED +ERROR_INSTALL_PACKAGE_VERSION +ERROR_INSTALL_PLATFORM_UNSUPPORTED +ERROR_INSTALL_REJECTED +ERROR_INSTALL_REMOTE_DISALLOWED +ERROR_INSTALL_REMOTE_PROHIBITED +ERROR_INSTALL_SERVICE_FAILURE +ERROR_INSTALL_SERVICE_SAFEBOOT +ERROR_INSTALL_SOURCE_ABSENT +ERROR_INSTALL_SUSPEND +ERROR_INSTALL_TEMP_UNWRITABLE +ERROR_INSTALL_TRANSFORM_FAILURE +ERROR_INSTALL_TRANSFORM_REJECTED +ERROR_INSTALL_UI_FAILURE +ERROR_INSTALL_USEREXIT +ERROR_INSTRUCTION_MISALIGNMENT +ERROR_INSUFFICIENT_BUFFER +ERROR_INSUFFICIENT_LOGON_INFO +ERROR_INSUFFICIENT_POWER +ERROR_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE +ERROR_INSUFFICIENT_VIRTUAL_ADDR_RESOURCES +ERROR_INTERMIXED_KERNEL_EA_OPERATION +ERROR_INTERNAL_DB_CORRUPTION +ERROR_INTERNAL_DB_ERROR +ERROR_INTERNAL_ERROR +ERROR_INTERRUPT_STILL_CONNECTED +ERROR_INTERRUPT_VECTOR_ALREADY_CONNECTED +ERROR_INVALID_ACCEL_HANDLE +ERROR_INVALID_ACCESS +ERROR_INVALID_ACCOUNT_NAME +ERROR_INVALID_ACE_CONDITION +ERROR_INVALID_ACL +ERROR_INVALID_ADDRESS +ERROR_INVALID_AT_INTERRUPT_TIME +ERROR_INVALID_BLOCK +ERROR_INVALID_BLOCK_LENGTH +ERROR_INVALID_CAP +ERROR_INVALID_CATEGORY +ERROR_INVALID_COMBOBOX_MESSAGE +ERROR_INVALID_COMMAND_LINE +ERROR_INVALID_COMPUTERNAME +ERROR_INVALID_CRUNTIME_PARAMETER +ERROR_INVALID_CURSOR_HANDLE +ERROR_INVALID_DATA +ERROR_INVALID_DATATYPE +ERROR_INVALID_DEVICE_OBJECT_PARAMETER +ERROR_INVALID_DLL +ERROR_INVALID_DOMAIN_ROLE +ERROR_INVALID_DOMAIN_STATE +ERROR_INVALID_DOMAINNAME +ERROR_INVALID_DRIVE +ERROR_INVALID_DWP_HANDLE +ERROR_INVALID_EA_HANDLE +ERROR_INVALID_EA_NAME +ERROR_INVALID_EDIT_HEIGHT +ERROR_INVALID_ENVIRONMENT +ERROR_INVALID_EVENT_COUNT +ERROR_INVALID_EVENTNAME +ERROR_INVALID_EXCEPTION_HANDLER +ERROR_INVALID_EXE_SIGNATURE +ERROR_INVALID_FIELD +ERROR_INVALID_FIELD_IN_PARAMETER_LIST +ERROR_INVALID_FILTER_PROC +ERROR_INVALID_FLAG_NUMBER +ERROR_INVALID_FLAGS +ERROR_INVALID_FORM_NAME +ERROR_INVALID_FORM_SIZE +ERROR_INVALID_FUNCTION +ERROR_INVALID_GROUP_ATTRIBUTES +ERROR_INVALID_GROUPNAME +ERROR_INVALID_GW_COMMAND +ERROR_INVALID_HANDLE +ERROR_INVALID_HANDLE_STATE +ERROR_INVALID_HOOK_FILTER +ERROR_INVALID_HOOK_HANDLE +ERROR_INVALID_HW_PROFILE +ERROR_INVALID_ICON_HANDLE +ERROR_INVALID_ID_AUTHORITY +ERROR_INVALID_IMAGE_HASH +ERROR_INVALID_IMPORT_OF_NON_DLL +ERROR_INVALID_INDEX +ERROR_INVALID_KERNEL_INFO_VERSION +ERROR_INVALID_KEYBOARD_HANDLE +ERROR_INVALID_LABEL +ERROR_INVALID_LB_MESSAGE +ERROR_INVALID_LDT_DESCRIPTOR +ERROR_INVALID_LDT_OFFSET +ERROR_INVALID_LDT_SIZE +ERROR_INVALID_LEVEL +ERROR_INVALID_LIST_FORMAT +ERROR_INVALID_LOCK_RANGE +ERROR_INVALID_LOGON_HOURS +ERROR_INVALID_LOGON_TYPE +ERROR_INVALID_MEMBER +ERROR_INVALID_MENU_HANDLE +ERROR_INVALID_MESSAGE +ERROR_INVALID_MESSAGEDEST +ERROR_INVALID_MESSAGENAME +ERROR_INVALID_MINALLOCSIZE +ERROR_INVALID_MODULETYPE +ERROR_INVALID_MONITOR_HANDLE +ERROR_INVALID_MSGBOX_STYLE +ERROR_INVALID_NAME +ERROR_INVALID_NETNAME +ERROR_INVALID_OPLOCK_PROTOCOL +ERROR_INVALID_ORDINAL +ERROR_INVALID_OWNER +ERROR_INVALID_PACKAGE_SID_LENGTH +ERROR_INVALID_PARAMETER +ERROR_INVALID_PASSWORD +ERROR_INVALID_PASSWORDNAME +ERROR_INVALID_PATCH_XML +ERROR_INVALID_PEP_INFO_VERSION +ERROR_INVALID_PLUGPLAY_DEVICE_PATH +ERROR_INVALID_PORT_ATTRIBUTES +ERROR_INVALID_PRIMARY_GROUP +ERROR_INVALID_PRINTER_COMMAND +ERROR_INVALID_PRINTER_NAME +ERROR_INVALID_PRINTER_STATE +ERROR_INVALID_PRIORITY +ERROR_INVALID_QUOTA_LOWER +ERROR_INVALID_REPARSE_DATA +ERROR_INVALID_SCROLLBAR_RANGE +ERROR_INVALID_SECURITY_DESCR +ERROR_INVALID_SEGDPL +ERROR_INVALID_SEGMENT_NUMBER +ERROR_INVALID_SEPARATOR_FILE +ERROR_INVALID_SERVER_STATE +ERROR_INVALID_SERVICE_ACCOUNT +ERROR_INVALID_SERVICE_CONTROL +ERROR_INVALID_SERVICE_LOCK +ERROR_INVALID_SERVICENAME +ERROR_INVALID_SHARENAME +ERROR_INVALID_SHOWWIN_COMMAND +ERROR_INVALID_SID +ERROR_INVALID_SIGNAL_NUMBER +ERROR_INVALID_SPI_VALUE +ERROR_INVALID_STACKSEG +ERROR_INVALID_STARTING_CODESEG +ERROR_INVALID_SUB_AUTHORITY +ERROR_INVALID_TABLE +ERROR_INVALID_TARGET_HANDLE +ERROR_INVALID_TASK_INDEX +ERROR_INVALID_TASK_NAME +ERROR_INVALID_THREAD_ID +ERROR_INVALID_TIME +ERROR_INVALID_TOKEN +ERROR_INVALID_UNWIND_TARGET +ERROR_INVALID_USER_BUFFER +ERROR_INVALID_USER_PRINCIPAL_NAME +ERROR_INVALID_VARIANT +ERROR_INVALID_VERIFY_SWITCH +ERROR_INVALID_WINDOW_HANDLE +ERROR_INVALID_WORKSTATION +ERROR_IO_DEVICE +ERROR_IO_INCOMPLETE +ERROR_IO_PENDING +ERROR_IO_PRIVILEGE_FAILED +ERROR_IO_REISSUE_AS_CACHED +ERROR_IOPL_NOT_ENABLED +ERROR_IP_ADDRESS_CONFLICT1 +ERROR_IP_ADDRESS_CONFLICT2 +ERROR_IPSEC_IKE_TIMED_OUT +ERROR_IRQ_BUSY +ERROR_IS_JOIN_PATH +ERROR_IS_JOIN_TARGET +ERROR_IS_JOINED +ERROR_IS_SUBST_PATH +ERROR_IS_SUBST_TARGET +ERROR_IS_SUBSTED +ERROR_ITERATED_DATA_EXCEEDS_64k +ERROR_JOB_NO_CONTAINER +ERROR_JOIN_TO_JOIN +ERROR_JOIN_TO_SUBST +ERROR_JOURNAL_DELETE_IN_PROGRESS +ERROR_JOURNAL_ENTRY_DELETED +ERROR_JOURNAL_HOOK_SET +ERROR_JOURNAL_NOT_ACTIVE +ERROR_KERNEL_APC +ERROR_KEY_DELETED +ERROR_KEY_HAS_CHILDREN +ERROR_KM_DRIVER_BLOCKED +ERROR_LABEL_TOO_LONG +ERROR_LAST_ADMIN +ERROR_LB_WITHOUT_TABSTOPS +ERROR_LICENSE_QUOTA_EXCEEDED +ERROR_LINUX_SUBSYSTEM_NOT_PRESENT +ERROR_LINUX_SUBSYSTEM_UPDATE_REQUIRED +ERROR_LISTBOX_ID_NOT_FOUND +ERROR_LM_CROSS_ENCRYPTION_REQUIRED +ERROR_LOCAL_POLICY_MODIFICATION_NOT_SUPPORTED +ERROR_LOCAL_USER_SESSION_KEY +ERROR_LOCK_FAILED +ERROR_LOCK_VIOLATION +ERROR_LOCKED +ERROR_LOG_FILE_FULL +ERROR_LOG_HARD_ERROR +ERROR_LOGIN_TIME_RESTRICTION +ERROR_LOGIN_WKSTA_RESTRICTION +ERROR_LOGON_FAILURE +ERROR_LOGON_NOT_GRANTED +ERROR_LOGON_SERVER_CONFLICT +ERROR_LOGON_SESSION_COLLISION +ERROR_LOGON_SESSION_EXISTS +ERROR_LOGON_TYPE_NOT_GRANTED +ERROR_LONGJUMP +ERROR_LOST_MODE_LOGON_RESTRICTION +ERROR_LOST_WRITEBEHIND_DATA +ERROR_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR +ERROR_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED +ERROR_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR +ERROR_LUIDS_EXHAUSTED +ERROR_MACHINE_LOCKED +ERROR_MAGAZINE_NOT_PRESENT +ERROR_MAPPED_ALIGNMENT +ERROR_MARKED_TO_DISALLOW_WRITES +ERROR_MARSHALL_OVERFLOW +ERROR_MAX_SESSIONS_REACHED +ERROR_MAX_THRDS_REACHED +ERROR_MCA_EXCEPTION +ERROR_MCA_OCCURED +ERROR_MEDIA_CHANGED +ERROR_MEDIA_CHECK +ERROR_MEMBER_IN_ALIAS +ERROR_MEMBER_IN_GROUP +ERROR_MEMBER_NOT_IN_ALIAS +ERROR_MEMBER_NOT_IN_GROUP +ERROR_MEMBERS_PRIMARY_GROUP +ERROR_MEMORY_HARDWARE +ERROR_MENU_ITEM_NOT_FOUND +ERROR_MESSAGE_SYNC_ONLY +ERROR_META_EXPANSION_TOO_LONG +ERROR_MISSING_SYSTEMFILE +ERROR_MOD_NOT_FOUND +ERROR_MORE_DATA +ERROR_MORE_WRITES +ERROR_MOUNT_POINT_NOT_RESOLVED +ERROR_MP_PROCESSOR_MISMATCH +ERROR_MR_MID_NOT_FOUND +ERROR_MULTIPLE_FAULT_VIOLATION +ERROR_MUTANT_LIMIT_EXCEEDED +ERROR_MUTUAL_AUTH_FAILED +ERROR_NEGATIVE_SEEK +ERROR_NESTING_NOT_ALLOWED +ERROR_NET_OPEN_FAILED +ERROR_NET_WRITE_FAULT +ERROR_NETLOGON_NOT_STARTED +ERROR_NETNAME_DELETED +ERROR_NETWORK_ACCESS_DENIED +ERROR_NETWORK_ACCESS_DENIED_EDP +ERROR_NETWORK_BUSY +ERROR_NETWORK_UNREACHABLE +ERROR_NO_ACE_CONDITION +ERROR_NO_ASSOCIATION +ERROR_NO_BYPASSIO_DRIVER_SUPPORT +ERROR_NO_CALLBACK_ACTIVE +ERROR_NO_DATA +ERROR_NO_DATA_DETECTED +ERROR_NO_EFS +ERROR_NO_EVENT_PAIR +ERROR_NO_GUID_TRANSLATION +ERROR_NO_IMPERSONATION_TOKEN +ERROR_NO_INHERITANCE +ERROR_NO_LOG_SPACE +ERROR_NO_LOGON_SERVERS +ERROR_NO_MATCH +ERROR_NO_MEDIA_IN_DRIVE +ERROR_NO_MORE_DEVICES +ERROR_NO_MORE_FILES +ERROR_NO_MORE_ITEMS +ERROR_NO_MORE_MATCHES +ERROR_NO_MORE_SEARCH_HANDLES +ERROR_NO_MORE_USER_HANDLES +ERROR_NO_NET_OR_BAD_PATH +ERROR_NO_NETWORK +ERROR_NO_NVRAM_RESOURCES +ERROR_NO_PAGEFILE +ERROR_NO_PHYSICALLY_ALIGNED_FREE_SPACE_FOUND +ERROR_NO_PROC_SLOTS +ERROR_NO_PROMOTION_ACTIVE +ERROR_NO_QUOTAS_FOR_ACCOUNT +ERROR_NO_RANGES_PROCESSED +ERROR_NO_RECOVERY_POLICY +ERROR_NO_RECOVERY_PROGRAM +ERROR_NO_SCROLLBARS +ERROR_NO_SECRETS +ERROR_NO_SECURITY_ON_OBJECT +ERROR_NO_SHUTDOWN_IN_PROGRESS +ERROR_NO_SIGNAL_SENT +ERROR_NO_SITE_SETTINGS_OBJECT +ERROR_NO_SITENAME +ERROR_NO_SPOOL_SPACE +ERROR_NO_SUCH_ALIAS +ERROR_NO_SUCH_DEVICE +ERROR_NO_SUCH_DOMAIN +ERROR_NO_SUCH_GROUP +ERROR_NO_SUCH_LOGON_SESSION +ERROR_NO_SUCH_MEMBER +ERROR_NO_SUCH_PACKAGE +ERROR_NO_SUCH_PRIVILEGE +ERROR_NO_SUCH_SITE +ERROR_NO_SUCH_USER +ERROR_NO_SYSTEM_MENU +ERROR_NO_SYSTEM_RESOURCES +ERROR_NO_TASK_QUEUE +ERROR_NO_TOKEN +ERROR_NO_TRACKING_SERVICE +ERROR_NO_TRUST_LSA_SECRET +ERROR_NO_TRUST_SAM_ACCOUNT +ERROR_NO_UNICODE_TRANSLATION +ERROR_NO_USER_KEYS +ERROR_NO_USER_SESSION_KEY +ERROR_NO_VOLUME_ID +ERROR_NO_VOLUME_LABEL +ERROR_NO_WILDCARD_CHARACTERS +ERROR_NO_WORK_DONE +ERROR_NO_WRITABLE_DC_FOUND +ERROR_NO_YIELD_PERFORMED +ERROR_NOACCESS +ERROR_NOINTERFACE +ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT +ERROR_NOLOGON_SERVER_TRUST_ACCOUNT +ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT +ERROR_NON_ACCOUNT_SID +ERROR_NON_DOMAIN_SID +ERROR_NON_MDICHILD_WINDOW +ERROR_NONE_MAPPED +ERROR_NONPAGED_SYSTEM_RESOURCES +ERROR_NOT_A_CLOUD_FILE +ERROR_NOT_A_CLOUD_SYNC_ROOT +ERROR_NOT_A_DAX_VOLUME +ERROR_NOT_A_REPARSE_POINT +ERROR_NOT_ALL_ASSIGNED +ERROR_NOT_ALLOWED_ON_SYSTEM_FILE +ERROR_NOT_APPCONTAINER +ERROR_NOT_AUTHENTICATED +ERROR_NOT_CAPABLE +ERROR_NOT_CHILD_WINDOW +ERROR_NOT_CONNECTED +ERROR_NOT_CONTAINER +ERROR_NOT_DAX_MAPPABLE +ERROR_NOT_DOS_DISK +ERROR_NOT_ENOUGH_MEMORY +ERROR_NOT_ENOUGH_QUOTA +ERROR_NOT_ENOUGH_SERVER_MEMORY +ERROR_NOT_EXPORT_FORMAT +ERROR_NOT_FOUND +ERROR_NOT_GUI_PROCESS +ERROR_NOT_JOINED +ERROR_NOT_LOCKED +ERROR_NOT_LOGGED_ON +ERROR_NOT_LOGON_PROCESS +ERROR_NOT_OWNER +ERROR_NOT_READ_FROM_COPY +ERROR_NOT_READY +ERROR_NOT_REDUNDANT_STORAGE +ERROR_NOT_REGISTRY_FILE +ERROR_NOT_SAFE_MODE_DRIVER +ERROR_NOT_SAFEBOOT_SERVICE +ERROR_NOT_SAME_DEVICE +ERROR_NOT_SAME_OBJECT +ERROR_NOT_SUBSTED +ERROR_NOT_SUPPORTED +ERROR_NOT_SUPPORTED_IN_APPCONTAINER +ERROR_NOT_SUPPORTED_ON_DAX +ERROR_NOT_SUPPORTED_ON_SBS +ERROR_NOT_SUPPORTED_ON_STANDARD_SERVER +ERROR_NOT_SUPPORTED_WITH_AUDITING +ERROR_NOT_SUPPORTED_WITH_BTT +ERROR_NOT_SUPPORTED_WITH_BYPASSIO +ERROR_NOT_SUPPORTED_WITH_CACHED_HANDLE +ERROR_NOT_SUPPORTED_WITH_COMPRESSION +ERROR_NOT_SUPPORTED_WITH_DEDUPLICATION +ERROR_NOT_SUPPORTED_WITH_ENCRYPTION +ERROR_NOT_SUPPORTED_WITH_MONITORING +ERROR_NOT_SUPPORTED_WITH_REPLICATION +ERROR_NOT_SUPPORTED_WITH_SNAPSHOT +ERROR_NOT_SUPPORTED_WITH_VIRTUALIZATION +ERROR_NOT_TINY_STREAM +ERROR_NOTHING_TO_TERMINATE +ERROR_NOTIFICATION_GUID_ALREADY_DEFINED +ERROR_NOTIFY_CLEANUP +ERROR_NOTIFY_ENUM_DIR +ERROR_NT_CROSS_ENCRYPTION_REQUIRED +ERROR_NTLM_BLOCKED +ERROR_NULL_LM_PASSWORD +ERROR_OBJECT_IS_IMMUTABLE +ERROR_OBJECT_NAME_EXISTS +ERROR_OBJECT_NOT_EXTERNALLY_BACKED +ERROR_OFFLOAD_READ_FILE_NOT_SUPPORTED +ERROR_OFFLOAD_READ_FLT_NOT_SUPPORTED +ERROR_OFFLOAD_WRITE_FILE_NOT_SUPPORTED +ERROR_OFFLOAD_WRITE_FLT_NOT_SUPPORTED +ERROR_OFFSET_ALIGNMENT_VIOLATION +ERROR_OLD_WIN_VERSION +ERROR_ONLY_IF_CONNECTED +ERROR_OPEN_FAILED +ERROR_OPEN_FILES +ERROR_OPERATION_ABORTED +ERROR_OPERATION_IN_PROGRESS +ERROR_OPLOCK_BREAK_IN_PROGRESS +ERROR_OPLOCK_HANDLE_CLOSED +ERROR_OPLOCK_NOT_GRANTED +ERROR_OPLOCK_SWITCHED_TO_NEW_HANDLE +ERROR_ORPHAN_NAME_EXHAUSTED +ERROR_OUT_OF_PAPER +ERROR_OUT_OF_STRUCTURES +ERROR_OUTOFMEMORY +ERROR_OVERRIDE_NOCHANGES +ERROR_PAGE_FAULT_COPY_ON_WRITE +ERROR_PAGE_FAULT_DEMAND_ZERO +ERROR_PAGE_FAULT_GUARD_PAGE +ERROR_PAGE_FAULT_PAGING_FILE +ERROR_PAGE_FAULT_TRANSITION +ERROR_PAGED_SYSTEM_RESOURCES +ERROR_PAGEFILE_CREATE_FAILED +ERROR_PAGEFILE_NOT_SUPPORTED +ERROR_PAGEFILE_QUOTA +ERROR_PAGEFILE_QUOTA_EXCEEDED +ERROR_PARAMETER_QUOTA_EXCEEDED +ERROR_PARTIAL_COPY +ERROR_PARTITION_FAILURE +ERROR_PARTITION_TERMINATING +ERROR_PASSWORD_CHANGE_REQUIRED +ERROR_PASSWORD_EXPIRED +ERROR_PASSWORD_MUST_CHANGE +ERROR_PASSWORD_RESTRICTION +ERROR_PATCH_MANAGED_ADVERTISED_PRODUCT +ERROR_PATCH_NO_SEQUENCE +ERROR_PATCH_PACKAGE_INVALID +ERROR_PATCH_PACKAGE_OPEN_FAILED +ERROR_PATCH_PACKAGE_REJECTED +ERROR_PATCH_PACKAGE_UNSUPPORTED +ERROR_PATCH_REMOVAL_DISALLOWED +ERROR_PATCH_REMOVAL_UNSUPPORTED +ERROR_PATCH_TARGET_NOT_FOUND +ERROR_PATH_BUSY +ERROR_PATH_NOT_FOUND +ERROR_PER_USER_TRUST_QUOTA_EXCEEDED +ERROR_PIPE_BUSY +ERROR_PIPE_CONNECTED +ERROR_PIPE_LISTENING +ERROR_PIPE_LOCAL +ERROR_PIPE_NOT_CONNECTED +ERROR_PKINIT_FAILURE +ERROR_PLUGPLAY_QUERY_VETOED +ERROR_PNP_BAD_MPS_TABLE +ERROR_PNP_INVALID_ID +ERROR_PNP_IRQ_TRANSLATION_FAILED +ERROR_PNP_QUERY_REMOVE_DEVICE_TIMEOUT +ERROR_PNP_QUERY_REMOVE_RELATED_DEVICE_TIMEOUT +ERROR_PNP_QUERY_REMOVE_UNRELATED_DEVICE_TIMEOUT +ERROR_PNP_REBOOT_REQUIRED +ERROR_PNP_RESTART_ENUMERATION +ERROR_PNP_TRANSLATION_FAILED +ERROR_POINT_NOT_FOUND +ERROR_POLICY_OBJECT_NOT_FOUND +ERROR_POLICY_ONLY_IN_DS +ERROR_POPUP_ALREADY_ACTIVE +ERROR_PORT_MESSAGE_TOO_LONG +ERROR_PORT_NOT_SET +ERROR_PORT_UNREACHABLE +ERROR_POSSIBLE_DEADLOCK +ERROR_POTENTIAL_FILE_FOUND +ERROR_PREDEFINED_HANDLE +ERROR_PRIMARY_TRANSPORT_CONNECT_FAILED +ERROR_PRINT_CANCELLED +ERROR_PRINTER_ALREADY_EXISTS +ERROR_PRINTER_DELETED +ERROR_PRINTER_DRIVER_ALREADY_INSTALLED +ERROR_PRINTQ_FULL +ERROR_PRIVATE_DIALOG_INDEX +ERROR_PRIVILEGE_NOT_HELD +ERROR_PROC_NOT_FOUND +ERROR_PROCESS_ABORTED +ERROR_PROCESS_IN_JOB +ERROR_PROCESS_IS_PROTECTED +ERROR_PROCESS_MODE_ALREADY_BACKGROUND +ERROR_PROCESS_MODE_NOT_BACKGROUND +ERROR_PROCESS_NOT_IN_JOB +ERROR_PRODUCT_UNINSTALLED +ERROR_PRODUCT_VERSION +ERROR_PROFILING_AT_LIMIT +ERROR_PROFILING_NOT_STARTED +ERROR_PROFILING_NOT_STOPPED +ERROR_PROMOTION_ACTIVE +ERROR_PROTOCOL_UNREACHABLE +ERROR_PWD_HISTORY_CONFLICT +ERROR_PWD_TOO_LONG +ERROR_PWD_TOO_RECENT +ERROR_PWD_TOO_SHORT +ERROR_QUOTA_ACTIVITY +ERROR_QUOTA_LIST_INCONSISTENT +ERROR_RANGE_LIST_CONFLICT +ERROR_RANGE_NOT_FOUND +ERROR_READ_FAULT +ERROR_RECEIVE_EXPEDITED +ERROR_RECEIVE_PARTIAL +ERROR_RECEIVE_PARTIAL_EXPEDITED +ERROR_RECOVERY_FAILURE +ERROR_REDIR_PAUSED +ERROR_REDIRECTOR_HAS_OPEN_HANDLES +ERROR_REG_NAT_CONSUMPTION +ERROR_REGISTRY_CORRUPT +ERROR_REGISTRY_HIVE_RECOVERED +ERROR_REGISTRY_IO_FAILED +ERROR_REGISTRY_QUOTA_LIMIT +ERROR_REGISTRY_RECOVERED +ERROR_RELOC_CHAIN_XEEDS_SEGLIM +ERROR_REM_NOT_LIST +ERROR_REMOTE_PRINT_CONNECTIONS_BLOCKED +ERROR_REMOTE_SESSION_LIMIT_EXCEEDED +ERROR_REMOTE_STORAGE_MEDIA_ERROR +ERROR_REMOTE_STORAGE_NOT_ACTIVE +ERROR_REPARSE +ERROR_REPARSE_ATTRIBUTE_CONFLICT +ERROR_REPARSE_OBJECT +ERROR_REPARSE_POINT_ENCOUNTERED +ERROR_REPARSE_TAG_INVALID +ERROR_REPARSE_TAG_MISMATCH +ERROR_REPLY_MESSAGE_MISMATCH +ERROR_REQ_NOT_ACCEP +ERROR_REQUEST_ABORTED +ERROR_REQUEST_OUT_OF_SEQUENCE +ERROR_REQUEST_PAUSED +ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION +ERROR_RESIDENT_FILE_NOT_SUPPORTED +ERROR_RESOURCE_CALL_TIMED_OUT +ERROR_RESOURCE_DATA_NOT_FOUND +ERROR_RESOURCE_LANG_NOT_FOUND +ERROR_RESOURCE_NAME_NOT_FOUND +ERROR_RESOURCE_REQUIREMENTS_CHANGED +ERROR_RESOURCE_TYPE_NOT_FOUND +ERROR_RESTART_APPLICATION +ERROR_RESUME_HIBERNATION +ERROR_RETRY +ERROR_RETURN_ADDRESS_HIJACK_ATTEMPT +ERROR_REVISION_MISMATCH +ERROR_RING2_STACK_IN_USE +ERROR_RING2SEG_MUST_BE_MOVABLE +ERROR_RMODE_APP +ERROR_ROWSNOTRELEASED +ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT +ERROR_RUNLEVEL_SWITCH_TIMEOUT +ERROR_RWRAW_ENCRYPTED_FILE_NOT_ENCRYPTED +ERROR_RWRAW_ENCRYPTED_INVALID_EDATAINFO_FILEOFFSET +ERROR_RWRAW_ENCRYPTED_INVALID_EDATAINFO_FILERANGE +ERROR_RWRAW_ENCRYPTED_INVALID_EDATAINFO_PARAMETER +ERROR_RXACT_COMMIT_FAILURE +ERROR_RXACT_COMMIT_NECESSARY +ERROR_RXACT_COMMITTED +ERROR_RXACT_INVALID_STATE +ERROR_RXACT_STATE_CREATED +ERROR_SAM_INIT_FAILURE +ERROR_SAME_DRIVE +ERROR_SCOPE_NOT_FOUND +ERROR_SCREEN_ALREADY_LOCKED +ERROR_SCRUB_DATA_DISABLED +ERROR_SECRET_TOO_LONG +ERROR_SECTION_DIRECT_MAP_ONLY +ERROR_SECTOR_NOT_FOUND +ERROR_SECURITY_DENIES_OPERATION +ERROR_SECURITY_STREAM_IS_INCONSISTENT +ERROR_SEEK +ERROR_SEEK_ON_DEVICE +ERROR_SEGMENT_NOTIFICATION +ERROR_SEM_IS_SET +ERROR_SEM_NOT_FOUND +ERROR_SEM_OWNER_DIED +ERROR_SEM_TIMEOUT +ERROR_SEM_USER_LIMIT +ERROR_SERIAL_NO_DEVICE +ERROR_SERVER_DISABLED +ERROR_SERVER_HAS_OPEN_HANDLES +ERROR_SERVER_NOT_DISABLED +ERROR_SERVER_SHUTDOWN_IN_PROGRESS +ERROR_SERVER_SID_MISMATCH +ERROR_SERVER_TRANSPORT_CONFLICT +ERROR_SERVICE_ALREADY_RUNNING +ERROR_SERVICE_CANNOT_ACCEPT_CTRL +ERROR_SERVICE_DATABASE_LOCKED +ERROR_SERVICE_DEPENDENCY_DELETED +ERROR_SERVICE_DEPENDENCY_FAIL +ERROR_SERVICE_DISABLED +ERROR_SERVICE_DOES_NOT_EXIST +ERROR_SERVICE_EXISTS +ERROR_SERVICE_LOGON_FAILED +ERROR_SERVICE_MARKED_FOR_DELETE +ERROR_SERVICE_NEVER_STARTED +ERROR_SERVICE_NO_THREAD +ERROR_SERVICE_NOT_ACTIVE +ERROR_SERVICE_NOT_FOUND +ERROR_SERVICE_NOT_IN_EXE +ERROR_SERVICE_NOTIFICATION +ERROR_SERVICE_NOTIFY_CLIENT_LAGGING +ERROR_SERVICE_REQUEST_TIMEOUT +ERROR_SERVICE_SPECIFIC_ERROR +ERROR_SERVICE_START_HANG +ERROR_SESSION_CREDENTIAL_CONFLICT +ERROR_SESSION_KEY_TOO_SHORT +ERROR_SET_CONTEXT_DENIED +ERROR_SET_NOT_FOUND +ERROR_SET_POWER_STATE_FAILED +ERROR_SET_POWER_STATE_VETOED +ERROR_SETCOUNT_ON_BAD_LB +ERROR_SETMARK_DETECTED +ERROR_SHARED_POLICY +ERROR_SHARING_BUFFER_EXCEEDED +ERROR_SHARING_PAUSED +ERROR_SHARING_VIOLATION +ERROR_SHORT_NAMES_NOT_ENABLED_ON_VOLUME +ERROR_SHUTDOWN_DISKS_NOT_IN_MAINTENANCE_MODE +ERROR_SHUTDOWN_IN_PROGRESS +ERROR_SHUTDOWN_IS_SCHEDULED +ERROR_SHUTDOWN_USERS_LOGGED_ON +ERROR_SIGNAL_PENDING +ERROR_SIGNAL_REFUSED +ERROR_SINGLE_INSTANCE_APP +ERROR_SMARTCARD_SUBSYSTEM_FAILURE +ERROR_SMB1_NOT_AVAILABLE +ERROR_SMB_GUEST_LOGON_BLOCKED +ERROR_SMR_GARBAGE_COLLECTION_REQUIRED +ERROR_SOME_NOT_MAPPED +ERROR_SOURCE_ELEMENT_EMPTY +ERROR_SPARSE_FILE_NOT_SUPPORTED +ERROR_SPECIAL_ACCOUNT +ERROR_SPECIAL_GROUP +ERROR_SPECIAL_USER +ERROR_SRC_SRV_DLL_LOAD_FAILED +ERROR_STACK_BUFFER_OVERRUN +ERROR_STACK_OVERFLOW +ERROR_STACK_OVERFLOW_READ +ERROR_STOPPED_ON_SYMLINK +ERROR_STORAGE_LOST_DATA_PERSISTENCE +ERROR_STORAGE_RESERVE_ALREADY_EXISTS +ERROR_STORAGE_RESERVE_DOES_NOT_EXIST +ERROR_STORAGE_RESERVE_ID_INVALID +ERROR_STORAGE_RESERVE_NOT_EMPTY +ERROR_STORAGE_STACK_ACCESS_DENIED +ERROR_STORAGE_TOPOLOGY_ID_MISMATCH +ERROR_STRICT_CFG_VIOLATION +ERROR_SUBST_TO_JOIN +ERROR_SUBST_TO_SUBST +ERROR_SUCCESS +ERROR_SUCCESS_REBOOT_INITIATED +ERROR_SWAPERROR +ERROR_SYMLINK_CLASS_DISABLED +ERROR_SYMLINK_NOT_SUPPORTED +ERROR_SYNC_FOREGROUND_REFRESH_REQUIRED +ERROR_SYNCHRONIZATION_REQUIRED +ERROR_SYSTEM_HIVE_TOO_LARGE +ERROR_SYSTEM_IMAGE_BAD_SIGNATURE +ERROR_SYSTEM_POWERSTATE_COMPLEX_TRANSITION +ERROR_SYSTEM_POWERSTATE_TRANSITION +ERROR_SYSTEM_PROCESS_TERMINATED +ERROR_SYSTEM_SHUTDOWN +ERROR_SYSTEM_TRACE +ERROR_THREAD_1_INACTIVE +ERROR_THREAD_ALREADY_IN_TASK +ERROR_THREAD_MODE_ALREADY_BACKGROUND +ERROR_THREAD_MODE_NOT_BACKGROUND +ERROR_THREAD_NOT_IN_PROCESS +ERROR_THREAD_WAS_SUSPENDED +ERROR_TIME_SENSITIVE_THREAD +ERROR_TIME_SKEW +ERROR_TIMEOUT +ERROR_TIMER_NOT_CANCELED +ERROR_TIMER_RESOLUTION_NOT_SET +ERROR_TIMER_RESUME_IGNORED +ERROR_TLW_WITH_WSCHILD +ERROR_TOKEN_ALREADY_IN_USE +ERROR_TOO_MANY_CMDS +ERROR_TOO_MANY_CONTEXT_IDS +ERROR_TOO_MANY_DESCRIPTORS +ERROR_TOO_MANY_LINKS +ERROR_TOO_MANY_LUIDS_REQUESTED +ERROR_TOO_MANY_MODULES +ERROR_TOO_MANY_MUXWAITERS +ERROR_TOO_MANY_NAMES +ERROR_TOO_MANY_OPEN_FILES +ERROR_TOO_MANY_POSTS +ERROR_TOO_MANY_SECRETS +ERROR_TOO_MANY_SEM_REQUESTS +ERROR_TOO_MANY_SEMAPHORES +ERROR_TOO_MANY_SESS +ERROR_TOO_MANY_SIDS +ERROR_TOO_MANY_TCBS +ERROR_TOO_MANY_THREADS +ERROR_TRANSLATION_COMPLETE +ERROR_TRUST_FAILURE +ERROR_TRUSTED_DOMAIN_FAILURE +ERROR_TRUSTED_RELATIONSHIP_FAILURE +ERROR_UNABLE_TO_LOCK_MEDIA +ERROR_UNABLE_TO_MOVE_REPLACEMENT +ERROR_UNABLE_TO_MOVE_REPLACEMENT_2 +ERROR_UNABLE_TO_REMOVE_REPLACED +ERROR_UNABLE_TO_UNLOAD_MEDIA +ERROR_UNDEFINED_CHARACTER +ERROR_UNDEFINED_SCOPE +ERROR_UNEXP_NET_ERR +ERROR_UNEXPECTED_MM_CREATE_ERR +ERROR_UNEXPECTED_MM_EXTEND_ERR +ERROR_UNEXPECTED_MM_MAP_ERROR +ERROR_UNEXPECTED_NTCACHEMANAGER_ERROR +ERROR_UNHANDLED_EXCEPTION +ERROR_UNIDENTIFIED_ERROR +ERROR_UNKNOWN_COMPONENT +ERROR_UNKNOWN_FEATURE +ERROR_UNKNOWN_PATCH +ERROR_UNKNOWN_PORT +ERROR_UNKNOWN_PRINTER_DRIVER +ERROR_UNKNOWN_PRINTPROCESSOR +ERROR_UNKNOWN_PRODUCT +ERROR_UNKNOWN_PROPERTY +ERROR_UNKNOWN_REVISION +ERROR_UNRECOGNIZED_MEDIA +ERROR_UNRECOGNIZED_VOLUME +ERROR_UNSATISFIED_DEPENDENCIES +ERROR_UNSUPPORTED_COMPRESSION +ERROR_UNSUPPORTED_TYPE +ERROR_UNTRUSTED_MOUNT_POINT +ERROR_UNWIND +ERROR_UNWIND_CONSOLIDATE +ERROR_USER_APC +ERROR_USER_DELETE_TRUST_QUOTA_EXCEEDED +ERROR_USER_EXISTS +ERROR_USER_MAPPED_FILE +ERROR_USER_PROFILE_LOAD +ERROR_VALIDATE_CONTINUE +ERROR_VC_DISCONNECTED +ERROR_VDM_DISALLOWED +ERROR_VDM_HARD_ERROR +ERROR_VERIFIER_STOP +ERROR_VERSION_PARSE_ERROR +ERROR_VIRUS_DELETED +ERROR_VIRUS_INFECTED +ERROR_VOLSNAP_HIBERNATE_READY +ERROR_VOLSNAP_PREPARE_HIBERNATE +ERROR_VOLUME_MOUNTED +ERROR_VOLUME_NOT_CLUSTER_ALIGNED +ERROR_VOLUME_NOT_SIS_ENABLED +ERROR_VOLUME_NOT_SUPPORT_EFS +ERROR_VOLUME_NOT_SUPPORTED +ERROR_VOLUME_WRITE_ACCESS_DENIED +ERROR_WAIT_1 +ERROR_WAIT_2 +ERROR_WAIT_3 +ERROR_WAIT_63 +ERROR_WAIT_FOR_OPLOCK +ERROR_WAIT_NO_CHILDREN +ERROR_WAKE_SYSTEM +ERROR_WAKE_SYSTEM_DEBUGGER +ERROR_WAS_LOCKED +ERROR_WAS_UNLOCKED +ERROR_WEAK_WHFBKEY_BLOCKED +ERROR_WINDOW_NOT_COMBOBOX +ERROR_WINDOW_NOT_DIALOG +ERROR_WINDOW_OF_OTHER_THREAD +ERROR_WIP_ENCRYPTION_FAILED +ERROR_WOF_FILE_RESOURCE_TABLE_CORRUPT +ERROR_WOF_WIM_HEADER_CORRUPT +ERROR_WOF_WIM_RESOURCE_TABLE_CORRUPT +ERROR_WORKING_SET_QUOTA +ERROR_WOW_ASSERTION +ERROR_WRITE_FAULT +ERROR_WRITE_PROTECT +ERROR_WRONG_COMPARTMENT +ERROR_WRONG_DISK +ERROR_WRONG_EFS +ERROR_WRONG_PASSWORD +ERROR_WRONG_TARGET_NAME +ERROR_WX86_ERROR +ERROR_WX86_WARNING +ERROR_XML_PARSE_ERROR +ERROR_XMLDSIG_ERROR +EXCEPTION_DISPOSITION +EXCEPTION_MAXIMUM_PARAMETERS +EXCEPTION_RECORD +EXCEPTION_STACK_OVERFLOW +ExceptionCollidedUnwind +ExceptionContinueExecution +ExceptionContinueSearch +ExceptionNestedException +ExitProcess +EXTENDED_STARTUPINFO_PRESENT +FACILITY_CODE +FACILITY_NT_BIT +FALSE +FARPROC +FAST_FAIL_FATAL_APP_EXIT +FD_SET +FILE_ACCESS_RIGHTS +FILE_ADD_FILE +FILE_ADD_SUBDIRECTORY +FILE_ALL_ACCESS +FILE_ALLOCATION_INFO +FILE_APPEND_DATA +FILE_ATTRIBUTE_ARCHIVE +FILE_ATTRIBUTE_COMPRESSED +FILE_ATTRIBUTE_DEVICE +FILE_ATTRIBUTE_DIRECTORY +FILE_ATTRIBUTE_EA +FILE_ATTRIBUTE_ENCRYPTED +FILE_ATTRIBUTE_HIDDEN +FILE_ATTRIBUTE_INTEGRITY_STREAM +FILE_ATTRIBUTE_NO_SCRUB_DATA +FILE_ATTRIBUTE_NORMAL +FILE_ATTRIBUTE_NOT_CONTENT_INDEXED +FILE_ATTRIBUTE_OFFLINE +FILE_ATTRIBUTE_PINNED +FILE_ATTRIBUTE_READONLY +FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS +FILE_ATTRIBUTE_RECALL_ON_OPEN +FILE_ATTRIBUTE_REPARSE_POINT +FILE_ATTRIBUTE_SPARSE_FILE +FILE_ATTRIBUTE_SYSTEM +FILE_ATTRIBUTE_TAG_INFO +FILE_ATTRIBUTE_TEMPORARY +FILE_ATTRIBUTE_UNPINNED +FILE_ATTRIBUTE_VIRTUAL +FILE_BASIC_INFO +FILE_BEGIN +FILE_COMPLETE_IF_OPLOCKED +FILE_CONTAINS_EXTENDED_CREATE_INFORMATION +FILE_CREATE +FILE_CREATE_PIPE_INSTANCE +FILE_CREATE_TREE_CONNECTION +FILE_CREATION_DISPOSITION +FILE_CURRENT +FILE_DELETE_CHILD +FILE_DELETE_ON_CLOSE +FILE_DIRECTORY_FILE +FILE_DISALLOW_EXCLUSIVE +FILE_DISPOSITION_FLAG_DELETE +FILE_DISPOSITION_FLAG_DO_NOT_DELETE +FILE_DISPOSITION_FLAG_FORCE_IMAGE_SECTION_CHECK +FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE +FILE_DISPOSITION_FLAG_ON_CLOSE +FILE_DISPOSITION_FLAG_POSIX_SEMANTICS +FILE_DISPOSITION_INFO +FILE_DISPOSITION_INFO_EX +FILE_DISPOSITION_INFO_EX_FLAGS +FILE_END +FILE_END_OF_FILE_INFO +FILE_EXECUTE +FILE_FLAG_BACKUP_SEMANTICS +FILE_FLAG_DELETE_ON_CLOSE +FILE_FLAG_FIRST_PIPE_INSTANCE +FILE_FLAG_NO_BUFFERING +FILE_FLAG_OPEN_NO_RECALL +FILE_FLAG_OPEN_REPARSE_POINT +FILE_FLAG_OVERLAPPED +FILE_FLAG_POSIX_SEMANTICS +FILE_FLAG_RANDOM_ACCESS +FILE_FLAG_SEQUENTIAL_SCAN +FILE_FLAG_SESSION_AWARE +FILE_FLAG_WRITE_THROUGH +FILE_FLAGS_AND_ATTRIBUTES +FILE_GENERIC_EXECUTE +FILE_GENERIC_READ +FILE_GENERIC_WRITE +FILE_ID_BOTH_DIR_INFO +FILE_INFO_BY_HANDLE_CLASS +FILE_IO_PRIORITY_HINT_INFO +FILE_LIST_DIRECTORY +FILE_NAME_NORMALIZED +FILE_NAME_OPENED +FILE_NO_EA_KNOWLEDGE +FILE_NO_INTERMEDIATE_BUFFERING +FILE_NON_DIRECTORY_FILE +FILE_OPEN +FILE_OPEN_BY_FILE_ID +FILE_OPEN_FOR_BACKUP_INTENT +FILE_OPEN_FOR_FREE_SPACE_QUERY +FILE_OPEN_IF +FILE_OPEN_REPARSE_POINT +FILE_OPEN_REQUIRING_OPLOCK +FILE_OVERWRITE +FILE_OVERWRITE_IF +FILE_RANDOM_ACCESS +FILE_READ_ATTRIBUTES +FILE_READ_DATA +FILE_READ_EA +FILE_RENAME_FLAG_POSIX_SEMANTICS +FILE_RENAME_FLAG_REPLACE_IF_EXISTS +FILE_RENAME_INFO +FILE_RESERVE_OPFILTER +FILE_SEQUENTIAL_ONLY +FILE_SESSION_AWARE +FILE_SHARE_DELETE +FILE_SHARE_MODE +FILE_SHARE_NONE +FILE_SHARE_READ +FILE_SHARE_WRITE +FILE_STANDARD_INFO +FILE_SUPERSEDE +FILE_SYNCHRONOUS_IO_ALERT +FILE_SYNCHRONOUS_IO_NONALERT +FILE_TRAVERSE +FILE_TYPE +FILE_TYPE_CHAR +FILE_TYPE_DISK +FILE_TYPE_PIPE +FILE_TYPE_REMOTE +FILE_TYPE_UNKNOWN +FILE_WRITE_ATTRIBUTES +FILE_WRITE_DATA +FILE_WRITE_EA +FILE_WRITE_THROUGH +FileAlignmentInfo +FileAllocationInfo +FileAttributeTagInfo +FileBasicInfo +FileCaseSensitiveInfo +FileCompressionInfo +FileDispositionInfo +FileDispositionInfoEx +FileEndOfFileInfo +FileFullDirectoryInfo +FileFullDirectoryRestartInfo +FileIdBothDirectoryInfo +FileIdBothDirectoryRestartInfo +FileIdExtdDirectoryInfo +FileIdExtdDirectoryRestartInfo +FileIdInfo +FileIoPriorityHintInfo +FileNameInfo +FileNormalizedNameInfo +FileRemoteProtocolInfo +FileRenameInfo +FileRenameInfoEx +FileStandardInfo +FileStorageInfo +FileStreamInfo +FILETIME +FindClose +FindExInfoBasic +FindExSearchNameMatch +FindFirstFileExW +FindNextFileW +FIONBIO +FlushFileBuffers +FORMAT_MESSAGE_ALLOCATE_BUFFER +FORMAT_MESSAGE_ARGUMENT_ARRAY +FORMAT_MESSAGE_FROM_HMODULE +FORMAT_MESSAGE_FROM_STRING +FORMAT_MESSAGE_FROM_SYSTEM +FORMAT_MESSAGE_IGNORE_INSERTS +FORMAT_MESSAGE_OPTIONS +FormatMessageW +freeaddrinfo +FreeEnvironmentStringsW +FRS_ERR_SYSVOL_POPULATE_TIMEOUT +FSCTL_GET_REPARSE_POINT +FSCTL_SET_REPARSE_POINT +GENERIC_ACCESS_RIGHTS +GENERIC_ALL +GENERIC_EXECUTE +GENERIC_READ +GENERIC_WRITE +GetActiveProcessorCount +getaddrinfo +GetCommandLineW +GetConsoleMode +GetConsoleOutputCP +GetCurrentDirectoryW +GetCurrentProcess +GetCurrentProcessId +GetCurrentThread +GetEnvironmentStringsW +GetEnvironmentVariableW +GetExitCodeProcess +GetFileAttributesW +GetFileInformationByHandle +GetFileInformationByHandleEx +GetFileType +GETFINALPATHNAMEBYHANDLE_FLAGS +GetFinalPathNameByHandleW +GetFullPathNameW +GetLastError +GetModuleFileNameW +GetModuleHandleA +GetModuleHandleW +GetOverlappedResult +getpeername +GetProcAddress +GetProcessId +getsockname +getsockopt +GetStdHandle +GetSystemDirectoryW +GetSystemInfo +GetSystemTimeAsFileTime +GetSystemTimePreciseAsFileTime +GetTempPathW +GetUserProfileDirectoryW +GetWindowsDirectoryW +HANDLE +HANDLE_FLAG_INHERIT +HANDLE_FLAG_PROTECT_FROM_CLOSE +HANDLE_FLAGS +HIGH_PRIORITY_CLASS +HMODULE +IDLE_PRIORITY_CLASS +IN6_ADDR +IN_ADDR +INFINITE +INHERIT_CALLER_PRIORITY +INHERIT_PARENT_AFFINITY +INIT_ONCE_INIT_FAILED +InitializeProcThreadAttributeList +InitOnceBeginInitialize +InitOnceComplete +INVALID_FILE_ATTRIBUTES +INVALID_SOCKET +IO_REPARSE_TAG_MOUNT_POINT +IO_REPARSE_TAG_SYMLINK +ioctlsocket +IP_ADD_MEMBERSHIP +IP_DROP_MEMBERSHIP +IP_MREQ +IP_MULTICAST_LOOP +IP_MULTICAST_TTL +IP_TTL +IPPROTO +IPPROTO_AH +IPPROTO_CBT +IPPROTO_DSTOPTS +IPPROTO_EGP +IPPROTO_ESP +IPPROTO_FRAGMENT +IPPROTO_GGP +IPPROTO_HOPOPTS +IPPROTO_ICLFXBM +IPPROTO_ICMP +IPPROTO_ICMPV6 +IPPROTO_IDP +IPPROTO_IGMP +IPPROTO_IGP +IPPROTO_IP +IPPROTO_IPV4 +IPPROTO_IPV6 +IPPROTO_L2TP +IPPROTO_MAX +IPPROTO_ND +IPPROTO_NONE +IPPROTO_PGM +IPPROTO_PIM +IPPROTO_PUP +IPPROTO_RAW +IPPROTO_RDP +IPPROTO_RESERVED_IPSEC +IPPROTO_RESERVED_IPSECOFFLOAD +IPPROTO_RESERVED_MAX +IPPROTO_RESERVED_RAW +IPPROTO_RESERVED_WNV +IPPROTO_RM +IPPROTO_ROUTING +IPPROTO_SCTP +IPPROTO_ST +IPPROTO_TCP +IPPROTO_UDP +IPV6_ADD_MEMBERSHIP +IPV6_DROP_MEMBERSHIP +IPV6_MREQ +IPV6_MULTICAST_LOOP +IPV6_V6ONLY +LINGER +listen +LocalFree +LOCKFILE_EXCLUSIVE_LOCK +LOCKFILE_FAIL_IMMEDIATELY +LockFileEx +LPOVERLAPPED_COMPLETION_ROUTINE +LPPROC_THREAD_ATTRIBUTE_LIST +LPPROGRESS_ROUTINE +LPPROGRESS_ROUTINE_CALLBACK_REASON +LPTHREAD_START_ROUTINE +LPWSAOVERLAPPED_COMPLETION_ROUTINE +M128A +MAX_PATH +MAXIMUM_REPARSE_DATA_BUFFER_SIZE +MaximumFileInfoByHandleClass +MB_COMPOSITE +MB_ERR_INVALID_CHARS +MB_PRECOMPOSED +MB_USEGLYPHCHARS +MOVE_FILE_FLAGS +MOVEFILE_COPY_ALLOWED +MOVEFILE_CREATE_HARDLINK +MOVEFILE_DELAY_UNTIL_REBOOT +MOVEFILE_FAIL_IF_NOT_TRACKABLE +MOVEFILE_REPLACE_EXISTING +MOVEFILE_WRITE_THROUGH +MoveFileExW +MSG_DONTROUTE +MSG_OOB +MSG_PEEK +MSG_PUSH_IMMEDIATE +MSG_WAITALL +MULTI_BYTE_TO_WIDE_CHAR_FLAGS +MultiByteToWideChar +NAMED_PIPE_MODE +NO_ERROR +NORMAL_PRIORITY_CLASS +NtCreateFile +NTCREATEFILE_CREATE_DISPOSITION +NTCREATEFILE_CREATE_OPTIONS +NtOpenFile +NtReadFile +NTSTATUS +NtWriteFile +OBJ_DONT_REPARSE +OPEN_ALWAYS +OPEN_EXISTING +OpenProcessToken +OVERLAPPED +PIPE_ACCEPT_REMOTE_CLIENTS +PIPE_ACCESS_DUPLEX +PIPE_ACCESS_INBOUND +PIPE_ACCESS_OUTBOUND +PIPE_CLIENT_END +PIPE_NOWAIT +PIPE_READMODE_BYTE +PIPE_READMODE_MESSAGE +PIPE_REJECT_REMOTE_CLIENTS +PIPE_SERVER_END +PIPE_TYPE_BYTE +PIPE_TYPE_MESSAGE +PIPE_WAIT +PROCESS_CREATION_FLAGS +PROCESS_INFORMATION +PROCESS_MODE_BACKGROUND_BEGIN +PROCESS_MODE_BACKGROUND_END +PROCESSOR_ARCHITECTURE +PROFILE_KERNEL +PROFILE_SERVER +PROFILE_USER +PROGRESS_CONTINUE +QueryPerformanceCounter +QueryPerformanceFrequency +READ_CONTROL +ReadConsoleW +ReadFile +ReadFileEx +REALTIME_PRIORITY_CLASS +recv +recvfrom +ReleaseSRWLockExclusive +ReleaseSRWLockShared +RemoveDirectoryW +RtlGenRandom +RtlNtStatusToDosError +SD_BOTH +SD_RECEIVE +SD_SEND +SECURITY_ANONYMOUS +SECURITY_ATTRIBUTES +SECURITY_CONTEXT_TRACKING +SECURITY_DELEGATION +SECURITY_EFFECTIVE_ONLY +SECURITY_IDENTIFICATION +SECURITY_IMPERSONATION +SECURITY_SQOS_PRESENT +SECURITY_VALID_SQOS_FLAGS +select +send +SEND_RECV_FLAGS +sendto +SET_FILE_POINTER_MOVE_METHOD +SetCurrentDirectoryW +SetEnvironmentVariableW +SetFileAttributesW +SetFileInformationByHandle +SetFilePointerEx +SetFileTime +SetHandleInformation +SetLastError +setsockopt +SetThreadStackGuarantee +SetWaitableTimer +shutdown +Sleep +SleepConditionVariableSRW +SleepEx +SO_BROADCAST +SO_ERROR +SO_LINGER +SO_RCVTIMEO +SO_SNDTIMEO +SOCK_DGRAM +SOCK_RAW +SOCK_RDM +SOCK_SEQPACKET +SOCK_STREAM +SOCKADDR +SOCKADDR_STORAGE +SOCKADDR_UN +SOCKET +SOCKET_ERROR +SOL_SOCKET +SPECIFIC_RIGHTS_ALL +STACK_SIZE_PARAM_IS_A_RESERVATION +STANDARD_RIGHTS_ALL +STANDARD_RIGHTS_EXECUTE +STANDARD_RIGHTS_READ +STANDARD_RIGHTS_REQUIRED +STANDARD_RIGHTS_WRITE +STARTF_FORCEOFFFEEDBACK +STARTF_FORCEONFEEDBACK +STARTF_PREVENTPINNING +STARTF_RUNFULLSCREEN +STARTF_TITLEISAPPID +STARTF_TITLEISLINKNAME +STARTF_UNTRUSTEDSOURCE +STARTF_USECOUNTCHARS +STARTF_USEFILLATTRIBUTE +STARTF_USEHOTKEY +STARTF_USEPOSITION +STARTF_USESHOWWINDOW +STARTF_USESIZE +STARTF_USESTDHANDLES +STARTUPINFOEXW +STARTUPINFOW +STARTUPINFOW_FLAGS +STATUS_DELETE_PENDING +STATUS_DIRECTORY_NOT_EMPTY +STATUS_END_OF_FILE +STATUS_FILE_DELETED +STATUS_INVALID_HANDLE +STATUS_INVALID_PARAMETER +STATUS_NOT_IMPLEMENTED +STATUS_PENDING +STATUS_SHARING_VIOLATION +STATUS_SUCCESS +STD_ERROR_HANDLE +STD_HANDLE +STD_INPUT_HANDLE +STD_OUTPUT_HANDLE +SwitchToThread +SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE +SYMBOLIC_LINK_FLAG_DIRECTORY +SYMBOLIC_LINK_FLAGS +SYMLINK_FLAG_RELATIVE +SYNCHRONIZE +SYSTEM_INFO +TCP_NODELAY +TerminateProcess +THREAD_CREATE_RUN_IMMEDIATELY +THREAD_CREATE_SUSPENDED +THREAD_CREATION_FLAGS +TIMER_ALL_ACCESS +TIMER_MODIFY_STATE +TIMEVAL +TLS_OUT_OF_INDEXES +TlsAlloc +TlsFree +TlsGetValue +TlsSetValue +TOKEN_ACCESS_MASK +TOKEN_ACCESS_PSEUDO_HANDLE +TOKEN_ACCESS_PSEUDO_HANDLE_WIN8 +TOKEN_ACCESS_SYSTEM_SECURITY +TOKEN_ADJUST_DEFAULT +TOKEN_ADJUST_GROUPS +TOKEN_ADJUST_PRIVILEGES +TOKEN_ADJUST_SESSIONID +TOKEN_ALL_ACCESS +TOKEN_ASSIGN_PRIMARY +TOKEN_DELETE +TOKEN_DUPLICATE +TOKEN_EXECUTE +TOKEN_IMPERSONATE +TOKEN_QUERY +TOKEN_QUERY_SOURCE +TOKEN_READ +TOKEN_READ_CONTROL +TOKEN_TRUST_CONSTRAINT_MASK +TOKEN_WRITE +TOKEN_WRITE_DAC +TOKEN_WRITE_OWNER +TRUE +TRUNCATE_EXISTING +TryAcquireSRWLockExclusive +TryAcquireSRWLockShared +UNICODE_STRING +UnlockFile +UpdateProcThreadAttribute +VOLUME_NAME_DOS +VOLUME_NAME_GUID +VOLUME_NAME_NONE +WAIT_ABANDONED +WAIT_ABANDONED_0 +WAIT_FAILED +WAIT_IO_COMPLETION +WAIT_OBJECT_0 +WAIT_TIMEOUT +WaitForMultipleObjects +WaitForSingleObject +WakeAllConditionVariable +WakeConditionVariable +WC_ERR_INVALID_CHARS +WideCharToMultiByte +WIN32_ERROR +WIN32_FIND_DATAW Windows.Wdk.Storage.FileSystem.FILE_NO_COMPRESSION -Windows.Wdk.Storage.FileSystem.FILE_NO_EA_KNOWLEDGE -Windows.Wdk.Storage.FileSystem.FILE_NO_INTERMEDIATE_BUFFERING -Windows.Wdk.Storage.FileSystem.FILE_NON_DIRECTORY_FILE -Windows.Wdk.Storage.FileSystem.FILE_OPEN -Windows.Wdk.Storage.FileSystem.FILE_OPEN_BY_FILE_ID -Windows.Wdk.Storage.FileSystem.FILE_OPEN_FOR_BACKUP_INTENT -Windows.Wdk.Storage.FileSystem.FILE_OPEN_FOR_FREE_SPACE_QUERY -Windows.Wdk.Storage.FileSystem.FILE_OPEN_IF Windows.Wdk.Storage.FileSystem.FILE_OPEN_NO_RECALL -Windows.Wdk.Storage.FileSystem.FILE_OPEN_REPARSE_POINT -Windows.Wdk.Storage.FileSystem.FILE_OPEN_REQUIRING_OPLOCK -Windows.Wdk.Storage.FileSystem.FILE_OVERWRITE -Windows.Wdk.Storage.FileSystem.FILE_OVERWRITE_IF -Windows.Wdk.Storage.FileSystem.FILE_RANDOM_ACCESS -Windows.Wdk.Storage.FileSystem.FILE_RESERVE_OPFILTER -Windows.Wdk.Storage.FileSystem.FILE_SEQUENTIAL_ONLY -Windows.Wdk.Storage.FileSystem.FILE_SESSION_AWARE -Windows.Wdk.Storage.FileSystem.FILE_SUPERSEDE -Windows.Wdk.Storage.FileSystem.FILE_SYNCHRONOUS_IO_ALERT -Windows.Wdk.Storage.FileSystem.FILE_SYNCHRONOUS_IO_NONALERT -Windows.Wdk.Storage.FileSystem.FILE_WRITE_THROUGH -Windows.Wdk.Storage.FileSystem.NtCreateFile -Windows.Wdk.Storage.FileSystem.NTCREATEFILE_CREATE_DISPOSITION -Windows.Wdk.Storage.FileSystem.NTCREATEFILE_CREATE_OPTIONS -Windows.Wdk.Storage.FileSystem.NtOpenFile -Windows.Wdk.Storage.FileSystem.NtReadFile -Windows.Wdk.Storage.FileSystem.NtWriteFile -Windows.Wdk.Storage.FileSystem.SYMLINK_FLAG_RELATIVE -Windows.Win32.Foundation.BOOL -Windows.Win32.Foundation.BOOLEAN -Windows.Win32.Foundation.CloseHandle -Windows.Win32.Foundation.DNS_ERROR_ADDRESS_REQUIRED -Windows.Win32.Foundation.DNS_ERROR_ALIAS_LOOP -Windows.Win32.Foundation.DNS_ERROR_AUTOZONE_ALREADY_EXISTS -Windows.Win32.Foundation.DNS_ERROR_AXFR -Windows.Win32.Foundation.DNS_ERROR_BACKGROUND_LOADING -Windows.Win32.Foundation.DNS_ERROR_BAD_KEYMASTER -Windows.Win32.Foundation.DNS_ERROR_BAD_PACKET -Windows.Win32.Foundation.DNS_ERROR_CANNOT_FIND_ROOT_HINTS -Windows.Win32.Foundation.DNS_ERROR_CLIENT_SUBNET_ALREADY_EXISTS -Windows.Win32.Foundation.DNS_ERROR_CLIENT_SUBNET_DOES_NOT_EXIST -Windows.Win32.Foundation.DNS_ERROR_CLIENT_SUBNET_IS_ACCESSED -Windows.Win32.Foundation.DNS_ERROR_CNAME_COLLISION -Windows.Win32.Foundation.DNS_ERROR_CNAME_LOOP -Windows.Win32.Foundation.DNS_ERROR_DATAFILE_OPEN_FAILURE -Windows.Win32.Foundation.DNS_ERROR_DATAFILE_PARSING -Windows.Win32.Foundation.DNS_ERROR_DEFAULT_SCOPE -Windows.Win32.Foundation.DNS_ERROR_DEFAULT_VIRTUALIZATION_INSTANCE -Windows.Win32.Foundation.DNS_ERROR_DEFAULT_ZONESCOPE -Windows.Win32.Foundation.DNS_ERROR_DELEGATION_REQUIRED -Windows.Win32.Foundation.DNS_ERROR_DNAME_COLLISION -Windows.Win32.Foundation.DNS_ERROR_DNSSEC_IS_DISABLED -Windows.Win32.Foundation.DNS_ERROR_DP_ALREADY_ENLISTED -Windows.Win32.Foundation.DNS_ERROR_DP_ALREADY_EXISTS -Windows.Win32.Foundation.DNS_ERROR_DP_DOES_NOT_EXIST -Windows.Win32.Foundation.DNS_ERROR_DP_FSMO_ERROR -Windows.Win32.Foundation.DNS_ERROR_DP_NOT_AVAILABLE -Windows.Win32.Foundation.DNS_ERROR_DP_NOT_ENLISTED -Windows.Win32.Foundation.DNS_ERROR_DS_UNAVAILABLE -Windows.Win32.Foundation.DNS_ERROR_DS_ZONE_ALREADY_EXISTS -Windows.Win32.Foundation.DNS_ERROR_DWORD_VALUE_TOO_LARGE -Windows.Win32.Foundation.DNS_ERROR_DWORD_VALUE_TOO_SMALL -Windows.Win32.Foundation.DNS_ERROR_FILE_WRITEBACK_FAILED -Windows.Win32.Foundation.DNS_ERROR_FORWARDER_ALREADY_EXISTS -Windows.Win32.Foundation.DNS_ERROR_INCONSISTENT_ROOT_HINTS -Windows.Win32.Foundation.DNS_ERROR_INVAILD_VIRTUALIZATION_INSTANCE_NAME -Windows.Win32.Foundation.DNS_ERROR_INVALID_CLIENT_SUBNET_NAME -Windows.Win32.Foundation.DNS_ERROR_INVALID_DATA -Windows.Win32.Foundation.DNS_ERROR_INVALID_DATAFILE_NAME -Windows.Win32.Foundation.DNS_ERROR_INVALID_INITIAL_ROLLOVER_OFFSET -Windows.Win32.Foundation.DNS_ERROR_INVALID_IP_ADDRESS -Windows.Win32.Foundation.DNS_ERROR_INVALID_KEY_SIZE -Windows.Win32.Foundation.DNS_ERROR_INVALID_NAME -Windows.Win32.Foundation.DNS_ERROR_INVALID_NAME_CHAR -Windows.Win32.Foundation.DNS_ERROR_INVALID_NSEC3_ITERATION_COUNT -Windows.Win32.Foundation.DNS_ERROR_INVALID_POLICY_TABLE -Windows.Win32.Foundation.DNS_ERROR_INVALID_PROPERTY -Windows.Win32.Foundation.DNS_ERROR_INVALID_ROLLOVER_PERIOD -Windows.Win32.Foundation.DNS_ERROR_INVALID_SCOPE_NAME -Windows.Win32.Foundation.DNS_ERROR_INVALID_SCOPE_OPERATION -Windows.Win32.Foundation.DNS_ERROR_INVALID_SIGNATURE_VALIDITY_PERIOD -Windows.Win32.Foundation.DNS_ERROR_INVALID_TYPE -Windows.Win32.Foundation.DNS_ERROR_INVALID_XML -Windows.Win32.Foundation.DNS_ERROR_INVALID_ZONE_OPERATION -Windows.Win32.Foundation.DNS_ERROR_INVALID_ZONE_TYPE -Windows.Win32.Foundation.DNS_ERROR_INVALID_ZONESCOPE_NAME -Windows.Win32.Foundation.DNS_ERROR_KEYMASTER_REQUIRED -Windows.Win32.Foundation.DNS_ERROR_KSP_DOES_NOT_SUPPORT_PROTECTION -Windows.Win32.Foundation.DNS_ERROR_KSP_NOT_ACCESSIBLE -Windows.Win32.Foundation.DNS_ERROR_LOAD_ZONESCOPE_FAILED -Windows.Win32.Foundation.DNS_ERROR_NAME_DOES_NOT_EXIST -Windows.Win32.Foundation.DNS_ERROR_NAME_NOT_IN_ZONE -Windows.Win32.Foundation.DNS_ERROR_NBSTAT_INIT_FAILED -Windows.Win32.Foundation.DNS_ERROR_NEED_SECONDARY_ADDRESSES -Windows.Win32.Foundation.DNS_ERROR_NEED_WINS_SERVERS -Windows.Win32.Foundation.DNS_ERROR_NO_BOOTFILE_IF_DS_ZONE -Windows.Win32.Foundation.DNS_ERROR_NO_CREATE_CACHE_DATA -Windows.Win32.Foundation.DNS_ERROR_NO_DNS_SERVERS -Windows.Win32.Foundation.DNS_ERROR_NO_MEMORY -Windows.Win32.Foundation.DNS_ERROR_NO_PACKET -Windows.Win32.Foundation.DNS_ERROR_NO_TCPIP -Windows.Win32.Foundation.DNS_ERROR_NO_VALID_TRUST_ANCHORS -Windows.Win32.Foundation.DNS_ERROR_NO_ZONE_INFO -Windows.Win32.Foundation.DNS_ERROR_NODE_CREATION_FAILED -Windows.Win32.Foundation.DNS_ERROR_NODE_IS_CNAME -Windows.Win32.Foundation.DNS_ERROR_NODE_IS_DNAME -Windows.Win32.Foundation.DNS_ERROR_NON_RFC_NAME -Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_ON_ACTIVE_SKD -Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_ON_RODC -Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_ON_ROOT_SERVER -Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_ON_SIGNED_ZONE -Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_ON_UNSIGNED_ZONE -Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_ON_ZSK -Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_UNDER_DELEGATION -Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_UNDER_DNAME -Windows.Win32.Foundation.DNS_ERROR_NOT_ALLOWED_WITH_ZONESCOPES -Windows.Win32.Foundation.DNS_ERROR_NOT_ENOUGH_SIGNING_KEY_DESCRIPTORS -Windows.Win32.Foundation.DNS_ERROR_NOT_UNIQUE -Windows.Win32.Foundation.DNS_ERROR_NSEC3_INCOMPATIBLE_WITH_RSA_SHA1 -Windows.Win32.Foundation.DNS_ERROR_NSEC3_NAME_COLLISION -Windows.Win32.Foundation.DNS_ERROR_NSEC_INCOMPATIBLE_WITH_NSEC3_RSA_SHA1 -Windows.Win32.Foundation.DNS_ERROR_NUMERIC_NAME -Windows.Win32.Foundation.DNS_ERROR_POLICY_ALREADY_EXISTS -Windows.Win32.Foundation.DNS_ERROR_POLICY_DOES_NOT_EXIST -Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_CRITERIA -Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_CRITERIA_CLIENT_SUBNET -Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_CRITERIA_FQDN -Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_CRITERIA_INTERFACE -Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_CRITERIA_NETWORK_PROTOCOL -Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_CRITERIA_QUERY_TYPE -Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_CRITERIA_TIME_OF_DAY -Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_CRITERIA_TRANSPORT_PROTOCOL -Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_NAME -Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_SETTINGS -Windows.Win32.Foundation.DNS_ERROR_POLICY_INVALID_WEIGHT -Windows.Win32.Foundation.DNS_ERROR_POLICY_LOCKED -Windows.Win32.Foundation.DNS_ERROR_POLICY_MISSING_CRITERIA -Windows.Win32.Foundation.DNS_ERROR_POLICY_PROCESSING_ORDER_INVALID -Windows.Win32.Foundation.DNS_ERROR_POLICY_SCOPE_MISSING -Windows.Win32.Foundation.DNS_ERROR_POLICY_SCOPE_NOT_ALLOWED -Windows.Win32.Foundation.DNS_ERROR_PRIMARY_REQUIRES_DATAFILE -Windows.Win32.Foundation.DNS_ERROR_RCODE -Windows.Win32.Foundation.DNS_ERROR_RCODE_BADKEY -Windows.Win32.Foundation.DNS_ERROR_RCODE_BADSIG -Windows.Win32.Foundation.DNS_ERROR_RCODE_BADTIME -Windows.Win32.Foundation.DNS_ERROR_RCODE_FORMAT_ERROR -Windows.Win32.Foundation.DNS_ERROR_RCODE_NAME_ERROR -Windows.Win32.Foundation.DNS_ERROR_RCODE_NOT_IMPLEMENTED -Windows.Win32.Foundation.DNS_ERROR_RCODE_NOTAUTH -Windows.Win32.Foundation.DNS_ERROR_RCODE_NOTZONE -Windows.Win32.Foundation.DNS_ERROR_RCODE_NXRRSET -Windows.Win32.Foundation.DNS_ERROR_RCODE_REFUSED -Windows.Win32.Foundation.DNS_ERROR_RCODE_SERVER_FAILURE -Windows.Win32.Foundation.DNS_ERROR_RCODE_YXDOMAIN -Windows.Win32.Foundation.DNS_ERROR_RCODE_YXRRSET -Windows.Win32.Foundation.DNS_ERROR_RECORD_ALREADY_EXISTS -Windows.Win32.Foundation.DNS_ERROR_RECORD_DOES_NOT_EXIST -Windows.Win32.Foundation.DNS_ERROR_RECORD_FORMAT -Windows.Win32.Foundation.DNS_ERROR_RECORD_ONLY_AT_ZONE_ROOT -Windows.Win32.Foundation.DNS_ERROR_RECORD_TIMED_OUT -Windows.Win32.Foundation.DNS_ERROR_ROLLOVER_ALREADY_QUEUED -Windows.Win32.Foundation.DNS_ERROR_ROLLOVER_IN_PROGRESS -Windows.Win32.Foundation.DNS_ERROR_ROLLOVER_NOT_POKEABLE -Windows.Win32.Foundation.DNS_ERROR_RRL_INVALID_IPV4_PREFIX -Windows.Win32.Foundation.DNS_ERROR_RRL_INVALID_IPV6_PREFIX -Windows.Win32.Foundation.DNS_ERROR_RRL_INVALID_LEAK_RATE -Windows.Win32.Foundation.DNS_ERROR_RRL_INVALID_TC_RATE -Windows.Win32.Foundation.DNS_ERROR_RRL_INVALID_WINDOW_SIZE -Windows.Win32.Foundation.DNS_ERROR_RRL_LEAK_RATE_LESSTHAN_TC_RATE -Windows.Win32.Foundation.DNS_ERROR_RRL_NOT_ENABLED -Windows.Win32.Foundation.DNS_ERROR_SCOPE_ALREADY_EXISTS -Windows.Win32.Foundation.DNS_ERROR_SCOPE_DOES_NOT_EXIST -Windows.Win32.Foundation.DNS_ERROR_SCOPE_LOCKED -Windows.Win32.Foundation.DNS_ERROR_SECONDARY_DATA -Windows.Win32.Foundation.DNS_ERROR_SECONDARY_REQUIRES_MASTER_IP -Windows.Win32.Foundation.DNS_ERROR_SERVERSCOPE_IS_REFERENCED -Windows.Win32.Foundation.DNS_ERROR_SIGNING_KEY_NOT_ACCESSIBLE -Windows.Win32.Foundation.DNS_ERROR_SOA_DELETE_INVALID -Windows.Win32.Foundation.DNS_ERROR_STANDBY_KEY_NOT_PRESENT -Windows.Win32.Foundation.DNS_ERROR_SUBNET_ALREADY_EXISTS -Windows.Win32.Foundation.DNS_ERROR_SUBNET_DOES_NOT_EXIST -Windows.Win32.Foundation.DNS_ERROR_TOO_MANY_SKDS -Windows.Win32.Foundation.DNS_ERROR_TRY_AGAIN_LATER -Windows.Win32.Foundation.DNS_ERROR_UNEXPECTED_CNG_ERROR -Windows.Win32.Foundation.DNS_ERROR_UNEXPECTED_DATA_PROTECTION_ERROR -Windows.Win32.Foundation.DNS_ERROR_UNKNOWN_RECORD_TYPE -Windows.Win32.Foundation.DNS_ERROR_UNKNOWN_SIGNING_PARAMETER_VERSION -Windows.Win32.Foundation.DNS_ERROR_UNSECURE_PACKET -Windows.Win32.Foundation.DNS_ERROR_UNSUPPORTED_ALGORITHM -Windows.Win32.Foundation.DNS_ERROR_VIRTUALIZATION_INSTANCE_ALREADY_EXISTS -Windows.Win32.Foundation.DNS_ERROR_VIRTUALIZATION_INSTANCE_DOES_NOT_EXIST -Windows.Win32.Foundation.DNS_ERROR_VIRTUALIZATION_TREE_LOCKED -Windows.Win32.Foundation.DNS_ERROR_WINS_INIT_FAILED -Windows.Win32.Foundation.DNS_ERROR_ZONE_ALREADY_EXISTS -Windows.Win32.Foundation.DNS_ERROR_ZONE_CONFIGURATION_ERROR -Windows.Win32.Foundation.DNS_ERROR_ZONE_CREATION_FAILED -Windows.Win32.Foundation.DNS_ERROR_ZONE_DOES_NOT_EXIST -Windows.Win32.Foundation.DNS_ERROR_ZONE_HAS_NO_NS_RECORDS -Windows.Win32.Foundation.DNS_ERROR_ZONE_HAS_NO_SOA_RECORD -Windows.Win32.Foundation.DNS_ERROR_ZONE_IS_SHUTDOWN -Windows.Win32.Foundation.DNS_ERROR_ZONE_LOCKED -Windows.Win32.Foundation.DNS_ERROR_ZONE_LOCKED_FOR_SIGNING -Windows.Win32.Foundation.DNS_ERROR_ZONE_NOT_SECONDARY -Windows.Win32.Foundation.DNS_ERROR_ZONE_REQUIRES_MASTER_IP -Windows.Win32.Foundation.DNS_ERROR_ZONESCOPE_ALREADY_EXISTS -Windows.Win32.Foundation.DNS_ERROR_ZONESCOPE_DOES_NOT_EXIST -Windows.Win32.Foundation.DNS_ERROR_ZONESCOPE_FILE_WRITEBACK_FAILED -Windows.Win32.Foundation.DNS_ERROR_ZONESCOPE_IS_REFERENCED -Windows.Win32.Foundation.DUPLICATE_CLOSE_SOURCE -Windows.Win32.Foundation.DUPLICATE_HANDLE_OPTIONS -Windows.Win32.Foundation.DUPLICATE_SAME_ACCESS -Windows.Win32.Foundation.DuplicateHandle -Windows.Win32.Foundation.E_NOTIMPL -Windows.Win32.Foundation.ERROR_ABANDON_HIBERFILE -Windows.Win32.Foundation.ERROR_ABANDONED_WAIT_0 -Windows.Win32.Foundation.ERROR_ABANDONED_WAIT_63 -Windows.Win32.Foundation.ERROR_ABIOS_ERROR -Windows.Win32.Foundation.ERROR_ACCESS_AUDIT_BY_POLICY -Windows.Win32.Foundation.ERROR_ACCESS_DENIED -Windows.Win32.Foundation.ERROR_ACCESS_DENIED_APPDATA -Windows.Win32.Foundation.ERROR_ACCESS_DISABLED_BY_POLICY -Windows.Win32.Foundation.ERROR_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY -Windows.Win32.Foundation.ERROR_ACCESS_DISABLED_WEBBLADE -Windows.Win32.Foundation.ERROR_ACCESS_DISABLED_WEBBLADE_TAMPER -Windows.Win32.Foundation.ERROR_ACCOUNT_DISABLED -Windows.Win32.Foundation.ERROR_ACCOUNT_EXPIRED -Windows.Win32.Foundation.ERROR_ACCOUNT_LOCKED_OUT -Windows.Win32.Foundation.ERROR_ACCOUNT_RESTRICTION -Windows.Win32.Foundation.ERROR_ACPI_ERROR -Windows.Win32.Foundation.ERROR_ACTIVE_CONNECTIONS -Windows.Win32.Foundation.ERROR_ADAP_HDW_ERR -Windows.Win32.Foundation.ERROR_ADDRESS_ALREADY_ASSOCIATED -Windows.Win32.Foundation.ERROR_ADDRESS_NOT_ASSOCIATED -Windows.Win32.Foundation.ERROR_ALERTED -Windows.Win32.Foundation.ERROR_ALIAS_EXISTS -Windows.Win32.Foundation.ERROR_ALL_USER_TRUST_QUOTA_EXCEEDED -Windows.Win32.Foundation.ERROR_ALLOCATE_BUCKET -Windows.Win32.Foundation.ERROR_ALLOTTED_SPACE_EXCEEDED -Windows.Win32.Foundation.ERROR_ALREADY_ASSIGNED -Windows.Win32.Foundation.ERROR_ALREADY_EXISTS -Windows.Win32.Foundation.ERROR_ALREADY_FIBER -Windows.Win32.Foundation.ERROR_ALREADY_HAS_STREAM_ID -Windows.Win32.Foundation.ERROR_ALREADY_INITIALIZED -Windows.Win32.Foundation.ERROR_ALREADY_REGISTERED -Windows.Win32.Foundation.ERROR_ALREADY_RUNNING_LKG -Windows.Win32.Foundation.ERROR_ALREADY_THREAD -Windows.Win32.Foundation.ERROR_ALREADY_WAITING -Windows.Win32.Foundation.ERROR_ALREADY_WIN32 -Windows.Win32.Foundation.ERROR_API_UNAVAILABLE -Windows.Win32.Foundation.ERROR_APP_HANG -Windows.Win32.Foundation.ERROR_APP_INIT_FAILURE -Windows.Win32.Foundation.ERROR_APP_WRONG_OS -Windows.Win32.Foundation.ERROR_APPCONTAINER_REQUIRED -Windows.Win32.Foundation.ERROR_APPEXEC_APP_COMPAT_BLOCK -Windows.Win32.Foundation.ERROR_APPEXEC_CALLER_WAIT_TIMEOUT -Windows.Win32.Foundation.ERROR_APPEXEC_CALLER_WAIT_TIMEOUT_LICENSING -Windows.Win32.Foundation.ERROR_APPEXEC_CALLER_WAIT_TIMEOUT_RESOURCES -Windows.Win32.Foundation.ERROR_APPEXEC_CALLER_WAIT_TIMEOUT_TERMINATION -Windows.Win32.Foundation.ERROR_APPEXEC_CONDITION_NOT_SATISFIED -Windows.Win32.Foundation.ERROR_APPEXEC_HANDLE_INVALIDATED -Windows.Win32.Foundation.ERROR_APPEXEC_HOST_ID_MISMATCH -Windows.Win32.Foundation.ERROR_APPEXEC_INVALID_HOST_GENERATION -Windows.Win32.Foundation.ERROR_APPEXEC_INVALID_HOST_STATE -Windows.Win32.Foundation.ERROR_APPEXEC_NO_DONOR -Windows.Win32.Foundation.ERROR_APPEXEC_UNEXPECTED_PROCESS_REGISTRATION -Windows.Win32.Foundation.ERROR_APPEXEC_UNKNOWN_USER -Windows.Win32.Foundation.ERROR_APPHELP_BLOCK -Windows.Win32.Foundation.ERROR_APPX_FILE_NOT_ENCRYPTED -Windows.Win32.Foundation.ERROR_ARBITRATION_UNHANDLED -Windows.Win32.Foundation.ERROR_ARENA_TRASHED -Windows.Win32.Foundation.ERROR_ARITHMETIC_OVERFLOW -Windows.Win32.Foundation.ERROR_ASSERTION_FAILURE -Windows.Win32.Foundation.ERROR_ATOMIC_LOCKS_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_AUDIT_FAILED -Windows.Win32.Foundation.ERROR_AUTHENTICATION_FIREWALL_FAILED -Windows.Win32.Foundation.ERROR_AUTHIP_FAILURE -Windows.Win32.Foundation.ERROR_AUTODATASEG_EXCEEDS_64k -Windows.Win32.Foundation.ERROR_BACKUP_CONTROLLER -Windows.Win32.Foundation.ERROR_BAD_ACCESSOR_FLAGS -Windows.Win32.Foundation.ERROR_BAD_ARGUMENTS -Windows.Win32.Foundation.ERROR_BAD_COMMAND -Windows.Win32.Foundation.ERROR_BAD_COMPRESSION_BUFFER -Windows.Win32.Foundation.ERROR_BAD_CONFIGURATION -Windows.Win32.Foundation.ERROR_BAD_CURRENT_DIRECTORY -Windows.Win32.Foundation.ERROR_BAD_DESCRIPTOR_FORMAT -Windows.Win32.Foundation.ERROR_BAD_DEV_TYPE -Windows.Win32.Foundation.ERROR_BAD_DEVICE -Windows.Win32.Foundation.ERROR_BAD_DEVICE_PATH -Windows.Win32.Foundation.ERROR_BAD_DLL_ENTRYPOINT -Windows.Win32.Foundation.ERROR_BAD_DRIVER_LEVEL -Windows.Win32.Foundation.ERROR_BAD_ENVIRONMENT -Windows.Win32.Foundation.ERROR_BAD_EXE_FORMAT -Windows.Win32.Foundation.ERROR_BAD_FILE_TYPE -Windows.Win32.Foundation.ERROR_BAD_FORMAT -Windows.Win32.Foundation.ERROR_BAD_FUNCTION_TABLE -Windows.Win32.Foundation.ERROR_BAD_IMPERSONATION_LEVEL -Windows.Win32.Foundation.ERROR_BAD_INHERITANCE_ACL -Windows.Win32.Foundation.ERROR_BAD_LENGTH -Windows.Win32.Foundation.ERROR_BAD_LOGON_SESSION_STATE -Windows.Win32.Foundation.ERROR_BAD_MCFG_TABLE -Windows.Win32.Foundation.ERROR_BAD_NET_NAME -Windows.Win32.Foundation.ERROR_BAD_NET_RESP -Windows.Win32.Foundation.ERROR_BAD_NETPATH -Windows.Win32.Foundation.ERROR_BAD_PATHNAME -Windows.Win32.Foundation.ERROR_BAD_PIPE -Windows.Win32.Foundation.ERROR_BAD_PROFILE -Windows.Win32.Foundation.ERROR_BAD_PROVIDER -Windows.Win32.Foundation.ERROR_BAD_QUERY_SYNTAX -Windows.Win32.Foundation.ERROR_BAD_RECOVERY_POLICY -Windows.Win32.Foundation.ERROR_BAD_REM_ADAP -Windows.Win32.Foundation.ERROR_BAD_SERVICE_ENTRYPOINT -Windows.Win32.Foundation.ERROR_BAD_STACK -Windows.Win32.Foundation.ERROR_BAD_THREADID_ADDR -Windows.Win32.Foundation.ERROR_BAD_TOKEN_TYPE -Windows.Win32.Foundation.ERROR_BAD_UNIT -Windows.Win32.Foundation.ERROR_BAD_USER_PROFILE -Windows.Win32.Foundation.ERROR_BAD_USERNAME -Windows.Win32.Foundation.ERROR_BAD_VALIDATION_CLASS -Windows.Win32.Foundation.ERROR_BADDB -Windows.Win32.Foundation.ERROR_BADKEY -Windows.Win32.Foundation.ERROR_BADSTARTPOSITION -Windows.Win32.Foundation.ERROR_BEGINNING_OF_MEDIA -Windows.Win32.Foundation.ERROR_BEYOND_VDL -Windows.Win32.Foundation.ERROR_BIOS_FAILED_TO_CONNECT_INTERRUPT -Windows.Win32.Foundation.ERROR_BLOCK_SHARED -Windows.Win32.Foundation.ERROR_BLOCK_SOURCE_WEAK_REFERENCE_INVALID -Windows.Win32.Foundation.ERROR_BLOCK_TARGET_WEAK_REFERENCE_INVALID -Windows.Win32.Foundation.ERROR_BLOCK_TOO_MANY_REFERENCES -Windows.Win32.Foundation.ERROR_BLOCK_WEAK_REFERENCE_INVALID -Windows.Win32.Foundation.ERROR_BLOCKED_BY_PARENTAL_CONTROLS -Windows.Win32.Foundation.ERROR_BOOT_ALREADY_ACCEPTED -Windows.Win32.Foundation.ERROR_BROKEN_PIPE -Windows.Win32.Foundation.ERROR_BUFFER_ALL_ZEROS -Windows.Win32.Foundation.ERROR_BUFFER_OVERFLOW -Windows.Win32.Foundation.ERROR_BUS_RESET -Windows.Win32.Foundation.ERROR_BUSY -Windows.Win32.Foundation.ERROR_BUSY_DRIVE -Windows.Win32.Foundation.ERROR_BYPASSIO_FLT_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_CACHE_PAGE_LOCKED -Windows.Win32.Foundation.ERROR_CALL_NOT_IMPLEMENTED -Windows.Win32.Foundation.ERROR_CALLBACK_INVOKE_INLINE -Windows.Win32.Foundation.ERROR_CALLBACK_POP_STACK -Windows.Win32.Foundation.ERROR_CALLBACK_SUPPLIED_INVALID_DATA -Windows.Win32.Foundation.ERROR_CAN_NOT_COMPLETE -Windows.Win32.Foundation.ERROR_CANCEL_VIOLATION -Windows.Win32.Foundation.ERROR_CANCELLED -Windows.Win32.Foundation.ERROR_CANNOT_BREAK_OPLOCK -Windows.Win32.Foundation.ERROR_CANNOT_COPY -Windows.Win32.Foundation.ERROR_CANNOT_DETECT_DRIVER_FAILURE -Windows.Win32.Foundation.ERROR_CANNOT_DETECT_PROCESS_ABORT -Windows.Win32.Foundation.ERROR_CANNOT_FIND_WND_CLASS -Windows.Win32.Foundation.ERROR_CANNOT_GRANT_REQUESTED_OPLOCK -Windows.Win32.Foundation.ERROR_CANNOT_IMPERSONATE -Windows.Win32.Foundation.ERROR_CANNOT_LOAD_REGISTRY_FILE -Windows.Win32.Foundation.ERROR_CANNOT_MAKE -Windows.Win32.Foundation.ERROR_CANNOT_OPEN_PROFILE -Windows.Win32.Foundation.ERROR_CANT_ACCESS_DOMAIN_INFO -Windows.Win32.Foundation.ERROR_CANT_ACCESS_FILE -Windows.Win32.Foundation.ERROR_CANT_CLEAR_ENCRYPTION_FLAG -Windows.Win32.Foundation.ERROR_CANT_DISABLE_MANDATORY -Windows.Win32.Foundation.ERROR_CANT_ENABLE_DENY_ONLY -Windows.Win32.Foundation.ERROR_CANT_OPEN_ANONYMOUS -Windows.Win32.Foundation.ERROR_CANT_RESOLVE_FILENAME -Windows.Win32.Foundation.ERROR_CANT_TERMINATE_SELF -Windows.Win32.Foundation.ERROR_CANT_WAIT -Windows.Win32.Foundation.ERROR_CANTFETCHBACKWARDS -Windows.Win32.Foundation.ERROR_CANTOPEN -Windows.Win32.Foundation.ERROR_CANTREAD -Windows.Win32.Foundation.ERROR_CANTSCROLLBACKWARDS -Windows.Win32.Foundation.ERROR_CANTWRITE -Windows.Win32.Foundation.ERROR_CAPAUTHZ_CHANGE_TYPE -Windows.Win32.Foundation.ERROR_CAPAUTHZ_DB_CORRUPTED -Windows.Win32.Foundation.ERROR_CAPAUTHZ_NO_POLICY -Windows.Win32.Foundation.ERROR_CAPAUTHZ_NOT_AUTHORIZED -Windows.Win32.Foundation.ERROR_CAPAUTHZ_NOT_DEVUNLOCKED -Windows.Win32.Foundation.ERROR_CAPAUTHZ_NOT_PROVISIONED -Windows.Win32.Foundation.ERROR_CAPAUTHZ_SCCD_DEV_MODE_REQUIRED -Windows.Win32.Foundation.ERROR_CAPAUTHZ_SCCD_INVALID_CATALOG -Windows.Win32.Foundation.ERROR_CAPAUTHZ_SCCD_NO_AUTH_ENTITY -Windows.Win32.Foundation.ERROR_CAPAUTHZ_SCCD_NO_CAPABILITY_MATCH -Windows.Win32.Foundation.ERROR_CAPAUTHZ_SCCD_PARSE_ERROR -Windows.Win32.Foundation.ERROR_CARDBUS_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_CASE_DIFFERING_NAMES_IN_DIR -Windows.Win32.Foundation.ERROR_CASE_SENSITIVE_PATH -Windows.Win32.Foundation.ERROR_CERTIFICATE_VALIDATION_PREFERENCE_CONFLICT -Windows.Win32.Foundation.ERROR_CHECKING_FILE_SYSTEM -Windows.Win32.Foundation.ERROR_CHECKOUT_REQUIRED -Windows.Win32.Foundation.ERROR_CHILD_MUST_BE_VOLATILE -Windows.Win32.Foundation.ERROR_CHILD_NOT_COMPLETE -Windows.Win32.Foundation.ERROR_CHILD_PROCESS_BLOCKED -Windows.Win32.Foundation.ERROR_CHILD_WINDOW_MENU -Windows.Win32.Foundation.ERROR_CIMFS_IMAGE_CORRUPT -Windows.Win32.Foundation.ERROR_CIMFS_IMAGE_VERSION_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_CIRCULAR_DEPENDENCY -Windows.Win32.Foundation.ERROR_CLASS_ALREADY_EXISTS -Windows.Win32.Foundation.ERROR_CLASS_DOES_NOT_EXIST -Windows.Win32.Foundation.ERROR_CLASS_HAS_WINDOWS -Windows.Win32.Foundation.ERROR_CLIENT_SERVER_PARAMETERS_INVALID -Windows.Win32.Foundation.ERROR_CLIPBOARD_NOT_OPEN -Windows.Win32.Foundation.ERROR_CLOUD_FILE_ACCESS_DENIED -Windows.Win32.Foundation.ERROR_CLOUD_FILE_ALREADY_CONNECTED -Windows.Win32.Foundation.ERROR_CLOUD_FILE_AUTHENTICATION_FAILED -Windows.Win32.Foundation.ERROR_CLOUD_FILE_CONNECTED_PROVIDER_ONLY -Windows.Win32.Foundation.ERROR_CLOUD_FILE_DEHYDRATION_DISALLOWED -Windows.Win32.Foundation.ERROR_CLOUD_FILE_IN_USE -Windows.Win32.Foundation.ERROR_CLOUD_FILE_INCOMPATIBLE_HARDLINKS -Windows.Win32.Foundation.ERROR_CLOUD_FILE_INSUFFICIENT_RESOURCES -Windows.Win32.Foundation.ERROR_CLOUD_FILE_INVALID_REQUEST -Windows.Win32.Foundation.ERROR_CLOUD_FILE_METADATA_CORRUPT -Windows.Win32.Foundation.ERROR_CLOUD_FILE_METADATA_TOO_LARGE -Windows.Win32.Foundation.ERROR_CLOUD_FILE_NETWORK_UNAVAILABLE -Windows.Win32.Foundation.ERROR_CLOUD_FILE_NOT_IN_SYNC -Windows.Win32.Foundation.ERROR_CLOUD_FILE_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_CLOUD_FILE_NOT_UNDER_SYNC_ROOT -Windows.Win32.Foundation.ERROR_CLOUD_FILE_PINNED -Windows.Win32.Foundation.ERROR_CLOUD_FILE_PROPERTY_BLOB_CHECKSUM_MISMATCH -Windows.Win32.Foundation.ERROR_CLOUD_FILE_PROPERTY_BLOB_TOO_LARGE -Windows.Win32.Foundation.ERROR_CLOUD_FILE_PROPERTY_CORRUPT -Windows.Win32.Foundation.ERROR_CLOUD_FILE_PROPERTY_LOCK_CONFLICT -Windows.Win32.Foundation.ERROR_CLOUD_FILE_PROPERTY_VERSION_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_CLOUD_FILE_PROVIDER_NOT_RUNNING -Windows.Win32.Foundation.ERROR_CLOUD_FILE_PROVIDER_TERMINATED -Windows.Win32.Foundation.ERROR_CLOUD_FILE_READ_ONLY_VOLUME -Windows.Win32.Foundation.ERROR_CLOUD_FILE_REQUEST_ABORTED -Windows.Win32.Foundation.ERROR_CLOUD_FILE_REQUEST_CANCELED -Windows.Win32.Foundation.ERROR_CLOUD_FILE_REQUEST_TIMEOUT -Windows.Win32.Foundation.ERROR_CLOUD_FILE_SYNC_ROOT_METADATA_CORRUPT -Windows.Win32.Foundation.ERROR_CLOUD_FILE_TOO_MANY_PROPERTY_BLOBS -Windows.Win32.Foundation.ERROR_CLOUD_FILE_UNSUCCESSFUL -Windows.Win32.Foundation.ERROR_CLOUD_FILE_US_MESSAGE_TIMEOUT -Windows.Win32.Foundation.ERROR_CLOUD_FILE_VALIDATION_FAILED -Windows.Win32.Foundation.ERROR_COMMITMENT_LIMIT -Windows.Win32.Foundation.ERROR_COMMITMENT_MINIMUM -Windows.Win32.Foundation.ERROR_COMPRESSED_FILE_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_COMPRESSION_DISABLED -Windows.Win32.Foundation.ERROR_COMPRESSION_NOT_BENEFICIAL -Windows.Win32.Foundation.ERROR_CONNECTED_OTHER_PASSWORD -Windows.Win32.Foundation.ERROR_CONNECTED_OTHER_PASSWORD_DEFAULT -Windows.Win32.Foundation.ERROR_CONNECTION_ABORTED -Windows.Win32.Foundation.ERROR_CONNECTION_ACTIVE -Windows.Win32.Foundation.ERROR_CONNECTION_COUNT_LIMIT -Windows.Win32.Foundation.ERROR_CONNECTION_INVALID -Windows.Win32.Foundation.ERROR_CONNECTION_REFUSED -Windows.Win32.Foundation.ERROR_CONNECTION_UNAVAIL -Windows.Win32.Foundation.ERROR_CONTAINER_ASSIGNED -Windows.Win32.Foundation.ERROR_CONTENT_BLOCKED -Windows.Win32.Foundation.ERROR_CONTEXT_EXPIRED -Windows.Win32.Foundation.ERROR_CONTINUE -Windows.Win32.Foundation.ERROR_CONTROL_C_EXIT -Windows.Win32.Foundation.ERROR_CONTROL_ID_NOT_FOUND -Windows.Win32.Foundation.ERROR_CONVERT_TO_LARGE -Windows.Win32.Foundation.ERROR_CORRUPT_LOG_CLEARED -Windows.Win32.Foundation.ERROR_CORRUPT_LOG_CORRUPTED -Windows.Win32.Foundation.ERROR_CORRUPT_LOG_DELETED_FULL -Windows.Win32.Foundation.ERROR_CORRUPT_LOG_OVERFULL -Windows.Win32.Foundation.ERROR_CORRUPT_LOG_UNAVAILABLE -Windows.Win32.Foundation.ERROR_CORRUPT_SYSTEM_FILE -Windows.Win32.Foundation.ERROR_COULD_NOT_INTERPRET -Windows.Win32.Foundation.ERROR_COUNTER_TIMEOUT -Windows.Win32.Foundation.ERROR_CPU_SET_INVALID -Windows.Win32.Foundation.ERROR_CRASH_DUMP -Windows.Win32.Foundation.ERROR_CRC -Windows.Win32.Foundation.ERROR_CREATE_FAILED -Windows.Win32.Foundation.ERROR_CROSS_PARTITION_VIOLATION -Windows.Win32.Foundation.ERROR_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE -Windows.Win32.Foundation.ERROR_CS_ENCRYPTION_FILE_NOT_CSE -Windows.Win32.Foundation.ERROR_CS_ENCRYPTION_INVALID_SERVER_RESPONSE -Windows.Win32.Foundation.ERROR_CS_ENCRYPTION_NEW_ENCRYPTED_FILE -Windows.Win32.Foundation.ERROR_CS_ENCRYPTION_UNSUPPORTED_SERVER -Windows.Win32.Foundation.ERROR_CSCSHARE_OFFLINE -Windows.Win32.Foundation.ERROR_CTX_CLIENT_QUERY_TIMEOUT -Windows.Win32.Foundation.ERROR_CTX_MODEM_RESPONSE_TIMEOUT -Windows.Win32.Foundation.ERROR_CURRENT_DIRECTORY -Windows.Win32.Foundation.ERROR_CURRENT_DOMAIN_NOT_ALLOWED -Windows.Win32.Foundation.ERROR_DATA_CHECKSUM_ERROR -Windows.Win32.Foundation.ERROR_DATA_NOT_ACCEPTED -Windows.Win32.Foundation.ERROR_DATABASE_DOES_NOT_EXIST -Windows.Win32.Foundation.ERROR_DATATYPE_MISMATCH -Windows.Win32.Foundation.ERROR_DAX_MAPPING_EXISTS -Windows.Win32.Foundation.ERROR_DBG_COMMAND_EXCEPTION -Windows.Win32.Foundation.ERROR_DBG_CONTINUE -Windows.Win32.Foundation.ERROR_DBG_CONTROL_BREAK -Windows.Win32.Foundation.ERROR_DBG_CONTROL_C -Windows.Win32.Foundation.ERROR_DBG_EXCEPTION_HANDLED -Windows.Win32.Foundation.ERROR_DBG_EXCEPTION_NOT_HANDLED -Windows.Win32.Foundation.ERROR_DBG_PRINTEXCEPTION_C -Windows.Win32.Foundation.ERROR_DBG_REPLY_LATER -Windows.Win32.Foundation.ERROR_DBG_RIPEXCEPTION -Windows.Win32.Foundation.ERROR_DBG_TERMINATE_PROCESS -Windows.Win32.Foundation.ERROR_DBG_TERMINATE_THREAD -Windows.Win32.Foundation.ERROR_DBG_UNABLE_TO_PROVIDE_HANDLE -Windows.Win32.Foundation.ERROR_DC_NOT_FOUND -Windows.Win32.Foundation.ERROR_DDE_FAIL -Windows.Win32.Foundation.ERROR_DEBUG_ATTACH_FAILED -Windows.Win32.Foundation.ERROR_DEBUGGER_INACTIVE -Windows.Win32.Foundation.ERROR_DECRYPTION_FAILED -Windows.Win32.Foundation.ERROR_DELAY_LOAD_FAILED -Windows.Win32.Foundation.ERROR_DELETE_PENDING -Windows.Win32.Foundation.ERROR_DEPENDENT_SERVICES_RUNNING -Windows.Win32.Foundation.ERROR_DESTINATION_ELEMENT_FULL -Windows.Win32.Foundation.ERROR_DESTROY_OBJECT_OF_OTHER_THREAD -Windows.Win32.Foundation.ERROR_DEV_NOT_EXIST -Windows.Win32.Foundation.ERROR_DEVICE_ALREADY_ATTACHED -Windows.Win32.Foundation.ERROR_DEVICE_ALREADY_REMEMBERED -Windows.Win32.Foundation.ERROR_DEVICE_DOOR_OPEN -Windows.Win32.Foundation.ERROR_DEVICE_ENUMERATION_ERROR -Windows.Win32.Foundation.ERROR_DEVICE_FEATURE_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_DEVICE_HARDWARE_ERROR -Windows.Win32.Foundation.ERROR_DEVICE_HINT_NAME_BUFFER_TOO_SMALL -Windows.Win32.Foundation.ERROR_DEVICE_IN_MAINTENANCE -Windows.Win32.Foundation.ERROR_DEVICE_IN_USE -Windows.Win32.Foundation.ERROR_DEVICE_NO_RESOURCES -Windows.Win32.Foundation.ERROR_DEVICE_NOT_CONNECTED -Windows.Win32.Foundation.ERROR_DEVICE_NOT_PARTITIONED -Windows.Win32.Foundation.ERROR_DEVICE_REINITIALIZATION_NEEDED -Windows.Win32.Foundation.ERROR_DEVICE_REMOVED -Windows.Win32.Foundation.ERROR_DEVICE_REQUIRES_CLEANING -Windows.Win32.Foundation.ERROR_DEVICE_RESET_REQUIRED -Windows.Win32.Foundation.ERROR_DEVICE_SUPPORT_IN_PROGRESS -Windows.Win32.Foundation.ERROR_DEVICE_UNREACHABLE -Windows.Win32.Foundation.ERROR_DHCP_ADDRESS_CONFLICT -Windows.Win32.Foundation.ERROR_DIFFERENT_SERVICE_ACCOUNT -Windows.Win32.Foundation.ERROR_DIR_EFS_DISALLOWED -Windows.Win32.Foundation.ERROR_DIR_NOT_EMPTY -Windows.Win32.Foundation.ERROR_DIR_NOT_ROOT -Windows.Win32.Foundation.ERROR_DIRECT_ACCESS_HANDLE -Windows.Win32.Foundation.ERROR_DIRECTORY -Windows.Win32.Foundation.ERROR_DIRECTORY_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_DISCARDED -Windows.Win32.Foundation.ERROR_DISK_CHANGE -Windows.Win32.Foundation.ERROR_DISK_CORRUPT -Windows.Win32.Foundation.ERROR_DISK_FULL -Windows.Win32.Foundation.ERROR_DISK_OPERATION_FAILED -Windows.Win32.Foundation.ERROR_DISK_QUOTA_EXCEEDED -Windows.Win32.Foundation.ERROR_DISK_RECALIBRATE_FAILED -Windows.Win32.Foundation.ERROR_DISK_REPAIR_DISABLED -Windows.Win32.Foundation.ERROR_DISK_REPAIR_REDIRECTED -Windows.Win32.Foundation.ERROR_DISK_REPAIR_UNSUCCESSFUL -Windows.Win32.Foundation.ERROR_DISK_RESET_FAILED -Windows.Win32.Foundation.ERROR_DISK_RESOURCES_EXHAUSTED -Windows.Win32.Foundation.ERROR_DISK_TOO_FRAGMENTED -Windows.Win32.Foundation.ERROR_DLL_INIT_FAILED -Windows.Win32.Foundation.ERROR_DLL_INIT_FAILED_LOGOFF -Windows.Win32.Foundation.ERROR_DLL_MIGHT_BE_INCOMPATIBLE -Windows.Win32.Foundation.ERROR_DLL_MIGHT_BE_INSECURE -Windows.Win32.Foundation.ERROR_DLL_NOT_FOUND -Windows.Win32.Foundation.ERROR_DLP_POLICY_DENIES_OPERATION -Windows.Win32.Foundation.ERROR_DLP_POLICY_SILENTLY_FAIL -Windows.Win32.Foundation.ERROR_DLP_POLICY_WARNS_AGAINST_OPERATION -Windows.Win32.Foundation.ERROR_DOMAIN_CONTROLLER_EXISTS -Windows.Win32.Foundation.ERROR_DOMAIN_CONTROLLER_NOT_FOUND -Windows.Win32.Foundation.ERROR_DOMAIN_CTRLR_CONFIG_ERROR -Windows.Win32.Foundation.ERROR_DOMAIN_EXISTS -Windows.Win32.Foundation.ERROR_DOMAIN_LIMIT_EXCEEDED -Windows.Win32.Foundation.ERROR_DOMAIN_SID_SAME_AS_LOCAL_WORKSTATION -Windows.Win32.Foundation.ERROR_DOMAIN_TRUST_INCONSISTENT -Windows.Win32.Foundation.ERROR_DOWNGRADE_DETECTED -Windows.Win32.Foundation.ERROR_DPL_NOT_SUPPORTED_FOR_USER -Windows.Win32.Foundation.ERROR_DRIVE_LOCKED -Windows.Win32.Foundation.ERROR_DRIVER_BLOCKED -Windows.Win32.Foundation.ERROR_DRIVER_CANCEL_TIMEOUT -Windows.Win32.Foundation.ERROR_DRIVER_DATABASE_ERROR -Windows.Win32.Foundation.ERROR_DRIVER_FAILED_PRIOR_UNLOAD -Windows.Win32.Foundation.ERROR_DRIVER_FAILED_SLEEP -Windows.Win32.Foundation.ERROR_DRIVER_PROCESS_TERMINATED -Windows.Win32.Foundation.ERROR_DRIVERS_LEAKING_LOCKED_PAGES -Windows.Win32.Foundation.ERROR_DS_ADD_REPLICA_INHIBITED -Windows.Win32.Foundation.ERROR_DS_ADMIN_LIMIT_EXCEEDED -Windows.Win32.Foundation.ERROR_DS_AFFECTS_MULTIPLE_DSAS -Windows.Win32.Foundation.ERROR_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER -Windows.Win32.Foundation.ERROR_DS_ALIAS_DEREF_PROBLEM -Windows.Win32.Foundation.ERROR_DS_ALIAS_POINTS_TO_ALIAS -Windows.Win32.Foundation.ERROR_DS_ALIAS_PROBLEM -Windows.Win32.Foundation.ERROR_DS_ALIASED_OBJ_MISSING -Windows.Win32.Foundation.ERROR_DS_ATT_ALREADY_EXISTS -Windows.Win32.Foundation.ERROR_DS_ATT_IS_NOT_ON_OBJ -Windows.Win32.Foundation.ERROR_DS_ATT_NOT_DEF_FOR_CLASS -Windows.Win32.Foundation.ERROR_DS_ATT_NOT_DEF_IN_SCHEMA -Windows.Win32.Foundation.ERROR_DS_ATT_SCHEMA_REQ_ID -Windows.Win32.Foundation.ERROR_DS_ATT_SCHEMA_REQ_SYNTAX -Windows.Win32.Foundation.ERROR_DS_ATT_VAL_ALREADY_EXISTS -Windows.Win32.Foundation.ERROR_DS_ATTRIBUTE_OR_VALUE_EXISTS -Windows.Win32.Foundation.ERROR_DS_ATTRIBUTE_OWNED_BY_SAM -Windows.Win32.Foundation.ERROR_DS_ATTRIBUTE_TYPE_UNDEFINED -Windows.Win32.Foundation.ERROR_DS_AUDIT_FAILURE -Windows.Win32.Foundation.ERROR_DS_AUTH_METHOD_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_DS_AUTH_UNKNOWN -Windows.Win32.Foundation.ERROR_DS_AUTHORIZATION_FAILED -Windows.Win32.Foundation.ERROR_DS_AUX_CLS_TEST_FAIL -Windows.Win32.Foundation.ERROR_DS_BACKLINK_WITHOUT_LINK -Windows.Win32.Foundation.ERROR_DS_BAD_ATT_SCHEMA_SYNTAX -Windows.Win32.Foundation.ERROR_DS_BAD_HIERARCHY_FILE -Windows.Win32.Foundation.ERROR_DS_BAD_INSTANCE_TYPE -Windows.Win32.Foundation.ERROR_DS_BAD_NAME_SYNTAX -Windows.Win32.Foundation.ERROR_DS_BAD_RDN_ATT_ID_SYNTAX -Windows.Win32.Foundation.ERROR_DS_BUILD_HIERARCHY_TABLE_FAILED -Windows.Win32.Foundation.ERROR_DS_BUSY -Windows.Win32.Foundation.ERROR_DS_CANT_ACCESS_REMOTE_PART_OF_AD -Windows.Win32.Foundation.ERROR_DS_CANT_ADD_ATT_VALUES -Windows.Win32.Foundation.ERROR_DS_CANT_ADD_SYSTEM_ONLY -Windows.Win32.Foundation.ERROR_DS_CANT_ADD_TO_GC -Windows.Win32.Foundation.ERROR_DS_CANT_CACHE_ATT -Windows.Win32.Foundation.ERROR_DS_CANT_CACHE_CLASS -Windows.Win32.Foundation.ERROR_DS_CANT_CREATE_IN_NONDOMAIN_NC -Windows.Win32.Foundation.ERROR_DS_CANT_CREATE_UNDER_SCHEMA -Windows.Win32.Foundation.ERROR_DS_CANT_DEL_MASTER_CROSSREF -Windows.Win32.Foundation.ERROR_DS_CANT_DELETE -Windows.Win32.Foundation.ERROR_DS_CANT_DELETE_DSA_OBJ -Windows.Win32.Foundation.ERROR_DS_CANT_DEMOTE_WITH_WRITEABLE_NC -Windows.Win32.Foundation.ERROR_DS_CANT_DEREF_ALIAS -Windows.Win32.Foundation.ERROR_DS_CANT_DERIVE_SPN_FOR_DELETED_DOMAIN -Windows.Win32.Foundation.ERROR_DS_CANT_DERIVE_SPN_WITHOUT_SERVER_REF -Windows.Win32.Foundation.ERROR_DS_CANT_FIND_DC_FOR_SRC_DOMAIN -Windows.Win32.Foundation.ERROR_DS_CANT_FIND_DSA_OBJ -Windows.Win32.Foundation.ERROR_DS_CANT_FIND_EXPECTED_NC -Windows.Win32.Foundation.ERROR_DS_CANT_FIND_NC_IN_CACHE -Windows.Win32.Foundation.ERROR_DS_CANT_MIX_MASTER_AND_REPS -Windows.Win32.Foundation.ERROR_DS_CANT_MOD_OBJ_CLASS -Windows.Win32.Foundation.ERROR_DS_CANT_MOD_PRIMARYGROUPID -Windows.Win32.Foundation.ERROR_DS_CANT_MOD_SYSTEM_ONLY -Windows.Win32.Foundation.ERROR_DS_CANT_MOVE_ACCOUNT_GROUP -Windows.Win32.Foundation.ERROR_DS_CANT_MOVE_APP_BASIC_GROUP -Windows.Win32.Foundation.ERROR_DS_CANT_MOVE_APP_QUERY_GROUP -Windows.Win32.Foundation.ERROR_DS_CANT_MOVE_DELETED_OBJECT -Windows.Win32.Foundation.ERROR_DS_CANT_MOVE_RESOURCE_GROUP -Windows.Win32.Foundation.ERROR_DS_CANT_ON_NON_LEAF -Windows.Win32.Foundation.ERROR_DS_CANT_ON_RDN -Windows.Win32.Foundation.ERROR_DS_CANT_REM_MISSING_ATT -Windows.Win32.Foundation.ERROR_DS_CANT_REM_MISSING_ATT_VAL -Windows.Win32.Foundation.ERROR_DS_CANT_REMOVE_ATT_CACHE -Windows.Win32.Foundation.ERROR_DS_CANT_REMOVE_CLASS_CACHE -Windows.Win32.Foundation.ERROR_DS_CANT_REPLACE_HIDDEN_REC -Windows.Win32.Foundation.ERROR_DS_CANT_RETRIEVE_ATTS -Windows.Win32.Foundation.ERROR_DS_CANT_RETRIEVE_CHILD -Windows.Win32.Foundation.ERROR_DS_CANT_RETRIEVE_DN -Windows.Win32.Foundation.ERROR_DS_CANT_RETRIEVE_INSTANCE -Windows.Win32.Foundation.ERROR_DS_CANT_RETRIEVE_SD -Windows.Win32.Foundation.ERROR_DS_CANT_START -Windows.Win32.Foundation.ERROR_DS_CANT_TREE_DELETE_CRITICAL_OBJ -Windows.Win32.Foundation.ERROR_DS_CANT_WITH_ACCT_GROUP_MEMBERSHPS -Windows.Win32.Foundation.ERROR_DS_CHILDREN_EXIST -Windows.Win32.Foundation.ERROR_DS_CLASS_MUST_BE_CONCRETE -Windows.Win32.Foundation.ERROR_DS_CLASS_NOT_DSA -Windows.Win32.Foundation.ERROR_DS_CLIENT_LOOP -Windows.Win32.Foundation.ERROR_DS_CODE_INCONSISTENCY -Windows.Win32.Foundation.ERROR_DS_COMPARE_FALSE -Windows.Win32.Foundation.ERROR_DS_COMPARE_TRUE -Windows.Win32.Foundation.ERROR_DS_CONFIDENTIALITY_REQUIRED -Windows.Win32.Foundation.ERROR_DS_CONFIG_PARAM_MISSING -Windows.Win32.Foundation.ERROR_DS_CONSTRAINT_VIOLATION -Windows.Win32.Foundation.ERROR_DS_CONSTRUCTED_ATT_MOD -Windows.Win32.Foundation.ERROR_DS_CONTROL_NOT_FOUND -Windows.Win32.Foundation.ERROR_DS_COULDNT_CONTACT_FSMO -Windows.Win32.Foundation.ERROR_DS_COULDNT_IDENTIFY_OBJECTS_FOR_TREE_DELETE -Windows.Win32.Foundation.ERROR_DS_COULDNT_LOCK_TREE_FOR_DELETE -Windows.Win32.Foundation.ERROR_DS_COULDNT_UPDATE_SPNS -Windows.Win32.Foundation.ERROR_DS_COUNTING_AB_INDICES_FAILED -Windows.Win32.Foundation.ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE -Windows.Win32.Foundation.ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE_V2 -Windows.Win32.Foundation.ERROR_DS_CROSS_DOM_MOVE_ERROR -Windows.Win32.Foundation.ERROR_DS_CROSS_DOMAIN_CLEANUP_REQD -Windows.Win32.Foundation.ERROR_DS_CROSS_NC_DN_RENAME -Windows.Win32.Foundation.ERROR_DS_CROSS_REF_BUSY -Windows.Win32.Foundation.ERROR_DS_CROSS_REF_EXISTS -Windows.Win32.Foundation.ERROR_DS_DATABASE_ERROR -Windows.Win32.Foundation.ERROR_DS_DECODING_ERROR -Windows.Win32.Foundation.ERROR_DS_DESTINATION_AUDITING_NOT_ENABLED -Windows.Win32.Foundation.ERROR_DS_DESTINATION_DOMAIN_NOT_IN_FOREST -Windows.Win32.Foundation.ERROR_DS_DIFFERENT_REPL_EPOCHS -Windows.Win32.Foundation.ERROR_DS_DISALLOWED_IN_SYSTEM_CONTAINER -Windows.Win32.Foundation.ERROR_DS_DISALLOWED_NC_REDIRECT -Windows.Win32.Foundation.ERROR_DS_DNS_LOOKUP_FAILURE -Windows.Win32.Foundation.ERROR_DS_DOMAIN_NAME_EXISTS_IN_FOREST -Windows.Win32.Foundation.ERROR_DS_DOMAIN_RENAME_IN_PROGRESS -Windows.Win32.Foundation.ERROR_DS_DOMAIN_VERSION_TOO_HIGH -Windows.Win32.Foundation.ERROR_DS_DOMAIN_VERSION_TOO_LOW -Windows.Win32.Foundation.ERROR_DS_DRA_ABANDON_SYNC -Windows.Win32.Foundation.ERROR_DS_DRA_ACCESS_DENIED -Windows.Win32.Foundation.ERROR_DS_DRA_BAD_DN -Windows.Win32.Foundation.ERROR_DS_DRA_BAD_INSTANCE_TYPE -Windows.Win32.Foundation.ERROR_DS_DRA_BAD_NC -Windows.Win32.Foundation.ERROR_DS_DRA_BUSY -Windows.Win32.Foundation.ERROR_DS_DRA_CONNECTION_FAILED -Windows.Win32.Foundation.ERROR_DS_DRA_CORRUPT_UTD_VECTOR -Windows.Win32.Foundation.ERROR_DS_DRA_DB_ERROR -Windows.Win32.Foundation.ERROR_DS_DRA_DN_EXISTS -Windows.Win32.Foundation.ERROR_DS_DRA_EARLIER_SCHEMA_CONFLICT -Windows.Win32.Foundation.ERROR_DS_DRA_EXTN_CONNECTION_FAILED -Windows.Win32.Foundation.ERROR_DS_DRA_GENERIC -Windows.Win32.Foundation.ERROR_DS_DRA_INCOMPATIBLE_PARTIAL_SET -Windows.Win32.Foundation.ERROR_DS_DRA_INCONSISTENT_DIT -Windows.Win32.Foundation.ERROR_DS_DRA_INTERNAL_ERROR -Windows.Win32.Foundation.ERROR_DS_DRA_INVALID_PARAMETER -Windows.Win32.Foundation.ERROR_DS_DRA_MAIL_PROBLEM -Windows.Win32.Foundation.ERROR_DS_DRA_MISSING_KRBTGT_SECRET -Windows.Win32.Foundation.ERROR_DS_DRA_MISSING_PARENT -Windows.Win32.Foundation.ERROR_DS_DRA_NAME_COLLISION -Windows.Win32.Foundation.ERROR_DS_DRA_NO_REPLICA -Windows.Win32.Foundation.ERROR_DS_DRA_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_DS_DRA_OBJ_IS_REP_SOURCE -Windows.Win32.Foundation.ERROR_DS_DRA_OBJ_NC_MISMATCH -Windows.Win32.Foundation.ERROR_DS_DRA_OUT_OF_MEM -Windows.Win32.Foundation.ERROR_DS_DRA_OUT_SCHEDULE_WINDOW -Windows.Win32.Foundation.ERROR_DS_DRA_PREEMPTED -Windows.Win32.Foundation.ERROR_DS_DRA_RECYCLED_TARGET -Windows.Win32.Foundation.ERROR_DS_DRA_REF_ALREADY_EXISTS -Windows.Win32.Foundation.ERROR_DS_DRA_REF_NOT_FOUND -Windows.Win32.Foundation.ERROR_DS_DRA_REPL_PENDING -Windows.Win32.Foundation.ERROR_DS_DRA_RPC_CANCELLED -Windows.Win32.Foundation.ERROR_DS_DRA_SCHEMA_CONFLICT -Windows.Win32.Foundation.ERROR_DS_DRA_SCHEMA_INFO_SHIP -Windows.Win32.Foundation.ERROR_DS_DRA_SCHEMA_MISMATCH -Windows.Win32.Foundation.ERROR_DS_DRA_SECRETS_DENIED -Windows.Win32.Foundation.ERROR_DS_DRA_SHUTDOWN -Windows.Win32.Foundation.ERROR_DS_DRA_SINK_DISABLED -Windows.Win32.Foundation.ERROR_DS_DRA_SOURCE_DISABLED -Windows.Win32.Foundation.ERROR_DS_DRA_SOURCE_IS_PARTIAL_REPLICA -Windows.Win32.Foundation.ERROR_DS_DRA_SOURCE_REINSTALLED -Windows.Win32.Foundation.ERROR_DS_DRS_EXTENSIONS_CHANGED -Windows.Win32.Foundation.ERROR_DS_DS_REQUIRED -Windows.Win32.Foundation.ERROR_DS_DSA_MUST_BE_INT_MASTER -Windows.Win32.Foundation.ERROR_DS_DST_DOMAIN_NOT_NATIVE -Windows.Win32.Foundation.ERROR_DS_DST_NC_MISMATCH -Windows.Win32.Foundation.ERROR_DS_DUP_LDAP_DISPLAY_NAME -Windows.Win32.Foundation.ERROR_DS_DUP_LINK_ID -Windows.Win32.Foundation.ERROR_DS_DUP_MAPI_ID -Windows.Win32.Foundation.ERROR_DS_DUP_MSDS_INTID -Windows.Win32.Foundation.ERROR_DS_DUP_OID -Windows.Win32.Foundation.ERROR_DS_DUP_RDN -Windows.Win32.Foundation.ERROR_DS_DUP_SCHEMA_ID_GUID -Windows.Win32.Foundation.ERROR_DS_DUPLICATE_ID_FOUND -Windows.Win32.Foundation.ERROR_DS_ENCODING_ERROR -Windows.Win32.Foundation.ERROR_DS_EPOCH_MISMATCH -Windows.Win32.Foundation.ERROR_DS_EXISTING_AD_CHILD_NC -Windows.Win32.Foundation.ERROR_DS_EXISTS_IN_AUX_CLS -Windows.Win32.Foundation.ERROR_DS_EXISTS_IN_MAY_HAVE -Windows.Win32.Foundation.ERROR_DS_EXISTS_IN_MUST_HAVE -Windows.Win32.Foundation.ERROR_DS_EXISTS_IN_POSS_SUP -Windows.Win32.Foundation.ERROR_DS_EXISTS_IN_RDNATTID -Windows.Win32.Foundation.ERROR_DS_EXISTS_IN_SUB_CLS -Windows.Win32.Foundation.ERROR_DS_FILTER_UNKNOWN -Windows.Win32.Foundation.ERROR_DS_FILTER_USES_CONTRUCTED_ATTRS -Windows.Win32.Foundation.ERROR_DS_FLAT_NAME_EXISTS_IN_FOREST -Windows.Win32.Foundation.ERROR_DS_FOREST_VERSION_TOO_HIGH -Windows.Win32.Foundation.ERROR_DS_FOREST_VERSION_TOO_LOW -Windows.Win32.Foundation.ERROR_DS_GC_NOT_AVAILABLE -Windows.Win32.Foundation.ERROR_DS_GC_REQUIRED -Windows.Win32.Foundation.ERROR_DS_GCVERIFY_ERROR -Windows.Win32.Foundation.ERROR_DS_GENERIC_ERROR -Windows.Win32.Foundation.ERROR_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER -Windows.Win32.Foundation.ERROR_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER -Windows.Win32.Foundation.ERROR_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER -Windows.Win32.Foundation.ERROR_DS_GOVERNSID_MISSING -Windows.Win32.Foundation.ERROR_DS_GROUP_CONVERSION_ERROR -Windows.Win32.Foundation.ERROR_DS_HAVE_PRIMARY_MEMBERS -Windows.Win32.Foundation.ERROR_DS_HIERARCHY_TABLE_MALLOC_FAILED -Windows.Win32.Foundation.ERROR_DS_HIERARCHY_TABLE_TOO_DEEP -Windows.Win32.Foundation.ERROR_DS_HIGH_ADLDS_FFL -Windows.Win32.Foundation.ERROR_DS_HIGH_DSA_VERSION -Windows.Win32.Foundation.ERROR_DS_ILLEGAL_BASE_SCHEMA_MOD -Windows.Win32.Foundation.ERROR_DS_ILLEGAL_MOD_OPERATION -Windows.Win32.Foundation.ERROR_DS_ILLEGAL_SUPERIOR -Windows.Win32.Foundation.ERROR_DS_ILLEGAL_XDOM_MOVE_OPERATION -Windows.Win32.Foundation.ERROR_DS_INAPPROPRIATE_AUTH -Windows.Win32.Foundation.ERROR_DS_INAPPROPRIATE_MATCHING -Windows.Win32.Foundation.ERROR_DS_INCOMPATIBLE_CONTROLS_USED -Windows.Win32.Foundation.ERROR_DS_INCOMPATIBLE_VERSION -Windows.Win32.Foundation.ERROR_DS_INCORRECT_ROLE_OWNER -Windows.Win32.Foundation.ERROR_DS_INIT_FAILURE -Windows.Win32.Foundation.ERROR_DS_INIT_FAILURE_CONSOLE -Windows.Win32.Foundation.ERROR_DS_INSTALL_NO_SCH_VERSION_IN_INIFILE -Windows.Win32.Foundation.ERROR_DS_INSTALL_NO_SRC_SCH_VERSION -Windows.Win32.Foundation.ERROR_DS_INSTALL_SCHEMA_MISMATCH -Windows.Win32.Foundation.ERROR_DS_INSUFF_ACCESS_RIGHTS -Windows.Win32.Foundation.ERROR_DS_INSUFFICIENT_ATTR_TO_CREATE_OBJECT -Windows.Win32.Foundation.ERROR_DS_INTERNAL_FAILURE -Windows.Win32.Foundation.ERROR_DS_INVALID_ATTRIBUTE_SYNTAX -Windows.Win32.Foundation.ERROR_DS_INVALID_DMD -Windows.Win32.Foundation.ERROR_DS_INVALID_DN_SYNTAX -Windows.Win32.Foundation.ERROR_DS_INVALID_GROUP_TYPE -Windows.Win32.Foundation.ERROR_DS_INVALID_LDAP_DISPLAY_NAME -Windows.Win32.Foundation.ERROR_DS_INVALID_NAME_FOR_SPN -Windows.Win32.Foundation.ERROR_DS_INVALID_ROLE_OWNER -Windows.Win32.Foundation.ERROR_DS_INVALID_SCRIPT -Windows.Win32.Foundation.ERROR_DS_INVALID_SEARCH_FLAG -Windows.Win32.Foundation.ERROR_DS_INVALID_SEARCH_FLAG_SUBTREE -Windows.Win32.Foundation.ERROR_DS_INVALID_SEARCH_FLAG_TUPLE -Windows.Win32.Foundation.ERROR_DS_IS_LEAF -Windows.Win32.Foundation.ERROR_DS_KEY_NOT_UNIQUE -Windows.Win32.Foundation.ERROR_DS_LDAP_SEND_QUEUE_FULL -Windows.Win32.Foundation.ERROR_DS_LINK_ID_NOT_AVAILABLE -Windows.Win32.Foundation.ERROR_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER -Windows.Win32.Foundation.ERROR_DS_LOCAL_ERROR -Windows.Win32.Foundation.ERROR_DS_LOCAL_MEMBER_OF_LOCAL_ONLY -Windows.Win32.Foundation.ERROR_DS_LOOP_DETECT -Windows.Win32.Foundation.ERROR_DS_LOW_ADLDS_FFL -Windows.Win32.Foundation.ERROR_DS_LOW_DSA_VERSION -Windows.Win32.Foundation.ERROR_DS_MACHINE_ACCOUNT_CREATED_PRENT4 -Windows.Win32.Foundation.ERROR_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED -Windows.Win32.Foundation.ERROR_DS_MAPI_ID_NOT_AVAILABLE -Windows.Win32.Foundation.ERROR_DS_MASTERDSA_REQUIRED -Windows.Win32.Foundation.ERROR_DS_MAX_OBJ_SIZE_EXCEEDED -Windows.Win32.Foundation.ERROR_DS_MEMBERSHIP_EVALUATED_LOCALLY -Windows.Win32.Foundation.ERROR_DS_MISSING_EXPECTED_ATT -Windows.Win32.Foundation.ERROR_DS_MISSING_FOREST_TRUST -Windows.Win32.Foundation.ERROR_DS_MISSING_FSMO_SETTINGS -Windows.Win32.Foundation.ERROR_DS_MISSING_INFRASTRUCTURE_CONTAINER -Windows.Win32.Foundation.ERROR_DS_MISSING_REQUIRED_ATT -Windows.Win32.Foundation.ERROR_DS_MISSING_SUPREF -Windows.Win32.Foundation.ERROR_DS_MODIFYDN_DISALLOWED_BY_FLAG -Windows.Win32.Foundation.ERROR_DS_MODIFYDN_DISALLOWED_BY_INSTANCE_TYPE -Windows.Win32.Foundation.ERROR_DS_MODIFYDN_WRONG_GRANDPARENT -Windows.Win32.Foundation.ERROR_DS_MUST_BE_RUN_ON_DST_DC -Windows.Win32.Foundation.ERROR_DS_NAME_ERROR_DOMAIN_ONLY -Windows.Win32.Foundation.ERROR_DS_NAME_ERROR_NO_MAPPING -Windows.Win32.Foundation.ERROR_DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING -Windows.Win32.Foundation.ERROR_DS_NAME_ERROR_NOT_FOUND -Windows.Win32.Foundation.ERROR_DS_NAME_ERROR_NOT_UNIQUE -Windows.Win32.Foundation.ERROR_DS_NAME_ERROR_RESOLVING -Windows.Win32.Foundation.ERROR_DS_NAME_ERROR_TRUST_REFERRAL -Windows.Win32.Foundation.ERROR_DS_NAME_NOT_UNIQUE -Windows.Win32.Foundation.ERROR_DS_NAME_REFERENCE_INVALID -Windows.Win32.Foundation.ERROR_DS_NAME_TOO_LONG -Windows.Win32.Foundation.ERROR_DS_NAME_TOO_MANY_PARTS -Windows.Win32.Foundation.ERROR_DS_NAME_TYPE_UNKNOWN -Windows.Win32.Foundation.ERROR_DS_NAME_UNPARSEABLE -Windows.Win32.Foundation.ERROR_DS_NAME_VALUE_TOO_LONG -Windows.Win32.Foundation.ERROR_DS_NAMING_MASTER_GC -Windows.Win32.Foundation.ERROR_DS_NAMING_VIOLATION -Windows.Win32.Foundation.ERROR_DS_NC_MUST_HAVE_NC_PARENT -Windows.Win32.Foundation.ERROR_DS_NC_STILL_HAS_DSAS -Windows.Win32.Foundation.ERROR_DS_NCNAME_MISSING_CR_REF -Windows.Win32.Foundation.ERROR_DS_NCNAME_MUST_BE_NC -Windows.Win32.Foundation.ERROR_DS_NO_ATTRIBUTE_OR_VALUE -Windows.Win32.Foundation.ERROR_DS_NO_BEHAVIOR_VERSION_IN_MIXEDDOMAIN -Windows.Win32.Foundation.ERROR_DS_NO_CHAINED_EVAL -Windows.Win32.Foundation.ERROR_DS_NO_CHAINING -Windows.Win32.Foundation.ERROR_DS_NO_CHECKPOINT_WITH_PDC -Windows.Win32.Foundation.ERROR_DS_NO_CROSSREF_FOR_NC -Windows.Win32.Foundation.ERROR_DS_NO_DELETED_NAME -Windows.Win32.Foundation.ERROR_DS_NO_FPO_IN_UNIVERSAL_GROUPS -Windows.Win32.Foundation.ERROR_DS_NO_MORE_RIDS -Windows.Win32.Foundation.ERROR_DS_NO_MSDS_INTID -Windows.Win32.Foundation.ERROR_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN -Windows.Win32.Foundation.ERROR_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN -Windows.Win32.Foundation.ERROR_DS_NO_NTDSA_OBJECT -Windows.Win32.Foundation.ERROR_DS_NO_OBJECT_MOVE_IN_SCHEMA_NC -Windows.Win32.Foundation.ERROR_DS_NO_PARENT_OBJECT -Windows.Win32.Foundation.ERROR_DS_NO_PKT_PRIVACY_ON_CONNECTION -Windows.Win32.Foundation.ERROR_DS_NO_RDN_DEFINED_IN_SCHEMA -Windows.Win32.Foundation.ERROR_DS_NO_REF_DOMAIN -Windows.Win32.Foundation.ERROR_DS_NO_REQUESTED_ATTS_FOUND -Windows.Win32.Foundation.ERROR_DS_NO_RESULTS_RETURNED -Windows.Win32.Foundation.ERROR_DS_NO_RIDS_ALLOCATED -Windows.Win32.Foundation.ERROR_DS_NO_SERVER_OBJECT -Windows.Win32.Foundation.ERROR_DS_NO_SUCH_OBJECT -Windows.Win32.Foundation.ERROR_DS_NO_TREE_DELETE_ABOVE_NC -Windows.Win32.Foundation.ERROR_DS_NON_ASQ_SEARCH -Windows.Win32.Foundation.ERROR_DS_NON_BASE_SEARCH -Windows.Win32.Foundation.ERROR_DS_NONEXISTENT_MAY_HAVE -Windows.Win32.Foundation.ERROR_DS_NONEXISTENT_MUST_HAVE -Windows.Win32.Foundation.ERROR_DS_NONEXISTENT_POSS_SUP -Windows.Win32.Foundation.ERROR_DS_NONSAFE_SCHEMA_CHANGE -Windows.Win32.Foundation.ERROR_DS_NOT_AN_OBJECT -Windows.Win32.Foundation.ERROR_DS_NOT_AUTHORITIVE_FOR_DST_NC -Windows.Win32.Foundation.ERROR_DS_NOT_CLOSEST -Windows.Win32.Foundation.ERROR_DS_NOT_INSTALLED -Windows.Win32.Foundation.ERROR_DS_NOT_ON_BACKLINK -Windows.Win32.Foundation.ERROR_DS_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_DS_NOT_SUPPORTED_SORT_ORDER -Windows.Win32.Foundation.ERROR_DS_NOTIFY_FILTER_TOO_COMPLEX -Windows.Win32.Foundation.ERROR_DS_NTDSCRIPT_PROCESS_ERROR -Windows.Win32.Foundation.ERROR_DS_NTDSCRIPT_SYNTAX_ERROR -Windows.Win32.Foundation.ERROR_DS_OBJ_CLASS_NOT_DEFINED -Windows.Win32.Foundation.ERROR_DS_OBJ_CLASS_NOT_SUBCLASS -Windows.Win32.Foundation.ERROR_DS_OBJ_CLASS_VIOLATION -Windows.Win32.Foundation.ERROR_DS_OBJ_GUID_EXISTS -Windows.Win32.Foundation.ERROR_DS_OBJ_NOT_FOUND -Windows.Win32.Foundation.ERROR_DS_OBJ_STRING_NAME_EXISTS -Windows.Win32.Foundation.ERROR_DS_OBJ_TOO_LARGE -Windows.Win32.Foundation.ERROR_DS_OBJECT_BEING_REMOVED -Windows.Win32.Foundation.ERROR_DS_OBJECT_CLASS_REQUIRED -Windows.Win32.Foundation.ERROR_DS_OBJECT_RESULTS_TOO_LARGE -Windows.Win32.Foundation.ERROR_DS_OFFSET_RANGE_ERROR -Windows.Win32.Foundation.ERROR_DS_OID_MAPPED_GROUP_CANT_HAVE_MEMBERS -Windows.Win32.Foundation.ERROR_DS_OID_NOT_FOUND -Windows.Win32.Foundation.ERROR_DS_OPERATIONS_ERROR -Windows.Win32.Foundation.ERROR_DS_OUT_OF_SCOPE -Windows.Win32.Foundation.ERROR_DS_OUT_OF_VERSION_STORE -Windows.Win32.Foundation.ERROR_DS_PARAM_ERROR -Windows.Win32.Foundation.ERROR_DS_PARENT_IS_AN_ALIAS -Windows.Win32.Foundation.ERROR_DS_PDC_OPERATION_IN_PROGRESS -Windows.Win32.Foundation.ERROR_DS_PER_ATTRIBUTE_AUTHZ_FAILED_DURING_ADD -Windows.Win32.Foundation.ERROR_DS_POLICY_NOT_KNOWN -Windows.Win32.Foundation.ERROR_DS_PROTOCOL_ERROR -Windows.Win32.Foundation.ERROR_DS_RANGE_CONSTRAINT -Windows.Win32.Foundation.ERROR_DS_RDN_DOESNT_MATCH_SCHEMA -Windows.Win32.Foundation.ERROR_DS_RECALCSCHEMA_FAILED -Windows.Win32.Foundation.ERROR_DS_REFERRAL -Windows.Win32.Foundation.ERROR_DS_REFERRAL_LIMIT_EXCEEDED -Windows.Win32.Foundation.ERROR_DS_REFUSING_FSMO_ROLES -Windows.Win32.Foundation.ERROR_DS_REMOTE_CROSSREF_OP_FAILED -Windows.Win32.Foundation.ERROR_DS_REPL_LIFETIME_EXCEEDED -Windows.Win32.Foundation.ERROR_DS_REPLICA_SET_CHANGE_NOT_ALLOWED_ON_DISABLED_CR -Windows.Win32.Foundation.ERROR_DS_REPLICATOR_ONLY -Windows.Win32.Foundation.ERROR_DS_RESERVED_LINK_ID -Windows.Win32.Foundation.ERROR_DS_RESERVED_MAPI_ID -Windows.Win32.Foundation.ERROR_DS_RIDMGR_DISABLED -Windows.Win32.Foundation.ERROR_DS_RIDMGR_INIT_ERROR -Windows.Win32.Foundation.ERROR_DS_ROLE_NOT_VERIFIED -Windows.Win32.Foundation.ERROR_DS_ROOT_CANT_BE_SUBREF -Windows.Win32.Foundation.ERROR_DS_ROOT_MUST_BE_NC -Windows.Win32.Foundation.ERROR_DS_ROOT_REQUIRES_CLASS_TOP -Windows.Win32.Foundation.ERROR_DS_SAM_INIT_FAILURE -Windows.Win32.Foundation.ERROR_DS_SAM_INIT_FAILURE_CONSOLE -Windows.Win32.Foundation.ERROR_DS_SAM_NEED_BOOTKEY_FLOPPY -Windows.Win32.Foundation.ERROR_DS_SAM_NEED_BOOTKEY_PASSWORD -Windows.Win32.Foundation.ERROR_DS_SCHEMA_ALLOC_FAILED -Windows.Win32.Foundation.ERROR_DS_SCHEMA_NOT_LOADED -Windows.Win32.Foundation.ERROR_DS_SCHEMA_UPDATE_DISALLOWED -Windows.Win32.Foundation.ERROR_DS_SEC_DESC_INVALID -Windows.Win32.Foundation.ERROR_DS_SEC_DESC_TOO_SHORT -Windows.Win32.Foundation.ERROR_DS_SECURITY_CHECKING_ERROR -Windows.Win32.Foundation.ERROR_DS_SECURITY_ILLEGAL_MODIFY -Windows.Win32.Foundation.ERROR_DS_SEMANTIC_ATT_TEST -Windows.Win32.Foundation.ERROR_DS_SENSITIVE_GROUP_VIOLATION -Windows.Win32.Foundation.ERROR_DS_SERVER_DOWN -Windows.Win32.Foundation.ERROR_DS_SHUTTING_DOWN -Windows.Win32.Foundation.ERROR_DS_SINGLE_USER_MODE_FAILED -Windows.Win32.Foundation.ERROR_DS_SINGLE_VALUE_CONSTRAINT -Windows.Win32.Foundation.ERROR_DS_SIZELIMIT_EXCEEDED -Windows.Win32.Foundation.ERROR_DS_SORT_CONTROL_MISSING -Windows.Win32.Foundation.ERROR_DS_SOURCE_AUDITING_NOT_ENABLED -Windows.Win32.Foundation.ERROR_DS_SOURCE_DOMAIN_IN_FOREST -Windows.Win32.Foundation.ERROR_DS_SPN_VALUE_NOT_UNIQUE_IN_FOREST -Windows.Win32.Foundation.ERROR_DS_SRC_AND_DST_NC_IDENTICAL -Windows.Win32.Foundation.ERROR_DS_SRC_AND_DST_OBJECT_CLASS_MISMATCH -Windows.Win32.Foundation.ERROR_DS_SRC_DC_MUST_BE_SP4_OR_GREATER -Windows.Win32.Foundation.ERROR_DS_SRC_GUID_MISMATCH -Windows.Win32.Foundation.ERROR_DS_SRC_NAME_MISMATCH -Windows.Win32.Foundation.ERROR_DS_SRC_OBJ_NOT_GROUP_OR_USER -Windows.Win32.Foundation.ERROR_DS_SRC_SID_EXISTS_IN_FOREST -Windows.Win32.Foundation.ERROR_DS_STRING_SD_CONVERSION_FAILED -Windows.Win32.Foundation.ERROR_DS_STRONG_AUTH_REQUIRED -Windows.Win32.Foundation.ERROR_DS_SUB_CLS_TEST_FAIL -Windows.Win32.Foundation.ERROR_DS_SUBREF_MUST_HAVE_PARENT -Windows.Win32.Foundation.ERROR_DS_SUBTREE_NOTIFY_NOT_NC_HEAD -Windows.Win32.Foundation.ERROR_DS_SYNTAX_MISMATCH -Windows.Win32.Foundation.ERROR_DS_THREAD_LIMIT_EXCEEDED -Windows.Win32.Foundation.ERROR_DS_TIMELIMIT_EXCEEDED -Windows.Win32.Foundation.ERROR_DS_TREE_DELETE_NOT_FINISHED -Windows.Win32.Foundation.ERROR_DS_UNABLE_TO_SURRENDER_ROLES -Windows.Win32.Foundation.ERROR_DS_UNAVAILABLE -Windows.Win32.Foundation.ERROR_DS_UNAVAILABLE_CRIT_EXTENSION -Windows.Win32.Foundation.ERROR_DS_UNDELETE_SAM_VALIDATION_FAILED -Windows.Win32.Foundation.ERROR_DS_UNICODEPWD_NOT_IN_QUOTES -Windows.Win32.Foundation.ERROR_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER -Windows.Win32.Foundation.ERROR_DS_UNKNOWN_ERROR -Windows.Win32.Foundation.ERROR_DS_UNKNOWN_OPERATION -Windows.Win32.Foundation.ERROR_DS_UNWILLING_TO_PERFORM -Windows.Win32.Foundation.ERROR_DS_UPN_VALUE_NOT_UNIQUE_IN_FOREST -Windows.Win32.Foundation.ERROR_DS_USER_BUFFER_TO_SMALL -Windows.Win32.Foundation.ERROR_DS_VALUE_KEY_NOT_UNIQUE -Windows.Win32.Foundation.ERROR_DS_VERSION_CHECK_FAILURE -Windows.Win32.Foundation.ERROR_DS_WKO_CONTAINER_CANNOT_BE_SPECIAL -Windows.Win32.Foundation.ERROR_DS_WRONG_LINKED_ATT_SYNTAX -Windows.Win32.Foundation.ERROR_DS_WRONG_OM_OBJ_CLASS -Windows.Win32.Foundation.ERROR_DUP_DOMAINNAME -Windows.Win32.Foundation.ERROR_DUP_NAME -Windows.Win32.Foundation.ERROR_DUPLICATE_PRIVILEGES -Windows.Win32.Foundation.ERROR_DUPLICATE_SERVICE_NAME -Windows.Win32.Foundation.ERROR_DYNAMIC_CODE_BLOCKED -Windows.Win32.Foundation.ERROR_DYNLINK_FROM_INVALID_RING -Windows.Win32.Foundation.ERROR_EA_ACCESS_DENIED -Windows.Win32.Foundation.ERROR_EA_FILE_CORRUPT -Windows.Win32.Foundation.ERROR_EA_LIST_INCONSISTENT -Windows.Win32.Foundation.ERROR_EA_TABLE_FULL -Windows.Win32.Foundation.ERROR_EAS_DIDNT_FIT -Windows.Win32.Foundation.ERROR_EAS_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_EDP_DPL_POLICY_CANT_BE_SATISFIED -Windows.Win32.Foundation.ERROR_EDP_POLICY_DENIES_OPERATION -Windows.Win32.Foundation.ERROR_EFS_ALG_BLOB_TOO_BIG -Windows.Win32.Foundation.ERROR_EFS_DISABLED -Windows.Win32.Foundation.ERROR_EFS_SERVER_NOT_TRUSTED -Windows.Win32.Foundation.ERROR_EFS_VERSION_NOT_SUPPORT -Windows.Win32.Foundation.ERROR_ELEVATION_REQUIRED -Windows.Win32.Foundation.ERROR_ENCLAVE_FAILURE -Windows.Win32.Foundation.ERROR_ENCLAVE_NOT_TERMINATED -Windows.Win32.Foundation.ERROR_ENCLAVE_VIOLATION -Windows.Win32.Foundation.ERROR_ENCRYPTED_FILE_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_ENCRYPTED_IO_NOT_POSSIBLE -Windows.Win32.Foundation.ERROR_ENCRYPTING_METADATA_DISALLOWED -Windows.Win32.Foundation.ERROR_ENCRYPTION_DISABLED -Windows.Win32.Foundation.ERROR_ENCRYPTION_FAILED -Windows.Win32.Foundation.ERROR_ENCRYPTION_POLICY_DENIES_OPERATION -Windows.Win32.Foundation.ERROR_END_OF_MEDIA -Windows.Win32.Foundation.ERROR_ENVVAR_NOT_FOUND -Windows.Win32.Foundation.ERROR_EOM_OVERFLOW -Windows.Win32.Foundation.ERROR_ERRORS_ENCOUNTERED -Windows.Win32.Foundation.ERROR_EVALUATION_EXPIRATION -Windows.Win32.Foundation.ERROR_EVENT_DONE -Windows.Win32.Foundation.ERROR_EVENT_PENDING -Windows.Win32.Foundation.ERROR_EVENTLOG_CANT_START -Windows.Win32.Foundation.ERROR_EVENTLOG_FILE_CHANGED -Windows.Win32.Foundation.ERROR_EVENTLOG_FILE_CORRUPT -Windows.Win32.Foundation.ERROR_EXCEPTION_IN_SERVICE -Windows.Win32.Foundation.ERROR_EXCL_SEM_ALREADY_OWNED -Windows.Win32.Foundation.ERROR_EXE_CANNOT_MODIFY_SIGNED_BINARY -Windows.Win32.Foundation.ERROR_EXE_CANNOT_MODIFY_STRONG_SIGNED_BINARY -Windows.Win32.Foundation.ERROR_EXE_MACHINE_TYPE_MISMATCH -Windows.Win32.Foundation.ERROR_EXE_MARKED_INVALID -Windows.Win32.Foundation.ERROR_EXTENDED_ERROR -Windows.Win32.Foundation.ERROR_EXTERNAL_BACKING_PROVIDER_UNKNOWN -Windows.Win32.Foundation.ERROR_EXTERNAL_SYSKEY_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_EXTRANEOUS_INFORMATION -Windows.Win32.Foundation.ERROR_FAIL_FAST_EXCEPTION -Windows.Win32.Foundation.ERROR_FAIL_I24 -Windows.Win32.Foundation.ERROR_FAIL_NOACTION_REBOOT -Windows.Win32.Foundation.ERROR_FAIL_RESTART -Windows.Win32.Foundation.ERROR_FAIL_SHUTDOWN -Windows.Win32.Foundation.ERROR_FAILED_DRIVER_ENTRY -Windows.Win32.Foundation.ERROR_FAILED_SERVICE_CONTROLLER_CONNECT -Windows.Win32.Foundation.ERROR_FATAL_APP_EXIT -Windows.Win32.Foundation.ERROR_FILE_CHECKED_OUT -Windows.Win32.Foundation.ERROR_FILE_CORRUPT -Windows.Win32.Foundation.ERROR_FILE_ENCRYPTED -Windows.Win32.Foundation.ERROR_FILE_EXISTS -Windows.Win32.Foundation.ERROR_FILE_HANDLE_REVOKED -Windows.Win32.Foundation.ERROR_FILE_INVALID -Windows.Win32.Foundation.ERROR_FILE_LEVEL_TRIM_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_FILE_METADATA_OPTIMIZATION_IN_PROGRESS -Windows.Win32.Foundation.ERROR_FILE_NOT_ENCRYPTED -Windows.Win32.Foundation.ERROR_FILE_NOT_FOUND -Windows.Win32.Foundation.ERROR_FILE_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_FILE_OFFLINE -Windows.Win32.Foundation.ERROR_FILE_PROTECTED_UNDER_DPL -Windows.Win32.Foundation.ERROR_FILE_READ_ONLY -Windows.Win32.Foundation.ERROR_FILE_SNAP_IN_PROGRESS -Windows.Win32.Foundation.ERROR_FILE_SNAP_INVALID_PARAMETER -Windows.Win32.Foundation.ERROR_FILE_SNAP_IO_NOT_COORDINATED -Windows.Win32.Foundation.ERROR_FILE_SNAP_MODIFY_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_FILE_SNAP_UNEXPECTED_ERROR -Windows.Win32.Foundation.ERROR_FILE_SNAP_USER_SECTION_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_FILE_SYSTEM_LIMITATION -Windows.Win32.Foundation.ERROR_FILE_SYSTEM_VIRTUALIZATION_BUSY -Windows.Win32.Foundation.ERROR_FILE_SYSTEM_VIRTUALIZATION_INVALID_OPERATION -Windows.Win32.Foundation.ERROR_FILE_SYSTEM_VIRTUALIZATION_METADATA_CORRUPT -Windows.Win32.Foundation.ERROR_FILE_SYSTEM_VIRTUALIZATION_PROVIDER_UNKNOWN -Windows.Win32.Foundation.ERROR_FILE_SYSTEM_VIRTUALIZATION_UNAVAILABLE -Windows.Win32.Foundation.ERROR_FILE_TOO_LARGE -Windows.Win32.Foundation.ERROR_FILEMARK_DETECTED -Windows.Win32.Foundation.ERROR_FILENAME_EXCED_RANGE -Windows.Win32.Foundation.ERROR_FIRMWARE_UPDATED -Windows.Win32.Foundation.ERROR_FLOAT_MULTIPLE_FAULTS -Windows.Win32.Foundation.ERROR_FLOAT_MULTIPLE_TRAPS -Windows.Win32.Foundation.ERROR_FLOPPY_BAD_REGISTERS -Windows.Win32.Foundation.ERROR_FLOPPY_ID_MARK_NOT_FOUND -Windows.Win32.Foundation.ERROR_FLOPPY_UNKNOWN_ERROR -Windows.Win32.Foundation.ERROR_FLOPPY_VOLUME -Windows.Win32.Foundation.ERROR_FLOPPY_WRONG_CYLINDER -Windows.Win32.Foundation.ERROR_FORMS_AUTH_REQUIRED -Windows.Win32.Foundation.ERROR_FOUND_OUT_OF_SCOPE -Windows.Win32.Foundation.ERROR_FS_DRIVER_REQUIRED -Windows.Win32.Foundation.ERROR_FS_METADATA_INCONSISTENT -Windows.Win32.Foundation.ERROR_FSFILTER_OP_COMPLETED_SUCCESSFULLY -Windows.Win32.Foundation.ERROR_FT_DI_SCAN_REQUIRED -Windows.Win32.Foundation.ERROR_FT_READ_FAILURE -Windows.Win32.Foundation.ERROR_FT_READ_FROM_COPY_FAILURE -Windows.Win32.Foundation.ERROR_FT_READ_RECOVERY_FROM_BACKUP -Windows.Win32.Foundation.ERROR_FT_WRITE_FAILURE -Windows.Win32.Foundation.ERROR_FT_WRITE_RECOVERY -Windows.Win32.Foundation.ERROR_FULLSCREEN_MODE -Windows.Win32.Foundation.ERROR_FUNCTION_FAILED -Windows.Win32.Foundation.ERROR_FUNCTION_NOT_CALLED -Windows.Win32.Foundation.ERROR_GDI_HANDLE_LEAK -Windows.Win32.Foundation.ERROR_GEN_FAILURE -Windows.Win32.Foundation.ERROR_GENERIC_NOT_MAPPED -Windows.Win32.Foundation.ERROR_GLOBAL_ONLY_HOOK -Windows.Win32.Foundation.ERROR_GRACEFUL_DISCONNECT -Windows.Win32.Foundation.ERROR_GROUP_EXISTS -Windows.Win32.Foundation.ERROR_GUID_SUBSTITUTION_MADE -Windows.Win32.Foundation.ERROR_HANDLE_DISK_FULL -Windows.Win32.Foundation.ERROR_HANDLE_EOF -Windows.Win32.Foundation.ERROR_HANDLE_REVOKED -Windows.Win32.Foundation.ERROR_HANDLES_CLOSED -Windows.Win32.Foundation.ERROR_HAS_SYSTEM_CRITICAL_FILES -Windows.Win32.Foundation.ERROR_HIBERNATED -Windows.Win32.Foundation.ERROR_HIBERNATION_FAILURE -Windows.Win32.Foundation.ERROR_HOOK_NEEDS_HMOD -Windows.Win32.Foundation.ERROR_HOOK_NOT_INSTALLED -Windows.Win32.Foundation.ERROR_HOOK_TYPE_NOT_ALLOWED -Windows.Win32.Foundation.ERROR_HOST_DOWN -Windows.Win32.Foundation.ERROR_HOST_UNREACHABLE -Windows.Win32.Foundation.ERROR_HOTKEY_ALREADY_REGISTERED -Windows.Win32.Foundation.ERROR_HOTKEY_NOT_REGISTERED -Windows.Win32.Foundation.ERROR_HWNDS_HAVE_DIFF_PARENT -Windows.Win32.Foundation.ERROR_ILL_FORMED_PASSWORD -Windows.Win32.Foundation.ERROR_ILLEGAL_CHARACTER -Windows.Win32.Foundation.ERROR_ILLEGAL_DLL_RELOCATION -Windows.Win32.Foundation.ERROR_ILLEGAL_ELEMENT_ADDRESS -Windows.Win32.Foundation.ERROR_ILLEGAL_FLOAT_CONTEXT -Windows.Win32.Foundation.ERROR_IMAGE_AT_DIFFERENT_BASE -Windows.Win32.Foundation.ERROR_IMAGE_MACHINE_TYPE_MISMATCH -Windows.Win32.Foundation.ERROR_IMAGE_MACHINE_TYPE_MISMATCH_EXE -Windows.Win32.Foundation.ERROR_IMAGE_NOT_AT_BASE -Windows.Win32.Foundation.ERROR_IMAGE_SUBSYSTEM_NOT_PRESENT -Windows.Win32.Foundation.ERROR_IMPLEMENTATION_LIMIT -Windows.Win32.Foundation.ERROR_INCOMPATIBLE_SERVICE_PRIVILEGE -Windows.Win32.Foundation.ERROR_INCOMPATIBLE_SERVICE_SID_TYPE -Windows.Win32.Foundation.ERROR_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING -Windows.Win32.Foundation.ERROR_INCORRECT_ACCOUNT_TYPE -Windows.Win32.Foundation.ERROR_INCORRECT_ADDRESS -Windows.Win32.Foundation.ERROR_INCORRECT_SIZE -Windows.Win32.Foundation.ERROR_INDEX_ABSENT -Windows.Win32.Foundation.ERROR_INDEX_OUT_OF_BOUNDS -Windows.Win32.Foundation.ERROR_INFLOOP_IN_RELOC_CHAIN -Windows.Win32.Foundation.ERROR_INSTALL_ALREADY_RUNNING -Windows.Win32.Foundation.ERROR_INSTALL_FAILURE -Windows.Win32.Foundation.ERROR_INSTALL_LANGUAGE_UNSUPPORTED -Windows.Win32.Foundation.ERROR_INSTALL_LOG_FAILURE -Windows.Win32.Foundation.ERROR_INSTALL_NOTUSED -Windows.Win32.Foundation.ERROR_INSTALL_PACKAGE_INVALID -Windows.Win32.Foundation.ERROR_INSTALL_PACKAGE_OPEN_FAILED -Windows.Win32.Foundation.ERROR_INSTALL_PACKAGE_REJECTED -Windows.Win32.Foundation.ERROR_INSTALL_PACKAGE_VERSION -Windows.Win32.Foundation.ERROR_INSTALL_PLATFORM_UNSUPPORTED -Windows.Win32.Foundation.ERROR_INSTALL_REJECTED -Windows.Win32.Foundation.ERROR_INSTALL_REMOTE_DISALLOWED -Windows.Win32.Foundation.ERROR_INSTALL_REMOTE_PROHIBITED -Windows.Win32.Foundation.ERROR_INSTALL_SERVICE_FAILURE -Windows.Win32.Foundation.ERROR_INSTALL_SERVICE_SAFEBOOT -Windows.Win32.Foundation.ERROR_INSTALL_SOURCE_ABSENT -Windows.Win32.Foundation.ERROR_INSTALL_SUSPEND -Windows.Win32.Foundation.ERROR_INSTALL_TEMP_UNWRITABLE -Windows.Win32.Foundation.ERROR_INSTALL_TRANSFORM_FAILURE -Windows.Win32.Foundation.ERROR_INSTALL_TRANSFORM_REJECTED -Windows.Win32.Foundation.ERROR_INSTALL_UI_FAILURE -Windows.Win32.Foundation.ERROR_INSTALL_USEREXIT -Windows.Win32.Foundation.ERROR_INSTRUCTION_MISALIGNMENT -Windows.Win32.Foundation.ERROR_INSUFFICIENT_BUFFER -Windows.Win32.Foundation.ERROR_INSUFFICIENT_LOGON_INFO -Windows.Win32.Foundation.ERROR_INSUFFICIENT_POWER -Windows.Win32.Foundation.ERROR_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE -Windows.Win32.Foundation.ERROR_INSUFFICIENT_VIRTUAL_ADDR_RESOURCES -Windows.Win32.Foundation.ERROR_INTERMIXED_KERNEL_EA_OPERATION -Windows.Win32.Foundation.ERROR_INTERNAL_DB_CORRUPTION -Windows.Win32.Foundation.ERROR_INTERNAL_DB_ERROR -Windows.Win32.Foundation.ERROR_INTERNAL_ERROR -Windows.Win32.Foundation.ERROR_INTERRUPT_STILL_CONNECTED -Windows.Win32.Foundation.ERROR_INTERRUPT_VECTOR_ALREADY_CONNECTED -Windows.Win32.Foundation.ERROR_INVALID_ACCEL_HANDLE -Windows.Win32.Foundation.ERROR_INVALID_ACCESS -Windows.Win32.Foundation.ERROR_INVALID_ACCOUNT_NAME -Windows.Win32.Foundation.ERROR_INVALID_ACE_CONDITION -Windows.Win32.Foundation.ERROR_INVALID_ACL -Windows.Win32.Foundation.ERROR_INVALID_ADDRESS -Windows.Win32.Foundation.ERROR_INVALID_AT_INTERRUPT_TIME -Windows.Win32.Foundation.ERROR_INVALID_BLOCK -Windows.Win32.Foundation.ERROR_INVALID_BLOCK_LENGTH -Windows.Win32.Foundation.ERROR_INVALID_CAP -Windows.Win32.Foundation.ERROR_INVALID_CATEGORY -Windows.Win32.Foundation.ERROR_INVALID_COMBOBOX_MESSAGE -Windows.Win32.Foundation.ERROR_INVALID_COMMAND_LINE -Windows.Win32.Foundation.ERROR_INVALID_COMPUTERNAME -Windows.Win32.Foundation.ERROR_INVALID_CRUNTIME_PARAMETER -Windows.Win32.Foundation.ERROR_INVALID_CURSOR_HANDLE -Windows.Win32.Foundation.ERROR_INVALID_DATA -Windows.Win32.Foundation.ERROR_INVALID_DATATYPE -Windows.Win32.Foundation.ERROR_INVALID_DEVICE_OBJECT_PARAMETER -Windows.Win32.Foundation.ERROR_INVALID_DLL -Windows.Win32.Foundation.ERROR_INVALID_DOMAIN_ROLE -Windows.Win32.Foundation.ERROR_INVALID_DOMAIN_STATE -Windows.Win32.Foundation.ERROR_INVALID_DOMAINNAME -Windows.Win32.Foundation.ERROR_INVALID_DRIVE -Windows.Win32.Foundation.ERROR_INVALID_DWP_HANDLE -Windows.Win32.Foundation.ERROR_INVALID_EA_HANDLE -Windows.Win32.Foundation.ERROR_INVALID_EA_NAME -Windows.Win32.Foundation.ERROR_INVALID_EDIT_HEIGHT -Windows.Win32.Foundation.ERROR_INVALID_ENVIRONMENT -Windows.Win32.Foundation.ERROR_INVALID_EVENT_COUNT -Windows.Win32.Foundation.ERROR_INVALID_EVENTNAME -Windows.Win32.Foundation.ERROR_INVALID_EXCEPTION_HANDLER -Windows.Win32.Foundation.ERROR_INVALID_EXE_SIGNATURE -Windows.Win32.Foundation.ERROR_INVALID_FIELD -Windows.Win32.Foundation.ERROR_INVALID_FIELD_IN_PARAMETER_LIST -Windows.Win32.Foundation.ERROR_INVALID_FILTER_PROC -Windows.Win32.Foundation.ERROR_INVALID_FLAG_NUMBER -Windows.Win32.Foundation.ERROR_INVALID_FLAGS -Windows.Win32.Foundation.ERROR_INVALID_FORM_NAME -Windows.Win32.Foundation.ERROR_INVALID_FORM_SIZE -Windows.Win32.Foundation.ERROR_INVALID_FUNCTION -Windows.Win32.Foundation.ERROR_INVALID_GROUP_ATTRIBUTES -Windows.Win32.Foundation.ERROR_INVALID_GROUPNAME -Windows.Win32.Foundation.ERROR_INVALID_GW_COMMAND -Windows.Win32.Foundation.ERROR_INVALID_HANDLE -Windows.Win32.Foundation.ERROR_INVALID_HANDLE_STATE -Windows.Win32.Foundation.ERROR_INVALID_HOOK_FILTER -Windows.Win32.Foundation.ERROR_INVALID_HOOK_HANDLE -Windows.Win32.Foundation.ERROR_INVALID_HW_PROFILE -Windows.Win32.Foundation.ERROR_INVALID_ICON_HANDLE -Windows.Win32.Foundation.ERROR_INVALID_ID_AUTHORITY -Windows.Win32.Foundation.ERROR_INVALID_IMAGE_HASH -Windows.Win32.Foundation.ERROR_INVALID_IMPORT_OF_NON_DLL -Windows.Win32.Foundation.ERROR_INVALID_INDEX -Windows.Win32.Foundation.ERROR_INVALID_KERNEL_INFO_VERSION -Windows.Win32.Foundation.ERROR_INVALID_KEYBOARD_HANDLE -Windows.Win32.Foundation.ERROR_INVALID_LABEL -Windows.Win32.Foundation.ERROR_INVALID_LB_MESSAGE -Windows.Win32.Foundation.ERROR_INVALID_LDT_DESCRIPTOR -Windows.Win32.Foundation.ERROR_INVALID_LDT_OFFSET -Windows.Win32.Foundation.ERROR_INVALID_LDT_SIZE -Windows.Win32.Foundation.ERROR_INVALID_LEVEL -Windows.Win32.Foundation.ERROR_INVALID_LIST_FORMAT -Windows.Win32.Foundation.ERROR_INVALID_LOCK_RANGE -Windows.Win32.Foundation.ERROR_INVALID_LOGON_HOURS -Windows.Win32.Foundation.ERROR_INVALID_LOGON_TYPE -Windows.Win32.Foundation.ERROR_INVALID_MEMBER -Windows.Win32.Foundation.ERROR_INVALID_MENU_HANDLE -Windows.Win32.Foundation.ERROR_INVALID_MESSAGE -Windows.Win32.Foundation.ERROR_INVALID_MESSAGEDEST -Windows.Win32.Foundation.ERROR_INVALID_MESSAGENAME -Windows.Win32.Foundation.ERROR_INVALID_MINALLOCSIZE -Windows.Win32.Foundation.ERROR_INVALID_MODULETYPE -Windows.Win32.Foundation.ERROR_INVALID_MONITOR_HANDLE -Windows.Win32.Foundation.ERROR_INVALID_MSGBOX_STYLE -Windows.Win32.Foundation.ERROR_INVALID_NAME -Windows.Win32.Foundation.ERROR_INVALID_NETNAME -Windows.Win32.Foundation.ERROR_INVALID_OPLOCK_PROTOCOL -Windows.Win32.Foundation.ERROR_INVALID_ORDINAL -Windows.Win32.Foundation.ERROR_INVALID_OWNER -Windows.Win32.Foundation.ERROR_INVALID_PACKAGE_SID_LENGTH -Windows.Win32.Foundation.ERROR_INVALID_PARAMETER -Windows.Win32.Foundation.ERROR_INVALID_PASSWORD -Windows.Win32.Foundation.ERROR_INVALID_PASSWORDNAME -Windows.Win32.Foundation.ERROR_INVALID_PATCH_XML -Windows.Win32.Foundation.ERROR_INVALID_PEP_INFO_VERSION -Windows.Win32.Foundation.ERROR_INVALID_PLUGPLAY_DEVICE_PATH -Windows.Win32.Foundation.ERROR_INVALID_PORT_ATTRIBUTES -Windows.Win32.Foundation.ERROR_INVALID_PRIMARY_GROUP -Windows.Win32.Foundation.ERROR_INVALID_PRINTER_COMMAND -Windows.Win32.Foundation.ERROR_INVALID_PRINTER_NAME -Windows.Win32.Foundation.ERROR_INVALID_PRINTER_STATE -Windows.Win32.Foundation.ERROR_INVALID_PRIORITY -Windows.Win32.Foundation.ERROR_INVALID_QUOTA_LOWER -Windows.Win32.Foundation.ERROR_INVALID_REPARSE_DATA -Windows.Win32.Foundation.ERROR_INVALID_SCROLLBAR_RANGE -Windows.Win32.Foundation.ERROR_INVALID_SECURITY_DESCR -Windows.Win32.Foundation.ERROR_INVALID_SEGDPL -Windows.Win32.Foundation.ERROR_INVALID_SEGMENT_NUMBER -Windows.Win32.Foundation.ERROR_INVALID_SEPARATOR_FILE -Windows.Win32.Foundation.ERROR_INVALID_SERVER_STATE -Windows.Win32.Foundation.ERROR_INVALID_SERVICE_ACCOUNT -Windows.Win32.Foundation.ERROR_INVALID_SERVICE_CONTROL -Windows.Win32.Foundation.ERROR_INVALID_SERVICE_LOCK -Windows.Win32.Foundation.ERROR_INVALID_SERVICENAME -Windows.Win32.Foundation.ERROR_INVALID_SHARENAME -Windows.Win32.Foundation.ERROR_INVALID_SHOWWIN_COMMAND -Windows.Win32.Foundation.ERROR_INVALID_SID -Windows.Win32.Foundation.ERROR_INVALID_SIGNAL_NUMBER -Windows.Win32.Foundation.ERROR_INVALID_SPI_VALUE -Windows.Win32.Foundation.ERROR_INVALID_STACKSEG -Windows.Win32.Foundation.ERROR_INVALID_STARTING_CODESEG -Windows.Win32.Foundation.ERROR_INVALID_SUB_AUTHORITY -Windows.Win32.Foundation.ERROR_INVALID_TABLE -Windows.Win32.Foundation.ERROR_INVALID_TARGET_HANDLE -Windows.Win32.Foundation.ERROR_INVALID_TASK_INDEX -Windows.Win32.Foundation.ERROR_INVALID_TASK_NAME -Windows.Win32.Foundation.ERROR_INVALID_THREAD_ID -Windows.Win32.Foundation.ERROR_INVALID_TIME -Windows.Win32.Foundation.ERROR_INVALID_TOKEN -Windows.Win32.Foundation.ERROR_INVALID_UNWIND_TARGET -Windows.Win32.Foundation.ERROR_INVALID_USER_BUFFER -Windows.Win32.Foundation.ERROR_INVALID_USER_PRINCIPAL_NAME -Windows.Win32.Foundation.ERROR_INVALID_VARIANT -Windows.Win32.Foundation.ERROR_INVALID_VERIFY_SWITCH -Windows.Win32.Foundation.ERROR_INVALID_WINDOW_HANDLE -Windows.Win32.Foundation.ERROR_INVALID_WORKSTATION -Windows.Win32.Foundation.ERROR_IO_DEVICE -Windows.Win32.Foundation.ERROR_IO_INCOMPLETE -Windows.Win32.Foundation.ERROR_IO_PENDING -Windows.Win32.Foundation.ERROR_IO_PRIVILEGE_FAILED -Windows.Win32.Foundation.ERROR_IO_REISSUE_AS_CACHED -Windows.Win32.Foundation.ERROR_IOPL_NOT_ENABLED -Windows.Win32.Foundation.ERROR_IP_ADDRESS_CONFLICT1 -Windows.Win32.Foundation.ERROR_IP_ADDRESS_CONFLICT2 -Windows.Win32.Foundation.ERROR_IPSEC_IKE_TIMED_OUT -Windows.Win32.Foundation.ERROR_IRQ_BUSY -Windows.Win32.Foundation.ERROR_IS_JOIN_PATH -Windows.Win32.Foundation.ERROR_IS_JOIN_TARGET -Windows.Win32.Foundation.ERROR_IS_JOINED -Windows.Win32.Foundation.ERROR_IS_SUBST_PATH -Windows.Win32.Foundation.ERROR_IS_SUBST_TARGET -Windows.Win32.Foundation.ERROR_IS_SUBSTED -Windows.Win32.Foundation.ERROR_ITERATED_DATA_EXCEEDS_64k -Windows.Win32.Foundation.ERROR_JOB_NO_CONTAINER -Windows.Win32.Foundation.ERROR_JOIN_TO_JOIN -Windows.Win32.Foundation.ERROR_JOIN_TO_SUBST -Windows.Win32.Foundation.ERROR_JOURNAL_DELETE_IN_PROGRESS -Windows.Win32.Foundation.ERROR_JOURNAL_ENTRY_DELETED -Windows.Win32.Foundation.ERROR_JOURNAL_HOOK_SET -Windows.Win32.Foundation.ERROR_JOURNAL_NOT_ACTIVE -Windows.Win32.Foundation.ERROR_KERNEL_APC -Windows.Win32.Foundation.ERROR_KEY_DELETED -Windows.Win32.Foundation.ERROR_KEY_HAS_CHILDREN -Windows.Win32.Foundation.ERROR_KM_DRIVER_BLOCKED -Windows.Win32.Foundation.ERROR_LABEL_TOO_LONG -Windows.Win32.Foundation.ERROR_LAST_ADMIN -Windows.Win32.Foundation.ERROR_LB_WITHOUT_TABSTOPS -Windows.Win32.Foundation.ERROR_LICENSE_QUOTA_EXCEEDED -Windows.Win32.Foundation.ERROR_LINUX_SUBSYSTEM_NOT_PRESENT -Windows.Win32.Foundation.ERROR_LINUX_SUBSYSTEM_UPDATE_REQUIRED -Windows.Win32.Foundation.ERROR_LISTBOX_ID_NOT_FOUND -Windows.Win32.Foundation.ERROR_LM_CROSS_ENCRYPTION_REQUIRED -Windows.Win32.Foundation.ERROR_LOCAL_POLICY_MODIFICATION_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_LOCAL_USER_SESSION_KEY -Windows.Win32.Foundation.ERROR_LOCK_FAILED -Windows.Win32.Foundation.ERROR_LOCK_VIOLATION -Windows.Win32.Foundation.ERROR_LOCKED -Windows.Win32.Foundation.ERROR_LOG_FILE_FULL -Windows.Win32.Foundation.ERROR_LOG_HARD_ERROR -Windows.Win32.Foundation.ERROR_LOGIN_TIME_RESTRICTION -Windows.Win32.Foundation.ERROR_LOGIN_WKSTA_RESTRICTION -Windows.Win32.Foundation.ERROR_LOGON_FAILURE -Windows.Win32.Foundation.ERROR_LOGON_NOT_GRANTED -Windows.Win32.Foundation.ERROR_LOGON_SERVER_CONFLICT -Windows.Win32.Foundation.ERROR_LOGON_SESSION_COLLISION -Windows.Win32.Foundation.ERROR_LOGON_SESSION_EXISTS -Windows.Win32.Foundation.ERROR_LOGON_TYPE_NOT_GRANTED -Windows.Win32.Foundation.ERROR_LONGJUMP -Windows.Win32.Foundation.ERROR_LOST_MODE_LOGON_RESTRICTION -Windows.Win32.Foundation.ERROR_LOST_WRITEBEHIND_DATA -Windows.Win32.Foundation.ERROR_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR -Windows.Win32.Foundation.ERROR_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED -Windows.Win32.Foundation.ERROR_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR -Windows.Win32.Foundation.ERROR_LUIDS_EXHAUSTED -Windows.Win32.Foundation.ERROR_MACHINE_LOCKED -Windows.Win32.Foundation.ERROR_MAGAZINE_NOT_PRESENT -Windows.Win32.Foundation.ERROR_MAPPED_ALIGNMENT -Windows.Win32.Foundation.ERROR_MARKED_TO_DISALLOW_WRITES -Windows.Win32.Foundation.ERROR_MARSHALL_OVERFLOW -Windows.Win32.Foundation.ERROR_MAX_SESSIONS_REACHED -Windows.Win32.Foundation.ERROR_MAX_THRDS_REACHED -Windows.Win32.Foundation.ERROR_MCA_EXCEPTION -Windows.Win32.Foundation.ERROR_MCA_OCCURED -Windows.Win32.Foundation.ERROR_MEDIA_CHANGED -Windows.Win32.Foundation.ERROR_MEDIA_CHECK -Windows.Win32.Foundation.ERROR_MEMBER_IN_ALIAS -Windows.Win32.Foundation.ERROR_MEMBER_IN_GROUP -Windows.Win32.Foundation.ERROR_MEMBER_NOT_IN_ALIAS -Windows.Win32.Foundation.ERROR_MEMBER_NOT_IN_GROUP -Windows.Win32.Foundation.ERROR_MEMBERS_PRIMARY_GROUP -Windows.Win32.Foundation.ERROR_MEMORY_HARDWARE -Windows.Win32.Foundation.ERROR_MENU_ITEM_NOT_FOUND -Windows.Win32.Foundation.ERROR_MESSAGE_SYNC_ONLY -Windows.Win32.Foundation.ERROR_META_EXPANSION_TOO_LONG -Windows.Win32.Foundation.ERROR_MISSING_SYSTEMFILE -Windows.Win32.Foundation.ERROR_MOD_NOT_FOUND -Windows.Win32.Foundation.ERROR_MORE_DATA -Windows.Win32.Foundation.ERROR_MORE_WRITES -Windows.Win32.Foundation.ERROR_MOUNT_POINT_NOT_RESOLVED -Windows.Win32.Foundation.ERROR_MP_PROCESSOR_MISMATCH -Windows.Win32.Foundation.ERROR_MR_MID_NOT_FOUND -Windows.Win32.Foundation.ERROR_MULTIPLE_FAULT_VIOLATION -Windows.Win32.Foundation.ERROR_MUTANT_LIMIT_EXCEEDED -Windows.Win32.Foundation.ERROR_MUTUAL_AUTH_FAILED -Windows.Win32.Foundation.ERROR_NEGATIVE_SEEK -Windows.Win32.Foundation.ERROR_NESTING_NOT_ALLOWED -Windows.Win32.Foundation.ERROR_NET_OPEN_FAILED -Windows.Win32.Foundation.ERROR_NET_WRITE_FAULT -Windows.Win32.Foundation.ERROR_NETLOGON_NOT_STARTED -Windows.Win32.Foundation.ERROR_NETNAME_DELETED -Windows.Win32.Foundation.ERROR_NETWORK_ACCESS_DENIED -Windows.Win32.Foundation.ERROR_NETWORK_ACCESS_DENIED_EDP -Windows.Win32.Foundation.ERROR_NETWORK_BUSY -Windows.Win32.Foundation.ERROR_NETWORK_UNREACHABLE -Windows.Win32.Foundation.ERROR_NO_ACE_CONDITION -Windows.Win32.Foundation.ERROR_NO_ASSOCIATION -Windows.Win32.Foundation.ERROR_NO_BYPASSIO_DRIVER_SUPPORT -Windows.Win32.Foundation.ERROR_NO_CALLBACK_ACTIVE -Windows.Win32.Foundation.ERROR_NO_DATA -Windows.Win32.Foundation.ERROR_NO_DATA_DETECTED -Windows.Win32.Foundation.ERROR_NO_EFS -Windows.Win32.Foundation.ERROR_NO_EVENT_PAIR -Windows.Win32.Foundation.ERROR_NO_GUID_TRANSLATION -Windows.Win32.Foundation.ERROR_NO_IMPERSONATION_TOKEN -Windows.Win32.Foundation.ERROR_NO_INHERITANCE -Windows.Win32.Foundation.ERROR_NO_LOG_SPACE -Windows.Win32.Foundation.ERROR_NO_LOGON_SERVERS -Windows.Win32.Foundation.ERROR_NO_MATCH -Windows.Win32.Foundation.ERROR_NO_MEDIA_IN_DRIVE -Windows.Win32.Foundation.ERROR_NO_MORE_DEVICES -Windows.Win32.Foundation.ERROR_NO_MORE_FILES -Windows.Win32.Foundation.ERROR_NO_MORE_ITEMS -Windows.Win32.Foundation.ERROR_NO_MORE_MATCHES -Windows.Win32.Foundation.ERROR_NO_MORE_SEARCH_HANDLES -Windows.Win32.Foundation.ERROR_NO_MORE_USER_HANDLES -Windows.Win32.Foundation.ERROR_NO_NET_OR_BAD_PATH -Windows.Win32.Foundation.ERROR_NO_NETWORK -Windows.Win32.Foundation.ERROR_NO_NVRAM_RESOURCES -Windows.Win32.Foundation.ERROR_NO_PAGEFILE -Windows.Win32.Foundation.ERROR_NO_PHYSICALLY_ALIGNED_FREE_SPACE_FOUND -Windows.Win32.Foundation.ERROR_NO_PROC_SLOTS -Windows.Win32.Foundation.ERROR_NO_PROMOTION_ACTIVE -Windows.Win32.Foundation.ERROR_NO_QUOTAS_FOR_ACCOUNT -Windows.Win32.Foundation.ERROR_NO_RANGES_PROCESSED -Windows.Win32.Foundation.ERROR_NO_RECOVERY_POLICY -Windows.Win32.Foundation.ERROR_NO_RECOVERY_PROGRAM -Windows.Win32.Foundation.ERROR_NO_SCROLLBARS -Windows.Win32.Foundation.ERROR_NO_SECRETS -Windows.Win32.Foundation.ERROR_NO_SECURITY_ON_OBJECT -Windows.Win32.Foundation.ERROR_NO_SHUTDOWN_IN_PROGRESS -Windows.Win32.Foundation.ERROR_NO_SIGNAL_SENT -Windows.Win32.Foundation.ERROR_NO_SITE_SETTINGS_OBJECT -Windows.Win32.Foundation.ERROR_NO_SITENAME -Windows.Win32.Foundation.ERROR_NO_SPOOL_SPACE -Windows.Win32.Foundation.ERROR_NO_SUCH_ALIAS -Windows.Win32.Foundation.ERROR_NO_SUCH_DEVICE -Windows.Win32.Foundation.ERROR_NO_SUCH_DOMAIN -Windows.Win32.Foundation.ERROR_NO_SUCH_GROUP -Windows.Win32.Foundation.ERROR_NO_SUCH_LOGON_SESSION -Windows.Win32.Foundation.ERROR_NO_SUCH_MEMBER -Windows.Win32.Foundation.ERROR_NO_SUCH_PACKAGE -Windows.Win32.Foundation.ERROR_NO_SUCH_PRIVILEGE -Windows.Win32.Foundation.ERROR_NO_SUCH_SITE -Windows.Win32.Foundation.ERROR_NO_SUCH_USER -Windows.Win32.Foundation.ERROR_NO_SYSTEM_MENU -Windows.Win32.Foundation.ERROR_NO_SYSTEM_RESOURCES -Windows.Win32.Foundation.ERROR_NO_TASK_QUEUE -Windows.Win32.Foundation.ERROR_NO_TOKEN -Windows.Win32.Foundation.ERROR_NO_TRACKING_SERVICE -Windows.Win32.Foundation.ERROR_NO_TRUST_LSA_SECRET -Windows.Win32.Foundation.ERROR_NO_TRUST_SAM_ACCOUNT -Windows.Win32.Foundation.ERROR_NO_UNICODE_TRANSLATION -Windows.Win32.Foundation.ERROR_NO_USER_KEYS -Windows.Win32.Foundation.ERROR_NO_USER_SESSION_KEY -Windows.Win32.Foundation.ERROR_NO_VOLUME_ID -Windows.Win32.Foundation.ERROR_NO_VOLUME_LABEL -Windows.Win32.Foundation.ERROR_NO_WILDCARD_CHARACTERS -Windows.Win32.Foundation.ERROR_NO_WORK_DONE -Windows.Win32.Foundation.ERROR_NO_WRITABLE_DC_FOUND -Windows.Win32.Foundation.ERROR_NO_YIELD_PERFORMED -Windows.Win32.Foundation.ERROR_NOACCESS -Windows.Win32.Foundation.ERROR_NOINTERFACE -Windows.Win32.Foundation.ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT -Windows.Win32.Foundation.ERROR_NOLOGON_SERVER_TRUST_ACCOUNT -Windows.Win32.Foundation.ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT -Windows.Win32.Foundation.ERROR_NON_ACCOUNT_SID -Windows.Win32.Foundation.ERROR_NON_DOMAIN_SID -Windows.Win32.Foundation.ERROR_NON_MDICHILD_WINDOW -Windows.Win32.Foundation.ERROR_NONE_MAPPED -Windows.Win32.Foundation.ERROR_NONPAGED_SYSTEM_RESOURCES -Windows.Win32.Foundation.ERROR_NOT_A_CLOUD_FILE -Windows.Win32.Foundation.ERROR_NOT_A_CLOUD_SYNC_ROOT -Windows.Win32.Foundation.ERROR_NOT_A_DAX_VOLUME -Windows.Win32.Foundation.ERROR_NOT_A_REPARSE_POINT -Windows.Win32.Foundation.ERROR_NOT_ALL_ASSIGNED -Windows.Win32.Foundation.ERROR_NOT_ALLOWED_ON_SYSTEM_FILE -Windows.Win32.Foundation.ERROR_NOT_APPCONTAINER -Windows.Win32.Foundation.ERROR_NOT_AUTHENTICATED -Windows.Win32.Foundation.ERROR_NOT_CAPABLE -Windows.Win32.Foundation.ERROR_NOT_CHILD_WINDOW -Windows.Win32.Foundation.ERROR_NOT_CONNECTED -Windows.Win32.Foundation.ERROR_NOT_CONTAINER -Windows.Win32.Foundation.ERROR_NOT_DAX_MAPPABLE -Windows.Win32.Foundation.ERROR_NOT_DOS_DISK -Windows.Win32.Foundation.ERROR_NOT_ENOUGH_MEMORY -Windows.Win32.Foundation.ERROR_NOT_ENOUGH_QUOTA -Windows.Win32.Foundation.ERROR_NOT_ENOUGH_SERVER_MEMORY -Windows.Win32.Foundation.ERROR_NOT_EXPORT_FORMAT -Windows.Win32.Foundation.ERROR_NOT_FOUND -Windows.Win32.Foundation.ERROR_NOT_GUI_PROCESS -Windows.Win32.Foundation.ERROR_NOT_JOINED -Windows.Win32.Foundation.ERROR_NOT_LOCKED -Windows.Win32.Foundation.ERROR_NOT_LOGGED_ON -Windows.Win32.Foundation.ERROR_NOT_LOGON_PROCESS -Windows.Win32.Foundation.ERROR_NOT_OWNER -Windows.Win32.Foundation.ERROR_NOT_READ_FROM_COPY -Windows.Win32.Foundation.ERROR_NOT_READY -Windows.Win32.Foundation.ERROR_NOT_REDUNDANT_STORAGE -Windows.Win32.Foundation.ERROR_NOT_REGISTRY_FILE -Windows.Win32.Foundation.ERROR_NOT_SAFE_MODE_DRIVER -Windows.Win32.Foundation.ERROR_NOT_SAFEBOOT_SERVICE -Windows.Win32.Foundation.ERROR_NOT_SAME_DEVICE -Windows.Win32.Foundation.ERROR_NOT_SAME_OBJECT -Windows.Win32.Foundation.ERROR_NOT_SUBSTED -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_IN_APPCONTAINER -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_ON_DAX -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_ON_SBS -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_ON_STANDARD_SERVER -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_AUDITING -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_BTT -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_BYPASSIO -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_CACHED_HANDLE -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_COMPRESSION -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_DEDUPLICATION -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_ENCRYPTION -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_MONITORING -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_REPLICATION -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_SNAPSHOT -Windows.Win32.Foundation.ERROR_NOT_SUPPORTED_WITH_VIRTUALIZATION -Windows.Win32.Foundation.ERROR_NOT_TINY_STREAM -Windows.Win32.Foundation.ERROR_NOTHING_TO_TERMINATE -Windows.Win32.Foundation.ERROR_NOTIFICATION_GUID_ALREADY_DEFINED -Windows.Win32.Foundation.ERROR_NOTIFY_CLEANUP -Windows.Win32.Foundation.ERROR_NOTIFY_ENUM_DIR -Windows.Win32.Foundation.ERROR_NT_CROSS_ENCRYPTION_REQUIRED -Windows.Win32.Foundation.ERROR_NTLM_BLOCKED -Windows.Win32.Foundation.ERROR_NULL_LM_PASSWORD -Windows.Win32.Foundation.ERROR_OBJECT_IS_IMMUTABLE -Windows.Win32.Foundation.ERROR_OBJECT_NAME_EXISTS -Windows.Win32.Foundation.ERROR_OBJECT_NOT_EXTERNALLY_BACKED -Windows.Win32.Foundation.ERROR_OFFLOAD_READ_FILE_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_OFFLOAD_READ_FLT_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_OFFLOAD_WRITE_FILE_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_OFFLOAD_WRITE_FLT_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_OFFSET_ALIGNMENT_VIOLATION -Windows.Win32.Foundation.ERROR_OLD_WIN_VERSION -Windows.Win32.Foundation.ERROR_ONLY_IF_CONNECTED -Windows.Win32.Foundation.ERROR_OPEN_FAILED -Windows.Win32.Foundation.ERROR_OPEN_FILES -Windows.Win32.Foundation.ERROR_OPERATION_ABORTED -Windows.Win32.Foundation.ERROR_OPERATION_IN_PROGRESS -Windows.Win32.Foundation.ERROR_OPLOCK_BREAK_IN_PROGRESS -Windows.Win32.Foundation.ERROR_OPLOCK_HANDLE_CLOSED -Windows.Win32.Foundation.ERROR_OPLOCK_NOT_GRANTED -Windows.Win32.Foundation.ERROR_OPLOCK_SWITCHED_TO_NEW_HANDLE -Windows.Win32.Foundation.ERROR_ORPHAN_NAME_EXHAUSTED -Windows.Win32.Foundation.ERROR_OUT_OF_PAPER -Windows.Win32.Foundation.ERROR_OUT_OF_STRUCTURES -Windows.Win32.Foundation.ERROR_OUTOFMEMORY -Windows.Win32.Foundation.ERROR_OVERRIDE_NOCHANGES -Windows.Win32.Foundation.ERROR_PAGE_FAULT_COPY_ON_WRITE -Windows.Win32.Foundation.ERROR_PAGE_FAULT_DEMAND_ZERO -Windows.Win32.Foundation.ERROR_PAGE_FAULT_GUARD_PAGE -Windows.Win32.Foundation.ERROR_PAGE_FAULT_PAGING_FILE -Windows.Win32.Foundation.ERROR_PAGE_FAULT_TRANSITION -Windows.Win32.Foundation.ERROR_PAGED_SYSTEM_RESOURCES -Windows.Win32.Foundation.ERROR_PAGEFILE_CREATE_FAILED -Windows.Win32.Foundation.ERROR_PAGEFILE_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_PAGEFILE_QUOTA -Windows.Win32.Foundation.ERROR_PAGEFILE_QUOTA_EXCEEDED -Windows.Win32.Foundation.ERROR_PARAMETER_QUOTA_EXCEEDED -Windows.Win32.Foundation.ERROR_PARTIAL_COPY -Windows.Win32.Foundation.ERROR_PARTITION_FAILURE -Windows.Win32.Foundation.ERROR_PARTITION_TERMINATING -Windows.Win32.Foundation.ERROR_PASSWORD_CHANGE_REQUIRED -Windows.Win32.Foundation.ERROR_PASSWORD_EXPIRED -Windows.Win32.Foundation.ERROR_PASSWORD_MUST_CHANGE -Windows.Win32.Foundation.ERROR_PASSWORD_RESTRICTION -Windows.Win32.Foundation.ERROR_PATCH_MANAGED_ADVERTISED_PRODUCT -Windows.Win32.Foundation.ERROR_PATCH_NO_SEQUENCE -Windows.Win32.Foundation.ERROR_PATCH_PACKAGE_INVALID -Windows.Win32.Foundation.ERROR_PATCH_PACKAGE_OPEN_FAILED -Windows.Win32.Foundation.ERROR_PATCH_PACKAGE_REJECTED -Windows.Win32.Foundation.ERROR_PATCH_PACKAGE_UNSUPPORTED -Windows.Win32.Foundation.ERROR_PATCH_REMOVAL_DISALLOWED -Windows.Win32.Foundation.ERROR_PATCH_REMOVAL_UNSUPPORTED -Windows.Win32.Foundation.ERROR_PATCH_TARGET_NOT_FOUND -Windows.Win32.Foundation.ERROR_PATH_BUSY -Windows.Win32.Foundation.ERROR_PATH_NOT_FOUND -Windows.Win32.Foundation.ERROR_PER_USER_TRUST_QUOTA_EXCEEDED -Windows.Win32.Foundation.ERROR_PIPE_BUSY -Windows.Win32.Foundation.ERROR_PIPE_CONNECTED -Windows.Win32.Foundation.ERROR_PIPE_LISTENING -Windows.Win32.Foundation.ERROR_PIPE_LOCAL -Windows.Win32.Foundation.ERROR_PIPE_NOT_CONNECTED -Windows.Win32.Foundation.ERROR_PKINIT_FAILURE -Windows.Win32.Foundation.ERROR_PLUGPLAY_QUERY_VETOED -Windows.Win32.Foundation.ERROR_PNP_BAD_MPS_TABLE -Windows.Win32.Foundation.ERROR_PNP_INVALID_ID -Windows.Win32.Foundation.ERROR_PNP_IRQ_TRANSLATION_FAILED -Windows.Win32.Foundation.ERROR_PNP_QUERY_REMOVE_DEVICE_TIMEOUT -Windows.Win32.Foundation.ERROR_PNP_QUERY_REMOVE_RELATED_DEVICE_TIMEOUT -Windows.Win32.Foundation.ERROR_PNP_QUERY_REMOVE_UNRELATED_DEVICE_TIMEOUT -Windows.Win32.Foundation.ERROR_PNP_REBOOT_REQUIRED -Windows.Win32.Foundation.ERROR_PNP_RESTART_ENUMERATION -Windows.Win32.Foundation.ERROR_PNP_TRANSLATION_FAILED -Windows.Win32.Foundation.ERROR_POINT_NOT_FOUND -Windows.Win32.Foundation.ERROR_POLICY_OBJECT_NOT_FOUND -Windows.Win32.Foundation.ERROR_POLICY_ONLY_IN_DS -Windows.Win32.Foundation.ERROR_POPUP_ALREADY_ACTIVE -Windows.Win32.Foundation.ERROR_PORT_MESSAGE_TOO_LONG -Windows.Win32.Foundation.ERROR_PORT_NOT_SET -Windows.Win32.Foundation.ERROR_PORT_UNREACHABLE -Windows.Win32.Foundation.ERROR_POSSIBLE_DEADLOCK -Windows.Win32.Foundation.ERROR_POTENTIAL_FILE_FOUND -Windows.Win32.Foundation.ERROR_PREDEFINED_HANDLE -Windows.Win32.Foundation.ERROR_PRIMARY_TRANSPORT_CONNECT_FAILED -Windows.Win32.Foundation.ERROR_PRINT_CANCELLED -Windows.Win32.Foundation.ERROR_PRINTER_ALREADY_EXISTS -Windows.Win32.Foundation.ERROR_PRINTER_DELETED -Windows.Win32.Foundation.ERROR_PRINTER_DRIVER_ALREADY_INSTALLED -Windows.Win32.Foundation.ERROR_PRINTQ_FULL -Windows.Win32.Foundation.ERROR_PRIVATE_DIALOG_INDEX -Windows.Win32.Foundation.ERROR_PRIVILEGE_NOT_HELD -Windows.Win32.Foundation.ERROR_PROC_NOT_FOUND -Windows.Win32.Foundation.ERROR_PROCESS_ABORTED -Windows.Win32.Foundation.ERROR_PROCESS_IN_JOB -Windows.Win32.Foundation.ERROR_PROCESS_IS_PROTECTED -Windows.Win32.Foundation.ERROR_PROCESS_MODE_ALREADY_BACKGROUND -Windows.Win32.Foundation.ERROR_PROCESS_MODE_NOT_BACKGROUND -Windows.Win32.Foundation.ERROR_PROCESS_NOT_IN_JOB -Windows.Win32.Foundation.ERROR_PRODUCT_UNINSTALLED -Windows.Win32.Foundation.ERROR_PRODUCT_VERSION -Windows.Win32.Foundation.ERROR_PROFILING_AT_LIMIT -Windows.Win32.Foundation.ERROR_PROFILING_NOT_STARTED -Windows.Win32.Foundation.ERROR_PROFILING_NOT_STOPPED -Windows.Win32.Foundation.ERROR_PROMOTION_ACTIVE -Windows.Win32.Foundation.ERROR_PROTOCOL_UNREACHABLE -Windows.Win32.Foundation.ERROR_PWD_HISTORY_CONFLICT -Windows.Win32.Foundation.ERROR_PWD_TOO_LONG -Windows.Win32.Foundation.ERROR_PWD_TOO_RECENT -Windows.Win32.Foundation.ERROR_PWD_TOO_SHORT -Windows.Win32.Foundation.ERROR_QUOTA_ACTIVITY -Windows.Win32.Foundation.ERROR_QUOTA_LIST_INCONSISTENT -Windows.Win32.Foundation.ERROR_RANGE_LIST_CONFLICT -Windows.Win32.Foundation.ERROR_RANGE_NOT_FOUND -Windows.Win32.Foundation.ERROR_READ_FAULT -Windows.Win32.Foundation.ERROR_RECEIVE_EXPEDITED -Windows.Win32.Foundation.ERROR_RECEIVE_PARTIAL -Windows.Win32.Foundation.ERROR_RECEIVE_PARTIAL_EXPEDITED -Windows.Win32.Foundation.ERROR_RECOVERY_FAILURE -Windows.Win32.Foundation.ERROR_REDIR_PAUSED -Windows.Win32.Foundation.ERROR_REDIRECTOR_HAS_OPEN_HANDLES -Windows.Win32.Foundation.ERROR_REG_NAT_CONSUMPTION -Windows.Win32.Foundation.ERROR_REGISTRY_CORRUPT -Windows.Win32.Foundation.ERROR_REGISTRY_HIVE_RECOVERED -Windows.Win32.Foundation.ERROR_REGISTRY_IO_FAILED -Windows.Win32.Foundation.ERROR_REGISTRY_QUOTA_LIMIT -Windows.Win32.Foundation.ERROR_REGISTRY_RECOVERED -Windows.Win32.Foundation.ERROR_RELOC_CHAIN_XEEDS_SEGLIM -Windows.Win32.Foundation.ERROR_REM_NOT_LIST -Windows.Win32.Foundation.ERROR_REMOTE_PRINT_CONNECTIONS_BLOCKED -Windows.Win32.Foundation.ERROR_REMOTE_SESSION_LIMIT_EXCEEDED -Windows.Win32.Foundation.ERROR_REMOTE_STORAGE_MEDIA_ERROR -Windows.Win32.Foundation.ERROR_REMOTE_STORAGE_NOT_ACTIVE -Windows.Win32.Foundation.ERROR_REPARSE -Windows.Win32.Foundation.ERROR_REPARSE_ATTRIBUTE_CONFLICT -Windows.Win32.Foundation.ERROR_REPARSE_OBJECT -Windows.Win32.Foundation.ERROR_REPARSE_POINT_ENCOUNTERED -Windows.Win32.Foundation.ERROR_REPARSE_TAG_INVALID -Windows.Win32.Foundation.ERROR_REPARSE_TAG_MISMATCH -Windows.Win32.Foundation.ERROR_REPLY_MESSAGE_MISMATCH -Windows.Win32.Foundation.ERROR_REQ_NOT_ACCEP -Windows.Win32.Foundation.ERROR_REQUEST_ABORTED -Windows.Win32.Foundation.ERROR_REQUEST_OUT_OF_SEQUENCE -Windows.Win32.Foundation.ERROR_REQUEST_PAUSED -Windows.Win32.Foundation.ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION -Windows.Win32.Foundation.ERROR_RESIDENT_FILE_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_RESOURCE_CALL_TIMED_OUT -Windows.Win32.Foundation.ERROR_RESOURCE_DATA_NOT_FOUND -Windows.Win32.Foundation.ERROR_RESOURCE_LANG_NOT_FOUND -Windows.Win32.Foundation.ERROR_RESOURCE_NAME_NOT_FOUND -Windows.Win32.Foundation.ERROR_RESOURCE_REQUIREMENTS_CHANGED -Windows.Win32.Foundation.ERROR_RESOURCE_TYPE_NOT_FOUND -Windows.Win32.Foundation.ERROR_RESTART_APPLICATION -Windows.Win32.Foundation.ERROR_RESUME_HIBERNATION -Windows.Win32.Foundation.ERROR_RETRY -Windows.Win32.Foundation.ERROR_RETURN_ADDRESS_HIJACK_ATTEMPT -Windows.Win32.Foundation.ERROR_REVISION_MISMATCH -Windows.Win32.Foundation.ERROR_RING2_STACK_IN_USE -Windows.Win32.Foundation.ERROR_RING2SEG_MUST_BE_MOVABLE -Windows.Win32.Foundation.ERROR_RMODE_APP -Windows.Win32.Foundation.ERROR_ROWSNOTRELEASED -Windows.Win32.Foundation.ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT -Windows.Win32.Foundation.ERROR_RUNLEVEL_SWITCH_TIMEOUT -Windows.Win32.Foundation.ERROR_RWRAW_ENCRYPTED_FILE_NOT_ENCRYPTED -Windows.Win32.Foundation.ERROR_RWRAW_ENCRYPTED_INVALID_EDATAINFO_FILEOFFSET -Windows.Win32.Foundation.ERROR_RWRAW_ENCRYPTED_INVALID_EDATAINFO_FILERANGE -Windows.Win32.Foundation.ERROR_RWRAW_ENCRYPTED_INVALID_EDATAINFO_PARAMETER -Windows.Win32.Foundation.ERROR_RXACT_COMMIT_FAILURE -Windows.Win32.Foundation.ERROR_RXACT_COMMIT_NECESSARY -Windows.Win32.Foundation.ERROR_RXACT_COMMITTED -Windows.Win32.Foundation.ERROR_RXACT_INVALID_STATE -Windows.Win32.Foundation.ERROR_RXACT_STATE_CREATED -Windows.Win32.Foundation.ERROR_SAM_INIT_FAILURE -Windows.Win32.Foundation.ERROR_SAME_DRIVE -Windows.Win32.Foundation.ERROR_SCOPE_NOT_FOUND -Windows.Win32.Foundation.ERROR_SCREEN_ALREADY_LOCKED -Windows.Win32.Foundation.ERROR_SCRUB_DATA_DISABLED -Windows.Win32.Foundation.ERROR_SECRET_TOO_LONG -Windows.Win32.Foundation.ERROR_SECTION_DIRECT_MAP_ONLY -Windows.Win32.Foundation.ERROR_SECTOR_NOT_FOUND -Windows.Win32.Foundation.ERROR_SECURITY_DENIES_OPERATION -Windows.Win32.Foundation.ERROR_SECURITY_STREAM_IS_INCONSISTENT -Windows.Win32.Foundation.ERROR_SEEK -Windows.Win32.Foundation.ERROR_SEEK_ON_DEVICE -Windows.Win32.Foundation.ERROR_SEGMENT_NOTIFICATION -Windows.Win32.Foundation.ERROR_SEM_IS_SET -Windows.Win32.Foundation.ERROR_SEM_NOT_FOUND -Windows.Win32.Foundation.ERROR_SEM_OWNER_DIED -Windows.Win32.Foundation.ERROR_SEM_TIMEOUT -Windows.Win32.Foundation.ERROR_SEM_USER_LIMIT -Windows.Win32.Foundation.ERROR_SERIAL_NO_DEVICE -Windows.Win32.Foundation.ERROR_SERVER_DISABLED -Windows.Win32.Foundation.ERROR_SERVER_HAS_OPEN_HANDLES -Windows.Win32.Foundation.ERROR_SERVER_NOT_DISABLED -Windows.Win32.Foundation.ERROR_SERVER_SHUTDOWN_IN_PROGRESS -Windows.Win32.Foundation.ERROR_SERVER_SID_MISMATCH -Windows.Win32.Foundation.ERROR_SERVER_TRANSPORT_CONFLICT -Windows.Win32.Foundation.ERROR_SERVICE_ALREADY_RUNNING -Windows.Win32.Foundation.ERROR_SERVICE_CANNOT_ACCEPT_CTRL -Windows.Win32.Foundation.ERROR_SERVICE_DATABASE_LOCKED -Windows.Win32.Foundation.ERROR_SERVICE_DEPENDENCY_DELETED -Windows.Win32.Foundation.ERROR_SERVICE_DEPENDENCY_FAIL -Windows.Win32.Foundation.ERROR_SERVICE_DISABLED -Windows.Win32.Foundation.ERROR_SERVICE_DOES_NOT_EXIST -Windows.Win32.Foundation.ERROR_SERVICE_EXISTS -Windows.Win32.Foundation.ERROR_SERVICE_LOGON_FAILED -Windows.Win32.Foundation.ERROR_SERVICE_MARKED_FOR_DELETE -Windows.Win32.Foundation.ERROR_SERVICE_NEVER_STARTED -Windows.Win32.Foundation.ERROR_SERVICE_NO_THREAD -Windows.Win32.Foundation.ERROR_SERVICE_NOT_ACTIVE -Windows.Win32.Foundation.ERROR_SERVICE_NOT_FOUND -Windows.Win32.Foundation.ERROR_SERVICE_NOT_IN_EXE -Windows.Win32.Foundation.ERROR_SERVICE_NOTIFICATION -Windows.Win32.Foundation.ERROR_SERVICE_NOTIFY_CLIENT_LAGGING -Windows.Win32.Foundation.ERROR_SERVICE_REQUEST_TIMEOUT -Windows.Win32.Foundation.ERROR_SERVICE_SPECIFIC_ERROR -Windows.Win32.Foundation.ERROR_SERVICE_START_HANG -Windows.Win32.Foundation.ERROR_SESSION_CREDENTIAL_CONFLICT -Windows.Win32.Foundation.ERROR_SESSION_KEY_TOO_SHORT -Windows.Win32.Foundation.ERROR_SET_CONTEXT_DENIED -Windows.Win32.Foundation.ERROR_SET_NOT_FOUND -Windows.Win32.Foundation.ERROR_SET_POWER_STATE_FAILED -Windows.Win32.Foundation.ERROR_SET_POWER_STATE_VETOED -Windows.Win32.Foundation.ERROR_SETCOUNT_ON_BAD_LB -Windows.Win32.Foundation.ERROR_SETMARK_DETECTED -Windows.Win32.Foundation.ERROR_SHARED_POLICY -Windows.Win32.Foundation.ERROR_SHARING_BUFFER_EXCEEDED -Windows.Win32.Foundation.ERROR_SHARING_PAUSED -Windows.Win32.Foundation.ERROR_SHARING_VIOLATION -Windows.Win32.Foundation.ERROR_SHORT_NAMES_NOT_ENABLED_ON_VOLUME -Windows.Win32.Foundation.ERROR_SHUTDOWN_DISKS_NOT_IN_MAINTENANCE_MODE -Windows.Win32.Foundation.ERROR_SHUTDOWN_IN_PROGRESS -Windows.Win32.Foundation.ERROR_SHUTDOWN_IS_SCHEDULED -Windows.Win32.Foundation.ERROR_SHUTDOWN_USERS_LOGGED_ON -Windows.Win32.Foundation.ERROR_SIGNAL_PENDING -Windows.Win32.Foundation.ERROR_SIGNAL_REFUSED -Windows.Win32.Foundation.ERROR_SINGLE_INSTANCE_APP -Windows.Win32.Foundation.ERROR_SMARTCARD_SUBSYSTEM_FAILURE -Windows.Win32.Foundation.ERROR_SMB1_NOT_AVAILABLE -Windows.Win32.Foundation.ERROR_SMB_GUEST_LOGON_BLOCKED -Windows.Win32.Foundation.ERROR_SMR_GARBAGE_COLLECTION_REQUIRED -Windows.Win32.Foundation.ERROR_SOME_NOT_MAPPED -Windows.Win32.Foundation.ERROR_SOURCE_ELEMENT_EMPTY -Windows.Win32.Foundation.ERROR_SPARSE_FILE_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_SPECIAL_ACCOUNT -Windows.Win32.Foundation.ERROR_SPECIAL_GROUP -Windows.Win32.Foundation.ERROR_SPECIAL_USER -Windows.Win32.Foundation.ERROR_SRC_SRV_DLL_LOAD_FAILED -Windows.Win32.Foundation.ERROR_STACK_BUFFER_OVERRUN -Windows.Win32.Foundation.ERROR_STACK_OVERFLOW -Windows.Win32.Foundation.ERROR_STACK_OVERFLOW_READ -Windows.Win32.Foundation.ERROR_STOPPED_ON_SYMLINK -Windows.Win32.Foundation.ERROR_STORAGE_LOST_DATA_PERSISTENCE -Windows.Win32.Foundation.ERROR_STORAGE_RESERVE_ALREADY_EXISTS -Windows.Win32.Foundation.ERROR_STORAGE_RESERVE_DOES_NOT_EXIST -Windows.Win32.Foundation.ERROR_STORAGE_RESERVE_ID_INVALID -Windows.Win32.Foundation.ERROR_STORAGE_RESERVE_NOT_EMPTY -Windows.Win32.Foundation.ERROR_STORAGE_STACK_ACCESS_DENIED -Windows.Win32.Foundation.ERROR_STORAGE_TOPOLOGY_ID_MISMATCH -Windows.Win32.Foundation.ERROR_STRICT_CFG_VIOLATION -Windows.Win32.Foundation.ERROR_SUBST_TO_JOIN -Windows.Win32.Foundation.ERROR_SUBST_TO_SUBST -Windows.Win32.Foundation.ERROR_SUCCESS -Windows.Win32.Foundation.ERROR_SUCCESS_REBOOT_INITIATED -Windows.Win32.Foundation.ERROR_SWAPERROR -Windows.Win32.Foundation.ERROR_SYMLINK_CLASS_DISABLED -Windows.Win32.Foundation.ERROR_SYMLINK_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_SYNC_FOREGROUND_REFRESH_REQUIRED -Windows.Win32.Foundation.ERROR_SYNCHRONIZATION_REQUIRED -Windows.Win32.Foundation.ERROR_SYSTEM_HIVE_TOO_LARGE -Windows.Win32.Foundation.ERROR_SYSTEM_IMAGE_BAD_SIGNATURE -Windows.Win32.Foundation.ERROR_SYSTEM_POWERSTATE_COMPLEX_TRANSITION -Windows.Win32.Foundation.ERROR_SYSTEM_POWERSTATE_TRANSITION -Windows.Win32.Foundation.ERROR_SYSTEM_PROCESS_TERMINATED -Windows.Win32.Foundation.ERROR_SYSTEM_SHUTDOWN -Windows.Win32.Foundation.ERROR_SYSTEM_TRACE -Windows.Win32.Foundation.ERROR_THREAD_1_INACTIVE -Windows.Win32.Foundation.ERROR_THREAD_ALREADY_IN_TASK -Windows.Win32.Foundation.ERROR_THREAD_MODE_ALREADY_BACKGROUND -Windows.Win32.Foundation.ERROR_THREAD_MODE_NOT_BACKGROUND -Windows.Win32.Foundation.ERROR_THREAD_NOT_IN_PROCESS -Windows.Win32.Foundation.ERROR_THREAD_WAS_SUSPENDED -Windows.Win32.Foundation.ERROR_TIME_SENSITIVE_THREAD -Windows.Win32.Foundation.ERROR_TIME_SKEW -Windows.Win32.Foundation.ERROR_TIMEOUT -Windows.Win32.Foundation.ERROR_TIMER_NOT_CANCELED -Windows.Win32.Foundation.ERROR_TIMER_RESOLUTION_NOT_SET -Windows.Win32.Foundation.ERROR_TIMER_RESUME_IGNORED -Windows.Win32.Foundation.ERROR_TLW_WITH_WSCHILD -Windows.Win32.Foundation.ERROR_TOKEN_ALREADY_IN_USE -Windows.Win32.Foundation.ERROR_TOO_MANY_CMDS -Windows.Win32.Foundation.ERROR_TOO_MANY_CONTEXT_IDS -Windows.Win32.Foundation.ERROR_TOO_MANY_DESCRIPTORS -Windows.Win32.Foundation.ERROR_TOO_MANY_LINKS -Windows.Win32.Foundation.ERROR_TOO_MANY_LUIDS_REQUESTED -Windows.Win32.Foundation.ERROR_TOO_MANY_MODULES -Windows.Win32.Foundation.ERROR_TOO_MANY_MUXWAITERS -Windows.Win32.Foundation.ERROR_TOO_MANY_NAMES -Windows.Win32.Foundation.ERROR_TOO_MANY_OPEN_FILES -Windows.Win32.Foundation.ERROR_TOO_MANY_POSTS -Windows.Win32.Foundation.ERROR_TOO_MANY_SECRETS -Windows.Win32.Foundation.ERROR_TOO_MANY_SEM_REQUESTS -Windows.Win32.Foundation.ERROR_TOO_MANY_SEMAPHORES -Windows.Win32.Foundation.ERROR_TOO_MANY_SESS -Windows.Win32.Foundation.ERROR_TOO_MANY_SIDS -Windows.Win32.Foundation.ERROR_TOO_MANY_TCBS -Windows.Win32.Foundation.ERROR_TOO_MANY_THREADS -Windows.Win32.Foundation.ERROR_TRANSLATION_COMPLETE -Windows.Win32.Foundation.ERROR_TRUST_FAILURE -Windows.Win32.Foundation.ERROR_TRUSTED_DOMAIN_FAILURE -Windows.Win32.Foundation.ERROR_TRUSTED_RELATIONSHIP_FAILURE -Windows.Win32.Foundation.ERROR_UNABLE_TO_LOCK_MEDIA -Windows.Win32.Foundation.ERROR_UNABLE_TO_MOVE_REPLACEMENT -Windows.Win32.Foundation.ERROR_UNABLE_TO_MOVE_REPLACEMENT_2 -Windows.Win32.Foundation.ERROR_UNABLE_TO_REMOVE_REPLACED -Windows.Win32.Foundation.ERROR_UNABLE_TO_UNLOAD_MEDIA -Windows.Win32.Foundation.ERROR_UNDEFINED_CHARACTER -Windows.Win32.Foundation.ERROR_UNDEFINED_SCOPE -Windows.Win32.Foundation.ERROR_UNEXP_NET_ERR -Windows.Win32.Foundation.ERROR_UNEXPECTED_MM_CREATE_ERR -Windows.Win32.Foundation.ERROR_UNEXPECTED_MM_EXTEND_ERR -Windows.Win32.Foundation.ERROR_UNEXPECTED_MM_MAP_ERROR -Windows.Win32.Foundation.ERROR_UNEXPECTED_NTCACHEMANAGER_ERROR -Windows.Win32.Foundation.ERROR_UNHANDLED_EXCEPTION -Windows.Win32.Foundation.ERROR_UNIDENTIFIED_ERROR -Windows.Win32.Foundation.ERROR_UNKNOWN_COMPONENT -Windows.Win32.Foundation.ERROR_UNKNOWN_FEATURE -Windows.Win32.Foundation.ERROR_UNKNOWN_PATCH -Windows.Win32.Foundation.ERROR_UNKNOWN_PORT -Windows.Win32.Foundation.ERROR_UNKNOWN_PRINTER_DRIVER -Windows.Win32.Foundation.ERROR_UNKNOWN_PRINTPROCESSOR -Windows.Win32.Foundation.ERROR_UNKNOWN_PRODUCT -Windows.Win32.Foundation.ERROR_UNKNOWN_PROPERTY -Windows.Win32.Foundation.ERROR_UNKNOWN_REVISION -Windows.Win32.Foundation.ERROR_UNRECOGNIZED_MEDIA -Windows.Win32.Foundation.ERROR_UNRECOGNIZED_VOLUME -Windows.Win32.Foundation.ERROR_UNSATISFIED_DEPENDENCIES -Windows.Win32.Foundation.ERROR_UNSUPPORTED_COMPRESSION -Windows.Win32.Foundation.ERROR_UNSUPPORTED_TYPE -Windows.Win32.Foundation.ERROR_UNTRUSTED_MOUNT_POINT -Windows.Win32.Foundation.ERROR_UNWIND -Windows.Win32.Foundation.ERROR_UNWIND_CONSOLIDATE -Windows.Win32.Foundation.ERROR_USER_APC -Windows.Win32.Foundation.ERROR_USER_DELETE_TRUST_QUOTA_EXCEEDED -Windows.Win32.Foundation.ERROR_USER_EXISTS -Windows.Win32.Foundation.ERROR_USER_MAPPED_FILE -Windows.Win32.Foundation.ERROR_USER_PROFILE_LOAD -Windows.Win32.Foundation.ERROR_VALIDATE_CONTINUE -Windows.Win32.Foundation.ERROR_VC_DISCONNECTED -Windows.Win32.Foundation.ERROR_VDM_DISALLOWED -Windows.Win32.Foundation.ERROR_VDM_HARD_ERROR -Windows.Win32.Foundation.ERROR_VERIFIER_STOP -Windows.Win32.Foundation.ERROR_VERSION_PARSE_ERROR -Windows.Win32.Foundation.ERROR_VIRUS_DELETED -Windows.Win32.Foundation.ERROR_VIRUS_INFECTED -Windows.Win32.Foundation.ERROR_VOLSNAP_HIBERNATE_READY -Windows.Win32.Foundation.ERROR_VOLSNAP_PREPARE_HIBERNATE -Windows.Win32.Foundation.ERROR_VOLUME_MOUNTED -Windows.Win32.Foundation.ERROR_VOLUME_NOT_CLUSTER_ALIGNED -Windows.Win32.Foundation.ERROR_VOLUME_NOT_SIS_ENABLED -Windows.Win32.Foundation.ERROR_VOLUME_NOT_SUPPORT_EFS -Windows.Win32.Foundation.ERROR_VOLUME_NOT_SUPPORTED -Windows.Win32.Foundation.ERROR_VOLUME_WRITE_ACCESS_DENIED -Windows.Win32.Foundation.ERROR_WAIT_1 -Windows.Win32.Foundation.ERROR_WAIT_2 -Windows.Win32.Foundation.ERROR_WAIT_3 -Windows.Win32.Foundation.ERROR_WAIT_63 -Windows.Win32.Foundation.ERROR_WAIT_FOR_OPLOCK -Windows.Win32.Foundation.ERROR_WAIT_NO_CHILDREN -Windows.Win32.Foundation.ERROR_WAKE_SYSTEM -Windows.Win32.Foundation.ERROR_WAKE_SYSTEM_DEBUGGER -Windows.Win32.Foundation.ERROR_WAS_LOCKED -Windows.Win32.Foundation.ERROR_WAS_UNLOCKED -Windows.Win32.Foundation.ERROR_WEAK_WHFBKEY_BLOCKED -Windows.Win32.Foundation.ERROR_WINDOW_NOT_COMBOBOX -Windows.Win32.Foundation.ERROR_WINDOW_NOT_DIALOG -Windows.Win32.Foundation.ERROR_WINDOW_OF_OTHER_THREAD -Windows.Win32.Foundation.ERROR_WIP_ENCRYPTION_FAILED -Windows.Win32.Foundation.ERROR_WOF_FILE_RESOURCE_TABLE_CORRUPT -Windows.Win32.Foundation.ERROR_WOF_WIM_HEADER_CORRUPT -Windows.Win32.Foundation.ERROR_WOF_WIM_RESOURCE_TABLE_CORRUPT -Windows.Win32.Foundation.ERROR_WORKING_SET_QUOTA -Windows.Win32.Foundation.ERROR_WOW_ASSERTION -Windows.Win32.Foundation.ERROR_WRITE_FAULT -Windows.Win32.Foundation.ERROR_WRITE_PROTECT -Windows.Win32.Foundation.ERROR_WRONG_COMPARTMENT -Windows.Win32.Foundation.ERROR_WRONG_DISK -Windows.Win32.Foundation.ERROR_WRONG_EFS -Windows.Win32.Foundation.ERROR_WRONG_PASSWORD -Windows.Win32.Foundation.ERROR_WRONG_TARGET_NAME -Windows.Win32.Foundation.ERROR_WX86_ERROR -Windows.Win32.Foundation.ERROR_WX86_WARNING -Windows.Win32.Foundation.ERROR_XML_PARSE_ERROR -Windows.Win32.Foundation.ERROR_XMLDSIG_ERROR -Windows.Win32.Foundation.EXCEPTION_STACK_OVERFLOW -Windows.Win32.Foundation.FALSE -Windows.Win32.Foundation.FARPROC -Windows.Win32.Foundation.FILETIME -Windows.Win32.Foundation.FRS_ERR_SYSVOL_POPULATE_TIMEOUT -Windows.Win32.Foundation.GENERIC_ACCESS_RIGHTS -Windows.Win32.Foundation.GENERIC_ALL -Windows.Win32.Foundation.GENERIC_EXECUTE -Windows.Win32.Foundation.GENERIC_READ -Windows.Win32.Foundation.GENERIC_WRITE -Windows.Win32.Foundation.GetLastError -Windows.Win32.Foundation.HANDLE -Windows.Win32.Foundation.HANDLE_FLAG_INHERIT -Windows.Win32.Foundation.HANDLE_FLAG_PROTECT_FROM_CLOSE -Windows.Win32.Foundation.HANDLE_FLAGS -Windows.Win32.Foundation.HMODULE -Windows.Win32.Foundation.LocalFree -Windows.Win32.Foundation.MAX_PATH -Windows.Win32.Foundation.NO_ERROR -Windows.Win32.Foundation.NTSTATUS -Windows.Win32.Foundation.RtlNtStatusToDosError -Windows.Win32.Foundation.SetHandleInformation -Windows.Win32.Foundation.SetLastError -Windows.Win32.Foundation.STATUS_DELETE_PENDING -Windows.Win32.Foundation.STATUS_DIRECTORY_NOT_EMPTY -Windows.Win32.Foundation.STATUS_END_OF_FILE -Windows.Win32.Foundation.STATUS_FILE_DELETED -Windows.Win32.Foundation.STATUS_INVALID_HANDLE -Windows.Win32.Foundation.STATUS_INVALID_PARAMETER -Windows.Win32.Foundation.STATUS_NOT_IMPLEMENTED -Windows.Win32.Foundation.STATUS_PENDING -Windows.Win32.Foundation.STATUS_SHARING_VIOLATION -Windows.Win32.Foundation.STATUS_SUCCESS -Windows.Win32.Foundation.TRUE -Windows.Win32.Foundation.UNICODE_STRING -Windows.Win32.Foundation.WAIT_ABANDONED -Windows.Win32.Foundation.WAIT_ABANDONED_0 -Windows.Win32.Foundation.WAIT_FAILED -Windows.Win32.Foundation.WAIT_IO_COMPLETION -Windows.Win32.Foundation.WAIT_OBJECT_0 -Windows.Win32.Foundation.WAIT_TIMEOUT -Windows.Win32.Foundation.WIN32_ERROR -Windows.Win32.Globalization.COMPARESTRING_RESULT -Windows.Win32.Globalization.CompareStringOrdinal -Windows.Win32.Globalization.CP_UTF8 -Windows.Win32.Globalization.CSTR_EQUAL -Windows.Win32.Globalization.CSTR_GREATER_THAN -Windows.Win32.Globalization.CSTR_LESS_THAN -Windows.Win32.Globalization.MB_COMPOSITE -Windows.Win32.Globalization.MB_ERR_INVALID_CHARS -Windows.Win32.Globalization.MB_PRECOMPOSED -Windows.Win32.Globalization.MB_USEGLYPHCHARS -Windows.Win32.Globalization.MULTI_BYTE_TO_WIDE_CHAR_FLAGS -Windows.Win32.Globalization.MultiByteToWideChar -Windows.Win32.Globalization.WC_ERR_INVALID_CHARS -Windows.Win32.Globalization.WideCharToMultiByte -Windows.Win32.Networking.WinSock.accept -Windows.Win32.Networking.WinSock.ADDRESS_FAMILY -Windows.Win32.Networking.WinSock.ADDRINFOA -Windows.Win32.Networking.WinSock.AF_INET -Windows.Win32.Networking.WinSock.AF_INET6 -Windows.Win32.Networking.WinSock.AF_UNIX -Windows.Win32.Networking.WinSock.AF_UNSPEC -Windows.Win32.Networking.WinSock.bind -Windows.Win32.Networking.WinSock.closesocket -Windows.Win32.Networking.WinSock.connect -Windows.Win32.Networking.WinSock.FD_SET -Windows.Win32.Networking.WinSock.FIONBIO -Windows.Win32.Networking.WinSock.freeaddrinfo -Windows.Win32.Networking.WinSock.getaddrinfo -Windows.Win32.Networking.WinSock.getpeername -Windows.Win32.Networking.WinSock.getsockname -Windows.Win32.Networking.WinSock.getsockopt -Windows.Win32.Networking.WinSock.IN6_ADDR -Windows.Win32.Networking.WinSock.IN_ADDR -Windows.Win32.Networking.WinSock.INVALID_SOCKET -Windows.Win32.Networking.WinSock.ioctlsocket -Windows.Win32.Networking.WinSock.IP_ADD_MEMBERSHIP -Windows.Win32.Networking.WinSock.IP_DROP_MEMBERSHIP -Windows.Win32.Networking.WinSock.IP_MREQ -Windows.Win32.Networking.WinSock.IP_MULTICAST_LOOP -Windows.Win32.Networking.WinSock.IP_MULTICAST_TTL -Windows.Win32.Networking.WinSock.IP_TTL -Windows.Win32.Networking.WinSock.IPPROTO -Windows.Win32.Networking.WinSock.IPPROTO_AH -Windows.Win32.Networking.WinSock.IPPROTO_CBT -Windows.Win32.Networking.WinSock.IPPROTO_DSTOPTS -Windows.Win32.Networking.WinSock.IPPROTO_EGP -Windows.Win32.Networking.WinSock.IPPROTO_ESP -Windows.Win32.Networking.WinSock.IPPROTO_FRAGMENT -Windows.Win32.Networking.WinSock.IPPROTO_GGP -Windows.Win32.Networking.WinSock.IPPROTO_HOPOPTS -Windows.Win32.Networking.WinSock.IPPROTO_ICLFXBM -Windows.Win32.Networking.WinSock.IPPROTO_ICMP -Windows.Win32.Networking.WinSock.IPPROTO_ICMPV6 -Windows.Win32.Networking.WinSock.IPPROTO_IDP -Windows.Win32.Networking.WinSock.IPPROTO_IGMP -Windows.Win32.Networking.WinSock.IPPROTO_IGP -Windows.Win32.Networking.WinSock.IPPROTO_IP -Windows.Win32.Networking.WinSock.IPPROTO_IPV4 -Windows.Win32.Networking.WinSock.IPPROTO_IPV6 -Windows.Win32.Networking.WinSock.IPPROTO_L2TP -Windows.Win32.Networking.WinSock.IPPROTO_MAX -Windows.Win32.Networking.WinSock.IPPROTO_ND -Windows.Win32.Networking.WinSock.IPPROTO_NONE -Windows.Win32.Networking.WinSock.IPPROTO_PGM -Windows.Win32.Networking.WinSock.IPPROTO_PIM -Windows.Win32.Networking.WinSock.IPPROTO_PUP -Windows.Win32.Networking.WinSock.IPPROTO_RAW -Windows.Win32.Networking.WinSock.IPPROTO_RDP -Windows.Win32.Networking.WinSock.IPPROTO_RESERVED_IPSEC -Windows.Win32.Networking.WinSock.IPPROTO_RESERVED_IPSECOFFLOAD -Windows.Win32.Networking.WinSock.IPPROTO_RESERVED_MAX -Windows.Win32.Networking.WinSock.IPPROTO_RESERVED_RAW -Windows.Win32.Networking.WinSock.IPPROTO_RESERVED_WNV -Windows.Win32.Networking.WinSock.IPPROTO_RM -Windows.Win32.Networking.WinSock.IPPROTO_ROUTING -Windows.Win32.Networking.WinSock.IPPROTO_SCTP -Windows.Win32.Networking.WinSock.IPPROTO_ST -Windows.Win32.Networking.WinSock.IPPROTO_TCP -Windows.Win32.Networking.WinSock.IPPROTO_UDP -Windows.Win32.Networking.WinSock.IPV6_ADD_MEMBERSHIP -Windows.Win32.Networking.WinSock.IPV6_DROP_MEMBERSHIP -Windows.Win32.Networking.WinSock.IPV6_MREQ -Windows.Win32.Networking.WinSock.IPV6_MULTICAST_LOOP -Windows.Win32.Networking.WinSock.IPV6_V6ONLY -Windows.Win32.Networking.WinSock.LINGER -Windows.Win32.Networking.WinSock.listen -Windows.Win32.Networking.WinSock.LPWSAOVERLAPPED_COMPLETION_ROUTINE -Windows.Win32.Networking.WinSock.MSG_DONTROUTE -Windows.Win32.Networking.WinSock.MSG_OOB -Windows.Win32.Networking.WinSock.MSG_PEEK -Windows.Win32.Networking.WinSock.MSG_PUSH_IMMEDIATE -Windows.Win32.Networking.WinSock.MSG_WAITALL -Windows.Win32.Networking.WinSock.recv -Windows.Win32.Networking.WinSock.recvfrom -Windows.Win32.Networking.WinSock.SD_BOTH -Windows.Win32.Networking.WinSock.SD_RECEIVE -Windows.Win32.Networking.WinSock.SD_SEND -Windows.Win32.Networking.WinSock.select -Windows.Win32.Networking.WinSock.send -Windows.Win32.Networking.WinSock.SEND_RECV_FLAGS -Windows.Win32.Networking.WinSock.sendto -Windows.Win32.Networking.WinSock.setsockopt -Windows.Win32.Networking.WinSock.shutdown -Windows.Win32.Networking.WinSock.SO_BROADCAST -Windows.Win32.Networking.WinSock.SO_ERROR -Windows.Win32.Networking.WinSock.SO_LINGER -Windows.Win32.Networking.WinSock.SO_RCVTIMEO -Windows.Win32.Networking.WinSock.SO_SNDTIMEO -Windows.Win32.Networking.WinSock.SOCK_DGRAM -Windows.Win32.Networking.WinSock.SOCK_RAW -Windows.Win32.Networking.WinSock.SOCK_RDM -Windows.Win32.Networking.WinSock.SOCK_SEQPACKET -Windows.Win32.Networking.WinSock.SOCK_STREAM -Windows.Win32.Networking.WinSock.SOCKADDR -Windows.Win32.Networking.WinSock.SOCKADDR_STORAGE -Windows.Win32.Networking.WinSock.SOCKADDR_UN -Windows.Win32.Networking.WinSock.SOCKET -Windows.Win32.Networking.WinSock.SOCKET_ERROR -Windows.Win32.Networking.WinSock.SOL_SOCKET -Windows.Win32.Networking.WinSock.TCP_NODELAY -Windows.Win32.Networking.WinSock.TIMEVAL -Windows.Win32.Networking.WinSock.WINSOCK_SHUTDOWN_HOW -Windows.Win32.Networking.WinSock.WINSOCK_SOCKET_TYPE -Windows.Win32.Networking.WinSock.WSA_E_CANCELLED -Windows.Win32.Networking.WinSock.WSA_E_NO_MORE -Windows.Win32.Networking.WinSock.WSA_ERROR -Windows.Win32.Networking.WinSock.WSA_FLAG_NO_HANDLE_INHERIT -Windows.Win32.Networking.WinSock.WSA_FLAG_OVERLAPPED -Windows.Win32.Networking.WinSock.WSA_INVALID_HANDLE -Windows.Win32.Networking.WinSock.WSA_INVALID_PARAMETER -Windows.Win32.Networking.WinSock.WSA_IO_INCOMPLETE -Windows.Win32.Networking.WinSock.WSA_IO_PENDING -Windows.Win32.Networking.WinSock.WSA_IPSEC_NAME_POLICY_ERROR -Windows.Win32.Networking.WinSock.WSA_NOT_ENOUGH_MEMORY -Windows.Win32.Networking.WinSock.WSA_OPERATION_ABORTED -Windows.Win32.Networking.WinSock.WSA_QOS_ADMISSION_FAILURE -Windows.Win32.Networking.WinSock.WSA_QOS_BAD_OBJECT -Windows.Win32.Networking.WinSock.WSA_QOS_BAD_STYLE -Windows.Win32.Networking.WinSock.WSA_QOS_EFILTERCOUNT -Windows.Win32.Networking.WinSock.WSA_QOS_EFILTERSTYLE -Windows.Win32.Networking.WinSock.WSA_QOS_EFILTERTYPE -Windows.Win32.Networking.WinSock.WSA_QOS_EFLOWCOUNT -Windows.Win32.Networking.WinSock.WSA_QOS_EFLOWDESC -Windows.Win32.Networking.WinSock.WSA_QOS_EFLOWSPEC -Windows.Win32.Networking.WinSock.WSA_QOS_EOBJLENGTH -Windows.Win32.Networking.WinSock.WSA_QOS_EPOLICYOBJ -Windows.Win32.Networking.WinSock.WSA_QOS_EPROVSPECBUF -Windows.Win32.Networking.WinSock.WSA_QOS_EPSFILTERSPEC -Windows.Win32.Networking.WinSock.WSA_QOS_EPSFLOWSPEC -Windows.Win32.Networking.WinSock.WSA_QOS_ESDMODEOBJ -Windows.Win32.Networking.WinSock.WSA_QOS_ESERVICETYPE -Windows.Win32.Networking.WinSock.WSA_QOS_ESHAPERATEOBJ -Windows.Win32.Networking.WinSock.WSA_QOS_EUNKOWNPSOBJ -Windows.Win32.Networking.WinSock.WSA_QOS_GENERIC_ERROR -Windows.Win32.Networking.WinSock.WSA_QOS_NO_RECEIVERS -Windows.Win32.Networking.WinSock.WSA_QOS_NO_SENDERS -Windows.Win32.Networking.WinSock.WSA_QOS_POLICY_FAILURE -Windows.Win32.Networking.WinSock.WSA_QOS_RECEIVERS -Windows.Win32.Networking.WinSock.WSA_QOS_REQUEST_CONFIRMED -Windows.Win32.Networking.WinSock.WSA_QOS_RESERVED_PETYPE -Windows.Win32.Networking.WinSock.WSA_QOS_SENDERS -Windows.Win32.Networking.WinSock.WSA_QOS_TRAFFIC_CTRL_ERROR -Windows.Win32.Networking.WinSock.WSA_SECURE_HOST_NOT_FOUND -Windows.Win32.Networking.WinSock.WSA_WAIT_EVENT_0 -Windows.Win32.Networking.WinSock.WSA_WAIT_IO_COMPLETION -Windows.Win32.Networking.WinSock.WSABASEERR -Windows.Win32.Networking.WinSock.WSABUF -Windows.Win32.Networking.WinSock.WSACleanup -Windows.Win32.Networking.WinSock.WSADATA -Windows.Win32.Networking.WinSock.WSADuplicateSocketW -Windows.Win32.Networking.WinSock.WSAEACCES -Windows.Win32.Networking.WinSock.WSAEADDRINUSE -Windows.Win32.Networking.WinSock.WSAEADDRNOTAVAIL -Windows.Win32.Networking.WinSock.WSAEAFNOSUPPORT -Windows.Win32.Networking.WinSock.WSAEALREADY -Windows.Win32.Networking.WinSock.WSAEBADF -Windows.Win32.Networking.WinSock.WSAECANCELLED -Windows.Win32.Networking.WinSock.WSAECONNABORTED -Windows.Win32.Networking.WinSock.WSAECONNREFUSED -Windows.Win32.Networking.WinSock.WSAECONNRESET -Windows.Win32.Networking.WinSock.WSAEDESTADDRREQ -Windows.Win32.Networking.WinSock.WSAEDISCON -Windows.Win32.Networking.WinSock.WSAEDQUOT -Windows.Win32.Networking.WinSock.WSAEFAULT -Windows.Win32.Networking.WinSock.WSAEHOSTDOWN -Windows.Win32.Networking.WinSock.WSAEHOSTUNREACH -Windows.Win32.Networking.WinSock.WSAEINPROGRESS -Windows.Win32.Networking.WinSock.WSAEINTR -Windows.Win32.Networking.WinSock.WSAEINVAL -Windows.Win32.Networking.WinSock.WSAEINVALIDPROCTABLE -Windows.Win32.Networking.WinSock.WSAEINVALIDPROVIDER -Windows.Win32.Networking.WinSock.WSAEISCONN -Windows.Win32.Networking.WinSock.WSAELOOP -Windows.Win32.Networking.WinSock.WSAEMFILE -Windows.Win32.Networking.WinSock.WSAEMSGSIZE -Windows.Win32.Networking.WinSock.WSAENAMETOOLONG -Windows.Win32.Networking.WinSock.WSAENETDOWN -Windows.Win32.Networking.WinSock.WSAENETRESET -Windows.Win32.Networking.WinSock.WSAENETUNREACH -Windows.Win32.Networking.WinSock.WSAENOBUFS -Windows.Win32.Networking.WinSock.WSAENOMORE -Windows.Win32.Networking.WinSock.WSAENOPROTOOPT -Windows.Win32.Networking.WinSock.WSAENOTCONN -Windows.Win32.Networking.WinSock.WSAENOTEMPTY -Windows.Win32.Networking.WinSock.WSAENOTSOCK -Windows.Win32.Networking.WinSock.WSAEOPNOTSUPP -Windows.Win32.Networking.WinSock.WSAEPFNOSUPPORT -Windows.Win32.Networking.WinSock.WSAEPROCLIM -Windows.Win32.Networking.WinSock.WSAEPROTONOSUPPORT -Windows.Win32.Networking.WinSock.WSAEPROTOTYPE -Windows.Win32.Networking.WinSock.WSAEPROVIDERFAILEDINIT -Windows.Win32.Networking.WinSock.WSAEREFUSED -Windows.Win32.Networking.WinSock.WSAEREMOTE -Windows.Win32.Networking.WinSock.WSAESHUTDOWN -Windows.Win32.Networking.WinSock.WSAESOCKTNOSUPPORT -Windows.Win32.Networking.WinSock.WSAESTALE -Windows.Win32.Networking.WinSock.WSAETIMEDOUT -Windows.Win32.Networking.WinSock.WSAETOOMANYREFS -Windows.Win32.Networking.WinSock.WSAEUSERS -Windows.Win32.Networking.WinSock.WSAEWOULDBLOCK -Windows.Win32.Networking.WinSock.WSAGetLastError -Windows.Win32.Networking.WinSock.WSAHOST_NOT_FOUND -Windows.Win32.Networking.WinSock.WSANO_DATA -Windows.Win32.Networking.WinSock.WSANO_RECOVERY -Windows.Win32.Networking.WinSock.WSANOTINITIALISED -Windows.Win32.Networking.WinSock.WSAPROTOCOL_INFOW -Windows.Win32.Networking.WinSock.WSAPROTOCOLCHAIN -Windows.Win32.Networking.WinSock.WSARecv -Windows.Win32.Networking.WinSock.WSASend -Windows.Win32.Networking.WinSock.WSASERVICE_NOT_FOUND -Windows.Win32.Networking.WinSock.WSASocketW -Windows.Win32.Networking.WinSock.WSAStartup -Windows.Win32.Networking.WinSock.WSASYSCALLFAILURE -Windows.Win32.Networking.WinSock.WSASYSNOTREADY -Windows.Win32.Networking.WinSock.WSATRY_AGAIN -Windows.Win32.Networking.WinSock.WSATYPE_NOT_FOUND -Windows.Win32.Networking.WinSock.WSAVERNOTSUPPORTED -Windows.Win32.Security.Authentication.Identity.RtlGenRandom -Windows.Win32.Security.SECURITY_ATTRIBUTES -Windows.Win32.Security.TOKEN_ACCESS_MASK -Windows.Win32.Security.TOKEN_ACCESS_PSEUDO_HANDLE -Windows.Win32.Security.TOKEN_ACCESS_PSEUDO_HANDLE_WIN8 -Windows.Win32.Security.TOKEN_ACCESS_SYSTEM_SECURITY -Windows.Win32.Security.TOKEN_ADJUST_DEFAULT -Windows.Win32.Security.TOKEN_ADJUST_GROUPS -Windows.Win32.Security.TOKEN_ADJUST_PRIVILEGES -Windows.Win32.Security.TOKEN_ADJUST_SESSIONID -Windows.Win32.Security.TOKEN_ALL_ACCESS -Windows.Win32.Security.TOKEN_ASSIGN_PRIMARY -Windows.Win32.Security.TOKEN_DELETE -Windows.Win32.Security.TOKEN_DUPLICATE -Windows.Win32.Security.TOKEN_EXECUTE -Windows.Win32.Security.TOKEN_IMPERSONATE -Windows.Win32.Security.TOKEN_QUERY -Windows.Win32.Security.TOKEN_QUERY_SOURCE -Windows.Win32.Security.TOKEN_READ -Windows.Win32.Security.TOKEN_READ_CONTROL -Windows.Win32.Security.TOKEN_TRUST_CONSTRAINT_MASK -Windows.Win32.Security.TOKEN_WRITE -Windows.Win32.Security.TOKEN_WRITE_DAC -Windows.Win32.Security.TOKEN_WRITE_OWNER -Windows.Win32.Storage.FileSystem.BY_HANDLE_FILE_INFORMATION -Windows.Win32.Storage.FileSystem.CALLBACK_CHUNK_FINISHED -Windows.Win32.Storage.FileSystem.CALLBACK_STREAM_SWITCH -Windows.Win32.Storage.FileSystem.CopyFileExW -Windows.Win32.Storage.FileSystem.CREATE_ALWAYS -Windows.Win32.Storage.FileSystem.CREATE_NEW -Windows.Win32.Storage.FileSystem.CreateDirectoryW -Windows.Win32.Storage.FileSystem.CreateFileW -Windows.Win32.Storage.FileSystem.CreateHardLinkW -Windows.Win32.Storage.FileSystem.CreateSymbolicLinkW -Windows.Win32.Storage.FileSystem.DELETE -Windows.Win32.Storage.FileSystem.DeleteFileW -Windows.Win32.Storage.FileSystem.FILE_ACCESS_RIGHTS -Windows.Win32.Storage.FileSystem.FILE_ADD_FILE -Windows.Win32.Storage.FileSystem.FILE_ADD_SUBDIRECTORY -Windows.Win32.Storage.FileSystem.FILE_ALL_ACCESS -Windows.Win32.Storage.FileSystem.FILE_ALLOCATION_INFO -Windows.Win32.Storage.FileSystem.FILE_APPEND_DATA -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_ARCHIVE -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_COMPRESSED -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_DEVICE -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_DIRECTORY -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_EA -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_ENCRYPTED -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_HIDDEN -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_INTEGRITY_STREAM -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_NO_SCRUB_DATA -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_NORMAL -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_NOT_CONTENT_INDEXED -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_OFFLINE -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_PINNED -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_READONLY -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_RECALL_ON_OPEN -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_REPARSE_POINT -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_SPARSE_FILE -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_SYSTEM -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_TAG_INFO -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_TEMPORARY -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_UNPINNED -Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_VIRTUAL -Windows.Win32.Storage.FileSystem.FILE_BASIC_INFO -Windows.Win32.Storage.FileSystem.FILE_BEGIN -Windows.Win32.Storage.FileSystem.FILE_CREATE_PIPE_INSTANCE -Windows.Win32.Storage.FileSystem.FILE_CREATION_DISPOSITION -Windows.Win32.Storage.FileSystem.FILE_CURRENT -Windows.Win32.Storage.FileSystem.FILE_DELETE_CHILD -Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_FLAG_DELETE -Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_FLAG_DO_NOT_DELETE -Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_FLAG_FORCE_IMAGE_SECTION_CHECK -Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE -Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_FLAG_ON_CLOSE -Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_FLAG_POSIX_SEMANTICS -Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_INFO -Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_INFO_EX -Windows.Win32.Storage.FileSystem.FILE_DISPOSITION_INFO_EX_FLAGS -Windows.Win32.Storage.FileSystem.FILE_END -Windows.Win32.Storage.FileSystem.FILE_END_OF_FILE_INFO -Windows.Win32.Storage.FileSystem.FILE_EXECUTE -Windows.Win32.Storage.FileSystem.FILE_FLAG_BACKUP_SEMANTICS -Windows.Win32.Storage.FileSystem.FILE_FLAG_DELETE_ON_CLOSE -Windows.Win32.Storage.FileSystem.FILE_FLAG_FIRST_PIPE_INSTANCE -Windows.Win32.Storage.FileSystem.FILE_FLAG_NO_BUFFERING -Windows.Win32.Storage.FileSystem.FILE_FLAG_OPEN_NO_RECALL -Windows.Win32.Storage.FileSystem.FILE_FLAG_OPEN_REPARSE_POINT -Windows.Win32.Storage.FileSystem.FILE_FLAG_OVERLAPPED -Windows.Win32.Storage.FileSystem.FILE_FLAG_POSIX_SEMANTICS -Windows.Win32.Storage.FileSystem.FILE_FLAG_RANDOM_ACCESS -Windows.Win32.Storage.FileSystem.FILE_FLAG_SEQUENTIAL_SCAN -Windows.Win32.Storage.FileSystem.FILE_FLAG_SESSION_AWARE -Windows.Win32.Storage.FileSystem.FILE_FLAG_WRITE_THROUGH -Windows.Win32.Storage.FileSystem.FILE_FLAGS_AND_ATTRIBUTES -Windows.Win32.Storage.FileSystem.FILE_GENERIC_EXECUTE -Windows.Win32.Storage.FileSystem.FILE_GENERIC_READ -Windows.Win32.Storage.FileSystem.FILE_GENERIC_WRITE -Windows.Win32.Storage.FileSystem.FILE_ID_BOTH_DIR_INFO -Windows.Win32.Storage.FileSystem.FILE_INFO_BY_HANDLE_CLASS -Windows.Win32.Storage.FileSystem.FILE_IO_PRIORITY_HINT_INFO -Windows.Win32.Storage.FileSystem.FILE_LIST_DIRECTORY -Windows.Win32.Storage.FileSystem.FILE_NAME_NORMALIZED -Windows.Win32.Storage.FileSystem.FILE_NAME_OPENED -Windows.Win32.Storage.FileSystem.FILE_READ_ATTRIBUTES -Windows.Win32.Storage.FileSystem.FILE_READ_DATA -Windows.Win32.Storage.FileSystem.FILE_READ_EA -Windows.Win32.Storage.FileSystem.FILE_RENAME_INFO -Windows.Win32.Storage.FileSystem.FILE_SHARE_DELETE -Windows.Win32.Storage.FileSystem.FILE_SHARE_MODE -Windows.Win32.Storage.FileSystem.FILE_SHARE_NONE -Windows.Win32.Storage.FileSystem.FILE_SHARE_READ -Windows.Win32.Storage.FileSystem.FILE_SHARE_WRITE -Windows.Win32.Storage.FileSystem.FILE_STANDARD_INFO -Windows.Win32.Storage.FileSystem.FILE_TRAVERSE -Windows.Win32.Storage.FileSystem.FILE_TYPE -Windows.Win32.Storage.FileSystem.FILE_TYPE_CHAR -Windows.Win32.Storage.FileSystem.FILE_TYPE_DISK -Windows.Win32.Storage.FileSystem.FILE_TYPE_PIPE -Windows.Win32.Storage.FileSystem.FILE_TYPE_REMOTE -Windows.Win32.Storage.FileSystem.FILE_TYPE_UNKNOWN -Windows.Win32.Storage.FileSystem.FILE_WRITE_ATTRIBUTES -Windows.Win32.Storage.FileSystem.FILE_WRITE_DATA -Windows.Win32.Storage.FileSystem.FILE_WRITE_EA -Windows.Win32.Storage.FileSystem.FileAlignmentInfo -Windows.Win32.Storage.FileSystem.FileAllocationInfo -Windows.Win32.Storage.FileSystem.FileAttributeTagInfo -Windows.Win32.Storage.FileSystem.FileBasicInfo -Windows.Win32.Storage.FileSystem.FileCaseSensitiveInfo -Windows.Win32.Storage.FileSystem.FileCompressionInfo -Windows.Win32.Storage.FileSystem.FileDispositionInfo -Windows.Win32.Storage.FileSystem.FileDispositionInfoEx -Windows.Win32.Storage.FileSystem.FileEndOfFileInfo -Windows.Win32.Storage.FileSystem.FileFullDirectoryInfo -Windows.Win32.Storage.FileSystem.FileFullDirectoryRestartInfo -Windows.Win32.Storage.FileSystem.FileIdBothDirectoryInfo -Windows.Win32.Storage.FileSystem.FileIdBothDirectoryRestartInfo -Windows.Win32.Storage.FileSystem.FileIdExtdDirectoryInfo -Windows.Win32.Storage.FileSystem.FileIdExtdDirectoryRestartInfo -Windows.Win32.Storage.FileSystem.FileIdInfo -Windows.Win32.Storage.FileSystem.FileIoPriorityHintInfo -Windows.Win32.Storage.FileSystem.FileNameInfo -Windows.Win32.Storage.FileSystem.FileNormalizedNameInfo -Windows.Win32.Storage.FileSystem.FileRemoteProtocolInfo -Windows.Win32.Storage.FileSystem.FileRenameInfo -Windows.Win32.Storage.FileSystem.FileRenameInfoEx -Windows.Win32.Storage.FileSystem.FileStandardInfo -Windows.Win32.Storage.FileSystem.FileStorageInfo -Windows.Win32.Storage.FileSystem.FileStreamInfo -Windows.Win32.Storage.FileSystem.FindClose -Windows.Win32.Storage.FileSystem.FindExInfoBasic -Windows.Win32.Storage.FileSystem.FindExSearchNameMatch -Windows.Win32.Storage.FileSystem.FindFirstFileExW -Windows.Win32.Storage.FileSystem.FindNextFileW -Windows.Win32.Storage.FileSystem.FlushFileBuffers -Windows.Win32.Storage.FileSystem.GetFileAttributesW -Windows.Win32.Storage.FileSystem.GetFileInformationByHandle -Windows.Win32.Storage.FileSystem.GetFileInformationByHandleEx -Windows.Win32.Storage.FileSystem.GetFileType -Windows.Win32.Storage.FileSystem.GETFINALPATHNAMEBYHANDLE_FLAGS -Windows.Win32.Storage.FileSystem.GetFinalPathNameByHandleW -Windows.Win32.Storage.FileSystem.GetFullPathNameW -Windows.Win32.Storage.FileSystem.GetTempPathW -Windows.Win32.Storage.FileSystem.INVALID_FILE_ATTRIBUTES -Windows.Win32.Storage.FileSystem.LOCKFILE_EXCLUSIVE_LOCK -Windows.Win32.Storage.FileSystem.LOCKFILE_FAIL_IMMEDIATELY -Windows.Win32.Storage.FileSystem.LockFileEx -Windows.Win32.Storage.FileSystem.LPPROGRESS_ROUTINE -Windows.Win32.Storage.FileSystem.LPPROGRESS_ROUTINE_CALLBACK_REASON -Windows.Win32.Storage.FileSystem.MAXIMUM_REPARSE_DATA_BUFFER_SIZE -Windows.Win32.Storage.FileSystem.MaximumFileInfoByHandleClass -Windows.Win32.Storage.FileSystem.MOVE_FILE_FLAGS -Windows.Win32.Storage.FileSystem.MOVEFILE_COPY_ALLOWED -Windows.Win32.Storage.FileSystem.MOVEFILE_CREATE_HARDLINK -Windows.Win32.Storage.FileSystem.MOVEFILE_DELAY_UNTIL_REBOOT -Windows.Win32.Storage.FileSystem.MOVEFILE_FAIL_IF_NOT_TRACKABLE -Windows.Win32.Storage.FileSystem.MOVEFILE_REPLACE_EXISTING -Windows.Win32.Storage.FileSystem.MOVEFILE_WRITE_THROUGH -Windows.Win32.Storage.FileSystem.MoveFileExW -Windows.Win32.Storage.FileSystem.OPEN_ALWAYS -Windows.Win32.Storage.FileSystem.OPEN_EXISTING -Windows.Win32.Storage.FileSystem.PIPE_ACCESS_DUPLEX -Windows.Win32.Storage.FileSystem.PIPE_ACCESS_INBOUND -Windows.Win32.Storage.FileSystem.PIPE_ACCESS_OUTBOUND -Windows.Win32.Storage.FileSystem.READ_CONTROL -Windows.Win32.Storage.FileSystem.ReadFile -Windows.Win32.Storage.FileSystem.ReadFileEx -Windows.Win32.Storage.FileSystem.RemoveDirectoryW -Windows.Win32.Storage.FileSystem.SECURITY_ANONYMOUS -Windows.Win32.Storage.FileSystem.SECURITY_CONTEXT_TRACKING -Windows.Win32.Storage.FileSystem.SECURITY_DELEGATION -Windows.Win32.Storage.FileSystem.SECURITY_EFFECTIVE_ONLY -Windows.Win32.Storage.FileSystem.SECURITY_IDENTIFICATION -Windows.Win32.Storage.FileSystem.SECURITY_IMPERSONATION -Windows.Win32.Storage.FileSystem.SECURITY_SQOS_PRESENT -Windows.Win32.Storage.FileSystem.SECURITY_VALID_SQOS_FLAGS -Windows.Win32.Storage.FileSystem.SET_FILE_POINTER_MOVE_METHOD -Windows.Win32.Storage.FileSystem.SetFileAttributesW -Windows.Win32.Storage.FileSystem.SetFileInformationByHandle -Windows.Win32.Storage.FileSystem.SetFilePointerEx -Windows.Win32.Storage.FileSystem.SetFileTime -Windows.Win32.Storage.FileSystem.SPECIFIC_RIGHTS_ALL -Windows.Win32.Storage.FileSystem.STANDARD_RIGHTS_ALL -Windows.Win32.Storage.FileSystem.STANDARD_RIGHTS_EXECUTE -Windows.Win32.Storage.FileSystem.STANDARD_RIGHTS_READ -Windows.Win32.Storage.FileSystem.STANDARD_RIGHTS_REQUIRED -Windows.Win32.Storage.FileSystem.STANDARD_RIGHTS_WRITE -Windows.Win32.Storage.FileSystem.SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE -Windows.Win32.Storage.FileSystem.SYMBOLIC_LINK_FLAG_DIRECTORY -Windows.Win32.Storage.FileSystem.SYMBOLIC_LINK_FLAGS -Windows.Win32.Storage.FileSystem.SYNCHRONIZE -Windows.Win32.Storage.FileSystem.TRUNCATE_EXISTING -Windows.Win32.Storage.FileSystem.UnlockFile -Windows.Win32.Storage.FileSystem.VOLUME_NAME_DOS -Windows.Win32.Storage.FileSystem.VOLUME_NAME_GUID -Windows.Win32.Storage.FileSystem.VOLUME_NAME_NONE -Windows.Win32.Storage.FileSystem.WIN32_FIND_DATAW -Windows.Win32.Storage.FileSystem.WRITE_DAC -Windows.Win32.Storage.FileSystem.WRITE_OWNER -Windows.Win32.Storage.FileSystem.WriteFileEx -Windows.Win32.System.Console.CONSOLE_MODE -Windows.Win32.System.Console.CONSOLE_READCONSOLE_CONTROL -Windows.Win32.System.Console.DISABLE_NEWLINE_AUTO_RETURN -Windows.Win32.System.Console.ENABLE_AUTO_POSITION -Windows.Win32.System.Console.ENABLE_ECHO_INPUT -Windows.Win32.System.Console.ENABLE_EXTENDED_FLAGS -Windows.Win32.System.Console.ENABLE_INSERT_MODE -Windows.Win32.System.Console.ENABLE_LINE_INPUT -Windows.Win32.System.Console.ENABLE_LVB_GRID_WORLDWIDE -Windows.Win32.System.Console.ENABLE_MOUSE_INPUT -Windows.Win32.System.Console.ENABLE_PROCESSED_INPUT -Windows.Win32.System.Console.ENABLE_PROCESSED_OUTPUT -Windows.Win32.System.Console.ENABLE_QUICK_EDIT_MODE -Windows.Win32.System.Console.ENABLE_VIRTUAL_TERMINAL_INPUT -Windows.Win32.System.Console.ENABLE_VIRTUAL_TERMINAL_PROCESSING -Windows.Win32.System.Console.ENABLE_WINDOW_INPUT -Windows.Win32.System.Console.ENABLE_WRAP_AT_EOL_OUTPUT -Windows.Win32.System.Console.GetConsoleMode -Windows.Win32.System.Console.GetConsoleOutputCP -Windows.Win32.System.Console.GetStdHandle -Windows.Win32.System.Console.ReadConsoleW -Windows.Win32.System.Console.STD_ERROR_HANDLE -Windows.Win32.System.Console.STD_HANDLE -Windows.Win32.System.Console.STD_INPUT_HANDLE -Windows.Win32.System.Console.STD_OUTPUT_HANDLE -Windows.Win32.System.Console.WriteConsoleW -Windows.Win32.System.Diagnostics.Debug.AddVectoredExceptionHandler -Windows.Win32.System.Diagnostics.Debug.ARM64_NT_NEON128 -Windows.Win32.System.Diagnostics.Debug.CONTEXT -Windows.Win32.System.Diagnostics.Debug.EXCEPTION_RECORD -Windows.Win32.System.Diagnostics.Debug.FACILITY_CODE -Windows.Win32.System.Diagnostics.Debug.FACILITY_NT_BIT -Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_ALLOCATE_BUFFER -Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_ARGUMENT_ARRAY -Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_FROM_HMODULE -Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_FROM_STRING -Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_FROM_SYSTEM -Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_IGNORE_INSERTS -Windows.Win32.System.Diagnostics.Debug.FORMAT_MESSAGE_OPTIONS -Windows.Win32.System.Diagnostics.Debug.FormatMessageW -Windows.Win32.System.Diagnostics.Debug.M128A Windows.Win32.System.Diagnostics.Debug.XSAVE_FORMAT -Windows.Win32.System.Environment.FreeEnvironmentStringsW -Windows.Win32.System.Environment.GetCommandLineW -Windows.Win32.System.Environment.GetCurrentDirectoryW -Windows.Win32.System.Environment.GetEnvironmentStringsW -Windows.Win32.System.Environment.GetEnvironmentVariableW -Windows.Win32.System.Environment.SetCurrentDirectoryW -Windows.Win32.System.Environment.SetEnvironmentVariableW -Windows.Win32.System.IO.CancelIo -Windows.Win32.System.IO.DeviceIoControl -Windows.Win32.System.IO.GetOverlappedResult -Windows.Win32.System.IO.LPOVERLAPPED_COMPLETION_ROUTINE -Windows.Win32.System.IO.OVERLAPPED -Windows.Win32.System.Ioctl.FSCTL_GET_REPARSE_POINT -Windows.Win32.System.Ioctl.FSCTL_SET_REPARSE_POINT -Windows.Win32.System.Kernel.EXCEPTION_DISPOSITION -Windows.Win32.System.Kernel.ExceptionCollidedUnwind -Windows.Win32.System.Kernel.ExceptionContinueExecution -Windows.Win32.System.Kernel.ExceptionContinueSearch -Windows.Win32.System.Kernel.ExceptionNestedException Windows.Win32.System.Kernel.FLOATING_SAVE_AREA -Windows.Win32.System.Kernel.OBJ_DONT_REPARSE -Windows.Win32.System.LibraryLoader.GetModuleFileNameW -Windows.Win32.System.LibraryLoader.GetModuleHandleA -Windows.Win32.System.LibraryLoader.GetModuleHandleW -Windows.Win32.System.LibraryLoader.GetProcAddress -Windows.Win32.System.Performance.QueryPerformanceCounter -Windows.Win32.System.Performance.QueryPerformanceFrequency -Windows.Win32.System.Pipes.CreateNamedPipeW -Windows.Win32.System.Pipes.CreatePipe -Windows.Win32.System.Pipes.NAMED_PIPE_MODE -Windows.Win32.System.Pipes.PIPE_ACCEPT_REMOTE_CLIENTS -Windows.Win32.System.Pipes.PIPE_CLIENT_END -Windows.Win32.System.Pipes.PIPE_NOWAIT -Windows.Win32.System.Pipes.PIPE_READMODE_BYTE -Windows.Win32.System.Pipes.PIPE_READMODE_MESSAGE -Windows.Win32.System.Pipes.PIPE_REJECT_REMOTE_CLIENTS -Windows.Win32.System.Pipes.PIPE_SERVER_END -Windows.Win32.System.Pipes.PIPE_TYPE_BYTE -Windows.Win32.System.Pipes.PIPE_TYPE_MESSAGE -Windows.Win32.System.Pipes.PIPE_WAIT -Windows.Win32.System.SystemInformation.GetSystemDirectoryW -Windows.Win32.System.SystemInformation.GetSystemInfo -Windows.Win32.System.SystemInformation.GetSystemTimeAsFileTime -Windows.Win32.System.SystemInformation.GetSystemTimePreciseAsFileTime -Windows.Win32.System.SystemInformation.GetWindowsDirectoryW -Windows.Win32.System.SystemInformation.PROCESSOR_ARCHITECTURE -Windows.Win32.System.SystemInformation.SYSTEM_INFO -Windows.Win32.System.SystemServices.DLL_PROCESS_DETACH -Windows.Win32.System.SystemServices.DLL_THREAD_DETACH -Windows.Win32.System.SystemServices.EXCEPTION_MAXIMUM_PARAMETERS -Windows.Win32.System.SystemServices.FAST_FAIL_FATAL_APP_EXIT -Windows.Win32.System.SystemServices.IO_REPARSE_TAG_MOUNT_POINT -Windows.Win32.System.SystemServices.IO_REPARSE_TAG_SYMLINK -Windows.Win32.System.Threading.ABOVE_NORMAL_PRIORITY_CLASS -Windows.Win32.System.Threading.AcquireSRWLockExclusive -Windows.Win32.System.Threading.AcquireSRWLockShared -Windows.Win32.System.Threading.ALL_PROCESSOR_GROUPS -Windows.Win32.System.Threading.BELOW_NORMAL_PRIORITY_CLASS -Windows.Win32.System.Threading.CREATE_BREAKAWAY_FROM_JOB -Windows.Win32.System.Threading.CREATE_DEFAULT_ERROR_MODE -Windows.Win32.System.Threading.CREATE_FORCEDOS -Windows.Win32.System.Threading.CREATE_IGNORE_SYSTEM_DEFAULT -Windows.Win32.System.Threading.CREATE_NEW_CONSOLE -Windows.Win32.System.Threading.CREATE_NEW_PROCESS_GROUP -Windows.Win32.System.Threading.CREATE_NO_WINDOW -Windows.Win32.System.Threading.CREATE_PRESERVE_CODE_AUTHZ_LEVEL -Windows.Win32.System.Threading.CREATE_PROTECTED_PROCESS -Windows.Win32.System.Threading.CREATE_SECURE_PROCESS -Windows.Win32.System.Threading.CREATE_SEPARATE_WOW_VDM -Windows.Win32.System.Threading.CREATE_SHARED_WOW_VDM -Windows.Win32.System.Threading.CREATE_SUSPENDED -Windows.Win32.System.Threading.CREATE_UNICODE_ENVIRONMENT -Windows.Win32.System.Threading.CREATE_WAITABLE_TIMER_HIGH_RESOLUTION -Windows.Win32.System.Threading.CREATE_WAITABLE_TIMER_MANUAL_RESET -Windows.Win32.System.Threading.CreateEventW -Windows.Win32.System.Threading.CreateProcessW -Windows.Win32.System.Threading.CreateThread -Windows.Win32.System.Threading.CreateWaitableTimerExW -Windows.Win32.System.Threading.DEBUG_ONLY_THIS_PROCESS -Windows.Win32.System.Threading.DEBUG_PROCESS -Windows.Win32.System.Threading.DeleteProcThreadAttributeList -Windows.Win32.System.Threading.DETACHED_PROCESS -Windows.Win32.System.Threading.ExitProcess -Windows.Win32.System.Threading.EXTENDED_STARTUPINFO_PRESENT -Windows.Win32.System.Threading.GetActiveProcessorCount -Windows.Win32.System.Threading.GetCurrentProcess -Windows.Win32.System.Threading.GetCurrentProcessId -Windows.Win32.System.Threading.GetCurrentThread -Windows.Win32.System.Threading.GetExitCodeProcess -Windows.Win32.System.Threading.GetProcessId -Windows.Win32.System.Threading.HIGH_PRIORITY_CLASS -Windows.Win32.System.Threading.IDLE_PRIORITY_CLASS -Windows.Win32.System.Threading.INFINITE -Windows.Win32.System.Threading.INHERIT_CALLER_PRIORITY -Windows.Win32.System.Threading.INHERIT_PARENT_AFFINITY -Windows.Win32.System.Threading.INIT_ONCE_INIT_FAILED -Windows.Win32.System.Threading.InitializeProcThreadAttributeList -Windows.Win32.System.Threading.InitOnceBeginInitialize -Windows.Win32.System.Threading.InitOnceComplete -Windows.Win32.System.Threading.LPPROC_THREAD_ATTRIBUTE_LIST -Windows.Win32.System.Threading.LPTHREAD_START_ROUTINE -Windows.Win32.System.Threading.NORMAL_PRIORITY_CLASS -Windows.Win32.System.Threading.OpenProcessToken -Windows.Win32.System.Threading.PROCESS_CREATION_FLAGS -Windows.Win32.System.Threading.PROCESS_INFORMATION -Windows.Win32.System.Threading.PROCESS_MODE_BACKGROUND_BEGIN -Windows.Win32.System.Threading.PROCESS_MODE_BACKGROUND_END -Windows.Win32.System.Threading.PROFILE_KERNEL -Windows.Win32.System.Threading.PROFILE_SERVER -Windows.Win32.System.Threading.PROFILE_USER -Windows.Win32.System.Threading.REALTIME_PRIORITY_CLASS -Windows.Win32.System.Threading.ReleaseSRWLockExclusive -Windows.Win32.System.Threading.ReleaseSRWLockShared -Windows.Win32.System.Threading.SetThreadStackGuarantee -Windows.Win32.System.Threading.SetWaitableTimer -Windows.Win32.System.Threading.Sleep -Windows.Win32.System.Threading.SleepConditionVariableSRW -Windows.Win32.System.Threading.SleepEx -Windows.Win32.System.Threading.STACK_SIZE_PARAM_IS_A_RESERVATION -Windows.Win32.System.Threading.STARTF_FORCEOFFFEEDBACK -Windows.Win32.System.Threading.STARTF_FORCEONFEEDBACK -Windows.Win32.System.Threading.STARTF_PREVENTPINNING -Windows.Win32.System.Threading.STARTF_RUNFULLSCREEN -Windows.Win32.System.Threading.STARTF_TITLEISAPPID -Windows.Win32.System.Threading.STARTF_TITLEISLINKNAME -Windows.Win32.System.Threading.STARTF_UNTRUSTEDSOURCE -Windows.Win32.System.Threading.STARTF_USECOUNTCHARS -Windows.Win32.System.Threading.STARTF_USEFILLATTRIBUTE -Windows.Win32.System.Threading.STARTF_USEHOTKEY -Windows.Win32.System.Threading.STARTF_USEPOSITION -Windows.Win32.System.Threading.STARTF_USESHOWWINDOW -Windows.Win32.System.Threading.STARTF_USESIZE -Windows.Win32.System.Threading.STARTF_USESTDHANDLES -Windows.Win32.System.Threading.STARTUPINFOEXW -Windows.Win32.System.Threading.STARTUPINFOW -Windows.Win32.System.Threading.STARTUPINFOW_FLAGS -Windows.Win32.System.Threading.SwitchToThread -Windows.Win32.System.Threading.TerminateProcess -Windows.Win32.System.Threading.THREAD_CREATE_RUN_IMMEDIATELY -Windows.Win32.System.Threading.THREAD_CREATE_SUSPENDED -Windows.Win32.System.Threading.THREAD_CREATION_FLAGS -Windows.Win32.System.Threading.TIMER_ALL_ACCESS -Windows.Win32.System.Threading.TIMER_MODIFY_STATE -Windows.Win32.System.Threading.TLS_OUT_OF_INDEXES -Windows.Win32.System.Threading.TlsAlloc -Windows.Win32.System.Threading.TlsFree -Windows.Win32.System.Threading.TlsGetValue -Windows.Win32.System.Threading.TlsSetValue -Windows.Win32.System.Threading.TryAcquireSRWLockExclusive -Windows.Win32.System.Threading.TryAcquireSRWLockShared -Windows.Win32.System.Threading.UpdateProcThreadAttribute -Windows.Win32.System.Threading.WaitForMultipleObjects -Windows.Win32.System.Threading.WaitForSingleObject -Windows.Win32.System.Threading.WakeAllConditionVariable -Windows.Win32.System.Threading.WakeConditionVariable -Windows.Win32.System.WindowsProgramming.FILE_RENAME_FLAG_POSIX_SEMANTICS -Windows.Win32.System.WindowsProgramming.FILE_RENAME_FLAG_REPLACE_IF_EXISTS -Windows.Win32.System.WindowsProgramming.PROGRESS_CONTINUE -Windows.Win32.UI.Shell.GetUserProfileDirectoryW +WINSOCK_SHUTDOWN_HOW +WINSOCK_SOCKET_TYPE +WRITE_DAC +WRITE_OWNER +WriteConsoleW +WriteFileEx +WSA_E_CANCELLED +WSA_E_NO_MORE +WSA_ERROR +WSA_FLAG_NO_HANDLE_INHERIT +WSA_FLAG_OVERLAPPED +WSA_INVALID_HANDLE +WSA_INVALID_PARAMETER +WSA_IO_INCOMPLETE +WSA_IO_PENDING +WSA_IPSEC_NAME_POLICY_ERROR +WSA_NOT_ENOUGH_MEMORY +WSA_OPERATION_ABORTED +WSA_QOS_ADMISSION_FAILURE +WSA_QOS_BAD_OBJECT +WSA_QOS_BAD_STYLE +WSA_QOS_EFILTERCOUNT +WSA_QOS_EFILTERSTYLE +WSA_QOS_EFILTERTYPE +WSA_QOS_EFLOWCOUNT +WSA_QOS_EFLOWDESC +WSA_QOS_EFLOWSPEC +WSA_QOS_EOBJLENGTH +WSA_QOS_EPOLICYOBJ +WSA_QOS_EPROVSPECBUF +WSA_QOS_EPSFILTERSPEC +WSA_QOS_EPSFLOWSPEC +WSA_QOS_ESDMODEOBJ +WSA_QOS_ESERVICETYPE +WSA_QOS_ESHAPERATEOBJ +WSA_QOS_EUNKOWNPSOBJ +WSA_QOS_GENERIC_ERROR +WSA_QOS_NO_RECEIVERS +WSA_QOS_NO_SENDERS +WSA_QOS_POLICY_FAILURE +WSA_QOS_RECEIVERS +WSA_QOS_REQUEST_CONFIRMED +WSA_QOS_RESERVED_PETYPE +WSA_QOS_SENDERS +WSA_QOS_TRAFFIC_CTRL_ERROR +WSA_SECURE_HOST_NOT_FOUND +WSA_WAIT_EVENT_0 +WSA_WAIT_IO_COMPLETION +WSABASEERR +WSABUF +WSACleanup +WSADATA +WSADuplicateSocketW +WSAEACCES +WSAEADDRINUSE +WSAEADDRNOTAVAIL +WSAEAFNOSUPPORT +WSAEALREADY +WSAEBADF +WSAECANCELLED +WSAECONNABORTED +WSAECONNREFUSED +WSAECONNRESET +WSAEDESTADDRREQ +WSAEDISCON +WSAEDQUOT +WSAEFAULT +WSAEHOSTDOWN +WSAEHOSTUNREACH +WSAEINPROGRESS +WSAEINTR +WSAEINVAL +WSAEINVALIDPROCTABLE +WSAEINVALIDPROVIDER +WSAEISCONN +WSAELOOP +WSAEMFILE +WSAEMSGSIZE +WSAENAMETOOLONG +WSAENETDOWN +WSAENETRESET +WSAENETUNREACH +WSAENOBUFS +WSAENOMORE +WSAENOPROTOOPT +WSAENOTCONN +WSAENOTEMPTY +WSAENOTSOCK +WSAEOPNOTSUPP +WSAEPFNOSUPPORT +WSAEPROCLIM +WSAEPROTONOSUPPORT +WSAEPROTOTYPE +WSAEPROVIDERFAILEDINIT +WSAEREFUSED +WSAEREMOTE +WSAESHUTDOWN +WSAESOCKTNOSUPPORT +WSAESTALE +WSAETIMEDOUT +WSAETOOMANYREFS +WSAEUSERS +WSAEWOULDBLOCK +WSAGetLastError +WSAHOST_NOT_FOUND +WSANO_DATA +WSANO_RECOVERY +WSANOTINITIALISED +WSAPROTOCOL_INFOW +WSAPROTOCOLCHAIN +WSARecv +WSASend +WSASERVICE_NOT_FOUND +WSASocketW +WSAStartup +WSASYSCALLFAILURE +WSASYSNOTREADY +WSATRY_AGAIN +WSATYPE_NOT_FOUND +WSAVERNOTSUPPORTED diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index 79513d33a1ac7..1d0e89f5d0f0e 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -1,15 +1,14 @@ -// Bindings generated by `windows-bindgen` 0.58.0 +// Bindings generated by `windows-bindgen` 0.59.0 #![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)] -windows_targets::link!("advapi32.dll" "system" fn OpenProcessToken(processhandle : HANDLE, desiredaccess : TOKEN_ACCESS_MASK, tokenhandle : *mut HANDLE) -> BOOL); -windows_targets::link!("advapi32.dll" "system" "SystemFunction036" fn RtlGenRandom(randombuffer : *mut core::ffi::c_void, randombufferlength : u32) -> BOOLEAN); + windows_targets::link!("kernel32.dll" "system" fn AcquireSRWLockExclusive(srwlock : *mut SRWLOCK)); windows_targets::link!("kernel32.dll" "system" fn AcquireSRWLockShared(srwlock : *mut SRWLOCK)); windows_targets::link!("kernel32.dll" "system" fn AddVectoredExceptionHandler(first : u32, handler : PVECTORED_EXCEPTION_HANDLER) -> *mut core::ffi::c_void); windows_targets::link!("kernel32.dll" "system" fn CancelIo(hfile : HANDLE) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn CloseHandle(hobject : HANDLE) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn CompareStringOrdinal(lpstring1 : PCWSTR, cchcount1 : i32, lpstring2 : PCWSTR, cchcount2 : i32, bignorecase : BOOL) -> COMPARESTRING_RESULT); -windows_targets::link!("kernel32.dll" "system" fn CopyFileExW(lpexistingfilename : PCWSTR, lpnewfilename : PCWSTR, lpprogressroutine : LPPROGRESS_ROUTINE, lpdata : *const core::ffi::c_void, pbcancel : *mut BOOL, dwcopyflags : u32) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn CopyFileExW(lpexistingfilename : PCWSTR, lpnewfilename : PCWSTR, lpprogressroutine : LPPROGRESS_ROUTINE, lpdata : *const core::ffi::c_void, pbcancel : *mut BOOL, dwcopyflags : COPYFILE_FLAGS) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn CreateDirectoryW(lppathname : PCWSTR, lpsecurityattributes : *const SECURITY_ATTRIBUTES) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn CreateEventW(lpeventattributes : *const SECURITY_ATTRIBUTES, bmanualreset : BOOL, binitialstate : BOOL, lpname : PCWSTR) -> HANDLE); windows_targets::link!("kernel32.dll" "system" fn CreateFileW(lpfilename : PCWSTR, dwdesiredaccess : u32, dwsharemode : FILE_SHARE_MODE, lpsecurityattributes : *const SECURITY_ATTRIBUTES, dwcreationdisposition : FILE_CREATION_DISPOSITION, dwflagsandattributes : FILE_FLAGS_AND_ATTRIBUTES, htemplatefile : HANDLE) -> HANDLE); @@ -17,7 +16,7 @@ windows_targets::link!("kernel32.dll" "system" fn CreateHardLinkW(lpfilename : P windows_targets::link!("kernel32.dll" "system" fn CreateNamedPipeW(lpname : PCWSTR, dwopenmode : FILE_FLAGS_AND_ATTRIBUTES, dwpipemode : NAMED_PIPE_MODE, nmaxinstances : u32, noutbuffersize : u32, ninbuffersize : u32, ndefaulttimeout : u32, lpsecurityattributes : *const SECURITY_ATTRIBUTES) -> HANDLE); windows_targets::link!("kernel32.dll" "system" fn CreatePipe(hreadpipe : *mut HANDLE, hwritepipe : *mut HANDLE, lppipeattributes : *const SECURITY_ATTRIBUTES, nsize : u32) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn CreateProcessW(lpapplicationname : PCWSTR, lpcommandline : PWSTR, lpprocessattributes : *const SECURITY_ATTRIBUTES, lpthreadattributes : *const SECURITY_ATTRIBUTES, binherithandles : BOOL, dwcreationflags : PROCESS_CREATION_FLAGS, lpenvironment : *const core::ffi::c_void, lpcurrentdirectory : PCWSTR, lpstartupinfo : *const STARTUPINFOW, lpprocessinformation : *mut PROCESS_INFORMATION) -> BOOL); -windows_targets::link!("kernel32.dll" "system" fn CreateSymbolicLinkW(lpsymlinkfilename : PCWSTR, lptargetfilename : PCWSTR, dwflags : SYMBOLIC_LINK_FLAGS) -> BOOLEAN); +windows_targets::link!("kernel32.dll" "system" fn CreateSymbolicLinkW(lpsymlinkfilename : PCWSTR, lptargetfilename : PCWSTR, dwflags : SYMBOLIC_LINK_FLAGS) -> bool); windows_targets::link!("kernel32.dll" "system" fn CreateThread(lpthreadattributes : *const SECURITY_ATTRIBUTES, dwstacksize : usize, lpstartaddress : LPTHREAD_START_ROUTINE, lpparameter : *const core::ffi::c_void, dwcreationflags : THREAD_CREATION_FLAGS, lpthreadid : *mut u32) -> HANDLE); windows_targets::link!("kernel32.dll" "system" fn CreateWaitableTimerExW(lptimerattributes : *const SECURITY_ATTRIBUTES, lptimername : PCWSTR, dwflags : u32, dwdesiredaccess : u32) -> HANDLE); windows_targets::link!("kernel32.dll" "system" fn DeleteFileW(lpfilename : PCWSTR) -> BOOL); @@ -61,6 +60,7 @@ windows_targets::link!("kernel32.dll" "system" fn GetSystemInfo(lpsysteminfo : * windows_targets::link!("kernel32.dll" "system" fn GetSystemTimeAsFileTime(lpsystemtimeasfiletime : *mut FILETIME)); windows_targets::link!("kernel32.dll" "system" fn GetSystemTimePreciseAsFileTime(lpsystemtimeasfiletime : *mut FILETIME)); windows_targets::link!("kernel32.dll" "system" fn GetTempPathW(nbufferlength : u32, lpbuffer : PWSTR) -> u32); +windows_targets::link!("userenv.dll" "system" fn GetUserProfileDirectoryW(htoken : HANDLE, lpprofiledir : PWSTR, lpcchsize : *mut u32) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn GetWindowsDirectoryW(lpbuffer : PWSTR, usize : u32) -> u32); windows_targets::link!("kernel32.dll" "system" fn InitOnceBeginInitialize(lpinitonce : *mut INIT_ONCE, dwflags : u32, fpending : *mut BOOL, lpcontext : *mut *mut core::ffi::c_void) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn InitOnceComplete(lpinitonce : *mut INIT_ONCE, dwflags : u32, lpcontext : *const core::ffi::c_void) -> BOOL); @@ -69,6 +69,11 @@ windows_targets::link!("kernel32.dll" "system" fn LocalFree(hmem : HLOCAL) -> HL windows_targets::link!("kernel32.dll" "system" fn LockFileEx(hfile : HANDLE, dwflags : LOCK_FILE_FLAGS, dwreserved : u32, nnumberofbytestolocklow : u32, nnumberofbytestolockhigh : u32, lpoverlapped : *mut OVERLAPPED) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn MoveFileExW(lpexistingfilename : PCWSTR, lpnewfilename : PCWSTR, dwflags : MOVE_FILE_FLAGS) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn MultiByteToWideChar(codepage : u32, dwflags : MULTI_BYTE_TO_WIDE_CHAR_FLAGS, lpmultibytestr : PCSTR, cbmultibyte : i32, lpwidecharstr : PWSTR, cchwidechar : i32) -> i32); +windows_targets::link!("ntdll.dll" "system" fn NtCreateFile(filehandle : *mut HANDLE, desiredaccess : FILE_ACCESS_RIGHTS, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, allocationsize : *const i64, fileattributes : FILE_FLAGS_AND_ATTRIBUTES, shareaccess : FILE_SHARE_MODE, createdisposition : NTCREATEFILE_CREATE_DISPOSITION, createoptions : NTCREATEFILE_CREATE_OPTIONS, eabuffer : *const core::ffi::c_void, ealength : u32) -> NTSTATUS); +windows_targets::link!("ntdll.dll" "system" fn NtOpenFile(filehandle : *mut HANDLE, desiredaccess : u32, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, shareaccess : u32, openoptions : u32) -> NTSTATUS); +windows_targets::link!("ntdll.dll" "system" fn NtReadFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *mut core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS); +windows_targets::link!("ntdll.dll" "system" fn NtWriteFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *const core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS); +windows_targets::link!("advapi32.dll" "system" fn OpenProcessToken(processhandle : HANDLE, desiredaccess : TOKEN_ACCESS_MASK, tokenhandle : *mut HANDLE) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn QueryPerformanceCounter(lpperformancecount : *mut i64) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn QueryPerformanceFrequency(lpfrequency : *mut i64) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn ReadConsoleW(hconsoleinput : HANDLE, lpbuffer : *mut core::ffi::c_void, nnumberofcharstoread : u32, lpnumberofcharsread : *mut u32, pinputcontrol : *const CONSOLE_READCONSOLE_CONTROL) -> BOOL); @@ -77,6 +82,8 @@ windows_targets::link!("kernel32.dll" "system" fn ReadFileEx(hfile : HANDLE, lpb windows_targets::link!("kernel32.dll" "system" fn ReleaseSRWLockExclusive(srwlock : *mut SRWLOCK)); windows_targets::link!("kernel32.dll" "system" fn ReleaseSRWLockShared(srwlock : *mut SRWLOCK)); windows_targets::link!("kernel32.dll" "system" fn RemoveDirectoryW(lppathname : PCWSTR) -> BOOL); +windows_targets::link!("advapi32.dll" "system" "SystemFunction036" fn RtlGenRandom(randombuffer : *mut core::ffi::c_void, randombufferlength : u32) -> bool); +windows_targets::link!("ntdll.dll" "system" fn RtlNtStatusToDosError(status : NTSTATUS) -> u32); windows_targets::link!("kernel32.dll" "system" fn SetCurrentDirectoryW(lppathname : PCWSTR) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn SetEnvironmentVariableW(lpname : PCWSTR, lpvalue : PCWSTR) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn SetFileAttributesW(lpfilename : PCWSTR, dwfileattributes : FILE_FLAGS_AND_ATTRIBUTES) -> BOOL); @@ -96,23 +103,10 @@ windows_targets::link!("kernel32.dll" "system" fn TlsAlloc() -> u32); windows_targets::link!("kernel32.dll" "system" fn TlsFree(dwtlsindex : u32) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn TlsGetValue(dwtlsindex : u32) -> *mut core::ffi::c_void); windows_targets::link!("kernel32.dll" "system" fn TlsSetValue(dwtlsindex : u32, lptlsvalue : *const core::ffi::c_void) -> BOOL); -windows_targets::link!("kernel32.dll" "system" fn TryAcquireSRWLockExclusive(srwlock : *mut SRWLOCK) -> BOOLEAN); -windows_targets::link!("kernel32.dll" "system" fn TryAcquireSRWLockShared(srwlock : *mut SRWLOCK) -> BOOLEAN); +windows_targets::link!("kernel32.dll" "system" fn TryAcquireSRWLockExclusive(srwlock : *mut SRWLOCK) -> bool); +windows_targets::link!("kernel32.dll" "system" fn TryAcquireSRWLockShared(srwlock : *mut SRWLOCK) -> bool); windows_targets::link!("kernel32.dll" "system" fn UnlockFile(hfile : HANDLE, dwfileoffsetlow : u32, dwfileoffsethigh : u32, nnumberofbytestounlocklow : u32, nnumberofbytestounlockhigh : u32) -> BOOL); windows_targets::link!("kernel32.dll" "system" fn UpdateProcThreadAttribute(lpattributelist : LPPROC_THREAD_ATTRIBUTE_LIST, dwflags : u32, attribute : usize, lpvalue : *const core::ffi::c_void, cbsize : usize, lppreviousvalue : *mut core::ffi::c_void, lpreturnsize : *const usize) -> BOOL); -windows_targets::link!("kernel32.dll" "system" fn WaitForMultipleObjects(ncount : u32, lphandles : *const HANDLE, bwaitall : BOOL, dwmilliseconds : u32) -> WAIT_EVENT); -windows_targets::link!("kernel32.dll" "system" fn WaitForSingleObject(hhandle : HANDLE, dwmilliseconds : u32) -> WAIT_EVENT); -windows_targets::link!("kernel32.dll" "system" fn WakeAllConditionVariable(conditionvariable : *mut CONDITION_VARIABLE)); -windows_targets::link!("kernel32.dll" "system" fn WakeConditionVariable(conditionvariable : *mut CONDITION_VARIABLE)); -windows_targets::link!("kernel32.dll" "system" fn WideCharToMultiByte(codepage : u32, dwflags : u32, lpwidecharstr : PCWSTR, cchwidechar : i32, lpmultibytestr : PSTR, cbmultibyte : i32, lpdefaultchar : PCSTR, lpuseddefaultchar : *mut BOOL) -> i32); -windows_targets::link!("kernel32.dll" "system" fn WriteConsoleW(hconsoleoutput : HANDLE, lpbuffer : PCWSTR, nnumberofcharstowrite : u32, lpnumberofcharswritten : *mut u32, lpreserved : *const core::ffi::c_void) -> BOOL); -windows_targets::link!("kernel32.dll" "system" fn WriteFileEx(hfile : HANDLE, lpbuffer : *const u8, nnumberofbytestowrite : u32, lpoverlapped : *mut OVERLAPPED, lpcompletionroutine : LPOVERLAPPED_COMPLETION_ROUTINE) -> BOOL); -windows_targets::link!("ntdll.dll" "system" fn NtCreateFile(filehandle : *mut HANDLE, desiredaccess : FILE_ACCESS_RIGHTS, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, allocationsize : *const i64, fileattributes : FILE_FLAGS_AND_ATTRIBUTES, shareaccess : FILE_SHARE_MODE, createdisposition : NTCREATEFILE_CREATE_DISPOSITION, createoptions : NTCREATEFILE_CREATE_OPTIONS, eabuffer : *const core::ffi::c_void, ealength : u32) -> NTSTATUS); -windows_targets::link!("ntdll.dll" "system" fn NtOpenFile(filehandle : *mut HANDLE, desiredaccess : u32, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, shareaccess : u32, openoptions : u32) -> NTSTATUS); -windows_targets::link!("ntdll.dll" "system" fn NtReadFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *mut core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS); -windows_targets::link!("ntdll.dll" "system" fn NtWriteFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *const core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS); -windows_targets::link!("ntdll.dll" "system" fn RtlNtStatusToDosError(status : NTSTATUS) -> u32); -windows_targets::link!("userenv.dll" "system" fn GetUserProfileDirectoryW(htoken : HANDLE, lpprofiledir : PWSTR, lpcchsize : *mut u32) -> BOOL); windows_targets::link!("ws2_32.dll" "system" fn WSACleanup() -> i32); windows_targets::link!("ws2_32.dll" "system" fn WSADuplicateSocketW(s : SOCKET, dwprocessid : u32, lpprotocolinfo : *mut WSAPROTOCOL_INFOW) -> i32); windows_targets::link!("ws2_32.dll" "system" fn WSAGetLastError() -> WSA_ERROR); @@ -120,6 +114,13 @@ windows_targets::link!("ws2_32.dll" "system" fn WSARecv(s : SOCKET, lpbuffers : windows_targets::link!("ws2_32.dll" "system" fn WSASend(s : SOCKET, lpbuffers : *const WSABUF, dwbuffercount : u32, lpnumberofbytessent : *mut u32, dwflags : u32, lpoverlapped : *mut OVERLAPPED, lpcompletionroutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE) -> i32); windows_targets::link!("ws2_32.dll" "system" fn WSASocketW(af : i32, r#type : i32, protocol : i32, lpprotocolinfo : *const WSAPROTOCOL_INFOW, g : u32, dwflags : u32) -> SOCKET); windows_targets::link!("ws2_32.dll" "system" fn WSAStartup(wversionrequested : u16, lpwsadata : *mut WSADATA) -> i32); +windows_targets::link!("kernel32.dll" "system" fn WaitForMultipleObjects(ncount : u32, lphandles : *const HANDLE, bwaitall : BOOL, dwmilliseconds : u32) -> WAIT_EVENT); +windows_targets::link!("kernel32.dll" "system" fn WaitForSingleObject(hhandle : HANDLE, dwmilliseconds : u32) -> WAIT_EVENT); +windows_targets::link!("kernel32.dll" "system" fn WakeAllConditionVariable(conditionvariable : *mut CONDITION_VARIABLE)); +windows_targets::link!("kernel32.dll" "system" fn WakeConditionVariable(conditionvariable : *mut CONDITION_VARIABLE)); +windows_targets::link!("kernel32.dll" "system" fn WideCharToMultiByte(codepage : u32, dwflags : u32, lpwidecharstr : PCWSTR, cchwidechar : i32, lpmultibytestr : PSTR, cbmultibyte : i32, lpdefaultchar : PCSTR, lpuseddefaultchar : *mut BOOL) -> i32); +windows_targets::link!("kernel32.dll" "system" fn WriteConsoleW(hconsoleoutput : HANDLE, lpbuffer : PCWSTR, nnumberofcharstowrite : u32, lpnumberofcharswritten : *mut u32, lpreserved : *const core::ffi::c_void) -> BOOL); +windows_targets::link!("kernel32.dll" "system" fn WriteFileEx(hfile : HANDLE, lpbuffer : *const u8, nnumberofbytestowrite : u32, lpoverlapped : *mut OVERLAPPED, lpcompletionroutine : LPOVERLAPPED_COMPLETION_ROUTINE) -> BOOL); windows_targets::link!("ws2_32.dll" "system" fn accept(s : SOCKET, addr : *mut SOCKADDR, addrlen : *mut i32) -> SOCKET); windows_targets::link!("ws2_32.dll" "system" fn bind(s : SOCKET, name : *const SOCKADDR, namelen : i32) -> i32); windows_targets::link!("ws2_32.dll" "system" fn closesocket(s : SOCKET) -> i32); @@ -139,6 +140,15 @@ windows_targets::link!("ws2_32.dll" "system" fn sendto(s : SOCKET, buf : PCSTR, windows_targets::link!("ws2_32.dll" "system" fn setsockopt(s : SOCKET, level : i32, optname : i32, optval : PCSTR, optlen : i32) -> i32); windows_targets::link!("ws2_32.dll" "system" fn shutdown(s : SOCKET, how : WINSOCK_SHUTDOWN_HOW) -> i32); pub const ABOVE_NORMAL_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 32768u32; +#[repr(C)] +#[derive(Clone, Copy)] +pub struct ACL { + pub AclRevision: u8, + pub Sbz1: u8, + pub AclSize: u16, + pub AceCount: u16, + pub Sbz2: u16, +} pub type ADDRESS_FAMILY = u16; #[repr(C)] #[derive(Clone, Copy)] @@ -174,7 +184,6 @@ pub struct ARM64_NT_NEON128_0 { } pub const BELOW_NORMAL_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 16384u32; pub type BOOL = i32; -pub type BOOLEAN = u8; #[repr(C)] #[derive(Clone, Copy)] pub struct BY_HANDLE_FILE_INFORMATION { @@ -207,64 +216,34 @@ pub struct CONSOLE_READCONSOLE_CONTROL { pub dwControlKeyState: u32, } #[repr(C)] -#[cfg(target_arch = "aarch64")] +#[cfg(target_arch = "x86")] #[derive(Clone, Copy)] pub struct CONTEXT { pub ContextFlags: CONTEXT_FLAGS, - pub Cpsr: u32, - pub Anonymous: CONTEXT_0, - pub Sp: u64, - pub Pc: u64, - pub V: [ARM64_NT_NEON128; 32], - pub Fpcr: u32, - pub Fpsr: u32, - pub Bcr: [u32; 8], - pub Bvr: [u64; 8], - pub Wcr: [u32; 2], - pub Wvr: [u64; 2], -} -#[repr(C)] -#[cfg(target_arch = "aarch64")] -#[derive(Clone, Copy)] -pub union CONTEXT_0 { - pub Anonymous: CONTEXT_0_0, - pub X: [u64; 31], -} -#[repr(C)] -#[cfg(target_arch = "aarch64")] -#[derive(Clone, Copy)] -pub struct CONTEXT_0_0 { - pub X0: u64, - pub X1: u64, - pub X2: u64, - pub X3: u64, - pub X4: u64, - pub X5: u64, - pub X6: u64, - pub X7: u64, - pub X8: u64, - pub X9: u64, - pub X10: u64, - pub X11: u64, - pub X12: u64, - pub X13: u64, - pub X14: u64, - pub X15: u64, - pub X16: u64, - pub X17: u64, - pub X18: u64, - pub X19: u64, - pub X20: u64, - pub X21: u64, - pub X22: u64, - pub X23: u64, - pub X24: u64, - pub X25: u64, - pub X26: u64, - pub X27: u64, - pub X28: u64, - pub Fp: u64, - pub Lr: u64, + pub Dr0: u32, + pub Dr1: u32, + pub Dr2: u32, + pub Dr3: u32, + pub Dr6: u32, + pub Dr7: u32, + pub FloatSave: FLOATING_SAVE_AREA, + pub SegGs: u32, + pub SegFs: u32, + pub SegEs: u32, + pub SegDs: u32, + pub Edi: u32, + pub Esi: u32, + pub Ebx: u32, + pub Edx: u32, + pub Ecx: u32, + pub Eax: u32, + pub Ebp: u32, + pub Eip: u32, + pub SegCs: u32, + pub EFlags: u32, + pub Esp: u32, + pub SegSs: u32, + pub ExtendedRegisters: [u8; 512], } #[repr(C)] #[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))] @@ -348,36 +327,68 @@ pub struct CONTEXT_0_0 { pub Xmm15: M128A, } #[repr(C)] -#[cfg(target_arch = "x86")] +#[cfg(target_arch = "aarch64")] #[derive(Clone, Copy)] pub struct CONTEXT { pub ContextFlags: CONTEXT_FLAGS, - pub Dr0: u32, - pub Dr1: u32, - pub Dr2: u32, - pub Dr3: u32, - pub Dr6: u32, - pub Dr7: u32, - pub FloatSave: FLOATING_SAVE_AREA, - pub SegGs: u32, - pub SegFs: u32, - pub SegEs: u32, - pub SegDs: u32, - pub Edi: u32, - pub Esi: u32, - pub Ebx: u32, - pub Edx: u32, - pub Ecx: u32, - pub Eax: u32, - pub Ebp: u32, - pub Eip: u32, - pub SegCs: u32, - pub EFlags: u32, - pub Esp: u32, - pub SegSs: u32, - pub ExtendedRegisters: [u8; 512], + pub Cpsr: u32, + pub Anonymous: CONTEXT_0, + pub Sp: u64, + pub Pc: u64, + pub V: [ARM64_NT_NEON128; 32], + pub Fpcr: u32, + pub Fpsr: u32, + pub Bcr: [u32; 8], + pub Bvr: [u64; 8], + pub Wcr: [u32; 2], + pub Wvr: [u64; 2], +} +#[repr(C)] +#[cfg(target_arch = "aarch64")] +#[derive(Clone, Copy)] +pub union CONTEXT_0 { + pub Anonymous: CONTEXT_0_0, + pub X: [u64; 31], +} +#[repr(C)] +#[cfg(target_arch = "aarch64")] +#[derive(Clone, Copy)] +pub struct CONTEXT_0_0 { + pub X0: u64, + pub X1: u64, + pub X2: u64, + pub X3: u64, + pub X4: u64, + pub X5: u64, + pub X6: u64, + pub X7: u64, + pub X8: u64, + pub X9: u64, + pub X10: u64, + pub X11: u64, + pub X12: u64, + pub X13: u64, + pub X14: u64, + pub X15: u64, + pub X16: u64, + pub X17: u64, + pub X18: u64, + pub X19: u64, + pub X20: u64, + pub X21: u64, + pub X22: u64, + pub X23: u64, + pub X24: u64, + pub X25: u64, + pub X26: u64, + pub X27: u64, + pub X28: u64, + pub Fp: u64, + pub Lr: u64, } pub type CONTEXT_FLAGS = u32; +pub type COPYFILE_FLAGS = u32; +pub type COPYPROGRESSROUTINE_PROGRESS = u32; pub const CP_UTF8: u32 = 65001u32; pub const CREATE_ALWAYS: FILE_CREATION_DISPOSITION = 2u32; pub const CREATE_BREAKAWAY_FROM_JOB: PROCESS_CREATION_FLAGS = 16777216u32; @@ -2396,7 +2407,7 @@ pub const FILE_DISPOSITION_FLAG_POSIX_SEMANTICS: FILE_DISPOSITION_INFO_EX_FLAGS #[repr(C)] #[derive(Clone, Copy)] pub struct FILE_DISPOSITION_INFO { - pub DeleteFile: BOOLEAN, + pub DeleteFile: bool, } #[repr(C)] #[derive(Clone, Copy)] @@ -2486,7 +2497,7 @@ pub struct FILE_RENAME_INFO { #[repr(C)] #[derive(Clone, Copy)] pub union FILE_RENAME_INFO_0 { - pub ReplaceIfExists: BOOLEAN, + pub ReplaceIfExists: bool, pub Flags: u32, } pub const FILE_RESERVE_OPFILTER: NTCREATEFILE_CREATE_OPTIONS = 1048576u32; @@ -2503,8 +2514,8 @@ pub struct FILE_STANDARD_INFO { pub AllocationSize: i64, pub EndOfFile: i64, pub NumberOfLinks: u32, - pub DeletePending: BOOLEAN, - pub Directory: BOOLEAN, + pub DeletePending: bool, + pub Directory: bool, } pub const FILE_SUPERSEDE: NTCREATEFILE_CREATE_DISPOSITION = 0u32; pub const FILE_SYNCHRONOUS_IO_ALERT: NTCREATEFILE_CREATE_OPTIONS = 16u32; @@ -2525,7 +2536,7 @@ pub type FINDEX_SEARCH_OPS = i32; pub type FIND_FIRST_EX_FLAGS = u32; pub const FIONBIO: i32 = -2147195266i32; #[repr(C)] -#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] +#[cfg(target_arch = "x86")] #[derive(Clone, Copy)] pub struct FLOATING_SAVE_AREA { pub ControlWord: u32, @@ -2536,10 +2547,10 @@ pub struct FLOATING_SAVE_AREA { pub DataOffset: u32, pub DataSelector: u32, pub RegisterArea: [u8; 80], - pub Cr0NpxState: u32, + pub Spare0: u32, } #[repr(C)] -#[cfg(target_arch = "x86")] +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] #[derive(Clone, Copy)] pub struct FLOATING_SAVE_AREA { pub ControlWord: u32, @@ -2550,7 +2561,7 @@ pub struct FLOATING_SAVE_AREA { pub DataOffset: u32, pub DataSelector: u32, pub RegisterArea: [u8; 80], - pub Spare0: u32, + pub Cr0NpxState: u32, } pub const FORMAT_MESSAGE_ALLOCATE_BUFFER: FORMAT_MESSAGE_OPTIONS = 256u32; pub const FORMAT_MESSAGE_ARGUMENT_ARRAY: FORMAT_MESSAGE_OPTIONS = 8192u32; @@ -2618,6 +2629,7 @@ pub type HANDLE_FLAGS = u32; pub const HANDLE_FLAG_INHERIT: HANDLE_FLAGS = 1u32; pub const HANDLE_FLAG_PROTECT_FROM_CLOSE: HANDLE_FLAGS = 2u32; pub const HIGH_PRIORITY_CLASS: PROCESS_CREATION_FLAGS = 128u32; +pub type HINSTANCE = *mut core::ffi::c_void; pub type HLOCAL = *mut core::ffi::c_void; pub type HMODULE = *mut core::ffi::c_void; pub type HRESULT = i32; @@ -2771,7 +2783,7 @@ pub type LPPROGRESS_ROUTINE = Option< hsourcefile: HANDLE, hdestinationfile: HANDLE, lpdata: *const core::ffi::c_void, - ) -> u32, + ) -> COPYPROGRESSROUTINE_PROGRESS, >; pub type LPPROGRESS_ROUTINE_CALLBACK_REASON = u32; pub type LPTHREAD_START_ROUTINE = @@ -2822,11 +2834,12 @@ pub struct OBJECT_ATTRIBUTES { pub Length: u32, pub RootDirectory: HANDLE, pub ObjectName: *const UNICODE_STRING, - pub Attributes: u32, - pub SecurityDescriptor: *const core::ffi::c_void, - pub SecurityQualityOfService: *const core::ffi::c_void, + pub Attributes: OBJECT_ATTRIBUTE_FLAGS, + pub SecurityDescriptor: *const SECURITY_DESCRIPTOR, + pub SecurityQualityOfService: *const SECURITY_QUALITY_OF_SERVICE, } -pub const OBJ_DONT_REPARSE: i32 = 4096i32; +pub type OBJECT_ATTRIBUTE_FLAGS = u32; +pub const OBJ_DONT_REPARSE: OBJECT_ATTRIBUTE_FLAGS = 4096u32; pub const OPEN_ALWAYS: FILE_CREATION_DISPOSITION = 4u32; pub const OPEN_EXISTING: FILE_CREATION_DISPOSITION = 3u32; #[repr(C)] @@ -2887,7 +2900,8 @@ pub const PROCESS_MODE_BACKGROUND_END: PROCESS_CREATION_FLAGS = 2097152u32; pub const PROFILE_KERNEL: PROCESS_CREATION_FLAGS = 536870912u32; pub const PROFILE_SERVER: PROCESS_CREATION_FLAGS = 1073741824u32; pub const PROFILE_USER: PROCESS_CREATION_FLAGS = 268435456u32; -pub const PROGRESS_CONTINUE: u32 = 0u32; +pub const PROGRESS_CONTINUE: COPYPROGRESSROUTINE_PROGRESS = 0u32; +pub type PSID = *mut core::ffi::c_void; pub type PSTR = *mut u8; pub type PTIMERAPCROUTINE = Option< unsafe extern "system" fn( @@ -2914,9 +2928,30 @@ pub struct SECURITY_ATTRIBUTES { } pub const SECURITY_CONTEXT_TRACKING: FILE_FLAGS_AND_ATTRIBUTES = 262144u32; pub const SECURITY_DELEGATION: FILE_FLAGS_AND_ATTRIBUTES = 196608u32; +#[repr(C)] +#[derive(Clone, Copy)] +pub struct SECURITY_DESCRIPTOR { + pub Revision: u8, + pub Sbz1: u8, + pub Control: SECURITY_DESCRIPTOR_CONTROL, + pub Owner: PSID, + pub Group: PSID, + pub Sacl: *mut ACL, + pub Dacl: *mut ACL, +} +pub type SECURITY_DESCRIPTOR_CONTROL = u16; pub const SECURITY_EFFECTIVE_ONLY: FILE_FLAGS_AND_ATTRIBUTES = 524288u32; pub const SECURITY_IDENTIFICATION: FILE_FLAGS_AND_ATTRIBUTES = 65536u32; pub const SECURITY_IMPERSONATION: FILE_FLAGS_AND_ATTRIBUTES = 131072u32; +pub type SECURITY_IMPERSONATION_LEVEL = i32; +#[repr(C)] +#[derive(Clone, Copy)] +pub struct SECURITY_QUALITY_OF_SERVICE { + pub Length: u32, + pub ImpersonationLevel: SECURITY_IMPERSONATION_LEVEL, + pub ContextTrackingMode: u8, + pub EffectiveOnly: bool, +} pub const SECURITY_SQOS_PRESENT: FILE_FLAGS_AND_ATTRIBUTES = 1048576u32; pub const SECURITY_VALID_SQOS_FLAGS: FILE_FLAGS_AND_ATTRIBUTES = 2031616u32; pub type SEND_RECV_FLAGS = i32; @@ -3137,28 +3172,28 @@ pub struct WSABUF { pub buf: PSTR, } #[repr(C)] -#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] +#[cfg(target_arch = "x86")] #[derive(Clone, Copy)] pub struct WSADATA { pub wVersion: u16, pub wHighVersion: u16, + pub szDescription: [i8; 257], + pub szSystemStatus: [i8; 129], pub iMaxSockets: u16, pub iMaxUdpDg: u16, pub lpVendorInfo: PSTR, - pub szDescription: [i8; 257], - pub szSystemStatus: [i8; 129], } #[repr(C)] -#[cfg(target_arch = "x86")] +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] #[derive(Clone, Copy)] pub struct WSADATA { pub wVersion: u16, pub wHighVersion: u16, - pub szDescription: [i8; 257], - pub szSystemStatus: [i8; 129], pub iMaxSockets: u16, pub iMaxUdpDg: u16, pub lpVendorInfo: PSTR, + pub szDescription: [i8; 257], + pub szSystemStatus: [i8; 129], } pub const WSAEACCES: WSA_ERROR = 10013i32; pub const WSAEADDRINUSE: WSA_ERROR = 10048i32; @@ -3293,7 +3328,7 @@ pub const WSA_SECURE_HOST_NOT_FOUND: WSA_ERROR = 11032i32; pub const WSA_WAIT_EVENT_0: WSA_ERROR = 0i32; pub const WSA_WAIT_IO_COMPLETION: WSA_ERROR = 192i32; #[repr(C)] -#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] +#[cfg(target_arch = "x86")] #[derive(Clone, Copy)] pub struct XSAVE_FORMAT { pub ControlWord: u16, @@ -3310,11 +3345,11 @@ pub struct XSAVE_FORMAT { pub MxCsr: u32, pub MxCsr_Mask: u32, pub FloatRegisters: [M128A; 8], - pub XmmRegisters: [M128A; 16], - pub Reserved4: [u8; 96], + pub XmmRegisters: [M128A; 8], + pub Reserved4: [u8; 224], } #[repr(C)] -#[cfg(target_arch = "x86")] +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))] #[derive(Clone, Copy)] pub struct XSAVE_FORMAT { pub ControlWord: u16, @@ -3331,8 +3366,8 @@ pub struct XSAVE_FORMAT { pub MxCsr: u32, pub MxCsr_Mask: u32, pub FloatRegisters: [M128A; 8], - pub XmmRegisters: [M128A; 8], - pub Reserved4: [u8; 224], + pub XmmRegisters: [M128A; 16], + pub Reserved4: [u8; 96], } #[cfg(target_arch = "arm")] diff --git a/library/std/src/sys/pal/windows/compat.rs b/library/std/src/sys/pal/windows/compat.rs index 42999da166451..2b9838437e9c1 100644 --- a/library/std/src/sys/pal/windows/compat.rs +++ b/library/std/src/sys/pal/windows/compat.rs @@ -39,7 +39,7 @@ use crate::sys::c; // See https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-170 #[cfg(target_vendor = "win7")] #[used] -#[link_section = ".CRT$XCT"] +#[unsafe(link_section = ".CRT$XCT")] static INIT_TABLE_ENTRY: unsafe extern "C" fn() = init; /// Preload some imported functions. diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs index b3659351b8c11..7ae36ba943ba1 100644 --- a/library/std/src/sys/pal/windows/fs.rs +++ b/library/std/src/sys/pal/windows/fs.rs @@ -1,10 +1,10 @@ use super::api::{self, WinError}; use super::{IoResult, to_u16s}; -use crate::alloc::{alloc, handle_alloc_error}; +use crate::alloc::{Layout, alloc, dealloc}; use crate::borrow::Cow; use crate::ffi::{OsStr, OsString, c_void}; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; -use crate::mem::{self, MaybeUninit}; +use crate::mem::{self, MaybeUninit, offset_of}; use crate::os::windows::io::{AsHandle, BorrowedHandle}; use crate::os::windows::prelude::*; use crate::path::{Path, PathBuf}; @@ -44,7 +44,7 @@ pub struct FileType { } pub struct ReadDir { - handle: FindNextFileHandle, + handle: Option, root: Arc, first: Option, } @@ -113,13 +113,13 @@ impl fmt::Debug for ReadDir { impl Iterator for ReadDir { type Item = io::Result; fn next(&mut self) -> Option> { - if self.handle.0 == c::INVALID_HANDLE_VALUE { + let Some(handle) = self.handle.as_ref() else { // This iterator was initialized with an `INVALID_HANDLE_VALUE` as its handle. // Simply return `None` because this is only the case when `FindFirstFileExW` in // the construction of this iterator returns `ERROR_FILE_NOT_FOUND` which means // no matchhing files can be found. return None; - } + }; if let Some(first) = self.first.take() { if let Some(e) = DirEntry::new(&self.root, &first) { return Some(Ok(e)); @@ -128,7 +128,7 @@ impl Iterator for ReadDir { unsafe { let mut wfd = mem::zeroed(); loop { - if c::FindNextFileW(self.handle.0, &mut wfd) == 0 { + if c::FindNextFileW(handle.0, &mut wfd) == 0 { match api::get_last_error() { WinError::NO_MORE_FILES => return None, WinError { code } => { @@ -296,6 +296,10 @@ impl OpenOptions { impl File { pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { let path = maybe_verbatim(path)?; + Self::open_native(&path, opts) + } + + fn open_native(path: &[u16], opts: &OpenOptions) -> io::Result { let creation = opts.get_creation_mode()?; let handle = unsafe { c::CreateFileW( @@ -328,9 +332,6 @@ impl File { mem::size_of::() as u32, ); if result == 0 { - if api::get_last_error().code != 0 { - panic!("FILE_ALLOCATION_INFO failed!!!"); - } let eof = c::FILE_END_OF_FILE_INFO { EndOfFile: 0 }; let result = c::SetFileInformationByHandle( handle.as_raw_handle(), @@ -811,7 +812,7 @@ impl File { /// will prevent anyone from opening a new handle to the file. #[allow(unused)] fn win32_delete(&self) -> Result<(), WinError> { - let info = c::FILE_DISPOSITION_INFO { DeleteFile: c::TRUE as _ }; + let info = c::FILE_DISPOSITION_INFO { DeleteFile: true }; api::set_file_information_by_handle(self.handle.as_raw_handle(), &info) } @@ -1193,7 +1194,7 @@ pub fn readdir(p: &Path) -> io::Result { if find_handle != c::INVALID_HANDLE_VALUE { Ok(ReadDir { - handle: FindNextFileHandle(find_handle), + handle: Some(FindNextFileHandle(find_handle)), root: Arc::new(root), first: Some(wfd), }) @@ -1211,11 +1212,7 @@ pub fn readdir(p: &Path) -> io::Result { // See issue #120040: https://github.com/rust-lang/rust/issues/120040. let last_error = api::get_last_error(); if last_error == WinError::FILE_NOT_FOUND { - return Ok(ReadDir { - handle: FindNextFileHandle(find_handle), - root: Arc::new(root), - first: None, - }); + return Ok(ReadDir { handle: None, root: Arc::new(root), first: None }); } // Just return the error constructed from the raw OS error if the above is not the case. @@ -1229,149 +1226,98 @@ pub fn readdir(p: &Path) -> io::Result { pub fn unlink(p: &Path) -> io::Result<()> { let p_u16s = maybe_verbatim(p)?; - cvt(unsafe { c::DeleteFileW(p_u16s.as_ptr()) })?; - Ok(()) + if unsafe { c::DeleteFileW(p_u16s.as_ptr()) } == 0 { + let err = api::get_last_error(); + // if `DeleteFileW` fails with ERROR_ACCESS_DENIED then try to remove + // the file while ignoring the readonly attribute. + // This is accomplished by calling the `posix_delete` function on an open file handle. + if err == WinError::ACCESS_DENIED { + let mut opts = OpenOptions::new(); + opts.access_mode(c::DELETE); + opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT); + if let Ok(f) = File::open_native(&p_u16s, &opts) { + if f.posix_delete().is_ok() { + return Ok(()); + } + } + } + // return the original error if any of the above fails. + Err(io::Error::from_raw_os_error(err.code as i32)) + } else { + Ok(()) + } } pub fn rename(old: &Path, new: &Path) -> io::Result<()> { let old = maybe_verbatim(old)?; let new = maybe_verbatim(new)?; - let new_len_without_nul_in_bytes = (new.len() - 1).try_into().unwrap(); - - // The last field of FILE_RENAME_INFO, the file name, is unsized, - // and FILE_RENAME_INFO has two padding bytes. - // Therefore we need to make sure to not allocate less than - // size_of::() bytes, which would be the case with - // 0 or 1 character paths + a null byte. - let struct_size = mem::size_of::() - .max(mem::offset_of!(c::FILE_RENAME_INFO, FileName) + new.len() * mem::size_of::()); - - let struct_size: u32 = struct_size.try_into().unwrap(); - - let create_file = |extra_access, extra_flags| { - let handle = unsafe { - HandleOrInvalid::from_raw_handle(c::CreateFileW( - old.as_ptr(), - c::SYNCHRONIZE | c::DELETE | extra_access, - c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE, - ptr::null(), - c::OPEN_EXISTING, - c::FILE_ATTRIBUTE_NORMAL | c::FILE_FLAG_BACKUP_SEMANTICS | extra_flags, - ptr::null_mut(), - )) - }; - - OwnedHandle::try_from(handle).map_err(|_| io::Error::last_os_error()) - }; - - // The following code replicates `MoveFileEx`'s behavior as reverse-engineered from its disassembly. - // If `old` refers to a mount point, we move it instead of the target. - let handle = match create_file(c::FILE_READ_ATTRIBUTES, c::FILE_FLAG_OPEN_REPARSE_POINT) { - Ok(handle) => { - let mut file_attribute_tag_info: MaybeUninit = - MaybeUninit::uninit(); - - let result = unsafe { - cvt(c::GetFileInformationByHandleEx( - handle.as_raw_handle(), - c::FileAttributeTagInfo, - file_attribute_tag_info.as_mut_ptr().cast(), - mem::size_of::().try_into().unwrap(), - )) + if unsafe { c::MoveFileExW(old.as_ptr(), new.as_ptr(), c::MOVEFILE_REPLACE_EXISTING) } == 0 { + let err = api::get_last_error(); + // if `MoveFileExW` fails with ERROR_ACCESS_DENIED then try to move + // the file while ignoring the readonly attribute. + // This is accomplished by calling `SetFileInformationByHandle` with `FileRenameInfoEx`. + if err == WinError::ACCESS_DENIED { + let mut opts = OpenOptions::new(); + opts.access_mode(c::DELETE); + opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS); + let Ok(f) = File::open_native(&old, &opts) else { return Err(err).io_result() }; + + // Calculate the layout of the `FILE_RENAME_INFO` we pass to `SetFileInformation` + // This is a dynamically sized struct so we need to get the position of the last field to calculate the actual size. + let Ok(new_len_without_nul_in_bytes): Result = ((new.len() - 1) * 2).try_into() + else { + return Err(err).io_result(); }; - - if let Err(err) = result { - if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as _) - || err.raw_os_error() == Some(c::ERROR_INVALID_FUNCTION as _) - { - // `GetFileInformationByHandleEx` documents that not all underlying drivers support all file information classes. - // Since we know we passed the correct arguments, this means the underlying driver didn't understand our request; - // `MoveFileEx` proceeds by reopening the file without inhibiting reparse point behavior. - None - } else { - Some(Err(err)) - } - } else { - // SAFETY: The struct has been initialized by GetFileInformationByHandleEx - let file_attribute_tag_info = unsafe { file_attribute_tag_info.assume_init() }; - let file_type = FileType::new( - file_attribute_tag_info.FileAttributes, - file_attribute_tag_info.ReparseTag, - ); - - if file_type.is_symlink() { - // The file is a mount point, junction point or symlink so - // don't reopen the file so that the link gets renamed. - Some(Ok(handle)) - } else { - // Otherwise reopen the file without inhibiting reparse point behavior. - None + let offset: u32 = offset_of!(c::FILE_RENAME_INFO, FileName).try_into().unwrap(); + let struct_size = offset + new_len_without_nul_in_bytes + 2; + let layout = + Layout::from_size_align(struct_size as usize, align_of::()) + .unwrap(); + + // SAFETY: We allocate enough memory for a full FILE_RENAME_INFO struct and a filename. + let file_rename_info; + unsafe { + file_rename_info = alloc(layout).cast::(); + if file_rename_info.is_null() { + return Err(io::ErrorKind::OutOfMemory.into()); } - } - } - // The underlying driver may not support `FILE_FLAG_OPEN_REPARSE_POINT`: Retry without it. - Err(err) if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as _) => None, - Err(err) => Some(Err(err)), - } - .unwrap_or_else(|| create_file(0, 0))?; - - let layout = core::alloc::Layout::from_size_align( - struct_size as _, - mem::align_of::(), - ) - .unwrap(); - - let file_rename_info = unsafe { alloc(layout) } as *mut c::FILE_RENAME_INFO; - if file_rename_info.is_null() { - handle_alloc_error(layout); - } - - // SAFETY: file_rename_info is a non-null pointer pointing to memory allocated by the global allocator. - let mut file_rename_info = unsafe { Box::from_raw(file_rename_info) }; - - // SAFETY: We have allocated enough memory for a full FILE_RENAME_INFO struct and a filename. - unsafe { - (&raw mut (*file_rename_info).Anonymous).write(c::FILE_RENAME_INFO_0 { - Flags: c::FILE_RENAME_FLAG_REPLACE_IF_EXISTS | c::FILE_RENAME_FLAG_POSIX_SEMANTICS, - }); - - (&raw mut (*file_rename_info).RootDirectory).write(ptr::null_mut()); - (&raw mut (*file_rename_info).FileNameLength).write(new_len_without_nul_in_bytes); - - new.as_ptr() - .copy_to_nonoverlapping((&raw mut (*file_rename_info).FileName) as *mut u16, new.len()); - } + (&raw mut (*file_rename_info).Anonymous).write(c::FILE_RENAME_INFO_0 { + Flags: c::FILE_RENAME_FLAG_REPLACE_IF_EXISTS + | c::FILE_RENAME_FLAG_POSIX_SEMANTICS, + }); - // We don't use `set_file_information_by_handle` here as `FILE_RENAME_INFO` is used for both `FileRenameInfo` and `FileRenameInfoEx`. - let result = unsafe { - cvt(c::SetFileInformationByHandle( - handle.as_raw_handle(), - c::FileRenameInfoEx, - (&raw const *file_rename_info).cast::(), - struct_size, - )) - }; + (&raw mut (*file_rename_info).RootDirectory).write(ptr::null_mut()); + // Don't include the NULL in the size + (&raw mut (*file_rename_info).FileNameLength).write(new_len_without_nul_in_bytes); - if let Err(err) = result { - if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as _) { - // FileRenameInfoEx and FILE_RENAME_FLAG_POSIX_SEMANTICS were added in Windows 10 1607; retry with FileRenameInfo. - file_rename_info.Anonymous.ReplaceIfExists = 1; + new.as_ptr().copy_to_nonoverlapping( + (&raw mut (*file_rename_info).FileName).cast::(), + new.len(), + ); + } - cvt(unsafe { + let result = unsafe { c::SetFileInformationByHandle( - handle.as_raw_handle(), - c::FileRenameInfo, - (&raw const *file_rename_info).cast::(), + f.as_raw_handle(), + c::FileRenameInfoEx, + file_rename_info.cast::(), struct_size, ) - })?; + }; + unsafe { dealloc(file_rename_info.cast::(), layout) }; + if result == 0 { + if api::get_last_error() == WinError::DIR_NOT_EMPTY { + return Err(WinError::DIR_NOT_EMPTY).io_result(); + } else { + return Err(err).io_result(); + } + } } else { - return Err(err); + return Err(err).io_result(); } } - Ok(()) } diff --git a/library/std/src/sys/pal/windows/io.rs b/library/std/src/sys/pal/windows/io.rs deleted file mode 100644 index f2865d2ffc168..0000000000000 --- a/library/std/src/sys/pal/windows/io.rs +++ /dev/null @@ -1,153 +0,0 @@ -use core::ffi::c_void; - -use crate::marker::PhantomData; -use crate::mem::size_of; -use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle}; -use crate::slice; -use crate::sys::c; - -#[derive(Copy, Clone)] -#[repr(transparent)] -pub struct IoSlice<'a> { - vec: c::WSABUF, - _p: PhantomData<&'a [u8]>, -} - -impl<'a> IoSlice<'a> { - #[inline] - pub fn new(buf: &'a [u8]) -> IoSlice<'a> { - assert!(buf.len() <= u32::MAX as usize); - IoSlice { - vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_ptr() as *mut u8 }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if (self.vec.len as usize) < n { - panic!("advancing IoSlice beyond its length"); - } - - unsafe { - self.vec.len -= n as u32; - self.vec.buf = self.vec.buf.add(n); - } - } - - #[inline] - pub const fn as_slice(&self) -> &'a [u8] { - unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) } - } -} - -#[repr(transparent)] -pub struct IoSliceMut<'a> { - vec: c::WSABUF, - _p: PhantomData<&'a mut [u8]>, -} - -impl<'a> IoSliceMut<'a> { - #[inline] - pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { - assert!(buf.len() <= u32::MAX as usize); - IoSliceMut { - vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_mut_ptr() }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if (self.vec.len as usize) < n { - panic!("advancing IoSliceMut beyond its length"); - } - - unsafe { - self.vec.len -= n as u32; - self.vec.buf = self.vec.buf.add(n); - } - } - - #[inline] - pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.vec.buf, self.vec.len as usize) } - } - - #[inline] - pub const fn into_slice(self) -> &'a mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } - } - - #[inline] - pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.buf, self.vec.len as usize) } - } -} - -pub fn is_terminal(h: &impl AsHandle) -> bool { - handle_is_console(h.as_handle()) -} - -fn handle_is_console(handle: BorrowedHandle<'_>) -> bool { - // A null handle means the process has no console. - if handle.as_raw_handle().is_null() { - return false; - } - - let mut out = 0; - if unsafe { c::GetConsoleMode(handle.as_raw_handle(), &mut out) != 0 } { - // False positives aren't possible. If we got a console then we definitely have a console. - return true; - } - - // Otherwise, we fall back to an msys hack to see if we can detect the presence of a pty. - msys_tty_on(handle) -} - -fn msys_tty_on(handle: BorrowedHandle<'_>) -> bool { - // Early return if the handle is not a pipe. - if unsafe { c::GetFileType(handle.as_raw_handle()) != c::FILE_TYPE_PIPE } { - return false; - } - - /// Mirrors [`FILE_NAME_INFO`], giving it a fixed length that we can stack - /// allocate - /// - /// [`FILE_NAME_INFO`]: https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_name_info - #[repr(C)] - #[allow(non_snake_case)] - struct FILE_NAME_INFO { - FileNameLength: u32, - FileName: [u16; c::MAX_PATH as usize], - } - let mut name_info = FILE_NAME_INFO { FileNameLength: 0, FileName: [0; c::MAX_PATH as usize] }; - // Safety: buffer length is fixed. - let res = unsafe { - c::GetFileInformationByHandleEx( - handle.as_raw_handle(), - c::FileNameInfo, - (&raw mut name_info) as *mut c_void, - size_of::() as u32, - ) - }; - if res == 0 { - return false; - } - - // Use `get` because `FileNameLength` can be out of range. - let s = match name_info.FileName.get(..name_info.FileNameLength as usize / 2) { - None => return false, - Some(s) => s, - }; - let name = String::from_utf16_lossy(s); - // Get the file name only. - let name = name.rsplit('\\').next().unwrap_or(&name); - // This checks whether 'pty' exists in the file name, which indicates that - // a pseudo-terminal is attached. To mitigate against false positives - // (e.g., an actual file name that contains 'pty'), we also require that - // the file name begins with either the strings 'msys-' or 'cygwin-'.) - let is_msys = name.starts_with("msys-") || name.starts_with("cygwin-"); - let is_pty = name.contains("-pty"); - is_msys && is_pty -} diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 88e6def7a7577..1eca346b76c2b 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -21,8 +21,6 @@ pub mod fs; #[cfg(not(target_vendor = "win7"))] pub mod futex; pub mod handle; -pub mod io; -pub mod net; pub mod os; pub mod pipe; pub mod process; @@ -63,7 +61,7 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) { // SAFETY: must be called only once during runtime cleanup. // NOTE: this is not guaranteed to run, for example when the program aborts. pub unsafe fn cleanup() { - net::cleanup(); + crate::sys::net::cleanup(); } #[inline] @@ -272,7 +270,7 @@ where unreachable!(); } else { // Safety: First `k` values are initialized. - let slice: &[u16] = MaybeUninit::slice_assume_init_ref(&buf[..k]); + let slice: &[u16] = buf[..k].assume_init_ref(); return Ok(f2(slice)); } } diff --git a/library/std/src/sys/pal/windows/net.rs b/library/std/src/sys/pal/windows/net.rs deleted file mode 100644 index a92853c642c06..0000000000000 --- a/library/std/src/sys/pal/windows/net.rs +++ /dev/null @@ -1,574 +0,0 @@ -#![unstable(issue = "none", feature = "windows_net")] - -use core::ffi::{c_int, c_long, c_ulong, c_ushort}; - -use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut, Read}; -use crate::net::{Shutdown, SocketAddr}; -use crate::os::windows::io::{ - AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket, -}; -use crate::sync::OnceLock; -use crate::sys::c; -use crate::sys_common::{AsInner, FromInner, IntoInner, net}; -use crate::time::Duration; -use crate::{cmp, mem, ptr, sys}; - -#[allow(non_camel_case_types)] -pub type wrlen_t = i32; - -pub mod netc { - //! BSD socket compatibility shim - //! - //! Some Windows API types are not quite what's expected by our cross-platform - //! net code. E.g. naming differences or different pointer types. - - use core::ffi::{c_char, c_int, c_uint, c_ulong, c_ushort, c_void}; - - use crate::sys::c::{self, ADDRESS_FAMILY, ADDRINFOA, SOCKADDR, SOCKET}; - // re-exports from Windows API bindings. - pub use crate::sys::c::{ - ADDRESS_FAMILY as sa_family_t, ADDRINFOA as addrinfo, IP_ADD_MEMBERSHIP, - IP_DROP_MEMBERSHIP, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TTL, IPPROTO_IP, IPPROTO_IPV6, - IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MULTICAST_LOOP, IPV6_V6ONLY, SO_BROADCAST, - SO_RCVTIMEO, SO_SNDTIMEO, SOCK_DGRAM, SOCK_STREAM, SOCKADDR as sockaddr, - SOCKADDR_STORAGE as sockaddr_storage, SOL_SOCKET, bind, connect, freeaddrinfo, getpeername, - getsockname, getsockopt, listen, setsockopt, - }; - - #[allow(non_camel_case_types)] - pub type socklen_t = c_int; - - pub const AF_INET: i32 = c::AF_INET as i32; - pub const AF_INET6: i32 = c::AF_INET6 as i32; - - // The following two structs use a union in the generated bindings but - // our cross-platform code expects a normal field so it's redefined here. - // As a consequence, we also need to redefine other structs that use this struct. - #[repr(C)] - #[derive(Copy, Clone)] - pub struct in_addr { - pub s_addr: u32, - } - - #[repr(C)] - #[derive(Copy, Clone)] - pub struct in6_addr { - pub s6_addr: [u8; 16], - } - - #[repr(C)] - pub struct ip_mreq { - pub imr_multiaddr: in_addr, - pub imr_interface: in_addr, - } - - #[repr(C)] - pub struct ipv6_mreq { - pub ipv6mr_multiaddr: in6_addr, - pub ipv6mr_interface: c_uint, - } - - #[repr(C)] - #[derive(Copy, Clone)] - pub struct sockaddr_in { - pub sin_family: ADDRESS_FAMILY, - pub sin_port: c_ushort, - pub sin_addr: in_addr, - pub sin_zero: [c_char; 8], - } - - #[repr(C)] - #[derive(Copy, Clone)] - pub struct sockaddr_in6 { - pub sin6_family: ADDRESS_FAMILY, - pub sin6_port: c_ushort, - pub sin6_flowinfo: c_ulong, - pub sin6_addr: in6_addr, - pub sin6_scope_id: c_ulong, - } - - pub unsafe fn send(socket: SOCKET, buf: *const c_void, len: c_int, flags: c_int) -> c_int { - unsafe { c::send(socket, buf.cast::(), len, flags) } - } - pub unsafe fn sendto( - socket: SOCKET, - buf: *const c_void, - len: c_int, - flags: c_int, - addr: *const SOCKADDR, - addrlen: c_int, - ) -> c_int { - unsafe { c::sendto(socket, buf.cast::(), len, flags, addr, addrlen) } - } - pub unsafe fn getaddrinfo( - node: *const c_char, - service: *const c_char, - hints: *const ADDRINFOA, - res: *mut *mut ADDRINFOA, - ) -> c_int { - unsafe { c::getaddrinfo(node.cast::(), service.cast::(), hints, res) } - } -} - -pub struct Socket(OwnedSocket); - -static WSA_CLEANUP: OnceLock i32> = OnceLock::new(); - -/// Checks whether the Windows socket interface has been started already, and -/// if not, starts it. -pub fn init() { - let _ = WSA_CLEANUP.get_or_init(|| unsafe { - let mut data: c::WSADATA = mem::zeroed(); - let ret = c::WSAStartup( - 0x202, // version 2.2 - &mut data, - ); - assert_eq!(ret, 0); - - // Only register `WSACleanup` if `WSAStartup` is actually ever called. - // Workaround to prevent linking to `WS2_32.dll` when no network functionality is used. - // See issue #85441. - c::WSACleanup - }); -} - -pub fn cleanup() { - // only perform cleanup if network functionality was actually initialized - if let Some(cleanup) = WSA_CLEANUP.get() { - unsafe { - cleanup(); - } - } -} - -/// Returns the last error from the Windows socket interface. -fn last_error() -> io::Error { - io::Error::from_raw_os_error(unsafe { c::WSAGetLastError() }) -} - -#[doc(hidden)] -pub trait IsMinusOne { - fn is_minus_one(&self) -> bool; -} - -macro_rules! impl_is_minus_one { - ($($t:ident)*) => ($(impl IsMinusOne for $t { - fn is_minus_one(&self) -> bool { - *self == -1 - } - })*) -} - -impl_is_minus_one! { i8 i16 i32 i64 isize } - -/// Checks if the signed integer is the Windows constant `SOCKET_ERROR` (-1) -/// and if so, returns the last error from the Windows socket interface. This -/// function must be called before another call to the socket API is made. -pub fn cvt(t: T) -> io::Result { - if t.is_minus_one() { Err(last_error()) } else { Ok(t) } -} - -/// A variant of `cvt` for `getaddrinfo` which return 0 for a success. -pub fn cvt_gai(err: c_int) -> io::Result<()> { - if err == 0 { Ok(()) } else { Err(last_error()) } -} - -/// Just to provide the same interface as sys/pal/unix/net.rs -pub fn cvt_r(mut f: F) -> io::Result -where - T: IsMinusOne, - F: FnMut() -> T, -{ - cvt(f()) -} - -impl Socket { - pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { - let family = match *addr { - SocketAddr::V4(..) => netc::AF_INET, - SocketAddr::V6(..) => netc::AF_INET6, - }; - let socket = unsafe { - c::WSASocketW( - family, - ty, - 0, - ptr::null_mut(), - 0, - c::WSA_FLAG_OVERLAPPED | c::WSA_FLAG_NO_HANDLE_INHERIT, - ) - }; - - if socket != c::INVALID_SOCKET { - unsafe { Ok(Self::from_raw(socket)) } - } else { - let error = unsafe { c::WSAGetLastError() }; - - if error != c::WSAEPROTOTYPE && error != c::WSAEINVAL { - return Err(io::Error::from_raw_os_error(error)); - } - - let socket = - unsafe { c::WSASocketW(family, ty, 0, ptr::null_mut(), 0, c::WSA_FLAG_OVERLAPPED) }; - - if socket == c::INVALID_SOCKET { - return Err(last_error()); - } - - unsafe { - let socket = Self::from_raw(socket); - socket.0.set_no_inherit()?; - Ok(socket) - } - } - } - - pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { - let (addr, len) = addr.into_inner(); - let result = unsafe { c::connect(self.as_raw(), addr.as_ptr(), len) }; - cvt(result).map(drop) - } - - pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { - self.set_nonblocking(true)?; - let result = self.connect(addr); - self.set_nonblocking(false)?; - - match result { - Err(ref error) if error.kind() == io::ErrorKind::WouldBlock => { - if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { - return Err(io::Error::ZERO_TIMEOUT); - } - - let mut timeout = c::TIMEVAL { - tv_sec: cmp::min(timeout.as_secs(), c_long::MAX as u64) as c_long, - tv_usec: timeout.subsec_micros() as c_long, - }; - - if timeout.tv_sec == 0 && timeout.tv_usec == 0 { - timeout.tv_usec = 1; - } - - let fds = { - let mut fds = unsafe { mem::zeroed::() }; - fds.fd_count = 1; - fds.fd_array[0] = self.as_raw(); - fds - }; - - let mut writefds = fds; - let mut errorfds = fds; - - let count = { - let result = unsafe { - c::select(1, ptr::null_mut(), &mut writefds, &mut errorfds, &timeout) - }; - cvt(result)? - }; - - match count { - 0 => Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")), - _ => { - if writefds.fd_count != 1 { - if let Some(e) = self.take_error()? { - return Err(e); - } - } - - Ok(()) - } - } - } - _ => result, - } - } - - pub fn accept(&self, storage: *mut c::SOCKADDR, len: *mut c_int) -> io::Result { - let socket = unsafe { c::accept(self.as_raw(), storage, len) }; - - match socket { - c::INVALID_SOCKET => Err(last_error()), - _ => unsafe { Ok(Self::from_raw(socket)) }, - } - } - - pub fn duplicate(&self) -> io::Result { - Ok(Self(self.0.try_clone()?)) - } - - fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { - // On unix when a socket is shut down all further reads return 0, so we - // do the same on windows to map a shut down socket to returning EOF. - let length = cmp::min(buf.capacity(), i32::MAX as usize) as i32; - let result = - unsafe { c::recv(self.as_raw(), buf.as_mut().as_mut_ptr() as *mut _, length, flags) }; - - match result { - c::SOCKET_ERROR => { - let error = unsafe { c::WSAGetLastError() }; - - if error == c::WSAESHUTDOWN { - Ok(()) - } else { - Err(io::Error::from_raw_os_error(error)) - } - } - _ => { - unsafe { buf.advance_unchecked(result as usize) }; - Ok(()) - } - } - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), 0)?; - Ok(buf.len()) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.recv_with_flags(buf, 0) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - // On unix when a socket is shut down all further reads return 0, so we - // do the same on windows to map a shut down socket to returning EOF. - let length = cmp::min(bufs.len(), u32::MAX as usize) as u32; - let mut nread = 0; - let mut flags = 0; - let result = unsafe { - c::WSARecv( - self.as_raw(), - bufs.as_mut_ptr() as *mut c::WSABUF, - length, - &mut nread, - &mut flags, - ptr::null_mut(), - None, - ) - }; - - match result { - 0 => Ok(nread as usize), - _ => { - let error = unsafe { c::WSAGetLastError() }; - - if error == c::WSAESHUTDOWN { - Ok(0) - } else { - Err(io::Error::from_raw_os_error(error)) - } - } - } - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - true - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), c::MSG_PEEK)?; - Ok(buf.len()) - } - - fn recv_from_with_flags( - &self, - buf: &mut [u8], - flags: c_int, - ) -> io::Result<(usize, SocketAddr)> { - let mut storage = unsafe { mem::zeroed::() }; - let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t; - let length = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; - - // On unix when a socket is shut down all further reads return 0, so we - // do the same on windows to map a shut down socket to returning EOF. - let result = unsafe { - c::recvfrom( - self.as_raw(), - buf.as_mut_ptr() as *mut _, - length, - flags, - (&raw mut storage) as *mut _, - &mut addrlen, - ) - }; - - match result { - c::SOCKET_ERROR => { - let error = unsafe { c::WSAGetLastError() }; - - if error == c::WSAESHUTDOWN { - Ok((0, net::sockaddr_to_addr(&storage, addrlen as usize)?)) - } else { - Err(io::Error::from_raw_os_error(error)) - } - } - _ => Ok((result as usize, net::sockaddr_to_addr(&storage, addrlen as usize)?)), - } - } - - pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, 0) - } - - pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, c::MSG_PEEK) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - let length = cmp::min(bufs.len(), u32::MAX as usize) as u32; - let mut nwritten = 0; - let result = unsafe { - c::WSASend( - self.as_raw(), - bufs.as_ptr() as *const c::WSABUF as *mut _, - length, - &mut nwritten, - 0, - ptr::null_mut(), - None, - ) - }; - cvt(result).map(|_| nwritten as usize) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - true - } - - pub fn set_timeout(&self, dur: Option, kind: c_int) -> io::Result<()> { - let timeout = match dur { - Some(dur) => { - let timeout = sys::dur2timeout(dur); - if timeout == 0 { - return Err(io::Error::ZERO_TIMEOUT); - } - timeout - } - None => 0, - }; - net::setsockopt(self, c::SOL_SOCKET, kind, timeout) - } - - pub fn timeout(&self, kind: c_int) -> io::Result> { - let raw: u32 = net::getsockopt(self, c::SOL_SOCKET, kind)?; - if raw == 0 { - Ok(None) - } else { - let secs = raw / 1000; - let nsec = (raw % 1000) * 1000000; - Ok(Some(Duration::new(secs as u64, nsec as u32))) - } - } - - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - let how = match how { - Shutdown::Write => c::SD_SEND, - Shutdown::Read => c::SD_RECEIVE, - Shutdown::Both => c::SD_BOTH, - }; - let result = unsafe { c::shutdown(self.as_raw(), how) }; - cvt(result).map(drop) - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - let mut nonblocking = nonblocking as c_ulong; - let result = - unsafe { c::ioctlsocket(self.as_raw(), c::FIONBIO as c_int, &mut nonblocking) }; - cvt(result).map(drop) - } - - pub fn set_linger(&self, linger: Option) -> io::Result<()> { - let linger = c::LINGER { - l_onoff: linger.is_some() as c_ushort, - l_linger: linger.unwrap_or_default().as_secs() as c_ushort, - }; - - net::setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger) - } - - pub fn linger(&self) -> io::Result> { - let val: c::LINGER = net::getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?; - - Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) - } - - pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - net::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL) - } - - pub fn nodelay(&self) -> io::Result { - let raw: c::BOOL = net::getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?; - Ok(raw != 0) - } - - pub fn take_error(&self) -> io::Result> { - let raw: c_int = net::getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)?; - if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } - } - - // This is used by sys_common code to abstract over Windows and Unix. - pub fn as_raw(&self) -> c::SOCKET { - debug_assert_eq!(mem::size_of::(), mem::size_of::()); - debug_assert_eq!(mem::align_of::(), mem::align_of::()); - self.as_inner().as_raw_socket() as c::SOCKET - } - pub unsafe fn from_raw(raw: c::SOCKET) -> Self { - debug_assert_eq!(mem::size_of::(), mem::size_of::()); - debug_assert_eq!(mem::align_of::(), mem::align_of::()); - unsafe { Self::from_raw_socket(raw as RawSocket) } - } -} - -#[unstable(reason = "not public", issue = "none", feature = "fd_read")] -impl<'a> Read for &'a Socket { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - (**self).read(buf) - } -} - -impl AsInner for Socket { - #[inline] - fn as_inner(&self) -> &OwnedSocket { - &self.0 - } -} - -impl FromInner for Socket { - fn from_inner(sock: OwnedSocket) -> Socket { - Socket(sock) - } -} - -impl IntoInner for Socket { - fn into_inner(self) -> OwnedSocket { - self.0 - } -} - -impl AsSocket for Socket { - fn as_socket(&self) -> BorrowedSocket<'_> { - self.0.as_socket() - } -} - -impl AsRawSocket for Socket { - fn as_raw_socket(&self) -> RawSocket { - self.0.as_raw_socket() - } -} - -impl IntoRawSocket for Socket { - fn into_raw_socket(self) -> RawSocket { - self.0.into_raw_socket() - } -} - -impl FromRawSocket for Socket { - unsafe fn from_raw_socket(raw_socket: RawSocket) -> Self { - unsafe { Self(FromRawSocket::from_raw_socket(raw_socket)) } - } -} diff --git a/library/std/src/sys/pal/windows/os.rs b/library/std/src/sys/pal/windows/os.rs index 5231a34469abf..044dc2e8cd8fa 100644 --- a/library/std/src/sys/pal/windows/os.rs +++ b/library/std/src/sys/pal/windows/os.rs @@ -5,8 +5,9 @@ #[cfg(test)] mod tests; -use super::api::{self, WinError}; -use super::to_u16s; +#[cfg(not(target_vendor = "uwp"))] +use super::api::WinError; +use super::{api, to_u16s}; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::os::windows::ffi::EncodeWide; diff --git a/library/std/src/sys/pal/windows/process.rs b/library/std/src/sys/pal/windows/process.rs index 2ca20a21dfe51..9332c9b49ffb9 100644 --- a/library/std/src/sys/pal/windows/process.rs +++ b/library/std/src/sys/pal/windows/process.rs @@ -142,11 +142,11 @@ impl AsRef for EnvKey { } } -pub(crate) fn ensure_no_nuls>(str: T) -> io::Result { - if str.as_ref().encode_wide().any(|b| b == 0) { +pub(crate) fn ensure_no_nuls>(s: T) -> io::Result { + if s.as_ref().encode_wide().any(|b| b == 0) { Err(io::const_error!(ErrorKind::InvalidInput, "nul byte found in provided data")) } else { - Ok(str) + Ok(s) } } diff --git a/library/std/src/sys/pal/windows/process/tests.rs b/library/std/src/sys/pal/windows/process/tests.rs index 1bcc5fa6b2048..1377e12162f2f 100644 --- a/library/std/src/sys/pal/windows/process/tests.rs +++ b/library/std/src/sys/pal/windows/process/tests.rs @@ -33,7 +33,7 @@ fn test_thread_handle() { assert!(p.is_ok()); let mut p = p.unwrap(); - extern "system" { + unsafe extern "system" { fn ResumeThread(_: BorrowedHandle<'_>) -> u32; } unsafe { @@ -138,8 +138,10 @@ fn windows_env_unicode_case() { let mut cmd = Command::new("cmd"); cmd.env(a, "1"); cmd.env(b, "2"); - env::set_var(a, "1"); - env::set_var(b, "2"); + unsafe { + env::set_var(a, "1"); + env::set_var(b, "2"); + } for (key, value) in cmd.get_envs() { assert_eq!( @@ -158,7 +160,7 @@ fn windows_exe_resolver() { use super::resolve_exe; use crate::io; use crate::sys::fs::symlink; - use crate::sys_common::io::test::tmpdir; + use crate::test_helpers::tmpdir; let env_paths = || env::var_os("PATH"); diff --git a/library/std/src/sys/pal/windows/stack_overflow.rs b/library/std/src/sys/pal/windows/stack_overflow.rs index 467e21ab56a28..734cd30bed08f 100644 --- a/library/std/src/sys/pal/windows/stack_overflow.rs +++ b/library/std/src/sys/pal/windows/stack_overflow.rs @@ -18,10 +18,10 @@ unsafe extern "system" fn vectored_handler(ExceptionInfo: *mut c::EXCEPTION_POIN let code = rec.ExceptionCode; if code == c::EXCEPTION_STACK_OVERFLOW { - rtprintpanic!( - "\nthread '{}' has overflowed its stack\n", - thread::current().name().unwrap_or("") - ); + thread::with_current_name(|name| { + let name = name.unwrap_or(""); + rtprintpanic!("\nthread '{name}' has overflowed its stack\n"); + }); } c::EXCEPTION_CONTINUE_SEARCH } diff --git a/library/std/src/sys/pal/windows/stdio.rs b/library/std/src/sys/pal/windows/stdio.rs index 1b735e7f0cb2e..fd3f559ba1901 100644 --- a/library/std/src/sys/pal/windows/stdio.rs +++ b/library/std/src/sys/pal/windows/stdio.rs @@ -207,7 +207,7 @@ fn write_valid_utf8_to_console(handle: c::HANDLE, utf8: &str) -> io::Result return Ok(bytes_copied + value), Err(e) => return Err(e), diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index a64cd06856006..1bd0e67f37162 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -5,9 +5,6 @@ pub mod args; pub mod env; #[path = "../unsupported/fs.rs"] pub mod fs; -#[path = "../unsupported/io.rs"] -pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/xous/net/mod.rs b/library/std/src/sys/pal/xous/net/mod.rs deleted file mode 100644 index 3e18ed24208d3..0000000000000 --- a/library/std/src/sys/pal/xous/net/mod.rs +++ /dev/null @@ -1,83 +0,0 @@ -mod dns; - -mod tcpstream; -pub use tcpstream::*; - -mod tcplistener; -pub use tcplistener::*; - -mod udp; -pub use udp::*; - -// this structure needs to be synchronized with what's in net/src/api.rs -#[repr(C)] -#[derive(Debug)] -enum NetError { - // Ok = 0, - Unaddressable = 1, - SocketInUse = 2, - // AccessDenied = 3, - Invalid = 4, - // Finished = 5, - LibraryError = 6, - // AlreadyUsed = 7, - TimedOut = 8, - WouldBlock = 9, -} - -#[repr(C, align(4096))] -struct ConnectRequest { - raw: [u8; 4096], -} - -#[repr(C, align(4096))] -struct SendData { - raw: [u8; 4096], -} - -#[repr(C, align(4096))] -pub struct ReceiveData { - raw: [u8; 4096], -} - -#[repr(C, align(4096))] -pub struct GetAddress { - raw: [u8; 4096], -} - -pub use dns::LookupHost; - -#[allow(nonstandard_style)] -pub mod netc { - pub const AF_INET: u8 = 0; - pub const AF_INET6: u8 = 1; - pub type sa_family_t = u8; - - #[derive(Copy, Clone)] - pub struct in_addr { - pub s_addr: u32, - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in { - #[allow(dead_code)] - pub sin_family: sa_family_t, - pub sin_port: u16, - pub sin_addr: in_addr, - } - - #[derive(Copy, Clone)] - pub struct in6_addr { - pub s6_addr: [u8; 16], - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in6 { - #[allow(dead_code)] - pub sin6_family: sa_family_t, - pub sin6_port: u16, - pub sin6_addr: in6_addr, - pub sin6_flowinfo: u32, - pub sin6_scope_id: u32, - } -} diff --git a/library/std/src/sys/pal/xous/os.rs b/library/std/src/sys/pal/xous/os.rs index b0ab01a6383d2..2c87e7d91f27d 100644 --- a/library/std/src/sys/pal/xous/os.rs +++ b/library/std/src/sys/pal/xous/os.rs @@ -37,16 +37,16 @@ mod eh_unwinding { #[cfg(not(test))] mod c_compat { use crate::os::xous::ffi::exit; - extern "C" { + unsafe extern "C" { fn main() -> u32; } - #[no_mangle] + #[unsafe(no_mangle)] pub extern "C" fn abort() { exit(1); } - #[no_mangle] + #[unsafe(no_mangle)] pub extern "C" fn _start(eh_frame: usize, params_address: usize) { #[cfg(feature = "panic_unwind")] { @@ -67,7 +67,7 @@ mod c_compat { // This function is needed by the panic runtime. The symbol is named in // pre-link args for the target specification, so keep that in sync. - #[no_mangle] + #[unsafe(no_mangle)] // NB. used by both libunwind and libpanic_abort pub extern "C" fn __rust_abort() -> ! { exit(101); diff --git a/library/std/src/sys/pal/zkvm/abi.rs b/library/std/src/sys/pal/zkvm/abi.rs index 53332d90e02c0..d000574f6844d 100644 --- a/library/std/src/sys/pal/zkvm/abi.rs +++ b/library/std/src/sys/pal/zkvm/abi.rs @@ -18,7 +18,7 @@ pub mod fileno { pub const JOURNAL: u32 = 3; } -extern "C" { +unsafe extern "C" { // Wrappers around syscalls provided by risc0-zkvm-platform: pub fn sys_halt(); pub fn sys_output(output_id: u32, output_value: u32); diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs index 6ea057720296d..054c867f90d8e 100644 --- a/library/std/src/sys/pal/zkvm/mod.rs +++ b/library/std/src/sys/pal/zkvm/mod.rs @@ -16,10 +16,6 @@ pub mod args; pub mod env; #[path = "../unsupported/fs.rs"] pub mod fs; -#[path = "../unsupported/io.rs"] -pub mod io; -#[path = "../unsupported/net.rs"] -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/zkvm/stdio.rs b/library/std/src/sys/pal/zkvm/stdio.rs index dd218c8894ca5..5f1d06dd1d78d 100644 --- a/library/std/src/sys/pal/zkvm/stdio.rs +++ b/library/std/src/sys/pal/zkvm/stdio.rs @@ -54,7 +54,7 @@ impl io::Write for Stderr { } } -pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; +pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; pub fn is_ebadf(_err: &io::Error) -> bool { true diff --git a/library/std/src/sys/path/mod.rs b/library/std/src/sys/path/mod.rs index 24a94ec782824..1fa4e80d6780c 100644 --- a/library/std/src/sys/path/mod.rs +++ b/library/std/src/sys/path/mod.rs @@ -5,12 +5,12 @@ cfg_if::cfg_if! { } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { mod sgx; pub use sgx::*; - } else if #[cfg(any( - target_os = "uefi", - target_os = "solid_asp3", - ))] { + } else if #[cfg(target_os = "solid_asp3")] { mod unsupported_backslash; pub use unsupported_backslash::*; + } else if #[cfg(target_os = "uefi")] { + mod uefi; + pub use uefi::*; } else { mod unix; pub use unix::*; diff --git a/library/std/src/sys/path/sgx.rs b/library/std/src/sys/path/sgx.rs index c805c15e70245..32c7752f605d9 100644 --- a/library/std/src/sys/path/sgx.rs +++ b/library/std/src/sys/path/sgx.rs @@ -23,3 +23,7 @@ pub const MAIN_SEP: char = '/'; pub(crate) fn absolute(_path: &Path) -> io::Result { unsupported() } + +pub(crate) fn is_absolute(path: &Path) -> bool { + path.has_root() && path.prefix().is_some() +} diff --git a/library/std/src/sys/path/uefi.rs b/library/std/src/sys/path/uefi.rs new file mode 100644 index 0000000000000..a3f4a3bfe1b67 --- /dev/null +++ b/library/std/src/sys/path/uefi.rs @@ -0,0 +1,105 @@ +#![forbid(unsafe_op_in_unsafe_fn)] +use crate::ffi::OsStr; +use crate::io; +use crate::path::{Path, PathBuf, Prefix}; +use crate::sys::{helpers, unsupported_err}; + +const FORWARD_SLASH: u8 = b'/'; +const COLON: u8 = b':'; + +#[inline] +pub fn is_sep_byte(b: u8) -> bool { + b == b'\\' +} + +#[inline] +pub fn is_verbatim_sep(b: u8) -> bool { + b == b'\\' +} + +pub fn parse_prefix(_: &OsStr) -> Option> { + None +} + +pub const MAIN_SEP_STR: &str = "\\"; +pub const MAIN_SEP: char = '\\'; + +/// UEFI paths can be of 4 types: +/// +/// 1. Absolute Shell Path: Uses shell mappings (eg: `FS0:`). Does not exist if UEFI shell not present. +/// It can be identified with `:`. +/// Eg: FS0:\abc\run.efi +/// +/// 2. Absolute Device Path: this is what we want +/// It can be identified with `/`. +/// Eg: PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)/\abc\run.efi +/// +/// 3: Relative root: path relative to the current volume. +/// It will start with `\`. +/// Eg: \abc\run.efi +/// +/// 4: Relative +/// Eg: run.efi +/// +/// The algorithm is mostly taken from edk2 UEFI shell implementation and is +/// somewhat simple. Check for the path type in order. +/// +/// The volume mapping in Absolute Shell Path (not the rest of the path) can be converted to Device +/// Path Protocol using `EFI_SHELL->GetDevicePathFromMap`. The rest of the path (Relative root +/// path), can just be appended to the remaining path. +/// +/// For Relative root, we get the current volume (either in Shell Mapping, or Device Path Protocol +/// form) and join it with the relative root path. We then recurse the function to resolve the Shell +/// Mapping if present. +/// +/// For Relative paths, we use the current working directory to construct +/// the new path and recurse the function to resolve the Shell mapping if present. +/// +/// Finally, at the end, we get the 2nd form, i.e. Absolute Device Path, which can be used in the +/// normal UEFI APIs such as file, process, etc. +/// Eg: PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)/\abc\run.efi +pub(crate) fn absolute(path: &Path) -> io::Result { + // Absolute Shell Path + if path.as_os_str().as_encoded_bytes().contains(&COLON) { + let mut path_components = path.components(); + // Since path is not empty, it has at least one Component + let prefix = path_components.next().unwrap(); + + let dev_path = helpers::get_device_path_from_map(prefix.as_ref())?; + let mut dev_path_text = dev_path.to_text().map_err(|_| unsupported_err())?; + + // UEFI Shell does not seem to end device path with `/` + if *dev_path_text.as_encoded_bytes().last().unwrap() != FORWARD_SLASH { + dev_path_text.push("/"); + } + + let mut ans = PathBuf::from(dev_path_text); + ans.push(path_components); + + return Ok(ans); + } + + // Absolute Device Path + if path.as_os_str().as_encoded_bytes().contains(&FORWARD_SLASH) { + return Ok(path.to_path_buf()); + } + + // cur_dir() always returns something + let cur_dir = crate::env::current_dir().unwrap(); + let mut path_components = path.components(); + + // Relative Root + if path_components.next().unwrap() == crate::path::Component::RootDir { + let mut ans = PathBuf::new(); + ans.push(cur_dir.components().next().unwrap()); + ans.push(path_components); + return absolute(&ans); + } + + absolute(&cur_dir.join(path)) +} + +pub(crate) fn is_absolute(path: &Path) -> bool { + let temp = path.as_os_str().as_encoded_bytes(); + temp.contains(&COLON) || temp.contains(&FORWARD_SLASH) +} diff --git a/library/std/src/sys/path/unix.rs b/library/std/src/sys/path/unix.rs index 2a7c025c3c46a..361e99964f18c 100644 --- a/library/std/src/sys/path/unix.rs +++ b/library/std/src/sys/path/unix.rs @@ -60,3 +60,14 @@ pub(crate) fn absolute(path: &Path) -> io::Result { Ok(normalized) } + +pub(crate) fn is_absolute(path: &Path) -> bool { + if cfg!(target_os = "redox") { + // FIXME: Allow Redox prefixes + path.has_root() || crate::path::has_redox_scheme(path.as_u8_slice()) + } else if cfg!(any(unix, target_os = "hermit", target_os = "wasi")) { + path.has_root() + } else { + path.has_root() && path.prefix().is_some() + } +} diff --git a/library/std/src/sys/path/unsupported_backslash.rs b/library/std/src/sys/path/unsupported_backslash.rs index 855f443678c6c..30b06c132c98d 100644 --- a/library/std/src/sys/path/unsupported_backslash.rs +++ b/library/std/src/sys/path/unsupported_backslash.rs @@ -24,3 +24,7 @@ pub const MAIN_SEP: char = '\\'; pub(crate) fn absolute(_path: &Path) -> io::Result { unsupported() } + +pub(crate) fn is_absolute(path: &Path) -> bool { + path.has_root() && path.prefix().is_some() +} diff --git a/library/std/src/sys/path/windows.rs b/library/std/src/sys/path/windows.rs index de042fa3f82ab..1c53472191699 100644 --- a/library/std/src/sys/path/windows.rs +++ b/library/std/src/sys/path/windows.rs @@ -346,3 +346,7 @@ pub(crate) fn absolute(path: &Path) -> io::Result { os2path, ) } + +pub(crate) fn is_absolute(path: &Path) -> bool { + path.has_root() && path.prefix().is_some() +} diff --git a/library/std/src/sys/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs index 88a25caeff0df..cd2c7899f4bf1 100644 --- a/library/std/src/sys/personality/gcc.rs +++ b/library/std/src/sys/personality/gcc.rs @@ -194,7 +194,7 @@ cfg_if::cfg_if! { } } // defined in libgcc - extern "C" { + unsafe extern "C" { fn __gnu_unwind_frame( exception_object: *mut uw::_Unwind_Exception, context: *mut uw::_Unwind_Context, diff --git a/library/std/src/sys/personality/mod.rs b/library/std/src/sys/personality/mod.rs index 9754e840d151a..2e1d2e53a2979 100644 --- a/library/std/src/sys/personality/mod.rs +++ b/library/std/src/sys/personality/mod.rs @@ -31,7 +31,7 @@ cfg_if::cfg_if! { target_os = "psp", target_os = "xous", target_os = "solid_asp3", - all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re"), not(target_os = "rtems"), not(target_os = "nuttx")), + all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re"), not(target_os = "nuttx")), all(target_vendor = "fortanix", target_env = "sgx"), ))] { mod gcc; diff --git a/library/std/src/sys/random/arc4random.rs b/library/std/src/sys/random/arc4random.rs index 32467e9ebaa64..e1957bceb9002 100644 --- a/library/std/src/sys/random/arc4random.rs +++ b/library/std/src/sys/random/arc4random.rs @@ -25,7 +25,7 @@ use libc::arc4random_buf; target_os = "vita", // See https://github.com/vitasdk/newlib/blob/b89e5bc183b516945f9ee07eef483ecb916e45ff/newlib/libc/include/stdlib.h#L74 ))] #[cfg_attr(target_os = "haiku", link(name = "bsd"))] -extern "C" { +unsafe extern "C" { fn arc4random_buf(buf: *mut core::ffi::c_void, nbytes: libc::size_t); } diff --git a/library/std/src/sys/random/espidf.rs b/library/std/src/sys/random/espidf.rs index fd52cb5559ce5..6f48f7f1f2952 100644 --- a/library/std/src/sys/random/espidf.rs +++ b/library/std/src/sys/random/espidf.rs @@ -1,6 +1,6 @@ use crate::ffi::c_void; -extern "C" { +unsafe extern "C" { fn esp_fill_random(buf: *mut c_void, len: usize); } diff --git a/library/std/src/sys/random/fuchsia.rs b/library/std/src/sys/random/fuchsia.rs index 77d72b3c5b784..269e0d9aeeb57 100644 --- a/library/std/src/sys/random/fuchsia.rs +++ b/library/std/src/sys/random/fuchsia.rs @@ -4,7 +4,7 @@ //! . #[link(name = "zircon")] -extern "C" { +unsafe extern "C" { fn zx_cprng_draw(buffer: *mut u8, len: usize); } diff --git a/library/std/src/sys/random/teeos.rs b/library/std/src/sys/random/teeos.rs index fd6b24e19e982..6ca59cc12c98f 100644 --- a/library/std/src/sys/random/teeos.rs +++ b/library/std/src/sys/random/teeos.rs @@ -1,4 +1,4 @@ -extern "C" { +unsafe extern "C" { fn TEE_GenerateRandom(randomBuffer: *mut core::ffi::c_void, randomBufferLen: libc::size_t); } diff --git a/library/std/src/sys/random/windows.rs b/library/std/src/sys/random/windows.rs index 7566000f9e6ff..f5da637f56ca9 100644 --- a/library/std/src/sys/random/windows.rs +++ b/library/std/src/sys/random/windows.rs @@ -14,7 +14,7 @@ pub fn fill_bytes(mut bytes: &mut [u8]) { while !bytes.is_empty() { let len = bytes.len().try_into().unwrap_or(u32::MAX); let ret = unsafe { c::RtlGenRandom(bytes.as_mut_ptr().cast(), len) }; - assert_ne!(ret, 0, "failed to generate random data"); + assert!(ret, "failed to generate random data"); bytes = &mut bytes[len as usize..]; } } diff --git a/library/std/src/sys/sync/condvar/no_threads.rs b/library/std/src/sys/sync/condvar/no_threads.rs index 88ce39305e1ae..18d97d4b17ab0 100644 --- a/library/std/src/sys/sync/condvar/no_threads.rs +++ b/library/std/src/sys/sync/condvar/no_threads.rs @@ -1,4 +1,5 @@ use crate::sys::sync::Mutex; +use crate::thread::sleep; use crate::time::Duration; pub struct Condvar {} @@ -19,7 +20,8 @@ impl Condvar { panic!("condvar wait not supported") } - pub unsafe fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool { - panic!("condvar wait not supported"); + pub unsafe fn wait_timeout(&self, _mutex: &Mutex, dur: Duration) -> bool { + sleep(dur); + false } } diff --git a/library/std/src/sys/sync/mutex/windows7.rs b/library/std/src/sys/sync/mutex/windows7.rs index 689dba10f01ed..0b57de78ba6dd 100644 --- a/library/std/src/sys/sync/mutex/windows7.rs +++ b/library/std/src/sys/sync/mutex/windows7.rs @@ -44,7 +44,7 @@ impl Mutex { #[inline] pub fn try_lock(&self) -> bool { - unsafe { c::TryAcquireSRWLockExclusive(raw(self)) != 0 } + unsafe { c::TryAcquireSRWLockExclusive(raw(self)) } } #[inline] diff --git a/library/std/src/sys/sync/thread_parking/darwin.rs b/library/std/src/sys/sync/thread_parking/darwin.rs index 0553c5e19a91f..a0d24a91e7c69 100644 --- a/library/std/src/sys/sync/thread_parking/darwin.rs +++ b/library/std/src/sys/sync/thread_parking/darwin.rs @@ -24,7 +24,7 @@ const DISPATCH_TIME_NOW: dispatch_time_t = 0; const DISPATCH_TIME_FOREVER: dispatch_time_t = !0; // Contained in libSystem.dylib, which is linked by default. -extern "C" { +unsafe extern "C" { fn dispatch_time(when: dispatch_time_t, delta: i64) -> dispatch_time_t; fn dispatch_semaphore_create(val: isize) -> dispatch_semaphore_t; fn dispatch_semaphore_wait(dsema: dispatch_semaphore_t, timeout: dispatch_time_t) -> isize; diff --git a/library/std/src/sys/sync/thread_parking/windows7.rs b/library/std/src/sys/sync/thread_parking/windows7.rs index f7585e882f055..a1a0f8427cd83 100644 --- a/library/std/src/sys/sync/thread_parking/windows7.rs +++ b/library/std/src/sys/sync/thread_parking/windows7.rs @@ -195,7 +195,7 @@ mod keyed_events { pub unsafe fn park(parker: Pin<&Parker>) { // Wait for unpark() to produce this event. - c::NtWaitForKeyedEvent(keyed_event_handle(), parker.ptr(), 0, ptr::null_mut()); + c::NtWaitForKeyedEvent(keyed_event_handle(), parker.ptr(), false, ptr::null_mut()); // Set the state back to EMPTY (from either PARKED or NOTIFIED). // Note that we don't just write EMPTY, but use swap() to also // include an acquire-ordered read to synchronize with unpark()'s @@ -218,7 +218,7 @@ mod keyed_events { // Wait for unpark() to produce this event. let unparked = - c::NtWaitForKeyedEvent(handle, parker.ptr(), 0, &mut timeout) == c::STATUS_SUCCESS; + c::NtWaitForKeyedEvent(handle, parker.ptr(), false, &mut timeout) == c::STATUS_SUCCESS; // Set the state back to EMPTY (from either PARKED or NOTIFIED). let prev_state = parker.state.swap(EMPTY, Acquire); @@ -228,7 +228,7 @@ mod keyed_events { // was set to NOTIFIED, which means we *just* missed an // unpark(), which is now blocked on us to wait for it. // Wait for it to consume the event and unblock that thread. - c::NtWaitForKeyedEvent(handle, parker.ptr(), 0, ptr::null_mut()); + c::NtWaitForKeyedEvent(handle, parker.ptr(), false, ptr::null_mut()); } } pub unsafe fn unpark(parker: Pin<&Parker>) { @@ -239,7 +239,7 @@ mod keyed_events { // To prevent this thread from blocking indefinitely in that case, // park_impl() will, after seeing the state set to NOTIFIED after // waking up, call NtWaitForKeyedEvent again to unblock us. - c::NtReleaseKeyedEvent(keyed_event_handle(), parker.ptr(), 0, ptr::null_mut()); + c::NtReleaseKeyedEvent(keyed_event_handle(), parker.ptr(), false, ptr::null_mut()); } fn keyed_event_handle() -> c::HANDLE { diff --git a/library/std/src/sys/thread_local/destructors/linux_like.rs b/library/std/src/sys/thread_local/destructors/linux_like.rs index f473dc4d79df5..817941229eefe 100644 --- a/library/std/src/sys/thread_local/destructors/linux_like.rs +++ b/library/std/src/sys/thread_local/destructors/linux_like.rs @@ -27,7 +27,7 @@ pub unsafe fn register(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { #[allow(non_camel_case_types)] pub struct c_int(#[allow(dead_code)] pub core::ffi::c_int); - extern "C" { + unsafe extern "C" { #[linkage = "extern_weak"] static __dso_handle: *mut u8; #[linkage = "extern_weak"] diff --git a/library/std/src/sys/thread_local/guard/apple.rs b/library/std/src/sys/thread_local/guard/apple.rs index fa25b116622fc..edcedf21e9ec6 100644 --- a/library/std/src/sys/thread_local/guard/apple.rs +++ b/library/std/src/sys/thread_local/guard/apple.rs @@ -10,7 +10,7 @@ pub fn enable() { #[thread_local] static REGISTERED: Cell = Cell::new(false); - extern "C" { + unsafe extern "C" { fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8); } diff --git a/library/std/src/sys/thread_local/guard/windows.rs b/library/std/src/sys/thread_local/guard/windows.rs index 1752b0e1208af..b15a0d7c0bdfb 100644 --- a/library/std/src/sys/thread_local/guard/windows.rs +++ b/library/std/src/sys/thread_local/guard/windows.rs @@ -74,7 +74,7 @@ pub fn enable() { unsafe { ptr::from_ref(&CALLBACK).read_volatile() }; } -#[link_section = ".CRT$XLB"] +#[unsafe(link_section = ".CRT$XLB")] #[cfg_attr(miri, used)] // Miri only considers explicitly `#[used]` statics for `lookup_link_section` pub static CALLBACK: unsafe extern "system" fn(*mut c_void, u32, *mut c_void) = tls_callback; diff --git a/library/std/src/sys/thread_local/key/unix.rs b/library/std/src/sys/thread_local/key/unix.rs index b4b58b3470631..93bd0d1f66850 100644 --- a/library/std/src/sys/thread_local/key/unix.rs +++ b/library/std/src/sys/thread_local/key/unix.rs @@ -8,7 +8,7 @@ mod libc { #[allow(non_camel_case_types)] pub type pthread_key_t = ffi::c_uint; - extern "C" { + unsafe extern "C" { pub fn pthread_key_create( key: *mut pthread_key_t, destructor: unsafe extern "C" fn(*mut ffi::c_void), diff --git a/library/std/src/sys/thread_local/key/xous.rs b/library/std/src/sys/thread_local/key/xous.rs index 2ab4bba7d8e98..55ac5b20e1ab0 100644 --- a/library/std/src/sys/thread_local/key/xous.rs +++ b/library/std/src/sys/thread_local/key/xous.rs @@ -51,15 +51,15 @@ const TLS_MEMORY_SIZE: usize = 4096; /// TLS keys start at `1`. Index `0` is unused #[cfg(not(test))] -#[export_name = "_ZN16__rust_internals3std3sys4xous16thread_local_key13TLS_KEY_INDEXE"] +#[unsafe(export_name = "_ZN16__rust_internals3std3sys4xous16thread_local_key13TLS_KEY_INDEXE")] static TLS_KEY_INDEX: AtomicUsize = AtomicUsize::new(1); #[cfg(not(test))] -#[export_name = "_ZN16__rust_internals3std3sys4xous16thread_local_key9DTORSE"] +#[unsafe(export_name = "_ZN16__rust_internals3std3sys4xous16thread_local_key9DTORSE")] static DTORS: AtomicPtr = AtomicPtr::new(ptr::null_mut()); #[cfg(test)] -extern "Rust" { +unsafe extern "Rust" { #[link_name = "_ZN16__rust_internals3std3sys4xous16thread_local_key13TLS_KEY_INDEXE"] static TLS_KEY_INDEX: AtomicUsize; diff --git a/library/std/src/sys_common/io.rs b/library/std/src/sys_common/io.rs deleted file mode 100644 index 6f6f282d432d6..0000000000000 --- a/library/std/src/sys_common/io.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Bare metal platforms usually have very small amounts of RAM -// (in the order of hundreds of KB) -pub const DEFAULT_BUF_SIZE: usize = if cfg!(target_os = "espidf") { 512 } else { 8 * 1024 }; - -#[cfg(test)] -#[allow(dead_code)] // not used on emscripten and wasi -pub mod test { - use rand::RngCore; - - use crate::path::{Path, PathBuf}; - use crate::{env, fs, thread}; - - pub struct TempDir(PathBuf); - - impl TempDir { - pub fn join(&self, path: &str) -> PathBuf { - let TempDir(ref p) = *self; - p.join(path) - } - - pub fn path(&self) -> &Path { - let TempDir(ref p) = *self; - p - } - } - - impl Drop for TempDir { - fn drop(&mut self) { - // Gee, seeing how we're testing the fs module I sure hope that we - // at least implement this correctly! - let TempDir(ref p) = *self; - let result = fs::remove_dir_all(p); - // Avoid panicking while panicking as this causes the process to - // immediately abort, without displaying test results. - if !thread::panicking() { - result.unwrap(); - } - } - } - - #[track_caller] // for `test_rng` - pub fn tmpdir() -> TempDir { - let p = env::temp_dir(); - let mut r = crate::test_helpers::test_rng(); - let ret = p.join(&format!("rust-{}", r.next_u32())); - fs::create_dir(&ret).unwrap(); - TempDir(ret) - } -} diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index 4f7a131f6bb90..4dc67d26bd8ff 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -21,25 +21,10 @@ mod tests; pub mod fs; -pub mod io; pub mod process; pub mod wstr; pub mod wtf8; -cfg_if::cfg_if! { - if #[cfg(any( - all(unix, not(target_os = "l4re")), - windows, - target_os = "hermit", - target_os = "solid_asp3", - all(target_os = "wasi", target_env = "p2") - ))] { - pub mod net; - } else { - pub use crate::sys::net; - } -} - // common error constructors /// A trait for viewing representations from std types diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs deleted file mode 100644 index 74306978d2284..0000000000000 --- a/library/std/src/sys_common/net.rs +++ /dev/null @@ -1,760 +0,0 @@ -#[cfg(test)] -mod tests; - -use crate::ffi::{c_int, c_void}; -use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; -use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; -use crate::sys::common::small_c_string::run_with_cstr; -use crate::sys::net::{Socket, cvt, cvt_gai, cvt_r, init, netc as c, wrlen_t}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::time::Duration; -use crate::{cmp, fmt, mem, ptr}; - -cfg_if::cfg_if! { - if #[cfg(any( - target_os = "dragonfly", - target_os = "freebsd", - target_os = "openbsd", - target_os = "netbsd", - target_os = "illumos", - target_os = "solaris", - target_os = "haiku", - target_os = "l4re", - target_os = "nto", - target_os = "nuttx", - target_vendor = "apple", - ))] { - use crate::sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; - use crate::sys::net::netc::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; - } else { - use crate::sys::net::netc::IPV6_ADD_MEMBERSHIP; - use crate::sys::net::netc::IPV6_DROP_MEMBERSHIP; - } -} - -cfg_if::cfg_if! { - if #[cfg(any( - target_os = "linux", target_os = "android", - target_os = "hurd", - target_os = "dragonfly", target_os = "freebsd", - target_os = "openbsd", target_os = "netbsd", - target_os = "solaris", target_os = "illumos", - target_os = "haiku", target_os = "nto"))] { - use libc::MSG_NOSIGNAL; - } else { - const MSG_NOSIGNAL: c_int = 0x0; - } -} - -cfg_if::cfg_if! { - if #[cfg(any( - target_os = "dragonfly", target_os = "freebsd", - target_os = "openbsd", target_os = "netbsd", - target_os = "solaris", target_os = "illumos", - target_os = "nto"))] { - use crate::ffi::c_uchar; - type IpV4MultiCastType = c_uchar; - } else { - type IpV4MultiCastType = c_int; - } -} - -//////////////////////////////////////////////////////////////////////////////// -// sockaddr and misc bindings -//////////////////////////////////////////////////////////////////////////////// - -pub fn setsockopt( - sock: &Socket, - level: c_int, - option_name: c_int, - option_value: T, -) -> io::Result<()> { - unsafe { - cvt(c::setsockopt( - sock.as_raw(), - level, - option_name, - (&raw const option_value) as *const _, - mem::size_of::() as c::socklen_t, - ))?; - Ok(()) - } -} - -pub fn getsockopt(sock: &Socket, level: c_int, option_name: c_int) -> io::Result { - unsafe { - let mut option_value: T = mem::zeroed(); - let mut option_len = mem::size_of::() as c::socklen_t; - cvt(c::getsockopt( - sock.as_raw(), - level, - option_name, - (&raw mut option_value) as *mut _, - &mut option_len, - ))?; - Ok(option_value) - } -} - -fn sockname(f: F) -> io::Result -where - F: FnOnce(*mut c::sockaddr, *mut c::socklen_t) -> c_int, -{ - unsafe { - let mut storage: c::sockaddr_storage = mem::zeroed(); - let mut len = mem::size_of_val(&storage) as c::socklen_t; - cvt(f((&raw mut storage) as *mut _, &mut len))?; - sockaddr_to_addr(&storage, len as usize) - } -} - -pub fn sockaddr_to_addr(storage: &c::sockaddr_storage, len: usize) -> io::Result { - match storage.ss_family as c_int { - c::AF_INET => { - assert!(len >= mem::size_of::()); - Ok(SocketAddr::V4(FromInner::from_inner(unsafe { - *(storage as *const _ as *const c::sockaddr_in) - }))) - } - c::AF_INET6 => { - assert!(len >= mem::size_of::()); - Ok(SocketAddr::V6(FromInner::from_inner(unsafe { - *(storage as *const _ as *const c::sockaddr_in6) - }))) - } - _ => Err(io::const_error!(ErrorKind::InvalidInput, "invalid argument")), - } -} - -#[cfg(target_os = "android")] -fn to_ipv6mr_interface(value: u32) -> c_int { - value as c_int -} - -#[cfg(not(target_os = "android"))] -fn to_ipv6mr_interface(value: u32) -> crate::ffi::c_uint { - value as crate::ffi::c_uint -} - -//////////////////////////////////////////////////////////////////////////////// -// get_host_addresses -//////////////////////////////////////////////////////////////////////////////// - -pub struct LookupHost { - original: *mut c::addrinfo, - cur: *mut c::addrinfo, - port: u16, -} - -impl LookupHost { - pub fn port(&self) -> u16 { - self.port - } -} - -impl Iterator for LookupHost { - type Item = SocketAddr; - fn next(&mut self) -> Option { - loop { - unsafe { - let cur = self.cur.as_ref()?; - self.cur = cur.ai_next; - match sockaddr_to_addr(mem::transmute(cur.ai_addr), cur.ai_addrlen as usize) { - Ok(addr) => return Some(addr), - Err(_) => continue, - } - } - } - } -} - -unsafe impl Sync for LookupHost {} -unsafe impl Send for LookupHost {} - -impl Drop for LookupHost { - fn drop(&mut self) { - unsafe { c::freeaddrinfo(self.original) } - } -} - -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(s: &str) -> io::Result { - macro_rules! try_opt { - ($e:expr, $msg:expr) => { - match $e { - Some(r) => r, - None => return Err(io::const_error!(io::ErrorKind::InvalidInput, $msg)), - } - }; - } - - // split the string by ':' and convert the second part to u16 - let (host, port_str) = try_opt!(s.rsplit_once(':'), "invalid socket address"); - let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); - (host, port).try_into() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from((host, port): (&'a str, u16)) -> io::Result { - init(); - - run_with_cstr(host.as_bytes(), &|c_host| { - let mut hints: c::addrinfo = unsafe { mem::zeroed() }; - hints.ai_socktype = c::SOCK_STREAM; - let mut res = ptr::null_mut(); - unsafe { - cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)) - .map(|_| LookupHost { original: res, cur: res, port }) - } - }) - } -} - -//////////////////////////////////////////////////////////////////////////////// -// TCP streams -//////////////////////////////////////////////////////////////////////////////// - -pub struct TcpStream { - inner: Socket, -} - -impl TcpStream { - pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result { - let addr = addr?; - - init(); - - let sock = Socket::new(addr, c::SOCK_STREAM)?; - sock.connect(addr)?; - Ok(TcpStream { inner: sock }) - } - - pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result { - init(); - - let sock = Socket::new(addr, c::SOCK_STREAM)?; - sock.connect_timeout(addr, timeout)?; - Ok(TcpStream { inner: sock }) - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { - self.inner.set_timeout(dur, c::SO_RCVTIMEO) - } - - pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { - self.inner.set_timeout(dur, c::SO_SNDTIMEO) - } - - pub fn read_timeout(&self) -> io::Result> { - self.inner.timeout(c::SO_RCVTIMEO) - } - - pub fn write_timeout(&self) -> io::Result> { - self.inner.timeout(c::SO_SNDTIMEO) - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - self.inner.peek(buf) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.inner.read(buf) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.inner.read_buf(buf) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.inner.read_vectored(bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - self.inner.is_read_vectored() - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; - let ret = cvt(unsafe { - c::send(self.inner.as_raw(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL) - })?; - Ok(ret as usize) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.inner.write_vectored(bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - self.inner.is_write_vectored() - } - - pub fn peer_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getpeername(self.inner.as_raw(), buf, len) }) - } - - pub fn socket_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) }) - } - - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - self.inner.shutdown(how) - } - - pub fn duplicate(&self) -> io::Result { - self.inner.duplicate().map(|s| TcpStream { inner: s }) - } - - pub fn set_linger(&self, linger: Option) -> io::Result<()> { - self.inner.set_linger(linger) - } - - pub fn linger(&self) -> io::Result> { - self.inner.linger() - } - - pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - self.inner.set_nodelay(nodelay) - } - - pub fn nodelay(&self) -> io::Result { - self.inner.nodelay() - } - - pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { - setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) - } - - pub fn ttl(&self) -> io::Result { - let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)?; - Ok(raw as u32) - } - - pub fn take_error(&self) -> io::Result> { - self.inner.take_error() - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - self.inner.set_nonblocking(nonblocking) - } -} - -impl AsInner for TcpStream { - #[inline] - fn as_inner(&self) -> &Socket { - &self.inner - } -} - -impl FromInner for TcpStream { - fn from_inner(socket: Socket) -> TcpStream { - TcpStream { inner: socket } - } -} - -impl fmt::Debug for TcpStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut res = f.debug_struct("TcpStream"); - - if let Ok(addr) = self.socket_addr() { - res.field("addr", &addr); - } - - if let Ok(peer) = self.peer_addr() { - res.field("peer", &peer); - } - - let name = if cfg!(windows) { "socket" } else { "fd" }; - res.field(name, &self.inner.as_raw()).finish() - } -} - -//////////////////////////////////////////////////////////////////////////////// -// TCP listeners -//////////////////////////////////////////////////////////////////////////////// - -pub struct TcpListener { - inner: Socket, -} - -impl TcpListener { - pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result { - let addr = addr?; - - init(); - - let sock = Socket::new(addr, c::SOCK_STREAM)?; - - // On platforms with Berkeley-derived sockets, this allows to quickly - // rebind a socket, without needing to wait for the OS to clean up the - // previous one. - // - // On Windows, this allows rebinding sockets which are actively in use, - // which allows “socket hijacking”, so we explicitly don't set it here. - // https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse - #[cfg(not(windows))] - setsockopt(&sock, c::SOL_SOCKET, c::SO_REUSEADDR, 1 as c_int)?; - - // Bind our new socket - let (addr, len) = addr.into_inner(); - cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?; - - cfg_if::cfg_if! { - if #[cfg(target_os = "horizon")] { - // The 3DS doesn't support a big connection backlog. Sometimes - // it allows up to about 37, but other times it doesn't even - // accept 32. There may be a global limitation causing this. - let backlog = 20; - } else if #[cfg(target_os = "haiku")] { - // Haiku does not support a queue length > 32 - // https://github.com/haiku/haiku/blob/979a0bc487864675517fb2fab28f87dc8bf43041/headers/posix/sys/socket.h#L81 - let backlog = 32; - } else { - // The default for all other platforms - let backlog = 128; - } - } - - // Start listening - cvt(unsafe { c::listen(sock.as_raw(), backlog) })?; - Ok(TcpListener { inner: sock }) - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn socket_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) }) - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - let mut storage: c::sockaddr_storage = unsafe { mem::zeroed() }; - let mut len = mem::size_of_val(&storage) as c::socklen_t; - let sock = self.inner.accept((&raw mut storage) as *mut _, &mut len)?; - let addr = sockaddr_to_addr(&storage, len as usize)?; - Ok((TcpStream { inner: sock }, addr)) - } - - pub fn duplicate(&self) -> io::Result { - self.inner.duplicate().map(|s| TcpListener { inner: s }) - } - - pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { - setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) - } - - pub fn ttl(&self) -> io::Result { - let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)?; - Ok(raw as u32) - } - - pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> { - setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY, only_v6 as c_int) - } - - pub fn only_v6(&self) -> io::Result { - let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY)?; - Ok(raw != 0) - } - - pub fn take_error(&self) -> io::Result> { - self.inner.take_error() - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - self.inner.set_nonblocking(nonblocking) - } -} - -impl FromInner for TcpListener { - fn from_inner(socket: Socket) -> TcpListener { - TcpListener { inner: socket } - } -} - -impl fmt::Debug for TcpListener { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut res = f.debug_struct("TcpListener"); - - if let Ok(addr) = self.socket_addr() { - res.field("addr", &addr); - } - - let name = if cfg!(windows) { "socket" } else { "fd" }; - res.field(name, &self.inner.as_raw()).finish() - } -} - -//////////////////////////////////////////////////////////////////////////////// -// UDP -//////////////////////////////////////////////////////////////////////////////// - -pub struct UdpSocket { - inner: Socket, -} - -impl UdpSocket { - pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result { - let addr = addr?; - - init(); - - let sock = Socket::new(addr, c::SOCK_DGRAM)?; - let (addr, len) = addr.into_inner(); - cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?; - Ok(UdpSocket { inner: sock }) - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn peer_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getpeername(self.inner.as_raw(), buf, len) }) - } - - pub fn socket_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) }) - } - - pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.inner.recv_from(buf) - } - - pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.inner.peek_from(buf) - } - - pub fn send_to(&self, buf: &[u8], dst: &SocketAddr) -> io::Result { - let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; - let (dst, dstlen) = dst.into_inner(); - let ret = cvt(unsafe { - c::sendto( - self.inner.as_raw(), - buf.as_ptr() as *const c_void, - len, - MSG_NOSIGNAL, - dst.as_ptr(), - dstlen, - ) - })?; - Ok(ret as usize) - } - - pub fn duplicate(&self) -> io::Result { - self.inner.duplicate().map(|s| UdpSocket { inner: s }) - } - - pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { - self.inner.set_timeout(dur, c::SO_RCVTIMEO) - } - - pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { - self.inner.set_timeout(dur, c::SO_SNDTIMEO) - } - - pub fn read_timeout(&self) -> io::Result> { - self.inner.timeout(c::SO_RCVTIMEO) - } - - pub fn write_timeout(&self) -> io::Result> { - self.inner.timeout(c::SO_SNDTIMEO) - } - - pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> { - setsockopt(&self.inner, c::SOL_SOCKET, c::SO_BROADCAST, broadcast as c_int) - } - - pub fn broadcast(&self) -> io::Result { - let raw: c_int = getsockopt(&self.inner, c::SOL_SOCKET, c::SO_BROADCAST)?; - Ok(raw != 0) - } - - pub fn set_multicast_loop_v4(&self, multicast_loop_v4: bool) -> io::Result<()> { - setsockopt( - &self.inner, - c::IPPROTO_IP, - c::IP_MULTICAST_LOOP, - multicast_loop_v4 as IpV4MultiCastType, - ) - } - - pub fn multicast_loop_v4(&self) -> io::Result { - let raw: IpV4MultiCastType = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_LOOP)?; - Ok(raw != 0) - } - - pub fn set_multicast_ttl_v4(&self, multicast_ttl_v4: u32) -> io::Result<()> { - setsockopt( - &self.inner, - c::IPPROTO_IP, - c::IP_MULTICAST_TTL, - multicast_ttl_v4 as IpV4MultiCastType, - ) - } - - pub fn multicast_ttl_v4(&self) -> io::Result { - let raw: IpV4MultiCastType = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_TTL)?; - Ok(raw as u32) - } - - pub fn set_multicast_loop_v6(&self, multicast_loop_v6: bool) -> io::Result<()> { - setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP, multicast_loop_v6 as c_int) - } - - pub fn multicast_loop_v6(&self) -> io::Result { - let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP)?; - Ok(raw != 0) - } - - pub fn join_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { - let mreq = c::ip_mreq { - imr_multiaddr: multiaddr.into_inner(), - imr_interface: interface.into_inner(), - }; - setsockopt(&self.inner, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreq) - } - - pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { - let mreq = c::ipv6_mreq { - ipv6mr_multiaddr: multiaddr.into_inner(), - ipv6mr_interface: to_ipv6mr_interface(interface), - }; - setsockopt(&self.inner, c::IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, mreq) - } - - pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { - let mreq = c::ip_mreq { - imr_multiaddr: multiaddr.into_inner(), - imr_interface: interface.into_inner(), - }; - setsockopt(&self.inner, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreq) - } - - pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { - let mreq = c::ipv6_mreq { - ipv6mr_multiaddr: multiaddr.into_inner(), - ipv6mr_interface: to_ipv6mr_interface(interface), - }; - setsockopt(&self.inner, c::IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, mreq) - } - - pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { - setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) - } - - pub fn ttl(&self) -> io::Result { - let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)?; - Ok(raw as u32) - } - - pub fn take_error(&self) -> io::Result> { - self.inner.take_error() - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - self.inner.set_nonblocking(nonblocking) - } - - pub fn recv(&self, buf: &mut [u8]) -> io::Result { - self.inner.read(buf) - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - self.inner.peek(buf) - } - - pub fn send(&self, buf: &[u8]) -> io::Result { - let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; - let ret = cvt(unsafe { - c::send(self.inner.as_raw(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL) - })?; - Ok(ret as usize) - } - - pub fn connect(&self, addr: io::Result<&SocketAddr>) -> io::Result<()> { - let (addr, len) = addr?.into_inner(); - cvt_r(|| unsafe { c::connect(self.inner.as_raw(), addr.as_ptr(), len) }).map(drop) - } -} - -impl FromInner for UdpSocket { - fn from_inner(socket: Socket) -> UdpSocket { - UdpSocket { inner: socket } - } -} - -impl fmt::Debug for UdpSocket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut res = f.debug_struct("UdpSocket"); - - if let Ok(addr) = self.socket_addr() { - res.field("addr", &addr); - } - - let name = if cfg!(windows) { "socket" } else { "fd" }; - res.field(name, &self.inner.as_raw()).finish() - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Converting SocketAddr to libc representation -//////////////////////////////////////////////////////////////////////////////// - -/// A type with the same memory layout as `c::sockaddr`. Used in converting Rust level -/// SocketAddr* types into their system representation. The benefit of this specific -/// type over using `c::sockaddr_storage` is that this type is exactly as large as it -/// needs to be and not a lot larger. And it can be initialized more cleanly from Rust. -#[repr(C)] -pub(crate) union SocketAddrCRepr { - v4: c::sockaddr_in, - v6: c::sockaddr_in6, -} - -impl SocketAddrCRepr { - pub fn as_ptr(&self) -> *const c::sockaddr { - self as *const _ as *const c::sockaddr - } -} - -impl<'a> IntoInner<(SocketAddrCRepr, c::socklen_t)> for &'a SocketAddr { - fn into_inner(self) -> (SocketAddrCRepr, c::socklen_t) { - match *self { - SocketAddr::V4(ref a) => { - let sockaddr = SocketAddrCRepr { v4: a.into_inner() }; - (sockaddr, mem::size_of::() as c::socklen_t) - } - SocketAddr::V6(ref a) => { - let sockaddr = SocketAddrCRepr { v6: a.into_inner() }; - (sockaddr, mem::size_of::() as c::socklen_t) - } - } - } -} diff --git a/library/std/src/sys_common/wtf8.rs b/library/std/src/sys_common/wtf8.rs index 666942bb8a10f..6c60d901ee904 100644 --- a/library/std/src/sys_common/wtf8.rs +++ b/library/std/src/sys_common/wtf8.rs @@ -204,8 +204,8 @@ impl Wtf8Buf { /// /// Since WTF-8 is a superset of UTF-8, this always succeeds. #[inline] - pub fn from_str(str: &str) -> Wtf8Buf { - Wtf8Buf { bytes: <[_]>::to_vec(str.as_bytes()), is_known_utf8: true } + pub fn from_str(s: &str) -> Wtf8Buf { + Wtf8Buf { bytes: <[_]>::to_vec(s.as_bytes()), is_known_utf8: true } } pub fn clear(&mut self) { diff --git a/library/std/src/sys_common/wtf8/tests.rs b/library/std/src/sys_common/wtf8/tests.rs index bc06eaa2b8fa1..b57c99a8452a1 100644 --- a/library/std/src/sys_common/wtf8/tests.rs +++ b/library/std/src/sys_common/wtf8/tests.rs @@ -356,32 +356,32 @@ fn wtf8buf_from_iterator() { fn f(values: &[u32]) -> Wtf8Buf { values.iter().map(|&c| CodePoint::from_u32(c).unwrap()).collect::() } - assert_eq!(f(&[0x61, 0xE9, 0x20, 0x1F4A9]), Wtf8Buf { - bytes: b"a\xC3\xA9 \xF0\x9F\x92\xA9".to_vec(), - is_known_utf8: true - }); + assert_eq!( + f(&[0x61, 0xE9, 0x20, 0x1F4A9]), + Wtf8Buf { bytes: b"a\xC3\xA9 \xF0\x9F\x92\xA9".to_vec(), is_known_utf8: true } + ); assert_eq!(f(&[0xD83D, 0xDCA9]).bytes, b"\xF0\x9F\x92\xA9"); // Magic! - assert_eq!(f(&[0xD83D, 0x20, 0xDCA9]), Wtf8Buf { - bytes: b"\xED\xA0\xBD \xED\xB2\xA9".to_vec(), - is_known_utf8: false - }); - assert_eq!(f(&[0xD800, 0xDBFF]), Wtf8Buf { - bytes: b"\xED\xA0\x80\xED\xAF\xBF".to_vec(), - is_known_utf8: false - }); - assert_eq!(f(&[0xD800, 0xE000]), Wtf8Buf { - bytes: b"\xED\xA0\x80\xEE\x80\x80".to_vec(), - is_known_utf8: false - }); - assert_eq!(f(&[0xD7FF, 0xDC00]), Wtf8Buf { - bytes: b"\xED\x9F\xBF\xED\xB0\x80".to_vec(), - is_known_utf8: false - }); - assert_eq!(f(&[0x61, 0xDC00]), Wtf8Buf { - bytes: b"\x61\xED\xB0\x80".to_vec(), - is_known_utf8: false - }); + assert_eq!( + f(&[0xD83D, 0x20, 0xDCA9]), + Wtf8Buf { bytes: b"\xED\xA0\xBD \xED\xB2\xA9".to_vec(), is_known_utf8: false } + ); + assert_eq!( + f(&[0xD800, 0xDBFF]), + Wtf8Buf { bytes: b"\xED\xA0\x80\xED\xAF\xBF".to_vec(), is_known_utf8: false } + ); + assert_eq!( + f(&[0xD800, 0xE000]), + Wtf8Buf { bytes: b"\xED\xA0\x80\xEE\x80\x80".to_vec(), is_known_utf8: false } + ); + assert_eq!( + f(&[0xD7FF, 0xDC00]), + Wtf8Buf { bytes: b"\xED\x9F\xBF\xED\xB0\x80".to_vec(), is_known_utf8: false } + ); + assert_eq!( + f(&[0x61, 0xDC00]), + Wtf8Buf { bytes: b"\x61\xED\xB0\x80".to_vec(), is_known_utf8: false } + ); assert_eq!(f(&[0xDC00]), Wtf8Buf { bytes: b"\xED\xB0\x80".to_vec(), is_known_utf8: false }); } @@ -396,36 +396,36 @@ fn wtf8buf_extend() { string } - assert_eq!(e(&[0x61, 0xE9], &[0x20, 0x1F4A9]), Wtf8Buf { - bytes: b"a\xC3\xA9 \xF0\x9F\x92\xA9".to_vec(), - is_known_utf8: true - }); + assert_eq!( + e(&[0x61, 0xE9], &[0x20, 0x1F4A9]), + Wtf8Buf { bytes: b"a\xC3\xA9 \xF0\x9F\x92\xA9".to_vec(), is_known_utf8: true } + ); assert_eq!(e(&[0xD83D], &[0xDCA9]).bytes, b"\xF0\x9F\x92\xA9"); // Magic! - assert_eq!(e(&[0xD83D, 0x20], &[0xDCA9]), Wtf8Buf { - bytes: b"\xED\xA0\xBD \xED\xB2\xA9".to_vec(), - is_known_utf8: false - }); - assert_eq!(e(&[0xD800], &[0xDBFF]), Wtf8Buf { - bytes: b"\xED\xA0\x80\xED\xAF\xBF".to_vec(), - is_known_utf8: false - }); - assert_eq!(e(&[0xD800], &[0xE000]), Wtf8Buf { - bytes: b"\xED\xA0\x80\xEE\x80\x80".to_vec(), - is_known_utf8: false - }); - assert_eq!(e(&[0xD7FF], &[0xDC00]), Wtf8Buf { - bytes: b"\xED\x9F\xBF\xED\xB0\x80".to_vec(), - is_known_utf8: false - }); - assert_eq!(e(&[0x61], &[0xDC00]), Wtf8Buf { - bytes: b"\x61\xED\xB0\x80".to_vec(), - is_known_utf8: false - }); - assert_eq!(e(&[], &[0xDC00]), Wtf8Buf { - bytes: b"\xED\xB0\x80".to_vec(), - is_known_utf8: false - }); + assert_eq!( + e(&[0xD83D, 0x20], &[0xDCA9]), + Wtf8Buf { bytes: b"\xED\xA0\xBD \xED\xB2\xA9".to_vec(), is_known_utf8: false } + ); + assert_eq!( + e(&[0xD800], &[0xDBFF]), + Wtf8Buf { bytes: b"\xED\xA0\x80\xED\xAF\xBF".to_vec(), is_known_utf8: false } + ); + assert_eq!( + e(&[0xD800], &[0xE000]), + Wtf8Buf { bytes: b"\xED\xA0\x80\xEE\x80\x80".to_vec(), is_known_utf8: false } + ); + assert_eq!( + e(&[0xD7FF], &[0xDC00]), + Wtf8Buf { bytes: b"\xED\x9F\xBF\xED\xB0\x80".to_vec(), is_known_utf8: false } + ); + assert_eq!( + e(&[0x61], &[0xDC00]), + Wtf8Buf { bytes: b"\x61\xED\xB0\x80".to_vec(), is_known_utf8: false } + ); + assert_eq!( + e(&[], &[0xDC00]), + Wtf8Buf { bytes: b"\xED\xB0\x80".to_vec(), is_known_utf8: false } + ); } #[test] @@ -556,9 +556,10 @@ fn wtf8_encode_wide() { let mut string = Wtf8Buf::from_str("aé "); string.push(CodePoint::from_u32(0xD83D).unwrap()); string.push_char('💩'); - assert_eq!(string.encode_wide().collect::>(), vec![ - 0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9 - ]); + assert_eq!( + string.encode_wide().collect::>(), + vec![0x61, 0xE9, 0x20, 0xD83D, 0xD83D, 0xDCA9] + ); } #[test] diff --git a/library/std/src/test_helpers.rs b/library/std/src/test_helpers.rs new file mode 100644 index 0000000000000..7c20f38c863b6 --- /dev/null +++ b/library/std/src/test_helpers.rs @@ -0,0 +1,65 @@ +use rand::{RngCore, SeedableRng}; + +use crate::hash::{BuildHasher, Hash, Hasher, RandomState}; +use crate::panic::Location; +use crate::path::{Path, PathBuf}; +use crate::{env, fs, thread}; + +/// Test-only replacement for `rand::thread_rng()`, which is unusable for +/// us, as we want to allow running stdlib tests on tier-3 targets which may +/// not have `getrandom` support. +/// +/// Does a bit of a song and dance to ensure that the seed is different on +/// each call (as some tests sadly rely on this), but doesn't try that hard. +/// +/// This is duplicated in the `core`, `alloc` test suites (as well as +/// `std`'s integration tests), but figuring out a mechanism to share these +/// seems far more painful than copy-pasting a 7 line function a couple +/// times, given that even under a perma-unstable feature, I don't think we +/// want to expose types from `rand` from `std`. +#[track_caller] +pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { + let mut hasher = RandomState::new().build_hasher(); + Location::caller().hash(&mut hasher); + let hc64 = hasher.finish(); + let seed_vec = hc64.to_le_bytes().into_iter().chain(0u8..8).collect::>(); + let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap(); + SeedableRng::from_seed(seed) +} + +pub struct TempDir(PathBuf); + +impl TempDir { + pub fn join(&self, path: &str) -> PathBuf { + let TempDir(ref p) = *self; + p.join(path) + } + + pub fn path(&self) -> &Path { + let TempDir(ref p) = *self; + p + } +} + +impl Drop for TempDir { + fn drop(&mut self) { + // Gee, seeing how we're testing the fs module I sure hope that we + // at least implement this correctly! + let TempDir(ref p) = *self; + let result = fs::remove_dir_all(p); + // Avoid panicking while panicking as this causes the process to + // immediately abort, without displaying test results. + if !thread::panicking() { + result.unwrap(); + } + } +} + +#[track_caller] // for `test_rng` +pub fn tmpdir() -> TempDir { + let p = env::temp_dir(); + let mut r = test_rng(); + let ret = p.join(&format!("rust-{}", r.next_u32())); + fs::create_dir(&ret).unwrap(); + TempDir(ret) +} diff --git a/library/std/src/thread/current.rs b/library/std/src/thread/current.rs index 3d2c288b36085..414711298f047 100644 --- a/library/std/src/thread/current.rs +++ b/library/std/src/thread/current.rs @@ -15,7 +15,7 @@ local_pointer! { /// /// We store the thread ID so that it never gets destroyed during the lifetime /// of a thread, either using `#[thread_local]` or multiple `local_pointer!`s. -mod id { +pub(super) mod id { use super::*; cfg_if::cfg_if! { @@ -27,7 +27,7 @@ mod id { pub(super) const CHEAP: bool = true; - pub(super) fn get() -> Option { + pub(crate) fn get() -> Option { ID.get() } @@ -44,7 +44,7 @@ mod id { pub(super) const CHEAP: bool = false; - pub(super) fn get() -> Option { + pub(crate) fn get() -> Option { let id0 = ID0.get().addr() as u64; let id16 = ID16.get().addr() as u64; let id32 = ID32.get().addr() as u64; @@ -67,7 +67,7 @@ mod id { pub(super) const CHEAP: bool = false; - pub(super) fn get() -> Option { + pub(crate) fn get() -> Option { let id0 = ID0.get().addr() as u64; let id32 = ID32.get().addr() as u64; ThreadId::from_u64((id32 << 32) + id0) @@ -85,7 +85,7 @@ mod id { pub(super) const CHEAP: bool = true; - pub(super) fn get() -> Option { + pub(crate) fn get() -> Option { let id = ID.get().addr() as u64; ThreadId::from_u64(id) } @@ -112,7 +112,7 @@ mod id { /// Tries to set the thread handle for the current thread. Fails if a handle was /// already set or if the thread ID of `thread` would change an already-set ID. -pub(crate) fn set_current(thread: Thread) -> Result<(), Thread> { +pub(super) fn set_current(thread: Thread) -> Result<(), Thread> { if CURRENT.get() != NONE { return Err(thread); } @@ -140,28 +140,31 @@ pub(crate) fn current_id() -> ThreadId { // to retrieve it from the current thread handle, which will only take one // TLS access. if !id::CHEAP { - let current = CURRENT.get(); - if current > DESTROYED { - unsafe { - let current = ManuallyDrop::new(Thread::from_raw(current)); - return current.id(); - } + if let Some(id) = try_with_current(|t| t.map(|t| t.id())) { + return id; } } id::get_or_init() } -/// Gets a handle to the thread that invokes it, if the handle has been initialized. -pub(crate) fn try_current() -> Option { +/// Gets a reference to the handle of the thread that invokes it, if the handle +/// has been initialized. +pub(super) fn try_with_current(f: F) -> R +where + F: FnOnce(Option<&Thread>) -> R, +{ let current = CURRENT.get(); if current > DESTROYED { + // SAFETY: `Arc` does not contain interior mutability, so it does not + // matter that the address of the handle might be different depending + // on where this is called. unsafe { let current = ManuallyDrop::new(Thread::from_raw(current)); - Some((*current).clone()) + f(Some(¤t)) } } else { - None + f(None) } } @@ -176,7 +179,7 @@ pub(crate) fn current_or_unnamed() -> Thread { (*current).clone() } } else if current == DESTROYED { - Thread::new_unnamed(id::get_or_init()) + Thread::new(id::get_or_init(), None) } else { init_current(current) } @@ -221,7 +224,7 @@ fn init_current(current: *mut ()) -> Thread { CURRENT.set(BUSY); // If the thread ID was initialized already, use it. let id = id::get_or_init(); - let thread = Thread::new_unnamed(id); + let thread = Thread::new(id, None); // Make sure that `crate::rt::thread_cleanup` will be run, which will // call `drop_current`. diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index 2313f4b5beb11..ca04aa4ada497 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -2,12 +2,6 @@ #![unstable(feature = "thread_local_internals", issue = "none")] -#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] -mod tests; - -#[cfg(test)] -mod dynamic_tests; - use crate::cell::{Cell, RefCell}; use crate::error::Error; use crate::fmt; @@ -230,6 +224,14 @@ impl fmt::Display for AccessError { #[stable(feature = "thread_local_try_with", since = "1.26.0")] impl Error for AccessError {} +// This ensures the panicking code is outlined from `with` for `LocalKey`. +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] +#[track_caller] +#[cold] +fn panic_access_error(err: AccessError) -> ! { + panic!("cannot access a Thread Local Storage value during or after destruction: {err:?}") +} + impl LocalKey { #[doc(hidden)] #[unstable( @@ -269,10 +271,10 @@ impl LocalKey { where F: FnOnce(&T) -> R, { - self.try_with(f).expect( - "cannot access a Thread Local Storage value \ - during or after destruction", - ) + match self.try_with(f) { + Ok(r) => r, + Err(err) => panic_access_error(err), + } } /// Acquires a reference to the value in this TLS key. @@ -327,10 +329,10 @@ impl LocalKey { let mut init = Some(init); let reference = unsafe { - (self.inner)(Some(&mut init)).as_ref().expect( - "cannot access a Thread Local Storage value \ - during or after destruction", - ) + match (self.inner)(Some(&mut init)).as_ref() { + Some(r) => r, + None => panic_access_error(AccessError), + } }; f(init, reference) diff --git a/library/std/src/thread/local/tests.rs b/library/std/src/thread/local/tests.rs deleted file mode 100644 index 9d4f52a09218e..0000000000000 --- a/library/std/src/thread/local/tests.rs +++ /dev/null @@ -1,376 +0,0 @@ -use crate::cell::{Cell, UnsafeCell}; -use crate::sync::atomic::{AtomicU8, Ordering}; -use crate::sync::{Arc, Condvar, Mutex}; -use crate::thread::{self, Builder, LocalKey}; -use crate::thread_local; - -#[derive(Clone, Default)] -struct Signal(Arc<(Mutex, Condvar)>); - -impl Signal { - fn notify(&self) { - let (set, cvar) = &*self.0; - *set.lock().unwrap() = true; - cvar.notify_one(); - } - - fn wait(&self) { - let (set, cvar) = &*self.0; - let mut set = set.lock().unwrap(); - while !*set { - set = cvar.wait(set).unwrap(); - } - } -} - -struct NotifyOnDrop(Signal); - -impl Drop for NotifyOnDrop { - fn drop(&mut self) { - let NotifyOnDrop(ref f) = *self; - f.notify(); - } -} - -#[test] -fn smoke_no_dtor() { - thread_local!(static FOO: Cell = Cell::new(1)); - run(&FOO); - thread_local!(static FOO2: Cell = const { Cell::new(1) }); - run(&FOO2); - - fn run(key: &'static LocalKey>) { - key.with(|f| { - assert_eq!(f.get(), 1); - f.set(2); - }); - let t = thread::spawn(move || { - key.with(|f| { - assert_eq!(f.get(), 1); - }); - }); - t.join().unwrap(); - - key.with(|f| { - assert_eq!(f.get(), 2); - }); - } -} - -#[test] -fn states() { - struct Foo(&'static LocalKey); - impl Drop for Foo { - fn drop(&mut self) { - assert!(self.0.try_with(|_| ()).is_err()); - } - } - - thread_local!(static FOO: Foo = Foo(&FOO)); - run(&FOO); - thread_local!(static FOO2: Foo = const { Foo(&FOO2) }); - run(&FOO2); - - fn run(foo: &'static LocalKey) { - thread::spawn(move || { - assert!(foo.try_with(|_| ()).is_ok()); - }) - .join() - .unwrap(); - } -} - -#[test] -fn smoke_dtor() { - thread_local!(static FOO: UnsafeCell> = UnsafeCell::new(None)); - run(&FOO); - thread_local!(static FOO2: UnsafeCell> = const { UnsafeCell::new(None) }); - run(&FOO2); - - fn run(key: &'static LocalKey>>) { - let signal = Signal::default(); - let signal2 = signal.clone(); - let t = thread::spawn(move || unsafe { - let mut signal = Some(signal2); - key.with(|f| { - *f.get() = Some(NotifyOnDrop(signal.take().unwrap())); - }); - }); - signal.wait(); - t.join().unwrap(); - } -} - -#[test] -fn circular() { - // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint - #![allow(static_mut_refs)] - - struct S1(&'static LocalKey>>, &'static LocalKey>>); - struct S2(&'static LocalKey>>, &'static LocalKey>>); - thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); - thread_local!(static K2: UnsafeCell> = UnsafeCell::new(None)); - thread_local!(static K3: UnsafeCell> = const { UnsafeCell::new(None) }); - thread_local!(static K4: UnsafeCell> = const { UnsafeCell::new(None) }); - static mut HITS: usize = 0; - - impl Drop for S1 { - fn drop(&mut self) { - unsafe { - HITS += 1; - if self.1.try_with(|_| ()).is_err() { - assert_eq!(HITS, 3); - } else { - if HITS == 1 { - self.1.with(|s| *s.get() = Some(S2(self.0, self.1))); - } else { - assert_eq!(HITS, 3); - } - } - } - } - } - impl Drop for S2 { - fn drop(&mut self) { - unsafe { - HITS += 1; - assert!(self.0.try_with(|_| ()).is_ok()); - assert_eq!(HITS, 2); - self.0.with(|s| *s.get() = Some(S1(self.0, self.1))); - } - } - } - - thread::spawn(move || { - drop(S1(&K1, &K2)); - }) - .join() - .unwrap(); - - unsafe { - HITS = 0; - } - - thread::spawn(move || { - drop(S1(&K3, &K4)); - }) - .join() - .unwrap(); -} - -#[test] -fn self_referential() { - struct S1(&'static LocalKey>>); - - thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); - thread_local!(static K2: UnsafeCell> = const { UnsafeCell::new(None) }); - - impl Drop for S1 { - fn drop(&mut self) { - assert!(self.0.try_with(|_| ()).is_err()); - } - } - - thread::spawn(move || unsafe { - K1.with(|s| *s.get() = Some(S1(&K1))); - }) - .join() - .unwrap(); - - thread::spawn(move || unsafe { - K2.with(|s| *s.get() = Some(S1(&K2))); - }) - .join() - .unwrap(); -} - -// Note that this test will deadlock if TLS destructors aren't run (this -// requires the destructor to be run to pass the test). -#[test] -fn dtors_in_dtors_in_dtors() { - struct S1(Signal); - thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); - thread_local!(static K2: UnsafeCell> = UnsafeCell::new(None)); - - impl Drop for S1 { - fn drop(&mut self) { - let S1(ref signal) = *self; - unsafe { - let _ = K2.try_with(|s| *s.get() = Some(NotifyOnDrop(signal.clone()))); - } - } - } - - let signal = Signal::default(); - let signal2 = signal.clone(); - let _t = thread::spawn(move || unsafe { - let mut signal = Some(signal2); - K1.with(|s| *s.get() = Some(S1(signal.take().unwrap()))); - }); - signal.wait(); -} - -#[test] -fn dtors_in_dtors_in_dtors_const_init() { - struct S1(Signal); - thread_local!(static K1: UnsafeCell> = const { UnsafeCell::new(None) }); - thread_local!(static K2: UnsafeCell> = const { UnsafeCell::new(None) }); - - impl Drop for S1 { - fn drop(&mut self) { - let S1(ref signal) = *self; - unsafe { - let _ = K2.try_with(|s| *s.get() = Some(NotifyOnDrop(signal.clone()))); - } - } - } - - let signal = Signal::default(); - let signal2 = signal.clone(); - let _t = thread::spawn(move || unsafe { - let mut signal = Some(signal2); - K1.with(|s| *s.get() = Some(S1(signal.take().unwrap()))); - }); - signal.wait(); -} - -// This test tests that TLS destructors have run before the thread joins. The -// test has no false positives (meaning: if the test fails, there's actually -// an ordering problem). It may have false negatives, where the test passes but -// join is not guaranteed to be after the TLS destructors. However, false -// negatives should be exceedingly rare due to judicious use of -// thread::yield_now and running the test several times. -#[test] -fn join_orders_after_tls_destructors() { - // We emulate a synchronous MPSC rendezvous channel using only atomics and - // thread::yield_now. We can't use std::mpsc as the implementation itself - // may rely on thread locals. - // - // The basic state machine for an SPSC rendezvous channel is: - // FRESH -> THREAD1_WAITING -> MAIN_THREAD_RENDEZVOUS - // where the first transition is done by the “receiving” thread and the 2nd - // transition is done by the “sending” thread. - // - // We add an additional state `THREAD2_LAUNCHED` between `FRESH` and - // `THREAD1_WAITING` to block until all threads are actually running. - // - // A thread that joins on the “receiving” thread completion should never - // observe the channel in the `THREAD1_WAITING` state. If this does occur, - // we switch to the “poison” state `THREAD2_JOINED` and panic all around. - // (This is equivalent to “sending” from an alternate producer thread.) - // - // Relaxed memory ordering is fine because and spawn()/join() already provide all the - // synchronization we need here. - const FRESH: u8 = 0; - const THREAD2_LAUNCHED: u8 = 1; - const THREAD1_WAITING: u8 = 2; - const MAIN_THREAD_RENDEZVOUS: u8 = 3; - const THREAD2_JOINED: u8 = 4; - static SYNC_STATE: AtomicU8 = AtomicU8::new(FRESH); - - for _ in 0..10 { - SYNC_STATE.store(FRESH, Ordering::Relaxed); - - let jh = thread::Builder::new() - .name("thread1".into()) - .spawn(move || { - struct TlDrop; - - impl Drop for TlDrop { - fn drop(&mut self) { - let mut sync_state = SYNC_STATE.swap(THREAD1_WAITING, Ordering::Relaxed); - loop { - match sync_state { - THREAD2_LAUNCHED | THREAD1_WAITING => thread::yield_now(), - MAIN_THREAD_RENDEZVOUS => break, - THREAD2_JOINED => panic!( - "Thread 1 still running after thread 2 joined on thread 1" - ), - v => unreachable!("sync state: {}", v), - } - sync_state = SYNC_STATE.load(Ordering::Relaxed); - } - } - } - - thread_local! { - static TL_DROP: TlDrop = TlDrop; - } - - TL_DROP.with(|_| {}); - - loop { - match SYNC_STATE.load(Ordering::Relaxed) { - FRESH => thread::yield_now(), - THREAD2_LAUNCHED => break, - v => unreachable!("sync state: {}", v), - } - } - }) - .unwrap(); - - let jh2 = thread::Builder::new() - .name("thread2".into()) - .spawn(move || { - assert_eq!(SYNC_STATE.swap(THREAD2_LAUNCHED, Ordering::Relaxed), FRESH); - jh.join().unwrap(); - match SYNC_STATE.swap(THREAD2_JOINED, Ordering::Relaxed) { - MAIN_THREAD_RENDEZVOUS => return, - THREAD2_LAUNCHED | THREAD1_WAITING => { - panic!("Thread 2 running after thread 1 join before main thread rendezvous") - } - v => unreachable!("sync state: {:?}", v), - } - }) - .unwrap(); - - loop { - match SYNC_STATE.compare_exchange( - THREAD1_WAITING, - MAIN_THREAD_RENDEZVOUS, - Ordering::Relaxed, - Ordering::Relaxed, - ) { - Ok(_) => break, - Err(FRESH) => thread::yield_now(), - Err(THREAD2_LAUNCHED) => thread::yield_now(), - Err(THREAD2_JOINED) => { - panic!("Main thread rendezvous after thread 2 joined thread 1") - } - v => unreachable!("sync state: {:?}", v), - } - } - jh2.join().unwrap(); - } -} - -// Test that thread::current is still available in TLS destructors. -#[test] -fn thread_current_in_dtor() { - // Go through one round of TLS destruction first. - struct Defer; - impl Drop for Defer { - fn drop(&mut self) { - RETRIEVE.with(|_| {}); - } - } - - struct RetrieveName; - impl Drop for RetrieveName { - fn drop(&mut self) { - *NAME.lock().unwrap() = Some(thread::current().name().unwrap().to_owned()); - } - } - - static NAME: Mutex> = Mutex::new(None); - - thread_local! { - static DEFER: Defer = const { Defer }; - static RETRIEVE: RetrieveName = const { RetrieveName }; - } - - Builder::new().name("test".to_owned()).spawn(|| DEFER.with(|_| {})).unwrap().join().unwrap(); - let name = NAME.lock().unwrap(); - let name = name.as_ref().unwrap(); - assert_eq!(name, "test"); -} diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 85ee369ca2b66..59b395336f2e3 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -158,12 +158,9 @@ #[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))] mod tests; -use core::cell::SyncUnsafeCell; -use core::ffi::CStr; -use core::mem::MaybeUninit; - use crate::any::Any; use crate::cell::UnsafeCell; +use crate::ffi::CStr; use crate::marker::PhantomData; use crate::mem::{self, ManuallyDrop, forget}; use crate::num::NonZero; @@ -186,7 +183,8 @@ mod current; #[stable(feature = "rust1", since = "1.0.0")] pub use current::current; -pub(crate) use current::{current_id, current_or_unnamed, drop_current, set_current, try_current}; +pub(crate) use current::{current_id, current_or_unnamed, drop_current}; +use current::{set_current, try_with_current}; mod spawnhook; @@ -501,10 +499,7 @@ impl Builder { }); let id = ThreadId::new(); - let my_thread = match name { - Some(name) => Thread::new(id, name.into()), - None => Thread::new_unnamed(id), - }; + let my_thread = Thread::new(id, name); let hooks = if no_hooks { spawnhook::ChildSpawnHooks::default() @@ -1235,7 +1230,7 @@ impl ThreadId { } } - #[cfg(not(target_thread_local))] + #[cfg(any(not(target_thread_local), target_has_atomic = "64"))] fn from_u64(v: u64) -> Option { NonZero::new(v).map(ThreadId) } @@ -1261,29 +1256,14 @@ impl ThreadId { // This module ensures private fields are kept private, which is necessary to enforce the safety requirements. mod thread_name_string { - use core::str; - use crate::ffi::{CStr, CString}; + use crate::str; /// Like a `String` it's guaranteed UTF-8 and like a `CString` it's null terminated. pub(crate) struct ThreadNameString { inner: CString, } - impl ThreadNameString { - pub fn as_str(&self) -> &str { - // SAFETY: `self.inner` is only initialised via `String`, which upholds the validity invariant of `str`. - unsafe { str::from_utf8_unchecked(self.inner.to_bytes()) } - } - } - - impl core::ops::Deref for ThreadNameString { - type Target = CStr; - fn deref(&self) -> &CStr { - &self.inner - } - } - impl From for ThreadNameString { fn from(s: String) -> Self { Self { @@ -1291,82 +1271,124 @@ mod thread_name_string { } } } -} -pub(crate) use thread_name_string::ThreadNameString; - -static MAIN_THREAD_INFO: SyncUnsafeCell<(MaybeUninit, MaybeUninit)> = - SyncUnsafeCell::new((MaybeUninit::uninit(), MaybeUninit::uninit())); - -/// The internal representation of a `Thread` that is not the main thread. -struct OtherInner { - name: Option, - id: ThreadId, - parker: Parker, -} - -/// The internal representation of a `Thread` handle. -#[derive(Clone)] -enum Inner { - /// Represents the main thread. May only be constructed by Thread::new_main. - Main(&'static (ThreadId, Parker)), - /// Represents any other thread. - Other(Pin>), -} - -impl Inner { - fn id(&self) -> ThreadId { - match self { - Self::Main((thread_id, _)) => *thread_id, - Self::Other(other) => other.id, - } - } - fn cname(&self) -> Option<&CStr> { - match self { - Self::Main(_) => Some(c"main"), - Self::Other(other) => other.name.as_deref(), + impl ThreadNameString { + pub fn as_cstr(&self) -> &CStr { + &self.inner } - } - fn name(&self) -> Option<&str> { - match self { - Self::Main(_) => Some("main"), - Self::Other(other) => other.name.as_ref().map(ThreadNameString::as_str), + pub fn as_str(&self) -> &str { + // SAFETY: `ThreadNameString` is guaranteed to be UTF-8. + unsafe { str::from_utf8_unchecked(self.inner.to_bytes()) } } } +} - fn into_raw(self) -> *const () { - match self { - // Just return the pointer to `MAIN_THREAD_INFO`. - Self::Main(ptr) => crate::ptr::from_ref(ptr).cast(), - Self::Other(arc) => { - // Safety: We only expose an opaque pointer, which maintains the `Pin` invariant. - let inner = unsafe { Pin::into_inner_unchecked(arc) }; - Arc::into_raw(inner) as *const () +use thread_name_string::ThreadNameString; + +/// Store the ID of the main thread. +/// +/// The thread handle for the main thread is created lazily, and this might even +/// happen pre-main. Since not every platform has a way to identify the main +/// thread when that happens – macOS's `pthread_main_np` function being a notable +/// exception – we cannot assign it the right name right then. Instead, in our +/// runtime startup code, we remember the thread ID of the main thread (through +/// this modules `set` function) and use it to identify the main thread from then +/// on. This works reliably and has the additional advantage that we can report +/// the right thread name on main even after the thread handle has been destroyed. +/// Note however that this also means that the name reported in pre-main functions +/// will be incorrect, but that's just something we have to live with. +pub(crate) mod main_thread { + cfg_if::cfg_if! { + if #[cfg(target_has_atomic = "64")] { + use super::ThreadId; + use crate::sync::atomic::AtomicU64; + use crate::sync::atomic::Ordering::Relaxed; + + static MAIN: AtomicU64 = AtomicU64::new(0); + + pub(super) fn get() -> Option { + ThreadId::from_u64(MAIN.load(Relaxed)) } - } - } - /// # Safety - /// - /// See [`Thread::from_raw`]. - unsafe fn from_raw(ptr: *const ()) -> Self { - // If the pointer is to `MAIN_THREAD_INFO`, we know it is the `Main` variant. - if crate::ptr::eq(ptr.cast(), &MAIN_THREAD_INFO) { - Self::Main(unsafe { &*ptr.cast() }) + /// # Safety + /// May only be called once. + pub(crate) unsafe fn set(id: ThreadId) { + MAIN.store(id.as_u64().get(), Relaxed) + } } else { - // Safety: Upheld by caller - Self::Other(unsafe { Pin::new_unchecked(Arc::from_raw(ptr as *const OtherInner)) }) + use super::ThreadId; + use crate::mem::MaybeUninit; + use crate::sync::atomic::AtomicBool; + use crate::sync::atomic::Ordering::{Acquire, Release}; + + static INIT: AtomicBool = AtomicBool::new(false); + static mut MAIN: MaybeUninit = MaybeUninit::uninit(); + + pub(super) fn get() -> Option { + if INIT.load(Acquire) { + Some(unsafe { MAIN.assume_init() }) + } else { + None + } + } + + /// # Safety + /// May only be called once. + pub(crate) unsafe fn set(id: ThreadId) { + unsafe { MAIN = MaybeUninit::new(id) }; + INIT.store(true, Release); + } } } +} - fn parker(&self) -> Pin<&Parker> { - match self { - Self::Main((_, parker_ref)) => Pin::static_ref(parker_ref), - Self::Other(inner) => unsafe { - Pin::map_unchecked(inner.as_ref(), |inner| &inner.parker) - }, +/// Run a function with the current thread's name. +/// +/// Modulo thread local accesses, this function is safe to call from signal +/// handlers and in similar circumstances where allocations are not possible. +pub(crate) fn with_current_name(f: F) -> R +where + F: FnOnce(Option<&str>) -> R, +{ + try_with_current(|thread| { + if let Some(thread) = thread { + // If there is a current thread handle, try to use the name stored + // there. + if let Some(name) = &thread.inner.name { + return f(Some(name.as_str())); + } else if Some(thread.inner.id) == main_thread::get() { + // The main thread doesn't store its name in the handle, we must + // identify it through its ID. Since we already have the `Thread`, + // we can retrieve the ID from it instead of going through another + // thread local. + return f(Some("main")); + } + } else if let Some(main) = main_thread::get() + && let Some(id) = current::id::get() + && id == main + { + // The main thread doesn't always have a thread handle, we must + // identify it through its ID instead. The checks are ordered so + // that the current ID is only loaded if it is actually needed, + // since loading it from TLS might need multiple expensive accesses. + return f(Some("main")); } + + f(None) + }) +} + +/// The internal representation of a `Thread` handle +struct Inner { + name: Option, + id: ThreadId, + parker: Parker, +} + +impl Inner { + fn parker(self: Pin<&Self>) -> Pin<&Parker> { + unsafe { Pin::map_unchecked(self, |inner| &inner.parker) } } } @@ -1390,47 +1412,21 @@ impl Inner { /// docs of [`Builder`] and [`spawn`] for more details. /// /// [`thread::current`]: current::current -pub struct Thread(Inner); +pub struct Thread { + inner: Pin>, +} impl Thread { - /// Used only internally to construct a thread object without spawning. - pub(crate) fn new(id: ThreadId, name: String) -> Thread { - Self::new_inner(id, Some(ThreadNameString::from(name))) - } + pub(crate) fn new(id: ThreadId, name: Option) -> Thread { + let name = name.map(ThreadNameString::from); - pub(crate) fn new_unnamed(id: ThreadId) -> Thread { - Self::new_inner(id, None) - } - - /// Used in runtime to construct main thread - /// - /// # Safety - /// - /// This must only ever be called once, and must be called on the main thread. - pub(crate) unsafe fn new_main(thread_id: ThreadId) -> Thread { - // Safety: As this is only called once and on the main thread, nothing else is accessing MAIN_THREAD_INFO - // as the only other read occurs in `main_thread_info` *after* the main thread has been constructed, - // and this function is the only one that constructs the main thread. - // - // Pre-main thread spawning cannot hit this either, as the caller promises that this is only called on the main thread. - let main_thread_info = unsafe { &mut *MAIN_THREAD_INFO.get() }; - - unsafe { Parker::new_in_place((&raw mut main_thread_info.1).cast()) }; - main_thread_info.0.write(thread_id); - - // Store a `'static` ref to the initialised ThreadId and Parker, - // to avoid having to repeatedly prove initialisation. - Self(Inner::Main(unsafe { &*MAIN_THREAD_INFO.get().cast() })) - } - - fn new_inner(id: ThreadId, name: Option) -> Thread { // We have to use `unsafe` here to construct the `Parker` in-place, // which is required for the UNIX implementation. // // SAFETY: We pin the Arc immediately after creation, so its address never // changes. let inner = unsafe { - let mut arc = Arc::::new_uninit(); + let mut arc = Arc::::new_uninit(); let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr(); (&raw mut (*ptr).name).write(name); (&raw mut (*ptr).id).write(id); @@ -1438,7 +1434,7 @@ impl Thread { Pin::new_unchecked(arc.assume_init()) }; - Self(Inner::Other(inner)) + Thread { inner } } /// Like the public [`park`], but callable on any handle. This is used to @@ -1447,7 +1443,7 @@ impl Thread { /// # Safety /// May only be called from the thread to which this handle belongs. pub(crate) unsafe fn park(&self) { - unsafe { self.0.parker().park() } + unsafe { self.inner.as_ref().parker().park() } } /// Like the public [`park_timeout`], but callable on any handle. This is @@ -1456,7 +1452,7 @@ impl Thread { /// # Safety /// May only be called from the thread to which this handle belongs. pub(crate) unsafe fn park_timeout(&self, dur: Duration) { - unsafe { self.0.parker().park_timeout(dur) } + unsafe { self.inner.as_ref().parker().park_timeout(dur) } } /// Atomically makes the handle's token available if it is not already. @@ -1492,7 +1488,7 @@ impl Thread { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn unpark(&self) { - self.0.parker().unpark(); + self.inner.as_ref().parker().unpark(); } /// Gets the thread's unique identifier. @@ -1512,7 +1508,7 @@ impl Thread { #[stable(feature = "thread_id", since = "1.19.0")] #[must_use] pub fn id(&self) -> ThreadId { - self.0.id() + self.inner.id } /// Gets the thread's name. @@ -1555,11 +1551,13 @@ impl Thread { #[stable(feature = "rust1", since = "1.0.0")] #[must_use] pub fn name(&self) -> Option<&str> { - self.0.name() - } - - fn cname(&self) -> Option<&CStr> { - self.0.cname() + if let Some(name) = &self.inner.name { + Some(name.as_str()) + } else if main_thread::get() == Some(self.inner.id) { + Some("main") + } else { + None + } } /// Consumes the `Thread`, returning a raw pointer. @@ -1583,7 +1581,9 @@ impl Thread { /// ``` #[unstable(feature = "thread_raw", issue = "97523")] pub fn into_raw(self) -> *const () { - self.0.into_raw() + // Safety: We only expose an opaque pointer, which maintains the `Pin` invariant. + let inner = unsafe { Pin::into_inner_unchecked(self.inner) }; + Arc::into_raw(inner) as *const () } /// Constructs a `Thread` from a raw pointer. @@ -1605,7 +1605,17 @@ impl Thread { #[unstable(feature = "thread_raw", issue = "97523")] pub unsafe fn from_raw(ptr: *const ()) -> Thread { // Safety: Upheld by caller. - unsafe { Thread(Inner::from_raw(ptr)) } + unsafe { Thread { inner: Pin::new_unchecked(Arc::from_raw(ptr as *const Inner)) } } + } + + fn cname(&self) -> Option<&CStr> { + if let Some(name) = &self.inner.name { + Some(name.as_cstr()) + } else if main_thread::get() == Some(self.inner.id) { + Some(c"main") + } else { + None + } } } diff --git a/library/std/src/time.rs b/library/std/src/time.rs index 9f4f8a0d0880c..88b3e9e0ceba0 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -31,9 +31,6 @@ #![stable(feature = "time", since = "1.3.0")] -#[cfg(test)] -mod tests; - #[stable(feature = "time", since = "1.3.0")] pub use core::time::Duration; #[stable(feature = "duration_checked_float", since = "1.66.0")] diff --git a/library/std/src/time/tests.rs b/library/std/src/time/tests.rs deleted file mode 100644 index e88f2d5e80676..0000000000000 --- a/library/std/src/time/tests.rs +++ /dev/null @@ -1,274 +0,0 @@ -use core::fmt::Debug; - -#[cfg(not(target_arch = "wasm32"))] -use test::{Bencher, black_box}; - -use super::{Duration, Instant, SystemTime, UNIX_EPOCH}; - -macro_rules! assert_almost_eq { - ($a:expr, $b:expr) => {{ - let (a, b) = ($a, $b); - if a != b { - let (a, b) = if a > b { (a, b) } else { (b, a) }; - assert!(a - Duration::from_micros(1) <= b, "{:?} is not almost equal to {:?}", a, b); - } - }}; -} - -#[test] -fn instant_monotonic() { - let a = Instant::now(); - loop { - let b = Instant::now(); - assert!(b >= a); - if b > a { - break; - } - } -} - -#[test] -#[cfg(not(target_arch = "wasm32"))] -fn instant_monotonic_concurrent() -> crate::thread::Result<()> { - let threads: Vec<_> = (0..8) - .map(|_| { - crate::thread::spawn(|| { - let mut old = Instant::now(); - let count = if cfg!(miri) { 1_000 } else { 5_000_000 }; - for _ in 0..count { - let new = Instant::now(); - assert!(new >= old); - old = new; - } - }) - }) - .collect(); - for t in threads { - t.join()?; - } - Ok(()) -} - -#[test] -fn instant_elapsed() { - let a = Instant::now(); - let _ = a.elapsed(); -} - -#[test] -fn instant_math() { - let a = Instant::now(); - let b = Instant::now(); - println!("a: {a:?}"); - println!("b: {b:?}"); - let dur = b.duration_since(a); - println!("dur: {dur:?}"); - assert_almost_eq!(b - dur, a); - assert_almost_eq!(a + dur, b); - - let second = Duration::SECOND; - assert_almost_eq!(a - second + second, a); - assert_almost_eq!(a.checked_sub(second).unwrap().checked_add(second).unwrap(), a); - - // checked_add_duration will not panic on overflow - let mut maybe_t = Some(Instant::now()); - let max_duration = Duration::from_secs(u64::MAX); - // in case `Instant` can store `>= now + max_duration`. - for _ in 0..2 { - maybe_t = maybe_t.and_then(|t| t.checked_add(max_duration)); - } - assert_eq!(maybe_t, None); - - // checked_add_duration calculates the right time and will work for another year - let year = Duration::from_secs(60 * 60 * 24 * 365); - assert_eq!(a + year, a.checked_add(year).unwrap()); -} - -#[test] -fn instant_math_is_associative() { - let now = Instant::now(); - let offset = Duration::from_millis(5); - // Changing the order of instant math shouldn't change the results, - // especially when the expression reduces to X + identity. - assert_eq!((now + offset) - now, (now - now) + offset); - - // On any platform, `Instant` should have the same resolution as `Duration` (e.g. 1 nanosecond) - // or better. Otherwise, math will be non-associative (see #91417). - let now = Instant::now(); - let provided_offset = Duration::from_nanos(1); - let later = now + provided_offset; - let measured_offset = later - now; - assert_eq!(measured_offset, provided_offset); -} - -#[test] -fn instant_duration_since_saturates() { - let a = Instant::now(); - assert_eq!((a - Duration::SECOND).duration_since(a), Duration::ZERO); -} - -#[test] -fn instant_checked_duration_since_nopanic() { - let now = Instant::now(); - let earlier = now - Duration::SECOND; - let later = now + Duration::SECOND; - assert_eq!(earlier.checked_duration_since(now), None); - assert_eq!(later.checked_duration_since(now), Some(Duration::SECOND)); - assert_eq!(now.checked_duration_since(now), Some(Duration::ZERO)); -} - -#[test] -fn instant_saturating_duration_since_nopanic() { - let a = Instant::now(); - #[allow(deprecated, deprecated_in_future)] - let ret = (a - Duration::SECOND).saturating_duration_since(a); - assert_eq!(ret, Duration::ZERO); -} - -#[test] -fn system_time_math() { - let a = SystemTime::now(); - let b = SystemTime::now(); - match b.duration_since(a) { - Ok(Duration::ZERO) => { - assert_almost_eq!(a, b); - } - Ok(dur) => { - assert!(b > a); - assert_almost_eq!(b - dur, a); - assert_almost_eq!(a + dur, b); - } - Err(dur) => { - let dur = dur.duration(); - assert!(a > b); - assert_almost_eq!(b + dur, a); - assert_almost_eq!(a - dur, b); - } - } - - let second = Duration::SECOND; - assert_almost_eq!(a.duration_since(a - second).unwrap(), second); - assert_almost_eq!(a.duration_since(a + second).unwrap_err().duration(), second); - - assert_almost_eq!(a - second + second, a); - assert_almost_eq!(a.checked_sub(second).unwrap().checked_add(second).unwrap(), a); - - let one_second_from_epoch = UNIX_EPOCH + Duration::SECOND; - let one_second_from_epoch2 = - UNIX_EPOCH + Duration::from_millis(500) + Duration::from_millis(500); - assert_eq!(one_second_from_epoch, one_second_from_epoch2); - - // checked_add_duration will not panic on overflow - let mut maybe_t = Some(SystemTime::UNIX_EPOCH); - let max_duration = Duration::from_secs(u64::MAX); - // in case `SystemTime` can store `>= UNIX_EPOCH + max_duration`. - for _ in 0..2 { - maybe_t = maybe_t.and_then(|t| t.checked_add(max_duration)); - } - assert_eq!(maybe_t, None); - - // checked_add_duration calculates the right time and will work for another year - let year = Duration::from_secs(60 * 60 * 24 * 365); - assert_eq!(a + year, a.checked_add(year).unwrap()); -} - -#[test] -fn system_time_elapsed() { - let a = SystemTime::now(); - drop(a.elapsed()); -} - -#[test] -fn since_epoch() { - let ts = SystemTime::now(); - let a = ts.duration_since(UNIX_EPOCH + Duration::SECOND).unwrap(); - let b = ts.duration_since(UNIX_EPOCH).unwrap(); - assert!(b > a); - assert_eq!(b - a, Duration::SECOND); - - let thirty_years = Duration::SECOND * 60 * 60 * 24 * 365 * 30; - - // Right now for CI this test is run in an emulator, and apparently the - // aarch64 emulator's sense of time is that we're still living in the - // 70s. This is also true for riscv (also qemu) - // - // Otherwise let's assume that we're all running computers later than - // 2000. - if !cfg!(target_arch = "aarch64") && !cfg!(target_arch = "riscv64") { - assert!(a > thirty_years); - } - - // let's assume that we're all running computers earlier than 2090. - // Should give us ~70 years to fix this! - let hundred_twenty_years = thirty_years * 4; - assert!(a < hundred_twenty_years); -} - -#[test] -fn big_math() { - // Check that the same result occurs when adding/subtracting each duration one at a time as when - // adding/subtracting them all at once. - #[track_caller] - fn check(start: Option, op: impl Fn(&T, Duration) -> Option) { - const DURATIONS: [Duration; 2] = - [Duration::from_secs(i64::MAX as _), Duration::from_secs(50)]; - if let Some(start) = start { - assert_eq!( - op(&start, DURATIONS.into_iter().sum()), - DURATIONS.into_iter().try_fold(start, |t, d| op(&t, d)) - ) - } - } - - check(SystemTime::UNIX_EPOCH.checked_sub(Duration::from_secs(100)), SystemTime::checked_add); - check(SystemTime::UNIX_EPOCH.checked_add(Duration::from_secs(100)), SystemTime::checked_sub); - - let instant = Instant::now(); - check(instant.checked_sub(Duration::from_secs(100)), Instant::checked_add); - check(instant.checked_sub(Duration::from_secs(i64::MAX as _)), Instant::checked_add); - check(instant.checked_add(Duration::from_secs(100)), Instant::checked_sub); - check(instant.checked_add(Duration::from_secs(i64::MAX as _)), Instant::checked_sub); -} - -macro_rules! bench_instant_threaded { - ($bench_name:ident, $thread_count:expr) => { - #[bench] - #[cfg(not(target_arch = "wasm32"))] - fn $bench_name(b: &mut Bencher) -> crate::thread::Result<()> { - use crate::sync::Arc; - use crate::sync::atomic::{AtomicBool, Ordering}; - - let running = Arc::new(AtomicBool::new(true)); - - let threads: Vec<_> = (0..$thread_count) - .map(|_| { - let flag = Arc::clone(&running); - crate::thread::spawn(move || { - while flag.load(Ordering::Relaxed) { - black_box(Instant::now()); - } - }) - }) - .collect(); - - b.iter(|| { - let a = Instant::now(); - let b = Instant::now(); - assert!(b >= a); - }); - - running.store(false, Ordering::Relaxed); - - for t in threads { - t.join()?; - } - Ok(()) - } - }; -} - -bench_instant_threaded!(instant_contention_01_threads, 0); -bench_instant_threaded!(instant_contention_02_threads, 1); -bench_instant_threaded!(instant_contention_04_threads, 3); -bench_instant_threaded!(instant_contention_08_threads, 7); -bench_instant_threaded!(instant_contention_16_threads, 15); diff --git a/library/std/tests/common/mod.rs b/library/std/tests/common/mod.rs index 7cf70c725e411..1e8e4cced6c03 100644 --- a/library/std/tests/common/mod.rs +++ b/library/std/tests/common/mod.rs @@ -18,7 +18,7 @@ pub(crate) fn test_rng() -> rand_xorshift::XorShiftRng { rand::SeedableRng::from_seed(seed) } -// Copied from std::sys_common::io +// Copied from std::test_helpers pub(crate) struct TempDir(PathBuf); impl TempDir { diff --git a/library/std/tests/env.rs b/library/std/tests/env.rs index 44fe84c989fb7..e754cf8263b0f 100644 --- a/library/std/tests/env.rs +++ b/library/std/tests/env.rs @@ -1,163 +1,123 @@ use std::env::*; -use std::ffi::{OsStr, OsString}; - -use rand::distributions::{Alphanumeric, DistString}; +use std::path::Path; mod common; -use std::thread; - -use common::test_rng; - -#[track_caller] -fn make_rand_name() -> OsString { - let n = format!("TEST{}", Alphanumeric.sample_string(&mut test_rng(), 10)); - let n = OsString::from(n); - assert!(var_os(&n).is_none()); - n -} - -fn eq(a: Option, b: Option<&str>) { - assert_eq!(a.as_ref().map(|s| &**s), b.map(OsStr::new).map(|s| &*s)); -} #[test] -fn test_set_var() { - let n = make_rand_name(); - set_var(&n, "VALUE"); - eq(var_os(&n), Some("VALUE")); +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi", target_env = "sgx"), ignore)] +fn test_self_exe_path() { + let path = current_exe(); + assert!(path.is_ok()); + let path = path.unwrap(); + + // Hard to test this function + assert!(path.is_absolute()); } #[test] -fn test_remove_var() { - let n = make_rand_name(); - set_var(&n, "VALUE"); - remove_var(&n); - eq(var_os(&n), None); -} +fn test() { + assert!((!Path::new("test-path").is_absolute())); -#[test] -fn test_set_var_overwrite() { - let n = make_rand_name(); - set_var(&n, "1"); - set_var(&n, "2"); - eq(var_os(&n), Some("2")); - set_var(&n, ""); - eq(var_os(&n), Some("")); + #[cfg(not(target_env = "sgx"))] + current_dir().unwrap(); } #[test] -#[cfg_attr(target_os = "emscripten", ignore)] -fn test_var_big() { - let mut s = "".to_string(); - let mut i = 0; - while i < 100 { - s.push_str("aaaaaaaaaa"); - i += 1; +#[cfg(windows)] +fn split_paths_windows() { + use std::path::PathBuf; + + fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { + split_paths(unparsed).collect::>() + == parsed.iter().map(|s| PathBuf::from(*s)).collect::>() } - let n = make_rand_name(); - set_var(&n, &s); - eq(var_os(&n), Some(&s)); -} -#[test] -#[cfg_attr(target_os = "emscripten", ignore)] -fn test_env_set_get_huge() { - let n = make_rand_name(); - let s = "x".repeat(10000); - set_var(&n, &s); - eq(var_os(&n), Some(&s)); - remove_var(&n); - eq(var_os(&n), None); + assert!(check_parse("", &mut [""])); + assert!(check_parse(r#""""#, &mut [""])); + assert!(check_parse(";;", &mut ["", "", ""])); + assert!(check_parse(r"c:\", &mut [r"c:\"])); + assert!(check_parse(r"c:\;", &mut [r"c:\", ""])); + assert!(check_parse(r"c:\;c:\Program Files\", &mut [r"c:\", r"c:\Program Files\"])); + assert!(check_parse(r#"c:\;c:\"foo"\"#, &mut [r"c:\", r"c:\foo\"])); + assert!(check_parse(r#"c:\;c:\"foo;bar"\;c:\baz"#, &mut [r"c:\", r"c:\foo;bar\", r"c:\baz"])); } #[test] -fn test_env_set_var() { - let n = make_rand_name(); +#[cfg(unix)] +fn split_paths_unix() { + use std::path::PathBuf; - let mut e = vars_os(); - set_var(&n, "VALUE"); - assert!(!e.any(|(k, v)| { &*k == &*n && &*v == "VALUE" })); + fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { + split_paths(unparsed).collect::>() + == parsed.iter().map(|s| PathBuf::from(*s)).collect::>() + } - assert!(vars_os().any(|(k, v)| { &*k == &*n && &*v == "VALUE" })); + assert!(check_parse("", &mut [""])); + assert!(check_parse("::", &mut ["", "", ""])); + assert!(check_parse("/", &mut ["/"])); + assert!(check_parse("/:", &mut ["/", ""])); + assert!(check_parse("/:/usr/local", &mut ["/", "/usr/local"])); } #[test] -#[cfg_attr(not(any(unix, windows)), ignore, allow(unused))] -#[allow(deprecated)] -fn env_home_dir() { - use std::path::PathBuf; +#[cfg(unix)] +fn join_paths_unix() { + use std::ffi::OsStr; - fn var_to_os_string(var: Result) -> Option { - match var { - Ok(var) => Some(OsString::from(var)), - Err(VarError::NotUnicode(var)) => Some(var), - _ => None, - } + fn test_eq(input: &[&str], output: &str) -> bool { + &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output) } - cfg_if::cfg_if! { - if #[cfg(unix)] { - let oldhome = var_to_os_string(var("HOME")); - - set_var("HOME", "/home/MountainView"); - assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); - - remove_var("HOME"); - if cfg!(target_os = "android") { - assert!(home_dir().is_none()); - } else { - // When HOME is not set, some platforms return `None`, - // but others return `Some` with a default. - // Just check that it is not "/home/MountainView". - assert_ne!(home_dir(), Some(PathBuf::from("/home/MountainView"))); - } - - if let Some(oldhome) = oldhome { set_var("HOME", oldhome); } - } else if #[cfg(windows)] { - let oldhome = var_to_os_string(var("HOME")); - let olduserprofile = var_to_os_string(var("USERPROFILE")); - - remove_var("HOME"); - remove_var("USERPROFILE"); - - assert!(home_dir().is_some()); - - set_var("HOME", "/home/PaloAlto"); - assert_ne!(home_dir(), Some(PathBuf::from("/home/PaloAlto")), "HOME must not be used"); + assert!(test_eq(&[], "")); + assert!(test_eq(&["/bin", "/usr/bin", "/usr/local/bin"], "/bin:/usr/bin:/usr/local/bin")); + assert!(test_eq(&["", "/bin", "", "", "/usr/bin", ""], ":/bin:::/usr/bin:")); + assert!(join_paths(["/te:st"].iter().cloned()).is_err()); +} - set_var("USERPROFILE", "/home/MountainView"); - assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); +#[test] +#[cfg(windows)] +fn join_paths_windows() { + use std::ffi::OsStr; - remove_var("HOME"); + fn test_eq(input: &[&str], output: &str) -> bool { + &*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output) + } - assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); + assert!(test_eq(&[], "")); + assert!(test_eq(&[r"c:\windows", r"c:\"], r"c:\windows;c:\")); + assert!(test_eq(&["", r"c:\windows", "", "", r"c:\", ""], r";c:\windows;;;c:\;")); + assert!(test_eq(&[r"c:\te;st", r"c:\"], r#""c:\te;st";c:\"#)); + assert!(join_paths([r#"c:\te"st"#].iter().cloned()).is_err()); +} - set_var("USERPROFILE", ""); - assert_ne!(home_dir(), Some(PathBuf::from("")), "Empty USERPROFILE must be ignored"); +#[test] +fn args_debug() { + assert_eq!( + format!("Args {{ inner: {:?} }}", args().collect::>()), + format!("{:?}", args()) + ); +} - remove_var("USERPROFILE"); +#[test] +fn args_os_debug() { + assert_eq!( + format!("ArgsOs {{ inner: {:?} }}", args_os().collect::>()), + format!("{:?}", args_os()) + ); +} - if let Some(oldhome) = oldhome { set_var("HOME", oldhome); } - if let Some(olduserprofile) = olduserprofile { set_var("USERPROFILE", olduserprofile); } - } - } +#[test] +fn vars_debug() { + assert_eq!( + format!("Vars {{ inner: {:?} }}", vars().collect::>()), + format!("{:?}", vars()) + ); } -#[test] // miri shouldn't detect any data race in this fn -#[cfg_attr(any(not(miri), target_os = "emscripten"), ignore)] -fn test_env_get_set_multithreaded() { - let getter = thread::spawn(|| { - for _ in 0..100 { - let _ = var_os("foo"); - } - }); - - let setter = thread::spawn(|| { - for _ in 0..100 { - set_var("foo", "bar"); - } - }); - - let _ = getter.join(); - let _ = setter.join(); +#[test] +fn vars_os_debug() { + assert_eq!( + format!("VarsOs {{ inner: {:?} }}", vars_os().collect::>()), + format!("{:?}", vars_os()) + ); } diff --git a/library/std/tests/env_modify.rs b/library/std/tests/env_modify.rs new file mode 100644 index 0000000000000..ba84978b35f8f --- /dev/null +++ b/library/std/tests/env_modify.rs @@ -0,0 +1,184 @@ +// These tests are in a separate integration test as they modify the environment, +// and would otherwise cause some other tests to fail. + +use std::env::*; +use std::ffi::{OsStr, OsString}; + +use rand::distr::{Alphanumeric, SampleString}; + +mod common; +use std::thread; + +use common::test_rng; + +#[track_caller] +fn make_rand_name() -> OsString { + let n = format!("TEST{}", Alphanumeric.sample_string(&mut test_rng(), 10)); + let n = OsString::from(n); + assert!(var_os(&n).is_none()); + n +} + +fn eq(a: Option, b: Option<&str>) { + assert_eq!(a.as_ref().map(|s| &**s), b.map(OsStr::new).map(|s| &*s)); +} + +#[test] +fn test_set_var() { + let n = make_rand_name(); + unsafe { + set_var(&n, "VALUE"); + } + eq(var_os(&n), Some("VALUE")); +} + +#[test] +fn test_remove_var() { + let n = make_rand_name(); + unsafe { + set_var(&n, "VALUE"); + remove_var(&n); + } + eq(var_os(&n), None); +} + +#[test] +fn test_set_var_overwrite() { + let n = make_rand_name(); + unsafe { + set_var(&n, "1"); + set_var(&n, "2"); + eq(var_os(&n), Some("2")); + set_var(&n, ""); + eq(var_os(&n), Some("")); + } +} + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn test_var_big() { + let mut s = "".to_string(); + let mut i = 0; + while i < 100 { + s.push_str("aaaaaaaaaa"); + i += 1; + } + let n = make_rand_name(); + unsafe { + set_var(&n, &s); + } + eq(var_os(&n), Some(&s)); +} + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn test_env_set_get_huge() { + let n = make_rand_name(); + let s = "x".repeat(10000); + unsafe { + set_var(&n, &s); + eq(var_os(&n), Some(&s)); + remove_var(&n); + eq(var_os(&n), None); + } +} + +#[test] +fn test_env_set_var() { + let n = make_rand_name(); + + let mut e = vars_os(); + unsafe { + set_var(&n, "VALUE"); + } + assert!(!e.any(|(k, v)| { &*k == &*n && &*v == "VALUE" })); + + assert!(vars_os().any(|(k, v)| { &*k == &*n && &*v == "VALUE" })); +} + +#[test] +#[cfg_attr(not(any(unix, windows)), ignore, allow(unused))] +#[allow(deprecated)] +fn env_home_dir() { + use std::path::PathBuf; + + fn var_to_os_string(var: Result) -> Option { + match var { + Ok(var) => Some(OsString::from(var)), + Err(VarError::NotUnicode(var)) => Some(var), + _ => None, + } + } + + cfg_if::cfg_if! { + if #[cfg(unix)] { + let oldhome = var_to_os_string(var("HOME")); + + unsafe { + set_var("HOME", "/home/MountainView"); + assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); + + remove_var("HOME"); + } + if cfg!(target_os = "android") { + assert!(home_dir().is_none()); + } else { + // When HOME is not set, some platforms return `None`, + // but others return `Some` with a default. + // Just check that it is not "/home/MountainView". + assert_ne!(home_dir(), Some(PathBuf::from("/home/MountainView"))); + } + + if let Some(oldhome) = oldhome { unsafe { set_var("HOME", oldhome); } } + } else if #[cfg(windows)] { + let oldhome = var_to_os_string(var("HOME")); + let olduserprofile = var_to_os_string(var("USERPROFILE")); + + unsafe { + remove_var("HOME"); + remove_var("USERPROFILE"); + + assert!(home_dir().is_some()); + + set_var("HOME", "/home/PaloAlto"); + assert_ne!(home_dir(), Some(PathBuf::from("/home/PaloAlto")), "HOME must not be used"); + + set_var("USERPROFILE", "/home/MountainView"); + assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); + + remove_var("HOME"); + + assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView"))); + + set_var("USERPROFILE", ""); + assert_ne!(home_dir(), Some(PathBuf::from("")), "Empty USERPROFILE must be ignored"); + + remove_var("USERPROFILE"); + + if let Some(oldhome) = oldhome { set_var("HOME", oldhome); } + if let Some(olduserprofile) = olduserprofile { set_var("USERPROFILE", olduserprofile); } + } + } + } +} + +#[test] // miri shouldn't detect any data race in this fn +#[cfg_attr(any(not(miri), target_os = "emscripten"), ignore)] +fn test_env_get_set_multithreaded() { + let getter = thread::spawn(|| { + for _ in 0..100 { + let _ = var_os("foo"); + } + }); + + let setter = thread::spawn(|| { + for _ in 0..100 { + unsafe { + set_var("foo", "bar"); + } + } + }); + + let _ = getter.join(); + let _ = setter.join(); +} diff --git a/library/std/tests/error.rs b/library/std/tests/error.rs new file mode 100644 index 0000000000000..8fd6eb3c02065 --- /dev/null +++ b/library/std/tests/error.rs @@ -0,0 +1,442 @@ +#![feature(error_generic_member_access, error_reporter)] + +use std::backtrace::Backtrace; +use std::error::{Error, Report, Request}; +use std::fmt; + +#[derive(Debug, PartialEq)] +struct A; +#[derive(Debug, PartialEq)] +struct B; + +impl fmt::Display for A { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "A") + } +} +impl fmt::Display for B { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "B") + } +} + +impl Error for A {} +impl Error for B {} + +#[test] +fn downcasting() { + let mut a = A; + let a = &mut a as &mut (dyn Error + 'static); + assert_eq!(a.downcast_ref::(), Some(&A)); + assert_eq!(a.downcast_ref::(), None); + assert_eq!(a.downcast_mut::(), Some(&mut A)); + assert_eq!(a.downcast_mut::(), None); + + let a: Box = Box::new(A); + match a.downcast::() { + Ok(..) => panic!("expected error"), + Err(e) => assert_eq!(*e.downcast::().unwrap(), A), + } +} + +#[derive(Debug)] +struct SuperError { + source: SuperErrorSideKick, +} + +impl fmt::Display for SuperError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SuperError is here!") + } +} + +impl Error for SuperError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + Some(&self.source) + } +} + +#[derive(Debug)] +struct SuperErrorSideKick; + +impl fmt::Display for SuperErrorSideKick { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SuperErrorSideKick is here!") + } +} + +impl Error for SuperErrorSideKick {} + +#[test] +fn single_line_formatting() { + let error = SuperError { source: SuperErrorSideKick }; + let report = Report::new(&error); + let actual = report.to_string(); + let expected = String::from("SuperError is here!: SuperErrorSideKick is here!"); + + assert_eq!(expected, actual); +} + +#[test] +fn multi_line_formatting() { + let error = SuperError { source: SuperErrorSideKick }; + let report = Report::new(&error).pretty(true); + let actual = report.to_string(); + let expected = String::from( + "\ +SuperError is here! + +Caused by: + SuperErrorSideKick is here!", + ); + + assert_eq!(expected, actual); +} + +#[test] +fn error_with_no_sources_formats_single_line_correctly() { + let report = Report::new(SuperErrorSideKick); + let actual = report.to_string(); + let expected = String::from("SuperErrorSideKick is here!"); + + assert_eq!(expected, actual); +} + +#[test] +fn error_with_no_sources_formats_multi_line_correctly() { + let report = Report::new(SuperErrorSideKick).pretty(true); + let actual = report.to_string(); + let expected = String::from("SuperErrorSideKick is here!"); + + assert_eq!(expected, actual); +} + +#[test] +fn error_with_backtrace_outputs_correctly_with_one_source() { + let trace = Backtrace::force_capture(); + let expected = format!( + "\ +The source of the error + +Caused by: + Error with backtrace + +Stack backtrace: +{}", + trace + ); + let error = GenericError::new("Error with backtrace"); + let mut error = GenericError::new_with_source("The source of the error", error); + error.backtrace = Some(trace); + let report = Report::new(error).pretty(true).show_backtrace(true); + + println!("Error: {report}"); + assert_eq!(expected.trim_end(), report.to_string()); +} + +#[test] +fn error_with_backtrace_outputs_correctly_with_two_sources() { + let trace = Backtrace::force_capture(); + let expected = format!( + "\ +Error with two sources + +Caused by: + 0: The source of the error + 1: Error with backtrace + +Stack backtrace: +{}", + trace + ); + let mut error = GenericError::new("Error with backtrace"); + error.backtrace = Some(trace); + let error = GenericError::new_with_source("The source of the error", error); + let error = GenericError::new_with_source("Error with two sources", error); + let report = Report::new(error).pretty(true).show_backtrace(true); + + println!("Error: {report}"); + assert_eq!(expected.trim_end(), report.to_string()); +} + +#[derive(Debug)] +struct GenericError { + message: D, + backtrace: Option, + source: Option>, +} + +impl GenericError { + fn new(message: D) -> GenericError { + Self { message, backtrace: None, source: None } + } + + fn new_with_source(message: D, source: E) -> GenericError + where + E: Error + 'static, + { + let source: Box = Box::new(source); + let source = Some(source); + GenericError { message, backtrace: None, source } + } +} + +impl fmt::Display for GenericError +where + D: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.message, f) + } +} + +impl Error for GenericError +where + D: fmt::Debug + fmt::Display, +{ + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.source.as_deref() + } + + fn provide<'a>(&'a self, req: &mut Request<'a>) { + self.backtrace.as_ref().map(|bt| req.provide_ref::(bt)); + } +} + +#[test] +fn error_formats_single_line_with_rude_display_impl() { + #[derive(Debug)] + struct MyMessage; + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("line 1\nline 2")?; + f.write_str("\nline 3\nline 4\n")?; + f.write_str("line 5\nline 6")?; + Ok(()) + } + } + + let error = GenericError::new(MyMessage); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let report = Report::new(error); + let expected = "\ +line 1 +line 2 +line 3 +line 4 +line 5 +line 6: line 1 +line 2 +line 3 +line 4 +line 5 +line 6: line 1 +line 2 +line 3 +line 4 +line 5 +line 6: line 1 +line 2 +line 3 +line 4 +line 5 +line 6"; + + let actual = report.to_string(); + assert_eq!(expected, actual); +} + +#[test] +fn error_formats_multi_line_with_rude_display_impl() { + #[derive(Debug)] + struct MyMessage; + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("line 1\nline 2")?; + f.write_str("\nline 3\nline 4\n")?; + f.write_str("line 5\nline 6")?; + Ok(()) + } + } + + let error = GenericError::new(MyMessage); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let report = Report::new(error).pretty(true); + let expected = "line 1 +line 2 +line 3 +line 4 +line 5 +line 6 + +Caused by: + 0: line 1 + line 2 + line 3 + line 4 + line 5 + line 6 + 1: line 1 + line 2 + line 3 + line 4 + line 5 + line 6 + 2: line 1 + line 2 + line 3 + line 4 + line 5 + line 6"; + + let actual = report.to_string(); + assert_eq!(expected, actual); +} + +#[test] +fn errors_that_start_with_newline_formats_correctly() { + #[derive(Debug)] + struct MyMessage; + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("\nThe message\n") + } + } + + let error = GenericError::new(MyMessage); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let report = Report::new(error).pretty(true); + let expected = " +The message + + +Caused by: + 0: \ +\n The message + \ +\n 1: \ +\n The message + "; + + let actual = report.to_string(); + assert_eq!(expected, actual); +} + +#[test] +fn errors_with_multiple_writes_on_same_line_dont_insert_erroneous_newlines() { + #[derive(Debug)] + struct MyMessage; + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("The message")?; + f.write_str(" goes on")?; + f.write_str(" and on.") + } + } + + let error = GenericError::new(MyMessage); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let report = Report::new(error).pretty(true); + let expected = "\ +The message goes on and on. + +Caused by: + 0: The message goes on and on. + 1: The message goes on and on."; + + let actual = report.to_string(); + println!("{actual}"); + assert_eq!(expected, actual); +} + +#[test] +fn errors_with_string_interpolation_formats_correctly() { + #[derive(Debug)] + struct MyMessage(usize); + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Got an error code: ({}). ", self.0)?; + write!(f, "What would you like to do in response?") + } + } + + let error = GenericError::new(MyMessage(10)); + let error = GenericError::new_with_source(MyMessage(20), error); + let report = Report::new(error).pretty(true); + let expected = "\ +Got an error code: (20). What would you like to do in response? + +Caused by: + Got an error code: (10). What would you like to do in response?"; + let actual = report.to_string(); + assert_eq!(expected, actual); +} + +#[test] +fn empty_lines_mid_message() { + #[derive(Debug)] + struct MyMessage; + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("line 1\n\nline 2") + } + } + + let error = GenericError::new(MyMessage); + let error = GenericError::new_with_source(MyMessage, error); + let error = GenericError::new_with_source(MyMessage, error); + let report = Report::new(error).pretty(true); + let expected = "\ +line 1 + +line 2 + +Caused by: + 0: line 1 + \ +\n line 2 + 1: line 1 + \ +\n line 2"; + + let actual = report.to_string(); + assert_eq!(expected, actual); +} + +#[test] +fn only_one_source() { + #[derive(Debug)] + struct MyMessage; + + impl fmt::Display for MyMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("line 1\nline 2") + } + } + + let error = GenericError::new(MyMessage); + let error = GenericError::new_with_source(MyMessage, error); + let report = Report::new(error).pretty(true); + let expected = "\ +line 1 +line 2 + +Caused by: + line 1 + line 2"; + + let actual = report.to_string(); + assert_eq!(expected, actual); +} diff --git a/library/std/tests/floats/f128.rs b/library/std/tests/floats/f128.rs new file mode 100644 index 0000000000000..d0e8b157e6b6f --- /dev/null +++ b/library/std/tests/floats/f128.rs @@ -0,0 +1,985 @@ +// FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy +#![cfg(reliable_f128)] + +use std::f128::consts; +use std::num::FpCategory as Fp; +#[cfg(reliable_f128_math)] +use std::ops::Rem; +use std::ops::{Add, Div, Mul, Sub}; + +// Note these tolerances make sense around zero, but not for more extreme exponents. + +/// For operations that are near exact, usually not involving math of different +/// signs. +const TOL_PRECISE: f128 = 1e-28; + +/// Default tolerances. Works for values that should be near precise but not exact. Roughly +/// the precision carried by `100 * 100`. +const TOL: f128 = 1e-12; + +/// Tolerances for math that is allowed to be imprecise, usually due to multiple chained +/// operations. +#[cfg(reliable_f128_math)] +const TOL_IMPR: f128 = 1e-10; + +/// Smallest number +const TINY_BITS: u128 = 0x1; + +/// Next smallest number +const TINY_UP_BITS: u128 = 0x2; + +/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 +const MAX_DOWN_BITS: u128 = 0x7ffefffffffffffffffffffffffffffe; + +/// Zeroed exponent, full significant +const LARGEST_SUBNORMAL_BITS: u128 = 0x0000ffffffffffffffffffffffffffff; + +/// Exponent = 0b1, zeroed significand +const SMALLEST_NORMAL_BITS: u128 = 0x00010000000000000000000000000000; + +/// First pattern over the mantissa +const NAN_MASK1: u128 = 0x0000aaaaaaaaaaaaaaaaaaaaaaaaaaaa; + +/// Second pattern over the mantissa +const NAN_MASK2: u128 = 0x00005555555555555555555555555555; + +/// Compare by representation +#[allow(unused_macros)] +macro_rules! assert_f128_biteq { + ($a:expr, $b:expr) => { + let (l, r): (&f128, &f128) = (&$a, &$b); + let lb = l.to_bits(); + let rb = r.to_bits(); + assert_eq!(lb, rb, "float {l:?} is not bitequal to {r:?}.\na: {lb:#034x}\nb: {rb:#034x}"); + }; +} + +#[test] +fn test_num_f128() { + // FIXME(f16_f128): replace with a `test_num` call once the required `fmodl`/`fmodf128` + // function is available on all platforms. + let ten = 10f128; + let two = 2f128; + assert_eq!(ten.add(two), ten + two); + assert_eq!(ten.sub(two), ten - two); + assert_eq!(ten.mul(two), ten * two); + assert_eq!(ten.div(two), ten / two); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_num_f128_rem() { + let ten = 10f128; + let two = 2f128; + assert_eq!(ten.rem(two), ten % two); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_min_nan() { + assert_eq!(f128::NAN.min(2.0), 2.0); + assert_eq!(2.0f128.min(f128::NAN), 2.0); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_max_nan() { + assert_eq!(f128::NAN.max(2.0), 2.0); + assert_eq!(2.0f128.max(f128::NAN), 2.0); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_minimum() { + assert!(f128::NAN.minimum(2.0).is_nan()); + assert!(2.0f128.minimum(f128::NAN).is_nan()); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_maximum() { + assert!(f128::NAN.maximum(2.0).is_nan()); + assert!(2.0f128.maximum(f128::NAN).is_nan()); +} + +#[test] +fn test_nan() { + let nan: f128 = f128::NAN; + assert!(nan.is_nan()); + assert!(!nan.is_infinite()); + assert!(!nan.is_finite()); + assert!(nan.is_sign_positive()); + assert!(!nan.is_sign_negative()); + assert!(!nan.is_normal()); + assert_eq!(Fp::Nan, nan.classify()); +} + +#[test] +fn test_infinity() { + let inf: f128 = f128::INFINITY; + assert!(inf.is_infinite()); + assert!(!inf.is_finite()); + assert!(inf.is_sign_positive()); + assert!(!inf.is_sign_negative()); + assert!(!inf.is_nan()); + assert!(!inf.is_normal()); + assert_eq!(Fp::Infinite, inf.classify()); +} + +#[test] +fn test_neg_infinity() { + let neg_inf: f128 = f128::NEG_INFINITY; + assert!(neg_inf.is_infinite()); + assert!(!neg_inf.is_finite()); + assert!(!neg_inf.is_sign_positive()); + assert!(neg_inf.is_sign_negative()); + assert!(!neg_inf.is_nan()); + assert!(!neg_inf.is_normal()); + assert_eq!(Fp::Infinite, neg_inf.classify()); +} + +#[test] +fn test_zero() { + let zero: f128 = 0.0f128; + assert_eq!(0.0, zero); + assert!(!zero.is_infinite()); + assert!(zero.is_finite()); + assert!(zero.is_sign_positive()); + assert!(!zero.is_sign_negative()); + assert!(!zero.is_nan()); + assert!(!zero.is_normal()); + assert_eq!(Fp::Zero, zero.classify()); +} + +#[test] +fn test_neg_zero() { + let neg_zero: f128 = -0.0; + assert_eq!(0.0, neg_zero); + assert!(!neg_zero.is_infinite()); + assert!(neg_zero.is_finite()); + assert!(!neg_zero.is_sign_positive()); + assert!(neg_zero.is_sign_negative()); + assert!(!neg_zero.is_nan()); + assert!(!neg_zero.is_normal()); + assert_eq!(Fp::Zero, neg_zero.classify()); +} + +#[test] +fn test_one() { + let one: f128 = 1.0f128; + assert_eq!(1.0, one); + assert!(!one.is_infinite()); + assert!(one.is_finite()); + assert!(one.is_sign_positive()); + assert!(!one.is_sign_negative()); + assert!(!one.is_nan()); + assert!(one.is_normal()); + assert_eq!(Fp::Normal, one.classify()); +} + +#[test] +fn test_is_nan() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert!(nan.is_nan()); + assert!(!0.0f128.is_nan()); + assert!(!5.3f128.is_nan()); + assert!(!(-10.732f128).is_nan()); + assert!(!inf.is_nan()); + assert!(!neg_inf.is_nan()); +} + +#[test] +fn test_is_infinite() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert!(!nan.is_infinite()); + assert!(inf.is_infinite()); + assert!(neg_inf.is_infinite()); + assert!(!0.0f128.is_infinite()); + assert!(!42.8f128.is_infinite()); + assert!(!(-109.2f128).is_infinite()); +} + +#[test] +fn test_is_finite() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert!(!nan.is_finite()); + assert!(!inf.is_finite()); + assert!(!neg_inf.is_finite()); + assert!(0.0f128.is_finite()); + assert!(42.8f128.is_finite()); + assert!((-109.2f128).is_finite()); +} + +#[test] +fn test_is_normal() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let zero: f128 = 0.0f128; + let neg_zero: f128 = -0.0; + assert!(!nan.is_normal()); + assert!(!inf.is_normal()); + assert!(!neg_inf.is_normal()); + assert!(!zero.is_normal()); + assert!(!neg_zero.is_normal()); + assert!(1f128.is_normal()); + assert!(1e-4931f128.is_normal()); + assert!(!1e-4932f128.is_normal()); +} + +#[test] +fn test_classify() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let zero: f128 = 0.0f128; + let neg_zero: f128 = -0.0; + assert_eq!(nan.classify(), Fp::Nan); + assert_eq!(inf.classify(), Fp::Infinite); + assert_eq!(neg_inf.classify(), Fp::Infinite); + assert_eq!(zero.classify(), Fp::Zero); + assert_eq!(neg_zero.classify(), Fp::Zero); + assert_eq!(1f128.classify(), Fp::Normal); + assert_eq!(1e-4931f128.classify(), Fp::Normal); + assert_eq!(1e-4932f128.classify(), Fp::Subnormal); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_floor() { + assert_approx_eq!(1.0f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.floor(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).floor(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).floor(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).floor(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).floor(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).floor(), -2.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_ceil() { + assert_approx_eq!(1.0f128.ceil(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.ceil(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.ceil(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.ceil(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.ceil(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).ceil(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).ceil(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).ceil(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).ceil(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).ceil(), -1.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_round() { + assert_approx_eq!(2.5f128.round(), 3.0f128, TOL_PRECISE); + assert_approx_eq!(1.0f128.round(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.round(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.round(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.round(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.round(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).round(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).round(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).round(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).round(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).round(), -2.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_round_ties_even() { + assert_approx_eq!(2.5f128.round_ties_even(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.0f128.round_ties_even(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.round_ties_even(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.round_ties_even(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.round_ties_even(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.round_ties_even(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).round_ties_even(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).round_ties_even(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).round_ties_even(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).round_ties_even(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).round_ties_even(), -2.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_trunc() { + assert_approx_eq!(1.0f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.trunc(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).trunc(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).trunc(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).trunc(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).trunc(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).trunc(), -1.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_fract() { + assert_approx_eq!(1.0f128.fract(), 0.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.fract(), 0.3f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.fract(), 0.5f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.fract(), 0.7f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.fract(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).fract(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).fract(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).fract(), -0.3f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).fract(), -0.5f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).fract(), -0.7f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_abs() { + assert_eq!(f128::INFINITY.abs(), f128::INFINITY); + assert_eq!(1f128.abs(), 1f128); + assert_eq!(0f128.abs(), 0f128); + assert_eq!((-0f128).abs(), 0f128); + assert_eq!((-1f128).abs(), 1f128); + assert_eq!(f128::NEG_INFINITY.abs(), f128::INFINITY); + assert_eq!((1f128 / f128::NEG_INFINITY).abs(), 0f128); + assert!(f128::NAN.abs().is_nan()); +} + +#[test] +fn test_is_sign_positive() { + assert!(f128::INFINITY.is_sign_positive()); + assert!(1f128.is_sign_positive()); + assert!(0f128.is_sign_positive()); + assert!(!(-0f128).is_sign_positive()); + assert!(!(-1f128).is_sign_positive()); + assert!(!f128::NEG_INFINITY.is_sign_positive()); + assert!(!(1f128 / f128::NEG_INFINITY).is_sign_positive()); + assert!(f128::NAN.is_sign_positive()); + assert!(!(-f128::NAN).is_sign_positive()); +} + +#[test] +fn test_is_sign_negative() { + assert!(!f128::INFINITY.is_sign_negative()); + assert!(!1f128.is_sign_negative()); + assert!(!0f128.is_sign_negative()); + assert!((-0f128).is_sign_negative()); + assert!((-1f128).is_sign_negative()); + assert!(f128::NEG_INFINITY.is_sign_negative()); + assert!((1f128 / f128::NEG_INFINITY).is_sign_negative()); + assert!(!f128::NAN.is_sign_negative()); + assert!((-f128::NAN).is_sign_negative()); +} + +#[test] +fn test_next_up() { + let tiny = f128::from_bits(TINY_BITS); + let tiny_up = f128::from_bits(TINY_UP_BITS); + let max_down = f128::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f128::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f128::from_bits(SMALLEST_NORMAL_BITS); + assert_f128_biteq!(f128::NEG_INFINITY.next_up(), f128::MIN); + assert_f128_biteq!(f128::MIN.next_up(), -max_down); + assert_f128_biteq!((-1.0 - f128::EPSILON).next_up(), -1.0); + assert_f128_biteq!((-smallest_normal).next_up(), -largest_subnormal); + assert_f128_biteq!((-tiny_up).next_up(), -tiny); + assert_f128_biteq!((-tiny).next_up(), -0.0f128); + assert_f128_biteq!((-0.0f128).next_up(), tiny); + assert_f128_biteq!(0.0f128.next_up(), tiny); + assert_f128_biteq!(tiny.next_up(), tiny_up); + assert_f128_biteq!(largest_subnormal.next_up(), smallest_normal); + assert_f128_biteq!(1.0f128.next_up(), 1.0 + f128::EPSILON); + assert_f128_biteq!(f128::MAX.next_up(), f128::INFINITY); + assert_f128_biteq!(f128::INFINITY.next_up(), f128::INFINITY); + + // Check that NaNs roundtrip. + let nan0 = f128::NAN; + let nan1 = f128::from_bits(f128::NAN.to_bits() ^ 0x002a_aaaa); + let nan2 = f128::from_bits(f128::NAN.to_bits() ^ 0x0055_5555); + assert_f128_biteq!(nan0.next_up(), nan0); + assert_f128_biteq!(nan1.next_up(), nan1); + assert_f128_biteq!(nan2.next_up(), nan2); +} + +#[test] +fn test_next_down() { + let tiny = f128::from_bits(TINY_BITS); + let tiny_up = f128::from_bits(TINY_UP_BITS); + let max_down = f128::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f128::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f128::from_bits(SMALLEST_NORMAL_BITS); + assert_f128_biteq!(f128::NEG_INFINITY.next_down(), f128::NEG_INFINITY); + assert_f128_biteq!(f128::MIN.next_down(), f128::NEG_INFINITY); + assert_f128_biteq!((-max_down).next_down(), f128::MIN); + assert_f128_biteq!((-1.0f128).next_down(), -1.0 - f128::EPSILON); + assert_f128_biteq!((-largest_subnormal).next_down(), -smallest_normal); + assert_f128_biteq!((-tiny).next_down(), -tiny_up); + assert_f128_biteq!((-0.0f128).next_down(), -tiny); + assert_f128_biteq!((0.0f128).next_down(), -tiny); + assert_f128_biteq!(tiny.next_down(), 0.0f128); + assert_f128_biteq!(tiny_up.next_down(), tiny); + assert_f128_biteq!(smallest_normal.next_down(), largest_subnormal); + assert_f128_biteq!((1.0 + f128::EPSILON).next_down(), 1.0f128); + assert_f128_biteq!(f128::MAX.next_down(), max_down); + assert_f128_biteq!(f128::INFINITY.next_down(), f128::MAX); + + // Check that NaNs roundtrip. + let nan0 = f128::NAN; + let nan1 = f128::from_bits(f128::NAN.to_bits() ^ 0x002a_aaaa); + let nan2 = f128::from_bits(f128::NAN.to_bits() ^ 0x0055_5555); + assert_f128_biteq!(nan0.next_down(), nan0); + assert_f128_biteq!(nan1.next_down(), nan1); + assert_f128_biteq!(nan2.next_down(), nan2); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_mul_add() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_approx_eq!(12.3f128.mul_add(4.5, 6.7), 62.05, TOL_PRECISE); + assert_approx_eq!((-12.3f128).mul_add(-4.5, -6.7), 48.65, TOL_PRECISE); + assert_approx_eq!(0.0f128.mul_add(8.9, 1.2), 1.2, TOL_PRECISE); + assert_approx_eq!(3.4f128.mul_add(-0.0, 5.6), 5.6, TOL_PRECISE); + assert!(nan.mul_add(7.8, 9.0).is_nan()); + assert_eq!(inf.mul_add(7.8, 9.0), inf); + assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_eq!(8.9f128.mul_add(inf, 3.2), inf); + assert_eq!((-3.2f128).mul_add(2.4, neg_inf), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_recip() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(1.0f128.recip(), 1.0); + assert_eq!(2.0f128.recip(), 0.5); + assert_eq!((-0.4f128).recip(), -2.5); + assert_eq!(0.0f128.recip(), inf); + assert_approx_eq!( + f128::MAX.recip(), + 8.40525785778023376565669454330438228902076605e-4933, + 1e-4900 + ); + assert!(nan.recip().is_nan()); + assert_eq!(inf.recip(), 0.0); + assert_eq!(neg_inf.recip(), 0.0); +} + +// Many math functions allow for less accurate results, so the next tolerance up is used + +#[test] +#[cfg(reliable_f128_math)] +fn test_powi() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(1.0f128.powi(1), 1.0); + assert_approx_eq!((-3.1f128).powi(2), 9.6100000000000005506706202140776519387, TOL); + assert_approx_eq!(5.9f128.powi(-2), 0.028727377190462507313100483690639638451, TOL); + assert_eq!(8.3f128.powi(0), 1.0); + assert!(nan.powi(2).is_nan()); + assert_eq!(inf.powi(3), inf); + assert_eq!(neg_inf.powi(2), inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_powf() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(1.0f128.powf(1.0), 1.0); + assert_approx_eq!(3.4f128.powf(4.5), 246.40818323761892815995637964326426756, TOL_IMPR); + assert_approx_eq!(2.7f128.powf(-3.2), 0.041652009108526178281070304373500889273, TOL_IMPR); + assert_approx_eq!((-3.1f128).powf(2.0), 9.6100000000000005506706202140776519387, TOL_IMPR); + assert_approx_eq!(5.9f128.powf(-2.0), 0.028727377190462507313100483690639638451, TOL_IMPR); + assert_eq!(8.3f128.powf(0.0), 1.0); + assert!(nan.powf(2.0).is_nan()); + assert_eq!(inf.powf(2.0), inf); + assert_eq!(neg_inf.powf(3.0), neg_inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_sqrt_domain() { + assert!(f128::NAN.sqrt().is_nan()); + assert!(f128::NEG_INFINITY.sqrt().is_nan()); + assert!((-1.0f128).sqrt().is_nan()); + assert_eq!((-0.0f128).sqrt(), -0.0); + assert_eq!(0.0f128.sqrt(), 0.0); + assert_eq!(1.0f128.sqrt(), 1.0); + assert_eq!(f128::INFINITY.sqrt(), f128::INFINITY); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_exp() { + assert_eq!(1.0, 0.0f128.exp()); + assert_approx_eq!(consts::E, 1.0f128.exp(), TOL); + assert_approx_eq!(148.41315910257660342111558004055227962348775, 5.0f128.exp(), TOL); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf, inf.exp()); + assert_eq!(0.0, neg_inf.exp()); + assert!(nan.exp().is_nan()); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_exp2() { + assert_eq!(32.0, 5.0f128.exp2()); + assert_eq!(1.0, 0.0f128.exp2()); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf, inf.exp2()); + assert_eq!(0.0, neg_inf.exp2()); + assert!(nan.exp2().is_nan()); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_ln() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_approx_eq!(1.0f128.exp().ln(), 1.0, TOL); + assert!(nan.ln().is_nan()); + assert_eq!(inf.ln(), inf); + assert!(neg_inf.ln().is_nan()); + assert!((-2.3f128).ln().is_nan()); + assert_eq!((-0.0f128).ln(), neg_inf); + assert_eq!(0.0f128.ln(), neg_inf); + assert_approx_eq!(4.0f128.ln(), 1.3862943611198906188344642429163531366, TOL); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_log() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(10.0f128.log(10.0), 1.0); + assert_approx_eq!(2.3f128.log(3.5), 0.66485771361478710036766645911922010272, TOL); + assert_eq!(1.0f128.exp().log(1.0f128.exp()), 1.0); + assert!(1.0f128.log(1.0).is_nan()); + assert!(1.0f128.log(-13.9).is_nan()); + assert!(nan.log(2.3).is_nan()); + assert_eq!(inf.log(10.0), inf); + assert!(neg_inf.log(8.8).is_nan()); + assert!((-2.3f128).log(0.1).is_nan()); + assert_eq!((-0.0f128).log(2.0), neg_inf); + assert_eq!(0.0f128.log(7.0), neg_inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_log2() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_approx_eq!(10.0f128.log2(), 3.32192809488736234787031942948939017, TOL); + assert_approx_eq!(2.3f128.log2(), 1.2016338611696504130002982471978765921, TOL); + assert_approx_eq!(1.0f128.exp().log2(), 1.4426950408889634073599246810018921381, TOL); + assert!(nan.log2().is_nan()); + assert_eq!(inf.log2(), inf); + assert!(neg_inf.log2().is_nan()); + assert!((-2.3f128).log2().is_nan()); + assert_eq!((-0.0f128).log2(), neg_inf); + assert_eq!(0.0f128.log2(), neg_inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_log10() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(10.0f128.log10(), 1.0); + assert_approx_eq!(2.3f128.log10(), 0.36172783601759284532595218865859309898, TOL); + assert_approx_eq!(1.0f128.exp().log10(), 0.43429448190325182765112891891660508222, TOL); + assert_eq!(1.0f128.log10(), 0.0); + assert!(nan.log10().is_nan()); + assert_eq!(inf.log10(), inf); + assert!(neg_inf.log10().is_nan()); + assert!((-2.3f128).log10().is_nan()); + assert_eq!((-0.0f128).log10(), neg_inf); + assert_eq!(0.0f128.log10(), neg_inf); +} + +#[test] +fn test_to_degrees() { + let pi: f128 = consts::PI; + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(0.0f128.to_degrees(), 0.0); + assert_approx_eq!((-5.8f128).to_degrees(), -332.31552117587745090765431723855668471, TOL); + assert_approx_eq!(pi.to_degrees(), 180.0, TOL); + assert!(nan.to_degrees().is_nan()); + assert_eq!(inf.to_degrees(), inf); + assert_eq!(neg_inf.to_degrees(), neg_inf); + assert_eq!(1_f128.to_degrees(), 57.2957795130823208767981548141051703); +} + +#[test] +fn test_to_radians() { + let pi: f128 = consts::PI; + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(0.0f128.to_radians(), 0.0); + assert_approx_eq!(154.6f128.to_radians(), 2.6982790235832334267135442069489767804, TOL); + assert_approx_eq!((-332.31f128).to_radians(), -5.7999036373023566567593094812182763013, TOL); + // check approx rather than exact because round trip for pi doesn't fall on an exactly + // representable value (unlike `f32` and `f64`). + assert_approx_eq!(180.0f128.to_radians(), pi, TOL_PRECISE); + assert!(nan.to_radians().is_nan()); + assert_eq!(inf.to_radians(), inf); + assert_eq!(neg_inf.to_radians(), neg_inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_asinh() { + // Lower accuracy results are allowed, use increased tolerances + assert_eq!(0.0f128.asinh(), 0.0f128); + assert_eq!((-0.0f128).asinh(), -0.0f128); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf.asinh(), inf); + assert_eq!(neg_inf.asinh(), neg_inf); + assert!(nan.asinh().is_nan()); + assert!((-0.0f128).asinh().is_sign_negative()); + + // issue 63271 + assert_approx_eq!(2.0f128.asinh(), 1.443635475178810342493276740273105f128, TOL_IMPR); + assert_approx_eq!((-2.0f128).asinh(), -1.443635475178810342493276740273105f128, TOL_IMPR); + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!( + (-67452098.07139316f128).asinh(), + -18.720075426274544393985484294000831757220, + TOL_IMPR + ); + + // test for low accuracy from issue 104548 + assert_approx_eq!(60.0f128, 60.0f128.sinh().asinh(), TOL_IMPR); + // mul needed for approximate comparison to be meaningful + assert_approx_eq!(1.0f128, 1e-15f128.sinh().asinh() * 1e15f128, TOL_IMPR); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_acosh() { + assert_eq!(1.0f128.acosh(), 0.0f128); + assert!(0.999f128.acosh().is_nan()); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf.acosh(), inf); + assert!(neg_inf.acosh().is_nan()); + assert!(nan.acosh().is_nan()); + assert_approx_eq!(2.0f128.acosh(), 1.31695789692481670862504634730796844f128, TOL_IMPR); + assert_approx_eq!(3.0f128.acosh(), 1.76274717403908605046521864995958461f128, TOL_IMPR); + + // test for low accuracy from issue 104548 + assert_approx_eq!(60.0f128, 60.0f128.cosh().acosh(), TOL_IMPR); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_atanh() { + assert_eq!(0.0f128.atanh(), 0.0f128); + assert_eq!((-0.0f128).atanh(), -0.0f128); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(1.0f128.atanh(), inf); + assert_eq!((-1.0f128).atanh(), neg_inf); + assert!(2f128.atanh().atanh().is_nan()); + assert!((-2f128).atanh().atanh().is_nan()); + assert!(inf.atanh().is_nan()); + assert!(neg_inf.atanh().is_nan()); + assert!(nan.atanh().is_nan()); + assert_approx_eq!(0.5f128.atanh(), 0.54930614433405484569762261846126285f128, TOL_IMPR); + assert_approx_eq!((-0.5f128).atanh(), -0.54930614433405484569762261846126285f128, TOL_IMPR); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_gamma() { + // precision can differ among platforms + assert_approx_eq!(1.0f128.gamma(), 1.0f128, TOL_IMPR); + assert_approx_eq!(2.0f128.gamma(), 1.0f128, TOL_IMPR); + assert_approx_eq!(3.0f128.gamma(), 2.0f128, TOL_IMPR); + assert_approx_eq!(4.0f128.gamma(), 6.0f128, TOL_IMPR); + assert_approx_eq!(5.0f128.gamma(), 24.0f128, TOL_IMPR); + assert_approx_eq!(0.5f128.gamma(), consts::PI.sqrt(), TOL_IMPR); + assert_approx_eq!((-0.5f128).gamma(), -2.0 * consts::PI.sqrt(), TOL_IMPR); + assert_eq!(0.0f128.gamma(), f128::INFINITY); + assert_eq!((-0.0f128).gamma(), f128::NEG_INFINITY); + assert!((-1.0f128).gamma().is_nan()); + assert!((-2.0f128).gamma().is_nan()); + assert!(f128::NAN.gamma().is_nan()); + assert!(f128::NEG_INFINITY.gamma().is_nan()); + assert_eq!(f128::INFINITY.gamma(), f128::INFINITY); + assert_eq!(1760.9f128.gamma(), f128::INFINITY); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_ln_gamma() { + assert_approx_eq!(1.0f128.ln_gamma().0, 0.0f128, TOL_IMPR); + assert_eq!(1.0f128.ln_gamma().1, 1); + assert_approx_eq!(2.0f128.ln_gamma().0, 0.0f128, TOL_IMPR); + assert_eq!(2.0f128.ln_gamma().1, 1); + assert_approx_eq!(3.0f128.ln_gamma().0, 2.0f128.ln(), TOL_IMPR); + assert_eq!(3.0f128.ln_gamma().1, 1); + assert_approx_eq!((-0.5f128).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), TOL_IMPR); + assert_eq!((-0.5f128).ln_gamma().1, -1); +} + +#[test] +fn test_real_consts() { + let pi: f128 = consts::PI; + let frac_pi_2: f128 = consts::FRAC_PI_2; + let frac_pi_3: f128 = consts::FRAC_PI_3; + let frac_pi_4: f128 = consts::FRAC_PI_4; + let frac_pi_6: f128 = consts::FRAC_PI_6; + let frac_pi_8: f128 = consts::FRAC_PI_8; + let frac_1_pi: f128 = consts::FRAC_1_PI; + let frac_2_pi: f128 = consts::FRAC_2_PI; + + assert_approx_eq!(frac_pi_2, pi / 2f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_3, pi / 3f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_4, pi / 4f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_6, pi / 6f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_8, pi / 8f128, TOL_PRECISE); + assert_approx_eq!(frac_1_pi, 1f128 / pi, TOL_PRECISE); + assert_approx_eq!(frac_2_pi, 2f128 / pi, TOL_PRECISE); + + #[cfg(reliable_f128_math)] + { + let frac_2_sqrtpi: f128 = consts::FRAC_2_SQRT_PI; + let sqrt2: f128 = consts::SQRT_2; + let frac_1_sqrt2: f128 = consts::FRAC_1_SQRT_2; + let e: f128 = consts::E; + let log2_e: f128 = consts::LOG2_E; + let log10_e: f128 = consts::LOG10_E; + let ln_2: f128 = consts::LN_2; + let ln_10: f128 = consts::LN_10; + + assert_approx_eq!(frac_2_sqrtpi, 2f128 / pi.sqrt(), TOL_PRECISE); + assert_approx_eq!(sqrt2, 2f128.sqrt(), TOL_PRECISE); + assert_approx_eq!(frac_1_sqrt2, 1f128 / 2f128.sqrt(), TOL_PRECISE); + assert_approx_eq!(log2_e, e.log2(), TOL_PRECISE); + assert_approx_eq!(log10_e, e.log10(), TOL_PRECISE); + assert_approx_eq!(ln_2, 2f128.ln(), TOL_PRECISE); + assert_approx_eq!(ln_10, 10f128.ln(), TOL_PRECISE); + } +} + +#[test] +fn test_float_bits_conv() { + assert_eq!((1f128).to_bits(), 0x3fff0000000000000000000000000000); + assert_eq!((12.5f128).to_bits(), 0x40029000000000000000000000000000); + assert_eq!((1337f128).to_bits(), 0x40094e40000000000000000000000000); + assert_eq!((-14.25f128).to_bits(), 0xc002c800000000000000000000000000); + assert_approx_eq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0, TOL_PRECISE); + assert_approx_eq!(f128::from_bits(0x40029000000000000000000000000000), 12.5, TOL_PRECISE); + assert_approx_eq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0, TOL_PRECISE); + assert_approx_eq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25, TOL_PRECISE); + + // Check that NaNs roundtrip their bits regardless of signaling-ness + // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits + let masked_nan1 = f128::NAN.to_bits() ^ NAN_MASK1; + let masked_nan2 = f128::NAN.to_bits() ^ NAN_MASK2; + assert!(f128::from_bits(masked_nan1).is_nan()); + assert!(f128::from_bits(masked_nan2).is_nan()); + + assert_eq!(f128::from_bits(masked_nan1).to_bits(), masked_nan1); + assert_eq!(f128::from_bits(masked_nan2).to_bits(), masked_nan2); +} + +#[test] +#[should_panic] +fn test_clamp_min_greater_than_max() { + let _ = 1.0f128.clamp(3.0, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_min_is_nan() { + let _ = 1.0f128.clamp(f128::NAN, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_max_is_nan() { + let _ = 1.0f128.clamp(3.0, f128::NAN); +} + +#[test] +fn test_total_cmp() { + use core::cmp::Ordering; + + fn quiet_bit_mask() -> u128 { + 1 << (f128::MANTISSA_DIGITS - 2) + } + + // FIXME(f16_f128): test subnormals when powf is available + // fn min_subnorm() -> f128 { + // f128::MIN_POSITIVE / f128::powf(2.0, f128::MANTISSA_DIGITS as f128 - 1.0) + // } + + // fn max_subnorm() -> f128 { + // f128::MIN_POSITIVE - min_subnorm() + // } + + fn q_nan() -> f128 { + f128::from_bits(f128::NAN.to_bits() | quiet_bit_mask()) + } + + fn s_nan() -> f128 { + f128::from_bits((f128::NAN.to_bits() & !quiet_bit_mask()) + 42) + } + + assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Equal, (-f128::INFINITY).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Equal, (-f128::MAX).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Equal, (-2.5_f128).total_cmp(&-2.5)); + assert_eq!(Ordering::Equal, (-1.0_f128).total_cmp(&-1.0)); + assert_eq!(Ordering::Equal, (-1.5_f128).total_cmp(&-1.5)); + assert_eq!(Ordering::Equal, (-0.5_f128).total_cmp(&-0.5)); + assert_eq!(Ordering::Equal, (-f128::MIN_POSITIVE).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Equal, (-0.0_f128).total_cmp(&-0.0)); + assert_eq!(Ordering::Equal, 0.0_f128.total_cmp(&0.0)); + // assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Equal, f128::MIN_POSITIVE.total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, 0.5_f128.total_cmp(&0.5)); + assert_eq!(Ordering::Equal, 1.0_f128.total_cmp(&1.0)); + assert_eq!(Ordering::Equal, 1.5_f128.total_cmp(&1.5)); + assert_eq!(Ordering::Equal, 2.5_f128.total_cmp(&2.5)); + assert_eq!(Ordering::Equal, f128::MAX.total_cmp(&f128::MAX)); + assert_eq!(Ordering::Equal, f128::INFINITY.total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); + assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Less, (-f128::INFINITY).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Less, (-f128::MAX).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-2.5_f128).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-1.5_f128).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-1.0_f128).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-0.5_f128).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-f128::MIN_POSITIVE).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); + // assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-0.0_f128).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, 0.0_f128.total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); + // assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Less, f128::MIN_POSITIVE.total_cmp(&0.5)); + assert_eq!(Ordering::Less, 0.5_f128.total_cmp(&1.0)); + assert_eq!(Ordering::Less, 1.0_f128.total_cmp(&1.5)); + assert_eq!(Ordering::Less, 1.5_f128.total_cmp(&2.5)); + assert_eq!(Ordering::Less, 2.5_f128.total_cmp(&f128::MAX)); + assert_eq!(Ordering::Less, f128::MAX.total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Less, f128::INFINITY.total_cmp(&s_nan())); + assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Greater, (-f128::INFINITY).total_cmp(&-s_nan())); + assert_eq!(Ordering::Greater, (-f128::MAX).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Greater, (-2.5_f128).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Greater, (-1.5_f128).total_cmp(&-2.5)); + assert_eq!(Ordering::Greater, (-1.0_f128).total_cmp(&-1.5)); + assert_eq!(Ordering::Greater, (-0.5_f128).total_cmp(&-1.0)); + assert_eq!(Ordering::Greater, (-f128::MIN_POSITIVE).total_cmp(&-0.5)); + // assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Greater, (-0.0_f128).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Greater, 0.0_f128.total_cmp(&-0.0)); + // assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); + // assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Greater, f128::MIN_POSITIVE.total_cmp(&max_subnorm())); + assert_eq!(Ordering::Greater, 0.5_f128.total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, 1.0_f128.total_cmp(&0.5)); + assert_eq!(Ordering::Greater, 1.5_f128.total_cmp(&1.0)); + assert_eq!(Ordering::Greater, 2.5_f128.total_cmp(&1.5)); + assert_eq!(Ordering::Greater, f128::MAX.total_cmp(&2.5)); + assert_eq!(Ordering::Greater, f128::INFINITY.total_cmp(&f128::MAX)); + assert_eq!(Ordering::Greater, s_nan().total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f128::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f128::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); +} diff --git a/library/std/tests/floats/f16.rs b/library/std/tests/floats/f16.rs new file mode 100644 index 0000000000000..5180f3d40f3a7 --- /dev/null +++ b/library/std/tests/floats/f16.rs @@ -0,0 +1,957 @@ +// FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy +#![cfg(reliable_f16)] + +use std::f16::consts; +use std::num::FpCategory as Fp; + +/// Tolerance for results on the order of 10.0e-2 +#[allow(unused)] +const TOL_N2: f16 = 0.0001; + +/// Tolerance for results on the order of 10.0e+0 +#[allow(unused)] +const TOL_0: f16 = 0.01; + +/// Tolerance for results on the order of 10.0e+2 +#[allow(unused)] +const TOL_P2: f16 = 0.5; + +/// Tolerance for results on the order of 10.0e+4 +#[allow(unused)] +const TOL_P4: f16 = 10.0; + +/// Smallest number +const TINY_BITS: u16 = 0x1; + +/// Next smallest number +const TINY_UP_BITS: u16 = 0x2; + +/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 +const MAX_DOWN_BITS: u16 = 0x7bfe; + +/// Zeroed exponent, full significant +const LARGEST_SUBNORMAL_BITS: u16 = 0x03ff; + +/// Exponent = 0b1, zeroed significand +const SMALLEST_NORMAL_BITS: u16 = 0x0400; + +/// First pattern over the mantissa +const NAN_MASK1: u16 = 0x02aa; + +/// Second pattern over the mantissa +const NAN_MASK2: u16 = 0x0155; + +/// Compare by representation +#[allow(unused_macros)] +macro_rules! assert_f16_biteq { + ($a:expr, $b:expr) => { + let (l, r): (&f16, &f16) = (&$a, &$b); + let lb = l.to_bits(); + let rb = r.to_bits(); + assert_eq!(lb, rb, "float {l:?} ({lb:#04x}) is not bitequal to {r:?} ({rb:#04x})"); + }; +} + +#[test] +fn test_num_f16() { + crate::test_num(10f16, 2f16); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_min_nan() { + assert_eq!(f16::NAN.min(2.0), 2.0); + assert_eq!(2.0f16.min(f16::NAN), 2.0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_max_nan() { + assert_eq!(f16::NAN.max(2.0), 2.0); + assert_eq!(2.0f16.max(f16::NAN), 2.0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_minimum() { + assert!(f16::NAN.minimum(2.0).is_nan()); + assert!(2.0f16.minimum(f16::NAN).is_nan()); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_maximum() { + assert!(f16::NAN.maximum(2.0).is_nan()); + assert!(2.0f16.maximum(f16::NAN).is_nan()); +} + +#[test] +fn test_nan() { + let nan: f16 = f16::NAN; + assert!(nan.is_nan()); + assert!(!nan.is_infinite()); + assert!(!nan.is_finite()); + assert!(nan.is_sign_positive()); + assert!(!nan.is_sign_negative()); + assert!(!nan.is_normal()); + assert_eq!(Fp::Nan, nan.classify()); +} + +#[test] +fn test_infinity() { + let inf: f16 = f16::INFINITY; + assert!(inf.is_infinite()); + assert!(!inf.is_finite()); + assert!(inf.is_sign_positive()); + assert!(!inf.is_sign_negative()); + assert!(!inf.is_nan()); + assert!(!inf.is_normal()); + assert_eq!(Fp::Infinite, inf.classify()); +} + +#[test] +fn test_neg_infinity() { + let neg_inf: f16 = f16::NEG_INFINITY; + assert!(neg_inf.is_infinite()); + assert!(!neg_inf.is_finite()); + assert!(!neg_inf.is_sign_positive()); + assert!(neg_inf.is_sign_negative()); + assert!(!neg_inf.is_nan()); + assert!(!neg_inf.is_normal()); + assert_eq!(Fp::Infinite, neg_inf.classify()); +} + +#[test] +fn test_zero() { + let zero: f16 = 0.0f16; + assert_eq!(0.0, zero); + assert!(!zero.is_infinite()); + assert!(zero.is_finite()); + assert!(zero.is_sign_positive()); + assert!(!zero.is_sign_negative()); + assert!(!zero.is_nan()); + assert!(!zero.is_normal()); + assert_eq!(Fp::Zero, zero.classify()); +} + +#[test] +fn test_neg_zero() { + let neg_zero: f16 = -0.0; + assert_eq!(0.0, neg_zero); + assert!(!neg_zero.is_infinite()); + assert!(neg_zero.is_finite()); + assert!(!neg_zero.is_sign_positive()); + assert!(neg_zero.is_sign_negative()); + assert!(!neg_zero.is_nan()); + assert!(!neg_zero.is_normal()); + assert_eq!(Fp::Zero, neg_zero.classify()); +} + +#[test] +fn test_one() { + let one: f16 = 1.0f16; + assert_eq!(1.0, one); + assert!(!one.is_infinite()); + assert!(one.is_finite()); + assert!(one.is_sign_positive()); + assert!(!one.is_sign_negative()); + assert!(!one.is_nan()); + assert!(one.is_normal()); + assert_eq!(Fp::Normal, one.classify()); +} + +#[test] +fn test_is_nan() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert!(nan.is_nan()); + assert!(!0.0f16.is_nan()); + assert!(!5.3f16.is_nan()); + assert!(!(-10.732f16).is_nan()); + assert!(!inf.is_nan()); + assert!(!neg_inf.is_nan()); +} + +#[test] +fn test_is_infinite() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert!(!nan.is_infinite()); + assert!(inf.is_infinite()); + assert!(neg_inf.is_infinite()); + assert!(!0.0f16.is_infinite()); + assert!(!42.8f16.is_infinite()); + assert!(!(-109.2f16).is_infinite()); +} + +#[test] +fn test_is_finite() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert!(!nan.is_finite()); + assert!(!inf.is_finite()); + assert!(!neg_inf.is_finite()); + assert!(0.0f16.is_finite()); + assert!(42.8f16.is_finite()); + assert!((-109.2f16).is_finite()); +} + +#[test] +fn test_is_normal() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let zero: f16 = 0.0f16; + let neg_zero: f16 = -0.0; + assert!(!nan.is_normal()); + assert!(!inf.is_normal()); + assert!(!neg_inf.is_normal()); + assert!(!zero.is_normal()); + assert!(!neg_zero.is_normal()); + assert!(1f16.is_normal()); + assert!(1e-4f16.is_normal()); + assert!(!1e-5f16.is_normal()); +} + +#[test] +fn test_classify() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let zero: f16 = 0.0f16; + let neg_zero: f16 = -0.0; + assert_eq!(nan.classify(), Fp::Nan); + assert_eq!(inf.classify(), Fp::Infinite); + assert_eq!(neg_inf.classify(), Fp::Infinite); + assert_eq!(zero.classify(), Fp::Zero); + assert_eq!(neg_zero.classify(), Fp::Zero); + assert_eq!(1f16.classify(), Fp::Normal); + assert_eq!(1e-4f16.classify(), Fp::Normal); + assert_eq!(1e-5f16.classify(), Fp::Subnormal); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_floor() { + assert_approx_eq!(1.0f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(1.7f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(0.0f16.floor(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).floor(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).floor(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).floor(), -2.0f16, TOL_0); + assert_approx_eq!((-1.5f16).floor(), -2.0f16, TOL_0); + assert_approx_eq!((-1.7f16).floor(), -2.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_ceil() { + assert_approx_eq!(1.0f16.ceil(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.ceil(), 2.0f16, TOL_0); + assert_approx_eq!(1.5f16.ceil(), 2.0f16, TOL_0); + assert_approx_eq!(1.7f16.ceil(), 2.0f16, TOL_0); + assert_approx_eq!(0.0f16.ceil(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).ceil(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).ceil(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).ceil(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).ceil(), -1.0f16, TOL_0); + assert_approx_eq!((-1.7f16).ceil(), -1.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_round() { + assert_approx_eq!(2.5f16.round(), 3.0f16, TOL_0); + assert_approx_eq!(1.0f16.round(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.round(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.round(), 2.0f16, TOL_0); + assert_approx_eq!(1.7f16.round(), 2.0f16, TOL_0); + assert_approx_eq!(0.0f16.round(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).round(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).round(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).round(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).round(), -2.0f16, TOL_0); + assert_approx_eq!((-1.7f16).round(), -2.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_round_ties_even() { + assert_approx_eq!(2.5f16.round_ties_even(), 2.0f16, TOL_0); + assert_approx_eq!(1.0f16.round_ties_even(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.round_ties_even(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.round_ties_even(), 2.0f16, TOL_0); + assert_approx_eq!(1.7f16.round_ties_even(), 2.0f16, TOL_0); + assert_approx_eq!(0.0f16.round_ties_even(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).round_ties_even(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).round_ties_even(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).round_ties_even(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).round_ties_even(), -2.0f16, TOL_0); + assert_approx_eq!((-1.7f16).round_ties_even(), -2.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_trunc() { + assert_approx_eq!(1.0f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(1.7f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(0.0f16.trunc(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).trunc(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).trunc(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).trunc(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).trunc(), -1.0f16, TOL_0); + assert_approx_eq!((-1.7f16).trunc(), -1.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_fract() { + assert_approx_eq!(1.0f16.fract(), 0.0f16, TOL_0); + assert_approx_eq!(1.3f16.fract(), 0.3f16, TOL_0); + assert_approx_eq!(1.5f16.fract(), 0.5f16, TOL_0); + assert_approx_eq!(1.7f16.fract(), 0.7f16, TOL_0); + assert_approx_eq!(0.0f16.fract(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).fract(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).fract(), -0.0f16, TOL_0); + assert_approx_eq!((-1.3f16).fract(), -0.3f16, TOL_0); + assert_approx_eq!((-1.5f16).fract(), -0.5f16, TOL_0); + assert_approx_eq!((-1.7f16).fract(), -0.7f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_abs() { + assert_eq!(f16::INFINITY.abs(), f16::INFINITY); + assert_eq!(1f16.abs(), 1f16); + assert_eq!(0f16.abs(), 0f16); + assert_eq!((-0f16).abs(), 0f16); + assert_eq!((-1f16).abs(), 1f16); + assert_eq!(f16::NEG_INFINITY.abs(), f16::INFINITY); + assert_eq!((1f16 / f16::NEG_INFINITY).abs(), 0f16); + assert!(f16::NAN.abs().is_nan()); +} + +#[test] +fn test_is_sign_positive() { + assert!(f16::INFINITY.is_sign_positive()); + assert!(1f16.is_sign_positive()); + assert!(0f16.is_sign_positive()); + assert!(!(-0f16).is_sign_positive()); + assert!(!(-1f16).is_sign_positive()); + assert!(!f16::NEG_INFINITY.is_sign_positive()); + assert!(!(1f16 / f16::NEG_INFINITY).is_sign_positive()); + assert!(f16::NAN.is_sign_positive()); + assert!(!(-f16::NAN).is_sign_positive()); +} + +#[test] +fn test_is_sign_negative() { + assert!(!f16::INFINITY.is_sign_negative()); + assert!(!1f16.is_sign_negative()); + assert!(!0f16.is_sign_negative()); + assert!((-0f16).is_sign_negative()); + assert!((-1f16).is_sign_negative()); + assert!(f16::NEG_INFINITY.is_sign_negative()); + assert!((1f16 / f16::NEG_INFINITY).is_sign_negative()); + assert!(!f16::NAN.is_sign_negative()); + assert!((-f16::NAN).is_sign_negative()); +} + +#[test] +fn test_next_up() { + let tiny = f16::from_bits(TINY_BITS); + let tiny_up = f16::from_bits(TINY_UP_BITS); + let max_down = f16::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f16::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f16::from_bits(SMALLEST_NORMAL_BITS); + assert_f16_biteq!(f16::NEG_INFINITY.next_up(), f16::MIN); + assert_f16_biteq!(f16::MIN.next_up(), -max_down); + assert_f16_biteq!((-1.0 - f16::EPSILON).next_up(), -1.0); + assert_f16_biteq!((-smallest_normal).next_up(), -largest_subnormal); + assert_f16_biteq!((-tiny_up).next_up(), -tiny); + assert_f16_biteq!((-tiny).next_up(), -0.0f16); + assert_f16_biteq!((-0.0f16).next_up(), tiny); + assert_f16_biteq!(0.0f16.next_up(), tiny); + assert_f16_biteq!(tiny.next_up(), tiny_up); + assert_f16_biteq!(largest_subnormal.next_up(), smallest_normal); + assert_f16_biteq!(1.0f16.next_up(), 1.0 + f16::EPSILON); + assert_f16_biteq!(f16::MAX.next_up(), f16::INFINITY); + assert_f16_biteq!(f16::INFINITY.next_up(), f16::INFINITY); + + // Check that NaNs roundtrip. + let nan0 = f16::NAN; + let nan1 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK1); + let nan2 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK2); + assert_f16_biteq!(nan0.next_up(), nan0); + assert_f16_biteq!(nan1.next_up(), nan1); + assert_f16_biteq!(nan2.next_up(), nan2); +} + +#[test] +fn test_next_down() { + let tiny = f16::from_bits(TINY_BITS); + let tiny_up = f16::from_bits(TINY_UP_BITS); + let max_down = f16::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f16::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f16::from_bits(SMALLEST_NORMAL_BITS); + assert_f16_biteq!(f16::NEG_INFINITY.next_down(), f16::NEG_INFINITY); + assert_f16_biteq!(f16::MIN.next_down(), f16::NEG_INFINITY); + assert_f16_biteq!((-max_down).next_down(), f16::MIN); + assert_f16_biteq!((-1.0f16).next_down(), -1.0 - f16::EPSILON); + assert_f16_biteq!((-largest_subnormal).next_down(), -smallest_normal); + assert_f16_biteq!((-tiny).next_down(), -tiny_up); + assert_f16_biteq!((-0.0f16).next_down(), -tiny); + assert_f16_biteq!((0.0f16).next_down(), -tiny); + assert_f16_biteq!(tiny.next_down(), 0.0f16); + assert_f16_biteq!(tiny_up.next_down(), tiny); + assert_f16_biteq!(smallest_normal.next_down(), largest_subnormal); + assert_f16_biteq!((1.0 + f16::EPSILON).next_down(), 1.0f16); + assert_f16_biteq!(f16::MAX.next_down(), max_down); + assert_f16_biteq!(f16::INFINITY.next_down(), f16::MAX); + + // Check that NaNs roundtrip. + let nan0 = f16::NAN; + let nan1 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK1); + let nan2 = f16::from_bits(f16::NAN.to_bits() ^ NAN_MASK2); + assert_f16_biteq!(nan0.next_down(), nan0); + assert_f16_biteq!(nan1.next_down(), nan1); + assert_f16_biteq!(nan2.next_down(), nan2); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_mul_add() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_approx_eq!(12.3f16.mul_add(4.5, 6.7), 62.05, TOL_P2); + assert_approx_eq!((-12.3f16).mul_add(-4.5, -6.7), 48.65, TOL_P2); + assert_approx_eq!(0.0f16.mul_add(8.9, 1.2), 1.2, TOL_0); + assert_approx_eq!(3.4f16.mul_add(-0.0, 5.6), 5.6, TOL_0); + assert!(nan.mul_add(7.8, 9.0).is_nan()); + assert_eq!(inf.mul_add(7.8, 9.0), inf); + assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_eq!(8.9f16.mul_add(inf, 3.2), inf); + assert_eq!((-3.2f16).mul_add(2.4, neg_inf), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_recip() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(1.0f16.recip(), 1.0); + assert_eq!(2.0f16.recip(), 0.5); + assert_eq!((-0.4f16).recip(), -2.5); + assert_eq!(0.0f16.recip(), inf); + assert_approx_eq!(f16::MAX.recip(), 1.526624e-5f16, 1e-4); + assert!(nan.recip().is_nan()); + assert_eq!(inf.recip(), 0.0); + assert_eq!(neg_inf.recip(), 0.0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_powi() { + // FIXME(llvm19): LLVM misoptimizes `powi.f16` + // + // let nan: f16 = f16::NAN; + // let inf: f16 = f16::INFINITY; + // let neg_inf: f16 = f16::NEG_INFINITY; + // assert_eq!(1.0f16.powi(1), 1.0); + // assert_approx_eq!((-3.1f16).powi(2), 9.61, TOL_0); + // assert_approx_eq!(5.9f16.powi(-2), 0.028727, TOL_N2); + // assert_eq!(8.3f16.powi(0), 1.0); + // assert!(nan.powi(2).is_nan()); + // assert_eq!(inf.powi(3), inf); + // assert_eq!(neg_inf.powi(2), inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_powf() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(1.0f16.powf(1.0), 1.0); + assert_approx_eq!(3.4f16.powf(4.5), 246.408183, TOL_P2); + assert_approx_eq!(2.7f16.powf(-3.2), 0.041652, TOL_N2); + assert_approx_eq!((-3.1f16).powf(2.0), 9.61, TOL_P2); + assert_approx_eq!(5.9f16.powf(-2.0), 0.028727, TOL_N2); + assert_eq!(8.3f16.powf(0.0), 1.0); + assert!(nan.powf(2.0).is_nan()); + assert_eq!(inf.powf(2.0), inf); + assert_eq!(neg_inf.powf(3.0), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_sqrt_domain() { + assert!(f16::NAN.sqrt().is_nan()); + assert!(f16::NEG_INFINITY.sqrt().is_nan()); + assert!((-1.0f16).sqrt().is_nan()); + assert_eq!((-0.0f16).sqrt(), -0.0); + assert_eq!(0.0f16.sqrt(), 0.0); + assert_eq!(1.0f16.sqrt(), 1.0); + assert_eq!(f16::INFINITY.sqrt(), f16::INFINITY); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_exp() { + assert_eq!(1.0, 0.0f16.exp()); + assert_approx_eq!(2.718282, 1.0f16.exp(), TOL_0); + assert_approx_eq!(148.413159, 5.0f16.exp(), TOL_0); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf, inf.exp()); + assert_eq!(0.0, neg_inf.exp()); + assert!(nan.exp().is_nan()); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_exp2() { + assert_eq!(32.0, 5.0f16.exp2()); + assert_eq!(1.0, 0.0f16.exp2()); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf, inf.exp2()); + assert_eq!(0.0, neg_inf.exp2()); + assert!(nan.exp2().is_nan()); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_ln() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_approx_eq!(1.0f16.exp().ln(), 1.0, TOL_0); + assert!(nan.ln().is_nan()); + assert_eq!(inf.ln(), inf); + assert!(neg_inf.ln().is_nan()); + assert!((-2.3f16).ln().is_nan()); + assert_eq!((-0.0f16).ln(), neg_inf); + assert_eq!(0.0f16.ln(), neg_inf); + assert_approx_eq!(4.0f16.ln(), 1.386294, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_log() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(10.0f16.log(10.0), 1.0); + assert_approx_eq!(2.3f16.log(3.5), 0.664858, TOL_0); + assert_eq!(1.0f16.exp().log(1.0f16.exp()), 1.0); + assert!(1.0f16.log(1.0).is_nan()); + assert!(1.0f16.log(-13.9).is_nan()); + assert!(nan.log(2.3).is_nan()); + assert_eq!(inf.log(10.0), inf); + assert!(neg_inf.log(8.8).is_nan()); + assert!((-2.3f16).log(0.1).is_nan()); + assert_eq!((-0.0f16).log(2.0), neg_inf); + assert_eq!(0.0f16.log(7.0), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_log2() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_approx_eq!(10.0f16.log2(), 3.321928, TOL_0); + assert_approx_eq!(2.3f16.log2(), 1.201634, TOL_0); + assert_approx_eq!(1.0f16.exp().log2(), 1.442695, TOL_0); + assert!(nan.log2().is_nan()); + assert_eq!(inf.log2(), inf); + assert!(neg_inf.log2().is_nan()); + assert!((-2.3f16).log2().is_nan()); + assert_eq!((-0.0f16).log2(), neg_inf); + assert_eq!(0.0f16.log2(), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_log10() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(10.0f16.log10(), 1.0); + assert_approx_eq!(2.3f16.log10(), 0.361728, TOL_0); + assert_approx_eq!(1.0f16.exp().log10(), 0.434294, TOL_0); + assert_eq!(1.0f16.log10(), 0.0); + assert!(nan.log10().is_nan()); + assert_eq!(inf.log10(), inf); + assert!(neg_inf.log10().is_nan()); + assert!((-2.3f16).log10().is_nan()); + assert_eq!((-0.0f16).log10(), neg_inf); + assert_eq!(0.0f16.log10(), neg_inf); +} + +#[test] +fn test_to_degrees() { + let pi: f16 = consts::PI; + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(0.0f16.to_degrees(), 0.0); + assert_approx_eq!((-5.8f16).to_degrees(), -332.315521, TOL_P2); + assert_approx_eq!(pi.to_degrees(), 180.0, TOL_P2); + assert!(nan.to_degrees().is_nan()); + assert_eq!(inf.to_degrees(), inf); + assert_eq!(neg_inf.to_degrees(), neg_inf); + assert_eq!(1_f16.to_degrees(), 57.2957795130823208767981548141051703); +} + +#[test] +fn test_to_radians() { + let pi: f16 = consts::PI; + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(0.0f16.to_radians(), 0.0); + assert_approx_eq!(154.6f16.to_radians(), 2.698279, TOL_0); + assert_approx_eq!((-332.31f16).to_radians(), -5.799903, TOL_0); + assert_approx_eq!(180.0f16.to_radians(), pi, TOL_0); + assert!(nan.to_radians().is_nan()); + assert_eq!(inf.to_radians(), inf); + assert_eq!(neg_inf.to_radians(), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_asinh() { + assert_eq!(0.0f16.asinh(), 0.0f16); + assert_eq!((-0.0f16).asinh(), -0.0f16); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf.asinh(), inf); + assert_eq!(neg_inf.asinh(), neg_inf); + assert!(nan.asinh().is_nan()); + assert!((-0.0f16).asinh().is_sign_negative()); + // issue 63271 + assert_approx_eq!(2.0f16.asinh(), 1.443635475178810342493276740273105f16, TOL_0); + assert_approx_eq!((-2.0f16).asinh(), -1.443635475178810342493276740273105f16, TOL_0); + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!((-200.0f16).asinh(), -5.991470797049389, TOL_0); + + // test for low accuracy from issue 104548 + assert_approx_eq!(10.0f16, 10.0f16.sinh().asinh(), TOL_0); + // mul needed for approximate comparison to be meaningful + assert_approx_eq!(1.0f16, 1e-3f16.sinh().asinh() * 1e3f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_acosh() { + assert_eq!(1.0f16.acosh(), 0.0f16); + assert!(0.999f16.acosh().is_nan()); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf.acosh(), inf); + assert!(neg_inf.acosh().is_nan()); + assert!(nan.acosh().is_nan()); + assert_approx_eq!(2.0f16.acosh(), 1.31695789692481670862504634730796844f16, TOL_0); + assert_approx_eq!(3.0f16.acosh(), 1.76274717403908605046521864995958461f16, TOL_0); + + // test for low accuracy from issue 104548 + assert_approx_eq!(10.0f16, 10.0f16.cosh().acosh(), TOL_P2); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_atanh() { + assert_eq!(0.0f16.atanh(), 0.0f16); + assert_eq!((-0.0f16).atanh(), -0.0f16); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(1.0f16.atanh(), inf); + assert_eq!((-1.0f16).atanh(), neg_inf); + assert!(2f16.atanh().atanh().is_nan()); + assert!((-2f16).atanh().atanh().is_nan()); + assert!(inf.atanh().is_nan()); + assert!(neg_inf.atanh().is_nan()); + assert!(nan.atanh().is_nan()); + assert_approx_eq!(0.5f16.atanh(), 0.54930614433405484569762261846126285f16, TOL_0); + assert_approx_eq!((-0.5f16).atanh(), -0.54930614433405484569762261846126285f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_gamma() { + // precision can differ among platforms + assert_approx_eq!(1.0f16.gamma(), 1.0f16, TOL_0); + assert_approx_eq!(2.0f16.gamma(), 1.0f16, TOL_0); + assert_approx_eq!(3.0f16.gamma(), 2.0f16, TOL_0); + assert_approx_eq!(4.0f16.gamma(), 6.0f16, TOL_0); + assert_approx_eq!(5.0f16.gamma(), 24.0f16, TOL_0); + assert_approx_eq!(0.5f16.gamma(), consts::PI.sqrt(), TOL_0); + assert_approx_eq!((-0.5f16).gamma(), -2.0 * consts::PI.sqrt(), TOL_0); + assert_eq!(0.0f16.gamma(), f16::INFINITY); + assert_eq!((-0.0f16).gamma(), f16::NEG_INFINITY); + assert!((-1.0f16).gamma().is_nan()); + assert!((-2.0f16).gamma().is_nan()); + assert!(f16::NAN.gamma().is_nan()); + assert!(f16::NEG_INFINITY.gamma().is_nan()); + assert_eq!(f16::INFINITY.gamma(), f16::INFINITY); + assert_eq!(171.71f16.gamma(), f16::INFINITY); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_ln_gamma() { + assert_approx_eq!(1.0f16.ln_gamma().0, 0.0f16, TOL_0); + assert_eq!(1.0f16.ln_gamma().1, 1); + assert_approx_eq!(2.0f16.ln_gamma().0, 0.0f16, TOL_0); + assert_eq!(2.0f16.ln_gamma().1, 1); + assert_approx_eq!(3.0f16.ln_gamma().0, 2.0f16.ln(), TOL_0); + assert_eq!(3.0f16.ln_gamma().1, 1); + assert_approx_eq!((-0.5f16).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), TOL_0); + assert_eq!((-0.5f16).ln_gamma().1, -1); +} + +#[test] +fn test_real_consts() { + // FIXME(f16_f128): add math tests when available + + let pi: f16 = consts::PI; + let frac_pi_2: f16 = consts::FRAC_PI_2; + let frac_pi_3: f16 = consts::FRAC_PI_3; + let frac_pi_4: f16 = consts::FRAC_PI_4; + let frac_pi_6: f16 = consts::FRAC_PI_6; + let frac_pi_8: f16 = consts::FRAC_PI_8; + let frac_1_pi: f16 = consts::FRAC_1_PI; + let frac_2_pi: f16 = consts::FRAC_2_PI; + + assert_approx_eq!(frac_pi_2, pi / 2f16, TOL_0); + assert_approx_eq!(frac_pi_3, pi / 3f16, TOL_0); + assert_approx_eq!(frac_pi_4, pi / 4f16, TOL_0); + assert_approx_eq!(frac_pi_6, pi / 6f16, TOL_0); + assert_approx_eq!(frac_pi_8, pi / 8f16, TOL_0); + assert_approx_eq!(frac_1_pi, 1f16 / pi, TOL_0); + assert_approx_eq!(frac_2_pi, 2f16 / pi, TOL_0); + + #[cfg(reliable_f16_math)] + { + let frac_2_sqrtpi: f16 = consts::FRAC_2_SQRT_PI; + let sqrt2: f16 = consts::SQRT_2; + let frac_1_sqrt2: f16 = consts::FRAC_1_SQRT_2; + let e: f16 = consts::E; + let log2_e: f16 = consts::LOG2_E; + let log10_e: f16 = consts::LOG10_E; + let ln_2: f16 = consts::LN_2; + let ln_10: f16 = consts::LN_10; + + assert_approx_eq!(frac_2_sqrtpi, 2f16 / pi.sqrt(), TOL_0); + assert_approx_eq!(sqrt2, 2f16.sqrt(), TOL_0); + assert_approx_eq!(frac_1_sqrt2, 1f16 / 2f16.sqrt(), TOL_0); + assert_approx_eq!(log2_e, e.log2(), TOL_0); + assert_approx_eq!(log10_e, e.log10(), TOL_0); + assert_approx_eq!(ln_2, 2f16.ln(), TOL_0); + assert_approx_eq!(ln_10, 10f16.ln(), TOL_0); + } +} + +#[test] +fn test_float_bits_conv() { + assert_eq!((1f16).to_bits(), 0x3c00); + assert_eq!((12.5f16).to_bits(), 0x4a40); + assert_eq!((1337f16).to_bits(), 0x6539); + assert_eq!((-14.25f16).to_bits(), 0xcb20); + assert_approx_eq!(f16::from_bits(0x3c00), 1.0, TOL_0); + assert_approx_eq!(f16::from_bits(0x4a40), 12.5, TOL_0); + assert_approx_eq!(f16::from_bits(0x6539), 1337.0, TOL_P4); + assert_approx_eq!(f16::from_bits(0xcb20), -14.25, TOL_0); + + // Check that NaNs roundtrip their bits regardless of signaling-ness + let masked_nan1 = f16::NAN.to_bits() ^ NAN_MASK1; + let masked_nan2 = f16::NAN.to_bits() ^ NAN_MASK2; + assert!(f16::from_bits(masked_nan1).is_nan()); + assert!(f16::from_bits(masked_nan2).is_nan()); + + assert_eq!(f16::from_bits(masked_nan1).to_bits(), masked_nan1); + assert_eq!(f16::from_bits(masked_nan2).to_bits(), masked_nan2); +} + +#[test] +#[should_panic] +fn test_clamp_min_greater_than_max() { + let _ = 1.0f16.clamp(3.0, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_min_is_nan() { + let _ = 1.0f16.clamp(f16::NAN, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_max_is_nan() { + let _ = 1.0f16.clamp(3.0, f16::NAN); +} + +#[test] +fn test_total_cmp() { + use core::cmp::Ordering; + + fn quiet_bit_mask() -> u16 { + 1 << (f16::MANTISSA_DIGITS - 2) + } + + // FIXME(f16_f128): test subnormals when powf is available + // fn min_subnorm() -> f16 { + // f16::MIN_POSITIVE / f16::powf(2.0, f16::MANTISSA_DIGITS as f16 - 1.0) + // } + + // fn max_subnorm() -> f16 { + // f16::MIN_POSITIVE - min_subnorm() + // } + + fn q_nan() -> f16 { + f16::from_bits(f16::NAN.to_bits() | quiet_bit_mask()) + } + + fn s_nan() -> f16 { + f16::from_bits((f16::NAN.to_bits() & !quiet_bit_mask()) + 42) + } + + assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Equal, (-f16::INFINITY).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Equal, (-f16::MAX).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Equal, (-2.5_f16).total_cmp(&-2.5)); + assert_eq!(Ordering::Equal, (-1.0_f16).total_cmp(&-1.0)); + assert_eq!(Ordering::Equal, (-1.5_f16).total_cmp(&-1.5)); + assert_eq!(Ordering::Equal, (-0.5_f16).total_cmp(&-0.5)); + assert_eq!(Ordering::Equal, (-f16::MIN_POSITIVE).total_cmp(&-f16::MIN_POSITIVE)); + // assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Equal, (-0.0_f16).total_cmp(&-0.0)); + assert_eq!(Ordering::Equal, 0.0_f16.total_cmp(&0.0)); + // assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Equal, f16::MIN_POSITIVE.total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, 0.5_f16.total_cmp(&0.5)); + assert_eq!(Ordering::Equal, 1.0_f16.total_cmp(&1.0)); + assert_eq!(Ordering::Equal, 1.5_f16.total_cmp(&1.5)); + assert_eq!(Ordering::Equal, 2.5_f16.total_cmp(&2.5)); + assert_eq!(Ordering::Equal, f16::MAX.total_cmp(&f16::MAX)); + assert_eq!(Ordering::Equal, f16::INFINITY.total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); + assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Less, (-f16::INFINITY).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Less, (-f16::MAX).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-2.5_f16).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-1.5_f16).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-1.0_f16).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-0.5_f16).total_cmp(&-f16::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-f16::MIN_POSITIVE).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); + // assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-0.0_f16).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, 0.0_f16.total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); + // assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Less, f16::MIN_POSITIVE.total_cmp(&0.5)); + assert_eq!(Ordering::Less, 0.5_f16.total_cmp(&1.0)); + assert_eq!(Ordering::Less, 1.0_f16.total_cmp(&1.5)); + assert_eq!(Ordering::Less, 1.5_f16.total_cmp(&2.5)); + assert_eq!(Ordering::Less, 2.5_f16.total_cmp(&f16::MAX)); + assert_eq!(Ordering::Less, f16::MAX.total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Less, f16::INFINITY.total_cmp(&s_nan())); + assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Greater, (-f16::INFINITY).total_cmp(&-s_nan())); + assert_eq!(Ordering::Greater, (-f16::MAX).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Greater, (-2.5_f16).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Greater, (-1.5_f16).total_cmp(&-2.5)); + assert_eq!(Ordering::Greater, (-1.0_f16).total_cmp(&-1.5)); + assert_eq!(Ordering::Greater, (-0.5_f16).total_cmp(&-1.0)); + assert_eq!(Ordering::Greater, (-f16::MIN_POSITIVE).total_cmp(&-0.5)); + // assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f16::MIN_POSITIVE)); + // assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Greater, (-0.0_f16).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Greater, 0.0_f16.total_cmp(&-0.0)); + // assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); + // assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Greater, f16::MIN_POSITIVE.total_cmp(&max_subnorm())); + assert_eq!(Ordering::Greater, 0.5_f16.total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, 1.0_f16.total_cmp(&0.5)); + assert_eq!(Ordering::Greater, 1.5_f16.total_cmp(&1.0)); + assert_eq!(Ordering::Greater, 2.5_f16.total_cmp(&1.5)); + assert_eq!(Ordering::Greater, f16::MAX.total_cmp(&2.5)); + assert_eq!(Ordering::Greater, f16::INFINITY.total_cmp(&f16::MAX)); + assert_eq!(Ordering::Greater, s_nan().total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f16::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f16::MIN_POSITIVE)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); + // assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f16::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); +} diff --git a/library/std/tests/floats/f32.rs b/library/std/tests/floats/f32.rs new file mode 100644 index 0000000000000..bf7641986ada8 --- /dev/null +++ b/library/std/tests/floats/f32.rs @@ -0,0 +1,917 @@ +use std::f32::consts; +use std::num::FpCategory as Fp; + +/// Smallest number +const TINY_BITS: u32 = 0x1; + +/// Next smallest number +const TINY_UP_BITS: u32 = 0x2; + +/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 +const MAX_DOWN_BITS: u32 = 0x7f7f_fffe; + +/// Zeroed exponent, full significant +const LARGEST_SUBNORMAL_BITS: u32 = 0x007f_ffff; + +/// Exponent = 0b1, zeroed significand +const SMALLEST_NORMAL_BITS: u32 = 0x0080_0000; + +/// First pattern over the mantissa +const NAN_MASK1: u32 = 0x002a_aaaa; + +/// Second pattern over the mantissa +const NAN_MASK2: u32 = 0x0055_5555; + +#[allow(unused_macros)] +macro_rules! assert_f32_biteq { + ($left : expr, $right : expr) => { + let l: &f32 = &$left; + let r: &f32 = &$right; + let lb = l.to_bits(); + let rb = r.to_bits(); + assert_eq!(lb, rb, "float {l} ({lb:#010x}) is not bitequal to {r} ({rb:#010x})"); + }; +} + +#[test] +fn test_num_f32() { + crate::test_num(10f32, 2f32); +} + +#[test] +fn test_min_nan() { + assert_eq!(f32::NAN.min(2.0), 2.0); + assert_eq!(2.0f32.min(f32::NAN), 2.0); +} + +#[test] +fn test_max_nan() { + assert_eq!(f32::NAN.max(2.0), 2.0); + assert_eq!(2.0f32.max(f32::NAN), 2.0); +} + +#[test] +fn test_minimum() { + assert!(f32::NAN.minimum(2.0).is_nan()); + assert!(2.0f32.minimum(f32::NAN).is_nan()); +} + +#[test] +fn test_maximum() { + assert!(f32::NAN.maximum(2.0).is_nan()); + assert!(2.0f32.maximum(f32::NAN).is_nan()); +} + +#[test] +fn test_nan() { + let nan: f32 = f32::NAN; + assert!(nan.is_nan()); + assert!(!nan.is_infinite()); + assert!(!nan.is_finite()); + assert!(!nan.is_normal()); + assert!(nan.is_sign_positive()); + assert!(!nan.is_sign_negative()); + assert_eq!(Fp::Nan, nan.classify()); +} + +#[test] +fn test_infinity() { + let inf: f32 = f32::INFINITY; + assert!(inf.is_infinite()); + assert!(!inf.is_finite()); + assert!(inf.is_sign_positive()); + assert!(!inf.is_sign_negative()); + assert!(!inf.is_nan()); + assert!(!inf.is_normal()); + assert_eq!(Fp::Infinite, inf.classify()); +} + +#[test] +fn test_neg_infinity() { + let neg_inf: f32 = f32::NEG_INFINITY; + assert!(neg_inf.is_infinite()); + assert!(!neg_inf.is_finite()); + assert!(!neg_inf.is_sign_positive()); + assert!(neg_inf.is_sign_negative()); + assert!(!neg_inf.is_nan()); + assert!(!neg_inf.is_normal()); + assert_eq!(Fp::Infinite, neg_inf.classify()); +} + +#[test] +fn test_zero() { + let zero: f32 = 0.0f32; + assert_eq!(0.0, zero); + assert!(!zero.is_infinite()); + assert!(zero.is_finite()); + assert!(zero.is_sign_positive()); + assert!(!zero.is_sign_negative()); + assert!(!zero.is_nan()); + assert!(!zero.is_normal()); + assert_eq!(Fp::Zero, zero.classify()); +} + +#[test] +fn test_neg_zero() { + let neg_zero: f32 = -0.0; + assert_eq!(0.0, neg_zero); + assert!(!neg_zero.is_infinite()); + assert!(neg_zero.is_finite()); + assert!(!neg_zero.is_sign_positive()); + assert!(neg_zero.is_sign_negative()); + assert!(!neg_zero.is_nan()); + assert!(!neg_zero.is_normal()); + assert_eq!(Fp::Zero, neg_zero.classify()); +} + +#[test] +fn test_one() { + let one: f32 = 1.0f32; + assert_eq!(1.0, one); + assert!(!one.is_infinite()); + assert!(one.is_finite()); + assert!(one.is_sign_positive()); + assert!(!one.is_sign_negative()); + assert!(!one.is_nan()); + assert!(one.is_normal()); + assert_eq!(Fp::Normal, one.classify()); +} + +#[test] +fn test_is_nan() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert!(nan.is_nan()); + assert!(!0.0f32.is_nan()); + assert!(!5.3f32.is_nan()); + assert!(!(-10.732f32).is_nan()); + assert!(!inf.is_nan()); + assert!(!neg_inf.is_nan()); +} + +#[test] +fn test_is_infinite() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert!(!nan.is_infinite()); + assert!(inf.is_infinite()); + assert!(neg_inf.is_infinite()); + assert!(!0.0f32.is_infinite()); + assert!(!42.8f32.is_infinite()); + assert!(!(-109.2f32).is_infinite()); +} + +#[test] +fn test_is_finite() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert!(!nan.is_finite()); + assert!(!inf.is_finite()); + assert!(!neg_inf.is_finite()); + assert!(0.0f32.is_finite()); + assert!(42.8f32.is_finite()); + assert!((-109.2f32).is_finite()); +} + +#[test] +fn test_is_normal() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + let zero: f32 = 0.0f32; + let neg_zero: f32 = -0.0; + assert!(!nan.is_normal()); + assert!(!inf.is_normal()); + assert!(!neg_inf.is_normal()); + assert!(!zero.is_normal()); + assert!(!neg_zero.is_normal()); + assert!(1f32.is_normal()); + assert!(1e-37f32.is_normal()); + assert!(!1e-38f32.is_normal()); +} + +#[test] +fn test_classify() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + let zero: f32 = 0.0f32; + let neg_zero: f32 = -0.0; + assert_eq!(nan.classify(), Fp::Nan); + assert_eq!(inf.classify(), Fp::Infinite); + assert_eq!(neg_inf.classify(), Fp::Infinite); + assert_eq!(zero.classify(), Fp::Zero); + assert_eq!(neg_zero.classify(), Fp::Zero); + assert_eq!(1f32.classify(), Fp::Normal); + assert_eq!(1e-37f32.classify(), Fp::Normal); + assert_eq!(1e-38f32.classify(), Fp::Subnormal); +} + +#[test] +fn test_floor() { + assert_approx_eq!(1.0f32.floor(), 1.0f32); + assert_approx_eq!(1.3f32.floor(), 1.0f32); + assert_approx_eq!(1.5f32.floor(), 1.0f32); + assert_approx_eq!(1.7f32.floor(), 1.0f32); + assert_approx_eq!(0.0f32.floor(), 0.0f32); + assert_approx_eq!((-0.0f32).floor(), -0.0f32); + assert_approx_eq!((-1.0f32).floor(), -1.0f32); + assert_approx_eq!((-1.3f32).floor(), -2.0f32); + assert_approx_eq!((-1.5f32).floor(), -2.0f32); + assert_approx_eq!((-1.7f32).floor(), -2.0f32); +} + +#[test] +fn test_ceil() { + assert_approx_eq!(1.0f32.ceil(), 1.0f32); + assert_approx_eq!(1.3f32.ceil(), 2.0f32); + assert_approx_eq!(1.5f32.ceil(), 2.0f32); + assert_approx_eq!(1.7f32.ceil(), 2.0f32); + assert_approx_eq!(0.0f32.ceil(), 0.0f32); + assert_approx_eq!((-0.0f32).ceil(), -0.0f32); + assert_approx_eq!((-1.0f32).ceil(), -1.0f32); + assert_approx_eq!((-1.3f32).ceil(), -1.0f32); + assert_approx_eq!((-1.5f32).ceil(), -1.0f32); + assert_approx_eq!((-1.7f32).ceil(), -1.0f32); +} + +#[test] +fn test_round() { + assert_approx_eq!(2.5f32.round(), 3.0f32); + assert_approx_eq!(1.0f32.round(), 1.0f32); + assert_approx_eq!(1.3f32.round(), 1.0f32); + assert_approx_eq!(1.5f32.round(), 2.0f32); + assert_approx_eq!(1.7f32.round(), 2.0f32); + assert_approx_eq!(0.0f32.round(), 0.0f32); + assert_approx_eq!((-0.0f32).round(), -0.0f32); + assert_approx_eq!((-1.0f32).round(), -1.0f32); + assert_approx_eq!((-1.3f32).round(), -1.0f32); + assert_approx_eq!((-1.5f32).round(), -2.0f32); + assert_approx_eq!((-1.7f32).round(), -2.0f32); +} + +#[test] +fn test_round_ties_even() { + assert_approx_eq!(2.5f32.round_ties_even(), 2.0f32); + assert_approx_eq!(1.0f32.round_ties_even(), 1.0f32); + assert_approx_eq!(1.3f32.round_ties_even(), 1.0f32); + assert_approx_eq!(1.5f32.round_ties_even(), 2.0f32); + assert_approx_eq!(1.7f32.round_ties_even(), 2.0f32); + assert_approx_eq!(0.0f32.round_ties_even(), 0.0f32); + assert_approx_eq!((-0.0f32).round_ties_even(), -0.0f32); + assert_approx_eq!((-1.0f32).round_ties_even(), -1.0f32); + assert_approx_eq!((-1.3f32).round_ties_even(), -1.0f32); + assert_approx_eq!((-1.5f32).round_ties_even(), -2.0f32); + assert_approx_eq!((-1.7f32).round_ties_even(), -2.0f32); +} + +#[test] +fn test_trunc() { + assert_approx_eq!(1.0f32.trunc(), 1.0f32); + assert_approx_eq!(1.3f32.trunc(), 1.0f32); + assert_approx_eq!(1.5f32.trunc(), 1.0f32); + assert_approx_eq!(1.7f32.trunc(), 1.0f32); + assert_approx_eq!(0.0f32.trunc(), 0.0f32); + assert_approx_eq!((-0.0f32).trunc(), -0.0f32); + assert_approx_eq!((-1.0f32).trunc(), -1.0f32); + assert_approx_eq!((-1.3f32).trunc(), -1.0f32); + assert_approx_eq!((-1.5f32).trunc(), -1.0f32); + assert_approx_eq!((-1.7f32).trunc(), -1.0f32); +} + +#[test] +fn test_fract() { + assert_approx_eq!(1.0f32.fract(), 0.0f32); + assert_approx_eq!(1.3f32.fract(), 0.3f32); + assert_approx_eq!(1.5f32.fract(), 0.5f32); + assert_approx_eq!(1.7f32.fract(), 0.7f32); + assert_approx_eq!(0.0f32.fract(), 0.0f32); + assert_approx_eq!((-0.0f32).fract(), -0.0f32); + assert_approx_eq!((-1.0f32).fract(), -0.0f32); + assert_approx_eq!((-1.3f32).fract(), -0.3f32); + assert_approx_eq!((-1.5f32).fract(), -0.5f32); + assert_approx_eq!((-1.7f32).fract(), -0.7f32); +} + +#[test] +fn test_abs() { + assert_eq!(f32::INFINITY.abs(), f32::INFINITY); + assert_eq!(1f32.abs(), 1f32); + assert_eq!(0f32.abs(), 0f32); + assert_eq!((-0f32).abs(), 0f32); + assert_eq!((-1f32).abs(), 1f32); + assert_eq!(f32::NEG_INFINITY.abs(), f32::INFINITY); + assert_eq!((1f32 / f32::NEG_INFINITY).abs(), 0f32); + assert!(f32::NAN.abs().is_nan()); +} + +#[test] +fn test_signum() { + assert_eq!(f32::INFINITY.signum(), 1f32); + assert_eq!(1f32.signum(), 1f32); + assert_eq!(0f32.signum(), 1f32); + assert_eq!((-0f32).signum(), -1f32); + assert_eq!((-1f32).signum(), -1f32); + assert_eq!(f32::NEG_INFINITY.signum(), -1f32); + assert_eq!((1f32 / f32::NEG_INFINITY).signum(), -1f32); + assert!(f32::NAN.signum().is_nan()); +} + +#[test] +fn test_is_sign_positive() { + assert!(f32::INFINITY.is_sign_positive()); + assert!(1f32.is_sign_positive()); + assert!(0f32.is_sign_positive()); + assert!(!(-0f32).is_sign_positive()); + assert!(!(-1f32).is_sign_positive()); + assert!(!f32::NEG_INFINITY.is_sign_positive()); + assert!(!(1f32 / f32::NEG_INFINITY).is_sign_positive()); + assert!(f32::NAN.is_sign_positive()); + assert!(!(-f32::NAN).is_sign_positive()); +} + +#[test] +fn test_is_sign_negative() { + assert!(!f32::INFINITY.is_sign_negative()); + assert!(!1f32.is_sign_negative()); + assert!(!0f32.is_sign_negative()); + assert!((-0f32).is_sign_negative()); + assert!((-1f32).is_sign_negative()); + assert!(f32::NEG_INFINITY.is_sign_negative()); + assert!((1f32 / f32::NEG_INFINITY).is_sign_negative()); + assert!(!f32::NAN.is_sign_negative()); + assert!((-f32::NAN).is_sign_negative()); +} + +#[test] +fn test_next_up() { + let tiny = f32::from_bits(TINY_BITS); + let tiny_up = f32::from_bits(TINY_UP_BITS); + let max_down = f32::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS); + assert_f32_biteq!(f32::NEG_INFINITY.next_up(), f32::MIN); + assert_f32_biteq!(f32::MIN.next_up(), -max_down); + assert_f32_biteq!((-1.0 - f32::EPSILON).next_up(), -1.0); + assert_f32_biteq!((-smallest_normal).next_up(), -largest_subnormal); + assert_f32_biteq!((-tiny_up).next_up(), -tiny); + assert_f32_biteq!((-tiny).next_up(), -0.0f32); + assert_f32_biteq!((-0.0f32).next_up(), tiny); + assert_f32_biteq!(0.0f32.next_up(), tiny); + assert_f32_biteq!(tiny.next_up(), tiny_up); + assert_f32_biteq!(largest_subnormal.next_up(), smallest_normal); + assert_f32_biteq!(1.0f32.next_up(), 1.0 + f32::EPSILON); + assert_f32_biteq!(f32::MAX.next_up(), f32::INFINITY); + assert_f32_biteq!(f32::INFINITY.next_up(), f32::INFINITY); + + // Check that NaNs roundtrip. + let nan0 = f32::NAN; + let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1); + let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2); + assert_f32_biteq!(nan0.next_up(), nan0); + assert_f32_biteq!(nan1.next_up(), nan1); + assert_f32_biteq!(nan2.next_up(), nan2); +} + +#[test] +fn test_next_down() { + let tiny = f32::from_bits(TINY_BITS); + let tiny_up = f32::from_bits(TINY_UP_BITS); + let max_down = f32::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f32::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f32::from_bits(SMALLEST_NORMAL_BITS); + assert_f32_biteq!(f32::NEG_INFINITY.next_down(), f32::NEG_INFINITY); + assert_f32_biteq!(f32::MIN.next_down(), f32::NEG_INFINITY); + assert_f32_biteq!((-max_down).next_down(), f32::MIN); + assert_f32_biteq!((-1.0f32).next_down(), -1.0 - f32::EPSILON); + assert_f32_biteq!((-largest_subnormal).next_down(), -smallest_normal); + assert_f32_biteq!((-tiny).next_down(), -tiny_up); + assert_f32_biteq!((-0.0f32).next_down(), -tiny); + assert_f32_biteq!((0.0f32).next_down(), -tiny); + assert_f32_biteq!(tiny.next_down(), 0.0f32); + assert_f32_biteq!(tiny_up.next_down(), tiny); + assert_f32_biteq!(smallest_normal.next_down(), largest_subnormal); + assert_f32_biteq!((1.0 + f32::EPSILON).next_down(), 1.0f32); + assert_f32_biteq!(f32::MAX.next_down(), max_down); + assert_f32_biteq!(f32::INFINITY.next_down(), f32::MAX); + + // Check that NaNs roundtrip. + let nan0 = f32::NAN; + let nan1 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK1); + let nan2 = f32::from_bits(f32::NAN.to_bits() ^ NAN_MASK2); + assert_f32_biteq!(nan0.next_down(), nan0); + assert_f32_biteq!(nan1.next_down(), nan1); + assert_f32_biteq!(nan2.next_down(), nan2); +} + +#[test] +fn test_mul_add() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_approx_eq!(12.3f32.mul_add(4.5, 6.7), 62.05); + assert_approx_eq!((-12.3f32).mul_add(-4.5, -6.7), 48.65); + assert_approx_eq!(0.0f32.mul_add(8.9, 1.2), 1.2); + assert_approx_eq!(3.4f32.mul_add(-0.0, 5.6), 5.6); + assert!(nan.mul_add(7.8, 9.0).is_nan()); + assert_eq!(inf.mul_add(7.8, 9.0), inf); + assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_eq!(8.9f32.mul_add(inf, 3.2), inf); + assert_eq!((-3.2f32).mul_add(2.4, neg_inf), neg_inf); +} + +#[test] +fn test_recip() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_eq!(1.0f32.recip(), 1.0); + assert_eq!(2.0f32.recip(), 0.5); + assert_eq!((-0.4f32).recip(), -2.5); + assert_eq!(0.0f32.recip(), inf); + assert!(nan.recip().is_nan()); + assert_eq!(inf.recip(), 0.0); + assert_eq!(neg_inf.recip(), 0.0); +} + +#[test] +fn test_powi() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_eq!(1.0f32.powi(1), 1.0); + assert_approx_eq!((-3.1f32).powi(2), 9.61); + assert_approx_eq!(5.9f32.powi(-2), 0.028727); + assert_eq!(8.3f32.powi(0), 1.0); + assert!(nan.powi(2).is_nan()); + assert_eq!(inf.powi(3), inf); + assert_eq!(neg_inf.powi(2), inf); +} + +#[test] +fn test_powf() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_eq!(1.0f32.powf(1.0), 1.0); + assert_approx_eq!(3.4f32.powf(4.5), 246.408218); + assert_approx_eq!(2.7f32.powf(-3.2), 0.041652); + assert_approx_eq!((-3.1f32).powf(2.0), 9.61); + assert_approx_eq!(5.9f32.powf(-2.0), 0.028727); + assert_eq!(8.3f32.powf(0.0), 1.0); + assert!(nan.powf(2.0).is_nan()); + assert_eq!(inf.powf(2.0), inf); + assert_eq!(neg_inf.powf(3.0), neg_inf); +} + +#[test] +fn test_sqrt_domain() { + assert!(f32::NAN.sqrt().is_nan()); + assert!(f32::NEG_INFINITY.sqrt().is_nan()); + assert!((-1.0f32).sqrt().is_nan()); + assert_eq!((-0.0f32).sqrt(), -0.0); + assert_eq!(0.0f32.sqrt(), 0.0); + assert_eq!(1.0f32.sqrt(), 1.0); + assert_eq!(f32::INFINITY.sqrt(), f32::INFINITY); +} + +#[test] +fn test_exp() { + assert_eq!(1.0, 0.0f32.exp()); + assert_approx_eq!(2.718282, 1.0f32.exp()); + assert_approx_eq!(148.413162, 5.0f32.exp()); + + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + let nan: f32 = f32::NAN; + assert_eq!(inf, inf.exp()); + assert_eq!(0.0, neg_inf.exp()); + assert!(nan.exp().is_nan()); +} + +#[test] +fn test_exp2() { + assert_eq!(32.0, 5.0f32.exp2()); + assert_eq!(1.0, 0.0f32.exp2()); + + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + let nan: f32 = f32::NAN; + assert_eq!(inf, inf.exp2()); + assert_eq!(0.0, neg_inf.exp2()); + assert!(nan.exp2().is_nan()); +} + +#[test] +fn test_ln() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_approx_eq!(1.0f32.exp().ln(), 1.0); + assert!(nan.ln().is_nan()); + assert_eq!(inf.ln(), inf); + assert!(neg_inf.ln().is_nan()); + assert!((-2.3f32).ln().is_nan()); + assert_eq!((-0.0f32).ln(), neg_inf); + assert_eq!(0.0f32.ln(), neg_inf); + assert_approx_eq!(4.0f32.ln(), 1.386294); +} + +#[test] +fn test_log() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_eq!(10.0f32.log(10.0), 1.0); + assert_approx_eq!(2.3f32.log(3.5), 0.664858); + assert_eq!(1.0f32.exp().log(1.0f32.exp()), 1.0); + assert!(1.0f32.log(1.0).is_nan()); + assert!(1.0f32.log(-13.9).is_nan()); + assert!(nan.log(2.3).is_nan()); + assert_eq!(inf.log(10.0), inf); + assert!(neg_inf.log(8.8).is_nan()); + assert!((-2.3f32).log(0.1).is_nan()); + assert_eq!((-0.0f32).log(2.0), neg_inf); + assert_eq!(0.0f32.log(7.0), neg_inf); +} + +#[test] +fn test_log2() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_approx_eq!(10.0f32.log2(), 3.321928); + assert_approx_eq!(2.3f32.log2(), 1.201634); + assert_approx_eq!(1.0f32.exp().log2(), 1.442695); + assert!(nan.log2().is_nan()); + assert_eq!(inf.log2(), inf); + assert!(neg_inf.log2().is_nan()); + assert!((-2.3f32).log2().is_nan()); + assert_eq!((-0.0f32).log2(), neg_inf); + assert_eq!(0.0f32.log2(), neg_inf); +} + +#[test] +fn test_log10() { + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_eq!(10.0f32.log10(), 1.0); + assert_approx_eq!(2.3f32.log10(), 0.361728); + assert_approx_eq!(1.0f32.exp().log10(), 0.434294); + assert_eq!(1.0f32.log10(), 0.0); + assert!(nan.log10().is_nan()); + assert_eq!(inf.log10(), inf); + assert!(neg_inf.log10().is_nan()); + assert!((-2.3f32).log10().is_nan()); + assert_eq!((-0.0f32).log10(), neg_inf); + assert_eq!(0.0f32.log10(), neg_inf); +} + +#[test] +fn test_to_degrees() { + let pi: f32 = consts::PI; + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_eq!(0.0f32.to_degrees(), 0.0); + assert_approx_eq!((-5.8f32).to_degrees(), -332.315521); + assert_eq!(pi.to_degrees(), 180.0); + assert!(nan.to_degrees().is_nan()); + assert_eq!(inf.to_degrees(), inf); + assert_eq!(neg_inf.to_degrees(), neg_inf); + assert_eq!(1_f32.to_degrees(), 57.2957795130823208767981548141051703); +} + +#[test] +fn test_to_radians() { + let pi: f32 = consts::PI; + let nan: f32 = f32::NAN; + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + assert_eq!(0.0f32.to_radians(), 0.0); + assert_approx_eq!(154.6f32.to_radians(), 2.698279); + assert_approx_eq!((-332.31f32).to_radians(), -5.799903); + assert_eq!(180.0f32.to_radians(), pi); + assert!(nan.to_radians().is_nan()); + assert_eq!(inf.to_radians(), inf); + assert_eq!(neg_inf.to_radians(), neg_inf); +} + +#[test] +fn test_asinh() { + assert_eq!(0.0f32.asinh(), 0.0f32); + assert_eq!((-0.0f32).asinh(), -0.0f32); + + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + let nan: f32 = f32::NAN; + assert_eq!(inf.asinh(), inf); + assert_eq!(neg_inf.asinh(), neg_inf); + assert!(nan.asinh().is_nan()); + assert!((-0.0f32).asinh().is_sign_negative()); // issue 63271 + assert_approx_eq!(2.0f32.asinh(), 1.443635475178810342493276740273105f32); + assert_approx_eq!((-2.0f32).asinh(), -1.443635475178810342493276740273105f32); + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!((-3000.0f32).asinh(), -8.699514775987968673236893537700647f32); + + // test for low accuracy from issue 104548 + assert_approx_eq!(60.0f32, 60.0f32.sinh().asinh()); + // mul needed for approximate comparison to be meaningful + assert_approx_eq!(1.0f32, 1e-15f32.sinh().asinh() * 1e15f32); +} + +#[test] +fn test_acosh() { + assert_eq!(1.0f32.acosh(), 0.0f32); + assert!(0.999f32.acosh().is_nan()); + + let inf: f32 = f32::INFINITY; + let neg_inf: f32 = f32::NEG_INFINITY; + let nan: f32 = f32::NAN; + assert_eq!(inf.acosh(), inf); + assert!(neg_inf.acosh().is_nan()); + assert!(nan.acosh().is_nan()); + assert_approx_eq!(2.0f32.acosh(), 1.31695789692481670862504634730796844f32); + assert_approx_eq!(3.0f32.acosh(), 1.76274717403908605046521864995958461f32); + + // test for low accuracy from issue 104548 + assert_approx_eq!(60.0f32, 60.0f32.cosh().acosh()); +} + +#[test] +fn test_atanh() { + assert_eq!(0.0f32.atanh(), 0.0f32); + assert_eq!((-0.0f32).atanh(), -0.0f32); + + let inf32: f32 = f32::INFINITY; + let neg_inf32: f32 = f32::NEG_INFINITY; + assert_eq!(1.0f32.atanh(), inf32); + assert_eq!((-1.0f32).atanh(), neg_inf32); + + assert!(2f64.atanh().atanh().is_nan()); + assert!((-2f64).atanh().atanh().is_nan()); + + let inf64: f32 = f32::INFINITY; + let neg_inf64: f32 = f32::NEG_INFINITY; + let nan32: f32 = f32::NAN; + assert!(inf64.atanh().is_nan()); + assert!(neg_inf64.atanh().is_nan()); + assert!(nan32.atanh().is_nan()); + + assert_approx_eq!(0.5f32.atanh(), 0.54930614433405484569762261846126285f32); + assert_approx_eq!((-0.5f32).atanh(), -0.54930614433405484569762261846126285f32); +} + +#[test] +fn test_gamma() { + // precision can differ between platforms + assert_approx_eq!(1.0f32.gamma(), 1.0f32); + assert_approx_eq!(2.0f32.gamma(), 1.0f32); + assert_approx_eq!(3.0f32.gamma(), 2.0f32); + assert_approx_eq!(4.0f32.gamma(), 6.0f32); + assert_approx_eq!(5.0f32.gamma(), 24.0f32); + assert_approx_eq!(0.5f32.gamma(), consts::PI.sqrt()); + assert_approx_eq!((-0.5f32).gamma(), -2.0 * consts::PI.sqrt()); + assert_eq!(0.0f32.gamma(), f32::INFINITY); + assert_eq!((-0.0f32).gamma(), f32::NEG_INFINITY); + assert!((-1.0f32).gamma().is_nan()); + assert!((-2.0f32).gamma().is_nan()); + assert!(f32::NAN.gamma().is_nan()); + assert!(f32::NEG_INFINITY.gamma().is_nan()); + assert_eq!(f32::INFINITY.gamma(), f32::INFINITY); + assert_eq!(171.71f32.gamma(), f32::INFINITY); +} + +#[test] +fn test_ln_gamma() { + assert_approx_eq!(1.0f32.ln_gamma().0, 0.0f32); + assert_eq!(1.0f32.ln_gamma().1, 1); + assert_approx_eq!(2.0f32.ln_gamma().0, 0.0f32); + assert_eq!(2.0f32.ln_gamma().1, 1); + assert_approx_eq!(3.0f32.ln_gamma().0, 2.0f32.ln()); + assert_eq!(3.0f32.ln_gamma().1, 1); + assert_approx_eq!((-0.5f32).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln()); + assert_eq!((-0.5f32).ln_gamma().1, -1); +} + +#[test] +fn test_real_consts() { + let pi: f32 = consts::PI; + let frac_pi_2: f32 = consts::FRAC_PI_2; + let frac_pi_3: f32 = consts::FRAC_PI_3; + let frac_pi_4: f32 = consts::FRAC_PI_4; + let frac_pi_6: f32 = consts::FRAC_PI_6; + let frac_pi_8: f32 = consts::FRAC_PI_8; + let frac_1_pi: f32 = consts::FRAC_1_PI; + let frac_2_pi: f32 = consts::FRAC_2_PI; + let frac_2_sqrtpi: f32 = consts::FRAC_2_SQRT_PI; + let sqrt2: f32 = consts::SQRT_2; + let frac_1_sqrt2: f32 = consts::FRAC_1_SQRT_2; + let e: f32 = consts::E; + let log2_e: f32 = consts::LOG2_E; + let log10_e: f32 = consts::LOG10_E; + let ln_2: f32 = consts::LN_2; + let ln_10: f32 = consts::LN_10; + + assert_approx_eq!(frac_pi_2, pi / 2f32); + assert_approx_eq!(frac_pi_3, pi / 3f32); + assert_approx_eq!(frac_pi_4, pi / 4f32); + assert_approx_eq!(frac_pi_6, pi / 6f32); + assert_approx_eq!(frac_pi_8, pi / 8f32); + assert_approx_eq!(frac_1_pi, 1f32 / pi); + assert_approx_eq!(frac_2_pi, 2f32 / pi); + assert_approx_eq!(frac_2_sqrtpi, 2f32 / pi.sqrt()); + assert_approx_eq!(sqrt2, 2f32.sqrt()); + assert_approx_eq!(frac_1_sqrt2, 1f32 / 2f32.sqrt()); + assert_approx_eq!(log2_e, e.log2()); + assert_approx_eq!(log10_e, e.log10()); + assert_approx_eq!(ln_2, 2f32.ln()); + assert_approx_eq!(ln_10, 10f32.ln()); +} + +#[test] +fn test_float_bits_conv() { + assert_eq!((1f32).to_bits(), 0x3f800000); + assert_eq!((12.5f32).to_bits(), 0x41480000); + assert_eq!((1337f32).to_bits(), 0x44a72000); + assert_eq!((-14.25f32).to_bits(), 0xc1640000); + assert_approx_eq!(f32::from_bits(0x3f800000), 1.0); + assert_approx_eq!(f32::from_bits(0x41480000), 12.5); + assert_approx_eq!(f32::from_bits(0x44a72000), 1337.0); + assert_approx_eq!(f32::from_bits(0xc1640000), -14.25); + + // Check that NaNs roundtrip their bits regardless of signaling-ness + // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits + let masked_nan1 = f32::NAN.to_bits() ^ NAN_MASK1; + let masked_nan2 = f32::NAN.to_bits() ^ NAN_MASK2; + assert!(f32::from_bits(masked_nan1).is_nan()); + assert!(f32::from_bits(masked_nan2).is_nan()); + + assert_eq!(f32::from_bits(masked_nan1).to_bits(), masked_nan1); + assert_eq!(f32::from_bits(masked_nan2).to_bits(), masked_nan2); +} + +#[test] +#[should_panic] +fn test_clamp_min_greater_than_max() { + let _ = 1.0f32.clamp(3.0, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_min_is_nan() { + let _ = 1.0f32.clamp(f32::NAN, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_max_is_nan() { + let _ = 1.0f32.clamp(3.0, f32::NAN); +} + +#[test] +fn test_total_cmp() { + use core::cmp::Ordering; + + fn quiet_bit_mask() -> u32 { + 1 << (f32::MANTISSA_DIGITS - 2) + } + + fn min_subnorm() -> f32 { + f32::MIN_POSITIVE / f32::powf(2.0, f32::MANTISSA_DIGITS as f32 - 1.0) + } + + fn max_subnorm() -> f32 { + f32::MIN_POSITIVE - min_subnorm() + } + + fn q_nan() -> f32 { + f32::from_bits(f32::NAN.to_bits() | quiet_bit_mask()) + } + + fn s_nan() -> f32 { + f32::from_bits((f32::NAN.to_bits() & !quiet_bit_mask()) + 42) + } + + assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Equal, (-f32::INFINITY).total_cmp(&-f32::INFINITY)); + assert_eq!(Ordering::Equal, (-f32::MAX).total_cmp(&-f32::MAX)); + assert_eq!(Ordering::Equal, (-2.5_f32).total_cmp(&-2.5)); + assert_eq!(Ordering::Equal, (-1.0_f32).total_cmp(&-1.0)); + assert_eq!(Ordering::Equal, (-1.5_f32).total_cmp(&-1.5)); + assert_eq!(Ordering::Equal, (-0.5_f32).total_cmp(&-0.5)); + assert_eq!(Ordering::Equal, (-f32::MIN_POSITIVE).total_cmp(&-f32::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Equal, (-0.0_f32).total_cmp(&-0.0)); + assert_eq!(Ordering::Equal, 0.0_f32.total_cmp(&0.0)); + assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); + assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Equal, f32::MIN_POSITIVE.total_cmp(&f32::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, 0.5_f32.total_cmp(&0.5)); + assert_eq!(Ordering::Equal, 1.0_f32.total_cmp(&1.0)); + assert_eq!(Ordering::Equal, 1.5_f32.total_cmp(&1.5)); + assert_eq!(Ordering::Equal, 2.5_f32.total_cmp(&2.5)); + assert_eq!(Ordering::Equal, f32::MAX.total_cmp(&f32::MAX)); + assert_eq!(Ordering::Equal, f32::INFINITY.total_cmp(&f32::INFINITY)); + assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); + assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY)); + assert_eq!(Ordering::Less, (-f32::INFINITY).total_cmp(&-f32::MAX)); + assert_eq!(Ordering::Less, (-f32::MAX).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-2.5_f32).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-1.5_f32).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-1.0_f32).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-0.5_f32).total_cmp(&-f32::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-f32::MIN_POSITIVE).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-0.0_f32).total_cmp(&0.0)); + assert_eq!(Ordering::Less, 0.0_f32.total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f32::MIN_POSITIVE)); + assert_eq!(Ordering::Less, f32::MIN_POSITIVE.total_cmp(&0.5)); + assert_eq!(Ordering::Less, 0.5_f32.total_cmp(&1.0)); + assert_eq!(Ordering::Less, 1.0_f32.total_cmp(&1.5)); + assert_eq!(Ordering::Less, 1.5_f32.total_cmp(&2.5)); + assert_eq!(Ordering::Less, 2.5_f32.total_cmp(&f32::MAX)); + assert_eq!(Ordering::Less, f32::MAX.total_cmp(&f32::INFINITY)); + assert_eq!(Ordering::Less, f32::INFINITY.total_cmp(&s_nan())); + assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Greater, (-f32::INFINITY).total_cmp(&-s_nan())); + assert_eq!(Ordering::Greater, (-f32::MAX).total_cmp(&-f32::INFINITY)); + assert_eq!(Ordering::Greater, (-2.5_f32).total_cmp(&-f32::MAX)); + assert_eq!(Ordering::Greater, (-1.5_f32).total_cmp(&-2.5)); + assert_eq!(Ordering::Greater, (-1.0_f32).total_cmp(&-1.5)); + assert_eq!(Ordering::Greater, (-0.5_f32).total_cmp(&-1.0)); + assert_eq!(Ordering::Greater, (-f32::MIN_POSITIVE).total_cmp(&-0.5)); + assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f32::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Greater, (-0.0_f32).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Greater, 0.0_f32.total_cmp(&-0.0)); + assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); + assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); + assert_eq!(Ordering::Greater, f32::MIN_POSITIVE.total_cmp(&max_subnorm())); + assert_eq!(Ordering::Greater, 0.5_f32.total_cmp(&f32::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, 1.0_f32.total_cmp(&0.5)); + assert_eq!(Ordering::Greater, 1.5_f32.total_cmp(&1.0)); + assert_eq!(Ordering::Greater, 2.5_f32.total_cmp(&1.5)); + assert_eq!(Ordering::Greater, f32::MAX.total_cmp(&2.5)); + assert_eq!(Ordering::Greater, f32::INFINITY.total_cmp(&f32::MAX)); + assert_eq!(Ordering::Greater, s_nan().total_cmp(&f32::INFINITY)); + assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); +} diff --git a/library/std/tests/floats/f64.rs b/library/std/tests/floats/f64.rs new file mode 100644 index 0000000000000..cbbfcd15efd26 --- /dev/null +++ b/library/std/tests/floats/f64.rs @@ -0,0 +1,896 @@ +use std::f64::consts; +use std::num::FpCategory as Fp; + +/// Smallest number +const TINY_BITS: u64 = 0x1; + +/// Next smallest number +const TINY_UP_BITS: u64 = 0x2; + +/// Exponent = 0b11...10, Sifnificand 0b1111..10. Min val > 0 +const MAX_DOWN_BITS: u64 = 0x7fef_ffff_ffff_fffe; + +/// Zeroed exponent, full significant +const LARGEST_SUBNORMAL_BITS: u64 = 0x000f_ffff_ffff_ffff; + +/// Exponent = 0b1, zeroed significand +const SMALLEST_NORMAL_BITS: u64 = 0x0010_0000_0000_0000; + +/// First pattern over the mantissa +const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa; + +/// Second pattern over the mantissa +const NAN_MASK2: u64 = 0x0005_5555_5555_5555; + +#[allow(unused_macros)] +macro_rules! assert_f64_biteq { + ($left : expr, $right : expr) => { + let l: &f64 = &$left; + let r: &f64 = &$right; + let lb = l.to_bits(); + let rb = r.to_bits(); + assert_eq!(lb, rb, "float {l} ({lb:#018x}) is not bitequal to {r} ({rb:#018x})"); + }; +} + +#[test] +fn test_num_f64() { + crate::test_num(10f64, 2f64); +} + +#[test] +fn test_min_nan() { + assert_eq!(f64::NAN.min(2.0), 2.0); + assert_eq!(2.0f64.min(f64::NAN), 2.0); +} + +#[test] +fn test_max_nan() { + assert_eq!(f64::NAN.max(2.0), 2.0); + assert_eq!(2.0f64.max(f64::NAN), 2.0); +} + +#[test] +fn test_nan() { + let nan: f64 = f64::NAN; + assert!(nan.is_nan()); + assert!(!nan.is_infinite()); + assert!(!nan.is_finite()); + assert!(!nan.is_normal()); + assert!(nan.is_sign_positive()); + assert!(!nan.is_sign_negative()); + assert_eq!(Fp::Nan, nan.classify()); +} + +#[test] +fn test_infinity() { + let inf: f64 = f64::INFINITY; + assert!(inf.is_infinite()); + assert!(!inf.is_finite()); + assert!(inf.is_sign_positive()); + assert!(!inf.is_sign_negative()); + assert!(!inf.is_nan()); + assert!(!inf.is_normal()); + assert_eq!(Fp::Infinite, inf.classify()); +} + +#[test] +fn test_neg_infinity() { + let neg_inf: f64 = f64::NEG_INFINITY; + assert!(neg_inf.is_infinite()); + assert!(!neg_inf.is_finite()); + assert!(!neg_inf.is_sign_positive()); + assert!(neg_inf.is_sign_negative()); + assert!(!neg_inf.is_nan()); + assert!(!neg_inf.is_normal()); + assert_eq!(Fp::Infinite, neg_inf.classify()); +} + +#[test] +fn test_zero() { + let zero: f64 = 0.0f64; + assert_eq!(0.0, zero); + assert!(!zero.is_infinite()); + assert!(zero.is_finite()); + assert!(zero.is_sign_positive()); + assert!(!zero.is_sign_negative()); + assert!(!zero.is_nan()); + assert!(!zero.is_normal()); + assert_eq!(Fp::Zero, zero.classify()); +} + +#[test] +fn test_neg_zero() { + let neg_zero: f64 = -0.0; + assert_eq!(0.0, neg_zero); + assert!(!neg_zero.is_infinite()); + assert!(neg_zero.is_finite()); + assert!(!neg_zero.is_sign_positive()); + assert!(neg_zero.is_sign_negative()); + assert!(!neg_zero.is_nan()); + assert!(!neg_zero.is_normal()); + assert_eq!(Fp::Zero, neg_zero.classify()); +} + +#[test] +fn test_one() { + let one: f64 = 1.0f64; + assert_eq!(1.0, one); + assert!(!one.is_infinite()); + assert!(one.is_finite()); + assert!(one.is_sign_positive()); + assert!(!one.is_sign_negative()); + assert!(!one.is_nan()); + assert!(one.is_normal()); + assert_eq!(Fp::Normal, one.classify()); +} + +#[test] +fn test_is_nan() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert!(nan.is_nan()); + assert!(!0.0f64.is_nan()); + assert!(!5.3f64.is_nan()); + assert!(!(-10.732f64).is_nan()); + assert!(!inf.is_nan()); + assert!(!neg_inf.is_nan()); +} + +#[test] +fn test_is_infinite() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert!(!nan.is_infinite()); + assert!(inf.is_infinite()); + assert!(neg_inf.is_infinite()); + assert!(!0.0f64.is_infinite()); + assert!(!42.8f64.is_infinite()); + assert!(!(-109.2f64).is_infinite()); +} + +#[test] +fn test_is_finite() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert!(!nan.is_finite()); + assert!(!inf.is_finite()); + assert!(!neg_inf.is_finite()); + assert!(0.0f64.is_finite()); + assert!(42.8f64.is_finite()); + assert!((-109.2f64).is_finite()); +} + +#[test] +fn test_is_normal() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let zero: f64 = 0.0f64; + let neg_zero: f64 = -0.0; + assert!(!nan.is_normal()); + assert!(!inf.is_normal()); + assert!(!neg_inf.is_normal()); + assert!(!zero.is_normal()); + assert!(!neg_zero.is_normal()); + assert!(1f64.is_normal()); + assert!(1e-307f64.is_normal()); + assert!(!1e-308f64.is_normal()); +} + +#[test] +fn test_classify() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let zero: f64 = 0.0f64; + let neg_zero: f64 = -0.0; + assert_eq!(nan.classify(), Fp::Nan); + assert_eq!(inf.classify(), Fp::Infinite); + assert_eq!(neg_inf.classify(), Fp::Infinite); + assert_eq!(zero.classify(), Fp::Zero); + assert_eq!(neg_zero.classify(), Fp::Zero); + assert_eq!(1e-307f64.classify(), Fp::Normal); + assert_eq!(1e-308f64.classify(), Fp::Subnormal); +} + +#[test] +fn test_floor() { + assert_approx_eq!(1.0f64.floor(), 1.0f64); + assert_approx_eq!(1.3f64.floor(), 1.0f64); + assert_approx_eq!(1.5f64.floor(), 1.0f64); + assert_approx_eq!(1.7f64.floor(), 1.0f64); + assert_approx_eq!(0.0f64.floor(), 0.0f64); + assert_approx_eq!((-0.0f64).floor(), -0.0f64); + assert_approx_eq!((-1.0f64).floor(), -1.0f64); + assert_approx_eq!((-1.3f64).floor(), -2.0f64); + assert_approx_eq!((-1.5f64).floor(), -2.0f64); + assert_approx_eq!((-1.7f64).floor(), -2.0f64); +} + +#[test] +fn test_ceil() { + assert_approx_eq!(1.0f64.ceil(), 1.0f64); + assert_approx_eq!(1.3f64.ceil(), 2.0f64); + assert_approx_eq!(1.5f64.ceil(), 2.0f64); + assert_approx_eq!(1.7f64.ceil(), 2.0f64); + assert_approx_eq!(0.0f64.ceil(), 0.0f64); + assert_approx_eq!((-0.0f64).ceil(), -0.0f64); + assert_approx_eq!((-1.0f64).ceil(), -1.0f64); + assert_approx_eq!((-1.3f64).ceil(), -1.0f64); + assert_approx_eq!((-1.5f64).ceil(), -1.0f64); + assert_approx_eq!((-1.7f64).ceil(), -1.0f64); +} + +#[test] +fn test_round() { + assert_approx_eq!(2.5f64.round(), 3.0f64); + assert_approx_eq!(1.0f64.round(), 1.0f64); + assert_approx_eq!(1.3f64.round(), 1.0f64); + assert_approx_eq!(1.5f64.round(), 2.0f64); + assert_approx_eq!(1.7f64.round(), 2.0f64); + assert_approx_eq!(0.0f64.round(), 0.0f64); + assert_approx_eq!((-0.0f64).round(), -0.0f64); + assert_approx_eq!((-1.0f64).round(), -1.0f64); + assert_approx_eq!((-1.3f64).round(), -1.0f64); + assert_approx_eq!((-1.5f64).round(), -2.0f64); + assert_approx_eq!((-1.7f64).round(), -2.0f64); +} + +#[test] +fn test_round_ties_even() { + assert_approx_eq!(2.5f64.round_ties_even(), 2.0f64); + assert_approx_eq!(1.0f64.round_ties_even(), 1.0f64); + assert_approx_eq!(1.3f64.round_ties_even(), 1.0f64); + assert_approx_eq!(1.5f64.round_ties_even(), 2.0f64); + assert_approx_eq!(1.7f64.round_ties_even(), 2.0f64); + assert_approx_eq!(0.0f64.round_ties_even(), 0.0f64); + assert_approx_eq!((-0.0f64).round_ties_even(), -0.0f64); + assert_approx_eq!((-1.0f64).round_ties_even(), -1.0f64); + assert_approx_eq!((-1.3f64).round_ties_even(), -1.0f64); + assert_approx_eq!((-1.5f64).round_ties_even(), -2.0f64); + assert_approx_eq!((-1.7f64).round_ties_even(), -2.0f64); +} + +#[test] +fn test_trunc() { + assert_approx_eq!(1.0f64.trunc(), 1.0f64); + assert_approx_eq!(1.3f64.trunc(), 1.0f64); + assert_approx_eq!(1.5f64.trunc(), 1.0f64); + assert_approx_eq!(1.7f64.trunc(), 1.0f64); + assert_approx_eq!(0.0f64.trunc(), 0.0f64); + assert_approx_eq!((-0.0f64).trunc(), -0.0f64); + assert_approx_eq!((-1.0f64).trunc(), -1.0f64); + assert_approx_eq!((-1.3f64).trunc(), -1.0f64); + assert_approx_eq!((-1.5f64).trunc(), -1.0f64); + assert_approx_eq!((-1.7f64).trunc(), -1.0f64); +} + +#[test] +fn test_fract() { + assert_approx_eq!(1.0f64.fract(), 0.0f64); + assert_approx_eq!(1.3f64.fract(), 0.3f64); + assert_approx_eq!(1.5f64.fract(), 0.5f64); + assert_approx_eq!(1.7f64.fract(), 0.7f64); + assert_approx_eq!(0.0f64.fract(), 0.0f64); + assert_approx_eq!((-0.0f64).fract(), -0.0f64); + assert_approx_eq!((-1.0f64).fract(), -0.0f64); + assert_approx_eq!((-1.3f64).fract(), -0.3f64); + assert_approx_eq!((-1.5f64).fract(), -0.5f64); + assert_approx_eq!((-1.7f64).fract(), -0.7f64); +} + +#[test] +fn test_abs() { + assert_eq!(f64::INFINITY.abs(), f64::INFINITY); + assert_eq!(1f64.abs(), 1f64); + assert_eq!(0f64.abs(), 0f64); + assert_eq!((-0f64).abs(), 0f64); + assert_eq!((-1f64).abs(), 1f64); + assert_eq!(f64::NEG_INFINITY.abs(), f64::INFINITY); + assert_eq!((1f64 / f64::NEG_INFINITY).abs(), 0f64); + assert!(f64::NAN.abs().is_nan()); +} + +#[test] +fn test_signum() { + assert_eq!(f64::INFINITY.signum(), 1f64); + assert_eq!(1f64.signum(), 1f64); + assert_eq!(0f64.signum(), 1f64); + assert_eq!((-0f64).signum(), -1f64); + assert_eq!((-1f64).signum(), -1f64); + assert_eq!(f64::NEG_INFINITY.signum(), -1f64); + assert_eq!((1f64 / f64::NEG_INFINITY).signum(), -1f64); + assert!(f64::NAN.signum().is_nan()); +} + +#[test] +fn test_is_sign_positive() { + assert!(f64::INFINITY.is_sign_positive()); + assert!(1f64.is_sign_positive()); + assert!(0f64.is_sign_positive()); + assert!(!(-0f64).is_sign_positive()); + assert!(!(-1f64).is_sign_positive()); + assert!(!f64::NEG_INFINITY.is_sign_positive()); + assert!(!(1f64 / f64::NEG_INFINITY).is_sign_positive()); + assert!(f64::NAN.is_sign_positive()); + assert!(!(-f64::NAN).is_sign_positive()); +} + +#[test] +fn test_is_sign_negative() { + assert!(!f64::INFINITY.is_sign_negative()); + assert!(!1f64.is_sign_negative()); + assert!(!0f64.is_sign_negative()); + assert!((-0f64).is_sign_negative()); + assert!((-1f64).is_sign_negative()); + assert!(f64::NEG_INFINITY.is_sign_negative()); + assert!((1f64 / f64::NEG_INFINITY).is_sign_negative()); + assert!(!f64::NAN.is_sign_negative()); + assert!((-f64::NAN).is_sign_negative()); +} + +#[test] +fn test_next_up() { + let tiny = f64::from_bits(TINY_BITS); + let tiny_up = f64::from_bits(TINY_UP_BITS); + let max_down = f64::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f64::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f64::from_bits(SMALLEST_NORMAL_BITS); + assert_f64_biteq!(f64::NEG_INFINITY.next_up(), f64::MIN); + assert_f64_biteq!(f64::MIN.next_up(), -max_down); + assert_f64_biteq!((-1.0 - f64::EPSILON).next_up(), -1.0); + assert_f64_biteq!((-smallest_normal).next_up(), -largest_subnormal); + assert_f64_biteq!((-tiny_up).next_up(), -tiny); + assert_f64_biteq!((-tiny).next_up(), -0.0f64); + assert_f64_biteq!((-0.0f64).next_up(), tiny); + assert_f64_biteq!(0.0f64.next_up(), tiny); + assert_f64_biteq!(tiny.next_up(), tiny_up); + assert_f64_biteq!(largest_subnormal.next_up(), smallest_normal); + assert_f64_biteq!(1.0f64.next_up(), 1.0 + f64::EPSILON); + assert_f64_biteq!(f64::MAX.next_up(), f64::INFINITY); + assert_f64_biteq!(f64::INFINITY.next_up(), f64::INFINITY); + + let nan0 = f64::NAN; + let nan1 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK1); + let nan2 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK2); + assert_f64_biteq!(nan0.next_up(), nan0); + assert_f64_biteq!(nan1.next_up(), nan1); + assert_f64_biteq!(nan2.next_up(), nan2); +} + +#[test] +fn test_next_down() { + let tiny = f64::from_bits(TINY_BITS); + let tiny_up = f64::from_bits(TINY_UP_BITS); + let max_down = f64::from_bits(MAX_DOWN_BITS); + let largest_subnormal = f64::from_bits(LARGEST_SUBNORMAL_BITS); + let smallest_normal = f64::from_bits(SMALLEST_NORMAL_BITS); + assert_f64_biteq!(f64::NEG_INFINITY.next_down(), f64::NEG_INFINITY); + assert_f64_biteq!(f64::MIN.next_down(), f64::NEG_INFINITY); + assert_f64_biteq!((-max_down).next_down(), f64::MIN); + assert_f64_biteq!((-1.0f64).next_down(), -1.0 - f64::EPSILON); + assert_f64_biteq!((-largest_subnormal).next_down(), -smallest_normal); + assert_f64_biteq!((-tiny).next_down(), -tiny_up); + assert_f64_biteq!((-0.0f64).next_down(), -tiny); + assert_f64_biteq!((0.0f64).next_down(), -tiny); + assert_f64_biteq!(tiny.next_down(), 0.0f64); + assert_f64_biteq!(tiny_up.next_down(), tiny); + assert_f64_biteq!(smallest_normal.next_down(), largest_subnormal); + assert_f64_biteq!((1.0 + f64::EPSILON).next_down(), 1.0f64); + assert_f64_biteq!(f64::MAX.next_down(), max_down); + assert_f64_biteq!(f64::INFINITY.next_down(), f64::MAX); + + let nan0 = f64::NAN; + let nan1 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK1); + let nan2 = f64::from_bits(f64::NAN.to_bits() ^ NAN_MASK2); + assert_f64_biteq!(nan0.next_down(), nan0); + assert_f64_biteq!(nan1.next_down(), nan1); + assert_f64_biteq!(nan2.next_down(), nan2); +} + +#[test] +fn test_mul_add() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_approx_eq!(12.3f64.mul_add(4.5, 6.7), 62.05); + assert_approx_eq!((-12.3f64).mul_add(-4.5, -6.7), 48.65); + assert_approx_eq!(0.0f64.mul_add(8.9, 1.2), 1.2); + assert_approx_eq!(3.4f64.mul_add(-0.0, 5.6), 5.6); + assert!(nan.mul_add(7.8, 9.0).is_nan()); + assert_eq!(inf.mul_add(7.8, 9.0), inf); + assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_eq!(8.9f64.mul_add(inf, 3.2), inf); + assert_eq!((-3.2f64).mul_add(2.4, neg_inf), neg_inf); +} + +#[test] +fn test_recip() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_eq!(1.0f64.recip(), 1.0); + assert_eq!(2.0f64.recip(), 0.5); + assert_eq!((-0.4f64).recip(), -2.5); + assert_eq!(0.0f64.recip(), inf); + assert!(nan.recip().is_nan()); + assert_eq!(inf.recip(), 0.0); + assert_eq!(neg_inf.recip(), 0.0); +} + +#[test] +fn test_powi() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_eq!(1.0f64.powi(1), 1.0); + assert_approx_eq!((-3.1f64).powi(2), 9.61); + assert_approx_eq!(5.9f64.powi(-2), 0.028727); + assert_eq!(8.3f64.powi(0), 1.0); + assert!(nan.powi(2).is_nan()); + assert_eq!(inf.powi(3), inf); + assert_eq!(neg_inf.powi(2), inf); +} + +#[test] +fn test_powf() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_eq!(1.0f64.powf(1.0), 1.0); + assert_approx_eq!(3.4f64.powf(4.5), 246.408183); + assert_approx_eq!(2.7f64.powf(-3.2), 0.041652); + assert_approx_eq!((-3.1f64).powf(2.0), 9.61); + assert_approx_eq!(5.9f64.powf(-2.0), 0.028727); + assert_eq!(8.3f64.powf(0.0), 1.0); + assert!(nan.powf(2.0).is_nan()); + assert_eq!(inf.powf(2.0), inf); + assert_eq!(neg_inf.powf(3.0), neg_inf); +} + +#[test] +fn test_sqrt_domain() { + assert!(f64::NAN.sqrt().is_nan()); + assert!(f64::NEG_INFINITY.sqrt().is_nan()); + assert!((-1.0f64).sqrt().is_nan()); + assert_eq!((-0.0f64).sqrt(), -0.0); + assert_eq!(0.0f64.sqrt(), 0.0); + assert_eq!(1.0f64.sqrt(), 1.0); + assert_eq!(f64::INFINITY.sqrt(), f64::INFINITY); +} + +#[test] +fn test_exp() { + assert_eq!(1.0, 0.0f64.exp()); + assert_approx_eq!(2.718282, 1.0f64.exp()); + assert_approx_eq!(148.413159, 5.0f64.exp()); + + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let nan: f64 = f64::NAN; + assert_eq!(inf, inf.exp()); + assert_eq!(0.0, neg_inf.exp()); + assert!(nan.exp().is_nan()); +} + +#[test] +fn test_exp2() { + assert_eq!(32.0, 5.0f64.exp2()); + assert_eq!(1.0, 0.0f64.exp2()); + + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let nan: f64 = f64::NAN; + assert_eq!(inf, inf.exp2()); + assert_eq!(0.0, neg_inf.exp2()); + assert!(nan.exp2().is_nan()); +} + +#[test] +fn test_ln() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_approx_eq!(1.0f64.exp().ln(), 1.0); + assert!(nan.ln().is_nan()); + assert_eq!(inf.ln(), inf); + assert!(neg_inf.ln().is_nan()); + assert!((-2.3f64).ln().is_nan()); + assert_eq!((-0.0f64).ln(), neg_inf); + assert_eq!(0.0f64.ln(), neg_inf); + assert_approx_eq!(4.0f64.ln(), 1.386294); +} + +#[test] +fn test_log() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_eq!(10.0f64.log(10.0), 1.0); + assert_approx_eq!(2.3f64.log(3.5), 0.664858); + assert_eq!(1.0f64.exp().log(1.0f64.exp()), 1.0); + assert!(1.0f64.log(1.0).is_nan()); + assert!(1.0f64.log(-13.9).is_nan()); + assert!(nan.log(2.3).is_nan()); + assert_eq!(inf.log(10.0), inf); + assert!(neg_inf.log(8.8).is_nan()); + assert!((-2.3f64).log(0.1).is_nan()); + assert_eq!((-0.0f64).log(2.0), neg_inf); + assert_eq!(0.0f64.log(7.0), neg_inf); +} + +#[test] +fn test_log2() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_approx_eq!(10.0f64.log2(), 3.321928); + assert_approx_eq!(2.3f64.log2(), 1.201634); + assert_approx_eq!(1.0f64.exp().log2(), 1.442695); + assert!(nan.log2().is_nan()); + assert_eq!(inf.log2(), inf); + assert!(neg_inf.log2().is_nan()); + assert!((-2.3f64).log2().is_nan()); + assert_eq!((-0.0f64).log2(), neg_inf); + assert_eq!(0.0f64.log2(), neg_inf); +} + +#[test] +fn test_log10() { + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_eq!(10.0f64.log10(), 1.0); + assert_approx_eq!(2.3f64.log10(), 0.361728); + assert_approx_eq!(1.0f64.exp().log10(), 0.434294); + assert_eq!(1.0f64.log10(), 0.0); + assert!(nan.log10().is_nan()); + assert_eq!(inf.log10(), inf); + assert!(neg_inf.log10().is_nan()); + assert!((-2.3f64).log10().is_nan()); + assert_eq!((-0.0f64).log10(), neg_inf); + assert_eq!(0.0f64.log10(), neg_inf); +} + +#[test] +fn test_to_degrees() { + let pi: f64 = consts::PI; + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_eq!(0.0f64.to_degrees(), 0.0); + assert_approx_eq!((-5.8f64).to_degrees(), -332.315521); + assert_eq!(pi.to_degrees(), 180.0); + assert!(nan.to_degrees().is_nan()); + assert_eq!(inf.to_degrees(), inf); + assert_eq!(neg_inf.to_degrees(), neg_inf); +} + +#[test] +fn test_to_radians() { + let pi: f64 = consts::PI; + let nan: f64 = f64::NAN; + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + assert_eq!(0.0f64.to_radians(), 0.0); + assert_approx_eq!(154.6f64.to_radians(), 2.698279); + assert_approx_eq!((-332.31f64).to_radians(), -5.799903); + assert_eq!(180.0f64.to_radians(), pi); + assert!(nan.to_radians().is_nan()); + assert_eq!(inf.to_radians(), inf); + assert_eq!(neg_inf.to_radians(), neg_inf); +} + +#[test] +fn test_asinh() { + assert_eq!(0.0f64.asinh(), 0.0f64); + assert_eq!((-0.0f64).asinh(), -0.0f64); + + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let nan: f64 = f64::NAN; + assert_eq!(inf.asinh(), inf); + assert_eq!(neg_inf.asinh(), neg_inf); + assert!(nan.asinh().is_nan()); + assert!((-0.0f64).asinh().is_sign_negative()); + // issue 63271 + assert_approx_eq!(2.0f64.asinh(), 1.443635475178810342493276740273105f64); + assert_approx_eq!((-2.0f64).asinh(), -1.443635475178810342493276740273105f64); + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!((-67452098.07139316f64).asinh(), -18.72007542627454439398548429400083); + + // test for low accuracy from issue 104548 + assert_approx_eq!(60.0f64, 60.0f64.sinh().asinh()); + // mul needed for approximate comparison to be meaningful + assert_approx_eq!(1.0f64, 1e-15f64.sinh().asinh() * 1e15f64); +} + +#[test] +fn test_acosh() { + assert_eq!(1.0f64.acosh(), 0.0f64); + assert!(0.999f64.acosh().is_nan()); + + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let nan: f64 = f64::NAN; + assert_eq!(inf.acosh(), inf); + assert!(neg_inf.acosh().is_nan()); + assert!(nan.acosh().is_nan()); + assert_approx_eq!(2.0f64.acosh(), 1.31695789692481670862504634730796844f64); + assert_approx_eq!(3.0f64.acosh(), 1.76274717403908605046521864995958461f64); + + // test for low accuracy from issue 104548 + assert_approx_eq!(60.0f64, 60.0f64.cosh().acosh()); +} + +#[test] +fn test_atanh() { + assert_eq!(0.0f64.atanh(), 0.0f64); + assert_eq!((-0.0f64).atanh(), -0.0f64); + + let inf: f64 = f64::INFINITY; + let neg_inf: f64 = f64::NEG_INFINITY; + let nan: f64 = f64::NAN; + assert_eq!(1.0f64.atanh(), inf); + assert_eq!((-1.0f64).atanh(), neg_inf); + assert!(2f64.atanh().atanh().is_nan()); + assert!((-2f64).atanh().atanh().is_nan()); + assert!(inf.atanh().is_nan()); + assert!(neg_inf.atanh().is_nan()); + assert!(nan.atanh().is_nan()); + assert_approx_eq!(0.5f64.atanh(), 0.54930614433405484569762261846126285f64); + assert_approx_eq!((-0.5f64).atanh(), -0.54930614433405484569762261846126285f64); +} + +#[test] +fn test_gamma() { + // precision can differ between platforms + assert_approx_eq!(1.0f64.gamma(), 1.0f64); + assert_approx_eq!(2.0f64.gamma(), 1.0f64); + assert_approx_eq!(3.0f64.gamma(), 2.0f64); + assert_approx_eq!(4.0f64.gamma(), 6.0f64); + assert_approx_eq!(5.0f64.gamma(), 24.0f64); + assert_approx_eq!(0.5f64.gamma(), consts::PI.sqrt()); + assert_approx_eq!((-0.5f64).gamma(), -2.0 * consts::PI.sqrt()); + assert_eq!(0.0f64.gamma(), f64::INFINITY); + assert_eq!((-0.0f64).gamma(), f64::NEG_INFINITY); + assert!((-1.0f64).gamma().is_nan()); + assert!((-2.0f64).gamma().is_nan()); + assert!(f64::NAN.gamma().is_nan()); + assert!(f64::NEG_INFINITY.gamma().is_nan()); + assert_eq!(f64::INFINITY.gamma(), f64::INFINITY); + assert_eq!(171.71f64.gamma(), f64::INFINITY); +} + +#[test] +fn test_ln_gamma() { + assert_approx_eq!(1.0f64.ln_gamma().0, 0.0f64); + assert_eq!(1.0f64.ln_gamma().1, 1); + assert_approx_eq!(2.0f64.ln_gamma().0, 0.0f64); + assert_eq!(2.0f64.ln_gamma().1, 1); + assert_approx_eq!(3.0f64.ln_gamma().0, 2.0f64.ln()); + assert_eq!(3.0f64.ln_gamma().1, 1); + assert_approx_eq!((-0.5f64).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln()); + assert_eq!((-0.5f64).ln_gamma().1, -1); +} + +#[test] +fn test_real_consts() { + let pi: f64 = consts::PI; + let frac_pi_2: f64 = consts::FRAC_PI_2; + let frac_pi_3: f64 = consts::FRAC_PI_3; + let frac_pi_4: f64 = consts::FRAC_PI_4; + let frac_pi_6: f64 = consts::FRAC_PI_6; + let frac_pi_8: f64 = consts::FRAC_PI_8; + let frac_1_pi: f64 = consts::FRAC_1_PI; + let frac_2_pi: f64 = consts::FRAC_2_PI; + let frac_2_sqrtpi: f64 = consts::FRAC_2_SQRT_PI; + let sqrt2: f64 = consts::SQRT_2; + let frac_1_sqrt2: f64 = consts::FRAC_1_SQRT_2; + let e: f64 = consts::E; + let log2_e: f64 = consts::LOG2_E; + let log10_e: f64 = consts::LOG10_E; + let ln_2: f64 = consts::LN_2; + let ln_10: f64 = consts::LN_10; + + assert_approx_eq!(frac_pi_2, pi / 2f64); + assert_approx_eq!(frac_pi_3, pi / 3f64); + assert_approx_eq!(frac_pi_4, pi / 4f64); + assert_approx_eq!(frac_pi_6, pi / 6f64); + assert_approx_eq!(frac_pi_8, pi / 8f64); + assert_approx_eq!(frac_1_pi, 1f64 / pi); + assert_approx_eq!(frac_2_pi, 2f64 / pi); + assert_approx_eq!(frac_2_sqrtpi, 2f64 / pi.sqrt()); + assert_approx_eq!(sqrt2, 2f64.sqrt()); + assert_approx_eq!(frac_1_sqrt2, 1f64 / 2f64.sqrt()); + assert_approx_eq!(log2_e, e.log2()); + assert_approx_eq!(log10_e, e.log10()); + assert_approx_eq!(ln_2, 2f64.ln()); + assert_approx_eq!(ln_10, 10f64.ln()); +} + +#[test] +fn test_float_bits_conv() { + assert_eq!((1f64).to_bits(), 0x3ff0000000000000); + assert_eq!((12.5f64).to_bits(), 0x4029000000000000); + assert_eq!((1337f64).to_bits(), 0x4094e40000000000); + assert_eq!((-14.25f64).to_bits(), 0xc02c800000000000); + assert_approx_eq!(f64::from_bits(0x3ff0000000000000), 1.0); + assert_approx_eq!(f64::from_bits(0x4029000000000000), 12.5); + assert_approx_eq!(f64::from_bits(0x4094e40000000000), 1337.0); + assert_approx_eq!(f64::from_bits(0xc02c800000000000), -14.25); + + // Check that NaNs roundtrip their bits regardless of signaling-ness + let masked_nan1 = f64::NAN.to_bits() ^ NAN_MASK1; + let masked_nan2 = f64::NAN.to_bits() ^ NAN_MASK2; + assert!(f64::from_bits(masked_nan1).is_nan()); + assert!(f64::from_bits(masked_nan2).is_nan()); + + assert_eq!(f64::from_bits(masked_nan1).to_bits(), masked_nan1); + assert_eq!(f64::from_bits(masked_nan2).to_bits(), masked_nan2); +} + +#[test] +#[should_panic] +fn test_clamp_min_greater_than_max() { + let _ = 1.0f64.clamp(3.0, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_min_is_nan() { + let _ = 1.0f64.clamp(f64::NAN, 1.0); +} + +#[test] +#[should_panic] +fn test_clamp_max_is_nan() { + let _ = 1.0f64.clamp(3.0, f64::NAN); +} + +#[test] +fn test_total_cmp() { + use core::cmp::Ordering; + + fn quiet_bit_mask() -> u64 { + 1 << (f64::MANTISSA_DIGITS - 2) + } + + fn min_subnorm() -> f64 { + f64::MIN_POSITIVE / f64::powf(2.0, f64::MANTISSA_DIGITS as f64 - 1.0) + } + + fn max_subnorm() -> f64 { + f64::MIN_POSITIVE - min_subnorm() + } + + fn q_nan() -> f64 { + f64::from_bits(f64::NAN.to_bits() | quiet_bit_mask()) + } + + fn s_nan() -> f64 { + f64::from_bits((f64::NAN.to_bits() & !quiet_bit_mask()) + 42) + } + + assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Equal, (-f64::INFINITY).total_cmp(&-f64::INFINITY)); + assert_eq!(Ordering::Equal, (-f64::MAX).total_cmp(&-f64::MAX)); + assert_eq!(Ordering::Equal, (-2.5_f64).total_cmp(&-2.5)); + assert_eq!(Ordering::Equal, (-1.0_f64).total_cmp(&-1.0)); + assert_eq!(Ordering::Equal, (-1.5_f64).total_cmp(&-1.5)); + assert_eq!(Ordering::Equal, (-0.5_f64).total_cmp(&-0.5)); + assert_eq!(Ordering::Equal, (-f64::MIN_POSITIVE).total_cmp(&-f64::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Equal, (-0.0_f64).total_cmp(&-0.0)); + assert_eq!(Ordering::Equal, 0.0_f64.total_cmp(&0.0)); + assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm())); + assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Equal, f64::MIN_POSITIVE.total_cmp(&f64::MIN_POSITIVE)); + assert_eq!(Ordering::Equal, 0.5_f64.total_cmp(&0.5)); + assert_eq!(Ordering::Equal, 1.0_f64.total_cmp(&1.0)); + assert_eq!(Ordering::Equal, 1.5_f64.total_cmp(&1.5)); + assert_eq!(Ordering::Equal, 2.5_f64.total_cmp(&2.5)); + assert_eq!(Ordering::Equal, f64::MAX.total_cmp(&f64::MAX)); + assert_eq!(Ordering::Equal, f64::INFINITY.total_cmp(&f64::INFINITY)); + assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan())); + assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY)); + assert_eq!(Ordering::Less, (-f64::INFINITY).total_cmp(&-f64::MAX)); + assert_eq!(Ordering::Less, (-f64::MAX).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-2.5_f64).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-1.5_f64).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-1.0_f64).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-0.5_f64).total_cmp(&-f64::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-f64::MIN_POSITIVE).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-0.0_f64).total_cmp(&0.0)); + assert_eq!(Ordering::Less, 0.0_f64.total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f64::MIN_POSITIVE)); + assert_eq!(Ordering::Less, f64::MIN_POSITIVE.total_cmp(&0.5)); + assert_eq!(Ordering::Less, 0.5_f64.total_cmp(&1.0)); + assert_eq!(Ordering::Less, 1.0_f64.total_cmp(&1.5)); + assert_eq!(Ordering::Less, 1.5_f64.total_cmp(&2.5)); + assert_eq!(Ordering::Less, 2.5_f64.total_cmp(&f64::MAX)); + assert_eq!(Ordering::Less, f64::MAX.total_cmp(&f64::INFINITY)); + assert_eq!(Ordering::Less, f64::INFINITY.total_cmp(&s_nan())); + assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan())); + + assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan())); + assert_eq!(Ordering::Greater, (-f64::INFINITY).total_cmp(&-s_nan())); + assert_eq!(Ordering::Greater, (-f64::MAX).total_cmp(&-f64::INFINITY)); + assert_eq!(Ordering::Greater, (-2.5_f64).total_cmp(&-f64::MAX)); + assert_eq!(Ordering::Greater, (-1.5_f64).total_cmp(&-2.5)); + assert_eq!(Ordering::Greater, (-1.0_f64).total_cmp(&-1.5)); + assert_eq!(Ordering::Greater, (-0.5_f64).total_cmp(&-1.0)); + assert_eq!(Ordering::Greater, (-f64::MIN_POSITIVE).total_cmp(&-0.5)); + assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f64::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Greater, (-0.0_f64).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Greater, 0.0_f64.total_cmp(&-0.0)); + assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0)); + assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm())); + assert_eq!(Ordering::Greater, f64::MIN_POSITIVE.total_cmp(&max_subnorm())); + assert_eq!(Ordering::Greater, 0.5_f64.total_cmp(&f64::MIN_POSITIVE)); + assert_eq!(Ordering::Greater, 1.0_f64.total_cmp(&0.5)); + assert_eq!(Ordering::Greater, 1.5_f64.total_cmp(&1.0)); + assert_eq!(Ordering::Greater, 2.5_f64.total_cmp(&1.5)); + assert_eq!(Ordering::Greater, f64::MAX.total_cmp(&2.5)); + assert_eq!(Ordering::Greater, f64::INFINITY.total_cmp(&f64::MAX)); + assert_eq!(Ordering::Greater, s_nan().total_cmp(&f64::INFINITY)); + assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MAX)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::INFINITY)); + assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan())); + + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm())); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MIN_POSITIVE)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MAX)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::INFINITY)); + assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan())); +} diff --git a/library/std/tests/floats/lib.rs b/library/std/tests/floats/lib.rs new file mode 100644 index 0000000000000..ad82f1a44e711 --- /dev/null +++ b/library/std/tests/floats/lib.rs @@ -0,0 +1,42 @@ +#![feature(f16, f128, float_gamma, float_minimum_maximum)] + +use std::fmt; +use std::ops::{Add, Div, Mul, Rem, Sub}; + +/// Verify that floats are within a tolerance of each other, 1.0e-6 by default. +macro_rules! assert_approx_eq { + ($a:expr, $b:expr) => {{ assert_approx_eq!($a, $b, 1.0e-6) }}; + ($a:expr, $b:expr, $lim:expr) => {{ + let (a, b) = (&$a, &$b); + let diff = (*a - *b).abs(); + assert!( + diff < $lim, + "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, difference {diff:?})", + lim = $lim + ); + }}; +} + +/// Helper function for testing numeric operations +pub fn test_num(ten: T, two: T) +where + T: PartialEq + + Add + + Sub + + Mul + + Div + + Rem + + fmt::Debug + + Copy, +{ + assert_eq!(ten.add(two), ten + two); + assert_eq!(ten.sub(two), ten - two); + assert_eq!(ten.mul(two), ten * two); + assert_eq!(ten.div(two), ten / two); + assert_eq!(ten.rem(two), ten % two); +} + +mod f128; +mod f16; +mod f32; +mod f64; diff --git a/library/std/tests/istr.rs b/library/std/tests/istr.rs index 9a127ae803e77..e481872977abf 100644 --- a/library/std/tests/istr.rs +++ b/library/std/tests/istr.rs @@ -5,7 +5,7 @@ fn test_stack_assign() { let t: String = "a".to_string(); assert_eq!(s, t); let u: String = "b".to_string(); - assert!((s != u)); + assert!(s != u); } #[test] @@ -19,7 +19,7 @@ fn test_heap_assign() { let t: String = "a big ol' string".to_string(); assert_eq!(s, t); let u: String = "a bad ol' string".to_string(); - assert!((s != u)); + assert!(s != u); } #[test] diff --git a/library/std/tests/num.rs b/library/std/tests/num.rs new file mode 100644 index 0000000000000..a7400f1c02df0 --- /dev/null +++ b/library/std/tests/num.rs @@ -0,0 +1,230 @@ +use std::ops::Mul; + +#[test] +fn test_saturating_add_uint() { + assert_eq!(3_usize.saturating_add(5_usize), 8_usize); + assert_eq!(3_usize.saturating_add(usize::MAX - 1), usize::MAX); + assert_eq!(usize::MAX.saturating_add(usize::MAX), usize::MAX); + assert_eq!((usize::MAX - 2).saturating_add(1), usize::MAX - 1); +} + +#[test] +fn test_saturating_sub_uint() { + assert_eq!(5_usize.saturating_sub(3_usize), 2_usize); + assert_eq!(3_usize.saturating_sub(5_usize), 0_usize); + assert_eq!(0_usize.saturating_sub(1_usize), 0_usize); + assert_eq!((usize::MAX - 1).saturating_sub(usize::MAX), 0); +} + +#[test] +fn test_saturating_add_int() { + assert_eq!(3i32.saturating_add(5), 8); + assert_eq!(3isize.saturating_add(isize::MAX - 1), isize::MAX); + assert_eq!(isize::MAX.saturating_add(isize::MAX), isize::MAX); + assert_eq!((isize::MAX - 2).saturating_add(1), isize::MAX - 1); + assert_eq!(3i32.saturating_add(-5), -2); + assert_eq!(isize::MIN.saturating_add(-1), isize::MIN); + assert_eq!((-2isize).saturating_add(-isize::MAX), isize::MIN); +} + +#[test] +fn test_saturating_sub_int() { + assert_eq!(3i32.saturating_sub(5), -2); + assert_eq!(isize::MIN.saturating_sub(1), isize::MIN); + assert_eq!((-2isize).saturating_sub(isize::MAX), isize::MIN); + assert_eq!(3i32.saturating_sub(-5), 8); + assert_eq!(3isize.saturating_sub(-(isize::MAX - 1)), isize::MAX); + assert_eq!(isize::MAX.saturating_sub(-isize::MAX), isize::MAX); + assert_eq!((isize::MAX - 2).saturating_sub(-1), isize::MAX - 1); +} + +#[test] +fn test_checked_add() { + let five_less = usize::MAX - 5; + assert_eq!(five_less.checked_add(0), Some(usize::MAX - 5)); + assert_eq!(five_less.checked_add(1), Some(usize::MAX - 4)); + assert_eq!(five_less.checked_add(2), Some(usize::MAX - 3)); + assert_eq!(five_less.checked_add(3), Some(usize::MAX - 2)); + assert_eq!(five_less.checked_add(4), Some(usize::MAX - 1)); + assert_eq!(five_less.checked_add(5), Some(usize::MAX)); + assert_eq!(five_less.checked_add(6), None); + assert_eq!(five_less.checked_add(7), None); +} + +#[test] +fn test_checked_sub() { + assert_eq!(5_usize.checked_sub(0), Some(5)); + assert_eq!(5_usize.checked_sub(1), Some(4)); + assert_eq!(5_usize.checked_sub(2), Some(3)); + assert_eq!(5_usize.checked_sub(3), Some(2)); + assert_eq!(5_usize.checked_sub(4), Some(1)); + assert_eq!(5_usize.checked_sub(5), Some(0)); + assert_eq!(5_usize.checked_sub(6), None); + assert_eq!(5_usize.checked_sub(7), None); +} + +#[test] +fn test_checked_mul() { + let third = usize::MAX / 3; + assert_eq!(third.checked_mul(0), Some(0)); + assert_eq!(third.checked_mul(1), Some(third)); + assert_eq!(third.checked_mul(2), Some(third * 2)); + assert_eq!(third.checked_mul(3), Some(third * 3)); + assert_eq!(third.checked_mul(4), None); +} + +macro_rules! test_is_power_of_two { + ($test_name:ident, $T:ident) => { + #[test] + fn $test_name() { + assert_eq!((0 as $T).is_power_of_two(), false); + assert_eq!((1 as $T).is_power_of_two(), true); + assert_eq!((2 as $T).is_power_of_two(), true); + assert_eq!((3 as $T).is_power_of_two(), false); + assert_eq!((4 as $T).is_power_of_two(), true); + assert_eq!((5 as $T).is_power_of_two(), false); + assert_eq!(($T::MAX / 2 + 1).is_power_of_two(), true); + } + }; +} + +test_is_power_of_two! { test_is_power_of_two_u8, u8 } +test_is_power_of_two! { test_is_power_of_two_u16, u16 } +test_is_power_of_two! { test_is_power_of_two_u32, u32 } +test_is_power_of_two! { test_is_power_of_two_u64, u64 } +test_is_power_of_two! { test_is_power_of_two_uint, usize } + +macro_rules! test_next_power_of_two { + ($test_name:ident, $T:ident) => { + #[test] + fn $test_name() { + assert_eq!((0 as $T).next_power_of_two(), 1); + let mut next_power = 1; + for i in 1 as $T..40 { + assert_eq!(i.next_power_of_two(), next_power); + if i == next_power { + next_power *= 2 + } + } + } + }; +} + +test_next_power_of_two! { test_next_power_of_two_u8, u8 } +test_next_power_of_two! { test_next_power_of_two_u16, u16 } +test_next_power_of_two! { test_next_power_of_two_u32, u32 } +test_next_power_of_two! { test_next_power_of_two_u64, u64 } +test_next_power_of_two! { test_next_power_of_two_uint, usize } + +macro_rules! test_checked_next_power_of_two { + ($test_name:ident, $T:ident) => { + #[test] + fn $test_name() { + assert_eq!((0 as $T).checked_next_power_of_two(), Some(1)); + let smax = $T::MAX >> 1; + assert_eq!(smax.checked_next_power_of_two(), Some(smax + 1)); + assert_eq!((smax + 1).checked_next_power_of_two(), Some(smax + 1)); + assert_eq!((smax + 2).checked_next_power_of_two(), None); + assert_eq!(($T::MAX - 1).checked_next_power_of_two(), None); + assert_eq!($T::MAX.checked_next_power_of_two(), None); + let mut next_power = 1; + for i in 1 as $T..40 { + assert_eq!(i.checked_next_power_of_two(), Some(next_power)); + if i == next_power { + next_power *= 2 + } + } + } + }; +} + +test_checked_next_power_of_two! { test_checked_next_power_of_two_u8, u8 } +test_checked_next_power_of_two! { test_checked_next_power_of_two_u16, u16 } +test_checked_next_power_of_two! { test_checked_next_power_of_two_u32, u32 } +test_checked_next_power_of_two! { test_checked_next_power_of_two_u64, u64 } +test_checked_next_power_of_two! { test_checked_next_power_of_two_uint, usize } + +#[test] +fn test_pow() { + fn naive_pow + Copy>(one: T, base: T, exp: usize) -> T { + (0..exp).fold(one, |acc, _| acc * base) + } + macro_rules! assert_pow { + (($num:expr, $exp:expr) => $expected:expr) => {{ + let result = $num.pow($exp); + assert_eq!(result, $expected); + assert_eq!(result, naive_pow(1, $num, $exp)); + }}; + } + assert_pow!((3u32, 0 ) => 1); + assert_pow!((5u32, 1 ) => 5); + assert_pow!((-4i32, 2 ) => 16); + assert_pow!((8u32, 3 ) => 512); + assert_pow!((2u64, 50) => 1125899906842624); +} + +#[test] +fn test_uint_to_str_overflow() { + let mut u8_val: u8 = 255; + assert_eq!(u8_val.to_string(), "255"); + + u8_val = u8_val.wrapping_add(1); + assert_eq!(u8_val.to_string(), "0"); + + let mut u16_val: u16 = 65_535; + assert_eq!(u16_val.to_string(), "65535"); + + u16_val = u16_val.wrapping_add(1); + assert_eq!(u16_val.to_string(), "0"); + + let mut u32_val: u32 = 4_294_967_295; + assert_eq!(u32_val.to_string(), "4294967295"); + + u32_val = u32_val.wrapping_add(1); + assert_eq!(u32_val.to_string(), "0"); + + let mut u64_val: u64 = 18_446_744_073_709_551_615; + assert_eq!(u64_val.to_string(), "18446744073709551615"); + + u64_val = u64_val.wrapping_add(1); + assert_eq!(u64_val.to_string(), "0"); +} + +fn from_str(t: &str) -> Option { + std::str::FromStr::from_str(t).ok() +} + +#[test] +fn test_uint_from_str_overflow() { + let mut u8_val: u8 = 255; + assert_eq!(from_str::("255"), Some(u8_val)); + assert_eq!(from_str::("256"), None); + + u8_val = u8_val.wrapping_add(1); + assert_eq!(from_str::("0"), Some(u8_val)); + assert_eq!(from_str::("-1"), None); + + let mut u16_val: u16 = 65_535; + assert_eq!(from_str::("65535"), Some(u16_val)); + assert_eq!(from_str::("65536"), None); + + u16_val = u16_val.wrapping_add(1); + assert_eq!(from_str::("0"), Some(u16_val)); + assert_eq!(from_str::("-1"), None); + + let mut u32_val: u32 = 4_294_967_295; + assert_eq!(from_str::("4294967295"), Some(u32_val)); + assert_eq!(from_str::("4294967296"), None); + + u32_val = u32_val.wrapping_add(1); + assert_eq!(from_str::("0"), Some(u32_val)); + assert_eq!(from_str::("-1"), None); + + let mut u64_val: u64 = 18_446_744_073_709_551_615; + assert_eq!(from_str::("18446744073709551615"), Some(u64_val)); + assert_eq!(from_str::("18446744073709551616"), None); + + u64_val = u64_val.wrapping_add(1); + assert_eq!(from_str::("0"), Some(u64_val)); + assert_eq!(from_str::("-1"), None); +} diff --git a/library/std/tests/panic.rs b/library/std/tests/panic.rs new file mode 100644 index 0000000000000..f13b931dd222e --- /dev/null +++ b/library/std/tests/panic.rs @@ -0,0 +1,56 @@ +#![allow(dead_code)] + +use std::cell::RefCell; +use std::panic::{AssertUnwindSafe, UnwindSafe}; +use std::rc::Rc; +use std::sync::{Arc, Mutex, RwLock}; + +struct Foo { + a: i32, +} + +fn assert() {} + +#[test] +fn panic_safety_traits() { + assert::(); + assert::<&i32>(); + assert::<*mut i32>(); + assert::<*const i32>(); + assert::(); + assert::(); + assert::<&str>(); + assert::(); + assert::<&Foo>(); + assert::>(); + assert::(); + assert::>(); + assert::>(); + assert::>(); + assert::>(); + assert::<&Mutex>(); + assert::<&RwLock>(); + assert::>(); + assert::>(); + assert::>(); + + { + trait Trait: UnwindSafe {} + assert::>(); + } + + fn bar() { + assert::>(); + assert::>(); + } + + fn baz() { + assert::>(); + assert::>(); + assert::>(); + assert::>(); + assert::<&AssertUnwindSafe>(); + assert::>>(); + assert::>>(); + } +} diff --git a/library/std/tests/path.rs b/library/std/tests/path.rs new file mode 100644 index 0000000000000..978402b6fdaea --- /dev/null +++ b/library/std/tests/path.rs @@ -0,0 +1,1978 @@ +#![feature( + clone_to_uninit, + path_add_extension, + path_file_prefix, + maybe_uninit_slice, + os_string_pathbuf_leak +)] + +use std::clone::CloneToUninit; +use std::ffi::OsStr; +use std::hash::{DefaultHasher, Hash, Hasher}; +use std::mem::MaybeUninit; +use std::path::*; +use std::ptr; +use std::rc::Rc; +use std::sync::Arc; + +#[allow(unknown_lints, unused_macro_rules)] +macro_rules! t ( + ($path:expr, iter: $iter:expr) => ( + { + let path = Path::new($path); + + // Forward iteration + let comps = path.iter() + .map(|p| p.to_string_lossy().into_owned()) + .collect::>(); + let exp: &[&str] = &$iter; + let exps = exp.iter().map(|s| s.to_string()).collect::>(); + assert!(comps == exps, "iter: Expected {:?}, found {:?}", + exps, comps); + + // Reverse iteration + let comps = Path::new($path).iter().rev() + .map(|p| p.to_string_lossy().into_owned()) + .collect::>(); + let exps = exps.into_iter().rev().collect::>(); + assert!(comps == exps, "iter().rev(): Expected {:?}, found {:?}", + exps, comps); + } + ); + + ($path:expr, has_root: $has_root:expr, is_absolute: $is_absolute:expr) => ( + { + let path = Path::new($path); + + let act_root = path.has_root(); + assert!(act_root == $has_root, "has_root: Expected {:?}, found {:?}", + $has_root, act_root); + + let act_abs = path.is_absolute(); + assert!(act_abs == $is_absolute, "is_absolute: Expected {:?}, found {:?}", + $is_absolute, act_abs); + } + ); + + ($path:expr, parent: $parent:expr, file_name: $file:expr) => ( + { + let path = Path::new($path); + + let parent = path.parent().map(|p| p.to_str().unwrap()); + let exp_parent: Option<&str> = $parent; + assert!(parent == exp_parent, "parent: Expected {:?}, found {:?}", + exp_parent, parent); + + let file = path.file_name().map(|p| p.to_str().unwrap()); + let exp_file: Option<&str> = $file; + assert!(file == exp_file, "file_name: Expected {:?}, found {:?}", + exp_file, file); + } + ); + + ($path:expr, file_stem: $file_stem:expr, extension: $extension:expr) => ( + { + let path = Path::new($path); + + let stem = path.file_stem().map(|p| p.to_str().unwrap()); + let exp_stem: Option<&str> = $file_stem; + assert!(stem == exp_stem, "file_stem: Expected {:?}, found {:?}", + exp_stem, stem); + + let ext = path.extension().map(|p| p.to_str().unwrap()); + let exp_ext: Option<&str> = $extension; + assert!(ext == exp_ext, "extension: Expected {:?}, found {:?}", + exp_ext, ext); + } + ); + + ($path:expr, file_prefix: $file_prefix:expr, extension: $extension:expr) => ( + { + let path = Path::new($path); + + let prefix = path.file_prefix().map(|p| p.to_str().unwrap()); + let exp_prefix: Option<&str> = $file_prefix; + assert!(prefix == exp_prefix, "file_prefix: Expected {:?}, found {:?}", + exp_prefix, prefix); + + let ext = path.extension().map(|p| p.to_str().unwrap()); + let exp_ext: Option<&str> = $extension; + assert!(ext == exp_ext, "extension: Expected {:?}, found {:?}", + exp_ext, ext); + } + ); + + ($path:expr, iter: $iter:expr, + has_root: $has_root:expr, is_absolute: $is_absolute:expr, + parent: $parent:expr, file_name: $file:expr, + file_stem: $file_stem:expr, extension: $extension:expr, + file_prefix: $file_prefix:expr) => ( + { + t!($path, iter: $iter); + t!($path, has_root: $has_root, is_absolute: $is_absolute); + t!($path, parent: $parent, file_name: $file); + t!($path, file_stem: $file_stem, extension: $extension); + t!($path, file_prefix: $file_prefix, extension: $extension); + } + ); +); + +#[test] +fn into() { + use std::borrow::Cow; + + let static_path = Path::new("/home/foo"); + let static_cow_path: Cow<'static, Path> = static_path.into(); + let pathbuf = PathBuf::from("/home/foo"); + + { + let path: &Path = &pathbuf; + let borrowed_cow_path: Cow<'_, Path> = path.into(); + + assert_eq!(static_cow_path, borrowed_cow_path); + } + + let owned_cow_path: Cow<'static, Path> = pathbuf.into(); + + assert_eq!(static_cow_path, owned_cow_path); +} + +#[test] +fn test_pathbuf_leak() { + let string = "/have/a/cake".to_owned(); + let (len, cap) = (string.len(), string.capacity()); + let buf = PathBuf::from(string); + let leaked = buf.leak(); + assert_eq!(leaked.as_os_str().as_encoded_bytes(), b"/have/a/cake"); + unsafe { drop(String::from_raw_parts(leaked.as_mut_os_str() as *mut OsStr as _, len, cap)) } +} + +#[test] +#[cfg(any(unix, target_os = "wasi"))] +pub fn test_decompositions_unix() { + t!("", + iter: [], + has_root: false, + is_absolute: false, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("foo", + iter: ["foo"], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None, + file_prefix: Some("foo") + ); + + t!("/", + iter: ["/"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("/foo", + iter: ["/", "foo"], + has_root: true, + is_absolute: true, + parent: Some("/"), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None, + file_prefix: Some("foo") + ); + + t!("foo/", + iter: ["foo"], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None, + file_prefix: Some("foo") + ); + + t!("/foo/", + iter: ["/", "foo"], + has_root: true, + is_absolute: true, + parent: Some("/"), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None, + file_prefix: Some("foo") + ); + + t!("foo/bar", + iter: ["foo", "bar"], + has_root: false, + is_absolute: false, + parent: Some("foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None, + file_prefix: Some("bar") + ); + + t!("/foo/bar", + iter: ["/", "foo", "bar"], + has_root: true, + is_absolute: true, + parent: Some("/foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None, + file_prefix: Some("bar") + ); + + t!("///foo///", + iter: ["/", "foo"], + has_root: true, + is_absolute: true, + parent: Some("/"), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None, + file_prefix: Some("foo") + ); + + t!("///foo///bar", + iter: ["/", "foo", "bar"], + has_root: true, + is_absolute: true, + parent: Some("///foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None, + file_prefix: Some("bar") + ); + + t!("./.", + iter: ["."], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("/..", + iter: ["/", ".."], + has_root: true, + is_absolute: true, + parent: Some("/"), + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("../", + iter: [".."], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("foo/.", + iter: ["foo"], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None, + file_prefix: Some("foo") + ); + + t!("foo/..", + iter: ["foo", ".."], + has_root: false, + is_absolute: false, + parent: Some("foo"), + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("foo/./", + iter: ["foo"], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None, + file_prefix: Some("foo") + ); + + t!("foo/./bar", + iter: ["foo", "bar"], + has_root: false, + is_absolute: false, + parent: Some("foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None, + file_prefix: Some("bar") + ); + + t!("foo/../", + iter: ["foo", ".."], + has_root: false, + is_absolute: false, + parent: Some("foo"), + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("foo/../bar", + iter: ["foo", "..", "bar"], + has_root: false, + is_absolute: false, + parent: Some("foo/.."), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None, + file_prefix: Some("bar") + ); + + t!("./a", + iter: [".", "a"], + has_root: false, + is_absolute: false, + parent: Some("."), + file_name: Some("a"), + file_stem: Some("a"), + extension: None, + file_prefix: Some("a") + ); + + t!(".", + iter: ["."], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("./", + iter: ["."], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("a/b", + iter: ["a", "b"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some("b"), + file_stem: Some("b"), + extension: None, + file_prefix: Some("b") + ); + + t!("a//b", + iter: ["a", "b"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some("b"), + file_stem: Some("b"), + extension: None, + file_prefix: Some("b") + ); + + t!("a/./b", + iter: ["a", "b"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some("b"), + file_stem: Some("b"), + extension: None, + file_prefix: Some("b") + ); + + t!("a/b/c", + iter: ["a", "b", "c"], + has_root: false, + is_absolute: false, + parent: Some("a/b"), + file_name: Some("c"), + file_stem: Some("c"), + extension: None, + file_prefix: Some("c") + ); + + t!(".foo", + iter: [".foo"], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: Some(".foo"), + file_stem: Some(".foo"), + extension: None, + file_prefix: Some(".foo") + ); + + t!("a/.foo", + iter: ["a", ".foo"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some(".foo"), + file_stem: Some(".foo"), + extension: None, + file_prefix: Some(".foo") + ); + + t!("a/.rustfmt.toml", + iter: ["a", ".rustfmt.toml"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some(".rustfmt.toml"), + file_stem: Some(".rustfmt"), + extension: Some("toml"), + file_prefix: Some(".rustfmt") + ); + + t!("a/.x.y.z", + iter: ["a", ".x.y.z"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some(".x.y.z"), + file_stem: Some(".x.y"), + extension: Some("z"), + file_prefix: Some(".x") + ); +} + +#[test] +#[cfg(windows)] +pub fn test_decompositions_windows() { + t!("", + iter: [], + has_root: false, + is_absolute: false, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("foo", + iter: ["foo"], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None, + file_prefix: Some("foo") + ); + + t!("/", + iter: ["\\"], + has_root: true, + is_absolute: false, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("\\", + iter: ["\\"], + has_root: true, + is_absolute: false, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("c:", + iter: ["c:"], + has_root: false, + is_absolute: false, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("c:\\", + iter: ["c:", "\\"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("c:/", + iter: ["c:", "\\"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("/foo", + iter: ["\\", "foo"], + has_root: true, + is_absolute: false, + parent: Some("/"), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None, + file_prefix: Some("foo") + ); + + t!("foo/", + iter: ["foo"], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None, + file_prefix: Some("foo") + ); + + t!("/foo/", + iter: ["\\", "foo"], + has_root: true, + is_absolute: false, + parent: Some("/"), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None, + file_prefix: Some("foo") + ); + + t!("foo/bar", + iter: ["foo", "bar"], + has_root: false, + is_absolute: false, + parent: Some("foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None, + file_prefix: Some("bar") + ); + + t!("/foo/bar", + iter: ["\\", "foo", "bar"], + has_root: true, + is_absolute: false, + parent: Some("/foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None, + file_prefix: Some("bar") + ); + + t!("///foo///", + iter: ["\\", "foo"], + has_root: true, + is_absolute: false, + parent: Some("/"), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None, + file_prefix: Some("foo") + ); + + t!("///foo///bar", + iter: ["\\", "foo", "bar"], + has_root: true, + is_absolute: false, + parent: Some("///foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None, + file_prefix: Some("bar") + ); + + t!("./.", + iter: ["."], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("/..", + iter: ["\\", ".."], + has_root: true, + is_absolute: false, + parent: Some("/"), + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("../", + iter: [".."], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("foo/.", + iter: ["foo"], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None, + file_prefix: Some("foo") + ); + + t!("foo/..", + iter: ["foo", ".."], + has_root: false, + is_absolute: false, + parent: Some("foo"), + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("foo/./", + iter: ["foo"], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: Some("foo"), + file_stem: Some("foo"), + extension: None, + file_prefix: Some("foo") + ); + + t!("foo/./bar", + iter: ["foo", "bar"], + has_root: false, + is_absolute: false, + parent: Some("foo"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None, + file_prefix: Some("bar") + ); + + t!("foo/../", + iter: ["foo", ".."], + has_root: false, + is_absolute: false, + parent: Some("foo"), + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("foo/../bar", + iter: ["foo", "..", "bar"], + has_root: false, + is_absolute: false, + parent: Some("foo/.."), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None, + file_prefix: Some("bar") + ); + + t!("./a", + iter: [".", "a"], + has_root: false, + is_absolute: false, + parent: Some("."), + file_name: Some("a"), + file_stem: Some("a"), + extension: None, + file_prefix: Some("a") + ); + + t!(".", + iter: ["."], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("./", + iter: ["."], + has_root: false, + is_absolute: false, + parent: Some(""), + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("a/b", + iter: ["a", "b"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some("b"), + file_stem: Some("b"), + extension: None, + file_prefix: Some("b") + ); + + t!("a//b", + iter: ["a", "b"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some("b"), + file_stem: Some("b"), + extension: None, + file_prefix: Some("b") + ); + + t!("a/./b", + iter: ["a", "b"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some("b"), + file_stem: Some("b"), + extension: None, + file_prefix: Some("b") + ); + + t!("a/b/c", + iter: ["a", "b", "c"], + has_root: false, + is_absolute: false, + parent: Some("a/b"), + file_name: Some("c"), + file_stem: Some("c"), + extension: None, + file_prefix: Some("c") + ); + + t!("a\\b\\c", + iter: ["a", "b", "c"], + has_root: false, + is_absolute: false, + parent: Some("a\\b"), + file_name: Some("c"), + file_stem: Some("c"), + extension: None, + file_prefix: Some("c") + ); + + t!("\\a", + iter: ["\\", "a"], + has_root: true, + is_absolute: false, + parent: Some("\\"), + file_name: Some("a"), + file_stem: Some("a"), + extension: None, + file_prefix: Some("a") + ); + + t!("c:\\foo.txt", + iter: ["c:", "\\", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("c:\\"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt"), + file_prefix: Some("foo") + ); + + t!("\\\\server\\share\\foo.txt", + iter: ["\\\\server\\share", "\\", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("\\\\server\\share\\"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt"), + file_prefix: Some("foo") + ); + + t!("\\\\server\\share", + iter: ["\\\\server\\share", "\\"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("\\\\server", + iter: ["\\", "server"], + has_root: true, + is_absolute: false, + parent: Some("\\"), + file_name: Some("server"), + file_stem: Some("server"), + extension: None, + file_prefix: Some("server") + ); + + t!("\\\\?\\bar\\foo.txt", + iter: ["\\\\?\\bar", "\\", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\bar\\"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt"), + file_prefix: Some("foo") + ); + + t!("\\\\?\\bar", + iter: ["\\\\?\\bar"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("\\\\?\\", + iter: ["\\\\?\\"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("\\\\?\\UNC\\server\\share\\foo.txt", + iter: ["\\\\?\\UNC\\server\\share", "\\", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\UNC\\server\\share\\"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt"), + file_prefix: Some("foo") + ); + + t!("\\\\?\\UNC\\server", + iter: ["\\\\?\\UNC\\server"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("\\\\?\\UNC\\", + iter: ["\\\\?\\UNC\\"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("\\\\?\\C:\\foo.txt", + iter: ["\\\\?\\C:", "\\", "foo.txt"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\C:\\"), + file_name: Some("foo.txt"), + file_stem: Some("foo"), + extension: Some("txt"), + file_prefix: Some("foo") + ); + + t!("\\\\?\\C:\\", + iter: ["\\\\?\\C:", "\\"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("\\\\?\\C:", + iter: ["\\\\?\\C:"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("\\\\?\\foo/bar", + iter: ["\\\\?\\foo/bar"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("\\\\?\\C:/foo/bar", + iter: ["\\\\?\\C:", "\\", "foo/bar"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\C:/"), + file_name: Some("foo/bar"), + file_stem: Some("foo/bar"), + extension: None, + file_prefix: Some("foo/bar") + ); + + t!("\\\\.\\foo\\bar", + iter: ["\\\\.\\foo", "\\", "bar"], + has_root: true, + is_absolute: true, + parent: Some("\\\\.\\foo\\"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None, + file_prefix: Some("bar") + ); + + t!("\\\\.\\foo", + iter: ["\\\\.\\foo", "\\"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("\\\\.\\foo/bar", + iter: ["\\\\.\\foo", "\\", "bar"], + has_root: true, + is_absolute: true, + parent: Some("\\\\.\\foo/"), + file_name: Some("bar"), + file_stem: Some("bar"), + extension: None, + file_prefix: Some("bar") + ); + + t!("\\\\.\\foo\\bar/baz", + iter: ["\\\\.\\foo", "\\", "bar", "baz"], + has_root: true, + is_absolute: true, + parent: Some("\\\\.\\foo\\bar"), + file_name: Some("baz"), + file_stem: Some("baz"), + extension: None, + file_prefix: Some("baz") + ); + + t!("\\\\.\\", + iter: ["\\\\.\\", "\\"], + has_root: true, + is_absolute: true, + parent: None, + file_name: None, + file_stem: None, + extension: None, + file_prefix: None + ); + + t!("\\\\?\\a\\b\\", + iter: ["\\\\?\\a", "\\", "b"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\a\\"), + file_name: Some("b"), + file_stem: Some("b"), + extension: None, + file_prefix: Some("b") + ); + + t!("\\\\?\\C:\\foo.txt.zip", + iter: ["\\\\?\\C:", "\\", "foo.txt.zip"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\C:\\"), + file_name: Some("foo.txt.zip"), + file_stem: Some("foo.txt"), + extension: Some("zip"), + file_prefix: Some("foo") + ); + + t!("\\\\?\\C:\\.foo.txt.zip", + iter: ["\\\\?\\C:", "\\", ".foo.txt.zip"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\C:\\"), + file_name: Some(".foo.txt.zip"), + file_stem: Some(".foo.txt"), + extension: Some("zip"), + file_prefix: Some(".foo") + ); + + t!("\\\\?\\C:\\.foo", + iter: ["\\\\?\\C:", "\\", ".foo"], + has_root: true, + is_absolute: true, + parent: Some("\\\\?\\C:\\"), + file_name: Some(".foo"), + file_stem: Some(".foo"), + extension: None, + file_prefix: Some(".foo") + ); + + t!("a/.x.y.z", + iter: ["a", ".x.y.z"], + has_root: false, + is_absolute: false, + parent: Some("a"), + file_name: Some(".x.y.z"), + file_stem: Some(".x.y"), + extension: Some("z"), + file_prefix: Some(".x") + ); +} + +#[test] +pub fn test_stem_ext() { + t!("foo", + file_stem: Some("foo"), + extension: None + ); + + t!("foo.", + file_stem: Some("foo"), + extension: Some("") + ); + + t!(".foo", + file_stem: Some(".foo"), + extension: None + ); + + t!("foo.txt", + file_stem: Some("foo"), + extension: Some("txt") + ); + + t!("foo.bar.txt", + file_stem: Some("foo.bar"), + extension: Some("txt") + ); + + t!("foo.bar.", + file_stem: Some("foo.bar"), + extension: Some("") + ); + + t!(".", file_stem: None, extension: None); + + t!("..", file_stem: None, extension: None); + + t!(".x.y.z", file_stem: Some(".x.y"), extension: Some("z")); + + t!("..x.y.z", file_stem: Some("..x.y"), extension: Some("z")); + + t!("", file_stem: None, extension: None); +} + +#[test] +pub fn test_prefix_ext() { + t!("foo", + file_prefix: Some("foo"), + extension: None + ); + + t!("foo.", + file_prefix: Some("foo"), + extension: Some("") + ); + + t!(".foo", + file_prefix: Some(".foo"), + extension: None + ); + + t!("foo.txt", + file_prefix: Some("foo"), + extension: Some("txt") + ); + + t!("foo.bar.txt", + file_prefix: Some("foo"), + extension: Some("txt") + ); + + t!("foo.bar.", + file_prefix: Some("foo"), + extension: Some("") + ); + + t!(".", file_prefix: None, extension: None); + + t!("..", file_prefix: None, extension: None); + + t!(".x.y.z", file_prefix: Some(".x"), extension: Some("z")); + + t!("..x.y.z", file_prefix: Some("."), extension: Some("z")); + + t!("", file_prefix: None, extension: None); +} + +#[test] +pub fn test_push() { + macro_rules! tp ( + ($path:expr, $push:expr, $expected:expr) => ({ + let mut actual = PathBuf::from($path); + actual.push($push); + assert!(actual.to_str() == Some($expected), + "pushing {:?} onto {:?}: Expected {:?}, got {:?}", + $push, $path, $expected, actual.to_str().unwrap()); + }); + ); + + if cfg!(unix) + || cfg!(target_os = "wasi") + || cfg!(all(target_env = "sgx", target_vendor = "fortanix")) + { + tp!("", "foo", "foo"); + tp!("foo", "bar", "foo/bar"); + tp!("foo/", "bar", "foo/bar"); + tp!("foo//", "bar", "foo//bar"); + tp!("foo/.", "bar", "foo/./bar"); + tp!("foo./.", "bar", "foo././bar"); + tp!("foo", "", "foo/"); + tp!("foo", ".", "foo/."); + tp!("foo", "..", "foo/.."); + tp!("foo", "/", "/"); + tp!("/foo/bar", "/", "/"); + tp!("/foo/bar", "/baz", "/baz"); + tp!("/foo/bar", "./baz", "/foo/bar/./baz"); + } else { + tp!("", "foo", "foo"); + tp!("foo", "bar", r"foo\bar"); + tp!("foo/", "bar", r"foo/bar"); + tp!(r"foo\", "bar", r"foo\bar"); + tp!("foo//", "bar", r"foo//bar"); + tp!(r"foo\\", "bar", r"foo\\bar"); + tp!("foo/.", "bar", r"foo/.\bar"); + tp!("foo./.", "bar", r"foo./.\bar"); + tp!(r"foo\.", "bar", r"foo\.\bar"); + tp!(r"foo.\.", "bar", r"foo.\.\bar"); + tp!("foo", "", "foo\\"); + tp!("foo", ".", r"foo\."); + tp!("foo", "..", r"foo\.."); + tp!("foo", "/", "/"); + tp!("foo", r"\", r"\"); + tp!("/foo/bar", "/", "/"); + tp!(r"\foo\bar", r"\", r"\"); + tp!("/foo/bar", "/baz", "/baz"); + tp!("/foo/bar", r"\baz", r"\baz"); + tp!("/foo/bar", "./baz", r"/foo/bar\./baz"); + tp!("/foo/bar", r".\baz", r"/foo/bar\.\baz"); + + tp!("c:\\", "windows", "c:\\windows"); + tp!("c:", "windows", "c:windows"); + + tp!("a\\b\\c", "d", "a\\b\\c\\d"); + tp!("\\a\\b\\c", "d", "\\a\\b\\c\\d"); + tp!("a\\b", "c\\d", "a\\b\\c\\d"); + tp!("a\\b", "\\c\\d", "\\c\\d"); + tp!("a\\b", ".", "a\\b\\."); + tp!("a\\b", "..\\c", "a\\b\\..\\c"); + tp!("a\\b", "C:a.txt", "C:a.txt"); + tp!("a\\b", "C:\\a.txt", "C:\\a.txt"); + tp!("C:\\a", "C:\\b.txt", "C:\\b.txt"); + tp!("C:\\a\\b\\c", "C:d", "C:d"); + tp!("C:a\\b\\c", "C:d", "C:d"); + tp!("C:", r"a\b\c", r"C:a\b\c"); + tp!("C:", r"..\a", r"C:..\a"); + tp!("\\\\server\\share\\foo", "bar", "\\\\server\\share\\foo\\bar"); + tp!("\\\\server\\share\\foo", "C:baz", "C:baz"); + tp!("\\\\?\\C:\\a\\b", "C:c\\d", "C:c\\d"); + tp!("\\\\?\\C:a\\b", "C:c\\d", "C:c\\d"); + tp!("\\\\?\\C:\\a\\b", "C:\\c\\d", "C:\\c\\d"); + tp!("\\\\?\\foo\\bar", "baz", "\\\\?\\foo\\bar\\baz"); + tp!("\\\\?\\UNC\\server\\share\\foo", "bar", "\\\\?\\UNC\\server\\share\\foo\\bar"); + tp!("\\\\?\\UNC\\server\\share", "C:\\a", "C:\\a"); + tp!("\\\\?\\UNC\\server\\share", "C:a", "C:a"); + + // Note: modified from old path API + tp!("\\\\?\\UNC\\server", "foo", "\\\\?\\UNC\\server\\foo"); + + tp!("C:\\a", "\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share"); + tp!("\\\\.\\foo\\bar", "baz", "\\\\.\\foo\\bar\\baz"); + tp!("\\\\.\\foo\\bar", "C:a", "C:a"); + // again, not sure about the following, but I'm assuming \\.\ should be verbatim + tp!("\\\\.\\foo", "..\\bar", "\\\\.\\foo\\..\\bar"); + + tp!("\\\\?\\C:", "foo", "\\\\?\\C:\\foo"); // this is a weird one + + tp!(r"\\?\C:\bar", "../foo", r"\\?\C:\foo"); + tp!(r"\\?\C:\bar", "../../foo", r"\\?\C:\foo"); + tp!(r"\\?\C:\", "../foo", r"\\?\C:\foo"); + tp!(r"\\?\C:", r"D:\foo/./", r"D:\foo/./"); + tp!(r"\\?\C:", r"\\?\D:\foo\.\", r"\\?\D:\foo\.\"); + tp!(r"\\?\A:\x\y", "/foo", r"\\?\A:\foo"); + tp!(r"\\?\A:", r"..\foo\.", r"\\?\A:\foo"); + tp!(r"\\?\A:\x\y", r".\foo\.", r"\\?\A:\x\y\foo"); + tp!(r"\\?\A:\x\y", r"", r"\\?\A:\x\y\"); + } +} + +#[test] +pub fn test_pop() { + macro_rules! tp ( + ($path:expr, $expected:expr, $output:expr) => ({ + let mut actual = PathBuf::from($path); + let output = actual.pop(); + assert!(actual.to_str() == Some($expected) && output == $output, + "popping from {:?}: Expected {:?}/{:?}, got {:?}/{:?}", + $path, $expected, $output, + actual.to_str().unwrap(), output); + }); + ); + + tp!("", "", false); + tp!("/", "/", false); + tp!("foo", "", true); + tp!(".", "", true); + tp!("/foo", "/", true); + tp!("/foo/bar", "/foo", true); + tp!("foo/bar", "foo", true); + tp!("foo/.", "", true); + tp!("foo//bar", "foo", true); + + if cfg!(windows) { + tp!("a\\b\\c", "a\\b", true); + tp!("\\a", "\\", true); + tp!("\\", "\\", false); + + tp!("C:\\a\\b", "C:\\a", true); + tp!("C:\\a", "C:\\", true); + tp!("C:\\", "C:\\", false); + tp!("C:a\\b", "C:a", true); + tp!("C:a", "C:", true); + tp!("C:", "C:", false); + tp!("\\\\server\\share\\a\\b", "\\\\server\\share\\a", true); + tp!("\\\\server\\share\\a", "\\\\server\\share\\", true); + tp!("\\\\server\\share", "\\\\server\\share", false); + tp!("\\\\?\\a\\b\\c", "\\\\?\\a\\b", true); + tp!("\\\\?\\a\\b", "\\\\?\\a\\", true); + tp!("\\\\?\\a", "\\\\?\\a", false); + tp!("\\\\?\\C:\\a\\b", "\\\\?\\C:\\a", true); + tp!("\\\\?\\C:\\a", "\\\\?\\C:\\", true); + tp!("\\\\?\\C:\\", "\\\\?\\C:\\", false); + tp!("\\\\?\\UNC\\server\\share\\a\\b", "\\\\?\\UNC\\server\\share\\a", true); + tp!("\\\\?\\UNC\\server\\share\\a", "\\\\?\\UNC\\server\\share\\", true); + tp!("\\\\?\\UNC\\server\\share", "\\\\?\\UNC\\server\\share", false); + tp!("\\\\.\\a\\b\\c", "\\\\.\\a\\b", true); + tp!("\\\\.\\a\\b", "\\\\.\\a\\", true); + tp!("\\\\.\\a", "\\\\.\\a", false); + + tp!("\\\\?\\a\\b\\", "\\\\?\\a\\", true); + } +} + +#[test] +pub fn test_set_file_name() { + macro_rules! tfn ( + ($path:expr, $file:expr, $expected:expr) => ({ + let mut p = PathBuf::from($path); + p.set_file_name($file); + assert!(p.to_str() == Some($expected), + "setting file name of {:?} to {:?}: Expected {:?}, got {:?}", + $path, $file, $expected, + p.to_str().unwrap()); + }); + ); + + tfn!("foo", "foo", "foo"); + tfn!("foo", "bar", "bar"); + tfn!("foo", "", ""); + tfn!("", "foo", "foo"); + if cfg!(unix) + || cfg!(target_os = "wasi") + || cfg!(all(target_env = "sgx", target_vendor = "fortanix")) + { + tfn!(".", "foo", "./foo"); + tfn!("foo/", "bar", "bar"); + tfn!("foo/.", "bar", "bar"); + tfn!("..", "foo", "../foo"); + tfn!("foo/..", "bar", "foo/../bar"); + tfn!("/", "foo", "/foo"); + } else { + tfn!(".", "foo", r".\foo"); + tfn!(r"foo\", "bar", r"bar"); + tfn!(r"foo\.", "bar", r"bar"); + tfn!("..", "foo", r"..\foo"); + tfn!(r"foo\..", "bar", r"foo\..\bar"); + tfn!(r"\", "foo", r"\foo"); + } +} + +#[test] +pub fn test_set_extension() { + macro_rules! tfe ( + ($path:expr, $ext:expr, $expected:expr, $output:expr) => ({ + let mut p = PathBuf::from($path); + let output = p.set_extension($ext); + assert!(p.to_str() == Some($expected) && output == $output, + "setting extension of {:?} to {:?}: Expected {:?}/{:?}, got {:?}/{:?}", + $path, $ext, $expected, $output, + p.to_str().unwrap(), output); + }); + ); + + tfe!("foo", "txt", "foo.txt", true); + tfe!("foo.bar", "txt", "foo.txt", true); + tfe!("foo.bar.baz", "txt", "foo.bar.txt", true); + tfe!(".test", "txt", ".test.txt", true); + tfe!("foo.txt", "", "foo", true); + tfe!("foo", "", "foo", true); + tfe!("", "foo", "", false); + tfe!(".", "foo", ".", false); + tfe!("foo/", "bar", "foo.bar", true); + tfe!("foo/.", "bar", "foo.bar", true); + tfe!("..", "foo", "..", false); + tfe!("foo/..", "bar", "foo/..", false); + tfe!("/", "foo", "/", false); +} + +#[test] +pub fn test_add_extension() { + macro_rules! tfe ( + ($path:expr, $ext:expr, $expected:expr, $output:expr) => ({ + let mut p = PathBuf::from($path); + let output = p.add_extension($ext); + assert!(p.to_str() == Some($expected) && output == $output, + "adding extension of {:?} to {:?}: Expected {:?}/{:?}, got {:?}/{:?}", + $path, $ext, $expected, $output, + p.to_str().unwrap(), output); + }); + ); + + tfe!("foo", "txt", "foo.txt", true); + tfe!("foo.bar", "txt", "foo.bar.txt", true); + tfe!("foo.bar.baz", "txt", "foo.bar.baz.txt", true); + tfe!(".test", "txt", ".test.txt", true); + tfe!("foo.txt", "", "foo.txt", true); + tfe!("foo", "", "foo", true); + tfe!("", "foo", "", false); + tfe!(".", "foo", ".", false); + tfe!("foo/", "bar", "foo.bar", true); + tfe!("foo/.", "bar", "foo.bar", true); + tfe!("..", "foo", "..", false); + tfe!("foo/..", "bar", "foo/..", false); + tfe!("/", "foo", "/", false); + + // edge cases + tfe!("/foo.ext////", "bar", "/foo.ext.bar", true); +} + +#[test] +pub fn test_with_extension() { + macro_rules! twe ( + ($input:expr, $extension:expr, $expected:expr) => ({ + let input = Path::new($input); + let output = input.with_extension($extension); + + assert!( + output.to_str() == Some($expected), + "calling Path::new({:?}).with_extension({:?}): Expected {:?}, got {:?}", + $input, $extension, $expected, output, + ); + }); + ); + + twe!("foo", "txt", "foo.txt"); + twe!("foo.bar", "txt", "foo.txt"); + twe!("foo.bar.baz", "txt", "foo.bar.txt"); + twe!(".test", "txt", ".test.txt"); + twe!("foo.txt", "", "foo"); + twe!("foo", "", "foo"); + twe!("", "foo", ""); + twe!(".", "foo", "."); + twe!("foo/", "bar", "foo.bar"); + twe!("foo/.", "bar", "foo.bar"); + twe!("..", "foo", ".."); + twe!("foo/..", "bar", "foo/.."); + twe!("/", "foo", "/"); + + // New extension is smaller than file name + twe!("aaa_aaa_aaa", "bbb_bbb", "aaa_aaa_aaa.bbb_bbb"); + // New extension is greater than file name + twe!("bbb_bbb", "aaa_aaa_aaa", "bbb_bbb.aaa_aaa_aaa"); + + // New extension is smaller than previous extension + twe!("ccc.aaa_aaa_aaa", "bbb_bbb", "ccc.bbb_bbb"); + // New extension is greater than previous extension + twe!("ccc.bbb_bbb", "aaa_aaa_aaa", "ccc.aaa_aaa_aaa"); +} + +#[test] +pub fn test_with_added_extension() { + macro_rules! twe ( + ($input:expr, $extension:expr, $expected:expr) => ({ + let input = Path::new($input); + let output = input.with_added_extension($extension); + + assert!( + output.to_str() == Some($expected), + "calling Path::new({:?}).with_added_extension({:?}): Expected {:?}, got {:?}", + $input, $extension, $expected, output, + ); + }); + ); + + twe!("foo", "txt", "foo.txt"); + twe!("foo.bar", "txt", "foo.bar.txt"); + twe!("foo.bar.baz", "txt", "foo.bar.baz.txt"); + twe!(".test", "txt", ".test.txt"); + twe!("foo.txt", "", "foo.txt"); + twe!("foo", "", "foo"); + twe!("", "foo", ""); + twe!(".", "foo", "."); + twe!("foo/", "bar", "foo.bar"); + twe!("foo/.", "bar", "foo.bar"); + twe!("..", "foo", ".."); + twe!("foo/..", "bar", "foo/.."); + twe!("/", "foo", "/"); + + // edge cases + twe!("/foo.ext////", "bar", "/foo.ext.bar"); + + // New extension is smaller than file name + twe!("aaa_aaa_aaa", "bbb_bbb", "aaa_aaa_aaa.bbb_bbb"); + // New extension is greater than file name + twe!("bbb_bbb", "aaa_aaa_aaa", "bbb_bbb.aaa_aaa_aaa"); + + // New extension is smaller than previous extension + twe!("ccc.aaa_aaa_aaa", "bbb_bbb", "ccc.aaa_aaa_aaa.bbb_bbb"); + // New extension is greater than previous extension + twe!("ccc.bbb_bbb", "aaa_aaa_aaa", "ccc.bbb_bbb.aaa_aaa_aaa"); +} + +#[test] +fn test_eq_receivers() { + use std::borrow::Cow; + + let borrowed: &Path = Path::new("foo/bar"); + let mut owned: PathBuf = PathBuf::new(); + owned.push("foo"); + owned.push("bar"); + let borrowed_cow: Cow<'_, Path> = borrowed.into(); + let owned_cow: Cow<'_, Path> = owned.clone().into(); + + macro_rules! t { + ($($current:expr),+) => { + $( + assert_eq!($current, borrowed); + assert_eq!($current, owned); + assert_eq!($current, borrowed_cow); + assert_eq!($current, owned_cow); + )+ + } + } + + t!(borrowed, owned, borrowed_cow, owned_cow); +} + +#[test] +pub fn test_compare() { + use std::hash::{DefaultHasher, Hash, Hasher}; + + fn hash(t: T) -> u64 { + let mut s = DefaultHasher::new(); + t.hash(&mut s); + s.finish() + } + + macro_rules! tc ( + ($path1:expr, $path2:expr, eq: $eq:expr, + starts_with: $starts_with:expr, ends_with: $ends_with:expr, + relative_from: $relative_from:expr) => ({ + let path1 = Path::new($path1); + let path2 = Path::new($path2); + + let eq = path1 == path2; + assert!(eq == $eq, "{:?} == {:?}, expected {:?}, got {:?}", + $path1, $path2, $eq, eq); + assert!($eq == (hash(path1) == hash(path2)), + "{:?} == {:?}, expected {:?}, got {} and {}", + $path1, $path2, $eq, hash(path1), hash(path2)); + + let starts_with = path1.starts_with(path2); + assert!(starts_with == $starts_with, + "{:?}.starts_with({:?}), expected {:?}, got {:?}", $path1, $path2, + $starts_with, starts_with); + + let ends_with = path1.ends_with(path2); + assert!(ends_with == $ends_with, + "{:?}.ends_with({:?}), expected {:?}, got {:?}", $path1, $path2, + $ends_with, ends_with); + + let relative_from = path1.strip_prefix(path2) + .map(|p| p.to_str().unwrap()) + .ok(); + let exp: Option<&str> = $relative_from; + assert!(relative_from == exp, + "{:?}.strip_prefix({:?}), expected {:?}, got {:?}", + $path1, $path2, exp, relative_from); + }); + ); + + tc!("", "", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + + tc!("foo", "", + eq: false, + starts_with: true, + ends_with: true, + relative_from: Some("foo") + ); + + tc!("", "foo", + eq: false, + starts_with: false, + ends_with: false, + relative_from: None + ); + + tc!("foo", "foo", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + + tc!("foo/", "foo", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + + tc!("foo//", "foo", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + + tc!("foo///", "foo", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + + tc!("foo/.", "foo", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + + tc!("foo/./bar", "foo/bar", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + + tc!("foo/.//bar", "foo/bar", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + + tc!("foo//./bar", "foo/bar", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + + tc!("foo/bar", "foo", + eq: false, + starts_with: true, + ends_with: false, + relative_from: Some("bar") + ); + + tc!("foo/bar", "foobar", + eq: false, + starts_with: false, + ends_with: false, + relative_from: None + ); + + tc!("foo/bar/baz", "foo/bar", + eq: false, + starts_with: true, + ends_with: false, + relative_from: Some("baz") + ); + + tc!("foo/bar", "foo/bar/baz", + eq: false, + starts_with: false, + ends_with: false, + relative_from: None + ); + + tc!("./foo/bar/", ".", + eq: false, + starts_with: true, + ends_with: false, + relative_from: Some("foo/bar") + ); + + if cfg!(windows) { + tc!(r"C:\src\rust\cargo-test\test\Cargo.toml", + r"c:\src\rust\cargo-test\test", + eq: false, + starts_with: true, + ends_with: false, + relative_from: Some("Cargo.toml") + ); + + tc!(r"c:\foo", r"C:\foo", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + + tc!(r"C:\foo\.\bar.txt", r"C:\foo\bar.txt", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + + tc!(r"C:\foo\.", r"C:\foo", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + + tc!(r"\\?\C:\foo\.\bar.txt", r"\\?\C:\foo\bar.txt", + eq: false, + starts_with: false, + ends_with: false, + relative_from: None + ); + } +} + +#[test] +fn test_components_debug() { + let path = Path::new("/tmp"); + + let mut components = path.components(); + + let expected = "Components([RootDir, Normal(\"tmp\")])"; + let actual = format!("{components:?}"); + assert_eq!(expected, actual); + + let _ = components.next().unwrap(); + let expected = "Components([Normal(\"tmp\")])"; + let actual = format!("{components:?}"); + assert_eq!(expected, actual); + + let _ = components.next().unwrap(); + let expected = "Components([])"; + let actual = format!("{components:?}"); + assert_eq!(expected, actual); +} + +#[cfg(any(unix, target_os = "wasi"))] +#[test] +fn test_iter_debug() { + let path = Path::new("/tmp"); + + let mut iter = path.iter(); + + let expected = "Iter([\"/\", \"tmp\"])"; + let actual = format!("{iter:?}"); + assert_eq!(expected, actual); + + let _ = iter.next().unwrap(); + let expected = "Iter([\"tmp\"])"; + let actual = format!("{iter:?}"); + assert_eq!(expected, actual); + + let _ = iter.next().unwrap(); + let expected = "Iter([])"; + let actual = format!("{iter:?}"); + assert_eq!(expected, actual); +} + +#[test] +fn into_boxed() { + let orig: &str = "some/sort/of/path"; + let path = Path::new(orig); + let boxed: Box = Box::from(path); + let path_buf = path.to_owned().into_boxed_path().into_path_buf(); + assert_eq!(path, &*boxed); + assert_eq!(&*boxed, &*path_buf); + assert_eq!(&*path_buf, path); +} + +#[test] +fn test_clone_into() { + let mut path_buf = PathBuf::from("supercalifragilisticexpialidocious"); + let path = Path::new("short"); + path.clone_into(&mut path_buf); + assert_eq!(path, path_buf); + assert!(path_buf.into_os_string().capacity() >= 15); +} + +#[test] +fn display_format_flags() { + assert_eq!(format!("a{:#<5}b", Path::new("").display()), "a#####b"); + assert_eq!(format!("a{:#<5}b", Path::new("a").display()), "aa####b"); +} + +#[test] +fn into_rc() { + let orig = "hello/world"; + let path = Path::new(orig); + let rc: Rc = Rc::from(path); + let arc: Arc = Arc::from(path); + + assert_eq!(&*rc, path); + assert_eq!(&*arc, path); + + let rc2: Rc = Rc::from(path.to_owned()); + let arc2: Arc = Arc::from(path.to_owned()); + + assert_eq!(&*rc2, path); + assert_eq!(&*arc2, path); +} + +#[test] +fn test_ord() { + macro_rules! ord( + ($ord:ident, $left:expr, $right:expr) => ({ + use core::cmp::Ordering; + + let left = Path::new($left); + let right = Path::new($right); + assert_eq!(left.cmp(&right), Ordering::$ord); + if (core::cmp::Ordering::$ord == Ordering::Equal) { + assert_eq!(left, right); + + let mut hasher = DefaultHasher::new(); + left.hash(&mut hasher); + let left_hash = hasher.finish(); + hasher = DefaultHasher::new(); + right.hash(&mut hasher); + let right_hash = hasher.finish(); + + assert_eq!(left_hash, right_hash, "hashes for {:?} and {:?} must match", left, right); + } else { + assert_ne!(left, right); + } + }); + ); + + ord!(Less, "1", "2"); + ord!(Less, "/foo/bar", "/foo./bar"); + ord!(Less, "foo/bar", "foo/bar."); + ord!(Equal, "foo/./bar", "foo/bar/"); + ord!(Equal, "foo/bar", "foo/bar/"); + ord!(Equal, "foo/bar", "foo/bar/."); + ord!(Equal, "foo/bar", "foo/bar//"); +} + +#[test] +#[cfg(any(unix, target_os = "wasi"))] +fn test_unix_absolute() { + use std::path::absolute; + + assert!(absolute("").is_err()); + + let relative = "a/b"; + let mut expected = std::env::current_dir().unwrap(); + expected.push(relative); + assert_eq!(absolute(relative).unwrap().as_os_str(), expected.as_os_str()); + + // Test how components are collected. + assert_eq!(absolute("/a/b/c").unwrap().as_os_str(), Path::new("/a/b/c").as_os_str()); + assert_eq!(absolute("/a//b/c").unwrap().as_os_str(), Path::new("/a/b/c").as_os_str()); + assert_eq!(absolute("//a/b/c").unwrap().as_os_str(), Path::new("//a/b/c").as_os_str()); + assert_eq!(absolute("///a/b/c").unwrap().as_os_str(), Path::new("/a/b/c").as_os_str()); + assert_eq!(absolute("/a/b/c/").unwrap().as_os_str(), Path::new("/a/b/c/").as_os_str()); + assert_eq!( + absolute("/a/./b/../c/.././..").unwrap().as_os_str(), + Path::new("/a/b/../c/../..").as_os_str() + ); + + // Test leading `.` and `..` components + let curdir = std::env::current_dir().unwrap(); + assert_eq!(absolute("./a").unwrap().as_os_str(), curdir.join("a").as_os_str()); + assert_eq!(absolute("../a").unwrap().as_os_str(), curdir.join("../a").as_os_str()); // return /pwd/../a +} + +#[test] +#[cfg(windows)] +fn test_windows_absolute() { + use std::path::absolute; + // An empty path is an error. + assert!(absolute("").is_err()); + + let relative = r"a\b"; + let mut expected = std::env::current_dir().unwrap(); + expected.push(relative); + assert_eq!(absolute(relative).unwrap().as_os_str(), expected.as_os_str()); + + macro_rules! unchanged( + ($path:expr) => { + assert_eq!(absolute($path).unwrap().as_os_str(), Path::new($path).as_os_str()); + } + ); + + unchanged!(r"C:\path\to\file"); + unchanged!(r"C:\path\to\file\"); + unchanged!(r"\\server\share\to\file"); + unchanged!(r"\\server.\share.\to\file"); + unchanged!(r"\\.\PIPE\name"); + unchanged!(r"\\.\C:\path\to\COM1"); + unchanged!(r"\\?\C:\path\to\file"); + unchanged!(r"\\?\UNC\server\share\to\file"); + unchanged!(r"\\?\PIPE\name"); + // Verbatim paths are always unchanged, no matter what. + unchanged!(r"\\?\path.\to/file.."); + + assert_eq!( + absolute(r"C:\path..\to.\file.").unwrap().as_os_str(), + Path::new(r"C:\path..\to\file").as_os_str() + ); + assert_eq!(absolute(r"COM1").unwrap().as_os_str(), Path::new(r"\\.\COM1").as_os_str()); +} + +#[test] +#[should_panic = "path separator"] +fn test_extension_path_sep() { + let mut path = PathBuf::from("path/to/file"); + path.set_extension("d/../../../../../etc/passwd"); +} + +#[test] +#[should_panic = "path separator"] +#[cfg(windows)] +fn test_extension_path_sep_alternate() { + let mut path = PathBuf::from("path/to/file"); + path.set_extension("d\\test"); +} + +#[test] +#[cfg(not(windows))] +fn test_extension_path_sep_alternate() { + let mut path = PathBuf::from("path/to/file"); + path.set_extension("d\\test"); + assert_eq!(path, Path::new("path/to/file.d\\test")); +} + +#[test] +fn clone_to_uninit() { + let a = Path::new("hello.txt"); + + let mut storage = vec![MaybeUninit::::uninit(); size_of_val::(a)]; + unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()).cast()) }; + assert_eq!(a.as_os_str().as_encoded_bytes(), unsafe { storage.assume_init_ref() }); + + let mut b: Box = Path::new("world.exe").into(); + assert_eq!(size_of_val::(a), size_of_val::(&b)); + assert_ne!(a, &*b); + unsafe { a.clone_to_uninit(ptr::from_mut::(&mut b).cast()) }; + assert_eq!(a, &*b); +} diff --git a/library/std/tests/pipe_subprocess.rs b/library/std/tests/pipe_subprocess.rs index 1535742a83a21..00d99a578d580 100644 --- a/library/std/tests/pipe_subprocess.rs +++ b/library/std/tests/pipe_subprocess.rs @@ -1,10 +1,9 @@ #![feature(anonymous_pipe)] fn main() { - #[cfg(all(not(miri), any(unix, windows)))] + #[cfg(all(not(miri), any(unix, windows), not(target_os = "emscripten")))] { - use std::io::Read; - use std::pipe::pipe; + use std::io::{Read, pipe}; use std::{env, process}; if env::var("I_AM_THE_CHILD").is_ok() { diff --git a/library/std/tests/process_spawning.rs b/library/std/tests/process_spawning.rs index 3e72e371ade19..43b45cb2d2b5c 100644 --- a/library/std/tests/process_spawning.rs +++ b/library/std/tests/process_spawning.rs @@ -5,7 +5,8 @@ use std::{env, fs, process, str}; mod common; #[test] -#[cfg_attr(any(miri, target_os = "wasi"), ignore)] // Process spawning not supported by Miri and wasi +// Process spawning not supported by Miri, Emscripten and wasi +#[cfg_attr(any(miri, target_os = "emscripten", target_os = "wasi"), ignore)] fn issue_15149() { // If we're the parent, copy our own binary to a new directory. let my_path = env::current_exe().unwrap(); diff --git a/library/std/tests/seq-compare.rs b/library/std/tests/seq-compare.rs index 221f1c7cabde5..ec39c5b603ccc 100644 --- a/library/std/tests/seq-compare.rs +++ b/library/std/tests/seq-compare.rs @@ -1,15 +1,15 @@ #[test] fn seq_compare() { - assert!(("hello".to_string() < "hellr".to_string())); - assert!(("hello ".to_string() > "hello".to_string())); - assert!(("hello".to_string() != "there".to_string())); - assert!((vec![1, 2, 3, 4] > vec![1, 2, 3])); - assert!((vec![1, 2, 3] < vec![1, 2, 3, 4])); - assert!((vec![1, 2, 4, 4] > vec![1, 2, 3, 4])); - assert!((vec![1, 2, 3, 4] < vec![1, 2, 4, 4])); - assert!((vec![1, 2, 3] <= vec![1, 2, 3])); - assert!((vec![1, 2, 3] <= vec![1, 2, 3, 3])); - assert!((vec![1, 2, 3, 4] > vec![1, 2, 3])); + assert!("hello".to_string() < "hellr".to_string()); + assert!("hello ".to_string() > "hello".to_string()); + assert!("hello".to_string() != "there".to_string()); + assert!(vec![1, 2, 3, 4] > vec![1, 2, 3]); + assert!(vec![1, 2, 3] < vec![1, 2, 3, 4]); + assert!(vec![1, 2, 4, 4] > vec![1, 2, 3, 4]); + assert!(vec![1, 2, 3, 4] < vec![1, 2, 4, 4]); + assert!(vec![1, 2, 3] <= vec![1, 2, 3]); + assert!(vec![1, 2, 3] <= vec![1, 2, 3, 3]); + assert!(vec![1, 2, 3, 4] > vec![1, 2, 3]); assert_eq!(vec![1, 2, 3], vec![1, 2, 3]); - assert!((vec![1, 2, 3] != vec![1, 1, 3])); + assert!(vec![1, 2, 3] != vec![1, 1, 3]); } diff --git a/library/std/tests/switch-stdout.rs b/library/std/tests/switch-stdout.rs index 42011a9b3da62..91fe0200f6cae 100644 --- a/library/std/tests/switch-stdout.rs +++ b/library/std/tests/switch-stdout.rs @@ -14,7 +14,7 @@ use std::os::windows::io::OwnedHandle; fn switch_stdout_to(file: OwnedFd) -> OwnedFd { use std::os::unix::prelude::*; - extern "C" { + unsafe extern "C" { fn dup(old: i32) -> i32; fn dup2(old: i32, new: i32) -> i32; } @@ -32,7 +32,7 @@ fn switch_stdout_to(file: OwnedFd) -> OwnedFd { fn switch_stdout_to(file: OwnedHandle) -> OwnedHandle { use std::os::windows::prelude::*; - extern "system" { + unsafe extern "system" { fn GetStdHandle(nStdHandle: u32) -> *mut u8; fn SetStdHandle(nStdHandle: u32, handle: *mut u8) -> i32; } diff --git a/library/std/tests/sync/barrier.rs b/library/std/tests/sync/barrier.rs new file mode 100644 index 0000000000000..8aefff9d5071c --- /dev/null +++ b/library/std/tests/sync/barrier.rs @@ -0,0 +1,35 @@ +use std::sync::mpsc::{TryRecvError, channel}; +use std::sync::{Arc, Barrier}; +use std::thread; + +#[test] +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +fn test_barrier() { + const N: usize = 10; + + let barrier = Arc::new(Barrier::new(N)); + let (tx, rx) = channel(); + + for _ in 0..N - 1 { + let c = barrier.clone(); + let tx = tx.clone(); + thread::spawn(move || { + tx.send(c.wait().is_leader()).unwrap(); + }); + } + + // At this point, all spawned threads should be blocked, + // so we shouldn't get anything from the port + assert!(matches!(rx.try_recv(), Err(TryRecvError::Empty))); + + let mut leader_found = barrier.wait().is_leader(); + + // Now, the barrier is cleared and we should get data. + for _ in 0..N - 1 { + if rx.recv().unwrap() { + assert!(!leader_found); + leader_found = true; + } + } + assert!(leader_found); +} diff --git a/library/std/tests/sync/condvar.rs b/library/std/tests/sync/condvar.rs new file mode 100644 index 0000000000000..834de6bb1c295 --- /dev/null +++ b/library/std/tests/sync/condvar.rs @@ -0,0 +1,190 @@ +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::mpsc::channel; +use std::sync::{Arc, Condvar, Mutex}; +use std::thread; +use std::time::Duration; + +#[test] +fn smoke() { + let c = Condvar::new(); + c.notify_one(); + c.notify_all(); +} + +#[test] +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +fn notify_one() { + let m = Arc::new(Mutex::new(())); + let m2 = m.clone(); + let c = Arc::new(Condvar::new()); + let c2 = c.clone(); + + let g = m.lock().unwrap(); + let _t = thread::spawn(move || { + let _g = m2.lock().unwrap(); + c2.notify_one(); + }); + let g = c.wait(g).unwrap(); + drop(g); +} + +#[test] +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +fn notify_all() { + const N: usize = 10; + + let data = Arc::new((Mutex::new(0), Condvar::new())); + let (tx, rx) = channel(); + for _ in 0..N { + let data = data.clone(); + let tx = tx.clone(); + thread::spawn(move || { + let &(ref lock, ref cond) = &*data; + let mut cnt = lock.lock().unwrap(); + *cnt += 1; + if *cnt == N { + tx.send(()).unwrap(); + } + while *cnt != 0 { + cnt = cond.wait(cnt).unwrap(); + } + tx.send(()).unwrap(); + }); + } + drop(tx); + + let &(ref lock, ref cond) = &*data; + rx.recv().unwrap(); + let mut cnt = lock.lock().unwrap(); + *cnt = 0; + cond.notify_all(); + drop(cnt); + + for _ in 0..N { + rx.recv().unwrap(); + } +} + +#[test] +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +fn wait_while() { + let pair = Arc::new((Mutex::new(false), Condvar::new())); + let pair2 = pair.clone(); + + // Inside of our lock, spawn a new thread, and then wait for it to start. + thread::spawn(move || { + let &(ref lock, ref cvar) = &*pair2; + let mut started = lock.lock().unwrap(); + *started = true; + // We notify the condvar that the value has changed. + cvar.notify_one(); + }); + + // Wait for the thread to start up. + let &(ref lock, ref cvar) = &*pair; + let guard = cvar.wait_while(lock.lock().unwrap(), |started| !*started); + assert!(*guard.unwrap()); +} + +#[test] +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // condvar wait not supported +fn wait_timeout_wait() { + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); + + loop { + let g = m.lock().unwrap(); + let (_g, no_timeout) = c.wait_timeout(g, Duration::from_millis(1)).unwrap(); + // spurious wakeups mean this isn't necessarily true + // so execute test again, if not timeout + if !no_timeout.timed_out() { + continue; + } + + break; + } +} + +#[test] +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // condvar wait not supported +fn wait_timeout_while_wait() { + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); + + let g = m.lock().unwrap(); + let (_g, wait) = c.wait_timeout_while(g, Duration::from_millis(1), |_| true).unwrap(); + // no spurious wakeups. ensure it timed-out + assert!(wait.timed_out()); +} + +#[test] +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // condvar wait not supported +fn wait_timeout_while_instant_satisfy() { + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); + + let g = m.lock().unwrap(); + let (_g, wait) = c.wait_timeout_while(g, Duration::from_millis(0), |_| false).unwrap(); + // ensure it didn't time-out even if we were not given any time. + assert!(!wait.timed_out()); +} + +#[test] +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +fn wait_timeout_while_wake() { + let pair = Arc::new((Mutex::new(false), Condvar::new())); + let pair_copy = pair.clone(); + + let &(ref m, ref c) = &*pair; + let g = m.lock().unwrap(); + let _t = thread::spawn(move || { + let &(ref lock, ref cvar) = &*pair_copy; + let mut started = lock.lock().unwrap(); + thread::sleep(Duration::from_millis(1)); + *started = true; + cvar.notify_one(); + }); + let (g2, wait) = c + .wait_timeout_while(g, Duration::from_millis(u64::MAX), |&mut notified| !notified) + .unwrap(); + // ensure it didn't time-out even if we were not given any time. + assert!(!wait.timed_out()); + assert!(*g2); +} + +#[test] +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +fn wait_timeout_wake() { + let m = Arc::new(Mutex::new(())); + let c = Arc::new(Condvar::new()); + + loop { + let g = m.lock().unwrap(); + + let c2 = c.clone(); + let m2 = m.clone(); + + let notified = Arc::new(AtomicBool::new(false)); + let notified_copy = notified.clone(); + + let t = thread::spawn(move || { + let _g = m2.lock().unwrap(); + thread::sleep(Duration::from_millis(1)); + notified_copy.store(true, Ordering::Relaxed); + c2.notify_one(); + }); + let (g, timeout_res) = c.wait_timeout(g, Duration::from_millis(u64::MAX)).unwrap(); + assert!(!timeout_res.timed_out()); + // spurious wakeups mean this isn't necessarily true + // so execute test again, if not notified + if !notified.load(Ordering::Relaxed) { + t.join().unwrap(); + continue; + } + drop(g); + + t.join().unwrap(); + + break; + } +} diff --git a/library/std/tests/sync/lazy_lock.rs b/library/std/tests/sync/lazy_lock.rs new file mode 100644 index 0000000000000..6c14b79f2ce7c --- /dev/null +++ b/library/std/tests/sync/lazy_lock.rs @@ -0,0 +1,167 @@ +use std::cell::LazyCell; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::SeqCst; +use std::sync::{LazyLock, Mutex, OnceLock}; +use std::{panic, thread}; + +fn spawn_and_wait(f: impl FnOnce() -> R + Send + 'static) -> R { + thread::spawn(f).join().unwrap() +} + +#[test] +fn lazy_default() { + static CALLED: AtomicUsize = AtomicUsize::new(0); + + struct Foo(u8); + impl Default for Foo { + fn default() -> Self { + CALLED.fetch_add(1, SeqCst); + Foo(42) + } + } + + let lazy: LazyCell> = <_>::default(); + + assert_eq!(CALLED.load(SeqCst), 0); + + assert_eq!(lazy.lock().unwrap().0, 42); + assert_eq!(CALLED.load(SeqCst), 1); + + lazy.lock().unwrap().0 = 21; + + assert_eq!(lazy.lock().unwrap().0, 21); + assert_eq!(CALLED.load(SeqCst), 1); +} + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn lazy_poisoning() { + let x: LazyCell = LazyCell::new(|| panic!("kaboom")); + for _ in 0..2 { + let res = panic::catch_unwind(panic::AssertUnwindSafe(|| x.len())); + assert!(res.is_err()); + } +} + +#[test] +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +fn sync_lazy_new() { + static CALLED: AtomicUsize = AtomicUsize::new(0); + static SYNC_LAZY: LazyLock = LazyLock::new(|| { + CALLED.fetch_add(1, SeqCst); + 92 + }); + + assert_eq!(CALLED.load(SeqCst), 0); + + spawn_and_wait(|| { + let y = *SYNC_LAZY - 30; + assert_eq!(y, 62); + assert_eq!(CALLED.load(SeqCst), 1); + }); + + let y = *SYNC_LAZY - 30; + assert_eq!(y, 62); + assert_eq!(CALLED.load(SeqCst), 1); +} + +#[test] +fn sync_lazy_default() { + static CALLED: AtomicUsize = AtomicUsize::new(0); + + struct Foo(u8); + impl Default for Foo { + fn default() -> Self { + CALLED.fetch_add(1, SeqCst); + Foo(42) + } + } + + let lazy: LazyLock> = <_>::default(); + + assert_eq!(CALLED.load(SeqCst), 0); + + assert_eq!(lazy.lock().unwrap().0, 42); + assert_eq!(CALLED.load(SeqCst), 1); + + lazy.lock().unwrap().0 = 21; + + assert_eq!(lazy.lock().unwrap().0, 21); + assert_eq!(CALLED.load(SeqCst), 1); +} + +#[test] +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +fn static_sync_lazy() { + static XS: LazyLock> = LazyLock::new(|| { + let mut xs = Vec::new(); + xs.push(1); + xs.push(2); + xs.push(3); + xs + }); + + spawn_and_wait(|| { + assert_eq!(&*XS, &vec![1, 2, 3]); + }); + + assert_eq!(&*XS, &vec![1, 2, 3]); +} + +#[test] +fn static_sync_lazy_via_fn() { + fn xs() -> &'static Vec { + static XS: OnceLock> = OnceLock::new(); + XS.get_or_init(|| { + let mut xs = Vec::new(); + xs.push(1); + xs.push(2); + xs.push(3); + xs + }) + } + assert_eq!(xs(), &vec![1, 2, 3]); +} + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn sync_lazy_poisoning() { + let x: LazyLock = LazyLock::new(|| panic!("kaboom")); + for _ in 0..2 { + let res = panic::catch_unwind(|| x.len()); + assert!(res.is_err()); + } +} + +// Check that we can infer `T` from closure's type. +#[test] +fn lazy_type_inference() { + let _ = LazyCell::new(|| ()); +} + +#[test] +fn is_sync_send() { + fn assert_traits() {} + assert_traits::>(); +} + +#[test] +#[should_panic = "has previously been poisoned"] +fn lazy_force_mut_panic() { + let mut lazy = LazyLock::::new(|| panic!()); + panic::catch_unwind(panic::AssertUnwindSafe(|| { + let _ = LazyLock::force_mut(&mut lazy); + })) + .unwrap_err(); + let _ = &*lazy; +} + +#[test] +fn lazy_force_mut() { + let s = "abc".to_owned(); + let mut lazy = LazyLock::new(move || s); + LazyLock::force_mut(&mut lazy); + let p = LazyLock::force_mut(&mut lazy); + p.clear(); + LazyLock::force_mut(&mut lazy); +} diff --git a/library/std/tests/sync/lib.rs b/library/std/tests/sync/lib.rs new file mode 100644 index 0000000000000..51190f0894fb7 --- /dev/null +++ b/library/std/tests/sync/lib.rs @@ -0,0 +1,31 @@ +#![feature(lazy_get)] +#![feature(mapped_lock_guards)] +#![feature(mpmc_channel)] +#![feature(once_cell_try)] +#![feature(lock_value_accessors)] +#![feature(reentrant_lock)] +#![feature(rwlock_downgrade)] +#![feature(std_internals)] +#![allow(internal_features)] + +mod barrier; +mod condvar; +mod lazy_lock; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod mpmc; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod mpsc; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod mpsc_sync; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod mutex; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod once; +mod once_lock; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod reentrant_lock; +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod rwlock; + +#[path = "../common/mod.rs"] +mod common; diff --git a/library/std/tests/sync/mpmc.rs b/library/std/tests/sync/mpmc.rs new file mode 100644 index 0000000000000..81b92297f76a3 --- /dev/null +++ b/library/std/tests/sync/mpmc.rs @@ -0,0 +1,729 @@ +use std::sync::mpmc::*; +use std::time::{Duration, Instant}; +use std::{env, thread}; + +pub fn stress_factor() -> usize { + match env::var("RUST_TEST_STRESS") { + Ok(val) => val.parse().unwrap(), + Err(..) => 1, + } +} + +#[test] +fn smoke() { + let (tx, rx) = channel::(); + tx.send(1).unwrap(); + assert_eq!(rx.recv().unwrap(), 1); +} + +#[test] +fn drop_full() { + let (tx, _rx) = channel::>(); + tx.send(Box::new(1)).unwrap(); +} + +#[test] +fn drop_full_shared() { + let (tx, _rx) = channel::>(); + drop(tx.clone()); + drop(tx.clone()); + tx.send(Box::new(1)).unwrap(); +} + +#[test] +fn smoke_shared() { + let (tx, rx) = channel::(); + tx.send(1).unwrap(); + assert_eq!(rx.recv().unwrap(), 1); + let tx = tx.clone(); + tx.send(1).unwrap(); + assert_eq!(rx.recv().unwrap(), 1); +} + +#[test] +fn smoke_threads() { + let (tx, rx) = channel::(); + let t1 = thread::spawn(move || { + for i in 0..2 { + tx.send(i).unwrap(); + } + }); + let t2 = thread::spawn(move || { + assert_eq!(rx.recv().unwrap(), 0); + assert_eq!(rx.recv().unwrap(), 1); + }); + t1.join().unwrap(); + t2.join().unwrap(); +} + +#[test] +fn smoke_port_gone() { + let (tx, rx) = channel::(); + drop(rx); + assert!(tx.send(1).is_err()); +} + +#[test] +fn smoke_shared_port_gone() { + let (tx, rx) = channel::(); + drop(rx); + assert!(tx.send(1).is_err()) +} + +#[test] +fn smoke_shared_port_gone2() { + let (tx, rx) = channel::(); + drop(rx); + let tx2 = tx.clone(); + drop(tx); + assert!(tx2.send(1).is_err()); +} + +#[test] +fn port_gone_concurrent() { + let (tx, rx) = channel::(); + let _t = thread::spawn(move || { + rx.recv().unwrap(); + }); + while tx.send(1).is_ok() {} +} + +#[test] +fn port_gone_concurrent_shared() { + let (tx, rx) = channel::(); + let tx2 = tx.clone(); + let _t = thread::spawn(move || { + rx.recv().unwrap(); + }); + while tx.send(1).is_ok() && tx2.send(1).is_ok() {} +} + +#[test] +fn smoke_chan_gone() { + let (tx, rx) = channel::(); + drop(tx); + assert!(rx.recv().is_err()); +} + +#[test] +fn smoke_chan_gone_shared() { + let (tx, rx) = channel::<()>(); + let tx2 = tx.clone(); + drop(tx); + drop(tx2); + assert!(rx.recv().is_err()); +} + +#[test] +fn chan_gone_concurrent() { + let (tx, rx) = channel::(); + let _t = thread::spawn(move || { + tx.send(1).unwrap(); + tx.send(1).unwrap(); + }); + while rx.recv().is_ok() {} +} + +#[test] +fn stress() { + let count = if cfg!(miri) { 100 } else { 10000 }; + let (tx, rx) = channel::(); + let t = thread::spawn(move || { + for _ in 0..count { + tx.send(1).unwrap(); + } + }); + for _ in 0..count { + assert_eq!(rx.recv().unwrap(), 1); + } + t.join().ok().expect("thread panicked"); +} + +#[test] +fn stress_shared() { + const AMT: u32 = if cfg!(miri) { 100 } else { 10000 }; + const NTHREADS: u32 = 8; + let (tx, rx) = channel::(); + + let t = thread::spawn(move || { + for _ in 0..AMT * NTHREADS { + assert_eq!(rx.recv().unwrap(), 1); + } + match rx.try_recv() { + Ok(..) => panic!(), + _ => {} + } + }); + + for _ in 0..NTHREADS { + let tx = tx.clone(); + thread::spawn(move || { + for _ in 0..AMT { + tx.send(1).unwrap(); + } + }); + } + drop(tx); + t.join().ok().expect("thread panicked"); +} + +#[test] +fn send_from_outside_runtime() { + let (tx1, rx1) = channel::<()>(); + let (tx2, rx2) = channel::(); + let t1 = thread::spawn(move || { + tx1.send(()).unwrap(); + for _ in 0..40 { + assert_eq!(rx2.recv().unwrap(), 1); + } + }); + rx1.recv().unwrap(); + let t2 = thread::spawn(move || { + for _ in 0..40 { + tx2.send(1).unwrap(); + } + }); + t1.join().ok().expect("thread panicked"); + t2.join().ok().expect("thread panicked"); +} + +#[test] +fn recv_from_outside_runtime() { + let (tx, rx) = channel::(); + let t = thread::spawn(move || { + for _ in 0..40 { + assert_eq!(rx.recv().unwrap(), 1); + } + }); + for _ in 0..40 { + tx.send(1).unwrap(); + } + t.join().ok().expect("thread panicked"); +} + +#[test] +fn no_runtime() { + let (tx1, rx1) = channel::(); + let (tx2, rx2) = channel::(); + let t1 = thread::spawn(move || { + assert_eq!(rx1.recv().unwrap(), 1); + tx2.send(2).unwrap(); + }); + let t2 = thread::spawn(move || { + tx1.send(1).unwrap(); + assert_eq!(rx2.recv().unwrap(), 2); + }); + t1.join().ok().expect("thread panicked"); + t2.join().ok().expect("thread panicked"); +} + +#[test] +fn oneshot_single_thread_close_port_first() { + // Simple test of closing without sending + let (_tx, rx) = channel::(); + drop(rx); +} + +#[test] +fn oneshot_single_thread_close_chan_first() { + // Simple test of closing without sending + let (tx, _rx) = channel::(); + drop(tx); +} + +#[test] +fn oneshot_single_thread_send_port_close() { + // Testing that the sender cleans up the payload if receiver is closed + let (tx, rx) = channel::>(); + drop(rx); + assert!(tx.send(Box::new(0)).is_err()); +} + +#[test] +fn oneshot_single_thread_recv_chan_close() { + // Receiving on a closed chan will panic + let res = thread::spawn(move || { + let (tx, rx) = channel::(); + drop(tx); + rx.recv().unwrap(); + }) + .join(); + // What is our res? + assert!(res.is_err()); +} + +#[test] +fn oneshot_single_thread_send_then_recv() { + let (tx, rx) = channel::>(); + tx.send(Box::new(10)).unwrap(); + assert!(*rx.recv().unwrap() == 10); +} + +#[test] +fn oneshot_single_thread_try_send_open() { + let (tx, rx) = channel::(); + assert!(tx.send(10).is_ok()); + assert!(rx.recv().unwrap() == 10); +} + +#[test] +fn oneshot_single_thread_try_send_closed() { + let (tx, rx) = channel::(); + drop(rx); + assert!(tx.send(10).is_err()); +} + +#[test] +fn oneshot_single_thread_try_recv_open() { + let (tx, rx) = channel::(); + tx.send(10).unwrap(); + assert!(rx.recv() == Ok(10)); +} + +#[test] +fn oneshot_single_thread_try_recv_closed() { + let (tx, rx) = channel::(); + drop(tx); + assert!(rx.recv().is_err()); +} + +#[test] +fn oneshot_single_thread_peek_data() { + let (tx, rx) = channel::(); + assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); + tx.send(10).unwrap(); + assert_eq!(rx.try_recv(), Ok(10)); +} + +#[test] +fn oneshot_single_thread_peek_close() { + let (tx, rx) = channel::(); + drop(tx); + assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); + assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); +} + +#[test] +fn oneshot_single_thread_peek_open() { + let (_tx, rx) = channel::(); + assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); +} + +#[test] +fn oneshot_multi_task_recv_then_send() { + let (tx, rx) = channel::>(); + let _t = thread::spawn(move || { + assert!(*rx.recv().unwrap() == 10); + }); + + tx.send(Box::new(10)).unwrap(); +} + +#[test] +fn oneshot_multi_task_recv_then_close() { + let (tx, rx) = channel::>(); + let _t = thread::spawn(move || { + drop(tx); + }); + let res = thread::spawn(move || { + assert!(*rx.recv().unwrap() == 10); + }) + .join(); + assert!(res.is_err()); +} + +#[test] +fn oneshot_multi_thread_close_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel::(); + let _t = thread::spawn(move || { + drop(rx); + }); + drop(tx); + } +} + +#[test] +fn oneshot_multi_thread_send_close_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel::(); + let _t = thread::spawn(move || { + drop(rx); + }); + let _ = thread::spawn(move || { + tx.send(1).unwrap(); + }) + .join(); + } +} + +#[test] +fn oneshot_multi_thread_recv_close_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel::(); + thread::spawn(move || { + let res = thread::spawn(move || { + rx.recv().unwrap(); + }) + .join(); + assert!(res.is_err()); + }); + let _t = thread::spawn(move || { + thread::spawn(move || { + drop(tx); + }); + }); + } +} + +#[test] +fn oneshot_multi_thread_send_recv_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel::>(); + let _t = thread::spawn(move || { + tx.send(Box::new(10)).unwrap(); + }); + assert!(*rx.recv().unwrap() == 10); + } +} + +#[test] +fn stream_send_recv_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel(); + + send(tx, 0); + recv(rx, 0); + + fn send(tx: Sender>, i: i32) { + if i == 10 { + return; + } + + thread::spawn(move || { + tx.send(Box::new(i)).unwrap(); + send(tx, i + 1); + }); + } + + fn recv(rx: Receiver>, i: i32) { + if i == 10 { + return; + } + + thread::spawn(move || { + assert!(*rx.recv().unwrap() == i); + recv(rx, i + 1); + }); + } + } +} + +#[test] +fn oneshot_single_thread_recv_timeout() { + let (tx, rx) = channel(); + tx.send(()).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); + tx.send(()).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); +} + +#[test] +fn stress_recv_timeout_two_threads() { + let (tx, rx) = channel(); + let stress = stress_factor() + 100; + let timeout = Duration::from_millis(100); + + thread::spawn(move || { + for i in 0..stress { + if i % 2 == 0 { + thread::sleep(timeout * 2); + } + tx.send(1usize).unwrap(); + } + }); + + let mut recv_count = 0; + loop { + match rx.recv_timeout(timeout) { + Ok(n) => { + assert_eq!(n, 1usize); + recv_count += 1; + } + Err(RecvTimeoutError::Timeout) => continue, + Err(RecvTimeoutError::Disconnected) => break, + } + } + + assert_eq!(recv_count, stress); +} + +#[test] +fn recv_timeout_upgrade() { + let (tx, rx) = channel::<()>(); + let timeout = Duration::from_millis(1); + let _tx_clone = tx.clone(); + + let start = Instant::now(); + assert_eq!(rx.recv_timeout(timeout), Err(RecvTimeoutError::Timeout)); + assert!(Instant::now() >= start + timeout); +} + +#[test] +fn stress_recv_timeout_shared() { + let (tx, rx) = channel(); + let stress = stress_factor() + 100; + + for i in 0..stress { + let tx = tx.clone(); + thread::spawn(move || { + thread::sleep(Duration::from_millis(i as u64 * 10)); + tx.send(1usize).unwrap(); + }); + } + + drop(tx); + + let mut recv_count = 0; + loop { + match rx.recv_timeout(Duration::from_millis(10)) { + Ok(n) => { + assert_eq!(n, 1usize); + recv_count += 1; + } + Err(RecvTimeoutError::Timeout) => continue, + Err(RecvTimeoutError::Disconnected) => break, + } + } + + assert_eq!(recv_count, stress); +} + +#[test] +fn very_long_recv_timeout_wont_panic() { + let (tx, rx) = channel::<()>(); + let join_handle = thread::spawn(move || rx.recv_timeout(Duration::from_secs(u64::MAX))); + thread::sleep(Duration::from_secs(1)); + assert!(tx.send(()).is_ok()); + assert_eq!(join_handle.join().unwrap(), Ok(())); +} + +#[test] +fn recv_a_lot() { + let count = if cfg!(miri) { 1000 } else { 10000 }; + // Regression test that we don't run out of stack in scheduler context + let (tx, rx) = channel(); + for _ in 0..count { + tx.send(()).unwrap(); + } + for _ in 0..count { + rx.recv().unwrap(); + } +} + +#[test] +fn shared_recv_timeout() { + let (tx, rx) = channel(); + let total = 5; + for _ in 0..total { + let tx = tx.clone(); + thread::spawn(move || { + tx.send(()).unwrap(); + }); + } + + for _ in 0..total { + rx.recv().unwrap(); + } + + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); + tx.send(()).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); +} + +#[test] +fn shared_chan_stress() { + let (tx, rx) = channel(); + let total = stress_factor() + 100; + for _ in 0..total { + let tx = tx.clone(); + thread::spawn(move || { + tx.send(()).unwrap(); + }); + } + + for _ in 0..total { + rx.recv().unwrap(); + } +} + +#[test] +fn test_nested_recv_iter() { + let (tx, rx) = channel::(); + let (total_tx, total_rx) = channel::(); + + let _t = thread::spawn(move || { + let mut acc = 0; + for x in rx.iter() { + acc += x; + } + total_tx.send(acc).unwrap(); + }); + + tx.send(3).unwrap(); + tx.send(1).unwrap(); + tx.send(2).unwrap(); + drop(tx); + assert_eq!(total_rx.recv().unwrap(), 6); +} + +#[test] +fn test_recv_iter_break() { + let (tx, rx) = channel::(); + let (count_tx, count_rx) = channel(); + + let _t = thread::spawn(move || { + let mut count = 0; + for x in rx.iter() { + if count >= 3 { + break; + } else { + count += x; + } + } + count_tx.send(count).unwrap(); + }); + + tx.send(2).unwrap(); + tx.send(2).unwrap(); + tx.send(2).unwrap(); + let _ = tx.send(2); + drop(tx); + assert_eq!(count_rx.recv().unwrap(), 4); +} + +#[test] +fn test_recv_try_iter() { + let (request_tx, request_rx) = channel(); + let (response_tx, response_rx) = channel(); + + // Request `x`s until we have `6`. + let t = thread::spawn(move || { + let mut count = 0; + loop { + for x in response_rx.try_iter() { + count += x; + if count == 6 { + return count; + } + } + request_tx.send(()).unwrap(); + } + }); + + for _ in request_rx.iter() { + if response_tx.send(2).is_err() { + break; + } + } + + assert_eq!(t.join().unwrap(), 6); +} + +#[test] +fn test_recv_into_iter_owned() { + let mut iter = { + let (tx, rx) = channel::(); + tx.send(1).unwrap(); + tx.send(2).unwrap(); + + rx.into_iter() + }; + assert_eq!(iter.next().unwrap(), 1); + assert_eq!(iter.next().unwrap(), 2); + assert_eq!(iter.next().is_none(), true); +} + +#[test] +fn test_recv_into_iter_borrowed() { + let (tx, rx) = channel::(); + tx.send(1).unwrap(); + tx.send(2).unwrap(); + drop(tx); + let mut iter = (&rx).into_iter(); + assert_eq!(iter.next().unwrap(), 1); + assert_eq!(iter.next().unwrap(), 2); + assert_eq!(iter.next().is_none(), true); +} + +#[test] +fn try_recv_states() { + let (tx1, rx1) = channel::(); + let (tx2, rx2) = channel::<()>(); + let (tx3, rx3) = channel::<()>(); + let _t = thread::spawn(move || { + rx2.recv().unwrap(); + tx1.send(1).unwrap(); + tx3.send(()).unwrap(); + rx2.recv().unwrap(); + drop(tx1); + tx3.send(()).unwrap(); + }); + + assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); + tx2.send(()).unwrap(); + rx3.recv().unwrap(); + assert_eq!(rx1.try_recv(), Ok(1)); + assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); + tx2.send(()).unwrap(); + rx3.recv().unwrap(); + assert_eq!(rx1.try_recv(), Err(TryRecvError::Disconnected)); +} + +// This bug used to end up in a livelock inside of the Receiver destructor +// because the internal state of the Shared packet was corrupted +#[test] +fn destroy_upgraded_shared_port_when_sender_still_active() { + let (tx, rx) = channel(); + let (tx2, rx2) = channel(); + let _t = thread::spawn(move || { + rx.recv().unwrap(); // wait on a oneshot + drop(rx); // destroy a shared + tx2.send(()).unwrap(); + }); + // make sure the other thread has gone to sleep + for _ in 0..5000 { + thread::yield_now(); + } + + // upgrade to a shared chan and send a message + let t = tx.clone(); + drop(tx); + t.send(()).unwrap(); + + // wait for the child thread to exit before we exit + rx2.recv().unwrap(); +} + +#[test] +fn issue_32114() { + let (tx, _) = channel(); + let _ = tx.send(123); + assert_eq!(tx.send(123), Err(SendError(123))); +} + +#[test] +fn issue_39364() { + let (tx, rx) = channel::<()>(); + let t = thread::spawn(move || { + thread::sleep(Duration::from_millis(300)); + let _ = tx.clone(); + // Don't drop; hand back to caller. + tx + }); + + let _ = rx.recv_timeout(Duration::from_millis(500)); + let _tx = t.join().unwrap(); // delay dropping until end of test + let _ = rx.recv_timeout(Duration::from_millis(500)); +} diff --git a/library/std/tests/sync/mpsc.rs b/library/std/tests/sync/mpsc.rs new file mode 100644 index 0000000000000..1d8edfde44bed --- /dev/null +++ b/library/std/tests/sync/mpsc.rs @@ -0,0 +1,722 @@ +use std::sync::mpsc::*; +use std::time::{Duration, Instant}; +use std::{env, thread}; + +pub fn stress_factor() -> usize { + match env::var("RUST_TEST_STRESS") { + Ok(val) => val.parse().unwrap(), + Err(..) => 1, + } +} + +#[test] +fn smoke() { + let (tx, rx) = channel::(); + tx.send(1).unwrap(); + assert_eq!(rx.recv().unwrap(), 1); +} + +#[test] +fn drop_full() { + let (tx, _rx) = channel::>(); + tx.send(Box::new(1)).unwrap(); +} + +#[test] +fn drop_full_shared() { + let (tx, _rx) = channel::>(); + drop(tx.clone()); + drop(tx.clone()); + tx.send(Box::new(1)).unwrap(); +} + +#[test] +fn smoke_shared() { + let (tx, rx) = channel::(); + tx.send(1).unwrap(); + assert_eq!(rx.recv().unwrap(), 1); + let tx = tx.clone(); + tx.send(1).unwrap(); + assert_eq!(rx.recv().unwrap(), 1); +} + +#[test] +fn smoke_threads() { + let (tx, rx) = channel::(); + let _t = thread::spawn(move || { + tx.send(1).unwrap(); + }); + assert_eq!(rx.recv().unwrap(), 1); +} + +#[test] +fn smoke_port_gone() { + let (tx, rx) = channel::(); + drop(rx); + assert!(tx.send(1).is_err()); +} + +#[test] +fn smoke_shared_port_gone() { + let (tx, rx) = channel::(); + drop(rx); + assert!(tx.send(1).is_err()) +} + +#[test] +fn smoke_shared_port_gone2() { + let (tx, rx) = channel::(); + drop(rx); + let tx2 = tx.clone(); + drop(tx); + assert!(tx2.send(1).is_err()); +} + +#[test] +fn port_gone_concurrent() { + let (tx, rx) = channel::(); + let _t = thread::spawn(move || { + rx.recv().unwrap(); + }); + while tx.send(1).is_ok() {} +} + +#[test] +fn port_gone_concurrent_shared() { + let (tx, rx) = channel::(); + let tx2 = tx.clone(); + let _t = thread::spawn(move || { + rx.recv().unwrap(); + }); + while tx.send(1).is_ok() && tx2.send(1).is_ok() {} +} + +#[test] +fn smoke_chan_gone() { + let (tx, rx) = channel::(); + drop(tx); + assert!(rx.recv().is_err()); +} + +#[test] +fn smoke_chan_gone_shared() { + let (tx, rx) = channel::<()>(); + let tx2 = tx.clone(); + drop(tx); + drop(tx2); + assert!(rx.recv().is_err()); +} + +#[test] +fn chan_gone_concurrent() { + let (tx, rx) = channel::(); + let _t = thread::spawn(move || { + tx.send(1).unwrap(); + tx.send(1).unwrap(); + }); + while rx.recv().is_ok() {} +} + +#[test] +fn stress() { + let count = if cfg!(miri) { 100 } else { 10000 }; + let (tx, rx) = channel::(); + let t = thread::spawn(move || { + for _ in 0..count { + tx.send(1).unwrap(); + } + }); + for _ in 0..count { + assert_eq!(rx.recv().unwrap(), 1); + } + t.join().ok().expect("thread panicked"); +} + +#[test] +fn stress_shared() { + const AMT: u32 = if cfg!(miri) { 100 } else { 10000 }; + const NTHREADS: u32 = 8; + let (tx, rx) = channel::(); + + let t = thread::spawn(move || { + for _ in 0..AMT * NTHREADS { + assert_eq!(rx.recv().unwrap(), 1); + } + match rx.try_recv() { + Ok(..) => panic!(), + _ => {} + } + }); + + for _ in 0..NTHREADS { + let tx = tx.clone(); + thread::spawn(move || { + for _ in 0..AMT { + tx.send(1).unwrap(); + } + }); + } + drop(tx); + t.join().ok().expect("thread panicked"); +} + +#[test] +fn send_from_outside_runtime() { + let (tx1, rx1) = channel::<()>(); + let (tx2, rx2) = channel::(); + let t1 = thread::spawn(move || { + tx1.send(()).unwrap(); + for _ in 0..40 { + assert_eq!(rx2.recv().unwrap(), 1); + } + }); + rx1.recv().unwrap(); + let t2 = thread::spawn(move || { + for _ in 0..40 { + tx2.send(1).unwrap(); + } + }); + t1.join().ok().expect("thread panicked"); + t2.join().ok().expect("thread panicked"); +} + +#[test] +fn recv_from_outside_runtime() { + let (tx, rx) = channel::(); + let t = thread::spawn(move || { + for _ in 0..40 { + assert_eq!(rx.recv().unwrap(), 1); + } + }); + for _ in 0..40 { + tx.send(1).unwrap(); + } + t.join().ok().expect("thread panicked"); +} + +#[test] +fn no_runtime() { + let (tx1, rx1) = channel::(); + let (tx2, rx2) = channel::(); + let t1 = thread::spawn(move || { + assert_eq!(rx1.recv().unwrap(), 1); + tx2.send(2).unwrap(); + }); + let t2 = thread::spawn(move || { + tx1.send(1).unwrap(); + assert_eq!(rx2.recv().unwrap(), 2); + }); + t1.join().ok().expect("thread panicked"); + t2.join().ok().expect("thread panicked"); +} + +#[test] +fn oneshot_single_thread_close_port_first() { + // Simple test of closing without sending + let (_tx, rx) = channel::(); + drop(rx); +} + +#[test] +fn oneshot_single_thread_close_chan_first() { + // Simple test of closing without sending + let (tx, _rx) = channel::(); + drop(tx); +} + +#[test] +fn oneshot_single_thread_send_port_close() { + // Testing that the sender cleans up the payload if receiver is closed + let (tx, rx) = channel::>(); + drop(rx); + assert!(tx.send(Box::new(0)).is_err()); +} + +#[test] +fn oneshot_single_thread_recv_chan_close() { + // Receiving on a closed chan will panic + let res = thread::spawn(move || { + let (tx, rx) = channel::(); + drop(tx); + rx.recv().unwrap(); + }) + .join(); + // What is our res? + assert!(res.is_err()); +} + +#[test] +fn oneshot_single_thread_send_then_recv() { + let (tx, rx) = channel::>(); + tx.send(Box::new(10)).unwrap(); + assert!(*rx.recv().unwrap() == 10); +} + +#[test] +fn oneshot_single_thread_try_send_open() { + let (tx, rx) = channel::(); + assert!(tx.send(10).is_ok()); + assert!(rx.recv().unwrap() == 10); +} + +#[test] +fn oneshot_single_thread_try_send_closed() { + let (tx, rx) = channel::(); + drop(rx); + assert!(tx.send(10).is_err()); +} + +#[test] +fn oneshot_single_thread_try_recv_open() { + let (tx, rx) = channel::(); + tx.send(10).unwrap(); + assert!(rx.recv() == Ok(10)); +} + +#[test] +fn oneshot_single_thread_try_recv_closed() { + let (tx, rx) = channel::(); + drop(tx); + assert!(rx.recv().is_err()); +} + +#[test] +fn oneshot_single_thread_peek_data() { + let (tx, rx) = channel::(); + assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); + tx.send(10).unwrap(); + assert_eq!(rx.try_recv(), Ok(10)); +} + +#[test] +fn oneshot_single_thread_peek_close() { + let (tx, rx) = channel::(); + drop(tx); + assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); + assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); +} + +#[test] +fn oneshot_single_thread_peek_open() { + let (_tx, rx) = channel::(); + assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); +} + +#[test] +fn oneshot_multi_task_recv_then_send() { + let (tx, rx) = channel::>(); + let _t = thread::spawn(move || { + assert!(*rx.recv().unwrap() == 10); + }); + + tx.send(Box::new(10)).unwrap(); +} + +#[test] +fn oneshot_multi_task_recv_then_close() { + let (tx, rx) = channel::>(); + let _t = thread::spawn(move || { + drop(tx); + }); + let res = thread::spawn(move || { + assert!(*rx.recv().unwrap() == 10); + }) + .join(); + assert!(res.is_err()); +} + +#[test] +fn oneshot_multi_thread_close_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel::(); + let _t = thread::spawn(move || { + drop(rx); + }); + drop(tx); + } +} + +#[test] +fn oneshot_multi_thread_send_close_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel::(); + let _t = thread::spawn(move || { + drop(rx); + }); + let _ = thread::spawn(move || { + tx.send(1).unwrap(); + }) + .join(); + } +} + +#[test] +fn oneshot_multi_thread_recv_close_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel::(); + thread::spawn(move || { + let res = thread::spawn(move || { + rx.recv().unwrap(); + }) + .join(); + assert!(res.is_err()); + }); + let _t = thread::spawn(move || { + thread::spawn(move || { + drop(tx); + }); + }); + } +} + +#[test] +fn oneshot_multi_thread_send_recv_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel::>(); + let _t = thread::spawn(move || { + tx.send(Box::new(10)).unwrap(); + }); + assert!(*rx.recv().unwrap() == 10); + } +} + +#[test] +fn stream_send_recv_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = channel(); + + send(tx, 0); + recv(rx, 0); + + fn send(tx: Sender>, i: i32) { + if i == 10 { + return; + } + + thread::spawn(move || { + tx.send(Box::new(i)).unwrap(); + send(tx, i + 1); + }); + } + + fn recv(rx: Receiver>, i: i32) { + if i == 10 { + return; + } + + thread::spawn(move || { + assert!(*rx.recv().unwrap() == i); + recv(rx, i + 1); + }); + } + } +} + +#[test] +fn oneshot_single_thread_recv_timeout() { + let (tx, rx) = channel(); + tx.send(()).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); + tx.send(()).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); +} + +#[test] +fn stress_recv_timeout_two_threads() { + let (tx, rx) = channel(); + let stress = stress_factor() + 100; + let timeout = Duration::from_millis(100); + + thread::spawn(move || { + for i in 0..stress { + if i % 2 == 0 { + thread::sleep(timeout * 2); + } + tx.send(1usize).unwrap(); + } + }); + + let mut recv_count = 0; + loop { + match rx.recv_timeout(timeout) { + Ok(n) => { + assert_eq!(n, 1usize); + recv_count += 1; + } + Err(RecvTimeoutError::Timeout) => continue, + Err(RecvTimeoutError::Disconnected) => break, + } + } + + assert_eq!(recv_count, stress); +} + +#[test] +fn recv_timeout_upgrade() { + let (tx, rx) = channel::<()>(); + let timeout = Duration::from_millis(1); + let _tx_clone = tx.clone(); + + let start = Instant::now(); + assert_eq!(rx.recv_timeout(timeout), Err(RecvTimeoutError::Timeout)); + assert!(Instant::now() >= start + timeout); +} + +#[test] +fn stress_recv_timeout_shared() { + let (tx, rx) = channel(); + let stress = stress_factor() + 100; + + for i in 0..stress { + let tx = tx.clone(); + thread::spawn(move || { + thread::sleep(Duration::from_millis(i as u64 * 10)); + tx.send(1usize).unwrap(); + }); + } + + drop(tx); + + let mut recv_count = 0; + loop { + match rx.recv_timeout(Duration::from_millis(10)) { + Ok(n) => { + assert_eq!(n, 1usize); + recv_count += 1; + } + Err(RecvTimeoutError::Timeout) => continue, + Err(RecvTimeoutError::Disconnected) => break, + } + } + + assert_eq!(recv_count, stress); +} + +#[test] +fn very_long_recv_timeout_wont_panic() { + let (tx, rx) = channel::<()>(); + let join_handle = thread::spawn(move || rx.recv_timeout(Duration::from_secs(u64::MAX))); + thread::sleep(Duration::from_secs(1)); + assert!(tx.send(()).is_ok()); + assert_eq!(join_handle.join().unwrap(), Ok(())); +} + +#[test] +fn recv_a_lot() { + let count = if cfg!(miri) { 1000 } else { 10000 }; + // Regression test that we don't run out of stack in scheduler context + let (tx, rx) = channel(); + for _ in 0..count { + tx.send(()).unwrap(); + } + for _ in 0..count { + rx.recv().unwrap(); + } +} + +#[test] +fn shared_recv_timeout() { + let (tx, rx) = channel(); + let total = 5; + for _ in 0..total { + let tx = tx.clone(); + thread::spawn(move || { + tx.send(()).unwrap(); + }); + } + + for _ in 0..total { + rx.recv().unwrap(); + } + + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); + tx.send(()).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); +} + +#[test] +fn shared_chan_stress() { + let (tx, rx) = channel(); + let total = stress_factor() + 100; + for _ in 0..total { + let tx = tx.clone(); + thread::spawn(move || { + tx.send(()).unwrap(); + }); + } + + for _ in 0..total { + rx.recv().unwrap(); + } +} + +#[test] +fn test_nested_recv_iter() { + let (tx, rx) = channel::(); + let (total_tx, total_rx) = channel::(); + + let _t = thread::spawn(move || { + let mut acc = 0; + for x in rx.iter() { + acc += x; + } + total_tx.send(acc).unwrap(); + }); + + tx.send(3).unwrap(); + tx.send(1).unwrap(); + tx.send(2).unwrap(); + drop(tx); + assert_eq!(total_rx.recv().unwrap(), 6); +} + +#[test] +fn test_recv_iter_break() { + let (tx, rx) = channel::(); + let (count_tx, count_rx) = channel(); + + let _t = thread::spawn(move || { + let mut count = 0; + for x in rx.iter() { + if count >= 3 { + break; + } else { + count += x; + } + } + count_tx.send(count).unwrap(); + }); + + tx.send(2).unwrap(); + tx.send(2).unwrap(); + tx.send(2).unwrap(); + let _ = tx.send(2); + drop(tx); + assert_eq!(count_rx.recv().unwrap(), 4); +} + +#[test] +fn test_recv_try_iter() { + let (request_tx, request_rx) = channel(); + let (response_tx, response_rx) = channel(); + + // Request `x`s until we have `6`. + let t = thread::spawn(move || { + let mut count = 0; + loop { + for x in response_rx.try_iter() { + count += x; + if count == 6 { + return count; + } + } + request_tx.send(()).unwrap(); + } + }); + + for _ in request_rx.iter() { + if response_tx.send(2).is_err() { + break; + } + } + + assert_eq!(t.join().unwrap(), 6); +} + +#[test] +fn test_recv_into_iter_owned() { + let mut iter = { + let (tx, rx) = channel::(); + tx.send(1).unwrap(); + tx.send(2).unwrap(); + + rx.into_iter() + }; + assert_eq!(iter.next().unwrap(), 1); + assert_eq!(iter.next().unwrap(), 2); + assert_eq!(iter.next().is_none(), true); +} + +#[test] +fn test_recv_into_iter_borrowed() { + let (tx, rx) = channel::(); + tx.send(1).unwrap(); + tx.send(2).unwrap(); + drop(tx); + let mut iter = (&rx).into_iter(); + assert_eq!(iter.next().unwrap(), 1); + assert_eq!(iter.next().unwrap(), 2); + assert_eq!(iter.next().is_none(), true); +} + +#[test] +fn try_recv_states() { + let (tx1, rx1) = channel::(); + let (tx2, rx2) = channel::<()>(); + let (tx3, rx3) = channel::<()>(); + let _t = thread::spawn(move || { + rx2.recv().unwrap(); + tx1.send(1).unwrap(); + tx3.send(()).unwrap(); + rx2.recv().unwrap(); + drop(tx1); + tx3.send(()).unwrap(); + }); + + assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); + tx2.send(()).unwrap(); + rx3.recv().unwrap(); + assert_eq!(rx1.try_recv(), Ok(1)); + assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); + tx2.send(()).unwrap(); + rx3.recv().unwrap(); + assert_eq!(rx1.try_recv(), Err(TryRecvError::Disconnected)); +} + +// This bug used to end up in a livelock inside of the Receiver destructor +// because the internal state of the Shared packet was corrupted +#[test] +fn destroy_upgraded_shared_port_when_sender_still_active() { + let (tx, rx) = channel(); + let (tx2, rx2) = channel(); + let _t = thread::spawn(move || { + rx.recv().unwrap(); // wait on a oneshot + drop(rx); // destroy a shared + tx2.send(()).unwrap(); + }); + // make sure the other thread has gone to sleep + for _ in 0..5000 { + thread::yield_now(); + } + + // upgrade to a shared chan and send a message + let t = tx.clone(); + drop(tx); + t.send(()).unwrap(); + + // wait for the child thread to exit before we exit + rx2.recv().unwrap(); +} + +#[test] +fn issue_32114() { + let (tx, _) = channel(); + let _ = tx.send(123); + assert_eq!(tx.send(123), Err(SendError(123))); +} + +#[test] +fn issue_39364() { + let (tx, rx) = channel::<()>(); + let t = thread::spawn(move || { + thread::sleep(Duration::from_millis(300)); + let _ = tx.clone(); + // Don't drop; hand back to caller. + tx + }); + + let _ = rx.recv_timeout(Duration::from_millis(500)); + let _tx = t.join().unwrap(); // delay dropping until end of test + let _ = rx.recv_timeout(Duration::from_millis(500)); +} diff --git a/library/std/tests/sync/mpsc_sync.rs b/library/std/tests/sync/mpsc_sync.rs new file mode 100644 index 0000000000000..a7f326d201b00 --- /dev/null +++ b/library/std/tests/sync/mpsc_sync.rs @@ -0,0 +1,670 @@ +use std::rc::Rc; +use std::sync::mpmc::SendTimeoutError; +use std::sync::mpsc::*; +use std::time::Duration; +use std::{env, thread}; + +pub fn stress_factor() -> usize { + match env::var("RUST_TEST_STRESS") { + Ok(val) => val.parse().unwrap(), + Err(..) => 1, + } +} + +#[test] +fn smoke() { + let (tx, rx) = sync_channel::(1); + tx.send(1).unwrap(); + assert_eq!(rx.recv().unwrap(), 1); +} + +#[test] +fn drop_full() { + let (tx, _rx) = sync_channel::>(1); + tx.send(Box::new(1)).unwrap(); +} + +#[test] +fn smoke_shared() { + let (tx, rx) = sync_channel::(1); + tx.send(1).unwrap(); + assert_eq!(rx.recv().unwrap(), 1); + let tx = tx.clone(); + tx.send(1).unwrap(); + assert_eq!(rx.recv().unwrap(), 1); +} + +#[test] +fn recv_timeout() { + let (tx, rx) = sync_channel::(1); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); + tx.send(1).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(1)); +} + +#[test] +fn send_timeout() { + let (tx, _rx) = sync_channel::(1); + assert_eq!(tx.send_timeout(1, Duration::from_millis(1)), Ok(())); + assert_eq!(tx.send_timeout(1, Duration::from_millis(1)), Err(SendTimeoutError::Timeout(1))); +} + +#[test] +fn smoke_threads() { + let (tx, rx) = sync_channel::(0); + let _t = thread::spawn(move || { + tx.send(1).unwrap(); + }); + assert_eq!(rx.recv().unwrap(), 1); +} + +#[test] +fn smoke_port_gone() { + let (tx, rx) = sync_channel::(0); + drop(rx); + assert!(tx.send(1).is_err()); +} + +#[test] +fn smoke_shared_port_gone2() { + let (tx, rx) = sync_channel::(0); + drop(rx); + let tx2 = tx.clone(); + drop(tx); + assert!(tx2.send(1).is_err()); +} + +#[test] +fn port_gone_concurrent() { + let (tx, rx) = sync_channel::(0); + let _t = thread::spawn(move || { + rx.recv().unwrap(); + }); + while tx.send(1).is_ok() {} +} + +#[test] +fn port_gone_concurrent_shared() { + let (tx, rx) = sync_channel::(0); + let tx2 = tx.clone(); + let _t = thread::spawn(move || { + rx.recv().unwrap(); + }); + while tx.send(1).is_ok() && tx2.send(1).is_ok() {} +} + +#[test] +fn smoke_chan_gone() { + let (tx, rx) = sync_channel::(0); + drop(tx); + assert!(rx.recv().is_err()); +} + +#[test] +fn smoke_chan_gone_shared() { + let (tx, rx) = sync_channel::<()>(0); + let tx2 = tx.clone(); + drop(tx); + drop(tx2); + assert!(rx.recv().is_err()); +} + +#[test] +fn chan_gone_concurrent() { + let (tx, rx) = sync_channel::(0); + thread::spawn(move || { + tx.send(1).unwrap(); + tx.send(1).unwrap(); + }); + while rx.recv().is_ok() {} +} + +#[test] +fn stress() { + let count = if cfg!(miri) { 100 } else { 10000 }; + let (tx, rx) = sync_channel::(0); + thread::spawn(move || { + for _ in 0..count { + tx.send(1).unwrap(); + } + }); + for _ in 0..count { + assert_eq!(rx.recv().unwrap(), 1); + } +} + +#[test] +fn stress_recv_timeout_two_threads() { + let count = if cfg!(miri) { 100 } else { 10000 }; + let (tx, rx) = sync_channel::(0); + + thread::spawn(move || { + for _ in 0..count { + tx.send(1).unwrap(); + } + }); + + let mut recv_count = 0; + loop { + match rx.recv_timeout(Duration::from_millis(1)) { + Ok(v) => { + assert_eq!(v, 1); + recv_count += 1; + } + Err(RecvTimeoutError::Timeout) => continue, + Err(RecvTimeoutError::Disconnected) => break, + } + } + + assert_eq!(recv_count, count); +} + +#[test] +fn stress_recv_timeout_shared() { + const AMT: u32 = if cfg!(miri) { 100 } else { 1000 }; + const NTHREADS: u32 = 8; + let (tx, rx) = sync_channel::(0); + let (dtx, drx) = sync_channel::<()>(0); + + thread::spawn(move || { + let mut recv_count = 0; + loop { + match rx.recv_timeout(Duration::from_millis(10)) { + Ok(v) => { + assert_eq!(v, 1); + recv_count += 1; + } + Err(RecvTimeoutError::Timeout) => continue, + Err(RecvTimeoutError::Disconnected) => break, + } + } + + assert_eq!(recv_count, AMT * NTHREADS); + assert!(rx.try_recv().is_err()); + + dtx.send(()).unwrap(); + }); + + for _ in 0..NTHREADS { + let tx = tx.clone(); + thread::spawn(move || { + for _ in 0..AMT { + tx.send(1).unwrap(); + } + }); + } + + drop(tx); + + drx.recv().unwrap(); +} + +#[test] +fn stress_shared() { + const AMT: u32 = if cfg!(miri) { 100 } else { 1000 }; + const NTHREADS: u32 = 8; + let (tx, rx) = sync_channel::(0); + let (dtx, drx) = sync_channel::<()>(0); + + thread::spawn(move || { + for _ in 0..AMT * NTHREADS { + assert_eq!(rx.recv().unwrap(), 1); + } + match rx.try_recv() { + Ok(..) => panic!(), + _ => {} + } + dtx.send(()).unwrap(); + }); + + for _ in 0..NTHREADS { + let tx = tx.clone(); + thread::spawn(move || { + for _ in 0..AMT { + tx.send(1).unwrap(); + } + }); + } + drop(tx); + drx.recv().unwrap(); +} + +#[test] +fn oneshot_single_thread_close_port_first() { + // Simple test of closing without sending + let (_tx, rx) = sync_channel::(0); + drop(rx); +} + +#[test] +fn oneshot_single_thread_close_chan_first() { + // Simple test of closing without sending + let (tx, _rx) = sync_channel::(0); + drop(tx); +} + +#[test] +fn oneshot_single_thread_send_port_close() { + // Testing that the sender cleans up the payload if receiver is closed + let (tx, rx) = sync_channel::>(0); + drop(rx); + assert!(tx.send(Box::new(0)).is_err()); +} + +#[test] +fn oneshot_single_thread_recv_chan_close() { + // Receiving on a closed chan will panic + let res = thread::spawn(move || { + let (tx, rx) = sync_channel::(0); + drop(tx); + rx.recv().unwrap(); + }) + .join(); + // What is our res? + assert!(res.is_err()); +} + +#[test] +fn oneshot_single_thread_send_then_recv() { + let (tx, rx) = sync_channel::>(1); + tx.send(Box::new(10)).unwrap(); + assert!(*rx.recv().unwrap() == 10); +} + +#[test] +fn oneshot_single_thread_try_send_open() { + let (tx, rx) = sync_channel::(1); + assert_eq!(tx.try_send(10), Ok(())); + assert!(rx.recv().unwrap() == 10); +} + +#[test] +fn oneshot_single_thread_try_send_closed() { + let (tx, rx) = sync_channel::(0); + drop(rx); + assert_eq!(tx.try_send(10), Err(TrySendError::Disconnected(10))); +} + +#[test] +fn oneshot_single_thread_try_send_closed2() { + let (tx, _rx) = sync_channel::(0); + assert_eq!(tx.try_send(10), Err(TrySendError::Full(10))); +} + +#[test] +fn oneshot_single_thread_try_recv_open() { + let (tx, rx) = sync_channel::(1); + tx.send(10).unwrap(); + assert!(rx.recv() == Ok(10)); +} + +#[test] +fn oneshot_single_thread_try_recv_closed() { + let (tx, rx) = sync_channel::(0); + drop(tx); + assert!(rx.recv().is_err()); +} + +#[test] +fn oneshot_single_thread_try_recv_closed_with_data() { + let (tx, rx) = sync_channel::(1); + tx.send(10).unwrap(); + drop(tx); + assert_eq!(rx.try_recv(), Ok(10)); + assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); +} + +#[test] +fn oneshot_single_thread_peek_data() { + let (tx, rx) = sync_channel::(1); + assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); + tx.send(10).unwrap(); + assert_eq!(rx.try_recv(), Ok(10)); +} + +#[test] +fn oneshot_single_thread_peek_close() { + let (tx, rx) = sync_channel::(0); + drop(tx); + assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); + assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); +} + +#[test] +fn oneshot_single_thread_peek_open() { + let (_tx, rx) = sync_channel::(0); + assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); +} + +#[test] +fn oneshot_multi_task_recv_then_send() { + let (tx, rx) = sync_channel::>(0); + let _t = thread::spawn(move || { + assert!(*rx.recv().unwrap() == 10); + }); + + tx.send(Box::new(10)).unwrap(); +} + +#[test] +fn oneshot_multi_task_recv_then_close() { + let (tx, rx) = sync_channel::>(0); + let _t = thread::spawn(move || { + drop(tx); + }); + let res = thread::spawn(move || { + assert!(*rx.recv().unwrap() == 10); + }) + .join(); + assert!(res.is_err()); +} + +#[test] +fn oneshot_multi_thread_close_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = sync_channel::(0); + let _t = thread::spawn(move || { + drop(rx); + }); + drop(tx); + } +} + +#[test] +fn oneshot_multi_thread_send_close_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = sync_channel::(0); + let _t = thread::spawn(move || { + drop(rx); + }); + let _ = thread::spawn(move || { + tx.send(1).unwrap(); + }) + .join(); + } +} + +#[test] +fn oneshot_multi_thread_recv_close_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = sync_channel::(0); + let _t = thread::spawn(move || { + let res = thread::spawn(move || { + rx.recv().unwrap(); + }) + .join(); + assert!(res.is_err()); + }); + let _t = thread::spawn(move || { + thread::spawn(move || { + drop(tx); + }); + }); + } +} + +#[test] +fn oneshot_multi_thread_send_recv_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = sync_channel::>(0); + let _t = thread::spawn(move || { + tx.send(Box::new(10)).unwrap(); + }); + assert!(*rx.recv().unwrap() == 10); + } +} + +#[test] +fn stream_send_recv_stress() { + for _ in 0..stress_factor() { + let (tx, rx) = sync_channel::>(0); + + send(tx, 0); + recv(rx, 0); + + fn send(tx: SyncSender>, i: i32) { + if i == 10 { + return; + } + + thread::spawn(move || { + tx.send(Box::new(i)).unwrap(); + send(tx, i + 1); + }); + } + + fn recv(rx: Receiver>, i: i32) { + if i == 10 { + return; + } + + thread::spawn(move || { + assert!(*rx.recv().unwrap() == i); + recv(rx, i + 1); + }); + } + } +} + +#[test] +fn recv_a_lot() { + let count = if cfg!(miri) { 1000 } else { 10000 }; + // Regression test that we don't run out of stack in scheduler context + let (tx, rx) = sync_channel(count); + for _ in 0..count { + tx.send(()).unwrap(); + } + for _ in 0..count { + rx.recv().unwrap(); + } +} + +#[test] +fn shared_chan_stress() { + let (tx, rx) = sync_channel(0); + let total = stress_factor() + 100; + for _ in 0..total { + let tx = tx.clone(); + thread::spawn(move || { + tx.send(()).unwrap(); + }); + } + + for _ in 0..total { + rx.recv().unwrap(); + } +} + +#[test] +fn test_nested_recv_iter() { + let (tx, rx) = sync_channel::(0); + let (total_tx, total_rx) = sync_channel::(0); + + let _t = thread::spawn(move || { + let mut acc = 0; + for x in rx.iter() { + acc += x; + } + total_tx.send(acc).unwrap(); + }); + + tx.send(3).unwrap(); + tx.send(1).unwrap(); + tx.send(2).unwrap(); + drop(tx); + assert_eq!(total_rx.recv().unwrap(), 6); +} + +#[test] +fn test_recv_iter_break() { + let (tx, rx) = sync_channel::(0); + let (count_tx, count_rx) = sync_channel(0); + + let _t = thread::spawn(move || { + let mut count = 0; + for x in rx.iter() { + if count >= 3 { + break; + } else { + count += x; + } + } + count_tx.send(count).unwrap(); + }); + + tx.send(2).unwrap(); + tx.send(2).unwrap(); + tx.send(2).unwrap(); + let _ = tx.try_send(2); + drop(tx); + assert_eq!(count_rx.recv().unwrap(), 4); +} + +#[test] +fn try_recv_states() { + let (tx1, rx1) = sync_channel::(1); + let (tx2, rx2) = sync_channel::<()>(1); + let (tx3, rx3) = sync_channel::<()>(1); + let _t = thread::spawn(move || { + rx2.recv().unwrap(); + tx1.send(1).unwrap(); + tx3.send(()).unwrap(); + rx2.recv().unwrap(); + drop(tx1); + tx3.send(()).unwrap(); + }); + + assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); + tx2.send(()).unwrap(); + rx3.recv().unwrap(); + assert_eq!(rx1.try_recv(), Ok(1)); + assert_eq!(rx1.try_recv(), Err(TryRecvError::Empty)); + tx2.send(()).unwrap(); + rx3.recv().unwrap(); + assert_eq!(rx1.try_recv(), Err(TryRecvError::Disconnected)); +} + +// This bug used to end up in a livelock inside of the Receiver destructor +// because the internal state of the Shared packet was corrupted +#[test] +fn destroy_upgraded_shared_port_when_sender_still_active() { + let (tx, rx) = sync_channel::<()>(0); + let (tx2, rx2) = sync_channel::<()>(0); + let _t = thread::spawn(move || { + rx.recv().unwrap(); // wait on a oneshot + drop(rx); // destroy a shared + tx2.send(()).unwrap(); + }); + // make sure the other thread has gone to sleep + for _ in 0..5000 { + thread::yield_now(); + } + + // upgrade to a shared chan and send a message + let t = tx.clone(); + drop(tx); + t.send(()).unwrap(); + + // wait for the child thread to exit before we exit + rx2.recv().unwrap(); +} + +#[test] +fn send1() { + let (tx, rx) = sync_channel::(0); + let _t = thread::spawn(move || { + rx.recv().unwrap(); + }); + assert_eq!(tx.send(1), Ok(())); +} + +#[test] +fn send2() { + let (tx, rx) = sync_channel::(0); + let _t = thread::spawn(move || { + drop(rx); + }); + assert!(tx.send(1).is_err()); +} + +#[test] +fn send3() { + let (tx, rx) = sync_channel::(1); + assert_eq!(tx.send(1), Ok(())); + let _t = thread::spawn(move || { + drop(rx); + }); + assert!(tx.send(1).is_err()); +} + +#[test] +fn send4() { + let (tx, rx) = sync_channel::(0); + let tx2 = tx.clone(); + let (done, donerx) = channel(); + let done2 = done.clone(); + let _t = thread::spawn(move || { + assert!(tx.send(1).is_err()); + done.send(()).unwrap(); + }); + let _t = thread::spawn(move || { + assert!(tx2.send(2).is_err()); + done2.send(()).unwrap(); + }); + drop(rx); + donerx.recv().unwrap(); + donerx.recv().unwrap(); +} + +#[test] +fn try_send1() { + let (tx, _rx) = sync_channel::(0); + assert_eq!(tx.try_send(1), Err(TrySendError::Full(1))); +} + +#[test] +fn try_send2() { + let (tx, _rx) = sync_channel::(1); + assert_eq!(tx.try_send(1), Ok(())); + assert_eq!(tx.try_send(1), Err(TrySendError::Full(1))); +} + +#[test] +fn try_send3() { + let (tx, rx) = sync_channel::(1); + assert_eq!(tx.try_send(1), Ok(())); + drop(rx); + assert_eq!(tx.try_send(1), Err(TrySendError::Disconnected(1))); +} + +#[test] +fn issue_15761() { + fn repro() { + let (tx1, rx1) = sync_channel::<()>(3); + let (tx2, rx2) = sync_channel::<()>(3); + + let _t = thread::spawn(move || { + rx1.recv().unwrap(); + tx2.try_send(()).unwrap(); + }); + + tx1.try_send(()).unwrap(); + rx2.recv().unwrap(); + } + + for _ in 0..100 { + repro() + } +} + +#[test] +fn drop_unreceived() { + let (tx, rx) = sync_channel::>(1); + let msg = Rc::new(()); + let weak = Rc::downgrade(&msg); + assert!(tx.send(msg).is_ok()); + drop(rx); + // Messages should be dropped immediately when the last receiver is destroyed. + assert!(weak.upgrade().is_none()); + drop(tx); +} diff --git a/library/std/tests/sync/mutex.rs b/library/std/tests/sync/mutex.rs new file mode 100644 index 0000000000000..74c627201073e --- /dev/null +++ b/library/std/tests/sync/mutex.rs @@ -0,0 +1,442 @@ +use std::fmt::Debug; +use std::ops::FnMut; +use std::panic::{self, AssertUnwindSafe}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::mpsc::channel; +use std::sync::{Arc, Condvar, MappedMutexGuard, Mutex, MutexGuard, TryLockError}; +use std::{hint, mem, thread}; + +struct Packet(Arc<(Mutex, Condvar)>); + +#[derive(Eq, PartialEq, Debug)] +struct NonCopy(i32); + +#[derive(Eq, PartialEq, Debug)] +struct NonCopyNeedsDrop(i32); + +impl Drop for NonCopyNeedsDrop { + fn drop(&mut self) { + hint::black_box(()); + } +} + +#[test] +fn test_needs_drop() { + assert!(!mem::needs_drop::()); + assert!(mem::needs_drop::()); +} + +#[derive(Clone, Eq, PartialEq, Debug)] +struct Cloneable(i32); + +#[test] +fn smoke() { + let m = Mutex::new(()); + drop(m.lock().unwrap()); + drop(m.lock().unwrap()); +} + +#[test] +fn lots_and_lots() { + const J: u32 = 1000; + const K: u32 = 3; + + let m = Arc::new(Mutex::new(0)); + + fn inc(m: &Mutex) { + for _ in 0..J { + *m.lock().unwrap() += 1; + } + } + + let (tx, rx) = channel(); + for _ in 0..K { + let tx2 = tx.clone(); + let m2 = m.clone(); + thread::spawn(move || { + inc(&m2); + tx2.send(()).unwrap(); + }); + let tx2 = tx.clone(); + let m2 = m.clone(); + thread::spawn(move || { + inc(&m2); + tx2.send(()).unwrap(); + }); + } + + drop(tx); + for _ in 0..2 * K { + rx.recv().unwrap(); + } + assert_eq!(*m.lock().unwrap(), J * K * 2); +} + +#[test] +fn try_lock() { + let m = Mutex::new(()); + *m.try_lock().unwrap() = (); +} + +fn new_poisoned_mutex(value: T) -> Mutex { + let mutex = Mutex::new(value); + + let catch_unwind_result = panic::catch_unwind(AssertUnwindSafe(|| { + let _guard = mutex.lock().unwrap(); + + panic!("test panic to poison mutex"); + })); + + assert!(catch_unwind_result.is_err()); + assert!(mutex.is_poisoned()); + + mutex +} + +#[test] +fn test_into_inner() { + let m = Mutex::new(NonCopy(10)); + assert_eq!(m.into_inner().unwrap(), NonCopy(10)); +} + +#[test] +fn test_into_inner_drop() { + struct Foo(Arc); + impl Drop for Foo { + fn drop(&mut self) { + self.0.fetch_add(1, Ordering::SeqCst); + } + } + let num_drops = Arc::new(AtomicUsize::new(0)); + let m = Mutex::new(Foo(num_drops.clone())); + assert_eq!(num_drops.load(Ordering::SeqCst), 0); + { + let _inner = m.into_inner().unwrap(); + assert_eq!(num_drops.load(Ordering::SeqCst), 0); + } + assert_eq!(num_drops.load(Ordering::SeqCst), 1); +} + +#[test] +fn test_into_inner_poison() { + let m = new_poisoned_mutex(NonCopy(10)); + + match m.into_inner() { + Err(e) => assert_eq!(e.into_inner(), NonCopy(10)), + Ok(x) => panic!("into_inner of poisoned Mutex is Ok: {x:?}"), + } +} + +#[test] +fn test_get_cloned() { + let m = Mutex::new(Cloneable(10)); + + assert_eq!(m.get_cloned().unwrap(), Cloneable(10)); +} + +#[test] +fn test_get_cloned_poison() { + let m = new_poisoned_mutex(Cloneable(10)); + + match m.get_cloned() { + Err(e) => assert_eq!(e.into_inner(), ()), + Ok(x) => panic!("get of poisoned Mutex is Ok: {x:?}"), + } +} + +#[test] +fn test_get_mut() { + let mut m = Mutex::new(NonCopy(10)); + *m.get_mut().unwrap() = NonCopy(20); + assert_eq!(m.into_inner().unwrap(), NonCopy(20)); +} + +#[test] +fn test_get_mut_poison() { + let mut m = new_poisoned_mutex(NonCopy(10)); + + match m.get_mut() { + Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)), + Ok(x) => panic!("get_mut of poisoned Mutex is Ok: {x:?}"), + } +} + +#[test] +fn test_set() { + fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = Mutex::new(init()); + + assert_eq!(*m.lock().unwrap(), init()); + m.set(value()).unwrap(); + assert_eq!(*m.lock().unwrap(), value()); + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_set_poison() { + fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = new_poisoned_mutex(init()); + + match m.set(value()) { + Err(e) => { + assert_eq!(e.into_inner(), value()); + assert_eq!(m.into_inner().unwrap_err().into_inner(), init()); + } + Ok(x) => panic!("set of poisoned Mutex is Ok: {x:?}"), + } + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_replace() { + fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = Mutex::new(init()); + + assert_eq!(*m.lock().unwrap(), init()); + assert_eq!(m.replace(value()).unwrap(), init()); + assert_eq!(*m.lock().unwrap(), value()); + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_replace_poison() { + fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = new_poisoned_mutex(init()); + + match m.replace(value()) { + Err(e) => { + assert_eq!(e.into_inner(), value()); + assert_eq!(m.into_inner().unwrap_err().into_inner(), init()); + } + Ok(x) => panic!("replace of poisoned Mutex is Ok: {x:?}"), + } + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_mutex_arc_condvar() { + let packet = Packet(Arc::new((Mutex::new(false), Condvar::new()))); + let packet2 = Packet(packet.0.clone()); + let (tx, rx) = channel(); + let _t = thread::spawn(move || { + // wait until parent gets in + rx.recv().unwrap(); + let &(ref lock, ref cvar) = &*packet2.0; + let mut lock = lock.lock().unwrap(); + *lock = true; + cvar.notify_one(); + }); + + let &(ref lock, ref cvar) = &*packet.0; + let mut lock = lock.lock().unwrap(); + tx.send(()).unwrap(); + assert!(!*lock); + while !*lock { + lock = cvar.wait(lock).unwrap(); + } +} + +#[test] +fn test_arc_condvar_poison() { + let packet = Packet(Arc::new((Mutex::new(1), Condvar::new()))); + let packet2 = Packet(packet.0.clone()); + let (tx, rx) = channel(); + + let _t = thread::spawn(move || -> () { + rx.recv().unwrap(); + let &(ref lock, ref cvar) = &*packet2.0; + let _g = lock.lock().unwrap(); + cvar.notify_one(); + // Parent should fail when it wakes up. + panic!(); + }); + + let &(ref lock, ref cvar) = &*packet.0; + let mut lock = lock.lock().unwrap(); + tx.send(()).unwrap(); + while *lock == 1 { + match cvar.wait(lock) { + Ok(l) => { + lock = l; + assert_eq!(*lock, 1); + } + Err(..) => break, + } + } +} + +#[test] +fn test_mutex_arc_poison() { + let arc = Arc::new(Mutex::new(1)); + assert!(!arc.is_poisoned()); + let arc2 = arc.clone(); + let _ = thread::spawn(move || { + let lock = arc2.lock().unwrap(); + assert_eq!(*lock, 2); // deliberate assertion failure to poison the mutex + }) + .join(); + assert!(arc.lock().is_err()); + assert!(arc.is_poisoned()); +} + +#[test] +fn test_mutex_arc_poison_mapped() { + let arc = Arc::new(Mutex::new(1)); + assert!(!arc.is_poisoned()); + let arc2 = arc.clone(); + let _ = thread::spawn(move || { + let lock = arc2.lock().unwrap(); + let lock = MutexGuard::map(lock, |val| val); + assert_eq!(*lock, 2); // deliberate assertion failure to poison the mutex + }) + .join(); + assert!(arc.lock().is_err()); + assert!(arc.is_poisoned()); +} + +#[test] +fn test_mutex_arc_nested() { + // Tests nested mutexes and access + // to underlying data. + let arc = Arc::new(Mutex::new(1)); + let arc2 = Arc::new(Mutex::new(arc)); + let (tx, rx) = channel(); + let _t = thread::spawn(move || { + let lock = arc2.lock().unwrap(); + let lock2 = lock.lock().unwrap(); + assert_eq!(*lock2, 1); + tx.send(()).unwrap(); + }); + rx.recv().unwrap(); +} + +#[test] +fn test_mutex_arc_access_in_unwind() { + let arc = Arc::new(Mutex::new(1)); + let arc2 = arc.clone(); + let _ = thread::spawn(move || -> () { + struct Unwinder { + i: Arc>, + } + impl Drop for Unwinder { + fn drop(&mut self) { + *self.i.lock().unwrap() += 1; + } + } + let _u = Unwinder { i: arc2 }; + panic!(); + }) + .join(); + let lock = arc.lock().unwrap(); + assert_eq!(*lock, 2); +} + +#[test] +fn test_mutex_unsized() { + let mutex: &Mutex<[i32]> = &Mutex::new([1, 2, 3]); + { + let b = &mut *mutex.lock().unwrap(); + b[0] = 4; + b[2] = 5; + } + let comp: &[i32] = &[4, 2, 5]; + assert_eq!(&*mutex.lock().unwrap(), comp); +} + +#[test] +fn test_mapping_mapped_guard() { + let arr = [0; 4]; + let mut lock = Mutex::new(arr); + let guard = lock.lock().unwrap(); + let guard = MutexGuard::map(guard, |arr| &mut arr[..2]); + let mut guard = MappedMutexGuard::map(guard, |slice| &mut slice[1..]); + assert_eq!(guard.len(), 1); + guard[0] = 42; + drop(guard); + assert_eq!(*lock.get_mut().unwrap(), [0, 42, 0, 0]); +} + +#[test] +fn panic_while_mapping_unlocked_poison() { + let lock = Mutex::new(()); + + let _ = panic::catch_unwind(|| { + let guard = lock.lock().unwrap(); + let _guard = MutexGuard::map::<(), _>(guard, |_| panic!()); + }); + + match lock.try_lock() { + Ok(_) => panic!("panicking in a MutexGuard::map closure should poison the Mutex"), + Err(TryLockError::WouldBlock) => { + panic!("panicking in a MutexGuard::map closure should unlock the mutex") + } + Err(TryLockError::Poisoned(_)) => {} + } + + let _ = panic::catch_unwind(|| { + let guard = lock.lock().unwrap(); + let _guard = MutexGuard::try_map::<(), _>(guard, |_| panic!()); + }); + + match lock.try_lock() { + Ok(_) => panic!("panicking in a MutexGuard::try_map closure should poison the Mutex"), + Err(TryLockError::WouldBlock) => { + panic!("panicking in a MutexGuard::try_map closure should unlock the mutex") + } + Err(TryLockError::Poisoned(_)) => {} + } + + let _ = panic::catch_unwind(|| { + let guard = lock.lock().unwrap(); + let guard = MutexGuard::map::<(), _>(guard, |val| val); + let _guard = MappedMutexGuard::map::<(), _>(guard, |_| panic!()); + }); + + match lock.try_lock() { + Ok(_) => panic!("panicking in a MappedMutexGuard::map closure should poison the Mutex"), + Err(TryLockError::WouldBlock) => { + panic!("panicking in a MappedMutexGuard::map closure should unlock the mutex") + } + Err(TryLockError::Poisoned(_)) => {} + } + + let _ = panic::catch_unwind(|| { + let guard = lock.lock().unwrap(); + let guard = MutexGuard::map::<(), _>(guard, |val| val); + let _guard = MappedMutexGuard::try_map::<(), _>(guard, |_| panic!()); + }); + + match lock.try_lock() { + Ok(_) => panic!("panicking in a MappedMutexGuard::try_map closure should poison the Mutex"), + Err(TryLockError::WouldBlock) => { + panic!("panicking in a MappedMutexGuard::try_map closure should unlock the mutex") + } + Err(TryLockError::Poisoned(_)) => {} + } + + drop(lock); +} diff --git a/library/std/tests/sync/once.rs b/library/std/tests/sync/once.rs new file mode 100644 index 0000000000000..a3ffc73fe06b9 --- /dev/null +++ b/library/std/tests/sync/once.rs @@ -0,0 +1,162 @@ +use std::sync::Once; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering::Relaxed; +use std::sync::mpsc::channel; +use std::time::Duration; +use std::{panic, thread}; + +#[test] +fn smoke_once() { + static O: Once = Once::new(); + let mut a = 0; + O.call_once(|| a += 1); + assert_eq!(a, 1); + O.call_once(|| a += 1); + assert_eq!(a, 1); +} + +#[test] +fn stampede_once() { + static O: Once = Once::new(); + static mut RUN: bool = false; + + let (tx, rx) = channel(); + for _ in 0..10 { + let tx = tx.clone(); + thread::spawn(move || { + for _ in 0..4 { + thread::yield_now() + } + unsafe { + O.call_once(|| { + assert!(!RUN); + RUN = true; + }); + assert!(RUN); + } + tx.send(()).unwrap(); + }); + } + + unsafe { + O.call_once(|| { + assert!(!RUN); + RUN = true; + }); + assert!(RUN); + } + + for _ in 0..10 { + rx.recv().unwrap(); + } +} + +#[test] +fn poison_bad() { + static O: Once = Once::new(); + + // poison the once + let t = panic::catch_unwind(|| { + O.call_once(|| panic!()); + }); + assert!(t.is_err()); + + // poisoning propagates + let t = panic::catch_unwind(|| { + O.call_once(|| {}); + }); + assert!(t.is_err()); + + // we can subvert poisoning, however + let mut called = false; + O.call_once_force(|p| { + called = true; + assert!(p.is_poisoned()) + }); + assert!(called); + + // once any success happens, we stop propagating the poison + O.call_once(|| {}); +} + +#[test] +fn wait_for_force_to_finish() { + static O: Once = Once::new(); + + // poison the once + let t = panic::catch_unwind(|| { + O.call_once(|| panic!()); + }); + assert!(t.is_err()); + + // make sure someone's waiting inside the once via a force + let (tx1, rx1) = channel(); + let (tx2, rx2) = channel(); + let t1 = thread::spawn(move || { + O.call_once_force(|p| { + assert!(p.is_poisoned()); + tx1.send(()).unwrap(); + rx2.recv().unwrap(); + }); + }); + + rx1.recv().unwrap(); + + // put another waiter on the once + let t2 = thread::spawn(|| { + let mut called = false; + O.call_once(|| { + called = true; + }); + assert!(!called); + }); + + tx2.send(()).unwrap(); + + assert!(t1.join().is_ok()); + assert!(t2.join().is_ok()); +} + +#[test] +fn wait() { + for _ in 0..50 { + let val = AtomicBool::new(false); + let once = Once::new(); + + thread::scope(|s| { + for _ in 0..4 { + s.spawn(|| { + once.wait(); + assert!(val.load(Relaxed)); + }); + } + + once.call_once(|| val.store(true, Relaxed)); + }); + } +} + +#[test] +fn wait_on_poisoned() { + let once = Once::new(); + + panic::catch_unwind(|| once.call_once(|| panic!())).unwrap_err(); + panic::catch_unwind(|| once.wait()).unwrap_err(); +} + +#[test] +fn wait_force_on_poisoned() { + let once = Once::new(); + + thread::scope(|s| { + panic::catch_unwind(|| once.call_once(|| panic!())).unwrap_err(); + + s.spawn(|| { + thread::sleep(Duration::from_millis(100)); + + once.call_once_force(|_| {}); + }); + + once.wait_force(); + }) +} diff --git a/library/std/tests/sync/once_lock.rs b/library/std/tests/sync/once_lock.rs new file mode 100644 index 0000000000000..ac9aaa8892eff --- /dev/null +++ b/library/std/tests/sync/once_lock.rs @@ -0,0 +1,190 @@ +use std::sync::OnceLock; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::SeqCst; +use std::sync::mpsc::channel; +use std::{panic, thread}; + +fn spawn_and_wait(f: impl FnOnce() -> R + Send + 'static) -> R { + thread::spawn(f).join().unwrap() +} + +#[test] +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +fn sync_once_cell() { + static ONCE_CELL: OnceLock = OnceLock::new(); + + assert!(ONCE_CELL.get().is_none()); + + spawn_and_wait(|| { + ONCE_CELL.get_or_init(|| 92); + assert_eq!(ONCE_CELL.get(), Some(&92)); + }); + + ONCE_CELL.get_or_init(|| panic!("Kaboom!")); + assert_eq!(ONCE_CELL.get(), Some(&92)); +} + +#[test] +fn sync_once_cell_get_mut() { + let mut c = OnceLock::new(); + assert!(c.get_mut().is_none()); + c.set(90).unwrap(); + *c.get_mut().unwrap() += 2; + assert_eq!(c.get_mut(), Some(&mut 92)); +} + +#[test] +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +fn sync_once_cell_drop() { + static DROP_CNT: AtomicUsize = AtomicUsize::new(0); + struct Dropper; + impl Drop for Dropper { + fn drop(&mut self) { + DROP_CNT.fetch_add(1, SeqCst); + } + } + + let x = OnceLock::new(); + spawn_and_wait(move || { + x.get_or_init(|| Dropper); + assert_eq!(DROP_CNT.load(SeqCst), 0); + drop(x); + }); + + assert_eq!(DROP_CNT.load(SeqCst), 1); +} + +#[test] +fn sync_once_cell_drop_empty() { + let x = OnceLock::::new(); + drop(x); +} + +#[test] +fn clone() { + let s = OnceLock::new(); + let c = s.clone(); + assert!(c.get().is_none()); + + s.set("hello".to_string()).unwrap(); + let c = s.clone(); + assert_eq!(c.get().map(String::as_str), Some("hello")); +} + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn get_or_try_init() { + let cell: OnceLock = OnceLock::new(); + assert!(cell.get().is_none()); + + let res = panic::catch_unwind(|| cell.get_or_try_init(|| -> Result<_, ()> { panic!() })); + assert!(res.is_err()); + assert!(cell.get().is_none()); + + assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); + + assert_eq!(cell.get_or_try_init(|| Ok::<_, ()>("hello".to_string())), Ok(&"hello".to_string())); + assert_eq!(cell.get(), Some(&"hello".to_string())); +} + +#[test] +fn from_impl() { + assert_eq!(OnceLock::from("value").get(), Some(&"value")); + assert_ne!(OnceLock::from("foo").get(), Some(&"bar")); +} + +#[test] +fn partialeq_impl() { + assert!(OnceLock::from("value") == OnceLock::from("value")); + assert!(OnceLock::from("foo") != OnceLock::from("bar")); + + assert!(OnceLock::::new() == OnceLock::new()); + assert!(OnceLock::::new() != OnceLock::from("value".to_owned())); +} + +#[test] +fn into_inner() { + let cell: OnceLock = OnceLock::new(); + assert_eq!(cell.into_inner(), None); + let cell = OnceLock::new(); + cell.set("hello".to_string()).unwrap(); + assert_eq!(cell.into_inner(), Some("hello".to_string())); +} + +#[test] +fn is_sync_send() { + fn assert_traits() {} + assert_traits::>(); +} + +#[test] +fn eval_once_macro() { + macro_rules! eval_once { + (|| -> $ty:ty { + $($body:tt)* + }) => {{ + static ONCE_CELL: OnceLock<$ty> = OnceLock::new(); + fn init() -> $ty { + $($body)* + } + ONCE_CELL.get_or_init(init) + }}; + } + + let fib: &'static Vec = eval_once! { + || -> Vec { + let mut res = vec![1, 1]; + for i in 0..10 { + let next = res[i] + res[i + 1]; + res.push(next); + } + res + } + }; + assert_eq!(fib[5], 8) +} + +#[test] +#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +fn sync_once_cell_does_not_leak_partially_constructed_boxes() { + static ONCE_CELL: OnceLock = OnceLock::new(); + + let n_readers = 10; + let n_writers = 3; + const MSG: &str = "Hello, World"; + + let (tx, rx) = channel(); + + for _ in 0..n_readers { + let tx = tx.clone(); + thread::spawn(move || { + loop { + if let Some(msg) = ONCE_CELL.get() { + tx.send(msg).unwrap(); + break; + } + #[cfg(target_env = "sgx")] + std::thread::yield_now(); + } + }); + } + for _ in 0..n_writers { + thread::spawn(move || { + let _ = ONCE_CELL.set(MSG.to_owned()); + }); + } + + for _ in 0..n_readers { + let msg = rx.recv().unwrap(); + assert_eq!(msg, MSG); + } +} + +#[test] +fn dropck() { + let cell = OnceLock::new(); + { + let s = String::new(); + cell.set(&s).unwrap(); + } +} diff --git a/library/std/tests/sync/reentrant_lock.rs b/library/std/tests/sync/reentrant_lock.rs new file mode 100644 index 0000000000000..2b7b87e36234a --- /dev/null +++ b/library/std/tests/sync/reentrant_lock.rs @@ -0,0 +1,52 @@ +use std::cell::RefCell; +use std::sync::{Arc, ReentrantLock}; +use std::thread; + +#[test] +fn smoke() { + let l = ReentrantLock::new(()); + { + let a = l.lock(); + { + let b = l.lock(); + { + let c = l.lock(); + assert_eq!(*c, ()); + } + assert_eq!(*b, ()); + } + assert_eq!(*a, ()); + } +} + +#[test] +fn is_mutex() { + let l = Arc::new(ReentrantLock::new(RefCell::new(0))); + let l2 = l.clone(); + let lock = l.lock(); + let child = thread::spawn(move || { + let lock = l2.lock(); + assert_eq!(*lock.borrow(), 4950); + }); + for i in 0..100 { + let lock = l.lock(); + *lock.borrow_mut() += i; + } + drop(lock); + child.join().unwrap(); +} + +#[test] +fn trylock_works() { + let l = Arc::new(ReentrantLock::new(())); + let l2 = l.clone(); + let _lock = l.try_lock(); + let _lock2 = l.try_lock(); + thread::spawn(move || { + let lock = l2.try_lock(); + assert!(lock.is_none()); + }) + .join() + .unwrap(); + let _lock3 = l.try_lock(); +} diff --git a/library/std/tests/sync/rwlock.rs b/library/std/tests/sync/rwlock.rs new file mode 100644 index 0000000000000..49f260648c6ac --- /dev/null +++ b/library/std/tests/sync/rwlock.rs @@ -0,0 +1,729 @@ +use std::fmt::Debug; +use std::ops::FnMut; +use std::panic::{self, AssertUnwindSafe}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::mpsc::channel; +use std::sync::{ + Arc, MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard, RwLockWriteGuard, + TryLockError, +}; +use std::{hint, mem, thread}; + +use rand::Rng; + +#[derive(Eq, PartialEq, Debug)] +struct NonCopy(i32); + +#[derive(Eq, PartialEq, Debug)] +struct NonCopyNeedsDrop(i32); + +impl Drop for NonCopyNeedsDrop { + fn drop(&mut self) { + hint::black_box(()); + } +} + +#[test] +fn test_needs_drop() { + assert!(!mem::needs_drop::()); + assert!(mem::needs_drop::()); +} + +#[derive(Clone, Eq, PartialEq, Debug)] +struct Cloneable(i32); + +#[test] +fn smoke() { + let l = RwLock::new(()); + drop(l.read().unwrap()); + drop(l.write().unwrap()); + drop((l.read().unwrap(), l.read().unwrap())); + drop(l.write().unwrap()); +} + +#[test] +// FIXME: On macOS we use a provenance-incorrect implementation and Miri +// catches that issue with a chance of around 1/1000. +// See for details. +#[cfg_attr(all(miri, target_os = "macos"), ignore)] +fn frob() { + const N: u32 = 10; + const M: usize = if cfg!(miri) { 100 } else { 1000 }; + + let r = Arc::new(RwLock::new(())); + + let (tx, rx) = channel::<()>(); + for _ in 0..N { + let tx = tx.clone(); + let r = r.clone(); + thread::spawn(move || { + let mut rng = crate::common::test_rng(); + for _ in 0..M { + if rng.random_bool(1.0 / (N as f64)) { + drop(r.write().unwrap()); + } else { + drop(r.read().unwrap()); + } + } + drop(tx); + }); + } + drop(tx); + let _ = rx.recv(); +} + +#[test] +fn test_rw_arc_poison_wr() { + let arc = Arc::new(RwLock::new(1)); + let arc2 = arc.clone(); + let _: Result<(), _> = thread::spawn(move || { + let _lock = arc2.write().unwrap(); + panic!(); + }) + .join(); + assert!(arc.read().is_err()); +} + +#[test] +fn test_rw_arc_poison_mapped_w_r() { + let arc = Arc::new(RwLock::new(1)); + let arc2 = arc.clone(); + let _: Result<(), _> = thread::spawn(move || { + let lock = arc2.write().unwrap(); + let _lock = RwLockWriteGuard::map(lock, |val| val); + panic!(); + }) + .join(); + assert!(arc.read().is_err()); +} + +#[test] +fn test_rw_arc_poison_ww() { + let arc = Arc::new(RwLock::new(1)); + assert!(!arc.is_poisoned()); + let arc2 = arc.clone(); + let _: Result<(), _> = thread::spawn(move || { + let _lock = arc2.write().unwrap(); + panic!(); + }) + .join(); + assert!(arc.write().is_err()); + assert!(arc.is_poisoned()); +} + +#[test] +fn test_rw_arc_poison_mapped_w_w() { + let arc = Arc::new(RwLock::new(1)); + let arc2 = arc.clone(); + let _: Result<(), _> = thread::spawn(move || { + let lock = arc2.write().unwrap(); + let _lock = RwLockWriteGuard::map(lock, |val| val); + panic!(); + }) + .join(); + assert!(arc.write().is_err()); + assert!(arc.is_poisoned()); +} + +#[test] +fn test_rw_arc_no_poison_rr() { + let arc = Arc::new(RwLock::new(1)); + let arc2 = arc.clone(); + let _: Result<(), _> = thread::spawn(move || { + let _lock = arc2.read().unwrap(); + panic!(); + }) + .join(); + let lock = arc.read().unwrap(); + assert_eq!(*lock, 1); +} + +#[test] +fn test_rw_arc_no_poison_mapped_r_r() { + let arc = Arc::new(RwLock::new(1)); + let arc2 = arc.clone(); + let _: Result<(), _> = thread::spawn(move || { + let lock = arc2.read().unwrap(); + let _lock = RwLockReadGuard::map(lock, |val| val); + panic!(); + }) + .join(); + let lock = arc.read().unwrap(); + assert_eq!(*lock, 1); +} + +#[test] +fn test_rw_arc_no_poison_rw() { + let arc = Arc::new(RwLock::new(1)); + let arc2 = arc.clone(); + let _: Result<(), _> = thread::spawn(move || { + let _lock = arc2.read().unwrap(); + panic!() + }) + .join(); + let lock = arc.write().unwrap(); + assert_eq!(*lock, 1); +} + +#[test] +fn test_rw_arc_no_poison_mapped_r_w() { + let arc = Arc::new(RwLock::new(1)); + let arc2 = arc.clone(); + let _: Result<(), _> = thread::spawn(move || { + let lock = arc2.read().unwrap(); + let _lock = RwLockReadGuard::map(lock, |val| val); + panic!(); + }) + .join(); + let lock = arc.write().unwrap(); + assert_eq!(*lock, 1); +} + +#[test] +fn test_rw_arc() { + let arc = Arc::new(RwLock::new(0)); + let arc2 = arc.clone(); + let (tx, rx) = channel(); + + thread::spawn(move || { + let mut lock = arc2.write().unwrap(); + for _ in 0..10 { + let tmp = *lock; + *lock = -1; + thread::yield_now(); + *lock = tmp + 1; + } + tx.send(()).unwrap(); + }); + + // Readers try to catch the writer in the act + let mut children = Vec::new(); + for _ in 0..5 { + let arc3 = arc.clone(); + children.push(thread::spawn(move || { + let lock = arc3.read().unwrap(); + assert!(*lock >= 0); + })); + } + + // Wait for children to pass their asserts + for r in children { + assert!(r.join().is_ok()); + } + + // Wait for writer to finish + rx.recv().unwrap(); + let lock = arc.read().unwrap(); + assert_eq!(*lock, 10); +} + +#[test] +fn test_rw_arc_access_in_unwind() { + let arc = Arc::new(RwLock::new(1)); + let arc2 = arc.clone(); + let _ = thread::spawn(move || -> () { + struct Unwinder { + i: Arc>, + } + impl Drop for Unwinder { + fn drop(&mut self) { + let mut lock = self.i.write().unwrap(); + *lock += 1; + } + } + let _u = Unwinder { i: arc2 }; + panic!(); + }) + .join(); + let lock = arc.read().unwrap(); + assert_eq!(*lock, 2); +} + +#[test] +fn test_rwlock_unsized() { + let rw: &RwLock<[i32]> = &RwLock::new([1, 2, 3]); + { + let b = &mut *rw.write().unwrap(); + b[0] = 4; + b[2] = 5; + } + let comp: &[i32] = &[4, 2, 5]; + assert_eq!(&*rw.read().unwrap(), comp); +} + +#[test] +fn test_rwlock_try_write() { + let lock = RwLock::new(0isize); + let read_guard = lock.read().unwrap(); + + let write_result = lock.try_write(); + match write_result { + Err(TryLockError::WouldBlock) => (), + Ok(_) => assert!(false, "try_write should not succeed while read_guard is in scope"), + Err(_) => assert!(false, "unexpected error"), + } + + drop(read_guard); + let mapped_read_guard = RwLockReadGuard::map(lock.read().unwrap(), |_| &()); + + let write_result = lock.try_write(); + match write_result { + Err(TryLockError::WouldBlock) => (), + Ok(_) => assert!(false, "try_write should not succeed while mapped_read_guard is in scope"), + Err(_) => assert!(false, "unexpected error"), + } + + drop(mapped_read_guard); +} + +fn new_poisoned_rwlock(value: T) -> RwLock { + let lock = RwLock::new(value); + + let catch_unwind_result = panic::catch_unwind(AssertUnwindSafe(|| { + let _guard = lock.write().unwrap(); + + panic!("test panic to poison RwLock"); + })); + + assert!(catch_unwind_result.is_err()); + assert!(lock.is_poisoned()); + + lock +} + +#[test] +fn test_into_inner() { + let m = RwLock::new(NonCopy(10)); + assert_eq!(m.into_inner().unwrap(), NonCopy(10)); +} + +#[test] +fn test_into_inner_drop() { + struct Foo(Arc); + impl Drop for Foo { + fn drop(&mut self) { + self.0.fetch_add(1, Ordering::SeqCst); + } + } + let num_drops = Arc::new(AtomicUsize::new(0)); + let m = RwLock::new(Foo(num_drops.clone())); + assert_eq!(num_drops.load(Ordering::SeqCst), 0); + { + let _inner = m.into_inner().unwrap(); + assert_eq!(num_drops.load(Ordering::SeqCst), 0); + } + assert_eq!(num_drops.load(Ordering::SeqCst), 1); +} + +#[test] +fn test_into_inner_poison() { + let m = new_poisoned_rwlock(NonCopy(10)); + + match m.into_inner() { + Err(e) => assert_eq!(e.into_inner(), NonCopy(10)), + Ok(x) => panic!("into_inner of poisoned RwLock is Ok: {x:?}"), + } +} + +#[test] +fn test_get_cloned() { + let m = RwLock::new(Cloneable(10)); + + assert_eq!(m.get_cloned().unwrap(), Cloneable(10)); +} + +#[test] +fn test_get_cloned_poison() { + let m = new_poisoned_rwlock(Cloneable(10)); + + match m.get_cloned() { + Err(e) => assert_eq!(e.into_inner(), ()), + Ok(x) => panic!("get of poisoned RwLock is Ok: {x:?}"), + } +} + +#[test] +fn test_get_mut() { + let mut m = RwLock::new(NonCopy(10)); + *m.get_mut().unwrap() = NonCopy(20); + assert_eq!(m.into_inner().unwrap(), NonCopy(20)); +} + +#[test] +fn test_get_mut_poison() { + let mut m = new_poisoned_rwlock(NonCopy(10)); + + match m.get_mut() { + Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)), + Ok(x) => panic!("get_mut of poisoned RwLock is Ok: {x:?}"), + } +} + +#[test] +fn test_set() { + fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = RwLock::new(init()); + + assert_eq!(*m.read().unwrap(), init()); + m.set(value()).unwrap(); + assert_eq!(*m.read().unwrap(), value()); + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_set_poison() { + fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = new_poisoned_rwlock(init()); + + match m.set(value()) { + Err(e) => { + assert_eq!(e.into_inner(), value()); + assert_eq!(m.into_inner().unwrap_err().into_inner(), init()); + } + Ok(x) => panic!("set of poisoned RwLock is Ok: {x:?}"), + } + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_replace() { + fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = RwLock::new(init()); + + assert_eq!(*m.read().unwrap(), init()); + assert_eq!(m.replace(value()).unwrap(), init()); + assert_eq!(*m.read().unwrap(), value()); + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_replace_poison() { + fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = new_poisoned_rwlock(init()); + + match m.replace(value()) { + Err(e) => { + assert_eq!(e.into_inner(), value()); + assert_eq!(m.into_inner().unwrap_err().into_inner(), init()); + } + Ok(x) => panic!("replace of poisoned RwLock is Ok: {x:?}"), + } + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_read_guard_covariance() { + fn do_stuff<'a>(_: RwLockReadGuard<'_, &'a i32>, _: &'a i32) {} + let j: i32 = 5; + let lock = RwLock::new(&j); + { + let i = 6; + do_stuff(lock.read().unwrap(), &i); + } + drop(lock); +} + +#[test] +fn test_mapped_read_guard_covariance() { + fn do_stuff<'a>(_: MappedRwLockReadGuard<'_, &'a i32>, _: &'a i32) {} + let j: i32 = 5; + let lock = RwLock::new((&j, &j)); + { + let i = 6; + let guard = lock.read().unwrap(); + let guard = RwLockReadGuard::map(guard, |(val, _val)| val); + do_stuff(guard, &i); + } + drop(lock); +} + +#[test] +fn test_mapping_mapped_guard() { + let arr = [0; 4]; + let mut lock = RwLock::new(arr); + let guard = lock.write().unwrap(); + let guard = RwLockWriteGuard::map(guard, |arr| &mut arr[..2]); + let mut guard = MappedRwLockWriteGuard::map(guard, |slice| &mut slice[1..]); + assert_eq!(guard.len(), 1); + guard[0] = 42; + drop(guard); + assert_eq!(*lock.get_mut().unwrap(), [0, 42, 0, 0]); + + let guard = lock.read().unwrap(); + let guard = RwLockReadGuard::map(guard, |arr| &arr[..2]); + let guard = MappedRwLockReadGuard::map(guard, |slice| &slice[1..]); + assert_eq!(*guard, [42]); + drop(guard); + assert_eq!(*lock.get_mut().unwrap(), [0, 42, 0, 0]); +} + +#[test] +fn panic_while_mapping_read_unlocked_no_poison() { + let lock = RwLock::new(()); + + let _ = panic::catch_unwind(|| { + let guard = lock.read().unwrap(); + let _guard = RwLockReadGuard::map::<(), _>(guard, |_| panic!()); + }); + + match lock.try_write() { + Ok(_) => {} + Err(TryLockError::WouldBlock) => { + panic!("panicking in a RwLockReadGuard::map closure should release the read lock") + } + Err(TryLockError::Poisoned(_)) => { + panic!("panicking in a RwLockReadGuard::map closure should not poison the RwLock") + } + } + + let _ = panic::catch_unwind(|| { + let guard = lock.read().unwrap(); + let _guard = RwLockReadGuard::try_map::<(), _>(guard, |_| panic!()); + }); + + match lock.try_write() { + Ok(_) => {} + Err(TryLockError::WouldBlock) => { + panic!("panicking in a RwLockReadGuard::try_map closure should release the read lock") + } + Err(TryLockError::Poisoned(_)) => { + panic!("panicking in a RwLockReadGuard::try_map closure should not poison the RwLock") + } + } + + let _ = panic::catch_unwind(|| { + let guard = lock.read().unwrap(); + let guard = RwLockReadGuard::map::<(), _>(guard, |val| val); + let _guard = MappedRwLockReadGuard::map::<(), _>(guard, |_| panic!()); + }); + + match lock.try_write() { + Ok(_) => {} + Err(TryLockError::WouldBlock) => { + panic!("panicking in a MappedRwLockReadGuard::map closure should release the read lock") + } + Err(TryLockError::Poisoned(_)) => { + panic!("panicking in a MappedRwLockReadGuard::map closure should not poison the RwLock") + } + } + + let _ = panic::catch_unwind(|| { + let guard = lock.read().unwrap(); + let guard = RwLockReadGuard::map::<(), _>(guard, |val| val); + let _guard = MappedRwLockReadGuard::try_map::<(), _>(guard, |_| panic!()); + }); + + match lock.try_write() { + Ok(_) => {} + Err(TryLockError::WouldBlock) => panic!( + "panicking in a MappedRwLockReadGuard::try_map closure should release the read lock" + ), + Err(TryLockError::Poisoned(_)) => panic!( + "panicking in a MappedRwLockReadGuard::try_map closure should not poison the RwLock" + ), + } + + drop(lock); +} + +#[test] +fn panic_while_mapping_write_unlocked_poison() { + let lock = RwLock::new(()); + + let _ = panic::catch_unwind(|| { + let guard = lock.write().unwrap(); + let _guard = RwLockWriteGuard::map::<(), _>(guard, |_| panic!()); + }); + + match lock.try_write() { + Ok(_) => panic!("panicking in a RwLockWriteGuard::map closure should poison the RwLock"), + Err(TryLockError::WouldBlock) => { + panic!("panicking in a RwLockWriteGuard::map closure should release the write lock") + } + Err(TryLockError::Poisoned(_)) => {} + } + + let _ = panic::catch_unwind(|| { + let guard = lock.write().unwrap(); + let _guard = RwLockWriteGuard::try_map::<(), _>(guard, |_| panic!()); + }); + + match lock.try_write() { + Ok(_) => { + panic!("panicking in a RwLockWriteGuard::try_map closure should poison the RwLock") + } + Err(TryLockError::WouldBlock) => { + panic!("panicking in a RwLockWriteGuard::try_map closure should release the write lock") + } + Err(TryLockError::Poisoned(_)) => {} + } + + let _ = panic::catch_unwind(|| { + let guard = lock.write().unwrap(); + let guard = RwLockWriteGuard::map::<(), _>(guard, |val| val); + let _guard = MappedRwLockWriteGuard::map::<(), _>(guard, |_| panic!()); + }); + + match lock.try_write() { + Ok(_) => { + panic!("panicking in a MappedRwLockWriteGuard::map closure should poison the RwLock") + } + Err(TryLockError::WouldBlock) => panic!( + "panicking in a MappedRwLockWriteGuard::map closure should release the write lock" + ), + Err(TryLockError::Poisoned(_)) => {} + } + + let _ = panic::catch_unwind(|| { + let guard = lock.write().unwrap(); + let guard = RwLockWriteGuard::map::<(), _>(guard, |val| val); + let _guard = MappedRwLockWriteGuard::try_map::<(), _>(guard, |_| panic!()); + }); + + match lock.try_write() { + Ok(_) => panic!( + "panicking in a MappedRwLockWriteGuard::try_map closure should poison the RwLock" + ), + Err(TryLockError::WouldBlock) => panic!( + "panicking in a MappedRwLockWriteGuard::try_map closure should release the write lock" + ), + Err(TryLockError::Poisoned(_)) => {} + } + + drop(lock); +} + +#[test] +fn test_downgrade_basic() { + let r = RwLock::new(()); + + let write_guard = r.write().unwrap(); + let _read_guard = RwLockWriteGuard::downgrade(write_guard); +} + +#[test] +// FIXME: On macOS we use a provenance-incorrect implementation and Miri catches that issue. +// See for details. +#[cfg_attr(all(miri, target_os = "macos"), ignore)] +fn test_downgrade_observe() { + // Taken from the test `test_rwlock_downgrade` from: + // https://github.com/Amanieu/parking_lot/blob/master/src/rwlock.rs + + const W: usize = 20; + const N: usize = if cfg!(miri) { 40 } else { 100 }; + + // This test spawns `W` writer threads, where each will increment a counter `N` times, ensuring + // that the value they wrote has not changed after downgrading. + + let rw = Arc::new(RwLock::new(0)); + + // Spawn the writers that will do `W * N` operations and checks. + let handles: Vec<_> = (0..W) + .map(|_| { + let rw = rw.clone(); + thread::spawn(move || { + for _ in 0..N { + // Increment the counter. + let mut write_guard = rw.write().unwrap(); + *write_guard += 1; + let cur_val = *write_guard; + + // Downgrade the lock to read mode, where the value protected cannot be modified. + let read_guard = RwLockWriteGuard::downgrade(write_guard); + assert_eq!(cur_val, *read_guard); + } + }) + }) + .collect(); + + for handle in handles { + handle.join().unwrap(); + } + + assert_eq!(*rw.read().unwrap(), W * N); +} + +#[test] +// FIXME: On macOS we use a provenance-incorrect implementation and Miri catches that issue. +// See for details. +#[cfg_attr(all(miri, target_os = "macos"), ignore)] +fn test_downgrade_atomic() { + const NEW_VALUE: i32 = -1; + + // This test checks that `downgrade` is atomic, meaning as soon as a write lock has been + // downgraded, the lock must be in read mode and no other threads can take the write lock to + // modify the protected value. + + // `W` is the number of evil writer threads. + const W: usize = 20; + let rwlock = Arc::new(RwLock::new(0)); + + // Spawns many evil writer threads that will try and write to the locked value before the + // initial writer (who has the exclusive lock) can read after it downgrades. + // If the `RwLock` behaves correctly, then the initial writer should read the value it wrote + // itself as no other thread should be able to mutate the protected value. + + // Put the lock in write mode, causing all future threads trying to access this go to sleep. + let mut main_write_guard = rwlock.write().unwrap(); + + // Spawn all of the evil writer threads. They will each increment the protected value by 1. + let handles: Vec<_> = (0..W) + .map(|_| { + let rwlock = rwlock.clone(); + thread::spawn(move || { + // Will go to sleep since the main thread initially has the write lock. + let mut evil_guard = rwlock.write().unwrap(); + *evil_guard += 1; + }) + }) + .collect(); + + // Wait for a good amount of time so that evil threads go to sleep. + // Note: this is not strictly necessary... + let eternity = std::time::Duration::from_millis(42); + thread::sleep(eternity); + + // Once everyone is asleep, set the value to `NEW_VALUE`. + *main_write_guard = NEW_VALUE; + + // Atomically downgrade the write guard into a read guard. + let main_read_guard = RwLockWriteGuard::downgrade(main_write_guard); + + // If the above is not atomic, then it would be possible for an evil thread to get in front of + // this read and change the value to be non-negative. + assert_eq!(*main_read_guard, NEW_VALUE, "`downgrade` was not atomic"); + + // Drop the main read guard and allow the evil writer threads to start incrementing. + drop(main_read_guard); + + for handle in handles { + handle.join().unwrap(); + } + + let final_check = rwlock.read().unwrap(); + assert_eq!(*final_check, W as i32 + NEW_VALUE); +} diff --git a/library/std/src/thread/local/dynamic_tests.rs b/library/std/tests/thread_local/dynamic_tests.rs similarity index 89% rename from library/std/src/thread/local/dynamic_tests.rs rename to library/std/tests/thread_local/dynamic_tests.rs index dd18004164824..454462b392510 100644 --- a/library/std/src/thread/local/dynamic_tests.rs +++ b/library/std/tests/thread_local/dynamic_tests.rs @@ -1,6 +1,6 @@ -use crate::cell::RefCell; -use crate::collections::HashMap; -use crate::thread_local; +use std::cell::RefCell; +use std::collections::HashMap; +use std::thread_local; #[test] fn smoke() { diff --git a/library/std/tests/thread_local/lib.rs b/library/std/tests/thread_local/lib.rs new file mode 100644 index 0000000000000..c52914354253c --- /dev/null +++ b/library/std/tests/thread_local/lib.rs @@ -0,0 +1,4 @@ +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] +mod tests; + +mod dynamic_tests; diff --git a/library/std/tests/thread_local/tests.rs b/library/std/tests/thread_local/tests.rs new file mode 100644 index 0000000000000..aa020c2559cc5 --- /dev/null +++ b/library/std/tests/thread_local/tests.rs @@ -0,0 +1,376 @@ +use std::cell::{Cell, UnsafeCell}; +use std::sync::atomic::{AtomicU8, Ordering}; +use std::sync::{Arc, Condvar, Mutex}; +use std::thread::{self, Builder, LocalKey}; +use std::thread_local; + +#[derive(Clone, Default)] +struct Signal(Arc<(Mutex, Condvar)>); + +impl Signal { + fn notify(&self) { + let (set, cvar) = &*self.0; + *set.lock().unwrap() = true; + cvar.notify_one(); + } + + fn wait(&self) { + let (set, cvar) = &*self.0; + let mut set = set.lock().unwrap(); + while !*set { + set = cvar.wait(set).unwrap(); + } + } +} + +struct NotifyOnDrop(Signal); + +impl Drop for NotifyOnDrop { + fn drop(&mut self) { + let NotifyOnDrop(ref f) = *self; + f.notify(); + } +} + +#[test] +fn smoke_no_dtor() { + thread_local!(static FOO: Cell = Cell::new(1)); + run(&FOO); + thread_local!(static FOO2: Cell = const { Cell::new(1) }); + run(&FOO2); + + fn run(key: &'static LocalKey>) { + key.with(|f| { + assert_eq!(f.get(), 1); + f.set(2); + }); + let t = thread::spawn(move || { + key.with(|f| { + assert_eq!(f.get(), 1); + }); + }); + t.join().unwrap(); + + key.with(|f| { + assert_eq!(f.get(), 2); + }); + } +} + +#[test] +fn states() { + struct Foo(&'static LocalKey); + impl Drop for Foo { + fn drop(&mut self) { + assert!(self.0.try_with(|_| ()).is_err()); + } + } + + thread_local!(static FOO: Foo = Foo(&FOO)); + run(&FOO); + thread_local!(static FOO2: Foo = const { Foo(&FOO2) }); + run(&FOO2); + + fn run(foo: &'static LocalKey) { + thread::spawn(move || { + assert!(foo.try_with(|_| ()).is_ok()); + }) + .join() + .unwrap(); + } +} + +#[test] +fn smoke_dtor() { + thread_local!(static FOO: UnsafeCell> = UnsafeCell::new(None)); + run(&FOO); + thread_local!(static FOO2: UnsafeCell> = const { UnsafeCell::new(None) }); + run(&FOO2); + + fn run(key: &'static LocalKey>>) { + let signal = Signal::default(); + let signal2 = signal.clone(); + let t = thread::spawn(move || unsafe { + let mut signal = Some(signal2); + key.with(|f| { + *f.get() = Some(NotifyOnDrop(signal.take().unwrap())); + }); + }); + signal.wait(); + t.join().unwrap(); + } +} + +#[test] +fn circular() { + // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint + #![allow(static_mut_refs)] + + struct S1(&'static LocalKey>>, &'static LocalKey>>); + struct S2(&'static LocalKey>>, &'static LocalKey>>); + thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); + thread_local!(static K2: UnsafeCell> = UnsafeCell::new(None)); + thread_local!(static K3: UnsafeCell> = const { UnsafeCell::new(None) }); + thread_local!(static K4: UnsafeCell> = const { UnsafeCell::new(None) }); + static mut HITS: usize = 0; + + impl Drop for S1 { + fn drop(&mut self) { + unsafe { + HITS += 1; + if self.1.try_with(|_| ()).is_err() { + assert_eq!(HITS, 3); + } else { + if HITS == 1 { + self.1.with(|s| *s.get() = Some(S2(self.0, self.1))); + } else { + assert_eq!(HITS, 3); + } + } + } + } + } + impl Drop for S2 { + fn drop(&mut self) { + unsafe { + HITS += 1; + assert!(self.0.try_with(|_| ()).is_ok()); + assert_eq!(HITS, 2); + self.0.with(|s| *s.get() = Some(S1(self.0, self.1))); + } + } + } + + thread::spawn(move || { + drop(S1(&K1, &K2)); + }) + .join() + .unwrap(); + + unsafe { + HITS = 0; + } + + thread::spawn(move || { + drop(S1(&K3, &K4)); + }) + .join() + .unwrap(); +} + +#[test] +fn self_referential() { + struct S1(&'static LocalKey>>); + + thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); + thread_local!(static K2: UnsafeCell> = const { UnsafeCell::new(None) }); + + impl Drop for S1 { + fn drop(&mut self) { + assert!(self.0.try_with(|_| ()).is_err()); + } + } + + thread::spawn(move || unsafe { + K1.with(|s| *s.get() = Some(S1(&K1))); + }) + .join() + .unwrap(); + + thread::spawn(move || unsafe { + K2.with(|s| *s.get() = Some(S1(&K2))); + }) + .join() + .unwrap(); +} + +// Note that this test will deadlock if TLS destructors aren't run (this +// requires the destructor to be run to pass the test). +#[test] +fn dtors_in_dtors_in_dtors() { + struct S1(Signal); + thread_local!(static K1: UnsafeCell> = UnsafeCell::new(None)); + thread_local!(static K2: UnsafeCell> = UnsafeCell::new(None)); + + impl Drop for S1 { + fn drop(&mut self) { + let S1(ref signal) = *self; + unsafe { + let _ = K2.try_with(|s| *s.get() = Some(NotifyOnDrop(signal.clone()))); + } + } + } + + let signal = Signal::default(); + let signal2 = signal.clone(); + let _t = thread::spawn(move || unsafe { + let mut signal = Some(signal2); + K1.with(|s| *s.get() = Some(S1(signal.take().unwrap()))); + }); + signal.wait(); +} + +#[test] +fn dtors_in_dtors_in_dtors_const_init() { + struct S1(Signal); + thread_local!(static K1: UnsafeCell> = const { UnsafeCell::new(None) }); + thread_local!(static K2: UnsafeCell> = const { UnsafeCell::new(None) }); + + impl Drop for S1 { + fn drop(&mut self) { + let S1(ref signal) = *self; + unsafe { + let _ = K2.try_with(|s| *s.get() = Some(NotifyOnDrop(signal.clone()))); + } + } + } + + let signal = Signal::default(); + let signal2 = signal.clone(); + let _t = thread::spawn(move || unsafe { + let mut signal = Some(signal2); + K1.with(|s| *s.get() = Some(S1(signal.take().unwrap()))); + }); + signal.wait(); +} + +// This test tests that TLS destructors have run before the thread joins. The +// test has no false positives (meaning: if the test fails, there's actually +// an ordering problem). It may have false negatives, where the test passes but +// join is not guaranteed to be after the TLS destructors. However, false +// negatives should be exceedingly rare due to judicious use of +// thread::yield_now and running the test several times. +#[test] +fn join_orders_after_tls_destructors() { + // We emulate a synchronous MPSC rendezvous channel using only atomics and + // thread::yield_now. We can't use std::mpsc as the implementation itself + // may rely on thread locals. + // + // The basic state machine for an SPSC rendezvous channel is: + // FRESH -> THREAD1_WAITING -> MAIN_THREAD_RENDEZVOUS + // where the first transition is done by the “receiving” thread and the 2nd + // transition is done by the “sending” thread. + // + // We add an additional state `THREAD2_LAUNCHED` between `FRESH` and + // `THREAD1_WAITING` to block until all threads are actually running. + // + // A thread that joins on the “receiving” thread completion should never + // observe the channel in the `THREAD1_WAITING` state. If this does occur, + // we switch to the “poison” state `THREAD2_JOINED` and panic all around. + // (This is equivalent to “sending” from an alternate producer thread.) + // + // Relaxed memory ordering is fine because and spawn()/join() already provide all the + // synchronization we need here. + const FRESH: u8 = 0; + const THREAD2_LAUNCHED: u8 = 1; + const THREAD1_WAITING: u8 = 2; + const MAIN_THREAD_RENDEZVOUS: u8 = 3; + const THREAD2_JOINED: u8 = 4; + static SYNC_STATE: AtomicU8 = AtomicU8::new(FRESH); + + for _ in 0..10 { + SYNC_STATE.store(FRESH, Ordering::Relaxed); + + let jh = thread::Builder::new() + .name("thread1".into()) + .spawn(move || { + struct TlDrop; + + impl Drop for TlDrop { + fn drop(&mut self) { + let mut sync_state = SYNC_STATE.swap(THREAD1_WAITING, Ordering::Relaxed); + loop { + match sync_state { + THREAD2_LAUNCHED | THREAD1_WAITING => thread::yield_now(), + MAIN_THREAD_RENDEZVOUS => break, + THREAD2_JOINED => panic!( + "Thread 1 still running after thread 2 joined on thread 1" + ), + v => unreachable!("sync state: {}", v), + } + sync_state = SYNC_STATE.load(Ordering::Relaxed); + } + } + } + + thread_local! { + static TL_DROP: TlDrop = TlDrop; + } + + TL_DROP.with(|_| {}); + + loop { + match SYNC_STATE.load(Ordering::Relaxed) { + FRESH => thread::yield_now(), + THREAD2_LAUNCHED => break, + v => unreachable!("sync state: {}", v), + } + } + }) + .unwrap(); + + let jh2 = thread::Builder::new() + .name("thread2".into()) + .spawn(move || { + assert_eq!(SYNC_STATE.swap(THREAD2_LAUNCHED, Ordering::Relaxed), FRESH); + jh.join().unwrap(); + match SYNC_STATE.swap(THREAD2_JOINED, Ordering::Relaxed) { + MAIN_THREAD_RENDEZVOUS => return, + THREAD2_LAUNCHED | THREAD1_WAITING => { + panic!("Thread 2 running after thread 1 join before main thread rendezvous") + } + v => unreachable!("sync state: {:?}", v), + } + }) + .unwrap(); + + loop { + match SYNC_STATE.compare_exchange( + THREAD1_WAITING, + MAIN_THREAD_RENDEZVOUS, + Ordering::Relaxed, + Ordering::Relaxed, + ) { + Ok(_) => break, + Err(FRESH) => thread::yield_now(), + Err(THREAD2_LAUNCHED) => thread::yield_now(), + Err(THREAD2_JOINED) => { + panic!("Main thread rendezvous after thread 2 joined thread 1") + } + v => unreachable!("sync state: {:?}", v), + } + } + jh2.join().unwrap(); + } +} + +// Test that thread::current is still available in TLS destructors. +#[test] +fn thread_current_in_dtor() { + // Go through one round of TLS destruction first. + struct Defer; + impl Drop for Defer { + fn drop(&mut self) { + RETRIEVE.with(|_| {}); + } + } + + struct RetrieveName; + impl Drop for RetrieveName { + fn drop(&mut self) { + *NAME.lock().unwrap() = Some(thread::current().name().unwrap().to_owned()); + } + } + + static NAME: Mutex> = Mutex::new(None); + + thread_local! { + static DEFER: Defer = const { Defer }; + static RETRIEVE: RetrieveName = const { RetrieveName }; + } + + Builder::new().name("test".to_owned()).spawn(|| DEFER.with(|_| {})).unwrap().join().unwrap(); + let name = NAME.lock().unwrap(); + let name = name.as_ref().unwrap(); + assert_eq!(name, "test"); +} diff --git a/library/std/tests/time.rs b/library/std/tests/time.rs new file mode 100644 index 0000000000000..40709eae37cfc --- /dev/null +++ b/library/std/tests/time.rs @@ -0,0 +1,229 @@ +#![feature(duration_constants)] + +use std::fmt::Debug; +use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; + +macro_rules! assert_almost_eq { + ($a:expr, $b:expr) => {{ + let (a, b) = ($a, $b); + if a != b { + let (a, b) = if a > b { (a, b) } else { (b, a) }; + assert!(a - Duration::from_micros(1) <= b, "{:?} is not almost equal to {:?}", a, b); + } + }}; +} + +#[test] +fn instant_monotonic() { + let a = Instant::now(); + loop { + let b = Instant::now(); + assert!(b >= a); + if b > a { + break; + } + } +} + +#[test] +#[cfg(not(target_arch = "wasm32"))] +fn instant_monotonic_concurrent() -> std::thread::Result<()> { + let threads: Vec<_> = (0..8) + .map(|_| { + std::thread::spawn(|| { + let mut old = Instant::now(); + let count = if cfg!(miri) { 1_000 } else { 5_000_000 }; + for _ in 0..count { + let new = Instant::now(); + assert!(new >= old); + old = new; + } + }) + }) + .collect(); + for t in threads { + t.join()?; + } + Ok(()) +} + +#[test] +fn instant_elapsed() { + let a = Instant::now(); + let _ = a.elapsed(); +} + +#[test] +fn instant_math() { + let a = Instant::now(); + let b = Instant::now(); + println!("a: {a:?}"); + println!("b: {b:?}"); + let dur = b.duration_since(a); + println!("dur: {dur:?}"); + assert_almost_eq!(b - dur, a); + assert_almost_eq!(a + dur, b); + + let second = Duration::SECOND; + assert_almost_eq!(a - second + second, a); + assert_almost_eq!(a.checked_sub(second).unwrap().checked_add(second).unwrap(), a); + + // checked_add_duration will not panic on overflow + let mut maybe_t = Some(Instant::now()); + let max_duration = Duration::from_secs(u64::MAX); + // in case `Instant` can store `>= now + max_duration`. + for _ in 0..2 { + maybe_t = maybe_t.and_then(|t| t.checked_add(max_duration)); + } + assert_eq!(maybe_t, None); + + // checked_add_duration calculates the right time and will work for another year + let year = Duration::from_secs(60 * 60 * 24 * 365); + assert_eq!(a + year, a.checked_add(year).unwrap()); +} + +#[test] +fn instant_math_is_associative() { + let now = Instant::now(); + let offset = Duration::from_millis(5); + // Changing the order of instant math shouldn't change the results, + // especially when the expression reduces to X + identity. + assert_eq!((now + offset) - now, (now - now) + offset); + + // On any platform, `Instant` should have the same resolution as `Duration` (e.g. 1 nanosecond) + // or better. Otherwise, math will be non-associative (see #91417). + let now = Instant::now(); + let provided_offset = Duration::from_nanos(1); + let later = now + provided_offset; + let measured_offset = later - now; + assert_eq!(measured_offset, provided_offset); +} + +#[test] +fn instant_duration_since_saturates() { + let a = Instant::now(); + assert_eq!((a - Duration::SECOND).duration_since(a), Duration::ZERO); +} + +#[test] +fn instant_checked_duration_since_nopanic() { + let now = Instant::now(); + let earlier = now - Duration::SECOND; + let later = now + Duration::SECOND; + assert_eq!(earlier.checked_duration_since(now), None); + assert_eq!(later.checked_duration_since(now), Some(Duration::SECOND)); + assert_eq!(now.checked_duration_since(now), Some(Duration::ZERO)); +} + +#[test] +fn instant_saturating_duration_since_nopanic() { + let a = Instant::now(); + #[allow(deprecated, deprecated_in_future)] + let ret = (a - Duration::SECOND).saturating_duration_since(a); + assert_eq!(ret, Duration::ZERO); +} + +#[test] +fn system_time_math() { + let a = SystemTime::now(); + let b = SystemTime::now(); + match b.duration_since(a) { + Ok(Duration::ZERO) => { + assert_almost_eq!(a, b); + } + Ok(dur) => { + assert!(b > a); + assert_almost_eq!(b - dur, a); + assert_almost_eq!(a + dur, b); + } + Err(dur) => { + let dur = dur.duration(); + assert!(a > b); + assert_almost_eq!(b + dur, a); + assert_almost_eq!(a - dur, b); + } + } + + let second = Duration::SECOND; + assert_almost_eq!(a.duration_since(a - second).unwrap(), second); + assert_almost_eq!(a.duration_since(a + second).unwrap_err().duration(), second); + + assert_almost_eq!(a - second + second, a); + assert_almost_eq!(a.checked_sub(second).unwrap().checked_add(second).unwrap(), a); + + let one_second_from_epoch = UNIX_EPOCH + Duration::SECOND; + let one_second_from_epoch2 = + UNIX_EPOCH + Duration::from_millis(500) + Duration::from_millis(500); + assert_eq!(one_second_from_epoch, one_second_from_epoch2); + + // checked_add_duration will not panic on overflow + let mut maybe_t = Some(SystemTime::UNIX_EPOCH); + let max_duration = Duration::from_secs(u64::MAX); + // in case `SystemTime` can store `>= UNIX_EPOCH + max_duration`. + for _ in 0..2 { + maybe_t = maybe_t.and_then(|t| t.checked_add(max_duration)); + } + assert_eq!(maybe_t, None); + + // checked_add_duration calculates the right time and will work for another year + let year = Duration::from_secs(60 * 60 * 24 * 365); + assert_eq!(a + year, a.checked_add(year).unwrap()); +} + +#[test] +fn system_time_elapsed() { + let a = SystemTime::now(); + drop(a.elapsed()); +} + +#[test] +fn since_epoch() { + let ts = SystemTime::now(); + let a = ts.duration_since(UNIX_EPOCH + Duration::SECOND).unwrap(); + let b = ts.duration_since(UNIX_EPOCH).unwrap(); + assert!(b > a); + assert_eq!(b - a, Duration::SECOND); + + let thirty_years = Duration::SECOND * 60 * 60 * 24 * 365 * 30; + + // Right now for CI this test is run in an emulator, and apparently the + // aarch64 emulator's sense of time is that we're still living in the + // 70s. This is also true for riscv (also qemu) + // + // Otherwise let's assume that we're all running computers later than + // 2000. + if !cfg!(target_arch = "aarch64") && !cfg!(target_arch = "riscv64") { + assert!(a > thirty_years); + } + + // let's assume that we're all running computers earlier than 2090. + // Should give us ~70 years to fix this! + let hundred_twenty_years = thirty_years * 4; + assert!(a < hundred_twenty_years); +} + +#[test] +fn big_math() { + // Check that the same result occurs when adding/subtracting each duration one at a time as when + // adding/subtracting them all at once. + #[track_caller] + fn check(start: Option, op: impl Fn(&T, Duration) -> Option) { + const DURATIONS: [Duration; 2] = + [Duration::from_secs(i64::MAX as _), Duration::from_secs(50)]; + if let Some(start) = start { + assert_eq!( + op(&start, DURATIONS.into_iter().sum()), + DURATIONS.into_iter().try_fold(start, |t, d| op(&t, d)) + ) + } + } + + check(SystemTime::UNIX_EPOCH.checked_sub(Duration::from_secs(100)), SystemTime::checked_add); + check(SystemTime::UNIX_EPOCH.checked_add(Duration::from_secs(100)), SystemTime::checked_sub); + + let instant = Instant::now(); + check(instant.checked_sub(Duration::from_secs(100)), Instant::checked_add); + check(instant.checked_sub(Duration::from_secs(i64::MAX as _)), Instant::checked_add); + check(instant.checked_add(Duration::from_secs(100)), Instant::checked_sub); + check(instant.checked_add(Duration::from_secs(i64::MAX as _)), Instant::checked_sub); +} diff --git a/library/std/tests/win_delete_self.rs b/library/std/tests/win_delete_self.rs new file mode 100644 index 0000000000000..ce505de69a22d --- /dev/null +++ b/library/std/tests/win_delete_self.rs @@ -0,0 +1,9 @@ +#![cfg(windows)] + +/// Attempting to delete a running binary should return an error on Windows. +#[test] +#[cfg_attr(miri, ignore)] // `remove_file` does not work in Miri on Windows +fn win_delete_self() { + let path = std::env::current_exe().unwrap(); + assert!(std::fs::remove_file(path).is_err()); +} diff --git a/library/test/src/cli.rs b/library/test/src/cli.rs index 4ccd825bf8dd3..ef6786f431670 100644 --- a/library/test/src/cli.rs +++ b/library/test/src/cli.rs @@ -1,7 +1,7 @@ //! Module converting command-line arguments into test configuration. use std::env; -use std::io::{self, IsTerminal}; +use std::io::{self, IsTerminal, Write}; use std::path::PathBuf; use super::options::{ColorConfig, Options, OutputFormat, RunIgnored}; @@ -44,7 +44,7 @@ impl TestOpts { } /// Result of parsing the options. -pub type OptRes = Result; +pub(crate) type OptRes = Result; /// Result of parsing the option part. type OptPartRes = Result; @@ -58,7 +58,7 @@ fn optgroups() -> getopts::Options { .optflag("", "bench", "Run benchmarks instead of tests") .optflag("", "list", "List all tests and benchmarks") .optflag("h", "help", "Display this message") - .optopt("", "logfile", "Write logs to the specified file", "PATH") + .optopt("", "logfile", "Write logs to the specified file (deprecated)", "PATH") .optflag( "", "nocapture", @@ -281,6 +281,10 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes { let options = Options::new().display_output(matches.opt_present("show-output")); + if logfile.is_some() { + let _ = write!(io::stderr(), "warning: `--logfile` is deprecated"); + } + let test_opts = TestOpts { list, filters, diff --git a/library/test/src/console.rs b/library/test/src/console.rs index 4d4cdcf4d7b6c..8f29f1dada528 100644 --- a/library/test/src/console.rs +++ b/library/test/src/console.rs @@ -20,7 +20,7 @@ use super::types::{NamePadding, TestDesc, TestDescAndFn}; use super::{filter_tests, run_tests, term}; /// Generic wrapper over stdout. -pub enum OutputLocation { +pub(crate) enum OutputLocation { Pretty(Box), Raw(T), } @@ -41,7 +41,7 @@ impl Write for OutputLocation { } } -pub struct ConsoleTestDiscoveryState { +pub(crate) struct ConsoleTestDiscoveryState { pub log_out: Option, pub tests: usize, pub benchmarks: usize, @@ -49,7 +49,7 @@ pub struct ConsoleTestDiscoveryState { } impl ConsoleTestDiscoveryState { - pub fn new(opts: &TestOpts) -> io::Result { + pub(crate) fn new(opts: &TestOpts) -> io::Result { let log_out = match opts.logfile { Some(ref path) => Some(File::create(path)?), None => None, @@ -58,7 +58,7 @@ impl ConsoleTestDiscoveryState { Ok(ConsoleTestDiscoveryState { log_out, tests: 0, benchmarks: 0, ignored: 0 }) } - pub fn write_log(&mut self, msg: F) -> io::Result<()> + pub(crate) fn write_log(&mut self, msg: F) -> io::Result<()> where S: AsRef, F: FnOnce() -> S, @@ -74,7 +74,7 @@ impl ConsoleTestDiscoveryState { } } -pub struct ConsoleTestState { +pub(crate) struct ConsoleTestState { pub log_out: Option, pub total: usize, pub passed: usize, @@ -92,7 +92,7 @@ pub struct ConsoleTestState { } impl ConsoleTestState { - pub fn new(opts: &TestOpts) -> io::Result { + pub(crate) fn new(opts: &TestOpts) -> io::Result { let log_out = match opts.logfile { Some(ref path) => Some(File::create(path)?), None => None, @@ -116,7 +116,7 @@ impl ConsoleTestState { }) } - pub fn write_log(&mut self, msg: F) -> io::Result<()> + pub(crate) fn write_log(&mut self, msg: F) -> io::Result<()> where S: AsRef, F: FnOnce() -> S, @@ -131,7 +131,7 @@ impl ConsoleTestState { } } - pub fn write_log_result( + pub(crate) fn write_log_result( &mut self, test: &TestDesc, result: &TestResult, @@ -170,7 +170,7 @@ impl ConsoleTestState { } // List the tests to console, and optionally to logfile. Filters are honored. -pub fn list_tests_console(opts: &TestOpts, tests: Vec) -> io::Result<()> { +pub(crate) fn list_tests_console(opts: &TestOpts, tests: Vec) -> io::Result<()> { let output = match term::stdout() { None => OutputLocation::Raw(io::stdout().lock()), Some(t) => OutputLocation::Pretty(t), @@ -314,9 +314,10 @@ pub fn run_tests_console(opts: &TestOpts, tests: Vec) -> io::Resu let mut st = ConsoleTestState::new(opts)?; // Prevent the usage of `Instant` in some cases: - // - It's currently not supported for wasm targets. + // - It's currently not supported for wasm targets without Emscripten nor WASI. + // - It's currently not supported for zkvm targets. let is_instant_unsupported = - (cfg!(target_family = "wasm") && !cfg!(target_os = "wasi")) || cfg!(target_os = "zkvm"); + (cfg!(target_family = "wasm") && cfg!(target_os = "unknown")) || cfg!(target_os = "zkvm"); let start_time = (!is_instant_unsupported).then(Instant::now); run_tests(opts, tests, |x| on_test_event(&x, &mut st, &mut *out))?; diff --git a/library/test/src/formatters/json.rs b/library/test/src/formatters/json.rs index aa1c50641cb54..92c1c0716f1f2 100644 --- a/library/test/src/formatters/json.rs +++ b/library/test/src/formatters/json.rs @@ -13,7 +13,7 @@ pub(crate) struct JsonFormatter { } impl JsonFormatter { - pub fn new(out: OutputLocation) -> Self { + pub(crate) fn new(out: OutputLocation) -> Self { Self { out } } diff --git a/library/test/src/formatters/junit.rs b/library/test/src/formatters/junit.rs index 96b432008404b..57b1b0feceefc 100644 --- a/library/test/src/formatters/junit.rs +++ b/library/test/src/formatters/junit.rs @@ -8,13 +8,13 @@ use crate::test_result::TestResult; use crate::time; use crate::types::{TestDesc, TestType}; -pub struct JunitFormatter { +pub(crate) struct JunitFormatter { out: OutputLocation, results: Vec<(TestDesc, TestResult, Duration, Vec)>, } impl JunitFormatter { - pub fn new(out: OutputLocation) -> Self { + pub(crate) fn new(out: OutputLocation) -> Self { Self { out, results: Vec::new() } } diff --git a/library/test/src/formatters/pretty.rs b/library/test/src/formatters/pretty.rs index 7089eae4330a0..bf3fc40db4117 100644 --- a/library/test/src/formatters/pretty.rs +++ b/library/test/src/formatters/pretty.rs @@ -20,7 +20,7 @@ pub(crate) struct PrettyFormatter { } impl PrettyFormatter { - pub fn new( + pub(crate) fn new( out: OutputLocation, use_color: bool, max_name_len: usize, @@ -31,19 +31,19 @@ impl PrettyFormatter { } #[cfg(test)] - pub fn output_location(&self) -> &OutputLocation { + pub(crate) fn output_location(&self) -> &OutputLocation { &self.out } - pub fn write_ok(&mut self) -> io::Result<()> { + pub(crate) fn write_ok(&mut self) -> io::Result<()> { self.write_short_result("ok", term::color::GREEN) } - pub fn write_failed(&mut self) -> io::Result<()> { + pub(crate) fn write_failed(&mut self) -> io::Result<()> { self.write_short_result("FAILED", term::color::RED) } - pub fn write_ignored(&mut self, message: Option<&'static str>) -> io::Result<()> { + pub(crate) fn write_ignored(&mut self, message: Option<&'static str>) -> io::Result<()> { if let Some(message) = message { self.write_short_result(&format!("ignored, {message}"), term::color::YELLOW) } else { @@ -51,15 +51,15 @@ impl PrettyFormatter { } } - pub fn write_time_failed(&mut self) -> io::Result<()> { + pub(crate) fn write_time_failed(&mut self) -> io::Result<()> { self.write_short_result("FAILED (time limit exceeded)", term::color::RED) } - pub fn write_bench(&mut self) -> io::Result<()> { + pub(crate) fn write_bench(&mut self) -> io::Result<()> { self.write_pretty("bench", term::color::CYAN) } - pub fn write_short_result( + pub(crate) fn write_short_result( &mut self, result: &str, color: term::color::Color, @@ -67,7 +67,7 @@ impl PrettyFormatter { self.write_pretty(result, color) } - pub fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> { + pub(crate) fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> { match self.out { OutputLocation::Pretty(ref mut term) => { if self.use_color { @@ -86,7 +86,7 @@ impl PrettyFormatter { } } - pub fn write_plain>(&mut self, s: S) -> io::Result<()> { + pub(crate) fn write_plain>(&mut self, s: S) -> io::Result<()> { let s = s.as_ref(); self.out.write_all(s.as_bytes())?; self.out.flush() @@ -154,15 +154,15 @@ impl PrettyFormatter { Ok(()) } - pub fn write_successes(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_successes(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_results(&state.not_failures, "successes") } - pub fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_results(&state.failures, "failures") } - pub fn write_time_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_time_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_results(&state.time_failures, "failures (time limit exceeded)") } diff --git a/library/test/src/formatters/terse.rs b/library/test/src/formatters/terse.rs index 534aa2f33110c..b28120ab56e69 100644 --- a/library/test/src/formatters/terse.rs +++ b/library/test/src/formatters/terse.rs @@ -25,7 +25,7 @@ pub(crate) struct TerseFormatter { } impl TerseFormatter { - pub fn new( + pub(crate) fn new( out: OutputLocation, use_color: bool, max_name_len: usize, @@ -42,11 +42,11 @@ impl TerseFormatter { } } - pub fn write_ok(&mut self) -> io::Result<()> { + pub(crate) fn write_ok(&mut self) -> io::Result<()> { self.write_short_result(".", term::color::GREEN) } - pub fn write_failed(&mut self, name: &str) -> io::Result<()> { + pub(crate) fn write_failed(&mut self, name: &str) -> io::Result<()> { // Put failed tests on their own line and include the test name, so that it's faster // to see which test failed without having to wait for them all to run. @@ -62,15 +62,15 @@ impl TerseFormatter { self.write_plain("\n") } - pub fn write_ignored(&mut self) -> io::Result<()> { + pub(crate) fn write_ignored(&mut self) -> io::Result<()> { self.write_short_result("i", term::color::YELLOW) } - pub fn write_bench(&mut self) -> io::Result<()> { + pub(crate) fn write_bench(&mut self) -> io::Result<()> { self.write_pretty("bench", term::color::CYAN) } - pub fn write_short_result( + pub(crate) fn write_short_result( &mut self, result: &str, color: term::color::Color, @@ -95,7 +95,7 @@ impl TerseFormatter { Ok(()) } - pub fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> { + pub(crate) fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> { match self.out { OutputLocation::Pretty(ref mut term) => { if self.use_color { @@ -114,13 +114,13 @@ impl TerseFormatter { } } - pub fn write_plain>(&mut self, s: S) -> io::Result<()> { + pub(crate) fn write_plain>(&mut self, s: S) -> io::Result<()> { let s = s.as_ref(); self.out.write_all(s.as_bytes())?; self.out.flush() } - pub fn write_outputs(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_outputs(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_plain("\nsuccesses:\n")?; let mut successes = Vec::new(); let mut stdouts = String::new(); @@ -146,7 +146,7 @@ impl TerseFormatter { Ok(()) } - pub fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_plain("\nfailures:\n")?; let mut failures = Vec::new(); let mut fail_out = String::new(); diff --git a/library/test/src/helpers/concurrency.rs b/library/test/src/helpers/concurrency.rs index b1545cbec438a..6648b669125f7 100644 --- a/library/test/src/helpers/concurrency.rs +++ b/library/test/src/helpers/concurrency.rs @@ -4,7 +4,7 @@ use std::num::NonZero; use std::{env, thread}; -pub fn get_concurrency() -> usize { +pub(crate) fn get_concurrency() -> usize { if let Ok(value) = env::var("RUST_TEST_THREADS") { match value.parse::>().ok() { Some(n) => n.get(), diff --git a/library/test/src/helpers/mod.rs b/library/test/src/helpers/mod.rs index 3c79b90b16754..2fb29b4c7bee5 100644 --- a/library/test/src/helpers/mod.rs +++ b/library/test/src/helpers/mod.rs @@ -1,6 +1,6 @@ //! Module with common helpers not directly related to tests //! but used in `libtest`. -pub mod concurrency; -pub mod metrics; -pub mod shuffle; +pub(crate) mod concurrency; +pub(crate) mod metrics; +pub(crate) mod shuffle; diff --git a/library/test/src/helpers/shuffle.rs b/library/test/src/helpers/shuffle.rs index 14389eb0e37af..53d1d0e42d4e8 100644 --- a/library/test/src/helpers/shuffle.rs +++ b/library/test/src/helpers/shuffle.rs @@ -4,7 +4,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; use crate::cli::TestOpts; use crate::types::{TestDescAndFn, TestId, TestName}; -pub fn get_shuffle_seed(opts: &TestOpts) -> Option { +pub(crate) fn get_shuffle_seed(opts: &TestOpts) -> Option { opts.shuffle_seed.or_else(|| { if opts.shuffle { Some( @@ -19,7 +19,7 @@ pub fn get_shuffle_seed(opts: &TestOpts) -> Option { }) } -pub fn shuffle_tests(shuffle_seed: u64, tests: &mut [(TestId, TestDescAndFn)]) { +pub(crate) fn shuffle_tests(shuffle_seed: u64, tests: &mut [(TestId, TestDescAndFn)]) { let test_names: Vec<&TestName> = tests.iter().map(|test| &test.1.desc.name).collect(); let test_names_hash = calculate_hash(&test_names); let mut rng = Rng::new(shuffle_seed, test_names_hash); diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 47407df909bdf..e523d30286619 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -27,6 +27,7 @@ #![feature(thread_spawn_hook)] #![allow(internal_features)] #![warn(rustdoc::unescaped_backticks)] +#![warn(unreachable_pub)] pub use cli::TestOpts; @@ -183,12 +184,16 @@ pub fn test_main_static_abort(tests: &[&TestDescAndFn]) { // If we're being run in SpawnedSecondary mode, run the test here. run_test // will then exit the process. if let Ok(name) = env::var(SECONDARY_TEST_INVOKER_VAR) { - env::remove_var(SECONDARY_TEST_INVOKER_VAR); + unsafe { + env::remove_var(SECONDARY_TEST_INVOKER_VAR); + } // Convert benchmarks to tests if we're not benchmarking. let mut tests = tests.iter().map(make_owned_test).collect::>(); if env::var(SECONDARY_TEST_BENCH_BENCHMARKS_VAR).is_ok() { - env::remove_var(SECONDARY_TEST_BENCH_BENCHMARKS_VAR); + unsafe { + env::remove_var(SECONDARY_TEST_BENCH_BENCHMARKS_VAR); + } } else { tests = convert_benchmarks_to_tests(tests); }; diff --git a/library/test/src/options.rs b/library/test/src/options.rs index 3eaad59474a12..7a5c55f4e2411 100644 --- a/library/test/src/options.rs +++ b/library/test/src/options.rs @@ -2,7 +2,7 @@ /// Number of times to run a benchmarked function #[derive(Clone, PartialEq, Eq)] -pub enum BenchMode { +pub(crate) enum BenchMode { Auto, Single, } diff --git a/library/test/src/stats/tests.rs b/library/test/src/stats/tests.rs index 4b209dcf214da..7804ddc929132 100644 --- a/library/test/src/stats/tests.rs +++ b/library/test/src/stats/tests.rs @@ -573,13 +573,13 @@ fn test_sum_f64_between_ints_that_sum_to_0() { } #[bench] -pub fn sum_three_items(b: &mut Bencher) { +fn sum_three_items(b: &mut Bencher) { b.iter(|| { [1e20f64, 1.5f64, -1e20f64].sum(); }) } #[bench] -pub fn sum_many_f64(b: &mut Bencher) { +fn sum_many_f64(b: &mut Bencher) { let nums = [-1e30f64, 1e60, 1e30, 1.0, -1e60]; let v = (0..500).map(|i| nums[i % 5]).collect::>(); @@ -589,4 +589,4 @@ pub fn sum_many_f64(b: &mut Bencher) { } #[bench] -pub fn no_iter(_: &mut Bencher) {} +fn no_iter(_: &mut Bencher) {} diff --git a/library/test/src/term.rs b/library/test/src/term.rs index e736e85d46966..d9880a776406d 100644 --- a/library/test/src/term.rs +++ b/library/test/src/term.rs @@ -62,7 +62,7 @@ pub(crate) mod color { /// A terminal with similar capabilities to an ANSI Terminal /// (foreground/background colors etc). -pub trait Terminal: Write { +pub(crate) trait Terminal: Write { /// Sets the foreground color to the given color. /// /// If the color is a bright color, but the terminal only supports 8 colors, diff --git a/library/test/src/term/terminfo/searcher/tests.rs b/library/test/src/term/terminfo/searcher/tests.rs index e1edd3b25cf4b..ff532a97d5eb9 100644 --- a/library/test/src/term/terminfo/searcher/tests.rs +++ b/library/test/src/term/terminfo/searcher/tests.rs @@ -11,7 +11,11 @@ fn test_get_dbpath_for_term() { } assert_eq!(x("screen"), PathBuf::from("/usr/share/terminfo/s/screen")); assert_eq!(get_dbpath_for_term(""), None); - env::set_var("TERMINFO_DIRS", ":"); + unsafe { + env::set_var("TERMINFO_DIRS", ":"); + } assert_eq!(x("screen"), PathBuf::from("/usr/share/terminfo/s/screen")); - env::remove_var("TERMINFO_DIRS"); + unsafe { + env::remove_var("TERMINFO_DIRS"); + } } diff --git a/library/test/src/term/win.rs b/library/test/src/term/win.rs index c77e6aac478bc..62e5c43ea2745 100644 --- a/library/test/src/term/win.rs +++ b/library/test/src/term/win.rs @@ -52,7 +52,7 @@ struct CONSOLE_SCREEN_BUFFER_INFO { #[allow(non_snake_case)] #[link(name = "kernel32")] -extern "system" { +unsafe extern "system" { fn SetConsoleTextAttribute(handle: HANDLE, attr: WORD) -> BOOL; fn GetStdHandle(which: DWORD) -> HANDLE; fn GetConsoleScreenBufferInfo(handle: HANDLE, info: *mut CONSOLE_SCREEN_BUFFER_INFO) -> BOOL; diff --git a/library/test/src/test_result.rs b/library/test/src/test_result.rs index 79fe07bc1ac5c..73dcc2e2a0cca 100644 --- a/library/test/src/test_result.rs +++ b/library/test/src/test_result.rs @@ -12,7 +12,7 @@ use super::types::TestDesc; // Return code for secondary process. // Start somewhere other than 0 so we know the return code means what we think // it means. -pub const TR_OK: i32 = 50; +pub(crate) const TR_OK: i32 = 50; // On Windows we use __fastfail to abort, which is documented to use this // exception code. @@ -39,7 +39,7 @@ pub enum TestResult { /// Creates a `TestResult` depending on the raw result of test execution /// and associated data. -pub fn calc_result<'a>( +pub(crate) fn calc_result<'a>( desc: &TestDesc, task_result: Result<(), &'a (dyn Any + 'static + Send)>, time_opts: Option<&time::TestTimeOptions>, @@ -93,7 +93,7 @@ pub fn calc_result<'a>( } /// Creates a `TestResult` depending on the exit code of test subprocess. -pub fn get_result_from_exit_code( +pub(crate) fn get_result_from_exit_code( desc: &TestDesc, status: ExitStatus, time_opts: Option<&time::TestTimeOptions>, diff --git a/library/test/src/tests.rs b/library/test/src/tests.rs index e85e61090a91b..47f581fefae1f 100644 --- a/library/test/src/tests.rs +++ b/library/test/src/tests.rs @@ -78,7 +78,7 @@ fn one_ignored_one_unignored_test() -> Vec { } #[test] -pub fn do_not_run_ignored_tests() { +fn do_not_run_ignored_tests() { fn f() -> Result<(), String> { panic!(); } @@ -106,7 +106,7 @@ pub fn do_not_run_ignored_tests() { } #[test] -pub fn ignored_tests_result_in_ignored() { +fn ignored_tests_result_in_ignored() { fn f() -> Result<(), String> { Ok(()) } @@ -133,9 +133,7 @@ pub fn ignored_tests_result_in_ignored() { assert_eq!(result, TrIgnored); } -// FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251) #[test] -#[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_should_panic() { fn f() -> Result<(), String> { @@ -164,9 +162,7 @@ fn test_should_panic() { assert_eq!(result, TrOk); } -// FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251) #[test] -#[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_should_panic_good_message() { fn f() -> Result<(), String> { @@ -195,9 +191,7 @@ fn test_should_panic_good_message() { assert_eq!(result, TrOk); } -// FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251) #[test] -#[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_should_panic_bad_message() { use crate::tests::TrFailedMsg; @@ -231,9 +225,7 @@ fn test_should_panic_bad_message() { assert_eq!(result, TrFailedMsg(failed_msg.to_string())); } -// FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251) #[test] -#[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_should_panic_non_string_message_type() { use std::any::TypeId; @@ -272,9 +264,7 @@ fn test_should_panic_non_string_message_type() { assert_eq!(result, TrFailedMsg(failed_msg)); } -// FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251) #[test] -#[cfg(not(target_os = "emscripten"))] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_should_panic_but_succeeds() { let should_panic_variants = [ShouldPanic::Yes, ShouldPanic::YesWithMessage("error message")]; @@ -479,7 +469,7 @@ fn parse_include_ignored_flag() { } #[test] -pub fn filter_for_ignored_option() { +fn filter_for_ignored_option() { // When we run ignored tests the test filter should filter out all the // unignored tests and flip the ignore flag on the rest to false @@ -496,7 +486,7 @@ pub fn filter_for_ignored_option() { } #[test] -pub fn run_include_ignored_option() { +fn run_include_ignored_option() { // When we "--include-ignored" tests, the ignore flag should be set to false on // all tests and no test filtered out @@ -513,7 +503,7 @@ pub fn run_include_ignored_option() { } #[test] -pub fn exclude_should_panic_option() { +fn exclude_should_panic_option() { let mut opts = TestOpts::new(); opts.run_tests = true; opts.exclude_should_panic = true; @@ -544,7 +534,7 @@ pub fn exclude_should_panic_option() { } #[test] -pub fn exact_filter_match() { +fn exact_filter_match() { fn tests() -> Vec { ["base", "base::test", "base::test1", "base::test2"] .into_iter() @@ -667,7 +657,7 @@ fn sample_tests() -> Vec { } #[test] -pub fn shuffle_tests() { +fn shuffle_tests() { let mut opts = TestOpts::new(); opts.shuffle = true; @@ -686,7 +676,7 @@ pub fn shuffle_tests() { } #[test] -pub fn shuffle_tests_with_seed() { +fn shuffle_tests_with_seed() { let mut opts = TestOpts::new(); opts.shuffle = true; @@ -704,7 +694,7 @@ pub fn shuffle_tests_with_seed() { } #[test] -pub fn order_depends_on_more_than_seed() { +fn order_depends_on_more_than_seed() { let mut opts = TestOpts::new(); opts.shuffle = true; @@ -732,7 +722,7 @@ pub fn order_depends_on_more_than_seed() { } #[test] -pub fn test_metricmap_compare() { +fn test_metricmap_compare() { let mut m1 = MetricMap::new(); let mut m2 = MetricMap::new(); m1.insert_metric("in-both-noise", 1000.0, 200.0); @@ -755,7 +745,7 @@ pub fn test_metricmap_compare() { } #[test] -pub fn test_bench_once_no_iter() { +fn test_bench_once_no_iter() { fn f(_: &mut Bencher) -> Result<(), String> { Ok(()) } @@ -763,7 +753,7 @@ pub fn test_bench_once_no_iter() { } #[test] -pub fn test_bench_once_iter() { +fn test_bench_once_iter() { fn f(b: &mut Bencher) -> Result<(), String> { b.iter(|| {}); Ok(()) @@ -772,7 +762,7 @@ pub fn test_bench_once_iter() { } #[test] -pub fn test_bench_no_iter() { +fn test_bench_no_iter() { fn f(_: &mut Bencher) -> Result<(), String> { Ok(()) } @@ -799,7 +789,7 @@ pub fn test_bench_no_iter() { } #[test] -pub fn test_bench_iter() { +fn test_bench_iter() { fn f(b: &mut Bencher) -> Result<(), String> { b.iter(|| {}); Ok(()) diff --git a/library/test/src/time.rs b/library/test/src/time.rs index 02ae050db55bd..f63b156b3dc5a 100644 --- a/library/test/src/time.rs +++ b/library/test/src/time.rs @@ -11,7 +11,7 @@ use std::{env, fmt}; use super::types::{TestDesc, TestType}; -pub const TEST_WARN_TIMEOUT_S: u64 = 60; +pub(crate) const TEST_WARN_TIMEOUT_S: u64 = 60; /// This small module contains constants used by `report-time` option. /// Those constants values will be used if corresponding environment variables are not set. @@ -22,42 +22,42 @@ pub const TEST_WARN_TIMEOUT_S: u64 = 60; /// /// Example of the expected format is `RUST_TEST_TIME_xxx=100,200`, where 100 means /// warn time, and 200 means critical time. -pub mod time_constants { +pub(crate) mod time_constants { use std::time::Duration; use super::TEST_WARN_TIMEOUT_S; /// Environment variable for overriding default threshold for unit-tests. - pub const UNIT_ENV_NAME: &str = "RUST_TEST_TIME_UNIT"; + pub(crate) const UNIT_ENV_NAME: &str = "RUST_TEST_TIME_UNIT"; // Unit tests are supposed to be really quick. - pub const UNIT_WARN: Duration = Duration::from_millis(50); - pub const UNIT_CRITICAL: Duration = Duration::from_millis(100); + pub(crate) const UNIT_WARN: Duration = Duration::from_millis(50); + pub(crate) const UNIT_CRITICAL: Duration = Duration::from_millis(100); /// Environment variable for overriding default threshold for unit-tests. - pub const INTEGRATION_ENV_NAME: &str = "RUST_TEST_TIME_INTEGRATION"; + pub(crate) const INTEGRATION_ENV_NAME: &str = "RUST_TEST_TIME_INTEGRATION"; // Integration tests may have a lot of work, so they can take longer to execute. - pub const INTEGRATION_WARN: Duration = Duration::from_millis(500); - pub const INTEGRATION_CRITICAL: Duration = Duration::from_millis(1000); + pub(crate) const INTEGRATION_WARN: Duration = Duration::from_millis(500); + pub(crate) const INTEGRATION_CRITICAL: Duration = Duration::from_millis(1000); /// Environment variable for overriding default threshold for unit-tests. - pub const DOCTEST_ENV_NAME: &str = "RUST_TEST_TIME_DOCTEST"; + pub(crate) const DOCTEST_ENV_NAME: &str = "RUST_TEST_TIME_DOCTEST"; // Doctests are similar to integration tests, because they can include a lot of // initialization code. - pub const DOCTEST_WARN: Duration = INTEGRATION_WARN; - pub const DOCTEST_CRITICAL: Duration = INTEGRATION_CRITICAL; + pub(crate) const DOCTEST_WARN: Duration = INTEGRATION_WARN; + pub(crate) const DOCTEST_CRITICAL: Duration = INTEGRATION_CRITICAL; // Do not suppose anything about unknown tests, base limits on the // `TEST_WARN_TIMEOUT_S` constant. - pub const UNKNOWN_WARN: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S); - pub const UNKNOWN_CRITICAL: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S * 2); + pub(crate) const UNKNOWN_WARN: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S); + pub(crate) const UNKNOWN_CRITICAL: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S * 2); } /// Returns an `Instance` object denoting when the test should be considered /// timed out. -pub fn get_default_test_timeout() -> Instant { +pub(crate) fn get_default_test_timeout() -> Instant { Instant::now() + Duration::from_secs(TEST_WARN_TIMEOUT_S) } @@ -73,7 +73,7 @@ impl fmt::Display for TestExecTime { /// The measured execution time of the whole test suite. #[derive(Debug, Clone, Default, PartialEq)] -pub struct TestSuiteExecTime(pub Duration); +pub(crate) struct TestSuiteExecTime(pub Duration); impl fmt::Display for TestSuiteExecTime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/library/unwind/Cargo.toml b/library/unwind/Cargo.toml index e13c9a06c05d1..66e8d1a3ffe5f 100644 --- a/library/unwind/Cargo.toml +++ b/library/unwind/Cargo.toml @@ -37,4 +37,4 @@ system-llvm-libunwind = [] [lints.rust.unexpected_cfgs] level = "warn" -check-cfg = [] +check-cfg = ['cfg(emscripten_wasm_eh)'] diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 40d409310ffab..2650b273a4dbe 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -4,10 +4,11 @@ #![feature(staged_api)] #![cfg_attr(not(target_env = "msvc"), feature(libc))] #![cfg_attr( - all(target_family = "wasm", not(target_os = "emscripten")), + all(target_family = "wasm", any(not(target_os = "emscripten"), emscripten_wasm_eh)), feature(simd_wasm64, wasm_exception_handling_intrinsics) )] #![allow(internal_features)] +#![cfg_attr(not(bootstrap), feature(cfg_emscripten_wasm_eh))] // Force libc to be included even if unused. This is required by many platforms. #[cfg(not(all(windows, target_env = "msvc")))] @@ -20,7 +21,6 @@ cfg_if::cfg_if! { target_os = "l4re", target_os = "none", target_os = "espidf", - target_os = "rtems", target_os = "nuttx", ))] { // These "unix" family members do not have unwinder. @@ -56,15 +56,15 @@ cfg_if::cfg_if! { compile_error!("`llvm-libunwind` and `system-llvm-libunwind` cannot be enabled at the same time"); } else if #[cfg(feature = "llvm-libunwind")] { #[link(name = "unwind", kind = "static", modifiers = "-bundle")] - extern "C" {} + unsafe extern "C" {} } else if #[cfg(feature = "system-llvm-libunwind")] { #[link(name = "unwind", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] #[link(name = "unwind", cfg(not(target_feature = "crt-static")))] - extern "C" {} + unsafe extern "C" {} } else { #[link(name = "unwind", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] #[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))] - extern "C" {} + unsafe extern "C" {} } } @@ -76,11 +76,11 @@ cfg_if::cfg_if! { compile_error!("`llvm-libunwind` and `system-llvm-libunwind` cannot be enabled at the same time"); } else if #[cfg(feature = "llvm-libunwind")] { #[link(name = "unwind", kind = "static", modifiers = "-bundle")] - extern "C" {} + unsafe extern "C" {} } else { #[link(name = "unwind", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] #[link(name = "unwind", cfg(not(target_feature = "crt-static")))] - extern "C" {} + unsafe extern "C" {} } } @@ -91,14 +91,14 @@ cfg_if::cfg_if! { } else { #[link(name = "unwind", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] #[link(name = "unwind", cfg(not(target_feature = "crt-static")))] - extern "C" {} + unsafe extern "C" {} } } // Android's unwinding library depends on dl_iterate_phdr in `libdl`. #[cfg(target_os = "android")] #[link(name = "dl", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] #[link(name = "dl", cfg(not(target_feature = "crt-static")))] -extern "C" {} +unsafe extern "C" {} // When building with crt-static, we get `gcc_eh` from the `libc` crate, since // glibc needs it, and needs it listed later on the linker command line. We @@ -110,7 +110,7 @@ extern "C" {} not(feature = "system-llvm-libunwind") ))] #[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))] -extern "C" {} +unsafe extern "C" {} #[cfg(all( target_os = "linux", @@ -119,67 +119,67 @@ extern "C" {} feature = "system-llvm-libunwind" ))] #[link(name = "unwind", cfg(not(target_feature = "crt-static")))] -extern "C" {} +unsafe extern "C" {} #[cfg(target_os = "redox")] #[link(name = "gcc_eh", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] #[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))] -extern "C" {} +unsafe extern "C" {} #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] #[link(name = "unwind", kind = "static", modifiers = "-bundle")] -extern "C" {} +unsafe extern "C" {} #[cfg(target_os = "netbsd")] #[link(name = "gcc_s")] -extern "C" {} +unsafe extern "C" {} #[cfg(target_os = "freebsd")] #[link(name = "gcc", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] #[link(name = "gcc_eh", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] #[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))] -extern "C" {} +unsafe extern "C" {} #[cfg(all(target_os = "openbsd", target_arch = "sparc64"))] #[link(name = "gcc")] -extern "C" {} +unsafe extern "C" {} #[cfg(all(target_os = "openbsd", not(target_arch = "sparc64")))] #[link(name = "c++abi")] -extern "C" {} +unsafe extern "C" {} #[cfg(any(target_os = "solaris", target_os = "illumos"))] #[link(name = "gcc_s")] -extern "C" {} +unsafe extern "C" {} #[cfg(target_os = "dragonfly")] #[link(name = "gcc_pic")] -extern "C" {} +unsafe extern "C" {} #[cfg(target_os = "haiku")] #[link(name = "gcc_s")] -extern "C" {} +unsafe extern "C" {} #[cfg(target_os = "aix")] #[link(name = "unwind")] -extern "C" {} +unsafe extern "C" {} #[cfg(target_os = "nto")] cfg_if::cfg_if! { if #[cfg(target_env = "nto70")] { #[link(name = "gcc")] - extern "C" {} + unsafe extern "C" {} } else { #[link(name = "gcc_s")] - extern "C" {} + unsafe extern "C" {} } } #[cfg(target_os = "hurd")] #[link(name = "gcc_s")] -extern "C" {} +unsafe extern "C" {} #[cfg(all(target_os = "windows", target_env = "gnu", target_abi = "llvm"))] #[link(name = "unwind", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] #[link(name = "unwind", cfg(not(target_feature = "crt-static")))] -extern "C" {} +unsafe extern "C" {} diff --git a/library/unwind/src/libunwind.rs b/library/unwind/src/libunwind.rs index 1fa9e480166b7..62165f8a20039 100644 --- a/library/unwind/src/libunwind.rs +++ b/library/unwind/src/libunwind.rs @@ -108,10 +108,10 @@ pub type _Unwind_Exception_Cleanup_Fn = ), link(name = "unwind", kind = "static", modifiers = "-bundle") )] -extern "C-unwind" { +unsafe extern "C-unwind" { pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !; } -extern "C" { +unsafe extern "C" { pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception); pub fn _Unwind_GetLanguageSpecificData(ctx: *mut _Unwind_Context) -> *mut c_void; pub fn _Unwind_GetRegionStart(ctx: *mut _Unwind_Context) -> _Unwind_Ptr; @@ -140,7 +140,7 @@ if #[cfg(any(target_vendor = "apple", target_os = "netbsd", not(target_arch = "a all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux", target_os = "xous")), link(name = "unwind", kind = "static", modifiers = "-bundle") )] - extern "C" { + unsafe extern "C" { pub fn _Unwind_GetGR(ctx: *mut _Unwind_Context, reg_index: c_int) -> _Unwind_Word; pub fn _Unwind_SetGR(ctx: *mut _Unwind_Context, reg_index: c_int, value: _Unwind_Word); pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> _Unwind_Word; @@ -198,7 +198,7 @@ if #[cfg(any(target_vendor = "apple", target_os = "netbsd", not(target_arch = "a all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux", target_os = "xous")), link(name = "unwind", kind = "static", modifiers = "-bundle") )] - extern "C" { + unsafe extern "C" { fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context, regclass: _Unwind_VRS_RegClass, regno: _Unwind_Word, @@ -261,7 +261,7 @@ cfg_if::cfg_if! { if #[cfg(all(target_vendor = "apple", not(target_os = "watchos"), target_arch = "arm"))] { // 32-bit ARM Apple (except for watchOS armv7k specifically) uses SjLj and // does not provide _Unwind_Backtrace() - extern "C-unwind" { + unsafe extern "C-unwind" { pub fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception) -> _Unwind_Reason_Code; } @@ -271,14 +271,14 @@ if #[cfg(all(target_vendor = "apple", not(target_os = "watchos"), target_arch = all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux", target_os = "xous")), link(name = "unwind", kind = "static", modifiers = "-bundle") )] - extern "C-unwind" { + unsafe extern "C-unwind" { pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) -> _Unwind_Reason_Code; } #[cfg_attr( all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux", target_os = "xous")), link(name = "unwind", kind = "static", modifiers = "-bundle") )] - extern "C" { + unsafe extern "C" { pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn, trace_argument: *mut c_void) -> _Unwind_Reason_Code; @@ -302,7 +302,7 @@ if #[cfg(all(windows, any(target_arch = "aarch64", target_arch = "x86_64"), targ context: *mut _Unwind_Context) -> _Unwind_Reason_Code; - extern "C" { + unsafe extern "C" { pub fn _GCC_specific_handler(exceptionRecord: *mut EXCEPTION_RECORD, establisherFrame: LPVOID, contextRecord: *mut CONTEXT, diff --git a/library/windows_targets/src/lib.rs b/library/windows_targets/src/lib.rs index 395cd6a4bab55..e89bde8b1abf4 100644 --- a/library/windows_targets/src/lib.rs +++ b/library/windows_targets/src/lib.rs @@ -39,4 +39,4 @@ pub macro link { #[link(name = "userenv")] #[link(name = "ws2_32")] #[link(name = "dbghelp")] // required for backtrace-rs symbolization -extern "C" {} +unsafe extern "C" {} diff --git a/license-metadata.json b/license-metadata.json index 09cc369356584..4cf9bea2861d4 100644 --- a/license-metadata.json +++ b/license-metadata.json @@ -113,8 +113,12 @@ { "directories": [], "files": [ + "FiraMono-Medium.woff2", + "FiraMono-Regular.woff2", + "FiraSans-Italic.woff2", "FiraSans-LICENSE.txt", "FiraSans-Medium.woff2", + "FiraSans-MediumItalic.woff2", "FiraSans-Regular.woff2" ], "license": { @@ -266,4 +270,4 @@ ], "type": "root" } -} \ No newline at end of file +} diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index c9697e670b777..890e64e2babbc 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -58,8 +58,12 @@ dependencies = [ "tar", "termcolor", "toml", + "tracing", + "tracing-chrome", + "tracing-subscriber", + "tracing-tree", "walkdir", - "windows 0.52.0", + "windows", "xz2", ] @@ -70,7 +74,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" dependencies = [ "memchr", - "regex-automata", + "regex-automata 0.4.9", "serde", ] @@ -84,9 +88,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.0" +version = "1.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8" +checksum = "9540e661f81799159abee814118cc139a2004b3a3aa3ea37724a1b66530b90e0" dependencies = [ "shlex", ] @@ -271,8 +275,8 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", ] [[package]] @@ -300,7 +304,7 @@ dependencies = [ "globset", "log", "memchr", - "regex-automata", + "regex-automata 0.4.9", "same-file", "walkdir", "winapi-util", @@ -322,6 +326,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.167" @@ -362,6 +372,15 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "memchr" version = "2.7.4" @@ -377,6 +396,25 @@ dependencies = [ "winapi", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "object" version = "0.36.5" @@ -386,6 +424,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + [[package]] name = "opener" version = "0.5.2" @@ -396,6 +440,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + [[package]] name = "pkg-config" version = "0.3.31" @@ -439,6 +495,27 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + [[package]] name = "regex-automata" version = "0.4.9" @@ -447,9 +524,15 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.5", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.5" @@ -539,12 +622,27 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + [[package]] name = "syn" version = "2.0.87" @@ -566,7 +664,7 @@ dependencies = [ "libc", "memchr", "ntapi", - "windows 0.57.0", + "windows", ] [[package]] @@ -589,6 +687,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "toml" version = "0.5.11" @@ -598,6 +706,90 @@ dependencies = [ "serde", ] +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-chrome" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf0a738ed5d6450a9fb96e86a23ad808de2b727fd1394585da5cdd6788ffe724" +dependencies = [ + "serde_json", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term 0.46.0", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "tracing-tree" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f459ca79f1b0d5f71c54ddfde6debfc59c8b6eeb46808ae492077f739dc7b49c" +dependencies = [ + "nu-ansi-term 0.50.1", + "tracing-core", + "tracing-log", + "tracing-subscriber", +] + [[package]] name = "typenum" version = "1.17.0" @@ -610,6 +802,12 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "version_check" version = "0.9.5" @@ -657,32 +855,13 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" -dependencies = [ - "windows-core 0.52.0", - "windows-targets", -] - [[package]] name = "windows" version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" dependencies = [ - "windows-core 0.57.0", - "windows-targets", -] - -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ + "windows-core", "windows-targets", ] diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index d8775a67e1939..2c1d85b01e6af 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -7,6 +7,7 @@ default-run = "bootstrap" [features] build-metrics = ["sysinfo"] +tracing = ["dep:tracing", "dep:tracing-chrome", "dep:tracing-subscriber", "dep:tracing-tree"] [lib] path = "src/lib.rs" @@ -36,7 +37,9 @@ test = false # Most of the time updating these dependencies requires modifications to the # bootstrap codebase(e.g., https://github.com/rust-lang/rust/issues/124565); # otherwise, some targets will fail. That's why these dependencies are explicitly pinned. -cc = "=1.2.0" +# +# Do not upgrade this crate unless https://github.com/rust-lang/cc-rs/issues/1317 is fixed. +cc = "=1.1.22" cmake = "=0.1.48" build_helper = { path = "../build_helper" } @@ -64,11 +67,17 @@ xz2 = "0.1" # Dependencies needed by the build-metrics feature sysinfo = { version = "0.33.0", default-features = false, optional = true, features = ["system"] } +# Dependencies needed by the `tracing` feature +tracing = { version = "0.1", optional = true, features = ["attributes"] } +tracing-chrome = { version = "0.7", optional = true } +tracing-subscriber = { version = "0.3", optional = true, features = ["env-filter", "fmt", "registry", "std"] } +tracing-tree = { version = "0.4.0", optional = true } + [target.'cfg(windows)'.dependencies.junction] version = "1.0.0" [target.'cfg(windows)'.dependencies.windows] -version = "0.52" +version = "0.57" features = [ "Win32_Foundation", "Win32_Security", diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 89415afbe3bc4..01a9792f1b375 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -1129,6 +1129,10 @@ def build_bootstrap_cmd(self, env): "-Zroot-dir=" + self.rust_root, ] args.extend("--verbose" for _ in range(self.verbose)) + + if "BOOTSTRAP_TRACING" in env: + args.append("--features=tracing") + if self.use_locked_deps: args.append("--locked") if self.use_vendored_sources: @@ -1264,6 +1268,11 @@ def bootstrap(args): config_toml = "" profile = RustBuild.get_toml_static(config_toml, "profile") + is_non_git_source = not os.path.exists(os.path.join(rust_root, ".git")) + + if profile is None and is_non_git_source: + profile = "dist" + if profile is not None: # Allows creating alias for profile names, allowing # profiles to be renamed while maintaining back compatibility @@ -1301,9 +1310,6 @@ def bootstrap(args): args = [build.bootstrap_binary()] args.extend(sys.argv[1:]) env = os.environ.copy() - # The Python process ID is used when creating a Windows job object - # (see src\bootstrap\src\utils\job.rs) - env["BOOTSTRAP_PARENT_ID"] = str(os.getpid()) env["BOOTSTRAP_PYTHON"] = sys.executable run(args, env=env, verbose=build.verbose, is_bootstrap=True) diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index a86c20d46bda5..ac971a64d7c22 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -44,10 +44,14 @@ def v(*args): o("verbose-tests", "rust.verbose-tests", "enable verbose output when running tests") o( "ccache", - "llvm.ccache", - "invoke gcc/clang via ccache to reuse object files between builds", + "build.ccache", + "invoke gcc/clang/rustc via ccache to reuse object files between builds", +) +o( + "sccache", + None, + "invoke gcc/clang/rustc via sccache to reuse object files between builds", ) -o("sccache", None, "invoke gcc/clang via sccache to reuse object files between builds") o("local-rust", None, "use an installed rustc rather than downloading a snapshot") v("local-rust-root", None, "set prefix for local rust binary") o( @@ -510,7 +514,7 @@ def apply_args(known_args, option_checking, config): build_triple = build(known_args) if option.name == "sccache": - set("llvm.ccache", "sccache", config) + set("build.ccache", "sccache", config) elif option.name == "local-rust": for path in os.environ["PATH"].split(os.pathsep): if os.path.exists(path + "/rustc"): diff --git a/src/bootstrap/defaults/config.compiler.toml b/src/bootstrap/defaults/config.compiler.toml index a737de3bd0856..269b90106e3aa 100644 --- a/src/bootstrap/defaults/config.compiler.toml +++ b/src/bootstrap/defaults/config.compiler.toml @@ -8,6 +8,8 @@ compiler-docs = true # where adding `debug!()` appears to do nothing. # However, it makes running the compiler slightly slower. debug-logging = true +# Enables debug assertions, which guard from many mistakes when working on the compiler. +debug-assertions = true # Get actually-useful information from backtraces, profiling, etc. with minimal added bytes debuginfo-level = "line-tables-only" # This greatly increases the speed of rebuilds, especially when there are only minor changes. However, it makes the initial build slightly slower. diff --git a/src/bootstrap/download-ci-llvm-stamp b/src/bootstrap/download-ci-llvm-stamp index 42cecbf5df9bb..d2c0d380fb40b 100644 --- a/src/bootstrap/download-ci-llvm-stamp +++ b/src/bootstrap/download-ci-llvm-stamp @@ -1,4 +1,4 @@ Change this file to make users of the `download-ci-llvm` configuration download a new version of LLVM from CI, even if the LLVM submodule hasn’t changed. -Last change is for: https://github.com/rust-lang/rust/pull/129788 +Last change is for: https://github.com/rust-lang/rust/pull/134740 diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in index a1f38b9ac147f..88aa70d4f2f83 100644 --- a/src/bootstrap/mk/Makefile.in +++ b/src/bootstrap/mk/Makefile.in @@ -55,14 +55,14 @@ check-aux: $(BOOTSTRAP_ARGS) # Run standard library tests in Miri. $(Q)$(BOOTSTRAP) miri --stage 2 \ - library/core \ + library/coretests \ library/alloc \ $(BOOTSTRAP_ARGS) \ --no-doc # Some doctests use file system operations to demonstrate dealing with `Result`. $(Q)MIRIFLAGS="-Zmiri-disable-isolation" \ $(BOOTSTRAP) miri --stage 2 \ - library/core \ + library/coretests \ library/alloc \ $(BOOTSTRAP_ARGS) \ --doc @@ -97,13 +97,20 @@ tidy: prepare: $(Q)$(BOOTSTRAP) build --stage 2 --dry-run +# Set of tests that represent around half of the time of the test suite. +# Used to split tests across multiple CI runners. +SKIP_COMPILER := --skip=compiler +SKIP_SRC := --skip=src +TEST_SET1 := $(SKIP_COMPILER) $(SKIP_SRC) +TEST_SET2 := --skip=tests --skip=coverage-map --skip=coverage-run --skip=library --skip=tidyselftest + ## MSVC native builders # this intentionally doesn't use `$(BOOTSTRAP)` so we can test the shebang on Windows ci-msvc-py: - $(Q)$(CFG_SRC_DIR)/x.py test --stage 2 tidy + $(Q)$(CFG_SRC_DIR)/x.py test --stage 2 $(TEST_SET1) ci-msvc-ps1: - $(Q)$(CFG_SRC_DIR)/x.ps1 test --stage 2 --skip tidy + $(Q)$(CFG_SRC_DIR)/x.ps1 test --stage 2 $(TEST_SET2) ci-msvc: ci-msvc-py ci-msvc-ps1 ## MingW native builders @@ -111,10 +118,14 @@ ci-msvc: ci-msvc-py ci-msvc-ps1 # Set of tests that should represent half of the time of the test suite. # Used to split tests across multiple CI runners. # Test both x and bootstrap entrypoints. +ci-mingw-x-1: + $(Q)$(CFG_SRC_DIR)/x test --stage 2 $(SKIP_COMPILER) $(TEST_SET2) +ci-mingw-x-2: + $(Q)$(CFG_SRC_DIR)/x test --stage 2 $(SKIP_SRC) $(TEST_SET2) ci-mingw-x: - $(Q)$(CFG_SRC_DIR)/x test --stage 2 --skip=compiler --skip=src + $(Q)$(CFG_SRC_DIR)/x test --stage 2 $(TEST_SET1) ci-mingw-bootstrap: - $(Q)$(BOOTSTRAP) test --stage 2 --skip=tests --skip=coverage-map --skip=coverage-run --skip=library --skip=tidyselftest + $(Q)$(BOOTSTRAP) test --stage 2 $(TEST_SET2) ci-mingw: ci-mingw-x ci-mingw-bootstrap .PHONY: dist diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index ee813de1c9e26..38b380e3db824 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -11,19 +11,27 @@ use std::str::FromStr; use std::{env, process}; use bootstrap::{ - Build, CONFIG_CHANGE_HISTORY, Config, Flags, Subcommand, find_recent_config_change_ids, + Build, CONFIG_CHANGE_HISTORY, Config, Flags, Subcommand, debug, find_recent_config_change_ids, human_readable_changes, t, }; use build_helper::ci::CiEnv; +#[cfg(feature = "tracing")] +use tracing::instrument; +#[cfg_attr(feature = "tracing", instrument(level = "trace", name = "main"))] fn main() { + #[cfg(feature = "tracing")] + let _guard = setup_tracing(); + let args = env::args().skip(1).collect::>(); if Flags::try_parse_verbose_help(&args) { return; } + debug!("parsing flags"); let flags = Flags::parse(&args); + debug!("parsing config based on flags"); let config = Config::parse(flags); let mut build_lock; @@ -47,7 +55,9 @@ fn main() { } err => { drop(err); - if let Ok(pid) = pid { + // #135972: We can reach this point when the lock has been taken, + // but the locker has not yet written its PID to the file + if let Some(pid) = pid.ok().filter(|pid| !pid.is_empty()) { println!("WARNING: build directory locked by process {pid}, waiting for lock"); } else { println!("WARNING: build directory locked, waiting for lock"); @@ -83,6 +93,7 @@ fn main() { let dump_bootstrap_shims = config.dump_bootstrap_shims; let out_dir = config.out.clone(); + debug!("creating new build based on config"); Build::new(config).build(); if suggest_setup { @@ -187,3 +198,37 @@ fn check_version(config: &Config) -> Option { Some(msg) } + +// # Note on `tracing` usage in bootstrap +// +// Due to the conditional compilation via the `tracing` cargo feature, this means that `tracing` +// usages in bootstrap need to be also gated behind the `tracing` feature: +// +// - `tracing` macros with log levels (`trace!`, `debug!`, `warn!`, `info`, `error`) should not be +// used *directly*. You should use the wrapped `tracing` macros which gate the actual invocations +// behind `feature = "tracing"`. +// - `tracing`'s `#[instrument(..)]` macro will need to be gated like `#![cfg_attr(feature = +// "tracing", instrument(..))]`. +#[cfg(feature = "tracing")] +fn setup_tracing() -> impl Drop { + use tracing_subscriber::EnvFilter; + use tracing_subscriber::layer::SubscriberExt; + + let filter = EnvFilter::from_env("BOOTSTRAP_TRACING"); + // cf. . + let layer = tracing_tree::HierarchicalLayer::default().with_targets(true).with_indent_amount(2); + + let mut chrome_layer = tracing_chrome::ChromeLayerBuilder::new().include_args(true); + + // Writes the Chrome profile to trace-.json if enabled + if !env::var("BOOTSTRAP_PROFILE").is_ok_and(|v| v == "1") { + chrome_layer = chrome_layer.writer(io::sink()); + } + + let (chrome_layer, _guard) = chrome_layer.build(); + + let registry = tracing_subscriber::registry().with(filter).with(layer).with(chrome_layer); + + tracing::subscriber::set_global_default(registry).unwrap(); + _guard +} diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 9434d876df8e8..21afb0312033a 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -1,7 +1,5 @@ //! Implementation of compiling the compiler and standard library, in "check"-based modes. -use std::path::PathBuf; - use crate::core::build_steps::compile::{ add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, std_crates_for_run_make, }; @@ -10,7 +8,8 @@ use crate::core::builder::{ self, Alias, Builder, Kind, RunConfig, ShouldRun, Step, crate_description, }; use crate::core::config::TargetSelection; -use crate::{Compiler, Mode, Subcommand}; +use crate::utils::build_stamp::{self, BuildStamp}; +use crate::{Mode, Subcommand}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Std { @@ -46,7 +45,7 @@ impl Step for Std { const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.crate_or_deps("sysroot").path("library") + run.crate_or_deps("sysroot").crate_or_deps("coretests").path("library") } fn make_run(run: RunConfig<'_>) { @@ -83,22 +82,16 @@ impl Step for Std { format_args!("library artifacts{}", crate_description(&self.crates)), target, ); - run_cargo( - builder, - cargo, - builder.config.free_args.clone(), - &libstd_stamp(builder, compiler, target), - vec![], - true, - false, - ); + + let stamp = build_stamp::libstd_stamp(builder, compiler, target).with_prefix("check"); + run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); // We skip populating the sysroot in non-zero stage because that'll lead // to rlib/rmeta conflicts if std gets built during this session. if compiler.stage == 0 { let libdir = builder.sysroot_target_libdir(compiler, target); let hostdir = builder.sysroot_target_libdir(compiler, compiler.host); - add_to_sysroot(builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target)); + add_to_sysroot(builder, &libdir, &hostdir, &stamp); } drop(_guard); @@ -139,16 +132,9 @@ impl Step for Std { cargo.arg("-p").arg(krate); } + let stamp = build_stamp::libstd_stamp(builder, compiler, target).with_prefix("check-test"); let _guard = builder.msg_check("library test/bench/example targets", target); - run_cargo( - builder, - cargo, - builder.config.free_args.clone(), - &libstd_test_stamp(builder, compiler, target), - vec![], - true, - false, - ); + run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); } } @@ -249,19 +235,14 @@ impl Step for Rustc { format_args!("compiler artifacts{}", crate_description(&self.crates)), target, ); - run_cargo( - builder, - cargo, - builder.config.free_args.clone(), - &librustc_stamp(builder, compiler, target), - vec![], - true, - false, - ); + + let stamp = build_stamp::librustc_stamp(builder, compiler, target).with_prefix("check"); + + run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); let libdir = builder.sysroot_target_libdir(compiler, target); let hostdir = builder.sysroot_target_libdir(compiler, compiler.host); - add_to_sysroot(builder, &libdir, &hostdir, &librustc_stamp(builder, compiler, target)); + add_to_sysroot(builder, &libdir, &hostdir, &stamp); } } @@ -315,15 +296,10 @@ impl Step for CodegenBackend { let _guard = builder.msg_check(backend, target); - run_cargo( - builder, - cargo, - builder.config.free_args.clone(), - &codegen_backend_stamp(builder, compiler, target, backend), - vec![], - true, - false, - ); + let stamp = build_stamp::codegen_backend_stamp(builder, compiler, target, backend) + .with_prefix("check"); + + run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); } } @@ -344,7 +320,7 @@ impl Step for RustAnalyzer { .config .tools .as_ref() - .map_or(true, |tools| tools.iter().any(|tool| tool == "rust-analyzer")), + .is_none_or(|tools| tools.iter().any(|tool| tool == "rust-analyzer")), ) } @@ -380,22 +356,13 @@ impl Step for RustAnalyzer { cargo.arg("--benches"); } - let _guard = builder.msg_check("rust-analyzer artifacts", target); - run_cargo( - builder, - cargo, - builder.config.free_args.clone(), - &stamp(builder, compiler, target), - vec![], - true, - false, - ); + // Cargo's output path in a given stage, compiled by a particular + // compiler for the specified target. + let stamp = BuildStamp::new(&builder.cargo_out(compiler, Mode::ToolRustc, target)) + .with_prefix("rust-analyzer-check"); - /// Cargo's output path in a given stage, compiled by a particular - /// compiler for the specified target. - fn stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf { - builder.cargo_out(compiler, Mode::ToolRustc, target).join(".rust-analyzer-check.stamp") - } + let _guard = builder.msg_check("rust-analyzer artifacts", target); + run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); } } @@ -469,9 +436,8 @@ fn run_tool_check_step( cargo.arg("--all-targets"); } - let stamp = builder - .cargo_out(compiler, Mode::ToolRustc, target) - .join(format!(".{}-check.stamp", step_type_name.to_lowercase())); + let stamp = BuildStamp::new(&builder.cargo_out(compiler, Mode::ToolRustc, target)) + .with_prefix(&format!("{}-check", step_type_name.to_lowercase())); let _guard = builder.msg_check(format!("{display_name} artifacts"), target); run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); @@ -489,43 +455,14 @@ tool_check_step!(Rls { path: "src/tools/rls" }); tool_check_step!(Rustfmt { path: "src/tools/rustfmt" }); tool_check_step!(MiroptTestTools { path: "src/tools/miropt-test-tools" }); tool_check_step!(TestFloatParse { path: "src/etc/test-float-parse" }); +tool_check_step!(FeaturesStatusDump { path: "src/tools/features-status-dump" }); tool_check_step!(Bootstrap { path: "src/bootstrap", default: false }); + +// `run-make-support` will be built as part of suitable run-make compiletest test steps, but support +// check to make it easier to work on. +tool_check_step!(RunMakeSupport { path: "src/tools/run-make-support", default: false }); + // Compiletest is implicitly "checked" when it gets built in order to run tests, // so this is mainly for people working on compiletest to run locally. tool_check_step!(Compiletest { path: "src/tools/compiletest", default: false }); - -/// Cargo's output path for the standard library in a given stage, compiled -/// by a particular compiler for the specified target. -fn libstd_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf { - builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check.stamp") -} - -/// Cargo's output path for the standard library in a given stage, compiled -/// by a particular compiler for the specified target. -fn libstd_test_stamp( - builder: &Builder<'_>, - compiler: Compiler, - target: TargetSelection, -) -> PathBuf { - builder.cargo_out(compiler, Mode::Std, target).join(".libstd-check-test.stamp") -} - -/// Cargo's output path for librustc in a given stage, compiled by a particular -/// compiler for the specified target. -fn librustc_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf { - builder.cargo_out(compiler, Mode::Rustc, target).join(".librustc-check.stamp") -} - -/// Cargo's output path for librustc_codegen_llvm in a given stage, compiled by a particular -/// compiler for the specified target and backend. -fn codegen_backend_stamp( - builder: &Builder<'_>, - compiler: Compiler, - target: TargetSelection, - backend: &str, -) -> PathBuf { - builder - .cargo_out(compiler, Mode::Codegen, target) - .join(format!(".librustc_codegen_{backend}-check.stamp")) -} diff --git a/src/bootstrap/src/core/build_steps/clean.rs b/src/bootstrap/src/core/build_steps/clean.rs index 61cc9eeed5555..21e9fea936387 100644 --- a/src/bootstrap/src/core/build_steps/clean.rs +++ b/src/bootstrap/src/core/build_steps/clean.rs @@ -10,6 +10,7 @@ use std::io::{self, ErrorKind}; use std::path::Path; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step, crate_description}; +use crate::utils::build_stamp::BuildStamp; use crate::utils::helpers::t; use crate::{Build, Compiler, Kind, Mode, Subcommand}; @@ -146,7 +147,7 @@ fn clean_default(build: &Build) { rm_rf(&build.out.join("dist")); rm_rf(&build.out.join("bootstrap").join(".last-warned-change-id")); rm_rf(&build.out.join("bootstrap-shims-dump")); - rm_rf(&build.out.join("rustfmt.stamp")); + rm_rf(BuildStamp::new(&build.out).with_prefix("rustfmt").path()); let mut hosts: Vec<_> = build.hosts.iter().map(|t| build.out.join(t)).collect(); // After cross-compilation, artifacts of the host architecture (which may differ from build.host) diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 518db156fea97..fe8c89f7a5394 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -1,12 +1,13 @@ //! Implementation of running clippy on the compiler, standard library and various tools. -use super::compile::{librustc_stamp, libstd_stamp, run_cargo, rustc_cargo, std_cargo}; +use super::compile::{run_cargo, rustc_cargo, std_cargo}; use super::tool::{SourceType, prepare_tool_cargo}; use super::{check, compile}; use crate::builder::{Builder, ShouldRun}; use crate::core::build_steps::compile::std_crates_for_run_make; use crate::core::builder; use crate::core::builder::{Alias, Kind, RunConfig, Step, crate_description}; +use crate::utils::build_stamp::{self, BuildStamp}; use crate::{Mode, Subcommand, TargetSelection}; /// Disable the most spammy clippy lints @@ -167,7 +168,7 @@ impl Step for Std { builder, cargo, lint_args(builder, &self.config, IGNORED_RULES_FOR_STD_AND_RUSTC), - &libstd_stamp(builder, compiler, target), + &build_stamp::libstd_stamp(builder, compiler, target), vec![], true, false, @@ -243,7 +244,7 @@ impl Step for Rustc { builder, cargo, lint_args(builder, &self.config, IGNORED_RULES_FOR_STD_AND_RUSTC), - &librustc_stamp(builder, compiler, target), + &build_stamp::librustc_stamp(builder, compiler, target), vec![], true, false, @@ -307,9 +308,9 @@ macro_rules! lint_any { &target, ); - let stamp = builder - .cargo_out(compiler, Mode::ToolRustc, target) - .join(format!(".{}-check.stamp", stringify!($name).to_lowercase())); + let stringified_name = stringify!($name).to_lowercase(); + let stamp = BuildStamp::new(&builder.cargo_out(compiler, Mode::ToolRustc, target)) + .with_prefix(&format!("{}-check", stringified_name)); run_cargo( builder, @@ -333,6 +334,7 @@ lint_any!( CargoMiri, "src/tools/miri/cargo-miri", "cargo-miri"; Clippy, "src/tools/clippy", "clippy"; CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata"; + CodegenGcc, "compiler/rustc_codegen_gcc", "rustc-codegen-gcc"; Compiletest, "src/tools/compiletest", "compiletest"; CoverageDump, "src/tools/coverage-dump", "coverage-dump"; Jsondocck, "src/tools/jsondocck", "jsondocck"; @@ -385,25 +387,60 @@ impl Step for CI { let library_clippy_cfg = LintConfig { allow: vec!["clippy::all".into()], warn: vec![], - deny: vec!["clippy::correctness".into()], + deny: vec![ + "clippy::correctness".into(), + "clippy::char_lit_as_u8".into(), + "clippy::four_forward_slashes".into(), + "clippy::needless_bool".into(), + "clippy::needless_bool_assign".into(), + "clippy::non_minimal_cfg".into(), + "clippy::print_literal".into(), + "clippy::same_item_push".into(), + "clippy::single_char_add_str".into(), + "clippy::to_string_in_format_args".into(), + ], forbid: vec![], }; - let compiler_clippy_cfg = LintConfig { - allow: vec!["clippy::all".into()], - warn: vec![], - deny: vec!["clippy::correctness".into(), "clippy::clone_on_ref_ptr".into()], - forbid: vec![], - }; - builder.ensure(Std { target: self.target, config: self.config.merge(&library_clippy_cfg), crates: vec![], }); + + let compiler_clippy_cfg = LintConfig { + allow: vec!["clippy::all".into()], + warn: vec![], + deny: vec![ + "clippy::correctness".into(), + "clippy::char_lit_as_u8".into(), + "clippy::clone_on_ref_ptr".into(), + "clippy::format_in_format_args".into(), + "clippy::four_forward_slashes".into(), + "clippy::needless_bool".into(), + "clippy::needless_bool_assign".into(), + "clippy::non_minimal_cfg".into(), + "clippy::print_literal".into(), + "clippy::same_item_push".into(), + "clippy::single_char_add_str".into(), + "clippy::to_string_in_format_args".into(), + ], + forbid: vec![], + }; builder.ensure(Rustc { target: self.target, config: self.config.merge(&compiler_clippy_cfg), crates: vec![], }); + + let rustc_codegen_gcc = LintConfig { + allow: vec![], + warn: vec![], + deny: vec!["warnings".into()], + forbid: vec![], + }; + builder.ensure(CodegenGcc { + target: self.target, + config: self.config.merge(&rustc_codegen_gcc), + }); } } diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index ca337aa9f4c32..479327d63695c 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -24,6 +24,8 @@ use crate::core::builder::{ Builder, Cargo, Kind, PathSet, RunConfig, ShouldRun, Step, TaskPath, crate_description, }; use crate::core::config::{DebuginfoLevel, LlvmLibunwind, RustcLto, TargetSelection}; +use crate::utils::build_stamp; +use crate::utils::build_stamp::BuildStamp; use crate::utils::exec::command; use crate::utils::helpers::{ exe, get_clang_cl_resource_dir, is_debug_info, is_dylib, symlink_dir, t, up_to_date, @@ -93,7 +95,7 @@ impl Step for Std { const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.crate_or_deps("sysroot").path("library") + run.crate_or_deps("sysroot").path("library").alias("core") } fn make_run(run: RunConfig<'_>) { @@ -189,7 +191,7 @@ impl Step for Std { // The LLD wrappers and `rust-lld` are self-contained linking components that can be // necessary to link the stdlib on some targets. We'll also need to copy these binaries to // the `stage0-sysroot` to ensure the linker is found when bootstrapping on such a target. - if compiler.stage == 0 && compiler.host == builder.config.build { + if compiler.stage == 0 && builder.is_builder_target(&compiler.host) { // We want to copy the host `bin` folder within the `rustlib` folder in the sysroot. let src_sysroot_bin = builder .rustc_snapshot_sysroot() @@ -254,7 +256,7 @@ impl Step for Std { builder, cargo, vec![], - &libstd_stamp(builder, compiler, target), + &build_stamp::libstd_stamp(builder, compiler, target), target_deps, self.is_for_mir_opt_tests, // is_check false, @@ -337,7 +339,7 @@ fn copy_self_contained_objects( // to using gcc from a glibc-targeting toolchain for linking. // To do that we have to distribute musl startup objects as a part of Rust toolchain // and link with them manually in the self-contained mode. - if target.contains("musl") && !target.contains("unikraft") { + if target.needs_crt_begin_end() { let srcdir = builder.musl_libdir(target).unwrap_or_else(|| { panic!("Target {:?} does not have a \"musl-libdir\" key", target.triple) }); @@ -441,8 +443,49 @@ fn compiler_rt_for_profiler(builder: &Builder<'_>) -> PathBuf { /// Configure cargo to compile the standard library, adding appropriate env vars /// and such. pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, cargo: &mut Cargo) { - if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") { - cargo.env("MACOSX_DEPLOYMENT_TARGET", target); + // rustc already ensures that it builds with the minimum deployment + // target, so ideally we shouldn't need to do anything here. + // + // However, `cc` currently defaults to a higher version for backwards + // compatibility, which means that compiler-rt, which is built via + // compiler-builtins' build script, gets built with a higher deployment + // target. This in turn causes warnings while linking, and is generally + // a compatibility hazard. + // + // So, at least until https://github.com/rust-lang/cc-rs/issues/1171, or + // perhaps https://github.com/rust-lang/cargo/issues/13115 is resolved, we + // explicitly set the deployment target environment variables to avoid + // this issue. + // + // This place also serves as an extension point if we ever wanted to raise + // rustc's default deployment target while keeping the prebuilt `std` at + // a lower version, so it's kinda nice to have in any case. + if target.contains("apple") && !builder.config.dry_run() { + // Query rustc for the deployment target, and the associated env var. + // The env var is one of the standard `*_DEPLOYMENT_TARGET` vars, i.e. + // `MACOSX_DEPLOYMENT_TARGET`, `IPHONEOS_DEPLOYMENT_TARGET`, etc. + let mut cmd = command(builder.rustc(cargo.compiler())); + cmd.arg("--target").arg(target.rustc_target_arg()); + cmd.arg("--print=deployment-target"); + let output = cmd.run_capture_stdout(builder).stdout(); + + let (env_var, value) = output.split_once('=').unwrap(); + // Unconditionally set the env var (if it was set in the environment + // already, rustc should've picked that up). + cargo.env(env_var.trim(), value.trim()); + + // Allow CI to override the deployment target for `std` on macOS. + // + // This is useful because we might want the host tooling LLVM, `rustc` + // and Cargo to have a different deployment target than `std` itself + // (currently, these two versions are the same, but in the past, we + // supported macOS 10.7 for user code and macOS 10.8 in host tooling). + // + // It is not necessary on the other platforms, since only macOS has + // support for host tooling. + if let Some(target) = env::var_os("MACOSX_STD_DEPLOYMENT_TARGET") { + cargo.env("MACOSX_DEPLOYMENT_TARGET", target); + } } // Paths needed by `library/profiler_builtins/build.rs`. @@ -469,7 +512,7 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car // If `compiler-rt` is available ensure that the `c` feature of the // `compiler-builtins` crate is enabled and it's configured to learn where // `compiler-rt` is located. - let compiler_builtins_c_feature = if builder.config.optimized_compiler_builtins { + let compiler_builtins_c_feature = if builder.config.optimized_compiler_builtins(target) { // NOTE: this interacts strangely with `llvm-has-rust-patches`. In that case, we enforce `submodules = false`, so this is a no-op. // But, the user could still decide to manually use an in-tree submodule. // @@ -644,7 +687,12 @@ impl Step for StdLink { (libdir, hostdir) }; - add_to_sysroot(builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target)); + add_to_sysroot( + builder, + &libdir, + &hostdir, + &build_stamp::libstd_stamp(builder, compiler, target), + ); // Special case for stage0, to make `rustup toolchain link` and `x dist --stage 0` // work for stage0-sysroot. We only do this if the stage0 compiler comes from beta, @@ -973,7 +1021,7 @@ impl Step for Rustc { compiler.host, target, ); - let stamp = librustc_stamp(builder, compiler, target); + let stamp = build_stamp::librustc_stamp(builder, compiler, target); run_cargo( builder, cargo, @@ -984,7 +1032,7 @@ impl Step for Rustc { true, // Only ship rustc_driver.so and .rmeta files, not all intermediate .rlib files. ); - let target_root_dir = stamp.parent().unwrap(); + let target_root_dir = stamp.path().parent().unwrap(); // When building `librustc_driver.so` (like `libLLVM.so`) on linux, it can contain // unexpected debuginfo from dependencies, for example from the C++ standard library used in // our LLVM wrapper. Unless we're explicitly requesting `librustc_driver` to be built with @@ -1042,8 +1090,12 @@ pub fn rustc_cargo( // . cargo.rustflag("-Zon-broken-pipe=kill"); + // We want to link against registerEnzyme and in the future we want to use additional + // functionality from Enzyme core. For that we need to link against Enzyme. if builder.config.llvm_enzyme { - cargo.rustflag("-l").rustflag("Enzyme-19"); + let llvm_config = builder.llvm_config(builder.config.build).unwrap(); + let llvm_version_major = llvm::get_llvm_version_major(builder, &llvm_config); + cargo.rustflag("-l").rustflag(&format!("Enzyme-{llvm_version_major}")); } // Building with protected visibility reduces the number of dynamic relocations needed, giving @@ -1054,9 +1106,7 @@ pub fn rustc_cargo( cargo.rustflag("-Zdefault-visibility=protected"); } - // We currently don't support cross-crate LTO in stage0. This also isn't hugely necessary - // and may just be a time sink. - if compiler.stage != 0 { + if is_lto_stage(compiler) { match builder.config.rust_lto { RustcLto::Thin | RustcLto::Fat => { // Since using LTO for optimizing dylibs is currently experimental, @@ -1207,6 +1257,15 @@ pub fn rustc_cargo_env( rustc_llvm_env(builder, cargo, target) } } + + // Build jemalloc on AArch64 with support for page sizes up to 64K + // See: https://github.com/rust-lang/rust/pull/135081 + if builder.config.jemalloc + && target.starts_with("aarch64") + && env::var_os("JEMALLOC_SYS_WITH_LG_PAGE").is_none() + { + cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "16"); + } } /// Pass down configuration from the LLVM build into the build of @@ -1215,6 +1274,9 @@ fn rustc_llvm_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelect if builder.is_rust_llvm(target) { cargo.env("LLVM_RUSTLLVM", "1"); } + if builder.config.llvm_enzyme { + cargo.env("LLVM_ENZYME", "1"); + } let llvm::LlvmResult { llvm_config, .. } = builder.ensure(llvm::Llvm { target }); cargo.env("LLVM_CONFIG", &llvm_config); @@ -1320,7 +1382,7 @@ impl Step for RustcLink { builder, &builder.sysroot_target_libdir(previous_stage_compiler, target), &builder.sysroot_target_libdir(previous_stage_compiler, compiler.host), - &librustc_stamp(builder, compiler, target), + &build_stamp::librustc_stamp(builder, compiler, target), ); } } @@ -1438,7 +1500,7 @@ impl Step for CodegenBackend { .arg(builder.src.join(format!("compiler/rustc_codegen_{backend}/Cargo.toml"))); rustc_cargo_env(builder, &mut cargo, target, compiler.stage); - let tmp_stamp = out_dir.join(".tmp.stamp"); + let tmp_stamp = BuildStamp::new(&out_dir).with_prefix("tmp"); let _guard = builder.msg_build(compiler, format_args!("codegen backend {backend}"), target); let files = run_cargo(builder, cargo, vec![], &tmp_stamp, vec![], false, false); @@ -1460,9 +1522,9 @@ impl Step for CodegenBackend { f.display() ); } - let stamp = codegen_backend_stamp(builder, compiler, target, &backend); + let stamp = build_stamp::codegen_backend_stamp(builder, compiler, target, &backend); let codegen_backend = codegen_backend.to_str().unwrap(); - t!(fs::write(stamp, codegen_backend)); + t!(stamp.add_stamp(codegen_backend).write()); } } @@ -1499,8 +1561,8 @@ fn copy_codegen_backends_to_sysroot( continue; // Already built as part of rustc } - let stamp = codegen_backend_stamp(builder, compiler, target, backend); - let dylib = t!(fs::read_to_string(&stamp)); + let stamp = build_stamp::codegen_backend_stamp(builder, compiler, target, backend); + let dylib = t!(fs::read_to_string(stamp.path())); let file = Path::new(&dylib); let filename = file.file_name().unwrap().to_str().unwrap(); // change `librustc_codegen_cranelift-xxxxxx.so` to @@ -1514,35 +1576,6 @@ fn copy_codegen_backends_to_sysroot( } } -/// Cargo's output path for the standard library in a given stage, compiled -/// by a particular compiler for the specified target. -pub fn libstd_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf { - builder.cargo_out(compiler, Mode::Std, target).join(".libstd.stamp") -} - -/// Cargo's output path for librustc in a given stage, compiled by a particular -/// compiler for the specified target. -pub fn librustc_stamp( - builder: &Builder<'_>, - compiler: Compiler, - target: TargetSelection, -) -> PathBuf { - builder.cargo_out(compiler, Mode::Rustc, target).join(".librustc.stamp") -} - -/// Cargo's output path for librustc_codegen_llvm in a given stage, compiled by a particular -/// compiler for the specified target and backend. -fn codegen_backend_stamp( - builder: &Builder<'_>, - compiler: Compiler, - target: TargetSelection, - backend: &str, -) -> PathBuf { - builder - .cargo_out(compiler, Mode::Codegen, target) - .join(format!(".librustc_codegen_{backend}.stamp")) -} - pub fn compiler_file( builder: &Builder<'_>, compiler: &Path, @@ -1554,7 +1587,8 @@ pub fn compiler_file( return PathBuf::new(); } let mut cmd = command(compiler); - cmd.args(builder.cflags(target, GitRepo::Rustc, c)); + cmd.args(builder.cc_handled_clags(target, c)); + cmd.args(builder.cc_unhandled_cflags(target, GitRepo::Rustc, c)); cmd.arg(format!("-print-file-name={file}")); let out = cmd.run_capture_stdout(builder).stdout(); PathBuf::from(out.trim()) @@ -1664,10 +1698,10 @@ impl Step for Sysroot { ]; let ci_rustc_dir = builder.config.ci_rustc_dir(); builder.cp_link_filtered(&ci_rustc_dir, &sysroot, &|path| { - if path.extension().map_or(true, |ext| !filtered_extensions.contains(&ext)) { + if path.extension().is_none_or(|ext| !filtered_extensions.contains(&ext)) { return true; } - if !path.parent().map_or(true, |p| p.ends_with(&suffix)) { + if !path.parent().is_none_or(|p| p.ends_with(&suffix)) { return true; } if !filtered_files.iter().all(|f| f != path.file_name().unwrap()) { @@ -1790,7 +1824,13 @@ impl Step for Assemble { // When using `download-ci-llvm`, some of the tools // may not exist, so skip trying to copy them. if src_path.exists() { - builder.copy_link(&src_path, &libdir_bin.join(&tool_exe)); + // There is a chance that these tools are being installed from an external LLVM. + // Use `Builder::resolve_symlink_and_copy` instead of `Builder::copy_link` to ensure + // we are copying the original file not the symlinked path, which causes issues for + // tarball distribution. + // + // See https://github.com/rust-lang/rust/issues/135554. + builder.resolve_symlink_and_copy(&src_path, &libdir_bin.join(&tool_exe)); } } } @@ -1899,7 +1939,7 @@ impl Step for Assemble { builder.info(&msg); // Link in all dylibs to the libdir - let stamp = librustc_stamp(builder, build_compiler, target_compiler.host); + let stamp = build_stamp::librustc_stamp(builder, build_compiler, target_compiler.host); let proc_macros = builder .read_stamp_file(&stamp) .into_iter() @@ -2005,7 +2045,7 @@ pub fn add_to_sysroot( builder: &Builder<'_>, sysroot_dst: &Path, sysroot_host_dst: &Path, - stamp: &Path, + stamp: &BuildStamp, ) { let self_contained_dst = &sysroot_dst.join("self-contained"); t!(fs::create_dir_all(sysroot_dst)); @@ -2025,13 +2065,13 @@ pub fn run_cargo( builder: &Builder<'_>, cargo: Cargo, tail_args: Vec, - stamp: &Path, + stamp: &BuildStamp, additional_target_deps: Vec<(PathBuf, DependencyType)>, is_check: bool, rlib_only_metadata: bool, ) -> Vec { // `target_root_dir` looks like $dir/$target/release - let target_root_dir = stamp.parent().unwrap(); + let target_root_dir = stamp.path().parent().unwrap(); // `target_deps_dir` looks like $dir/$target/release/deps let target_deps_dir = target_root_dir.join("deps"); // `host_root_dir` looks like $dir/release @@ -2184,7 +2224,7 @@ pub fn run_cargo( new_contents.extend(dep.to_str().unwrap().as_bytes()); new_contents.extend(b"\0"); } - t!(fs::write(stamp, &new_contents)); + t!(fs::write(stamp.path(), &new_contents)); deps.into_iter().map(|(d, _)| d).collect() } @@ -2195,6 +2235,10 @@ pub fn stream_cargo( cb: &mut dyn FnMut(CargoMessage<'_>), ) -> bool { let mut cmd = cargo.into_cmd(); + + #[cfg(feature = "tracing")] + let _run_span = crate::trace_cmd!(cmd); + let cargo = cmd.as_command_mut(); // Instruct Cargo to give us json messages on stdout, critically leaving // stderr as piped so we can get those pretty colors. @@ -2271,7 +2315,8 @@ pub fn strip_debug(builder: &Builder<'_>, target: TargetSelection, path: &Path) // FIXME: to make things simpler for now, limit this to the host and target where we know // `strip -g` is both available and will fix the issue, i.e. on a x64 linux host that is not // cross-compiling. Expand this to other appropriate targets in the future. - if target != "x86_64-unknown-linux-gnu" || target != builder.config.build || !path.exists() { + if target != "x86_64-unknown-linux-gnu" || !builder.is_builder_target(&target) || !path.exists() + { return; } @@ -2294,3 +2339,8 @@ pub fn strip_debug(builder: &Builder<'_>, target: TargetSelection, path: &Path) // everything else (standard library, future stages...) to be rebuilt. t!(file.set_modified(previous_mtime)); } + +/// We only use LTO for stage 2+, to speed up build time of intermediate stages. +pub fn is_lto_stage(build_compiler: &Compiler) -> bool { + build_compiler.stage != 0 +} diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 8bfa9cc94b341..f9ff133b6517d 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -19,10 +19,11 @@ use object::read::archive::ArchiveFile; use crate::core::build_steps::doc::DocumentationFormat; use crate::core::build_steps::tool::{self, Tool}; -use crate::core::build_steps::vendor::default_paths_to_vendor; +use crate::core::build_steps::vendor::{VENDOR_DIR, Vendor}; use crate::core::build_steps::{compile, llvm}; use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step}; use crate::core::config::TargetSelection; +use crate::utils::build_stamp::{self, BuildStamp}; use crate::utils::channel::{self, Info}; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{ @@ -47,7 +48,7 @@ fn should_build_extended_tool(builder: &Builder<'_>, tool: &str) -> bool { if !builder.config.extended { return false; } - builder.config.tools.as_ref().map_or(true, |tools| tools.contains(tool)) + builder.config.tools.as_ref().is_none_or(|tools| tools.contains(tool)) } #[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] @@ -410,7 +411,7 @@ impl Step for Rustc { .config .tools .as_ref() - .map_or(true, |tools| tools.iter().any(|tool| tool == "rustdoc")) + .is_none_or(|tools| tools.iter().any(|tool| tool == "rustdoc")) { let rustdoc = builder.rustdoc(compiler); builder.install(&rustdoc, &image.join("bin"), 0o755); @@ -503,14 +504,22 @@ impl Step for Rustc { // Debugger scripts builder.ensure(DebuggerScripts { sysroot: image.to_owned(), host }); - // Misc license info - let cp = |file: &str| { - builder.install(&builder.src.join(file), &image.join("share/doc/rust"), 0o644); + // HTML copyright files + let file_list = builder.ensure(super::run::GenerateCopyright); + for file in file_list { + builder.install(&file, &image.join("share/doc/rust"), 0o644); + } + + // README + builder.install(&builder.src.join("README.md"), &image.join("share/doc/rust"), 0o644); + + // The REUSE-managed license files + let license = |path: &Path| { + builder.install(path, &image.join("share/doc/rust/licenses"), 0o644); }; - cp("COPYRIGHT"); - cp("LICENSE-APACHE"); - cp("LICENSE-MIT"); - cp("README.md"); + for entry in t!(std::fs::read_dir(builder.src.join("LICENSES"))).flatten() { + license(&entry.path()); + } } } } @@ -573,7 +582,7 @@ impl Step for DebuggerScripts { fn skip_host_target_lib(builder: &Builder<'_>, compiler: Compiler) -> bool { // The only true set of target libraries came from the build triple, so // let's reduce redundant work by only producing archives from that host. - if compiler.host != builder.config.build { + if !builder.is_builder_target(&compiler.host) { builder.info("\tskipping, not a build host"); true } else { @@ -584,7 +593,7 @@ fn skip_host_target_lib(builder: &Builder<'_>, compiler: Compiler) -> bool { /// Check that all objects in rlibs for UEFI targets are COFF. This /// ensures that the C compiler isn't producing ELF objects, which would /// not link correctly with the COFF objects. -fn verify_uefi_rlib_format(builder: &Builder<'_>, target: TargetSelection, stamp: &Path) { +fn verify_uefi_rlib_format(builder: &Builder<'_>, target: TargetSelection, stamp: &BuildStamp) { if !target.ends_with("-uefi") { return; } @@ -615,7 +624,12 @@ fn verify_uefi_rlib_format(builder: &Builder<'_>, target: TargetSelection, stamp } /// Copy stamped files into an image's `target/lib` directory. -fn copy_target_libs(builder: &Builder<'_>, target: TargetSelection, image: &Path, stamp: &Path) { +fn copy_target_libs( + builder: &Builder<'_>, + target: TargetSelection, + image: &Path, + stamp: &BuildStamp, +) { let dst = image.join("lib/rustlib").join(target).join("lib"); let self_contained_dst = dst.join("self-contained"); t!(fs::create_dir_all(&dst)); @@ -623,7 +637,7 @@ fn copy_target_libs(builder: &Builder<'_>, target: TargetSelection, image: &Path for (path, dependency_type) in builder.read_stamp_file(stamp) { if dependency_type == DependencyType::TargetSelfContained { builder.copy_link(&path, &self_contained_dst.join(path.file_name().unwrap())); - } else if dependency_type == DependencyType::Target || builder.config.build == target { + } else if dependency_type == DependencyType::Target || builder.is_builder_target(&target) { builder.copy_link(&path, &dst.join(path.file_name().unwrap())); } } @@ -668,7 +682,7 @@ impl Step for Std { tarball.include_target_in_component_name(true); let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); - let stamp = compile::libstd_stamp(builder, compiler_to_use, target); + let stamp = build_stamp::libstd_stamp(builder, compiler_to_use, target); verify_uefi_rlib_format(builder, target, &stamp); copy_target_libs(builder, target, tarball.image_dir(), &stamp); @@ -718,7 +732,7 @@ impl Step for RustcDev { let tarball = Tarball::new(builder, "rustc-dev", &target.triple); let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); - let stamp = compile::librustc_stamp(builder, compiler_to_use, target); + let stamp = build_stamp::librustc_stamp(builder, compiler_to_use, target); copy_target_libs(builder, target, tarball.image_dir(), &stamp); let src_files = &["Cargo.lock"]; @@ -772,7 +786,7 @@ impl Step for Analysis { fn run(self, builder: &Builder<'_>) -> Option { let compiler = self.compiler; let target = self.target; - if compiler.host != builder.config.build { + if !builder.is_builder_target(&compiler.host) { return None; } @@ -980,22 +994,39 @@ impl Step for PlainSourceTarball { // This is the set of root paths which will become part of the source package let src_files = [ + // tidy-alphabetical-start + ".gitmodules", + "Cargo.lock", + "Cargo.toml", + "config.example.toml", + "configure", + "CONTRIBUTING.md", "COPYRIGHT", "LICENSE-APACHE", + "license-metadata.json", "LICENSE-MIT", - "CONTRIBUTING.md", "README.md", "RELEASES.md", - "configure", + "REUSE.toml", + "x", + "x.ps1", "x.py", - "config.example.toml", - "Cargo.toml", - "Cargo.lock", - ".gitmodules", + // tidy-alphabetical-end ]; - let src_dirs = ["src", "compiler", "library", "tests"]; + let src_dirs = ["src", "compiler", "library", "tests", "LICENSES"]; - copy_src_dirs(builder, &builder.src, &src_dirs, &[], plain_dst_src); + copy_src_dirs( + builder, + &builder.src, + &src_dirs, + &[ + // We don't currently use the GCC source code for building any official components, + // it is very big, and has unclear licensing implications due to being GPL licensed. + // We thus exclude it from the source tarball from now. + "src/gcc", + ], + plain_dst_src, + ); // Copy the files normally for item in &src_files { @@ -1019,19 +1050,6 @@ impl Step for PlainSourceTarball { if builder.config.dist_vendor { builder.require_and_update_all_submodules(); - // Vendor all Cargo dependencies - let mut cmd = command(&builder.initial_cargo); - cmd.arg("vendor").arg("--versioned-dirs"); - - for (p, _) in default_paths_to_vendor(builder) { - cmd.arg("--sync").arg(p); - } - - cmd - // Will read the libstd Cargo.toml which uses the unstable `public-dependency` feature. - .env("RUSTC_BOOTSTRAP", "1") - .current_dir(plain_dst_src); - // Vendor packages that are required by opt-dist to collect PGO profiles. let pkgs_for_pgo_training = build_helper::LLVM_PGO_CRATES .iter() @@ -1043,15 +1061,18 @@ impl Step for PlainSourceTarball { manifest_path.push("Cargo.toml"); manifest_path }); - for manifest_path in pkgs_for_pgo_training { - cmd.arg("--sync").arg(manifest_path); - } - let config = cmd.run_capture(builder).stdout(); + // Vendor all Cargo dependencies + let vendor = builder.ensure(Vendor { + sync_args: pkgs_for_pgo_training.collect(), + versioned_dirs: true, + root_dir: plain_dst_src.into(), + output_dir: VENDOR_DIR.into(), + }); let cargo_config_dir = plain_dst_src.join(".cargo"); builder.create_dir(&cargo_config_dir); - builder.create(&cargo_config_dir.join("config.toml"), &config); + builder.create(&cargo_config_dir.join("config.toml"), &vendor.config); } // Delete extraneous directories @@ -1592,15 +1613,9 @@ impl Step for Extended { prepare("cargo"); prepare("rust-std"); prepare("rust-analysis"); - - for tool in &[ - "clippy", - "rustfmt", - "rust-analyzer", - "rust-docs", - "miri", - "rustc-codegen-cranelift", - ] { + prepare("clippy"); + prepare("rust-analyzer"); + for tool in &["rust-docs", "miri", "rustc-codegen-cranelift"] { if built_tools.contains(tool) { prepare(tool); } @@ -1640,8 +1655,6 @@ impl Step for Extended { "rust-analyzer-preview".to_string() } else if name == "clippy" { "clippy-preview".to_string() - } else if name == "rustfmt" { - "rustfmt-preview".to_string() } else if name == "miri" { "miri-preview".to_string() } else if name == "rustc-codegen-cranelift" { @@ -1661,7 +1674,7 @@ impl Step for Extended { prepare("cargo"); prepare("rust-analysis"); prepare("rust-std"); - for tool in &["clippy", "rustfmt", "rust-analyzer", "rust-docs", "miri"] { + for tool in &["clippy", "rust-analyzer", "rust-docs", "miri"] { if built_tools.contains(tool) { prepare(tool); } @@ -1779,24 +1792,6 @@ impl Step for Extended { .arg(etc.join("msi/remove-duplicates.xsl")) .run(builder); } - if built_tools.contains("rustfmt") { - command(&heat) - .current_dir(&exe) - .arg("dir") - .arg("rustfmt") - .args(heat_flags) - .arg("-cg") - .arg("RustFmtGroup") - .arg("-dr") - .arg("RustFmt") - .arg("-var") - .arg("var.RustFmtDir") - .arg("-out") - .arg(exe.join("RustFmtGroup.wxs")) - .arg("-t") - .arg(etc.join("msi/remove-duplicates.xsl")) - .run(builder); - } if built_tools.contains("miri") { command(&heat) .current_dir(&exe) @@ -1868,9 +1863,6 @@ impl Step for Extended { if built_tools.contains("clippy") { cmd.arg("-dClippyDir=clippy"); } - if built_tools.contains("rustfmt") { - cmd.arg("-dRustFmtDir=rustfmt"); - } if built_tools.contains("rust-docs") { cmd.arg("-dDocsDir=rust-docs"); } @@ -1897,9 +1889,6 @@ impl Step for Extended { if built_tools.contains("clippy") { candle("ClippyGroup.wxs".as_ref()); } - if built_tools.contains("rustfmt") { - candle("RustFmtGroup.wxs".as_ref()); - } if built_tools.contains("miri") { candle("MiriGroup.wxs".as_ref()); } @@ -1938,9 +1927,6 @@ impl Step for Extended { if built_tools.contains("clippy") { cmd.arg("ClippyGroup.wixobj"); } - if built_tools.contains("rustfmt") { - cmd.arg("RustFmtGroup.wixobj"); - } if built_tools.contains("miri") { cmd.arg("MiriGroup.wixobj"); } diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 1e9f7cbd9b4ca..dedcc139ae198 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -18,7 +18,7 @@ use crate::core::builder::{ self, Alias, Builder, Compiler, Kind, RunConfig, ShouldRun, Step, crate_description, }; use crate::core::config::{Config, TargetSelection}; -use crate::helpers::{is_path_in_submodule, symlink_dir, t, up_to_date}; +use crate::helpers::{submodule_path_of, symlink_dir, t, up_to_date}; macro_rules! book { ($($name:ident, $path:expr, $book_name:expr, $lang:expr ;)+) => { @@ -44,8 +44,8 @@ macro_rules! book { } fn run(self, builder: &Builder<'_>) { - if is_path_in_submodule(&builder, $path) { - builder.require_submodule($path, None); + if let Some(submodule_path) = submodule_path_of(&builder, $path) { + builder.require_submodule(&submodule_path, None) } builder.ensure(RustbookSrc { @@ -65,8 +65,6 @@ macro_rules! book { // NOTE: When adding a book here, make sure to ALSO build the book by // adding a build step in `src/bootstrap/code/builder/mod.rs`! // NOTE: Make sure to add the corresponding submodule when adding a new book. -// FIXME: Make checking for a submodule automatic somehow (maybe by having a list of all submodules -// and checking against it?). book!( CargoBook, "src/tools/cargo/src/doc", "cargo", &[]; ClippyBook, "src/tools/clippy/book", "clippy", &[]; @@ -574,7 +572,10 @@ impl Step for Std { fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let builder = run.builder; - run.crate_or_deps("sysroot").path("library").default_condition(builder.config.docs) + run.crate_or_deps("sysroot") + .path("library") + .alias("core") + .default_condition(builder.config.docs) } fn make_run(run: RunConfig<'_>) { @@ -829,7 +830,8 @@ impl Step for Rustc { cargo.rustdocflag("--show-type-layout"); // FIXME: `--generate-link-to-definition` tries to resolve cfged out code // see https://github.com/rust-lang/rust/pull/122066#issuecomment-1983049222 - // cargo.rustdocflag("--generate-link-to-definition"); + // If there is any bug, please comment out the next line. + cargo.rustdocflag("--generate-link-to-definition"); compile::rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates); cargo.arg("-Zskip-rustdoc-fingerprint"); @@ -930,9 +932,9 @@ macro_rules! tool_doc { fn run(self, builder: &Builder<'_>) { let mut source_type = SourceType::InTree; - if is_path_in_submodule(&builder, $path) { + if let Some(submodule_path) = submodule_path_of(&builder, $path) { source_type = SourceType::Submodule; - builder.require_submodule($path, None); + builder.require_submodule(&submodule_path, None); } let stage = builder.top_stage; diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs index f15e0f38e6921..c7eafadee2df3 100644 --- a/src/bootstrap/src/core/build_steps/format.rs +++ b/src/bootstrap/src/core/build_steps/format.rs @@ -11,8 +11,9 @@ use build_helper::git::get_git_modified_files; use ignore::WalkBuilder; use crate::core::builder::Builder; +use crate::utils::build_stamp::BuildStamp; use crate::utils::exec::command; -use crate::utils::helpers::{self, program_out_of_date, t}; +use crate::utils::helpers::{self, t}; #[must_use] enum RustfmtStatus { @@ -55,8 +56,8 @@ fn rustfmt( } } -fn get_rustfmt_version(build: &Builder<'_>) -> Option<(String, PathBuf)> { - let stamp_file = build.out.join("rustfmt.stamp"); +fn get_rustfmt_version(build: &Builder<'_>) -> Option<(String, BuildStamp)> { + let stamp_file = BuildStamp::new(&build.out).with_prefix("rustfmt"); let mut cmd = command(build.initial_rustfmt()?); cmd.arg("--version"); @@ -73,7 +74,7 @@ fn verify_rustfmt_version(build: &Builder<'_>) -> bool { let Some((version, stamp_file)) = get_rustfmt_version(build) else { return false; }; - !program_out_of_date(&stamp_file, &version) + stamp_file.add_stamp(version).is_up_to_date() } /// Updates the last rustfmt version used. @@ -81,7 +82,7 @@ fn update_rustfmt_version(build: &Builder<'_>) { let Some((version, stamp_file)) = get_rustfmt_version(build) else { return; }; - t!(std::fs::write(stamp_file, version)) + t!(std::fs::write(stamp_file.path(), version)) } /// Returns the Rust files modified between the `merge-base` of HEAD and @@ -114,6 +115,9 @@ fn print_paths(verb: &str, adjective: Option<&str>, paths: &[String]) { } else { println!("fmt: {verb} {len} {adjective}files"); } + if len > 1000 && !CiEnv::is_ci() { + println!("hint: if this number seems too high, try running `git fetch origin master`"); + } } pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) { diff --git a/src/bootstrap/src/core/build_steps/gcc.rs b/src/bootstrap/src/core/build_steps/gcc.rs index b950bec11fd01..98b8635132b0d 100644 --- a/src/bootstrap/src/core/build_steps/gcc.rs +++ b/src/bootstrap/src/core/build_steps/gcc.rs @@ -12,14 +12,17 @@ use std::fs; use std::path::PathBuf; use std::sync::OnceLock; +use build_helper::ci::CiEnv; + +use crate::Kind; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::core::config::TargetSelection; +use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash}; use crate::utils::exec::command; -use crate::utils::helpers::{self, HashStamp, t}; -use crate::{Kind, generate_smart_stamp_hash}; +use crate::utils::helpers::{self, t}; pub struct Meta { - stamp: HashStamp, + stamp: BuildStamp, out_dir: PathBuf, install_dir: PathBuf, root: PathBuf, @@ -54,18 +57,17 @@ pub fn prebuilt_gcc_config(builder: &Builder<'_>, target: TargetSelection) -> Gc ) }); - let stamp = out_dir.join("gcc-finished-building"); - let stamp = HashStamp::new(stamp, Some(smart_stamp_hash)); + let stamp = BuildStamp::new(&out_dir).with_prefix("gcc").add_stamp(smart_stamp_hash); - if stamp.is_done() { - if stamp.hash.is_none() { + if stamp.is_up_to_date() { + if stamp.stamp().is_empty() { builder.info( "Could not determine the GCC submodule commit hash. \ Assuming that an GCC rebuild is not necessary.", ); builder.info(&format!( "To force GCC to rebuild, remove the file `{}`", - stamp.path.display() + stamp.path().display() )); } return GccBuildStatus::AlreadyBuilt; @@ -112,16 +114,60 @@ impl Step for Gcc { return true; } - command(root.join("contrib/download_prerequisites")).current_dir(&root).run(builder); - command(root.join("configure")) + // GCC creates files (e.g. symlinks to the downloaded dependencies) + // in the source directory, which does not work with our CI setup, where we mount + // source directories as read-only on Linux. + // Therefore, as a part of the build in CI, we first copy the whole source directory + // to the build directory, and perform the build from there. + let src_dir = if CiEnv::is_ci() { + let src_dir = builder.gcc_out(target).join("src"); + if src_dir.exists() { + builder.remove_dir(&src_dir); + } + builder.create_dir(&src_dir); + builder.cp_link_r(&root, &src_dir); + src_dir + } else { + root + }; + + command(src_dir.join("contrib/download_prerequisites")).current_dir(&src_dir).run(builder); + let mut configure_cmd = command(src_dir.join("configure")); + configure_cmd .current_dir(&out_dir) + // On CI, we compile GCC with Clang. + // The -Wno-everything flag is needed to make GCC compile with Clang 19. + // `-g -O2` are the default flags that are otherwise used by Make. + // FIXME(kobzol): change the flags once we have [gcc] configuration in config.toml. + .env("CXXFLAGS", "-Wno-everything -g -O2") + .env("CFLAGS", "-Wno-everything -g -O2") .arg("--enable-host-shared") .arg("--enable-languages=jit") .arg("--enable-checking=release") .arg("--disable-bootstrap") .arg("--disable-multilib") - .arg(format!("--prefix={}", install_dir.display())) - .run(builder); + .arg(format!("--prefix={}", install_dir.display())); + let cc = builder.build.cc(target).display().to_string(); + let cc = builder + .build + .config + .ccache + .as_ref() + .map_or_else(|| cc.clone(), |ccache| format!("{ccache} {cc}")); + configure_cmd.env("CC", cc); + + if let Ok(ref cxx) = builder.build.cxx(target) { + let cxx = cxx.display().to_string(); + let cxx = builder + .build + .config + .ccache + .as_ref() + .map_or_else(|| cxx.clone(), |ccache| format!("{ccache} {cxx}")); + configure_cmd.env("CXX", cxx); + } + configure_cmd.run(builder); + command("make").current_dir(&out_dir).arg(format!("-j{}", builder.jobs())).run(builder); command("make").current_dir(&out_dir).arg("install").run(builder); diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs index b6862c2d5c4a9..4f96d67a5bd89 100644 --- a/src/bootstrap/src/core/build_steps/install.rs +++ b/src/bootstrap/src/core/build_steps/install.rs @@ -307,7 +307,7 @@ impl Step for Src { fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let config = &run.builder.config; - let cond = config.extended && config.tools.as_ref().map_or(true, |t| t.contains("src")); + let cond = config.extended && config.tools.as_ref().is_none_or(|t| t.contains("src")); run.path("src").default_condition(cond) } diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index be5b405705162..18da0e8252b90 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -8,23 +8,23 @@ //! LLVM and compiler-rt are essentially just wired up to everything else to //! ensure that they're always in place if needed. -use std::env; use std::env::consts::EXE_EXTENSION; use std::ffi::{OsStr, OsString}; -use std::fs::{self, File}; use std::path::{Path, PathBuf}; use std::sync::OnceLock; +use std::{env, fs}; use build_helper::ci::CiEnv; use build_helper::git::get_closest_merge_commit; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::core::config::{Config, TargetSelection}; +use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash}; use crate::utils::exec::command; use crate::utils::helpers::{ - self, HashStamp, exe, get_clang_cl_resource_dir, t, unhashed_basename, up_to_date, + self, exe, get_clang_cl_resource_dir, t, unhashed_basename, up_to_date, }; -use crate::{CLang, GitRepo, Kind, generate_smart_stamp_hash}; +use crate::{CLang, GitRepo, Kind}; #[derive(Clone)] pub struct LlvmResult { @@ -36,7 +36,7 @@ pub struct LlvmResult { } pub struct Meta { - stamp: HashStamp, + stamp: BuildStamp, res: LlvmResult, out_dir: PathBuf, root: String, @@ -54,6 +54,14 @@ impl LlvmBuildStatus { LlvmBuildStatus::ShouldBuild(_) => true, } } + + #[cfg(test)] + pub fn llvm_result(&self) -> &LlvmResult { + match self { + LlvmBuildStatus::AlreadyBuilt(res) => res, + LlvmBuildStatus::ShouldBuild(meta) => &meta.res, + } + } } /// Linker flags to pass to LLVM's CMake invocation. @@ -120,9 +128,19 @@ pub fn prebuilt_llvm_config( let root = "src/llvm-project/llvm"; let out_dir = builder.llvm_out(target); - let mut llvm_config_ret_dir = builder.llvm_out(builder.config.build); - llvm_config_ret_dir.push("bin"); - let build_llvm_config = llvm_config_ret_dir.join(exe("llvm-config", builder.config.build)); + let build_llvm_config = if let Some(build_llvm_config) = builder + .config + .target_config + .get(&builder.config.build) + .and_then(|config| config.llvm_config.clone()) + { + build_llvm_config + } else { + let mut llvm_config_ret_dir = builder.llvm_out(builder.config.build); + llvm_config_ret_dir.push("bin"); + llvm_config_ret_dir.join(exe("llvm-config", builder.config.build)) + }; + let llvm_cmake_dir = out_dir.join("lib/cmake/llvm"); let res = LlvmResult { llvm_config: build_llvm_config, llvm_cmake_dir }; @@ -135,18 +153,17 @@ pub fn prebuilt_llvm_config( ) }); - let stamp = out_dir.join("llvm-finished-building"); - let stamp = HashStamp::new(stamp, Some(smart_stamp_hash)); + let stamp = BuildStamp::new(&out_dir).with_prefix("llvm").add_stamp(smart_stamp_hash); - if stamp.is_done() { - if stamp.hash.is_none() { + if stamp.is_up_to_date() { + if stamp.stamp().is_empty() { builder.info( "Could not determine the LLVM submodule commit hash. \ Assuming that an LLVM rebuild is not necessary.", ); builder.info(&format!( "To force LLVM to rebuild, remove the file `{}`", - stamp.path.display() + stamp.path().display() )); } return LlvmBuildStatus::AlreadyBuilt(res); @@ -158,12 +175,16 @@ pub fn prebuilt_llvm_config( /// This retrieves the LLVM sha we *want* to use, according to git history. pub(crate) fn detect_llvm_sha(config: &Config, is_git: bool) -> String { let llvm_sha = if is_git { - get_closest_merge_commit(Some(&config.src), &config.git_config(), &[ - config.src.join("src/llvm-project"), - config.src.join("src/bootstrap/download-ci-llvm-stamp"), - // the LLVM shared object file is named `LLVM-12-rust-{version}-nightly` - config.src.join("src/version"), - ]) + get_closest_merge_commit( + Some(&config.src), + &config.git_config(), + &[ + config.src.join("src/llvm-project"), + config.src.join("src/bootstrap/download-ci-llvm-stamp"), + // the LLVM shared object file is named `LLVM-12-rust-{version}-nightly` + config.src.join("src/version"), + ], + ) .unwrap() } else if let Some(info) = crate::utils::channel::read_commit_info_file(&config.src) { info.sha.trim().to_owned() @@ -332,7 +353,7 @@ impl Step for Llvm { let llvm_targets = match &builder.config.llvm_targets { Some(s) => s, None => { - "AArch64;ARM;BPF;Hexagon;LoongArch;MSP430;Mips;NVPTX;PowerPC;RISCV;\ + "AArch64;AMDGPU;ARM;BPF;Hexagon;LoongArch;MSP430;Mips;NVPTX;PowerPC;RISCV;\ Sparc;SystemZ;WebAssembly;X86" } }; @@ -495,7 +516,7 @@ impl Step for Llvm { } // https://llvm.org/docs/HowToCrossCompileLLVM.html - if target != builder.config.build { + if !builder.is_builder_target(&target) { let LlvmResult { llvm_config, .. } = builder.ensure(Llvm { target: builder.config.build }); if !builder.config.dry_run() { @@ -550,10 +571,7 @@ impl Step for Llvm { // Helper to find the name of LLVM's shared library on darwin and linux. let find_llvm_lib_name = |extension| { - let version = - command(&res.llvm_config).arg("--version").run_capture_stdout(builder).stdout(); - let major = version.split('.').next().unwrap(); - + let major = get_llvm_version_major(builder, &res.llvm_config); match &llvm_version_suffix { Some(version_suffix) => format!("libLLVM-{major}{version_suffix}.{extension}"), None => format!("libLLVM-{major}.{extension}"), @@ -603,12 +621,22 @@ impl Step for Llvm { } } +pub fn get_llvm_version(builder: &Builder<'_>, llvm_config: &Path) -> String { + command(llvm_config).arg("--version").run_capture_stdout(builder).stdout().trim().to_owned() +} + +pub fn get_llvm_version_major(builder: &Builder<'_>, llvm_config: &Path) -> u8 { + let version = get_llvm_version(builder, llvm_config); + let major_str = version.split_once('.').expect("Failed to parse LLVM version").0; + major_str.parse().unwrap() +} + fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) { if builder.config.dry_run() { return; } - let version = command(llvm_config).arg("--version").run_capture_stdout(builder).stdout(); + let version = get_llvm_version(builder, llvm_config); let mut parts = version.split('.').take(2).filter_map(|s| s.parse::().ok()); if let (Some(major), Some(_minor)) = (parts.next(), parts.next()) { if major >= 18 { @@ -640,7 +668,7 @@ fn configure_cmake( } cfg.target(&target.triple).host(&builder.config.build.triple); - if target != builder.config.build { + if !builder.is_builder_target(&target) { cfg.define("CMAKE_CROSSCOMPILING", "True"); if target.contains("netbsd") { @@ -758,9 +786,15 @@ fn configure_cmake( } cfg.build_arg("-j").build_arg(builder.jobs().to_string()); + // FIXME(madsmtm): Allow `cmake-rs` to select flags by itself by passing + // our flags via `.cflag`/`.cxxflag` instead. + // + // Needs `suppressed_compiler_flag_prefixes` to be gone, and hence + // https://github.com/llvm/llvm-project/issues/88780 to be fixed. let mut cflags: OsString = builder - .cflags(target, GitRepo::Llvm, CLang::C) + .cc_handled_clags(target, CLang::C) .into_iter() + .chain(builder.cc_unhandled_cflags(target, GitRepo::Llvm, CLang::C)) .filter(|flag| { !suppressed_compiler_flag_prefixes .iter() @@ -779,8 +813,9 @@ fn configure_cmake( } cfg.define("CMAKE_C_FLAGS", cflags); let mut cxxflags: OsString = builder - .cflags(target, GitRepo::Llvm, CLang::Cxx) + .cc_handled_clags(target, CLang::Cxx) .into_iter() + .chain(builder.cc_unhandled_cflags(target, GitRepo::Llvm, CLang::Cxx)) .filter(|flag| { !suppressed_compiler_flag_prefixes .iter() @@ -922,18 +957,17 @@ impl Step for Enzyme { }); let out_dir = builder.enzyme_out(target); - let stamp = out_dir.join("enzyme-finished-building"); - let stamp = HashStamp::new(stamp, Some(smart_stamp_hash)); + let stamp = BuildStamp::new(&out_dir).with_prefix("enzyme").add_stamp(smart_stamp_hash); - if stamp.is_done() { - if stamp.hash.is_none() { + if stamp.is_up_to_date() { + if stamp.stamp().is_empty() { builder.info( "Could not determine the Enzyme submodule commit hash. \ Assuming that an Enzyme rebuild is not necessary.", ); builder.info(&format!( "To force Enzyme to rebuild, remove the file `{}`", - stamp.path.display() + stamp.path().display() )); } return out_dir; @@ -966,6 +1000,7 @@ impl Step for Enzyme { .env("LLVM_CONFIG_REAL", &llvm_config) .define("LLVM_ENABLE_ASSERTIONS", "ON") .define("ENZYME_EXTERNAL_SHARED_LIB", "ON") + .define("ENZYME_RUNPASS", "ON") .define("LLVM_DIR", builder.llvm_out(target)); cfg.build(); @@ -1016,8 +1051,9 @@ impl Step for Lld { } let out_dir = builder.lld_out(target); - let done_stamp = out_dir.join("lld-finished-building"); - if done_stamp.exists() { + + let lld_stamp = BuildStamp::new(&out_dir).with_prefix("lld"); + if lld_stamp.path().exists() { return out_dir; } @@ -1082,7 +1118,7 @@ impl Step for Lld { .define("LLVM_CMAKE_DIR", llvm_cmake_dir) .define("LLVM_INCLUDE_TESTS", "OFF"); - if target != builder.config.build { + if !builder.is_builder_target(&target) { // Use the host llvm-tblgen binary. cfg.define( "LLVM_TABLEGEN_EXE", @@ -1092,7 +1128,7 @@ impl Step for Lld { cfg.build(); - t!(File::create(&done_stamp)); + t!(lld_stamp.write()); out_dir } } @@ -1122,25 +1158,32 @@ impl Step for Sanitizers { let out_dir = builder.native_dir(self.target).join("sanitizers"); let runtimes = supported_sanitizers(&out_dir, self.target, &builder.config.channel); - if runtimes.is_empty() { + + if builder.config.dry_run() || runtimes.is_empty() { return runtimes; } let LlvmResult { llvm_config, .. } = builder.ensure(Llvm { target: builder.config.build }); - if builder.config.dry_run() { - return runtimes; - } - let stamp = out_dir.join("sanitizers-finished-building"); - let stamp = HashStamp::new(stamp, builder.in_tree_llvm_info.sha()); + static STAMP_HASH_MEMO: OnceLock = OnceLock::new(); + let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| { + generate_smart_stamp_hash( + builder, + &builder.config.src.join("src/llvm-project/compiler-rt"), + builder.in_tree_llvm_info.sha().unwrap_or_default(), + ) + }); + + let stamp = BuildStamp::new(&out_dir).with_prefix("sanitizers").add_stamp(smart_stamp_hash); - if stamp.is_done() { - if stamp.hash.is_none() { + if stamp.is_up_to_date() { + if stamp.stamp().is_empty() { builder.info(&format!( "Rebuild sanitizers by removing the file `{}`", - stamp.path.display() + stamp.path().display() )); } + return runtimes; } @@ -1289,7 +1332,9 @@ impl Step for CrtBeginEnd { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(CrtBeginEnd { target: run.target }); + if run.target.needs_crt_begin_end() { + run.builder.ensure(CrtBeginEnd { target: run.target }); + } } /// Build crtbegin.o/crtend.o for musl target. @@ -1332,7 +1377,7 @@ impl Step for CrtBeginEnd { .file(crtbegin_src) .file(crtend_src); - // Those flags are defined in src/llvm-project/compiler-rt/lib/crt/CMakeLists.txt + // Those flags are defined in src/llvm-project/compiler-rt/lib/builtins/CMakeLists.txt // Currently only consumer of those objects is musl, which use .init_array/.fini_array // instead of .ctors/.dtors cfg.flag("-std=c11") diff --git a/src/bootstrap/src/core/build_steps/perf.rs b/src/bootstrap/src/core/build_steps/perf.rs index 5b83080a3260f..98c63a41e768b 100644 --- a/src/bootstrap/src/core/build_steps/perf.rs +++ b/src/bootstrap/src/core/build_steps/perf.rs @@ -1,35 +1,231 @@ +use std::fmt::{Display, Formatter}; + use crate::core::build_steps::compile::{Std, Sysroot}; -use crate::core::build_steps::tool::{RustcPerf, Tool}; +use crate::core::build_steps::tool::{RustcPerf, Rustdoc}; use crate::core::builder::Builder; use crate::core::config::DebuginfoLevel; +use crate::utils::exec::{BootstrapCommand, command}; + +#[derive(Debug, Clone, clap::Parser)] +pub struct PerfArgs { + #[clap(subcommand)] + cmd: PerfCommand, +} + +#[derive(Debug, Clone, clap::Parser)] +enum PerfCommand { + /// Run `profile_local eprintln`. + /// This executes the compiler on the given benchmarks and stores its stderr output. + Eprintln { + #[clap(flatten)] + opts: SharedOpts, + }, + /// Run `profile_local samply` + /// This executes the compiler on the given benchmarks and profiles it with `samply`. + /// You need to install `samply`, e.g. using `cargo install samply`. + Samply { + #[clap(flatten)] + opts: SharedOpts, + }, + /// Run `profile_local cachegrind`. + /// This executes the compiler on the given benchmarks under `Cachegrind`. + Cachegrind { + #[clap(flatten)] + opts: SharedOpts, + }, + /// Run compile benchmarks with a locally built compiler. + Benchmark { + /// Identifier to associate benchmark results with + #[clap(name = "benchmark-id")] + id: String, + + #[clap(flatten)] + opts: SharedOpts, + }, + /// Compare the results of two previously executed benchmark runs. + Compare { + /// The name of the base artifact to be compared. + base: String, + + /// The name of the modified artifact to be compared. + modified: String, + }, +} + +impl PerfCommand { + fn shared_opts(&self) -> Option<&SharedOpts> { + match self { + PerfCommand::Eprintln { opts, .. } + | PerfCommand::Samply { opts, .. } + | PerfCommand::Cachegrind { opts, .. } + | PerfCommand::Benchmark { opts, .. } => Some(opts), + PerfCommand::Compare { .. } => None, + } + } +} + +#[derive(Debug, Clone, clap::Parser)] +struct SharedOpts { + /// Select the benchmarks that you want to run (separated by commas). + /// If unspecified, all benchmarks will be executed. + #[clap(long, global = true, value_delimiter = ',')] + include: Vec, + + /// Select the benchmarks matching a prefix in this comma-separated list that you don't want to run. + #[clap(long, global = true, value_delimiter = ',')] + exclude: Vec, + + /// Select the scenarios that should be benchmarked. + #[clap( + long, + global = true, + value_delimiter = ',', + default_value = "Full,IncrFull,IncrUnchanged,IncrPatched" + )] + scenarios: Vec, + /// Select the profiles that should be benchmarked. + #[clap(long, global = true, value_delimiter = ',', default_value = "Check,Debug,Opt")] + profiles: Vec, +} + +#[derive(Clone, Copy, Debug, PartialEq, clap::ValueEnum)] +#[value(rename_all = "PascalCase")] +pub enum Profile { + Check, + Debug, + Doc, + Opt, + Clippy, +} + +impl Display for Profile { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let name = match self { + Profile::Check => "Check", + Profile::Debug => "Debug", + Profile::Doc => "Doc", + Profile::Opt => "Opt", + Profile::Clippy => "Clippy", + }; + f.write_str(name) + } +} + +#[derive(Clone, Copy, Debug, clap::ValueEnum)] +#[value(rename_all = "PascalCase")] +pub enum Scenario { + Full, + IncrFull, + IncrUnchanged, + IncrPatched, +} + +impl Display for Scenario { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let name = match self { + Scenario::Full => "Full", + Scenario::IncrFull => "IncrFull", + Scenario::IncrUnchanged => "IncrUnchanged", + Scenario::IncrPatched => "IncrPatched", + }; + f.write_str(name) + } +} /// Performs profiling using `rustc-perf` on a built version of the compiler. -pub fn perf(builder: &Builder<'_>) { +pub fn perf(builder: &Builder<'_>, args: &PerfArgs) { let collector = builder.ensure(RustcPerf { compiler: builder.compiler(0, builder.config.build), target: builder.config.build, }); - if builder.build.config.rust_debuginfo_level_rustc == DebuginfoLevel::None { + let is_profiling = match &args.cmd { + PerfCommand::Eprintln { .. } + | PerfCommand::Samply { .. } + | PerfCommand::Cachegrind { .. } => true, + PerfCommand::Benchmark { .. } | PerfCommand::Compare { .. } => false, + }; + if is_profiling && builder.build.config.rust_debuginfo_level_rustc == DebuginfoLevel::None { builder.info(r#"WARNING: You are compiling rustc without debuginfo, this will make profiling less useful. Consider setting `rust.debuginfo-level = 1` in `config.toml`."#); } let compiler = builder.compiler(builder.top_stage, builder.config.build); builder.ensure(Std::new(compiler, builder.config.build)); + + if let Some(opts) = args.cmd.shared_opts() { + if opts.profiles.contains(&Profile::Doc) { + builder.ensure(Rustdoc { compiler }); + } + } + let sysroot = builder.ensure(Sysroot::new(compiler)); let rustc = sysroot.join("bin/rustc"); let rustc_perf_dir = builder.build.tempdir().join("rustc-perf"); - let profile_results_dir = rustc_perf_dir.join("results"); + let results_dir = rustc_perf_dir.join("results"); + builder.create_dir(&results_dir); + + let mut cmd = command(collector); + + // We need to set the working directory to `src/tools/rustc-perf`, so that it can find the directory + // with compile-time benchmarks. + cmd.current_dir(builder.src.join("src/tools/rustc-perf")); + + let db_path = results_dir.join("results.db"); + + match &args.cmd { + PerfCommand::Eprintln { opts } + | PerfCommand::Samply { opts } + | PerfCommand::Cachegrind { opts } => { + cmd.arg("profile_local"); + cmd.arg(match &args.cmd { + PerfCommand::Eprintln { .. } => "eprintln", + PerfCommand::Samply { .. } => "samply", + PerfCommand::Cachegrind { .. } => "cachegrind", + _ => unreachable!(), + }); - // We need to take args passed after `--` and pass them to `rustc-perf-wrapper` - let args = std::env::args().skip_while(|a| a != "--").skip(1); + cmd.arg("--out-dir").arg(&results_dir); + cmd.arg(rustc); - let mut cmd = builder.tool_cmd(Tool::RustcPerfWrapper); - cmd.env("RUSTC_REAL", rustc) - .env("PERF_COLLECTOR", collector) - .env("PERF_RESULT_DIR", profile_results_dir) - .args(args); - cmd.run(builder); + apply_shared_opts(&mut cmd, opts); + cmd.run(builder); + + println!("You can find the results at `{}`", &results_dir.display()); + } + PerfCommand::Benchmark { id, opts } => { + cmd.arg("bench_local"); + cmd.arg("--db").arg(&db_path); + cmd.arg("--id").arg(id); + cmd.arg(rustc); + + apply_shared_opts(&mut cmd, opts); + cmd.run(builder); + } + PerfCommand::Compare { base, modified } => { + cmd.arg("bench_cmp"); + cmd.arg("--db").arg(&db_path); + cmd.arg(base).arg(modified); + + cmd.run(builder); + } + } +} + +fn apply_shared_opts(cmd: &mut BootstrapCommand, opts: &SharedOpts) { + if !opts.include.is_empty() { + cmd.arg("--include").arg(opts.include.join(",")); + } + if !opts.exclude.is_empty() { + cmd.arg("--exclude").arg(opts.exclude.join(",")); + } + if !opts.profiles.is_empty() { + cmd.arg("--profiles") + .arg(opts.profiles.iter().map(|p| p.to_string()).collect::>().join(",")); + } + if !opts.scenarios.is_empty() { + cmd.arg("--scenarios") + .arg(opts.scenarios.iter().map(|p| p.to_string()).collect::>().join(",")); + } } diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index 54aad08855259..2b17e02cae5a4 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -9,6 +9,7 @@ use crate::Mode; use crate::core::build_steps::dist::distdir; use crate::core::build_steps::test; use crate::core::build_steps::tool::{self, SourceType, Tool}; +use crate::core::build_steps::vendor::{Vendor, default_paths_to_vendor}; use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step}; use crate::core::config::TargetSelection; use crate::core::config::flags::get_completion; @@ -196,7 +197,7 @@ impl Step for CollectLicenseMetadata { pub struct GenerateCopyright; impl Step for GenerateCopyright { - type Output = PathBuf; + type Output = Vec; const ONLY_HOSTS: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -212,15 +213,46 @@ impl Step for GenerateCopyright { let dest = builder.out.join("COPYRIGHT.html"); let dest_libstd = builder.out.join("COPYRIGHT-library.html"); + let paths_to_vendor = default_paths_to_vendor(builder); + for (_, submodules) in &paths_to_vendor { + for submodule in submodules { + builder.build.require_submodule(submodule, None); + } + } + let cargo_manifests = paths_to_vendor + .into_iter() + .map(|(path, _submodules)| path.to_str().unwrap().to_string()) + .inspect(|path| assert!(!path.contains(','), "{path} contains a comma in its name")) + .collect::>() + .join(","); + + let vendored_sources = if let Some(path) = builder.vendored_crates_path() { + path + } else { + let cache_dir = builder.out.join("tmp").join("generate-copyright-vendor"); + builder.ensure(Vendor { + sync_args: Vec::new(), + versioned_dirs: true, + root_dir: builder.src.clone(), + output_dir: cache_dir.clone(), + }); + cache_dir + }; + let mut cmd = builder.tool_cmd(Tool::GenerateCopyright); + cmd.env("CARGO_MANIFESTS", &cargo_manifests); cmd.env("LICENSE_METADATA", &license_metadata); cmd.env("DEST", &dest); cmd.env("DEST_LIBSTD", &dest_libstd); - cmd.env("OUT_DIR", &builder.out); + cmd.env("SRC_DIR", &builder.src); + cmd.env("VENDOR_DIR", &vendored_sources); cmd.env("CARGO", &builder.initial_cargo); + // it is important that generate-copyright runs from the root of the + // source tree, because it uses relative paths + cmd.current_dir(&builder.src); cmd.run(builder); - dest + vec![dest, dest_libstd] } } @@ -308,3 +340,34 @@ impl Step for UnicodeTableGenerator { cmd.run(builder); } } + +#[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] +pub struct FeaturesStatusDump; + +impl Step for FeaturesStatusDump { + type Output = (); + const ONLY_HOSTS: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("src/tools/features-status-dump") + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(FeaturesStatusDump); + } + + fn run(self, builder: &Builder<'_>) { + let mut cmd = builder.tool_cmd(Tool::FeaturesStatusDump); + + cmd.arg("--library-path"); + cmd.arg(builder.src.join("library")); + + cmd.arg("--compiler-path"); + cmd.arg(builder.src.join("compiler")); + + cmd.arg("--output-path"); + cmd.arg(builder.out.join("features-status-dump.json")); + + cmd.run(builder); + } +} diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 84d09bbc2e092..26ed0e5deaa05 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -12,6 +12,7 @@ use clap_complete::shells; use crate::core::build_steps::compile::run_cargo; use crate::core::build_steps::doc::DocumentationFormat; +use crate::core::build_steps::llvm::get_llvm_version; use crate::core::build_steps::synthetic_targets::MirOptPanicAbortSyntheticTarget; use crate::core::build_steps::tool::{self, SourceType, Tool}; use crate::core::build_steps::toolstate::ToolState; @@ -21,16 +22,18 @@ use crate::core::builder::{ }; use crate::core::config::TargetSelection; use crate::core::config::flags::{Subcommand, get_completion}; +use crate::utils::build_stamp::{self, BuildStamp}; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{ - self, LldThreads, add_link_lib_path, add_rustdoc_cargo_linker_args, dylib_path, dylib_path_var, - linker_args, linker_flags, t, target_supports_cranelift_backend, up_to_date, + self, LldThreads, add_rustdoc_cargo_linker_args, dylib_path, dylib_path_var, linker_args, + linker_flags, t, target_supports_cranelift_backend, up_to_date, }; use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests}; -use crate::{CLang, DocTests, GitRepo, Mode, envify}; +use crate::{CLang, DocTests, GitRepo, Mode, PathSet, envify}; const ADB_TEST_DIR: &str = "/data/local/tmp/work"; +/// Runs `cargo test` on various internal tools used by bootstrap. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateBootstrap { path: PathBuf, @@ -43,13 +46,21 @@ impl Step for CrateBootstrap { const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + // This step is responsible for several different tool paths. By default + // it will test all of them, but requesting specific tools on the + // command-line (e.g. `./x test suggest-tests`) will test only the + // specified tools. run.path("src/tools/jsondoclint") .path("src/tools/suggest-tests") .path("src/tools/replace-version-placeholder") + // We want `./x test tidy` to _run_ the tidy tool, not its tests. + // So we need a separate alias to test the tidy tool itself. .alias("tidyselftest") } fn make_run(run: RunConfig<'_>) { + // Create and ensure a separate instance of this step for each path + // that was selected on the command-line (or selected by default). for path in run.paths { let path = path.assert_single_path().path.clone(); run.builder.ensure(CrateBootstrap { host: run.target, path }); @@ -60,6 +71,8 @@ impl Step for CrateBootstrap { let bootstrap_host = builder.config.build; let compiler = builder.compiler(0, bootstrap_host); let mut path = self.path.to_str().unwrap(); + + // Map alias `tidyselftest` back to the actual crate path of tidy. if path == "tidyselftest" { path = "src/tools/tidy"; } @@ -74,8 +87,9 @@ impl Step for CrateBootstrap { SourceType::InTree, &[], ); + let crate_name = path.rsplit_once('/').unwrap().1; - run_cargo_test(cargo, &[], &[], crate_name, crate_name, compiler, bootstrap_host, builder); + run_cargo_test(cargo, &[], &[], crate_name, crate_name, bootstrap_host, builder); } } @@ -131,7 +145,6 @@ You can skip linkcheck with --skip src/tools/linkchecker" &[], "linkchecker", "linkchecker self tests", - compiler, bootstrap_host, builder, ); @@ -212,6 +225,9 @@ impl Step for HtmlCheck { } } +/// Builds cargo and then runs the `src/tools/cargotest` tool, which checks out +/// some representative crate repositories and runs `cargo test` on them, in +/// order to test cargo. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Cargotest { stage: u32, @@ -257,6 +273,7 @@ impl Step for Cargotest { } } +/// Runs `cargo test` for cargo itself. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Cargo { stage: u32, @@ -296,7 +313,7 @@ impl Step for Cargo { ); // NOTE: can't use `run_cargo_test` because we need to overwrite `PATH` - let mut cargo = prepare_cargo_test(cargo, &[], &[], "cargo", compiler, self.host, builder); + let mut cargo = prepare_cargo_test(cargo, &[], &[], "cargo", self.host, builder); // Don't run cross-compile tests, we may not have cross-compiled libstd libs // available. @@ -381,10 +398,11 @@ impl Step for RustAnalyzer { cargo.env("SKIP_SLOW_TESTS", "1"); cargo.add_rustc_lib_path(builder); - run_cargo_test(cargo, &[], &[], "rust-analyzer", "rust-analyzer", compiler, host, builder); + run_cargo_test(cargo, &[], &[], "rust-analyzer", "rust-analyzer", host, builder); } } +/// Runs `cargo test` for rustfmt. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Rustfmt { stage: u32, @@ -428,7 +446,7 @@ impl Step for Rustfmt { cargo.add_rustc_lib_path(builder); - run_cargo_test(cargo, &[], &[], "rustfmt", "rustfmt", compiler, host, builder); + run_cargo_test(cargo, &[], &[], "rustfmt", "rustfmt", host, builder); } } @@ -528,7 +546,7 @@ impl Step for Miri { // The mtime of `miri_sysroot` changes when the sysroot gets rebuilt (also see // ). // We can hence use that directly as a signal to clear the ui test dir. - builder.clear_if_dirty(&ui_test_dep_dir, &miri_sysroot); + build_stamp::clear_if_dirty(builder, &ui_test_dep_dir, &miri_sysroot); } // Run `cargo test`. @@ -548,7 +566,7 @@ impl Step for Miri { // We can NOT use `run_cargo_test` since Miri's integration tests do not use the usual test // harness and therefore do not understand the flags added by `add_flags_and_try_run_test`. - let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", host_compiler, host, builder); + let mut cargo = prepare_cargo_test(cargo, &[], &[], "miri", host, builder); // miri tests need to know about the stage sysroot cargo.env("MIRI_SYSROOT", &miri_sysroot); @@ -589,6 +607,8 @@ impl Step for Miri { } } +/// Runs `cargo miri test` to demonstrate that `src/tools/miri/cargo-miri` +/// works and that libtest works under miri. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CargoMiri { target: TargetSelection, @@ -694,16 +714,7 @@ impl Step for CompiletestTest { &[], ); cargo.allow_features("test"); - run_cargo_test( - cargo, - &[], - &[], - "compiletest", - "compiletest self test", - compiler, - host, - builder, - ); + run_cargo_test(cargo, &[], &[], "compiletest", "compiletest self test", host, builder); } } @@ -750,7 +761,7 @@ impl Step for Clippy { cargo.env("HOST_LIBS", host_libs); cargo.add_rustc_lib_path(builder); - let cargo = prepare_cargo_test(cargo, &[], &[], "clippy", compiler, host, builder); + let cargo = prepare_cargo_test(cargo, &[], &[], "clippy", host, builder); let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "clippy", host, host); @@ -896,7 +907,7 @@ impl Step for RustdocJSNotStd { builder.ensure(Compiletest { compiler: self.compiler, target: self.target, - mode: "js-doc-test", + mode: "rustdoc-js", suite: "rustdoc-js", path: "tests/rustdoc-js", compare_mode: None, @@ -963,7 +974,7 @@ impl Step for RustdocGUI { let mut cmd = builder.tool_cmd(Tool::RustdocGUITest); let out_dir = builder.test_out(self.target).join("rustdoc-gui"); - builder.clear_if_dirty(&out_dir, &builder.rustdoc(self.compiler)); + build_stamp::clear_if_dirty(builder, &out_dir, &builder.rustdoc(self.compiler)); if let Some(src) = builder.config.src.to_str() { cmd.arg("--rust-src").arg(src); @@ -1020,6 +1031,10 @@ impl Step for RustdocGUI { } } +/// Runs `src/tools/tidy` and `cargo fmt --check` to detect various style +/// problems in the repository. +/// +/// (To run the tidy tool's internal tests, use the alias "tidyselftest" instead.) #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Tidy; @@ -1185,53 +1200,6 @@ macro_rules! test { }; } -/// Declares an alias for running the [`Coverage`] tests in only one mode. -/// Adapted from [`test`]. -macro_rules! coverage_test_alias { - ( - $( #[$attr:meta] )* // allow docstrings and attributes - $name:ident { - alias_and_mode: $alias_and_mode:expr, // &'static str - default: $default:expr, // bool - only_hosts: $only_hosts:expr // bool - $( , )? // optional trailing comma - } - ) => { - $( #[$attr] )* - #[derive(Debug, Clone, PartialEq, Eq, Hash)] - pub struct $name { - pub compiler: Compiler, - pub target: TargetSelection, - } - - impl $name { - const MODE: &'static str = $alias_and_mode; - } - - impl Step for $name { - type Output = (); - const DEFAULT: bool = $default; - const ONLY_HOSTS: bool = $only_hosts; - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - // Register the mode name as a command-line alias. - // This allows `x test coverage-map` and `x test coverage-run`. - run.alias($alias_and_mode) - } - - fn make_run(run: RunConfig<'_>) { - let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); - - run.builder.ensure($name { compiler, target: run.target }); - } - - fn run(self, builder: &Builder<'_>) { - Coverage::run_coverage_tests(builder, self.compiler, self.target, Self::MODE); - } - } - }; -} - #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)] pub struct RunMakeSupport { pub compiler: Compiler, @@ -1277,6 +1245,8 @@ impl Step for RunMakeSupport { } } +/// Runs `cargo test` on the `src/tools/run-make-support` crate. +/// That crate is used by run-make tests. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateRunMakeSupport { host: TargetSelection, @@ -1316,7 +1286,6 @@ impl Step for CrateRunMakeSupport { &[], "run-make-support", "run-make-support self test", - compiler, host, builder, ); @@ -1356,16 +1325,7 @@ impl Step for CrateBuildHelper { &[], ); cargo.allow_features("test"); - run_cargo_test( - cargo, - &[], - &[], - "build_helper", - "build_helper self test", - compiler, - host, - builder, - ); + run_cargo_test(cargo, &[], &[], "build_helper", "build_helper self test", host, builder); } } @@ -1473,99 +1433,96 @@ impl Step for RunMake { test!(Assembly { path: "tests/assembly", mode: "assembly", suite: "assembly", default: true }); -/// Coverage tests are a bit more complicated than other test suites, because -/// we want to run the same set of test files in multiple different modes, -/// in a way that's convenient and flexible when invoked manually. -/// -/// This combined step runs the specified tests (or all of `tests/coverage`) -/// in both "coverage-map" and "coverage-run" modes. -/// -/// Used by: -/// - `x test coverage` -/// - `x test tests/coverage` -/// - `x test tests/coverage/trivial.rs` (etc) -/// -/// (Each individual mode also has its own step that will run the tests in -/// just that mode.) -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +/// Runs the coverage test suite at `tests/coverage` in some or all of the +/// coverage test modes. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Coverage { pub compiler: Compiler, pub target: TargetSelection, + pub mode: &'static str, } impl Coverage { const PATH: &'static str = "tests/coverage"; const SUITE: &'static str = "coverage"; - - /// Runs the coverage test suite (or a user-specified subset) in one mode. - /// - /// This same function is used by the multi-mode step ([`Coverage`]) and by - /// the single-mode steps ([`CoverageMap`] and [`CoverageRun`]), to help - /// ensure that they all behave consistently with each other, regardless of - /// how the coverage tests have been invoked. - fn run_coverage_tests( - builder: &Builder<'_>, - compiler: Compiler, - target: TargetSelection, - mode: &'static str, - ) { - // Like many other test steps, we delegate to a `Compiletest` step to - // actually run the tests. (See `test_definitions!`.) - builder.ensure(Compiletest { - compiler, - target, - mode, - suite: Self::SUITE, - path: Self::PATH, - compare_mode: None, - }); - } + const ALL_MODES: &[&str] = &["coverage-map", "coverage-run"]; } impl Step for Coverage { type Output = (); - /// We rely on the individual CoverageMap/CoverageRun steps to run themselves. - const DEFAULT: bool = false; - /// When manually invoked, try to run as much as possible. + const DEFAULT: bool = true; /// Compiletest will automatically skip the "coverage-run" tests if necessary. const ONLY_HOSTS: bool = false; - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - // Take responsibility for command-line paths within `tests/coverage`. - run.suite_path(Self::PATH) + fn should_run(mut run: ShouldRun<'_>) -> ShouldRun<'_> { + // Support various invocation styles, including: + // - `./x test coverage` + // - `./x test tests/coverage/trivial.rs` + // - `./x test coverage-map` + // - `./x test coverage-run -- tests/coverage/trivial.rs` + run = run.suite_path(Self::PATH); + for mode in Self::ALL_MODES { + run = run.alias(mode); + } + run } fn make_run(run: RunConfig<'_>) { let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); + let target = run.target; + + // List of (coverage) test modes that the coverage test suite will be + // run in. It's OK for this to contain duplicates, because the call to + // `Builder::ensure` below will take care of deduplication. + let mut modes = vec![]; + + // From the pathsets that were selected on the command-line (or by default), + // determine which modes to run in. + for path in &run.paths { + match path { + PathSet::Set(_) => { + for mode in Self::ALL_MODES { + if path.assert_single_path().path == Path::new(mode) { + modes.push(mode); + break; + } + } + } + PathSet::Suite(_) => { + modes.extend(Self::ALL_MODES); + break; + } + } + } - run.builder.ensure(Coverage { compiler, target: run.target }); - } + // Skip any modes that were explicitly skipped/excluded on the command-line. + // FIXME(Zalathar): Integrate this into central skip handling somehow? + modes.retain(|mode| !run.builder.config.skip.iter().any(|skip| skip == Path::new(mode))); - fn run(self, builder: &Builder<'_>) { - // Run the specified coverage tests (possibly all of them) in both modes. - Self::run_coverage_tests(builder, self.compiler, self.target, CoverageMap::MODE); - Self::run_coverage_tests(builder, self.compiler, self.target, CoverageRun::MODE); - } -} + // FIXME(Zalathar): Make these commands skip all coverage tests, as expected: + // - `./x test --skip=tests` + // - `./x test --skip=tests/coverage` + // - `./x test --skip=coverage` + // Skip handling currently doesn't have a way to know that skipping the coverage + // suite should also skip the `coverage-map` and `coverage-run` aliases. -coverage_test_alias! { - /// Runs the `tests/coverage` test suite in "coverage-map" mode only. - /// Used by `x test` and `x test coverage-map`. - CoverageMap { - alias_and_mode: "coverage-map", - default: true, - only_hosts: false, + for mode in modes { + run.builder.ensure(Coverage { compiler, target, mode }); + } } -} -coverage_test_alias! { - /// Runs the `tests/coverage` test suite in "coverage-run" mode only. - /// Used by `x test` and `x test coverage-run`. - CoverageRun { - alias_and_mode: "coverage-run", - default: true, - // Compiletest knows how to automatically skip these tests when cross-compiling, - // but skipping the whole step here makes it clearer that they haven't run at all. - only_hosts: true, + + fn run(self, builder: &Builder<'_>) { + let Self { compiler, target, mode } = self; + // Like other compiletest suite test steps, delegate to an internal + // compiletest task to actually run the tests. + builder.ensure(Compiletest { + compiler, + target, + mode, + suite: Self::SUITE, + path: Self::PATH, + compare_mode: None, + }); } } @@ -1692,16 +1649,17 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the // bootstrap compiler. // NOTE: Only stage 1 is special cased because we need the rustc_private artifacts to match the // running compiler in stage 2 when plugins run. - let stage_id = if suite == "ui-fulldeps" && compiler.stage == 1 { - // At stage 0 (stage - 1) we are using the beta compiler. Using `self.target` can lead finding - // an incorrect compiler path on cross-targets, as the stage 0 beta compiler is always equal - // to `build.build` in the configuration. + let (stage, stage_id) = if suite == "ui-fulldeps" && compiler.stage == 1 { + // At stage 0 (stage - 1) we are using the beta compiler. Using `self.target` can lead + // finding an incorrect compiler path on cross-targets, as the stage 0 beta compiler is + // always equal to `build.build` in the configuration. let build = builder.build.build; - compiler = builder.compiler(compiler.stage - 1, build); - format!("stage{}-{}", compiler.stage + 1, build) + let test_stage = compiler.stage + 1; + (test_stage, format!("stage{}-{}", test_stage, build)) } else { - format!("stage{}-{}", compiler.stage, target) + let stage = compiler.stage; + (stage, format!("stage{}-{}", stage, target)) }; if suite.ends_with("fulldeps") { @@ -1743,6 +1701,9 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the // compiletest currently has... a lot of arguments, so let's just pass all // of them! + cmd.arg("--stage").arg(stage.to_string()); + cmd.arg("--stage-id").arg(stage_id); + cmd.arg("--compile-lib-path").arg(builder.rustc_libdir(compiler)); cmd.arg("--run-lib-path").arg(builder.sysroot_target_libdir(compiler, target)); cmd.arg("--rustc-path").arg(builder.rustc(compiler)); @@ -1752,7 +1713,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the cmd.arg("--minicore-path") .arg(builder.src.join("tests").join("auxiliary").join("minicore.rs")); - let is_rustdoc = suite.ends_with("rustdoc-ui") || suite.ends_with("rustdoc-js"); + let is_rustdoc = suite == "rustdoc-ui" || suite == "rustdoc-js"; if mode == "run-make" { let cargo_path = if builder.top_stage == 0 { @@ -1780,7 +1741,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the if mode == "rustdoc" || mode == "run-make" || (mode == "ui" && is_rustdoc) - || mode == "js-doc-test" + || mode == "rustdoc-js" || mode == "rustdoc-json" || suite == "coverage-run-rustdoc" { @@ -1811,8 +1772,9 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the } else { builder.sysroot(compiler).to_path_buf() }; + cmd.arg("--sysroot-base").arg(sysroot); - cmd.arg("--stage-id").arg(stage_id); + cmd.arg("--suite").arg(suite); cmd.arg("--mode").arg(mode); cmd.arg("--target").arg(target.rustc_target_arg()); @@ -1852,8 +1814,8 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the if let Some(ref nodejs) = builder.config.nodejs { cmd.arg("--nodejs").arg(nodejs); - } else if mode == "js-doc-test" { - panic!("need nodejs to run js-doc-test suite"); + } else if mode == "rustdoc-js" { + panic!("need nodejs to run rustdoc-js suite"); } if let Some(ref npm) = builder.config.npm { cmd.arg("--npm").arg(npm); @@ -1884,16 +1846,24 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the } } + // FIXME(136096): on macOS, we get linker warnings about duplicate `-lm` flags. + // NOTE: `stage > 1` here because `test --stage 1 ui-fulldeps` is a hack that compiles + // with stage 0, but links the tests against stage 1. + // cfg(bootstrap) - remove only the `stage > 1` check, leave everything else. + if suite == "ui-fulldeps" && compiler.stage > 1 && target.ends_with("darwin") { + flags.push("-Alinker_messages".into()); + } + let mut hostflags = flags.clone(); hostflags.push(format!("-Lnative={}", builder.test_helpers_out(compiler.host).display())); hostflags.extend(linker_flags(builder, compiler.host, LldThreads::No)); - for flag in hostflags { - cmd.arg("--host-rustcflags").arg(flag); - } let mut targetflags = flags; targetflags.push(format!("-Lnative={}", builder.test_helpers_out(target).display())); - targetflags.extend(linker_flags(builder, compiler.host, LldThreads::No)); + + for flag in hostflags { + cmd.arg("--host-rustcflags").arg(flag); + } for flag in targetflags { cmd.arg("--target-rustcflags").arg(flag); } @@ -1978,8 +1948,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the let llvm::LlvmResult { llvm_config, .. } = builder.ensure(llvm::Llvm { target: builder.config.build }); if !builder.config.dry_run() { - let llvm_version = - command(&llvm_config).arg("--version").run_capture_stdout(builder).stdout(); + let llvm_version = get_llvm_version(builder, &llvm_config); let llvm_components = command(&llvm_config).arg("--components").run_capture_stdout(builder).stdout(); // Remove trailing newline from llvm-config output. @@ -1998,11 +1967,16 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the // Tests that use compiler libraries may inherit the `-lLLVM` link // requirement, but the `-L` library path is not propagated across // separate compilations. We can add LLVM's library path to the - // platform-specific environment variable as a workaround. + // rustc args as a workaround. if !builder.config.dry_run() && suite.ends_with("fulldeps") { let llvm_libdir = command(&llvm_config).arg("--libdir").run_capture_stdout(builder).stdout(); - add_link_lib_path(vec![llvm_libdir.trim().into()], &mut cmd); + let link_llvm = if target.is_msvc() { + format!("-Clink-arg=-LIBPATH:{llvm_libdir}") + } else { + format!("-Clink-arg=-L{llvm_libdir}") + }; + cmd.arg("--host-rustcflags").arg(link_llvm); } if !builder.config.dry_run() && matches!(mode, "run-make" | "coverage-run") { @@ -2038,14 +2012,18 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the // Only pass correct values for these flags for the `run-make` suite as it // requires that a C++ compiler was configured which isn't always the case. if !builder.config.dry_run() && mode == "run-make" { + let mut cflags = builder.cc_handled_clags(target, CLang::C); + cflags.extend(builder.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::C)); + let mut cxxflags = builder.cc_handled_clags(target, CLang::Cxx); + cxxflags.extend(builder.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::Cxx)); cmd.arg("--cc") .arg(builder.cc(target)) .arg("--cxx") .arg(builder.cxx(target).unwrap()) .arg("--cflags") - .arg(builder.cflags(target, GitRepo::Rustc, CLang::C).join(" ")) + .arg(cflags.join(" ")) .arg("--cxxflags") - .arg(builder.cflags(target, GitRepo::Rustc, CLang::Cxx).join(" ")); + .arg(cxxflags.join(" ")); copts_passed = true; if let Some(ar) = builder.ar(target) { cmd.arg("--ar").arg(ar); @@ -2285,10 +2263,8 @@ impl BookTest { &[], ); - let stamp = builder - .cargo_out(compiler, mode, target) - .join(PathBuf::from(dep).file_name().unwrap()) - .with_extension("stamp"); + let stamp = BuildStamp::new(&builder.cargo_out(compiler, mode, target)) + .with_prefix(PathBuf::from(dep).file_name().and_then(|v| v.to_str()).unwrap()); let output_paths = run_cargo(builder, cargo, vec![], &stamp, vec![], false, false); let directories = output_paths @@ -2517,6 +2493,10 @@ fn markdown_test(builder: &Builder<'_>, compiler: Compiler, markdown: &Path) -> } } +/// Runs `cargo test` for the compiler crates in `compiler/`. +/// +/// (This step does not test `rustc_codegen_cranelift` or `rustc_codegen_gcc`, +/// which have their own separate test steps.) #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateLibrustc { compiler: Compiler, @@ -2545,6 +2525,7 @@ impl Step for CrateLibrustc { fn run(self, builder: &Builder<'_>) { builder.ensure(compile::Std::new(self.compiler, self.target)); + // To actually run the tests, delegate to a copy of the `Crate` step. builder.ensure(Crate { compiler: self.compiler, target: self.target, @@ -2557,19 +2538,17 @@ impl Step for CrateLibrustc { /// Given a `cargo test` subcommand, add the appropriate flags and run it. /// /// Returns whether the test succeeded. -#[allow(clippy::too_many_arguments)] // FIXME: reduce the number of args and remove this. fn run_cargo_test<'a>( - cargo: impl Into, + cargo: builder::Cargo, libtest_args: &[&str], crates: &[String], primary_crate: &str, description: impl Into>, - compiler: Compiler, target: TargetSelection, builder: &Builder<'_>, ) -> bool { - let mut cargo = - prepare_cargo_test(cargo, libtest_args, crates, primary_crate, compiler, target, builder); + let compiler = cargo.compiler(); + let mut cargo = prepare_cargo_test(cargo, libtest_args, crates, primary_crate, target, builder); let _time = helpers::timeit(builder); let _group = description.into().and_then(|what| { builder.msg_sysroot_tool(Kind::Test, compiler.stage, what, compiler.host, target) @@ -2590,15 +2569,15 @@ fn run_cargo_test<'a>( /// Given a `cargo test` subcommand, pass it the appropriate test flags given a `builder`. fn prepare_cargo_test( - cargo: impl Into, + cargo: builder::Cargo, libtest_args: &[&str], crates: &[String], primary_crate: &str, - compiler: Compiler, target: TargetSelection, builder: &Builder<'_>, ) -> BootstrapCommand { - let mut cargo = cargo.into(); + let compiler = cargo.compiler(); + let mut cargo: BootstrapCommand = cargo.into(); // Propagate `--bless` if it has not already been set/unset // Any tools that want to use this should bless if `RUSTC_BLESS` is set to @@ -2670,6 +2649,13 @@ fn prepare_cargo_test( cargo } +/// Runs `cargo test` for standard library crates. +/// +/// (Also used internally to run `cargo test` for compiler crates.) +/// +/// FIXME(Zalathar): Try to split this into two separate steps: a user-visible +/// step for testing standard library crates, and an internal step used for both +/// library crates and compiler crates. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Crate { pub compiler: Compiler, @@ -2683,7 +2669,7 @@ impl Step for Crate { const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.crate_or_deps("sysroot") + run.crate_or_deps("sysroot").crate_or_deps("coretests") } fn make_run(run: RunConfig<'_>) { @@ -2757,7 +2743,7 @@ impl Step for Crate { cargo } else { // Also prepare a sysroot for the target. - if builder.config.build != target { + if !builder.is_builder_target(&target) { builder.ensure(compile::Std::new(compiler, target).force_recompile(true)); builder.ensure(RemoteCopyLibs { compiler, target }); } @@ -2803,7 +2789,6 @@ impl Step for Crate { &self.crates, &self.crates[0], &*crate_description(&self.crates), - compiler, target, builder, ); @@ -2905,7 +2890,6 @@ impl Step for CrateRustdoc { &["rustdoc:0.0.0".to_string()], "rustdoc", "rustdoc", - compiler, target, builder, ); @@ -2966,7 +2950,6 @@ impl Step for CrateRustdocJsonTypes { &["rustdoc-json-types".to_string()], "rustdoc-json-types", "rustdoc-json-types", - compiler, target, builder, ); @@ -3123,23 +3106,27 @@ impl Step for Bootstrap { // Use `python -m unittest` manually if you want to pass arguments. check_bootstrap.delay_failure().run(builder); - let mut cmd = command(&builder.initial_cargo); - cmd.arg("test") - .current_dir(builder.src.join("src/bootstrap")) - .env("RUSTFLAGS", "--cfg test -Cdebuginfo=2") + let mut cargo = tool::prepare_tool_cargo( + builder, + compiler, + Mode::ToolBootstrap, + host, + Kind::Test, + "src/bootstrap", + SourceType::InTree, + &[], + ); + + cargo.release_build(false); + + cargo + .rustflag("-Cdebuginfo=2") .env("CARGO_TARGET_DIR", builder.out.join("bootstrap")) - .env("RUSTC_BOOTSTRAP", "1") - .env("RUSTDOC", builder.rustdoc(compiler)) - .env("RUSTC", &builder.initial_rustc); - if let Some(flags) = option_env!("RUSTFLAGS") { - // Use the same rustc flags for testing as for "normal" compilation, - // so that Cargo doesn’t recompile the entire dependency graph every time: - // https://github.com/rust-lang/rust/issues/49215 - cmd.env("RUSTFLAGS", flags); - } + .env("RUSTC_BOOTSTRAP", "1"); + // bootstrap tests are racy on directory creation so just run them one at a time. // Since there's not many this shouldn't be a problem. - run_cargo_test(cmd, &["--test-threads=1"], &[], "bootstrap", None, compiler, host, builder); + run_cargo_test(cargo, &["--test-threads=1"], &[], "bootstrap", None, host, builder); } fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -3264,7 +3251,7 @@ impl Step for RustInstaller { bootstrap_host, bootstrap_host, ); - run_cargo_test(cargo, &[], &[], "installer", None, compiler, bootstrap_host, builder); + run_cargo_test(cargo, &[], &[], "installer", None, bootstrap_host, builder); // We currently don't support running the test.sh script outside linux(?) environments. // Eventually this should likely migrate to #[test]s in rust-installer proper rather than a @@ -3587,6 +3574,8 @@ impl Step for CodegenGCC { let mut cargo = build_cargo(); cargo + // cg_gcc's build system ignores RUSTFLAGS. pass some flags through CG_RUSTFLAGS instead. + .env("CG_RUSTFLAGS", "-Alinker-messages") .arg("--") .arg("test") .arg("--use-system-gcc") @@ -3603,6 +3592,10 @@ impl Step for CodegenGCC { } } +/// Test step that does two things: +/// - Runs `cargo test` for the `src/etc/test-float-parse` tool. +/// - Invokes the `test-float-parse` tool to test the standard library's +/// float parsing routines. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct TestFloatParse { path: PathBuf, @@ -3645,16 +3638,7 @@ impl Step for TestFloatParse { &[], ); - run_cargo_test( - cargo_test, - &[], - &[], - crate_name, - crate_name, - compiler, - bootstrap_host, - builder, - ); + run_cargo_test(cargo_test, &[], &[], crate_name, crate_name, bootstrap_host, builder); // Run the actual parse tests. let mut cargo_run = tool::prepare_tool_cargo( @@ -3676,6 +3660,9 @@ impl Step for TestFloatParse { } } +/// Runs the tool `src/tools/collect-license-metadata` in `ONLY_CHECK=1` mode, +/// which verifies that `license-metadata.json` is up-to-date and therefore +/// running the tool normally would not update anything. #[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] pub struct CollectLicenseMetadata; diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index d0058eeb43d3c..1291a634a6f6f 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -1,11 +1,14 @@ use std::path::PathBuf; use std::{env, fs}; +use crate::core::build_steps::compile::is_lto_stage; use crate::core::build_steps::toolstate::ToolState; use crate::core::build_steps::{compile, llvm}; use crate::core::builder; -use crate::core::builder::{Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step}; -use crate::core::config::{DebuginfoLevel, TargetSelection}; +use crate::core::builder::{ + Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step, cargo_profile_var, +}; +use crate::core::config::{DebuginfoLevel, RustcLto, TargetSelection}; use crate::utils::channel::GitInfo; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{add_dylib_path, exe, t}; @@ -334,7 +337,11 @@ macro_rules! bootstrap_tool { } bootstrap_tool!( - Rustbook, "src/tools/rustbook", "rustbook", submodules = SUBMODULES_FOR_RUSTBOOK; + // This is marked as an external tool because it includes dependencies + // from submodules. Trying to keep the lints in sync between all the repos + // is a bit of a pain. Unfortunately it means the rustbook source itself + // doesn't deny warnings, but it is a relatively small piece of code. + Rustbook, "src/tools/rustbook", "rustbook", is_external_tool = true, submodules = SUBMODULES_FOR_RUSTBOOK; UnstableBookGen, "src/tools/unstable-book-gen", "unstable-book-gen"; Tidy, "src/tools/tidy", "tidy"; Linkchecker, "src/tools/linkchecker", "linkchecker"; @@ -356,9 +363,9 @@ bootstrap_tool!( GenerateWindowsSys, "src/tools/generate-windows-sys", "generate-windows-sys"; RustdocGUITest, "src/tools/rustdoc-gui-test", "rustdoc-gui-test", is_unstable_tool = true, allow_features = "test"; CoverageDump, "src/tools/coverage-dump", "coverage-dump"; - RustcPerfWrapper, "src/tools/rustc-perf-wrapper", "rustc-perf-wrapper"; WasmComponentLd, "src/tools/wasm-component-ld", "wasm-component-ld", is_unstable_tool = true, allow_features = "min_specialization"; UnicodeTableGenerator, "src/tools/unicode-table-generator", "unicode-table-generator"; + FeaturesStatusDump, "src/tools/features-status-dump", "features-status-dump"; ); /// These are the submodules that are required for rustbook to work due to @@ -575,7 +582,7 @@ impl Step for Rustdoc { if !target_compiler.is_snapshot(builder) { panic!("rustdoc in stage 0 must be snapshot rustdoc"); } - return builder.initial_rustc.with_file_name(exe("rustdoc", target_compiler.host)); + return builder.initial_rustdoc.clone(); } let target = target_compiler.host; @@ -641,7 +648,7 @@ impl Step for Rustdoc { } // NOTE: Never modify the rustflags here, it breaks the build cache for other tools! - let cargo = prepare_tool_cargo( + let mut cargo = prepare_tool_cargo( builder, build_compiler, Mode::ToolRustc, @@ -652,6 +659,19 @@ impl Step for Rustdoc { features.as_slice(), ); + // rustdoc is performance sensitive, so apply LTO to it. + if is_lto_stage(&build_compiler) { + let lto = match builder.config.rust_lto { + RustcLto::Off => Some("off"), + RustcLto::Thin => Some("thin"), + RustcLto::Fat => Some("fat"), + RustcLto::ThinLocal => None, + }; + if let Some(lto) = lto { + cargo.env(cargo_profile_var("LTO", &builder.config), lto); + } + } + let _guard = builder.msg_tool( Kind::Build, Mode::ToolRustc, diff --git a/src/bootstrap/src/core/build_steps/toolstate.rs b/src/bootstrap/src/core/build_steps/toolstate.rs index 84871331bd509..668133f05cb35 100644 --- a/src/bootstrap/src/core/build_steps/toolstate.rs +++ b/src/bootstrap/src/core/build_steps/toolstate.rs @@ -42,11 +42,15 @@ pub enum ToolState { impl fmt::Display for ToolState { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", match self { - ToolState::TestFail => "test-fail", - ToolState::TestPass => "test-pass", - ToolState::BuildFail => "build-fail", - }) + write!( + f, + "{}", + match self { + ToolState::TestFail => "test-fail", + ToolState::TestPass => "test-pass", + ToolState::BuildFail => "build-fail", + } + ) } } diff --git a/src/bootstrap/src/core/build_steps/vendor.rs b/src/bootstrap/src/core/build_steps/vendor.rs index 26d0f100ffd52..410dbc04f0306 100644 --- a/src/bootstrap/src/core/build_steps/vendor.rs +++ b/src/bootstrap/src/core/build_steps/vendor.rs @@ -4,6 +4,8 @@ use crate::core::build_steps::tool::SUBMODULES_FOR_RUSTBOOK; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::utils::exec::command; +pub const VENDOR_DIR: &str = "vendor"; + /// Returns the cargo workspaces to vendor for `x vendor` and dist tarballs. /// /// Returns a `Vec` of `(path_to_manifest, submodules_required)` where @@ -29,13 +31,14 @@ pub fn default_paths_to_vendor(builder: &Builder<'_>) -> Vec<(PathBuf, Vec<&'sta #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub(crate) struct Vendor { - sync_args: Vec, - versioned_dirs: bool, - root_dir: PathBuf, + pub(crate) sync_args: Vec, + pub(crate) versioned_dirs: bool, + pub(crate) root_dir: PathBuf, + pub(crate) output_dir: PathBuf, } impl Step for Vendor { - type Output = (); + type Output = VendorOutput; const DEFAULT: bool = true; const ONLY_HOSTS: bool = true; @@ -48,10 +51,13 @@ impl Step for Vendor { sync_args: run.builder.config.cmd.vendor_sync_args(), versioned_dirs: run.builder.config.cmd.vendor_versioned_dirs(), root_dir: run.builder.src.clone(), + output_dir: run.builder.src.join(VENDOR_DIR), }); } fn run(self, builder: &Builder<'_>) -> Self::Output { + builder.info(&format!("Vendoring sources to {:?}", self.root_dir)); + let mut cmd = command(&builder.initial_cargo); cmd.arg("vendor"); @@ -81,8 +87,14 @@ impl Step for Vendor { // which uses the unstable `public-dependency` feature. cmd.env("RUSTC_BOOTSTRAP", "1"); - cmd.current_dir(self.root_dir); + cmd.current_dir(self.root_dir).arg(&self.output_dir); - cmd.run(builder); + let config = cmd.run_capture_stdout(builder); + VendorOutput { config: config.stdout() } } } + +#[derive(Debug, Clone)] +pub(crate) struct VendorOutput { + pub(crate) config: String, +} diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 432fbb8d63682..1ec3e601cad19 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -7,11 +7,10 @@ use crate::core::build_steps::tool::SourceType; use crate::core::build_steps::{compile, test}; use crate::core::config::SplitDebuginfo; use crate::core::config::flags::Color; -use crate::utils::helpers::{ - self, LldThreads, add_link_lib_path, check_cfg_arg, linker_args, linker_flags, -}; +use crate::utils::build_stamp; +use crate::utils::helpers::{self, LldThreads, check_cfg_arg, linker_args, linker_flags}; use crate::{ - BootstrapCommand, CLang, Compiler, DocTests, DryRun, EXTRA_CHECK_CFGS, GitRepo, Mode, + BootstrapCommand, CLang, Compiler, Config, DocTests, DryRun, EXTRA_CHECK_CFGS, GitRepo, Mode, TargetSelection, command, prepare_behaviour_dump_dir, t, }; @@ -89,12 +88,14 @@ impl HostFlags { #[derive(Debug)] pub struct Cargo { command: BootstrapCommand, + args: Vec, compiler: Compiler, target: TargetSelection, rustflags: Rustflags, rustdocflags: Rustflags, hostflags: HostFlags, allow_features: String, + release_build: bool, } impl Cargo { @@ -122,6 +123,14 @@ impl Cargo { cargo } + pub fn release_build(&mut self, release_build: bool) { + self.release_build = release_build; + } + + pub fn compiler(&self) -> Compiler { + self.compiler + } + pub fn into_cmd(self) -> BootstrapCommand { self.into() } @@ -150,7 +159,7 @@ impl Cargo { } pub fn arg(&mut self, arg: impl AsRef) -> &mut Cargo { - self.command.arg(arg.as_ref()); + self.args.push(arg.as_ref().into()); self } @@ -236,7 +245,11 @@ impl Cargo { // flesh out rpath support more fully in the future. self.rustflags.arg("-Zosx-rpath-install-name"); Some(format!("-Wl,-rpath,@loader_path/../{libdir}")) - } else if !target.is_windows() && !target.contains("aix") && !target.contains("xous") { + } else if !target.is_windows() + && !target.contains("cygwin") + && !target.contains("aix") + && !target.contains("xous") + { self.rustflags.arg("-Clink-args=-Wl,-z,origin"); Some(format!("-Wl,-rpath,$ORIGIN/../{libdir}")) } else { @@ -270,6 +283,13 @@ impl Cargo { self.rustflags.arg("-Clink-arg=-gz"); } + // Ignore linker warnings for now. These are complicated to fix and don't affect the build. + // FIXME: we should really investigate these... + // cfg(bootstrap) + if compiler.stage != 0 { + self.rustflags.arg("-Alinker-messages"); + } + // Throughout the build Cargo can execute a number of build scripts // compiling C/C++ code and we need to pass compilers, archivers, flags, etc // obtained previously to those build scripts. @@ -307,8 +327,15 @@ impl Cargo { let cc = ccacheify(&builder.cc(target)); self.command.env(format!("CC_{triple_underscored}"), &cc); - let cflags = builder.cflags(target, GitRepo::Rustc, CLang::C).join(" "); - self.command.env(format!("CFLAGS_{triple_underscored}"), &cflags); + // Extend `CXXFLAGS_$TARGET` with our extra flags. + let env = format!("CFLAGS_{triple_underscored}"); + let mut cflags = + builder.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::C).join(" "); + if let Ok(var) = std::env::var(&env) { + cflags.push(' '); + cflags.push_str(&var); + } + self.command.env(env, &cflags); if let Some(ar) = builder.ar(target) { let ranlib = format!("{} s", ar.display()); @@ -319,10 +346,17 @@ impl Cargo { if let Ok(cxx) = builder.cxx(target) { let cxx = ccacheify(&cxx); - let cxxflags = builder.cflags(target, GitRepo::Rustc, CLang::Cxx).join(" "); - self.command - .env(format!("CXX_{triple_underscored}"), &cxx) - .env(format!("CXXFLAGS_{triple_underscored}"), cxxflags); + self.command.env(format!("CXX_{triple_underscored}"), &cxx); + + // Extend `CXXFLAGS_$TARGET` with our extra flags. + let env = format!("CXXFLAGS_{triple_underscored}"); + let mut cxxflags = + builder.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::Cxx).join(" "); + if let Ok(var) = std::env::var(&env) { + cxxflags.push(' '); + cxxflags.push_str(&var); + } + self.command.env(&env, cxxflags); } } @@ -332,6 +366,12 @@ impl Cargo { impl From for BootstrapCommand { fn from(mut cargo: Cargo) -> BootstrapCommand { + if cargo.release_build { + cargo.args.insert(0, "--release".into()); + } + + cargo.command.args(cargo.args); + let rustflags = &cargo.rustflags.0; if !rustflags.is_empty() { cargo.command.env("RUSTFLAGS", rustflags); @@ -350,6 +390,7 @@ impl From for BootstrapCommand { if !cargo.allow_features.is_empty() { cargo.command.env("RUSTC_ALLOW_FEATURES", cargo.allow_features); } + cargo.command } } @@ -419,13 +460,6 @@ impl Builder<'_> { assert_eq!(target, compiler.host); } - if self.config.rust_optimize.is_release() && - // cargo bench/install do not accept `--release` and miri doesn't want it - !matches!(cmd_kind, Kind::Bench | Kind::Install | Kind::Miri | Kind::MiriSetup | Kind::MiriTest) - { - cargo.arg("--release"); - } - // Remove make-related flags to ensure Cargo can correctly set things up cargo.env_remove("MAKEFLAGS"); cargo.env_remove("MFLAGS"); @@ -454,7 +488,7 @@ impl Builder<'_> { // Codegen backends are not yet tracked by -Zbinary-dep-depinfo, // so we need to explicitly clear out if they've been updated. for backend in self.codegen_backends(compiler) { - self.clear_if_dirty(&out_dir, &backend); + build_stamp::clear_if_dirty(self, &out_dir, &backend); } if cmd_kind == Kind::Doc { @@ -471,13 +505,10 @@ impl Builder<'_> { _ => panic!("doc mode {mode:?} not expected"), }; let rustdoc = self.rustdoc(compiler); - self.clear_if_dirty(&my_out, &rustdoc); + build_stamp::clear_if_dirty(self, &my_out, &rustdoc); } - let profile_var = |name: &str| { - let profile = if self.config.rust_optimize.is_release() { "RELEASE" } else { "DEV" }; - format!("CARGO_PROFILE_{}_{}", profile, name) - }; + let profile_var = |name: &str| cargo_profile_var(name, &self.config); // See comment in rustc_llvm/build.rs for why this is necessary, largely llvm-config // needs to not accidentally link to libLLVM in stage0/lib. @@ -624,8 +655,6 @@ impl Builder<'_> { // get warnings about it being unexpected. hostflags.arg("-Zunstable-options"); hostflags.arg("--check-cfg=cfg(bootstrap)"); - // #[cfg(bootstrap)] as we are transition `test` to userspace cfg - hostflags.arg("--check-cfg=cfg(test)"); // FIXME: It might be better to use the same value for both `RUSTFLAGS` and `RUSTDOCFLAGS`, // but this breaks CI. At the very least, stage0 `rustdoc` needs `--cfg bootstrap`. See @@ -648,7 +677,10 @@ impl Builder<'_> { // Build proc macros both for the host and the target unless proc-macros are not // supported by the target. if target != compiler.host && cmd_kind != Kind::Check { - let error = command(self.rustc(compiler)) + let mut rustc_cmd = command(self.rustc(compiler)); + self.add_rustc_lib_path(compiler, &mut rustc_cmd); + + let error = rustc_cmd .arg("--target") .arg(target.rustc_target_arg()) .arg("--print=file-names") @@ -656,6 +688,7 @@ impl Builder<'_> { .arg("-") .run_capture(self) .stderr(); + let not_supported = error .lines() .any(|line| line.contains("unsupported crate type `proc-macro`")); @@ -765,7 +798,7 @@ impl Builder<'_> { // Only clear out the directory if we're compiling std; otherwise, we // should let Cargo take care of things for us (via depdep info) if !self.config.dry_run() && mode == Mode::Std && cmd_kind == Kind::Build { - self.clear_if_dirty(&out_dir, &self.rustc(compiler)); + build_stamp::clear_if_dirty(self, &out_dir, &self.rustc(compiler)); } let rustdoc_path = match cmd_kind { @@ -891,8 +924,7 @@ impl Builder<'_> { if self.config.rust_remap_debuginfo { let mut env_var = OsString::new(); - if self.config.vendor { - let vendor = self.build.src.join("vendor"); + if let Some(vendor) = self.build.vendored_crates_path() { env_var.push(vendor); env_var.push("=/rust/deps"); } else { @@ -948,12 +980,16 @@ impl Builder<'_> { // Tools that use compiler libraries may inherit the `-lLLVM` link // requirement, but the `-L` library path is not propagated across // separate Cargo projects. We can add LLVM's library path to the - // platform-specific environment variable as a workaround. + // rustc args as a workaround. if mode == Mode::ToolRustc || mode == Mode::Codegen { if let Some(llvm_config) = self.llvm_config(target) { let llvm_libdir = command(llvm_config).arg("--libdir").run_capture_stdout(self).stdout(); - add_link_lib_path(vec![llvm_libdir.trim().into()], &mut cargo); + if target.is_msvc() { + rustflags.arg(&format!("-Clink-arg=-LIBPATH:{llvm_libdir}")); + } else { + rustflags.arg(&format!("-Clink-arg=-L{llvm_libdir}")); + } } } @@ -1206,20 +1242,28 @@ impl Builder<'_> { // so that it'll be available when downstream consumers of std try to use it. rustflags.arg("-Zinline-mir-preserve-debug"); - // FIXME: always pass this after the next `#[cfg(bootstrap)]` update. - if compiler.stage != 0 { - rustflags.arg("-Zmir_strip_debuginfo=locals-in-tiny-functions"); - } + rustflags.arg("-Zmir_strip_debuginfo=locals-in-tiny-functions"); } + let release_build = self.config.rust_optimize.is_release() && + // cargo bench/install do not accept `--release` and miri doesn't want it + !matches!(cmd_kind, Kind::Bench | Kind::Install | Kind::Miri | Kind::MiriSetup | Kind::MiriTest); + Cargo { command: cargo, + args: vec![], compiler, target, rustflags, rustdocflags, hostflags, allow_features, + release_build, } } } + +pub fn cargo_profile_var(name: &str, config: &Config) -> String { + let profile = if config.rust_optimize.is_release() { "RELEASE" } else { "DEV" }; + format!("CARGO_PROFILE_{}_{}", profile, name) +} diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 30e42a5bfb78d..ecec589fc32eb 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1,9 +1,7 @@ -mod cargo; - use std::any::{Any, type_name}; use std::cell::{Cell, RefCell}; use std::collections::BTreeSet; -use std::fmt::{Debug, Write}; +use std::fmt::{self, Debug, Write}; use std::hash::Hash; use std::ops::Deref; use std::path::{Path, PathBuf}; @@ -13,7 +11,7 @@ use std::{env, fs}; use clap::ValueEnum; -pub use self::cargo::Cargo; +pub use self::cargo::{Cargo, cargo_profile_var}; pub use crate::Compiler; use crate::core::build_steps::{ check, clean, clippy, compile, dist, doc, gcc, install, llvm, run, setup, test, tool, vendor, @@ -25,6 +23,8 @@ use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{self, LldThreads, add_dylib_path, exe, libdir, linker_args, t}; use crate::{Build, Crate}; +mod cargo; + #[cfg(test)] mod tests; @@ -271,16 +271,17 @@ impl PathSet { /// This is used for `StepDescription::krate`, which passes all matching crates at once to /// `Step::make_run`, rather than calling it many times with a single crate. /// See `tests.rs` for examples. - fn intersection_removing_matches(&self, needles: &mut Vec, module: Kind) -> PathSet { + fn intersection_removing_matches(&self, needles: &mut [CLIStepPath], module: Kind) -> PathSet { let mut check = |p| { - for (i, n) in needles.iter().enumerate() { - let matched = Self::check(p, n, module); + let mut result = false; + for n in needles.iter_mut() { + let matched = Self::check(p, &n.path, module); if matched { - needles.remove(i); - return true; + n.will_be_executed = true; + result = true; } } - false + result }; match self { PathSet::Set(set) => PathSet::Set(set.iter().filter(|&p| check(p)).cloned().collect()), @@ -314,29 +315,32 @@ const PATH_REMAP: &[(&str, &[&str])] = &[ // actual path is `proc-macro-srv-cli` ("rust-analyzer-proc-macro-srv", &["src/tools/rust-analyzer/crates/proc-macro-srv-cli"]), // Make `x test tests` function the same as `x t tests/*` - ("tests", &[ - // tidy-alphabetical-start - "tests/assembly", - "tests/codegen", - "tests/codegen-units", - "tests/coverage", - "tests/coverage-run-rustdoc", - "tests/crashes", - "tests/debuginfo", - "tests/incremental", - "tests/mir-opt", - "tests/pretty", - "tests/run-make", - "tests/rustdoc", - "tests/rustdoc-gui", - "tests/rustdoc-js", - "tests/rustdoc-js-std", - "tests/rustdoc-json", - "tests/rustdoc-ui", - "tests/ui", - "tests/ui-fulldeps", - // tidy-alphabetical-end - ]), + ( + "tests", + &[ + // tidy-alphabetical-start + "tests/assembly", + "tests/codegen", + "tests/codegen-units", + "tests/coverage", + "tests/coverage-run-rustdoc", + "tests/crashes", + "tests/debuginfo", + "tests/incremental", + "tests/mir-opt", + "tests/pretty", + "tests/run-make", + "tests/rustdoc", + "tests/rustdoc-gui", + "tests/rustdoc-js", + "tests/rustdoc-js-std", + "tests/rustdoc-json", + "tests/rustdoc-ui", + "tests/ui", + "tests/ui-fulldeps", + // tidy-alphabetical-end + ], + ), ]; fn remap_paths(paths: &mut Vec) { @@ -361,6 +365,32 @@ fn remap_paths(paths: &mut Vec) { paths.append(&mut add); } +#[derive(Clone, PartialEq)] +struct CLIStepPath { + path: PathBuf, + will_be_executed: bool, +} + +#[cfg(test)] +impl CLIStepPath { + fn will_be_executed(mut self, will_be_executed: bool) -> Self { + self.will_be_executed = will_be_executed; + self + } +} + +impl Debug for CLIStepPath { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.path.display()) + } +} + +impl From for CLIStepPath { + fn from(path: PathBuf) -> Self { + Self { path, will_be_executed: false } + } +} + impl StepDescription { fn from(kind: Kind) -> StepDescription { StepDescription { @@ -478,7 +508,8 @@ impl StepDescription { return; } - let mut path_lookup: Vec<(PathBuf, bool)> = + let mut paths: Vec = paths.into_iter().map(|p| p.into()).collect(); + let mut path_lookup: Vec<(CLIStepPath, bool)> = paths.clone().into_iter().map(|p| (p, false)).collect(); // List of `(usize, &StepDescription, Vec)` where `usize` is the closest index of a path @@ -518,8 +549,10 @@ impl StepDescription { } } + paths.retain(|p| !p.will_be_executed); + if !paths.is_empty() { - eprintln!("ERROR: no `{}` rules matched {:?}", builder.kind.as_str(), paths,); + eprintln!("ERROR: no `{}` rules matched {:?}", builder.kind.as_str(), paths); eprintln!( "HELP: run `x.py {} --help --verbose` to show a list of available paths", builder.kind.as_str() @@ -682,7 +715,7 @@ impl<'a> ShouldRun<'a> { /// (for now, just `all_krates` and `paths`, but we may want to add an `aliases` function in the future?) fn pathset_for_paths_removing_matches( &self, - paths: &mut Vec, + paths: &mut [CLIStepPath], kind: Kind, ) -> Vec { let mut sets = vec![]; @@ -825,12 +858,8 @@ impl<'a> Builder<'a> { match kind { Kind::Build => describe!( compile::Std, - // FIXME(#135022): `compile::Assemble` **must** come before `compile::Rustc` after - // `PathSet` also permits prefix-matching, because `compile::Rustc` can consume the - // `"compiler"` path filter first, causing `compile::Assemble` to no longer run when - // the user writes `./x build compiler --stage 0`. - compile::Assemble, compile::Rustc, + compile::Assemble, compile::CodegenBackend, compile::StartupObjects, tool::BuildManifest, @@ -874,6 +903,7 @@ impl<'a> Builder<'a> { clippy::BuildManifest, clippy::CargoMiri, clippy::Clippy, + clippy::CodegenGcc, clippy::CollectLicenseMetadata, clippy::Compiletest, clippy::CoverageDump, @@ -909,7 +939,9 @@ impl<'a> Builder<'a> { check::RustAnalyzer, check::TestFloatParse, check::Bootstrap, + check::RunMakeSupport, check::Compiletest, + check::FeaturesStatusDump, ), Kind::Test => describe!( crate::core::build_steps::toolstate::ToolStateCheck, @@ -917,8 +949,6 @@ impl<'a> Builder<'a> { test::Ui, test::Crashes, test::Coverage, - test::CoverageMap, - test::CoverageRun, test::MirOpt, test::Codegen, test::CodegenUnits, @@ -929,14 +959,10 @@ impl<'a> Builder<'a> { test::Rustdoc, test::CoverageRunRustdoc, test::Pretty, - test::Crate, - test::CrateLibrustc, - // The cranelift and gcc tests need to be listed after the - // compiler unit tests (CrateLibrustc) so that they don't - // hijack the whole `compiler` directory during path matching. - // test::CodegenCranelift, test::CodegenGCC, + test::Crate, + test::CrateLibrustc, test::CrateRustdoc, test::CrateRustdocJsonTypes, test::CrateBootstrap, @@ -1067,6 +1093,7 @@ impl<'a> Builder<'a> { run::GenerateWindowsSys, run::GenerateCompletions, run::UnicodeTableGenerator, + run::FeaturesStatusDump, ), Kind::Setup => { describe!(setup::Profile, setup::Hook, setup::Link, setup::Editor) @@ -1404,7 +1431,7 @@ impl<'a> Builder<'a> { /// /// Note that this returns `None` if LLVM is disabled, or if we're in a /// check build or dry-run, where there's no need to build all of LLVM. - fn llvm_config(&self, target: TargetSelection) -> Option { + pub fn llvm_config(&self, target: TargetSelection) -> Option { if self.config.llvm_enabled(target) && self.kind != Kind::Check && !self.config.dry_run() { let llvm::LlvmResult { llvm_config, .. } = self.ensure(llvm::Llvm { target }); if llvm_config.is_file() { @@ -1422,7 +1449,7 @@ impl<'a> Builder<'a> { let mut stack = self.stack.borrow_mut(); for stack_step in stack.iter() { // should skip - if stack_step.downcast_ref::().map_or(true, |stack_step| *stack_step != step) { + if stack_step.downcast_ref::().is_none_or(|stack_step| *stack_step != step) { continue; } let mut out = String::new(); diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 21694cf46fe21..5e3e0ef654fdf 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1,5 +1,7 @@ use std::thread; +use llvm::prebuilt_llvm_config; + use super::*; use crate::Flags; use crate::core::build_steps::doc::DocumentationFormat; @@ -108,13 +110,43 @@ fn test_intersection() { }; let library_set = set(&["library/core", "library/alloc", "library/std"]); let mut command_paths = vec![ - PathBuf::from("library/core"), - PathBuf::from("library/alloc"), - PathBuf::from("library/stdarch"), + CLIStepPath::from(PathBuf::from("library/core")), + CLIStepPath::from(PathBuf::from("library/alloc")), + CLIStepPath::from(PathBuf::from("library/stdarch")), ]; let subset = library_set.intersection_removing_matches(&mut command_paths, Kind::Build); assert_eq!(subset, set(&["library/core", "library/alloc"]),); - assert_eq!(command_paths, vec![PathBuf::from("library/stdarch")]); + assert_eq!( + command_paths, + vec![ + CLIStepPath::from(PathBuf::from("library/core")).will_be_executed(true), + CLIStepPath::from(PathBuf::from("library/alloc")).will_be_executed(true), + CLIStepPath::from(PathBuf::from("library/stdarch")).will_be_executed(false), + ] + ); +} + +#[test] +fn test_resolve_parent_and_subpaths() { + let set = |paths: &[&str]| { + PathSet::Set(paths.into_iter().map(|p| TaskPath { path: p.into(), kind: None }).collect()) + }; + + let mut command_paths = vec![ + CLIStepPath::from(PathBuf::from("src/tools/miri")), + CLIStepPath::from(PathBuf::from("src/tools/miri/cargo-miri")), + ]; + + let library_set = set(&["src/tools/miri", "src/tools/miri/cargo-miri"]); + library_set.intersection_removing_matches(&mut command_paths, Kind::Build); + + assert_eq!( + command_paths, + vec![ + CLIStepPath::from(PathBuf::from("src/tools/miri")).will_be_executed(true), + CLIStepPath::from(PathBuf::from("src/tools/miri/cargo-miri")).will_be_executed(true), + ] + ); } #[test] @@ -188,18 +220,22 @@ fn alias_and_path_for_library() { &["library".into(), "core".into()], configure("build", &[TEST_TRIPLE_1], &[TEST_TRIPLE_1]), ); - assert_eq!(first(cache.all::()), &[ - std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), - std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1) - ]); + assert_eq!( + first(cache.all::()), + &[ + std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), + std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1) + ] + ); let mut cache = run_build( &["library".into(), "core".into()], configure("doc", &[TEST_TRIPLE_1], &[TEST_TRIPLE_1]), ); - assert_eq!(first(cache.all::()), &[ - doc_std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0) - ]); + assert_eq!( + first(cache.all::()), + &[doc_std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0)] + ); } #[test] @@ -247,10 +283,13 @@ mod defaults { let mut cache = run_build(&[], configure("build", &[TEST_TRIPLE_1], &[TEST_TRIPLE_1])); let a = TargetSelection::from_user(TEST_TRIPLE_1); - assert_eq!(first(cache.all::()), &[ - std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), - std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1), - ]); + assert_eq!( + first(cache.all::()), + &[ + std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), + std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1), + ] + ); assert!(!cache.all::().is_empty()); // Make sure rustdoc is only built once. assert_eq!( @@ -259,9 +298,10 @@ mod defaults { // - this is the compiler it's _linked_ to, not built with. &[tool::Rustdoc { compiler: Compiler { host: a, stage: 1 } }], ); - assert_eq!(first(cache.all::()), &[ - rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0) - ],); + assert_eq!( + first(cache.all::()), + &[rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0)], + ); } #[test] @@ -270,9 +310,10 @@ mod defaults { let mut cache = run_build(&[], config); let a = TargetSelection::from_user(TEST_TRIPLE_1); - assert_eq!(first(cache.all::()), &[ - std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0) - ]); + assert_eq!( + first(cache.all::()), + &[std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0)] + ); assert!(!cache.all::().is_empty()); assert_eq!( first(cache.all::()), @@ -299,25 +340,37 @@ mod defaults { // there's not really a need for us to build for target A in this case // (since we're producing stage 1 libraries/binaries). But currently // bootstrap is just a bit buggy here; this should be fixed though. - assert_eq!(first(cache.all::()), &[ - std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), - std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1), - std!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 0), - std!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 1), - ]); - assert_eq!(first(cache.all::()), &[ - compile::Assemble { target_compiler: Compiler { host: a, stage: 0 } }, - compile::Assemble { target_compiler: Compiler { host: a, stage: 1 } }, - compile::Assemble { target_compiler: Compiler { host: b, stage: 1 } }, - ]); - assert_eq!(first(cache.all::()), &[ - tool::Rustdoc { compiler: Compiler { host: a, stage: 1 } }, - tool::Rustdoc { compiler: Compiler { host: b, stage: 1 } }, - ],); - assert_eq!(first(cache.all::()), &[ - rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), - rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 0), - ]); + assert_eq!( + first(cache.all::()), + &[ + std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), + std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1), + std!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 0), + std!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 1), + ] + ); + assert_eq!( + first(cache.all::()), + &[ + compile::Assemble { target_compiler: Compiler { host: a, stage: 0 } }, + compile::Assemble { target_compiler: Compiler { host: a, stage: 1 } }, + compile::Assemble { target_compiler: Compiler { host: b, stage: 1 } }, + ] + ); + assert_eq!( + first(cache.all::()), + &[ + tool::Rustdoc { compiler: Compiler { host: a, stage: 1 } }, + tool::Rustdoc { compiler: Compiler { host: b, stage: 1 } }, + ], + ); + assert_eq!( + first(cache.all::()), + &[ + rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), + rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 0), + ] + ); } #[test] @@ -331,15 +384,17 @@ mod defaults { // error_index_generator uses stage 0 to share rustdoc artifacts with the // rustdoc tool. assert_eq!(first(cache.all::()), &[doc::ErrorIndex { target: a },]); - assert_eq!(first(cache.all::()), &[tool::ErrorIndex { - compiler: Compiler { host: a, stage: 0 } - }]); + assert_eq!( + first(cache.all::()), + &[tool::ErrorIndex { compiler: Compiler { host: a, stage: 0 } }] + ); // docs should be built with the beta compiler, not with the stage0 artifacts. // recall that rustdoc is off-by-one: `stage` is the compiler rustdoc is _linked_ to, // not the one it was built by. - assert_eq!(first(cache.all::()), &[tool::Rustdoc { - compiler: Compiler { host: a, stage: 0 } - },]); + assert_eq!( + first(cache.all::()), + &[tool::Rustdoc { compiler: Compiler { host: a, stage: 0 } },] + ); } } @@ -361,18 +416,20 @@ mod dist { assert_eq!(first(cache.all::()), &[dist::Docs { host: a },]); assert_eq!(first(cache.all::()), &[dist::Mingw { host: a },]); - assert_eq!(first(cache.all::()), &[dist::Rustc { - compiler: Compiler { host: a, stage: 2 } - },]); - assert_eq!(first(cache.all::()), &[dist::Std { - compiler: Compiler { host: a, stage: 1 }, - target: a - },]); + assert_eq!( + first(cache.all::()), + &[dist::Rustc { compiler: Compiler { host: a, stage: 2 } },] + ); + assert_eq!( + first(cache.all::()), + &[dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },] + ); assert_eq!(first(cache.all::()), &[dist::Src]); // Make sure rustdoc is only built once. - assert_eq!(first(cache.all::()), &[tool::Rustdoc { - compiler: Compiler { host: a, stage: 2 } - },]); + assert_eq!( + first(cache.all::()), + &[tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } },] + ); } #[test] @@ -383,19 +440,25 @@ mod dist { let a = TargetSelection::from_user(TEST_TRIPLE_1); let b = TargetSelection::from_user(TEST_TRIPLE_2); - assert_eq!(first(cache.all::()), &[dist::Docs { host: a }, dist::Docs { - host: b - },]); - assert_eq!(first(cache.all::()), &[dist::Mingw { host: a }, dist::Mingw { - host: b - },]); - assert_eq!(first(cache.all::()), &[dist::Rustc { - compiler: Compiler { host: a, stage: 2 } - },]); - assert_eq!(first(cache.all::()), &[ - dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, - dist::Std { compiler: Compiler { host: a, stage: 2 }, target: b }, - ]); + assert_eq!( + first(cache.all::()), + &[dist::Docs { host: a }, dist::Docs { host: b },] + ); + assert_eq!( + first(cache.all::()), + &[dist::Mingw { host: a }, dist::Mingw { host: b },] + ); + assert_eq!( + first(cache.all::()), + &[dist::Rustc { compiler: Compiler { host: a, stage: 2 } },] + ); + assert_eq!( + first(cache.all::()), + &[ + dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, + dist::Std { compiler: Compiler { host: a, stage: 2 }, target: b }, + ] + ); assert_eq!(first(cache.all::()), &[dist::Src]); } @@ -409,27 +472,38 @@ mod dist { let a = TargetSelection::from_user(TEST_TRIPLE_1); let b = TargetSelection::from_user(TEST_TRIPLE_2); - assert_eq!(first(cache.all::()), &[dist::Docs { host: a }, dist::Docs { - host: b - },]); - assert_eq!(first(cache.all::()), &[dist::Mingw { host: a }, dist::Mingw { - host: b - },]); - assert_eq!(first(cache.all::()), &[ - dist::Rustc { compiler: Compiler { host: a, stage: 2 } }, - dist::Rustc { compiler: Compiler { host: b, stage: 2 } }, - ]); - assert_eq!(first(cache.all::()), &[ - dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, - dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, - ]); - assert_eq!(first(cache.all::()), &[ - std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), - std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1), - std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 2), - std!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 1), - std!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 2), - ],); + assert_eq!( + first(cache.all::()), + &[dist::Docs { host: a }, dist::Docs { host: b },] + ); + assert_eq!( + first(cache.all::()), + &[dist::Mingw { host: a }, dist::Mingw { host: b },] + ); + assert_eq!( + first(cache.all::()), + &[ + dist::Rustc { compiler: Compiler { host: a, stage: 2 } }, + dist::Rustc { compiler: Compiler { host: b, stage: 2 } }, + ] + ); + assert_eq!( + first(cache.all::()), + &[ + dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, + dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, + ] + ); + assert_eq!( + first(cache.all::()), + &[ + std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), + std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1), + std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 2), + std!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 1), + std!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 2), + ], + ); assert_eq!(first(cache.all::()), &[dist::Src]); } @@ -443,49 +517,56 @@ mod dist { config.hosts = vec![b]; let mut cache = run_build(&[], config); - assert_eq!(first(cache.all::()), &[dist::Rustc { - compiler: Compiler { host: b, stage: 2 } - },]); - assert_eq!(first(cache.all::()), &[ - rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), - rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 1), - ]); + assert_eq!( + first(cache.all::()), + &[dist::Rustc { compiler: Compiler { host: b, stage: 2 } },] + ); + assert_eq!( + first(cache.all::()), + &[ + rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), + rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 1), + ] + ); } #[test] fn dist_with_targets_and_hosts() { let mut cache = run_build( &[], - configure(&[TEST_TRIPLE_1, TEST_TRIPLE_2], &[ - TEST_TRIPLE_1, - TEST_TRIPLE_2, - TEST_TRIPLE_3, - ]), + configure( + &[TEST_TRIPLE_1, TEST_TRIPLE_2], + &[TEST_TRIPLE_1, TEST_TRIPLE_2, TEST_TRIPLE_3], + ), ); let a = TargetSelection::from_user(TEST_TRIPLE_1); let b = TargetSelection::from_user(TEST_TRIPLE_2); let c = TargetSelection::from_user(TEST_TRIPLE_3); - assert_eq!(first(cache.all::()), &[ - dist::Docs { host: a }, - dist::Docs { host: b }, - dist::Docs { host: c }, - ]); - assert_eq!(first(cache.all::()), &[ - dist::Mingw { host: a }, - dist::Mingw { host: b }, - dist::Mingw { host: c }, - ]); - assert_eq!(first(cache.all::()), &[ - dist::Rustc { compiler: Compiler { host: a, stage: 2 } }, - dist::Rustc { compiler: Compiler { host: b, stage: 2 } }, - ]); - assert_eq!(first(cache.all::()), &[ - dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, - dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, - dist::Std { compiler: Compiler { host: a, stage: 2 }, target: c }, - ]); + assert_eq!( + first(cache.all::()), + &[dist::Docs { host: a }, dist::Docs { host: b }, dist::Docs { host: c },] + ); + assert_eq!( + first(cache.all::()), + &[dist::Mingw { host: a }, dist::Mingw { host: b }, dist::Mingw { host: c },] + ); + assert_eq!( + first(cache.all::()), + &[ + dist::Rustc { compiler: Compiler { host: a, stage: 2 } }, + dist::Rustc { compiler: Compiler { host: b, stage: 2 } }, + ] + ); + assert_eq!( + first(cache.all::()), + &[ + dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, + dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, + dist::Std { compiler: Compiler { host: a, stage: 2 }, target: c }, + ] + ); assert_eq!(first(cache.all::()), &[dist::Src]); } @@ -499,10 +580,10 @@ mod dist { assert_eq!(first(cache.all::()), &[dist::Docs { host: c },]); assert_eq!(first(cache.all::()), &[dist::Mingw { host: c },]); - assert_eq!(first(cache.all::()), &[dist::Std { - compiler: Compiler { host: a, stage: 2 }, - target: c - },]); + assert_eq!( + first(cache.all::()), + &[dist::Std { compiler: Compiler { host: a, stage: 2 }, target: c },] + ); } #[test] @@ -515,65 +596,84 @@ mod dist { let a = TargetSelection::from_user(TEST_TRIPLE_1); let b = TargetSelection::from_user(TEST_TRIPLE_2); - assert_eq!(first(cache.all::()), &[dist::Docs { host: a }, dist::Docs { - host: b - },]); - assert_eq!(first(cache.all::()), &[dist::Mingw { host: a }, dist::Mingw { - host: b - },]); - assert_eq!(first(cache.all::()), &[ - dist::Rustc { compiler: Compiler { host: a, stage: 2 } }, - dist::Rustc { compiler: Compiler { host: b, stage: 2 } }, - ]); - assert_eq!(first(cache.all::()), &[ - dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, - dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, - ]); + assert_eq!( + first(cache.all::()), + &[dist::Docs { host: a }, dist::Docs { host: b },] + ); + assert_eq!( + first(cache.all::()), + &[dist::Mingw { host: a }, dist::Mingw { host: b },] + ); + assert_eq!( + first(cache.all::()), + &[ + dist::Rustc { compiler: Compiler { host: a, stage: 2 } }, + dist::Rustc { compiler: Compiler { host: b, stage: 2 } }, + ] + ); + assert_eq!( + first(cache.all::()), + &[ + dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a }, + dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b }, + ] + ); assert_eq!(first(cache.all::()), &[dist::Src]); - assert_eq!(first(cache.all::()), &[ - std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), - std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1), - std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 2), - std!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 1), - std!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 2), - ]); - assert_eq!(first(cache.all::()), &[ - compile::Assemble { target_compiler: Compiler { host: a, stage: 0 } }, - compile::Assemble { target_compiler: Compiler { host: a, stage: 1 } }, - compile::Assemble { target_compiler: Compiler { host: a, stage: 2 } }, - compile::Assemble { target_compiler: Compiler { host: b, stage: 2 } }, - ]); + assert_eq!( + first(cache.all::()), + &[ + std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), + std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1), + std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 2), + std!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 1), + std!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 2), + ] + ); + assert_eq!( + first(cache.all::()), + &[ + compile::Assemble { target_compiler: Compiler { host: a, stage: 0 } }, + compile::Assemble { target_compiler: Compiler { host: a, stage: 1 } }, + compile::Assemble { target_compiler: Compiler { host: a, stage: 2 } }, + compile::Assemble { target_compiler: Compiler { host: b, stage: 2 } }, + ] + ); } #[test] fn build_all() { - let build = Build::new(configure(&[TEST_TRIPLE_1, TEST_TRIPLE_2], &[ - TEST_TRIPLE_1, - TEST_TRIPLE_2, - TEST_TRIPLE_3, - ])); + let build = Build::new(configure( + &[TEST_TRIPLE_1, TEST_TRIPLE_2], + &[TEST_TRIPLE_1, TEST_TRIPLE_2, TEST_TRIPLE_3], + )); let mut builder = Builder::new(&build); - builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[ - "compiler/rustc".into(), - "library".into(), - ]); + builder.run_step_descriptions( + &Builder::get_step_descriptions(Kind::Build), + &["compiler/rustc".into(), "library".into()], + ); - assert_eq!(first(builder.cache.all::()), &[ - std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), - std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1), - std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 2), - std!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 1), - std!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 2), - std!(TEST_TRIPLE_1 => TEST_TRIPLE_3, stage = 2), - ]); + assert_eq!( + first(builder.cache.all::()), + &[ + std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), + std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1), + std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 2), + std!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 1), + std!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 2), + std!(TEST_TRIPLE_1 => TEST_TRIPLE_3, stage = 2), + ] + ); assert_eq!(builder.cache.all::().len(), 5); - assert_eq!(first(builder.cache.all::()), &[ - rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), - rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1), - rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 2), - rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 1), - rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 2), - ]); + assert_eq!( + first(builder.cache.all::()), + &[ + rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), + rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1), + rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 2), + rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 1), + rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_2, stage = 2), + ] + ); } #[test] @@ -602,20 +702,29 @@ mod dist { let a = TargetSelection::from_user(TEST_TRIPLE_1); - assert_eq!(first(builder.cache.all::()), &[ - std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), - std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1), - std!(TEST_TRIPLE_1 => TEST_TRIPLE_3, stage = 2), - ]); - assert_eq!(first(builder.cache.all::()), &[ - compile::Assemble { target_compiler: Compiler { host: a, stage: 0 } }, - compile::Assemble { target_compiler: Compiler { host: a, stage: 1 } }, - compile::Assemble { target_compiler: Compiler { host: a, stage: 2 } }, - ]); - assert_eq!(first(builder.cache.all::()), &[ - rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), - rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1), - ]); + assert_eq!( + first(builder.cache.all::()), + &[ + std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), + std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1), + std!(TEST_TRIPLE_1 => TEST_TRIPLE_3, stage = 2), + ] + ); + assert_eq!( + first(builder.cache.all::()), + &[ + compile::Assemble { target_compiler: Compiler { host: a, stage: 0 } }, + compile::Assemble { target_compiler: Compiler { host: a, stage: 1 } }, + compile::Assemble { target_compiler: Compiler { host: a, stage: 2 } }, + ] + ); + assert_eq!( + first(builder.cache.all::()), + &[ + rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0), + rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1), + ] + ); } #[test] @@ -645,18 +754,22 @@ mod dist { let host = TargetSelection::from_user(TEST_TRIPLE_1); - builder.run_step_descriptions(&[StepDescription::from::(Kind::Test)], &[ - "library/std".into(), - ]); + builder.run_step_descriptions( + &[StepDescription::from::(Kind::Test)], + &["library/std".into()], + ); // Ensure we don't build any compiler artifacts. assert!(!builder.cache.contains::()); - assert_eq!(first(builder.cache.all::()), &[test::Crate { - compiler: Compiler { host, stage: 0 }, - target: host, - mode: crate::Mode::Std, - crates: vec!["std".to_owned()], - },]); + assert_eq!( + first(builder.cache.all::()), + &[test::Crate { + compiler: Compiler { host, stage: 0 }, + target: host, + mode: crate::Mode::Std, + crates: vec!["std".to_owned()], + },] + ); } #[test] @@ -675,14 +788,16 @@ mod dist { first(builder.cache.all::()), &[doc::ErrorIndex { target: a },] ); - assert_eq!(first(builder.cache.all::()), &[tool::ErrorIndex { - compiler: Compiler { host: a, stage: 1 } - }]); + assert_eq!( + first(builder.cache.all::()), + &[tool::ErrorIndex { compiler: Compiler { host: a, stage: 1 } }] + ); // This is actually stage 1, but Rustdoc::run swaps out the compiler with // stage minus 1 if --stage is not 0. Very confusing! - assert_eq!(first(builder.cache.all::()), &[tool::Rustdoc { - compiler: Compiler { host: a, stage: 2 } - },]); + assert_eq!( + first(builder.cache.all::()), + &[tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } },] + ); } #[test] @@ -719,9 +834,10 @@ mod dist { first(builder.cache.all::()), &[doc::ErrorIndex { target: a },] ); - assert_eq!(first(builder.cache.all::()), &[tool::ErrorIndex { - compiler: Compiler { host: a, stage: 1 } - }]); + assert_eq!( + first(builder.cache.all::()), + &[tool::ErrorIndex { compiler: Compiler { host: a, stage: 1 } }] + ); // Unfortunately rustdoc is built twice. Once from stage1 for compiletest // (and other things), and once from stage0 for std crates. Ideally it // would only be built once. If someone wants to fix this, it might be @@ -733,11 +849,14 @@ mod dist { // The stage 0 copy is the one downloaded for bootstrapping. It is // (currently) needed to run "cargo test" on the linkchecker, and // should be relatively "free". - assert_eq!(first(builder.cache.all::()), &[ - tool::Rustdoc { compiler: Compiler { host: a, stage: 0 } }, - tool::Rustdoc { compiler: Compiler { host: a, stage: 1 } }, - tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } }, - ]); + assert_eq!( + first(builder.cache.all::()), + &[ + tool::Rustdoc { compiler: Compiler { host: a, stage: 0 } }, + tool::Rustdoc { compiler: Compiler { host: a, stage: 1 } }, + tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } }, + ] + ); } } @@ -804,3 +923,164 @@ fn test_test_compiler() { assert_eq!((compiler, cranelift, gcc), (true, false, false)); } + +#[test] +fn test_test_coverage() { + struct Case { + cmd: &'static [&'static str], + expected: &'static [&'static str], + } + let cases = &[ + Case { cmd: &["test"], expected: &["coverage-map", "coverage-run"] }, + Case { cmd: &["test", "coverage"], expected: &["coverage-map", "coverage-run"] }, + Case { cmd: &["test", "coverage-map"], expected: &["coverage-map"] }, + Case { cmd: &["test", "coverage-run"], expected: &["coverage-run"] }, + Case { cmd: &["test", "coverage", "--skip=coverage"], expected: &[] }, + Case { cmd: &["test", "coverage", "--skip=tests/coverage"], expected: &[] }, + Case { cmd: &["test", "coverage", "--skip=coverage-map"], expected: &["coverage-run"] }, + Case { cmd: &["test", "coverage", "--skip=coverage-run"], expected: &["coverage-map"] }, + Case { cmd: &["test", "--skip=coverage-map", "--skip=coverage-run"], expected: &[] }, + Case { cmd: &["test", "coverage", "--skip=tests"], expected: &[] }, + ]; + + for &Case { cmd, expected } in cases { + // Print each test case so that if one fails, the most recently printed + // case is the one that failed. + println!("Testing case: {cmd:?}"); + let cmd = cmd.iter().copied().map(str::to_owned).collect::>(); + let config = configure_with_args(&cmd, &[TEST_TRIPLE_1], &[TEST_TRIPLE_1]); + let mut cache = run_build(&config.paths.clone(), config); + + let modes = + cache.all::().iter().map(|(step, ())| step.mode).collect::>(); + assert_eq!(modes, expected); + } +} + +#[test] +fn test_prebuilt_llvm_config_path_resolution() { + fn configure(config: &str) -> Config { + Config::parse_inner( + Flags::parse(&[ + "build".to_string(), + "--dry-run".to_string(), + "--config=/does/not/exist".to_string(), + ]), + |&_| toml::from_str(&config), + ) + } + + // Removes Windows disk prefix if present + fn drop_win_disk_prefix_if_present(path: PathBuf) -> PathBuf { + let path_str = path.to_str().unwrap(); + if let Some((_, without_prefix)) = path_str.split_once(":/") { + return PathBuf::from(format!("/{}", without_prefix)); + } + + path + } + + let config = configure( + r#" + [llvm] + download-ci-llvm = false + + [build] + build = "x86_64-unknown-linux-gnu" + host = ["arm-unknown-linux-gnueabihf"] + target = ["arm-unknown-linux-gnueabihf"] + + [target.x86_64-unknown-linux-gnu] + llvm-config = "/some/path/to/llvm-config" + + [target.arm-unknown-linux-gnueabihf] + cc = "arm-linux-gnueabihf-gcc" + cxx = "arm-linux-gnueabihf-g++" + "#, + ); + + let build = Build::new(config); + let builder = Builder::new(&build); + + let expected = PathBuf::from("/some/path/to/llvm-config"); + + let actual = prebuilt_llvm_config( + &builder, + TargetSelection::from_user("arm-unknown-linux-gnueabihf"), + false, + ) + .llvm_result() + .llvm_config + .clone(); + let actual = drop_win_disk_prefix_if_present(actual); + assert_eq!(expected, actual); + + let actual = prebuilt_llvm_config(&builder, builder.config.build, false) + .llvm_result() + .llvm_config + .clone(); + let actual = drop_win_disk_prefix_if_present(actual); + assert_eq!(expected, actual); + assert_eq!(expected, actual); + + let config = configure( + r#" + [llvm] + download-ci-llvm = false + "#, + ); + + let build = Build::new(config.clone()); + let builder = Builder::new(&build); + + let actual = prebuilt_llvm_config(&builder, builder.config.build, false) + .llvm_result() + .llvm_config + .clone(); + let expected = builder + .out + .join(builder.config.build) + .join("llvm/bin") + .join(exe("llvm-config", builder.config.build)); + assert_eq!(expected, actual); + + let config = configure( + r#" + [llvm] + download-ci-llvm = true + "#, + ); + + // CI-LLVM isn't always available; check if it's enabled before testing. + if config.llvm_from_ci { + let build = Build::new(config.clone()); + let builder = Builder::new(&build); + + let actual = prebuilt_llvm_config(&builder, builder.config.build, false) + .llvm_result() + .llvm_config + .clone(); + let expected = builder + .out + .join(builder.config.build) + .join("ci-llvm/bin") + .join(exe("llvm-config", builder.config.build)); + assert_eq!(expected, actual); + } +} + +#[test] +fn test_is_builder_target() { + let target1 = TargetSelection::from_user(TEST_TRIPLE_1); + let target2 = TargetSelection::from_user(TEST_TRIPLE_2); + + for (target1, target2) in [(target1, target2), (target2, target1)] { + let mut config = configure("build", &[], &[]); + config.build = target1; + let build = Build::new(config); + let builder = Builder::new(&build); + + assert!(builder.is_builder_target(&target1)); + assert!(!builder.is_builder_target(&target2)); + } +} diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index dd2f11ad4690a..65f286a05bd5c 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -18,6 +18,8 @@ use build_helper::exit; use build_helper::git::{GitConfig, get_closest_merge_commit, output_result}; use serde::{Deserialize, Deserializer}; use serde_derive::Deserialize; +#[cfg(feature = "tracing")] +use tracing::{instrument, span}; use crate::core::build_steps::compile::CODEGEN_BACKEND_PREFIX; use crate::core::build_steps::llvm; @@ -42,6 +44,8 @@ use crate::utils::helpers::{self, exe, output, t}; #[rustfmt::skip] // We don't want rustfmt to oneline this list pub(crate) const RUSTC_IF_UNCHANGED_ALLOWED_PATHS: &[&str] = &[ ":!src/tools", + ":!src/librustdoc", + ":!src/rustdoc-json-types", ":!tests", ":!triagebot.toml", ]; @@ -571,6 +575,10 @@ impl TargetSelection { env::var("OSTYPE").is_ok_and(|v| v.to_lowercase().contains("cygwin")) } + pub fn needs_crt_begin_end(&self) -> bool { + self.contains("musl") && !self.contains("unikraft") + } + /// Path to the file defining the custom target, if any. pub fn filepath(&self) -> Option<&Path> { self.file.as_ref().map(Path::new) @@ -634,6 +642,7 @@ pub struct Target { pub runner: Option, pub no_std: bool, pub codegen_backends: Option>, + pub optimized_compiler_builtins: Option, } impl Target { @@ -695,7 +704,7 @@ trait Merge { impl Merge for TomlConfig { fn merge( &mut self, - TomlConfig { build, install, llvm, rust, dist, target, profile: _, change_id }: Self, + TomlConfig { build, install, llvm, rust, dist, target, profile, change_id }: Self, replace: ReplaceOpt, ) { fn do_merge(x: &mut Option, y: Option, replace: ReplaceOpt) { @@ -707,7 +716,10 @@ impl Merge for TomlConfig { } } } + self.change_id.inner.merge(change_id.inner, replace); + self.profile.merge(profile, replace); + do_merge(&mut self.build, build, replace); do_merge(&mut self.install, install, replace); do_merge(&mut self.llvm, llvm, replace); @@ -923,6 +935,7 @@ define_config! { optimized_compiler_builtins: Option = "optimized-compiler-builtins", jobs: Option = "jobs", compiletest_diff_tool: Option = "compiletest-diff-tool", + ccache: Option = "ccache", } } @@ -949,6 +962,7 @@ define_config! { tests: Option = "tests", enzyme: Option = "enzyme", plugins: Option = "plugins", + // FIXME: Remove this field at Q2 2025, it has been replaced by build.ccache ccache: Option = "ccache", static_libstdcpp: Option = "static-libstdcpp", libzstd: Option = "libzstd", @@ -1125,7 +1139,7 @@ impl<'de> Deserialize<'de> for LldMode { match v { "external" => Ok(LldMode::External), "self-contained" => Ok(LldMode::SelfContained), - _ => Err(E::custom("unknown mode {v}")), + _ => Err(E::custom(format!("unknown mode {v}"))), } } } @@ -1219,11 +1233,19 @@ define_config! { no_std: Option = "no-std", codegen_backends: Option> = "codegen-backends", runner: Option = "runner", + optimized_compiler_builtins: Option = "optimized-compiler-builtins", } } impl Config { + #[cfg_attr( + feature = "tracing", + instrument(target = "CONFIG_HANDLING", level = "trace", name = "Config::default_opts") + )] pub fn default_opts() -> Config { + #[cfg(feature = "tracing")] + span!(target: "CONFIG_HANDLING", tracing::Level::TRACE, "constructing default config"); + Config { bypass_bootstrap_lock: false, llvm_optimize: true, @@ -1307,10 +1329,23 @@ impl Config { }) } + #[cfg_attr( + feature = "tracing", + instrument(target = "CONFIG_HANDLING", level = "trace", name = "Config::parse", skip_all) + )] pub fn parse(flags: Flags) -> Config { Self::parse_inner(flags, Self::get_toml) } + #[cfg_attr( + feature = "tracing", + instrument( + target = "CONFIG_HANDLING", + level = "trace", + name = "Config::parse_inner", + skip_all + ) + )] pub(crate) fn parse_inner( mut flags: Flags, get_toml: impl Fn(&Path) -> Result, @@ -1319,6 +1354,17 @@ impl Config { // Set flags. config.paths = std::mem::take(&mut flags.paths); + + #[cfg(feature = "tracing")] + span!( + target: "CONFIG_HANDLING", + tracing::Level::TRACE, + "collecting paths and path exclusions", + "flags.paths" = ?flags.paths, + "flags.skip" = ?flags.skip, + "flags.exclude" = ?flags.exclude + ); + config.skip = flags .skip .into_iter() @@ -1335,6 +1381,14 @@ impl Config { }) .collect(); + #[cfg(feature = "tracing")] + span!( + target: "CONFIG_HANDLING", + tracing::Level::TRACE, + "normalizing and combining `flag.skip`/`flag.exclude` paths", + "config.skip" = ?config.skip, + ); + config.include_default_paths = flags.include_default_paths; config.rustc_error_format = flags.rustc_error_format; config.json_output = flags.json_output; @@ -1414,7 +1468,11 @@ impl Config { config.stage0_metadata = build_helper::stage0_parser::parse_stage0_file(); - // Read from `--config`, then `RUST_BOOTSTRAP_CONFIG`, then `./config.toml`, then `config.toml` in the root directory. + // Find configuration file, with the following cascading fallback (first match wins): + // - `--config ` + // - `RUST_BOOTSTRAP_CONFIG` + // - `./config.toml` + // - `config.toml` in the root directory. let toml_path = flags .config .clone() @@ -1456,6 +1514,10 @@ impl Config { build.cargo = build.cargo.take().or(std::env::var_os("CARGO").map(|p| p.into())); } + if GitInfo::new(false, &config.src).is_from_tarball() && toml.profile.is_none() { + toml.profile = Some("dist".into()); + } + if let Some(include) = &toml.profile { // Allows creating alias for profile names, allowing // profiles to be renamed while maintaining back compatibility @@ -1562,6 +1624,7 @@ impl Config { optimized_compiler_builtins, jobs, compiletest_diff_tool, + mut ccache, } = toml.build.unwrap_or_default(); config.jobs = Some(threads_from_config(flags.jobs.unwrap_or(jobs.unwrap_or(0)))); @@ -1868,11 +1931,14 @@ impl Config { config.rustc_default_linker = default_linker; config.musl_root = musl_root.map(PathBuf::from); config.save_toolstates = save_toolstates.map(PathBuf::from); - set(&mut config.deny_warnings, match flags.warnings { - Warnings::Deny => Some(true), - Warnings::Warn => Some(false), - Warnings::Default => deny_warnings, - }); + set( + &mut config.deny_warnings, + match flags.warnings { + Warnings::Deny => Some(true), + Warnings::Warn => Some(false), + Warnings::Default => deny_warnings, + }, + ); set(&mut config.backtrace_on_ice, backtrace_on_ice); set(&mut config.rust_verify_llvm_ir, verify_llvm_ir); config.rust_thin_lto_import_instr_limit = thin_lto_import_instr_limit; @@ -1943,7 +2009,7 @@ impl Config { tests, enzyme, plugins, - ccache, + ccache: llvm_ccache, static_libstdcpp, libzstd, ninja, @@ -1966,13 +2032,11 @@ impl Config { download_ci_llvm, build_config, } = llvm; - match ccache { - Some(StringOrBool::String(ref s)) => config.ccache = Some(s.to_string()), - Some(StringOrBool::Bool(true)) => { - config.ccache = Some("ccache".to_string()); - } - Some(StringOrBool::Bool(false)) | None => {} + if llvm_ccache.is_some() { + eprintln!("Warning: llvm.ccache is deprecated. Use build.ccache instead."); } + + ccache = ccache.or(llvm_ccache); set(&mut config.ninja_in_file, ninja); llvm_tests = tests; llvm_enzyme = enzyme; @@ -2096,6 +2160,7 @@ impl Config { target.sanitizers = cfg.sanitizers; target.profiler = cfg.profiler; target.rpath = cfg.rpath; + target.optimized_compiler_builtins = cfg.optimized_compiler_builtins; if let Some(ref backends) = cfg.codegen_backends { let available_backends = ["llvm", "cranelift", "gcc"]; @@ -2125,6 +2190,14 @@ impl Config { } } + match ccache { + Some(StringOrBool::String(ref s)) => config.ccache = Some(s.to_string()), + Some(StringOrBool::Bool(true)) => { + config.ccache = Some("ccache".to_string()); + } + Some(StringOrBool::Bool(false)) | None => {} + } + if config.llvm_from_ci { let triple = &config.build.triple; let ci_llvm_bin = config.ci_llvm_root().join("bin"); @@ -2609,6 +2682,13 @@ impl Config { self.target_config.get(&target).and_then(|t| t.rpath).unwrap_or(self.rust_rpath) } + pub fn optimized_compiler_builtins(&self, target: TargetSelection) -> bool { + self.target_config + .get(&target) + .and_then(|t| t.optimized_compiler_builtins) + .unwrap_or(self.optimized_compiler_builtins) + } + pub fn llvm_enabled(&self, target: TargetSelection) -> bool { self.codegen_backends(target).contains(&"llvm".to_owned()) } @@ -2668,7 +2748,7 @@ impl Config { /// used instead to provide a nice error to the user if the submodule is /// missing. pub(crate) fn update_submodule(&self, relative_path: &str) { - if !self.submodules() { + if self.rust_info.is_from_tarball() || !self.submodules() { return; } @@ -2863,21 +2943,26 @@ impl Config { allowed_paths.push(":!library"); } - // Look for a version to compare to based on the current commit. - // Only commits merged by bors will have CI artifacts. - let commit = match self.last_modified_commit(&allowed_paths, "download-rustc", if_unchanged) - { - Some(commit) => commit, - None => { - if if_unchanged { - return None; + let commit = if self.rust_info.is_managed_git_subrepository() { + // Look for a version to compare to based on the current commit. + // Only commits merged by bors will have CI artifacts. + match self.last_modified_commit(&allowed_paths, "download-rustc", if_unchanged) { + Some(commit) => commit, + None => { + if if_unchanged { + return None; + } + println!("ERROR: could not find commit hash for downloading rustc"); + println!("HELP: maybe your repository history is too shallow?"); + println!("HELP: consider setting `rust.download-rustc=false` in config.toml"); + println!("HELP: or fetch enough history to include one upstream commit"); + crate::exit!(1); } - println!("ERROR: could not find commit hash for downloading rustc"); - println!("HELP: maybe your repository history is too shallow?"); - println!("HELP: consider setting `rust.download-rustc=false` in config.toml"); - println!("HELP: or fetch enough history to include one upstream commit"); - crate::exit!(1); } + } else { + channel::read_commit_info_file(&self.src) + .map(|info| info.sha.trim().to_owned()) + .expect("git-commit-info is missing in the project root") }; if CiEnv::is_ci() && { @@ -2914,10 +2999,8 @@ impl Config { let if_unchanged = || { if self.rust_info.is_from_tarball() { // Git is needed for running "if-unchanged" logic. - println!( - "WARNING: 'if-unchanged' has no effect on tarball sources; ignoring `download-ci-llvm`." - ); - return false; + println!("ERROR: 'if-unchanged' is only compatible with Git managed sources."); + crate::exit!(1); } // Fetching the LLVM submodule is unnecessary for self-tests. @@ -2959,6 +3042,11 @@ impl Config { option_name: &str, if_unchanged: bool, ) -> Option { + assert!( + self.rust_info.is_managed_git_subrepository(), + "Can't run `Config::last_modified_commit` on a non-git source." + ); + // Look for a version to compare to based on the current commit. // Only commits merged by bors will have CI artifacts. let commit = get_closest_merge_commit(Some(&self.src), &self.git_config(), &[]).unwrap(); @@ -3162,6 +3250,9 @@ fn check_incompatible_options_for_ci_rustc( let profiler = &ci_cfg.profiler; err!(current_cfg.profiler, profiler, "build"); + + let optimized_compiler_builtins = &ci_cfg.optimized_compiler_builtins; + err!(current_cfg.optimized_compiler_builtins, optimized_compiler_builtins, "build"); } } diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index f17103f97dc40..c08a041ebcd3f 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -6,7 +6,10 @@ use std::path::{Path, PathBuf}; use clap::{CommandFactory, Parser, ValueEnum}; +#[cfg(feature = "tracing")] +use tracing::instrument; +use crate::core::build_steps::perf::PerfArgs; use crate::core::build_steps::setup::Profile; use crate::core::builder::{Builder, Kind}; use crate::core::config::{Config, TargetSelectionList, target_selection_list}; @@ -211,6 +214,10 @@ impl Flags { } } + #[cfg_attr( + feature = "tracing", + instrument(level = "trace", name = "Flags::parse", skip_all, fields(args = ?args)) + )] pub fn parse(args: &[String]) -> Self { Flags::parse_from(normalize_args(args)) } @@ -475,11 +482,8 @@ Arguments: #[arg(long)] versioned_dirs: bool, }, - /// Perform profiling and benchmarking of the compiler using the - /// `rustc-perf-wrapper` tool. - /// - /// You need to pass arguments after `--`, e.g.`x perf -- cachegrind`. - Perf {}, + /// Perform profiling and benchmarking of the compiler using `rustc-perf`. + Perf(PerfArgs), } impl Subcommand { diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index 24f932a172432..f0a185ee3a7eb 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -92,7 +92,7 @@ fn detect_src_and_out() { // `{build-dir}/bootstrap/debug/deps/bootstrap-c7ee91d5661e2804` // `{build-dir}` can be anywhere, not just in the rust project directory. let dep = Path::new(args.first().unwrap()); - let expected_out = dep.ancestors().nth(4).unwrap(); + let expected_out = dep.ancestors().nth(5).unwrap(); assert_eq!(&cfg.out, expected_out); } @@ -120,6 +120,7 @@ fn override_toml() { "--set=change-id=1".to_owned(), "--set=rust.lto=fat".to_owned(), "--set=rust.deny-warnings=false".to_owned(), + "--set=build.optimized-compiler-builtins=true".to_owned(), "--set=build.gdb=\"bar\"".to_owned(), "--set=build.tools=[\"cargo\"]".to_owned(), "--set=llvm.build-config={\"foo\" = \"bar\"}".to_owned(), @@ -127,6 +128,7 @@ fn override_toml() { "--set=target.x86_64-unknown-linux-gnu.rpath=false".to_owned(), "--set=target.aarch64-unknown-linux-gnu.sanitizers=false".to_owned(), "--set=target.aarch64-apple-darwin.runner=apple".to_owned(), + "--set=target.aarch64-apple-darwin.optimized-compiler-builtins=false".to_owned(), ]), |&_| { toml::from_str( @@ -167,6 +169,7 @@ runner = "x86_64-runner" ); assert_eq!(config.gdb, Some("bar".into()), "setting string value with quotes"); assert!(!config.deny_warnings, "setting boolean value"); + assert!(config.optimized_compiler_builtins, "setting boolean value"); assert_eq!( config.tools, Some(["cargo".to_string()].into_iter().collect()), @@ -193,7 +196,11 @@ runner = "x86_64-runner" ..Default::default() }; let darwin = TargetSelection::from_user("aarch64-apple-darwin"); - let darwin_values = Target { runner: Some("apple".into()), ..Default::default() }; + let darwin_values = Target { + runner: Some("apple".into()), + optimized_compiler_builtins: Some(false), + ..Default::default() + }; assert_eq!( config.target_config, [(x86_64, x86_64_values), (aarch64, aarch64_values), (darwin, darwin_values)] diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index b5f7ed5313122..c477bdb829a91 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -10,8 +10,9 @@ use build_helper::ci::CiEnv; use xz2::bufread::XzDecoder; use crate::core::config::BUILDER_CONFIG_FILENAME; +use crate::utils::build_stamp::BuildStamp; use crate::utils::exec::{BootstrapCommand, command}; -use crate::utils::helpers::{check_run, exe, hex_encode, move_file, program_out_of_date}; +use crate::utils::helpers::{check_run, exe, hex_encode, move_file}; use crate::{Config, t}; static SHOULD_FIX_BINS_AND_DYLIBS: OnceLock = OnceLock::new(); @@ -46,7 +47,7 @@ impl Config { self.verbose > 0 } - pub(crate) fn create(&self, path: &Path, s: &str) { + pub(crate) fn create>(&self, path: P, s: &str) { if self.dry_run() { return; } @@ -426,9 +427,10 @@ impl Config { let version = &self.stage0_metadata.compiler.version; let host = self.build; - let clippy_stamp = self.initial_sysroot.join(".clippy-stamp"); + let clippy_stamp = + BuildStamp::new(&self.initial_sysroot).with_prefix("clippy").add_stamp(date); let cargo_clippy = self.initial_sysroot.join("bin").join(exe("cargo-clippy", host)); - if cargo_clippy.exists() && !program_out_of_date(&clippy_stamp, date) { + if cargo_clippy.exists() && clippy_stamp.is_up_to_date() { return cargo_clippy; } @@ -439,7 +441,7 @@ impl Config { self.fix_bin_or_dylib(&cargo_clippy.with_file_name(exe("clippy-driver", host))); } - self.create(&clippy_stamp, date); + t!(clippy_stamp.write()); cargo_clippy } @@ -460,8 +462,8 @@ impl Config { let host = self.build; let bin_root = self.out.join(host).join("rustfmt"); let rustfmt_path = bin_root.join("bin").join(exe("rustfmt", host)); - let rustfmt_stamp = bin_root.join(".rustfmt-stamp"); - if rustfmt_path.exists() && !program_out_of_date(&rustfmt_stamp, &channel) { + let rustfmt_stamp = BuildStamp::new(&bin_root).with_prefix("rustfmt").add_stamp(channel); + if rustfmt_path.exists() && rustfmt_stamp.is_up_to_date() { return Some(rustfmt_path); } @@ -492,7 +494,7 @@ impl Config { } } - self.create(&rustfmt_stamp, &channel); + t!(rustfmt_stamp.write()); Some(rustfmt_path) } @@ -567,10 +569,10 @@ impl Config { ) { let host = self.build.triple; let bin_root = self.out.join(host).join(sysroot); - let rustc_stamp = bin_root.join(".rustc-stamp"); + let rustc_stamp = BuildStamp::new(&bin_root).with_prefix("rustc").add_stamp(stamp_key); if !bin_root.join("bin").join(exe("rustc", self.build)).exists() - || program_out_of_date(&rustc_stamp, stamp_key) + || !rustc_stamp.is_up_to_date() { if bin_root.exists() { t!(fs::remove_dir_all(&bin_root)); @@ -601,7 +603,7 @@ impl Config { } } - t!(fs::write(rustc_stamp, stamp_key)); + t!(rustc_stamp.write()); } } @@ -728,10 +730,10 @@ download-rustc = false } let llvm_root = self.ci_llvm_root(); - let llvm_stamp = llvm_root.join(".llvm-stamp"); let llvm_sha = detect_llvm_sha(self, self.rust_info.is_managed_git_subrepository()); - let key = format!("{}{}", llvm_sha, self.llvm_assertions); - if program_out_of_date(&llvm_stamp, &key) && !self.dry_run() { + let stamp_key = format!("{}{}", llvm_sha, self.llvm_assertions); + let llvm_stamp = BuildStamp::new(&llvm_root).with_prefix("llvm").add_stamp(stamp_key); + if !llvm_stamp.is_up_to_date() && !self.dry_run() { self.download_ci_llvm(&llvm_sha); if self.should_fix_bins_and_dylibs() { @@ -764,7 +766,7 @@ download-rustc = false } } - t!(fs::write(llvm_stamp, key)); + t!(llvm_stamp.write()); } if let Some(config_path) = &self.config { diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index ed0155622c226..9e4a0816e0d48 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -34,6 +34,10 @@ pub struct Finder { // Targets can be removed from this list once they are present in the stage0 compiler (usually by updating the beta compiler of the bootstrap). const STAGE0_MISSING_TARGETS: &[&str] = &[ // just a dummy comment so the list doesn't get onelined + "aarch64-unknown-nto-qnx710_iosock", + "x86_64-pc-nto-qnx710_iosock", + "x86_64-pc-nto-qnx800", + "aarch64-unknown-nto-qnx800", ]; /// Minimum version threshold for libstdc++ required when using prebuilt LLVM @@ -325,7 +329,7 @@ than building it. if target.contains("musl") && !target.contains("unikraft") { // If this is a native target (host is also musl) and no musl-root is given, // fall back to the system toolchain in /usr before giving up - if build.musl_root(*target).is_none() && build.config.build == *target { + if build.musl_root(*target).is_none() && build.is_builder_target(target) { let target = build.config.target_config.entry(*target).or_default(); target.musl_root = Some("/usr".into()); } diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 8405c22aff08f..21b02a3b541a1 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -19,27 +19,23 @@ use std::cell::{Cell, RefCell}; use std::collections::{BTreeSet, HashMap, HashSet}; use std::fmt::Display; -use std::fs::{self, File}; use std::path::{Path, PathBuf}; use std::process::Command; use std::sync::OnceLock; use std::time::SystemTime; -use std::{env, io, str}; +use std::{env, fs, io, str}; use build_helper::ci::gha; use build_helper::exit; -use sha2::digest::Digest; use termcolor::{ColorChoice, StandardStream, WriteColor}; +use utils::build_stamp::BuildStamp; use utils::channel::GitInfo; -use utils::helpers::hex_encode; use crate::core::builder; -use crate::core::builder::{Builder, Kind}; +use crate::core::builder::Kind; use crate::core::config::{DryRun, LldMode, LlvmLibunwind, Target, TargetSelection, flags}; use crate::utils::exec::{BehaviorOnFailure, BootstrapCommand, CommandOutput, OutputMode, command}; -use crate::utils::helpers::{ - self, dir_is_empty, exe, libdir, mtime, output, set_file_times, symlink_dir, -}; +use crate::utils::helpers::{self, dir_is_empty, exe, libdir, output, set_file_times, symlink_dir}; mod core; mod utils; @@ -48,10 +44,14 @@ pub use core::builder::PathSet; pub use core::config::Config; pub use core::config::flags::{Flags, Subcommand}; +#[cfg(feature = "tracing")] +use tracing::{instrument, span}; pub use utils::change_tracker::{ CONFIG_CHANGE_HISTORY, find_recent_config_change_ids, human_readable_changes, }; +use crate::core::build_steps::vendor::VENDOR_DIR; + const LLVM_TOOLS: &[&str] = &[ "llvm-cov", // used to generate coverage report "llvm-nm", // used to inspect binaries; it shows symbol names, their sizes and visibility @@ -77,8 +77,6 @@ const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"]; #[allow(clippy::type_complexity)] // It's fine for hard-coded list and type is explained above. const EXTRA_CHECK_CFGS: &[(Option, &str, Option<&[&'static str]>)] = &[ (None, "bootstrap", None), - // #[cfg(bootstrap)] to be removed when Cargo is updated - (None, "test", None), (Some(Mode::Rustc), "llvm_enzyme", None), (Some(Mode::Codegen), "llvm_enzyme", None), (Some(Mode::ToolRustc), "llvm_enzyme", None), @@ -158,6 +156,7 @@ pub struct Build { targets: Vec, initial_rustc: PathBuf, + initial_rustdoc: PathBuf, initial_cargo: PathBuf, initial_lld: PathBuf, initial_relative_libdir: PathBuf, @@ -252,6 +251,7 @@ impl Mode { } } +#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum CLang { C, Cxx, @@ -358,6 +358,7 @@ impl Build { initial_lld, initial_relative_libdir, initial_rustc: config.initial_rustc.clone(), + initial_rustdoc: config.initial_rustc.with_file_name(exe("rustdoc", config.build)), initial_cargo: config.initial_cargo.clone(), initial_sysroot: config.initial_sysroot.clone(), local_rebuild: config.local_rebuild, @@ -471,6 +472,10 @@ impl Build { /// The given `err_hint` will be shown to the user if the submodule is not /// checked out and submodule management is disabled. pub fn require_submodule(&self, submodule: &str, err_hint: Option<&str>) { + if self.rust_info().is_from_tarball() { + return; + } + // When testing bootstrap itself, it is much faster to ignore // submodules. Almost all Steps work fine without their submodules. if cfg!(test) && !self.config.submodules() { @@ -543,49 +548,84 @@ impl Build { } /// Executes the entire build, as configured by the flags and configuration. + #[cfg_attr(feature = "tracing", instrument(level = "debug", name = "Build::build", skip_all))] pub fn build(&mut self) { + trace!("setting up job management"); unsafe { crate::utils::job::setup(self); } // Download rustfmt early so that it can be used in rust-analyzer configs. + trace!("downloading rustfmt early"); let _ = &builder::Builder::new(self).initial_rustfmt(); - // hardcoded subcommands - match &self.config.cmd { - Subcommand::Format { check, all } => { - return core::build_steps::format::format( - &builder::Builder::new(self), - *check, - *all, - &self.config.paths, - ); - } - Subcommand::Suggest { run } => { - return core::build_steps::suggest::suggest(&builder::Builder::new(self), *run); - } - Subcommand::Perf { .. } => { - return core::build_steps::perf::perf(&builder::Builder::new(self)); + // Handle hard-coded subcommands. + { + #[cfg(feature = "tracing")] + let _hardcoded_span = span!( + tracing::Level::DEBUG, + "handling hardcoded subcommands (Format, Suggest, Perf)" + ) + .entered(); + + match &self.config.cmd { + Subcommand::Format { check, all } => { + return core::build_steps::format::format( + &builder::Builder::new(self), + *check, + *all, + &self.config.paths, + ); + } + Subcommand::Suggest { run } => { + return core::build_steps::suggest::suggest(&builder::Builder::new(self), *run); + } + Subcommand::Perf(args) => { + return core::build_steps::perf::perf(&builder::Builder::new(self), args); + } + _cmd => { + debug!(cmd = ?_cmd, "not a hardcoded subcommand; returning to normal handling"); + } } - _ => (), + + debug!("handling subcommand normally"); } if !self.config.dry_run() { + #[cfg(feature = "tracing")] + let _real_run_span = span!(tracing::Level::DEBUG, "executing real run").entered(); + + // We first do a dry-run. This is a sanity-check to ensure that + // steps don't do anything expensive in the dry-run. { - // We first do a dry-run. This is a sanity-check to ensure that - // steps don't do anything expensive in the dry-run. + #[cfg(feature = "tracing")] + let _sanity_check_span = + span!(tracing::Level::DEBUG, "(1) executing dry-run sanity-check").entered(); self.config.dry_run = DryRun::SelfCheck; let builder = builder::Builder::new(self); builder.execute_cli(); } - self.config.dry_run = DryRun::Disabled; - let builder = builder::Builder::new(self); - builder.execute_cli(); + + // Actual run. + { + #[cfg(feature = "tracing")] + let _actual_run_span = + span!(tracing::Level::DEBUG, "(2) executing actual run").entered(); + self.config.dry_run = DryRun::Disabled; + let builder = builder::Builder::new(self); + builder.execute_cli(); + } } else { + #[cfg(feature = "tracing")] + let _dry_run_span = span!(tracing::Level::DEBUG, "executing dry run").entered(); + let builder = builder::Builder::new(self); builder.execute_cli(); } + #[cfg(feature = "tracing")] + debug!("checking for postponed test failures from `test --no-fail-fast`"); + // Check for postponed failures from `test --no-fail-fast`. let failures = self.delayed_failures.borrow(); if failures.len() > 0 { @@ -600,24 +640,6 @@ impl Build { self.metrics.persist(self); } - /// Clear out `dir` if `input` is newer. - /// - /// After this executes, it will also ensure that `dir` exists. - fn clear_if_dirty(&self, dir: &Path, input: &Path) -> bool { - let stamp = dir.join(".stamp"); - let mut cleared = false; - if mtime(&stamp) < mtime(input) { - self.verbose(|| println!("Dirty - {}", dir.display())); - let _ = fs::remove_dir_all(dir); - cleared = true; - } else if stamp.exists() { - return cleared; - } - t!(fs::create_dir_all(dir)); - t!(File::create(stamp)); - cleared - } - fn rust_info(&self) -> &GitInfo { &self.config.rust_info } @@ -723,7 +745,7 @@ impl Build { /// Note that if LLVM is configured externally then the directory returned /// will likely be empty. fn llvm_out(&self, target: TargetSelection) -> PathBuf { - if self.config.llvm_from_ci && self.config.build == target { + if self.config.llvm_from_ci && self.is_builder_target(&target) { self.config.ci_llvm_root() } else { self.out.join(target).join("llvm") @@ -766,6 +788,11 @@ impl Build { self.out.join(target).join("md-doc") } + /// Path to the vendored Rust crates. + fn vendored_crates_path(&self) -> Option { + if self.config.vendor { Some(self.src.join(VENDOR_DIR)) } else { None } + } + /// Returns `true` if this is an external version of LLVM not managed by bootstrap. /// In particular, we expect llvm sources to be available when this is false. /// @@ -773,7 +800,7 @@ impl Build { fn is_system_llvm(&self, target: TargetSelection) -> bool { match self.config.target_config.get(&target) { Some(Target { llvm_config: Some(_), .. }) => { - let ci_llvm = self.config.llvm_from_ci && target == self.config.build; + let ci_llvm = self.config.llvm_from_ci && self.is_builder_target(&target); !ci_llvm } // We're building from the in-tree src/llvm-project sources. @@ -889,6 +916,9 @@ impl Build { return CommandOutput::default(); } + #[cfg(feature = "tracing")] + let _run_span = trace_cmd!(command); + let created_at = command.get_created_location(); let executed_at = std::panic::Location::caller(); @@ -1163,9 +1193,9 @@ Executed at: {executed_at}"#, self.cc.borrow()[&target].path().into() } - /// Returns a list of flags to pass to the C compiler for the target - /// specified. - fn cflags(&self, target: TargetSelection, which: GitRepo, c: CLang) -> Vec { + /// Returns C flags that `cc-rs` thinks should be enabled for the + /// specified target by default. + fn cc_handled_clags(&self, target: TargetSelection, c: CLang) -> Vec { if self.config.dry_run() { return Vec::new(); } @@ -1174,14 +1204,23 @@ Executed at: {executed_at}"#, CLang::Cxx => self.cxx.borrow()[&target].clone(), }; - // Filter out -O and /O (the optimization flags) that we picked up from - // cc-rs because the build scripts will determine that for themselves. - let mut base = base - .args() + // Filter out -O and /O (the optimization flags) that we picked up + // from cc-rs, that's up to the caller to figure out. + base.args() .iter() .map(|s| s.to_string_lossy().into_owned()) .filter(|s| !s.starts_with("-O") && !s.starts_with("/O")) - .collect::>(); + .collect::>() + } + + /// Returns extra C flags that `cc-rs` doesn't handle. + fn cc_unhandled_cflags( + &self, + target: TargetSelection, + which: GitRepo, + c: CLang, + ) -> Vec { + let mut base = Vec::new(); // If we're compiling C++ on macOS then we add a flag indicating that // we want libc++ (more filled out than libstdc++), ensuring that @@ -1249,7 +1288,7 @@ Executed at: {executed_at}"#, // need to use CXX compiler as linker to resolve the exception functions // that are only existed in CXX libraries Some(self.cxx.borrow()[&target].path().into()) - } else if target != self.config.build + } else if !self.is_builder_target(&target) && helpers::use_host_linker(target) && !target.is_msvc() { @@ -1624,21 +1663,21 @@ Executed at: {executed_at}"#, ret } - fn read_stamp_file(&self, stamp: &Path) -> Vec<(PathBuf, DependencyType)> { + fn read_stamp_file(&self, stamp: &BuildStamp) -> Vec<(PathBuf, DependencyType)> { if self.config.dry_run() { return Vec::new(); } - if !stamp.exists() { + if !stamp.path().exists() { eprintln!( "ERROR: Unable to find the stamp file {}, did you try to keep a nonexistent build stage?", - stamp.display() + stamp.path().display() ); crate::exit!(1); } let mut paths = Vec::new(); - let contents = t!(fs::read(stamp), &stamp); + let contents = t!(fs::read(stamp.path()), stamp.path()); // This is the method we use for extracting paths from the stamp file passed to us. See // run_cargo for more information (in compile.rs). for part in contents.split(|b| *b == 0) { @@ -1657,6 +1696,14 @@ Executed at: {executed_at}"#, paths } + /// Copies a file from `src` to `dst`. + /// + /// If `src` is a symlink, `src` will be resolved to the actual path + /// and copied to `dst` instead of the symlink itself. + pub fn resolve_symlink_and_copy(&self, src: &Path, dst: &Path) { + self.copy_link_internal(src, dst, true); + } + /// Links a file from `src` to `dst`. /// Attempts to use hard links if possible, falling back to copying. /// You can neither rely on this being a copy nor it being a link, @@ -1892,6 +1939,11 @@ to download LLVM rather than building it. stream.reset().unwrap(); result } + + /// Checks if the given target is the same as the builder target. + fn is_builder_target(&self, target: &TargetSelection) -> bool { + &self.config.build == target + } } #[cfg(unix)] @@ -1924,52 +1976,6 @@ fn envify(s: &str) -> String { .collect() } -/// Computes a hash representing the state of a repository/submodule and additional input. -/// -/// It uses `git diff` for the actual changes, and `git status` for including the untracked -/// files in the specified directory. The additional input is also incorporated into the -/// computation of the hash. -/// -/// # Parameters -/// -/// - `dir`: A reference to the directory path of the target repository/submodule. -/// - `additional_input`: An additional input to be included in the hash. -/// -/// # Panics -/// -/// In case of errors during `git` command execution (e.g., in tarball sources), default values -/// are used to prevent panics. -pub fn generate_smart_stamp_hash( - builder: &Builder<'_>, - dir: &Path, - additional_input: &str, -) -> String { - let diff = helpers::git(Some(dir)) - .allow_failure() - .arg("diff") - .run_capture_stdout(builder) - .stdout_if_ok() - .unwrap_or_default(); - - let status = helpers::git(Some(dir)) - .allow_failure() - .arg("status") - .arg("--porcelain") - .arg("-z") - .arg("--untracked-files=normal") - .run_capture_stdout(builder) - .stdout_if_ok() - .unwrap_or_default(); - - let mut hasher = sha2::Sha256::new(); - - hasher.update(diff); - hasher.update(status); - hasher.update(additional_input); - - hex_encode(hasher.finalize().as_slice()) -} - /// Ensures that the behavior dump directory is properly initialized. pub fn prepare_behaviour_dump_dir(build: &Build) { static INITIALIZED: OnceLock = OnceLock::new(); diff --git a/src/bootstrap/src/utils/build_stamp.rs b/src/bootstrap/src/utils/build_stamp.rs new file mode 100644 index 0000000000000..f43d860893f6b --- /dev/null +++ b/src/bootstrap/src/utils/build_stamp.rs @@ -0,0 +1,204 @@ +//! Module for managing build stamp files. +//! +//! Contains the core implementation of how bootstrap utilizes stamp files on build processes. + +use std::path::{Path, PathBuf}; +use std::{fs, io}; + +use sha2::digest::Digest; + +use crate::core::builder::Builder; +use crate::core::config::TargetSelection; +use crate::utils::helpers::{hex_encode, mtime}; +use crate::{Compiler, Mode, helpers, t}; + +#[cfg(test)] +mod tests; + +/// Manages a stamp file to track build state. The file is created in the given +/// directory and can have custom content and name. +#[derive(Clone)] +pub struct BuildStamp { + path: PathBuf, + stamp: String, +} + +impl BuildStamp { + /// Creates a new `BuildStamp` for a given directory. + /// + /// By default, stamp will be an empty file named `.stamp` within the specified directory. + pub fn new(dir: &Path) -> Self { + // Avoid using `is_dir()` as the directory may not exist yet. + // It is more appropriate to assert that the path is not a file. + assert!(!dir.is_file(), "can't be a file path"); + Self { path: dir.join(".stamp"), stamp: String::new() } + } + + /// Returns path of the stamp file. + pub fn path(&self) -> &Path { + &self.path + } + + /// Returns the value of the stamp. + /// + /// Note that this is empty by default and is populated using `BuildStamp::add_stamp`. + /// It is not read from an actual file, but rather it holds the value that will be used + /// when `BuildStamp::write` is called. + pub fn stamp(&self) -> &str { + &self.stamp + } + + /// Adds specified stamp content to the current value. + /// + /// This method can be used incrementally e.g., `add_stamp("x").add_stamp("y").add_stamp("z")`. + pub fn add_stamp(mut self, stamp: S) -> Self { + self.stamp.push_str(&stamp.to_string()); + self + } + + /// Adds a prefix to stamp's name. + /// + /// Prefix cannot start or end with a dot (`.`). + pub fn with_prefix(mut self, prefix: &str) -> Self { + assert!( + !prefix.starts_with('.') && !prefix.ends_with('.'), + "prefix can not start or end with '.'" + ); + + let stamp_filename = self.path.file_name().unwrap().to_str().unwrap(); + let stamp_filename = stamp_filename.strip_prefix('.').unwrap_or(stamp_filename); + self.path.set_file_name(format!(".{prefix}-{stamp_filename}")); + + self + } + + /// Removes the stamp file if it exists. + pub fn remove(&self) -> io::Result<()> { + match fs::remove_file(&self.path) { + Ok(()) => Ok(()), + Err(e) => { + if e.kind() == io::ErrorKind::NotFound { + Ok(()) + } else { + Err(e) + } + } + } + } + + /// Creates the stamp file. + pub fn write(&self) -> io::Result<()> { + fs::write(&self.path, &self.stamp) + } + + /// Checks if the stamp file is up-to-date. + /// + /// It is considered up-to-date if file content matches with the stamp string. + pub fn is_up_to_date(&self) -> bool { + match fs::read(&self.path) { + Ok(h) => self.stamp.as_bytes() == h.as_slice(), + Err(e) if e.kind() == io::ErrorKind::NotFound => false, + Err(e) => { + panic!("failed to read stamp file `{}`: {}", self.path.display(), e); + } + } + } +} + +/// Clear out `dir` if `input` is newer. +/// +/// After this executes, it will also ensure that `dir` exists. +pub fn clear_if_dirty(builder: &Builder<'_>, dir: &Path, input: &Path) -> bool { + let stamp = BuildStamp::new(dir); + let mut cleared = false; + if mtime(stamp.path()) < mtime(input) { + builder.verbose(|| println!("Dirty - {}", dir.display())); + let _ = fs::remove_dir_all(dir); + cleared = true; + } else if stamp.path().exists() { + return cleared; + } + t!(fs::create_dir_all(dir)); + t!(fs::File::create(stamp.path())); + cleared +} + +/// Cargo's output path for librustc_codegen_llvm in a given stage, compiled by a particular +/// compiler for the specified target and backend. +pub fn codegen_backend_stamp( + builder: &Builder<'_>, + compiler: Compiler, + target: TargetSelection, + backend: &str, +) -> BuildStamp { + BuildStamp::new(&builder.cargo_out(compiler, Mode::Codegen, target)) + .with_prefix(&format!("librustc_codegen_{backend}")) +} + +/// Cargo's output path for the standard library in a given stage, compiled +/// by a particular compiler for the specified target. +pub fn libstd_stamp( + builder: &Builder<'_>, + compiler: Compiler, + target: TargetSelection, +) -> BuildStamp { + BuildStamp::new(&builder.cargo_out(compiler, Mode::Std, target)).with_prefix("libstd") +} + +/// Cargo's output path for librustc in a given stage, compiled by a particular +/// compiler for the specified target. +pub fn librustc_stamp( + builder: &Builder<'_>, + compiler: Compiler, + target: TargetSelection, +) -> BuildStamp { + BuildStamp::new(&builder.cargo_out(compiler, Mode::Rustc, target)).with_prefix("librustc") +} + +/// Computes a hash representing the state of a repository/submodule and additional input. +/// +/// It uses `git diff` for the actual changes, and `git status` for including the untracked +/// files in the specified directory. The additional input is also incorporated into the +/// computation of the hash. +/// +/// # Parameters +/// +/// - `dir`: A reference to the directory path of the target repository/submodule. +/// - `additional_input`: An additional input to be included in the hash. +/// +/// # Panics +/// +/// In case of errors during `git` command execution (e.g., in tarball sources), default values +/// are used to prevent panics. +pub fn generate_smart_stamp_hash( + builder: &Builder<'_>, + dir: &Path, + additional_input: &str, +) -> String { + let diff = helpers::git(Some(dir)) + .allow_failure() + .arg("diff") + .arg(".") + .run_capture_stdout(builder) + .stdout_if_ok() + .unwrap_or_default(); + + let status = helpers::git(Some(dir)) + .allow_failure() + .arg("status") + .arg(".") + .arg("--porcelain") + .arg("-z") + .arg("--untracked-files=normal") + .run_capture_stdout(builder) + .stdout_if_ok() + .unwrap_or_default(); + + let mut hasher = sha2::Sha256::new(); + + hasher.update(diff); + hasher.update(status); + hasher.update(additional_input); + + hex_encode(hasher.finalize().as_slice()) +} diff --git a/src/bootstrap/src/utils/build_stamp/tests.rs b/src/bootstrap/src/utils/build_stamp/tests.rs new file mode 100644 index 0000000000000..2d7d1f7124655 --- /dev/null +++ b/src/bootstrap/src/utils/build_stamp/tests.rs @@ -0,0 +1,60 @@ +use std::path::PathBuf; + +use crate::{BuildStamp, Config, Flags}; + +fn temp_dir() -> PathBuf { + let config = + Config::parse(Flags::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()])); + config.tempdir() +} + +#[test] +#[should_panic(expected = "prefix can not start or end with '.'")] +fn test_with_invalid_prefix() { + let dir = temp_dir(); + BuildStamp::new(&dir).with_prefix(".invalid"); +} + +#[test] +#[should_panic(expected = "prefix can not start or end with '.'")] +fn test_with_invalid_prefix2() { + let dir = temp_dir(); + BuildStamp::new(&dir).with_prefix("invalid."); +} + +#[test] +fn test_is_up_to_date() { + let dir = temp_dir(); + + let mut build_stamp = BuildStamp::new(&dir).add_stamp("v1.0.0"); + build_stamp.write().unwrap(); + + assert!( + build_stamp.is_up_to_date(), + "Expected stamp file to be up-to-date, but contents do not match the expected value." + ); + + build_stamp.stamp = "dummy value".to_owned(); + assert!( + !build_stamp.is_up_to_date(), + "Stamp should no longer be up-to-date as we changed its content right above." + ); + + build_stamp.remove().unwrap(); +} + +#[test] +fn test_with_prefix() { + let dir = temp_dir(); + + let stamp = BuildStamp::new(&dir).add_stamp("v1.0.0"); + assert_eq!(stamp.path.file_name().unwrap(), ".stamp"); + + let stamp = stamp.with_prefix("test"); + let expected_filename = ".test-stamp"; + assert_eq!(stamp.path.file_name().unwrap(), expected_filename); + + let stamp = stamp.with_prefix("extra-prefix"); + let expected_filename = ".extra-prefix-test-stamp"; + assert_eq!(stamp.path.file_name().unwrap(), expected_filename); +} diff --git a/src/bootstrap/src/utils/cache.rs b/src/bootstrap/src/utils/cache.rs index 29342cc5a2c5b..1c8cc4025df11 100644 --- a/src/bootstrap/src/utils/cache.rs +++ b/src/bootstrap/src/utils/cache.rs @@ -1,3 +1,17 @@ +//! This module helps you efficiently store and retrieve values using interning. +//! +//! Interning is a neat trick that keeps only one copy of identical values, saving memory +//! and making comparisons super fast. Here, we provide the `Interned` struct and the `Internable` trait +//! to make interning easy for different data types. +//! +//! The `Interner` struct handles caching for common types like `String`, `PathBuf`, and `Vec`, +//! while the `Cache` struct acts as a write-once storage for linking computation steps with their results. +//! +//! # Thread Safety +//! +//! We use `Mutex` to make sure interning and retrieval are thread-safe. But keep in mind—once a value is +//! interned, it sticks around for the entire lifetime of the program. + use std::any::{Any, TypeId}; use std::borrow::Borrow; use std::cell::RefCell; @@ -12,6 +26,9 @@ use std::{fmt, mem}; use crate::core::builder::Step; +/// Represents an interned value of type `T`, allowing for efficient comparisons and retrieval. +/// +/// This struct stores a unique index referencing the interned value within an internal cache. pub struct Interned(usize, PhantomData<*const T>); impl Default for Interned { @@ -111,6 +128,10 @@ impl Ord for Interned { } } +/// A structure for managing the interning of values of type `T`. +/// +/// `TyIntern` maintains a mapping between values and their interned representations, +/// ensuring that duplicate values are not stored multiple times. struct TyIntern { items: Vec, set: HashMap>, @@ -123,6 +144,9 @@ impl Default for TyIntern { } impl TyIntern { + /// Interns a borrowed value, ensuring it is stored uniquely. + /// + /// If the value has been previously interned, the same `Interned` instance is returned. fn intern_borrow(&mut self, item: &B) -> Interned where B: Eq + Hash + ToOwned + ?Sized, @@ -138,6 +162,9 @@ impl TyIntern { interned } + /// Interns an owned value, storing it uniquely. + /// + /// If the value has been previously interned, the existing `Interned` is returned. fn intern(&mut self, item: T) -> Interned { if let Some(i) = self.set.get(&item) { return *i; @@ -148,11 +175,16 @@ impl TyIntern { interned } + /// Retrieves a reference to the interned value associated with the given `Interned` instance. fn get(&self, i: Interned) -> &T { &self.items[i.0] } } +/// A global interner for managing interned values of common types. +/// +/// This structure maintains caches for `String`, `PathBuf`, and `Vec`, ensuring efficient storage +/// and retrieval of frequently used values. #[derive(Default)] pub struct Interner { strs: Mutex>, @@ -160,6 +192,10 @@ pub struct Interner { lists: Mutex>>, } +/// Defines the behavior required for a type to be internable. +/// +/// Types implementing this trait must provide access to a static cache and define an `intern` method +/// that ensures values are stored uniquely. trait Internable: Clone + Eq + Hash + 'static { fn intern_cache() -> &'static Mutex>; @@ -187,11 +223,15 @@ impl Internable for Vec { } impl Interner { + /// Interns a string reference, ensuring it is stored uniquely. + /// + /// If the string has been previously interned, the same `Interned` instance is returned. pub fn intern_str(&self, s: &str) -> Interned { self.strs.lock().unwrap().intern_borrow(s) } } +/// A global instance of `Interner` that caches common interned values. pub static INTERNER: LazyLock = LazyLock::new(Interner::default); /// This is essentially a `HashMap` which allows storing any type in its input and @@ -209,10 +249,12 @@ pub struct Cache( ); impl Cache { + /// Creates a new empty cache. pub fn new() -> Cache { Cache(RefCell::new(HashMap::new())) } + /// Stores the result of a computation step in the cache. pub fn put(&self, step: S, value: S::Output) { let mut cache = self.0.borrow_mut(); let type_id = TypeId::of::(); @@ -225,6 +267,7 @@ impl Cache { stepcache.insert(step, value); } + /// Retrieves a cached result for the given step, if available. pub fn get(&self, step: &S) -> Option { let mut cache = self.0.borrow_mut(); let type_id = TypeId::of::(); @@ -255,3 +298,6 @@ impl Cache { self.0.borrow().contains_key(&TypeId::of::()) } } + +#[cfg(test)] +mod tests; diff --git a/src/bootstrap/src/utils/cache/tests.rs b/src/bootstrap/src/utils/cache/tests.rs new file mode 100644 index 0000000000000..28f5563a589b1 --- /dev/null +++ b/src/bootstrap/src/utils/cache/tests.rs @@ -0,0 +1,52 @@ +use std::path::PathBuf; + +use crate::utils::cache::{INTERNER, Internable, TyIntern}; + +#[test] +fn test_string_interning() { + let s1 = INTERNER.intern_str("Hello"); + let s2 = INTERNER.intern_str("Hello"); + let s3 = INTERNER.intern_str("world"); + + assert_eq!(s1, s2, "Same strings should be interned to the same instance"); + assert_ne!(s1, s3, "Different strings should have different interned values"); +} + +#[test] +fn test_path_interning() { + let p1 = PathBuf::from("/tmp/file").intern(); + let p2 = PathBuf::from("/tmp/file").intern(); + let p3 = PathBuf::from("/tmp/other").intern(); + + assert_eq!(p1, p2); + assert_ne!(p1, p3); +} + +#[test] +fn test_vec_interning() { + let v1 = vec!["a".to_string(), "b".to_string()].intern(); + let v2 = vec!["a".to_string(), "b".to_string()].intern(); + let v3 = vec!["c".to_string()].intern(); + + assert_eq!(v1, v2); + assert_ne!(v1, v3); +} + +#[test] +fn test_interned_equality() { + let s1 = INTERNER.intern_str("test"); + let s2 = INTERNER.intern_str("test"); + + assert_eq!(s1, s2); + assert_eq!(s1, "test"); +} + +#[test] +fn test_ty_intern_intern_borrow() { + let mut interner = TyIntern::default(); + let s1 = interner.intern_borrow("borrowed"); + let s2 = interner.intern("borrowed".to_string()); + + assert_eq!(s1, s2); + assert_eq!(interner.get(s1), "borrowed"); +} diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs index 4aec554b4321a..45797c1276c58 100644 --- a/src/bootstrap/src/utils/cc_detect.rs +++ b/src/bootstrap/src/utils/cc_detect.rs @@ -29,11 +29,8 @@ use crate::core::config::TargetSelection; use crate::utils::exec::{BootstrapCommand, command}; use crate::{Build, CLang, GitRepo}; -// The `cc` crate doesn't provide a way to obtain a path to the detected archiver, -// so use some simplified logic here. First we respect the environment variable `AR`, then -// try to infer the archiver path from the C compiler path. -// In the future this logic should be replaced by calling into the `cc` crate. -fn cc2ar(cc: &Path, target: TargetSelection) -> Option { +/// FIXME(onur-ozkan): This logic should be replaced by calling into the `cc` crate. +fn cc2ar(cc: &Path, target: TargetSelection, default_ar: PathBuf) -> Option { if let Some(ar) = env::var_os(format!("AR_{}", target.triple.replace('-', "_"))) { Some(PathBuf::from(ar)) } else if let Some(ar) = env::var_os("AR") { @@ -57,16 +54,7 @@ fn cc2ar(cc: &Path, target: TargetSelection) -> Option { } else if target.contains("android") || target.contains("-wasi") { Some(cc.parent().unwrap().join(PathBuf::from("llvm-ar"))) } else { - let parent = cc.parent().unwrap(); - let file = cc.file_name().unwrap().to_str().unwrap(); - for suffix in &["gcc", "cc", "clang"] { - if let Some(idx) = file.rfind(suffix) { - let mut file = file[..idx].to_owned(); - file.push_str("ar"); - return Some(parent.join(&file)); - } - } - Some(parent.join(file)) + Some(default_ar) } } @@ -138,11 +126,12 @@ pub fn find_target(build: &Build, target: TargetSelection) { let ar = if let ar @ Some(..) = config.and_then(|c| c.ar.clone()) { ar } else { - cc2ar(compiler.path(), target) + cc2ar(compiler.path(), target, PathBuf::from(cfg.get_archiver().get_program())) }; build.cc.borrow_mut().insert(target, compiler.clone()); - let cflags = build.cflags(target, GitRepo::Rustc, CLang::C); + let mut cflags = build.cc_handled_clags(target, CLang::C); + cflags.extend(build.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::C)); // If we use llvm-libunwind, we will need a C++ compiler as well for all targets // We'll need one anyways if the target triple is also a host triple @@ -168,7 +157,8 @@ pub fn find_target(build: &Build, target: TargetSelection) { build.verbose(|| println!("CC_{} = {:?}", target.triple, build.cc(target))); build.verbose(|| println!("CFLAGS_{} = {cflags:?}", target.triple)); if let Ok(cxx) = build.cxx(target) { - let cxxflags = build.cflags(target, GitRepo::Rustc, CLang::Cxx); + let mut cxxflags = build.cc_handled_clags(target, CLang::Cxx); + cxxflags.extend(build.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::Cxx)); build.verbose(|| println!("CXX_{} = {cxx:?}", target.triple)); build.verbose(|| println!("CXXFLAGS_{} = {cxxflags:?}", target.triple)); } diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 8fd6c8aa23aed..9b23cf1843ef5 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -325,4 +325,29 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Warning, summary: "Removed `rust.parallel-compiler` as it was deprecated in #132282 long time ago.", }, + ChangeInfo { + change_id: 135326, + severity: ChangeSeverity::Warning, + summary: "It is now possible to configure `optimized-compiler-builtins` for per target.", + }, + ChangeInfo { + change_id: 135281, + severity: ChangeSeverity::Warning, + summary: "Some stamp names in the build artifacts may have changed slightly (e.g., from `llvm-finished-building` to `.llvm-stamp`).", + }, + ChangeInfo { + change_id: 135729, + severity: ChangeSeverity::Info, + summary: "Change the compiler profile to default to rust.debug-assertions = true", + }, + ChangeInfo { + change_id: 135832, + severity: ChangeSeverity::Info, + summary: "Rustdoc now respects the value of rust.lto.", + }, + ChangeInfo { + change_id: 136941, + severity: ChangeSeverity::Info, + summary: "The llvm.ccache option has moved to build.ccache. llvm.ccache is now deprecated.", + }, ]; diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index 530d760a584c8..7eb9ab96c8a4a 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -1,3 +1,8 @@ +//! Command Execution Module +//! +//! This module provides a structured way to execute and manage commands efficiently, +//! ensuring controlled failure handling and output management. + use std::ffi::OsStr; use std::fmt::{Debug, Formatter}; use std::path::Path; @@ -120,7 +125,7 @@ impl BootstrapCommand { Self { failure_behavior: BehaviorOnFailure::DelayFail, ..self } } - #[must_use] + #[allow(dead_code)] pub fn fail_fast(self) -> Self { Self { failure_behavior: BehaviorOnFailure::Exit, ..self } } @@ -275,7 +280,7 @@ impl CommandOutput { !self.is_success() } - #[must_use] + #[allow(dead_code)] pub fn status(&self) -> Option { match self.status { CommandStatus::Finished(status) => Some(status), @@ -324,3 +329,25 @@ impl Default for CommandOutput { } } } + +/// Helper trait to format both Command and BootstrapCommand as a short execution line, +/// without all the other details (e.g. environment variables). +#[allow(unused)] +pub trait FormatShortCmd { + fn format_short_cmd(&self) -> String; +} + +impl FormatShortCmd for BootstrapCommand { + fn format_short_cmd(&self) -> String { + self.command.format_short_cmd() + } +} + +impl FormatShortCmd for Command { + fn format_short_cmd(&self) -> String { + let program = Path::new(self.get_program()); + let mut line = vec![program.file_name().unwrap().to_str().unwrap()]; + line.extend(self.get_args().map(|arg| arg.to_str().unwrap())); + line.join(" ") + } +} diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index 985d733a41f74..3fee397da091d 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -16,6 +16,7 @@ use object::read::archive::ArchiveFile; use crate::LldMode; use crate::core::builder::Builder; use crate::core::config::{Config, TargetSelection}; +use crate::utils::exec::{BootstrapCommand, command}; pub use crate::utils::shared_helpers::{dylib_path, dylib_path_var}; #[cfg(test)] @@ -45,10 +46,8 @@ macro_rules! t { } }; } -pub use t; - -use crate::utils::exec::{BootstrapCommand, command}; +pub use t; pub fn exe(name: &str, target: TargetSelection) -> String { crate::utils::shared_helpers::exe(name, &target.triple) } @@ -60,10 +59,12 @@ pub fn is_dylib(path: &Path) -> bool { }) } -/// Returns `true` if the given path is part of a submodule. -pub fn is_path_in_submodule(builder: &Builder<'_>, path: &str) -> bool { +/// Return the path to the containing submodule if available. +pub fn submodule_path_of(builder: &Builder<'_>, path: &str) -> Option { let submodule_paths = build_helper::util::parse_gitmodules(&builder.src); - submodule_paths.iter().any(|submodule_path| path.starts_with(submodule_path)) + submodule_paths.iter().find_map(|submodule_path| { + if path.starts_with(submodule_path) { Some(submodule_path.to_string()) } else { None } + }) } fn is_aix_shared_archive(path: &Path) -> bool { @@ -105,31 +106,6 @@ pub fn add_dylib_path(path: Vec, cmd: &mut BootstrapCommand) { cmd.env(dylib_path_var(), t!(env::join_paths(list))); } -/// Adds a list of lookup paths to `cmd`'s link library lookup path. -pub fn add_link_lib_path(path: Vec, cmd: &mut BootstrapCommand) { - let mut list = link_lib_path(); - for path in path { - list.insert(0, path); - } - cmd.env(link_lib_path_var(), t!(env::join_paths(list))); -} - -/// Returns the environment variable which the link library lookup path -/// resides in for this platform. -fn link_lib_path_var() -> &'static str { - if cfg!(target_env = "msvc") { "LIB" } else { "LIBRARY_PATH" } -} - -/// Parses the `link_lib_path_var()` environment variable, returning a list of -/// paths that are members of this lookup path. -fn link_lib_path() -> Vec { - let var = match env::var_os(link_lib_path_var()) { - Some(v) => v, - None => return vec![], - }; - env::split_paths(&var).collect() -} - pub struct TimeIt(bool, Instant); /// Returns an RAII structure that prints out how long it took to drop. @@ -146,14 +122,6 @@ impl Drop for TimeIt { } } -/// Used for download caching -pub(crate) fn program_out_of_date(stamp: &Path, key: &str) -> bool { - if !stamp.exists() { - return true; - } - t!(fs::read_to_string(stamp)) != key -} - /// Symlinks two directories, using junctions on Windows and normal symlinks on /// Unix. pub fn symlink_dir(config: &Config, original: &Path, link: &Path) -> io::Result<()> { @@ -179,10 +147,7 @@ pub fn symlink_dir(config: &Config, original: &Path, link: &Path) -> io::Result< /// copy and remove the file otherwise pub fn move_file, Q: AsRef>(from: P, to: Q) -> io::Result<()> { match fs::rename(&from, &to) { - // FIXME: Once `ErrorKind::CrossesDevices` is stabilized use - // if e.kind() == io::ErrorKind::CrossesDevices { - #[cfg(unix)] - Err(e) if e.raw_os_error() == Some(libc::EXDEV) => { + Err(e) if e.kind() == io::ErrorKind::CrossesDevices => { std::fs::copy(&from, &to)?; std::fs::remove_file(&from) } @@ -300,6 +265,9 @@ pub fn make(host: &str) -> PathBuf { #[track_caller] pub fn output(cmd: &mut Command) -> String { + #[cfg(feature = "tracing")] + let _run_span = crate::trace_cmd!(cmd); + let output = match cmd.stderr(Stdio::inherit()).output() { Ok(status) => status, Err(e) => fail(&format!("failed to execute command: {cmd:?}\nERROR: {e}")), @@ -438,7 +406,7 @@ fn lld_flag_no_threads(builder: &Builder<'_>, lld_mode: LldMode, is_windows: boo } pub fn dir_is_empty(dir: &Path) -> bool { - t!(std::fs::read_dir(dir)).next().is_none() + t!(std::fs::read_dir(dir), dir).next().is_none() } /// Extract the beta revision from the full version string. @@ -481,7 +449,20 @@ pub fn linker_flags( ) -> Vec { let mut args = vec![]; if !builder.is_lld_direct_linker(target) && builder.config.lld_mode.is_used() { - args.push(String::from("-Clink-arg=-fuse-ld=lld")); + match builder.config.lld_mode { + LldMode::External => { + args.push("-Clinker-flavor=gnu-lld-cc".to_string()); + // FIXME(kobzol): remove this flag once MCP510 gets stabilized + args.push("-Zunstable-options".to_string()); + } + LldMode::SelfContained => { + args.push("-Clinker-flavor=gnu-lld-cc".to_string()); + args.push("-Clink-self-contained=+linker".to_string()); + // FIXME(kobzol): remove this flag once MCP510 gets stabilized + args.push("-Zunstable-options".to_string()); + } + LldMode::Unused => unreachable!(), + }; if matches!(lld_threads, LldThreads::No) { args.push(format!( @@ -586,41 +567,3 @@ pub fn set_file_times>(path: P, times: fs::FileTimes) -> io::Resu }; f.set_times(times) } - -pub struct HashStamp { - pub path: PathBuf, - pub hash: Option>, -} - -impl HashStamp { - pub fn new(path: PathBuf, hash: Option<&str>) -> Self { - HashStamp { path, hash: hash.map(|s| s.as_bytes().to_owned()) } - } - - pub fn is_done(&self) -> bool { - match fs::read(&self.path) { - Ok(h) => self.hash.as_deref().unwrap_or(b"") == h.as_slice(), - Err(e) if e.kind() == io::ErrorKind::NotFound => false, - Err(e) => { - panic!("failed to read stamp file `{}`: {}", self.path.display(), e); - } - } - } - - pub fn remove(&self) -> io::Result<()> { - match fs::remove_file(&self.path) { - Ok(()) => Ok(()), - Err(e) => { - if e.kind() == io::ErrorKind::NotFound { - Ok(()) - } else { - Err(e) - } - } - } - } - - pub fn write(&self) -> io::Result<()> { - fs::write(&self.path, self.hash.as_deref().unwrap_or(b"")) - } -} diff --git a/src/bootstrap/src/utils/helpers/tests.rs b/src/bootstrap/src/utils/helpers/tests.rs index 7bd2a47c63c1c..9030ca2820a8b 100644 --- a/src/bootstrap/src/utils/helpers/tests.rs +++ b/src/bootstrap/src/utils/helpers/tests.rs @@ -1,10 +1,10 @@ -use std::fs::{self, File, remove_file}; +use std::fs::{self, File}; use std::io::Write; use std::path::PathBuf; use crate::utils::helpers::{ - check_cfg_arg, extract_beta_rev, hex_encode, is_path_in_submodule, make, program_out_of_date, - set_file_times, symlink_dir, + check_cfg_arg, extract_beta_rev, hex_encode, make, set_file_times, submodule_path_of, + symlink_dir, }; use crate::{Config, Flags}; @@ -57,22 +57,6 @@ fn test_check_cfg_arg() { ); } -#[test] -fn test_program_out_of_date() { - let config = - Config::parse(Flags::parse(&["check".to_owned(), "--config=/does/not/exist".to_owned()])); - let tempfile = config.tempdir().join(".tmp-stamp-file"); - File::create(&tempfile).unwrap().write_all(b"dummy value").unwrap(); - assert!(tempfile.exists()); - - // up-to-date - assert!(!program_out_of_date(&tempfile, "dummy value")); - // out-of-date - assert!(program_out_of_date(&tempfile, "")); - - remove_file(tempfile).unwrap(); -} - #[test] fn test_symlink_dir() { let config = @@ -117,16 +101,22 @@ fn test_set_file_times_sanity_check() { } #[test] -fn test_is_path_in_submodule() { +fn test_submodule_path_of() { let config = Config::parse_inner(Flags::parse(&["build".into(), "--dry-run".into()]), |&_| { Ok(Default::default()) }); let build = crate::Build::new(config.clone()); let builder = crate::core::builder::Builder::new(&build); - assert!(!is_path_in_submodule(&builder, "invalid/path")); - assert!(is_path_in_submodule(&builder, "src/tools/cargo")); - assert!(is_path_in_submodule(&builder, "src/llvm-project")); + assert_eq!(submodule_path_of(&builder, "invalid/path"), None); + assert_eq!(submodule_path_of(&builder, "src/tools/cargo"), Some("src/tools/cargo".to_string())); + assert_eq!( + submodule_path_of(&builder, "src/llvm-project"), + Some("src/llvm-project".to_string()) + ); // Make sure subdirs are handled properly - assert!(is_path_in_submodule(&builder, "src/tools/cargo/random-subdir")); + assert_eq!( + submodule_path_of(&builder, "src/tools/cargo/random-subdir"), + Some("src/tools/cargo".to_string()) + ); } diff --git a/src/bootstrap/src/utils/job.rs b/src/bootstrap/src/utils/job.rs index fb69d331d27e1..10efed130d624 100644 --- a/src/bootstrap/src/utils/job.rs +++ b/src/bootstrap/src/utils/job.rs @@ -42,9 +42,9 @@ pub unsafe fn setup(build: &mut crate::Build) { #[cfg(windows)] mod for_windows { use std::ffi::c_void; - use std::{env, io, mem}; + use std::{io, mem}; - use windows::Win32::Foundation::{CloseHandle, DUPLICATE_SAME_ACCESS, DuplicateHandle, HANDLE}; + use windows::Win32::Foundation::CloseHandle; use windows::Win32::System::Diagnostics::Debug::{ SEM_NOGPFAULTERRORBOX, SetErrorMode, THREAD_ERROR_MODE, }; @@ -53,9 +53,7 @@ mod for_windows { JOB_OBJECT_LIMIT_PRIORITY_CLASS, JOBOBJECT_EXTENDED_LIMIT_INFORMATION, JobObjectExtendedLimitInformation, SetInformationJobObject, }; - use windows::Win32::System::Threading::{ - BELOW_NORMAL_PRIORITY_CLASS, GetCurrentProcess, OpenProcess, PROCESS_DUP_HANDLE, - }; + use windows::Win32::System::Threading::{BELOW_NORMAL_PRIORITY_CLASS, GetCurrentProcess}; use windows::core::PCWSTR; use crate::Build; @@ -95,49 +93,8 @@ mod for_windows { return; } - // If we've got a parent process (e.g., the python script that called us) - // then move ownership of this job object up to them. That way if the python - // script is killed (e.g., via ctrl-c) then we'll all be torn down. - // - // If we don't have a parent (e.g., this was run directly) then we - // intentionally leak the job object handle. When our process exits + // Note: we intentionally leak the job object handle. When our process exits // (normally or abnormally) it will close the handle implicitly, causing all // processes in the job to be cleaned up. - let pid = match env::var("BOOTSTRAP_PARENT_ID") { - Ok(s) => s, - Err(..) => return, - }; - - let parent = match OpenProcess(PROCESS_DUP_HANDLE, false, pid.parse().unwrap()).ok() { - Some(parent) => parent, - _ => { - // If we get a null parent pointer here, it is possible that either - // we have an invalid pid or the parent process has been closed. - // Since the first case rarely happens - // (only when wrongly setting the environmental variable), - // it might be better to improve the experience of the second case - // when users have interrupted the parent process and we haven't finish - // duplicating the handle yet. - return; - } - }; - - let mut parent_handle = HANDLE::default(); - // If this fails, well at least we tried! An example of DuplicateHandle - // failing in the past has been when the wrong python2 package spawned this - // build system (e.g., the `python2` package in MSYS instead of - // `mingw-w64-x86_64-python2`). Not sure why it failed, but the "failure - // mode" here is that we only clean everything up when the build system - // dies, not when the python parent does, so not too bad. - let _ = DuplicateHandle( - GetCurrentProcess(), - job, - parent, - &mut parent_handle, - 0, - false, - DUPLICATE_SAME_ACCESS, - ); - CloseHandle(parent).ok(); } } diff --git a/src/bootstrap/src/utils/mod.rs b/src/bootstrap/src/utils/mod.rs index b5f5e2ba6dc8b..caef8ce3088a7 100644 --- a/src/bootstrap/src/utils/mod.rs +++ b/src/bootstrap/src/utils/mod.rs @@ -2,6 +2,7 @@ //! support for a wide range of tasks and operations such as caching, tarballs, release //! channels, job management, etc. +pub(crate) mod build_stamp; pub(crate) mod cache; pub(crate) mod cc_detect; pub(crate) mod change_tracker; @@ -9,10 +10,14 @@ pub(crate) mod channel; pub(crate) mod exec; pub(crate) mod helpers; pub(crate) mod job; -#[cfg(feature = "build-metrics")] -pub(crate) mod metrics; pub(crate) mod render_tests; pub(crate) mod shared_helpers; pub(crate) mod tarball; + +pub(crate) mod tracing; + +#[cfg(feature = "build-metrics")] +pub(crate) mod metrics; + #[cfg(test)] mod tests; diff --git a/src/bootstrap/src/utils/render_tests.rs b/src/bootstrap/src/utils/render_tests.rs index 46b250555f2d5..3f58328a5b594 100644 --- a/src/bootstrap/src/utils/render_tests.rs +++ b/src/bootstrap/src/utils/render_tests.rs @@ -10,6 +10,7 @@ use std::io::{BufRead, BufReader, Read, Write}; use std::process::{ChildStdout, Stdio}; use std::time::Duration; +use build_helper::ci::CiEnv; use termcolor::{Color, ColorSpec, WriteColor}; use crate::core::builder::Builder; @@ -91,7 +92,9 @@ struct Renderer<'a> { /// Number of tests that were skipped due to already being up-to-date /// (i.e. no relevant changes occurred since they last ran). up_to_date_tests: usize, + ignored_tests: usize, terse_tests_in_line: usize, + ci_latest_logged_percentage: f64, } impl<'a> Renderer<'a> { @@ -104,7 +107,9 @@ impl<'a> Renderer<'a> { tests_count: None, executed_tests: 0, up_to_date_tests: 0, + ignored_tests: 0, terse_tests_in_line: 0, + ci_latest_logged_percentage: 0.0, } } @@ -159,9 +164,12 @@ impl<'a> Renderer<'a> { fn render_test_outcome(&mut self, outcome: Outcome<'_>, test: &TestOutcome) { self.executed_tests += 1; - // Keep this in sync with the "up-to-date" ignore message inserted by compiletest. - if let Outcome::Ignored { reason: Some("up-to-date") } = outcome { - self.up_to_date_tests += 1; + if let Outcome::Ignored { reason } = outcome { + self.ignored_tests += 1; + // Keep this in sync with the "up-to-date" ignore message inserted by compiletest. + if reason == Some("up-to-date") { + self.up_to_date_tests += 1; + } } #[cfg(feature = "build-metrics")] @@ -179,6 +187,8 @@ impl<'a> Renderer<'a> { if self.builder.config.verbose_tests { self.render_test_outcome_verbose(outcome, test); + } else if CiEnv::is_ci() { + self.render_test_outcome_ci(outcome, test); } else { self.render_test_outcome_terse(outcome, test); } @@ -209,6 +219,31 @@ impl<'a> Renderer<'a> { let _ = std::io::stdout().flush(); } + fn render_test_outcome_ci(&mut self, outcome: Outcome<'_>, test: &TestOutcome) { + if let Some(total) = self.tests_count { + let percent = self.executed_tests as f64 / total as f64; + + if self.ci_latest_logged_percentage + 0.10 < percent { + let total = total.to_string(); + let executed = format!("{:>width$}", self.executed_tests, width = total.len()); + let pretty_percent = format!("{:.0}%", percent * 100.0); + let passed_tests = self.executed_tests - (self.failures.len() + self.ignored_tests); + println!( + "{:<4} -- {executed}/{total}, {:>total_indent$} passed, {} failed, {} ignored", + pretty_percent, + passed_tests, + self.failures.len(), + self.ignored_tests, + total_indent = total.len() + ); + self.ci_latest_logged_percentage += 0.10; + } + } + + self.builder.colored_stdout(|stdout| outcome.write_ci(stdout, &test.name)).unwrap(); + let _ = std::io::stdout().flush(); + } + fn render_suite_outcome(&self, outcome: Outcome<'_>, suite: &SuiteOutcome) { // The terse output doesn't end with a newline, so we need to add it ourselves. if !self.builder.config.verbose_tests { @@ -275,6 +310,9 @@ impl<'a> Renderer<'a> { match message { Message::Suite(SuiteMessage::Started { test_count }) => { println!("\nrunning {test_count} tests"); + self.benches = vec![]; + self.failures = vec![]; + self.ignored_tests = 0; self.executed_tests = 0; self.terse_tests_in_line = 0; self.tests_count = Some(test_count); @@ -378,6 +416,17 @@ impl Outcome<'_> { } writer.reset() } + + fn write_ci(&self, writer: &mut dyn WriteColor, name: &str) -> Result<(), std::io::Error> { + match self { + Outcome::Ok | Outcome::BenchOk | Outcome::Ignored { .. } => {} + Outcome::Failed => { + writer.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?; + writeln!(writer, " {name} ... FAILED")?; + } + } + writer.reset() + } } #[derive(serde_derive::Deserialize)] diff --git a/src/bootstrap/src/utils/tracing.rs b/src/bootstrap/src/utils/tracing.rs new file mode 100644 index 0000000000000..99849341dc3b9 --- /dev/null +++ b/src/bootstrap/src/utils/tracing.rs @@ -0,0 +1,66 @@ +//! Wrapper macros for `tracing` macros to avoid having to write `cfg(feature = "tracing")`-gated +//! `debug!`/`trace!` everytime, e.g. +//! +//! ```rust,ignore (example) +//! #[cfg(feature = "tracing")] +//! trace!("..."); +//! ``` +//! +//! When `feature = "tracing"` is inactive, these macros expand to nothing. + +#[macro_export] +macro_rules! trace { + ($($tokens:tt)*) => { + #[cfg(feature = "tracing")] + ::tracing::trace!($($tokens)*) + } +} + +#[macro_export] +macro_rules! debug { + ($($tokens:tt)*) => { + #[cfg(feature = "tracing")] + ::tracing::debug!($($tokens)*) + } +} + +#[macro_export] +macro_rules! warn { + ($($tokens:tt)*) => { + #[cfg(feature = "tracing")] + ::tracing::warn!($($tokens)*) + } +} + +#[macro_export] +macro_rules! info { + ($($tokens:tt)*) => { + #[cfg(feature = "tracing")] + ::tracing::info!($($tokens)*) + } +} + +#[macro_export] +macro_rules! error { + ($($tokens:tt)*) => { + #[cfg(feature = "tracing")] + ::tracing::error!($($tokens)*) + } +} + +#[macro_export] +macro_rules! trace_cmd { + ($cmd:expr) => { + { + use $crate::utils::exec::FormatShortCmd; + + ::tracing::span!( + target: "COMMAND", + ::tracing::Level::TRACE, + "executing command", + cmd = $cmd.format_short_cmd(), + full_cmd = ?$cmd + ).entered() + } + }; +} diff --git a/src/build_helper/src/git.rs b/src/build_helper/src/git.rs index 2aad5650fa898..3ef9c7ac35e9d 100644 --- a/src/build_helper/src/git.rs +++ b/src/build_helper/src/git.rs @@ -30,8 +30,8 @@ pub fn output_result(cmd: &mut Command) -> Result { /// Finds the remote for rust-lang/rust. /// For example for these remotes it will return `upstream`. /// ```text -/// origin https://github.com/Nilstrieb/rust.git (fetch) -/// origin https://github.com/Nilstrieb/rust.git (push) +/// origin https://github.com/pietroalbani/rust.git (fetch) +/// origin https://github.com/pietroalbani/rust.git (push) /// upstream https://github.com/rust-lang/rust (fetch) /// upstream https://github.com/rust-lang/rust (push) /// ``` @@ -129,8 +129,19 @@ pub fn get_closest_merge_commit( git.current_dir(git_dir); } + let channel = include_str!("../../ci/channel"); + let merge_base = { - if CiEnv::is_ci() { + if CiEnv::is_ci() && + // FIXME: When running on rust-lang managed CI and it's not a nightly build, + // `git_upstream_merge_base` fails with an error message similar to this: + // ``` + // called `Result::unwrap()` on an `Err` value: "command did not execute successfully: + // cd \"/checkout\" && \"git\" \"merge-base\" \"origin/master\" \"HEAD\"\nexpected success, got: exit status: 1\n" + // ``` + // Investigate and resolve this issue instead of skipping it like this. + (channel == "nightly" || !CiEnv::is_rust_lang_managed_ci_job()) + { git_upstream_merge_base(config, git_dir).unwrap() } else { // For non-CI environments, ignore rust-lang/rust upstream as it usually gets diff --git a/src/build_helper/src/metrics.rs b/src/build_helper/src/metrics.rs index 2d0c66a8f3339..538c33e9b1591 100644 --- a/src/build_helper/src/metrics.rs +++ b/src/build_helper/src/metrics.rs @@ -16,6 +16,7 @@ pub struct JsonInvocation { // // This is necessary to easily correlate this invocation with logs or other data. pub start_time: u64, + #[serde(deserialize_with = "null_as_f64_nan")] pub duration_including_children_sec: f64, pub children: Vec, } @@ -28,6 +29,7 @@ pub enum JsonNode { type_: String, debug_repr: String, + #[serde(deserialize_with = "null_as_f64_nan")] duration_excluding_children_sec: f64, system_stats: JsonStepSystemStats, @@ -88,5 +90,11 @@ pub struct JsonInvocationSystemStats { #[derive(Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub struct JsonStepSystemStats { + #[serde(deserialize_with = "null_as_f64_nan")] pub cpu_utilization_percent: f64, } + +fn null_as_f64_nan<'de, D: serde::Deserializer<'de>>(d: D) -> Result { + use serde::Deserialize as _; + Option::::deserialize(d).map(|f| f.unwrap_or(f64::NAN)) +} diff --git a/src/ci/channel b/src/ci/channel index bf867e0ae5b6c..2bf5ad0447d33 100644 --- a/src/ci/channel +++ b/src/ci/channel @@ -1 +1 @@ -nightly +stable diff --git a/src/ci/docker/README.md b/src/ci/docker/README.md index 508b7b40c01e4..2b1de43c2f68c 100644 --- a/src/ci/docker/README.md +++ b/src/ci/docker/README.md @@ -1,29 +1,30 @@ # Docker images for CI This folder contains a bunch of docker images used by the continuous integration -(CI) of Rust. An script is accompanied (`run.sh`) with these images to actually -execute them. To test out an image execute: +(CI) of Rust. A script is accompanied (`run.sh`) with these images to actually +execute them. -``` -./src/ci/docker/run.sh $image_name -``` +Note that a single Docker image can be used by multiple CI jobs, so the job name +is the important thing that you should know. You can examine the existing CI jobs in +the [`jobs.yml`](../github-actions/jobs.yml) file. -for example: +To run a specific CI job locally, you can use the following script: ``` -./src/ci/docker/run.sh x86_64-gnu +python3 ./src/ci/github-actions/ci.py run-local ``` -Images will output artifacts in an `obj/$image_name` dir at the root of a repository. Note -that the script will overwrite the contents of this directory. - -To match conditions in rusts CI, also set the environment variable `DEPLOY=1`, e.g.: +For example, to run the `x86_64-gnu-llvm-18-1` job: ``` -DEPLOY=1 ./src/ci/docker/run.sh x86_64-gnu +python3 ./src/ci/github-actions/ci.py run-local x86_64-gnu-llvm-18-1 ``` +The job will output artifacts in an `obj/` dir at the root of a repository. Note +that the script will overwrite the contents of this directory. `` is set based on the +Docker image executed in the given CI job. + **NOTE**: In CI, the script outputs the artifacts to the `obj` directory, -while locally, to the `obj/$image_name` directory. This is primarily to prevent +while locally, to the `obj/` directory. This is primarily to prevent strange linker errors when using multiple Docker images. For some Linux workflows (for example `x86_64-gnu-llvm-18-N`), the process is more involved. You will need to see which script is executed for the given workflow inside the [`jobs.yml`](../github-actions/jobs.yml) file and pass it through the `DOCKER_SCRIPT` environment variable. For example, to reproduce the `x86_64-gnu-llvm-18-3` workflow, you can run the following script: @@ -304,8 +305,6 @@ For targets: `mips-unknown-linux-gnu` - Path and misc options > Prefix directory = /x-tools/${CT\_TARGET} - Path and misc options > Use a mirror = ENABLE - Path and misc options > Base URL = https://ci-mirrors.rust-lang.org/rustc -- Path and misc options > Patches origin = Bundled, then local -- Path and misc options > Local patch directory = /tmp/patches - Target options > Target Architecture = mips - Target options > ABI = o32 - Target options > Endianness = Big endian @@ -326,8 +325,6 @@ For targets: `mipsel-unknown-linux-gnu` - Path and misc options > Prefix directory = /x-tools/${CT\_TARGET} - Path and misc options > Use a mirror = ENABLE - Path and misc options > Base URL = https://ci-mirrors.rust-lang.org/rustc -- Path and misc options > Patches origin = Bundled, then local -- Path and misc options > Local patch directory = /tmp/patches - Target options > Target Architecture = mips - Target options > ABI = o32 - Target options > Endianness = Little endian @@ -348,8 +345,6 @@ For targets: `mips64-unknown-linux-gnuabi64` - Path and misc options > Prefix directory = /x-tools/${CT\_TARGET} - Path and misc options > Use a mirror = ENABLE - Path and misc options > Base URL = https://ci-mirrors.rust-lang.org/rustc -- Path and misc options > Patches origin = Bundled, then local -- Path and misc options > Local patch directory = /tmp/patches - Target options > Target Architecture = mips - Target options > ABI = n64 - Target options > Endianness = Big endian @@ -369,8 +364,6 @@ For targets: `mips64el-unknown-linux-gnuabi64` - Path and misc options > Prefix directory = /x-tools/${CT\_TARGET} - Path and misc options > Use a mirror = ENABLE - Path and misc options > Base URL = https://ci-mirrors.rust-lang.org/rustc -- Path and misc options > Patches origin = Bundled, then local -- Path and misc options > Local patch directory = /tmp/patches - Target options > Target Architecture = mips - Target options > ABI = n64 - Target options > Endianness = Little endian diff --git a/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile b/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile new file mode 100644 index 0000000000000..2b8a3f829c608 --- /dev/null +++ b/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile @@ -0,0 +1,104 @@ +# We document platform support for minimum glibc 2.17 and kernel 3.2. +# CentOS 7 has headers for kernel 3.10, but that's fine as long as we don't +# actually use newer APIs in rustc or std without a fallback. It's more +# important that we match glibc for ELF symbol versioning. +FROM centos:7 + +WORKDIR /build + +# CentOS 7 EOL is June 30, 2024, but the repos remain in the vault. +RUN sed -i /etc/yum.repos.d/*.repo -e 's!^mirrorlist!#mirrorlist!' \ + -e 's!^#baseurl=http://mirror.centos.org/!baseurl=https://vault.centos.org/!' +RUN sed -i 's/enabled=1/enabled=0/' /etc/yum/pluginconf.d/fastestmirror.conf + +RUN yum upgrade -y && \ + yum install -y \ + automake \ + bzip2 \ + file \ + gcc \ + gcc-c++ \ + git \ + glibc-devel \ + glibc-static \ + libedit-devel \ + libstdc++-devel \ + make \ + ncurses-devel \ + openssl-devel \ + patch \ + perl \ + perl-core \ + pkgconfig \ + python3 \ + unzip \ + wget \ + xz \ + zlib-devel \ + && yum clean all + +RUN mkdir -p /rustroot/bin + +ENV PATH=/rustroot/bin:$PATH +ENV LD_LIBRARY_PATH=/rustroot/lib64:/rustroot/lib32:/rustroot/lib +ENV PKG_CONFIG_PATH=/rustroot/lib/pkgconfig +WORKDIR /tmp +RUN mkdir /home/user +COPY scripts/shared.sh /tmp/ + +# Need at least GCC 5.1 to compile LLVM +COPY scripts/build-gcc.sh /tmp/ +ENV GCC_VERSION=9.5.0 +ENV GCC_BUILD_TARGET=aarch64-unknown-linux-gnu +RUN ./build-gcc.sh && yum remove -y gcc gcc-c++ + +ENV CC=gcc CXX=g++ + +# LLVM 17 needs cmake 3.20 or higher. +COPY scripts/cmake.sh /tmp/ +RUN ./cmake.sh + +# Build LLVM+Clang +COPY scripts/build-clang.sh /tmp/ +ENV LLVM_BUILD_TARGETS=AArch64 +RUN ./build-clang.sh +ENV CC=clang CXX=clang++ + +# Build zstd to enable `llvm.libzstd`. +COPY scripts/build-zstd.sh /tmp/ +RUN ./build-zstd.sh + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +ENV PGO_HOST=aarch64-unknown-linux-gnu +ENV HOSTS=aarch64-unknown-linux-gnu + +ENV CPATH=/usr/include/aarch64-linux-gnu/:$CPATH + +ENV RUST_CONFIGURE_ARGS \ + --build=aarch64-unknown-linux-gnu \ + --enable-full-tools \ + --enable-profiler \ + --enable-sanitizers \ + --enable-compiler-docs \ + --set target.aarch64-unknown-linux-gnu.linker=clang \ + --set target.aarch64-unknown-linux-gnu.ar=/rustroot/bin/llvm-ar \ + --set target.aarch64-unknown-linux-gnu.ranlib=/rustroot/bin/llvm-ranlib \ + --set llvm.link-shared=true \ + --set llvm.thin-lto=true \ + --set llvm.libzstd=true \ + --set llvm.ninja=false \ + --set rust.debug-assertions=false \ + --set rust.jemalloc \ + --set rust.use-lld=true \ + --set rust.lto=thin \ + --set rust.codegen-units=1 + +ENV SCRIPT python3 ../x.py build --set rust.debug=true opt-dist && \ + ./build/$HOSTS/stage0-tools-bin/opt-dist linux-ci -- python3 ../x.py dist \ + --host $HOSTS --target $HOSTS --include-default-paths build-manifest bootstrap + +ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=clang +ENV LIBCURL_NO_PKG_CONFIG 1 +ENV DIST_REQUIRE_ALL_TOOLS 1 diff --git a/src/ci/docker/host-x86_64/dist-aarch64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-aarch64-linux/Dockerfile deleted file mode 100644 index 18972387e34d7..0000000000000 --- a/src/ci/docker/host-x86_64/dist-aarch64-linux/Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -FROM ubuntu:22.04 - -COPY scripts/cross-apt-packages.sh /scripts/ -RUN sh /scripts/cross-apt-packages.sh - -COPY scripts/crosstool-ng.sh /scripts/ -RUN sh /scripts/crosstool-ng.sh - -COPY scripts/rustbuild-setup.sh /scripts/ -RUN sh /scripts/rustbuild-setup.sh -WORKDIR /tmp - -COPY scripts/crosstool-ng-build.sh /scripts/ -COPY host-x86_64/dist-aarch64-linux/aarch64-linux-gnu.defconfig /tmp/crosstool.defconfig -RUN /scripts/crosstool-ng-build.sh - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh - -ENV PATH=$PATH:/x-tools/aarch64-unknown-linux-gnu/bin - -ENV CC_aarch64_unknown_linux_gnu=aarch64-unknown-linux-gnu-gcc \ - AR_aarch64_unknown_linux_gnu=aarch64-unknown-linux-gnu-ar \ - CXX_aarch64_unknown_linux_gnu=aarch64-unknown-linux-gnu-g++ - -ENV HOSTS=aarch64-unknown-linux-gnu - -ENV RUST_CONFIGURE_ARGS \ - --enable-full-tools \ - --enable-profiler \ - --enable-sanitizers -ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/host-x86_64/dist-aarch64-linux/aarch64-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-aarch64-linux/aarch64-linux-gnu.defconfig deleted file mode 100644 index 520b1667c8be1..0000000000000 --- a/src/ci/docker/host-x86_64/dist-aarch64-linux/aarch64-linux-gnu.defconfig +++ /dev/null @@ -1,10 +0,0 @@ -CT_CONFIG_VERSION="4" -CT_PREFIX_DIR="/x-tools/${CT_TARGET}" -CT_USE_MIRROR=y -CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" -CT_ARCH_ARM=y -CT_ARCH_64=y -CT_KERNEL_LINUX=y -CT_LINUX_V_4_1=y -CT_GLIBC_V_2_17=y -CT_CC_LANG_CXX=y diff --git a/src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile index 7e946df616310..ea5b208a7c1d3 100644 --- a/src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-i686-linux/Dockerfile @@ -19,6 +19,7 @@ RUN yum upgrade -y && \ gcc \ gcc-c++ \ git \ + binutils.i686 \ glibc-devel.i686 \ glibc-devel.x86_64 \ libedit-devel \ @@ -46,11 +47,12 @@ ENV LD_LIBRARY_PATH=/rustroot/lib64:/rustroot/lib32:/rustroot/lib ENV PKG_CONFIG_PATH=/rustroot/lib/pkgconfig WORKDIR /tmp RUN mkdir /home/user -COPY host-x86_64/dist-x86_64-linux/shared.sh /tmp/ +COPY scripts/shared.sh /tmp/ # Need at least GCC 5.1 to compile LLVM nowadays -COPY host-x86_64/dist-x86_64-linux/build-gcc.sh /tmp/ +COPY scripts/build-gcc.sh /tmp/ ENV GCC_VERSION=9.5.0 +ENV GCC_BUILD_TARGET=i686-pc-linux-gnu RUN ./build-gcc.sh && yum remove -y gcc gcc-c++ COPY scripts/cmake.sh /tmp/ @@ -58,7 +60,8 @@ RUN ./cmake.sh # Now build LLVM+Clang, afterwards configuring further compilations to use the # clang/clang++ compilers. -COPY host-x86_64/dist-x86_64-linux/build-clang.sh /tmp/ +COPY scripts/build-clang.sh /tmp/ +ENV LLVM_BUILD_TARGETS=X86 RUN ./build-clang.sh ENV CC=clang CXX=clang++ diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile index 71eb72686b06a..e3f23149284e5 100644 --- a/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile @@ -3,8 +3,8 @@ FROM ubuntu:22.04 COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh -COPY scripts/crosstool-ng-git.sh /scripts/ -RUN sh /scripts/crosstool-ng-git.sh +COPY scripts/crosstool-ng.sh /scripts/ +RUN sh /scripts/crosstool-ng.sh COPY scripts/rustbuild-setup.sh /scripts/ RUN sh /scripts/rustbuild-setup.sh diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile b/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile index 5081f25e56743..2c33b5526eebc 100644 --- a/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile @@ -3,8 +3,8 @@ FROM ubuntu:22.04 COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh -COPY scripts/crosstool-ng-git.sh /scripts/ -RUN sh /scripts/crosstool-ng-git.sh +COPY scripts/crosstool-ng.sh /scripts/ +RUN sh /scripts/crosstool-ng.sh COPY scripts/rustbuild-setup.sh /scripts/ RUN sh /scripts/rustbuild-setup.sh diff --git a/src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile index c08febf423adb..4a090dcdd5085 100644 --- a/src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-mips-linux/Dockerfile @@ -11,7 +11,6 @@ RUN sh /scripts/rustbuild-setup.sh WORKDIR /tmp COPY scripts/crosstool-ng-build.sh /scripts/ -COPY host-x86_64/dist-mips-linux/patches/ /tmp/patches/ COPY host-x86_64/dist-mips-linux/mips-linux-gnu.defconfig /tmp/crosstool.defconfig RUN /scripts/crosstool-ng-build.sh diff --git a/src/ci/docker/host-x86_64/dist-mips-linux/mips-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-mips-linux/mips-linux-gnu.defconfig index 75743fe81416b..94db07922a226 100644 --- a/src/ci/docker/host-x86_64/dist-mips-linux/mips-linux-gnu.defconfig +++ b/src/ci/docker/host-x86_64/dist-mips-linux/mips-linux-gnu.defconfig @@ -2,8 +2,6 @@ CT_CONFIG_VERSION="4" CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" -CT_PATCH_BUNDLED_LOCAL=y -CT_LOCAL_PATCH_DIR="/tmp/patches" CT_ARCH_MIPS=y CT_ARCH_mips_o32=y CT_ARCH_ARCH="mips32r2" diff --git a/src/ci/docker/host-x86_64/dist-mips-linux/patches/glibc/2.23/0001-MIPS-SPARC-fix-wrong-vfork-aliases-in-libpthread.so.patch b/src/ci/docker/host-x86_64/dist-mips-linux/patches/glibc/2.23/0001-MIPS-SPARC-fix-wrong-vfork-aliases-in-libpthread.so.patch deleted file mode 100644 index 393084df3249a..0000000000000 --- a/src/ci/docker/host-x86_64/dist-mips-linux/patches/glibc/2.23/0001-MIPS-SPARC-fix-wrong-vfork-aliases-in-libpthread.so.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 43c2948756bb6e144c7b871e827bba37d61ad3a3 Mon Sep 17 00:00:00 2001 -From: Aurelien Jarno -Date: Sat, 18 Jun 2016 19:11:23 +0200 -Subject: [PATCH 1/2] MIPS, SPARC: fix wrong vfork aliases in libpthread.so - -With recent binutils versions the GNU libc fails to build on at least -MISP and SPARC, with this kind of error: - - /home/aurel32/glibc/glibc-build/nptl/libpthread.so:(*IND*+0x0): multiple definition of `vfork@GLIBC_2.0' - /home/aurel32/glibc/glibc-build/nptl/libpthread.so::(.text+0xee50): first defined here - -It appears that on these architectures pt-vfork.S includes vfork.S -(through the alpha version of pt-vfork.S) and that the __vfork aliases -are not conditionalized on IS_IN (libc) like on other architectures. -Therefore the aliases are also wrongly included in libpthread.so. - -Fix this by properly conditionalizing the aliases like on other -architectures. - -Changelog: - * sysdeps/unix/sysv/linux/mips/vfork.S (__vfork): Conditionalize - hidden_def, weak_alias and strong_alias on [IS_IN (libc)]. - * sysdeps/unix/sysv/linux/sparc/sparc32/vfork.S: Likewise. - * sysdeps/unix/sysv/linux/sparc/sparc64/vfork.S: Likewise. ---- - sysdeps/unix/sysv/linux/mips/vfork.S | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/sysdeps/unix/sysv/linux/mips/vfork.S b/sysdeps/unix/sysv/linux/mips/vfork.S -index 8c6615143708..c0c0ce699159 100644 ---- a/sysdeps/unix/sysv/linux/mips/vfork.S -+++ b/sysdeps/unix/sysv/linux/mips/vfork.S -@@ -106,6 +106,8 @@ L(error): - #endif - END(__vfork) - -+#if IS_IN (libc) - libc_hidden_def(__vfork) - weak_alias (__vfork, vfork) - strong_alias (__vfork, __libc_vfork) -+#endif --- -2.37.3 - diff --git a/src/ci/docker/host-x86_64/dist-mips-linux/patches/glibc/2.23/0002-MIPS-SPARC-more-fixes-to-the-vfork-aliases-in-libpth.patch b/src/ci/docker/host-x86_64/dist-mips-linux/patches/glibc/2.23/0002-MIPS-SPARC-more-fixes-to-the-vfork-aliases-in-libpth.patch deleted file mode 100644 index e28c7ae99f098..0000000000000 --- a/src/ci/docker/host-x86_64/dist-mips-linux/patches/glibc/2.23/0002-MIPS-SPARC-more-fixes-to-the-vfork-aliases-in-libpth.patch +++ /dev/null @@ -1,63 +0,0 @@ -From b87c1ec3fa398646f042a68f0ce0f7d09c1348c7 Mon Sep 17 00:00:00 2001 -From: Aurelien Jarno -Date: Tue, 21 Jun 2016 23:59:37 +0200 -Subject: [PATCH 2/2] MIPS, SPARC: more fixes to the vfork aliases in - libpthread.so - -Commit 43c29487 tried to fix the vfork aliases in libpthread.so on MIPS -and SPARC, but failed to do it correctly, introducing an ABI change. - -This patch does the remaining changes needed to align the MIPS and SPARC -vfork implementations with the other architectures. That way the the -alpha version of pt-vfork.S works correctly for MIPS and SPARC. The -changes for alpha were done in 82aab97c. - -Changelog: - * sysdeps/unix/sysv/linux/mips/vfork.S (__vfork): Rename into - __libc_vfork. - (__vfork) [IS_IN (libc)]: Remove alias. - (__libc_vfork) [IS_IN (libc)]: Define as an alias. - * sysdeps/unix/sysv/linux/sparc/sparc32/vfork.S: Likewise. - * sysdeps/unix/sysv/linux/sparc/sparc64/vfork.S: Likewise. ---- - sysdeps/unix/sysv/linux/mips/vfork.S | 12 ++++++------ - 1 file changed, 6 insertions(+), 6 deletions(-) - -diff --git a/sysdeps/unix/sysv/linux/mips/vfork.S b/sysdeps/unix/sysv/linux/mips/vfork.S -index c0c0ce699159..1867c8626ebe 100644 ---- a/sysdeps/unix/sysv/linux/mips/vfork.S -+++ b/sysdeps/unix/sysv/linux/mips/vfork.S -@@ -31,13 +31,13 @@ - LOCALSZ= 1 - FRAMESZ= (((NARGSAVE+LOCALSZ)*SZREG)+ALSZ)&ALMASK - GPOFF= FRAMESZ-(1*SZREG) --NESTED(__vfork,FRAMESZ,sp) -+NESTED(__libc_vfork,FRAMESZ,sp) - #ifdef __PIC__ - SETUP_GP - #endif - PTR_SUBU sp, FRAMESZ - cfi_adjust_cfa_offset (FRAMESZ) -- SETUP_GP64_REG (a5, __vfork) -+ SETUP_GP64_REG (a5, __libc_vfork) - #ifdef __PIC__ - SAVE_GP (GPOFF) - #endif -@@ -104,10 +104,10 @@ L(error): - RESTORE_GP64_REG - j __syscall_error - #endif -- END(__vfork) -+ END(__libc_vfork) - - #if IS_IN (libc) --libc_hidden_def(__vfork) --weak_alias (__vfork, vfork) --strong_alias (__vfork, __libc_vfork) -+weak_alias (__libc_vfork, vfork) -+strong_alias (__libc_vfork, __vfork) -+libc_hidden_def (__vfork) - #endif --- -2.37.3 - diff --git a/src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile index 10f31075e2d55..18b0375f40080 100644 --- a/src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-mips64-linux/Dockerfile @@ -11,7 +11,6 @@ RUN sh /scripts/rustbuild-setup.sh WORKDIR /tmp COPY scripts/crosstool-ng-build.sh /scripts/ -COPY host-x86_64/dist-mips-linux/patches/ /tmp/patches/ COPY host-x86_64/dist-mips64-linux/mips64-linux-gnu.defconfig /tmp/crosstool.defconfig RUN /scripts/crosstool-ng-build.sh diff --git a/src/ci/docker/host-x86_64/dist-mips64-linux/mips64-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-mips64-linux/mips64-linux-gnu.defconfig index 4b8f7a5492033..f295a2fafc6b1 100644 --- a/src/ci/docker/host-x86_64/dist-mips64-linux/mips64-linux-gnu.defconfig +++ b/src/ci/docker/host-x86_64/dist-mips64-linux/mips64-linux-gnu.defconfig @@ -2,8 +2,6 @@ CT_CONFIG_VERSION="4" CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" -CT_PATCH_BUNDLED_LOCAL=y -CT_LOCAL_PATCH_DIR="/tmp/patches" CT_ARCH_MIPS=y CT_ARCH_mips_n64=y CT_ARCH_64=y diff --git a/src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile index 5ab4a53de8a88..87407203880b4 100644 --- a/src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-mips64el-linux/Dockerfile @@ -11,7 +11,6 @@ RUN sh /scripts/rustbuild-setup.sh WORKDIR /tmp COPY scripts/crosstool-ng-build.sh /scripts/ -COPY host-x86_64/dist-mips-linux/patches/ /tmp/patches/ COPY host-x86_64/dist-mips64el-linux/mips64el-linux-gnu.defconfig /tmp/crosstool.defconfig RUN /scripts/crosstool-ng-build.sh diff --git a/src/ci/docker/host-x86_64/dist-mips64el-linux/mips64el-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-mips64el-linux/mips64el-linux-gnu.defconfig index 9c8eb5007b79b..47d6246356526 100644 --- a/src/ci/docker/host-x86_64/dist-mips64el-linux/mips64el-linux-gnu.defconfig +++ b/src/ci/docker/host-x86_64/dist-mips64el-linux/mips64el-linux-gnu.defconfig @@ -2,8 +2,6 @@ CT_CONFIG_VERSION="4" CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" -CT_PATCH_BUNDLED_LOCAL=y -CT_LOCAL_PATCH_DIR="/tmp/patches" CT_ARCH_MIPS=y CT_ARCH_mips_n64=y CT_ARCH_LE=y diff --git a/src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile index 0bbaf00339d0a..553f6ea86b72d 100644 --- a/src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-mipsel-linux/Dockerfile @@ -11,7 +11,6 @@ RUN sh /scripts/rustbuild-setup.sh WORKDIR /tmp COPY scripts/crosstool-ng-build.sh /scripts/ -COPY host-x86_64/dist-mips-linux/patches/ /tmp/patches/ COPY host-x86_64/dist-mipsel-linux/mipsel-linux-gnu.defconfig /tmp/crosstool.defconfig RUN /scripts/crosstool-ng-build.sh diff --git a/src/ci/docker/host-x86_64/dist-mipsel-linux/mipsel-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-mipsel-linux/mipsel-linux-gnu.defconfig index a9dae121e536a..5daa83ebc02a7 100644 --- a/src/ci/docker/host-x86_64/dist-mipsel-linux/mipsel-linux-gnu.defconfig +++ b/src/ci/docker/host-x86_64/dist-mipsel-linux/mipsel-linux-gnu.defconfig @@ -2,8 +2,6 @@ CT_CONFIG_VERSION="4" CT_PREFIX_DIR="/x-tools/${CT_TARGET}" CT_USE_MIRROR=y CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc" -CT_PATCH_BUNDLED_LOCAL=y -CT_LOCAL_PATCH_DIR="/tmp/patches" CT_ARCH_MIPS=y CT_ARCH_mips_o32=y CT_ARCH_LE=y diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile index 9ef391892497f..cb20f43cff706 100644 --- a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux/Dockerfile @@ -3,8 +3,8 @@ FROM ubuntu:22.04 COPY scripts/cross-apt-packages.sh /scripts/ RUN sh /scripts/cross-apt-packages.sh -COPY scripts/crosstool-ng-git.sh /scripts/ -RUN sh /scripts/crosstool-ng-git.sh +COPY scripts/crosstool-ng.sh /scripts/ +RUN sh /scripts/crosstool-ng.sh COPY scripts/rustbuild-setup.sh /scripts/ RUN sh /scripts/rustbuild-setup.sh @@ -18,7 +18,7 @@ RUN /scripts/crosstool-ng-build.sh WORKDIR /build RUN apt-get install -y --no-install-recommends rpm2cpio cpio -COPY host-x86_64/dist-powerpc64le-linux/shared.sh host-x86_64/dist-powerpc64le-linux/build-powerpc64le-toolchain.sh /build/ +COPY scripts/shared.sh host-x86_64/dist-powerpc64le-linux/build-powerpc64le-toolchain.sh /build/ RUN ./build-powerpc64le-toolchain.sh COPY scripts/sccache.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/shared.sh b/src/ci/docker/host-x86_64/dist-powerpc64le-linux/shared.sh deleted file mode 100644 index dc86dddd464f2..0000000000000 --- a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/shared.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -hide_output() { - set +x - on_err=" -echo ERROR: An error was encountered with the build. -cat /tmp/build.log -exit 1 -" - trap "$on_err" ERR - bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & - PING_LOOP_PID=$! - "$@" &> /tmp/build.log - trap - ERR - kill $PING_LOOP_PID - set -x -} diff --git a/src/ci/docker/host-x86_64/dist-riscv64-linux/patches/gcc/8.5.0/0002-hidden-jump-target.patch b/src/ci/docker/host-x86_64/dist-riscv64-linux/patches/gcc/8.5.0/0002-hidden-jump-target.patch index 7ae4469428b13..1ae0ecf6cb5ea 100644 --- a/src/ci/docker/host-x86_64/dist-riscv64-linux/patches/gcc/8.5.0/0002-hidden-jump-target.patch +++ b/src/ci/docker/host-x86_64/dist-riscv64-linux/patches/gcc/8.5.0/0002-hidden-jump-target.patch @@ -10,7 +10,7 @@ https://sourceware.org/bugzilla/show_bug.cgi?id=28509 And this is the first version of the proposed binutils patch, https://sourceware.org/pipermail/binutils/2021-November/118398.html -After applying the binutils patch, I get the the unexpected error when +After applying the binutils patch, I get the unexpected error when building libgcc, /scratch/nelsonc/riscv-gnu-toolchain/riscv-gcc/libgcc/config/riscv/div.S:42: diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile index bbb4fe216a55e..0b4682ac32ba0 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile @@ -21,6 +21,8 @@ RUN yum upgrade -y && \ git \ glibc-devel.i686 \ glibc-devel.x86_64 \ + glibc-static.i686 \ + glibc-static.x86_64 \ libedit-devel \ libstdc++-devel.i686 \ libstdc++-devel.x86_64 \ @@ -34,6 +36,7 @@ RUN yum upgrade -y && \ python3 \ unzip \ wget \ + flex \ xz \ zlib-devel.i686 \ zlib-devel.x86_64 \ @@ -47,11 +50,12 @@ ENV PKG_CONFIG_PATH=/rustroot/lib/pkgconfig # Clang needs to access GCC headers to enable linker plugin LTO WORKDIR /tmp RUN mkdir /home/user -COPY host-x86_64/dist-x86_64-linux/shared.sh /tmp/ +COPY scripts/shared.sh /tmp/ # Need at least GCC 5.1 to compile LLVM nowadays -COPY host-x86_64/dist-x86_64-linux/build-gcc.sh /tmp/ +COPY scripts/build-gcc.sh /tmp/ ENV GCC_VERSION=9.5.0 +ENV GCC_BUILD_TARGET=x86_64-pc-linux-gnu RUN ./build-gcc.sh && yum remove -y gcc gcc-c++ # LLVM 17 needs cmake 3.20 or higher. @@ -60,12 +64,13 @@ RUN ./cmake.sh # Now build LLVM+Clang, afterwards configuring further compilations to use the # clang/clang++ compilers. -COPY host-x86_64/dist-x86_64-linux/build-clang.sh /tmp/ +COPY scripts/build-clang.sh /tmp/ +ENV LLVM_BUILD_TARGETS=X86 RUN ./build-clang.sh ENV CC=clang CXX=clang++ # Build zstd to enable `llvm.libzstd`. -COPY host-x86_64/dist-x86_64-linux/build-zstd.sh /tmp/ +COPY scripts/build-zstd.sh /tmp/ RUN ./build-zstd.sh COPY scripts/sccache.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-zstd.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-zstd.sh deleted file mode 100755 index a3d37ccc31120..0000000000000 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-zstd.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -set -ex - -hide_output() { - set +x - on_err=" -echo ERROR: An error was encountered with the build. -cat /tmp/zstd_build.log -exit 1 -" - trap "$on_err" ERR - bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & - PING_LOOP_PID=$! - "$@" &> /tmp/zstd_build.log - trap - ERR - kill $PING_LOOP_PID - rm /tmp/zstd_build.log - set -x -} - -ZSTD=1.5.6 -curl -L https://github.com/facebook/zstd/releases/download/v$ZSTD/zstd-$ZSTD.tar.gz | tar xzf - - -cd zstd-$ZSTD -CFLAGS=-fPIC hide_output make -j$(nproc) VERBOSE=1 -hide_output make install - -cd .. -rm -rf zstd-$ZSTD diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/shared.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/shared.sh deleted file mode 100644 index dc86dddd464f2..0000000000000 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/shared.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -hide_output() { - set +x - on_err=" -echo ERROR: An error was encountered with the build. -cat /tmp/build.log -exit 1 -" - trap "$on_err" ERR - bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & - PING_LOOP_PID=$! - "$@" &> /tmp/build.log - trap - ERR - kill $PING_LOOP_PID - set -x -} diff --git a/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile b/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile index f52e306974cce..9ca8cc740a5c7 100644 --- a/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile @@ -1,4 +1,6 @@ -FROM ubuntu:22.04 +# We use the ghcr base image because ghcr doesn't have a rate limit +# and the mingw-check-tidy job doesn't cache docker images in CI. +FROM ghcr.io/rust-lang/ubuntu:22.04 ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index d408cd518a00b..9234c6dc921ee 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -29,7 +29,7 @@ ENV PATH="/node/bin:${PATH}" # Install es-check # Pin its version to prevent unrelated CI failures due to future es-check versions. -RUN npm install es-check@6.1.1 eslint@8.6.0 -g +RUN npm install es-check@6.1.1 eslint@8.6.0 typescript@5.7.3 -g COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh @@ -68,4 +68,5 @@ ENV SCRIPT \ es-check es2019 ../src/librustdoc/html/static/js/*.js && \ eslint -c ../src/librustdoc/html/static/.eslintrc.js ../src/librustdoc/html/static/js/*.js && \ eslint -c ../src/tools/rustdoc-js/.eslintrc.js ../src/tools/rustdoc-js/tester.js && \ - eslint -c ../src/tools/rustdoc-gui/.eslintrc.js ../src/tools/rustdoc-gui/tester.js + eslint -c ../src/tools/rustdoc-gui/.eslintrc.js ../src/tools/rustdoc-gui/tester.js && \ + tsc --project ../src/librustdoc/html/static/js/tsconfig.json diff --git a/src/ci/docker/host-x86_64/x86_64-fuchsia/build-fuchsia.sh b/src/ci/docker/host-x86_64/x86_64-fuchsia/build-fuchsia.sh index 7027c93857c3c..d0a138b79033b 100755 --- a/src/ci/docker/host-x86_64/x86_64-fuchsia/build-fuchsia.sh +++ b/src/ci/docker/host-x86_64/x86_64-fuchsia/build-fuchsia.sh @@ -35,7 +35,7 @@ PICK_REFS=() # commit hash of fuchsia.git and some other repos in the "monorepo" checkout, in # addition to versions of prebuilts. It should be bumped regularly by the # Fuchsia team – we aim for every 1-2 months. -INTEGRATION_SHA=bb38af4e3d45e490531b71fc52a460003141d032 +INTEGRATION_SHA=f6f83d3e3852209f7752be55694006afbe979e50 checkout=fuchsia jiri=.jiri_root/bin/jiri diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile index e157debfd09a8..0a58f337d9ddb 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile @@ -54,8 +54,8 @@ ENV RUST_CONFIGURE_ARGS \ --set rust.randomize-layout=true \ --set rust.thin-lto-import-instr-limit=10 -COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/ -COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/ +COPY scripts/shared.sh /scripts/ +COPY scripts/build-gccjit.sh /scripts/ RUN /scripts/build-gccjit.sh /scripts diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile index e7016e7d3c0d6..092847cdfe04c 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile @@ -54,8 +54,8 @@ ENV RUST_CONFIGURE_ARGS \ --set rust.randomize-layout=true \ --set rust.thin-lto-import-instr-limit=10 -COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/ -COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/ +COPY scripts/shared.sh /scripts/ +COPY scripts/build-gccjit.sh /scripts/ RUN /scripts/build-gccjit.sh /scripts diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile index 2a09cd54b139a..ab749b3fdd5a6 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile @@ -89,8 +89,8 @@ ENV HOST_TARGET x86_64-unknown-linux-gnu # assertions enabled! Therefore, we cannot force download CI rustc. #ENV FORCE_CI_RUSTC 1 -COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/ -COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/ +COPY scripts/shared.sh /scripts/ +COPY scripts/build-gccjit.sh /scripts/ RUN /scripts/build-gccjit.sh /scripts diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version index 7211b157c6945..a158e5b6253cd 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version @@ -1 +1 @@ -0.18.2 \ No newline at end of file +0.20.2 \ No newline at end of file diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh index d1bc0519bc1eb..d2697ac27ab7a 100755 --- a/src/ci/docker/run.sh +++ b/src/ci/docker/run.sh @@ -123,6 +123,7 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then build_args+=("--build-arg" "SCRIPT_ARG=${DOCKER_SCRIPT}") fi + GHCR_BUILDKIT_IMAGE="ghcr.io/rust-lang/buildkit:buildx-stable-1" # On non-CI jobs, we try to download a pre-built image from the rust-lang-ci # ghcr.io registry. If it is not possible, we fall back to building the image # locally. @@ -140,7 +141,9 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then elif [[ "$PR_CI_JOB" == "1" ]]; then # Enable a new Docker driver so that --cache-from works with a registry backend - docker buildx create --use --driver docker-container + # Use a custom image to avoid DockerHub rate limits + docker buildx create --use --driver docker-container \ + --driver-opt image=${GHCR_BUILDKIT_IMAGE} # Build the image using registry caching backend retry docker \ @@ -156,7 +159,9 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then --password-stdin # Enable a new Docker driver so that --cache-from/to works with a registry backend - docker buildx create --use --driver docker-container + # Use a custom image to avoid DockerHub rate limits + docker buildx create --use --driver docker-container \ + --driver-opt image=${GHCR_BUILDKIT_IMAGE} # Build the image using registry caching backend retry docker \ diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh b/src/ci/docker/scripts/build-clang.sh similarity index 79% rename from src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh rename to src/ci/docker/scripts/build-clang.sh index 3c8123d90de0b..841a0adb2ab8c 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh +++ b/src/ci/docker/scripts/build-clang.sh @@ -21,6 +21,12 @@ cd clang-build # include path, /rustroot/include, to clang's default include path. INC="/rustroot/include:/usr/include" +GCC_PLUGIN_TARGET=$GCC_BUILD_TARGET +# We build gcc for the i686 job on x86_64 so the plugin will end up under an x86_64 path +if [[ $GCC_PLUGIN_TARGET == "i686-pc-linux-gnu" ]]; then + GCC_PLUGIN_TARGET=x86_64-pc-linux-gnu +fi + # We need compiler-rt for the profile runtime (used later to PGO the LLVM build) # but sanitizers aren't currently building. Since we don't need those, just # disable them. BOLT is used for optimizing LLVM. @@ -34,12 +40,12 @@ hide_output \ -DCOMPILER_RT_BUILD_XRAY=OFF \ -DCOMPILER_RT_BUILD_MEMPROF=OFF \ -DCOMPILER_RT_BUILD_CTX_PROFILE=OFF \ - -DLLVM_TARGETS_TO_BUILD=X86 \ + -DLLVM_TARGETS_TO_BUILD=$LLVM_BUILD_TARGETS \ -DLLVM_INCLUDE_BENCHMARKS=OFF \ -DLLVM_INCLUDE_TESTS=OFF \ -DLLVM_INCLUDE_EXAMPLES=OFF \ -DLLVM_ENABLE_PROJECTS="clang;lld;compiler-rt;bolt" \ - -DLLVM_BINUTILS_INCDIR="/rustroot/lib/gcc/x86_64-pc-linux-gnu/$GCC_VERSION/plugin/include/" \ + -DLLVM_BINUTILS_INCDIR="/rustroot/lib/gcc/$GCC_PLUGIN_TARGET/$GCC_VERSION/plugin/include/" \ -DC_INCLUDE_DIRS="$INC" hide_output make -j$(nproc) diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh b/src/ci/docker/scripts/build-gcc.sh similarity index 87% rename from src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh rename to src/ci/docker/scripts/build-gcc.sh index 57d4d338a5065..11db5aa811ce8 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh +++ b/src/ci/docker/scripts/build-gcc.sh @@ -51,7 +51,9 @@ cd .. rm -rf gcc-build rm -rf gcc-$GCC -# FIXME: clang doesn't find 32-bit libraries in /rustroot/lib, -# but it does look all the way under /rustroot/lib/[...]/32, -# so we can link stuff there to help it out. -ln /rustroot/lib/*.{a,so} -rst /rustroot/lib/gcc/x86_64-pc-linux-gnu/$GCC/32/ +if [[ $GCC_BUILD_TARGET == "i686-pc-linux-gnu" ]]; then + # FIXME: clang doesn't find 32-bit libraries in /rustroot/lib, + # but it does look all the way under /rustroot/lib/[...]/32, + # so we can link stuff there to help it out. + ln /rustroot/lib/*.{a,so} -rst /rustroot/lib/gcc/x86_64-pc-linux-gnu/$GCC/32/ +fi diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gccjit.sh b/src/ci/docker/scripts/build-gccjit.sh similarity index 93% rename from src/ci/docker/host-x86_64/dist-x86_64-linux/build-gccjit.sh rename to src/ci/docker/scripts/build-gccjit.sh index c565922dcd137..43ed2270d313f 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gccjit.sh +++ b/src/ci/docker/scripts/build-gccjit.sh @@ -3,7 +3,7 @@ GIT_REPO="https://github.com/rust-lang/gcc" # This commit hash needs to be updated to use a more recent gcc fork version. -GIT_COMMIT="e744a9459d33864067214741daf5c5bc2a7b88c6" +GIT_COMMIT="45648c2edd4ecd862d9f08196d3d6c6ccba79f07" set -ex diff --git a/src/ci/docker/scripts/build-zstd.sh b/src/ci/docker/scripts/build-zstd.sh new file mode 100755 index 0000000000000..cffa7151e3864 --- /dev/null +++ b/src/ci/docker/scripts/build-zstd.sh @@ -0,0 +1,35 @@ +#!/bin/bash +set -ex + +hide_output() { + set +x + on_err=" +echo ERROR: An error was encountered with the build. +cat /tmp/zstd_build.log +exit 1 +" + trap "$on_err" ERR + bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & + PING_LOOP_PID=$! + "$@" &> /tmp/zstd_build.log + trap - ERR + kill $PING_LOOP_PID + rm /tmp/zstd_build.log + set -x +} + +ZSTD=1.5.6 +curl -L https://github.com/facebook/zstd/releases/download/v$ZSTD/zstd-$ZSTD.tar.gz | tar xzf - + +cd zstd-$ZSTD +CFLAGS=-fPIC hide_output make -j$(nproc) VERBOSE=1 +hide_output make install + +# It doesn't seem to be possible to move destination directory +# of the `make install` above. We thus copy the built artifacts +# manually to our custom rustroot, so that it can be found through +# LD_LIBRARY_PATH. +cp /usr/local/lib/libzstd* /rustroot/lib64 + +cd .. +rm -rf zstd-$ZSTD diff --git a/src/ci/docker/scripts/crosstool-ng-git.sh b/src/ci/docker/scripts/crosstool-ng-git.sh deleted file mode 100644 index e86810ae6133e..0000000000000 --- a/src/ci/docker/scripts/crosstool-ng-git.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh -set -ex - -URL=https://github.com/crosstool-ng/crosstool-ng -REV=ed12fa68402f58e171a6f79500f73f4781fdc9e5 - -mkdir crosstool-ng -cd crosstool-ng -git init -git fetch --depth=1 ${URL} ${REV} -git reset --hard FETCH_HEAD -./bootstrap -./configure --prefix=/usr/local -make -j$(nproc) -make install -cd .. -rm -rf crosstool-ng diff --git a/src/ci/docker/scripts/crosstool-ng.sh b/src/ci/docker/scripts/crosstool-ng.sh index c3ee19b8d2c1f..eee912b0d750d 100644 --- a/src/ci/docker/scripts/crosstool-ng.sh +++ b/src/ci/docker/scripts/crosstool-ng.sh @@ -1,7 +1,7 @@ #!/bin/sh set -ex -CT_NG=1.26.0 +CT_NG=1.27.0 url="https://github.com/crosstool-ng/crosstool-ng/archive/crosstool-ng-$CT_NG.tar.gz" curl -Lf $url | tar xzf - diff --git a/src/ci/docker/scripts/emscripten.sh b/src/ci/docker/scripts/emscripten.sh deleted file mode 100644 index 8b2b39ee1629e..0000000000000 --- a/src/ci/docker/scripts/emscripten.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -set -ex - -hide_output() { - set +x - on_err=" -echo ERROR: An error was encountered with the build. -cat /tmp/build.log -exit 1 -" - trap "$on_err" ERR - bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & - PING_LOOP_PID=$! - "$@" &> /tmp/build.log - trap - ERR - kill $PING_LOOP_PID - rm -f /tmp/build.log - set -x -} - -git clone https://github.com/emscripten-core/emsdk.git /emsdk-portable -cd /emsdk-portable -hide_output ./emsdk install 3.1.68 -./emsdk activate 3.1.68 diff --git a/src/ci/docker/scripts/musl.sh b/src/ci/docker/scripts/musl.sh index ece8e6c15c0cb..9878bec6fbe8c 100644 --- a/src/ci/docker/scripts/musl.sh +++ b/src/ci/docker/scripts/musl.sh @@ -30,6 +30,47 @@ MUSL=musl-1.2.3 # may have been downloaded in a previous run if [ ! -d $MUSL ]; then curl https://www.musl-libc.org/releases/$MUSL.tar.gz | tar xzf - + + # Apply patches for CVE-2025-26519. At the time of adding these patches no release containing them + # has been published by the musl project, so we just apply them directly on top of the version we + # were distributing already. The patches should be removed once we upgrade to musl >= 1.2.6. + # + # Advisory: https://www.openwall.com/lists/musl/2025/02/13/1 + # + # Patches applied: + # - https://www.openwall.com/lists/musl/2025/02/13/1/1 + # - https://www.openwall.com/lists/musl/2025/02/13/1/2 + # + # ignore-tidy-tab + # ignore-tidy-linelength + patch -p1 -d $MUSL <= 93 || d >= 94) { + c += (0xa1-0x81); + d += 0xa1; +- if (c >= 93 || c>=0xc6-0x81 && d>0x52) ++ if (c > 0xc6-0x81 || c==0xc6-0x81 && d>0x52) + goto ilseq; + if (d-'A'<26) d = d-'A'; + else if (d-'a'<26) d = d-'a'+26; +EOF + patch -p1 -d $MUSL <4) goto ilseq; + *out += k; + *outb -= k; + break; +EOF fi cd $MUSL diff --git a/src/ci/docker/scripts/rfl-build.sh b/src/ci/docker/scripts/rfl-build.sh index 8776e0f0be901..22468d84678f9 100755 --- a/src/ci/docker/scripts/rfl-build.sh +++ b/src/ci/docker/scripts/rfl-build.sh @@ -2,22 +2,16 @@ set -euo pipefail -LINUX_VERSION=v6.13-rc1 +LINUX_VERSION=50e57739141b41f731ab31f8380821c7969f9dc4 # Build rustc, rustdoc, cargo, clippy-driver and rustfmt ../x.py build --stage 2 library rustdoc clippy rustfmt ../x.py build --stage 0 cargo -# Install rustup so that we can use the built toolchain easily, and also -# install bindgen in an easy way. -curl --proto '=https' --tlsv1.2 -sSf -o rustup.sh https://sh.rustup.rs -sh rustup.sh -y --default-toolchain none +BUILD_DIR=$(realpath ./build/x86_64-unknown-linux-gnu) -source /cargo/env - -BUILD_DIR=$(realpath ./build) -rustup toolchain link local "${BUILD_DIR}"/x86_64-unknown-linux-gnu/stage2 -rustup default local +# Provide path to rustc, rustdoc, clippy-driver and rustfmt to RfL +export PATH=${PATH}:${BUILD_DIR}/stage2/bin mkdir -p rfl cd rfl @@ -28,15 +22,19 @@ rm -rf linux || true # Download Linux at a specific commit mkdir -p linux git -C linux init -git -C linux remote add origin https://github.com/Rust-for-Linux/linux.git +git -C linux remote add origin https://github.com/Darksonn/linux.git git -C linux fetch --depth 1 origin ${LINUX_VERSION} git -C linux checkout FETCH_HEAD # Install bindgen -"${BUILD_DIR}"/x86_64-unknown-linux-gnu/stage0/bin/cargo install \ +"${BUILD_DIR}"/stage0/bin/cargo install \ --version $(linux/scripts/min-tool-version.sh bindgen) \ + --root ${BUILD_DIR}/bindgen \ bindgen-cli +# Provide path to bindgen to RfL +export PATH=${PATH}:${BUILD_DIR}/bindgen/bin + # Configure Rust for Linux cat < linux/kernel/configs/rfl-for-rust-ci.config # CONFIG_WERROR is not set diff --git a/src/ci/github-actions/calculate-job-matrix.py b/src/ci/github-actions/calculate-job-matrix.py deleted file mode 100755 index 1f994f0ffd2ef..0000000000000 --- a/src/ci/github-actions/calculate-job-matrix.py +++ /dev/null @@ -1,210 +0,0 @@ -#!/usr/bin/env python3 - -""" -This script serves for generating a matrix of jobs that should -be executed on CI. - -It reads job definitions from `src/ci/github-actions/jobs.yml` -and filters them based on the event that happened on CI. -""" - -import dataclasses -import json -import logging -import os -import re -import typing -from pathlib import Path -from typing import List, Dict, Any, Optional - -import yaml - -CI_DIR = Path(__file__).absolute().parent.parent -JOBS_YAML_PATH = Path(__file__).absolute().parent / "jobs.yml" - -Job = Dict[str, Any] - - -def name_jobs(jobs: List[Dict], prefix: str) -> List[Job]: - """ - Add a `name` attribute to each job, based on its image and the given `prefix`. - """ - for job in jobs: - job["name"] = f"{prefix} - {job['image']}" - return jobs - - -def add_base_env(jobs: List[Job], environment: Dict[str, str]) -> List[Job]: - """ - Prepends `environment` to the `env` attribute of each job. - The `env` of each job has higher precedence than `environment`. - """ - for job in jobs: - env = environment.copy() - env.update(job.get("env", {})) - job["env"] = env - return jobs - - -@dataclasses.dataclass -class PRRunType: - pass - - -@dataclasses.dataclass -class TryRunType: - custom_jobs: List[str] - - -@dataclasses.dataclass -class AutoRunType: - pass - - -WorkflowRunType = typing.Union[PRRunType, TryRunType, AutoRunType] - - -@dataclasses.dataclass -class GitHubCtx: - event_name: str - ref: str - repository: str - commit_message: Optional[str] - - -def get_custom_jobs(ctx: GitHubCtx) -> List[str]: - """ - Tries to parse names of specific CI jobs that should be executed in the form of - try-job: - from the commit message of the passed GitHub context. - """ - if ctx.commit_message is None: - return [] - - regex = re.compile(r"^try-job: (.*)", re.MULTILINE) - jobs = [] - for match in regex.finditer(ctx.commit_message): - jobs.append(match.group(1)) - return jobs - - -def find_run_type(ctx: GitHubCtx) -> Optional[WorkflowRunType]: - if ctx.event_name == "pull_request": - return PRRunType() - elif ctx.event_name == "push": - try_build = ctx.ref in ( - "refs/heads/try", - "refs/heads/try-perf", - "refs/heads/automation/bors/try", - ) - - # Unrolled branch from a rollup for testing perf - # This should **not** allow custom try jobs - is_unrolled_perf_build = ctx.ref == "refs/heads/try-perf" - - if try_build: - custom_jobs = [] - if not is_unrolled_perf_build: - custom_jobs = get_custom_jobs(ctx) - return TryRunType(custom_jobs=custom_jobs) - - if ctx.ref == "refs/heads/auto": - return AutoRunType() - - return None - - -def calculate_jobs(run_type: WorkflowRunType, job_data: Dict[str, Any]) -> List[Job]: - if isinstance(run_type, PRRunType): - return add_base_env(name_jobs(job_data["pr"], "PR"), job_data["envs"]["pr"]) - elif isinstance(run_type, TryRunType): - jobs = job_data["try"] - custom_jobs = run_type.custom_jobs - if custom_jobs: - if len(custom_jobs) > 10: - raise Exception( - f"It is only possible to schedule up to 10 custom jobs, " - f"received {len(custom_jobs)} jobs" - ) - - jobs = [] - unknown_jobs = [] - for custom_job in custom_jobs: - job = [j for j in job_data["auto"] if j["image"] == custom_job] - if not job: - unknown_jobs.append(custom_job) - continue - jobs.append(job[0]) - if unknown_jobs: - raise Exception( - f"Custom job(s) `{unknown_jobs}` not found in auto jobs" - ) - - return add_base_env(name_jobs(jobs, "try"), job_data["envs"]["try"]) - elif isinstance(run_type, AutoRunType): - return add_base_env( - name_jobs(job_data["auto"], "auto"), job_data["envs"]["auto"] - ) - - return [] - - -def skip_jobs(jobs: List[Dict[str, Any]], channel: str) -> List[Job]: - """ - Skip CI jobs that are not supposed to be executed on the given `channel`. - """ - return [j for j in jobs if j.get("only_on_channel", channel) == channel] - - -def get_github_ctx() -> GitHubCtx: - event_name = os.environ["GITHUB_EVENT_NAME"] - - commit_message = None - if event_name == "push": - commit_message = os.environ["COMMIT_MESSAGE"] - return GitHubCtx( - event_name=event_name, - ref=os.environ["GITHUB_REF"], - repository=os.environ["GITHUB_REPOSITORY"], - commit_message=commit_message, - ) - - -def format_run_type(run_type: WorkflowRunType) -> str: - if isinstance(run_type, PRRunType): - return "pr" - elif isinstance(run_type, AutoRunType): - return "auto" - elif isinstance(run_type, TryRunType): - return "try" - else: - raise AssertionError() - - -if __name__ == "__main__": - logging.basicConfig(level=logging.INFO) - - with open(JOBS_YAML_PATH) as f: - data = yaml.safe_load(f) - - github_ctx = get_github_ctx() - - run_type = find_run_type(github_ctx) - logging.info(f"Job type: {run_type}") - - with open(CI_DIR / "channel") as f: - channel = f.read().strip() - - jobs = [] - if run_type is not None: - jobs = calculate_jobs(run_type, data) - jobs = skip_jobs(jobs, channel) - - if not jobs: - raise Exception("Scheduled job list is empty, this is an error") - - run_type = format_run_type(run_type) - - logging.info(f"Output:\n{yaml.dump(dict(jobs=jobs, run_type=run_type), indent=4)}") - print(f"jobs={json.dumps(jobs)}") - print(f"run_type={run_type}") diff --git a/src/ci/github-actions/ci.py b/src/ci/github-actions/ci.py new file mode 100755 index 0000000000000..c93766ef33a0d --- /dev/null +++ b/src/ci/github-actions/ci.py @@ -0,0 +1,318 @@ +#!/usr/bin/env python3 + +""" +This script contains CI functionality. +It can be used to generate a matrix of jobs that should +be executed on CI, or run a specific CI job locally. + +It reads job definitions from `src/ci/github-actions/jobs.yml`. +""" + +import argparse +import dataclasses +import json +import logging +import os +import re +import subprocess +import typing +from pathlib import Path +from typing import List, Dict, Any, Optional + +import yaml + +CI_DIR = Path(__file__).absolute().parent.parent +JOBS_YAML_PATH = Path(__file__).absolute().parent / "jobs.yml" + +Job = Dict[str, Any] + + +def add_job_properties(jobs: List[Dict], prefix: str) -> List[Job]: + """ + Modify the `name` attribute of each job, based on its base name and the given `prefix`. + Add an `image` attribute to each job, based on its image. + """ + modified_jobs = [] + for job in jobs: + # Create a copy of the `job` dictionary to avoid modifying `jobs` + new_job = dict(job) + new_job["image"] = get_job_image(new_job) + new_job["full_name"] = f"{prefix} - {new_job['name']}" + modified_jobs.append(new_job) + return modified_jobs + + +def add_base_env(jobs: List[Job], environment: Dict[str, str]) -> List[Job]: + """ + Prepends `environment` to the `env` attribute of each job. + The `env` of each job has higher precedence than `environment`. + """ + modified_jobs = [] + for job in jobs: + env = environment.copy() + env.update(job.get("env", {})) + + new_job = dict(job) + new_job["env"] = env + modified_jobs.append(new_job) + return modified_jobs + + +@dataclasses.dataclass +class PRRunType: + pass + + +@dataclasses.dataclass +class TryRunType: + custom_jobs: List[str] + + +@dataclasses.dataclass +class AutoRunType: + pass + + +WorkflowRunType = typing.Union[PRRunType, TryRunType, AutoRunType] + + +@dataclasses.dataclass +class GitHubCtx: + event_name: str + ref: str + repository: str + commit_message: Optional[str] + + +def get_custom_jobs(ctx: GitHubCtx) -> List[str]: + """ + Tries to parse names of specific CI jobs that should be executed in the form of + try-job: + from the commit message of the passed GitHub context. + """ + if ctx.commit_message is None: + return [] + + regex = re.compile(r"^try-job: (.*)", re.MULTILINE) + jobs = [] + for match in regex.finditer(ctx.commit_message): + jobs.append(match.group(1)) + return jobs + + +def find_run_type(ctx: GitHubCtx) -> Optional[WorkflowRunType]: + if ctx.event_name == "pull_request": + return PRRunType() + elif ctx.event_name == "push": + try_build = ctx.ref in ( + "refs/heads/try", + "refs/heads/try-perf", + "refs/heads/automation/bors/try", + ) + + # Unrolled branch from a rollup for testing perf + # This should **not** allow custom try jobs + is_unrolled_perf_build = ctx.ref == "refs/heads/try-perf" + + if try_build: + custom_jobs = [] + if not is_unrolled_perf_build: + custom_jobs = get_custom_jobs(ctx) + return TryRunType(custom_jobs=custom_jobs) + + if ctx.ref == "refs/heads/auto": + return AutoRunType() + + return None + + +def calculate_jobs(run_type: WorkflowRunType, job_data: Dict[str, Any]) -> List[Job]: + if isinstance(run_type, PRRunType): + return add_base_env( + add_job_properties(job_data["pr"], "PR"), job_data["envs"]["pr"] + ) + elif isinstance(run_type, TryRunType): + jobs = job_data["try"] + custom_jobs = run_type.custom_jobs + if custom_jobs: + if len(custom_jobs) > 10: + raise Exception( + f"It is only possible to schedule up to 10 custom jobs, " + f"received {len(custom_jobs)} jobs" + ) + + jobs = [] + unknown_jobs = [] + for custom_job in custom_jobs: + job = [j for j in job_data["auto"] if j["name"] == custom_job] + if not job: + unknown_jobs.append(custom_job) + continue + jobs.append(job[0]) + if unknown_jobs: + raise Exception( + f"Custom job(s) `{unknown_jobs}` not found in auto jobs" + ) + + return add_base_env(add_job_properties(jobs, "try"), job_data["envs"]["try"]) + elif isinstance(run_type, AutoRunType): + return add_base_env( + add_job_properties(job_data["auto"], "auto"), job_data["envs"]["auto"] + ) + + return [] + + +def skip_jobs(jobs: List[Dict[str, Any]], channel: str) -> List[Job]: + """ + Skip CI jobs that are not supposed to be executed on the given `channel`. + """ + return [j for j in jobs if j.get("only_on_channel", channel) == channel] + + +def get_github_ctx() -> GitHubCtx: + event_name = os.environ["GITHUB_EVENT_NAME"] + + commit_message = None + if event_name == "push": + commit_message = os.environ["COMMIT_MESSAGE"] + return GitHubCtx( + event_name=event_name, + ref=os.environ["GITHUB_REF"], + repository=os.environ["GITHUB_REPOSITORY"], + commit_message=commit_message, + ) + + +def format_run_type(run_type: WorkflowRunType) -> str: + if isinstance(run_type, PRRunType): + return "pr" + elif isinstance(run_type, AutoRunType): + return "auto" + elif isinstance(run_type, TryRunType): + return "try" + else: + raise AssertionError() + + +def get_job_image(job: Job) -> str: + """ + By default, the Docker image of a job is based on its name. + However, it can be overridden by its IMAGE environment variable. + """ + env = job.get("env", {}) + # Return the IMAGE environment variable if it exists, otherwise return the job name + return env.get("IMAGE", job["name"]) + + +def is_linux_job(job: Job) -> bool: + return "ubuntu" in job["os"] + + +def find_linux_job(job_data: Dict[str, Any], job_name: str, pr_jobs: bool) -> Job: + candidates = job_data["pr"] if pr_jobs else job_data["auto"] + jobs = [job for job in candidates if job.get("name") == job_name] + if len(jobs) == 0: + available_jobs = "\n".join( + sorted(job["name"] for job in candidates if is_linux_job(job)) + ) + raise Exception(f"""Job `{job_name}` not found in {'pr' if pr_jobs else 'auto'} jobs. +The following jobs are available: +{available_jobs}""") + assert len(jobs) == 1 + + job = jobs[0] + if not is_linux_job(job): + raise Exception("Only Linux jobs can be executed locally") + return job + + +def run_workflow_locally(job_data: Dict[str, Any], job_name: str, pr_jobs: bool): + DOCKER_DIR = Path(__file__).absolute().parent.parent / "docker" + + job = find_linux_job(job_data, job_name=job_name, pr_jobs=pr_jobs) + + custom_env = {} + # Replicate src/ci/scripts/setup-environment.sh + # Adds custom environment variables to the job + if job_name.startswith("dist-"): + if job_name.endswith("-alt"): + custom_env["DEPLOY_ALT"] = "1" + else: + custom_env["DEPLOY"] = "1" + custom_env.update({k: str(v) for (k, v) in job.get("env", {}).items()}) + + args = [str(DOCKER_DIR / "run.sh"), get_job_image(job)] + env_formatted = [f"{k}={v}" for (k, v) in sorted(custom_env.items())] + print(f"Executing `{' '.join(env_formatted)} {' '.join(args)}`") + + env = os.environ.copy() + env.update(custom_env) + + subprocess.run(args, env=env, check=True) + + +def calculate_job_matrix(job_data: Dict[str, Any]): + github_ctx = get_github_ctx() + + run_type = find_run_type(github_ctx) + logging.info(f"Job type: {run_type}") + + with open(CI_DIR / "channel") as f: + channel = f.read().strip() + + jobs = [] + if run_type is not None: + jobs = calculate_jobs(run_type, job_data) + jobs = skip_jobs(jobs, channel) + + if not jobs: + raise Exception("Scheduled job list is empty, this is an error") + + run_type = format_run_type(run_type) + + logging.info(f"Output:\n{yaml.dump(dict(jobs=jobs, run_type=run_type), indent=4)}") + print(f"jobs={json.dumps(jobs)}") + print(f"run_type={run_type}") + + +def create_cli_parser(): + parser = argparse.ArgumentParser( + prog="ci.py", description="Generate or run CI workflows" + ) + subparsers = parser.add_subparsers( + help="Command to execute", dest="command", required=True + ) + subparsers.add_parser( + "calculate-job-matrix", + help="Generate a matrix of jobs that should be executed in CI", + ) + run_parser = subparsers.add_parser( + "run-local", help="Run a CI jobs locally (on Linux)" + ) + run_parser.add_argument( + "job_name", + help="CI job that should be executed. By default, a merge (auto) " + "job with the given name will be executed", + ) + run_parser.add_argument( + "--pr", action="store_true", help="Run a PR job instead of an auto job" + ) + return parser + + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) + + with open(JOBS_YAML_PATH) as f: + data = yaml.safe_load(f) + + parser = create_cli_parser() + args = parser.parse_args() + + if args.command == "calculate-job-matrix": + calculate_job_matrix(data) + elif args.command == "run-local": + run_workflow_locally(data, args.job_name, args.pr) + else: + raise Exception(f"Unknown command {args.command}") diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 876a779359215..cae05e1f8ae1b 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -5,22 +5,22 @@ runners: env: { } - &job-linux-4c - os: ubuntu-22.04 + os: ubuntu-24.04 # Free some disk space to avoid running out of space during the build. free_disk: true <<: *base-job # Large runner used mainly for its bigger disk capacity - &job-linux-4c-largedisk - os: ubuntu-22.04-4core-16gb + os: ubuntu-24.04-4core-16gb <<: *base-job - &job-linux-8c - os: ubuntu-22.04-8core-32gb + os: ubuntu-24.04-8core-32gb <<: *base-job - &job-linux-16c - os: ubuntu-22.04-16core-64gb + os: ubuntu-24.04-16core-64gb <<: *base-job - &job-macos-xl @@ -35,22 +35,31 @@ runners: os: windows-2022 <<: *base-job + - &job-windows-25 + os: windows-2025 + <<: *base-job + - &job-windows-8c os: windows-2022-8core-32gb <<: *base-job - - &job-windows-16c - os: windows-2022-16core-64gb + - &job-windows-25-8c + os: windows-2025-8core-32gb <<: *base-job - &job-aarch64-linux - os: ubuntu-22.04-arm64-8core-32gb + # Free some disk space to avoid running out of space during the build. + free_disk: true + os: ubuntu-22.04-arm + - &job-aarch64-linux-8c + os: ubuntu-22.04-arm64-8core-32gb envs: env-x86_64-apple-tests: &env-x86_64-apple-tests SCRIPT: ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc -- --exact RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 + # Ensure that host tooling is tested on our minimum supported macOS version. MACOSX_DEPLOYMENT_TARGET: 10.12 MACOSX_STD_DEPLOYMENT_TARGET: 10.12 SELECT_XCODE: /Applications/Xcode_15.2.app @@ -78,7 +87,7 @@ envs: # builds) # - not running `opt-dist`'s post-optimization smoke tests on the resulting toolchain # - # If you *want* these to happen however, temporarily uncomment it before triggering a try build. + # If you *want* these to happen however, temporarily comment it before triggering a try build. DIST_TRY_BUILD: 1 auto: @@ -91,26 +100,26 @@ envs: # These jobs automatically inherit envs.pr, to avoid repeating # it in each job definition. pr: - - image: mingw-check + - name: mingw-check <<: *job-linux-4c - - image: mingw-check-tidy + - name: mingw-check-tidy continue_on_error: true <<: *job-linux-4c - - image: x86_64-gnu-llvm-18 + - name: x86_64-gnu-llvm-18 env: ENABLE_GCC_CODEGEN: "1" # We are adding (temporarily) a dummy commit on the compiler READ_ONLY_SRC: "0" DOCKER_SCRIPT: x86_64-gnu-llvm.sh <<: *job-linux-16c - - image: x86_64-gnu-tools + - name: x86_64-gnu-tools <<: *job-linux-16c # Jobs that run when you perform a try build (@bors try) # These jobs automatically inherit envs.try, to avoid repeating # it in each job definition. try: - - image: dist-x86_64-linux + - name: dist-x86_64-linux env: CODEGEN_BACKENDS: llvm,cranelift <<: *job-linux-16c @@ -123,106 +132,106 @@ auto: # Linux/Docker builders # ############################# - - image: aarch64-gnu + - name: aarch64-gnu <<: *job-aarch64-linux - - image: aarch64-gnu-debug + - name: aarch64-gnu-debug <<: *job-aarch64-linux - - image: arm-android + - name: arm-android <<: *job-linux-4c - - image: armhf-gnu + - name: armhf-gnu <<: *job-linux-4c - - image: dist-aarch64-linux + - name: dist-aarch64-linux env: CODEGEN_BACKENDS: llvm,cranelift - <<: *job-linux-4c + <<: *job-aarch64-linux-8c - - image: dist-android + - name: dist-android <<: *job-linux-4c - - image: dist-arm-linux + - name: dist-arm-linux <<: *job-linux-8c - - image: dist-armhf-linux + - name: dist-armhf-linux <<: *job-linux-4c - - image: dist-armv7-linux + - name: dist-armv7-linux <<: *job-linux-4c - - image: dist-i586-gnu-i586-i686-musl + - name: dist-i586-gnu-i586-i686-musl <<: *job-linux-4c - - image: dist-i686-linux + - name: dist-i686-linux <<: *job-linux-4c - - image: dist-loongarch64-linux + - name: dist-loongarch64-linux <<: *job-linux-4c - - image: dist-loongarch64-musl + - name: dist-loongarch64-musl <<: *job-linux-4c - - image: dist-ohos + - name: dist-ohos <<: *job-linux-4c - - image: dist-powerpc-linux + - name: dist-powerpc-linux <<: *job-linux-4c - - image: dist-powerpc64-linux + - name: dist-powerpc64-linux <<: *job-linux-4c - - image: dist-powerpc64le-linux + - name: dist-powerpc64le-linux <<: *job-linux-4c-largedisk - - image: dist-riscv64-linux + - name: dist-riscv64-linux <<: *job-linux-4c - - image: dist-s390x-linux + - name: dist-s390x-linux <<: *job-linux-4c - - image: dist-various-1 + - name: dist-various-1 <<: *job-linux-4c - - image: dist-various-2 + - name: dist-various-2 <<: *job-linux-4c - - image: dist-x86_64-freebsd + - name: dist-x86_64-freebsd <<: *job-linux-4c - - image: dist-x86_64-illumos + - name: dist-x86_64-illumos <<: *job-linux-4c - - image: dist-x86_64-linux + - name: dist-x86_64-linux env: CODEGEN_BACKENDS: llvm,cranelift <<: *job-linux-16c - - image: dist-x86_64-linux-alt + - name: dist-x86_64-linux-alt env: IMAGE: dist-x86_64-linux CODEGEN_BACKENDS: llvm,cranelift <<: *job-linux-16c - - image: dist-x86_64-musl + - name: dist-x86_64-musl env: CODEGEN_BACKENDS: llvm,cranelift <<: *job-linux-4c - - image: dist-x86_64-netbsd + - name: dist-x86_64-netbsd <<: *job-linux-4c # The i686-gnu job is split into multiple jobs to run tests in parallel. # i686-gnu-1 skips tests that run in i686-gnu-2. - - image: i686-gnu-1 + - name: i686-gnu-1 env: IMAGE: i686-gnu DOCKER_SCRIPT: stage_2_test_set1.sh <<: *job-linux-4c # Skip tests that run in i686-gnu-1 - - image: i686-gnu-2 + - name: i686-gnu-2 env: IMAGE: i686-gnu DOCKER_SCRIPT: stage_2_test_set2.sh @@ -230,14 +239,14 @@ auto: # The i686-gnu-nopt job is split into multiple jobs to run tests in parallel. # i686-gnu-nopt-1 skips tests that run in i686-gnu-nopt-2 - - image: i686-gnu-nopt-1 + - name: i686-gnu-nopt-1 env: IMAGE: i686-gnu-nopt DOCKER_SCRIPT: /scripts/stage_2_test_set1.sh <<: *job-linux-4c # Skip tests that run in i686-gnu-nopt-1 - - image: i686-gnu-nopt-2 + - name: i686-gnu-nopt-2 env: IMAGE: i686-gnu-nopt DOCKER_SCRIPT: >- @@ -245,13 +254,13 @@ auto: /scripts/stage_2_test_set2.sh <<: *job-linux-4c - - image: mingw-check + - name: mingw-check <<: *job-linux-4c - - image: test-various + - name: test-various <<: *job-linux-4c - - image: x86_64-fuchsia + - name: x86_64-fuchsia # Only run this job on the nightly channel. Fuchsia requires # nightly features to compile, and this job would fail if # executed on beta and stable. @@ -260,10 +269,10 @@ auto: # Tests integration with Rust for Linux. # Builds stage 1 compiler and tries to compile a few RfL examples with it. - - image: x86_64-rust-for-linux + - name: x86_64-rust-for-linux <<: *job-linux-4c - - image: x86_64-gnu + - name: x86_64-gnu <<: *job-linux-4c # This job ensures commits landing on nightly still pass the full @@ -271,7 +280,7 @@ auto: # depend on the channel being built (for example if they include the # channel name on the output), and this builder prevents landing # changes that would result in broken builds after a promotion. - - image: x86_64-gnu-stable + - name: x86_64-gnu-stable # Only run this job on the nightly channel. Running this on beta # could cause failures when `dev: 1` in `stage0.txt`, and running # this on stable is useless. @@ -281,20 +290,18 @@ auto: RUST_CI_OVERRIDE_RELEASE_CHANNEL: stable <<: *job-linux-4c - - image: x86_64-gnu-aux + - name: x86_64-gnu-aux <<: *job-linux-4c - - image: x86_64-gnu-debug - # This seems to be needed because a full stage 2 build + run-make tests - # overwhelms the storage capacity of the standard 4c runner. - <<: *job-linux-4c-largedisk + - name: x86_64-gnu-debug + <<: *job-linux-4c - - image: x86_64-gnu-distcheck + - name: x86_64-gnu-distcheck <<: *job-linux-8c # The x86_64-gnu-llvm-19 job is split into multiple jobs to run tests in parallel. # x86_64-gnu-llvm-19-1 skips tests that run in x86_64-gnu-llvm-19-{2,3}. - - image: x86_64-gnu-llvm-19-1 + - name: x86_64-gnu-llvm-19-1 env: RUST_BACKTRACE: 1 IMAGE: x86_64-gnu-llvm-19 @@ -302,7 +309,7 @@ auto: <<: *job-linux-4c # Skip tests that run in x86_64-gnu-llvm-19-{1,3} - - image: x86_64-gnu-llvm-19-2 + - name: x86_64-gnu-llvm-19-2 env: RUST_BACKTRACE: 1 IMAGE: x86_64-gnu-llvm-19 @@ -310,7 +317,7 @@ auto: <<: *job-linux-4c # Skip tests that run in x86_64-gnu-llvm-19-{1,2} - - image: x86_64-gnu-llvm-19-3 + - name: x86_64-gnu-llvm-19-3 env: RUST_BACKTRACE: 1 IMAGE: x86_64-gnu-llvm-19 @@ -319,7 +326,7 @@ auto: # The x86_64-gnu-llvm-18 job is split into multiple jobs to run tests in parallel. # x86_64-gnu-llvm-18-1 skips tests that run in x86_64-gnu-llvm-18-{2,3}. - - image: x86_64-gnu-llvm-18-1 + - name: x86_64-gnu-llvm-18-1 env: RUST_BACKTRACE: 1 READ_ONLY_SRC: "0" @@ -328,7 +335,7 @@ auto: <<: *job-linux-4c # Skip tests that run in x86_64-gnu-llvm-18-{1,3} - - image: x86_64-gnu-llvm-18-2 + - name: x86_64-gnu-llvm-18-2 env: RUST_BACKTRACE: 1 READ_ONLY_SRC: "0" @@ -337,7 +344,7 @@ auto: <<: *job-linux-4c # Skip tests that run in x86_64-gnu-llvm-18-{1,2} - - image: x86_64-gnu-llvm-18-3 + - name: x86_64-gnu-llvm-18-3 env: RUST_BACKTRACE: 1 READ_ONLY_SRC: "0" @@ -345,10 +352,10 @@ auto: DOCKER_SCRIPT: x86_64-gnu-llvm3.sh <<: *job-linux-4c - - image: x86_64-gnu-nopt + - name: x86_64-gnu-nopt <<: *job-linux-4c - - image: x86_64-gnu-tools + - name: x86_64-gnu-tools env: DEPLOY_TOOLSTATES_JSON: toolstates-linux.json <<: *job-linux-4c @@ -357,12 +364,14 @@ auto: # macOS Builders # #################### - - image: dist-x86_64-apple + - name: dist-x86_64-apple env: SCRIPT: ./x.py dist bootstrap --include-default-paths --host=x86_64-apple-darwin --target=x86_64-apple-darwin RUST_CONFIGURE_ARGS: --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set rust.lto=thin --set rust.codegen-units=1 RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 + # Ensure that host tooling is built to support our minimum support macOS version. MACOSX_DEPLOYMENT_TARGET: 10.12 + MACOSX_STD_DEPLOYMENT_TARGET: 10.12 SELECT_XCODE: /Applications/Xcode_15.2.app NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 @@ -371,33 +380,35 @@ auto: CODEGEN_BACKENDS: llvm,cranelift <<: *job-macos-xl - - image: dist-apple-various + - name: dist-apple-various env: SCRIPT: ./x.py dist bootstrap --include-default-paths --host='' --target=aarch64-apple-ios,x86_64-apple-ios,aarch64-apple-ios-sim,aarch64-apple-ios-macabi,x86_64-apple-ios-macabi # Mac Catalyst cannot currently compile the sanitizer: # https://github.com/rust-lang/rust/issues/129069 RUST_CONFIGURE_ARGS: --enable-sanitizers --enable-profiler --set rust.jemalloc --set target.aarch64-apple-ios-macabi.sanitizers=false --set target.x86_64-apple-ios-macabi.sanitizers=false RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 + # Ensure that host tooling is built to support our minimum support macOS version. + # FIXME(madsmtm): This might be redundant, as we're not building host tooling here (?) MACOSX_DEPLOYMENT_TARGET: 10.12 + MACOSX_STD_DEPLOYMENT_TARGET: 10.12 SELECT_XCODE: /Applications/Xcode_15.2.app NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 <<: *job-macos-xl - - image: x86_64-apple-1 + - name: x86_64-apple-1 env: <<: *env-x86_64-apple-tests <<: *job-macos-xl - - image: x86_64-apple-2 + - name: x86_64-apple-2 env: SCRIPT: ./x.py --stage 2 test tests/ui tests/rustdoc <<: *env-x86_64-apple-tests <<: *job-macos-xl - # This target only needs to support 11.0 and up as nothing else supports the hardware - - image: dist-aarch64-apple + - name: dist-aarch64-apple env: SCRIPT: ./x.py dist bootstrap --include-default-paths --host=aarch64-apple-darwin --target=aarch64-apple-darwin RUST_CONFIGURE_ARGS: >- @@ -411,6 +422,8 @@ auto: RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 SELECT_XCODE: /Applications/Xcode_15.4.app USE_XCODE_CLANG: 1 + # Aarch64 tooling only needs to support macOS 11.0 and up as nothing else + # supports the hardware. MACOSX_DEPLOYMENT_TARGET: 11.0 MACOSX_STD_DEPLOYMENT_TARGET: 11.0 NO_LLVM_ASSERTIONS: 1 @@ -420,8 +433,7 @@ auto: CODEGEN_BACKENDS: llvm,cranelift <<: *job-macos-m1 - # This target only needs to support 11.0 and up as nothing else supports the hardware - - image: aarch64-apple + - name: aarch64-apple env: SCRIPT: ./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin RUST_CONFIGURE_ARGS: >- @@ -431,6 +443,8 @@ auto: RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 SELECT_XCODE: /Applications/Xcode_15.4.app USE_XCODE_CLANG: 1 + # Aarch64 tooling only needs to support macOS 11.0 and up as nothing else + # supports the hardware, so only need to test it there. MACOSX_DEPLOYMENT_TARGET: 11.0 MACOSX_STD_DEPLOYMENT_TARGET: 11.0 NO_LLVM_ASSERTIONS: 1 @@ -442,20 +456,34 @@ auto: # Windows Builders # ###################### - - image: x86_64-msvc + # x86_64-msvc is split into two jobs to run tests in parallel. + - name: x86_64-msvc-1 + env: + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-sanitizers --enable-profiler + SCRIPT: make ci-msvc-py + <<: *job-windows-25 + + - name: x86_64-msvc-2 + env: + RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-sanitizers --enable-profiler + SCRIPT: make ci-msvc-ps1 + <<: *job-windows-25 + + # i686-msvc is split into two jobs to run tests in parallel. + - name: i686-msvc-1 env: - RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-profiler - SCRIPT: make ci-msvc - <<: *job-windows-8c + RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc --enable-sanitizers + SCRIPT: make ci-msvc-py + <<: *job-windows - - image: i686-msvc + - name: i686-msvc-2 env: - RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc - SCRIPT: make ci-msvc - <<: *job-windows-8c + RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc --enable-sanitizers + SCRIPT: make ci-msvc-ps1 + <<: *job-windows # x86_64-msvc-ext is split into multiple jobs to run tests in parallel. - - image: x86_64-msvc-ext1 + - name: x86_64-msvc-ext1 env: SCRIPT: python x.py --stage 2 test src/tools/cargotest src/tools/cargo RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-lld @@ -464,7 +492,7 @@ auto: # Temporary builder to workaround CI issues # See #FIXME: Remove this, and re-enable the same tests in `checktools.sh`, once CI issues are fixed. - - image: x86_64-msvc-ext2 + - name: x86_64-msvc-ext2 env: SCRIPT: > python x.py test --stage 2 src/tools/miri --target aarch64-apple-darwin --test-args pass && @@ -476,7 +504,7 @@ auto: <<: *job-windows # Run `checktools.sh` and upload the toolstate file. - - image: x86_64-msvc-ext3 + - name: x86_64-msvc-ext3 env: SCRIPT: src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh x.py /tmp/toolstate/toolstates.json windows HOST_TARGET: x86_64-pc-windows-msvc @@ -500,35 +528,49 @@ auto: # came from the mingw-w64 SourceForge download site. Unfortunately # SourceForge is notoriously flaky, so we mirror it on our own infrastructure. - - image: i686-mingw + # i686-mingw is split into three jobs to run tests in parallel. + - name: i686-mingw-1 env: RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu - SCRIPT: make ci-mingw - # We are intentionally allowing an old toolchain on this builder (and that's - # incompatible with LLVM downloads today). + SCRIPT: make ci-mingw-x-1 + # There is no dist-i686-mingw-alt, so there is no prebuilt LLVM with assertions NO_DOWNLOAD_CI_LLVM: 1 - <<: *job-windows-8c + <<: *job-windows-25 + + - name: i686-mingw-2 + env: + RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu + SCRIPT: make ci-mingw-x-2 + # There is no dist-i686-mingw-alt, so there is no prebuilt LLVM with assertions + NO_DOWNLOAD_CI_LLVM: 1 + <<: *job-windows-25 + + - name: i686-mingw-3 + env: + RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu + SCRIPT: make ci-mingw-bootstrap + # There is no dist-i686-mingw-alt, so there is no prebuilt LLVM with assertions + NO_DOWNLOAD_CI_LLVM: 1 + <<: *job-windows-25 # x86_64-mingw is split into two jobs to run tests in parallel. - - image: x86_64-mingw-1 + - name: x86_64-mingw-1 env: SCRIPT: make ci-mingw-x RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu - # We are intentionally allowing an old toolchain on this builder (and that's - # incompatible with LLVM downloads today). + # There is no dist-x86_64-mingw-alt, so there is no prebuilt LLVM with assertions NO_DOWNLOAD_CI_LLVM: 1 <<: *job-windows - - image: x86_64-mingw-2 + - name: x86_64-mingw-2 env: SCRIPT: make ci-mingw-bootstrap RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu - # We are intentionally allowing an old toolchain on this builder (and that's - # incompatible with LLVM downloads today). + # There is no dist-x86_64-mingw-alt, so there is no prebuilt LLVM with assertions NO_DOWNLOAD_CI_LLVM: 1 <<: *job-windows - - image: dist-x86_64-msvc + - name: dist-x86_64-msvc env: RUST_CONFIGURE_ARGS: >- --build=x86_64-pc-windows-msvc @@ -540,9 +582,9 @@ auto: SCRIPT: python x.py build --set rust.debug=true opt-dist && PGO_HOST=x86_64-pc-windows-msvc ./build/x86_64-pc-windows-msvc/stage0-tools-bin/opt-dist windows-ci -- python x.py dist bootstrap --include-default-paths DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift - <<: *job-windows-8c + <<: *job-windows-25-8c - - image: dist-i686-msvc + - name: dist-i686-msvc env: RUST_CONFIGURE_ARGS: >- --build=i686-pc-windows-msvc @@ -555,7 +597,7 @@ auto: CODEGEN_BACKENDS: llvm,cranelift <<: *job-windows - - image: dist-aarch64-msvc + - name: dist-aarch64-msvc env: RUST_CONFIGURE_ARGS: >- --build=x86_64-pc-windows-msvc @@ -567,33 +609,27 @@ auto: DIST_REQUIRE_ALL_TOOLS: 1 <<: *job-windows - - image: dist-i686-mingw + - name: dist-i686-mingw env: RUST_CONFIGURE_ARGS: >- --build=i686-pc-windows-gnu --enable-full-tools - # We are intentionally allowing an old toolchain on this builder (and that's - # incompatible with LLVM downloads today). - NO_DOWNLOAD_CI_LLVM: 1 SCRIPT: python x.py dist bootstrap --include-default-paths DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift <<: *job-windows - - image: dist-x86_64-mingw + - name: dist-x86_64-mingw env: SCRIPT: python x.py dist bootstrap --include-default-paths RUST_CONFIGURE_ARGS: >- --build=x86_64-pc-windows-gnu --enable-full-tools - # We are intentionally allowing an old toolchain on this builder (and that's - # incompatible with LLVM downloads today). - NO_DOWNLOAD_CI_LLVM: 1 DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift <<: *job-windows - - image: dist-x86_64-msvc-alt + - name: dist-x86_64-msvc-alt env: RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-extended --enable-profiler SCRIPT: python x.py dist bootstrap --include-default-paths diff --git a/src/ci/scripts/free-disk-space.sh b/src/ci/scripts/free-disk-space.sh new file mode 100755 index 0000000000000..055a6ac2211e3 --- /dev/null +++ b/src/ci/scripts/free-disk-space.sh @@ -0,0 +1,216 @@ +#!/bin/bash +set -euo pipefail + +# Free disk space on Linux GitHub action runners +# Script inspired by https://github.com/jlumbroso/free-disk-space + +isX86() { + local arch + arch=$(uname -m) + if [ "$arch" = "x86_64" ]; then + return 0 + else + return 1 + fi +} + +# print a line of the specified character +printSeparationLine() { + for ((i = 0; i < 80; i++)); do + printf "%s" "$1" + done + printf "\n" +} + +# compute available space +# REF: https://unix.stackexchange.com/a/42049/60849 +# REF: https://stackoverflow.com/a/450821/408734 +getAvailableSpace() { + df -a | awk 'NR > 1 {avail+=$4} END {print avail}' +} + +# make Kb human readable (assume the input is Kb) +# REF: https://unix.stackexchange.com/a/44087/60849 +formatByteCount() { + numfmt --to=iec-i --suffix=B --padding=7 "$1"'000' +} + +# macro to output saved space +printSavedSpace() { + # Disk space before the operation + local before=${1} + local title=${2:-} + + local after + after=$(getAvailableSpace) + local saved=$((after - before)) + + echo "" + printSeparationLine "*" + if [ -n "${title}" ]; then + echo "=> ${title}: Saved $(formatByteCount "$saved")" + else + echo "=> Saved $(formatByteCount "$saved")" + fi + printSeparationLine "*" + echo "" +} + +# macro to print output of df with caption +printDF() { + local caption=${1} + + printSeparationLine "=" + echo "${caption}" + echo "" + echo "$ df -h" + echo "" + df -h + printSeparationLine "=" +} + +removeUnusedFilesAndDirs() { + local to_remove=( + "/usr/local/aws-sam-cli" + "/usr/local/doc/cmake" + "/usr/local/julia"* + "/usr/local/lib/android" + "/usr/local/share/chromedriver-"* + "/usr/local/share/chromium" + "/usr/local/share/cmake-"* + "/usr/local/share/edge_driver" + "/usr/local/share/gecko_driver" + "/usr/local/share/icons" + "/usr/local/share/vim" + "/usr/local/share/emacs" + "/usr/local/share/powershell" + "/usr/local/share/vcpkg" + "/usr/share/apache-maven-"* + "/usr/share/gradle-"* + "/usr/share/java" + "/usr/share/kotlinc" + "/usr/share/miniconda" + "/usr/share/php" + "/usr/share/ri" + "/usr/share/swift" + + # binaries + "/usr/local/bin/azcopy" + "/usr/local/bin/bicep" + "/usr/local/bin/ccmake" + "/usr/local/bin/cmake-"* + "/usr/local/bin/cmake" + "/usr/local/bin/cpack" + "/usr/local/bin/ctest" + "/usr/local/bin/helm" + "/usr/local/bin/kind" + "/usr/local/bin/kustomize" + "/usr/local/bin/minikube" + "/usr/local/bin/packer" + "/usr/local/bin/phpunit" + "/usr/local/bin/pulumi-"* + "/usr/local/bin/pulumi" + "/usr/local/bin/stack" + + # Haskell runtime + "/usr/local/.ghcup" + + # Azure + "/opt/az" + "/usr/share/az_"* + + # Environment variable set by GitHub Actions + "$AGENT_TOOLSDIRECTORY" + ) + + for element in "${to_remove[@]}"; do + if [ ! -e "$element" ]; then + # The file or directory doesn't exist. + # Maybe it was removed in a newer version of the runner or it's not present in a + # specific architecture (e.g. ARM). + echo "::warning::Directory or file $element does not exist, skipping." + fi + done + + # Remove all files and directories at once to save time. + sudo rm -rf "${to_remove[@]}" +} + +execAndMeasureSpaceChange() { + local operation=${1} # Function to execute + local title=${2} + + local before + before=$(getAvailableSpace) + $operation + + printSavedSpace "$before" "$title" +} + +# Remove large packages +# REF: https://github.com/apache/flink/blob/master/tools/azure-pipelines/free_disk_space.sh +cleanPackages() { + local packages=( + '^aspnetcore-.*' + '^dotnet-.*' + '^llvm-.*' + '^mongodb-.*' + 'azure-cli' + 'firefox' + 'libgl1-mesa-dri' + 'mono-devel' + 'php.*' + ) + + if isX86; then + packages+=( + 'google-chrome-stable' + 'google-cloud-cli' + 'google-cloud-sdk' + 'powershell' + ) + fi + + sudo apt-get -qq remove -y --fix-missing "${packages[@]}" + + sudo apt-get autoremove -y || echo "::warning::The command [sudo apt-get autoremove -y] failed" + sudo apt-get clean || echo "::warning::The command [sudo apt-get clean] failed failed" +} + +# Remove Docker images. +# Ubuntu 22 runners have docker images already installed. +# They aren't present in ubuntu 24 runners. +cleanDocker() { + echo "=> Removing the following docker images:" + sudo docker image ls + echo "=> Removing docker images..." + sudo docker image prune --all --force || true +} + +# Remove Swap storage +cleanSwap() { + sudo swapoff -a || true + sudo rm -rf /mnt/swapfile || true + free -h +} + +# Display initial disk space stats + +AVAILABLE_INITIAL=$(getAvailableSpace) + +printDF "BEFORE CLEAN-UP:" +echo "" + +execAndMeasureSpaceChange cleanPackages "Unused packages" +execAndMeasureSpaceChange cleanDocker "Docker images" +execAndMeasureSpaceChange cleanSwap "Swap storage" +execAndMeasureSpaceChange removeUnusedFilesAndDirs "Unused files and directories" + +# Output saved space statistic +echo "" +printDF "AFTER CLEAN-UP:" + +echo "" +echo "" + +printSavedSpace "$AVAILABLE_INITIAL" "Total saved" diff --git a/src/ci/scripts/install-mingw.sh b/src/ci/scripts/install-mingw.sh index 91eab2e7a0816..c8c501e646a9d 100755 --- a/src/ci/scripts/install-mingw.sh +++ b/src/ci/scripts/install-mingw.sh @@ -32,6 +32,12 @@ if isWindows && isKnownToBeMingwBuild; then ;; esac + # Stop /msys64/bin from being prepended to PATH by adding the bin directory manually. + # Note that this intentionally uses a Windows style path instead of the msys2 path to + # avoid being auto-translated into `/usr/bin`, which will not have the desired effect. + msys2Path="c:/msys64" + ciCommandAddPath "${msys2Path}/usr/bin" + mingw_dir="mingw${bits}" curl -o mingw.7z "${MIRRORS_BASE}/${mingw_archive}" diff --git a/src/doc/book b/src/doc/book index 04d06dfe54160..d4d2c18cbd208 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 04d06dfe541607e6419f3d028c3f9b245f3be4d9 +Subproject commit d4d2c18cbd20876b2130a546e790446a8444cb32 diff --git a/src/doc/edition-guide b/src/doc/edition-guide index d56e0f3a0656b..8dbdda7cae4fa 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit d56e0f3a0656b7702ca466d4b191e16c28262b82 +Subproject commit 8dbdda7cae4fa030f09f8f5b63994d4d1dde74b9 diff --git a/src/doc/embedded-book b/src/doc/embedded-book index ddbf1b4e2858f..0b8219ac23a3e 160000 --- a/src/doc/embedded-book +++ b/src/doc/embedded-book @@ -1 +1 @@ -Subproject commit ddbf1b4e2858fedb71b7c42eb15c4576517dc125 +Subproject commit 0b8219ac23a3e09464e4e0166c768cf1c4bba0d5 diff --git a/src/doc/nomicon b/src/doc/nomicon index 7ef05b9777c94..336f75835a6c0 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 7ef05b9777c94836bc92f50f23e6e00981521a89 +Subproject commit 336f75835a6c0514852cc65aba9a698b699b13c8 diff --git a/src/doc/reference b/src/doc/reference index acd6794e712d5..6195dbd70fc6f 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit acd6794e712d5e2ef6f5c84fb95688d32a69b816 +Subproject commit 6195dbd70fc6f0980c314b4d23875ac570d8253a diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index 093397535b48a..66543bbc5b7db 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit 093397535b48ae13ec76bc526b7e6eb8c096a85c +Subproject commit 66543bbc5b7dbd4e679092c07ae06ba6c73fd912 diff --git a/src/doc/rustc-dev-guide/.github/workflows/ci.yml b/src/doc/rustc-dev-guide/.github/workflows/ci.yml index 07c20d8d88bfe..3f810e2fbcc99 100644 --- a/src/doc/rustc-dev-guide/.github/workflows/ci.yml +++ b/src/doc/rustc-dev-guide/.github/workflows/ci.yml @@ -35,12 +35,15 @@ jobs: ~/.cargo/bin key: ${{ runner.os }}-${{ env.MDBOOK_VERSION }}--${{ env.MDBOOK_LINKCHECK2_VERSION }}--${{ env.MDBOOK_TOC_VERSION }}--${{ env.MDBOOK_MERMAID_VERSION }} - - name: Cache linkcheck - uses: actions/cache@v4 + - name: Restore cached Linkcheck + if: github.event_name == 'schedule' + id: cache-linkcheck-restore + uses: actions/cache/restore@v4 with: - path: | - ~/book/linkcheck - key: ${{ runner.os }}-${{ hashFiles('./book/linkcheck') }} + path: book/linkcheck/cache.json + key: linkcheck--${{ env.MDBOOK_LINKCHECK2_VERSION }}--${{ github.run_id }} + restore-keys: | + linkcheck--${{ env.MDBOOK_LINKCHECK2_VERSION }}-- - name: Install latest nightly Rust toolchain if: steps.mdbook-cache.outputs.cache-hit != 'true' @@ -59,6 +62,14 @@ jobs: - name: Check build run: ENABLE_LINKCHECK=1 mdbook build + - name: Save cached Linkcheck + id: cache-linkcheck-save + if: ${{ !cancelled() && github.event_name == 'schedule' }} + uses: actions/cache/save@v4 + with: + path: book/linkcheck/cache.json + key: linkcheck--${{ env.MDBOOK_LINKCHECK2_VERSION }}--${{ github.run_id }} + - name: Deploy to gh-pages if: github.event_name == 'push' run: | diff --git a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml new file mode 100644 index 0000000000000..dc5395a19dd03 --- /dev/null +++ b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml @@ -0,0 +1,114 @@ +name: rustc-pull + +on: + workflow_dispatch: + schedule: + # Run at 04:00 UTC every Monday and Thursday + - cron: '0 4 * * 1,4' + +jobs: + pull: + if: github.repository == 'rust-lang/rustc-dev-guide' + runs-on: ubuntu-latest + outputs: + pr_url: ${{ steps.update-pr.outputs.pr_url }} + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@v4 + with: + # We need the full history for josh to work + fetch-depth: '0' + - name: Install stable Rust toolchain + run: rustup update stable + - uses: Swatinem/rust-cache@v2 + with: + workspaces: "josh-sync" + # Cache the josh directory with checked out rustc + cache-directories: "/home/runner/.cache/rustc-dev-guide-josh" + - name: Install josh + run: RUSTFLAGS="--cap-lints warn" cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04 + - name: Setup bot git name and email + run: | + git config --global user.name 'The rustc-dev-guide Cronjob Bot' + git config --global user.email 'github-actions@github.com' + - name: Perform rustc-pull + id: rustc-pull + # Turn off -e to disable early exit + shell: bash {0} + run: | + cargo run --manifest-path josh-sync/Cargo.toml -- rustc-pull + exitcode=$? + + # If no pull was performed, we want to mark this job as successful, + # but we do not want to perform the follow-up steps. + if [ $exitcode -eq 0 ]; then + echo "pull_result=pull-finished" >> $GITHUB_OUTPUT + elif [ $exitcode -eq 2 ]; then + echo "pull_result=skipped" >> $GITHUB_OUTPUT + exitcode=0 + fi + + exit ${exitcode} + - name: Push changes to a branch + if: ${{ steps.rustc-pull.outputs.pull_result == 'pull-finished' }} + run: | + # Update a sticky branch that is used only for rustc pulls + BRANCH="rustc-pull" + git switch -c $BRANCH + git push -u origin $BRANCH --force + - name: Create pull request + id: update-pr + if: ${{ steps.rustc-pull.outputs.pull_result == 'pull-finished' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Check if an open pull request for an rustc pull update already exists + # If it does, the previous push has just updated it + # If not, we create it now + RESULT=`gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | length' --json title` + if [[ "$RESULT" -eq 0 ]]; then + echo "Creating new pull request" + PR_URL=`gh pr create -B master --title 'Rustc pull update' --body 'Latest update from rustc.'` + echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT + else + PR_URL=`gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | .[0].url' --json url,title` + echo "Updating pull request ${PR_URL}" + echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT + fi + send-zulip-message: + needs: [pull] + if: ${{ !cancelled() }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Compute message + id: create-message + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + if [ "${{ needs.pull.result }}" == "failure" ]; then + WORKFLOW_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + echo "message=Rustc pull sync failed. Check out the [workflow URL]($WORKFLOW_URL)." >> $GITHUB_OUTPUT + else + CREATED_AT=`gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | .[0].createdAt' --json createdAt,title` + PR_URL=`gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | .[0].url' --json url,title` + week_ago=$(date +%F -d '7 days ago') + + # If there is an open PR that is at least a week old, post a message about it + if [[ -n $DATE_GH && $DATE_GH < $week_ago ]]; then + echo "message=A PR with a Rustc pull has been opened for more a week. Check out the [PR](${PR_URL})." >> $GITHUB_OUTPUT + fi + fi + - name: Send a Zulip message about updated PR + if: ${{ steps.create-message.outputs.message != '' }} + uses: zulip/github-actions-zulip/send-message@e4c8f27c732ba9bd98ac6be0583096dea82feea5 + with: + api-key: ${{ secrets.ZULIP_API_TOKEN }} + email: "rustc-dev-guide-gha-notif-bot@rust-lang.zulipchat.com" + organization-url: "https://rust-lang.zulipchat.com" + to: 196385 + type: "stream" + topic: "Subtree sync automation" + content: ${{ steps.message.outputs.message }} diff --git a/src/doc/rustc-dev-guide/.gitignore b/src/doc/rustc-dev-guide/.gitignore index 160c5f0fe5c7a..f03fcae753f41 100644 --- a/src/doc/rustc-dev-guide/.gitignore +++ b/src/doc/rustc-dev-guide/.gitignore @@ -4,3 +4,5 @@ ci/date-check/target/ # Generated by check-in.sh pulls.json + +josh-sync/target diff --git a/src/doc/rustc-dev-guide/README.md b/src/doc/rustc-dev-guide/README.md index 2a686b5471c68..2464ffbbc5032 100644 --- a/src/doc/rustc-dev-guide/README.md +++ b/src/doc/rustc-dev-guide/README.md @@ -70,46 +70,28 @@ $ ENABLE_LINKCHECK=1 mdbook serve We use `mdbook-toc` to auto-generate TOCs for long sections. You can invoke the preprocessor by including the `` marker at the place where you want the TOC. -## How to fix toolstate failures +## Synchronizing josh subtree with rustc -> [!NOTE] -> Currently, we do not track the rustc-dev-guide toolstate due to -> [spurious failures](https://github.com/rust-lang/rust/pull/71731), -> but we leave these instructions for when we do it again in the future. +This repository is linked to `rust-lang/rust` as a [josh](https://josh-project.github.io/josh/intro.html) subtree. You can use the following commands to synchronize the subtree in both directions. -1. You will get a ping from the toolstate commit. e.g. https://github.com/rust-lang-nursery/rust-toolstate/commit/8ffa0e4c30ac9ba8546b7046e5c4ccc2b96ebdd4 +You'll need to install `josh-proxy` locally via -2. The commit contains a link to the PR that caused the breakage. e.g. https://github.com/rust-lang/rust/pull/64321 - -3. If you go to that PR's thread, there is a post from bors with a link to the CI status: https://github.com/rust-lang/rust/pull/64321#issuecomment-529763807 - -4. Follow the check-actions link to get to the Actions page for that build - -5. There will be approximately 1 billion different jobs for the build. They are for different configurations and platforms. The rustc-dev-guide build only runs on the Linux x86_64-gnu-tools job. So click on that job in the list, which is about 60% down in the list. - -6. Click the Run build step in the job to get the console log for the step. - -7. Click on the log and Ctrl-f to get a search box in the log - -8. Search for rustc-dev-guide. This gets you to the place where the links are checked. It is usually ~11K lines into the log. - -9. Look at the links in the log near that point in the log - -10. Fix those links in the rustc-dev-guide (by making a PR in the rustc-dev-guide repo) - -11. Make a PR on the rust-lang/rust repo to update the rustc-dev-guide git submodule in src/docs/rustc-dev-guide. -To make a PR, the following steps are useful. - -```bash -# Assuming you already cloned the rust-lang/rust repo and you're in the correct directory -git submodule update --remote src/doc/rustc-dev-guide -git add -u -git commit -m "Update rustc-dev-guide" -# Note that you can use -i, which is short for --incremental, in the following command -./x test --incremental src/doc/rustc-dev-guide # This is optional and should succeed anyway -# Open a PR in rust-lang/rust ``` - -12. Wait for PR to merge - -Voilà! +cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04 +``` +Older versions of `josh-proxy` may not round trip commits losslessly so it is important to install this exact version. + +### Pull changes from `rust-lang/rust` into this repository +1) Checkout a new branch that will be used to create a PR into `rust-lang/rustc-dev-guide` +2) Run the pull command + ``` + $ cargo run --manifest-path josh-sync/Cargo.toml rustc-pull + ``` +3) Push the branch to your fork and create a PR into `rustc-dev-guide` + +### Push changes from this repository into `rust-lang/rust` +1) Run the push command to create a branch named `` in a `rustc` fork under the `` account + ``` + $ cargo run --manifest-path josh-sync/Cargo.toml rustc-push + ``` +2) Create a PR from `` into `rust-lang/rust` diff --git a/src/doc/rustc-dev-guide/book.toml b/src/doc/rustc-dev-guide/book.toml index 2f67aecf6f059..67069d9930f57 100644 --- a/src/doc/rustc-dev-guide/book.toml +++ b/src/doc/rustc-dev-guide/book.toml @@ -52,7 +52,9 @@ exclude = [ # 500 is returned for HEAD request "code\\.visualstudio\\.com/docs/editor/tasks", ] -cache-timeout = 86400 +# The scheduled CI runs every day and so we need to reuse a part of the cache +# in order to face "Server returned 429 Too Many Requests" errors for github.com. +cache-timeout = 90000 warning-policy = "error" [output.html.redirect] diff --git a/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs b/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs index 576bbcea965d9..14998965ac863 100644 --- a/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs +++ b/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs @@ -15,11 +15,11 @@ extern crate rustc_span; use std::io; use std::path::Path; +use std::sync::Arc; use rustc_ast_pretty::pprust::item_to_string; -use rustc_data_structures::sync::Lrc; -use rustc_driver::{Compilation, RunCompiler}; -use rustc_interface::interface::Compiler; +use rustc_driver::{Compilation, run_compiler}; +use rustc_interface::interface::{Compiler, Config}; use rustc_middle::ty::TyCtxt; struct MyFileLoader; @@ -43,7 +43,7 @@ fn main() { } } - fn read_binary_file(&self, _path: &Path) -> io::Result> { + fn read_binary_file(&self, _path: &Path) -> io::Result> { Err(io::Error::other("oops")) } } @@ -51,10 +51,14 @@ fn main() { struct MyCallbacks; impl rustc_driver::Callbacks for MyCallbacks { + fn config(&mut self, config: &mut Config) { + config.file_loader = Some(Box::new(MyFileLoader)); + } + fn after_crate_root_parsing( &mut self, _compiler: &Compiler, - krate: &rustc_ast::Crate, + krate: &mut rustc_ast::Crate, ) -> Compilation { for item in &krate.items { println!("{}", item_to_string(&item)); @@ -83,10 +87,5 @@ impl rustc_driver::Callbacks for MyCallbacks { } fn main() { - match RunCompiler::new(&["main.rs".to_string()], &mut MyCallbacks) { - mut compiler => { - compiler.set_file_loader(Some(Box::new(MyFileLoader))); - compiler.run(); - } - } + run_compiler(&["main.rs".to_string()], &mut MyCallbacks); } diff --git a/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs b/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs index 90a85d5db2108..9fcb16b0fca38 100644 --- a/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs +++ b/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs @@ -15,11 +15,11 @@ extern crate rustc_span; use std::io; use std::path::Path; +use std::sync::Arc; use rustc_ast_pretty::pprust::item_to_string; -use rustc_data_structures::sync::Lrc; -use rustc_driver::{Compilation, RunCompiler}; -use rustc_interface::interface::Compiler; +use rustc_driver::{Compilation, run_compiler}; +use rustc_interface::interface::{Compiler, Config}; use rustc_middle::ty::TyCtxt; struct MyFileLoader; @@ -43,7 +43,7 @@ fn main() { } } - fn read_binary_file(&self, _path: &Path) -> io::Result> { + fn read_binary_file(&self, _path: &Path) -> io::Result> { Err(io::Error::other("oops")) } } @@ -51,10 +51,14 @@ fn main() { struct MyCallbacks; impl rustc_driver::Callbacks for MyCallbacks { + fn config(&mut self, config: &mut Config) { + config.file_loader = Some(Box::new(MyFileLoader)); + } + fn after_crate_root_parsing( &mut self, _compiler: &Compiler, - krate: &rustc_ast::Crate, + krate: &mut rustc_ast::Crate, ) -> Compilation { for item in &krate.items { println!("{}", item_to_string(&item)); @@ -90,10 +94,5 @@ impl rustc_driver::Callbacks for MyCallbacks { } fn main() { - match RunCompiler::new(&["main.rs".to_string()], &mut MyCallbacks) { - mut compiler => { - compiler.set_file_loader(Some(Box::new(MyFileLoader))); - compiler.run(); - } - } + run_compiler(&["main.rs".to_string()], &mut MyCallbacks); } diff --git a/src/doc/rustc-dev-guide/examples/rustc-interface-getting-diagnostics.rs b/src/doc/rustc-dev-guide/examples/rustc-interface-getting-diagnostics.rs index be37dd867b256..2355cb85ab3dd 100644 --- a/src/doc/rustc-dev-guide/examples/rustc-interface-getting-diagnostics.rs +++ b/src/doc/rustc-dev-guide/examples/rustc-interface-getting-diagnostics.rs @@ -10,6 +10,8 @@ extern crate rustc_interface; extern crate rustc_session; extern crate rustc_span; +use std::sync::{Arc, Mutex}; + use rustc_errors::emitter::Emitter; use rustc_errors::registry::{self, Registry}; use rustc_errors::translation::Translate; @@ -17,8 +19,6 @@ use rustc_errors::{DiagCtxt, DiagInner, FluentBundle}; use rustc_session::config; use rustc_span::source_map::SourceMap; -use std::sync::{Arc, Mutex}; - struct DebugEmitter { source_map: Arc, diagnostics: Arc>>, @@ -67,10 +67,10 @@ fn main() { locale_resources: rustc_driver::DEFAULT_LOCALE_RESOURCES.to_owned(), lint_caps: rustc_hash::FxHashMap::default(), psess_created: Some(Box::new(|parse_sess| { - parse_sess.set_dcx(DiagCtxt::new(Box::new(DebugEmitter { + parse_sess.dcx().set_emitter(Box::new(DebugEmitter { source_map: parse_sess.clone_source_map(), diagnostics, - }))); + })); })), register_lints: None, override_queries: None, diff --git a/src/doc/rustc-dev-guide/josh-sync/Cargo.lock b/src/doc/rustc-dev-guide/josh-sync/Cargo.lock new file mode 100644 index 0000000000000..844518628c437 --- /dev/null +++ b/src/doc/rustc-dev-guide/josh-sync/Cargo.lock @@ -0,0 +1,430 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + +[[package]] +name = "anyhow" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "josh-sync" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "directories", + "xshell", +] + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "xshell" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e7290c623014758632efe00737145b6867b66292c42167f2ec381eb566a373d" +dependencies = [ + "xshell-macros", +] + +[[package]] +name = "xshell-macros" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32ac00cd3f8ec9c1d33fb3e7958a82df6989c42d747bd326c822b1d625283547" diff --git a/src/doc/rustc-dev-guide/josh-sync/Cargo.toml b/src/doc/rustc-dev-guide/josh-sync/Cargo.toml new file mode 100644 index 0000000000000..81d0d1ebd22d8 --- /dev/null +++ b/src/doc/rustc-dev-guide/josh-sync/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "josh-sync" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.95" +clap = { version = "4.5.21", features = ["derive"] } +directories = "5" +xshell = "0.2.6" diff --git a/src/doc/rustc-dev-guide/josh-sync/README.md b/src/doc/rustc-dev-guide/josh-sync/README.md new file mode 100644 index 0000000000000..a3dd876e8b87b --- /dev/null +++ b/src/doc/rustc-dev-guide/josh-sync/README.md @@ -0,0 +1,4 @@ +# Git josh sync +This utility serves for syncing the josh git subtree to and from the rust-lang/rust repository. + +See CLI help for usage. diff --git a/src/doc/rustc-dev-guide/josh-sync/src/main.rs b/src/doc/rustc-dev-guide/josh-sync/src/main.rs new file mode 100644 index 0000000000000..175f016f73907 --- /dev/null +++ b/src/doc/rustc-dev-guide/josh-sync/src/main.rs @@ -0,0 +1,43 @@ +use clap::Parser; +use crate::sync::{GitSync, RustcPullError}; + +mod sync; + +#[derive(clap::Parser)] +enum Args { + /// Pull changes from the main `rustc` repository. + /// This creates new commits that should be then merged into `rustc-dev-guide`. + RustcPull, + /// Push changes from `rustc-dev-guide` to the given `branch` of a `rustc` fork under the given + /// GitHub `username`. + /// The pushed branch should then be merged into the `rustc` repository. + RustcPush { + branch: String, + github_username: String + } +} + +fn main() -> anyhow::Result<()> { + let args = Args::parse(); + let sync = GitSync::from_current_dir()?; + match args { + Args::RustcPull => { + if let Err(error) = sync.rustc_pull(None) { + match error { + RustcPullError::NothingToPull => { + eprintln!("Nothing to pull"); + std::process::exit(2); + } + RustcPullError::PullFailed(error) => { + eprintln!("Pull failure: {error:?}"); + std::process::exit(1); + } + } + } + } + Args::RustcPush { github_username, branch } => { + sync.rustc_push(github_username, branch)?; + } + } + Ok(()) +} diff --git a/src/doc/rustc-dev-guide/josh-sync/src/sync.rs b/src/doc/rustc-dev-guide/josh-sync/src/sync.rs new file mode 100644 index 0000000000000..cd64be6367032 --- /dev/null +++ b/src/doc/rustc-dev-guide/josh-sync/src/sync.rs @@ -0,0 +1,263 @@ +use std::ops::Not; +use std::path::PathBuf; +use std::{env, net, process}; +use std::io::Write; +use std::time::Duration; +use anyhow::{anyhow, bail, Context}; +use xshell::{cmd, Shell}; + +/// Used for rustc syncs. +const JOSH_FILTER: &str = ":/src/doc/rustc-dev-guide"; +const JOSH_PORT: u16 = 42042; +const UPSTREAM_REPO: &str = "rust-lang/rust"; + +pub enum RustcPullError { + /// No changes are available to be pulled. + NothingToPull, + /// A rustc-pull has failed, probably a git operation error has occurred. + PullFailed(anyhow::Error) +} + +impl From for RustcPullError where E: Into { + fn from(error: E) -> Self { + Self::PullFailed(error.into()) + } +} + +pub struct GitSync { + dir: PathBuf, +} + +/// This code was adapted from the miri repository +/// (https://github.com/rust-lang/miri/blob/6a68a79f38064c3bc30617cca4bdbfb2c336b140/miri-script/src/commands.rs#L236). +impl GitSync { + pub fn from_current_dir() -> anyhow::Result { + Ok(Self { + dir: std::env::current_dir()? + }) + } + + pub fn rustc_pull(&self, commit: Option) -> Result<(), RustcPullError> { + let sh = Shell::new()?; + sh.change_dir(&self.dir); + let commit = commit.map(Ok).unwrap_or_else(|| { + let rust_repo_head = + cmd!(sh, "git ls-remote https://github.com/{UPSTREAM_REPO}/ HEAD").read()?; + rust_repo_head + .split_whitespace() + .next() + .map(|front| front.trim().to_owned()) + .ok_or_else(|| anyhow!("Could not obtain Rust repo HEAD from remote.")) + })?; + // Make sure the repo is clean. + if cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty().not() { + return Err(anyhow::anyhow!("working directory must be clean before performing rustc pull").into()); + } + // Make sure josh is running. + let josh = Self::start_josh()?; + let josh_url = + format!("http://localhost:{JOSH_PORT}/{UPSTREAM_REPO}.git@{commit}{JOSH_FILTER}.git"); + + let previous_base_commit = sh.read_file("rust-version")?.trim().to_string(); + if previous_base_commit == commit { + return Err(RustcPullError::NothingToPull); + } + + // Update rust-version file. As a separate commit, since making it part of + // the merge has confused the heck out of josh in the past. + // We pass `--no-verify` to avoid running git hooks. + // We do this before the merge so that if there are merge conflicts, we have + // the right rust-version file while resolving them. + sh.write_file("rust-version", format!("{commit}\n"))?; + const PREPARING_COMMIT_MESSAGE: &str = "Preparing for merge from rustc"; + cmd!(sh, "git commit rust-version --no-verify -m {PREPARING_COMMIT_MESSAGE}") + .run() + .context("FAILED to commit rust-version file, something went wrong")?; + + // Fetch given rustc commit. + cmd!(sh, "git fetch {josh_url}") + .run() + .inspect_err(|_| { + // Try to un-do the previous `git commit`, to leave the repo in the state we found it. + cmd!(sh, "git reset --hard HEAD^") + .run() + .expect("FAILED to clean up again after failed `git fetch`, sorry for that"); + }) + .context("FAILED to fetch new commits, something went wrong (committing the rust-version file has been undone)")?; + + // This should not add any new root commits. So count those before and after merging. + let num_roots = || -> anyhow::Result { + Ok(cmd!(sh, "git rev-list HEAD --max-parents=0 --count") + .read() + .context("failed to determine the number of root commits")? + .parse::()?) + }; + let num_roots_before = num_roots()?; + + let sha = cmd!(sh, "git rev-parse HEAD").output().context("FAILED to get current commit")?.stdout; + + // Merge the fetched commit. + const MERGE_COMMIT_MESSAGE: &str = "Merge from rustc"; + cmd!(sh, "git merge FETCH_HEAD --no-verify --no-ff -m {MERGE_COMMIT_MESSAGE}") + .run() + .context("FAILED to merge new commits, something went wrong")?; + + let current_sha = cmd!(sh, "git rev-parse HEAD").output().context("FAILED to get current commit")?.stdout; + if current_sha == sha { + cmd!(sh, "git reset --hard HEAD^") + .run() + .expect("FAILED to clean up after creating the preparation commit"); + eprintln!("No merge was performed, no changes to pull were found. Rolled back the preparation commit."); + return Err(RustcPullError::NothingToPull); + } + + // Check that the number of roots did not increase. + if num_roots()? != num_roots_before { + return Err(anyhow::anyhow!("Josh created a new root commit. This is probably not the history you want.").into()); + } + + drop(josh); + Ok(()) + } + + pub fn rustc_push(&self, github_user: String, branch: String) -> anyhow::Result<()> { + let sh = Shell::new()?; + sh.change_dir(&self.dir); + let base = sh.read_file("rust-version")?.trim().to_owned(); + // Make sure the repo is clean. + if cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty().not() { + bail!("working directory must be clean before running `rustc-push`"); + } + // Make sure josh is running. + let josh = Self::start_josh()?; + let josh_url = + format!("http://localhost:{JOSH_PORT}/{github_user}/rust.git{JOSH_FILTER}.git"); + + // Find a repo we can do our preparation in. + if let Ok(rustc_git) = env::var("RUSTC_GIT") { + // If rustc_git is `Some`, we'll use an existing fork for the branch updates. + sh.change_dir(rustc_git); + } else { + // Otherwise, do this in the local repo. + println!( + "This will pull a copy of the rust-lang/rust history into this checkout, growing it by about 1GB." + ); + print!( + "To avoid that, abort now and set the `RUSTC_GIT` environment variable to an existing rustc checkout. Proceed? [y/N] " + ); + std::io::stdout().flush()?; + let mut answer = String::new(); + std::io::stdin().read_line(&mut answer)?; + if answer.trim().to_lowercase() != "y" { + std::process::exit(1); + } + }; + // Prepare the branch. Pushing works much better if we use as base exactly + // the commit that we pulled from last time, so we use the `rust-version` + // file to find out which commit that would be. + println!("Preparing {github_user}/rust (base: {base})..."); + if cmd!(sh, "git fetch https://github.com/{github_user}/rust {branch}") + .ignore_stderr() + .read() + .is_ok() + { + println!( + "The branch '{branch}' seems to already exist in 'https://github.com/{github_user}/rust'. Please delete it and try again." + ); + std::process::exit(1); + } + cmd!(sh, "git fetch https://github.com/{UPSTREAM_REPO} {base}").run()?; + cmd!(sh, "git push https://github.com/{github_user}/rust {base}:refs/heads/{branch}") + .ignore_stdout() + .ignore_stderr() // silence the "create GitHub PR" message + .run()?; + println!(); + + // Do the actual push. + sh.change_dir(&self.dir); + println!("Pushing changes..."); + cmd!(sh, "git push {josh_url} HEAD:{branch}").run()?; + println!(); + + // Do a round-trip check to make sure the push worked as expected. + cmd!(sh, "git fetch {josh_url} {branch}").ignore_stderr().read()?; + let head = cmd!(sh, "git rev-parse HEAD").read()?; + let fetch_head = cmd!(sh, "git rev-parse FETCH_HEAD").read()?; + if head != fetch_head { + bail!( + "Josh created a non-roundtrip push! Do NOT merge this into rustc!\n\ + Expected {head}, got {fetch_head}." + ); + } + println!( + "Confirmed that the push round-trips back to rustc-dev-guide properly. Please create a rustc PR:" + ); + println!( + // Open PR with `subtree update` title to silence the `no-merges` triagebot check + " https://github.com/{UPSTREAM_REPO}/compare/{github_user}:{branch}?quick_pull=1&title=Rustc+dev+guide+subtree+update&body=r?+@ghost" + ); + + drop(josh); + Ok(()) + } + + fn start_josh() -> anyhow::Result { + // Determine cache directory. + let local_dir = { + let user_dirs = + directories::ProjectDirs::from("org", "rust-lang", "rustc-dev-guide-josh").unwrap(); + user_dirs.cache_dir().to_owned() + }; + + // Start josh, silencing its output. + let mut cmd = process::Command::new("josh-proxy"); + cmd.arg("--local").arg(local_dir); + cmd.arg("--remote").arg("https://github.com"); + cmd.arg("--port").arg(JOSH_PORT.to_string()); + cmd.arg("--no-background"); + cmd.stdout(process::Stdio::null()); + cmd.stderr(process::Stdio::null()); + let josh = cmd.spawn().context("failed to start josh-proxy, make sure it is installed")?; + + // Create a wrapper that stops it on drop. + struct Josh(process::Child); + impl Drop for Josh { + fn drop(&mut self) { + #[cfg(unix)] + { + // Try to gracefully shut it down. + process::Command::new("kill") + .args(["-s", "INT", &self.0.id().to_string()]) + .output() + .expect("failed to SIGINT josh-proxy"); + // Sadly there is no "wait with timeout"... so we just give it some time to finish. + std::thread::sleep(Duration::from_millis(100)); + // Now hopefully it is gone. + if self.0.try_wait().expect("failed to wait for josh-proxy").is_some() { + return; + } + } + // If that didn't work (or we're not on Unix), kill it hard. + eprintln!( + "I have to kill josh-proxy the hard way, let's hope this does not break anything." + ); + self.0.kill().expect("failed to SIGKILL josh-proxy"); + } + } + + // Wait until the port is open. We try every 10ms until 1s passed. + for _ in 0..100 { + // This will generally fail immediately when the port is still closed. + let josh_ready = net::TcpStream::connect_timeout( + &net::SocketAddr::from(([127, 0, 0, 1], JOSH_PORT)), + Duration::from_millis(1), + ); + if josh_ready.is_ok() { + return Ok(Josh(josh)); + } + // Not ready yet. + std::thread::sleep(Duration::from_millis(10)); + } + bail!("Even after waiting for 1s, josh-proxy is still not available.") + } +} diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version new file mode 100644 index 0000000000000..78e9ecdf174be --- /dev/null +++ b/src/doc/rustc-dev-guide/rust-version @@ -0,0 +1 @@ +124cc92199ffa924f6b4c7cc819a85b65e0c3984 diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md index 2fbbb187d6b3f..91c4aeacbd749 100644 --- a/src/doc/rustc-dev-guide/src/SUMMARY.md +++ b/src/doc/rustc-dev-guide/src/SUMMARY.md @@ -75,6 +75,7 @@ - [Prologue](./building/bootstrapping/intro.md) - [What Bootstrapping does](./building/bootstrapping/what-bootstrapping-does.md) - [How Bootstrap does it](./building/bootstrapping/how-bootstrap-does-it.md) +- [Debugging bootstrap](./building/bootstrapping/debugging-bootstrap.md) # High-level Compiler Architecture @@ -134,14 +135,13 @@ - [Prologue](./part-4-intro.md) - [Generic parameter definitions](./generic_parameters_summary.md) - - [Implementation nuances of early/late bound parameters](./early-late-bound-params/early-late-bound-implementation-nuances.md) - - [Interactions with turbofishing](./early-late-bound-params/turbofishing-and-early-late-bound.md) + - [`EarlyBinder` and instantiating parameters](./ty_module/early_binder.md) +- [Binders and Higher ranked regions](./ty_module/binders.md) + - [Instantiating binders](./ty_module/instantiating_binders.md) +- [Early vs Late bound parameters](./early_late_parameters.md) - [The `ty` module: representing types](./ty.md) - [ADTs and Generic Arguments](./ty_module/generic_arguments.md) - [Parameter types/consts/regions](./ty_module/param_ty_const_regions.md) - - [`EarlyBinder` and instantiating parameters](./ty_module/early_binder.md) - - [`Binder` and Higher ranked regions](./ty_module/binders.md) - - [Instantiating binders](./ty_module/instantiating_binders.md) - [`TypeFolder` and `TypeFoldable`](./ty-fold.md) - [Parameter Environments](./param_env/param_env_summary.md) - [What is it?](./param_env/param_env_what_is_it.md) diff --git a/src/doc/rustc-dev-guide/src/about-this-guide.md b/src/doc/rustc-dev-guide/src/about-this-guide.md index 793bfa9e66e59..781a5c51bf7a8 100644 --- a/src/doc/rustc-dev-guide/src/about-this-guide.md +++ b/src/doc/rustc-dev-guide/src/about-this-guide.md @@ -72,7 +72,6 @@ You might also find the following sites useful: - The [Rust reference][rr], even though it doesn't specifically talk about Rust's internals, is a great resource nonetheless - Although out of date, [Tom Lee's great blog article][tlgba] is very helpful -- [rustaceans.org][ro] is helpful, but mostly dedicated to IRC - The [Rust Compiler Testing Docs][rctd] - For [@bors], [this cheat sheet][cheatsheet] is helpful - Google is always helpful when programming. diff --git a/src/doc/rustc-dev-guide/src/appendix/bibliography.md b/src/doc/rustc-dev-guide/src/appendix/bibliography.md index 8f6810cbcaeac..93426b645a61e 100644 --- a/src/doc/rustc-dev-guide/src/appendix/bibliography.md +++ b/src/doc/rustc-dev-guide/src/appendix/bibliography.md @@ -82,7 +82,7 @@ Rust, as well as publications about Rust. * [Ownership is Theft: Experiences Building an Embedded OS in Rust - Amit Levy, et. al.](https://amitlevy.com/papers/tock-plos2015.pdf) * [You can't spell trust without Rust](https://faultlore.com/blah/papers/thesis.pdf). Aria Beingessner's master's thesis. * [Rust-Bio: a fast and safe bioinformatics library](https://rust-bio.github.io/). Johannes Köster -* [Safe, Correct, and Fast Low-Level Networking](https://octarineparrot.com/assets/msci_paper.pdf). Robert Clipsham's master's thesis. +* [Safe, Correct, and Fast Low-Level Networking](https://csperkins.org/research/thesis-msci-clipsham.pdf). Robert Clipsham's master's thesis. * [Formalizing Rust traits](https://open.library.ubc.ca/cIRcle/collections/ubctheses/24/items/1.0220521). Jonatan Milewski's master's thesis. * [Rust as a Language for High Performance GC Implementation](https://dl.acm.org/doi/pdf/10.1145/3241624.2926707) * [Simple Verification of Rust Programs via Functional Purification](https://github.com/Kha/electrolysis). Sebastian Ullrich's master's thesis. diff --git a/src/doc/rustc-dev-guide/src/appendix/code-index.md b/src/doc/rustc-dev-guide/src/appendix/code-index.md index c33887d729418..b96ede68eab51 100644 --- a/src/doc/rustc-dev-guide/src/appendix/code-index.md +++ b/src/doc/rustc-dev-guide/src/appendix/code-index.md @@ -14,17 +14,16 @@ Item | Kind | Short description | Chapter | `Diag` | struct | A struct for a compiler diagnostic, such as an error or lint | [Emitting Diagnostics] | [compiler/rustc_errors/src/diagnostic.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Diag.html) `DocContext` | struct | A state container used by rustdoc when crawling through a crate to gather its documentation | [Rustdoc] | [src/librustdoc/core.rs](https://github.com/rust-lang/rust/blob/master/src/librustdoc/core.rs) `HirId` | struct | One of four types of HIR node identifiers | [Identifiers in the HIR] | [compiler/rustc_hir/src/hir_id.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir_id/struct.HirId.html) +`Lexer` | struct | This is the lexer used during parsing. It consumes characters from the raw source code being compiled and produces a series of tokens for use by the rest of the parser | [The parser] | [compiler/rustc_parse/src/lexer/mod.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.Lexer.html) `NodeId` | struct | One of four types of HIR node identifiers. Being phased out | [Identifiers in the HIR] | [compiler/rustc_ast/src/ast.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/node_id/struct.NodeId.html) `P` | struct | An owned immutable smart pointer. By contrast, `&T` is not owned, and `Box` is not immutable. | None | [compiler/rustc_ast/src/ptr.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ptr/struct.P.html) `ParamEnv` | struct | Information about generic parameters or `Self`, useful for working with associated or generic items | [Parameter Environment] | [compiler/rustc_middle/src/ty/mod.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html) `ParseSess` | struct | This struct contains information about a parsing session | [The parser] | [compiler/rustc_session/src/parse/parse.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/parse/struct.ParseSess.html) -`Query` | struct | Represents the result of query to the `Compiler` interface and allows stealing, borrowing, and returning the results of compiler passes. | [The Rustc Driver and Interface] | [compiler/rustc_interface/src/queries.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/queries/struct.Query.html) `Rib` | struct | Represents a single scope of names | [Name resolution] | [compiler/rustc_resolve/src/lib.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/late/struct.Rib.html) `Session` | struct | The data associated with a compilation session | [The parser], [The Rustc Driver and Interface] | [compiler/rustc_session/src/session.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/struct.Session.html) `SourceFile` | struct | Part of the `SourceMap`. Maps AST nodes to their source code for a single source file. Was previously called FileMap | [The parser] | [compiler/rustc_span/src/lib.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.SourceFile.html) `SourceMap` | struct | Maps AST nodes to their source code. It is composed of `SourceFile`s. Was previously called CodeMap | [The parser] | [compiler/rustc_span/src/source_map.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/source_map/struct.SourceMap.html) `Span` | struct | A location in the user's source code, used for error reporting primarily | [Emitting Diagnostics] | [compiler/rustc_span/src/span_encoding.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html) -`StringReader` | struct | This is the lexer used during parsing. It consumes characters from the raw source code being compiled and produces a series of tokens for use by the rest of the parser | [The parser] | [compiler/rustc_parse/src/lexer/mod.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.StringReader.html) `rustc_ast::token_stream::TokenStream` | struct | An abstract sequence of tokens, organized into `TokenTree`s | [The parser], [Macro expansion] | [compiler/rustc_ast/src/tokenstream.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/tokenstream/struct.TokenStream.html) `TraitDef` | struct | This struct contains a trait's definition with type information | [The `ty` modules] | [compiler/rustc_middle/src/ty/trait_def.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/trait_def/struct.TraitDef.html) `TraitRef` | struct | The combination of a trait and its input types (e.g. `P0: Trait`) | [Trait Solving: Goals and Clauses] | [compiler/rustc_middle/src/ty/sty.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.TraitRef.html) diff --git a/src/doc/rustc-dev-guide/src/appendix/glossary.md b/src/doc/rustc-dev-guide/src/appendix/glossary.md index bf3475a984b03..a7c3236d356ba 100644 --- a/src/doc/rustc-dev-guide/src/appendix/glossary.md +++ b/src/doc/rustc-dev-guide/src/appendix/glossary.md @@ -69,7 +69,7 @@ Term | Meaning rib | A data structure in the name resolver that keeps track of a single scope for names. ([see more](../name-resolution.md)) RPIT | A return-position `impl Trait`. ([see the reference](https://doc.rust-lang.org/reference/types/impl-trait.html#abstract-return-types)). RPITIT | A return-position `impl Trait` in trait. Unlike RPIT, this is desugared to a generic associated type (GAT). Introduced in [RFC 3425](https://rust-lang.github.io/rfcs/3425-return-position-impl-trait-in-traits.html). ([see more](../return-position-impl-trait-in-trait.md)) -scrutinee | A scrutinee is the expression that is matched on in `match` expressions and similar pattern matching constructs. For example, in `match x { A => 1, B => 2 }`, the expression `x` is the scrutinee. +scrutinee | A scrutinee is the expression that is matched on in `match` expressions and similar pattern matching constructs. For example, in `match x { A => 1, B => 2 }`, the expression `x` is the scrutinee. `sess` | The compiler _session_, which stores global data used throughout compilation side tables | Because the [AST](#ast) and HIR are immutable once created, we often carry extra information about them in the form of hashtables, indexed by the id of a particular node. sigil | Like a keyword but composed entirely of non-alphanumeric tokens. For example, `&` is a sigil for references. diff --git a/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md b/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md index 3521c00f5cf2d..0f81d3cb48a1d 100644 --- a/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md +++ b/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md @@ -45,7 +45,7 @@ heavily on other parts of the crate. The separation of the code must not affect the logic of the code nor its performance. For these reasons, the separation process involves two transformations that -have to be done at the same time for the resulting code to compile : +have to be done at the same time for the resulting code to compile: 1. replace all the LLVM-specific types by generics inside function signatures and structure definitions; diff --git a/src/doc/rustc-dev-guide/src/backend/debugging.md b/src/doc/rustc-dev-guide/src/backend/debugging.md index 275a1777a9964..805291017c274 100644 --- a/src/doc/rustc-dev-guide/src/backend/debugging.md +++ b/src/doc/rustc-dev-guide/src/backend/debugging.md @@ -56,7 +56,7 @@ These tools include: By default, the Rust build system does not check for changes to the LLVM source code or its build configuration settings. So, if you need to rebuild the LLVM that is linked -into `rustc`, first delete the file `llvm-finished-building`, which should be located +into `rustc`, first delete the file `.llvm-stamp`, which should be located in `build//llvm/`. The default rustc compilation pipeline has multiple codegen units, which is diff --git a/src/doc/rustc-dev-guide/src/backend/libs-and-metadata.md b/src/doc/rustc-dev-guide/src/backend/libs-and-metadata.md index b0823b9a5eed3..556b3fdf8f84f 100644 --- a/src/doc/rustc-dev-guide/src/backend/libs-and-metadata.md +++ b/src/doc/rustc-dev-guide/src/backend/libs-and-metadata.md @@ -42,7 +42,7 @@ format is specific to `rustc`, and may change over time. This file contains: ### dylib A `dylib` is a platform-specific shared library. It includes the `rustc` -[metadata] in a special link section called `.rustc` in a compressed format. +[metadata] in a special link section called `.rustc`. ### rmeta diff --git a/src/doc/rustc-dev-guide/src/borrow_check/region_inference/closure_constraints.md b/src/doc/rustc-dev-guide/src/borrow_check/region_inference/closure_constraints.md index be279b773cf8a..b95c9f72306be 100644 --- a/src/doc/rustc-dev-guide/src/borrow_check/region_inference/closure_constraints.md +++ b/src/doc/rustc-dev-guide/src/borrow_check/region_inference/closure_constraints.md @@ -31,9 +31,9 @@ It starts by calling `fn try_promote_type_test_subject`. This function takes the We then promote the `lower_bound` into the context of the caller. If the lower bound is equal to a placeholder, we replace it with `'static` -We then look at all universal regions `uv` which are required to outlive `lower_bound`, i.e. for which borrow checking adding region constraints. For each of these we then emit a `ClosureOutlivesRequirement` for non-local universal regions which are known to outlive `uv`. +We then look at all universal regions `uv` which are required to be outlived by `lower_bound`, i.e. for which borrow checking added region constraints. For each of these we then emit a `ClosureOutlivesRequirement` for all non-local universal regions which are known to outlive `uv`. -As we've already built the region graph of the closure at this point and emitted errors if that one is inconsistent, we are also able to assume that the outlive constraints `uv: lower_bound` hold. +As we've already built the region graph of the closure at this point and separately check that it is consistent, we are also able to assume the outlive constraints `uv: lower_bound` here. So if we have a type-outlives bounds we can't prove, e.g. `T: 'local_infer`, we use the region graph to go to universal variables `'a` with `'a: local_infer`. In case `'a` are local, we then use the assumed outlived constraints to go to non-local ones. diff --git a/src/doc/rustc-dev-guide/src/bug-fix-procedure.md b/src/doc/rustc-dev-guide/src/bug-fix-procedure.md index 4857cf5e0bee4..e6a16df6d2a9c 100644 --- a/src/doc/rustc-dev-guide/src/bug-fix-procedure.md +++ b/src/doc/rustc-dev-guide/src/bug-fix-procedure.md @@ -227,7 +227,7 @@ that we use for unstable features: Ideally, breaking changes should have landed on the **stable branch** of the compiler before they are finalized. - + ### Removing a lint diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md new file mode 100644 index 0000000000000..04fa5b204dd4e --- /dev/null +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md @@ -0,0 +1,134 @@ +# Debugging bootstrap + +> FIXME: this section should be expanded + +## `tracing` in bootstrap + +Bootstrap has conditional [`tracing`][tracing] setup to provide structured logging. + +[tracing]: https://docs.rs/tracing/0.1.41/tracing/index.html + +### Enabling `tracing` output + +Bootstrap will conditionally build `tracing` support and enable `tracing` output if the `BOOTSTRAP_TRACING` env var is set. + +#### Basic usage + +Example basic usage[^just-trace]: + +[^just-trace]: It is not recommend to use *just* `BOOTSTRAP_TRACING=TRACE` because that will dump *everything* at `TRACE` level, including logs intentionally gated behind custom targets as they are too verbose even for `TRACE` level by default. + +```bash +$ BOOTSTRAP_TRACING=bootstrap=TRACE ./x build library --stage 1 +``` + +Example output[^unstable]: + +``` +$ BOOTSTRAP_TRACING=bootstrap=TRACE ./x check src/bootstrap/ +Building bootstrap + Compiling bootstrap v0.0.0 (/home/joe/repos/rust/src/bootstrap) + Finished `dev` profile [unoptimized] target(s) in 2.74s + DEBUG bootstrap parsing flags + bootstrap::core::config::flags::Flags::parse args=["check", "src/bootstrap/"] + DEBUG bootstrap parsing config based on flags + DEBUG bootstrap creating new build based on config + bootstrap::Build::build + TRACE bootstrap setting up job management + TRACE bootstrap downloading rustfmt early + bootstrap::handling hardcoded subcommands (Format, Suggest, Perf) + DEBUG bootstrap not a hardcoded subcommand; returning to normal handling, cmd=Check { all_targets: false } + DEBUG bootstrap handling subcommand normally + bootstrap::executing real run + bootstrap::(1) executing dry-run sanity-check + bootstrap::(2) executing actual run +Checking stage0 library artifacts (x86_64-unknown-linux-gnu) + Finished `release` profile [optimized + debuginfo] target(s) in 0.04s +Checking stage0 compiler artifacts {rustc-main, rustc_abi, rustc_arena, rustc_ast, rustc_ast_ir, rustc_ast_lowering, rustc_ast_passes, rustc_ast_pretty, rustc_attr_data_structures, rustc_attr_parsing, rustc_baked_icu_data, rustc_borrowck, rustc_builtin_macros, rustc_codegen_llvm, rustc_codegen_ssa, rustc_const_eval, rustc_data_structures, rustc_driver, rustc_driver_impl, rustc_error_codes, rustc_error_messages, rustc_errors, rustc_expand, rustc_feature, rustc_fluent_macro, rustc_fs_util, rustc_graphviz, rustc_hir, rustc_hir_analysis, rustc_hir_pretty, rustc_hir_typeck, rustc_incremental, rustc_index, rustc_index_macros, rustc_infer, rustc_interface, rustc_lexer, rustc_lint, rustc_lint_defs, rustc_llvm, rustc_log, rustc_macros, rustc_metadata, rustc_middle, rustc_mir_build, rustc_mir_dataflow, rustc_mir_transform, rustc_monomorphize, rustc_next_trait_solver, rustc_parse, rustc_parse_format, rustc_passes, rustc_pattern_analysis, rustc_privacy, rustc_query_impl, rustc_query_system, rustc_resolve, rustc_sanitizers, rustc_serialize, rustc_session, rustc_smir, rustc_span, rustc_symbol_mangling, rustc_target, rustc_trait_selection, rustc_traits, rustc_transmute, rustc_ty_utils, rustc_type_ir, rustc_type_ir_macros, stable_mir} (x86_64-unknown-linux-gnu) + Finished `release` profile [optimized + debuginfo] target(s) in 0.23s +Checking stage0 bootstrap artifacts (x86_64-unknown-linux-gnu) + Checking bootstrap v0.0.0 (/home/joe/repos/rust/src/bootstrap) + Finished `release` profile [optimized + debuginfo] target(s) in 0.64s + DEBUG bootstrap checking for postponed test failures from `test --no-fail-fast` +Build completed successfully in 0:00:08 +``` + +#### Controlling log output + +The env var `BOOTSTRAP_TRACING` accepts a [`tracing` env-filter][tracing-env-filter]. + +There are two orthogonal ways to control which kind of logs you want: + +1. You can specify the log **level**, e.g. `DEBUG` or `TRACE`. +2. You can also control the log **target**, e.g. `bootstrap` or `bootstrap::core::config` vs custom targets like `CONFIG_HANDLING`. + - Custom targets are used to limit what is output when `BOOTSTRAP_TRACING=bootstrap=TRACE` is used, as they can be too verbose even for `TRACE` level by default. Currently used custom targets: + - `CONFIG_HANDLING` + +The `TRACE` filter will enable *all* `trace` level or less verbose level tracing output. + +You can of course combine them (custom target logs are typically gated behind `TRACE` log level additionally): + +```bash +$ BOOTSTRAP_TRACING=CONFIG_HANDLING=TRACE ./x build library --stage 1 +``` + +[^unstable]: This output is always subject to further changes. + +[tracing-env-filter]: https://docs.rs/tracing-subscriber/0.3.19/tracing_subscriber/filter/struct.EnvFilter.html + +### Using `tracing` in bootstrap + +Both `tracing::*` macros and the `tracing::instrument` proc-macro attribute need to be gated behind `tracing` feature. Examples: + +```rs +#[cfg(feature = "tracing")] +use tracing::{instrument, trace}; + +struct Foo; + +impl Step for Foo { + type Output = (); + + #[cfg_attr(feature = "tracing", instrument(level = "trace", name = "Foo::should_run", skip_all))] + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + #[cfg(feature = "tracing")] + trace!(?run, "entered Foo::should_run"); + + todo!() + } + + #[cfg_attr( + feature = "tracing", + instrument( + level = "trace", + name = "Foo::run", + skip_all, + fields(compiler = ?builder.compiler), + ), + )] + fn run(self, builder: &Builder<'_>) -> Self::Output { + #[cfg(feature = "tracing")] + trace!(?run, "entered Foo::run"); + + todo!() + } +} +``` + +For `#[instrument]`, it's recommended to: + +- Gate it behind `trace` level for fine-granularity, possibly `debug` level for core functions. +- Explicitly pick an instrumentation name via `name = ".."` to distinguish between e.g. `run` of different steps. +- Take care to not cause diverging behavior via tracing, e.g. building extra things only when tracing infra is enabled. + +### Profiling bootstrap + +You can use the `COMMAND` tracing target to trace execution of most commands spawned by bootstrap. If you also use the `BOOTSTRAP_PROFILE=1` environment variable, bootstrap will generate a Chrome JSON trace file, which can be visualized in Chrome's `chrome://tracing` page or on https://ui.perfetto.dev. + +```bash +$ BOOTSTRAP_TRACING=COMMAND=trace BOOTSTRAP_PROFILE=1 ./x build library +``` + +### rust-analyzer integration? + +Unfortunately, because bootstrap is a `rust-analyzer.linkedProjects`, you can't ask r-a to check/build bootstrap itself with `tracing` feature enabled to get relevant completions, due to lack of support as described in . diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/intro.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/intro.md index f829884fb9374..f72918c8377fc 100644 --- a/src/doc/rustc-dev-guide/src/building/bootstrapping/intro.md +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/intro.md @@ -17,5 +17,8 @@ In this section, we give a high-level overview of [what Bootstrap does](./what-bootstrapping-does.md), followed by a high-level introduction to [how Bootstrap does it](./how-bootstrap-does-it.md). +Additionally, see [debugging bootstrap](./debugging-bootstrap.md) to learn +about debugging methods. + [boot]: https://en.wikipedia.org/wiki/Bootstrapping_(compilers) [ocaml-compiler]: https://github.com/rust-lang/rust/tree/ef75860a0a72f79f97216f8aaa5b388d98da6480/src/boot diff --git a/src/doc/rustc-dev-guide/src/building/new-target.md b/src/doc/rustc-dev-guide/src/building/new-target.md index 1d9fa1b52d595..cd215277e69f6 100644 --- a/src/doc/rustc-dev-guide/src/building/new-target.md +++ b/src/doc/rustc-dev-guide/src/building/new-target.md @@ -4,8 +4,13 @@ These are a set of steps to add support for a new target. There are numerous end states and paths to get there, so not all sections may be relevant to your desired goal. +See also the associated documentation in the +[target tier policy][target_tier_policy_add]. + +[target_tier_policy_add]: https://doc.rust-lang.org/rustc/target-tier-policy.html#adding-a-new-target + ## Specifying a new LLVM For very new targets, you may need to use a different fork of LLVM diff --git a/src/doc/rustc-dev-guide/src/building/optimized-build.md b/src/doc/rustc-dev-guide/src/building/optimized-build.md index 3080dc6bf5d3f..8feda59829b45 100644 --- a/src/doc/rustc-dev-guide/src/building/optimized-build.md +++ b/src/doc/rustc-dev-guide/src/building/optimized-build.md @@ -126,4 +126,4 @@ Here is an example of how can `opt-dist` be used locally (outside of CI): [`Environment`]: https://github.com/rust-lang/rust/blob/ee451f8faccf3050c76cdcd82543c917b40c7962/src/tools/opt-dist/src/environment.rs#L5 > Note: if you want to run the actual CI pipeline, instead of running `opt-dist` locally, -> you can execute `DEPLOY=1 src/ci/docker/run.sh dist-x86_64-linux`. +> you can execute `python3 src/ci/github-actions/ci.py run-local dist-x86_64-linux`. diff --git a/src/doc/rustc-dev-guide/src/building/suggested.md b/src/doc/rustc-dev-guide/src/building/suggested.md index bf5ffbc00afda..2c6c3fe1df84b 100644 --- a/src/doc/rustc-dev-guide/src/building/suggested.md +++ b/src/doc/rustc-dev-guide/src/building/suggested.md @@ -135,24 +135,24 @@ and follow the same instructions as above. ### Emacs Emacs provides support for rust-analyzer with project-local configuration -through [Eglot](https://www.gnu.org/software/emacs/manual/html_node/eglot/). +through [Eglot](https://www.gnu.org/software/emacs/manual/html_node/eglot/). Steps for setting up Eglot with rust-analyzer can be [found -here](https://rust-analyzer.github.io/manual.html#eglot). +here](https://rust-analyzer.github.io/manual.html#eglot). Having set up Emacs & Eglot for Rust development in general, you can run `./x setup editor` and select `emacs`, which will prompt you to create `.dir-locals.el` with the recommended configuration for Eglot. -The recommended settings live at [`src/etc/rust_analyzer_eglot.el`]. +The recommended settings live at [`src/etc/rust_analyzer_eglot.el`]. For more information on project-specific Eglot configuration, consult [the manual](https://www.gnu.org/software/emacs/manual/html_node/eglot/Project_002dspecific-configuration.html). ### Helix -Helix comes with built-in LSP and rust-analyzer support. +Helix comes with built-in LSP and rust-analyzer support. It can be configured through `languages.toml`, as described -[here](https://docs.helix-editor.com/languages.html). +[here](https://docs.helix-editor.com/languages.html). You can run `./x setup editor` and select `helix`, which will prompt you to create `languages.toml` with the recommended configuration for Helix. The -recommended settings live at [`src/etc/rust_analyzer_helix.toml`]. +recommended settings live at [`src/etc/rust_analyzer_helix.toml`]. ## Check, check, and check again @@ -181,7 +181,7 @@ example, running `tidy` and `linkchecker` is useful when editing Markdown files, whereas UI tests are much less likely to be helpful. While `x suggest` is a useful tool, it does not guarantee perfect coverage (just as PR CI isn't a substitute for bors). See the [dedicated chapter](../tests/suggest-tests.md) for -more information and contribution instructions. +more information and contribution instructions. Please note that `x suggest` is in a beta state currently and the tests that it will suggest are limited. @@ -332,29 +332,22 @@ git worktree add -b my-feature ../rust2 master You can then use that rust2 folder as a separate workspace for modifying and building `rustc`! -## Using nix-shell +## Working with nix -If you're using nix, you can use the following nix-shell to work on Rust: +Several nix configurations are defined in `src/tools/nix-dev-shell`. -```nix -{ pkgs ? import {} }: -pkgs.mkShell { - name = "rustc"; - nativeBuildInputs = with pkgs; [ - binutils cmake ninja pkg-config python3 git curl cacert patchelf nix - ]; - buildInputs = with pkgs; [ - openssl glibc.out glibc.static - ]; - # Avoid creating text files for ICEs. - RUSTC_ICE = "0"; - # Provide `libstdc++.so.6` for the self-contained lld. - LD_LIBRARY_PATH = "${with pkgs; lib.makeLibraryPath [ - stdenv.cc.cc.lib - ]}"; -} +If you're using direnv, you can create a symbol link to `src/tools/nix-dev-shell/envrc-flake` or `src/tools/nix-dev-shell/envrc-shell` + +```bash +ln -s ./src/tools/nix-dev-shell/envrc-flake ./.envrc # Use flake +``` +or +```bash +ln -s ./src/tools/nix-dev-shell/envrc-shell ./.envrc # Use nix-shell ``` +### Note + Note that when using nix on a not-NixOS distribution, it may be necessary to set **`patch-binaries-for-nix = true` in `config.toml`**. Bootstrap tries to detect whether it's running in nix and enable patching automatically, but this diff --git a/src/doc/rustc-dev-guide/src/closure.md b/src/doc/rustc-dev-guide/src/closure.md index 718a0e5d78b11..427919cd57995 100644 --- a/src/doc/rustc-dev-guide/src/closure.md +++ b/src/doc/rustc-dev-guide/src/closure.md @@ -157,7 +157,7 @@ The other option is to step through the code using lldb or gdb. 1. `rust-lldb build/host/stage1/bin/rustc test.rs` 2. In lldb: - 1. `b upvar.rs:134` // Setting the breakpoint on a certain line in the upvar.rs file` + 1. `b upvar.rs:134` // Setting the breakpoint on a certain line in the upvar.rs file 2. `r` // Run the program until it hits the breakpoint Let's start with [`upvar.rs`][upvar]. This file has something called diff --git a/src/doc/rustc-dev-guide/src/compiler-debugging.md b/src/doc/rustc-dev-guide/src/compiler-debugging.md index a2a6c8085df58..e2097b26e5c83 100644 --- a/src/doc/rustc-dev-guide/src/compiler-debugging.md +++ b/src/doc/rustc-dev-guide/src/compiler-debugging.md @@ -275,7 +275,7 @@ Here are some notable ones: | `rustc_dump_def_parents` | Dumps the chain of `DefId` parents of certain definitions. | | `rustc_dump_item_bounds` | Dumps the [`item_bounds`] of an item. | | `rustc_dump_predicates` | Dumps the [`predicates_of`] an item. | -| `rustc_dump_vtable` | | +| `rustc_dump_vtable` | Dumps the vtable layout of an impl, or a type alias of a dyn type. | | `rustc_hidden_type_of_opaques` | Dumps the [hidden type of each opaque types][opaq] in the crate. | | `rustc_layout` | [See this section](#debugging-type-layouts). | | `rustc_object_lifetime_default` | Dumps the [object lifetime defaults] of an item. | diff --git a/src/doc/rustc-dev-guide/src/const-eval.md b/src/doc/rustc-dev-guide/src/const-eval.md index ee0269601af1c..69329a3e08511 100644 --- a/src/doc/rustc-dev-guide/src/const-eval.md +++ b/src/doc/rustc-dev-guide/src/const-eval.md @@ -17,7 +17,7 @@ Prominent examples are: * need to be known to check for overlapping patterns Additionally constant evaluation can be used to reduce the workload or binary -size at runtime by precomputing complex operations at compiletime and only +size at runtime by precomputing complex operations at compile time and only storing the result. All uses of constant evaluation can either be categorized as "influencing the type system" diff --git a/src/doc/rustc-dev-guide/src/contributing.md b/src/doc/rustc-dev-guide/src/contributing.md index ad1d33265c55a..9817326f07ba9 100644 --- a/src/doc/rustc-dev-guide/src/contributing.md +++ b/src/doc/rustc-dev-guide/src/contributing.md @@ -422,68 +422,8 @@ Just a few things to keep in mind: ## Issue triage -Sometimes, an issue will stay open, even though the bug has been fixed. -And sometimes, the original bug may go stale because something has changed in the meantime. +Please see . -It can be helpful to go through older bug reports and make sure that they are still valid. -Load up an older issue, double check that it's still true, -and leave a comment letting us know if it is or is not. -The [least recently updated sort][lru] is good for finding issues like this. - -[Thanks to `@rustbot`][rustbot], anyone can help triage issues by adding -appropriate labels to issues that haven't been triaged yet: - -[lru]: https://github.com/rust-lang/rust/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-asc -[rustbot]: ./rustbot.md - - - -| Labels | Color | Description | -|--------|-------|-------------| -| [A-] |  Yellow | The **area** of the project an issue relates to. | -| [B-] |  Magenta | Issues which are **blockers**. | -| [beta-] |  Dark Blue | Tracks changes which need to be [backported to beta][beta-backport] | -| [C-] |  Light Purple | The **category** of an issue. | -| [D-] |  Mossy Green | Issues for **diagnostics**. | -| [E-] |  Green | The **experience** level necessary to fix an issue. | -| [F-] |  Peach | Issues for **nightly features**. | -| [I-] |  Red | The **importance** of the issue. | -| [I-\*-nominated] |  Red | The issue has been nominated for discussion at the next meeting of the corresponding team. | -| [I-prioritize] |  Red | The issue has been nominated for prioritization by the team tagged with a **T**-prefixed label. | -| [L-] |  Teal | The relevant **lint**. | -| [metabug] |  Purple | Bugs that collect other bugs. | -| [O-] |  Purple Grey | The **operating system** or platform that the issue is specific to. | -| [P-] |  Orange | The issue **priority**. These labels can be assigned by anyone that understand the issue and is able to prioritize it, and remove the [I-prioritize] label. | -| [regression-] |  Pink | Tracks regressions from a stable release. | -| [relnotes] |  Light Orange | Changes that should be documented in the release notes of the next release. | -| [S-] |  Gray | Tracks the **status** of pull requests. | -| [S-tracking-] |  Steel Blue | Tracks the **status** of [tracking issues]. | -| [stable-] |  Dark Blue | Tracks changes which need to be [backported to stable][stable-backport] in anticipation of a point release. | -| [T-] |  Blue | Denotes which **team** the issue belongs to. | -| [WG-] |  Green | Denotes which **working group** the issue belongs to. | - - -[A-]: https://github.com/rust-lang/rust/labels?q=A -[B-]: https://github.com/rust-lang/rust/labels?q=B -[C-]: https://github.com/rust-lang/rust/labels?q=C -[D-]: https://github.com/rust-lang/rust/labels?q=D -[E-]: https://github.com/rust-lang/rust/labels?q=E -[F-]: https://github.com/rust-lang/rust/labels?q=F -[I-]: https://github.com/rust-lang/rust/labels?q=I -[L-]: https://github.com/rust-lang/rust/labels?q=L -[O-]: https://github.com/rust-lang/rust/labels?q=O -[P-]: https://github.com/rust-lang/rust/labels?q=P -[S-]: https://github.com/rust-lang/rust/labels?q=S -[T-]: https://github.com/rust-lang/rust/labels?q=T -[WG-]: https://github.com/rust-lang/rust/labels?q=WG [stable-]: https://github.com/rust-lang/rust/labels?q=stable [beta-]: https://github.com/rust-lang/rust/labels?q=beta [I-\*-nominated]: https://github.com/rust-lang/rust/labels?q=nominated diff --git a/src/doc/rustc-dev-guide/src/conventions.md b/src/doc/rustc-dev-guide/src/conventions.md index 37af8121cd1dd..0e624a4566d2a 100644 --- a/src/doc/rustc-dev-guide/src/conventions.md +++ b/src/doc/rustc-dev-guide/src/conventions.md @@ -1,4 +1,4 @@ -This file offers some tips on the coding conventions for rustc. This +This file offers some tips on the coding conventions for rustc. This chapter covers [formatting](#formatting), [coding for correctness](#cc), [using crates from crates.io](#cio), and some tips on [structuring your PR for easy review](#er). @@ -25,6 +25,7 @@ pass the `--edition=2021` argument yourself when c `rustfmt` directly. [fmt]: https://github.com/rust-dev-tools/fmt-rfcs + [`rustfmt`]:https://github.com/rust-lang/rustfmt ## Formatting C++ code @@ -40,6 +41,26 @@ When modifying that code, use this command to format it: This uses a pinned version of `clang-format`, to avoid relying on the local environment. +## Formatting and linting Python code + +The Rust repository contains quite a lot of Python code. We try to keep +it both linted and formatted by the [ruff][ruff] tool. + +When modifying Python code, use this command to format it: +```sh +./x test tidy --extra-checks=py:fmt --bless +``` + +and the following command to run lints: +```sh +./x test tidy --extra-checks=py:lint +``` + +This uses a pinned version of `ruff`, to avoid relying on the local +environment. + +[ruff]: https://github.com/astral-sh/ruff + @@ -84,7 +105,7 @@ Using `_` in a match is convenient, but it means that when new variants are added to the enum, they may not get handled correctly. Ask yourself: if a new variant were added to this enum, what's the chance that it would want to use the `_` code, versus having some -other treatment? Unless the answer is "low", then prefer an +other treatment? Unless the answer is "low", then prefer an exhaustive match. (The same advice applies to `if let` and `while let`, which are effectively tests for a single variant.) @@ -124,7 +145,7 @@ See the [crates.io dependencies][crates] section. # How to structure your PR How you prepare the commits in your PR can make a big difference for the -reviewer. Here are some tips. +reviewer. Here are some tips. **Isolate "pure refactorings" into their own commit.** For example, if you rename a method, then put that rename into its own commit, along @@ -165,4 +186,5 @@ to the compiler. crate-related, often the spelling is changed to `krate`. [tcx]: ./ty.md + [crates]: ./crates-io.md diff --git a/src/doc/rustc-dev-guide/src/diagnostics.md b/src/doc/rustc-dev-guide/src/diagnostics.md index ccd1011659d58..972309b5cd343 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics.md +++ b/src/doc/rustc-dev-guide/src/diagnostics.md @@ -111,7 +111,7 @@ Here are a few examples: their crate, making this a hard error would make refactoring and development very painful. - [future-incompatible lints]: - these are silencable lints. + these are silenceable lints. It was decided that making them fixed errors would cause too much breakage, so warnings are instead emitted, and will eventually be turned into fixed (hard) errors. @@ -601,8 +601,8 @@ The trait implementation allows you to check certain syntactic constructs as the linter walks the AST. You can then choose to emit lints in a very similar way to compile errors. -You also declare the metadata of a particular lint via the `declare_lint!` -macro. This includes the name, the default level, a short description, and some +You also declare the metadata of a particular lint via the [`declare_lint!`] +macro. This macro includes the name, the default level, a short description, and some more details. Note that the lint and the lint pass must be registered with the compiler. @@ -671,6 +671,8 @@ example-use-loop = denote infinite loops with `loop {"{"} ... {"}"}` .suggestion = use `loop` ``` +[`declare_lint!`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/macro.declare_lint.html + ### Edition-gated lints Sometimes we want to change the behavior of a lint in a new edition. To do this, diff --git a/src/doc/rustc-dev-guide/src/diagnostics/lintstore.md b/src/doc/rustc-dev-guide/src/diagnostics/lintstore.md index bd2b02529456c..7b98bc62116b4 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics/lintstore.md +++ b/src/doc/rustc-dev-guide/src/diagnostics/lintstore.md @@ -54,7 +54,7 @@ Lints are registered via the [`LintStore::register_lint`] function. This should happen just once for any lint, or an ICE will occur. Once the registration is complete, we "freeze" the lint store by placing it in -an `Lrc`. +an `Arc`. Lint passes are registered separately into one of the categories (pre-expansion, early, late, late module). Passes are registered as a closure diff --git a/src/doc/rustc-dev-guide/src/early-late-bound-params/early-late-bound-implementation-nuances.md b/src/doc/rustc-dev-guide/src/early-late-bound-params/early-late-bound-implementation-nuances.md deleted file mode 100644 index 01569e8dc6a01..0000000000000 --- a/src/doc/rustc-dev-guide/src/early-late-bound-params/early-late-bound-implementation-nuances.md +++ /dev/null @@ -1,197 +0,0 @@ -# Early and Late Bound Parameter Implementation Nuances - -> Note: this chapter makes reference to information discussed later on in the [representing types][ch_representing_types] chapter. Specifically, it uses concise notation to represent some more complex kinds of types that have not yet been discussed, such as inference variables. - -[ch_representing_types]: ../ty.md - -Understanding this page likely requires a rudimentary understanding of higher ranked -trait bounds/`for<'a>`and also what types such as `dyn for<'a> Trait<'a>` and - `for<'a> fn(&'a u32)` mean. Reading [the nomincon chapter](https://doc.rust-lang.org/nomicon/hrtb.html) -on HRTB may be useful for understanding this syntax. The meaning of `for<'a> fn(&'a u32)` -is incredibly similar to the meaning of `T: for<'a> Trait<'a>`. - -## What does it mean for parameters to be early or late bound - -All function definitions conceptually have a ZST (this is represented by `TyKind::FnDef` in rustc). -The only generics on this ZST are the early bound parameters of the function definition. e.g. -```rust -fn foo<'a>(_: &'a u32) {} - -fn main() { - let b = foo; - // ^ `b` has type `FnDef(foo, [])` (no args because `'a` is late bound) - assert!(std::mem::size_of_val(&b) == 0); -} -``` - -In order to call `b` the late bound parameters do need to be provided, these are inferred at the -call site instead of when we refer to `foo`. -```rust -fn main() { - let b = foo; - let a: &'static u32 = &10; - foo(a); - // the lifetime argument for `'a` on `foo` is inferred at the callsite - // the generic parameter `'a` on `foo` is inferred to `'static` here -} -``` - -Because late bound parameters are not part of the `FnDef`'s args this allows us to prove trait -bounds such as `F: for<'a> Fn(&'a u32)` where `F` is `foo`'s `FnDef`. e.g. -```rust -fn foo_early<'a, T: Trait<'a>>(_: &'a u32, _: T) {} -fn foo_late<'a, T>(_: &'a u32, _: T) {} - -fn accepts_hr_func Fn(&'a u32, u32)>(_: F) {} - -fn main() { - // doesn't work, the instantiated bound is `for<'a> FnDef<'?0>: Fn(&'a u32, u32)` - // `foo_early` only implements `for<'a> FnDef<'a>: Fn(&'a u32, u32)`- the lifetime - // of the borrow in the function argument must be the same as the lifetime - // on the `FnDef`. - accepts_hr_func(foo_early); - - // works, the instantiated bound is `for<'a> FnDef: Fn(&'a u32, u32)` - accepts_hr_func(foo_late); -} - -// the builtin `Fn` impls for `foo_early` and `foo_late` look something like: -// `foo_early` -impl<'a, T: Trait<'a>> Fn(&'a u32, T) for FooEarlyFnDef<'a, T> { ... } -// `foo_late` -impl<'a, T> Fn(&'a u32, T) for FooLateFnDef { ... } - -``` - -Early bound parameters are present on the `FnDef`. Late bound generic parameters are not present -on the `FnDef` but are instead constrained by the builtin `Fn*` impl. - -The same distinction applies to closures. Instead of `FnDef` we are talking about the anonymous -closure type. Closures are [currently unsound](https://github.com/rust-lang/rust/issues/84366) in -ways that are closely related to the distinction between early/late bound -parameters (more on this later) - -The early/late boundness of generic parameters is only relevant for the desugaring of -functions/closures into types with builtin `Fn*` impls. It does not make sense to talk about -in other contexts. - -The `generics_of` query in rustc only contains early bound parameters. In this way it acts more -like `generics_of(my_func)` is the generics for the FnDef than the generics provided to the function -body although it's not clear to the author of this section if this was the actual justification for -making `generics_of` behave this way. - -## What parameters are currently late bound - -Below are the current requirements for determining if a generic parameter is late bound. It is worth -keeping in mind that these are not necessarily set in stone and it is almost certainly possible to -be more flexible. - -### Must be a lifetime parameter - -Rust can't support types such as `for dyn Trait` or `for fn(T)`, this is a -fundamental limitation of the language as we are required to monomorphize type/const -parameters and cannot do so behind dynamic dispatch. (technically we could probably -support `for dyn MarkerTrait` as there is nothing to monomorphize) - -Not being able to support `for dyn Trait` resulted in making all type and const -parameters early bound. Only lifetime parameters can be late bound. - -### Must not appear in the where clauses - -In order for a generic parameter to be late bound it must not appear in any where clauses. -This is currently an incredibly simplistic check that causes lifetimes to be early bound even -if the where clause they appear in are always true, or implied by well formedness of function -arguments. e.g. -```rust -fn foo1<'a: 'a>(_: &'a u32) {} -// ^^ early bound parameter because it's in a `'a: 'a` clause -// even though the bound obviously holds all the time -fn foo2<'a, T: Trait<'a>(a: T, b: &'a u32) {} -// ^^ early bound parameter because it's used in the `T: Trait<'a>` clause -fn foo3<'a, T: 'a>(_: &'a T) {} -// ^^ early bound parameter because it's used in the `T: 'a` clause -// even though that bound is implied by wellformedness of `&'a T` -fn foo4<'a, 'b: 'a>(_: Inv<&'a ()>, _: Inv<&'b ()>) {} -// ^^ ^^ ^^^ note: -// ^^ ^^ `Inv` stands for `Invariant` and is used to -// ^^ ^^ make the type parameter invariant. This -// ^^ ^^ is necessary for demonstration purposes as -// ^^ ^^ `for<'a, 'b> fn(&'a (), &'b ())` and -// ^^ ^^ `for<'a> fn(&'a u32, &'a u32)` are subtypes- -// ^^ ^^ of each other which makes the bound trivially -// ^^ ^^ satisfiable when making the fnptr. `Inv` -// ^^ ^^ disables this subtyping. -// ^^ ^^ -// ^^^^^^ both early bound parameters because they are present in the -// `'b: 'a` clause -``` - -The reason for this requirement is that we cannot represent the `T: Trait<'a>` or `'a: 'b` clauses -on a function pointer. `for<'a, 'b> fn(Inv<&'a ()>, Inv<&'b ()>)` is not a valid function pointer to -represent`foo4` as it would allow calling the function without `'b: 'a` holding. - -### Must be constrained by where clauses or function argument types - -The builtin impls of the `Fn*` traits for closures and `FnDef`s cannot not have any unconstrained -parameters. For example the following impl is illegal: -```rust -impl<'a> Trait for u32 { type Assoc = &'a u32; } -``` -We must not end up with a similar impl for the `Fn*` traits e.g. -```rust -impl<'a> Fn<()> for FnDef { type Assoc = &'a u32 } -``` - -Violating this rule can trivially lead to unsoundness as seen in [#84366](https://github.com/rust-lang/rust/issues/84366). -Additionally if we ever support late bound type params then an impl like: -```rust -impl Fn<()> for FnDef { type Assoc = T; } -``` -would break the compiler in various ways. - -In order to ensure that everything functions correctly, we do not allow generic parameters to -be late bound if it would result in a builtin impl that does not constrain all of the generic -parameters on the builtin impl. Making a generic parameter be early bound trivially makes it be -constrained by the builtin impl as it ends up on the self type. - -Because of the requirement that late bound parameters must not appear in where clauses, checking -this is simpler than the rules for checking impl headers constrain all the parameters on the impl. -We only have to ensure that all late bound parameters appear at least once in the function argument -types outside of an alias (e.g. an associated type). - -The requirement that they not indirectly be in the args of an alias for it to count is the -same as why the follow code is forbidden: -```rust -impl OtherTrait for ::Assoc { type Assoc = T } -``` -There is no guarantee that `::Assoc` will normalize to different types for every -instantiation of `T`. If we were to allow this impl we could get overlapping impls and the -same is true of the builtin `Fn*` impls. - -## Making more generic parameters late bound - -It is generally considered desirable for more parameters to be late bound as it makes -the builtin `Fn*` impls more flexible. Right now many of the requirements for making -a parameter late bound are overly restrictive as they are tied to what we can currently -(or can ever) do with fn ptrs. - -It would be theoretically possible to support late bound params in `where`-clauses in the -language by introducing implication types which would allow us to express types such as: -`for<'a, 'b: 'a> fn(Inv<&'a u32>, Inv<&'b u32>)` which would ensure `'b: 'a` is upheld when -calling the function pointer. - -It would also be theoretically possible to support it by making the coercion to a fn ptr -instantiate the parameter with an infer var while still allowing the FnDef to not have the -generic parameter present as trait impls are perfectly capable of representing the where clauses -on the function on the impl itself. This would also allow us to support late bound type/const -vars allowing bounds like `F: for Fn(T)` to hold. - -It is almost somewhat unclear if we can change the `Fn` traits to be structured differently -so that we never have to make a parameter early bound just to make the builtin impl have all -generics be constrained. Of all the possible causes of a generic parameter being early bound -this seems the most difficult to remove. - -Whether these would be good ideas to implement is a separate question- they are only brought -up to illustrate that the current rules are not necessarily set in stone and a result of -"its the only way of doing this". - diff --git a/src/doc/rustc-dev-guide/src/early-late-bound-params/turbofishing-and-early-late-bound.md b/src/doc/rustc-dev-guide/src/early-late-bound-params/turbofishing-and-early-late-bound.md deleted file mode 100644 index 6fc30c6767d24..0000000000000 --- a/src/doc/rustc-dev-guide/src/early-late-bound-params/turbofishing-and-early-late-bound.md +++ /dev/null @@ -1,125 +0,0 @@ -# Turbofishing's interactions with early/late bound parameters - -> Note: this chapter makes reference to information discussed later on in the [representing types][ch_representing_types] chapter. Specifically, it uses concise notation to represent some more complex kinds of types that have not yet been discussed, such as inference variables. - -[ch_representing_types]: ../ty.md - -The early/late bound parameter distinction on functions introduces some complications -when providing generic arguments to functions. This document discusses what those are -and how they might interact with future changes to make more things late bound. - -## Can't turbofish generic arguments on functions sometimes - -When a function has any late bound lifetime parameters (be they explicitly defined or -implicitly introduced via lifetime elision) we disallow specifying any lifetime arguments -on the function. Sometimes this is a hard error other times it is a future compat lint -([`late_bound_lifetime_arguments`](https://github.com/rust-lang/rust/issues/42868)). - -```rust -fn early<'a: 'a>(a: &'a ()) -> &'a () { a } -fn late<'a>(a: &'a ()) -> &'a () { a } - -fn mixed<'a, 'b: 'b>(a: &'a (), b: &'b ()) -> &'a () { a } - -struct Foo; -impl Foo { - fn late<'a>(self, a: &'a ()) -> &'a () { a } -} - -fn main() { - // fine - let f = early::<'static>; - - // some variation of hard errors and future compat lints - Foo.late::<'static>(&()); - let f = late::<'static>; - let f = mixed::<'static, 'static>; - let f = mixed::<'static>; - late::<'static>(&()); -} -``` - -The justification for this is that late bound parameters are not present on the -`FnDef` so the arguments to late bound parameters can't be present in the generic arguments -for the type. i.e. the `late` function in the above code snippet would not have -any generic parameters on the `FnDef` zst: -```rust -// example desugaring of the `late` function and its zst + builtin Fn impl -struct LateFnDef; -impl<'a> Fn<(&'a ())> for LateFnDef { - type Output = &'a (); - ... -} -``` - -The cause for some situations giving future compat lints and others giving hard errors -is a little arbitrary but explainable: -- It's always a hard error for method calls -- It's only a hard error on paths to free functions if there is no unambiguous way to -create the generic arguments for the fndef from the lifetime arguments. (i.e. the amount of -lifetimes provided must be exactly equal to the amount of early bound lifetimes or -else it's a hard error) - -## Back compat issues from turning early bound to late bound - -Because of the previously mentioned restriction on turbofishing generic arguments, it -is a breaking change to upgrade a lifetime from early bound to late bound as it can cause -existing turbofishies to become hard errors/future compat lints. - -Many t-types members have expressed interest in wanting more parameters to be late bound. -We cannot do so if making something late bound is going to break code that many would -expect to work (judging by the future compat lint issue many people do expect to be able -to turbofish late bound parameters). - -## Interactions with late bound type/const parameters - -If we were to make some type/const parameters late bound we would definitely not want -to disallow turbofishing them as it presumably(?) would break a Tonne of code. - -While lifetimes do differ from type/consts in some ways I(BoxyUwU) do not believe there -is any justification for why it would make sense to allow turbofishing late bound -type/const parameters but not late bound lifetimes. - -## Removing the hard error/fcw - -From reasons above it seems reasonable that we may want to remove the hard error and fcw -(removing the errors/fcw is definitely a blocker for making more things late bound). - -example behaviour: -```rust -fn late<'a>(a: &'a ()) -> &'a () { a } - -fn accepts_fn(_: impl for<'a> Fn(&'a ()) -> &'a ()) {} -fn accepts_fn_2(_: impl Fn(&'static ()) -> &'static ()) {} - -fn main() { - let f = late::<'static>; - - accepts_fn(f); //~ error: `f` doesn't implement `for<'a> Fn(&'a ()) -> &'a ()` - accepts_fn_2(f) // works - - accepts_fn(late) // works -} -```` - -one potential complication is that we would want a way to specify a generic argument -to a function without having to specify arguments for all previous parameters. i.e. -ideally you could write the following code somehow. -```rust -fn late<'a, 'b>(_: &'a (), _: &'b ()) {} - -fn accepts_fn(_: impl for<'a> Fn(&'a (), &'static ())) {} - -fn main() { - // a naive implementation would have an inference variable as - // the argument to the `'a` parameter no longer allowing the `FnDef` - // to satisfy the bound `for<'a> Fn(&'a ())` - let f = late::<'_, 'static>; - accepts_fn(f); -} -``` -Maybe we can just special case HIR ty lowering for `_`/`'_` arguments for late bound -parameters somehow and have it not mean the same thing as `_` for early bound parameters. -Regardless I think we would need a solution that would allow writing the above code even -if it was done by some new syntax such as having to write `late::` -(naturally `k#no_argument` would only make sense as an argument to late bound parameters). diff --git a/src/doc/rustc-dev-guide/src/early_late_parameters.md b/src/doc/rustc-dev-guide/src/early_late_parameters.md new file mode 100644 index 0000000000000..3b2a5e8a155b6 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/early_late_parameters.md @@ -0,0 +1,424 @@ + +# Early vs Late bound parameters + + + +> **NOTE**: This chapter largely talks about early/late bound as being solely relevant when discussing function item types/function definitions. This is potentially not completely true, async blocks and closures should likely be discussed somewhat in this chapter. + +## What does it mean to be "early" bound or "late" bound + +Every function definition has a corresponding ZST that implements the `Fn*` traits known as a [function item type][function_item_type]. This part of the chapter will talk a little bit about the "desugaring" of function item types as it is useful context for explaining the difference between early bound and late bound generic parameters. + +Let's start with a very trivial example involving no generic parameters: + +```rust +fn foo(a: String) -> u8 { + # 1 + /* snip */ +} +``` + +If we explicitly wrote out the definitions for the function item type corresponding to `foo` and its associated `Fn` impl it would look something like this: +```rust,ignore +struct FooFnItem; + +impl Fn<(String,)> for FooFnItem { + type Output = u8; + /* fn call(&self, ...) -> ... { ... } */ +} +``` + +The builtin impls for the `FnMut`/`FnOnce` traits as well as the impls for `Copy` and `Clone` were omitted for brevity reasons (although these traits *are* implemented for function item types). + +A slightly more complicated example would involve introducing generic parameters to the function: +```rust +fn foo(a: T) -> T { + # a + /* snip */ +} +``` +Writing out the definitions would look something like this: +```rust,ignore +struct FooFnItem(PhantomData T>); + +impl Fn<(T,)> for FooFnItem { + type Output = T; + /* fn call(&self, ...) -> ... { ... } */ +} +``` + +Note that the function item type `FooFnItem` is generic over some type parameter `T` as defined on the function `foo`. However, not all generic parameters defined on functions are also defined on the function item type as demonstrated here: +```rust +fn foo<'a, T: Sized>(a: &'a T) -> &'a T { + # a + /* snip */ +} +``` +With its "desugared" form looking like so: +```rust,ignore +struct FooFnItem(PhantomData fn(&'a T) -> &'a T>); + +impl<'a, T: Sized> Fn<(&'a T,)> for FooFnItem { + type Output = &'a T; + /* fn call(&self, ...) -> ... { ... } */ +} +``` + +The lifetime parameter `'a` from the function `foo` is not present on the function item type `FooFnItem` and is instead introduced on the builtin impl solely for use in representing the argument types. + +Generic parameters not all being defined on the function item type means that there are two steps where generic arguments are provided when calling a function. +1. Naming the function (e.g. `let a = foo;`) the arguments for `FooFnItem` are provided. +2. Calling the function (e.g. `a(&10);`) any parameters defined on the builtin impl are provided. + +This two-step system is where the early vs late naming scheme comes from, early bound parameters are provided in the *earliest* step (naming the function), whereas late bound parameters are provided in the *latest* step (calling the function). + +Looking at the desugaring from the previous example we can tell that `T` is an early bound type parameter and `'a` is a late bound lifetime parameter as `T` is present on the function item type but `'a` is not. See this example of calling `foo` annotated with where each generic parameter has an argument provided: +```rust +fn foo<'a, T: Sized>(a: &'a T) -> &'a T { + # a + /* snip */ +} + +// Here we provide a type argument `String` to the +// type parameter `T` on the function item type +let my_func = foo::; + +// Here (implicitly) a lifetime argument is provided +// to the lifetime parameter `'a` on the builtin impl. +my_func(&String::new()); +``` + +[function_item_type]: https://doc.rust-lang.org/reference/types/function-item.html + +## Differences between early and late bound parameters + +### Higher ranked function pointers and trait bounds + +A generic parameter being late bound allows for more flexible usage of the function item. For example if we have some function `foo` with an early bound lifetime parameter and some function `bar` with a late bound lifetime parameter `'a` we would have the following builtin `Fn` impls: +```rust,ignore +impl<'a> Fn<(&'a String,)> for FooFnItem<'a> { /* ... */ } +impl<'a> Fn<(&'a String,)> for BarFnItem { /* ... */ } +``` + +The `bar` function has a strictly more flexible signature as the function item type can be called with a borrow with *any* lifetime, whereas the `foo` function item type would only be callable with a borrow with the same lifetime on the function item type. We can show this by simply trying to call `foo`'s function item type multiple times with different lifetimes: + +```rust +// The `'a: 'a` bound forces this lifetime to be early bound. +fn foo<'a: 'a>(b: &'a String) -> &'a String { b } +fn bar<'a>(b: &'a String) -> &'a String { b } + +// Early bound generic parameters are instantiated here when naming +// the function `foo`. As `'a` is early bound an argument is provided. +let f = foo::<'_>; + +// Both function arguments are required to have the same lifetime as +// the lifetime parameter being early bound means that `f` is only +// callable for one specific lifetime. +// +// As we call this with borrows of different lifetimes, the borrow checker +// will error here. +f(&String::new()); +f(&String::new()); +``` + +In this example we call `foo`'s function item type twice, each time with a borrow of a temporary. These two borrows could not possible have lifetimes that overlap as the temporaries are only alive during the function call, not after. The lifetime parameter on `foo` being early bound requires all callers of `f` to provide a borrow with the same lifetime, as this is not possible the borrow checker errors. + +If the lifetime parameter on `foo` was late bound this would be able to compile as each caller could provide a different lifetime argument for its borrow. See the following example which demonstrates this using the `bar` function defined above: + +```rust +# fn foo<'a: 'a>(b: &'a String) -> &'a String { b } +# fn bar<'a>(b: &'a String) -> &'a String { b } +# +// Early bound parameters are instantiated here, however as `'a` is +// late bound it is not provided here. +let b = bar; + +// Late bound parameters are instantiated separately at each call site +// allowing different lifetimes to be used by each caller. +b(&String::new()); +b(&String::new()); +``` + +This is reflected in the ability to coerce function item types to higher ranked function pointers and prove higher ranked `Fn` trait bounds. We can demonstrate this with the following example: +```rust +// The `'a: 'a` bound forces this lifetime to be early bound. +fn foo<'a: 'a>(b: &'a String) -> &'a String { b } +fn bar<'a>(b: &'a String) -> &'a String { b } + +fn accepts_hr_fn(_: impl for<'a> Fn(&'a String) -> &'a String) {} + +fn higher_ranked_trait_bound() { + let bar_fn_item = bar; + accepts_hr_fn(bar_fn_item); + + let foo_fn_item = foo::<'_>; + // errors + accepts_hr_fn(foo_fn_item); +} + +fn higher_ranked_fn_ptr() { + let bar_fn_item = bar; + let fn_ptr: for<'a> fn(&'a String) -> &'a String = bar_fn_item; + + let foo_fn_item = foo::<'_>; + // errors + let fn_ptr: for<'a> fn(&'a String) -> &'a String = foo_fn_item; +} +``` + +In both of these cases the borrow checker errors as it does not consider `foo_fn_item` to be callable with a borrow of any lifetime. This is due to the fact that the lifetime parameter on `foo` is early bound, causing `foo_fn_item` to have a type of `FooFnItem<'_>` which (as demonstrated by the desugared `Fn` impl) is only callable with a borrow of the same lifetime `'_`. + +### Turbofishing in the presence of late bound parameters + +As mentioned previously, the distinction between early and late bound parameters means that there are two places where generic parameters are instantiated: +- When naming a function (early) +- When calling a function (late) + +There currently is no syntax for explicitly specifying generic arguments for late bound parameters as part of the call step, only specifying generic arguments when naming a function. The syntax `foo::<'static>();`, despite being part of a function call, behaves as `(foo::<'static>)();` and instantiates the early bound generic parameters on the function item type. + +See the following example: +```rust +fn foo<'a>(b: &'a u32) -> &'a u32 { b } + +let f /* : FooFnItem */ = foo::<'static>; +``` + +The above example errors as the lifetime parameter `'a` is late bound and so cannot be instantiated as part of the "naming a function" step. If we make the lifetime parameter early bound we will see this code start to compile: +```rust +fn foo<'a: 'a>(b: &'a u32) -> &'a u32 { b } + +let f /* : FooFnItem<'static> */ = foo::<'static>; +``` + +What the current implementation of the compiler aims to do is error when specifying lifetime arguments to a function that has both early *and* late bound lifetime parameters. In practice, due to excessive breakage, some cases are actually only future compatibility warnings ([#42868](https://github.com/rust-lang/rust/issues/42868)): +- When the amount of lifetime arguments is the same as the number of early bound lifetime parameters a FCW is emitted instead of an error +- An error is always downgraded to a FCW when using method call syntax + +To demonstrate this we can write out the different kinds of functions and give them both a late and early bound lifetime: +```rust,ignore +fn free_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} + +struct Foo; + +trait Trait: Sized { + fn trait_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()); + fn trait_function<'a: 'a, 'b>(_: &'a (), _: &'b ()); +} + +impl Trait for Foo { + fn trait_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()) {} + fn trait_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} +} + +impl Foo { + fn inherent_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()) {} + fn inherent_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} +} +``` + +Then, for the first case, we can call each function with a single lifetime argument (corresponding to the one early bound lifetime parameter) and note that it only results in a FCW rather than a hard error. +```rust +#![deny(late_bound_lifetime_arguments)] + +# fn free_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} +# +# struct Foo; +# +# trait Trait: Sized { +# fn trait_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()); +# fn trait_function<'a: 'a, 'b>(_: &'a (), _: &'b ()); +# } +# +# impl Trait for Foo { +# fn trait_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()) {} +# fn trait_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} +# } +# +# impl Foo { +# fn inherent_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()) {} +# fn inherent_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} +# } +# +// Specifying as many arguments as there are early +// bound parameters is always a future compat warning +Foo.trait_method::<'static>(&(), &()); +Foo::trait_method::<'static>(Foo, &(), &()); +Foo::trait_function::<'static>(&(), &()); +Foo.inherent_method::<'static>(&(), &()); +Foo::inherent_function::<'static>(&(), &()); +free_function::<'static>(&(), &()); +``` + +For the second case we call each function with more lifetime arguments than there are lifetime parameters (be it early or late bound) and note that method calls result in a FCW as opposed to the free/associated functions which result in a hard error: +```rust +# fn free_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} +# +# struct Foo; +# +# trait Trait: Sized { +# fn trait_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()); +# fn trait_function<'a: 'a, 'b>(_: &'a (), _: &'b ()); +# } +# +# impl Trait for Foo { +# fn trait_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()) {} +# fn trait_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} +# } +# +# impl Foo { +# fn inherent_method<'a: 'a, 'b>(self, _: &'a (), _: &'b ()) {} +# fn inherent_function<'a: 'a, 'b>(_: &'a (), _: &'b ()) {} +# } +# +// Specifying more arguments than there are early +// bound parameters is a future compat warning when +// using method call syntax. +Foo.trait_method::<'static, 'static, 'static>(&(), &()); +Foo.inherent_method::<'static, 'static, 'static>(&(), &()); +// However, it is a hard error when not using method call syntax. +Foo::trait_method::<'static, 'static, 'static>(Foo, &(), &()); +Foo::trait_function::<'static, 'static, 'static>(&(), &()); +Foo::inherent_function::<'static, 'static, 'static>(&(), &()); +free_function::<'static, 'static, 'static>(&(), &()); +``` + +Even when specifying enough lifetime arguments for both the late and early bound lifetime parameter, these arguments are not actually used to annotate the lifetime provided to late bound parameters. We can demonstrate this by turbofishing `'static` to a function while providing a non-static borrow: +```rust +struct Foo; + +impl Foo { + fn inherent_method<'a: 'a, 'b>(self, _: &'a (), _: &'b String ) {} +} + +Foo.inherent_method::<'static, 'static>(&(), &String::new()); +``` + +This compiles even though the `&String::new()` function argument does not have a `'static` lifetime, this is because "extra" lifetime arguments are discarded rather than taken into account for late bound parameters when actually calling the function. + +### Liveness of types with late bound parameters + +When checking type outlives bounds involving function item types we take into account early bound parameters. For example: + +```rust +fn foo(_: T) {} + +fn requires_static(_: T) {} + +fn bar() { + let f /* : FooFnItem */ = foo::; + requires_static(f); +} +``` + +As the type parameter `T` is early bound, the desugaring of the function item type for `foo` would look something like `struct FooFnItem`. Then in order for `FooFnItem: 'static` to hold we must also require `T: 'static` to hold as otherwise we would wind up with soundness bugs. + +Unfortunately, due to bugs in the compiler, we do not take into account early bound *lifetimes*, which is the cause of the open soundness bug [#84366](https://github.com/rust-lang/rust/issues/84366). This means that it's impossible to demonstrate a "difference" between early/late bound parameters for liveness/type outlives bounds as the only kind of generic parameters that are able to be late bound are lifetimes which are handled incorrectly. + +Regardless, in theory the code example below *should* demonstrate such a difference once [#84366](https://github.com/rust-lang/rust/issues/84366) is fixed: +```rust +fn early_bound<'a: 'a>(_: &'a String) {} +fn late_bound<'a>(_: &'a String) {} + +fn requires_static(_: T) {} + +fn bar<'b>() { + let e = early_bound::<'b>; + // this *should* error but does not + requires_static(e); + + let l = late_bound; + // this correctly does not error + requires_static(l); +} +``` + +## Requirements for a parameter to be late bound + +### Must be a lifetime parameter + +Type and Const parameters are not able to be late bound as we do not have a way to support types such as `dyn for Fn(Box)` or `for fn(Box)`. Calling such types requires being able to monomorphize the underlying function which is not possible with indirection through dynamic dispatch. + +### Must not be used in a where clause + +Currently when a generic parameter is used in a where clause it must be early bound. For example: +```rust +# trait Trait<'a> {} +fn foo<'a, T: Trait<'a>>(_: &'a String, _: T) {} +``` + +In this example the lifetime parameter `'a` is considered to be early bound as it appears in the where clause `T: Trait<'a>`. This is true even for "trivial" where clauses such as `'a: 'a` or those implied by wellformedness of function arguments, for example: +```rust +fn foo<'a: 'a>(_: &'a String) {} +fn bar<'a, T: 'a>(_: &'a T) {} +``` + +In both of these functions the lifetime parameter `'a` would be considered to be early bound even though the where clauses they are used in arguably do not actually impose any constraints on the caller. + +The reason for this restriction is a combination of two things: +- We cannot prove bounds on late bound parameters until they have been instantiated +- Function pointers and trait objects do not have a way to represent yet to be proven where clauses from the underlying function + +Take the following example: +```rust +trait Trait<'a> {} +fn foo<'a, T: Trait<'a>>(_: &'a T) {} + +let f = foo::; +let f = f as for<'a> fn(&'a String); +f(&String::new()); +``` + +At *some point* during type checking an error should be emitted for this code as `String` does not implement `Trait` for any lifetime. + +If the lifetime `'a` were late bound then this becomes difficult to check. When naming `foo` we do not know what lifetime should be used as part of the `T: Trait<'a>` trait bound as it has not yet been instantiated. When coercing the function item type to a function pointer we have no way of tracking the `String: Trait<'a>` trait bound that must be proven when calling the function. + +If the lifetime `'a` is early bound (which it is in the current implementation in rustc), then the trait bound can be checked when naming the function `foo`. Requiring parameters used in where clauses to be early bound gives a natural place to check where clauses defined on the function. + +Finally, we do not require lifetimes to be early bound if they are used in *implied bounds*, for example: +```rust +fn foo<'a, T>(_: &'a T) {} + +let f = foo; +f(&String::new()); +f(&String::new()); +``` + +This code compiles, demonstrating that the lifetime parameter is late bound, even though `'a` is used in the type `&'a T` which implicitly requires `T: 'a` to hold. Implied bounds can be treated specially as any types introducing implied bounds are in the signature of the function pointer type, which means that when calling the function we know to prove `T: 'a`. + +### Must be constrained by argument types + +It is important that builtin impls on function item types do not wind up with unconstrained generic parameters as this can lead to unsoundness. This is the same kind of restriction as applies to user written impls, for example the following code results in an error: +```rust +trait Trait { + type Assoc; +} + +impl<'a> Trait for u8 { + type Assoc = &'a String; +} +``` + +The analogous example for builtin impls on function items would be the following: +```rust,ignore +fn foo<'a>() -> &'a String { /* ... */ } +``` +If the lifetime parameter `'a` were to be late bound we would wind up with a builtin impl with an unconstrained lifetime, we can manually write out the desugaring for the function item type and its impls with `'a` being late bound to demonstrate this: +```rust,ignore +// NOTE: this is just for demonstration, in practice `'a` is early bound +struct FooFnItem; + +impl<'a> Fn<()> for FooFnItem { + type Output = &'a String; + /* fn call(...) -> ... { ... } */ +} +``` + +In order to avoid such a situation we consider `'a` to be early bound which causes the lifetime on the impl to be constrained by the self type: +```rust,ignore +struct FooFnItem<'a>(PhantomData &'a String>); + +impl<'a> Fn<()> for FooFnItem<'a> { + type Output = &'a String; + /* fn call(...) -> ... { ... } */ +} +``` diff --git a/src/doc/rustc-dev-guide/src/external-repos.md b/src/doc/rustc-dev-guide/src/external-repos.md index 533f7eb5e7344..f3170c9222d88 100644 --- a/src/doc/rustc-dev-guide/src/external-repos.md +++ b/src/doc/rustc-dev-guide/src/external-repos.md @@ -3,24 +3,26 @@ The `rust-lang/rust` git repository depends on several other repos in the `rust-lang` organization. There are three main ways we use dependencies: 1. As a Cargo dependency through crates.io (e.g. `rustc-rayon`) -2. As a git subtree (e.g. `clippy`) +2. As a git (e.g. `clippy`) or a [josh] (e.g. `miri`) subtree 3. As a git submodule (e.g. `cargo`) -As a general rule, use crates.io for libraries that could be useful for others in the ecosystem; use -subtrees for tools that depend on compiler internals and need to be updated if there are breaking -changes; and use submodules for tools that are independent of the compiler. +As a general rule: +- Use crates.io for libraries that could be useful for others in the ecosystem +- Use subtrees for tools that depend on compiler internals and need to be updated if there are breaking +changes +- Use submodules for tools that are independent of the compiler -## External Dependencies (subtree) +## External Dependencies (subtrees) -As a developer to this repository, you don't have to treat the following external projects -differently from other crates that are directly in this repo: +The following external projects are managed using some form of a `subtree`: -* [Clippy](https://github.com/rust-lang/rust-clippy) -* [Miri] +* [clippy](https://github.com/rust-lang/rust-clippy) +* [miri](https://github.com/rust-lang/miri) +* [portable-simd](https://github.com/rust-lang/portable-simd) * [rustfmt](https://github.com/rust-lang/rustfmt) * [rust-analyzer](https://github.com/rust-lang/rust-analyzer) - -[Miri]: https://github.com/rust-lang/miri +* [rustc_codegen_cranelift](https://github.com/rust-lang/rustc_codegen_cranelift) +* [rustc-dev-guide](https://github.com/rust-lang/rustc-dev-guide) In contrast to `submodule` dependencies (see below for those), the `subtree` dependencies are just regular files and directories which can @@ -29,6 +31,22 @@ to these tools should be filed against the tools directly in their respective upstream repositories. The exception is that when rustc changes are required to implement a new tool feature or test, that should happen in one collective rustc PR. +`subtree` dependencies are currently managed by two distinct approaches: + +* Using `git subtree` + * `clippy` ([sync guide](https://doc.rust-lang.org/nightly/clippy/development/infrastructure/sync.html#performing-the-sync-from-rust-langrust-to-clippy)) + * `portable-simd` ([sync script](https://github.com/rust-lang/portable-simd/blob/master/subtree-sync.sh)) + * `rustfmt` + * `rustc_codegen_cranelift` ([sync script](https://github.com/rust-lang/rustc_codegen_cranelift/blob/113af154d459e41b3dc2c5d7d878e3d3a8f33c69/scripts/rustup.sh#L7)) +* Using the [josh] tool + * `miri` ([sync guide](https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#advanced-topic-syncing-with-the-rustc-repo)) + * `rust-analyzer` ([sync script](https://github.com/rust-lang/rust-analyzer/blob/2e13684be123eca7181aa48e043e185d8044a84a/xtask/src/release.rs#L147)) + * `rustc-dev-guide` ([sync guide](https://github.com/rust-lang/rustc-dev-guide#synchronizing-josh-subtree-with-rustc)) + +The [josh] tool is an alternative to git subtrees, which manages git history in a different way and scales better to larger repositories. Specific tooling is required to work with josh, you can check out the `miri` or `rust-analyzer` scripts linked above for inspiration. If you want to migrate a repository dependency from `git subtree` or `git submodule` to josh, you can check out [this guide](https://hackmd.io/7pOuxnkdQDaL1Y1FQr65xg). + +Below you can find a guide on how to perform push and pull synchronization with the main rustc repo using `git subtree`, although these instructions might differ repo from repo. + ### Synchronizing a subtree Periodically the changes made to subtree based dependencies need to be synchronized between this @@ -84,7 +102,6 @@ Now you're done, the `src/tools/clippy` directory behaves as if Clippy were part of the rustc monorepo, so no one but you (or others that synchronize subtrees) actually needs to use `git subtree`. - ## External Dependencies (submodules) Building Rust will also use external git repositories tracked using [git @@ -111,3 +128,4 @@ the week leading up to the beta cut. [The Rust Reference]: https://github.com/rust-lang/reference/ [toolstate website]: https://rust-lang-nursery.github.io/rust-toolstate/ [Toolstate chapter]: https://forge.rust-lang.org/infra/toolstate.html +[josh]: https://josh-project.github.io/josh/intro.html diff --git a/src/doc/rustc-dev-guide/src/generic_parameters_summary.md b/src/doc/rustc-dev-guide/src/generic_parameters_summary.md index 3403c9f2991a1..da38ba0455c2c 100644 --- a/src/doc/rustc-dev-guide/src/generic_parameters_summary.md +++ b/src/doc/rustc-dev-guide/src/generic_parameters_summary.md @@ -25,42 +25,4 @@ Interestingly, `ty::Generics` does not currently contain _every_ generic paramet [ch_representing_types]: ./ty.md [`ty::Generics`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Generics.html [`GenericParamDef`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/generics/struct.GenericParamDef.html - -# Early vs Late bound parameters - - -```rust -fn foo<'a, T>(b: &'a T) -> &'a T { b } -// ^^ ^early bound -// ^^ -// ^^late bound -``` - -Generally when referring to an item with generic parameters you must specify a list of generic arguments corresponding to the item's generic parameters. In some cases it is permitted to elide these arguments but still, implicitly, a set of arguments are provided (e.g. `Vec::default()` desugars to `Vec::<_>::default()`). - -For functions this is not necessarily the case, for example if we take the function `foo` from the example above and write the following code: -```rust -fn main() { - let f = foo::<_>; - - let b = String::new(); - let c = String::new(); - - f(&b); - drop(b); - f(&c); -} -``` - -This code compiles perfectly fine even though there is no single lifetime that could possibly be specified in `foo::<_>` that would allow for both -the `&b` and `&c` borrows to be used as arguments (note: the `drop(b)` line forces the `&b` borrow to be shorter than the `&c` borrow). This works because the `'a` lifetime is _late bound_. - -A generic parameter being late bound means that when we write `foo::<_>` we do not actually provide an argument for that parameter, instead we wait until _calling_ the function to provide the generic argument. In the above example this means that we are doing something like `f::<'_>(&b);` and `f::<'_>(&c);` (although in practice we do not actually support turbofishing late bound parameters in this manner) - -It may be helpful to think of "early bound parameter" or "late bound parameter" as meaning "early provided parameter" and "late provided parameter", i.e. we provide the argument to the parameter either early (when naming the function) or late (when calling it). - -Late bound parameters on functions are tracked with a [`Binder`] when accessing the signature of the function, this can be done with the [`fn_sig`] query. For more information of binders see the [chapter on `Binder`s ][ch_binders]. - -[`Binder`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/binder/struct.Binder.html -[`fn_sig`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.fn_sig -[ch_binders]: ./ty_module/binders.md \ No newline at end of file +[ch_binders]: ./ty_module/binders.md diff --git a/src/doc/rustc-dev-guide/src/getting-started.md b/src/doc/rustc-dev-guide/src/getting-started.md index 03d2811e8b108..8bf14bef2a033 100644 --- a/src/doc/rustc-dev-guide/src/getting-started.md +++ b/src/doc/rustc-dev-guide/src/getting-started.md @@ -101,7 +101,6 @@ it's easy to pick up work without a large time commitment: - [Rustdoc Askama Migration](https://github.com/rust-lang/rust/issues/108868) - [Diagnostic Translation](https://github.com/rust-lang/rust/issues/100717) - [Move UI tests to subdirectories](https://github.com/rust-lang/rust/issues/73494) -- [Port run-make tests from Make to Rust](https://github.com/rust-lang/rust/issues/121876) If you find more recurring work, please feel free to add it here! @@ -137,6 +136,10 @@ pull request, continuing the work on the feature. [abandoned-prs]: https://github.com/rust-lang/rust/pulls?q=is%3Apr+label%3AS-inactive+is%3Aclosed +### Writing tests + +Issues that have been resolved but do not have a regression test are marked with the `E-needs-test` label. Writing unit tests is a low-risk, lower-priority task that offers new contributors a great opportunity to familiarize themselves with the testing infrastructure and contribution workflow. + ### Contributing to std (standard library) See [std-dev-guide](https://std-dev-guide.rust-lang.org/). diff --git a/src/doc/rustc-dev-guide/src/guides/editions.md b/src/doc/rustc-dev-guide/src/guides/editions.md index 336e391df1267..ea207167791b7 100644 --- a/src/doc/rustc-dev-guide/src/guides/editions.md +++ b/src/doc/rustc-dev-guide/src/guides/editions.md @@ -91,7 +91,7 @@ stability. ## Edition parsing For the most part, the lexer is edition-agnostic. -Within [`StringReader`], tokens can be modified based on edition-specific behavior. +Within [`Lexer`], tokens can be modified based on edition-specific behavior. For example, C-String literals like `c"foo"` are split into multiple tokens in editions before 2021. This is also where things like reserved prefixes are handled for the 2021 edition. @@ -114,7 +114,7 @@ For example, the deprecated `start...end` pattern syntax emits the [`ellipsis_inclusive_range_patterns`] lint on editions before 2021, and in 2021 is an hard error via the `emit_err` method. -[`StringReader`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.StringReader.html +[`Lexer`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.Lexer.html [`ParseSess::edition`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/parse/struct.ParseSess.html#structfield.edition [`ellipsis_inclusive_range_patterns`]: https://doc.rust-lang.org/nightly/rustc/lints/listing/warn-by-default.html#ellipsis-inclusive-range-patterns diff --git a/src/doc/rustc-dev-guide/src/implementing_new_features.md b/src/doc/rustc-dev-guide/src/implementing_new_features.md index d87afeaedce6e..5b67ccd7f4c5e 100644 --- a/src/doc/rustc-dev-guide/src/implementing_new_features.md +++ b/src/doc/rustc-dev-guide/src/implementing_new_features.md @@ -9,7 +9,11 @@ smoothly. **NOTE: this section is for *language* features, not *library* features, which use [a different process].** +See also [the Rust Language Design Team's procedures][lang-propose] for +proposing changes to the language. + [a different process]: ./stability.md +[lang-propose]: https://lang-team.rust-lang.org/how_to/propose.html ## The @rfcbot FCP process diff --git a/src/doc/rustc-dev-guide/src/method-lookup.md b/src/doc/rustc-dev-guide/src/method-lookup.md index 8b49e8d004b17..c8d529a32b532 100644 --- a/src/doc/rustc-dev-guide/src/method-lookup.md +++ b/src/doc/rustc-dev-guide/src/method-lookup.md @@ -67,6 +67,7 @@ imported to use an inherent method, they are associated with the type itself (note that inherent impls can only be defined in the same crate as the type itself). + **Extension candidates** are derived from imported traits. If I have the trait `ToString` imported, and I call `to_string()` as a method, diff --git a/src/doc/rustc-dev-guide/src/mir/index.md b/src/doc/rustc-dev-guide/src/mir/index.md index 778c583919b5c..f355875aa156e 100644 --- a/src/doc/rustc-dev-guide/src/mir/index.md +++ b/src/doc/rustc-dev-guide/src/mir/index.md @@ -304,9 +304,9 @@ The most important rule for this representation is that every value must be uniquely represented. In other words: a specific value must only be representable in one specific way. For example: there is only one way to represent an array of two integers as a `ValTree`: -`ValTree::Branch(&[ValTree::Leaf(first_int), ValTree::Leaf(second_int)])`. +`Branch([Leaf(first_int), Leaf(second_int)])`. Even though theoretically a `[u32; 2]` could be encoded in a `u64` and thus just be a -`ValTree::Leaf(bits_of_two_u32)`, that is not a legal construction of `ValTree` +`Leaf(bits_of_two_u32)`, that is not a legal construction of `ValTree` (and is very complex to do, so it is unlikely anyone is tempted to do so). These rules also mean that some values are not representable. There can be no `union`s in type diff --git a/src/doc/rustc-dev-guide/src/opaque-types-impl-trait-inference.md b/src/doc/rustc-dev-guide/src/opaque-types-impl-trait-inference.md index c56d51a4bb716..bdf4e4cd870b8 100644 --- a/src/doc/rustc-dev-guide/src/opaque-types-impl-trait-inference.md +++ b/src/doc/rustc-dev-guide/src/opaque-types-impl-trait-inference.md @@ -78,7 +78,7 @@ If that fails, we reveal the hidden type of the opaque type, but only to prove this specific trait bound, not in general. Revealing is done by invoking the `type_of` query on the `DefId` of the opaque type. The query will internally request the hidden types from the defining function(s) -and return that (see [the section on `type_of`](#Within-the-type_of-query) for more details). +and return that (see [the section on `type_of`](#within-the-type_of-query) for more details). #### Flowchart of type checking steps diff --git a/src/doc/rustc-dev-guide/src/overview.md b/src/doc/rustc-dev-guide/src/overview.md index cc17eaa9e48c9..21ab0040646b4 100644 --- a/src/doc/rustc-dev-guide/src/overview.md +++ b/src/doc/rustc-dev-guide/src/overview.md @@ -38,7 +38,7 @@ Unicode character encoding. The token stream passes through a higher-level lexer located in [`rustc_parse`] to prepare for the next stage of the compile process. The -[`StringReader`] `struct` is used at this stage to perform a set of validations +[`Lexer`] `struct` is used at this stage to perform a set of validations and turn strings into interned symbols (_interning_ is discussed later). [String interning] is a way of storing only one immutable copy of each distinct string value. @@ -153,7 +153,7 @@ the final binary. [`rustc_parse::parser::Parser`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/parser/struct.Parser.html [`rustc_parse`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/index.html [`simplify_try`]: https://github.com/rust-lang/rust/pull/66282 -[`StringReader`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.StringReader.html +[`Lexer`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.Lexer.html [`Ty<'tcx>`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html [borrow checking]: borrow_check.md [codegen]: backend/codegen.md diff --git a/src/doc/rustc-dev-guide/src/parallel-rustc.md b/src/doc/rustc-dev-guide/src/parallel-rustc.md index 2dae95a3491d1..c5b70706a8180 100644 --- a/src/doc/rustc-dev-guide/src/parallel-rustc.md +++ b/src/doc/rustc-dev-guide/src/parallel-rustc.md @@ -46,9 +46,6 @@ are implemented differently depending on whether `parallel-compiler` is true. | data structure | parallel | non-parallel | | -------------------------------- | --------------------------------------------------- | ------------ | -| Lrc | std::sync::Arc | std::rc::Rc | -| Weak | std::sync::Weak | std::rc::Weak | -| Atomic{Bool}/{Usize}/{U32}/{U64} | std::sync::atomic::Atomic{Bool}/{Usize}/{U32}/{U64} | (std::cell::Cell) | | OnceCell | std::sync::OnceLock | std::cell::OnceCell | | Lock\ | (parking_lot::Mutex\) | (std::cell::RefCell) | | RwLock\ | (parking_lot::RwLock\) | (std::cell::RefCell) | @@ -59,7 +56,6 @@ are implemented differently depending on whether `parallel-compiler` is true. | WriteGuard | parking_lot::RwLockWriteGuard | std::cell::RefMut | | MappedWriteGuard | parking_lot::MappedRwLockWriteGuard | std::cell::RefMut | | LockGuard | parking_lot::MutexGuard | std::cell::RefMut | -| MappedLockGuard | parking_lot::MappedMutexGuard | std::cell::RefMut | - These thread-safe data structures are interspersed during compilation which can cause lock contention resulting in degraded performance as the number of @@ -174,12 +170,10 @@ Here are some resources that can be used to learn more: - [This list of interior mutability in the compiler by nikomatsakis][imlist] [`rayon`]: https://crates.io/crates/rayon -[Arc]: https://doc.rust-lang.org/std/sync/struct.Arc.html [imlist]: https://github.com/nikomatsakis/rustc-parallelization/blob/master/interior-mutability-list.md [irlo0]: https://internals.rust-lang.org/t/parallelizing-rustc-using-rayon/6606 [irlo1]: https://internals.rust-lang.org/t/help-test-parallel-rustc/11503 [monomorphization]: backend/monomorph.md [parallel-rustdoc]: https://github.com/rust-lang/rust/issues/82741 -[Rc]: https://doc.rust-lang.org/std/rc/struct.Rc.html [rustc-rayon]: https://github.com/rust-lang/rustc-rayon [tracking]: https://github.com/rust-lang/rust/issues/48685 diff --git a/src/doc/rustc-dev-guide/src/param_env/param_env_acquisition.md b/src/doc/rustc-dev-guide/src/param_env/param_env_acquisition.md index 391e562910fa7..f6cff2d6c63cc 100644 --- a/src/doc/rustc-dev-guide/src/param_env/param_env_acquisition.md +++ b/src/doc/rustc-dev-guide/src/param_env/param_env_acquisition.md @@ -21,7 +21,7 @@ Creating an env from an arbitrary set of where clauses is usually unnecessary an Creating an empty environment via `ParamEnv::empty` is almost always wrong. There are very few places where we actually know that the environment should be empty. One of the only places where we do actually know this is after monomorphization, however the `ParamEnv` there should be constructed via `ParamEnv::reveal_all` instead as at this point we should be able to determine the hidden type of opaque types. Codegen/Post-mono is one of the only places that should be using `ParamEnv::reveal_all`. -An additional piece of complexity here is specifying the [`Reveal`][reveal] (see linked docs for explanation of what reveal does) used for the `ParamEnv`. When constructing a param env using the `param_env` query it will have `Reveal::UserFacing`, if `Reveal::All` is desired then the [`tcx.param_env_reveal_all_normalized`][env_reveal_all_normalized] query can be used instead. +An additional piece of complexity here is specifying the `Reveal` (see linked docs for explanation of what reveal does) used for the `ParamEnv`. When constructing a param env using the `param_env` query it will have `Reveal::UserFacing`, if `Reveal::All` is desired then the [`tcx.param_env_reveal_all_normalized`][env_reveal_all_normalized] query can be used instead. The `ParamEnv` type has a method [`ParamEnv::with_reveal_all_normalized`][with_reveal_all] which converts an existing `ParamEnv` into one with `Reveal::All` specified. Where possible the previously mentioned query should be preferred as it is more efficient. @@ -38,7 +38,6 @@ The `ParamEnv` type has a method [`ParamEnv::with_reveal_all_normalized`][with_r [with_reveal_all]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html#method.with_reveal_all_normalized [env_reveal_all]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html#method.reveal_all [env_empty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html#method.empty -[reveal]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/traits/enum.Reveal.html [pe]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html [param_env_query]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn_ctxt/struct.FnCtxt.html#structfield.param_env [method_pred_entailment]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.compare_method_predicate_entailment.html diff --git a/src/doc/rustc-dev-guide/src/param_env/param_env_what_is_it.md b/src/doc/rustc-dev-guide/src/param_env/param_env_what_is_it.md index ca09518d99f4b..5c2f4d594052e 100644 --- a/src/doc/rustc-dev-guide/src/param_env/param_env_what_is_it.md +++ b/src/doc/rustc-dev-guide/src/param_env/param_env_what_is_it.md @@ -3,7 +3,7 @@ The type system relies on information in the environment in order for it to function correctly. This information is stored in the [`ParamEnv`][pe] type and it is important to use the correct `ParamEnv` when interacting with the type system. -The information represented by `ParamEnv` is a list of in-scope where-clauses, and a [`Reveal`][reveal] (see linked docs for more information). A `ParamEnv` typically corresponds to a specific item's where clauses, some clauses are not explicitly written bounds and instead are implicitly added in [`predicates_of`][predicates_of] such as `ConstArgHasType` or some implied bounds. +The information represented by `ParamEnv` is a list of in-scope where-clauses, and a `Reveal` (see linked docs for more information). A `ParamEnv` typically corresponds to a specific item's where clauses, some clauses are not explicitly written bounds and instead are implicitly added in [`predicates_of`][predicates_of] such as `ConstArgHasType` or some implied bounds. A `ParamEnv` can also be created with arbitrary data that is not derived from a specific item such as in [`compare_method_predicate_entailment`][method_pred_entailment] which creates a hybrid `ParamEnv` consisting of the impl's where clauses and the trait definition's function's where clauses. In most cases `ParamEnv`s are initially created via the [`param_env` query][query] which returns a `ParamEnv` derived from the provided item's where clauses. @@ -57,4 +57,3 @@ It's very important to use the correct `ParamEnv` when interacting with the type [method_pred_entailment]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.compare_method_predicate_entailment.html [pe]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html [query]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.param_env -[reveal]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/traits/enum.Reveal.html \ No newline at end of file diff --git a/src/doc/rustc-dev-guide/src/profiling/with_rustc_perf.md b/src/doc/rustc-dev-guide/src/profiling/with_rustc_perf.md index 87d205d9ebe06..eda8c3a179ca5 100644 --- a/src/doc/rustc-dev-guide/src/profiling/with_rustc_perf.md +++ b/src/doc/rustc-dev-guide/src/profiling/with_rustc_perf.md @@ -7,9 +7,7 @@ However, using the suite manually can be a bit cumbersome. To make this easier f the compiler build system (`bootstrap`) also provides built-in integration with the benchmarking suite, which will download and build the suite for you, build a local compiler toolchain and let you profile it using a simplified command-line interface. -You can use the `./x perf -- [options]` command to use this integration. - -> Note that you need to specify arguments after `--` in the `x perf` command! You will not be able to pass arguments without the double dashes. +You can use the `./x perf [options]` command to use this integration. You can use normal bootstrap flags for this command, such as `--stage 1` or `--stage 2`, for example to modify the stage of the created sysroot. It might also be useful to configure `config.toml` to better support profiling, e.g. set `rust.debuginfo-level = 1` to add source line information to the built compiler. diff --git a/src/doc/rustc-dev-guide/src/rustc-driver/intro.md b/src/doc/rustc-dev-guide/src/rustc-driver/intro.md index a6234dc129ffe..40500e6bc7a5f 100644 --- a/src/doc/rustc-dev-guide/src/rustc-driver/intro.md +++ b/src/doc/rustc-dev-guide/src/rustc-driver/intro.md @@ -6,7 +6,7 @@ The [`rustc_driver`] is essentially `rustc`'s `main` function. It acts as the glue for running the various phases of the compiler in the correct order, using the interface defined in the [`rustc_interface`] crate. Where possible, using [`rustc_driver`] rather than [`rustc_interface`] is recommended. -The main entry point of [`rustc_driver`] is [`rustc_driver::RunCompiler`][rd_rc]. +The main entry point of [`rustc_driver`] is [`rustc_driver::run_compiler`][rd_rc]. This builder accepts the same command-line args as rustc as well as an implementation of [`Callbacks`][cb] and a couple of other optional options. [`Callbacks`][cb] is a `trait` that allows for custom compiler configuration, as well as allowing custom code to run after different phases of the compilation. @@ -40,7 +40,7 @@ specifically [`rustc_driver_impl::run_compiler`][rdi_rc] [cb]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/trait.Callbacks.html [example]: https://github.com/rust-lang/rustc-dev-guide/blob/master/examples/rustc-interface-example.rs [i_rc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/interface/fn.run_compiler.html -[rd_rc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/struct.RunCompiler.html +[rd_rc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/fn.run_compiler.html [rdi_rc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver_impl/fn.run_compiler.html [stupid-stats]: https://github.com/nrc/stupid-stats [`nightly-rustc`]: https://doc.rust-lang.org/nightly/nightly-rustc/ diff --git a/src/doc/rustc-dev-guide/src/rustdoc-internals/search.md b/src/doc/rustc-dev-guide/src/rustdoc-internals/search.md index ddf8ec405f81d..3506431118baf 100644 --- a/src/doc/rustc-dev-guide/src/rustdoc-internals/search.md +++ b/src/doc/rustc-dev-guide/src/rustdoc-internals/search.md @@ -46,8 +46,8 @@ For space savings, it's also written without newlines or spaces. ] ``` -[`src/librustdoc/html/static/js/externs.js`] -defines an actual schema in a Closure `@typedef`. +[`src/librustdoc/html/static/js/rustdoc.d.ts`] +defines an actual schema in a TypeScript `type`. | Key | Name | Description | | --- | -------------------- | ------------ | @@ -68,7 +68,7 @@ with a free function called `function_name` and a struct called `Data`, with the type signature `Data, i32 -> str`, and an alias, `get_name`, that equivalently refers to `function_name`. -[`src/librustdoc/html/static/js/externs.js`]: https://github.com/rust-lang/rust/blob/79b710c13968a1a48d94431d024d2b1677940866/src/librustdoc/html/static/js/externs.js#L204-L258 +[`src/librustdoc/html/static/js/rustdoc.d.ts`]: https://github.com/rust-lang/rust/blob/2f92f050e83bf3312ce4ba73c31fe843ad3cbc60/src/librustdoc/html/static/js/rustdoc.d.ts#L344-L390 The search index needs to fit the needs of the `rustdoc` compiler, the `search.js` frontend, @@ -469,7 +469,7 @@ want the libs team to be able to add new items without causing unrelated tests to fail, but standalone tests will use it more often. The `ResultsTable` and `ParsedQuery` types are specified in -[`externs.js`](https://github.com/rust-lang/rust/blob/master/src/librustdoc/html/static/js/externs.js). +[`rustdoc.d.ts`](https://github.com/rust-lang/rust/blob/master/src/librustdoc/html/static/js/rustdoc.d.ts). For example, imagine we needed to fix a bug where a function named `constructor` couldn't be found. To do this, write two files: diff --git a/src/doc/rustc-dev-guide/src/rustdoc.md b/src/doc/rustc-dev-guide/src/rustdoc.md index 3867d2489886d..2a0e212f98e31 100644 --- a/src/doc/rustc-dev-guide/src/rustdoc.md +++ b/src/doc/rustc-dev-guide/src/rustdoc.md @@ -58,10 +58,13 @@ does is call the `main()` that's in this crate's `lib.rs`, though.) * If you want to copy those docs to a webserver, copy all of `build/host/doc`, since that's where the CSS, JS, fonts, and landing page are. + * For frontend debugging, disable the `rust.docs-minification` option in [`config.toml`]. * Use `./x test tests/rustdoc*` to run the tests using a stage1 rustdoc. * See [Rustdoc internals] for more information about tests. +[`config.toml`]: ./building/how-to-build-and-run.md + ## Code structure * All paths in this section are relative to `src/librustdoc` in the rust-lang/rust repository. @@ -77,6 +80,7 @@ does is call the `main()` that's in this crate's `lib.rs`, though.) * The tests on the structure of rustdoc HTML output are located in `tests/rustdoc`, where they're handled by the test runner of bootstrap and the supplementary script `src/etc/htmldocck.py`. +* Frontend CSS and JavaScript are stored in `html/static/`. ## Tests @@ -91,6 +95,11 @@ does is call the `main()` that's in this crate's `lib.rs`, though.) browser-UI-test](https://github.com/GuillaumeGomez/browser-UI-test/) that uses puppeteer to run tests in a headless browser and check rendering and interactivity. +* Additionally, JavaScript type annotations are written using [TypeScript-flavored JSDoc] + comments and an external d.ts file. The code itself is plain, valid JavaScript; we only + use tsc as a linter. + +[TypeScript-flavored JSDoc]: https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html ## Constraints diff --git a/src/doc/rustc-dev-guide/src/solve/caching.md b/src/doc/rustc-dev-guide/src/solve/caching.md index 72b90c59beed2..e568d47ca252a 100644 --- a/src/doc/rustc-dev-guide/src/solve/caching.md +++ b/src/doc/rustc-dev-guide/src/solve/caching.md @@ -98,8 +98,8 @@ TODO: write this :3 [req-depth-ck]: https://github.com/rust-lang/rust/blob/7606c13961ddc1174b70638e934df0439b7dc515/compiler/rustc_middle/src/traits/solve/cache.rs#L76-L86 [update-depth]: https://github.com/rust-lang/rust/blob/7606c13961ddc1174b70638e934df0439b7dc515/compiler/rustc_trait_selection/src/solve/search_graph.rs#L308 [rem-depth]: https://github.com/rust-lang/rust/blob/7606c13961ddc1174b70638e934df0439b7dc515/compiler/rustc_middle/src/traits/solve/cache.rs#L124 -[^1]: This is overly restrictive: if all nested goal return the overflow response with some -availabledepth `n`, then their result should be the same for any depths smaller than `n`. +[^1]: This is overly restrictive: if all nested goals return the overflow response with some +available depth `n`, then their result should be the same for any depths smaller than `n`. We can implement this optimization in the future. [chapter on coinduction]: ./coinduction.md diff --git a/src/doc/rustc-dev-guide/src/solve/invariants.md b/src/doc/rustc-dev-guide/src/solve/invariants.md index c16a3aeb2b375..fd12b19575748 100644 --- a/src/doc/rustc-dev-guide/src/solve/invariants.md +++ b/src/doc/rustc-dev-guide/src/solve/invariants.md @@ -23,9 +23,6 @@ well-formed after normalizing said aliases. We rely on this as otherwise we would have to re-check for well-formedness for these types. -This is unfortunately broken for `>::Output` due to implied bounds, -resulting in [#114936]. - ### Structural equality modulo regions implies semantic equality ✅ If you have a some type and equate it to itself after replacing any regions with unique @@ -33,7 +30,7 @@ inference variables in both the lhs and rhs, the now potentially structurally di types should still be equal to each other. Needed to prevent goals from succeeding in HIR typeck and then failing in MIR borrowck. -If this does invariant is broken MIR typeck ends up failing with an ICE. +If this invariant is broken MIR typeck ends up failing with an ICE. ### Applying inference results from a goal does not change its result ❌ @@ -91,7 +88,7 @@ it can easily result in unsoundness, e.g. [#57893](https://github.com/rust-lang/ If a trait goal holds with an empty environment, there should be a unique `impl`, either user-defined or builtin, which is used to prove that goal. This is -necessary to select a unique method. It +necessary to select a unique method. We do however break this invariant in few cases, some of which are due to bugs, some by design: diff --git a/src/doc/rustc-dev-guide/src/solve/normalization.md b/src/doc/rustc-dev-guide/src/solve/normalization.md index 8858258b54269..99dc20c46b5d4 100644 --- a/src/doc/rustc-dev-guide/src/solve/normalization.md +++ b/src/doc/rustc-dev-guide/src/solve/normalization.md @@ -1,5 +1,7 @@ # Normalization in the new solver +> FIXME: Normalization has been changed significantly since this chapter was written. + With the new solver we've made some fairly significant changes to normalization when compared to the existing implementation. @@ -52,12 +54,14 @@ before assigning the resulting rigid type to an inference variable. This is used This has to be used whenever we match on the value of some type, both inside and outside of the trait solver. + [structural_norm]: https://github.com/rust-lang/rust/blob/2627e9f3012a97d3136b3e11bf6bd0853c38a534/compiler/rustc_trait_selection/src/solve/alias_relate.rs#L140-L175 [structural-relate]: https://github.com/rust-lang/rust/blob/a0569fa8f91b5271e92d2f73fd252de7d3d05b9c/compiler/rustc_trait_selection/src/solve/alias_relate.rs#L88-L107 @@ -72,11 +76,13 @@ possible. However, this only works for aliases referencing bound variables if th not ambiguous as we're unable to replace the alias with a corresponding inference variable without leaking universes. + [generalize-no-alias]: https://github.com/rust-lang/rust/blob/a0569fa8f91b5271e92d2f73fd252de7d3d05b9c/compiler/rustc_infer/src/infer/relate/generalize.rs#L353-L358 diff --git a/src/doc/rustc-dev-guide/src/solve/opaque-types.md b/src/doc/rustc-dev-guide/src/solve/opaque-types.md index 87531705c2da4..672aab7708018 100644 --- a/src/doc/rustc-dev-guide/src/solve/opaque-types.md +++ b/src/doc/rustc-dev-guide/src/solve/opaque-types.md @@ -60,6 +60,7 @@ Finally, we check whether the item bounds of the opaque hold for the expected ty [eq-prev]: https://github.com/rust-lang/rust/blob/384d26fc7e3bdd7687cc17b2662b091f6017ec2a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs#L51-L59 [insert-storage]: https://github.com/rust-lang/rust/blob/384d26fc7e3bdd7687cc17b2662b091f6017ec2a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs#L68 [item-bounds-ck]: https://github.com/rust-lang/rust/blob/384d26fc7e3bdd7687cc17b2662b091f6017ec2a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs#L69-L74 + [^1]: FIXME: this should ideally only result in a unique candidate given that we require the args to be placeholders and regions are always inference vars [^2]: FIXME: why do we check whether the expected type is rigid for this. @@ -101,6 +102,7 @@ The handling of member constraints does not change in the new solver. See the FIXME: We need to continue to support calling methods on still unconstrained opaque types in their defining scope. It's unclear how to best do this. + ```rust use std::future::Future; use futures::FutureExt; diff --git a/src/doc/rustc-dev-guide/src/solve/significant-changes.md b/src/doc/rustc-dev-guide/src/solve/significant-changes.md index c5bb8a01b12b5..c82b5d468961a 100644 --- a/src/doc/rustc-dev-guide/src/solve/significant-changes.md +++ b/src/doc/rustc-dev-guide/src/solve/significant-changes.md @@ -42,7 +42,7 @@ old implementation structurally relates the aliases instead. This enables the new solver to stall equality until it is able to normalize the related aliases. The behavior of the old solver is incomplete and relies on eager normalization -which replaces ambiguous aliases with inference variables. As this is not +which replaces ambiguous aliases with inference variables. As this is not possible for aliases containing bound variables, the old implementation does not handle aliases inside of binders correctly, e.g. [#102048]. See the chapter on [normalization] for more details. diff --git a/src/doc/rustc-dev-guide/src/solve/the-solver.md b/src/doc/rustc-dev-guide/src/solve/the-solver.md index f7d82d117a05e..006fb649d2204 100644 --- a/src/doc/rustc-dev-guide/src/solve/the-solver.md +++ b/src/doc/rustc-dev-guide/src/solve/the-solver.md @@ -25,7 +25,7 @@ a separate "probe", to not leak inference constraints to the other candidates. We then try to merge the assembled candidates via `EvalCtxt::merge_candidates`. -## Important concepts and design pattern +## Important concepts and design patterns ### `EvalCtxt::add_goal` @@ -64,7 +64,7 @@ eagerly instantiates `'a` with a placeholder and then recursively proves Some goals can be proven in multiple ways. In these cases we try each option in a separate "probe" and then attempt to merge the resulting responses by using `EvalCtxt::try_merge_responses`. If merging the responses fails, we use -`EvalCtxt::flounder` instead, returning ambiguity. For some goals, we try +`EvalCtxt::flounder` instead, returning ambiguity. For some goals, we try to incompletely prefer some choices over others in case `EvalCtxt::try_merge_responses` fails. diff --git a/src/doc/rustc-dev-guide/src/stability.md b/src/doc/rustc-dev-guide/src/stability.md index 1bfe911c900c4..230925252bac3 100644 --- a/src/doc/rustc-dev-guide/src/stability.md +++ b/src/doc/rustc-dev-guide/src/stability.md @@ -34,7 +34,8 @@ Previously, due to a [rustc bug], stable items inside unstable modules were available to stable code in that location. As of September 2024, items with [accidentally stabilized paths] are marked with the `#[rustc_allowed_through_unstable_modules]` attribute -to prevent code dependent on those paths from breaking. +to prevent code dependent on those paths from breaking. Do *not* add this attribute +to any more items unless that is needed to avoid breaking changes. The `unstable` attribute may also have the `soft` value, which makes it a future-incompatible deny-by-default lint instead of a hard error. This is used diff --git a/src/doc/rustc-dev-guide/src/tests/ci.md b/src/doc/rustc-dev-guide/src/tests/ci.md index 5e27a2fd770c4..a4b22392f1976 100644 --- a/src/doc/rustc-dev-guide/src/tests/ci.md +++ b/src/doc/rustc-dev-guide/src/tests/ci.md @@ -28,7 +28,7 @@ Our CI is primarily executed on [GitHub Actions], with a single workflow defined in [`.github/workflows/ci.yml`], which contains a bunch of steps that are unified for all CI jobs that we execute. When a commit is pushed to a corresponding branch or a PR, the workflow executes the -[`calculate-job-matrix.py`] script, which dynamically generates the specific CI +[`src/ci/github-actions/ci.py`] script, which dynamically generates the specific CI jobs that should be executed. This script uses the [`jobs.yml`] file as an input, which contains a declarative configuration of all our CI jobs. @@ -299,8 +299,7 @@ platform’s custom [Docker container]. This has a lot of advantages for us: - We can avoid reinstalling tools (like QEMU or the Android emulator) every time thanks to Docker image caching. - Users can run the same tests in the same environment locally by just running - `src/ci/docker/run.sh image-name`, which is awesome to debug failures. Note - that there are only linux docker images available locally due to licensing and + `python3 src/ci/github-actions/ci.py run-local `, which is awesome to debug failures. Note that there are only linux docker images available locally due to licensing and other restrictions. The docker images prefixed with `dist-` are used for building artifacts while @@ -323,7 +322,7 @@ Our CI workflow uses various caching mechanisms, mainly for two things: ### Docker images caching The Docker images we use to run most of the Linux-based builders take a *long* -time to fully build. To speed up the build, we cache it using [Docker registry +time to fully build. To speed up the build, we cache them using [Docker registry caching], with the intermediate artifacts being stored on [ghcr.io]. We also push the built Docker images to ghcr, so that they can be reused by other tools (rustup) or by developers running the Docker build locally (to speed up their @@ -335,6 +334,13 @@ override the cache for the others. Instead, we store the images under different tags, identifying them with a custom hash made from the contents of all the Dockerfiles and related scripts. +The CI calculates a hash key, so that the cache of a Docker image is +invalidated if one of the following changes: + +- Dockerfile +- Files copied into the Docker image in the Dockerfile +- The architecture of the GitHub runner (x86 or ARM) + [ghcr.io]: https://github.com/rust-lang-ci/rust/pkgs/container/rust-ci [Docker registry caching]: https://docs.docker.com/build/cache/backends/registry/ @@ -342,9 +348,18 @@ Dockerfiles and related scripts. We build some C/C++ stuff in various CI jobs, and we rely on [sccache] to cache the intermediate LLVM artifacts. Sccache is a distributed ccache developed by -Mozilla, which can use an object storage bucket as the storage backend. In our -case, the artefacts are uploaded to an S3 bucket that we control -(`rust-lang-ci-sccache2`). +Mozilla, which can use an object storage bucket as the storage backend. + +With sccache there's no need to calculate the hash key ourselves. Sccache +invalidates the cache automatically when it detects changes to relevant inputs, +such as the source code, the version of the compiler, and important environment +variables. +So we just pass the sccache wrapper on top of cargo and sccache does the rest. + +We store the persistent artifacts on the S3 bucket `rust-lang-ci-sccache2`. So +when the CI runs, if sccache sees that LLVM is being compiled with the same C/C++ +compiler and the LLVM source code is the same, sccache retrieves the individual +compiled translation units from S3. [sccache]: https://github.com/mozilla/sccache @@ -410,10 +425,25 @@ To learn more about the dashboard, see the [Datadog CI docs]. [Datadog CI docs]: https://docs.datadoghq.com/continuous_integration/ [public dashboard]: https://p.datadoghq.com/sb/3a172e20-e9e1-11ed-80e3-da7ad0900002-b5f7bb7e08b664a06b08527da85f7e30 +## Determining the CI configuration + +If you want to determine which `config.toml` settings are used in CI for a +particular job, it is probably easiest to just look at the build log. To do +this: + +1. Go to + + to find the most recently successful build, and click on it. +2. Choose the job you are interested in on the left-hand side. +3. Click on the gear icon and choose "View raw logs" +4. Search for the string "Configure the build" +5. All of the build settings are listed below that starting with the + `configure:` prefix. + [GitHub Actions]: https://github.com/rust-lang/rust/actions [`jobs.yml`]: https://github.com/rust-lang/rust/blob/master/src/ci/github-actions/jobs.yml [`.github/workflows/ci.yml`]: https://github.com/rust-lang/rust/blob/master/.github/workflows/ci.yml -[`calculate-job-matrix.py`]: https://github.com/rust-lang/rust/blob/master/src/ci/github-actions/calculate-job-matrix.py +[`src/ci/github-actions/ci.py`]: https://github.com/rust-lang/rust/blob/master/src/ci/github-actions/ci.py [rust-lang-ci]: https://github.com/rust-lang-ci/rust/actions [bors]: https://github.com/bors [homu]: https://github.com/rust-lang/homu diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index 17106be46744c..459c082906eba 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -441,6 +441,46 @@ $ COMPILETEST_FORCE_STAGE0=1 x test --stage 0 tests/run-make/ Of course, some tests will not successfully *run* in this way. +#### Using rust-analyzer with `rmake.rs` + +Like other test programs, the `rmake.rs` scripts used by run-make tests do not +have rust-analyzer integration by default. + +To work around this when working on a particular test, temporarily create a +`Cargo.toml` file in the test's directory +(e.g. `tests/run-make/sysroot-crates-are-unstable/Cargo.toml`) +with these contents: + +

+Be careful not to add this `Cargo.toml` or its `Cargo.lock` to your actual PR! +
+ +```toml +# Convince cargo that this isn't part of an enclosing workspace. +[workspace] + +[package] +name = "rmake" +version = "0.1.0" +edition = "2021" + +[dependencies] +run_make_support = { path = "../../../src/tools/run-make-support" } + +[[bin]] +name = "rmake" +path = "rmake.rs" +``` + +Then add a corresponding entry to `"rust-analyzer.linkedProjects"` +(e.g. in `.vscode/settings.json`): + +```json +"rust-analyzer.linkedProjects": [ + "tests/run-make/sysroot-crates-are-unstable/Cargo.toml" +], +``` + #### Using Makefiles (legacy)
diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index b0527da7bf564..b6209bcb2d806 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -2,7 +2,9 @@ -> **FIXME(jieyouxu)** completely revise this chapter. + Directives are special comments that tell compiletest how to build and interpret a test. They must appear before the Rust source in the test. They may also @@ -92,7 +94,8 @@ for more details. | Directive | Explanation | Supported test suites | Possible values | |-----------------------------------|--------------------------------------------------------------------------------------------------------------------------|----------------------------------------------|-----------------------------------------------------------------------------------------| | `check-run-results` | Check run test binary `run-{pass,fail}` output snapshot | `ui`, `crashes`, `incremental` if `run-pass` | N/A | -| `error-pattern` | Check that output contains a regex pattern | `ui`, `crashes`, `incremental` if `run-pass` | Regex | +| `error-pattern` | Check that output contains a specific string | `ui`, `crashes`, `incremental` if `run-pass` | String | +| `regex-error-pattern` | Check that output contains a regex pattern | `ui`, `crashes`, `incremental` if `run-pass` | Regex | | `check-stdout` | Check `stdout` against `error-pattern`s from running test binary[^check_stdout] | `ui`, `crashes`, `incremental` | N/A | | `normalize-stderr-32bit` | Normalize actual stderr (for 32-bit platforms) with a rule `"" -> ""` before comparing against snapshot | `ui`, `incremental` | `"" -> ""`, ``/`` is regex capture and replace syntax | | `normalize-stderr-64bit` | Normalize actual stderr (for 64-bit platforms) with a rule `"" -> ""` before comparing against snapshot | `ui`, `incremental` | `"" -> ""`, ``/`` is regex capture and replace syntax | @@ -149,6 +152,8 @@ Some examples of `X` in `ignore-X` or `only-X`: `compare-mode-split-dwarf`, `compare-mode-split-dwarf-single` - The two different test modes used by coverage tests: `ignore-coverage-map`, `ignore-coverage-run` +- When testing a dist toolchain: `dist` + - This needs to be enabled with `COMPILETEST_ENABLE_DIST_TESTS=1` The following directives will check rustc build settings and target settings: @@ -171,6 +176,7 @@ settings: - `needs-rust-lld` — ignores if the rust lld support is not enabled (`rust.lld = true` in `config.toml`) - `needs-threads` — ignores if the target does not have threading support +- `needs-subprocess` — ignores if the target does not have subprocess support - `needs-symlink` — ignores if the target does not support symlinks. This can be the case on Windows if the developer did not enable privileged symlink permissions. @@ -186,6 +192,8 @@ settings: specified atomic widths, e.g. the test with `//@ needs-target-has-atomic: 8, 16, ptr` will only run if it supports the comma-separated list of atomic widths. +- `needs-dynamic-linking` - ignores if target does not support dynamic linking + (which is orthogonal to it being unable to create `dylib` and `cdylib` crate types) The following directives will check LLVM support: @@ -246,12 +254,13 @@ Consider writing the test as a proper incremental test instead. | Directive | Explanation | Supported test suites | Possible values | |-------------|--------------------------------------------------------------|------------------------------------------|---------------------------| -| `doc-flags` | Flags passed to `rustdoc` when building the test or aux file | `rustdoc`, `js-doc-test`, `rustdoc-json` | Any valid `rustdoc` flags | +| `doc-flags` | Flags passed to `rustdoc` when building the test or aux file | `rustdoc`, `rustdoc-js`, `rustdoc-json` | Any valid `rustdoc` flags | -> **FIXME(rustdoc)**: what does `check-test-line-numbers-match` do? -> -> Asked in -> . + ### Pretty printing diff --git a/src/doc/rustc-dev-guide/src/tests/docker.md b/src/doc/rustc-dev-guide/src/tests/docker.md index 31e3825f5673e..2ca08d42130a5 100644 --- a/src/doc/rustc-dev-guide/src/tests/docker.md +++ b/src/doc/rustc-dev-guide/src/tests/docker.md @@ -1,36 +1,44 @@ # Testing with Docker -The Rust tree includes [Docker] image definitions for the platforms used on -GitHub Actions in [`src/ci/docker`]. -The script [`src/ci/docker/run.sh`] is used to build the Docker image, run it, -build Rust within the image, and run the tests. - -You can run these images on your local development machine. This can be -helpful to test environments different from your local system. First you will +The [`src/ci/docker`] directory includes [Docker] image definitions for Linux-based jobs executed on GitHub Actions (non-Linux jobs run outside Docker). You can run these jobs on your local development machine, which can be +helpful to test environments different from your local system. You will need to install Docker on a Linux, Windows, or macOS system (typically Linux will be much faster than Windows or macOS because the latter use virtual -machines to emulate a Linux environment). To enter interactive mode which will -start a bash shell in the container, run `src/ci/docker/run.sh --dev ` -where `` is one of the directory names in `src/ci/docker` (for example -`x86_64-gnu` is a fairly standard Ubuntu environment). - -The docker script will mount your local Rust source tree in read-only mode, -and an `obj` directory in read-write mode. All of the compiler artifacts will -be stored in the `obj` directory. The shell will start out in the `obj` -directory. From there, you can run `../src/ci/run.sh` which will run the build -as defined by the image. - -Alternatively, you can run individual commands to do specific tasks. For -example, you can run `../x test tests/ui` to just run UI tests. -Note that there is some configuration in the [`src/ci/run.sh`] script that you -may need to recreate. Particularly, set `submodules = false` in your -`config.toml` so that it doesn't attempt to modify the read-only directory. +machines to emulate a Linux environment). + +Jobs running in CI are configured through a set of bash scripts, and it is not always trivial to reproduce their behavior locally. If you want to run a CI job locally in the simplest way possible, you can use a provided helper Python script that tries to replicate what happens on CI as closely as possible: -Some additional notes about using the Docker images: +```bash +python3 src/ci/github-actions/ci.py run-local +# For example: +python3 src/ci/github-actions/ci.py run-local dist-x86_64-linux-alt +``` +If the above script does not work for you, you would like to have more control of the Docker image execution, or you want to understand what exactly happens during Docker job execution, then continue reading below. + +## The `run.sh` script +The [`src/ci/docker/run.sh`] script is used to build a specific Docker image, run it, +build Rust within the image, and either run tests or prepare a set of archives designed for distribution. The script will mount your local Rust source tree in read-only mode, and an `obj` directory in read-write mode. All the compiler artifacts will be stored in the `obj` directory. The shell will start out in the `obj`directory. From there, it will execute `../src/ci/run.sh` which starts the build as defined by the Docker image. + +You can run `src/ci/docker/run.sh ` directly. A few important notes regarding the `run.sh` script: +- When executed on CI, the script expects that all submodules are checked out. If some submodule that is accessed by the job is not available, the build will result in an error. You should thus make sure that you have all required submodules checked out locally. You can either do that manually through git, or set `submodules = true` in your `config.toml` and run a command such as `x build` to let bootstrap download the most important submodules (this might not be enough for the given CI job that you are trying to execute though). +- `` corresponds to a single directory located in one of the `src/ci/docker/host-*` directories. Note that image name does not necessarily correspond to a job name, as some jobs execute the same image, but with different environment variables or Docker build arguments (this is a part of the complexity that makes it difficult to run CI jobs locally). +- If you are executing a "dist" job (job beginning with `dist-`), you should set the `DEPLOY=1` environment variable. +- If you are executing an "alternative dist" job (job beginning with `dist-` and ending with `-alt`), you should set the `DEPLOY_ALT=1` environment variable. - Some of the std tests require IPv6 support. Docker on Linux seems to have it disabled by default. Run the commands in [`enable-docker-ipv6.sh`] to enable IPv6 before creating the container. This only needs to be done once. + +### Interactive mode + +Sometimes, it can be useful to build a specific Docker image, and then run custom commands inside it, so that you can experiment with how the given system behaves. You can do that using an interactive mode, which will +start a bash shell in the container, using `src/ci/docker/run.sh --dev `. + +When inside the Docker container, you can run individual commands to do specific tasks. For +example, you can run `../x test tests/ui` to just run UI tests. + +Some additional notes about using the interactive mode: + - The container will be deleted automatically when you exit the shell, however the build artifacts persist in the `obj` directory. If you are switching between different Docker images, the artifacts from previous environments diff --git a/src/doc/rustc-dev-guide/src/the-parser.md b/src/doc/rustc-dev-guide/src/the-parser.md index 703ef27941e04..60a71ae3873ff 100644 --- a/src/doc/rustc-dev-guide/src/the-parser.md +++ b/src/doc/rustc-dev-guide/src/the-parser.md @@ -52,7 +52,7 @@ the token stream, and then execute the parser to get a [`Crate`] (the root AST node). To minimize the amount of copying that is done, -both [`StringReader`] and [`Parser`] have lifetimes which bind them to the parent [`ParseSess`]. +both [`Lexer`] and [`Parser`] have lifetimes which bind them to the parent [`ParseSess`]. This contains all the information needed while parsing, as well as the [`SourceMap`] itself. Note that while parsing, we may encounter macro definitions or invocations. @@ -67,7 +67,7 @@ Code for lexical analysis is split between two crates: constituting tokens. Although it is popular to implement lexers as generated finite state machines, the lexer in [`rustc_lexer`] is hand-written. -- [`StringReader`] integrates [`rustc_lexer`] with data structures specific to +- [`Lexer`] integrates [`rustc_lexer`] with data structures specific to `rustc`. Specifically, it adds `Span` information to tokens returned by [`rustc_lexer`] and interns identifiers. @@ -76,7 +76,7 @@ Code for lexical analysis is split between two crates: [`ParseSess`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/parse/struct.ParseSess.html [`rustc_lexer`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lexer/index.html [`SourceMap`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/source_map/struct.SourceMap.html -[`StringReader`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.StringReader.html +[`Lexer`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.Lexer.html [ast module]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html [ast]: ./ast-validation.md [parser]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/parser/index.html diff --git a/src/doc/rustc-dev-guide/src/traits/chalk.md b/src/doc/rustc-dev-guide/src/traits/chalk.md index 78deb367506c5..844f42b9879f8 100644 --- a/src/doc/rustc-dev-guide/src/traits/chalk.md +++ b/src/doc/rustc-dev-guide/src/traits/chalk.md @@ -10,7 +10,7 @@ stream and say hello! [Types team]: https://github.com/rust-lang/types-team [`#t-types`]: https://rust-lang.zulipchat.com/#narrow/stream/144729-t-types -The new-style trait solver is based on the work done in [chalk][chalk]. Chalk +The new-style trait solver is based on the work done in [chalk]. Chalk recasts Rust's trait system explicitly in terms of logic programming. It does this by "lowering" Rust code into a kind of logic program we can then execute queries against. @@ -30,7 +30,7 @@ You can read more about chalk itself in the ## Ongoing work The design of the new-style trait solving happens in two places: -**chalk**. The [chalk][chalk] repository is where we experiment with new ideas +**chalk**. The [chalk] repository is where we experiment with new ideas and designs for the trait system. **rustc**. Once we are happy with the logical rules, we proceed to diff --git a/src/doc/rustc-dev-guide/src/traits/implied-bounds.md b/src/doc/rustc-dev-guide/src/traits/implied-bounds.md index 911553ad3dfb6..cdcb90d3e2edf 100644 --- a/src/doc/rustc-dev-guide/src/traits/implied-bounds.md +++ b/src/doc/rustc-dev-guide/src/traits/implied-bounds.md @@ -40,8 +40,8 @@ requirements of impls and functions as explicit predicates. ### using implicit implied bounds as assumptions These bounds are not added to the `ParamEnv` of the affected item itself. For lexical -region resolution they are added using [`fn OutlivesEnvironment::with_bounds`]. -Similarly,during MIR borrowck we add them using +region resolution they are added using [`fn OutlivesEnvironment::from_normalized_bounds`]. +Similarly, during MIR borrowck we add them using [`fn UniversalRegionRelationsBuilder::add_implied_bounds`]. [We add implied bounds for the function signature and impl header in MIR borrowck][mir]. @@ -55,7 +55,7 @@ The assumed outlives constraints for implicit bounds are computed using the MIR borrowck adds the outlives constraints for both the normalized and unnormalized types, lexical region resolution [only uses the unnormalized types][notnorm]. -[`fn OutlivesEnvironment::with_bounds`]: https://github.com/rust-lang/rust/blob/5b8bc568d28b2e922290c9a966b3231d0ce9398b/compiler/rustc_infer/src/infer/outlives/env.rs#L90-L97 +[`fn OutlivesEnvironment::from_normalized_bounds`]: https://github.com/rust-lang/rust/blob/8239a37f9c0951a037cfc51763ea52a20e71e6bd/compiler/rustc_infer/src/infer/outlives/env.rs#L50-L55 [`fn UniversalRegionRelationsBuilder::add_implied_bounds`]: https://github.com/rust-lang/rust/blob/5b8bc568d28b2e922290c9a966b3231d0ce9398b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs#L316 [mir]: https://github.com/rust-lang/rust/blob/91cae1dcdcf1a31bd8a92e4a63793d65cfe289bb/compiler/rustc_borrowck/src/type_check/free_region_relations.rs#L258-L332 [`fn assumed_wf_types`]: https://github.com/rust-lang/rust/blob/5b8bc568d28b2e922290c9a966b3231d0ce9398b/compiler/rustc_ty_utils/src/implied_bounds.rs#L21 @@ -81,4 +81,4 @@ This results in multiple unsoundnesses: [#25860]: https://github.com/rust-lang/rust/issues/25860 [#84591]: https://github.com/rust-lang/rust/issues/84591 -[#100051]: https://github.com/rust-lang/rust/issues/100051 \ No newline at end of file +[#100051]: https://github.com/rust-lang/rust/issues/100051 diff --git a/src/doc/rustc-dev-guide/src/ty_module/binders.md b/src/doc/rustc-dev-guide/src/ty_module/binders.md index 6f31103d7f358..defb7cde514a0 100644 --- a/src/doc/rustc-dev-guide/src/ty_module/binders.md +++ b/src/doc/rustc-dev-guide/src/ty_module/binders.md @@ -16,7 +16,7 @@ Usages of these parameters is represented by the `RegionKind::Bound` (or `TyKind - A [`BoundVar`] which specifies which of the parameters the `Binder` introduces we are referring to. - We also sometimes store some extra information for diagnostics reasons via the [`BoundTyKind`]/[`BoundRegionKind`] but this is not important for type equality or more generally the semantics of `Ty`. (omitted from the above example) -In debug output (and also informally when talking to eachother) we tend to write these bound variables in the format of `^DebruijnIndex_BoundVar`. The above example would instead be written as `Binder(fn(&'^0_0), &[BoundVariableKind::Region])`. Sometimes when the `DebruijnIndex` is `0` we just omit it and would write `^0`. +In debug output (and also informally when talking to each other) we tend to write these bound variables in the format of `^DebruijnIndex_BoundVar`. The above example would instead be written as `Binder(fn(&'^0_0), &[BoundVariableKind::Region])`. Sometimes when the `DebruijnIndex` is `0` we just omit it and would write `^0`. Another concrete example, this time a mixture of `for<'a>` in a where clause and a type: ``` diff --git a/src/doc/rustc-dev-guide/src/ty_module/early_binder.md b/src/doc/rustc-dev-guide/src/ty_module/early_binder.md index 69db189350fdd..d83d73c955bfc 100644 --- a/src/doc/rustc-dev-guide/src/ty_module/early_binder.md +++ b/src/doc/rustc-dev-guide/src/ty_module/early_binder.md @@ -47,10 +47,10 @@ fn bar(foo: Foo) { In the compiler the `instantiate` call for this is done in [`FieldDef::ty`] ([src][field_def_ty_src]), at some point during type checking `bar` we will wind up calling `FieldDef::ty(x, &[u32, f32])` in order to obtain the type of `foo.x`. -**Note on indices:** It is possible for the indices in `Param` to not match with what the `EarlyBinder` binds. For -example, the index could be out of bounds or it could be the index of a lifetime when we were expecting a type. -These sorts of errors would be caught earlier in the compiler when translating from a `rustc_hir::Ty` to a `ty::Ty`. -If they occur later, that is a compiler bug. +**Note on indices:** It is a bug if the index of a `Param` does not match what the `EarlyBinder` binds. For +example, if the index is out of bounds or the index of a lifetime corresponds to a type parameter. +These sorts of errors are caught earlier in the compiler during name resolution where we disallow references +to generics parameters introduced by items that should not be nameable by the inner item. [`FieldDef::ty`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.FieldDef.html#method.ty [field_def_ty_src]: https://github.com/rust-lang/rust/blob/44d679b9021f03a79133021b94e6d23e9b55b3ab/compiler/rustc_middle/src/ty/mod.rs#L1421-L1426 diff --git a/src/doc/rustc-dev-guide/src/ty_module/param_ty_const_regions.md b/src/doc/rustc-dev-guide/src/ty_module/param_ty_const_regions.md index 6b467724f49c9..c52f0c0df2a4c 100644 --- a/src/doc/rustc-dev-guide/src/ty_module/param_ty_const_regions.md +++ b/src/doc/rustc-dev-guide/src/ty_module/param_ty_const_regions.md @@ -53,7 +53,7 @@ Concretely given the `ty::Generics` for the item the parameter is defined on, if The index fully defines the `Ty` and is the only part of `TyKind::Param` that matters for reasoning about the code we are compiling. -Generally we do not care what the name is and only use the index is included for diagnostics and debug logs as otherwise it would be +Generally we do not care what the name is and only use the index. The name is included for diagnostics and debug logs as otherwise it would be incredibly difficult to understand the output, i.e. `Vec: Sized` vs `Vec: Sized`. In debug output, parameter types are often printed out as `{name}/#{index}`, for example in the function `foo` if we were to debug print `Vec` it would be written as `Vec`. @@ -65,7 +65,7 @@ The rules against shadowing make this difficult but those language rules could c ### Lifetime parameters -In contrast to `Ty`/`Const`'s `Param` singular `Param` variant, lifetimes have two variants for representing region parameters: [`RegionKind::EarlyParam`] and [`RegionKind::LateParam`]. The reason for this is due to function's distinguishing between [early and late bound parameters](../early-late-bound-params/early-late-bound-summary.md) which is discussed in an earlier chapter (see link). +In contrast to `Ty`/`Const`'s `Param` singular `Param` variant, lifetimes have two variants for representing region parameters: [`RegionKind::EarlyParam`] and [`RegionKind::LateParam`]. The reason for this is due to function's distinguishing between [early and late bound parameters][ch_early_late_bound] which is discussed in an earlier chapter (see link). `RegionKind::EarlyParam` is structured identically to `Ty/Const`'s `Param` variant, it is simply a `u32` index and a `Symbol`. For lifetime parameters defined on non-function items we always use `ReEarlyParam`. For functions we use `ReEarlyParam` for any early bound parameters and `ReLateParam` for any late bound parameters. Note that just like `Ty` and `Const` params we often debug format them as `'SYMBOL/#INDEX`, see for example: @@ -85,7 +85,7 @@ fn foo<'a, 'b, T: 'a>(one: T, two: &'a &'b u32) -> &'b u32 { `RegionKind::LateParam` will be discussed more in the chapter on [instantiating binders][ch_instantiating_binders]. -[ch_early_late_bound]: ../early-late-bound-params/early-late-bound-summary.md +[ch_early_late_bound]: ../early_late_parameters.md [ch_binders]: ./binders.md [ch_instantiating_binders]: ./instantiating_binders.md [`BoundRegionKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.BoundRegionKind.html diff --git a/src/doc/rustc-dev-guide/triagebot.toml b/src/doc/rustc-dev-guide/triagebot.toml index ccb0de862ef05..12aa0b7b8ff15 100644 --- a/src/doc/rustc-dev-guide/triagebot.toml +++ b/src/doc/rustc-dev-guide/triagebot.toml @@ -6,3 +6,6 @@ allow-unauthenticated = [ "waiting-on-author", "blocked", ] + +# Automatically close and reopen PRs made by bots to run CI on them +[bot-pull-requests] diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index f0c3720eae14f..6c7cdec348024 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -29,6 +29,7 @@ - [\*-apple-watchos](platform-support/apple-watchos.md) - [\*-apple-visionos](platform-support/apple-visionos.md) - [aarch64-nintendo-switch-freestanding](platform-support/aarch64-nintendo-switch-freestanding.md) + - [amdgcn-amd-amdhsa](platform-support/amdgcn-amd-amdhsa.md) - [armeb-unknown-linux-gnueabi](platform-support/armeb-unknown-linux-gnueabi.md) - [arm-none-eabi](platform-support/arm-none-eabi.md) - [armv4t-none-eabi](platform-support/armv4t-none-eabi.md) @@ -60,16 +61,18 @@ - [loongarch\*-unknown-linux-\*](platform-support/loongarch-linux.md) - [loongarch\*-unknown-none\*](platform-support/loongarch-none.md) - [m68k-unknown-linux-gnu](platform-support/m68k-unknown-linux-gnu.md) + - [m68k-unknown-none-elf](platform-support/m68k-unknown-none-elf.md) - [mips64-openwrt-linux-musl](platform-support/mips64-openwrt-linux-musl.md) - [mipsel-sony-psx](platform-support/mipsel-sony-psx.md) + - [mips\*-mti-none-elf](platform-support/mips-mti-none-elf.md) - [mipsisa\*r6\*-unknown-linux-gnu\*](platform-support/mips-release-6.md) - [nvptx64-nvidia-cuda](platform-support/nvptx64-nvidia-cuda.md) - [powerpc-unknown-openbsd](platform-support/powerpc-unknown-openbsd.md) - [powerpc-unknown-linux-muslspe](platform-support/powerpc-unknown-linux-muslspe.md) - [powerpc64-ibm-aix](platform-support/aix.md) - [powerpc64le-unknown-linux-musl](platform-support/powerpc64le-unknown-linux-musl.md) - - [riscv32e*-unknown-none-elf](platform-support/riscv32e-unknown-none-elf.md) - - [riscv32i*-unknown-none-elf](platform-support/riscv32-unknown-none-elf.md) + - [riscv32e\*-unknown-none-elf](platform-support/riscv32e-unknown-none-elf.md) + - [riscv32i\*-unknown-none-elf](platform-support/riscv32-unknown-none-elf.md) - [riscv32im-risc0-zkvm-elf](platform-support/riscv32im-risc0-zkvm-elf.md) - [riscv32imac-unknown-xous-elf](platform-support/riscv32imac-unknown-xous-elf.md) - [riscv64gc-unknown-linux-gnu](platform-support/riscv64gc-unknown-linux-gnu.md) @@ -78,15 +81,16 @@ - [s390x-unknown-linux-musl](platform-support/s390x-unknown-linux-musl.md) - [sparc-unknown-none-elf](./platform-support/sparc-unknown-none-elf.md) - [sparcv9-sun-solaris](platform-support/solaris.md) - - [*-pc-windows-gnullvm](platform-support/pc-windows-gnullvm.md) + - [\*-pc-windows-gnullvm](platform-support/pc-windows-gnullvm.md) - [\*-nto-qnx-\*](platform-support/nto-qnx.md) - - [*-unikraft-linux-musl](platform-support/unikraft-linux-musl.md) - - [*-unknown-hermit](platform-support/hermit.md) - - [*-unknown-freebsd](platform-support/freebsd.md) + - [\*-unikraft-linux-musl](platform-support/unikraft-linux-musl.md) + - [\*-unknown-hermit](platform-support/hermit.md) + - [\*-unknown-freebsd](platform-support/freebsd.md) - [\*-unknown-netbsd\*](platform-support/netbsd.md) - - [*-unknown-openbsd](platform-support/openbsd.md) - - [*-unknown-redox](platform-support/redox.md) + - [\*-unknown-openbsd](platform-support/openbsd.md) + - [\*-unknown-redox](platform-support/redox.md) - [\*-unknown-uefi](platform-support/unknown-uefi.md) + - [\*-uwp-windows-msvc](platform-support/uwp-windows-msvc.md) - [\*-wrs-vxworks](platform-support/vxworks.md) - [wasm32-wasip1](platform-support/wasm32-wasip1.md) - [wasm32-wasip1-threads](platform-support/wasm32-wasip1-threads.md) @@ -95,13 +99,15 @@ - [wasm32-unknown-unknown](platform-support/wasm32-unknown-unknown.md) - [wasm32v1-none](platform-support/wasm32v1-none.md) - [wasm64-unknown-unknown](platform-support/wasm64-unknown-unknown.md) + - [\*-win7-windows-gnu](platform-support/win7-windows-gnu.md) - [\*-win7-windows-msvc](platform-support/win7-windows-msvc.md) - [x86_64-fortanix-unknown-sgx](platform-support/x86_64-fortanix-unknown-sgx.md) + - [x86_64-pc-cygwin](platform-support/x86_64-pc-cygwin.md) - [x86_64-pc-solaris](platform-support/solaris.md) - [x86_64-unknown-linux-none.md](platform-support/x86_64-unknown-linux-none.md) - [x86_64-unknown-none](platform-support/x86_64-unknown-none.md) - [xtensa-\*-none-elf](platform-support/xtensa.md) - - [*-nuttx-\*](platform-support/nuttx.md) + - [\*-nuttx-\*](platform-support/nuttx.md) - [Targets](targets/index.md) - [Built-in Targets](targets/built-in.md) - [Custom Targets](targets/custom.md) diff --git a/src/doc/rustc/src/check-cfg.md b/src/doc/rustc/src/check-cfg.md index f7e831dc8bd85..00add2651ae8b 100644 --- a/src/doc/rustc/src/check-cfg.md +++ b/src/doc/rustc/src/check-cfg.md @@ -134,8 +134,8 @@ As of `2025-01-02T`, the list of known names is as follows: - `unix` - `windows` -> Starting with CURRENT_RUSTC_VERSION, the `test` cfg is consider to be a "userspace" config -> despite being also set by `rustc` and should be managed by the build-system it-self. +> Starting with 1.85.0, the `test` cfg is consider to be a "userspace" config +> despite being also set by `rustc` and should be managed by the build system itself. Like with `values(any())`, well known names checking can be disabled by passing `cfg(any())` as argument to `--check-cfg`. diff --git a/src/doc/rustc/src/check-cfg/cargo-specifics.md b/src/doc/rustc/src/check-cfg/cargo-specifics.md index bd4bebbc874a6..371bbd26e943f 100644 --- a/src/doc/rustc/src/check-cfg/cargo-specifics.md +++ b/src/doc/rustc/src/check-cfg/cargo-specifics.md @@ -13,6 +13,8 @@ the `unexpected_cfgs` lint and `--check-cfg` flag. It is not intended to provide individual details, for that refer to the [`--check-cfg` documentation](../check-cfg.md) and to the [Cargo book](../../cargo/index.html). +> The full list of well known cfgs (aka builtins) can be found under [Checking conditional configurations / Well known names and values](../check-cfg.md#well-known-names-and-values). + ## Cargo feature *See the [`[features]` section in the Cargo book][cargo-features] for more details.* diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index e987d06b0f31f..8c1769a8c7722 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md @@ -207,14 +207,14 @@ options should be separated by spaces. ## link-dead-code -This flag controls whether the linker will keep dead code. It takes one of -the following values: +Tries to generate and link dead code that would otherwise not be generated or +linked. It takes one of the following values: -* `y`, `yes`, `on`, `true` or no value: keep dead code. +* `y`, `yes`, `on`, `true` or no value: try to keep dead code. * `n`, `no`, `off` or `false`: remove dead code (the default). -An example of when this flag might be useful is when trying to construct code coverage -metrics. +This flag was historically used to help improve some older forms of code +coverage measurement. Its use is not recommended. ## link-self-contained @@ -368,7 +368,7 @@ This flag controls the optimization level. * `s`: optimize for binary size. * `z`: optimize for binary size, but also turn off loop vectorization. -Note: The [`-O` flag][option-o-optimize] is an alias for `-C opt-level=2`. +Note: The [`-O` flag][option-o-optimize] is an alias for `-C opt-level=3`. The default is `0`. diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md index 0942b5ebfee37..9dd2e7de1b330 100644 --- a/src/doc/rustc/src/command-line-arguments.md +++ b/src/doc/rustc/src/command-line-arguments.md @@ -308,7 +308,7 @@ A synonym for [`-C debuginfo=2`](codegen-options/index.md#debuginfo). ## `-O`: optimize your code -A synonym for [`-C opt-level=2`](codegen-options/index.md#opt-level). +A synonym for [`-C opt-level=3`](codegen-options/index.md#opt-level). ## `-o`: filename of the output diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 24e9a3c812106..b5cd3864620d3 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -34,9 +34,9 @@ target | notes -------|------- [`aarch64-apple-darwin`](platform-support/apple-darwin.md) | ARM64 macOS (11.0+, Big Sur+) `aarch64-unknown-linux-gnu` | ARM64 Linux (kernel 4.1, glibc 2.17+) -`i686-pc-windows-gnu` | 32-bit MinGW (Windows 10+, Windows Server 2016+) [^x86_32-floats-return-ABI] -`i686-pc-windows-msvc` | 32-bit MSVC (Windows 10+, Windows Server 2016+) [^x86_32-floats-return-ABI] -`i686-unknown-linux-gnu` | 32-bit Linux (kernel 3.2+, glibc 2.17+) [^x86_32-floats-return-ABI] +`i686-pc-windows-gnu` | 32-bit MinGW (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] +`i686-pc-windows-msvc` | 32-bit MSVC (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] +`i686-unknown-linux-gnu` | 32-bit Linux (kernel 3.2+, glibc 2.17+, Pentium 4) [^x86_32-floats-return-ABI] [`x86_64-apple-darwin`](platform-support/apple-darwin.md) | 64-bit macOS (10.12+, Sierra+) `x86_64-pc-windows-gnu` | 64-bit MinGW (Windows 10+, Windows Server 2016+) `x86_64-pc-windows-msvc` | 64-bit MSVC (Windows 10+, Windows Server 2016+) @@ -101,7 +101,7 @@ target | notes [`riscv64gc-unknown-linux-gnu`](platform-support/riscv64gc-unknown-linux-gnu.md) | RISC-V Linux (kernel 4.20, glibc 2.29) [`riscv64gc-unknown-linux-musl`](platform-support/riscv64gc-unknown-linux-musl.md) | RISC-V Linux (kernel 4.20, musl 1.2.3) [`s390x-unknown-linux-gnu`](platform-support/s390x-unknown-linux-gnu.md) | S390x Linux (kernel 3.2, glibc 2.17) -[`x86_64-unknown-freebsd`](platform-support/freebsd.md) | 64-bit amd64 FreeBSD +[`x86_64-unknown-freebsd`](platform-support/freebsd.md) | 64-bit x86 FreeBSD [`x86_64-unknown-illumos`](platform-support/illumos.md) | illumos `x86_64-unknown-linux-musl` | 64-bit Linux with musl 1.2.3 [`x86_64-unknown-netbsd`](platform-support/netbsd.md) | NetBSD/amd64 @@ -162,14 +162,14 @@ target | std | notes [`armv7a-none-eabi`](platform-support/arm-none-eabi.md) | * | Bare Armv7-A [`armv7r-none-eabi`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R [`armv7r-none-eabihf`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, hardfloat -`i586-pc-windows-msvc` | * | 32-bit Windows w/o SSE [^x86_32-floats-x87] -`i586-unknown-linux-gnu` | ✓ | 32-bit Linux w/o SSE (kernel 3.2, glibc 2.17) [^x86_32-floats-x87] -`i586-unknown-linux-musl` | ✓ | 32-bit Linux w/o SSE, musl 1.2.3 [^x86_32-floats-x87] -[`i686-linux-android`](platform-support/android.md) | ✓ | 32-bit x86 Android [^x86_32-floats-return-ABI] -[`i686-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | 32-bit x86 MinGW (Windows 10+), LLVM ABI [^x86_32-floats-return-ABI] -[`i686-unknown-freebsd`](platform-support/freebsd.md) | ✓ | 32-bit x86 FreeBSD [^x86_32-floats-return-ABI] -`i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 [^x86_32-floats-return-ABI] -[`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI +`i586-pc-windows-msvc` | * | 32-bit Windows (original Pentium) [^x86_32-floats-x87] +`i586-unknown-linux-gnu` | ✓ | 32-bit Linux (kernel 3.2, glibc 2.17, original Pentium) [^x86_32-floats-x87] +`i586-unknown-linux-musl` | ✓ | 32-bit Linux (musl 1.2.3, original Pentium) [^x86_32-floats-x87] +[`i686-linux-android`](platform-support/android.md) | ✓ | 32-bit x86 Android ([Pentium 4 plus various extensions](https://developer.android.com/ndk/guides/abis.html#x86)) [^x86_32-floats-return-ABI] +[`i686-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | 32-bit x86 MinGW (Windows 10+, Pentium 4), LLVM ABI [^x86_32-floats-return-ABI] +[`i686-unknown-freebsd`](platform-support/freebsd.md) | ✓ | 32-bit x86 FreeBSD (Pentium 4) [^x86_32-floats-return-ABI] +`i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 (Pentium 4) [^x86_32-floats-return-ABI] +[`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI (Pentium 4, softfloat) [`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64D ABI) [`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64S ABI) [`nvptx64-nvidia-cuda`](platform-support/nvptx64-nvidia-cuda.md) | * | --emit=asm generates PTX code that [runs on NVIDIA GPUs] @@ -259,16 +259,20 @@ target | std | host | notes `aarch64-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (ILP32 ABI) [`aarch64-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | ARM64 NetBSD [`aarch64-unknown-nto-qnx700`](platform-support/nto-qnx.md) | ? | | ARM64 QNX Neutrino 7.0 RTOS | -[`aarch64-unknown-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | ARM64 QNX Neutrino 7.1 RTOS | +[`aarch64-unknown-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | ARM64 QNX Neutrino 7.1 RTOS with default network stack (io-pkt) | +[`aarch64-unknown-nto-qnx710_iosock`](platform-support/nto-qnx.md) | ✓ | | ARM64 QNX Neutrino 7.1 RTOS with new network stack (io-sock) | +[`aarch64-unknown-nto-qnx800`](platform-support/nto-qnx.md) | ✓ | | ARM64 QNX Neutrino 8.0 RTOS | +[`aarch64-unknown-nuttx`](platform-support/nuttx.md) | ✓ | | ARM64 with NuttX [`aarch64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | ARM64 OpenBSD [`aarch64-unknown-redox`](platform-support/redox.md) | ✓ | | ARM64 Redox OS [`aarch64-unknown-teeos`](platform-support/aarch64-unknown-teeos.md) | ? | | ARM64 TEEOS | [`aarch64-unknown-trusty`](platform-support/trusty.md) | ? | | -`aarch64-uwp-windows-msvc` | ✓ | | +[`aarch64-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ | | [`aarch64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | ARM64 VxWorks OS `aarch64_be-unknown-linux-gnu` | ✓ | ✓ | ARM64 Linux (big-endian) `aarch64_be-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (big-endian, ILP32 ABI) [`aarch64_be-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | ARM64 NetBSD (big-endian) +[`amdgcn-amd-amdhsa`](platform-support/amdgcn-amd-amdhsa.md) | * | | `-Ctarget-cpu=gfx...` to specify [the AMD GPU] to compile for [`arm64_32-apple-watchos`](platform-support/apple-watchos.md) | ✓ | | Arm Apple WatchOS 64-bit with 32-bit pointers [`arm64e-apple-darwin`](platform-support/arm64e-apple-darwin.md) | ✓ | ✓ | ARM64e Apple Darwin [`arm64e-apple-ios`](platform-support/arm64e-apple-ios.md) | ✓ | | ARM64e Apple iOS @@ -295,6 +299,8 @@ target | std | host | notes [`armv7k-apple-watchos`](platform-support/apple-watchos.md) | ✓ | | Armv7-A Apple WatchOS [`armv7s-apple-ios`](platform-support/apple-ios.md) | ✓ | | Armv7-A Apple-A6 Apple iOS [`armv8r-none-eabihf`](platform-support/armv8r-none-eabihf.md) | * | | Bare Armv8-R, hardfloat +[`armv7a-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv7-A with NuttX +[`armv7a-nuttx-eabihf`](platform-support/nuttx.md) | ✓ | | ARMv7-A with NuttX, hardfloat `avr-unknown-gnu-atmega328` | * | | AVR. Requires `-Z build-std=core` `bpfeb-unknown-none` | * | | BPF (big endian) `bpfel-unknown-none` | * | | BPF (little endian) @@ -302,21 +308,23 @@ target | std | host | notes `csky-unknown-linux-gnuabiv2hf` | ✓ | | C-SKY abiv2 Linux, hardfloat (little endian) [`hexagon-unknown-linux-musl`](platform-support/hexagon-unknown-linux-musl.md) | ✓ | | Hexagon Linux with musl 1.2.3 [`hexagon-unknown-none-elf`](platform-support/hexagon-unknown-none-elf.md)| * | | Bare Hexagon (v60+, HVX) -[`i386-apple-ios`](platform-support/apple-ios.md) | ✓ | | 32-bit x86 iOS [^x86_32-floats-return-ABI] -[`i586-pc-nto-qnx700`](platform-support/nto-qnx.md) | * | | 32-bit x86 QNX Neutrino 7.0 RTOS [^x86_32-floats-return-ABI] -[`i586-unknown-netbsd`](platform-support/netbsd.md) | ✓ | | 32-bit x86, restricted to Pentium -[`i686-apple-darwin`](platform-support/apple-darwin.md) | ✓ | ✓ | 32-bit macOS (10.12+, Sierra+) [^x86_32-floats-return-ABI] -`i686-unknown-haiku` | ✓ | ✓ | 32-bit Haiku [^x86_32-floats-return-ABI] -[`i686-unknown-hurd-gnu`](platform-support/hurd.md) | ✓ | ✓ | 32-bit GNU/Hurd [^x86_32-floats-return-ABI] -[`i686-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD/i386 with SSE2 [^x86_32-floats-return-ABI] -[`i686-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 32-bit OpenBSD [^x86_32-floats-return-ABI] -[`i686-unknown-redox`](platform-support/redox.md) | ✓ | | i686 Redox OS +[`i386-apple-ios`](platform-support/apple-ios.md) | ✓ | | 32-bit x86 iOS (Penryn) [^x86_32-floats-return-ABI] +[`i586-pc-nto-qnx700`](platform-support/nto-qnx.md) | * | | 32-bit x86 QNX Neutrino 7.0 RTOS (Pentium 4) [^x86_32-floats-return-ABI] +[`i586-unknown-netbsd`](platform-support/netbsd.md) | ✓ | | 32-bit x86 (original Pentium) [^x86_32-floats-x87] +[`i586-unknown-redox`](platform-support/redox.md) | ✓ | | 32-bit x86 Redox OS (PentiumPro) [^x86_32-floats-x87] +[`i686-apple-darwin`](platform-support/apple-darwin.md) | ✓ | ✓ | 32-bit macOS (10.12+, Sierra+, Penryn) [^x86_32-floats-return-ABI] +`i686-unknown-haiku` | ✓ | ✓ | 32-bit Haiku (Pentium 4) [^x86_32-floats-return-ABI] +[`i686-unknown-hurd-gnu`](platform-support/hurd.md) | ✓ | ✓ | 32-bit GNU/Hurd (Pentium 4) [^x86_32-floats-return-ABI] +[`i686-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD/i386 (Pentium 4) [^x86_32-floats-return-ABI] +[`i686-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 32-bit OpenBSD (Pentium 4) [^x86_32-floats-return-ABI] `i686-uwp-windows-gnu` | ✓ | | [^x86_32-floats-return-ABI] -`i686-uwp-windows-msvc` | ✓ | | [^x86_32-floats-return-ABI] +[`i686-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ | | [^x86_32-floats-return-ABI] +[`i686-win7-windows-gnu`](platform-support/win7-windows-gnu.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] [`i686-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] [`i686-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [^x86_32-floats-return-ABI] [`loongarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | | LoongArch64 OpenHarmony [`m68k-unknown-linux-gnu`](platform-support/m68k-unknown-linux-gnu.md) | ? | | Motorola 680x0 Linux +[`m68k-unknown-none-elf`](platform-support/m68k-unknown-none-elf.md) | | | Motorola 680x0 `mips-unknown-linux-gnu` | ✓ | ✓ | MIPS Linux (kernel 4.4, glibc 2.23) `mips-unknown-linux-musl` | ✓ | | MIPS Linux with musl 1.2.3 `mips-unknown-linux-uclibc` | ✓ | | MIPS Linux with uClibc @@ -332,6 +340,8 @@ target | std | host | notes `mipsel-unknown-linux-uclibc` | ✓ | | MIPS (LE) Linux with uClibc [`mipsel-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | 32-bit MIPS (LE), requires mips32 cpu support `mipsel-unknown-none` | * | | Bare MIPS (LE) softfloat +[`mips-mti-none-elf`](platform-support/mips-mti-none-elf.md) | * | | Bare MIPS32r2 (BE) softfloat +[`mipsel-mti-none-elf`](platform-support/mips-mti-none-elf.md) | * | | Bare MIPS32r2 (LE) softfloat [`mipsisa32r6-unknown-linux-gnu`](platform-support/mips-release-6.md) | ? | | 32-bit MIPS Release 6 Big Endian [`mipsisa32r6el-unknown-linux-gnu`](platform-support/mips-release-6.md) | ? | | 32-bit MIPS Release 6 Little Endian [`mipsisa64r6-unknown-linux-gnuabi64`](platform-support/mips-release-6.md) | ? | | 64-bit MIPS Release 6 Big Endian @@ -360,21 +370,21 @@ target | std | host | notes [`riscv32im-risc0-zkvm-elf`](platform-support/riscv32im-risc0-zkvm-elf.md) | ? | | RISC Zero's zero-knowledge Virtual Machine (RV32IM ISA) [`riscv32ima-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | | Bare RISC-V (RV32IMA ISA) [`riscv32imac-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF -[`riscv32imac-unknown-nuttx-elf`](platform-support/nuttx.md) | * | | RISC-V 32bit with NuttX +[`riscv32imac-unknown-nuttx-elf`](platform-support/nuttx.md) | ✓ | | RISC-V 32bit with NuttX [`riscv32imac-unknown-xous-elf`](platform-support/riscv32imac-unknown-xous-elf.md) | ? | | RISC-V Xous (RV32IMAC ISA) [`riscv32imafc-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF -[`riscv32imafc-unknown-nuttx-elf`](platform-support/nuttx.md) | * | | RISC-V 32bit with NuttX +[`riscv32imafc-unknown-nuttx-elf`](platform-support/nuttx.md) | ✓ | | RISC-V 32bit with NuttX [`riscv32imc-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF -[`riscv32imc-unknown-nuttx-elf`](platform-support/nuttx.md) | * | | RISC-V 32bit with NuttX +[`riscv32imc-unknown-nuttx-elf`](platform-support/nuttx.md) | ✓ | | RISC-V 32bit with NuttX [`riscv64-linux-android`](platform-support/android.md) | ? | | RISC-V 64-bit Android [`riscv64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | `riscv64gc-unknown-freebsd` | ? | | RISC-V FreeBSD `riscv64gc-unknown-fuchsia` | ? | | RISC-V Fuchsia [`riscv64gc-unknown-hermit`](platform-support/hermit.md) | ✓ | | RISC-V Hermit [`riscv64gc-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | RISC-V NetBSD -[`riscv64gc-unknown-nuttx-elf`](platform-support/nuttx.md) | * | | RISC-V 64bit with NuttX +[`riscv64gc-unknown-nuttx-elf`](platform-support/nuttx.md) | ✓ | | RISC-V 64bit with NuttX [`riscv64gc-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/riscv64 -[`riscv64imac-unknown-nuttx-elf`](platform-support/nuttx.md) | * | | RISC-V 64bit with NuttX +[`riscv64imac-unknown-nuttx-elf`](platform-support/nuttx.md) | ✓ | | RISC-V 64bit with NuttX [`s390x-unknown-linux-musl`](platform-support/s390x-unknown-linux-musl.md) | ✓ | | S390x Linux (kernel 3.2, musl 1.2.3) `sparc-unknown-linux-gnu` | ✓ | | 32-bit SPARC Linux [`sparc-unknown-none-elf`](./platform-support/sparc-unknown-none-elf.md) | * | | Bare 32-bit SPARC V7+ @@ -382,20 +392,25 @@ target | std | host | notes [`sparc64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/sparc64 [`thumbv4t-none-eabi`](platform-support/armv4t-none-eabi.md) | * | | Thumb-mode Bare Armv4T [`thumbv5te-none-eabi`](platform-support/armv5te-none-eabi.md) | * | | Thumb-mode Bare Armv5TE -[`thumbv6m-nuttx-eabi`](platform-support/nuttx.md) | * | | ARMv6M with NuttX -`thumbv7a-pc-windows-msvc` | ✓ | | -`thumbv7a-uwp-windows-msvc` | ✓ | | -[`thumbv7em-nuttx-eabi`](platform-support/nuttx.md) | * | | ARMv7EM with NuttX -[`thumbv7em-nuttx-eabihf`](platform-support/nuttx.md) | * | | ARMv7EM with NuttX, hardfloat -[`thumbv7m-nuttx-eabi`](platform-support/nuttx.md) | * | | ARMv7M with NuttX +[`thumbv6m-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv6M with NuttX +`thumbv7a-pc-windows-msvc` | | | +[`thumbv7a-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | | | +[`thumbv7a-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv7-A with NuttX +[`thumbv7a-nuttx-eabihf`](platform-support/nuttx.md) | ✓ | | ARMv7-A with NuttX, hardfloat +[`thumbv7em-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv7EM with NuttX +[`thumbv7em-nuttx-eabihf`](platform-support/nuttx.md) | ✓ | | ARMv7EM with NuttX, hardfloat +[`thumbv7m-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv7M with NuttX `thumbv7neon-unknown-linux-musleabihf` | ? | | Thumb2-mode Armv7-A Linux with NEON, musl 1.2.3 -[`thumbv8m.base-nuttx-eabi`](platform-support/nuttx.md) | * | | ARMv8M Baseline with NuttX -[`thumbv8m.main-nuttx-eabi`](platform-support/nuttx.md) | * | | ARMv8M Mainline with NuttX -[`thumbv8m.main-nuttx-eabihf`](platform-support/nuttx.md) | * | | ARMv8M Mainline with NuttX, hardfloat +[`thumbv8m.base-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv8M Baseline with NuttX +[`thumbv8m.main-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv8M Mainline with NuttX +[`thumbv8m.main-nuttx-eabihf`](platform-support/nuttx.md) | ✓ | | ARMv8M Mainline with NuttX, hardfloat [`wasm64-unknown-unknown`](platform-support/wasm64-unknown-unknown.md) | ? | | WebAssembly [`x86_64-apple-tvos`](platform-support/apple-tvos.md) | ✓ | | x86 64-bit tvOS [`x86_64-apple-watchos-sim`](platform-support/apple-watchos.md) | ✓ | | x86 64-bit Apple WatchOS simulator -[`x86_64-pc-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | x86 64-bit QNX Neutrino 7.1 RTOS | +[`x86_64-pc-cygwin`](platform-support/x86_64-pc-cygwin.md) | ? | | 64-bit x86 Cygwin | +[`x86_64-pc-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | x86 64-bit QNX Neutrino 7.1 RTOS with default network stack (io-pkt) | +[`x86_64-pc-nto-qnx710_iosock`](platform-support/nto-qnx.md) | ✓ | | x86 64-bit QNX Neutrino 7.1 RTOS with new network stack (io-sock) | +[`x86_64-pc-nto-qnx800`](platform-support/nto-qnx.md) | ✓ | | x86 64-bit QNX Neutrino 8.0 RTOS | [`x86_64-unikraft-linux-musl`](platform-support/unikraft-linux-musl.md) | ✓ | | 64-bit Unikraft with musl 1.2.3 `x86_64-unknown-dragonfly` | ✓ | ✓ | 64-bit DragonFlyBSD `x86_64-unknown-haiku` | ✓ | ✓ | 64-bit Haiku @@ -406,7 +421,8 @@ target | std | host | notes [`x86_64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 64-bit OpenBSD [`x86_64-unknown-trusty`](platform-support/trusty.md) | ? | | `x86_64-uwp-windows-gnu` | ✓ | | -`x86_64-uwp-windows-msvc` | ✓ | | +[`x86_64-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | ✓ | | +[`x86_64-win7-windows-gnu`](platform-support/win7-windows-gnu.md) | ✓ | | 64-bit Windows 7 support [`x86_64-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 64-bit Windows 7 support [`x86_64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [`x86_64h-apple-darwin`](platform-support/x86_64h-apple-darwin.md) | ✓ | ✓ | macOS with late-gen Intel (at least Haswell) @@ -418,3 +434,4 @@ target | std | host | notes [`xtensa-esp32s3-none-elf`](platform-support/xtensa.md) | * | | Xtensa ESP32-S3 [runs on NVIDIA GPUs]: https://github.com/japaric-archived/nvptx#targets +[the AMD GPU]: https://llvm.org/docs/AMDGPUUsage.html#processors diff --git a/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md b/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md new file mode 100644 index 0000000000000..0b2f798e66de1 --- /dev/null +++ b/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md @@ -0,0 +1,111 @@ +# `amdgcn-amd-amdhsa` + +**Tier: 3** + +AMD GPU target for compute/HSA (Heterogeneous System Architecture). + +## Target maintainers + +- [@Flakebi](https://github.com/Flakebi) + +## Requirements + +AMD GPUs can be targeted via cross-compilation. +Supported GPUs depend on the LLVM version that is used by Rust. +In general, most GPUs starting from gfx7 (Sea Islands/CI) are supported as compilation targets, though older GPUs are not supported by the latest host runtime. +Details about supported GPUs can be found in [LLVM’s documentation] and [ROCm documentation]. + +Binaries can be loaded by [HIP] or by the HSA runtime implemented in [ROCR-Runtime]. +The format of binaries is a linked ELF. + +Binaries must be built with no-std. +They can use `core` and `alloc` (`alloc` only if an allocator is supplied). +At least one function needs to use the `"gpu-kernel"` calling convention and should be marked with `no_mangle` for simplicity. +Functions using the `"gpu-kernel"` calling convention are kernel entrypoints and can be used from the host runtime. + +## Building the target + +The target is included in rustc. + +## Building Rust programs + +The amdgpu target supports many hardware generations, which need different binaries. +The generations are exposed as different target-cpus in the backend. +As there are many, Rust does not ship pre-compiled libraries for this target. +Therefore, you have to build your own copy of `core` by using `cargo -Zbuild-std=core` or similar. + +To build a binary, create a no-std library: +```rust,ignore (platform-specific) +// src/lib.rs +#![feature(abi_gpu_kernel)] +#![no_std] + +#[panic_handler] +fn panic(_: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[no_mangle] +pub extern "gpu-kernel" fn kernel(/* Arguments */) { + // Code +} +``` + +Build the library as `cdylib`: +```toml +# Cargo.toml +[lib] +crate-type = ["cdylib"] + +[profile.dev] +lto = true # LTO must be explicitly enabled for now +[profile.release] +lto = true +``` + +The target-cpu must be from the list [supported by LLVM] (or printed with `rustc --target amdgcn-amd-amdhsa --print target-cpus`). +The GPU version on the current system can be found e.g. with [`rocminfo`]. + +Example `.cargo/config.toml` file to set the target and GPU generation: +```toml +# .cargo/config.toml +[build] +target = "amdgcn-amd-amdhsa" +rustflags = ["-Ctarget-cpu=gfx1100"] + +[unstable] +build-std = ["core"] # Optional: "alloc" +``` + +## Running Rust programs + +To run a binary on an AMD GPU, a host runtime is needed. +On Linux and Windows, [HIP] can be used to load and run binaries. +Example code on how to load a compiled binary and run it is available in [ROCm examples]. + +On Linux, binaries can also run through the HSA runtime as implemented in [ROCR-Runtime]. + + + + + +## Additional information + +More information can be found on the [LLVM page for amdgpu]. + +[LLVM’s documentation]: https://llvm.org/docs/AMDGPUUsage.html#processors +[ROCm documentation]: https://rocmdocs.amd.com +[HIP]: https://rocm.docs.amd.com/projects/HIP/ +[ROCR-Runtime]: https://github.com/ROCm/ROCR-Runtime +[supported by LLVM]: https://llvm.org/docs/AMDGPUUsage.html#processors +[LLVM page for amdgpu]: https://llvm.org/docs/AMDGPUUsage.html +[`rocminfo`]: https://github.com/ROCm/rocminfo +[ROCm examples]: https://github.com/ROCm/rocm-examples/tree/ca8ef5b6f1390176616cd1c18fbc98785cbc73f6/HIP-Basic/module_api diff --git a/src/doc/rustc/src/platform-support/apple-darwin.md b/src/doc/rustc/src/platform-support/apple-darwin.md index 17ea225805b5c..dba2c4b2aaf0e 100644 --- a/src/doc/rustc/src/platform-support/apple-darwin.md +++ b/src/doc/rustc/src/platform-support/apple-darwin.md @@ -30,6 +30,18 @@ The current default deployment target for `rustc` can be retrieved with [deployment target]: https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/cross_development/Configuring/configuring.html [rustc-print]: ../command-line-arguments.md#option-print +### Host tooling + +The minimum supported OS versions for the host tooling (`rustc`, `cargo`, +etc.) are currently the same as for applications, namely 10.12 on x86 and 11.0 +on ARM64. +The minimum supported Xcode version is 9.2. + +Building from source likely requires that you can build LLVM from source too, +which [currently][llvm-os] requires Xcode 10.0 and macOS 10.13 (for LLVM 19). + +[llvm-os]: https://releases.llvm.org/19.1.0/docs/GettingStarted.html#host-c-toolchain-both-compiler-and-standard-library + ### Binary format The default binary format is Mach-O, the executable format used on Apple's diff --git a/src/doc/rustc/src/platform-support/m68k-unknown-none-elf.md b/src/doc/rustc/src/platform-support/m68k-unknown-none-elf.md new file mode 100644 index 0000000000000..92780cb5a5ca5 --- /dev/null +++ b/src/doc/rustc/src/platform-support/m68k-unknown-none-elf.md @@ -0,0 +1,108 @@ +# m68k-unknown-none-elf + +**Tier: 3** + +Bare metal Motorola 680x0 + +## Designated Developers + +* [@knickish](https://github.com/knickish) + +## Requirements + +This target requires an m68k build environment for cross-compilation which +is available on Debian, Debian-based systems, openSUSE, and other distributions. + +On Debian-based systems, it should be sufficient to install a g++ cross-compiler for the m68k +architecture which will automatically pull in additional dependencies such as +the glibc cross development package: + +```sh +apt install g++-m68k-linux-gnu +``` + +Binaries can be run using QEMU user emulation. On Debian-based systems, it should be +sufficient to install the package `qemu-user-static` to be able to run simple static +binaries: + +```text +# apt install qemu-user-static +``` + +To run more complex programs, it will be necessary to set up a Debian/m68k chroot with +the help of the command `debootstrap`: + +```text +# apt install debootstrap debian-ports-archive-keyring +# debootstrap --keyring=/usr/share/keyrings/debian-ports-archive-keyring.gpg --arch=m68k unstable debian-68k http://ftp.ports.debian.org/debian-ports +``` + +This chroot can then seamlessly entered using the normal `chroot` command thanks to +QEMU user emulation: + +```text +# chroot /path/to/debian-68k +``` + +To get started with native builds, which are currently untested, a native Debian/m68k +system can be installed either on real hardware such as 68k-based Commodore Amiga or +Atari systems or emulated environments such as QEMU version 4.2 or newer or ARAnyM. + +ISO images for installation are provided by the Debian Ports team and can be obtained +from the Debian CD image server available at: + +[https://cdimage.debian.org/cdimage/ports/current](https://cdimage.debian.org/cdimage/ports/current/) + +Documentation for Debian/m68k is available on the Debian Wiki at: + +[https://wiki.debian.org/M68k](https://wiki.debian.org/M68k) + +Support is available either through the `debian-68k` mailing list: + +[https://lists.debian.org/debian-68k/](https://lists.debian.org/debian-68k/) + +or the `#debian-68k` IRC channel on OFTC network. + +## Building + +At least llvm version `19.1.5` is required to build `core` and `alloc` for this target, and currently the gnu linker is required, as `lld` has no support for the `m68k` architecture + +## Cross-compilation + +This target can be cross-compiled from a standard Debian or Debian-based, openSUSE or any +other distribution which has a basic m68k cross-toolchain available. + +## Testing + +Currently there is no support to run the rustc test suite for this target. + +## Building Rust programs + +Recommended `.cargo/config.toml`: +```toml +[unstable] +build-std = ["panic_abort", "core", "alloc"] + +[target.m68k-unknown-none-elf] +# as we're building for ELF, the m68k-linux linker should be adequate +linker = "m68k-linux-gnu-ld" + +# the mold linker also supports m68k, remove the above line and uncomment the +# following ones to use that instead +# linker = "clang" +# rustflags = ["-C", "link-arg=-fuse-ld=/path/to/mold/binary"] +``` + +Rust programs can be built for this target using: + +```sh +cargo build --target m68k-unknown-none-elf +``` + +Very simple programs can be run using the `qemu-m68k-static` program: + +```sh +qemu-m68k-static your-code +``` + +For more complex applications, a chroot or native m68k system is required for testing. diff --git a/src/doc/rustc/src/platform-support/mips-mti-none-elf.md b/src/doc/rustc/src/platform-support/mips-mti-none-elf.md new file mode 100644 index 0000000000000..731f0a8c42f18 --- /dev/null +++ b/src/doc/rustc/src/platform-support/mips-mti-none-elf.md @@ -0,0 +1,31 @@ +# `mips*-mti-none-elf` + +**Tier: 3** + +MIPS32r2 baremetal softfloat, Big Endian or Little Endian. + +- mips-mti-none-elf +- mipsel-mti-none-elf + +## Target maintainers + +- YunQiang Su, `syq@debian.org`, https://github.com/wzssyqa + +## Background + +These 2 targets, aka mips-mti-none-elf and mipsel-mti-none-elf, are for +baremetal development of MIPS32r2. The lld is used instead of Gnu-ld. + +## Requirements + +The target only supports cross compilation and no host tools. The target +supports `alloc` with a default allocator while only support `no-std` development. + +The vendor name `mti` follows the naming of gcc to indicate MIPS32r2. + +## Cross-compilation toolchains and C code + +Compatible C code can be built for this target on any compiler that has a MIPS32r2 +target. On clang and ld.lld linker, it can be generated using the +`-march=mips`/`-march=mipsel`, `-mabi=32` with llvm features flag +`features=+mips32r2,+soft-float,+noabicalls`. diff --git a/src/doc/rustc/src/platform-support/nto-qnx.md b/src/doc/rustc/src/platform-support/nto-qnx.md index 1c240d1255a3d..339741f14727d 100644 --- a/src/doc/rustc/src/platform-support/nto-qnx.md +++ b/src/doc/rustc/src/platform-support/nto-qnx.md @@ -2,11 +2,13 @@ **Tier: 3** -[QNX®][BlackBerry] Neutrino (nto) Real-time operating system. -The support has been implemented jointly by [Elektrobit Automotive GmbH][Elektrobit] -and [Blackberry QNX][BlackBerry]. +The [QNX®][qnx.com] Neutrino (nto) Real-time operating system. Known as QNX OS +from version 8 onwards. -[BlackBerry]: https://blackberry.qnx.com +This support has been implemented jointly by [Elektrobit Automotive GmbH][Elektrobit] +and [QNX][qnx.com]. + +[qnx.com]: https://blackberry.qnx.com [Elektrobit]: https://www.elektrobit.com ## Target maintainers @@ -18,21 +20,29 @@ and [Blackberry QNX][BlackBerry]. ## Requirements -Currently, the following QNX Neutrino versions and compilation targets are supported: +Currently, the following QNX versions and compilation targets are supported: + +| Target Tuple | QNX Version | Target Architecture | Full support | `no_std` support | +| ----------------------------------- | ----------------------------- | ------------------- | :----------: | :--------------: | +| `aarch64-unknown-nto-qnx800` | QNX OS 8.0 | AArch64 | ? | ✓ | +| `x86_64-pc-nto-qnx800` | QNX OS 8.0 | x86_64 | ? | ✓ | +| `aarch64-unknown-nto-qnx710` | QNX Neutrino 7.1 with io-pkt | AArch64 | ✓ | ✓ | +| `x86_64-pc-nto-qnx710` | QNX Neutrino 7.1 with io-pkt | x86_64 | ✓ | ✓ | +| `aarch64-unknown-nto-qnx710_iosock` | QNX Neutrino 7.1 with io-sock | AArch64 | ? | ✓ | +| `x86_64-pc-nto-qnx710_iosock` | QNX Neutrino 7.1 with io-sock | x86_64 | ? | ✓ | +| `aarch64-unknown-nto-qnx700` | QNX Neutrino 7.0 | AArch64 | ? | ✓ | +| `i586-pc-nto-qnx700` | QNX Neutrino 7.0 | x86 | | ✓ | -| QNX Neutrino Version | Target Architecture | Full support | `no_std` support | -|----------------------|---------------------|:------------:|:----------------:| -| 7.1 | AArch64 | ✓ | ✓ | -| 7.1 | x86_64 | ✓ | ✓ | -| 7.0 | AArch64 | ? | ✓ | -| 7.0 | x86 | | ✓ | +On QNX Neutrino 7.0 and 7.1, `io-pkt` is used as network stack by default. +QNX Neutrino 7.1 includes the optional network stack `io-sock`. +QNX OS 8.0 always uses `io-sock`. QNX OS 8.0 support is currently work in progress. -Adding other architectures that are supported by QNX Neutrino is possible. +Adding other architectures that are supported by QNX is possible. -In the table above, 'full support' indicates support for building Rust applications with the full standard library. -'`no_std` support' indicates that only `core` and `alloc` are available. +In the table above, 'full support' indicates support for building Rust applications with the full standard library. A '?' means that support is in-progress. +'`no_std` support' is for building `#![no_std]` applications where only `core` and `alloc` are available. -For building or using the Rust toolchain for QNX Neutrino, the +For building or using the Rust toolchain for QNX, the [QNX Software Development Platform (SDP)](https://blackberry.qnx.com/en/products/foundation-software/qnx-software-development-platform) must be installed and initialized. Initialization is usually done by sourcing `qnxsdp-env.sh` (this will be installed as part of the SDP, see also installation instruction provided with the SDP). @@ -78,7 +88,7 @@ extern "C" { } #[no_mangle] -pub extern "C" fn main(_argc: isize, _argv: *const *const u8) -> isize { +pub extern "C" fn main(_argc: core::ffi::c_int, _argv: *const *const u8) -> core::ffi::c_int { const HELLO: &'static str = "Hello World, the answer is %d\n\0"; unsafe { printf(HELLO.as_ptr() as *const _, 42); @@ -98,52 +108,73 @@ fn panic(_panic: &PanicInfo<'_>) -> ! { pub extern "C" fn rust_eh_personality() {} ``` -The QNX Neutrino support of Rust has been tested with QNX Neutrino 7.0 and 7.1. +The QNX support in Rust has been tested with QNX Neutrino 7.0 and 7.1. Support for QNX OS 8.0 is a work in progress. There are no further known requirements. ## Conditional compilation -For conditional compilation, following QNX Neutrino specific attributes are defined: +For conditional compilation, following QNX specific attributes are defined: - `target_os` = `"nto"` -- `target_env` = `"nto71"` (for QNX Neutrino 7.1) +- `target_env` = `"nto71"` (for QNX Neutrino 7.1 with "classic" network stack "io_pkt") +- `target_env` = `"nto71_iosock"` (for QNX Neutrino 7.1 with network stack "io_sock") - `target_env` = `"nto70"` (for QNX Neutrino 7.0) +- `target_env` = `"nto80"` (for QNX OS 8.0) ## Building the target 1. Create a `config.toml` -Example content: + Example content: -```toml -profile = "compiler" -change-id = 115898 -``` + ```toml + profile = "compiler" + change-id = 999999 + ``` -2. Compile the Rust toolchain for an `x86_64-unknown-linux-gnu` host (for both `aarch64` and `x86_64` targets) +2. Compile the Rust toolchain for an `x86_64-unknown-linux-gnu` host -Compiling the Rust toolchain requires the same environment variables used for compiling C binaries. -Refer to the [QNX developer manual](https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.prog/topic/devel_OS_version.html). + Compiling the Rust toolchain requires the same environment variables used for compiling C binaries. + Refer to the [QNX developer manual](https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.prog/topic/devel_OS_version.html). -To compile for QNX Neutrino (aarch64 and x86_64) and Linux (x86_64): + To compile for QNX, environment variables must be set to use the correct tools and compiler switches: -```bash -export build_env=' - CC_aarch64-unknown-nto-qnx710=qcc - CFLAGS_aarch64-unknown-nto-qnx710=-Vgcc_ntoaarch64le_cxx - CXX_aarch64-unknown-nto-qnx710=qcc - AR_aarch64_unknown_nto_qnx710=ntoaarch64-ar - CC_x86_64-pc-nto-qnx710=qcc - CFLAGS_x86_64-pc-nto-qnx710=-Vgcc_ntox86_64_cxx - CXX_x86_64-pc-nto-qnx710=qcc - AR_x86_64_pc_nto_qnx710=ntox86_64-ar' + - `CC_=qcc` + - `CFLAGS_=` + - `CXX_=qcc` + - `AR_=` -env $build_env \ - ./x.py build \ - --target aarch64-unknown-nto-qnx710,x86_64-pc-nto-qnx710,x86_64-unknown-linux-gnu \ - rustc library/core library/alloc library/std -``` + With: + + - `` target triplet using underscores instead of hyphens, e.g. `aarch64_unknown_nto_qnx710` + - `` + + - `-Vgcc_ntox86_cxx` for x86 (32 bit) + - `-Vgcc_ntox86_64_cxx` for x86_64 (64 bit) + - `-Vgcc_ntoaarch64le_cxx` for Aarch64 (64 bit) + + - `` + + - `ntox86-ar` for x86 (32 bit) + - `ntox86_64-ar` for x86_64 (64 bit) + - `ntoaarch64-ar` for Aarch64 (64 bit) + + Example to build the Rust toolchain including a standard library for x86_64-linux-gnu and Aarch64-QNX-7.1: + + ```bash + export build_env=' + CC_aarch64_unknown_nto_qnx710=qcc + CFLAGS_aarch64_unknown_nto_qnx710=-Vgcc_ntoaarch64le_cxx + CXX_aarch64_unknown_nto_qnx710=qcc + AR_aarch64_unknown_nto_qnx710=ntoaarch64-ar + ' + + env $build_env \ + ./x.py build \ + --target x86_64-unknown-linux-gnu,aarch64-unknown-nto-qnx710 \ + rustc library/core library/alloc library/std + ``` ## Running the Rust test suite @@ -153,19 +184,11 @@ addition of the TEST_DEVICE_ADDR environment variable. The TEST_DEVICE_ADDR variable controls the remote runner and should point to the target, despite localhost being shown in the following example. Note that some tests are failing which is why they are currently excluded by the target maintainers which can be seen in the following example. -To run all tests on a x86_64 QNX Neutrino target: +To run all tests on a x86_64 QNX Neutrino 7.1 target: ```bash export TEST_DEVICE_ADDR="localhost:12345" # must address the test target, can be a SSH tunnel -export build_env=' - CC_aarch64-unknown-nto-qnx710=qcc - CFLAGS_aarch64-unknown-nto-qnx710=-Vgcc_ntoaarch64le_cxx - CXX_aarch64-unknown-nto-qnx710=qcc - AR_aarch64_unknown_nto_qnx710=ntoaarch64-ar - CC_x86_64-pc-nto-qnx710=qcc - CFLAGS_x86_64-pc-nto-qnx710=-Vgcc_ntox86_64_cxx - CXX_x86_64-pc-nto-qnx710=qcc - AR_x86_64_pc_nto_qnx710=ntox86_64-ar' +export build_env= # Disable tests that only work on the host or don't make sense for this target. # See also: @@ -195,7 +218,7 @@ or build your own copy of `core` by using `build-std` or similar. ## Testing -Compiled executables can run directly on QNX Neutrino. +Compiled executables can run directly on QNX. ### Rust std library test suite diff --git a/src/doc/rustc/src/platform-support/nuttx.md b/src/doc/rustc/src/platform-support/nuttx.md index 433a092aab22a..f76fe0887b5dd 100644 --- a/src/doc/rustc/src/platform-support/nuttx.md +++ b/src/doc/rustc/src/platform-support/nuttx.md @@ -20,8 +20,13 @@ The target name follow this format: `ARCH[-VENDOR]-nuttx-ABI`, where `ARCH` is t The following target names are defined: -- `thumbv6m-nuttx-eal` -- `thumbv7m-nuttx-eal` +- `aarch64-unknown-nuttx` +- `armv7a-nuttx-eabi` +- `armv7a-nuttx-eabihf` +- `thumbv6m-nuttx-eabi` +- `thumbv7a-nuttx-eabi` +- `thumbv7a-nuttx-eabihf` +- `thumbv7m-nuttx-eabi` - `thumbv7em-nuttx-eabi` - `thumbv7em-nuttx-eabihf` - `thumbv8m.base-nuttx-eabi` diff --git a/src/doc/rustc/src/platform-support/redox.md b/src/doc/rustc/src/platform-support/redox.md index 1b3321956ef7b..2bba92d504c46 100644 --- a/src/doc/rustc/src/platform-support/redox.md +++ b/src/doc/rustc/src/platform-support/redox.md @@ -9,7 +9,7 @@ Target triplets available so far: - `x86_64-unknown-redox` (tier 2) - `aarch64-unknown-redox` (tier 3) -- `i686-unknown-redox` (tier 3) +- `i586-unknown-redox` (tier 3) ## Target maintainers @@ -36,7 +36,7 @@ target = [ "", "x86_64-unknown-redox", "aarch64-unknown-redox", - "i686-unknown-redox", + "i586-unknown-redox", ] ``` diff --git a/src/doc/rustc/src/platform-support/uwp-windows-msvc.md b/src/doc/rustc/src/platform-support/uwp-windows-msvc.md new file mode 100644 index 0000000000000..ce2ebb686fa50 --- /dev/null +++ b/src/doc/rustc/src/platform-support/uwp-windows-msvc.md @@ -0,0 +1,52 @@ +# `x86_64-uwp-windows-msvc`, `i686-uwp-windows-msvc`, `thumbv7a-uwp-windows-msvc` and `aarch64-uwp-windows-msvc` + +**Tier: 3** + +Windows targets for Universal Windows Platform (UWP) applications, using MSVC toolchain. + +## Target maintainers + +- [@bdbai](https://github.com/bdbai) + +## Requirements + +These targets are cross-compiled with std support. The host requirement and +binary format are the same as the corresponding non-UWP targets (i.e. +`x86_64-pc-windows-msvc`, `i686-pc-windows-msvc`, `thumbv7a-pc-windows-msvc` +and `aarch64-pc-windows-msvc`). + +## Building the targets + +The targets can be built by enabling them for a `rustc` build, for example: + +```toml +[build] +build-stage = 1 +target = ["x86_64-uwp-windows-msvc", "aarch64-uwp-windows-msvc"] +``` + +## Building Rust programs + +Rust does not yet ship pre-compiled artifacts for these targets. To compile for +these targets, you will either need to build Rust with the targets enabled (see +"Building the targets" above), or build your own copy of `std` by using +`build-std` or similar. + +Example of building a Rust project for x64 UWP using `build-std`: + +```pwsh +cargo build -Z build-std=std,panic_abort --target x86_64-uwp-windows-msvc +``` + +## Testing + +Currently there is no support to run the rustc test suite for this target. + +## Cross-compilation toolchains and C code + +In general, the toolchain target should match the corresponding non-UWP +targets. Beware that not all Win32 APIs behave the same way in UWP, and some +are restricted in [AppContainer](https://learn.microsoft.com/en-us/windows/win32/secauthz/appcontainer-for-legacy-applications-) +or even not available at all. If the C code being compiled happens to use any +of restricted or unavailable APIs, consider using allowed alternatives or +disable certain feature sets to avoid using them. diff --git a/src/doc/rustc/src/platform-support/wasm32-unknown-emscripten.md b/src/doc/rustc/src/platform-support/wasm32-unknown-emscripten.md index 7a9cd4b522b9c..d364852b1c1dc 100644 --- a/src/doc/rustc/src/platform-support/wasm32-unknown-emscripten.md +++ b/src/doc/rustc/src/platform-support/wasm32-unknown-emscripten.md @@ -118,7 +118,7 @@ This target is not extensively tested in CI for the rust-lang/rust repository. I can be tested locally, for example, with: ```sh -./x.py test --target wasm32-unknown-emscripten --skip src/tools/linkchecker +EMCC_CFLAGS="-s MAXIMUM_MEMORY=2GB" ./x.py test --target wasm32-unknown-emscripten --skip src/tools/linkchecker ``` To run these tests, both `emcc` and `node` need to be in your `$PATH`. You can diff --git a/src/doc/rustc/src/platform-support/win7-windows-gnu.md b/src/doc/rustc/src/platform-support/win7-windows-gnu.md new file mode 100644 index 0000000000000..180a1dc6d2650 --- /dev/null +++ b/src/doc/rustc/src/platform-support/win7-windows-gnu.md @@ -0,0 +1,48 @@ +# \*-win7-windows-gnu + +**Tier: 3** + +Windows targets continuing support of Windows 7. + +Target triples: +- `i686-win7-windows-gnu` +- `x86_64-win7-windows-gnu` + +## Target maintainers + +- @tbu- + +## Requirements + +This target supports all of core, alloc, std and test. Host +tools may also work, though those are not currently tested. + +Those targets follow Windows calling convention for extern "C". + +Like any other Windows target, the created binaries are in PE format. + +## Building the target + +You can build Rust with support for the targets by adding it to the target list in config.toml: + +```toml +[build] +build-stage = 1 +target = ["x86_64-win7-windows-gnu"] +``` + +## Building Rust programs + +Rust does not ship pre-compiled artifacts for this target. To compile for this +target, you will either need to build Rust with the target enabled (see +"Building the target" above), or build your own copy by using `build-std` or +similar. + +## Testing + +Created binaries work fine on Windows or Wine using native hardware. Remote +testing is possible using the `remote-test-server` described [here](https://rustc-dev-guide.rust-lang.org/tests/running.html#running-tests-on-a-remote-machine). + +## Cross-compilation toolchains and C code + +Compatible C code can be built with gcc's `{i686,x86_64}-w64-mingw32-gcc`. diff --git a/src/doc/rustc/src/platform-support/win7-windows-msvc.md b/src/doc/rustc/src/platform-support/win7-windows-msvc.md index 45b00a2be8294..77b7d68212bf3 100644 --- a/src/doc/rustc/src/platform-support/win7-windows-msvc.md +++ b/src/doc/rustc/src/platform-support/win7-windows-msvc.md @@ -1,8 +1,12 @@ -# *-win7-windows-msvc +# \*-win7-windows-msvc **Tier: 3** -Windows targets continuing support of windows7. +Windows targets continuing support of Windows 7. + +Target triples: +- `i686-win7-windows-msvc` +- `x86_64-win7-windows-msvc` ## Target maintainers diff --git a/src/doc/rustc/src/platform-support/x86_64-pc-cygwin.md b/src/doc/rustc/src/platform-support/x86_64-pc-cygwin.md new file mode 100644 index 0000000000000..a8fc4f181d8ad --- /dev/null +++ b/src/doc/rustc/src/platform-support/x86_64-pc-cygwin.md @@ -0,0 +1,39 @@ +# `x86_64-pc-cygwin` + +**Tier: 3** + +Windows targets supporting Cygwin. +The `*-cygwin` targets are **not** intended as native target for applications, +a developer writing Windows applications should use the `*-pc-windows-*` targets instead, which are *native* Windows. + +Cygwin is only intended as an emulation layer for Unix-only programs which do not support the native Windows targets. + +## Target maintainers + +- [Berrysoft](https://github.com/Berrysoft) + +## Requirements + +This target is cross compiled. It needs `x86_64-pc-cygwin-gcc` as linker. + +The `target_os` of the target is `cygwin`, and it is `unix`. + +## Building the target + +For cross-compilation you want LLVM with [llvm/llvm-project#121439 (merged)](https://github.com/llvm/llvm-project/pull/121439) applied to fix the LLVM codegen on importing external global variables from DLLs. +No native builds on Cygwin now. It should be possible theoretically though, but might need a lot of patches. + +## Building Rust programs + +Rust does not yet ship pre-compiled artifacts for this target. To compile for +this target, you will either need to build Rust with the target enabled (see +"Building the target" above), or build your own copy of `core` by using +`build-std` or similar. + +## Testing + +Created binaries work fine on Windows with Cygwin. + +## Cross-compilation toolchains and C code + +Compatible C code can be built with GCC shipped with Cygwin. Clang is untested. diff --git a/src/doc/rustc/src/profile-guided-optimization.md b/src/doc/rustc/src/profile-guided-optimization.md index 38b655b75422b..4050494793a32 100644 --- a/src/doc/rustc/src/profile-guided-optimization.md +++ b/src/doc/rustc/src/profile-guided-optimization.md @@ -3,7 +3,7 @@ `rustc` supports doing profile-guided optimization (PGO). This chapter describes what PGO is, what it is good for, and how it can be used. -## What Is Profiled-Guided Optimization? +## What Is Profile-Guided Optimization? The basic concept of PGO is to collect data about the typical execution of a program (e.g. which branches it is likely to take) and then use this data diff --git a/src/doc/rustc/src/target-tier-policy.md b/src/doc/rustc/src/target-tier-policy.md index bdcf2c0b07efc..a0acfdf0e4ab5 100644 --- a/src/doc/rustc/src/target-tier-policy.md +++ b/src/doc/rustc/src/target-tier-policy.md @@ -122,12 +122,23 @@ To propose addition of a new target, open a pull request on [`rust-lang/rust`]: r? compiler ``` +See also the documentation in the `rustc-dev-guide` on [adding a new target to +`rustc`][rustc_dev_guide_add_target]. + +Note that adding a new target that wants to support `std` would transitively +require `cc` and `libc` support. However, these would like to know about the +target from `rustc` as well. To break this cycle, you are strongly encouraged +to add a _minimal_ `#![no_core]` target spec first to teach `rustc` about the +target's existence, and add `std` support as a follow-up once you've added +support for the target in `cc` and `libc`. + [tier3example]: https://github.com/rust-lang/rust/pull/94872 [platform_template]: https://github.com/rust-lang/rust/blob/master/src/doc/rustc/src/platform-support/TEMPLATE.md [summary]: https://github.com/rust-lang/rust/blob/master/src/doc/rustc/src/SUMMARY.md [platformsupport]: https://github.com/rust-lang/rust/blob/master/src/doc/rustc/src/platform-support.md [rust_compiler_team]: https://www.rust-lang.org/governance/teams/compiler [`rust-lang/rust`]: https://github.com/rust-lang/rust +[rustc_dev_guide_add_target]: https://rustc-dev-guide.rust-lang.org/building/new-target.html ## Tier 3 target policy diff --git a/src/doc/rustc/src/tests/index.md b/src/doc/rustc/src/tests/index.md index 32baed9c94498..d2026513d2f0d 100644 --- a/src/doc/rustc/src/tests/index.md +++ b/src/doc/rustc/src/tests/index.md @@ -268,6 +268,8 @@ Controls the format of the output. Valid options: Writes the results of the tests to the given file. +This option is deprecated. + #### `--report-time` ⚠️ 🚧 This option is [unstable](#unstable-options), and requires the `-Z diff --git a/src/doc/rustdoc/src/read-documentation/search.md b/src/doc/rustdoc/src/read-documentation/search.md index e06dcdb7ed2fb..bace2f5f95390 100644 --- a/src/doc/rustdoc/src/read-documentation/search.md +++ b/src/doc/rustdoc/src/read-documentation/search.md @@ -52,9 +52,10 @@ methods on the allocator or free functions. [`Layout`]: ../../alloc/index.html?search=Layout&filter-crate=alloc -## Searching By Type Signature for functions +## Searching By Type Signature If you know more specifically what the function you want to look at does, +or you want to know how to get from one type to another, Rustdoc can search by more than one type at once in the parameters and return value. Multiple parameters are separated by `,` commas, and the return value is written with after a `->` arrow. @@ -86,6 +87,17 @@ the standard library and functions that are included in the results list: [iterasslice]: ../../std/vec/struct.Vec.html?search=vec%3A%3Aintoiter%20->%20[T]&filter-crate=std [iterreduce]: ../../std/index.html?search=iterator%2C%20fnmut%20->%20T&filter-crate=std +### Non-functions in type-based search +Certain items that are not functions are treated as though they +were a semantically equivelent function. + +For example, struct fields are treated as though they were getter methods. +This means that a search for `CpuidResult -> u32` will show +the `CpuidResult::eax` field in the results. + +Additionally, `const` and `static` items are treated as nullary functions, +so `-> u32` will match `u32::MAX`. + ### How type-based search works In a complex type-based search, Rustdoc always treats every item's name as literal. diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index d1d42e4732258..3abd538d372c4 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -330,7 +330,7 @@ the source. ## `--show-type-layout`: add a section to each type's docs describing its memory layout -* Tracking issue: [#113248](https://github.com/rust-lang/rust/issues/113248) + * Tracking issue: [#113248](https://github.com/rust-lang/rust/issues/113248) Using this flag looks like this: @@ -524,9 +524,12 @@ use `-o -`. ## `-w`/`--output-format`: output format +### json + + * Tracking Issue: [#76578](https://github.com/rust-lang/rust/issues/76578) + `--output-format json` emits documentation in the experimental -[JSON format](https://doc.rust-lang.org/nightly/nightly-rustc/rustdoc_json_types/). `--output-format html` has no effect, -and is also accepted on stable toolchains. +[JSON format](https://doc.rust-lang.org/nightly/nightly-rustc/rustdoc_json_types/). JSON Output for toolchain crates (`std`, `alloc`, `core`, `test`, and `proc_macro`) is available via the `rust-docs-json` rustup component. @@ -542,6 +545,73 @@ It can also be used with `--show-coverage`. Take a look at its [documentation](#--show-coverage-calculate-the-percentage-of-items-with-documentation) for more information. +### doctest + + * Tracking issue: [#134529](https://github.com/rust-lang/rust/issues/134529) + +`--output-format doctest` emits JSON on stdout which gives you information about doctests in the +provided crate. + +You can use this option like this: + +```bash +rustdoc -Zunstable-options --output-format=doctest src/lib.rs +``` + +For this rust code: + +```rust +/// ``` +/// let x = 12; +/// ``` +pub trait Trait {} +``` + +The generated output (formatted) will look like this: + +```json +{ + "format_version": 1, + "doctests": [ + { + "file": "foo.rs", + "line": 1, + "doctest_attributes": { + "original": "", + "should_panic": false, + "no_run": false, + "ignore": "None", + "rust": true, + "test_harness": false, + "compile_fail": false, + "standalone_crate": false, + "error_codes": [], + "edition": null, + "added_css_classes": [], + "unknown": [] + }, + "original_code": "let x = 12;", + "doctest_code": "#![allow(unused)]\nfn main() {\nlet x = 12;\n}", + "name": "foo.rs - Trait (line 1)" + } + ] +} +``` + + * `format_version` gives you the current version of the generated JSON. If we change the output in any way, the number will increase. + * `doctests` contains the list of doctests present in the crate. + * `file` is the file path where the doctest is located. + * `line` is the line where the doctest starts (so where the \`\`\` is located in the current code). + * `doctest_attributes` contains computed information about the attributes used on the doctests. For more information about doctest attributes, take a look [here](write-documentation/documentation-tests.html#attributes). + * `original_code` is the code as written in the source code before rustdoc modifies it. + * `doctest_code` is the code modified by rustdoc that will be run. If there is a fatal syntax error, this field will not be present. + * `name` is the name generated by rustdoc which represents this doctest. + +### html + +`--output-format html` has no effect, as the default output is HTML. This is +accepted on stable, even though the other options for this flag aren't. + ## `--enable-per-target-ignores`: allow `ignore-foo` style filters for doctests * Tracking issue: [#64245](https://github.com/rust-lang/rust/issues/64245) diff --git a/src/doc/style-guide/src/editions.md b/src/doc/style-guide/src/editions.md index b9a89c20cee40..19e62c4867c99 100644 --- a/src/doc/style-guide/src/editions.md +++ b/src/doc/style-guide/src/editions.md @@ -36,10 +36,6 @@ For a full history of changes in the Rust 2024 style edition, see the git history of the style guide. Notable changes in the Rust 2024 style edition include: -- [#114764](https://github.com/rust-lang/rust/pull/114764) As the last member - of a delimited expression, delimited expressions are generally combinable, - regardless of the number of members. Previously only applied with exactly - one member (except for closures with explicit blocks). - Miscellaneous `rustfmt` bugfixes. - Use version-sort (sort `x8`, `x16`, `x32`, `x64`, `x128` in that order). - Change "ASCIIbetical" sort to Unicode-aware "non-lowercase before lowercase". diff --git a/src/doc/style-guide/src/expressions.md b/src/doc/style-guide/src/expressions.md index 171a24cd89d73..12037b5992ec0 100644 --- a/src/doc/style-guide/src/expressions.md +++ b/src/doc/style-guide/src/expressions.md @@ -818,11 +818,11 @@ E.g., `&&Some(foo)` matches, `Foo(4, Bar)` does not. ## Combinable expressions -When the last argument in a function call is formatted across -multiple-lines, format the outer call as if it were a single-line call, +Where a function call has a single argument, and that argument is formatted +across multiple-lines, format the outer call as if it were a single-line call, if the result fits. Apply the same combining behaviour to any similar expressions which have multi-line, block-indented lists of sub-expressions -delimited by parentheses, brackets, or braces. E.g., +delimited by parentheses (e.g., macros or tuple struct literals). E.g., ```rust foo(bar( @@ -848,61 +848,20 @@ let arr = [combinable( an_expr, another_expr, )]; - -let x = Thing(an_expr, another_expr, match cond { - A => 1, - B => 2, -}); - -let x = format!("Stuff: {}", [ - an_expr, - another_expr, -]); - -let x = func(an_expr, another_expr, SomeStruct { - field: this_is_long, - another_field: 123, -}); ``` Apply this behavior recursively. -If the last argument is a multi-line closure with an explicit block, -only apply the combining behavior if there are no other closure arguments. +For a function with multiple arguments, if the last argument is a multi-line +closure with an explicit block, there are no other closure arguments, and all +the arguments and the first line of the closure fit on the first line, use the +same combining behavior: ```rust -// Combinable foo(first_arg, x, |param| { action(); foo(param) }) -// Not combinable, because the closure is not the last argument -foo( - first_arg, - |param| { - action(); - foo(param) - }, - whatever, -) -// Not combinable, because the first line of the closure does not fit -foo( - first_arg, - x, - move |very_long_param_causing_line_to_overflow| -> Bar { - action(); - foo(param) - }, -) -// Not combinable, because there is more than one closure argument -foo( - first_arg, - |x| x.bar(), - |param| { - action(); - foo(param) - }, -) ``` ## Ranges diff --git a/src/doc/unstable-book/src/compiler-flags/autodiff.md b/src/doc/unstable-book/src/compiler-flags/autodiff.md new file mode 100644 index 0000000000000..4e55be0ad95a0 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/autodiff.md @@ -0,0 +1,23 @@ +# `autodiff` + +The tracking issue for this feature is: [#124509](https://github.com/rust-lang/rust/issues/124509). + +------------------------ + +This feature allows you to differentiate functions using automatic differentiation. +Set the `-Zautodiff=` compiler flag to adjust the behaviour of the autodiff feature. +Multiple options can be separated with a comma. Valid options are: + +`PrintTA` - print Type Analysis Information +`PrintAA` - print Activity Analysis Information +`PrintPerf` - print Performance Warnings from Enzyme +`Print` - prints all intermediate transformations +`PrintModBefore` - print the whole module, before running opts +`PrintModAfterOpts` - print the whole module just before we pass it to Enzyme +`PrintModAfterEnzyme` - print the module after Enzyme differentiated everything +`LooseTypes` - Enzyme's loose type debug helper (can cause incorrect gradients) +`Inline` - runs Enzyme specific Inlining +`NoModOptAfter` - do not optimize the module after Enzyme is done +`EnableFncOpt` - tell Enzyme to run LLVM Opts on each function it generated +`NoVecUnroll` - do not unroll vectorized loops +`RuntimeActivity` - allow specifying activity at runtime diff --git a/src/doc/unstable-book/src/compiler-flags/dwarf-version.md b/src/doc/unstable-book/src/compiler-flags/dwarf-version.md index c5e86f17df741..e88799d2cf044 100644 --- a/src/doc/unstable-book/src/compiler-flags/dwarf-version.md +++ b/src/doc/unstable-book/src/compiler-flags/dwarf-version.md @@ -1,5 +1,9 @@ ## `dwarf-version` +The tracking issue for this feature is: + +---------------------------- + This option controls the version of DWARF that the compiler emits, on platforms that use DWARF to encode debug information. It takes one of the following values: diff --git a/src/doc/unstable-book/src/compiler-flags/emscripten-wasm-eh.md b/src/doc/unstable-book/src/compiler-flags/emscripten-wasm-eh.md new file mode 100644 index 0000000000000..eab29a1744b67 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/emscripten-wasm-eh.md @@ -0,0 +1,6 @@ +# `emscripten-wasm-eh` + +Use the WebAssembly exception handling ABI to unwind for the +`wasm32-unknown-emscripten`. If compiling with this setting, the `emcc` linker +should be invoked with `-fwasm-exceptions`. If linking with C/C++ files, the +C/C++ files should also be compiled with `-fwasm-exceptions`. diff --git a/src/doc/unstable-book/src/compiler-flags/min-function-alignment.md b/src/doc/unstable-book/src/compiler-flags/min-function-alignment.md new file mode 100644 index 0000000000000..b7a3aa71fc4c8 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/min-function-alignment.md @@ -0,0 +1,24 @@ +# `min-function-alignment` + +The tracking issue for this feature is: https://github.com/rust-lang/rust/issues/82232. + +------------------------ + +The `-Zmin-function-alignment=` flag specifies the minimum alignment of functions for which code is generated. +The `align` value must be a power of 2, other values are rejected. + +Note that `-Zbuild-std` (or similar) is required to apply this minimum alignment to standard library functions. +By default, these functions come precompiled and their alignments won't respect the `min-function-alignment` flag. + +This flag is equivalent to: + +- `-fmin-function-alignment` for [GCC](https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-fmin-function-alignment_003dn) +- `-falign-functions` for [Clang](https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang1-falign-functions) + +The specified alignment is a minimum. A higher alignment can be specified for specific functions by using the [`repr(align(...))`](https://github.com/rust-lang/rust/issues/82232) feature and annotating the function with a `#[repr(align())]` attribute. The attribute's value is ignored when it is lower than the value passed to `min-function-alignment`. + +There are two additional edge cases for this flag: + +- targets have a minimum alignment for functions (e.g. on x86_64 the lowest that LLVM generates is 16 bytes). + A `min-function-alignment` value lower than the target's minimum has no effect. +- the maximum alignment supported by rust (and LLVM) is `2^29`. Trying to set a higher value results in an error. diff --git a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md index c2f4170d7d295..d9566c9f55c5c 100644 --- a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md +++ b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md @@ -199,3 +199,5 @@ These flags registers must be restored upon exiting the asm block if the `preser - SPARC - Integer condition codes (`icc` and `xcc`) - Floating-point condition codes (`fcc[0-3]`) +- CSKY + - Condition/carry bit (C) in `PSR`. diff --git a/src/doc/unstable-book/src/language-features/default-field-values.md b/src/doc/unstable-book/src/language-features/default-field-values.md new file mode 100644 index 0000000000000..3143b2d7cae3d --- /dev/null +++ b/src/doc/unstable-book/src/language-features/default-field-values.md @@ -0,0 +1,93 @@ +# `default_field_values` + +The tracking issue for this feature is: [#132162] + +[#132162]: https://github.com/rust-lang/rust/issues/132162 + +The RFC for this feature is: [#3681] + +[#3681]: https://github.com/rust-lang/rfcs/blob/master/text/3681-default-field-values.md + +------------------------ + +The `default_field_values` feature allows users to specify a const value for +individual fields in struct definitions, allowing those to be omitted from +initializers. + +## Examples + +```rust +#![feature(default_field_values)] + +#[derive(Default)] +struct Pet { + name: Option, // impl Default for Pet will use Default::default() for name + age: i128 = 42, // impl Default for Pet will use the literal 42 for age +} + +fn main() { + let a = Pet { name: Some(String::new()), .. }; // Pet { name: Some(""), age: 42 } + let b = Pet::default(); // Pet { name: None, age: 42 } + assert_eq!(a.age, b.age); + // The following would be a compilation error: `name` needs to be specified + // let _ = Pet { .. }; +} +``` + +## `#[derive(Default)]` + +When deriving Default, the provided values are then used. On enum variants, +the variant must still be marked with `#[default]` and have all its fields +with default values. + +```rust +#![feature(default_field_values)] + +#[derive(Default)] +enum A { + #[default] + B { + x: i32 = 0, + y: i32 = 0, + }, + C, +} +``` + +## Enum variants + +This feature also supports enum variants for both specifying default values +and `#[derive(Default)]`. + +## Interaction with `#[non_exhaustive]` + +A struct or enum variant marked with `#[non_exhaustive]` is not allowed to +have default field values. + +## Lints + +When manually implementing the `Default` trait for a type that has default +field values, if any of these are overriden in the impl the +`default_overrides_default_fields` lint will trigger. This lint is in place +to avoid surprising diverging behavior between `S { .. }` and +`S::default()`, where using the same type in both ways could result in +different values. The appropriate way to write a manual `Default` +implementation is to use the functional update syntax: + +```rust +#![feature(default_field_values)] + +struct Pet { + name: String, + age: i128 = 42, // impl Default for Pet will use the literal 42 for age +} + +impl Default for Pet { + fn default() -> Pet { + Pet { + name: "no-name".to_string(), + .. + } + } +} +``` diff --git a/src/doc/unstable-book/src/language-features/extended-varargs-abi-support.md b/src/doc/unstable-book/src/language-features/extended-varargs-abi-support.md new file mode 100644 index 0000000000000..b20c30ec8f1c8 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/extended-varargs-abi-support.md @@ -0,0 +1,10 @@ +# `extended_varargs_abi_support` + +The tracking issue for this feature is: [#100189] + +[#100189]: https://github.com/rust-lang/rust/issues/100189 + +------------------------ + +This feature adds the possibility of using `sysv64`, `win64` or `efiapi` calling +conventions on functions with varargs. diff --git a/src/doc/unstable-book/src/language-features/lang-items.md b/src/doc/unstable-book/src/language-features/lang-items.md index 32b882e763d65..1122bbc5a8786 100644 --- a/src/doc/unstable-book/src/language-features/lang-items.md +++ b/src/doc/unstable-book/src/language-features/lang-items.md @@ -46,14 +46,15 @@ allocation. A freestanding program that uses the `Box` sugar for dynamic allocations via `malloc` and `free`: ```rust,ignore (libc-is-finicky) -#![feature(lang_items, start, core_intrinsics, rustc_private, panic_unwind, rustc_attrs)] +#![feature(lang_items, core_intrinsics, rustc_private, panic_unwind, rustc_attrs)] #![allow(internal_features)] #![no_std] +#![no_main] extern crate libc; extern crate unwind; -use core::ffi::c_void; +use core::ffi::{c_int, c_void}; use core::intrinsics; use core::panic::PanicInfo; use core::ptr::NonNull; @@ -91,8 +92,8 @@ unsafe fn allocate(size: usize, _align: usize) -> *mut u8 { p } -#[start] -fn main(_argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(_argc: c_int, _argv: *const *const u8) -> c_int { let _x = Box::new(1); 0 diff --git a/src/doc/unstable-book/src/language-features/new-range.md b/src/doc/unstable-book/src/language-features/new-range.md new file mode 100644 index 0000000000000..e7464f31e5377 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/new-range.md @@ -0,0 +1,9 @@ +# `new_range` + +The tracking issue for this feature is: [#123741] + +[#123741]: https://github.com/rust-lang/rust/issues/123741 + +--- + +Switch the syntaxes `a..`, `a..b`, and `a..=b` to resolve the new range types. diff --git a/src/doc/unstable-book/src/language-features/ref-pat-eat-one-layer-2024-structural.md b/src/doc/unstable-book/src/language-features/ref-pat-eat-one-layer-2024-structural.md new file mode 100644 index 0000000000000..bc58768611175 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/ref-pat-eat-one-layer-2024-structural.md @@ -0,0 +1,19 @@ +# `ref_pat_eat_one_layer_2024_structural` + +The tracking issue for this feature is: [#123076] + +[#123076]: https://github.com/rust-lang/rust/issues/123076 + +--- + +This feature is incomplete and not yet intended for general use. + +This implements experimental, Edition-dependent match ergonomics under consideration for inclusion +in Rust. +For more information, see the corresponding typing rules for [Editions 2024 and later]. +On earlier Editions, the current behavior is unspecified. + +For alternative experimental match ergonomics, see the feature +[`ref_pat_eat_one_layer_2024`](./ref-pat-eat-one-layer-2024.md). + +[Editions 2024 and later]: https://nadrieril.github.io/typing-rust-patterns/?compare=false&opts1=AQEBAgEBAQEBAgIAAAAAAAAAAAAAAAA%3D&mode=rules&do_cmp=false diff --git a/src/doc/unstable-book/src/language-features/ref-pat-eat-one-layer-2024.md b/src/doc/unstable-book/src/language-features/ref-pat-eat-one-layer-2024.md new file mode 100644 index 0000000000000..43de1849a5ebb --- /dev/null +++ b/src/doc/unstable-book/src/language-features/ref-pat-eat-one-layer-2024.md @@ -0,0 +1,19 @@ +# `ref_pat_eat_one_layer_2024` + +The tracking issue for this feature is: [#123076] + +[#123076]: https://github.com/rust-lang/rust/issues/123076 + +--- + +This feature is incomplete and not yet intended for general use. + +This implements experimental, Edition-dependent match ergonomics under consideration for inclusion +in Rust. +For more information, see the corresponding typing rules for [Editions 2024 and later]. +On earlier Editions, the current behavior is unspecified. + +For alternative experimental match ergonomics, see the feature +[`ref_pat_eat_one_layer_2024_structural`](./ref-pat-eat-one-layer-2024-structural.md). + +[Editions 2024 and later]: https://nadrieril.github.io/typing-rust-patterns/?compare=false&opts1=AQEBAAABAQABAgIAAQEBAAEBAAABAAA%3D&mode=rules&do_cmp=false diff --git a/src/doc/unstable-book/src/language-features/start.md b/src/doc/unstable-book/src/language-features/start.md deleted file mode 100644 index 09e4875a2e4f7..0000000000000 --- a/src/doc/unstable-book/src/language-features/start.md +++ /dev/null @@ -1,59 +0,0 @@ -# `start` - -The tracking issue for this feature is: [#29633] - -[#29633]: https://github.com/rust-lang/rust/issues/29633 - ------------------------- - -Allows you to mark a function as the entry point of the executable, which is -necessary in `#![no_std]` environments. - -The function marked `#[start]` is passed the command line parameters in the same -format as the C main function (aside from the integer types being used). -It has to be non-generic and have the following signature: - -```rust,ignore (only-for-syntax-highlight) -# let _: -fn(isize, *const *const u8) -> isize -# ; -``` - -This feature should not be confused with the `start` *lang item* which is -defined by the `std` crate and is written `#[lang = "start"]`. - -## Usage together with the `std` crate - -`#[start]` can be used in combination with the `std` crate, in which case the -normal `main` function (which would get called from the `std` crate) won't be -used as an entry point. -The initialization code in `std` will be skipped this way. - -Example: - -```rust -#![feature(start)] - -#[start] -fn start(_argc: isize, _argv: *const *const u8) -> isize { - 0 -} -``` - -Unwinding the stack past the `#[start]` function is currently considered -Undefined Behavior (for any unwinding implementation): - -```rust,ignore (UB) -#![feature(start)] - -#[start] -fn start(_argc: isize, _argv: *const *const u8) -> isize { - std::panic::catch_unwind(|| { - panic!(); // panic safely gets caught or safely aborts execution - }); - - panic!(); // UB! - - 0 -} -``` diff --git a/src/doc/unstable-book/src/language-features/trait-upcasting.md b/src/doc/unstable-book/src/language-features/trait-upcasting.md deleted file mode 100644 index a5f99cc86f270..0000000000000 --- a/src/doc/unstable-book/src/language-features/trait-upcasting.md +++ /dev/null @@ -1,26 +0,0 @@ -# `trait_upcasting` - -The tracking issue for this feature is: [#65991] - -[#65991]: https://github.com/rust-lang/rust/issues/65991 - ------------------------- - -The `trait_upcasting` feature adds support for trait upcasting coercion. This allows a -trait object of type `dyn Bar` to be cast to a trait object of type `dyn Foo` -so long as `Bar: Foo`. - -```rust,edition2018 -#![feature(trait_upcasting)] - -trait Foo {} - -trait Bar: Foo {} - -impl Foo for i32 {} - -impl Bar for T {} - -let bar: &dyn Bar = &123; -let foo: &dyn Foo = bar; -``` diff --git a/src/etc/completions/x.fish b/src/etc/completions/x.fish index 49fe10a4ea220..933557ab0d717 100644 --- a/src/etc/completions/x.fish +++ b/src/etc/completions/x.fish @@ -73,7 +73,7 @@ complete -c x -n "__fish_x_needs_command" -a "run" -d 'Run tools contained in th complete -c x -n "__fish_x_needs_command" -a "setup" -d 'Set up the environment for development' complete -c x -n "__fish_x_needs_command" -a "suggest" -d 'Suggest a subset of tests to run, based on modified files' complete -c x -n "__fish_x_needs_command" -a "vendor" -d 'Vendor dependencies' -complete -c x -n "__fish_x_needs_command" -a "perf" -d 'Perform profiling and benchmarking of the compiler using the `rustc-perf-wrapper` tool' +complete -c x -n "__fish_x_needs_command" -a "perf" -d 'Perform profiling and benchmarking of the compiler using `rustc-perf`' complete -c x -n "__fish_x_using_subcommand build" -l config -d 'TOML configuration file for build' -r -F complete -c x -n "__fish_x_using_subcommand build" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)" complete -c x -n "__fish_x_using_subcommand build" -l build -d 'build target of the stage0 compiler' -r -f @@ -638,36 +638,218 @@ complete -c x -n "__fish_x_using_subcommand vendor" -l llvm-profile-generate -d complete -c x -n "__fish_x_using_subcommand vendor" -l enable-bolt-settings -d 'Enable BOLT link flags' complete -c x -n "__fish_x_using_subcommand vendor" -l skip-stage0-validation -d 'Skip stage0 compiler validation' complete -c x -n "__fish_x_using_subcommand vendor" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c x -n "__fish_x_using_subcommand perf" -l config -d 'TOML configuration file for build' -r -F -complete -c x -n "__fish_x_using_subcommand perf" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)" -complete -c x -n "__fish_x_using_subcommand perf" -l build -d 'build target of the stage0 compiler' -r -f -complete -c x -n "__fish_x_using_subcommand perf" -l host -d 'host targets to build' -r -f -complete -c x -n "__fish_x_using_subcommand perf" -l target -d 'target targets to build' -r -f -complete -c x -n "__fish_x_using_subcommand perf" -l exclude -d 'build paths to exclude' -r -F -complete -c x -n "__fish_x_using_subcommand perf" -l skip -d 'build paths to skip' -r -F -complete -c x -n "__fish_x_using_subcommand perf" -l rustc-error-format -r -f -complete -c x -n "__fish_x_using_subcommand perf" -l on-fail -d 'command to run on failure' -r -f -a "(__fish_complete_command)" -complete -c x -n "__fish_x_using_subcommand perf" -l stage -d 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)' -r -f -complete -c x -n "__fish_x_using_subcommand perf" -l keep-stage -d 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f -complete -c x -n "__fish_x_using_subcommand perf" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f -complete -c x -n "__fish_x_using_subcommand perf" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)" -complete -c x -n "__fish_x_using_subcommand perf" -s j -l jobs -d 'number of jobs to run in parallel' -r -f -complete -c x -n "__fish_x_using_subcommand perf" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny\t'',warn\t'',default\t''}" -complete -c x -n "__fish_x_using_subcommand perf" -l error-format -d 'rustc error format' -r -f -complete -c x -n "__fish_x_using_subcommand perf" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always\t'',never\t'',auto\t''}" -complete -c x -n "__fish_x_using_subcommand perf" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F -complete -c x -n "__fish_x_using_subcommand perf" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F -complete -c x -n "__fish_x_using_subcommand perf" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F -complete -c x -n "__fish_x_using_subcommand perf" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r -complete -c x -n "__fish_x_using_subcommand perf" -l set -d 'override options in config.toml' -r -f -complete -c x -n "__fish_x_using_subcommand perf" -s v -l verbose -d 'use verbose output (-vv for very verbose)' -complete -c x -n "__fish_x_using_subcommand perf" -s i -l incremental -d 'use incremental compilation' -complete -c x -n "__fish_x_using_subcommand perf" -l include-default-paths -d 'include default paths in addition to the provided ones' -complete -c x -n "__fish_x_using_subcommand perf" -l dry-run -d 'dry run; don\'t build anything' -complete -c x -n "__fish_x_using_subcommand perf" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims' -complete -c x -n "__fish_x_using_subcommand perf" -l json-output -d 'use message-format=json' -complete -c x -n "__fish_x_using_subcommand perf" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)' -complete -c x -n "__fish_x_using_subcommand perf" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc' -complete -c x -n "__fish_x_using_subcommand perf" -l enable-bolt-settings -d 'Enable BOLT link flags' -complete -c x -n "__fish_x_using_subcommand perf" -l skip-stage0-validation -d 'Skip stage0 compiler validation' -complete -c x -n "__fish_x_using_subcommand perf" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l config -d 'TOML configuration file for build' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)" +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l build -d 'build target of the stage0 compiler' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l host -d 'host targets to build' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l target -d 'target targets to build' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l exclude -d 'build paths to exclude' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l skip -d 'build paths to skip' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l rustc-error-format -r -f +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l on-fail -d 'command to run on failure' -r -f -a "(__fish_complete_command)" +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l stage -d 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l keep-stage -d 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)" +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -s j -l jobs -d 'number of jobs to run in parallel' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny\t'',warn\t'',default\t''}" +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l error-format -d 'rustc error format' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always\t'',never\t'',auto\t''}" +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l set -d 'override options in config.toml' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -s v -l verbose -d 'use verbose output (-vv for very verbose)' +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -s i -l incremental -d 'use incremental compilation' +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l include-default-paths -d 'include default paths in addition to the provided ones' +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l dry-run -d 'dry run; don\'t build anything' +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims' +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l json-output -d 'use message-format=json' +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)' +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc' +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l enable-bolt-settings -d 'Enable BOLT link flags' +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l skip-stage0-validation -d 'Skip stage0 compiler validation' +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -a "eprintln" -d 'Run `profile_local eprintln`. This executes the compiler on the given benchmarks and stores its stderr output' +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -a "samply" -d 'Run `profile_local samply` This executes the compiler on the given benchmarks and profiles it with `samply`. You need to install `samply`, e.g. using `cargo install samply`' +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -a "cachegrind" -d 'Run `profile_local cachegrind`. This executes the compiler on the given benchmarks under `Cachegrind`' +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -a "benchmark" -d 'Run compile benchmarks with a locally built compiler' +complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -a "compare" -d 'Compare the results of two previously executed benchmark runs' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l include -d 'Select the benchmarks that you want to run (separated by commas). If unspecified, all benchmarks will be executed' -r +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l exclude -d 'Select the benchmarks matching a prefix in this comma-separated list that you don\'t want to run' -r +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l scenarios -d 'Select the scenarios that should be benchmarked' -r -f -a "{Full\t'',IncrFull\t'',IncrUnchanged\t'',IncrPatched\t''}" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l profiles -d 'Select the profiles that should be benchmarked' -r -f -a "{Check\t'',Debug\t'',Doc\t'',Opt\t'',Clippy\t''}" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l config -d 'TOML configuration file for build' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l build -d 'build target of the stage0 compiler' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l host -d 'host targets to build' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l target -d 'target targets to build' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l skip -d 'build paths to skip' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l rustc-error-format -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l on-fail -d 'command to run on failure' -r -f -a "(__fish_complete_command)" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l stage -d 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l keep-stage -d 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -s j -l jobs -d 'number of jobs to run in parallel' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny\t'',warn\t'',default\t''}" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l error-format -d 'rustc error format' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always\t'',never\t'',auto\t''}" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l set -d 'override options in config.toml' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -s v -l verbose -d 'use verbose output (-vv for very verbose)' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -s i -l incremental -d 'use incremental compilation' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l include-default-paths -d 'include default paths in addition to the provided ones' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l dry-run -d 'dry run; don\'t build anything' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l json-output -d 'use message-format=json' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l enable-bolt-settings -d 'Enable BOLT link flags' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l skip-stage0-validation -d 'Skip stage0 compiler validation' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l include -d 'Select the benchmarks that you want to run (separated by commas). If unspecified, all benchmarks will be executed' -r +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l exclude -d 'Select the benchmarks matching a prefix in this comma-separated list that you don\'t want to run' -r +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l scenarios -d 'Select the scenarios that should be benchmarked' -r -f -a "{Full\t'',IncrFull\t'',IncrUnchanged\t'',IncrPatched\t''}" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l profiles -d 'Select the profiles that should be benchmarked' -r -f -a "{Check\t'',Debug\t'',Doc\t'',Opt\t'',Clippy\t''}" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l config -d 'TOML configuration file for build' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l build -d 'build target of the stage0 compiler' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l host -d 'host targets to build' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l target -d 'target targets to build' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l skip -d 'build paths to skip' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l rustc-error-format -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l on-fail -d 'command to run on failure' -r -f -a "(__fish_complete_command)" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l stage -d 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l keep-stage -d 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -s j -l jobs -d 'number of jobs to run in parallel' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny\t'',warn\t'',default\t''}" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l error-format -d 'rustc error format' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always\t'',never\t'',auto\t''}" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l set -d 'override options in config.toml' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -s v -l verbose -d 'use verbose output (-vv for very verbose)' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -s i -l incremental -d 'use incremental compilation' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l include-default-paths -d 'include default paths in addition to the provided ones' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l dry-run -d 'dry run; don\'t build anything' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l json-output -d 'use message-format=json' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l enable-bolt-settings -d 'Enable BOLT link flags' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l skip-stage0-validation -d 'Skip stage0 compiler validation' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l include -d 'Select the benchmarks that you want to run (separated by commas). If unspecified, all benchmarks will be executed' -r +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l exclude -d 'Select the benchmarks matching a prefix in this comma-separated list that you don\'t want to run' -r +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l scenarios -d 'Select the scenarios that should be benchmarked' -r -f -a "{Full\t'',IncrFull\t'',IncrUnchanged\t'',IncrPatched\t''}" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l profiles -d 'Select the profiles that should be benchmarked' -r -f -a "{Check\t'',Debug\t'',Doc\t'',Opt\t'',Clippy\t''}" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l config -d 'TOML configuration file for build' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l build -d 'build target of the stage0 compiler' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l host -d 'host targets to build' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l target -d 'target targets to build' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l skip -d 'build paths to skip' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l rustc-error-format -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l on-fail -d 'command to run on failure' -r -f -a "(__fish_complete_command)" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l stage -d 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l keep-stage -d 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -s j -l jobs -d 'number of jobs to run in parallel' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny\t'',warn\t'',default\t''}" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l error-format -d 'rustc error format' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always\t'',never\t'',auto\t''}" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l set -d 'override options in config.toml' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -s v -l verbose -d 'use verbose output (-vv for very verbose)' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -s i -l incremental -d 'use incremental compilation' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l include-default-paths -d 'include default paths in addition to the provided ones' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l dry-run -d 'dry run; don\'t build anything' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l json-output -d 'use message-format=json' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l enable-bolt-settings -d 'Enable BOLT link flags' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l skip-stage0-validation -d 'Skip stage0 compiler validation' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l include -d 'Select the benchmarks that you want to run (separated by commas). If unspecified, all benchmarks will be executed' -r +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l exclude -d 'Select the benchmarks matching a prefix in this comma-separated list that you don\'t want to run' -r +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l scenarios -d 'Select the scenarios that should be benchmarked' -r -f -a "{Full\t'',IncrFull\t'',IncrUnchanged\t'',IncrPatched\t''}" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l profiles -d 'Select the profiles that should be benchmarked' -r -f -a "{Check\t'',Debug\t'',Doc\t'',Opt\t'',Clippy\t''}" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l config -d 'TOML configuration file for build' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l build -d 'build target of the stage0 compiler' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l host -d 'host targets to build' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l target -d 'target targets to build' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l skip -d 'build paths to skip' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l rustc-error-format -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l on-fail -d 'command to run on failure' -r -f -a "(__fish_complete_command)" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l stage -d 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l keep-stage -d 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -s j -l jobs -d 'number of jobs to run in parallel' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny\t'',warn\t'',default\t''}" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l error-format -d 'rustc error format' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always\t'',never\t'',auto\t''}" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l set -d 'override options in config.toml' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -s v -l verbose -d 'use verbose output (-vv for very verbose)' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -s i -l incremental -d 'use incremental compilation' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l include-default-paths -d 'include default paths in addition to the provided ones' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l dry-run -d 'dry run; don\'t build anything' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l json-output -d 'use message-format=json' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l enable-bolt-settings -d 'Enable BOLT link flags' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l skip-stage0-validation -d 'Skip stage0 compiler validation' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l config -d 'TOML configuration file for build' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l build -d 'build target of the stage0 compiler' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l host -d 'host targets to build' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l target -d 'target targets to build' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l exclude -d 'build paths to exclude' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l skip -d 'build paths to skip' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l rustc-error-format -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l on-fail -d 'command to run on failure' -r -f -a "(__fish_complete_command)" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l stage -d 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l keep-stage -d 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -s j -l jobs -d 'number of jobs to run in parallel' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny\t'',warn\t'',default\t''}" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l error-format -d 'rustc error format' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always\t'',never\t'',auto\t''}" +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l set -d 'override options in config.toml' -r -f +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -s v -l verbose -d 'use verbose output (-vv for very verbose)' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -s i -l incremental -d 'use incremental compilation' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l include-default-paths -d 'include default paths in addition to the provided ones' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l dry-run -d 'dry run; don\'t build anything' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l json-output -d 'use message-format=json' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l enable-bolt-settings -d 'Enable BOLT link flags' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l skip-stage0-validation -d 'Skip stage0 compiler validation' +complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -s h -l help -d 'Print help (see more with \'--help\')' diff --git a/src/etc/completions/x.ps1 b/src/etc/completions/x.ps1 index fa833b6876ac8..b848b95d88f45 100644 --- a/src/etc/completions/x.ps1 +++ b/src/etc/completions/x.ps1 @@ -74,7 +74,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock { [CompletionResult]::new('setup', 'setup', [CompletionResultType]::ParameterValue, 'Set up the environment for development') [CompletionResult]::new('suggest', 'suggest', [CompletionResultType]::ParameterValue, 'Suggest a subset of tests to run, based on modified files') [CompletionResult]::new('vendor', 'vendor', [CompletionResultType]::ParameterValue, 'Vendor dependencies') - [CompletionResult]::new('perf', 'perf', [CompletionResultType]::ParameterValue, 'Perform profiling and benchmarking of the compiler using the `rustc-perf-wrapper` tool') + [CompletionResult]::new('perf', 'perf', [CompletionResultType]::ParameterValue, 'Perform profiling and benchmarking of the compiler using `rustc-perf`') break } 'x;build' { @@ -754,6 +754,223 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock { break } 'x;perf' { + [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') + [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `config.toml`') + [CompletionResult]::new('--build', '--build', [CompletionResultType]::ParameterName, 'build target of the stage0 compiler') + [CompletionResult]::new('--host', '--host', [CompletionResultType]::ParameterName, 'host targets to build') + [CompletionResult]::new('--target', '--target', [CompletionResultType]::ParameterName, 'target targets to build') + [CompletionResult]::new('--exclude', '--exclude', [CompletionResultType]::ParameterName, 'build paths to exclude') + [CompletionResult]::new('--skip', '--skip', [CompletionResultType]::ParameterName, 'build paths to skip') + [CompletionResult]::new('--rustc-error-format', '--rustc-error-format', [CompletionResultType]::ParameterName, 'rustc-error-format') + [CompletionResult]::new('--on-fail', '--on-fail', [CompletionResultType]::ParameterName, 'command to run on failure') + [CompletionResult]::new('--stage', '--stage', [CompletionResultType]::ParameterName, 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)') + [CompletionResult]::new('--keep-stage', '--keep-stage', [CompletionResultType]::ParameterName, 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--keep-stage-std', '--keep-stage-std', [CompletionResultType]::ParameterName, 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--src', '--src', [CompletionResultType]::ParameterName, 'path to the root of the rust checkout') + [CompletionResult]::new('-j', '-j', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--jobs', '--jobs', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--warnings', '--warnings', [CompletionResultType]::ParameterName, 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour') + [CompletionResult]::new('--error-format', '--error-format', [CompletionResultType]::ParameterName, 'rustc error format') + [CompletionResult]::new('--color', '--color', [CompletionResultType]::ParameterName, 'whether to use color in cargo and rustc output') + [CompletionResult]::new('--rust-profile-generate', '--rust-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with rustc build') + [CompletionResult]::new('--rust-profile-use', '--rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') + [CompletionResult]::new('--llvm-profile-use', '--llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') + [CompletionResult]::new('--reproducible-artifact', '--reproducible-artifact', [CompletionResultType]::ParameterName, 'Additional reproducible artifacts that should be added to the reproducible artifacts archive') + [CompletionResult]::new('--set', '--set', [CompletionResultType]::ParameterName, 'override options in config.toml') + [CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('--verbose', '--verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--incremental', '--incremental', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--include-default-paths', '--include-default-paths', [CompletionResultType]::ParameterName, 'include default paths in addition to the provided ones') + [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything') + [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims') + [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json') + [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)') + [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc') + [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags') + [CompletionResult]::new('--skip-stage0-validation', '--skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation') + [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + [CompletionResult]::new('eprintln', 'eprintln', [CompletionResultType]::ParameterValue, 'Run `profile_local eprintln`. This executes the compiler on the given benchmarks and stores its stderr output') + [CompletionResult]::new('samply', 'samply', [CompletionResultType]::ParameterValue, 'Run `profile_local samply` This executes the compiler on the given benchmarks and profiles it with `samply`. You need to install `samply`, e.g. using `cargo install samply`') + [CompletionResult]::new('cachegrind', 'cachegrind', [CompletionResultType]::ParameterValue, 'Run `profile_local cachegrind`. This executes the compiler on the given benchmarks under `Cachegrind`') + [CompletionResult]::new('benchmark', 'benchmark', [CompletionResultType]::ParameterValue, 'Run compile benchmarks with a locally built compiler') + [CompletionResult]::new('compare', 'compare', [CompletionResultType]::ParameterValue, 'Compare the results of two previously executed benchmark runs') + break + } + 'x;perf;eprintln' { + [CompletionResult]::new('--include', '--include', [CompletionResultType]::ParameterName, 'Select the benchmarks that you want to run (separated by commas). If unspecified, all benchmarks will be executed') + [CompletionResult]::new('--exclude', '--exclude', [CompletionResultType]::ParameterName, 'Select the benchmarks matching a prefix in this comma-separated list that you don''t want to run') + [CompletionResult]::new('--scenarios', '--scenarios', [CompletionResultType]::ParameterName, 'Select the scenarios that should be benchmarked') + [CompletionResult]::new('--profiles', '--profiles', [CompletionResultType]::ParameterName, 'Select the profiles that should be benchmarked') + [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') + [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `config.toml`') + [CompletionResult]::new('--build', '--build', [CompletionResultType]::ParameterName, 'build target of the stage0 compiler') + [CompletionResult]::new('--host', '--host', [CompletionResultType]::ParameterName, 'host targets to build') + [CompletionResult]::new('--target', '--target', [CompletionResultType]::ParameterName, 'target targets to build') + [CompletionResult]::new('--skip', '--skip', [CompletionResultType]::ParameterName, 'build paths to skip') + [CompletionResult]::new('--rustc-error-format', '--rustc-error-format', [CompletionResultType]::ParameterName, 'rustc-error-format') + [CompletionResult]::new('--on-fail', '--on-fail', [CompletionResultType]::ParameterName, 'command to run on failure') + [CompletionResult]::new('--stage', '--stage', [CompletionResultType]::ParameterName, 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)') + [CompletionResult]::new('--keep-stage', '--keep-stage', [CompletionResultType]::ParameterName, 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--keep-stage-std', '--keep-stage-std', [CompletionResultType]::ParameterName, 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--src', '--src', [CompletionResultType]::ParameterName, 'path to the root of the rust checkout') + [CompletionResult]::new('-j', '-j', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--jobs', '--jobs', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--warnings', '--warnings', [CompletionResultType]::ParameterName, 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour') + [CompletionResult]::new('--error-format', '--error-format', [CompletionResultType]::ParameterName, 'rustc error format') + [CompletionResult]::new('--color', '--color', [CompletionResultType]::ParameterName, 'whether to use color in cargo and rustc output') + [CompletionResult]::new('--rust-profile-generate', '--rust-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with rustc build') + [CompletionResult]::new('--rust-profile-use', '--rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') + [CompletionResult]::new('--llvm-profile-use', '--llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') + [CompletionResult]::new('--reproducible-artifact', '--reproducible-artifact', [CompletionResultType]::ParameterName, 'Additional reproducible artifacts that should be added to the reproducible artifacts archive') + [CompletionResult]::new('--set', '--set', [CompletionResultType]::ParameterName, 'override options in config.toml') + [CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('--verbose', '--verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--incremental', '--incremental', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--include-default-paths', '--include-default-paths', [CompletionResultType]::ParameterName, 'include default paths in addition to the provided ones') + [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything') + [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims') + [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json') + [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)') + [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc') + [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags') + [CompletionResult]::new('--skip-stage0-validation', '--skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation') + [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + break + } + 'x;perf;samply' { + [CompletionResult]::new('--include', '--include', [CompletionResultType]::ParameterName, 'Select the benchmarks that you want to run (separated by commas). If unspecified, all benchmarks will be executed') + [CompletionResult]::new('--exclude', '--exclude', [CompletionResultType]::ParameterName, 'Select the benchmarks matching a prefix in this comma-separated list that you don''t want to run') + [CompletionResult]::new('--scenarios', '--scenarios', [CompletionResultType]::ParameterName, 'Select the scenarios that should be benchmarked') + [CompletionResult]::new('--profiles', '--profiles', [CompletionResultType]::ParameterName, 'Select the profiles that should be benchmarked') + [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') + [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `config.toml`') + [CompletionResult]::new('--build', '--build', [CompletionResultType]::ParameterName, 'build target of the stage0 compiler') + [CompletionResult]::new('--host', '--host', [CompletionResultType]::ParameterName, 'host targets to build') + [CompletionResult]::new('--target', '--target', [CompletionResultType]::ParameterName, 'target targets to build') + [CompletionResult]::new('--skip', '--skip', [CompletionResultType]::ParameterName, 'build paths to skip') + [CompletionResult]::new('--rustc-error-format', '--rustc-error-format', [CompletionResultType]::ParameterName, 'rustc-error-format') + [CompletionResult]::new('--on-fail', '--on-fail', [CompletionResultType]::ParameterName, 'command to run on failure') + [CompletionResult]::new('--stage', '--stage', [CompletionResultType]::ParameterName, 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)') + [CompletionResult]::new('--keep-stage', '--keep-stage', [CompletionResultType]::ParameterName, 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--keep-stage-std', '--keep-stage-std', [CompletionResultType]::ParameterName, 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--src', '--src', [CompletionResultType]::ParameterName, 'path to the root of the rust checkout') + [CompletionResult]::new('-j', '-j', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--jobs', '--jobs', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--warnings', '--warnings', [CompletionResultType]::ParameterName, 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour') + [CompletionResult]::new('--error-format', '--error-format', [CompletionResultType]::ParameterName, 'rustc error format') + [CompletionResult]::new('--color', '--color', [CompletionResultType]::ParameterName, 'whether to use color in cargo and rustc output') + [CompletionResult]::new('--rust-profile-generate', '--rust-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with rustc build') + [CompletionResult]::new('--rust-profile-use', '--rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') + [CompletionResult]::new('--llvm-profile-use', '--llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') + [CompletionResult]::new('--reproducible-artifact', '--reproducible-artifact', [CompletionResultType]::ParameterName, 'Additional reproducible artifacts that should be added to the reproducible artifacts archive') + [CompletionResult]::new('--set', '--set', [CompletionResultType]::ParameterName, 'override options in config.toml') + [CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('--verbose', '--verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--incremental', '--incremental', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--include-default-paths', '--include-default-paths', [CompletionResultType]::ParameterName, 'include default paths in addition to the provided ones') + [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything') + [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims') + [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json') + [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)') + [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc') + [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags') + [CompletionResult]::new('--skip-stage0-validation', '--skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation') + [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + break + } + 'x;perf;cachegrind' { + [CompletionResult]::new('--include', '--include', [CompletionResultType]::ParameterName, 'Select the benchmarks that you want to run (separated by commas). If unspecified, all benchmarks will be executed') + [CompletionResult]::new('--exclude', '--exclude', [CompletionResultType]::ParameterName, 'Select the benchmarks matching a prefix in this comma-separated list that you don''t want to run') + [CompletionResult]::new('--scenarios', '--scenarios', [CompletionResultType]::ParameterName, 'Select the scenarios that should be benchmarked') + [CompletionResult]::new('--profiles', '--profiles', [CompletionResultType]::ParameterName, 'Select the profiles that should be benchmarked') + [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') + [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `config.toml`') + [CompletionResult]::new('--build', '--build', [CompletionResultType]::ParameterName, 'build target of the stage0 compiler') + [CompletionResult]::new('--host', '--host', [CompletionResultType]::ParameterName, 'host targets to build') + [CompletionResult]::new('--target', '--target', [CompletionResultType]::ParameterName, 'target targets to build') + [CompletionResult]::new('--skip', '--skip', [CompletionResultType]::ParameterName, 'build paths to skip') + [CompletionResult]::new('--rustc-error-format', '--rustc-error-format', [CompletionResultType]::ParameterName, 'rustc-error-format') + [CompletionResult]::new('--on-fail', '--on-fail', [CompletionResultType]::ParameterName, 'command to run on failure') + [CompletionResult]::new('--stage', '--stage', [CompletionResultType]::ParameterName, 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)') + [CompletionResult]::new('--keep-stage', '--keep-stage', [CompletionResultType]::ParameterName, 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--keep-stage-std', '--keep-stage-std', [CompletionResultType]::ParameterName, 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--src', '--src', [CompletionResultType]::ParameterName, 'path to the root of the rust checkout') + [CompletionResult]::new('-j', '-j', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--jobs', '--jobs', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--warnings', '--warnings', [CompletionResultType]::ParameterName, 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour') + [CompletionResult]::new('--error-format', '--error-format', [CompletionResultType]::ParameterName, 'rustc error format') + [CompletionResult]::new('--color', '--color', [CompletionResultType]::ParameterName, 'whether to use color in cargo and rustc output') + [CompletionResult]::new('--rust-profile-generate', '--rust-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with rustc build') + [CompletionResult]::new('--rust-profile-use', '--rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') + [CompletionResult]::new('--llvm-profile-use', '--llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') + [CompletionResult]::new('--reproducible-artifact', '--reproducible-artifact', [CompletionResultType]::ParameterName, 'Additional reproducible artifacts that should be added to the reproducible artifacts archive') + [CompletionResult]::new('--set', '--set', [CompletionResultType]::ParameterName, 'override options in config.toml') + [CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('--verbose', '--verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--incremental', '--incremental', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--include-default-paths', '--include-default-paths', [CompletionResultType]::ParameterName, 'include default paths in addition to the provided ones') + [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything') + [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims') + [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json') + [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)') + [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc') + [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags') + [CompletionResult]::new('--skip-stage0-validation', '--skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation') + [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + break + } + 'x;perf;benchmark' { + [CompletionResult]::new('--include', '--include', [CompletionResultType]::ParameterName, 'Select the benchmarks that you want to run (separated by commas). If unspecified, all benchmarks will be executed') + [CompletionResult]::new('--exclude', '--exclude', [CompletionResultType]::ParameterName, 'Select the benchmarks matching a prefix in this comma-separated list that you don''t want to run') + [CompletionResult]::new('--scenarios', '--scenarios', [CompletionResultType]::ParameterName, 'Select the scenarios that should be benchmarked') + [CompletionResult]::new('--profiles', '--profiles', [CompletionResultType]::ParameterName, 'Select the profiles that should be benchmarked') + [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') + [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `config.toml`') + [CompletionResult]::new('--build', '--build', [CompletionResultType]::ParameterName, 'build target of the stage0 compiler') + [CompletionResult]::new('--host', '--host', [CompletionResultType]::ParameterName, 'host targets to build') + [CompletionResult]::new('--target', '--target', [CompletionResultType]::ParameterName, 'target targets to build') + [CompletionResult]::new('--skip', '--skip', [CompletionResultType]::ParameterName, 'build paths to skip') + [CompletionResult]::new('--rustc-error-format', '--rustc-error-format', [CompletionResultType]::ParameterName, 'rustc-error-format') + [CompletionResult]::new('--on-fail', '--on-fail', [CompletionResultType]::ParameterName, 'command to run on failure') + [CompletionResult]::new('--stage', '--stage', [CompletionResultType]::ParameterName, 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)') + [CompletionResult]::new('--keep-stage', '--keep-stage', [CompletionResultType]::ParameterName, 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--keep-stage-std', '--keep-stage-std', [CompletionResultType]::ParameterName, 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--src', '--src', [CompletionResultType]::ParameterName, 'path to the root of the rust checkout') + [CompletionResult]::new('-j', '-j', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--jobs', '--jobs', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--warnings', '--warnings', [CompletionResultType]::ParameterName, 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour') + [CompletionResult]::new('--error-format', '--error-format', [CompletionResultType]::ParameterName, 'rustc error format') + [CompletionResult]::new('--color', '--color', [CompletionResultType]::ParameterName, 'whether to use color in cargo and rustc output') + [CompletionResult]::new('--rust-profile-generate', '--rust-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with rustc build') + [CompletionResult]::new('--rust-profile-use', '--rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') + [CompletionResult]::new('--llvm-profile-use', '--llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') + [CompletionResult]::new('--reproducible-artifact', '--reproducible-artifact', [CompletionResultType]::ParameterName, 'Additional reproducible artifacts that should be added to the reproducible artifacts archive') + [CompletionResult]::new('--set', '--set', [CompletionResultType]::ParameterName, 'override options in config.toml') + [CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('--verbose', '--verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--incremental', '--incremental', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--include-default-paths', '--include-default-paths', [CompletionResultType]::ParameterName, 'include default paths in addition to the provided ones') + [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything') + [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims') + [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json') + [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)') + [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc') + [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags') + [CompletionResult]::new('--skip-stage0-validation', '--skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation') + [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + break + } + 'x;perf;compare' { [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `config.toml`') [CompletionResult]::new('--build', '--build', [CompletionResultType]::ParameterName, 'build target of the stage0 compiler') diff --git a/src/etc/completions/x.py.fish b/src/etc/completions/x.py.fish index 07144ad22d1a1..ff4f7425da6c5 100644 --- a/src/etc/completions/x.py.fish +++ b/src/etc/completions/x.py.fish @@ -73,7 +73,7 @@ complete -c x.py -n "__fish_x.py_needs_command" -a "run" -d 'Run tools contained complete -c x.py -n "__fish_x.py_needs_command" -a "setup" -d 'Set up the environment for development' complete -c x.py -n "__fish_x.py_needs_command" -a "suggest" -d 'Suggest a subset of tests to run, based on modified files' complete -c x.py -n "__fish_x.py_needs_command" -a "vendor" -d 'Vendor dependencies' -complete -c x.py -n "__fish_x.py_needs_command" -a "perf" -d 'Perform profiling and benchmarking of the compiler using the `rustc-perf-wrapper` tool' +complete -c x.py -n "__fish_x.py_needs_command" -a "perf" -d 'Perform profiling and benchmarking of the compiler using `rustc-perf`' complete -c x.py -n "__fish_x.py_using_subcommand build" -l config -d 'TOML configuration file for build' -r -F complete -c x.py -n "__fish_x.py_using_subcommand build" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)" complete -c x.py -n "__fish_x.py_using_subcommand build" -l build -d 'build target of the stage0 compiler' -r -f @@ -638,36 +638,218 @@ complete -c x.py -n "__fish_x.py_using_subcommand vendor" -l llvm-profile-genera complete -c x.py -n "__fish_x.py_using_subcommand vendor" -l enable-bolt-settings -d 'Enable BOLT link flags' complete -c x.py -n "__fish_x.py_using_subcommand vendor" -l skip-stage0-validation -d 'Skip stage0 compiler validation' complete -c x.py -n "__fish_x.py_using_subcommand vendor" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l config -d 'TOML configuration file for build' -r -F -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)" -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l build -d 'build target of the stage0 compiler' -r -f -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l host -d 'host targets to build' -r -f -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l target -d 'target targets to build' -r -f -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l exclude -d 'build paths to exclude' -r -F -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l skip -d 'build paths to skip' -r -F -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l rustc-error-format -r -f -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l on-fail -d 'command to run on failure' -r -f -a "(__fish_complete_command)" -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l stage -d 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)' -r -f -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l keep-stage -d 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)" -complete -c x.py -n "__fish_x.py_using_subcommand perf" -s j -l jobs -d 'number of jobs to run in parallel' -r -f -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny\t'',warn\t'',default\t''}" -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l error-format -d 'rustc error format' -r -f -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always\t'',never\t'',auto\t''}" -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l set -d 'override options in config.toml' -r -f -complete -c x.py -n "__fish_x.py_using_subcommand perf" -s v -l verbose -d 'use verbose output (-vv for very verbose)' -complete -c x.py -n "__fish_x.py_using_subcommand perf" -s i -l incremental -d 'use incremental compilation' -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l include-default-paths -d 'include default paths in addition to the provided ones' -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l dry-run -d 'dry run; don\'t build anything' -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims' -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l json-output -d 'use message-format=json' -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)' -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc' -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l enable-bolt-settings -d 'Enable BOLT link flags' -complete -c x.py -n "__fish_x.py_using_subcommand perf" -l skip-stage0-validation -d 'Skip stage0 compiler validation' -complete -c x.py -n "__fish_x.py_using_subcommand perf" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l config -d 'TOML configuration file for build' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l build -d 'build target of the stage0 compiler' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l host -d 'host targets to build' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l target -d 'target targets to build' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l exclude -d 'build paths to exclude' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l skip -d 'build paths to skip' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l rustc-error-format -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l on-fail -d 'command to run on failure' -r -f -a "(__fish_complete_command)" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l stage -d 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l keep-stage -d 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -s j -l jobs -d 'number of jobs to run in parallel' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny\t'',warn\t'',default\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l error-format -d 'rustc error format' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always\t'',never\t'',auto\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l set -d 'override options in config.toml' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -s v -l verbose -d 'use verbose output (-vv for very verbose)' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -s i -l incremental -d 'use incremental compilation' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l include-default-paths -d 'include default paths in addition to the provided ones' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l dry-run -d 'dry run; don\'t build anything' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l json-output -d 'use message-format=json' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l enable-bolt-settings -d 'Enable BOLT link flags' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l skip-stage0-validation -d 'Skip stage0 compiler validation' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -a "eprintln" -d 'Run `profile_local eprintln`. This executes the compiler on the given benchmarks and stores its stderr output' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -a "samply" -d 'Run `profile_local samply` This executes the compiler on the given benchmarks and profiles it with `samply`. You need to install `samply`, e.g. using `cargo install samply`' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -a "cachegrind" -d 'Run `profile_local cachegrind`. This executes the compiler on the given benchmarks under `Cachegrind`' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -a "benchmark" -d 'Run compile benchmarks with a locally built compiler' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -a "compare" -d 'Compare the results of two previously executed benchmark runs' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l include -d 'Select the benchmarks that you want to run (separated by commas). If unspecified, all benchmarks will be executed' -r +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l exclude -d 'Select the benchmarks matching a prefix in this comma-separated list that you don\'t want to run' -r +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l scenarios -d 'Select the scenarios that should be benchmarked' -r -f -a "{Full\t'',IncrFull\t'',IncrUnchanged\t'',IncrPatched\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l profiles -d 'Select the profiles that should be benchmarked' -r -f -a "{Check\t'',Debug\t'',Doc\t'',Opt\t'',Clippy\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l config -d 'TOML configuration file for build' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l build -d 'build target of the stage0 compiler' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l host -d 'host targets to build' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l target -d 'target targets to build' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l skip -d 'build paths to skip' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l rustc-error-format -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l on-fail -d 'command to run on failure' -r -f -a "(__fish_complete_command)" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l stage -d 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l keep-stage -d 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -s j -l jobs -d 'number of jobs to run in parallel' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny\t'',warn\t'',default\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l error-format -d 'rustc error format' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always\t'',never\t'',auto\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l set -d 'override options in config.toml' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -s v -l verbose -d 'use verbose output (-vv for very verbose)' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -s i -l incremental -d 'use incremental compilation' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l include-default-paths -d 'include default paths in addition to the provided ones' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l dry-run -d 'dry run; don\'t build anything' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l json-output -d 'use message-format=json' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l enable-bolt-settings -d 'Enable BOLT link flags' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l skip-stage0-validation -d 'Skip stage0 compiler validation' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l include -d 'Select the benchmarks that you want to run (separated by commas). If unspecified, all benchmarks will be executed' -r +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l exclude -d 'Select the benchmarks matching a prefix in this comma-separated list that you don\'t want to run' -r +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l scenarios -d 'Select the scenarios that should be benchmarked' -r -f -a "{Full\t'',IncrFull\t'',IncrUnchanged\t'',IncrPatched\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l profiles -d 'Select the profiles that should be benchmarked' -r -f -a "{Check\t'',Debug\t'',Doc\t'',Opt\t'',Clippy\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l config -d 'TOML configuration file for build' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l build -d 'build target of the stage0 compiler' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l host -d 'host targets to build' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l target -d 'target targets to build' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l skip -d 'build paths to skip' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l rustc-error-format -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l on-fail -d 'command to run on failure' -r -f -a "(__fish_complete_command)" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l stage -d 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l keep-stage -d 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -s j -l jobs -d 'number of jobs to run in parallel' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny\t'',warn\t'',default\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l error-format -d 'rustc error format' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always\t'',never\t'',auto\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l set -d 'override options in config.toml' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -s v -l verbose -d 'use verbose output (-vv for very verbose)' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -s i -l incremental -d 'use incremental compilation' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l include-default-paths -d 'include default paths in addition to the provided ones' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l dry-run -d 'dry run; don\'t build anything' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l json-output -d 'use message-format=json' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l enable-bolt-settings -d 'Enable BOLT link flags' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l skip-stage0-validation -d 'Skip stage0 compiler validation' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l include -d 'Select the benchmarks that you want to run (separated by commas). If unspecified, all benchmarks will be executed' -r +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l exclude -d 'Select the benchmarks matching a prefix in this comma-separated list that you don\'t want to run' -r +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l scenarios -d 'Select the scenarios that should be benchmarked' -r -f -a "{Full\t'',IncrFull\t'',IncrUnchanged\t'',IncrPatched\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l profiles -d 'Select the profiles that should be benchmarked' -r -f -a "{Check\t'',Debug\t'',Doc\t'',Opt\t'',Clippy\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l config -d 'TOML configuration file for build' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l build -d 'build target of the stage0 compiler' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l host -d 'host targets to build' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l target -d 'target targets to build' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l skip -d 'build paths to skip' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l rustc-error-format -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l on-fail -d 'command to run on failure' -r -f -a "(__fish_complete_command)" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l stage -d 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l keep-stage -d 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -s j -l jobs -d 'number of jobs to run in parallel' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny\t'',warn\t'',default\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l error-format -d 'rustc error format' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always\t'',never\t'',auto\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l set -d 'override options in config.toml' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -s v -l verbose -d 'use verbose output (-vv for very verbose)' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -s i -l incremental -d 'use incremental compilation' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l include-default-paths -d 'include default paths in addition to the provided ones' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l dry-run -d 'dry run; don\'t build anything' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l json-output -d 'use message-format=json' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l enable-bolt-settings -d 'Enable BOLT link flags' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l skip-stage0-validation -d 'Skip stage0 compiler validation' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l include -d 'Select the benchmarks that you want to run (separated by commas). If unspecified, all benchmarks will be executed' -r +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l exclude -d 'Select the benchmarks matching a prefix in this comma-separated list that you don\'t want to run' -r +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l scenarios -d 'Select the scenarios that should be benchmarked' -r -f -a "{Full\t'',IncrFull\t'',IncrUnchanged\t'',IncrPatched\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l profiles -d 'Select the profiles that should be benchmarked' -r -f -a "{Check\t'',Debug\t'',Doc\t'',Opt\t'',Clippy\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l config -d 'TOML configuration file for build' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l build -d 'build target of the stage0 compiler' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l host -d 'host targets to build' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l target -d 'target targets to build' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l skip -d 'build paths to skip' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l rustc-error-format -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l on-fail -d 'command to run on failure' -r -f -a "(__fish_complete_command)" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l stage -d 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l keep-stage -d 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -s j -l jobs -d 'number of jobs to run in parallel' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny\t'',warn\t'',default\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l error-format -d 'rustc error format' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always\t'',never\t'',auto\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l set -d 'override options in config.toml' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -s v -l verbose -d 'use verbose output (-vv for very verbose)' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -s i -l incremental -d 'use incremental compilation' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l include-default-paths -d 'include default paths in addition to the provided ones' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l dry-run -d 'dry run; don\'t build anything' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l json-output -d 'use message-format=json' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l enable-bolt-settings -d 'Enable BOLT link flags' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l skip-stage0-validation -d 'Skip stage0 compiler validation' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l config -d 'TOML configuration file for build' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l build -d 'build target of the stage0 compiler' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l host -d 'host targets to build' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l target -d 'target targets to build' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l exclude -d 'build paths to exclude' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l skip -d 'build paths to skip' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l rustc-error-format -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l on-fail -d 'command to run on failure' -r -f -a "(__fish_complete_command)" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l stage -d 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l keep-stage -d 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -s j -l jobs -d 'number of jobs to run in parallel' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny\t'',warn\t'',default\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l error-format -d 'rustc error format' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always\t'',never\t'',auto\t''}" +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l set -d 'override options in config.toml' -r -f +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -s v -l verbose -d 'use verbose output (-vv for very verbose)' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -s i -l incremental -d 'use incremental compilation' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l include-default-paths -d 'include default paths in addition to the provided ones' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l dry-run -d 'dry run; don\'t build anything' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l json-output -d 'use message-format=json' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l enable-bolt-settings -d 'Enable BOLT link flags' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l skip-stage0-validation -d 'Skip stage0 compiler validation' +complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -s h -l help -d 'Print help (see more with \'--help\')' diff --git a/src/etc/completions/x.py.ps1 b/src/etc/completions/x.py.ps1 index 7d5bd3c9632be..e5ee174f4ca84 100644 --- a/src/etc/completions/x.py.ps1 +++ b/src/etc/completions/x.py.ps1 @@ -74,7 +74,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { [CompletionResult]::new('setup', 'setup', [CompletionResultType]::ParameterValue, 'Set up the environment for development') [CompletionResult]::new('suggest', 'suggest', [CompletionResultType]::ParameterValue, 'Suggest a subset of tests to run, based on modified files') [CompletionResult]::new('vendor', 'vendor', [CompletionResultType]::ParameterValue, 'Vendor dependencies') - [CompletionResult]::new('perf', 'perf', [CompletionResultType]::ParameterValue, 'Perform profiling and benchmarking of the compiler using the `rustc-perf-wrapper` tool') + [CompletionResult]::new('perf', 'perf', [CompletionResultType]::ParameterValue, 'Perform profiling and benchmarking of the compiler using `rustc-perf`') break } 'x.py;build' { @@ -754,6 +754,223 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock { break } 'x.py;perf' { + [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') + [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `config.toml`') + [CompletionResult]::new('--build', '--build', [CompletionResultType]::ParameterName, 'build target of the stage0 compiler') + [CompletionResult]::new('--host', '--host', [CompletionResultType]::ParameterName, 'host targets to build') + [CompletionResult]::new('--target', '--target', [CompletionResultType]::ParameterName, 'target targets to build') + [CompletionResult]::new('--exclude', '--exclude', [CompletionResultType]::ParameterName, 'build paths to exclude') + [CompletionResult]::new('--skip', '--skip', [CompletionResultType]::ParameterName, 'build paths to skip') + [CompletionResult]::new('--rustc-error-format', '--rustc-error-format', [CompletionResultType]::ParameterName, 'rustc-error-format') + [CompletionResult]::new('--on-fail', '--on-fail', [CompletionResultType]::ParameterName, 'command to run on failure') + [CompletionResult]::new('--stage', '--stage', [CompletionResultType]::ParameterName, 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)') + [CompletionResult]::new('--keep-stage', '--keep-stage', [CompletionResultType]::ParameterName, 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--keep-stage-std', '--keep-stage-std', [CompletionResultType]::ParameterName, 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--src', '--src', [CompletionResultType]::ParameterName, 'path to the root of the rust checkout') + [CompletionResult]::new('-j', '-j', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--jobs', '--jobs', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--warnings', '--warnings', [CompletionResultType]::ParameterName, 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour') + [CompletionResult]::new('--error-format', '--error-format', [CompletionResultType]::ParameterName, 'rustc error format') + [CompletionResult]::new('--color', '--color', [CompletionResultType]::ParameterName, 'whether to use color in cargo and rustc output') + [CompletionResult]::new('--rust-profile-generate', '--rust-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with rustc build') + [CompletionResult]::new('--rust-profile-use', '--rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') + [CompletionResult]::new('--llvm-profile-use', '--llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') + [CompletionResult]::new('--reproducible-artifact', '--reproducible-artifact', [CompletionResultType]::ParameterName, 'Additional reproducible artifacts that should be added to the reproducible artifacts archive') + [CompletionResult]::new('--set', '--set', [CompletionResultType]::ParameterName, 'override options in config.toml') + [CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('--verbose', '--verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--incremental', '--incremental', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--include-default-paths', '--include-default-paths', [CompletionResultType]::ParameterName, 'include default paths in addition to the provided ones') + [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything') + [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims') + [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json') + [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)') + [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc') + [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags') + [CompletionResult]::new('--skip-stage0-validation', '--skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation') + [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + [CompletionResult]::new('eprintln', 'eprintln', [CompletionResultType]::ParameterValue, 'Run `profile_local eprintln`. This executes the compiler on the given benchmarks and stores its stderr output') + [CompletionResult]::new('samply', 'samply', [CompletionResultType]::ParameterValue, 'Run `profile_local samply` This executes the compiler on the given benchmarks and profiles it with `samply`. You need to install `samply`, e.g. using `cargo install samply`') + [CompletionResult]::new('cachegrind', 'cachegrind', [CompletionResultType]::ParameterValue, 'Run `profile_local cachegrind`. This executes the compiler on the given benchmarks under `Cachegrind`') + [CompletionResult]::new('benchmark', 'benchmark', [CompletionResultType]::ParameterValue, 'Run compile benchmarks with a locally built compiler') + [CompletionResult]::new('compare', 'compare', [CompletionResultType]::ParameterValue, 'Compare the results of two previously executed benchmark runs') + break + } + 'x.py;perf;eprintln' { + [CompletionResult]::new('--include', '--include', [CompletionResultType]::ParameterName, 'Select the benchmarks that you want to run (separated by commas). If unspecified, all benchmarks will be executed') + [CompletionResult]::new('--exclude', '--exclude', [CompletionResultType]::ParameterName, 'Select the benchmarks matching a prefix in this comma-separated list that you don''t want to run') + [CompletionResult]::new('--scenarios', '--scenarios', [CompletionResultType]::ParameterName, 'Select the scenarios that should be benchmarked') + [CompletionResult]::new('--profiles', '--profiles', [CompletionResultType]::ParameterName, 'Select the profiles that should be benchmarked') + [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') + [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `config.toml`') + [CompletionResult]::new('--build', '--build', [CompletionResultType]::ParameterName, 'build target of the stage0 compiler') + [CompletionResult]::new('--host', '--host', [CompletionResultType]::ParameterName, 'host targets to build') + [CompletionResult]::new('--target', '--target', [CompletionResultType]::ParameterName, 'target targets to build') + [CompletionResult]::new('--skip', '--skip', [CompletionResultType]::ParameterName, 'build paths to skip') + [CompletionResult]::new('--rustc-error-format', '--rustc-error-format', [CompletionResultType]::ParameterName, 'rustc-error-format') + [CompletionResult]::new('--on-fail', '--on-fail', [CompletionResultType]::ParameterName, 'command to run on failure') + [CompletionResult]::new('--stage', '--stage', [CompletionResultType]::ParameterName, 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)') + [CompletionResult]::new('--keep-stage', '--keep-stage', [CompletionResultType]::ParameterName, 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--keep-stage-std', '--keep-stage-std', [CompletionResultType]::ParameterName, 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--src', '--src', [CompletionResultType]::ParameterName, 'path to the root of the rust checkout') + [CompletionResult]::new('-j', '-j', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--jobs', '--jobs', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--warnings', '--warnings', [CompletionResultType]::ParameterName, 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour') + [CompletionResult]::new('--error-format', '--error-format', [CompletionResultType]::ParameterName, 'rustc error format') + [CompletionResult]::new('--color', '--color', [CompletionResultType]::ParameterName, 'whether to use color in cargo and rustc output') + [CompletionResult]::new('--rust-profile-generate', '--rust-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with rustc build') + [CompletionResult]::new('--rust-profile-use', '--rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') + [CompletionResult]::new('--llvm-profile-use', '--llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') + [CompletionResult]::new('--reproducible-artifact', '--reproducible-artifact', [CompletionResultType]::ParameterName, 'Additional reproducible artifacts that should be added to the reproducible artifacts archive') + [CompletionResult]::new('--set', '--set', [CompletionResultType]::ParameterName, 'override options in config.toml') + [CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('--verbose', '--verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--incremental', '--incremental', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--include-default-paths', '--include-default-paths', [CompletionResultType]::ParameterName, 'include default paths in addition to the provided ones') + [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything') + [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims') + [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json') + [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)') + [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc') + [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags') + [CompletionResult]::new('--skip-stage0-validation', '--skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation') + [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + break + } + 'x.py;perf;samply' { + [CompletionResult]::new('--include', '--include', [CompletionResultType]::ParameterName, 'Select the benchmarks that you want to run (separated by commas). If unspecified, all benchmarks will be executed') + [CompletionResult]::new('--exclude', '--exclude', [CompletionResultType]::ParameterName, 'Select the benchmarks matching a prefix in this comma-separated list that you don''t want to run') + [CompletionResult]::new('--scenarios', '--scenarios', [CompletionResultType]::ParameterName, 'Select the scenarios that should be benchmarked') + [CompletionResult]::new('--profiles', '--profiles', [CompletionResultType]::ParameterName, 'Select the profiles that should be benchmarked') + [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') + [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `config.toml`') + [CompletionResult]::new('--build', '--build', [CompletionResultType]::ParameterName, 'build target of the stage0 compiler') + [CompletionResult]::new('--host', '--host', [CompletionResultType]::ParameterName, 'host targets to build') + [CompletionResult]::new('--target', '--target', [CompletionResultType]::ParameterName, 'target targets to build') + [CompletionResult]::new('--skip', '--skip', [CompletionResultType]::ParameterName, 'build paths to skip') + [CompletionResult]::new('--rustc-error-format', '--rustc-error-format', [CompletionResultType]::ParameterName, 'rustc-error-format') + [CompletionResult]::new('--on-fail', '--on-fail', [CompletionResultType]::ParameterName, 'command to run on failure') + [CompletionResult]::new('--stage', '--stage', [CompletionResultType]::ParameterName, 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)') + [CompletionResult]::new('--keep-stage', '--keep-stage', [CompletionResultType]::ParameterName, 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--keep-stage-std', '--keep-stage-std', [CompletionResultType]::ParameterName, 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--src', '--src', [CompletionResultType]::ParameterName, 'path to the root of the rust checkout') + [CompletionResult]::new('-j', '-j', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--jobs', '--jobs', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--warnings', '--warnings', [CompletionResultType]::ParameterName, 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour') + [CompletionResult]::new('--error-format', '--error-format', [CompletionResultType]::ParameterName, 'rustc error format') + [CompletionResult]::new('--color', '--color', [CompletionResultType]::ParameterName, 'whether to use color in cargo and rustc output') + [CompletionResult]::new('--rust-profile-generate', '--rust-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with rustc build') + [CompletionResult]::new('--rust-profile-use', '--rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') + [CompletionResult]::new('--llvm-profile-use', '--llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') + [CompletionResult]::new('--reproducible-artifact', '--reproducible-artifact', [CompletionResultType]::ParameterName, 'Additional reproducible artifacts that should be added to the reproducible artifacts archive') + [CompletionResult]::new('--set', '--set', [CompletionResultType]::ParameterName, 'override options in config.toml') + [CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('--verbose', '--verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--incremental', '--incremental', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--include-default-paths', '--include-default-paths', [CompletionResultType]::ParameterName, 'include default paths in addition to the provided ones') + [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything') + [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims') + [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json') + [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)') + [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc') + [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags') + [CompletionResult]::new('--skip-stage0-validation', '--skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation') + [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + break + } + 'x.py;perf;cachegrind' { + [CompletionResult]::new('--include', '--include', [CompletionResultType]::ParameterName, 'Select the benchmarks that you want to run (separated by commas). If unspecified, all benchmarks will be executed') + [CompletionResult]::new('--exclude', '--exclude', [CompletionResultType]::ParameterName, 'Select the benchmarks matching a prefix in this comma-separated list that you don''t want to run') + [CompletionResult]::new('--scenarios', '--scenarios', [CompletionResultType]::ParameterName, 'Select the scenarios that should be benchmarked') + [CompletionResult]::new('--profiles', '--profiles', [CompletionResultType]::ParameterName, 'Select the profiles that should be benchmarked') + [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') + [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `config.toml`') + [CompletionResult]::new('--build', '--build', [CompletionResultType]::ParameterName, 'build target of the stage0 compiler') + [CompletionResult]::new('--host', '--host', [CompletionResultType]::ParameterName, 'host targets to build') + [CompletionResult]::new('--target', '--target', [CompletionResultType]::ParameterName, 'target targets to build') + [CompletionResult]::new('--skip', '--skip', [CompletionResultType]::ParameterName, 'build paths to skip') + [CompletionResult]::new('--rustc-error-format', '--rustc-error-format', [CompletionResultType]::ParameterName, 'rustc-error-format') + [CompletionResult]::new('--on-fail', '--on-fail', [CompletionResultType]::ParameterName, 'command to run on failure') + [CompletionResult]::new('--stage', '--stage', [CompletionResultType]::ParameterName, 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)') + [CompletionResult]::new('--keep-stage', '--keep-stage', [CompletionResultType]::ParameterName, 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--keep-stage-std', '--keep-stage-std', [CompletionResultType]::ParameterName, 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--src', '--src', [CompletionResultType]::ParameterName, 'path to the root of the rust checkout') + [CompletionResult]::new('-j', '-j', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--jobs', '--jobs', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--warnings', '--warnings', [CompletionResultType]::ParameterName, 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour') + [CompletionResult]::new('--error-format', '--error-format', [CompletionResultType]::ParameterName, 'rustc error format') + [CompletionResult]::new('--color', '--color', [CompletionResultType]::ParameterName, 'whether to use color in cargo and rustc output') + [CompletionResult]::new('--rust-profile-generate', '--rust-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with rustc build') + [CompletionResult]::new('--rust-profile-use', '--rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') + [CompletionResult]::new('--llvm-profile-use', '--llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') + [CompletionResult]::new('--reproducible-artifact', '--reproducible-artifact', [CompletionResultType]::ParameterName, 'Additional reproducible artifacts that should be added to the reproducible artifacts archive') + [CompletionResult]::new('--set', '--set', [CompletionResultType]::ParameterName, 'override options in config.toml') + [CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('--verbose', '--verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--incremental', '--incremental', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--include-default-paths', '--include-default-paths', [CompletionResultType]::ParameterName, 'include default paths in addition to the provided ones') + [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything') + [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims') + [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json') + [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)') + [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc') + [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags') + [CompletionResult]::new('--skip-stage0-validation', '--skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation') + [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + break + } + 'x.py;perf;benchmark' { + [CompletionResult]::new('--include', '--include', [CompletionResultType]::ParameterName, 'Select the benchmarks that you want to run (separated by commas). If unspecified, all benchmarks will be executed') + [CompletionResult]::new('--exclude', '--exclude', [CompletionResultType]::ParameterName, 'Select the benchmarks matching a prefix in this comma-separated list that you don''t want to run') + [CompletionResult]::new('--scenarios', '--scenarios', [CompletionResultType]::ParameterName, 'Select the scenarios that should be benchmarked') + [CompletionResult]::new('--profiles', '--profiles', [CompletionResultType]::ParameterName, 'Select the profiles that should be benchmarked') + [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') + [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `config.toml`') + [CompletionResult]::new('--build', '--build', [CompletionResultType]::ParameterName, 'build target of the stage0 compiler') + [CompletionResult]::new('--host', '--host', [CompletionResultType]::ParameterName, 'host targets to build') + [CompletionResult]::new('--target', '--target', [CompletionResultType]::ParameterName, 'target targets to build') + [CompletionResult]::new('--skip', '--skip', [CompletionResultType]::ParameterName, 'build paths to skip') + [CompletionResult]::new('--rustc-error-format', '--rustc-error-format', [CompletionResultType]::ParameterName, 'rustc-error-format') + [CompletionResult]::new('--on-fail', '--on-fail', [CompletionResultType]::ParameterName, 'command to run on failure') + [CompletionResult]::new('--stage', '--stage', [CompletionResultType]::ParameterName, 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)') + [CompletionResult]::new('--keep-stage', '--keep-stage', [CompletionResultType]::ParameterName, 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--keep-stage-std', '--keep-stage-std', [CompletionResultType]::ParameterName, 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)') + [CompletionResult]::new('--src', '--src', [CompletionResultType]::ParameterName, 'path to the root of the rust checkout') + [CompletionResult]::new('-j', '-j', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--jobs', '--jobs', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel') + [CompletionResult]::new('--warnings', '--warnings', [CompletionResultType]::ParameterName, 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour') + [CompletionResult]::new('--error-format', '--error-format', [CompletionResultType]::ParameterName, 'rustc error format') + [CompletionResult]::new('--color', '--color', [CompletionResultType]::ParameterName, 'whether to use color in cargo and rustc output') + [CompletionResult]::new('--rust-profile-generate', '--rust-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with rustc build') + [CompletionResult]::new('--rust-profile-use', '--rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build') + [CompletionResult]::new('--llvm-profile-use', '--llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build') + [CompletionResult]::new('--reproducible-artifact', '--reproducible-artifact', [CompletionResultType]::ParameterName, 'Additional reproducible artifacts that should be added to the reproducible artifacts archive') + [CompletionResult]::new('--set', '--set', [CompletionResultType]::ParameterName, 'override options in config.toml') + [CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('--verbose', '--verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)') + [CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--incremental', '--incremental', [CompletionResultType]::ParameterName, 'use incremental compilation') + [CompletionResult]::new('--include-default-paths', '--include-default-paths', [CompletionResultType]::ParameterName, 'include default paths in addition to the provided ones') + [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything') + [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims') + [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json') + [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)') + [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc') + [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags') + [CompletionResult]::new('--skip-stage0-validation', '--skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation') + [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')') + break + } + 'x.py;perf;compare' { [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build') [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `config.toml`') [CompletionResult]::new('--build', '--build', [CompletionResultType]::ParameterName, 'build target of the stage0 compiler') diff --git a/src/etc/completions/x.py.sh b/src/etc/completions/x.py.sh index 5c5e4ef0c1589..4952b57b8ab0a 100644 --- a/src/etc/completions/x.py.sh +++ b/src/etc/completions/x.py.sh @@ -63,6 +63,21 @@ _x.py() { x.py,vendor) cmd="x.py__vendor" ;; + x.py__perf,benchmark) + cmd="x.py__perf__benchmark" + ;; + x.py__perf,cachegrind) + cmd="x.py__perf__cachegrind" + ;; + x.py__perf,compare) + cmd="x.py__perf__compare" + ;; + x.py__perf,eprintln) + cmd="x.py__perf__eprintln" + ;; + x.py__perf,samply) + cmd="x.py__perf__samply" + ;; *) ;; esac @@ -2359,7 +2374,7 @@ _x.py() { return 0 ;; x.py__perf) - opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]... eprintln samply cachegrind benchmark compare" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -2547,6 +2562,999 @@ _x.py() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + x.py__perf__benchmark) + opts="-v -i -j -h --include --exclude --scenarios --profiles --verbose --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --include) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --exclude) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --scenarios) + COMPREPLY=($(compgen -W "Full IncrFull IncrUnchanged IncrPatched" -- "${cur}")) + return 0 + ;; + --profiles) + COMPREPLY=($(compgen -W "Check Debug Doc Opt Clippy" -- "${cur}")) + return 0 + ;; + --config) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --build-dir) + COMPREPLY=() + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o plusdirs + fi + return 0 + ;; + --build) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --host) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --target) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --skip) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --rustc-error-format) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --on-fail) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --stage) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --keep-stage) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --keep-stage-std) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --src) + COMPREPLY=() + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o plusdirs + fi + return 0 + ;; + --jobs) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + -j) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --warnings) + COMPREPLY=($(compgen -W "deny warn default" -- "${cur}")) + return 0 + ;; + --error-format) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --color) + COMPREPLY=($(compgen -W "always never auto" -- "${cur}")) + return 0 + ;; + --rust-profile-generate) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --rust-profile-use) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --llvm-profile-use) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --reproducible-artifact) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --set) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + x.py__perf__cachegrind) + opts="-v -i -j -h --include --exclude --scenarios --profiles --verbose --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --include) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --exclude) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --scenarios) + COMPREPLY=($(compgen -W "Full IncrFull IncrUnchanged IncrPatched" -- "${cur}")) + return 0 + ;; + --profiles) + COMPREPLY=($(compgen -W "Check Debug Doc Opt Clippy" -- "${cur}")) + return 0 + ;; + --config) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --build-dir) + COMPREPLY=() + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o plusdirs + fi + return 0 + ;; + --build) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --host) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --target) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --skip) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --rustc-error-format) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --on-fail) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --stage) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --keep-stage) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --keep-stage-std) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --src) + COMPREPLY=() + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o plusdirs + fi + return 0 + ;; + --jobs) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + -j) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --warnings) + COMPREPLY=($(compgen -W "deny warn default" -- "${cur}")) + return 0 + ;; + --error-format) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --color) + COMPREPLY=($(compgen -W "always never auto" -- "${cur}")) + return 0 + ;; + --rust-profile-generate) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --rust-profile-use) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --llvm-profile-use) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --reproducible-artifact) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --set) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + x.py__perf__compare) + opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --config) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --build-dir) + COMPREPLY=() + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o plusdirs + fi + return 0 + ;; + --build) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --host) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --target) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --exclude) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --skip) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --rustc-error-format) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --on-fail) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --stage) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --keep-stage) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --keep-stage-std) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --src) + COMPREPLY=() + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o plusdirs + fi + return 0 + ;; + --jobs) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + -j) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --warnings) + COMPREPLY=($(compgen -W "deny warn default" -- "${cur}")) + return 0 + ;; + --error-format) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --color) + COMPREPLY=($(compgen -W "always never auto" -- "${cur}")) + return 0 + ;; + --rust-profile-generate) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --rust-profile-use) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --llvm-profile-use) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --reproducible-artifact) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --set) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + x.py__perf__eprintln) + opts="-v -i -j -h --include --exclude --scenarios --profiles --verbose --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --include) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --exclude) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --scenarios) + COMPREPLY=($(compgen -W "Full IncrFull IncrUnchanged IncrPatched" -- "${cur}")) + return 0 + ;; + --profiles) + COMPREPLY=($(compgen -W "Check Debug Doc Opt Clippy" -- "${cur}")) + return 0 + ;; + --config) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --build-dir) + COMPREPLY=() + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o plusdirs + fi + return 0 + ;; + --build) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --host) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --target) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --skip) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --rustc-error-format) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --on-fail) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --stage) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --keep-stage) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --keep-stage-std) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --src) + COMPREPLY=() + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o plusdirs + fi + return 0 + ;; + --jobs) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + -j) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --warnings) + COMPREPLY=($(compgen -W "deny warn default" -- "${cur}")) + return 0 + ;; + --error-format) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --color) + COMPREPLY=($(compgen -W "always never auto" -- "${cur}")) + return 0 + ;; + --rust-profile-generate) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --rust-profile-use) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --llvm-profile-use) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --reproducible-artifact) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --set) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + x.py__perf__samply) + opts="-v -i -j -h --include --exclude --scenarios --profiles --verbose --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --include) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --exclude) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --scenarios) + COMPREPLY=($(compgen -W "Full IncrFull IncrUnchanged IncrPatched" -- "${cur}")) + return 0 + ;; + --profiles) + COMPREPLY=($(compgen -W "Check Debug Doc Opt Clippy" -- "${cur}")) + return 0 + ;; + --config) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --build-dir) + COMPREPLY=() + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o plusdirs + fi + return 0 + ;; + --build) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --host) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --target) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --skip) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --rustc-error-format) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --on-fail) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --stage) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --keep-stage) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --keep-stage-std) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --src) + COMPREPLY=() + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o plusdirs + fi + return 0 + ;; + --jobs) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + -j) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --warnings) + COMPREPLY=($(compgen -W "deny warn default" -- "${cur}")) + return 0 + ;; + --error-format) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --color) + COMPREPLY=($(compgen -W "always never auto" -- "${cur}")) + return 0 + ;; + --rust-profile-generate) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --rust-profile-use) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --llvm-profile-use) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --reproducible-artifact) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --set) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; x.py__run) opts="-v -i -j -h --args --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then diff --git a/src/etc/completions/x.py.zsh b/src/etc/completions/x.py.zsh index dd71ec00edfa8..71afd5e73caa5 100644 --- a/src/etc/completions/x.py.zsh +++ b/src/etc/completions/x.py.zsh @@ -811,8 +811,246 @@ _arguments "${_arguments_options[@]}" : \ '--skip-stage0-validation[Skip stage0 compiler validation]' \ '-h[Print help (see more with '\''--help'\'')]' \ '--help[Print help (see more with '\''--help'\'')]' \ +'::paths -- paths for the subcommand:_files' \ +'::free_args -- arguments passed to subcommands:_default' \ +":: :_x.py__perf_commands" \ +"*::: :->perf" \ +&& ret=0 + + case $state in + (perf) + words=($line[3] "${words[@]}") + (( CURRENT += 1 )) + curcontext="${curcontext%:*:*}:x.py-perf-command-$line[3]:" + case $line[3] in + (eprintln) +_arguments "${_arguments_options[@]}" : \ +'*--include=[Select the benchmarks that you want to run (separated by commas). If unspecified, all benchmarks will be executed]:INCLUDE:_default' \ +'*--exclude=[Select the benchmarks matching a prefix in this comma-separated list that you don'\''t want to run]:EXCLUDE:_default' \ +'*--scenarios=[Select the scenarios that should be benchmarked]:SCENARIOS:(Full IncrFull IncrUnchanged IncrPatched)' \ +'*--profiles=[Select the profiles that should be benchmarked]:PROFILES:(Check Debug Doc Opt Clippy)' \ +'--config=[TOML configuration file for build]:FILE:_files' \ +'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \ +'--build=[build target of the stage0 compiler]:BUILD:' \ +'--host=[host targets to build]:HOST:' \ +'--target=[target targets to build]:TARGET:' \ +'*--skip=[build paths to skip]:PATH:_files' \ +'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:' \ +'--on-fail=[command to run on failure]:CMD:_cmdstring' \ +'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:' \ +'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:' \ +'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:' \ +'--src=[path to the root of the rust checkout]:DIR:_files -/' \ +'-j+[number of jobs to run in parallel]:JOBS:' \ +'--jobs=[number of jobs to run in parallel]:JOBS:' \ +'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \ +'--error-format=[rustc error format]:FORMAT:' \ +'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \ +'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \ +'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \ +'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \ +'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT:_default' \ +'*--set=[override options in config.toml]:section.option=value:' \ +'*-v[use verbose output (-vv for very verbose)]' \ +'*--verbose[use verbose output (-vv for very verbose)]' \ +'-i[use incremental compilation]' \ +'--incremental[use incremental compilation]' \ +'--include-default-paths[include default paths in addition to the provided ones]' \ +'--dry-run[dry run; don'\''t build anything]' \ +'--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \ +'--json-output[use message-format=json]' \ +'--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \ +'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \ +'--enable-bolt-settings[Enable BOLT link flags]' \ +'--skip-stage0-validation[Skip stage0 compiler validation]' \ +'-h[Print help (see more with '\''--help'\'')]' \ +'--help[Print help (see more with '\''--help'\'')]' \ +'*::paths -- paths for the subcommand:_files' \ +&& ret=0 +;; +(samply) +_arguments "${_arguments_options[@]}" : \ +'*--include=[Select the benchmarks that you want to run (separated by commas). If unspecified, all benchmarks will be executed]:INCLUDE:_default' \ +'*--exclude=[Select the benchmarks matching a prefix in this comma-separated list that you don'\''t want to run]:EXCLUDE:_default' \ +'*--scenarios=[Select the scenarios that should be benchmarked]:SCENARIOS:(Full IncrFull IncrUnchanged IncrPatched)' \ +'*--profiles=[Select the profiles that should be benchmarked]:PROFILES:(Check Debug Doc Opt Clippy)' \ +'--config=[TOML configuration file for build]:FILE:_files' \ +'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \ +'--build=[build target of the stage0 compiler]:BUILD:' \ +'--host=[host targets to build]:HOST:' \ +'--target=[target targets to build]:TARGET:' \ +'*--skip=[build paths to skip]:PATH:_files' \ +'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:' \ +'--on-fail=[command to run on failure]:CMD:_cmdstring' \ +'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:' \ +'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:' \ +'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:' \ +'--src=[path to the root of the rust checkout]:DIR:_files -/' \ +'-j+[number of jobs to run in parallel]:JOBS:' \ +'--jobs=[number of jobs to run in parallel]:JOBS:' \ +'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \ +'--error-format=[rustc error format]:FORMAT:' \ +'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \ +'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \ +'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \ +'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \ +'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT:_default' \ +'*--set=[override options in config.toml]:section.option=value:' \ +'*-v[use verbose output (-vv for very verbose)]' \ +'*--verbose[use verbose output (-vv for very verbose)]' \ +'-i[use incremental compilation]' \ +'--incremental[use incremental compilation]' \ +'--include-default-paths[include default paths in addition to the provided ones]' \ +'--dry-run[dry run; don'\''t build anything]' \ +'--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \ +'--json-output[use message-format=json]' \ +'--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \ +'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \ +'--enable-bolt-settings[Enable BOLT link flags]' \ +'--skip-stage0-validation[Skip stage0 compiler validation]' \ +'-h[Print help (see more with '\''--help'\'')]' \ +'--help[Print help (see more with '\''--help'\'')]' \ '*::paths -- paths for the subcommand:_files' \ && ret=0 +;; +(cachegrind) +_arguments "${_arguments_options[@]}" : \ +'*--include=[Select the benchmarks that you want to run (separated by commas). If unspecified, all benchmarks will be executed]:INCLUDE:_default' \ +'*--exclude=[Select the benchmarks matching a prefix in this comma-separated list that you don'\''t want to run]:EXCLUDE:_default' \ +'*--scenarios=[Select the scenarios that should be benchmarked]:SCENARIOS:(Full IncrFull IncrUnchanged IncrPatched)' \ +'*--profiles=[Select the profiles that should be benchmarked]:PROFILES:(Check Debug Doc Opt Clippy)' \ +'--config=[TOML configuration file for build]:FILE:_files' \ +'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \ +'--build=[build target of the stage0 compiler]:BUILD:' \ +'--host=[host targets to build]:HOST:' \ +'--target=[target targets to build]:TARGET:' \ +'*--skip=[build paths to skip]:PATH:_files' \ +'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:' \ +'--on-fail=[command to run on failure]:CMD:_cmdstring' \ +'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:' \ +'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:' \ +'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:' \ +'--src=[path to the root of the rust checkout]:DIR:_files -/' \ +'-j+[number of jobs to run in parallel]:JOBS:' \ +'--jobs=[number of jobs to run in parallel]:JOBS:' \ +'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \ +'--error-format=[rustc error format]:FORMAT:' \ +'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \ +'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \ +'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \ +'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \ +'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT:_default' \ +'*--set=[override options in config.toml]:section.option=value:' \ +'*-v[use verbose output (-vv for very verbose)]' \ +'*--verbose[use verbose output (-vv for very verbose)]' \ +'-i[use incremental compilation]' \ +'--incremental[use incremental compilation]' \ +'--include-default-paths[include default paths in addition to the provided ones]' \ +'--dry-run[dry run; don'\''t build anything]' \ +'--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \ +'--json-output[use message-format=json]' \ +'--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \ +'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \ +'--enable-bolt-settings[Enable BOLT link flags]' \ +'--skip-stage0-validation[Skip stage0 compiler validation]' \ +'-h[Print help (see more with '\''--help'\'')]' \ +'--help[Print help (see more with '\''--help'\'')]' \ +'*::paths -- paths for the subcommand:_files' \ +&& ret=0 +;; +(benchmark) +_arguments "${_arguments_options[@]}" : \ +'*--include=[Select the benchmarks that you want to run (separated by commas). If unspecified, all benchmarks will be executed]:INCLUDE:_default' \ +'*--exclude=[Select the benchmarks matching a prefix in this comma-separated list that you don'\''t want to run]:EXCLUDE:_default' \ +'*--scenarios=[Select the scenarios that should be benchmarked]:SCENARIOS:(Full IncrFull IncrUnchanged IncrPatched)' \ +'*--profiles=[Select the profiles that should be benchmarked]:PROFILES:(Check Debug Doc Opt Clippy)' \ +'--config=[TOML configuration file for build]:FILE:_files' \ +'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \ +'--build=[build target of the stage0 compiler]:BUILD:' \ +'--host=[host targets to build]:HOST:' \ +'--target=[target targets to build]:TARGET:' \ +'*--skip=[build paths to skip]:PATH:_files' \ +'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:' \ +'--on-fail=[command to run on failure]:CMD:_cmdstring' \ +'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:' \ +'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:' \ +'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:' \ +'--src=[path to the root of the rust checkout]:DIR:_files -/' \ +'-j+[number of jobs to run in parallel]:JOBS:' \ +'--jobs=[number of jobs to run in parallel]:JOBS:' \ +'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \ +'--error-format=[rustc error format]:FORMAT:' \ +'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \ +'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \ +'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \ +'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \ +'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT:_default' \ +'*--set=[override options in config.toml]:section.option=value:' \ +'*-v[use verbose output (-vv for very verbose)]' \ +'*--verbose[use verbose output (-vv for very verbose)]' \ +'-i[use incremental compilation]' \ +'--incremental[use incremental compilation]' \ +'--include-default-paths[include default paths in addition to the provided ones]' \ +'--dry-run[dry run; don'\''t build anything]' \ +'--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \ +'--json-output[use message-format=json]' \ +'--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \ +'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \ +'--enable-bolt-settings[Enable BOLT link flags]' \ +'--skip-stage0-validation[Skip stage0 compiler validation]' \ +'-h[Print help (see more with '\''--help'\'')]' \ +'--help[Print help (see more with '\''--help'\'')]' \ +':benchmark-id -- Identifier to associate benchmark results with:_default' \ +'*::paths -- paths for the subcommand:_files' \ +&& ret=0 +;; +(compare) +_arguments "${_arguments_options[@]}" : \ +'--config=[TOML configuration file for build]:FILE:_files' \ +'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \ +'--build=[build target of the stage0 compiler]:BUILD:' \ +'--host=[host targets to build]:HOST:' \ +'--target=[target targets to build]:TARGET:' \ +'*--exclude=[build paths to exclude]:PATH:_files' \ +'*--skip=[build paths to skip]:PATH:_files' \ +'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:' \ +'--on-fail=[command to run on failure]:CMD:_cmdstring' \ +'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:' \ +'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:' \ +'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:' \ +'--src=[path to the root of the rust checkout]:DIR:_files -/' \ +'-j+[number of jobs to run in parallel]:JOBS:' \ +'--jobs=[number of jobs to run in parallel]:JOBS:' \ +'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \ +'--error-format=[rustc error format]:FORMAT:' \ +'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \ +'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \ +'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \ +'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \ +'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT:_default' \ +'*--set=[override options in config.toml]:section.option=value:' \ +'*-v[use verbose output (-vv for very verbose)]' \ +'*--verbose[use verbose output (-vv for very verbose)]' \ +'-i[use incremental compilation]' \ +'--incremental[use incremental compilation]' \ +'--include-default-paths[include default paths in addition to the provided ones]' \ +'--dry-run[dry run; don'\''t build anything]' \ +'--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \ +'--json-output[use message-format=json]' \ +'--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \ +'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \ +'--enable-bolt-settings[Enable BOLT link flags]' \ +'--skip-stage0-validation[Skip stage0 compiler validation]' \ +'-h[Print help (see more with '\''--help'\'')]' \ +'--help[Print help (see more with '\''--help'\'')]' \ +':base -- The name of the base artifact to be compared:_default' \ +':modified -- The name of the modified artifact to be compared:_default' \ +'*::paths -- paths for the subcommand:_files' \ +&& ret=0 +;; + esac + ;; +esac ;; esac ;; @@ -838,7 +1076,7 @@ _x.py_commands() { 'setup:Set up the environment for development' \ 'suggest:Suggest a subset of tests to run, based on modified files' \ 'vendor:Vendor dependencies' \ -'perf:Perform profiling and benchmarking of the compiler using the \`rustc-perf-wrapper\` tool' \ +'perf:Perform profiling and benchmarking of the compiler using \`rustc-perf\`' \ ) _describe -t commands 'x.py commands' commands "$@" } @@ -899,9 +1137,40 @@ _x.py__miri_commands() { } (( $+functions[_x.py__perf_commands] )) || _x.py__perf_commands() { - local commands; commands=() + local commands; commands=( +'eprintln:Run \`profile_local eprintln\`. This executes the compiler on the given benchmarks and stores its stderr output' \ +'samply:Run \`profile_local samply\` This executes the compiler on the given benchmarks and profiles it with \`samply\`. You need to install \`samply\`, e.g. using \`cargo install samply\`' \ +'cachegrind:Run \`profile_local cachegrind\`. This executes the compiler on the given benchmarks under \`Cachegrind\`' \ +'benchmark:Run compile benchmarks with a locally built compiler' \ +'compare:Compare the results of two previously executed benchmark runs' \ + ) _describe -t commands 'x.py perf commands' commands "$@" } +(( $+functions[_x.py__perf__benchmark_commands] )) || +_x.py__perf__benchmark_commands() { + local commands; commands=() + _describe -t commands 'x.py perf benchmark commands' commands "$@" +} +(( $+functions[_x.py__perf__cachegrind_commands] )) || +_x.py__perf__cachegrind_commands() { + local commands; commands=() + _describe -t commands 'x.py perf cachegrind commands' commands "$@" +} +(( $+functions[_x.py__perf__compare_commands] )) || +_x.py__perf__compare_commands() { + local commands; commands=() + _describe -t commands 'x.py perf compare commands' commands "$@" +} +(( $+functions[_x.py__perf__eprintln_commands] )) || +_x.py__perf__eprintln_commands() { + local commands; commands=() + _describe -t commands 'x.py perf eprintln commands' commands "$@" +} +(( $+functions[_x.py__perf__samply_commands] )) || +_x.py__perf__samply_commands() { + local commands; commands=() + _describe -t commands 'x.py perf samply commands' commands "$@" +} (( $+functions[_x.py__run_commands] )) || _x.py__run_commands() { local commands; commands=() diff --git a/src/etc/completions/x.sh b/src/etc/completions/x.sh index 057af1ffc6d53..26a44b3ff2f52 100644 --- a/src/etc/completions/x.sh +++ b/src/etc/completions/x.sh @@ -63,6 +63,21 @@ _x() { x,vendor) cmd="x__vendor" ;; + x__perf,benchmark) + cmd="x__perf__benchmark" + ;; + x__perf,cachegrind) + cmd="x__perf__cachegrind" + ;; + x__perf,compare) + cmd="x__perf__compare" + ;; + x__perf,eprintln) + cmd="x__perf__eprintln" + ;; + x__perf,samply) + cmd="x__perf__samply" + ;; *) ;; esac @@ -2359,7 +2374,7 @@ _x() { return 0 ;; x__perf) - opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." + opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]... eprintln samply cachegrind benchmark compare" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -2547,6 +2562,999 @@ _x() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + x__perf__benchmark) + opts="-v -i -j -h --include --exclude --scenarios --profiles --verbose --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --include) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --exclude) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --scenarios) + COMPREPLY=($(compgen -W "Full IncrFull IncrUnchanged IncrPatched" -- "${cur}")) + return 0 + ;; + --profiles) + COMPREPLY=($(compgen -W "Check Debug Doc Opt Clippy" -- "${cur}")) + return 0 + ;; + --config) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --build-dir) + COMPREPLY=() + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o plusdirs + fi + return 0 + ;; + --build) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --host) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --target) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --skip) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --rustc-error-format) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --on-fail) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --stage) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --keep-stage) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --keep-stage-std) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --src) + COMPREPLY=() + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o plusdirs + fi + return 0 + ;; + --jobs) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + -j) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --warnings) + COMPREPLY=($(compgen -W "deny warn default" -- "${cur}")) + return 0 + ;; + --error-format) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --color) + COMPREPLY=($(compgen -W "always never auto" -- "${cur}")) + return 0 + ;; + --rust-profile-generate) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --rust-profile-use) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --llvm-profile-use) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --reproducible-artifact) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --set) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + x__perf__cachegrind) + opts="-v -i -j -h --include --exclude --scenarios --profiles --verbose --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --include) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --exclude) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --scenarios) + COMPREPLY=($(compgen -W "Full IncrFull IncrUnchanged IncrPatched" -- "${cur}")) + return 0 + ;; + --profiles) + COMPREPLY=($(compgen -W "Check Debug Doc Opt Clippy" -- "${cur}")) + return 0 + ;; + --config) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --build-dir) + COMPREPLY=() + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o plusdirs + fi + return 0 + ;; + --build) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --host) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --target) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --skip) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --rustc-error-format) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --on-fail) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --stage) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --keep-stage) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --keep-stage-std) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --src) + COMPREPLY=() + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o plusdirs + fi + return 0 + ;; + --jobs) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + -j) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --warnings) + COMPREPLY=($(compgen -W "deny warn default" -- "${cur}")) + return 0 + ;; + --error-format) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --color) + COMPREPLY=($(compgen -W "always never auto" -- "${cur}")) + return 0 + ;; + --rust-profile-generate) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --rust-profile-use) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --llvm-profile-use) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --reproducible-artifact) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --set) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + x__perf__compare) + opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --config) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --build-dir) + COMPREPLY=() + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o plusdirs + fi + return 0 + ;; + --build) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --host) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --target) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --exclude) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --skip) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --rustc-error-format) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --on-fail) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --stage) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --keep-stage) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --keep-stage-std) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --src) + COMPREPLY=() + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o plusdirs + fi + return 0 + ;; + --jobs) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + -j) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --warnings) + COMPREPLY=($(compgen -W "deny warn default" -- "${cur}")) + return 0 + ;; + --error-format) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --color) + COMPREPLY=($(compgen -W "always never auto" -- "${cur}")) + return 0 + ;; + --rust-profile-generate) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --rust-profile-use) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --llvm-profile-use) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --reproducible-artifact) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --set) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + x__perf__eprintln) + opts="-v -i -j -h --include --exclude --scenarios --profiles --verbose --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --include) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --exclude) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --scenarios) + COMPREPLY=($(compgen -W "Full IncrFull IncrUnchanged IncrPatched" -- "${cur}")) + return 0 + ;; + --profiles) + COMPREPLY=($(compgen -W "Check Debug Doc Opt Clippy" -- "${cur}")) + return 0 + ;; + --config) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --build-dir) + COMPREPLY=() + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o plusdirs + fi + return 0 + ;; + --build) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --host) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --target) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --skip) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --rustc-error-format) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --on-fail) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --stage) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --keep-stage) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --keep-stage-std) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --src) + COMPREPLY=() + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o plusdirs + fi + return 0 + ;; + --jobs) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + -j) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --warnings) + COMPREPLY=($(compgen -W "deny warn default" -- "${cur}")) + return 0 + ;; + --error-format) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --color) + COMPREPLY=($(compgen -W "always never auto" -- "${cur}")) + return 0 + ;; + --rust-profile-generate) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --rust-profile-use) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --llvm-profile-use) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --reproducible-artifact) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --set) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + x__perf__samply) + opts="-v -i -j -h --include --exclude --scenarios --profiles --verbose --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --include) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --exclude) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --scenarios) + COMPREPLY=($(compgen -W "Full IncrFull IncrUnchanged IncrPatched" -- "${cur}")) + return 0 + ;; + --profiles) + COMPREPLY=($(compgen -W "Check Debug Doc Opt Clippy" -- "${cur}")) + return 0 + ;; + --config) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --build-dir) + COMPREPLY=() + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o plusdirs + fi + return 0 + ;; + --build) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --host) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --target) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --skip) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --rustc-error-format) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --on-fail) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --stage) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --keep-stage) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --keep-stage-std) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --src) + COMPREPLY=() + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o plusdirs + fi + return 0 + ;; + --jobs) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + -j) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --warnings) + COMPREPLY=($(compgen -W "deny warn default" -- "${cur}")) + return 0 + ;; + --error-format) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + --color) + COMPREPLY=($(compgen -W "always never auto" -- "${cur}")) + return 0 + ;; + --rust-profile-generate) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --rust-profile-use) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --llvm-profile-use) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --reproducible-artifact) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --set) + COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; x__run) opts="-v -i -j -h --args --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [PATHS]... [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then diff --git a/src/etc/completions/x.zsh b/src/etc/completions/x.zsh index 6215f9af83306..b08eb06f8e642 100644 --- a/src/etc/completions/x.zsh +++ b/src/etc/completions/x.zsh @@ -811,8 +811,246 @@ _arguments "${_arguments_options[@]}" : \ '--skip-stage0-validation[Skip stage0 compiler validation]' \ '-h[Print help (see more with '\''--help'\'')]' \ '--help[Print help (see more with '\''--help'\'')]' \ +'::paths -- paths for the subcommand:_files' \ +'::free_args -- arguments passed to subcommands:_default' \ +":: :_x__perf_commands" \ +"*::: :->perf" \ +&& ret=0 + + case $state in + (perf) + words=($line[3] "${words[@]}") + (( CURRENT += 1 )) + curcontext="${curcontext%:*:*}:x-perf-command-$line[3]:" + case $line[3] in + (eprintln) +_arguments "${_arguments_options[@]}" : \ +'*--include=[Select the benchmarks that you want to run (separated by commas). If unspecified, all benchmarks will be executed]:INCLUDE:_default' \ +'*--exclude=[Select the benchmarks matching a prefix in this comma-separated list that you don'\''t want to run]:EXCLUDE:_default' \ +'*--scenarios=[Select the scenarios that should be benchmarked]:SCENARIOS:(Full IncrFull IncrUnchanged IncrPatched)' \ +'*--profiles=[Select the profiles that should be benchmarked]:PROFILES:(Check Debug Doc Opt Clippy)' \ +'--config=[TOML configuration file for build]:FILE:_files' \ +'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \ +'--build=[build target of the stage0 compiler]:BUILD:' \ +'--host=[host targets to build]:HOST:' \ +'--target=[target targets to build]:TARGET:' \ +'*--skip=[build paths to skip]:PATH:_files' \ +'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:' \ +'--on-fail=[command to run on failure]:CMD:_cmdstring' \ +'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:' \ +'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:' \ +'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:' \ +'--src=[path to the root of the rust checkout]:DIR:_files -/' \ +'-j+[number of jobs to run in parallel]:JOBS:' \ +'--jobs=[number of jobs to run in parallel]:JOBS:' \ +'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \ +'--error-format=[rustc error format]:FORMAT:' \ +'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \ +'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \ +'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \ +'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \ +'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT:_default' \ +'*--set=[override options in config.toml]:section.option=value:' \ +'*-v[use verbose output (-vv for very verbose)]' \ +'*--verbose[use verbose output (-vv for very verbose)]' \ +'-i[use incremental compilation]' \ +'--incremental[use incremental compilation]' \ +'--include-default-paths[include default paths in addition to the provided ones]' \ +'--dry-run[dry run; don'\''t build anything]' \ +'--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \ +'--json-output[use message-format=json]' \ +'--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \ +'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \ +'--enable-bolt-settings[Enable BOLT link flags]' \ +'--skip-stage0-validation[Skip stage0 compiler validation]' \ +'-h[Print help (see more with '\''--help'\'')]' \ +'--help[Print help (see more with '\''--help'\'')]' \ +'*::paths -- paths for the subcommand:_files' \ +&& ret=0 +;; +(samply) +_arguments "${_arguments_options[@]}" : \ +'*--include=[Select the benchmarks that you want to run (separated by commas). If unspecified, all benchmarks will be executed]:INCLUDE:_default' \ +'*--exclude=[Select the benchmarks matching a prefix in this comma-separated list that you don'\''t want to run]:EXCLUDE:_default' \ +'*--scenarios=[Select the scenarios that should be benchmarked]:SCENARIOS:(Full IncrFull IncrUnchanged IncrPatched)' \ +'*--profiles=[Select the profiles that should be benchmarked]:PROFILES:(Check Debug Doc Opt Clippy)' \ +'--config=[TOML configuration file for build]:FILE:_files' \ +'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \ +'--build=[build target of the stage0 compiler]:BUILD:' \ +'--host=[host targets to build]:HOST:' \ +'--target=[target targets to build]:TARGET:' \ +'*--skip=[build paths to skip]:PATH:_files' \ +'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:' \ +'--on-fail=[command to run on failure]:CMD:_cmdstring' \ +'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:' \ +'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:' \ +'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:' \ +'--src=[path to the root of the rust checkout]:DIR:_files -/' \ +'-j+[number of jobs to run in parallel]:JOBS:' \ +'--jobs=[number of jobs to run in parallel]:JOBS:' \ +'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \ +'--error-format=[rustc error format]:FORMAT:' \ +'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \ +'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \ +'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \ +'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \ +'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT:_default' \ +'*--set=[override options in config.toml]:section.option=value:' \ +'*-v[use verbose output (-vv for very verbose)]' \ +'*--verbose[use verbose output (-vv for very verbose)]' \ +'-i[use incremental compilation]' \ +'--incremental[use incremental compilation]' \ +'--include-default-paths[include default paths in addition to the provided ones]' \ +'--dry-run[dry run; don'\''t build anything]' \ +'--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \ +'--json-output[use message-format=json]' \ +'--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \ +'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \ +'--enable-bolt-settings[Enable BOLT link flags]' \ +'--skip-stage0-validation[Skip stage0 compiler validation]' \ +'-h[Print help (see more with '\''--help'\'')]' \ +'--help[Print help (see more with '\''--help'\'')]' \ '*::paths -- paths for the subcommand:_files' \ && ret=0 +;; +(cachegrind) +_arguments "${_arguments_options[@]}" : \ +'*--include=[Select the benchmarks that you want to run (separated by commas). If unspecified, all benchmarks will be executed]:INCLUDE:_default' \ +'*--exclude=[Select the benchmarks matching a prefix in this comma-separated list that you don'\''t want to run]:EXCLUDE:_default' \ +'*--scenarios=[Select the scenarios that should be benchmarked]:SCENARIOS:(Full IncrFull IncrUnchanged IncrPatched)' \ +'*--profiles=[Select the profiles that should be benchmarked]:PROFILES:(Check Debug Doc Opt Clippy)' \ +'--config=[TOML configuration file for build]:FILE:_files' \ +'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \ +'--build=[build target of the stage0 compiler]:BUILD:' \ +'--host=[host targets to build]:HOST:' \ +'--target=[target targets to build]:TARGET:' \ +'*--skip=[build paths to skip]:PATH:_files' \ +'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:' \ +'--on-fail=[command to run on failure]:CMD:_cmdstring' \ +'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:' \ +'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:' \ +'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:' \ +'--src=[path to the root of the rust checkout]:DIR:_files -/' \ +'-j+[number of jobs to run in parallel]:JOBS:' \ +'--jobs=[number of jobs to run in parallel]:JOBS:' \ +'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \ +'--error-format=[rustc error format]:FORMAT:' \ +'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \ +'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \ +'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \ +'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \ +'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT:_default' \ +'*--set=[override options in config.toml]:section.option=value:' \ +'*-v[use verbose output (-vv for very verbose)]' \ +'*--verbose[use verbose output (-vv for very verbose)]' \ +'-i[use incremental compilation]' \ +'--incremental[use incremental compilation]' \ +'--include-default-paths[include default paths in addition to the provided ones]' \ +'--dry-run[dry run; don'\''t build anything]' \ +'--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \ +'--json-output[use message-format=json]' \ +'--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \ +'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \ +'--enable-bolt-settings[Enable BOLT link flags]' \ +'--skip-stage0-validation[Skip stage0 compiler validation]' \ +'-h[Print help (see more with '\''--help'\'')]' \ +'--help[Print help (see more with '\''--help'\'')]' \ +'*::paths -- paths for the subcommand:_files' \ +&& ret=0 +;; +(benchmark) +_arguments "${_arguments_options[@]}" : \ +'*--include=[Select the benchmarks that you want to run (separated by commas). If unspecified, all benchmarks will be executed]:INCLUDE:_default' \ +'*--exclude=[Select the benchmarks matching a prefix in this comma-separated list that you don'\''t want to run]:EXCLUDE:_default' \ +'*--scenarios=[Select the scenarios that should be benchmarked]:SCENARIOS:(Full IncrFull IncrUnchanged IncrPatched)' \ +'*--profiles=[Select the profiles that should be benchmarked]:PROFILES:(Check Debug Doc Opt Clippy)' \ +'--config=[TOML configuration file for build]:FILE:_files' \ +'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \ +'--build=[build target of the stage0 compiler]:BUILD:' \ +'--host=[host targets to build]:HOST:' \ +'--target=[target targets to build]:TARGET:' \ +'*--skip=[build paths to skip]:PATH:_files' \ +'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:' \ +'--on-fail=[command to run on failure]:CMD:_cmdstring' \ +'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:' \ +'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:' \ +'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:' \ +'--src=[path to the root of the rust checkout]:DIR:_files -/' \ +'-j+[number of jobs to run in parallel]:JOBS:' \ +'--jobs=[number of jobs to run in parallel]:JOBS:' \ +'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \ +'--error-format=[rustc error format]:FORMAT:' \ +'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \ +'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \ +'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \ +'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \ +'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT:_default' \ +'*--set=[override options in config.toml]:section.option=value:' \ +'*-v[use verbose output (-vv for very verbose)]' \ +'*--verbose[use verbose output (-vv for very verbose)]' \ +'-i[use incremental compilation]' \ +'--incremental[use incremental compilation]' \ +'--include-default-paths[include default paths in addition to the provided ones]' \ +'--dry-run[dry run; don'\''t build anything]' \ +'--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \ +'--json-output[use message-format=json]' \ +'--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \ +'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \ +'--enable-bolt-settings[Enable BOLT link flags]' \ +'--skip-stage0-validation[Skip stage0 compiler validation]' \ +'-h[Print help (see more with '\''--help'\'')]' \ +'--help[Print help (see more with '\''--help'\'')]' \ +':benchmark-id -- Identifier to associate benchmark results with:_default' \ +'*::paths -- paths for the subcommand:_files' \ +&& ret=0 +;; +(compare) +_arguments "${_arguments_options[@]}" : \ +'--config=[TOML configuration file for build]:FILE:_files' \ +'--build-dir=[Build directory, overrides \`build.build-dir\` in \`config.toml\`]:DIR:_files -/' \ +'--build=[build target of the stage0 compiler]:BUILD:' \ +'--host=[host targets to build]:HOST:' \ +'--target=[target targets to build]:TARGET:' \ +'*--exclude=[build paths to exclude]:PATH:_files' \ +'*--skip=[build paths to skip]:PATH:_files' \ +'--rustc-error-format=[]:RUSTC_ERROR_FORMAT:' \ +'--on-fail=[command to run on failure]:CMD:_cmdstring' \ +'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:' \ +'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:' \ +'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:' \ +'--src=[path to the root of the rust checkout]:DIR:_files -/' \ +'-j+[number of jobs to run in parallel]:JOBS:' \ +'--jobs=[number of jobs to run in parallel]:JOBS:' \ +'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \ +'--error-format=[rustc error format]:FORMAT:' \ +'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \ +'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \ +'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \ +'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \ +'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT:_default' \ +'*--set=[override options in config.toml]:section.option=value:' \ +'*-v[use verbose output (-vv for very verbose)]' \ +'*--verbose[use verbose output (-vv for very verbose)]' \ +'-i[use incremental compilation]' \ +'--incremental[use incremental compilation]' \ +'--include-default-paths[include default paths in addition to the provided ones]' \ +'--dry-run[dry run; don'\''t build anything]' \ +'--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \ +'--json-output[use message-format=json]' \ +'--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \ +'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \ +'--enable-bolt-settings[Enable BOLT link flags]' \ +'--skip-stage0-validation[Skip stage0 compiler validation]' \ +'-h[Print help (see more with '\''--help'\'')]' \ +'--help[Print help (see more with '\''--help'\'')]' \ +':base -- The name of the base artifact to be compared:_default' \ +':modified -- The name of the modified artifact to be compared:_default' \ +'*::paths -- paths for the subcommand:_files' \ +&& ret=0 +;; + esac + ;; +esac ;; esac ;; @@ -838,7 +1076,7 @@ _x_commands() { 'setup:Set up the environment for development' \ 'suggest:Suggest a subset of tests to run, based on modified files' \ 'vendor:Vendor dependencies' \ -'perf:Perform profiling and benchmarking of the compiler using the \`rustc-perf-wrapper\` tool' \ +'perf:Perform profiling and benchmarking of the compiler using \`rustc-perf\`' \ ) _describe -t commands 'x commands' commands "$@" } @@ -899,9 +1137,40 @@ _x__miri_commands() { } (( $+functions[_x__perf_commands] )) || _x__perf_commands() { - local commands; commands=() + local commands; commands=( +'eprintln:Run \`profile_local eprintln\`. This executes the compiler on the given benchmarks and stores its stderr output' \ +'samply:Run \`profile_local samply\` This executes the compiler on the given benchmarks and profiles it with \`samply\`. You need to install \`samply\`, e.g. using \`cargo install samply\`' \ +'cachegrind:Run \`profile_local cachegrind\`. This executes the compiler on the given benchmarks under \`Cachegrind\`' \ +'benchmark:Run compile benchmarks with a locally built compiler' \ +'compare:Compare the results of two previously executed benchmark runs' \ + ) _describe -t commands 'x perf commands' commands "$@" } +(( $+functions[_x__perf__benchmark_commands] )) || +_x__perf__benchmark_commands() { + local commands; commands=() + _describe -t commands 'x perf benchmark commands' commands "$@" +} +(( $+functions[_x__perf__cachegrind_commands] )) || +_x__perf__cachegrind_commands() { + local commands; commands=() + _describe -t commands 'x perf cachegrind commands' commands "$@" +} +(( $+functions[_x__perf__compare_commands] )) || +_x__perf__compare_commands() { + local commands; commands=() + _describe -t commands 'x perf compare commands' commands "$@" +} +(( $+functions[_x__perf__eprintln_commands] )) || +_x__perf__eprintln_commands() { + local commands; commands=() + _describe -t commands 'x perf eprintln commands' commands "$@" +} +(( $+functions[_x__perf__samply_commands] )) || +_x__perf__samply_commands() { + local commands; commands=() + _describe -t commands 'x perf samply commands' commands "$@" +} (( $+functions[_x__run_commands] )) || _x__run_commands() { local commands; commands=() diff --git a/src/etc/dec2flt_table.py b/src/etc/dec2flt_table.py index 791186de9c190..ecfdacc1f3115 100755 --- a/src/etc/dec2flt_table.py +++ b/src/etc/dec2flt_table.py @@ -43,10 +43,10 @@ def main(): print(HEADER.strip()) print() - print("pub const SMALLEST_POWER_OF_FIVE: i32 = {};".format(min_exp)) - print("pub const LARGEST_POWER_OF_FIVE: i32 = {};".format(max_exp)) - print("pub const N_POWERS_OF_FIVE: usize = ", end="") - print("(LARGEST_POWER_OF_FIVE - SMALLEST_POWER_OF_FIVE + 1) as usize;") + print("pub(super) const SMALLEST_POWER_OF_FIVE: i32 = {};".format(min_exp)) + print("pub(super) const LARGEST_POWER_OF_FIVE: i32 = {};".format(max_exp)) + print("pub(super) const N_POWERS_OF_FIVE: usize =") + print(" (LARGEST_POWER_OF_FIVE - SMALLEST_POWER_OF_FIVE + 1) as usize;") print() print_proper_powers(min_exp, max_exp, bias) @@ -97,7 +97,7 @@ def print_proper_powers(min_exp, max_exp, bias): print(STATIC_WARNING.strip()) print("#[rustfmt::skip]") typ = "[(u64, u64); N_POWERS_OF_FIVE]" - print("pub static POWER_OF_FIVE_128: {} = [".format(typ)) + print("pub(super) static POWER_OF_FIVE_128: {} = [".format(typ)) for c, exp in powers: hi = "0x{:x}".format(c // (1 << 64)) lo = "0x{:x}".format(c % (1 << 64)) diff --git a/src/etc/gdb_providers.py b/src/etc/gdb_providers.py index 34bb5c39909e4..c8f4a32cb17e2 100644 --- a/src/etc/gdb_providers.py +++ b/src/etc/gdb_providers.py @@ -71,7 +71,7 @@ def __init__(self, valobj): self._valobj = valobj buf = self._valobj["inner"]["inner"] is_windows = "Wtf8Buf" in buf.type.name - vec = buf[ZERO_FIELD] if is_windows else buf + vec = buf["bytes"] if is_windows else buf self._length = int(vec["len"]) self._data_ptr = unwrap_unique_or_non_null(vec["buf"]["inner"]["ptr"]) diff --git a/src/etc/installer/msi/rust.wxs b/src/etc/installer/msi/rust.wxs index 2d155bf0b1019..f29e1e4d27a27 100644 --- a/src/etc/installer/msi/rust.wxs +++ b/src/etc/installer/msi/rust.wxs @@ -172,11 +172,6 @@ - - - - - @@ -284,41 +279,7 @@ - - - - - - - - - - - - - - - + diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 70749f7cb17a0..c593cdcbcd225 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -13,7 +13,7 @@ rinja = { version = "0.3", default-features = false, features = ["config"] } base64 = "0.21.7" itertools = "0.12" indexmap = "2" -minifier = { version = "0.3.2", default-features = false } +minifier = { version = "0.3.4", default-features = false } pulldown-cmark-old = { version = "0.9.6", package = "pulldown-cmark", default-features = false } regex = "1" rustdoc-json-types = { path = "../rustdoc-json-types" } @@ -33,6 +33,7 @@ features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] [build-dependencies] sha2 = "0.10.8" +minifier = { version = "0.3.2", default-features = false } [dev-dependencies] expect-test = "1.4.0" diff --git a/src/librustdoc/build.rs b/src/librustdoc/build.rs index 69337fb1d2504..07269d5bdc240 100644 --- a/src/librustdoc/build.rs +++ b/src/librustdoc/build.rs @@ -1,3 +1,6 @@ +use std::str; + +use sha2::Digest; fn main() { // generate sha256 files // this avoids having to perform hashing at runtime @@ -17,10 +20,15 @@ fn main() { "static/images/rust-logo.svg", "static/images/favicon.svg", "static/images/favicon-32x32.png", + "static/fonts/FiraSans-Italic.woff2", "static/fonts/FiraSans-Regular.woff2", "static/fonts/FiraSans-Medium.woff2", + "static/fonts/FiraSans-MediumItalic.woff2", + "static/fonts/FiraMono-Regular.woff2", + "static/fonts/FiraMono-Medium.woff2", "static/fonts/FiraSans-LICENSE.txt", "static/fonts/SourceSerif4-Regular.ttf.woff2", + "static/fonts/SourceSerif4-Semibold.ttf.woff2", "static/fonts/SourceSerif4-Bold.ttf.woff2", "static/fonts/SourceSerif4-It.ttf.woff2", "static/fonts/SourceSerif4-LICENSE.md", @@ -35,14 +43,27 @@ fn main() { for path in files { let inpath = format!("html/{path}"); println!("cargo::rerun-if-changed={inpath}"); - let bytes = std::fs::read(inpath).expect("static path exists"); - use sha2::Digest; - let bytes = sha2::Sha256::digest(bytes); - let mut digest = format!("-{bytes:x}"); + let data_bytes = std::fs::read(&inpath).expect("static path exists"); + let hash_bytes = sha2::Sha256::digest(&data_bytes); + let mut digest = format!("-{hash_bytes:x}"); digest.truncate(9); let outpath = std::path::PathBuf::from(format!("{out_dir}/{path}.sha256")); std::fs::create_dir_all(outpath.parent().expect("all file paths are in a directory")) .expect("should be able to write to out_dir"); std::fs::write(&outpath, digest.as_bytes()).expect("write to out_dir"); + let minified_path = std::path::PathBuf::from(format!("{out_dir}/{path}.min")); + if path.ends_with(".js") || path.ends_with(".css") { + let minified: String = if path.ends_with(".css") { + minifier::css::minify(str::from_utf8(&data_bytes).unwrap()) + .unwrap() + .to_string() + .into() + } else { + minifier::js::minify(str::from_utf8(&data_bytes).unwrap()).to_string().into() + }; + std::fs::write(&minified_path, minified.as_bytes()).expect("write to out_dir"); + } else { + std::fs::copy(&inpath, &minified_path).unwrap(); + } } } diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index c59dce185f4c1..bec7fbe8f52bd 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -13,6 +13,7 @@ use rustc_session::parse::ParseSess; use rustc_span::Span; use rustc_span::symbol::{Symbol, sym}; +use crate::display::Joined as _; use crate::html::escape::Escape; #[cfg(test)] @@ -90,11 +91,11 @@ impl Cfg { }, MetaItemKind::List(ref items) => { let orig_len = items.len(); - let sub_cfgs = + let mut sub_cfgs = items.iter().filter_map(|i| Cfg::parse_nested(i, exclude).transpose()); let ret = match name { - sym::all => sub_cfgs.fold(Ok(Cfg::True), |x, y| Ok(x? & y?)), - sym::any => sub_cfgs.fold(Ok(Cfg::False), |x, y| Ok(x? | y?)), + sym::all => sub_cfgs.try_fold(Cfg::True, |x, y| Ok(x & y?)), + sym::any => sub_cfgs.try_fold(Cfg::False, |x, y| Ok(x | y?)), sym::not => { if orig_len == 1 { let mut sub_cfgs = sub_cfgs.collect::>(); @@ -389,6 +390,60 @@ fn write_with_opt_paren( Ok(()) } +impl Display<'_> { + fn display_sub_cfgs( + &self, + fmt: &mut fmt::Formatter<'_>, + sub_cfgs: &[Cfg], + separator: &str, + ) -> fmt::Result { + use fmt::Display as _; + + let short_longhand = self.1.is_long() && { + let all_crate_features = + sub_cfgs.iter().all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::feature, Some(_)))); + let all_target_features = sub_cfgs + .iter() + .all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::target_feature, Some(_)))); + + if all_crate_features { + fmt.write_str("crate features ")?; + true + } else if all_target_features { + fmt.write_str("target features ")?; + true + } else { + false + } + }; + + fmt::from_fn(|f| { + sub_cfgs + .iter() + .map(|sub_cfg| { + fmt::from_fn(move |fmt| { + if let Cfg::Cfg(_, Some(feat)) = sub_cfg + && short_longhand + { + if self.1.is_html() { + write!(fmt, "{feat}")?; + } else { + write!(fmt, "`{feat}`")?; + } + } else { + write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1))?; + } + Ok(()) + }) + }) + .joined(separator, f) + }) + .fmt(fmt)?; + + Ok(()) + } +} + impl fmt::Display for Display<'_> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match *self.0 { @@ -396,11 +451,20 @@ impl fmt::Display for Display<'_> { Cfg::Any(ref sub_cfgs) => { let separator = if sub_cfgs.iter().all(Cfg::is_simple) { " nor " } else { ", nor " }; - for (i, sub_cfg) in sub_cfgs.iter().enumerate() { - fmt.write_str(if i == 0 { "neither " } else { separator })?; - write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1))?; - } - Ok(()) + fmt.write_str("neither ")?; + + sub_cfgs + .iter() + .map(|sub_cfg| { + fmt::from_fn(|fmt| { + write_with_opt_paren( + fmt, + !sub_cfg.is_all(), + Display(sub_cfg, self.1), + ) + }) + }) + .joined(separator, fmt) } ref simple @ Cfg::Cfg(..) => write!(fmt, "non-{}", Display(simple, self.1)), ref c => write!(fmt, "not ({})", Display(c, self.1)), @@ -408,79 +472,9 @@ impl fmt::Display for Display<'_> { Cfg::Any(ref sub_cfgs) => { let separator = if sub_cfgs.iter().all(Cfg::is_simple) { " or " } else { ", or " }; - - let short_longhand = self.1.is_long() && { - let all_crate_features = sub_cfgs - .iter() - .all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::feature, Some(_)))); - let all_target_features = sub_cfgs - .iter() - .all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::target_feature, Some(_)))); - - if all_crate_features { - fmt.write_str("crate features ")?; - true - } else if all_target_features { - fmt.write_str("target features ")?; - true - } else { - false - } - }; - - for (i, sub_cfg) in sub_cfgs.iter().enumerate() { - if i != 0 { - fmt.write_str(separator)?; - } - if let (true, Cfg::Cfg(_, Some(feat))) = (short_longhand, sub_cfg) { - if self.1.is_html() { - write!(fmt, "{feat}")?; - } else { - write!(fmt, "`{feat}`")?; - } - } else { - write_with_opt_paren(fmt, !sub_cfg.is_all(), Display(sub_cfg, self.1))?; - } - } - Ok(()) - } - - Cfg::All(ref sub_cfgs) => { - let short_longhand = self.1.is_long() && { - let all_crate_features = sub_cfgs - .iter() - .all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::feature, Some(_)))); - let all_target_features = sub_cfgs - .iter() - .all(|sub_cfg| matches!(sub_cfg, Cfg::Cfg(sym::target_feature, Some(_)))); - - if all_crate_features { - fmt.write_str("crate features ")?; - true - } else if all_target_features { - fmt.write_str("target features ")?; - true - } else { - false - } - }; - - for (i, sub_cfg) in sub_cfgs.iter().enumerate() { - if i != 0 { - fmt.write_str(" and ")?; - } - if let (true, Cfg::Cfg(_, Some(feat))) = (short_longhand, sub_cfg) { - if self.1.is_html() { - write!(fmt, "{feat}")?; - } else { - write!(fmt, "`{feat}`")?; - } - } else { - write_with_opt_paren(fmt, !sub_cfg.is_simple(), Display(sub_cfg, self.1))?; - } - } - Ok(()) + self.display_sub_cfgs(fmt, sub_cfgs, separator) } + Cfg::All(ref sub_cfgs) => self.display_sub_cfgs(fmt, sub_cfgs, " and "), Cfg::True => fmt.write_str("everywhere"), Cfg::False => fmt.write_str("nowhere"), diff --git a/src/librustdoc/clean/cfg/tests.rs b/src/librustdoc/clean/cfg/tests.rs index 2c62e12c96dc1..26dc101e4b590 100644 --- a/src/librustdoc/clean/cfg/tests.rs +++ b/src/librustdoc/clean/cfg/tests.rs @@ -276,10 +276,13 @@ fn test_parse_ok() { let mi = dummy_meta_item_list!(not, [a]); assert_eq!(Cfg::parse(&mi), Ok(!word_cfg("a"))); - let mi = dummy_meta_item_list!(not, [dummy_meta_item_list!(any, [ - dummy_meta_item_word("a"), - dummy_meta_item_list!(all, [b, c]), - ]),]); + let mi = dummy_meta_item_list!( + not, + [dummy_meta_item_list!( + any, + [dummy_meta_item_word("a"), dummy_meta_item_list!(all, [b, c]),] + ),] + ); assert_eq!(Cfg::parse(&mi), Ok(!(word_cfg("a") | (word_cfg("b") & word_cfg("c"))))); let mi = dummy_meta_item_list!(all, [a, b, c]); @@ -302,18 +305,16 @@ fn test_parse_err() { let mi = dummy_meta_item_list!(foo, []); assert!(Cfg::parse(&mi).is_err()); - let mi = - dummy_meta_item_list!( - all, - [dummy_meta_item_list!(foo, []), dummy_meta_item_word("b"),] - ); + let mi = dummy_meta_item_list!( + all, + [dummy_meta_item_list!(foo, []), dummy_meta_item_word("b"),] + ); assert!(Cfg::parse(&mi).is_err()); - let mi = - dummy_meta_item_list!( - any, - [dummy_meta_item_word("a"), dummy_meta_item_list!(foo, []),] - ); + let mi = dummy_meta_item_list!( + any, + [dummy_meta_item_word("a"), dummy_meta_item_list!(foo, []),] + ); assert!(Cfg::parse(&mi).is_err()); let mi = dummy_meta_item_list!(not, [dummy_meta_item_list!(foo, []),]); diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 019a888bd2f62..3d51ab1967d44 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -17,12 +17,12 @@ use rustc_span::symbol::{Symbol, sym}; use thin_vec::{ThinVec, thin_vec}; use tracing::{debug, trace}; -use super::Item; +use super::{Item, extract_cfg_from_attrs}; use crate::clean::{ - self, Attributes, AttributesExt, ImplKind, ItemId, Type, clean_bound_vars, clean_generics, - clean_impl_item, clean_middle_assoc_item, clean_middle_field, clean_middle_ty, - clean_poly_fn_sig, clean_trait_ref_with_constraints, clean_ty, clean_ty_alias_inner_type, - clean_ty_generics, clean_variant_def, utils, + self, Attributes, ImplKind, ItemId, Type, clean_bound_vars, clean_generics, clean_impl_item, + clean_middle_assoc_item, clean_middle_field, clean_middle_ty, clean_poly_fn_sig, + clean_trait_ref_with_constraints, clean_ty, clean_ty_alias_inner_type, clean_ty_generics, + clean_variant_def, utils, }; use crate::core::DocContext; use crate::formats::item_type::ItemType; @@ -408,10 +408,13 @@ pub(crate) fn merge_attrs( } else { Attributes::from_hir(&both) }, - both.cfg(cx.tcx, &cx.cache.hidden_cfg), + extract_cfg_from_attrs(both.iter(), cx.tcx, &cx.cache.hidden_cfg), ) } else { - (Attributes::from_hir(old_attrs), old_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg)) + ( + Attributes::from_hir(old_attrs), + extract_cfg_from_attrs(old_attrs.iter(), cx.tcx, &cx.cache.hidden_cfg), + ) } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 0f84b43866290..1d62a93e723a9 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -186,8 +186,7 @@ fn generate_item_with_correct_attrs( // For glob re-exports the item may or may not exist to be re-exported (potentially the cfgs // on the path up until the glob can be removed, and only cfgs on the globbed item itself // matter), for non-inlined re-exports see #85043. - let is_inline = inline::load_attrs(cx, import_id.to_def_id()) - .lists(sym::doc) + let is_inline = hir_attr_lists(inline::load_attrs(cx, import_id.to_def_id()), sym::doc) .get_word_attr(sym::inline) .is_some() || (is_glob_import(cx.tcx, import_id) @@ -199,8 +198,14 @@ fn generate_item_with_correct_attrs( // We only keep the item's attributes. target_attrs.iter().map(|attr| (Cow::Borrowed(attr), None)).collect() }; - - let cfg = attrs.cfg(cx.tcx, &cx.cache.hidden_cfg); + let cfg = extract_cfg_from_attrs( + attrs.iter().map(move |(attr, _)| match attr { + Cow::Borrowed(attr) => *attr, + Cow::Owned(attr) => attr, + }), + cx.tcx, + &cx.cache.hidden_cfg, + ); let attrs = Attributes::from_hir_iter(attrs.iter().map(|(attr, did)| (&**attr, *did)), false); let name = renamed.or(Some(name)); @@ -511,8 +516,7 @@ fn projection_to_path_segment<'tcx>( ty.map_bound(|ty| &ty.args[generics.parent_count..]), false, def_id, - ) - .into(), + ), constraints: Default::default(), }, } @@ -540,14 +544,18 @@ fn clean_generic_param_def( } else { None }; - (def.name, GenericParamDefKind::Type { - bounds: ThinVec::new(), // These are filled in from the where-clauses. - default: default.map(Box::new), - synthetic, - }) + ( + def.name, + GenericParamDefKind::Type { + bounds: ThinVec::new(), // These are filled in from the where-clauses. + default: default.map(Box::new), + synthetic, + }, + ) } - ty::GenericParamDefKind::Const { has_default, synthetic } => { - (def.name, GenericParamDefKind::Const { + ty::GenericParamDefKind::Const { has_default, synthetic } => ( + def.name, + GenericParamDefKind::Const { ty: Box::new(clean_middle_ty( ty::Binder::dummy( cx.tcx @@ -569,8 +577,8 @@ fn clean_generic_param_def( None }, synthetic, - }) - } + }, + ), }; GenericParamDef { name, def_id: def.def_id, kind } @@ -615,21 +623,25 @@ fn clean_generic_param<'tcx>( } else { ThinVec::new() }; - (param.name.ident().name, GenericParamDefKind::Type { - bounds, - default: default.map(|t| clean_ty(t, cx)).map(Box::new), - synthetic, - }) + ( + param.name.ident().name, + GenericParamDefKind::Type { + bounds, + default: default.map(|t| clean_ty(t, cx)).map(Box::new), + synthetic, + }, + ) } - hir::GenericParamKind::Const { ty, default, synthetic } => { - (param.name.ident().name, GenericParamDefKind::Const { + hir::GenericParamKind::Const { ty, default, synthetic } => ( + param.name.ident().name, + GenericParamDefKind::Const { ty: Box::new(clean_ty(ty, cx)), default: default.map(|ct| { Box::new(lower_const_arg_for_rustdoc(cx.tcx, ct, FeedConstTy::No).to_string()) }), synthetic, - }) - } + }, + ), }; GenericParamDef { name, def_id: param.def_id.to_def_id(), kind } @@ -649,9 +661,10 @@ fn is_impl_trait(param: &hir::GenericParam<'_>) -> bool { /// /// See `lifetime_to_generic_param` in `rustc_ast_lowering` for more information. fn is_elided_lifetime(param: &hir::GenericParam<'_>) -> bool { - matches!(param.kind, hir::GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Elided(_) - }) + matches!( + param.kind, + hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Elided(_) } + ) } pub(crate) fn clean_generics<'tcx>( @@ -979,13 +992,14 @@ fn clean_proc_macro<'tcx>( ) -> ItemKind { let attrs = cx.tcx.hir().attrs(item.hir_id()); if kind == MacroKind::Derive - && let Some(derive_name) = attrs.lists(sym::proc_macro_derive).find_map(|mi| mi.ident()) + && let Some(derive_name) = + hir_attr_lists(attrs, sym::proc_macro_derive).find_map(|mi| mi.ident()) { *name = derive_name.name; } let mut helpers = Vec::new(); - for mi in attrs.lists(sym::proc_macro_derive) { + for mi in hir_attr_lists(attrs, sym::proc_macro_derive) { if !mi.has_name(sym::attributes) { continue; } @@ -1050,11 +1064,10 @@ fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[hir::Attrib .. } = param { - func.decl.inputs.values.insert(a.get() as _, Argument { - name, - type_: *ty, - is_const: true, - }); + func.decl + .inputs + .values + .insert(a.get() as _, Argument { name, type_: *ty, is_const: true }); } else { panic!("unexpected non const in position {pos}"); } @@ -1098,17 +1111,29 @@ fn clean_args_from_types_and_names<'tcx>( types: &[hir::Ty<'tcx>], names: &[Ident], ) -> Arguments { + fn nonempty_name(ident: &Ident) -> Option { + if ident.name == kw::Underscore || ident.name == kw::Empty { + None + } else { + Some(ident.name) + } + } + + // If at least one argument has a name, use `_` as the name of unnamed + // arguments. Otherwise omit argument names. + let default_name = if names.iter().any(|ident| nonempty_name(ident).is_some()) { + kw::Underscore + } else { + kw::Empty + }; + Arguments { values: types .iter() .enumerate() .map(|(i, ty)| Argument { type_: clean_ty(ty, cx), - name: names - .get(i) - .map(|ident| ident.name) - .filter(|ident| !ident.is_empty()) - .unwrap_or(kw::Underscore), + name: names.get(i).and_then(nonempty_name).unwrap_or(default_name), is_const: false, }) .collect(), @@ -1405,11 +1430,11 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo let bounds = tcx.explicit_item_bounds(assoc_item.def_id).iter_identity_copied(); predicates = tcx.arena.alloc_from_iter(bounds.chain(predicates.iter().copied())); } - let mut generics = - clean_ty_generics(cx, tcx.generics_of(assoc_item.def_id), ty::GenericPredicates { - parent: None, - predicates, - }); + let mut generics = clean_ty_generics( + cx, + tcx.generics_of(assoc_item.def_id), + ty::GenericPredicates { parent: None, predicates }, + ); simplify::move_bounds_to_generic_parameters(&mut generics); if let ty::AssocItemContainer::Trait = assoc_item.container { @@ -1741,9 +1766,9 @@ fn maybe_expand_private_type_alias<'tcx>( }; let hir::ItemKind::TyAlias(ty, generics) = alias else { return None }; - let provided_params = &path.segments.last().expect("segments were empty"); + let final_seg = &path.segments.last().expect("segments were empty"); let mut args = DefIdMap::default(); - let generic_args = provided_params.args(); + let generic_args = final_seg.args(); let mut indices: hir::GenericParamCount = Default::default(); for param in generics.params.iter() { @@ -1775,7 +1800,7 @@ fn maybe_expand_private_type_alias<'tcx>( let type_ = generic_args.args.iter().find_map(|arg| match arg { hir::GenericArg::Type(ty) => { if indices.types == j { - return Some(*ty); + return Some(ty.as_unambig_ty()); } j += 1; None @@ -1837,10 +1862,13 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T ImplTrait(ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect()) } TyKind::Path(_) => clean_qpath(ty, cx), - TyKind::TraitObject(bounds, lifetime, _) => { + TyKind::TraitObject(bounds, lifetime) => { let bounds = bounds.iter().map(|bound| clean_poly_trait_ref(bound, cx)).collect(); - let lifetime = - if !lifetime.is_elided() { Some(clean_lifetime(lifetime, cx)) } else { None }; + let lifetime = if !lifetime.is_elided() { + Some(clean_lifetime(lifetime.pointer(), cx)) + } else { + None + }; DynTrait(bounds, lifetime) } TyKind::BareFn(barefn) => BareFunction(Box::new(clean_bare_fn_ty(barefn, cx))), @@ -1848,7 +1876,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T UnsafeBinder(Box::new(clean_unsafe_binder_ty(unsafe_binder_ty, cx))) } // Rustdoc handles `TyKind::Err`s by turning them into `Type::Infer`s. - TyKind::Infer + TyKind::Infer(()) | TyKind::Err(_) | TyKind::Typeof(..) | TyKind::InferDelegation(..) @@ -1925,7 +1953,7 @@ fn can_elide_trait_object_lifetime_bound<'tcx>( preds: &'tcx ty::List>, tcx: TyCtxt<'tcx>, ) -> bool { - // Below we quote extracts from https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes + // Below we quote extracts from https://doc.rust-lang.org/stable/reference/lifetime-elision.html#default-trait-object-lifetimes // > If the trait object is used as a type argument of a generic type then the containing type is // > first used to try to infer a bound. @@ -2193,8 +2221,7 @@ pub(crate) fn clean_middle_ty<'tcx>( alias_ty.map_bound(|ty| ty.args.as_slice()), true, def_id, - ) - .into(), + ), constraints: Default::default(), }, }, @@ -2512,7 +2539,7 @@ fn clean_generic_args<'tcx>( ) -> GenericArgs { // FIXME(return_type_notation): Fix RTN parens rendering if let Some((inputs, output)) = generic_args.paren_sugar_inputs_output() { - let inputs = inputs.iter().map(|x| clean_ty(x, cx)).collect::>().into(); + let inputs = inputs.iter().map(|x| clean_ty(x, cx)).collect::>().into(); let output = match output.kind { hir::TyKind::Tup(&[]) => None, _ => Some(Box::new(clean_ty(output, cx))), @@ -2527,11 +2554,13 @@ fn clean_generic_args<'tcx>( GenericArg::Lifetime(clean_lifetime(lt, cx)) } hir::GenericArg::Lifetime(_) => GenericArg::Lifetime(Lifetime::elided()), - hir::GenericArg::Type(ty) => GenericArg::Type(clean_ty(ty, cx)), - hir::GenericArg::Const(ct) => GenericArg::Const(Box::new(clean_const(ct, cx))), + hir::GenericArg::Type(ty) => GenericArg::Type(clean_ty(ty.as_unambig_ty(), cx)), + hir::GenericArg::Const(ct) => { + GenericArg::Const(Box::new(clean_const(ct.as_unambig_ct(), cx))) + } hir::GenericArg::Infer(_inf) => GenericArg::Infer, }) - .collect::>() + .collect::>() .into(); let constraints = generic_args .constraints @@ -2810,7 +2839,7 @@ fn clean_maybe_renamed_item<'tcx>( }), ItemKind::Macro(_, macro_kind) => clean_proc_macro(item, &mut name, macro_kind, cx), // proc macros can have a name set by attributes - ItemKind::Fn(ref sig, generics, body_id) => { + ItemKind::Fn { ref sig, generics, body: body_id, .. } => { clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx) } ItemKind::Trait(_, _, generics, bounds, item_ids) => { @@ -2985,7 +3014,7 @@ fn clean_use_statement_inner<'tcx>( let visibility = cx.tcx.visibility(import.owner_id); let attrs = cx.tcx.hir().attrs(import.hir_id()); - let inline_attr = attrs.lists(sym::doc).get_word_attr(sym::inline); + let inline_attr = hir_attr_lists(attrs, sym::doc).get_word_attr(sym::inline); let pub_underscore = visibility.is_public() && name == kw::Underscore; let current_mod = cx.tcx.parent_module_from_def_id(import.owner_id.def_id); let import_def_id = import.owner_id.def_id; @@ -3094,7 +3123,7 @@ fn clean_maybe_renamed_foreign_item<'tcx>( let kind = match item.kind { hir::ForeignItemKind::Fn(sig, names, generics) => ForeignFunctionItem( clean_function(cx, &sig, generics, FunctionArgs::Names(names)), - sig.header.safety, + sig.header.safety(), ), hir::ForeignItemKind::Static(ty, mutability, safety) => ForeignStaticItem( Static { type_: Box::new(clean_ty(ty, cx)), mutability, expr: None }, diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 3c4fad4bca97e..5f7c30a33ab8b 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use std::hash::Hash; use std::path::PathBuf; use std::sync::{Arc, OnceLock as OnceCell}; @@ -400,7 +399,25 @@ impl Item { } pub(crate) fn deprecation(&self, tcx: TyCtxt<'_>) -> Option { - self.def_id().and_then(|did| tcx.lookup_deprecation(did)) + self.def_id().and_then(|did| tcx.lookup_deprecation(did)).or_else(|| { + // `allowed_through_unstable_modules` is a bug-compatibility hack for old rustc + // versions; the paths that are exposed through it are "deprecated" because they + // were never supposed to work at all. + let stab = self.stability(tcx)?; + if let rustc_attr_parsing::StabilityLevel::Stable { + allowed_through_unstable_modules: Some(note), + .. + } = stab.level + { + Some(Deprecation { + since: rustc_attr_parsing::DeprecatedSince::Unspecified, + note: Some(note), + suggestion: None, + }) + } else { + None + } + }) } pub(crate) fn inner_docs(&self, tcx: TyCtxt<'_>) -> bool { @@ -459,7 +476,7 @@ impl Item { name, kind, Attributes::from_hir(hir_attrs), - hir_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg), + extract_cfg_from_attrs(hir_attrs.iter(), cx.tcx, &cx.cache.hidden_cfg), ) } @@ -648,17 +665,28 @@ impl Item { ty::Asyncness::Yes => hir::IsAsync::Async(DUMMY_SP), ty::Asyncness::No => hir::IsAsync::NotAsync, }; - hir::FnHeader { safety: sig.safety(), abi: sig.abi(), constness, asyncness } + hir::FnHeader { + safety: if tcx.codegen_fn_attrs(def_id).safe_target_features { + hir::HeaderSafety::SafeTargetFeatures + } else { + sig.safety().into() + }, + abi: sig.abi(), + constness, + asyncness, + } } let header = match self.kind { ItemKind::ForeignFunctionItem(_, safety) => { let def_id = self.def_id().unwrap(); let abi = tcx.fn_sig(def_id).skip_binder().abi(); hir::FnHeader { - safety: if abi == ExternAbi::RustIntrinsic { - intrinsic_operation_unsafety(tcx, def_id.expect_local()) + safety: if tcx.codegen_fn_attrs(def_id).safe_target_features { + hir::HeaderSafety::SafeTargetFeatures + } else if abi == ExternAbi::RustIntrinsic { + intrinsic_operation_unsafety(tcx, def_id.expect_local()).into() } else { - safety + safety.into() }, abi, constness: if tcx.is_const_fn(def_id) { @@ -959,147 +987,107 @@ pub(crate) struct Module { pub(crate) span: Span, } -pub(crate) trait AttributesExt { - type AttributeIterator<'a>: Iterator - where - Self: 'a; - type Attributes<'a>: Iterator - where - Self: 'a; - - fn lists(&self, name: Symbol) -> Self::AttributeIterator<'_>; - - fn iter(&self) -> Self::Attributes<'_>; - - fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet) -> Option> { - let sess = tcx.sess; - let doc_cfg_active = tcx.features().doc_cfg(); - let doc_auto_cfg_active = tcx.features().doc_auto_cfg(); - - fn single(it: T) -> Option { - let mut iter = it.into_iter(); - let item = iter.next()?; - if iter.next().is_some() { - return None; - } - Some(item) +pub(crate) fn hir_attr_lists<'a, I: IntoIterator>( + attrs: I, + name: Symbol, +) -> impl Iterator + use<'a, I> { + attrs + .into_iter() + .filter(move |attr| attr.has_name(name)) + .filter_map(ast::attr::AttributeExt::meta_item_list) + .flatten() +} + +pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator + Clone>( + attrs: I, + tcx: TyCtxt<'_>, + hidden_cfg: &FxHashSet, +) -> Option> { + let sess = tcx.sess; + let doc_cfg_active = tcx.features().doc_cfg(); + let doc_auto_cfg_active = tcx.features().doc_auto_cfg(); + + fn single(it: T) -> Option { + let mut iter = it.into_iter(); + let item = iter.next()?; + if iter.next().is_some() { + return None; } + Some(item) + } - let mut cfg = if doc_cfg_active || doc_auto_cfg_active { - let mut doc_cfg = self - .iter() - .filter(|attr| attr.has_name(sym::doc)) - .flat_map(|attr| attr.meta_item_list().unwrap_or_default()) + let mut cfg = if doc_cfg_active || doc_auto_cfg_active { + let mut doc_cfg = attrs + .clone() + .filter(|attr| attr.has_name(sym::doc)) + .flat_map(|attr| attr.meta_item_list().unwrap_or_default()) + .filter(|attr| attr.has_name(sym::cfg)) + .peekable(); + if doc_cfg.peek().is_some() && doc_cfg_active { + doc_cfg + .filter_map(|attr| Cfg::parse(&attr).ok()) + .fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg) + } else if doc_auto_cfg_active { + // If there is no `doc(cfg())`, then we retrieve the `cfg()` attributes (because + // `doc(cfg())` overrides `cfg()`). + attrs + .clone() .filter(|attr| attr.has_name(sym::cfg)) - .peekable(); - if doc_cfg.peek().is_some() && doc_cfg_active { - doc_cfg - .filter_map(|attr| Cfg::parse(&attr).ok()) - .fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg) - } else if doc_auto_cfg_active { - // If there is no `doc(cfg())`, then we retrieve the `cfg()` attributes (because - // `doc(cfg())` overrides `cfg()`). - self.iter() - .filter(|attr| attr.has_name(sym::cfg)) - .filter_map(|attr| single(attr.meta_item_list()?)) - .filter_map(|attr| { - Cfg::parse_without(attr.meta_item()?, hidden_cfg).ok().flatten() - }) - .fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg) - } else { - Cfg::True - } + .filter_map(|attr| single(attr.meta_item_list()?)) + .filter_map(|attr| Cfg::parse_without(attr.meta_item()?, hidden_cfg).ok().flatten()) + .fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg) } else { Cfg::True - }; - - for attr in self.iter() { - // #[doc] - if attr.doc_str().is_none() && attr.has_name(sym::doc) { - // #[doc(...)] - if let Some(list) = attr.meta_item_list() { - for item in list { - // #[doc(hidden)] - if !item.has_name(sym::cfg) { - continue; - } - // #[doc(cfg(...))] - if let Some(cfg_mi) = item - .meta_item() - .and_then(|item| rustc_expand::config::parse_cfg(item, sess)) - { - match Cfg::parse(cfg_mi) { - Ok(new_cfg) => cfg &= new_cfg, - Err(e) => { - sess.dcx().span_err(e.span, e.msg); - } + } + } else { + Cfg::True + }; + + for attr in attrs.clone() { + // #[doc] + if attr.doc_str().is_none() && attr.has_name(sym::doc) { + // #[doc(...)] + if let Some(list) = attr.meta_item_list() { + for item in list { + // #[doc(hidden)] + if !item.has_name(sym::cfg) { + continue; + } + // #[doc(cfg(...))] + if let Some(cfg_mi) = item + .meta_item() + .and_then(|item| rustc_expand::config::parse_cfg(item, sess)) + { + match Cfg::parse(cfg_mi) { + Ok(new_cfg) => cfg &= new_cfg, + Err(e) => { + sess.dcx().span_err(e.span, e.msg); } } } } } } + } - // treat #[target_feature(enable = "feat")] attributes as if they were - // #[doc(cfg(target_feature = "feat"))] attributes as well - for attr in self.lists(sym::target_feature) { - if attr.has_name(sym::enable) { - if attr.value_str().is_some() { - // Clone `enable = "feat"`, change to `target_feature = "feat"`. - // Unwrap is safe because `value_str` succeeded above. - let mut meta = attr.meta_item().unwrap().clone(); - meta.path = ast::Path::from_ident(Ident::with_dummy_span(sym::target_feature)); - - if let Ok(feat_cfg) = Cfg::parse(&ast::MetaItemInner::MetaItem(meta)) { - cfg &= feat_cfg; - } + // treat #[target_feature(enable = "feat")] attributes as if they were + // #[doc(cfg(target_feature = "feat"))] attributes as well + for attr in hir_attr_lists(attrs, sym::target_feature) { + if attr.has_name(sym::enable) { + if attr.value_str().is_some() { + // Clone `enable = "feat"`, change to `target_feature = "feat"`. + // Unwrap is safe because `value_str` succeeded above. + let mut meta = attr.meta_item().unwrap().clone(); + meta.path = ast::Path::from_ident(Ident::with_dummy_span(sym::target_feature)); + + if let Ok(feat_cfg) = Cfg::parse(&ast::MetaItemInner::MetaItem(meta)) { + cfg &= feat_cfg; } } } - - if cfg == Cfg::True { None } else { Some(Arc::new(cfg)) } - } -} - -impl AttributesExt for [hir::Attribute] { - type AttributeIterator<'a> = impl Iterator + 'a; - type Attributes<'a> = impl Iterator + 'a; - - fn lists(&self, name: Symbol) -> Self::AttributeIterator<'_> { - self.iter() - .filter(move |attr| attr.has_name(name)) - .filter_map(ast::attr::AttributeExt::meta_item_list) - .flatten() - } - - fn iter(&self) -> Self::Attributes<'_> { - self.iter() - } -} - -impl AttributesExt for [(Cow<'_, hir::Attribute>, Option)] { - type AttributeIterator<'a> - = impl Iterator + 'a - where - Self: 'a; - type Attributes<'a> - = impl Iterator + 'a - where - Self: 'a; - - fn lists(&self, name: Symbol) -> Self::AttributeIterator<'_> { - AttributesExt::iter(self) - .filter(move |attr| attr.has_name(name)) - .filter_map(hir::Attribute::meta_item_list) - .flatten() } - fn iter(&self) -> Self::Attributes<'_> { - self.iter().map(move |(attr, _)| match attr { - Cow::Borrowed(attr) => *attr, - Cow::Owned(attr) => attr, - }) - } + if cfg == Cfg::True { None } else { Some(Arc::new(cfg)) } } pub(crate) trait NestedAttributesExt { @@ -1165,7 +1153,7 @@ pub(crate) struct Attributes { impl Attributes { pub(crate) fn lists(&self, name: Symbol) -> impl Iterator + '_ { - self.other_attrs.lists(name) + hir_attr_lists(&self.other_attrs[..], name) } pub(crate) fn has_doc_flag(&self, flag: Symbol) -> bool { @@ -1232,7 +1220,9 @@ impl Attributes { pub(crate) fn get_doc_aliases(&self) -> Box<[Symbol]> { let mut aliases = FxIndexSet::default(); - for attr in self.other_attrs.lists(sym::doc).filter(|a| a.has_name(sym::alias)) { + for attr in + hir_attr_lists(&self.other_attrs[..], sym::doc).filter(|a| a.has_name(sym::alias)) + { if let Some(values) = attr.meta_item_list() { for l in values { if let Some(lit) = l.lit() @@ -1263,10 +1253,13 @@ impl GenericBound { } pub(crate) fn maybe_sized(cx: &mut DocContext<'_>) -> GenericBound { - Self::sized_with(cx, hir::TraitBoundModifiers { - polarity: hir::BoundPolarity::Maybe(DUMMY_SP), - constness: hir::BoundConstness::Never, - }) + Self::sized_with( + cx, + hir::TraitBoundModifiers { + polarity: hir::BoundPolarity::Maybe(DUMMY_SP), + constness: hir::BoundConstness::Never, + }, + ) } fn sized_with(cx: &mut DocContext<'_>, modifiers: hir::TraitBoundModifiers) -> GenericBound { @@ -2256,8 +2249,8 @@ impl GenericArg { #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) enum GenericArgs { - AngleBracketed { args: Box<[GenericArg]>, constraints: ThinVec }, - Parenthesized { inputs: Box<[Type]>, output: Option> }, + AngleBracketed { args: ThinVec, constraints: ThinVec }, + Parenthesized { inputs: ThinVec, output: Option> }, } impl GenericArgs { @@ -2281,7 +2274,7 @@ impl GenericArgs { assoc: PathSegment { name: sym::Output, args: GenericArgs::AngleBracketed { - args: Vec::new().into_boxed_slice(), + args: ThinVec::new(), constraints: ThinVec::new(), }, }, @@ -2598,12 +2591,12 @@ mod size_asserts { static_assert_size!(Crate, 56); // frequently moved by-value static_assert_size!(DocFragment, 32); static_assert_size!(GenericArg, 32); - static_assert_size!(GenericArgs, 32); + static_assert_size!(GenericArgs, 24); static_assert_size!(GenericParamDef, 40); static_assert_size!(Generics, 16); static_assert_size!(Item, 48); static_assert_size!(ItemKind, 48); - static_assert_size!(PathSegment, 40); + static_assert_size!(PathSegment, 32); static_assert_size!(Type, 32); // tidy-alphabetical-end } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 8aeebdde7bb45..8a7d140bb1a69 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -81,11 +81,11 @@ pub(crate) fn clean_middle_generic_args<'tcx>( args: ty::Binder<'tcx, &'tcx [ty::GenericArg<'tcx>]>, mut has_self: bool, owner: DefId, -) -> Vec { +) -> ThinVec { let (args, bound_vars) = (args.skip_binder(), args.bound_vars()); if args.is_empty() { // Fast path which avoids executing the query `generics_of`. - return Vec::new(); + return ThinVec::new(); } // If the container is a trait object type, the arguments won't contain the self type but the @@ -144,7 +144,7 @@ pub(crate) fn clean_middle_generic_args<'tcx>( }; let offset = if has_self { 1 } else { 0 }; - let mut clean_args = Vec::with_capacity(args.len().saturating_sub(offset)); + let mut clean_args = ThinVec::with_capacity(args.len().saturating_sub(offset)); clean_args.extend(args.iter().enumerate().rev().filter_map(clean_arg)); clean_args.reverse(); clean_args @@ -303,7 +303,8 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { return kw::Underscore; } PatKind::Binding(_, _, ident, _) => return ident.name, - PatKind::TupleStruct(ref p, ..) | PatKind::Path(ref p) => qpath_to_string(p), + PatKind::TupleStruct(ref p, ..) + | PatKind::Expr(PatExpr { kind: PatExprKind::Path(ref p), .. }) => qpath_to_string(p), PatKind::Or(pats) => { pats.iter().map(|p| name_from_pat(p).to_string()).collect::>().join(" | ") } @@ -314,12 +315,13 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { PatKind::Box(p) => return name_from_pat(p), PatKind::Deref(p) => format!("deref!({})", name_from_pat(p)), PatKind::Ref(p, _) => return name_from_pat(p), - PatKind::Lit(..) => { + PatKind::Expr(..) => { warn!( - "tried to get argument name from PatKind::Lit, which is silly in function arguments" + "tried to get argument name from PatKind::Expr, which is silly in function arguments" ); return Symbol::intern("()"); } + PatKind::Guard(p, _) => return name_from_pat(p), PatKind::Range(..) => return kw::Underscore, PatKind::Slice(begin, ref mid, end) => { let begin = begin.iter().map(|p| name_from_pat(p).to_string()); @@ -342,10 +344,8 @@ pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String { s } // array lengths are obviously usize - ty::ConstKind::Value(ty, ty::ValTree::Leaf(scalar)) - if *ty.kind() == ty::Uint(ty::UintTy::Usize) => - { - scalar.to_string() + ty::ConstKind::Value(cv) if *cv.ty.kind() == ty::Uint(ty::UintTy::Usize) => { + cv.valtree.unwrap_leaf().to_string() } _ => n.to_string(), } @@ -588,9 +588,9 @@ pub(crate) fn attrs_have_doc_flag<'a>( /// so that the channel is consistent. /// /// Set by `bootstrap::Builder::doc_rust_lang_org_channel` in order to keep tests passing on beta/stable. -pub(crate) const DOC_RUST_LANG_ORG_CHANNEL: &str = env!("DOC_RUST_LANG_ORG_CHANNEL"); -pub(crate) static DOC_CHANNEL: Lazy<&'static str> = - Lazy::new(|| DOC_RUST_LANG_ORG_CHANNEL.rsplit('/').find(|c| !c.is_empty()).unwrap()); +pub(crate) const DOC_RUST_LANG_ORG_VERSION: &str = env!("DOC_RUST_LANG_ORG_CHANNEL"); +pub(crate) static RUSTDOC_VERSION: Lazy<&'static str> = + Lazy::new(|| DOC_RUST_LANG_ORG_VERSION.rsplit('/').find(|c| !c.is_empty()).unwrap()); /// Render a sequence of macro arms in a format suitable for displaying to the user /// as part of an item declaration. diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index af3c7cc7be345..fd4d9845c8920 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -9,8 +9,8 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::DiagCtxtHandle; use rustc_session::config::{ self, CodegenOptions, CrateType, ErrorOutputType, Externs, Input, JsonUnusedExterns, - UnstableOptions, get_cmd_lint_options, nightly_options, parse_crate_types_from_list, - parse_externs, parse_target_triple, + OptionsTargetModifiers, UnstableOptions, get_cmd_lint_options, nightly_options, + parse_crate_types_from_list, parse_externs, parse_target_triple, }; use rustc_session::lint::Level; use rustc_session::search_paths::SearchPath; @@ -33,6 +33,7 @@ pub(crate) enum OutputFormat { Json, #[default] Html, + Doctest, } impl OutputFormat { @@ -48,6 +49,7 @@ impl TryFrom<&str> for OutputFormat { match value { "json" => Ok(OutputFormat::Json), "html" => Ok(OutputFormat::Html), + "doctest" => Ok(OutputFormat::Doctest), _ => Err(format!("unknown output format `{value}`")), } } @@ -303,6 +305,8 @@ pub(crate) struct RenderOptions { pub(crate) include_parts_dir: Vec, /// Where to write crate-info pub(crate) parts_out_dir: Option, + /// disable minification of CSS/JS + pub(crate) disable_minification: bool, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -385,8 +389,9 @@ impl Options { config::parse_error_format(early_dcx, matches, color, json_color, json_rendered); let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_default(); - let codegen_options = CodegenOptions::build(early_dcx, matches); - let unstable_opts = UnstableOptions::build(early_dcx, matches); + let mut target_modifiers = BTreeMap::::new(); + let codegen_options = CodegenOptions::build(early_dcx, matches, &mut target_modifiers); + let unstable_opts = UnstableOptions::build(early_dcx, matches, &mut target_modifiers); let remap_path_prefix = match parse_remap_path_prefix(matches) { Ok(prefix_mappings) => prefix_mappings, @@ -443,20 +448,48 @@ impl Options { } } + let show_coverage = matches.opt_present("show-coverage"); + let output_format_s = matches.opt_str("output-format"); + let output_format = match output_format_s { + Some(ref s) => match OutputFormat::try_from(s.as_str()) { + Ok(out_fmt) => out_fmt, + Err(e) => dcx.fatal(e), + }, + None => OutputFormat::default(), + }; + // check for `--output-format=json` - if !matches!(matches.opt_str("output-format").as_deref(), None | Some("html")) - && !matches.opt_present("show-coverage") - && !nightly_options::is_unstable_enabled(matches) - { - dcx.fatal( - "the -Z unstable-options flag must be passed to enable --output-format for documentation generation (see https://github.com/rust-lang/rust/issues/76578)", - ); + match ( + output_format_s.as_ref().map(|_| output_format), + show_coverage, + nightly_options::is_unstable_enabled(matches), + ) { + (None | Some(OutputFormat::Json), true, _) => {} + (_, true, _) => { + dcx.fatal(format!( + "`--output-format={}` is not supported for the `--show-coverage` option", + output_format_s.unwrap_or_default(), + )); + } + // If `-Zunstable-options` is used, nothing to check after this point. + (_, false, true) => {} + (None | Some(OutputFormat::Html), false, _) => {} + (Some(OutputFormat::Json), false, false) => { + dcx.fatal( + "the -Z unstable-options flag must be passed to enable --output-format for documentation generation (see https://github.com/rust-lang/rust/issues/76578)", + ); + } + (Some(OutputFormat::Doctest), false, false) => { + dcx.fatal( + "the -Z unstable-options flag must be passed to enable --output-format for documentation generation (see https://github.com/rust-lang/rust/issues/134529)", + ); + } } let to_check = matches.opt_strs("check-theme"); if !to_check.is_empty() { let mut content = - std::str::from_utf8(static_files::STATIC_FILES.rustdoc_css.bytes).unwrap(); + std::str::from_utf8(static_files::STATIC_FILES.rustdoc_css.src_bytes).unwrap(); if let Some((_, inside)) = content.split_once("/* Begin theme: light */") { content = inside; } @@ -605,7 +638,7 @@ impl Options { let mut themes = Vec::new(); if matches.opt_present("theme") { let mut content = - std::str::from_utf8(static_files::STATIC_FILES.rustdoc_css.bytes).unwrap(); + std::str::from_utf8(static_files::STATIC_FILES.rustdoc_css.src_bytes).unwrap(); if let Some((_, inside)) = content.split_once("/* Begin theme: light */") { content = inside; } @@ -702,8 +735,6 @@ impl Options { }) .collect(); - let show_coverage = matches.opt_present("show-coverage"); - let crate_types = match parse_crate_types_from_list(matches.opt_strs("crate-type")) { Ok(types) => types, Err(e) => { @@ -711,20 +742,6 @@ impl Options { } }; - let output_format = match matches.opt_str("output-format") { - Some(s) => match OutputFormat::try_from(s.as_str()) { - Ok(out_fmt) => { - if !out_fmt.is_json() && show_coverage { - dcx.fatal( - "html output format isn't supported for the --show-coverage option", - ); - } - out_fmt - } - Err(e) => dcx.fatal(e), - }, - None => OutputFormat::default(), - }; let crate_name = matches.opt_str("crate-name"); let bin_crate = crate_types.contains(&CrateType::Executable); let proc_macro_crate = crate_types.contains(&CrateType::ProcMacro); @@ -781,6 +798,9 @@ impl Options { let unstable_features = rustc_feature::UnstableFeatures::from_environment(crate_name.as_deref()); + + let disable_minification = matches.opt_present("disable-minification"); + let options = Options { bin_crate, proc_macro_crate, @@ -857,6 +877,7 @@ impl Options { should_merge, include_parts_dir, parts_out_dir, + disable_minification, }; Some((input, options, render_options)) } diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 0dda3466a7173..5c146da03acc7 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -1,10 +1,9 @@ -use std::sync::atomic::AtomicBool; use std::sync::{Arc, LazyLock}; use std::{io, mem}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; -use rustc_data_structures::sync::Lrc; use rustc_data_structures::unord::UnordSet; +use rustc_driver::USING_INTERNAL_FEATURES; use rustc_errors::TerminalUrl; use rustc_errors::codes::*; use rustc_errors::emitter::{ @@ -145,7 +144,7 @@ impl<'tcx> DocContext<'tcx> { /// will be created for the `DiagCtxt`. pub(crate) fn new_dcx( error_format: ErrorOutputType, - source_map: Option>, + source_map: Option>, diagnostic_width: Option, unstable_opts: &UnstableOptions, ) -> rustc_errors::DiagCtxt { @@ -173,12 +172,12 @@ pub(crate) fn new_dcx( } ErrorOutputType::Json { pretty, json_rendered, color_config } => { let source_map = source_map.unwrap_or_else(|| { - Lrc::new(source_map::SourceMap::new(source_map::FilePathMapping::empty())) + Arc::new(source_map::SourceMap::new(source_map::FilePathMapping::empty())) }); Box::new( JsonEmitter::new( Box::new(io::BufWriter::new(io::stderr())), - source_map, + Some(source_map), fallback_bundle, pretty, json_rendered, @@ -221,7 +220,6 @@ pub(crate) fn create_config( .. }: RustdocOptions, RenderOptions { document_private, .. }: &RenderOptions, - using_internal_features: Arc, ) -> rustc_interface::Config { // Add the doc cfg into the doc build. cfgs.push("doc".to_string()); @@ -316,7 +314,7 @@ pub(crate) fn create_config( make_codegen_backend: None, registry: rustc_driver::diagnostics_registry(), ice_file: None, - using_internal_features, + using_internal_features: &USING_INTERNAL_FEATURES, expanded_args, } } @@ -337,14 +335,14 @@ pub(crate) fn run_global_ctxt( // NOTE: These are copy/pasted from typeck/lib.rs and should be kept in sync with those changes. let _ = tcx.sess.time("wf_checking", || { - tcx.hir().try_par_for_each_module(|module| tcx.ensure().check_mod_type_wf(module)) + tcx.hir().try_par_for_each_module(|module| tcx.ensure_ok().check_mod_type_wf(module)) }); tcx.dcx().abort_if_errors(); tcx.sess.time("missing_docs", || rustc_lint::check_crate(tcx)); tcx.sess.time("check_mod_attrs", || { - tcx.hir().for_each_module(|module| tcx.ensure().check_mod_attrs(module)) + tcx.hir().for_each_module(|module| tcx.ensure_ok().check_mod_attrs(module)) }); rustc_passes::stability::check_unused_or_stable_features(tcx); @@ -388,7 +386,7 @@ pub(crate) fn run_global_ctxt( let help = format!( "The following guide may be of use:\n\ {}/rustdoc/how-to-write-documentation.html", - crate::DOC_RUST_LANG_ORG_CHANNEL + crate::DOC_RUST_LANG_ORG_VERSION ); tcx.node_lint( crate::lint::MISSING_CRATE_LEVEL_DOCS, diff --git a/src/librustdoc/display.rs b/src/librustdoc/display.rs new file mode 100644 index 0000000000000..ee8dde013ee93 --- /dev/null +++ b/src/librustdoc/display.rs @@ -0,0 +1,47 @@ +//! Various utilities for working with [`fmt::Display`] implementations. + +use std::fmt::{self, Display, Formatter}; + +pub(crate) trait Joined: IntoIterator { + /// Takes an iterator over elements that implement [`Display`], and format them into `f`, separated by `sep`. + /// + /// This is similar to [`Itertools::format`](itertools::Itertools::format), but instead of returning an implementation of `Display`, + /// it formats directly into a [`Formatter`]. + /// + /// The performance of `joined` is slightly better than `format`, since it doesn't need to use a `Cell` to keep track of whether [`fmt`](Display::fmt) + /// was already called (`joined`'s API doesn't allow it be called more than once). + fn joined(self, sep: &str, f: &mut Formatter<'_>) -> fmt::Result; +} + +impl Joined for I +where + I: IntoIterator, + T: Display, +{ + fn joined(self, sep: &str, f: &mut Formatter<'_>) -> fmt::Result { + let mut iter = self.into_iter(); + let Some(first) = iter.next() else { return Ok(()) }; + first.fmt(f)?; + while let Some(item) = iter.next() { + f.write_str(sep)?; + item.fmt(f)?; + } + Ok(()) + } +} + +pub(crate) trait MaybeDisplay { + /// For a given `Option`, returns a `Display` implementation that will display `t` if `Some(t)`, or nothing if `None`. + fn maybe_display(self) -> impl Display; +} + +impl MaybeDisplay for Option { + fn maybe_display(self) -> impl Display { + fmt::from_fn(move |f| { + if let Some(t) = self.as_ref() { + t.fmt(f)?; + } + Ok(()) + }) + } +} diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 009e9662933ae..211f4d524e463 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -1,3 +1,4 @@ +mod extracted; mod make; mod markdown; mod runner; @@ -30,7 +31,7 @@ use tempfile::{Builder as TempFileBuilder, TempDir}; use tracing::debug; use self::rust::HirCollector; -use crate::config::Options as RustdocOptions; +use crate::config::{Options as RustdocOptions, OutputFormat}; use crate::html::markdown::{ErrorCodes, Ignore, LangString, MdRelLine}; use crate::lint::init_lints; @@ -95,7 +96,7 @@ pub(crate) fn generate_args_file(file_path: &Path, options: &RustdocOptions) -> .map_err(|error| format!("failed to create args file: {error:?}"))?; // We now put the common arguments into the file we created. - let mut content = vec!["--crate-type=bin".to_string()]; + let mut content = vec![]; for cfg in &options.cfgs { content.push(format!("--cfg={cfg}")); @@ -193,7 +194,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions make_codegen_backend: None, registry: rustc_driver::diagnostics_registry(), ice_file: None, - using_internal_features: Arc::default(), + using_internal_features: &rustc_driver::USING_INTERNAL_FEATURES, expanded_args: options.expanded_args.clone(), }; @@ -209,15 +210,8 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions let args_path = temp_dir.path().join("rustdoc-cfgs"); crate::wrap_return(dcx, generate_args_file(&args_path, &options)); - let CreateRunnableDocTests { - standalone_tests, - mergeable_tests, - rustdoc_options, - opts, - unused_extern_reports, - compiling_test_count, - .. - } = interface::run_compiler(config, |compiler| { + let extract_doctests = options.output_format == OutputFormat::Doctest; + let result = interface::run_compiler(config, |compiler| { let krate = rustc_interface::passes::parse(&compiler.sess); let collector = rustc_interface::create_and_enter_global_ctxt(compiler, krate, |tcx| { @@ -226,22 +220,53 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions let opts = scrape_test_config(crate_name, crate_attrs, args_path); let enable_per_target_ignores = options.enable_per_target_ignores; - let mut collector = CreateRunnableDocTests::new(options, opts); let hir_collector = HirCollector::new( ErrorCodes::from(compiler.sess.opts.unstable_features.is_nightly_build()), enable_per_target_ignores, tcx, ); let tests = hir_collector.collect_crate(); - tests.into_iter().for_each(|t| collector.add_test(t)); + if extract_doctests { + let mut collector = extracted::ExtractedDocTests::new(); + tests.into_iter().for_each(|t| collector.add_test(t, &opts, &options)); + + let stdout = std::io::stdout(); + let mut stdout = stdout.lock(); + if let Err(error) = serde_json::ser::to_writer(&mut stdout, &collector) { + eprintln!(); + Err(format!("Failed to generate JSON output for doctests: {error:?}")) + } else { + Ok(None) + } + } else { + let mut collector = CreateRunnableDocTests::new(options, opts); + tests.into_iter().for_each(|t| collector.add_test(t)); - collector + Ok(Some(collector)) + } }); compiler.sess.dcx().abort_if_errors(); collector }); + let CreateRunnableDocTests { + standalone_tests, + mergeable_tests, + rustdoc_options, + opts, + unused_extern_reports, + compiling_test_count, + .. + } = match result { + Ok(Some(collector)) => collector, + Ok(None) => return, + Err(error) => { + eprintln!("{error}"); + std::process::exit(1); + } + }; + run_tests(opts, &rustdoc_options, &unused_extern_reports, standalone_tests, mergeable_tests); let compiling_test_count = compiling_test_count.load(Ordering::SeqCst); @@ -488,12 +513,18 @@ pub(crate) struct RunnableDocTest { line: usize, edition: Edition, no_run: bool, - is_multiple_tests: bool, + merged_test_code: Option, } impl RunnableDocTest { - fn path_for_merged_doctest(&self) -> PathBuf { - self.test_opts.outdir.path().join(format!("doctest_{}.rs", self.edition)) + fn path_for_merged_doctest_bundle(&self) -> PathBuf { + self.test_opts.outdir.path().join(format!("doctest_bundle_{}.rs", self.edition)) + } + fn path_for_merged_doctest_runner(&self) -> PathBuf { + self.test_opts.outdir.path().join(format!("doctest_runner_{}.rs", self.edition)) + } + fn is_multiple_tests(&self) -> bool { + self.merged_test_code.is_some() } } @@ -512,91 +543,108 @@ fn run_test( let rust_out = add_exe_suffix("rust_out".to_owned(), &rustdoc_options.target); let output_file = doctest.test_opts.outdir.path().join(rust_out); - let rustc_binary = rustdoc_options - .test_builder - .as_deref() - .unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc")); - let mut compiler = wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary); + // Common arguments used for compiling the doctest runner. + // On merged doctests, the compiler is invoked twice: once for the test code itself, + // and once for the runner wrapper (which needs to use `#![feature]` on stable). + let mut compiler_args = vec![]; - compiler.arg(format!("@{}", doctest.global_opts.args_file.display())); + compiler_args.push(format!("@{}", doctest.global_opts.args_file.display())); if let Some(sysroot) = &rustdoc_options.maybe_sysroot { - compiler.arg(format!("--sysroot={}", sysroot.display())); + compiler_args.push(format!("--sysroot={}", sysroot.display())); } - compiler.arg("--edition").arg(doctest.edition.to_string()); - if !doctest.is_multiple_tests { - // Setting these environment variables is unneeded if this is a merged doctest. - compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", &doctest.test_opts.path); - compiler.env( - "UNSTABLE_RUSTDOC_TEST_LINE", - format!("{}", doctest.line as isize - doctest.full_test_line_offset as isize), - ); - } - compiler.arg("-o").arg(&output_file); + compiler_args.extend_from_slice(&["--edition".to_owned(), doctest.edition.to_string()]); if langstr.test_harness { - compiler.arg("--test"); + compiler_args.push("--test".to_owned()); } if rustdoc_options.json_unused_externs.is_enabled() && !langstr.compile_fail { - compiler.arg("--error-format=json"); - compiler.arg("--json").arg("unused-externs"); - compiler.arg("-W").arg("unused_crate_dependencies"); - compiler.arg("-Z").arg("unstable-options"); + compiler_args.push("--error-format=json".to_owned()); + compiler_args.extend_from_slice(&["--json".to_owned(), "unused-externs".to_owned()]); + compiler_args.extend_from_slice(&["-W".to_owned(), "unused_crate_dependencies".to_owned()]); + compiler_args.extend_from_slice(&["-Z".to_owned(), "unstable-options".to_owned()]); } if doctest.no_run && !langstr.compile_fail && rustdoc_options.persist_doctests.is_none() { // FIXME: why does this code check if it *shouldn't* persist doctests // -- shouldn't it be the negation? - compiler.arg("--emit=metadata"); + compiler_args.push("--emit=metadata".to_owned()); } - compiler.arg("--target").arg(match &rustdoc_options.target { - TargetTuple::TargetTuple(s) => s, - TargetTuple::TargetJson { path_for_rustdoc, .. } => { - path_for_rustdoc.to_str().expect("target path must be valid unicode") - } - }); + compiler_args.extend_from_slice(&[ + "--target".to_owned(), + match &rustdoc_options.target { + TargetTuple::TargetTuple(s) => s.clone(), + TargetTuple::TargetJson { path_for_rustdoc, .. } => { + path_for_rustdoc.to_str().expect("target path must be valid unicode").to_owned() + } + }, + ]); if let ErrorOutputType::HumanReadable(kind, color_config) = rustdoc_options.error_format { let short = kind.short(); let unicode = kind == HumanReadableErrorType::Unicode; if short { - compiler.arg("--error-format").arg("short"); + compiler_args.extend_from_slice(&["--error-format".to_owned(), "short".to_owned()]); } if unicode { - compiler.arg("--error-format").arg("human-unicode"); + compiler_args + .extend_from_slice(&["--error-format".to_owned(), "human-unicode".to_owned()]); } match color_config { ColorConfig::Never => { - compiler.arg("--color").arg("never"); + compiler_args.extend_from_slice(&["--color".to_owned(), "never".to_owned()]); } ColorConfig::Always => { - compiler.arg("--color").arg("always"); + compiler_args.extend_from_slice(&["--color".to_owned(), "always".to_owned()]); } ColorConfig::Auto => { - compiler.arg("--color").arg(if supports_color { "always" } else { "never" }); + compiler_args.extend_from_slice(&[ + "--color".to_owned(), + if supports_color { "always" } else { "never" }.to_owned(), + ]); } } } + let rustc_binary = rustdoc_options + .test_builder + .as_deref() + .unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc")); + let mut compiler = wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary); + + compiler.args(&compiler_args); + // If this is a merged doctest, we need to write it into a file instead of using stdin // because if the size of the merged doctests is too big, it'll simply break stdin. - if doctest.is_multiple_tests { + if doctest.is_multiple_tests() { // It makes the compilation failure much faster if it is for a combined doctest. compiler.arg("--error-format=short"); - let input_file = doctest.path_for_merged_doctest(); + let input_file = doctest.path_for_merged_doctest_bundle(); if std::fs::write(&input_file, &doctest.full_test_code).is_err() { // If we cannot write this file for any reason, we leave. All combined tests will be // tested as standalone tests. return Err(TestFailure::CompileError); } - compiler.arg(input_file); if !rustdoc_options.nocapture { // If `nocapture` is disabled, then we don't display rustc's output when compiling // the merged doctests. compiler.stderr(Stdio::null()); } + // bundled tests are an rlib, loaded by a separate runner executable + compiler + .arg("--crate-type=lib") + .arg("--out-dir") + .arg(doctest.test_opts.outdir.path()) + .arg(input_file); } else { + compiler.arg("--crate-type=bin").arg("-o").arg(&output_file); + // Setting these environment variables is unneeded if this is a merged doctest. + compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", &doctest.test_opts.path); + compiler.env( + "UNSTABLE_RUSTDOC_TEST_LINE", + format!("{}", doctest.line as isize - doctest.full_test_line_offset as isize), + ); compiler.arg("-"); compiler.stdin(Stdio::piped()); compiler.stderr(Stdio::piped()); @@ -605,8 +653,65 @@ fn run_test( debug!("compiler invocation for doctest: {compiler:?}"); let mut child = compiler.spawn().expect("Failed to spawn rustc process"); - let output = if doctest.is_multiple_tests { + let output = if let Some(merged_test_code) = &doctest.merged_test_code { + // compile-fail tests never get merged, so this should always pass let status = child.wait().expect("Failed to wait"); + + // the actual test runner is a separate component, built with nightly-only features; + // build it now + let runner_input_file = doctest.path_for_merged_doctest_runner(); + + let mut runner_compiler = + wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary); + // the test runner does not contain any user-written code, so this doesn't allow + // the user to exploit nightly-only features on stable + runner_compiler.env("RUSTC_BOOTSTRAP", "1"); + runner_compiler.args(compiler_args); + runner_compiler.args(&["--crate-type=bin", "-o"]).arg(&output_file); + let mut extern_path = std::ffi::OsString::from(format!( + "--extern=doctest_bundle_{edition}=", + edition = doctest.edition + )); + for extern_str in &rustdoc_options.extern_strs { + if let Some((_cratename, path)) = extern_str.split_once('=') { + // Direct dependencies of the tests themselves are + // indirect dependencies of the test runner. + // They need to be in the library search path. + let dir = Path::new(path) + .parent() + .filter(|x| x.components().count() > 0) + .unwrap_or(Path::new(".")); + runner_compiler.arg("-L").arg(dir); + } + } + let output_bundle_file = doctest + .test_opts + .outdir + .path() + .join(format!("libdoctest_bundle_{edition}.rlib", edition = doctest.edition)); + extern_path.push(&output_bundle_file); + runner_compiler.arg(extern_path); + runner_compiler.arg(&runner_input_file); + if std::fs::write(&runner_input_file, &merged_test_code).is_err() { + // If we cannot write this file for any reason, we leave. All combined tests will be + // tested as standalone tests. + return Err(TestFailure::CompileError); + } + if !rustdoc_options.nocapture { + // If `nocapture` is disabled, then we don't display rustc's output when compiling + // the merged doctests. + runner_compiler.stderr(Stdio::null()); + } + runner_compiler.arg("--error-format=short"); + debug!("compiler invocation for doctest runner: {runner_compiler:?}"); + + let status = if !status.success() { + status + } else { + let mut child_runner = runner_compiler.spawn().expect("Failed to spawn rustc process"); + child_runner.wait().expect("Failed to wait") + }; + process::Output { status, stdout: Vec::new(), stderr: Vec::new() } } else { let stdin = child.stdin.as_mut().expect("Failed to open stdin"); @@ -683,7 +788,7 @@ fn run_test( cmd.arg(&output_file); } else { cmd = Command::new(&output_file); - if doctest.is_multiple_tests { + if doctest.is_multiple_tests() { cmd.env("RUSTDOC_DOCTEST_BIN_PATH", &output_file); } } @@ -691,7 +796,7 @@ fn run_test( cmd.current_dir(run_directory); } - let result = if doctest.is_multiple_tests || rustdoc_options.nocapture { + let result = if doctest.is_multiple_tests() || rustdoc_options.nocapture { cmd.status().map(|status| process::Output { status, stdout: Vec::new(), @@ -761,6 +866,7 @@ impl IndividualTestOptions { /// [`clean`]: crate::clean /// [`run_merged_tests`]: crate::doctest::runner::DocTestRunner::run_merged_tests /// [`generate_unique_doctest`]: crate::doctest::make::DocTestBuilder::generate_unique_doctest +#[derive(Debug)] pub(crate) struct ScrapedDocTest { filename: FileName, line: usize, @@ -977,7 +1083,7 @@ fn doctest_run_fn( line: scraped_test.line, edition: scraped_test.edition(&rustdoc_options), no_run: scraped_test.no_run(&rustdoc_options), - is_multiple_tests: false, + merged_test_code: None, }; let res = run_test(runnable_test, &rustdoc_options, doctest.supports_color, report_unused_externs); diff --git a/src/librustdoc/doctest/extracted.rs b/src/librustdoc/doctest/extracted.rs new file mode 100644 index 0000000000000..03c8814a4c960 --- /dev/null +++ b/src/librustdoc/doctest/extracted.rs @@ -0,0 +1,141 @@ +//! Rustdoc's doctest extraction. +//! +//! This module contains the logic to extract doctests and output a JSON containing this +//! information. + +use serde::Serialize; + +use super::{DocTestBuilder, ScrapedDocTest}; +use crate::config::Options as RustdocOptions; +use crate::html::markdown; + +/// The version of JSON output that this code generates. +/// +/// This integer is incremented with every breaking change to the API, +/// and is returned along with the JSON blob into the `format_version` root field. +/// Consuming code should assert that this value matches the format version(s) that it supports. +const FORMAT_VERSION: u32 = 1; + +#[derive(Serialize)] +pub(crate) struct ExtractedDocTests { + format_version: u32, + doctests: Vec, +} + +impl ExtractedDocTests { + pub(crate) fn new() -> Self { + Self { format_version: FORMAT_VERSION, doctests: Vec::new() } + } + + pub(crate) fn add_test( + &mut self, + scraped_test: ScrapedDocTest, + opts: &super::GlobalTestOptions, + options: &RustdocOptions, + ) { + let edition = scraped_test.edition(&options); + + let ScrapedDocTest { filename, line, langstr, text, name } = scraped_test; + + let doctest = DocTestBuilder::new( + &text, + Some(&opts.crate_name), + edition, + false, + None, + Some(&langstr), + ); + let (full_test_code, size) = doctest.generate_unique_doctest( + &text, + langstr.test_harness, + &opts, + Some(&opts.crate_name), + ); + self.doctests.push(ExtractedDocTest { + file: filename.prefer_remapped_unconditionaly().to_string(), + line, + doctest_attributes: langstr.into(), + doctest_code: if size != 0 { Some(full_test_code) } else { None }, + original_code: text, + name, + }); + } +} + +#[derive(Serialize)] +pub(crate) struct ExtractedDocTest { + file: String, + line: usize, + doctest_attributes: LangString, + original_code: String, + /// `None` if the code syntax is invalid. + doctest_code: Option, + name: String, +} + +#[derive(Serialize)] +pub(crate) enum Ignore { + All, + None, + Some(Vec), +} + +impl From for Ignore { + fn from(original: markdown::Ignore) -> Self { + match original { + markdown::Ignore::All => Self::All, + markdown::Ignore::None => Self::None, + markdown::Ignore::Some(values) => Self::Some(values), + } + } +} + +#[derive(Serialize)] +struct LangString { + pub(crate) original: String, + pub(crate) should_panic: bool, + pub(crate) no_run: bool, + pub(crate) ignore: Ignore, + pub(crate) rust: bool, + pub(crate) test_harness: bool, + pub(crate) compile_fail: bool, + pub(crate) standalone_crate: bool, + pub(crate) error_codes: Vec, + pub(crate) edition: Option, + pub(crate) added_css_classes: Vec, + pub(crate) unknown: Vec, +} + +impl From for LangString { + fn from(original: markdown::LangString) -> Self { + let markdown::LangString { + original, + should_panic, + no_run, + ignore, + rust, + test_harness, + compile_fail, + standalone_crate, + error_codes, + edition, + added_classes, + unknown, + } = original; + + Self { + original, + should_panic, + no_run, + ignore: ignore.into(), + rust, + test_harness, + compile_fail, + standalone_crate, + error_codes, + edition: edition.map(|edition| edition.to_string()), + added_css_classes: added_classes, + unknown, + } + } +} diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index 7bcb9465948d7..d89caabefe3e0 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -2,9 +2,9 @@ //! runnable, e.g. by adding a `main` function if it doesn't already exist. use std::io; +use std::sync::Arc; use rustc_ast as ast; -use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::stderr_destination; use rustc_errors::{ColorConfig, FatalError}; use rustc_parse::new_parser_from_source_str; @@ -280,7 +280,7 @@ fn parse_source( // Any errors in parsing should also appear when the doctest is compiled for real, so just // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let fallback_bundle = rustc_errors::fallback_fluent_bundle( rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false, @@ -474,7 +474,7 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> Option let filename = FileName::anon_source_code(source); // Any errors in parsing should also appear when the doctest is compiled for real, so just // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let fallback_bundle = rustc_errors::fallback_fluent_bundle( rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false, diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index 234f40c6c1ab2..58efa35711a0e 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -14,6 +14,7 @@ pub(crate) struct DocTestRunner { crate_attrs: FxIndexSet, ids: String, output: String, + output_merged_tests: String, supports_color: bool, nb_tests: usize, } @@ -24,6 +25,7 @@ impl DocTestRunner { crate_attrs: FxIndexSet::default(), ids: String::new(), output: String::new(), + output_merged_tests: String::new(), supports_color: true, nb_tests: 0, } @@ -55,7 +57,8 @@ impl DocTestRunner { scraped_test, ignore, self.nb_tests, - &mut self.output + &mut self.output, + &mut self.output_merged_tests, ), )); self.supports_color &= doctest.supports_color; @@ -78,9 +81,11 @@ impl DocTestRunner { " .to_string(); + let mut code_prefix = String::new(); + for crate_attr in &self.crate_attrs { - code.push_str(crate_attr); - code.push('\n'); + code_prefix.push_str(crate_attr); + code_prefix.push('\n'); } if opts.attrs.is_empty() { @@ -88,15 +93,16 @@ impl DocTestRunner { // lints that are commonly triggered in doctests. The crate-level test attributes are // commonly used to make tests fail in case they trigger warnings, so having this there in // that case may cause some tests to pass when they shouldn't have. - code.push_str("#![allow(unused)]\n"); + code_prefix.push_str("#![allow(unused)]\n"); } // Next, any attributes that came from the crate root via #![doc(test(attr(...)))]. for attr in &opts.attrs { - code.push_str(&format!("#![{attr}]\n")); + code_prefix.push_str(&format!("#![{attr}]\n")); } code.push_str("extern crate test;\n"); + writeln!(code, "extern crate doctest_bundle_{edition} as doctest_bundle;").unwrap(); let test_args = test_args.iter().fold(String::new(), |mut x, arg| { write!(x, "{arg:?}.to_string(),").unwrap(); @@ -161,12 +167,12 @@ the same process\"); std::process::Termination::report(test::test_main(test_args, Vec::from(TESTS), None)) }}", nb_tests = self.nb_tests, - output = self.output, + output = self.output_merged_tests, ids = self.ids, ) .expect("failed to generate test code"); let runnable_test = RunnableDocTest { - full_test_code: code, + full_test_code: format!("{code_prefix}{code}", code = self.output), full_test_line_offset: 0, test_opts: test_options, global_opts: opts.clone(), @@ -174,7 +180,7 @@ std::process::Termination::report(test::test_main(test_args, Vec::from(TESTS), N line: 0, edition, no_run: false, - is_multiple_tests: true, + merged_test_code: Some(code), }; let ret = run_test(runnable_test, rustdoc_options, self.supports_color, |_: UnusedExterns| {}); @@ -189,14 +195,15 @@ fn generate_mergeable_doctest( ignore: bool, id: usize, output: &mut String, + output_merged_tests: &mut String, ) -> String { let test_id = format!("__doctest_{id}"); if ignore { // We generate nothing else. - writeln!(output, "mod {test_id} {{\n").unwrap(); + writeln!(output, "pub mod {test_id} {{}}\n").unwrap(); } else { - writeln!(output, "mod {test_id} {{\n{}{}", doctest.crates, doctest.maybe_crate_attrs) + writeln!(output, "pub mod {test_id} {{\n{}{}", doctest.crates, doctest.maybe_crate_attrs) .unwrap(); if doctest.has_main_fn { output.push_str(&doctest.everything_else); @@ -216,11 +223,17 @@ fn main() {returns_result} {{ ) .unwrap(); } + writeln!( + output, + "\npub fn __main_fn() -> impl std::process::Termination {{ main() }} \n}}\n" + ) + .unwrap(); } let not_running = ignore || scraped_test.langstr.no_run; writeln!( - output, + output_merged_tests, " +mod {test_id} {{ pub const TEST: test::TestDescAndFn = test::TestDescAndFn::new_doctest( {test_name:?}, {ignore}, {file:?}, {line}, {no_run}, {should_panic}, test::StaticTestFn( @@ -242,7 +255,7 @@ test::StaticTestFn( if let Some(bin_path) = crate::__doctest_mod::doctest_path() {{ test::assert_test_result(crate::__doctest_mod::doctest_runner(bin_path, {id})) }} else {{ - test::assert_test_result(self::main()) + test::assert_test_result(doctest_bundle::{test_id}::__main_fn()) }} ", ) diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs index 0903baddabebe..c4956bfd2b4af 100644 --- a/src/librustdoc/doctest/rust.rs +++ b/src/librustdoc/doctest/rust.rs @@ -1,9 +1,9 @@ //! Doctest functionality used only for doctests in `.rs` source files. use std::env; +use std::sync::Arc; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::sync::Lrc; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; use rustc_hir::{self as hir, CRATE_HIR_ID, intravisit}; use rustc_middle::hir::nested_filter; @@ -13,12 +13,11 @@ use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, DUMMY_SP, FileName, Pos, Span}; use super::{DocTestVisitor, ScrapedDocTest}; -use crate::clean::Attributes; -use crate::clean::types::AttributesExt; +use crate::clean::{Attributes, extract_cfg_from_attrs}; use crate::html::markdown::{self, ErrorCodes, LangString, MdRelLine}; struct RustCollector { - source_map: Lrc, + source_map: Arc, tests: Vec, cur_path: Vec, position: Span, @@ -97,7 +96,9 @@ impl HirCollector<'_> { nested: F, ) { let ast_attrs = self.tcx.hir().attrs(self.tcx.local_def_id_to_hir_id(def_id)); - if let Some(ref cfg) = ast_attrs.cfg(self.tcx, &FxHashSet::default()) { + if let Some(ref cfg) = + extract_cfg_from_attrs(ast_attrs.iter(), self.tcx, &FxHashSet::default()) + { if !cfg.matches(&self.tcx.sess.psess, Some(self.tcx.features())) { return; } diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index e64baca974dab..4760e579199aa 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -1,5 +1,6 @@ use std::mem; +use rustc_attr_parsing::StabilityLevel; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet}; use rustc_middle::ty::{self, TyCtxt}; @@ -139,6 +140,7 @@ struct CacheBuilder<'a, 'tcx> { /// This field is used to prevent duplicated impl blocks. impl_ids: DefIdMap, tcx: TyCtxt<'tcx>, + is_json_output: bool, } impl Cache { @@ -183,8 +185,13 @@ impl Cache { } let (krate, mut impl_ids) = { - let mut cache_builder = - CacheBuilder { tcx, cache: &mut cx.cache, impl_ids: Default::default() }; + let is_json_output = cx.is_json_output(); + let mut cache_builder = CacheBuilder { + tcx, + cache: &mut cx.cache, + impl_ids: Default::default(), + is_json_output, + }; krate = cache_builder.fold_crate(krate); (krate, cache_builder.impl_ids) }; @@ -306,7 +313,13 @@ impl DocFolder for CacheBuilder<'_, '_> { | clean::ProcMacroItem(..) | clean::VariantItem(..) => { use rustc_data_structures::fx::IndexEntry as Entry; - if !self.cache.stripped_mod { + + let skip_because_unstable = matches!( + item.stability.map(|stab| stab.level), + Some(StabilityLevel::Stable { allowed_through_unstable_modules: Some(_), .. }) + ); + + if (!self.cache.stripped_mod && !skip_because_unstable) || self.is_json_output { // Re-exported items mean that the same id can show up twice // in the rustdoc ast that we're looking at. We know, // however, that a re-exported item doesn't show up in the diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 621abd535010f..91b4b3ba1ebac 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -8,12 +8,11 @@ //! not be used external to this module. use std::borrow::Cow; -use std::cell::Cell; use std::cmp::Ordering; use std::fmt::{self, Display, Write}; use std::iter::{self, once}; -use itertools::Itertools; +use itertools::Either; use rustc_abi::ExternAbi; use rustc_attr_parsing::{ConstStability, StabilityLevel, StableSince}; use rustc_data_structures::captures::Captures; @@ -31,150 +30,29 @@ use super::url_parts_builder::{UrlPartsBuilder, estimate_item_path_byte_length}; use crate::clean::types::ExternalLocation; use crate::clean::utils::find_nearest_parent_module; use crate::clean::{self, ExternalCrate, PrimitiveType}; +use crate::display::Joined as _; use crate::formats::cache::Cache; use crate::formats::item_type::ItemType; use crate::html::escape::{Escape, EscapeBodyText}; use crate::html::render::Context; use crate::passes::collect_intra_doc_links::UrlFragment; -pub(crate) trait Print { - fn print(self, buffer: &mut Buffer); -} - -impl Print for F -where - F: FnOnce(&mut Buffer), -{ - fn print(self, buffer: &mut Buffer) { - (self)(buffer) - } -} - -impl Print for String { - fn print(self, buffer: &mut Buffer) { - buffer.write_str(&self); - } -} - -impl Print for &'_ str { - fn print(self, buffer: &mut Buffer) { - buffer.write_str(self); - } -} - -#[derive(Debug, Clone)] -pub(crate) struct Buffer { - for_html: bool, - buffer: String, -} - -impl core::fmt::Write for Buffer { - #[inline] - fn write_str(&mut self, s: &str) -> fmt::Result { - self.buffer.write_str(s) - } - - #[inline] - fn write_char(&mut self, c: char) -> fmt::Result { - self.buffer.write_char(c) - } - - #[inline] - fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result { - self.buffer.write_fmt(args) - } -} - -impl Buffer { - pub(crate) fn empty_from(v: &Buffer) -> Buffer { - Buffer { for_html: v.for_html, buffer: String::new() } - } - - pub(crate) fn html() -> Buffer { - Buffer { for_html: true, buffer: String::new() } - } - - pub(crate) fn new() -> Buffer { - Buffer { for_html: false, buffer: String::new() } - } - - pub(crate) fn is_empty(&self) -> bool { - self.buffer.is_empty() - } - - pub(crate) fn into_inner(self) -> String { - self.buffer - } - - pub(crate) fn push(&mut self, c: char) { - self.buffer.push(c); - } - - pub(crate) fn push_str(&mut self, s: &str) { - self.buffer.push_str(s); - } - - pub(crate) fn push_buffer(&mut self, other: Buffer) { - self.buffer.push_str(&other.buffer); - } - - // Intended for consumption by write! and writeln! (std::fmt) but without - // the fmt::Result return type imposed by fmt::Write (and avoiding the trait - // import). - pub(crate) fn write_str(&mut self, s: &str) { - self.buffer.push_str(s); - } - - // Intended for consumption by write! and writeln! (std::fmt) but without - // the fmt::Result return type imposed by fmt::Write (and avoiding the trait - // import). - pub(crate) fn write_fmt(&mut self, v: fmt::Arguments<'_>) { - self.buffer.write_fmt(v).unwrap(); - } - - pub(crate) fn to_display(mut self, t: T) -> String { - t.print(&mut self); - self.into_inner() - } - - pub(crate) fn reserve(&mut self, additional: usize) { - self.buffer.reserve(additional) - } - - pub(crate) fn len(&self) -> usize { - self.buffer.len() - } -} - -pub(crate) fn comma_sep( - items: impl Iterator, - space_after_comma: bool, -) -> impl Display { - display_fn(move |f| { - for (i, item) in items.enumerate() { - if i != 0 { - write!(f, ",{}", if space_after_comma { " " } else { "" })?; - } - item.fmt(f)?; - } - Ok(()) - }) +pub(crate) fn write_str(s: &mut String, f: fmt::Arguments<'_>) { + s.write_fmt(f).unwrap(); } pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>( bounds: &'a [clean::GenericBound], cx: &'a Context<'tcx>, ) -> impl Display + 'a + Captures<'tcx> { - display_fn(move |f| { + fmt::from_fn(move |f| { let mut bounds_dup = FxHashSet::default(); - for (i, bound) in bounds.iter().filter(|b| bounds_dup.insert(*b)).enumerate() { - if i > 0 { - f.write_str(" + ")?; - } - bound.print(cx).fmt(f)?; - } - Ok(()) + bounds + .iter() + .filter(move |b| bounds_dup.insert(*b)) + .map(|bound| bound.print(cx)) + .joined(" + ", f) }) } @@ -183,18 +61,13 @@ impl clean::GenericParamDef { &'a self, cx: &'a Context<'tcx>, ) -> impl Display + 'a + Captures<'tcx> { - display_fn(move |f| match &self.kind { + fmt::from_fn(move |f| match &self.kind { clean::GenericParamDefKind::Lifetime { outlives } => { write!(f, "{}", self.name)?; if !outlives.is_empty() { f.write_str(": ")?; - for (i, lt) in outlives.iter().enumerate() { - if i != 0 { - f.write_str(" + ")?; - } - write!(f, "{}", lt.print())?; - } + outlives.iter().map(|lt| lt.print()).joined(" + ", f)?; } Ok(()) @@ -238,16 +111,18 @@ impl clean::Generics { &'a self, cx: &'a Context<'tcx>, ) -> impl Display + 'a + Captures<'tcx> { - display_fn(move |f| { + fmt::from_fn(move |f| { let mut real_params = self.params.iter().filter(|p| !p.is_synthetic_param()).peekable(); if real_params.peek().is_none() { return Ok(()); } + let real_params = + fmt::from_fn(|f| real_params.clone().map(|g| g.print(cx)).joined(", ", f)); if f.alternate() { - write!(f, "<{:#}>", comma_sep(real_params.map(|g| g.print(cx)), true)) + write!(f, "<{:#}>", real_params) } else { - write!(f, "<{}>", comma_sep(real_params.map(|g| g.print(cx)), true)) + write!(f, "<{}>", real_params) } }) } @@ -259,6 +134,42 @@ pub(crate) enum Ending { NoNewline, } +fn print_where_predicate<'a, 'tcx: 'a>( + predicate: &'a clean::WherePredicate, + cx: &'a Context<'tcx>, +) -> impl Display + 'a + Captures<'tcx> { + fmt::from_fn(move |f| { + match predicate { + clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => { + print_higher_ranked_params_with_space(bound_params, cx, "for").fmt(f)?; + ty.print(cx).fmt(f)?; + f.write_str(":")?; + if !bounds.is_empty() { + f.write_str(" ")?; + print_generic_bounds(bounds, cx).fmt(f)?; + } + Ok(()) + } + clean::WherePredicate::RegionPredicate { lifetime, bounds } => { + // We don't need to check `alternate` since we can be certain that neither + // the lifetime nor the bounds contain any characters which need escaping. + write!(f, "{}:", lifetime.print())?; + if !bounds.is_empty() { + write!(f, " {}", print_generic_bounds(bounds, cx))?; + } + Ok(()) + } + clean::WherePredicate::EqPredicate { lhs, rhs } => { + if f.alternate() { + write!(f, "{:#} == {:#}", lhs.print(cx), rhs.print(cx)) + } else { + write!(f, "{} == {}", lhs.print(cx), rhs.print(cx)) + } + } + } + }) +} + /// * The Generics from which to emit a where-clause. /// * The number of spaces to indent each line with. /// * Whether the where-clause needs to add a comma and newline after the last bound. @@ -268,56 +179,27 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>( indent: usize, ending: Ending, ) -> impl Display + 'a + Captures<'tcx> { - display_fn(move |f| { - let mut where_predicates = gens - .where_predicates - .iter() - .map(|pred| { - display_fn(move |f| { - if f.alternate() { - f.write_str(" ")?; - } else { - f.write_str("\n")?; - } + fmt::from_fn(move |f| { + if gens.where_predicates.is_empty() { + return Ok(()); + } - match pred { - clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => { - print_higher_ranked_params_with_space(bound_params, cx, "for") - .fmt(f)?; - ty.print(cx).fmt(f)?; - f.write_str(":")?; - if !bounds.is_empty() { - f.write_str(" ")?; - print_generic_bounds(bounds, cx).fmt(f)?; - } - Ok(()) - } - clean::WherePredicate::RegionPredicate { lifetime, bounds } => { - // We don't need to check `alternate` since we can be certain that neither - // the lifetime nor the bounds contain any characters which need escaping. - write!(f, "{}:", lifetime.print())?; - if !bounds.is_empty() { - write!(f, " {}", print_generic_bounds(bounds, cx))?; - } - Ok(()) - } - clean::WherePredicate::EqPredicate { lhs, rhs } => { - if f.alternate() { - write!(f, "{:#} == {:#}", lhs.print(cx), rhs.print(cx)) - } else { - write!(f, "{} == {}", lhs.print(cx), rhs.print(cx)) - } + let where_preds = fmt::from_fn(|f| { + gens.where_predicates + .iter() + .map(|predicate| { + fmt::from_fn(|f| { + if f.alternate() { + f.write_str(" ")?; + } else { + f.write_str("\n")?; } - } + print_where_predicate(predicate, cx).fmt(f) + }) }) - }) - .peekable(); - - if where_predicates.peek().is_none() { - return Ok(()); - } + .joined(",", f) + }); - let where_preds = comma_sep(where_predicates, false); let clause = if f.alternate() { if ending == Ending::Newline { format!(" where{where_preds},") @@ -376,17 +258,15 @@ impl clean::Lifetime { impl clean::ConstantKind { pub(crate) fn print(&self, tcx: TyCtxt<'_>) -> impl Display + '_ { let expr = self.expr(tcx); - display_fn( - move |f| { - if f.alternate() { f.write_str(&expr) } else { write!(f, "{}", Escape(&expr)) } - }, - ) + fmt::from_fn(move |f| { + if f.alternate() { f.write_str(&expr) } else { write!(f, "{}", Escape(&expr)) } + }) } } impl clean::PolyTrait { fn print<'a, 'tcx: 'a>(&'a self, cx: &'a Context<'tcx>) -> impl Display + 'a + Captures<'tcx> { - display_fn(move |f| { + fmt::from_fn(move |f| { print_higher_ranked_params_with_space(&self.generic_params, cx, "for").fmt(f)?; self.trait_.print(cx).fmt(f) }) @@ -398,7 +278,7 @@ impl clean::GenericBound { &'a self, cx: &'a Context<'tcx>, ) -> impl Display + 'a + Captures<'tcx> { - display_fn(move |f| match self { + fmt::from_fn(move |f| match self { clean::GenericBound::Outlives(lt) => write!(f, "{}", lt.print()), clean::GenericBound::TraitBound(ty, modifiers) => { // `const` and `~const` trait bounds are experimental; don't render them. @@ -416,12 +296,7 @@ impl clean::GenericBound { } else { f.write_str("use<")?; } - for (i, arg) in args.iter().enumerate() { - if i > 0 { - write!(f, ", ")?; - } - arg.fmt(f)?; - } + args.iter().joined(", ", f)?; if f.alternate() { f.write_str(">") } else { f.write_str(">") } } }) @@ -430,7 +305,7 @@ impl clean::GenericBound { impl clean::GenericArgs { fn print<'a, 'tcx: 'a>(&'a self, cx: &'a Context<'tcx>) -> impl Display + 'a + Captures<'tcx> { - display_fn(move |f| { + fmt::from_fn(move |f| { match self { clean::GenericArgs::AngleBracketed { args, constraints } => { if !args.is_empty() || !constraints.is_empty() { @@ -439,29 +314,18 @@ impl clean::GenericArgs { } else { f.write_str("<")?; } - let mut comma = false; - for arg in args.iter() { - if comma { - f.write_str(", ")?; - } - comma = true; - if f.alternate() { - write!(f, "{:#}", arg.print(cx))?; - } else { - write!(f, "{}", arg.print(cx))?; - } - } - for constraint in constraints.iter() { - if comma { - f.write_str(", ")?; - } - comma = true; - if f.alternate() { - write!(f, "{:#}", constraint.print(cx))?; - } else { - write!(f, "{}", constraint.print(cx))?; - } - } + + [Either::Left(args), Either::Right(constraints)] + .into_iter() + .flat_map(Either::factor_into_iter) + .map(|either| { + either.map_either( + |arg| arg.print(cx), + |constraint| constraint.print(cx), + ) + }) + .joined(", ", f)?; + if f.alternate() { f.write_str(">")?; } else { @@ -471,14 +335,7 @@ impl clean::GenericArgs { } clean::GenericArgs::Parenthesized { inputs, output } => { f.write_str("(")?; - let mut comma = false; - for ty in inputs.iter() { - if comma { - f.write_str(", ")?; - } - comma = true; - ty.print(cx).fmt(f)?; - } + inputs.iter().map(|ty| ty.print(cx)).joined(", ", f)?; f.write_str(")")?; if let Some(ref ty) = *output { if f.alternate() { @@ -525,6 +382,7 @@ pub(crate) enum HrefError { // Panics if `syms` is empty. pub(crate) fn join_with_double_colon(syms: &[Symbol]) -> String { let mut s = String::with_capacity(estimate_item_path_byte_length(syms.len())); + // NOTE: using `Joined::joined` here causes a noticeable perf regression s.push_str(syms[0].as_str()); for sym in &syms[1..] { s.push_str("::"); @@ -573,20 +431,20 @@ fn generate_macro_def_id_path( } if let Some(last) = path.last_mut() { - *last = Symbol::intern(&format!("macro.{}.html", last.as_str())); + *last = Symbol::intern(&format!("macro.{last}.html")); } let url = match cache.extern_locations[&def_id.krate] { ExternalLocation::Remote(ref s) => { // `ExternalLocation::Remote` always end with a `/`. - format!("{s}{path}", path = path.iter().map(|p| p.as_str()).join("/")) + format!("{s}{path}", path = fmt::from_fn(|f| path.iter().joined("/", f))) } ExternalLocation::Local => { // `root_path` always end with a `/`. format!( "{root_path}{path}", root_path = root_path.unwrap_or(""), - path = path.iter().map(|p| p.as_str()).join("/") + path = fmt::from_fn(|f| path.iter().joined("/", f)) ) } ExternalLocation::Unknown => { @@ -683,9 +541,8 @@ fn make_href( url_parts.push("index.html"); } _ => { - let prefix = shortty.as_str(); let last = fqp.last().unwrap(); - url_parts.push_fmt(format_args!("{prefix}.{last}.html")); + url_parts.push_fmt(format_args!("{shortty}.{last}.html")); } } Ok((url_parts.finish(), shortty, fqp.to_vec())) @@ -810,7 +667,7 @@ pub(crate) fn link_tooltip(did: DefId, fragment: &Option, cx: &Cont else { return String::new(); }; - let mut buf = Buffer::new(); + let mut buf = String::new(); let fqp = if *shortty == ItemType::Primitive { // primitives are documented in a crate, but not actually part of it &fqp[fqp.len() - 1..] @@ -818,19 +675,19 @@ pub(crate) fn link_tooltip(did: DefId, fragment: &Option, cx: &Cont fqp }; if let &Some(UrlFragment::Item(id)) = fragment { - write!(buf, "{} ", cx.tcx().def_descr(id)); + write_str(&mut buf, format_args!("{} ", cx.tcx().def_descr(id))); for component in fqp { - write!(buf, "{component}::"); + write_str(&mut buf, format_args!("{component}::")); } - write!(buf, "{}", cx.tcx().item_name(id)); + write_str(&mut buf, format_args!("{}", cx.tcx().item_name(id))); } else if !fqp.is_empty() { let mut fqp_it = fqp.iter(); - write!(buf, "{shortty} {}", fqp_it.next().unwrap()); + write_str(&mut buf, format_args!("{shortty} {}", fqp_it.next().unwrap())); for component in fqp_it { - write!(buf, "::{component}"); + write_str(&mut buf, format_args!("::{component}")); } } - buf.into_inner() + buf } /// Used to render a [`clean::Path`]. @@ -852,19 +709,22 @@ fn resolved_path( if w.alternate() { write!(w, "{}{:#}", last.name, last.args.print(cx))?; } else { - let path = if use_absolute { - if let Ok((_, _, fqp)) = href(did, cx) { - format!( - "{path}::{anchor}", - path = join_with_double_colon(&fqp[..fqp.len() - 1]), - anchor = anchor(did, *fqp.last().unwrap(), cx) - ) + let path = fmt::from_fn(|f| { + if use_absolute { + if let Ok((_, _, fqp)) = href(did, cx) { + write!( + f, + "{path}::{anchor}", + path = join_with_double_colon(&fqp[..fqp.len() - 1]), + anchor = anchor(did, *fqp.last().unwrap(), cx) + ) + } else { + write!(f, "{}", last.name) + } } else { - last.name.to_string() + write!(f, "{}", anchor(did, last.name, cx)) } - } else { - anchor(did, last.name, cx).to_string() - }; + }); write!(w, "{path}{args}", args = last.args.print(cx))?; } Ok(()) @@ -892,16 +752,20 @@ fn primitive_link_fragment( match m.primitive_locations.get(&prim) { Some(&def_id) if def_id.is_local() => { let len = cx.current.len(); - let path = if len == 0 { - let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx()); - format!("{cname_sym}/") - } else { - "../".repeat(len - 1) - }; + let path = fmt::from_fn(|f| { + if len == 0 { + let cname_sym = ExternalCrate { crate_num: def_id.krate }.name(cx.tcx()); + write!(f, "{cname_sym}/")?; + } else { + for _ in 0..(len - 1) { + f.write_str("../")?; + } + } + Ok(()) + }); write!( f, - "", - path, + "", prim.as_sym() )?; needs_termination = true; @@ -950,13 +814,8 @@ fn tybounds<'a, 'tcx: 'a>( lt: &'a Option, cx: &'a Context<'tcx>, ) -> impl Display + 'a + Captures<'tcx> { - display_fn(move |f| { - for (i, bound) in bounds.iter().enumerate() { - if i > 0 { - write!(f, " + ")?; - } - bound.print(cx).fmt(f)?; - } + fmt::from_fn(move |f| { + bounds.iter().map(|bound| bound.print(cx)).joined(" + ", f)?; if let Some(lt) = lt { // We don't need to check `alternate` since we can be certain that // the lifetime doesn't contain any characters which need escaping. @@ -971,24 +830,24 @@ fn print_higher_ranked_params_with_space<'a, 'tcx: 'a>( cx: &'a Context<'tcx>, keyword: &'static str, ) -> impl Display + 'a + Captures<'tcx> { - display_fn(move |f| { + fmt::from_fn(move |f| { if !params.is_empty() { f.write_str(keyword)?; f.write_str(if f.alternate() { "<" } else { "<" })?; - comma_sep(params.iter().map(|lt| lt.print(cx)), true).fmt(f)?; + params.iter().map(|lt| lt.print(cx)).joined(", ", f)?; f.write_str(if f.alternate() { "> " } else { "> " })?; } Ok(()) }) } -pub(crate) fn anchor<'a, 'cx: 'a>( +pub(crate) fn anchor<'a: 'cx, 'cx>( did: DefId, text: Symbol, - cx: &'cx Context<'_>, -) -> impl Display + 'a { - let parts = href(did, cx); - display_fn(move |f| { + cx: &'cx Context<'a>, +) -> impl Display + Captures<'a> + 'cx { + fmt::from_fn(move |f| { + let parts = href(did, cx); if let Ok((url, short_ty, fqp)) = parts { write!( f, @@ -1026,9 +885,7 @@ fn fmt_type( clean::Primitive(clean::PrimitiveType::Never) => { primitive_link(f, PrimitiveType::Never, format_args!("!"), cx) } - clean::Primitive(prim) => { - primitive_link(f, prim, format_args!("{}", prim.as_sym().as_str()), cx) - } + clean::Primitive(prim) => primitive_link(f, prim, format_args!("{}", prim.as_sym()), cx), clean::BareFunction(ref decl) => { print_higher_ranked_params_with_space(&decl.generic_params, cx, "for").fmt(f)?; decl.safety.print_with_space().fmt(f)?; @@ -1068,18 +925,16 @@ fn fmt_type( primitive_link( f, PrimitiveType::Tuple, - format_args!("({})", generic_names.iter().map(|s| s.as_str()).join(", ")), + format_args!( + "({})", + fmt::from_fn(|f| generic_names.iter().joined(", ", f)) + ), cx, ) } else { - write!(f, "(")?; - for (i, item) in many.iter().enumerate() { - if i != 0 { - write!(f, ", ")?; - } - item.print(cx).fmt(f)?; - } - write!(f, ")") + f.write_str("(")?; + many.iter().map(|item| item.print(cx)).joined(", ", f)?; + f.write_str(")") } } }, @@ -1150,7 +1005,7 @@ fn fmt_type( } } clean::BorrowedRef { lifetime: ref l, mutability, type_: ref ty } => { - let lt = display_fn(|f| match l { + let lt = fmt::from_fn(|f| match l { Some(l) => write!(f, "{} ", l.print()), _ => Ok(()), }); @@ -1270,7 +1125,7 @@ impl clean::Type { &'a self, cx: &'a Context<'tcx>, ) -> impl Display + 'b + Captures<'tcx> { - display_fn(move |f| fmt_type(self, f, false, cx)) + fmt::from_fn(move |f| fmt_type(self, f, false, cx)) } } @@ -1279,7 +1134,7 @@ impl clean::Path { &'a self, cx: &'a Context<'tcx>, ) -> impl Display + 'b + Captures<'tcx> { - display_fn(move |f| resolved_path(f, self.def_id(), self, false, false, cx)) + fmt::from_fn(move |f| resolved_path(f, self.def_id(), self, false, false, cx)) } } @@ -1289,7 +1144,7 @@ impl clean::Impl { use_absolute: bool, cx: &'a Context<'tcx>, ) -> impl Display + 'a + Captures<'tcx> { - display_fn(move |f| { + fmt::from_fn(move |f| { f.write_str("impl")?; self.generics.print(cx).fmt(f)?; f.write_str(" ")?; @@ -1407,15 +1262,18 @@ impl clean::Arguments { &'a self, cx: &'a Context<'tcx>, ) -> impl Display + 'a + Captures<'tcx> { - display_fn(move |f| { - for (i, input) in self.values.iter().enumerate() { - write!(f, "{}: ", input.name)?; - input.type_.print(cx).fmt(f)?; - if i + 1 < self.values.len() { - write!(f, ", ")?; - } - } - Ok(()) + fmt::from_fn(move |f| { + self.values + .iter() + .map(|input| { + fmt::from_fn(|f| { + if !input.name.is_empty() { + write!(f, "{}: ", input.name)?; + } + input.type_.print(cx).fmt(f) + }) + }) + .joined(", ", f) }) } } @@ -1447,7 +1305,7 @@ impl clean::FnDecl { &'a self, cx: &'a Context<'tcx>, ) -> impl Display + 'b + Captures<'tcx> { - display_fn(move |f| { + fmt::from_fn(move |f| { let ellipsis = if self.c_variadic { ", ..." } else { "" }; if f.alternate() { write!( @@ -1481,10 +1339,10 @@ impl clean::FnDecl { indent: usize, cx: &'a Context<'tcx>, ) -> impl Display + 'a + Captures<'tcx> { - display_fn(move |f| { + fmt::from_fn(move |f| { // First, generate the text form of the declaration, with no line wrapping, and count the bytes. let mut counter = WriteCounter(0); - write!(&mut counter, "{:#}", display_fn(|f| { self.inner_full_print(None, f, cx) })) + write!(&mut counter, "{:#}", fmt::from_fn(|f| { self.inner_full_print(None, f, cx) })) .unwrap(); // If the text form was over 80 characters wide, we will line-wrap our output. let line_wrapping_indent = @@ -1566,7 +1424,7 @@ impl clean::FnDecl { &'a self, cx: &'a Context<'tcx>, ) -> impl Display + 'a + Captures<'tcx> { - display_fn(move |f| match &self.output { + fmt::from_fn(move |f| match &self.output { clean::Tuple(tys) if tys.is_empty() => Ok(()), ty if f.alternate() => { write!(f, " -> {:#}", ty.print(cx)) @@ -1618,7 +1476,7 @@ pub(crate) fn visibility_print_with_space<'a, 'tcx: 'a>( }; let is_doc_hidden = item.is_doc_hidden(); - display_fn(move |f| { + fmt::from_fn(move |f| { if is_doc_hidden { f.write_str("#[doc(hidden)] ")?; } @@ -1637,6 +1495,15 @@ impl PrintWithSpace for hir::Safety { } } +impl PrintWithSpace for hir::HeaderSafety { + fn print_with_space(&self) -> &str { + match self { + hir::HeaderSafety::SafeTargetFeatures => "", + hir::HeaderSafety::Normal(safety) => safety.print_with_space(), + } + } +} + impl PrintWithSpace for hir::IsAsync { fn print_with_space(&self) -> &str { match self { @@ -1683,7 +1550,7 @@ impl clean::Import { &'a self, cx: &'a Context<'tcx>, ) -> impl Display + 'a + Captures<'tcx> { - display_fn(move |f| match self.kind { + fmt::from_fn(move |f| match self.kind { clean::ImportKind::Simple(name) => { if name == self.source.path.last() { write!(f, "use {};", self.source.print(cx)) @@ -1707,7 +1574,7 @@ impl clean::ImportSource { &'a self, cx: &'a Context<'tcx>, ) -> impl Display + 'a + Captures<'tcx> { - display_fn(move |f| match self.did { + fmt::from_fn(move |f| match self.did { Some(did) => resolved_path(f, did, &self.path, true, false, cx), _ => { for seg in &self.path.segments[..self.path.segments.len() - 1] { @@ -1715,12 +1582,7 @@ impl clean::ImportSource { } let name = self.path.last(); if let hir::def::Res::PrimTy(p) = self.path.res { - primitive_link( - f, - PrimitiveType::from(p), - format_args!("{}", name.as_str()), - cx, - )?; + primitive_link(f, PrimitiveType::from(p), format_args!("{name}"), cx)?; } else { f.write_str(name.as_str())?; } @@ -1735,7 +1597,7 @@ impl clean::AssocItemConstraint { &'a self, cx: &'a Context<'tcx>, ) -> impl Display + 'a + Captures<'tcx> { - display_fn(move |f| { + fmt::from_fn(move |f| { f.write_str(self.assoc.name.as_str())?; self.assoc.args.print(cx).fmt(f)?; match self.kind { @@ -1756,7 +1618,7 @@ impl clean::AssocItemConstraint { } pub(crate) fn print_abi_with_space(abi: ExternAbi) -> impl Display { - display_fn(move |f| { + fmt::from_fn(move |f| { let quot = if f.alternate() { "\"" } else { """ }; match abi { ExternAbi::Rust => Ok(()), @@ -1774,7 +1636,7 @@ impl clean::GenericArg { &'a self, cx: &'a Context<'tcx>, ) -> impl Display + 'a + Captures<'tcx> { - display_fn(move |f| match self { + fmt::from_fn(move |f| match self { clean::GenericArg::Lifetime(lt) => lt.print().fmt(f), clean::GenericArg::Type(ty) => ty.print(cx).fmt(f), clean::GenericArg::Const(ct) => ct.print(cx.tcx()).fmt(f), @@ -1788,24 +1650,9 @@ impl clean::Term { &'a self, cx: &'a Context<'tcx>, ) -> impl Display + 'a + Captures<'tcx> { - display_fn(move |f| match self { + fmt::from_fn(move |f| match self { clean::Term::Type(ty) => ty.print(cx).fmt(f), clean::Term::Constant(ct) => ct.print(cx.tcx()).fmt(f), }) } } - -pub(crate) fn display_fn(f: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result) -> impl Display { - struct WithFormatter(Cell>); - - impl Display for WithFormatter - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (self.0.take()).unwrap()(f) - } - } - - WithFormatter(Cell::new(Some(f))) -} diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 48a537ad5e7ad..ed4b97d36252c 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -14,7 +14,7 @@ use rustc_span::edition::Edition; use rustc_span::symbol::Symbol; use rustc_span::{BytePos, DUMMY_SP, Span}; -use super::format::{self, Buffer}; +use super::format::{self, write_str}; use crate::clean::PrimitiveType; use crate::html::escape::EscapeBodyText; use crate::html::render::{Context, LinkFromSrc}; @@ -48,64 +48,80 @@ pub(crate) enum Tooltip { /// Highlights `src` as an inline example, returning the HTML output. pub(crate) fn render_example_with_highlighting( src: &str, - out: &mut Buffer, + out: &mut String, tooltip: Tooltip, playground_button: Option<&str>, extra_classes: &[String], ) { write_header(out, "rust-example-rendered", None, tooltip, extra_classes); - write_code(out, src, None, None); + write_code(out, src, None, None, None); write_footer(out, playground_button); } fn write_header( - out: &mut Buffer, + out: &mut String, class: &str, - extra_content: Option, + extra_content: Option<&str>, tooltip: Tooltip, extra_classes: &[String], ) { - write!(out, "
", match tooltip { - Tooltip::Ignore => " ignore", - Tooltip::CompileFail => " compile_fail", - Tooltip::ShouldPanic => " should_panic", - Tooltip::Edition(_) => " edition", - Tooltip::None => "", - },); + write_str( + out, + format_args!( + "
", + match tooltip { + Tooltip::Ignore => " ignore", + Tooltip::CompileFail => " compile_fail", + Tooltip::ShouldPanic => " should_panic", + Tooltip::Edition(_) => " edition", + Tooltip::None => "", + } + ), + ); if tooltip != Tooltip::None { let edition_code; - write!(out, "", match tooltip { - Tooltip::Ignore => "This example is not tested", - Tooltip::CompileFail => "This example deliberately fails to compile", - Tooltip::ShouldPanic => "This example panics", - Tooltip::Edition(edition) => { - edition_code = format!("This example runs with edition {edition}"); - &edition_code - } - Tooltip::None => unreachable!(), - },); + write_str( + out, + format_args!( + "", + match tooltip { + Tooltip::Ignore => "This example is not tested", + Tooltip::CompileFail => "This example deliberately fails to compile", + Tooltip::ShouldPanic => "This example panics", + Tooltip::Edition(edition) => { + edition_code = format!("This example runs with edition {edition}"); + &edition_code + } + Tooltip::None => unreachable!(), + } + ), + ); } if let Some(extra) = extra_content { - out.push_buffer(extra); + out.push_str(&extra); } if class.is_empty() { - write!( + write_str( out, - "
",
-            if extra_classes.is_empty() { "" } else { " " },
-            extra_classes.join(" "),
+            format_args!(
+                "
",
+                if extra_classes.is_empty() { "" } else { " " },
+                extra_classes.join(" ")
+            ),
         );
     } else {
-        write!(
+        write_str(
             out,
-            "
",
-            if extra_classes.is_empty() { "" } else { " " },
-            extra_classes.join(" "),
+            format_args!(
+                "
",
+                if extra_classes.is_empty() { "" } else { " " },
+                extra_classes.join(" ")
+            ),
         );
     }
-    write!(out, "");
+    write_str(out, format_args!(""));
 }
 
 /// Check if two `Class` can be merged together. In the following rules, "unclassified" means `None`
@@ -142,6 +158,7 @@ struct TokenHandler<'a, 'tcx, F: Write> {
     /// used to generate links.
     pending_elems: Vec<(&'a str, Option)>,
     href_context: Option>,
+    write_line_number: fn(&mut F, u32, &'static str),
 }
 
 impl TokenHandler<'_, '_, F> {
@@ -174,7 +191,14 @@ impl TokenHandler<'_, '_, F> {
             && can_merge(current_class, Some(*parent_class), "")
         {
             for (text, class) in self.pending_elems.iter() {
-                string(self.out, EscapeBodyText(text), *class, &self.href_context, false);
+                string(
+                    self.out,
+                    EscapeBodyText(text),
+                    *class,
+                    &self.href_context,
+                    false,
+                    self.write_line_number,
+                );
             }
         } else {
             // We only want to "open" the tag ourselves if we have more than one pending and if the
@@ -196,6 +220,7 @@ impl TokenHandler<'_, '_, F> {
                     *class,
                     &self.href_context,
                     close_tag.is_none(),
+                    self.write_line_number,
                 );
             }
             if let Some(close_tag) = close_tag {
@@ -205,6 +230,11 @@ impl TokenHandler<'_, '_, F> {
         self.pending_elems.clear();
         true
     }
+
+    #[inline]
+    fn write_line_number(&mut self, line: u32, extra: &'static str) {
+        (self.write_line_number)(&mut self.out, line, extra);
+    }
 }
 
 impl Drop for TokenHandler<'_, '_, F> {
@@ -218,6 +248,43 @@ impl Drop for TokenHandler<'_, '_, F> {
     }
 }
 
+fn write_scraped_line_number(out: &mut impl Write, line: u32, extra: &'static str) {
+    // https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr
+    // Do not show "1 2 3 4 5 ..." in web search results.
+    write!(out, "{extra}{line}",).unwrap();
+}
+
+fn write_line_number(out: &mut impl Write, line: u32, extra: &'static str) {
+    // https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr
+    // Do not show "1 2 3 4 5 ..." in web search results.
+    write!(out, "{extra}{line}",).unwrap();
+}
+
+fn empty_line_number(out: &mut impl Write, _: u32, extra: &'static str) {
+    out.write_str(extra).unwrap();
+}
+
+#[derive(Clone, Copy)]
+pub(super) struct LineInfo {
+    pub(super) start_line: u32,
+    max_lines: u32,
+    pub(super) is_scraped_example: bool,
+}
+
+impl LineInfo {
+    pub(super) fn new(max_lines: u32) -> Self {
+        Self { start_line: 1, max_lines: max_lines + 1, is_scraped_example: false }
+    }
+
+    pub(super) fn new_scraped(max_lines: u32, start_line: u32) -> Self {
+        Self {
+            start_line: start_line + 1,
+            max_lines: max_lines + start_line + 1,
+            is_scraped_example: true,
+        }
+    }
+}
+
 /// Convert the given `src` source code into HTML by adding classes for highlighting.
 ///
 /// This code is used to render code blocks (in the documentation) as well as the source code pages.
@@ -233,7 +300,8 @@ pub(super) fn write_code(
     out: &mut impl Write,
     src: &str,
     href_context: Option>,
-    decoration_info: Option,
+    decoration_info: Option<&DecorationInfo>,
+    line_info: Option,
 ) {
     // This replace allows to fix how the code source with DOS backline characters is displayed.
     let src = src.replace("\r\n", "\n");
@@ -244,6 +312,23 @@ pub(super) fn write_code(
         current_class: None,
         pending_elems: Vec::new(),
         href_context,
+        write_line_number: match line_info {
+            Some(line_info) => {
+                if line_info.is_scraped_example {
+                    write_scraped_line_number
+                } else {
+                    write_line_number
+                }
+            }
+            None => empty_line_number,
+        },
+    };
+
+    let (mut line, max_lines) = if let Some(line_info) = line_info {
+        token_handler.write_line_number(line_info.start_line, "");
+        (line_info.start_line, line_info.max_lines)
+    } else {
+        (0, u32::MAX)
     };
 
     Classifier::new(
@@ -274,7 +359,14 @@ pub(super) fn write_code(
                 if need_current_class_update {
                     token_handler.current_class = class.map(Class::dummy);
                 }
-                token_handler.pending_elems.push((text, class));
+                if text == "\n" {
+                    line += 1;
+                    if line < max_lines {
+                        token_handler.pending_elems.push((text, Some(Class::Backline(line))));
+                    }
+                } else {
+                    token_handler.pending_elems.push((text, class));
+                }
             }
             Highlight::EnterSpan { class } => {
                 let mut should_add = true;
@@ -314,8 +406,8 @@ pub(super) fn write_code(
     });
 }
 
-fn write_footer(out: &mut Buffer, playground_button: Option<&str>) {
-    writeln!(out, "
{}
", playground_button.unwrap_or_default()); +fn write_footer(out: &mut String, playground_button: Option<&str>) { + write_str(out, format_args_nl!("{}
", playground_button.unwrap_or_default())); } /// How a span of text is classified. Mostly corresponds to token kinds. @@ -337,9 +429,10 @@ enum Class { Ident(Span), Lifetime, PreludeTy(Span), - PreludeVal, + PreludeVal(Span), QuestionMark, Decoration(&'static str), + Backline(u32), } impl Class { @@ -385,9 +478,10 @@ impl Class { Class::Ident(_) => "", Class::Lifetime => "lifetime", Class::PreludeTy(_) => "prelude-ty", - Class::PreludeVal => "prelude-val", + Class::PreludeVal(_) => "prelude-val", Class::QuestionMark => "question-mark", Class::Decoration(kind) => kind, + Class::Backline(_) => "", } } @@ -395,7 +489,11 @@ impl Class { /// a "span" (a tuple representing `(lo, hi)` equivalent of `Span`). fn get_span(self) -> Option { match self { - Self::Ident(sp) | Self::Self_(sp) | Self::Macro(sp) | Self::PreludeTy(sp) => Some(sp), + Self::Ident(sp) + | Self::Self_(sp) + | Self::Macro(sp) + | Self::PreludeTy(sp) + | Self::PreludeVal(sp) => Some(sp), Self::Comment | Self::DocComment | Self::Attribute @@ -406,9 +504,9 @@ impl Class { | Self::Number | Self::Bool | Self::Lifetime - | Self::PreludeVal | Self::QuestionMark - | Self::Decoration(_) => None, + | Self::Decoration(_) + | Self::Backline(_) => None, } } } @@ -507,12 +605,12 @@ struct Decorations { } impl Decorations { - fn new(info: DecorationInfo) -> Self { + fn new(info: &DecorationInfo) -> Self { // Extract tuples (start, end, kind) into separate sequences of (start, kind) and (end). let (mut starts, mut ends): (Vec<_>, Vec<_>) = info .0 - .into_iter() - .flat_map(|(kind, ranges)| ranges.into_iter().map(move |(lo, hi)| ((lo, kind), hi))) + .iter() + .flat_map(|(&kind, ranges)| ranges.into_iter().map(move |&(lo, hi)| ((lo, kind), hi))) .unzip(); // Sort the sequences in document order. @@ -539,7 +637,7 @@ struct Classifier<'src> { impl<'src> Classifier<'src> { /// Takes as argument the source code to HTML-ify, the rust edition to use and the source code /// file span which will be used later on by the `span_correspondence_map`. - fn new(src: &str, file_span: Span, decoration_info: Option) -> Classifier<'_> { + fn new(src: &'src str, file_span: Span, decoration_info: Option<&DecorationInfo>) -> Self { let tokens = PeekIter::new(TokenIter { src, cursor: Cursor::new(src) }); let decorations = decoration_info.map(Decorations::new); Classifier { @@ -683,8 +781,13 @@ impl<'src> Classifier<'src> { ) { let lookahead = self.peek(); let no_highlight = |sink: &mut dyn FnMut(_)| sink(Highlight::Token { text, class: None }); + let whitespace = |sink: &mut dyn FnMut(_)| { + for part in text.split('\n').intersperse("\n").filter(|s| !s.is_empty()) { + sink(Highlight::Token { text: part, class: None }); + } + }; let class = match token { - TokenKind::Whitespace => return no_highlight(sink), + TokenKind::Whitespace => return whitespace(sink), TokenKind::LineComment { doc_style } | TokenKind::BlockComment { doc_style, .. } => { if doc_style.is_some() { Class::DocComment @@ -705,7 +808,7 @@ impl<'src> Classifier<'src> { // or a reference or pointer type. Unless, of course, it looks like // a logical and or a multiplication operator: `&&` or `* `. TokenKind::Star => match self.tokens.peek() { - Some((TokenKind::Whitespace, _)) => return no_highlight(sink), + Some((TokenKind::Whitespace, _)) => return whitespace(sink), Some((TokenKind::Ident, "mut")) => { self.next(); sink(Highlight::Token { text: "*mut", class: Some(Class::RefKeyWord) }); @@ -729,7 +832,7 @@ impl<'src> Classifier<'src> { sink(Highlight::Token { text: "&=", class: None }); return; } - Some((TokenKind::Whitespace, _)) => return no_highlight(sink), + Some((TokenKind::Whitespace, _)) => return whitespace(sink), Some((TokenKind::Ident, "mut")) => { self.next(); sink(Highlight::Token { text: "&mut", class: Some(Class::RefKeyWord) }); @@ -851,7 +954,9 @@ impl<'src> Classifier<'src> { TokenKind::Ident => match get_real_ident_class(text, false) { None => match text { "Option" | "Result" => Class::PreludeTy(self.new_span(before, text)), - "Some" | "None" | "Ok" | "Err" => Class::PreludeVal, + "Some" | "None" | "Ok" | "Err" => { + Class::PreludeVal(self.new_span(before, text)) + } // "union" is a weak keyword and is only considered as a keyword when declaring // a union type. "union" if self.check_if_is_union_keyword() => Class::KeyWord, @@ -874,7 +979,9 @@ impl<'src> Classifier<'src> { }; // Anything that didn't return above is the simple case where we the // class just spans a single token, so we can use the `string` method. - sink(Highlight::Token { text, class: Some(class) }); + for part in text.split('\n').intersperse("\n").filter(|s| !s.is_empty()) { + sink(Highlight::Token { text: part, class: Some(class) }); + } } fn peek(&mut self) -> Option { @@ -926,14 +1033,18 @@ fn exit_span(out: &mut impl Write, closing_tag: &str) { /// Note that if `context` is not `None` and that the given `klass` contains a `Span`, the function /// will then try to find this `span` in the `span_correspondence_map`. If found, it'll then /// generate a link for this element (which corresponds to where its definition is located). -fn string( - out: &mut impl Write, +fn string( + out: &mut W, text: T, klass: Option, href_context: &Option>, open_tag: bool, + write_line_number_callback: fn(&mut W, u32, &'static str), ) { - if let Some(closing_tag) = string_without_closing_tag(out, text, klass, href_context, open_tag) + if let Some(Class::Backline(line)) = klass { + write_line_number_callback(out, line, "\n"); + } else if let Some(closing_tag) = + string_without_closing_tag(out, text, klass, href_context, open_tag) { out.write_str(closing_tag).unwrap(); } diff --git a/src/librustdoc/html/highlight/tests.rs b/src/librustdoc/html/highlight/tests.rs index fd5275189d661..2603e887bead5 100644 --- a/src/librustdoc/html/highlight/tests.rs +++ b/src/librustdoc/html/highlight/tests.rs @@ -3,7 +3,6 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_span::create_default_session_globals_then; use super::{DecorationInfo, write_code}; -use crate::html::format::Buffer; const STYLE: &str = r#"
#[allow(dead_code)]
-#[rustfmt::skip]
+#[rustfmt::skip]
 #[proc_macros::identity]
 #[derive(Default)]
 /// This is a doc comment
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html
index edd9639768abe..c6eab90e42b05 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html
@@ -45,7 +45,7 @@
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
-
macro_rules! foo {
+
macro_rules! foo {
     ($foo:ident) => {
         mod y {
             pub struct $foo;
@@ -53,9 +53,9 @@
     };
 }
 fn main() {
-    foo!(Foo);
+    foo!(Foo);
     mod module {
-        foo!(Bar);
+        foo!(Bar);
         fn func(_: y::Bar) {
             mod inner {
                 struct Innerest<const C: usize> { field: [(); {C}] }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_const.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_const.html
index 05289cfe3fe3b..96cdb532dd543 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_const.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_const.html
@@ -45,7 +45,7 @@
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
-
macro_rules! id {
+
macro_rules! id {
     ($($tt:tt)*) => {
         $($tt)*
     };
@@ -57,7 +57,7 @@
     const {
         const || {}
     }
-    id!(
+    id!(
         CONST_ITEM;
         CONST_PARAM;
         const {
@@ -78,7 +78,7 @@
     const fn assoc_const_fn() {}
 }
 
-macro_rules! unsafe_deref {
+macro_rules! unsafe_deref {
     () => {
         *(&() as *const ())
     };
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index aa9d23250c154..5ff96ae2a74f2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -147,10 +147,10 @@
 }
 
 /// ```
-/// macro_rules! noop { ($expr:expr) => { $expr }}
-/// noop!(1);
+/// macro_rules! noop { ($expr:expr) => { $expr }}
+/// noop!(1);
 /// ```
-macro_rules! noop {
+macro_rules! noop {
     ($expr:expr) => {
         $expr
     }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
index 7820e4e5a5f96..fe5f5ab6a9a27 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
@@ -45,7 +45,7 @@
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
-
extern crate self as this;
+
extern crate self as this;
 extern crate std;
 extern crate alloc as abc;
 extern crate unresolved as definitely_unresolved;
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
index 6c3fbcfcf41e2..5fbed35192bb2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
@@ -45,7 +45,7 @@
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
-
fn fixture(ra_fixture: &str) {}
+
fn fixture(#[rust_analyzer::rust_fixture] ra_fixture: &str) {}
 
 fn main() {
     fixture(r#"
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html
index 361dcd1bc37c7..06817af1b1f23 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html
@@ -46,8 +46,8 @@
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
 
fn main() {
-    template!(template);
+    template!(template);
 }
 
 #[proc_macros::issue_18089]
-fn template() {}
\ No newline at end of file +fn template() {}
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html index c2bf94fd9b6f0..2d3407dbcda00 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html @@ -53,22 +53,22 @@ use super::*; } -macro_rules! void { +macro_rules! void { ($($tt:tt)*) => {} } struct __ where Self:; fn __(_: Self) {} -void!(Self); +void!(Self); // edition dependent -void!(try async await gen); +void!(try async await gen); // edition and context dependent -void!(dyn); +void!(dyn); // builtin custom syntax -void!(builtin offset_of format_args asm); +void!(builtin offset_of format_args asm); // contextual -void!(macro_rules, union, default, raw, auto, yeet); +void!(macro_rules, union, default, raw, auto, yeet); // reserved -void!(abstract become box do final macro override priv typeof unsized virtual yield); -void!('static 'self 'unsafe)
\ No newline at end of file +void!(abstract become box do final macro override priv typeof unsized virtual yield); +void!('static 'self 'unsafe)
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html index a30d16d53271e..f8eb5d068a81f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html @@ -53,22 +53,22 @@ use super::*; } -macro_rules! void { +macro_rules! void { ($($tt:tt)*) => {} } struct __ where Self:; fn __(_: Self) {} -void!(Self); +void!(Self); // edition dependent -void!(try async await gen); +void!(try async await gen); // edition and context dependent -void!(dyn); +void!(dyn); // builtin custom syntax -void!(builtin offset_of format_args asm); +void!(builtin offset_of format_args asm); // contextual -void!(macro_rules, union, default, raw, auto, yeet); +void!(macro_rules, union, default, raw, auto, yeet); // reserved -void!(abstract become box do final macro override priv typeof unsized virtual yield); -void!('static 'self 'unsafe)
\ No newline at end of file +void!(abstract become box do final macro override priv typeof unsized virtual yield); +void!('static 'self 'unsafe)
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html index a30d16d53271e..f8eb5d068a81f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html @@ -53,22 +53,22 @@ use super::*; } -macro_rules! void { +macro_rules! void { ($($tt:tt)*) => {} } struct __ where Self:; fn __(_: Self) {} -void!(Self); +void!(Self); // edition dependent -void!(try async await gen); +void!(try async await gen); // edition and context dependent -void!(dyn); +void!(dyn); // builtin custom syntax -void!(builtin offset_of format_args asm); +void!(builtin offset_of format_args asm); // contextual -void!(macro_rules, union, default, raw, auto, yeet); +void!(macro_rules, union, default, raw, auto, yeet); // reserved -void!(abstract become box do final macro override priv typeof unsized virtual yield); -void!('static 'self 'unsafe)
\ No newline at end of file +void!(abstract become box do final macro override priv typeof unsized virtual yield); +void!('static 'self 'unsafe)
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html index b82a3f9f81967..fca8401706968 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html @@ -53,22 +53,22 @@ use super::*; } -macro_rules! void { +macro_rules! void { ($($tt:tt)*) => {} } struct __ where Self:; fn __(_: Self) {} -void!(Self); +void!(Self); // edition dependent -void!(try async await gen); +void!(try async await gen); // edition and context dependent -void!(dyn); +void!(dyn); // builtin custom syntax -void!(builtin offset_of format_args asm); +void!(builtin offset_of format_args asm); // contextual -void!(macro_rules, union, default, raw, auto, yeet); +void!(macro_rules, union, default, raw, auto, yeet); // reserved -void!(abstract become box do final macro override priv typeof unsized virtual yield); -void!('static 'self 'unsafe)
\ No newline at end of file +void!(abstract become box do final macro override priv typeof unsized virtual yield); +void!('static 'self 'unsafe)
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html index 06673d1a73c7b..f640a5e6ca7f7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html @@ -53,34 +53,34 @@ ,i32 :y pub } Foo struct } -macro_rules! def_fn { +macro_rules! def_fn { ($($tt:tt)*) => {$($tt)*} } -def_fn! { +def_fn! { fn bar() -> u32 { 100 } } -macro_rules! dont_color_me_braces { +macro_rules! dont_color_me_braces { () => {0} } -macro_rules! noop { +macro_rules! noop { ($expr:expr) => { $expr } } /// textually shadow previous definition -macro_rules! noop { +macro_rules! noop { ($expr:expr) => { $expr } } -macro_rules! keyword_frag { +macro_rules! keyword_frag { ($type:ty) => ($type) } @@ -94,7 +94,7 @@ } } -macro_rules! id { +macro_rules! id { ($($tt:tt)*) => { $($tt)* }; @@ -106,10 +106,10 @@ fn main() { struct TestLocal; // regression test, TestLocal here used to not resolve - let _: S<id![TestLocal]>; + let _: S<id![TestLocal]>; format_args!("Hello, {}!", (92,).0); - dont_color_me_braces!(); - noop!(noop!(1)); + dont_color_me_braces!(); + noop!(noop!(1)); } \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index 1385ae0510aa6..1794d7dbfe29e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -45,14 +45,14 @@ .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } -
macro_rules! println {
+
macro_rules! println {
     ($($arg:tt)*) => ({
         $crate::io::_print(format_args_nl!($($arg)*));
     })
 }
 
 mod panic {
-    pub macro panic_2015 {
+    pub macro panic_2015 {
         () => (
             panic("explicit panic")
         ),
@@ -73,15 +73,19 @@
     }
 }
 
-macro_rules! toho {
+macro_rules! toho {
     () => ($crate::panic!("not yet implemented"));
     ($($arg:tt)+) => ($crate::panic!("not yet implemented: {}", format_args!($($arg)+)));
 }
 
-macro_rules! reuse_twice {
+macro_rules! reuse_twice {
     ($literal:literal) => {{stringify!($literal); format_args!($literal)}};
 }
 
+use foo::bar as baz;
+trait Bar = Baz;
+trait Foo = Bar;
+
 fn main() {
     let a = '\n';
     let a = '\t';
@@ -95,74 +99,74 @@
 
     let a = b'\xFF';
 
-    println!("Hello {{Hello}}");
+    println!("Hello {{Hello}}");
     // from https://doc.rust-lang.org/std/fmt/index.html
-    println!("Hello");                 // => "Hello"
-    println!("Hello, {}!", "world");   // => "Hello, world!"
-    println!("The number is {}", 1);   // => "The number is 1"
-    println!("{:?}", (3, 4));          // => "(3, 4)"
-    println!("{value}", value=4);      // => "4"
-    println!("{} {}", 1, 2);           // => "1 2"
-    println!("{:04}", 42);             // => "0042" with leading zerosV
-    println!("{1} {} {0} {}", 1, 2);   // => "2 1 1 2"
-    println!("{argument}", argument = "test");   // => "test"
-    println!("{name} {}", 1, name = 2);          // => "2 1"
-    println!("{a} {c} {b}", a="a", b='b', c=3);  // => "a 3 b"
-    println!("{{{}}}", 2);                       // => "{2}"
-    println!("Hello {:5}!", "x");
-    println!("Hello {:1$}!", "x", 5);
-    println!("Hello {1:0$}!", 5, "x");
-    println!("Hello {:width$}!", "x", width = 5);
-    println!("Hello {:<5}!", "x");
-    println!("Hello {:-<5}!", "x");
-    println!("Hello {:^5}!", "x");
-    println!("Hello {:>5}!", "x");
-    println!("Hello {:+}!", 5);
-    println!("{:#x}!", 27);
-    println!("Hello {:05}!", 5);
-    println!("Hello {:05}!", -5);
-    println!("{:#010x}!", 27);
-    println!("Hello {0} is {1:.5}", "x", 0.01);
-    println!("Hello {1} is {2:.0$}", 5, "x", 0.01);
-    println!("Hello {0} is {2:.1$}", "x", 5, 0.01);
-    println!("Hello {} is {:.*}",    "x", 5, 0.01);
-    println!("Hello {} is {2:.*}",   "x", 5, 0.01);
-    println!("Hello {} is {number:.prec$}", "x", prec = 5, number = 0.01);
-    println!("{}, `{name:.*}` has 3 fractional digits", "Hello", 3, name=1234.56);
-    println!("{}, `{name:.*}` has 3 characters", "Hello", 3, name="1234.56");
-    println!("{}, `{name:>8.*}` has 3 right-aligned characters", "Hello", 3, name="1234.56");
+    println!("Hello");                 // => "Hello"
+    println!("Hello, {}!", "world");   // => "Hello, world!"
+    println!("The number is {}", 1);   // => "The number is 1"
+    println!("{:?}", (3, 4));          // => "(3, 4)"
+    println!("{value}", value=4);      // => "4"
+    println!("{} {}", 1, 2);           // => "1 2"
+    println!("{:04}", 42);             // => "0042" with leading zerosV
+    println!("{1} {} {0} {}", 1, 2);   // => "2 1 1 2"
+    println!("{argument}", argument = "test");   // => "test"
+    println!("{name} {}", 1, name = 2);          // => "2 1"
+    println!("{a} {c} {b}", a="a", b='b', c=3);  // => "a 3 b"
+    println!("{{{}}}", 2);                       // => "{2}"
+    println!("Hello {:5}!", "x");
+    println!("Hello {:1$}!", "x", 5);
+    println!("Hello {1:0$}!", 5, "x");
+    println!("Hello {:width$}!", "x", width = 5);
+    println!("Hello {:<5}!", "x");
+    println!("Hello {:-<5}!", "x");
+    println!("Hello {:^5}!", "x");
+    println!("Hello {:>5}!", "x");
+    println!("Hello {:+}!", 5);
+    println!("{:#x}!", 27);
+    println!("Hello {:05}!", 5);
+    println!("Hello {:05}!", -5);
+    println!("{:#010x}!", 27);
+    println!("Hello {0} is {1:.5}", "x", 0.01);
+    println!("Hello {1} is {2:.0$}", 5, "x", 0.01);
+    println!("Hello {0} is {2:.1$}", "x", 5, 0.01);
+    println!("Hello {} is {:.*}",    "x", 5, 0.01);
+    println!("Hello {} is {2:.*}",   "x", 5, 0.01);
+    println!("Hello {} is {number:.prec$}", "x", prec = 5, number = 0.01);
+    println!("{}, `{name:.*}` has 3 fractional digits", "Hello", 3, name=1234.56);
+    println!("{}, `{name:.*}` has 3 characters", "Hello", 3, name="1234.56");
+    println!("{}, `{name:>8.*}` has 3 right-aligned characters", "Hello", 3, name="1234.56");
 
     let _ = "{}"
     let _ = "{{}}";
 
-    println!("Hello {{}}");
-    println!("{{ Hello");
-    println!("Hello }}");
-    println!("{{Hello}}");
-    println!("{{ Hello }}");
-    println!("{{Hello }}");
-    println!("{{ Hello}}");
+    println!("Hello {{}}");
+    println!("{{ Hello");
+    println!("Hello }}");
+    println!("{{Hello}}");
+    println!("{{ Hello }}");
+    println!("{{Hello }}");
+    println!("{{ Hello}}");
 
-    println!(r"Hello, {}!", "world");
+    println!(r"Hello, {}!", "world");
 
     // escape sequences
-    println!("Hello\nWorld");
-    println!("\u{48}\x65\x6C\x6C\x6F World");
+    println!("Hello\nWorld");
+    println!("\u{48}\x65\x6C\x6C\x6F World");
 
     let _ = "\x28\x28\x00\x63\xFF\u{FF}\n"; // invalid non-UTF8 escape sequences
     let _ = b"\x28\x28\x00\x63\xFF\u{FF}\n"; // valid bytes, invalid unicodes
     let _ = c"\u{FF}\xFF"; // valid bytes, valid unicodes
     let backslash = r"\\";
 
-    println!("{\x41}", A = 92);
-    println!("{ничоси}", ничоси = 92);
+    println!("{\x41}", A = 92);
+    println!("{ничоси}", ничоси = 92);
 
-    println!("{:x?} {} ", thingy, n2);
+    println!("{:x?} {} ", thingy, n2);
     panic!("{}", 0);
     panic!("more {}", 1);
     assert!(true, "{}", 1);
     assert!(true, "{} asdasd", 1);
-    toho!("{}fmt", 0);
+    toho!("{}fmt", 0);
     let i: u64 = 3;
     let o: u64;
     core::arch::asm!(
@@ -175,6 +179,6 @@
     const CONSTANT: () = ():
     let mut m = ();
     format_args!(concat!("{}"), "{}");
-    format_args!("{} {} {} {} {} {} {backslash} {CONSTANT} {m}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash);
-    reuse_twice!("{backslash}");
+    format_args!("{} {} {} {} {} {} {backslash} {CONSTANT} {m}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash);
+    reuse_twice!("{backslash}");
 }
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index 4e69c82f3da63..d9beac3089825 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html @@ -45,12 +45,12 @@ .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } -
macro_rules! id {
+
macro_rules! id {
     ($($tt:tt)*) => {
         $($tt)*
     };
 }
-macro_rules! unsafe_deref {
+macro_rules! unsafe_deref {
     () => {
         *(&() as *const ())
     };
@@ -92,13 +92,13 @@
     let x = &5 as *const _ as *const usize;
     let u = Union { b: 0 };
 
-    id! {
-        unsafe { unsafe_deref!() }
+    id! {
+        unsafe { unsafe_deref!() }
     };
 
     unsafe {
-        unsafe_deref!();
-        id! { unsafe_deref!() };
+        unsafe_deref!();
+        id! { unsafe_deref!() };
 
         // unsafe fn and method calls
         unsafe_fn();
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
index a20147add362c..b9520ae2bba27 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
@@ -466,6 +466,10 @@ macro_rules! reuse_twice {
     ($literal:literal) => {{stringify!($literal); format_args!($literal)}};
 }
 
+use foo::bar as baz;
+trait Bar = Baz;
+trait Foo = Bar;
+
 fn main() {
     let a = '\n';
     let a = '\t';
@@ -990,7 +994,7 @@ impl t for foo {
 fn test_injection() {
     check_highlighting(
         r##"
-fn fixture(ra_fixture: &str) {}
+fn fixture(#[rust_analyzer::rust_fixture] ra_fixture: &str) {}
 
 fn main() {
     fixture(r#"
@@ -1188,7 +1192,11 @@ fn foo(x: &fn(&dyn Trait)) {}
 /// Highlights the code given by the `ra_fixture` argument, renders the
 /// result as HTML, and compares it with the HTML file given as `snapshot`.
 /// Note that the `snapshot` file is overwritten by the rendered HTML.
-fn check_highlighting(ra_fixture: &str, expect: ExpectFile, rainbow: bool) {
+fn check_highlighting(
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    expect: ExpectFile,
+    rainbow: bool,
+) {
     let (analysis, file_id) = fixture::file(ra_fixture.trim());
     let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap();
     expect.assert_eq(actual_html)
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs
deleted file mode 100644
index e241cb82bd5b2..0000000000000
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs
+++ /dev/null
@@ -1,338 +0,0 @@
-use hir::Semantics;
-use ide_db::{FileId, RootDatabase};
-use syntax::{
-    AstNode, NodeOrToken, SourceFile, SyntaxKind::STRING, SyntaxToken, TextRange, TextSize,
-};
-
-// Feature: Show Syntax Tree
-//
-// Shows the parse tree of the current file. It exists mostly for debugging
-// rust-analyzer itself.
-//
-// |===
-// | Editor  | Action Name
-//
-// | VS Code | **rust-analyzer: Show Syntax Tree**
-// |===
-// image::https://user-images.githubusercontent.com/48062697/113065586-068bdb80-91b1-11eb-9507-fee67f9f45a0.gif[]
-pub(crate) fn syntax_tree(
-    db: &RootDatabase,
-    file_id: FileId,
-    text_range: Option,
-) -> String {
-    let sema = Semantics::new(db);
-    let parse = sema.parse_guess_edition(file_id);
-    if let Some(text_range) = text_range {
-        let node = match parse.syntax().covering_element(text_range) {
-            NodeOrToken::Node(node) => node,
-            NodeOrToken::Token(token) => {
-                if let Some(tree) = syntax_tree_for_string(&token, text_range) {
-                    return tree;
-                }
-                token.parent().unwrap()
-            }
-        };
-
-        format!("{node:#?}")
-    } else {
-        format!("{:#?}", parse.syntax())
-    }
-}
-
-/// Attempts parsing the selected contents of a string literal
-/// as rust syntax and returns its syntax tree
-fn syntax_tree_for_string(token: &SyntaxToken, text_range: TextRange) -> Option {
-    // When the range is inside a string
-    // we'll attempt parsing it as rust syntax
-    // to provide the syntax tree of the contents of the string
-    match token.kind() {
-        STRING => syntax_tree_for_token(token, text_range),
-        _ => None,
-    }
-}
-
-fn syntax_tree_for_token(node: &SyntaxToken, text_range: TextRange) -> Option {
-    // Range of the full node
-    let node_range = node.text_range();
-    let text = node.text().to_owned();
-
-    // We start at some point inside the node
-    // Either we have selected the whole string
-    // or our selection is inside it
-    let start = text_range.start() - node_range.start();
-
-    // how many characters we have selected
-    let len = text_range.len();
-
-    let node_len = node_range.len();
-
-    // We want to cap our length
-    let len = len.min(node_len);
-
-    // Ensure our slice is inside the actual string
-    let end =
-        if start + len < TextSize::of(&text) { start + len } else { TextSize::of(&text) - start };
-
-    let text = &text[TextRange::new(start, end)];
-
-    // Remove possible extra string quotes from the start
-    // and the end of the string
-    let text = text
-        .trim_start_matches('r')
-        .trim_start_matches('#')
-        .trim_start_matches('"')
-        .trim_end_matches('#')
-        .trim_end_matches('"')
-        .trim()
-        // Remove custom markers
-        .replace("$0", "");
-
-    let parsed = SourceFile::parse(&text, span::Edition::CURRENT_FIXME);
-
-    // If the "file" parsed without errors,
-    // return its syntax
-    if parsed.errors().is_empty() {
-        return Some(format!("{:#?}", parsed.tree().syntax()));
-    }
-
-    None
-}
-
-#[cfg(test)]
-mod tests {
-    use expect_test::expect;
-
-    use crate::fixture;
-
-    fn check(ra_fixture: &str, expect: expect_test::Expect) {
-        let (analysis, file_id) = fixture::file(ra_fixture);
-        let syn = analysis.syntax_tree(file_id, None).unwrap();
-        expect.assert_eq(&syn)
-    }
-    fn check_range(ra_fixture: &str, expect: expect_test::Expect) {
-        let (analysis, frange) = fixture::range(ra_fixture);
-        let syn = analysis.syntax_tree(frange.file_id, Some(frange.range)).unwrap();
-        expect.assert_eq(&syn)
-    }
-
-    #[test]
-    fn test_syntax_tree_without_range() {
-        // Basic syntax
-        check(
-            r#"fn foo() {}"#,
-            expect![[r#"
-                SOURCE_FILE@0..11
-                  FN@0..11
-                    FN_KW@0..2 "fn"
-                    WHITESPACE@2..3 " "
-                    NAME@3..6
-                      IDENT@3..6 "foo"
-                    PARAM_LIST@6..8
-                      L_PAREN@6..7 "("
-                      R_PAREN@7..8 ")"
-                    WHITESPACE@8..9 " "
-                    BLOCK_EXPR@9..11
-                      STMT_LIST@9..11
-                        L_CURLY@9..10 "{"
-                        R_CURLY@10..11 "}"
-            "#]],
-        );
-
-        check(
-            r#"
-fn test() {
-    assert!("
-    fn foo() {
-    }
-    ", "");
-}"#,
-            expect![[r#"
-                SOURCE_FILE@0..60
-                  FN@0..60
-                    FN_KW@0..2 "fn"
-                    WHITESPACE@2..3 " "
-                    NAME@3..7
-                      IDENT@3..7 "test"
-                    PARAM_LIST@7..9
-                      L_PAREN@7..8 "("
-                      R_PAREN@8..9 ")"
-                    WHITESPACE@9..10 " "
-                    BLOCK_EXPR@10..60
-                      STMT_LIST@10..60
-                        L_CURLY@10..11 "{"
-                        WHITESPACE@11..16 "\n    "
-                        EXPR_STMT@16..58
-                          MACRO_EXPR@16..57
-                            MACRO_CALL@16..57
-                              PATH@16..22
-                                PATH_SEGMENT@16..22
-                                  NAME_REF@16..22
-                                    IDENT@16..22 "assert"
-                              BANG@22..23 "!"
-                              TOKEN_TREE@23..57
-                                L_PAREN@23..24 "("
-                                STRING@24..52 "\"\n    fn foo() {\n     ..."
-                                COMMA@52..53 ","
-                                WHITESPACE@53..54 " "
-                                STRING@54..56 "\"\""
-                                R_PAREN@56..57 ")"
-                          SEMICOLON@57..58 ";"
-                        WHITESPACE@58..59 "\n"
-                        R_CURLY@59..60 "}"
-            "#]],
-        )
-    }
-
-    #[test]
-    fn test_syntax_tree_with_range() {
-        check_range(
-            r#"$0fn foo() {}$0"#,
-            expect![[r#"
-                FN@0..11
-                  FN_KW@0..2 "fn"
-                  WHITESPACE@2..3 " "
-                  NAME@3..6
-                    IDENT@3..6 "foo"
-                  PARAM_LIST@6..8
-                    L_PAREN@6..7 "("
-                    R_PAREN@7..8 ")"
-                  WHITESPACE@8..9 " "
-                  BLOCK_EXPR@9..11
-                    STMT_LIST@9..11
-                      L_CURLY@9..10 "{"
-                      R_CURLY@10..11 "}"
-            "#]],
-        );
-
-        check_range(
-            r#"
-fn test() {
-    $0assert!("
-    fn foo() {
-    }
-    ", "");$0
-}"#,
-            expect![[r#"
-                EXPR_STMT@16..58
-                  MACRO_EXPR@16..57
-                    MACRO_CALL@16..57
-                      PATH@16..22
-                        PATH_SEGMENT@16..22
-                          NAME_REF@16..22
-                            IDENT@16..22 "assert"
-                      BANG@22..23 "!"
-                      TOKEN_TREE@23..57
-                        L_PAREN@23..24 "("
-                        STRING@24..52 "\"\n    fn foo() {\n     ..."
-                        COMMA@52..53 ","
-                        WHITESPACE@53..54 " "
-                        STRING@54..56 "\"\""
-                        R_PAREN@56..57 ")"
-                  SEMICOLON@57..58 ";"
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_syntax_tree_inside_string() {
-        check_range(
-            r#"fn test() {
-    assert!("
-$0fn foo() {
-}$0
-fn bar() {
-}
-    ", "");
-}"#,
-            expect![[r#"
-                SOURCE_FILE@0..12
-                  FN@0..12
-                    FN_KW@0..2 "fn"
-                    WHITESPACE@2..3 " "
-                    NAME@3..6
-                      IDENT@3..6 "foo"
-                    PARAM_LIST@6..8
-                      L_PAREN@6..7 "("
-                      R_PAREN@7..8 ")"
-                    WHITESPACE@8..9 " "
-                    BLOCK_EXPR@9..12
-                      STMT_LIST@9..12
-                        L_CURLY@9..10 "{"
-                        WHITESPACE@10..11 "\n"
-                        R_CURLY@11..12 "}"
-            "#]],
-        );
-
-        // With a raw string
-        check_range(
-            r###"fn test() {
-    assert!(r#"
-$0fn foo() {
-}$0
-fn bar() {
-}
-    "#, "");
-}"###,
-            expect![[r#"
-                SOURCE_FILE@0..12
-                  FN@0..12
-                    FN_KW@0..2 "fn"
-                    WHITESPACE@2..3 " "
-                    NAME@3..6
-                      IDENT@3..6 "foo"
-                    PARAM_LIST@6..8
-                      L_PAREN@6..7 "("
-                      R_PAREN@7..8 ")"
-                    WHITESPACE@8..9 " "
-                    BLOCK_EXPR@9..12
-                      STMT_LIST@9..12
-                        L_CURLY@9..10 "{"
-                        WHITESPACE@10..11 "\n"
-                        R_CURLY@11..12 "}"
-            "#]],
-        );
-
-        // With a raw string
-        check_range(
-            r###"fn test() {
-    assert!(r$0#"
-fn foo() {
-}
-fn bar() {
-}"$0#, "");
-}"###,
-            expect![[r#"
-                SOURCE_FILE@0..25
-                  FN@0..12
-                    FN_KW@0..2 "fn"
-                    WHITESPACE@2..3 " "
-                    NAME@3..6
-                      IDENT@3..6 "foo"
-                    PARAM_LIST@6..8
-                      L_PAREN@6..7 "("
-                      R_PAREN@7..8 ")"
-                    WHITESPACE@8..9 " "
-                    BLOCK_EXPR@9..12
-                      STMT_LIST@9..12
-                        L_CURLY@9..10 "{"
-                        WHITESPACE@10..11 "\n"
-                        R_CURLY@11..12 "}"
-                  WHITESPACE@12..13 "\n"
-                  FN@13..25
-                    FN_KW@13..15 "fn"
-                    WHITESPACE@15..16 " "
-                    NAME@16..19
-                      IDENT@16..19 "bar"
-                    PARAM_LIST@19..21
-                      L_PAREN@19..20 "("
-                      R_PAREN@20..21 ")"
-                    WHITESPACE@21..22 " "
-                    BLOCK_EXPR@22..25
-                      STMT_LIST@22..25
-                        L_CURLY@22..23 "{"
-                        WHITESPACE@23..24 "\n"
-                        R_CURLY@24..25 "}"
-            "#]],
-        );
-    }
-}
diff --git a/src/tools/rust-analyzer/crates/ide/src/typing.rs b/src/tools/rust-analyzer/crates/ide/src/typing.rs
index d37318ff45706..8c9dd05145272 100644
--- a/src/tools/rust-analyzer/crates/ide/src/typing.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/typing.rs
@@ -51,16 +51,15 @@ struct ExtendedTextEdit {
 // - typing `{` in a use item adds a closing `}` in the right place
 // - typing `>` to complete a return type `->` will insert a whitespace after it
 //
-// VS Code::
+// #### VS Code
 //
 // Add the following to `settings.json`:
-// [source,json]
-// ----
+// ```json
 // "editor.formatOnType": true,
-// ----
+// ```
 //
-// image::https://user-images.githubusercontent.com/48062697/113166163-69758500-923a-11eb-81ee-eb33ec380399.gif[]
-// image::https://user-images.githubusercontent.com/48062697/113171066-105c2000-923f-11eb-87ab-f4a263346567.gif[]
+// ![On Typing Assists](https://user-images.githubusercontent.com/48062697/113166163-69758500-923a-11eb-81ee-eb33ec380399.gif)
+// ![On Typing Assists](https://user-images.githubusercontent.com/48062697/113171066-105c2000-923f-11eb-87ab-f4a263346567.gif)
 pub(crate) fn on_char_typed(
     db: &RootDatabase,
     position: FilePosition,
@@ -174,7 +173,7 @@ fn on_delimited_node_typed(
     kinds: &[fn(SyntaxKind) -> bool],
 ) -> Option {
     let t = reparsed.syntax().token_at_offset(offset).right_biased()?;
-    if t.prev_token().map_or(false, |t| t.kind().is_any_identifier()) {
+    if t.prev_token().is_some_and(|t| t.kind().is_any_identifier()) {
         return None;
     }
     let (filter, node) = t
@@ -436,14 +435,18 @@ mod tests {
         })
     }
 
-    fn type_char(char_typed: char, ra_fixture_before: &str, ra_fixture_after: &str) {
+    fn type_char(
+        char_typed: char,
+        #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+        #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+    ) {
         let actual = do_type_char(char_typed, ra_fixture_before)
             .unwrap_or_else(|| panic!("typing `{char_typed}` did nothing"));
 
         assert_eq_text!(ra_fixture_after, &actual);
     }
 
-    fn type_char_noop(char_typed: char, ra_fixture_before: &str) {
+    fn type_char_noop(char_typed: char, #[rust_analyzer::rust_fixture] ra_fixture_before: &str) {
         let file_change = do_type_char(char_typed, ra_fixture_before);
         assert_eq!(file_change, None)
     }
@@ -889,7 +892,7 @@ fn main() {
         type_char_noop(
             '{',
             r##"
-fn check_with(ra_fixture: &str, expect: Expect) {
+fn check_with(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let base = r#"
 enum E { T(), R$0, C }
 use self::E::X;
@@ -1191,7 +1194,7 @@ fn f(n: a<>b::::c) {}
         type_char_noop(
             '(',
             r##"
-fn check_with(ra_fixture: &str, expect: Expect) {
+fn check_with(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
     let base = r#"
 enum E { T(), R$0, C }
 use self::E::X;
diff --git a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs
index 773e352220ece..c6d1c283f4eca 100644
--- a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs
@@ -16,12 +16,12 @@ use ide_db::text_edit::TextEdit;
 
 // Feature: On Enter
 //
-// rust-analyzer can override kbd:[Enter] key to make it smarter:
+// rust-analyzer can override Enter key to make it smarter:
 //
-// - kbd:[Enter] inside triple-slash comments automatically inserts `///`
-// - kbd:[Enter] in the middle or after a trailing space in `//` inserts `//`
-// - kbd:[Enter] inside `//!` doc comments automatically inserts `//!`
-// - kbd:[Enter] after `{` indents contents and closing `}` of single-line block
+// - Enter inside triple-slash comments automatically inserts `///`
+// - Enter in the middle or after a trailing space in `//` inserts `//`
+// - Enter inside `//!` doc comments automatically inserts `//!`
+// - Enter after `{` indents contents and closing `}` of single-line block
 //
 // This action needs to be assigned to shortcut explicitly.
 //
@@ -29,29 +29,27 @@ use ide_db::text_edit::TextEdit;
 // Similarly, if rust-analyzer crashes or stops responding, `Enter` might not work.
 // In that case, you can still press `Shift-Enter` to insert a newline.
 //
-// VS Code::
+// #### VS Code
 //
 // Add the following to `keybindings.json`:
-// [source,json]
-// ----
+// ```json
 // {
 //   "key": "Enter",
 //   "command": "rust-analyzer.onEnter",
 //   "when": "editorTextFocus && !suggestWidgetVisible && editorLangId == rust"
 // }
-// ----
+// ````
 //
 // When using the Vim plugin:
-// [source,json]
-// ----
+// ```json
 // {
 //   "key": "Enter",
 //   "command": "rust-analyzer.onEnter",
 //   "when": "editorTextFocus && !suggestWidgetVisible && editorLangId == rust && vim.mode == 'Insert'"
 // }
-// ----
+// ````
 //
-// image::https://user-images.githubusercontent.com/48062697/113065578-04c21800-91b1-11eb-82b8-22b8c481e645.gif[]
+// ![On Enter](https://user-images.githubusercontent.com/48062697/113065578-04c21800-91b1-11eb-82b8-22b8c481e645.gif)
 pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option {
     let parse = db.parse(EditionedFileId::current_edition(position.file_id));
     let file = parse.tree();
@@ -208,7 +206,10 @@ mod tests {
         Some(actual)
     }
 
-    fn do_check(ra_fixture_before: &str, ra_fixture_after: &str) {
+    fn do_check(
+        #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+        #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+    ) {
         let ra_fixture_after = &trim_indent(ra_fixture_after);
         let actual = apply_on_enter(ra_fixture_before).unwrap();
         assert_eq_text!(ra_fixture_after, &actual);
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs
index 9ff099f479e7b..eb6eb7da1e90a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs
@@ -12,11 +12,9 @@ use triomphe::Arc;
 //
 // Only workspace crates are included, no crates.io dependencies or sysroot crates.
 //
-// |===
-// | Editor  | Action Name
-//
-// | VS Code | **rust-analyzer: View Crate Graph**
-// |===
+// | Editor  | Action Name |
+// |---------|-------------|
+// | VS Code | **rust-analyzer: View Crate Graph** |
 pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> Result {
     let crate_graph = db.crate_graph();
     let crates_to_render = crate_graph
@@ -86,7 +84,8 @@ impl<'a> dot::Labeller<'a, CrateId, Edge<'a>> for DotCrateGraph {
     }
 
     fn node_label(&'a self, n: &CrateId) -> LabelText<'a> {
-        let name = self.graph[*n].display_name.as_ref().map_or("(unnamed crate)", |name| name);
+        let name =
+            self.graph[*n].display_name.as_ref().map_or("(unnamed crate)", |name| name.as_str());
         LabelText::LabelStr(name.into())
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_hir.rs b/src/tools/rust-analyzer/crates/ide/src/view_hir.rs
index fe532f4cc55ee..bfdf9d0f3374e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_hir.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_hir.rs
@@ -4,12 +4,11 @@ use syntax::{algo::ancestors_at_offset, ast, AstNode};
 
 // Feature: View Hir
 //
-// |===
-// | Editor  | Action Name
-//
+// | Editor  | Action Name |
+// |---------|--------------|
 // | VS Code | **rust-analyzer: View Hir**
-// |===
-// image::https://user-images.githubusercontent.com/48062697/113065588-068bdb80-91b1-11eb-9a78-0b4ef1e972fb.gif[]
+//
+// ![View Hir](https://user-images.githubusercontent.com/48062697/113065588-068bdb80-91b1-11eb-9a78-0b4ef1e972fb.gif)
 pub(crate) fn view_hir(db: &RootDatabase, position: FilePosition) -> String {
     body_hir(db, position).unwrap_or_else(|| "Not inside a function body".to_owned())
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
index a6352b99d4f52..67c241cbb9153 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
@@ -6,11 +6,9 @@ use span::EditionedFileId;
 //
 // Displays the ItemTree of the currently open file, for debugging.
 //
-// |===
-// | Editor  | Action Name
-//
-// | VS Code | **rust-analyzer: Debug ItemTree**
-// |===
+// | Editor  | Action Name |
+// |---------|-------------|
+// | VS Code | **rust-analyzer: Debug ItemTree** |
 pub(crate) fn view_item_tree(db: &RootDatabase, file_id: FileId) -> String {
     let sema = Semantics::new(db);
     let file_id = sema
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs
index 830c39e21ea53..edb83bc4eac4c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs
@@ -75,11 +75,9 @@ impl FieldOrTupleIdx {
 //
 // Displays the recursive memory layout of a datatype.
 //
-// |===
-// | Editor  | Action Name
-//
-// | VS Code | **rust-analyzer: View Memory Layout**
-// |===
+// | Editor  | Action Name |
+// |---------|-------------|
+// | VS Code | **rust-analyzer: View Memory Layout** |
 pub(crate) fn view_memory_layout(
     db: &RootDatabase,
     position: FilePosition,
@@ -220,7 +218,9 @@ mod tests {
     use crate::fixture;
     use expect_test::expect;
 
-    fn make_memory_layout(ra_fixture: &str) -> Option {
+    fn make_memory_layout(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    ) -> Option {
         let (analysis, position, _) = fixture::annotations(ra_fixture);
 
         view_memory_layout(&analysis.db, position)
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_mir.rs b/src/tools/rust-analyzer/crates/ide/src/view_mir.rs
index 7a228375d5e97..aa4ff64a819e1 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_mir.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_mir.rs
@@ -4,11 +4,9 @@ use syntax::{algo::ancestors_at_offset, ast, AstNode};
 
 // Feature: View Mir
 //
-// |===
-// | Editor  | Action Name
-//
+// | Editor  | Action Name |
+// |---------|-------------|
 // | VS Code | **rust-analyzer: View Mir**
-// |===
 pub(crate) fn view_mir(db: &RootDatabase, position: FilePosition) -> String {
     body_mir(db, position).unwrap_or_else(|| "Not inside a function body".to_owned())
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs b/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs
new file mode 100644
index 0000000000000..407720864bfdb
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs
@@ -0,0 +1,255 @@
+use hir::Semantics;
+use ide_db::{
+    line_index::{LineCol, LineIndex},
+    FileId, LineIndexDatabase, RootDatabase,
+};
+use span::{TextRange, TextSize};
+use stdx::format_to;
+use syntax::{
+    ast::{self, IsString},
+    AstNode, AstToken, NodeOrToken, SourceFile, SyntaxNode, SyntaxToken, WalkEvent,
+};
+use triomphe::Arc;
+
+// Feature: Show Syntax Tree
+//
+// Shows a tree view with the syntax tree of the current file
+//
+// | Editor  | Panel Name |
+// |---------|-------------|
+// | VS Code | **Rust Syntax Tree** |
+pub(crate) fn view_syntax_tree(db: &RootDatabase, file_id: FileId) -> String {
+    let sema = Semantics::new(db);
+    let line_index = db.line_index(file_id);
+    let parse = sema.parse_guess_edition(file_id);
+
+    let ctx = SyntaxTreeCtx { line_index, in_string: None };
+
+    syntax_node_to_json(parse.syntax(), &ctx)
+}
+
+fn syntax_node_to_json(node: &SyntaxNode, ctx: &SyntaxTreeCtx) -> String {
+    let mut result = String::new();
+    for event in node.preorder_with_tokens() {
+        match event {
+            WalkEvent::Enter(it) => {
+                let kind = it.kind();
+                let (text_range, inner_range_str) = match &ctx.in_string {
+                    Some(in_string) => {
+                        let start_pos = TextPosition::new(&ctx.line_index, it.text_range().start());
+                        let end_pos = TextPosition::new(&ctx.line_index, it.text_range().end());
+
+                        let inner_start: u32 = it.text_range().start().into();
+                        let inner_end: u32 = it.text_range().start().into();
+
+                        let mut true_start = inner_start + in_string.offset;
+                        let mut true_end = inner_end + in_string.offset;
+                        for pos in &in_string.marker_positions {
+                            if *pos >= inner_end {
+                                break;
+                            }
+
+                            // We conditionally add to true_start in case
+                            // the marker is between the start and end.
+                            true_start += 2 * (*pos < inner_start) as u32;
+                            true_end += 2;
+                        }
+
+                        let true_range = TextRange::new(true_start.into(), true_end.into());
+
+                        (true_range, format!(r#","istart":{start_pos},"iend":{end_pos}"#,))
+                    }
+                    None => (it.text_range(), "".to_owned()),
+                };
+
+                let start = TextPosition::new(&ctx.line_index, text_range.start());
+                let end = TextPosition::new(&ctx.line_index, text_range.end());
+
+                match it {
+                    NodeOrToken::Node(_) => {
+                        format_to!(
+                            result,
+                            r#"{{"type":"Node","kind":"{kind:?}","start":{start},"end":{end}{inner_range_str},"children":["#
+                        );
+                    }
+                    NodeOrToken::Token(token) => {
+                        let comma = if token.next_sibling_or_token().is_some() { "," } else { "" };
+                        match parse_rust_string(token, ctx) {
+                            Some(parsed) => {
+                                format_to!(
+                                    result,
+                                    r#"{{"type":"Node","kind":"{kind:?}","start":{start},"end":{end}{inner_range_str},"children":[{parsed}]}}{comma}"#
+                                );
+                            }
+                            None => format_to!(
+                                result,
+                                r#"{{"type":"Token","kind":"{kind:?}","start":{start},"end":{end}{inner_range_str}}}{comma}"#
+                            ),
+                        }
+                    }
+                }
+            }
+            WalkEvent::Leave(it) => match it {
+                NodeOrToken::Node(node) => {
+                    let comma = if node.next_sibling_or_token().is_some() { "," } else { "" };
+                    format_to!(result, "]}}{comma}")
+                }
+                NodeOrToken::Token(_) => (),
+            },
+        }
+    }
+
+    result
+}
+
+struct TextPosition {
+    offset: TextSize,
+    line: u32,
+    col: u32,
+}
+
+impl std::fmt::Display for TextPosition {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "[{:?},{},{}]", self.offset, self.line, self.col)
+    }
+}
+
+impl TextPosition {
+    pub(crate) fn new(line_index: &LineIndex, offset: TextSize) -> Self {
+        let LineCol { line, col } = line_index.line_col(offset);
+        Self { offset, line, col }
+    }
+}
+
+fn parse_rust_string(token: SyntaxToken, ctx: &SyntaxTreeCtx) -> Option {
+    let string_node = ast::String::cast(token)?;
+    let text = string_node.value().ok()?;
+
+    let mut trim_result = String::new();
+    let mut marker_positions = Vec::new();
+    let mut skipped = 0;
+    let mut last_end = 0;
+    for (start, part) in text.match_indices("$0") {
+        marker_positions.push((start - skipped) as u32);
+        trim_result.push_str(&text[last_end..start]);
+        skipped += part.len();
+        last_end = start + part.len();
+    }
+    trim_result.push_str(&text[last_end..text.len()]);
+
+    let parsed = SourceFile::parse(&trim_result, span::Edition::CURRENT);
+
+    if !parsed.errors().is_empty() {
+        return None;
+    }
+
+    let node: &SyntaxNode = &parsed.syntax_node();
+
+    if node.children().count() == 0 {
+        // C'mon, you should have at least one node other than SOURCE_FILE
+        return None;
+    }
+
+    let ctx = SyntaxTreeCtx {
+        line_index: ctx.line_index.clone(),
+        in_string: Some(InStringCtx {
+            offset: string_node.text_range_between_quotes()?.start().into(),
+            marker_positions,
+        }),
+    };
+
+    Some(syntax_node_to_json(node, &ctx))
+}
+
+struct SyntaxTreeCtx {
+    line_index: Arc,
+    in_string: Option,
+}
+
+struct InStringCtx {
+    offset: u32,
+    marker_positions: Vec,
+}
+
+#[cfg(test)]
+mod tests {
+    use expect_test::expect;
+
+    use crate::fixture;
+
+    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: expect_test::Expect) {
+        let (analysis, file_id) = fixture::file(ra_fixture);
+        let syn = analysis.view_syntax_tree(file_id).unwrap();
+        expect.assert_eq(&syn)
+    }
+
+    #[test]
+    fn view_syntax_tree() {
+        // Basic syntax
+        check(
+            r#"fn foo() {}"#,
+            expect![[
+                r#"{"type":"Node","kind":"SOURCE_FILE","start":[0,0,0],"end":[11,0,11],"children":[{"type":"Node","kind":"FN","start":[0,0,0],"end":[11,0,11],"children":[{"type":"Token","kind":"FN_KW","start":[0,0,0],"end":[2,0,2]},{"type":"Token","kind":"WHITESPACE","start":[2,0,2],"end":[3,0,3]},{"type":"Node","kind":"NAME","start":[3,0,3],"end":[6,0,6],"children":[{"type":"Token","kind":"IDENT","start":[3,0,3],"end":[6,0,6]}]},{"type":"Node","kind":"PARAM_LIST","start":[6,0,6],"end":[8,0,8],"children":[{"type":"Token","kind":"L_PAREN","start":[6,0,6],"end":[7,0,7]},{"type":"Token","kind":"R_PAREN","start":[7,0,7],"end":[8,0,8]}]},{"type":"Token","kind":"WHITESPACE","start":[8,0,8],"end":[9,0,9]},{"type":"Node","kind":"BLOCK_EXPR","start":[9,0,9],"end":[11,0,11],"children":[{"type":"Node","kind":"STMT_LIST","start":[9,0,9],"end":[11,0,11],"children":[{"type":"Token","kind":"L_CURLY","start":[9,0,9],"end":[10,0,10]},{"type":"Token","kind":"R_CURLY","start":[10,0,10],"end":[11,0,11]}]}]}]}]}"#
+            ]],
+        );
+
+        check(
+            r#"
+fn test() {
+    assert!("
+    fn foo() {
+    }
+    ", "");
+}"#,
+            expect![[
+                r#"{"type":"Node","kind":"SOURCE_FILE","start":[0,0,0],"end":[60,5,1],"children":[{"type":"Node","kind":"FN","start":[0,0,0],"end":[60,5,1],"children":[{"type":"Token","kind":"FN_KW","start":[0,0,0],"end":[2,0,2]},{"type":"Token","kind":"WHITESPACE","start":[2,0,2],"end":[3,0,3]},{"type":"Node","kind":"NAME","start":[3,0,3],"end":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[3,0,3],"end":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[7,0,7],"end":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[7,0,7],"end":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[8,0,8],"end":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[9,0,9],"end":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[10,0,10],"end":[60,5,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[10,0,10],"end":[60,5,1],"children":[{"type":"Token","kind":"L_CURLY","start":[10,0,10],"end":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[11,0,11],"end":[16,1,4]},{"type":"Node","kind":"EXPR_STMT","start":[16,1,4],"end":[58,4,11],"children":[{"type":"Node","kind":"MACRO_EXPR","start":[16,1,4],"end":[57,4,10],"children":[{"type":"Node","kind":"MACRO_CALL","start":[16,1,4],"end":[57,4,10],"children":[{"type":"Node","kind":"PATH","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"PATH_SEGMENT","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"NAME_REF","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Token","kind":"IDENT","start":[16,1,4],"end":[22,1,10]}]}]}]},{"type":"Token","kind":"BANG","start":[22,1,10],"end":[23,1,11]},{"type":"Node","kind":"TOKEN_TREE","start":[23,1,11],"end":[57,4,10],"children":[{"type":"Token","kind":"L_PAREN","start":[23,1,11],"end":[24,1,12]},{"type":"Node","kind":"STRING","start":[24,1,12],"end":[52,4,5],"children":[{"type":"Node","kind":"SOURCE_FILE","start":[25,1,13],"end":[25,1,13],"istart":[0,0,0],"iend":[26,2,0],"children":[{"type":"Token","kind":"WHITESPACE","start":[25,1,13],"end":[25,1,13],"istart":[0,0,0],"iend":[5,0,5]},{"type":"Node","kind":"FN","start":[30,2,4],"end":[30,2,4],"istart":[5,0,5],"iend":[21,1,9],"children":[{"type":"Token","kind":"FN_KW","start":[30,2,4],"end":[30,2,4],"istart":[5,0,5],"iend":[7,0,7]},{"type":"Token","kind":"WHITESPACE","start":[32,2,6],"end":[32,2,6],"istart":[7,0,7],"iend":[8,0,8]},{"type":"Node","kind":"NAME","start":[33,2,7],"end":[33,2,7],"istart":[8,0,8],"iend":[11,0,11],"children":[{"type":"Token","kind":"IDENT","start":[33,2,7],"end":[33,2,7],"istart":[8,0,8],"iend":[11,0,11]}]},{"type":"Node","kind":"PARAM_LIST","start":[36,2,10],"end":[36,2,10],"istart":[11,0,11],"iend":[13,1,1],"children":[{"type":"Token","kind":"L_PAREN","start":[36,2,10],"end":[36,2,10],"istart":[11,0,11],"iend":[12,1,0]},{"type":"Token","kind":"R_PAREN","start":[37,2,11],"end":[37,2,11],"istart":[12,1,0],"iend":[13,1,1]}]},{"type":"Token","kind":"WHITESPACE","start":[38,2,12],"end":[38,2,12],"istart":[13,1,1],"iend":[14,1,2]},{"type":"Node","kind":"BLOCK_EXPR","start":[39,2,13],"end":[39,2,13],"istart":[14,1,2],"iend":[21,1,9],"children":[{"type":"Node","kind":"STMT_LIST","start":[39,2,13],"end":[39,2,13],"istart":[14,1,2],"iend":[21,1,9],"children":[{"type":"Token","kind":"L_CURLY","start":[39,2,13],"end":[39,2,13],"istart":[14,1,2],"iend":[15,1,3]},{"type":"Token","kind":"WHITESPACE","start":[40,2,14],"end":[40,2,14],"istart":[15,1,3],"iend":[20,1,8]},{"type":"Token","kind":"R_CURLY","start":[45,3,4],"end":[45,3,4],"istart":[20,1,8],"iend":[21,1,9]}]}]}]},{"type":"Token","kind":"WHITESPACE","start":[46,3,5],"end":[46,3,5],"istart":[21,1,9],"iend":[26,2,0]}]}]},{"type":"Token","kind":"COMMA","start":[52,4,5],"end":[53,4,6]},{"type":"Token","kind":"WHITESPACE","start":[53,4,6],"end":[54,4,7]},{"type":"Token","kind":"STRING","start":[54,4,7],"end":[56,4,9]},{"type":"Token","kind":"R_PAREN","start":[56,4,9],"end":[57,4,10]}]}]}]},{"type":"Token","kind":"SEMICOLON","start":[57,4,10],"end":[58,4,11]}]},{"type":"Token","kind":"WHITESPACE","start":[58,4,11],"end":[59,5,0]},{"type":"Token","kind":"R_CURLY","start":[59,5,0],"end":[60,5,1]}]}]}]}]}"#
+            ]],
+        )
+    }
+
+    #[test]
+    fn view_syntax_tree_inside_string() {
+        check(
+            r#"fn test() {
+    assert!("
+$0fn foo() {
+}$0
+fn bar() {
+}
+    ", "");
+}"#,
+            expect![[
+                r#"{"type":"Node","kind":"SOURCE_FILE","start":[0,0,0],"end":[65,7,1],"children":[{"type":"Node","kind":"FN","start":[0,0,0],"end":[65,7,1],"children":[{"type":"Token","kind":"FN_KW","start":[0,0,0],"end":[2,0,2]},{"type":"Token","kind":"WHITESPACE","start":[2,0,2],"end":[3,0,3]},{"type":"Node","kind":"NAME","start":[3,0,3],"end":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[3,0,3],"end":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[7,0,7],"end":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[7,0,7],"end":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[8,0,8],"end":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[9,0,9],"end":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[10,0,10],"end":[65,7,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[10,0,10],"end":[65,7,1],"children":[{"type":"Token","kind":"L_CURLY","start":[10,0,10],"end":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[11,0,11],"end":[16,1,4]},{"type":"Node","kind":"EXPR_STMT","start":[16,1,4],"end":[63,6,11],"children":[{"type":"Node","kind":"MACRO_EXPR","start":[16,1,4],"end":[62,6,10],"children":[{"type":"Node","kind":"MACRO_CALL","start":[16,1,4],"end":[62,6,10],"children":[{"type":"Node","kind":"PATH","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"PATH_SEGMENT","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"NAME_REF","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Token","kind":"IDENT","start":[16,1,4],"end":[22,1,10]}]}]}]},{"type":"Token","kind":"BANG","start":[22,1,10],"end":[23,1,11]},{"type":"Node","kind":"TOKEN_TREE","start":[23,1,11],"end":[62,6,10],"children":[{"type":"Token","kind":"L_PAREN","start":[23,1,11],"end":[24,1,12]},{"type":"Node","kind":"STRING","start":[24,1,12],"end":[57,6,5],"children":[{"type":"Node","kind":"SOURCE_FILE","start":[25,1,13],"end":[25,1,13],"istart":[0,0,0],"iend":[31,2,5],"children":[{"type":"Token","kind":"WHITESPACE","start":[25,1,13],"end":[25,1,13],"istart":[0,0,0],"iend":[1,0,1]},{"type":"Node","kind":"FN","start":[26,2,0],"end":[26,2,0],"istart":[1,0,1],"iend":[13,1,1],"children":[{"type":"Token","kind":"FN_KW","start":[26,2,0],"end":[26,2,0],"istart":[1,0,1],"iend":[3,0,3]},{"type":"Token","kind":"WHITESPACE","start":[28,2,2],"end":[28,2,2],"istart":[3,0,3],"iend":[4,0,4]},{"type":"Node","kind":"NAME","start":[29,2,3],"end":[29,2,3],"istart":[4,0,4],"iend":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[29,2,3],"end":[29,2,3],"istart":[4,0,4],"iend":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[32,2,6],"end":[32,2,6],"istart":[7,0,7],"iend":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[32,2,6],"end":[32,2,6],"istart":[7,0,7],"iend":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[33,2,7],"end":[33,2,7],"istart":[8,0,8],"iend":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[34,2,8],"end":[34,2,8],"istart":[9,0,9],"iend":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[35,2,9],"end":[35,2,9],"istart":[10,0,10],"iend":[13,1,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[35,2,9],"end":[35,2,9],"istart":[10,0,10],"iend":[13,1,1],"children":[{"type":"Token","kind":"L_CURLY","start":[35,2,9],"end":[35,2,9],"istart":[10,0,10],"iend":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[36,2,10],"end":[36,2,10],"istart":[11,0,11],"iend":[12,1,0]},{"type":"Token","kind":"R_CURLY","start":[37,3,0],"end":[37,3,0],"istart":[12,1,0],"iend":[13,1,1]}]}]}]},{"type":"Token","kind":"WHITESPACE","start":[38,3,1],"end":[38,3,1],"istart":[13,1,1],"iend":[14,1,2]},{"type":"Node","kind":"FN","start":[39,4,0],"end":[39,4,0],"istart":[14,1,2],"iend":[26,2,0],"children":[{"type":"Token","kind":"FN_KW","start":[39,4,0],"end":[39,4,0],"istart":[14,1,2],"iend":[16,1,4]},{"type":"Token","kind":"WHITESPACE","start":[41,4,2],"end":[41,4,2],"istart":[16,1,4],"iend":[17,1,5]},{"type":"Node","kind":"NAME","start":[42,4,3],"end":[42,4,3],"istart":[17,1,5],"iend":[20,1,8],"children":[{"type":"Token","kind":"IDENT","start":[42,4,3],"end":[42,4,3],"istart":[17,1,5],"iend":[20,1,8]}]},{"type":"Node","kind":"PARAM_LIST","start":[45,4,6],"end":[45,4,6],"istart":[20,1,8],"iend":[22,1,10],"children":[{"type":"Token","kind":"L_PAREN","start":[45,4,6],"end":[45,4,6],"istart":[20,1,8],"iend":[21,1,9]},{"type":"Token","kind":"R_PAREN","start":[46,4,7],"end":[46,4,7],"istart":[21,1,9],"iend":[22,1,10]}]},{"type":"Token","kind":"WHITESPACE","start":[47,4,8],"end":[47,4,8],"istart":[22,1,10],"iend":[23,1,11]},{"type":"Node","kind":"BLOCK_EXPR","start":[48,4,9],"end":[48,4,9],"istart":[23,1,11],"iend":[26,2,0],"children":[{"type":"Node","kind":"STMT_LIST","start":[48,4,9],"end":[48,4,9],"istart":[23,1,11],"iend":[26,2,0],"children":[{"type":"Token","kind":"L_CURLY","start":[48,4,9],"end":[48,4,9],"istart":[23,1,11],"iend":[24,1,12]},{"type":"Token","kind":"WHITESPACE","start":[49,4,10],"end":[49,4,10],"istart":[24,1,12],"iend":[25,1,13]},{"type":"Token","kind":"R_CURLY","start":[50,5,0],"end":[50,5,0],"istart":[25,1,13],"iend":[26,2,0]}]}]}]},{"type":"Token","kind":"WHITESPACE","start":[51,5,1],"end":[51,5,1],"istart":[26,2,0],"iend":[31,2,5]}]}]},{"type":"Token","kind":"COMMA","start":[57,6,5],"end":[58,6,6]},{"type":"Token","kind":"WHITESPACE","start":[58,6,6],"end":[59,6,7]},{"type":"Token","kind":"STRING","start":[59,6,7],"end":[61,6,9]},{"type":"Token","kind":"R_PAREN","start":[61,6,9],"end":[62,6,10]}]}]}]},{"type":"Token","kind":"SEMICOLON","start":[62,6,10],"end":[63,6,11]}]},{"type":"Token","kind":"WHITESPACE","start":[63,6,11],"end":[64,7,0]},{"type":"Token","kind":"R_CURLY","start":[64,7,0],"end":[65,7,1]}]}]}]}]}"#
+            ]],
+        );
+
+        // With a raw string
+        check(
+            r###"fn test() {
+    assert!(r#"
+$0fn foo() {
+}$0
+fn bar() {
+}
+    "#, "");
+}"###,
+            expect![[
+                r#"{"type":"Node","kind":"SOURCE_FILE","start":[0,0,0],"end":[68,7,1],"children":[{"type":"Node","kind":"FN","start":[0,0,0],"end":[68,7,1],"children":[{"type":"Token","kind":"FN_KW","start":[0,0,0],"end":[2,0,2]},{"type":"Token","kind":"WHITESPACE","start":[2,0,2],"end":[3,0,3]},{"type":"Node","kind":"NAME","start":[3,0,3],"end":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[3,0,3],"end":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[7,0,7],"end":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[7,0,7],"end":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[8,0,8],"end":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[9,0,9],"end":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[10,0,10],"end":[68,7,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[10,0,10],"end":[68,7,1],"children":[{"type":"Token","kind":"L_CURLY","start":[10,0,10],"end":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[11,0,11],"end":[16,1,4]},{"type":"Node","kind":"EXPR_STMT","start":[16,1,4],"end":[66,6,12],"children":[{"type":"Node","kind":"MACRO_EXPR","start":[16,1,4],"end":[65,6,11],"children":[{"type":"Node","kind":"MACRO_CALL","start":[16,1,4],"end":[65,6,11],"children":[{"type":"Node","kind":"PATH","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"PATH_SEGMENT","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"NAME_REF","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Token","kind":"IDENT","start":[16,1,4],"end":[22,1,10]}]}]}]},{"type":"Token","kind":"BANG","start":[22,1,10],"end":[23,1,11]},{"type":"Node","kind":"TOKEN_TREE","start":[23,1,11],"end":[65,6,11],"children":[{"type":"Token","kind":"L_PAREN","start":[23,1,11],"end":[24,1,12]},{"type":"Node","kind":"STRING","start":[24,1,12],"end":[60,6,6],"children":[{"type":"Node","kind":"SOURCE_FILE","start":[27,1,15],"end":[27,1,15],"istart":[0,0,0],"iend":[31,2,3],"children":[{"type":"Token","kind":"WHITESPACE","start":[27,1,15],"end":[27,1,15],"istart":[0,0,0],"iend":[1,0,1]},{"type":"Node","kind":"FN","start":[28,2,0],"end":[28,2,0],"istart":[1,0,1],"iend":[13,1,1],"children":[{"type":"Token","kind":"FN_KW","start":[28,2,0],"end":[28,2,0],"istart":[1,0,1],"iend":[3,0,3]},{"type":"Token","kind":"WHITESPACE","start":[30,2,2],"end":[30,2,2],"istart":[3,0,3],"iend":[4,0,4]},{"type":"Node","kind":"NAME","start":[31,2,3],"end":[31,2,3],"istart":[4,0,4],"iend":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[31,2,3],"end":[31,2,3],"istart":[4,0,4],"iend":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[34,2,6],"end":[34,2,6],"istart":[7,0,7],"iend":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[34,2,6],"end":[34,2,6],"istart":[7,0,7],"iend":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[35,2,7],"end":[35,2,7],"istart":[8,0,8],"iend":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[36,2,8],"end":[36,2,8],"istart":[9,0,9],"iend":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[37,2,9],"end":[37,2,9],"istart":[10,0,10],"iend":[13,1,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[37,2,9],"end":[37,2,9],"istart":[10,0,10],"iend":[13,1,1],"children":[{"type":"Token","kind":"L_CURLY","start":[37,2,9],"end":[37,2,9],"istart":[10,0,10],"iend":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[38,2,10],"end":[38,2,10],"istart":[11,0,11],"iend":[12,1,0]},{"type":"Token","kind":"R_CURLY","start":[39,3,0],"end":[39,3,0],"istart":[12,1,0],"iend":[13,1,1]}]}]}]},{"type":"Token","kind":"WHITESPACE","start":[40,3,1],"end":[40,3,1],"istart":[13,1,1],"iend":[14,1,2]},{"type":"Node","kind":"FN","start":[41,4,0],"end":[41,4,0],"istart":[14,1,2],"iend":[26,1,14],"children":[{"type":"Token","kind":"FN_KW","start":[41,4,0],"end":[41,4,0],"istart":[14,1,2],"iend":[16,1,4]},{"type":"Token","kind":"WHITESPACE","start":[43,4,2],"end":[43,4,2],"istart":[16,1,4],"iend":[17,1,5]},{"type":"Node","kind":"NAME","start":[44,4,3],"end":[44,4,3],"istart":[17,1,5],"iend":[20,1,8],"children":[{"type":"Token","kind":"IDENT","start":[44,4,3],"end":[44,4,3],"istart":[17,1,5],"iend":[20,1,8]}]},{"type":"Node","kind":"PARAM_LIST","start":[47,4,6],"end":[47,4,6],"istart":[20,1,8],"iend":[22,1,10],"children":[{"type":"Token","kind":"L_PAREN","start":[47,4,6],"end":[47,4,6],"istart":[20,1,8],"iend":[21,1,9]},{"type":"Token","kind":"R_PAREN","start":[48,4,7],"end":[48,4,7],"istart":[21,1,9],"iend":[22,1,10]}]},{"type":"Token","kind":"WHITESPACE","start":[49,4,8],"end":[49,4,8],"istart":[22,1,10],"iend":[23,1,11]},{"type":"Node","kind":"BLOCK_EXPR","start":[50,4,9],"end":[50,4,9],"istart":[23,1,11],"iend":[26,1,14],"children":[{"type":"Node","kind":"STMT_LIST","start":[50,4,9],"end":[50,4,9],"istart":[23,1,11],"iend":[26,1,14],"children":[{"type":"Token","kind":"L_CURLY","start":[50,4,9],"end":[50,4,9],"istart":[23,1,11],"iend":[24,1,12]},{"type":"Token","kind":"WHITESPACE","start":[51,4,10],"end":[51,4,10],"istart":[24,1,12],"iend":[25,1,13]},{"type":"Token","kind":"R_CURLY","start":[52,5,0],"end":[52,5,0],"istart":[25,1,13],"iend":[26,1,14]}]}]}]},{"type":"Token","kind":"WHITESPACE","start":[53,5,1],"end":[53,5,1],"istart":[26,1,14],"iend":[31,2,3]}]}]},{"type":"Token","kind":"COMMA","start":[60,6,6],"end":[61,6,7]},{"type":"Token","kind":"WHITESPACE","start":[61,6,7],"end":[62,6,8]},{"type":"Token","kind":"STRING","start":[62,6,8],"end":[64,6,10]},{"type":"Token","kind":"R_PAREN","start":[64,6,10],"end":[65,6,11]}]}]}]},{"type":"Token","kind":"SEMICOLON","start":[65,6,11],"end":[66,6,12]}]},{"type":"Token","kind":"WHITESPACE","start":[66,6,12],"end":[67,7,0]},{"type":"Token","kind":"R_CURLY","start":[67,7,0],"end":[68,7,1]}]}]}]}]}"#
+            ]],
+        );
+
+        // With a raw string
+        check(
+            r###"fn test() {
+    assert!(r$0#"
+fn foo() {
+}
+fn bar() {
+}"$0#, "");
+}"###,
+            expect![[
+                r#"{"type":"Node","kind":"SOURCE_FILE","start":[0,0,0],"end":[63,6,1],"children":[{"type":"Node","kind":"FN","start":[0,0,0],"end":[63,6,1],"children":[{"type":"Token","kind":"FN_KW","start":[0,0,0],"end":[2,0,2]},{"type":"Token","kind":"WHITESPACE","start":[2,0,2],"end":[3,0,3]},{"type":"Node","kind":"NAME","start":[3,0,3],"end":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[3,0,3],"end":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[7,0,7],"end":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[7,0,7],"end":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[8,0,8],"end":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[9,0,9],"end":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[10,0,10],"end":[63,6,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[10,0,10],"end":[63,6,1],"children":[{"type":"Token","kind":"L_CURLY","start":[10,0,10],"end":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[11,0,11],"end":[16,1,4]},{"type":"Node","kind":"EXPR_STMT","start":[16,1,4],"end":[61,5,9],"children":[{"type":"Node","kind":"MACRO_EXPR","start":[16,1,4],"end":[60,5,8],"children":[{"type":"Node","kind":"MACRO_CALL","start":[16,1,4],"end":[60,5,8],"children":[{"type":"Node","kind":"PATH","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"PATH_SEGMENT","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"NAME_REF","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Token","kind":"IDENT","start":[16,1,4],"end":[22,1,10]}]}]}]},{"type":"Token","kind":"BANG","start":[22,1,10],"end":[23,1,11]},{"type":"Node","kind":"TOKEN_TREE","start":[23,1,11],"end":[60,5,8],"children":[{"type":"Token","kind":"L_PAREN","start":[23,1,11],"end":[24,1,12]},{"type":"Node","kind":"STRING","start":[24,1,12],"end":[55,5,3],"children":[{"type":"Node","kind":"SOURCE_FILE","start":[27,1,15],"end":[27,1,15],"istart":[0,0,0],"iend":[26,1,14],"children":[{"type":"Token","kind":"WHITESPACE","start":[27,1,15],"end":[27,1,15],"istart":[0,0,0],"iend":[1,0,1]},{"type":"Node","kind":"FN","start":[28,2,0],"end":[28,2,0],"istart":[1,0,1],"iend":[13,1,1],"children":[{"type":"Token","kind":"FN_KW","start":[28,2,0],"end":[28,2,0],"istart":[1,0,1],"iend":[3,0,3]},{"type":"Token","kind":"WHITESPACE","start":[30,2,2],"end":[30,2,2],"istart":[3,0,3],"iend":[4,0,4]},{"type":"Node","kind":"NAME","start":[31,2,3],"end":[31,2,3],"istart":[4,0,4],"iend":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[31,2,3],"end":[31,2,3],"istart":[4,0,4],"iend":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[34,2,6],"end":[34,2,6],"istart":[7,0,7],"iend":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[34,2,6],"end":[34,2,6],"istart":[7,0,7],"iend":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[35,2,7],"end":[35,2,7],"istart":[8,0,8],"iend":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[36,2,8],"end":[36,2,8],"istart":[9,0,9],"iend":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[37,2,9],"end":[37,2,9],"istart":[10,0,10],"iend":[13,1,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[37,2,9],"end":[37,2,9],"istart":[10,0,10],"iend":[13,1,1],"children":[{"type":"Token","kind":"L_CURLY","start":[37,2,9],"end":[37,2,9],"istart":[10,0,10],"iend":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[38,2,10],"end":[38,2,10],"istart":[11,0,11],"iend":[12,1,0]},{"type":"Token","kind":"R_CURLY","start":[39,3,0],"end":[39,3,0],"istart":[12,1,0],"iend":[13,1,1]}]}]}]},{"type":"Token","kind":"WHITESPACE","start":[40,3,1],"end":[40,3,1],"istart":[13,1,1],"iend":[14,1,2]},{"type":"Node","kind":"FN","start":[41,4,0],"end":[41,4,0],"istart":[14,1,2],"iend":[26,1,14],"children":[{"type":"Token","kind":"FN_KW","start":[41,4,0],"end":[41,4,0],"istart":[14,1,2],"iend":[16,1,4]},{"type":"Token","kind":"WHITESPACE","start":[43,4,2],"end":[43,4,2],"istart":[16,1,4],"iend":[17,1,5]},{"type":"Node","kind":"NAME","start":[44,4,3],"end":[44,4,3],"istart":[17,1,5],"iend":[20,1,8],"children":[{"type":"Token","kind":"IDENT","start":[44,4,3],"end":[44,4,3],"istart":[17,1,5],"iend":[20,1,8]}]},{"type":"Node","kind":"PARAM_LIST","start":[47,4,6],"end":[47,4,6],"istart":[20,1,8],"iend":[22,1,10],"children":[{"type":"Token","kind":"L_PAREN","start":[47,4,6],"end":[47,4,6],"istart":[20,1,8],"iend":[21,1,9]},{"type":"Token","kind":"R_PAREN","start":[48,4,7],"end":[48,4,7],"istart":[21,1,9],"iend":[22,1,10]}]},{"type":"Token","kind":"WHITESPACE","start":[49,4,8],"end":[49,4,8],"istart":[22,1,10],"iend":[23,1,11]},{"type":"Node","kind":"BLOCK_EXPR","start":[50,4,9],"end":[50,4,9],"istart":[23,1,11],"iend":[26,1,14],"children":[{"type":"Node","kind":"STMT_LIST","start":[50,4,9],"end":[50,4,9],"istart":[23,1,11],"iend":[26,1,14],"children":[{"type":"Token","kind":"L_CURLY","start":[50,4,9],"end":[50,4,9],"istart":[23,1,11],"iend":[24,1,12]},{"type":"Token","kind":"WHITESPACE","start":[51,4,10],"end":[51,4,10],"istart":[24,1,12],"iend":[25,1,13]},{"type":"Token","kind":"R_CURLY","start":[52,5,0],"end":[52,5,0],"istart":[25,1,13],"iend":[26,1,14]}]}]}]}]}]},{"type":"Token","kind":"COMMA","start":[55,5,3],"end":[56,5,4]},{"type":"Token","kind":"WHITESPACE","start":[56,5,4],"end":[57,5,5]},{"type":"Token","kind":"STRING","start":[57,5,5],"end":[59,5,7]},{"type":"Token","kind":"R_PAREN","start":[59,5,7],"end":[60,5,8]}]}]}]},{"type":"Token","kind":"SEMICOLON","start":[60,5,8],"end":[61,5,9]}]},{"type":"Token","kind":"WHITESPACE","start":[61,5,9],"end":[62,6,0]},{"type":"Token","kind":"R_CURLY","start":[62,6,0],"end":[63,6,1]}]}]}]}]}"#
+            ]],
+        );
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/intern/Cargo.toml b/src/tools/rust-analyzer/crates/intern/Cargo.toml
index 5e7ee54c6af77..c0358ef929b4d 100644
--- a/src/tools/rust-analyzer/crates/intern/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/intern/Cargo.toml
@@ -19,7 +19,6 @@ dashmap.workspace = true
 hashbrown.workspace = true
 rustc-hash.workspace = true
 triomphe.workspace = true
-sptr = "0.3.2"
 
 [lints]
 workspace = true
diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol.rs b/src/tools/rust-analyzer/crates/intern/src/symbol.rs
index 200b14027f804..b3bf285edfb11 100644
--- a/src/tools/rust-analyzer/crates/intern/src/symbol.rs
+++ b/src/tools/rust-analyzer/crates/intern/src/symbol.rs
@@ -13,7 +13,6 @@ use std::{
 use dashmap::{DashMap, SharedValue};
 use hashbrown::{hash_map::RawEntryMut, HashMap};
 use rustc_hash::FxHasher;
-use sptr::Strict;
 use triomphe::Arc;
 
 pub mod symbols;
@@ -84,7 +83,7 @@ impl TaggedArcPtr {
     #[inline]
     pub(crate) unsafe fn try_as_arc_owned(self) -> Option>>> {
         // Unpack the tag from the alignment niche
-        let tag = Strict::addr(self.packed.as_ptr()) & Self::BOOL_BITS;
+        let tag = self.packed.as_ptr().addr() & Self::BOOL_BITS;
         if tag != 0 {
             // Safety: We checked that the tag is non-zero -> true, so we are pointing to the data offset of an `Arc`
             Some(ManuallyDrop::new(unsafe {
@@ -99,40 +98,18 @@ impl TaggedArcPtr {
     fn pack_arc(ptr: NonNull<*const str>) -> NonNull<*const str> {
         let packed_tag = true as usize;
 
-        // can't use this strict provenance stuff here due to trait methods not being const
-        // unsafe {
-        //     // Safety: The pointer is derived from a non-null
-        //     NonNull::new_unchecked(Strict::map_addr(ptr.as_ptr(), |addr| {
-        //         // Safety:
-        //         // - The pointer is `NonNull` => it's address is `NonZero`
-        //         // - `P::BITS` least significant bits are always zero (`Pointer` contract)
-        //         // - `T::BITS <= P::BITS` (from `Self::ASSERTION`)
-        //         //
-        //         // Thus `addr >> T::BITS` is guaranteed to be non-zero.
-        //         //
-        //         // `{non_zero} | packed_tag` can't make the value zero.
-
-        //         (addr >> Self::BOOL_BITS) | packed_tag
-        //     }))
-        // }
-        // so what follows is roughly what the above looks like but inlined
-
-        let self_addr = ptr.as_ptr() as *const *const str as usize;
-        let addr = self_addr | packed_tag;
-        let dest_addr = addr as isize;
-        let offset = dest_addr.wrapping_sub(self_addr as isize);
-
-        // SAFETY: The resulting pointer is guaranteed to be NonNull as we only modify the niche bytes
-        unsafe { NonNull::new_unchecked(ptr.as_ptr().cast::().wrapping_offset(offset).cast()) }
+        unsafe {
+            // Safety: The pointer is derived from a non-null and bit-oring it with true (1) will
+            // not make it null.
+            NonNull::new_unchecked(ptr.as_ptr().map_addr(|addr| addr | packed_tag))
+        }
     }
 
     #[inline]
     pub(crate) fn pointer(self) -> NonNull<*const str> {
         // SAFETY: The resulting pointer is guaranteed to be NonNull as we only modify the niche bytes
         unsafe {
-            NonNull::new_unchecked(Strict::map_addr(self.packed.as_ptr(), |addr| {
-                addr & !Self::BOOL_BITS
-            }))
+            NonNull::new_unchecked(self.packed.as_ptr().map_addr(|addr| addr & !Self::BOOL_BITS))
         }
     }
 
diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
index c15751e7c680f..66553a2661a20 100644
--- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
+++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
@@ -174,6 +174,7 @@ define_symbols! {
     const_param_ty,
     Context,
     Continue,
+    convert,
     copy,
     Copy,
     core_panic,
@@ -239,6 +240,10 @@ define_symbols! {
     format_unsafe_arg,
     format,
     freeze,
+    from,
+    From,
+    FromStr,
+    from_str,
     from_output,
     from_residual,
     from_usize,
@@ -270,6 +275,8 @@ define_symbols! {
     index_mut,
     index,
     Index,
+    into,
+    Into,
     into_future,
     into_iter,
     IntoFuture,
@@ -340,6 +347,7 @@ define_symbols! {
     option,
     Option,
     Ord,
+    Ordering,
     Output,
     CallRefFuture,
     CallOnceFuture,
@@ -356,11 +364,14 @@ define_symbols! {
     panic_location,
     panic_misaligned_pointer_dereference,
     panic_nounwind,
+    panic_null_pointer_dereference,
     panic,
     Param,
+    parse,
     partial_ord,
     PartialEq,
     PartialOrd,
+    CoercePointee,
     path,
     Pending,
     phantom_data,
@@ -385,6 +396,7 @@ define_symbols! {
     RangeToInclusive,
     Ready,
     receiver,
+    receiver_target,
     recursion_limit,
     register_attr,
     register_tool,
@@ -428,6 +440,7 @@ define_symbols! {
     shr,
     simd,
     sized,
+    skip,
     slice_len_fn,
     Some,
     start,
@@ -446,15 +459,22 @@ define_symbols! {
     system,
     sysv64,
     Target,
+    target_feature,
+    enable,
     termination,
     test_case,
     test,
+    then,
     thiscall,
+    to_string,
     trace_macros,
     transmute_opts,
     transmute_trait,
     transparent,
+    try_into,
     Try,
+    TryFrom,
+    try_from,
     tuple_trait,
     u128,
     u16,
@@ -462,6 +482,7 @@ define_symbols! {
     u64,
     u8,
     unadjusted,
+    unknown,
     Unknown,
     unpin,
     unreachable_2015,
diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
index 1b2162dad0f76..5654c04a59287 100644
--- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
@@ -14,7 +14,7 @@ use ide_db::{
     prime_caches, ChangeWithProcMacros, FxHashMap, RootDatabase,
 };
 use itertools::Itertools;
-use proc_macro_api::{MacroDylib, ProcMacroServer};
+use proc_macro_api::{MacroDylib, ProcMacroClient};
 use project_model::{CargoConfig, PackageRoot, ProjectManifest, ProjectWorkspace};
 use span::Span;
 use vfs::{
@@ -42,7 +42,7 @@ pub fn load_workspace_at(
     cargo_config: &CargoConfig,
     load_config: &LoadCargoConfig,
     progress: &dyn Fn(String),
-) -> anyhow::Result<(RootDatabase, vfs::Vfs, Option)> {
+) -> anyhow::Result<(RootDatabase, vfs::Vfs, Option)> {
     let root = AbsPathBuf::assert_utf8(std::env::current_dir()?.join(root));
     let root = ProjectManifest::discover_single(&root)?;
     let mut workspace = ProjectWorkspace::load(root, cargo_config, progress)?;
@@ -59,7 +59,7 @@ pub fn load_workspace(
     ws: ProjectWorkspace,
     extra_env: &FxHashMap,
     load_config: &LoadCargoConfig,
-) -> anyhow::Result<(RootDatabase, vfs::Vfs, Option)> {
+) -> anyhow::Result<(RootDatabase, vfs::Vfs, Option)> {
     let (sender, receiver) = unbounded();
     let mut vfs = vfs::Vfs::default();
     let mut loader = {
@@ -71,10 +71,10 @@ pub fn load_workspace(
     let proc_macro_server = match &load_config.with_proc_macro_server {
         ProcMacroServerChoice::Sysroot => ws
             .find_sysroot_proc_macro_srv()
-            .and_then(|it| ProcMacroServer::spawn(&it, extra_env).map_err(Into::into))
+            .and_then(|it| ProcMacroClient::spawn(&it, extra_env).map_err(Into::into))
             .map_err(|e| (e, true)),
         ProcMacroServerChoice::Explicit(path) => {
-            ProcMacroServer::spawn(path, extra_env).map_err(Into::into).map_err(|e| (e, true))
+            ProcMacroClient::spawn(path, extra_env).map_err(Into::into).map_err(|e| (e, true))
         }
         ProcMacroServerChoice::None => {
             Err((anyhow::format_err!("proc macro server disabled"), false))
@@ -82,7 +82,7 @@ pub fn load_workspace(
     };
     match &proc_macro_server {
         Ok(server) => {
-            tracing::info!(path=%server.path(), "Proc-macro server started")
+            tracing::info!(path=%server.server_path(), "Proc-macro server started")
         }
         Err((e, _)) => {
             tracing::info!(%e, "Failed to start proc-macro server")
@@ -242,9 +242,6 @@ impl ProjectFolders {
                     }
                 }
 
-                if dirs.include.is_empty() {
-                    continue;
-                }
                 vfs::loader::Entry::Directories(dirs)
             };
 
@@ -259,6 +256,24 @@ impl ProjectFolders {
             fsc.add_file_set(file_set_roots)
         }
 
+        for ws in workspaces.iter() {
+            let mut file_set_roots: Vec = vec![];
+            let mut entries = vec![];
+
+            for buildfile in ws.buildfiles() {
+                file_set_roots.push(VfsPath::from(buildfile.to_owned()));
+                entries.push(buildfile.to_owned());
+            }
+
+            if !file_set_roots.is_empty() {
+                let entry = vfs::loader::Entry::Files(entries);
+                res.watch.push(res.load.len());
+                res.load.push(entry);
+                local_filesets.push(fsc.len() as u64);
+                fsc.add_file_set(file_set_roots)
+            }
+        }
+
         if let Some(user_config_path) = user_config_dir_path {
             let ratoml_path = {
                 let mut p = user_config_path.to_path_buf();
@@ -267,7 +282,7 @@ impl ProjectFolders {
             };
 
             let file_set_roots = vec![VfsPath::from(ratoml_path.to_owned())];
-            let entry = vfs::loader::Entry::Files(vec![ratoml_path]);
+            let entry = vfs::loader::Entry::Files(vec![ratoml_path.to_owned()]);
 
             res.watch.push(res.load.len());
             res.load.push(entry);
@@ -362,7 +377,7 @@ impl SourceRootConfig {
 
 /// Load the proc-macros for the given lib path, disabling all expanders whose names are in `ignored_macros`.
 pub fn load_proc_macro(
-    server: &ProcMacroServer,
+    server: &ProcMacroClient,
     path: &AbsPath,
     ignored_macros: &[Box],
 ) -> ProcMacroLoadResult {
@@ -476,17 +491,17 @@ struct Expander(proc_macro_api::ProcMacro);
 impl ProcMacroExpander for Expander {
     fn expand(
         &self,
-        subtree: &tt::Subtree,
-        attrs: Option<&tt::Subtree>,
+        subtree: &tt::TopSubtree,
+        attrs: Option<&tt::TopSubtree>,
         env: &Env,
         def_site: Span,
         call_site: Span,
         mixed_site: Span,
         current_dir: Option,
-    ) -> Result, ProcMacroExpansionError> {
+    ) -> Result, ProcMacroExpansionError> {
         match self.0.expand(
-            subtree,
-            attrs,
+            subtree.view(),
+            attrs.map(|attrs| attrs.view()),
             env.clone().into(),
             def_site,
             call_site,
diff --git a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs
index 270bc05a4ee2c..89c300300379c 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs
@@ -53,11 +53,11 @@ fn benchmark_expand_macro_rules() {
             .map(|(id, tt)| {
                 let res = rules[&id].expand(&tt, |_| (), DUMMY, Edition::CURRENT);
                 assert!(res.err.is_none());
-                res.value.0.token_trees.len()
+                res.value.0 .0.len()
             })
             .sum()
     };
-    assert_eq!(hash, 65720);
+    assert_eq!(hash, 450144);
 }
 
 fn macro_rules_fixtures() -> FxHashMap {
@@ -68,7 +68,7 @@ fn macro_rules_fixtures() -> FxHashMap {
         .collect()
 }
 
-fn macro_rules_fixtures_tt() -> FxHashMap> {
+fn macro_rules_fixtures_tt() -> FxHashMap> {
     let fixture = bench_fixture::numerous_macro_rules();
     let source_file = ast::SourceFile::parse(&fixture, span::Edition::CURRENT).ok().unwrap();
 
@@ -92,7 +92,7 @@ fn macro_rules_fixtures_tt() -> FxHashMap> {
 /// Generate random invocation fixtures from rules
 fn invocation_fixtures(
     rules: &FxHashMap,
-) -> Vec<(String, tt::Subtree)> {
+) -> Vec<(String, tt::TopSubtree)> {
     let mut seed = 123456789;
     let mut res = Vec::new();
 
@@ -112,19 +112,16 @@ fn invocation_fixtures(
                 // So we just skip any error cases and try again
                 let mut try_cnt = 0;
                 loop {
-                    let mut token_trees = Vec::new();
+                    let mut builder = tt::TopSubtreeBuilder::new(tt::Delimiter {
+                        open: DUMMY,
+                        close: DUMMY,
+                        kind: tt::DelimiterKind::Invisible,
+                    });
                     for op in rule.lhs.iter() {
-                        collect_from_op(op, &mut token_trees, &mut seed);
+                        collect_from_op(op, &mut builder, &mut seed);
                     }
+                    let subtree = builder.build();
 
-                    let subtree = tt::Subtree {
-                        delimiter: tt::Delimiter {
-                            open: DUMMY,
-                            close: DUMMY,
-                            kind: tt::DelimiterKind::Invisible,
-                        },
-                        token_trees: token_trees.into_boxed_slice(),
-                    };
                     if it.expand(&subtree, |_| (), DUMMY, Edition::CURRENT).err.is_none() {
                         res.push((name.clone(), subtree));
                         break;
@@ -139,43 +136,41 @@ fn invocation_fixtures(
     }
     return res;
 
-    fn collect_from_op(op: &Op, token_trees: &mut Vec>, seed: &mut usize) {
+    fn collect_from_op(op: &Op, builder: &mut tt::TopSubtreeBuilder, seed: &mut usize) {
         return match op {
             Op::Var { kind, .. } => match kind.as_ref() {
-                Some(MetaVarKind::Ident) => token_trees.push(make_ident("foo")),
-                Some(MetaVarKind::Ty) => token_trees.push(make_ident("Foo")),
-                Some(MetaVarKind::Tt) => token_trees.push(make_ident("foo")),
-                Some(MetaVarKind::Vis) => token_trees.push(make_ident("pub")),
-                Some(MetaVarKind::Pat) => token_trees.push(make_ident("foo")),
-                Some(MetaVarKind::Path) => token_trees.push(make_ident("foo")),
-                Some(MetaVarKind::Literal) => token_trees.push(make_literal("1")),
-                Some(MetaVarKind::Expr(_)) => token_trees.push(make_ident("foo")),
+                Some(MetaVarKind::Ident) => builder.push(make_ident("foo")),
+                Some(MetaVarKind::Ty) => builder.push(make_ident("Foo")),
+                Some(MetaVarKind::Tt) => builder.push(make_ident("foo")),
+                Some(MetaVarKind::Vis) => builder.push(make_ident("pub")),
+                Some(MetaVarKind::Pat) => builder.push(make_ident("foo")),
+                Some(MetaVarKind::Path) => builder.push(make_ident("foo")),
+                Some(MetaVarKind::Literal) => builder.push(make_literal("1")),
+                Some(MetaVarKind::Expr(_)) => builder.push(make_ident("foo")),
                 Some(MetaVarKind::Lifetime) => {
-                    token_trees.push(make_punct('\''));
-                    token_trees.push(make_ident("a"));
-                }
-                Some(MetaVarKind::Block) => {
-                    token_trees.push(make_subtree(tt::DelimiterKind::Brace, None))
+                    builder.push(make_punct('\''));
+                    builder.push(make_ident("a"));
                 }
+                Some(MetaVarKind::Block) => make_subtree(tt::DelimiterKind::Brace, builder),
                 Some(MetaVarKind::Item) => {
-                    token_trees.push(make_ident("fn"));
-                    token_trees.push(make_ident("foo"));
-                    token_trees.push(make_subtree(tt::DelimiterKind::Parenthesis, None));
-                    token_trees.push(make_subtree(tt::DelimiterKind::Brace, None));
+                    builder.push(make_ident("fn"));
+                    builder.push(make_ident("foo"));
+                    make_subtree(tt::DelimiterKind::Parenthesis, builder);
+                    make_subtree(tt::DelimiterKind::Brace, builder);
                 }
                 Some(MetaVarKind::Meta) => {
-                    token_trees.push(make_ident("foo"));
-                    token_trees.push(make_subtree(tt::DelimiterKind::Parenthesis, None));
+                    builder.push(make_ident("foo"));
+                    make_subtree(tt::DelimiterKind::Parenthesis, builder);
                 }
 
                 None => (),
                 Some(kind) => panic!("Unhandled kind {kind:?}"),
             },
-            Op::Literal(it) => token_trees.push(tt::Leaf::from(it.clone()).into()),
-            Op::Ident(it) => token_trees.push(tt::Leaf::from(it.clone()).into()),
+            Op::Literal(it) => builder.push(tt::Leaf::from(it.clone())),
+            Op::Ident(it) => builder.push(tt::Leaf::from(it.clone())),
             Op::Punct(puncts) => {
                 for punct in puncts.as_slice() {
-                    token_trees.push(tt::Leaf::from(*punct).into());
+                    builder.push(tt::Leaf::from(*punct));
                 }
             }
             Op::Repeat { tokens, kind, separator } => {
@@ -187,20 +182,18 @@ fn invocation_fixtures(
                 };
                 for i in 0..cnt {
                     for it in tokens.iter() {
-                        collect_from_op(it, token_trees, seed);
+                        collect_from_op(it, builder, seed);
                     }
                     if i + 1 != cnt {
                         if let Some(sep) = separator {
                             match &**sep {
                                 Separator::Literal(it) => {
-                                    token_trees.push(tt::Leaf::Literal(it.clone()).into())
-                                }
-                                Separator::Ident(it) => {
-                                    token_trees.push(tt::Leaf::Ident(it.clone()).into())
+                                    builder.push(tt::Leaf::Literal(it.clone()))
                                 }
+                                Separator::Ident(it) => builder.push(tt::Leaf::Ident(it.clone())),
                                 Separator::Puncts(puncts) => {
                                     for it in puncts {
-                                        token_trees.push(tt::Leaf::Punct(*it).into())
+                                        builder.push(tt::Leaf::Punct(*it))
                                     }
                                 }
                             };
@@ -209,15 +202,9 @@ fn invocation_fixtures(
                 }
             }
             Op::Subtree { tokens, delimiter } => {
-                let mut subtree = Vec::new();
-                tokens.iter().for_each(|it| {
-                    collect_from_op(it, &mut subtree, seed);
-                });
-
-                let subtree =
-                    tt::Subtree { delimiter: *delimiter, token_trees: subtree.into_boxed_slice() };
-
-                token_trees.push(subtree.into());
+                builder.open(delimiter.kind, delimiter.open);
+                tokens.iter().for_each(|it| collect_from_op(it, builder, seed));
+                builder.close(delimiter.close);
             }
             Op::Ignore { .. }
             | Op::Index { .. }
@@ -233,35 +220,27 @@ fn invocation_fixtures(
             *seed = usize::wrapping_add(usize::wrapping_mul(*seed, a), c);
             *seed
         }
-        fn make_ident(ident: &str) -> tt::TokenTree {
+        fn make_ident(ident: &str) -> tt::Leaf {
             tt::Leaf::Ident(tt::Ident {
                 span: DUMMY,
                 sym: Symbol::intern(ident),
                 is_raw: tt::IdentIsRaw::No,
             })
-            .into()
         }
-        fn make_punct(char: char) -> tt::TokenTree {
-            tt::Leaf::Punct(tt::Punct { span: DUMMY, char, spacing: tt::Spacing::Alone }).into()
+        fn make_punct(char: char) -> tt::Leaf {
+            tt::Leaf::Punct(tt::Punct { span: DUMMY, char, spacing: tt::Spacing::Alone })
         }
-        fn make_literal(lit: &str) -> tt::TokenTree {
+        fn make_literal(lit: &str) -> tt::Leaf {
             tt::Leaf::Literal(tt::Literal {
                 span: DUMMY,
                 symbol: Symbol::intern(lit),
                 kind: tt::LitKind::Str,
                 suffix: None,
             })
-            .into()
         }
-        fn make_subtree(
-            kind: tt::DelimiterKind,
-            token_trees: Option>>,
-        ) -> tt::TokenTree {
-            tt::Subtree {
-                delimiter: tt::Delimiter { open: DUMMY, close: DUMMY, kind },
-                token_trees: token_trees.map(Vec::into_boxed_slice).unwrap_or_default(),
-            }
-            .into()
+        fn make_subtree(kind: tt::DelimiterKind, builder: &mut tt::TopSubtreeBuilder) {
+            builder.open(kind, DUMMY);
+            builder.close(DUMMY);
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander.rs b/src/tools/rust-analyzer/crates/mbe/src/expander.rs
index 1979e5171ab0c..5539a88c707d1 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/expander.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/expander.rs
@@ -13,12 +13,12 @@ use crate::{parser::MetaVarKind, ExpandError, ExpandErrorKind, ExpandResult, Mat
 
 pub(crate) fn expand_rules(
     rules: &[crate::Rule],
-    input: &tt::Subtree,
+    input: &tt::TopSubtree,
     marker: impl Fn(&mut Span) + Copy,
     call_site: Span,
     def_site_edition: Edition,
-) -> ExpandResult<(tt::Subtree, MatchedArmIndex)> {
-    let mut match_: Option<(matcher::Match, &crate::Rule, usize)> = None;
+) -> ExpandResult<(tt::TopSubtree, MatchedArmIndex)> {
+    let mut match_: Option<(matcher::Match<'_>, &crate::Rule, usize)> = None;
     for (idx, rule) in rules.iter().enumerate() {
         let new_match = matcher::match_(&rule.lhs, input, def_site_edition);
 
@@ -50,13 +50,7 @@ pub(crate) fn expand_rules(
         ExpandResult { value: (value, idx.try_into().ok()), err: match_.err.or(transcribe_err) }
     } else {
         ExpandResult::new(
-            (
-                tt::Subtree {
-                    delimiter: tt::Delimiter::invisible_spanned(call_site),
-                    token_trees: Box::default(),
-                },
-                None,
-            ),
+            (tt::TopSubtree::empty(tt::DelimSpan::from_single(call_site)), None),
             ExpandError::new(call_site, ExpandErrorKind::NoMatchingRule),
         )
     }
@@ -107,32 +101,35 @@ pub(crate) fn expand_rules(
 /// In other words, `Bindings` is a *multi* mapping from `Symbol` to
 /// `tt::TokenTree`, where the index to select a particular `TokenTree` among
 /// many is not a plain `usize`, but a `&[usize]`.
-#[derive(Debug, Default, Clone, PartialEq, Eq)]
-struct Bindings {
-    inner: FxHashMap,
+#[derive(Debug, Default, Clone)]
+struct Bindings<'a> {
+    inner: FxHashMap>,
 }
 
-#[derive(Debug, Clone, PartialEq, Eq)]
-enum Binding {
-    Fragment(Fragment),
-    Nested(Vec),
+#[derive(Debug, Clone)]
+enum Binding<'a> {
+    Fragment(Fragment<'a>),
+    Nested(Vec>),
     Empty,
     Missing(MetaVarKind),
 }
 
-#[derive(Debug, Clone, PartialEq, Eq)]
-enum Fragment {
+#[derive(Debug, Default, Clone)]
+enum Fragment<'a> {
+    #[default]
     Empty,
     /// token fragments are just copy-pasted into the output
-    Tokens(tt::TokenTree),
-    /// Expr ast fragments are surrounded with `()` on insertion to preserve
-    /// precedence. Note that this impl is different from the one currently in
-    /// `rustc` -- `rustc` doesn't translate fragments into token trees at all.
+    Tokens(tt::TokenTreesView<'a, Span>),
+    /// Expr ast fragments are surrounded with `()` on transcription to preserve precedence.
+    /// Note that this impl is different from the one currently in `rustc` --
+    /// `rustc` doesn't translate fragments into token trees at all.
     ///
     /// At one point in time, we tried to use "fake" delimiters here à la
     /// proc-macro delimiter=none. As we later discovered, "none" delimiters are
     /// tricky to handle in the parser, and rustc doesn't handle those either.
-    Expr(tt::Subtree),
+    ///
+    /// The span of the outer delimiters is marked on transcription.
+    Expr(tt::TokenTreesView<'a, Span>),
     /// There are roughly two types of paths: paths in expression context, where a
     /// separator `::` between an identifier and its following generic argument list
     /// is mandatory, and paths in type context, where `::` can be omitted.
@@ -142,5 +139,18 @@ enum Fragment {
     /// and is trasncribed as an expression-context path, verbatim transcription
     /// would cause a syntax error. We need to fix it up just before transcribing;
     /// see `transcriber::fix_up_and_push_path_tt()`.
-    Path(tt::Subtree),
+    Path(tt::TokenTreesView<'a, Span>),
+    TokensOwned(tt::TopSubtree),
+}
+
+impl Fragment<'_> {
+    fn is_empty(&self) -> bool {
+        match self {
+            Fragment::Empty => true,
+            Fragment::Tokens(it) => it.len() == 0,
+            Fragment::Expr(it) => it.len() == 0,
+            Fragment::Path(it) => it.len() == 0,
+            Fragment::TokensOwned(it) => it.0.is_empty(),
+        }
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs
index 95641abc0f37b..b7f25aa380961 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs
@@ -64,7 +64,10 @@ use std::{rc::Rc, sync::Arc};
 use intern::{sym, Symbol};
 use smallvec::{smallvec, SmallVec};
 use span::{Edition, Span};
-use tt::{iter::TtIter, DelimSpan};
+use tt::{
+    iter::{TtElement, TtIter},
+    DelimSpan,
+};
 
 use crate::{
     expander::{Binding, Bindings, ExpandResult, Fragment},
@@ -73,7 +76,7 @@ use crate::{
     ExpandError, ExpandErrorKind, MetaTemplate, ValueResult,
 };
 
-impl Bindings {
+impl<'a> Bindings<'a> {
     fn push_optional(&mut self, name: Symbol) {
         self.inner.insert(name, Binding::Fragment(Fragment::Empty));
     }
@@ -82,14 +85,14 @@ impl Bindings {
         self.inner.insert(name, Binding::Empty);
     }
 
-    fn bindings(&self) -> impl Iterator {
+    fn bindings(&self) -> impl Iterator> {
         self.inner.values()
     }
 }
 
-#[derive(Clone, Default, Debug, PartialEq, Eq)]
-pub(super) struct Match {
-    pub(super) bindings: Bindings,
+#[derive(Clone, Default, Debug)]
+pub(super) struct Match<'a> {
+    pub(super) bindings: Bindings<'a>,
     /// We currently just keep the first error and count the rest to compare matches.
     pub(super) err: Option,
     pub(super) err_count: usize,
@@ -99,7 +102,7 @@ pub(super) struct Match {
     pub(super) bound_count: usize,
 }
 
-impl Match {
+impl Match<'_> {
     fn add_err(&mut self, err: ExpandError) {
         let prev_err = self.err.take();
         self.err = prev_err.or(Some(err));
@@ -108,12 +111,16 @@ impl Match {
 }
 
 /// Matching errors are added to the `Match`.
-pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree, edition: Edition) -> Match {
+pub(super) fn match_<'t>(
+    pattern: &'t MetaTemplate,
+    input: &'t tt::TopSubtree,
+    edition: Edition,
+) -> Match<'t> {
     let mut res = match_loop(pattern, input, edition);
     res.bound_count = count(res.bindings.bindings());
     return res;
 
-    fn count<'a>(bindings: impl Iterator) -> usize {
+    fn count<'a>(bindings: impl Iterator>) -> usize {
         bindings
             .map(|it| match it {
                 Binding::Fragment(_) => 1,
@@ -126,10 +133,10 @@ pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree, edition:
 }
 
 #[derive(Debug, Clone)]
-enum BindingKind {
+enum BindingKind<'a> {
     Empty(Symbol),
     Optional(Symbol),
-    Fragment(Symbol, Fragment),
+    Fragment(Symbol, Fragment<'a>),
     Missing(Symbol, MetaVarKind),
     Nested(usize, usize),
 }
@@ -144,12 +151,12 @@ enum LinkNode {
 }
 
 #[derive(Default)]
-struct BindingsBuilder {
-    nodes: Vec>>>,
+struct BindingsBuilder<'a> {
+    nodes: Vec>>>>,
     nested: Vec>>,
 }
 
-impl BindingsBuilder {
+impl<'a> BindingsBuilder<'a> {
     fn alloc(&mut self) -> BindingsIdx {
         let idx = self.nodes.len();
         self.nodes.push(Vec::new());
@@ -186,7 +193,7 @@ impl BindingsBuilder {
         self.nodes[idx.0].push(LinkNode::Node(Rc::new(BindingKind::Optional(var.clone()))));
     }
 
-    fn push_fragment(&mut self, idx: &mut BindingsIdx, var: &Symbol, fragment: Fragment) {
+    fn push_fragment(&mut self, idx: &mut BindingsIdx, var: &Symbol, fragment: Fragment<'a>) {
         self.nodes[idx.0]
             .push(LinkNode::Node(Rc::new(BindingKind::Fragment(var.clone(), fragment))));
     }
@@ -207,11 +214,11 @@ impl BindingsBuilder {
         idx.0 = new_idx;
     }
 
-    fn build(self, idx: &BindingsIdx) -> Bindings {
+    fn build(self, idx: &BindingsIdx) -> Bindings<'a> {
         self.build_inner(&self.nodes[idx.0])
     }
 
-    fn build_inner(&self, link_nodes: &[LinkNode>]) -> Bindings {
+    fn build_inner(&self, link_nodes: &[LinkNode>>]) -> Bindings<'a> {
         let mut bindings = Bindings::default();
         let mut nodes = Vec::new();
         self.collect_nodes(link_nodes, &mut nodes);
@@ -257,11 +264,11 @@ impl BindingsBuilder {
         bindings
     }
 
-    fn collect_nested_ref<'a>(
-        &'a self,
+    fn collect_nested_ref<'b>(
+        &'b self,
         id: usize,
         len: usize,
-        nested_refs: &mut Vec<&'a [LinkNode>]>,
+        nested_refs: &mut Vec<&'b [LinkNode>>]>,
     ) {
         self.nested[id].iter().take(len).for_each(|it| match it {
             LinkNode::Node(id) => nested_refs.push(&self.nodes[*id]),
@@ -269,7 +276,7 @@ impl BindingsBuilder {
         });
     }
 
-    fn collect_nested(&self, idx: usize, nested_idx: usize, nested: &mut Vec) {
+    fn collect_nested(&self, idx: usize, nested_idx: usize, nested: &mut Vec>) {
         let last = &self.nodes[idx];
         let mut nested_refs: Vec<&[_]> = Vec::new();
         self.nested[nested_idx].iter().for_each(|it| match *it {
@@ -280,17 +287,22 @@ impl BindingsBuilder {
         nested.extend(nested_refs.into_iter().map(|iter| self.build_inner(iter)));
     }
 
-    fn collect_nodes_ref<'a>(&'a self, id: usize, len: usize, nodes: &mut Vec<&'a BindingKind>) {
+    fn collect_nodes_ref<'b>(
+        &'b self,
+        id: usize,
+        len: usize,
+        nodes: &mut Vec<&'b BindingKind<'a>>,
+    ) {
         self.nodes[id].iter().take(len).for_each(|it| match it {
             LinkNode::Node(it) => nodes.push(it),
             LinkNode::Parent { idx, len } => self.collect_nodes_ref(*idx, *len, nodes),
         });
     }
 
-    fn collect_nodes<'a>(
-        &'a self,
-        link_nodes: &'a [LinkNode>],
-        nodes: &mut Vec<&'a BindingKind>,
+    fn collect_nodes<'b>(
+        &'b self,
+        link_nodes: &'b [LinkNode>>],
+        nodes: &mut Vec<&'b BindingKind<'a>>,
     ) {
         link_nodes.iter().for_each(|it| match it {
             LinkNode::Node(it) => nodes.push(it),
@@ -327,7 +339,7 @@ struct MatchState<'t> {
     bindings: BindingsIdx,
 
     /// Cached result of meta variable parsing
-    meta_result: Option<(TtIter<'t, Span>, ExpandResult>)>,
+    meta_result: Option<(TtIter<'t, Span>, ExpandResult>>)>,
 
     /// Is error occurred in this state, will `poised` to "parent"
     is_error: bool,
@@ -355,8 +367,8 @@ struct MatchState<'t> {
 fn match_loop_inner<'t>(
     src: TtIter<'t, Span>,
     stack: &[TtIter<'t, Span>],
-    res: &mut Match,
-    bindings_builder: &mut BindingsBuilder,
+    res: &mut Match<'t>,
+    bindings_builder: &mut BindingsBuilder<'t>,
     cur_items: &mut SmallVec<[MatchState<'t>; 1]>,
     bb_items: &mut SmallVec<[MatchState<'t>; 1]>,
     next_items: &mut Vec>,
@@ -463,7 +475,7 @@ fn match_loop_inner<'t>(
                 })
             }
             OpDelimited::Op(Op::Subtree { tokens, delimiter }) => {
-                if let Ok(subtree) = src.clone().expect_subtree() {
+                if let Ok((subtree, _)) = src.clone().expect_subtree() {
                     if subtree.delimiter.kind == delimiter.kind {
                         item.stack.push(item.dot);
                         item.dot = tokens.iter_delimited_with(*delimiter);
@@ -478,8 +490,8 @@ fn match_loop_inner<'t>(
                     match match_res.err {
                         None => {
                             // Some meta variables are optional (e.g. vis)
-                            if match_res.value.is_some() {
-                                item.meta_result = Some((fork, match_res));
+                            if !match_res.value.is_empty() {
+                                item.meta_result = Some((fork, match_res.map(Some)));
                                 try_push!(bb_items, item);
                             } else {
                                 bindings_builder.push_optional(&mut item.bindings, name);
@@ -489,15 +501,14 @@ fn match_loop_inner<'t>(
                         }
                         Some(err) => {
                             res.add_err(err);
-                            match match_res.value {
-                                Some(fragment) => bindings_builder.push_fragment(
+                            if !match_res.value.is_empty() {
+                                bindings_builder.push_fragment(
                                     &mut item.bindings,
                                     name,
-                                    fragment,
-                                ),
-                                None => {
-                                    bindings_builder.push_missing(&mut item.bindings, name, kind)
-                                }
+                                    match_res.value,
+                                )
+                            } else {
+                                bindings_builder.push_missing(&mut item.bindings, name, kind)
                             }
                             item.is_error = true;
                             error_items.push(item);
@@ -593,13 +604,13 @@ fn match_loop_inner<'t>(
                 stdx::never!("metavariable expression in lhs found");
             }
             OpDelimited::Open => {
-                if matches!(src.peek_n(0), Some(tt::TokenTree::Subtree(..))) {
+                if matches!(src.peek(), Some(TtElement::Subtree(..))) {
                     item.dot.next();
                     try_push!(next_items, item);
                 }
             }
             OpDelimited::Close => {
-                let is_delim_closed = src.peek_n(0).is_none() && !stack.is_empty();
+                let is_delim_closed = src.is_empty() && !stack.is_empty();
                 if is_delim_closed {
                     item.dot.next();
                     try_push!(next_items, item);
@@ -609,9 +620,13 @@ fn match_loop_inner<'t>(
     }
 }
 
-fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, edition: Edition) -> Match {
-    let span = src.delimiter.delim_span();
-    let mut src = TtIter::new(src);
+fn match_loop<'t>(
+    pattern: &'t MetaTemplate,
+    src: &'t tt::TopSubtree,
+    edition: Edition,
+) -> Match<'t> {
+    let span = src.top_subtree().delimiter.delim_span();
+    let mut src = src.iter();
     let mut stack: SmallVec<[TtIter<'_, Span>; 1]> = SmallVec::new();
     let mut res = Match::default();
     let mut error_recover_item = None;
@@ -663,7 +678,7 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, edition: Edition)
         // We need to do some post processing after the `match_loop_inner`.
         // If we reached the EOF, check that there is EXACTLY ONE possible matcher. Otherwise,
         // either the parse is ambiguous (which should never happen) or there is a syntax error.
-        if src.peek_n(0).is_none() && stack.is_empty() {
+        if src.is_empty() && stack.is_empty() {
             if let [state] = &*eof_items {
                 // remove all errors, because it is the correct answer !
                 res = Match::default();
@@ -687,11 +702,7 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, edition: Edition)
             || !(bb_items.is_empty() || next_items.is_empty())
             || bb_items.len() > 1;
         if has_leftover_tokens {
-            res.unmatched_tts += src.len();
-            while let Some(it) = stack.pop() {
-                src = it;
-                res.unmatched_tts += src.len();
-            }
+            res.unmatched_tts += src.remaining().flat_tokens().len();
             res.add_err(ExpandError::new(span.open, ExpandErrorKind::LeftoverTokens));
 
             if let Some(error_recover_item) = error_recover_item {
@@ -714,9 +725,9 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, edition: Edition)
                 }
             } else {
                 match src.next() {
-                    Some(tt::TokenTree::Subtree(subtree)) => {
+                    Some(TtElement::Subtree(_, subtree_iter)) => {
                         stack.push(src.clone());
-                        src = TtIter::new(subtree);
+                        src = subtree_iter;
                     }
                     None => {
                         if let Some(iter) = stack.pop() {
@@ -760,18 +771,16 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, edition: Edition)
     }
 }
 
-fn match_meta_var(
+fn match_meta_var<'t>(
     kind: MetaVarKind,
-    input: &mut TtIter<'_, Span>,
+    input: &mut TtIter<'t, Span>,
     delim_span: DelimSpan,
     edition: Edition,
-) -> ExpandResult> {
+) -> ExpandResult> {
     let fragment = match kind {
         MetaVarKind::Path => {
             return expect_fragment(input, parser::PrefixEntryPoint::Path, edition, delim_span)
-                .map(|it| {
-                    it.map(|it| tt::TokenTree::subtree_or_wrap(it, delim_span)).map(Fragment::Path)
-                });
+                .map(Fragment::Path);
         }
         MetaVarKind::Expr(expr) => {
             // `expr_2021` should not match underscores, let expressions, or inline const.
@@ -782,8 +791,8 @@ fn match_meta_var(
             // rustc [explicitly checks the next token][1].
             // [0]: https://github.com/rust-lang/rust/issues/86730
             // [1]: https://github.com/rust-lang/rust/blob/f0c4da499/compiler/rustc_expand/src/mbe/macro_parser.rs#L576
-            match input.peek_n(0) {
-                Some(tt::TokenTree::Leaf(tt::Leaf::Ident(it))) => {
+            match input.peek() {
+                Some(TtElement::Leaf(tt::Leaf::Ident(it))) => {
                     let is_err = if it.is_raw.no() && matches!(expr, ExprKind::Expr2021) {
                         it.sym == sym::underscore || it.sym == sym::let_ || it.sym == sym::const_
                     } else {
@@ -799,34 +808,15 @@ fn match_meta_var(
                 _ => {}
             };
             return expect_fragment(input, parser::PrefixEntryPoint::Expr, edition, delim_span)
-                .map(|tt| {
-                    tt.map(|tt| match tt {
-                        tt::TokenTree::Leaf(leaf) => tt::Subtree {
-                            delimiter: tt::Delimiter::invisible_spanned(*leaf.span()),
-                            token_trees: Box::new([leaf.into()]),
-                        },
-                        tt::TokenTree::Subtree(mut s) => {
-                            if s.delimiter.kind == tt::DelimiterKind::Invisible {
-                                s.delimiter.kind = tt::DelimiterKind::Parenthesis;
-                            }
-                            s
-                        }
-                    })
-                    .map(Fragment::Expr)
-                });
+                .map(Fragment::Expr);
         }
         MetaVarKind::Ident | MetaVarKind::Tt | MetaVarKind::Lifetime | MetaVarKind::Literal => {
             let span = input.next_span();
-            let tt_result = match kind {
-                MetaVarKind::Ident => input
-                    .expect_ident()
-                    .map(|ident| tt::Leaf::from(ident.clone()).into())
-                    .map_err(|()| {
-                        ExpandError::binding_error(
-                            span.unwrap_or(delim_span.close),
-                            "expected ident",
-                        )
-                    }),
+            let savepoint = input.savepoint();
+            let err = match kind {
+                MetaVarKind::Ident => input.expect_ident().map(drop).map_err(|()| {
+                    ExpandError::binding_error(span.unwrap_or(delim_span.close), "expected ident")
+                }),
                 MetaVarKind::Tt => expect_tt(input).map_err(|()| {
                     ExpandError::binding_error(
                         span.unwrap_or(delim_span.close),
@@ -840,29 +830,19 @@ fn match_meta_var(
                     )
                 }),
                 MetaVarKind::Literal => {
-                    let neg = eat_char(input, '-');
-                    input
-                        .expect_literal()
-                        .map(|literal| {
-                            let lit = literal.clone();
-                            match neg {
-                                None => lit.into(),
-                                Some(neg) => tt::TokenTree::Subtree(tt::Subtree {
-                                    delimiter: tt::Delimiter::invisible_spanned(*literal.span()),
-                                    token_trees: Box::new([neg, lit.into()]),
-                                }),
-                            }
-                        })
-                        .map_err(|()| {
-                            ExpandError::binding_error(
-                                span.unwrap_or(delim_span.close),
-                                "expected literal",
-                            )
-                        })
+                    eat_char(input, '-');
+                    input.expect_literal().map(drop).map_err(|()| {
+                        ExpandError::binding_error(
+                            span.unwrap_or(delim_span.close),
+                            "expected literal",
+                        )
+                    })
                 }
                 _ => unreachable!(),
-            };
-            return tt_result.map(|it| Some(Fragment::Tokens(it))).into();
+            }
+            .err();
+            let tt_result = input.from_savepoint(savepoint);
+            return ValueResult { value: Fragment::Tokens(tt_result), err };
         }
         MetaVarKind::Ty => parser::PrefixEntryPoint::Ty,
         MetaVarKind::Pat => parser::PrefixEntryPoint::PatTop,
@@ -873,7 +853,7 @@ fn match_meta_var(
         MetaVarKind::Item => parser::PrefixEntryPoint::Item,
         MetaVarKind::Vis => parser::PrefixEntryPoint::Vis,
     };
-    expect_fragment(input, fragment, edition, delim_span).map(|it| it.map(Fragment::Tokens))
+    expect_fragment(input, fragment, edition, delim_span).map(Fragment::Tokens)
 }
 
 fn collect_vars(collector_fun: &mut impl FnMut(Symbol), pattern: &MetaTemplate) {
@@ -990,54 +970,31 @@ fn expect_separator(iter: &mut TtIter<'_, S>, separator: &Separator) ->
     ok
 }
 
-fn expect_tt(iter: &mut TtIter<'_, S>) -> Result, ()> {
-    if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = iter.peek_n(0) {
+fn expect_tt(iter: &mut TtIter<'_, S>) -> Result<(), ()> {
+    if let Some(TtElement::Leaf(tt::Leaf::Punct(punct))) = iter.peek() {
         if punct.char == '\'' {
-            expect_lifetime(iter)
+            expect_lifetime(iter)?;
         } else {
-            let puncts = iter.expect_glued_punct()?;
-            let delimiter = tt::Delimiter {
-                open: puncts.first().unwrap().span,
-                close: puncts.last().unwrap().span,
-                kind: tt::DelimiterKind::Invisible,
-            };
-            let token_trees = puncts.into_iter().map(|p| tt::Leaf::Punct(p).into()).collect();
-            Ok(tt::TokenTree::Subtree(tt::Subtree { delimiter, token_trees }))
+            iter.expect_glued_punct()?;
         }
     } else {
-        iter.next().ok_or(()).cloned()
+        iter.next().ok_or(())?;
     }
+    Ok(())
 }
 
-fn expect_lifetime(iter: &mut TtIter<'_, S>) -> Result, ()> {
+fn expect_lifetime(iter: &mut TtIter<'_, S>) -> Result<(), ()> {
     let punct = iter.expect_single_punct()?;
     if punct.char != '\'' {
         return Err(());
     }
-    let ident = iter.expect_ident_or_underscore()?;
-
-    Ok(tt::Subtree {
-        delimiter: tt::Delimiter {
-            open: punct.span,
-            close: ident.span,
-            kind: tt::DelimiterKind::Invisible,
-        },
-        token_trees: Box::new([
-            tt::Leaf::Punct(*punct).into(),
-            tt::Leaf::Ident(ident.clone()).into(),
-        ]),
-    }
-    .into())
+    iter.expect_ident_or_underscore()?;
+    Ok(())
 }
 
-fn eat_char(iter: &mut TtIter<'_, S>, c: char) -> Option> {
-    let mut fork = iter.clone();
-    match fork.expect_char(c) {
-        Ok(_) => {
-            let tt = iter.next().cloned();
-            *iter = fork;
-            tt
-        }
-        Err(_) => None,
+fn eat_char(iter: &mut TtIter<'_, S>, c: char) {
+    if matches!(iter.peek(), Some(TtElement::Leaf(tt::Leaf::Punct(tt::Punct { char, .. }))) if *char == c)
+    {
+        iter.next().expect("already peeked");
     }
 }
diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs
index 1db2f35d26232..7710ea7938951 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs
@@ -3,7 +3,7 @@
 
 use intern::{sym, Symbol};
 use span::{Edition, Span};
-use tt::Delimiter;
+use tt::{iter::TtElement, Delimiter, TopSubtreeBuilder};
 
 use crate::{
     expander::{Binding, Bindings, Fragment},
@@ -11,8 +11,8 @@ use crate::{
     ExpandError, ExpandErrorKind, ExpandResult, MetaTemplate,
 };
 
-impl Bindings {
-    fn get(&self, name: &Symbol, span: Span) -> Result<&Binding, ExpandError> {
+impl<'t> Bindings<'t> {
+    fn get(&self, name: &Symbol, span: Span) -> Result<&Binding<'t>, ExpandError> {
         match self.inner.get(name) {
             Some(binding) => Ok(binding),
             None => Err(ExpandError::new(
@@ -28,7 +28,7 @@ impl Bindings {
         mut span: Span,
         nesting: &mut [NestingState],
         marker: impl Fn(&mut Span),
-    ) -> Result {
+    ) -> Result, ExpandError> {
         macro_rules! binding_err {
             ($($arg:tt)*) => { ExpandError::binding_error(span, format!($($arg)*)) };
         }
@@ -38,7 +38,10 @@ impl Bindings {
             nesting_state.hit = true;
             b = match b {
                 Binding::Fragment(_) => break,
-                Binding::Missing(_) => break,
+                Binding::Missing(_) => {
+                    nesting_state.at_end = true;
+                    break;
+                }
                 Binding::Nested(bs) => bs.get(nesting_state.idx).ok_or_else(|| {
                     nesting_state.at_end = true;
                     binding_err!("could not find nested binding `{name}`")
@@ -50,86 +53,61 @@ impl Bindings {
             };
         }
         match b {
-            Binding::Fragment(f @ (Fragment::Path(sub) | Fragment::Expr(sub))) => {
-                let tt::Subtree { delimiter, token_trees } = sub;
-                marker(&mut span);
-                let subtree = tt::Subtree {
-                    delimiter: tt::Delimiter {
-                        // FIXME split span
-                        open: span,
-                        close: span,
-                        kind: delimiter.kind,
-                    },
-                    token_trees: token_trees.clone(),
-                };
-                Ok(match f {
-                    Fragment::Tokens(_) | Fragment::Empty => unreachable!(),
-                    Fragment::Expr(_) => Fragment::Expr,
-                    Fragment::Path(_) => Fragment::Path,
-                }(subtree))
-            }
-            Binding::Fragment(it @ (Fragment::Tokens(_) | Fragment::Empty)) => Ok(it.clone()),
+            Binding::Fragment(f) => Ok(f.clone()),
             // emit some reasonable default expansion for missing bindings,
             // this gives better recovery than emitting the `$fragment-name` verbatim
             Binding::Missing(it) => Ok({
                 marker(&mut span);
+                let mut builder = TopSubtreeBuilder::new(tt::Delimiter::invisible_spanned(span));
                 match it {
                     MetaVarKind::Stmt => {
-                        Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
+                        builder.push(tt::Leaf::Punct(tt::Punct {
                             span,
                             char: ';',
                             spacing: tt::Spacing::Alone,
-                        })))
+                        }));
                     }
-                    MetaVarKind::Block => Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree {
-                        delimiter: tt::Delimiter {
-                            open: span,
-                            close: span,
-                            kind: tt::DelimiterKind::Brace,
-                        },
-                        token_trees: Box::new([]),
-                    })),
-                    // FIXME: Meta and Item should get proper defaults
-                    MetaVarKind::Meta | MetaVarKind::Item | MetaVarKind::Tt | MetaVarKind::Vis => {
-                        Fragment::Empty
+                    MetaVarKind::Block => {
+                        builder.open(tt::DelimiterKind::Brace, span);
+                        builder.close(span);
                     }
+                    // FIXME: Meta and Item should get proper defaults
+                    MetaVarKind::Meta | MetaVarKind::Item | MetaVarKind::Tt | MetaVarKind::Vis => {}
                     MetaVarKind::Path
                     | MetaVarKind::Ty
                     | MetaVarKind::Pat
                     | MetaVarKind::PatParam
                     | MetaVarKind::Expr(_)
                     | MetaVarKind::Ident => {
-                        Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
+                        builder.push(tt::Leaf::Ident(tt::Ident {
                             sym: sym::missing.clone(),
                             span,
                             is_raw: tt::IdentIsRaw::No,
-                        })))
+                        }));
                     }
                     MetaVarKind::Lifetime => {
-                        Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree {
-                            delimiter: tt::Delimiter::invisible_spanned(span),
-                            token_trees: Box::new([
-                                tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
-                                    char: '\'',
-                                    span,
-                                    spacing: tt::Spacing::Joint,
-                                })),
-                                tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
-                                    sym: sym::missing.clone(),
-                                    span,
-                                    is_raw: tt::IdentIsRaw::No,
-                                })),
-                            ]),
-                        }))
+                        builder.extend([
+                            tt::Leaf::Punct(tt::Punct {
+                                char: '\'',
+                                span,
+                                spacing: tt::Spacing::Joint,
+                            }),
+                            tt::Leaf::Ident(tt::Ident {
+                                sym: sym::missing.clone(),
+                                span,
+                                is_raw: tt::IdentIsRaw::No,
+                            }),
+                        ]);
                     }
                     MetaVarKind::Literal => {
-                        Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
+                        builder.push(tt::Leaf::Ident(tt::Ident {
                             sym: sym::missing.clone(),
                             span,
                             is_raw: tt::IdentIsRaw::No,
-                        })))
+                        }));
                     }
                 }
+                Fragment::TokensOwned(builder.build())
             }),
             Binding::Nested(_) => {
                 Err(binding_err!("expected simple binding, found nested binding `{name}`"))
@@ -143,13 +121,13 @@ impl Bindings {
 
 pub(super) fn transcribe(
     template: &MetaTemplate,
-    bindings: &Bindings,
+    bindings: &Bindings<'_>,
     marker: impl Fn(&mut Span) + Copy,
     call_site: Span,
-) -> ExpandResult> {
+) -> ExpandResult> {
     let mut ctx = ExpandCtx { bindings, nesting: Vec::new(), call_site };
-    let mut arena: Vec> = Vec::new();
-    expand_subtree(&mut ctx, template, None, &mut arena, marker)
+    let mut builder = tt::TopSubtreeBuilder::new(tt::Delimiter::invisible_spanned(ctx.call_site));
+    expand_subtree(&mut ctx, template, &mut builder, marker).map(|()| builder.build())
 }
 
 #[derive(Debug)]
@@ -165,103 +143,97 @@ struct NestingState {
 
 #[derive(Debug)]
 struct ExpandCtx<'a> {
-    bindings: &'a Bindings,
+    bindings: &'a Bindings<'a>,
     nesting: Vec,
     call_site: Span,
 }
 
-fn expand_subtree(
+fn expand_subtree_with_delimiter(
     ctx: &mut ExpandCtx<'_>,
     template: &MetaTemplate,
+    builder: &mut tt::TopSubtreeBuilder,
     delimiter: Option>,
-    arena: &mut Vec>,
     marker: impl Fn(&mut Span) + Copy,
-) -> ExpandResult> {
-    // remember how many elements are in the arena now - when returning, we want to drain exactly how many elements we added. This way, the recursive uses of the arena get their own "view" of the arena, but will reuse the allocation
-    let start_elements = arena.len();
+) -> ExpandResult<()> {
+    let delimiter = delimiter.unwrap_or_else(|| tt::Delimiter::invisible_spanned(ctx.call_site));
+    builder.open(delimiter.kind, delimiter.open);
+    let result = expand_subtree(ctx, template, builder, marker);
+    builder.close(delimiter.close);
+    result
+}
+
+fn expand_subtree(
+    ctx: &mut ExpandCtx<'_>,
+    template: &MetaTemplate,
+    builder: &mut tt::TopSubtreeBuilder,
+    marker: impl Fn(&mut Span) + Copy,
+) -> ExpandResult<()> {
     let mut err = None;
     'ops: for op in template.iter() {
         match op {
-            Op::Literal(it) => arena.push(
-                tt::Leaf::from({
-                    let mut it = it.clone();
-                    marker(&mut it.span);
-                    it
-                })
-                .into(),
-            ),
-            Op::Ident(it) => arena.push(
-                tt::Leaf::from({
-                    let mut it = it.clone();
-                    marker(&mut it.span);
-                    it
-                })
-                .into(),
-            ),
+            Op::Literal(it) => builder.push(tt::Leaf::from({
+                let mut it = it.clone();
+                marker(&mut it.span);
+                it
+            })),
+            Op::Ident(it) => builder.push(tt::Leaf::from({
+                let mut it = it.clone();
+                marker(&mut it.span);
+                it
+            })),
             Op::Punct(puncts) => {
-                for punct in puncts.as_slice() {
-                    arena.push(
-                        tt::Leaf::from({
-                            let mut it = *punct;
-                            marker(&mut it.span);
-                            it
-                        })
-                        .into(),
-                    );
-                }
+                builder.extend(puncts.iter().map(|punct| {
+                    tt::Leaf::from({
+                        let mut it = *punct;
+                        marker(&mut it.span);
+                        it
+                    })
+                }));
             }
             Op::Subtree { tokens, delimiter } => {
                 let mut delimiter = *delimiter;
                 marker(&mut delimiter.open);
                 marker(&mut delimiter.close);
-                let ExpandResult { value: tt, err: e } =
-                    expand_subtree(ctx, tokens, Some(delimiter), arena, marker);
+                let ExpandResult { value: (), err: e } =
+                    expand_subtree_with_delimiter(ctx, tokens, builder, Some(delimiter), marker);
                 err = err.or(e);
-                arena.push(tt.into());
             }
             Op::Var { name, id, .. } => {
-                let ExpandResult { value: fragment, err: e } = expand_var(ctx, name, *id, marker);
+                let ExpandResult { value: (), err: e } =
+                    expand_var(ctx, name, *id, builder, marker);
                 err = err.or(e);
-                push_fragment(ctx, arena, fragment);
             }
             Op::Repeat { tokens: subtree, kind, separator } => {
-                let ExpandResult { value: fragment, err: e } =
-                    expand_repeat(ctx, subtree, *kind, separator.as_deref(), arena, marker);
+                let ExpandResult { value: (), err: e } =
+                    expand_repeat(ctx, subtree, *kind, separator.as_deref(), builder, marker);
                 err = err.or(e);
-                push_fragment(ctx, arena, fragment)
             }
             Op::Ignore { name, id } => {
                 // Expand the variable, but ignore the result. This registers the repetition count.
                 // FIXME: Any emitted errors are dropped.
-                expand_var(ctx, name, *id, marker);
+                let _ = ctx.bindings.get_fragment(name, *id, &mut ctx.nesting, marker);
             }
             Op::Index { depth } => {
                 let index =
                     ctx.nesting.get(ctx.nesting.len() - 1 - depth).map_or(0, |nest| nest.idx);
-                arena.push(
-                    tt::Leaf::Literal(tt::Literal {
-                        symbol: Symbol::integer(index),
-                        span: ctx.call_site,
-                        kind: tt::LitKind::Integer,
-                        suffix: None,
-                    })
-                    .into(),
-                );
+                builder.push(tt::Leaf::Literal(tt::Literal {
+                    symbol: Symbol::integer(index),
+                    span: ctx.call_site,
+                    kind: tt::LitKind::Integer,
+                    suffix: None,
+                }));
             }
             Op::Len { depth } => {
                 let length = ctx.nesting.get(ctx.nesting.len() - 1 - depth).map_or(0, |_nest| {
                     // FIXME: to be implemented
                     0
                 });
-                arena.push(
-                    tt::Leaf::Literal(tt::Literal {
-                        symbol: Symbol::integer(length),
-                        span: ctx.call_site,
-                        kind: tt::LitKind::Integer,
-                        suffix: None,
-                    })
-                    .into(),
-                );
+                builder.push(tt::Leaf::Literal(tt::Literal {
+                    symbol: Symbol::integer(length),
+                    span: ctx.call_site,
+                    kind: tt::LitKind::Integer,
+                    suffix: None,
+                }));
             }
             Op::Count { name, depth } => {
                 let mut binding = match ctx.bindings.get(name, ctx.call_site) {
@@ -302,15 +274,12 @@ fn expand_subtree(
 
                 let res = count(binding, 0, depth.unwrap_or(0));
 
-                arena.push(
-                    tt::Leaf::Literal(tt::Literal {
-                        symbol: Symbol::integer(res),
-                        span: ctx.call_site,
-                        suffix: None,
-                        kind: tt::LitKind::Integer,
-                    })
-                    .into(),
-                );
+                builder.push(tt::Leaf::Literal(tt::Literal {
+                    symbol: Symbol::integer(res),
+                    span: ctx.call_site,
+                    suffix: None,
+                    kind: tt::LitKind::Integer,
+                }));
             }
             Op::Concat { elements, span: concat_span } => {
                 let mut concatenated = String::new();
@@ -342,11 +311,22 @@ fn expand_subtree(
                                     continue;
                                 }
                             };
-                            let value = match &var_value {
-                                Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => {
+                            let values = match &var_value {
+                                Fragment::Tokens(tokens) => {
+                                    let mut iter = tokens.iter();
+                                    (iter.next(), iter.next())
+                                }
+                                Fragment::TokensOwned(tokens) => {
+                                    let mut iter = tokens.iter();
+                                    (iter.next(), iter.next())
+                                }
+                                _ => (None, None),
+                            };
+                            let value = match values {
+                                (Some(TtElement::Leaf(tt::Leaf::Ident(ident))), None) => {
                                     ident.sym.as_str()
                                 }
-                                Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) => {
+                                (Some(TtElement::Leaf(tt::Leaf::Literal(lit))), None) => {
                                     lit.symbol.as_str()
                                 }
                                 _ => {
@@ -382,36 +362,53 @@ fn expand_subtree(
                 let needs_raw =
                     parser::SyntaxKind::from_keyword(&concatenated, Edition::LATEST).is_some();
                 let is_raw = if needs_raw { tt::IdentIsRaw::Yes } else { tt::IdentIsRaw::No };
-                arena.push(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
+                builder.push(tt::Leaf::Ident(tt::Ident {
                     is_raw,
                     span: result_span,
                     sym: Symbol::intern(&concatenated),
-                })));
+                }));
             }
         }
     }
-    // drain the elements added in this instance of expand_subtree
-    let tts = arena.drain(start_elements..).collect();
-    ExpandResult {
-        value: tt::Subtree {
-            delimiter: delimiter.unwrap_or_else(|| tt::Delimiter::invisible_spanned(ctx.call_site)),
-            token_trees: tts,
-        },
-        err,
-    }
+    ExpandResult { value: (), err }
 }
 
 fn expand_var(
     ctx: &mut ExpandCtx<'_>,
     v: &Symbol,
     id: Span,
-    marker: impl Fn(&mut Span),
-) -> ExpandResult {
+    builder: &mut tt::TopSubtreeBuilder,
+    marker: impl Fn(&mut Span) + Copy,
+) -> ExpandResult<()> {
     // We already handle $crate case in mbe parser
     debug_assert!(*v != sym::crate_);
 
     match ctx.bindings.get_fragment(v, id, &mut ctx.nesting, marker) {
-        Ok(it) => ExpandResult::ok(it),
+        Ok(fragment) => {
+            match fragment {
+                Fragment::Tokens(tt) => builder.extend_with_tt(tt.strip_invisible()),
+                Fragment::TokensOwned(tt) => builder.extend_with_tt(tt.view().strip_invisible()),
+                Fragment::Expr(sub) => {
+                    let sub = sub.strip_invisible();
+                    let mut span = id;
+                    marker(&mut span);
+                    let wrap_in_parens = !matches!(sub.flat_tokens(), [tt::TokenTree::Leaf(_)])
+                        && sub.try_into_subtree().is_none_or(|it| {
+                            it.top_subtree().delimiter.kind == tt::DelimiterKind::Invisible
+                        });
+                    if wrap_in_parens {
+                        builder.open(tt::DelimiterKind::Parenthesis, span);
+                    }
+                    builder.extend_with_tt(sub);
+                    if wrap_in_parens {
+                        builder.close(span);
+                    }
+                }
+                Fragment::Path(tt) => fix_up_and_push_path_tt(ctx, builder, tt),
+                Fragment::Empty => (),
+            };
+            ExpandResult::ok(())
+        }
         Err(e) if matches!(e.inner.1, ExpandErrorKind::UnresolvedBinding(_)) => {
             // Note that it is possible to have a `$var` inside a macro which is not bound.
             // For example:
@@ -426,29 +423,13 @@ fn expand_var(
             //     }
             // ```
             // We just treat it a normal tokens
-            let tt = tt::Subtree {
-                delimiter: tt::Delimiter::invisible_spanned(id),
-                token_trees: Box::new([
-                    tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone, span: id })
-                        .into(),
-                    tt::Leaf::from(tt::Ident {
-                        sym: v.clone(),
-                        span: id,
-                        is_raw: tt::IdentIsRaw::No,
-                    })
-                    .into(),
-                ]),
-            }
-            .into();
-            ExpandResult::ok(Fragment::Tokens(tt))
+            builder.extend([
+                tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone, span: id }),
+                tt::Leaf::from(tt::Ident { sym: v.clone(), span: id, is_raw: tt::IdentIsRaw::No }),
+            ]);
+            ExpandResult::ok(())
         }
-        Err(e) => ExpandResult {
-            value: Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree::empty(tt::DelimSpan {
-                open: ctx.call_site,
-                close: ctx.call_site,
-            }))),
-            err: Some(e),
-        },
+        Err(e) => ExpandResult::only_err(e),
     }
 }
 
@@ -457,21 +438,21 @@ fn expand_repeat(
     template: &MetaTemplate,
     kind: RepeatKind,
     separator: Option<&Separator>,
-    arena: &mut Vec>,
+    builder: &mut tt::TopSubtreeBuilder,
     marker: impl Fn(&mut Span) + Copy,
-) -> ExpandResult {
-    let mut buf: Vec> = Vec::new();
+) -> ExpandResult<()> {
     ctx.nesting.push(NestingState { idx: 0, at_end: false, hit: false });
     // Dirty hack to make macro-expansion terminate.
     // This should be replaced by a proper macro-by-example implementation
     let limit = 65536;
-    let mut has_seps = 0;
     let mut counter = 0;
     let mut err = None;
 
+    let initial_restore_point = builder.restore_point();
+    let mut restore_point = builder.restore_point();
     loop {
-        let ExpandResult { value: mut t, err: e } =
-            expand_subtree(ctx, template, None, arena, marker);
+        let ExpandResult { value: (), err: e } =
+            expand_subtree_with_delimiter(ctx, template, builder, None, marker);
         let nesting_state = ctx.nesting.last_mut().unwrap();
         if nesting_state.at_end || !nesting_state.hit {
             break;
@@ -479,23 +460,18 @@ fn expand_repeat(
         nesting_state.idx += 1;
         nesting_state.hit = false;
 
+        builder.remove_last_subtree_if_invisible();
+
+        restore_point = builder.restore_point();
+
         counter += 1;
         if counter == limit {
-            tracing::warn!(
-                "expand_tt in repeat pattern exceed limit => {:#?}\n{:#?}",
-                template,
-                ctx
-            );
-            return ExpandResult {
-                value: Fragment::Tokens(
-                    tt::Subtree {
-                        delimiter: tt::Delimiter::invisible_spanned(ctx.call_site),
-                        token_trees: Box::new([]),
-                    }
-                    .into(),
-                ),
-                err: Some(ExpandError::new(ctx.call_site, ExpandErrorKind::LimitExceeded)),
-            };
+            // FIXME: This is a bug here, we get here when we shouldn't, see https://github.com/rust-lang/rust-analyzer/issues/18910.
+            // If we don't restore we emit a lot of nodes which causes a stack overflow down the road. For now just ignore them,
+            // there is always an error here anyway.
+            builder.restore(initial_restore_point);
+            err = Some(ExpandError::new(ctx.call_site, ExpandErrorKind::LimitExceeded));
+            break;
         }
 
         if e.is_some() {
@@ -503,24 +479,14 @@ fn expand_repeat(
             continue;
         }
 
-        t.delimiter.kind = tt::DelimiterKind::Invisible;
-        push_subtree(&mut buf, t);
-
         if let Some(sep) = separator {
-            has_seps = match sep {
-                Separator::Ident(ident) => {
-                    buf.push(tt::Leaf::from(ident.clone()).into());
-                    1
-                }
-                Separator::Literal(lit) => {
-                    buf.push(tt::Leaf::from(lit.clone()).into());
-                    1
-                }
+            match sep {
+                Separator::Ident(ident) => builder.push(tt::Leaf::from(ident.clone())),
+                Separator::Literal(lit) => builder.push(tt::Leaf::from(lit.clone())),
                 Separator::Puncts(puncts) => {
                     for &punct in puncts {
-                        buf.push(tt::Leaf::from(punct).into());
+                        builder.push(tt::Leaf::from(punct));
                     }
-                    puncts.len()
                 }
             };
         }
@@ -529,46 +495,18 @@ fn expand_repeat(
             break;
         }
     }
+    // Lose the last separator and last after-the-end round.
+    builder.restore(restore_point);
 
     ctx.nesting.pop().unwrap();
-    for _ in 0..has_seps {
-        buf.pop();
-    }
 
     // Check if it is a single token subtree without any delimiter
     // e.g {Delimiter:None> ['>'] /Delimiter:None>}
-    let tt = tt::Subtree {
-        delimiter: tt::Delimiter::invisible_spanned(ctx.call_site),
-        token_trees: buf.into_boxed_slice(),
-    };
 
-    if RepeatKind::OneOrMore == kind && counter == 0 {
-        let span = tt.delimiter.open;
-        return ExpandResult {
-            value: Fragment::Tokens(tt.into()),
-            err: Some(ExpandError::new(span, ExpandErrorKind::UnexpectedToken)),
-        };
-    }
-    ExpandResult { value: Fragment::Tokens(tt.into()), err }
-}
-
-fn push_fragment(ctx: &ExpandCtx<'_>, buf: &mut Vec>, fragment: Fragment) {
-    match fragment {
-        Fragment::Tokens(tt::TokenTree::Subtree(tt)) => push_subtree(buf, tt),
-        Fragment::Expr(sub) => {
-            push_subtree(buf, sub);
-        }
-        Fragment::Path(tt) => fix_up_and_push_path_tt(ctx, buf, tt),
-        Fragment::Tokens(tt) => buf.push(tt),
-        Fragment::Empty => (),
-    }
-}
-
-fn push_subtree(buf: &mut Vec>, tt: tt::Subtree) {
-    match tt.delimiter.kind {
-        tt::DelimiterKind::Invisible => buf.extend(Vec::from(tt.token_trees)),
-        _ => buf.push(tt.into()),
+    if RepeatKind::OneOrMore == kind && counter == 0 && err.is_none() {
+        err = Some(ExpandError::new(ctx.call_site, ExpandErrorKind::UnexpectedToken));
     }
+    ExpandResult { value: (), err }
 }
 
 /// Inserts the path separator `::` between an identifier and its following generic
@@ -576,47 +514,45 @@ fn push_subtree(buf: &mut Vec>, tt: tt::Subtree) {
 /// we need this fixup.
 fn fix_up_and_push_path_tt(
     ctx: &ExpandCtx<'_>,
-    buf: &mut Vec>,
-    subtree: tt::Subtree,
+    builder: &mut tt::TopSubtreeBuilder,
+    subtree: tt::TokenTreesView<'_, Span>,
 ) {
-    stdx::always!(matches!(subtree.delimiter.kind, tt::DelimiterKind::Invisible));
     let mut prev_was_ident = false;
     // Note that we only need to fix up the top-level `TokenTree`s because the
     // context of the paths in the descendant `Subtree`s won't be changed by the
     // mbe transcription.
-    for tt in Vec::from(subtree.token_trees) {
+    let mut iter = subtree.iter();
+    while let Some(tt) = iter.next_as_view() {
         if prev_was_ident {
             // Pedantically, `(T) -> U` in `FnOnce(T) -> U` is treated as a generic
             // argument list and thus needs `::` between it and `FnOnce`. However in
             // today's Rust this type of path *semantically* cannot appear as a
             // top-level expression-context path, so we can safely ignore it.
-            if let tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '<', .. })) = tt {
-                buf.push(
+            if let [tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '<', .. }))] =
+                tt.flat_tokens()
+            {
+                builder.extend([
                     tt::Leaf::Punct(tt::Punct {
                         char: ':',
                         spacing: tt::Spacing::Joint,
                         span: ctx.call_site,
-                    })
-                    .into(),
-                );
-                buf.push(
+                    }),
                     tt::Leaf::Punct(tt::Punct {
                         char: ':',
                         spacing: tt::Spacing::Alone,
                         span: ctx.call_site,
-                    })
-                    .into(),
-                );
+                    }),
+                ]);
             }
         }
-        prev_was_ident = matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Ident(_)));
-        buf.push(tt);
+        prev_was_ident = matches!(tt.flat_tokens(), [tt::TokenTree::Leaf(tt::Leaf::Ident(_))]);
+        builder.extend_with_tt(tt);
     }
 }
 
 /// Handles `${count(t, depth)}`. `our_depth` is the recursion depth and `count_depth` is the depth
 /// defined by the metavar expression.
-fn count(binding: &Binding, depth_curr: usize, depth_max: usize) -> usize {
+fn count(binding: &Binding<'_>, depth_curr: usize, depth_max: usize) -> usize {
     match binding {
         Binding::Nested(bs) => {
             if depth_curr == depth_max {
diff --git a/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs
index ca10a2be27328..bebd29ef74700 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs
@@ -148,17 +148,17 @@ impl DeclarativeMacro {
 
     /// The old, `macro_rules! m {}` flavor.
     pub fn parse_macro_rules(
-        tt: &tt::Subtree,
+        tt: &tt::TopSubtree,
         ctx_edition: impl Copy + Fn(SyntaxContextId) -> Edition,
     ) -> DeclarativeMacro {
         // Note: this parsing can be implemented using mbe machinery itself, by
         // matching against `$($lhs:tt => $rhs:tt);*` pattern, but implementing
         // manually seems easier.
-        let mut src = TtIter::new(tt);
+        let mut src = tt.iter();
         let mut rules = Vec::new();
         let mut err = None;
 
-        while src.len() > 0 {
+        while !src.is_empty() {
             let rule = match Rule::parse(ctx_edition, &mut src) {
                 Ok(it) => it,
                 Err(e) => {
@@ -168,7 +168,7 @@ impl DeclarativeMacro {
             };
             rules.push(rule);
             if let Err(()) = src.expect_char(';') {
-                if src.len() > 0 {
+                if !src.is_empty() {
                     err = Some(Box::new(ParseError::expected("expected `;`")));
                 }
                 break;
@@ -187,8 +187,8 @@ impl DeclarativeMacro {
 
     /// The new, unstable `macro m {}` flavor.
     pub fn parse_macro2(
-        args: Option<&tt::Subtree>,
-        body: &tt::Subtree,
+        args: Option<&tt::TopSubtree>,
+        body: &tt::TopSubtree,
         ctx_edition: impl Copy + Fn(SyntaxContextId) -> Edition,
     ) -> DeclarativeMacro {
         let mut rules = Vec::new();
@@ -198,8 +198,8 @@ impl DeclarativeMacro {
             cov_mark::hit!(parse_macro_def_simple);
 
             let rule = (|| {
-                let lhs = MetaTemplate::parse_pattern(ctx_edition, args)?;
-                let rhs = MetaTemplate::parse_template(ctx_edition, body)?;
+                let lhs = MetaTemplate::parse_pattern(ctx_edition, args.iter())?;
+                let rhs = MetaTemplate::parse_template(ctx_edition, body.iter())?;
 
                 Ok(crate::Rule { lhs, rhs })
             })();
@@ -210,8 +210,8 @@ impl DeclarativeMacro {
             }
         } else {
             cov_mark::hit!(parse_macro_def_rules);
-            let mut src = TtIter::new(body);
-            while src.len() > 0 {
+            let mut src = body.iter();
+            while !src.is_empty() {
                 let rule = match Rule::parse(ctx_edition, &mut src) {
                     Ok(it) => it,
                     Err(e) => {
@@ -221,7 +221,7 @@ impl DeclarativeMacro {
                 };
                 rules.push(rule);
                 if let Err(()) = src.expect_any_char(&[';', ',']) {
-                    if src.len() > 0 {
+                    if !src.is_empty() {
                         err = Some(Box::new(ParseError::expected(
                             "expected `;` or `,` to delimit rules",
                         )));
@@ -251,11 +251,11 @@ impl DeclarativeMacro {
 
     pub fn expand(
         &self,
-        tt: &tt::Subtree,
+        tt: &tt::TopSubtree,
         marker: impl Fn(&mut Span) + Copy,
         call_site: Span,
         def_site_edition: Edition,
-    ) -> ExpandResult<(tt::Subtree, MatchedArmIndex)> {
+    ) -> ExpandResult<(tt::TopSubtree, MatchedArmIndex)> {
         expander::expand_rules(&self.rules, tt, marker, call_site, def_site_edition)
     }
 }
@@ -265,10 +265,12 @@ impl Rule {
         edition: impl Copy + Fn(SyntaxContextId) -> Edition,
         src: &mut TtIter<'_, Span>,
     ) -> Result {
-        let lhs = src.expect_subtree().map_err(|()| ParseError::expected("expected subtree"))?;
+        let (_, lhs) =
+            src.expect_subtree().map_err(|()| ParseError::expected("expected subtree"))?;
         src.expect_char('=').map_err(|()| ParseError::expected("expected `=`"))?;
         src.expect_char('>').map_err(|()| ParseError::expected("expected `>`"))?;
-        let rhs = src.expect_subtree().map_err(|()| ParseError::expected("expected subtree"))?;
+        let (_, rhs) =
+            src.expect_subtree().map_err(|()| ParseError::expected("expected subtree"))?;
 
         let lhs = MetaTemplate::parse_pattern(edition, lhs)?;
         let rhs = MetaTemplate::parse_template(edition, rhs)?;
@@ -359,17 +361,18 @@ impl From> for ValueResult {
     }
 }
 
-pub fn expect_fragment(
-    tt_iter: &mut TtIter<'_, Span>,
+pub fn expect_fragment<'t>(
+    tt_iter: &mut TtIter<'t, Span>,
     entry_point: ::parser::PrefixEntryPoint,
     edition: ::parser::Edition,
     delim_span: DelimSpan,
-) -> ExpandResult>> {
+) -> ExpandResult> {
     use ::parser;
-    let buffer = tt::buffer::TokenBuffer::from_tokens(tt_iter.as_slice());
-    let parser_input = to_parser_input(edition, &buffer);
+    let buffer = tt_iter.remaining();
+    // FIXME: Pass the correct edition per token. Due to the split between mbe and hir-expand it's complicated.
+    let parser_input = to_parser_input(buffer, &mut |_ctx| edition);
     let tree_traversal = entry_point.parse(&parser_input, edition);
-    let mut cursor = buffer.begin();
+    let mut cursor = buffer.cursor();
     let mut error = false;
     for step in tree_traversal.iter() {
         match step {
@@ -378,13 +381,13 @@ pub fn expect_fragment(
                     n_input_tokens = 2;
                 }
                 for _ in 0..n_input_tokens {
-                    cursor = cursor.bump_subtree();
+                    cursor.bump_or_end();
                 }
             }
             parser::Step::FloatSplit { .. } => {
                 // FIXME: We need to split the tree properly here, but mutating the token trees
                 // in the buffer is somewhat tricky to pull off.
-                cursor = cursor.bump_subtree();
+                cursor.bump_or_end();
             }
             parser::Step::Enter { .. } | parser::Step::Exit => (),
             parser::Step::Error { .. } => error = true,
@@ -393,29 +396,19 @@ pub fn expect_fragment(
 
     let err = if error || !cursor.is_root() {
         Some(ExpandError::binding_error(
-            buffer.begin().token_tree().map_or(delim_span.close, |tt| tt.span()),
+            buffer.cursor().token_tree().map_or(delim_span.close, |tt| tt.first_span()),
             format!("expected {entry_point:?}"),
         ))
     } else {
         None
     };
 
-    let mut curr = buffer.begin();
-    let mut res = vec![];
-
-    while curr != cursor {
-        let Some(token) = curr.token_tree() else { break };
-        res.push(token.cloned());
-        curr = curr.bump();
+    while !cursor.is_root() {
+        cursor.bump_or_end();
     }
 
-    *tt_iter = TtIter::new_iter(tt_iter.as_slice()[res.len()..].iter());
-    let res = match &*res {
-        [] | [_] => res.pop(),
-        [first, ..] => Some(tt::TokenTree::Subtree(tt::Subtree {
-            delimiter: Delimiter::invisible_spanned(first.first_span()),
-            token_trees: res.into_boxed_slice(),
-        })),
-    };
+    let res = cursor.crossed();
+    tt_iter.flat_advance(res.len());
+
     ExpandResult { value: res, err }
 }
diff --git a/src/tools/rust-analyzer/crates/mbe/src/parser.rs b/src/tools/rust-analyzer/crates/mbe/src/parser.rs
index b55edf4a5e0cd..16d55492a04b6 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/parser.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/parser.rs
@@ -6,7 +6,7 @@ use std::sync::Arc;
 use arrayvec::ArrayVec;
 use intern::{sym, Symbol};
 use span::{Edition, Span, SyntaxContextId};
-use tt::iter::TtIter;
+use tt::iter::{TtElement, TtIter};
 
 use crate::ParseError;
 
@@ -29,14 +29,14 @@ pub(crate) struct MetaTemplate(pub(crate) Box<[Op]>);
 impl MetaTemplate {
     pub(crate) fn parse_pattern(
         edition: impl Copy + Fn(SyntaxContextId) -> Edition,
-        pattern: &tt::Subtree,
+        pattern: TtIter<'_, Span>,
     ) -> Result {
         MetaTemplate::parse(edition, pattern, Mode::Pattern)
     }
 
     pub(crate) fn parse_template(
         edition: impl Copy + Fn(SyntaxContextId) -> Edition,
-        template: &tt::Subtree,
+        template: TtIter<'_, Span>,
     ) -> Result {
         MetaTemplate::parse(edition, template, Mode::Template)
     }
@@ -47,13 +47,11 @@ impl MetaTemplate {
 
     fn parse(
         edition: impl Copy + Fn(SyntaxContextId) -> Edition,
-        tt: &tt::Subtree,
+        mut src: TtIter<'_, Span>,
         mode: Mode,
     ) -> Result {
-        let mut src = TtIter::new(tt);
-
         let mut res = Vec::new();
-        while let Some(first) = src.peek_n(0) {
+        while let Some(first) = src.peek() {
             let op = next_op(edition, first, &mut src, mode)?;
             res.push(op);
         }
@@ -182,12 +180,12 @@ enum Mode {
 
 fn next_op(
     edition: impl Copy + Fn(SyntaxContextId) -> Edition,
-    first_peeked: &tt::TokenTree,
+    first_peeked: TtElement<'_, Span>,
     src: &mut TtIter<'_, Span>,
     mode: Mode,
 ) -> Result {
     let res = match first_peeked {
-        tt::TokenTree::Leaf(tt::Leaf::Punct(p @ tt::Punct { char: '$', .. })) => {
+        TtElement::Leaf(tt::Leaf::Punct(p @ tt::Punct { char: '$', .. })) => {
             src.next().expect("first token already peeked");
             // Note that the '$' itself is a valid token inside macro_rules.
             let second = match src.next() {
@@ -201,18 +199,16 @@ fn next_op(
                 Some(it) => it,
             };
             match second {
-                tt::TokenTree::Subtree(subtree) => match subtree.delimiter.kind {
+                TtElement::Subtree(subtree, mut subtree_iter) => match subtree.delimiter.kind {
                     tt::DelimiterKind::Parenthesis => {
                         let (separator, kind) = parse_repeat(src)?;
-                        let tokens = MetaTemplate::parse(edition, subtree, mode)?;
+                        let tokens = MetaTemplate::parse(edition, subtree_iter, mode)?;
                         Op::Repeat { tokens, separator: separator.map(Arc::new), kind }
                     }
                     tt::DelimiterKind::Brace => match mode {
-                        Mode::Template => {
-                            parse_metavar_expr(&mut TtIter::new(subtree)).map_err(|()| {
-                                ParseError::unexpected("invalid metavariable expression")
-                            })?
-                        }
+                        Mode::Template => parse_metavar_expr(&mut subtree_iter).map_err(|()| {
+                            ParseError::unexpected("invalid metavariable expression")
+                        })?,
                         Mode::Pattern => {
                             return Err(ParseError::unexpected(
                                 "`${}` metavariable expressions are not allowed in matchers",
@@ -225,7 +221,7 @@ fn next_op(
                         ))
                     }
                 },
-                tt::TokenTree::Leaf(leaf) => match leaf {
+                TtElement::Leaf(leaf) => match leaf {
                     tt::Leaf::Ident(ident) if ident.sym == sym::crate_ => {
                         // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path.
                         Op::Ident(tt::Ident {
@@ -265,25 +261,25 @@ fn next_op(
             }
         }
 
-        tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => {
+        TtElement::Leaf(tt::Leaf::Literal(it)) => {
             src.next().expect("first token already peeked");
             Op::Literal(it.clone())
         }
 
-        tt::TokenTree::Leaf(tt::Leaf::Ident(it)) => {
+        TtElement::Leaf(tt::Leaf::Ident(it)) => {
             src.next().expect("first token already peeked");
             Op::Ident(it.clone())
         }
 
-        tt::TokenTree::Leaf(tt::Leaf::Punct(_)) => {
+        TtElement::Leaf(tt::Leaf::Punct(_)) => {
             // There's at least one punct so this shouldn't fail.
             let puncts = src.expect_glued_punct().unwrap();
             Op::Punct(Box::new(puncts))
         }
 
-        tt::TokenTree::Subtree(subtree) => {
+        TtElement::Subtree(subtree, subtree_iter) => {
             src.next().expect("first token already peeked");
-            let tokens = MetaTemplate::parse(edition, subtree, mode)?;
+            let tokens = MetaTemplate::parse(edition, subtree_iter, mode)?;
             Op::Subtree { tokens, delimiter: subtree.delimiter }
         }
     };
@@ -343,8 +339,8 @@ fn parse_repeat(src: &mut TtIter<'_, Span>) -> Result<(Option, Repeat
     let mut separator = Separator::Puncts(ArrayVec::new());
     for tt in src {
         let tt = match tt {
-            tt::TokenTree::Leaf(leaf) => leaf,
-            tt::TokenTree::Subtree(_) => return Err(ParseError::InvalidRepeat),
+            TtElement::Leaf(leaf) => leaf,
+            TtElement::Subtree(..) => return Err(ParseError::InvalidRepeat),
         };
         let has_sep = match &separator {
             Separator::Puncts(puncts) => !puncts.is_empty(),
@@ -378,37 +374,39 @@ fn parse_repeat(src: &mut TtIter<'_, Span>) -> Result<(Option, Repeat
 
 fn parse_metavar_expr(src: &mut TtIter<'_, Span>) -> Result {
     let func = src.expect_ident()?;
-    let args = src.expect_subtree()?;
+    let (args, mut args_iter) = src.expect_subtree()?;
 
     if args.delimiter.kind != tt::DelimiterKind::Parenthesis {
         return Err(());
     }
 
-    let mut args = TtIter::new(args);
-
     let op = match &func.sym {
         s if sym::ignore == *s => {
-            args.expect_dollar()?;
-            let ident = args.expect_ident()?;
+            args_iter.expect_dollar()?;
+            let ident = args_iter.expect_ident()?;
             Op::Ignore { name: ident.sym.clone(), id: ident.span }
         }
-        s if sym::index == *s => Op::Index { depth: parse_depth(&mut args)? },
-        s if sym::len == *s => Op::Len { depth: parse_depth(&mut args)? },
+        s if sym::index == *s => Op::Index { depth: parse_depth(&mut args_iter)? },
+        s if sym::len == *s => Op::Len { depth: parse_depth(&mut args_iter)? },
         s if sym::count == *s => {
-            args.expect_dollar()?;
-            let ident = args.expect_ident()?;
-            let depth = if try_eat_comma(&mut args) { Some(parse_depth(&mut args)?) } else { None };
+            args_iter.expect_dollar()?;
+            let ident = args_iter.expect_ident()?;
+            let depth = if try_eat_comma(&mut args_iter) {
+                Some(parse_depth(&mut args_iter)?)
+            } else {
+                None
+            };
             Op::Count { name: ident.sym.clone(), depth }
         }
         s if sym::concat == *s => {
             let mut elements = Vec::new();
-            while let Some(next) = args.peek_n(0) {
-                let element = if let tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) = next {
-                    args.next().expect("already peeked");
+            while let Some(next) = args_iter.peek() {
+                let element = if let TtElement::Leaf(tt::Leaf::Literal(lit)) = next {
+                    args_iter.next().expect("already peeked");
                     ConcatMetaVarExprElem::Literal(lit.clone())
                 } else {
-                    let is_var = try_eat_dollar(&mut args);
-                    let ident = args.expect_ident_or_underscore()?.clone();
+                    let is_var = try_eat_dollar(&mut args_iter);
+                    let ident = args_iter.expect_ident_or_underscore()?.clone();
 
                     if is_var {
                         ConcatMetaVarExprElem::Var(ident)
@@ -417,8 +415,8 @@ fn parse_metavar_expr(src: &mut TtIter<'_, Span>) -> Result {
                     }
                 };
                 elements.push(element);
-                if args.peek_n(0).is_some() {
-                    args.expect_comma()?;
+                if !args_iter.is_empty() {
+                    args_iter.expect_comma()?;
                 }
             }
             if elements.len() < 2 {
@@ -429,7 +427,7 @@ fn parse_metavar_expr(src: &mut TtIter<'_, Span>) -> Result {
         _ => return Err(()),
     };
 
-    if args.next().is_some() {
+    if args_iter.next().is_some() {
         return Err(());
     }
 
@@ -437,7 +435,7 @@ fn parse_metavar_expr(src: &mut TtIter<'_, Span>) -> Result {
 }
 
 fn parse_depth(src: &mut TtIter<'_, Span>) -> Result {
-    if src.len() == 0 {
+    if src.is_empty() {
         Ok(0)
     } else if let tt::Leaf::Literal(tt::Literal { symbol: text, suffix: None, .. }) =
         src.expect_literal()?
@@ -450,7 +448,7 @@ fn parse_depth(src: &mut TtIter<'_, Span>) -> Result {
 }
 
 fn try_eat_comma(src: &mut TtIter<'_, Span>) -> bool {
-    if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', .. }))) = src.peek_n(0) {
+    if let Some(TtElement::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', .. }))) = src.peek() {
         let _ = src.next();
         return true;
     }
@@ -458,7 +456,7 @@ fn try_eat_comma(src: &mut TtIter<'_, Span>) -> bool {
 }
 
 fn try_eat_dollar(src: &mut TtIter<'_, Span>) -> bool {
-    if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '$', .. }))) = src.peek_n(0) {
+    if let Some(TtElement::Leaf(tt::Leaf::Punct(tt::Punct { char: '$', .. }))) = src.peek() {
         let _ = src.next();
         return true;
     }
diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs
deleted file mode 100644
index 2988fb3cf154e..0000000000000
--- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs
+++ /dev/null
@@ -1,101 +0,0 @@
-use rustc_hash::FxHashMap;
-use span::Span;
-use syntax::{ast, AstNode};
-use test_utils::extract_annotations;
-use tt::{
-    buffer::{TokenBuffer, TokenTreeRef},
-    Leaf, Punct, Spacing,
-};
-
-use crate::{syntax_node_to_token_tree, DocCommentDesugarMode, DummyTestSpanMap, DUMMY};
-
-fn check_punct_spacing(fixture: &str) {
-    let source_file = ast::SourceFile::parse(fixture, span::Edition::CURRENT).ok().unwrap();
-    let subtree = syntax_node_to_token_tree(
-        source_file.syntax(),
-        DummyTestSpanMap,
-        DUMMY,
-        DocCommentDesugarMode::Mbe,
-    );
-    let mut annotations: FxHashMap<_, _> = extract_annotations(fixture)
-        .into_iter()
-        .map(|(range, annotation)| {
-            let spacing = match annotation.as_str() {
-                "Alone" => Spacing::Alone,
-                "Joint" => Spacing::Joint,
-                a => panic!("unknown annotation: {a}"),
-            };
-            (range, spacing)
-        })
-        .collect();
-
-    let buf = TokenBuffer::from_subtree(&subtree);
-    let mut cursor = buf.begin();
-    while !cursor.eof() {
-        while let Some(token_tree) = cursor.token_tree() {
-            if let TokenTreeRef::Leaf(
-                Leaf::Punct(Punct { spacing, span: Span { range, .. }, .. }),
-                _,
-            ) = token_tree
-            {
-                if let Some(expected) = annotations.remove(range) {
-                    assert_eq!(expected, *spacing);
-                }
-            }
-            cursor = cursor.bump_subtree();
-        }
-        cursor = cursor.bump();
-    }
-
-    assert!(annotations.is_empty(), "unchecked annotations: {annotations:?}");
-}
-
-#[test]
-fn punct_spacing() {
-    check_punct_spacing(
-        r#"
-fn main() {
-    0+0;
-   //^ Alone
-    0+(0);
-   //^ Alone
-    0<=0;
-   //^ Joint
-   // ^ Alone
-    0<=(0);
-   // ^ Alone
-    a=0;
-   //^ Alone
-    a=(0);
-   //^ Alone
-    a+=0;
-   //^ Joint
-   // ^ Alone
-    a+=(0);
-   // ^ Alone
-    a&&b;
-   //^ Joint
-   // ^ Alone
-    a&&(b);
-   // ^ Alone
-    foo::bar;
-   //  ^ Joint
-   //   ^ Alone
-    use foo::{bar,baz,};
-   //       ^ Alone
-   //            ^ Alone
-   //                ^ Alone
-    struct Struct<'a> {};
-   //            ^ Joint
-   //             ^ Joint
-    Struct::<0>;
-   //       ^ Alone
-    Struct::<{0}>;
-   //       ^ Alone
-    ;;
-  //^ Joint
-  // ^ Alone
-}
-        "#,
-    );
-}
diff --git a/src/tools/rust-analyzer/crates/mbe/src/tests.rs b/src/tools/rust-analyzer/crates/mbe/src/tests.rs
index e63ad113ffd23..fb68d35a4c84f 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/tests.rs
@@ -26,7 +26,7 @@ fn check_(
             file_id: EditionedFileId::new(FileId::from_raw(0), def_edition),
             ast_id: ErasedFileAstId::from_raw(0),
         },
-        SyntaxContextId::ROOT,
+        SyntaxContextId::root(Edition::CURRENT),
         decl,
     )
     .unwrap();
@@ -39,16 +39,20 @@ fn check_(
         file_id: EditionedFileId::new(FileId::from_raw(1), call_edition),
         ast_id: ErasedFileAstId::from_raw(0),
     };
-    let arg_tt =
-        syntax_bridge::parse_to_token_tree(call_edition, call_anchor, SyntaxContextId::ROOT, arg)
-            .unwrap();
+    let arg_tt = syntax_bridge::parse_to_token_tree(
+        call_edition,
+        call_anchor,
+        SyntaxContextId::root(Edition::CURRENT),
+        arg,
+    )
+    .unwrap();
     let res = mac.expand(
         &arg_tt,
         |_| (),
         Span {
             range: TextRange::up_to(TextSize::of(arg)),
             anchor: call_anchor,
-            ctx: SyntaxContextId::ROOT,
+            ctx: SyntaxContextId::root(Edition::CURRENT),
         },
         def_edition,
     );
@@ -59,7 +63,12 @@ fn check_(
     if render_debug {
         format_to!(expect_res, "{:#?}\n\n", res.value.0);
     }
-    let (node, _) = syntax_bridge::token_tree_to_syntax_node(&res.value.0, parse, def_edition);
+    let (node, _) = syntax_bridge::token_tree_to_syntax_node(
+        &res.value.0,
+        parse,
+        &mut |_| def_edition,
+        def_edition,
+    );
     format_to!(
         expect_res,
         "{}",
@@ -106,25 +115,25 @@ fn token_mapping_smoke_test() {
 struct MyTraitMap2
 "#,
         expect![[r#"
-            SUBTREE $$ 1:0@0..20#0 1:0@0..20#0
-              IDENT   struct 0:0@34..40#0
-              IDENT   MyTraitMap2 1:0@8..19#0
-              SUBTREE {} 0:0@48..49#0 0:0@100..101#0
-                IDENT   map 0:0@58..61#0
-                PUNCH   : [alone] 0:0@61..62#0
-                PUNCH   : [joint] 0:0@63..64#0
-                PUNCH   : [alone] 0:0@64..65#0
-                IDENT   std 0:0@65..68#0
-                PUNCH   : [joint] 0:0@68..69#0
-                PUNCH   : [alone] 0:0@69..70#0
-                IDENT   collections 0:0@70..81#0
-                PUNCH   : [joint] 0:0@81..82#0
-                PUNCH   : [alone] 0:0@82..83#0
-                IDENT   HashSet 0:0@83..90#0
-                PUNCH   < [alone] 0:0@90..91#0
-                SUBTREE () 0:0@91..92#0 0:0@92..93#0
-                PUNCH   > [joint] 0:0@93..94#0
-                PUNCH   , [alone] 0:0@94..95#0
+            SUBTREE $$ 1:0@0..20#2 1:0@0..20#2
+              IDENT   struct 0:0@34..40#2
+              IDENT   MyTraitMap2 1:0@8..19#2
+              SUBTREE {} 0:0@48..49#2 0:0@100..101#2
+                IDENT   map 0:0@58..61#2
+                PUNCH   : [alone] 0:0@61..62#2
+                PUNCH   : [joint] 0:0@63..64#2
+                PUNCH   : [alone] 0:0@64..65#2
+                IDENT   std 0:0@65..68#2
+                PUNCH   : [joint] 0:0@68..69#2
+                PUNCH   : [alone] 0:0@69..70#2
+                IDENT   collections 0:0@70..81#2
+                PUNCH   : [joint] 0:0@81..82#2
+                PUNCH   : [alone] 0:0@82..83#2
+                IDENT   HashSet 0:0@83..90#2
+                PUNCH   < [alone] 0:0@90..91#2
+                SUBTREE () 0:0@91..92#2 0:0@92..93#2
+                PUNCH   > [joint] 0:0@93..94#2
+                PUNCH   , [alone] 0:0@94..95#2
 
             struct MyTraitMap2 {
                 map: ::std::collections::HashSet<()>,
@@ -153,28 +162,28 @@ fn main() {
 }
 "#,
         expect![[r#"
-            SUBTREE $$ 1:0@0..63#0 1:0@0..63#0
-              IDENT   fn 1:0@1..3#0
-              IDENT   main 1:0@4..8#0
-              SUBTREE () 1:0@8..9#0 1:0@9..10#0
-              SUBTREE {} 1:0@11..12#0 1:0@61..62#0
-                LITERAL Integer 1 1:0@17..18#0
-                PUNCH   ; [alone] 1:0@18..19#0
-                LITERAL Float 1.0 1:0@24..27#0
-                PUNCH   ; [alone] 1:0@27..28#0
-                SUBTREE () 1:0@33..34#0 1:0@39..40#0
-                  SUBTREE () 1:0@34..35#0 1:0@37..38#0
-                    LITERAL Integer 1 1:0@35..36#0
-                    PUNCH   , [alone] 1:0@36..37#0
-                  PUNCH   , [alone] 1:0@38..39#0
-                PUNCH   . [alone] 1:0@40..41#0
-                LITERAL Float 0.0 1:0@41..44#0
-                PUNCH   ; [alone] 1:0@44..45#0
-                IDENT   let 1:0@50..53#0
-                IDENT   x 1:0@54..55#0
-                PUNCH   = [alone] 1:0@56..57#0
-                LITERAL Integer 1 1:0@58..59#0
-                PUNCH   ; [alone] 1:0@59..60#0
+            SUBTREE $$ 1:0@0..63#2 1:0@0..63#2
+              IDENT   fn 1:0@1..3#2
+              IDENT   main 1:0@4..8#2
+              SUBTREE () 1:0@8..9#2 1:0@9..10#2
+              SUBTREE {} 1:0@11..12#2 1:0@61..62#2
+                LITERAL Integer 1 1:0@17..18#2
+                PUNCH   ; [alone] 1:0@18..19#2
+                LITERAL Float 1.0 1:0@24..27#2
+                PUNCH   ; [alone] 1:0@27..28#2
+                SUBTREE () 1:0@33..34#2 1:0@39..40#2
+                  SUBTREE () 1:0@34..35#2 1:0@37..38#2
+                    LITERAL Integer 1 1:0@35..36#2
+                    PUNCH   , [alone] 1:0@36..37#2
+                  PUNCH   , [alone] 1:0@38..39#2
+                PUNCH   . [alone] 1:0@40..41#2
+                LITERAL Float 0.0 1:0@41..44#2
+                PUNCH   ; [alone] 1:0@44..45#2
+                IDENT   let 1:0@50..53#2
+                IDENT   x 1:0@54..55#2
+                PUNCH   = [alone] 1:0@56..57#2
+                LITERAL Integer 1 1:0@58..59#2
+                PUNCH   ; [alone] 1:0@59..60#2
 
             fn main(){
                 1;
@@ -200,14 +209,14 @@ fn expr_2021() {
     const { 1 },
 "#,
         expect![[r#"
-            SUBTREE $$ 1:0@0..25#0 1:0@0..25#0
-              IDENT   _ 1:0@5..6#0
-              PUNCH   ; [joint] 0:0@36..37#0
-              SUBTREE () 0:0@34..35#0 0:0@34..35#0
-                IDENT   const 1:0@12..17#0
-                SUBTREE {} 1:0@18..19#0 1:0@22..23#0
-                  LITERAL Integer 1 1:0@20..21#0
-              PUNCH   ; [alone] 0:0@39..40#0
+            SUBTREE $$ 1:0@0..25#2 1:0@0..25#2
+              IDENT   _ 1:0@5..6#2
+              PUNCH   ; [joint] 0:0@36..37#2
+              SUBTREE () 0:0@34..35#2 0:0@34..35#2
+                IDENT   const 1:0@12..17#2
+                SUBTREE {} 1:0@18..19#2 1:0@22..23#2
+                  LITERAL Integer 1 1:0@20..21#2
+              PUNCH   ; [alone] 0:0@39..40#2
 
             _;
             (const  {
@@ -228,13 +237,13 @@ fn expr_2021() {
         expect![[r#"
             ExpandError {
                 inner: (
-                    1:0@5..6#0,
+                    1:0@5..6#2,
                     NoMatchingRule,
                 ),
             }
 
-            SUBTREE $$ 1:0@0..8#0 1:0@0..8#0
-              PUNCH   ; [alone] 0:0@39..40#0
+            SUBTREE $$ 1:0@0..8#2 1:0@0..8#2
+              PUNCH   ; [alone] 0:0@39..40#2
 
             ;"#]],
     );
@@ -252,13 +261,13 @@ fn expr_2021() {
         expect![[r#"
             ExpandError {
                 inner: (
-                    1:0@5..10#0,
+                    1:0@5..10#2,
                     NoMatchingRule,
                 ),
             }
 
-            SUBTREE $$ 1:0@0..18#0 1:0@0..18#0
-              PUNCH   ; [alone] 0:0@39..40#0
+            SUBTREE $$ 1:0@0..18#2 1:0@0..18#2
+              PUNCH   ; [alone] 0:0@39..40#2
 
             ;"#]],
     );
@@ -278,26 +287,26 @@ fn expr_2021() {
     break 'foo bar,
 "#,
         expect![[r#"
-            SUBTREE $$ 1:0@0..76#0 1:0@0..76#0
-              LITERAL Integer 4 1:0@5..6#0
-              PUNCH   ; [joint] 0:0@41..42#0
-              LITERAL Str literal 1:0@12..21#0
-              PUNCH   ; [joint] 0:0@41..42#0
-              SUBTREE () 0:0@39..40#0 0:0@39..40#0
-                IDENT   funcall 1:0@27..34#0
-                SUBTREE () 1:0@34..35#0 1:0@35..36#0
-              PUNCH   ; [joint] 0:0@41..42#0
-              SUBTREE () 0:0@39..40#0 0:0@39..40#0
-                IDENT   future 1:0@42..48#0
-                PUNCH   . [alone] 1:0@48..49#0
-                IDENT   await 1:0@49..54#0
-              PUNCH   ; [joint] 0:0@41..42#0
-              SUBTREE () 0:0@39..40#0 0:0@39..40#0
-                IDENT   break 1:0@60..65#0
-                PUNCH   ' [joint] 1:0@66..67#0
-                IDENT   foo 1:0@67..70#0
-                IDENT   bar 1:0@71..74#0
-              PUNCH   ; [alone] 0:0@44..45#0
+            SUBTREE $$ 1:0@0..76#2 1:0@0..76#2
+              LITERAL Integer 4 1:0@5..6#2
+              PUNCH   ; [joint] 0:0@41..42#2
+              LITERAL Str literal 1:0@12..21#2
+              PUNCH   ; [joint] 0:0@41..42#2
+              SUBTREE () 0:0@39..40#2 0:0@39..40#2
+                IDENT   funcall 1:0@27..34#2
+                SUBTREE () 1:0@34..35#2 1:0@35..36#2
+              PUNCH   ; [joint] 0:0@41..42#2
+              SUBTREE () 0:0@39..40#2 0:0@39..40#2
+                IDENT   future 1:0@42..48#2
+                PUNCH   . [alone] 1:0@48..49#2
+                IDENT   await 1:0@49..54#2
+              PUNCH   ; [joint] 0:0@41..42#2
+              SUBTREE () 0:0@39..40#2 0:0@39..40#2
+                IDENT   break 1:0@60..65#2
+                PUNCH   ' [joint] 1:0@66..67#2
+                IDENT   foo 1:0@67..70#2
+                IDENT   bar 1:0@71..74#2
+              PUNCH   ; [alone] 0:0@44..45#2
 
             4;
             "literal";
@@ -319,13 +328,13 @@ fn expr_2021() {
         expect![[r#"
             ExpandError {
                 inner: (
-                    1:0@5..6#0,
+                    1:0@5..6#2,
                     NoMatchingRule,
                 ),
             }
 
-            SUBTREE $$ 1:0@0..8#0 1:0@0..8#0
-              PUNCH   ; [alone] 0:0@44..45#0
+            SUBTREE $$ 1:0@0..8#2 1:0@0..8#2
+              PUNCH   ; [alone] 0:0@44..45#2
 
             ;"#]],
     );
diff --git a/src/tools/rust-analyzer/crates/parser/src/event.rs b/src/tools/rust-analyzer/crates/parser/src/event.rs
index e38571dd3ec64..b197b086f377a 100644
--- a/src/tools/rust-analyzer/crates/parser/src/event.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/event.rs
@@ -12,7 +12,7 @@ use crate::{
 /// `Parser` produces a flat list of `Event`s.
 /// They are converted to a tree-structure in
 /// a separate pass, via `TreeBuilder`.
-#[derive(Debug)]
+#[derive(Debug, PartialEq)]
 pub(crate) enum Event {
     /// This event signifies the start of the node.
     /// It should be either abandoned (in which case the
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
index 3b3f11be1307d..fe1316c9bfde3 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
@@ -134,10 +134,12 @@ pub(super) fn let_stmt(p: &mut Parser<'_>, with_semi: Semicolon) {
         // test_err let_else_right_curly_brace
         // fn func() { let Some(_) = {Some(1)} else { panic!("h") };}
         if let Some(expr) = expr_after_eq {
-            if BlockLike::is_blocklike(expr.kind()) {
-                p.error(
-                    "right curly brace `}` before `else` in a `let...else` statement not allowed",
-                )
+            if let Some(token) = expr.last_token(p) {
+                if token == T!['}'] {
+                    p.error(
+                        "right curly brace `}` before `else` in a `let...else` statement not allowed"
+                    )
+                }
             }
         }
 
@@ -339,13 +341,20 @@ fn lhs(p: &mut Parser<'_>, r: Restrictions) -> Option<(CompletedMarker, BlockLik
         //     // raw reference operator
         //     let _ = &raw mut foo;
         //     let _ = &raw const foo;
+        //     let _ = &raw foo;
         // }
         T![&] => {
             m = p.start();
             p.bump(T![&]);
-            if p.at_contextual_kw(T![raw]) && [T![mut], T![const]].contains(&p.nth(1)) {
-                p.bump_remap(T![raw]);
-                p.bump_any();
+            if p.at_contextual_kw(T![raw]) {
+                if [T![mut], T![const]].contains(&p.nth(1)) {
+                    p.bump_remap(T![raw]);
+                    p.bump_any();
+                } else if p.nth_at(1, SyntaxKind::IDENT) {
+                    // we treat raw as keyword in this case
+                    // &raw foo;
+                    p.bump_remap(T![raw]);
+                }
             } else {
                 p.eat(T![mut]);
             }
@@ -669,6 +678,8 @@ fn path_expr(p: &mut Parser<'_>, r: Restrictions) -> (CompletedMarker, BlockLike
 //     S { x };
 //     S { x, y: 32, };
 //     S { x, y: 32, ..Default::default() };
+//     S { x, y: 32, .. };
+//     S { .. };
 //     S { x: ::default() };
 //     TupleStruct { 0: 1 };
 // }
@@ -700,6 +711,8 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) {
                 // fn main() {
                 //     S { field ..S::default() }
                 //     S { 0 ..S::default() }
+                //     S { field .. }
+                //     S { 0 .. }
                 // }
                 name_ref_or_index(p);
                 p.error("expected `:`");
@@ -730,7 +743,13 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) {
                 //     S { .. } = S {};
                 // }
 
-                // We permit `.. }` on the left-hand side of a destructuring assignment.
+                // test struct_initializer_with_defaults
+                // fn foo() {
+                //     let _s = S { .. };
+                // }
+
+                // We permit `.. }` on the left-hand side of a destructuring assignment
+                // or defaults values.
                 if !p.at(T!['}']) {
                     expr(p);
 
@@ -741,6 +760,12 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) {
                         //     S { ..x, a: 0 }
                         // }
 
+                        // test_err comma_after_default_values_syntax
+                        // fn foo() {
+                        //     S { .., };
+                        //     S { .., a: 0 }
+                        // }
+
                         // Do not bump, so we can support additional fields after this comma.
                         p.error("cannot use a comma after the base struct");
                     }
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs
index 21078175c0ec0..9a16c9db6daf1 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs
@@ -135,6 +135,11 @@ pub(crate) fn record_field_list(p: &mut Parser<'_>) {
             name(p);
             p.expect(T![:]);
             types::type_(p);
+            // test record_field_default_values
+            // struct S { f: f32 = 0.0 }
+            if p.eat(T![=]) {
+                expressions::expr(p);
+            }
             m.complete(p, RECORD_FIELD);
         } else {
             m.abandon(p);
diff --git a/src/tools/rust-analyzer/crates/parser/src/input.rs b/src/tools/rust-analyzer/crates/parser/src/input.rs
index 9504bd4d9ec83..c90b358cfbb44 100644
--- a/src/tools/rust-analyzer/crates/parser/src/input.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/input.rs
@@ -72,7 +72,7 @@ impl Input {
     }
     pub(crate) fn is_joint(&self, n: usize) -> bool {
         let (idx, b_idx) = self.bit_index(n);
-        self.joint[idx] & 1 << b_idx != 0
+        self.joint[idx] & (1 << b_idx) != 0
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/parser/src/output.rs b/src/tools/rust-analyzer/crates/parser/src/output.rs
index 41d4c68b2d748..386d03a62cc1a 100644
--- a/src/tools/rust-analyzer/crates/parser/src/output.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/output.rs
@@ -85,7 +85,7 @@ impl Output {
     }
 
     pub(crate) fn float_split_hack(&mut self, ends_in_dot: bool) {
-        let e = (Self::SPLIT_EVENT as u32) << Self::TAG_SHIFT
+        let e = ((Self::SPLIT_EVENT as u32) << Self::TAG_SHIFT)
             | ((ends_in_dot as u32) << Self::N_INPUT_TOKEN_SHIFT)
             | Self::EVENT_MASK;
         self.event.push(e);
@@ -99,7 +99,7 @@ impl Output {
     }
 
     pub(crate) fn leave_node(&mut self) {
-        let e = (Self::EXIT_EVENT as u32) << Self::TAG_SHIFT | Self::EVENT_MASK;
+        let e = ((Self::EXIT_EVENT as u32) << Self::TAG_SHIFT) | Self::EVENT_MASK;
         self.event.push(e)
     }
 
diff --git a/src/tools/rust-analyzer/crates/parser/src/parser.rs b/src/tools/rust-analyzer/crates/parser/src/parser.rs
index 75a75f601cf11..2f6ba525747c3 100644
--- a/src/tools/rust-analyzer/crates/parser/src/parser.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/parser.rs
@@ -318,7 +318,8 @@ impl Marker {
             _ => unreachable!(),
         }
         p.push_event(Event::Finish);
-        CompletedMarker::new(self.pos, kind)
+        let end_pos = p.events.len() as u32;
+        CompletedMarker::new(self.pos, end_pos, kind)
     }
 
     /// Abandons the syntax tree node. All its children
@@ -336,13 +337,14 @@ impl Marker {
 }
 
 pub(crate) struct CompletedMarker {
-    pos: u32,
+    start_pos: u32,
+    end_pos: u32,
     kind: SyntaxKind,
 }
 
 impl CompletedMarker {
-    fn new(pos: u32, kind: SyntaxKind) -> Self {
-        CompletedMarker { pos, kind }
+    fn new(start_pos: u32, end_pos: u32, kind: SyntaxKind) -> Self {
+        CompletedMarker { start_pos, end_pos, kind }
     }
 
     /// This method allows to create a new node which starts
@@ -360,10 +362,10 @@ impl CompletedMarker {
     /// distance to `NEWSTART` into forward_parent(=2 in this case);
     pub(crate) fn precede(self, p: &mut Parser<'_>) -> Marker {
         let new_pos = p.start();
-        let idx = self.pos as usize;
+        let idx = self.start_pos as usize;
         match &mut p.events[idx] {
             Event::Start { forward_parent, .. } => {
-                *forward_parent = Some(new_pos.pos - self.pos);
+                *forward_parent = Some(new_pos.pos - self.start_pos);
             }
             _ => unreachable!(),
         }
@@ -376,7 +378,7 @@ impl CompletedMarker {
         let idx = m.pos as usize;
         match &mut p.events[idx] {
             Event::Start { forward_parent, .. } => {
-                *forward_parent = Some(self.pos - m.pos);
+                *forward_parent = Some(self.start_pos - m.pos);
             }
             _ => unreachable!(),
         }
@@ -386,4 +388,13 @@ impl CompletedMarker {
     pub(crate) fn kind(&self) -> SyntaxKind {
         self.kind
     }
+
+    pub(crate) fn last_token(&self, p: &Parser<'_>) -> Option {
+        let end_pos = self.end_pos as usize;
+        debug_assert_eq!(p.events[end_pos - 1], Event::Finish);
+        p.events[..end_pos].iter().rev().find_map(|event| match event {
+            Event::Token { kind, .. } => Some(*kind),
+            _ => None,
+        })
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs b/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs
index 7adedba7c4382..32569d5c3fe92 100644
--- a/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs
@@ -5,7 +5,7 @@
 //! abstract token parsing, and string tokenization as completely separate
 //! layers.
 //!
-//! However, often you do pares text into syntax trees and the glue code for
+//! However, often you do parse text into syntax trees and the glue code for
 //! that needs to live somewhere. Rather than putting it to lexer or parser, we
 //! use a separate shortcuts module for that.
 
diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
index 0c9c6ffd715e9..79900425a17cc 100644
--- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
@@ -1,4 +1,4 @@
-//! Generated by `cargo codegen grammar`, do not edit by hand.
+//! Generated by `cargo xtask codegen grammar`, do not edit by hand.
 
 #![allow(bad_style, missing_docs, unreachable_pub)]
 use crate::Edition;
@@ -331,6 +331,331 @@ pub enum SyntaxKind {
 }
 use self::SyntaxKind::*;
 impl SyntaxKind {
+    #[allow(unreachable_patterns)]
+    pub const fn text(self) -> &'static str {
+        match self {
+            TOMBSTONE
+            | EOF
+            | __LAST
+            | BYTE
+            | BYTE_STRING
+            | CHAR
+            | C_STRING
+            | FLOAT_NUMBER
+            | INT_NUMBER
+            | RAW_BYTE_STRING
+            | RAW_C_STRING
+            | RAW_STRING
+            | STRING
+            | ABI
+            | ADT
+            | ARG_LIST
+            | ARRAY_EXPR
+            | ARRAY_TYPE
+            | ASM_CLOBBER_ABI
+            | ASM_CONST
+            | ASM_DIR_SPEC
+            | ASM_EXPR
+            | ASM_LABEL
+            | ASM_OPERAND
+            | ASM_OPERAND_EXPR
+            | ASM_OPERAND_NAMED
+            | ASM_OPTION
+            | ASM_OPTIONS
+            | ASM_PIECE
+            | ASM_REG_OPERAND
+            | ASM_REG_SPEC
+            | ASM_SYM
+            | ASSOC_ITEM
+            | ASSOC_ITEM_LIST
+            | ASSOC_TYPE_ARG
+            | ATTR
+            | AWAIT_EXPR
+            | BECOME_EXPR
+            | BIN_EXPR
+            | BLOCK_EXPR
+            | BOX_PAT
+            | BREAK_EXPR
+            | CALL_EXPR
+            | CAST_EXPR
+            | CLOSURE_BINDER
+            | CLOSURE_EXPR
+            | CONST
+            | CONST_ARG
+            | CONST_BLOCK_PAT
+            | CONST_PARAM
+            | CONTINUE_EXPR
+            | DYN_TRAIT_TYPE
+            | ENUM
+            | EXPR
+            | EXPR_STMT
+            | EXTERN_BLOCK
+            | EXTERN_CRATE
+            | EXTERN_ITEM
+            | EXTERN_ITEM_LIST
+            | FIELD_EXPR
+            | FIELD_LIST
+            | FN
+            | FN_PTR_TYPE
+            | FORMAT_ARGS_ARG
+            | FORMAT_ARGS_EXPR
+            | FOR_EXPR
+            | FOR_TYPE
+            | GENERIC_ARG
+            | GENERIC_ARG_LIST
+            | GENERIC_PARAM
+            | GENERIC_PARAM_LIST
+            | IDENT_PAT
+            | IF_EXPR
+            | IMPL
+            | IMPL_TRAIT_TYPE
+            | INDEX_EXPR
+            | INFER_TYPE
+            | ITEM
+            | ITEM_LIST
+            | LABEL
+            | LET_ELSE
+            | LET_EXPR
+            | LET_STMT
+            | LIFETIME
+            | LIFETIME_ARG
+            | LIFETIME_PARAM
+            | LITERAL
+            | LITERAL_PAT
+            | LOOP_EXPR
+            | MACRO_CALL
+            | MACRO_DEF
+            | MACRO_EXPR
+            | MACRO_ITEMS
+            | MACRO_PAT
+            | MACRO_RULES
+            | MACRO_STMTS
+            | MACRO_TYPE
+            | MATCH_ARM
+            | MATCH_ARM_LIST
+            | MATCH_EXPR
+            | MATCH_GUARD
+            | META
+            | METHOD_CALL_EXPR
+            | MODULE
+            | NAME
+            | NAME_REF
+            | NEVER_TYPE
+            | OFFSET_OF_EXPR
+            | OR_PAT
+            | PARAM
+            | PARAM_LIST
+            | PARENTHESIZED_ARG_LIST
+            | PAREN_EXPR
+            | PAREN_PAT
+            | PAREN_TYPE
+            | PAT
+            | PATH
+            | PATH_EXPR
+            | PATH_PAT
+            | PATH_SEGMENT
+            | PATH_TYPE
+            | PREFIX_EXPR
+            | PTR_TYPE
+            | RANGE_EXPR
+            | RANGE_PAT
+            | RECORD_EXPR
+            | RECORD_EXPR_FIELD
+            | RECORD_EXPR_FIELD_LIST
+            | RECORD_FIELD
+            | RECORD_FIELD_LIST
+            | RECORD_PAT
+            | RECORD_PAT_FIELD
+            | RECORD_PAT_FIELD_LIST
+            | REF_EXPR
+            | REF_PAT
+            | REF_TYPE
+            | RENAME
+            | REST_PAT
+            | RETURN_EXPR
+            | RETURN_TYPE_SYNTAX
+            | RET_TYPE
+            | SELF_PARAM
+            | SLICE_PAT
+            | SLICE_TYPE
+            | SOURCE_FILE
+            | STATIC
+            | STMT
+            | STMT_LIST
+            | STRUCT
+            | TOKEN_TREE
+            | TRAIT
+            | TRAIT_ALIAS
+            | TRY_EXPR
+            | TUPLE_EXPR
+            | TUPLE_FIELD
+            | TUPLE_FIELD_LIST
+            | TUPLE_PAT
+            | TUPLE_STRUCT_PAT
+            | TUPLE_TYPE
+            | TYPE
+            | TYPE_ALIAS
+            | TYPE_ARG
+            | TYPE_BOUND
+            | TYPE_BOUND_LIST
+            | TYPE_PARAM
+            | UNDERSCORE_EXPR
+            | UNION
+            | USE
+            | USE_BOUND_GENERIC_ARG
+            | USE_BOUND_GENERIC_ARGS
+            | USE_TREE
+            | USE_TREE_LIST
+            | VARIANT
+            | VARIANT_LIST
+            | VISIBILITY
+            | WHERE_CLAUSE
+            | WHERE_PRED
+            | WHILE_EXPR
+            | WILDCARD_PAT
+            | YEET_EXPR
+            | YIELD_EXPR
+            | COMMENT
+            | ERROR
+            | IDENT
+            | LIFETIME_IDENT
+            | NEWLINE
+            | SHEBANG
+            | WHITESPACE => panic!("no text for these `SyntaxKind`s"),
+            DOLLAR => "$",
+            SEMICOLON => ";",
+            COMMA => ",",
+            L_PAREN => "(",
+            R_PAREN => ")",
+            L_CURLY => "{",
+            R_CURLY => "}",
+            L_BRACK => "[",
+            R_BRACK => "]",
+            L_ANGLE => "<",
+            R_ANGLE => ">",
+            AT => "@",
+            POUND => "#",
+            TILDE => "~",
+            QUESTION => "?",
+            AMP => "&",
+            PIPE => "|",
+            PLUS => "+",
+            STAR => "*",
+            SLASH => "/",
+            CARET => "^",
+            PERCENT => "%",
+            UNDERSCORE => "_",
+            DOT => ".",
+            DOT2 => "..",
+            DOT3 => "...",
+            DOT2EQ => "..=",
+            COLON => ":",
+            COLON2 => "::",
+            EQ => "=",
+            EQ2 => "==",
+            FAT_ARROW => "=>",
+            BANG => "!",
+            NEQ => "!=",
+            MINUS => "-",
+            THIN_ARROW => "->",
+            LTEQ => "<=",
+            GTEQ => ">=",
+            PLUSEQ => "+=",
+            MINUSEQ => "-=",
+            PIPEEQ => "|=",
+            AMPEQ => "&=",
+            CARETEQ => "^=",
+            SLASHEQ => "/=",
+            STAREQ => "*=",
+            PERCENTEQ => "%=",
+            AMP2 => "&&",
+            PIPE2 => "||",
+            SHL => "<<",
+            SHR => ">>",
+            SHLEQ => "<<=",
+            SHREQ => ">>=",
+            SELF_TYPE_KW => "Self",
+            ABSTRACT_KW => "abstract",
+            AS_KW => "as",
+            BECOME_KW => "become",
+            BOX_KW => "box",
+            BREAK_KW => "break",
+            CONST_KW => "const",
+            CONTINUE_KW => "continue",
+            CRATE_KW => "crate",
+            DO_KW => "do",
+            ELSE_KW => "else",
+            ENUM_KW => "enum",
+            EXTERN_KW => "extern",
+            FALSE_KW => "false",
+            FINAL_KW => "final",
+            FN_KW => "fn",
+            FOR_KW => "for",
+            IF_KW => "if",
+            IMPL_KW => "impl",
+            IN_KW => "in",
+            LET_KW => "let",
+            LOOP_KW => "loop",
+            MACRO_KW => "macro",
+            MATCH_KW => "match",
+            MOD_KW => "mod",
+            MOVE_KW => "move",
+            MUT_KW => "mut",
+            OVERRIDE_KW => "override",
+            PRIV_KW => "priv",
+            PUB_KW => "pub",
+            REF_KW => "ref",
+            RETURN_KW => "return",
+            SELF_KW => "self",
+            STATIC_KW => "static",
+            STRUCT_KW => "struct",
+            SUPER_KW => "super",
+            TRAIT_KW => "trait",
+            TRUE_KW => "true",
+            TYPE_KW => "type",
+            TYPEOF_KW => "typeof",
+            UNSAFE_KW => "unsafe",
+            UNSIZED_KW => "unsized",
+            USE_KW => "use",
+            VIRTUAL_KW => "virtual",
+            WHERE_KW => "where",
+            WHILE_KW => "while",
+            YIELD_KW => "yield",
+            ASM_KW => "asm",
+            ATT_SYNTAX_KW => "att_syntax",
+            AUTO_KW => "auto",
+            BUILTIN_KW => "builtin",
+            CLOBBER_ABI_KW => "clobber_abi",
+            DEFAULT_KW => "default",
+            DYN_KW => "dyn",
+            FORMAT_ARGS_KW => "format_args",
+            INLATEOUT_KW => "inlateout",
+            INOUT_KW => "inout",
+            LABEL_KW => "label",
+            LATEOUT_KW => "lateout",
+            MACRO_RULES_KW => "macro_rules",
+            MAY_UNWIND_KW => "may_unwind",
+            NOMEM_KW => "nomem",
+            NORETURN_KW => "noreturn",
+            NOSTACK_KW => "nostack",
+            OFFSET_OF_KW => "offset_of",
+            OPTIONS_KW => "options",
+            OUT_KW => "out",
+            PRESERVES_FLAGS_KW => "preserves_flags",
+            PURE_KW => "pure",
+            RAW_KW => "raw",
+            READONLY_KW => "readonly",
+            SAFE_KW => "safe",
+            SYM_KW => "sym",
+            UNION_KW => "union",
+            YEET_KW => "yeet",
+            ASYNC_KW => "async",
+            AWAIT_KW => "await",
+            DYN_KW => "dyn",
+            GEN_KW => "gen",
+            TRY_KW => "try",
+        }
+    }
     #[doc = r" Checks whether this syntax kind is a strict keyword for the given edition."]
     #[doc = r" Strict keywords are identifiers that are always considered keywords."]
     pub fn is_strict_keyword(self, edition: Edition) -> bool {
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
index b9f87b6af2421..c8ea8c547a98b 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
@@ -482,6 +482,10 @@ mod ok {
         run_and_expect_no_errors("test_data/parser/inline/ok/record_field_attrs.rs");
     }
     #[test]
+    fn record_field_default_values() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/record_field_default_values.rs");
+    }
+    #[test]
     fn record_field_list() {
         run_and_expect_no_errors("test_data/parser/inline/ok/record_field_list.rs");
     }
@@ -544,6 +548,10 @@ mod ok {
         run_and_expect_no_errors("test_data/parser/inline/ok/stmt_postfix_expr_ambiguity.rs");
     }
     #[test]
+    fn struct_initializer_with_defaults() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/struct_initializer_with_defaults.rs");
+    }
+    #[test]
     fn struct_item() { run_and_expect_no_errors("test_data/parser/inline/ok/struct_item.rs"); }
     #[test]
     fn trait_alias() { run_and_expect_no_errors("test_data/parser/inline/ok/trait_alias.rs"); }
@@ -719,6 +727,10 @@ mod err {
         );
     }
     #[test]
+    fn comma_after_default_values_syntax() {
+        run_and_expect_errors("test_data/parser/inline/err/comma_after_default_values_syntax.rs");
+    }
+    #[test]
     fn crate_visibility_empty_recover() {
         run_and_expect_errors("test_data/parser/inline/err/crate_visibility_empty_recover.rs");
     }
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rast
new file mode 100644
index 0000000000000..578dc2b0f9663
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rast
@@ -0,0 +1,79 @@
+SOURCE_FILE
+  STRUCT
+    STRUCT_KW "struct"
+    WHITESPACE " "
+    NAME
+      IDENT "X"
+    WHITESPACE " "
+    RECORD_FIELD_LIST
+      L_CURLY "{"
+      RECORD_FIELD
+        NAME
+          IDENT "a"
+        COLON ":"
+        WHITESPACE " "
+        PATH_TYPE
+          PATH
+            PATH_SEGMENT
+              NAME_REF
+                IDENT "i32"
+      R_CURLY "}"
+  WHITESPACE "\n"
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "f"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          IDENT_PAT
+            NAME
+              IDENT "foo"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          RECORD_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "X"
+            WHITESPACE " "
+            RECORD_EXPR_FIELD_LIST
+              L_CURLY "{"
+              WHITESPACE "\n        "
+              RECORD_EXPR_FIELD
+                NAME_REF
+                  IDENT "a"
+                COLON ":"
+                WHITESPACE " "
+                LITERAL
+                  INT_NUMBER "1"
+              WHITESPACE "\n    "
+              R_CURLY "}"
+          WHITESPACE " "
+          LET_ELSE
+            ELSE_KW "else"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n        "
+                EXPR_STMT
+                  RETURN_EXPR
+                    RETURN_KW "return"
+                  SEMICOLON ";"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+error 63: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rs
new file mode 100644
index 0000000000000..c0c0edc983017
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rs
@@ -0,0 +1,8 @@
+struct X {a: i32}
+fn f() {
+    let foo = X {
+        a: 1
+    } else {
+        return;
+    };
+}
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rast
new file mode 100644
index 0000000000000..8e994f22d41b3
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rast
@@ -0,0 +1,42 @@
+SOURCE_FILE
+  ERROR
+    LET_KW "let"
+    WHITESPACE " "
+    IDENT_PAT
+      NAME
+        IDENT "foo"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    BIN_EXPR
+      LITERAL
+        INT_NUMBER "1"
+      WHITESPACE " "
+      PLUS "+"
+      WHITESPACE " "
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          LITERAL
+            INT_NUMBER "1"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    WHITESPACE " "
+    LET_ELSE
+      ELSE_KW "else"
+      WHITESPACE " "
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          EXPR_STMT
+            RETURN_EXPR
+              RETURN_KW "return"
+            SEMICOLON ";"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+error 0: expected an item
+error 23: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rs
new file mode 100644
index 0000000000000..c29ddcce1ff50
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rs
@@ -0,0 +1,5 @@
+let foo = 1 + {
+    1
+} else {
+    return;
+};
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rast
new file mode 100644
index 0000000000000..055b583acec81
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rast
@@ -0,0 +1,90 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "r"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          IDENT_PAT
+            NAME
+              IDENT "ok"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          MACRO_EXPR
+            MACRO_CALL
+              PATH
+                PATH_SEGMENT
+                  NAME_REF
+                    IDENT "format_args"
+              BANG "!"
+              TOKEN_TREE
+                L_PAREN "("
+                STRING "\"\""
+                R_PAREN ")"
+          WHITESPACE " "
+          LET_ELSE
+            ELSE_KW "else"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE " "
+                EXPR_STMT
+                  RETURN_EXPR
+                    RETURN_KW "return"
+                  SEMICOLON ";"
+                WHITESPACE " "
+                R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          IDENT_PAT
+            NAME
+              IDENT "bad"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          MACRO_EXPR
+            MACRO_CALL
+              PATH
+                PATH_SEGMENT
+                  NAME_REF
+                    IDENT "format_args"
+              BANG "!"
+              WHITESPACE " "
+              TOKEN_TREE
+                L_CURLY "{"
+                STRING "\"\""
+                R_CURLY "}"
+          WHITESPACE " "
+          LET_ELSE
+            ELSE_KW "else"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE " "
+                EXPR_STMT
+                  RETURN_EXPR
+                    RETURN_KW "return"
+                  SEMICOLON ";"
+                WHITESPACE " "
+                R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+  WHITESPACE "\n"
+error 89: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rs
new file mode 100644
index 0000000000000..5916fa07dc2ff
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rs
@@ -0,0 +1,5 @@
+fn r() {
+    let ok = format_args!("") else { return; };
+
+    let bad = format_args! {""} else { return; };
+}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rast
new file mode 100644
index 0000000000000..8c7fd8c295d95
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rast
@@ -0,0 +1,40 @@
+SOURCE_FILE
+  ERROR
+    LET_KW "let"
+    WHITESPACE " "
+    IDENT_PAT
+      NAME
+        IDENT "foo"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    RANGE_EXPR
+      LITERAL
+        INT_NUMBER "1"
+      DOT2 ".."
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          LITERAL
+            INT_NUMBER "1"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    WHITESPACE " "
+    LET_ELSE
+      ELSE_KW "else"
+      WHITESPACE " "
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          EXPR_STMT
+            RETURN_EXPR
+              RETURN_KW "return"
+            SEMICOLON ";"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+error 0: expected an item
+error 22: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rs
new file mode 100644
index 0000000000000..5417131d28eac
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rs
@@ -0,0 +1,5 @@
+let foo = 1..{
+    1
+} else {
+    return;
+};
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rast
new file mode 100644
index 0000000000000..57925a0d192f2
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rast
@@ -0,0 +1,55 @@
+SOURCE_FILE
+  ERROR
+    LET_KW "let"
+    WHITESPACE " "
+    IDENT_PAT
+      NAME
+        IDENT "foo"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    CLOSURE_EXPR
+      PARAM_LIST
+        PIPE "|"
+        PARAM
+          IDENT_PAT
+            NAME
+              IDENT "x"
+          COLON ":"
+          WHITESPACE " "
+          PATH_TYPE
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "i32"
+        PIPE "|"
+      WHITESPACE " "
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          PATH_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "x"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    WHITESPACE " "
+    LET_ELSE
+      ELSE_KW "else"
+      WHITESPACE " "
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          EXPR_STMT
+            RETURN_EXPR
+              RETURN_KW "return"
+            SEMICOLON ";"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+error 0: expected an item
+error 28: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rs
new file mode 100644
index 0000000000000..89c7579b071f1
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rs
@@ -0,0 +1,5 @@
+let foo = |x: i32| {
+    x
+} else {
+    return;
+};
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rast
new file mode 100644
index 0000000000000..4fb70bd50e386
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rast
@@ -0,0 +1,38 @@
+SOURCE_FILE
+  ERROR
+    LET_KW "let"
+    WHITESPACE " "
+    IDENT_PAT
+      NAME
+        IDENT "foo"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    PREFIX_EXPR
+      MINUS "-"
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          LITERAL
+            INT_NUMBER "1"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    WHITESPACE " "
+    LET_ELSE
+      ELSE_KW "else"
+      WHITESPACE " "
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          EXPR_STMT
+            RETURN_EXPR
+              RETURN_KW "return"
+            SEMICOLON ";"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+error 0: expected an item
+error 20: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rs
new file mode 100644
index 0000000000000..1ba7f7d761bae
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rs
@@ -0,0 +1,5 @@
+let foo = -{
+    1
+} else {
+    return;
+};
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rast
new file mode 100644
index 0000000000000..e8eeeee695e9c
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rast
@@ -0,0 +1,90 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "o"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    RET_TYPE
+      THIN_ARROW "->"
+      WHITESPACE " "
+      PATH_TYPE
+        PATH
+          PATH_SEGMENT
+            NAME_REF
+              IDENT "Result"
+            GENERIC_ARG_LIST
+              L_ANGLE "<"
+              TYPE_ARG
+                TUPLE_TYPE
+                  L_PAREN "("
+                  R_PAREN ")"
+              COMMA ","
+              WHITESPACE " "
+              TYPE_ARG
+                TUPLE_TYPE
+                  L_PAREN "("
+                  R_PAREN ")"
+              R_ANGLE ">"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          IDENT_PAT
+            NAME
+              IDENT "foo"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          YEET_EXPR
+            DO_KW "do"
+            WHITESPACE " "
+            YEET_KW "yeet"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n        "
+                TUPLE_EXPR
+                  L_PAREN "("
+                  R_PAREN ")"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+          WHITESPACE " "
+          LET_ELSE
+            ELSE_KW "else"
+            WHITESPACE " "
+            BLOCK_EXPR
+              STMT_LIST
+                L_CURLY "{"
+                WHITESPACE "\n        "
+                EXPR_STMT
+                  RETURN_EXPR
+                    RETURN_KW "return"
+                    WHITESPACE " "
+                    CALL_EXPR
+                      PATH_EXPR
+                        PATH
+                          PATH_SEGMENT
+                            NAME_REF
+                              IDENT "Ok"
+                      ARG_LIST
+                        L_PAREN "("
+                        TUPLE_EXPR
+                          L_PAREN "("
+                          R_PAREN ")"
+                        R_PAREN ")"
+                  SEMICOLON ";"
+                WHITESPACE "\n    "
+                R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+error 67: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rs
new file mode 100644
index 0000000000000..188fb07d91b4c
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rs
@@ -0,0 +1,7 @@
+fn o() -> Result<(), ()> {
+    let foo = do yeet {
+        ()
+    } else {
+        return Ok(());
+    };
+}
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rast
new file mode 100644
index 0000000000000..cc5e1278c3de7
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rast
@@ -0,0 +1,40 @@
+SOURCE_FILE
+  ERROR
+    LET_KW "let"
+    WHITESPACE " "
+    IDENT_PAT
+      NAME
+        IDENT "foo"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    BECOME_EXPR
+      BECOME_KW "become"
+      WHITESPACE " "
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          TUPLE_EXPR
+            L_PAREN "("
+            R_PAREN ")"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    WHITESPACE " "
+    LET_ELSE
+      ELSE_KW "else"
+      WHITESPACE " "
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          EXPR_STMT
+            RETURN_EXPR
+              RETURN_KW "return"
+            SEMICOLON ";"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+error 0: expected an item
+error 27: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rs
new file mode 100644
index 0000000000000..622548b8f33ca
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rs
@@ -0,0 +1,5 @@
+let foo = become {
+    ()
+} else {
+    return;
+};
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rast
new file mode 100644
index 0000000000000..ea2f4f28e2d81
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rast
@@ -0,0 +1,38 @@
+SOURCE_FILE
+  ERROR
+    LET_KW "let"
+    WHITESPACE " "
+    IDENT_PAT
+      NAME
+        IDENT "foo"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    REF_EXPR
+      AMP "&"
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          LITERAL
+            INT_NUMBER "1"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    WHITESPACE " "
+    LET_ELSE
+      ELSE_KW "else"
+      WHITESPACE " "
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          EXPR_STMT
+            RETURN_EXPR
+              RETURN_KW "return"
+            SEMICOLON ";"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+error 0: expected an item
+error 20: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rs
new file mode 100644
index 0000000000000..9a00dca3689fe
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rs
@@ -0,0 +1,5 @@
+let foo = &{
+    1
+} else {
+    return;
+};
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rast
new file mode 100644
index 0000000000000..47396140c5c36
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rast
@@ -0,0 +1,45 @@
+SOURCE_FILE
+  ERROR
+    LET_KW "let"
+    WHITESPACE " "
+    IDENT_PAT
+      NAME
+        IDENT "foo"
+    WHITESPACE " "
+    EQ "="
+    WHITESPACE " "
+    BIN_EXPR
+      PATH_EXPR
+        PATH
+          PATH_SEGMENT
+            NAME_REF
+              IDENT "bar"
+      WHITESPACE " "
+      EQ "="
+      WHITESPACE " "
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          LITERAL
+            INT_NUMBER "1"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    WHITESPACE " "
+    LET_ELSE
+      ELSE_KW "else"
+      WHITESPACE " "
+      BLOCK_EXPR
+        STMT_LIST
+          L_CURLY "{"
+          WHITESPACE "\n    "
+          EXPR_STMT
+            RETURN_EXPR
+              RETURN_KW "return"
+            SEMICOLON ";"
+          WHITESPACE "\n"
+          R_CURLY "}"
+    SEMICOLON ";"
+  WHITESPACE "\n"
+error 0: expected an item
+error 25: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rs
new file mode 100644
index 0000000000000..08e677416f10d
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rs
@@ -0,0 +1,5 @@
+let foo = bar = {
+    1
+} else {
+    return;
+};
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast
new file mode 100644
index 0000000000000..feb617e1aa2ab
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast
@@ -0,0 +1,59 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "foo"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          RECORD_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "S"
+            WHITESPACE " "
+            RECORD_EXPR_FIELD_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              DOT2 ".."
+              ERROR
+                COMMA ","
+              WHITESPACE " "
+              R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n    "
+        RECORD_EXPR
+          PATH
+            PATH_SEGMENT
+              NAME_REF
+                IDENT "S"
+          WHITESPACE " "
+          RECORD_EXPR_FIELD_LIST
+            L_CURLY "{"
+            WHITESPACE " "
+            DOT2 ".."
+            ERROR
+              COMMA ","
+            WHITESPACE " "
+            RECORD_EXPR_FIELD
+              NAME_REF
+                IDENT "a"
+              COLON ":"
+              WHITESPACE " "
+              LITERAL
+                INT_NUMBER "0"
+            WHITESPACE " "
+            R_CURLY "}"
+        WHITESPACE "\n"
+        R_CURLY "}"
+  WHITESPACE "\n"
+error 21: expected expression
+error 36: expected expression
+error 37: expected COMMA
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs
new file mode 100644
index 0000000000000..f1ecdf89fab17
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs
@@ -0,0 +1,4 @@
+fn foo() {
+    S { .., };
+    S { .., a: 0 }
+}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast
index 08ae906421c30..12b4e233e3044 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast
@@ -44,6 +44,56 @@ SOURCE_FILE
               WHITESPACE " "
               R_CURLY "}"
         WHITESPACE "\n    "
+        EXPR_STMT
+          RECORD_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "S"
+            WHITESPACE " "
+            RECORD_EXPR_FIELD_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              RECORD_EXPR_FIELD
+                NAME_REF
+                  INT_NUMBER "0"
+              WHITESPACE " "
+              DOT2 ".."
+              CALL_EXPR
+                PATH_EXPR
+                  PATH
+                    PATH
+                      PATH_SEGMENT
+                        NAME_REF
+                          IDENT "S"
+                    COLON2 "::"
+                    PATH_SEGMENT
+                      NAME_REF
+                        IDENT "default"
+                ARG_LIST
+                  L_PAREN "("
+                  R_PAREN ")"
+              WHITESPACE " "
+              R_CURLY "}"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          RECORD_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "S"
+            WHITESPACE " "
+            RECORD_EXPR_FIELD_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              RECORD_EXPR_FIELD
+                NAME_REF
+                  IDENT "field"
+              WHITESPACE " "
+              DOT2 ".."
+              WHITESPACE " "
+              R_CURLY "}"
+        WHITESPACE "\n    "
         RECORD_EXPR
           PATH
             PATH_SEGMENT
@@ -58,20 +108,6 @@ SOURCE_FILE
                 INT_NUMBER "0"
             WHITESPACE " "
             DOT2 ".."
-            CALL_EXPR
-              PATH_EXPR
-                PATH
-                  PATH
-                    PATH_SEGMENT
-                      NAME_REF
-                        IDENT "S"
-                  COLON2 "::"
-                  PATH_SEGMENT
-                    NAME_REF
-                      IDENT "default"
-              ARG_LIST
-                L_PAREN "("
-                R_PAREN ")"
             WHITESPACE " "
             R_CURLY "}"
         WHITESPACE "\n"
@@ -82,3 +118,9 @@ error 25: expected COMMA
 error 42: expected SEMICOLON
 error 52: expected `:`
 error 52: expected COMMA
+error 69: expected SEMICOLON
+error 83: expected `:`
+error 83: expected COMMA
+error 88: expected SEMICOLON
+error 98: expected `:`
+error 98: expected COMMA
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs
index 65398ccb88e5d..416cd763fdb56 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs
@@ -1,4 +1,6 @@
 fn main() {
     S { field ..S::default() }
     S { 0 ..S::default() }
+    S { field .. }
+    S { 0 .. }
 }
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast
new file mode 100644
index 0000000000000..33088f2cabf39
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast
@@ -0,0 +1,28 @@
+SOURCE_FILE
+  STRUCT
+    STRUCT_KW "struct"
+    WHITESPACE " "
+    NAME
+      IDENT "S"
+    WHITESPACE " "
+    RECORD_FIELD_LIST
+      L_CURLY "{"
+      WHITESPACE " "
+      RECORD_FIELD
+        NAME
+          IDENT "f"
+        COLON ":"
+        WHITESPACE " "
+        PATH_TYPE
+          PATH
+            PATH_SEGMENT
+              NAME_REF
+                IDENT "f32"
+        WHITESPACE " "
+        EQ "="
+        WHITESPACE " "
+        LITERAL
+          FLOAT_NUMBER "0.0"
+      WHITESPACE " "
+      R_CURLY "}"
+  WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs
new file mode 100644
index 0000000000000..d7b38944a8aa8
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs
@@ -0,0 +1 @@
+struct S { f: f32 = 0.0 }
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast
index 00948c322f4c9..b868da55bcea7 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast
@@ -120,6 +120,53 @@ SOURCE_FILE
               R_CURLY "}"
           SEMICOLON ";"
         WHITESPACE "\n    "
+        EXPR_STMT
+          RECORD_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "S"
+            WHITESPACE " "
+            RECORD_EXPR_FIELD_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              RECORD_EXPR_FIELD
+                PATH_EXPR
+                  PATH
+                    PATH_SEGMENT
+                      NAME_REF
+                        IDENT "x"
+              COMMA ","
+              WHITESPACE " "
+              RECORD_EXPR_FIELD
+                NAME_REF
+                  IDENT "y"
+                COLON ":"
+                WHITESPACE " "
+                LITERAL
+                  INT_NUMBER "32"
+              COMMA ","
+              WHITESPACE " "
+              DOT2 ".."
+              WHITESPACE " "
+              R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          RECORD_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "S"
+            WHITESPACE " "
+            RECORD_EXPR_FIELD_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              DOT2 ".."
+              WHITESPACE " "
+              R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n    "
         EXPR_STMT
           RECORD_EXPR
             PATH
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs
index 86411fbb7dc05..42895f759b2be 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs
@@ -3,6 +3,8 @@ fn foo() {
     S { x };
     S { x, y: 32, };
     S { x, y: 32, ..Default::default() };
+    S { x, y: 32, .. };
+    S { .. };
     S { x: ::default() };
     TupleStruct { 0: 1 };
 }
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_expr.rast
index 108b0802c3345..8dc916e5cc541 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_expr.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_expr.rast
@@ -134,6 +134,25 @@ SOURCE_FILE
                   NAME_REF
                     IDENT "foo"
           SEMICOLON ";"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          WILDCARD_PAT
+            UNDERSCORE "_"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          REF_EXPR
+            AMP "&"
+            RAW_KW "raw"
+            WHITESPACE " "
+            PATH_EXPR
+              PATH
+                PATH_SEGMENT
+                  NAME_REF
+                    IDENT "foo"
+          SEMICOLON ";"
         WHITESPACE "\n"
         R_CURLY "}"
   WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_expr.rs
index c5262f4469b06..31a2485b439be 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_expr.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/ref_expr.rs
@@ -7,4 +7,5 @@ fn foo() {
     // raw reference operator
     let _ = &raw mut foo;
     let _ = &raw const foo;
+    let _ = &raw foo;
 }
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast
new file mode 100644
index 0000000000000..987e219ae822b
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast
@@ -0,0 +1,39 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "foo"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        LET_STMT
+          LET_KW "let"
+          WHITESPACE " "
+          IDENT_PAT
+            NAME
+              IDENT "_s"
+          WHITESPACE " "
+          EQ "="
+          WHITESPACE " "
+          RECORD_EXPR
+            PATH
+              PATH_SEGMENT
+                NAME_REF
+                  IDENT "S"
+            WHITESPACE " "
+            RECORD_EXPR_FIELD_LIST
+              L_CURLY "{"
+              WHITESPACE " "
+              DOT2 ".."
+              WHITESPACE " "
+              R_CURLY "}"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+  WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs
new file mode 100644
index 0000000000000..e08204f94c4b4
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs
@@ -0,0 +1,3 @@
+fn foo() {
+    let _s = S { .. };
+}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/json.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/json.rs
similarity index 100%
rename from src/tools/rust-analyzer/crates/proc-macro-api/src/json.rs
rename to src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/json.rs
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs
new file mode 100644
index 0000000000000..4b831e4acebb9
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs
@@ -0,0 +1,296 @@
+//! Defines messages for cross-process message passing based on `ndjson` wire protocol
+pub(crate) mod flat;
+
+use std::io::{self, BufRead, Write};
+
+use paths::Utf8PathBuf;
+use serde::de::DeserializeOwned;
+use serde_derive::{Deserialize, Serialize};
+
+use crate::ProcMacroKind;
+
+pub use self::flat::{
+    deserialize_span_data_index_map, serialize_span_data_index_map, FlatTree, SpanDataIndexMap,
+};
+pub use span::TokenId;
+
+// The versions of the server protocol
+pub const NO_VERSION_CHECK_VERSION: u32 = 0;
+pub const VERSION_CHECK_VERSION: u32 = 1;
+pub const ENCODE_CLOSE_SPAN_VERSION: u32 = 2;
+pub const HAS_GLOBAL_SPANS: u32 = 3;
+pub const RUST_ANALYZER_SPAN_SUPPORT: u32 = 4;
+/// Whether literals encode their kind as an additional u32 field and idents their rawness as a u32 field
+pub const EXTENDED_LEAF_DATA: u32 = 5;
+
+pub const CURRENT_API_VERSION: u32 = EXTENDED_LEAF_DATA;
+
+#[derive(Debug, Serialize, Deserialize)]
+pub enum Request {
+    /// Since [`NO_VERSION_CHECK_VERSION`]
+    ListMacros { dylib_path: Utf8PathBuf },
+    /// Since [`NO_VERSION_CHECK_VERSION`]
+    ExpandMacro(Box),
+    /// Since [`VERSION_CHECK_VERSION`]
+    ApiVersionCheck {},
+    /// Since [`RUST_ANALYZER_SPAN_SUPPORT`]
+    SetConfig(ServerConfig),
+}
+
+#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize)]
+pub enum SpanMode {
+    #[default]
+    Id,
+    RustAnalyzer,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub enum Response {
+    /// Since [`NO_VERSION_CHECK_VERSION`]
+    ListMacros(Result, String>),
+    /// Since [`NO_VERSION_CHECK_VERSION`]
+    ExpandMacro(Result),
+    /// Since [`NO_VERSION_CHECK_VERSION`]
+    ApiVersionCheck(u32),
+    /// Since [`RUST_ANALYZER_SPAN_SUPPORT`]
+    SetConfig(ServerConfig),
+    /// Since [`RUST_ANALYZER_SPAN_SUPPORT`]
+    ExpandMacroExtended(Result),
+}
+
+#[derive(Debug, Serialize, Deserialize, Default)]
+#[serde(default)]
+pub struct ServerConfig {
+    pub span_mode: SpanMode,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct ExpandMacroExtended {
+    pub tree: FlatTree,
+    pub span_data_table: Vec,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct PanicMessage(pub String);
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct ExpandMacro {
+    pub lib: Utf8PathBuf,
+    /// Environment variables to set during macro expansion.
+    pub env: Vec<(String, String)>,
+    pub current_dir: Option,
+    #[serde(flatten)]
+    pub data: ExpandMacroData,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct ExpandMacroData {
+    /// Argument of macro call.
+    ///
+    /// In custom derive this will be a struct or enum; in attribute-like macro - underlying
+    /// item; in function-like macro - the macro body.
+    pub macro_body: FlatTree,
+
+    /// Name of macro to expand.
+    ///
+    /// In custom derive this is the name of the derived trait (`Serialize`, `Getters`, etc.).
+    /// In attribute-like and function-like macros - single name of macro itself (`show_streams`).
+    pub macro_name: String,
+
+    /// Possible attributes for the attribute-like macros.
+    pub attributes: Option,
+    /// marker for serde skip stuff
+    #[serde(skip_serializing_if = "ExpnGlobals::skip_serializing_if")]
+    #[serde(default)]
+    pub has_global_spans: ExpnGlobals,
+    #[serde(skip_serializing_if = "Vec::is_empty")]
+    #[serde(default)]
+    pub span_data_table: Vec,
+}
+
+#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize)]
+pub struct ExpnGlobals {
+    #[serde(skip_serializing)]
+    #[serde(default)]
+    pub serialize: bool,
+    pub def_site: usize,
+    pub call_site: usize,
+    pub mixed_site: usize,
+}
+
+impl ExpnGlobals {
+    fn skip_serializing_if(&self) -> bool {
+        !self.serialize
+    }
+}
+
+pub trait Message: serde::Serialize + DeserializeOwned {
+    fn read(
+        from_proto: ProtocolRead,
+        inp: &mut R,
+        buf: &mut String,
+    ) -> io::Result> {
+        Ok(match from_proto(inp, buf)? {
+            None => None,
+            Some(text) => {
+                let mut deserializer = serde_json::Deserializer::from_str(text);
+                // Note that some proc-macro generate very deep syntax tree
+                // We have to disable the current limit of serde here
+                deserializer.disable_recursion_limit();
+                Some(Self::deserialize(&mut deserializer)?)
+            }
+        })
+    }
+    fn write(self, to_proto: ProtocolWrite, out: &mut W) -> io::Result<()> {
+        let text = serde_json::to_string(&self)?;
+        to_proto(out, &text)
+    }
+}
+
+impl Message for Request {}
+impl Message for Response {}
+
+#[allow(type_alias_bounds)]
+type ProtocolRead =
+    for<'i, 'buf> fn(inp: &'i mut R, buf: &'buf mut String) -> io::Result>;
+#[allow(type_alias_bounds)]
+type ProtocolWrite = for<'o, 'msg> fn(out: &'o mut W, msg: &'msg str) -> io::Result<()>;
+
+#[cfg(test)]
+mod tests {
+    use intern::{sym, Symbol};
+    use span::{Edition, ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, TextRange, TextSize};
+    use tt::{
+        Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, Spacing, TopSubtree,
+        TopSubtreeBuilder,
+    };
+
+    use super::*;
+
+    fn fixture_token_tree() -> TopSubtree {
+        let anchor = SpanAnchor {
+            file_id: span::EditionedFileId::new(
+                span::FileId::from_raw(0xe4e4e),
+                span::Edition::CURRENT,
+            ),
+            ast_id: ErasedFileAstId::from_raw(0),
+        };
+
+        let mut builder = TopSubtreeBuilder::new(Delimiter {
+            open: Span {
+                range: TextRange::empty(TextSize::new(0)),
+                anchor,
+                ctx: SyntaxContextId::root(Edition::CURRENT),
+            },
+            close: Span {
+                range: TextRange::empty(TextSize::new(19)),
+                anchor,
+                ctx: SyntaxContextId::root(Edition::CURRENT),
+            },
+            kind: DelimiterKind::Invisible,
+        });
+
+        builder.push(
+            Ident {
+                sym: Symbol::intern("struct"),
+                span: Span {
+                    range: TextRange::at(TextSize::new(0), TextSize::of("struct")),
+                    anchor,
+                    ctx: SyntaxContextId::root(Edition::CURRENT),
+                },
+                is_raw: tt::IdentIsRaw::No,
+            }
+            .into(),
+        );
+        builder.push(
+            Ident {
+                sym: Symbol::intern("Foo"),
+                span: Span {
+                    range: TextRange::at(TextSize::new(5), TextSize::of("r#Foo")),
+                    anchor,
+                    ctx: SyntaxContextId::root(Edition::CURRENT),
+                },
+                is_raw: tt::IdentIsRaw::Yes,
+            }
+            .into(),
+        );
+        builder.push(Leaf::Literal(Literal {
+            symbol: Symbol::intern("Foo"),
+            span: Span {
+                range: TextRange::at(TextSize::new(10), TextSize::of("\"Foo\"")),
+                anchor,
+                ctx: SyntaxContextId::root(Edition::CURRENT),
+            },
+            kind: tt::LitKind::Str,
+            suffix: None,
+        }));
+        builder.push(Leaf::Punct(Punct {
+            char: '@',
+            span: Span {
+                range: TextRange::at(TextSize::new(13), TextSize::of('@')),
+                anchor,
+                ctx: SyntaxContextId::root(Edition::CURRENT),
+            },
+            spacing: Spacing::Joint,
+        }));
+        builder.open(
+            DelimiterKind::Brace,
+            Span {
+                range: TextRange::at(TextSize::new(14), TextSize::of('{')),
+                anchor,
+                ctx: SyntaxContextId::root(Edition::CURRENT),
+            },
+        );
+        builder.push(Leaf::Literal(Literal {
+            symbol: sym::INTEGER_0.clone(),
+            span: Span {
+                range: TextRange::at(TextSize::new(15), TextSize::of("0u32")),
+                anchor,
+                ctx: SyntaxContextId::root(Edition::CURRENT),
+            },
+            kind: tt::LitKind::Integer,
+            suffix: Some(sym::u32.clone()),
+        }));
+        builder.close(Span {
+            range: TextRange::at(TextSize::new(19), TextSize::of('}')),
+            anchor,
+            ctx: SyntaxContextId::root(Edition::CURRENT),
+        });
+
+        builder.build()
+    }
+
+    #[test]
+    fn test_proc_macro_rpc_works() {
+        let tt = fixture_token_tree();
+        for v in RUST_ANALYZER_SPAN_SUPPORT..=CURRENT_API_VERSION {
+            let mut span_data_table = Default::default();
+            let task = ExpandMacro {
+                data: ExpandMacroData {
+                    macro_body: FlatTree::new(tt.view(), v, &mut span_data_table),
+                    macro_name: Default::default(),
+                    attributes: None,
+                    has_global_spans: ExpnGlobals {
+                        serialize: true,
+                        def_site: 0,
+                        call_site: 0,
+                        mixed_site: 0,
+                    },
+                    span_data_table: Vec::new(),
+                },
+                lib: Utf8PathBuf::from_path_buf(std::env::current_dir().unwrap()).unwrap(),
+                env: Default::default(),
+                current_dir: Default::default(),
+            };
+
+            let json = serde_json::to_string(&task).unwrap();
+            // println!("{}", json);
+            let back: ExpandMacro = serde_json::from_str(&json).unwrap();
+
+            assert!(
+                tt == back.data.macro_body.to_subtree_resolved(v, &span_data_table),
+                "version: {v}"
+            );
+        }
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs
new file mode 100644
index 0000000000000..c194f301714fc
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs
@@ -0,0 +1,628 @@
+//! Serialization-friendly representation of `tt::TopSubtree`.
+//!
+//! It is possible to serialize `TopSubtree` recursively, as a tree, but using
+//! arbitrary-nested trees in JSON is problematic, as they can cause the JSON
+//! parser to overflow the stack.
+//!
+//! Additionally, such implementation would be pretty verbose, and we do care
+//! about performance here a bit.
+//!
+//! So what this module does is dumping a `tt::TopSubtree` into a bunch of flat
+//! array of numbers. See the test in the parent module to get an example
+//! output.
+//!
+//! ```json
+//!  {
+//!    // Array of subtrees, each subtree is represented by 4 numbers:
+//!    // id of delimiter, delimiter kind, index of first child in `token_tree`,
+//!    // index of last child in `token_tree`
+//!    "subtree":[4294967295,0,0,5,2,2,5,5],
+//!    // 2 ints per literal: [token id, index into `text`]
+//!    "literal":[4294967295,1],
+//!    // 3 ints per punct: [token id, char, spacing]
+//!    "punct":[4294967295,64,1],
+//!    // 2 ints per ident: [token id, index into `text`]
+//!    "ident":   [0,0,1,1],
+//!    // children of all subtrees, concatenated. Each child is represented as `index << 2 | tag`
+//!    // where tag denotes one of subtree, literal, punct or ident.
+//!    "token_tree":[3,7,1,4],
+//!    // Strings shared by idents and literals
+//!    "text": ["struct","Foo"]
+//!  }
+//! ```
+//!
+//! We probably should replace most of the code here with bincode someday, but,
+//! as we don't have bincode in Cargo.toml yet, lets stick with serde_json for
+//! the time being.
+
+use std::collections::VecDeque;
+
+use intern::Symbol;
+use rustc_hash::FxHashMap;
+use serde_derive::{Deserialize, Serialize};
+use span::{
+    EditionedFileId, ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, TextRange, TokenId,
+};
+
+use crate::legacy_protocol::msg::{ENCODE_CLOSE_SPAN_VERSION, EXTENDED_LEAF_DATA};
+
+pub type SpanDataIndexMap =
+    indexmap::IndexSet>;
+
+pub fn serialize_span_data_index_map(map: &SpanDataIndexMap) -> Vec {
+    map.iter()
+        .flat_map(|span| {
+            [
+                span.anchor.file_id.as_u32(),
+                span.anchor.ast_id.into_raw(),
+                span.range.start().into(),
+                span.range.end().into(),
+                span.ctx.into_u32(),
+            ]
+        })
+        .collect()
+}
+
+pub fn deserialize_span_data_index_map(map: &[u32]) -> SpanDataIndexMap {
+    debug_assert!(map.len() % 5 == 0);
+    map.chunks_exact(5)
+        .map(|span| {
+            let &[file_id, ast_id, start, end, e] = span else { unreachable!() };
+            Span {
+                anchor: SpanAnchor {
+                    file_id: EditionedFileId::from_raw(file_id),
+                    ast_id: ErasedFileAstId::from_raw(ast_id),
+                },
+                range: TextRange::new(start.into(), end.into()),
+                ctx: SyntaxContextId::from_u32(e),
+            }
+        })
+        .collect()
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct FlatTree {
+    subtree: Vec,
+    literal: Vec,
+    punct: Vec,
+    ident: Vec,
+    token_tree: Vec,
+    text: Vec,
+}
+
+struct SubtreeRepr {
+    open: TokenId,
+    close: TokenId,
+    kind: tt::DelimiterKind,
+    tt: [u32; 2],
+}
+
+struct LiteralRepr {
+    id: TokenId,
+    text: u32,
+    suffix: u32,
+    kind: u16,
+}
+
+struct PunctRepr {
+    id: TokenId,
+    char: char,
+    spacing: tt::Spacing,
+}
+
+struct IdentRepr {
+    id: TokenId,
+    text: u32,
+    is_raw: bool,
+}
+
+impl FlatTree {
+    pub fn new(
+        subtree: tt::SubtreeView<'_, Span>,
+        version: u32,
+        span_data_table: &mut SpanDataIndexMap,
+    ) -> FlatTree {
+        let mut w = Writer {
+            string_table: FxHashMap::default(),
+            work: VecDeque::new(),
+            span_data_table,
+
+            subtree: Vec::new(),
+            literal: Vec::new(),
+            punct: Vec::new(),
+            ident: Vec::new(),
+            token_tree: Vec::new(),
+            text: Vec::new(),
+            version,
+        };
+        w.write(subtree);
+
+        FlatTree {
+            subtree: if version >= ENCODE_CLOSE_SPAN_VERSION {
+                write_vec(w.subtree, SubtreeRepr::write_with_close_span)
+            } else {
+                write_vec(w.subtree, SubtreeRepr::write)
+            },
+            literal: if version >= EXTENDED_LEAF_DATA {
+                write_vec(w.literal, LiteralRepr::write_with_kind)
+            } else {
+                write_vec(w.literal, LiteralRepr::write)
+            },
+            punct: write_vec(w.punct, PunctRepr::write),
+            ident: if version >= EXTENDED_LEAF_DATA {
+                write_vec(w.ident, IdentRepr::write_with_rawness)
+            } else {
+                write_vec(w.ident, IdentRepr::write)
+            },
+            token_tree: w.token_tree,
+            text: w.text,
+        }
+    }
+
+    pub fn new_raw(subtree: tt::SubtreeView<'_, TokenId>, version: u32) -> FlatTree {
+        let mut w = Writer {
+            string_table: FxHashMap::default(),
+            work: VecDeque::new(),
+            span_data_table: &mut (),
+
+            subtree: Vec::new(),
+            literal: Vec::new(),
+            punct: Vec::new(),
+            ident: Vec::new(),
+            token_tree: Vec::new(),
+            text: Vec::new(),
+            version,
+        };
+        w.write(subtree);
+
+        FlatTree {
+            subtree: if version >= ENCODE_CLOSE_SPAN_VERSION {
+                write_vec(w.subtree, SubtreeRepr::write_with_close_span)
+            } else {
+                write_vec(w.subtree, SubtreeRepr::write)
+            },
+            literal: if version >= EXTENDED_LEAF_DATA {
+                write_vec(w.literal, LiteralRepr::write_with_kind)
+            } else {
+                write_vec(w.literal, LiteralRepr::write)
+            },
+            punct: write_vec(w.punct, PunctRepr::write),
+            ident: if version >= EXTENDED_LEAF_DATA {
+                write_vec(w.ident, IdentRepr::write_with_rawness)
+            } else {
+                write_vec(w.ident, IdentRepr::write)
+            },
+            token_tree: w.token_tree,
+            text: w.text,
+        }
+    }
+
+    pub fn to_subtree_resolved(
+        self,
+        version: u32,
+        span_data_table: &SpanDataIndexMap,
+    ) -> tt::TopSubtree {
+        Reader {
+            subtree: if version >= ENCODE_CLOSE_SPAN_VERSION {
+                read_vec(self.subtree, SubtreeRepr::read_with_close_span)
+            } else {
+                read_vec(self.subtree, SubtreeRepr::read)
+            },
+            literal: if version >= EXTENDED_LEAF_DATA {
+                read_vec(self.literal, LiteralRepr::read_with_kind)
+            } else {
+                read_vec(self.literal, LiteralRepr::read)
+            },
+            punct: read_vec(self.punct, PunctRepr::read),
+            ident: if version >= EXTENDED_LEAF_DATA {
+                read_vec(self.ident, IdentRepr::read_with_rawness)
+            } else {
+                read_vec(self.ident, IdentRepr::read)
+            },
+            token_tree: self.token_tree,
+            text: self.text,
+            span_data_table,
+            version,
+        }
+        .read()
+    }
+
+    pub fn to_subtree_unresolved(self, version: u32) -> tt::TopSubtree {
+        Reader {
+            subtree: if version >= ENCODE_CLOSE_SPAN_VERSION {
+                read_vec(self.subtree, SubtreeRepr::read_with_close_span)
+            } else {
+                read_vec(self.subtree, SubtreeRepr::read)
+            },
+            literal: if version >= EXTENDED_LEAF_DATA {
+                read_vec(self.literal, LiteralRepr::read_with_kind)
+            } else {
+                read_vec(self.literal, LiteralRepr::read)
+            },
+            punct: read_vec(self.punct, PunctRepr::read),
+            ident: if version >= EXTENDED_LEAF_DATA {
+                read_vec(self.ident, IdentRepr::read_with_rawness)
+            } else {
+                read_vec(self.ident, IdentRepr::read)
+            },
+            token_tree: self.token_tree,
+            text: self.text,
+            span_data_table: &(),
+            version,
+        }
+        .read()
+    }
+}
+
+fn read_vec T, const N: usize>(xs: Vec, f: F) -> Vec {
+    let mut chunks = xs.chunks_exact(N);
+    let res = chunks.by_ref().map(|chunk| f(chunk.try_into().unwrap())).collect();
+    assert!(chunks.remainder().is_empty());
+    res
+}
+
+fn write_vec [u32; N], const N: usize>(xs: Vec, f: F) -> Vec {
+    xs.into_iter().flat_map(f).collect()
+}
+
+impl SubtreeRepr {
+    fn write(self) -> [u32; 4] {
+        let kind = match self.kind {
+            tt::DelimiterKind::Invisible => 0,
+            tt::DelimiterKind::Parenthesis => 1,
+            tt::DelimiterKind::Brace => 2,
+            tt::DelimiterKind::Bracket => 3,
+        };
+        [self.open.0, kind, self.tt[0], self.tt[1]]
+    }
+    fn read([open, kind, lo, len]: [u32; 4]) -> SubtreeRepr {
+        let kind = match kind {
+            0 => tt::DelimiterKind::Invisible,
+            1 => tt::DelimiterKind::Parenthesis,
+            2 => tt::DelimiterKind::Brace,
+            3 => tt::DelimiterKind::Bracket,
+            other => panic!("bad kind {other}"),
+        };
+        SubtreeRepr { open: TokenId(open), close: TokenId(!0), kind, tt: [lo, len] }
+    }
+    fn write_with_close_span(self) -> [u32; 5] {
+        let kind = match self.kind {
+            tt::DelimiterKind::Invisible => 0,
+            tt::DelimiterKind::Parenthesis => 1,
+            tt::DelimiterKind::Brace => 2,
+            tt::DelimiterKind::Bracket => 3,
+        };
+        [self.open.0, self.close.0, kind, self.tt[0], self.tt[1]]
+    }
+    fn read_with_close_span([open, close, kind, lo, len]: [u32; 5]) -> SubtreeRepr {
+        let kind = match kind {
+            0 => tt::DelimiterKind::Invisible,
+            1 => tt::DelimiterKind::Parenthesis,
+            2 => tt::DelimiterKind::Brace,
+            3 => tt::DelimiterKind::Bracket,
+            other => panic!("bad kind {other}"),
+        };
+        SubtreeRepr { open: TokenId(open), close: TokenId(close), kind, tt: [lo, len] }
+    }
+}
+
+impl LiteralRepr {
+    fn write(self) -> [u32; 2] {
+        [self.id.0, self.text]
+    }
+    fn read([id, text]: [u32; 2]) -> LiteralRepr {
+        LiteralRepr { id: TokenId(id), text, kind: 0, suffix: !0 }
+    }
+    fn write_with_kind(self) -> [u32; 4] {
+        [self.id.0, self.text, self.kind as u32, self.suffix]
+    }
+    fn read_with_kind([id, text, kind, suffix]: [u32; 4]) -> LiteralRepr {
+        LiteralRepr { id: TokenId(id), text, kind: kind as u16, suffix }
+    }
+}
+
+impl PunctRepr {
+    fn write(self) -> [u32; 3] {
+        let spacing = match self.spacing {
+            tt::Spacing::Alone | tt::Spacing::JointHidden => 0,
+            tt::Spacing::Joint => 1,
+        };
+        [self.id.0, self.char as u32, spacing]
+    }
+    fn read([id, char, spacing]: [u32; 3]) -> PunctRepr {
+        let spacing = match spacing {
+            0 => tt::Spacing::Alone,
+            1 => tt::Spacing::Joint,
+            other => panic!("bad spacing {other}"),
+        };
+        PunctRepr { id: TokenId(id), char: char.try_into().unwrap(), spacing }
+    }
+}
+
+impl IdentRepr {
+    fn write(self) -> [u32; 2] {
+        [self.id.0, self.text]
+    }
+    fn read(data: [u32; 2]) -> IdentRepr {
+        IdentRepr { id: TokenId(data[0]), text: data[1], is_raw: false }
+    }
+    fn write_with_rawness(self) -> [u32; 3] {
+        [self.id.0, self.text, self.is_raw as u32]
+    }
+    fn read_with_rawness([id, text, is_raw]: [u32; 3]) -> IdentRepr {
+        IdentRepr { id: TokenId(id), text, is_raw: is_raw == 1 }
+    }
+}
+
+trait InternableSpan: Copy {
+    type Table;
+    fn token_id_of(table: &mut Self::Table, s: Self) -> TokenId;
+    fn span_for_token_id(table: &Self::Table, id: TokenId) -> Self;
+}
+
+impl InternableSpan for TokenId {
+    type Table = ();
+    fn token_id_of((): &mut Self::Table, token_id: Self) -> TokenId {
+        token_id
+    }
+
+    fn span_for_token_id((): &Self::Table, id: TokenId) -> Self {
+        id
+    }
+}
+impl InternableSpan for Span {
+    type Table = SpanDataIndexMap;
+    fn token_id_of(table: &mut Self::Table, span: Self) -> TokenId {
+        TokenId(table.insert_full(span).0 as u32)
+    }
+    fn span_for_token_id(table: &Self::Table, id: TokenId) -> Self {
+        *table.get_index(id.0 as usize).unwrap_or_else(|| &table[0])
+    }
+}
+
+struct Writer<'a, 'span, S: InternableSpan> {
+    work: VecDeque<(usize, tt::iter::TtIter<'a, S>)>,
+    string_table: FxHashMap, u32>,
+    span_data_table: &'span mut S::Table,
+    version: u32,
+
+    subtree: Vec,
+    literal: Vec,
+    punct: Vec,
+    ident: Vec,
+    token_tree: Vec,
+    text: Vec,
+}
+
+impl<'a, S: InternableSpan> Writer<'a, '_, S> {
+    fn write(&mut self, root: tt::SubtreeView<'a, S>) {
+        let subtree = root.top_subtree();
+        self.enqueue(subtree, root.iter());
+        while let Some((idx, subtree)) = self.work.pop_front() {
+            self.subtree(idx, subtree);
+        }
+    }
+
+    fn token_id_of(&mut self, span: S) -> TokenId {
+        S::token_id_of(self.span_data_table, span)
+    }
+
+    fn subtree(&mut self, idx: usize, subtree: tt::iter::TtIter<'a, S>) {
+        let mut first_tt = self.token_tree.len();
+        let n_tt = subtree.clone().count(); // FIXME: `count()` walks over the entire iterator.
+        self.token_tree.resize(first_tt + n_tt, !0);
+
+        self.subtree[idx].tt = [first_tt as u32, (first_tt + n_tt) as u32];
+
+        for child in subtree {
+            let idx_tag = match child {
+                tt::iter::TtElement::Subtree(subtree, subtree_iter) => {
+                    let idx = self.enqueue(subtree, subtree_iter);
+                    idx << 2
+                }
+                tt::iter::TtElement::Leaf(leaf) => match leaf {
+                    tt::Leaf::Literal(lit) => {
+                        let idx = self.literal.len() as u32;
+                        let id = self.token_id_of(lit.span);
+                        let (text, suffix) = if self.version >= EXTENDED_LEAF_DATA {
+                            (
+                                self.intern(lit.symbol.as_str()),
+                                lit.suffix.as_ref().map(|s| self.intern(s.as_str())).unwrap_or(!0),
+                            )
+                        } else {
+                            (self.intern_owned(format!("{lit}")), !0)
+                        };
+                        self.literal.push(LiteralRepr {
+                            id,
+                            text,
+                            kind: u16::from_le_bytes(match lit.kind {
+                                tt::LitKind::Err(_) => [0, 0],
+                                tt::LitKind::Byte => [1, 0],
+                                tt::LitKind::Char => [2, 0],
+                                tt::LitKind::Integer => [3, 0],
+                                tt::LitKind::Float => [4, 0],
+                                tt::LitKind::Str => [5, 0],
+                                tt::LitKind::StrRaw(r) => [6, r],
+                                tt::LitKind::ByteStr => [7, 0],
+                                tt::LitKind::ByteStrRaw(r) => [8, r],
+                                tt::LitKind::CStr => [9, 0],
+                                tt::LitKind::CStrRaw(r) => [10, r],
+                            }),
+                            suffix,
+                        });
+                        (idx << 2) | 0b01
+                    }
+                    tt::Leaf::Punct(punct) => {
+                        let idx = self.punct.len() as u32;
+                        let id = self.token_id_of(punct.span);
+                        self.punct.push(PunctRepr { char: punct.char, spacing: punct.spacing, id });
+                        (idx << 2) | 0b10
+                    }
+                    tt::Leaf::Ident(ident) => {
+                        let idx = self.ident.len() as u32;
+                        let id = self.token_id_of(ident.span);
+                        let text = if self.version >= EXTENDED_LEAF_DATA {
+                            self.intern(ident.sym.as_str())
+                        } else if ident.is_raw.yes() {
+                            self.intern_owned(format!("r#{}", ident.sym.as_str(),))
+                        } else {
+                            self.intern(ident.sym.as_str())
+                        };
+                        self.ident.push(IdentRepr { id, text, is_raw: ident.is_raw.yes() });
+                        (idx << 2) | 0b11
+                    }
+                },
+            };
+            self.token_tree[first_tt] = idx_tag;
+            first_tt += 1;
+        }
+    }
+
+    fn enqueue(&mut self, subtree: &'a tt::Subtree, contents: tt::iter::TtIter<'a, S>) -> u32 {
+        let idx = self.subtree.len();
+        let open = self.token_id_of(subtree.delimiter.open);
+        let close = self.token_id_of(subtree.delimiter.close);
+        let delimiter_kind = subtree.delimiter.kind;
+        self.subtree.push(SubtreeRepr { open, close, kind: delimiter_kind, tt: [!0, !0] });
+        self.work.push_back((idx, contents));
+        idx as u32
+    }
+
+    pub(crate) fn intern(&mut self, text: &'a str) -> u32 {
+        let table = &mut self.text;
+        *self.string_table.entry(text.into()).or_insert_with(|| {
+            let idx = table.len();
+            table.push(text.to_owned());
+            idx as u32
+        })
+    }
+
+    pub(crate) fn intern_owned(&mut self, text: String) -> u32 {
+        let table = &mut self.text;
+        *self.string_table.entry(text.clone().into()).or_insert_with(|| {
+            let idx = table.len();
+            table.push(text);
+            idx as u32
+        })
+    }
+}
+
+struct Reader<'span, S: InternableSpan> {
+    version: u32,
+    subtree: Vec,
+    literal: Vec,
+    punct: Vec,
+    ident: Vec,
+    token_tree: Vec,
+    text: Vec,
+    span_data_table: &'span S::Table,
+}
+
+impl Reader<'_, S> {
+    pub(crate) fn read(self) -> tt::TopSubtree {
+        let mut res: Vec, Vec>)>> =
+            vec![None; self.subtree.len()];
+        let read_span = |id| S::span_for_token_id(self.span_data_table, id);
+        for i in (0..self.subtree.len()).rev() {
+            let repr = &self.subtree[i];
+            let token_trees = &self.token_tree[repr.tt[0] as usize..repr.tt[1] as usize];
+            let delimiter = tt::Delimiter {
+                open: read_span(repr.open),
+                close: read_span(repr.close),
+                kind: repr.kind,
+            };
+            let mut s = Vec::new();
+            for &idx_tag in token_trees {
+                let tag = idx_tag & 0b11;
+                let idx = (idx_tag >> 2) as usize;
+                match tag {
+                    // XXX: we iterate subtrees in reverse to guarantee
+                    // that this unwrap doesn't fire.
+                    0b00 => {
+                        let (delimiter, subtree) = res[idx].take().unwrap();
+                        s.push(tt::TokenTree::Subtree(tt::Subtree {
+                            delimiter,
+                            len: subtree.len() as u32,
+                        }));
+                        s.extend(subtree)
+                    }
+                    0b01 => {
+                        use tt::LitKind::*;
+                        let repr = &self.literal[idx];
+                        let text = self.text[repr.text as usize].as_str();
+                        let span = read_span(repr.id);
+                        s.push(
+                            tt::Leaf::Literal(if self.version >= EXTENDED_LEAF_DATA {
+                                tt::Literal {
+                                    symbol: Symbol::intern(text),
+                                    span,
+                                    kind: match u16::to_le_bytes(repr.kind) {
+                                        [0, _] => Err(()),
+                                        [1, _] => Byte,
+                                        [2, _] => Char,
+                                        [3, _] => Integer,
+                                        [4, _] => Float,
+                                        [5, _] => Str,
+                                        [6, r] => StrRaw(r),
+                                        [7, _] => ByteStr,
+                                        [8, r] => ByteStrRaw(r),
+                                        [9, _] => CStr,
+                                        [10, r] => CStrRaw(r),
+                                        _ => unreachable!(),
+                                    },
+                                    suffix: if repr.suffix != !0 {
+                                        Some(Symbol::intern(
+                                            self.text[repr.suffix as usize].as_str(),
+                                        ))
+                                    } else {
+                                        None
+                                    },
+                                }
+                            } else {
+                                tt::token_to_literal(text, span)
+                            })
+                            .into(),
+                        )
+                    }
+                    0b10 => {
+                        let repr = &self.punct[idx];
+                        s.push(
+                            tt::Leaf::Punct(tt::Punct {
+                                char: repr.char,
+                                spacing: repr.spacing,
+                                span: read_span(repr.id),
+                            })
+                            .into(),
+                        )
+                    }
+                    0b11 => {
+                        let repr = &self.ident[idx];
+                        let text = self.text[repr.text as usize].as_str();
+                        let (is_raw, text) = if self.version >= EXTENDED_LEAF_DATA {
+                            (
+                                if repr.is_raw { tt::IdentIsRaw::Yes } else { tt::IdentIsRaw::No },
+                                text,
+                            )
+                        } else {
+                            tt::IdentIsRaw::split_from_symbol(text)
+                        };
+                        s.push(
+                            tt::Leaf::Ident(tt::Ident {
+                                sym: Symbol::intern(text),
+                                span: read_span(repr.id),
+                                is_raw,
+                            })
+                            .into(),
+                        )
+                    }
+                    other => panic!("bad tag: {other}"),
+                }
+            }
+            res[i] = Some((delimiter, s));
+        }
+
+        let (delimiter, mut res) = res[0].take().unwrap();
+        res.insert(0, tt::TokenTree::Subtree(tt::Subtree { delimiter, len: res.len() as u32 }));
+        tt::TopSubtree(res.into_boxed_slice())
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
index e54d501b94ccf..dc3328ebcda48 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
@@ -5,26 +5,26 @@
 //! is used to provide basic infrastructure for communication between two
 //! processes: Client (RA itself), Server (the external program)
 
-pub mod json;
-pub mod msg;
+pub mod legacy_protocol {
+    pub mod json;
+    pub mod msg;
+}
 mod process;
 
 use paths::{AbsPath, AbsPathBuf};
 use span::Span;
 use std::{fmt, io, sync::Arc};
 
-use serde::{Deserialize, Serialize};
-
 use crate::{
-    msg::{
+    legacy_protocol::msg::{
         deserialize_span_data_index_map, flat::serialize_span_data_index_map, ExpandMacro,
-        ExpnGlobals, FlatTree, PanicMessage, SpanDataIndexMap, HAS_GLOBAL_SPANS,
-        RUST_ANALYZER_SPAN_SUPPORT,
+        ExpandMacroData, ExpnGlobals, FlatTree, PanicMessage, Request, Response, SpanDataIndexMap,
+        HAS_GLOBAL_SPANS, RUST_ANALYZER_SPAN_SUPPORT,
     },
-    process::ProcMacroProcessSrv,
+    process::ProcMacroServerProcess,
 };
 
-#[derive(Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
+#[derive(Copy, Clone, Eq, PartialEq, Debug, serde_derive::Serialize, serde_derive::Deserialize)]
 pub enum ProcMacroKind {
     CustomDerive,
     Attr,
@@ -37,12 +37,12 @@ pub enum ProcMacroKind {
 /// A handle to an external process which load dylibs with macros (.so or .dll)
 /// and runs actual macro expansion functions.
 #[derive(Debug)]
-pub struct ProcMacroServer {
+pub struct ProcMacroClient {
     /// Currently, the proc macro process expands all procedural macros sequentially.
     ///
     /// That means that concurrent salsa requests may block each other when expanding proc macros,
     /// which is unfortunate, but simple and good enough for the time being.
-    process: Arc,
+    process: Arc,
     path: AbsPathBuf,
 }
 
@@ -56,13 +56,13 @@ impl MacroDylib {
     }
 }
 
-/// A handle to a specific macro (a `#[proc_macro]` annotated function).
+/// A handle to a specific proc-macro (a `#[proc_macro]` annotated function).
 ///
-/// It exists within a context of a specific [`ProcMacroProcess`] -- currently
-/// we share a single expander process for all macros.
+/// It exists within the context of a specific proc-macro server -- currently
+/// we share a single expander process for all macros within a workspace.
 #[derive(Debug, Clone)]
 pub struct ProcMacro {
-    process: Arc,
+    process: Arc,
     dylib_path: Arc,
     name: Box,
     kind: ProcMacroKind,
@@ -95,21 +95,22 @@ impl fmt::Display for ServerError {
     }
 }
 
-impl ProcMacroServer {
+impl ProcMacroClient {
     /// Spawns an external process as the proc macro server and returns a client connected to it.
     pub fn spawn(
         process_path: &AbsPath,
         env: impl IntoIterator, impl AsRef)>
             + Clone,
-    ) -> io::Result {
-        let process = ProcMacroProcessSrv::run(process_path, env)?;
-        Ok(ProcMacroServer { process: Arc::new(process), path: process_path.to_owned() })
+    ) -> io::Result {
+        let process = ProcMacroServerProcess::run(process_path, env)?;
+        Ok(ProcMacroClient { process: Arc::new(process), path: process_path.to_owned() })
     }
 
-    pub fn path(&self) -> &AbsPath {
+    pub fn server_path(&self) -> &AbsPath {
         &self.path
     }
 
+    /// Loads a proc-macro dylib into the server process returning a list of `ProcMacro`s loaded.
     pub fn load_dylib(&self, dylib: MacroDylib) -> Result, ServerError> {
         let _p = tracing::info_span!("ProcMacroServer::load_dylib").entered();
         let macros = self.process.find_proc_macros(&dylib.path)?;
@@ -145,14 +146,14 @@ impl ProcMacro {
 
     pub fn expand(
         &self,
-        subtree: &tt::Subtree,
-        attr: Option<&tt::Subtree>,
+        subtree: tt::SubtreeView<'_, Span>,
+        attr: Option>,
         env: Vec<(String, String)>,
         def_site: Span,
         call_site: Span,
         mixed_site: Span,
         current_dir: Option,
-    ) -> Result, PanicMessage>, ServerError> {
+    ) -> Result, PanicMessage>, ServerError> {
         let version = self.process.version();
 
         let mut span_data_table = SpanDataIndexMap::default();
@@ -160,7 +161,7 @@ impl ProcMacro {
         let call_site = span_data_table.insert_full(call_site).0;
         let mixed_site = span_data_table.insert_full(mixed_site).0;
         let task = ExpandMacro {
-            data: msg::ExpandMacroData {
+            data: ExpandMacroData {
                 macro_body: FlatTree::new(subtree, version, &mut span_data_table),
                 macro_name: self.name.to_string(),
                 attributes: attr
@@ -182,13 +183,13 @@ impl ProcMacro {
             current_dir,
         };
 
-        let response = self.process.send_task(msg::Request::ExpandMacro(Box::new(task)))?;
+        let response = self.process.send_task(Request::ExpandMacro(Box::new(task)))?;
 
         match response {
-            msg::Response::ExpandMacro(it) => {
+            Response::ExpandMacro(it) => {
                 Ok(it.map(|tree| FlatTree::to_subtree_resolved(tree, version, &span_data_table)))
             }
-            msg::Response::ExpandMacroExtended(it) => Ok(it.map(|resp| {
+            Response::ExpandMacroExtended(it) => Ok(it.map(|resp| {
                 FlatTree::to_subtree_resolved(
                     resp.tree,
                     version,
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs
deleted file mode 100644
index bbd9f582df9ad..0000000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs
+++ /dev/null
@@ -1,299 +0,0 @@
-//! Defines messages for cross-process message passing based on `ndjson` wire protocol
-pub(crate) mod flat;
-
-use std::io::{self, BufRead, Write};
-
-use paths::Utf8PathBuf;
-use serde::de::DeserializeOwned;
-use serde_derive::{Deserialize, Serialize};
-
-use crate::ProcMacroKind;
-
-pub use crate::msg::flat::{
-    deserialize_span_data_index_map, serialize_span_data_index_map, FlatTree, SpanDataIndexMap,
-    TokenId,
-};
-
-// The versions of the server protocol
-pub const NO_VERSION_CHECK_VERSION: u32 = 0;
-pub const VERSION_CHECK_VERSION: u32 = 1;
-pub const ENCODE_CLOSE_SPAN_VERSION: u32 = 2;
-pub const HAS_GLOBAL_SPANS: u32 = 3;
-pub const RUST_ANALYZER_SPAN_SUPPORT: u32 = 4;
-/// Whether literals encode their kind as an additional u32 field and idents their rawness as a u32 field
-pub const EXTENDED_LEAF_DATA: u32 = 5;
-
-pub const CURRENT_API_VERSION: u32 = EXTENDED_LEAF_DATA;
-
-#[derive(Debug, Serialize, Deserialize)]
-pub enum Request {
-    /// Since [`NO_VERSION_CHECK_VERSION`]
-    ListMacros { dylib_path: Utf8PathBuf },
-    /// Since [`NO_VERSION_CHECK_VERSION`]
-    ExpandMacro(Box),
-    /// Since [`VERSION_CHECK_VERSION`]
-    ApiVersionCheck {},
-    /// Since [`RUST_ANALYZER_SPAN_SUPPORT`]
-    SetConfig(ServerConfig),
-}
-
-#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize)]
-pub enum SpanMode {
-    #[default]
-    Id,
-    RustAnalyzer,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub enum Response {
-    /// Since [`NO_VERSION_CHECK_VERSION`]
-    ListMacros(Result, String>),
-    /// Since [`NO_VERSION_CHECK_VERSION`]
-    ExpandMacro(Result),
-    /// Since [`NO_VERSION_CHECK_VERSION`]
-    ApiVersionCheck(u32),
-    /// Since [`RUST_ANALYZER_SPAN_SUPPORT`]
-    SetConfig(ServerConfig),
-    /// Since [`RUST_ANALYZER_SPAN_SUPPORT`]
-    ExpandMacroExtended(Result),
-}
-
-#[derive(Debug, Serialize, Deserialize, Default)]
-#[serde(default)]
-pub struct ServerConfig {
-    pub span_mode: SpanMode,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct ExpandMacroExtended {
-    pub tree: FlatTree,
-    pub span_data_table: Vec,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct PanicMessage(pub String);
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct ExpandMacro {
-    pub lib: Utf8PathBuf,
-    /// Environment variables to set during macro expansion.
-    pub env: Vec<(String, String)>,
-    pub current_dir: Option,
-    #[serde(flatten)]
-    pub data: ExpandMacroData,
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct ExpandMacroData {
-    /// Argument of macro call.
-    ///
-    /// In custom derive this will be a struct or enum; in attribute-like macro - underlying
-    /// item; in function-like macro - the macro body.
-    pub macro_body: FlatTree,
-
-    /// Name of macro to expand.
-    ///
-    /// In custom derive this is the name of the derived trait (`Serialize`, `Getters`, etc.).
-    /// In attribute-like and function-like macros - single name of macro itself (`show_streams`).
-    pub macro_name: String,
-
-    /// Possible attributes for the attribute-like macros.
-    pub attributes: Option,
-    /// marker for serde skip stuff
-    #[serde(skip_serializing_if = "ExpnGlobals::skip_serializing_if")]
-    #[serde(default)]
-    pub has_global_spans: ExpnGlobals,
-    #[serde(skip_serializing_if = "Vec::is_empty")]
-    #[serde(default)]
-    pub span_data_table: Vec,
-}
-
-#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize)]
-pub struct ExpnGlobals {
-    #[serde(skip_serializing)]
-    #[serde(default)]
-    pub serialize: bool,
-    pub def_site: usize,
-    pub call_site: usize,
-    pub mixed_site: usize,
-}
-
-impl ExpnGlobals {
-    fn skip_serializing_if(&self) -> bool {
-        !self.serialize
-    }
-}
-
-pub trait Message: serde::Serialize + DeserializeOwned {
-    fn read(
-        from_proto: ProtocolRead,
-        inp: &mut R,
-        buf: &mut String,
-    ) -> io::Result> {
-        Ok(match from_proto(inp, buf)? {
-            None => None,
-            Some(text) => {
-                let mut deserializer = serde_json::Deserializer::from_str(text);
-                // Note that some proc-macro generate very deep syntax tree
-                // We have to disable the current limit of serde here
-                deserializer.disable_recursion_limit();
-                Some(Self::deserialize(&mut deserializer)?)
-            }
-        })
-    }
-    fn write(self, to_proto: ProtocolWrite, out: &mut W) -> io::Result<()> {
-        let text = serde_json::to_string(&self)?;
-        to_proto(out, &text)
-    }
-}
-
-impl Message for Request {}
-impl Message for Response {}
-
-#[allow(type_alias_bounds)]
-type ProtocolRead =
-    for<'i, 'buf> fn(inp: &'i mut R, buf: &'buf mut String) -> io::Result>;
-#[allow(type_alias_bounds)]
-type ProtocolWrite = for<'o, 'msg> fn(out: &'o mut W, msg: &'msg str) -> io::Result<()>;
-
-#[cfg(test)]
-mod tests {
-    use intern::{sym, Symbol};
-    use span::{ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, TextRange, TextSize};
-    use tt::{Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, Spacing, Subtree, TokenTree};
-
-    use super::*;
-
-    fn fixture_token_tree() -> Subtree {
-        let anchor = SpanAnchor {
-            file_id: span::EditionedFileId::new(
-                span::FileId::from_raw(0xe4e4e),
-                span::Edition::CURRENT,
-            ),
-            ast_id: ErasedFileAstId::from_raw(0),
-        };
-
-        let token_trees = Box::new([
-            TokenTree::Leaf(
-                Ident {
-                    sym: Symbol::intern("struct"),
-                    span: Span {
-                        range: TextRange::at(TextSize::new(0), TextSize::of("struct")),
-                        anchor,
-                        ctx: SyntaxContextId::ROOT,
-                    },
-                    is_raw: tt::IdentIsRaw::No,
-                }
-                .into(),
-            ),
-            TokenTree::Leaf(
-                Ident {
-                    sym: Symbol::intern("Foo"),
-                    span: Span {
-                        range: TextRange::at(TextSize::new(5), TextSize::of("r#Foo")),
-                        anchor,
-                        ctx: SyntaxContextId::ROOT,
-                    },
-                    is_raw: tt::IdentIsRaw::Yes,
-                }
-                .into(),
-            ),
-            TokenTree::Leaf(Leaf::Literal(Literal {
-                symbol: Symbol::intern("Foo"),
-                span: Span {
-                    range: TextRange::at(TextSize::new(10), TextSize::of("\"Foo\"")),
-                    anchor,
-                    ctx: SyntaxContextId::ROOT,
-                },
-                kind: tt::LitKind::Str,
-                suffix: None,
-            })),
-            TokenTree::Leaf(Leaf::Punct(Punct {
-                char: '@',
-                span: Span {
-                    range: TextRange::at(TextSize::new(13), TextSize::of('@')),
-                    anchor,
-                    ctx: SyntaxContextId::ROOT,
-                },
-                spacing: Spacing::Joint,
-            })),
-            TokenTree::Subtree(Subtree {
-                delimiter: Delimiter {
-                    open: Span {
-                        range: TextRange::at(TextSize::new(14), TextSize::of('{')),
-                        anchor,
-                        ctx: SyntaxContextId::ROOT,
-                    },
-                    close: Span {
-                        range: TextRange::at(TextSize::new(19), TextSize::of('}')),
-                        anchor,
-                        ctx: SyntaxContextId::ROOT,
-                    },
-                    kind: DelimiterKind::Brace,
-                },
-                token_trees: Box::new([TokenTree::Leaf(Leaf::Literal(Literal {
-                    symbol: sym::INTEGER_0.clone(),
-                    span: Span {
-                        range: TextRange::at(TextSize::new(15), TextSize::of("0u32")),
-                        anchor,
-                        ctx: SyntaxContextId::ROOT,
-                    },
-                    kind: tt::LitKind::Integer,
-                    suffix: Some(sym::u32.clone()),
-                }))]),
-            }),
-        ]);
-
-        Subtree {
-            delimiter: Delimiter {
-                open: Span {
-                    range: TextRange::empty(TextSize::new(0)),
-                    anchor,
-                    ctx: SyntaxContextId::ROOT,
-                },
-                close: Span {
-                    range: TextRange::empty(TextSize::new(19)),
-                    anchor,
-                    ctx: SyntaxContextId::ROOT,
-                },
-                kind: DelimiterKind::Invisible,
-            },
-            token_trees,
-        }
-    }
-
-    #[test]
-    fn test_proc_macro_rpc_works() {
-        let tt = fixture_token_tree();
-        for v in RUST_ANALYZER_SPAN_SUPPORT..=CURRENT_API_VERSION {
-            let mut span_data_table = Default::default();
-            let task = ExpandMacro {
-                data: ExpandMacroData {
-                    macro_body: FlatTree::new(&tt, v, &mut span_data_table),
-                    macro_name: Default::default(),
-                    attributes: None,
-                    has_global_spans: ExpnGlobals {
-                        serialize: true,
-                        def_site: 0,
-                        call_site: 0,
-                        mixed_site: 0,
-                    },
-                    span_data_table: Vec::new(),
-                },
-                lib: Utf8PathBuf::from_path_buf(std::env::current_dir().unwrap()).unwrap(),
-                env: Default::default(),
-                current_dir: Default::default(),
-            };
-
-            let json = serde_json::to_string(&task).unwrap();
-            // println!("{}", json);
-            let back: ExpandMacro = serde_json::from_str(&json).unwrap();
-
-            assert_eq!(
-                tt,
-                back.data.macro_body.to_subtree_resolved(v, &span_data_table),
-                "version: {v}"
-            );
-        }
-    }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs
deleted file mode 100644
index ce4b060fca50e..0000000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs
+++ /dev/null
@@ -1,627 +0,0 @@
-//! Serialization-friendly representation of `tt::Subtree`.
-//!
-//! It is possible to serialize `Subtree` as is, as a tree, but using
-//! arbitrary-nested trees in JSON is problematic, as they can cause the JSON
-//! parser to overflow the stack.
-//!
-//! Additionally, such implementation would be pretty verbose, and we do care
-//! about performance here a bit.
-//!
-//! So what this module does is dumping a `tt::Subtree` into a bunch of flat
-//! array of numbers. See the test in the parent module to get an example
-//! output.
-//!
-//! ```json
-//!  {
-//!    // Array of subtrees, each subtree is represented by 4 numbers:
-//!    // id of delimiter, delimiter kind, index of first child in `token_tree`,
-//!    // index of last child in `token_tree`
-//!    "subtree":[4294967295,0,0,5,2,2,5,5],
-//!    // 2 ints per literal: [token id, index into `text`]
-//!    "literal":[4294967295,1],
-//!    // 3 ints per punct: [token id, char, spacing]
-//!    "punct":[4294967295,64,1],
-//!    // 2 ints per ident: [token id, index into `text`]
-//!    "ident":   [0,0,1,1],
-//!    // children of all subtrees, concatenated. Each child is represented as `index << 2 | tag`
-//!    // where tag denotes one of subtree, literal, punct or ident.
-//!    "token_tree":[3,7,1,4],
-//!    // Strings shared by idents and literals
-//!    "text": ["struct","Foo"]
-//!  }
-//! ```
-//!
-//! We probably should replace most of the code here with bincode someday, but,
-//! as we don't have bincode in Cargo.toml yet, lets stick with serde_json for
-//! the time being.
-
-use std::collections::VecDeque;
-
-use intern::Symbol;
-use rustc_hash::FxHashMap;
-use serde_derive::{Deserialize, Serialize};
-use span::{EditionedFileId, ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, TextRange};
-
-use crate::msg::{ENCODE_CLOSE_SPAN_VERSION, EXTENDED_LEAF_DATA};
-
-pub type SpanDataIndexMap =
-    indexmap::IndexSet>;
-
-pub fn serialize_span_data_index_map(map: &SpanDataIndexMap) -> Vec {
-    map.iter()
-        .flat_map(|span| {
-            [
-                span.anchor.file_id.as_u32(),
-                span.anchor.ast_id.into_raw(),
-                span.range.start().into(),
-                span.range.end().into(),
-                span.ctx.into_u32(),
-            ]
-        })
-        .collect()
-}
-
-pub fn deserialize_span_data_index_map(map: &[u32]) -> SpanDataIndexMap {
-    debug_assert!(map.len() % 5 == 0);
-    map.chunks_exact(5)
-        .map(|span| {
-            let &[file_id, ast_id, start, end, e] = span else { unreachable!() };
-            Span {
-                anchor: SpanAnchor {
-                    file_id: EditionedFileId::from_raw(file_id),
-                    ast_id: ErasedFileAstId::from_raw(ast_id),
-                },
-                range: TextRange::new(start.into(), end.into()),
-                ctx: SyntaxContextId::from_u32(e),
-            }
-        })
-        .collect()
-}
-
-#[derive(Clone, Copy, PartialEq, Eq, Hash)]
-pub struct TokenId(pub u32);
-
-impl std::fmt::Debug for TokenId {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        self.0.fmt(f)
-    }
-}
-
-#[derive(Serialize, Deserialize, Debug)]
-pub struct FlatTree {
-    subtree: Vec,
-    literal: Vec,
-    punct: Vec,
-    ident: Vec,
-    token_tree: Vec,
-    text: Vec,
-}
-
-struct SubtreeRepr {
-    open: TokenId,
-    close: TokenId,
-    kind: tt::DelimiterKind,
-    tt: [u32; 2],
-}
-
-struct LiteralRepr {
-    id: TokenId,
-    text: u32,
-    suffix: u32,
-    kind: u16,
-}
-
-struct PunctRepr {
-    id: TokenId,
-    char: char,
-    spacing: tt::Spacing,
-}
-
-struct IdentRepr {
-    id: TokenId,
-    text: u32,
-    is_raw: bool,
-}
-
-impl FlatTree {
-    pub fn new(
-        subtree: &tt::Subtree,
-        version: u32,
-        span_data_table: &mut SpanDataIndexMap,
-    ) -> FlatTree {
-        let mut w = Writer {
-            string_table: FxHashMap::default(),
-            work: VecDeque::new(),
-            span_data_table,
-
-            subtree: Vec::new(),
-            literal: Vec::new(),
-            punct: Vec::new(),
-            ident: Vec::new(),
-            token_tree: Vec::new(),
-            text: Vec::new(),
-            version,
-        };
-        w.write(subtree);
-
-        FlatTree {
-            subtree: if version >= ENCODE_CLOSE_SPAN_VERSION {
-                write_vec(w.subtree, SubtreeRepr::write_with_close_span)
-            } else {
-                write_vec(w.subtree, SubtreeRepr::write)
-            },
-            literal: if version >= EXTENDED_LEAF_DATA {
-                write_vec(w.literal, LiteralRepr::write_with_kind)
-            } else {
-                write_vec(w.literal, LiteralRepr::write)
-            },
-            punct: write_vec(w.punct, PunctRepr::write),
-            ident: if version >= EXTENDED_LEAF_DATA {
-                write_vec(w.ident, IdentRepr::write_with_rawness)
-            } else {
-                write_vec(w.ident, IdentRepr::write)
-            },
-            token_tree: w.token_tree,
-            text: w.text,
-        }
-    }
-
-    pub fn new_raw(subtree: &tt::Subtree, version: u32) -> FlatTree {
-        let mut w = Writer {
-            string_table: FxHashMap::default(),
-            work: VecDeque::new(),
-            span_data_table: &mut (),
-
-            subtree: Vec::new(),
-            literal: Vec::new(),
-            punct: Vec::new(),
-            ident: Vec::new(),
-            token_tree: Vec::new(),
-            text: Vec::new(),
-            version,
-        };
-        w.write(subtree);
-
-        FlatTree {
-            subtree: if version >= ENCODE_CLOSE_SPAN_VERSION {
-                write_vec(w.subtree, SubtreeRepr::write_with_close_span)
-            } else {
-                write_vec(w.subtree, SubtreeRepr::write)
-            },
-            literal: if version >= EXTENDED_LEAF_DATA {
-                write_vec(w.literal, LiteralRepr::write_with_kind)
-            } else {
-                write_vec(w.literal, LiteralRepr::write)
-            },
-            punct: write_vec(w.punct, PunctRepr::write),
-            ident: if version >= EXTENDED_LEAF_DATA {
-                write_vec(w.ident, IdentRepr::write_with_rawness)
-            } else {
-                write_vec(w.ident, IdentRepr::write)
-            },
-            token_tree: w.token_tree,
-            text: w.text,
-        }
-    }
-
-    pub fn to_subtree_resolved(
-        self,
-        version: u32,
-        span_data_table: &SpanDataIndexMap,
-    ) -> tt::Subtree {
-        Reader {
-            subtree: if version >= ENCODE_CLOSE_SPAN_VERSION {
-                read_vec(self.subtree, SubtreeRepr::read_with_close_span)
-            } else {
-                read_vec(self.subtree, SubtreeRepr::read)
-            },
-            literal: if version >= EXTENDED_LEAF_DATA {
-                read_vec(self.literal, LiteralRepr::read_with_kind)
-            } else {
-                read_vec(self.literal, LiteralRepr::read)
-            },
-            punct: read_vec(self.punct, PunctRepr::read),
-            ident: if version >= EXTENDED_LEAF_DATA {
-                read_vec(self.ident, IdentRepr::read_with_rawness)
-            } else {
-                read_vec(self.ident, IdentRepr::read)
-            },
-            token_tree: self.token_tree,
-            text: self.text,
-            span_data_table,
-            version,
-        }
-        .read()
-    }
-
-    pub fn to_subtree_unresolved(self, version: u32) -> tt::Subtree {
-        Reader {
-            subtree: if version >= ENCODE_CLOSE_SPAN_VERSION {
-                read_vec(self.subtree, SubtreeRepr::read_with_close_span)
-            } else {
-                read_vec(self.subtree, SubtreeRepr::read)
-            },
-            literal: if version >= EXTENDED_LEAF_DATA {
-                read_vec(self.literal, LiteralRepr::read_with_kind)
-            } else {
-                read_vec(self.literal, LiteralRepr::read)
-            },
-            punct: read_vec(self.punct, PunctRepr::read),
-            ident: if version >= EXTENDED_LEAF_DATA {
-                read_vec(self.ident, IdentRepr::read_with_rawness)
-            } else {
-                read_vec(self.ident, IdentRepr::read)
-            },
-            token_tree: self.token_tree,
-            text: self.text,
-            span_data_table: &(),
-            version,
-        }
-        .read()
-    }
-}
-
-fn read_vec T, const N: usize>(xs: Vec, f: F) -> Vec {
-    let mut chunks = xs.chunks_exact(N);
-    let res = chunks.by_ref().map(|chunk| f(chunk.try_into().unwrap())).collect();
-    assert!(chunks.remainder().is_empty());
-    res
-}
-
-fn write_vec [u32; N], const N: usize>(xs: Vec, f: F) -> Vec {
-    xs.into_iter().flat_map(f).collect()
-}
-
-impl SubtreeRepr {
-    fn write(self) -> [u32; 4] {
-        let kind = match self.kind {
-            tt::DelimiterKind::Invisible => 0,
-            tt::DelimiterKind::Parenthesis => 1,
-            tt::DelimiterKind::Brace => 2,
-            tt::DelimiterKind::Bracket => 3,
-        };
-        [self.open.0, kind, self.tt[0], self.tt[1]]
-    }
-    fn read([open, kind, lo, len]: [u32; 4]) -> SubtreeRepr {
-        let kind = match kind {
-            0 => tt::DelimiterKind::Invisible,
-            1 => tt::DelimiterKind::Parenthesis,
-            2 => tt::DelimiterKind::Brace,
-            3 => tt::DelimiterKind::Bracket,
-            other => panic!("bad kind {other}"),
-        };
-        SubtreeRepr { open: TokenId(open), close: TokenId(!0), kind, tt: [lo, len] }
-    }
-    fn write_with_close_span(self) -> [u32; 5] {
-        let kind = match self.kind {
-            tt::DelimiterKind::Invisible => 0,
-            tt::DelimiterKind::Parenthesis => 1,
-            tt::DelimiterKind::Brace => 2,
-            tt::DelimiterKind::Bracket => 3,
-        };
-        [self.open.0, self.close.0, kind, self.tt[0], self.tt[1]]
-    }
-    fn read_with_close_span([open, close, kind, lo, len]: [u32; 5]) -> SubtreeRepr {
-        let kind = match kind {
-            0 => tt::DelimiterKind::Invisible,
-            1 => tt::DelimiterKind::Parenthesis,
-            2 => tt::DelimiterKind::Brace,
-            3 => tt::DelimiterKind::Bracket,
-            other => panic!("bad kind {other}"),
-        };
-        SubtreeRepr { open: TokenId(open), close: TokenId(close), kind, tt: [lo, len] }
-    }
-}
-
-impl LiteralRepr {
-    fn write(self) -> [u32; 2] {
-        [self.id.0, self.text]
-    }
-    fn read([id, text]: [u32; 2]) -> LiteralRepr {
-        LiteralRepr { id: TokenId(id), text, kind: 0, suffix: !0 }
-    }
-    fn write_with_kind(self) -> [u32; 4] {
-        [self.id.0, self.text, self.kind as u32, self.suffix]
-    }
-    fn read_with_kind([id, text, kind, suffix]: [u32; 4]) -> LiteralRepr {
-        LiteralRepr { id: TokenId(id), text, kind: kind as u16, suffix }
-    }
-}
-
-impl PunctRepr {
-    fn write(self) -> [u32; 3] {
-        let spacing = match self.spacing {
-            tt::Spacing::Alone | tt::Spacing::JointHidden => 0,
-            tt::Spacing::Joint => 1,
-        };
-        [self.id.0, self.char as u32, spacing]
-    }
-    fn read([id, char, spacing]: [u32; 3]) -> PunctRepr {
-        let spacing = match spacing {
-            0 => tt::Spacing::Alone,
-            1 => tt::Spacing::Joint,
-            other => panic!("bad spacing {other}"),
-        };
-        PunctRepr { id: TokenId(id), char: char.try_into().unwrap(), spacing }
-    }
-}
-
-impl IdentRepr {
-    fn write(self) -> [u32; 2] {
-        [self.id.0, self.text]
-    }
-    fn read(data: [u32; 2]) -> IdentRepr {
-        IdentRepr { id: TokenId(data[0]), text: data[1], is_raw: false }
-    }
-    fn write_with_rawness(self) -> [u32; 3] {
-        [self.id.0, self.text, self.is_raw as u32]
-    }
-    fn read_with_rawness([id, text, is_raw]: [u32; 3]) -> IdentRepr {
-        IdentRepr { id: TokenId(id), text, is_raw: is_raw == 1 }
-    }
-}
-
-trait InternableSpan: Copy {
-    type Table;
-    fn token_id_of(table: &mut Self::Table, s: Self) -> TokenId;
-    fn span_for_token_id(table: &Self::Table, id: TokenId) -> Self;
-}
-
-impl InternableSpan for TokenId {
-    type Table = ();
-    fn token_id_of((): &mut Self::Table, token_id: Self) -> TokenId {
-        token_id
-    }
-
-    fn span_for_token_id((): &Self::Table, id: TokenId) -> Self {
-        id
-    }
-}
-impl InternableSpan for Span {
-    type Table = SpanDataIndexMap;
-    fn token_id_of(table: &mut Self::Table, span: Self) -> TokenId {
-        TokenId(table.insert_full(span).0 as u32)
-    }
-    fn span_for_token_id(table: &Self::Table, id: TokenId) -> Self {
-        *table.get_index(id.0 as usize).unwrap_or_else(|| &table[0])
-    }
-}
-
-struct Writer<'a, 'span, S: InternableSpan> {
-    work: VecDeque<(usize, &'a tt::Subtree)>,
-    string_table: FxHashMap, u32>,
-    span_data_table: &'span mut S::Table,
-    version: u32,
-
-    subtree: Vec,
-    literal: Vec,
-    punct: Vec,
-    ident: Vec,
-    token_tree: Vec,
-    text: Vec,
-}
-
-impl<'a, S: InternableSpan> Writer<'a, '_, S> {
-    fn write(&mut self, root: &'a tt::Subtree) {
-        self.enqueue(root);
-        while let Some((idx, subtree)) = self.work.pop_front() {
-            self.subtree(idx, subtree);
-        }
-    }
-
-    fn token_id_of(&mut self, span: S) -> TokenId {
-        S::token_id_of(self.span_data_table, span)
-    }
-
-    fn subtree(&mut self, idx: usize, subtree: &'a tt::Subtree) {
-        let mut first_tt = self.token_tree.len();
-        let n_tt = subtree.token_trees.len();
-        self.token_tree.resize(first_tt + n_tt, !0);
-
-        self.subtree[idx].tt = [first_tt as u32, (first_tt + n_tt) as u32];
-
-        for child in subtree.token_trees.iter() {
-            let idx_tag = match child {
-                tt::TokenTree::Subtree(it) => {
-                    let idx = self.enqueue(it);
-                    idx << 2
-                }
-                tt::TokenTree::Leaf(leaf) => match leaf {
-                    tt::Leaf::Literal(lit) => {
-                        let idx = self.literal.len() as u32;
-                        let id = self.token_id_of(lit.span);
-                        let (text, suffix) = if self.version >= EXTENDED_LEAF_DATA {
-                            (
-                                self.intern(lit.symbol.as_str()),
-                                lit.suffix.as_ref().map(|s| self.intern(s.as_str())).unwrap_or(!0),
-                            )
-                        } else {
-                            (self.intern_owned(format!("{lit}")), !0)
-                        };
-                        self.literal.push(LiteralRepr {
-                            id,
-                            text,
-                            kind: u16::from_le_bytes(match lit.kind {
-                                tt::LitKind::Err(_) => [0, 0],
-                                tt::LitKind::Byte => [1, 0],
-                                tt::LitKind::Char => [2, 0],
-                                tt::LitKind::Integer => [3, 0],
-                                tt::LitKind::Float => [4, 0],
-                                tt::LitKind::Str => [5, 0],
-                                tt::LitKind::StrRaw(r) => [6, r],
-                                tt::LitKind::ByteStr => [7, 0],
-                                tt::LitKind::ByteStrRaw(r) => [8, r],
-                                tt::LitKind::CStr => [9, 0],
-                                tt::LitKind::CStrRaw(r) => [10, r],
-                            }),
-                            suffix,
-                        });
-                        idx << 2 | 0b01
-                    }
-                    tt::Leaf::Punct(punct) => {
-                        let idx = self.punct.len() as u32;
-                        let id = self.token_id_of(punct.span);
-                        self.punct.push(PunctRepr { char: punct.char, spacing: punct.spacing, id });
-                        idx << 2 | 0b10
-                    }
-                    tt::Leaf::Ident(ident) => {
-                        let idx = self.ident.len() as u32;
-                        let id = self.token_id_of(ident.span);
-                        let text = if self.version >= EXTENDED_LEAF_DATA {
-                            self.intern(ident.sym.as_str())
-                        } else if ident.is_raw.yes() {
-                            self.intern_owned(format!("r#{}", ident.sym.as_str(),))
-                        } else {
-                            self.intern(ident.sym.as_str())
-                        };
-                        self.ident.push(IdentRepr { id, text, is_raw: ident.is_raw.yes() });
-                        idx << 2 | 0b11
-                    }
-                },
-            };
-            self.token_tree[first_tt] = idx_tag;
-            first_tt += 1;
-        }
-    }
-
-    fn enqueue(&mut self, subtree: &'a tt::Subtree) -> u32 {
-        let idx = self.subtree.len();
-        let open = self.token_id_of(subtree.delimiter.open);
-        let close = self.token_id_of(subtree.delimiter.close);
-        let delimiter_kind = subtree.delimiter.kind;
-        self.subtree.push(SubtreeRepr { open, close, kind: delimiter_kind, tt: [!0, !0] });
-        self.work.push_back((idx, subtree));
-        idx as u32
-    }
-
-    pub(crate) fn intern(&mut self, text: &'a str) -> u32 {
-        let table = &mut self.text;
-        *self.string_table.entry(text.into()).or_insert_with(|| {
-            let idx = table.len();
-            table.push(text.to_owned());
-            idx as u32
-        })
-    }
-
-    pub(crate) fn intern_owned(&mut self, text: String) -> u32 {
-        let table = &mut self.text;
-        *self.string_table.entry(text.clone().into()).or_insert_with(|| {
-            let idx = table.len();
-            table.push(text);
-            idx as u32
-        })
-    }
-}
-
-struct Reader<'span, S: InternableSpan> {
-    version: u32,
-    subtree: Vec,
-    literal: Vec,
-    punct: Vec,
-    ident: Vec,
-    token_tree: Vec,
-    text: Vec,
-    span_data_table: &'span S::Table,
-}
-
-impl Reader<'_, S> {
-    pub(crate) fn read(self) -> tt::Subtree {
-        let mut res: Vec>> = vec![None; self.subtree.len()];
-        let read_span = |id| S::span_for_token_id(self.span_data_table, id);
-        for i in (0..self.subtree.len()).rev() {
-            let repr = &self.subtree[i];
-            let token_trees = &self.token_tree[repr.tt[0] as usize..repr.tt[1] as usize];
-            let s = tt::Subtree {
-                delimiter: tt::Delimiter {
-                    open: read_span(repr.open),
-                    close: read_span(repr.close),
-                    kind: repr.kind,
-                },
-                token_trees: token_trees
-                    .iter()
-                    .copied()
-                    .map(|idx_tag| {
-                        let tag = idx_tag & 0b11;
-                        let idx = (idx_tag >> 2) as usize;
-                        match tag {
-                            // XXX: we iterate subtrees in reverse to guarantee
-                            // that this unwrap doesn't fire.
-                            0b00 => res[idx].take().unwrap().into(),
-                            0b01 => {
-                                use tt::LitKind::*;
-                                let repr = &self.literal[idx];
-                                let text = self.text[repr.text as usize].as_str();
-                                let span = read_span(repr.id);
-                                tt::Leaf::Literal(if self.version >= EXTENDED_LEAF_DATA {
-                                    tt::Literal {
-                                        symbol: Symbol::intern(text),
-                                        span,
-                                        kind: match u16::to_le_bytes(repr.kind) {
-                                            [0, _] => Err(()),
-                                            [1, _] => Byte,
-                                            [2, _] => Char,
-                                            [3, _] => Integer,
-                                            [4, _] => Float,
-                                            [5, _] => Str,
-                                            [6, r] => StrRaw(r),
-                                            [7, _] => ByteStr,
-                                            [8, r] => ByteStrRaw(r),
-                                            [9, _] => CStr,
-                                            [10, r] => CStrRaw(r),
-                                            _ => unreachable!(),
-                                        },
-                                        suffix: if repr.suffix != !0 {
-                                            Some(Symbol::intern(
-                                                self.text[repr.suffix as usize].as_str(),
-                                            ))
-                                        } else {
-                                            None
-                                        },
-                                    }
-                                } else {
-                                    tt::token_to_literal(text, span)
-                                })
-                                .into()
-                            }
-                            0b10 => {
-                                let repr = &self.punct[idx];
-                                tt::Leaf::Punct(tt::Punct {
-                                    char: repr.char,
-                                    spacing: repr.spacing,
-                                    span: read_span(repr.id),
-                                })
-                                .into()
-                            }
-                            0b11 => {
-                                let repr = &self.ident[idx];
-                                let text = self.text[repr.text as usize].as_str();
-                                let (is_raw, text) = if self.version >= EXTENDED_LEAF_DATA {
-                                    (
-                                        if repr.is_raw {
-                                            tt::IdentIsRaw::Yes
-                                        } else {
-                                            tt::IdentIsRaw::No
-                                        },
-                                        text,
-                                    )
-                                } else {
-                                    tt::IdentIsRaw::split_from_symbol(text)
-                                };
-                                tt::Leaf::Ident(tt::Ident {
-                                    sym: Symbol::intern(text),
-                                    span: read_span(repr.id),
-                                    is_raw,
-                                })
-                                .into()
-                            }
-                            other => panic!("bad tag: {other}"),
-                        }
-                    })
-                    .collect(),
-            };
-            res[i] = Some(s);
-        }
-
-        res[0].take().unwrap()
-    }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
index 4045e25fdf11f..d998b23d3bbef 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
@@ -11,13 +11,18 @@ use paths::AbsPath;
 use stdx::JodChild;
 
 use crate::{
-    json::{read_json, write_json},
-    msg::{Message, Request, Response, SpanMode, CURRENT_API_VERSION, RUST_ANALYZER_SPAN_SUPPORT},
+    legacy_protocol::{
+        json::{read_json, write_json},
+        msg::{
+            Message, Request, Response, ServerConfig, SpanMode, CURRENT_API_VERSION,
+            RUST_ANALYZER_SPAN_SUPPORT,
+        },
+    },
     ProcMacroKind, ServerError,
 };
 
 #[derive(Debug)]
-pub(crate) struct ProcMacroProcessSrv {
+pub(crate) struct ProcMacroServerProcess {
     /// The state of the proc-macro server process, the protocol is currently strictly sequential
     /// hence the lock on the state.
     state: Mutex,
@@ -34,24 +39,24 @@ struct ProcessSrvState {
     stdout: BufReader,
 }
 
-impl ProcMacroProcessSrv {
+impl ProcMacroServerProcess {
     pub(crate) fn run(
         process_path: &AbsPath,
         env: impl IntoIterator, impl AsRef)>
             + Clone,
-    ) -> io::Result {
-        let create_srv = |null_stderr| {
-            let mut process = Process::run(process_path, env.clone(), null_stderr)?;
+    ) -> io::Result {
+        let create_srv = || {
+            let mut process = Process::run(process_path, env.clone())?;
             let (stdin, stdout) = process.stdio().expect("couldn't access child stdio");
 
-            io::Result::Ok(ProcMacroProcessSrv {
+            io::Result::Ok(ProcMacroServerProcess {
                 state: Mutex::new(ProcessSrvState { process, stdin, stdout }),
                 version: 0,
                 mode: SpanMode::Id,
                 exited: OnceLock::new(),
             })
         };
-        let mut srv = create_srv(true)?;
+        let mut srv = create_srv()?;
         tracing::info!("sending proc-macro server version check");
         match srv.version_check() {
             Ok(v) if v > CURRENT_API_VERSION => Err(io::Error::new(
@@ -62,7 +67,6 @@ impl ProcMacroProcessSrv {
             )),
             Ok(v) => {
                 tracing::info!("Proc-macro server version: {v}");
-                srv = create_srv(false)?;
                 srv.version = v;
                 if srv.version >= RUST_ANALYZER_SPAN_SUPPORT {
                     if let Ok(mode) = srv.enable_rust_analyzer_spans() {
@@ -73,8 +77,10 @@ impl ProcMacroProcessSrv {
                 Ok(srv)
             }
             Err(e) => {
-                tracing::info!(%e, "proc-macro version check failed, restarting and assuming version 0");
-                create_srv(false)
+                tracing::info!(%e, "proc-macro version check failed");
+                Err(
+                    io::Error::new(io::ErrorKind::Other, format!("proc-macro server version check failed: {e}")),
+                )
             }
         }
     }
@@ -98,13 +104,11 @@ impl ProcMacroProcessSrv {
     }
 
     fn enable_rust_analyzer_spans(&self) -> Result {
-        let request = Request::SetConfig(crate::msg::ServerConfig {
-            span_mode: crate::msg::SpanMode::RustAnalyzer,
-        });
+        let request = Request::SetConfig(ServerConfig { span_mode: SpanMode::RustAnalyzer });
         let response = self.send_task(request)?;
 
         match response {
-            Response::SetConfig(crate::msg::ServerConfig { span_mode }) => Ok(span_mode),
+            Response::SetConfig(ServerConfig { span_mode }) => Ok(span_mode),
             _ => Err(ServerError { message: "unexpected response".to_owned(), io: None }),
         }
     }
@@ -182,9 +186,8 @@ impl Process {
     fn run(
         path: &AbsPath,
         env: impl IntoIterator, impl AsRef)>,
-        null_stderr: bool,
     ) -> io::Result {
-        let child = JodChild(mk_child(path, env, null_stderr)?);
+        let child = JodChild(mk_child(path, env)?);
         Ok(Process { child })
     }
 
@@ -200,14 +203,14 @@ impl Process {
 fn mk_child(
     path: &AbsPath,
     env: impl IntoIterator, impl AsRef)>,
-    null_stderr: bool,
 ) -> io::Result {
+    #[allow(clippy::disallowed_methods)]
     let mut cmd = Command::new(path);
     cmd.envs(env)
         .env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable")
         .stdin(Stdio::piped())
         .stdout(Stdio::piped())
-        .stderr(if null_stderr { Stdio::null() } else { Stdio::inherit() });
+        .stderr(Stdio::inherit());
     if cfg!(windows) {
         let mut path_var = std::ffi::OsString::new();
         path_var.push(path.parent().unwrap().parent().unwrap());
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml
index 1c394513c459d..57a28b00365f6 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml
@@ -12,6 +12,7 @@ rust-version.workspace = true
 [dependencies]
 proc-macro-srv.workspace = true
 proc-macro-api.workspace = true
+tt.workspace = true
 
 [features]
 sysroot-abi = ["proc-macro-srv/sysroot-abi"]
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs
index 137efd5e7a058..de59e88aac40c 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs
@@ -6,7 +6,10 @@
 #[cfg(feature = "in-rust-tree")]
 extern crate rustc_driver as _;
 
-use std::io;
+#[cfg(any(feature = "sysroot-abi", rust_analyzer))]
+mod main_loop;
+#[cfg(any(feature = "sysroot-abi", rust_analyzer))]
+use main_loop::run;
 
 fn main() -> std::io::Result<()> {
     let v = std::env::var("RUST_ANALYZER_INTERNALS_DO_NOT_USE");
@@ -22,57 +25,10 @@ fn main() -> std::io::Result<()> {
 }
 
 #[cfg(not(any(feature = "sysroot-abi", rust_analyzer)))]
-fn run() -> io::Result<()> {
-    Err(io::Error::new(
-        io::ErrorKind::Unsupported,
+fn run() -> std::io::Result<()> {
+    Err(std::io::Error::new(
+        std::io::ErrorKind::Unsupported,
         "proc-macro-srv-cli needs to be compiled with the `sysroot-abi` feature to function"
             .to_owned(),
     ))
 }
-
-#[cfg(any(feature = "sysroot-abi", rust_analyzer))]
-fn run() -> io::Result<()> {
-    use proc_macro_api::{
-        json::{read_json, write_json},
-        msg::{self, Message},
-    };
-    use proc_macro_srv::EnvSnapshot;
-
-    let read_request =
-        |buf: &mut String| msg::Request::read(read_json, &mut io::stdin().lock(), buf);
-
-    let write_response = |msg: msg::Response| msg.write(write_json, &mut io::stdout().lock());
-
-    let env = EnvSnapshot::default();
-    let mut srv = proc_macro_srv::ProcMacroSrv::new(&env);
-    let mut buf = String::new();
-
-    while let Some(req) = read_request(&mut buf)? {
-        let res = match req {
-            msg::Request::ListMacros { dylib_path } => {
-                msg::Response::ListMacros(srv.list_macros(&dylib_path))
-            }
-            msg::Request::ExpandMacro(task) => match srv.span_mode() {
-                msg::SpanMode::Id => {
-                    msg::Response::ExpandMacro(srv.expand(*task).map(|(it, _)| it))
-                }
-                msg::SpanMode::RustAnalyzer => msg::Response::ExpandMacroExtended(
-                    srv.expand(*task).map(|(tree, span_data_table)| msg::ExpandMacroExtended {
-                        tree,
-                        span_data_table,
-                    }),
-                ),
-            },
-            msg::Request::ApiVersionCheck {} => {
-                msg::Response::ApiVersionCheck(proc_macro_api::msg::CURRENT_API_VERSION)
-            }
-            msg::Request::SetConfig(config) => {
-                srv.set_span_mode(config.span_mode);
-                msg::Response::SetConfig(config)
-            }
-        };
-        write_response(res)?
-    }
-
-    Ok(())
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs
new file mode 100644
index 0000000000000..569070766f1c6
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs
@@ -0,0 +1,132 @@
+//! The main loop of the proc-macro server.
+use std::io;
+
+use proc_macro_api::legacy_protocol::{
+    json::{read_json, write_json},
+    msg::{
+        self, deserialize_span_data_index_map, serialize_span_data_index_map, ExpandMacroData,
+        ExpnGlobals, Message, SpanMode, TokenId, CURRENT_API_VERSION,
+    },
+};
+use proc_macro_srv::EnvSnapshot;
+
+pub(crate) fn run() -> io::Result<()> {
+    fn macro_kind_to_api(kind: proc_macro_srv::ProcMacroKind) -> proc_macro_api::ProcMacroKind {
+        match kind {
+            proc_macro_srv::ProcMacroKind::CustomDerive => {
+                proc_macro_api::ProcMacroKind::CustomDerive
+            }
+            proc_macro_srv::ProcMacroKind::Bang => proc_macro_api::ProcMacroKind::Bang,
+            proc_macro_srv::ProcMacroKind::Attr => proc_macro_api::ProcMacroKind::Attr,
+        }
+    }
+
+    let mut buf = String::new();
+    let mut read_request = || msg::Request::read(read_json, &mut io::stdin().lock(), &mut buf);
+    let write_response = |msg: msg::Response| msg.write(write_json, &mut io::stdout().lock());
+
+    let env = EnvSnapshot::default();
+    let srv = proc_macro_srv::ProcMacroSrv::new(&env);
+
+    let mut span_mode = SpanMode::Id;
+
+    while let Some(req) = read_request()? {
+        let res = match req {
+            msg::Request::ListMacros { dylib_path } => {
+                msg::Response::ListMacros(srv.list_macros(&dylib_path).map(|macros| {
+                    macros.into_iter().map(|(name, kind)| (name, macro_kind_to_api(kind))).collect()
+                }))
+            }
+            msg::Request::ExpandMacro(task) => {
+                let msg::ExpandMacro {
+                    lib,
+                    env,
+                    current_dir,
+                    data:
+                        ExpandMacroData {
+                            macro_body,
+                            macro_name,
+                            attributes,
+                            has_global_spans:
+                                ExpnGlobals { serialize: _, def_site, call_site, mixed_site },
+                            span_data_table,
+                        },
+                } = *task;
+                match span_mode {
+                    SpanMode::Id => msg::Response::ExpandMacro({
+                        let def_site = TokenId(def_site as u32);
+                        let call_site = TokenId(call_site as u32);
+                        let mixed_site = TokenId(mixed_site as u32);
+
+                        let macro_body = macro_body.to_subtree_unresolved(CURRENT_API_VERSION);
+                        let attributes =
+                            attributes.map(|it| it.to_subtree_unresolved(CURRENT_API_VERSION));
+
+                        srv.expand(
+                            lib,
+                            env,
+                            current_dir,
+                            macro_name,
+                            macro_body,
+                            attributes,
+                            def_site,
+                            call_site,
+                            mixed_site,
+                        )
+                        .map(|it| {
+                            msg::FlatTree::new_raw(tt::SubtreeView::new(&it), CURRENT_API_VERSION)
+                        })
+                        .map_err(msg::PanicMessage)
+                    }),
+                    SpanMode::RustAnalyzer => msg::Response::ExpandMacroExtended({
+                        let mut span_data_table = deserialize_span_data_index_map(&span_data_table);
+
+                        let def_site = span_data_table[def_site];
+                        let call_site = span_data_table[call_site];
+                        let mixed_site = span_data_table[mixed_site];
+
+                        let macro_body =
+                            macro_body.to_subtree_resolved(CURRENT_API_VERSION, &span_data_table);
+                        let attributes = attributes.map(|it| {
+                            it.to_subtree_resolved(CURRENT_API_VERSION, &span_data_table)
+                        });
+                        srv.expand(
+                            lib,
+                            env,
+                            current_dir,
+                            macro_name,
+                            macro_body,
+                            attributes,
+                            def_site,
+                            call_site,
+                            mixed_site,
+                        )
+                        .map(|it| {
+                            (
+                                msg::FlatTree::new(
+                                    tt::SubtreeView::new(&it),
+                                    CURRENT_API_VERSION,
+                                    &mut span_data_table,
+                                ),
+                                serialize_span_data_index_map(&span_data_table),
+                            )
+                        })
+                        .map(|(tree, span_data_table)| msg::ExpandMacroExtended {
+                            tree,
+                            span_data_table,
+                        })
+                        .map_err(msg::PanicMessage)
+                    }),
+                }
+            }
+            msg::Request::ApiVersionCheck {} => msg::Response::ApiVersionCheck(CURRENT_API_VERSION),
+            msg::Request::SetConfig(config) => {
+                span_mode = config.span_mode;
+                msg::Response::SetConfig(config)
+            }
+        };
+        write_response(res)?
+    }
+
+    Ok(())
+}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml
index 9838596945995..191535ac55e9c 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml
@@ -14,6 +14,7 @@ doctest = false
 
 [dependencies]
 object.workspace = true
+libc.workspace = true
 libloading.workspace = true
 memmap2.workspace = true
 
@@ -23,7 +24,6 @@ syntax-bridge.workspace = true
 paths.workspace = true
 # span = {workspace = true, default-features = false} does not work
 span = { path = "../span", version = "0.0.0", default-features = false}
-proc-macro-api.workspace = true
 intern.workspace = true
 
 ra-ap-rustc_lexer.workspace = true
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/build.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/build.rs
index 9a17cfc9f360d..07a10aaae578c 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/build.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/build.rs
@@ -7,6 +7,7 @@ fn main() {
     println!("cargo::rustc-check-cfg=cfg(rust_analyzer)");
 
     let rustc = env::var("RUSTC").expect("proc-macro-srv's build script expects RUSTC to be set");
+    #[allow(clippy::disallowed_methods)]
     let output = Command::new(rustc).arg("--version").output().expect("rustc --version must run");
     let version_string = std::str::from_utf8(&output.stdout[..])
         .expect("rustc --version output must be UTF-8")
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs
index ff2f5d1863913..d3d58a6df0115 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs
@@ -7,6 +7,8 @@
 //! a specific rustup toolchain: this allows testing against older ABIs (e.g.
 //! 1.58) and future ABIs (stage1, nightly)
 
+#![allow(clippy::disallowed_methods)]
+
 use std::{
     env,
     path::{Path, PathBuf},
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs
index 26f6af84aaeb1..cbf7a277bfae6 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs
@@ -8,39 +8,8 @@ use std::{fmt, fs, io, time::SystemTime};
 use libloading::Library;
 use object::Object;
 use paths::{Utf8Path, Utf8PathBuf};
-use proc_macro_api::ProcMacroKind;
 
-use crate::ProcMacroSrvSpan;
-
-const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_";
-
-fn invalid_data_err(e: impl Into>) -> io::Error {
-    io::Error::new(io::ErrorKind::InvalidData, e)
-}
-
-fn is_derive_registrar_symbol(symbol: &str) -> bool {
-    symbol.contains(NEW_REGISTRAR_SYMBOL)
-}
-
-fn find_registrar_symbol(obj: &object::File<'_>) -> object::Result> {
-    Ok(obj
-        .exports()?
-        .into_iter()
-        .map(|export| export.name())
-        .filter_map(|sym| String::from_utf8(sym.into()).ok())
-        .find(|sym| is_derive_registrar_symbol(sym))
-        .map(|sym| {
-            // From MacOS docs:
-            // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html
-            // Unlike other dyld API's, the symbol name passed to dlsym() must NOT be
-            // prepended with an underscore.
-            if cfg!(target_os = "macos") && sym.starts_with('_') {
-                sym[1..].to_owned()
-            } else {
-                sym
-            }
-        }))
-}
+use crate::{proc_macros::ProcMacros, server_impl::TopSubtree, ProcMacroKind, ProcMacroSrvSpan};
 
 /// Loads dynamic library in platform dependent manner.
 ///
@@ -59,11 +28,16 @@ fn load_library(file: &Utf8Path) -> Result {
 
 #[cfg(unix)]
 fn load_library(file: &Utf8Path) -> Result {
+    // not defined by POSIX, different values on mips vs other targets
+    #[cfg(target_env = "gnu")]
+    use libc::RTLD_DEEPBIND;
     use libloading::os::unix::Library as UnixLibrary;
-    use std::os::raw::c_int;
+    // defined by POSIX
+    use libloading::os::unix::RTLD_NOW;
 
-    const RTLD_NOW: c_int = 0x00002;
-    const RTLD_DEEPBIND: c_int = 0x00008;
+    // MUSL and bionic don't have it..
+    #[cfg(not(target_env = "gnu"))]
+    const RTLD_DEEPBIND: std::os::raw::c_int = 0x0;
 
     unsafe { UnixLibrary::open(Some(file), RTLD_NOW | RTLD_DEEPBIND).map(|lib| lib.into()) }
 }
@@ -100,13 +74,14 @@ impl From for LoadProcMacroDylibError {
     }
 }
 
-struct ProcMacroLibraryLibloading {
+struct ProcMacroLibrary {
+    // 'static is actually the lifetime of library, so make sure this drops before _lib
+    proc_macros: &'static ProcMacros,
     // Hold on to the library so it doesn't unload
     _lib: Library,
-    proc_macros: crate::proc_macros::ProcMacros,
 }
 
-impl ProcMacroLibraryLibloading {
+impl ProcMacroLibrary {
     fn open(path: &Utf8Path) -> Result {
         let file = fs::File::open(path)?;
         let file = unsafe { memmap2::Mmap::map(&file) }?;
@@ -119,27 +94,22 @@ impl ProcMacroLibraryLibloading {
             })?;
 
         let lib = load_library(path).map_err(invalid_data_err)?;
-        let proc_macros = crate::proc_macros::ProcMacros::from_lib(
-            &lib,
-            symbol_name,
-            &version_info.version_string,
-        )?;
-        Ok(ProcMacroLibraryLibloading { _lib: lib, proc_macros })
-    }
-}
-
-struct RemoveFileOnDrop(Utf8PathBuf);
-impl Drop for RemoveFileOnDrop {
-    fn drop(&mut self) {
-        #[cfg(windows)]
-        std::fs::remove_file(&self.0).unwrap();
-        _ = self.0;
+        let proc_macros = unsafe {
+            // SAFETY: We extend the lifetime here to avoid referential borrow problems
+            // We never reveal proc_macros to the outside and drop it before _lib
+            std::mem::transmute::<&ProcMacros, &'static ProcMacros>(ProcMacros::from_lib(
+                &lib,
+                symbol_name,
+                &version_info.version_string,
+            )?)
+        };
+        Ok(ProcMacroLibrary { _lib: lib, proc_macros })
     }
 }
 
 // Drop order matters as we can't remove the dylib before the library is unloaded
 pub(crate) struct Expander {
-    inner: ProcMacroLibraryLibloading,
+    inner: ProcMacroLibrary,
     _remove_on_drop: RemoveFileOnDrop,
     modified_time: SystemTime,
 }
@@ -152,7 +122,7 @@ impl Expander {
         let modified_time = fs::metadata(&lib).and_then(|it| it.modified())?;
 
         let path = ensure_file_with_lock_free_access(&lib)?;
-        let library = ProcMacroLibraryLibloading::open(path.as_ref())?;
+        let library = ProcMacroLibrary::open(path.as_ref())?;
 
         Ok(Expander { inner: library, _remove_on_drop: RemoveFileOnDrop(path), modified_time })
     }
@@ -160,12 +130,12 @@ impl Expander {
     pub(crate) fn expand(
         &self,
         macro_name: &str,
-        macro_body: tt::Subtree,
-        attributes: Option>,
+        macro_body: TopSubtree,
+        attributes: Option>,
         def_site: S,
         call_site: S,
         mixed_site: S,
-    ) -> Result, String>
+    ) -> Result, String>
     where
         ::TokenStream: Default,
     {
@@ -185,6 +155,44 @@ impl Expander {
     }
 }
 
+fn invalid_data_err(e: impl Into>) -> io::Error {
+    io::Error::new(io::ErrorKind::InvalidData, e)
+}
+
+fn is_derive_registrar_symbol(symbol: &str) -> bool {
+    const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_";
+    symbol.contains(NEW_REGISTRAR_SYMBOL)
+}
+
+fn find_registrar_symbol(obj: &object::File<'_>) -> object::Result> {
+    Ok(obj
+        .exports()?
+        .into_iter()
+        .map(|export| export.name())
+        .filter_map(|sym| String::from_utf8(sym.into()).ok())
+        .find(|sym| is_derive_registrar_symbol(sym))
+        .map(|sym| {
+            // From MacOS docs:
+            // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html
+            // Unlike other dyld API's, the symbol name passed to dlsym() must NOT be
+            // prepended with an underscore.
+            if cfg!(target_os = "macos") && sym.starts_with('_') {
+                sym[1..].to_owned()
+            } else {
+                sym
+            }
+        }))
+}
+
+struct RemoveFileOnDrop(Utf8PathBuf);
+impl Drop for RemoveFileOnDrop {
+    fn drop(&mut self) {
+        #[cfg(windows)]
+        std::fs::remove_file(&self.0).unwrap();
+        _ = self.0;
+    }
+}
+
 /// Copy the dylib to temp directory to prevent locking in Windows
 #[cfg(windows)]
 fn ensure_file_with_lock_free_access(path: &Utf8Path) -> io::Result {
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
index 85833dab1b092..f28821b4afc5c 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
@@ -14,6 +14,7 @@
 #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
 #![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)]
 #![allow(unreachable_pub, internal_features, clippy::disallowed_types, clippy::print_stderr)]
+#![deny(deprecated_safe)]
 
 extern crate proc_macro;
 #[cfg(feature = "in-rust-tree")]
@@ -34,96 +35,125 @@ use std::{
     ffi::OsString,
     fs,
     path::{Path, PathBuf},
+    sync::{Arc, Mutex, PoisonError},
     thread,
 };
 
 use paths::{Utf8Path, Utf8PathBuf};
-use proc_macro_api::{
-    msg::{
-        self, deserialize_span_data_index_map, serialize_span_data_index_map, ExpnGlobals,
-        SpanMode, TokenId, CURRENT_API_VERSION,
-    },
-    ProcMacroKind,
-};
-use span::Span;
+use span::{Span, TokenId};
 
 use crate::server_impl::TokenStream;
 
+#[derive(Copy, Clone, Eq, PartialEq, Debug)]
+pub enum ProcMacroKind {
+    CustomDerive,
+    Attr,
+    Bang,
+}
+
 pub const RUSTC_VERSION_STRING: &str = env!("RUSTC_VERSION");
 
 pub struct ProcMacroSrv<'env> {
-    expanders: HashMap,
-    span_mode: SpanMode,
+    expanders: Mutex>>,
     env: &'env EnvSnapshot,
 }
 
 impl<'env> ProcMacroSrv<'env> {
     pub fn new(env: &'env EnvSnapshot) -> Self {
-        Self { expanders: Default::default(), span_mode: Default::default(), env }
+        Self { expanders: Default::default(), env }
     }
 }
 
 const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024;
 
 impl ProcMacroSrv<'_> {
-    pub fn set_span_mode(&mut self, span_mode: SpanMode) {
-        self.span_mode = span_mode;
-    }
-
-    pub fn span_mode(&self) -> SpanMode {
-        self.span_mode
-    }
-
-    pub fn expand(
-        &mut self,
-        msg::ExpandMacro { lib, env, current_dir, data }: msg::ExpandMacro,
-    ) -> Result<(msg::FlatTree, Vec), msg::PanicMessage> {
-        let span_mode = self.span_mode;
+    pub fn expand(
+        &self,
+        lib: impl AsRef,
+        env: Vec<(String, String)>,
+        current_dir: Option>,
+        macro_name: String,
+        macro_body: tt::TopSubtree,
+        attribute: Option>,
+        def_site: S,
+        call_site: S,
+        mixed_site: S,
+    ) -> Result>, String> {
         let snapped_env = self.env;
-        let expander = self
-            .expander(lib.as_ref())
-            .map_err(|err| msg::PanicMessage(format!("failed to load macro: {err}")))?;
+        let expander =
+            self.expander(lib.as_ref()).map_err(|err| format!("failed to load macro: {err}"))?;
 
         let prev_env = EnvChange::apply(snapped_env, env, current_dir.as_ref().map(<_>::as_ref));
 
-        let result = match span_mode {
-            SpanMode::Id => expand_id(data, expander).map(|it| (it, vec![])),
-            SpanMode::RustAnalyzer => expand_ra_span(data, expander),
-        };
-
+        // Note, we spawn a new thread here so that thread locals allocation don't accumulate (this
+        // includes the proc-macro symbol interner)
+        let result = thread::scope(|s| {
+            let thread = thread::Builder::new()
+                .stack_size(EXPANDER_STACK_SIZE)
+                .name(macro_name.clone())
+                .spawn_scoped(s, move || {
+                    expander
+                        .expand(
+                            ¯o_name,
+                            server_impl::TopSubtree(macro_body.0.into_vec()),
+                            attribute.map(|it| server_impl::TopSubtree(it.0.into_vec())),
+                            def_site,
+                            call_site,
+                            mixed_site,
+                        )
+                        .map(|tt| tt.0)
+                });
+            let res = match thread {
+                Ok(handle) => handle.join(),
+                Err(e) => return Err(e.to_string()),
+            };
+
+            match res {
+                Ok(res) => res,
+                Err(e) => std::panic::resume_unwind(e),
+            }
+        });
         prev_env.rollback();
 
-        result.map_err(msg::PanicMessage)
+        result
     }
 
     pub fn list_macros(
-        &mut self,
+        &self,
         dylib_path: &Utf8Path,
     ) -> Result, String> {
         let expander = self.expander(dylib_path)?;
         Ok(expander.list_macros())
     }
 
-    fn expander(&mut self, path: &Utf8Path) -> Result<&dylib::Expander, String> {
+    fn expander(&self, path: &Utf8Path) -> Result, String> {
         let expander = || {
-            dylib::Expander::new(path)
-                .map_err(|err| format!("Cannot create expander for {path}: {err}",))
+            let expander = dylib::Expander::new(path)
+                .map_err(|err| format!("Cannot create expander for {path}: {err}",));
+            expander.map(Arc::new)
         };
 
-        Ok(match self.expanders.entry(path.to_path_buf()) {
-            Entry::Vacant(v) => v.insert(expander()?),
-            Entry::Occupied(mut e) => {
-                let time = fs::metadata(path).and_then(|it| it.modified()).ok();
-                if Some(e.get().modified_time()) != time {
-                    e.insert(expander()?);
+        Ok(
+            match self
+                .expanders
+                .lock()
+                .unwrap_or_else(PoisonError::into_inner)
+                .entry(path.to_path_buf())
+            {
+                Entry::Vacant(v) => v.insert(expander()?).clone(),
+                Entry::Occupied(mut e) => {
+                    let time = fs::metadata(path).and_then(|it| it.modified()).ok();
+                    if Some(e.get().modified_time()) != time {
+                        e.insert(expander()?);
+                    }
+                    e.get().clone()
                 }
-                e.into_mut()
-            }
-        })
+            },
+        )
     }
 }
 
-trait ProcMacroSrvSpan: Copy {
+pub trait ProcMacroSrvSpan: Copy + Send {
     type Server: proc_macro::bridge::server::Server>;
     fn make_server(call_site: Self, def_site: Self, mixed_site: Self) -> Self::Server;
 }
@@ -147,93 +177,6 @@ impl ProcMacroSrvSpan for Span {
         }
     }
 }
-
-fn expand_id(
-    msg::ExpandMacroData {
-        macro_body,
-        macro_name,
-        attributes,
-        has_global_spans: ExpnGlobals { serialize: _, def_site, call_site, mixed_site },
-        span_data_table: _,
-    }: msg::ExpandMacroData,
-    expander: &dylib::Expander,
-) -> Result {
-    let def_site = TokenId(def_site as u32);
-    let call_site = TokenId(call_site as u32);
-    let mixed_site = TokenId(mixed_site as u32);
-
-    let macro_body = macro_body.to_subtree_unresolved(CURRENT_API_VERSION);
-    let attributes = attributes.map(|it| it.to_subtree_unresolved(CURRENT_API_VERSION));
-    let result = thread::scope(|s| {
-        let thread = thread::Builder::new()
-            .stack_size(EXPANDER_STACK_SIZE)
-            .name(macro_name.clone())
-            .spawn_scoped(s, || {
-                expander
-                    .expand(¯o_name, macro_body, attributes, def_site, call_site, mixed_site)
-                    .map(|it| msg::FlatTree::new_raw(&it, CURRENT_API_VERSION))
-            });
-        let res = match thread {
-            Ok(handle) => handle.join(),
-            Err(e) => std::panic::resume_unwind(Box::new(e)),
-        };
-
-        match res {
-            Ok(res) => res,
-            Err(e) => std::panic::resume_unwind(e),
-        }
-    });
-    result
-}
-
-fn expand_ra_span(
-    msg::ExpandMacroData {
-        macro_body,
-        macro_name,
-        attributes,
-        has_global_spans: ExpnGlobals { serialize: _, def_site, call_site, mixed_site },
-        span_data_table,
-    }: msg::ExpandMacroData,
-    expander: &dylib::Expander,
-) -> Result<(msg::FlatTree, Vec), String> {
-    let mut span_data_table = deserialize_span_data_index_map(&span_data_table);
-
-    let def_site = span_data_table[def_site];
-    let call_site = span_data_table[call_site];
-    let mixed_site = span_data_table[mixed_site];
-
-    let macro_body = macro_body.to_subtree_resolved(CURRENT_API_VERSION, &span_data_table);
-    let attributes =
-        attributes.map(|it| it.to_subtree_resolved(CURRENT_API_VERSION, &span_data_table));
-    // Note, we spawn a new thread here so that thread locals allocation don't accumulate (this
-    // includes the proc-macro symbol interner)
-    let result = thread::scope(|s| {
-        let thread = thread::Builder::new()
-            .stack_size(EXPANDER_STACK_SIZE)
-            .name(macro_name.clone())
-            .spawn_scoped(s, || {
-                expander
-                    .expand(¯o_name, macro_body, attributes, def_site, call_site, mixed_site)
-                    .map(|it| {
-                        (
-                            msg::FlatTree::new(&it, CURRENT_API_VERSION, &mut span_data_table),
-                            serialize_span_data_index_map(&span_data_table),
-                        )
-                    })
-            });
-        let res = match thread {
-            Ok(handle) => handle.join(),
-            Err(e) => std::panic::resume_unwind(Box::new(e)),
-        };
-
-        match res {
-            Ok(res) => res,
-            Err(e) => std::panic::resume_unwind(e),
-        }
-    });
-    result
-}
-
 pub struct PanicMessage {
     message: Option,
 }
@@ -254,10 +197,13 @@ impl Default for EnvSnapshot {
     }
 }
 
+static ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
+
 struct EnvChange<'snap> {
     changed_vars: Vec,
     prev_working_dir: Option,
     snap: &'snap EnvSnapshot,
+    _guard: std::sync::MutexGuard<'snap, ()>,
 }
 
 impl<'snap> EnvChange<'snap> {
@@ -266,6 +212,7 @@ impl<'snap> EnvChange<'snap> {
         new_vars: Vec<(String, String)>,
         current_dir: Option<&Path>,
     ) -> EnvChange<'snap> {
+        let guard = ENV_LOCK.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
         let prev_working_dir = match current_dir {
             Some(dir) => {
                 let prev_working_dir = std::env::current_dir().ok();
@@ -284,11 +231,13 @@ impl<'snap> EnvChange<'snap> {
             changed_vars: new_vars
                 .into_iter()
                 .map(|(k, v)| {
-                    env::set_var(&k, v);
+                    // SAFETY: We have acquired the environment lock
+                    unsafe { env::set_var(&k, v) };
                     k
                 })
                 .collect(),
             prev_working_dir,
+            _guard: guard,
         }
     }
 
@@ -298,9 +247,12 @@ impl<'snap> EnvChange<'snap> {
 impl Drop for EnvChange<'_> {
     fn drop(&mut self) {
         for name in self.changed_vars.drain(..) {
-            match self.snap.vars.get::(name.as_ref()) {
-                Some(prev_val) => env::set_var(name, prev_val),
-                None => env::remove_var(name),
+            // SAFETY: We have acquired the environment lock
+            unsafe {
+                match self.snap.vars.get::(name.as_ref()) {
+                    Some(prev_val) => env::set_var(name, prev_val),
+                    None => env::remove_var(name),
+                }
             }
         }
 
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs
index 097b39a3f9123..58f5e80dc4ea6 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs
@@ -1,15 +1,15 @@
 //! Proc macro ABI
 
 use proc_macro::bridge;
-use proc_macro_api::ProcMacroKind;
 
 use libloading::Library;
 
-use crate::{dylib::LoadProcMacroDylibError, ProcMacroSrvSpan};
+use crate::{
+    dylib::LoadProcMacroDylibError, server_impl::TopSubtree, ProcMacroKind, ProcMacroSrvSpan,
+};
 
-pub(crate) struct ProcMacros {
-    exported_macros: Vec,
-}
+#[repr(transparent)]
+pub(crate) struct ProcMacros([bridge::client::ProcMacro]);
 
 impl From for crate::PanicMessage {
     fn from(p: bridge::PanicMessage) -> Self {
@@ -27,29 +27,28 @@ impl ProcMacros {
     /// *`info` - RustCInfo about the compiler that was used to compile the
     ///           macro crate. This is the information we use to figure out
     ///           which ABI to return
-    pub(crate) fn from_lib(
-        lib: &Library,
+    pub(crate) fn from_lib<'l>(
+        lib: &'l Library,
         symbol_name: String,
         version_string: &str,
-    ) -> Result {
-        if version_string == crate::RUSTC_VERSION_STRING {
-            let macros =
-                unsafe { lib.get::<&&[bridge::client::ProcMacro]>(symbol_name.as_bytes()) }?;
-
-            return Ok(Self { exported_macros: macros.to_vec() });
+    ) -> Result<&'l ProcMacros, LoadProcMacroDylibError> {
+        if version_string != crate::RUSTC_VERSION_STRING {
+            return Err(LoadProcMacroDylibError::AbiMismatch(version_string.to_owned()));
         }
-        Err(LoadProcMacroDylibError::AbiMismatch(version_string.to_owned()))
+        unsafe { lib.get::<&'l &'l ProcMacros>(symbol_name.as_bytes()) }
+            .map(|it| **it)
+            .map_err(Into::into)
     }
 
     pub(crate) fn expand(
         &self,
         macro_name: &str,
-        macro_body: tt::Subtree,
-        attributes: Option>,
+        macro_body: TopSubtree,
+        attributes: Option>,
         def_site: S,
         call_site: S,
         mixed_site: S,
-    ) -> Result, crate::PanicMessage> {
+    ) -> Result, crate::PanicMessage> {
         let parsed_body = crate::server_impl::TokenStream::with_subtree(macro_body);
 
         let parsed_attributes = attributes
@@ -57,7 +56,7 @@ impl ProcMacros {
                 crate::server_impl::TokenStream::with_subtree(attr)
             });
 
-        for proc_macro in &self.exported_macros {
+        for proc_macro in &self.0 {
             match proc_macro {
                 bridge::client::ProcMacro::CustomDerive { trait_name, client, .. }
                     if *trait_name == macro_name =>
@@ -103,7 +102,7 @@ impl ProcMacros {
     }
 
     pub(crate) fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
-        self.exported_macros
+        self.0
             .iter()
             .map(|proc_macro| match proc_macro {
                 bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs
index c9a8621690552..3d999421794bb 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs
@@ -8,6 +8,8 @@
 //!
 //! FIXME: No span and source file information is implemented yet
 
+use std::fmt;
+
 use proc_macro::bridge;
 
 mod token_stream;
@@ -19,6 +21,32 @@ pub mod token_id;
 // pub use symbol::*;
 use tt::Spacing;
 
+#[derive(Clone)]
+pub(crate) struct TopSubtree(pub(crate) Vec>);
+
+impl fmt::Debug for TopSubtree {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt::Debug::fmt(&tt::TokenTreesView::new(&self.0), f)
+    }
+}
+
+impl TopSubtree {
+    pub(crate) fn top_subtree(&self) -> &tt::Subtree {
+        let tt::TokenTree::Subtree(subtree) = &self.0[0] else {
+            unreachable!("the first token tree is always the top subtree");
+        };
+        subtree
+    }
+
+    pub(crate) fn from_bridge(group: bridge::Group, S>) -> Self {
+        let delimiter = delim_to_internal(group.delimiter, group.span);
+        let mut tts =
+            group.stream.map(|it| it.token_trees).unwrap_or_else(|| Vec::with_capacity(1));
+        tts.insert(0, tt::TokenTree::Subtree(tt::Subtree { delimiter, len: tts.len() as u32 }));
+        TopSubtree(tts)
+    }
+}
+
 fn delim_to_internal(d: proc_macro::Delimiter, span: bridge::DelimSpan) -> tt::Delimiter {
     let kind = match d {
         proc_macro::Delimiter::Parenthesis => tt::DelimiterKind::Parenthesis,
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
index 1b535d2a1ccc0..59293ee3f9659 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
@@ -6,23 +6,18 @@
 //! change their representation to be compatible with rust-analyzer's.
 use std::{
     collections::{HashMap, HashSet},
-    iter,
     ops::{Bound, Range},
 };
 
 use intern::Symbol;
 use proc_macro::bridge::{self, server};
-use span::{Span, FIXUP_ERASED_FILE_AST_ID_MARKER};
+use span::{FileId, Span, FIXUP_ERASED_FILE_AST_ID_MARKER};
 use tt::{TextRange, TextSize};
 
-use crate::server_impl::{
-    delim_to_external, delim_to_internal, literal_kind_to_external, literal_kind_to_internal,
-    token_stream::TokenStreamBuilder,
-};
+use crate::server_impl::{literal_kind_to_internal, token_stream::TokenStreamBuilder, TopSubtree};
 mod tt {
     pub use tt::*;
 
-    pub type Subtree = ::tt::Subtree;
     pub type TokenTree = ::tt::TokenTree;
     pub type Leaf = ::tt::Leaf;
     pub type Literal = ::tt::Literal;
@@ -32,8 +27,10 @@ mod tt {
 
 type TokenStream = crate::server_impl::TokenStream;
 
-#[derive(Clone)]
-pub struct SourceFile;
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub struct SourceFile {
+    file_id: FileId,
+}
 pub struct FreeFunctions;
 
 pub struct RaSpanServer {
@@ -159,15 +156,8 @@ impl server::TokenStream for RaSpanServer {
     ) -> Self::TokenStream {
         match tree {
             bridge::TokenTree::Group(group) => {
-                let group = tt::Subtree {
-                    delimiter: delim_to_internal(group.delimiter, group.span),
-                    token_trees: match group.stream {
-                        Some(stream) => stream.into_iter().collect(),
-                        None => Box::new([]),
-                    },
-                };
-                let tree = tt::TokenTree::from(group);
-                Self::TokenStream::from_iter(iter::once(tree))
+                let group = TopSubtree::from_bridge(group);
+                TokenStream { token_trees: group.0 }
             }
 
             bridge::TokenTree::Ident(ident) => {
@@ -179,7 +169,7 @@ impl server::TokenStream for RaSpanServer {
                 };
                 let leaf = tt::Leaf::from(ident);
                 let tree = tt::TokenTree::from(leaf);
-                Self::TokenStream::from_iter(iter::once(tree))
+                TokenStream { token_trees: vec![tree] }
             }
 
             bridge::TokenTree::Literal(literal) => {
@@ -192,7 +182,7 @@ impl server::TokenStream for RaSpanServer {
 
                 let leaf: tt::Leaf = tt::Leaf::from(literal);
                 let tree = tt::TokenTree::from(leaf);
-                Self::TokenStream::from_iter(iter::once(tree))
+                TokenStream { token_trees: vec![tree] }
             }
 
             bridge::TokenTree::Punct(p) => {
@@ -203,7 +193,7 @@ impl server::TokenStream for RaSpanServer {
                 };
                 let leaf = tt::Leaf::from(punct);
                 let tree = tt::TokenTree::from(leaf);
-                Self::TokenStream::from_iter(iter::once(tree))
+                TokenStream { token_trees: vec![tree] }
             }
         }
     }
@@ -251,49 +241,13 @@ impl server::TokenStream for RaSpanServer {
         &mut self,
         stream: Self::TokenStream,
     ) -> Vec> {
-        stream
-            .into_iter()
-            .map(|tree| match tree {
-                tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
-                    bridge::TokenTree::Ident(bridge::Ident {
-                        sym: ident.sym,
-                        is_raw: ident.is_raw.yes(),
-                        span: ident.span,
-                    })
-                }
-                tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
-                    bridge::TokenTree::Literal(bridge::Literal {
-                        span: lit.span,
-                        kind: literal_kind_to_external(lit.kind),
-                        symbol: lit.symbol,
-                        suffix: lit.suffix,
-                    })
-                }
-                tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => {
-                    bridge::TokenTree::Punct(bridge::Punct {
-                        ch: punct.char as u8,
-                        joint: punct.spacing == tt::Spacing::Joint,
-                        span: punct.span,
-                    })
-                }
-                tt::TokenTree::Subtree(subtree) => bridge::TokenTree::Group(bridge::Group {
-                    delimiter: delim_to_external(subtree.delimiter),
-                    stream: if subtree.token_trees.is_empty() {
-                        None
-                    } else {
-                        Some(subtree.token_trees.into_vec().into_iter().collect())
-                    },
-                    span: bridge::DelimSpan::from_single(subtree.delimiter.open),
-                }),
-            })
-            .collect()
+        stream.into_bridge()
     }
 }
 
 impl server::SourceFile for RaSpanServer {
-    fn eq(&mut self, _file1: &Self::SourceFile, _file2: &Self::SourceFile) -> bool {
-        // FIXME
-        true
+    fn eq(&mut self, file1: &Self::SourceFile, file2: &Self::SourceFile) -> bool {
+        file1 == file2
     }
     fn path(&mut self, _file: &Self::SourceFile) -> String {
         // FIXME
@@ -308,9 +262,8 @@ impl server::Span for RaSpanServer {
     fn debug(&mut self, span: Self::Span) -> String {
         format!("{:?}", span)
     }
-    fn source_file(&mut self, _span: Self::Span) -> Self::SourceFile {
-        // FIXME stub, requires db
-        SourceFile {}
+    fn source_file(&mut self, span: Self::Span) -> Self::SourceFile {
+        SourceFile { file_id: span.anchor.file_id.file_id() }
     }
     fn save_span(&mut self, _span: Self::Span) -> usize {
         // FIXME, quote is incompatible with third-party tools
@@ -439,12 +392,12 @@ impl server::Span for RaSpanServer {
 
     fn line(&mut self, _span: Self::Span) -> usize {
         // FIXME requires db to resolve line index, THIS IS NOT INCREMENTAL
-        0
+        1
     }
 
     fn column(&mut self, _span: Self::Span) -> usize {
         // FIXME requires db to resolve line index, THIS IS NOT INCREMENTAL
-        0
+        1
     }
 }
 
@@ -487,7 +440,7 @@ mod tests {
                 file_id: EditionedFileId::current_edition(FileId::from_raw(0)),
                 ast_id: span::ErasedFileAstId::from_raw(0),
             },
-            ctx: SyntaxContextId::ROOT,
+            ctx: SyntaxContextId::root(span::Edition::CURRENT),
         };
         let s = TokenStream {
             token_trees: vec![
@@ -507,13 +460,14 @@ mod tests {
                         close: span,
                         kind: tt::DelimiterKind::Brace,
                     },
-                    token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
-                        kind: tt::LitKind::Str,
-                        symbol: Symbol::intern("string"),
-                        suffix: None,
-                        span,
-                    }))]),
+                    len: 1,
                 }),
+                tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
+                    kind: tt::LitKind::Str,
+                    symbol: Symbol::intern("string"),
+                    suffix: None,
+                    span,
+                })),
             ],
         };
 
@@ -528,37 +482,40 @@ mod tests {
                 file_id: EditionedFileId::current_edition(FileId::from_raw(0)),
                 ast_id: span::ErasedFileAstId::from_raw(0),
             },
-            ctx: SyntaxContextId::ROOT,
+            ctx: SyntaxContextId::root(span::Edition::CURRENT),
         };
-        let subtree_paren_a = tt::TokenTree::Subtree(tt::Subtree {
-            delimiter: tt::Delimiter {
-                open: span,
-                close: span,
-                kind: tt::DelimiterKind::Parenthesis,
-            },
-            token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
+        let subtree_paren_a = vec![
+            tt::TokenTree::Subtree(tt::Subtree {
+                delimiter: tt::Delimiter {
+                    open: span,
+                    close: span,
+                    kind: tt::DelimiterKind::Parenthesis,
+                },
+                len: 1,
+            }),
+            tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
                 is_raw: tt::IdentIsRaw::No,
                 sym: Symbol::intern("a"),
                 span,
-            }))]),
-        });
+            })),
+        ];
 
         let t1 = TokenStream::from_str("(a)", span).unwrap();
-        assert_eq!(t1.token_trees.len(), 1);
-        assert_eq!(t1.token_trees[0], subtree_paren_a);
+        assert_eq!(t1.token_trees.len(), 2);
+        assert!(t1.token_trees == subtree_paren_a);
 
         let t2 = TokenStream::from_str("(a);", span).unwrap();
-        assert_eq!(t2.token_trees.len(), 2);
-        assert_eq!(t2.token_trees[0], subtree_paren_a);
+        assert_eq!(t2.token_trees.len(), 3);
+        assert!(t2.token_trees[0..2] == subtree_paren_a);
 
         let underscore = TokenStream::from_str("_", span).unwrap();
-        assert_eq!(
-            underscore.token_trees[0],
-            tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
-                sym: Symbol::intern("_"),
-                span,
-                is_raw: tt::IdentIsRaw::No,
-            }))
+        assert!(
+            underscore.token_trees[0]
+                == tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
+                    sym: Symbol::intern("_"),
+                    span,
+                    is_raw: tt::IdentIsRaw::No,
+                }))
         );
     }
 }
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs
index e478b1c853be7..409cf3cc78134 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs
@@ -1,30 +1,22 @@
 //! proc-macro server backend based on [`proc_macro_api::msg::TokenId`] as the backing span.
 //! This backend is rather inflexible, used by RustRover and older rust-analyzer versions.
-use std::{
-    iter,
-    ops::{Bound, Range},
-};
+use std::ops::{Bound, Range};
 
 use intern::Symbol;
 use proc_macro::bridge::{self, server};
 
-use crate::server_impl::{
-    delim_to_external, delim_to_internal, literal_kind_to_external, literal_kind_to_internal,
-    token_stream::TokenStreamBuilder,
-};
+use crate::server_impl::{literal_kind_to_internal, token_stream::TokenStreamBuilder, TopSubtree};
 mod tt {
-    pub use proc_macro_api::msg::TokenId;
+    pub use span::TokenId;
 
     pub use tt::*;
 
-    pub type Subtree = ::tt::Subtree;
     pub type TokenTree = ::tt::TokenTree;
     pub type Leaf = ::tt::Leaf;
     pub type Literal = ::tt::Literal;
     pub type Punct = ::tt::Punct;
     pub type Ident = ::tt::Ident;
 }
-type Group = tt::Subtree;
 type TokenTree = tt::TokenTree;
 type Punct = tt::Punct;
 type Spacing = tt::Spacing;
@@ -148,15 +140,8 @@ impl server::TokenStream for TokenIdServer {
     ) -> Self::TokenStream {
         match tree {
             bridge::TokenTree::Group(group) => {
-                let group = Group {
-                    delimiter: delim_to_internal(group.delimiter, group.span),
-                    token_trees: match group.stream {
-                        Some(stream) => stream.into_iter().collect(),
-                        None => Box::new([]),
-                    },
-                };
-                let tree = TokenTree::from(group);
-                Self::TokenStream::from_iter(iter::once(tree))
+                let group = TopSubtree::from_bridge(group);
+                TokenStream { token_trees: group.0 }
             }
 
             bridge::TokenTree::Ident(ident) => {
@@ -167,7 +152,7 @@ impl server::TokenStream for TokenIdServer {
                 };
                 let leaf = tt::Leaf::from(ident);
                 let tree = TokenTree::from(leaf);
-                Self::TokenStream::from_iter(iter::once(tree))
+                TokenStream { token_trees: vec![tree] }
             }
 
             bridge::TokenTree::Literal(literal) => {
@@ -180,7 +165,7 @@ impl server::TokenStream for TokenIdServer {
 
                 let leaf = tt::Leaf::from(literal);
                 let tree = TokenTree::from(leaf);
-                Self::TokenStream::from_iter(iter::once(tree))
+                TokenStream { token_trees: vec![tree] }
             }
 
             bridge::TokenTree::Punct(p) => {
@@ -191,7 +176,7 @@ impl server::TokenStream for TokenIdServer {
                 };
                 let leaf = tt::Leaf::from(punct);
                 let tree = TokenTree::from(leaf);
-                Self::TokenStream::from_iter(iter::once(tree))
+                TokenStream { token_trees: vec![tree] }
             }
         }
     }
@@ -234,42 +219,7 @@ impl server::TokenStream for TokenIdServer {
         &mut self,
         stream: Self::TokenStream,
     ) -> Vec> {
-        stream
-            .into_iter()
-            .map(|tree| match tree {
-                tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
-                    bridge::TokenTree::Ident(bridge::Ident {
-                        sym: ident.sym,
-                        is_raw: ident.is_raw.yes(),
-                        span: ident.span,
-                    })
-                }
-                tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
-                    bridge::TokenTree::Literal(bridge::Literal {
-                        span: lit.span,
-                        kind: literal_kind_to_external(lit.kind),
-                        symbol: lit.symbol,
-                        suffix: lit.suffix,
-                    })
-                }
-                tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => {
-                    bridge::TokenTree::Punct(bridge::Punct {
-                        ch: punct.char as u8,
-                        joint: punct.spacing == Spacing::Joint,
-                        span: punct.span,
-                    })
-                }
-                tt::TokenTree::Subtree(subtree) => bridge::TokenTree::Group(bridge::Group {
-                    delimiter: delim_to_external(subtree.delimiter),
-                    stream: if subtree.token_trees.is_empty() {
-                        None
-                    } else {
-                        Some(TokenStream { token_trees: subtree.token_trees.into_vec() })
-                    },
-                    span: bridge::DelimSpan::from_single(subtree.delimiter.open),
-                }),
-            })
-            .collect()
+        stream.into_bridge()
     }
 }
 
@@ -341,11 +291,11 @@ impl server::Span for TokenIdServer {
     }
 
     fn line(&mut self, _span: Self::Span) -> usize {
-        0
+        1
     }
 
     fn column(&mut self, _span: Self::Span) -> usize {
-        0
+        1
     }
 }
 
@@ -398,7 +348,7 @@ mod tests {
                         close: tt::TokenId(0),
                         kind: tt::DelimiterKind::Brace,
                     },
-                    token_trees: Box::new([]),
+                    len: 0,
                 }),
             ],
         };
@@ -408,35 +358,38 @@ mod tests {
 
     #[test]
     fn test_ra_server_from_str() {
-        let subtree_paren_a = tt::TokenTree::Subtree(tt::Subtree {
-            delimiter: tt::Delimiter {
-                open: tt::TokenId(0),
-                close: tt::TokenId(0),
-                kind: tt::DelimiterKind::Parenthesis,
-            },
-            token_trees: Box::new([tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
+        let subtree_paren_a = vec![
+            tt::TokenTree::Subtree(tt::Subtree {
+                delimiter: tt::Delimiter {
+                    open: tt::TokenId(0),
+                    close: tt::TokenId(0),
+                    kind: tt::DelimiterKind::Parenthesis,
+                },
+                len: 1,
+            }),
+            tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
                 is_raw: tt::IdentIsRaw::No,
                 sym: Symbol::intern("a"),
                 span: tt::TokenId(0),
-            }))]),
-        });
+            })),
+        ];
 
         let t1 = TokenStream::from_str("(a)", tt::TokenId(0)).unwrap();
-        assert_eq!(t1.token_trees.len(), 1);
-        assert_eq!(t1.token_trees[0], subtree_paren_a);
+        assert_eq!(t1.token_trees.len(), 2);
+        assert!(t1.token_trees[0..2] == subtree_paren_a);
 
         let t2 = TokenStream::from_str("(a);", tt::TokenId(0)).unwrap();
-        assert_eq!(t2.token_trees.len(), 2);
-        assert_eq!(t2.token_trees[0], subtree_paren_a);
+        assert_eq!(t2.token_trees.len(), 3);
+        assert!(t2.token_trees[0..2] == subtree_paren_a);
 
         let underscore = TokenStream::from_str("_", tt::TokenId(0)).unwrap();
-        assert_eq!(
-            underscore.token_trees[0],
-            tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
-                sym: Symbol::intern("_"),
-                span: tt::TokenId(0),
-                is_raw: tt::IdentIsRaw::No,
-            }))
+        assert!(
+            underscore.token_trees[0]
+                == tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
+                    sym: Symbol::intern("_"),
+                    span: tt::TokenId(0),
+                    is_raw: tt::IdentIsRaw::No,
+                }))
         );
     }
 }
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs
index 5649e60e0bb16..645f7e7c59a32 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_stream.rs
@@ -1,10 +1,20 @@
 //! TokenStream implementation used by sysroot ABI
 
-use tt::TokenTree;
+use proc_macro::bridge;
 
-#[derive(Debug, Clone)]
+use crate::server_impl::{delim_to_external, literal_kind_to_external, TopSubtree};
+
+#[derive(Clone)]
 pub struct TokenStream {
-    pub(super) token_trees: Vec>,
+    pub(super) token_trees: Vec>,
+}
+
+impl std::fmt::Debug for TokenStream {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("TokenStream")
+            .field("token_trees", &tt::TokenTreesView::new(&self.token_trees))
+            .finish()
+    }
 }
 
 impl Default for TokenStream {
@@ -13,84 +23,85 @@ impl Default for TokenStream {
     }
 }
 
-impl TokenStream {
+impl TokenStream {
     pub(crate) fn new() -> Self {
         TokenStream::default()
     }
 
-    pub(crate) fn with_subtree(subtree: tt::Subtree) -> Self {
-        if subtree.delimiter.kind != tt::DelimiterKind::Invisible {
-            TokenStream { token_trees: vec![TokenTree::Subtree(subtree)] }
-        } else {
-            TokenStream { token_trees: subtree.token_trees.into_vec() }
+    pub(crate) fn with_subtree(subtree: TopSubtree) -> Self {
+        let delimiter_kind = subtree.top_subtree().delimiter.kind;
+        let mut token_trees = subtree.0;
+        if delimiter_kind == tt::DelimiterKind::Invisible {
+            token_trees.remove(0);
         }
+        TokenStream { token_trees }
     }
 
-    pub(crate) fn into_subtree(self, call_site: S) -> tt::Subtree
+    pub(crate) fn into_subtree(mut self, call_site: S) -> TopSubtree
     where
         S: Copy,
     {
-        tt::Subtree {
-            delimiter: tt::Delimiter {
-                open: call_site,
-                close: call_site,
-                kind: tt::DelimiterKind::Invisible,
-            },
-            token_trees: self.token_trees.into_boxed_slice(),
-        }
+        self.token_trees.insert(
+            0,
+            tt::TokenTree::Subtree(tt::Subtree {
+                delimiter: tt::Delimiter {
+                    open: call_site,
+                    close: call_site,
+                    kind: tt::DelimiterKind::Invisible,
+                },
+                len: self.token_trees.len() as u32,
+            }),
+        );
+        TopSubtree(self.token_trees)
     }
 
     pub(super) fn is_empty(&self) -> bool {
         self.token_trees.is_empty()
     }
-}
 
-/// Creates a token stream containing a single token tree.
-impl From> for TokenStream {
-    fn from(tree: TokenTree) -> TokenStream {
-        TokenStream { token_trees: vec![tree] }
-    }
-}
-
-/// Collects a number of token trees into a single stream.
-impl FromIterator> for TokenStream {
-    fn from_iter>>(trees: I) -> Self {
-        trees.into_iter().map(TokenStream::from).collect()
-    }
-}
-
-/// A "flattening" operation on token streams, collects token trees
-/// from multiple token streams into a single stream.
-impl FromIterator> for TokenStream {
-    fn from_iter>>(streams: I) -> Self {
-        let mut builder = TokenStreamBuilder::new();
-        streams.into_iter().for_each(|stream| builder.push(stream));
-        builder.build()
-    }
-}
-
-impl Extend> for TokenStream {
-    fn extend>>(&mut self, trees: I) {
-        self.extend(trees.into_iter().map(TokenStream::from));
-    }
-}
-
-impl Extend> for TokenStream {
-    fn extend>>(&mut self, streams: I) {
-        for item in streams {
-            for tkn in item {
-                match tkn {
-                    tt::TokenTree::Subtree(subtree)
-                        if subtree.delimiter.kind == tt::DelimiterKind::Invisible =>
-                    {
-                        self.token_trees.extend(subtree.token_trees.into_vec().into_iter());
-                    }
-                    _ => {
-                        self.token_trees.push(tkn);
-                    }
+    pub(crate) fn into_bridge(self) -> Vec> {
+        let mut result = Vec::new();
+        let mut iter = self.token_trees.into_iter();
+        while let Some(tree) = iter.next() {
+            match tree {
+                tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
+                    result.push(bridge::TokenTree::Ident(bridge::Ident {
+                        sym: ident.sym,
+                        is_raw: ident.is_raw.yes(),
+                        span: ident.span,
+                    }))
+                }
+                tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
+                    result.push(bridge::TokenTree::Literal(bridge::Literal {
+                        span: lit.span,
+                        kind: literal_kind_to_external(lit.kind),
+                        symbol: lit.symbol,
+                        suffix: lit.suffix,
+                    }))
+                }
+                tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => {
+                    result.push(bridge::TokenTree::Punct(bridge::Punct {
+                        ch: punct.char as u8,
+                        joint: punct.spacing == tt::Spacing::Joint,
+                        span: punct.span,
+                    }))
+                }
+                tt::TokenTree::Subtree(subtree) => {
+                    result.push(bridge::TokenTree::Group(bridge::Group {
+                        delimiter: delim_to_external(subtree.delimiter),
+                        stream: if subtree.len == 0 {
+                            None
+                        } else {
+                            Some(TokenStream {
+                                token_trees: iter.by_ref().take(subtree.usize_len()).collect(),
+                            })
+                        },
+                        span: bridge::DelimSpan::from_single(subtree.delimiter.open),
+                    }))
                 }
             }
         }
+        result
     }
 }
 
@@ -103,19 +114,7 @@ pub(super) mod token_stream_impls {
 
     use core::fmt;
 
-    use super::{TokenStream, TokenTree};
-
-    /// An iterator over `TokenStream`'s `TokenTree`s.
-    /// The iteration is "shallow", e.g., the iterator doesn't recurse into delimited groups,
-    /// and returns whole groups as token trees.
-    impl IntoIterator for TokenStream {
-        type Item = TokenTree;
-        type IntoIter = std::vec::IntoIter>;
-
-        fn into_iter(self) -> Self::IntoIter {
-            self.token_trees.into_iter()
-        }
-    }
+    use super::{TokenStream, TopSubtree};
 
     /// Attempts to break the string into tokens and parse those tokens into a token stream.
     /// May fail for a number of reasons, for example, if the string contains unbalanced delimiters
@@ -133,7 +132,7 @@ pub(super) mod token_stream_impls {
             )
             .ok_or_else(|| format!("lexing error: {src}"))?;
 
-            Ok(TokenStream::with_subtree(subtree))
+            Ok(TokenStream::with_subtree(TopSubtree(subtree.0.into_vec())))
         }
     }
 
@@ -145,13 +144,13 @@ pub(super) mod token_stream_impls {
     }
 }
 
-impl TokenStreamBuilder {
+impl TokenStreamBuilder {
     pub(super) fn new() -> TokenStreamBuilder {
         TokenStreamBuilder { acc: TokenStream::new() }
     }
 
     pub(super) fn push(&mut self, stream: TokenStream) {
-        self.acc.extend(stream)
+        self.acc.token_trees.extend(stream.token_trees)
     }
 
     pub(super) fn build(self) -> TokenStream {
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs
index dc6e71163b2f2..15de88ea656d0 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs
@@ -12,7 +12,7 @@ fn test_derive_empty() {
         "DeriveEmpty",
         r#"struct S;"#,
         expect!["SUBTREE $$ 1 1"],
-        expect!["SUBTREE $$ 42:2@0..100#0 42:2@0..100#0"],
+        expect!["SUBTREE $$ 42:2@0..100#2 42:2@0..100#2"],
     );
 }
 
@@ -29,12 +29,12 @@ fn test_derive_error() {
                 LITERAL Str #[derive(DeriveError)] struct S ; 1
               PUNCH   ; [alone] 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
-              IDENT   compile_error 42:2@0..100#0
-              PUNCH   ! [alone] 42:2@0..100#0
-              SUBTREE () 42:2@0..100#0 42:2@0..100#0
-                LITERAL Str #[derive(DeriveError)] struct S ; 42:2@0..100#0
-              PUNCH   ; [alone] 42:2@0..100#0"#]],
+            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
+              IDENT   compile_error 42:2@0..100#2
+              PUNCH   ! [alone] 42:2@0..100#2
+              SUBTREE () 42:2@0..100#2 42:2@0..100#2
+                LITERAL Str #[derive(DeriveError)] struct S ; 42:2@0..100#2
+              PUNCH   ; [alone] 42:2@0..100#2"#]],
     );
 }
 
@@ -53,14 +53,14 @@ fn test_fn_like_macro_noop() {
               PUNCH   , [alone] 1
               SUBTREE [] 1 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
-              IDENT   ident 42:2@0..5#0
-              PUNCH   , [alone] 42:2@5..6#0
-              LITERAL Integer 0 42:2@7..8#0
-              PUNCH   , [alone] 42:2@8..9#0
-              LITERAL Integer 1 42:2@10..11#0
-              PUNCH   , [alone] 42:2@11..12#0
-              SUBTREE [] 42:2@13..14#0 42:2@14..15#0"#]],
+            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
+              IDENT   ident 42:2@0..5#2
+              PUNCH   , [alone] 42:2@5..6#2
+              LITERAL Integer 0 42:2@7..8#2
+              PUNCH   , [alone] 42:2@8..9#2
+              LITERAL Integer 1 42:2@10..11#2
+              PUNCH   , [alone] 42:2@11..12#2
+              SUBTREE [] 42:2@13..14#2 42:2@14..15#2"#]],
     );
 }
 
@@ -75,10 +75,10 @@ fn test_fn_like_macro_clone_ident_subtree() {
               PUNCH   , [alone] 1
               SUBTREE [] 1 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
-              IDENT   ident 42:2@0..5#0
-              PUNCH   , [alone] 42:2@5..6#0
-              SUBTREE [] 42:2@7..8#0 42:2@7..8#0"#]],
+            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
+              IDENT   ident 42:2@0..5#2
+              PUNCH   , [alone] 42:2@5..6#2
+              SUBTREE [] 42:2@7..8#2 42:2@7..8#2"#]],
     );
 }
 
@@ -91,8 +91,8 @@ fn test_fn_like_macro_clone_raw_ident() {
             SUBTREE $$ 1 1
               IDENT   r#async 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
-              IDENT   r#async 42:2@0..7#0"#]],
+            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
+              IDENT   r#async 42:2@0..7#2"#]],
     );
 }
 
@@ -105,8 +105,8 @@ fn test_fn_like_fn_like_span_join() {
             SUBTREE $$ 1 1
               IDENT   r#joined 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
-              IDENT   r#joined 42:2@0..11#0"#]],
+            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
+              IDENT   r#joined 42:2@0..11#2"#]],
     );
 }
 
@@ -121,10 +121,10 @@ fn test_fn_like_fn_like_span_ops() {
               IDENT   resolved_at_def_site 1
               IDENT   start_span 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
-              IDENT   set_def_site 41:1@0..150#0
-              IDENT   resolved_at_def_site 42:2@13..33#0
-              IDENT   start_span 42:2@34..34#0"#]],
+            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
+              IDENT   set_def_site 41:1@0..150#2
+              IDENT   resolved_at_def_site 42:2@13..33#2
+              IDENT   start_span 42:2@34..34#2"#]],
     );
 }
 
@@ -143,14 +143,14 @@ fn test_fn_like_mk_literals() {
               LITERAL Integer 123i64 1
               LITERAL Integer 123 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
-              LITERAL ByteStr byte_string 42:2@0..100#0
-              LITERAL Char c 42:2@0..100#0
-              LITERAL Str string 42:2@0..100#0
-              LITERAL Float 3.14f64 42:2@0..100#0
-              LITERAL Float 3.14 42:2@0..100#0
-              LITERAL Integer 123i64 42:2@0..100#0
-              LITERAL Integer 123 42:2@0..100#0"#]],
+            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
+              LITERAL ByteStr byte_string 42:2@0..100#2
+              LITERAL Char c 42:2@0..100#2
+              LITERAL Str string 42:2@0..100#2
+              LITERAL Float 3.14f64 42:2@0..100#2
+              LITERAL Float 3.14 42:2@0..100#2
+              LITERAL Integer 123i64 42:2@0..100#2
+              LITERAL Integer 123 42:2@0..100#2"#]],
     );
 }
 
@@ -164,9 +164,9 @@ fn test_fn_like_mk_idents() {
               IDENT   standard 1
               IDENT   r#raw 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
-              IDENT   standard 42:2@0..100#0
-              IDENT   r#raw 42:2@0..100#0"#]],
+            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
+              IDENT   standard 42:2@0..100#2
+              IDENT   r#raw 42:2@0..100#2"#]],
     );
 }
 
@@ -198,27 +198,27 @@ fn test_fn_like_macro_clone_literals() {
               PUNCH   , [alone] 1
               LITERAL CStr null 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
-              LITERAL Integer 1u16 42:2@0..4#0
-              PUNCH   , [alone] 42:2@4..5#0
-              LITERAL Integer 2_u32 42:2@6..11#0
-              PUNCH   , [alone] 42:2@11..12#0
-              PUNCH   - [alone] 42:2@13..14#0
-              LITERAL Integer 4i64 42:2@14..18#0
-              PUNCH   , [alone] 42:2@18..19#0
-              LITERAL Float 3.14f32 42:2@20..27#0
-              PUNCH   , [alone] 42:2@27..28#0
-              LITERAL Str hello bridge 42:2@29..43#0
-              PUNCH   , [alone] 42:2@43..44#0
-              LITERAL Str suffixedsuffix 42:2@45..61#0
-              PUNCH   , [alone] 42:2@61..62#0
-              LITERAL StrRaw(2) raw 42:2@63..73#0
-              PUNCH   , [alone] 42:2@73..74#0
-              LITERAL Char a 42:2@75..78#0
-              PUNCH   , [alone] 42:2@78..79#0
-              LITERAL Byte b 42:2@80..84#0
-              PUNCH   , [alone] 42:2@84..85#0
-              LITERAL CStr null 42:2@86..93#0"#]],
+            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
+              LITERAL Integer 1u16 42:2@0..4#2
+              PUNCH   , [alone] 42:2@4..5#2
+              LITERAL Integer 2_u32 42:2@6..11#2
+              PUNCH   , [alone] 42:2@11..12#2
+              PUNCH   - [alone] 42:2@13..14#2
+              LITERAL Integer 4i64 42:2@14..18#2
+              PUNCH   , [alone] 42:2@18..19#2
+              LITERAL Float 3.14f32 42:2@20..27#2
+              PUNCH   , [alone] 42:2@27..28#2
+              LITERAL Str hello bridge 42:2@29..43#2
+              PUNCH   , [alone] 42:2@43..44#2
+              LITERAL Str suffixedsuffix 42:2@45..61#2
+              PUNCH   , [alone] 42:2@61..62#2
+              LITERAL StrRaw(2) raw 42:2@63..73#2
+              PUNCH   , [alone] 42:2@73..74#2
+              LITERAL Char a 42:2@75..78#2
+              PUNCH   , [alone] 42:2@78..79#2
+              LITERAL Byte b 42:2@80..84#2
+              PUNCH   , [alone] 42:2@84..85#2
+              LITERAL CStr null 42:2@86..93#2"#]],
     );
 }
 
@@ -239,12 +239,12 @@ fn test_attr_macro() {
                 LITERAL Str #[attr_error(some arguments)] mod m {} 1
               PUNCH   ; [alone] 1"#]],
         expect![[r#"
-            SUBTREE $$ 42:2@0..100#0 42:2@0..100#0
-              IDENT   compile_error 42:2@0..100#0
-              PUNCH   ! [alone] 42:2@0..100#0
-              SUBTREE () 42:2@0..100#0 42:2@0..100#0
-                LITERAL Str #[attr_error(some arguments)] mod m {} 42:2@0..100#0
-              PUNCH   ; [alone] 42:2@0..100#0"#]],
+            SUBTREE $$ 42:2@0..100#2 42:2@0..100#2
+              IDENT   compile_error 42:2@0..100#2
+              PUNCH   ! [alone] 42:2@0..100#2
+              SUBTREE () 42:2@0..100#2 42:2@0..100#2
+                LITERAL Str #[attr_error(some arguments)] mod m {} 42:2@0..100#2
+              PUNCH   ; [alone] 42:2@0..100#2"#]],
     );
 }
 
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs
index cc5d4a8913180..1b085520d5656 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs
@@ -1,17 +1,18 @@
 //! utils used in proc-macro tests
 
 use expect_test::Expect;
-use proc_macro_api::msg::TokenId;
-use span::{EditionedFileId, ErasedFileAstId, FileId, Span, SpanAnchor, SyntaxContextId};
+use span::{EditionedFileId, ErasedFileAstId, FileId, Span, SpanAnchor, SyntaxContextId, TokenId};
 use tt::TextRange;
 
 use crate::{dylib, proc_macro_test_dylib_path, EnvSnapshot, ProcMacroSrv};
 
 fn parse_string(call_site: TokenId, src: &str) -> crate::server_impl::TokenStream {
-    crate::server_impl::TokenStream::with_subtree(
+    crate::server_impl::TokenStream::with_subtree(crate::server_impl::TopSubtree(
         syntax_bridge::parse_to_token_tree_static_span(span::Edition::CURRENT, call_site, src)
-            .unwrap(),
-    )
+            .unwrap()
+            .0
+            .into_vec(),
+    ))
 }
 
 fn parse_string_spanned(
@@ -19,18 +20,26 @@ fn parse_string_spanned(
     call_site: SyntaxContextId,
     src: &str,
 ) -> crate::server_impl::TokenStream {
-    crate::server_impl::TokenStream::with_subtree(
-        syntax_bridge::parse_to_token_tree(span::Edition::CURRENT, anchor, call_site, src).unwrap(),
-    )
+    crate::server_impl::TokenStream::with_subtree(crate::server_impl::TopSubtree(
+        syntax_bridge::parse_to_token_tree(span::Edition::CURRENT, anchor, call_site, src)
+            .unwrap()
+            .0
+            .into_vec(),
+    ))
 }
 
-pub fn assert_expand(macro_name: &str, ra_fixture: &str, expect: Expect, expect_s: Expect) {
+pub fn assert_expand(
+    macro_name: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    expect: Expect,
+    expect_s: Expect,
+) {
     assert_expand_impl(macro_name, ra_fixture, None, expect, expect_s);
 }
 
 pub fn assert_expand_attr(
     macro_name: &str,
-    ra_fixture: &str,
+    #[rust_analyzer::rust_fixture] ra_fixture: &str,
     attr_args: &str,
     expect: Expect,
     expect_s: Expect,
@@ -72,7 +81,7 @@ fn assert_expand_impl(
             file_id: EditionedFileId::current_edition(FileId::from_raw(41)),
             ast_id: ErasedFileAstId::from_raw(1),
         },
-        ctx: SyntaxContextId::ROOT,
+        ctx: SyntaxContextId::root(span::Edition::CURRENT),
     };
     let call_site = Span {
         range: TextRange::new(0.into(), 100.into()),
@@ -80,7 +89,7 @@ fn assert_expand_impl(
             file_id: EditionedFileId::current_edition(FileId::from_raw(42)),
             ast_id: ErasedFileAstId::from_raw(2),
         },
-        ctx: SyntaxContextId::ROOT,
+        ctx: SyntaxContextId::root(span::Edition::CURRENT),
     };
     let mixed_site = call_site;
 
@@ -98,7 +107,7 @@ fn assert_expand_impl(
 pub(crate) fn list() -> Vec {
     let dylib_path = proc_macro_test_dylib_path();
     let env = EnvSnapshot::default();
-    let mut srv = ProcMacroSrv::new(&env);
+    let srv = ProcMacroSrv::new(&env);
     let res = srv.list_macros(&dylib_path).unwrap();
     res.into_iter().map(|(name, kind)| format!("{name} [{kind:?}]")).collect()
 }
diff --git a/src/tools/rust-analyzer/crates/profile/Cargo.toml b/src/tools/rust-analyzer/crates/profile/Cargo.toml
index 2e3413f339b63..3179c810f6999 100644
--- a/src/tools/rust-analyzer/crates/profile/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/profile/Cargo.toml
@@ -21,7 +21,10 @@ jemalloc-ctl = { version = "0.5.0", package = "tikv-jemalloc-ctl", optional = tr
 perf-event = "=0.4.7"
 
 [target.'cfg(windows)'.dependencies]
-windows-sys = { version = "0.52", features = ["Win32_System_Threading", "Win32_System_ProcessStatus"] }
+windows-sys = { version = "0.59", features = [
+    "Win32_System_Threading",
+    "Win32_System_ProcessStatus",
+] }
 
 [features]
 cpu_profiler = []
diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs
index 524323b973630..b0939229f93e2 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs
@@ -172,19 +172,18 @@ impl WorkspaceBuildScripts {
         }
         let res = (|| {
             let target_libdir = (|| {
-                let mut cargo_config = sysroot.tool(Tool::Cargo);
+                let mut cargo_config = sysroot.tool(Tool::Cargo, current_dir);
                 cargo_config.envs(extra_env);
                 cargo_config
-                    .current_dir(current_dir)
                     .args(["rustc", "-Z", "unstable-options", "--print", "target-libdir"])
                     .env("RUSTC_BOOTSTRAP", "1");
-                if let Ok(it) = utf8_stdout(cargo_config) {
+                if let Ok(it) = utf8_stdout(&mut cargo_config) {
                     return Ok(it);
                 }
-                let mut cmd = sysroot.tool(Tool::Rustc);
+                let mut cmd = sysroot.tool(Tool::Rustc, current_dir);
                 cmd.envs(extra_env);
                 cmd.args(["--print", "target-libdir"]);
-                utf8_stdout(cmd)
+                utf8_stdout(&mut cmd)
             })()?;
 
             let target_libdir = AbsPathBuf::try_from(Utf8PathBuf::from(target_libdir))
@@ -390,12 +389,12 @@ impl WorkspaceBuildScripts {
     ) -> io::Result {
         let mut cmd = match config.run_build_script_command.as_deref() {
             Some([program, args @ ..]) => {
-                let mut cmd = Command::new(program);
+                let mut cmd = toolchain::command(program, current_dir);
                 cmd.args(args);
                 cmd
             }
             _ => {
-                let mut cmd = sysroot.tool(Tool::Cargo);
+                let mut cmd = sysroot.tool(Tool::Cargo, current_dir);
 
                 cmd.args(["check", "--quiet", "--workspace", "--message-format=json"]);
                 cmd.args(&config.extra_args);
@@ -448,7 +447,6 @@ impl WorkspaceBuildScripts {
             }
         };
 
-        cmd.current_dir(current_dir);
         cmd.envs(&config.extra_env);
         if config.wrap_rustc_in_build_scripts {
             // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use
diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
index ba4946bf0b99a..e4a6113462054 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
@@ -4,6 +4,7 @@ use std::ops;
 use std::str::from_utf8;
 
 use anyhow::Context;
+use base_db::Env;
 use cargo_metadata::{CargoOpt, MetadataCommand};
 use la_arena::{Arena, Idx};
 use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
@@ -13,8 +14,8 @@ use serde_json::from_value;
 use span::Edition;
 use toolchain::Tool;
 
-use crate::{utf8_stdout, ManifestPath, Sysroot, SysrootQueryMetadata};
 use crate::{CfgOverrides, InvocationStrategy};
+use crate::{ManifestPath, Sysroot};
 
 /// [`CargoWorkspace`] represents the logical structure of, well, a Cargo
 /// workspace. It pretty closely mirrors `cargo metadata` output.
@@ -34,6 +35,8 @@ pub struct CargoWorkspace {
     target_directory: AbsPathBuf,
     manifest_path: ManifestPath,
     is_virtual_workspace: bool,
+    /// Environment variables set in the `.cargo/config` file.
+    config_env: Env,
 }
 
 impl ops::Index for CargoWorkspace {
@@ -86,11 +89,11 @@ pub struct CargoConfig {
     pub target: Option,
     /// Sysroot loading behavior
     pub sysroot: Option,
-    /// How to query metadata for the sysroot crate.
-    pub sysroot_query_metadata: SysrootQueryMetadata,
     pub sysroot_src: Option,
     /// rustc private crate source
     pub rustc_source: Option,
+    /// Extra includes to add to the VFS.
+    pub extra_includes: Vec,
     pub cfg_overrides: CfgOverrides,
     /// Invoke `cargo check` through the RUSTC_WRAPPER.
     pub wrap_rustc_in_build_scripts: bool,
@@ -251,6 +254,18 @@ impl TargetKind {
     }
 }
 
+#[derive(Default, Clone, Debug, PartialEq, Eq)]
+pub struct CargoMetadataConfig {
+    /// List of features to activate.
+    pub features: CargoFeatures,
+    /// rustc targets
+    pub targets: Vec,
+    /// Extra args to pass to the cargo command.
+    pub extra_args: Vec,
+    /// Extra env vars to set when invoking the cargo command
+    pub extra_env: FxHashMap,
+}
+
 // Deserialize helper for the cargo metadata
 #[derive(Deserialize, Default)]
 struct PackageMetadata {
@@ -265,7 +280,7 @@ impl CargoWorkspace {
     pub fn fetch_metadata(
         cargo_toml: &ManifestPath,
         current_dir: &AbsPath,
-        config: &CargoConfig,
+        config: &CargoMetadataConfig,
         sysroot: &Sysroot,
         locked: bool,
         progress: &dyn Fn(String),
@@ -276,15 +291,13 @@ impl CargoWorkspace {
     fn fetch_metadata_(
         cargo_toml: &ManifestPath,
         current_dir: &AbsPath,
-        config: &CargoConfig,
+        config: &CargoMetadataConfig,
         sysroot: &Sysroot,
         locked: bool,
         no_deps: bool,
         progress: &dyn Fn(String),
     ) -> anyhow::Result<(cargo_metadata::Metadata, Option)> {
-        let targets = find_list_of_build_targets(config, cargo_toml, sysroot);
-
-        let cargo = sysroot.tool(Tool::Cargo);
+        let cargo = sysroot.tool(Tool::Cargo, current_dir);
         let mut meta = MetadataCommand::new();
         meta.cargo_path(cargo.get_program());
         cargo.get_envs().for_each(|(var, val)| _ = meta.env(var, val.unwrap_or_default()));
@@ -319,12 +332,9 @@ impl CargoWorkspace {
             }
         }
 
-        if !targets.is_empty() {
-            other_options.append(
-                &mut targets
-                    .into_iter()
-                    .flat_map(|target| ["--filter-platform".to_owned(), target])
-                    .collect(),
+        if !config.targets.is_empty() {
+            other_options.extend(
+                config.targets.iter().flat_map(|it| ["--filter-platform".to_owned(), it.clone()]),
             );
         }
         // The manifest is a rust file, so this means its a script manifest
@@ -388,6 +398,7 @@ impl CargoWorkspace {
     pub fn new(
         mut meta: cargo_metadata::Metadata,
         ws_manifest_path: ManifestPath,
+        cargo_config_env: Env,
     ) -> CargoWorkspace {
         let mut pkg_by_id = FxHashMap::default();
         let mut packages = Arena::default();
@@ -509,6 +520,7 @@ impl CargoWorkspace {
             target_directory,
             manifest_path: ws_manifest_path,
             is_virtual_workspace,
+            config_env: cargo_config_env,
         }
     }
 
@@ -595,80 +607,8 @@ impl CargoWorkspace {
     pub fn is_virtual_workspace(&self) -> bool {
         self.is_virtual_workspace
     }
-}
-
-fn find_list_of_build_targets(
-    config: &CargoConfig,
-    cargo_toml: &ManifestPath,
-    sysroot: &Sysroot,
-) -> Vec {
-    if let Some(target) = &config.target {
-        return [target.into()].to_vec();
-    }
-
-    let build_targets = cargo_config_build_target(cargo_toml, &config.extra_env, sysroot);
-    if !build_targets.is_empty() {
-        return build_targets;
-    }
-
-    rustc_discover_host_triple(cargo_toml, &config.extra_env, sysroot).into_iter().collect()
-}
-
-fn rustc_discover_host_triple(
-    cargo_toml: &ManifestPath,
-    extra_env: &FxHashMap,
-    sysroot: &Sysroot,
-) -> Option {
-    let mut rustc = sysroot.tool(Tool::Rustc);
-    rustc.envs(extra_env);
-    rustc.current_dir(cargo_toml.parent()).arg("-vV");
-    tracing::debug!("Discovering host platform by {:?}", rustc);
-    match utf8_stdout(rustc) {
-        Ok(stdout) => {
-            let field = "host: ";
-            let target = stdout.lines().find_map(|l| l.strip_prefix(field));
-            if let Some(target) = target {
-                Some(target.to_owned())
-            } else {
-                // If we fail to resolve the host platform, it's not the end of the world.
-                tracing::info!("rustc -vV did not report host platform, got:\n{}", stdout);
-                None
-            }
-        }
-        Err(e) => {
-            tracing::warn!("Failed to discover host platform: {}", e);
-            None
-        }
-    }
-}
-
-fn cargo_config_build_target(
-    cargo_toml: &ManifestPath,
-    extra_env: &FxHashMap,
-    sysroot: &Sysroot,
-) -> Vec {
-    let mut cargo_config = sysroot.tool(Tool::Cargo);
-    cargo_config.envs(extra_env);
-    cargo_config
-        .current_dir(cargo_toml.parent())
-        .args(["-Z", "unstable-options", "config", "get", "build.target"])
-        .env("RUSTC_BOOTSTRAP", "1");
-    // if successful we receive `build.target = "target-triple"`
-    // or `build.target = ["", ..]`
-    tracing::debug!("Discovering cargo config target by {:?}", cargo_config);
-    utf8_stdout(cargo_config).map(parse_output_cargo_config_build_target).unwrap_or_default()
-}
-
-fn parse_output_cargo_config_build_target(stdout: String) -> Vec {
-    let trimmed = stdout.trim_start_matches("build.target = ").trim_matches('"');
-
-    if !trimmed.starts_with('[') {
-        return [trimmed.to_owned()].to_vec();
-    }
 
-    let res = serde_json::from_str(trimmed);
-    if let Err(e) = &res {
-        tracing::warn!("Failed to parse `build.target` as an array of target: {}`", e);
+    pub fn env(&self) -> &Env {
+        &self.config_env
     }
-    res.unwrap_or_default()
 }
diff --git a/src/tools/rust-analyzer/crates/project-model/src/env.rs b/src/tools/rust-analyzer/crates/project-model/src/env.rs
index ff9d2035f60a7..37fffba295590 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/env.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/env.rs
@@ -1,9 +1,10 @@
 //! Cargo-like environment variables injection.
 use base_db::Env;
+use paths::Utf8Path;
 use rustc_hash::FxHashMap;
 use toolchain::Tool;
 
-use crate::{utf8_stdout, CargoWorkspace, ManifestPath, PackageData, Sysroot, TargetKind};
+use crate::{utf8_stdout, ManifestPath, PackageData, Sysroot, TargetKind};
 
 /// Recreates the compile-time environment variables that Cargo sets.
 ///
@@ -50,34 +51,23 @@ pub(crate) fn inject_cargo_env(env: &mut Env) {
     env.set("CARGO", Tool::Cargo.path().to_string());
 }
 
-pub(crate) fn inject_rustc_tool_env(
-    env: &mut Env,
-    cargo: &CargoWorkspace,
-    cargo_name: &str,
-    kind: TargetKind,
-) {
+pub(crate) fn inject_rustc_tool_env(env: &mut Env, cargo_name: &str, kind: TargetKind) {
     _ = kind;
     // FIXME
     // if kind.is_executable() {
     //     env.set("CARGO_BIN_NAME", cargo_name);
     // }
     env.set("CARGO_CRATE_NAME", cargo_name.replace('-', "_"));
-    // NOTE: Technically we should set this for all crates, but that will worsen the deduplication
-    // logic so for now just keeping it proc-macros ought to be fine.
-    if kind.is_proc_macro() {
-        env.set("CARGO_RUSTC_CURRENT_DIR", cargo.manifest_path().parent().to_string());
-    }
 }
 
 pub(crate) fn cargo_config_env(
     manifest: &ManifestPath,
     extra_env: &FxHashMap,
     sysroot: &Sysroot,
-) -> FxHashMap {
-    let mut cargo_config = sysroot.tool(Tool::Cargo);
+) -> Env {
+    let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent());
     cargo_config.envs(extra_env);
     cargo_config
-        .current_dir(manifest.parent())
         .args(["-Z", "unstable-options", "config", "get", "env"])
         .env("RUSTC_BOOTSTRAP", "1");
     if manifest.is_rust_manifest() {
@@ -85,8 +75,8 @@ pub(crate) fn cargo_config_env(
     }
     // if successful we receive `env.key.value = "value" per entry
     tracing::debug!("Discovering cargo config env by {:?}", cargo_config);
-    utf8_stdout(cargo_config)
-        .map(parse_output_cargo_config_env)
+    utf8_stdout(&mut cargo_config)
+        .map(|stdout| parse_output_cargo_config_env(manifest, &stdout))
         .inspect(|env| {
             tracing::debug!("Discovered cargo config env: {:?}", env);
         })
@@ -96,18 +86,61 @@ pub(crate) fn cargo_config_env(
         .unwrap_or_default()
 }
 
-fn parse_output_cargo_config_env(stdout: String) -> FxHashMap {
-    stdout
-        .lines()
-        .filter_map(|l| l.strip_prefix("env."))
-        .filter_map(|l| l.split_once(" = "))
-        .filter_map(|(k, v)| {
-            if k.contains('.') {
-                k.strip_suffix(".value").zip(Some(v))
-            } else {
-                Some((k, v))
+fn parse_output_cargo_config_env(manifest: &ManifestPath, stdout: &str) -> Env {
+    let mut env = Env::default();
+    let mut relatives = vec![];
+    for (key, val) in
+        stdout.lines().filter_map(|l| l.strip_prefix("env.")).filter_map(|l| l.split_once(" = "))
+    {
+        let val = val.trim_matches('"').to_owned();
+        if let Some((key, modifier)) = key.split_once('.') {
+            match modifier {
+                "relative" => relatives.push((key, val)),
+                "value" => _ = env.insert(key, val),
+                _ => {
+                    tracing::warn!(
+                        "Unknown modifier in cargo config env: {}, expected `relative` or `value`",
+                        modifier
+                    );
+                    continue;
+                }
             }
-        })
-        .map(|(key, value)| (key.to_owned(), value.trim_matches('"').to_owned()))
-        .collect()
+        } else {
+            env.insert(key, val);
+        }
+    }
+    // FIXME: The base here should be the parent of the `.cargo/config` file, not the manifest.
+    // But cargo does not provide this information.
+    let base = <_ as AsRef>::as_ref(manifest.parent());
+    for (key, relative) in relatives {
+        if relative != "true" {
+            continue;
+        }
+        if let Some(suffix) = env.get(key) {
+            env.insert(key, base.join(suffix).to_string());
+        }
+    }
+    env
+}
+
+#[test]
+fn parse_output_cargo_config_env_works() {
+    let stdout = r#"
+env.CARGO_WORKSPACE_DIR.relative = true
+env.CARGO_WORKSPACE_DIR.value = ""
+env.RELATIVE.relative = true
+env.RELATIVE.value = "../relative"
+env.INVALID.relative = invalidbool
+env.INVALID.value = "../relative"
+env.TEST.value = "test"
+"#
+    .trim();
+    let cwd = paths::Utf8PathBuf::try_from(std::env::current_dir().unwrap()).unwrap();
+    let manifest = paths::AbsPathBuf::assert(cwd.join("Cargo.toml"));
+    let manifest = ManifestPath::try_from(manifest).unwrap();
+    let env = parse_output_cargo_config_env(&manifest, stdout);
+    assert_eq!(env.get("CARGO_WORKSPACE_DIR").as_deref(), Some(cwd.join("").as_str()));
+    assert_eq!(env.get("RELATIVE").as_deref(), Some(cwd.join("../relative").as_str()));
+    assert_eq!(env.get("INVALID").as_deref(), Some("../relative"));
+    assert_eq!(env.get("TEST").as_deref(), Some("test"));
 }
diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs
index da8afc5d3a184..fc1fd7b877fcc 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs
@@ -15,14 +15,32 @@
 //!   procedural macros).
 //! * Lowering of concrete model to a [`base_db::CrateGraph`]
 
+pub mod project_json;
+pub mod toolchain_info {
+    pub mod rustc_cfg;
+    pub mod target_data_layout;
+    pub mod target_tuple;
+    pub mod version;
+
+    use std::path::Path;
+
+    use crate::{ManifestPath, Sysroot};
+
+    #[derive(Copy, Clone)]
+    pub enum QueryConfig<'a> {
+        /// Directly invoke `rustc` to query the desired information.
+        Rustc(&'a Sysroot, &'a Path),
+        /// Attempt to use cargo to query the desired information, honoring cargo configurations.
+        /// If this fails, falls back to invoking `rustc` directly.
+        Cargo(&'a Sysroot, &'a ManifestPath),
+    }
+}
+
 mod build_dependencies;
 mod cargo_workspace;
 mod env;
 mod manifest_path;
-pub mod project_json;
-mod rustc_cfg;
 mod sysroot;
-pub mod target_data_layout;
 mod workspace;
 
 #[cfg(test)]
@@ -42,8 +60,8 @@ use rustc_hash::FxHashSet;
 pub use crate::{
     build_dependencies::WorkspaceBuildScripts,
     cargo_workspace::{
-        CargoConfig, CargoFeatures, CargoWorkspace, Package, PackageData, PackageDependency,
-        RustLibSource, Target, TargetData, TargetKind,
+        CargoConfig, CargoFeatures, CargoMetadataConfig, CargoWorkspace, Package, PackageData,
+        PackageDependency, RustLibSource, Target, TargetData, TargetKind,
     },
     manifest_path::ManifestPath,
     project_json::{ProjectJson, ProjectJsonData},
@@ -181,7 +199,7 @@ impl fmt::Display for ProjectManifest {
     }
 }
 
-fn utf8_stdout(mut cmd: Command) -> anyhow::Result {
+fn utf8_stdout(cmd: &mut Command) -> anyhow::Result {
     let output = cmd.output().with_context(|| format!("{cmd:?} failed"))?;
     if !output.status.success() {
         match String::from_utf8(output.stderr) {
@@ -241,9 +259,20 @@ fn parse_cfg(s: &str) -> Result {
     Ok(res)
 }
 
-#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
-pub enum SysrootQueryMetadata {
-    #[default]
-    CargoMetadata,
-    None,
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum SysrootSourceWorkspaceConfig {
+    CargoMetadata(CargoMetadataConfig),
+    Stitched,
+}
+
+impl Default for SysrootSourceWorkspaceConfig {
+    fn default() -> Self {
+        SysrootSourceWorkspaceConfig::default_cargo()
+    }
+}
+
+impl SysrootSourceWorkspaceConfig {
+    pub fn default_cargo() -> Self {
+        SysrootSourceWorkspaceConfig::CargoMetadata(Default::default())
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs b/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs
index a72dab607525f..73a4e6e121691 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs
@@ -46,7 +46,7 @@ impl ManifestPath {
     }
 
     pub fn is_rust_manifest(&self) -> bool {
-        self.file.extension().map_or(false, |ext| ext == "rs")
+        self.file.extension() == Some("rs")
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
index 6a88cf022dfb0..feee40a1fc9b2 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
@@ -63,7 +63,7 @@ use crate::{ManifestPath, TargetKind};
 pub struct ProjectJson {
     /// e.g. `path/to/sysroot`
     pub(crate) sysroot: Option,
-    /// e.g. `path/to/sysroot/lib/rustlib/src/rust`
+    /// e.g. `path/to/sysroot/lib/rustlib/src/rust/library`
     pub(crate) sysroot_src: Option,
     project_root: AbsPathBuf,
     /// The path to the rust-project.json file. May be None if this
@@ -508,5 +508,5 @@ fn serialize_crate_name(name: &CrateName, se: S) -> Result
 where
     S: serde::Serializer,
 {
-    se.serialize_str(name)
+    se.serialize_str(name.as_str())
 }
diff --git a/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs b/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs
deleted file mode 100644
index bc1f0e6fbf262..0000000000000
--- a/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs
+++ /dev/null
@@ -1,99 +0,0 @@
-//! Runs `rustc --print cfg` to get built-in cfg flags.
-
-use anyhow::Context;
-use cfg::CfgAtom;
-use intern::Symbol;
-use rustc_hash::FxHashMap;
-use toolchain::Tool;
-
-use crate::{utf8_stdout, ManifestPath, Sysroot};
-
-/// Determines how `rustc --print cfg` is discovered and invoked.
-pub(crate) enum RustcCfgConfig<'a> {
-    /// Use `rustc --print cfg`, either from with the binary from the sysroot or by discovering via
-    /// [`toolchain::rustc`].
-    Rustc(&'a Sysroot),
-    /// Use `cargo --print cfg`, either from with the binary from the sysroot or by discovering via
-    /// [`toolchain::cargo`].
-    Cargo(&'a Sysroot, &'a ManifestPath),
-}
-
-pub(crate) fn get(
-    target: Option<&str>,
-    extra_env: &FxHashMap,
-    config: RustcCfgConfig<'_>,
-) -> Vec {
-    let _p = tracing::info_span!("rustc_cfg::get").entered();
-    let mut res: Vec<_> = Vec::with_capacity(7 * 2 + 1);
-
-    // Some nightly-only cfgs, which are required for stdlib
-    res.push(CfgAtom::Flag(Symbol::intern("target_thread_local")));
-    for key in ["target_has_atomic", "target_has_atomic_load_store"] {
-        for ty in ["8", "16", "32", "64", "cas", "ptr"] {
-            res.push(CfgAtom::KeyValue { key: Symbol::intern(key), value: Symbol::intern(ty) });
-        }
-        res.push(CfgAtom::Flag(Symbol::intern(key)));
-    }
-
-    let rustc_cfgs = get_rust_cfgs(target, extra_env, config);
-
-    let rustc_cfgs = match rustc_cfgs {
-        Ok(cfgs) => cfgs,
-        Err(e) => {
-            tracing::error!(?e, "failed to get rustc cfgs");
-            return res;
-        }
-    };
-
-    let rustc_cfgs = rustc_cfgs.lines().map(crate::parse_cfg).collect::, _>>();
-
-    match rustc_cfgs {
-        Ok(rustc_cfgs) => {
-            tracing::debug!(?rustc_cfgs, "rustc cfgs found");
-            res.extend(rustc_cfgs);
-        }
-        Err(e) => {
-            tracing::error!(?e, "failed to get rustc cfgs")
-        }
-    }
-
-    res
-}
-
-fn get_rust_cfgs(
-    target: Option<&str>,
-    extra_env: &FxHashMap,
-    config: RustcCfgConfig<'_>,
-) -> anyhow::Result {
-    let sysroot = match config {
-        RustcCfgConfig::Cargo(sysroot, cargo_toml) => {
-            let mut cmd = sysroot.tool(Tool::Cargo);
-
-            cmd.envs(extra_env);
-            cmd.current_dir(cargo_toml.parent())
-                .args(["rustc", "-Z", "unstable-options", "--print", "cfg"])
-                .env("RUSTC_BOOTSTRAP", "1");
-            if let Some(target) = target {
-                cmd.args(["--target", target]);
-            }
-
-            match utf8_stdout(cmd) {
-                Ok(it) => return Ok(it),
-                Err(e) => {
-                    tracing::warn!("failed to run `cargo rustc --print cfg`, falling back to invoking rustc directly: {e}");
-                    sysroot
-                }
-            }
-        }
-        RustcCfgConfig::Rustc(sysroot) => sysroot,
-    };
-
-    let mut cmd = sysroot.tool(Tool::Rustc);
-    cmd.envs(extra_env);
-    cmd.args(["--print", "cfg", "-O"]);
-    if let Some(target) = target {
-        cmd.args(["--target", target]);
-    }
-
-    utf8_stdout(cmd).context("unable to fetch cfgs via `rustc --print cfg -O`")
-}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
index 8426e689a64d1..8f633d24be9a2 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
@@ -4,7 +4,12 @@
 //! but we can't process `.rlib` and need source code instead. The source code
 //! is typically installed with `rustup component add rust-src` command.
 
-use std::{env, fs, ops, process::Command};
+use std::{
+    env, fs,
+    ops::{self, Not},
+    path::Path,
+    process::Command,
+};
 
 use anyhow::{format_err, Result};
 use base_db::CrateName;
@@ -12,20 +17,24 @@ use itertools::Itertools;
 use la_arena::{Arena, Idx};
 use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
 use rustc_hash::FxHashMap;
+use stdx::format_to;
 use toolchain::{probe_for_binary, Tool};
 
-use crate::{utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath, SysrootQueryMetadata};
+use crate::{
+    cargo_workspace::CargoMetadataConfig, utf8_stdout, CargoWorkspace, ManifestPath,
+    SysrootSourceWorkspaceConfig,
+};
 
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct Sysroot {
     root: Option,
     src_root: Option,
-    mode: SysrootMode,
+    workspace: SysrootWorkspace,
     error: Option,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
-pub(crate) enum SysrootMode {
+pub(crate) enum SysrootWorkspace {
     Workspace(CargoWorkspace),
     Stitched(Stitched),
     Empty,
@@ -79,7 +88,7 @@ pub(crate) struct SysrootCrateData {
 
 impl Sysroot {
     pub const fn empty() -> Sysroot {
-        Sysroot { root: None, src_root: None, mode: SysrootMode::Empty, error: None }
+        Sysroot { root: None, src_root: None, workspace: SysrootWorkspace::Empty, error: None }
     }
 
     /// Returns sysroot "root" directory, where `bin/`, `etc/`, `lib/`, `libexec/`
@@ -96,10 +105,10 @@ impl Sysroot {
     }
 
     pub fn is_empty(&self) -> bool {
-        match &self.mode {
-            SysrootMode::Workspace(ws) => ws.packages().next().is_none(),
-            SysrootMode::Stitched(stitched) => stitched.crates.is_empty(),
-            SysrootMode::Empty => true,
+        match &self.workspace {
+            SysrootWorkspace::Workspace(ws) => ws.packages().next().is_none(),
+            SysrootWorkspace::Stitched(stitched) => stitched.crates.is_empty(),
+            SysrootWorkspace::Empty => true,
         }
     }
 
@@ -108,66 +117,53 @@ impl Sysroot {
     }
 
     pub fn num_packages(&self) -> usize {
-        match &self.mode {
-            SysrootMode::Workspace(ws) => ws.packages().count(),
-            SysrootMode::Stitched(c) => c.crates().count(),
-            SysrootMode::Empty => 0,
+        match &self.workspace {
+            SysrootWorkspace::Workspace(ws) => ws.packages().count(),
+            SysrootWorkspace::Stitched(c) => c.crates().count(),
+            SysrootWorkspace::Empty => 0,
         }
     }
 
-    pub(crate) fn mode(&self) -> &SysrootMode {
-        &self.mode
+    pub(crate) fn workspace(&self) -> &SysrootWorkspace {
+        &self.workspace
     }
 }
 
-// FIXME: Expose a builder api as loading the sysroot got way too modular and complicated.
 impl Sysroot {
     /// Attempts to discover the toolchain's sysroot from the given `dir`.
-    pub fn discover(
-        dir: &AbsPath,
-        extra_env: &FxHashMap,
-        sysroot_query_metadata: SysrootQueryMetadata,
-    ) -> Sysroot {
+    pub fn discover(dir: &AbsPath, extra_env: &FxHashMap) -> Sysroot {
         let sysroot_dir = discover_sysroot_dir(dir, extra_env);
         let sysroot_src_dir = sysroot_dir.as_ref().ok().map(|sysroot_dir| {
             discover_sysroot_src_dir_or_add_component(sysroot_dir, dir, extra_env)
         });
-        Sysroot::load_core_check(Some(sysroot_dir), sysroot_src_dir, sysroot_query_metadata)
+        Sysroot::assemble(Some(sysroot_dir), sysroot_src_dir)
     }
 
     pub fn discover_with_src_override(
         current_dir: &AbsPath,
         extra_env: &FxHashMap,
         sysroot_src_dir: AbsPathBuf,
-        sysroot_query_metadata: SysrootQueryMetadata,
     ) -> Sysroot {
         let sysroot_dir = discover_sysroot_dir(current_dir, extra_env);
-        Sysroot::load_core_check(
-            Some(sysroot_dir),
-            Some(Ok(sysroot_src_dir)),
-            sysroot_query_metadata,
-        )
+        Sysroot::assemble(Some(sysroot_dir), Some(Ok(sysroot_src_dir)))
     }
 
-    pub fn discover_sysroot_src_dir(
-        sysroot_dir: AbsPathBuf,
-        sysroot_query_metadata: SysrootQueryMetadata,
-    ) -> Sysroot {
+    pub fn discover_sysroot_src_dir(sysroot_dir: AbsPathBuf) -> Sysroot {
         let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir)
             .ok_or_else(|| format_err!("can't find standard library sources in {sysroot_dir}"));
-        Sysroot::load_core_check(
-            Some(Ok(sysroot_dir)),
-            Some(sysroot_src_dir),
-            sysroot_query_metadata,
-        )
+        Sysroot::assemble(Some(Ok(sysroot_dir)), Some(sysroot_src_dir))
     }
 
     pub fn discover_rustc_src(&self) -> Option {
         get_rustc_src(self.root()?)
     }
 
+    pub fn new(sysroot_dir: Option, sysroot_src_dir: Option) -> Sysroot {
+        Self::assemble(sysroot_dir.map(Ok), sysroot_src_dir.map(Ok))
+    }
+
     /// Returns a command to run a tool preferring the cargo proxies if the sysroot exists.
-    pub fn tool(&self, tool: Tool) -> Command {
+    pub fn tool(&self, tool: Tool, current_dir: impl AsRef) -> Command {
         match self.root() {
             Some(root) => {
                 // special case rustc, we can look that up directly in the sysroot's bin folder
@@ -176,15 +172,15 @@ impl Sysroot {
                     if let Some(path) =
                         probe_for_binary(root.join("bin").join(Tool::Rustc.name()).into())
                     {
-                        return Command::new(path);
+                        return toolchain::command(path, current_dir);
                     }
                 }
 
-                let mut cmd = Command::new(tool.prefer_proxy());
+                let mut cmd = toolchain::command(tool.prefer_proxy(), current_dir);
                 cmd.env("RUSTUP_TOOLCHAIN", AsRef::::as_ref(root));
                 cmd
             }
-            _ => Command::new(tool.path()),
+            _ => toolchain::command(tool.path(), current_dir),
         }
     }
 
@@ -202,90 +198,51 @@ impl Sysroot {
             })
     }
 
-    pub fn load(
-        sysroot_dir: Option,
-        sysroot_src_dir: Option,
-        sysroot_query_metadata: SysrootQueryMetadata,
-    ) -> Sysroot {
-        Self::load_core_check(sysroot_dir.map(Ok), sysroot_src_dir.map(Ok), sysroot_query_metadata)
-    }
-
-    fn load_core_check(
+    fn assemble(
         sysroot_dir: Option>,
         sysroot_src_dir: Option>,
-        sysroot_query_metadata: SysrootQueryMetadata,
     ) -> Sysroot {
-        let mut sysroot = Self::load_(sysroot_dir, sysroot_src_dir, sysroot_query_metadata);
-        if sysroot.error.is_none() {
-            if let Some(src_root) = &sysroot.src_root {
-                let has_core = match &sysroot.mode {
-                    SysrootMode::Workspace(ws) => ws.packages().any(|p| ws[p].name == "core"),
-                    SysrootMode::Stitched(stitched) => stitched.by_name("core").is_some(),
-                    SysrootMode::Empty => true,
-                };
-                if !has_core {
-                    let var_note = if env::var_os("RUST_SRC_PATH").is_some() {
-                        " (env var `RUST_SRC_PATH` is set and may be incorrect, try unsetting it)"
-                    } else {
-                        ", try running `rustup component add rust-src` to possibly fix this"
-                    };
-                    sysroot.error = Some(format!(
-                        "sysroot at `{src_root}` is missing a `core` library{var_note}",
-                    ));
-                }
-            }
-        }
-        sysroot
-    }
-
-    fn load_(
-        sysroot_dir: Option>,
-        sysroot_src_dir: Option>,
-        sysroot_query_metadata: SysrootQueryMetadata,
-    ) -> Sysroot {
-        let sysroot_dir = match sysroot_dir {
+        let mut errors = String::new();
+        let root = match sysroot_dir {
             Some(Ok(sysroot_dir)) => Some(sysroot_dir),
             Some(Err(e)) => {
-                return Sysroot {
-                    root: None,
-                    src_root: None,
-                    mode: SysrootMode::Empty,
-                    error: Some(e.to_string()),
-                }
+                format_to!(errors, "{e}\n");
+                None
             }
             None => None,
         };
-        let sysroot_src_dir = match sysroot_src_dir {
-            Some(Ok(sysroot_src_dir)) => sysroot_src_dir,
+        let src_root = match sysroot_src_dir {
+            Some(Ok(sysroot_src_dir)) => Some(sysroot_src_dir),
             Some(Err(e)) => {
-                return Sysroot {
-                    root: sysroot_dir,
-                    src_root: None,
-                    mode: SysrootMode::Empty,
-                    error: Some(e.to_string()),
-                }
-            }
-            None => {
-                return Sysroot {
-                    root: sysroot_dir,
-                    src_root: None,
-                    mode: SysrootMode::Empty,
-                    error: None,
-                }
+                format_to!(errors, "{e}\n");
+                None
             }
+            None => None,
         };
-        if sysroot_query_metadata == SysrootQueryMetadata::CargoMetadata {
-            let library_manifest =
-                ManifestPath::try_from(sysroot_src_dir.join("Cargo.toml")).unwrap();
+        Sysroot {
+            root,
+            src_root,
+            workspace: SysrootWorkspace::Empty,
+            error: errors.is_empty().not().then_some(errors),
+        }
+    }
+
+    pub fn load_workspace(&mut self, sysroot_source_config: &SysrootSourceWorkspaceConfig) {
+        assert!(matches!(self.workspace, SysrootWorkspace::Empty), "workspace already loaded");
+        let Self { root: _, src_root: Some(src_root), workspace, error: _ } = self else { return };
+        if let SysrootSourceWorkspaceConfig::CargoMetadata(cargo_config) = sysroot_source_config {
+            let library_manifest = ManifestPath::try_from(src_root.join("Cargo.toml")).unwrap();
             if fs::metadata(&library_manifest).is_ok() {
-                if let Some(sysroot) =
-                    Self::load_library_via_cargo(library_manifest, &sysroot_dir, &sysroot_src_dir)
+                if let Some(loaded) =
+                    Self::load_library_via_cargo(library_manifest, src_root, cargo_config)
                 {
-                    return sysroot;
+                    *workspace = loaded;
+                    self.load_core_check();
+                    return;
                 }
             }
         }
-        tracing::debug!("Stitching sysroot library: {sysroot_src_dir}");
+        tracing::debug!("Stitching sysroot library: {src_root}");
 
         let mut stitched = Stitched { crates: Arena::default() };
 
@@ -293,7 +250,7 @@ impl Sysroot {
             let name = path.split('/').last().unwrap();
             let root = [format!("{path}/src/lib.rs"), format!("lib{path}/lib.rs")]
                 .into_iter()
-                .map(|it| sysroot_src_dir.join(it))
+                .map(|it| src_root.join(it))
                 .filter_map(|it| ManifestPath::try_from(it).ok())
                 .find(|it| fs::metadata(it).is_ok());
 
@@ -329,21 +286,39 @@ impl Sysroot {
                 }
             }
         }
-        Sysroot {
-            root: sysroot_dir,
-            src_root: Some(sysroot_src_dir),
-            mode: SysrootMode::Stitched(stitched),
-            error: None,
+        *workspace = SysrootWorkspace::Stitched(stitched);
+        self.load_core_check();
+    }
+
+    fn load_core_check(&mut self) {
+        if self.error.is_none() {
+            if let Some(src_root) = &self.src_root {
+                let has_core = match &self.workspace {
+                    SysrootWorkspace::Workspace(ws) => ws.packages().any(|p| ws[p].name == "core"),
+                    SysrootWorkspace::Stitched(stitched) => stitched.by_name("core").is_some(),
+                    SysrootWorkspace::Empty => true,
+                };
+                if !has_core {
+                    let var_note = if env::var_os("RUST_SRC_PATH").is_some() {
+                        " (env var `RUST_SRC_PATH` is set and may be incorrect, try unsetting it)"
+                    } else {
+                        ", try running `rustup component add rust-src` to possibly fix this"
+                    };
+                    self.error = Some(format!(
+                        "sysroot at `{src_root}` is missing a `core` library{var_note}",
+                    ));
+                }
+            }
         }
     }
 
     fn load_library_via_cargo(
         library_manifest: ManifestPath,
-        sysroot_dir: &Option,
         sysroot_src_dir: &AbsPathBuf,
-    ) -> Option {
+        cargo_config: &CargoMetadataConfig,
+    ) -> Option {
         tracing::debug!("Loading library metadata: {library_manifest}");
-        let mut cargo_config = CargoConfig::default();
+        let mut cargo_config = cargo_config.clone();
         // the sysroot uses `public-dependency`, so we make cargo think it's a nightly
         cargo_config.extra_env.insert(
             "__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS".to_owned(),
@@ -415,13 +390,8 @@ impl Sysroot {
             res.packages.remove(idx);
         });
 
-        let cargo_workspace = CargoWorkspace::new(res, library_manifest);
-        Some(Sysroot {
-            root: sysroot_dir.clone(),
-            src_root: Some(sysroot_src_dir.clone()),
-            mode: SysrootMode::Workspace(cargo_workspace),
-            error: None,
-        })
+        let cargo_workspace = CargoWorkspace::new(res, library_manifest, Default::default());
+        Some(SysrootWorkspace::Workspace(cargo_workspace))
     }
 }
 
@@ -429,11 +399,11 @@ fn discover_sysroot_dir(
     current_dir: &AbsPath,
     extra_env: &FxHashMap,
 ) -> Result {
-    let mut rustc = Command::new(Tool::Rustc.path());
+    let mut rustc = toolchain::command(Tool::Rustc.path(), current_dir);
     rustc.envs(extra_env);
     rustc.current_dir(current_dir).args(["--print", "sysroot"]);
     tracing::debug!("Discovering sysroot by {:?}", rustc);
-    let stdout = utf8_stdout(rustc)?;
+    let stdout = utf8_stdout(&mut rustc)?;
     Ok(AbsPathBuf::assert(Utf8PathBuf::from(stdout)))
 }
 
@@ -461,11 +431,11 @@ fn discover_sysroot_src_dir_or_add_component(
 ) -> Result {
     discover_sysroot_src_dir(sysroot_path)
         .or_else(|| {
-            let mut rustup = Command::new(Tool::Rustup.prefer_proxy());
+            let mut rustup = toolchain::command(Tool::Rustup.prefer_proxy(), current_dir);
             rustup.envs(extra_env);
-            rustup.current_dir(current_dir).args(["component", "add", "rust-src"]);
+            rustup.args(["component", "add", "rust-src"]);
             tracing::info!("adding rust-src component by {:?}", rustup);
-            utf8_stdout(rustup).ok()?;
+            utf8_stdout(&mut rustup).ok()?;
             get_rust_src(sysroot_path)
         })
         .ok_or_else(|| {
diff --git a/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs b/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs
deleted file mode 100644
index 8a8a2d32558b3..0000000000000
--- a/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs
+++ /dev/null
@@ -1,67 +0,0 @@
-//! Runs `rustc --print target-spec-json` to get the target_data_layout.
-
-use rustc_hash::FxHashMap;
-use toolchain::Tool;
-
-use crate::{utf8_stdout, ManifestPath, Sysroot};
-
-/// Determines how `rustc --print target-spec-json` is discovered and invoked.
-pub enum RustcDataLayoutConfig<'a> {
-    /// Use `rustc --print target-spec-json`, either from with the binary from the sysroot or by discovering via
-    /// [`toolchain::rustc`].
-    Rustc(&'a Sysroot),
-    /// Use `cargo --print target-spec-json`, either from with the binary from the sysroot or by discovering via
-    /// [`toolchain::cargo`].
-    Cargo(&'a Sysroot, &'a ManifestPath),
-}
-
-pub fn get(
-    config: RustcDataLayoutConfig<'_>,
-    target: Option<&str>,
-    extra_env: &FxHashMap,
-) -> anyhow::Result {
-    let process = |output: String| {
-        (|| Some(output.split_once(r#""data-layout": ""#)?.1.split_once('"')?.0.to_owned()))()
-            .ok_or_else(|| {
-                anyhow::format_err!("could not fetch target-spec-json from command output")
-            })
-    };
-    let sysroot = match config {
-        RustcDataLayoutConfig::Cargo(sysroot, cargo_toml) => {
-            let mut cmd = sysroot.tool(Tool::Cargo);
-            cmd.envs(extra_env);
-            cmd.current_dir(cargo_toml.parent())
-                .args([
-                    "rustc",
-                    "-Z",
-                    "unstable-options",
-                    "--print",
-                    "target-spec-json",
-                    "--",
-                    "-Z",
-                    "unstable-options",
-                ])
-                .env("RUSTC_BOOTSTRAP", "1");
-            if let Some(target) = target {
-                cmd.args(["--target", target]);
-            }
-            match utf8_stdout(cmd) {
-                Ok(output) => return process(output),
-                Err(e) => {
-                    tracing::warn!("failed to run `cargo rustc --print target-spec-json`, falling back to invoking rustc directly: {e}");
-                    sysroot
-                }
-            }
-        }
-        RustcDataLayoutConfig::Rustc(sysroot) => sysroot,
-    };
-
-    let mut cmd = Sysroot::tool(sysroot, Tool::Rustc);
-    cmd.envs(extra_env)
-        .args(["-Z", "unstable-options", "--print", "target-spec-json"])
-        .env("RUSTC_BOOTSTRAP", "1");
-    if let Some(target) = target {
-        cmd.args(["--target", target]);
-    }
-    process(utf8_stdout(cmd)?)
-}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
index f3cf2d83eaca6..2856086543666 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
@@ -12,39 +12,45 @@ use span::FileId;
 use triomphe::Arc;
 
 use crate::{
-    sysroot::SysrootMode, workspace::ProjectWorkspaceKind, CargoWorkspace, CfgOverrides,
-    ManifestPath, ProjectJson, ProjectJsonData, ProjectWorkspace, Sysroot, SysrootQueryMetadata,
-    WorkspaceBuildScripts,
+    sysroot::SysrootWorkspace, workspace::ProjectWorkspaceKind, CargoWorkspace, CfgOverrides,
+    ManifestPath, ProjectJson, ProjectJsonData, ProjectWorkspace, Sysroot,
+    SysrootSourceWorkspaceConfig, WorkspaceBuildScripts,
 };
 
 fn load_cargo(file: &str) -> (CrateGraph, ProcMacroPaths) {
-    load_cargo_with_overrides(file, CfgOverrides::default())
+    let project_workspace = load_workspace_from_metadata(file);
+    to_crate_graph(project_workspace, &mut Default::default())
 }
 
 fn load_cargo_with_overrides(
     file: &str,
     cfg_overrides: CfgOverrides,
 ) -> (CrateGraph, ProcMacroPaths) {
+    let project_workspace =
+        ProjectWorkspace { cfg_overrides, ..load_workspace_from_metadata(file) };
+    to_crate_graph(project_workspace, &mut Default::default())
+}
+
+fn load_workspace_from_metadata(file: &str) -> ProjectWorkspace {
     let meta: Metadata = get_test_json_file(file);
     let manifest_path =
         ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap();
-    let cargo_workspace = CargoWorkspace::new(meta, manifest_path);
-    let project_workspace = ProjectWorkspace {
+    let cargo_workspace = CargoWorkspace::new(meta, manifest_path, Default::default());
+    ProjectWorkspace {
         kind: ProjectWorkspaceKind::Cargo {
             cargo: cargo_workspace,
             build_scripts: WorkspaceBuildScripts::default(),
             rustc: Err(None),
-            cargo_config_extra_env: Default::default(),
             error: None,
             set_test: true,
         },
-        cfg_overrides,
+        cfg_overrides: Default::default(),
         sysroot: Sysroot::empty(),
         rustc_cfg: Vec::new(),
         toolchain: None,
         target_layout: Err("target_data_layout not loaded".into()),
-    };
-    to_crate_graph(project_workspace)
+        extra_includes: Vec::new(),
+    }
 }
 
 fn load_rust_project(file: &str) -> (CrateGraph, ProcMacroPaths) {
@@ -58,8 +64,9 @@ fn load_rust_project(file: &str) -> (CrateGraph, ProcMacroPaths) {
         toolchain: None,
         target_layout: Err(Arc::from("test has no data layout")),
         cfg_overrides: Default::default(),
+        extra_includes: Vec::new(),
     };
-    to_crate_graph(project_workspace)
+    to_crate_graph(project_workspace, &mut Default::default())
 }
 
 fn get_test_json_file(file: &str) -> T {
@@ -117,7 +124,9 @@ fn get_fake_sysroot() -> Sysroot {
     // fake sysroot, so we give them both the same path:
     let sysroot_dir = AbsPathBuf::assert(sysroot_path);
     let sysroot_src_dir = sysroot_dir.clone();
-    Sysroot::load(Some(sysroot_dir), Some(sysroot_src_dir), SysrootQueryMetadata::CargoMetadata)
+    let mut sysroot = Sysroot::new(Some(sysroot_dir), Some(sysroot_src_dir));
+    sysroot.load_workspace(&SysrootSourceWorkspaceConfig::default_cargo());
+    sysroot
 }
 
 fn rooted_project_json(data: ProjectJsonData) -> ProjectJson {
@@ -128,13 +137,15 @@ fn rooted_project_json(data: ProjectJsonData) -> ProjectJson {
     ProjectJson::new(None, base, data)
 }
 
-fn to_crate_graph(project_workspace: ProjectWorkspace) -> (CrateGraph, ProcMacroPaths) {
+fn to_crate_graph(
+    project_workspace: ProjectWorkspace,
+    file_map: &mut FxHashMap,
+) -> (CrateGraph, ProcMacroPaths) {
     project_workspace.to_crate_graph(
         &mut {
-            let mut counter = 0;
-            move |_path| {
-                counter += 1;
-                Some(FileId::from_raw(counter))
+            |path| {
+                let len = file_map.len() + 1;
+                Some(*file_map.entry(path.to_path_buf()).or_insert(FileId::from_raw(len as u32)))
             }
         },
         &Default::default(),
@@ -219,7 +230,34 @@ fn rust_project_is_proc_macro_has_proc_macro_dep() {
     let crate_data = &crate_graph[crate_id];
     // Assert that the project crate with `is_proc_macro` has a dependency
     // on the proc_macro sysroot crate.
-    crate_data.dependencies.iter().find(|&dep| dep.name.deref() == "proc_macro").unwrap();
+    crate_data.dependencies.iter().find(|&dep| *dep.name.deref() == sym::proc_macro).unwrap();
+}
+
+#[test]
+fn crate_graph_dedup_identical() {
+    let (mut crate_graph, proc_macros) = load_cargo("regex-metadata.json");
+
+    let (d_crate_graph, mut d_proc_macros) = (crate_graph.clone(), proc_macros.clone());
+
+    crate_graph.extend(d_crate_graph.clone(), &mut d_proc_macros);
+    assert!(crate_graph.iter().eq(d_crate_graph.iter()));
+    assert_eq!(proc_macros, d_proc_macros);
+}
+
+#[test]
+fn crate_graph_dedup() {
+    let mut file_map = Default::default();
+
+    let ripgrep_workspace = load_workspace_from_metadata("ripgrep-metadata.json");
+    let (mut crate_graph, _proc_macros) = to_crate_graph(ripgrep_workspace, &mut file_map);
+    assert_eq!(crate_graph.iter().count(), 71);
+
+    let regex_workspace = load_workspace_from_metadata("regex-metadata.json");
+    let (regex_crate_graph, mut regex_proc_macros) = to_crate_graph(regex_workspace, &mut file_map);
+    assert_eq!(regex_crate_graph.iter().count(), 50);
+
+    crate_graph.extend(regex_crate_graph, &mut regex_proc_macros);
+    assert_eq!(crate_graph.iter().count(), 108);
 }
 
 #[test]
@@ -228,19 +266,18 @@ fn smoke_test_real_sysroot_cargo() {
     let meta: Metadata = get_test_json_file("hello-world-metadata.json");
     let manifest_path =
         ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap();
-    let cargo_workspace = CargoWorkspace::new(meta, manifest_path);
-    let sysroot = Sysroot::discover(
+    let cargo_workspace = CargoWorkspace::new(meta, manifest_path, Default::default());
+    let mut sysroot = Sysroot::discover(
         AbsPath::assert(Utf8Path::new(env!("CARGO_MANIFEST_DIR"))),
         &Default::default(),
-        SysrootQueryMetadata::CargoMetadata,
     );
-    assert!(matches!(sysroot.mode(), SysrootMode::Workspace(_)));
+    sysroot.load_workspace(&SysrootSourceWorkspaceConfig::default_cargo());
+    assert!(matches!(sysroot.workspace(), SysrootWorkspace::Workspace(_)));
     let project_workspace = ProjectWorkspace {
         kind: ProjectWorkspaceKind::Cargo {
             cargo: cargo_workspace,
             build_scripts: WorkspaceBuildScripts::default(),
             rustc: Err(None),
-            cargo_config_extra_env: Default::default(),
             error: None,
             set_test: true,
         },
@@ -249,6 +286,7 @@ fn smoke_test_real_sysroot_cargo() {
         cfg_overrides: Default::default(),
         toolchain: None,
         target_layout: Err("target_data_layout not loaded".into()),
+        extra_includes: Vec::new(),
     };
     project_workspace.to_crate_graph(
         &mut {
diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs
new file mode 100644
index 0000000000000..e472da0c89b0d
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs
@@ -0,0 +1,124 @@
+//! Get the built-in cfg flags for the to be compile platform.
+
+use anyhow::Context;
+use cfg::CfgAtom;
+use rustc_hash::FxHashMap;
+use toolchain::Tool;
+
+use crate::{toolchain_info::QueryConfig, utf8_stdout};
+
+/// Uses `rustc --print cfg` to fetch the builtin cfgs.
+pub fn get(
+    config: QueryConfig<'_>,
+    target: Option<&str>,
+    extra_env: &FxHashMap,
+) -> Vec {
+    let _p = tracing::info_span!("rustc_cfg::get").entered();
+
+    let rustc_cfgs = rustc_print_cfg(target, extra_env, config);
+    let rustc_cfgs = match rustc_cfgs {
+        Ok(cfgs) => cfgs,
+        Err(e) => {
+            tracing::warn!(?e, "failed to get rustc cfgs");
+            return vec![];
+        }
+    };
+
+    // These are unstable but the standard libraries gate on them.
+    let unstable = vec![
+        r#"target_has_atomic_equal_alignment="8""#,
+        r#"target_has_atomic_equal_alignment="16""#,
+        r#"target_has_atomic_equal_alignment="32""#,
+        r#"target_has_atomic_equal_alignment="64""#,
+        r#"target_has_atomic_equal_alignment="128""#,
+        r#"target_has_atomic_equal_alignment="ptr""#,
+        r#"target_has_atomic_load_store"#,
+        r#"target_has_atomic_load_store="8""#,
+        r#"target_has_atomic_load_store="16""#,
+        r#"target_has_atomic_load_store="32""#,
+        r#"target_has_atomic_load_store="64""#,
+        r#"target_has_atomic_load_store="128""#,
+        r#"target_has_atomic_load_store="ptr""#,
+        r#"target_thread_local"#,
+        r#"target_has_atomic"#,
+    ];
+    let rustc_cfgs =
+        rustc_cfgs.lines().chain(unstable).map(crate::parse_cfg).collect::, _>>();
+    match rustc_cfgs {
+        Ok(rustc_cfgs) => {
+            tracing::debug!(?rustc_cfgs, "rustc cfgs found");
+            rustc_cfgs
+        }
+        Err(e) => {
+            tracing::error!(?e, "failed to parse rustc cfgs");
+            vec![]
+        }
+    }
+}
+
+fn rustc_print_cfg(
+    target: Option<&str>,
+    extra_env: &FxHashMap,
+    config: QueryConfig<'_>,
+) -> anyhow::Result {
+    const RUSTC_ARGS: [&str; 2] = ["--print", "cfg"];
+    let (sysroot, current_dir) = match config {
+        QueryConfig::Cargo(sysroot, cargo_toml) => {
+            let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent());
+            cmd.envs(extra_env);
+            cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS);
+            if let Some(target) = target {
+                cmd.args(["--target", target]);
+            }
+            cmd.args(["--", "-O"]);
+
+            match utf8_stdout(&mut cmd) {
+                Ok(it) => return Ok(it),
+                Err(e) => {
+                    tracing::warn!(
+                        %e,
+                        "failed to run `{cmd:?}`, falling back to invoking rustc directly"
+                    );
+                    (sysroot, cargo_toml.parent().as_ref())
+                }
+            }
+        }
+        QueryConfig::Rustc(sysroot, current_dir) => (sysroot, current_dir),
+    };
+
+    let mut cmd = sysroot.tool(Tool::Rustc, current_dir);
+    cmd.envs(extra_env);
+    cmd.args(RUSTC_ARGS);
+    cmd.arg("-O");
+    if let Some(target) = target {
+        cmd.args(["--target", target]);
+    }
+
+    utf8_stdout(&mut cmd).with_context(|| format!("unable to fetch cfgs via `{cmd:?}`"))
+}
+
+#[cfg(test)]
+mod tests {
+    use paths::{AbsPathBuf, Utf8PathBuf};
+
+    use crate::{ManifestPath, Sysroot};
+
+    use super::*;
+
+    #[test]
+    fn cargo() {
+        let manifest_path = concat!(env!("CARGO_MANIFEST_DIR"), "/Cargo.toml");
+        let sysroot = Sysroot::empty();
+        let manifest_path =
+            ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
+        let cfg = QueryConfig::Cargo(&sysroot, &manifest_path);
+        assert_ne!(get(cfg, None, &FxHashMap::default()), vec![]);
+    }
+
+    #[test]
+    fn rustc() {
+        let sysroot = Sysroot::empty();
+        let cfg = QueryConfig::Rustc(&sysroot, env!("CARGO_MANIFEST_DIR").as_ref());
+        assert_ne!(get(cfg, None, &FxHashMap::default()), vec![]);
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs
new file mode 100644
index 0000000000000..94645a91f65ba
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs
@@ -0,0 +1,83 @@
+//! Runs `rustc --print target-spec-json` to get the target_data_layout.
+
+use anyhow::Context;
+use rustc_hash::FxHashMap;
+use toolchain::Tool;
+
+use crate::{toolchain_info::QueryConfig, utf8_stdout, Sysroot};
+
+/// Uses `rustc --print target-spec-json`.
+pub fn get(
+    config: QueryConfig<'_>,
+    target: Option<&str>,
+    extra_env: &FxHashMap,
+) -> anyhow::Result {
+    const RUSTC_ARGS: [&str; 2] = ["--print", "target-spec-json"];
+    let process = |output: String| {
+        (|| Some(output.split_once(r#""data-layout": ""#)?.1.split_once('"')?.0.to_owned()))()
+            .ok_or_else(|| {
+                anyhow::format_err!("could not parse target-spec-json from command output")
+            })
+    };
+    let (sysroot, current_dir) = match config {
+        QueryConfig::Cargo(sysroot, cargo_toml) => {
+            let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent());
+            cmd.envs(extra_env);
+            cmd.env("RUSTC_BOOTSTRAP", "1");
+            cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS).args([
+                "--",
+                "-Z",
+                "unstable-options",
+            ]);
+            if let Some(target) = target {
+                cmd.args(["--target", target]);
+            }
+            match utf8_stdout(&mut cmd) {
+                Ok(output) => return process(output),
+                Err(e) => {
+                    tracing::warn!(%e, "failed to run `{cmd:?}`, falling back to invoking rustc directly");
+                    (sysroot, cargo_toml.parent().as_ref())
+                }
+            }
+        }
+        QueryConfig::Rustc(sysroot, current_dir) => (sysroot, current_dir),
+    };
+
+    let mut cmd = Sysroot::tool(sysroot, Tool::Rustc, current_dir);
+    cmd.envs(extra_env)
+        .env("RUSTC_BOOTSTRAP", "1")
+        .args(["-Z", "unstable-options"])
+        .args(RUSTC_ARGS);
+    if let Some(target) = target {
+        cmd.args(["--target", target]);
+    }
+    utf8_stdout(&mut cmd)
+        .with_context(|| format!("unable to fetch target-data-layout via `{cmd:?}`"))
+        .and_then(process)
+}
+
+#[cfg(test)]
+mod tests {
+    use paths::{AbsPathBuf, Utf8PathBuf};
+
+    use crate::{ManifestPath, Sysroot};
+
+    use super::*;
+
+    #[test]
+    fn cargo() {
+        let manifest_path = concat!(env!("CARGO_MANIFEST_DIR"), "/Cargo.toml");
+        let sysroot = Sysroot::empty();
+        let manifest_path =
+            ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
+        let cfg = QueryConfig::Cargo(&sysroot, &manifest_path);
+        assert!(get(cfg, None, &FxHashMap::default()).is_ok());
+    }
+
+    #[test]
+    fn rustc() {
+        let sysroot = Sysroot::empty();
+        let cfg = QueryConfig::Rustc(&sysroot, env!("CARGO_MANIFEST_DIR").as_ref());
+        assert!(get(cfg, None, &FxHashMap::default()).is_ok());
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_tuple.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_tuple.rs
new file mode 100644
index 0000000000000..0476de58f2309
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_tuple.rs
@@ -0,0 +1,105 @@
+//! Functionality to discover the current build target(s).
+use std::path::Path;
+
+use anyhow::Context;
+use rustc_hash::FxHashMap;
+use toolchain::Tool;
+
+use crate::{toolchain_info::QueryConfig, utf8_stdout, ManifestPath, Sysroot};
+
+/// For cargo, runs `cargo -Zunstable-options config get build.target` to get the configured project target(s).
+/// For rustc, runs `rustc --print -vV` to get the host target.
+pub fn get(
+    config: QueryConfig<'_>,
+    target: Option<&str>,
+    extra_env: &FxHashMap,
+) -> anyhow::Result> {
+    let _p = tracing::info_span!("target_tuple::get").entered();
+    if let Some(target) = target {
+        return Ok(vec![target.to_owned()]);
+    }
+
+    let (sysroot, current_dir) = match config {
+        QueryConfig::Cargo(sysroot, cargo_toml) => {
+            match cargo_config_build_target(cargo_toml, extra_env, sysroot) {
+                Some(it) => return Ok(it),
+                None => (sysroot, cargo_toml.parent().as_ref()),
+            }
+        }
+        QueryConfig::Rustc(sysroot, current_dir) => (sysroot, current_dir),
+    };
+    rustc_discover_host_tuple(extra_env, sysroot, current_dir).map(|it| vec![it])
+}
+
+fn rustc_discover_host_tuple(
+    extra_env: &FxHashMap,
+    sysroot: &Sysroot,
+    current_dir: &Path,
+) -> anyhow::Result {
+    let mut cmd = sysroot.tool(Tool::Rustc, current_dir);
+    cmd.envs(extra_env);
+    cmd.arg("-vV");
+    let stdout = utf8_stdout(&mut cmd)
+        .with_context(|| format!("unable to discover host platform via `{cmd:?}`"))?;
+    let field = "host: ";
+    let target = stdout.lines().find_map(|l| l.strip_prefix(field));
+    if let Some(target) = target {
+        Ok(target.to_owned())
+    } else {
+        // If we fail to resolve the host platform, it's not the end of the world.
+        Err(anyhow::format_err!("rustc -vV did not report host platform, got:\n{}", stdout))
+    }
+}
+
+fn cargo_config_build_target(
+    cargo_toml: &ManifestPath,
+    extra_env: &FxHashMap,
+    sysroot: &Sysroot,
+) -> Option> {
+    let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent());
+    cmd.envs(extra_env);
+    cmd.current_dir(cargo_toml.parent()).env("RUSTC_BOOTSTRAP", "1");
+    cmd.args(["-Z", "unstable-options", "config", "get", "build.target"]);
+    // if successful we receive `build.target = "target-tuple"`
+    // or `build.target = ["", ..]`
+    // this might be `error: config value `build.target` is not set` in which case we
+    // don't wanna log the error
+    utf8_stdout(&mut cmd).and_then(parse_output_cargo_config_build_target).ok()
+}
+
+// Parses `"build.target = [target-tuple, target-tuple, ...]"` or `"build.target = "target-tuple"`
+fn parse_output_cargo_config_build_target(stdout: String) -> anyhow::Result> {
+    let trimmed = stdout.trim_start_matches("build.target = ").trim_matches('"');
+
+    if !trimmed.starts_with('[') {
+        return Ok([trimmed.to_owned()].to_vec());
+    }
+
+    serde_json::from_str(trimmed).context("Failed to parse `build.target` as an array of target")
+}
+
+#[cfg(test)]
+mod tests {
+    use paths::{AbsPathBuf, Utf8PathBuf};
+
+    use crate::{ManifestPath, Sysroot};
+
+    use super::*;
+
+    #[test]
+    fn cargo() {
+        let manifest_path = concat!(env!("CARGO_MANIFEST_DIR"), "/Cargo.toml");
+        let sysroot = Sysroot::empty();
+        let manifest_path =
+            ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
+        let cfg = QueryConfig::Cargo(&sysroot, &manifest_path);
+        assert!(get(cfg, None, &FxHashMap::default()).is_ok());
+    }
+
+    #[test]
+    fn rustc() {
+        let sysroot = Sysroot::empty();
+        let cfg = QueryConfig::Rustc(&sysroot, env!("CARGO_MANIFEST_DIR").as_ref());
+        assert!(get(cfg, None, &FxHashMap::default()).is_ok());
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/version.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/version.rs
new file mode 100644
index 0000000000000..e795fdf1d64fa
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/version.rs
@@ -0,0 +1,58 @@
+//! Get the version string of the toolchain.
+
+use anyhow::Context;
+use rustc_hash::FxHashMap;
+use semver::Version;
+use toolchain::Tool;
+
+use crate::{toolchain_info::QueryConfig, utf8_stdout};
+
+pub(crate) fn get(
+    config: QueryConfig<'_>,
+    extra_env: &FxHashMap,
+) -> Result, anyhow::Error> {
+    let (mut cmd, prefix) = match config {
+        QueryConfig::Cargo(sysroot, cargo_toml) => {
+            (sysroot.tool(Tool::Cargo, cargo_toml.parent()), "cargo ")
+        }
+        QueryConfig::Rustc(sysroot, current_dir) => {
+            (sysroot.tool(Tool::Rustc, current_dir), "rustc ")
+        }
+    };
+    cmd.envs(extra_env);
+    cmd.arg("--version");
+    let out = utf8_stdout(&mut cmd).with_context(|| format!("Failed to query rust toolchain version via `{cmd:?}`, is your toolchain setup correctly?"))?;
+
+    let version =
+        out.strip_prefix(prefix).and_then(|it| Version::parse(it.split_whitespace().next()?).ok());
+    if version.is_none() {
+        tracing::warn!("Failed to parse `{cmd:?}` output `{out}` as a semver version");
+    }
+    anyhow::Ok(version)
+}
+
+#[cfg(test)]
+mod tests {
+    use paths::{AbsPathBuf, Utf8PathBuf};
+
+    use crate::{ManifestPath, Sysroot};
+
+    use super::*;
+
+    #[test]
+    fn cargo() {
+        let manifest_path = concat!(env!("CARGO_MANIFEST_DIR"), "/Cargo.toml");
+        let sysroot = Sysroot::empty();
+        let manifest_path =
+            ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
+        let cfg = QueryConfig::Cargo(&sysroot, &manifest_path);
+        assert!(get(cfg, &FxHashMap::default()).is_ok());
+    }
+
+    #[test]
+    fn rustc() {
+        let sysroot = Sysroot::empty();
+        let cfg = QueryConfig::Rustc(&sysroot, env!("CARGO_MANIFEST_DIR").as_ref());
+        assert!(get(cfg, &FxHashMap::default()).is_ok());
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
index 71ddee3091002..dcd62753cb2f9 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -2,7 +2,7 @@
 //! metadata` or `rust-project.json`) into representation stored in the salsa
 //! database -- `CrateGraph`.
 
-use std::{collections::VecDeque, fmt, fs, iter, sync};
+use std::{collections::VecDeque, fmt, fs, iter, ops::Deref, sync};
 
 use anyhow::Context;
 use base_db::{
@@ -11,25 +11,23 @@ use base_db::{
 };
 use cfg::{CfgAtom, CfgDiff, CfgOptions};
 use intern::{sym, Symbol};
-use itertools::Itertools;
 use paths::{AbsPath, AbsPathBuf};
-use rustc_hash::FxHashMap;
+use rustc_hash::{FxHashMap, FxHashSet};
 use semver::Version;
 use span::{Edition, FileId};
-use toolchain::Tool;
 use tracing::instrument;
 use triomphe::Arc;
 
 use crate::{
     build_dependencies::BuildScriptOutput,
-    cargo_workspace::{DepKind, PackageData, RustLibSource},
+    cargo_workspace::{CargoMetadataConfig, DepKind, PackageData, RustLibSource},
     env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env},
     project_json::{Crate, CrateArrayIdx},
-    rustc_cfg::{self, RustcCfgConfig},
-    sysroot::{SysrootCrate, SysrootMode},
-    target_data_layout::{self, RustcDataLayoutConfig},
-    utf8_stdout, CargoConfig, CargoWorkspace, CfgOverrides, InvocationStrategy, ManifestPath,
-    Package, ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts,
+    sysroot::{SysrootCrate, SysrootWorkspace},
+    toolchain_info::{rustc_cfg, target_data_layout, target_tuple, version, QueryConfig},
+    CargoConfig, CargoWorkspace, CfgOverrides, InvocationStrategy, ManifestPath, Package,
+    ProjectJson, ProjectManifest, Sysroot, SysrootSourceWorkspaceConfig, TargetData, TargetKind,
+    WorkspaceBuildScripts,
 };
 use tracing::{debug, error, info};
 
@@ -64,6 +62,8 @@ pub struct ProjectWorkspace {
     pub target_layout: TargetLayoutLoadResult,
     /// A set of cfg overrides for this workspace.
     pub cfg_overrides: CfgOverrides,
+    /// Additional includes to add for the VFS.
+    pub extra_includes: Vec,
 }
 
 #[derive(Clone)]
@@ -79,8 +79,6 @@ pub enum ProjectWorkspaceKind {
         /// The rustc workspace loaded for this workspace. An `Err(None)` means loading has been
         /// disabled or was otherwise not requested.
         rustc: Result, Option>,
-        /// Environment variables set in the `.cargo/config` file.
-        cargo_config_extra_env: FxHashMap,
         set_test: bool,
     },
     /// Project workspace was specified using a `rust-project.json` file.
@@ -100,8 +98,6 @@ pub enum ProjectWorkspaceKind {
         file: ManifestPath,
         /// Is this file a cargo script file?
         cargo: Option<(CargoWorkspace, WorkspaceBuildScripts, Option>)>,
-        /// Environment variables set in the `.cargo/config` file.
-        cargo_config_extra_env: FxHashMap,
         set_test: bool,
     },
 }
@@ -109,16 +105,17 @@ pub enum ProjectWorkspaceKind {
 impl fmt::Debug for ProjectWorkspace {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         // Make sure this isn't too verbose.
-        let Self { kind, sysroot, rustc_cfg, toolchain, target_layout, cfg_overrides } = self;
+        let Self {
+            kind,
+            sysroot,
+            rustc_cfg,
+            toolchain,
+            target_layout,
+            cfg_overrides,
+            extra_includes,
+        } = self;
         match kind {
-            ProjectWorkspaceKind::Cargo {
-                cargo,
-                error: _,
-                build_scripts,
-                rustc,
-                cargo_config_extra_env,
-                set_test,
-            } => f
+            ProjectWorkspaceKind::Cargo { cargo, error: _, build_scripts, rustc, set_test } => f
                 .debug_struct("Cargo")
                 .field("root", &cargo.workspace_root().file_name())
                 .field("n_packages", &cargo.packages().len())
@@ -129,9 +126,9 @@ impl fmt::Debug for ProjectWorkspace {
                 )
                 .field("n_rustc_cfg", &rustc_cfg.len())
                 .field("n_cfg_overrides", &cfg_overrides.len())
+                .field("n_extra_includes", &extra_includes.len())
                 .field("toolchain", &toolchain)
                 .field("data_layout", &target_layout)
-                .field("cargo_config_extra_env", &cargo_config_extra_env)
                 .field("set_test", set_test)
                 .field("build_scripts", &build_scripts.error().unwrap_or("ok"))
                 .finish(),
@@ -143,16 +140,12 @@ impl fmt::Debug for ProjectWorkspace {
                     .field("n_rustc_cfg", &rustc_cfg.len())
                     .field("toolchain", &toolchain)
                     .field("data_layout", &target_layout)
-                    .field("n_cfg_overrides", &cfg_overrides.len());
+                    .field("n_cfg_overrides", &cfg_overrides.len())
+                    .field("n_extra_includes", &extra_includes.len());
 
                 debug_struct.finish()
             }
-            ProjectWorkspaceKind::DetachedFile {
-                file,
-                cargo: cargo_script,
-                cargo_config_extra_env,
-                set_test,
-            } => f
+            ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, set_test } => f
                 .debug_struct("DetachedFiles")
                 .field("file", &file)
                 .field("cargo_script", &cargo_script.is_some())
@@ -162,34 +155,13 @@ impl fmt::Debug for ProjectWorkspace {
                 .field("toolchain", &toolchain)
                 .field("data_layout", &target_layout)
                 .field("n_cfg_overrides", &cfg_overrides.len())
-                .field("cargo_config_extra_env", &cargo_config_extra_env)
+                .field("n_extra_includes", &extra_includes.len())
                 .field("set_test", set_test)
                 .finish(),
         }
     }
 }
 
-fn get_toolchain_version(
-    current_dir: &AbsPath,
-    sysroot: &Sysroot,
-    tool: Tool,
-    extra_env: &FxHashMap,
-    prefix: &str,
-) -> Result, anyhow::Error> {
-    let cargo_version = utf8_stdout({
-        let mut cmd = Sysroot::tool(sysroot, tool);
-        cmd.envs(extra_env);
-        cmd.arg("--version").current_dir(current_dir);
-        cmd
-    })
-    .with_context(|| format!("Failed to query rust toolchain version at {current_dir}, is your toolchain setup correctly?"))?;
-    anyhow::Ok(
-        cargo_version
-            .get(prefix.len()..)
-            .and_then(|it| Version::parse(it.split_whitespace().next()?).ok()),
-    )
-}
-
 impl ProjectWorkspace {
     pub fn load(
         manifest: ProjectManifest,
@@ -220,167 +192,160 @@ impl ProjectWorkspace {
                 ProjectWorkspace::load_detached_file(rust_file, config)?
             }
             ProjectManifest::CargoToml(cargo_toml) => {
-                let sysroot = match (&config.sysroot, &config.sysroot_src) {
-                    (Some(RustLibSource::Discover), None) => Sysroot::discover(
-                        cargo_toml.parent(),
-                        &config.extra_env,
-                        config.sysroot_query_metadata,
-                    ),
-                    (Some(RustLibSource::Discover), Some(sysroot_src)) => {
-                        Sysroot::discover_with_src_override(
-                            cargo_toml.parent(),
-                            &config.extra_env,
-                            sysroot_src.clone(),
-                            config.sysroot_query_metadata,
-                        )
-                    }
-                    (Some(RustLibSource::Path(path)), None) => Sysroot::discover_sysroot_src_dir(
-                        path.clone(),
-                        config.sysroot_query_metadata,
-                    ),
-                    (Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => Sysroot::load(
-                        Some(sysroot.clone()),
-                        Some(sysroot_src.clone()),
-                        config.sysroot_query_metadata,
-                    ),
-                    (None, _) => Sysroot::empty(),
-                };
-                tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.src_root(), root = ?sysroot.root(), "Using sysroot");
-
-                let rustc_dir = match &config.rustc_source {
-                    Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone())
-                        .map_err(|p| Some(format!("rustc source path is not absolute: {p}"))),
-                    Some(RustLibSource::Discover) => {
-                        sysroot.discover_rustc_src().ok_or_else(|| {
-                            Some("Failed to discover rustc source for sysroot.".to_owned())
-                        })
-                    }
-                    None => Err(None),
-                };
+                ProjectWorkspace::load_cargo(cargo_toml, config, progress)?
+            }
+        };
 
-                let rustc =  rustc_dir.and_then(|rustc_dir| {
-                    info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source");
-                    match CargoWorkspace::fetch_metadata(
-                        &rustc_dir,
-                        cargo_toml.parent(),
-                        &CargoConfig {
-                            features: crate::CargoFeatures::default(),
-                            ..config.clone()
-                        },
-                        &sysroot,
-                        false,
-                        progress,
-                    ) {
-                        Ok((meta, _error)) => {
-                            let workspace = CargoWorkspace::new(meta, cargo_toml.clone());
-                            let buildscripts = WorkspaceBuildScripts::rustc_crates(
-                                &workspace,
-                                cargo_toml.parent(),
-                                &config.extra_env,
-                                &sysroot
-                            );
-                            Ok(Box::new((workspace, buildscripts)))
-                        }
-                        Err(e) => {
-                            tracing::error!(
-                                %e,
-                                "Failed to read Cargo metadata from rustc source at {rustc_dir}",
-                            );
-                            Err(Some(format!(
-                                "Failed to read Cargo metadata from rustc source at {rustc_dir}: {e}"
-                            )))
-                        }
-                    }
-                });
+        Ok(res)
+    }
 
-                let toolchain = get_toolchain_version(
+    fn load_cargo(
+        cargo_toml: &ManifestPath,
+        config: &CargoConfig,
+        progress: &dyn Fn(String),
+    ) -> Result {
+        let mut sysroot = match (&config.sysroot, &config.sysroot_src) {
+            (Some(RustLibSource::Discover), None) => {
+                Sysroot::discover(cargo_toml.parent(), &config.extra_env)
+            }
+            (Some(RustLibSource::Discover), Some(sysroot_src)) => {
+                Sysroot::discover_with_src_override(
                     cargo_toml.parent(),
-                    &sysroot,
-                    Tool::Cargo,
-                    &config.extra_env,
-                    "cargo ",
-                )?;
-                let rustc_cfg = rustc_cfg::get(
-                    config.target.as_deref(),
                     &config.extra_env,
-                    RustcCfgConfig::Cargo(&sysroot, cargo_toml),
-                );
+                    sysroot_src.clone(),
+                )
+            }
+            (Some(RustLibSource::Path(path)), None) => {
+                Sysroot::discover_sysroot_src_dir(path.clone())
+            }
+            (Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => {
+                Sysroot::new(Some(sysroot.clone()), Some(sysroot_src.clone()))
+            }
+            (None, _) => Sysroot::empty(),
+        };
 
-                let cfg_overrides = config.cfg_overrides.clone();
-                let data_layout = target_data_layout::get(
-                    RustcDataLayoutConfig::Cargo(&sysroot, cargo_toml),
-                    config.target.as_deref(),
-                    &config.extra_env,
-                );
-                if let Err(e) = &data_layout {
-                    tracing::error!(%e, "failed fetching data layout for {cargo_toml:?} workspace");
-                }
+        let rustc_dir = match &config.rustc_source {
+            Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone())
+                .map_err(|p| Some(format!("rustc source path is not absolute: {p}"))),
+            Some(RustLibSource::Discover) => sysroot
+                .discover_rustc_src()
+                .ok_or_else(|| Some("Failed to discover rustc source for sysroot.".to_owned())),
+            None => Err(None),
+        };
 
-                let (meta, error) = CargoWorkspace::fetch_metadata(
-                    cargo_toml,
-                    cargo_toml.parent(),
-                    config,
-                    &sysroot,
-                        false,
-                        progress,
+        tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.src_root(), root = ?sysroot.root(), "Using sysroot");
+        let toolchain_config = QueryConfig::Cargo(&sysroot, cargo_toml);
+        let targets =
+            target_tuple::get(toolchain_config, config.target.as_deref(), &config.extra_env)
+                .unwrap_or_default();
+        let toolchain = version::get(toolchain_config, &config.extra_env)
+            .inspect_err(|e| {
+                tracing::error!(%e,
+                    "failed fetching toolchain version for {cargo_toml:?} workspace"
                 )
-                .with_context(|| {
-                    format!(
-                        "Failed to read Cargo metadata from Cargo.toml file {cargo_toml}, {toolchain:?}",
-                    )
-                })?;
-                let cargo = CargoWorkspace::new(meta, cargo_toml.clone());
-
-                let cargo_config_extra_env =
-                    cargo_config_env(cargo_toml, &config.extra_env, &sysroot);
-                ProjectWorkspace {
-                    kind: ProjectWorkspaceKind::Cargo {
-                        cargo,
-                        build_scripts: WorkspaceBuildScripts::default(),
-                        rustc,
-                        cargo_config_extra_env,
-                        error: error.map(Arc::new),
-                        set_test: config.set_test,
-                    },
-                    sysroot,
-                    rustc_cfg,
-                    cfg_overrides,
-                    toolchain,
-                    target_layout: data_layout
-                        .map(Arc::from)
-                        .map_err(|it| Arc::from(it.to_string())),
+            })
+            .ok()
+            .flatten();
+        let rustc_cfg =
+            rustc_cfg::get(toolchain_config, targets.first().map(Deref::deref), &config.extra_env);
+        let cfg_overrides = config.cfg_overrides.clone();
+        let data_layout = target_data_layout::get(
+            toolchain_config,
+            targets.first().map(Deref::deref),
+            &config.extra_env,
+        );
+        if let Err(e) = &data_layout {
+            tracing::error!(%e, "failed fetching data layout for {cargo_toml:?} workspace");
+        }
+        sysroot.load_workspace(&SysrootSourceWorkspaceConfig::CargoMetadata(
+            sysroot_metadata_config(&config.extra_env, &targets),
+        ));
+
+        let rustc = rustc_dir.and_then(|rustc_dir| {
+            info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source");
+            match CargoWorkspace::fetch_metadata(
+                &rustc_dir,
+                cargo_toml.parent(),
+                &CargoMetadataConfig {
+                    features: crate::CargoFeatures::default(),
+                    targets: targets.clone(),
+                    extra_args: config.extra_args.clone(),
+                    extra_env: config.extra_env.clone(),
+                },
+                &sysroot,
+                false,
+                progress,
+            ) {
+                Ok((meta, _error)) => {
+                    let workspace = CargoWorkspace::new(meta, cargo_toml.clone(), Env::default());
+                    let build_scripts = WorkspaceBuildScripts::rustc_crates(
+                        &workspace,
+                        cargo_toml.parent(),
+                        &config.extra_env,
+                        &sysroot,
+                    );
+                    Ok(Box::new((workspace, build_scripts)))
+                }
+                Err(e) => {
+                    tracing::error!(
+                        %e,
+                        "Failed to read Cargo metadata from rustc source at {rustc_dir}",
+                    );
+                    Err(Some(format!(
+                        "Failed to read Cargo metadata from rustc source at {rustc_dir}: {e}"
+                    )))
                 }
             }
-        };
+        });
+
+        let (meta, error) = CargoWorkspace::fetch_metadata(
+            cargo_toml,
+            cargo_toml.parent(),
+            &CargoMetadataConfig {
+                features: config.features.clone(),
+                targets,
+                extra_args: config.extra_args.clone(),
+                extra_env: config.extra_env.clone(),
+            },
+            &sysroot,
+            false,
+            progress,
+        )
+        .with_context(|| {
+            format!(
+                "Failed to read Cargo metadata from Cargo.toml file {cargo_toml}, {toolchain:?}",
+            )
+        })?;
+        let cargo_config_extra_env = cargo_config_env(cargo_toml, &config.extra_env, &sysroot);
+        let cargo = CargoWorkspace::new(meta, cargo_toml.clone(), cargo_config_extra_env);
 
-        Ok(res)
+        Ok(ProjectWorkspace {
+            kind: ProjectWorkspaceKind::Cargo {
+                cargo,
+                build_scripts: WorkspaceBuildScripts::default(),
+                rustc,
+                error: error.map(Arc::new),
+                set_test: config.set_test,
+            },
+            sysroot,
+            rustc_cfg,
+            cfg_overrides,
+            toolchain,
+            target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
+            extra_includes: config.extra_includes.clone(),
+        })
     }
 
     pub fn load_inline(project_json: ProjectJson, config: &CargoConfig) -> ProjectWorkspace {
-        let sysroot = Sysroot::load(
-            project_json.sysroot.clone(),
-            project_json.sysroot_src.clone(),
-            config.sysroot_query_metadata,
-        );
-        let cfg_config = RustcCfgConfig::Rustc(&sysroot);
-        let data_layout_config = RustcDataLayoutConfig::Rustc(&sysroot);
-        let toolchain = match get_toolchain_version(
-            project_json.path(),
-            &sysroot,
-            Tool::Rustc,
-            &config.extra_env,
-            "rustc ",
-        ) {
-            Ok(it) => it,
-            Err(e) => {
-                tracing::error!("{e}");
-                None
-            }
-        };
+        let mut sysroot =
+            Sysroot::new(project_json.sysroot.clone(), project_json.sysroot_src.clone());
+        sysroot.load_workspace(&SysrootSourceWorkspaceConfig::Stitched);
+        let query_config = QueryConfig::Rustc(&sysroot, project_json.path().as_ref());
+        let toolchain = version::get(query_config, &config.extra_env).ok().flatten();
 
         let target = config.target.as_deref();
-        let rustc_cfg = rustc_cfg::get(target, &config.extra_env, cfg_config);
-        let data_layout = target_data_layout::get(data_layout_config, target, &config.extra_env);
+        let rustc_cfg = rustc_cfg::get(query_config, target, &config.extra_env);
+        let data_layout = target_data_layout::get(query_config, target, &config.extra_env);
         ProjectWorkspace {
             kind: ProjectWorkspaceKind::Json(project_json),
             sysroot,
@@ -388,6 +353,7 @@ impl ProjectWorkspace {
             toolchain,
             target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
             cfg_overrides: config.cfg_overrides.clone(),
+            extra_includes: config.extra_includes.clone(),
         }
     }
 
@@ -396,49 +362,50 @@ impl ProjectWorkspace {
         config: &CargoConfig,
     ) -> anyhow::Result {
         let dir = detached_file.parent();
-        let sysroot = match &config.sysroot {
-            Some(RustLibSource::Path(path)) => {
-                Sysroot::discover_sysroot_src_dir(path.clone(), config.sysroot_query_metadata)
-            }
-            Some(RustLibSource::Discover) => {
-                Sysroot::discover(dir, &config.extra_env, config.sysroot_query_metadata)
-            }
+        let mut sysroot = match &config.sysroot {
+            Some(RustLibSource::Path(path)) => Sysroot::discover_sysroot_src_dir(path.clone()),
+            Some(RustLibSource::Discover) => Sysroot::discover(dir, &config.extra_env),
             None => Sysroot::empty(),
         };
 
-        let toolchain =
-            match get_toolchain_version(dir, &sysroot, Tool::Rustc, &config.extra_env, "rustc ") {
-                Ok(it) => it,
-                Err(e) => {
-                    tracing::error!("{e}");
-                    None
-                }
-            };
-
-        let rustc_cfg = rustc_cfg::get(None, &config.extra_env, RustcCfgConfig::Rustc(&sysroot));
-        let data_layout = target_data_layout::get(
-            RustcDataLayoutConfig::Rustc(&sysroot),
-            None,
-            &config.extra_env,
-        );
-
-        let cargo_script =
-            CargoWorkspace::fetch_metadata(detached_file, dir, config, &sysroot, false, &|_| ())
-                .ok()
-                .map(|(ws, error)| {
-                    (
-                        CargoWorkspace::new(ws, detached_file.clone()),
-                        WorkspaceBuildScripts::default(),
-                        error.map(Arc::new),
-                    )
-                });
+        let query_config = QueryConfig::Cargo(&sysroot, detached_file);
+        let toolchain = version::get(query_config, &config.extra_env).ok().flatten();
+        let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env)
+            .unwrap_or_default();
+        let rustc_cfg = rustc_cfg::get(query_config, None, &config.extra_env);
+        let data_layout = target_data_layout::get(query_config, None, &config.extra_env);
+        sysroot.load_workspace(&SysrootSourceWorkspaceConfig::CargoMetadata(
+            sysroot_metadata_config(&config.extra_env, &targets),
+        ));
+
+        let cargo_script = CargoWorkspace::fetch_metadata(
+            detached_file,
+            dir,
+            &CargoMetadataConfig {
+                features: config.features.clone(),
+                targets,
+                extra_args: config.extra_args.clone(),
+                extra_env: config.extra_env.clone(),
+            },
+            &sysroot,
+            false,
+            &|_| (),
+        )
+        .ok()
+        .map(|(ws, error)| {
+            let cargo_config_extra_env =
+                cargo_config_env(detached_file, &config.extra_env, &sysroot);
+            (
+                CargoWorkspace::new(ws, detached_file.clone(), cargo_config_extra_env),
+                WorkspaceBuildScripts::default(),
+                error.map(Arc::new),
+            )
+        });
 
-        let cargo_config_extra_env = cargo_config_env(detached_file, &config.extra_env, &sysroot);
         Ok(ProjectWorkspace {
             kind: ProjectWorkspaceKind::DetachedFile {
                 file: detached_file.to_owned(),
                 cargo: cargo_script,
-                cargo_config_extra_env,
                 set_test: config.set_test,
             },
             sysroot,
@@ -446,6 +413,7 @@ impl ProjectWorkspace {
             toolchain,
             target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
             cfg_overrides: config.cfg_overrides.clone(),
+            extra_includes: config.extra_includes.clone(),
         })
     }
 
@@ -556,6 +524,17 @@ impl ProjectWorkspace {
         }
     }
 
+    pub fn buildfiles(&self) -> Vec {
+        match &self.kind {
+            ProjectWorkspaceKind::Json(project) => project
+                .crates()
+                .filter_map(|(_, krate)| krate.build.as_ref().map(|build| build.build_file.clone()))
+                .map(|build_file| self.workspace_root().join(build_file))
+                .collect(),
+            _ => vec![],
+        }
+    }
+
     pub fn find_sysroot_proc_macro_srv(&self) -> anyhow::Result {
         self.sysroot.discover_proc_macro_srv()
     }
@@ -565,8 +544,8 @@ impl ProjectWorkspace {
     /// the root is a member of the current workspace
     pub fn to_roots(&self) -> Vec {
         let mk_sysroot = || {
-            let mut r = match self.sysroot.mode() {
-                SysrootMode::Workspace(ws) => ws
+            let mut r = match self.sysroot.workspace() {
+                SysrootWorkspace::Workspace(ws) => ws
                     .packages()
                     .filter_map(|pkg| {
                         if ws[pkg].is_local {
@@ -587,7 +566,7 @@ impl ProjectWorkspace {
                         Some(PackageRoot { is_local: false, include, exclude })
                     })
                     .collect(),
-                SysrootMode::Stitched(_) | SysrootMode::Empty => vec![],
+                SysrootWorkspace::Stitched(_) | SysrootWorkspace::Empty => vec![],
             };
 
             r.push(PackageRoot {
@@ -600,33 +579,21 @@ impl ProjectWorkspace {
         match &self.kind {
             ProjectWorkspaceKind::Json(project) => project
                 .crates()
-                .map(|(_, krate)| {
-                    let build_files = project
-                        .crates()
-                        .filter_map(|(_, krate)| {
-                            krate.build.as_ref().map(|build| build.build_file.clone())
-                        })
-                        // FIXME: PackageRoots dont allow specifying files, only directories
-                        .filter_map(|build_file| {
-                            self.workspace_root().join(build_file).parent().map(ToOwned::to_owned)
-                        });
-                    PackageRoot {
-                        is_local: krate.is_workspace_member,
-                        include: krate.include.iter().cloned().chain(build_files).collect(),
-                        exclude: krate.exclude.clone(),
-                    }
+                .map(|(_, krate)| PackageRoot {
+                    is_local: krate.is_workspace_member,
+                    include: krate
+                        .include
+                        .iter()
+                        .cloned()
+                        .chain(self.extra_includes.iter().cloned())
+                        .collect(),
+                    exclude: krate.exclude.clone(),
                 })
+                .collect::>()
+                .into_iter()
                 .chain(mk_sysroot())
-                .unique()
-                .collect(),
-            ProjectWorkspaceKind::Cargo {
-                cargo,
-                rustc,
-                build_scripts,
-                cargo_config_extra_env: _,
-                error: _,
-                set_test: _,
-            } => {
+                .collect::>(),
+            ProjectWorkspaceKind::Cargo { cargo, rustc, build_scripts, error: _, set_test: _ } => {
                 cargo
                     .packages()
                     .map(|pkg| {
@@ -657,6 +624,8 @@ impl ProjectWorkspace {
 
                         let mut exclude = vec![pkg_root.join(".git")];
                         if is_local {
+                            include.extend(self.extra_includes.iter().cloned());
+
                             exclude.push(pkg_root.join("target"));
                         } else {
                             exclude.push(pkg_root.join("tests"));
@@ -673,11 +642,6 @@ impl ProjectWorkspace {
                             exclude: Vec::new(),
                         })
                     }))
-                    .chain(cargo.is_virtual_workspace().then(|| PackageRoot {
-                        is_local: true,
-                        include: vec![cargo.workspace_root().to_path_buf()],
-                        exclude: Vec::new(),
-                    }))
                     .collect()
             }
             ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, .. } => {
@@ -715,6 +679,8 @@ impl ProjectWorkspace {
 
                         let mut exclude = vec![pkg_root.join(".git")];
                         if is_local {
+                            include.extend(self.extra_includes.iter().cloned());
+
                             exclude.push(pkg_root.join("target"));
                         } else {
                             exclude.push(pkg_root.join("tests"));
@@ -763,23 +729,18 @@ impl ProjectWorkspace {
                 extra_env,
                 cfg_overrides,
             ),
-            ProjectWorkspaceKind::Cargo {
-                cargo,
-                rustc,
-                build_scripts,
-                cargo_config_extra_env: _,
-                error: _,
-                set_test,
-            } => cargo_to_crate_graph(
-                load,
-                rustc.as_ref().map(|a| a.as_ref()).ok(),
-                cargo,
-                sysroot,
-                rustc_cfg.clone(),
-                cfg_overrides,
-                build_scripts,
-                *set_test,
-            ),
+            ProjectWorkspaceKind::Cargo { cargo, rustc, build_scripts, error: _, set_test } => {
+                cargo_to_crate_graph(
+                    load,
+                    rustc.as_ref().map(|a| a.as_ref()).ok(),
+                    cargo,
+                    sysroot,
+                    rustc_cfg.clone(),
+                    cfg_overrides,
+                    build_scripts,
+                    *set_test,
+                )
+            }
             ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, set_test, .. } => {
                 if let Some((cargo, build_scripts, _)) = cargo_script {
                     cargo_to_crate_graph(
@@ -824,7 +785,6 @@ impl ProjectWorkspace {
                 ProjectWorkspaceKind::Cargo {
                     cargo,
                     rustc,
-                    cargo_config_extra_env,
                     build_scripts: _,
                     error: _,
                     set_test: _,
@@ -832,16 +792,11 @@ impl ProjectWorkspace {
                 ProjectWorkspaceKind::Cargo {
                     cargo: o_cargo,
                     rustc: o_rustc,
-                    cargo_config_extra_env: o_cargo_config_extra_env,
                     build_scripts: _,
                     error: _,
                     set_test: _,
                 },
-            ) => {
-                cargo == o_cargo
-                    && rustc == o_rustc
-                    && cargo_config_extra_env == o_cargo_config_extra_env
-            }
+            ) => cargo == o_cargo && rustc == o_rustc,
             (ProjectWorkspaceKind::Json(project), ProjectWorkspaceKind::Json(o_project)) => {
                 project == o_project
             }
@@ -849,20 +804,14 @@ impl ProjectWorkspace {
                 ProjectWorkspaceKind::DetachedFile {
                     file,
                     cargo: Some((cargo_script, _, _)),
-                    cargo_config_extra_env,
                     set_test: _,
                 },
                 ProjectWorkspaceKind::DetachedFile {
                     file: o_file,
                     cargo: Some((o_cargo_script, _, _)),
-                    cargo_config_extra_env: o_cargo_config_extra_env,
                     set_test: _,
                 },
-            ) => {
-                file == o_file
-                    && cargo_script == o_cargo_script
-                    && cargo_config_extra_env == o_cargo_config_extra_env
-            }
+            ) => file == o_file && cargo_script == o_cargo_script,
             _ => return false,
         }) && sysroot == o_sysroot
             && rustc_cfg == o_rustc_cfg
@@ -921,7 +870,11 @@ fn project_json_to_crate_graph(
 
                 let target_cfgs = match target.as_deref() {
                     Some(target) => cfg_cache.entry(target).or_insert_with(|| {
-                        rustc_cfg::get(Some(target), extra_env, RustcCfgConfig::Rustc(sysroot))
+                        rustc_cfg::get(
+                            QueryConfig::Rustc(sysroot, project.project_root().as_ref()),
+                            Some(target),
+                            extra_env,
+                        )
                     }),
                     None => &rustc_cfg,
                 };
@@ -1374,15 +1327,13 @@ fn add_target_crate_root(
         opts
     };
 
-    let mut env = Env::default();
+    let mut env = cargo.env().clone();
     inject_cargo_package_env(&mut env, pkg);
     inject_cargo_env(&mut env);
-    inject_rustc_tool_env(&mut env, cargo, cargo_name, kind);
+    inject_rustc_tool_env(&mut env, cargo_name, kind);
 
     if let Some(envs) = build_data.map(|(it, _)| &it.envs) {
-        for (k, v) in envs {
-            env.set(k, v.clone());
-        }
+        env.extend_from_other(envs);
     }
     let crate_id = crate_graph.add_crate_root(
         file_id,
@@ -1433,8 +1384,8 @@ fn sysroot_to_crate_graph(
     load: FileLoader<'_>,
 ) -> (SysrootPublicDeps, Option) {
     let _p = tracing::info_span!("sysroot_to_crate_graph").entered();
-    match sysroot.mode() {
-        SysrootMode::Workspace(cargo) => {
+    match sysroot.workspace() {
+        SysrootWorkspace::Workspace(cargo) => {
             let (mut cg, mut pm) = cargo_to_crate_graph(
                 load,
                 None,
@@ -1509,7 +1460,7 @@ fn sysroot_to_crate_graph(
 
             (SysrootPublicDeps { deps: pub_deps }, libproc_macro)
         }
-        SysrootMode::Stitched(stitched) => {
+        SysrootWorkspace::Stitched(stitched) => {
             let cfg_options = Arc::new({
                 let mut cfg_options = CfgOptions::default();
                 cfg_options.extend(rustc_cfg);
@@ -1562,7 +1513,7 @@ fn sysroot_to_crate_graph(
                 stitched.proc_macro().and_then(|it| sysroot_crates.get(&it).copied());
             (public_deps, libproc_macro)
         }
-        SysrootMode::Empty => (SysrootPublicDeps { deps: vec![] }, None),
+        SysrootWorkspace::Empty => (SysrootPublicDeps { deps: vec![] }, None),
     }
 }
 
@@ -1597,3 +1548,15 @@ fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) {
         tracing::warn!("{}", err)
     }
 }
+
+fn sysroot_metadata_config(
+    extra_env: &FxHashMap,
+    targets: &[String],
+) -> CargoMetadataConfig {
+    CargoMetadataConfig {
+        features: Default::default(),
+        targets: targets.to_vec(),
+        extra_args: Default::default(),
+        extra_env: extra_env.clone(),
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt
index 90f41a9c2fc8b..2026ab2b8c2fa 100644
--- a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt
@@ -479,7 +479,7 @@
     },
     11: CrateData {
         root_file_id: FileId(
-            12,
+            11,
         ),
         edition: Edition2018,
         version: None,
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json b/src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json
new file mode 100644
index 0000000000000..371464dd20aa1
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json
@@ -0,0 +1,6420 @@
+{
+    "packages": [
+        {
+            "name": "aho-corasick",
+            "version": "0.7.20",
+            "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "Unlicense OR MIT",
+            "license_file": null,
+            "description": "Fast multiple substring searching.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "memchr",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^2.4.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "aho_corasick",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                }
+            ],
+            "features": {
+                "default": [
+                    "std"
+                ],
+                "std": [
+                    "memchr/std"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Andrew Gallant "
+            ],
+            "categories": [
+                "text-processing"
+            ],
+            "keywords": [
+                "string",
+                "search",
+                "text",
+                "aho",
+                "multi"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/aho-corasick",
+            "homepage": "https://github.com/BurntSushi/aho-corasick",
+            "documentation": null,
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "cc",
+            "version": "1.0.79",
+            "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "A build-time dependency for Cargo build scripts to assist in invoking the native\nC compiler to compile native C code into a static archive to be linked into Rust\ncode.\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "jobserver",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1.16",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "tempfile",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^3",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "cc",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bin"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "gcc-shim",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/bin/gcc-shim.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "cc_env",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cc_env.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "cflags",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cflags.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "cxxflags",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cxxflags.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/test.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                }
+            ],
+            "features": {
+                "jobserver": [
+                    "dep:jobserver"
+                ],
+                "parallel": [
+                    "jobserver"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Alex Crichton "
+            ],
+            "categories": [
+                "development-tools::build-utils"
+            ],
+            "keywords": [
+                "build-dependencies"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/rust-lang/cc-rs",
+            "homepage": "https://github.com/rust-lang/cc-rs",
+            "documentation": "https://docs.rs/cc",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "cfg-if",
+            "version": "0.1.10",
+            "id": "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT/Apache-2.0",
+            "license_file": null,
+            "description": "A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "compiler_builtins",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1.2",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rustc-std-workspace-core",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.0",
+                    "kind": null,
+                    "rename": "core",
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "cfg-if",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-0.1.10/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "xcrate",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-0.1.10/tests/xcrate.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                }
+            ],
+            "features": {
+                "compiler_builtins": [
+                    "dep:compiler_builtins"
+                ],
+                "core": [
+                    "dep:core"
+                ],
+                "rustc-dep-of-std": [
+                    "core",
+                    "compiler_builtins"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-0.1.10/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Alex Crichton "
+            ],
+            "categories": [],
+            "keywords": [],
+            "readme": "README.md",
+            "repository": "https://github.com/alexcrichton/cfg-if",
+            "homepage": "https://github.com/alexcrichton/cfg-if",
+            "documentation": "https://docs.rs/cfg-if",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "cfg-if",
+            "version": "1.0.0",
+            "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT/Apache-2.0",
+            "license_file": null,
+            "description": "A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "compiler_builtins",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1.2",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rustc-std-workspace-core",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.0",
+                    "kind": null,
+                    "rename": "core",
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "cfg-if",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "xcrate",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/tests/xcrate.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                }
+            ],
+            "features": {
+                "compiler_builtins": [
+                    "dep:compiler_builtins"
+                ],
+                "core": [
+                    "dep:core"
+                ],
+                "rustc-dep-of-std": [
+                    "core",
+                    "compiler_builtins"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Alex Crichton "
+            ],
+            "categories": [],
+            "keywords": [],
+            "readme": "README.md",
+            "repository": "https://github.com/alexcrichton/cfg-if",
+            "homepage": "https://github.com/alexcrichton/cfg-if",
+            "documentation": "https://docs.rs/cfg-if",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "docopt",
+            "version": "1.1.1",
+            "id": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "Unlicense/MIT",
+            "license_file": null,
+            "description": "Command line argument parsing.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "lazy_static",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.3",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "regex",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.4.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [
+                        "std",
+                        "unicode"
+                    ],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [
+                        "derive"
+                    ],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "strsim",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.10",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "docopt",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bin"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "docopt-wordlist",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/src/wordlist.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "cargo",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/cargo.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "cp",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/cp.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "decode",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/decode.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "hashmap",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/hashmap.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "optional_command",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/optional_command.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "verbose_multiple",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/verbose_multiple.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Andrew Gallant "
+            ],
+            "categories": [
+                "command-line-interface"
+            ],
+            "keywords": [
+                "docopt",
+                "argument",
+                "command",
+                "argv"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/docopt/docopt.rs",
+            "homepage": "https://github.com/docopt/docopt.rs",
+            "documentation": "http://burntsushi.net/rustdoc/docopt/",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "getrandom",
+            "version": "0.2.9",
+            "id": "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "A small cross-platform library for retrieving random data from system source",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "cfg-if",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "compiler_builtins",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rustc-std-workspace-core",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": null,
+                    "rename": "core",
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "js-sys",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": "cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))",
+                    "registry": null
+                },
+                {
+                    "name": "wasm-bindgen",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2.62",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": "cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))",
+                    "registry": null
+                },
+                {
+                    "name": "wasm-bindgen-test",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3.18",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": "cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))",
+                    "registry": null
+                },
+                {
+                    "name": "wasi",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.11",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": "cfg(target_os = \"wasi\")",
+                    "registry": null
+                },
+                {
+                    "name": "libc",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2.139",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": "cfg(unix)",
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "getrandom",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "custom",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/tests/custom.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "normal",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/tests/normal.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "rdrand",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/tests/rdrand.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "buffer",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/benches/buffer.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "compiler_builtins": [
+                    "dep:compiler_builtins"
+                ],
+                "core": [
+                    "dep:core"
+                ],
+                "custom": [],
+                "js": [
+                    "wasm-bindgen",
+                    "js-sys"
+                ],
+                "js-sys": [
+                    "dep:js-sys"
+                ],
+                "rdrand": [],
+                "rustc-dep-of-std": [
+                    "compiler_builtins",
+                    "core",
+                    "libc/rustc-dep-of-std",
+                    "wasi/rustc-dep-of-std"
+                ],
+                "std": [],
+                "test-in-browser": [],
+                "wasm-bindgen": [
+                    "dep:wasm-bindgen"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "features": [
+                            "std",
+                            "custom"
+                        ],
+                        "rustdoc-args": [
+                            "--cfg",
+                            "docsrs"
+                        ]
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "The Rand Project Developers"
+            ],
+            "categories": [
+                "os",
+                "no-std"
+            ],
+            "keywords": [],
+            "readme": "README.md",
+            "repository": "https://github.com/rust-random/getrandom",
+            "homepage": null,
+            "documentation": "https://docs.rs/getrandom",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "lazy_static",
+            "version": "1.4.0",
+            "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT/Apache-2.0",
+            "license_file": null,
+            "description": "A macro for declaring lazily evaluated statics in Rust.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "spin",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.5.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "doc-comment",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3.1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "lazy_static",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "no_std",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/no_std.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/test.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                }
+            ],
+            "features": {
+                "spin": [
+                    "dep:spin"
+                ],
+                "spin_no_std": [
+                    "spin"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Marvin Löbel "
+            ],
+            "categories": [
+                "no-std",
+                "rust-patterns",
+                "memory-management"
+            ],
+            "keywords": [
+                "macro",
+                "lazy",
+                "static"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/rust-lang-nursery/lazy-static.rs",
+            "homepage": null,
+            "documentation": "https://docs.rs/lazy_static",
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "libc",
+            "version": "0.2.142",
+            "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "Raw FFI bindings to platform libraries like libc.\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "rustc-std-workspace-core",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "libc",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "const_fn",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/tests/const_fn.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/build.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "align": [],
+                "const-extern-fn": [],
+                "default": [
+                    "std"
+                ],
+                "extra_traits": [],
+                "rustc-dep-of-std": [
+                    "align",
+                    "rustc-std-workspace-core"
+                ],
+                "rustc-std-workspace-core": [
+                    "dep:rustc-std-workspace-core"
+                ],
+                "std": [],
+                "use_std": [
+                    "std"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "features": [
+                            "const-extern-fn",
+                            "extra_traits"
+                        ]
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "The Rust Project Developers"
+            ],
+            "categories": [
+                "external-ffi-bindings",
+                "no-std",
+                "os"
+            ],
+            "keywords": [
+                "libc",
+                "ffi",
+                "bindings",
+                "operating",
+                "system"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/rust-lang/libc",
+            "homepage": "https://github.com/rust-lang/libc",
+            "documentation": "https://docs.rs/libc/",
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "memchr",
+            "version": "2.5.0",
+            "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "Unlicense/MIT",
+            "license_file": null,
+            "description": "Safe interface to memchr.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "compiler_builtins",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1.2",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rustc-std-workspace-core",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.0",
+                    "kind": null,
+                    "rename": "core",
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "libc",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2.18",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "quickcheck",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.3",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "memchr",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/build.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "compiler_builtins": [
+                    "dep:compiler_builtins"
+                ],
+                "core": [
+                    "dep:core"
+                ],
+                "default": [
+                    "std"
+                ],
+                "libc": [
+                    "dep:libc"
+                ],
+                "rustc-dep-of-std": [
+                    "core",
+                    "compiler_builtins"
+                ],
+                "std": [],
+                "use_std": [
+                    "std"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Andrew Gallant ",
+                "bluss"
+            ],
+            "categories": [],
+            "keywords": [
+                "memchr",
+                "char",
+                "scan",
+                "strchr",
+                "string"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/memchr",
+            "homepage": "https://github.com/BurntSushi/memchr",
+            "documentation": "https://docs.rs/memchr/",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "memmap",
+            "version": "0.6.2",
+            "id": "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT/Apache-2.0",
+            "license_file": null,
+            "description": "Cross-platform Rust API for memory-mapped file IO",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "tempdir",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "libc",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": "cfg(unix)",
+                    "registry": null
+                },
+                {
+                    "name": "winapi",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [
+                        "basetsd",
+                        "handleapi",
+                        "memoryapi",
+                        "minwindef",
+                        "std",
+                        "sysinfoapi"
+                    ],
+                    "target": "cfg(windows)",
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "memmap",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap-0.6.2/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "cat",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap-0.6.2/examples/cat.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap-0.6.2/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Dan Burkert "
+            ],
+            "categories": [],
+            "keywords": [
+                "mmap",
+                "memory-map",
+                "io",
+                "file"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/danburkert/memmap-rs",
+            "homepage": null,
+            "documentation": "https://docs.rs/memmap",
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "pkg-config",
+            "version": "0.3.26",
+            "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "A library to run the pkg-config system tool at build time in order to be used in\nCargo build scripts.\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "lazy_static",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "pkg-config",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/tests/test.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Alex Crichton "
+            ],
+            "categories": [],
+            "keywords": [
+                "build-dependencies"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/rust-lang/pkg-config-rs",
+            "homepage": null,
+            "documentation": "https://docs.rs/pkg-config",
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "proc-macro2",
+            "version": "1.0.56",
+            "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "unicode-ident",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "quote",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rustversion",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "proc-macro2",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "comments",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/comments.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "features",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/features.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "marker",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/marker.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_fmt",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_fmt.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_size",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_size.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/build.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "default": [
+                    "proc-macro"
+                ],
+                "nightly": [],
+                "proc-macro": [],
+                "span-locations": []
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "rustc-args": [
+                            "--cfg",
+                            "procmacro2_semver_exempt"
+                        ],
+                        "rustdoc-args": [
+                            "--cfg",
+                            "procmacro2_semver_exempt",
+                            "--cfg",
+                            "doc_cfg"
+                        ],
+                        "targets": [
+                            "x86_64-unknown-linux-gnu"
+                        ]
+                    }
+                },
+                "playground": {
+                    "features": [
+                        "span-locations"
+                    ]
+                }
+            },
+            "publish": null,
+            "authors": [
+                "David Tolnay ",
+                "Alex Crichton "
+            ],
+            "categories": [
+                "development-tools::procedural-macro-helpers"
+            ],
+            "keywords": [
+                "macros",
+                "syn"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/dtolnay/proc-macro2",
+            "homepage": null,
+            "documentation": "https://docs.rs/proc-macro2",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.31"
+        },
+        {
+            "name": "quickcheck",
+            "version": "1.0.3",
+            "id": "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "Unlicense/MIT",
+            "license_file": null,
+            "description": "Automatic property based testing with shrinking.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "env_logger",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.8.2",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "log",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rand",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.8",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [
+                        "getrandom",
+                        "small_rng"
+                    ],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "quickcheck",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "btree_set_range",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/btree_set_range.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "out_of_bounds",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/out_of_bounds.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "reverse",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/reverse.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "reverse_single",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/reverse_single.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "sieve",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/sieve.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "sort",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/sort.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "default": [
+                    "regex",
+                    "use_logging"
+                ],
+                "env_logger": [
+                    "dep:env_logger"
+                ],
+                "log": [
+                    "dep:log"
+                ],
+                "regex": [
+                    "env_logger/regex"
+                ],
+                "use_logging": [
+                    "log",
+                    "env_logger"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Andrew Gallant "
+            ],
+            "categories": [
+                "development-tools::testing"
+            ],
+            "keywords": [
+                "testing",
+                "quickcheck",
+                "property",
+                "shrinking",
+                "fuzz"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/quickcheck",
+            "homepage": "https://github.com/BurntSushi/quickcheck",
+            "documentation": "https://docs.rs/quickcheck",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "quote",
+            "version": "1.0.26",
+            "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "Quasi-quoting macro quote!(...)",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "proc-macro2",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.52",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rustversion",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "trybuild",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.66",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [
+                        "diff"
+                    ],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "quote",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "compiletest",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/compiletest.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/test.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/build.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "default": [
+                    "proc-macro"
+                ],
+                "proc-macro": [
+                    "proc-macro2/proc-macro"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "targets": [
+                            "x86_64-unknown-linux-gnu"
+                        ]
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "David Tolnay "
+            ],
+            "categories": [
+                "development-tools::procedural-macro-helpers"
+            ],
+            "keywords": [
+                "macros",
+                "syn"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/dtolnay/quote",
+            "homepage": null,
+            "documentation": "https://docs.rs/quote/",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.31"
+        },
+        {
+            "name": "rand",
+            "version": "0.8.5",
+            "id": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "Random number generators and other randomness functionality.\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "log",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4.4",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "packed_simd_2",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3.7",
+                    "kind": null,
+                    "rename": "packed_simd",
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [
+                        "into_bits"
+                    ],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rand_chacha",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rand_core",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.6.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.103",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [
+                        "derive"
+                    ],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "bincode",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.2.1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rand_pcg",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "libc",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2.22",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": "cfg(unix)",
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "rand",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand-0.8.5/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                }
+            ],
+            "features": {
+                "alloc": [
+                    "rand_core/alloc"
+                ],
+                "default": [
+                    "std",
+                    "std_rng"
+                ],
+                "getrandom": [
+                    "rand_core/getrandom"
+                ],
+                "libc": [
+                    "dep:libc"
+                ],
+                "log": [
+                    "dep:log"
+                ],
+                "min_const_gen": [],
+                "nightly": [],
+                "packed_simd": [
+                    "dep:packed_simd"
+                ],
+                "rand_chacha": [
+                    "dep:rand_chacha"
+                ],
+                "serde": [
+                    "dep:serde"
+                ],
+                "serde1": [
+                    "serde",
+                    "rand_core/serde1"
+                ],
+                "simd_support": [
+                    "packed_simd"
+                ],
+                "small_rng": [],
+                "std": [
+                    "rand_core/std",
+                    "rand_chacha/std",
+                    "alloc",
+                    "getrandom",
+                    "libc"
+                ],
+                "std_rng": [
+                    "rand_chacha"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand-0.8.5/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "all-features": true,
+                        "rustdoc-args": [
+                            "--cfg",
+                            "doc_cfg"
+                        ]
+                    }
+                },
+                "playground": {
+                    "features": [
+                        "small_rng",
+                        "serde1"
+                    ]
+                }
+            },
+            "publish": null,
+            "authors": [
+                "The Rand Project Developers",
+                "The Rust Project Developers"
+            ],
+            "categories": [
+                "algorithms",
+                "no-std"
+            ],
+            "keywords": [
+                "random",
+                "rng"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/rust-random/rand",
+            "homepage": "https://rust-random.github.io/book",
+            "documentation": "https://docs.rs/rand",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "rand_core",
+            "version": "0.6.4",
+            "id": "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "Core random number generator traits and tools for implementation.\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "getrandom",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [
+                        "derive"
+                    ],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "rand_core",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand_core-0.6.4/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                }
+            ],
+            "features": {
+                "alloc": [],
+                "getrandom": [
+                    "dep:getrandom"
+                ],
+                "serde": [
+                    "dep:serde"
+                ],
+                "serde1": [
+                    "serde"
+                ],
+                "std": [
+                    "alloc",
+                    "getrandom",
+                    "getrandom/std"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand_core-0.6.4/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "all-features": true,
+                        "rustdoc-args": [
+                            "--cfg",
+                            "doc_cfg"
+                        ]
+                    }
+                },
+                "playground": {
+                    "all-features": true
+                }
+            },
+            "publish": null,
+            "authors": [
+                "The Rand Project Developers",
+                "The Rust Project Developers"
+            ],
+            "categories": [
+                "algorithms",
+                "no-std"
+            ],
+            "keywords": [
+                "random",
+                "rng"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/rust-random/rand",
+            "homepage": "https://rust-random.github.io/book",
+            "documentation": "https://docs.rs/rand_core",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "regex",
+            "version": "1.7.1",
+            "id": "regex 1.7.1 (path+file:///$ROOT$regex)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n",
+            "source": null,
+            "dependencies": [
+                {
+                    "name": "aho-corasick",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.7.18",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "memchr",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^2.4.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "regex-syntax",
+                    "source": null,
+                    "req": "^0.6.27",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null,
+                    "path": "$ROOT$regex/regex-syntax"
+                },
+                {
+                    "name": "lazy_static",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "quickcheck",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.3",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rand",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.8.3",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [
+                        "getrandom",
+                        "small_rng"
+                    ],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "regex",
+                    "src_path": "$ROOT$regex/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "shootout-regex-dna-bytes",
+                    "src_path": "$ROOT$regex/examples/shootout-regex-dna-bytes.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "shootout-regex-dna-cheat",
+                    "src_path": "$ROOT$regex/examples/shootout-regex-dna-cheat.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "shootout-regex-dna-replace",
+                    "src_path": "$ROOT$regex/examples/shootout-regex-dna-replace.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "shootout-regex-dna-single-cheat",
+                    "src_path": "$ROOT$regex/examples/shootout-regex-dna-single-cheat.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "shootout-regex-dna-single",
+                    "src_path": "$ROOT$regex/examples/shootout-regex-dna-single.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "shootout-regex-dna",
+                    "src_path": "$ROOT$regex/examples/shootout-regex-dna.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "default",
+                    "src_path": "$ROOT$regex/tests/test_default.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "default-bytes",
+                    "src_path": "$ROOT$regex/tests/test_default_bytes.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "nfa",
+                    "src_path": "$ROOT$regex/tests/test_nfa.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "nfa-utf8bytes",
+                    "src_path": "$ROOT$regex/tests/test_nfa_utf8bytes.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "nfa-bytes",
+                    "src_path": "$ROOT$regex/tests/test_nfa_bytes.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "backtrack",
+                    "src_path": "$ROOT$regex/tests/test_backtrack.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "backtrack-utf8bytes",
+                    "src_path": "$ROOT$regex/tests/test_backtrack_utf8bytes.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "backtrack-bytes",
+                    "src_path": "$ROOT$regex/tests/test_backtrack_bytes.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "crates-regex",
+                    "src_path": "$ROOT$regex/tests/test_crates_regex.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                }
+            ],
+            "features": {
+                "aho-corasick": [
+                    "dep:aho-corasick"
+                ],
+                "default": [
+                    "std",
+                    "perf",
+                    "unicode",
+                    "regex-syntax/default"
+                ],
+                "memchr": [
+                    "dep:memchr"
+                ],
+                "pattern": [],
+                "perf": [
+                    "perf-cache",
+                    "perf-dfa",
+                    "perf-inline",
+                    "perf-literal"
+                ],
+                "perf-cache": [],
+                "perf-dfa": [],
+                "perf-inline": [],
+                "perf-literal": [
+                    "aho-corasick",
+                    "memchr"
+                ],
+                "std": [],
+                "unicode": [
+                    "unicode-age",
+                    "unicode-bool",
+                    "unicode-case",
+                    "unicode-gencat",
+                    "unicode-perl",
+                    "unicode-script",
+                    "unicode-segment",
+                    "regex-syntax/unicode"
+                ],
+                "unicode-age": [
+                    "regex-syntax/unicode-age"
+                ],
+                "unicode-bool": [
+                    "regex-syntax/unicode-bool"
+                ],
+                "unicode-case": [
+                    "regex-syntax/unicode-case"
+                ],
+                "unicode-gencat": [
+                    "regex-syntax/unicode-gencat"
+                ],
+                "unicode-perl": [
+                    "regex-syntax/unicode-perl"
+                ],
+                "unicode-script": [
+                    "regex-syntax/unicode-script"
+                ],
+                "unicode-segment": [
+                    "regex-syntax/unicode-segment"
+                ],
+                "unstable": [
+                    "pattern"
+                ],
+                "use_std": [
+                    "std"
+                ]
+            },
+            "manifest_path": "$ROOT$regex/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "The Rust Project Developers"
+            ],
+            "categories": [
+                "text-processing"
+            ],
+            "keywords": [],
+            "readme": "README.md",
+            "repository": "https://github.com/rust-lang/regex",
+            "homepage": "https://github.com/rust-lang/regex",
+            "documentation": "https://docs.rs/regex",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "regex",
+            "version": "1.8.1",
+            "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "aho-corasick",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "memchr",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^2.5.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "regex-syntax",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.7.1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "lazy_static",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "quickcheck",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.3",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rand",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.8.3",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [
+                        "getrandom",
+                        "small_rng"
+                    ],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "regex",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/src/lib.rs",
+                    "edition": "2021",
+                    "doc": true,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "shootout-regex-dna-bytes",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-bytes.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "shootout-regex-dna-cheat",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-cheat.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "shootout-regex-dna-replace",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-replace.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "shootout-regex-dna-single-cheat",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single-cheat.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "shootout-regex-dna-single",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "shootout-regex-dna",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "default",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "default-bytes",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default_bytes.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "nfa",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "nfa-utf8bytes",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_utf8bytes.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "nfa-bytes",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_bytes.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "backtrack",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "backtrack-utf8bytes",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_utf8bytes.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "backtrack-bytes",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_bytes.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "crates-regex",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_crates_regex.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                }
+            ],
+            "features": {
+                "aho-corasick": [
+                    "dep:aho-corasick"
+                ],
+                "default": [
+                    "std",
+                    "perf",
+                    "unicode",
+                    "regex-syntax/default"
+                ],
+                "memchr": [
+                    "dep:memchr"
+                ],
+                "pattern": [],
+                "perf": [
+                    "perf-cache",
+                    "perf-dfa",
+                    "perf-inline",
+                    "perf-literal"
+                ],
+                "perf-cache": [],
+                "perf-dfa": [],
+                "perf-inline": [],
+                "perf-literal": [
+                    "aho-corasick",
+                    "memchr"
+                ],
+                "std": [],
+                "unicode": [
+                    "unicode-age",
+                    "unicode-bool",
+                    "unicode-case",
+                    "unicode-gencat",
+                    "unicode-perl",
+                    "unicode-script",
+                    "unicode-segment",
+                    "regex-syntax/unicode"
+                ],
+                "unicode-age": [
+                    "regex-syntax/unicode-age"
+                ],
+                "unicode-bool": [
+                    "regex-syntax/unicode-bool"
+                ],
+                "unicode-case": [
+                    "regex-syntax/unicode-case"
+                ],
+                "unicode-gencat": [
+                    "regex-syntax/unicode-gencat"
+                ],
+                "unicode-perl": [
+                    "regex-syntax/unicode-perl"
+                ],
+                "unicode-script": [
+                    "regex-syntax/unicode-script"
+                ],
+                "unicode-segment": [
+                    "regex-syntax/unicode-segment"
+                ],
+                "unstable": [
+                    "pattern"
+                ],
+                "use_std": [
+                    "std"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "The Rust Project Developers"
+            ],
+            "categories": [
+                "text-processing"
+            ],
+            "keywords": [],
+            "readme": "README.md",
+            "repository": "https://github.com/rust-lang/regex",
+            "homepage": "https://github.com/rust-lang/regex",
+            "documentation": "https://docs.rs/regex",
+            "edition": "2021",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.60.0"
+        },
+        {
+            "name": "regex-benchmark",
+            "version": "0.1.0",
+            "id": "regex-benchmark 0.1.0 (path+file:///$ROOT$regex/bench)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "Regex benchmarks for Rust's and other engines.",
+            "source": null,
+            "dependencies": [
+                {
+                    "name": "cfg-if",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "docopt",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "lazy_static",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "libc",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "libpcre-sys",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "memmap",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.6.2",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "onig",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^3",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "regex",
+                    "source": null,
+                    "req": "^1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null,
+                    "path": "$ROOT$regex"
+                },
+                {
+                    "name": "regex-syntax",
+                    "source": null,
+                    "req": "^0.6",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null,
+                    "path": "$ROOT$regex/regex-syntax"
+                },
+                {
+                    "name": "serde",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [
+                        "derive"
+                    ],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "cc",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "build",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "pkg-config",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3.9",
+                    "kind": "build",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "bin"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "regex-run-one",
+                    "src_path": "$ROOT$regex/bench/src/main.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "bench",
+                    "src_path": "$ROOT$regex/bench/src/bench.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$regex/bench/build.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "libpcre-sys": [
+                    "dep:libpcre-sys"
+                ],
+                "onig": [
+                    "dep:onig"
+                ],
+                "re-onig": [
+                    "onig"
+                ],
+                "re-pcre1": [
+                    "libpcre-sys"
+                ],
+                "re-pcre2": [],
+                "re-re2": [],
+                "re-rust": [],
+                "re-rust-bytes": [],
+                "re-tcl": []
+            },
+            "manifest_path": "$ROOT$regex/bench/Cargo.toml",
+            "metadata": null,
+            "publish": [],
+            "authors": [
+                "The Rust Project Developers"
+            ],
+            "categories": [],
+            "keywords": [],
+            "readme": null,
+            "repository": "https://github.com/rust-lang/regex",
+            "homepage": "https://github.com/rust-lang/regex",
+            "documentation": "https://docs.rs/regex",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "regex-debug",
+            "version": "0.1.0",
+            "id": "regex-debug 0.1.0 (path+file:///$ROOT$regex/regex-debug)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "A tool useful for debugging regular expressions.",
+            "source": null,
+            "dependencies": [
+                {
+                    "name": "docopt",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "regex",
+                    "source": null,
+                    "req": "^1.1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null,
+                    "path": "$ROOT$regex"
+                },
+                {
+                    "name": "regex-syntax",
+                    "source": null,
+                    "req": "^0.6",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null,
+                    "path": "$ROOT$regex/regex-syntax"
+                },
+                {
+                    "name": "serde",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [
+                        "derive"
+                    ],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "bin"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "regex-debug",
+                    "src_path": "$ROOT$regex/regex-debug/src/main.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": false,
+                    "test": true
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$regex/regex-debug/Cargo.toml",
+            "metadata": null,
+            "publish": [],
+            "authors": [
+                "The Rust Project Developers"
+            ],
+            "categories": [],
+            "keywords": [],
+            "readme": null,
+            "repository": "https://github.com/rust-lang/regex",
+            "homepage": "https://github.com/rust-lang/regex",
+            "documentation": "https://docs.rs/regex",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "regex-syntax",
+            "version": "0.6.28",
+            "id": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "A regular expression parser.",
+            "source": null,
+            "dependencies": [],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "regex-syntax",
+                    "src_path": "$ROOT$regex/regex-syntax/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "bench",
+                    "src_path": "$ROOT$regex/regex-syntax/benches/bench.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "default": [
+                    "unicode"
+                ],
+                "unicode": [
+                    "unicode-age",
+                    "unicode-bool",
+                    "unicode-case",
+                    "unicode-gencat",
+                    "unicode-perl",
+                    "unicode-script",
+                    "unicode-segment"
+                ],
+                "unicode-age": [],
+                "unicode-bool": [],
+                "unicode-case": [],
+                "unicode-gencat": [],
+                "unicode-perl": [],
+                "unicode-script": [],
+                "unicode-segment": []
+            },
+            "manifest_path": "$ROOT$regex/regex-syntax/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "The Rust Project Developers"
+            ],
+            "categories": [],
+            "keywords": [],
+            "readme": "README.md",
+            "repository": "https://github.com/rust-lang/regex",
+            "homepage": "https://github.com/rust-lang/regex",
+            "documentation": "https://docs.rs/regex-syntax",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "regex-syntax",
+            "version": "0.7.1",
+            "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "A regular expression parser.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "regex-syntax",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/src/lib.rs",
+                    "edition": "2021",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "bench",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/benches/bench.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "default": [
+                    "std",
+                    "unicode"
+                ],
+                "std": [],
+                "unicode": [
+                    "unicode-age",
+                    "unicode-bool",
+                    "unicode-case",
+                    "unicode-gencat",
+                    "unicode-perl",
+                    "unicode-script",
+                    "unicode-segment"
+                ],
+                "unicode-age": [],
+                "unicode-bool": [],
+                "unicode-case": [],
+                "unicode-gencat": [],
+                "unicode-perl": [],
+                "unicode-script": [],
+                "unicode-segment": []
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "all-features": true,
+                        "rustdoc-args": [
+                            "--cfg",
+                            "docsrs"
+                        ]
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "The Rust Project Developers"
+            ],
+            "categories": [],
+            "keywords": [],
+            "readme": "README.md",
+            "repository": "https://github.com/rust-lang/regex",
+            "homepage": "https://github.com/rust-lang/regex",
+            "documentation": "https://docs.rs/regex-syntax",
+            "edition": "2021",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.60.0"
+        },
+        {
+            "name": "rure",
+            "version": "0.2.2",
+            "id": "rure 0.2.2 (path+file:///$ROOT$regex/regex-capi)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "A C API for Rust's regular expression library.\n",
+            "source": null,
+            "dependencies": [
+                {
+                    "name": "libc",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "regex",
+                    "source": null,
+                    "req": "^1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null,
+                    "path": "$ROOT$regex"
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "staticlib",
+                        "cdylib",
+                        "rlib"
+                    ],
+                    "crate_types": [
+                        "staticlib",
+                        "cdylib",
+                        "rlib"
+                    ],
+                    "name": "rure",
+                    "src_path": "$ROOT$regex/regex-capi/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$regex/regex-capi/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "The Rust Project Developers"
+            ],
+            "categories": [],
+            "keywords": [],
+            "readme": "README.md",
+            "repository": "https://github.com/rust-lang/regex",
+            "homepage": "https://github.com/rust-lang/regex",
+            "documentation": "https://github.com/rust-lang/regex/tree/master/regex-capi",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "serde",
+            "version": "1.0.160",
+            "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "A generic serialization/deserialization framework",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "serde_derive",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "=1.0.160",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde_derive",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "serde",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/build.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "alloc": [],
+                "default": [
+                    "std"
+                ],
+                "derive": [
+                    "serde_derive"
+                ],
+                "rc": [],
+                "serde_derive": [
+                    "dep:serde_derive"
+                ],
+                "std": [],
+                "unstable": []
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "features": [
+                            "derive"
+                        ],
+                        "targets": [
+                            "x86_64-unknown-linux-gnu"
+                        ]
+                    }
+                },
+                "playground": {
+                    "features": [
+                        "derive",
+                        "rc"
+                    ]
+                }
+            },
+            "publish": null,
+            "authors": [
+                "Erick Tryzelaar ",
+                "David Tolnay "
+            ],
+            "categories": [
+                "encoding",
+                "no-std"
+            ],
+            "keywords": [
+                "serde",
+                "serialization",
+                "no_std"
+            ],
+            "readme": "crates-io.md",
+            "repository": "https://github.com/serde-rs/serde",
+            "homepage": "https://serde.rs",
+            "documentation": "https://docs.rs/serde",
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.19"
+        },
+        {
+            "name": "serde_derive",
+            "version": "1.0.160",
+            "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "proc-macro2",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "quote",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "syn",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^2.0.3",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "proc-macro"
+                    ],
+                    "crate_types": [
+                        "proc-macro"
+                    ],
+                    "name": "serde_derive",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/build.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "default": [],
+                "deserialize_in_place": []
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "targets": [
+                            "x86_64-unknown-linux-gnu"
+                        ]
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "Erick Tryzelaar ",
+                "David Tolnay "
+            ],
+            "categories": [
+                "no-std"
+            ],
+            "keywords": [
+                "serde",
+                "serialization",
+                "no_std",
+                "derive"
+            ],
+            "readme": "crates-io.md",
+            "repository": "https://github.com/serde-rs/serde",
+            "homepage": "https://serde.rs",
+            "documentation": "https://serde.rs/derive.html",
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.56"
+        },
+        {
+            "name": "strsim",
+            "version": "0.10.0",
+            "id": "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT",
+            "license_file": null,
+            "description": "Implementations of string similarity metrics. Includes Hamming, Levenshtein,\nOSA, Damerau-Levenshtein, Jaro, Jaro-Winkler, and Sørensen-Dice.\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "strsim",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "lib",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/tests/lib.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "benches",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/benches/benches.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Danny Guo "
+            ],
+            "categories": [],
+            "keywords": [
+                "string",
+                "similarity",
+                "Hamming",
+                "Levenshtein",
+                "Jaro"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/dguo/strsim-rs",
+            "homepage": "https://github.com/dguo/strsim-rs",
+            "documentation": "https://docs.rs/strsim/",
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "syn",
+            "version": "2.0.15",
+            "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "Parser for Rust source code",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "proc-macro2",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.55",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "quote",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.25",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "unicode-ident",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "anyhow",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "automod",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "flate2",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "insta",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rayon",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "ref-cast",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "regex",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "reqwest",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.11",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [
+                        "blocking"
+                    ],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rustversion",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "syn-test-suite",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "tar",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4.16",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "termcolor",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "walkdir",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^2.3.2",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "syn",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/src/lib.rs",
+                    "edition": "2021",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "regression",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/regression.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_asyncness",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_asyncness.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_attribute",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_attribute.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_derive_input",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_derive_input.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_expr",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_expr.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_generics",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_generics.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_grouping",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_grouping.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_ident",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ident.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_item",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_item.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_iterators",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_iterators.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_lit",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_lit.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_meta",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_meta.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_parse_buffer",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_buffer.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_parse_stream",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_stream.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_pat",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_pat.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_path",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_path.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_precedence",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_precedence.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_receiver",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_receiver.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_round_trip",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_round_trip.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_shebang",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_shebang.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_should_parse",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_should_parse.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_size",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_size.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_stmt",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_stmt.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_token_trees",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_token_trees.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_ty",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ty.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_visibility",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_visibility.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "zzz_stable",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/zzz_stable.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "rust",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/rust.rs",
+                    "edition": "2021",
+                    "required-features": [
+                        "full",
+                        "parsing"
+                    ],
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "file",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/file.rs",
+                    "edition": "2021",
+                    "required-features": [
+                        "full",
+                        "parsing"
+                    ],
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "clone-impls": [],
+                "default": [
+                    "derive",
+                    "parsing",
+                    "printing",
+                    "clone-impls",
+                    "proc-macro"
+                ],
+                "derive": [],
+                "extra-traits": [],
+                "fold": [],
+                "full": [],
+                "parsing": [],
+                "printing": [
+                    "quote"
+                ],
+                "proc-macro": [
+                    "proc-macro2/proc-macro",
+                    "quote/proc-macro"
+                ],
+                "quote": [
+                    "dep:quote"
+                ],
+                "test": [
+                    "syn-test-suite/all-features"
+                ],
+                "visit": [],
+                "visit-mut": []
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "all-features": true,
+                        "rustdoc-args": [
+                            "--cfg",
+                            "doc_cfg"
+                        ],
+                        "targets": [
+                            "x86_64-unknown-linux-gnu"
+                        ]
+                    }
+                },
+                "playground": {
+                    "features": [
+                        "full",
+                        "visit",
+                        "visit-mut",
+                        "fold",
+                        "extra-traits"
+                    ]
+                }
+            },
+            "publish": null,
+            "authors": [
+                "David Tolnay "
+            ],
+            "categories": [
+                "development-tools::procedural-macro-helpers",
+                "parser-implementations"
+            ],
+            "keywords": [
+                "macros",
+                "syn"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/dtolnay/syn",
+            "homepage": null,
+            "documentation": "https://docs.rs/syn",
+            "edition": "2021",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.56"
+        },
+        {
+            "name": "unicode-ident",
+            "version": "1.0.8",
+            "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "(MIT OR Apache-2.0) AND Unicode-DFS-2016",
+            "license_file": null,
+            "description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "criterion",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "fst",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rand",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.8",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [
+                        "small_rng"
+                    ],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "roaring",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.10",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "ucd-trie",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "unicode-xid",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2.4",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "unicode-ident",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "compare",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/compare.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "static_size",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/static_size.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "xid",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/benches/xid.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "targets": [
+                            "x86_64-unknown-linux-gnu"
+                        ]
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "David Tolnay "
+            ],
+            "categories": [
+                "development-tools::procedural-macro-helpers",
+                "no-std"
+            ],
+            "keywords": [
+                "unicode",
+                "xid"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/dtolnay/unicode-ident",
+            "homepage": null,
+            "documentation": "https://docs.rs/unicode-ident",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.31"
+        },
+        {
+            "name": "wasi",
+            "version": "0.11.0+wasi-snapshot-preview1",
+            "id": "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT",
+            "license_file": null,
+            "description": "Experimental WASI API bindings for Rust",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "compiler_builtins",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rustc-std-workspace-core",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": null,
+                    "rename": "core",
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rustc-std-workspace-alloc",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "wasi",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasi-0.11.0+wasi-snapshot-preview1/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                }
+            ],
+            "features": {
+                "compiler_builtins": [
+                    "dep:compiler_builtins"
+                ],
+                "core": [
+                    "dep:core"
+                ],
+                "default": [
+                    "std"
+                ],
+                "rustc-dep-of-std": [
+                    "compiler_builtins",
+                    "core",
+                    "rustc-std-workspace-alloc"
+                ],
+                "rustc-std-workspace-alloc": [
+                    "dep:rustc-std-workspace-alloc"
+                ],
+                "std": []
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasi-0.11.0+wasi-snapshot-preview1/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "The Cranelift Project Developers"
+            ],
+            "categories": [
+                "no-std",
+                "wasm"
+            ],
+            "keywords": [
+                "webassembly",
+                "wasm"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/bytecodealliance/wasi",
+            "homepage": null,
+            "documentation": "https://docs.rs/wasi",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "winapi",
+            "version": "0.3.9",
+            "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT/Apache-2.0",
+            "license_file": null,
+            "description": "Raw FFI bindings for all of Windows API.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "winapi-i686-pc-windows-gnu",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": "i686-pc-windows-gnu",
+                    "registry": null
+                },
+                {
+                    "name": "winapi-x86_64-pc-windows-gnu",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": "x86_64-pc-windows-gnu",
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "winapi",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/build.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "accctrl": [],
+                "aclapi": [],
+                "activation": [],
+                "adhoc": [],
+                "appmgmt": [],
+                "audioclient": [],
+                "audiosessiontypes": [],
+                "avrt": [],
+                "basetsd": [],
+                "bcrypt": [],
+                "bits": [],
+                "bits10_1": [],
+                "bits1_5": [],
+                "bits2_0": [],
+                "bits2_5": [],
+                "bits3_0": [],
+                "bits4_0": [],
+                "bits5_0": [],
+                "bitscfg": [],
+                "bitsmsg": [],
+                "bluetoothapis": [],
+                "bluetoothleapis": [],
+                "bthdef": [],
+                "bthioctl": [],
+                "bthledef": [],
+                "bthsdpdef": [],
+                "bugcodes": [],
+                "cderr": [],
+                "cfg": [],
+                "cfgmgr32": [],
+                "cguid": [],
+                "combaseapi": [],
+                "coml2api": [],
+                "commapi": [],
+                "commctrl": [],
+                "commdlg": [],
+                "commoncontrols": [],
+                "consoleapi": [],
+                "corecrt": [],
+                "corsym": [],
+                "d2d1": [],
+                "d2d1_1": [],
+                "d2d1_2": [],
+                "d2d1_3": [],
+                "d2d1effectauthor": [],
+                "d2d1effects": [],
+                "d2d1effects_1": [],
+                "d2d1effects_2": [],
+                "d2d1svg": [],
+                "d2dbasetypes": [],
+                "d3d": [],
+                "d3d10": [],
+                "d3d10_1": [],
+                "d3d10_1shader": [],
+                "d3d10effect": [],
+                "d3d10misc": [],
+                "d3d10sdklayers": [],
+                "d3d10shader": [],
+                "d3d11": [],
+                "d3d11_1": [],
+                "d3d11_2": [],
+                "d3d11_3": [],
+                "d3d11_4": [],
+                "d3d11on12": [],
+                "d3d11sdklayers": [],
+                "d3d11shader": [],
+                "d3d11tokenizedprogramformat": [],
+                "d3d12": [],
+                "d3d12sdklayers": [],
+                "d3d12shader": [],
+                "d3d9": [],
+                "d3d9caps": [],
+                "d3d9types": [],
+                "d3dcommon": [],
+                "d3dcompiler": [],
+                "d3dcsx": [],
+                "d3dkmdt": [],
+                "d3dkmthk": [],
+                "d3dukmdt": [],
+                "d3dx10core": [],
+                "d3dx10math": [],
+                "d3dx10mesh": [],
+                "datetimeapi": [],
+                "davclnt": [],
+                "dbghelp": [],
+                "dbt": [],
+                "dcommon": [],
+                "dcomp": [],
+                "dcompanimation": [],
+                "dcomptypes": [],
+                "dde": [],
+                "ddraw": [],
+                "ddrawi": [],
+                "ddrawint": [],
+                "debug": [
+                    "impl-debug"
+                ],
+                "debugapi": [],
+                "devguid": [],
+                "devicetopology": [],
+                "devpkey": [],
+                "devpropdef": [],
+                "dinput": [],
+                "dinputd": [],
+                "dispex": [],
+                "dmksctl": [],
+                "dmusicc": [],
+                "docobj": [],
+                "documenttarget": [],
+                "dot1x": [],
+                "dpa_dsa": [],
+                "dpapi": [],
+                "dsgetdc": [],
+                "dsound": [],
+                "dsrole": [],
+                "dvp": [],
+                "dwmapi": [],
+                "dwrite": [],
+                "dwrite_1": [],
+                "dwrite_2": [],
+                "dwrite_3": [],
+                "dxdiag": [],
+                "dxfile": [],
+                "dxgi": [],
+                "dxgi1_2": [],
+                "dxgi1_3": [],
+                "dxgi1_4": [],
+                "dxgi1_5": [],
+                "dxgi1_6": [],
+                "dxgidebug": [],
+                "dxgiformat": [],
+                "dxgitype": [],
+                "dxva2api": [],
+                "dxvahd": [],
+                "eaptypes": [],
+                "enclaveapi": [],
+                "endpointvolume": [],
+                "errhandlingapi": [],
+                "everything": [],
+                "evntcons": [],
+                "evntprov": [],
+                "evntrace": [],
+                "excpt": [],
+                "exdisp": [],
+                "fibersapi": [],
+                "fileapi": [],
+                "functiondiscoverykeys_devpkey": [],
+                "gl-gl": [],
+                "guiddef": [],
+                "handleapi": [],
+                "heapapi": [],
+                "hidclass": [],
+                "hidpi": [],
+                "hidsdi": [],
+                "hidusage": [],
+                "highlevelmonitorconfigurationapi": [],
+                "hstring": [],
+                "http": [],
+                "ifdef": [],
+                "ifmib": [],
+                "imm": [],
+                "impl-debug": [],
+                "impl-default": [],
+                "in6addr": [],
+                "inaddr": [],
+                "inspectable": [],
+                "interlockedapi": [],
+                "intsafe": [],
+                "ioapiset": [],
+                "ipexport": [],
+                "iphlpapi": [],
+                "ipifcons": [],
+                "ipmib": [],
+                "iprtrmib": [],
+                "iptypes": [],
+                "jobapi": [],
+                "jobapi2": [],
+                "knownfolders": [],
+                "ks": [],
+                "ksmedia": [],
+                "ktmtypes": [],
+                "ktmw32": [],
+                "l2cmn": [],
+                "libloaderapi": [],
+                "limits": [],
+                "lmaccess": [],
+                "lmalert": [],
+                "lmapibuf": [],
+                "lmat": [],
+                "lmcons": [],
+                "lmdfs": [],
+                "lmerrlog": [],
+                "lmjoin": [],
+                "lmmsg": [],
+                "lmremutl": [],
+                "lmrepl": [],
+                "lmserver": [],
+                "lmshare": [],
+                "lmstats": [],
+                "lmsvc": [],
+                "lmuse": [],
+                "lmwksta": [],
+                "lowlevelmonitorconfigurationapi": [],
+                "lsalookup": [],
+                "memoryapi": [],
+                "minschannel": [],
+                "minwinbase": [],
+                "minwindef": [],
+                "mmdeviceapi": [],
+                "mmeapi": [],
+                "mmreg": [],
+                "mmsystem": [],
+                "mprapidef": [],
+                "msaatext": [],
+                "mscat": [],
+                "mschapp": [],
+                "mssip": [],
+                "mstcpip": [],
+                "mswsock": [],
+                "mswsockdef": [],
+                "namedpipeapi": [],
+                "namespaceapi": [],
+                "nb30": [],
+                "ncrypt": [],
+                "netioapi": [],
+                "nldef": [],
+                "ntddndis": [],
+                "ntddscsi": [],
+                "ntddser": [],
+                "ntdef": [],
+                "ntlsa": [],
+                "ntsecapi": [],
+                "ntstatus": [],
+                "oaidl": [],
+                "objbase": [],
+                "objidl": [],
+                "objidlbase": [],
+                "ocidl": [],
+                "ole2": [],
+                "oleauto": [],
+                "olectl": [],
+                "oleidl": [],
+                "opmapi": [],
+                "pdh": [],
+                "perflib": [],
+                "physicalmonitorenumerationapi": [],
+                "playsoundapi": [],
+                "portabledevice": [],
+                "portabledeviceapi": [],
+                "portabledevicetypes": [],
+                "powerbase": [],
+                "powersetting": [],
+                "powrprof": [],
+                "processenv": [],
+                "processsnapshot": [],
+                "processthreadsapi": [],
+                "processtopologyapi": [],
+                "profileapi": [],
+                "propidl": [],
+                "propkey": [],
+                "propkeydef": [],
+                "propsys": [],
+                "prsht": [],
+                "psapi": [],
+                "qos": [],
+                "realtimeapiset": [],
+                "reason": [],
+                "restartmanager": [],
+                "restrictederrorinfo": [],
+                "rmxfguid": [],
+                "roapi": [],
+                "robuffer": [],
+                "roerrorapi": [],
+                "rpc": [],
+                "rpcdce": [],
+                "rpcndr": [],
+                "rtinfo": [],
+                "sapi": [],
+                "sapi51": [],
+                "sapi53": [],
+                "sapiddk": [],
+                "sapiddk51": [],
+                "schannel": [],
+                "sddl": [],
+                "securityappcontainer": [],
+                "securitybaseapi": [],
+                "servprov": [],
+                "setupapi": [],
+                "shellapi": [],
+                "shellscalingapi": [],
+                "shlobj": [],
+                "shobjidl": [],
+                "shobjidl_core": [],
+                "shtypes": [],
+                "softpub": [],
+                "spapidef": [],
+                "spellcheck": [],
+                "sporder": [],
+                "sql": [],
+                "sqlext": [],
+                "sqltypes": [],
+                "sqlucode": [],
+                "sspi": [],
+                "std": [],
+                "stralign": [],
+                "stringapiset": [],
+                "strmif": [],
+                "subauth": [],
+                "synchapi": [],
+                "sysinfoapi": [],
+                "systemtopologyapi": [],
+                "taskschd": [],
+                "tcpestats": [],
+                "tcpmib": [],
+                "textstor": [],
+                "threadpoolapiset": [],
+                "threadpoollegacyapiset": [],
+                "timeapi": [],
+                "timezoneapi": [],
+                "tlhelp32": [],
+                "transportsettingcommon": [],
+                "tvout": [],
+                "udpmib": [],
+                "unknwnbase": [],
+                "urlhist": [],
+                "urlmon": [],
+                "usb": [],
+                "usbioctl": [],
+                "usbiodef": [],
+                "usbscan": [],
+                "usbspec": [],
+                "userenv": [],
+                "usp10": [],
+                "utilapiset": [],
+                "uxtheme": [],
+                "vadefs": [],
+                "vcruntime": [],
+                "vsbackup": [],
+                "vss": [],
+                "vsserror": [],
+                "vswriter": [],
+                "wbemads": [],
+                "wbemcli": [],
+                "wbemdisp": [],
+                "wbemprov": [],
+                "wbemtran": [],
+                "wct": [],
+                "werapi": [],
+                "winbase": [],
+                "wincodec": [],
+                "wincodecsdk": [],
+                "wincon": [],
+                "wincontypes": [],
+                "wincred": [],
+                "wincrypt": [],
+                "windef": [],
+                "windot11": [],
+                "windowsceip": [],
+                "windowsx": [],
+                "winefs": [],
+                "winerror": [],
+                "winevt": [],
+                "wingdi": [],
+                "winhttp": [],
+                "wininet": [],
+                "winineti": [],
+                "winioctl": [],
+                "winnetwk": [],
+                "winnls": [],
+                "winnt": [],
+                "winreg": [],
+                "winsafer": [],
+                "winscard": [],
+                "winsmcrd": [],
+                "winsock2": [],
+                "winspool": [],
+                "winstring": [],
+                "winsvc": [],
+                "wintrust": [],
+                "winusb": [],
+                "winusbio": [],
+                "winuser": [],
+                "winver": [],
+                "wlanapi": [],
+                "wlanihv": [],
+                "wlanihvtypes": [],
+                "wlantypes": [],
+                "wlclient": [],
+                "wmistr": [],
+                "wnnc": [],
+                "wow64apiset": [],
+                "wpdmtpextensions": [],
+                "ws2bth": [],
+                "ws2def": [],
+                "ws2ipdef": [],
+                "ws2spi": [],
+                "ws2tcpip": [],
+                "wtsapi32": [],
+                "wtypes": [],
+                "wtypesbase": [],
+                "xinput": []
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "default-target": "x86_64-pc-windows-msvc",
+                        "features": [
+                            "everything",
+                            "impl-debug",
+                            "impl-default"
+                        ],
+                        "targets": [
+                            "aarch64-pc-windows-msvc",
+                            "i686-pc-windows-msvc",
+                            "x86_64-pc-windows-msvc"
+                        ]
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "Peter Atashian "
+            ],
+            "categories": [
+                "external-ffi-bindings",
+                "no-std",
+                "os::windows-apis"
+            ],
+            "keywords": [
+                "windows",
+                "ffi",
+                "win32",
+                "com",
+                "directx"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/retep998/winapi-rs",
+            "homepage": null,
+            "documentation": "https://docs.rs/winapi/",
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "winapi-i686-pc-windows-gnu",
+            "version": "0.4.0",
+            "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT/Apache-2.0",
+            "license_file": null,
+            "description": "Import libraries for the i686-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "winapi-i686-pc-windows-gnu",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/build.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Peter Atashian "
+            ],
+            "categories": [],
+            "keywords": [
+                "windows"
+            ],
+            "readme": null,
+            "repository": "https://github.com/retep998/winapi-rs",
+            "homepage": null,
+            "documentation": null,
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "winapi-x86_64-pc-windows-gnu",
+            "version": "0.4.0",
+            "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT/Apache-2.0",
+            "license_file": null,
+            "description": "Import libraries for the x86_64-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "winapi-x86_64-pc-windows-gnu",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/build.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Peter Atashian "
+            ],
+            "categories": [],
+            "keywords": [
+                "windows"
+            ],
+            "readme": null,
+            "repository": "https://github.com/retep998/winapi-rs",
+            "homepage": null,
+            "documentation": null,
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        }
+    ],
+    "workspace_members": [
+        "regex-benchmark 0.1.0 (path+file:///$ROOT$regex/bench)",
+        "regex 1.7.1 (path+file:///$ROOT$regex)",
+        "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)",
+        "rure 0.2.2 (path+file:///$ROOT$regex/regex-capi)",
+        "regex-debug 0.1.0 (path+file:///$ROOT$regex/regex-debug)"
+    ],
+    "resolve": {
+        "nodes": [
+            {
+                "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "memchr",
+                        "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "default",
+                    "std"
+                ]
+            },
+            {
+                "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": []
+            },
+            {
+                "id": "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": []
+            },
+            {
+                "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": []
+            },
+            {
+                "id": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "lazy_static",
+                        "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "regex",
+                        "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "serde",
+                        "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "strsim",
+                        "pkg": "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "cfg_if",
+                        "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "libc",
+                        "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": "cfg(unix)"
+                            }
+                        ]
+                    },
+                    {
+                        "name": "wasi",
+                        "pkg": "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": "cfg(target_os = \"wasi\")"
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": []
+            },
+            {
+                "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": [
+                    "default",
+                    "std"
+                ]
+            },
+            {
+                "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": [
+                    "default",
+                    "std"
+                ]
+            },
+            {
+                "id": "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "libc",
+                        "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": "cfg(unix)"
+                            }
+                        ]
+                    },
+                    {
+                        "name": "winapi",
+                        "pkg": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": "cfg(windows)"
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": []
+            },
+            {
+                "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "unicode_ident",
+                        "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "default",
+                    "proc-macro"
+                ]
+            },
+            {
+                "id": "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "rand",
+                        "pkg": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "proc_macro2",
+                        "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "default",
+                    "proc-macro"
+                ]
+            },
+            {
+                "id": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "rand_core",
+                        "pkg": "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "getrandom",
+                    "small_rng"
+                ]
+            },
+            {
+                "id": "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "getrandom",
+                        "pkg": "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "getrandom"
+                ]
+            },
+            {
+                "id": "regex 1.7.1 (path+file:///$ROOT$regex)",
+                "dependencies": [
+                    "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)"
+                ],
+                "deps": [
+                    {
+                        "name": "aho_corasick",
+                        "pkg": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "lazy_static",
+                        "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": "dev",
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "memchr",
+                        "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "quickcheck",
+                        "pkg": "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": "dev",
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "rand",
+                        "pkg": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": "dev",
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "regex_syntax",
+                        "pkg": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "aho-corasick",
+                    "default",
+                    "memchr",
+                    "perf",
+                    "perf-cache",
+                    "perf-dfa",
+                    "perf-inline",
+                    "perf-literal",
+                    "std",
+                    "unicode",
+                    "unicode-age",
+                    "unicode-bool",
+                    "unicode-case",
+                    "unicode-gencat",
+                    "unicode-perl",
+                    "unicode-script",
+                    "unicode-segment"
+                ]
+            },
+            {
+                "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "regex_syntax",
+                        "pkg": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "std",
+                    "unicode",
+                    "unicode-age",
+                    "unicode-bool",
+                    "unicode-case",
+                    "unicode-gencat",
+                    "unicode-perl",
+                    "unicode-script",
+                    "unicode-segment"
+                ]
+            },
+            {
+                "id": "regex-benchmark 0.1.0 (path+file:///$ROOT$regex/bench)",
+                "dependencies": [
+                    "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "regex 1.7.1 (path+file:///$ROOT$regex)",
+                    "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)",
+                    "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "cc",
+                        "pkg": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": "build",
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "cfg_if",
+                        "pkg": "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "docopt",
+                        "pkg": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "lazy_static",
+                        "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "libc",
+                        "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "memmap",
+                        "pkg": "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "pkg_config",
+                        "pkg": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": "build",
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "regex",
+                        "pkg": "regex 1.7.1 (path+file:///$ROOT$regex)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "regex_syntax",
+                        "pkg": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "serde",
+                        "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "regex-debug 0.1.0 (path+file:///$ROOT$regex/regex-debug)",
+                "dependencies": [
+                    "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "regex 1.7.1 (path+file:///$ROOT$regex)",
+                    "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)",
+                    "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "docopt",
+                        "pkg": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "regex",
+                        "pkg": "regex 1.7.1 (path+file:///$ROOT$regex)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "regex_syntax",
+                        "pkg": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "serde",
+                        "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)",
+                "dependencies": [],
+                "deps": [],
+                "features": [
+                    "default",
+                    "unicode",
+                    "unicode-age",
+                    "unicode-bool",
+                    "unicode-case",
+                    "unicode-gencat",
+                    "unicode-perl",
+                    "unicode-script",
+                    "unicode-segment"
+                ]
+            },
+            {
+                "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": [
+                    "unicode",
+                    "unicode-age",
+                    "unicode-bool",
+                    "unicode-case",
+                    "unicode-gencat",
+                    "unicode-perl",
+                    "unicode-script",
+                    "unicode-segment"
+                ]
+            },
+            {
+                "id": "rure 0.2.2 (path+file:///$ROOT$regex/regex-capi)",
+                "dependencies": [
+                    "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "regex 1.7.1 (path+file:///$ROOT$regex)"
+                ],
+                "deps": [
+                    {
+                        "name": "libc",
+                        "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "regex",
+                        "pkg": "regex 1.7.1 (path+file:///$ROOT$regex)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "serde_derive",
+                        "pkg": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "default",
+                    "derive",
+                    "serde_derive",
+                    "std"
+                ]
+            },
+            {
+                "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "proc_macro2",
+                        "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "quote",
+                        "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "syn",
+                        "pkg": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "default"
+                ]
+            },
+            {
+                "id": "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": []
+            },
+            {
+                "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "proc_macro2",
+                        "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "quote",
+                        "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "unicode_ident",
+                        "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "clone-impls",
+                    "default",
+                    "derive",
+                    "parsing",
+                    "printing",
+                    "proc-macro",
+                    "quote"
+                ]
+            },
+            {
+                "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": []
+            },
+            {
+                "id": "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": []
+            },
+            {
+                "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "winapi_i686_pc_windows_gnu",
+                        "pkg": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": "i686-pc-windows-gnu"
+                            }
+                        ]
+                    },
+                    {
+                        "name": "winapi_x86_64_pc_windows_gnu",
+                        "pkg": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": "x86_64-pc-windows-gnu"
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "basetsd",
+                    "handleapi",
+                    "memoryapi",
+                    "minwindef",
+                    "std",
+                    "sysinfoapi"
+                ]
+            },
+            {
+                "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": []
+            },
+            {
+                "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": []
+            }
+        ],
+        "root": "regex 1.7.1 (path+file:///$ROOT$regex)"
+    },
+    "target_directory": "$ROOT$regex/target",
+    "version": 1,
+    "workspace_root": "$ROOT$regex",
+    "metadata": null
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json b/src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json
new file mode 100644
index 0000000000000..131ff5dd71571
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json
@@ -0,0 +1,12816 @@
+{
+    "packages": [
+        {
+            "name": "aho-corasick",
+            "version": "0.7.20",
+            "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "Unlicense OR MIT",
+            "license_file": null,
+            "description": "Fast multiple substring searching.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "memchr",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^2.4.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "aho_corasick",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                }
+            ],
+            "features": {
+                "default": [
+                    "std"
+                ],
+                "std": [
+                    "memchr/std"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Andrew Gallant "
+            ],
+            "categories": [
+                "text-processing"
+            ],
+            "keywords": [
+                "string",
+                "search",
+                "text",
+                "aho",
+                "multi"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/aho-corasick",
+            "homepage": "https://github.com/BurntSushi/aho-corasick",
+            "documentation": null,
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "aho-corasick",
+            "version": "1.0.1",
+            "id": "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "Unlicense OR MIT",
+            "license_file": null,
+            "description": "Fast multiple substring searching.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "log",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4.17",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "memchr",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^2.4.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "doc-comment",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3.3",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "aho_corasick",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-1.0.1/src/lib.rs",
+                    "edition": "2021",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                }
+            ],
+            "features": {
+                "default": [
+                    "std",
+                    "perf-literal"
+                ],
+                "logging": [
+                    "dep:log"
+                ],
+                "perf-literal": [
+                    "dep:memchr"
+                ],
+                "std": [
+                    "memchr?/std"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-1.0.1/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "all-features": true,
+                        "rustdoc-args": [
+                            "--cfg",
+                            "docsrs"
+                        ]
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "Andrew Gallant "
+            ],
+            "categories": [
+                "text-processing"
+            ],
+            "keywords": [
+                "string",
+                "search",
+                "text",
+                "pattern",
+                "multi"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/aho-corasick",
+            "homepage": "https://github.com/BurntSushi/aho-corasick",
+            "documentation": null,
+            "edition": "2021",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.60.0"
+        },
+        {
+            "name": "atty",
+            "version": "0.2.14",
+            "id": "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT",
+            "license_file": null,
+            "description": "A simple interface for querying atty",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "hermit-abi",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1.6",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": "cfg(target_os = \"hermit\")",
+                    "registry": null
+                },
+                {
+                    "name": "libc",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": "cfg(unix)",
+                    "registry": null
+                },
+                {
+                    "name": "winapi",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [
+                        "consoleapi",
+                        "processenv",
+                        "minwinbase",
+                        "minwindef",
+                        "winbase"
+                    ],
+                    "target": "cfg(windows)",
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "atty",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/atty-0.2.14/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "atty",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/atty-0.2.14/examples/atty.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/atty-0.2.14/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "softprops "
+            ],
+            "categories": [],
+            "keywords": [
+                "terminal",
+                "tty",
+                "isatty"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/softprops/atty",
+            "homepage": "https://github.com/softprops/atty",
+            "documentation": "http://softprops.github.io/atty",
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "base64",
+            "version": "0.20.0",
+            "id": "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "encodes and decodes base64 as bytes or utf8",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "criterion",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rand",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.8.5",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [
+                        "small_rng"
+                    ],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rstest",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.12.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rstest_reuse",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "structopt",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3.26",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "base64",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/src/lib.rs",
+                    "edition": "2021",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "base64",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/examples/base64.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "encode",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/tests/encode.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "tests",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/tests/tests.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "benchmarks",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/benches/benchmarks.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "alloc": [],
+                "default": [
+                    "std"
+                ],
+                "std": []
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Alice Maz ",
+                "Marshall Pierce "
+            ],
+            "categories": [
+                "encoding"
+            ],
+            "keywords": [
+                "base64",
+                "utf8",
+                "encode",
+                "decode",
+                "no_std"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/marshallpierce/rust-base64",
+            "homepage": null,
+            "documentation": "https://docs.rs/base64",
+            "edition": "2021",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.57.0"
+        },
+        {
+            "name": "bitflags",
+            "version": "1.3.2",
+            "id": "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT/Apache-2.0",
+            "license_file": null,
+            "description": "A macro to generate structures which behave like bitflags.\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "compiler_builtins",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1.2",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rustc-std-workspace-core",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.0",
+                    "kind": null,
+                    "rename": "core",
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rustversion",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde_derive",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde_json",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "trybuild",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "walkdir",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^2.3",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "bitflags",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "basic",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/tests/basic.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "compile",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/tests/compile.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                }
+            ],
+            "features": {
+                "compiler_builtins": [
+                    "dep:compiler_builtins"
+                ],
+                "core": [
+                    "dep:core"
+                ],
+                "default": [],
+                "example_generated": [],
+                "rustc-dep-of-std": [
+                    "core",
+                    "compiler_builtins"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "features": [
+                            "example_generated"
+                        ]
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "The Rust Project Developers"
+            ],
+            "categories": [
+                "no-std"
+            ],
+            "keywords": [
+                "bit",
+                "bitmask",
+                "bitflags",
+                "flags"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/bitflags/bitflags",
+            "homepage": "https://github.com/bitflags/bitflags",
+            "documentation": "https://docs.rs/bitflags",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "bstr",
+            "version": "1.4.0",
+            "id": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "A string type that is not required to be valid UTF-8.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "memchr",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^2.4.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "once_cell",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.14.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "regex-automata",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1.5",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.85",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "quickcheck",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "ucd-parse",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1.3",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "unicode-segmentation",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.2.1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "bstr",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/src/lib.rs",
+                    "edition": "2021",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "graphemes",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/graphemes.rs",
+                    "edition": "2021",
+                    "required-features": [
+                        "std",
+                        "unicode"
+                    ],
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "lines",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/lines.rs",
+                    "edition": "2021",
+                    "required-features": [
+                        "std"
+                    ],
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "uppercase",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/uppercase.rs",
+                    "edition": "2021",
+                    "required-features": [
+                        "std",
+                        "unicode"
+                    ],
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "words",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/words.rs",
+                    "edition": "2021",
+                    "required-features": [
+                        "std",
+                        "unicode"
+                    ],
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "graphemes-std",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/graphemes-std.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "lines-std",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/lines-std.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "uppercase-std",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/uppercase-std.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "words-std",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/words-std.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "alloc": [
+                    "serde?/alloc"
+                ],
+                "default": [
+                    "std",
+                    "unicode"
+                ],
+                "serde": [
+                    "dep:serde"
+                ],
+                "std": [
+                    "alloc",
+                    "memchr/std",
+                    "serde?/std"
+                ],
+                "unicode": [
+                    "dep:once_cell",
+                    "dep:regex-automata"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "all-features": true,
+                        "rustdoc-args": [
+                            "--cfg",
+                            "docsrs"
+                        ]
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "Andrew Gallant "
+            ],
+            "categories": [
+                "text-processing",
+                "encoding"
+            ],
+            "keywords": [
+                "string",
+                "str",
+                "byte",
+                "bytes",
+                "text"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/bstr",
+            "homepage": "https://github.com/BurntSushi/bstr",
+            "documentation": "https://docs.rs/bstr",
+            "edition": "2021",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.60"
+        },
+        {
+            "name": "bytecount",
+            "version": "0.6.3",
+            "id": "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "Apache-2.0/MIT",
+            "license_file": null,
+            "description": "count occurrences of a given byte, or the number of UTF-8 code points, in a byte slice, fast",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "packed_simd_2",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3.8",
+                    "kind": null,
+                    "rename": "packed_simd",
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "criterion",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "quickcheck",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rand",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.8",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "bytecount",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "check",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/tests/check.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "bench",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/benches/bench.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "generic-simd": [
+                    "packed_simd"
+                ],
+                "html_report": [],
+                "packed_simd": [
+                    "dep:packed_simd"
+                ],
+                "runtime-dispatch-simd": []
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Andre Bogus ",
+                "Joshua Landau "
+            ],
+            "categories": [
+                "algorithms",
+                "no-std"
+            ],
+            "keywords": [],
+            "readme": "README.md",
+            "repository": "https://github.com/llogiq/bytecount",
+            "homepage": null,
+            "documentation": null,
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "cc",
+            "version": "1.0.79",
+            "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "A build-time dependency for Cargo build scripts to assist in invoking the native\nC compiler to compile native C code into a static archive to be linked into Rust\ncode.\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "jobserver",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1.16",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "tempfile",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^3",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "cc",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bin"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "gcc-shim",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/bin/gcc-shim.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "cc_env",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cc_env.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "cflags",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cflags.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "cxxflags",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cxxflags.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/test.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                }
+            ],
+            "features": {
+                "jobserver": [
+                    "dep:jobserver"
+                ],
+                "parallel": [
+                    "jobserver"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Alex Crichton "
+            ],
+            "categories": [
+                "development-tools::build-utils"
+            ],
+            "keywords": [
+                "build-dependencies"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/rust-lang/cc-rs",
+            "homepage": "https://github.com/rust-lang/cc-rs",
+            "documentation": "https://docs.rs/cc",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "cfg-if",
+            "version": "1.0.0",
+            "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT/Apache-2.0",
+            "license_file": null,
+            "description": "A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "compiler_builtins",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1.2",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rustc-std-workspace-core",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.0",
+                    "kind": null,
+                    "rename": "core",
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "cfg-if",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "xcrate",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/tests/xcrate.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                }
+            ],
+            "features": {
+                "compiler_builtins": [
+                    "dep:compiler_builtins"
+                ],
+                "core": [
+                    "dep:core"
+                ],
+                "rustc-dep-of-std": [
+                    "core",
+                    "compiler_builtins"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Alex Crichton "
+            ],
+            "categories": [],
+            "keywords": [],
+            "readme": "README.md",
+            "repository": "https://github.com/alexcrichton/cfg-if",
+            "homepage": "https://github.com/alexcrichton/cfg-if",
+            "documentation": "https://docs.rs/cfg-if",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "clap",
+            "version": "2.34.0",
+            "id": "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT",
+            "license_file": null,
+            "description": "A simple to use, efficient, and full-featured Command Line Argument Parser\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "atty",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2.2",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "bitflags",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "clippy",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "~0.0.166",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "strsim",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.8",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "term_size",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "textwrap",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.11.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "unicode-width",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1.4",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "vec_map",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.8",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "yaml-rust",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3.5",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "lazy_static",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.3",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "regex",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "version-sync",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.8",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "ansi_term",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.12",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": "cfg(not(windows))",
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "clap",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/clap-2.34.0/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                }
+            ],
+            "features": {
+                "ansi_term": [
+                    "dep:ansi_term"
+                ],
+                "atty": [
+                    "dep:atty"
+                ],
+                "clippy": [
+                    "dep:clippy"
+                ],
+                "color": [
+                    "ansi_term",
+                    "atty"
+                ],
+                "debug": [],
+                "default": [
+                    "suggestions",
+                    "color",
+                    "vec_map"
+                ],
+                "doc": [
+                    "yaml"
+                ],
+                "nightly": [],
+                "no_cargo": [],
+                "strsim": [
+                    "dep:strsim"
+                ],
+                "suggestions": [
+                    "strsim"
+                ],
+                "term_size": [
+                    "dep:term_size"
+                ],
+                "unstable": [],
+                "vec_map": [
+                    "dep:vec_map"
+                ],
+                "wrap_help": [
+                    "term_size",
+                    "textwrap/term_size"
+                ],
+                "yaml": [
+                    "yaml-rust"
+                ],
+                "yaml-rust": [
+                    "dep:yaml-rust"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/clap-2.34.0/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "features": [
+                            "doc"
+                        ]
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "Kevin K. "
+            ],
+            "categories": [
+                "command-line-interface"
+            ],
+            "keywords": [
+                "argument",
+                "cli",
+                "arg",
+                "parser",
+                "parse"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/clap-rs/clap",
+            "homepage": "https://clap.rs/",
+            "documentation": "https://docs.rs/clap/",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "crossbeam-channel",
+            "version": "0.5.8",
+            "id": "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "Multi-producer multi-consumer channels for message passing",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "cfg-if",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "crossbeam-utils",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.8",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "num_cpus",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.13.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rand",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.8",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "signal-hook",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "crossbeam-channel",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "fibonacci",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/examples/fibonacci.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "matching",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/examples/matching.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "stopwatch",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/examples/stopwatch.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "after",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/after.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "array",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/array.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "golang",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/golang.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "iter",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/iter.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "list",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/list.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "mpsc",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/mpsc.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "never",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/never.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "ready",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/ready.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "same_channel",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/same_channel.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "select",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/select.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "select_macro",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/select_macro.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "thread_locals",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/thread_locals.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "tick",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/tick.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "zero",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/zero.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "crossbeam",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/benches/crossbeam.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "crossbeam-utils": [
+                    "dep:crossbeam-utils"
+                ],
+                "default": [
+                    "std"
+                ],
+                "std": [
+                    "crossbeam-utils/std"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [],
+            "categories": [
+                "algorithms",
+                "concurrency",
+                "data-structures"
+            ],
+            "keywords": [
+                "channel",
+                "mpmc",
+                "select",
+                "golang",
+                "message"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/crossbeam-rs/crossbeam",
+            "homepage": "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-channel",
+            "documentation": null,
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.38"
+        },
+        {
+            "name": "crossbeam-utils",
+            "version": "0.8.15",
+            "id": "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "Utilities for concurrent programming",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "cfg-if",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rand",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.8",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rustversion",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "loom",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.5",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": "cfg(crossbeam_loom)",
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "crossbeam-utils",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "atomic_cell",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/atomic_cell.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "cache_padded",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/cache_padded.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "parker",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/parker.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "sharded_lock",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/sharded_lock.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "thread",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/thread.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "wait_group",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/wait_group.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "atomic_cell",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/benches/atomic_cell.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/build.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "default": [
+                    "std"
+                ],
+                "loom": [
+                    "dep:loom"
+                ],
+                "nightly": [],
+                "std": []
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [],
+            "categories": [
+                "algorithms",
+                "concurrency",
+                "data-structures",
+                "no-std"
+            ],
+            "keywords": [
+                "scoped",
+                "thread",
+                "atomic",
+                "cache"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/crossbeam-rs/crossbeam",
+            "homepage": "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-utils",
+            "documentation": null,
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.38"
+        },
+        {
+            "name": "encoding_rs",
+            "version": "0.8.32",
+            "id": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "(Apache-2.0 OR MIT) AND BSD-3-Clause",
+            "license_file": null,
+            "description": "A Gecko-oriented implementation of the Encoding Standard",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "cfg-if",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "packed_simd_2",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3.4",
+                    "kind": null,
+                    "rename": "packed_simd",
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "bincode",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde_derive",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde_json",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "encoding_rs",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs-0.8.32/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                }
+            ],
+            "features": {
+                "alloc": [],
+                "default": [
+                    "alloc"
+                ],
+                "fast-big5-hanzi-encode": [],
+                "fast-gb-hanzi-encode": [],
+                "fast-hangul-encode": [],
+                "fast-hanja-encode": [],
+                "fast-kanji-encode": [],
+                "fast-legacy-encode": [
+                    "fast-hangul-encode",
+                    "fast-hanja-encode",
+                    "fast-kanji-encode",
+                    "fast-gb-hanzi-encode",
+                    "fast-big5-hanzi-encode"
+                ],
+                "less-slow-big5-hanzi-encode": [],
+                "less-slow-gb-hanzi-encode": [],
+                "less-slow-kanji-encode": [],
+                "packed_simd": [
+                    "dep:packed_simd"
+                ],
+                "serde": [
+                    "dep:serde"
+                ],
+                "simd-accel": [
+                    "packed_simd",
+                    "packed_simd/into_bits"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs-0.8.32/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Henri Sivonen "
+            ],
+            "categories": [
+                "text-processing",
+                "encoding",
+                "web-programming",
+                "internationalization"
+            ],
+            "keywords": [
+                "encoding",
+                "web",
+                "unicode",
+                "charset"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/hsivonen/encoding_rs",
+            "homepage": "https://docs.rs/encoding_rs/",
+            "documentation": "https://docs.rs/encoding_rs/",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "encoding_rs_io",
+            "version": "0.1.7",
+            "id": "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "Streaming transcoding for encoding_rs",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "encoding_rs",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.8",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "encoding_rs_io",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs_io-0.1.7/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs_io-0.1.7/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Andrew Gallant "
+            ],
+            "categories": [
+                "text-processing",
+                "encoding",
+                "web-programming",
+                "email"
+            ],
+            "keywords": [
+                "encoding",
+                "transcoding",
+                "stream",
+                "io",
+                "read"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/encoding_rs_io",
+            "homepage": null,
+            "documentation": "https://docs.rs/encoding_rs_io",
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "fnv",
+            "version": "1.0.7",
+            "id": "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "Apache-2.0 / MIT",
+            "license_file": null,
+            "description": "Fowler–Noll–Vo hash function",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "fnv",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/fnv-1.0.7/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                }
+            ],
+            "features": {
+                "default": [
+                    "std"
+                ],
+                "std": []
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/fnv-1.0.7/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Alex Crichton "
+            ],
+            "categories": [],
+            "keywords": [],
+            "readme": "README.md",
+            "repository": "https://github.com/servo/rust-fnv",
+            "homepage": null,
+            "documentation": "https://doc.servo.org/fnv/",
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "glob",
+            "version": "0.3.1",
+            "id": "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "Support for matching file paths against Unix shell style patterns.\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "doc-comment",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "tempdir",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "glob",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/glob-0.3.1/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "glob-std",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/glob-0.3.1/tests/glob-std.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/glob-0.3.1/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "The Rust Project Developers"
+            ],
+            "categories": [
+                "filesystem"
+            ],
+            "keywords": [],
+            "readme": "README.md",
+            "repository": "https://github.com/rust-lang/glob",
+            "homepage": "https://github.com/rust-lang/glob",
+            "documentation": "https://docs.rs/glob/0.3.1",
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "globset",
+            "version": "0.4.10",
+            "id": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)",
+            "license": "Unlicense OR MIT",
+            "license_file": null,
+            "description": "Cross platform single glob and glob set matching. Glob set matching is the\nprocess of matching one or more glob patterns against a single candidate path\nsimultaneously, and returning all of the globs that matched.\n",
+            "source": null,
+            "dependencies": [
+                {
+                    "name": "aho-corasick",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.7.3",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "bstr",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [
+                        "std"
+                    ],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "fnv",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.6",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "log",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4.5",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "regex",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.1.5",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [
+                        "perf",
+                        "std"
+                    ],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.104",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "glob",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "lazy_static",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde_json",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.45",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "globset",
+                    "src_path": "$ROOT$ripgrep/crates/globset/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "bench",
+                    "src_path": "$ROOT$ripgrep/crates/globset/benches/bench.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "default": [
+                    "log"
+                ],
+                "log": [
+                    "dep:log"
+                ],
+                "serde": [
+                    "dep:serde"
+                ],
+                "serde1": [
+                    "serde"
+                ],
+                "simd-accel": []
+            },
+            "manifest_path": "$ROOT$ripgrep/crates/globset/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Andrew Gallant "
+            ],
+            "categories": [],
+            "keywords": [
+                "regex",
+                "glob",
+                "multiple",
+                "set",
+                "pattern"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/globset",
+            "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/globset",
+            "documentation": "https://docs.rs/globset",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "grep",
+            "version": "0.2.11",
+            "id": "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)",
+            "license": "Unlicense OR MIT",
+            "license_file": null,
+            "description": "Fast line oriented regex searching as a library.\n",
+            "source": null,
+            "dependencies": [
+                {
+                    "name": "grep-cli",
+                    "source": null,
+                    "req": "^0.1.7",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null,
+                    "path": "$ROOT$ripgrep/crates/cli"
+                },
+                {
+                    "name": "grep-matcher",
+                    "source": null,
+                    "req": "^0.1.6",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null,
+                    "path": "$ROOT$ripgrep/crates/matcher"
+                },
+                {
+                    "name": "grep-pcre2",
+                    "source": null,
+                    "req": "^0.1.6",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null,
+                    "path": "$ROOT$ripgrep/crates/pcre2"
+                },
+                {
+                    "name": "grep-printer",
+                    "source": null,
+                    "req": "^0.1.7",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null,
+                    "path": "$ROOT$ripgrep/crates/printer"
+                },
+                {
+                    "name": "grep-regex",
+                    "source": null,
+                    "req": "^0.1.11",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null,
+                    "path": "$ROOT$ripgrep/crates/regex"
+                },
+                {
+                    "name": "grep-searcher",
+                    "source": null,
+                    "req": "^0.1.11",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null,
+                    "path": "$ROOT$ripgrep/crates/searcher"
+                },
+                {
+                    "name": "termcolor",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.4",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "walkdir",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^2.2.7",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "grep",
+                    "src_path": "$ROOT$ripgrep/crates/grep/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "simplegrep",
+                    "src_path": "$ROOT$ripgrep/crates/grep/examples/simplegrep.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "avx-accel": [],
+                "grep-pcre2": [
+                    "dep:grep-pcre2"
+                ],
+                "pcre2": [
+                    "grep-pcre2"
+                ],
+                "simd-accel": [
+                    "grep-searcher/simd-accel"
+                ]
+            },
+            "manifest_path": "$ROOT$ripgrep/crates/grep/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Andrew Gallant "
+            ],
+            "categories": [],
+            "keywords": [
+                "regex",
+                "grep",
+                "egrep",
+                "search",
+                "pattern"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/grep",
+            "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/grep",
+            "documentation": "https://docs.rs/grep",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "grep-cli",
+            "version": "0.1.7",
+            "id": "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)",
+            "license": "Unlicense OR MIT",
+            "license_file": null,
+            "description": "Utilities for search oriented command line applications.\n",
+            "source": null,
+            "dependencies": [
+                {
+                    "name": "atty",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2.11",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "bstr",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "globset",
+                    "source": null,
+                    "req": "^0.4.10",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null,
+                    "path": "$ROOT$ripgrep/crates/globset"
+                },
+                {
+                    "name": "lazy_static",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "log",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4.5",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "regex",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "same-file",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.4",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "termcolor",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.4",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "winapi-util",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1.1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": "cfg(windows)",
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "grep-cli",
+                    "src_path": "$ROOT$ripgrep/crates/cli/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$ripgrep/crates/cli/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Andrew Gallant "
+            ],
+            "categories": [],
+            "keywords": [
+                "regex",
+                "grep",
+                "cli",
+                "utility",
+                "util"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/cli",
+            "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/cli",
+            "documentation": "https://docs.rs/grep-cli",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "grep-matcher",
+            "version": "0.1.6",
+            "id": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+            "license": "Unlicense OR MIT",
+            "license_file": null,
+            "description": "A trait for regular expressions, with a focus on line oriented search.\n",
+            "source": null,
+            "dependencies": [
+                {
+                    "name": "memchr",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^2.1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "regex",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "grep-matcher",
+                    "src_path": "$ROOT$ripgrep/crates/matcher/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "integration",
+                    "src_path": "$ROOT$ripgrep/crates/matcher/tests/tests.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$ripgrep/crates/matcher/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Andrew Gallant "
+            ],
+            "categories": [],
+            "keywords": [
+                "regex",
+                "pattern",
+                "trait"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/matcher",
+            "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/matcher",
+            "documentation": "https://docs.rs/grep-matcher",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "grep-pcre2",
+            "version": "0.1.6",
+            "id": "grep-pcre2 0.1.6 (path+file:///$ROOT$ripgrep/crates/pcre2)",
+            "license": "Unlicense OR MIT",
+            "license_file": null,
+            "description": "Use PCRE2 with the 'grep' crate.\n",
+            "source": null,
+            "dependencies": [
+                {
+                    "name": "grep-matcher",
+                    "source": null,
+                    "req": "^0.1.6",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null,
+                    "path": "$ROOT$ripgrep/crates/matcher"
+                },
+                {
+                    "name": "pcre2",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2.3",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "grep-pcre2",
+                    "src_path": "$ROOT$ripgrep/crates/pcre2/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$ripgrep/crates/pcre2/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Andrew Gallant "
+            ],
+            "categories": [],
+            "keywords": [
+                "regex",
+                "grep",
+                "pcre",
+                "backreference",
+                "look"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/pcre2",
+            "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/pcre2",
+            "documentation": "https://docs.rs/grep-pcre2",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "grep-printer",
+            "version": "0.1.7",
+            "id": "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)",
+            "license": "Unlicense OR MIT",
+            "license_file": null,
+            "description": "An implementation of the grep crate's Sink trait that provides standard\nprinting of search results, similar to grep itself.\n",
+            "source": null,
+            "dependencies": [
+                {
+                    "name": "base64",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.20.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "bstr",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "grep-matcher",
+                    "source": null,
+                    "req": "^0.1.6",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null,
+                    "path": "$ROOT$ripgrep/crates/matcher"
+                },
+                {
+                    "name": "grep-searcher",
+                    "source": null,
+                    "req": "^0.1.11",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null,
+                    "path": "$ROOT$ripgrep/crates/searcher"
+                },
+                {
+                    "name": "serde",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.77",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [
+                        "derive"
+                    ],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde_json",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.27",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "termcolor",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.4",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "grep-regex",
+                    "source": null,
+                    "req": "^0.1.11",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null,
+                    "path": "$ROOT$ripgrep/crates/regex"
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "grep-printer",
+                    "src_path": "$ROOT$ripgrep/crates/printer/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                }
+            ],
+            "features": {
+                "base64": [
+                    "dep:base64"
+                ],
+                "default": [
+                    "serde1"
+                ],
+                "serde": [
+                    "dep:serde"
+                ],
+                "serde1": [
+                    "base64",
+                    "serde",
+                    "serde_json"
+                ],
+                "serde_json": [
+                    "dep:serde_json"
+                ]
+            },
+            "manifest_path": "$ROOT$ripgrep/crates/printer/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Andrew Gallant "
+            ],
+            "categories": [],
+            "keywords": [
+                "grep",
+                "pattern",
+                "print",
+                "printer",
+                "sink"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/printer",
+            "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/printer",
+            "documentation": "https://docs.rs/grep-printer",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "grep-regex",
+            "version": "0.1.11",
+            "id": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+            "license": "Unlicense OR MIT",
+            "license_file": null,
+            "description": "Use Rust's regex library with the 'grep' crate.\n",
+            "source": null,
+            "dependencies": [
+                {
+                    "name": "aho-corasick",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.7.3",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "bstr",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "grep-matcher",
+                    "source": null,
+                    "req": "^0.1.6",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null,
+                    "path": "$ROOT$ripgrep/crates/matcher"
+                },
+                {
+                    "name": "log",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4.5",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "regex",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "regex-syntax",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.6.5",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "thread_local",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.1.2",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "grep-regex",
+                    "src_path": "$ROOT$ripgrep/crates/regex/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$ripgrep/crates/regex/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Andrew Gallant "
+            ],
+            "categories": [],
+            "keywords": [
+                "regex",
+                "grep",
+                "search",
+                "pattern",
+                "line"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/regex",
+            "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/regex",
+            "documentation": "https://docs.rs/grep-regex",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "grep-searcher",
+            "version": "0.1.11",
+            "id": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)",
+            "license": "Unlicense OR MIT",
+            "license_file": null,
+            "description": "Fast line oriented regex searching as a library.\n",
+            "source": null,
+            "dependencies": [
+                {
+                    "name": "bstr",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [
+                        "std"
+                    ],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "bytecount",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.6",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "encoding_rs",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.8.14",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "encoding_rs_io",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1.6",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "grep-matcher",
+                    "source": null,
+                    "req": "^0.1.6",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null,
+                    "path": "$ROOT$ripgrep/crates/matcher"
+                },
+                {
+                    "name": "log",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4.5",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "memmap2",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.5.3",
+                    "kind": null,
+                    "rename": "memmap",
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "grep-regex",
+                    "source": null,
+                    "req": "^0.1.11",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null,
+                    "path": "$ROOT$ripgrep/crates/regex"
+                },
+                {
+                    "name": "regex",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "grep-searcher",
+                    "src_path": "$ROOT$ripgrep/crates/searcher/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "search-stdin",
+                    "src_path": "$ROOT$ripgrep/crates/searcher/examples/search-stdin.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "avx-accel": [],
+                "default": [
+                    "bytecount/runtime-dispatch-simd"
+                ],
+                "simd-accel": [
+                    "encoding_rs/simd-accel"
+                ]
+            },
+            "manifest_path": "$ROOT$ripgrep/crates/searcher/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Andrew Gallant "
+            ],
+            "categories": [],
+            "keywords": [
+                "regex",
+                "grep",
+                "egrep",
+                "search",
+                "pattern"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/searcher",
+            "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/searcher",
+            "documentation": "https://docs.rs/grep-searcher",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "hermit-abi",
+            "version": "0.1.19",
+            "id": "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT/Apache-2.0",
+            "license_file": null,
+            "description": "hermit-abi is small interface to call functions from the unikernel RustyHermit.\nIt is used to build the target `x86_64-unknown-hermit`.\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "compiler_builtins",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rustc-std-workspace-core",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.0",
+                    "kind": null,
+                    "rename": "core",
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "libc",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2.51",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "hermit-abi",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/hermit-abi-0.1.19/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                }
+            ],
+            "features": {
+                "compiler_builtins": [
+                    "dep:compiler_builtins"
+                ],
+                "core": [
+                    "dep:core"
+                ],
+                "default": [],
+                "docs": [],
+                "rustc-dep-of-std": [
+                    "core",
+                    "compiler_builtins/rustc-dep-of-std",
+                    "libc/rustc-dep-of-std"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/hermit-abi-0.1.19/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "default-target": "x86_64-unknown-hermit",
+                        "features": [
+                            "docs"
+                        ]
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "Stefan Lankes"
+            ],
+            "categories": [
+                "os"
+            ],
+            "keywords": [
+                "unikernel",
+                "libos"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/hermitcore/libhermit-rs",
+            "homepage": null,
+            "documentation": "https://hermitcore.github.io/rusty-hermit/hermit_abi",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "ignore",
+            "version": "0.4.20",
+            "id": "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)",
+            "license": "Unlicense OR MIT",
+            "license_file": null,
+            "description": "A fast library for efficiently matching ignore files such as `.gitignore`\nagainst file paths.\n",
+            "source": null,
+            "dependencies": [
+                {
+                    "name": "globset",
+                    "source": null,
+                    "req": "^0.4.10",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null,
+                    "path": "$ROOT$ripgrep/crates/globset"
+                },
+                {
+                    "name": "lazy_static",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "log",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4.5",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "memchr",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^2.1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "regex",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "same-file",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.4",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "thread_local",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "walkdir",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^2.2.7",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "crossbeam-channel",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.5.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "winapi-util",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1.2",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": "cfg(windows)",
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "ignore",
+                    "src_path": "$ROOT$ripgrep/crates/ignore/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "walk",
+                    "src_path": "$ROOT$ripgrep/crates/ignore/examples/walk.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "gitignore_matched_path_or_any_parents_tests",
+                    "src_path": "$ROOT$ripgrep/crates/ignore/tests/gitignore_matched_path_or_any_parents_tests.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                }
+            ],
+            "features": {
+                "simd-accel": [
+                    "globset/simd-accel"
+                ]
+            },
+            "manifest_path": "$ROOT$ripgrep/crates/ignore/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Andrew Gallant "
+            ],
+            "categories": [],
+            "keywords": [
+                "glob",
+                "ignore",
+                "gitignore",
+                "pattern",
+                "file"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/ignore",
+            "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/ignore",
+            "documentation": "https://docs.rs/ignore",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "itoa",
+            "version": "1.0.6",
+            "id": "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "Fast integer primitive to string conversion",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "no-panic",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "itoa",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/tests/test.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "bench",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/benches/bench.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "no-panic": [
+                    "dep:no-panic"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "targets": [
+                            "x86_64-unknown-linux-gnu"
+                        ]
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "David Tolnay "
+            ],
+            "categories": [
+                "value-formatting",
+                "no-std"
+            ],
+            "keywords": [
+                "integer"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/dtolnay/itoa",
+            "homepage": null,
+            "documentation": "https://docs.rs/itoa",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.36"
+        },
+        {
+            "name": "jemalloc-sys",
+            "version": "0.5.3+5.3.0-patched",
+            "id": "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT/Apache-2.0",
+            "license_file": null,
+            "description": "Rust FFI bindings to jemalloc\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "libc",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2.8",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "cc",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.13",
+                    "kind": "build",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "jemalloc-sys",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "malloc_conf_empty",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/tests/malloc_conf_empty.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "malloc_conf_set",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/tests/malloc_conf_set.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "unprefixed_malloc",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/tests/unprefixed_malloc.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/build.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "background_threads": [
+                    "background_threads_runtime_support"
+                ],
+                "background_threads_runtime_support": [],
+                "debug": [],
+                "default": [
+                    "background_threads_runtime_support"
+                ],
+                "disable_initial_exec_tls": [],
+                "profiling": [],
+                "stats": [],
+                "unprefixed_malloc_on_supported_platforms": []
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "rustdoc-args": [
+                            "--cfg",
+                            "jemallocator_docs"
+                        ]
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "Alex Crichton ",
+                "Gonzalo Brito Gadeschi ",
+                "The TiKV Project Developers"
+            ],
+            "categories": [],
+            "keywords": [
+                "allocator",
+                "jemalloc"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/tikv/jemallocator",
+            "homepage": "https://github.com/tikv/jemallocator",
+            "documentation": "https://docs.rs/jemallocator-sys",
+            "edition": "2018",
+            "links": "jemalloc",
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "jemallocator",
+            "version": "0.5.0",
+            "id": "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT/Apache-2.0",
+            "license_file": null,
+            "description": "A Rust allocator backed by jemalloc\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "jemalloc-sys",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.5.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "libc",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2.8",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "paste",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "jemallocator",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "background_thread_defaults",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/background_thread_defaults.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "background_thread_enabled",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/background_thread_enabled.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "ffi",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/ffi.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "grow_in_place",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/grow_in_place.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "malloctl",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/malloctl.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "shrink_in_place",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/shrink_in_place.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "smoke",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/smoke.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "smoke_ffi",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/smoke_ffi.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "usable_size",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/usable_size.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "roundtrip",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/benches/roundtrip.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "alloc_trait": [],
+                "background_threads": [
+                    "jemalloc-sys/background_threads"
+                ],
+                "background_threads_runtime_support": [
+                    "jemalloc-sys/background_threads_runtime_support"
+                ],
+                "debug": [
+                    "jemalloc-sys/debug"
+                ],
+                "default": [
+                    "background_threads_runtime_support"
+                ],
+                "disable_initial_exec_tls": [
+                    "jemalloc-sys/disable_initial_exec_tls"
+                ],
+                "profiling": [
+                    "jemalloc-sys/profiling"
+                ],
+                "stats": [
+                    "jemalloc-sys/stats"
+                ],
+                "unprefixed_malloc_on_supported_platforms": [
+                    "jemalloc-sys/unprefixed_malloc_on_supported_platforms"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "features": [],
+                        "rustdoc-args": [
+                            "--cfg",
+                            "jemallocator_docs"
+                        ]
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "Alex Crichton ",
+                "Gonzalo Brito Gadeschi ",
+                "Simon Sapin ",
+                "Steven Fackler ",
+                "The TiKV Project Developers"
+            ],
+            "categories": [
+                "memory-management",
+                "api-bindings"
+            ],
+            "keywords": [
+                "allocator",
+                "jemalloc"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/tikv/jemallocator",
+            "homepage": "https://github.com/tikv/jemallocator",
+            "documentation": "https://docs.rs/jemallocator",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "jobserver",
+            "version": "0.1.26",
+            "id": "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT/Apache-2.0",
+            "license_file": null,
+            "description": "An implementation of the GNU make jobserver for Rust\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "futures",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "num_cpus",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "tempfile",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^3",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "tokio-core",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "tokio-process",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "libc",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2.50",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": "cfg(unix)",
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "jobserver",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "client",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/client.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "server",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/server.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "client-of-myself",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/client-of-myself.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "make-as-a-client",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/make-as-a-client.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "helper",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/helper.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Alex Crichton "
+            ],
+            "categories": [],
+            "keywords": [],
+            "readme": "README.md",
+            "repository": "https://github.com/alexcrichton/jobserver-rs",
+            "homepage": "https://github.com/alexcrichton/jobserver-rs",
+            "documentation": "https://docs.rs/jobserver",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "lazy_static",
+            "version": "1.4.0",
+            "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT/Apache-2.0",
+            "license_file": null,
+            "description": "A macro for declaring lazily evaluated statics in Rust.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "spin",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.5.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "doc-comment",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3.1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "lazy_static",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "no_std",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/no_std.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/test.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                }
+            ],
+            "features": {
+                "spin": [
+                    "dep:spin"
+                ],
+                "spin_no_std": [
+                    "spin"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Marvin Löbel "
+            ],
+            "categories": [
+                "no-std",
+                "rust-patterns",
+                "memory-management"
+            ],
+            "keywords": [
+                "macro",
+                "lazy",
+                "static"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/rust-lang-nursery/lazy-static.rs",
+            "homepage": null,
+            "documentation": "https://docs.rs/lazy_static",
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "libc",
+            "version": "0.2.142",
+            "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "Raw FFI bindings to platform libraries like libc.\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "rustc-std-workspace-core",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "libc",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "const_fn",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/tests/const_fn.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/build.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "align": [],
+                "const-extern-fn": [],
+                "default": [
+                    "std"
+                ],
+                "extra_traits": [],
+                "rustc-dep-of-std": [
+                    "align",
+                    "rustc-std-workspace-core"
+                ],
+                "rustc-std-workspace-core": [
+                    "dep:rustc-std-workspace-core"
+                ],
+                "std": [],
+                "use_std": [
+                    "std"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "features": [
+                            "const-extern-fn",
+                            "extra_traits"
+                        ]
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "The Rust Project Developers"
+            ],
+            "categories": [
+                "external-ffi-bindings",
+                "no-std",
+                "os"
+            ],
+            "keywords": [
+                "libc",
+                "ffi",
+                "bindings",
+                "operating",
+                "system"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/rust-lang/libc",
+            "homepage": "https://github.com/rust-lang/libc",
+            "documentation": "https://docs.rs/libc/",
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "log",
+            "version": "0.4.17",
+            "id": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "A lightweight logging facade for Rust\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "cfg-if",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "sval",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "=1.0.0-alpha.5",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "value-bag",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "=1.0.0-alpha.9",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rustversion",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [
+                        "derive"
+                    ],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde_test",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "sval",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "=1.0.0-alpha.5",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [
+                        "derive"
+                    ],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "value-bag",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "=1.0.0-alpha.9",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [
+                        "test"
+                    ],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "log",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "filters",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/tests/filters.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "macros",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/tests/macros.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "value",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/benches/value.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/build.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "kv_unstable": [
+                    "value-bag"
+                ],
+                "kv_unstable_serde": [
+                    "kv_unstable_std",
+                    "value-bag/serde",
+                    "serde"
+                ],
+                "kv_unstable_std": [
+                    "std",
+                    "kv_unstable",
+                    "value-bag/error"
+                ],
+                "kv_unstable_sval": [
+                    "kv_unstable",
+                    "value-bag/sval",
+                    "sval"
+                ],
+                "max_level_debug": [],
+                "max_level_error": [],
+                "max_level_info": [],
+                "max_level_off": [],
+                "max_level_trace": [],
+                "max_level_warn": [],
+                "release_max_level_debug": [],
+                "release_max_level_error": [],
+                "release_max_level_info": [],
+                "release_max_level_off": [],
+                "release_max_level_trace": [],
+                "release_max_level_warn": [],
+                "serde": [
+                    "dep:serde"
+                ],
+                "std": [],
+                "sval": [
+                    "dep:sval"
+                ],
+                "value-bag": [
+                    "dep:value-bag"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "features": [
+                            "std",
+                            "serde",
+                            "kv_unstable_std",
+                            "kv_unstable_sval",
+                            "kv_unstable_serde"
+                        ]
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "The Rust Project Developers"
+            ],
+            "categories": [
+                "development-tools::debugging"
+            ],
+            "keywords": [
+                "logging"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/rust-lang/log",
+            "homepage": null,
+            "documentation": "https://docs.rs/log",
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "memchr",
+            "version": "2.5.0",
+            "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "Unlicense/MIT",
+            "license_file": null,
+            "description": "Safe interface to memchr.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "compiler_builtins",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1.2",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rustc-std-workspace-core",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.0",
+                    "kind": null,
+                    "rename": "core",
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "libc",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2.18",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "quickcheck",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.3",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "memchr",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/build.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "compiler_builtins": [
+                    "dep:compiler_builtins"
+                ],
+                "core": [
+                    "dep:core"
+                ],
+                "default": [
+                    "std"
+                ],
+                "libc": [
+                    "dep:libc"
+                ],
+                "rustc-dep-of-std": [
+                    "core",
+                    "compiler_builtins"
+                ],
+                "std": [],
+                "use_std": [
+                    "std"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Andrew Gallant ",
+                "bluss"
+            ],
+            "categories": [],
+            "keywords": [
+                "memchr",
+                "char",
+                "scan",
+                "strchr",
+                "string"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/memchr",
+            "homepage": "https://github.com/BurntSushi/memchr",
+            "documentation": "https://docs.rs/memchr/",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "memmap2",
+            "version": "0.5.10",
+            "id": "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "Cross-platform Rust API for memory-mapped file IO",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "stable_deref_trait",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "owning_ref",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4.1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "tempfile",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^3",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "libc",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": "cfg(unix)",
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "memmap2",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.5.10/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "cat",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.5.10/examples/cat.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "stable_deref_trait": [
+                    "dep:stable_deref_trait"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.5.10/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Dan Burkert ",
+                "Yevhenii Reizner "
+            ],
+            "categories": [],
+            "keywords": [
+                "mmap",
+                "memory-map",
+                "io",
+                "file"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/RazrFalcon/memmap2-rs",
+            "homepage": null,
+            "documentation": "https://docs.rs/memmap2",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "once_cell",
+            "version": "1.17.1",
+            "id": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "Single assignment cells and lazy values.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "atomic-polyfill",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": null,
+                    "rename": "atomic_polyfill",
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "critical-section",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": null,
+                    "rename": "critical_section",
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "parking_lot_core",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.9.3",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "critical-section",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.1.1",
+                    "kind": "dev",
+                    "rename": "critical_section",
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [
+                        "std"
+                    ],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "crossbeam-utils",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.8.7",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "lazy_static",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "regex",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.2.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "once_cell",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/src/lib.rs",
+                    "edition": "2021",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "bench",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/bench.rs",
+                    "edition": "2021",
+                    "required-features": [
+                        "std"
+                    ],
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "bench_acquire",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/bench_acquire.rs",
+                    "edition": "2021",
+                    "required-features": [
+                        "std"
+                    ],
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "bench_vs_lazy_static",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/bench_vs_lazy_static.rs",
+                    "edition": "2021",
+                    "required-features": [
+                        "std"
+                    ],
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "lazy_static",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/lazy_static.rs",
+                    "edition": "2021",
+                    "required-features": [
+                        "std"
+                    ],
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "reentrant_init_deadlocks",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/reentrant_init_deadlocks.rs",
+                    "edition": "2021",
+                    "required-features": [
+                        "std"
+                    ],
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "regex",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/regex.rs",
+                    "edition": "2021",
+                    "required-features": [
+                        "std"
+                    ],
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_synchronization",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/test_synchronization.rs",
+                    "edition": "2021",
+                    "required-features": [
+                        "std"
+                    ],
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "it",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/tests/it.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                }
+            ],
+            "features": {
+                "alloc": [
+                    "race"
+                ],
+                "atomic-polyfill": [
+                    "critical-section"
+                ],
+                "atomic_polyfill": [
+                    "dep:atomic_polyfill"
+                ],
+                "critical-section": [
+                    "critical_section",
+                    "atomic_polyfill"
+                ],
+                "critical_section": [
+                    "dep:critical_section"
+                ],
+                "default": [
+                    "std"
+                ],
+                "parking_lot": [
+                    "parking_lot_core"
+                ],
+                "parking_lot_core": [
+                    "dep:parking_lot_core"
+                ],
+                "race": [],
+                "std": [
+                    "alloc"
+                ],
+                "unstable": []
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "all-features": true
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "Aleksey Kladov "
+            ],
+            "categories": [
+                "rust-patterns",
+                "memory-management"
+            ],
+            "keywords": [
+                "lazy",
+                "static"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/matklad/once_cell",
+            "homepage": null,
+            "documentation": "https://docs.rs/once_cell",
+            "edition": "2021",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.56"
+        },
+        {
+            "name": "pcre2",
+            "version": "0.2.3",
+            "id": "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "Unlicense/MIT",
+            "license_file": null,
+            "description": "High level wrapper library for PCRE2.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "libc",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2.46",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "log",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4.5",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "pcre2-sys",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "thread_local",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "pcre2",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-0.2.3/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-0.2.3/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Andrew Gallant "
+            ],
+            "categories": [
+                "text-processing"
+            ],
+            "keywords": [
+                "pcre",
+                "pcre2",
+                "regex",
+                "jit",
+                "perl"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/rust-pcre2",
+            "homepage": "https://github.com/BurntSushi/rust-pcre2",
+            "documentation": "https://docs.rs/pcre2",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "pcre2-sys",
+            "version": "0.2.5",
+            "id": "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "Unlicense/MIT",
+            "license_file": null,
+            "description": "Low level bindings to PCRE2.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "libc",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "cc",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "build",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [
+                        "parallel"
+                    ],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "pkg-config",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3.13",
+                    "kind": "build",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "pcre2-sys",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-sys-0.2.5/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-sys-0.2.5/build.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-sys-0.2.5/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Andrew Gallant "
+            ],
+            "categories": [
+                "external-ffi-bindings"
+            ],
+            "keywords": [
+                "pcre",
+                "pcre2",
+                "regex",
+                "jit"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/rust-pcre2",
+            "homepage": "https://github.com/BurntSushi/rust-pcre2",
+            "documentation": "https://docs.rs/pcre2-sys",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "pkg-config",
+            "version": "0.3.26",
+            "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "A library to run the pkg-config system tool at build time in order to be used in\nCargo build scripts.\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "lazy_static",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "pkg-config",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/tests/test.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Alex Crichton "
+            ],
+            "categories": [],
+            "keywords": [
+                "build-dependencies"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/rust-lang/pkg-config-rs",
+            "homepage": null,
+            "documentation": "https://docs.rs/pkg-config",
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "proc-macro2",
+            "version": "1.0.56",
+            "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "unicode-ident",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "quote",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rustversion",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "proc-macro2",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "comments",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/comments.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "features",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/features.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "marker",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/marker.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_fmt",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_fmt.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_size",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_size.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/build.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "default": [
+                    "proc-macro"
+                ],
+                "nightly": [],
+                "proc-macro": [],
+                "span-locations": []
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "rustc-args": [
+                            "--cfg",
+                            "procmacro2_semver_exempt"
+                        ],
+                        "rustdoc-args": [
+                            "--cfg",
+                            "procmacro2_semver_exempt",
+                            "--cfg",
+                            "doc_cfg"
+                        ],
+                        "targets": [
+                            "x86_64-unknown-linux-gnu"
+                        ]
+                    }
+                },
+                "playground": {
+                    "features": [
+                        "span-locations"
+                    ]
+                }
+            },
+            "publish": null,
+            "authors": [
+                "David Tolnay ",
+                "Alex Crichton "
+            ],
+            "categories": [
+                "development-tools::procedural-macro-helpers"
+            ],
+            "keywords": [
+                "macros",
+                "syn"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/dtolnay/proc-macro2",
+            "homepage": null,
+            "documentation": "https://docs.rs/proc-macro2",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.31"
+        },
+        {
+            "name": "quote",
+            "version": "1.0.26",
+            "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "Quasi-quoting macro quote!(...)",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "proc-macro2",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.52",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rustversion",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "trybuild",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.66",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [
+                        "diff"
+                    ],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "quote",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "compiletest",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/compiletest.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/test.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/build.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "default": [
+                    "proc-macro"
+                ],
+                "proc-macro": [
+                    "proc-macro2/proc-macro"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "targets": [
+                            "x86_64-unknown-linux-gnu"
+                        ]
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "David Tolnay "
+            ],
+            "categories": [
+                "development-tools::procedural-macro-helpers"
+            ],
+            "keywords": [
+                "macros",
+                "syn"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/dtolnay/quote",
+            "homepage": null,
+            "documentation": "https://docs.rs/quote/",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.31"
+        },
+        {
+            "name": "regex",
+            "version": "1.8.1",
+            "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "aho-corasick",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "memchr",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^2.5.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "regex-syntax",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.7.1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "lazy_static",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "quickcheck",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.3",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rand",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.8.3",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [
+                        "getrandom",
+                        "small_rng"
+                    ],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "regex",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/src/lib.rs",
+                    "edition": "2021",
+                    "doc": true,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "shootout-regex-dna-bytes",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-bytes.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "shootout-regex-dna-cheat",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-cheat.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "shootout-regex-dna-replace",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-replace.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "shootout-regex-dna-single-cheat",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single-cheat.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "shootout-regex-dna-single",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "shootout-regex-dna",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "default",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "default-bytes",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default_bytes.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "nfa",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "nfa-utf8bytes",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_utf8bytes.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "nfa-bytes",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_bytes.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "backtrack",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "backtrack-utf8bytes",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_utf8bytes.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "backtrack-bytes",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_bytes.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "crates-regex",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_crates_regex.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                }
+            ],
+            "features": {
+                "aho-corasick": [
+                    "dep:aho-corasick"
+                ],
+                "default": [
+                    "std",
+                    "perf",
+                    "unicode",
+                    "regex-syntax/default"
+                ],
+                "memchr": [
+                    "dep:memchr"
+                ],
+                "pattern": [],
+                "perf": [
+                    "perf-cache",
+                    "perf-dfa",
+                    "perf-inline",
+                    "perf-literal"
+                ],
+                "perf-cache": [],
+                "perf-dfa": [],
+                "perf-inline": [],
+                "perf-literal": [
+                    "aho-corasick",
+                    "memchr"
+                ],
+                "std": [],
+                "unicode": [
+                    "unicode-age",
+                    "unicode-bool",
+                    "unicode-case",
+                    "unicode-gencat",
+                    "unicode-perl",
+                    "unicode-script",
+                    "unicode-segment",
+                    "regex-syntax/unicode"
+                ],
+                "unicode-age": [
+                    "regex-syntax/unicode-age"
+                ],
+                "unicode-bool": [
+                    "regex-syntax/unicode-bool"
+                ],
+                "unicode-case": [
+                    "regex-syntax/unicode-case"
+                ],
+                "unicode-gencat": [
+                    "regex-syntax/unicode-gencat"
+                ],
+                "unicode-perl": [
+                    "regex-syntax/unicode-perl"
+                ],
+                "unicode-script": [
+                    "regex-syntax/unicode-script"
+                ],
+                "unicode-segment": [
+                    "regex-syntax/unicode-segment"
+                ],
+                "unstable": [
+                    "pattern"
+                ],
+                "use_std": [
+                    "std"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "The Rust Project Developers"
+            ],
+            "categories": [
+                "text-processing"
+            ],
+            "keywords": [],
+            "readme": "README.md",
+            "repository": "https://github.com/rust-lang/regex",
+            "homepage": "https://github.com/rust-lang/regex",
+            "documentation": "https://docs.rs/regex",
+            "edition": "2021",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.60.0"
+        },
+        {
+            "name": "regex-automata",
+            "version": "0.1.10",
+            "id": "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "Unlicense/MIT",
+            "license_file": null,
+            "description": "Automata construction and matching using regular expressions.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "fst",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "regex-syntax",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.6.16",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "bstr",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [
+                        "std"
+                    ],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "lazy_static",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.2.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "regex",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.82",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde_bytes",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.11",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde_derive",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.82",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "toml",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4.10",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "regex-automata",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.1.10/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "default",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.1.10/tests/tests.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                }
+            ],
+            "features": {
+                "default": [
+                    "std"
+                ],
+                "fst": [
+                    "dep:fst"
+                ],
+                "regex-syntax": [
+                    "dep:regex-syntax"
+                ],
+                "std": [
+                    "regex-syntax"
+                ],
+                "transducer": [
+                    "std",
+                    "fst"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.1.10/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Andrew Gallant "
+            ],
+            "categories": [
+                "text-processing"
+            ],
+            "keywords": [
+                "regex",
+                "dfa",
+                "automata",
+                "automaton",
+                "nfa"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/regex-automata",
+            "homepage": "https://github.com/BurntSushi/regex-automata",
+            "documentation": "https://docs.rs/regex-automata",
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "regex-syntax",
+            "version": "0.6.29",
+            "id": "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "A regular expression parser.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "regex-syntax",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.6.29/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "bench",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.6.29/benches/bench.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "default": [
+                    "unicode"
+                ],
+                "unicode": [
+                    "unicode-age",
+                    "unicode-bool",
+                    "unicode-case",
+                    "unicode-gencat",
+                    "unicode-perl",
+                    "unicode-script",
+                    "unicode-segment"
+                ],
+                "unicode-age": [],
+                "unicode-bool": [],
+                "unicode-case": [],
+                "unicode-gencat": [],
+                "unicode-perl": [],
+                "unicode-script": [],
+                "unicode-segment": []
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.6.29/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "The Rust Project Developers"
+            ],
+            "categories": [],
+            "keywords": [],
+            "readme": "README.md",
+            "repository": "https://github.com/rust-lang/regex",
+            "homepage": "https://github.com/rust-lang/regex",
+            "documentation": "https://docs.rs/regex-syntax",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "regex-syntax",
+            "version": "0.7.1",
+            "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "A regular expression parser.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "regex-syntax",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/src/lib.rs",
+                    "edition": "2021",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "bench",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/benches/bench.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "default": [
+                    "std",
+                    "unicode"
+                ],
+                "std": [],
+                "unicode": [
+                    "unicode-age",
+                    "unicode-bool",
+                    "unicode-case",
+                    "unicode-gencat",
+                    "unicode-perl",
+                    "unicode-script",
+                    "unicode-segment"
+                ],
+                "unicode-age": [],
+                "unicode-bool": [],
+                "unicode-case": [],
+                "unicode-gencat": [],
+                "unicode-perl": [],
+                "unicode-script": [],
+                "unicode-segment": []
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "all-features": true,
+                        "rustdoc-args": [
+                            "--cfg",
+                            "docsrs"
+                        ]
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "The Rust Project Developers"
+            ],
+            "categories": [],
+            "keywords": [],
+            "readme": "README.md",
+            "repository": "https://github.com/rust-lang/regex",
+            "homepage": "https://github.com/rust-lang/regex",
+            "documentation": "https://docs.rs/regex-syntax",
+            "edition": "2021",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.60.0"
+        },
+        {
+            "name": "ripgrep",
+            "version": "13.0.0",
+            "id": "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)",
+            "license": "Unlicense OR MIT",
+            "license_file": null,
+            "description": "ripgrep is a line-oriented search tool that recursively searches the current\ndirectory for a regex pattern while respecting gitignore rules. ripgrep has\nfirst class support on Windows, macOS and Linux.\n",
+            "source": null,
+            "dependencies": [
+                {
+                    "name": "bstr",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "clap",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^2.33.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [
+                        "suggestions"
+                    ],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "grep",
+                    "source": null,
+                    "req": "^0.2.11",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null,
+                    "path": "$ROOT$ripgrep/crates/grep"
+                },
+                {
+                    "name": "ignore",
+                    "source": null,
+                    "req": "^0.4.19",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null,
+                    "path": "$ROOT$ripgrep/crates/ignore"
+                },
+                {
+                    "name": "lazy_static",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "log",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4.5",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "regex",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.3.5",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde_json",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.23",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "termcolor",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.77",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde_derive",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.77",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "walkdir",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^2",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "clap",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^2.33.0",
+                    "kind": "build",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [
+                        "suggestions"
+                    ],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "lazy_static",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.1.0",
+                    "kind": "build",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "jemallocator",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.5.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": "cfg(all(target_env = \"musl\", target_pointer_width = \"64\"))",
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "bin"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "rg",
+                    "src_path": "$ROOT$ripgrep/crates/core/main.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "integration",
+                    "src_path": "$ROOT$ripgrep/tests/tests.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$ripgrep/build.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "pcre2": [
+                    "grep/pcre2"
+                ],
+                "simd-accel": [
+                    "grep/simd-accel"
+                ]
+            },
+            "manifest_path": "$ROOT$ripgrep/Cargo.toml",
+            "metadata": {
+                "deb": {
+                    "assets": [
+                        [
+                            "target/release/rg",
+                            "usr/bin/",
+                            "755"
+                        ],
+                        [
+                            "COPYING",
+                            "usr/share/doc/ripgrep/",
+                            "644"
+                        ],
+                        [
+                            "LICENSE-MIT",
+                            "usr/share/doc/ripgrep/",
+                            "644"
+                        ],
+                        [
+                            "UNLICENSE",
+                            "usr/share/doc/ripgrep/",
+                            "644"
+                        ],
+                        [
+                            "CHANGELOG.md",
+                            "usr/share/doc/ripgrep/CHANGELOG",
+                            "644"
+                        ],
+                        [
+                            "README.md",
+                            "usr/share/doc/ripgrep/README",
+                            "644"
+                        ],
+                        [
+                            "FAQ.md",
+                            "usr/share/doc/ripgrep/FAQ",
+                            "644"
+                        ],
+                        [
+                            "deployment/deb/rg.1",
+                            "usr/share/man/man1/rg.1",
+                            "644"
+                        ],
+                        [
+                            "deployment/deb/rg.bash",
+                            "usr/share/bash-completion/completions/rg",
+                            "644"
+                        ],
+                        [
+                            "deployment/deb/rg.fish",
+                            "usr/share/fish/vendor_completions.d/rg.fish",
+                            "644"
+                        ],
+                        [
+                            "deployment/deb/_rg",
+                            "usr/share/zsh/vendor-completions/",
+                            "644"
+                        ]
+                    ],
+                    "extended-description": "ripgrep (rg) recursively searches your current directory for a regex pattern.\nBy default, ripgrep will respect your .gitignore and automatically skip hidden\nfiles/directories and binary files.\n",
+                    "features": [
+                        "pcre2"
+                    ],
+                    "section": "utils"
+                }
+            },
+            "publish": null,
+            "authors": [
+                "Andrew Gallant "
+            ],
+            "categories": [
+                "command-line-utilities",
+                "text-processing"
+            ],
+            "keywords": [
+                "regex",
+                "grep",
+                "egrep",
+                "search",
+                "pattern"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/ripgrep",
+            "homepage": "https://github.com/BurntSushi/ripgrep",
+            "documentation": "https://github.com/BurntSushi/ripgrep",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.65"
+        },
+        {
+            "name": "ryu",
+            "version": "1.0.13",
+            "id": "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "Apache-2.0 OR BSL-1.0",
+            "license_file": null,
+            "description": "Fast floating point to string conversion",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "no-panic",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "num_cpus",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.8",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rand",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.8",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rand_xorshift",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "ryu",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "upstream_benchmark",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/examples/upstream_benchmark.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "common_test",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/common_test.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "d2s_table_test",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/d2s_table_test.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "d2s_test",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/d2s_test.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "exhaustive",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/exhaustive.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "f2s_test",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/f2s_test.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "s2d_test",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/s2d_test.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "s2f_test",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/s2f_test.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "bench",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/benches/bench.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "no-panic": [
+                    "dep:no-panic"
+                ],
+                "small": []
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "targets": [
+                            "x86_64-unknown-linux-gnu"
+                        ]
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "David Tolnay "
+            ],
+            "categories": [
+                "value-formatting",
+                "no-std"
+            ],
+            "keywords": [
+                "float"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/dtolnay/ryu",
+            "homepage": null,
+            "documentation": "https://docs.rs/ryu",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.36"
+        },
+        {
+            "name": "same-file",
+            "version": "1.0.6",
+            "id": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "Unlicense/MIT",
+            "license_file": null,
+            "description": "A simple crate for determining whether two file paths point to the same file.\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "doc-comment",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "winapi-util",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1.1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": "cfg(windows)",
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "same-file",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "is_same_file",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/examples/is_same_file.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "is_stderr",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/examples/is_stderr.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Andrew Gallant "
+            ],
+            "categories": [],
+            "keywords": [
+                "same",
+                "file",
+                "equal",
+                "inode"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/same-file",
+            "homepage": "https://github.com/BurntSushi/same-file",
+            "documentation": "https://docs.rs/same-file",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "serde",
+            "version": "1.0.160",
+            "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "A generic serialization/deserialization framework",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "serde_derive",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "=1.0.160",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde_derive",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "serde",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/build.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "alloc": [],
+                "default": [
+                    "std"
+                ],
+                "derive": [
+                    "serde_derive"
+                ],
+                "rc": [],
+                "serde_derive": [
+                    "dep:serde_derive"
+                ],
+                "std": [],
+                "unstable": []
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "features": [
+                            "derive"
+                        ],
+                        "targets": [
+                            "x86_64-unknown-linux-gnu"
+                        ]
+                    }
+                },
+                "playground": {
+                    "features": [
+                        "derive",
+                        "rc"
+                    ]
+                }
+            },
+            "publish": null,
+            "authors": [
+                "Erick Tryzelaar ",
+                "David Tolnay "
+            ],
+            "categories": [
+                "encoding",
+                "no-std"
+            ],
+            "keywords": [
+                "serde",
+                "serialization",
+                "no_std"
+            ],
+            "readme": "crates-io.md",
+            "repository": "https://github.com/serde-rs/serde",
+            "homepage": "https://serde.rs",
+            "documentation": "https://docs.rs/serde",
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.19"
+        },
+        {
+            "name": "serde_derive",
+            "version": "1.0.160",
+            "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "proc-macro2",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "quote",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "syn",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^2.0.3",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "proc-macro"
+                    ],
+                    "crate_types": [
+                        "proc-macro"
+                    ],
+                    "name": "serde_derive",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/build.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "default": [],
+                "deserialize_in_place": []
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "targets": [
+                            "x86_64-unknown-linux-gnu"
+                        ]
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "Erick Tryzelaar ",
+                "David Tolnay "
+            ],
+            "categories": [
+                "no-std"
+            ],
+            "keywords": [
+                "serde",
+                "serialization",
+                "no_std",
+                "derive"
+            ],
+            "readme": "crates-io.md",
+            "repository": "https://github.com/serde-rs/serde",
+            "homepage": "https://serde.rs",
+            "documentation": "https://serde.rs/derive.html",
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.56"
+        },
+        {
+            "name": "serde_json",
+            "version": "1.0.96",
+            "id": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "A JSON serialization file format",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "indexmap",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.5.2",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [
+                        "std"
+                    ],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "itoa",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "ryu",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.100",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "automod",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "indoc",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^2.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "ref-cast",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rustversion",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.100",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [
+                        "derive"
+                    ],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde_bytes",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.11",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde_derive",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "serde_stacker",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "trybuild",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.49",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [
+                        "diff"
+                    ],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "serde_json",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "compiletest",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/compiletest.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "debug",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/debug.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "lexical",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/lexical.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "map",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/map.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "regression",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/regression.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "stream",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/stream.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/test.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/build.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "alloc": [
+                    "serde/alloc"
+                ],
+                "arbitrary_precision": [],
+                "default": [
+                    "std"
+                ],
+                "float_roundtrip": [],
+                "indexmap": [
+                    "dep:indexmap"
+                ],
+                "preserve_order": [
+                    "indexmap",
+                    "std"
+                ],
+                "raw_value": [],
+                "std": [
+                    "serde/std"
+                ],
+                "unbounded_depth": []
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "features": [
+                            "raw_value",
+                            "unbounded_depth"
+                        ],
+                        "rustdoc-args": [
+                            "--cfg",
+                            "docsrs"
+                        ],
+                        "targets": [
+                            "x86_64-unknown-linux-gnu"
+                        ]
+                    }
+                },
+                "playground": {
+                    "features": [
+                        "raw_value"
+                    ]
+                }
+            },
+            "publish": null,
+            "authors": [
+                "Erick Tryzelaar ",
+                "David Tolnay "
+            ],
+            "categories": [
+                "encoding",
+                "parser-implementations",
+                "no-std"
+            ],
+            "keywords": [
+                "json",
+                "serde",
+                "serialization"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/serde-rs/json",
+            "homepage": null,
+            "documentation": "https://docs.rs/serde_json",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.36"
+        },
+        {
+            "name": "strsim",
+            "version": "0.8.0",
+            "id": "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT",
+            "license_file": null,
+            "description": "Implementations of string similarity metrics.\nIncludes Hamming, Levenshtein, OSA, Damerau-Levenshtein, Jaro, and Jaro-Winkler.\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "strsim",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "lib",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/tests/lib.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "benches",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/benches/benches.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Danny Guo "
+            ],
+            "categories": [],
+            "keywords": [
+                "string",
+                "similarity",
+                "Hamming",
+                "Levenshtein",
+                "Jaro"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/dguo/strsim-rs",
+            "homepage": "https://github.com/dguo/strsim-rs",
+            "documentation": "https://docs.rs/strsim/",
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "syn",
+            "version": "2.0.15",
+            "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "Parser for Rust source code",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "proc-macro2",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.55",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "quote",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.25",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "unicode-ident",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "anyhow",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "automod",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "flate2",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "insta",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rayon",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "ref-cast",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "regex",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "reqwest",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.11",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [
+                        "blocking"
+                    ],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rustversion",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "syn-test-suite",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "tar",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4.16",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "termcolor",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "walkdir",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^2.3.2",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "syn",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/src/lib.rs",
+                    "edition": "2021",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "regression",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/regression.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_asyncness",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_asyncness.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_attribute",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_attribute.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_derive_input",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_derive_input.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_expr",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_expr.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_generics",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_generics.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_grouping",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_grouping.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_ident",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ident.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_item",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_item.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_iterators",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_iterators.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_lit",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_lit.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_meta",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_meta.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_parse_buffer",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_buffer.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_parse_stream",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_stream.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_pat",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_pat.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_path",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_path.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_precedence",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_precedence.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_receiver",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_receiver.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_round_trip",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_round_trip.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_shebang",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_shebang.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_should_parse",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_should_parse.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_size",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_size.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_stmt",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_stmt.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_token_trees",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_token_trees.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_ty",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ty.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "test_visibility",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_visibility.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "zzz_stable",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/zzz_stable.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "rust",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/rust.rs",
+                    "edition": "2021",
+                    "required-features": [
+                        "full",
+                        "parsing"
+                    ],
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "file",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/file.rs",
+                    "edition": "2021",
+                    "required-features": [
+                        "full",
+                        "parsing"
+                    ],
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "clone-impls": [],
+                "default": [
+                    "derive",
+                    "parsing",
+                    "printing",
+                    "clone-impls",
+                    "proc-macro"
+                ],
+                "derive": [],
+                "extra-traits": [],
+                "fold": [],
+                "full": [],
+                "parsing": [],
+                "printing": [
+                    "quote"
+                ],
+                "proc-macro": [
+                    "proc-macro2/proc-macro",
+                    "quote/proc-macro"
+                ],
+                "quote": [
+                    "dep:quote"
+                ],
+                "test": [
+                    "syn-test-suite/all-features"
+                ],
+                "visit": [],
+                "visit-mut": []
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "all-features": true,
+                        "rustdoc-args": [
+                            "--cfg",
+                            "doc_cfg"
+                        ],
+                        "targets": [
+                            "x86_64-unknown-linux-gnu"
+                        ]
+                    }
+                },
+                "playground": {
+                    "features": [
+                        "full",
+                        "visit",
+                        "visit-mut",
+                        "fold",
+                        "extra-traits"
+                    ]
+                }
+            },
+            "publish": null,
+            "authors": [
+                "David Tolnay "
+            ],
+            "categories": [
+                "development-tools::procedural-macro-helpers",
+                "parser-implementations"
+            ],
+            "keywords": [
+                "macros",
+                "syn"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/dtolnay/syn",
+            "homepage": null,
+            "documentation": "https://docs.rs/syn",
+            "edition": "2021",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.56"
+        },
+        {
+            "name": "termcolor",
+            "version": "1.2.0",
+            "id": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "Unlicense OR MIT",
+            "license_file": null,
+            "description": "A simple cross platform library for writing colored text to a terminal.\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "winapi-util",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1.3",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": "cfg(windows)",
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "termcolor",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/termcolor-1.2.0/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/termcolor-1.2.0/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Andrew Gallant "
+            ],
+            "categories": [],
+            "keywords": [
+                "windows",
+                "win",
+                "color",
+                "ansi",
+                "console"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/termcolor",
+            "homepage": "https://github.com/BurntSushi/termcolor",
+            "documentation": "https://docs.rs/termcolor",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "textwrap",
+            "version": "0.11.0",
+            "id": "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT",
+            "license_file": null,
+            "description": "Textwrap is a small library for word wrapping, indenting, and\ndedenting strings.\n\nYou can use it to format strings (such as help and error messages) for\ndisplay in commandline applications. It is designed to be efficient\nand handle Unicode characters correctly.\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "hyphenation",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.7.1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [
+                        "embed_all"
+                    ],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "term_size",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "unicode-width",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1.3",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "lipsum",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.6",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rand",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.6",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rand_xorshift",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "version-sync",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.6",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "textwrap",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "layout",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/examples/layout.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "example"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "termwidth",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/examples/termwidth.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "version-numbers",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/tests/version-numbers.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "linear",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/benches/linear.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "hyphenation": [
+                    "dep:hyphenation"
+                ],
+                "term_size": [
+                    "dep:term_size"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "all-features": true
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "Martin Geisler "
+            ],
+            "categories": [
+                "text-processing",
+                "command-line-interface"
+            ],
+            "keywords": [
+                "text",
+                "formatting",
+                "wrap",
+                "typesetting",
+                "hyphenation"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/mgeisler/textwrap",
+            "homepage": null,
+            "documentation": "https://docs.rs/textwrap/",
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "thread_local",
+            "version": "1.1.7",
+            "id": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT OR Apache-2.0",
+            "license_file": null,
+            "description": "Per-object thread-local storage",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "cfg-if",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.0",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "once_cell",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.5.2",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "criterion",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4.0",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "thread_local",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/thread_local-1.1.7/src/lib.rs",
+                    "edition": "2021",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "thread_local",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/thread_local-1.1.7/benches/thread_local.rs",
+                    "edition": "2021",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "nightly": []
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/thread_local-1.1.7/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Amanieu d'Antras "
+            ],
+            "categories": [],
+            "keywords": [
+                "thread_local",
+                "concurrent",
+                "thread"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/Amanieu/thread_local-rs",
+            "homepage": null,
+            "documentation": "https://docs.rs/thread_local/",
+            "edition": "2021",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "unicode-ident",
+            "version": "1.0.8",
+            "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "(MIT OR Apache-2.0) AND Unicode-DFS-2016",
+            "license_file": null,
+            "description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "criterion",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "fst",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rand",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.8",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [
+                        "small_rng"
+                    ],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "roaring",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.10",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "ucd-trie",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": false,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "unicode-xid",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.2.4",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "unicode-ident",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "compare",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/compare.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "test"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "static_size",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/static_size.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "bench"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "xid",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/benches/xid.rs",
+                    "edition": "2018",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "targets": [
+                            "x86_64-unknown-linux-gnu"
+                        ]
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "David Tolnay "
+            ],
+            "categories": [
+                "development-tools::procedural-macro-helpers",
+                "no-std"
+            ],
+            "keywords": [
+                "unicode",
+                "xid"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/dtolnay/unicode-ident",
+            "homepage": null,
+            "documentation": "https://docs.rs/unicode-ident",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": "1.31"
+        },
+        {
+            "name": "unicode-width",
+            "version": "0.1.10",
+            "id": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT/Apache-2.0",
+            "license_file": null,
+            "description": "Determine displayed width of `char` and `str` types\naccording to Unicode Standard Annex #11 rules.\n",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "compiler_builtins",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rustc-std-workspace-core",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": null,
+                    "rename": "core",
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "rustc-std-workspace-std",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0",
+                    "kind": null,
+                    "rename": "std",
+                    "optional": true,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "unicode-width",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-width-0.1.10/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                }
+            ],
+            "features": {
+                "bench": [],
+                "compiler_builtins": [
+                    "dep:compiler_builtins"
+                ],
+                "core": [
+                    "dep:core"
+                ],
+                "default": [],
+                "no_std": [],
+                "rustc-dep-of-std": [
+                    "std",
+                    "core",
+                    "compiler_builtins"
+                ],
+                "std": [
+                    "dep:std"
+                ]
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-width-0.1.10/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "kwantam ",
+                "Manish Goregaokar "
+            ],
+            "categories": [],
+            "keywords": [
+                "text",
+                "width",
+                "unicode"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/unicode-rs/unicode-width",
+            "homepage": "https://github.com/unicode-rs/unicode-width",
+            "documentation": "https://unicode-rs.github.io/unicode-width",
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "walkdir",
+            "version": "2.3.3",
+            "id": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "Unlicense/MIT",
+            "license_file": null,
+            "description": "Recursively walk a directory.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "same-file",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^1.0.1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "doc-comment",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3",
+                    "kind": "dev",
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": null,
+                    "registry": null
+                },
+                {
+                    "name": "winapi-util",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.1.1",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": "cfg(windows)",
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "walkdir",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/walkdir-2.3.3/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/walkdir-2.3.3/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Andrew Gallant "
+            ],
+            "categories": [
+                "filesystem"
+            ],
+            "keywords": [
+                "directory",
+                "recursive",
+                "walk",
+                "iterator"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/walkdir",
+            "homepage": "https://github.com/BurntSushi/walkdir",
+            "documentation": "https://docs.rs/walkdir/",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "winapi",
+            "version": "0.3.9",
+            "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT/Apache-2.0",
+            "license_file": null,
+            "description": "Raw FFI bindings for all of Windows API.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "winapi-i686-pc-windows-gnu",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": "i686-pc-windows-gnu",
+                    "registry": null
+                },
+                {
+                    "name": "winapi-x86_64-pc-windows-gnu",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.4",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [],
+                    "target": "x86_64-pc-windows-gnu",
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "winapi",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/build.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {
+                "accctrl": [],
+                "aclapi": [],
+                "activation": [],
+                "adhoc": [],
+                "appmgmt": [],
+                "audioclient": [],
+                "audiosessiontypes": [],
+                "avrt": [],
+                "basetsd": [],
+                "bcrypt": [],
+                "bits": [],
+                "bits10_1": [],
+                "bits1_5": [],
+                "bits2_0": [],
+                "bits2_5": [],
+                "bits3_0": [],
+                "bits4_0": [],
+                "bits5_0": [],
+                "bitscfg": [],
+                "bitsmsg": [],
+                "bluetoothapis": [],
+                "bluetoothleapis": [],
+                "bthdef": [],
+                "bthioctl": [],
+                "bthledef": [],
+                "bthsdpdef": [],
+                "bugcodes": [],
+                "cderr": [],
+                "cfg": [],
+                "cfgmgr32": [],
+                "cguid": [],
+                "combaseapi": [],
+                "coml2api": [],
+                "commapi": [],
+                "commctrl": [],
+                "commdlg": [],
+                "commoncontrols": [],
+                "consoleapi": [],
+                "corecrt": [],
+                "corsym": [],
+                "d2d1": [],
+                "d2d1_1": [],
+                "d2d1_2": [],
+                "d2d1_3": [],
+                "d2d1effectauthor": [],
+                "d2d1effects": [],
+                "d2d1effects_1": [],
+                "d2d1effects_2": [],
+                "d2d1svg": [],
+                "d2dbasetypes": [],
+                "d3d": [],
+                "d3d10": [],
+                "d3d10_1": [],
+                "d3d10_1shader": [],
+                "d3d10effect": [],
+                "d3d10misc": [],
+                "d3d10sdklayers": [],
+                "d3d10shader": [],
+                "d3d11": [],
+                "d3d11_1": [],
+                "d3d11_2": [],
+                "d3d11_3": [],
+                "d3d11_4": [],
+                "d3d11on12": [],
+                "d3d11sdklayers": [],
+                "d3d11shader": [],
+                "d3d11tokenizedprogramformat": [],
+                "d3d12": [],
+                "d3d12sdklayers": [],
+                "d3d12shader": [],
+                "d3d9": [],
+                "d3d9caps": [],
+                "d3d9types": [],
+                "d3dcommon": [],
+                "d3dcompiler": [],
+                "d3dcsx": [],
+                "d3dkmdt": [],
+                "d3dkmthk": [],
+                "d3dukmdt": [],
+                "d3dx10core": [],
+                "d3dx10math": [],
+                "d3dx10mesh": [],
+                "datetimeapi": [],
+                "davclnt": [],
+                "dbghelp": [],
+                "dbt": [],
+                "dcommon": [],
+                "dcomp": [],
+                "dcompanimation": [],
+                "dcomptypes": [],
+                "dde": [],
+                "ddraw": [],
+                "ddrawi": [],
+                "ddrawint": [],
+                "debug": [
+                    "impl-debug"
+                ],
+                "debugapi": [],
+                "devguid": [],
+                "devicetopology": [],
+                "devpkey": [],
+                "devpropdef": [],
+                "dinput": [],
+                "dinputd": [],
+                "dispex": [],
+                "dmksctl": [],
+                "dmusicc": [],
+                "docobj": [],
+                "documenttarget": [],
+                "dot1x": [],
+                "dpa_dsa": [],
+                "dpapi": [],
+                "dsgetdc": [],
+                "dsound": [],
+                "dsrole": [],
+                "dvp": [],
+                "dwmapi": [],
+                "dwrite": [],
+                "dwrite_1": [],
+                "dwrite_2": [],
+                "dwrite_3": [],
+                "dxdiag": [],
+                "dxfile": [],
+                "dxgi": [],
+                "dxgi1_2": [],
+                "dxgi1_3": [],
+                "dxgi1_4": [],
+                "dxgi1_5": [],
+                "dxgi1_6": [],
+                "dxgidebug": [],
+                "dxgiformat": [],
+                "dxgitype": [],
+                "dxva2api": [],
+                "dxvahd": [],
+                "eaptypes": [],
+                "enclaveapi": [],
+                "endpointvolume": [],
+                "errhandlingapi": [],
+                "everything": [],
+                "evntcons": [],
+                "evntprov": [],
+                "evntrace": [],
+                "excpt": [],
+                "exdisp": [],
+                "fibersapi": [],
+                "fileapi": [],
+                "functiondiscoverykeys_devpkey": [],
+                "gl-gl": [],
+                "guiddef": [],
+                "handleapi": [],
+                "heapapi": [],
+                "hidclass": [],
+                "hidpi": [],
+                "hidsdi": [],
+                "hidusage": [],
+                "highlevelmonitorconfigurationapi": [],
+                "hstring": [],
+                "http": [],
+                "ifdef": [],
+                "ifmib": [],
+                "imm": [],
+                "impl-debug": [],
+                "impl-default": [],
+                "in6addr": [],
+                "inaddr": [],
+                "inspectable": [],
+                "interlockedapi": [],
+                "intsafe": [],
+                "ioapiset": [],
+                "ipexport": [],
+                "iphlpapi": [],
+                "ipifcons": [],
+                "ipmib": [],
+                "iprtrmib": [],
+                "iptypes": [],
+                "jobapi": [],
+                "jobapi2": [],
+                "knownfolders": [],
+                "ks": [],
+                "ksmedia": [],
+                "ktmtypes": [],
+                "ktmw32": [],
+                "l2cmn": [],
+                "libloaderapi": [],
+                "limits": [],
+                "lmaccess": [],
+                "lmalert": [],
+                "lmapibuf": [],
+                "lmat": [],
+                "lmcons": [],
+                "lmdfs": [],
+                "lmerrlog": [],
+                "lmjoin": [],
+                "lmmsg": [],
+                "lmremutl": [],
+                "lmrepl": [],
+                "lmserver": [],
+                "lmshare": [],
+                "lmstats": [],
+                "lmsvc": [],
+                "lmuse": [],
+                "lmwksta": [],
+                "lowlevelmonitorconfigurationapi": [],
+                "lsalookup": [],
+                "memoryapi": [],
+                "minschannel": [],
+                "minwinbase": [],
+                "minwindef": [],
+                "mmdeviceapi": [],
+                "mmeapi": [],
+                "mmreg": [],
+                "mmsystem": [],
+                "mprapidef": [],
+                "msaatext": [],
+                "mscat": [],
+                "mschapp": [],
+                "mssip": [],
+                "mstcpip": [],
+                "mswsock": [],
+                "mswsockdef": [],
+                "namedpipeapi": [],
+                "namespaceapi": [],
+                "nb30": [],
+                "ncrypt": [],
+                "netioapi": [],
+                "nldef": [],
+                "ntddndis": [],
+                "ntddscsi": [],
+                "ntddser": [],
+                "ntdef": [],
+                "ntlsa": [],
+                "ntsecapi": [],
+                "ntstatus": [],
+                "oaidl": [],
+                "objbase": [],
+                "objidl": [],
+                "objidlbase": [],
+                "ocidl": [],
+                "ole2": [],
+                "oleauto": [],
+                "olectl": [],
+                "oleidl": [],
+                "opmapi": [],
+                "pdh": [],
+                "perflib": [],
+                "physicalmonitorenumerationapi": [],
+                "playsoundapi": [],
+                "portabledevice": [],
+                "portabledeviceapi": [],
+                "portabledevicetypes": [],
+                "powerbase": [],
+                "powersetting": [],
+                "powrprof": [],
+                "processenv": [],
+                "processsnapshot": [],
+                "processthreadsapi": [],
+                "processtopologyapi": [],
+                "profileapi": [],
+                "propidl": [],
+                "propkey": [],
+                "propkeydef": [],
+                "propsys": [],
+                "prsht": [],
+                "psapi": [],
+                "qos": [],
+                "realtimeapiset": [],
+                "reason": [],
+                "restartmanager": [],
+                "restrictederrorinfo": [],
+                "rmxfguid": [],
+                "roapi": [],
+                "robuffer": [],
+                "roerrorapi": [],
+                "rpc": [],
+                "rpcdce": [],
+                "rpcndr": [],
+                "rtinfo": [],
+                "sapi": [],
+                "sapi51": [],
+                "sapi53": [],
+                "sapiddk": [],
+                "sapiddk51": [],
+                "schannel": [],
+                "sddl": [],
+                "securityappcontainer": [],
+                "securitybaseapi": [],
+                "servprov": [],
+                "setupapi": [],
+                "shellapi": [],
+                "shellscalingapi": [],
+                "shlobj": [],
+                "shobjidl": [],
+                "shobjidl_core": [],
+                "shtypes": [],
+                "softpub": [],
+                "spapidef": [],
+                "spellcheck": [],
+                "sporder": [],
+                "sql": [],
+                "sqlext": [],
+                "sqltypes": [],
+                "sqlucode": [],
+                "sspi": [],
+                "std": [],
+                "stralign": [],
+                "stringapiset": [],
+                "strmif": [],
+                "subauth": [],
+                "synchapi": [],
+                "sysinfoapi": [],
+                "systemtopologyapi": [],
+                "taskschd": [],
+                "tcpestats": [],
+                "tcpmib": [],
+                "textstor": [],
+                "threadpoolapiset": [],
+                "threadpoollegacyapiset": [],
+                "timeapi": [],
+                "timezoneapi": [],
+                "tlhelp32": [],
+                "transportsettingcommon": [],
+                "tvout": [],
+                "udpmib": [],
+                "unknwnbase": [],
+                "urlhist": [],
+                "urlmon": [],
+                "usb": [],
+                "usbioctl": [],
+                "usbiodef": [],
+                "usbscan": [],
+                "usbspec": [],
+                "userenv": [],
+                "usp10": [],
+                "utilapiset": [],
+                "uxtheme": [],
+                "vadefs": [],
+                "vcruntime": [],
+                "vsbackup": [],
+                "vss": [],
+                "vsserror": [],
+                "vswriter": [],
+                "wbemads": [],
+                "wbemcli": [],
+                "wbemdisp": [],
+                "wbemprov": [],
+                "wbemtran": [],
+                "wct": [],
+                "werapi": [],
+                "winbase": [],
+                "wincodec": [],
+                "wincodecsdk": [],
+                "wincon": [],
+                "wincontypes": [],
+                "wincred": [],
+                "wincrypt": [],
+                "windef": [],
+                "windot11": [],
+                "windowsceip": [],
+                "windowsx": [],
+                "winefs": [],
+                "winerror": [],
+                "winevt": [],
+                "wingdi": [],
+                "winhttp": [],
+                "wininet": [],
+                "winineti": [],
+                "winioctl": [],
+                "winnetwk": [],
+                "winnls": [],
+                "winnt": [],
+                "winreg": [],
+                "winsafer": [],
+                "winscard": [],
+                "winsmcrd": [],
+                "winsock2": [],
+                "winspool": [],
+                "winstring": [],
+                "winsvc": [],
+                "wintrust": [],
+                "winusb": [],
+                "winusbio": [],
+                "winuser": [],
+                "winver": [],
+                "wlanapi": [],
+                "wlanihv": [],
+                "wlanihvtypes": [],
+                "wlantypes": [],
+                "wlclient": [],
+                "wmistr": [],
+                "wnnc": [],
+                "wow64apiset": [],
+                "wpdmtpextensions": [],
+                "ws2bth": [],
+                "ws2def": [],
+                "ws2ipdef": [],
+                "ws2spi": [],
+                "ws2tcpip": [],
+                "wtsapi32": [],
+                "wtypes": [],
+                "wtypesbase": [],
+                "xinput": []
+            },
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "default-target": "x86_64-pc-windows-msvc",
+                        "features": [
+                            "everything",
+                            "impl-debug",
+                            "impl-default"
+                        ],
+                        "targets": [
+                            "aarch64-pc-windows-msvc",
+                            "i686-pc-windows-msvc",
+                            "x86_64-pc-windows-msvc"
+                        ]
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "Peter Atashian "
+            ],
+            "categories": [
+                "external-ffi-bindings",
+                "no-std",
+                "os::windows-apis"
+            ],
+            "keywords": [
+                "windows",
+                "ffi",
+                "win32",
+                "com",
+                "directx"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/retep998/winapi-rs",
+            "homepage": null,
+            "documentation": "https://docs.rs/winapi/",
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "winapi-i686-pc-windows-gnu",
+            "version": "0.4.0",
+            "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT/Apache-2.0",
+            "license_file": null,
+            "description": "Import libraries for the i686-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "winapi-i686-pc-windows-gnu",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/build.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Peter Atashian "
+            ],
+            "categories": [],
+            "keywords": [
+                "windows"
+            ],
+            "readme": null,
+            "repository": "https://github.com/retep998/winapi-rs",
+            "homepage": null,
+            "documentation": null,
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "winapi-util",
+            "version": "0.1.5",
+            "id": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "Unlicense/MIT",
+            "license_file": null,
+            "description": "A dumping ground for high level safe wrappers over winapi.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [
+                {
+                    "name": "winapi",
+                    "source": "registry+https://github.com/rust-lang/crates.io-index",
+                    "req": "^0.3",
+                    "kind": null,
+                    "rename": null,
+                    "optional": false,
+                    "uses_default_features": true,
+                    "features": [
+                        "std",
+                        "consoleapi",
+                        "errhandlingapi",
+                        "fileapi",
+                        "minwindef",
+                        "processenv",
+                        "winbase",
+                        "wincon",
+                        "winerror",
+                        "winnt"
+                    ],
+                    "target": "cfg(windows)",
+                    "registry": null
+                }
+            ],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "winapi-util",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-util-0.1.5/src/lib.rs",
+                    "edition": "2018",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-util-0.1.5/Cargo.toml",
+            "metadata": {
+                "docs": {
+                    "rs": {
+                        "targets": [
+                            "x86_64-pc-windows-msvc"
+                        ]
+                    }
+                }
+            },
+            "publish": null,
+            "authors": [
+                "Andrew Gallant "
+            ],
+            "categories": [
+                "os::windows-apis",
+                "external-ffi-bindings"
+            ],
+            "keywords": [
+                "windows",
+                "winapi",
+                "util",
+                "win"
+            ],
+            "readme": "README.md",
+            "repository": "https://github.com/BurntSushi/winapi-util",
+            "homepage": "https://github.com/BurntSushi/winapi-util",
+            "documentation": "https://docs.rs/winapi-util",
+            "edition": "2018",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        },
+        {
+            "name": "winapi-x86_64-pc-windows-gnu",
+            "version": "0.4.0",
+            "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+            "license": "MIT/Apache-2.0",
+            "license_file": null,
+            "description": "Import libraries for the x86_64-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.",
+            "source": "registry+https://github.com/rust-lang/crates.io-index",
+            "dependencies": [],
+            "targets": [
+                {
+                    "kind": [
+                        "lib"
+                    ],
+                    "crate_types": [
+                        "lib"
+                    ],
+                    "name": "winapi-x86_64-pc-windows-gnu",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/src/lib.rs",
+                    "edition": "2015",
+                    "doc": true,
+                    "doctest": true,
+                    "test": true
+                },
+                {
+                    "kind": [
+                        "custom-build"
+                    ],
+                    "crate_types": [
+                        "bin"
+                    ],
+                    "name": "build-script-build",
+                    "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/build.rs",
+                    "edition": "2015",
+                    "doc": false,
+                    "doctest": false,
+                    "test": false
+                }
+            ],
+            "features": {},
+            "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/Cargo.toml",
+            "metadata": null,
+            "publish": null,
+            "authors": [
+                "Peter Atashian "
+            ],
+            "categories": [],
+            "keywords": [
+                "windows"
+            ],
+            "readme": null,
+            "repository": "https://github.com/retep998/winapi-rs",
+            "homepage": null,
+            "documentation": null,
+            "edition": "2015",
+            "links": null,
+            "default_run": null,
+            "rust_version": null
+        }
+    ],
+    "workspace_members": [
+        "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)",
+        "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)",
+        "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)",
+        "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+        "grep-pcre2 0.1.6 (path+file:///$ROOT$ripgrep/crates/pcre2)",
+        "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)",
+        "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)",
+        "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+        "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)",
+        "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)"
+    ],
+    "resolve": {
+        "nodes": [
+            {
+                "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "memchr",
+                        "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "default",
+                    "std"
+                ]
+            },
+            {
+                "id": "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "memchr",
+                        "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "default",
+                    "perf-literal",
+                    "std"
+                ]
+            },
+            {
+                "id": "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "hermit_abi",
+                        "pkg": "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": "cfg(target_os = \"hermit\")"
+                            }
+                        ]
+                    },
+                    {
+                        "name": "libc",
+                        "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": "cfg(unix)"
+                            }
+                        ]
+                    },
+                    {
+                        "name": "winapi",
+                        "pkg": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": "cfg(windows)"
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": [
+                    "default",
+                    "std"
+                ]
+            },
+            {
+                "id": "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": [
+                    "default"
+                ]
+            },
+            {
+                "id": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "memchr",
+                        "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "once_cell",
+                        "pkg": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "regex_automata",
+                        "pkg": "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "serde",
+                        "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "alloc",
+                    "default",
+                    "std",
+                    "unicode"
+                ]
+            },
+            {
+                "id": "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": [
+                    "runtime-dispatch-simd"
+                ]
+            },
+            {
+                "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "jobserver",
+                        "pkg": "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "jobserver",
+                    "parallel"
+                ]
+            },
+            {
+                "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": []
+            },
+            {
+                "id": "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "bitflags",
+                        "pkg": "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "strsim",
+                        "pkg": "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "textwrap",
+                        "pkg": "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "unicode_width",
+                        "pkg": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "strsim",
+                    "suggestions"
+                ]
+            },
+            {
+                "id": "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "cfg_if",
+                        "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "crossbeam_utils",
+                        "pkg": "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "crossbeam-utils",
+                    "default",
+                    "std"
+                ]
+            },
+            {
+                "id": "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "cfg_if",
+                        "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "std"
+                ]
+            },
+            {
+                "id": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "cfg_if",
+                        "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "alloc",
+                    "default"
+                ]
+            },
+            {
+                "id": "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "encoding_rs",
+                        "pkg": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": [
+                    "default",
+                    "std"
+                ]
+            },
+            {
+                "id": "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": []
+            },
+            {
+                "id": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)",
+                "dependencies": [
+                    "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "aho_corasick",
+                        "pkg": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "bstr",
+                        "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "fnv",
+                        "pkg": "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "glob",
+                        "pkg": "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": "dev",
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "lazy_static",
+                        "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": "dev",
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "log",
+                        "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "regex",
+                        "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "serde_json",
+                        "pkg": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": "dev",
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "default",
+                    "log"
+                ]
+            },
+            {
+                "id": "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)",
+                "dependencies": [
+                    "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)",
+                    "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+                    "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)",
+                    "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+                    "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)",
+                    "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "grep_cli",
+                        "pkg": "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "grep_matcher",
+                        "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "grep_printer",
+                        "pkg": "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "grep_regex",
+                        "pkg": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "grep_searcher",
+                        "pkg": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "termcolor",
+                        "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": "dev",
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "walkdir",
+                        "pkg": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": "dev",
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)",
+                "dependencies": [
+                    "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)",
+                    "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "atty",
+                        "pkg": "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "bstr",
+                        "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "globset",
+                        "pkg": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "lazy_static",
+                        "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "log",
+                        "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "regex",
+                        "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "same_file",
+                        "pkg": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "termcolor",
+                        "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "winapi_util",
+                        "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": "cfg(windows)"
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+                "dependencies": [
+                    "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "memchr",
+                        "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "regex",
+                        "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": "dev",
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "grep-pcre2 0.1.6 (path+file:///$ROOT$ripgrep/crates/pcre2)",
+                "dependencies": [
+                    "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+                    "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "grep_matcher",
+                        "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "pcre2",
+                        "pkg": "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)",
+                "dependencies": [
+                    "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+                    "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+                    "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)",
+                    "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "base64",
+                        "pkg": "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "bstr",
+                        "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "grep_matcher",
+                        "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "grep_regex",
+                        "pkg": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+                        "dep_kinds": [
+                            {
+                                "kind": "dev",
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "grep_searcher",
+                        "pkg": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "serde",
+                        "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "serde_json",
+                        "pkg": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "termcolor",
+                        "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "base64",
+                    "default",
+                    "serde",
+                    "serde1",
+                    "serde_json"
+                ]
+            },
+            {
+                "id": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+                "dependencies": [
+                    "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+                    "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "aho_corasick",
+                        "pkg": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "bstr",
+                        "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "grep_matcher",
+                        "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "log",
+                        "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "regex",
+                        "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "regex_syntax",
+                        "pkg": "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "thread_local",
+                        "pkg": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)",
+                "dependencies": [
+                    "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+                    "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+                    "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "bstr",
+                        "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "bytecount",
+                        "pkg": "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "encoding_rs",
+                        "pkg": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "encoding_rs_io",
+                        "pkg": "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "grep_matcher",
+                        "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "grep_regex",
+                        "pkg": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+                        "dep_kinds": [
+                            {
+                                "kind": "dev",
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "log",
+                        "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "memmap",
+                        "pkg": "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "regex",
+                        "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": "dev",
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "default"
+                ]
+            },
+            {
+                "id": "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "libc",
+                        "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "default"
+                ]
+            },
+            {
+                "id": "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)",
+                "dependencies": [
+                    "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)",
+                    "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "crossbeam_channel",
+                        "pkg": "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": "dev",
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "globset",
+                        "pkg": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "lazy_static",
+                        "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "log",
+                        "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "memchr",
+                        "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "regex",
+                        "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "same_file",
+                        "pkg": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "thread_local",
+                        "pkg": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "walkdir",
+                        "pkg": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "winapi_util",
+                        "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": "cfg(windows)"
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": []
+            },
+            {
+                "id": "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "cc",
+                        "pkg": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": "build",
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "libc",
+                        "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "background_threads_runtime_support"
+                ]
+            },
+            {
+                "id": "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)",
+                    "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "jemalloc_sys",
+                        "pkg": "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "libc",
+                        "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "background_threads_runtime_support",
+                    "default"
+                ]
+            },
+            {
+                "id": "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "libc",
+                        "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": "cfg(unix)"
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": []
+            },
+            {
+                "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": [
+                    "default",
+                    "std"
+                ]
+            },
+            {
+                "id": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "cfg_if",
+                        "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": [
+                    "default",
+                    "std"
+                ]
+            },
+            {
+                "id": "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "libc",
+                        "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": "cfg(unix)"
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": [
+                    "alloc",
+                    "default",
+                    "race",
+                    "std"
+                ]
+            },
+            {
+                "id": "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "libc",
+                        "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "log",
+                        "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "pcre2_sys",
+                        "pkg": "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "thread_local",
+                        "pkg": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "cc",
+                        "pkg": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": "build",
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "libc",
+                        "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "pkg_config",
+                        "pkg": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": "build",
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": []
+            },
+            {
+                "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "unicode_ident",
+                        "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "default",
+                    "proc-macro"
+                ]
+            },
+            {
+                "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "proc_macro2",
+                        "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "default",
+                    "proc-macro"
+                ]
+            },
+            {
+                "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "aho_corasick",
+                        "pkg": "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "memchr",
+                        "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "regex_syntax",
+                        "pkg": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "aho-corasick",
+                    "default",
+                    "memchr",
+                    "perf",
+                    "perf-cache",
+                    "perf-dfa",
+                    "perf-inline",
+                    "perf-literal",
+                    "std",
+                    "unicode",
+                    "unicode-age",
+                    "unicode-bool",
+                    "unicode-case",
+                    "unicode-gencat",
+                    "unicode-perl",
+                    "unicode-script",
+                    "unicode-segment"
+                ]
+            },
+            {
+                "id": "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": []
+            },
+            {
+                "id": "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": [
+                    "default",
+                    "unicode",
+                    "unicode-age",
+                    "unicode-bool",
+                    "unicode-case",
+                    "unicode-gencat",
+                    "unicode-perl",
+                    "unicode-script",
+                    "unicode-segment"
+                ]
+            },
+            {
+                "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": [
+                    "default",
+                    "std",
+                    "unicode",
+                    "unicode-age",
+                    "unicode-bool",
+                    "unicode-case",
+                    "unicode-gencat",
+                    "unicode-perl",
+                    "unicode-script",
+                    "unicode-segment"
+                ]
+            },
+            {
+                "id": "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)",
+                "dependencies": [
+                    "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)",
+                    "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)",
+                    "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "bstr",
+                        "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "clap",
+                        "pkg": "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            },
+                            {
+                                "kind": "build",
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "grep",
+                        "pkg": "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "ignore",
+                        "pkg": "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "jemallocator",
+                        "pkg": "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": "cfg(all(target_env = \"musl\", target_pointer_width = \"64\"))"
+                            }
+                        ]
+                    },
+                    {
+                        "name": "lazy_static",
+                        "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            },
+                            {
+                                "kind": "build",
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "log",
+                        "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "regex",
+                        "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "serde",
+                        "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": "dev",
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "serde_derive",
+                        "pkg": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": "dev",
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "serde_json",
+                        "pkg": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "termcolor",
+                        "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "walkdir",
+                        "pkg": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": "dev",
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": []
+            },
+            {
+                "id": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "winapi_util",
+                        "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": "cfg(windows)"
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "serde_derive",
+                        "pkg": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "alloc",
+                    "default",
+                    "derive",
+                    "serde_derive",
+                    "std"
+                ]
+            },
+            {
+                "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "proc_macro2",
+                        "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "quote",
+                        "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "syn",
+                        "pkg": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "default"
+                ]
+            },
+            {
+                "id": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "itoa",
+                        "pkg": "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "ryu",
+                        "pkg": "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "serde",
+                        "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "default",
+                    "std"
+                ]
+            },
+            {
+                "id": "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": []
+            },
+            {
+                "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "proc_macro2",
+                        "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "quote",
+                        "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "unicode_ident",
+                        "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "clone-impls",
+                    "default",
+                    "derive",
+                    "parsing",
+                    "printing",
+                    "proc-macro",
+                    "quote"
+                ]
+            },
+            {
+                "id": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "winapi_util",
+                        "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": "cfg(windows)"
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "unicode_width",
+                        "pkg": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "cfg_if",
+                        "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "once_cell",
+                        "pkg": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": []
+            },
+            {
+                "id": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": [
+                    "default"
+                ]
+            },
+            {
+                "id": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "same_file",
+                        "pkg": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": null
+                            }
+                        ]
+                    },
+                    {
+                        "name": "winapi_util",
+                        "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": "cfg(windows)"
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                    "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "winapi_i686_pc_windows_gnu",
+                        "pkg": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": "i686-pc-windows-gnu"
+                            }
+                        ]
+                    },
+                    {
+                        "name": "winapi_x86_64_pc_windows_gnu",
+                        "pkg": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": "x86_64-pc-windows-gnu"
+                            }
+                        ]
+                    }
+                ],
+                "features": [
+                    "consoleapi",
+                    "errhandlingapi",
+                    "fileapi",
+                    "minwinbase",
+                    "minwindef",
+                    "processenv",
+                    "std",
+                    "winbase",
+                    "wincon",
+                    "winerror",
+                    "winnt"
+                ]
+            },
+            {
+                "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": []
+            },
+            {
+                "id": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [
+                    "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)"
+                ],
+                "deps": [
+                    {
+                        "name": "winapi",
+                        "pkg": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+                        "dep_kinds": [
+                            {
+                                "kind": null,
+                                "target": "cfg(windows)"
+                            }
+                        ]
+                    }
+                ],
+                "features": []
+            },
+            {
+                "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+                "dependencies": [],
+                "deps": [],
+                "features": []
+            }
+        ],
+        "root": "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)"
+    },
+    "target_directory": "$ROOT$ripgrep/target",
+    "version": 1,
+    "workspace_root": "$ROOT$ripgrep",
+    "metadata": null
+}
diff --git a/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/query_group.rs b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/query_group.rs
index 88db6093ee0e7..d761a5e798e89 100644
--- a/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/query_group.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/ra-salsa-macros/src/query_group.rs
@@ -242,7 +242,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
         let tracing = if let QueryStorage::Memoized | QueryStorage::LruMemoized = query.storage {
             let s = format!("{trait_name}::{fn_name}");
             Some(quote! {
-                let _p = tracing::debug_span!(#s, #(#key_names = tracing::field::debug(&#key_names)),*).entered();
+                let _p = tracing::trace_span!(#s, #(#key_names = tracing::field::debug(&#key_names)),*).entered();
             })
         } else {
             None
diff --git a/src/tools/rust-analyzer/crates/ra-salsa/src/derived/slot.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/derived/slot.rs
index 6c5ccba173b99..cfe2c48f411f1 100644
--- a/src/tools/rust-analyzer/crates/ra-salsa/src/derived/slot.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/derived/slot.rs
@@ -13,7 +13,7 @@ use crate::{Database, DatabaseKeyIndex, Event, EventKind, QueryDb};
 use parking_lot::{RawRwLock, RwLock};
 use std::ops::Deref;
 use std::sync::atomic::{AtomicBool, Ordering};
-use tracing::{debug, info};
+use tracing::trace;
 
 pub(super) struct Slot
 where
@@ -126,7 +126,7 @@ where
         // doing any `set` invocations while the query function runs.
         let revision_now = runtime.current_revision();
 
-        info!("{:?}: invoked at {:?}", self, revision_now,);
+        trace!("{:?}: invoked at {:?}", self, revision_now,);
 
         // First, do a check with a read-lock.
         loop {
@@ -152,7 +152,7 @@ where
     ) -> StampedValue {
         let runtime = db.salsa_runtime();
 
-        debug!("{:?}: read_upgrade(revision_now={:?})", self, revision_now,);
+        trace!("{:?}: read_upgrade(revision_now={:?})", self, revision_now,);
 
         // Check with an upgradable read to see if there is a value
         // already. (This permits other readers but prevents anyone
@@ -184,7 +184,7 @@ where
         // inputs and check whether they are out of date.
         if let Some(memo) = &mut old_memo {
             if let Some(value) = memo.verify_value(db.ops_database(), revision_now, &active_query) {
-                info!("{:?}: validated old memoized value", self,);
+                trace!("{:?}: validated old memoized value", self,);
 
                 db.salsa_event(Event {
                     runtime_id: runtime.id(),
@@ -212,7 +212,7 @@ where
         old_memo: Option>,
         key: &Q::Key,
     ) -> StampedValue {
-        tracing::info!("{:?}: executing query", self.database_key_index().debug(db));
+        tracing::trace!("{:?}: executing query", self.database_key_index().debug(db));
 
         db.salsa_event(Event {
             runtime_id: db.salsa_runtime().id(),
@@ -224,7 +224,7 @@ where
         let value = match Cycle::catch(|| Q::execute(db, key.clone())) {
             Ok(v) => v,
             Err(cycle) => {
-                tracing::debug!(
+                tracing::trace!(
                     "{:?}: caught cycle {:?}, have strategy {:?}",
                     self.database_key_index().debug(db),
                     cycle,
@@ -272,9 +272,10 @@ where
             // consumers must be aware of. Becoming *more* durable
             // is not. See the test `constant_to_non_constant`.
             if revisions.durability >= old_memo.revisions.durability && old_memo.value == value {
-                debug!(
+                trace!(
                     "read_upgrade({:?}): value is equal, back-dating to {:?}",
-                    self, old_memo.revisions.changed_at,
+                    self,
+                    old_memo.revisions.changed_at,
                 );
 
                 assert!(old_memo.revisions.changed_at <= revisions.changed_at);
@@ -290,7 +291,7 @@ where
 
         let memo_value = new_value.value.clone();
 
-        debug!("read_upgrade({:?}): result.revisions = {:#?}", self, revisions,);
+        trace!("read_upgrade({:?}): result.revisions = {:#?}", self, revisions,);
 
         panic_guard.proceed(Some(Memo { value: memo_value, verified_at: revision_now, revisions }));
 
@@ -339,9 +340,11 @@ where
             }
 
             QueryState::Memoized(memo) => {
-                debug!(
+                trace!(
                     "{:?}: found memoized value, verified_at={:?}, changed_at={:?}",
-                    self, memo.verified_at, memo.revisions.changed_at,
+                    self,
+                    memo.verified_at,
+                    memo.revisions.changed_at,
                 );
 
                 if memo.verified_at < revision_now {
@@ -355,7 +358,7 @@ where
                     value: value.clone(),
                 };
 
-                info!("{:?}: returning memoized value changed at {:?}", self, value.changed_at);
+                trace!("{:?}: returning memoized value changed at {:?}", self, value.changed_at);
 
                 ProbeState::UpToDate(value)
             }
@@ -387,7 +390,7 @@ where
     }
 
     pub(super) fn invalidate(&self, new_revision: Revision) -> Option {
-        tracing::debug!("Slot::invalidate(new_revision = {:?})", new_revision);
+        tracing::trace!("Slot::invalidate(new_revision = {:?})", new_revision);
         match &mut *self.state.write() {
             QueryState::Memoized(memo) => {
                 memo.revisions.untracked = true;
@@ -411,9 +414,11 @@ where
 
         db.unwind_if_cancelled();
 
-        debug!(
+        trace!(
             "maybe_changed_after({:?}) called with revision={:?}, revision_now={:?}",
-            self, revision, revision_now,
+            self,
+            revision,
+            revision_now,
         );
 
         // Do an initial probe with just the read-lock.
@@ -680,9 +685,11 @@ where
         assert!(self.verified_at != revision_now);
         let verified_at = self.verified_at;
 
-        debug!(
+        trace!(
             "verify_revisions: verified_at={:?}, revision_now={:?}, inputs={:#?}",
-            verified_at, revision_now, self.revisions.inputs
+            verified_at,
+            revision_now,
+            self.revisions.inputs
         );
 
         if self.check_durability(db.salsa_runtime()) {
@@ -708,7 +715,7 @@ where
                 let changed_input =
                     inputs.slice.iter().find(|&&input| db.maybe_changed_after(input, verified_at));
                 if let Some(input) = changed_input {
-                    debug!("validate_memoized_value: `{:?}` may have changed", input);
+                    trace!("validate_memoized_value: `{:?}` may have changed", input);
 
                     return false;
                 }
@@ -721,7 +728,7 @@ where
     /// True if this memo is known not to have changed based on its durability.
     fn check_durability(&self, runtime: &Runtime) -> bool {
         let last_changed = runtime.last_changed_revision(self.revisions.durability);
-        debug!(
+        trace!(
             "check_durability(last_changed={:?} <= verified_at={:?}) = {:?}",
             last_changed,
             self.verified_at,
diff --git a/src/tools/rust-analyzer/crates/ra-salsa/src/derived_lru/slot.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/derived_lru/slot.rs
index ff9cc4eade2cf..73a5e07aa05ab 100644
--- a/src/tools/rust-analyzer/crates/ra-salsa/src/derived_lru/slot.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/derived_lru/slot.rs
@@ -17,7 +17,7 @@ use parking_lot::{RawRwLock, RwLock};
 use std::marker::PhantomData;
 use std::ops::Deref;
 use std::sync::atomic::{AtomicBool, Ordering};
-use tracing::{debug, info};
+use tracing::trace;
 
 pub(super) struct Slot
 where
@@ -140,7 +140,7 @@ where
         // doing any `set` invocations while the query function runs.
         let revision_now = runtime.current_revision();
 
-        info!("{:?}: invoked at {:?}", self, revision_now,);
+        trace!("{:?}: invoked at {:?}", self, revision_now,);
 
         // First, do a check with a read-lock.
         loop {
@@ -168,7 +168,7 @@ where
     ) -> StampedValue {
         let runtime = db.salsa_runtime();
 
-        debug!("{:?}: read_upgrade(revision_now={:?})", self, revision_now,);
+        trace!("{:?}: read_upgrade(revision_now={:?})", self, revision_now,);
 
         // Check with an upgradable read to see if there is a value
         // already. (This permits other readers but prevents anyone
@@ -202,7 +202,7 @@ where
         // inputs and check whether they are out of date.
         if let Some(memo) = &mut old_memo {
             if let Some(value) = memo.verify_value(db.ops_database(), revision_now, &active_query) {
-                info!("{:?}: validated old memoized value", self,);
+                trace!("{:?}: validated old memoized value", self,);
 
                 db.salsa_event(Event {
                     runtime_id: runtime.id(),
@@ -230,7 +230,7 @@ where
         old_memo: Option>,
         key: &Q::Key,
     ) -> StampedValue {
-        tracing::info!("{:?}: executing query", self.database_key_index().debug(db));
+        tracing::trace!("{:?}: executing query", self.database_key_index().debug(db));
 
         db.salsa_event(Event {
             runtime_id: db.salsa_runtime().id(),
@@ -242,7 +242,7 @@ where
         let value = match Cycle::catch(|| Q::execute(db, key.clone())) {
             Ok(v) => v,
             Err(cycle) => {
-                tracing::debug!(
+                tracing::trace!(
                     "{:?}: caught cycle {:?}, have strategy {:?}",
                     self.database_key_index().debug(db),
                     cycle,
@@ -293,9 +293,10 @@ where
                 if revisions.durability >= old_memo.revisions.durability
                     && MP::memoized_value_eq(old_value, &value)
                 {
-                    debug!(
+                    trace!(
                         "read_upgrade({:?}): value is equal, back-dating to {:?}",
-                        self, old_memo.revisions.changed_at,
+                        self,
+                        old_memo.revisions.changed_at,
                     );
 
                     assert!(old_memo.revisions.changed_at <= revisions.changed_at);
@@ -313,7 +314,7 @@ where
         let memo_value =
             if self.should_memoize_value(key) { Some(new_value.value.clone()) } else { None };
 
-        debug!("read_upgrade({:?}): result.revisions = {:#?}", self, revisions,);
+        trace!("read_upgrade({:?}): result.revisions = {:#?}", self, revisions,);
 
         panic_guard.proceed(Some(Memo { value: memo_value, verified_at: revision_now, revisions }));
 
@@ -362,9 +363,11 @@ where
             }
 
             QueryState::Memoized(memo) => {
-                debug!(
+                trace!(
                     "{:?}: found memoized value, verified_at={:?}, changed_at={:?}",
-                    self, memo.verified_at, memo.revisions.changed_at,
+                    self,
+                    memo.verified_at,
+                    memo.revisions.changed_at,
                 );
 
                 if memo.verified_at < revision_now {
@@ -378,7 +381,11 @@ where
                         value: value.clone(),
                     };
 
-                    info!("{:?}: returning memoized value changed at {:?}", self, value.changed_at);
+                    trace!(
+                        "{:?}: returning memoized value changed at {:?}",
+                        self,
+                        value.changed_at
+                    );
 
                     ProbeState::UpToDate(value)
                 } else {
@@ -426,7 +433,7 @@ where
     }
 
     pub(super) fn invalidate(&self, new_revision: Revision) -> Option {
-        tracing::debug!("Slot::invalidate(new_revision = {:?})", new_revision);
+        tracing::trace!("Slot::invalidate(new_revision = {:?})", new_revision);
         match &mut *self.state.write() {
             QueryState::Memoized(memo) => {
                 memo.revisions.untracked = true;
@@ -450,9 +457,11 @@ where
 
         db.unwind_if_cancelled();
 
-        debug!(
+        trace!(
             "maybe_changed_after({:?}) called with revision={:?}, revision_now={:?}",
-            self, revision, revision_now,
+            self,
+            revision,
+            revision_now,
         );
 
         // Do an initial probe with just the read-lock.
@@ -734,9 +743,11 @@ where
         assert!(self.verified_at != revision_now);
         let verified_at = self.verified_at;
 
-        debug!(
+        trace!(
             "verify_revisions: verified_at={:?}, revision_now={:?}, inputs={:#?}",
-            verified_at, revision_now, self.revisions.inputs
+            verified_at,
+            revision_now,
+            self.revisions.inputs
         );
 
         if self.check_durability(db.salsa_runtime()) {
@@ -762,7 +773,7 @@ where
                 let changed_input =
                     inputs.slice.iter().find(|&&input| db.maybe_changed_after(input, verified_at));
                 if let Some(input) = changed_input {
-                    debug!("validate_memoized_value: `{:?}` may have changed", input);
+                    trace!("validate_memoized_value: `{:?}` may have changed", input);
 
                     return false;
                 }
@@ -775,7 +786,7 @@ where
     /// True if this memo is known not to have changed based on its durability.
     fn check_durability(&self, runtime: &Runtime) -> bool {
         let last_changed = runtime.last_changed_revision(self.revisions.durability);
-        debug!(
+        trace!(
             "check_durability(last_changed={:?} <= verified_at={:?}) = {:?}",
             last_changed,
             self.verified_at,
diff --git a/src/tools/rust-analyzer/crates/ra-salsa/src/input.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/input.rs
index f04f48e3bab85..4992a0c7271cc 100644
--- a/src/tools/rust-analyzer/crates/ra-salsa/src/input.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/input.rs
@@ -14,7 +14,7 @@ use crate::{DatabaseKeyIndex, QueryDb};
 use indexmap::map::Entry;
 use parking_lot::RwLock;
 use std::iter;
-use tracing::debug;
+use tracing::trace;
 
 /// Input queries store the result plus a list of the other queries
 /// that they invoked. This means we can avoid recomputing them when
@@ -73,11 +73,11 @@ where
             return true;
         };
 
-        debug!("maybe_changed_after(slot={:?}, revision={:?})", Q::default(), revision,);
+        trace!("maybe_changed_after(slot={:?}, revision={:?})", Q::default(), revision,);
 
         let changed_at = slot.stamped_value.read().changed_at;
 
-        debug!("maybe_changed_after: changed_at = {:?}", changed_at);
+        trace!("maybe_changed_after: changed_at = {:?}", changed_at);
 
         changed_at > revision
     }
@@ -140,7 +140,7 @@ where
     Q: Query,
 {
     fn set(&self, runtime: &mut Runtime, key: &Q::Key, value: Q::Value, durability: Durability) {
-        tracing::debug!("{:?}({:?}) = {:?} ({:?})", Q::default(), key, value, durability);
+        tracing::trace!("{:?}({:?}) = {:?} ({:?})", Q::default(), key, value, durability);
 
         // The value is changing, so we need a new revision (*). We also
         // need to update the 'last changed' revision by invoking
@@ -234,14 +234,14 @@ where
     ) -> bool {
         debug_assert!(revision < db.salsa_runtime().current_revision());
 
-        debug!("maybe_changed_after(slot={:?}, revision={:?})", Q::default(), revision,);
+        trace!("maybe_changed_after(slot={:?}, revision={:?})", Q::default(), revision,);
 
         let Some(value) = &*self.slot.stamped_value.read() else {
             return true;
         };
         let changed_at = value.changed_at;
 
-        debug!("maybe_changed_after: changed_at = {:?}", changed_at);
+        trace!("maybe_changed_after: changed_at = {:?}", changed_at);
 
         changed_at > revision
     }
@@ -298,7 +298,7 @@ where
     Q: Query,
 {
     fn set(&self, runtime: &mut Runtime, (): &Q::Key, value: Q::Value, durability: Durability) {
-        tracing::debug!("{:?} = {:?} ({:?})", Q::default(), value, durability);
+        tracing::trace!("{:?} = {:?} ({:?})", Q::default(), value, durability);
 
         // The value is changing, so we need a new revision (*). We also
         // need to update the 'last changed' revision by invoking
diff --git a/src/tools/rust-analyzer/crates/ra-salsa/src/lib.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/lib.rs
index 8530521d9157b..843b6d31f0c33 100644
--- a/src/tools/rust-analyzer/crates/ra-salsa/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/lib.rs
@@ -79,7 +79,7 @@ pub trait Database: plumbing::DatabaseOps {
 
         let current_revision = runtime.current_revision();
         let pending_revision = runtime.pending_revision();
-        tracing::debug!(
+        tracing::trace!(
             "unwind_if_cancelled: current_revision={:?}, pending_revision={:?}",
             current_revision,
             pending_revision
@@ -684,7 +684,7 @@ impl Cycle {
     }
 
     pub(crate) fn throw(self) -> ! {
-        tracing::debug!("throwing cycle {:?}", self);
+        tracing::trace!("throwing cycle {:?}", self);
         std::panic::resume_unwind(Box::new(self))
     }
 
diff --git a/src/tools/rust-analyzer/crates/ra-salsa/src/lru.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/lru.rs
index a6f96beeab11a..7fbd42f92627a 100644
--- a/src/tools/rust-analyzer/crates/ra-salsa/src/lru.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/lru.rs
@@ -103,11 +103,11 @@ where
 
     /// Records that `node` was used. This may displace an old node (if the LRU limits are
     pub(crate) fn record_use(&self, node: &Arc) -> Option> {
-        tracing::debug!("record_use(node={:?})", node);
+        tracing::trace!("record_use(node={:?})", node);
 
         // Load green zone length and check if the LRU cache is even enabled.
         let green_zone = self.green_zone.load(Ordering::Acquire);
-        tracing::debug!("record_use: green_zone={}", green_zone);
+        tracing::trace!("record_use: green_zone={}", green_zone);
         if green_zone == 0 {
             return None;
         }
@@ -115,7 +115,7 @@ where
         // Find current index of list (if any) and the current length
         // of our green zone.
         let index = node.lru_index().load();
-        tracing::debug!("record_use: index={}", index);
+        tracing::trace!("record_use: index={}", index);
 
         // Already a member of the list, and in the green zone -- nothing to do!
         if index < green_zone {
@@ -162,9 +162,9 @@ where
         let entries =
             std::mem::replace(&mut self.entries, Vec::with_capacity(self.end_red_zone as usize));
 
-        tracing::debug!("green_zone = {:?}", self.green_zone());
-        tracing::debug!("yellow_zone = {:?}", self.yellow_zone());
-        tracing::debug!("red_zone = {:?}", self.red_zone());
+        tracing::trace!("green_zone = {:?}", self.green_zone());
+        tracing::trace!("yellow_zone = {:?}", self.yellow_zone());
+        tracing::trace!("red_zone = {:?}", self.red_zone());
 
         // We expect to resize when the LRU cache is basically empty.
         // So just forget all the old LRU indices to start.
@@ -180,7 +180,7 @@ where
     /// list may displace an old member of the red zone, in which case
     /// that is returned.
     fn record_use(&mut self, node: &Arc) -> Option> {
-        tracing::debug!("record_use(node={:?})", node);
+        tracing::trace!("record_use(node={:?})", node);
 
         // NB: When this is invoked, we have typically already loaded
         // the LRU index (to check if it is in green zone). But that
@@ -212,7 +212,7 @@ where
         if len < self.end_red_zone {
             self.entries.push(node.clone());
             node.lru_index().store(len);
-            tracing::debug!("inserted node {:?} at {}", node, len);
+            tracing::trace!("inserted node {:?} at {}", node, len);
             return self.record_use(node);
         }
 
@@ -220,7 +220,7 @@ where
         // zone and then promoting.
         let victim_index = self.pick_index(self.red_zone());
         let victim_node = std::mem::replace(&mut self.entries[victim_index as usize], node.clone());
-        tracing::debug!("evicting red node {:?} from {}", victim_node, victim_index);
+        tracing::trace!("evicting red node {:?} from {}", victim_node, victim_index);
         victim_node.lru_index().clear();
         self.promote_red_to_green(node, victim_index);
         Some(victim_node)
@@ -241,7 +241,7 @@ where
         // going to invoke `self.promote_yellow` next, and it will get
         // updated then.
         let yellow_index = self.pick_index(self.yellow_zone());
-        tracing::debug!(
+        tracing::trace!(
             "demoting yellow node {:?} from {} to red at {}",
             self.entries[yellow_index as usize],
             yellow_index,
@@ -265,7 +265,7 @@ where
 
         // Pick a yellow at random and switch places with it.
         let green_index = self.pick_index(self.green_zone());
-        tracing::debug!(
+        tracing::trace!(
             "demoting green node {:?} from {} to yellow at {}",
             self.entries[green_index as usize],
             green_index,
@@ -275,7 +275,7 @@ where
         self.entries[yellow_index as usize].lru_index().store(yellow_index);
         node.lru_index().store(green_index);
 
-        tracing::debug!("promoted {:?} to green index {}", node, green_index);
+        tracing::trace!("promoted {:?} to green index {}", node, green_index);
     }
 
     fn pick_index(&mut self, zone: std::ops::Range) -> u16 {
diff --git a/src/tools/rust-analyzer/crates/ra-salsa/src/runtime.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/runtime.rs
index 5fe5f4b46d3e8..cb16ba0044dfd 100644
--- a/src/tools/rust-analyzer/crates/ra-salsa/src/runtime.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/runtime.rs
@@ -9,7 +9,7 @@ use parking_lot::{Mutex, RwLock};
 use std::hash::Hash;
 use std::panic::panic_any;
 use std::sync::atomic::{AtomicU32, Ordering};
-use tracing::debug;
+use tracing::trace;
 use triomphe::{Arc, ThinArc};
 
 mod dependency_graph;
@@ -177,7 +177,7 @@ impl Runtime {
     where
         F: FnOnce(Revision) -> Option,
     {
-        tracing::debug!("increment_revision()");
+        tracing::trace!("increment_revision()");
 
         if !self.permits_increment() {
             panic!("increment_revision invoked during a query computation");
@@ -196,7 +196,7 @@ impl Runtime {
 
         let new_revision = current_revision.next();
 
-        debug!("increment_revision: incremented to {:?}", new_revision);
+        trace!("increment_revision: incremented to {:?}", new_revision);
 
         if let Some(d) = op(new_revision) {
             for rev in &self.shared_state.revisions[1..=d.index()] {
@@ -267,7 +267,7 @@ impl Runtime {
         database_key_index: DatabaseKeyIndex,
         to_id: RuntimeId,
     ) {
-        debug!("unblock_cycle_and_maybe_throw(database_key={:?})", database_key_index);
+        trace!("unblock_cycle_and_maybe_throw(database_key={:?})", database_key_index);
 
         let mut from_stack = self.local_state.take_query_stack();
         let from_id = self.id();
@@ -305,7 +305,7 @@ impl Runtime {
 
             Cycle::new(Arc::new(v))
         };
-        debug!("cycle {:?}, cycle_query {:#?}", cycle.debug(db), cycle_query,);
+        trace!("cycle {:?}, cycle_query {:#?}", cycle.debug(db), cycle_query,);
 
         // We can remove the cycle participants from the list of dependencies;
         // they are a strongly connected component (SCC) and we only care about
@@ -323,7 +323,7 @@ impl Runtime {
                     CycleRecoveryStrategy::Fallback => false,
                 })
                 .for_each(|aq| {
-                    debug!("marking {:?} for fallback", aq.database_key_index.debug(db));
+                    trace!("marking {:?} for fallback", aq.database_key_index.debug(db));
                     aq.take_inputs_from(&cycle_query);
                     assert!(aq.cycle.is_none());
                     aq.cycle = Some(cycle.clone());
diff --git a/src/tools/rust-analyzer/crates/ra-salsa/src/runtime/local_state.rs b/src/tools/rust-analyzer/crates/ra-salsa/src/runtime/local_state.rs
index 738696718868f..4ab4bad0cc508 100644
--- a/src/tools/rust-analyzer/crates/ra-salsa/src/runtime/local_state.rs
+++ b/src/tools/rust-analyzer/crates/ra-salsa/src/runtime/local_state.rs
@@ -1,4 +1,4 @@
-use tracing::debug;
+use tracing::trace;
 use triomphe::ThinArc;
 
 use crate::durability::Durability;
@@ -78,7 +78,7 @@ impl LocalState {
         durability: Durability,
         changed_at: Revision,
     ) {
-        debug!(
+        trace!(
             "report_query_read_and_unwind_if_cycle_resulted(input={:?}, durability={:?}, changed_at={:?})",
             input, durability, changed_at
         );
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
index fa9ff6b56df0e..b8ce2b7430b98 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
@@ -37,7 +37,7 @@ rustc-hash.workspace = true
 serde_json = { workspace = true, features = ["preserve_order"] }
 serde.workspace = true
 serde_derive.workspace = true
-tenthash = "0.4.0"
+tenthash = "1.0.0"
 num_cpus = "1.15.0"
 mimalloc = { version = "0.1.30", default-features = false, optional = true }
 lsp-server.workspace = true
@@ -76,7 +76,10 @@ vfs.workspace = true
 paths.workspace = true
 
 [target.'cfg(windows)'.dependencies]
-windows-sys = { version = "0.52", features = ["Win32_System_Threading"] }
+windows-sys = { version = "0.59", features = [
+  "Win32_System_Diagnostics_Debug",
+  "Win32_System_Threading",
+] }
 
 [target.'cfg(not(target_env = "msvc"))'.dependencies]
 jemallocator = { version = "0.5.0", package = "tikv-jemallocator", optional = true }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/build.rs b/src/tools/rust-analyzer/crates/rust-analyzer/build.rs
index 72b741de00ee7..0fd381d61221a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/build.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/build.rs
@@ -32,6 +32,7 @@ fn set_rerun() {
 }
 
 fn set_commit_info() {
+    #[allow(clippy::disallowed_methods)]
     let output = match Command::new("git")
         .arg("log")
         .arg("-1")
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
index a753621eca8ee..1a9cdef256d28 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
@@ -44,11 +44,7 @@ fn actual_main() -> anyhow::Result {
 
     #[cfg(debug_assertions)]
     if flags.wait_dbg || env::var("RA_WAIT_DBG").is_ok() {
-        #[allow(unused_mut)]
-        let mut d = 4;
-        while d == 4 {
-            d = 4;
-        }
+        wait_for_debugger();
     }
 
     if let Err(e) = setup_logging(flags.log_file.clone()) {
@@ -96,6 +92,28 @@ fn actual_main() -> anyhow::Result {
     Ok(ExitCode::SUCCESS)
 }
 
+#[cfg(debug_assertions)]
+fn wait_for_debugger() {
+    #[cfg(target_os = "windows")]
+    {
+        use windows_sys::Win32::System::Diagnostics::Debug::IsDebuggerPresent;
+        // SAFETY: WinAPI generated code that is defensively marked `unsafe` but
+        // in practice can not be used in an unsafe way.
+        while unsafe { IsDebuggerPresent() } == 0 {
+            std::thread::sleep(std::time::Duration::from_millis(100));
+        }
+    }
+    #[cfg(not(target_os = "windows"))]
+    {
+        #[allow(unused_mut)]
+        let mut d = 4;
+        while d == 4 {
+            d = 4;
+            std::thread::sleep(std::time::Duration::from_millis(100));
+        }
+    }
+}
+
 fn setup_logging(log_file_flag: Option) -> anyhow::Result<()> {
     if cfg!(windows) {
         // This is required so that windows finds our pdb that is placed right beside the exe.
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/rustc_wrapper.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/rustc_wrapper.rs
index 684b3f52afc86..b9fcd2e187094 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/rustc_wrapper.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/rustc_wrapper.rs
@@ -46,6 +46,7 @@ fn run_rustc_skipping_cargo_checking(
 }
 
 fn run_rustc(rustc_executable: OsString, args: Vec) -> io::Result {
+    #[allow(clippy::disallowed_methods)]
     let mut child = Command::new(rustc_executable)
         .args(args)
         .stdin(Stdio::inherit())
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
index 66cd2e424e385..4fc6180920f5f 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -13,7 +13,7 @@ use hir::{
     ModuleDef, Name,
 };
 use hir_def::{
-    body::BodySourceMap,
+    expr_store::BodySourceMap,
     hir::{ExprId, PatId},
     SyntheticSyntax,
 };
@@ -66,10 +66,6 @@ impl flags::AnalysisStats {
                 true => None,
                 false => Some(RustLibSource::Discover),
             },
-            sysroot_query_metadata: match self.no_query_sysroot_metadata {
-                true => project_model::SysrootQueryMetadata::None,
-                false => project_model::SysrootQueryMetadata::CargoMetadata,
-            },
             all_targets: true,
             set_test: !self.no_test,
             cfg_overrides: CfgOverrides {
@@ -469,6 +465,7 @@ impl flags::AnalysisStats {
                                 prefer_no_std: false,
                                 prefer_prelude: true,
                                 prefer_absolute: false,
+                                allow_unstable: true,
                             },
                             Edition::LATEST,
                         )
@@ -1055,6 +1052,7 @@ impl flags::AnalysisStats {
                 &InlayHintsConfig {
                     render_colons: false,
                     type_hints: true,
+                    sized_bound: false,
                     discriminant_hints: ide::DiscriminantHints::Always,
                     parameter_hints: true,
                     generic_parameter_hints: ide::GenericParameterHints {
@@ -1074,6 +1072,7 @@ impl flags::AnalysisStats {
                     param_names_for_lifetime_elision_hints: true,
                     hide_named_constructor_hints: false,
                     hide_closure_initialization_hints: false,
+                    hide_closure_parameter_hints: false,
                     closure_style: hir::ClosureStyle::ImplFn,
                     max_length: Some(25),
                     closing_brace_hints_min_lines: Some(20),
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
index 28f25975d64aa..6a3ceb640b91a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -4,7 +4,7 @@
 use project_model::{CargoConfig, RustLibSource};
 use rustc_hash::FxHashSet;
 
-use hir::{db::HirDatabase, Crate, HirFileIdExt, Module};
+use hir::{db::HirDatabase, sym, Crate, HirFileIdExt, Module};
 use ide::{AnalysisHost, AssistResolveStrategy, Diagnostic, DiagnosticsConfig, Severity};
 use ide_db::{base_db::SourceRootDatabase, LineIndexDatabase};
 use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice};
@@ -60,7 +60,7 @@ impl flags::Diagnostics {
             let file_id = module.definition_source_file_id(db).original_file(db);
             if !visited_files.contains(&file_id) {
                 let crate_name =
-                    module.krate().display_name(db).as_deref().unwrap_or("unknown").to_owned();
+                    module.krate().display_name(db).as_deref().unwrap_or(&sym::unknown).to_owned();
                 println!(
                     "processing crate: {crate_name}, module: {}",
                     _vfs.file_path(file_id.into())
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
index 920a2a37efb69..ff24602144a9d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
@@ -71,9 +71,6 @@ xflags::xflags! {
             optional --with-deps
             /// Don't load sysroot crates (`std`, `core` & friends).
             optional --no-sysroot
-            /// Don't run cargo metadata on the sysroot to analyze its third-party dependencies.
-            /// Requires --no-sysroot to not be set.
-            optional --no-query-sysroot-metadata
             /// Don't set #[cfg(test)].
             optional --no-test
 
@@ -238,7 +235,6 @@ pub struct AnalysisStats {
     pub only: Option,
     pub with_deps: bool,
     pub no_sysroot: bool,
-    pub no_query_sysroot_metadata: bool,
     pub no_test: bool,
     pub disable_build_scripts: bool,
     pub disable_proc_macros: bool,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
index 33c4f31fbee4d..eb5c44418b72d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
@@ -4,8 +4,9 @@ use std::env;
 use std::time::Instant;
 
 use ide::{
-    Analysis, AnalysisHost, FileId, FileRange, MonikerKind, PackageInformation, RootDatabase,
-    StaticIndex, StaticIndexedFile, TokenId, TokenStaticData, VendoredLibrariesConfig,
+    Analysis, AnalysisHost, FileId, FileRange, MonikerKind, MonikerResult, PackageInformation,
+    RootDatabase, StaticIndex, StaticIndexedFile, TokenId, TokenStaticData,
+    VendoredLibrariesConfig,
 };
 use ide_db::{line_index::WideEncoding, LineIndexDatabase};
 use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice};
@@ -167,7 +168,7 @@ impl LsifManager<'_, '_> {
                 out_v: result_set_id.into(),
             }));
         }
-        if let Some(moniker) = token.moniker {
+        if let Some(MonikerResult::Moniker(moniker)) = token.moniker {
             let package_id = self.get_package_id(moniker.package_information);
             let moniker_id = self.add_vertex(lsif::Vertex::Moniker(lsp_types::Moniker {
                 scheme: "rust-analyzer".to_owned(),
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
index 6483afc85b21d..199f61e70f027 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
@@ -10,10 +10,10 @@ use ide::{AnalysisHost, DiagnosticCode, DiagnosticsConfig};
 use itertools::Either;
 use paths::Utf8PathBuf;
 use profile::StopWatch;
-use project_model::target_data_layout::RustcDataLayoutConfig;
+use project_model::toolchain_info::{target_data_layout, QueryConfig};
 use project_model::{
-    target_data_layout, CargoConfig, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind,
-    RustLibSource, Sysroot, SysrootQueryMetadata,
+    CargoConfig, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, RustLibSource, Sysroot,
+    SysrootSourceWorkspaceConfig,
 };
 
 use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice};
@@ -74,13 +74,10 @@ impl Tester {
             ..Default::default()
         };
 
-        let sysroot = Sysroot::discover(
-            tmp_file.parent().unwrap(),
-            &cargo_config.extra_env,
-            SysrootQueryMetadata::CargoMetadata,
-        );
+        let mut sysroot = Sysroot::discover(tmp_file.parent().unwrap(), &cargo_config.extra_env);
+        sysroot.load_workspace(&SysrootSourceWorkspaceConfig::default_cargo());
         let data_layout = target_data_layout::get(
-            RustcDataLayoutConfig::Rustc(&sysroot),
+            QueryConfig::Rustc(&sysroot, tmp_file.parent().unwrap().as_ref()),
             None,
             &cargo_config.extra_env,
         );
@@ -89,7 +86,6 @@ impl Tester {
             kind: ProjectWorkspaceKind::DetachedFile {
                 file: ManifestPath::try_from(tmp_file).unwrap(),
                 cargo: None,
-                cargo_config_extra_env: Default::default(),
                 set_test: true,
             },
             sysroot,
@@ -97,6 +93,7 @@ impl Tester {
             toolchain: None,
             target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
             cfg_overrides: Default::default(),
+            extra_includes: vec![],
         };
         let load_cargo_config = LoadCargoConfig {
             load_out_dirs_from_check: false,
@@ -302,7 +299,7 @@ impl flags::RustcTests {
                     continue;
                 }
             }
-            if p.extension().map_or(true, |x| x != "rs") {
+            if p.extension().is_none_or(|x| x != "rs") {
                 continue;
             }
             if let Err(e) = std::panic::catch_unwind({
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
index ff009e69547a8..fe75872105aec 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
@@ -3,14 +3,16 @@
 use std::{path::PathBuf, time::Instant};
 
 use ide::{
-    AnalysisHost, LineCol, MonikerDescriptorKind, MonikerResult, StaticIndex, StaticIndexedFile,
-    SymbolInformationKind, TextRange, TokenId, VendoredLibrariesConfig,
+    AnalysisHost, LineCol, Moniker, MonikerDescriptorKind, MonikerIdentifier, MonikerResult,
+    RootDatabase, StaticIndex, StaticIndexedFile, SymbolInformationKind, TextRange, TokenId,
+    TokenStaticData, VendoredLibrariesConfig,
 };
 use ide_db::LineIndexDatabase;
 use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice};
 use rustc_hash::{FxHashMap, FxHashSet};
-use scip::types as scip_types;
+use scip::types::{self as scip_types, SymbolInformation};
 use tracing::error;
+use vfs::FileId;
 
 use crate::{
     cli::flags,
@@ -83,104 +85,112 @@ impl flags::Scip {
             text_document_encoding: scip_types::TextEncoding::UTF8.into(),
             special_fields: Default::default(),
         };
-        let mut documents = Vec::new();
-
-        let mut symbols_emitted: FxHashSet = FxHashSet::default();
-        let mut tokens_to_symbol: FxHashMap = FxHashMap::default();
-        let mut tokens_to_enclosing_symbol: FxHashMap> =
-            FxHashMap::default();
 
-        for StaticIndexedFile { file_id, tokens, .. } in si.files {
-            let mut local_count = 0;
-            let mut new_local_symbol = || {
-                let new_symbol = scip::types::Symbol::new_local(local_count);
-                local_count += 1;
+        let mut documents = Vec::new();
 
-                new_symbol
+        // All TokenIds where an Occurrence has been emitted that references a symbol.
+        let mut token_ids_referenced: FxHashSet = FxHashSet::default();
+        // All TokenIds where the SymbolInformation has been written to the document.
+        let mut token_ids_emitted: FxHashSet = FxHashSet::default();
+        // All FileIds emitted as documents.
+        let mut file_ids_emitted: FxHashSet = FxHashSet::default();
+
+        // All non-local symbols encountered, for detecting duplicate symbol errors.
+        let mut nonlocal_symbols_emitted: FxHashSet = FxHashSet::default();
+        // List of (source_location, symbol) for duplicate symbol errors to report.
+        let mut duplicate_symbol_errors: Vec<(String, String)> = Vec::new();
+        // This is called after definitions have been deduplicated by token_ids_emitted. The purpose
+        // is to detect reuse of symbol names because this causes ambiguity about their meaning.
+        let mut record_error_if_symbol_already_used =
+            |symbol: String,
+             is_inherent_impl: bool,
+             relative_path: &str,
+             line_index: &LineIndex,
+             text_range: TextRange| {
+                let is_local = symbol.starts_with("local ");
+                if !is_local && !nonlocal_symbols_emitted.insert(symbol.clone()) {
+                    if is_inherent_impl {
+                        // FIXME: See #18772. Duplicate SymbolInformation for inherent impls is
+                        // omitted. It would be preferable to emit them with numbers with
+                        // disambiguation, but this is more complex to implement.
+                        false
+                    } else {
+                        let source_location =
+                            text_range_to_string(relative_path, line_index, text_range);
+                        duplicate_symbol_errors.push((source_location, symbol));
+                        // Keep duplicate SymbolInformation. This behavior is preferred over
+                        // omitting so that the issue might be visible within downstream tools.
+                        true
+                    }
+                } else {
+                    true
+                }
             };
 
-            let relative_path = match get_relative_filepath(&vfs, &root, file_id) {
-                Some(relative_path) => relative_path,
-                None => continue,
-            };
+        // Generates symbols from token monikers.
+        let mut symbol_generator = SymbolGenerator::new();
 
-            let line_index = LineIndex {
-                index: db.line_index(file_id),
-                encoding: PositionEncoding::Utf8,
-                endings: LineEndings::Unix,
-            };
+        for StaticIndexedFile { file_id, tokens, .. } in si.files {
+            symbol_generator.clear_document_local_state();
+
+            let Some(relative_path) = get_relative_filepath(&vfs, &root, file_id) else { continue };
+            let line_index = get_line_index(db, file_id);
 
             let mut occurrences = Vec::new();
             let mut symbols = Vec::new();
 
-            tokens.into_iter().for_each(|(text_range, id)| {
+            for (text_range, id) in tokens.into_iter() {
                 let token = si.tokens.get(id).unwrap();
 
-                let range = text_range_to_scip_range(&line_index, text_range);
-                let symbol = tokens_to_symbol
-                    .entry(id)
-                    .or_insert_with(|| {
-                        let symbol = token
-                            .moniker
-                            .as_ref()
-                            .map(moniker_to_symbol)
-                            .unwrap_or_else(&mut new_local_symbol);
-                        scip::symbol::format_symbol(symbol)
-                    })
-                    .clone();
-                let enclosing_symbol = tokens_to_enclosing_symbol
-                    .entry(id)
-                    .or_insert_with(|| {
-                        token
-                            .enclosing_moniker
-                            .as_ref()
-                            .map(moniker_to_symbol)
-                            .map(scip::symbol::format_symbol)
-                    })
-                    .clone();
-
-                let mut symbol_roles = Default::default();
-
-                if let Some(def) = token.definition {
-                    // if the range of the def and the range of the token are the same, this must be the definition.
-                    // they also must be in the same file. See https://github.com/rust-lang/rust-analyzer/pull/17988
-                    if def.file_id == file_id && def.range == text_range {
-                        symbol_roles |= scip_types::SymbolRole::Definition as i32;
+                let Some(TokenSymbols { symbol, enclosing_symbol, is_inherent_impl }) =
+                    symbol_generator.token_symbols(id, token)
+                else {
+                    // token did not have a moniker, so there is no reasonable occurrence to emit
+                    // see ide::moniker::def_to_moniker
+                    continue;
+                };
+
+                let is_defined_in_this_document = match token.definition {
+                    Some(def) => def.file_id == file_id,
+                    _ => false,
+                };
+                if is_defined_in_this_document {
+                    if token_ids_emitted.insert(id) {
+                        // token_ids_emitted does deduplication. This checks that this results
+                        // in unique emitted symbols, as otherwise references are ambiguous.
+                        let should_emit = record_error_if_symbol_already_used(
+                            symbol.clone(),
+                            is_inherent_impl,
+                            relative_path.as_str(),
+                            &line_index,
+                            text_range,
+                        );
+                        if should_emit {
+                            symbols.push(compute_symbol_info(
+                                symbol.clone(),
+                                enclosing_symbol,
+                                token,
+                            ));
+                        }
                     }
+                } else {
+                    token_ids_referenced.insert(id);
+                }
 
-                    if symbols_emitted.insert(id) {
-                        let documentation = match &token.documentation {
-                            Some(doc) => vec![doc.as_str().to_owned()],
-                            None => vec![],
-                        };
-
-                        let position_encoding =
-                            scip_types::PositionEncoding::UTF8CodeUnitOffsetFromLineStart.into();
-                        let signature_documentation =
-                            token.signature.clone().map(|text| scip_types::Document {
-                                relative_path: relative_path.clone(),
-                                language: "rust".to_owned(),
-                                text,
-                                position_encoding,
-                                ..Default::default()
-                            });
-                        let symbol_info = scip_types::SymbolInformation {
-                            symbol: symbol.clone(),
-                            documentation,
-                            relationships: Vec::new(),
-                            special_fields: Default::default(),
-                            kind: symbol_kind(token.kind).into(),
-                            display_name: token.display_name.clone().unwrap_or_default(),
-                            signature_documentation: signature_documentation.into(),
-                            enclosing_symbol: enclosing_symbol.unwrap_or_default(),
-                        };
-
-                        symbols.push(symbol_info)
-                    }
+                // If the range of the def and the range of the token are the same, this must be the definition.
+                // they also must be in the same file. See https://github.com/rust-lang/rust-analyzer/pull/17988
+                let is_definition = match token.definition {
+                    Some(def) => def.file_id == file_id && def.range == text_range,
+                    _ => false,
+                };
+
+                let mut symbol_roles = Default::default();
+                if is_definition {
+                    symbol_roles |= scip_types::SymbolRole::Definition as i32;
                 }
 
                 occurrences.push(scip_types::Occurrence {
-                    range,
+                    range: text_range_to_scip_range(&line_index, text_range),
                     symbol,
                     symbol_roles,
                     override_documentation: Vec::new(),
@@ -189,7 +199,7 @@ impl flags::Scip {
                     special_fields: Default::default(),
                     enclosing_range: Vec::new(),
                 });
-            });
+            }
 
             if occurrences.is_empty() {
                 continue;
@@ -206,15 +216,63 @@ impl flags::Scip {
                 position_encoding,
                 special_fields: Default::default(),
             });
+            if !file_ids_emitted.insert(file_id) {
+                panic!("Invariant violation: file emitted multiple times.");
+            }
+        }
+
+        // Collect all symbols referenced by the files but not defined within them.
+        let mut external_symbols = Vec::new();
+        for id in token_ids_referenced.difference(&token_ids_emitted) {
+            let id = *id;
+            let token = si.tokens.get(id).unwrap();
+
+            let Some(definition) = token.definition else {
+                break;
+            };
+
+            let file_id = definition.file_id;
+            let Some(relative_path) = get_relative_filepath(&vfs, &root, file_id) else { continue };
+            let line_index = get_line_index(db, file_id);
+            let text_range = definition.range;
+            if file_ids_emitted.contains(&file_id) {
+                tracing::error!(
+                    "Bug: definition at {} should have been in an SCIP document but was not.",
+                    text_range_to_string(relative_path.as_str(), &line_index, text_range)
+                );
+                continue;
+            }
+
+            let TokenSymbols { symbol, enclosing_symbol, .. } = symbol_generator
+                .token_symbols(id, token)
+                .expect("To have been referenced, the symbol must be in the cache.");
+
+            record_error_if_symbol_already_used(
+                symbol.clone(),
+                false,
+                relative_path.as_str(),
+                &line_index,
+                text_range,
+            );
+            external_symbols.push(compute_symbol_info(symbol.clone(), enclosing_symbol, token));
         }
 
         let index = scip_types::Index {
             metadata: Some(metadata).into(),
             documents,
-            external_symbols: Vec::new(),
+            external_symbols,
             special_fields: Default::default(),
         };
 
+        if !duplicate_symbol_errors.is_empty() {
+            eprintln!("{}", DUPLICATE_SYMBOLS_MESSAGE);
+            for (source_location, symbol) in duplicate_symbol_errors {
+                eprintln!("{}", source_location);
+                eprintln!("  Duplicate symbol: {}", symbol);
+                eprintln!();
+            }
+        }
+
         let out_path = self.output.unwrap_or_else(|| PathBuf::from(r"index.scip"));
         scip::write_message_to_file(out_path, index)
             .map_err(|err| anyhow::format_err!("Failed to write scip to file: {}", err))?;
@@ -224,6 +282,53 @@ impl flags::Scip {
     }
 }
 
+// FIXME: Known buggy cases are described here.
+const DUPLICATE_SYMBOLS_MESSAGE: &str = "
+Encountered duplicate scip symbols, indicating an internal rust-analyzer bug. These duplicates are
+included in the output, but this causes information lookup to be ambiguous and so information about
+these symbols presented by downstream tools may be incorrect.
+
+Known rust-analyzer bugs that can cause this:
+
+  * Definitions in crate example binaries which have the same symbol as definitions in the library
+    or some other example.
+
+  * Struct/enum/const/static/impl definitions nested in a function do not mention the function name.
+    See #18771.
+
+Duplicate symbols encountered:
+";
+
+fn compute_symbol_info(
+    symbol: String,
+    enclosing_symbol: Option,
+    token: &TokenStaticData,
+) -> SymbolInformation {
+    let documentation = match &token.documentation {
+        Some(doc) => vec![doc.as_str().to_owned()],
+        None => vec![],
+    };
+
+    let position_encoding = scip_types::PositionEncoding::UTF8CodeUnitOffsetFromLineStart.into();
+    let signature_documentation = token.signature.clone().map(|text| scip_types::Document {
+        relative_path: "".to_owned(),
+        language: "rust".to_owned(),
+        text,
+        position_encoding,
+        ..Default::default()
+    });
+    scip_types::SymbolInformation {
+        symbol,
+        documentation,
+        relationships: Vec::new(),
+        special_fields: Default::default(),
+        kind: symbol_kind(token.kind).into(),
+        display_name: token.display_name.clone().unwrap_or_default(),
+        signature_documentation: signature_documentation.into(),
+        enclosing_symbol: enclosing_symbol.unwrap_or_default(),
+    }
+}
+
 fn get_relative_filepath(
     vfs: &vfs::Vfs,
     rootpath: &vfs::AbsPathBuf,
@@ -232,6 +337,14 @@ fn get_relative_filepath(
     Some(vfs.file_path(file_id).as_path()?.strip_prefix(rootpath)?.as_str().to_owned())
 }
 
+fn get_line_index(db: &RootDatabase, file_id: FileId) -> LineIndex {
+    LineIndex {
+        index: db.line_index(file_id),
+        encoding: PositionEncoding::Utf8,
+        endings: LineEndings::Unix,
+    }
+}
+
 // SCIP Ranges have a (very large) optimization that ranges if they are on the same line
 // only encode as a vector of [start_line, start_col, end_col].
 //
@@ -247,6 +360,13 @@ fn text_range_to_scip_range(line_index: &LineIndex, range: TextRange) -> Vec String {
+    let LineCol { line: start_line, col: start_col } = line_index.index.line_col(range.start());
+    let LineCol { line: end_line, col: end_col } = line_index.index.line_col(range.end());
+
+    format!("{relative_path}:{start_line}:{start_col}-{end_line}:{end_col}")
+}
+
 fn new_descriptor_str(
     name: &str,
     suffix: scip_types::descriptor::Suffix,
@@ -259,14 +379,6 @@ fn new_descriptor_str(
     }
 }
 
-fn new_descriptor(name: &str, suffix: scip_types::descriptor::Suffix) -> scip_types::Descriptor {
-    if name.contains('\'') {
-        new_descriptor_str(&format!("`{name}`"), suffix)
-    } else {
-        new_descriptor_str(name, suffix)
-    }
-}
-
 fn symbol_kind(kind: SymbolInformationKind) -> scip_types::symbol_information::Kind {
     use scip_types::symbol_information::Kind as ScipKind;
     match kind {
@@ -295,17 +407,91 @@ fn symbol_kind(kind: SymbolInformationKind) -> scip_types::symbol_information::K
     }
 }
 
-fn moniker_to_symbol(moniker: &MonikerResult) -> scip_types::Symbol {
-    use scip_types::descriptor::Suffix::*;
+#[derive(Clone)]
+struct TokenSymbols {
+    symbol: String,
+    /// Definition that contains this one. Only set when `symbol` is local.
+    enclosing_symbol: Option,
+    /// True if this symbol is for an inherent impl. This is used to only emit `SymbolInformation`
+    /// for a struct's first inherent impl, since their symbol names are not disambiguated.
+    is_inherent_impl: bool,
+}
+
+struct SymbolGenerator {
+    token_to_symbols: FxHashMap>,
+    local_count: usize,
+}
+
+impl SymbolGenerator {
+    fn new() -> Self {
+        SymbolGenerator { token_to_symbols: FxHashMap::default(), local_count: 0 }
+    }
+
+    fn clear_document_local_state(&mut self) {
+        self.local_count = 0;
+    }
+
+    fn token_symbols(&mut self, id: TokenId, token: &TokenStaticData) -> Option {
+        let mut local_count = self.local_count;
+        let token_symbols = self
+            .token_to_symbols
+            .entry(id)
+            .or_insert_with(|| {
+                Some(match token.moniker.as_ref()? {
+                    MonikerResult::Moniker(moniker) => TokenSymbols {
+                        symbol: scip::symbol::format_symbol(moniker_to_symbol(moniker)),
+                        enclosing_symbol: None,
+                        is_inherent_impl: match &moniker.identifier.description[..] {
+                            // inherent impls are represented as impl#[SelfType]
+                            [.., descriptor, _] => {
+                                descriptor.desc == MonikerDescriptorKind::Type
+                                    && descriptor.name == "impl"
+                            }
+                            _ => false,
+                        },
+                    },
+                    MonikerResult::Local { enclosing_moniker } => {
+                        let local_symbol = scip::types::Symbol::new_local(local_count);
+                        local_count += 1;
+                        TokenSymbols {
+                            symbol: scip::symbol::format_symbol(local_symbol),
+                            enclosing_symbol: enclosing_moniker
+                                .as_ref()
+                                .map(moniker_to_symbol)
+                                .map(scip::symbol::format_symbol),
+                            is_inherent_impl: false,
+                        }
+                    }
+                })
+            })
+            .clone();
+        self.local_count = local_count;
+        token_symbols
+    }
+}
+
+fn moniker_to_symbol(moniker: &Moniker) -> scip_types::Symbol {
+    scip_types::Symbol {
+        scheme: "rust-analyzer".into(),
+        package: Some(scip_types::Package {
+            manager: "cargo".to_owned(),
+            name: moniker.package_information.name.clone(),
+            version: moniker.package_information.version.clone().unwrap_or_else(|| ".".to_owned()),
+            special_fields: Default::default(),
+        })
+        .into(),
+        descriptors: moniker_descriptors(&moniker.identifier),
+        special_fields: Default::default(),
+    }
+}
 
-    let package_name = moniker.package_information.name.clone();
-    let version = moniker.package_information.version.clone();
-    let descriptors = moniker
-        .identifier
+fn moniker_descriptors(identifier: &MonikerIdentifier) -> Vec {
+    use scip_types::descriptor::Suffix::*;
+    identifier
         .description
         .iter()
         .map(|desc| {
-            new_descriptor(
+            new_descriptor_str(
                 &desc.name,
                 match desc.desc {
                     MonikerDescriptorKind::Namespace => Namespace,
@@ -319,31 +505,17 @@ fn moniker_to_symbol(moniker: &MonikerResult) -> scip_types::Symbol {
                 },
             )
         })
-        .collect();
-
-    scip_types::Symbol {
-        scheme: "rust-analyzer".into(),
-        package: Some(scip_types::Package {
-            manager: "cargo".to_owned(),
-            name: package_name,
-            version: version.unwrap_or_else(|| ".".to_owned()),
-            special_fields: Default::default(),
-        })
-        .into(),
-        descriptors,
-        special_fields: Default::default(),
-    }
+        .collect()
 }
 
 #[cfg(test)]
 mod test {
     use super::*;
     use ide::{FilePosition, TextSize};
-    use scip::symbol::format_symbol;
     use test_fixture::ChangeFixture;
     use vfs::VfsPath;
 
-    fn position(ra_fixture: &str) -> (AnalysisHost, FilePosition) {
+    fn position(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (AnalysisHost, FilePosition) {
         let mut host = AnalysisHost::default();
         let change_fixture = ChangeFixture::parse(ra_fixture);
         host.raw_database_mut().apply_change(change_fixture.change);
@@ -355,7 +527,7 @@ mod test {
 
     /// If expected == "", then assert that there are no symbols (this is basically local symbol)
     #[track_caller]
-    fn check_symbol(ra_fixture: &str, expected: &str) {
+    fn check_symbol(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected: &str) {
         let (host, position) = position(ra_fixture);
 
         let analysis = host.analysis();
@@ -374,9 +546,25 @@ mod test {
                 continue;
             }
             for &(range, id) in &file.tokens {
-                if range.contains(offset - TextSize::from(1)) {
+                // check if cursor is within token, ignoring token for the module defined by the file (whose range is the whole file)
+                if range.start() != TextSize::from(0) && range.contains(offset - TextSize::from(1))
+                {
                     let token = si.tokens.get(id).unwrap();
-                    found_symbol = token.moniker.as_ref().map(moniker_to_symbol);
+                    found_symbol = match token.moniker.as_ref() {
+                        None => None,
+                        Some(MonikerResult::Moniker(moniker)) => {
+                            Some(scip::symbol::format_symbol(moniker_to_symbol(moniker)))
+                        }
+                        Some(MonikerResult::Local { enclosing_moniker: Some(moniker) }) => {
+                            Some(format!(
+                                "local enclosed by {}",
+                                scip::symbol::format_symbol(moniker_to_symbol(moniker))
+                            ))
+                        }
+                        Some(MonikerResult::Local { enclosing_moniker: None }) => {
+                            Some("unenclosed local".to_owned())
+                        }
+                    };
                     break;
                 }
             }
@@ -388,9 +576,7 @@ mod test {
         }
 
         assert!(found_symbol.is_some(), "must have one symbol {found_symbol:?}");
-        let res = found_symbol.unwrap();
-        let formatted = format_symbol(res);
-        assert_eq!(formatted, expected);
+        assert_eq!(found_symbol.unwrap(), expected);
     }
 
     #[test]
@@ -467,8 +653,7 @@ pub mod module {
         }
     }
     "#,
-            // "foo::module::MyTrait::MyType",
-            "rust-analyzer cargo foo 0.1.0 module/MyTrait#[MyType]",
+            "rust-analyzer cargo foo 0.1.0 module/MyTrait#MyType#",
         );
     }
 
@@ -489,8 +674,7 @@ pub mod module {
         }
     }
     "#,
-            // "foo::module::MyStruct::MyTrait::func",
-            "rust-analyzer cargo foo 0.1.0 module/MyStruct#MyTrait#func().",
+            "rust-analyzer cargo foo 0.1.0 module/impl#[MyStruct][MyTrait]func().",
         );
     }
 
@@ -526,7 +710,7 @@ pub mod example_mod {
     pub fn func(x$0: usize) {}
 }
 "#,
-            "rust-analyzer cargo foo 0.1.0 example_mod/func().(x)",
+            "local enclosed by rust-analyzer cargo foo 0.1.0 example_mod/func().",
         );
     }
 
@@ -546,7 +730,7 @@ pub mod example_mod {
     }
 }
 "#,
-            "rust-analyzer cargo foo 0.1.0 example_mod/func().(x)",
+            "local enclosed by rust-analyzer cargo foo 0.1.0 example_mod/func().",
         );
     }
 
@@ -566,7 +750,7 @@ pub mod example_mod {
         }
     }
     "#,
-            "",
+            "local enclosed by rust-analyzer cargo foo 0.1.0 module/func().",
         );
     }
 
@@ -609,7 +793,7 @@ pub mod example_mod {
     }
 
     #[test]
-    fn symbol_for_for_type_alias() {
+    fn symbol_for_type_alias() {
         check_symbol(
             r#"
     //- /workspace/lib.rs crate:main
@@ -619,6 +803,70 @@ pub mod example_mod {
         );
     }
 
+    // FIXME: This test represents current misbehavior.
+    #[test]
+    fn symbol_for_nested_function() {
+        check_symbol(
+            r#"
+    //- /workspace/lib.rs crate:main
+    pub fn func() {
+       pub fn inner_func$0() {}
+    }
+    "#,
+            "rust-analyzer cargo main . inner_func().",
+            // FIXME: This should be a local:
+            // "local enclosed by rust-analyzer cargo main . func().",
+        );
+    }
+
+    // FIXME: This test represents current misbehavior.
+    #[test]
+    fn symbol_for_struct_in_function() {
+        check_symbol(
+            r#"
+    //- /workspace/lib.rs crate:main
+    pub fn func() {
+       struct SomeStruct$0 {}
+    }
+    "#,
+            "rust-analyzer cargo main . SomeStruct#",
+            // FIXME: This should be a local:
+            // "local enclosed by rust-analyzer cargo main . func().",
+        );
+    }
+
+    // FIXME: This test represents current misbehavior.
+    #[test]
+    fn symbol_for_const_in_function() {
+        check_symbol(
+            r#"
+    //- /workspace/lib.rs crate:main
+    pub fn func() {
+       const SOME_CONST$0: u32 = 1;
+    }
+    "#,
+            "rust-analyzer cargo main . SOME_CONST.",
+            // FIXME: This should be a local:
+            // "local enclosed by rust-analyzer cargo main . func().",
+        );
+    }
+
+    // FIXME: This test represents current misbehavior.
+    #[test]
+    fn symbol_for_static_in_function() {
+        check_symbol(
+            r#"
+    //- /workspace/lib.rs crate:main
+    pub fn func() {
+       static SOME_STATIC$0: u32 = 1;
+    }
+    "#,
+            "rust-analyzer cargo main . SOME_STATIC.",
+            // FIXME: This should be a local:
+            // "local enclosed by rust-analyzer cargo main . func().",
+        );
+    }
+
     #[test]
     fn documentation_matches_doc_comment() {
         let s = "/// foo\nfn bar() {}";
@@ -636,7 +884,7 @@ pub mod example_mod {
         );
 
         let file = si.files.first().unwrap();
-        let (_, token_id) = file.tokens.first().unwrap();
+        let (_, token_id) = file.tokens.get(1).unwrap(); // first token is file module, second is `bar`
         let token = si.tokens.get(*token_id).unwrap();
 
         assert_eq!(token.documentation.as_ref().map(|d| d.as_str()), Some("foo"));
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs
index 986bd018b424d..021b1bff39301 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs
@@ -1,5 +1,5 @@
 //! Reports references in code that the IDE layer cannot resolve.
-use hir::{db::HirDatabase, AnyDiagnostic, Crate, HirFileIdExt as _, Module, Semantics};
+use hir::{db::HirDatabase, sym, AnyDiagnostic, Crate, HirFileIdExt as _, Module, Semantics};
 use ide::{AnalysisHost, RootDatabase, TextRange};
 use ide_db::{
     base_db::{SourceDatabase, SourceRootDatabase},
@@ -66,7 +66,7 @@ impl flags::UnresolvedReferences {
             let file_id = module.definition_source_file_id(db).original_file(db);
             if !visited_files.contains(&file_id) {
                 let crate_name =
-                    module.krate().display_name(db).as_deref().unwrap_or("unknown").to_owned();
+                    module.krate().display_name(db).as_deref().unwrap_or(&sym::unknown).to_owned();
                 let file_path = vfs.file_path(file_id.into());
                 eprintln!("processing crate: {crate_name}, module: {file_path}",);
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index b06117f73835a..e915e55722bbb 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -50,6 +50,14 @@ mod patch_old_style;
 //  - Don't use abbreviations unless really necessary
 //  - foo_command = overrides the subcommand, foo_overrideCommand allows full overwriting, extra args only applies for foo_command
 
+#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub enum MaxSubstitutionLength {
+    Hide,
+    #[serde(untagged)]
+    Limit(usize),
+}
+
 // Defines the server-side configuration of the rust-analyzer. We generate
 // *parts* of VS Code's `package.json` config from this. Run `cargo test` to
 // re-generate that file.
@@ -111,6 +119,9 @@ config_data! {
         /// Whether to show `Run` action. Only applies when
         /// `#rust-analyzer.hover.actions.enable#` is set.
         hover_actions_run_enable: bool             = true,
+        /// Whether to show `Update Test` action. Only applies when
+        /// `#rust-analyzer.hover.actions.enable#` and `#rust-analyzer.hover.actions.run.enable#` are set.
+        hover_actions_updateTest_enable: bool     = true,
 
         /// Whether to show documentation on hover.
         hover_documentation_enable: bool           = true,
@@ -119,6 +130,12 @@ config_data! {
         hover_documentation_keywords_enable: bool  = true,
         /// Use markdown syntax for links on hover.
         hover_links_enable: bool = true,
+        /// Whether to show what types are used as generic arguments in calls etc. on hover, and what is their max length to show such types, beyond it they will be shown with ellipsis.
+        ///
+        /// This can take three values: `null` means "unlimited", the string `"hide"` means to not show generic substitutions at all, and a number means to limit them to X characters.
+        ///
+        /// The default is 20 characters.
+        hover_maxSubstitutionLength: Option = Some(MaxSubstitutionLength::Limit(20)),
         /// How to render the align information in a memory layout hover.
         hover_memoryLayout_alignment: Option = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal),
         /// Whether to show memory layout data on hover.
@@ -168,6 +185,8 @@ config_data! {
         inlayHints_genericParameterHints_type_enable: bool = false,
         /// Whether to show implicit drop hints.
         inlayHints_implicitDrops_enable: bool                      = false,
+        /// Whether to show inlay hints for the implied type parameter `Sized` bound.
+        inlayHints_implicitSizedBoundHints_enable: bool            = false,
         /// Whether to show inlay type hints for elided lifetimes in function signatures.
         inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = LifetimeElisionDef::Never,
         /// Whether to prefer using parameter names as the name for elided lifetime hints if possible.
@@ -189,6 +208,8 @@ config_data! {
         /// Whether to hide inlay type hints for `let` statements that initialize to a closure.
         /// Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`.
         inlayHints_typeHints_hideClosureInitialization: bool       = false,
+        /// Whether to hide inlay parameter type hints for closures.
+        inlayHints_typeHints_hideClosureParameter:bool             = false,
         /// Whether to hide inlay type hints for constructors.
         inlayHints_typeHints_hideNamedConstructor: bool            = false,
 
@@ -229,6 +250,9 @@ config_data! {
         /// Whether to show `Run` lens. Only applies when
         /// `#rust-analyzer.lens.enable#` is set.
         lens_run_enable: bool              = true,
+        /// Whether to show `Update Test` lens. Only applies when
+        /// `#rust-analyzer.lens.enable#` and `#rust-analyzer.lens.run.enable#` are set.
+        lens_updateTest_enable: bool = true,
 
         /// Disable project auto-discovery in favor of explicitly specified set
         /// of projects.
@@ -304,8 +328,16 @@ config_data! {
         /// Show documentation.
         signatureInfo_documentation_enable: bool                       = true,
 
-        /// Specify the characters to exclude from triggering typing assists. The default trigger characters are `.`, `=`, `<`, `>`, `{`, and `(`.
-        typing_excludeChars: Option = Some("|<".to_owned()),
+        /// Specify the characters allowed to invoke special on typing triggers.
+        /// - typing `=` after `let` tries to smartly add `;` if `=` is followed by an existing expression
+        /// - typing `=` between two expressions adds `;` when in statement position
+        /// - typing `=` to turn an assignment into an equality comparison removes `;` when in expression position
+        /// - typing `.` in a chain method call auto-indents
+        /// - typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the expression
+        /// - typing `{` in a use item adds a closing `}` in the right place
+        /// - typing `>` to complete a return type `->` will insert a whitespace after it
+        /// - typing `<` in a path or type position inserts a closing `>` after the path or type.
+        typing_triggerChars: Option = Some("=.".to_owned()),
 
 
         /// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].
@@ -423,14 +455,39 @@ config_data! {
         ///
         /// In `match` arms it completes a comma instead.
         completion_addSemicolonToUnit: bool = true,
+        /// Toggles the additional completions that automatically show method calls and field accesses with `await` prefixed to them when completing on a future.
+        completion_autoAwait_enable: bool        = true,
+        /// Toggles the additional completions that automatically show method calls with `iter()` or `into_iter()` prefixed to them when completing on a type that has them.
+        completion_autoIter_enable: bool        = true,
         /// Toggles the additional completions that automatically add imports when completed.
         /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.
         completion_autoimport_enable: bool       = true,
+        /// A list of full paths to items to exclude from auto-importing completions.
+        ///
+        /// Traits in this list won't have their methods suggested in completions unless the trait
+        /// is in scope.
+        ///
+        /// You can either specify a string path which defaults to type "always" or use the more verbose
+        /// form `{ "path": "path::to::item", type: "always" }`.
+        ///
+        /// For traits the type "methods" can be used to only exclude the methods but not the trait itself.
+        ///
+        /// This setting also inherits `#rust-analyzer.completion.excludeTraits#`.
+        completion_autoimport_exclude: Vec = vec![
+            AutoImportExclusion::Verbose { path: "core::borrow::Borrow".to_owned(), r#type: AutoImportExclusionType::Methods },
+            AutoImportExclusion::Verbose { path: "core::borrow::BorrowMut".to_owned(), r#type: AutoImportExclusionType::Methods },
+        ],
         /// Toggles the additional completions that automatically show method calls and field accesses
         /// with `self` prefixed to them when inside a method.
         completion_autoself_enable: bool        = true,
         /// Whether to add parenthesis and argument snippets when completing function.
         completion_callable_snippets: CallableCompletionDef  = CallableCompletionDef::FillArguments,
+        /// A list of full paths to traits whose methods to exclude from completion.
+        ///
+        /// Methods from these traits won't be completed, even if the trait is in scope. However, they will still be suggested on expressions whose type is `dyn Trait`, `impl Trait` or `T where T: Trait`.
+        ///
+        /// Note that the trait themselves can still be completed.
+        completion_excludeTraits: Vec = Vec::new(),
         /// Whether to show full function/method signatures in completion docs.
         completion_fullFunctionSignatures_enable: bool = false,
         /// Whether to omit deprecated items from autocompletion. By default they are marked as deprecated but not hidden.
@@ -473,7 +530,7 @@ config_data! {
         imports_granularity_enforce: bool              = false,
         /// How imports should be grouped into use statements.
         imports_granularity_group: ImportGranularityDef  = ImportGranularityDef::Crate,
-        /// Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-import[following order]. Groups are separated by newlines.
+        /// Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.
         imports_group_enable: bool                           = true,
         /// Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`.
         imports_merge_glob: bool           = true,
@@ -530,11 +587,8 @@ config_data! {
         /// avoid checking unnecessary things.
         cargo_buildScripts_useRustcWrapper: bool = true,
         /// List of cfg options to enable with the given values.
-        cargo_cfgs: FxHashMap> = {
-            let mut m = FxHashMap::default();
-            m.insert("debug_assertions".to_owned(), None);
-            m.insert("miri".to_owned(), None);
-            m
+        cargo_cfgs: Vec = {
+            vec!["debug_assertions".into(), "miri".into()]
         },
         /// Extra arguments that are passed to every cargo invocation.
         cargo_extraArgs: Vec = vec![],
@@ -554,15 +608,12 @@ config_data! {
         ///
         /// This option does not take effect until rust-analyzer is restarted.
         cargo_sysroot: Option    = Some("discover".to_owned()),
-        /// How to query metadata for the sysroot crate. Using cargo metadata allows rust-analyzer
-        /// to analyze third-party dependencies of the standard libraries.
-        cargo_sysrootQueryMetadata: SysrootQueryMetadata = SysrootQueryMetadata::CargoMetadata,
         /// Relative path to the sysroot library sources. If left unset, this will default to
         /// `{cargo.sysroot}/lib/rustlib/src/rust/library`.
         ///
         /// This option does not take effect until rust-analyzer is restarted.
         cargo_sysrootSrc: Option    = None,
-        /// Compilation target override (target triple).
+        /// Compilation target override (target tuple).
         // FIXME(@poliorcetics): move to multiple targets here too, but this will need more work
         // than `checkOnSave_target`
         cargo_target: Option     = None,
@@ -690,6 +741,10 @@ config_data! {
         /// available on a nightly build.
         rustfmt_rangeFormatting_enable: bool = false,
 
+        /// Additional paths to include in the VFS. Generally for code that is
+        /// generated or otherwise managed by a build system outside of Cargo,
+        /// though Cargo might be the eventual consumer.
+        vfs_extraIncludes: Vec = vec![],
 
         /// Workspace symbol search kind.
         workspace_symbol_search_kind: WorkspaceSymbolSearchKindDef = WorkspaceSymbolSearchKindDef::OnlyTypes,
@@ -854,7 +909,7 @@ impl Config {
         if let Some(mut json) = change.client_config_change {
             tracing::info!("updating config from JSON: {:#}", json);
 
-            if !(json.is_null() || json.as_object().map_or(false, |it| it.is_empty())) {
+            if !(json.is_null() || json.as_object().is_some_and(|it| it.is_empty())) {
                 let mut json_errors = vec![];
                 let detached_files = get_field_json::>(
                     &mut json,
@@ -869,11 +924,18 @@ impl Config {
 
                 patch_old_style::patch_json_for_outdated_configs(&mut json);
 
+                let mut json_errors = vec![];
+                let snips = get_field_json::>(
+                    &mut json,
+                    &mut json_errors,
+                    "completion_snippets_custom",
+                    None,
+                )
+                .unwrap_or(self.completion_snippets_custom().to_owned());
+
                 // IMPORTANT : This holds as long as ` completion_snippets_custom` is declared `client`.
                 config.snippets.clear();
 
-                let snips = self.completion_snippets_custom().to_owned();
-
                 for (name, def) in snips.iter() {
                     if def.prefix.is_empty() && def.postfix.is_empty() {
                         continue;
@@ -1147,6 +1209,7 @@ pub struct LensConfig {
     // runnables
     pub run: bool,
     pub debug: bool,
+    pub update_test: bool,
     pub interpret: bool,
 
     // implementations
@@ -1182,6 +1245,7 @@ impl LensConfig {
     pub fn any(&self) -> bool {
         self.run
             || self.debug
+            || self.update_test
             || self.implementations
             || self.method_refs
             || self.refs_adt
@@ -1194,7 +1258,7 @@ impl LensConfig {
     }
 
     pub fn runnable(&self) -> bool {
-        self.run || self.debug
+        self.run || self.debug || self.update_test
     }
 
     pub fn references(&self) -> bool {
@@ -1208,6 +1272,7 @@ pub struct HoverActionsConfig {
     pub references: bool,
     pub run: bool,
     pub debug: bool,
+    pub update_test: bool,
     pub goto_type_def: bool,
 }
 
@@ -1217,6 +1282,7 @@ impl HoverActionsConfig {
         references: false,
         run: false,
         debug: false,
+        update_test: false,
         goto_type_def: false,
     };
 
@@ -1229,7 +1295,7 @@ impl HoverActionsConfig {
     }
 
     pub fn runnable(&self) -> bool {
-        self.run || self.debug
+        self.run || self.debug || self.update_test
     }
 }
 
@@ -1417,13 +1483,15 @@ impl Config {
         CallHierarchyConfig { exclude_tests: self.references_excludeTests().to_owned() }
     }
 
-    pub fn completion(&self, source_root: Option) -> CompletionConfig {
+    pub fn completion(&self, source_root: Option) -> CompletionConfig<'_> {
         let client_capability_fields = self.completion_resolve_support_properties();
         CompletionConfig {
             enable_postfix_completions: self.completion_postfix_enable(source_root).to_owned(),
             enable_imports_on_the_fly: self.completion_autoimport_enable(source_root).to_owned()
                 && self.caps.completion_item_edit_resolve(),
             enable_self_on_the_fly: self.completion_autoself_enable(source_root).to_owned(),
+            enable_auto_iter: *self.completion_autoIter_enable(source_root),
+            enable_auto_await: *self.completion_autoAwait_enable(source_root),
             enable_private_editable: self.completion_privateEditable_enable(source_root).to_owned(),
             full_function_signatures: self
                 .completion_fullFunctionSignatures_enable(source_root)
@@ -1448,6 +1516,27 @@ impl Config {
             } else {
                 CompletionFieldsToResolve::from_client_capabilities(&client_capability_fields)
             },
+            exclude_flyimport: self
+                .completion_autoimport_exclude(source_root)
+                .iter()
+                .map(|it| match it {
+                    AutoImportExclusion::Path(path) => {
+                        (path.clone(), ide_completion::AutoImportExclusionType::Always)
+                    }
+                    AutoImportExclusion::Verbose { path, r#type } => (
+                        path.clone(),
+                        match r#type {
+                            AutoImportExclusionType::Always => {
+                                ide_completion::AutoImportExclusionType::Always
+                            }
+                            AutoImportExclusionType::Methods => {
+                                ide_completion::AutoImportExclusionType::Methods
+                            }
+                        },
+                    ),
+                })
+                .collect(),
+            exclude_traits: self.completion_excludeTraits(source_root),
         }
     }
 
@@ -1503,6 +1592,9 @@ impl Config {
             references: enable && self.hover_actions_references_enable().to_owned(),
             run: enable && self.hover_actions_run_enable().to_owned(),
             debug: enable && self.hover_actions_debug_enable().to_owned(),
+            update_test: enable
+                && self.hover_actions_run_enable().to_owned()
+                && self.hover_actions_updateTest_enable().to_owned(),
             goto_type_def: enable && self.hover_actions_gotoTypeDef_enable().to_owned(),
         }
     }
@@ -1533,6 +1625,11 @@ impl Config {
             max_trait_assoc_items_count: self.hover_show_traitAssocItems().to_owned(),
             max_fields_count: self.hover_show_fields().to_owned(),
             max_enum_variants_count: self.hover_show_enumVariants().to_owned(),
+            max_subst_ty_len: match self.hover_maxSubstitutionLength() {
+                Some(MaxSubstitutionLength::Hide) => ide::SubstTyLen::Hide,
+                Some(MaxSubstitutionLength::Limit(limit)) => ide::SubstTyLen::LimitTo(*limit),
+                None => ide::SubstTyLen::Unlimited,
+            },
         }
     }
 
@@ -1542,6 +1639,7 @@ impl Config {
         InlayHintsConfig {
             render_colons: self.inlayHints_renderColons().to_owned(),
             type_hints: self.inlayHints_typeHints_enable().to_owned(),
+            sized_bound: self.inlayHints_implicitSizedBoundHints_enable().to_owned(),
             parameter_hints: self.inlayHints_parameterHints_enable().to_owned(),
             generic_parameter_hints: GenericParameterHints {
                 type_hints: self.inlayHints_genericParameterHints_type_enable().to_owned(),
@@ -1570,6 +1668,9 @@ impl Config {
             hide_closure_initialization_hints: self
                 .inlayHints_typeHints_hideClosureInitialization()
                 .to_owned(),
+            hide_closure_parameter_hints: self
+                .inlayHints_typeHints_hideClosureParameter()
+                .to_owned(),
             closure_style: match self.inlayHints_closureStyle() {
                 ClosureStyle::ImplFn => hir::ClosureStyle::ImplFn,
                 ClosureStyle::RustAnalyzer => hir::ClosureStyle::RANotation,
@@ -1848,6 +1949,13 @@ impl Config {
         });
         let sysroot_src =
             self.cargo_sysrootSrc(source_root).as_ref().map(|sysroot| self.root_path.join(sysroot));
+        let extra_includes = self
+            .vfs_extraIncludes(source_root)
+            .iter()
+            .map(String::as_str)
+            .map(AbsPathBuf::try_from)
+            .filter_map(Result::ok)
+            .collect();
 
         CargoConfig {
             all_targets: *self.cargo_allTargets(source_root),
@@ -1860,18 +1968,20 @@ impl Config {
             },
             target: self.cargo_target(source_root).clone(),
             sysroot,
-            sysroot_query_metadata: match self.cargo_sysrootQueryMetadata(None) {
-                SysrootQueryMetadata::CargoMetadata => {
-                    project_model::SysrootQueryMetadata::CargoMetadata
-                }
-                SysrootQueryMetadata::None => project_model::SysrootQueryMetadata::None,
-            },
             sysroot_src,
             rustc_source,
+            extra_includes,
             cfg_overrides: project_model::CfgOverrides {
                 global: CfgDiff::new(
                     self.cargo_cfgs(source_root)
                         .iter()
+                        // parse any cfg setting formatted as key=value or just key (without value)
+                        .filter_map(|s| {
+                            let mut sp = s.splitn(2, "=");
+                            let key = sp.next();
+                            let val = sp.next();
+                            key.map(|key| (key, val))
+                        })
                         .map(|(key, val)| match val {
                             Some(val) => CfgAtom::KeyValue {
                                 key: Symbol::intern(key),
@@ -1969,7 +2079,7 @@ impl Config {
 
     pub(crate) fn cargo_test_options(&self, source_root: Option) -> CargoOptions {
         CargoOptions {
-            target_triples: self.cargo_target(source_root).clone().into_iter().collect(),
+            target_tuples: self.cargo_target(source_root).clone().into_iter().collect(),
             all_targets: false,
             no_default_features: *self.cargo_noDefaultFeatures(source_root),
             all_features: matches!(self.cargo_features(source_root), CargoFeaturesDef::All),
@@ -2004,7 +2114,7 @@ impl Config {
             Some(_) | None => FlycheckConfig::CargoCommand {
                 command: self.check_command(source_root).clone(),
                 options: CargoOptions {
-                    target_triples: self
+                    target_tuples: self
                         .check_targets(source_root)
                         .clone()
                         .and_then(|targets| match &targets.0[..] {
@@ -2047,11 +2157,13 @@ impl Config {
     fn target_dir_from_config(&self, source_root: Option) -> Option {
         self.cargo_targetDir(source_root).as_ref().and_then(|target_dir| match target_dir {
             TargetDirectory::UseSubdirectory(true) => {
-                Some(Utf8PathBuf::from("target/rust-analyzer"))
+                let env_var = env::var("CARGO_TARGET_DIR").ok();
+                let mut path = Utf8PathBuf::from(env_var.as_deref().unwrap_or("target"));
+                path.push("rust-analyzer");
+                Some(path)
             }
             TargetDirectory::UseSubdirectory(false) => None,
-            TargetDirectory::Directory(dir) if dir.is_relative() => Some(dir.clone()),
-            TargetDirectory::Directory(_) => None,
+            TargetDirectory::Directory(dir) => Some(dir.clone()),
         })
     }
 
@@ -2096,6 +2208,9 @@ impl Config {
         LensConfig {
             run: *self.lens_enable() && *self.lens_run_enable(),
             debug: *self.lens_enable() && *self.lens_debug_enable(),
+            update_test: *self.lens_enable()
+                && *self.lens_updateTest_enable()
+                && *self.lens_run_enable(),
             interpret: *self.lens_enable() && *self.lens_run_enable() && *self.interpret_tests(),
             implementations: *self.lens_enable() && *self.lens_implementations_enable(),
             method_refs: *self.lens_enable() && *self.lens_references_method_enable(),
@@ -2155,8 +2270,8 @@ impl Config {
         }
     }
 
-    pub fn typing_exclude_chars(&self) -> Option {
-        self.typing_excludeChars().clone()
+    pub fn typing_trigger_chars(&self) -> &str {
+        self.typing_triggerChars().as_deref().unwrap_or_default()
     }
 
     // VSCode is our reference implementation, so we allow ourselves to work around issues by
@@ -2377,6 +2492,21 @@ enum ExprFillDefaultDef {
     Default,
 }
 
+#[derive(Serialize, Deserialize, Debug, Clone)]
+#[serde(untagged)]
+#[serde(rename_all = "snake_case")]
+pub enum AutoImportExclusion {
+    Path(String),
+    Verbose { path: String, r#type: AutoImportExclusionType },
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+#[serde(rename_all = "snake_case")]
+pub enum AutoImportExclusionType {
+    Always,
+    Methods,
+}
+
 #[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(rename_all = "snake_case")]
 enum ImportGranularityDef {
@@ -2568,13 +2698,6 @@ pub enum NumThreads {
     Concrete(usize),
 }
 
-#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
-#[serde(rename_all = "snake_case")]
-pub enum SysrootQueryMetadata {
-    CargoMetadata,
-    None,
-}
-
 macro_rules! _default_val {
     (@verbatim: $s:literal, $ty:ty) => {{
         let default_: $ty = serde_json::from_str(&$s).unwrap();
@@ -3426,13 +3549,45 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
                 }
             ]
         },
-        "SysrootQueryMetadata" => set! {
-            "type": "string",
-            "enum": ["none", "cargo_metadata"],
-            "enumDescriptions": [
-                "Do not query sysroot metadata, always use stitched sysroot.",
-                "Use `cargo metadata` to query sysroot metadata."
-            ],
+        "Option" => set! {
+            "anyOf": [
+                {
+                    "type": "null"
+                },
+                {
+                    "type": "string",
+                    "enum": ["hide"]
+                },
+                {
+                    "type": "integer"
+                }
+            ]
+        },
+        "Vec" => set! {
+            "type": "array",
+            "items": {
+                "anyOf": [
+                    {
+                        "type": "string",
+                    },
+                    {
+                        "type": "object",
+                        "properties": {
+                            "path": {
+                                "type": "string",
+                            },
+                            "type": {
+                                "type": "string",
+                                "enum": ["always", "methods"],
+                                "enumDescriptions": [
+                                    "Do not show this item or its methods (if it is a trait) in auto-import completions.",
+                                    "Do not show this traits methods in auto-import completions."
+                                ],
+                            },
+                        }
+                    }
+                ]
+             }
         },
         _ => panic!("missing entry for {ty}: {default} (field {field})"),
     }
@@ -3474,21 +3629,9 @@ fn manual(fields: &[SchemaField]) -> String {
         let name = format!("rust-analyzer.{}", field.replace('_', "."));
         let doc = doc_comment_to_string(doc);
         if default.contains('\n') {
-            format_to_acc!(
-                acc,
-                r#"[[{name}]]{name}::
-+
---
-Default:
-----
-{default}
-----
-{doc}
---
-"#
-            )
+            format_to_acc!(acc, " **{name}**\n\nDefault:\n\n```{default}\n\n```\n\n {doc}\n\n ")
         } else {
-            format_to_acc!(acc, "[[{name}]]{name} (default: `{default}`)::\n+\n--\n{doc}--\n")
+            format_to_acc!(acc, "**{name}** (default: {default})\n\n {doc}\n\n")
         }
     })
 }
@@ -3566,7 +3709,7 @@ mod tests {
 
     #[test]
     fn generate_config_documentation() {
-        let docs_path = project_root().join("docs/user/generated_config.adoc");
+        let docs_path = project_root().join("docs/book/src/configuration_generated.md");
         let expected = FullConfigInput::manual();
         ensure_file_contents(docs_path.as_std_path(), &expected);
     }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
index c3ab7f3ae7156..fafffa043f988 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -500,7 +500,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
 fn rustc_code_description(code: Option<&str>) -> Option {
     code.filter(|code| {
         let mut chars = code.chars();
-        chars.next().map_or(false, |c| c == 'E')
+        chars.next() == Some('E')
             && chars.by_ref().take(4).all(|c| c.is_ascii_digit())
             && chars.next().is_none()
     })
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs
index 96b164228efea..0c111319bb41b 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs
@@ -1,6 +1,6 @@
 //! Infrastructure for lazy project discovery. Currently only support rust-project.json discovery
 //! via a custom discover command.
-use std::{io, process::Command};
+use std::{io, path::Path};
 
 use crossbeam_channel::Sender;
 use paths::{AbsPathBuf, Utf8Path, Utf8PathBuf};
@@ -43,7 +43,11 @@ impl DiscoverCommand {
     }
 
     /// Spawn the command inside [Discover] and report progress, if any.
-    pub(crate) fn spawn(&self, discover_arg: DiscoverArgument) -> io::Result {
+    pub(crate) fn spawn(
+        &self,
+        discover_arg: DiscoverArgument,
+        current_dir: &Path,
+    ) -> io::Result {
         let command = &self.command[0];
         let args = &self.command[1..];
 
@@ -58,7 +62,7 @@ impl DiscoverCommand {
             })
             .collect();
 
-        let mut cmd = Command::new(command);
+        let mut cmd = toolchain::command(command, current_dir);
         cmd.args(args);
 
         Ok(DiscoverHandle {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs
index 53c145f884e01..2309f94a7429b 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs
@@ -1,10 +1,11 @@
 //! Flycheck provides the functionality needed to run `cargo check` to provide
 //! LSP diagnostics based on the output of the command.
 
-use std::{fmt, io, mem, process::Command, time::Duration};
+use std::{fmt, io, process::Command, time::Duration};
 
 use cargo_metadata::PackageId;
 use crossbeam_channel::{select_biased, unbounded, Receiver, Sender};
+use ide_db::FxHashSet;
 use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
 use rustc_hash::FxHashMap;
 use serde::Deserialize as _;
@@ -27,7 +28,7 @@ pub(crate) enum InvocationStrategy {
 
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub(crate) struct CargoOptions {
-    pub(crate) target_triples: Vec,
+    pub(crate) target_tuples: Vec,
     pub(crate) all_targets: bool,
     pub(crate) no_default_features: bool,
     pub(crate) all_features: bool,
@@ -38,7 +39,7 @@ pub(crate) struct CargoOptions {
     pub(crate) target_dir: Option,
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub(crate) enum Target {
     Bin(String),
     Example(String),
@@ -48,7 +49,7 @@ pub(crate) enum Target {
 
 impl CargoOptions {
     pub(crate) fn apply_on_command(&self, cmd: &mut Command) {
-        for target in &self.target_triples {
+        for target in &self.target_tuples {
             cmd.args(["--target", target.as_str()]);
         }
         if self.all_targets {
@@ -87,6 +88,17 @@ pub(crate) enum FlycheckConfig {
     },
 }
 
+impl FlycheckConfig {
+    pub(crate) fn invocation_strategy_once(&self) -> bool {
+        match self {
+            FlycheckConfig::CargoCommand { .. } => false,
+            FlycheckConfig::CustomCommand { invocation_strategy, .. } => {
+                *invocation_strategy == InvocationStrategy::Once
+            }
+        }
+    }
+}
+
 impl fmt::Display for FlycheckConfig {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
@@ -231,13 +243,9 @@ struct FlycheckActor {
     command_handle: Option>,
     /// The receiver side of the channel mentioned above.
     command_receiver: Option>,
-    package_status: FxHashMap, DiagnosticReceived>,
-}
-
-#[derive(PartialEq, Eq, Copy, Clone, Debug)]
-enum DiagnosticReceived {
-    Yes,
-    No,
+    diagnostics_cleared_for: FxHashSet>,
+    diagnostics_cleared_for_all: bool,
+    diagnostics_received: bool,
 }
 
 #[allow(clippy::large_enum_variant)]
@@ -267,7 +275,9 @@ impl FlycheckActor {
             manifest_path,
             command_handle: None,
             command_receiver: None,
-            package_status: FxHashMap::default(),
+            diagnostics_cleared_for: Default::default(),
+            diagnostics_cleared_for_all: false,
+            diagnostics_received: false,
         }
     }
 
@@ -344,23 +354,16 @@ impl FlycheckActor {
                             error
                         );
                     }
-                    if self.package_status.is_empty() {
+                    if !self.diagnostics_received {
+                        tracing::trace!(flycheck_id = self.id, "clearing diagnostics");
                         // We finished without receiving any diagnostics.
-                        // That means all of them are stale.
+                        // Clear everything for good measure
                         self.send(FlycheckMessage::ClearDiagnostics {
                             id: self.id,
                             package_id: None,
                         });
-                    } else {
-                        for (package_id, status) in mem::take(&mut self.package_status) {
-                            if let DiagnosticReceived::No = status {
-                                self.send(FlycheckMessage::ClearDiagnostics {
-                                    id: self.id,
-                                    package_id: Some(package_id),
-                                });
-                            }
-                        }
                     }
+                    self.clear_diagnostics_state();
 
                     self.report_progress(Progress::DidFinish(res));
                 }
@@ -373,9 +376,18 @@ impl FlycheckActor {
                             "artifact received"
                         );
                         self.report_progress(Progress::DidCheckCrate(msg.target.name));
-                        self.package_status
-                            .entry(Arc::new(msg.package_id))
-                            .or_insert(DiagnosticReceived::No);
+                        let package_id = Arc::new(msg.package_id);
+                        if self.diagnostics_cleared_for.insert(package_id.clone()) {
+                            tracing::trace!(
+                                flycheck_id = self.id,
+                                package_id = package_id.repr,
+                                "clearing diagnostics"
+                            );
+                            self.send(FlycheckMessage::ClearDiagnostics {
+                                id: self.id,
+                                package_id: Some(package_id),
+                            });
+                        }
                     }
                     CargoCheckMessage::Diagnostic { diagnostic, package_id } => {
                         tracing::trace!(
@@ -384,15 +396,25 @@ impl FlycheckActor {
                             package_id = package_id.as_ref().map(|it| &it.repr),
                             "diagnostic received"
                         );
+                        self.diagnostics_received = true;
                         if let Some(package_id) = &package_id {
-                            if !self.package_status.contains_key(package_id) {
-                                self.package_status
-                                    .insert(package_id.clone(), DiagnosticReceived::Yes);
+                            if self.diagnostics_cleared_for.insert(package_id.clone()) {
+                                tracing::trace!(
+                                    flycheck_id = self.id,
+                                    package_id = package_id.repr,
+                                    "clearing diagnostics"
+                                );
                                 self.send(FlycheckMessage::ClearDiagnostics {
                                     id: self.id,
                                     package_id: Some(package_id.clone()),
                                 });
                             }
+                        } else if !self.diagnostics_cleared_for_all {
+                            self.diagnostics_cleared_for_all = true;
+                            self.send(FlycheckMessage::ClearDiagnostics {
+                                id: self.id,
+                                package_id: None,
+                            });
                         }
                         self.send(FlycheckMessage::AddDiagnostic {
                             id: self.id,
@@ -417,8 +439,14 @@ impl FlycheckActor {
             command_handle.cancel();
             self.command_receiver.take();
             self.report_progress(Progress::DidCancel);
-            self.package_status.clear();
         }
+        self.clear_diagnostics_state();
+    }
+
+    fn clear_diagnostics_state(&mut self) {
+        self.diagnostics_cleared_for.clear();
+        self.diagnostics_cleared_for_all = false;
+        self.diagnostics_received = false;
     }
 
     /// Construct a `Command` object for checking the user's code. If the user
@@ -432,12 +460,11 @@ impl FlycheckActor {
     ) -> Option {
         match &self.config {
             FlycheckConfig::CargoCommand { command, options, ansi_color_output } => {
-                let mut cmd = Command::new(Tool::Cargo.path());
+                let mut cmd = toolchain::command(Tool::Cargo.path(), &*self.root);
                 if let Some(sysroot_root) = &self.sysroot_root {
                     cmd.env("RUSTUP_TOOLCHAIN", AsRef::::as_ref(sysroot_root));
                 }
                 cmd.arg(command);
-                cmd.current_dir(&*self.root);
 
                 match package {
                     Some(pkg) => cmd.arg("-p").arg(pkg),
@@ -462,7 +489,7 @@ impl FlycheckActor {
                 if let Some(manifest_path) = &self.manifest_path {
                     cmd.arg("--manifest-path");
                     cmd.arg(manifest_path);
-                    if manifest_path.extension().map_or(false, |ext| ext == "rs") {
+                    if manifest_path.extension() == Some("rs") {
                         cmd.arg("-Zscript");
                     }
                 }
@@ -474,18 +501,15 @@ impl FlycheckActor {
                 Some(cmd)
             }
             FlycheckConfig::CustomCommand { command, args, extra_env, invocation_strategy } => {
-                let mut cmd = Command::new(command);
-                cmd.envs(extra_env);
-
-                match invocation_strategy {
-                    InvocationStrategy::Once => {
-                        cmd.current_dir(&*self.root);
-                    }
+                let root = match invocation_strategy {
+                    InvocationStrategy::Once => &*self.root,
                     InvocationStrategy::PerWorkspace => {
-                        // FIXME: cmd.current_dir(&affected_workspace);
-                        cmd.current_dir(&*self.root);
+                        // FIXME: &affected_workspace
+                        &*self.root
                     }
-                }
+                };
+                let mut cmd = toolchain::command(command, root);
+                cmd.envs(extra_env);
 
                 // If the custom command has a $saved_file placeholder, and
                 // we're saving a file, replace the placeholder in the arguments.
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
index dd13bdba4cb2b..b52f64aaacecb 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
@@ -17,7 +17,7 @@ use parking_lot::{
     MappedRwLockReadGuard, Mutex, RwLock, RwLockReadGuard, RwLockUpgradableReadGuard,
     RwLockWriteGuard,
 };
-use proc_macro_api::ProcMacroServer;
+use proc_macro_api::ProcMacroClient;
 use project_model::{ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, WorkspaceBuildScripts};
 use rustc_hash::{FxHashMap, FxHashSet};
 use tracing::{span, trace, Level};
@@ -95,7 +95,7 @@ pub(crate) struct GlobalState {
     pub(crate) last_reported_status: lsp_ext::ServerStatusParams,
 
     // proc macros
-    pub(crate) proc_macro_clients: Arc<[anyhow::Result]>,
+    pub(crate) proc_macro_clients: Arc<[anyhow::Result]>,
     pub(crate) build_deps_changed: bool,
 
     // Flycheck
@@ -396,6 +396,7 @@ impl GlobalState {
             || !self.config.same_source_root_parent_map(&self.local_roots_parent_map)
         {
             let config_change = {
+                let _p = span!(Level::INFO, "GlobalState::process_changes/config_change").entered();
                 let user_config_path = (|| {
                     let mut p = Config::user_config_dir_path()?;
                     p.push("rust-analyzer.toml");
@@ -569,12 +570,12 @@ impl GlobalState {
         if let Some((method, start)) = self.req_queue.incoming.complete(&response.id) {
             if let Some(err) = &response.error {
                 if err.message.starts_with("server panicked") {
-                    self.poke_rust_analyzer_developer(format!("{}, check the log", err.message))
+                    self.poke_rust_analyzer_developer(format!("{}, check the log", err.message));
                 }
             }
 
             let duration = start.elapsed();
-            tracing::debug!("handled {} - ({}) in {:0.2?}", method, response.id, duration);
+            tracing::debug!(name: "message response", method, %response.id, duration = format_args!("{:0.2?}", duration));
             self.send(response.into());
         }
     }
@@ -726,7 +727,6 @@ impl GlobalStateSnapshot {
                     };
 
                     return Some(TargetSpec::ProjectJson(ProjectJsonTargetSpec {
-                        crate_id,
                         label: build.label,
                         target_kind: build.target_kind,
                         shell_runnables: project.runnables().to_owned(),
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs
index 2aa4ffbe1dc11..4683877db69b0 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs
@@ -5,6 +5,7 @@ use std::{
 };
 
 use ide::Cancelled;
+use ide_db::base_db::ra_salsa::Cycle;
 use lsp_server::{ExtractError, Response, ResponseError};
 use serde::{de::DeserializeOwned, Serialize};
 use stdx::thread::ThreadIntent;
@@ -117,7 +118,7 @@ impl RequestDispatcher<'_> {
             }
             return self;
         }
-        self.on_with_thread_intent::(
+        self.on_with_thread_intent::(
             ThreadIntent::Worker,
             f,
             Self::content_modified_error,
@@ -146,7 +147,7 @@ impl RequestDispatcher<'_> {
             }
             return self;
         }
-        self.on_with_thread_intent::(ThreadIntent::Worker, f, on_cancelled)
+        self.on_with_thread_intent::(ThreadIntent::Worker, f, on_cancelled)
     }
 
     /// Dispatches a non-latency-sensitive request onto the thread pool. When the VFS is marked not
@@ -165,7 +166,7 @@ impl RequestDispatcher<'_> {
             }
             return self;
         }
-        self.on_with_thread_intent::(
+        self.on_with_thread_intent::(
             ThreadIntent::Worker,
             f,
             Self::content_modified_error,
@@ -192,7 +193,7 @@ impl RequestDispatcher<'_> {
             }
             return self;
         }
-        self.on_with_thread_intent::(
+        self.on_with_thread_intent::(
             ThreadIntent::LatencySensitive,
             f,
             Self::content_modified_error,
@@ -211,7 +212,7 @@ impl RequestDispatcher<'_> {
         R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
         R::Result: Serialize,
     {
-        self.on_with_thread_intent::(
+        self.on_with_thread_intent::(
             ThreadIntent::LatencySensitive,
             f,
             Self::content_modified_error,
@@ -230,7 +231,7 @@ impl RequestDispatcher<'_> {
         }
     }
 
-    fn on_with_thread_intent(
+    fn on_with_thread_intent(
         &mut self,
         intent: ThreadIntent,
         f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result,
@@ -250,10 +251,10 @@ impl RequestDispatcher<'_> {
         tracing::debug!(?params);
 
         let world = self.global_state.snapshot();
-        if MAIN_POOL {
-            &mut self.global_state.task_pool.handle
-        } else {
+        if RUSTFMT {
             &mut self.global_state.fmt_pool.handle
+        } else {
+            &mut self.global_state.task_pool.handle
         }
         .spawn(intent, move || {
             let result = panic::catch_unwind(move || {
@@ -307,10 +308,31 @@ impl RequestDispatcher<'_> {
     }
 }
 
+#[derive(Debug)]
+enum HandlerCancelledError {
+    PropagatedPanic,
+    Inner(ide::Cancelled),
+}
+
+impl std::error::Error for HandlerCancelledError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        match self {
+            HandlerCancelledError::PropagatedPanic => None,
+            HandlerCancelledError::Inner(cancelled) => Some(cancelled),
+        }
+    }
+}
+
+impl fmt::Display for HandlerCancelledError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "Cancelled")
+    }
+}
+
 fn thread_result_to_response(
     id: lsp_server::RequestId,
     result: thread::Result>,
-) -> Result
+) -> Result
 where
     R: lsp_types::request::Request,
     R::Params: DeserializeOwned,
@@ -328,7 +350,13 @@ where
             if let Some(panic_message) = panic_message {
                 message.push_str(": ");
                 message.push_str(panic_message)
-            };
+            } else if let Some(cycle) = panic.downcast_ref::() {
+                tracing::error!("Cycle propagated out of salsa! This is a bug: {cycle:?}");
+                return Err(HandlerCancelledError::PropagatedPanic);
+            } else if let Ok(cancelled) = panic.downcast::() {
+                tracing::error!("Cancellation propagated out of salsa! This is a bug");
+                return Err(HandlerCancelledError::Inner(*cancelled));
+            }
 
             Ok(lsp_server::Response::new_err(
                 id,
@@ -342,7 +370,7 @@ where
 fn result_to_response(
     id: lsp_server::RequestId,
     result: anyhow::Result,
-) -> Result
+) -> Result
 where
     R: lsp_types::request::Request,
     R::Params: DeserializeOwned,
@@ -353,7 +381,7 @@ where
         Err(e) => match e.downcast::() {
             Ok(lsp_error) => lsp_server::Response::new_err(id, lsp_error.code, lsp_error.message),
             Err(e) => match e.downcast::() {
-                Ok(cancelled) => return Err(cancelled),
+                Ok(cancelled) => return Err(HandlerCancelledError::Inner(cancelled)),
                 Err(e) => lsp_server::Response::new_err(
                     id,
                     lsp_server::ErrorCode::InternalError as i32,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
index c0231fd04e5d6..48856d19e155d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
@@ -10,7 +10,6 @@ use lsp_types::{
     DidOpenTextDocumentParams, DidSaveTextDocumentParams, WorkDoneProgressCancelParams,
 };
 use paths::Utf8PathBuf;
-use stdx::TupleExt;
 use triomphe::Arc;
 use vfs::{AbsPathBuf, ChangeKind, VfsPath};
 
@@ -75,8 +74,8 @@ pub(crate) fn handle_did_open_text_document(
             tracing::error!("duplicate DidOpenTextDocument: {}", path);
         }
 
-        tracing::info!("New file content set {:?}", params.text_document.text);
-        state.vfs.write().0.set_file_contents(path, Some(params.text_document.text.into_bytes()));
+        let contents = params.text_document.text.into_bytes();
+        state.vfs.write().0.set_file_contents(path, Some(contents));
         if state.config.discover_workspace_config().is_some() {
             tracing::debug!("queuing task");
             let _ = state
@@ -293,15 +292,20 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
     let file_id = state.vfs.read().0.file_id(&vfs_path);
     if let Some(file_id) = file_id {
         let world = state.snapshot();
+        let invocation_strategy_once = state.config.flycheck(None).invocation_strategy_once();
         let may_flycheck_workspace = state.config.flycheck_workspace(None);
         let mut updated = false;
         let task = move || -> std::result::Result<(), ide::Cancelled> {
-            // Is the target binary? If so we let flycheck run only for the workspace that contains the crate.
+            if invocation_strategy_once {
+                let saved_file = vfs_path.as_path().map(|p| p.to_owned());
+                world.flycheck[0].restart_workspace(saved_file.clone());
+            }
+
             let target = TargetSpec::for_file(&world, file_id)?.and_then(|it| {
                 let tgt_kind = it.target_kind();
-                let (tgt_name, crate_id) = match it {
-                    TargetSpec::Cargo(c) => (c.target, c.crate_id),
-                    TargetSpec::ProjectJson(p) => (p.label, p.crate_id),
+                let (tgt_name, root, package) = match it {
+                    TargetSpec::Cargo(c) => (c.target, c.workspace_root, c.package),
+                    _ => return None,
                 };
 
                 let tgt = match tgt_kind {
@@ -309,28 +313,49 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
                     project_model::TargetKind::Example => Target::Example(tgt_name),
                     project_model::TargetKind::Test => Target::Test(tgt_name),
                     project_model::TargetKind::Bench => Target::Benchmark(tgt_name),
-                    _ => return None,
+                    _ => return Some((None, root, package)),
                 };
 
-                Some((tgt, crate_id))
+                Some((Some(tgt), root, package))
             });
-
-            let crate_ids = match target {
-                // Trigger flychecks for the only crate which the target belongs to
-                Some((_, krate)) => vec![krate],
-                None => {
-                    // Trigger flychecks for all workspaces that depend on the saved file
-                    // Crates containing or depending on the saved file
-                    world
-                        .analysis
-                        .crates_for(file_id)?
-                        .into_iter()
-                        .flat_map(|id| world.analysis.transitive_rev_deps(id))
-                        .flatten()
-                        .unique()
-                        .collect::>()
+            tracing::debug!(?target, "flycheck target");
+            // we have a specific non-library target, attempt to only check that target, nothing
+            // else will be affected
+            if let Some((target, root, package)) = target {
+                // trigger a package check if we have a non-library target as that can't affect
+                // anything else in the workspace OR if we're not allowed to check the workspace as
+                // the user opted into package checks then
+                let package_check_allowed = target.is_some() || !may_flycheck_workspace;
+                if package_check_allowed {
+                    let workspace = world.workspaces.iter().position(|ws| match &ws.kind {
+                        project_model::ProjectWorkspaceKind::Cargo { cargo, .. }
+                        | project_model::ProjectWorkspaceKind::DetachedFile {
+                            cargo: Some((cargo, _, _)),
+                            ..
+                        } => *cargo.workspace_root() == root,
+                        _ => false,
+                    });
+                    if let Some(idx) = workspace {
+                        world.flycheck[idx].restart_for_package(package, target);
+                    }
                 }
-            };
+            }
+
+            if !may_flycheck_workspace {
+                return Ok(());
+            }
+
+            // Trigger flychecks for all workspaces that depend on the saved file
+            // Crates containing or depending on the saved file
+            let crate_ids = world
+                .analysis
+                .crates_for(file_id)?
+                .into_iter()
+                .flat_map(|id| world.analysis.transitive_rev_deps(id))
+                .flatten()
+                .unique()
+                .collect::>();
+            tracing::debug!(?crate_ids, "flycheck crate ids");
             let crate_root_paths: Vec<_> = crate_ids
                 .iter()
                 .filter_map(|&crate_id| {
@@ -344,53 +369,41 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
                 })
                 .collect::>()?;
             let crate_root_paths: Vec<_> = crate_root_paths.iter().map(Deref::deref).collect();
+            tracing::debug!(?crate_root_paths, "flycheck crate roots");
 
             // Find all workspaces that have at least one target containing the saved file
-            let workspace_ids = world.workspaces.iter().enumerate().filter_map(|(idx, ws)| {
-                let package = match &ws.kind {
+            let workspace_ids =
+                world.workspaces.iter().enumerate().filter(|(_, ws)| match &ws.kind {
                     project_model::ProjectWorkspaceKind::Cargo { cargo, .. }
                     | project_model::ProjectWorkspaceKind::DetachedFile {
                         cargo: Some((cargo, _, _)),
                         ..
-                    } => cargo.packages().find_map(|pkg| {
-                        let has_target_with_root = cargo[pkg]
+                    } => cargo.packages().any(|pkg| {
+                        cargo[pkg]
                             .targets
                             .iter()
-                            .any(|&it| crate_root_paths.contains(&cargo[it].root.as_path()));
-                        has_target_with_root.then(|| cargo.package_flag(&cargo[pkg]))
+                            .any(|&it| crate_root_paths.contains(&cargo[it].root.as_path()))
                     }),
-                    project_model::ProjectWorkspaceKind::Json(project) => {
-                        if !project.crates().any(|(_, krate)| {
-                            crate_root_paths.contains(&krate.root_module.as_path())
-                        }) {
-                            return None;
-                        }
-                        None
-                    }
-                    project_model::ProjectWorkspaceKind::DetachedFile { .. } => return None,
-                };
-                Some((idx, package))
-            });
+                    project_model::ProjectWorkspaceKind::Json(project) => project
+                        .crates()
+                        .any(|(_, krate)| crate_root_paths.contains(&krate.root_module.as_path())),
+                    project_model::ProjectWorkspaceKind::DetachedFile { .. } => false,
+                });
 
             let saved_file = vfs_path.as_path().map(|p| p.to_owned());
 
             // Find and trigger corresponding flychecks
             'flychecks: for flycheck in world.flycheck.iter() {
-                for (id, package) in workspace_ids.clone() {
+                for (id, _) in workspace_ids.clone() {
                     if id == flycheck.id() {
                         updated = true;
-                        if may_flycheck_workspace {
-                            flycheck.restart_workspace(saved_file.clone())
-                        } else if let Some(package) = package {
-                            flycheck
-                                .restart_for_package(package, target.clone().map(TupleExt::head))
-                        }
+                        flycheck.restart_workspace(saved_file.clone());
                         continue 'flychecks;
                     }
                 }
             }
             // No specific flycheck was triggered, so let's trigger all of them.
-            if !updated && may_flycheck_workspace {
+            if !updated {
                 for flycheck in world.flycheck.iter() {
                     flycheck.restart_workspace(saved_file.clone());
                 }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
index 8f2bf80ea26d5..ed028f1d37b67 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
@@ -1,12 +1,7 @@
 //! This module is responsible for implementing handlers for Language Server
 //! Protocol. This module specifically handles requests.
 
-use std::{
-    fs,
-    io::Write as _,
-    ops::Not,
-    process::{self, Stdio},
-};
+use std::{fs, io::Write as _, ops::Not, process::Stdio};
 
 use anyhow::Context;
 
@@ -32,7 +27,7 @@ use paths::Utf8PathBuf;
 use project_model::{CargoWorkspace, ManifestPath, ProjectWorkspaceKind, TargetKind};
 use serde_json::json;
 use stdx::{format_to, never};
-use syntax::{algo, ast, AstNode, TextRange, TextSize};
+use syntax::{TextRange, TextSize};
 use triomphe::Arc;
 use vfs::{AbsPath, AbsPathBuf, FileId, VfsPath};
 
@@ -141,15 +136,13 @@ pub(crate) fn handle_memory_usage(state: &mut GlobalState, _: ()) -> anyhow::Res
     Ok(out)
 }
 
-pub(crate) fn handle_syntax_tree(
+pub(crate) fn handle_view_syntax_tree(
     snap: GlobalStateSnapshot,
-    params: lsp_ext::SyntaxTreeParams,
+    params: lsp_ext::ViewSyntaxTreeParams,
 ) -> anyhow::Result {
-    let _p = tracing::info_span!("handle_syntax_tree").entered();
+    let _p = tracing::info_span!("handle_view_syntax_tree").entered();
     let id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
-    let line_index = snap.file_line_index(id)?;
-    let text_range = params.range.and_then(|r| from_proto::text_range(&line_index, r).ok());
-    let res = snap.analysis.syntax_tree(id, text_range)?;
+    let res = snap.analysis.view_syntax_tree(id)?;
     Ok(res)
 }
 
@@ -441,29 +434,24 @@ pub(crate) fn handle_on_type_formatting(
     params: lsp_types::DocumentOnTypeFormattingParams,
 ) -> anyhow::Result>> {
     let _p = tracing::info_span!("handle_on_type_formatting").entered();
+    let char_typed = params.ch.chars().next().unwrap_or('\0');
+    if !snap.config.typing_trigger_chars().contains(char_typed) {
+        return Ok(None);
+    }
+
     let mut position = from_proto::file_position(&snap, params.text_document_position)?;
     let line_index = snap.file_line_index(position.file_id)?;
 
     // in `ide`, the `on_type` invariant is that
     // `text.char_at(position) == typed_char`.
     position.offset -= TextSize::of('.');
-    let char_typed = params.ch.chars().next().unwrap_or('\0');
 
     let text = snap.analysis.file_text(position.file_id)?;
     if stdx::never!(!text[usize::from(position.offset)..].starts_with(char_typed)) {
         return Ok(None);
     }
 
-    // We have an assist that inserts ` ` after typing `->` in `fn foo() ->{`,
-    // but it requires precise cursor positioning to work, and one can't
-    // position the cursor with on_type formatting. So, let's just toggle this
-    // feature off here, hoping that we'll enable it one day, 😿.
-    if char_typed == '>' {
-        return Ok(None);
-    }
-    let chars_to_exclude = snap.config.typing_exclude_chars();
-
-    let edit = snap.analysis.on_char_typed(position, char_typed, chars_to_exclude)?;
+    let edit = snap.analysis.on_char_typed(position, char_typed)?;
     let edit = match edit {
         Some(it) => it,
         None => return Ok(None),
@@ -933,39 +921,32 @@ pub(crate) fn handle_runnables(
     let offset = params.position.and_then(|it| from_proto::offset(&line_index, it).ok());
     let target_spec = TargetSpec::for_file(&snap, file_id)?;
 
-    let expect_test = match offset {
-        Some(offset) => {
-            let source_file = snap.analysis.parse(file_id)?;
-            algo::find_node_at_offset::(source_file.syntax(), offset)
-                .and_then(|it| it.path()?.segment()?.name_ref())
-                .map_or(false, |it| it.text() == "expect" || it.text() == "expect_file")
-        }
-        None => false,
-    };
-
     let mut res = Vec::new();
     for runnable in snap.analysis.runnables(file_id)? {
-        if should_skip_for_offset(&runnable, offset) {
-            continue;
-        }
-        if should_skip_target(&runnable, target_spec.as_ref()) {
+        if should_skip_for_offset(&runnable, offset)
+            || should_skip_target(&runnable, target_spec.as_ref())
+        {
             continue;
         }
+
+        let update_test = runnable.update_test;
         if let Some(mut runnable) = to_proto::runnable(&snap, runnable)? {
-            if expect_test {
-                if let lsp_ext::RunnableArgs::Cargo(r) = &mut runnable.args {
-                    runnable.label = format!("{} + expect", runnable.label);
-                    r.environment.insert("UPDATE_EXPECT".to_owned(), "1".to_owned());
-                    if let Some(TargetSpec::Cargo(CargoTargetSpec {
-                        sysroot_root: Some(sysroot_root),
-                        ..
-                    })) = &target_spec
-                    {
-                        r.environment
-                            .insert("RUSTC_TOOLCHAIN".to_owned(), sysroot_root.to_string());
-                    }
-                }
+            if let Some(runnable) =
+                to_proto::make_update_runnable(&runnable, &update_test.label(), &update_test.env())
+            {
+                res.push(runnable);
             }
+
+            if let lsp_ext::RunnableArgs::Cargo(r) = &mut runnable.args {
+                if let Some(TargetSpec::Cargo(CargoTargetSpec {
+                    sysroot_root: Some(sysroot_root),
+                    ..
+                })) = &target_spec
+                {
+                    r.environment.insert("RUSTC_TOOLCHAIN".to_owned(), sysroot_root.to_string());
+                }
+            };
+
             res.push(runnable);
         }
     }
@@ -1173,10 +1154,7 @@ pub(crate) fn handle_completion_resolve(
             .resolve_completion_edits(
                 &forced_resolve_completions_config,
                 position,
-                resolve_data
-                    .imports
-                    .into_iter()
-                    .map(|import| (import.full_import_path, import.imported_name)),
+                resolve_data.imports.into_iter().map(|import| import.full_import_path),
             )?
             .into_iter()
             .flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel)))
@@ -1831,6 +1809,7 @@ pub(crate) fn handle_call_hierarchy_outgoing(
     let doc = TextDocumentIdentifier::new(item.uri);
     let frange = from_proto::file_range(&snap, &doc, item.selection_range)?;
     let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
+    let line_index = snap.file_line_index(fpos.file_id)?;
 
     let config = snap.config.call_hierarchy();
     let call_items = match snap.analysis.outgoing_calls(config, fpos)? {
@@ -1841,8 +1820,6 @@ pub(crate) fn handle_call_hierarchy_outgoing(
     let mut res = vec![];
 
     for call_item in call_items.into_iter() {
-        let file_id = call_item.target.file_id;
-        let line_index = snap.file_line_index(file_id)?;
         let item = to_proto::call_hierarchy_item(&snap, call_item.target)?;
         res.push(CallHierarchyOutgoingCall {
             to: item,
@@ -2148,6 +2125,7 @@ fn runnable_action_links(
     }
 
     let title = runnable.title();
+    let update_test = runnable.update_test;
     let r = to_proto::runnable(snap, runnable).ok()??;
 
     let mut group = lsp_ext::CommandLinkGroup::default();
@@ -2159,7 +2137,15 @@ fn runnable_action_links(
 
     if hover_actions_config.debug && client_commands_config.debug_single {
         let dbg_command = to_proto::command::debug_single(&r);
-        group.commands.push(to_command_link(dbg_command, r.label));
+        group.commands.push(to_command_link(dbg_command, r.label.clone()));
+    }
+
+    if hover_actions_config.update_test && client_commands_config.run_single {
+        let label = update_test.label();
+        if let Some(r) = to_proto::make_update_runnable(&r, &label, &update_test.env()) {
+            let update_command = to_proto::command::run_single(&r, label.unwrap().as_str());
+            group.commands.push(to_command_link(update_command, r.label.clone()));
+        }
     }
 
     Some(group)
@@ -2243,10 +2229,31 @@ fn run_rustfmt(
     let line_index = snap.file_line_index(file_id)?;
     let source_root_id = snap.analysis.source_root_id(file_id).ok();
 
+    // try to chdir to the file so we can respect `rustfmt.toml`
+    // FIXME: use `rustfmt --config-path` once
+    // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed
+    let current_dir = match text_document.uri.to_file_path() {
+        Ok(mut path) => {
+            // pop off file name
+            if path.pop() && path.is_dir() {
+                path
+            } else {
+                std::env::current_dir()?
+            }
+        }
+        Err(_) => {
+            tracing::error!(
+                text_document = ?text_document.uri,
+                "Unable to get path, rustfmt.toml might be ignored"
+            );
+            std::env::current_dir()?
+        }
+    };
+
     let mut command = match snap.config.rustfmt(source_root_id) {
         RustfmtConfig::Rustfmt { extra_args, enable_range_formatting } => {
             // FIXME: Set RUSTUP_TOOLCHAIN
-            let mut cmd = process::Command::new(toolchain::Tool::Rustfmt.path());
+            let mut cmd = toolchain::command(toolchain::Tool::Rustfmt.path(), current_dir);
             cmd.envs(snap.config.extra_env(source_root_id));
             cmd.args(extra_args);
 
@@ -2300,9 +2307,9 @@ fn run_rustfmt(
                     } else {
                         cmd
                     };
-                    process::Command::new(cmd_path)
+                    toolchain::command(cmd_path, current_dir)
                 }
-                _ => process::Command::new(cmd),
+                _ => toolchain::command(cmd, current_dir),
             };
 
             cmd.envs(snap.config.extra_env(source_root_id));
@@ -2311,36 +2318,21 @@ fn run_rustfmt(
         }
     };
 
-    tracing::debug!(?command, "created format command");
+    let output = {
+        let _p = tracing::info_span!("rustfmt", ?command).entered();
 
-    // try to chdir to the file so we can respect `rustfmt.toml`
-    // FIXME: use `rustfmt --config-path` once
-    // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed
-    match text_document.uri.to_file_path() {
-        Ok(mut path) => {
-            // pop off file name
-            if path.pop() && path.is_dir() {
-                command.current_dir(path);
-            }
-        }
-        Err(_) => {
-            tracing::error!(
-                text_document = ?text_document.uri,
-                "Unable to get path, rustfmt.toml might be ignored"
-            );
-        }
-    }
+        let mut rustfmt = command
+            .stdin(Stdio::piped())
+            .stdout(Stdio::piped())
+            .stderr(Stdio::piped())
+            .spawn()
+            .context(format!("Failed to spawn {command:?}"))?;
 
-    let mut rustfmt = command
-        .stdin(Stdio::piped())
-        .stdout(Stdio::piped())
-        .stderr(Stdio::piped())
-        .spawn()
-        .context(format!("Failed to spawn {command:?}"))?;
+        rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?;
 
-    rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?;
+        rustfmt.wait_with_output()?
+    };
 
-    let output = rustfmt.wait_with_output()?;
     let captured_stdout = String::from_utf8(output.stdout)?;
     let captured_stderr = String::from_utf8(output.stderr).unwrap_or_default();
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
index 8946c7acb9381..5cdc51a1c1995 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -174,6 +174,10 @@ fn integrated_completion_benchmark() {
             limit: None,
             add_semicolon_to_unit: true,
             fields_to_resolve: CompletionFieldsToResolve::empty(),
+            exclude_flyimport: vec![],
+            exclude_traits: &[],
+            enable_auto_await: true,
+            enable_auto_iter: true,
         };
         let position =
             FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
@@ -222,6 +226,10 @@ fn integrated_completion_benchmark() {
             limit: None,
             add_semicolon_to_unit: true,
             fields_to_resolve: CompletionFieldsToResolve::empty(),
+            exclude_flyimport: vec![],
+            exclude_traits: &[],
+            enable_auto_await: true,
+            enable_auto_iter: true,
         };
         let position =
             FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
@@ -268,6 +276,10 @@ fn integrated_completion_benchmark() {
             limit: None,
             add_semicolon_to_unit: true,
             fields_to_resolve: CompletionFieldsToResolve::empty(),
+            exclude_flyimport: vec![],
+            exclude_traits: &[],
+            enable_auto_await: true,
+            enable_auto_iter: true,
         };
         let position =
             FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
index e7f5a7f5e7820..1221f7c7012e7 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
@@ -47,9 +47,10 @@ use self::lsp::ext as lsp_ext;
 #[cfg(test)]
 mod integrated_benchmarks;
 
-use ide::{CompletionItem, CompletionRelevance};
+use hir::Mutability;
+use ide::{CompletionItem, CompletionItemRefMode, CompletionRelevance};
 use serde::de::DeserializeOwned;
-use tenthash::TentHasher;
+use tenthash::TentHash;
 
 pub use crate::{
     lsp::capabilities::server_capabilities, main_loop::main_loop, reload::ws_to_crate_graph,
@@ -65,7 +66,7 @@ pub fn from_json(
 }
 
 fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; 20] {
-    fn hash_completion_relevance(hasher: &mut TentHasher, relevance: &CompletionRelevance) {
+    fn hash_completion_relevance(hasher: &mut TentHash, relevance: &CompletionRelevance) {
         use ide_completion::{
             CompletionRelevancePostfixMatch, CompletionRelevanceReturnType,
             CompletionRelevanceTypeMatch,
@@ -78,67 +79,97 @@ fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8;
             u8::from(relevance.requires_import),
             u8::from(relevance.is_private_editable),
         ]);
-        if let Some(type_match) = &relevance.type_match {
-            let label = match type_match {
-                CompletionRelevanceTypeMatch::CouldUnify => "could_unify",
-                CompletionRelevanceTypeMatch::Exact => "exact",
-            };
-            hasher.update(label);
+
+        match relevance.type_match {
+            None => hasher.update([0u8]),
+            Some(CompletionRelevanceTypeMatch::CouldUnify) => hasher.update([1u8]),
+            Some(CompletionRelevanceTypeMatch::Exact) => hasher.update([2u8]),
         }
+
+        hasher.update([u8::from(relevance.trait_.is_some())]);
         if let Some(trait_) = &relevance.trait_ {
             hasher.update([u8::from(trait_.is_op_method), u8::from(trait_.notable_trait)]);
         }
-        if let Some(postfix_match) = &relevance.postfix_match {
-            let label = match postfix_match {
-                CompletionRelevancePostfixMatch::NonExact => "non_exact",
-                CompletionRelevancePostfixMatch::Exact => "exact",
-            };
-            hasher.update(label);
+
+        match relevance.postfix_match {
+            None => hasher.update([0u8]),
+            Some(CompletionRelevancePostfixMatch::NonExact) => hasher.update([1u8]),
+            Some(CompletionRelevancePostfixMatch::Exact) => hasher.update([2u8]),
         }
+
+        hasher.update([u8::from(relevance.function.is_some())]);
         if let Some(function) = &relevance.function {
             hasher.update([u8::from(function.has_params), u8::from(function.has_self_param)]);
-            let label = match function.return_type {
-                CompletionRelevanceReturnType::Other => "other",
-                CompletionRelevanceReturnType::DirectConstructor => "direct_constructor",
-                CompletionRelevanceReturnType::Constructor => "constructor",
-                CompletionRelevanceReturnType::Builder => "builder",
+            let discriminant: u8 = match function.return_type {
+                CompletionRelevanceReturnType::Other => 0,
+                CompletionRelevanceReturnType::DirectConstructor => 1,
+                CompletionRelevanceReturnType::Constructor => 2,
+                CompletionRelevanceReturnType::Builder => 3,
             };
-            hasher.update(label);
+            hasher.update([discriminant]);
         }
     }
 
-    let mut hasher = TentHasher::new();
+    let mut hasher = TentHash::new();
     hasher.update([
         u8::from(is_ref_completion),
         u8::from(item.is_snippet),
         u8::from(item.deprecated),
         u8::from(item.trigger_call_info),
     ]);
+
+    hasher.update(item.label.primary.len().to_ne_bytes());
     hasher.update(&item.label.primary);
+
+    hasher.update([u8::from(item.label.detail_left.is_some())]);
     if let Some(label_detail) = &item.label.detail_left {
+        hasher.update(label_detail.len().to_ne_bytes());
         hasher.update(label_detail);
     }
+
+    hasher.update([u8::from(item.label.detail_right.is_some())]);
     if let Some(label_detail) = &item.label.detail_right {
+        hasher.update(label_detail.len().to_ne_bytes());
         hasher.update(label_detail);
     }
+
     // NB: do not hash edits or source range, as those may change between the time the client sends the resolve request
     // and the time it receives it: some editors do allow changing the buffer between that, leading to ranges being different.
     //
     // Documentation hashing is skipped too, as it's a large blob to process,
     // while not really making completion properties more unique as they are already.
-    hasher.update(item.kind.tag());
+
+    let kind_tag = item.kind.tag();
+    hasher.update(kind_tag.len().to_ne_bytes());
+    hasher.update(kind_tag);
+
+    hasher.update(item.lookup.len().to_ne_bytes());
     hasher.update(&item.lookup);
+
+    hasher.update([u8::from(item.detail.is_some())]);
     if let Some(detail) = &item.detail {
+        hasher.update(detail.len().to_ne_bytes());
         hasher.update(detail);
     }
+
     hash_completion_relevance(&mut hasher, &item.relevance);
-    if let Some((mutability, text_size)) = &item.ref_match {
-        hasher.update(mutability.as_keyword_for_ref());
-        hasher.update(u32::from(*text_size).to_le_bytes());
+
+    hasher.update([u8::from(item.ref_match.is_some())]);
+    if let Some((ref_mode, text_size)) = &item.ref_match {
+        let discriminant = match ref_mode {
+            CompletionItemRefMode::Reference(Mutability::Shared) => 0u8,
+            CompletionItemRefMode::Reference(Mutability::Mut) => 1u8,
+            CompletionItemRefMode::Dereference => 2u8,
+        };
+        hasher.update([discriminant]);
+        hasher.update(u32::from(*text_size).to_ne_bytes());
     }
-    for (import_path, import_name) in &item.import_to_add {
+
+    hasher.update(item.import_to_add.len().to_ne_bytes());
+    for import_path in &item.import_to_add {
+        hasher.update(import_path.len().to_ne_bytes());
         hasher.update(import_path);
-        hasher.update(import_name);
     }
+
     hasher.finalize()
 }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs
index c0173d9c2470f..ca4372aa83f8d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs
@@ -1,5 +1,9 @@
 //! rust-analyzer extensions to the LSP.
 
+// Note when adding new resolve payloads, add a #[serde(default)] on boolean fields as some clients
+// might strip `false` values from the JSON payload due to their reserialization logic turning false
+// into null which will then cause them to be omitted in the resolve request. See https://github.com/rust-lang/rust-analyzer/issues/18767
+
 #![allow(clippy::disallowed_types)]
 
 use std::ops;
@@ -104,19 +108,18 @@ impl Request for RebuildProcMacros {
     const METHOD: &'static str = "rust-analyzer/rebuildProcMacros";
 }
 
-pub enum SyntaxTree {}
+pub enum ViewSyntaxTree {}
 
-impl Request for SyntaxTree {
-    type Params = SyntaxTreeParams;
+impl Request for ViewSyntaxTree {
+    type Params = ViewSyntaxTreeParams;
     type Result = String;
-    const METHOD: &'static str = "rust-analyzer/syntaxTree";
+    const METHOD: &'static str = "rust-analyzer/viewSyntaxTree";
 }
 
 #[derive(Deserialize, Serialize, Debug)]
 #[serde(rename_all = "camelCase")]
-pub struct SyntaxTreeParams {
+pub struct ViewSyntaxTreeParams {
     pub text_document: TextDocumentIdentifier,
-    pub range: Option,
 }
 
 pub enum ViewHir {}
@@ -427,14 +430,14 @@ impl Request for Runnables {
     const METHOD: &'static str = "experimental/runnables";
 }
 
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(rename_all = "camelCase")]
 pub struct RunnablesParams {
     pub text_document: TextDocumentIdentifier,
     pub position: Option,
 }
 
-#[derive(Deserialize, Serialize, Debug)]
+#[derive(Deserialize, Serialize, Debug, Clone)]
 #[serde(rename_all = "camelCase")]
 pub struct Runnable {
     pub label: String,
@@ -444,7 +447,7 @@ pub struct Runnable {
     pub args: RunnableArgs,
 }
 
-#[derive(Deserialize, Serialize, Debug)]
+#[derive(Deserialize, Serialize, Debug, Clone)]
 #[serde(rename_all = "camelCase")]
 #[serde(untagged)]
 pub enum RunnableArgs {
@@ -452,14 +455,14 @@ pub enum RunnableArgs {
     Shell(ShellRunnableArgs),
 }
 
-#[derive(Serialize, Deserialize, Debug)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(rename_all = "lowercase")]
 pub enum RunnableKind {
     Cargo,
     Shell,
 }
 
-#[derive(Deserialize, Serialize, Debug)]
+#[derive(Deserialize, Serialize, Debug, Clone)]
 #[serde(rename_all = "camelCase")]
 pub struct CargoRunnableArgs {
     #[serde(skip_serializing_if = "FxHashMap::is_empty")]
@@ -475,7 +478,7 @@ pub struct CargoRunnableArgs {
     pub executable_args: Vec,
 }
 
-#[derive(Deserialize, Serialize, Debug)]
+#[derive(Deserialize, Serialize, Debug, Clone)]
 #[serde(rename_all = "camelCase")]
 pub struct ShellRunnableArgs {
     #[serde(skip_serializing_if = "FxHashMap::is_empty")]
@@ -829,6 +832,7 @@ pub struct CompletionResolveData {
     pub version: Option,
     #[serde(skip_serializing_if = "Option::is_none", default)]
     pub trigger_character: Option,
+    #[serde(default)]
     pub for_ref: bool,
     pub hash: String,
 }
@@ -846,7 +850,6 @@ pub struct InlayHintResolveData {
 #[derive(Debug, Serialize, Deserialize)]
 pub struct CompletionImport {
     pub full_import_path: String,
-    pub imported_name: String,
 }
 
 #[derive(Debug, Deserialize, Default)]
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs
index 991c10743f7a6..3c21e19925257 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs
@@ -24,7 +24,7 @@ macro_rules! define_semantic_token_types {
         }
 
         pub(crate) const SUPPORTED_TYPES: &[SemanticTokenType] = &[
-            $(SemanticTokenType::$standard,)*
+            $(self::types::$standard,)*
             $(self::types::$custom),*
         ];
 
@@ -32,7 +32,7 @@ macro_rules! define_semantic_token_types {
             use self::types::*;
             $(
                 if token == $custom {
-                    None $(.or(Some(SemanticTokenType::$fallback)))?
+                    None $(.or(Some(self::types::$fallback)))?
                 } else
             )*
             { Some(token )}
@@ -60,6 +60,7 @@ define_semantic_token_types![
         STRUCT,
         TYPE_PARAMETER,
         VARIABLE,
+        TYPE,
     }
 
     custom {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
index 05e93b4e6acf0..bff53cf98b7b8 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -11,8 +11,8 @@ use ide::{
     Annotation, AnnotationKind, Assist, AssistKind, Cancellable, CompletionFieldsToResolve,
     CompletionItem, CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange,
     FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel,
-    InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayKind, Markup,
-    NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp,
+    InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayKind, LazyProperty,
+    Markup, NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp,
     SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize,
 };
 use ide_db::{assists, rust_doc::format_docs, FxHasher};
@@ -20,6 +20,7 @@ use itertools::Itertools;
 use paths::{Utf8Component, Utf8Prefix};
 use semver::VersionReq;
 use serde_json::to_value;
+use syntax::SmolStr;
 use vfs::AbsPath;
 
 use crate::{
@@ -393,10 +394,7 @@ fn completion_item(
             item.import_to_add
                 .clone()
                 .into_iter()
-                .map(|(import_path, import_name)| lsp_ext::CompletionImport {
-                    full_import_path: import_path,
-                    imported_name: import_name,
-                })
+                .map(|import_path| lsp_ext::CompletionImport { full_import_path: import_path })
                 .collect()
         } else {
             Vec::new()
@@ -546,7 +544,17 @@ pub(crate) fn inlay_hint(
     file_id: FileId,
     mut inlay_hint: InlayHint,
 ) -> Cancellable {
-    let resolve_range_and_hash = inlay_hint.needs_resolve().map(|range| {
+    let hint_needs_resolve = |hint: &InlayHint| -> Option {
+        hint.resolve_parent.filter(|_| {
+            hint.text_edit.as_ref().is_some_and(LazyProperty::is_lazy)
+                || hint.label.parts.iter().any(|part| {
+                    part.linked_location.as_ref().is_some_and(LazyProperty::is_lazy)
+                        || part.tooltip.as_ref().is_some_and(LazyProperty::is_lazy)
+                })
+        })
+    };
+
+    let resolve_range_and_hash = hint_needs_resolve(&inlay_hint).map(|range| {
         (
             range,
             std::hash::BuildHasher::hash_one(
@@ -557,19 +565,21 @@ pub(crate) fn inlay_hint(
     });
 
     let mut something_to_resolve = false;
-    let text_edits = if snap
-        .config
-        .visual_studio_code_version()
-        // https://github.com/microsoft/vscode/issues/193124
-        .map_or(true, |version| VersionReq::parse(">=1.86.0").unwrap().matches(version))
-        && resolve_range_and_hash.is_some()
-        && fields_to_resolve.resolve_text_edits
-    {
-        something_to_resolve |= inlay_hint.text_edit.is_some();
-        None
-    } else {
-        inlay_hint.text_edit.take().map(|it| text_edit_vec(line_index, it))
-    };
+    let text_edits = inlay_hint
+        .text_edit
+        .take()
+        .and_then(|it| match it {
+            LazyProperty::Computed(it) => Some(it),
+            LazyProperty::Lazy => {
+                something_to_resolve |=
+                    snap.config.visual_studio_code_version().is_none_or(|version| {
+                        VersionReq::parse(">=1.86.0").unwrap().matches(version)
+                    }) && resolve_range_and_hash.is_some()
+                        && fields_to_resolve.resolve_text_edits;
+                None
+            }
+        })
+        .map(|it| text_edit_vec(line_index, it));
     let (label, tooltip) = inlay_hint_label(
         snap,
         fields_to_resolve,
@@ -622,22 +632,23 @@ fn inlay_hint_label(
     let (label, tooltip) = match &*label.parts {
         [InlayHintLabelPart { linked_location: None, .. }] => {
             let InlayHintLabelPart { text, tooltip, .. } = label.parts.pop().unwrap();
-            let hint_tooltip = if needs_resolve && fields_to_resolve.resolve_hint_tooltip {
-                *something_to_resolve |= tooltip.is_some();
-                None
-            } else {
-                match tooltip {
-                    Some(ide::InlayTooltip::String(s)) => {
-                        Some(lsp_types::InlayHintTooltip::String(s))
-                    }
-                    Some(ide::InlayTooltip::Markdown(s)) => {
-                        Some(lsp_types::InlayHintTooltip::MarkupContent(lsp_types::MarkupContent {
-                            kind: lsp_types::MarkupKind::Markdown,
-                            value: s,
-                        }))
-                    }
-                    None => None,
+            let tooltip = tooltip.and_then(|it| match it {
+                LazyProperty::Computed(it) => Some(it),
+                LazyProperty::Lazy => {
+                    *something_to_resolve |=
+                        needs_resolve && fields_to_resolve.resolve_hint_tooltip;
+                    None
+                }
+            });
+            let hint_tooltip = match tooltip {
+                Some(ide::InlayTooltip::String(s)) => Some(lsp_types::InlayHintTooltip::String(s)),
+                Some(ide::InlayTooltip::Markdown(s)) => {
+                    Some(lsp_types::InlayHintTooltip::MarkupContent(lsp_types::MarkupContent {
+                        kind: lsp_types::MarkupKind::Markdown,
+                        value: s,
+                    }))
                 }
+                None => None,
             };
             (lsp_types::InlayHintLabel::String(text), hint_tooltip)
         }
@@ -646,31 +657,38 @@ fn inlay_hint_label(
                 .parts
                 .into_iter()
                 .map(|part| {
-                    let tooltip = if needs_resolve && fields_to_resolve.resolve_label_tooltip {
-                        *something_to_resolve |= part.tooltip.is_some();
-                        None
-                    } else {
-                        match part.tooltip {
-                            Some(ide::InlayTooltip::String(s)) => {
-                                Some(lsp_types::InlayHintLabelPartTooltip::String(s))
-                            }
-                            Some(ide::InlayTooltip::Markdown(s)) => {
-                                Some(lsp_types::InlayHintLabelPartTooltip::MarkupContent(
-                                    lsp_types::MarkupContent {
-                                        kind: lsp_types::MarkupKind::Markdown,
-                                        value: s,
-                                    },
-                                ))
-                            }
-                            None => None,
+                    let tooltip = part.tooltip.and_then(|it| match it {
+                        LazyProperty::Computed(it) => Some(it),
+                        LazyProperty::Lazy => {
+                            *something_to_resolve |= fields_to_resolve.resolve_label_tooltip;
+                            None
                         }
+                    });
+                    let tooltip = match tooltip {
+                        Some(ide::InlayTooltip::String(s)) => {
+                            Some(lsp_types::InlayHintLabelPartTooltip::String(s))
+                        }
+                        Some(ide::InlayTooltip::Markdown(s)) => {
+                            Some(lsp_types::InlayHintLabelPartTooltip::MarkupContent(
+                                lsp_types::MarkupContent {
+                                    kind: lsp_types::MarkupKind::Markdown,
+                                    value: s,
+                                },
+                            ))
+                        }
+                        None => None,
                     };
-                    let location = if needs_resolve && fields_to_resolve.resolve_label_location {
-                        *something_to_resolve |= part.linked_location.is_some();
-                        None
-                    } else {
-                        part.linked_location.map(|range| location(snap, range)).transpose()?
-                    };
+                    let location = part
+                        .linked_location
+                        .and_then(|it| match it {
+                            LazyProperty::Computed(it) => Some(it),
+                            LazyProperty::Lazy => {
+                                *something_to_resolve |= fields_to_resolve.resolve_label_location;
+                                None
+                            }
+                        })
+                        .map(|range| location(snap, range))
+                        .transpose()?;
                     Ok(lsp_types::InlayHintLabelPart {
                         value: part.text,
                         tooltip,
@@ -1567,6 +1585,7 @@ pub(crate) fn code_lens(
             let line_index = snap.file_line_index(run.nav.file_id)?;
             let annotation_range = range(&line_index, annotation.range);
 
+            let update_test = run.update_test;
             let title = run.title();
             let can_debug = match run.kind {
                 ide::RunnableKind::DocTest { .. } => false,
@@ -1602,6 +1621,18 @@ pub(crate) fn code_lens(
                             data: None,
                         })
                     }
+                    if lens_config.update_test && client_commands_config.run_single {
+                        let label = update_test.label();
+                        let env = update_test.env();
+                        if let Some(r) = make_update_runnable(&r, &label, &env) {
+                            let command = command::run_single(&r, label.unwrap().as_str());
+                            acc.push(lsp_types::CodeLens {
+                                range: annotation_range,
+                                command: Some(command),
+                                data: None,
+                            })
+                        }
+                    }
                 }
 
                 if lens_config.interpret {
@@ -1786,7 +1817,7 @@ pub(crate) mod command {
 
     pub(crate) fn debug_single(runnable: &lsp_ext::Runnable) -> lsp_types::Command {
         lsp_types::Command {
-            title: "Debug".into(),
+            title: "⚙\u{fe0e} Debug".into(),
             command: "rust-analyzer.debugSingle".into(),
             arguments: Some(vec![to_value(runnable).unwrap()]),
         }
@@ -1838,6 +1869,28 @@ pub(crate) mod command {
     }
 }
 
+pub(crate) fn make_update_runnable(
+    runnable: &lsp_ext::Runnable,
+    label: &Option,
+    env: &[(&str, &str)],
+) -> Option {
+    if !matches!(runnable.args, lsp_ext::RunnableArgs::Cargo(_)) {
+        return None;
+    }
+    let label = label.as_ref()?;
+
+    let mut runnable = runnable.clone();
+    runnable.label = format!("{} + {}", runnable.label, label);
+
+    let lsp_ext::RunnableArgs::Cargo(r) = &mut runnable.args else {
+        unreachable!();
+    };
+
+    r.environment.extend(env.iter().map(|(k, v)| (k.to_string(), v.to_string())));
+
+    Some(runnable)
+}
+
 pub(crate) fn implementation_title(count: usize) -> String {
     if count == 1 {
         "1 implementation".into()
@@ -1958,7 +2011,7 @@ fn bar(_: usize) {}
 
     #[track_caller]
     fn check_rendered_snippets_in_source(
-        ra_fixture: &str,
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
         edit: TextEdit,
         snippets: SnippetEdit,
         expect: Expect,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
index d97d96d54a014..ebc65373b5226 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
@@ -253,6 +253,11 @@ impl GlobalState {
         &self,
         inbox: &Receiver,
     ) -> Result, crossbeam_channel::RecvError> {
+        // Make sure we reply to formatting requests ASAP so the editor doesn't block
+        if let Ok(task) = self.fmt_pool.receiver.try_recv() {
+            return Ok(Some(Event::Task(task)));
+        }
+
         select! {
             recv(inbox) -> msg =>
                 return Ok(msg.ok().map(Event::Lsp)),
@@ -320,26 +325,30 @@ impl GlobalState {
                 }
 
                 for progress in prime_caches_progress {
-                    let (state, message, fraction);
+                    let (state, message, fraction, title);
                     match progress {
                         PrimeCachesProgress::Begin => {
                             state = Progress::Begin;
                             message = None;
                             fraction = 0.0;
+                            title = "Indexing";
                         }
                         PrimeCachesProgress::Report(report) => {
                             state = Progress::Report;
+                            title = report.work_type;
 
-                            message = match &report.crates_currently_indexing[..] {
+                            message = match &*report.crates_currently_indexing {
                                 [crate_name] => Some(format!(
-                                    "{}/{} ({crate_name})",
-                                    report.crates_done, report.crates_total
+                                    "{}/{} ({})",
+                                    report.crates_done,
+                                    report.crates_total,
+                                    crate_name.as_str(),
                                 )),
                                 [crate_name, rest @ ..] => Some(format!(
                                     "{}/{} ({} + {} more)",
                                     report.crates_done,
                                     report.crates_total,
-                                    crate_name,
+                                    crate_name.as_str(),
                                     rest.len()
                                 )),
                                 _ => None,
@@ -351,6 +360,7 @@ impl GlobalState {
                             state = Progress::End;
                             message = None;
                             fraction = 1.0;
+                            title = "Indexing";
 
                             self.prime_caches_queue.op_completed(());
                             if cancelled {
@@ -360,7 +370,13 @@ impl GlobalState {
                         }
                     };
 
-                    self.report_progress("Indexing", state, message, Some(fraction), None);
+                    self.report_progress(
+                        title,
+                        state,
+                        message,
+                        Some(fraction),
+                        Some("rustAnalyzer/cachePriming".to_owned()),
+                    );
                 }
             }
             Event::Vfs(message) => {
@@ -744,7 +760,8 @@ impl GlobalState {
                             DiscoverProjectParam::Path(it) => DiscoverArgument::Path(it),
                         };
 
-                        let handle = discover.spawn(arg).unwrap();
+                        let handle =
+                            discover.spawn(arg, &std::env::current_dir().unwrap()).unwrap();
                         self.discover_handle = Some(handle);
                     }
                 }
@@ -1144,7 +1161,7 @@ impl GlobalState {
             .on::(handlers::handle_workspace_symbol)
             .on::(handlers::handle_ssr)
             .on::(handlers::handle_view_recursive_memory_layout)
-            .on::(handlers::handle_syntax_tree)
+            .on::(handlers::handle_view_syntax_tree)
             .on::(handlers::handle_view_hir)
             .on::(handlers::handle_view_mir)
             .on::(handlers::handle_interpret_function)
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index 3444773695b4e..d18e577047749 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -24,7 +24,7 @@ use ide_db::{
 use itertools::Itertools;
 use load_cargo::{load_proc_macro, ProjectFolders};
 use lsp_types::FileSystemWatcher;
-use proc_macro_api::ProcMacroServer;
+use proc_macro_api::ProcMacroClient;
 use project_model::{ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, WorkspaceBuildScripts};
 use stdx::{format_to, thread::ThreadIntent};
 use triomphe::Arc;
@@ -630,13 +630,10 @@ impl GlobalState {
                 };
 
                 let env: FxHashMap<_, _> = match &ws.kind {
-                    ProjectWorkspaceKind::Cargo { cargo_config_extra_env, .. }
-                    | ProjectWorkspaceKind::DetachedFile {
-                        cargo: Some(_),
-                        cargo_config_extra_env,
-                        ..
-                    } => cargo_config_extra_env
-                        .iter()
+                    ProjectWorkspaceKind::Cargo { cargo, .. }
+                    | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, ..)), .. } => cargo
+                        .env()
+                        .into_iter()
                         .chain(self.config.extra_env(None))
                         .map(|(a, b)| (a.clone(), b.clone()))
                         .chain(
@@ -650,7 +647,7 @@ impl GlobalState {
                 };
                 info!("Using proc-macro server at {path}");
 
-                ProcMacroServer::spawn(&path, &env).map_err(|err| {
+                ProcMacroClient::spawn(&path, &env).map_err(|err| {
                     tracing::error!(
                         "Failed to run proc-macro server from path {path}, error: {err:?}",
                     );
@@ -704,8 +701,7 @@ impl GlobalState {
 
         let (crate_graph, proc_macro_paths, ws_data) = {
             // Create crate graph from all the workspaces
-            let vfs = &mut self.vfs.write().0;
-
+            let vfs = &self.vfs.read().0;
             let load = |path: &AbsPath| {
                 let vfs_path = vfs::VfsPath::from(path.to_path_buf());
                 self.crate_graph_file_dependencies.insert(vfs_path.clone());
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs
index b4aa73d2780d6..b28567fe09b58 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs
@@ -62,7 +62,6 @@ pub(crate) struct CargoTargetSpec {
 
 #[derive(Clone, Debug)]
 pub(crate) struct ProjectJsonTargetSpec {
-    pub(crate) crate_id: CrateId,
     pub(crate) label: String,
     pub(crate) target_kind: TargetKind,
     pub(crate) shell_runnables: Vec,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs
index 2bcd8505e8100..c5de69bb9fc83 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs
@@ -1,6 +1,8 @@
 //! A thin wrapper around [`stdx::thread::Pool`] which threads a sender through spawned jobs.
 //! It is used in [`crate::global_state::GlobalState`] throughout the main loop.
 
+use std::panic::UnwindSafe;
+
 use crossbeam_channel::Sender;
 use stdx::thread::{Pool, ThreadIntent};
 
@@ -18,7 +20,7 @@ impl TaskPool {
 
     pub(crate) fn spawn(&mut self, intent: ThreadIntent, task: F)
     where
-        F: FnOnce() -> T + Send + 'static,
+        F: FnOnce() -> T + Send + UnwindSafe + 'static,
         T: Send + 'static,
     {
         self.pool.spawn(intent, {
@@ -29,7 +31,7 @@ impl TaskPool {
 
     pub(crate) fn spawn_with_sender(&mut self, intent: ThreadIntent, task: F)
     where
-        F: FnOnce(Sender) + Send + 'static,
+        F: FnOnce(Sender) + Send + UnwindSafe + 'static,
         T: Send + 'static,
     {
         self.pool.spawn(intent, {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs
index 2fd52547336e7..3edfb812cf5cf 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs
@@ -1,8 +1,6 @@
 //! This module provides the functionality needed to run `cargo test` in a background
 //! thread and report the result of each test in a channel.
 
-use std::process::Command;
-
 use crossbeam_channel::Sender;
 use paths::AbsPath;
 use serde::Deserialize as _;
@@ -20,7 +18,11 @@ pub(crate) enum TestState {
     Started,
     Ok,
     Ignored,
-    Failed { stdout: String },
+    Failed {
+        // the stdout field is not always present depending on cargo test flags
+        #[serde(skip_serializing_if = "String::is_empty", default)]
+        stdout: String,
+    },
 }
 
 #[derive(Debug, Deserialize)]
@@ -78,7 +80,7 @@ impl CargoTestHandle {
         test_target: TestTarget,
         sender: Sender,
     ) -> std::io::Result {
-        let mut cmd = Command::new(Tool::Cargo.path());
+        let mut cmd = toolchain::command(Tool::Cargo.path(), root);
         cmd.env("RUSTC_BOOTSTRAP", "1");
         cmd.arg("test");
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/cli.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/cli.rs
index fba5466691289..4ef930e9854ea 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/cli.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/cli.rs
@@ -43,89 +43,93 @@ mod tests {
     expect![[r#"
         {"id":2,"type":"vertex","label":"foldingRangeResult","result":[{"startLine":2,"startCharacter":43,"endLine":6,"endCharacter":1},{"startLine":3,"startCharacter":19,"endLine":5,"endCharacter":5},{"startLine":9,"startCharacter":10,"endLine":12,"endCharacter":1}]}
         {"id":3,"type":"edge","label":"textDocument/foldingRange","inV":2,"outV":1}
-        {"id":4,"type":"vertex","label":"range","start":{"line":0,"character":3},"end":{"line":0,"character":8}}
+        {"id":4,"type":"vertex","label":"range","start":{"line":0,"character":0},"end":{"line":13,"character":0}}
         {"id":5,"type":"vertex","label":"resultSet"}
         {"id":6,"type":"edge","label":"next","inV":5,"outV":4}
-        {"id":7,"type":"vertex","label":"range","start":{"line":2,"character":13},"end":{"line":2,"character":43}}
+        {"id":7,"type":"vertex","label":"range","start":{"line":0,"character":3},"end":{"line":0,"character":8}}
         {"id":8,"type":"vertex","label":"resultSet"}
         {"id":9,"type":"edge","label":"next","inV":8,"outV":7}
-        {"id":10,"type":"vertex","label":"range","start":{"line":8,"character":0},"end":{"line":8,"character":30}}
-        {"id":11,"type":"edge","label":"next","inV":8,"outV":10}
-        {"id":12,"type":"vertex","label":"range","start":{"line":8,"character":32},"end":{"line":8,"character":39}}
-        {"id":13,"type":"vertex","label":"resultSet"}
-        {"id":14,"type":"edge","label":"next","inV":13,"outV":12}
-        {"id":15,"type":"vertex","label":"range","start":{"line":9,"character":4},"end":{"line":9,"character":9}}
+        {"id":10,"type":"vertex","label":"range","start":{"line":2,"character":13},"end":{"line":2,"character":43}}
+        {"id":11,"type":"vertex","label":"resultSet"}
+        {"id":12,"type":"edge","label":"next","inV":11,"outV":10}
+        {"id":13,"type":"vertex","label":"range","start":{"line":8,"character":0},"end":{"line":8,"character":30}}
+        {"id":14,"type":"edge","label":"next","inV":11,"outV":13}
+        {"id":15,"type":"vertex","label":"range","start":{"line":8,"character":32},"end":{"line":8,"character":39}}
         {"id":16,"type":"vertex","label":"resultSet"}
         {"id":17,"type":"edge","label":"next","inV":16,"outV":15}
-        {"id":18,"type":"vertex","label":"range","start":{"line":10,"character":8},"end":{"line":10,"character":13}}
+        {"id":18,"type":"vertex","label":"range","start":{"line":9,"character":4},"end":{"line":9,"character":9}}
         {"id":19,"type":"vertex","label":"resultSet"}
         {"id":20,"type":"edge","label":"next","inV":19,"outV":18}
-        {"id":21,"type":"vertex","label":"range","start":{"line":11,"character":4},"end":{"line":11,"character":34}}
-        {"id":22,"type":"edge","label":"next","inV":8,"outV":21}
-        {"id":23,"type":"vertex","label":"range","start":{"line":11,"character":36},"end":{"line":11,"character":43}}
-        {"id":24,"type":"vertex","label":"resultSet"}
-        {"id":25,"type":"edge","label":"next","inV":24,"outV":23}
-        {"id":26,"type":"edge","label":"contains","inVs":[4,7,10,12,15,18,21,23],"outV":1}
-        {"id":27,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\n#[allow]\n```\n\n---\n\nValid forms are:\n\n* \\#\\[allow(lint1, lint2, ..., /\\*opt\\*/ reason = \"...\")\\]"}}}
-        {"id":28,"type":"edge","label":"textDocument/hover","inV":27,"outV":5}
-        {"id":29,"type":"vertex","label":"referenceResult"}
-        {"id":30,"type":"edge","label":"textDocument/references","inV":29,"outV":5}
-        {"id":31,"type":"edge","label":"item","document":1,"property":"references","inVs":[4],"outV":29}
-        {"id":32,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nmacro_rules! generate_const_from_identifier\n```"}}}
-        {"id":33,"type":"edge","label":"textDocument/hover","inV":32,"outV":8}
-        {"id":34,"type":"vertex","label":"packageInformation","name":"foo","manager":"cargo","version":"0.0.0"}
-        {"id":35,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::generate_const_from_identifier","unique":"scheme","kind":"export"}
-        {"id":36,"type":"edge","label":"packageInformation","inV":34,"outV":35}
-        {"id":37,"type":"edge","label":"moniker","inV":35,"outV":8}
-        {"id":38,"type":"vertex","label":"definitionResult"}
-        {"id":39,"type":"edge","label":"item","document":1,"inVs":[7],"outV":38}
-        {"id":40,"type":"edge","label":"textDocument/definition","inV":38,"outV":8}
-        {"id":41,"type":"vertex","label":"referenceResult"}
-        {"id":42,"type":"edge","label":"textDocument/references","inV":41,"outV":8}
-        {"id":43,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[7],"outV":41}
-        {"id":44,"type":"edge","label":"item","document":1,"property":"references","inVs":[10,21],"outV":41}
-        {"id":45,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nconst REQ_001: &str = \"encoded_data\"\n```"}}}
-        {"id":46,"type":"edge","label":"textDocument/hover","inV":45,"outV":13}
-        {"id":47,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::REQ_001","unique":"scheme","kind":"export"}
-        {"id":48,"type":"edge","label":"packageInformation","inV":34,"outV":47}
-        {"id":49,"type":"edge","label":"moniker","inV":47,"outV":13}
-        {"id":50,"type":"vertex","label":"definitionResult"}
-        {"id":51,"type":"edge","label":"item","document":1,"inVs":[12],"outV":50}
-        {"id":52,"type":"edge","label":"textDocument/definition","inV":50,"outV":13}
-        {"id":53,"type":"vertex","label":"referenceResult"}
-        {"id":54,"type":"edge","label":"textDocument/references","inV":53,"outV":13}
-        {"id":55,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[12],"outV":53}
-        {"id":56,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nmod tests\n```"}}}
-        {"id":57,"type":"edge","label":"textDocument/hover","inV":56,"outV":16}
-        {"id":58,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::tests","unique":"scheme","kind":"export"}
-        {"id":59,"type":"edge","label":"packageInformation","inV":34,"outV":58}
-        {"id":60,"type":"edge","label":"moniker","inV":58,"outV":16}
-        {"id":61,"type":"vertex","label":"definitionResult"}
-        {"id":62,"type":"edge","label":"item","document":1,"inVs":[15],"outV":61}
-        {"id":63,"type":"edge","label":"textDocument/definition","inV":61,"outV":16}
-        {"id":64,"type":"vertex","label":"referenceResult"}
-        {"id":65,"type":"edge","label":"textDocument/references","inV":64,"outV":16}
-        {"id":66,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[15],"outV":64}
-        {"id":67,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nextern crate foo\n```"}}}
-        {"id":68,"type":"edge","label":"textDocument/hover","inV":67,"outV":19}
-        {"id":69,"type":"vertex","label":"definitionResult"}
-        {"id":70,"type":"vertex","label":"range","start":{"line":0,"character":0},"end":{"line":13,"character":0}}
-        {"id":71,"type":"edge","label":"contains","inVs":[70],"outV":1}
-        {"id":72,"type":"edge","label":"item","document":1,"inVs":[70],"outV":69}
-        {"id":73,"type":"edge","label":"textDocument/definition","inV":69,"outV":19}
-        {"id":74,"type":"vertex","label":"referenceResult"}
-        {"id":75,"type":"edge","label":"textDocument/references","inV":74,"outV":19}
-        {"id":76,"type":"edge","label":"item","document":1,"property":"references","inVs":[18],"outV":74}
-        {"id":77,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo::tests\n```\n\n```rust\nconst REQ_002: &str = \"encoded_data\"\n```"}}}
-        {"id":78,"type":"edge","label":"textDocument/hover","inV":77,"outV":24}
-        {"id":79,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::tests::REQ_002","unique":"scheme","kind":"export"}
-        {"id":80,"type":"edge","label":"packageInformation","inV":34,"outV":79}
-        {"id":81,"type":"edge","label":"moniker","inV":79,"outV":24}
-        {"id":82,"type":"vertex","label":"definitionResult"}
-        {"id":83,"type":"edge","label":"item","document":1,"inVs":[23],"outV":82}
-        {"id":84,"type":"edge","label":"textDocument/definition","inV":82,"outV":24}
-        {"id":85,"type":"vertex","label":"referenceResult"}
-        {"id":86,"type":"edge","label":"textDocument/references","inV":85,"outV":24}
-        {"id":87,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[23],"outV":85}
+        {"id":21,"type":"vertex","label":"range","start":{"line":10,"character":8},"end":{"line":10,"character":13}}
+        {"id":22,"type":"edge","label":"next","inV":5,"outV":21}
+        {"id":23,"type":"vertex","label":"range","start":{"line":11,"character":4},"end":{"line":11,"character":34}}
+        {"id":24,"type":"edge","label":"next","inV":11,"outV":23}
+        {"id":25,"type":"vertex","label":"range","start":{"line":11,"character":36},"end":{"line":11,"character":43}}
+        {"id":26,"type":"vertex","label":"resultSet"}
+        {"id":27,"type":"edge","label":"next","inV":26,"outV":25}
+        {"id":28,"type":"edge","label":"contains","inVs":[4,7,10,13,15,18,21,23,25],"outV":1}
+        {"id":29,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nextern crate foo\n```"}}}
+        {"id":30,"type":"edge","label":"textDocument/hover","inV":29,"outV":5}
+        {"id":31,"type":"vertex","label":"packageInformation","name":"foo","manager":"cargo","version":"0.0.0"}
+        {"id":32,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::crate","unique":"scheme","kind":"export"}
+        {"id":33,"type":"edge","label":"packageInformation","inV":31,"outV":32}
+        {"id":34,"type":"edge","label":"moniker","inV":32,"outV":5}
+        {"id":35,"type":"vertex","label":"definitionResult"}
+        {"id":36,"type":"edge","label":"item","document":1,"inVs":[4],"outV":35}
+        {"id":37,"type":"edge","label":"textDocument/definition","inV":35,"outV":5}
+        {"id":38,"type":"vertex","label":"referenceResult"}
+        {"id":39,"type":"edge","label":"textDocument/references","inV":38,"outV":5}
+        {"id":40,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[4],"outV":38}
+        {"id":41,"type":"edge","label":"item","document":1,"property":"references","inVs":[21],"outV":38}
+        {"id":42,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\n#[allow]\n```\n\n---\n\nValid forms are:\n\n* \\#\\[allow(lint1, lint2, ..., /\\*opt\\*/ reason = \"...\")\\]"}}}
+        {"id":43,"type":"edge","label":"textDocument/hover","inV":42,"outV":8}
+        {"id":44,"type":"vertex","label":"referenceResult"}
+        {"id":45,"type":"edge","label":"textDocument/references","inV":44,"outV":8}
+        {"id":46,"type":"edge","label":"item","document":1,"property":"references","inVs":[7],"outV":44}
+        {"id":47,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nmacro_rules! generate_const_from_identifier\n```"}}}
+        {"id":48,"type":"edge","label":"textDocument/hover","inV":47,"outV":11}
+        {"id":49,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::generate_const_from_identifier","unique":"scheme","kind":"export"}
+        {"id":50,"type":"edge","label":"packageInformation","inV":31,"outV":49}
+        {"id":51,"type":"edge","label":"moniker","inV":49,"outV":11}
+        {"id":52,"type":"vertex","label":"definitionResult"}
+        {"id":53,"type":"edge","label":"item","document":1,"inVs":[10],"outV":52}
+        {"id":54,"type":"edge","label":"textDocument/definition","inV":52,"outV":11}
+        {"id":55,"type":"vertex","label":"referenceResult"}
+        {"id":56,"type":"edge","label":"textDocument/references","inV":55,"outV":11}
+        {"id":57,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[10],"outV":55}
+        {"id":58,"type":"edge","label":"item","document":1,"property":"references","inVs":[13,23],"outV":55}
+        {"id":59,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nconst REQ_001: &str = \"encoded_data\"\n```"}}}
+        {"id":60,"type":"edge","label":"textDocument/hover","inV":59,"outV":16}
+        {"id":61,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::REQ_001","unique":"scheme","kind":"export"}
+        {"id":62,"type":"edge","label":"packageInformation","inV":31,"outV":61}
+        {"id":63,"type":"edge","label":"moniker","inV":61,"outV":16}
+        {"id":64,"type":"vertex","label":"definitionResult"}
+        {"id":65,"type":"edge","label":"item","document":1,"inVs":[15],"outV":64}
+        {"id":66,"type":"edge","label":"textDocument/definition","inV":64,"outV":16}
+        {"id":67,"type":"vertex","label":"referenceResult"}
+        {"id":68,"type":"edge","label":"textDocument/references","inV":67,"outV":16}
+        {"id":69,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[15],"outV":67}
+        {"id":70,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nmod tests\n```"}}}
+        {"id":71,"type":"edge","label":"textDocument/hover","inV":70,"outV":19}
+        {"id":72,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::tests","unique":"scheme","kind":"export"}
+        {"id":73,"type":"edge","label":"packageInformation","inV":31,"outV":72}
+        {"id":74,"type":"edge","label":"moniker","inV":72,"outV":19}
+        {"id":75,"type":"vertex","label":"definitionResult"}
+        {"id":76,"type":"edge","label":"item","document":1,"inVs":[18],"outV":75}
+        {"id":77,"type":"edge","label":"textDocument/definition","inV":75,"outV":19}
+        {"id":78,"type":"vertex","label":"referenceResult"}
+        {"id":79,"type":"edge","label":"textDocument/references","inV":78,"outV":19}
+        {"id":80,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[18],"outV":78}
+        {"id":81,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo::tests\n```\n\n```rust\nconst REQ_002: &str = \"encoded_data\"\n```"}}}
+        {"id":82,"type":"edge","label":"textDocument/hover","inV":81,"outV":26}
+        {"id":83,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::tests::REQ_002","unique":"scheme","kind":"export"}
+        {"id":84,"type":"edge","label":"packageInformation","inV":31,"outV":83}
+        {"id":85,"type":"edge","label":"moniker","inV":83,"outV":26}
+        {"id":86,"type":"vertex","label":"definitionResult"}
+        {"id":87,"type":"edge","label":"item","document":1,"inVs":[25],"outV":86}
+        {"id":88,"type":"edge","label":"textDocument/definition","inV":86,"outV":26}
+        {"id":89,"type":"vertex","label":"referenceResult"}
+        {"id":90,"type":"edge","label":"textDocument/references","inV":89,"outV":26}
+        {"id":91,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[25],"outV":89}
     "#]].assert_eq(stdout);
 }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
index c1ca59606379a..2b3c0a47a220d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -1082,11 +1082,11 @@ fn resolve_proc_macro() {
         return;
     }
 
-    let sysroot = project_model::Sysroot::discover(
+    let mut sysroot = project_model::Sysroot::discover(
         &AbsPathBuf::assert_utf8(std::env::current_dir().unwrap()),
         &Default::default(),
-        project_model::SysrootQueryMetadata::CargoMetadata,
     );
+    sysroot.load_workspace(&project_model::SysrootSourceWorkspaceConfig::default_cargo());
 
     let proc_macro_server_path = sysroot.discover_proc_macro_srv().unwrap();
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs
index d113bd512789a..409be2894fea7 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs
@@ -43,10 +43,15 @@ impl TestDir {
             }
             fs::create_dir_all(&path).unwrap();
 
-            #[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
+            #[cfg(any(
+                target_os = "macos",
+                target_os = "linux",
+                target_os = "windows",
+                target_os = "freebsd"
+            ))]
             if symlink {
                 let symlink_path = base.join(format!("{pid}_{cnt}_symlink"));
-                #[cfg(any(target_os = "macos", target_os = "linux"))]
+                #[cfg(any(target_os = "macos", target_os = "linux", target_os = "freebsd"))]
                 std::os::unix::fs::symlink(path, &symlink_path).unwrap();
 
                 #[cfg(target_os = "windows")]
diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs
index 87a948df550ad..6becc8e41ed00 100644
--- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs
+++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs
@@ -26,7 +26,7 @@ use crate::InternId;
 #[cfg(feature = "ra-salsa")]
 use ra_salsa::{InternId, InternValue};
 
-use crate::MacroCallId;
+use crate::{Edition, MacroCallId};
 
 /// Interned [`SyntaxContextData`].
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -59,11 +59,20 @@ impl fmt::Display for SyntaxContextId {
 }
 
 impl SyntaxContextId {
+    #[inline]
+    pub fn remove_root_edition(&mut self) {
+        if self.is_root() {
+            *self = Self::root(Edition::Edition2015);
+        }
+    }
+
     /// The root context, which is the parent of all other contexts. All [`FileId`]s have this context.
-    pub const ROOT: Self = SyntaxContextId(unsafe { InternId::new_unchecked(0) });
+    pub const fn root(edition: Edition) -> Self {
+        SyntaxContextId(unsafe { InternId::new_unchecked(edition as u32) })
+    }
 
     pub fn is_root(self) -> bool {
-        self == Self::ROOT
+        self.into_u32() <= Edition::LATEST as u32
     }
 
     /// Deconstruct a `SyntaxContextId` into a raw `u32`.
@@ -89,6 +98,7 @@ pub struct SyntaxContextData {
     // per crate. Though that is likely not a problem as `MacroCallId`s are already crate calling dependent.
     pub outer_expn: Option,
     pub outer_transparency: Transparency,
+    pub edition: Edition,
     pub parent: SyntaxContextId,
     /// This context, but with all transparent and semi-transparent expansions filtered away.
     pub opaque: SyntaxContextId,
@@ -98,10 +108,10 @@ pub struct SyntaxContextData {
 
 #[cfg(feature = "ra-salsa")]
 impl InternValue for SyntaxContextData {
-    type Key = (SyntaxContextId, Option, Transparency);
+    type Key = (SyntaxContextId, Option, Transparency, Edition);
 
     fn into_key(&self) -> Self::Key {
-        (self.parent, self.outer_expn, self.outer_transparency)
+        (self.parent, self.outer_expn, self.outer_transparency, self.edition)
     }
 }
 
@@ -118,13 +128,14 @@ impl std::fmt::Debug for SyntaxContextData {
 }
 
 impl SyntaxContextData {
-    pub fn root() -> Self {
+    pub fn root(edition: Edition) -> Self {
         SyntaxContextData {
             outer_expn: None,
             outer_transparency: Transparency::Opaque,
-            parent: SyntaxContextId::ROOT,
-            opaque: SyntaxContextId::ROOT,
-            opaque_and_semitransparent: SyntaxContextId::ROOT,
+            parent: SyntaxContextId::root(edition),
+            opaque: SyntaxContextId::root(edition),
+            opaque_and_semitransparent: SyntaxContextId::root(edition),
+            edition,
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/span/src/lib.rs b/src/tools/rust-analyzer/crates/span/src/lib.rs
index 20c3b087af5d6..8dc957350381c 100644
--- a/src/tools/rust-analyzer/crates/span/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/span/src/lib.rs
@@ -358,6 +358,18 @@ impl HirFileId {
     }
 }
 
+/// Legacy span type, only defined here as it is still used by the proc-macro server.
+/// While rust-analyzer doesn't use this anymore at all, RustRover relies on the legacy type for
+/// proc-macro expansion.
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+pub struct TokenId(pub u32);
+
+impl std::fmt::Debug for TokenId {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        self.0.fmt(f)
+    }
+}
+
 #[cfg(not(feature = "ra-salsa"))]
 mod intern_id_proxy {
     use std::fmt;
diff --git a/src/tools/rust-analyzer/crates/span/src/map.rs b/src/tools/rust-analyzer/crates/span/src/map.rs
index 66bbce185940b..dc35de67fd8db 100644
--- a/src/tools/rust-analyzer/crates/span/src/map.rs
+++ b/src/tools/rust-analyzer/crates/span/src/map.rs
@@ -208,7 +208,7 @@ impl RealSpanMap {
         Span {
             range: range - offset,
             anchor: SpanAnchor { file_id: self.file_id, ast_id },
-            ctx: SyntaxContextId::ROOT,
+            ctx: SyntaxContextId::root(self.file_id.edition()),
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/stdx/Cargo.toml b/src/tools/rust-analyzer/crates/stdx/Cargo.toml
index bf0d6df9ad810..1ebb48c577a6f 100644
--- a/src/tools/rust-analyzer/crates/stdx/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/stdx/Cargo.toml
@@ -23,7 +23,7 @@ itertools.workspace = true
 
 [target.'cfg(windows)'.dependencies]
 miow = "0.6.0"
-windows-sys = { version = "0.52", features = ["Win32_Foundation"] }
+windows-sys = { version = "0.59", features = ["Win32_Foundation"] }
 
 [features]
 # Uncomment to enable for the whole crate graph
diff --git a/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs b/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs
index 2ddd7da74c291..9acc1de922af9 100644
--- a/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs
+++ b/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs
@@ -7,9 +7,12 @@
 //! The thread pool is implemented entirely using
 //! the threading utilities in [`crate::thread`].
 
-use std::sync::{
-    atomic::{AtomicUsize, Ordering},
-    Arc,
+use std::{
+    panic::{self, UnwindSafe},
+    sync::{
+        atomic::{AtomicUsize, Ordering},
+        Arc,
+    },
 };
 
 use crossbeam_channel::{Receiver, Sender};
@@ -25,13 +28,13 @@ pub struct Pool {
     // so that the channel is actually closed
     // before we join the worker threads!
     job_sender: Sender,
-    _handles: Vec,
+    _handles: Box<[JoinHandle]>,
     extant_tasks: Arc,
 }
 
 struct Job {
     requested_intent: ThreadIntent,
-    f: Box,
+    f: Box,
 }
 
 impl Pool {
@@ -47,6 +50,7 @@ impl Pool {
             let handle = Builder::new(INITIAL_INTENT)
                 .stack_size(STACK_SIZE)
                 .name("Worker".into())
+                .allow_leak(true)
                 .spawn({
                     let extant_tasks = Arc::clone(&extant_tasks);
                     let job_receiver: Receiver = job_receiver.clone();
@@ -58,7 +62,8 @@ impl Pool {
                                 current_intent = job.requested_intent;
                             }
                             extant_tasks.fetch_add(1, Ordering::SeqCst);
-                            (job.f)();
+                            // discard the panic, we should've logged the backtrace already
+                            _ = panic::catch_unwind(job.f);
                             extant_tasks.fetch_sub(1, Ordering::SeqCst);
                         }
                     }
@@ -68,12 +73,12 @@ impl Pool {
             handles.push(handle);
         }
 
-        Pool { _handles: handles, extant_tasks, job_sender }
+        Pool { _handles: handles.into_boxed_slice(), extant_tasks, job_sender }
     }
 
     pub fn spawn(&self, intent: ThreadIntent, f: F)
     where
-        F: FnOnce() + Send + 'static,
+        F: FnOnce() + Send + UnwindSafe + 'static,
     {
         let f = Box::new(move || {
             if cfg!(debug_assertions) {
diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs
index 3a05b83e49703..19801c49e4341 100644
--- a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs
@@ -1,21 +1,18 @@
 //! Conversions between [`SyntaxNode`] and [`tt::TokenTree`].
 
-use std::fmt;
+use std::{fmt, hash::Hash};
 
 use intern::Symbol;
 use rustc_hash::{FxHashMap, FxHashSet};
 use span::{Edition, SpanAnchor, SpanData, SpanMap};
-use stdx::{format_to, never, non_empty_vec::NonEmptyVec};
+use stdx::{format_to, never};
 use syntax::{
     ast::{self, make::tokens::doc_comment},
     format_smolstr, AstToken, Parse, PreorderWithTokens, SmolStr, SyntaxElement,
     SyntaxKind::{self, *},
     SyntaxNode, SyntaxToken, SyntaxTreeBuilder, TextRange, TextSize, WalkEvent, T,
 };
-use tt::{
-    buffer::{Cursor, TokenBuffer},
-    token_to_literal,
-};
+use tt::{buffer::Cursor, token_to_literal};
 
 pub mod prettify_macro_expansion;
 mod to_parser_input;
@@ -61,7 +58,7 @@ pub mod dummy_test_span_utils {
             ),
             ast_id: span::ROOT_ERASED_FILE_AST_ID,
         },
-        ctx: SyntaxContextId::ROOT,
+        ctx: SyntaxContextId::root(Edition::CURRENT),
     };
 
     pub struct DummyTestSpanMap;
@@ -77,7 +74,7 @@ pub mod dummy_test_span_utils {
                     ),
                     ast_id: span::ROOT_ERASED_FILE_AST_ID,
                 },
-                ctx: SyntaxContextId::ROOT,
+                ctx: SyntaxContextId::root(Edition::CURRENT),
             }
         }
     }
@@ -99,7 +96,7 @@ pub fn syntax_node_to_token_tree(
     map: SpanMap,
     span: SpanData,
     mode: DocCommentDesugarMode,
-) -> tt::Subtree>
+) -> tt::TopSubtree>
 where
     SpanData: Copy + fmt::Debug,
     SpanMap: SpanMapper>,
@@ -118,7 +115,7 @@ pub fn syntax_node_to_token_tree_modified(
     remove: FxHashSet,
     call_site: SpanData,
     mode: DocCommentDesugarMode,
-) -> tt::Subtree>
+) -> tt::TopSubtree>
 where
     SpanMap: SpanMapper>,
     SpanData: Copy + fmt::Debug,
@@ -142,24 +139,19 @@ where
 /// Converts a [`tt::Subtree`] back to a [`SyntaxNode`].
 /// The produced `SpanMap` contains a mapping from the syntax nodes offsets to the subtree's spans.
 pub fn token_tree_to_syntax_node(
-    tt: &tt::Subtree>,
+    tt: &tt::TopSubtree>,
     entry_point: parser::TopEntryPoint,
-    edition: parser::Edition,
+    span_to_edition: &mut dyn FnMut(Ctx) -> Edition,
+    top_edition: Edition,
 ) -> (Parse, SpanMap)
 where
-    SpanData: Copy + fmt::Debug,
-    Ctx: PartialEq,
+    Ctx: Copy + fmt::Debug + PartialEq + PartialEq + Eq + Hash,
 {
-    let buffer = match tt {
-        tt::Subtree {
-            delimiter: tt::Delimiter { kind: tt::DelimiterKind::Invisible, .. },
-            token_trees,
-        } => TokenBuffer::from_tokens(token_trees),
-        _ => TokenBuffer::from_subtree(tt),
-    };
-    let parser_input = to_parser_input(edition, &buffer);
-    let parser_output = entry_point.parse(&parser_input, edition);
-    let mut tree_sink = TtTreeSink::new(buffer.begin());
+    let buffer = tt.view().strip_invisible();
+    let parser_input = to_parser_input(buffer, span_to_edition);
+    // It matters what edition we parse with even when we escape all identifiers correctly.
+    let parser_output = entry_point.parse(&parser_input, top_edition);
+    let mut tree_sink = TtTreeSink::new(buffer.cursor());
     for event in parser_output.iter() {
         match event {
             parser::Step::Token { kind, n_input_tokens: n_raw_tokens } => {
@@ -183,7 +175,7 @@ pub fn parse_to_token_tree(
     anchor: SpanAnchor,
     ctx: Ctx,
     text: &str,
-) -> Option>>
+) -> Option>>
 where
     SpanData: Copy + fmt::Debug,
     Ctx: Copy,
@@ -202,7 +194,7 @@ pub fn parse_to_token_tree_static_span(
     edition: Edition,
     span: S,
     text: &str,
-) -> Option>
+) -> Option>
 where
     S: Copy + fmt::Debug,
 {
@@ -215,47 +207,38 @@ where
     Some(convert_tokens(&mut conv))
 }
 
-fn convert_tokens(conv: &mut C) -> tt::Subtree
+fn convert_tokens(conv: &mut C) -> tt::TopSubtree
 where
     C: TokenConverter,
     S: Copy + fmt::Debug,
     C::Token: fmt::Debug,
 {
-    let entry = tt::SubtreeBuilder {
-        delimiter: tt::Delimiter::invisible_spanned(conv.call_site()),
-        token_trees: vec![],
-    };
-    let mut stack = NonEmptyVec::new(entry);
+    let mut builder =
+        tt::TopSubtreeBuilder::new(tt::Delimiter::invisible_spanned(conv.call_site()));
 
     while let Some((token, abs_range)) = conv.bump() {
-        let tt::SubtreeBuilder { delimiter, token_trees } = stack.last_mut();
-
+        let delimiter = builder.expected_delimiter().map(|it| it.kind);
         let tt = match token.as_leaf() {
-            Some(leaf) => tt::TokenTree::Leaf(leaf.clone()),
+            Some(leaf) => leaf.clone(),
             None => match token.kind(conv) {
                 // Desugar doc comments into doc attributes
                 COMMENT => {
                     let span = conv.span_for(abs_range);
-                    if let Some(tokens) = conv.convert_doc_comment(&token, span) {
-                        token_trees.extend(tokens);
-                    }
+                    conv.convert_doc_comment(&token, span, &mut builder);
                     continue;
                 }
                 kind if kind.is_punct() && kind != UNDERSCORE => {
-                    let expected = match delimiter.kind {
-                        tt::DelimiterKind::Parenthesis => Some(T![')']),
-                        tt::DelimiterKind::Brace => Some(T!['}']),
-                        tt::DelimiterKind::Bracket => Some(T![']']),
-                        tt::DelimiterKind::Invisible => None,
+                    let expected = match delimiter {
+                        Some(tt::DelimiterKind::Parenthesis) => Some(T![')']),
+                        Some(tt::DelimiterKind::Brace) => Some(T!['}']),
+                        Some(tt::DelimiterKind::Bracket) => Some(T![']']),
+                        Some(tt::DelimiterKind::Invisible) | None => None,
                     };
 
                     // Current token is a closing delimiter that we expect, fix up the closing span
                     // and end the subtree here
                     if matches!(expected, Some(expected) if expected == kind) {
-                        if let Some(mut subtree) = stack.pop() {
-                            subtree.delimiter.close = conv.span_for(abs_range);
-                            stack.last_mut().token_trees.push(subtree.build().into());
-                        }
+                        builder.close(conv.span_for(abs_range));
                         continue;
                     }
 
@@ -268,16 +251,7 @@ where
 
                     // Start a new subtree
                     if let Some(kind) = delim {
-                        let open = conv.span_for(abs_range);
-                        stack.push(tt::SubtreeBuilder {
-                            delimiter: tt::Delimiter {
-                                open,
-                                // will be overwritten on subtree close above
-                                close: open,
-                                kind,
-                            },
-                            token_trees: vec![],
-                        });
+                        builder.open(kind, conv.span_for(abs_range));
                         continue;
                     }
 
@@ -289,7 +263,6 @@ where
                         panic!("Token from lexer must be single char: token = {token:#?}")
                     };
                     tt::Leaf::from(tt::Punct { char, spacing, span: conv.span_for(abs_range) })
-                        .into()
                 }
                 kind => {
                     macro_rules! make_ident {
@@ -320,7 +293,7 @@ where
                                 span: conv
                                     .span_for(TextRange::at(abs_range.start(), TextSize::of('\''))),
                             });
-                            token_trees.push(apostrophe.into());
+                            builder.push(apostrophe);
 
                             let ident = tt::Leaf::from(tt::Ident {
                                 sym: Symbol::intern(&token.to_text(conv)[1..]),
@@ -330,47 +303,26 @@ where
                                 )),
                                 is_raw: tt::IdentIsRaw::No,
                             });
-                            token_trees.push(ident.into());
+                            builder.push(ident);
                             continue;
                         }
                         _ => continue,
                     };
 
-                    leaf.into()
+                    leaf
                 }
             },
         };
 
-        token_trees.push(tt);
+        builder.push(tt);
     }
 
     // If we get here, we've consumed all input tokens.
     // We might have more than one subtree in the stack, if the delimiters are improperly balanced.
     // Merge them so we're left with one.
-    while let Some(entry) = stack.pop() {
-        let parent = stack.last_mut();
-
-        let leaf: tt::Leaf<_> = tt::Punct {
-            span: entry.delimiter.open,
-            char: match entry.delimiter.kind {
-                tt::DelimiterKind::Parenthesis => '(',
-                tt::DelimiterKind::Brace => '{',
-                tt::DelimiterKind::Bracket => '[',
-                tt::DelimiterKind::Invisible => '$',
-            },
-            spacing: tt::Spacing::Alone,
-        }
-        .into();
-        parent.token_trees.push(leaf.into());
-        parent.token_trees.extend(entry.token_trees);
-    }
+    builder.flatten_unclosed_subtrees();
 
-    let subtree = stack.into_last().build();
-    if let [tt::TokenTree::Subtree(first)] = &*subtree.token_trees {
-        first.clone()
-    } else {
-        subtree
-    }
+    builder.build_skip_top_subtree()
 }
 
 fn is_single_token_op(kind: SyntaxKind) -> bool {
@@ -436,25 +388,17 @@ fn convert_doc_comment(
     token: &syntax::SyntaxToken,
     span: S,
     mode: DocCommentDesugarMode,
-) -> Option>> {
-    let comment = ast::Comment::cast(token.clone())?;
-    let doc = comment.kind().doc?;
+    builder: &mut tt::TopSubtreeBuilder,
+) {
+    let Some(comment) = ast::Comment::cast(token.clone()) else { return };
+    let Some(doc) = comment.kind().doc else { return };
 
     let mk_ident = |s: &str| {
-        tt::TokenTree::from(tt::Leaf::from(tt::Ident {
-            sym: Symbol::intern(s),
-            span,
-            is_raw: tt::IdentIsRaw::No,
-        }))
+        tt::Leaf::from(tt::Ident { sym: Symbol::intern(s), span, is_raw: tt::IdentIsRaw::No })
     };
 
-    let mk_punct = |c: char| {
-        tt::TokenTree::from(tt::Leaf::from(tt::Punct {
-            char: c,
-            spacing: tt::Spacing::Alone,
-            span,
-        }))
-    };
+    let mk_punct =
+        |c: char| tt::Leaf::from(tt::Punct { char: c, spacing: tt::Spacing::Alone, span });
 
     let mk_doc_literal = |comment: &ast::Comment| {
         let prefix_len = comment.prefix().len();
@@ -467,24 +411,20 @@ fn convert_doc_comment(
         let (text, kind) = desugar_doc_comment_text(text, mode);
         let lit = tt::Literal { symbol: text, span, kind, suffix: None };
 
-        tt::TokenTree::from(tt::Leaf::from(lit))
+        tt::Leaf::from(lit)
     };
 
     // Make `doc="\" Comments\""
-    let meta_tkns = Box::new([mk_ident("doc"), mk_punct('='), mk_doc_literal(&comment)]);
+    let meta_tkns = [mk_ident("doc"), mk_punct('='), mk_doc_literal(&comment)];
 
     // Make `#![]`
-    let mut token_trees = Vec::with_capacity(3);
-    token_trees.push(mk_punct('#'));
+    builder.push(mk_punct('#'));
     if let ast::CommentPlacement::Inner = doc {
-        token_trees.push(mk_punct('!'));
+        builder.push(mk_punct('!'));
     }
-    token_trees.push(tt::TokenTree::from(tt::Subtree {
-        delimiter: tt::Delimiter { open: span, close: span, kind: tt::DelimiterKind::Bracket },
-        token_trees: meta_tkns,
-    }));
-
-    Some(token_trees)
+    builder.open(tt::DelimiterKind::Bracket, span);
+    builder.extend(meta_tkns);
+    builder.close(span);
 }
 
 /// A raw token (straight from lexer) converter
@@ -518,7 +458,12 @@ trait SrcToken {
 trait TokenConverter: Sized {
     type Token: SrcToken;
 
-    fn convert_doc_comment(&self, token: &Self::Token, span: S) -> Option>>;
+    fn convert_doc_comment(
+        &self,
+        token: &Self::Token,
+        span: S,
+        builder: &mut tt::TopSubtreeBuilder,
+    );
 
     fn bump(&mut self) -> Option<(Self::Token, TextRange)>;
 
@@ -567,9 +512,10 @@ where
         &self,
         &token: &usize,
         span: SpanData,
-    ) -> Option>>> {
+        builder: &mut tt::TopSubtreeBuilder>,
+    ) {
         let text = self.lexed.text(token);
-        convert_doc_comment(&doc_comment(text), span, self.mode)
+        convert_doc_comment(&doc_comment(text), span, self.mode, builder);
     }
 
     fn bump(&mut self) -> Option<(Self::Token, TextRange)> {
@@ -606,9 +552,9 @@ where
 {
     type Token = usize;
 
-    fn convert_doc_comment(&self, &token: &usize, span: S) -> Option>> {
+    fn convert_doc_comment(&self, &token: &usize, span: S, builder: &mut tt::TopSubtreeBuilder) {
         let text = self.lexed.text(token);
-        convert_doc_comment(&doc_comment(text), span, self.mode)
+        convert_doc_comment(&doc_comment(text), span, self.mode, builder);
     }
 
     fn bump(&mut self) -> Option<(Self::Token, TextRange)> {
@@ -773,8 +719,13 @@ where
     SpanMap: SpanMapper,
 {
     type Token = SynToken;
-    fn convert_doc_comment(&self, token: &Self::Token, span: S) -> Option>> {
-        convert_doc_comment(token.token(), span, self.mode)
+    fn convert_doc_comment(
+        &self,
+        token: &Self::Token,
+        span: S,
+        builder: &mut tt::TopSubtreeBuilder,
+    ) {
+        convert_doc_comment(token.token(), span, self.mode, builder);
     }
 
     fn bump(&mut self) -> Option<(Self::Token, TextRange)> {
@@ -899,15 +850,12 @@ where
     /// This occurs when a float literal is used as a field access.
     fn float_split(&mut self, has_pseudo_dot: bool) {
         let (text, span) = match self.cursor.token_tree() {
-            Some(tt::buffer::TokenTreeRef::Leaf(
-                tt::Leaf::Literal(tt::Literal {
-                    symbol: text,
-                    span,
-                    kind: tt::LitKind::Float,
-                    suffix: _,
-                }),
-                _,
-            )) => (text.as_str(), *span),
+            Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
+                symbol: text,
+                span,
+                kind: tt::LitKind::Float,
+                suffix: _,
+            }))) => (text.as_str(), *span),
             tt => unreachable!("{tt:?}"),
         };
         // FIXME: Span splitting
@@ -942,7 +890,7 @@ where
             }
             None => unreachable!(),
         }
-        self.cursor = self.cursor.bump();
+        self.cursor.bump();
     }
 
     fn token(&mut self, kind: SyntaxKind, mut n_tokens: u8) {
@@ -950,24 +898,24 @@ where
             n_tokens = 2;
         }
 
-        let mut last = self.cursor;
+        let mut last_two = self.cursor.peek_two_leaves();
         let mut combined_span = None;
         'tokens: for _ in 0..n_tokens {
             let tmp: u8;
             if self.cursor.eof() {
                 break;
             }
-            last = self.cursor;
+            last_two = self.cursor.peek_two_leaves();
             let (text, span) = loop {
                 break match self.cursor.token_tree() {
-                    Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => match leaf {
+                    Some(tt::TokenTree::Leaf(leaf)) => match leaf {
                         tt::Leaf::Ident(ident) => {
                             if ident.is_raw.yes() {
                                 self.buf.push_str("r#");
                                 self.text_pos += TextSize::of("r#");
                             }
                             let r = (ident.sym.as_str(), ident.span);
-                            self.cursor = self.cursor.bump();
+                            self.cursor.bump();
                             r
                         }
                         tt::Leaf::Punct(punct) => {
@@ -977,7 +925,7 @@ where
                                 std::str::from_utf8(std::slice::from_ref(&tmp)).unwrap(),
                                 punct.span,
                             );
-                            self.cursor = self.cursor.bump();
+                            self.cursor.bump();
                             r
                         }
                         tt::Leaf::Literal(lit) => {
@@ -989,20 +937,19 @@ where
                                 None => Some(lit.span),
                                 Some(prev_span) => Some(Self::merge_spans(prev_span, lit.span)),
                             };
-                            self.cursor = self.cursor.bump();
+                            self.cursor.bump();
                             continue 'tokens;
                         }
                     },
-                    Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) => {
-                        self.cursor = self.cursor.subtree().unwrap();
+                    Some(tt::TokenTree::Subtree(subtree)) => {
+                        self.cursor.bump();
                         match delim_to_str(subtree.delimiter.kind, false) {
                             Some(it) => (it, subtree.delimiter.open),
                             None => continue,
                         }
                     }
                     None => {
-                        let parent = self.cursor.end().unwrap();
-                        self.cursor = self.cursor.bump();
+                        let parent = self.cursor.end();
                         match delim_to_str(parent.delimiter.kind, true) {
                             Some(it) => (it, parent.delimiter.close),
                             None => continue,
@@ -1023,12 +970,7 @@ where
         self.buf.clear();
         // FIXME: Emitting whitespace for this is really just a hack, we should get rid of it.
         // Add whitespace between adjoint puncts
-        let next = last.bump();
-        if let (
-            Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(curr), _)),
-            Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(next), _)),
-        ) = (last.token_tree(), next.token_tree())
-        {
+        if let Some([tt::Leaf::Punct(curr), tt::Leaf::Punct(next)]) = last_two {
             // Note: We always assume the semi-colon would be the last token in
             // other parts of RA such that we don't add whitespace here.
             //
@@ -1058,7 +1000,7 @@ where
         // We don't do what rustc does exactly, rustc does something clever when the spans have different syntax contexts
         // but this runs afoul of our separation between `span` and `hir-expand`.
         SpanData {
-            range: if a.ctx == b.ctx {
+            range: if a.ctx == b.ctx && a.anchor == b.anchor {
                 TextRange::new(
                     std::cmp::min(a.range.start(), b.range.start()),
                     std::cmp::max(a.range.end(), b.range.end()),
diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/tests.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/tests.rs
index 7b8e3f2b49c20..d37cb508de19d 100644
--- a/src/tools/rust-analyzer/crates/syntax-bridge/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/tests.rs
@@ -2,10 +2,7 @@ use rustc_hash::FxHashMap;
 use span::Span;
 use syntax::{ast, AstNode};
 use test_utils::extract_annotations;
-use tt::{
-    buffer::{TokenBuffer, TokenTreeRef},
-    Leaf, Punct, Spacing,
-};
+use tt::{buffer::Cursor, Leaf, Punct, Spacing};
 
 use crate::{
     dummy_test_span_utils::{DummyTestSpanMap, DUMMY},
@@ -32,22 +29,22 @@ fn check_punct_spacing(fixture: &str) {
         })
         .collect();
 
-    let buf = TokenBuffer::from_subtree(&subtree);
-    let mut cursor = buf.begin();
+    let mut cursor = Cursor::new(&subtree.0);
     while !cursor.eof() {
         while let Some(token_tree) = cursor.token_tree() {
-            if let TokenTreeRef::Leaf(
-                Leaf::Punct(Punct { spacing, span: Span { range, .. }, .. }),
-                _,
-            ) = token_tree
+            if let tt::TokenTree::Leaf(Leaf::Punct(Punct {
+                spacing,
+                span: Span { range, .. },
+                ..
+            })) = token_tree
             {
                 if let Some(expected) = annotations.remove(range) {
                     assert_eq!(expected, *spacing);
                 }
             }
-            cursor = cursor.bump_subtree();
+            cursor.bump();
         }
-        cursor = cursor.bump();
+        cursor.bump_or_end();
     }
 
     assert!(annotations.is_empty(), "unchecked annotations: {annotations:?}");
diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/to_parser_input.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/to_parser_input.rs
index 14216e3093285..0dcb2be316c38 100644
--- a/src/tools/rust-analyzer/crates/syntax-bridge/src/to_parser_input.rs
+++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/to_parser_input.rs
@@ -2,41 +2,41 @@
 //! format that works for our parser.
 
 use std::fmt;
+use std::hash::Hash;
 
-use span::Edition;
+use rustc_hash::FxHashMap;
+use span::{Edition, SpanData};
 use syntax::{SyntaxKind, SyntaxKind::*, T};
 
-use tt::buffer::TokenBuffer;
-
-pub fn to_parser_input(
-    edition: Edition,
-    buffer: &TokenBuffer<'_, S>,
+pub fn to_parser_input(
+    buffer: tt::TokenTreesView<'_, SpanData>,
+    span_to_edition: &mut dyn FnMut(Ctx) -> Edition,
 ) -> parser::Input {
     let mut res = parser::Input::default();
 
-    let mut current = buffer.begin();
+    let mut current = buffer.cursor();
+    let mut syntax_context_to_edition_cache = FxHashMap::default();
 
     while !current.eof() {
-        let cursor = current;
-        let tt = cursor.token_tree();
+        let tt = current.token_tree();
 
         // Check if it is lifetime
-        if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(punct), _)) = tt {
+        if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = tt {
             if punct.char == '\'' {
-                let next = cursor.bump();
-                match next.token_tree() {
-                    Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Ident(_ident), _)) => {
+                current.bump();
+                match current.token_tree() {
+                    Some(tt::TokenTree::Leaf(tt::Leaf::Ident(_ident))) => {
                         res.push(LIFETIME_IDENT);
-                        current = next.bump();
+                        current.bump();
                         continue;
                     }
-                    _ => panic!("Next token must be ident : {:#?}", next.token_tree()),
+                    _ => panic!("Next token must be ident"),
                 }
             }
         }
 
-        current = match tt {
-            Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => {
+        match tt {
+            Some(tt::TokenTree::Leaf(leaf)) => {
                 match leaf {
                     tt::Leaf::Literal(lit) => {
                         let kind = match lit.kind {
@@ -60,20 +60,25 @@ pub fn to_parser_input(
                             res.was_joint();
                         }
                     }
-                    tt::Leaf::Ident(ident) => match ident.sym.as_str() {
-                        "_" => res.push(T![_]),
-                        i if i.starts_with('\'') => res.push(LIFETIME_IDENT),
-                        _ if ident.is_raw.yes() => res.push(IDENT),
-                        text => match SyntaxKind::from_keyword(text, edition) {
-                            Some(kind) => res.push(kind),
-                            None => {
-                                let contextual_keyword =
-                                    SyntaxKind::from_contextual_keyword(text, edition)
-                                        .unwrap_or(SyntaxKind::IDENT);
-                                res.push_ident(contextual_keyword);
-                            }
-                        },
-                    },
+                    tt::Leaf::Ident(ident) => {
+                        let edition = *syntax_context_to_edition_cache
+                            .entry(ident.span.ctx)
+                            .or_insert_with(|| span_to_edition(ident.span.ctx));
+                        match ident.sym.as_str() {
+                            "_" => res.push(T![_]),
+                            i if i.starts_with('\'') => res.push(LIFETIME_IDENT),
+                            _ if ident.is_raw.yes() => res.push(IDENT),
+                            text => match SyntaxKind::from_keyword(text, edition) {
+                                Some(kind) => res.push(kind),
+                                None => {
+                                    let contextual_keyword =
+                                        SyntaxKind::from_contextual_keyword(text, edition)
+                                            .unwrap_or(SyntaxKind::IDENT);
+                                    res.push_ident(contextual_keyword);
+                                }
+                            },
+                        }
+                    }
                     tt::Leaf::Punct(punct) => {
                         let kind = SyntaxKind::from_char(punct.char)
                             .unwrap_or_else(|| panic!("{punct:#?} is not a valid punct"));
@@ -83,9 +88,9 @@ pub fn to_parser_input(
                         }
                     }
                 }
-                cursor.bump()
+                current.bump();
             }
-            Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) => {
+            Some(tt::TokenTree::Subtree(subtree)) => {
                 if let Some(kind) = match subtree.delimiter.kind {
                     tt::DelimiterKind::Parenthesis => Some(T!['(']),
                     tt::DelimiterKind::Brace => Some(T!['{']),
@@ -94,22 +99,19 @@ pub fn to_parser_input(
                 } {
                     res.push(kind);
                 }
-                cursor.subtree().unwrap()
+                current.bump();
             }
-            None => match cursor.end() {
-                Some(subtree) => {
-                    if let Some(kind) = match subtree.delimiter.kind {
-                        tt::DelimiterKind::Parenthesis => Some(T![')']),
-                        tt::DelimiterKind::Brace => Some(T!['}']),
-                        tt::DelimiterKind::Bracket => Some(T![']']),
-                        tt::DelimiterKind::Invisible => None,
-                    } {
-                        res.push(kind);
-                    }
-                    cursor.bump()
+            None => {
+                let subtree = current.end();
+                if let Some(kind) = match subtree.delimiter.kind {
+                    tt::DelimiterKind::Parenthesis => Some(T![')']),
+                    tt::DelimiterKind::Brace => Some(T!['}']),
+                    tt::DelimiterKind::Bracket => Some(T![']']),
+                    tt::DelimiterKind::Invisible => None,
+                } {
+                    res.push(kind);
                 }
-                None => continue,
-            },
+            }
         };
     }
 
diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram
index 4e2a70d6cd917..bbb8413cbc080 100644
--- a/src/tools/rust-analyzer/crates/syntax/rust.ungram
+++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram
@@ -241,7 +241,7 @@ RecordFieldList =
 
 RecordField =
   Attr* Visibility?
-  Name ':' Type
+  Name ':' Type ('=' Expr)?
 
 TupleFieldList =
   '(' fields:(TupleField (',' TupleField)* ','?)? ')'
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast.rs b/src/tools/rust-analyzer/crates/syntax/src/ast.rs
index 32b1f5f754489..72a46f2f9f00b 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast.rs
@@ -42,6 +42,14 @@ pub use self::{
 /// the same representation: a pointer to the tree root and a pointer to the
 /// node itself.
 pub trait AstNode {
+    /// This panics if the `SyntaxKind` is not statically known.
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        panic!("dynamic `SyntaxKind` for `AstNode::kind()`")
+    }
+
     fn can_cast(kind: SyntaxKind) -> bool
     where
         Self: Sized;
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs
index de40d638be393..579f3ba8b4fee 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs
@@ -153,8 +153,8 @@ impl AstNodeEdit for N {}
 #[test]
 fn test_increase_indent() {
     let arm_list = {
-        let arm = make::match_arm(iter::once(make::wildcard_pat().into()), None, make::expr_unit());
-        make::match_arm_list(vec![arm.clone(), arm])
+        let arm = make::match_arm(make::wildcard_pat().into(), None, make::ext::expr_unit());
+        make::match_arm_list([arm.clone(), arm])
     };
     assert_eq!(
         arm_list.syntax().to_string(),
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
index ffe9f16cfd526..291fc646e2161 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
@@ -751,7 +751,7 @@ impl ast::MatchArmList {
         ted::insert_all(position, elements);
 
         fn needs_comma(arm: &ast::MatchArm) -> bool {
-            arm.expr().map_or(false, |e| !e.is_block_like()) && arm.comma_token().is_none()
+            arm.expr().is_some_and(|e| !e.is_block_like()) && arm.comma_token().is_none()
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs
index f3053f59836f6..93faeb40c32ba 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs
@@ -10,7 +10,7 @@ use crate::{
         FormatArgsArg, FormatArgsExpr, MacroDef, Static, TokenTree,
     },
     AstToken,
-    SyntaxKind::*,
+    SyntaxKind::{self, *},
     SyntaxNode, SyntaxToken, T,
 };
 
@@ -50,6 +50,27 @@ impl From for ElseBranch {
     }
 }
 
+impl AstNode for ElseBranch {
+    fn can_cast(kind: SyntaxKind) -> bool {
+        ast::BlockExpr::can_cast(kind) || ast::IfExpr::can_cast(kind)
+    }
+
+    fn cast(syntax: SyntaxNode) -> Option {
+        if let Some(block_expr) = ast::BlockExpr::cast(syntax.clone()) {
+            Some(Self::Block(block_expr))
+        } else {
+            ast::IfExpr::cast(syntax).map(Self::IfExpr)
+        }
+    }
+
+    fn syntax(&self) -> &SyntaxNode {
+        match self {
+            ElseBranch::Block(block_expr) => block_expr.syntax(),
+            ElseBranch::IfExpr(if_expr) => if_expr.syntax(),
+        }
+    }
+}
+
 impl ast::IfExpr {
     pub fn condition(&self) -> Option {
         // If the condition is a BlockExpr, check if the then body is missing.
@@ -393,7 +414,7 @@ impl ast::BlockExpr {
             FOR_EXPR | IF_EXPR => parent
                 .children()
                 .find(|it| ast::Expr::can_cast(it.kind()))
-                .map_or(true, |it| it == *self.syntax()),
+                .is_none_or(|it| it == *self.syntax()),
             LET_ELSE | FN | WHILE_EXPR | LOOP_EXPR | CONST_BLOCK_PAT => false,
             _ => true,
         }
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
index 3876ef71a0772..58c76a456ab10 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
@@ -1,4 +1,4 @@
-//! Generated by `cargo codegen grammar`, do not edit by hand.
+//! Generated by `cargo xtask codegen grammar`, do not edit by hand.
 
 #![allow(non_snake_case)]
 use crate::{
@@ -1538,10 +1538,14 @@ impl ast::HasDocComments for RecordField {}
 impl ast::HasName for RecordField {}
 impl ast::HasVisibility for RecordField {}
 impl RecordField {
+    #[inline]
+    pub fn expr(&self) -> Option { support::child(&self.syntax) }
     #[inline]
     pub fn ty(&self) -> Option { support::child(&self.syntax) }
     #[inline]
     pub fn colon_token(&self) -> Option { support::token(&self.syntax, T![:]) }
+    #[inline]
+    pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -2502,6 +2506,13 @@ pub struct AnyHasVisibility {
 }
 impl ast::HasVisibility for AnyHasVisibility {}
 impl AstNode for Abi {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        ABI
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == ABI }
     #[inline]
@@ -2516,6 +2527,13 @@ impl AstNode for Abi {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ArgList {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        ARG_LIST
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == ARG_LIST }
     #[inline]
@@ -2530,6 +2548,13 @@ impl AstNode for ArgList {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ArrayExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        ARRAY_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == ARRAY_EXPR }
     #[inline]
@@ -2544,6 +2569,13 @@ impl AstNode for ArrayExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ArrayType {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        ARRAY_TYPE
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == ARRAY_TYPE }
     #[inline]
@@ -2558,6 +2590,13 @@ impl AstNode for ArrayType {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for AsmClobberAbi {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        ASM_CLOBBER_ABI
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_CLOBBER_ABI }
     #[inline]
@@ -2572,6 +2611,13 @@ impl AstNode for AsmClobberAbi {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for AsmConst {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        ASM_CONST
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_CONST }
     #[inline]
@@ -2586,6 +2632,13 @@ impl AstNode for AsmConst {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for AsmDirSpec {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        ASM_DIR_SPEC
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_DIR_SPEC }
     #[inline]
@@ -2600,6 +2653,13 @@ impl AstNode for AsmDirSpec {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for AsmExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        ASM_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_EXPR }
     #[inline]
@@ -2614,6 +2674,13 @@ impl AstNode for AsmExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for AsmLabel {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        ASM_LABEL
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_LABEL }
     #[inline]
@@ -2628,6 +2695,13 @@ impl AstNode for AsmLabel {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for AsmOperandExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        ASM_OPERAND_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPERAND_EXPR }
     #[inline]
@@ -2642,6 +2716,13 @@ impl AstNode for AsmOperandExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for AsmOperandNamed {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        ASM_OPERAND_NAMED
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPERAND_NAMED }
     #[inline]
@@ -2656,6 +2737,13 @@ impl AstNode for AsmOperandNamed {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for AsmOption {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        ASM_OPTION
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPTION }
     #[inline]
@@ -2670,6 +2758,13 @@ impl AstNode for AsmOption {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for AsmOptions {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        ASM_OPTIONS
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPTIONS }
     #[inline]
@@ -2684,6 +2779,13 @@ impl AstNode for AsmOptions {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for AsmRegOperand {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        ASM_REG_OPERAND
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_REG_OPERAND }
     #[inline]
@@ -2698,6 +2800,13 @@ impl AstNode for AsmRegOperand {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for AsmRegSpec {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        ASM_REG_SPEC
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_REG_SPEC }
     #[inline]
@@ -2712,6 +2821,13 @@ impl AstNode for AsmRegSpec {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for AsmSym {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        ASM_SYM
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_SYM }
     #[inline]
@@ -2726,6 +2842,13 @@ impl AstNode for AsmSym {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for AssocItemList {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        ASSOC_ITEM_LIST
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASSOC_ITEM_LIST }
     #[inline]
@@ -2740,6 +2863,13 @@ impl AstNode for AssocItemList {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for AssocTypeArg {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        ASSOC_TYPE_ARG
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASSOC_TYPE_ARG }
     #[inline]
@@ -2754,6 +2884,13 @@ impl AstNode for AssocTypeArg {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for Attr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        ATTR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == ATTR }
     #[inline]
@@ -2768,6 +2905,13 @@ impl AstNode for Attr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for AwaitExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        AWAIT_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == AWAIT_EXPR }
     #[inline]
@@ -2782,6 +2926,13 @@ impl AstNode for AwaitExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for BecomeExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        BECOME_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == BECOME_EXPR }
     #[inline]
@@ -2796,6 +2947,13 @@ impl AstNode for BecomeExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for BinExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        BIN_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == BIN_EXPR }
     #[inline]
@@ -2810,6 +2968,13 @@ impl AstNode for BinExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for BlockExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        BLOCK_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == BLOCK_EXPR }
     #[inline]
@@ -2824,6 +2989,13 @@ impl AstNode for BlockExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for BoxPat {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        BOX_PAT
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == BOX_PAT }
     #[inline]
@@ -2838,6 +3010,13 @@ impl AstNode for BoxPat {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for BreakExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        BREAK_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == BREAK_EXPR }
     #[inline]
@@ -2852,6 +3031,13 @@ impl AstNode for BreakExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for CallExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        CALL_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == CALL_EXPR }
     #[inline]
@@ -2866,6 +3052,13 @@ impl AstNode for CallExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for CastExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        CAST_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == CAST_EXPR }
     #[inline]
@@ -2880,6 +3073,13 @@ impl AstNode for CastExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ClosureBinder {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        CLOSURE_BINDER
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == CLOSURE_BINDER }
     #[inline]
@@ -2894,6 +3094,13 @@ impl AstNode for ClosureBinder {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ClosureExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        CLOSURE_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == CLOSURE_EXPR }
     #[inline]
@@ -2908,6 +3115,13 @@ impl AstNode for ClosureExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for Const {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        CONST
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == CONST }
     #[inline]
@@ -2922,6 +3136,13 @@ impl AstNode for Const {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ConstArg {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        CONST_ARG
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == CONST_ARG }
     #[inline]
@@ -2936,6 +3157,13 @@ impl AstNode for ConstArg {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ConstBlockPat {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        CONST_BLOCK_PAT
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == CONST_BLOCK_PAT }
     #[inline]
@@ -2950,6 +3178,13 @@ impl AstNode for ConstBlockPat {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ConstParam {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        CONST_PARAM
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == CONST_PARAM }
     #[inline]
@@ -2964,6 +3199,13 @@ impl AstNode for ConstParam {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ContinueExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        CONTINUE_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == CONTINUE_EXPR }
     #[inline]
@@ -2978,6 +3220,13 @@ impl AstNode for ContinueExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for DynTraitType {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        DYN_TRAIT_TYPE
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == DYN_TRAIT_TYPE }
     #[inline]
@@ -2992,6 +3241,13 @@ impl AstNode for DynTraitType {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for Enum {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        ENUM
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == ENUM }
     #[inline]
@@ -3006,6 +3262,13 @@ impl AstNode for Enum {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ExprStmt {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        EXPR_STMT
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == EXPR_STMT }
     #[inline]
@@ -3020,6 +3283,13 @@ impl AstNode for ExprStmt {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ExternBlock {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        EXTERN_BLOCK
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == EXTERN_BLOCK }
     #[inline]
@@ -3034,6 +3304,13 @@ impl AstNode for ExternBlock {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ExternCrate {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        EXTERN_CRATE
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == EXTERN_CRATE }
     #[inline]
@@ -3048,6 +3325,13 @@ impl AstNode for ExternCrate {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ExternItemList {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        EXTERN_ITEM_LIST
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == EXTERN_ITEM_LIST }
     #[inline]
@@ -3062,6 +3346,13 @@ impl AstNode for ExternItemList {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for FieldExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        FIELD_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == FIELD_EXPR }
     #[inline]
@@ -3076,6 +3367,13 @@ impl AstNode for FieldExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for Fn {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        FN
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == FN }
     #[inline]
@@ -3090,6 +3388,13 @@ impl AstNode for Fn {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for FnPtrType {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        FN_PTR_TYPE
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == FN_PTR_TYPE }
     #[inline]
@@ -3104,6 +3409,13 @@ impl AstNode for FnPtrType {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ForExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        FOR_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == FOR_EXPR }
     #[inline]
@@ -3118,6 +3430,13 @@ impl AstNode for ForExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ForType {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        FOR_TYPE
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == FOR_TYPE }
     #[inline]
@@ -3132,6 +3451,13 @@ impl AstNode for ForType {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for FormatArgsArg {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        FORMAT_ARGS_ARG
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == FORMAT_ARGS_ARG }
     #[inline]
@@ -3146,6 +3472,13 @@ impl AstNode for FormatArgsArg {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for FormatArgsExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        FORMAT_ARGS_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == FORMAT_ARGS_EXPR }
     #[inline]
@@ -3160,6 +3493,13 @@ impl AstNode for FormatArgsExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for GenericArgList {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        GENERIC_ARG_LIST
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == GENERIC_ARG_LIST }
     #[inline]
@@ -3174,6 +3514,13 @@ impl AstNode for GenericArgList {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for GenericParamList {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        GENERIC_PARAM_LIST
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == GENERIC_PARAM_LIST }
     #[inline]
@@ -3188,6 +3535,13 @@ impl AstNode for GenericParamList {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for IdentPat {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        IDENT_PAT
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == IDENT_PAT }
     #[inline]
@@ -3202,6 +3556,13 @@ impl AstNode for IdentPat {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for IfExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        IF_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == IF_EXPR }
     #[inline]
@@ -3216,6 +3577,13 @@ impl AstNode for IfExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for Impl {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        IMPL
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == IMPL }
     #[inline]
@@ -3230,6 +3598,13 @@ impl AstNode for Impl {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ImplTraitType {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        IMPL_TRAIT_TYPE
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == IMPL_TRAIT_TYPE }
     #[inline]
@@ -3244,6 +3619,13 @@ impl AstNode for ImplTraitType {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for IndexExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        INDEX_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == INDEX_EXPR }
     #[inline]
@@ -3258,6 +3640,13 @@ impl AstNode for IndexExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for InferType {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        INFER_TYPE
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == INFER_TYPE }
     #[inline]
@@ -3272,6 +3661,13 @@ impl AstNode for InferType {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ItemList {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        ITEM_LIST
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == ITEM_LIST }
     #[inline]
@@ -3286,6 +3682,13 @@ impl AstNode for ItemList {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for Label {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        LABEL
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == LABEL }
     #[inline]
@@ -3300,6 +3703,13 @@ impl AstNode for Label {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for LetElse {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        LET_ELSE
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == LET_ELSE }
     #[inline]
@@ -3314,6 +3724,13 @@ impl AstNode for LetElse {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for LetExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        LET_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == LET_EXPR }
     #[inline]
@@ -3328,6 +3745,13 @@ impl AstNode for LetExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for LetStmt {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        LET_STMT
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == LET_STMT }
     #[inline]
@@ -3342,6 +3766,13 @@ impl AstNode for LetStmt {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for Lifetime {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        LIFETIME
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == LIFETIME }
     #[inline]
@@ -3356,6 +3787,13 @@ impl AstNode for Lifetime {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for LifetimeArg {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        LIFETIME_ARG
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == LIFETIME_ARG }
     #[inline]
@@ -3370,6 +3808,13 @@ impl AstNode for LifetimeArg {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for LifetimeParam {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        LIFETIME_PARAM
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == LIFETIME_PARAM }
     #[inline]
@@ -3384,6 +3829,13 @@ impl AstNode for LifetimeParam {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for Literal {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        LITERAL
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == LITERAL }
     #[inline]
@@ -3398,6 +3850,13 @@ impl AstNode for Literal {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for LiteralPat {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        LITERAL_PAT
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == LITERAL_PAT }
     #[inline]
@@ -3412,6 +3871,13 @@ impl AstNode for LiteralPat {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for LoopExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        LOOP_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == LOOP_EXPR }
     #[inline]
@@ -3426,6 +3892,13 @@ impl AstNode for LoopExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for MacroCall {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        MACRO_CALL
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_CALL }
     #[inline]
@@ -3440,6 +3913,13 @@ impl AstNode for MacroCall {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for MacroDef {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        MACRO_DEF
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_DEF }
     #[inline]
@@ -3454,6 +3934,13 @@ impl AstNode for MacroDef {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for MacroExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        MACRO_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_EXPR }
     #[inline]
@@ -3468,6 +3955,13 @@ impl AstNode for MacroExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for MacroItems {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        MACRO_ITEMS
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_ITEMS }
     #[inline]
@@ -3482,6 +3976,13 @@ impl AstNode for MacroItems {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for MacroPat {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        MACRO_PAT
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_PAT }
     #[inline]
@@ -3496,6 +3997,13 @@ impl AstNode for MacroPat {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for MacroRules {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        MACRO_RULES
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_RULES }
     #[inline]
@@ -3510,6 +4018,13 @@ impl AstNode for MacroRules {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for MacroStmts {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        MACRO_STMTS
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_STMTS }
     #[inline]
@@ -3524,6 +4039,13 @@ impl AstNode for MacroStmts {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for MacroType {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        MACRO_TYPE
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_TYPE }
     #[inline]
@@ -3538,6 +4060,13 @@ impl AstNode for MacroType {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for MatchArm {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        MATCH_ARM
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == MATCH_ARM }
     #[inline]
@@ -3552,6 +4081,13 @@ impl AstNode for MatchArm {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for MatchArmList {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        MATCH_ARM_LIST
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == MATCH_ARM_LIST }
     #[inline]
@@ -3566,6 +4102,13 @@ impl AstNode for MatchArmList {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for MatchExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        MATCH_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == MATCH_EXPR }
     #[inline]
@@ -3580,6 +4123,13 @@ impl AstNode for MatchExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for MatchGuard {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        MATCH_GUARD
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == MATCH_GUARD }
     #[inline]
@@ -3594,6 +4144,13 @@ impl AstNode for MatchGuard {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for Meta {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        META
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == META }
     #[inline]
@@ -3608,6 +4165,13 @@ impl AstNode for Meta {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for MethodCallExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        METHOD_CALL_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == METHOD_CALL_EXPR }
     #[inline]
@@ -3622,6 +4186,13 @@ impl AstNode for MethodCallExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for Module {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        MODULE
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == MODULE }
     #[inline]
@@ -3636,6 +4207,13 @@ impl AstNode for Module {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for Name {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        NAME
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == NAME }
     #[inline]
@@ -3650,6 +4228,13 @@ impl AstNode for Name {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for NameRef {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        NAME_REF
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == NAME_REF }
     #[inline]
@@ -3664,6 +4249,13 @@ impl AstNode for NameRef {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for NeverType {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        NEVER_TYPE
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == NEVER_TYPE }
     #[inline]
@@ -3678,6 +4270,13 @@ impl AstNode for NeverType {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for OffsetOfExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        OFFSET_OF_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == OFFSET_OF_EXPR }
     #[inline]
@@ -3692,6 +4291,13 @@ impl AstNode for OffsetOfExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for OrPat {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        OR_PAT
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == OR_PAT }
     #[inline]
@@ -3706,6 +4312,13 @@ impl AstNode for OrPat {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for Param {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        PARAM
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == PARAM }
     #[inline]
@@ -3720,6 +4333,13 @@ impl AstNode for Param {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ParamList {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        PARAM_LIST
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == PARAM_LIST }
     #[inline]
@@ -3734,6 +4354,13 @@ impl AstNode for ParamList {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ParenExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        PAREN_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == PAREN_EXPR }
     #[inline]
@@ -3748,6 +4375,13 @@ impl AstNode for ParenExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ParenPat {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        PAREN_PAT
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == PAREN_PAT }
     #[inline]
@@ -3762,6 +4396,13 @@ impl AstNode for ParenPat {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ParenType {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        PAREN_TYPE
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == PAREN_TYPE }
     #[inline]
@@ -3776,6 +4417,13 @@ impl AstNode for ParenType {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ParenthesizedArgList {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        PARENTHESIZED_ARG_LIST
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == PARENTHESIZED_ARG_LIST }
     #[inline]
@@ -3790,6 +4438,13 @@ impl AstNode for ParenthesizedArgList {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for Path {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        PATH
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == PATH }
     #[inline]
@@ -3804,6 +4459,13 @@ impl AstNode for Path {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for PathExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        PATH_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == PATH_EXPR }
     #[inline]
@@ -3818,6 +4480,13 @@ impl AstNode for PathExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for PathPat {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        PATH_PAT
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == PATH_PAT }
     #[inline]
@@ -3832,6 +4501,13 @@ impl AstNode for PathPat {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for PathSegment {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        PATH_SEGMENT
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == PATH_SEGMENT }
     #[inline]
@@ -3846,6 +4522,13 @@ impl AstNode for PathSegment {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for PathType {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        PATH_TYPE
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == PATH_TYPE }
     #[inline]
@@ -3860,6 +4543,13 @@ impl AstNode for PathType {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for PrefixExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        PREFIX_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == PREFIX_EXPR }
     #[inline]
@@ -3874,6 +4564,13 @@ impl AstNode for PrefixExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for PtrType {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        PTR_TYPE
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == PTR_TYPE }
     #[inline]
@@ -3888,6 +4585,13 @@ impl AstNode for PtrType {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for RangeExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        RANGE_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == RANGE_EXPR }
     #[inline]
@@ -3902,6 +4606,13 @@ impl AstNode for RangeExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for RangePat {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        RANGE_PAT
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == RANGE_PAT }
     #[inline]
@@ -3916,6 +4627,13 @@ impl AstNode for RangePat {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for RecordExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        RECORD_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_EXPR }
     #[inline]
@@ -3930,6 +4648,13 @@ impl AstNode for RecordExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for RecordExprField {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        RECORD_EXPR_FIELD
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_EXPR_FIELD }
     #[inline]
@@ -3944,6 +4669,13 @@ impl AstNode for RecordExprField {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for RecordExprFieldList {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        RECORD_EXPR_FIELD_LIST
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_EXPR_FIELD_LIST }
     #[inline]
@@ -3958,6 +4690,13 @@ impl AstNode for RecordExprFieldList {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for RecordField {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        RECORD_FIELD
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_FIELD }
     #[inline]
@@ -3972,6 +4711,13 @@ impl AstNode for RecordField {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for RecordFieldList {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        RECORD_FIELD_LIST
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_FIELD_LIST }
     #[inline]
@@ -3986,6 +4732,13 @@ impl AstNode for RecordFieldList {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for RecordPat {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        RECORD_PAT
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_PAT }
     #[inline]
@@ -4000,6 +4753,13 @@ impl AstNode for RecordPat {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for RecordPatField {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        RECORD_PAT_FIELD
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_PAT_FIELD }
     #[inline]
@@ -4014,6 +4774,13 @@ impl AstNode for RecordPatField {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for RecordPatFieldList {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        RECORD_PAT_FIELD_LIST
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == RECORD_PAT_FIELD_LIST }
     #[inline]
@@ -4028,6 +4795,13 @@ impl AstNode for RecordPatFieldList {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for RefExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        REF_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == REF_EXPR }
     #[inline]
@@ -4042,6 +4816,13 @@ impl AstNode for RefExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for RefPat {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        REF_PAT
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == REF_PAT }
     #[inline]
@@ -4056,6 +4837,13 @@ impl AstNode for RefPat {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for RefType {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        REF_TYPE
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == REF_TYPE }
     #[inline]
@@ -4070,6 +4858,13 @@ impl AstNode for RefType {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for Rename {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        RENAME
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == RENAME }
     #[inline]
@@ -4084,6 +4879,13 @@ impl AstNode for Rename {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for RestPat {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        REST_PAT
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == REST_PAT }
     #[inline]
@@ -4098,6 +4900,13 @@ impl AstNode for RestPat {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for RetType {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        RET_TYPE
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == RET_TYPE }
     #[inline]
@@ -4112,6 +4921,13 @@ impl AstNode for RetType {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ReturnExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        RETURN_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == RETURN_EXPR }
     #[inline]
@@ -4126,6 +4942,13 @@ impl AstNode for ReturnExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for ReturnTypeSyntax {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        RETURN_TYPE_SYNTAX
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == RETURN_TYPE_SYNTAX }
     #[inline]
@@ -4140,6 +4963,13 @@ impl AstNode for ReturnTypeSyntax {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for SelfParam {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        SELF_PARAM
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == SELF_PARAM }
     #[inline]
@@ -4154,6 +4984,13 @@ impl AstNode for SelfParam {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for SlicePat {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        SLICE_PAT
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == SLICE_PAT }
     #[inline]
@@ -4168,6 +5005,13 @@ impl AstNode for SlicePat {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for SliceType {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        SLICE_TYPE
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == SLICE_TYPE }
     #[inline]
@@ -4182,6 +5026,13 @@ impl AstNode for SliceType {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for SourceFile {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        SOURCE_FILE
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == SOURCE_FILE }
     #[inline]
@@ -4196,6 +5047,13 @@ impl AstNode for SourceFile {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for Static {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        STATIC
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == STATIC }
     #[inline]
@@ -4210,6 +5068,13 @@ impl AstNode for Static {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for StmtList {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        STMT_LIST
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == STMT_LIST }
     #[inline]
@@ -4224,6 +5089,13 @@ impl AstNode for StmtList {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for Struct {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        STRUCT
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == STRUCT }
     #[inline]
@@ -4238,6 +5110,13 @@ impl AstNode for Struct {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for TokenTree {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        TOKEN_TREE
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == TOKEN_TREE }
     #[inline]
@@ -4252,6 +5131,13 @@ impl AstNode for TokenTree {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for Trait {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        TRAIT
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == TRAIT }
     #[inline]
@@ -4266,6 +5152,13 @@ impl AstNode for Trait {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for TraitAlias {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        TRAIT_ALIAS
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == TRAIT_ALIAS }
     #[inline]
@@ -4280,6 +5173,13 @@ impl AstNode for TraitAlias {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for TryExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        TRY_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == TRY_EXPR }
     #[inline]
@@ -4294,6 +5194,13 @@ impl AstNode for TryExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for TupleExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        TUPLE_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == TUPLE_EXPR }
     #[inline]
@@ -4308,6 +5215,13 @@ impl AstNode for TupleExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for TupleField {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        TUPLE_FIELD
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == TUPLE_FIELD }
     #[inline]
@@ -4322,6 +5236,13 @@ impl AstNode for TupleField {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for TupleFieldList {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        TUPLE_FIELD_LIST
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == TUPLE_FIELD_LIST }
     #[inline]
@@ -4336,6 +5257,13 @@ impl AstNode for TupleFieldList {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for TuplePat {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        TUPLE_PAT
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == TUPLE_PAT }
     #[inline]
@@ -4350,6 +5278,13 @@ impl AstNode for TuplePat {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for TupleStructPat {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        TUPLE_STRUCT_PAT
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == TUPLE_STRUCT_PAT }
     #[inline]
@@ -4364,6 +5299,13 @@ impl AstNode for TupleStructPat {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for TupleType {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        TUPLE_TYPE
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == TUPLE_TYPE }
     #[inline]
@@ -4378,6 +5320,13 @@ impl AstNode for TupleType {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for TypeAlias {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        TYPE_ALIAS
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_ALIAS }
     #[inline]
@@ -4392,6 +5341,13 @@ impl AstNode for TypeAlias {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for TypeArg {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        TYPE_ARG
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_ARG }
     #[inline]
@@ -4406,6 +5362,13 @@ impl AstNode for TypeArg {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for TypeBound {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        TYPE_BOUND
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_BOUND }
     #[inline]
@@ -4420,6 +5383,13 @@ impl AstNode for TypeBound {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for TypeBoundList {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        TYPE_BOUND_LIST
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_BOUND_LIST }
     #[inline]
@@ -4434,6 +5404,13 @@ impl AstNode for TypeBoundList {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for TypeParam {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        TYPE_PARAM
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == TYPE_PARAM }
     #[inline]
@@ -4448,6 +5425,13 @@ impl AstNode for TypeParam {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for UnderscoreExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        UNDERSCORE_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == UNDERSCORE_EXPR }
     #[inline]
@@ -4462,6 +5446,13 @@ impl AstNode for UnderscoreExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for Union {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        UNION
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == UNION }
     #[inline]
@@ -4476,6 +5467,13 @@ impl AstNode for Union {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for Use {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        USE
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == USE }
     #[inline]
@@ -4490,6 +5488,13 @@ impl AstNode for Use {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for UseBoundGenericArgs {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        USE_BOUND_GENERIC_ARGS
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == USE_BOUND_GENERIC_ARGS }
     #[inline]
@@ -4504,6 +5509,13 @@ impl AstNode for UseBoundGenericArgs {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for UseTree {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        USE_TREE
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == USE_TREE }
     #[inline]
@@ -4518,6 +5530,13 @@ impl AstNode for UseTree {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for UseTreeList {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        USE_TREE_LIST
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == USE_TREE_LIST }
     #[inline]
@@ -4532,6 +5551,13 @@ impl AstNode for UseTreeList {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for Variant {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        VARIANT
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == VARIANT }
     #[inline]
@@ -4546,6 +5572,13 @@ impl AstNode for Variant {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for VariantList {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        VARIANT_LIST
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == VARIANT_LIST }
     #[inline]
@@ -4560,6 +5593,13 @@ impl AstNode for VariantList {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for Visibility {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        VISIBILITY
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == VISIBILITY }
     #[inline]
@@ -4574,6 +5614,13 @@ impl AstNode for Visibility {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for WhereClause {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        WHERE_CLAUSE
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == WHERE_CLAUSE }
     #[inline]
@@ -4588,6 +5635,13 @@ impl AstNode for WhereClause {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for WherePred {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        WHERE_PRED
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == WHERE_PRED }
     #[inline]
@@ -4602,6 +5656,13 @@ impl AstNode for WherePred {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for WhileExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        WHILE_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == WHILE_EXPR }
     #[inline]
@@ -4616,6 +5677,13 @@ impl AstNode for WhileExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for WildcardPat {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        WILDCARD_PAT
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == WILDCARD_PAT }
     #[inline]
@@ -4630,6 +5698,13 @@ impl AstNode for WildcardPat {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for YeetExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        YEET_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == YEET_EXPR }
     #[inline]
@@ -4644,6 +5719,13 @@ impl AstNode for YeetExpr {
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
 impl AstNode for YieldExpr {
+    #[inline]
+    fn kind() -> SyntaxKind
+    where
+        Self: Sized,
+    {
+        YIELD_EXPR
+    }
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == YIELD_EXPR }
     #[inline]
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs
index 85d20c2bd8ce4..df2e9619db1c3 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs
@@ -1,4 +1,4 @@
-//! Generated by `cargo codegen grammar`, do not edit by hand.
+//! Generated by `cargo xtask codegen grammar`, do not edit by hand.
 
 use crate::{
     ast::AstToken,
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
index eb96ab6ef5903..ff027ac5848b3 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
@@ -8,7 +8,10 @@
 //! Keep in mind that `from_text` functions should be kept private. The public
 //! API should require to assemble every node piecewise. The trick of
 //! `parse(format!())` we use internally is an implementation detail -- long
-//! term, it will be replaced with direct tree manipulation.
+//! term, it will be replaced with `quote!`. Do not add more usages to `from_text` -
+//! use `quote!` instead.
+
+mod quote;
 
 use itertools::Itertools;
 use parser::{Edition, T};
@@ -16,7 +19,7 @@ use rowan::NodeOrToken;
 use stdx::{format_to, format_to_acc, never};
 
 use crate::{
-    ast::{self, Param},
+    ast::{self, make::quote::quote, Param},
     utils::is_raw_identifier,
     AstNode, SourceFile, SyntaxKind, SyntaxToken,
 };
@@ -60,6 +63,9 @@ pub mod ext {
         Some(expr)
     }
 
+    pub fn expr_unit() -> ast::Expr {
+        expr_tuple([]).into()
+    }
     pub fn expr_unreachable() -> ast::Expr {
         expr_from_text("unreachable!()")
     }
@@ -118,7 +124,11 @@ pub fn name(name: &str) -> ast::Name {
 }
 pub fn name_ref(name_ref: &str) -> ast::NameRef {
     let raw_escape = raw_ident_esc(name_ref);
-    ast_from_text(&format!("fn f() {{ {raw_escape}{name_ref}; }}"))
+    quote! {
+        NameRef {
+            [IDENT format!("{raw_escape}{name_ref}")]
+        }
+    }
 }
 fn raw_ident_esc(ident: &str) -> &'static str {
     if is_raw_identifier(ident, Edition::CURRENT) {
@@ -135,7 +145,11 @@ pub fn lifetime(text: &str) -> ast::Lifetime {
         tmp = format!("'{text}");
         text = &tmp;
     }
-    ast_from_text(&format!("fn f<{text}>() {{ }}"))
+    quote! {
+        Lifetime {
+            [LIFETIME_IDENT text]
+        }
+    }
 }
 
 // FIXME: replace stringly-typed constructor with a family of typed ctors, a-la
@@ -175,63 +189,37 @@ pub fn ty_alias(
     where_clause: Option,
     assignment: Option<(ast::Type, Option)>,
 ) -> ast::TypeAlias {
-    let mut s = String::new();
-    s.push_str(&format!("type {ident}"));
-
-    if let Some(list) = generic_param_list {
-        s.push_str(&list.to_string());
-    }
-
-    if let Some(list) = type_param_bounds {
-        s.push_str(&format!(" : {list}"));
-    }
-
-    if let Some(cl) = where_clause {
-        s.push_str(&format!(" {cl}"));
-    }
-
-    if let Some(exp) = assignment {
-        if let Some(cl) = exp.1 {
-            s.push_str(&format!(" = {} {cl}", exp.0));
-        } else {
-            s.push_str(&format!(" = {}", exp.0));
+    let (assignment_ty, assignment_where) = assignment.unzip();
+    let assignment_where = assignment_where.flatten();
+    quote! {
+        TypeAlias {
+            [type] " "
+                Name { [IDENT ident] }
+                #generic_param_list
+                #(" " [:] " " #type_param_bounds)*
+                #(" " #where_clause)*
+                #(" " [=] " " #assignment_ty)*
+                #(" " #assignment_where)*
+            [;]
         }
     }
-
-    s.push(';');
-    ast_from_text(&s)
 }
 
 pub fn ty_fn_ptr>(
-    for_lifetime_list: Option,
     is_unsafe: bool,
     abi: Option,
-    params: I,
+    mut params: I,
     ret_type: Option,
 ) -> ast::FnPtrType {
-    let mut s = String::from("type __ = ");
-
-    if let Some(list) = for_lifetime_list {
-        format_to!(s, "for{} ", list);
-    }
-
-    if is_unsafe {
-        s.push_str("unsafe ");
-    }
-
-    if let Some(abi) = abi {
-        format_to!(s, "{} ", abi)
-    }
-
-    s.push_str("fn");
-
-    format_to!(s, "({})", params.map(|p| p.to_string()).join(", "));
-
-    if let Some(ret_type) = ret_type {
-        format_to!(s, " {}", ret_type);
+    let is_unsafe = is_unsafe.then_some(());
+    let first_param = params.next();
+    quote! {
+        FnPtrType {
+            #(#is_unsafe [unsafe] " ")* #(#abi " ")* [fn]
+                ['('] #first_param #([,] " " #params)* [')']
+                #(" " #ret_type)*
+        }
     }
-
-    ast_from_text(&s)
 }
 
 pub fn assoc_item_list() -> ast::AssocItemList {
@@ -351,6 +339,24 @@ pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment {
     ast_from_text(&format!("type __ = {name_ref};"))
 }
 
+/// Type and expressions/patterns path differ in whether they require `::` before generic arguments.
+/// Type paths allow them but they are often omitted, while expression/pattern paths require them.
+pub fn generic_ty_path_segment(
+    name_ref: ast::NameRef,
+    generic_args: impl IntoIterator,
+) -> ast::PathSegment {
+    let mut generic_args = generic_args.into_iter();
+    let first_generic_arg = generic_args.next();
+    quote! {
+        PathSegment {
+            #name_ref
+            GenericArgList {
+                [<] #first_generic_arg #([,] " " #generic_args)* [>]
+            }
+        }
+    }
+}
+
 pub fn path_segment_ty(type_ref: ast::Type, trait_ref: Option) -> ast::PathSegment {
     let text = match trait_ref {
         Some(trait_ref) => format!("fn f(x: <{type_ref} as {trait_ref}>) {{}}"),
@@ -405,6 +411,11 @@ pub fn path_from_text(text: &str) -> ast::Path {
     ast_from_text(&format!("fn main() {{ let test: {text}; }}"))
 }
 
+// FIXME: should not be pub
+pub fn path_from_text_with_edition(text: &str, edition: Edition) -> ast::Path {
+    ast_from_text_with_edition(&format!("fn main() {{ let test: {text}; }}"), edition)
+}
+
 pub fn use_tree_glob() -> ast::UseTree {
     ast_from_text("use *;")
 }
@@ -480,15 +491,16 @@ pub fn block_expr(
     stmts: impl IntoIterator,
     tail_expr: Option,
 ) -> ast::BlockExpr {
-    let mut buf = "{\n".to_owned();
-    for stmt in stmts.into_iter() {
-        format_to!(buf, "    {stmt}\n");
-    }
-    if let Some(tail_expr) = tail_expr {
-        format_to!(buf, "    {tail_expr}\n");
+    quote! {
+        BlockExpr {
+            StmtList {
+                ['{'] "\n"
+                #("    " #stmts "\n")*
+                #("    " #tail_expr "\n")*
+                ['}']
+            }
+        }
     }
-    buf += "}";
-    ast_from_text(&format!("fn f() {buf}"))
 }
 
 pub fn async_move_block_expr(
@@ -542,10 +554,6 @@ pub fn hacky_block_expr(
     ast_from_text(&format!("fn f() {buf}"))
 }
 
-pub fn expr_unit() -> ast::Expr {
-    expr_from_text("()")
-}
-
 pub fn expr_literal(text: &str) -> ast::Literal {
     assert_eq!(text.trim(), text);
     ast_from_text(&format!("fn f() {{ let _ = {text}; }}"))
@@ -555,8 +563,8 @@ pub fn expr_const_value(text: &str) -> ast::ConstArg {
     ast_from_text(&format!("trait Foo {{}}"))
 }
 
-pub fn expr_empty_block() -> ast::Expr {
-    expr_from_text("{}")
+pub fn expr_empty_block() -> ast::BlockExpr {
+    ast_from_text("const C: () = {};")
 }
 pub fn expr_path(path: ast::Path) -> ast::Expr {
     expr_from_text(&path.to_string())
@@ -596,14 +604,14 @@ pub fn expr_try(expr: ast::Expr) -> ast::Expr {
 pub fn expr_await(expr: ast::Expr) -> ast::Expr {
     expr_from_text(&format!("{expr}.await"))
 }
-pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::Expr {
+pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::MatchExpr {
     expr_from_text(&format!("match {expr} {match_arm_list}"))
 }
 pub fn expr_if(
     condition: ast::Expr,
     then_branch: ast::BlockExpr,
     else_branch: Option,
-) -> ast::Expr {
+) -> ast::IfExpr {
     let else_branch = match else_branch {
         Some(ast::ElseBranch::Block(block)) => format!("else {block}"),
         Some(ast::ElseBranch::IfExpr(if_expr)) => format!("else {if_expr}"),
@@ -619,7 +627,7 @@ pub fn expr_loop(block: ast::BlockExpr) -> ast::Expr {
     expr_from_text(&format!("loop {block}"))
 }
 
-pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::Expr {
+pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::PrefixExpr {
     let token = token(op);
     expr_from_text(&format!("{token}{expr}"))
 }
@@ -652,14 +660,14 @@ pub fn expr_field(receiver: ast::Expr, field: &str) -> ast::Expr {
 pub fn expr_paren(expr: ast::Expr) -> ast::Expr {
     expr_from_text(&format!("({expr})"))
 }
-pub fn expr_tuple(elements: impl IntoIterator) -> ast::Expr {
+pub fn expr_tuple(elements: impl IntoIterator) -> ast::TupleExpr {
     let expr = elements.into_iter().format(", ");
     expr_from_text(&format!("({expr})"))
 }
 pub fn expr_assignment(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr {
     expr_from_text(&format!("{lhs} = {rhs}"))
 }
-fn expr_from_text(text: &str) -> ast::Expr {
+fn expr_from_text + AstNode>(text: &str) -> E {
     ast_from_text(&format!("const C: () = {text};"))
 }
 pub fn expr_let(pattern: ast::Pat, expr: ast::Expr) -> ast::LetExpr {
@@ -784,15 +792,21 @@ pub fn path_pat(path: ast::Path) -> ast::Pat {
     }
 }
 
-pub fn match_arm(
-    pats: impl IntoIterator,
-    guard: Option,
-    expr: ast::Expr,
-) -> ast::MatchArm {
-    let pats_str = pats.into_iter().join(" | ");
+/// Returns a `Pat` if the path has just one segment, an `OrPat` otherwise.
+pub fn or_pat(pats: impl IntoIterator, leading_pipe: bool) -> ast::Pat {
+    let leading_pipe = if leading_pipe { "| " } else { "" };
+    let pats = pats.into_iter().join(" | ");
+
+    return from_text(&format!("{leading_pipe}{pats}"));
+    fn from_text(text: &str) -> ast::Pat {
+        ast_from_text(&format!("fn f({text}: ())"))
+    }
+}
+
+pub fn match_arm(pat: ast::Pat, guard: Option, expr: ast::Expr) -> ast::MatchArm {
     return match guard {
-        Some(guard) => from_text(&format!("{pats_str} if {guard} => {expr}")),
-        None => from_text(&format!("{pats_str} => {expr}")),
+        Some(guard) => from_text(&format!("{pat} {guard} => {expr}")),
+        None => from_text(&format!("{pat} => {expr}")),
     };
 
     fn from_text(text: &str) -> ast::MatchArm {
@@ -813,9 +827,17 @@ pub fn match_arm_with_guard(
     }
 }
 
+pub fn match_guard(condition: ast::Expr) -> ast::MatchGuard {
+    return from_text(&format!("if {condition}"));
+
+    fn from_text(text: &str) -> ast::MatchGuard {
+        ast_from_text(&format!("fn f() {{ match () {{() {text} => () }}"))
+    }
+}
+
 pub fn match_arm_list(arms: impl IntoIterator) -> ast::MatchArmList {
     let arms_str = arms.into_iter().fold(String::new(), |mut acc, arm| {
-        let needs_comma = arm.expr().map_or(true, |it| !it.is_block_like());
+        let needs_comma = arm.expr().is_none_or(|it| !it.is_block_like());
         let comma = if needs_comma { "," } else { "" };
         let arm = arm.syntax();
         format_to_acc!(acc, "    {arm}{comma}\n")
@@ -828,7 +850,7 @@ pub fn match_arm_list(arms: impl IntoIterator) -> ast::Mat
 }
 
 pub fn where_pred(
-    path: ast::Path,
+    path: ast::Type,
     bounds: impl IntoIterator,
 ) -> ast::WherePred {
     let bounds = bounds.into_iter().join(" + ");
@@ -1213,7 +1235,12 @@ pub fn token_tree(
 
 #[track_caller]
 fn ast_from_text(text: &str) -> N {
-    let parse = SourceFile::parse(text, Edition::CURRENT);
+    ast_from_text_with_edition(text, Edition::CURRENT)
+}
+
+#[track_caller]
+fn ast_from_text_with_edition(text: &str, edition: Edition) -> N {
+    let parse = SourceFile::parse(text, edition);
     let node = match parse.tree().syntax().descendants().find_map(N::cast) {
         Some(it) => it,
         None => {
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make/quote.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make/quote.rs
new file mode 100644
index 0000000000000..300ef25c137cf
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make/quote.rs
@@ -0,0 +1,191 @@
+//! A `quote!`-like API for crafting AST nodes.
+
+pub(crate) use rowan::{GreenNode, GreenToken, NodeOrToken, SyntaxKind as RSyntaxKind};
+
+macro_rules! quote_impl_ {
+    ( @append $children:ident ) => {}; // Base case.
+
+    ( @append $children:ident
+        $node:ident {
+            $($tree:tt)*
+        }
+        $($rest:tt)*
+    ) => {
+        {
+            #[allow(unused_mut)]
+            let mut inner_children = ::std::vec::Vec::<$crate::ast::make::quote::NodeOrToken<
+                $crate::ast::make::quote::GreenNode,
+                $crate::ast::make::quote::GreenToken,
+            >>::new();
+            $crate::ast::make::quote::quote_impl!( @append inner_children
+                $($tree)*
+            );
+            let kind = <$crate::ast::$node as $crate::ast::AstNode>::kind();
+            let node = $crate::ast::make::quote::GreenNode::new($crate::ast::make::quote::RSyntaxKind(kind as u16), inner_children);
+            $children.push($crate::ast::make::quote::NodeOrToken::Node(node));
+        }
+        $crate::ast::make::quote::quote_impl!( @append $children $($rest)* );
+    };
+
+    ( @append $children:ident
+        [ $token_kind:ident $token_text:expr ]
+        $($rest:tt)*
+    ) => {
+        $children.push($crate::ast::make::quote::NodeOrToken::Token(
+            $crate::ast::make::quote::GreenToken::new(
+                $crate::ast::make::quote::RSyntaxKind($crate::SyntaxKind::$token_kind as u16),
+                &$token_text,
+            ),
+        ));
+        $crate::ast::make::quote::quote_impl!( @append $children $($rest)* );
+    };
+
+    ( @append $children:ident
+        [$($token:tt)+]
+        $($rest:tt)*
+    ) => {
+        $children.push($crate::ast::make::quote::NodeOrToken::Token(
+            $crate::ast::make::quote::GreenToken::new(
+                $crate::ast::make::quote::RSyntaxKind($crate::T![ $($token)+ ] as u16),
+                const { $crate::T![ $($token)+ ].text() },
+            ),
+        ));
+        $crate::ast::make::quote::quote_impl!( @append $children $($rest)* );
+    };
+
+    ( @append $children:ident
+        $whitespace:literal
+        $($rest:tt)*
+    ) => {
+        const { $crate::ast::make::quote::verify_only_whitespaces($whitespace) };
+        $children.push($crate::ast::make::quote::NodeOrToken::Token(
+            $crate::ast::make::quote::GreenToken::new(
+                $crate::ast::make::quote::RSyntaxKind($crate::SyntaxKind::WHITESPACE as u16),
+                $whitespace,
+            ),
+        ));
+        $crate::ast::make::quote::quote_impl!( @append $children $($rest)* );
+    };
+
+    ( @append $children:ident
+        # $var:ident
+        $($rest:tt)*
+    ) => {
+        $crate::ast::make::quote::ToNodeChild::append_node_child($var, &mut $children);
+        $crate::ast::make::quote::quote_impl!( @append $children $($rest)* );
+    };
+
+    ( @append $children:ident
+        #( $($repetition:tt)+ )*
+        $($rest:tt)*
+    ) => {
+        $crate::ast::make::quote::quote_impl!( @extract_pounded_in_repetition $children
+            [] [] $($repetition)*
+        );
+        $crate::ast::make::quote::quote_impl!( @append $children $($rest)* );
+    };
+
+    // Base case - no repetition var.
+    ( @extract_pounded_in_repetition $children:ident
+        [ $($repetition:tt)* ] [ ]
+    ) => {
+        ::std::compile_error!("repetition in `ast::make::quote!()` without variable");
+    };
+
+    // Base case - repetition var found.
+    ( @extract_pounded_in_repetition $children:ident
+        [ $($repetition:tt)* ] [ $repetition_var:ident ]
+    ) => {
+        ::std::iter::IntoIterator::into_iter($repetition_var).for_each(|$repetition_var| {
+            $crate::ast::make::quote::quote_impl!( @append $children $($repetition)* );
+        });
+    };
+
+    ( @extract_pounded_in_repetition $children:ident
+        [ $($repetition:tt)* ] [ $repetition_var1:ident ] # $repetition_var2:ident $($rest:tt)*
+    ) => {
+        ::std::compile_error!("repetition in `ast::make::quote!()` with more than one variable");
+    };
+
+    ( @extract_pounded_in_repetition $children:ident
+        [ $($repetition:tt)* ] [ ] # $repetition_var:ident $($rest:tt)*
+    ) => {
+        $crate::ast::make::quote::quote_impl!( @extract_pounded_in_repetition $children
+            [ $($repetition)* # $repetition_var ] [ $repetition_var ] $($rest)*
+        );
+    };
+
+    ( @extract_pounded_in_repetition $children:ident
+        [ $($repetition:tt)* ] [ $($repetition_var:tt)* ] $non_repetition_var:tt $($rest:tt)*
+    ) => {
+        $crate::ast::make::quote::quote_impl!( @extract_pounded_in_repetition $children
+            [ $($repetition)* $non_repetition_var ] [ $($repetition_var)* ] $($rest)*
+        );
+    };
+}
+pub(crate) use quote_impl_ as quote_impl;
+
+/// A `quote!`-like API for crafting AST nodes.
+///
+/// Syntax: AST nodes are created with `Node { children }`, where `Node` is the node name in `ast` (`ast::Node`).
+/// Tokens are creates with their syntax enclosed by brackets, e.g. `[::]` or `['{']`. Alternatively, tokens can
+/// be created with the syntax `[token_kind token_text]`, where `token_kind` is a variant of `SyntaxKind` (e.g.
+/// `IDENT`) and `token_text` is an expression producing `String` or `&str`. Whitespaces can be added
+/// as string literals (i.e. `"\n    "` is a whitespace token). Interpolation is allowed with `#` (`#variable`),
+/// from `AstNode`s and `Option`s of them. Repetition is also supported, with only one repeating variable
+/// and no separator (`#("\n" #variable [>])*`), for any `IntoIterator`. Note that `Option`s are also `IntoIterator`,
+/// which can help when you want to conditionally include something along with an optional node.
+///
+/// There needs to be one root node, and its type is returned.
+///
+/// Be careful to closely match the Ungrammar AST, there is no validation for this!
+macro_rules! quote_ {
+    ( $root:ident { $($tree:tt)* } ) => {{
+        #[allow(unused_mut)]
+        let mut root = ::std::vec::Vec::<$crate::ast::make::quote::NodeOrToken<
+            $crate::ast::make::quote::GreenNode,
+            $crate::ast::make::quote::GreenToken,
+        >>::with_capacity(1);
+        $crate::ast::make::quote::quote_impl!( @append root $root { $($tree)* } );
+        let root = root.into_iter().next().unwrap();
+        let root = $crate::SyntaxNode::new_root(root.into_node().unwrap());
+        <$crate::ast::$root as $crate::ast::AstNode>::cast(root).unwrap()
+    }};
+}
+pub(crate) use quote_ as quote;
+
+use crate::AstNode;
+
+pub(crate) trait ToNodeChild {
+    fn append_node_child(self, children: &mut Vec>);
+}
+
+impl ToNodeChild for N {
+    fn append_node_child(self, children: &mut Vec>) {
+        children.push((*self.syntax().clone_subtree().green()).to_owned().into());
+    }
+}
+
+impl ToNodeChild for Option {
+    fn append_node_child(self, children: &mut Vec>) {
+        if let Some(child) = self {
+            child.append_node_child(children);
+        }
+    }
+}
+
+// This is useful when you want conditionally, based on some `bool`, to emit some code.
+impl ToNodeChild for () {
+    fn append_node_child(self, _children: &mut Vec>) {}
+}
+
+pub(crate) const fn verify_only_whitespaces(text: &str) {
+    let text = text.as_bytes();
+    let mut i = 0;
+    while i < text.len() {
+        if !text[i].is_ascii_whitespace() {
+            panic!("non-whitespace found in whitespace token");
+        }
+        i += 1;
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
index 6ec73e76f78d0..56f94b965e320 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
@@ -185,6 +185,14 @@ impl ast::Attr {
         Some((self.simple_name()?, tt))
     }
 
+    pub fn as_simple_path(&self) -> Option {
+        let meta = self.meta()?;
+        if meta.eq_token().is_some() || meta.token_tree().is_some() {
+            return None;
+        }
+        self.path()
+    }
+
     pub fn simple_name(&self) -> Option {
         let path = self.meta()?.path()?;
         match (path.segment(), path.qualifier()) {
@@ -333,7 +341,7 @@ impl ast::Path {
 
 impl ast::Use {
     pub fn is_simple_glob(&self) -> bool {
-        self.use_tree().map_or(false, |use_tree| {
+        self.use_tree().is_some_and(|use_tree| {
             use_tree.use_tree_list().is_none() && use_tree.star_token().is_some()
         })
     }
@@ -387,7 +395,7 @@ impl ast::UseTreeList {
             if let Some((single_subtree,)) = u.use_trees().collect_tuple() {
                 // We have a single subtree, check whether it is self.
 
-                let is_self = single_subtree.path().as_ref().map_or(false, |path| {
+                let is_self = single_subtree.path().as_ref().is_some_and(|path| {
                     path.segment().and_then(|seg| seg.self_token()).is_some()
                         && path.qualifier().is_none()
                 });
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs
index 28089ffb3771f..5d33f132ac150 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs
@@ -5,7 +5,122 @@ use crate::{
     match_ast, AstNode, SyntaxNode,
 };
 
+#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
+pub enum ExprPrecedence {
+    // return, break, yield, closures
+    Jump,
+    // = += -= *= /= %= &= |= ^= <<= >>=
+    Assign,
+    // .. ..=
+    Range,
+    // ||
+    LOr,
+    // &&
+    LAnd,
+    // == != < > <= >=
+    Compare,
+    // |
+    BitOr,
+    // ^
+    BitXor,
+    // &
+    BitAnd,
+    // << >>
+    Shift,
+    // + -
+    Sum,
+    // * / %
+    Product,
+    // as
+    Cast,
+    // unary - * ! & &mut
+    Prefix,
+    // paths, loops, function calls, array indexing, field expressions, method calls
+    Unambiguous,
+}
+
+#[derive(PartialEq, Debug)]
+pub enum Fixity {
+    /// The operator is left-associative
+    Left,
+    /// The operator is right-associative
+    Right,
+    /// The operator is not associative
+    None,
+}
+
+pub fn precedence(expr: &ast::Expr) -> ExprPrecedence {
+    match expr {
+        Expr::ClosureExpr(closure) => match closure.ret_type() {
+            None => ExprPrecedence::Jump,
+            Some(_) => ExprPrecedence::Unambiguous,
+        },
+
+        Expr::BreakExpr(_)
+        | Expr::ContinueExpr(_)
+        | Expr::ReturnExpr(_)
+        | Expr::YeetExpr(_)
+        | Expr::YieldExpr(_) => ExprPrecedence::Jump,
+
+        Expr::RangeExpr(..) => ExprPrecedence::Range,
+
+        Expr::BinExpr(bin_expr) => match bin_expr.op_kind() {
+            Some(it) => match it {
+                BinaryOp::LogicOp(logic_op) => match logic_op {
+                    ast::LogicOp::And => ExprPrecedence::LAnd,
+                    ast::LogicOp::Or => ExprPrecedence::LOr,
+                },
+                BinaryOp::ArithOp(arith_op) => match arith_op {
+                    ast::ArithOp::Add | ast::ArithOp::Sub => ExprPrecedence::Sum,
+                    ast::ArithOp::Div | ast::ArithOp::Rem | ast::ArithOp::Mul => {
+                        ExprPrecedence::Product
+                    }
+                    ast::ArithOp::Shl | ast::ArithOp::Shr => ExprPrecedence::Shift,
+                    ast::ArithOp::BitXor => ExprPrecedence::BitXor,
+                    ast::ArithOp::BitOr => ExprPrecedence::BitOr,
+                    ast::ArithOp::BitAnd => ExprPrecedence::BitAnd,
+                },
+                BinaryOp::CmpOp(_) => ExprPrecedence::Compare,
+                BinaryOp::Assignment { .. } => ExprPrecedence::Assign,
+            },
+            None => ExprPrecedence::Unambiguous,
+        },
+        Expr::CastExpr(_) => ExprPrecedence::Cast,
+
+        Expr::LetExpr(_) | Expr::PrefixExpr(_) | Expr::RefExpr(_) => ExprPrecedence::Prefix,
+
+        Expr::ArrayExpr(_)
+        | Expr::AsmExpr(_)
+        | Expr::AwaitExpr(_)
+        | Expr::BecomeExpr(_)
+        | Expr::BlockExpr(_)
+        | Expr::CallExpr(_)
+        | Expr::FieldExpr(_)
+        | Expr::ForExpr(_)
+        | Expr::FormatArgsExpr(_)
+        | Expr::IfExpr(_)
+        | Expr::IndexExpr(_)
+        | Expr::Literal(_)
+        | Expr::LoopExpr(_)
+        | Expr::MacroExpr(_)
+        | Expr::MatchExpr(_)
+        | Expr::MethodCallExpr(_)
+        | Expr::OffsetOfExpr(_)
+        | Expr::ParenExpr(_)
+        | Expr::PathExpr(_)
+        | Expr::RecordExpr(_)
+        | Expr::TryExpr(_)
+        | Expr::TupleExpr(_)
+        | Expr::UnderscoreExpr(_)
+        | Expr::WhileExpr(_) => ExprPrecedence::Unambiguous,
+    }
+}
+
 impl Expr {
+    pub fn precedence(&self) -> ExprPrecedence {
+        precedence(self)
+    }
+
     // Implementation is based on
     // - https://doc.rust-lang.org/reference/expressions.html#expression-precedence
     // - https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html
@@ -261,7 +376,7 @@ impl Expr {
     }
 
     /// Returns true if self is one of `return`, `break`, `continue` or `yield` with **no associated value**.
-    fn is_ret_like_with_no_value(&self) -> bool {
+    pub fn is_ret_like_with_no_value(&self) -> bool {
         use Expr::*;
 
         match self {
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs
index 280c5c25cb950..572622db544cd 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs
@@ -1,8 +1,9 @@
 //! Wrappers over [`make`] constructors
-use itertools::Itertools;
-
 use crate::{
-    ast::{self, make, HasGenericParams, HasName, HasTypeBounds, HasVisibility},
+    ast::{
+        self, make, HasArgList, HasGenericArgs, HasGenericParams, HasName, HasTypeBounds,
+        HasVisibility,
+    },
     syntax_editor::SyntaxMappingBuilder,
     AstNode, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken,
 };
@@ -14,6 +15,14 @@ impl SyntaxFactory {
         make::name(name).clone_for_update()
     }
 
+    pub fn name_ref(&self, name: &str) -> ast::NameRef {
+        make::name_ref(name).clone_for_update()
+    }
+
+    pub fn lifetime(&self, text: &str) -> ast::Lifetime {
+        make::lifetime(text).clone_for_update()
+    }
+
     pub fn ty(&self, text: &str) -> ast::Type {
         make::ty(text).clone_for_update()
     }
@@ -26,6 +35,20 @@ impl SyntaxFactory {
         ast
     }
 
+    pub fn ty_path(&self, path: ast::Path) -> ast::PathType {
+        let ast::Type::PathType(ast) = make::ty_path(path.clone()).clone_for_update() else {
+            unreachable!()
+        };
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(path.syntax().clone(), ast.path().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
     pub fn type_param(
         &self,
         name: ast::Name,
@@ -48,6 +71,71 @@ impl SyntaxFactory {
         ast
     }
 
+    pub fn path_segment(&self, name_ref: ast::NameRef) -> ast::PathSegment {
+        let ast = make::path_segment(name_ref.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(name_ref.syntax().clone(), ast.name_ref().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn path_segment_generics(
+        &self,
+        name_ref: ast::NameRef,
+        generic_arg_list: ast::GenericArgList,
+    ) -> ast::PathSegment {
+        let ast::Type::PathType(path) = make::ty(&format!("{name_ref}{generic_arg_list}")) else {
+            unreachable!();
+        };
+
+        let ast = path.path().unwrap().segment().unwrap().clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(name_ref.syntax().clone(), ast.name_ref().unwrap().syntax().clone());
+            builder.map_node(
+                generic_arg_list.syntax().clone(),
+                ast.generic_arg_list().unwrap().syntax().clone(),
+            );
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn path_unqualified(&self, segment: ast::PathSegment) -> ast::Path {
+        let ast = make::path_unqualified(segment.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(segment.syntax().clone(), ast.segment().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn path_from_segments(
+        &self,
+        segments: impl IntoIterator,
+        is_abs: bool,
+    ) -> ast::Path {
+        let (segments, input) = iterator_input(segments);
+        let ast = make::path_from_segments(segments, is_abs).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_children(input.into_iter(), ast.segments().map(|it| it.syntax().clone()));
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
     pub fn ident_pat(&self, ref_: bool, mut_: bool, name: ast::Name) -> ast::IdentPat {
         let ast = make::ident_pat(ref_, mut_, name.clone()).clone_for_update();
 
@@ -60,15 +148,40 @@ impl SyntaxFactory {
         ast
     }
 
+    pub fn wildcard_pat(&self) -> ast::WildcardPat {
+        make::wildcard_pat().clone_for_update()
+    }
+
+    pub fn literal_pat(&self, text: &str) -> ast::LiteralPat {
+        make::literal_pat(text).clone_for_update()
+    }
+
+    pub fn tuple_struct_pat(
+        &self,
+        path: ast::Path,
+        fields: impl IntoIterator,
+    ) -> ast::TupleStructPat {
+        let (fields, input) = iterator_input(fields);
+        let ast = make::tuple_struct_pat(path.clone(), fields).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(path.syntax().clone(), ast.path().unwrap().syntax().clone());
+            builder.map_children(input.into_iter(), ast.fields().map(|it| it.syntax().clone()));
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
     pub fn block_expr(
         &self,
-        stmts: impl IntoIterator,
+        statements: impl IntoIterator,
         tail_expr: Option,
     ) -> ast::BlockExpr {
-        let stmts = stmts.into_iter().collect_vec();
-        let mut input = stmts.iter().map(|it| it.syntax().clone()).collect_vec();
+        let (statements, mut input) = iterator_input(statements);
 
-        let ast = make::block_expr(stmts, tail_expr.clone()).clone_for_update();
+        let ast = make::block_expr(statements, tail_expr.clone()).clone_for_update();
 
         if let Some(mut mapping) = self.mappings() {
             let stmt_list = ast.stmt_list().unwrap();
@@ -98,7 +211,20 @@ impl SyntaxFactory {
     }
 
     pub fn expr_empty_block(&self) -> ast::BlockExpr {
-        ast::BlockExpr { syntax: make::expr_empty_block().syntax().clone_for_update() }
+        make::expr_empty_block().clone_for_update()
+    }
+
+    pub fn expr_tuple(&self, fields: impl IntoIterator) -> ast::TupleExpr {
+        let (fields, input) = iterator_input(fields);
+        let ast = make::expr_tuple(fields).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_children(input.into_iter(), ast.fields().map(|it| it.syntax().clone()));
+            builder.finish(&mut mapping);
+        }
+
+        ast
     }
 
     pub fn expr_bin(&self, lhs: ast::Expr, op: ast::BinaryOp, rhs: ast::Expr) -> ast::BinExpr {
@@ -118,6 +244,10 @@ impl SyntaxFactory {
         ast
     }
 
+    pub fn expr_literal(&self, text: &str) -> ast::Literal {
+        make::expr_literal(text).clone_for_update()
+    }
+
     pub fn expr_path(&self, path: ast::Path) -> ast::Expr {
         let ast::Expr::PathExpr(ast) = make::expr_path(path.clone()).clone_for_update() else {
             unreachable!()
@@ -132,6 +262,49 @@ impl SyntaxFactory {
         ast.into()
     }
 
+    pub fn expr_prefix(&self, op: SyntaxKind, expr: ast::Expr) -> ast::PrefixExpr {
+        let ast = make::expr_prefix(op, expr.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn expr_call(&self, expr: ast::Expr, arg_list: ast::ArgList) -> ast::CallExpr {
+        // FIXME: `make::expr_call`` should return a `CallExpr`, not just an `Expr`
+        let ast::Expr::CallExpr(ast) =
+            make::expr_call(expr.clone(), arg_list.clone()).clone_for_update()
+        else {
+            unreachable!()
+        };
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
+            builder.map_node(arg_list.syntax().clone(), ast.arg_list().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn arg_list(&self, args: impl IntoIterator) -> ast::ArgList {
+        let (args, input) = iterator_input(args);
+        let ast = make::arg_list(args).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax.clone());
+            builder.map_children(input.into_iter(), ast.args().map(|it| it.syntax().clone()));
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
     pub fn expr_ref(&self, expr: ast::Expr, exclusive: bool) -> ast::Expr {
         let ast::Expr::RefExpr(ast) = make::expr_ref(expr.clone(), exclusive).clone_for_update()
         else {
@@ -163,6 +336,125 @@ impl SyntaxFactory {
         ast
     }
 
+    pub fn expr_if(
+        &self,
+        condition: ast::Expr,
+        then_branch: ast::BlockExpr,
+        else_branch: Option,
+    ) -> ast::IfExpr {
+        let ast = make::expr_if(condition.clone(), then_branch.clone(), else_branch.clone())
+            .clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(condition.syntax().clone(), ast.condition().unwrap().syntax().clone());
+            builder.map_node(
+                then_branch.syntax().clone(),
+                ast.then_branch().unwrap().syntax().clone(),
+            );
+
+            if let Some(else_branch) = else_branch {
+                builder.map_node(
+                    else_branch.syntax().clone(),
+                    ast.else_branch().unwrap().syntax().clone(),
+                );
+            }
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn expr_let(&self, pattern: ast::Pat, expr: ast::Expr) -> ast::LetExpr {
+        let ast = make::expr_let(pattern.clone(), expr.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(pattern.syntax().clone(), ast.pat().unwrap().syntax().clone());
+            builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn expr_stmt(&self, expr: ast::Expr) -> ast::ExprStmt {
+        let ast = make::expr_stmt(expr.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn expr_match(&self, expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::MatchExpr {
+        let ast = make::expr_match(expr.clone(), match_arm_list.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
+            builder.map_node(
+                match_arm_list.syntax().clone(),
+                ast.match_arm_list().unwrap().syntax().clone(),
+            );
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn match_arm(
+        &self,
+        pat: ast::Pat,
+        guard: Option,
+        expr: ast::Expr,
+    ) -> ast::MatchArm {
+        let ast = make::match_arm(pat.clone(), guard.clone(), expr.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(pat.syntax().clone(), ast.pat().unwrap().syntax().clone());
+            if let Some(guard) = guard {
+                builder.map_node(guard.syntax().clone(), ast.guard().unwrap().syntax().clone());
+            }
+            builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn match_guard(&self, condition: ast::Expr) -> ast::MatchGuard {
+        let ast = make::match_guard(condition.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(condition.syntax().clone(), ast.condition().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn match_arm_list(
+        &self,
+        match_arms: impl IntoIterator,
+    ) -> ast::MatchArmList {
+        let (match_arms, input) = iterator_input(match_arms);
+        let ast = make::match_arm_list(match_arms).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_children(input.into_iter(), ast.arms().map(|it| it.syntax().clone()));
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
     pub fn let_stmt(
         &self,
         pattern: ast::Pat,
@@ -188,6 +480,30 @@ impl SyntaxFactory {
         ast
     }
 
+    pub fn type_arg(&self, ty: ast::Type) -> ast::TypeArg {
+        let ast = make::type_arg(ty.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(ty.syntax().clone(), ast.ty().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn lifetime_arg(&self, lifetime: ast::Lifetime) -> ast::LifetimeArg {
+        let ast = make::lifetime_arg(lifetime.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(lifetime.syntax().clone(), ast.lifetime().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
     pub fn item_const(
         &self,
         visibility: Option,
@@ -255,16 +571,22 @@ impl SyntaxFactory {
         ast
     }
 
-    pub fn turbofish_generic_arg_list(
+    pub fn generic_arg_list(
         &self,
-        args: impl IntoIterator + Clone,
+        generic_args: impl IntoIterator,
+        is_turbo: bool,
     ) -> ast::GenericArgList {
-        let ast = make::turbofish_generic_arg_list(args.clone()).clone_for_update();
+        let (generic_args, input) = iterator_input(generic_args);
+        let ast = if is_turbo {
+            make::turbofish_generic_arg_list(generic_args).clone_for_update()
+        } else {
+            make::generic_arg_list(generic_args).clone_for_update()
+        };
 
         if let Some(mut mapping) = self.mappings() {
             let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
             builder.map_children(
-                args.into_iter().map(|arg| arg.syntax().clone()),
+                input.into_iter(),
                 ast.generic_args().map(|arg| arg.syntax().clone()),
             );
             builder.finish(&mut mapping);
@@ -277,8 +599,7 @@ impl SyntaxFactory {
         &self,
         fields: impl IntoIterator,
     ) -> ast::RecordFieldList {
-        let fields: Vec = fields.into_iter().collect();
-        let input: Vec<_> = fields.iter().map(|it| it.syntax().clone()).collect();
+        let (fields, input) = iterator_input(fields);
         let ast = make::record_field_list(fields).clone_for_update();
 
         if let Some(mut mapping) = self.mappings() {
@@ -323,8 +644,7 @@ impl SyntaxFactory {
         &self,
         fields: impl IntoIterator,
     ) -> ast::TupleFieldList {
-        let fields: Vec = fields.into_iter().collect();
-        let input: Vec<_> = fields.iter().map(|it| it.syntax().clone()).collect();
+        let (fields, input) = iterator_input(fields);
         let ast = make::tuple_field_list(fields).clone_for_update();
 
         if let Some(mut mapping) = self.mappings() {
@@ -419,8 +739,7 @@ impl SyntaxFactory {
         &self,
         variants: impl IntoIterator,
     ) -> ast::VariantList {
-        let variants: Vec = variants.into_iter().collect();
-        let input: Vec<_> = variants.iter().map(|it| it.syntax().clone()).collect();
+        let (variants, input) = iterator_input(variants);
         let ast = make::variant_list(variants).clone_for_update();
 
         if let Some(mut mapping) = self.mappings() {
@@ -481,7 +800,7 @@ impl SyntaxFactory {
     pub fn token_tree(
         &self,
         delimiter: SyntaxKind,
-        tt: Vec>,
+        tt: impl IntoIterator>,
     ) -> ast::TokenTree {
         let tt: Vec<_> = tt.into_iter().collect();
         let input: Vec<_> = tt.iter().cloned().filter_map(only_nodes).collect();
@@ -508,7 +827,54 @@ impl SyntaxFactory {
         make::token(kind)
     }
 
-    pub fn whitespace(&self, text: &str) -> ast::SyntaxToken {
+    pub fn whitespace(&self, text: &str) -> SyntaxToken {
         make::tokens::whitespace(text)
     }
 }
+
+// `ext` constructors
+impl SyntaxFactory {
+    pub fn ident_path(&self, ident: &str) -> ast::Path {
+        self.path_unqualified(self.path_segment(self.name_ref(ident)))
+    }
+
+    pub fn expr_unit(&self) -> ast::Expr {
+        self.expr_tuple([]).into()
+    }
+
+    pub fn ty_option(&self, t: ast::Type) -> ast::PathType {
+        let generic_arg_list = self.generic_arg_list([self.type_arg(t).into()], false);
+        let path = self.path_unqualified(
+            self.path_segment_generics(self.name_ref("Option"), generic_arg_list),
+        );
+
+        self.ty_path(path)
+    }
+
+    pub fn ty_result(&self, t: ast::Type, e: ast::Type) -> ast::PathType {
+        let generic_arg_list =
+            self.generic_arg_list([self.type_arg(t).into(), self.type_arg(e).into()], false);
+        let path = self.path_unqualified(
+            self.path_segment_generics(self.name_ref("Result"), generic_arg_list),
+        );
+
+        self.ty_path(path)
+    }
+}
+
+// We need to collect `input` here instead of taking `impl IntoIterator + Clone`,
+// because if we took `impl IntoIterator + Clone`, that could be something like an
+// `Iterator::map` with a closure that also makes use of a `SyntaxFactory` constructor.
+//
+// In that case, the iterator would be evaluated inside of the call to `map_children`,
+// and the inner constructor would try to take a mutable borrow of the mappings `RefCell`,
+// which would panic since it's already being mutably borrowed in the outer constructor.
+fn iterator_input(input: impl IntoIterator) -> (Vec, Vec) {
+    input
+        .into_iter()
+        .map(|it| {
+            let syntax = it.syntax().clone();
+            (it, syntax)
+        })
+        .collect()
+}
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs
index df017ddde6432..7d5ca2704354d 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs
@@ -116,7 +116,7 @@ impl CommentKind {
 impl ast::Whitespace {
     pub fn spans_multiple_lines(&self) -> bool {
         let text = self.text();
-        text.find('\n').map_or(false, |idx| text[idx + 1..].contains('\n'))
+        text.find('\n').is_some_and(|idx| text[idx + 1..].contains('\n'))
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs
index 992a847663af2..b82181ae13add 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs
@@ -335,7 +335,7 @@ mod tests {
     #[test]
     fn basic_usage() {
         let root = make::match_arm(
-            [make::wildcard_pat().into()],
+            make::wildcard_pat().into(),
             None,
             make::expr_tuple([
                 make::expr_bin_op(
@@ -344,7 +344,8 @@ mod tests {
                     make::expr_literal("2").into(),
                 ),
                 make::expr_literal("true").into(),
-            ]),
+            ])
+            .into(),
         );
 
         let to_wrap = root.syntax().descendants().find_map(ast::TupleExpr::cast).unwrap();
@@ -549,7 +550,7 @@ mod tests {
             None,
             None,
             make::param_list(None, []),
-            make::block_expr([], Some(make::expr_unit())),
+            make::block_expr([], Some(make::ext::expr_unit())),
             Some(make::ret_type(make::ty_unit())),
             false,
             false,
diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs
index 8069fdd06f74d..450d601615ee9 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs
@@ -24,7 +24,7 @@ impl SyntaxEditor {
                     if last_param
                         .syntax()
                         .next_sibling_or_token()
-                        .map_or(false, |it| it.kind() == SyntaxKind::COMMA)
+                        .is_some_and(|it| it.kind() == SyntaxKind::COMMA)
                     {
                         self.insert(
                             Position::after(last_param.syntax()),
diff --git a/src/tools/rust-analyzer/crates/test-fixture/Cargo.toml b/src/tools/rust-analyzer/crates/test-fixture/Cargo.toml
index c860e7b1183d5..95f4cb9d67e25 100644
--- a/src/tools/rust-analyzer/crates/test-fixture/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/test-fixture/Cargo.toml
@@ -2,6 +2,8 @@
 name = "test-fixture"
 version = "0.0.0"
 rust-version.workspace = true
+description = "Test fixtures for rust-analyzer."
+
 edition.workspace = true
 license.workspace = true
 authors.workspace = true
diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
index 889a7d10adad8..ca596583590a6 100644
--- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
@@ -13,22 +13,26 @@ use hir_expand::{
     proc_macro::{
         ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, ProcMacrosBuilder,
     },
-    quote, FileRange,
+    quote,
+    tt::{Leaf, TokenTree, TopSubtree, TopSubtreeBuilder, TtElement, TtIter},
+    FileRange,
 };
 use intern::Symbol;
 use rustc_hash::FxHashMap;
 use span::{Edition, EditionedFileId, FileId, Span};
+use stdx::itertools::Itertools;
 use test_utils::{
     extract_range_or_offset, Fixture, FixtureWithProjectMeta, RangeOrOffset, CURSOR_MARKER,
     ESCAPED_CURSOR_MARKER,
 };
-use tt::{Leaf, Subtree, TokenTree};
 
 pub const WORKSPACE: base_db::SourceRootId = base_db::SourceRootId(0);
 
 pub trait WithFixture: Default + ExpandDatabase + SourceRootDatabase + 'static {
     #[track_caller]
-    fn with_single_file(ra_fixture: &str) -> (Self, EditionedFileId) {
+    fn with_single_file(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    ) -> (Self, EditionedFileId) {
         let fixture = ChangeFixture::parse(ra_fixture);
         let mut db = Self::default();
         fixture.change.apply(&mut db);
@@ -37,7 +41,9 @@ pub trait WithFixture: Default + ExpandDatabase + SourceRootDatabase + 'static {
     }
 
     #[track_caller]
-    fn with_many_files(ra_fixture: &str) -> (Self, Vec) {
+    fn with_many_files(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    ) -> (Self, Vec) {
         let fixture = ChangeFixture::parse(ra_fixture);
         let mut db = Self::default();
         fixture.change.apply(&mut db);
@@ -46,7 +52,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceRootDatabase + 'static {
     }
 
     #[track_caller]
-    fn with_files(ra_fixture: &str) -> Self {
+    fn with_files(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> Self {
         let fixture = ChangeFixture::parse(ra_fixture);
         let mut db = Self::default();
         fixture.change.apply(&mut db);
@@ -56,7 +62,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceRootDatabase + 'static {
 
     #[track_caller]
     fn with_files_extra_proc_macros(
-        ra_fixture: &str,
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
         proc_macros: Vec<(String, ProcMacro)>,
     ) -> Self {
         let fixture = ChangeFixture::parse_with_proc_macros(ra_fixture, proc_macros);
@@ -67,21 +73,23 @@ pub trait WithFixture: Default + ExpandDatabase + SourceRootDatabase + 'static {
     }
 
     #[track_caller]
-    fn with_position(ra_fixture: &str) -> (Self, FilePosition) {
+    fn with_position(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Self, FilePosition) {
         let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
         let offset = range_or_offset.expect_offset();
         (db, FilePosition { file_id, offset })
     }
 
     #[track_caller]
-    fn with_range(ra_fixture: &str) -> (Self, FileRange) {
+    fn with_range(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Self, FileRange) {
         let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
         let range = range_or_offset.expect_range();
         (db, FileRange { file_id, range })
     }
 
     #[track_caller]
-    fn with_range_or_offset(ra_fixture: &str) -> (Self, EditionedFileId, RangeOrOffset) {
+    fn with_range_or_offset(
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
+    ) -> (Self, EditionedFileId, RangeOrOffset) {
         let fixture = ChangeFixture::parse(ra_fixture);
         let mut db = Self::default();
         fixture.change.apply(&mut db);
@@ -114,12 +122,12 @@ pub struct ChangeFixture {
 const SOURCE_ROOT_PREFIX: &str = "/";
 
 impl ChangeFixture {
-    pub fn parse(ra_fixture: &str) -> ChangeFixture {
+    pub fn parse(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> ChangeFixture {
         Self::parse_with_proc_macros(ra_fixture, Vec::new())
     }
 
     pub fn parse_with_proc_macros(
-        ra_fixture: &str,
+        #[rust_analyzer::rust_fixture] ra_fixture: &str,
         mut proc_macro_defs: Vec<(String, ProcMacro)>,
     ) -> ChangeFixture {
         let FixtureWithProjectMeta {
@@ -250,15 +258,7 @@ impl ChangeFixture {
                 let to_id = crates[&to];
                 let sysroot = crate_graph[to_id].origin.is_lang();
                 crate_graph
-                    .add_dep(
-                        from_id,
-                        Dependency::with_prelude(
-                            CrateName::new(&to).unwrap(),
-                            to_id,
-                            prelude,
-                            sysroot,
-                        ),
-                    )
+                    .add_dep(from_id, Dependency::with_prelude(to.clone(), to_id, prelude, sysroot))
                     .unwrap();
             }
         }
@@ -374,8 +374,8 @@ impl ChangeFixture {
     }
 }
 
-fn default_test_proc_macros() -> [(String, ProcMacro); 6] {
-    [
+fn default_test_proc_macros() -> Box<[(String, ProcMacro)]> {
+    Box::new([
         (
             r#"
 #[proc_macro_attribute]
@@ -466,7 +466,52 @@ pub fn issue_18089(_attr: TokenStream, _item: TokenStream) -> TokenStream {
                 disabled: false,
             },
         ),
-    ]
+        (
+            r#"
+#[proc_macro_attribute]
+pub fn issue_18840(_attr: TokenStream, _item: TokenStream) -> TokenStream {
+    loop {}
+}
+"#
+            .into(),
+            ProcMacro {
+                name: Symbol::intern("issue_18840"),
+                kind: ProcMacroKind::Attr,
+                expander: sync::Arc::new(Issue18840ProcMacroExpander),
+                disabled: false,
+            },
+        ),
+        (
+            r#"
+#[proc_macro]
+pub fn issue_17479(input: TokenStream) -> TokenStream {
+    input
+}
+"#
+            .into(),
+            ProcMacro {
+                name: Symbol::intern("issue_17479"),
+                kind: ProcMacroKind::Bang,
+                expander: sync::Arc::new(Issue17479ProcMacroExpander),
+                disabled: false,
+            },
+        ),
+        (
+            r#"
+#[proc_macro_attribute]
+pub fn issue_18898(_attr: TokenStream, input: TokenStream) -> TokenStream {
+    input
+}
+"#
+            .into(),
+            ProcMacro {
+                name: Symbol::intern("issue_18898"),
+                kind: ProcMacroKind::Bang,
+                expander: sync::Arc::new(Issue18898ProcMacroExpander),
+                disabled: false,
+            },
+        ),
+    ])
 }
 
 fn filter_test_proc_macros(
@@ -580,14 +625,14 @@ struct IdentityProcMacroExpander;
 impl ProcMacroExpander for IdentityProcMacroExpander {
     fn expand(
         &self,
-        subtree: &Subtree,
-        _: Option<&Subtree>,
+        subtree: &TopSubtree,
+        _: Option<&TopSubtree>,
         _: &Env,
         _: Span,
         _: Span,
         _: Span,
         _: Option,
-    ) -> Result, ProcMacroExpansionError> {
+    ) -> Result {
         Ok(subtree.clone())
     }
 }
@@ -598,15 +643,17 @@ struct Issue18089ProcMacroExpander;
 impl ProcMacroExpander for Issue18089ProcMacroExpander {
     fn expand(
         &self,
-        subtree: &Subtree,
-        _: Option<&Subtree>,
+        subtree: &TopSubtree,
+        _: Option<&TopSubtree>,
         _: &Env,
         _: Span,
         call_site: Span,
         _: Span,
         _: Option,
-    ) -> Result, ProcMacroExpansionError> {
-        let macro_name = &subtree.token_trees[1];
+    ) -> Result {
+        let tt::TokenTree::Leaf(macro_name) = &subtree.0[2] else {
+            return Err(ProcMacroExpansionError::Panic("incorrect input".to_owned()));
+        };
         Ok(quote! { call_site =>
             #[macro_export]
             macro_rules! my_macro___ {
@@ -627,45 +674,79 @@ struct AttributeInputReplaceProcMacroExpander;
 impl ProcMacroExpander for AttributeInputReplaceProcMacroExpander {
     fn expand(
         &self,
-        _: &Subtree,
-        attrs: Option<&Subtree>,
+        _: &TopSubtree,
+        attrs: Option<&TopSubtree>,
         _: &Env,
         _: Span,
         _: Span,
         _: Span,
         _: Option,
-    ) -> Result, ProcMacroExpansionError> {
+    ) -> Result {
         attrs
             .cloned()
             .ok_or_else(|| ProcMacroExpansionError::Panic("Expected attribute input".into()))
     }
 }
 
+#[derive(Debug)]
+struct Issue18840ProcMacroExpander;
+impl ProcMacroExpander for Issue18840ProcMacroExpander {
+    fn expand(
+        &self,
+        fn_: &TopSubtree,
+        _: Option<&TopSubtree>,
+        _: &Env,
+        def_site: Span,
+        _: Span,
+        _: Span,
+        _: Option,
+    ) -> Result {
+        // Input:
+        // ```
+        // #[issue_18840]
+        // fn foo() { let loop {} }
+        // ```
+
+        // The span that was created by the fixup infra.
+        let fixed_up_span = fn_.token_trees().flat_tokens()[5].first_span();
+        let mut result =
+            quote! {fixed_up_span => ::core::compile_error! { "my cool compile_error!" } };
+        // Make it so we won't remove the top subtree when reversing fixups.
+        let top_subtree_delimiter_mut = result.top_subtree_delimiter_mut();
+        top_subtree_delimiter_mut.open = def_site;
+        top_subtree_delimiter_mut.close = def_site;
+        Ok(result)
+    }
+}
+
 #[derive(Debug)]
 struct MirrorProcMacroExpander;
 impl ProcMacroExpander for MirrorProcMacroExpander {
     fn expand(
         &self,
-        input: &Subtree,
-        _: Option<&Subtree>,
+        input: &TopSubtree,
+        _: Option<&TopSubtree>,
         _: &Env,
         _: Span,
         _: Span,
         _: Span,
         _: Option,
-    ) -> Result, ProcMacroExpansionError> {
-        fn traverse(input: &Subtree) -> Subtree {
-            let mut token_trees = vec![];
-            for tt in input.token_trees.iter().rev() {
-                let tt = match tt {
-                    tt::TokenTree::Leaf(leaf) => tt::TokenTree::Leaf(leaf.clone()),
-                    tt::TokenTree::Subtree(sub) => tt::TokenTree::Subtree(traverse(sub)),
-                };
-                token_trees.push(tt);
+    ) -> Result {
+        fn traverse(builder: &mut TopSubtreeBuilder, iter: TtIter<'_>) {
+            for tt in iter.collect_vec().into_iter().rev() {
+                match tt {
+                    TtElement::Leaf(leaf) => builder.push(leaf.clone()),
+                    TtElement::Subtree(subtree, subtree_iter) => {
+                        builder.open(subtree.delimiter.kind, subtree.delimiter.open);
+                        traverse(builder, subtree_iter);
+                        builder.close(subtree.delimiter.close);
+                    }
+                }
             }
-            Subtree { delimiter: input.delimiter, token_trees: token_trees.into_boxed_slice() }
         }
-        Ok(traverse(input))
+        let mut builder = TopSubtreeBuilder::new(input.top_subtree().delimiter);
+        traverse(&mut builder, input.iter());
+        Ok(builder.build())
     }
 }
 
@@ -677,31 +758,24 @@ struct ShortenProcMacroExpander;
 impl ProcMacroExpander for ShortenProcMacroExpander {
     fn expand(
         &self,
-        input: &Subtree,
-        _: Option<&Subtree>,
+        input: &TopSubtree,
+        _: Option<&TopSubtree>,
         _: &Env,
         _: Span,
         _: Span,
         _: Span,
         _: Option,
-    ) -> Result, ProcMacroExpansionError> {
-        return Ok(traverse(input));
-
-        fn traverse(input: &Subtree) -> Subtree {
-            let token_trees = input
-                .token_trees
-                .iter()
-                .map(|it| match it {
-                    TokenTree::Leaf(leaf) => tt::TokenTree::Leaf(modify_leaf(leaf)),
-                    TokenTree::Subtree(subtree) => tt::TokenTree::Subtree(traverse(subtree)),
-                })
-                .collect();
-            Subtree { delimiter: input.delimiter, token_trees }
+    ) -> Result {
+        let mut result = input.0.clone();
+        for it in &mut result {
+            if let TokenTree::Leaf(leaf) = it {
+                modify_leaf(leaf)
+            }
         }
+        return Ok(tt::TopSubtree(result));
 
-        fn modify_leaf(leaf: &Leaf) -> Leaf {
-            let mut leaf = leaf.clone();
-            match &mut leaf {
+        fn modify_leaf(leaf: &mut Leaf) {
+            match leaf {
                 Leaf::Literal(it) => {
                     // XXX Currently replaces any literals with an empty string, but supporting
                     // "shortening" other literals would be nice.
@@ -712,7 +786,82 @@ impl ProcMacroExpander for ShortenProcMacroExpander {
                     it.sym = Symbol::intern(&it.sym.as_str().chars().take(1).collect::());
                 }
             }
-            leaf
         }
     }
 }
+
+// Reads ident type within string quotes, for issue #17479.
+#[derive(Debug)]
+struct Issue17479ProcMacroExpander;
+impl ProcMacroExpander for Issue17479ProcMacroExpander {
+    fn expand(
+        &self,
+        subtree: &TopSubtree,
+        _: Option<&TopSubtree>,
+        _: &Env,
+        _: Span,
+        _: Span,
+        _: Span,
+        _: Option,
+    ) -> Result {
+        let TokenTree::Leaf(Leaf::Literal(lit)) = &subtree.0[1] else {
+            return Err(ProcMacroExpansionError::Panic("incorrect Input".into()));
+        };
+        let symbol = &lit.symbol;
+        let span = lit.span;
+        Ok(quote! { span =>
+            #symbol()
+        })
+    }
+}
+
+// Reads ident type within string quotes, for issue #17479.
+#[derive(Debug)]
+struct Issue18898ProcMacroExpander;
+impl ProcMacroExpander for Issue18898ProcMacroExpander {
+    fn expand(
+        &self,
+        subtree: &TopSubtree,
+        _: Option<&TopSubtree>,
+        _: &Env,
+        def_site: Span,
+        _: Span,
+        _: Span,
+        _: Option,
+    ) -> Result {
+        let span = subtree
+            .token_trees()
+            .flat_tokens()
+            .last()
+            .ok_or_else(|| ProcMacroExpansionError::Panic("malformed input".to_owned()))?
+            .first_span();
+        let overly_long_subtree = quote! {span =>
+            {
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+                let a = 5;
+            }
+        };
+        Ok(quote! { def_site =>
+            fn foo() {
+                #overly_long_subtree
+            }
+        })
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
index 54c9db7aaccb4..7fe26d53bf219 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
@@ -168,7 +168,7 @@ impl FixtureWithProjectMeta {
     /// That will set toolchain to nightly and include predefined proc macros and a subset of
     /// `libcore` into the fixture, see `minicore.rs` for what's available. Note that toolchain
     /// defaults to stable.
-    pub fn parse(ra_fixture: &str) -> Self {
+    pub fn parse(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> Self {
         let fixture = trim_indent(ra_fixture);
         let mut fixture = fixture.as_str();
         let mut toolchain = None;
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
index 99dfabe174eeb..4ed68d18e8071 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -17,6 +17,7 @@
 //!     builtin_impls:
 //!     cell: copy, drop
 //!     clone: sized
+//!     coerce_pointee: derive, sized, unsize, coerce_unsized, dispatch_from_dyn
 //!     coerce_unsized: unsize
 //!     concat:
 //!     copy: clone
@@ -31,7 +32,7 @@
 //!     error: fmt
 //!     fmt: option, result, transmute, coerce_unsized, copy, clone, derive
 //!     fn: tuple
-//!     from: sized
+//!     from: sized, result
 //!     future: pin
 //!     coroutine: pin
 //!     dispatch_from_dyn: unsize, pin
@@ -52,6 +53,7 @@
 //!     pin:
 //!     pointee: copy, send, sync, ord, hash, unpin
 //!     range:
+//!     receiver: deref
 //!     result:
 //!     send: sized
 //!     size_of: sized
@@ -157,6 +159,14 @@ pub mod marker {
         type Discriminant;
     }
     // endregion:discriminant
+
+    // region:coerce_pointee
+    #[rustc_builtin_macro(CoercePointee, attributes(pointee))]
+    #[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize)]
+    pub macro CoercePointee($item:item) {
+        /* compiler built-in */
+    }
+    // endregion:coerce_pointee
 }
 
 // region:default
@@ -323,6 +333,25 @@ pub mod convert {
             t
         }
     }
+
+    pub trait TryFrom: Sized {
+        type Error;
+        fn try_from(value: T) -> Result;
+    }
+    pub trait TryInto: Sized {
+        type Error;
+        fn try_into(self) -> Result;
+    }
+
+    impl TryInto for T
+    where
+        U: TryFrom,
+    {
+        type Error = U::Error;
+        fn try_into(self) -> Result {
+            U::try_from(self)
+        }
+    }
     // endregion:from
 
     // region:as_ref
@@ -485,10 +514,26 @@ pub mod ops {
             fn deref_mut(&mut self) -> &mut Self::Target;
         }
         // endregion:deref_mut
+
+        // region:receiver
+        #[lang = "receiver"]
+        pub trait Receiver {
+            #[lang = "receiver_target"]
+            type Target: ?Sized;
+        }
+
+        impl Receiver for P
+        where
+            P: Deref,
+        {
+            type Target = T;
+        }
+        // endregion:receiver
     }
     pub use self::deref::{
         Deref,
         DerefMut, // :deref_mut
+        Receiver, // :receiver
     };
     // endregion:deref
 
@@ -1501,7 +1546,7 @@ pub mod iter {
             impl IntoIterator for [T; N] {
                 type Item = T;
                 type IntoIter = IntoIter;
-                fn into_iter(self) -> I {
+                fn into_iter(self) -> Self::IntoIter {
                     IntoIter { data: self, range: IndexRange { start: 0, end: loop {} } }
                 }
             }
@@ -1511,6 +1556,29 @@ pub mod iter {
                     loop {}
                 }
             }
+            pub struct Iter<'a, T> {
+                slice: &'a [T],
+            }
+            impl<'a, T> IntoIterator for &'a [T; N] {
+                type Item = &'a T;
+                type IntoIter = Iter<'a, T>;
+                fn into_iter(self) -> Self::IntoIter {
+                    loop {}
+                }
+            }
+            impl<'a, T> IntoIterator for &'a [T] {
+                type Item = &'a T;
+                type IntoIter = Iter<'a, T>;
+                fn into_iter(self) -> Self::IntoIter {
+                    loop {}
+                }
+            }
+            impl<'a, T> Iterator for Iter<'a, T> {
+                type Item = &'a T;
+                fn next(&mut self) -> Option {
+                    loop {}
+                }
+            }
         }
         pub use self::collect::IntoIterator;
     }
@@ -1523,6 +1591,15 @@ pub mod str {
     pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str {
         ""
     }
+    pub trait FromStr: Sized {
+        type Err;
+        fn from_str(s: &str) -> Result;
+    }
+    impl str {
+        pub fn parse(&self) -> Result {
+            FromStr::from_str(self)
+        }
+    }
 }
 // endregion:str
 
@@ -1782,7 +1859,7 @@ pub mod prelude {
             cmp::{Eq, PartialEq},                    // :eq
             cmp::{Ord, PartialOrd},                  // :ord
             convert::AsRef,                          // :as_ref
-            convert::{From, Into},                   // :from
+            convert::{From, Into, TryFrom, TryInto}, // :from
             default::Default,                        // :default
             iter::{IntoIterator, Iterator},          // :iterator
             macros::builtin::{derive, derive_const}, // :derive
@@ -1797,6 +1874,7 @@ pub mod prelude {
             option::Option::{self, None, Some},      // :option
             panic,                                   // :panic
             result::Result::{self, Err, Ok},         // :result
+            str::FromStr,                            // :str
         };
     }
 
diff --git a/src/tools/rust-analyzer/crates/toolchain/src/lib.rs b/src/tools/rust-analyzer/crates/toolchain/src/lib.rs
index a0603e35a09fa..325b94cc33bae 100644
--- a/src/tools/rust-analyzer/crates/toolchain/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/toolchain/src/lib.rs
@@ -1,6 +1,12 @@
 //! Discovery of `cargo` & `rustc` executables.
 
-use std::{env, iter, path::PathBuf};
+use std::{
+    env,
+    ffi::OsStr,
+    iter,
+    path::{Path, PathBuf},
+    process::Command,
+};
 
 use camino::{Utf8Path, Utf8PathBuf};
 
@@ -65,6 +71,14 @@ impl Tool {
     }
 }
 
+pub fn command(cmd: impl AsRef, working_directory: impl AsRef) -> Command {
+    // we are `toolchain::command``
+    #[allow(clippy::disallowed_methods)]
+    let mut cmd = Command::new(cmd);
+    cmd.current_dir(working_directory);
+    cmd
+}
+
 fn invoke(list: &[fn(&str) -> Option], executable: &str) -> Utf8PathBuf {
     list.iter().find_map(|it| it(executable)).unwrap_or_else(|| executable.into())
 }
@@ -102,7 +116,6 @@ fn lookup_in_path(exec: &str) -> Option {
     let paths = env::var_os("PATH").unwrap_or_default();
     env::split_paths(&paths)
         .map(|path| path.join(exec))
-        .map(PathBuf::from)
         .map(Utf8PathBuf::try_from)
         .filter_map(Result::ok)
         .find_map(probe_for_binary)
diff --git a/src/tools/rust-analyzer/crates/tt/src/buffer.rs b/src/tools/rust-analyzer/crates/tt/src/buffer.rs
index acb7e2d6c51ab..02a722895a4b8 100644
--- a/src/tools/rust-analyzer/crates/tt/src/buffer.rs
+++ b/src/tools/rust-analyzer/crates/tt/src/buffer.rs
@@ -1,259 +1,108 @@
 //! Stateful iteration over token trees.
 //!
 //! We use this as the source of tokens for parser.
-use crate::{Leaf, Subtree, TokenTree};
+use crate::{Leaf, Subtree, TokenTree, TokenTreesView};
 
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-struct EntryId(usize);
-
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-struct EntryPtr(
-    /// The index of the buffer containing the entry.
-    EntryId,
-    /// The index of the entry within the buffer.
-    usize,
-);
-
-/// Internal type which is used instead of `TokenTree` to represent a token tree
-/// within a `TokenBuffer`.
-#[derive(Debug)]
-enum Entry<'t, Span> {
-    // Mimicking types from proc-macro.
-    Subtree(Option<&'t TokenTree>, &'t Subtree, EntryId),
-    Leaf(&'t TokenTree),
-    /// End entries contain a pointer to the entry from the containing
-    /// token tree, or [`None`] if this is the outermost level.
-    End(Option),
-}
-
-/// A token tree buffer
-/// The safe version of `syn` [`TokenBuffer`](https://github.com/dtolnay/syn/blob/6533607f91686545cb034d2838beea338d9d0742/src/buffer.rs#L41)
-#[derive(Debug)]
-pub struct TokenBuffer<'t, Span> {
-    buffers: Vec]>>,
-}
-
-trait TokenList<'a, Span> {
-    fn entries(
-        &self,
-    ) -> (Vec<(usize, (&'a Subtree, Option<&'a TokenTree>))>, Vec>);
-}
-
-impl<'a, Span> TokenList<'a, Span> for &'a [TokenTree] {
-    fn entries(
-        &self,
-    ) -> (Vec<(usize, (&'a Subtree, Option<&'a TokenTree>))>, Vec>)
-    {
-        // Must contain everything in tokens and then the Entry::End
-        let start_capacity = self.len() + 1;
-        let mut entries = Vec::with_capacity(start_capacity);
-        let mut children = vec![];
-        for (idx, tt) in self.iter().enumerate() {
-            match tt {
-                TokenTree::Leaf(_) => {
-                    entries.push(Entry::Leaf(tt));
-                }
-                TokenTree::Subtree(subtree) => {
-                    entries.push(Entry::End(None));
-                    children.push((idx, (subtree, Some(tt))));
-                }
-            }
-        }
-        (children, entries)
-    }
-}
-
-impl<'a, Span> TokenList<'a, Span> for &'a Subtree {
-    fn entries(
-        &self,
-    ) -> (Vec<(usize, (&'a Subtree, Option<&'a TokenTree>))>, Vec>)
-    {
-        // Must contain everything in tokens and then the Entry::End
-        let mut entries = vec![];
-        let mut children = vec![];
-        entries.push(Entry::End(None));
-        children.push((0usize, (*self, None)));
-        (children, entries)
-    }
-}
-
-impl<'t, Span> TokenBuffer<'t, Span> {
-    pub fn from_tokens(tokens: &'t [TokenTree]) -> TokenBuffer<'t, Span> {
-        Self::new(tokens)
-    }
-
-    pub fn from_subtree(subtree: &'t Subtree) -> TokenBuffer<'t, Span> {
-        Self::new(subtree)
-    }
-
-    fn new>(tokens: T) -> TokenBuffer<'t, Span> {
-        let mut buffers = vec![];
-        let idx = TokenBuffer::new_inner(tokens, &mut buffers, None);
-        assert_eq!(idx, 0);
-        TokenBuffer { buffers }
-    }
-
-    fn new_inner>(
-        tokens: T,
-        buffers: &mut Vec]>>,
-        next: Option,
-    ) -> usize {
-        let (children, mut entries) = tokens.entries();
-
-        entries.push(Entry::End(next));
-        let res = buffers.len();
-        buffers.push(entries.into_boxed_slice());
-
-        for (child_idx, (subtree, tt)) in children {
-            let idx = TokenBuffer::new_inner(
-                &*subtree.token_trees,
-                buffers,
-                Some(EntryPtr(EntryId(res), child_idx + 1)),
-            );
-            buffers[res].as_mut()[child_idx] = Entry::Subtree(tt, subtree, EntryId(idx));
-        }
-
-        res
-    }
-
-    /// Creates a cursor referencing the first token in the buffer and able to
-    /// traverse until the end of the buffer.
-    pub fn begin(&self) -> Cursor<'_, Span> {
-        Cursor::create(self, EntryPtr(EntryId(0), 0))
-    }
-
-    fn entry(&self, ptr: &EntryPtr) -> Option<&Entry<'_, Span>> {
-        let id = ptr.0;
-        self.buffers[id.0].get(ptr.1)
-    }
-}
-
-#[derive(Debug)]
-pub enum TokenTreeRef<'a, Span> {
-    Subtree(&'a Subtree, Option<&'a TokenTree>),
-    Leaf(&'a Leaf, &'a TokenTree),
-}
-
-impl TokenTreeRef<'_, Span> {
-    pub fn span(&self) -> Span {
-        match self {
-            TokenTreeRef::Subtree(subtree, _) => subtree.delimiter.open,
-            TokenTreeRef::Leaf(leaf, _) => *leaf.span(),
-        }
-    }
-}
-
-impl TokenTreeRef<'_, Span> {
-    pub fn cloned(&self) -> TokenTree {
-        match self {
-            TokenTreeRef::Subtree(subtree, tt) => match tt {
-                Some(it) => (*it).clone(),
-                None => (*subtree).clone().into(),
-            },
-            TokenTreeRef::Leaf(_, tt) => (*tt).clone(),
-        }
-    }
-}
-
-/// A safe version of `Cursor` from `syn` crate 
-#[derive(Copy, Clone, Debug)]
 pub struct Cursor<'a, Span> {
-    buffer: &'a TokenBuffer<'a, Span>,
-    ptr: EntryPtr,
+    buffer: &'a [TokenTree],
+    index: usize,
+    subtrees_stack: Vec,
 }
 
-impl PartialEq for Cursor<'_, Span> {
-    fn eq(&self, other: &Cursor<'_, Span>) -> bool {
-        self.ptr == other.ptr && std::ptr::eq(self.buffer, other.buffer)
+impl<'a, Span: Copy> Cursor<'a, Span> {
+    pub fn new(buffer: &'a [TokenTree]) -> Self {
+        Self { buffer, index: 0, subtrees_stack: Vec::new() }
     }
-}
 
-impl Eq for Cursor<'_, Span> {}
-
-impl<'a, Span> Cursor<'a, Span> {
     /// Check whether it is eof
-    pub fn eof(self) -> bool {
-        matches!(self.buffer.entry(&self.ptr), None | Some(Entry::End(None)))
+    pub fn eof(&self) -> bool {
+        self.index == self.buffer.len() && self.subtrees_stack.is_empty()
     }
 
-    /// If the cursor is pointing at the end of a subtree, returns
-    /// the parent subtree
-    pub fn end(self) -> Option<&'a Subtree> {
-        match self.entry() {
-            Some(Entry::End(Some(ptr))) => {
-                let idx = ptr.1;
-                if let Some(Entry::Subtree(_, subtree, _)) =
-                    self.buffer.entry(&EntryPtr(ptr.0, idx - 1))
-                {
-                    return Some(subtree);
-                }
-                None
+    pub fn is_root(&self) -> bool {
+        self.subtrees_stack.is_empty()
+    }
+
+    fn last_subtree(&self) -> Option<(usize, &'a Subtree)> {
+        self.subtrees_stack.last().map(|&subtree_idx| {
+            let TokenTree::Subtree(subtree) = &self.buffer[subtree_idx] else {
+                panic!("subtree pointing to non-subtree");
+            };
+            (subtree_idx, subtree)
+        })
+    }
+
+    pub fn end(&mut self) -> &'a Subtree {
+        let (last_subtree_idx, last_subtree) =
+            self.last_subtree().expect("called `Cursor::end()` without an open subtree");
+        // +1 because `Subtree.len` excludes the subtree itself.
+        assert_eq!(
+            last_subtree_idx + last_subtree.usize_len() + 1,
+            self.index,
+            "called `Cursor::end()` without finishing a subtree"
+        );
+        self.subtrees_stack.pop();
+        last_subtree
+    }
+
+    /// Returns the `TokenTree` at the cursor if it is not at the end of a subtree.
+    pub fn token_tree(&self) -> Option<&'a TokenTree> {
+        if let Some((last_subtree_idx, last_subtree)) = self.last_subtree() {
+            // +1 because `Subtree.len` excludes the subtree itself.
+            if last_subtree_idx + last_subtree.usize_len() + 1 == self.index {
+                return None;
             }
-            _ => None,
         }
+        self.buffer.get(self.index)
     }
 
-    fn entry(&self) -> Option<&'a Entry<'a, Span>> {
-        self.buffer.entry(&self.ptr)
-    }
-
-    /// If the cursor is pointing at a `Subtree`, returns
-    /// a cursor into that subtree
-    pub fn subtree(self) -> Option> {
-        match self.entry() {
-            Some(Entry::Subtree(_, _, entry_id)) => {
-                Some(Cursor::create(self.buffer, EntryPtr(*entry_id, 0)))
-            }
-            _ => None,
+    /// Bump the cursor, and enters a subtree if it is on one.
+    pub fn bump(&mut self) {
+        if let Some((last_subtree_idx, last_subtree)) = self.last_subtree() {
+            // +1 because `Subtree.len` excludes the subtree itself.
+            assert_ne!(
+                last_subtree_idx + last_subtree.usize_len() + 1,
+                self.index,
+                "called `Cursor::bump()` when at the end of a subtree"
+            );
         }
-    }
-
-    /// If the cursor is pointing at a `TokenTree`, returns it
-    pub fn token_tree(self) -> Option> {
-        match self.entry() {
-            Some(Entry::Leaf(tt)) => match tt {
-                TokenTree::Leaf(leaf) => Some(TokenTreeRef::Leaf(leaf, tt)),
-                TokenTree::Subtree(subtree) => Some(TokenTreeRef::Subtree(subtree, Some(tt))),
-            },
-            Some(Entry::Subtree(tt, subtree, _)) => Some(TokenTreeRef::Subtree(subtree, *tt)),
-            Some(Entry::End(_)) | None => None,
+        if let TokenTree::Subtree(_) = self.buffer[self.index] {
+            self.subtrees_stack.push(self.index);
         }
+        self.index += 1;
     }
 
-    fn create(buffer: &'a TokenBuffer<'_, Span>, ptr: EntryPtr) -> Cursor<'a, Span> {
-        Cursor { buffer, ptr }
-    }
-
-    /// Bump the cursor
-    pub fn bump(self) -> Cursor<'a, Span> {
-        if let Some(Entry::End(exit)) = self.buffer.entry(&self.ptr) {
-            match exit {
-                Some(exit) => Cursor::create(self.buffer, *exit),
-                None => self,
+    pub fn bump_or_end(&mut self) {
+        if let Some((last_subtree_idx, last_subtree)) = self.last_subtree() {
+            // +1 because `Subtree.len` excludes the subtree itself.
+            if last_subtree_idx + last_subtree.usize_len() + 1 == self.index {
+                self.subtrees_stack.pop();
+                return;
             }
-        } else {
-            Cursor::create(self.buffer, EntryPtr(self.ptr.0, self.ptr.1 + 1))
         }
+        // +1 because `Subtree.len` excludes the subtree itself.
+        if let TokenTree::Subtree(_) = self.buffer[self.index] {
+            self.subtrees_stack.push(self.index);
+        }
+        self.index += 1;
     }
 
-    /// Bump the cursor, if it is a subtree, returns
-    /// a cursor into that subtree
-    pub fn bump_subtree(self) -> Cursor<'a, Span> {
-        match self.entry() {
-            Some(&Entry::Subtree(_, _, entry_id)) => {
-                Cursor::create(self.buffer, EntryPtr(entry_id, 0))
+    pub fn peek_two_leaves(&self) -> Option<[&'a Leaf; 2]> {
+        if let Some((last_subtree_idx, last_subtree)) = self.last_subtree() {
+            // +1 because `Subtree.len` excludes the subtree itself.
+            let last_end = last_subtree_idx + last_subtree.usize_len() + 1;
+            if last_end == self.index || last_end == self.index + 1 {
+                return None;
             }
-            Some(Entry::End(exit)) => match exit {
-                Some(exit) => Cursor::create(self.buffer, *exit),
-                None => self,
-            },
-            _ => Cursor::create(self.buffer, EntryPtr(self.ptr.0, self.ptr.1 + 1)),
         }
+        self.buffer.get(self.index..self.index + 2).and_then(|it| match it {
+            [TokenTree::Leaf(a), TokenTree::Leaf(b)] => Some([a, b]),
+            _ => None,
+        })
     }
 
-    /// Check whether it is a top level
-    pub fn is_root(&self) -> bool {
-        let entry_id = self.ptr.0;
-        entry_id.0 == 0
+    pub fn crossed(&self) -> TokenTreesView<'a, Span> {
+        assert!(self.is_root());
+        TokenTreesView::new(&self.buffer[..self.index])
     }
 }
diff --git a/src/tools/rust-analyzer/crates/tt/src/iter.rs b/src/tools/rust-analyzer/crates/tt/src/iter.rs
index 587b903aa97a5..1d88218810de6 100644
--- a/src/tools/rust-analyzer/crates/tt/src/iter.rs
+++ b/src/tools/rust-analyzer/crates/tt/src/iter.rs
@@ -1,51 +1,64 @@
 //! A "Parser" structure for token trees. We use this when parsing a declarative
 //! macro definition into a list of patterns and templates.
 
+use std::fmt;
+
 use arrayvec::ArrayVec;
 use intern::sym;
 
-use crate::{Ident, Leaf, Punct, Spacing, Subtree, TokenTree};
+use crate::{Ident, Leaf, Punct, Spacing, Subtree, TokenTree, TokenTreesView};
 
-#[derive(Debug, Clone)]
+#[derive(Clone)]
 pub struct TtIter<'a, S> {
     inner: std::slice::Iter<'a, TokenTree>,
 }
 
-impl<'a, S: Copy> TtIter<'a, S> {
-    pub fn new(subtree: &'a Subtree) -> TtIter<'a, S> {
-        TtIter { inner: subtree.token_trees.iter() }
+impl fmt::Debug for TtIter<'_, S> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("TtIter").field("remaining", &self.remaining()).finish()
     }
+}
 
-    pub fn new_iter(iter: std::slice::Iter<'a, TokenTree>) -> TtIter<'a, S> {
-        TtIter { inner: iter }
+#[derive(Clone, Copy)]
+pub struct TtIterSavepoint<'a, S>(&'a [TokenTree]);
+
+impl<'a, S: Copy> TtIterSavepoint<'a, S> {
+    pub fn remaining(self) -> TokenTreesView<'a, S> {
+        TokenTreesView::new(self.0)
+    }
+}
+
+impl<'a, S: Copy> TtIter<'a, S> {
+    pub(crate) fn new(tt: &'a [TokenTree]) -> TtIter<'a, S> {
+        TtIter { inner: tt.iter() }
     }
 
     pub fn expect_char(&mut self, char: char) -> Result<(), ()> {
         match self.next() {
-            Some(&TokenTree::Leaf(Leaf::Punct(Punct { char: c, .. }))) if c == char => Ok(()),
+            Some(TtElement::Leaf(&Leaf::Punct(Punct { char: c, .. }))) if c == char => Ok(()),
             _ => Err(()),
         }
     }
 
     pub fn expect_any_char(&mut self, chars: &[char]) -> Result<(), ()> {
         match self.next() {
-            Some(TokenTree::Leaf(Leaf::Punct(Punct { char: c, .. }))) if chars.contains(c) => {
+            Some(TtElement::Leaf(Leaf::Punct(Punct { char: c, .. }))) if chars.contains(c) => {
                 Ok(())
             }
             _ => Err(()),
         }
     }
 
-    pub fn expect_subtree(&mut self) -> Result<&'a Subtree, ()> {
+    pub fn expect_subtree(&mut self) -> Result<(&'a Subtree, TtIter<'a, S>), ()> {
         match self.next() {
-            Some(TokenTree::Subtree(it)) => Ok(it),
+            Some(TtElement::Subtree(subtree, iter)) => Ok((subtree, iter)),
             _ => Err(()),
         }
     }
 
     pub fn expect_leaf(&mut self) -> Result<&'a Leaf, ()> {
         match self.next() {
-            Some(TokenTree::Leaf(it)) => Ok(it),
+            Some(TtElement::Leaf(it)) => Ok(it),
             _ => Err(()),
         }
     }
@@ -99,7 +112,7 @@ impl<'a, S: Copy> TtIter<'a, S> {
     /// This method currently may return a single quotation, which is part of lifetime ident and
     /// conceptually not a punct in the context of mbe. Callers should handle this.
     pub fn expect_glued_punct(&mut self) -> Result, 3>, ()> {
-        let TokenTree::Leaf(Leaf::Punct(first)) = self.next().ok_or(())?.clone() else {
+        let TtElement::Leaf(&Leaf::Punct(first)) = self.next().ok_or(())? else {
             return Err(());
         };
 
@@ -132,6 +145,7 @@ impl<'a, S: Copy> TtIter<'a, S> {
             }
             ('-' | '!' | '*' | '/' | '&' | '%' | '^' | '+' | '<' | '=' | '>' | '|', '=', _)
             | ('-' | '=' | '>', '>', _)
+            | (_, _, Some(';'))
             | ('<', '-', _)
             | (':', ':', _)
             | ('.', '.', _)
@@ -146,28 +160,84 @@ impl<'a, S: Copy> TtIter<'a, S> {
         }
         Ok(res)
     }
-    pub fn peek_n(&self, n: usize) -> Option<&'a TokenTree> {
+
+    /// This method won't check for subtrees, so the nth token tree may not be the nth sibling of the current tree.
+    fn peek_n(&self, n: usize) -> Option<&'a TokenTree> {
         self.inner.as_slice().get(n)
     }
 
+    pub fn peek(&self) -> Option> {
+        match self.inner.as_slice().first()? {
+            TokenTree::Leaf(leaf) => Some(TtElement::Leaf(leaf)),
+            TokenTree::Subtree(subtree) => {
+                let nested_iter =
+                    TtIter { inner: self.inner.as_slice()[1..][..subtree.usize_len()].iter() };
+                Some(TtElement::Subtree(subtree, nested_iter))
+            }
+        }
+    }
+
+    /// Equivalent to `peek().is_none()`, but a bit faster.
+    pub fn is_empty(&self) -> bool {
+        self.inner.len() == 0
+    }
+
     pub fn next_span(&self) -> Option {
         Some(self.inner.as_slice().first()?.first_span())
     }
 
-    pub fn as_slice(&self) -> &'a [TokenTree] {
-        self.inner.as_slice()
+    pub fn remaining(&self) -> TokenTreesView<'a, S> {
+        TokenTreesView::new(self.inner.as_slice())
     }
-}
 
-impl<'a, S> Iterator for TtIter<'a, S> {
-    type Item = &'a TokenTree;
-    fn next(&mut self) -> Option {
-        self.inner.next()
+    /// **Warning**: This advances `skip` **flat** token trees, subtrees account for children+1!
+    pub fn flat_advance(&mut self, skip: usize) {
+        self.inner = self.inner.as_slice()[skip..].iter();
+    }
+
+    pub fn savepoint(&self) -> TtIterSavepoint<'a, S> {
+        TtIterSavepoint(self.inner.as_slice())
+    }
+
+    pub fn from_savepoint(&self, savepoint: TtIterSavepoint<'a, S>) -> TokenTreesView<'a, S> {
+        let len = (self.inner.as_slice().as_ptr() as usize - savepoint.0.as_ptr() as usize)
+            / size_of::>();
+        TokenTreesView::new(&savepoint.0[..len])
     }
 
-    fn size_hint(&self) -> (usize, Option) {
-        self.inner.size_hint()
+    pub fn next_as_view(&mut self) -> Option> {
+        let savepoint = self.savepoint();
+        self.next()?;
+        Some(self.from_savepoint(savepoint))
     }
 }
 
-impl std::iter::ExactSizeIterator for TtIter<'_, S> {}
+pub enum TtElement<'a, S> {
+    Leaf(&'a Leaf),
+    Subtree(&'a Subtree, TtIter<'a, S>),
+}
+
+impl TtElement<'_, S> {
+    #[inline]
+    pub fn first_span(&self) -> S {
+        match self {
+            TtElement::Leaf(it) => *it.span(),
+            TtElement::Subtree(it, _) => it.delimiter.open,
+        }
+    }
+}
+
+impl<'a, S> Iterator for TtIter<'a, S> {
+    type Item = TtElement<'a, S>;
+    fn next(&mut self) -> Option {
+        match self.inner.next()? {
+            TokenTree::Leaf(leaf) => Some(TtElement::Leaf(leaf)),
+            TokenTree::Subtree(subtree) => {
+                let nested_iter =
+                    TtIter { inner: self.inner.as_slice()[..subtree.usize_len()].iter() };
+                self.inner = self.inner.as_slice()[subtree.usize_len()..].iter();
+                Some(TtElement::Subtree(subtree, nested_iter))
+            }
+        }
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/tt/src/lib.rs b/src/tools/rust-analyzer/crates/tt/src/lib.rs
index 8d915d0a51e32..7705ba876e1ae 100644
--- a/src/tools/rust-analyzer/crates/tt/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/tt/src/lib.rs
@@ -1,6 +1,7 @@
 //! `tt` crate defines a `TokenTree` data structure: this is the interface (both
-//! input and output) of macros. It closely mirrors `proc_macro` crate's
-//! `TokenTree`.
+//! input and output) of macros.
+//!
+//! The `TokenTree` is semantically a tree, but for performance reasons it is stored as a flat structure.
 
 #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
 
@@ -14,7 +15,9 @@ pub mod iter;
 
 use std::fmt;
 
+use buffer::Cursor;
 use intern::Symbol;
+use iter::{TtElement, TtIter};
 use stdx::{impl_from, itertools::Itertools as _};
 
 pub use text_size::{TextRange, TextSize};
@@ -75,23 +78,6 @@ pub enum TokenTree {
 }
 impl_from!(Leaf, Subtree for TokenTree);
 impl TokenTree {
-    pub fn empty(span: S) -> Self {
-        Self::Subtree(Subtree {
-            delimiter: Delimiter::invisible_spanned(span),
-            token_trees: Box::new([]),
-        })
-    }
-
-    pub fn subtree_or_wrap(self, span: DelimSpan) -> Subtree {
-        match self {
-            TokenTree::Leaf(_) => Subtree {
-                delimiter: Delimiter::invisible_delim_spanned(span),
-                token_trees: Box::new([self]),
-            },
-            TokenTree::Subtree(s) => s,
-        }
-    }
-
     pub fn first_span(&self) -> S {
         match self {
             TokenTree::Leaf(l) => *l.span(),
@@ -118,38 +104,422 @@ impl Leaf {
 }
 impl_from!(Literal, Punct, Ident for Leaf);
 
-#[derive(Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Subtree {
     pub delimiter: Delimiter,
-    pub token_trees: Box<[TokenTree]>,
+    /// Number of following token trees that belong to this subtree, excluding this subtree.
+    pub len: u32,
 }
 
-impl Subtree {
+impl Subtree {
+    pub fn usize_len(&self) -> usize {
+        self.len as usize
+    }
+}
+
+#[derive(Clone, PartialEq, Eq, Hash)]
+pub struct TopSubtree(pub Box<[TokenTree]>);
+
+impl TopSubtree {
     pub fn empty(span: DelimSpan) -> Self {
-        Subtree { delimiter: Delimiter::invisible_delim_spanned(span), token_trees: Box::new([]) }
+        Self(Box::new([TokenTree::Subtree(Subtree {
+            delimiter: Delimiter::invisible_delim_spanned(span),
+            len: 0,
+        })]))
+    }
+
+    pub fn invisible_from_leaves(delim_span: S, leaves: [Leaf; N]) -> Self {
+        let mut builder = TopSubtreeBuilder::new(Delimiter::invisible_spanned(delim_span));
+        builder.extend(leaves);
+        builder.build()
+    }
+
+    pub fn from_token_trees(delimiter: Delimiter, token_trees: TokenTreesView<'_, S>) -> Self {
+        let mut builder = TopSubtreeBuilder::new(delimiter);
+        builder.extend_with_tt(token_trees);
+        builder.build()
+    }
+
+    pub fn from_subtree(subtree: SubtreeView<'_, S>) -> Self {
+        Self(subtree.0.into())
+    }
+
+    pub fn view(&self) -> SubtreeView<'_, S> {
+        SubtreeView::new(&self.0)
+    }
+
+    pub fn iter(&self) -> TtIter<'_, S> {
+        self.view().iter()
     }
 
-    /// This is slow, and should be avoided, as it will always reallocate!
-    pub fn push(&mut self, subtree: TokenTree) {
-        let mut mutable_trees = std::mem::take(&mut self.token_trees).into_vec();
+    pub fn top_subtree(&self) -> &Subtree {
+        self.view().top_subtree()
+    }
 
-        // Reserve exactly space for one element, to avoid `into_boxed_slice` having to reallocate again.
-        mutable_trees.reserve_exact(1);
-        mutable_trees.push(subtree);
+    pub fn top_subtree_delimiter_mut(&mut self) -> &mut Delimiter {
+        let TokenTree::Subtree(subtree) = &mut self.0[0] else {
+            unreachable!("the first token tree is always the top subtree");
+        };
+        &mut subtree.delimiter
+    }
 
-        self.token_trees = mutable_trees.into_boxed_slice();
+    pub fn token_trees(&self) -> TokenTreesView<'_, S> {
+        self.view().token_trees()
     }
 }
 
-#[derive(Clone, PartialEq, Eq, Hash)]
-pub struct SubtreeBuilder {
-    pub delimiter: Delimiter,
-    pub token_trees: Vec>,
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct TopSubtreeBuilder {
+    unclosed_subtree_indices: Vec,
+    token_trees: Vec>,
+    last_closed_subtree: Option,
+}
+
+impl TopSubtreeBuilder {
+    pub fn new(top_delimiter: Delimiter) -> Self {
+        let mut result = Self {
+            unclosed_subtree_indices: Vec::new(),
+            token_trees: Vec::new(),
+            last_closed_subtree: None,
+        };
+        let top_subtree = TokenTree::Subtree(Subtree { delimiter: top_delimiter, len: 0 });
+        result.token_trees.push(top_subtree);
+        result
+    }
+
+    pub fn open(&mut self, delimiter_kind: DelimiterKind, open_span: S) {
+        self.unclosed_subtree_indices.push(self.token_trees.len());
+        self.token_trees.push(TokenTree::Subtree(Subtree {
+            delimiter: Delimiter {
+                open: open_span,
+                close: open_span, // Will be overwritten on close.
+                kind: delimiter_kind,
+            },
+            len: 0,
+        }));
+    }
+
+    pub fn close(&mut self, close_span: S) {
+        let last_unclosed_index = self
+            .unclosed_subtree_indices
+            .pop()
+            .expect("attempt to close a `tt::Subtree` when none is open");
+        let subtree_len = (self.token_trees.len() - last_unclosed_index - 1) as u32;
+        let TokenTree::Subtree(subtree) = &mut self.token_trees[last_unclosed_index] else {
+            unreachable!("unclosed token tree is always a subtree");
+        };
+        subtree.len = subtree_len;
+        subtree.delimiter.close = close_span;
+        self.last_closed_subtree = Some(last_unclosed_index);
+    }
+
+    /// You cannot call this consecutively, it will only work once after close.
+    pub fn remove_last_subtree_if_invisible(&mut self) {
+        let Some(last_subtree_idx) = self.last_closed_subtree else { return };
+        if let TokenTree::Subtree(Subtree {
+            delimiter: Delimiter { kind: DelimiterKind::Invisible, .. },
+            ..
+        }) = self.token_trees[last_subtree_idx]
+        {
+            self.token_trees.remove(last_subtree_idx);
+            self.last_closed_subtree = None;
+        }
+    }
+
+    pub fn push(&mut self, leaf: Leaf) {
+        self.token_trees.push(TokenTree::Leaf(leaf));
+    }
+
+    pub fn extend(&mut self, leaves: impl IntoIterator>) {
+        self.token_trees.extend(leaves.into_iter().map(TokenTree::Leaf));
+    }
+
+    /// This does not check the token trees are valid, beware!
+    pub fn extend_tt_dangerous(&mut self, tt: impl IntoIterator>) {
+        self.token_trees.extend(tt);
+    }
+
+    pub fn extend_with_tt(&mut self, tt: TokenTreesView<'_, S>) {
+        self.token_trees.extend(tt.0.iter().cloned());
+    }
+
+    pub fn expected_delimiter(&self) -> Option<&Delimiter> {
+        self.unclosed_subtree_indices.last().map(|&subtree_idx| {
+            let TokenTree::Subtree(subtree) = &self.token_trees[subtree_idx] else {
+                unreachable!("unclosed token tree is always a subtree")
+            };
+            &subtree.delimiter
+        })
+    }
+
+    /// Converts unclosed subtree to a punct of their open delimiter.
+    // FIXME: This is incorrect to do, delimiters can never be puncts. See #18244.
+    pub fn flatten_unclosed_subtrees(&mut self) {
+        for &subtree_idx in &self.unclosed_subtree_indices {
+            let TokenTree::Subtree(subtree) = &self.token_trees[subtree_idx] else {
+                unreachable!("unclosed token tree is always a subtree")
+            };
+            let char = match subtree.delimiter.kind {
+                DelimiterKind::Parenthesis => '(',
+                DelimiterKind::Brace => '{',
+                DelimiterKind::Bracket => '[',
+                DelimiterKind::Invisible => '$',
+            };
+            self.token_trees[subtree_idx] = TokenTree::Leaf(Leaf::Punct(Punct {
+                char,
+                spacing: Spacing::Alone,
+                span: subtree.delimiter.open,
+            }));
+        }
+        self.unclosed_subtree_indices.clear();
+    }
+
+    /// Builds, and remove the top subtree if it has only one subtree child.
+    pub fn build_skip_top_subtree(mut self) -> TopSubtree {
+        let top_tts = TokenTreesView::new(&self.token_trees[1..]);
+        match top_tts.try_into_subtree() {
+            Some(_) => {
+                assert!(
+                    self.unclosed_subtree_indices.is_empty(),
+                    "attempt to build an unbalanced `TopSubtreeBuilder`"
+                );
+                TopSubtree(self.token_trees.drain(1..).collect())
+            }
+            None => self.build(),
+        }
+    }
+
+    pub fn build(mut self) -> TopSubtree {
+        assert!(
+            self.unclosed_subtree_indices.is_empty(),
+            "attempt to build an unbalanced `TopSubtreeBuilder`"
+        );
+        let total_len = self.token_trees.len() as u32;
+        let TokenTree::Subtree(top_subtree) = &mut self.token_trees[0] else {
+            unreachable!("first token tree is always a subtree");
+        };
+        top_subtree.len = total_len - 1;
+        TopSubtree(self.token_trees.into_boxed_slice())
+    }
+
+    pub fn restore_point(&self) -> SubtreeBuilderRestorePoint {
+        SubtreeBuilderRestorePoint {
+            unclosed_subtree_indices_len: self.unclosed_subtree_indices.len(),
+            token_trees_len: self.token_trees.len(),
+            last_closed_subtree: self.last_closed_subtree,
+        }
+    }
+
+    pub fn restore(&mut self, restore_point: SubtreeBuilderRestorePoint) {
+        self.unclosed_subtree_indices.truncate(restore_point.unclosed_subtree_indices_len);
+        self.token_trees.truncate(restore_point.token_trees_len);
+        self.last_closed_subtree = restore_point.last_closed_subtree;
+    }
+}
+
+#[derive(Clone, Copy)]
+pub struct SubtreeBuilderRestorePoint {
+    unclosed_subtree_indices_len: usize,
+    token_trees_len: usize,
+    last_closed_subtree: Option,
+}
+
+#[derive(Clone, Copy)]
+pub struct TokenTreesView<'a, S>(&'a [TokenTree]);
+
+impl<'a, S: Copy> TokenTreesView<'a, S> {
+    pub fn new(tts: &'a [TokenTree]) -> Self {
+        if cfg!(debug_assertions) {
+            tts.iter().enumerate().for_each(|(idx, tt)| {
+                if let TokenTree::Subtree(tt) = &tt {
+                    // `<` and not `<=` because `Subtree.len` does not include the subtree node itself.
+                    debug_assert!(
+                        idx + tt.usize_len() < tts.len(),
+                        "`TokenTreeView::new()` was given a cut-in-half list"
+                    );
+                }
+            });
+        }
+        Self(tts)
+    }
+
+    pub fn iter(&self) -> TtIter<'a, S> {
+        TtIter::new(self.0)
+    }
+
+    pub fn cursor(&self) -> Cursor<'a, S> {
+        Cursor::new(self.0)
+    }
+
+    pub fn len(&self) -> usize {
+        self.0.len()
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.0.is_empty()
+    }
+
+    pub fn try_into_subtree(self) -> Option> {
+        if let Some(TokenTree::Subtree(subtree)) = self.0.first() {
+            if subtree.usize_len() == (self.0.len() - 1) {
+                return Some(SubtreeView::new(self.0));
+            }
+        }
+        None
+    }
+
+    pub fn strip_invisible(self) -> TokenTreesView<'a, S> {
+        self.try_into_subtree().map(|subtree| subtree.strip_invisible()).unwrap_or(self)
+    }
+
+    /// This returns a **flat** structure of tokens (subtrees will be represented by a single node
+    /// preceding their children), so it isn't suited for most use cases, only for matching leaves
+    /// at the beginning/end with no subtrees before them. If you need a structured pass, use [`TtIter`].
+    pub fn flat_tokens(&self) -> &'a [TokenTree] {
+        self.0
+    }
+
+    pub fn split(
+        self,
+        mut split_fn: impl FnMut(TtElement<'a, S>) -> bool,
+    ) -> impl Iterator> {
+        let mut subtree_iter = self.iter();
+        let mut need_to_yield_even_if_empty = true;
+        let result = std::iter::from_fn(move || {
+            if subtree_iter.is_empty() && !need_to_yield_even_if_empty {
+                return None;
+            };
+
+            need_to_yield_even_if_empty = false;
+            let savepoint = subtree_iter.savepoint();
+            let mut result = subtree_iter.from_savepoint(savepoint);
+            while let Some(tt) = subtree_iter.next() {
+                if split_fn(tt) {
+                    need_to_yield_even_if_empty = true;
+                    break;
+                }
+                result = subtree_iter.from_savepoint(savepoint);
+            }
+            Some(result)
+        });
+        result
+    }
 }
 
-impl SubtreeBuilder {
-    pub fn build(self) -> Subtree {
-        Subtree { delimiter: self.delimiter, token_trees: self.token_trees.into_boxed_slice() }
+impl fmt::Debug for TokenTreesView<'_, S> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let mut iter = self.iter();
+        while let Some(tt) = iter.next() {
+            print_debug_token(f, 0, tt)?;
+            if !iter.is_empty() {
+                writeln!(f)?;
+            }
+        }
+        Ok(())
+    }
+}
+
+impl fmt::Display for TokenTreesView<'_, S> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        return token_trees_display(f, self.iter());
+
+        fn subtree_display(
+            subtree: &Subtree,
+            f: &mut fmt::Formatter<'_>,
+            iter: TtIter<'_, S>,
+        ) -> fmt::Result {
+            let (l, r) = match subtree.delimiter.kind {
+                DelimiterKind::Parenthesis => ("(", ")"),
+                DelimiterKind::Brace => ("{", "}"),
+                DelimiterKind::Bracket => ("[", "]"),
+                DelimiterKind::Invisible => ("", ""),
+            };
+            f.write_str(l)?;
+            token_trees_display(f, iter)?;
+            f.write_str(r)?;
+            Ok(())
+        }
+
+        fn token_trees_display(f: &mut fmt::Formatter<'_>, iter: TtIter<'_, S>) -> fmt::Result {
+            let mut needs_space = false;
+            for child in iter {
+                if needs_space {
+                    f.write_str(" ")?;
+                }
+                needs_space = true;
+
+                match child {
+                    TtElement::Leaf(Leaf::Punct(p)) => {
+                        needs_space = p.spacing == Spacing::Alone;
+                        fmt::Display::fmt(p, f)?;
+                    }
+                    TtElement::Leaf(leaf) => fmt::Display::fmt(leaf, f)?,
+                    TtElement::Subtree(subtree, subtree_iter) => {
+                        subtree_display(subtree, f, subtree_iter)?
+                    }
+                }
+            }
+            Ok(())
+        }
+    }
+}
+
+#[derive(Clone, Copy)]
+// Invariant: always starts with `Subtree` that covers the entire thing.
+pub struct SubtreeView<'a, S>(&'a [TokenTree]);
+
+impl<'a, S: Copy> SubtreeView<'a, S> {
+    pub fn new(tts: &'a [TokenTree]) -> Self {
+        if cfg!(debug_assertions) {
+            let TokenTree::Subtree(subtree) = &tts[0] else {
+                panic!("first token tree must be a subtree in `SubtreeView`");
+            };
+            assert_eq!(
+                subtree.usize_len(),
+                tts.len() - 1,
+                "subtree must cover the entire `SubtreeView`"
+            );
+        }
+        Self(tts)
+    }
+
+    pub fn as_token_trees(self) -> TokenTreesView<'a, S> {
+        TokenTreesView::new(self.0)
+    }
+
+    pub fn iter(&self) -> TtIter<'a, S> {
+        TtIter::new(&self.0[1..])
+    }
+
+    pub fn top_subtree(&self) -> &'a Subtree {
+        let TokenTree::Subtree(subtree) = &self.0[0] else {
+            unreachable!("the first token tree is always the top subtree");
+        };
+        subtree
+    }
+
+    pub fn strip_invisible(&self) -> TokenTreesView<'a, S> {
+        if self.top_subtree().delimiter.kind == DelimiterKind::Invisible {
+            TokenTreesView::new(&self.0[1..])
+        } else {
+            TokenTreesView::new(self.0)
+        }
+    }
+
+    pub fn token_trees(&self) -> TokenTreesView<'a, S> {
+        TokenTreesView::new(&self.0[1..])
+    }
+}
+
+impl fmt::Debug for SubtreeView<'_, S> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt::Debug::fmt(&TokenTreesView(self.0), f)
+    }
+}
+
+impl fmt::Display for SubtreeView<'_, S> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt::Display::fmt(&TokenTreesView(self.0), f)
     }
 }
 
@@ -348,6 +718,7 @@ fn print_debug_subtree(
     f: &mut fmt::Formatter<'_>,
     subtree: &Subtree,
     level: usize,
+    iter: TtIter<'_, S>,
 ) -> fmt::Result {
     let align = "  ".repeat(level);
 
@@ -363,14 +734,9 @@ fn print_debug_subtree(
     fmt::Debug::fmt(&open, f)?;
     write!(f, " ")?;
     fmt::Debug::fmt(&close, f)?;
-    if !subtree.token_trees.is_empty() {
+    for child in iter {
         writeln!(f)?;
-        for (idx, child) in subtree.token_trees.iter().enumerate() {
-            print_debug_token(f, child, level + 1)?;
-            if idx != subtree.token_trees.len() - 1 {
-                writeln!(f)?;
-            }
-        }
+        print_debug_token(f, level + 1, child)?;
     }
 
     Ok(())
@@ -378,13 +744,13 @@ fn print_debug_subtree(
 
 fn print_debug_token(
     f: &mut fmt::Formatter<'_>,
-    tkn: &TokenTree,
     level: usize,
+    tt: TtElement<'_, S>,
 ) -> fmt::Result {
     let align = "  ".repeat(level);
 
-    match tkn {
-        TokenTree::Leaf(leaf) => match leaf {
+    match tt {
+        TtElement::Leaf(leaf) => match leaf {
             Leaf::Literal(lit) => {
                 write!(
                     f,
@@ -417,54 +783,23 @@ fn print_debug_token(
                 )?;
             }
         },
-        TokenTree::Subtree(subtree) => {
-            print_debug_subtree(f, subtree, level)?;
+        TtElement::Subtree(subtree, subtree_iter) => {
+            print_debug_subtree(f, subtree, level, subtree_iter)?;
         }
     }
 
     Ok(())
 }
 
-impl fmt::Debug for Subtree {
+impl fmt::Debug for TopSubtree {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        print_debug_subtree(f, self, 0)
+        fmt::Debug::fmt(&self.view(), f)
     }
 }
 
-impl fmt::Display for TokenTree {
+impl fmt::Display for TopSubtree {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            TokenTree::Leaf(it) => fmt::Display::fmt(it, f),
-            TokenTree::Subtree(it) => fmt::Display::fmt(it, f),
-        }
-    }
-}
-
-impl fmt::Display for Subtree {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let (l, r) = match self.delimiter.kind {
-            DelimiterKind::Parenthesis => ("(", ")"),
-            DelimiterKind::Brace => ("{", "}"),
-            DelimiterKind::Bracket => ("[", "]"),
-            DelimiterKind::Invisible => ("", ""),
-        };
-        f.write_str(l)?;
-        let mut needs_space = false;
-        for tt in self.token_trees.iter() {
-            if needs_space {
-                f.write_str(" ")?;
-            }
-            needs_space = true;
-            match tt {
-                TokenTree::Leaf(Leaf::Punct(p)) => {
-                    needs_space = p.spacing == Spacing::Alone;
-                    fmt::Display::fmt(p, f)?;
-                }
-                tt => fmt::Display::fmt(tt, f)?,
-            }
-        }
-        f.write_str(r)?;
-        Ok(())
+        fmt::Display::fmt(&self.view(), f)
     }
 }
 
@@ -538,34 +873,45 @@ impl fmt::Display for Punct {
 impl Subtree {
     /// Count the number of tokens recursively
     pub fn count(&self) -> usize {
-        let children_count = self
-            .token_trees
-            .iter()
-            .map(|c| match c {
-                TokenTree::Subtree(c) => c.count(),
-                TokenTree::Leaf(_) => 0,
-            })
-            .sum::();
-
-        self.token_trees.len() + children_count
+        self.usize_len()
     }
 }
 
-impl Subtree {
+impl TopSubtree {
     /// A simple line string used for debugging
-    pub fn as_debug_string(&self) -> String {
-        let delim = match self.delimiter.kind {
-            DelimiterKind::Brace => ("{", "}"),
-            DelimiterKind::Bracket => ("[", "]"),
-            DelimiterKind::Parenthesis => ("(", ")"),
-            DelimiterKind::Invisible => ("$", "$"),
-        };
+    pub fn subtree_as_debug_string(&self, subtree_idx: usize) -> String {
+        fn debug_subtree(
+            output: &mut String,
+            subtree: &Subtree,
+            iter: &mut std::slice::Iter<'_, TokenTree>,
+        ) {
+            let delim = match subtree.delimiter.kind {
+                DelimiterKind::Brace => ("{", "}"),
+                DelimiterKind::Bracket => ("[", "]"),
+                DelimiterKind::Parenthesis => ("(", ")"),
+                DelimiterKind::Invisible => ("$", "$"),
+            };
 
-        let mut res = String::new();
-        res.push_str(delim.0);
-        let mut last = None;
-        for child in self.token_trees.iter() {
-            let s = match child {
+            output.push_str(delim.0);
+            let mut last = None;
+            let mut idx = 0;
+            while idx < subtree.len {
+                let child = iter.next().unwrap();
+                debug_token_tree(output, child, last, iter);
+                last = Some(child);
+                idx += 1;
+            }
+
+            output.push_str(delim.1);
+        }
+
+        fn debug_token_tree(
+            output: &mut String,
+            tt: &TokenTree,
+            last: Option<&TokenTree>,
+            iter: &mut std::slice::Iter<'_, TokenTree>,
+        ) {
+            match tt {
                 TokenTree::Leaf(it) => {
                     let s = match it {
                         Leaf::Literal(it) => it.symbol.to_string(),
@@ -574,31 +920,37 @@ impl Subtree {
                     };
                     match (it, last) {
                         (Leaf::Ident(_), Some(&TokenTree::Leaf(Leaf::Ident(_)))) => {
-                            " ".to_owned() + &s
+                            output.push(' ');
+                            output.push_str(&s);
                         }
                         (Leaf::Punct(_), Some(TokenTree::Leaf(Leaf::Punct(punct)))) => {
                             if punct.spacing == Spacing::Alone {
-                                " ".to_owned() + &s
+                                output.push(' ');
+                                output.push_str(&s);
                             } else {
-                                s
+                                output.push_str(&s);
                             }
                         }
-                        _ => s,
+                        _ => output.push_str(&s),
                     }
                 }
-                TokenTree::Subtree(it) => it.as_debug_string(),
-            };
-            res.push_str(&s);
-            last = Some(child);
+                TokenTree::Subtree(it) => debug_subtree(output, it, iter),
+            }
         }
 
-        res.push_str(delim.1);
+        let mut res = String::new();
+        debug_token_tree(
+            &mut res,
+            &self.0[subtree_idx],
+            None,
+            &mut self.0[subtree_idx + 1..].iter(),
+        );
         res
     }
 }
 
-pub fn pretty(tkns: &[TokenTree]) -> String {
-    fn tokentree_to_text(tkn: &TokenTree) -> String {
+pub fn pretty(mut tkns: &[TokenTree]) -> String {
+    fn tokentree_to_text(tkn: &TokenTree, tkns: &mut &[TokenTree]) -> String {
         match tkn {
             TokenTree::Leaf(Leaf::Ident(ident)) => {
                 format!("{}{}", ident.is_raw.as_str(), ident.sym)
@@ -606,7 +958,9 @@ pub fn pretty(tkns: &[TokenTree]) -> String {
             TokenTree::Leaf(Leaf::Literal(literal)) => format!("{literal}"),
             TokenTree::Leaf(Leaf::Punct(punct)) => format!("{}", punct.char),
             TokenTree::Subtree(subtree) => {
-                let content = pretty(&subtree.token_trees);
+                let (subtree_content, rest) = tkns.split_at(subtree.usize_len());
+                let content = pretty(subtree_content);
+                *tkns = rest;
                 let (open, close) = match subtree.delimiter.kind {
                     DelimiterKind::Brace => ("{", "}"),
                     DelimiterKind::Bracket => ("[", "]"),
@@ -618,16 +972,18 @@ pub fn pretty(tkns: &[TokenTree]) -> String {
         }
     }
 
-    tkns.iter()
-        .fold((String::new(), true), |(last, last_to_joint), tkn| {
-            let s = [last, tokentree_to_text(tkn)].join(if last_to_joint { "" } else { " " });
-            let mut is_joint = false;
-            if let TokenTree::Leaf(Leaf::Punct(punct)) = tkn {
-                if punct.spacing == Spacing::Joint {
-                    is_joint = true;
-                }
+    let mut last = String::new();
+    let mut last_to_joint = true;
+
+    while let Some((tkn, rest)) = tkns.split_first() {
+        tkns = rest;
+        last = [last, tokentree_to_text(tkn, &mut tkns)].join(if last_to_joint { "" } else { " " });
+        last_to_joint = false;
+        if let TokenTree::Leaf(Leaf::Punct(punct)) = tkn {
+            if punct.spacing == Spacing::Joint {
+                last_to_joint = true;
             }
-            (s, is_joint)
-        })
-        .0
+        }
+    }
+    last
 }
diff --git a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
index 09296dc6dd533..bc54d7168f093 100644
--- a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
@@ -16,7 +16,7 @@ doctest = false
 tracing.workspace = true
 walkdir = "2.3.2"
 crossbeam-channel.workspace = true
-notify = "6.1.1"
+notify = "8.0.0"
 rayon = "1.10.0"
 
 stdx.workspace = true
diff --git a/src/tools/rust-analyzer/docs/book/README.md b/src/tools/rust-analyzer/docs/book/README.md
new file mode 100644
index 0000000000000..a9d10df664355
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/README.md
@@ -0,0 +1,29 @@
+# rust-analyzer documentation
+
+The rust analyzer manual uses [mdbook](https://rust-lang.github.io/mdBook/).
+
+## Quick start
+
+To run the documentation site locally:
+
+```shell
+cargo install mdbook
+cd docs/book
+mdbook serve
+# make changes to documentation files in doc/book/src
+# ...
+```
+
+mdbook will rebuild the documentation as changes are made.
+
+## Making updates
+
+While not required, installing the mdbook binary can be helfpul in order to see the changes.
+Start with the mdbook [User Guide](https://rust-lang.github.io/mdBook/guide/installation.html) to familiarize yourself with the tool.
+
+## Generated documentation
+
+Four sections are generated dynamically: assists, configuration, diagnostics and features. Their content is found in the `generated.md` files
+of the respective book section, for example `src/configuration_generated.md`, and are included in the book via mdbook's
+[include](https://rust-lang.github.io/mdBook/format/mdbook.html#including-files) functionality. Generated files can be rebuilt by running the various
+test cases that generate them, or by simply running all of the `rust-analyzer` tests with `cargo test`.
diff --git a/src/tools/rust-analyzer/docs/book/book.toml b/src/tools/rust-analyzer/docs/book/book.toml
new file mode 100644
index 0000000000000..ba3c1dede5db7
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/book.toml
@@ -0,0 +1,41 @@
+[book]
+authors = ["The rust-analyzer authors"]
+language = "en"
+multilingual = false
+src = "src"
+title = "rust-analyzer"
+
+[rust]
+edition = "2021"
+
+[output.html]
+edit-url-template = "https://github.com/rust-lang/rust-analyzer/edit/master/manual/{path}"
+git-repository-url = "https://github.com/rust-lang/rust-analyzer/tree/master/manual"
+mathjax-support = true
+site-url = "/manual/"
+
+[output.html.playground]
+editable = true
+runnable = false
+line-numbers = true
+
+[output.html.search]
+boost-hierarchy = 2
+boost-paragraph = 1
+boost-title = 2
+expand = true
+heading-split-level = 2
+limit-results = 20
+use-boolean-and = true
+
+[output.html.redirect]
+"/manual.html" = "/index.html"
+
+[output.html.fold]
+enable = true
+level = 3
+
+[preprocessor.toc]
+command = "mdbook-toc"
+renderer = ["html"]
+max-level = 3
diff --git a/src/tools/rust-analyzer/docs/book/src/README.md b/src/tools/rust-analyzer/docs/book/src/README.md
new file mode 100644
index 0000000000000..71f34e0346646
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/README.md
@@ -0,0 +1,21 @@
+# rust-analyzer
+
+At its core, rust-analyzer is a **library** for semantic analysis of
+Rust code as it changes over time. This manual focuses on a specific
+usage of the library -- running it as part of a server that implements
+the [Language Server
+Protocol](https://microsoft.github.io/language-server-protocol/) (LSP).
+The LSP allows various code editors, like VS Code, Emacs or Vim, to
+implement semantic features like completion or goto definition by
+talking to an external language server process.
+
+To improve this document, send a pull request:
+[https://github.com/rust-lang/rust-analyzer](https://github.com/rust-lang/rust-analyzer/blob/master/docs/book/README.md)
+
+The manual is written in markdown and includes
+some extra files which are generated from the source code. Run
+`cargo test` and `cargo xtask codegen` to create these.
+
+If you have questions about using rust-analyzer, please ask them in the
+["IDEs and Editors"](https://users.rust-lang.org/c/ide/14) topic of Rust
+users forum.
diff --git a/src/tools/rust-analyzer/docs/book/src/SUMMARY.md b/src/tools/rust-analyzer/docs/book/src/SUMMARY.md
new file mode 100644
index 0000000000000..b3ed1e6df0a7a
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/SUMMARY.md
@@ -0,0 +1,13 @@
+# Summary
+
+- [Introduction](README.md)
+- [Installation](installation.md)
+- [Troubleshooting](troubleshooting.md)
+- [Configuration](configuration.md)
+  - [Non-Cargo Based Projects](non_cargo_based_projects.md)
+- [Security](security.md)
+- [Privacy](privacy.md)
+- [Features](features.md)
+  - [Assists (Code Actions)](assists.md)
+  - [Diagnostics](diagnostics.md)
+- [Editor Features](editor_features.md)
diff --git a/src/tools/rust-analyzer/docs/book/src/assists.md b/src/tools/rust-analyzer/docs/book/src/assists.md
new file mode 100644
index 0000000000000..9d7c3bc1d5b94
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/assists.md
@@ -0,0 +1,8 @@
+# Assists
+
+Assists, or code actions, are small local refactorings, available in a
+particular context. They are usually triggered by a shortcut or by
+clicking a light bulb icon in the editor. Cursor position or selection
+is signified by `┃` character.
+
+{{#include assists_generated.md:2:}}
diff --git a/src/tools/rust-analyzer/docs/book/src/configuration.md b/src/tools/rust-analyzer/docs/book/src/configuration.md
new file mode 100644
index 0000000000000..221a571c17c84
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/configuration.md
@@ -0,0 +1,51 @@
+# Configuration
+
+**Source:**
+[config.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/rust-analyzer/src/config.rs)
+
+The [Installation](#_installation) section contains details on
+configuration for some of the editors. In general `rust-analyzer` is
+configured via LSP messages, which means that it’s up to the editor to
+decide on the exact format and location of configuration files.
+
+Some clients, such as [VS Code](#vs-code) or [COC plugin in
+Vim](#coc-rust-analyzer) provide `rust-analyzer` specific configuration
+UIs. Others may require you to know a bit more about the interaction
+with `rust-analyzer`.
+
+For the later category, it might help to know that the initial
+configuration is specified as a value of the `initializationOptions`
+field of the [`InitializeParams` message, in the LSP
+protocol](https://microsoft.github.io/language-server-protocol/specifications/specification-current/#initialize).
+The spec says that the field type is `any?`, but `rust-analyzer` is
+looking for a JSON object that is constructed using settings from the
+list below. Name of the setting, ignoring the `rust-analyzer.` prefix,
+is used as a path, and value of the setting becomes the JSON property
+value.
+
+For example, a very common configuration is to enable proc-macro
+support, can be achieved by sending this JSON:
+
+    {
+      "cargo": {
+        "buildScripts": {
+          "enable": true,
+        },
+      },
+      "procMacro": {
+        "enable": true,
+      }
+    }
+
+Please consult your editor’s documentation to learn more about how to
+configure [LSP
+servers](https://microsoft.github.io/language-server-protocol/).
+
+To verify which configuration is actually used by `rust-analyzer`, set
+`RA_LOG` environment variable to `rust_analyzer=info` and look for
+config-related messages. Logs should show both the JSON that
+`rust-analyzer` sees as well as the updated config.
+
+This is the list of config options `rust-analyzer` supports:
+
+{{#include configuration_generated.md}}
diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
new file mode 100644
index 0000000000000..55678926609c4
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
@@ -0,0 +1,1206 @@
+**rust-analyzer.assist.emitMustUse** (default: false)
+
+ Whether to insert #[must_use] when generating `as_` methods
+for enum variants.
+
+
+**rust-analyzer.assist.expressionFillDefault** (default: "todo")
+
+ Placeholder expression to use for missing expressions in assists.
+
+
+**rust-analyzer.assist.termSearch.borrowcheck** (default: true)
+
+ Enable borrow checking for term search code assists. If set to false, also there will be more suggestions, but some of them may not borrow-check.
+
+
+**rust-analyzer.assist.termSearch.fuel** (default: 1800)
+
+ Term search fuel in "units of work" for assists (Defaults to 1800).
+
+
+**rust-analyzer.cachePriming.enable** (default: true)
+
+ Warm up caches on project load.
+
+
+**rust-analyzer.cachePriming.numThreads** (default: "physical")
+
+ How many worker threads to handle priming caches. The default `0` means to pick automatically.
+
+
+**rust-analyzer.cargo.allTargets** (default: true)
+
+ Pass `--all-targets` to cargo invocation.
+
+
+**rust-analyzer.cargo.autoreload** (default: true)
+
+ Automatically refresh project info via `cargo metadata` on
+`Cargo.toml` or `.cargo/config.toml` changes.
+
+
+**rust-analyzer.cargo.buildScripts.enable** (default: true)
+
+ Run build scripts (`build.rs`) for more precise code analysis.
+
+
+**rust-analyzer.cargo.buildScripts.invocationStrategy** (default: "per_workspace")
+
+ Specifies the invocation strategy to use when running the build scripts command.
+If `per_workspace` is set, the command will be executed for each Rust workspace with the
+workspace as the working directory.
+If `once` is set, the command will be executed once with the opened project as the
+working directory.
+This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
+is set.
+
+
+**rust-analyzer.cargo.buildScripts.overrideCommand** (default: null)
+
+ Override the command rust-analyzer uses to run build scripts and
+build procedural macros. The command is required to output json
+and should therefore include `--message-format=json` or a similar
+option.
+
+If there are multiple linked projects/workspaces, this command is invoked for
+each of them, with the working directory being the workspace root
+(i.e., the folder containing the `Cargo.toml`). This can be overwritten
+by changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#`.
+
+By default, a cargo invocation will be constructed for the configured
+targets and features, with the following base command line:
+
+```bash
+cargo check --quiet --workspace --message-format=json --all-targets --keep-going
+```
+.
+
+
+**rust-analyzer.cargo.buildScripts.rebuildOnSave** (default: true)
+
+ Rerun proc-macros building/build-scripts running when proc-macro
+or build-script sources change and are saved.
+
+
+**rust-analyzer.cargo.buildScripts.useRustcWrapper** (default: true)
+
+ Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
+avoid checking unnecessary things.
+
+
+ **rust-analyzer.cargo.cfgs**
+
+Default:
+
+```[
+  "debug_assertions",
+  "miri"
+]
+
+```
+
+ List of cfg options to enable with the given values.
+
+
+ **rust-analyzer.cargo.extraArgs** (default: [])
+
+ Extra arguments that are passed to every cargo invocation.
+
+
+**rust-analyzer.cargo.extraEnv** (default: {})
+
+ Extra environment variables that will be set when running cargo, rustc
+or other commands within the workspace. Useful for setting RUSTFLAGS.
+
+
+**rust-analyzer.cargo.features** (default: [])
+
+ List of features to activate.
+
+Set this to `"all"` to pass `--all-features` to cargo.
+
+
+**rust-analyzer.cargo.noDefaultFeatures** (default: false)
+
+ Whether to pass `--no-default-features` to cargo.
+
+
+**rust-analyzer.cargo.sysroot** (default: "discover")
+
+ Relative path to the sysroot, or "discover" to try to automatically find it via
+"rustc --print sysroot".
+
+Unsetting this disables sysroot loading.
+
+This option does not take effect until rust-analyzer is restarted.
+
+
+**rust-analyzer.cargo.sysrootSrc** (default: null)
+
+ Relative path to the sysroot library sources. If left unset, this will default to
+`{cargo.sysroot}/lib/rustlib/src/rust/library`.
+
+This option does not take effect until rust-analyzer is restarted.
+
+
+**rust-analyzer.cargo.target** (default: null)
+
+ Compilation target override (target tuple).
+
+
+**rust-analyzer.cargo.targetDir** (default: null)
+
+ Optional path to a rust-analyzer specific target directory.
+This prevents rust-analyzer's `cargo check` and initial build-script and proc-macro
+building from locking the `Cargo.lock` at the expense of duplicating build artifacts.
+
+Set to `true` to use a subdirectory of the existing target directory or
+set to a path relative to the workspace to use that path.
+
+
+**rust-analyzer.cfg.setTest** (default: true)
+
+ Set `cfg(test)` for local crates. Defaults to true.
+
+
+**rust-analyzer.checkOnSave** (default: true)
+
+ Run the check command for diagnostics on save.
+
+
+**rust-analyzer.check.allTargets** (default: null)
+
+ Check all targets and tests (`--all-targets`). Defaults to
+`#rust-analyzer.cargo.allTargets#`.
+
+
+**rust-analyzer.check.command** (default: "check")
+
+ Cargo command to use for `cargo check`.
+
+
+**rust-analyzer.check.extraArgs** (default: [])
+
+ Extra arguments for `cargo check`.
+
+
+**rust-analyzer.check.extraEnv** (default: {})
+
+ Extra environment variables that will be set when running `cargo check`.
+Extends `#rust-analyzer.cargo.extraEnv#`.
+
+
+**rust-analyzer.check.features** (default: null)
+
+ List of features to activate. Defaults to
+`#rust-analyzer.cargo.features#`.
+
+Set to `"all"` to pass `--all-features` to Cargo.
+
+
+**rust-analyzer.check.ignore** (default: [])
+
+ List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore.
+
+For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,...
+
+
+**rust-analyzer.check.invocationStrategy** (default: "per_workspace")
+
+ Specifies the invocation strategy to use when running the check command.
+If `per_workspace` is set, the command will be executed for each workspace.
+If `once` is set, the command will be executed once.
+This config only has an effect when `#rust-analyzer.check.overrideCommand#`
+is set.
+
+
+**rust-analyzer.check.noDefaultFeatures** (default: null)
+
+ Whether to pass `--no-default-features` to Cargo. Defaults to
+`#rust-analyzer.cargo.noDefaultFeatures#`.
+
+
+**rust-analyzer.check.overrideCommand** (default: null)
+
+ Override the command rust-analyzer uses instead of `cargo check` for
+diagnostics on save. The command is required to output json and
+should therefore include `--message-format=json` or a similar option
+(if your client supports the `colorDiagnosticOutput` experimental
+capability, you can use `--message-format=json-diagnostic-rendered-ansi`).
+
+If you're changing this because you're using some tool wrapping
+Cargo, you might also want to change
+`#rust-analyzer.cargo.buildScripts.overrideCommand#`.
+
+If there are multiple linked projects/workspaces, this command is invoked for
+each of them, with the working directory being the workspace root
+(i.e., the folder containing the `Cargo.toml`). This can be overwritten
+by changing `#rust-analyzer.check.invocationStrategy#`.
+
+If `$saved_file` is part of the command, rust-analyzer will pass
+the absolute path of the saved file to the provided command. This is
+intended to be used with non-Cargo build systems.
+Note that `$saved_file` is experimental and may be removed in the future.
+
+An example command would be:
+
+```bash
+cargo check --workspace --message-format=json --all-targets
+```
+.
+
+
+**rust-analyzer.check.targets** (default: null)
+
+ Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty.
+
+Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g.
+`["aarch64-apple-darwin", "x86_64-apple-darwin"]`.
+
+Aliased as `"checkOnSave.targets"`.
+
+
+**rust-analyzer.check.workspace** (default: true)
+
+ Whether `--workspace` should be passed to `cargo check`.
+If false, `-p ` will be passed instead if applicable. In case it is not, no
+check will be performed.
+
+
+**rust-analyzer.completion.addSemicolonToUnit** (default: true)
+
+ Whether to automatically add a semicolon when completing unit-returning functions.
+
+In `match` arms it completes a comma instead.
+
+
+**rust-analyzer.completion.autoAwait.enable** (default: true)
+
+ Toggles the additional completions that automatically show method calls and field accesses with `await` prefixed to them when completing on a future.
+
+
+**rust-analyzer.completion.autoIter.enable** (default: true)
+
+ Toggles the additional completions that automatically show method calls with `iter()` or `into_iter()` prefixed to them when completing on a type that has them.
+
+
+**rust-analyzer.completion.autoimport.enable** (default: true)
+
+ Toggles the additional completions that automatically add imports when completed.
+Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.
+
+
+ **rust-analyzer.completion.autoimport.exclude**
+
+Default:
+
+```[
+  {
+    "path": "core::borrow::Borrow",
+    "type": "methods"
+  },
+  {
+    "path": "core::borrow::BorrowMut",
+    "type": "methods"
+  }
+]
+
+```
+
+ A list of full paths to items to exclude from auto-importing completions.
+
+Traits in this list won't have their methods suggested in completions unless the trait
+is in scope.
+
+You can either specify a string path which defaults to type "always" or use the more verbose
+form `{ "path": "path::to::item", type: "always" }`.
+
+For traits the type "methods" can be used to only exclude the methods but not the trait itself.
+
+This setting also inherits `#rust-analyzer.completion.excludeTraits#`.
+
+
+ **rust-analyzer.completion.autoself.enable** (default: true)
+
+ Toggles the additional completions that automatically show method calls and field accesses
+with `self` prefixed to them when inside a method.
+
+
+**rust-analyzer.completion.callable.snippets** (default: "fill_arguments")
+
+ Whether to add parenthesis and argument snippets when completing function.
+
+
+**rust-analyzer.completion.excludeTraits** (default: [])
+
+ A list of full paths to traits whose methods to exclude from completion.
+
+Methods from these traits won't be completed, even if the trait is in scope. However, they will still be suggested on expressions whose type is `dyn Trait`, `impl Trait` or `T where T: Trait`.
+
+Note that the trait themselves can still be completed.
+
+
+**rust-analyzer.completion.fullFunctionSignatures.enable** (default: false)
+
+ Whether to show full function/method signatures in completion docs.
+
+
+**rust-analyzer.completion.hideDeprecated** (default: false)
+
+ Whether to omit deprecated items from autocompletion. By default they are marked as deprecated but not hidden.
+
+
+**rust-analyzer.completion.limit** (default: null)
+
+ Maximum number of completions to return. If `None`, the limit is infinite.
+
+
+**rust-analyzer.completion.postfix.enable** (default: true)
+
+ Whether to show postfix snippets like `dbg`, `if`, `not`, etc.
+
+
+**rust-analyzer.completion.privateEditable.enable** (default: false)
+
+ Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position.
+
+
+ **rust-analyzer.completion.snippets.custom**
+
+Default:
+
+```{
+  "Ok": {
+    "postfix": "ok",
+    "body": "Ok(${receiver})",
+    "description": "Wrap the expression in a `Result::Ok`",
+    "scope": "expr"
+  },
+  "Box::pin": {
+    "postfix": "pinbox",
+    "body": "Box::pin(${receiver})",
+    "requires": "std::boxed::Box",
+    "description": "Put the expression into a pinned `Box`",
+    "scope": "expr"
+  },
+  "Arc::new": {
+    "postfix": "arc",
+    "body": "Arc::new(${receiver})",
+    "requires": "std::sync::Arc",
+    "description": "Put the expression into an `Arc`",
+    "scope": "expr"
+  },
+  "Some": {
+    "postfix": "some",
+    "body": "Some(${receiver})",
+    "description": "Wrap the expression in an `Option::Some`",
+    "scope": "expr"
+  },
+  "Err": {
+    "postfix": "err",
+    "body": "Err(${receiver})",
+    "description": "Wrap the expression in a `Result::Err`",
+    "scope": "expr"
+  },
+  "Rc::new": {
+    "postfix": "rc",
+    "body": "Rc::new(${receiver})",
+    "requires": "std::rc::Rc",
+    "description": "Put the expression into an `Rc`",
+    "scope": "expr"
+  }
+}
+
+```
+
+ Custom completion snippets.
+
+
+ **rust-analyzer.completion.termSearch.enable** (default: false)
+
+ Whether to enable term search based snippets like `Some(foo.bar().baz())`.
+
+
+**rust-analyzer.completion.termSearch.fuel** (default: 1000)
+
+ Term search fuel in "units of work" for autocompletion (Defaults to 1000).
+
+
+**rust-analyzer.diagnostics.disabled** (default: [])
+
+ List of rust-analyzer diagnostics to disable.
+
+
+**rust-analyzer.diagnostics.enable** (default: true)
+
+ Whether to show native rust-analyzer diagnostics.
+
+
+**rust-analyzer.diagnostics.experimental.enable** (default: false)
+
+ Whether to show experimental rust-analyzer diagnostics that might
+have more false positives than usual.
+
+
+**rust-analyzer.diagnostics.remapPrefix** (default: {})
+
+ Map of prefixes to be substituted when parsing diagnostic file paths.
+This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.
+
+
+**rust-analyzer.diagnostics.styleLints.enable** (default: false)
+
+ Whether to run additional style lints.
+
+
+**rust-analyzer.diagnostics.warningsAsHint** (default: [])
+
+ List of warnings that should be displayed with hint severity.
+
+The warnings will be indicated by faded text or three dots in code
+and will not show up in the `Problems Panel`.
+
+
+**rust-analyzer.diagnostics.warningsAsInfo** (default: [])
+
+ List of warnings that should be displayed with info severity.
+
+The warnings will be indicated by a blue squiggly underline in code
+and a blue icon in the `Problems Panel`.
+
+
+**rust-analyzer.files.excludeDirs** (default: [])
+
+ These directories will be ignored by rust-analyzer. They are
+relative to the workspace root, and globs are not supported. You may
+also need to add the folders to Code's `files.watcherExclude`.
+
+
+**rust-analyzer.files.watcher** (default: "client")
+
+ Controls file watching implementation.
+
+
+**rust-analyzer.highlightRelated.breakPoints.enable** (default: true)
+
+ Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords.
+
+
+**rust-analyzer.highlightRelated.closureCaptures.enable** (default: true)
+
+ Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure.
+
+
+**rust-analyzer.highlightRelated.exitPoints.enable** (default: true)
+
+ Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`).
+
+
+**rust-analyzer.highlightRelated.references.enable** (default: true)
+
+ Enables highlighting of related references while the cursor is on any identifier.
+
+
+**rust-analyzer.highlightRelated.yieldPoints.enable** (default: true)
+
+ Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords.
+
+
+**rust-analyzer.hover.actions.debug.enable** (default: true)
+
+ Whether to show `Debug` action. Only applies when
+`#rust-analyzer.hover.actions.enable#` is set.
+
+
+**rust-analyzer.hover.actions.enable** (default: true)
+
+ Whether to show HoverActions in Rust files.
+
+
+**rust-analyzer.hover.actions.gotoTypeDef.enable** (default: true)
+
+ Whether to show `Go to Type Definition` action. Only applies when
+`#rust-analyzer.hover.actions.enable#` is set.
+
+
+**rust-analyzer.hover.actions.implementations.enable** (default: true)
+
+ Whether to show `Implementations` action. Only applies when
+`#rust-analyzer.hover.actions.enable#` is set.
+
+
+**rust-analyzer.hover.actions.references.enable** (default: false)
+
+ Whether to show `References` action. Only applies when
+`#rust-analyzer.hover.actions.enable#` is set.
+
+
+**rust-analyzer.hover.actions.run.enable** (default: true)
+
+ Whether to show `Run` action. Only applies when
+`#rust-analyzer.hover.actions.enable#` is set.
+
+
+**rust-analyzer.hover.actions.updateTest.enable** (default: true)
+
+ Whether to show `Update Test` action. Only applies when
+`#rust-analyzer.hover.actions.enable#` and `#rust-analyzer.hover.actions.run.enable#` are set.
+
+
+**rust-analyzer.hover.documentation.enable** (default: true)
+
+ Whether to show documentation on hover.
+
+
+**rust-analyzer.hover.documentation.keywords.enable** (default: true)
+
+ Whether to show keyword hover popups. Only applies when
+`#rust-analyzer.hover.documentation.enable#` is set.
+
+
+**rust-analyzer.hover.links.enable** (default: true)
+
+ Use markdown syntax for links on hover.
+
+
+**rust-analyzer.hover.maxSubstitutionLength** (default: 20)
+
+ Whether to show what types are used as generic arguments in calls etc. on hover, and what is their max length to show such types, beyond it they will be shown with ellipsis.
+
+This can take three values: `null` means "unlimited", the string `"hide"` means to not show generic substitutions at all, and a number means to limit them to X characters.
+
+The default is 20 characters.
+
+
+**rust-analyzer.hover.memoryLayout.alignment** (default: "hexadecimal")
+
+ How to render the align information in a memory layout hover.
+
+
+**rust-analyzer.hover.memoryLayout.enable** (default: true)
+
+ Whether to show memory layout data on hover.
+
+
+**rust-analyzer.hover.memoryLayout.niches** (default: false)
+
+ How to render the niche information in a memory layout hover.
+
+
+**rust-analyzer.hover.memoryLayout.offset** (default: "hexadecimal")
+
+ How to render the offset information in a memory layout hover.
+
+
+**rust-analyzer.hover.memoryLayout.size** (default: "both")
+
+ How to render the size information in a memory layout hover.
+
+
+**rust-analyzer.hover.show.enumVariants** (default: 5)
+
+ How many variants of an enum to display when hovering on. Show none if empty.
+
+
+**rust-analyzer.hover.show.fields** (default: 5)
+
+ How many fields of a struct, variant or union to display when hovering on. Show none if empty.
+
+
+**rust-analyzer.hover.show.traitAssocItems** (default: null)
+
+ How many associated items of a trait to display when hovering a trait.
+
+
+**rust-analyzer.imports.granularity.enforce** (default: false)
+
+ Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file.
+
+
+**rust-analyzer.imports.granularity.group** (default: "crate")
+
+ How imports should be grouped into use statements.
+
+
+**rust-analyzer.imports.group.enable** (default: true)
+
+ Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.
+
+
+**rust-analyzer.imports.merge.glob** (default: true)
+
+ Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`.
+
+
+**rust-analyzer.imports.preferNoStd** (default: false)
+
+ Prefer to unconditionally use imports of the core and alloc crate, over the std crate.
+
+
+**rust-analyzer.imports.preferPrelude** (default: false)
+
+ Whether to prefer import paths containing a `prelude` module.
+
+
+**rust-analyzer.imports.prefix** (default: "plain")
+
+ The path structure for newly inserted paths to use.
+
+
+**rust-analyzer.imports.prefixExternPrelude** (default: false)
+
+ Whether to prefix external (including std, core) crate imports with `::`. e.g. "use ::std::io::Read;".
+
+
+**rust-analyzer.inlayHints.bindingModeHints.enable** (default: false)
+
+ Whether to show inlay type hints for binding modes.
+
+
+**rust-analyzer.inlayHints.chainingHints.enable** (default: true)
+
+ Whether to show inlay type hints for method chains.
+
+
+**rust-analyzer.inlayHints.closingBraceHints.enable** (default: true)
+
+ Whether to show inlay hints after a closing `}` to indicate what item it belongs to.
+
+
+**rust-analyzer.inlayHints.closingBraceHints.minLines** (default: 25)
+
+ Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1
+to always show them).
+
+
+**rust-analyzer.inlayHints.closureCaptureHints.enable** (default: false)
+
+ Whether to show inlay hints for closure captures.
+
+
+**rust-analyzer.inlayHints.closureReturnTypeHints.enable** (default: "never")
+
+ Whether to show inlay type hints for return types of closures.
+
+
+**rust-analyzer.inlayHints.closureStyle** (default: "impl_fn")
+
+ Closure notation in type and chaining inlay hints.
+
+
+**rust-analyzer.inlayHints.discriminantHints.enable** (default: "never")
+
+ Whether to show enum variant discriminant hints.
+
+
+**rust-analyzer.inlayHints.expressionAdjustmentHints.enable** (default: "never")
+
+ Whether to show inlay hints for type adjustments.
+
+
+**rust-analyzer.inlayHints.expressionAdjustmentHints.hideOutsideUnsafe** (default: false)
+
+ Whether to hide inlay hints for type adjustments outside of `unsafe` blocks.
+
+
+**rust-analyzer.inlayHints.expressionAdjustmentHints.mode** (default: "prefix")
+
+ Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc).
+
+
+**rust-analyzer.inlayHints.genericParameterHints.const.enable** (default: true)
+
+ Whether to show const generic parameter name inlay hints.
+
+
+**rust-analyzer.inlayHints.genericParameterHints.lifetime.enable** (default: false)
+
+ Whether to show generic lifetime parameter name inlay hints.
+
+
+**rust-analyzer.inlayHints.genericParameterHints.type.enable** (default: false)
+
+ Whether to show generic type parameter name inlay hints.
+
+
+**rust-analyzer.inlayHints.implicitDrops.enable** (default: false)
+
+ Whether to show implicit drop hints.
+
+
+**rust-analyzer.inlayHints.implicitSizedBoundHints.enable** (default: false)
+
+ Whether to show inlay hints for the implied type parameter `Sized` bound.
+
+
+**rust-analyzer.inlayHints.lifetimeElisionHints.enable** (default: "never")
+
+ Whether to show inlay type hints for elided lifetimes in function signatures.
+
+
+**rust-analyzer.inlayHints.lifetimeElisionHints.useParameterNames** (default: false)
+
+ Whether to prefer using parameter names as the name for elided lifetime hints if possible.
+
+
+**rust-analyzer.inlayHints.maxLength** (default: 25)
+
+ Maximum length for inlay hints. Set to null to have an unlimited length.
+
+
+**rust-analyzer.inlayHints.parameterHints.enable** (default: true)
+
+ Whether to show function parameter name inlay hints at the call
+site.
+
+
+**rust-analyzer.inlayHints.rangeExclusiveHints.enable** (default: false)
+
+ Whether to show exclusive range inlay hints.
+
+
+**rust-analyzer.inlayHints.reborrowHints.enable** (default: "never")
+
+ Whether to show inlay hints for compiler inserted reborrows.
+This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#.
+
+
+**rust-analyzer.inlayHints.renderColons** (default: true)
+
+ Whether to render leading colons for type hints, and trailing colons for parameter hints.
+
+
+**rust-analyzer.inlayHints.typeHints.enable** (default: true)
+
+ Whether to show inlay type hints for variables.
+
+
+**rust-analyzer.inlayHints.typeHints.hideClosureInitialization** (default: false)
+
+ Whether to hide inlay type hints for `let` statements that initialize to a closure.
+Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`.
+
+
+**rust-analyzer.inlayHints.typeHints.hideClosureParameter** (default: false)
+
+ Whether to hide inlay parameter type hints for closures.
+
+
+**rust-analyzer.inlayHints.typeHints.hideNamedConstructor** (default: false)
+
+ Whether to hide inlay type hints for constructors.
+
+
+**rust-analyzer.interpret.tests** (default: false)
+
+ Enables the experimental support for interpreting tests.
+
+
+**rust-analyzer.joinLines.joinAssignments** (default: true)
+
+ Join lines merges consecutive declaration and initialization of an assignment.
+
+
+**rust-analyzer.joinLines.joinElseIf** (default: true)
+
+ Join lines inserts else between consecutive ifs.
+
+
+**rust-analyzer.joinLines.removeTrailingComma** (default: true)
+
+ Join lines removes trailing commas.
+
+
+**rust-analyzer.joinLines.unwrapTrivialBlock** (default: true)
+
+ Join lines unwraps trivial blocks.
+
+
+**rust-analyzer.lens.debug.enable** (default: true)
+
+ Whether to show `Debug` lens. Only applies when
+`#rust-analyzer.lens.enable#` is set.
+
+
+**rust-analyzer.lens.enable** (default: true)
+
+ Whether to show CodeLens in Rust files.
+
+
+**rust-analyzer.lens.implementations.enable** (default: true)
+
+ Whether to show `Implementations` lens. Only applies when
+`#rust-analyzer.lens.enable#` is set.
+
+
+**rust-analyzer.lens.location** (default: "above_name")
+
+ Where to render annotations.
+
+
+**rust-analyzer.lens.references.adt.enable** (default: false)
+
+ Whether to show `References` lens for Struct, Enum, and Union.
+Only applies when `#rust-analyzer.lens.enable#` is set.
+
+
+**rust-analyzer.lens.references.enumVariant.enable** (default: false)
+
+ Whether to show `References` lens for Enum Variants.
+Only applies when `#rust-analyzer.lens.enable#` is set.
+
+
+**rust-analyzer.lens.references.method.enable** (default: false)
+
+ Whether to show `Method References` lens. Only applies when
+`#rust-analyzer.lens.enable#` is set.
+
+
+**rust-analyzer.lens.references.trait.enable** (default: false)
+
+ Whether to show `References` lens for Trait.
+Only applies when `#rust-analyzer.lens.enable#` is set.
+
+
+**rust-analyzer.lens.run.enable** (default: true)
+
+ Whether to show `Run` lens. Only applies when
+`#rust-analyzer.lens.enable#` is set.
+
+
+**rust-analyzer.lens.updateTest.enable** (default: true)
+
+ Whether to show `Update Test` lens. Only applies when
+`#rust-analyzer.lens.enable#` and `#rust-analyzer.lens.run.enable#` are set.
+
+
+**rust-analyzer.linkedProjects** (default: [])
+
+ Disable project auto-discovery in favor of explicitly specified set
+of projects.
+
+Elements must be paths pointing to `Cargo.toml`,
+`rust-project.json`, `.rs` files (which will be treated as standalone files) or JSON
+objects in `rust-project.json` format.
+
+
+**rust-analyzer.lru.capacity** (default: null)
+
+ Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.
+
+
+**rust-analyzer.lru.query.capacities** (default: {})
+
+ Sets the LRU capacity of the specified queries.
+
+
+**rust-analyzer.notifications.cargoTomlNotFound** (default: true)
+
+ Whether to show `can't find Cargo.toml` error message.
+
+
+**rust-analyzer.numThreads** (default: null)
+
+ How many worker threads in the main loop. The default `null` means to pick automatically.
+
+
+**rust-analyzer.procMacro.attributes.enable** (default: true)
+
+ Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set.
+
+
+**rust-analyzer.procMacro.enable** (default: true)
+
+ Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`.
+
+
+**rust-analyzer.procMacro.ignored** (default: {})
+
+ These proc-macros will be ignored when trying to expand them.
+
+This config takes a map of crate names with the exported proc-macro names to ignore as values.
+
+
+**rust-analyzer.procMacro.server** (default: null)
+
+ Internal config, path to proc-macro server executable.
+
+
+**rust-analyzer.references.excludeImports** (default: false)
+
+ Exclude imports from find-all-references.
+
+
+**rust-analyzer.references.excludeTests** (default: false)
+
+ Exclude tests from find-all-references and call-hierarchy.
+
+
+**rust-analyzer.runnables.command** (default: null)
+
+ Command to be executed instead of 'cargo' for runnables.
+
+
+**rust-analyzer.runnables.extraArgs** (default: [])
+
+ Additional arguments to be passed to cargo for runnables such as
+tests or binaries. For example, it may be `--release`.
+
+
+ **rust-analyzer.runnables.extraTestBinaryArgs**
+
+Default:
+
+```[
+  "--show-output"
+]
+
+```
+
+ Additional arguments to be passed through Cargo to launched tests, benchmarks, or
+doc-tests.
+
+Unless the launched target uses a
+[custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field),
+they will end up being interpreted as options to
+[`rustc`’s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments).
+
+
+ **rust-analyzer.rustc.source** (default: null)
+
+ Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private
+projects, or "discover" to try to automatically find it if the `rustc-dev` component
+is installed.
+
+Any project which uses rust-analyzer with the rustcPrivate
+crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.
+
+This option does not take effect until rust-analyzer is restarted.
+
+
+**rust-analyzer.rustfmt.extraArgs** (default: [])
+
+ Additional arguments to `rustfmt`.
+
+
+**rust-analyzer.rustfmt.overrideCommand** (default: null)
+
+ Advanced option, fully override the command rust-analyzer uses for
+formatting. This should be the equivalent of `rustfmt` here, and
+not that of `cargo fmt`. The file contents will be passed on the
+standard input and the formatted result will be read from the
+standard output.
+
+
+**rust-analyzer.rustfmt.rangeFormatting.enable** (default: false)
+
+ Enables the use of rustfmt's unstable range formatting command for the
+`textDocument/rangeFormatting` request. The rustfmt option is unstable and only
+available on a nightly build.
+
+
+**rust-analyzer.semanticHighlighting.doc.comment.inject.enable** (default: true)
+
+ Inject additional highlighting into doc comments.
+
+When enabled, rust-analyzer will highlight rust source in doc comments as well as intra
+doc links.
+
+
+**rust-analyzer.semanticHighlighting.nonStandardTokens** (default: true)
+
+ Whether the server is allowed to emit non-standard tokens and modifiers.
+
+
+**rust-analyzer.semanticHighlighting.operator.enable** (default: true)
+
+ Use semantic tokens for operators.
+
+When disabled, rust-analyzer will emit semantic tokens only for operator tokens when
+they are tagged with modifiers.
+
+
+**rust-analyzer.semanticHighlighting.operator.specialization.enable** (default: false)
+
+ Use specialized semantic tokens for operators.
+
+When enabled, rust-analyzer will emit special token types for operator tokens instead
+of the generic `operator` token type.
+
+
+**rust-analyzer.semanticHighlighting.punctuation.enable** (default: false)
+
+ Use semantic tokens for punctuation.
+
+When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when
+they are tagged with modifiers or have a special role.
+
+
+**rust-analyzer.semanticHighlighting.punctuation.separate.macro.bang** (default: false)
+
+ When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro
+calls.
+
+
+**rust-analyzer.semanticHighlighting.punctuation.specialization.enable** (default: false)
+
+ Use specialized semantic tokens for punctuation.
+
+When enabled, rust-analyzer will emit special token types for punctuation tokens instead
+of the generic `punctuation` token type.
+
+
+**rust-analyzer.semanticHighlighting.strings.enable** (default: true)
+
+ Use semantic tokens for strings.
+
+In some editors (e.g. vscode) semantic tokens override other highlighting grammars.
+By disabling semantic tokens for strings, other grammars can be used to highlight
+their contents.
+
+
+**rust-analyzer.signatureInfo.detail** (default: "full")
+
+ Show full signature of the callable. Only shows parameters if disabled.
+
+
+**rust-analyzer.signatureInfo.documentation.enable** (default: true)
+
+ Show documentation.
+
+
+**rust-analyzer.typing.triggerChars** (default: "=.")
+
+ Specify the characters allowed to invoke special on typing triggers.
+- typing `=` after `let` tries to smartly add `;` if `=` is followed by an existing expression
+- typing `=` between two expressions adds `;` when in statement position
+- typing `=` to turn an assignment into an equality comparison removes `;` when in expression position
+- typing `.` in a chain method call auto-indents
+- typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the expression
+- typing `{` in a use item adds a closing `}` in the right place
+- typing `>` to complete a return type `->` will insert a whitespace after it
+- typing `<` in a path or type position inserts a closing `>` after the path or type.
+
+
+**rust-analyzer.vfs.extraIncludes** (default: [])
+
+ Additional paths to include in the VFS. Generally for code that is
+generated or otherwise managed by a build system outside of Cargo,
+though Cargo might be the eventual consumer.
+
+
+**rust-analyzer.workspace.discoverConfig** (default: null)
+
+ Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].
+
+[`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`.
+`progress_label` is used for the title in progress indicators, whereas `files_to_watch`
+is used to determine which build system-specific files should be watched in order to
+reload rust-analyzer.
+
+Below is an example of a valid configuration:
+```json
+"rust-analyzer.workspace.discoverConfig": {
+    "command": [
+        "rust-project",
+        "develop-json"
+    ],
+    "progressLabel": "rust-analyzer",
+    "filesToWatch": [
+        "BUCK"
+    ]
+}
+```
+
+## On `DiscoverWorkspaceConfig::command`
+
+**Warning**: This format is provisional and subject to change.
+
+[`DiscoverWorkspaceConfig::command`] *must* return a JSON object
+corresponding to `DiscoverProjectData::Finished`:
+
+```norun
+#[derive(Debug, Clone, Deserialize, Serialize)]
+#[serde(tag = "kind")]
+#[serde(rename_all = "snake_case")]
+enum DiscoverProjectData {
+    Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },
+    Error { error: String, source: Option },
+    Progress { message: String },
+}
+```
+
+As JSON, `DiscoverProjectData::Finished` is:
+
+```json
+{
+    // the internally-tagged representation of the enum.
+    "kind": "finished",
+    // the file used by a non-Cargo build system to define
+    // a package or target.
+    "buildfile": "rust-analyzer/BUILD",
+    // the contents of a rust-project.json, elided for brevity
+    "project": {
+        "sysroot": "foo",
+        "crates": []
+    }
+}
+```
+
+It is encouraged, but not required, to use the other variants on
+`DiscoverProjectData` to provide a more polished end-user experience.
+
+`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`,
+which will be substituted with the JSON-serialized form of the following
+enum:
+
+```norun
+#[derive(PartialEq, Clone, Debug, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub enum DiscoverArgument {
+   Path(AbsPathBuf),
+   Buildfile(AbsPathBuf),
+}
+```
+
+The JSON representation of `DiscoverArgument::Path` is:
+
+```json
+{
+    "path": "src/main.rs"
+}
+```
+
+Similarly, the JSON representation of `DiscoverArgument::Buildfile` is:
+
+```
+{
+    "buildfile": "BUILD"
+}
+```
+
+`DiscoverArgument::Path` is used to find and generate a `rust-project.json`,
+and therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to
+to update an existing workspace. As a reference for implementors,
+buck2's `rust-project` will likely be useful:
+https://github.com/facebook/buck2/tree/main/integrations/rust-project.
+
+
+**rust-analyzer.workspace.symbol.search.kind** (default: "only_types")
+
+ Workspace symbol search kind.
+
+
+**rust-analyzer.workspace.symbol.search.limit** (default: 128)
+
+ Limits the number of items returned from a workspace symbol search (Defaults to 128).
+Some clients like vs-code issue new searches on result filtering and don't require all results to be returned in the initial search.
+Other clients requires all results upfront and might require a higher limit.
+
+
+**rust-analyzer.workspace.symbol.search.scope** (default: "workspace")
+
+ Workspace symbol search scope.
+
+
diff --git a/src/tools/rust-analyzer/docs/book/src/diagnostics.md b/src/tools/rust-analyzer/docs/book/src/diagnostics.md
new file mode 100644
index 0000000000000..60685c98da711
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/diagnostics.md
@@ -0,0 +1,16 @@
+# Diagnostics
+
+While most errors and warnings provided by rust-analyzer come from the
+`cargo check` integration, there’s a growing number of diagnostics
+implemented using rust-analyzer’s own analysis. Some of these
+diagnostics don’t respect `#[allow]` or `#[deny]` attributes yet, but
+can be turned off using the `rust-analyzer.diagnostics.enable`,
+`rust-analyzer.diagnostics.experimental.enable` or
+`rust-analyzer.diagnostics.disabled` settings.
+
+## Clippy
+
+To run `cargo clippy` instead of `cargo check`, you can set
+`"rust-analyzer.check.command": "clippy"`.
+
+{{#include diagnostics_generated.md:2:}}
diff --git a/src/tools/rust-analyzer/docs/book/src/editor_features.md b/src/tools/rust-analyzer/docs/book/src/editor_features.md
new file mode 100644
index 0000000000000..73fe9f49a96e4
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/editor_features.md
@@ -0,0 +1,204 @@
+# Editor Features
+
+
+## VS Code
+
+### Color configurations
+
+It is possible to change the foreground/background color and font
+family/size of inlay hints. Just add this to your `settings.json`:
+
+```json
+{
+  "editor.inlayHints.fontFamily": "Courier New",
+  "editor.inlayHints.fontSize": 11,
+
+  "workbench.colorCustomizations": {
+    // Name of the theme you are currently using
+    "[Default Dark+]": {
+      "editorInlayHint.foreground": "#868686f0",
+      "editorInlayHint.background": "#3d3d3d48",
+
+      // Overrides for specific kinds of inlay hints
+      "editorInlayHint.typeForeground": "#fdb6fdf0",
+      "editorInlayHint.parameterForeground": "#fdb6fdf0",
+    }
+  }
+}
+```
+
+### Semantic style customizations
+
+You can customize the look of different semantic elements in the source
+code. For example, mutable bindings are underlined by default and you
+can override this behavior by adding the following section to your
+`settings.json`:
+
+```json
+{
+  "editor.semanticTokenColorCustomizations": {
+    "rules": {
+      "*.mutable": {
+        "fontStyle": "", // underline is the default
+      },
+    }
+  },
+}
+```
+
+Most themes doesn’t support styling unsafe operations differently yet.
+You can fix this by adding overrides for the rules `operator.unsafe`,
+`function.unsafe`, and `method.unsafe`:
+
+```json
+{
+   "editor.semanticTokenColorCustomizations": {
+         "rules": {
+             "operator.unsafe": "#ff6600",
+             "function.unsafe": "#ff6600",
+             "method.unsafe": "#ff6600"
+         }
+    },
+}
+```
+
+In addition to the top-level rules you can specify overrides for
+specific themes. For example, if you wanted to use a darker text color
+on a specific light theme, you might write:
+
+```json
+{
+   "editor.semanticTokenColorCustomizations": {
+         "rules": {
+             "operator.unsafe": "#ff6600"
+         },
+         "[Ayu Light]": {
+            "rules": {
+               "operator.unsafe": "#572300"
+            }
+         }
+    },
+}
+```
+
+Make sure you include the brackets around the theme name. For example,
+use `"[Ayu Light]"` to customize the theme Ayu Light.
+
+### Special `when` clause context for keybindings.
+
+You may use `inRustProject` context to configure keybindings for rust
+projects only. For example:
+
+```json
+{
+    "key": "ctrl+alt+d",
+    "command": "rust-analyzer.openDocs",
+    "when": "inRustProject"
+}
+```
+
+More about `when` clause contexts
+[here](https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts).
+
+### Setting runnable environment variables
+
+You can use "rust-analyzer.runnables.extraEnv" setting to define
+runnable environment-specific substitution variables. The simplest way
+for all runnables in a bunch:
+
+```json
+"rust-analyzer.runnables.extraEnv": {
+    "RUN_SLOW_TESTS": "1"
+}
+```
+
+Or it is possible to specify vars more granularly:
+
+```json
+"rust-analyzer.runnables.extraEnv": [
+    {
+        // "mask": null, // null mask means that this rule will be applied for all runnables
+        env: {
+                "APP_ID": "1",
+                "APP_DATA": "asdf"
+        }
+    },
+    {
+        "mask": "test_name",
+        "env": {
+                "APP_ID": "2", // overwrites only APP_ID
+        }
+    }
+]
+```
+
+You can use any valid regular expression as a mask. Also note that a
+full runnable name is something like **run bin\_or\_example\_name**,
+**test some::mod::test\_name** or **test-mod some::mod**, so it is
+possible to distinguish binaries, single tests, and test modules with
+this masks: `"^run"`, `"^test "` (the trailing space matters!), and
+`"^test-mod"` respectively.
+
+If needed, you can set different values for different platforms:
+
+```json
+"rust-analyzer.runnables.extraEnv": [
+    {
+        "platform": "win32", // windows only
+        env: {
+                "APP_DATA": "windows specific data"
+        }
+    },
+    {
+        "platform": ["linux"],
+        "env": {
+                "APP_DATA": "linux data",
+        }
+    },
+    { // for all platforms
+        "env": {
+                "APP_COMMON_DATA": "xxx",
+        }
+    }
+]
+```
+
+### Compiler feedback from external commands
+
+Instead of relying on the built-in `cargo check`, you can configure Code
+to run a command in the background and use the `$rustc-watch` problem
+matcher to generate inline error markers from its output.
+
+To do this you need to create a new [VS Code
+Task](https://code.visualstudio.com/docs/editor/tasks) and set
+`"rust-analyzer.checkOnSave": false` in preferences.
+
+For example, if you want to run
+[`cargo watch`](https://crates.io/crates/cargo-watch) instead, you might
+add the following to `.vscode/tasks.json`:
+
+```json
+{
+    "label": "Watch",
+    "group": "build",
+    "type": "shell",
+    "command": "cargo watch",
+    "problemMatcher": "$rustc-watch",
+    "isBackground": true
+}
+```
+
+### Live Share
+
+VS Code Live Share has partial support for rust-analyzer.
+
+Live Share *requires* the official Microsoft build of VS Code, OSS
+builds will not work correctly.
+
+The host’s rust-analyzer instance will be shared with all guests joining
+the session. The guests do not have to have the rust-analyzer extension
+installed for this to work.
+
+If you are joining a Live Share session and *do* have rust-analyzer
+installed locally, commands from the command palette will not work
+correctly since they will attempt to communicate with the local server.
diff --git a/src/tools/rust-analyzer/docs/book/src/features.md b/src/tools/rust-analyzer/docs/book/src/features.md
new file mode 100644
index 0000000000000..0829a0213b76f
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/features.md
@@ -0,0 +1,3 @@
+# Features
+
+{{#include features_generated.md:2:}}
diff --git a/src/tools/rust-analyzer/docs/book/src/installation.md b/src/tools/rust-analyzer/docs/book/src/installation.md
new file mode 100644
index 0000000000000..5b697e9bc33d4
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/installation.md
@@ -0,0 +1,644 @@
+# Installation
+
+In theory, one should be able to just install the [`rust-analyzer`
+binary](#rust-analyzer-language-server-binary) and have it automatically
+work with any editor. We are not there yet, so some editor specific
+setup is required.
+
+Additionally, rust-analyzer needs the sources of the standard library.
+If the source code is not present, rust-analyzer will attempt to install
+it automatically.
+
+To add the sources manually, run the following command:
+
+    $ rustup component add rust-src
+
+## Toolchain
+
+Only the latest stable standard library source is officially supported
+for use with rust-analyzer. If you are using an older toolchain or have
+an override set, rust-analyzer may fail to understand the Rust source.
+You will either need to update your toolchain or use an older version of
+rust-analyzer that is compatible with your toolchain.
+
+If you are using an override in your project, you can still force
+rust-analyzer to use the stable toolchain via the environment variable
+`RUSTUP_TOOLCHAIN`. For example, with VS Code or coc-rust-analyzer:
+
+    { "rust-analyzer.server.extraEnv": { "RUSTUP_TOOLCHAIN": "stable" } }
+
+## VS Code
+
+This is the best supported editor at the moment. The rust-analyzer
+plugin for VS Code is maintained [in
+tree](https://github.com/rust-lang/rust-analyzer/tree/master/editors/code).
+
+You can install the latest release of the plugin from [the
+marketplace](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer).
+
+Note that the plugin may cause conflicts with the [previous official
+Rust
+plugin](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust).
+The latter is no longer maintained and should be uninstalled.
+
+The server binary is stored in the extension install directory, which
+starts with `rust-lang.rust-analyzer-` and is located under:
+
+-   Linux: `~/.vscode/extensions`
+
+-   Linux (Remote, such as WSL): `~/.vscode-server/extensions`
+
+-   macOS: `~/.vscode/extensions`
+
+-   Windows: `%USERPROFILE%\.vscode\extensions`
+
+As an exception, on NixOS, the extension makes a copy of the server and
+stores it under
+`~/.config/Code/User/globalStorage/rust-lang.rust-analyzer`.
+
+Note that we only support the two most recent versions of VS Code.
+
+### Updates
+
+The extension will be updated automatically as new versions become
+available. It will ask your permission to download the matching language
+server version binary if needed.
+
+#### Nightly
+
+We ship nightly releases for VS Code. To help us out by testing the
+newest code, you can enable pre-release versions in the Code extension
+page.
+
+### Manual installation
+
+Alternatively, download a VSIX corresponding to your platform from the
+[releases](https://github.com/rust-lang/rust-analyzer/releases) page.
+
+Install the extension with the `Extensions: Install from VSIX` command
+within VS Code, or from the command line via:
+
+    $ code --install-extension /path/to/rust-analyzer.vsix
+
+If you are running an unsupported platform, you can install
+`rust-analyzer-no-server.vsix` and compile or obtain a server binary.
+Copy the server anywhere, then add the path to your settings.json, for
+example:
+
+    { "rust-analyzer.server.path": "~/.local/bin/rust-analyzer-linux" }
+
+### Building From Source
+
+Both the server and the Code plugin can be installed from source:
+
+    $ git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer
+    $ cargo xtask install
+
+You’ll need Cargo, nodejs (matching a supported version of VS Code) and
+npm for this.
+
+Note that installing via `xtask install` does not work for VS Code
+Remote, instead you’ll need to install the `.vsix` manually.
+
+If you’re not using Code, you can compile and install only the LSP
+server:
+
+    $ cargo xtask install --server
+
+Make sure that `.cargo/bin` is in `$PATH` and precedes paths where
+`rust-analyzer` may also be installed. Specifically, `rustup` includes a
+proxy called `rust-analyzer`, which can cause problems if you’re
+planning to use a source build or even a downloaded binary.
+
+## rust-analyzer Language Server Binary
+
+Other editors generally require the `rust-analyzer` binary to be in
+`$PATH`. You can download pre-built binaries from the
+[releases](https://github.com/rust-lang/rust-analyzer/releases) page.
+You will need to uncompress and rename the binary for your platform,
+e.g. from `rust-analyzer-aarch64-apple-darwin.gz` on Mac OS to
+`rust-analyzer`, make it executable, then move it into a directory in
+your `$PATH`.
+
+On Linux to install the `rust-analyzer` binary into `~/.local/bin`,
+these commands should work:
+
+    $ mkdir -p ~/.local/bin
+    $ curl -L https://github.com/rust-lang/rust-analyzer/releases/latest/download/rust-analyzer-x86_64-unknown-linux-gnu.gz | gunzip -c - > ~/.local/bin/rust-analyzer
+    $ chmod +x ~/.local/bin/rust-analyzer
+
+Make sure that `~/.local/bin` is listed in the `$PATH` variable and use
+the appropriate URL if you’re not on a `x86-64` system.
+
+You don’t have to use `~/.local/bin`, any other path like `~/.cargo/bin`
+or `/usr/local/bin` will work just as well.
+
+Alternatively, you can install it from source using the command below.
+You’ll need the latest stable version of the Rust toolchain.
+
+    $ git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer
+    $ cargo xtask install --server
+
+If your editor can’t find the binary even though the binary is on your
+`$PATH`, the likely explanation is that it doesn’t see the same `$PATH`
+as the shell, see [this
+issue](https://github.com/rust-lang/rust-analyzer/issues/1811). On Unix,
+running the editor from a shell or changing the `.desktop` file to set
+the environment should help.
+
+### rustup
+
+`rust-analyzer` is available in `rustup`:
+
+    $ rustup component add rust-analyzer
+
+### Arch Linux
+
+The `rust-analyzer` binary can be installed from the repos or AUR (Arch
+User Repository):
+
+-   [`rust-analyzer`](https://www.archlinux.org/packages/extra/x86_64/rust-analyzer/)
+    (built from latest tagged source)
+
+-   [`rust-analyzer-git`](https://aur.archlinux.org/packages/rust-analyzer-git)
+    (latest Git version)
+
+Install it with pacman, for example:
+
+    $ pacman -S rust-analyzer
+
+### Gentoo Linux
+
+`rust-analyzer` is installed when the `rust-analyzer` use flag is set for dev-lang/rust or dev-lang/rust-bin. You also need to set the `rust-src` use flag.
+
+### macOS
+
+The `rust-analyzer` binary can be installed via
+[Homebrew](https://brew.sh/).
+
+    $ brew install rust-analyzer
+
+### Windows
+
+It is recommended to install the latest Microsoft Visual C++ Redistributable prior to installation.
+Download links can be found
+[here](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist).
+
+## VS Code or VSCodium in Flatpak
+
+Setting up `rust-analyzer` with a Flatpak version of Code is not trivial
+because of the Flatpak sandbox. While the sandbox can be disabled for
+some directories, `/usr/bin` will always be mounted under
+`/run/host/usr/bin`. This prevents access to the system’s C compiler, a
+system-wide installation of Rust, or any other libraries you might want
+to link to. Some compilers and libraries can be acquired as Flatpak
+SDKs, such as `org.freedesktop.Sdk.Extension.rust-stable` or
+`org.freedesktop.Sdk.Extension.llvm15`.
+
+If you use a Flatpak SDK for Rust, it must be in your `PATH`:
+
+ * install the SDK extensions with `flatpak install org.freedesktop.Sdk.Extension.{llvm15,rust-stable}//23.08`
+ * enable SDK extensions in the editor with the environment variable `FLATPAK_ENABLE_SDK_EXT=llvm15,rust-stable` (this can be done using flatseal or `flatpak override`)
+
+If you want to use Flatpak in combination with `rustup`, the following
+steps might help:
+
+-   both Rust and `rustup` have to be installed using
+    . Distro packages *will not* work.
+
+-   you need to launch Code, open a terminal and run `echo $PATH`
+
+-   using
+    [Flatseal](https://flathub.org/apps/details/com.github.tchx84.Flatseal),
+    you must add an environment variable called `PATH`. Set its value to
+    the output from above, appending `:~/.cargo/bin`, where `~` is the
+    path to your home directory. You must replace `~`, as it won’t be
+    expanded otherwise.
+
+-   while Flatseal is open, you must enable access to "All user files"
+
+A C compiler should already be available via `org.freedesktop.Sdk`. Any
+other tools or libraries you will need to acquire from Flatpak.
+
+## Emacs
+
+Prerequisites: You have installed the [`rust-analyzer`
+binary](#rust-analyzer-language-server-binary).
+
+To use `rust-analyzer`, you need to install and enable one of the two
+popular LSP client implementations for Emacs,
+[Eglot](https://github.com/joaotavora/eglot) or [LSP
+Mode](https://github.com/emacs-lsp/lsp-mode). Both enable
+`rust-analyzer` by default in rust buffers if it is available.
+
+### Eglot
+
+Eglot is the more minimalistic and lightweight LSP client for Emacs,
+integrates well with existing Emacs functionality and is built into
+Emacs starting from release 29.
+
+After installing Eglot, e.g. via `M-x package-install` (not needed from
+Emacs 29), you can enable it via the `M-x eglot` command or load it
+automatically in `rust-mode` via
+
+    (add-hook 'rust-mode-hook 'eglot-ensure)
+
+To enable clippy, you will need to configure the initialization options
+to pass the `check.command` setting.
+
+    (add-to-list 'eglot-server-programs
+                 '((rust-ts-mode rust-mode) .
+                   ("rust-analyzer" :initializationOptions (:check (:command "clippy")))))
+
+For more detailed instructions and options see the [Eglot
+manual](https://joaotavora.github.io/eglot) (also available from Emacs
+via `M-x info`) and the [Eglot
+readme](https://github.com/joaotavora/eglot/blob/master/README.md).
+
+Eglot does not support the rust-analyzer extensions to the
+language-server protocol and does not aim to do so in the future. The
+[eglot-x](https://github.com/nemethf/eglot-x#rust-analyzer-extensions)
+package adds experimental support for those LSP extensions.
+
+### LSP Mode
+
+LSP-mode is the original LSP-client for emacs. Compared to Eglot it has
+a larger codebase and supports more features, like LSP protocol
+extensions. With extension packages like [LSP
+UI](https://github.com/emacs-lsp/lsp-mode) it offers a lot of visual
+eyecandy. Further it integrates well with [DAP
+mode](https://github.com/emacs-lsp/dap-mode) for support of the Debug
+Adapter Protocol.
+
+You can install LSP-mode via `M-x package-install` and then run it via
+the `M-x lsp` command or load it automatically in rust buffers with
+
+    (add-hook 'rust-mode-hook 'lsp-deferred)
+
+For more information on how to set up LSP mode and its extension package
+see the instructions in the [LSP mode
+manual](https://emacs-lsp.github.io/lsp-mode/page/installation). Also
+see the [rust-analyzer
+section](https://emacs-lsp.github.io/lsp-mode/page/lsp-rust-analyzer/)
+for `rust-analyzer` specific options and commands, which you can
+optionally bind to keys.
+
+Note the excellent
+[guide](https://robert.kra.hn/posts/2021-02-07_rust-with-emacs/) from
+[@rksm](https://github.com/rksm) on how to set-up Emacs for Rust
+development with LSP mode and several other packages.
+
+## Vim/Neovim
+
+Prerequisites: You have installed the [`rust-analyzer`
+binary](#rust-analyzer-language-server-binary). Not needed if the
+extension can install/update it on its own, coc-rust-analyzer is one
+example.
+
+There are several LSP client implementations for Vim or Neovim:
+
+### coc-rust-analyzer
+
+1.  Install coc.nvim by following the instructions at
+    [coc.nvim](https://github.com/neoclide/coc.nvim) (Node.js required)
+
+2.  Run `:CocInstall coc-rust-analyzer` to install
+    [coc-rust-analyzer](https://github.com/fannheyward/coc-rust-analyzer),
+    this extension implements *most* of the features supported in the
+    VSCode extension:
+
+    -   automatically install and upgrade stable/nightly releases
+
+    -   same configurations as VSCode extension,
+        `rust-analyzer.server.path`, `rust-analyzer.cargo.features` etc.
+
+    -   same commands too, `rust-analyzer.analyzerStatus`,
+        `rust-analyzer.ssr` etc.
+
+    -   inlay hints for variables and method chaining, *Neovim Only*
+
+Note: for code actions, use `coc-codeaction-cursor` and
+`coc-codeaction-selected`; `coc-codeaction` and `coc-codeaction-line`
+are unlikely to be useful.
+
+### LanguageClient-neovim
+
+1.  Install LanguageClient-neovim by following the instructions
+    [here](https://github.com/autozimu/LanguageClient-neovim)
+
+    -   The GitHub project wiki has extra tips on configuration
+
+2.  Configure by adding this to your Vim/Neovim config file (replacing
+    the existing Rust-specific line if it exists):
+
+        let g:LanguageClient_serverCommands = {
+        \ 'rust': ['rust-analyzer'],
+        \ }
+
+### YouCompleteMe
+
+Install YouCompleteMe by following the instructions
+[here](https://github.com/ycm-core/YouCompleteMe#installation).
+
+rust-analyzer is the default in ycm, it should work out of the box.
+
+### ALE
+
+To use the LSP server in [ale](https://github.com/dense-analysis/ale):
+
+    let g:ale_linters = {'rust': ['analyzer']}
+
+### nvim-lsp
+
+Neovim 0.5 has built-in language server support. For a quick start
+configuration of rust-analyzer, use
+[neovim/nvim-lspconfig](https://github.com/neovim/nvim-lspconfig#rust_analyzer).
+Once `neovim/nvim-lspconfig` is installed, use
+`lua require'lspconfig'.rust_analyzer.setup({})` in your `init.vim`.
+
+You can also pass LSP settings to the server:
+
+    lua << EOF
+    local lspconfig = require'lspconfig'
+
+    local on_attach = function(client)
+        require'completion'.on_attach(client)
+    end
+
+    lspconfig.rust_analyzer.setup({
+        on_attach = on_attach,
+        settings = {
+            ["rust-analyzer"] = {
+                imports = {
+                    granularity = {
+                        group = "module",
+                    },
+                    prefix = "self",
+                },
+                cargo = {
+                    buildScripts = {
+                        enable = true,
+                    },
+                },
+                procMacro = {
+                    enable = true
+                },
+            }
+        }
+    })
+    EOF
+
+If you're running Neovim 0.10 or later, you can enable inlay hints via `on_attach`:
+
+```vim
+lspconfig.rust_analyzer.setup({
+    on_attach = function(client, bufnr)
+        vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })
+    end
+})
+```
+
+Note that the hints are only visible after `rust-analyzer` has finished loading **and** you have to
+edit the file to trigger a re-render.
+
+See  for more tips on
+getting started.
+
+Check out  for a batteries
+included rust-analyzer setup for Neovim.
+
+### vim-lsp
+
+vim-lsp is installed by following [the plugin
+instructions](https://github.com/prabirshrestha/vim-lsp). It can be as
+simple as adding this line to your `.vimrc`:
+
+    Plug 'prabirshrestha/vim-lsp'
+
+Next you need to register the `rust-analyzer` binary. If it is avim.lspvailable
+in `$PATH`, you may want to add this to your `.vimrc`:
+
+    if executable('rust-analyzer')
+      au User lsp_setup call lsp#register_server({
+            \   'name': 'Rust Language Server',
+            \   'cmd': {server_info->['rust-analyzer']},
+            \   'whitelist': ['rust'],
+            \ })
+    endif
+
+There is no dedicated UI for the server configuration, so you would need
+to send any options as a value of the `initialization_options` field, as
+described in the [Configuration](#configuration) section. Here is an
+example of how to enable the proc-macro support:
+
+    if executable('rust-analyzer')
+      au User lsp_setup call lsp#register_server({
+            \   'name': 'Rust Language Server',
+            \   'cmd': {server_info->['rust-analyzer']},
+            \   'whitelist': ['rust'],
+            \   'initialization_options': {
+            \     'cargo': {
+            \       'buildScripts': {
+            \         'enable': v:true,
+            \       },
+            \     },
+            \     'procMacro': {
+            \       'enable': v:true,
+            \     },
+            \   },
+            \ })
+    endif
+
+## Sublime Text
+
+### Sublime Text 4:
+
+-   Follow the instructions in
+    [LSP-rust-analyzer](https://github.com/sublimelsp/LSP-rust-analyzer).
+
+Install
+[LSP-file-watcher-chokidar](https://packagecontrol.io/packages/LSP-file-watcher-chokidar)
+to enable file watching (`workspace/didChangeWatchedFiles`).
+
+### Sublime Text 3:
+
+-   Install the [`rust-analyzer`
+    binary](#rust-analyzer-language-server-binary).
+
+-   Install the [LSP package](https://packagecontrol.io/packages/LSP).
+
+-   From the command palette, run `LSP: Enable Language Server Globally`
+    and select `rust-analyzer`.
+
+If it worked, you should see "rust-analyzer, Line X, Column Y" on the
+left side of the status bar, and after waiting a bit, functionalities
+like tooltips on hovering over variables should become available.
+
+If you get an error saying `No such file or directory: 'rust-analyzer'`,
+see the [`rust-analyzer` binary](#rust-analyzer-language-server-binary)
+section on installing the language server binary.
+
+## GNOME Builder
+
+GNOME Builder 3.37.1 and newer has native `rust-analyzer` support. If
+the LSP binary is not available, GNOME Builder can install it when
+opening a Rust file.
+
+## Eclipse IDE
+
+Support for Rust development in the Eclipse IDE is provided by [Eclipse
+Corrosion](https://github.com/eclipse/corrosion). If available in PATH
+or in some standard location, `rust-analyzer` is detected and powers
+editing of Rust files without further configuration. If `rust-analyzer`
+is not detected, Corrosion will prompt you for configuration of your
+Rust toolchain and language server with a link to the *Window >
+Preferences > Rust* preference page; from here a button allows to
+download and configure `rust-analyzer`, but you can also reference
+another installation. You’ll need to close and reopen all .rs and Cargo
+files, or to restart the IDE, for this change to take effect.
+
+## Kate Text Editor
+
+Support for the language server protocol is built into Kate through the
+LSP plugin, which is included by default. It is preconfigured to use
+rust-analyzer for Rust sources since Kate 21.12.
+
+To change rust-analyzer config options, start from the following example
+and put it into Kate’s "User Server Settings" tab (located under the LSP
+Client settings):
+
+    {
+        "servers": {
+            "rust": {
+                "initializationOptions": {
+                    "cachePriming": {
+                        "enable": false
+                    },
+                    "check": {
+                        "allTargets": false
+                    },
+                    "checkOnSave": false
+                }
+            }
+        }
+    }
+
+Then click on apply, and restart the LSP server for your rust project.
+
+## juCi++
+
+[juCi++](https://gitlab.com/cppit/jucipp) has built-in support for the
+language server protocol, and since version 1.7.0 offers installation of
+both Rust and rust-analyzer when opening a Rust file.
+
+## Kakoune
+
+[Kakoune](https://kakoune.org/) supports LSP with the help of
+[`kak-lsp`](https://github.com/kak-lsp/kak-lsp). Follow the
+[instructions](https://github.com/kak-lsp/kak-lsp#installation) to
+install `kak-lsp`. To configure `kak-lsp`, refer to the [configuration
+section](https://github.com/kak-lsp/kak-lsp#configuring-kak-lsp) which
+is basically about copying the [configuration
+file](https://github.com/kak-lsp/kak-lsp/blob/master/kak-lsp.toml) in
+the right place (latest versions should use `rust-analyzer` by default).
+
+Finally, you need to configure Kakoune to talk to `kak-lsp` (see [Usage
+section](https://github.com/kak-lsp/kak-lsp#usage)). A basic
+configuration will only get you LSP but you can also activate inlay
+diagnostics and auto-formatting on save. The following might help you
+get all of this.
+
+    eval %sh{kak-lsp --kakoune -s $kak_session}  # Not needed if you load it with plug.kak.
+    hook global WinSetOption filetype=rust %{
+        # Enable LSP
+        lsp-enable-window
+
+        # Auto-formatting on save
+        hook window BufWritePre .* lsp-formatting-sync
+
+        # Configure inlay hints (only on save)
+        hook window -group rust-inlay-hints BufWritePost .* rust-analyzer-inlay-hints
+        hook -once -always window WinSetOption filetype=.* %{
+            remove-hooks window rust-inlay-hints
+        }
+    }
+
+## Helix
+
+[Helix](https://docs.helix-editor.com/) supports LSP by default.
+However, it won’t install `rust-analyzer` automatically. You can follow
+instructions for installing [`rust-analyzer`
+binary](#rust-analyzer-language-server-binary).
+
+## Visual Studio 2022
+
+There are multiple rust-analyzer extensions for Visual Studio 2022 on
+Windows:
+
+### rust-analyzer.vs
+
+(License: Creative Commons Attribution-NonCommercial-ShareAlike 4.0
+International)
+
+[Visual Studio
+Marketplace](https://marketplace.visualstudio.com/items?itemName=kitamstudios.RustAnalyzer)
+
+[GitHub](https://github.com/kitamstudios/rust-analyzer/)
+
+Support for Rust development in the Visual Studio IDE is enabled by the
+[rust-analyzer](https://marketplace.visualstudio.com/items?itemName=kitamstudios.RustAnalyzer)
+package. Either click on the download link or install from IDE’s
+extension manager. For now [Visual Studio
+2022](https://visualstudio.microsoft.com/downloads/) is required. All
+editions are supported viz. Community, Professional & Enterprise. The
+package aims to provide 0-friction installation and therefore comes
+loaded with most things required including rust-analyzer binary. If
+anything it needs is missing, appropriate errors / warnings will guide
+the user. E.g. cargo.exe needs to be in path and the package will tell
+you as much. This package is under rapid active development. So if you
+encounter any issues please file it at
+[rust-analyzer.vs](https://github.com/kitamstudios/rust-analyzer/).
+
+### VS\_RustAnalyzer
+
+(License: GPL)
+
+[Visual Studio
+Marketplace](https://marketplace.visualstudio.com/items?itemName=cchharris.vsrustanalyzer)
+
+[GitHub](https://github.com/cchharris/VS-RustAnalyzer)
+
+### SourceGear Rust
+
+(License: closed source)
+
+[Visual Studio
+Marketplace](https://marketplace.visualstudio.com/items?itemName=SourceGear.SourceGearRust)
+
+[GitHub (docs, issues,
+discussions)](https://github.com/sourcegear/rust-vs-extension)
+
+-   Free (no-cost)
+
+-   Supports all editions of Visual Studio 2022 on Windows: Community,
+    Professional, or Enterprise
+
+## Lapce
+
+[Lapce](https://lapce.dev/) has a Rust plugin which you can install
+directly. Unfortunately, it downloads an old version of `rust-analyzer`,
+but you can set the server path under Settings.
+
+## Crates
+
+There is a package named `ra_ap_rust_analyzer` available on
+[crates.io](https://crates.io/crates/ra_ap_rust-analyzer), for someone
+who wants to use it programmatically.
+
+For more details, see [the publish
+workflow](https://github.com/rust-lang/rust-analyzer/blob/master/.github/workflows/autopublish.yaml).
+
+## Zed
+
+[Zed](https://zed.dev) has native `rust-analyzer` support. If the LSP
+binary is not available, Zed can install it when opening a Rust file.
diff --git a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md
new file mode 100644
index 0000000000000..151f8758a176c
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md
@@ -0,0 +1,246 @@
+# Non-Cargo Based Projects
+
+rust-analyzer does not require Cargo. However, if you use some other
+build system, you’ll have to describe the structure of your project for
+rust-analyzer in the `rust-project.json` format:
+
+```typescript
+interface JsonProject {
+    /// Path to the sysroot directory.
+    ///
+    /// The sysroot is where rustc looks for the
+    /// crates that are built-in to rust, such as
+    /// std.
+    ///
+    /// https://doc.rust-lang.org/rustc/command-line-arguments.html#--sysroot-override-the-system-root
+    ///
+    /// To see the current value of sysroot, you
+    /// can query rustc:
+    ///
+    /// ```
+    /// $ rustc --print sysroot
+    /// /Users/yourname/.rustup/toolchains/stable-x86_64-apple-darwin
+    /// ```
+    sysroot?: string;
+    /// Path to the directory with *source code* of
+    /// sysroot crates.
+    ///
+    /// By default, this is `lib/rustlib/src/rust/library`
+    /// relative to the sysroot.
+    ///
+    /// It should point to the directory where std,
+    /// core, and friends can be found:
+    ///
+    /// https://github.com/rust-lang/rust/tree/master/library.
+    ///
+    /// If provided, rust-analyzer automatically adds
+    /// dependencies on sysroot crates. Conversely,
+    /// if you omit this path, you can specify sysroot
+    /// dependencies yourself and, for example, have
+    /// several different "sysroots" in one graph of
+    /// crates.
+    sysroot_src?: string;
+    /// List of groups of common cfg values, to allow
+    /// sharing them between crates.
+    ///
+    /// Maps from group name to its cfgs. Cfg follow
+    /// the same format as `Crate.cfg`.
+    cfg_groups?: { [key: string]: string[]; };
+    /// The set of crates comprising the current
+    /// project. Must include all transitive
+    /// dependencies as well as sysroot crate (libstd,
+    /// libcore and such).
+    crates: Crate[];
+    /// Configuration for CLI commands.
+    ///
+    /// These are used for running and debugging binaries
+    /// and tests without encoding build system-specific
+    /// knowledge into rust-analyzer.
+    ///
+    /// # Example
+    ///
+    /// Below is an example of a test runnable. `{label}` and `{test_id}`
+    /// are explained in `Runnable::args`'s documentation below.
+    ///
+    /// ```json
+    /// {
+    ///     "program": "buck",
+    ///     "args": [
+    ///         "test",
+    ///          "{label}",
+    ///          "--",
+    ///          "{test_id}",
+    ///          "--print-passing-details"
+    ///     ],
+    ///     "cwd": "/home/user/repo-root/",
+    ///     "kind": "testOne"
+    /// }
+    /// ```
+    runnables?: Runnable[];
+}
+
+interface Crate {
+    /// Optional crate name used for display purposes,
+    /// without affecting semantics. See the `deps`
+    /// key for semantically-significant crate names.
+    display_name?: string;
+    /// Path to the root module of the crate.
+    root_module: string;
+    /// Edition of the crate.
+    edition: '2015' | '2018' | '2021' | '2024';
+    /// The version of the crate. Used for calculating
+    /// the correct docs.rs URL.
+    version?: string;
+    /// Dependencies
+    deps: Dep[];
+    /// Should this crate be treated as a member of
+    /// current "workspace".
+    ///
+    /// By default, inferred from the `root_module`
+    /// (members are the crates which reside inside
+    /// the directory opened in the editor).
+    ///
+    /// Set this to `false` for things like standard
+    /// library and 3rd party crates to enable
+    /// performance optimizations (rust-analyzer
+    /// assumes that non-member crates don't change).
+    is_workspace_member?: boolean;
+    /// Optionally specify the (super)set of `.rs`
+    /// files comprising this crate.
+    ///
+    /// By default, rust-analyzer assumes that only
+    /// files under `root_module.parent` can belong
+    /// to a crate. `include_dirs` are included
+    /// recursively, unless a subdirectory is in
+    /// `exclude_dirs`.
+    ///
+    /// Different crates can share the same `source`.
+    ///
+    /// If two crates share an `.rs` file in common,
+    /// they *must* have the same `source`.
+    /// rust-analyzer assumes that files from one
+    /// source can't refer to files in another source.
+    source?: {
+        include_dirs: string[];
+        exclude_dirs: string[];
+    };
+    /// List of cfg groups this crate inherits.
+    ///
+    /// All cfg in these groups will be concatenated to
+    /// `cfg`. It is impossible to replace a value from
+    /// the groups.
+    cfg_groups?: string[];
+    /// The set of cfgs activated for a given crate, like
+    /// `["unix", "feature=\"foo\"", "feature=\"bar\""]`.
+    cfg: string[];
+    /// Target tuple for this Crate.
+    ///
+    /// Used when running `rustc --print cfg`
+    /// to get target-specific cfgs.
+    target?: string;
+    /// Environment variables, used for
+    /// the `env!` macro
+    env: { [key: string]: string; };
+
+    /// Whether the crate is a proc-macro crate.
+    is_proc_macro: boolean;
+    /// For proc-macro crates, path to compiled
+    /// proc-macro (.so file).
+    proc_macro_dylib_path?: string;
+
+    /// Repository, matching the URL that would be used
+    /// in Cargo.toml.
+    repository?: string;
+
+    /// Build-specific data about this crate.
+    build?: BuildInfo;
+}
+
+interface Dep {
+    /// Index of a crate in the `crates` array.
+    crate: number;
+    /// Name as should appear in the (implicit)
+    /// `extern crate name` declaration.
+    name: string;
+}
+
+interface BuildInfo {
+    /// The name associated with this crate.
+    ///
+    /// This is determined by the build system that produced
+    /// the `rust-project.json` in question. For instance, if buck were used,
+    /// the label might be something like `//ide/rust/rust-analyzer:rust-analyzer`.
+    ///
+    /// Do not attempt to parse the contents of this string; it is a build system-specific
+    /// identifier similar to `Crate::display_name`.
+    label: string;
+    /// Path corresponding to the build system-specific file defining the crate.
+    build_file: string;
+    /// The kind of target.
+    ///
+    /// This information is used to determine what sort
+    /// of runnable codelens to provide, if any.
+    target_kind: 'bin' | 'lib' | 'test';
+}
+
+interface Runnable {
+    /// The program invoked by the runnable.
+    ///
+    /// For example, this might be `cargo`, `buck`, or `bazel`.
+    program: string;
+    /// The arguments passed to `program`.
+    args: string[];
+    /// The current working directory of the runnable.
+    cwd: string;
+    /// Used to decide what code lens to offer.
+    ///
+    /// `testOne`: This runnable will be used when the user clicks the 'Run Test'
+    /// CodeLens above a test.
+    ///
+    /// The args for testOne can contain two template strings:
+    /// `{label}` and `{test_id}`. `{label}` will be replaced
+    /// with the `Build::label` and `{test_id}` will be replaced
+    /// with the test name.
+    kind: 'testOne' | string;
+}
+```
+
+This format is provisional and subject to change. Specifically, the
+`roots` setup will be different eventually.
+
+There are three ways to feed `rust-project.json` to rust-analyzer:
+
+-   Place `rust-project.json` file at the root of the project, and
+    rust-analyzer will discover it.
+
+-   Specify
+    `"rust-analyzer.linkedProjects": [ "path/to/rust-project.json" ]` in
+    the settings (and make sure that your LSP client sends settings as a
+    part of initialize request).
+
+-   Specify
+    `"rust-analyzer.linkedProjects": [ { "roots": […​], "crates": […​] }]`
+    inline.
+
+Relative paths are interpreted relative to `rust-project.json` file
+location or (for inline JSON) relative to `rootUri`.
+
+You can set the `RA_LOG` environment variable to `rust_analyzer=info` to
+inspect how rust-analyzer handles config and project loading.
+
+Note that calls to `cargo check` are disabled when using
+`rust-project.json` by default, so compilation errors and warnings will
+no longer be sent to your LSP client. To enable these compilation errors
+you will need to specify explicitly what command rust-analyzer should
+run to perform the checks using the
+`rust-analyzer.check.overrideCommand` configuration. As an example, the
+following configuration explicitly sets `cargo check` as the `check`
+command.
+
+    { "rust-analyzer.check.overrideCommand": ["cargo", "check", "--message-format=json"] }
+
+`check.overrideCommand` requires the command specified to output json
+error messages for rust-analyzer to consume. The `--message-format=json`
+flag does this for `cargo check` so whichever command you use must also
+output errors in this format. See the [Configuration](#_configuration)
+section for more information.
diff --git a/src/tools/rust-analyzer/docs/book/src/privacy.md b/src/tools/rust-analyzer/docs/book/src/privacy.md
new file mode 100644
index 0000000000000..602c68d6f67d8
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/privacy.md
@@ -0,0 +1,15 @@
+# Privacy
+
+The LSP server performs no network access in itself, but runs
+`cargo metadata` which will update or download the crate registry and
+the source code of the project dependencies. If enabled (the default),
+build scripts and procedural macros can do anything.
+
+The Code extension does not access the network.
+
+Any other editor plugins are not under the control of the
+`rust-analyzer` developers. For any privacy concerns, you should check
+with their respective developers.
+
+For `rust-analyzer` developers, `cargo xtask release` uses the GitHub
+API to put together the release notes.
diff --git a/src/tools/rust-analyzer/docs/book/src/security.md b/src/tools/rust-analyzer/docs/book/src/security.md
new file mode 100644
index 0000000000000..1444af03248da
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/security.md
@@ -0,0 +1,19 @@
+# Security
+
+At the moment, rust-analyzer assumes that all code is trusted. Here is a
+**non-exhaustive** list of ways to make rust-analyzer execute arbitrary
+code:
+
+-   proc macros and build scripts are executed by default
+
+-   `.cargo/config` can override `rustc` with an arbitrary executable
+
+-   `rust-toolchain.toml` can override `rustc` with an arbitrary
+    executable
+
+-   VS Code plugin reads configuration from project directory, and that
+    can be used to override paths to various executables, like `rustfmt`
+    or `rust-analyzer` itself.
+
+-   rust-analyzer’s syntax trees library uses a lot of `unsafe` and
+    hasn’t been properly audited for memory safety.
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/docs/book/src/troubleshooting.md b/src/tools/rust-analyzer/docs/book/src/troubleshooting.md
new file mode 100644
index 0000000000000..4092b9de990cc
--- /dev/null
+++ b/src/tools/rust-analyzer/docs/book/src/troubleshooting.md
@@ -0,0 +1,50 @@
+# Troubleshooting
+
+Start with looking at the rust-analyzer version. Try **rust-analyzer:
+Show RA Version** in VS Code (using **Command Palette** feature
+typically activated by Ctrl+Shift+P) or `rust-analyzer --version` in the
+command line. If the date is more than a week ago, it’s better to update
+rust-analyzer version.
+
+The next thing to check would be panic messages in rust-analyzer’s log.
+Log messages are printed to stderr, in VS Code you can see them in the
+`Output > Rust Analyzer Language Server` tab of the panel. To see more
+logs, set the `RA_LOG=info` environment variable, this can be done
+either by setting the environment variable manually or by using
+`rust-analyzer.server.extraEnv`, note that both of these approaches
+require the server to be restarted.
+
+To fully capture LSP messages between the editor and the server, run
+the `rust-analyzer: Toggle LSP Logs` command and check `Output > Rust
+Analyzer Language Server Trace`.
+
+The root cause for many "nothing works" problems is that rust-analyzer
+fails to understand the project structure. To debug that, first note the
+`rust-analyzer` section in the status bar. If it has an error icon and
+red, that’s the problem (hover will have somewhat helpful error
+message). **rust-analyzer: Status** prints dependency information for
+the current file. Finally, `RA_LOG=project_model=debug` enables verbose
+logs during project loading.
+
+If rust-analyzer outright crashes, try running
+`rust-analyzer analysis-stats /path/to/project/directory/` on the
+command line. This command type checks the whole project in batch mode
+bypassing LSP machinery.
+
+When filing issues, it is useful (but not necessary) to try to minimize
+examples. An ideal bug reproduction looks like this:
+
+```shell
+$ git clone https://github.com/username/repo.git && cd repo && git switch --detach commit-hash
+$ rust-analyzer --version
+rust-analyzer dd12184e4 2021-05-08 dev
+$ rust-analyzer analysis-stats .
+💀 💀 💀
+```
+
+It is especially useful when the `repo` doesn’t use external crates or
+the standard library.
+
+If you want to go as far as to modify the source code to debug the
+problem, be sure to take a look at the [dev
+docs](https://github.com/rust-lang/rust-analyzer/tree/master/docs/dev)!
diff --git a/src/tools/rust-analyzer/docs/dev/README.md b/src/tools/rust-analyzer/docs/dev/README.md
index cd0f49174cdc3..c990212d585d2 100644
--- a/src/tools/rust-analyzer/docs/dev/README.md
+++ b/src/tools/rust-analyzer/docs/dev/README.md
@@ -154,19 +154,21 @@ There are also several VS Code commands which might be of interest:
 
 * `rust-analyzer: Status` shows some memory-usage statistics.
 
-* `rust-analyzer: Syntax Tree` shows syntax tree of the current file/selection.
-
 * `rust-analyzer: View Hir` shows the HIR expressions within the function containing the cursor.
 
-  You can hover over syntax nodes in the opened text file to see the appropriate
-  rust code that it refers to and the rust editor will also highlight the proper
-  text range.
+* If `rust-analyzer.showSyntaxTree` is enabled in settings, `Rust Syntax Tree: Focus on Rust Syntax Tree View` shows the syntax tree of the current file.
+
+  You can click on nodes in the rust editor to go to the corresponding syntax node.
+
+  You can click on `Reveal Syntax Element` next to a syntax node to go to the corresponding rust code and highlight the proper text range.
 
   If you trigger Go to Definition in the inspected Rust source file,
-  the syntax tree read-only editor should scroll to and select the
+  the syntax tree view should scroll to and select the
   appropriate syntax node token.
 
-  ![demo](https://user-images.githubusercontent.com/36276403/78225773-6636a480-74d3-11ea-9d9f-1c9d42da03b0.png)
+  You can click on `Copy` next to a syntax node to copy a text representation of the node.
+
+  ![demo](https://github.com/user-attachments/assets/2d20ae87-0abf-495f-bee8-54aa2494a00d)
 
 ## Profiling
 
@@ -267,19 +269,13 @@ Note: we tag releases by dates, releasing a patch release on the same day should
 
 ## Permissions
 
-There are three sets of people with extra permissions:
+There are two sets of people with extra permissions:
 
-* rust-analyzer GitHub organization [**admins**](https://github.com/orgs/rust-analyzer/people?query=role:owner) (which include current t-compiler leads).
-  Admins have full access to the org.
-* [**review**](https://github.com/orgs/rust-analyzer/teams/review) team in the organization.
-  Reviewers have `r+` access to all of organization's repositories and publish rights on crates.io.
-  They also have direct commit access, but all changes should via bors queue.
+* The [rust-lang](https://github.com/rust-lang) team [t-rust-analyzer](https://github.com/rust-lang/team/blob/master/teams/rust-analyzer.toml).
+  This team has write access to the repository and merge queue permissions (note the repo itself is managed by infra admins).
   It's ok to self-approve if you think you know what you are doing!
-  bors should automatically sync the permissions.
   Feel free to request a review or assign any PR to a reviewer with the relevant expertise to bring the work to their attention.
   Don't feel pressured to review assigned PRs though.
-  If you don't feel like reviewing for whatever reason, someone else will pick the review up!
-* [**triage**](https://github.com/orgs/rust-analyzer/teams/triage) team in the organization.
-  This team can label and close issues.
-
-Note that at the time being you need to be a member of the org yourself to view the links.
+  If you don't feel like reviewing for whatever reason, someone else will pick the review up (but please speak up if you don't feel like it)!
+* The [rust-lang](https://github.com/rust-lang) team [t-rust-analyzer-contributors]([https://github.com/orgs/rust-analyzer/teams/triage](https://github.com/rust-lang/team/blob/master/teams/rust-analyzer-contributors.toml)).
+  This team has general triaging permissions allowing to label, close and re-open issues.
diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
index 0e37611a5493e..c7ee4e402363c 100644
--- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
+++ b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
 "))
-    {
-        Directive::Ignore(false)
-    } else {
-        Directive::Deny
+        return [Directive::Deny; N];
     }
+    checks.map(|check| {
+        // Update `can_contain` when changing this
+        if contents.contains(&format!("// ignore-tidy-{check}"))
+            || contents.contains(&format!("# ignore-tidy-{check}"))
+            || contents.contains(&format!("/* ignore-tidy-{check} */"))
+            || contents.contains(&format!(""))
+        {
+            Directive::Ignore(false)
+        } else {
+            Directive::Deny
+        }
+    })
 }
 
 macro_rules! suppressible_tidy_err {
@@ -370,6 +394,7 @@ pub fn check(path: &Path, bad: &mut bool) {
             COLS
         };
 
+        // When you change this, also change the `directive_line_starts` variable below
         let can_contain = contents.contains("// ignore-tidy-")
             || contents.contains("# ignore-tidy-")
             || contents.contains("/* ignore-tidy-")
@@ -385,22 +410,19 @@ pub fn check(path: &Path, bad: &mut bool) {
                 return;
             }
         }
-        let mut skip_cr = contains_ignore_directive(can_contain, &contents, "cr");
-        let mut skip_undocumented_unsafe =
-            contains_ignore_directive(can_contain, &contents, "undocumented-unsafe");
-        let mut skip_tab = contains_ignore_directive(can_contain, &contents, "tab");
-        let mut skip_line_length = contains_ignore_directive(can_contain, &contents, "linelength");
-        let mut skip_file_length = contains_ignore_directive(can_contain, &contents, "filelength");
-        let mut skip_end_whitespace =
-            contains_ignore_directive(can_contain, &contents, "end-whitespace");
-        let mut skip_trailing_newlines =
-            contains_ignore_directive(can_contain, &contents, "trailing-newlines");
-        let mut skip_leading_newlines =
-            contains_ignore_directive(can_contain, &contents, "leading-newlines");
-        let mut skip_copyright = contains_ignore_directive(can_contain, &contents, "copyright");
-        let mut skip_dbg = contains_ignore_directive(can_contain, &contents, "dbg");
-        let mut skip_odd_backticks =
-            contains_ignore_directive(can_contain, &contents, "odd-backticks");
+        let [
+            mut skip_cr,
+            mut skip_undocumented_unsafe,
+            mut skip_tab,
+            mut skip_line_length,
+            mut skip_file_length,
+            mut skip_end_whitespace,
+            mut skip_trailing_newlines,
+            mut skip_leading_newlines,
+            mut skip_copyright,
+            mut skip_dbg,
+            mut skip_odd_backticks,
+        ] = contains_ignore_directives(can_contain, &contents, CONFIGURABLE_CHECKS);
         let mut leading_new_lines = false;
         let mut trailing_new_lines = 0;
         let mut lines = 0;
@@ -474,6 +496,22 @@ pub fn check(path: &Path, bad: &mut bool) {
                 suppressible_tidy_err!(err, skip_cr, "CR character");
             }
             if !is_this_file {
+                let directive_line_starts = ["// ", "# ", "/* ", " $DIR/failed-doctest-test-crate.rs:15:5
+   |
+LL | use test::*;
+   |     ^^^^ use of unresolved module or unlinked crate `test`
+   |
+help: you might be missing a crate named `test`, add it to your project and import it in your code
+   |
+LL + extern crate test;
+   |
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0432`.
+Couldn't compile the test.
+
+failures:
+    $DIR/failed-doctest-test-crate.rs - m (line 14)
+
+test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
+
diff --git a/tests/rustdoc-ui/doctest/failed-doctest-test-crate.edition2024.stdout b/tests/rustdoc-ui/doctest/failed-doctest-test-crate.edition2024.stdout
new file mode 100644
index 0000000000000..80642e93bbde7
--- /dev/null
+++ b/tests/rustdoc-ui/doctest/failed-doctest-test-crate.edition2024.stdout
@@ -0,0 +1,25 @@
+
+running 1 test
+test $DIR/failed-doctest-test-crate.rs - m (line 14) ... FAILED
+
+failures:
+
+---- $DIR/failed-doctest-test-crate.rs - m (line 14) stdout ----
+error[E0432]: unresolved import `test`
+  --> $DIR/failed-doctest-test-crate.rs:15:5
+   |
+LL | use test::*;
+   |     ^^^^ use of unresolved module or unlinked crate `test`
+   |
+   = help: you might be missing a crate named `test`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0432`.
+Couldn't compile the test.
+
+failures:
+    $DIR/failed-doctest-test-crate.rs - m (line 14)
+
+test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
+
diff --git a/tests/rustdoc-ui/doctest/failed-doctest-test-crate.rs b/tests/rustdoc-ui/doctest/failed-doctest-test-crate.rs
new file mode 100644
index 0000000000000..6966d3df11ce7
--- /dev/null
+++ b/tests/rustdoc-ui/doctest/failed-doctest-test-crate.rs
@@ -0,0 +1,17 @@
+// FIXME: if/when the output of the test harness can be tested on its own, this test should be
+// adapted to use that, and that normalize line can go away
+
+//@ revisions: edition2015 edition2024
+//@[edition2015]edition:2015
+//@[edition2024]edition:2024
+//@ compile-flags:--test
+//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR"
+//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
+//@ failure-status: 101
+
+/// 
+///
+/// ```rust
+/// use test::*;
+/// ```
+pub mod m {}
diff --git a/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.rs b/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.rs
index 5c9e2978e48a6..6fddaa49faced 100644
--- a/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.rs
+++ b/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.rs
@@ -1,4 +1,4 @@
-//@ ignore-windows
+//@ ignore-windows different error message
 //@ revisions: edition2015 edition2024
 //@[edition2015]edition:2015
 //@[edition2015]check-fail
diff --git a/tests/rustdoc-ui/extract-doctests.rs b/tests/rustdoc-ui/extract-doctests.rs
new file mode 100644
index 0000000000000..06bd35969d0c4
--- /dev/null
+++ b/tests/rustdoc-ui/extract-doctests.rs
@@ -0,0 +1,15 @@
+// Test to ensure that it generates expected output for `--output-format=doctest` command-line
+// flag.
+
+//@ compile-flags:-Z unstable-options --output-format=doctest
+//@ normalize-stdout: "tests/rustdoc-ui" -> "$$DIR"
+//@ check-pass
+
+//! ```ignore (checking attributes)
+//! let x = 12;
+//! let y = 14;
+//! ```
+//!
+//! ```edition2018,compile_fail
+//! let
+//! ```
diff --git a/tests/rustdoc-ui/extract-doctests.stdout b/tests/rustdoc-ui/extract-doctests.stdout
new file mode 100644
index 0000000000000..fa8604cae948a
--- /dev/null
+++ b/tests/rustdoc-ui/extract-doctests.stdout
@@ -0,0 +1 @@
+{"format_version":1,"doctests":[{"file":"$DIR/extract-doctests.rs","line":8,"doctest_attributes":{"original":"ignore (checking attributes)","should_panic":false,"no_run":false,"ignore":"All","rust":true,"test_harness":false,"compile_fail":false,"standalone_crate":false,"error_codes":[],"edition":null,"added_css_classes":[],"unknown":[]},"original_code":"let x = 12;\nlet y = 14;","doctest_code":"#![allow(unused)]\nfn main() {\nlet x = 12;\nlet y = 14;\n}","name":"$DIR/extract-doctests.rs - (line 8)"},{"file":"$DIR/extract-doctests.rs","line":13,"doctest_attributes":{"original":"edition2018,compile_fail","should_panic":false,"no_run":true,"ignore":"None","rust":true,"test_harness":false,"compile_fail":true,"standalone_crate":false,"error_codes":[],"edition":"2018","added_css_classes":[],"unknown":[]},"original_code":"let","doctest_code":"#![allow(unused)]\nfn main() {\nlet\n}","name":"$DIR/extract-doctests.rs - (line 13)"}]}
\ No newline at end of file
diff --git a/tests/rustdoc-ui/hashtag-doctest.rs b/tests/rustdoc-ui/hashtag-doctest.rs
new file mode 100644
index 0000000000000..5f7456115305f
--- /dev/null
+++ b/tests/rustdoc-ui/hashtag-doctest.rs
@@ -0,0 +1,17 @@
+// This test ensures that `##` are not emitting a warning when generating
+// docs with the 2024 edition (or any edition).
+// Regression test for .
+
+//@ check-pass
+//@revisions: edition2021 edition2024
+//@[edition2021] edition:2021
+//@[edition2024] edition:2024
+
+#![deny(warnings)]
+
+//! Test
+//!
+//! ```
+//! ##[allow(dead_code)]
+//! println!("hello world");
+//! ```
diff --git a/tests/rustdoc-ui/ice-unresolved-import-100241.stderr b/tests/rustdoc-ui/ice-unresolved-import-100241.stderr
index 2eebedba9a5ee..a82847d381c5c 100644
--- a/tests/rustdoc-ui/ice-unresolved-import-100241.stderr
+++ b/tests/rustdoc-ui/ice-unresolved-import-100241.stderr
@@ -2,9 +2,9 @@ error[E0432]: unresolved import `inner`
   --> $DIR/ice-unresolved-import-100241.rs:9:13
    |
 LL |     pub use inner::S;
-   |             ^^^^^ you might be missing crate `inner`
+   |             ^^^^^ use of unresolved module or unlinked crate `inner`
    |
-help: consider importing the `inner` crate
+help: you might be missing a crate named `inner`, add it to your project and import it in your code
    |
 LL + extern crate inner;
    |
diff --git a/tests/rustdoc/intra-doc/auxiliary/issue-103463-aux.rs b/tests/rustdoc-ui/intra-doc/auxiliary/issue-103463-aux.rs
similarity index 100%
rename from tests/rustdoc/intra-doc/auxiliary/issue-103463-aux.rs
rename to tests/rustdoc-ui/intra-doc/auxiliary/issue-103463-aux.rs
diff --git a/tests/rustdoc-ui/intra-doc/disambiguator-mismatch.stderr b/tests/rustdoc-ui/intra-doc/disambiguator-mismatch.stderr
index ef7fec77b1e0d..3bba27e593cfd 100644
--- a/tests/rustdoc-ui/intra-doc/disambiguator-mismatch.stderr
+++ b/tests/rustdoc-ui/intra-doc/disambiguator-mismatch.stderr
@@ -11,8 +11,9 @@ LL | #![deny(rustdoc::broken_intra_doc_links)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: to link to the enum, prefix with `enum@`
    |
-LL | /// Link to [enum@S]
-   |              ~~~~~
+LL - /// Link to [struct@S]
+LL + /// Link to [enum@S]
+   |
 
 error: incompatible link kind for `S`
   --> $DIR/disambiguator-mismatch.rs:27:14
@@ -22,8 +23,9 @@ LL | /// Link to [mod@S]
    |
 help: to link to the enum, prefix with `enum@`
    |
-LL | /// Link to [enum@S]
-   |              ~~~~~
+LL - /// Link to [mod@S]
+LL + /// Link to [enum@S]
+   |
 
 error: incompatible link kind for `S`
   --> $DIR/disambiguator-mismatch.rs:32:14
@@ -33,8 +35,9 @@ LL | /// Link to [union@S]
    |
 help: to link to the enum, prefix with `enum@`
    |
-LL | /// Link to [enum@S]
-   |              ~~~~~
+LL - /// Link to [union@S]
+LL + /// Link to [enum@S]
+   |
 
 error: incompatible link kind for `S`
   --> $DIR/disambiguator-mismatch.rs:37:14
@@ -44,8 +47,9 @@ LL | /// Link to [trait@S]
    |
 help: to link to the enum, prefix with `enum@`
    |
-LL | /// Link to [enum@S]
-   |              ~~~~~
+LL - /// Link to [trait@S]
+LL + /// Link to [enum@S]
+   |
 
 error: incompatible link kind for `T`
   --> $DIR/disambiguator-mismatch.rs:42:14
@@ -55,8 +59,9 @@ LL | /// Link to [struct@T]
    |
 help: to link to the trait, prefix with `trait@`
    |
-LL | /// Link to [trait@T]
-   |              ~~~~~~
+LL - /// Link to [struct@T]
+LL + /// Link to [trait@T]
+   |
 
 error: incompatible link kind for `m`
   --> $DIR/disambiguator-mismatch.rs:47:14
@@ -89,8 +94,9 @@ LL | /// Link to [const@s]
    |
 help: to link to the static, prefix with `static@`
    |
-LL | /// Link to [static@s]
-   |              ~~~~~~~
+LL - /// Link to [const@s]
+LL + /// Link to [static@s]
+   |
 
 error: incompatible link kind for `c`
   --> $DIR/disambiguator-mismatch.rs:63:14
@@ -100,8 +106,9 @@ LL | /// Link to [static@c]
    |
 help: to link to the constant, prefix with `const@`
    |
-LL | /// Link to [const@c]
-   |              ~~~~~~
+LL - /// Link to [static@c]
+LL + /// Link to [const@c]
+   |
 
 error: incompatible link kind for `c`
   --> $DIR/disambiguator-mismatch.rs:68:14
@@ -111,8 +118,9 @@ LL | /// Link to [fn@c]
    |
 help: to link to the constant, prefix with `const@`
    |
-LL | /// Link to [const@c]
-   |              ~~~~~~
+LL - /// Link to [fn@c]
+LL + /// Link to [const@c]
+   |
 
 error: incompatible link kind for `c`
   --> $DIR/disambiguator-mismatch.rs:73:14
@@ -146,8 +154,9 @@ LL | /// Link to [fn@std]
    |
 help: to link to the crate, prefix with `mod@`
    |
-LL | /// Link to [mod@std]
-   |              ~~~~
+LL - /// Link to [fn@std]
+LL + /// Link to [mod@std]
+   |
 
 error: incompatible link kind for `X::y`
   --> $DIR/disambiguator-mismatch.rs:88:14
@@ -157,8 +166,9 @@ LL | /// Link to [method@X::y]
    |
 help: to link to the field, prefix with `field@`
    |
-LL | /// Link to [field@X::y]
-   |              ~~~~~~
+LL - /// Link to [method@X::y]
+LL + /// Link to [field@X::y]
+   |
 
 error: unresolved link to `S::A`
   --> $DIR/disambiguator-mismatch.rs:93:14
@@ -168,8 +178,9 @@ LL | /// Link to [field@S::A]
    |
 help: to link to the variant, prefix with `variant@`
    |
-LL | /// Link to [variant@S::A]
-   |              ~~~~~~~~
+LL - /// Link to [field@S::A]
+LL + /// Link to [variant@S::A]
+   |
 
 error: aborting due to 15 previous errors
 
diff --git a/tests/rustdoc-ui/intra-doc/ice-extern-trait-local-impl-104145.rs b/tests/rustdoc-ui/intra-doc/ice-extern-trait-local-impl-104145.rs
new file mode 100644
index 0000000000000..0e403b21c8a18
--- /dev/null
+++ b/tests/rustdoc-ui/intra-doc/ice-extern-trait-local-impl-104145.rs
@@ -0,0 +1,17 @@
+// https://github.com/rust-lang/rust/issues/104145
+//@ check-pass
+
+// Doc links in `Trait`'s methods are resolved because it has a local impl.
+
+//@ aux-build:issue-103463-aux.rs
+
+extern crate issue_103463_aux;
+use issue_103463_aux::Trait;
+
+pub struct LocalType;
+
+impl Trait for LocalType {
+    fn method() {}
+}
+
+fn main() {}
diff --git a/tests/rustdoc-ui/intra-doc/ice-priv-use-103463.rs b/tests/rustdoc-ui/intra-doc/ice-priv-use-103463.rs
new file mode 100644
index 0000000000000..10894282e55b4
--- /dev/null
+++ b/tests/rustdoc-ui/intra-doc/ice-priv-use-103463.rs
@@ -0,0 +1,11 @@
+// https://github.com/rust-lang/rust/issues/103463
+//@ check-pass
+
+// The `Trait` is not pulled into the crate resulting in doc links in its methods being resolved.
+
+//@ aux-build:issue-103463-aux.rs
+
+extern crate issue_103463_aux;
+use issue_103463_aux::Trait;
+
+fn main() {}
diff --git a/tests/rustdoc-ui/intra-doc/incompatible-primitive-disambiguator.stderr b/tests/rustdoc-ui/intra-doc/incompatible-primitive-disambiguator.stderr
index b952517022b75..2818b6b4bbabd 100644
--- a/tests/rustdoc-ui/intra-doc/incompatible-primitive-disambiguator.stderr
+++ b/tests/rustdoc-ui/intra-doc/incompatible-primitive-disambiguator.stderr
@@ -11,8 +11,9 @@ LL | #![deny(rustdoc::broken_intra_doc_links)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: to link to the associated constant, prefix with `const@`
    |
-LL | //! [const@u8::MIN]
-   |      ~~~~~~
+LL - //! [static@u8::MIN]
+LL + //! [const@u8::MIN]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/rustdoc-ui/intra-doc/prim-conflict.stderr b/tests/rustdoc-ui/intra-doc/prim-conflict.stderr
index 03ce8f15f0a5e..c50f6bb9b8753 100644
--- a/tests/rustdoc-ui/intra-doc/prim-conflict.stderr
+++ b/tests/rustdoc-ui/intra-doc/prim-conflict.stderr
@@ -26,12 +26,14 @@ LL | /// [type@char]
    |
 help: to link to the module, prefix with `mod@`
    |
-LL | /// [mod@char]
-   |      ~~~~
+LL - /// [type@char]
+LL + /// [mod@char]
+   |
 help: to link to the primitive type, prefix with `prim@`
    |
-LL | /// [prim@char]
-   |      ~~~~~
+LL - /// [type@char]
+LL + /// [prim@char]
+   |
 
 error: incompatible link kind for `char`
   --> $DIR/prim-conflict.rs:19:6
@@ -41,8 +43,9 @@ LL | /// [struct@char]
    |
 help: to link to the module, prefix with `mod@`
    |
-LL | /// [mod@char]
-   |      ~~~~
+LL - /// [struct@char]
+LL + /// [mod@char]
+   |
 
 error: incompatible link kind for `char`
   --> $DIR/prim-conflict.rs:26:10
@@ -52,8 +55,9 @@ LL |     //! [struct@char]
    |
 help: to link to the primitive type, prefix with `prim@`
    |
-LL |     //! [prim@char]
-   |          ~~~~~
+LL -     //! [struct@char]
+LL +     //! [prim@char]
+   |
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/rustdoc-ui/intra-doc/unresolved-import-recovery.stderr b/tests/rustdoc-ui/intra-doc/unresolved-import-recovery.stderr
index e68943192130d..dcdd230c25a1c 100644
--- a/tests/rustdoc-ui/intra-doc/unresolved-import-recovery.stderr
+++ b/tests/rustdoc-ui/intra-doc/unresolved-import-recovery.stderr
@@ -1,10 +1,10 @@
-error[E0433]: failed to resolve: you might be missing crate `unresolved_crate`
+error[E0433]: failed to resolve: use of unresolved module or unlinked crate `unresolved_crate`
   --> $DIR/unresolved-import-recovery.rs:3:5
    |
 LL | use unresolved_crate::module::Name;
-   |     ^^^^^^^^^^^^^^^^ you might be missing crate `unresolved_crate`
+   |     ^^^^^^^^^^^^^^^^ use of unresolved module or unlinked crate `unresolved_crate`
    |
-help: consider importing the `unresolved_crate` crate
+help: you might be missing a crate named `unresolved_crate`, add it to your project and import it in your code
    |
 LL + extern crate unresolved_crate;
    |
diff --git a/tests/rustdoc-ui/intra-doc/value-ctor.stderr b/tests/rustdoc-ui/intra-doc/value-ctor.stderr
index 8d2a6649f4c01..cfc2bb67396c8 100644
--- a/tests/rustdoc-ui/intra-doc/value-ctor.stderr
+++ b/tests/rustdoc-ui/intra-doc/value-ctor.stderr
@@ -11,8 +11,9 @@ LL | #![deny(rustdoc::broken_intra_doc_links)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: to link to the variant, prefix with `variant@`
    |
-LL | /// [variant@Foo::X]
-   |      ~~~~~~~~
+LL - /// [value@Foo::X]
+LL + /// [variant@Foo::X]
+   |
 
 error: unresolved link to `MyStruct`
   --> $DIR/value-ctor.rs:10:11
@@ -22,8 +23,9 @@ LL | /// [tst][value@MyStruct]
    |
 help: to link to the struct, prefix with `struct@`
    |
-LL | /// [tst][struct@MyStruct]
-   |           ~~~~~~~
+LL - /// [tst][value@MyStruct]
+LL + /// [tst][struct@MyStruct]
+   |
 
 error: unresolved link to `Internals`
   --> $DIR/value-ctor.rs:20:15
@@ -33,8 +35,9 @@ LL | /// while [b][value@Internals] fails.
    |
 help: to link to the struct, prefix with `struct@`
    |
-LL | /// while [b][struct@Internals] fails.
-   |               ~~~~~~~
+LL - /// while [b][value@Internals] fails.
+LL + /// while [b][struct@Internals] fails.
+   |
 
 error: incompatible link kind for `Internals`
   --> $DIR/value-ctor.rs:22:15
@@ -44,8 +47,9 @@ LL | /// while [d][variant@Internals] fails.
    |
 help: to link to the struct, prefix with `struct@`
    |
-LL | /// while [d][struct@Internals] fails.
-   |               ~~~~~~~
+LL - /// while [d][variant@Internals] fails.
+LL + /// while [d][struct@Internals] fails.
+   |
 
 error: unresolved link to `Internals2`
   --> $DIR/value-ctor.rs:34:15
@@ -55,8 +59,9 @@ LL | /// while [b][value@Internals2] fails.
    |
 help: to link to the enum, prefix with `enum@`
    |
-LL | /// while [b][enum@Internals2] fails.
-   |               ~~~~~
+LL - /// while [b][value@Internals2] fails.
+LL + /// while [b][enum@Internals2] fails.
+   |
 
 error: aborting due to 5 previous errors
 
diff --git a/tests/rustdoc-ui/intra-doc/weird-syntax.stderr b/tests/rustdoc-ui/intra-doc/weird-syntax.stderr
index 17bcbc783fd9e..1381c1b31ebbf 100644
--- a/tests/rustdoc-ui/intra-doc/weird-syntax.stderr
+++ b/tests/rustdoc-ui/intra-doc/weird-syntax.stderr
@@ -11,8 +11,9 @@ LL | #![deny(rustdoc::broken_intra_doc_links)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: to link to the trait, prefix with `trait@`
    |
-LL | /// [`trait@Clone`]
-   |       ~~~~~~
+LL - /// [`struct@Clone`]
+LL + /// [`trait@Clone`]
+   |
 
 error: incompatible link kind for `Clone`
   --> $DIR/weird-syntax.rs:21:9
@@ -22,8 +23,9 @@ LL | /// [```struct@Clone```]
    |
 help: to link to the trait, prefix with `trait@`
    |
-LL | /// [```trait@Clone```]
-   |         ~~~~~~
+LL - /// [```struct@Clone```]
+LL + /// [```trait@Clone```]
+   |
 
 error: incompatible link kind for `Clone`
   --> $DIR/weird-syntax.rs:24:11
@@ -33,8 +35,9 @@ LL | /// [  `  struct@Clone  `  ]
    |
 help: to link to the trait, prefix with `trait@`
    |
-LL | /// [  `  trait@Clone  `  ]
-   |           ~~~~~~
+LL - /// [  `  struct@Clone  `  ]
+LL + /// [  `  trait@Clone  `  ]
+   |
 
 error: unresolved link to `Clone`
   --> $DIR/weird-syntax.rs:27:9
@@ -104,8 +107,9 @@ LL | /// [x][ struct@Clone]
    |
 help: to link to the trait, prefix with `trait@`
    |
-LL | /// [x][ trait@Clone]
-   |          ~~~~~~
+LL - /// [x][ struct@Clone]
+LL + /// [x][ trait@Clone]
+   |
 
 error: incompatible link kind for `Clone`
   --> $DIR/weird-syntax.rs:65:9
@@ -115,8 +119,9 @@ LL | /// [x][struct@Clone ]
    |
 help: to link to the trait, prefix with `trait@`
    |
-LL | /// [x][trait@Clone ]
-   |         ~~~~~~
+LL - /// [x][struct@Clone ]
+LL + /// [x][trait@Clone ]
+   |
 
 error: unresolved link to `Clone`
   --> $DIR/weird-syntax.rs:74:9
@@ -158,8 +163,9 @@ LL | /// [w]( struct@Clone)
    |
 help: to link to the trait, prefix with `trait@`
    |
-LL | /// [w]( trait@Clone)
-   |          ~~~~~~
+LL - /// [w]( struct@Clone)
+LL + /// [w]( trait@Clone)
+   |
 
 error: incompatible link kind for `Clone`
   --> $DIR/weird-syntax.rs:94:9
@@ -169,8 +175,9 @@ LL | /// [w](struct@Clone )
    |
 help: to link to the trait, prefix with `trait@`
    |
-LL | /// [w](trait@Clone )
-   |         ~~~~~~
+LL - /// [w](struct@Clone )
+LL + /// [w](trait@Clone )
+   |
 
 error: unresolved link to `Clone`
   --> $DIR/weird-syntax.rs:97:9
diff --git a/tests/rustdoc-ui/invalid_const_in_lifetime_position.rs b/tests/rustdoc-ui/invalid_const_in_lifetime_position.rs
index 07fc239a8f86b..427c84679ba1c 100644
--- a/tests/rustdoc-ui/invalid_const_in_lifetime_position.rs
+++ b/tests/rustdoc-ui/invalid_const_in_lifetime_position.rs
@@ -8,4 +8,4 @@ fn f<'a>(arg : Box = &'a ()>>) {}
 //~| ERROR associated type takes 0 generic arguments but 1 generic argument
 //~| ERROR associated type takes 1 lifetime argument but 0 lifetime arguments
 //~| ERROR associated type takes 0 generic arguments but 1 generic argument
-//~| ERROR trait `X` cannot be made into an object
+//~| ERROR trait `X` is not dyn compatible
diff --git a/tests/rustdoc-ui/invalid_const_in_lifetime_position.stderr b/tests/rustdoc-ui/invalid_const_in_lifetime_position.stderr
index 0c3826c566558..2c25589a55314 100644
--- a/tests/rustdoc-ui/invalid_const_in_lifetime_position.stderr
+++ b/tests/rustdoc-ui/invalid_const_in_lifetime_position.stderr
@@ -92,17 +92,18 @@ LL |     type Y<'a>;
    |          ^
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
-error[E0038]: the trait `X` cannot be made into an object
+error[E0038]: the trait `X` is not dyn compatible
   --> $DIR/invalid_const_in_lifetime_position.rs:4:20
    |
 LL | fn f<'a>(arg : Box = &'a ()>>) {}
-   |                    ^^^^^^^^^^^^^^^^^^^^ `X` cannot be made into an object
+   |                    ^^^^^^^^^^^^^^^^^^^^ `X` is not dyn compatible
    |
-note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit 
+note: for a trait to be dyn compatible it needs to allow building a vtable
+      for more information, visit 
   --> $DIR/invalid_const_in_lifetime_position.rs:2:10
    |
 LL | trait X {
-   |       - this trait cannot be made into an object...
+   |       - this trait is not dyn compatible...
 LL |     type Y<'a>;
    |          ^ ...because it contains the generic associated type `Y`
    = help: consider moving `Y` to another trait
diff --git a/tests/rustdoc-ui/issues/ice-generic-type-alias-105742.rs b/tests/rustdoc-ui/issues/ice-generic-type-alias-105742.rs
index 027574923c7ad..89b55beaea185 100644
--- a/tests/rustdoc-ui/issues/ice-generic-type-alias-105742.rs
+++ b/tests/rustdoc-ui/issues/ice-generic-type-alias-105742.rs
@@ -5,8 +5,8 @@ use std::ops::Index;
 pub fn next<'a, T>(s: &'a mut dyn SVec) {
     //~^ expected 1 lifetime argument
     //~| expected 1 generic argument
-    //~| the trait `SVec` cannot be made into an object
-    //~| `SVec` cannot be made into an object
+    //~| the trait `SVec` is not dyn compatible
+    //~| `SVec` is not dyn compatible
     //~| missing generics for associated type `SVec::Item`
     //~| missing generics for associated type `SVec::Item`
     let _ = s;
diff --git a/tests/rustdoc-ui/issues/ice-generic-type-alias-105742.stderr b/tests/rustdoc-ui/issues/ice-generic-type-alias-105742.stderr
index f9080bf07853f..e4a846554863c 100644
--- a/tests/rustdoc-ui/issues/ice-generic-type-alias-105742.stderr
+++ b/tests/rustdoc-ui/issues/ice-generic-type-alias-105742.stderr
@@ -294,19 +294,20 @@ help: add missing generic argument
 LL |     Output = ::Item> as SVec>::Item,
    |                                                  +++
 
-error[E0038]: the trait `SVec` cannot be made into an object
-  --> $DIR/ice-generic-type-alias-105742.rs:5:31
+error[E0038]: the trait `SVec` is not dyn compatible
+  --> $DIR/ice-generic-type-alias-105742.rs:5:35
    |
 LL | pub fn next<'a, T>(s: &'a mut dyn SVec) {
-   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `SVec` cannot be made into an object
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^ `SVec` is not dyn compatible
    |
-note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit 
+note: for a trait to be dyn compatible it needs to allow building a vtable
+      for more information, visit 
   --> $DIR/ice-generic-type-alias-105742.rs:15:17
    |
 LL |    pub trait SVec: Index<
    |  ____________----__^
    | |            |
-   | |            this trait cannot be made into an object...
+   | |            this trait is not dyn compatible...
 LL | |      ::Item,
 ...  |
 LL | |/     Output = ::Item,
@@ -318,8 +319,9 @@ LL | |  > {
    | |__^ ...because it uses `Self` as a type parameter
 help: consider using an opaque type instead
    |
-LL | pub fn next<'a, T>(s: &'a mut impl SVec) {
-   |                               ~~~~
+LL - pub fn next<'a, T>(s: &'a mut dyn SVec) {
+LL + pub fn next<'a, T>(s: &'a mut impl SVec) {
+   |
 
 error[E0107]: missing generics for associated type `SVec::Item`
   --> $DIR/ice-generic-type-alias-105742.rs:16:21
diff --git a/tests/rustdoc-ui/issues/ice-typeof-102986.stderr b/tests/rustdoc-ui/issues/ice-typeof-102986.stderr
index 20dbb2661bc2e..02e257a9163c1 100644
--- a/tests/rustdoc-ui/issues/ice-typeof-102986.stderr
+++ b/tests/rustdoc-ui/issues/ice-typeof-102986.stderr
@@ -6,8 +6,9 @@ LL |     y: (typeof("hey"),),
    |
 help: consider replacing `typeof(...)` with an actual type
    |
-LL |     y: (&str,),
-   |         ~~~~
+LL -     y: (typeof("hey"),),
+LL +     y: (&str,),
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/rustdoc-ui/issues/issue-120444-1.stderr b/tests/rustdoc-ui/issues/issue-120444-1.stderr
index 7bc56b4263f26..aabdc91df2bdc 100644
--- a/tests/rustdoc-ui/issues/issue-120444-1.stderr
+++ b/tests/rustdoc-ui/issues/issue-120444-1.stderr
@@ -15,8 +15,9 @@ LL | #![deny(rustdoc::redundant_explicit_links)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: remove explicit link target
    |
-LL | /// [`Vfs`]
-   |     ~~~~~~~
+LL - /// [`Vfs`][crate::Vfs]
+LL + /// [`Vfs`]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/rustdoc-ui/issues/issue-120444-2.stderr b/tests/rustdoc-ui/issues/issue-120444-2.stderr
index 310bf08e2b525..ee160736b4d26 100644
--- a/tests/rustdoc-ui/issues/issue-120444-2.stderr
+++ b/tests/rustdoc-ui/issues/issue-120444-2.stderr
@@ -15,8 +15,9 @@ LL | #![deny(rustdoc::redundant_explicit_links)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: remove explicit link target
    |
-LL | /// [`Vfs`]
-   |     ~~~~~~~
+LL - /// [`Vfs`][crate::Vfs]
+LL + /// [`Vfs`]
+   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/rustdoc-ui/issues/issue-61732.rs b/tests/rustdoc-ui/issues/issue-61732.rs
index 3969ab92c32ee..d5d9ad5e4637d 100644
--- a/tests/rustdoc-ui/issues/issue-61732.rs
+++ b/tests/rustdoc-ui/issues/issue-61732.rs
@@ -1,4 +1,4 @@
 // This previously triggered an ICE.
 
 pub(in crate::r#mod) fn main() {}
-//~^ ERROR failed to resolve: you might be missing crate `r#mod`
+//~^ ERROR failed to resolve: use of unresolved module or unlinked crate `r#mod`
diff --git a/tests/rustdoc-ui/issues/issue-61732.stderr b/tests/rustdoc-ui/issues/issue-61732.stderr
index 0aa7d558c307d..c4e6997ab74d4 100644
--- a/tests/rustdoc-ui/issues/issue-61732.stderr
+++ b/tests/rustdoc-ui/issues/issue-61732.stderr
@@ -1,10 +1,10 @@
-error[E0433]: failed to resolve: you might be missing crate `r#mod`
+error[E0433]: failed to resolve: use of unresolved module or unlinked crate `r#mod`
   --> $DIR/issue-61732.rs:3:15
    |
 LL | pub(in crate::r#mod) fn main() {}
-   |               ^^^^^ you might be missing crate `r#mod`
+   |               ^^^^^ use of unresolved module or unlinked crate `r#mod`
    |
-help: consider importing the `r#mod` crate
+help: you might be missing a crate named `r#mod`, add it to your project and import it in your code
    |
 LL + extern crate r#mod;
    |
diff --git a/tests/rustdoc-ui/lints/redundant_explicit_links.stderr b/tests/rustdoc-ui/lints/redundant_explicit_links.stderr
index 34ec9be6646c8..f90c41af9f1ac 100644
--- a/tests/rustdoc-ui/lints/redundant_explicit_links.stderr
+++ b/tests/rustdoc-ui/lints/redundant_explicit_links.stderr
@@ -15,8 +15,9 @@ LL | #![deny(rustdoc::redundant_explicit_links)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 help: remove explicit link target
    |
-LL | /// [dummy_target]
-   |     ~~~~~~~~~~~~~~
+LL - /// [dummy_target](dummy_target)
+LL + /// [dummy_target]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:9:22
@@ -30,8 +31,9 @@ LL | /// [`dummy_target`](dummy_target)
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`dummy_target`]
-   |     ~~~~~~~~~~~~~~~~
+LL - /// [`dummy_target`](dummy_target)
+LL + /// [`dummy_target`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:12:11
@@ -45,8 +47,9 @@ LL | /// [Vec](Vec)
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [Vec]
-   |     ~~~~~
+LL - /// [Vec](Vec)
+LL + /// [Vec]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:14:13
@@ -60,8 +63,9 @@ LL | /// [`Vec`](Vec)
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`Vec`]
-   |     ~~~~~~~
+LL - /// [`Vec`](Vec)
+LL + /// [`Vec`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:16:11
@@ -75,8 +79,9 @@ LL | /// [Vec](std::vec::Vec)
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [Vec]
-   |     ~~~~~
+LL - /// [Vec](std::vec::Vec)
+LL + /// [Vec]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:18:13
@@ -90,8 +95,9 @@ LL | /// [`Vec`](std::vec::Vec)
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`Vec`]
-   |     ~~~~~~~
+LL - /// [`Vec`](std::vec::Vec)
+LL + /// [`Vec`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:20:21
@@ -105,8 +111,9 @@ LL | /// [std::vec::Vec](Vec)
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [std::vec::Vec]
-   |     ~~~~~~~~~~~~~~~
+LL - /// [std::vec::Vec](Vec)
+LL + /// [std::vec::Vec]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:22:23
@@ -120,8 +127,9 @@ LL | /// [`std::vec::Vec`](Vec)
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`std::vec::Vec`]
-   |     ~~~~~~~~~~~~~~~~~
+LL - /// [`std::vec::Vec`](Vec)
+LL + /// [`std::vec::Vec`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:24:21
@@ -135,8 +143,9 @@ LL | /// [std::vec::Vec](std::vec::Vec)
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [std::vec::Vec]
-   |     ~~~~~~~~~~~~~~~
+LL - /// [std::vec::Vec](std::vec::Vec)
+LL + /// [std::vec::Vec]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:26:23
@@ -150,8 +159,9 @@ LL | /// [`std::vec::Vec`](std::vec::Vec)
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`std::vec::Vec`]
-   |     ~~~~~~~~~~~~~~~~~
+LL - /// [`std::vec::Vec`](std::vec::Vec)
+LL + /// [`std::vec::Vec`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:29:13
@@ -165,8 +175,9 @@ LL | /// [usize](usize)
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [usize]
-   |     ~~~~~~~
+LL - /// [usize](usize)
+LL + /// [usize]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:31:15
@@ -180,8 +191,9 @@ LL | /// [`usize`](usize)
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`usize`]
-   |     ~~~~~~~~~
+LL - /// [`usize`](usize)
+LL + /// [`usize`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:33:13
@@ -195,8 +207,9 @@ LL | /// [usize](std::primitive::usize)
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [usize]
-   |     ~~~~~~~
+LL - /// [usize](std::primitive::usize)
+LL + /// [usize]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:35:15
@@ -210,8 +223,9 @@ LL | /// [`usize`](std::primitive::usize)
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`usize`]
-   |     ~~~~~~~~~
+LL - /// [`usize`](std::primitive::usize)
+LL + /// [`usize`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:37:29
@@ -225,8 +239,9 @@ LL | /// [std::primitive::usize](usize)
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [std::primitive::usize]
-   |     ~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// [std::primitive::usize](usize)
+LL + /// [std::primitive::usize]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:39:31
@@ -240,8 +255,9 @@ LL | /// [`std::primitive::usize`](usize)
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`std::primitive::usize`]
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// [`std::primitive::usize`](usize)
+LL + /// [`std::primitive::usize`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:41:29
@@ -255,8 +271,9 @@ LL | /// [std::primitive::usize](std::primitive::usize)
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [std::primitive::usize]
-   |     ~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// [std::primitive::usize](std::primitive::usize)
+LL + /// [std::primitive::usize]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:43:31
@@ -270,8 +287,9 @@ LL | /// [`std::primitive::usize`](std::primitive::usize)
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`std::primitive::usize`]
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// [`std::primitive::usize`](std::primitive::usize)
+LL + /// [`std::primitive::usize`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:46:20
@@ -285,8 +303,9 @@ LL | /// [dummy_target](dummy_target) TEXT
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [dummy_target] TEXT
-   |     ~~~~~~~~~~~~~~
+LL - /// [dummy_target](dummy_target) TEXT
+LL + /// [dummy_target] TEXT
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:48:22
@@ -300,8 +319,9 @@ LL | /// [`dummy_target`](dummy_target) TEXT
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`dummy_target`] TEXT
-   |     ~~~~~~~~~~~~~~~~
+LL - /// [`dummy_target`](dummy_target) TEXT
+LL + /// [`dummy_target`] TEXT
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:56:20
@@ -315,8 +335,9 @@ LL | /// [dummy_target][dummy_target]
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [dummy_target]
-   |     ~~~~~~~~~~~~~~
+LL - /// [dummy_target][dummy_target]
+LL + /// [dummy_target]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:58:22
@@ -330,8 +351,9 @@ LL | /// [`dummy_target`][dummy_target]
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`dummy_target`]
-   |     ~~~~~~~~~~~~~~~~
+LL - /// [`dummy_target`][dummy_target]
+LL + /// [`dummy_target`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:61:11
@@ -345,8 +367,9 @@ LL | /// [Vec][Vec]
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [Vec]
-   |     ~~~~~
+LL - /// [Vec][Vec]
+LL + /// [Vec]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:63:13
@@ -360,8 +383,9 @@ LL | /// [`Vec`][Vec]
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`Vec`]
-   |     ~~~~~~~
+LL - /// [`Vec`][Vec]
+LL + /// [`Vec`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:65:11
@@ -375,8 +399,9 @@ LL | /// [Vec][std::vec::Vec]
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [Vec]
-   |     ~~~~~
+LL - /// [Vec][std::vec::Vec]
+LL + /// [Vec]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:67:13
@@ -390,8 +415,9 @@ LL | /// [`Vec`][std::vec::Vec]
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`Vec`]
-   |     ~~~~~~~
+LL - /// [`Vec`][std::vec::Vec]
+LL + /// [`Vec`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:69:21
@@ -405,8 +431,9 @@ LL | /// [std::vec::Vec][Vec]
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [std::vec::Vec]
-   |     ~~~~~~~~~~~~~~~
+LL - /// [std::vec::Vec][Vec]
+LL + /// [std::vec::Vec]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:71:23
@@ -420,8 +447,9 @@ LL | /// [`std::vec::Vec`][Vec]
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`std::vec::Vec`]
-   |     ~~~~~~~~~~~~~~~~~
+LL - /// [`std::vec::Vec`][Vec]
+LL + /// [`std::vec::Vec`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:73:21
@@ -435,8 +463,9 @@ LL | /// [std::vec::Vec][std::vec::Vec]
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [std::vec::Vec]
-   |     ~~~~~~~~~~~~~~~
+LL - /// [std::vec::Vec][std::vec::Vec]
+LL + /// [std::vec::Vec]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:75:23
@@ -450,8 +479,9 @@ LL | /// [`std::vec::Vec`][std::vec::Vec]
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`std::vec::Vec`]
-   |     ~~~~~~~~~~~~~~~~~
+LL - /// [`std::vec::Vec`][std::vec::Vec]
+LL + /// [`std::vec::Vec`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:78:13
@@ -465,8 +495,9 @@ LL | /// [usize][usize]
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [usize]
-   |     ~~~~~~~
+LL - /// [usize][usize]
+LL + /// [usize]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:80:15
@@ -480,8 +511,9 @@ LL | /// [`usize`][usize]
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`usize`]
-   |     ~~~~~~~~~
+LL - /// [`usize`][usize]
+LL + /// [`usize`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:82:13
@@ -495,8 +527,9 @@ LL | /// [usize][std::primitive::usize]
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [usize]
-   |     ~~~~~~~
+LL - /// [usize][std::primitive::usize]
+LL + /// [usize]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:84:15
@@ -510,8 +543,9 @@ LL | /// [`usize`][std::primitive::usize]
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`usize`]
-   |     ~~~~~~~~~
+LL - /// [`usize`][std::primitive::usize]
+LL + /// [`usize`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:86:29
@@ -525,8 +559,9 @@ LL | /// [std::primitive::usize][usize]
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [std::primitive::usize]
-   |     ~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// [std::primitive::usize][usize]
+LL + /// [std::primitive::usize]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:88:31
@@ -540,8 +575,9 @@ LL | /// [`std::primitive::usize`][usize]
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`std::primitive::usize`]
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// [`std::primitive::usize`][usize]
+LL + /// [`std::primitive::usize`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:90:29
@@ -555,8 +591,9 @@ LL | /// [std::primitive::usize][std::primitive::usize]
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [std::primitive::usize]
-   |     ~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// [std::primitive::usize][std::primitive::usize]
+LL + /// [std::primitive::usize]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:92:31
@@ -570,8 +607,9 @@ LL | /// [`std::primitive::usize`][std::primitive::usize]
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`std::primitive::usize`]
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// [`std::primitive::usize`][std::primitive::usize]
+LL + /// [`std::primitive::usize`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:95:20
@@ -585,8 +623,9 @@ LL | /// [dummy_target][dummy_target] TEXT
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [dummy_target] TEXT
-   |     ~~~~~~~~~~~~~~
+LL - /// [dummy_target][dummy_target] TEXT
+LL + /// [dummy_target] TEXT
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:97:22
@@ -600,8 +639,9 @@ LL | /// [`dummy_target`][dummy_target] TEXT
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`dummy_target`] TEXT
-   |     ~~~~~~~~~~~~~~~~
+LL - /// [`dummy_target`][dummy_target] TEXT
+LL + /// [`dummy_target`] TEXT
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:105:20
@@ -620,8 +660,9 @@ LL | /// [dummy_target]: dummy_target
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [dummy_target]
-   |     ~~~~~~~~~~~~~~
+LL - /// [dummy_target][dummy_target]
+LL + /// [dummy_target]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:107:22
@@ -640,8 +681,9 @@ LL | /// [dummy_target]: dummy_target
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`dummy_target`]
-   |     ~~~~~~~~~~~~~~~~
+LL - /// [`dummy_target`][dummy_target]
+LL + /// [`dummy_target`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:110:11
@@ -660,8 +702,9 @@ LL | /// [Vec]: Vec
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [Vec]
-   |     ~~~~~
+LL - /// [Vec][Vec]
+LL + /// [Vec]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:112:13
@@ -680,8 +723,9 @@ LL | /// [Vec]: Vec
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`Vec`]
-   |     ~~~~~~~
+LL - /// [`Vec`][Vec]
+LL + /// [`Vec`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:114:11
@@ -700,8 +744,9 @@ LL | /// [std::vec::Vec]: Vec
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [Vec]
-   |     ~~~~~
+LL - /// [Vec][std::vec::Vec]
+LL + /// [Vec]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:116:13
@@ -720,8 +765,9 @@ LL | /// [std::vec::Vec]: Vec
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`Vec`]
-   |     ~~~~~~~
+LL - /// [`Vec`][std::vec::Vec]
+LL + /// [`Vec`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:118:21
@@ -740,8 +786,9 @@ LL | /// [Vec]: Vec
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [std::vec::Vec]
-   |     ~~~~~~~~~~~~~~~
+LL - /// [std::vec::Vec][Vec]
+LL + /// [std::vec::Vec]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:120:23
@@ -760,8 +807,9 @@ LL | /// [Vec]: Vec
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`std::vec::Vec`]
-   |     ~~~~~~~~~~~~~~~~~
+LL - /// [`std::vec::Vec`][Vec]
+LL + /// [`std::vec::Vec`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:122:21
@@ -780,8 +828,9 @@ LL | /// [std::vec::Vec]: Vec
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [std::vec::Vec]
-   |     ~~~~~~~~~~~~~~~
+LL - /// [std::vec::Vec][std::vec::Vec]
+LL + /// [std::vec::Vec]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:124:23
@@ -800,8 +849,9 @@ LL | /// [std::vec::Vec]: Vec
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`std::vec::Vec`]
-   |     ~~~~~~~~~~~~~~~~~
+LL - /// [`std::vec::Vec`][std::vec::Vec]
+LL + /// [`std::vec::Vec`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:127:13
@@ -820,8 +870,9 @@ LL | /// [usize]: usize
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [usize]
-   |     ~~~~~~~
+LL - /// [usize][usize]
+LL + /// [usize]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:129:15
@@ -840,8 +891,9 @@ LL | /// [usize]: usize
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`usize`]
-   |     ~~~~~~~~~
+LL - /// [`usize`][usize]
+LL + /// [`usize`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:131:13
@@ -860,8 +912,9 @@ LL | /// [std::primitive::usize]: usize
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [usize]
-   |     ~~~~~~~
+LL - /// [usize][std::primitive::usize]
+LL + /// [usize]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:133:15
@@ -880,8 +933,9 @@ LL | /// [std::primitive::usize]: usize
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`usize`]
-   |     ~~~~~~~~~
+LL - /// [`usize`][std::primitive::usize]
+LL + /// [`usize`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:135:29
@@ -900,8 +954,9 @@ LL | /// [usize]: usize
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [std::primitive::usize]
-   |     ~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// [std::primitive::usize][usize]
+LL + /// [std::primitive::usize]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:137:31
@@ -920,8 +975,9 @@ LL | /// [usize]: usize
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`std::primitive::usize`]
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// [`std::primitive::usize`][usize]
+LL + /// [`std::primitive::usize`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:139:29
@@ -940,8 +996,9 @@ LL | /// [std::primitive::usize]: usize
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [std::primitive::usize]
-   |     ~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// [std::primitive::usize][std::primitive::usize]
+LL + /// [std::primitive::usize]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:141:31
@@ -960,8 +1017,9 @@ LL | /// [std::primitive::usize]: usize
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`std::primitive::usize`]
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL - /// [`std::primitive::usize`][std::primitive::usize]
+LL + /// [`std::primitive::usize`]
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:144:20
@@ -980,8 +1038,9 @@ LL | /// [dummy_target]: dummy_target
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [dummy_target] TEXT
-   |     ~~~~~~~~~~~~~~
+LL - /// [dummy_target][dummy_target] TEXT
+LL + /// [dummy_target] TEXT
+   |
 
 error: redundant explicit link target
   --> $DIR/redundant_explicit_links.rs:146:22
@@ -1000,8 +1059,9 @@ LL | /// [dummy_target]: dummy_target
            the label is used to resolve intra-doc links
 help: remove explicit link target
    |
-LL | /// [`dummy_target`] TEXT
-   |     ~~~~~~~~~~~~~~~~
+LL - /// [`dummy_target`][dummy_target] TEXT
+LL + /// [`dummy_target`] TEXT
+   |
 
 error: aborting due to 60 previous errors
 
diff --git a/tests/rustdoc-ui/macro-docs.stdout b/tests/rustdoc-ui/macro-docs.stdout
deleted file mode 100644
index e69de29bb2d1d..0000000000000
diff --git a/tests/rustdoc-ui/target-feature-stability.rs b/tests/rustdoc-ui/target-feature-stability.rs
new file mode 100644
index 0000000000000..17fa3ccfe3e89
--- /dev/null
+++ b/tests/rustdoc-ui/target-feature-stability.rs
@@ -0,0 +1,28 @@
+//! This is a regression test for , ensuring
+//! that we can use the `neon` target feature on ARM32 targets in rustdoc despite there
+//! being a "forbidden" feature of the same name for aarch64, and rustdoc merging the
+//! target features of all targets.
+//@ check-pass
+//@ revisions: arm aarch64
+//@[arm] compile-flags: --target armv7-unknown-linux-gnueabihf
+//@[arm] needs-llvm-components: arm
+//@[aarch64] compile-flags: --target aarch64-unknown-none-softfloat
+//@[aarch64] needs-llvm-components: aarch64
+
+#![crate_type = "lib"]
+#![feature(no_core, lang_items)]
+#![feature(arm_target_feature)]
+#![no_core]
+
+#[lang = "sized"]
+pub trait Sized {}
+
+// `fp-armv8` is "forbidden" on aarch64 as we tie it to `neon`.
+#[target_feature(enable = "fp-armv8")]
+pub fn fun1() {}
+
+// This would usually be rejected as it changes the ABI.
+// But we disable that check in rustdoc since we are building "for all targets" and the
+// check can't really handle that.
+#[target_feature(enable = "soft-float")]
+pub fn fun2() {}
diff --git a/tests/rustdoc/anonymous-reexport-108931.rs b/tests/rustdoc/anonymous-reexport-108931.rs
index f4cc7f1239628..b995c89b61407 100644
--- a/tests/rustdoc/anonymous-reexport-108931.rs
+++ b/tests/rustdoc/anonymous-reexport-108931.rs
@@ -16,7 +16,7 @@ mod bar {
 //@ count - '//*[@id="main-content"]/h2' 2
 //@ has - '//*[@id="main-content"]/h2' 'Re-exports'
 //@ has - '//*[@id="main-content"]/h2' 'Modules'
-//@ has - '//*[@id="main-content"]//*[@class="item-table"]//li//code' 'pub use foo::Foo as _;'
-//@ has - '//*[@id="main-content"]//*[@class="item-table"]//li//code' 'pub use bar::Bar as _;'
+//@ has - '//*[@id="main-content"]//*[@class="item-table reexports"]/dt//code' 'pub use foo::Foo as _;'
+//@ has - '//*[@id="main-content"]//*[@class="item-table reexports"]/dt//code' 'pub use bar::Bar as _;'
 pub use foo::Foo as _;
 pub use bar::Bar as _;
diff --git a/tests/rustdoc/anonymous-reexport.rs b/tests/rustdoc/anonymous-reexport.rs
index 8021008dc6684..bf5fa93f953a6 100644
--- a/tests/rustdoc/anonymous-reexport.rs
+++ b/tests/rustdoc/anonymous-reexport.rs
@@ -9,7 +9,7 @@
 //@ has - '//*[@id="main-content"]/h2' 'Structs'
 //@ has - '//*[@id="main-content"]/h2' 'Re-exports'
 // The 3 re-exports.
-//@ count - '//*[@id="main-content"]//*[@class="item-table"]//li//code' 3
+//@ count - '//*[@id="main-content"]//*[@class="item-table reexports"]/dt//code' 3
 // The public struct.
 //@ count - '//*[@id="main-content"]//a[@class="struct"]' 1
 
diff --git a/tests/rustdoc/assoc-consts.rs b/tests/rustdoc/assoc-consts.rs
index cb8e839541c3b..247b5b180a869 100644
--- a/tests/rustdoc/assoc-consts.rs
+++ b/tests/rustdoc/assoc-consts.rs
@@ -45,7 +45,7 @@ pub fn f(_: &(ToString + 'static)) {}
 
 impl Bar {
     //@ has assoc_consts/struct.Bar.html '//*[@id="associatedconstant.F"]' \
-    //      "const F: fn(_: &(dyn ToString + 'static))"
+    //      "const F: fn(&(dyn ToString + 'static))"
     pub const F: fn(_: &(ToString + 'static)) = f;
 }
 
diff --git a/tests/rustdoc/attributes-inlining-108281.rs b/tests/rustdoc/attributes-inlining-108281.rs
index ba6c570b59bc8..9dfaf1a6846f3 100644
--- a/tests/rustdoc/attributes-inlining-108281.rs
+++ b/tests/rustdoc/attributes-inlining-108281.rs
@@ -11,15 +11,15 @@ mod sub {
     pub fn public() {}
 }
 
-//@ matches - '//*[@class="desc docblock-short"]' '^Displayed$'
+//@ matches - '//dd' '^Displayed$'
 /// Displayed
 #[doc(inline)]
 pub use crate::bar as Bar;
-//@ matches - '//*[@class="desc docblock-short"]' '^Hello\sDisplayed$'
+//@ matches - '//dd' '^Hello\sDisplayed$'
 #[doc(inline)]
 /// Hello
 pub use crate::Bar as Bar2;
 
-//@ matches - '//*[@class="desc docblock-short"]' '^Public$'
+//@ matches - '//dd' '^Public$'
 /// Public
 pub use crate::sub::public as Public;
diff --git a/tests/rustdoc/cfg_doc_reexport.rs b/tests/rustdoc/cfg_doc_reexport.rs
index a07e4fe2f0291..f8101e2a95807 100644
--- a/tests/rustdoc/cfg_doc_reexport.rs
+++ b/tests/rustdoc/cfg_doc_reexport.rs
@@ -5,8 +5,8 @@
 #![no_core]
 
 //@ has 'foo/index.html'
-//@ has - '//*[@class="item-name"]/*[@class="stab portability"]' 'foobar'
-//@ has - '//*[@class="item-name"]/*[@class="stab portability"]' 'bar'
+//@ has - '//dt/*[@class="stab portability"]' 'foobar'
+//@ has - '//dt/*[@class="stab portability"]' 'bar'
 
 #[doc(cfg(feature = "foobar"))]
 mod imp_priv {
diff --git a/tests/rustdoc/check-source-code-urls-to-def.rs b/tests/rustdoc/check-source-code-urls-to-def.rs
index 8703287abc553..d701b88bf9fd0 100644
--- a/tests/rustdoc/check-source-code-urls-to-def.rs
+++ b/tests/rustdoc/check-source-code-urls-to-def.rs
@@ -31,7 +31,8 @@ fn babar() {}
 //@ has - '//pre[@class="rust"]//a/@href' '/struct.String.html'
 //@ has - '//pre[@class="rust"]//a/@href' '/primitive.u32.html'
 //@ has - '//pre[@class="rust"]//a/@href' '/primitive.str.html'
-//@ count - '//pre[@class="rust"]//a[@href="#23"]' 5
+// The 5 links to line 23 and the line 23 itself.
+//@ count - '//pre[@class="rust"]//a[@href="#23"]' 6
 //@ has - '//pre[@class="rust"]//a[@href="../../source_code/struct.SourceCode.html"]' \
 //        'source_code::SourceCode'
 pub fn foo(a: u32, b: &str, c: String, d: Foo, e: bar::Bar, f: source_code::SourceCode) {
@@ -50,8 +51,8 @@ pub fn foo2(t: &T, v: &V, b: bool) {}
 pub trait AnotherTrait {}
 pub trait WhyNot {}
 
-//@ has - '//pre[@class="rust"]//a[@href="#50"]' 'AnotherTrait'
-//@ has - '//pre[@class="rust"]//a[@href="#51"]' 'WhyNot'
+//@ has - '//pre[@class="rust"]//a[@href="#51"]' 'AnotherTrait'
+//@ has - '//pre[@class="rust"]//a[@href="#52"]' 'WhyNot'
 pub fn foo3(t: &T, v: &V)
 where
     T: AnotherTrait,
@@ -60,7 +61,7 @@ where
 
 pub trait AnotherTrait2 {}
 
-//@ has - '//pre[@class="rust"]//a[@href="#61"]' 'AnotherTrait2'
+//@ has - '//pre[@class="rust"]//a[@href="#62"]' 'AnotherTrait2'
 pub fn foo4() {
     let x: Vec<&dyn AnotherTrait2> = Vec::new();
 }
diff --git a/tests/rustdoc/deprecated.rs b/tests/rustdoc/deprecated.rs
index b39da9b440ab7..a84657a3df5aa 100644
--- a/tests/rustdoc/deprecated.rs
+++ b/tests/rustdoc/deprecated.rs
@@ -1,6 +1,5 @@
-//@ has deprecated/index.html '//*[@class="item-name"]/span[@class="stab deprecated"]' \
-//      'Deprecated'
-//@ has - '//*[@class="desc docblock-short"]' 'Deprecated docs'
+//@ has deprecated/index.html '//dt/span[@class="stab deprecated"]' 'Deprecated'
+//@ has - '//dd' 'Deprecated docs'
 
 //@ has deprecated/struct.S.html '//*[@class="stab deprecated"]' \
 //      'Deprecated since 1.0.0: text'
@@ -8,7 +7,7 @@
 #[deprecated(since = "1.0.0", note = "text")]
 pub struct S;
 
-//@ matches deprecated/index.html '//*[@class="desc docblock-short"]' '^Docs'
+//@ matches deprecated/index.html '//dd' '^Docs'
 /// Docs
 pub struct T;
 
diff --git a/tests/rustdoc/display-hidden-items.rs b/tests/rustdoc/display-hidden-items.rs
index d9f53435e4635..40cd636e2fe29 100644
--- a/tests/rustdoc/display-hidden-items.rs
+++ b/tests/rustdoc/display-hidden-items.rs
@@ -5,19 +5,19 @@
 #![crate_name = "foo"]
 
 //@ has 'foo/index.html'
-//@ has - '//*[@class="item-name"]/span[@title="Hidden item"]' '👻'
+//@ has - '//dt/span[@title="Hidden item"]' '👻'
 
 //@ has - '//*[@id="reexport.hidden_reexport"]/code' '#[doc(hidden)] pub use hidden::inside_hidden as hidden_reexport;'
 #[doc(hidden)]
 pub use hidden::inside_hidden as hidden_reexport;
 
-//@ has - '//*[@class="item-name"]/a[@class="trait"]' 'TraitHidden'
+//@ has - '//dt/a[@class="trait"]' 'TraitHidden'
 //@ has 'foo/trait.TraitHidden.html'
 //@ has - '//code' '#[doc(hidden)] pub trait TraitHidden'
 #[doc(hidden)]
 pub trait TraitHidden {}
 
-//@ has 'foo/index.html' '//*[@class="item-name"]/a[@class="trait"]' 'Trait'
+//@ has 'foo/index.html' '//dt/a[@class="trait"]' 'Trait'
 pub trait Trait {
     //@ has 'foo/trait.Trait.html'
     //@ has - '//*[@id="associatedconstant.BAR"]/*[@class="code-header"]' '#[doc(hidden)] const BAR: u32 = 0u32'
@@ -29,7 +29,7 @@ pub trait Trait {
     fn foo() {}
 }
 
-//@ has 'foo/index.html' '//*[@class="item-name"]/a[@class="struct"]' 'Struct'
+//@ has 'foo/index.html' '//dt/a[@class="struct"]' 'Struct'
 //@ has 'foo/struct.Struct.html'
 pub struct Struct {
     //@ has - '//*[@id="structfield.a"]/code' 'a: u32'
@@ -50,7 +50,7 @@ impl Trait for Struct {
 //@ has - '//*[@id="impl-TraitHidden-for-Struct"]/*[@class="code-header"]' 'impl TraitHidden for Struct'
 impl TraitHidden for Struct {}
 
-//@ has 'foo/index.html' '//*[@class="item-name"]/a[@class="enum"]' 'HiddenEnum'
+//@ has 'foo/index.html' '//dt/a[@class="enum"]' 'HiddenEnum'
 //@ has 'foo/enum.HiddenEnum.html'
 //@ has - '//code' '#[doc(hidden)] pub enum HiddenEnum'
 #[doc(hidden)]
@@ -58,18 +58,18 @@ pub enum HiddenEnum {
     A,
 }
 
-//@ has 'foo/index.html' '//*[@class="item-name"]/a[@class="enum"]' 'Enum'
+//@ has 'foo/index.html' '//dt/a[@class="enum"]' 'Enum'
 pub enum Enum {
     //@ has 'foo/enum.Enum.html' '//*[@id="variant.A"]/*[@class="code-header"]' 'A'
     #[doc(hidden)]
     A,
 }
 
-//@ has 'foo/index.html' '//*[@class="item-name"]/a[@class="mod"]' 'hidden'
+//@ has 'foo/index.html' '//dt/a[@class="mod"]' 'hidden'
 #[doc(hidden)]
 pub mod hidden {
     //@ has 'foo/hidden/index.html'
-    //@ has - '//*[@class="item-name"]/a[@class="fn"]' 'inside_hidden'
+    //@ has - '//dt/a[@class="fn"]' 'inside_hidden'
     //@ has 'foo/hidden/fn.inside_hidden.html'
     pub fn inside_hidden() {}
 }
diff --git a/tests/rustdoc/doc-cfg.rs b/tests/rustdoc/doc-cfg.rs
index 6c973b5666b43..652c8419b4fb8 100644
--- a/tests/rustdoc/doc-cfg.rs
+++ b/tests/rustdoc/doc-cfg.rs
@@ -12,7 +12,7 @@ pub struct Portable;
 //@ has doc_cfg/unix_only/index.html \
 //  '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
 //  'Available on Unix only.'
-//@ matches - '//*[@class="item-name"]//*[@class="stab portability"]' '\AARM\Z'
+//@ matches - '//dt//*[@class="stab portability"]' '\AARM\Z'
 //@ count - '//*[@class="stab portability"]' 2
 #[doc(cfg(unix))]
 pub mod unix_only {
@@ -42,7 +42,7 @@ pub mod unix_only {
 //@ has doc_cfg/wasi_only/index.html \
 //  '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
 //  'Available on WASI only.'
-//@ matches - '//*[@class="item-name"]//*[@class="stab portability"]' '\AWebAssembly\Z'
+//@ matches - '//dt//*[@class="stab portability"]' '\AWebAssembly\Z'
 //@ count - '//*[@class="stab portability"]' 2
 #[doc(cfg(target_os = "wasi"))]
 pub mod wasi_only {
@@ -74,7 +74,7 @@ pub mod wasi_only {
 
 // the portability header is different on the module view versus the full view
 //@ has doc_cfg/index.html
-//@ matches - '//*[@class="item-name"]//*[@class="stab portability"]' '\Aavx\Z'
+//@ matches - '//dt//*[@class="stab portability"]' '\Aavx\Z'
 
 //@ has doc_cfg/fn.uses_target_feature.html
 //@ has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
diff --git a/tests/rustdoc/doc-hidden-reexports-109449.rs b/tests/rustdoc/doc-hidden-reexports-109449.rs
index cc3679f6196af..8f195544120af 100644
--- a/tests/rustdoc/doc-hidden-reexports-109449.rs
+++ b/tests/rustdoc/doc-hidden-reexports-109449.rs
@@ -26,7 +26,7 @@ pub mod single_reexport {
     //@ has 'foo/single_reexport/index.html'
 
     // First we check that we have 4 type aliases.
-    //@ count - '//*[@id="main-content"]/*[@class="item-table"]//code' 4
+    //@ count - '//*[@id="main-content"]/*[@class="item-table reexports"]//code' 4
 
     // Then we check that we have the correct link for each re-export.
 
@@ -131,10 +131,10 @@ mod private {
 pub mod doc_hidden_reexport {
     //@ has 'foo/doc_hidden_reexport/index.html'
     // Ensure there is only one item in this page and that it's a struct.
-    //@ count - '//*[@class="item-name"]' 1
+    //@ count - '//dt' 1
     //@ has - '//a[@class="struct"]' 'Reexport'
     // Check that the `#[doc(hidden)]` re-export's attributes are not taken into account.
-    //@ has - '//*[@class="desc docblock-short"]' 'Visible. Original.'
+    //@ has - '//dd' 'Visible. Original.'
     /// Visible.
     pub use self::Bar3 as Reexport;
     /// Hidden.
diff --git a/tests/rustdoc/double-hyphen-to-dash.rs b/tests/rustdoc/double-hyphen-to-dash.rs
index 009de4faf41b6..c14acd065cde0 100644
--- a/tests/rustdoc/double-hyphen-to-dash.rs
+++ b/tests/rustdoc/double-hyphen-to-dash.rs
@@ -2,7 +2,7 @@
 
 #![crate_name = "foo"]
 
-//@ has 'foo/index.html' '//*[@class="desc docblock-short"]' '–'
+//@ has 'foo/index.html' '//dd' '–'
 //@ has 'foo/struct.Bar.html' '//*[@class="docblock"]' '–'
 
 /// --
diff --git a/tests/rustdoc/duplicate-cfg.rs b/tests/rustdoc/duplicate-cfg.rs
index 87c089e9735b3..93f26ab944d34 100644
--- a/tests/rustdoc/duplicate-cfg.rs
+++ b/tests/rustdoc/duplicate-cfg.rs
@@ -2,8 +2,8 @@
 #![feature(doc_cfg)]
 
 //@ has 'foo/index.html'
-//@ matches '-' '//*[@class="item-name"]//*[@class="stab portability"]' '^sync$'
-//@ has '-' '//*[@class="item-name"]//*[@class="stab portability"]/@title' 'Available on crate feature `sync` only'
+//@ matches '-' '//dt//*[@class="stab portability"]' '^sync$'
+//@ has '-' '//dt//*[@class="stab portability"]/@title' 'Available on crate feature `sync` only'
 
 //@ has 'foo/struct.Foo.html'
 //@ has '-' '//*[@class="stab portability"]' 'sync'
diff --git a/tests/rustdoc/fn-pointer-arg-name.rs b/tests/rustdoc/fn-pointer-arg-name.rs
index 3bde6e9ecfa8c..0ee1964511a1d 100644
--- a/tests/rustdoc/fn-pointer-arg-name.rs
+++ b/tests/rustdoc/fn-pointer-arg-name.rs
@@ -3,3 +3,11 @@
 //@ has foo/fn.f.html
 //@ has - '//pre[@class="rust item-decl"]' 'pub fn f(callback: fn(len: usize, foo: u32))'
 pub fn f(callback: fn(len: usize, foo: u32)) {}
+
+//@ has foo/fn.g.html
+//@ has - '//pre[@class="rust item-decl"]' 'pub fn g(_: fn(usize, u32))'
+pub fn g(_: fn(usize, _: u32)) {}
+
+//@ has foo/fn.mixed.html
+//@ has - '//pre[@class="rust item-decl"]' 'pub fn mixed(_: fn(_: usize, foo: u32))'
+pub fn mixed(_: fn(usize, foo: u32)) {}
diff --git a/tests/rustdoc/footnote-in-summary.rs b/tests/rustdoc/footnote-in-summary.rs
index d69282f104165..2a9668a9963b0 100644
--- a/tests/rustdoc/footnote-in-summary.rs
+++ b/tests/rustdoc/footnote-in-summary.rs
@@ -4,8 +4,8 @@
 #![crate_name = "foo"]
 
 //@ has 'foo/index.html'
-//@ has - '//*[@class="desc docblock-short"]' 'hello bla'
-//@ !has - '//*[@class="desc docblock-short"]/sup' '1'
+//@ has - '//dd' 'hello bla'
+//@ !has - '//dd/sup' '1'
 
 //@ has 'foo/struct.S.html'
 //@ has - '//*[@class="docblock"]//sup' '1'
diff --git a/tests/rustdoc/glob-reexport-attribute-merge-120487.rs b/tests/rustdoc/glob-reexport-attribute-merge-120487.rs
index 2fa10f546d577..5b918e0ffd9f2 100644
--- a/tests/rustdoc/glob-reexport-attribute-merge-120487.rs
+++ b/tests/rustdoc/glob-reexport-attribute-merge-120487.rs
@@ -7,9 +7,9 @@
 
 //@ has 'foo/index.html'
 // There are two items.
-//@ count - '//*[@class="item-table"]//div[@class="item-name"]' 2
+//@ count - '//*[@class="item-table"]/dt' 2
 // Only one of them should have an attribute.
-//@ count - '//*[@class="item-table"]//div[@class="item-name"]/*[@class="stab portability"]' 1
+//@ count - '//*[@class="item-table"]/dt/*[@class="stab portability"]' 1
 
 mod a {
     #[doc(cfg(not(feature = "a")))]
diff --git a/tests/rustdoc/glob-reexport-attribute-merge-doc-auto-cfg.rs b/tests/rustdoc/glob-reexport-attribute-merge-doc-auto-cfg.rs
index 314b457c2ad7f..d0a2165ec8abb 100644
--- a/tests/rustdoc/glob-reexport-attribute-merge-doc-auto-cfg.rs
+++ b/tests/rustdoc/glob-reexport-attribute-merge-doc-auto-cfg.rs
@@ -6,9 +6,9 @@
 
 //@ has 'foo/index.html'
 // There are two items.
-//@ count - '//*[@class="item-table"]//div[@class="item-name"]' 2
+//@ count - '//*[@class="item-table"]/dt' 2
 // Only one of them should have an attribute.
-//@ count - '//*[@class="item-table"]//div[@class="item-name"]/*[@class="stab portability"]' 1
+//@ count - '//*[@class="item-table"]/dt/*[@class="stab portability"]' 1
 
 mod a {
     #[cfg(not(feature = "a"))]
diff --git a/tests/rustdoc/glob-shadowing-const.rs b/tests/rustdoc/glob-shadowing-const.rs
index 1eb5596cd9ccf..fbc22dbccaa77 100644
--- a/tests/rustdoc/glob-shadowing-const.rs
+++ b/tests/rustdoc/glob-shadowing-const.rs
@@ -15,6 +15,6 @@ mod sub4 {
 pub use sub4::inner::*;
 
 //@ has 'foo/index.html'
-//@ has - '//div[@class="desc docblock-short"]' '1'
-//@ !has - '//div[@class="desc docblock-short"]' '0'
+//@ has - '//dd' '1'
+//@ !has - '//dd' '0'
 fn main() { assert_eq!(X, 1); }
diff --git a/tests/rustdoc/glob-shadowing.rs b/tests/rustdoc/glob-shadowing.rs
index a051bd407d5b1..d9e9ead3f9a90 100644
--- a/tests/rustdoc/glob-shadowing.rs
+++ b/tests/rustdoc/glob-shadowing.rs
@@ -1,17 +1,17 @@
 //@ has 'glob_shadowing/index.html'
-//@ count - '//div[@class="item-name"]' 6
-//@ !has - '//div[@class="desc docblock-short"]' 'sub1::describe'
-//@ has - '//div[@class="desc docblock-short"]' 'sub2::describe'
+//@ count - '//dt' 6
+//@ !has - '//dd' 'sub1::describe'
+//@ has - '//dd' 'sub2::describe'
 
-//@ !has - '//div[@class="desc docblock-short"]' 'sub1::describe2'
+//@ !has - '//dd' 'sub1::describe2'
 
-//@ !has - '//div[@class="desc docblock-short"]' 'sub1::prelude'
-//@ has - '//div[@class="desc docblock-short"]' 'mod::prelude'
+//@ !has - '//dd' 'sub1::prelude'
+//@ has - '//dd' 'mod::prelude'
 
-//@ has - '//div[@class="desc docblock-short"]' 'sub1::Foo (struct)'
-//@ has - '//div[@class="desc docblock-short"]' 'mod::Foo (function)'
+//@ has - '//dd' 'sub1::Foo (struct)'
+//@ has - '//dd' 'mod::Foo (function)'
 
-//@ has - '//div[@class="desc docblock-short"]' 'sub4::inner::X'
+//@ has - '//dd' 'sub4::inner::X'
 
 //@ has 'glob_shadowing/fn.describe.html'
 //@ has - '//div[@class="docblock"]' 'sub2::describe'
diff --git a/tests/rustdoc/impl-on-ty-alias-issue-119015.rs b/tests/rustdoc/impl-on-ty-alias-issue-119015.rs
index cea0f5565a20e..a514bc35bfc41 100644
--- a/tests/rustdoc/impl-on-ty-alias-issue-119015.rs
+++ b/tests/rustdoc/impl-on-ty-alias-issue-119015.rs
@@ -2,8 +2,8 @@
 
 //@ has 'foo/index.html'
 // There should be only `type A`.
-//@ count - '//*[@class="item-table"]//*[@class="item-name"]' 1
-//@ has - '//*[@class="item-name"]/a[@href="type.A.html"]' 'A'
+//@ count - '//*[@class="item-table"]//dt' 1
+//@ has - '//dt/a[@href="type.A.html"]' 'A'
 
 mod foo {
     pub struct S;
diff --git a/tests/rustdoc/inherent-projections.rs b/tests/rustdoc/inherent-projections.rs
index 4fc769f70f55c..ced4db9a490c8 100644
--- a/tests/rustdoc/inherent-projections.rs
+++ b/tests/rustdoc/inherent-projections.rs
@@ -13,7 +13,7 @@ impl Owner {
 }
 
 // Make sure we handle bound vars correctly.
-//@ has 'inherent_projections/fn.user.html' '//pre[@class="rust item-decl"]' "user(_: for<'a> fn(_: Carrier<'a>::Focus))"
+//@ has 'inherent_projections/fn.user.html' '//pre[@class="rust item-decl"]' "user(_: for<'a> fn(Carrier<'a>::Focus))"
 pub fn user(_: for<'a> fn(Carrier<'a>::Focus)) {}
 
 pub struct Carrier<'a>(&'a ());
diff --git a/tests/rustdoc/inline-private-with-intermediate-doc-hidden.rs b/tests/rustdoc/inline-private-with-intermediate-doc-hidden.rs
index 752f3843eeae0..d27ecbad1690e 100644
--- a/tests/rustdoc/inline-private-with-intermediate-doc-hidden.rs
+++ b/tests/rustdoc/inline-private-with-intermediate-doc-hidden.rs
@@ -9,7 +9,7 @@
 //@ count - '//*[@id="main-content"]/*[@class="section-header"]' 1
 //@ has - '//*[@id="main-content"]/*[@class="section-header"]' 'Structs'
 //@ has - '//*[@id="main-content"]//a[@href="struct.Reexport.html"]' 'Reexport'
-//@ has - '//*[@id="main-content"]//*[@class="desc docblock-short"]' 'Visible. Original.'
+//@ has - '//*[@id="main-content"]//dd' 'Visible. Original.'
 
 mod private {
     /// Original.
diff --git a/tests/rustdoc/inline_cross/doc-hidden-broken-link-28480.rs b/tests/rustdoc/inline_cross/doc-hidden-broken-link-28480.rs
new file mode 100644
index 0000000000000..e1ca7403c03f6
--- /dev/null
+++ b/tests/rustdoc/inline_cross/doc-hidden-broken-link-28480.rs
@@ -0,0 +1,16 @@
+// https://github.com/rust-lang/rust/issues/28480
+#![crate_name="foobar"]
+
+//@ aux-build:rustdoc-hidden-sig.rs
+//@ build-aux-docs
+//@ ignore-cross-compile
+
+//@ has rustdoc_hidden_sig/struct.Bar.html
+//@ !has -  '//a/@title' 'Hidden'
+//@ has -  '//a' 'u8'
+extern crate rustdoc_hidden_sig;
+
+//@ has foobar/struct.Bar.html
+//@ !has -  '//a/@title' 'Hidden'
+//@ has -  '//a' 'u8'
+pub use rustdoc_hidden_sig::Bar;
diff --git a/tests/rustdoc/inline_cross/doc-reachability-impl-31948-1.rs b/tests/rustdoc/inline_cross/doc-reachability-impl-31948-1.rs
new file mode 100644
index 0000000000000..baab1da95470c
--- /dev/null
+++ b/tests/rustdoc/inline_cross/doc-reachability-impl-31948-1.rs
@@ -0,0 +1,30 @@
+// https://github.com/rust-lang/rust/issues/31948
+#![crate_name="foobar"]
+
+//@ aux-build:rustdoc-nonreachable-impls.rs
+//@ build-aux-docs
+//@ ignore-cross-compile
+
+extern crate rustdoc_nonreachable_impls;
+
+//@ has foobar/struct.Wobble.html
+//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'Bark for'
+//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'Woof for'
+//@ !has - '//*[@class="impl"]//h3[@class="code-header"]' 'Bar for'
+//@ !has - '//*[@class="impl"]//h3[@class="code-header"]' 'Qux for'
+pub use rustdoc_nonreachable_impls::hidden::Wobble;
+
+//@ has foobar/trait.Bark.html
+//@ has - '//h3[@class="code-header"]' 'for Foo'
+//@ has - '//h3[@class="code-header"]' 'for Wobble'
+//@ !has - '//h3[@class="code-header"]' 'for Wibble'
+pub use rustdoc_nonreachable_impls::Bark;
+
+//@ has foobar/trait.Woof.html
+//@ has - '//h3[@class="code-header"]' 'for Foo'
+//@ has - '//h3[@class="code-header"]' 'for Wobble'
+//@ !has - '//h3[@class="code-header"]' 'for Wibble'
+pub use rustdoc_nonreachable_impls::Woof;
+
+//@ !has foobar/trait.Bar.html
+//@ !has foobar/trait.Qux.html
diff --git a/tests/rustdoc/inline_cross/doc-reachability-impl-31948-2.rs b/tests/rustdoc/inline_cross/doc-reachability-impl-31948-2.rs
new file mode 100644
index 0000000000000..40e9108ec62ef
--- /dev/null
+++ b/tests/rustdoc/inline_cross/doc-reachability-impl-31948-2.rs
@@ -0,0 +1,24 @@
+// https://github.com/rust-lang/rust/issues/31948
+#![crate_name="foobar"]
+
+//@ aux-build:rustdoc-nonreachable-impls.rs
+//@ build-aux-docs
+//@ ignore-cross-compile
+
+extern crate rustdoc_nonreachable_impls;
+
+//@ has foobar/struct.Wobble.html
+//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'Qux for'
+//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'Bark for'
+//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'Woof for'
+//@ !has - '//*[@class="impl"]//h3[@class="code-header"]' 'Bar for'
+pub use rustdoc_nonreachable_impls::hidden::Wobble;
+
+//@ has foobar/trait.Qux.html
+//@ has - '//h3[@class="code-header"]' 'for Foo'
+//@ has - '//h3[@class="code-header"]' 'for Wobble'
+pub use rustdoc_nonreachable_impls::hidden::Qux;
+
+//@ !has foobar/trait.Bar.html
+//@ !has foobar/trait.Woof.html
+//@ !has foobar/trait.Bark.html
diff --git a/tests/rustdoc/inline_cross/doc-reachability-impl-31948.rs b/tests/rustdoc/inline_cross/doc-reachability-impl-31948.rs
new file mode 100644
index 0000000000000..ab0048513c729
--- /dev/null
+++ b/tests/rustdoc/inline_cross/doc-reachability-impl-31948.rs
@@ -0,0 +1,32 @@
+// https://github.com/rust-lang/rust/issues/31948
+#![crate_name="foobar"]
+
+//@ aux-build:rustdoc-nonreachable-impls.rs
+//@ build-aux-docs
+//@ ignore-cross-compile
+
+extern crate rustdoc_nonreachable_impls;
+
+//@ has foobar/struct.Foo.html
+//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'Bark for'
+//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'Woof for'
+//@ !has - '//*[@class="impl"]//h3[@class="code-header"]' 'Bar for'
+//@ !has - '//*[@class="impl"]//h3[@class="code-header"]' 'Qux for'
+pub use rustdoc_nonreachable_impls::Foo;
+
+//@ has foobar/trait.Bark.html
+//@ has - '//h3[@class="code-header"]' 'for Foo'
+//@ !has - '//h3[@class="code-header"]' 'for Wibble'
+//@ !has - '//h3[@class="code-header"]' 'for Wobble'
+pub use rustdoc_nonreachable_impls::Bark;
+
+//@ has foobar/trait.Woof.html
+//@ has - '//h3[@class="code-header"]' 'for Foo'
+//@ !has - '//h3[@class="code-header"]' 'for Wibble'
+//@ !has - '//h3[@class="code-header"]' 'for Wobble'
+pub use rustdoc_nonreachable_impls::Woof;
+
+//@ !has foobar/trait.Bar.html
+//@ !has foobar/trait.Qux.html
+//@ !has foobar/struct.Wibble.html
+//@ !has foobar/struct.Wobble.html
diff --git a/tests/rustdoc/inline_cross/impl-dyn-trait-32881.rs b/tests/rustdoc/inline_cross/impl-dyn-trait-32881.rs
new file mode 100644
index 0000000000000..f7dc741445520
--- /dev/null
+++ b/tests/rustdoc/inline_cross/impl-dyn-trait-32881.rs
@@ -0,0 +1,14 @@
+// https://github.com/rust-lang/rust/issues/32881
+#![crate_name="foobar"]
+
+//@ aux-build:rustdoc-trait-object-impl.rs
+//@ build-aux-docs
+//@ ignore-cross-compile
+
+extern crate rustdoc_trait_object_impl;
+
+//@ has foobar/trait.Bar.html
+//@ has - '//h3[@class="code-header"]' "impl<'a> dyn Bar"
+//@ has - '//h3[@class="code-header"]' "impl<'a> Debug for dyn Bar"
+
+pub use rustdoc_trait_object_impl::Bar;
diff --git a/tests/rustdoc/inline_cross/impl-ref-33113.rs b/tests/rustdoc/inline_cross/impl-ref-33113.rs
new file mode 100644
index 0000000000000..9ac4f02e00c15
--- /dev/null
+++ b/tests/rustdoc/inline_cross/impl-ref-33113.rs
@@ -0,0 +1,13 @@
+// https://github.com/rust-lang/rust/issues/33113
+#![crate_name="foobar"]
+
+//@ aux-build:issue-33113.rs
+//@ build-aux-docs
+//@ ignore-cross-compile
+
+extern crate bar;
+
+//@ has foobar/trait.Bar.html
+//@ has - '//h3[@class="code-header"]' "for &'a char"
+//@ has - '//h3[@class="code-header"]' "for Foo"
+pub use bar::Bar;
diff --git a/tests/rustdoc/inline_cross/inline_hidden.rs b/tests/rustdoc/inline_cross/inline_hidden.rs
index 095cd2d3c55f1..49ca2db6a2245 100644
--- a/tests/rustdoc/inline_cross/inline_hidden.rs
+++ b/tests/rustdoc/inline_cross/inline_hidden.rs
@@ -11,14 +11,14 @@ extern crate rustdoc_hidden;
 pub use rustdoc_hidden::Foo;
 
 // Even if the foreign item has `doc(hidden)`, we should be able to inline it.
-//@ has - '//*[@class="item-name"]/a[@class="struct"]' 'Inlined'
+//@ has - '//dt/a[@class="struct"]' 'Inlined'
 #[doc(inline)]
 pub use rustdoc_hidden::Foo as Inlined;
 
 // Even with this import, we should not see `Foo`.
-//@ count - '//*[@class="item-name"]' 4
-//@ has - '//*[@class="item-name"]/a[@class="struct"]' 'Bar'
-//@ has - '//*[@class="item-name"]/a[@class="fn"]' 'foo'
+//@ count - '//dt' 4
+//@ has - '//dt/a[@class="struct"]' 'Bar'
+//@ has - '//dt/a[@class="fn"]' 'foo'
 pub use rustdoc_hidden::*;
 
 //@ has inline_hidden/fn.foo.html
diff --git a/tests/rustdoc/inline_cross/issue-24183.rs b/tests/rustdoc/inline_cross/issue-24183.rs
deleted file mode 100644
index 8299eecc575d1..0000000000000
--- a/tests/rustdoc/inline_cross/issue-24183.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-#![crate_type = "lib"]
-#![crate_name = "usr"]
-
-//@ aux-crate:issue_24183=issue-24183.rs
-//@ edition: 2021
-
-//@ has usr/trait.U.html
-//@ has - '//*[@class="rust item-decl"]' "pub trait U {"
-//@ has - '//*[@id="method.modified"]' \
-// "fn modified(self) -> Self\
-// where \
-//     Self: Sized"
-//@ snapshot method_no_where_self_sized - '//*[@id="method.touch"]/*[@class="code-header"]'
-pub use issue_24183::U;
-
-//@ has usr/trait.S.html
-//@ has - '//*[@class="rust item-decl"]' 'pub trait S: Sized {'
-pub use issue_24183::S;
diff --git a/tests/rustdoc/inline_cross/issue-28480.rs b/tests/rustdoc/inline_cross/issue-28480.rs
deleted file mode 100644
index 004510fd9225e..0000000000000
--- a/tests/rustdoc/inline_cross/issue-28480.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-//@ aux-build:rustdoc-hidden-sig.rs
-//@ build-aux-docs
-//@ ignore-cross-compile
-
-//@ has rustdoc_hidden_sig/struct.Bar.html
-//@ !has -  '//a/@title' 'Hidden'
-//@ has -  '//a' 'u8'
-extern crate rustdoc_hidden_sig;
-
-//@ has issue_28480/struct.Bar.html
-//@ !has -  '//a/@title' 'Hidden'
-//@ has -  '//a' 'u8'
-pub use rustdoc_hidden_sig::Bar;
diff --git a/tests/rustdoc/inline_cross/issue-31948-1.rs b/tests/rustdoc/inline_cross/issue-31948-1.rs
deleted file mode 100644
index e59da87c29de7..0000000000000
--- a/tests/rustdoc/inline_cross/issue-31948-1.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-//@ aux-build:rustdoc-nonreachable-impls.rs
-//@ build-aux-docs
-//@ ignore-cross-compile
-
-extern crate rustdoc_nonreachable_impls;
-
-//@ has issue_31948_1/struct.Wobble.html
-//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'Bark for'
-//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'Woof for'
-//@ !has - '//*[@class="impl"]//h3[@class="code-header"]' 'Bar for'
-//@ !has - '//*[@class="impl"]//h3[@class="code-header"]' 'Qux for'
-pub use rustdoc_nonreachable_impls::hidden::Wobble;
-
-//@ has issue_31948_1/trait.Bark.html
-//@ has - '//h3[@class="code-header"]' 'for Foo'
-//@ has - '//h3[@class="code-header"]' 'for Wobble'
-//@ !has - '//h3[@class="code-header"]' 'for Wibble'
-pub use rustdoc_nonreachable_impls::Bark;
-
-//@ has issue_31948_1/trait.Woof.html
-//@ has - '//h3[@class="code-header"]' 'for Foo'
-//@ has - '//h3[@class="code-header"]' 'for Wobble'
-//@ !has - '//h3[@class="code-header"]' 'for Wibble'
-pub use rustdoc_nonreachable_impls::Woof;
-
-//@ !has issue_31948_1/trait.Bar.html
-//@ !has issue_31948_1/trait.Qux.html
diff --git a/tests/rustdoc/inline_cross/issue-31948-2.rs b/tests/rustdoc/inline_cross/issue-31948-2.rs
deleted file mode 100644
index 34b570528832a..0000000000000
--- a/tests/rustdoc/inline_cross/issue-31948-2.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-//@ aux-build:rustdoc-nonreachable-impls.rs
-//@ build-aux-docs
-//@ ignore-cross-compile
-
-extern crate rustdoc_nonreachable_impls;
-
-//@ has issue_31948_2/struct.Wobble.html
-//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'Qux for'
-//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'Bark for'
-//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'Woof for'
-//@ !has - '//*[@class="impl"]//h3[@class="code-header"]' 'Bar for'
-pub use rustdoc_nonreachable_impls::hidden::Wobble;
-
-//@ has issue_31948_2/trait.Qux.html
-//@ has - '//h3[@class="code-header"]' 'for Foo'
-//@ has - '//h3[@class="code-header"]' 'for Wobble'
-pub use rustdoc_nonreachable_impls::hidden::Qux;
-
-//@ !has issue_31948_2/trait.Bar.html
-//@ !has issue_31948_2/trait.Woof.html
-//@ !has issue_31948_2/trait.Bark.html
diff --git a/tests/rustdoc/inline_cross/issue-31948.rs b/tests/rustdoc/inline_cross/issue-31948.rs
deleted file mode 100644
index 7a43fc7b279de..0000000000000
--- a/tests/rustdoc/inline_cross/issue-31948.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-//@ aux-build:rustdoc-nonreachable-impls.rs
-//@ build-aux-docs
-//@ ignore-cross-compile
-
-extern crate rustdoc_nonreachable_impls;
-
-//@ has issue_31948/struct.Foo.html
-//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'Bark for'
-//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'Woof for'
-//@ !has - '//*[@class="impl"]//h3[@class="code-header"]' 'Bar for'
-//@ !has - '//*[@class="impl"]//h3[@class="code-header"]' 'Qux for'
-pub use rustdoc_nonreachable_impls::Foo;
-
-//@ has issue_31948/trait.Bark.html
-//@ has - '//h3[@class="code-header"]' 'for Foo'
-//@ !has - '//h3[@class="code-header"]' 'for Wibble'
-//@ !has - '//h3[@class="code-header"]' 'for Wobble'
-pub use rustdoc_nonreachable_impls::Bark;
-
-//@ has issue_31948/trait.Woof.html
-//@ has - '//h3[@class="code-header"]' 'for Foo'
-//@ !has - '//h3[@class="code-header"]' 'for Wibble'
-//@ !has - '//h3[@class="code-header"]' 'for Wobble'
-pub use rustdoc_nonreachable_impls::Woof;
-
-//@ !has issue_31948/trait.Bar.html
-//@ !has issue_31948/trait.Qux.html
-//@ !has issue_31948/struct.Wibble.html
-//@ !has issue_31948/struct.Wobble.html
diff --git a/tests/rustdoc/inline_cross/issue-32881.rs b/tests/rustdoc/inline_cross/issue-32881.rs
deleted file mode 100644
index d4ebf10a1ca9a..0000000000000
--- a/tests/rustdoc/inline_cross/issue-32881.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-//@ aux-build:rustdoc-trait-object-impl.rs
-//@ build-aux-docs
-//@ ignore-cross-compile
-
-extern crate rustdoc_trait_object_impl;
-
-//@ has issue_32881/trait.Bar.html
-//@ has - '//h3[@class="code-header"]' "impl<'a> dyn Bar"
-//@ has - '//h3[@class="code-header"]' "impl<'a> Debug for dyn Bar"
-
-pub use rustdoc_trait_object_impl::Bar;
diff --git a/tests/rustdoc/inline_cross/issue-33113.rs b/tests/rustdoc/inline_cross/issue-33113.rs
deleted file mode 100644
index 05e87d962cb67..0000000000000
--- a/tests/rustdoc/inline_cross/issue-33113.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-//@ aux-build:issue-33113.rs
-//@ build-aux-docs
-//@ ignore-cross-compile
-
-extern crate bar;
-
-//@ has issue_33113/trait.Bar.html
-//@ has - '//h3[@class="code-header"]' "for &'a char"
-//@ has - '//h3[@class="code-header"]' "for Foo"
-pub use bar::Bar;
diff --git a/tests/rustdoc/inline_cross/issue-76736-1.rs b/tests/rustdoc/inline_cross/issue-76736-1.rs
deleted file mode 100644
index fe52702fd6f51..0000000000000
--- a/tests/rustdoc/inline_cross/issue-76736-1.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-//@ aux-build:issue-76736-1.rs
-//@ aux-build:issue-76736-2.rs
-
-#![crate_name = "foo"]
-
-extern crate issue_76736_1;
-extern crate issue_76736_2;
-
-//@ has foo/struct.Foo.html
-//@ !has - '//*[@class="impl"]//h3[@class="code-header"]' 'MaybeResult'
-pub struct Foo;
-
-//@ has foo/struct.Bar.html
-//@ !has - '//*[@class="impl"]//h3[@class="code-header"]' 'MaybeResult'
-pub use issue_76736_2::Bar;
diff --git a/tests/rustdoc/inline_cross/issue-76736-2.rs b/tests/rustdoc/inline_cross/issue-76736-2.rs
deleted file mode 100644
index df376ebe9a143..0000000000000
--- a/tests/rustdoc/inline_cross/issue-76736-2.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-//@ aux-build:issue-76736-1.rs
-//@ aux-build:issue-76736-2.rs
-
-// https://github.com/rust-lang/rust/issues/124635
-
-#![crate_name = "foo"]
-#![feature(rustc_private)]
-
-extern crate issue_76736_1;
-extern crate issue_76736_2;
-
-//@ has foo/struct.Foo.html
-//@ !has - '//*[@class="impl"]//h3[@class="code-header"]' 'MaybeResult'
-pub struct Foo;
-
-//@ has foo/struct.Bar.html
-//@ !has - '//*[@class="impl"]//h3[@class="code-header"]' 'MaybeResult'
-pub use issue_76736_2::Bar;
diff --git a/tests/rustdoc/inline_cross/issue-76736-3.rs b/tests/rustdoc/inline_cross/issue-76736-3.rs
deleted file mode 100644
index 1bed4621c049e..0000000000000
--- a/tests/rustdoc/inline_cross/issue-76736-3.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-//@ compile-flags: -Zforce-unstable-if-unmarked
-//@ aux-build:issue-76736-1.rs
-//@ aux-build:issue-76736-2.rs
-
-#![crate_name = "foo"]
-
-extern crate issue_76736_1;
-extern crate issue_76736_2;
-
-//@ has foo/struct.Foo.html
-//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'MaybeResult'
-pub struct Foo;
-
-//@ has foo/struct.Bar.html
-//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'MaybeResult'
-pub use issue_76736_2::Bar;
diff --git a/tests/rustdoc/inline_cross/issue-76736-4.rs b/tests/rustdoc/inline_cross/issue-76736-4.rs
deleted file mode 100644
index 487e90301082b..0000000000000
--- a/tests/rustdoc/inline_cross/issue-76736-4.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-//@ aux-build:issue-76736-1.rs
-//@ aux-build:issue-76736-2.rs
-
-// https://github.com/rust-lang/rust/issues/124635
-
-#![crate_name = "foo"]
-#![feature(rustc_private, staged_api)]
-#![unstable(feature = "rustc_private", issue = "none")]
-
-extern crate issue_76736_1;
-extern crate issue_76736_2;
-
-//@ has foo/struct.Foo.html
-//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'MaybeResult'
-pub struct Foo;
-
-//@ has foo/struct.Bar.html
-//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'MaybeResult'
-pub use issue_76736_2::Bar;
diff --git a/tests/rustdoc/inline_cross/macros.rs b/tests/rustdoc/inline_cross/macros.rs
index aab7a3650b107..57eec77899e0b 100644
--- a/tests/rustdoc/inline_cross/macros.rs
+++ b/tests/rustdoc/inline_cross/macros.rs
@@ -6,10 +6,8 @@
 
 extern crate macros;
 
-//@ has foo/index.html '//*[@class="item-name"]/span[@class="stab deprecated"]' \
-//         Deprecated
-//@ has - '//*[@class="item-name"]/span[@class="stab unstable"]' \
-//         Experimental
+//@ has foo/index.html '//dt/span[@class="stab deprecated"]' Deprecated
+//@ has - '//dt/span[@class="stab unstable"]' Experimental
 
 //@ has foo/macro.my_macro.html
 //@ has - '//*[@class="docblock"]' 'docs for my_macro'
diff --git a/tests/rustdoc/inline_cross/rustc-private-76736-1.rs b/tests/rustdoc/inline_cross/rustc-private-76736-1.rs
new file mode 100644
index 0000000000000..3ffa5e6cc06e9
--- /dev/null
+++ b/tests/rustdoc/inline_cross/rustc-private-76736-1.rs
@@ -0,0 +1,17 @@
+// https://github.com/rust-lang/rust/issues/76736
+
+//@ aux-build:issue-76736-1.rs
+//@ aux-build:issue-76736-2.rs
+
+#![crate_name = "foo"]
+
+extern crate issue_76736_1;
+extern crate issue_76736_2;
+
+//@ has foo/struct.Foo.html
+//@ !has - '//*[@class="impl"]//h3[@class="code-header"]' 'MaybeResult'
+pub struct Foo;
+
+//@ has foo/struct.Bar.html
+//@ !has - '//*[@class="impl"]//h3[@class="code-header"]' 'MaybeResult'
+pub use issue_76736_2::Bar;
diff --git a/tests/rustdoc/inline_cross/rustc-private-76736-2.rs b/tests/rustdoc/inline_cross/rustc-private-76736-2.rs
new file mode 100644
index 0000000000000..843b2941602cb
--- /dev/null
+++ b/tests/rustdoc/inline_cross/rustc-private-76736-2.rs
@@ -0,0 +1,20 @@
+// https://github.com/rust-lang/rust/issues/76736
+
+//@ aux-build:issue-76736-1.rs
+//@ aux-build:issue-76736-2.rs
+
+// https://github.com/rust-lang/rust/issues/124635
+
+#![crate_name = "foo"]
+#![feature(rustc_private)]
+
+extern crate issue_76736_1;
+extern crate issue_76736_2;
+
+//@ has foo/struct.Foo.html
+//@ !has - '//*[@class="impl"]//h3[@class="code-header"]' 'MaybeResult'
+pub struct Foo;
+
+//@ has foo/struct.Bar.html
+//@ !has - '//*[@class="impl"]//h3[@class="code-header"]' 'MaybeResult'
+pub use issue_76736_2::Bar;
diff --git a/tests/rustdoc/inline_cross/rustc-private-76736-3.rs b/tests/rustdoc/inline_cross/rustc-private-76736-3.rs
new file mode 100644
index 0000000000000..f9b46caa02ff6
--- /dev/null
+++ b/tests/rustdoc/inline_cross/rustc-private-76736-3.rs
@@ -0,0 +1,18 @@
+// https://github.com/rust-lang/rust/issues/76736
+
+//@ compile-flags: -Zforce-unstable-if-unmarked
+//@ aux-build:issue-76736-1.rs
+//@ aux-build:issue-76736-2.rs
+
+#![crate_name = "foo"]
+
+extern crate issue_76736_1;
+extern crate issue_76736_2;
+
+//@ has foo/struct.Foo.html
+//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'MaybeResult'
+pub struct Foo;
+
+//@ has foo/struct.Bar.html
+//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'MaybeResult'
+pub use issue_76736_2::Bar;
diff --git a/tests/rustdoc/inline_cross/rustc-private-76736-4.rs b/tests/rustdoc/inline_cross/rustc-private-76736-4.rs
new file mode 100644
index 0000000000000..511464f2c498f
--- /dev/null
+++ b/tests/rustdoc/inline_cross/rustc-private-76736-4.rs
@@ -0,0 +1,21 @@
+// https://github.com/rust-lang/rust/issues/76736
+
+//@ aux-build:issue-76736-1.rs
+//@ aux-build:issue-76736-2.rs
+
+// https://github.com/rust-lang/rust/issues/124635
+
+#![crate_name = "foo"]
+#![feature(rustc_private, staged_api)]
+#![unstable(feature = "rustc_private", issue = "none")]
+
+extern crate issue_76736_1;
+extern crate issue_76736_2;
+
+//@ has foo/struct.Foo.html
+//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'MaybeResult'
+pub struct Foo;
+
+//@ has foo/struct.Bar.html
+//@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'MaybeResult'
+pub use issue_76736_2::Bar;
diff --git a/tests/rustdoc/inline_cross/issue-24183.method_no_where_self_sized.html b/tests/rustdoc/inline_cross/self-sized-bounds-24183.method_no_where_self_sized.html
similarity index 100%
rename from tests/rustdoc/inline_cross/issue-24183.method_no_where_self_sized.html
rename to tests/rustdoc/inline_cross/self-sized-bounds-24183.method_no_where_self_sized.html
diff --git a/tests/rustdoc/inline_cross/self-sized-bounds-24183.rs b/tests/rustdoc/inline_cross/self-sized-bounds-24183.rs
new file mode 100644
index 0000000000000..909005532f505
--- /dev/null
+++ b/tests/rustdoc/inline_cross/self-sized-bounds-24183.rs
@@ -0,0 +1,19 @@
+// https://github.com/rust-lang/rust/issues/24183
+#![crate_type = "lib"]
+#![crate_name = "usr"]
+
+//@ aux-crate:issue_24183=issue-24183.rs
+//@ edition: 2021
+
+//@ has usr/trait.U.html
+//@ has - '//*[@class="rust item-decl"]' "pub trait U {"
+//@ has - '//*[@id="method.modified"]' \
+// "fn modified(self) -> Self\
+// where \
+//     Self: Sized"
+//@ snapshot method_no_where_self_sized - '//*[@id="method.touch"]/*[@class="code-header"]'
+pub use issue_24183::U;
+
+//@ has usr/trait.S.html
+//@ has - '//*[@class="rust item-decl"]' 'pub trait S: Sized {'
+pub use issue_24183::S;
diff --git a/tests/rustdoc/inline_local/doc-no-inline-32343.rs b/tests/rustdoc/inline_local/doc-no-inline-32343.rs
new file mode 100644
index 0000000000000..ed11614a50038
--- /dev/null
+++ b/tests/rustdoc/inline_local/doc-no-inline-32343.rs
@@ -0,0 +1,26 @@
+// https://github.com/rust-lang/rust/issues/32343
+#![crate_name="foobar"]
+
+//@ !has foobar/struct.Foo.html
+//@ has foobar/index.html
+//@ has - '//code' 'pub use foo::Foo'
+//@ !has - '//code/a' 'Foo'
+#[doc(no_inline)]
+pub use foo::Foo;
+
+//@ !has foobar/struct.Bar.html
+//@ has foobar/index.html
+//@ has - '//code' 'pub use foo::Bar'
+//@ has - '//code/a' 'Bar'
+#[doc(no_inline)]
+pub use foo::Bar;
+
+mod foo {
+    pub struct Foo;
+    pub struct Bar;
+}
+
+pub mod bar {
+    //@ has foobar/bar/struct.Bar.html
+    pub use ::foo::Bar;
+}
diff --git a/tests/rustdoc/inline_local/fully-stable-path-is-better.rs b/tests/rustdoc/inline_local/fully-stable-path-is-better.rs
new file mode 100644
index 0000000000000..41bf42d2e7aad
--- /dev/null
+++ b/tests/rustdoc/inline_local/fully-stable-path-is-better.rs
@@ -0,0 +1,40 @@
+//! Test case for [134702]
+//!
+//! [134702]: https://github.com/rust-lang/rust/issues/134702
+#![crate_name = "foo"]
+#![stable(since = "1.0", feature = "v1")]
+
+#![feature(staged_api, rustc_attrs)]
+
+#[stable(since = "1.0", feature = "stb1")]
+pub mod stb1 {
+    #[doc(inline)]
+    #[stable(since = "1.0", feature = "stb1")]
+    pub use crate::uns::Inside1;
+}
+
+#[unstable(feature = "uns", issue = "135003")]
+pub mod uns {
+    #[stable(since = "1.0", feature = "stb1")]
+    #[rustc_allowed_through_unstable_modules = "use stable path instead"]
+    pub struct Inside1;
+    #[stable(since = "1.0", feature = "stb2")]
+    #[rustc_allowed_through_unstable_modules = "use stable path instead"]
+    pub struct Inside2;
+}
+
+#[stable(since = "1.0", feature = "stb2")]
+pub mod stb2 {
+    #[doc(inline)]
+    #[stable(since = "1.0", feature = "stb2")]
+    pub use crate::uns::Inside2;
+}
+
+#[stable(since = "1.0", feature = "nested")]
+pub mod nested {
+    //! [Inside1] [Inside2]
+    //@ has foo/nested/index.html '//a[@href="../stb1/struct.Inside1.html"]' 'Inside1'
+    //@ has foo/nested/index.html '//a[@href="../stb2/struct.Inside2.html"]' 'Inside2'
+    use crate::stb1::Inside1;
+    use crate::stb2::Inside2;
+}
diff --git a/tests/rustdoc/inline_local/issue-28537.rs b/tests/rustdoc/inline_local/issue-28537.rs
deleted file mode 100644
index d5ba94d2e6c9e..0000000000000
--- a/tests/rustdoc/inline_local/issue-28537.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-#[doc(hidden)]
-pub mod foo {
-    pub struct Foo;
-}
-
-mod bar {
-    pub use self::bar::Bar;
-    mod bar {
-        pub struct Bar;
-    }
-}
-
-//@ has issue_28537/struct.Foo.html
-pub use foo::Foo;
-
-//@ has issue_28537/struct.Bar.html
-pub use self::bar::Bar;
diff --git a/tests/rustdoc/inline_local/issue-32343.rs b/tests/rustdoc/inline_local/issue-32343.rs
deleted file mode 100644
index 2ec123fdc5cfe..0000000000000
--- a/tests/rustdoc/inline_local/issue-32343.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-//@ !has issue_32343/struct.Foo.html
-//@ has issue_32343/index.html
-//@ has - '//code' 'pub use foo::Foo'
-//@ !has - '//code/a' 'Foo'
-#[doc(no_inline)]
-pub use foo::Foo;
-
-//@ !has issue_32343/struct.Bar.html
-//@ has issue_32343/index.html
-//@ has - '//code' 'pub use foo::Bar'
-//@ has - '//code/a' 'Bar'
-#[doc(no_inline)]
-pub use foo::Bar;
-
-mod foo {
-    pub struct Foo;
-    pub struct Bar;
-}
-
-pub mod bar {
-    //@ has issue_32343/bar/struct.Bar.html
-    pub use ::foo::Bar;
-}
diff --git a/tests/rustdoc/inline_local/pub-re-export-28537.rs b/tests/rustdoc/inline_local/pub-re-export-28537.rs
new file mode 100644
index 0000000000000..0e9836c7ceeab
--- /dev/null
+++ b/tests/rustdoc/inline_local/pub-re-export-28537.rs
@@ -0,0 +1,20 @@
+// https://github.com/rust-lang/rust/issues/28537
+#![crate_name="foo"]
+
+#[doc(hidden)]
+pub mod foo {
+    pub struct Foo;
+}
+
+mod bar {
+    pub use self::bar::Bar;
+    mod bar {
+        pub struct Bar;
+    }
+}
+
+//@ has foo/struct.Foo.html
+pub use foo::Foo;
+
+//@ has foo/struct.Bar.html
+pub use self::bar::Bar;
diff --git a/tests/rustdoc/inline_local/staged-inline.rs b/tests/rustdoc/inline_local/staged-inline.rs
new file mode 100644
index 0000000000000..f2131ad5f9442
--- /dev/null
+++ b/tests/rustdoc/inline_local/staged-inline.rs
@@ -0,0 +1,18 @@
+// https://github.com/rust-lang/rust/issues/135078
+#![crate_name = "foo"]
+#![feature(staged_api)]
+#![stable(feature = "v1", since="1.0.0")]
+
+#[stable(feature = "v1", since="1.0.0")]
+pub mod ffi {
+    #[stable(feature = "core_ffi", since="1.99.0")]
+    //@ has "foo/ffi/struct.CStr.html" "//span[@class='sub-heading']/span[@class='since']" "1.99.0"
+    //@ !has - "//span[@class='sub-heading']/span[@class='since']" "1.0.0"
+    pub struct CStr;
+}
+
+#[stable(feature = "v1", since = "1.0.0")]
+#[doc(inline)]
+//@ has "foo/struct.CStr.html" "//span[@class='sub-heading']/span[@class='since']" "1.0.0"
+//@ !has - "//span[@class='sub-heading']/span[@class='since']" "1.99.0"
+pub use ffi::CStr;
diff --git a/tests/rustdoc/internal.rs b/tests/rustdoc/internal.rs
index e0bccefda1d1c..244e9138f2ba0 100644
--- a/tests/rustdoc/internal.rs
+++ b/tests/rustdoc/internal.rs
@@ -8,7 +8,7 @@
 //@ !matches internal/index.html \
 //      '//*[@class="desc docblock-short"]/span[@class="stab internal"]' \
 //      ''
-//@ matches - '//*[@class="desc docblock-short"]' 'Docs'
+//@ matches - '//dd' 'Docs'
 
 //@ !has internal/struct.S.html '//*[@class="stab unstable"]' ''
 //@ !has internal/struct.S.html '//*[@class="stab internal"]' ''
diff --git a/tests/rustdoc/intra-doc/enum-self-82209.rs b/tests/rustdoc/intra-doc/enum-self-82209.rs
new file mode 100644
index 0000000000000..615115a5f8b2c
--- /dev/null
+++ b/tests/rustdoc/intra-doc/enum-self-82209.rs
@@ -0,0 +1,13 @@
+// https://github.com/rust-lang/rust/issues/82209
+
+#![crate_name = "foo"]
+#![deny(rustdoc::broken_intra_doc_links)]
+pub enum Foo {
+    Bar {
+        abc: i32,
+        /// [Self::Bar::abc]
+        xyz: i32,
+    },
+}
+
+//@ has foo/enum.Foo.html '//a/@href' 'enum.Foo.html#variant.Bar.field.abc'
diff --git a/tests/rustdoc/intra-doc/issue-103463.rs b/tests/rustdoc/intra-doc/issue-103463.rs
deleted file mode 100644
index 9b5cb67fd32a6..0000000000000
--- a/tests/rustdoc/intra-doc/issue-103463.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-// The `Trait` is not pulled into the crate resulting in doc links in its methods being resolved.
-
-//@ aux-build:issue-103463-aux.rs
-
-extern crate issue_103463_aux;
-use issue_103463_aux::Trait;
-
-fn main() {}
diff --git a/tests/rustdoc/intra-doc/issue-104145.rs b/tests/rustdoc/intra-doc/issue-104145.rs
deleted file mode 100644
index 5690803af5ae9..0000000000000
--- a/tests/rustdoc/intra-doc/issue-104145.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-// Doc links in `Trait`'s methods are resolved because it has a local impl.
-
-//@ aux-build:issue-103463-aux.rs
-
-extern crate issue_103463_aux;
-use issue_103463_aux::Trait;
-
-pub struct LocalType;
-
-impl Trait for LocalType {
-    fn method() {}
-}
-
-fn main() {}
diff --git a/tests/rustdoc/intra-doc/issue-108459.rs b/tests/rustdoc/intra-doc/issue-108459.rs
deleted file mode 100644
index 18424c069d359..0000000000000
--- a/tests/rustdoc/intra-doc/issue-108459.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-#![deny(rustdoc::broken_intra_doc_links)]
-#![allow(rustdoc::redundant_explicit_links)]
-
-pub struct S;
-pub mod char {}
-
-// Ensure this doesn't ICE due to trying to slice off non-existent backticks from "S"
-
-/// See [S] and [`S`]
-pub struct MyStruct1;
-
-// Ensure that link texts are replaced correctly even if there are multiple links with
-// the same target but different text
-
-/// See also [crate::char] and [mod@char] and [prim@char]
-//@ has issue_108459/struct.MyStruct2.html '//*[@href="char/index.html"]' 'crate::char'
-//@ has - '//*[@href="char/index.html"]' 'char'
-//@ has - '//*[@href="{{channel}}/std/primitive.char.html"]' 'char'
-pub struct MyStruct2;
-
-/// See also [mod@char] and [prim@char] and [crate::char]
-//@ has issue_108459/struct.MyStruct3.html '//*[@href="char/index.html"]' 'crate::char'
-//@ has - '//*[@href="char/index.html"]' 'char'
-//@ has - '//*[@href="{{channel}}/std/primitive.char.html"]' 'char'
-pub struct MyStruct3;
-
-// Ensure that links are correct even if there are multiple links with the same text but
-// different targets
-
-/// See also [char][mod@char] and [char][prim@char]
-//@ has issue_108459/struct.MyStruct4.html '//*[@href="char/index.html"]' 'char'
-//@ has - '//*[@href="{{channel}}/std/primitive.char.html"]' 'char'
-pub struct MyStruct4;
-
-/// See also [char][prim@char] and [char][crate::char]
-//@ has issue_108459/struct.MyStruct5.html '//*[@href="char/index.html"]' 'char'
-//@ has - '//*[@href="{{channel}}/std/primitive.char.html"]' 'char'
-pub struct MyStruct5;
diff --git a/tests/rustdoc/intra-doc/issue-66159.rs b/tests/rustdoc/intra-doc/issue-66159.rs
deleted file mode 100644
index 5d50f63f299f9..0000000000000
--- a/tests/rustdoc/intra-doc/issue-66159.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-//@ aux-crate:priv:pub_struct=pub-struct.rs
-//@ compile-flags:-Z unstable-options
-
-// The issue was an ICE which meant that we never actually generated the docs
-// so if we have generated the docs, we're okay.
-// Since we don't generate the docs for the auxiliary files, we can't actually
-// verify that the struct is linked correctly.
-
-//@ has issue_66159/index.html
-//! [pub_struct::SomeStruct]
diff --git a/tests/rustdoc/intra-doc/issue-82209.rs b/tests/rustdoc/intra-doc/issue-82209.rs
deleted file mode 100644
index 46d028e535c2f..0000000000000
--- a/tests/rustdoc/intra-doc/issue-82209.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-#![crate_name = "foo"]
-#![deny(rustdoc::broken_intra_doc_links)]
-pub enum Foo {
-    Bar {
-        abc: i32,
-        /// [Self::Bar::abc]
-        xyz: i32,
-    },
-}
-
-//@ has foo/enum.Foo.html '//a/@href' 'enum.Foo.html#variant.Bar.field.abc'
diff --git a/tests/rustdoc/intra-doc/link-same-name-different-disambiguator-108459.rs b/tests/rustdoc/intra-doc/link-same-name-different-disambiguator-108459.rs
new file mode 100644
index 0000000000000..0b339eaf6b27c
--- /dev/null
+++ b/tests/rustdoc/intra-doc/link-same-name-different-disambiguator-108459.rs
@@ -0,0 +1,41 @@
+// https://github.com/rust-lang/rust/pull/108459
+#![crate_name="foobar"]
+
+#![deny(rustdoc::broken_intra_doc_links)]
+#![allow(rustdoc::redundant_explicit_links)]
+
+pub struct S;
+pub mod char {}
+
+// Ensure this doesn't ICE due to trying to slice off non-existent backticks from "S"
+
+/// See [S] and [`S`]
+pub struct MyStruct1;
+
+// Ensure that link texts are replaced correctly even if there are multiple links with
+// the same target but different text
+
+/// See also [crate::char] and [mod@char] and [prim@char]
+//@ has foobar/struct.MyStruct2.html '//*[@href="char/index.html"]' 'crate::char'
+//@ has - '//*[@href="char/index.html"]' 'char'
+//@ has - '//*[@href="{{channel}}/std/primitive.char.html"]' 'char'
+pub struct MyStruct2;
+
+/// See also [mod@char] and [prim@char] and [crate::char]
+//@ has foobar/struct.MyStruct3.html '//*[@href="char/index.html"]' 'crate::char'
+//@ has - '//*[@href="char/index.html"]' 'char'
+//@ has - '//*[@href="{{channel}}/std/primitive.char.html"]' 'char'
+pub struct MyStruct3;
+
+// Ensure that links are correct even if there are multiple links with the same text but
+// different targets
+
+/// See also [char][mod@char] and [char][prim@char]
+//@ has foobar/struct.MyStruct4.html '//*[@href="char/index.html"]' 'char'
+//@ has - '//*[@href="{{channel}}/std/primitive.char.html"]' 'char'
+pub struct MyStruct4;
+
+/// See also [char][prim@char] and [char][crate::char]
+//@ has foobar/struct.MyStruct5.html '//*[@href="char/index.html"]' 'char'
+//@ has - '//*[@href="{{channel}}/std/primitive.char.html"]' 'char'
+pub struct MyStruct5;
diff --git a/tests/rustdoc/intra-doc/module-scope-name-resolution-55364.rs b/tests/rustdoc/intra-doc/module-scope-name-resolution-55364.rs
index 06cb764423e14..f0362f684ad00 100644
--- a/tests/rustdoc/intra-doc/module-scope-name-resolution-55364.rs
+++ b/tests/rustdoc/intra-doc/module-scope-name-resolution-55364.rs
@@ -32,8 +32,8 @@ pub mod subone {
 //@ has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo'
 //@ has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar'
 // Though there should be such links later
-//@ has - '//section[@id="main-content"]/ul[@class="item-table"]//div[@class="item-name"]/a[@class="fn"][@href="fn.foo.html"]' 'foo'
-//@ has - '//section[@id="main-content"]/ul[@class="item-table"]//div[@class="item-name"]/a[@class="fn"][@href="fn.bar.html"]' 'bar'
+//@ has - '//section[@id="main-content"]/dl[@class="item-table"]/dt/a[@class="fn"][@href="fn.foo.html"]' 'foo'
+//@ has - '//section[@id="main-content"]/dl[@class="item-table"]/dt/a[@class="fn"][@href="fn.bar.html"]' 'bar'
 /// See either [foo] or [bar].
 pub mod subtwo {
 
@@ -71,8 +71,8 @@ pub mod subthree {
 // Next we go *deeper* - In order to ensure it's not just "this or parent"
 // we test `crate::` and a `super::super::...` chain
 //@ has foo/subfour/subfive/subsix/subseven/subeight/index.html
-//@ has - '//section[@id="main-content"]/ul[@class="item-table"]//div[@class="desc docblock-short"]//a[@href="../../../../../subone/fn.foo.html"]' 'other foo'
-//@ has - '//section[@id="main-content"]/ul[@class="item-table"]//div[@class="desc docblock-short"]//a[@href="../../../../../subtwo/fn.bar.html"]' 'other bar'
+//@ has - '//section[@id="main-content"]/dl[@class="item-table"]/dd//a[@href="../../../../../subone/fn.foo.html"]' 'other foo'
+//@ has - '//section[@id="main-content"]/dl[@class="item-table"]/dd//a[@href="../../../../../subtwo/fn.bar.html"]' 'other bar'
 pub mod subfour {
     pub mod subfive {
         pub mod subsix {
diff --git a/tests/rustdoc/intra-doc/same-name-different-crates-66159.rs b/tests/rustdoc/intra-doc/same-name-different-crates-66159.rs
new file mode 100644
index 0000000000000..7e3ace9355aeb
--- /dev/null
+++ b/tests/rustdoc/intra-doc/same-name-different-crates-66159.rs
@@ -0,0 +1,13 @@
+// https://github.com/rust-lang/rust/issues/66159
+#![crate_name="foobar"]
+
+//@ aux-crate:priv:pub_struct=pub-struct.rs
+//@ compile-flags:-Z unstable-options
+
+// The issue was an ICE which meant that we never actually generated the docs
+// so if we have generated the docs, we're okay.
+// Since we don't generate the docs for the auxiliary files, we can't actually
+// verify that the struct is linked correctly.
+
+//@ has foobar/index.html
+//! [pub_struct::SomeStruct]
diff --git a/tests/rustdoc/item-desc-list-at-start.item-table.html b/tests/rustdoc/item-desc-list-at-start.item-table.html
index cff4f816529c3..89b4ac640f246 100644
--- a/tests/rustdoc/item-desc-list-at-start.item-table.html
+++ b/tests/rustdoc/item-desc-list-at-start.item-table.html
@@ -1 +1 @@
-
\ No newline at end of file
+
MY_CONSTANT
Groups: SamplePatternSGIS, SamplePatternEXT
\ No newline at end of file diff --git a/tests/rustdoc/item-desc-list-at-start.rs b/tests/rustdoc/item-desc-list-at-start.rs index fbcc36066f154..7c2b31c9460a3 100644 --- a/tests/rustdoc/item-desc-list-at-start.rs +++ b/tests/rustdoc/item-desc-list-at-start.rs @@ -1,7 +1,8 @@ //@ has item_desc_list_at_start/index.html -//@ count - '//ul[@class="item-table"]/li/div/li' 0 -//@ count - '//ul[@class="item-table"]/li' 1 -//@ snapshot item-table - '//ul[@class="item-table"]' +//@ count - '//dl[@class="item-table"]/dd//ul' 0 +//@ count - '//dl[@class="item-table"]/dd//li' 0 +//@ count - '//dl[@class="item-table"]/dd' 1 +//@ snapshot item-table - '//dl[@class="item-table"]' // based on https://docs.rs/gl_constants/0.1.1/src/gl_constants/lib.rs.html#16 diff --git a/tests/rustdoc/jump-to-def-pats.rs b/tests/rustdoc/jump-to-def-pats.rs new file mode 100644 index 0000000000000..147902b44cf5c --- /dev/null +++ b/tests/rustdoc/jump-to-def-pats.rs @@ -0,0 +1,52 @@ +// This test ensures that patterns also get a link generated. + +//@ compile-flags: -Zunstable-options --generate-link-to-definition + +#![crate_name = "foo"] + +//@ has 'src/foo/jump-to-def-pats.rs.html' + +use std::fmt; + +pub enum MyEnum { + Ok(T), + Err(E), + Some(T), + None, +} + +pub enum X { + A, +} + +pub fn foo() -> Result<(), ()> { + // FIXME: would be nice to be able to check both the class and the href at the same time so + // we could check the text as well... + //@ has - '//a[@class="prelude-val"]/@href' '{{channel}}/core/result/enum.Result.html#variant.Ok' + //@ has - '//a[@href="{{channel}}/core/result/enum.Result.html#variant.Ok"]' 'Ok' + Ok(()) +} + +impl fmt::Display for MyEnum { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + //@ has - '//a[@href="#12"]' 'Self::Ok' + Self::Ok(_) => f.write_str("MyEnum::Ok"), + //@ has - '//a[@href="#13"]' 'MyEnum::Err' + MyEnum::Err(_) => f.write_str("MyEnum::Err"), + //@ has - '//a[@href="#14"]' 'Self::Some' + Self::Some(_) => f.write_str("MyEnum::Some"), + //@ has - '//a[@href="#15"]' 'Self::None' + Self::None => f.write_str("MyEnum::None"), + } + } +} + +impl X { + fn p(&self) -> &str { + match self { + //@ has - '//a[@href="#19"]' 'Self::A' + Self::A => "X::A", + } + } +} diff --git a/tests/rustdoc/macro-higher-kinded-function.rs b/tests/rustdoc/macro-higher-kinded-function.rs index f14125d13091c..738ea8fb3f153 100644 --- a/tests/rustdoc/macro-higher-kinded-function.rs +++ b/tests/rustdoc/macro-higher-kinded-function.rs @@ -11,10 +11,10 @@ macro_rules! gen { } //@ has 'foo/struct.Providers.html' -//@ has - '//*[@class="rust item-decl"]//code' "pub a: for<'tcx> fn(_: TyCtxt<'tcx>, _: u8) -> i8," -//@ has - '//*[@class="rust item-decl"]//code' "pub b: for<'tcx> fn(_: TyCtxt<'tcx>, _: u16) -> i16," -//@ has - '//*[@id="structfield.a"]/code' "a: for<'tcx> fn(_: TyCtxt<'tcx>, _: u8) -> i8" -//@ has - '//*[@id="structfield.b"]/code' "b: for<'tcx> fn(_: TyCtxt<'tcx>, _: u16) -> i16" +//@ has - '//*[@class="rust item-decl"]//code' "pub a: for<'tcx> fn(TyCtxt<'tcx>, u8) -> i8," +//@ has - '//*[@class="rust item-decl"]//code' "pub b: for<'tcx> fn(TyCtxt<'tcx>, u16) -> i16," +//@ has - '//*[@id="structfield.a"]/code' "a: for<'tcx> fn(TyCtxt<'tcx>, u8) -> i8" +//@ has - '//*[@id="structfield.b"]/code' "b: for<'tcx> fn(TyCtxt<'tcx>, u16) -> i16" gen! { (a, 'tcx, [u8], [i8]) (b, 'tcx, [u16], [i16]) diff --git a/tests/rustdoc/macro-rules-broken-intra-doc-106142.rs b/tests/rustdoc/macro-rules-broken-intra-doc-106142.rs deleted file mode 100644 index 0d146a3c5cd0c..0000000000000 --- a/tests/rustdoc/macro-rules-broken-intra-doc-106142.rs +++ /dev/null @@ -1,17 +0,0 @@ -// https://github.com/rust-lang/rust/issues/106142 -#![crate_name="foo"] - -//@ has 'foo/a/index.html' -//@ count 'foo/a/index.html' '//ul[@class="item-table"]//li//a' 1 - -#![allow(rustdoc::broken_intra_doc_links)] - -pub mod a { - /// [`m`] - pub fn f() {} - - #[macro_export] - macro_rules! m { - () => {}; - } -} diff --git a/tests/rustdoc/multiple-mods-w-same-name-doc-inline-83375.rs b/tests/rustdoc/multiple-mods-w-same-name-doc-inline-83375.rs index 6d255ed600407..be32fcc7e4a42 100644 --- a/tests/rustdoc/multiple-mods-w-same-name-doc-inline-83375.rs +++ b/tests/rustdoc/multiple-mods-w-same-name-doc-inline-83375.rs @@ -10,7 +10,7 @@ pub mod sub { } //@ count foo/index.html '//a[@class="mod"][@title="mod foo::prelude"]' 1 -//@ count foo/prelude/index.html '//div[@class="item-row"]' 0 +//@ count foo/prelude/index.html '//ul[@class="item-table"]' 0 pub mod prelude {} #[doc(inline)] diff --git a/tests/rustdoc/multiple-mods-w-same-name-doc-inline-last-item-83375.rs b/tests/rustdoc/multiple-mods-w-same-name-doc-inline-last-item-83375.rs index a59b48232a347..4b3b467382b55 100644 --- a/tests/rustdoc/multiple-mods-w-same-name-doc-inline-last-item-83375.rs +++ b/tests/rustdoc/multiple-mods-w-same-name-doc-inline-last-item-83375.rs @@ -13,5 +13,5 @@ pub mod sub { pub use sub::*; //@ count foo/index.html '//a[@class="mod"][@title="mod foo::prelude"]' 1 -//@ count foo/prelude/index.html '//div[@class="item-row"]' 0 +//@ count foo/prelude/index.html '//ul[@class="item-table"]' 0 pub mod prelude {} diff --git a/tests/rustdoc/nested-items-issue-111415.rs b/tests/rustdoc/nested-items-issue-111415.rs index a5cd3ca0b1ae0..79dc2b0378ff7 100644 --- a/tests/rustdoc/nested-items-issue-111415.rs +++ b/tests/rustdoc/nested-items-issue-111415.rs @@ -10,7 +10,7 @@ //@ has - '//*[@id="main-content"]/*[@class="section-header"]' 'Functions' //@ has - '//*[@id="main-content"]/*[@class="section-header"]' 'Traits' // Checking that there are only three items. -//@ count - '//*[@id="main-content"]//*[@class="item-name"]' 3 +//@ count - '//*[@id="main-content"]//dt' 3 //@ has - '//*[@id="main-content"]//a[@href="struct.Bar.html"]' 'Bar' //@ has - '//*[@id="main-content"]//a[@href="fn.foo.html"]' 'foo' //@ has - '//*[@id="main-content"]//a[@href="trait.Foo.html"]' 'Foo' diff --git a/tests/rustdoc/overlapping-reexport-105735-2.rs b/tests/rustdoc/overlapping-reexport-105735-2.rs index 9f823ec5923cd..fa43924ff4ebf 100644 --- a/tests/rustdoc/overlapping-reexport-105735-2.rs +++ b/tests/rustdoc/overlapping-reexport-105735-2.rs @@ -5,8 +5,8 @@ #![no_std] //@ has 'foo/index.html' -//@ has - '//*[@class="item-name"]/a[@class="type"]' 'AtomicU8' -//@ has - '//*[@class="item-name"]/a[@class="constant"]' 'AtomicU8' +//@ has - '//dt/a[@class="type"]' 'AtomicU8' +//@ has - '//dt/a[@class="constant"]' 'AtomicU8' // We also ensure we don't have another item displayed. //@ count - '//*[@id="main-content"]/*[@class="section-header"]' 2 //@ has - '//*[@id="main-content"]/*[@class="section-header"]' 'Type Aliases' diff --git a/tests/rustdoc/overlapping-reexport-105735.rs b/tests/rustdoc/overlapping-reexport-105735.rs index 2a2d0fa98309e..d1b5c0b6749ad 100644 --- a/tests/rustdoc/overlapping-reexport-105735.rs +++ b/tests/rustdoc/overlapping-reexport-105735.rs @@ -5,8 +5,8 @@ #![no_std] //@ has 'foo/index.html' -//@ has - '//*[@class="item-name"]/a[@class="struct"]' 'AtomicU8' -//@ has - '//*[@class="item-name"]/a[@class="constant"]' 'AtomicU8' +//@ has - '//dt/a[@class="struct"]' 'AtomicU8' +//@ has - '//dt/a[@class="constant"]' 'AtomicU8' // We also ensure we don't have another item displayed. //@ count - '//*[@id="main-content"]/*[@class="section-header"]' 2 //@ has - '//*[@id="main-content"]/*[@class="section-header"]' 'Structs' diff --git a/tests/rustdoc/pub-use-root-path-95873.rs b/tests/rustdoc/pub-use-root-path-95873.rs index e3d5ee6e31581..8e4fd9e8d5081 100644 --- a/tests/rustdoc/pub-use-root-path-95873.rs +++ b/tests/rustdoc/pub-use-root-path-95873.rs @@ -1,5 +1,5 @@ // https://github.com/rust-lang/rust/issues/95873 #![crate_name = "foo"] -//@ has foo/index.html "//*[@class='item-name']" "pub use ::std as x;" +//@ has foo/index.html "//dt" "pub use ::std as x;" pub use ::std as x; diff --git a/tests/rustdoc/reexport-cfg.rs b/tests/rustdoc/reexport-cfg.rs index 7270da3d678f8..73b6682431663 100644 --- a/tests/rustdoc/reexport-cfg.rs +++ b/tests/rustdoc/reexport-cfg.rs @@ -13,18 +13,18 @@ mod foo { } //@ has 'foo/index.html' -//@ has - '//*[@class="item-name"]' 'BabarNon-lie' +//@ has - '//dt' 'BabarNon-lie' #[cfg(not(feature = "lie"))] pub use crate::foo::Bar as Babar; -//@ has - '//*[@class="item-name"]' 'Babar2Non-cake' +//@ has - '//dt' 'Babar2Non-cake' #[doc(cfg(not(feature = "cake")))] pub use crate::foo::Bar2 as Babar2; -//@ has - '//*[@class="item-table"]/li' 'pub use crate::Babar as Elephant;Non-robot' +//@ has - '//*[@class="item-table reexports"]/dt' 'pub use crate::Babar as Elephant;Non-robot' #[cfg(not(feature = "robot"))] pub use crate::Babar as Elephant; -//@ has - '//*[@class="item-table"]/li' 'pub use crate::Babar2 as Elephant2;Non-cat' +//@ has - '//*[@class="item-table reexports"]/dt' 'pub use crate::Babar2 as Elephant2;Non-cat' #[doc(cfg(not(feature = "cat")))] pub use crate::Babar2 as Elephant2; diff --git a/tests/rustdoc/reexport-check.rs b/tests/rustdoc/reexport-check.rs index 0f4e203d1d3b7..fc10e3aadd02b 100644 --- a/tests/rustdoc/reexport-check.rs +++ b/tests/rustdoc/reexport-check.rs @@ -8,13 +8,13 @@ extern crate reexport_check; #[allow(deprecated, deprecated_in_future)] pub use std::i32; //@ !has 'foo/index.html' '//code' 'pub use self::string::String;' -//@ has 'foo/index.html' '//div[@class="item-name"]' 'String' +//@ has 'foo/index.html' '//dt' 'String' pub use std::string::String; // i32 is deprecated, String is not //@ count 'foo/index.html' '//span[@class="stab deprecated"]' 1 -//@ has 'foo/index.html' '//div[@class="desc docblock-short"]' 'Docs in original' +//@ has 'foo/index.html' '//dd' 'Docs in original' // this is a no-op, but shows what happens if there's an attribute that isn't a doc-comment #[doc(inline)] pub use reexport_check::S; diff --git a/tests/rustdoc/reexport-doc-hidden-inside-private.rs b/tests/rustdoc/reexport-doc-hidden-inside-private.rs index fac928fc2a391..8e194ef74fb82 100644 --- a/tests/rustdoc/reexport-doc-hidden-inside-private.rs +++ b/tests/rustdoc/reexport-doc-hidden-inside-private.rs @@ -12,5 +12,5 @@ mod private_module { //@ has - '//*[@id="reexport.Foo"]/code' 'pub use crate::private_module::Public as Foo;' pub use crate::private_module::Public as Foo; // Glob re-exports with no visible items should not be displayed. -//@ count - '//*[@class="item-table"]/li' 1 +//@ count - '//*[@class="item-table reexports"]/dt' 1 pub use crate::private_module::*; diff --git a/tests/rustdoc/reexport-of-reexport-108679.rs b/tests/rustdoc/reexport-of-reexport-108679.rs index 5c1b4bcbd8384..0d2faf71d3267 100644 --- a/tests/rustdoc/reexport-of-reexport-108679.rs +++ b/tests/rustdoc/reexport-of-reexport-108679.rs @@ -25,5 +25,6 @@ pub mod a { //@ has - '//*[@id="main-content"]//*[@id="reexport.A"]' 'pub use self::a::A;' //@ has - '//*[@id="main-content"]//*[@id="reexport.B"]' 'pub use self::a::B;' // Should only contain "Modules" and "Re-exports". -//@ count - '//*[@id="main-content"]//*[@class="item-table"]' 2 +//@ count - '//*[@id="main-content"]//*[@class="item-table"]' 1 +//@ count - '//*[@id="main-content"]//*[@class="item-table reexports"]' 1 pub use self::a::{A, B}; diff --git a/tests/rustdoc/reexport-trait-from-hidden-111064.rs b/tests/rustdoc/reexport-trait-from-hidden-111064.rs index 84ec818ef338f..8b9ad7616ea20 100644 --- a/tests/rustdoc/reexport-trait-from-hidden-111064.rs +++ b/tests/rustdoc/reexport-trait-from-hidden-111064.rs @@ -5,7 +5,7 @@ #![crate_name = "foo"] //@ has 'foo/index.html' -//@ has - '//*[@id="main-content"]//*[@class="item-name"]/a[@href="trait.Foo.html"]' 'Foo' +//@ has - '//*[@id="main-content"]//dt/a[@href="trait.Foo.html"]' 'Foo' //@ has 'foo/trait.Foo.html' //@ has - '//*[@id="main-content"]//*[@class="code-header"]' 'fn test()' diff --git a/tests/rustdoc/short-docblock.rs b/tests/rustdoc/short-docblock.rs index c80a5025ebecc..fa0af85696ac4 100644 --- a/tests/rustdoc/short-docblock.rs +++ b/tests/rustdoc/short-docblock.rs @@ -1,7 +1,7 @@ #![crate_name = "foo"] -//@ has foo/index.html '//*[@class="desc docblock-short"]' 'fooo' -//@ !has foo/index.html '//*[@class="desc docblock-short"]/h1' 'fooo' +//@ has foo/index.html '//dd' 'fooo' +//@ !has foo/index.html '//dd//h1' 'fooo' //@ has foo/fn.foo.html '//h2[@id="fooo"]' 'fooo' //@ has foo/fn.foo.html '//h2[@id="fooo"]/a[@href="#fooo"]' '§' @@ -10,8 +10,8 @@ /// foo pub fn foo() {} -//@ has foo/index.html '//*[@class="desc docblock-short"]' 'mooood' -//@ !has foo/index.html '//*[@class="desc docblock-short"]/h2' 'mooood' +//@ has foo/index.html '//dd' 'mooood' +//@ !has foo/index.html '//dd//h2' 'mooood' //@ has foo/foo/index.html '//h3[@id="mooood"]' 'mooood' //@ has foo/foo/index.html '//h3[@id="mooood"]/a[@href="#mooood"]' '§' @@ -20,8 +20,7 @@ pub fn foo() {} /// foo mod pub mod foo {} -//@ has foo/index.html '//*[@class="desc docblock-short"]/a[@href=\ -// "https://nougat.world"]/code' 'nougat' +//@ has foo/index.html '//dd/a[@href="https://nougat.world"]/code' 'nougat' /// [`nougat`](https://nougat.world) pub struct Bar; diff --git a/tests/rustdoc/sidebar/sidebar-items.rs b/tests/rustdoc/sidebar/sidebar-items.rs index 57c2eee91a92a..6e13457796e5e 100644 --- a/tests/rustdoc/sidebar/sidebar-items.rs +++ b/tests/rustdoc/sidebar/sidebar-items.rs @@ -26,7 +26,7 @@ pub trait Foo { } //@ has foo/trait.DynCompatible.html -//@ !has - '//div[@class="sidebar-elems"]//h3/a[@href="#object-safety"]' '' +//@ !has - '//div[@class="sidebar-elems"]//h3/a[@href="#dyn-compatibility"]' '' pub trait DynCompatible { fn access(&self); } diff --git a/tests/rustdoc/source-line-numbers.rs b/tests/rustdoc/source-line-numbers.rs new file mode 100644 index 0000000000000..0b654b1a00476 --- /dev/null +++ b/tests/rustdoc/source-line-numbers.rs @@ -0,0 +1,35 @@ +// This test ensures that we have the expected number of line generated. + +#![crate_name = "foo"] + +//@ has 'src/foo/source-line-numbers.rs.html' +//@ count - '//a[@data-nosnippet]' 35 +//@ has - '//a[@id="35"]' '35' + +#[ +macro_export +] +macro_rules! bar { + ($x:ident) => {{ + $x += 2; + $x *= 2; + }} +} + +/* +multi line +comment +*/ +fn x(_: u8, _: u8) {} + +fn foo() { + let mut y = 0; + bar!(y); + println!(" + {y} + "); + x( + 1, + 2, + ); +} diff --git a/tests/rustdoc/stability.rs b/tests/rustdoc/stability.rs index 550eb0bc13776..22cd4b9cd5952 100644 --- a/tests/rustdoc/stability.rs +++ b/tests/rustdoc/stability.rs @@ -5,9 +5,9 @@ #![stable(feature = "core", since = "1.6.0")] //@ has stability/index.html -//@ has - '//ul[@class="item-table"]/li[1]//a' AaStable -//@ has - '//ul[@class="item-table"]/li[2]//a' ZzStable -//@ has - '//ul[@class="item-table"]/li[3]//a' Unstable +//@ has - '//dl[@class="item-table"]/dt[1]//a' AaStable +//@ has - '//dl[@class="item-table"]/dt[2]//a' ZzStable +//@ has - '//dl[@class="item-table"]/dt[3]//a' Unstable #[stable(feature = "rust2", since = "2.2.2")] pub struct AaStable; @@ -85,7 +85,7 @@ pub mod stable_later { } #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_allowed_through_unstable_modules] +#[rustc_allowed_through_unstable_modules = "use stable path instead"] pub mod stable_earlier1 { //@ has stability/stable_earlier1/struct.StableInUnstable.html \ // '//div[@class="main-heading"]//span[@class="since"]' '1.0.0' diff --git a/tests/rustdoc/staged-api-deprecated-unstable-32374.rs b/tests/rustdoc/staged-api-deprecated-unstable-32374.rs index 556b6fb61acd5..1021ce86df08f 100644 --- a/tests/rustdoc/staged-api-deprecated-unstable-32374.rs +++ b/tests/rustdoc/staged-api-deprecated-unstable-32374.rs @@ -4,11 +4,9 @@ #![unstable(feature = "test", issue = "32374")] #![crate_name="issue_32374"] -//@ matches issue_32374/index.html '//*[@class="item-name"]/span[@class="stab deprecated"]' \ -// 'Deprecated' -//@ matches issue_32374/index.html '//*[@class="item-name"]/span[@class="stab unstable"]' \ -// 'Experimental' -//@ matches issue_32374/index.html '//*[@class="desc docblock-short"]/text()' 'Docs' +//@ matches issue_32374/index.html '//dt/span[@class="stab deprecated"]' 'Deprecated' +//@ matches issue_32374/index.html '//dt/span[@class="stab unstable"]' 'Experimental' +//@ matches issue_32374/index.html '//dd/text()' 'Docs' //@ has issue_32374/struct.T.html '//*[@class="stab deprecated"]/span' '👎' //@ has issue_32374/struct.T.html '//*[@class="stab deprecated"]/span' \ diff --git a/tests/rustdoc/summary-header-46377.rs b/tests/rustdoc/summary-header-46377.rs index 11445f0dad6e1..c84f3a65cfbf7 100644 --- a/tests/rustdoc/summary-header-46377.rs +++ b/tests/rustdoc/summary-header-46377.rs @@ -1,6 +1,6 @@ // https://github.com/rust-lang/rust/issues/46377 #![crate_name="foo"] -//@ has 'foo/index.html' '//*[@class="desc docblock-short"]' 'Check out this struct!' +//@ has 'foo/index.html' '//dd' 'Check out this struct!' /// # Check out this struct! pub struct SomeStruct; diff --git a/tests/rustdoc/type-alias/deref-32077.rs b/tests/rustdoc/type-alias/deref-32077.rs index 79a833813406e..faab4b6f522ba 100644 --- a/tests/rustdoc/type-alias/deref-32077.rs +++ b/tests/rustdoc/type-alias/deref-32077.rs @@ -19,8 +19,8 @@ impl Foo for GenericStruct {} impl Bar for GenericStruct {} //@ has 'foo/type.TypedefStruct.html' -// We check that "Aliased type" is also present as a title in the sidebar. -//@ has - '//*[@class="sidebar-elems"]//h3/a[@href="#aliased-type"]' 'Aliased type' +// We check that "Aliased Type" is also present as a title in the sidebar. +//@ has - '//*[@class="sidebar-elems"]//h3/a[@href="#aliased-type"]' 'Aliased Type' // We check that we have the implementation of the type alias itself. //@ has - '//*[@id="impl-GenericStruct%3Cu8%3E"]/h3' 'impl TypedefStruct' //@ has - '//*[@id="method.on_alias"]/h4' 'pub fn on_alias()' diff --git a/tests/rustdoc/type-layout.rs b/tests/rustdoc/type-layout.rs index 1e462210cba21..6de435dbcc143 100644 --- a/tests/rustdoc/type-layout.rs +++ b/tests/rustdoc/type-layout.rs @@ -37,7 +37,8 @@ pub struct Y(u8); pub struct Z; // We can't compute layout for generic types. -//@ hasraw type_layout/struct.Generic.html 'Unable to compute type layout, possibly due to this type having generic parameters' +//@ hasraw type_layout/struct.Generic.html 'Unable to compute type layout, possibly due to this type having generic parameters.' +//@ hasraw type_layout/struct.Generic.html 'Layout can only be computed for concrete, fully-instantiated types.' //@ !hasraw - 'Size: ' pub struct Generic(T); @@ -85,9 +86,15 @@ pub enum WithNiche { } //@ hasraw type_layout/enum.Uninhabited.html 'Size: ' -//@ hasraw - '0 bytes (uninhabited)' +//@ hasraw - '0 bytes (uninhabited)' pub enum Uninhabited {} //@ hasraw type_layout/struct.Uninhabited2.html 'Size: ' -//@ hasraw - '8 bytes (uninhabited)' +//@ hasraw - '8 bytes (uninhabited)' pub struct Uninhabited2(std::convert::Infallible, u64); + +pub trait Project { type Assoc; } +// We can't compute layout. A `LayoutError::Unknown` is returned. +//@ hasraw type_layout/struct.Unknown.html 'Unable to compute type layout.' +//@ !hasraw - 'Size: ' +pub struct Unknown(<() as Project>::Assoc) where for<'a> (): Project; diff --git a/tests/ui-fulldeps/codegen-backend/hotplug.rs b/tests/ui-fulldeps/codegen-backend/hotplug.rs index 917b20fcdb56e..ce310ba3e315e 100644 --- a/tests/ui-fulldeps/codegen-backend/hotplug.rs +++ b/tests/ui-fulldeps/codegen-backend/hotplug.rs @@ -6,6 +6,10 @@ //@ normalize-stdout: "libthe_backend.dylib" -> "libthe_backend.so" //@ normalize-stdout: "the_backend.dll" -> "libthe_backend.so" +// Pick a target that requires no target features, so that no warning is shown +// about missing target features. +//@ compile-flags: --target arm-unknown-linux-gnueabi +//@ needs-llvm-components: arm //@ revisions: normal dep bindep //@ compile-flags: --crate-type=lib //@ [normal] compile-flags: --emit=link=- diff --git a/tests/ui-fulldeps/compiler-calls.rs b/tests/ui-fulldeps/compiler-calls.rs index 5fb47c87e501f..d6148dfec43ce 100644 --- a/tests/ui-fulldeps/compiler-calls.rs +++ b/tests/ui-fulldeps/compiler-calls.rs @@ -25,7 +25,7 @@ fn main() { let mut count = 1; let args = vec!["compiler-calls".to_string(), "foo.rs".to_string()]; rustc_driver::catch_fatal_errors(|| -> interface::Result<()> { - rustc_driver::RunCompiler::new(&args, &mut TestCalls { count: &mut count }).run(); + rustc_driver::run_compiler(&args, &mut TestCalls { count: &mut count }); Ok(()) }) .ok(); diff --git a/tests/ui-fulldeps/obtain-borrowck.rs b/tests/ui-fulldeps/obtain-borrowck.rs index 8ea2ac6197199..c6bec4f77a062 100644 --- a/tests/ui-fulldeps/obtain-borrowck.rs +++ b/tests/ui-fulldeps/obtain-borrowck.rs @@ -47,8 +47,7 @@ fn main() { rustc_args.push("-Zpolonius".to_owned()); let mut callbacks = CompilerCalls::default(); // Call the Rust compiler with our callbacks. - rustc_driver::RunCompiler::new(&rustc_args, &mut callbacks).run(); - Ok(()) + rustc_driver::run_compiler(&rustc_args, &mut callbacks); }); std::process::exit(exit_code); } diff --git a/tests/ui-fulldeps/run-compiler-twice.rs b/tests/ui-fulldeps/run-compiler-twice.rs index bcc235e58ed33..f414c961627dd 100644 --- a/tests/ui-fulldeps/run-compiler-twice.rs +++ b/tests/ui-fulldeps/run-compiler-twice.rs @@ -72,7 +72,7 @@ fn compile(code: String, output: PathBuf, sysroot: PathBuf, linker: Option<&Path override_queries: None, make_codegen_backend: None, registry: rustc_driver::diagnostics_registry(), - using_internal_features: std::sync::Arc::default(), + using_internal_features: &rustc_driver::USING_INTERNAL_FEATURES, expanded_args: Default::default(), }; diff --git a/tests/ui-fulldeps/stable-mir/check_abi.rs b/tests/ui-fulldeps/stable-mir/check_abi.rs index 8caf3032afc44..ef2d5b4854bef 100644 --- a/tests/ui-fulldeps/stable-mir/check_abi.rs +++ b/tests/ui-fulldeps/stable-mir/check_abi.rs @@ -4,7 +4,6 @@ //@ ignore-stage1 //@ ignore-cross-compile //@ ignore-remote -//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 #![feature(rustc_private)] #![feature(assert_matches)] diff --git a/tests/ui-fulldeps/stable-mir/check_allocation.rs b/tests/ui-fulldeps/stable-mir/check_allocation.rs index 072c8ba6a442c..c102f86a2286b 100644 --- a/tests/ui-fulldeps/stable-mir/check_allocation.rs +++ b/tests/ui-fulldeps/stable-mir/check_allocation.rs @@ -5,7 +5,6 @@ //@ ignore-stage1 //@ ignore-cross-compile //@ ignore-remote -//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 //@ edition: 2021 #![feature(rustc_private)] diff --git a/tests/ui-fulldeps/stable-mir/check_attribute.rs b/tests/ui-fulldeps/stable-mir/check_attribute.rs index 22481e275a99f..11cb63f3f8a58 100644 --- a/tests/ui-fulldeps/stable-mir/check_attribute.rs +++ b/tests/ui-fulldeps/stable-mir/check_attribute.rs @@ -4,7 +4,6 @@ //@ ignore-stage1 //@ ignore-cross-compile //@ ignore-remote -//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 #![feature(rustc_private)] diff --git a/tests/ui-fulldeps/stable-mir/check_binop.rs b/tests/ui-fulldeps/stable-mir/check_binop.rs index 8c44e28510843..65b3ffd27ab05 100644 --- a/tests/ui-fulldeps/stable-mir/check_binop.rs +++ b/tests/ui-fulldeps/stable-mir/check_binop.rs @@ -4,7 +4,6 @@ //@ ignore-stage1 //@ ignore-cross-compile //@ ignore-remote -//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 #![feature(rustc_private)] diff --git a/tests/ui-fulldeps/stable-mir/check_crate_defs.rs b/tests/ui-fulldeps/stable-mir/check_crate_defs.rs index ed093903381b2..71cca94c34f62 100644 --- a/tests/ui-fulldeps/stable-mir/check_crate_defs.rs +++ b/tests/ui-fulldeps/stable-mir/check_crate_defs.rs @@ -4,7 +4,6 @@ //@ ignore-stage1 //@ ignore-cross-compile //@ ignore-remote -//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 #![feature(rustc_private)] #![feature(assert_matches)] diff --git a/tests/ui-fulldeps/stable-mir/check_def_ty.rs b/tests/ui-fulldeps/stable-mir/check_def_ty.rs index 482dbd22d5fe2..37b9a83e33e76 100644 --- a/tests/ui-fulldeps/stable-mir/check_def_ty.rs +++ b/tests/ui-fulldeps/stable-mir/check_def_ty.rs @@ -5,7 +5,6 @@ //@ ignore-stage1 //@ ignore-cross-compile //@ ignore-remote -//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 //@ edition: 2021 #![feature(rustc_private)] diff --git a/tests/ui-fulldeps/stable-mir/check_defs.rs b/tests/ui-fulldeps/stable-mir/check_defs.rs index bf1f1a2ceab98..cd3d76d876012 100644 --- a/tests/ui-fulldeps/stable-mir/check_defs.rs +++ b/tests/ui-fulldeps/stable-mir/check_defs.rs @@ -4,7 +4,6 @@ //@ ignore-stage1 //@ ignore-cross-compile //@ ignore-remote -//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 //@ edition: 2021 #![feature(rustc_private)] diff --git a/tests/ui-fulldeps/stable-mir/check_foreign.rs b/tests/ui-fulldeps/stable-mir/check_foreign.rs index 4acbabbb6be1d..bc3956b309088 100644 --- a/tests/ui-fulldeps/stable-mir/check_foreign.rs +++ b/tests/ui-fulldeps/stable-mir/check_foreign.rs @@ -4,7 +4,6 @@ //@ ignore-stage1 //@ ignore-cross-compile //@ ignore-remote -//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 //@ edition: 2021 #![feature(rustc_private)] diff --git a/tests/ui-fulldeps/stable-mir/check_instance.rs b/tests/ui-fulldeps/stable-mir/check_instance.rs index 464350b104571..72a138f907e72 100644 --- a/tests/ui-fulldeps/stable-mir/check_instance.rs +++ b/tests/ui-fulldeps/stable-mir/check_instance.rs @@ -4,7 +4,6 @@ //@ ignore-stage1 //@ ignore-cross-compile //@ ignore-remote -//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 //@ edition: 2021 #![feature(rustc_private)] diff --git a/tests/ui-fulldeps/stable-mir/check_intrinsics.rs b/tests/ui-fulldeps/stable-mir/check_intrinsics.rs index 6edebaf756c56..2f772b978865a 100644 --- a/tests/ui-fulldeps/stable-mir/check_intrinsics.rs +++ b/tests/ui-fulldeps/stable-mir/check_intrinsics.rs @@ -8,7 +8,6 @@ //@ ignore-stage1 //@ ignore-cross-compile //@ ignore-remote -//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 #![feature(rustc_private)] #![feature(assert_matches)] diff --git a/tests/ui-fulldeps/stable-mir/check_item_kind.rs b/tests/ui-fulldeps/stable-mir/check_item_kind.rs index 23b54e6c60b39..647ce534589e1 100644 --- a/tests/ui-fulldeps/stable-mir/check_item_kind.rs +++ b/tests/ui-fulldeps/stable-mir/check_item_kind.rs @@ -4,7 +4,6 @@ //@ ignore-stage1 //@ ignore-cross-compile //@ ignore-remote -//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 //@ edition: 2021 #![feature(rustc_private)] diff --git a/tests/ui-fulldeps/stable-mir/check_normalization.rs b/tests/ui-fulldeps/stable-mir/check_normalization.rs index 928173b154b4f..de14202adb9c0 100644 --- a/tests/ui-fulldeps/stable-mir/check_normalization.rs +++ b/tests/ui-fulldeps/stable-mir/check_normalization.rs @@ -4,7 +4,6 @@ //@ ignore-stage1 //@ ignore-cross-compile //@ ignore-remote -//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 //@ edition: 2021 #![feature(rustc_private)] diff --git a/tests/ui-fulldeps/stable-mir/check_trait_queries.rs b/tests/ui-fulldeps/stable-mir/check_trait_queries.rs index 304a7ce925554..23c2844d3f166 100644 --- a/tests/ui-fulldeps/stable-mir/check_trait_queries.rs +++ b/tests/ui-fulldeps/stable-mir/check_trait_queries.rs @@ -4,7 +4,6 @@ //@ ignore-stage1 //@ ignore-cross-compile //@ ignore-remote -//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 //@ edition: 2021 #![feature(rustc_private)] diff --git a/tests/ui-fulldeps/stable-mir/check_transform.rs b/tests/ui-fulldeps/stable-mir/check_transform.rs index bcf79c456b073..d9fc924933fa8 100644 --- a/tests/ui-fulldeps/stable-mir/check_transform.rs +++ b/tests/ui-fulldeps/stable-mir/check_transform.rs @@ -4,7 +4,6 @@ //@ ignore-stage1 //@ ignore-cross-compile //@ ignore-remote -//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 #![feature(rustc_private)] #![feature(assert_matches)] diff --git a/tests/ui-fulldeps/stable-mir/check_ty_fold.rs b/tests/ui-fulldeps/stable-mir/check_ty_fold.rs index e21508c9b4612..9fa4929d68e27 100644 --- a/tests/ui-fulldeps/stable-mir/check_ty_fold.rs +++ b/tests/ui-fulldeps/stable-mir/check_ty_fold.rs @@ -5,7 +5,6 @@ //@ ignore-stage1 //@ ignore-cross-compile //@ ignore-remote -//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 //@ edition: 2021 #![feature(rustc_private)] diff --git a/tests/ui-fulldeps/stable-mir/compilation-result.rs b/tests/ui-fulldeps/stable-mir/compilation-result.rs index d921de73f4364..b8a9e720e5465 100644 --- a/tests/ui-fulldeps/stable-mir/compilation-result.rs +++ b/tests/ui-fulldeps/stable-mir/compilation-result.rs @@ -4,7 +4,6 @@ //@ ignore-stage1 //@ ignore-cross-compile //@ ignore-remote -//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 //@ edition: 2021 #![feature(rustc_private)] diff --git a/tests/ui-fulldeps/stable-mir/crate-info.rs b/tests/ui-fulldeps/stable-mir/crate-info.rs index 53be8eb10c1ed..7f0e9b50335d3 100644 --- a/tests/ui-fulldeps/stable-mir/crate-info.rs +++ b/tests/ui-fulldeps/stable-mir/crate-info.rs @@ -4,7 +4,6 @@ //@ ignore-stage1 //@ ignore-cross-compile //@ ignore-remote -//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 //@ edition: 2021 #![feature(rustc_private)] diff --git a/tests/ui-fulldeps/stable-mir/projections.rs b/tests/ui-fulldeps/stable-mir/projections.rs index fdb7eeed1b0e5..3cc71247e6744 100644 --- a/tests/ui-fulldeps/stable-mir/projections.rs +++ b/tests/ui-fulldeps/stable-mir/projections.rs @@ -4,7 +4,6 @@ //@ ignore-stage1 //@ ignore-cross-compile //@ ignore-remote -//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 //@ edition: 2021 #![feature(rustc_private)] diff --git a/tests/ui-fulldeps/stable-mir/smir_internal.rs b/tests/ui-fulldeps/stable-mir/smir_internal.rs index 6f5478c08bf9b..453e5372de4f8 100644 --- a/tests/ui-fulldeps/stable-mir/smir_internal.rs +++ b/tests/ui-fulldeps/stable-mir/smir_internal.rs @@ -5,7 +5,6 @@ //@ ignore-stage1 //@ ignore-cross-compile //@ ignore-remote -//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 //@ edition: 2021 #![feature(rustc_private)] diff --git a/tests/ui-fulldeps/stable-mir/smir_serde.rs b/tests/ui-fulldeps/stable-mir/smir_serde.rs index 7dbf892f9e4b1..2c74276d550cf 100644 --- a/tests/ui-fulldeps/stable-mir/smir_serde.rs +++ b/tests/ui-fulldeps/stable-mir/smir_serde.rs @@ -4,7 +4,6 @@ //@ ignore-stage1 //@ ignore-cross-compile //@ ignore-remote -//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 //@ edition: 2021 #![feature(rustc_private)] diff --git a/tests/ui-fulldeps/stable-mir/smir_visitor.rs b/tests/ui-fulldeps/stable-mir/smir_visitor.rs index 666000d3b070f..0a6415d490e4b 100644 --- a/tests/ui-fulldeps/stable-mir/smir_visitor.rs +++ b/tests/ui-fulldeps/stable-mir/smir_visitor.rs @@ -4,7 +4,6 @@ //@ ignore-stage1 //@ ignore-cross-compile //@ ignore-remote -//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 //@ edition: 2021 #![feature(rustc_private)] diff --git a/tests/ui/abi/abi-sysv64-arg-passing.rs b/tests/ui/abi/abi-sysv64-arg-passing.rs index 04942e984a813..c18752418a1d6 100644 --- a/tests/ui/abi/abi-sysv64-arg-passing.rs +++ b/tests/ui/abi/abi-sysv64-arg-passing.rs @@ -29,7 +29,8 @@ //@ ignore-aarch64 //@ ignore-windows -// note: windows is ignored as rust_test_helpers does not have the sysv64 abi on windows +// Windows is ignored because bootstrap doesn't yet know to compile rust_test_helpers with +// the sysv64 ABI on Windows. #[allow(dead_code)] #[allow(improper_ctypes)] diff --git a/tests/ui/abi/c-zst.aarch64-darwin.stderr b/tests/ui/abi/c-zst.aarch64-darwin.stderr index 7d384bc875f98..d9742612bcfd2 100644 --- a/tests/ui/abi/c-zst.aarch64-darwin.stderr +++ b/tests/ui/abi/c-zst.aarch64-darwin.stderr @@ -22,6 +22,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, }, }, mode: Ignore, @@ -49,6 +50,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, }, }, mode: Ignore, diff --git a/tests/ui/abi/c-zst.powerpc-linux.stderr b/tests/ui/abi/c-zst.powerpc-linux.stderr index 7980710bab676..0e98b5f806bc2 100644 --- a/tests/ui/abi/c-zst.powerpc-linux.stderr +++ b/tests/ui/abi/c-zst.powerpc-linux.stderr @@ -22,6 +22,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, }, }, mode: Indirect { @@ -60,6 +61,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, }, }, mode: Ignore, diff --git a/tests/ui/abi/c-zst.s390x-linux.stderr b/tests/ui/abi/c-zst.s390x-linux.stderr index 7980710bab676..0e98b5f806bc2 100644 --- a/tests/ui/abi/c-zst.s390x-linux.stderr +++ b/tests/ui/abi/c-zst.s390x-linux.stderr @@ -22,6 +22,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, }, }, mode: Indirect { @@ -60,6 +61,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, }, }, mode: Ignore, diff --git a/tests/ui/abi/c-zst.sparc64-linux.stderr b/tests/ui/abi/c-zst.sparc64-linux.stderr index 7980710bab676..0e98b5f806bc2 100644 --- a/tests/ui/abi/c-zst.sparc64-linux.stderr +++ b/tests/ui/abi/c-zst.sparc64-linux.stderr @@ -22,6 +22,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, }, }, mode: Indirect { @@ -60,6 +61,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, }, }, mode: Ignore, diff --git a/tests/ui/abi/c-zst.x86_64-linux.stderr b/tests/ui/abi/c-zst.x86_64-linux.stderr index 7d384bc875f98..d9742612bcfd2 100644 --- a/tests/ui/abi/c-zst.x86_64-linux.stderr +++ b/tests/ui/abi/c-zst.x86_64-linux.stderr @@ -22,6 +22,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, }, }, mode: Ignore, @@ -49,6 +50,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, }, }, mode: Ignore, diff --git a/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr b/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr index 7980710bab676..0e98b5f806bc2 100644 --- a/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr +++ b/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr @@ -22,6 +22,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, }, }, mode: Indirect { @@ -60,6 +61,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, }, }, mode: Ignore, diff --git a/tests/ui/abi/debug.rs b/tests/ui/abi/debug.rs index 565743bf978ea..6dbc316146412 100644 --- a/tests/ui/abi/debug.rs +++ b/tests/ui/abi/debug.rs @@ -1,4 +1,5 @@ //@ normalize-stderr: "(abi|pref|unadjusted_abi_align): Align\([1-8] bytes\)" -> "$1: $$SOME_ALIGN" +//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" //@ normalize-stderr: "(size): Size\([48] bytes\)" -> "$1: $$SOME_SIZE" //@ normalize-stderr: "(can_unwind): (true|false)" -> "$1: $$SOME_BOOL" //@ normalize-stderr: "(valid_range): 0\.\.=(4294967295|18446744073709551615)" -> "$1: $$FULL" diff --git a/tests/ui/abi/debug.stderr b/tests/ui/abi/debug.stderr index aa51c42c58dc4..e550e5bfcf3c3 100644 --- a/tests/ui/abi/debug.stderr +++ b/tests/ui/abi/debug.stderr @@ -25,6 +25,7 @@ error: fn_abi_of(test) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Direct( @@ -71,6 +72,7 @@ error: fn_abi_of(test) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Direct( @@ -87,7 +89,7 @@ error: fn_abi_of(test) = FnAbi { conv: Rust, can_unwind: $SOME_BOOL, } - --> $DIR/debug.rs:15:1 + --> $DIR/debug.rs:16:1 | LL | fn test(_x: u8) -> bool { true } | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -128,6 +130,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Direct( @@ -165,6 +168,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Direct( @@ -181,7 +185,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { conv: Rust, can_unwind: $SOME_BOOL, } - --> $DIR/debug.rs:18:1 + --> $DIR/debug.rs:19:1 | LL | type TestFnPtr = fn(bool) -> u8; | ^^^^^^^^^^^^^^ @@ -214,6 +218,7 @@ error: fn_abi_of(test_generic) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Direct( @@ -248,6 +253,7 @@ error: fn_abi_of(test_generic) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Ignore, @@ -257,13 +263,13 @@ error: fn_abi_of(test_generic) = FnAbi { conv: Rust, can_unwind: $SOME_BOOL, } - --> $DIR/debug.rs:21:1 + --> $DIR/debug.rs:22:1 | LL | fn test_generic(_x: *const T) { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `#[rustc_abi]` can only be applied to function items, type aliases, and associated functions - --> $DIR/debug.rs:24:1 + --> $DIR/debug.rs:25:1 | LL | const C: () = (); | ^^^^^^^^^^^ @@ -296,6 +302,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Direct( @@ -330,6 +337,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Ignore, @@ -366,6 +374,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Direct( @@ -400,6 +409,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Ignore, @@ -409,7 +419,7 @@ error: ABIs are not compatible conv: Rust, can_unwind: $SOME_BOOL, } - --> $DIR/debug.rs:40:1 + --> $DIR/debug.rs:41:1 | LL | type TestAbiNe = (fn(u8), fn(u32)); | ^^^^^^^^^^^^^^ @@ -439,6 +449,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Indirect { @@ -477,6 +488,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Ignore, @@ -510,6 +522,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Indirect { @@ -548,6 +561,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Ignore, @@ -557,7 +571,7 @@ error: ABIs are not compatible conv: Rust, can_unwind: $SOME_BOOL, } - --> $DIR/debug.rs:43:1 + --> $DIR/debug.rs:44:1 | LL | type TestAbiNeLarger = (fn([u8; 32]), fn([u32; 32])); | ^^^^^^^^^^^^^^^^^^^^ @@ -589,6 +603,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Direct( @@ -623,6 +638,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Ignore, @@ -659,6 +675,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Direct( @@ -693,6 +710,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Ignore, @@ -702,7 +720,7 @@ error: ABIs are not compatible conv: Rust, can_unwind: $SOME_BOOL, } - --> $DIR/debug.rs:46:1 + --> $DIR/debug.rs:47:1 | LL | type TestAbiNeFloat = (fn(f32), fn(u32)); | ^^^^^^^^^^^^^^^^^^^ @@ -735,6 +753,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Direct( @@ -769,6 +788,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Ignore, @@ -805,6 +825,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Direct( @@ -839,6 +860,7 @@ error: ABIs are not compatible }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Ignore, @@ -848,13 +870,13 @@ error: ABIs are not compatible conv: Rust, can_unwind: $SOME_BOOL, } - --> $DIR/debug.rs:50:1 + --> $DIR/debug.rs:51:1 | LL | type TestAbiNeSign = (fn(i32), fn(u32)); | ^^^^^^^^^^^^^^^^^^ error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/debug.rs:53:46 + --> $DIR/debug.rs:54:46 | LL | type TestAbiEqNonsense = (fn((str, str)), fn((str, str))); | ^^^^^^^^^^ doesn't have a size known at compile-time @@ -863,7 +885,7 @@ LL | type TestAbiEqNonsense = (fn((str, str)), fn((str, str))); = note: only the last element of a tuple may have a dynamically sized type error: `#[rustc_abi]` can only be applied to function items, type aliases, and associated functions - --> $DIR/debug.rs:28:5 + --> $DIR/debug.rs:29:5 | LL | const C: () = (); | ^^^^^^^^^^^ @@ -906,6 +928,7 @@ error: fn_abi_of(assoc_test) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Direct( @@ -942,6 +965,7 @@ error: fn_abi_of(assoc_test) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: $SEED, }, }, mode: Ignore, @@ -951,7 +975,7 @@ error: fn_abi_of(assoc_test) = FnAbi { conv: Rust, can_unwind: $SOME_BOOL, } - --> $DIR/debug.rs:33:5 + --> $DIR/debug.rs:34:5 | LL | fn assoc_test(&self) { } | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/homogenous-floats-target-feature-mixup.rs b/tests/ui/abi/homogenous-floats-target-feature-mixup.rs index 4afb710b193eb..22b9b029a4049 100644 --- a/tests/ui/abi/homogenous-floats-target-feature-mixup.rs +++ b/tests/ui/abi/homogenous-floats-target-feature-mixup.rs @@ -5,8 +5,7 @@ // without #[repr(simd)] //@ run-pass -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess #![feature(avx512_target_feature)] diff --git a/tests/ui/abi/large-byval-align.rs b/tests/ui/abi/large-byval-align.rs index e39170df72b49..ddd579f264e67 100644 --- a/tests/ui/abi/large-byval-align.rs +++ b/tests/ui/abi/large-byval-align.rs @@ -1,6 +1,5 @@ //@ compile-flags: -Copt-level=0 //@ only-x86_64 -//@ ignore-windows //@ min-llvm-version: 19 //@ build-pass diff --git a/tests/ui/abi/removed-wasm-abi.rs b/tests/ui/abi/removed-wasm-abi.rs deleted file mode 100644 index a45e42bfe020e..0000000000000 --- a/tests/ui/abi/removed-wasm-abi.rs +++ /dev/null @@ -1,4 +0,0 @@ -extern "wasm" fn test() {} -//~^ ERROR invalid ABI: found `wasm` - -fn main() {} diff --git a/tests/ui/abi/removed-wasm-abi.stderr b/tests/ui/abi/removed-wasm-abi.stderr deleted file mode 100644 index 6007c4e258014..0000000000000 --- a/tests/ui/abi/removed-wasm-abi.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0703]: invalid ABI: found `wasm` - --> $DIR/removed-wasm-abi.rs:1:8 - | -LL | extern "wasm" fn test() {} - | ^^^^^^ invalid ABI - | - = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions - = note: non-standard wasm ABI is no longer supported - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0703`. diff --git a/tests/ui/abi/riscv-discoverability-guidance.riscv32.stderr b/tests/ui/abi/riscv-discoverability-guidance.riscv32.stderr index e80411fda344e..e1f433479857a 100644 --- a/tests/ui/abi/riscv-discoverability-guidance.riscv32.stderr +++ b/tests/ui/abi/riscv-discoverability-guidance.riscv32.stderr @@ -8,10 +8,9 @@ LL | extern "riscv-interrupt" fn isr() {} | help: did you mean: `"riscv-interrupt-m"` | = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions - = note: please use one of riscv-interrupt-m or riscv-interrupt-s for machine- or supervisor-level interrupts, respectively error[E0703]: invalid ABI: found `riscv-interrupt-u` - --> $DIR/riscv-discoverability-guidance.rs:23:8 + --> $DIR/riscv-discoverability-guidance.rs:22:8 | LL | extern "riscv-interrupt-u" fn isr_U() {} | ^^^^^^^^^^^^^^^^^^^ @@ -20,7 +19,6 @@ LL | extern "riscv-interrupt-u" fn isr_U() {} | help: did you mean: `"riscv-interrupt-m"` | = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions - = note: user-mode interrupt handlers have been removed from LLVM pending standardization, see: https://reviews.llvm.org/D149314 error: aborting due to 2 previous errors diff --git a/tests/ui/abi/riscv-discoverability-guidance.riscv64.stderr b/tests/ui/abi/riscv-discoverability-guidance.riscv64.stderr index e80411fda344e..e1f433479857a 100644 --- a/tests/ui/abi/riscv-discoverability-guidance.riscv64.stderr +++ b/tests/ui/abi/riscv-discoverability-guidance.riscv64.stderr @@ -8,10 +8,9 @@ LL | extern "riscv-interrupt" fn isr() {} | help: did you mean: `"riscv-interrupt-m"` | = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions - = note: please use one of riscv-interrupt-m or riscv-interrupt-s for machine- or supervisor-level interrupts, respectively error[E0703]: invalid ABI: found `riscv-interrupt-u` - --> $DIR/riscv-discoverability-guidance.rs:23:8 + --> $DIR/riscv-discoverability-guidance.rs:22:8 | LL | extern "riscv-interrupt-u" fn isr_U() {} | ^^^^^^^^^^^^^^^^^^^ @@ -20,7 +19,6 @@ LL | extern "riscv-interrupt-u" fn isr_U() {} | help: did you mean: `"riscv-interrupt-m"` | = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions - = note: user-mode interrupt handlers have been removed from LLVM pending standardization, see: https://reviews.llvm.org/D149314 error: aborting due to 2 previous errors diff --git a/tests/ui/abi/riscv-discoverability-guidance.rs b/tests/ui/abi/riscv-discoverability-guidance.rs index 1b189d907baf6..dec5059b0a7f0 100644 --- a/tests/ui/abi/riscv-discoverability-guidance.rs +++ b/tests/ui/abi/riscv-discoverability-guidance.rs @@ -18,10 +18,8 @@ extern "riscv-interrupt" fn isr() {} //~^ ERROR invalid ABI //~^^ NOTE invalid ABI //~^^^ NOTE invoke `rustc --print=calling-conventions` for a full list of supported calling conventions -//~^^^^ NOTE please use one of riscv-interrupt-m or riscv-interrupt-s extern "riscv-interrupt-u" fn isr_U() {} //~^ ERROR invalid ABI //~^^ NOTE invalid ABI //~^^^ NOTE invoke `rustc --print=calling-conventions` for a full list of supported calling conventions -//~^^^^ NOTE user-mode interrupt handlers have been removed from LLVM pending standardization diff --git a/tests/ui/abi/segfault-no-out-of-stack.rs b/tests/ui/abi/segfault-no-out-of-stack.rs index 113c82c30e927..5e8b4e0dbf2a6 100644 --- a/tests/ui/abi/segfault-no-out-of-stack.rs +++ b/tests/ui/abi/segfault-no-out-of-stack.rs @@ -1,6 +1,6 @@ //@ run-pass -//@ ignore-wasm32 can't run commands -//@ ignore-sgx no processes +//@ needs-subprocess +//@ compile-flags: -Zub-checks=no -Zmir-enable-passes=-CheckNull //@ ignore-fuchsia must translate zircon signal to SIGSEGV/SIGBUS, FIXME (#58590) #![feature(rustc_private)] diff --git a/tests/ui/abi/sparcv8plus-llvm19.rs b/tests/ui/abi/sparcv8plus-llvm19.rs new file mode 100644 index 0000000000000..a884e5ca06f10 --- /dev/null +++ b/tests/ui/abi/sparcv8plus-llvm19.rs @@ -0,0 +1,43 @@ +//@ revisions: sparc sparcv8plus sparc_cpu_v9 sparc_feature_v8plus sparc_cpu_v9_feature_v8plus +//@[sparc] compile-flags: --target sparc-unknown-none-elf +//@[sparc] needs-llvm-components: sparc +//@[sparcv8plus] compile-flags: --target sparc-unknown-linux-gnu +//@[sparcv8plus] needs-llvm-components: sparc +//@[sparc_cpu_v9] compile-flags: --target sparc-unknown-none-elf -C target-cpu=v9 +//@[sparc_cpu_v9] needs-llvm-components: sparc +//@[sparc_feature_v8plus] compile-flags: --target sparc-unknown-none-elf -C target-feature=+v8plus +//@[sparc_feature_v8plus] needs-llvm-components: sparc +//@[sparc_cpu_v9_feature_v8plus] compile-flags: --target sparc-unknown-none-elf -C target-cpu=v9 -C target-feature=+v8plus +//@[sparc_cpu_v9_feature_v8plus] needs-llvm-components: sparc +//@ exact-llvm-major-version: 19 + +#![crate_type = "rlib"] +#![feature(no_core, rustc_attrs, lang_items)] +#![no_core] + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +#[rustc_builtin_macro] +macro_rules! compile_error { + () => {}; +} + +#[cfg(all(not(target_feature = "v8plus"), not(target_feature = "v9")))] +compile_error!("-v8plus,-v9"); +//[sparc]~^ ERROR -v8plus,-v9 + +// FIXME: sparc_cpu_v9 should be in "-v8plus,+v9" group (fixed in LLVM 20) +#[cfg(all(target_feature = "v8plus", target_feature = "v9"))] +compile_error!("+v8plus,+v9"); +//[sparcv8plus,sparc_cpu_v9_feature_v8plus,sparc_cpu_v9]~^ ERROR +v8plus,+v9 + +// FIXME: should be rejected +#[cfg(all(target_feature = "v8plus", not(target_feature = "v9")))] +compile_error!("+v8plus,-v9 (FIXME)"); +//[sparc_feature_v8plus]~^ ERROR +v8plus,-v9 (FIXME) + +#[cfg(all(not(target_feature = "v8plus"), target_feature = "v9"))] +compile_error!("-v8plus,+v9"); diff --git a/tests/ui/abi/sparcv8plus-llvm19.sparc.stderr b/tests/ui/abi/sparcv8plus-llvm19.sparc.stderr new file mode 100644 index 0000000000000..7eedf26135f54 --- /dev/null +++ b/tests/ui/abi/sparcv8plus-llvm19.sparc.stderr @@ -0,0 +1,8 @@ +error: -v8plus,-v9 + --> $DIR/sparcv8plus-llvm19.rs:29:1 + | +LL | compile_error!("-v8plus,-v9"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/abi/sparcv8plus-llvm19.sparc_cpu_v9.stderr b/tests/ui/abi/sparcv8plus-llvm19.sparc_cpu_v9.stderr new file mode 100644 index 0000000000000..ac61df3567808 --- /dev/null +++ b/tests/ui/abi/sparcv8plus-llvm19.sparc_cpu_v9.stderr @@ -0,0 +1,8 @@ +error: +v8plus,+v9 + --> $DIR/sparcv8plus-llvm19.rs:34:1 + | +LL | compile_error!("+v8plus,+v9"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/abi/sparcv8plus-llvm19.sparc_cpu_v9_feature_v8plus.stderr b/tests/ui/abi/sparcv8plus-llvm19.sparc_cpu_v9_feature_v8plus.stderr new file mode 100644 index 0000000000000..ac61df3567808 --- /dev/null +++ b/tests/ui/abi/sparcv8plus-llvm19.sparc_cpu_v9_feature_v8plus.stderr @@ -0,0 +1,8 @@ +error: +v8plus,+v9 + --> $DIR/sparcv8plus-llvm19.rs:34:1 + | +LL | compile_error!("+v8plus,+v9"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/abi/sparcv8plus-llvm19.sparc_feature_v8plus.stderr b/tests/ui/abi/sparcv8plus-llvm19.sparc_feature_v8plus.stderr new file mode 100644 index 0000000000000..1bf7a3ad76a83 --- /dev/null +++ b/tests/ui/abi/sparcv8plus-llvm19.sparc_feature_v8plus.stderr @@ -0,0 +1,8 @@ +error: +v8plus,-v9 (FIXME) + --> $DIR/sparcv8plus-llvm19.rs:39:1 + | +LL | compile_error!("+v8plus,-v9 (FIXME)"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/abi/sparcv8plus-llvm19.sparcv8plus.stderr b/tests/ui/abi/sparcv8plus-llvm19.sparcv8plus.stderr new file mode 100644 index 0000000000000..ac61df3567808 --- /dev/null +++ b/tests/ui/abi/sparcv8plus-llvm19.sparcv8plus.stderr @@ -0,0 +1,8 @@ +error: +v8plus,+v9 + --> $DIR/sparcv8plus-llvm19.rs:34:1 + | +LL | compile_error!("+v8plus,+v9"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/abi/sparcv8plus.rs b/tests/ui/abi/sparcv8plus.rs index 108279b34949b..a78ae0cd32889 100644 --- a/tests/ui/abi/sparcv8plus.rs +++ b/tests/ui/abi/sparcv8plus.rs @@ -9,7 +9,7 @@ //@[sparc_feature_v8plus] needs-llvm-components: sparc //@[sparc_cpu_v9_feature_v8plus] compile-flags: --target sparc-unknown-none-elf -C target-cpu=v9 -C target-feature=+v8plus //@[sparc_cpu_v9_feature_v8plus] needs-llvm-components: sparc -//@ min-llvm-version: 19 +//@ min-llvm-version: 20 #![crate_type = "rlib"] #![feature(no_core, rustc_attrs, lang_items)] @@ -29,10 +29,9 @@ macro_rules! compile_error { compile_error!("-v8plus,-v9"); //[sparc]~^ ERROR -v8plus,-v9 -// FIXME: sparc_cpu_v9 should be in "-v8plus,+v9" group (fixed in LLVM 20) #[cfg(all(target_feature = "v8plus", target_feature = "v9"))] compile_error!("+v8plus,+v9"); -//[sparcv8plus,sparc_cpu_v9_feature_v8plus,sparc_cpu_v9]~^ ERROR +v8plus,+v9 +//[sparcv8plus,sparc_cpu_v9_feature_v8plus]~^ ERROR +v8plus,+v9 // FIXME: should be rejected #[cfg(all(target_feature = "v8plus", not(target_feature = "v9")))] @@ -41,3 +40,4 @@ compile_error!("+v8plus,-v9 (FIXME)"); #[cfg(all(not(target_feature = "v8plus"), target_feature = "v9"))] compile_error!("-v8plus,+v9"); +//[sparc_cpu_v9]~^ ERROR -v8plus,+v9 diff --git a/tests/ui/abi/sparcv8plus.sparc_cpu_v9.stderr b/tests/ui/abi/sparcv8plus.sparc_cpu_v9.stderr index 5e1e1fa5c798f..00fd7ef4ea8fb 100644 --- a/tests/ui/abi/sparcv8plus.sparc_cpu_v9.stderr +++ b/tests/ui/abi/sparcv8plus.sparc_cpu_v9.stderr @@ -1,7 +1,7 @@ -error: +v8plus,+v9 - --> $DIR/sparcv8plus.rs:34:1 +error: -v8plus,+v9 + --> $DIR/sparcv8plus.rs:42:1 | -LL | compile_error!("+v8plus,+v9"); +LL | compile_error!("-v8plus,+v9"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/abi/sparcv8plus.sparc_cpu_v9_feature_v8plus.stderr b/tests/ui/abi/sparcv8plus.sparc_cpu_v9_feature_v8plus.stderr index 5e1e1fa5c798f..a3c74e67f8f11 100644 --- a/tests/ui/abi/sparcv8plus.sparc_cpu_v9_feature_v8plus.stderr +++ b/tests/ui/abi/sparcv8plus.sparc_cpu_v9_feature_v8plus.stderr @@ -1,5 +1,5 @@ error: +v8plus,+v9 - --> $DIR/sparcv8plus.rs:34:1 + --> $DIR/sparcv8plus.rs:33:1 | LL | compile_error!("+v8plus,+v9"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/sparcv8plus.sparc_feature_v8plus.stderr b/tests/ui/abi/sparcv8plus.sparc_feature_v8plus.stderr index 8a5375a46bc05..84f560d158ca9 100644 --- a/tests/ui/abi/sparcv8plus.sparc_feature_v8plus.stderr +++ b/tests/ui/abi/sparcv8plus.sparc_feature_v8plus.stderr @@ -1,5 +1,5 @@ error: +v8plus,-v9 (FIXME) - --> $DIR/sparcv8plus.rs:39:1 + --> $DIR/sparcv8plus.rs:38:1 | LL | compile_error!("+v8plus,-v9 (FIXME)"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/sparcv8plus.sparcv8plus.stderr b/tests/ui/abi/sparcv8plus.sparcv8plus.stderr index 5e1e1fa5c798f..a3c74e67f8f11 100644 --- a/tests/ui/abi/sparcv8plus.sparcv8plus.stderr +++ b/tests/ui/abi/sparcv8plus.sparcv8plus.stderr @@ -1,5 +1,5 @@ error: +v8plus,+v9 - --> $DIR/sparcv8plus.rs:34:1 + --> $DIR/sparcv8plus.rs:33:1 | LL | compile_error!("+v8plus,+v9"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/stack-probes-lto.rs b/tests/ui/abi/stack-probes-lto.rs index e6c26c5c4de85..c6e5bea5f4225 100644 --- a/tests/ui/abi/stack-probes-lto.rs +++ b/tests/ui/abi/stack-probes-lto.rs @@ -3,7 +3,7 @@ //@[aarch64] only-aarch64 //@[x32] only-x86 //@[x64] only-x86_64 -//@ ignore-sgx no processes +//@ needs-subprocess //@ ignore-musl FIXME #31506 //@ ignore-fuchsia no exception handler registered for segfault //@ compile-flags: -C lto diff --git a/tests/ui/abi/stack-probes.rs b/tests/ui/abi/stack-probes.rs index 1c0e50250d768..f0fbd80d2e734 100644 --- a/tests/ui/abi/stack-probes.rs +++ b/tests/ui/abi/stack-probes.rs @@ -3,8 +3,7 @@ //@[aarch64] only-aarch64 //@[x32] only-x86 //@[x64] only-x86_64 -//@ ignore-emscripten no processes -//@ ignore-sgx no processes +//@ needs-subprocess //@ ignore-fuchsia no exception handler registered for segfault //@ ignore-nto Crash analysis impossible at SIGSEGV in QNX Neutrino //@ ignore-ios Stack probes are enabled, but the SIGSEGV handler isn't diff --git a/tests/ui/abi/sysv64-zst.stderr b/tests/ui/abi/sysv64-zst.stderr index 8e1791e27d27c..781e9b2f4c9d5 100644 --- a/tests/ui/abi/sysv64-zst.stderr +++ b/tests/ui/abi/sysv64-zst.stderr @@ -22,6 +22,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, }, }, mode: Ignore, @@ -49,6 +50,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, max_repr_align: None, unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, }, }, mode: Ignore, diff --git a/tests/ui/abi/unsupported.aarch64.stderr b/tests/ui/abi/unsupported.aarch64.stderr index 81aa200012feb..2eb6ab08232e6 100644 --- a/tests/ui/abi/unsupported.aarch64.stderr +++ b/tests/ui/abi/unsupported.aarch64.stderr @@ -1,5 +1,5 @@ warning: the calling convention "ptx-kernel" is not supported on this target - --> $DIR/unsupported.rs:35:15 + --> $DIR/unsupported.rs:36:15 | LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,13 +9,13 @@ LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) { = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:40:1 + --> $DIR/unsupported.rs:41:1 | LL | extern "ptx-kernel" {} | ^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "aapcs" is not supported on this target - --> $DIR/unsupported.rs:49:17 + --> $DIR/unsupported.rs:52:17 | LL | fn aapcs_ptr(f: extern "aapcs" fn()) { | ^^^^^^^^^^^^^^^^^^^ @@ -24,13 +24,13 @@ LL | fn aapcs_ptr(f: extern "aapcs" fn()) { = note: for more information, see issue #130260 error[E0570]: `"aapcs"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:62:1 + --> $DIR/unsupported.rs:65:1 | LL | extern "aapcs" {} | ^^^^^^^^^^^^^^^^^ warning: the calling convention "msp430-interrupt" is not supported on this target - --> $DIR/unsupported.rs:71:18 + --> $DIR/unsupported.rs:74:18 | LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -39,13 +39,13 @@ LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { = note: for more information, see issue #130260 error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:76:1 + --> $DIR/unsupported.rs:79:1 | LL | extern "msp430-interrupt" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "avr-interrupt" is not supported on this target - --> $DIR/unsupported.rs:81:15 + --> $DIR/unsupported.rs:84:15 | LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -54,13 +54,13 @@ LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { = note: for more information, see issue #130260 error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:86:1 + --> $DIR/unsupported.rs:89:1 | LL | extern "avr-interrupt" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "riscv-interrupt-m" is not supported on this target - --> $DIR/unsupported.rs:94:17 + --> $DIR/unsupported.rs:97:17 | LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -69,13 +69,13 @@ LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) { = note: for more information, see issue #130260 error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:105:1 + --> $DIR/unsupported.rs:108:1 | LL | extern "riscv-interrupt-m" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "x86-interrupt" is not supported on this target - --> $DIR/unsupported.rs:116:15 + --> $DIR/unsupported.rs:119:15 | LL | fn x86_ptr(f: extern "x86-interrupt" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -84,13 +84,13 @@ LL | fn x86_ptr(f: extern "x86-interrupt" fn()) { = note: for more information, see issue #130260 error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:127:1 + --> $DIR/unsupported.rs:130:1 | LL | extern "x86-interrupt" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "thiscall" is not supported on this target - --> $DIR/unsupported.rs:139:20 + --> $DIR/unsupported.rs:142:20 | LL | fn thiscall_ptr(f: extern "thiscall" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^ @@ -99,13 +99,13 @@ LL | fn thiscall_ptr(f: extern "thiscall" fn()) { = note: for more information, see issue #130260 error[E0570]: `"thiscall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:152:1 + --> $DIR/unsupported.rs:155:1 | LL | extern "thiscall" {} | ^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "stdcall" is not supported on this target - --> $DIR/unsupported.rs:165:19 + --> $DIR/unsupported.rs:168:19 | LL | fn stdcall_ptr(f: extern "stdcall" fn()) { | ^^^^^^^^^^^^^^^^^^^^^ @@ -114,13 +114,13 @@ LL | fn stdcall_ptr(f: extern "stdcall" fn()) { = note: for more information, see issue #130260 error[E0570]: `"stdcall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:178:1 + --> $DIR/unsupported.rs:181:1 | LL | extern "stdcall" {} | ^^^^^^^^^^^^^^^^^^^ warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target - --> $DIR/unsupported.rs:185:21 + --> $DIR/unsupported.rs:188:21 | LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -129,7 +129,7 @@ LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { = note: for more information, see issue #130260 warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target - --> $DIR/unsupported.rs:193:22 + --> $DIR/unsupported.rs:196:22 | LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -138,65 +138,71 @@ LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { = note: for more information, see issue #130260 error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:198:1 + --> $DIR/unsupported.rs:201:1 | LL | extern "C-cmse-nonsecure-entry" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:33:1 + --> $DIR/unsupported.rs:34:1 | LL | extern "ptx-kernel" fn ptx() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0570]: `"aapcs"` is not a supported ABI for the current target +error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target --> $DIR/unsupported.rs:43:1 | +LL | extern "gpu-kernel" fn gpu() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0570]: `"aapcs"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:46:1 + | LL | extern "aapcs" fn aapcs() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:69:1 + --> $DIR/unsupported.rs:72:1 | LL | extern "msp430-interrupt" fn msp430() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:79:1 + --> $DIR/unsupported.rs:82:1 | LL | extern "avr-interrupt" fn avr() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:89:1 + --> $DIR/unsupported.rs:92:1 | LL | extern "riscv-interrupt-m" fn riscv() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:111:1 + --> $DIR/unsupported.rs:114:1 | LL | extern "x86-interrupt" fn x86() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"thiscall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:133:1 + --> $DIR/unsupported.rs:136:1 | LL | extern "thiscall" fn thiscall() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"stdcall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:159:1 + --> $DIR/unsupported.rs:162:1 | LL | extern "stdcall" fn stdcall() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:191:1 + --> $DIR/unsupported.rs:194:1 | LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 18 previous errors; 10 warnings emitted +error: aborting due to 19 previous errors; 10 warnings emitted For more information about this error, try `rustc --explain E0570`. diff --git a/tests/ui/abi/unsupported.arm.stderr b/tests/ui/abi/unsupported.arm.stderr index 8e758ee451f53..ee878379cc646 100644 --- a/tests/ui/abi/unsupported.arm.stderr +++ b/tests/ui/abi/unsupported.arm.stderr @@ -1,5 +1,5 @@ warning: the calling convention "ptx-kernel" is not supported on this target - --> $DIR/unsupported.rs:35:15 + --> $DIR/unsupported.rs:36:15 | LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,13 +9,13 @@ LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) { = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:40:1 + --> $DIR/unsupported.rs:41:1 | LL | extern "ptx-kernel" {} | ^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "msp430-interrupt" is not supported on this target - --> $DIR/unsupported.rs:71:18 + --> $DIR/unsupported.rs:74:18 | LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,13 +24,13 @@ LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { = note: for more information, see issue #130260 error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:76:1 + --> $DIR/unsupported.rs:79:1 | LL | extern "msp430-interrupt" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "avr-interrupt" is not supported on this target - --> $DIR/unsupported.rs:81:15 + --> $DIR/unsupported.rs:84:15 | LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -39,13 +39,13 @@ LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { = note: for more information, see issue #130260 error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:86:1 + --> $DIR/unsupported.rs:89:1 | LL | extern "avr-interrupt" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "riscv-interrupt-m" is not supported on this target - --> $DIR/unsupported.rs:94:17 + --> $DIR/unsupported.rs:97:17 | LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -54,13 +54,13 @@ LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) { = note: for more information, see issue #130260 error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:105:1 + --> $DIR/unsupported.rs:108:1 | LL | extern "riscv-interrupt-m" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "x86-interrupt" is not supported on this target - --> $DIR/unsupported.rs:116:15 + --> $DIR/unsupported.rs:119:15 | LL | fn x86_ptr(f: extern "x86-interrupt" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -69,13 +69,13 @@ LL | fn x86_ptr(f: extern "x86-interrupt" fn()) { = note: for more information, see issue #130260 error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:127:1 + --> $DIR/unsupported.rs:130:1 | LL | extern "x86-interrupt" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "thiscall" is not supported on this target - --> $DIR/unsupported.rs:139:20 + --> $DIR/unsupported.rs:142:20 | LL | fn thiscall_ptr(f: extern "thiscall" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^ @@ -84,13 +84,13 @@ LL | fn thiscall_ptr(f: extern "thiscall" fn()) { = note: for more information, see issue #130260 error[E0570]: `"thiscall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:152:1 + --> $DIR/unsupported.rs:155:1 | LL | extern "thiscall" {} | ^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "stdcall" is not supported on this target - --> $DIR/unsupported.rs:165:19 + --> $DIR/unsupported.rs:168:19 | LL | fn stdcall_ptr(f: extern "stdcall" fn()) { | ^^^^^^^^^^^^^^^^^^^^^ @@ -99,13 +99,13 @@ LL | fn stdcall_ptr(f: extern "stdcall" fn()) { = note: for more information, see issue #130260 error[E0570]: `"stdcall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:178:1 + --> $DIR/unsupported.rs:181:1 | LL | extern "stdcall" {} | ^^^^^^^^^^^^^^^^^^^ warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target - --> $DIR/unsupported.rs:185:21 + --> $DIR/unsupported.rs:188:21 | LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -114,7 +114,7 @@ LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { = note: for more information, see issue #130260 warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target - --> $DIR/unsupported.rs:193:22 + --> $DIR/unsupported.rs:196:22 | LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -123,59 +123,65 @@ LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { = note: for more information, see issue #130260 error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:198:1 + --> $DIR/unsupported.rs:201:1 | LL | extern "C-cmse-nonsecure-entry" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:33:1 + --> $DIR/unsupported.rs:34:1 | LL | extern "ptx-kernel" fn ptx() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:43:1 + | +LL | extern "gpu-kernel" fn gpu() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:69:1 + --> $DIR/unsupported.rs:72:1 | LL | extern "msp430-interrupt" fn msp430() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:79:1 + --> $DIR/unsupported.rs:82:1 | LL | extern "avr-interrupt" fn avr() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:89:1 + --> $DIR/unsupported.rs:92:1 | LL | extern "riscv-interrupt-m" fn riscv() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:111:1 + --> $DIR/unsupported.rs:114:1 | LL | extern "x86-interrupt" fn x86() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"thiscall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:133:1 + --> $DIR/unsupported.rs:136:1 | LL | extern "thiscall" fn thiscall() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"stdcall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:159:1 + --> $DIR/unsupported.rs:162:1 | LL | extern "stdcall" fn stdcall() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:191:1 + --> $DIR/unsupported.rs:194:1 | LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 16 previous errors; 9 warnings emitted +error: aborting due to 17 previous errors; 9 warnings emitted For more information about this error, try `rustc --explain E0570`. diff --git a/tests/ui/abi/unsupported.i686.stderr b/tests/ui/abi/unsupported.i686.stderr index b3c74ad635375..02b2cdd356f7d 100644 --- a/tests/ui/abi/unsupported.i686.stderr +++ b/tests/ui/abi/unsupported.i686.stderr @@ -1,5 +1,5 @@ warning: the calling convention "ptx-kernel" is not supported on this target - --> $DIR/unsupported.rs:35:15 + --> $DIR/unsupported.rs:36:15 | LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,13 +9,13 @@ LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) { = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:40:1 + --> $DIR/unsupported.rs:41:1 | LL | extern "ptx-kernel" {} | ^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "aapcs" is not supported on this target - --> $DIR/unsupported.rs:49:17 + --> $DIR/unsupported.rs:52:17 | LL | fn aapcs_ptr(f: extern "aapcs" fn()) { | ^^^^^^^^^^^^^^^^^^^ @@ -24,13 +24,13 @@ LL | fn aapcs_ptr(f: extern "aapcs" fn()) { = note: for more information, see issue #130260 error[E0570]: `"aapcs"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:62:1 + --> $DIR/unsupported.rs:65:1 | LL | extern "aapcs" {} | ^^^^^^^^^^^^^^^^^ warning: the calling convention "msp430-interrupt" is not supported on this target - --> $DIR/unsupported.rs:71:18 + --> $DIR/unsupported.rs:74:18 | LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -39,13 +39,13 @@ LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { = note: for more information, see issue #130260 error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:76:1 + --> $DIR/unsupported.rs:79:1 | LL | extern "msp430-interrupt" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "avr-interrupt" is not supported on this target - --> $DIR/unsupported.rs:81:15 + --> $DIR/unsupported.rs:84:15 | LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -54,13 +54,13 @@ LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { = note: for more information, see issue #130260 error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:86:1 + --> $DIR/unsupported.rs:89:1 | LL | extern "avr-interrupt" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "riscv-interrupt-m" is not supported on this target - --> $DIR/unsupported.rs:94:17 + --> $DIR/unsupported.rs:97:17 | LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -69,13 +69,13 @@ LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) { = note: for more information, see issue #130260 error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:105:1 + --> $DIR/unsupported.rs:108:1 | LL | extern "riscv-interrupt-m" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target - --> $DIR/unsupported.rs:185:21 + --> $DIR/unsupported.rs:188:21 | LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -84,7 +84,7 @@ LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { = note: for more information, see issue #130260 warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target - --> $DIR/unsupported.rs:193:22 + --> $DIR/unsupported.rs:196:22 | LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -93,47 +93,53 @@ LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { = note: for more information, see issue #130260 error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:198:1 + --> $DIR/unsupported.rs:201:1 | LL | extern "C-cmse-nonsecure-entry" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:33:1 + --> $DIR/unsupported.rs:34:1 | LL | extern "ptx-kernel" fn ptx() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0570]: `"aapcs"` is not a supported ABI for the current target +error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target --> $DIR/unsupported.rs:43:1 | +LL | extern "gpu-kernel" fn gpu() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0570]: `"aapcs"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:46:1 + | LL | extern "aapcs" fn aapcs() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:69:1 + --> $DIR/unsupported.rs:72:1 | LL | extern "msp430-interrupt" fn msp430() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:79:1 + --> $DIR/unsupported.rs:82:1 | LL | extern "avr-interrupt" fn avr() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:89:1 + --> $DIR/unsupported.rs:92:1 | LL | extern "riscv-interrupt-m" fn riscv() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:191:1 + --> $DIR/unsupported.rs:194:1 | LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 12 previous errors; 7 warnings emitted +error: aborting due to 13 previous errors; 7 warnings emitted For more information about this error, try `rustc --explain E0570`. diff --git a/tests/ui/abi/unsupported.riscv32.stderr b/tests/ui/abi/unsupported.riscv32.stderr index 92728b1df18c0..abf403da8bd15 100644 --- a/tests/ui/abi/unsupported.riscv32.stderr +++ b/tests/ui/abi/unsupported.riscv32.stderr @@ -1,5 +1,5 @@ warning: the calling convention "ptx-kernel" is not supported on this target - --> $DIR/unsupported.rs:35:15 + --> $DIR/unsupported.rs:36:15 | LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,13 +9,13 @@ LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) { = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:40:1 + --> $DIR/unsupported.rs:41:1 | LL | extern "ptx-kernel" {} | ^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "aapcs" is not supported on this target - --> $DIR/unsupported.rs:49:17 + --> $DIR/unsupported.rs:52:17 | LL | fn aapcs_ptr(f: extern "aapcs" fn()) { | ^^^^^^^^^^^^^^^^^^^ @@ -24,13 +24,13 @@ LL | fn aapcs_ptr(f: extern "aapcs" fn()) { = note: for more information, see issue #130260 error[E0570]: `"aapcs"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:62:1 + --> $DIR/unsupported.rs:65:1 | LL | extern "aapcs" {} | ^^^^^^^^^^^^^^^^^ warning: the calling convention "msp430-interrupt" is not supported on this target - --> $DIR/unsupported.rs:71:18 + --> $DIR/unsupported.rs:74:18 | LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -39,13 +39,13 @@ LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { = note: for more information, see issue #130260 error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:76:1 + --> $DIR/unsupported.rs:79:1 | LL | extern "msp430-interrupt" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "avr-interrupt" is not supported on this target - --> $DIR/unsupported.rs:81:15 + --> $DIR/unsupported.rs:84:15 | LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -54,13 +54,13 @@ LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { = note: for more information, see issue #130260 error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:86:1 + --> $DIR/unsupported.rs:89:1 | LL | extern "avr-interrupt" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "x86-interrupt" is not supported on this target - --> $DIR/unsupported.rs:116:15 + --> $DIR/unsupported.rs:119:15 | LL | fn x86_ptr(f: extern "x86-interrupt" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -69,13 +69,13 @@ LL | fn x86_ptr(f: extern "x86-interrupt" fn()) { = note: for more information, see issue #130260 error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:127:1 + --> $DIR/unsupported.rs:130:1 | LL | extern "x86-interrupt" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "thiscall" is not supported on this target - --> $DIR/unsupported.rs:139:20 + --> $DIR/unsupported.rs:142:20 | LL | fn thiscall_ptr(f: extern "thiscall" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^ @@ -84,13 +84,13 @@ LL | fn thiscall_ptr(f: extern "thiscall" fn()) { = note: for more information, see issue #130260 error[E0570]: `"thiscall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:152:1 + --> $DIR/unsupported.rs:155:1 | LL | extern "thiscall" {} | ^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "stdcall" is not supported on this target - --> $DIR/unsupported.rs:165:19 + --> $DIR/unsupported.rs:168:19 | LL | fn stdcall_ptr(f: extern "stdcall" fn()) { | ^^^^^^^^^^^^^^^^^^^^^ @@ -99,13 +99,13 @@ LL | fn stdcall_ptr(f: extern "stdcall" fn()) { = note: for more information, see issue #130260 error[E0570]: `"stdcall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:178:1 + --> $DIR/unsupported.rs:181:1 | LL | extern "stdcall" {} | ^^^^^^^^^^^^^^^^^^^ warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target - --> $DIR/unsupported.rs:185:21 + --> $DIR/unsupported.rs:188:21 | LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -114,7 +114,7 @@ LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { = note: for more information, see issue #130260 warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target - --> $DIR/unsupported.rs:193:22 + --> $DIR/unsupported.rs:196:22 | LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -123,59 +123,65 @@ LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { = note: for more information, see issue #130260 error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:198:1 + --> $DIR/unsupported.rs:201:1 | LL | extern "C-cmse-nonsecure-entry" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:33:1 + --> $DIR/unsupported.rs:34:1 | LL | extern "ptx-kernel" fn ptx() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0570]: `"aapcs"` is not a supported ABI for the current target +error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target --> $DIR/unsupported.rs:43:1 | +LL | extern "gpu-kernel" fn gpu() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0570]: `"aapcs"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:46:1 + | LL | extern "aapcs" fn aapcs() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:69:1 + --> $DIR/unsupported.rs:72:1 | LL | extern "msp430-interrupt" fn msp430() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:79:1 + --> $DIR/unsupported.rs:82:1 | LL | extern "avr-interrupt" fn avr() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:111:1 + --> $DIR/unsupported.rs:114:1 | LL | extern "x86-interrupt" fn x86() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"thiscall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:133:1 + --> $DIR/unsupported.rs:136:1 | LL | extern "thiscall" fn thiscall() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"stdcall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:159:1 + --> $DIR/unsupported.rs:162:1 | LL | extern "stdcall" fn stdcall() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:191:1 + --> $DIR/unsupported.rs:194:1 | LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 16 previous errors; 9 warnings emitted +error: aborting due to 17 previous errors; 9 warnings emitted For more information about this error, try `rustc --explain E0570`. diff --git a/tests/ui/abi/unsupported.riscv64.stderr b/tests/ui/abi/unsupported.riscv64.stderr index 92728b1df18c0..abf403da8bd15 100644 --- a/tests/ui/abi/unsupported.riscv64.stderr +++ b/tests/ui/abi/unsupported.riscv64.stderr @@ -1,5 +1,5 @@ warning: the calling convention "ptx-kernel" is not supported on this target - --> $DIR/unsupported.rs:35:15 + --> $DIR/unsupported.rs:36:15 | LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,13 +9,13 @@ LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) { = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:40:1 + --> $DIR/unsupported.rs:41:1 | LL | extern "ptx-kernel" {} | ^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "aapcs" is not supported on this target - --> $DIR/unsupported.rs:49:17 + --> $DIR/unsupported.rs:52:17 | LL | fn aapcs_ptr(f: extern "aapcs" fn()) { | ^^^^^^^^^^^^^^^^^^^ @@ -24,13 +24,13 @@ LL | fn aapcs_ptr(f: extern "aapcs" fn()) { = note: for more information, see issue #130260 error[E0570]: `"aapcs"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:62:1 + --> $DIR/unsupported.rs:65:1 | LL | extern "aapcs" {} | ^^^^^^^^^^^^^^^^^ warning: the calling convention "msp430-interrupt" is not supported on this target - --> $DIR/unsupported.rs:71:18 + --> $DIR/unsupported.rs:74:18 | LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -39,13 +39,13 @@ LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { = note: for more information, see issue #130260 error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:76:1 + --> $DIR/unsupported.rs:79:1 | LL | extern "msp430-interrupt" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "avr-interrupt" is not supported on this target - --> $DIR/unsupported.rs:81:15 + --> $DIR/unsupported.rs:84:15 | LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -54,13 +54,13 @@ LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { = note: for more information, see issue #130260 error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:86:1 + --> $DIR/unsupported.rs:89:1 | LL | extern "avr-interrupt" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "x86-interrupt" is not supported on this target - --> $DIR/unsupported.rs:116:15 + --> $DIR/unsupported.rs:119:15 | LL | fn x86_ptr(f: extern "x86-interrupt" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -69,13 +69,13 @@ LL | fn x86_ptr(f: extern "x86-interrupt" fn()) { = note: for more information, see issue #130260 error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:127:1 + --> $DIR/unsupported.rs:130:1 | LL | extern "x86-interrupt" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "thiscall" is not supported on this target - --> $DIR/unsupported.rs:139:20 + --> $DIR/unsupported.rs:142:20 | LL | fn thiscall_ptr(f: extern "thiscall" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^ @@ -84,13 +84,13 @@ LL | fn thiscall_ptr(f: extern "thiscall" fn()) { = note: for more information, see issue #130260 error[E0570]: `"thiscall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:152:1 + --> $DIR/unsupported.rs:155:1 | LL | extern "thiscall" {} | ^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "stdcall" is not supported on this target - --> $DIR/unsupported.rs:165:19 + --> $DIR/unsupported.rs:168:19 | LL | fn stdcall_ptr(f: extern "stdcall" fn()) { | ^^^^^^^^^^^^^^^^^^^^^ @@ -99,13 +99,13 @@ LL | fn stdcall_ptr(f: extern "stdcall" fn()) { = note: for more information, see issue #130260 error[E0570]: `"stdcall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:178:1 + --> $DIR/unsupported.rs:181:1 | LL | extern "stdcall" {} | ^^^^^^^^^^^^^^^^^^^ warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target - --> $DIR/unsupported.rs:185:21 + --> $DIR/unsupported.rs:188:21 | LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -114,7 +114,7 @@ LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { = note: for more information, see issue #130260 warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target - --> $DIR/unsupported.rs:193:22 + --> $DIR/unsupported.rs:196:22 | LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -123,59 +123,65 @@ LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { = note: for more information, see issue #130260 error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:198:1 + --> $DIR/unsupported.rs:201:1 | LL | extern "C-cmse-nonsecure-entry" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:33:1 + --> $DIR/unsupported.rs:34:1 | LL | extern "ptx-kernel" fn ptx() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0570]: `"aapcs"` is not a supported ABI for the current target +error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target --> $DIR/unsupported.rs:43:1 | +LL | extern "gpu-kernel" fn gpu() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0570]: `"aapcs"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:46:1 + | LL | extern "aapcs" fn aapcs() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:69:1 + --> $DIR/unsupported.rs:72:1 | LL | extern "msp430-interrupt" fn msp430() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:79:1 + --> $DIR/unsupported.rs:82:1 | LL | extern "avr-interrupt" fn avr() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:111:1 + --> $DIR/unsupported.rs:114:1 | LL | extern "x86-interrupt" fn x86() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"thiscall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:133:1 + --> $DIR/unsupported.rs:136:1 | LL | extern "thiscall" fn thiscall() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"stdcall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:159:1 + --> $DIR/unsupported.rs:162:1 | LL | extern "stdcall" fn stdcall() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:191:1 + --> $DIR/unsupported.rs:194:1 | LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 16 previous errors; 9 warnings emitted +error: aborting due to 17 previous errors; 9 warnings emitted For more information about this error, try `rustc --explain E0570`. diff --git a/tests/ui/abi/unsupported.rs b/tests/ui/abi/unsupported.rs index a56f001ef9520..7d4142f0dee79 100644 --- a/tests/ui/abi/unsupported.rs +++ b/tests/ui/abi/unsupported.rs @@ -19,6 +19,7 @@ abi_ptx, abi_msp430_interrupt, abi_avr_interrupt, + abi_gpu_kernel, abi_x86_interrupt, abi_riscv_interrupt, abi_c_cmse_nonsecure_call, @@ -39,6 +40,8 @@ fn ptx_ptr(f: extern "ptx-kernel" fn()) { } extern "ptx-kernel" {} //~^ ERROR is not a supported ABI +extern "gpu-kernel" fn gpu() {} +//~^ ERROR is not a supported ABI extern "aapcs" fn aapcs() {} //[x64]~^ ERROR is not a supported ABI diff --git a/tests/ui/abi/unsupported.x64.stderr b/tests/ui/abi/unsupported.x64.stderr index 27a4f1f532c6c..824a33c948add 100644 --- a/tests/ui/abi/unsupported.x64.stderr +++ b/tests/ui/abi/unsupported.x64.stderr @@ -1,5 +1,5 @@ warning: the calling convention "ptx-kernel" is not supported on this target - --> $DIR/unsupported.rs:35:15 + --> $DIR/unsupported.rs:36:15 | LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,13 +9,13 @@ LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) { = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:40:1 + --> $DIR/unsupported.rs:41:1 | LL | extern "ptx-kernel" {} | ^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "aapcs" is not supported on this target - --> $DIR/unsupported.rs:49:17 + --> $DIR/unsupported.rs:52:17 | LL | fn aapcs_ptr(f: extern "aapcs" fn()) { | ^^^^^^^^^^^^^^^^^^^ @@ -24,13 +24,13 @@ LL | fn aapcs_ptr(f: extern "aapcs" fn()) { = note: for more information, see issue #130260 error[E0570]: `"aapcs"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:62:1 + --> $DIR/unsupported.rs:65:1 | LL | extern "aapcs" {} | ^^^^^^^^^^^^^^^^^ warning: the calling convention "msp430-interrupt" is not supported on this target - --> $DIR/unsupported.rs:71:18 + --> $DIR/unsupported.rs:74:18 | LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -39,13 +39,13 @@ LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) { = note: for more information, see issue #130260 error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:76:1 + --> $DIR/unsupported.rs:79:1 | LL | extern "msp430-interrupt" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "avr-interrupt" is not supported on this target - --> $DIR/unsupported.rs:81:15 + --> $DIR/unsupported.rs:84:15 | LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -54,13 +54,13 @@ LL | fn avr_ptr(f: extern "avr-interrupt" fn()) { = note: for more information, see issue #130260 error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:86:1 + --> $DIR/unsupported.rs:89:1 | LL | extern "avr-interrupt" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "riscv-interrupt-m" is not supported on this target - --> $DIR/unsupported.rs:94:17 + --> $DIR/unsupported.rs:97:17 | LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -69,13 +69,13 @@ LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) { = note: for more information, see issue #130260 error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:105:1 + --> $DIR/unsupported.rs:108:1 | LL | extern "riscv-interrupt-m" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "thiscall" is not supported on this target - --> $DIR/unsupported.rs:139:20 + --> $DIR/unsupported.rs:142:20 | LL | fn thiscall_ptr(f: extern "thiscall" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^ @@ -84,13 +84,13 @@ LL | fn thiscall_ptr(f: extern "thiscall" fn()) { = note: for more information, see issue #130260 error[E0570]: `"thiscall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:152:1 + --> $DIR/unsupported.rs:155:1 | LL | extern "thiscall" {} | ^^^^^^^^^^^^^^^^^^^^ warning: the calling convention "stdcall" is not supported on this target - --> $DIR/unsupported.rs:165:19 + --> $DIR/unsupported.rs:168:19 | LL | fn stdcall_ptr(f: extern "stdcall" fn()) { | ^^^^^^^^^^^^^^^^^^^^^ @@ -99,13 +99,13 @@ LL | fn stdcall_ptr(f: extern "stdcall" fn()) { = note: for more information, see issue #130260 error[E0570]: `"stdcall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:178:1 + --> $DIR/unsupported.rs:181:1 | LL | extern "stdcall" {} | ^^^^^^^^^^^^^^^^^^^ warning: the calling convention "C-cmse-nonsecure-call" is not supported on this target - --> $DIR/unsupported.rs:185:21 + --> $DIR/unsupported.rs:188:21 | LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -114,7 +114,7 @@ LL | fn cmse_call_ptr(f: extern "C-cmse-nonsecure-call" fn()) { = note: for more information, see issue #130260 warning: the calling convention "C-cmse-nonsecure-entry" is not supported on this target - --> $DIR/unsupported.rs:193:22 + --> $DIR/unsupported.rs:196:22 | LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -123,59 +123,65 @@ LL | fn cmse_entry_ptr(f: extern "C-cmse-nonsecure-entry" fn()) { = note: for more information, see issue #130260 error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:198:1 + --> $DIR/unsupported.rs:201:1 | LL | extern "C-cmse-nonsecure-entry" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:33:1 + --> $DIR/unsupported.rs:34:1 | LL | extern "ptx-kernel" fn ptx() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0570]: `"aapcs"` is not a supported ABI for the current target +error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target --> $DIR/unsupported.rs:43:1 | +LL | extern "gpu-kernel" fn gpu() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0570]: `"aapcs"` is not a supported ABI for the current target + --> $DIR/unsupported.rs:46:1 + | LL | extern "aapcs" fn aapcs() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:69:1 + --> $DIR/unsupported.rs:72:1 | LL | extern "msp430-interrupt" fn msp430() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:79:1 + --> $DIR/unsupported.rs:82:1 | LL | extern "avr-interrupt" fn avr() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:89:1 + --> $DIR/unsupported.rs:92:1 | LL | extern "riscv-interrupt-m" fn riscv() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"thiscall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:133:1 + --> $DIR/unsupported.rs:136:1 | LL | extern "thiscall" fn thiscall() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"stdcall"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:159:1 + --> $DIR/unsupported.rs:162:1 | LL | extern "stdcall" fn stdcall() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target - --> $DIR/unsupported.rs:191:1 + --> $DIR/unsupported.rs:194:1 | LL | extern "C-cmse-nonsecure-entry" fn cmse_entry() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 16 previous errors; 9 warnings emitted +error: aborting due to 17 previous errors; 9 warnings emitted For more information about this error, try `rustc --explain E0570`. diff --git a/tests/ui/abi/win64-zst.rs b/tests/ui/abi/win64-zst.rs deleted file mode 100644 index bc4e0e629eb42..0000000000000 --- a/tests/ui/abi/win64-zst.rs +++ /dev/null @@ -1,24 +0,0 @@ -//@ normalize-stderr: "(abi|pref|unadjusted_abi_align): Align\([1-8] bytes\)" -> "$1: $$SOME_ALIGN" -//@ only-x86_64 - -//@ revisions: x86_64-linux -//@[x86_64-linux] compile-flags: --target x86_64-unknown-linux-gnu -//@[x86_64-linux] needs-llvm-components: x86 - -//@ revisions: x86_64-windows-gnu -//@[x86_64-windows-gnu] compile-flags: --target x86_64-pc-windows-gnu -//@[x86_64-windows-gnu] needs-llvm-components: x86 - -//@ revisions: x86_64-windows-msvc -//@[x86_64-windows-msvc] compile-flags: --target x86_64-pc-windows-msvc -//@[x86_64-windows-msvc] needs-llvm-components: x86 - -#![feature(no_core, lang_items, rustc_attrs)] -#![no_core] -#![crate_type = "lib"] - -#[lang = "sized"] -trait Sized {} - -#[rustc_abi(debug)] -extern "win64" fn pass_zst(_: ()) {} //~ ERROR: fn_abi diff --git a/tests/ui/abi/win64-zst.x86_64-linux.stderr b/tests/ui/abi/win64-zst.x86_64-linux.stderr deleted file mode 100644 index 76d90670eb1dd..0000000000000 --- a/tests/ui/abi/win64-zst.x86_64-linux.stderr +++ /dev/null @@ -1,67 +0,0 @@ -error: fn_abi_of(pass_zst) = FnAbi { - args: [ - ArgAbi { - layout: TyAndLayout { - ty: (), - layout: Layout { - size: Size(0 bytes), - align: AbiAndPrefAlign { - abi: $SOME_ALIGN, - pref: $SOME_ALIGN, - }, - abi: Memory { - sized: true, - }, - fields: Arbitrary { - offsets: [], - memory_index: [], - }, - largest_niche: None, - variants: Single { - index: 0, - }, - max_repr_align: None, - unadjusted_abi_align: $SOME_ALIGN, - }, - }, - mode: Ignore, - }, - ], - ret: ArgAbi { - layout: TyAndLayout { - ty: (), - layout: Layout { - size: Size(0 bytes), - align: AbiAndPrefAlign { - abi: $SOME_ALIGN, - pref: $SOME_ALIGN, - }, - abi: Memory { - sized: true, - }, - fields: Arbitrary { - offsets: [], - memory_index: [], - }, - largest_niche: None, - variants: Single { - index: 0, - }, - max_repr_align: None, - unadjusted_abi_align: $SOME_ALIGN, - }, - }, - mode: Ignore, - }, - c_variadic: false, - fixed_count: 1, - conv: X86_64Win64, - can_unwind: false, - } - --> $DIR/win64-zst.rs:24:1 - | -LL | extern "win64" fn pass_zst(_: ()) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/abi/win64-zst.x86_64-windows-gnu.stderr b/tests/ui/abi/win64-zst.x86_64-windows-gnu.stderr deleted file mode 100644 index 7ee90e2474413..0000000000000 --- a/tests/ui/abi/win64-zst.x86_64-windows-gnu.stderr +++ /dev/null @@ -1,78 +0,0 @@ -error: fn_abi_of(pass_zst) = FnAbi { - args: [ - ArgAbi { - layout: TyAndLayout { - ty: (), - layout: Layout { - size: Size(0 bytes), - align: AbiAndPrefAlign { - abi: $SOME_ALIGN, - pref: $SOME_ALIGN, - }, - abi: Memory { - sized: true, - }, - fields: Arbitrary { - offsets: [], - memory_index: [], - }, - largest_niche: None, - variants: Single { - index: 0, - }, - max_repr_align: None, - unadjusted_abi_align: $SOME_ALIGN, - }, - }, - mode: Indirect { - attrs: ArgAttributes { - regular: NoAlias | NoCapture | NonNull | NoUndef, - arg_ext: None, - pointee_size: Size(0 bytes), - pointee_align: Some( - Align(1 bytes), - ), - }, - meta_attrs: None, - on_stack: false, - }, - }, - ], - ret: ArgAbi { - layout: TyAndLayout { - ty: (), - layout: Layout { - size: Size(0 bytes), - align: AbiAndPrefAlign { - abi: $SOME_ALIGN, - pref: $SOME_ALIGN, - }, - abi: Memory { - sized: true, - }, - fields: Arbitrary { - offsets: [], - memory_index: [], - }, - largest_niche: None, - variants: Single { - index: 0, - }, - max_repr_align: None, - unadjusted_abi_align: $SOME_ALIGN, - }, - }, - mode: Ignore, - }, - c_variadic: false, - fixed_count: 1, - conv: X86_64Win64, - can_unwind: false, - } - --> $DIR/win64-zst.rs:24:1 - | -LL | extern "win64" fn pass_zst(_: ()) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/abi/win64-zst.x86_64-windows-msvc.stderr b/tests/ui/abi/win64-zst.x86_64-windows-msvc.stderr deleted file mode 100644 index 76d90670eb1dd..0000000000000 --- a/tests/ui/abi/win64-zst.x86_64-windows-msvc.stderr +++ /dev/null @@ -1,67 +0,0 @@ -error: fn_abi_of(pass_zst) = FnAbi { - args: [ - ArgAbi { - layout: TyAndLayout { - ty: (), - layout: Layout { - size: Size(0 bytes), - align: AbiAndPrefAlign { - abi: $SOME_ALIGN, - pref: $SOME_ALIGN, - }, - abi: Memory { - sized: true, - }, - fields: Arbitrary { - offsets: [], - memory_index: [], - }, - largest_niche: None, - variants: Single { - index: 0, - }, - max_repr_align: None, - unadjusted_abi_align: $SOME_ALIGN, - }, - }, - mode: Ignore, - }, - ], - ret: ArgAbi { - layout: TyAndLayout { - ty: (), - layout: Layout { - size: Size(0 bytes), - align: AbiAndPrefAlign { - abi: $SOME_ALIGN, - pref: $SOME_ALIGN, - }, - abi: Memory { - sized: true, - }, - fields: Arbitrary { - offsets: [], - memory_index: [], - }, - largest_niche: None, - variants: Single { - index: 0, - }, - max_repr_align: None, - unadjusted_abi_align: $SOME_ALIGN, - }, - }, - mode: Ignore, - }, - c_variadic: false, - fixed_count: 1, - conv: X86_64Win64, - can_unwind: false, - } - --> $DIR/win64-zst.rs:24:1 - | -LL | extern "win64" fn pass_zst(_: ()) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/alloc-error/default-alloc-error-hook.rs b/tests/ui/alloc-error/default-alloc-error-hook.rs index 5f977460b8c6e..7fbc66ca5f472 100644 --- a/tests/ui/alloc-error/default-alloc-error-hook.rs +++ b/tests/ui/alloc-error/default-alloc-error-hook.rs @@ -1,6 +1,5 @@ //@ run-pass -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess use std::alloc::{Layout, handle_alloc_error}; use std::env; diff --git a/tests/ui/amdgpu-require-explicit-cpu.nocpu.stderr b/tests/ui/amdgpu-require-explicit-cpu.nocpu.stderr new file mode 100644 index 0000000000000..7480a8ed38f15 --- /dev/null +++ b/tests/ui/amdgpu-require-explicit-cpu.nocpu.stderr @@ -0,0 +1,4 @@ +error: target requires explicitly specifying a cpu with `-C target-cpu` + +error: aborting due to 1 previous error + diff --git a/tests/ui/amdgpu-require-explicit-cpu.rs b/tests/ui/amdgpu-require-explicit-cpu.rs new file mode 100644 index 0000000000000..46778a1094fab --- /dev/null +++ b/tests/ui/amdgpu-require-explicit-cpu.rs @@ -0,0 +1,17 @@ +//@ revisions: nocpu cpu +//@ no-prefer-dynamic +//@ compile-flags: --crate-type=cdylib --target=amdgcn-amd-amdhsa +//@ needs-llvm-components: amdgpu +//@ needs-rust-lld +//@[nocpu] error-pattern: target requires explicitly specifying a cpu +//@[nocpu] build-fail +//@[cpu] compile-flags: -Ctarget-cpu=gfx900 +//@[cpu] build-pass + +#![feature(no_core, lang_items)] +#![no_core] + +#[lang="sized"] +trait Sized {} + +pub fn foo() {} diff --git a/tests/ui/anon-params/anon-params-denied-2018.stderr b/tests/ui/anon-params/anon-params-denied-2018.stderr index 2d6356ffcb1c0..0c38987ca6f1a 100644 --- a/tests/ui/anon-params/anon-params-denied-2018.stderr +++ b/tests/ui/anon-params/anon-params-denied-2018.stderr @@ -31,8 +31,9 @@ LL | fn foo_with_ref(self: &mut i32); | +++++ help: if this is a parameter name, give it a type | -LL | fn foo_with_ref(i32: &mut TypeName); - | ~~~~~~~~~~~~~~~~~~ +LL - fn foo_with_ref(&mut i32); +LL + fn foo_with_ref(i32: &mut TypeName); + | help: if this is a type, explicitly ignore the parameter name | LL | fn foo_with_ref(_: &mut i32); diff --git a/tests/ui/argument-suggestions/basic.stderr b/tests/ui/argument-suggestions/basic.stderr index 2d52df2123369..9a639d4b5e4ef 100644 --- a/tests/ui/argument-suggestions/basic.stderr +++ b/tests/ui/argument-suggestions/basic.stderr @@ -42,8 +42,9 @@ LL | fn missing(_i: u32) {} | ^^^^^^^ ------- help: provide the argument | -LL | missing(/* u32 */); - | ~~~~~~~~~~~ +LL - missing(); +LL + missing(/* u32 */); + | error[E0308]: arguments to this function are incorrect --> $DIR/basic.rs:23:5 @@ -57,11 +58,12 @@ note: function defined here --> $DIR/basic.rs:16:4 | LL | fn swapped(_i: u32, _s: &str) {} - | ^^^^^^^ ------- -------- + | ^^^^^^^ help: swap these arguments | -LL | swapped(1, ""); - | ~~~~~~~ +LL - swapped("", 1); +LL + swapped(1, ""); + | error[E0308]: arguments to this function are incorrect --> $DIR/basic.rs:24:5 @@ -76,11 +78,12 @@ note: function defined here --> $DIR/basic.rs:17:4 | LL | fn permuted(_x: X, _y: Y, _z: Z) {} - | ^^^^^^^^ ----- ----- ----- + | ^^^^^^^^ help: reorder these arguments | -LL | permuted(X {}, Y {}, Z {}); - | ~~~~~~~~~~~~~~~~~~ +LL - permuted(Y {}, Z {}, X {}); +LL + permuted(X {}, Y {}, Z {}); + | error[E0057]: this function takes 1 argument but 0 arguments were supplied --> $DIR/basic.rs:27:5 @@ -95,8 +98,9 @@ LL | let closure = |x| x; | ^^^ help: provide the argument | -LL | closure(/* x */); - | ~~~~~~~~~ +LL - closure(); +LL + closure(/* x */); + | error: aborting due to 6 previous errors diff --git a/tests/ui/argument-suggestions/complex.stderr b/tests/ui/argument-suggestions/complex.stderr index bb3817421df19..be8838f17d75b 100644 --- a/tests/ui/argument-suggestions/complex.stderr +++ b/tests/ui/argument-suggestions/complex.stderr @@ -8,11 +8,12 @@ note: function defined here --> $DIR/complex.rs:11:4 | LL | fn complex(_i: u32, _s: &str, _e: E, _f: F, _g: G, _x: X, _y: Y, _z: Z ) {} - | ^^^^^^^ ------- -------- ----- ----- ----- ----- ----- ----- + | ^^^^^^^ ------- ----- help: did you mean | -LL | complex(/* u32 */, &"", /* E */, F::X2, G{}, X {}, Y {}, Z {}); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - complex(1.0, H {}, &"", G{}, F::X2, Z {}, X {}, Y {}); +LL + complex(/* u32 */, &"", /* E */, F::X2, G{}, X {}, Y {}, Z {}); + | error: aborting due to 1 previous error diff --git a/tests/ui/argument-suggestions/display-is-suggestable.stderr b/tests/ui/argument-suggestions/display-is-suggestable.stderr index eea88c3e78d70..bb5df1ec234cd 100644 --- a/tests/ui/argument-suggestions/display-is-suggestable.stderr +++ b/tests/ui/argument-suggestions/display-is-suggestable.stderr @@ -11,8 +11,9 @@ LL | fn foo(x: &(dyn Display + Send)) {} | ^^^ ------------------------ help: provide the argument | -LL | foo(/* &dyn std::fmt::Display + Send */); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - foo(); +LL + foo(/* &dyn std::fmt::Display + Send */); + | error: aborting due to 1 previous error diff --git a/tests/ui/argument-suggestions/extern-fn-arg-names.stderr b/tests/ui/argument-suggestions/extern-fn-arg-names.stderr index 47fbfc98c676a..62670316cd163 100644 --- a/tests/ui/argument-suggestions/extern-fn-arg-names.stderr +++ b/tests/ui/argument-suggestions/extern-fn-arg-names.stderr @@ -14,11 +14,12 @@ note: function defined here --> $DIR/extern-fn-arg-names.rs:2:8 | LL | fn dstfn(src: i32, dst: err); - | ^^^^^ + | ^^^^^ --- help: provide the argument | -LL | dstfn(1, /* dst */); - | ~~~~~~~~~~~~~~ +LL - dstfn(1); +LL + dstfn(1, /* dst */); + | error: aborting due to 2 previous errors diff --git a/tests/ui/argument-suggestions/extra_arguments.stderr b/tests/ui/argument-suggestions/extra_arguments.stderr index 8c95cc86a273a..5ed42a9ddd218 100644 --- a/tests/ui/argument-suggestions/extra_arguments.stderr +++ b/tests/ui/argument-suggestions/extra_arguments.stderr @@ -44,7 +44,7 @@ note: function defined here --> $DIR/extra_arguments.rs:2:4 | LL | fn one_arg(_a: T) {} - | ^^^^^^^ ----- + | ^^^^^^^ help: remove the extra argument | LL - one_arg(1, 1); @@ -61,7 +61,7 @@ note: function defined here --> $DIR/extra_arguments.rs:2:4 | LL | fn one_arg(_a: T) {} - | ^^^^^^^ ----- + | ^^^^^^^ help: remove the extra argument | LL - one_arg(1, ""); @@ -80,7 +80,7 @@ note: function defined here --> $DIR/extra_arguments.rs:2:4 | LL | fn one_arg(_a: T) {} - | ^^^^^^^ ----- + | ^^^^^^^ help: remove the extra arguments | LL - one_arg(1, "", 1.0); @@ -97,7 +97,7 @@ note: function defined here --> $DIR/extra_arguments.rs:3:4 | LL | fn two_arg_same(_a: i32, _b: i32) {} - | ^^^^^^^^^^^^ ------- ------- + | ^^^^^^^^^^^^ help: remove the extra argument | LL - two_arg_same(1, 1, 1); @@ -114,7 +114,7 @@ note: function defined here --> $DIR/extra_arguments.rs:3:4 | LL | fn two_arg_same(_a: i32, _b: i32) {} - | ^^^^^^^^^^^^ ------- ------- + | ^^^^^^^^^^^^ help: remove the extra argument | LL - two_arg_same(1, 1, 1.0); @@ -131,7 +131,7 @@ note: function defined here --> $DIR/extra_arguments.rs:4:4 | LL | fn two_arg_diff(_a: i32, _b: &str) {} - | ^^^^^^^^^^^^ ------- -------- + | ^^^^^^^^^^^^ help: remove the extra argument | LL - two_arg_diff(1, 1, ""); @@ -148,7 +148,7 @@ note: function defined here --> $DIR/extra_arguments.rs:4:4 | LL | fn two_arg_diff(_a: i32, _b: &str) {} - | ^^^^^^^^^^^^ ------- -------- + | ^^^^^^^^^^^^ help: remove the extra argument | LL - two_arg_diff(1, "", ""); @@ -167,7 +167,7 @@ note: function defined here --> $DIR/extra_arguments.rs:4:4 | LL | fn two_arg_diff(_a: i32, _b: &str) {} - | ^^^^^^^^^^^^ ------- -------- + | ^^^^^^^^^^^^ help: remove the extra arguments | LL - two_arg_diff(1, 1, "", ""); @@ -186,7 +186,7 @@ note: function defined here --> $DIR/extra_arguments.rs:4:4 | LL | fn two_arg_diff(_a: i32, _b: &str) {} - | ^^^^^^^^^^^^ ------- -------- + | ^^^^^^^^^^^^ help: remove the extra arguments | LL - two_arg_diff(1, "", 1, ""); @@ -203,7 +203,7 @@ note: function defined here --> $DIR/extra_arguments.rs:3:4 | LL | fn two_arg_same(_a: i32, _b: i32) {} - | ^^^^^^^^^^^^ ------- ------- + | ^^^^^^^^^^^^ help: remove the extra argument | LL - two_arg_same(1, 1, ""); @@ -220,7 +220,7 @@ note: function defined here --> $DIR/extra_arguments.rs:4:4 | LL | fn two_arg_diff(_a: i32, _b: &str) {} - | ^^^^^^^^^^^^ ------- -------- + | ^^^^^^^^^^^^ help: remove the extra argument | LL - two_arg_diff(1, 1, ""); @@ -240,7 +240,7 @@ note: function defined here --> $DIR/extra_arguments.rs:3:4 | LL | fn two_arg_same(_a: i32, _b: i32) {} - | ^^^^^^^^^^^^ ------- ------- + | ^^^^^^^^^^^^ help: remove the extra argument | LL - 1, @@ -261,7 +261,7 @@ note: function defined here --> $DIR/extra_arguments.rs:4:4 | LL | fn two_arg_diff(_a: i32, _b: &str) {} - | ^^^^^^^^^^^^ ------- -------- + | ^^^^^^^^^^^^ help: remove the extra argument | LL - 1, @@ -335,7 +335,7 @@ note: function defined here --> $DIR/extra_arguments.rs:2:4 | LL | fn one_arg(_a: T) {} - | ^^^^^^^ ----- + | ^^^^^^^ help: remove the extra argument | LL - one_arg(1, panic!()); @@ -352,7 +352,7 @@ note: function defined here --> $DIR/extra_arguments.rs:2:4 | LL | fn one_arg(_a: T) {} - | ^^^^^^^ ----- + | ^^^^^^^ help: remove the extra argument | LL - one_arg(panic!(), 1); @@ -369,7 +369,7 @@ note: function defined here --> $DIR/extra_arguments.rs:2:4 | LL | fn one_arg(_a: T) {} - | ^^^^^^^ ----- + | ^^^^^^^ help: remove the extra argument | LL - one_arg(stringify!($e), 1); @@ -386,7 +386,7 @@ note: function defined here --> $DIR/extra_arguments.rs:2:4 | LL | fn one_arg(_a: T) {} - | ^^^^^^^ ----- + | ^^^^^^^ help: remove the extra argument | LL - one_arg(for _ in 1.. {}, 1); diff --git a/tests/ui/argument-suggestions/invalid_arguments.stderr b/tests/ui/argument-suggestions/invalid_arguments.stderr index d26f33d098be0..a50058149b811 100644 --- a/tests/ui/argument-suggestions/invalid_arguments.stderr +++ b/tests/ui/argument-suggestions/invalid_arguments.stderr @@ -150,7 +150,7 @@ note: function defined here --> $DIR/invalid_arguments.rs:8:4 | LL | fn three_arg_diff(_a: i32, _b: f32, _c: &str) {} - | ^^^^^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^^^^^ ------- ------- error[E0308]: arguments to this function are incorrect --> $DIR/invalid_arguments.rs:29:3 @@ -164,7 +164,7 @@ note: function defined here --> $DIR/invalid_arguments.rs:8:4 | LL | fn three_arg_diff(_a: i32, _b: f32, _c: &str) {} - | ^^^^^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^^^^^ ------- -------- error[E0308]: arguments to this function are incorrect --> $DIR/invalid_arguments.rs:30:3 @@ -178,7 +178,7 @@ note: function defined here --> $DIR/invalid_arguments.rs:8:4 | LL | fn three_arg_diff(_a: i32, _b: f32, _c: &str) {} - | ^^^^^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^^^^^ ------- -------- error[E0308]: arguments to this function are incorrect --> $DIR/invalid_arguments.rs:32:3 @@ -249,7 +249,7 @@ note: function defined here --> $DIR/invalid_arguments.rs:9:4 | LL | fn three_arg_repeat(_a: i32, _b: i32, _c: &str) {} - | ^^^^^^^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^^^^^^^ ------- ------- error[E0308]: arguments to this function are incorrect --> $DIR/invalid_arguments.rs:39:3 @@ -263,7 +263,7 @@ note: function defined here --> $DIR/invalid_arguments.rs:9:4 | LL | fn three_arg_repeat(_a: i32, _b: i32, _c: &str) {} - | ^^^^^^^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^^^^^^^ ------- -------- error[E0308]: arguments to this function are incorrect --> $DIR/invalid_arguments.rs:40:3 @@ -277,7 +277,7 @@ note: function defined here --> $DIR/invalid_arguments.rs:9:4 | LL | fn three_arg_repeat(_a: i32, _b: i32, _c: &str) {} - | ^^^^^^^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^^^^^^^ ------- -------- error[E0308]: arguments to this function are incorrect --> $DIR/invalid_arguments.rs:42:3 diff --git a/tests/ui/argument-suggestions/issue-100478.stderr b/tests/ui/argument-suggestions/issue-100478.stderr index 94709f0ebc6a4..8889a0ab5df0b 100644 --- a/tests/ui/argument-suggestions/issue-100478.stderr +++ b/tests/ui/argument-suggestions/issue-100478.stderr @@ -11,11 +11,12 @@ note: function defined here --> $DIR/issue-100478.rs:30:4 | LL | fn three_diff(_a: T1, _b: T2, _c: T3) {} - | ^^^^^^^^^^ ------ ------ ------ + | ^^^^^^^^^^ ------ ------ help: provide the arguments | -LL | three_diff(/* T1 */, T2::new(0), /* T3 */); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - three_diff(T2::new(0)); +LL + three_diff(/* T1 */, T2::new(0), /* T3 */); + | error[E0308]: arguments to this function are incorrect --> $DIR/issue-100478.rs:35:5 @@ -31,11 +32,12 @@ note: function defined here --> $DIR/issue-100478.rs:31:4 | LL | fn four_shuffle(_a: T1, _b: T2, _c: T3, _d: T4) {} - | ^^^^^^^^^^^^ ------ ------ ------ ------ + | ^^^^^^^^^^^^ help: did you mean | -LL | four_shuffle(T1::default(), T2::default(), T3::default(), T4::default()); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - four_shuffle(T3::default(), T4::default(), T1::default(), T2::default()); +LL + four_shuffle(T1::default(), T2::default(), T3::default(), T4::default()); + | error[E0308]: arguments to this function are incorrect --> $DIR/issue-100478.rs:36:5 @@ -50,11 +52,12 @@ note: function defined here --> $DIR/issue-100478.rs:31:4 | LL | fn four_shuffle(_a: T1, _b: T2, _c: T3, _d: T4) {} - | ^^^^^^^^^^^^ ------ ------ ------ ------ + | ^^^^^^^^^^^^ ------ help: swap these arguments | -LL | four_shuffle(T1::default(), T2::default(), T3::default(), /* T4 */); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - four_shuffle(T3::default(), T2::default(), T1::default(), T3::default()); +LL + four_shuffle(T1::default(), T2::default(), T3::default(), /* T4 */); + | error[E0061]: this function takes 8 arguments but 7 arguments were supplied --> $DIR/issue-100478.rs:47:5 @@ -69,11 +72,16 @@ note: function defined here --> $DIR/issue-100478.rs:29:4 | LL | fn foo(p1: T1, p2: Arc, p3: T3, p4: Arc, p5: T5, p6: T6, p7: T7, p8: Arc) {} - | ^^^ ------ ----------- ------ ----------- ------ ------ ------ ----------- + | ^^^ ----------- help: provide the argument | -LL | foo(p1, /* Arc */, p3, p4, p5, p6, p7, p8); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - foo( +LL - +LL - p1, //p2, +LL - p3, p4, p5, p6, p7, p8, +LL - ); +LL + foo(p1, /* Arc */, p3, p4, p5, p6, p7, p8); + | error: aborting due to 4 previous errors diff --git a/tests/ui/argument-suggestions/issue-101097.stderr b/tests/ui/argument-suggestions/issue-101097.stderr index 6e21f19ab4fb4..25770e36aa370 100644 --- a/tests/ui/argument-suggestions/issue-101097.stderr +++ b/tests/ui/argument-suggestions/issue-101097.stderr @@ -13,22 +13,11 @@ note: function defined here | LL | fn f( | ^ -LL | a1: A, - | ----- -LL | a2: A, - | ----- -LL | b1: B, - | ----- -LL | b2: B, - | ----- -LL | c1: C, - | ----- -LL | c2: C, - | ----- help: did you mean | -LL | f(A, A, B, B, C, C); - | ~~~~~~~~~~~~~~~~~~ +LL - f(C, A, A, A, B, B, C); +LL + f(A, A, B, B, C, C); + | error[E0308]: arguments to this function are incorrect --> $DIR/issue-101097.rs:17:5 @@ -41,22 +30,11 @@ note: function defined here | LL | fn f( | ^ -LL | a1: A, - | ----- -LL | a2: A, - | ----- -LL | b1: B, - | ----- -LL | b2: B, - | ----- -LL | c1: C, - | ----- -LL | c2: C, - | ----- help: did you mean | -LL | f(A, A, B, B, C, C); - | ~~~~~~~~~~~~~~~~~~ +LL - f(C, C, A, A, B, B); +LL + f(A, A, B, B, C, C); + | error[E0308]: arguments to this function are incorrect --> $DIR/issue-101097.rs:18:5 @@ -72,22 +50,16 @@ note: function defined here | LL | fn f( | ^ -LL | a1: A, - | ----- -LL | a2: A, - | ----- -LL | b1: B, - | ----- -LL | b2: B, - | ----- +... LL | c1: C, | ----- LL | c2: C, | ----- help: did you mean | -LL | f(A, A, B, B, /* C */, /* C */); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - f(A, A, D, D, B, B); +LL + f(A, A, B, B, /* C */, /* C */); + | error[E0308]: arguments to this function are incorrect --> $DIR/issue-101097.rs:19:5 @@ -104,22 +76,11 @@ note: function defined here | LL | fn f( | ^ -LL | a1: A, - | ----- -LL | a2: A, - | ----- -LL | b1: B, - | ----- -LL | b2: B, - | ----- -LL | c1: C, - | ----- -LL | c2: C, - | ----- help: did you mean | -LL | f(A, A, B, B, C, C); - | ~~~~~~~~~~~~~~~~~~ +LL - f(C, C, B, B, A, A); +LL + f(A, A, B, B, C, C); + | error[E0308]: arguments to this function are incorrect --> $DIR/issue-101097.rs:20:5 @@ -137,22 +98,14 @@ note: function defined here | LL | fn f( | ^ -LL | a1: A, - | ----- -LL | a2: A, - | ----- +... LL | b1: B, | ----- -LL | b2: B, - | ----- -LL | c1: C, - | ----- -LL | c2: C, - | ----- help: did you mean | -LL | f(A, A, /* B */, B, C, C); - | ~~~~~~~~~~~~~~~~~~~~~~~~ +LL - f(C, C, A, B, A, A); +LL + f(A, A, /* B */, B, C, C); + | error: aborting due to 5 previous errors diff --git a/tests/ui/argument-suggestions/issue-109425.stderr b/tests/ui/argument-suggestions/issue-109425.stderr index 2cd53ed528e48..232dc0183c865 100644 --- a/tests/ui/argument-suggestions/issue-109425.stderr +++ b/tests/ui/argument-suggestions/issue-109425.stderr @@ -29,7 +29,7 @@ note: function defined here --> $DIR/issue-109425.rs:4:4 | LL | fn i(_: u32) {} - | ^ ------ + | ^ help: remove the extra arguments | LL - i(0, 1, 2,); // i(0,) @@ -48,7 +48,7 @@ note: function defined here --> $DIR/issue-109425.rs:4:4 | LL | fn i(_: u32) {} - | ^ ------ + | ^ help: remove the extra arguments | LL - i(0, 1, 2); // i(0) @@ -67,7 +67,7 @@ note: function defined here --> $DIR/issue-109425.rs:5:4 | LL | fn is(_: u32, _: &str) {} - | ^^ ------ ------- + | ^^ help: remove the extra arguments | LL - is(0, 1, 2, ""); // is(0, "") @@ -86,7 +86,7 @@ note: function defined here --> $DIR/issue-109425.rs:5:4 | LL | fn is(_: u32, _: &str) {} - | ^^ ------ ------- + | ^^ help: remove the extra arguments | LL - is((), 1, "", ()); @@ -105,7 +105,7 @@ note: function defined here --> $DIR/issue-109425.rs:5:4 | LL | fn is(_: u32, _: &str) {} - | ^^ ------ ------- + | ^^ help: remove the extra arguments | LL - is(1, (), "", ()); @@ -124,7 +124,7 @@ note: function defined here --> $DIR/issue-109425.rs:6:4 | LL | fn s(_: &str) {} - | ^ ------- + | ^ help: remove the extra arguments | LL - s(0, 1, ""); // s("") diff --git a/tests/ui/argument-suggestions/issue-109831.stderr b/tests/ui/argument-suggestions/issue-109831.stderr index 12be0887121a2..1a3a597175d83 100644 --- a/tests/ui/argument-suggestions/issue-109831.stderr +++ b/tests/ui/argument-suggestions/issue-109831.stderr @@ -9,8 +9,9 @@ LL | fn f(b1: B, b2: B, a2: C) {} | help: a struct with a similar name exists | -LL | fn f(b1: B, b2: B, a2: A) {} - | ~ +LL - fn f(b1: B, b2: B, a2: C) {} +LL + fn f(b1: B, b2: B, a2: A) {} + | help: you might be missing a type parameter | LL | fn f(b1: B, b2: B, a2: C) {} @@ -38,7 +39,7 @@ note: function defined here --> $DIR/issue-109831.rs:4:4 | LL | fn f(b1: B, b2: B, a2: C) {} - | ^ ----- ----- ----- + | ^ ----- ----- help: remove the extra argument | LL - f(A, A, B, C); diff --git a/tests/ui/argument-suggestions/issue-96638.stderr b/tests/ui/argument-suggestions/issue-96638.stderr index 6492acbad9403..288a6853d0591 100644 --- a/tests/ui/argument-suggestions/issue-96638.stderr +++ b/tests/ui/argument-suggestions/issue-96638.stderr @@ -10,11 +10,12 @@ note: function defined here --> $DIR/issue-96638.rs:1:4 | LL | fn f(_: usize, _: &usize, _: usize) {} - | ^ -------- --------- -------- + | ^ -------- -------- help: provide the argument | -LL | f(/* usize */, &x, /* usize */); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - f(&x, ""); +LL + f(/* usize */, &x, /* usize */); + | error: aborting due to 1 previous error diff --git a/tests/ui/argument-suggestions/issue-97197.stderr b/tests/ui/argument-suggestions/issue-97197.stderr index 3768367a5e61a..acaf4f1510735 100644 --- a/tests/ui/argument-suggestions/issue-97197.stderr +++ b/tests/ui/argument-suggestions/issue-97197.stderr @@ -8,11 +8,12 @@ note: function defined here --> $DIR/issue-97197.rs:6:8 | LL | pub fn g(a1: (), a2: bool, a3: bool, a4: bool, a5: bool, a6: ()) -> () {} - | ^ ------ -------- -------- -------- -------- ------ + | ^ -------- -------- -------- -------- help: provide the arguments | -LL | g((), /* bool */, /* bool */, /* bool */, /* bool */, ()); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - g((), ()); +LL + g((), /* bool */, /* bool */, /* bool */, /* bool */, ()); + | error: aborting due to 1 previous error diff --git a/tests/ui/argument-suggestions/issue-97484.stderr b/tests/ui/argument-suggestions/issue-97484.stderr index a7708f6e0d733..e09d1928f7450 100644 --- a/tests/ui/argument-suggestions/issue-97484.stderr +++ b/tests/ui/argument-suggestions/issue-97484.stderr @@ -12,7 +12,7 @@ note: function defined here --> $DIR/issue-97484.rs:9:4 | LL | fn foo(a: &A, d: D, e: &E, g: G) {} - | ^^^ ----- ---- ----- ---- + | ^^^ ----- help: consider borrowing here | LL | foo(&&A, B, C, D, &E, F, G); diff --git a/tests/ui/argument-suggestions/issue-98894.stderr b/tests/ui/argument-suggestions/issue-98894.stderr index 93e604c3101fb..44353cb33388a 100644 --- a/tests/ui/argument-suggestions/issue-98894.stderr +++ b/tests/ui/argument-suggestions/issue-98894.stderr @@ -11,8 +11,9 @@ LL | (|_, ()| ())(if true {} else {return;}); | ^^^^^^^ help: provide the argument | -LL | (|_, ()| ())(if true {} else {return;}, ()); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - (|_, ()| ())(if true {} else {return;}); +LL + (|_, ()| ())(if true {} else {return;}, ()); + | error: aborting due to 1 previous error diff --git a/tests/ui/argument-suggestions/issue-98897.stderr b/tests/ui/argument-suggestions/issue-98897.stderr index 671e3d99d8552..fd3ef467b1c39 100644 --- a/tests/ui/argument-suggestions/issue-98897.stderr +++ b/tests/ui/argument-suggestions/issue-98897.stderr @@ -11,8 +11,9 @@ LL | (|_, ()| ())([return, ()]); | ^^^^^^^ help: provide the argument | -LL | (|_, ()| ())([return, ()], ()); - | ~~~~~~~~~~~~~~~~~~ +LL - (|_, ()| ())([return, ()]); +LL + (|_, ()| ())([return, ()], ()); + | error: aborting due to 1 previous error diff --git a/tests/ui/argument-suggestions/issue-99482.stderr b/tests/ui/argument-suggestions/issue-99482.stderr index be40787461523..b3c39604a99b0 100644 --- a/tests/ui/argument-suggestions/issue-99482.stderr +++ b/tests/ui/argument-suggestions/issue-99482.stderr @@ -11,8 +11,9 @@ LL | let f = |_: (), f: fn()| f; | ^^^^^^^^^^^^^^^^ help: provide the argument | -LL | let _f = f((), main); - | ~~~~~~~~~~ +LL - let _f = f(main); +LL + let _f = f((), main); + | error: aborting due to 1 previous error diff --git a/tests/ui/argument-suggestions/missing_arguments.stderr b/tests/ui/argument-suggestions/missing_arguments.stderr index 3a27a51d0320f..264c485cbe1c3 100644 --- a/tests/ui/argument-suggestions/missing_arguments.stderr +++ b/tests/ui/argument-suggestions/missing_arguments.stderr @@ -11,8 +11,9 @@ LL | fn one_arg(_a: i32) {} | ^^^^^^^ ------- help: provide the argument | -LL | one_arg(/* i32 */); - | ~~~~~~~~~~~ +LL - one_arg(); +LL + one_arg(/* i32 */); + | error[E0061]: this function takes 2 arguments but 0 arguments were supplied --> $DIR/missing_arguments.rs:14:3 @@ -27,8 +28,9 @@ LL | fn two_same(_a: i32, _b: i32) {} | ^^^^^^^^ ------- ------- help: provide the arguments | -LL | two_same(/* i32 */, /* i32 */); - | ~~~~~~~~~~~~~~~~~~~~~~ +LL - two_same( ); +LL + two_same(/* i32 */, /* i32 */); + | error[E0061]: this function takes 2 arguments but 1 argument was supplied --> $DIR/missing_arguments.rs:15:3 @@ -40,11 +42,12 @@ note: function defined here --> $DIR/missing_arguments.rs:2:4 | LL | fn two_same(_a: i32, _b: i32) {} - | ^^^^^^^^ ------- ------- + | ^^^^^^^^ ------- help: provide the argument | -LL | two_same(1, /* i32 */); - | ~~~~~~~~~~~~~~ +LL - two_same( 1 ); +LL + two_same(1, /* i32 */); + | error[E0061]: this function takes 2 arguments but 0 arguments were supplied --> $DIR/missing_arguments.rs:16:3 @@ -59,8 +62,9 @@ LL | fn two_diff(_a: i32, _b: f32) {} | ^^^^^^^^ ------- ------- help: provide the arguments | -LL | two_diff(/* i32 */, /* f32 */); - | ~~~~~~~~~~~~~~~~~~~~~~ +LL - two_diff( ); +LL + two_diff(/* i32 */, /* f32 */); + | error[E0061]: this function takes 2 arguments but 1 argument was supplied --> $DIR/missing_arguments.rs:17:3 @@ -72,11 +76,12 @@ note: function defined here --> $DIR/missing_arguments.rs:3:4 | LL | fn two_diff(_a: i32, _b: f32) {} - | ^^^^^^^^ ------- ------- + | ^^^^^^^^ ------- help: provide the argument | -LL | two_diff(1, /* f32 */); - | ~~~~~~~~~~~~~~ +LL - two_diff( 1 ); +LL + two_diff(1, /* f32 */); + | error[E0061]: this function takes 2 arguments but 1 argument was supplied --> $DIR/missing_arguments.rs:18:3 @@ -88,11 +93,12 @@ note: function defined here --> $DIR/missing_arguments.rs:3:4 | LL | fn two_diff(_a: i32, _b: f32) {} - | ^^^^^^^^ ------- ------- + | ^^^^^^^^ ------- help: provide the argument | -LL | two_diff(/* i32 */, 1.0); - | ~~~~~~~~~~~~~~~~ +LL - two_diff( 1.0 ); +LL + two_diff(/* i32 */, 1.0); + | error[E0061]: this function takes 3 arguments but 0 arguments were supplied --> $DIR/missing_arguments.rs:21:3 @@ -107,8 +113,9 @@ LL | fn three_same(_a: i32, _b: i32, _c: i32) {} | ^^^^^^^^^^ ------- ------- ------- help: provide the arguments | -LL | three_same(/* i32 */, /* i32 */, /* i32 */); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - three_same( ); +LL + three_same(/* i32 */, /* i32 */, /* i32 */); + | error[E0061]: this function takes 3 arguments but 1 argument was supplied --> $DIR/missing_arguments.rs:22:3 @@ -120,11 +127,12 @@ note: function defined here --> $DIR/missing_arguments.rs:4:4 | LL | fn three_same(_a: i32, _b: i32, _c: i32) {} - | ^^^^^^^^^^ ------- ------- ------- + | ^^^^^^^^^^ ------- ------- help: provide the arguments | -LL | three_same(1, /* i32 */, /* i32 */); - | ~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - three_same( 1 ); +LL + three_same(1, /* i32 */, /* i32 */); + | error[E0061]: this function takes 3 arguments but 2 arguments were supplied --> $DIR/missing_arguments.rs:23:3 @@ -136,11 +144,12 @@ note: function defined here --> $DIR/missing_arguments.rs:4:4 | LL | fn three_same(_a: i32, _b: i32, _c: i32) {} - | ^^^^^^^^^^ ------- ------- ------- + | ^^^^^^^^^^ ------- help: provide the argument | -LL | three_same(1, 1, /* i32 */); - | ~~~~~~~~~~~~~~~~~ +LL - three_same( 1, 1 ); +LL + three_same(1, 1, /* i32 */); + | error[E0061]: this function takes 3 arguments but 2 arguments were supplied --> $DIR/missing_arguments.rs:26:3 @@ -152,11 +161,12 @@ note: function defined here --> $DIR/missing_arguments.rs:5:4 | LL | fn three_diff(_a: i32, _b: f32, _c: &str) {} - | ^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^ ------- help: provide the argument | -LL | three_diff(/* i32 */, 1.0, ""); - | ~~~~~~~~~~~~~~~~~~~~ +LL - three_diff( 1.0, "" ); +LL + three_diff(/* i32 */, 1.0, ""); + | error[E0061]: this function takes 3 arguments but 2 arguments were supplied --> $DIR/missing_arguments.rs:27:3 @@ -168,11 +178,12 @@ note: function defined here --> $DIR/missing_arguments.rs:5:4 | LL | fn three_diff(_a: i32, _b: f32, _c: &str) {} - | ^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^ ------- help: provide the argument | -LL | three_diff(1, /* f32 */, ""); - | ~~~~~~~~~~~~~~~~~~ +LL - three_diff( 1, "" ); +LL + three_diff(1, /* f32 */, ""); + | error[E0061]: this function takes 3 arguments but 2 arguments were supplied --> $DIR/missing_arguments.rs:28:3 @@ -184,11 +195,12 @@ note: function defined here --> $DIR/missing_arguments.rs:5:4 | LL | fn three_diff(_a: i32, _b: f32, _c: &str) {} - | ^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^ -------- help: provide the argument | -LL | three_diff(1, 1.0, /* &str */); - | ~~~~~~~~~~~~~~~~~~~~ +LL - three_diff( 1, 1.0 ); +LL + three_diff(1, 1.0, /* &str */); + | error[E0061]: this function takes 3 arguments but 1 argument was supplied --> $DIR/missing_arguments.rs:29:3 @@ -200,11 +212,12 @@ note: function defined here --> $DIR/missing_arguments.rs:5:4 | LL | fn three_diff(_a: i32, _b: f32, _c: &str) {} - | ^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^ ------- ------- help: provide the arguments | -LL | three_diff(/* i32 */, /* f32 */, ""); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - three_diff( "" ); +LL + three_diff(/* i32 */, /* f32 */, ""); + | error[E0061]: this function takes 3 arguments but 1 argument was supplied --> $DIR/missing_arguments.rs:30:3 @@ -219,11 +232,12 @@ note: function defined here --> $DIR/missing_arguments.rs:5:4 | LL | fn three_diff(_a: i32, _b: f32, _c: &str) {} - | ^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^ ------- -------- help: provide the arguments | -LL | three_diff(/* i32 */, 1.0, /* &str */); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - three_diff( 1.0 ); +LL + three_diff(/* i32 */, 1.0, /* &str */); + | error[E0061]: this function takes 3 arguments but 1 argument was supplied --> $DIR/missing_arguments.rs:31:3 @@ -235,11 +249,12 @@ note: function defined here --> $DIR/missing_arguments.rs:5:4 | LL | fn three_diff(_a: i32, _b: f32, _c: &str) {} - | ^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^ ------- -------- help: provide the arguments | -LL | three_diff(1, /* f32 */, /* &str */); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - three_diff( 1 ); +LL + three_diff(1, /* f32 */, /* &str */); + | error[E0061]: this function takes 4 arguments but 0 arguments were supplied --> $DIR/missing_arguments.rs:34:3 @@ -254,8 +269,9 @@ LL | fn four_repeated(_a: i32, _b: f32, _c: f32, _d: &str) {} | ^^^^^^^^^^^^^ ------- ------- ------- -------- help: provide the arguments | -LL | four_repeated(/* i32 */, /* f32 */, /* f32 */, /* &str */); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - four_repeated( ); +LL + four_repeated(/* i32 */, /* f32 */, /* f32 */, /* &str */); + | error[E0061]: this function takes 4 arguments but 2 arguments were supplied --> $DIR/missing_arguments.rs:35:3 @@ -267,11 +283,12 @@ note: function defined here --> $DIR/missing_arguments.rs:6:4 | LL | fn four_repeated(_a: i32, _b: f32, _c: f32, _d: &str) {} - | ^^^^^^^^^^^^^ ------- ------- ------- -------- + | ^^^^^^^^^^^^^ ------- ------- help: provide the arguments | -LL | four_repeated(1, /* f32 */, /* f32 */, ""); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - four_repeated( 1, "" ); +LL + four_repeated(1, /* f32 */, /* f32 */, ""); + | error[E0061]: this function takes 5 arguments but 0 arguments were supplied --> $DIR/missing_arguments.rs:38:3 @@ -286,8 +303,9 @@ LL | fn complex(_a: i32, _b: f32, _c: i32, _d: f32, _e: &str) {} | ^^^^^^^ ------- ------- ------- ------- -------- help: provide the arguments | -LL | complex(/* i32 */, /* f32 */, /* i32 */, /* f32 */, /* &str */); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - complex( ); +LL + complex(/* i32 */, /* f32 */, /* i32 */, /* f32 */, /* &str */); + | error[E0061]: this function takes 5 arguments but 2 arguments were supplied --> $DIR/missing_arguments.rs:39:3 @@ -299,11 +317,12 @@ note: function defined here --> $DIR/missing_arguments.rs:7:4 | LL | fn complex(_a: i32, _b: f32, _c: i32, _d: f32, _e: &str) {} - | ^^^^^^^ ------- ------- ------- ------- -------- + | ^^^^^^^ ------- ------- ------- help: provide the arguments | -LL | complex(1, /* f32 */, /* i32 */, /* f32 */, ""); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - complex( 1, "" ); +LL + complex(1, /* f32 */, /* i32 */, /* f32 */, ""); + | error: aborting due to 19 previous errors diff --git a/tests/ui/argument-suggestions/mixed_cases.stderr b/tests/ui/argument-suggestions/mixed_cases.stderr index bec5d4dc16ba1..0dcb4337eb166 100644 --- a/tests/ui/argument-suggestions/mixed_cases.stderr +++ b/tests/ui/argument-suggestions/mixed_cases.stderr @@ -10,7 +10,7 @@ note: function defined here --> $DIR/mixed_cases.rs:5:4 | LL | fn two_args(_a: i32, _b: f32) {} - | ^^^^^^^^ ------- ------- + | ^^^^^^^^ ------- help: remove the extra argument | LL - two_args(1, "", X {}); @@ -30,11 +30,12 @@ note: function defined here --> $DIR/mixed_cases.rs:6:4 | LL | fn three_args(_a: i32, _b: f32, _c: &str) {} - | ^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^ ------- help: did you mean | -LL | three_args(1, /* f32 */, ""); - | ~~~~~~~~~~~~~~~~~~ +LL - three_args(1, "", X {}, ""); +LL + three_args(1, /* f32 */, ""); + | error[E0061]: this function takes 3 arguments but 2 arguments were supplied --> $DIR/mixed_cases.rs:14:3 @@ -49,11 +50,12 @@ note: function defined here --> $DIR/mixed_cases.rs:6:4 | LL | fn three_args(_a: i32, _b: f32, _c: &str) {} - | ^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^ ------- -------- help: provide the argument | -LL | three_args(1, /* f32 */, /* &str */); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - three_args(1, X {}); +LL + three_args(1, /* f32 */, /* &str */); + | error[E0308]: arguments to this function are incorrect --> $DIR/mixed_cases.rs:17:3 @@ -67,11 +69,12 @@ note: function defined here --> $DIR/mixed_cases.rs:6:4 | LL | fn three_args(_a: i32, _b: f32, _c: &str) {} - | ^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^ ------- help: did you mean | -LL | three_args(1, /* f32 */, ""); - | ~~~~~~~~~~~~~~~~~~ +LL - three_args(1, "", X {}); +LL + three_args(1, /* f32 */, ""); + | error[E0308]: arguments to this function are incorrect --> $DIR/mixed_cases.rs:20:3 @@ -86,11 +89,12 @@ note: function defined here --> $DIR/mixed_cases.rs:6:4 | LL | fn three_args(_a: i32, _b: f32, _c: &str) {} - | ^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^ ------- help: swap these arguments | -LL | three_args(1, /* f32 */, ""); - | ~~~~~~~~~~~~~~~~~~ +LL - three_args("", X {}, 1); +LL + three_args(1, /* f32 */, ""); + | error[E0061]: this function takes 3 arguments but 2 arguments were supplied --> $DIR/mixed_cases.rs:23:3 @@ -106,11 +110,12 @@ note: function defined here --> $DIR/mixed_cases.rs:6:4 | LL | fn three_args(_a: i32, _b: f32, _c: &str) {} - | ^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^ ------- help: did you mean | -LL | three_args(1, /* f32 */, ""); - | ~~~~~~~~~~~~~~~~~~ +LL - three_args("", 1); +LL + three_args(1, /* f32 */, ""); + | error: aborting due to 6 previous errors diff --git a/tests/ui/argument-suggestions/permuted_arguments.stderr b/tests/ui/argument-suggestions/permuted_arguments.stderr index 655807a7f382f..9592fc2801c4b 100644 --- a/tests/ui/argument-suggestions/permuted_arguments.stderr +++ b/tests/ui/argument-suggestions/permuted_arguments.stderr @@ -11,11 +11,12 @@ note: function defined here --> $DIR/permuted_arguments.rs:5:4 | LL | fn three_args(_a: i32, _b: f32, _c: &str) {} - | ^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^ help: reorder these arguments | -LL | three_args(1, 1.0, ""); - | ~~~~~~~~~~~~ +LL - three_args(1.0, "", 1); +LL + three_args(1, 1.0, ""); + | error[E0308]: arguments to this function are incorrect --> $DIR/permuted_arguments.rs:12:3 @@ -32,11 +33,12 @@ note: function defined here --> $DIR/permuted_arguments.rs:6:4 | LL | fn many_args(_a: i32, _b: f32, _c: &str, _d: X, _e: Y) {} - | ^^^^^^^^^ ------- ------- -------- ----- ----- + | ^^^^^^^^^ help: reorder these arguments | -LL | many_args(1, 1.0, "", X {}, Y {}); - | ~~~~~~~~~~~~~~~~~~~~~~~~ +LL - many_args(X {}, Y {}, 1, 1.0, ""); +LL + many_args(1, 1.0, "", X {}, Y {}); + | error: aborting due to 2 previous errors diff --git a/tests/ui/argument-suggestions/suggest-better-removing-issue-126246.stderr b/tests/ui/argument-suggestions/suggest-better-removing-issue-126246.stderr index 730f20cfb88e7..48c647764dfb3 100644 --- a/tests/ui/argument-suggestions/suggest-better-removing-issue-126246.stderr +++ b/tests/ui/argument-suggestions/suggest-better-removing-issue-126246.stderr @@ -38,7 +38,7 @@ note: function defined here --> $DIR/suggest-better-removing-issue-126246.rs:1:4 | LL | fn add_one(x: i32) -> i32 { - | ^^^^^^^ ------ + | ^^^^^^^ help: remove the extra argument | LL - add_one(2, 2); @@ -55,7 +55,7 @@ note: function defined here --> $DIR/suggest-better-removing-issue-126246.rs:1:4 | LL | fn add_one(x: i32) -> i32 { - | ^^^^^^^ ------ + | ^^^^^^^ help: remove the extra argument | LL - add_one(no_such_local, 10); @@ -72,7 +72,7 @@ note: function defined here --> $DIR/suggest-better-removing-issue-126246.rs:1:4 | LL | fn add_one(x: i32) -> i32 { - | ^^^^^^^ ------ + | ^^^^^^^ help: remove the extra argument | LL - add_one(10, no_such_local); @@ -89,7 +89,7 @@ note: function defined here --> $DIR/suggest-better-removing-issue-126246.rs:5:4 | LL | fn add_two(x: i32, y: i32) -> i32 { - | ^^^^^^^ ------ ------ + | ^^^^^^^ help: remove the extra argument | LL - add_two(10, no_such_local, 10); @@ -106,7 +106,7 @@ note: function defined here --> $DIR/suggest-better-removing-issue-126246.rs:5:4 | LL | fn add_two(x: i32, y: i32) -> i32 { - | ^^^^^^^ ------ ------ + | ^^^^^^^ help: remove the extra argument | LL - add_two(no_such_local, 10, 10); @@ -123,7 +123,7 @@ note: function defined here --> $DIR/suggest-better-removing-issue-126246.rs:5:4 | LL | fn add_two(x: i32, y: i32) -> i32 { - | ^^^^^^^ ------ ------ + | ^^^^^^^ help: remove the extra argument | LL - add_two(10, 10, no_such_local); diff --git a/tests/ui/argument-suggestions/swapped_arguments.stderr b/tests/ui/argument-suggestions/swapped_arguments.stderr index dabf5e952b26b..f72659363ac45 100644 --- a/tests/ui/argument-suggestions/swapped_arguments.stderr +++ b/tests/ui/argument-suggestions/swapped_arguments.stderr @@ -10,11 +10,12 @@ note: function defined here --> $DIR/swapped_arguments.rs:3:4 | LL | fn two_args(_a: i32, _b: f32) {} - | ^^^^^^^^ ------- ------- + | ^^^^^^^^ help: swap these arguments | -LL | two_args(1, 1.0); - | ~~~~~~~~ +LL - two_args(1.0, 1); +LL + two_args(1, 1.0); + | error[E0308]: arguments to this function are incorrect --> $DIR/swapped_arguments.rs:9:3 @@ -28,11 +29,12 @@ note: function defined here --> $DIR/swapped_arguments.rs:4:4 | LL | fn three_args(_a: i32, _b: f32, _c: &str) {} - | ^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^ help: swap these arguments | -LL | three_args(1, 1.0, ""); - | ~~~~~~~~~~~~ +LL - three_args(1.0, 1, ""); +LL + three_args(1, 1.0, ""); + | error[E0308]: arguments to this function are incorrect --> $DIR/swapped_arguments.rs:10:3 @@ -46,11 +48,12 @@ note: function defined here --> $DIR/swapped_arguments.rs:4:4 | LL | fn three_args(_a: i32, _b: f32, _c: &str) {} - | ^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^ help: swap these arguments | -LL | three_args(1, 1.0, ""); - | ~~~~~~~~~~~~ +LL - three_args( 1, "", 1.0); +LL + three_args(1, 1.0, ""); + | error[E0308]: arguments to this function are incorrect --> $DIR/swapped_arguments.rs:11:3 @@ -64,11 +67,12 @@ note: function defined here --> $DIR/swapped_arguments.rs:4:4 | LL | fn three_args(_a: i32, _b: f32, _c: &str) {} - | ^^^^^^^^^^ ------- ------- -------- + | ^^^^^^^^^^ help: swap these arguments | -LL | three_args(1, 1.0, ""); - | ~~~~~~~~~~~~ +LL - three_args( "", 1.0, 1); +LL + three_args(1, 1.0, ""); + | error[E0308]: arguments to this function are incorrect --> $DIR/swapped_arguments.rs:13:3 @@ -84,11 +88,12 @@ note: function defined here --> $DIR/swapped_arguments.rs:5:4 | LL | fn four_args(_a: i32, _b: f32, _c: &str, _d: X) {} - | ^^^^^^^^^ ------- ------- -------- ----- + | ^^^^^^^^^ help: did you mean | -LL | four_args(1, 1.0, "", X {}); - | ~~~~~~~~~~~~~~~~~~ +LL - four_args(1.0, 1, X {}, ""); +LL + four_args(1, 1.0, "", X {}); + | error: aborting due to 5 previous errors diff --git a/tests/ui/array-slice-vec/bounds-check-no-overflow.rs b/tests/ui/array-slice-vec/bounds-check-no-overflow.rs index 4614df44084f2..c5ff805a853e0 100644 --- a/tests/ui/array-slice-vec/bounds-check-no-overflow.rs +++ b/tests/ui/array-slice-vec/bounds-check-no-overflow.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:index out of bounds -//@ ignore-emscripten no processes +//@ needs-subprocess use std::mem::size_of; diff --git a/tests/ui/array-slice-vec/box-of-array-of-drop-1.rs b/tests/ui/array-slice-vec/box-of-array-of-drop-1.rs index d64df4f7e4d9a..c7c05946c4ca9 100644 --- a/tests/ui/array-slice-vec/box-of-array-of-drop-1.rs +++ b/tests/ui/array-slice-vec/box-of-array-of-drop-1.rs @@ -1,12 +1,12 @@ //@ run-pass //@ needs-unwind +//@ needs-threads + #![allow(overflowing_literals)] // Test that we cleanup a fixed size Box<[D; k]> properly when D has a // destructor. -//@ ignore-emscripten no threads support - use std::thread; use std::sync::atomic::{AtomicUsize, Ordering}; diff --git a/tests/ui/array-slice-vec/box-of-array-of-drop-2.rs b/tests/ui/array-slice-vec/box-of-array-of-drop-2.rs index 5ca3d60ad1dc9..98175a26ec0d1 100644 --- a/tests/ui/array-slice-vec/box-of-array-of-drop-2.rs +++ b/tests/ui/array-slice-vec/box-of-array-of-drop-2.rs @@ -1,12 +1,12 @@ //@ run-pass //@ needs-unwind +//@ needs-threads + #![allow(overflowing_literals)] // Test that we cleanup dynamic sized Box<[D]> properly when D has a // destructor. -//@ ignore-emscripten no threads support - use std::thread; use std::sync::atomic::{AtomicUsize, Ordering}; diff --git a/tests/ui/array-slice-vec/driftsort-off-by-one-issue-136103.rs b/tests/ui/array-slice-vec/driftsort-off-by-one-issue-136103.rs new file mode 100644 index 0000000000000..42197ff102d72 --- /dev/null +++ b/tests/ui/array-slice-vec/driftsort-off-by-one-issue-136103.rs @@ -0,0 +1,10 @@ +//@ run-pass +// Ensures that driftsort doesn't crash under specific slice +// length and memory size. +// Based on the example given in https://github.com/rust-lang/rust/issues/136103. +fn main() { + let n = 127; + let mut objs: Vec<_> = + (0..n).map(|i| [(i % 2) as u8; 125001]).collect(); + objs.sort(); +} diff --git a/tests/ui/array-slice-vec/dst-raw-slice.rs b/tests/ui/array-slice-vec/dst-raw-slice.rs index f1281f4e302f2..ab9dedc139d7e 100644 --- a/tests/ui/array-slice-vec/dst-raw-slice.rs +++ b/tests/ui/array-slice-vec/dst-raw-slice.rs @@ -2,7 +2,7 @@ //@ run-fail //@ error-pattern:index out of bounds -//@ ignore-emscripten no processes +//@ needs-subprocess #[allow(unconditional_panic)] fn main() { diff --git a/tests/ui/array-slice-vec/nested-vec-3.rs b/tests/ui/array-slice-vec/nested-vec-3.rs index ce61401aab451..51975743742cf 100644 --- a/tests/ui/array-slice-vec/nested-vec-3.rs +++ b/tests/ui/array-slice-vec/nested-vec-3.rs @@ -1,8 +1,8 @@ //@ run-pass //@ needs-unwind -#![allow(overflowing_literals)] +//@ needs-threads -//@ ignore-emscripten no threads support +#![allow(overflowing_literals)] // Test that using the `vec!` macro nested within itself works when // the contents implement Drop and we hit a panic in the middle of diff --git a/tests/ui/array-slice-vec/slice-panic-1.rs b/tests/ui/array-slice-vec/slice-panic-1.rs index d4f584c163222..a745dff96afc7 100644 --- a/tests/ui/array-slice-vec/slice-panic-1.rs +++ b/tests/ui/array-slice-vec/slice-panic-1.rs @@ -1,7 +1,6 @@ //@ run-pass //@ needs-unwind - -//@ ignore-emscripten no threads support +//@ needs-threads // Test that if a slicing expr[..] fails, the correct cleanups happen. diff --git a/tests/ui/array-slice-vec/slice-panic-2.rs b/tests/ui/array-slice-vec/slice-panic-2.rs index b3d1dc455735a..483a4cbe245db 100644 --- a/tests/ui/array-slice-vec/slice-panic-2.rs +++ b/tests/ui/array-slice-vec/slice-panic-2.rs @@ -1,7 +1,6 @@ //@ run-pass //@ needs-unwind - -//@ ignore-emscripten no threads support +//@ needs-threads // Test that if a slicing expr[..] fails, the correct cleanups happen. diff --git a/tests/ui/array-slice-vec/suggest-array-length.fixed b/tests/ui/array-slice-vec/suggest-array-length.fixed index 29f85da56e578..2eacc2517d310 100644 --- a/tests/ui/array-slice-vec/suggest-array-length.fixed +++ b/tests/ui/array-slice-vec/suggest-array-length.fixed @@ -3,24 +3,21 @@ fn main() { const Foo: [i32; 3] = [1, 2, 3]; - //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment - //~| ERROR using `_` for array lengths is unstable + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for constants const REF_FOO: &[u8; 1] = &[1]; - //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment - //~| ERROR using `_` for array lengths is unstable + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for constants + static Statik: [i32; 3] = [1, 2, 3]; + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for static variables + static REF_STATIK: &[u8; 1] = &[1]; + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for static variables let foo: [i32; 3] = [1, 2, 3]; - //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment - //~| ERROR using `_` for array lengths is unstable + //~^ ERROR using `_` for array lengths is unstable let bar: [i32; 3] = [0; 3]; - //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment - //~| ERROR using `_` for array lengths is unstable + //~^ ERROR using `_` for array lengths is unstable let ref_foo: &[i32; 3] = &[1, 2, 3]; - //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment - //~| ERROR using `_` for array lengths is unstable + //~^ ERROR using `_` for array lengths is unstable let ref_bar: &[i32; 3] = &[0; 3]; - //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment - //~| ERROR using `_` for array lengths is unstable + //~^ ERROR using `_` for array lengths is unstable let multiple_ref_foo: &&[i32; 3] = &&[1, 2, 3]; - //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment - //~| ERROR using `_` for array lengths is unstable + //~^ ERROR using `_` for array lengths is unstable } diff --git a/tests/ui/array-slice-vec/suggest-array-length.rs b/tests/ui/array-slice-vec/suggest-array-length.rs index 82d871cf8754a..fb4424cfed99d 100644 --- a/tests/ui/array-slice-vec/suggest-array-length.rs +++ b/tests/ui/array-slice-vec/suggest-array-length.rs @@ -3,24 +3,21 @@ fn main() { const Foo: [i32; _] = [1, 2, 3]; - //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment - //~| ERROR using `_` for array lengths is unstable + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for constants const REF_FOO: &[u8; _] = &[1]; - //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment - //~| ERROR using `_` for array lengths is unstable + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for constants + static Statik: [i32; _] = [1, 2, 3]; + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for static variables + static REF_STATIK: &[u8; _] = &[1]; + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for static variables let foo: [i32; _] = [1, 2, 3]; - //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment - //~| ERROR using `_` for array lengths is unstable + //~^ ERROR using `_` for array lengths is unstable let bar: [i32; _] = [0; 3]; - //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment - //~| ERROR using `_` for array lengths is unstable + //~^ ERROR using `_` for array lengths is unstable let ref_foo: &[i32; _] = &[1, 2, 3]; - //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment - //~| ERROR using `_` for array lengths is unstable + //~^ ERROR using `_` for array lengths is unstable let ref_bar: &[i32; _] = &[0; 3]; - //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment - //~| ERROR using `_` for array lengths is unstable + //~^ ERROR using `_` for array lengths is unstable let multiple_ref_foo: &&[i32; _] = &&[1, 2, 3]; - //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment - //~| ERROR using `_` for array lengths is unstable + //~^ ERROR using `_` for array lengths is unstable } diff --git a/tests/ui/array-slice-vec/suggest-array-length.stderr b/tests/ui/array-slice-vec/suggest-array-length.stderr index fdab7ba7064e4..14d10832e3608 100644 --- a/tests/ui/array-slice-vec/suggest-array-length.stderr +++ b/tests/ui/array-slice-vec/suggest-array-length.stderr @@ -1,67 +1,53 @@ -error: in expressions, `_` can only be used on the left-hand side of an assignment - --> $DIR/suggest-array-length.rs:11:20 +error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants + --> $DIR/suggest-array-length.rs:5:22 | -LL | let foo: [i32; _] = [1, 2, 3]; - | ^ `_` not allowed here - -error: in expressions, `_` can only be used on the left-hand side of an assignment - --> $DIR/suggest-array-length.rs:14:20 +LL | const Foo: [i32; _] = [1, 2, 3]; + | ^ not allowed in type signatures | -LL | let bar: [i32; _] = [0; 3]; - | ^ `_` not allowed here - -error: in expressions, `_` can only be used on the left-hand side of an assignment - --> $DIR/suggest-array-length.rs:17:25 +help: replace this with a fully-specified type | -LL | let ref_foo: &[i32; _] = &[1, 2, 3]; - | ^ `_` not allowed here - -error: in expressions, `_` can only be used on the left-hand side of an assignment - --> $DIR/suggest-array-length.rs:20:25 +LL - const Foo: [i32; _] = [1, 2, 3]; +LL + const Foo: [i32; 3] = [1, 2, 3]; | -LL | let ref_bar: &[i32; _] = &[0; 3]; - | ^ `_` not allowed here -error: in expressions, `_` can only be used on the left-hand side of an assignment - --> $DIR/suggest-array-length.rs:23:35 +error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants + --> $DIR/suggest-array-length.rs:7:26 | -LL | let multiple_ref_foo: &&[i32; _] = &&[1, 2, 3]; - | ^ `_` not allowed here - -error: in expressions, `_` can only be used on the left-hand side of an assignment - --> $DIR/suggest-array-length.rs:5:22 +LL | const REF_FOO: &[u8; _] = &[1]; + | ^ not allowed in type signatures | -LL | const Foo: [i32; _] = [1, 2, 3]; - | ^ `_` not allowed here - -error: in expressions, `_` can only be used on the left-hand side of an assignment - --> $DIR/suggest-array-length.rs:8:26 +help: replace this with a fully-specified type + | +LL - const REF_FOO: &[u8; _] = &[1]; +LL + const REF_FOO: &[u8; 1] = &[1]; | -LL | const REF_FOO: &[u8; _] = &[1]; - | ^ `_` not allowed here -error[E0658]: using `_` for array lengths is unstable - --> $DIR/suggest-array-length.rs:5:22 +error[E0121]: the placeholder `_` is not allowed within types on item signatures for static variables + --> $DIR/suggest-array-length.rs:9:26 | -LL | const Foo: [i32; _] = [1, 2, 3]; - | ^ help: consider specifying the array length: `3` +LL | static Statik: [i32; _] = [1, 2, 3]; + | ^ not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - static Statik: [i32; _] = [1, 2, 3]; +LL + static Statik: [i32; 3] = [1, 2, 3]; | - = note: see issue #85077 for more information - = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: using `_` for array lengths is unstable - --> $DIR/suggest-array-length.rs:8:26 +error[E0121]: the placeholder `_` is not allowed within types on item signatures for static variables + --> $DIR/suggest-array-length.rs:11:30 | -LL | const REF_FOO: &[u8; _] = &[1]; - | ^ help: consider specifying the array length: `1` +LL | static REF_STATIK: &[u8; _] = &[1]; + | ^ not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - static REF_STATIK: &[u8; _] = &[1]; +LL + static REF_STATIK: &[u8; 1] = &[1]; | - = note: see issue #85077 for more information - = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: using `_` for array lengths is unstable - --> $DIR/suggest-array-length.rs:11:20 + --> $DIR/suggest-array-length.rs:13:20 | LL | let foo: [i32; _] = [1, 2, 3]; | ^ help: consider specifying the array length: `3` @@ -71,7 +57,7 @@ LL | let foo: [i32; _] = [1, 2, 3]; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: using `_` for array lengths is unstable - --> $DIR/suggest-array-length.rs:14:20 + --> $DIR/suggest-array-length.rs:15:20 | LL | let bar: [i32; _] = [0; 3]; | ^ help: consider specifying the array length: `3` @@ -91,7 +77,7 @@ LL | let ref_foo: &[i32; _] = &[1, 2, 3]; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: using `_` for array lengths is unstable - --> $DIR/suggest-array-length.rs:20:25 + --> $DIR/suggest-array-length.rs:19:25 | LL | let ref_bar: &[i32; _] = &[0; 3]; | ^ help: consider specifying the array length: `3` @@ -101,7 +87,7 @@ LL | let ref_bar: &[i32; _] = &[0; 3]; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: using `_` for array lengths is unstable - --> $DIR/suggest-array-length.rs:23:35 + --> $DIR/suggest-array-length.rs:21:35 | LL | let multiple_ref_foo: &&[i32; _] = &&[1, 2, 3]; | ^ help: consider specifying the array length: `3` @@ -110,6 +96,7 @@ LL | let multiple_ref_foo: &&[i32; _] = &&[1, 2, 3]; = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 14 previous errors +error: aborting due to 9 previous errors -For more information about this error, try `rustc --explain E0658`. +Some errors have detailed explanations: E0121, E0658. +For more information about an error, try `rustc --explain E0121`. diff --git a/tests/ui/array-slice-vec/vec-macro-no-std.rs b/tests/ui/array-slice-vec/vec-macro-no-std.rs index 1b5ab536dcb2f..ea0df0bea71df 100644 --- a/tests/ui/array-slice-vec/vec-macro-no-std.rs +++ b/tests/ui/array-slice-vec/vec-macro-no-std.rs @@ -1,21 +1,21 @@ //@ run-pass - //@ ignore-emscripten no no_std executables +//@ ignore-wasm different `main` convention -#![feature(lang_items, start, rustc_private)] #![no_std] +#![no_main] +// Import global allocator and panic handler. extern crate std as other; -#[macro_use] -extern crate alloc; +#[macro_use] extern crate alloc; use alloc::vec::Vec; // Issue #16806 -#[start] -fn start(_argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(_argc: core::ffi::c_int, _argv: *const *const u8) -> core::ffi::c_int { let x: Vec = vec![0, 1, 2]; match x.last() { Some(&2) => (), diff --git a/tests/ui/array-slice-vec/vec-overrun.rs b/tests/ui/array-slice-vec/vec-overrun.rs index 10f8350869fb8..3b3e9215279f9 100644 --- a/tests/ui/array-slice-vec/vec-overrun.rs +++ b/tests/ui/array-slice-vec/vec-overrun.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:index out of bounds: the len is 1 but the index is 2 -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { let v: Vec = vec![10]; diff --git a/tests/ui/asm/aarch64/bad-reg.stderr b/tests/ui/asm/aarch64/bad-reg.stderr index 370752ad0f11c..c76722f32a74e 100644 --- a/tests/ui/asm/aarch64/bad-reg.stderr +++ b/tests/ui/asm/aarch64/bad-reg.stderr @@ -3,6 +3,8 @@ error: invalid register class `foo`: unknown register class | LL | asm!("{}", in(foo) foo); | ^^^^^^^^^^^ + | + = note: the following register classes are supported on this target: `reg`, `vreg`, `vreg_low16`, `preg` error: invalid register `foo`: unknown register --> $DIR/bad-reg.rs:14:18 diff --git a/tests/ui/asm/aarch64/parse-error.stderr b/tests/ui/asm/aarch64/parse-error.stderr index 7b273282ee6a3..b5e1169e5f6b4 100644 --- a/tests/ui/asm/aarch64/parse-error.stderr +++ b/tests/ui/asm/aarch64/parse-error.stderr @@ -330,8 +330,9 @@ LL | asm!("{}", options(), const foo); | help: consider using `const` instead of `let` | -LL | const foo: /* Type */ = 0; - | ~~~~~ ++++++++++++ +LL - let mut foo = 0; +LL + const foo: /* Type */ = 0; + | error[E0435]: attempt to use a non-constant value in a constant --> $DIR/parse-error.rs:45:44 @@ -341,8 +342,9 @@ LL | asm!("{}", clobber_abi("C"), const foo); | help: consider using `const` instead of `let` | -LL | const foo: /* Type */ = 0; - | ~~~~~ ++++++++++++ +LL - let mut foo = 0; +LL + const foo: /* Type */ = 0; + | error[E0435]: attempt to use a non-constant value in a constant --> $DIR/parse-error.rs:48:55 @@ -352,8 +354,9 @@ LL | asm!("{}", options(), clobber_abi("C"), const foo); | help: consider using `const` instead of `let` | -LL | const foo: /* Type */ = 0; - | ~~~~~ ++++++++++++ +LL - let mut foo = 0; +LL + const foo: /* Type */ = 0; + | error[E0435]: attempt to use a non-constant value in a constant --> $DIR/parse-error.rs:50:31 @@ -363,8 +366,9 @@ LL | asm!("{a}", a = const foo, a = const bar); | help: consider using `const` instead of `let` | -LL | const foo: /* Type */ = 0; - | ~~~~~ ++++++++++++ +LL - let mut foo = 0; +LL + const foo: /* Type */ = 0; + | error[E0435]: attempt to use a non-constant value in a constant --> $DIR/parse-error.rs:50:46 @@ -374,8 +378,9 @@ LL | asm!("{a}", a = const foo, a = const bar); | help: consider using `const` instead of `let` | -LL | const bar: /* Type */ = 0; - | ~~~~~ ++++++++++++ +LL - let mut bar = 0; +LL + const bar: /* Type */ = 0; + | error[E0435]: attempt to use a non-constant value in a constant --> $DIR/parse-error.rs:57:45 @@ -385,8 +390,9 @@ LL | asm!("{a}", in("x0") foo, a = const bar); | help: consider using `const` instead of `let` | -LL | const bar: /* Type */ = 0; - | ~~~~~ ++++++++++++ +LL - let mut bar = 0; +LL + const bar: /* Type */ = 0; + | error[E0435]: attempt to use a non-constant value in a constant --> $DIR/parse-error.rs:59:45 @@ -396,8 +402,9 @@ LL | asm!("{a}", in("x0") foo, a = const bar); | help: consider using `const` instead of `let` | -LL | const bar: /* Type */ = 0; - | ~~~~~ ++++++++++++ +LL - let mut bar = 0; +LL + const bar: /* Type */ = 0; + | error[E0435]: attempt to use a non-constant value in a constant --> $DIR/parse-error.rs:61:41 @@ -407,8 +414,9 @@ LL | asm!("{1}", in("x0") foo, const bar); | help: consider using `const` instead of `let` | -LL | const bar: /* Type */ = 0; - | ~~~~~ ++++++++++++ +LL - let mut bar = 0; +LL + const bar: /* Type */ = 0; + | error: aborting due to 59 previous errors diff --git a/tests/ui/asm/invalid-const-operand.stderr b/tests/ui/asm/invalid-const-operand.stderr index bda4b0355b7c4..13bb10e84a533 100644 --- a/tests/ui/asm/invalid-const-operand.stderr +++ b/tests/ui/asm/invalid-const-operand.stderr @@ -6,8 +6,9 @@ LL | asm!("{}", const x); | help: consider using `const` instead of `let` | -LL | const x: /* Type */ = 0; - | ~~~~~ ++++++++++++ +LL - let x = 0; +LL + const x: /* Type */ = 0; + | error[E0435]: attempt to use a non-constant value in a constant --> $DIR/invalid-const-operand.rs:43:36 @@ -17,8 +18,9 @@ LL | asm!("{}", const const_foo(x)); | help: consider using `const` instead of `let` | -LL | const x: /* Type */ = 0; - | ~~~~~ ++++++++++++ +LL - let x = 0; +LL + const x: /* Type */ = 0; + | error[E0435]: attempt to use a non-constant value in a constant --> $DIR/invalid-const-operand.rs:46:36 @@ -28,8 +30,9 @@ LL | asm!("{}", const const_bar(x)); | help: consider using `const` instead of `let` | -LL | const x: /* Type */ = 0; - | ~~~~~ ++++++++++++ +LL - let x = 0; +LL + const x: /* Type */ = 0; + | error: invalid type for `const` operand --> $DIR/invalid-const-operand.rs:12:19 diff --git a/tests/ui/asm/parse-error.stderr b/tests/ui/asm/parse-error.stderr index 6d0e629b93775..74647372a3557 100644 --- a/tests/ui/asm/parse-error.stderr +++ b/tests/ui/asm/parse-error.stderr @@ -424,8 +424,9 @@ LL | asm!("{}", options(), const foo); | help: consider using `const` instead of `let` | -LL | const foo: /* Type */ = 0; - | ~~~~~ ++++++++++++ +LL - let mut foo = 0; +LL + const foo: /* Type */ = 0; + | error[E0435]: attempt to use a non-constant value in a constant --> $DIR/parse-error.rs:69:44 @@ -435,8 +436,9 @@ LL | asm!("{}", clobber_abi("C"), const foo); | help: consider using `const` instead of `let` | -LL | const foo: /* Type */ = 0; - | ~~~~~ ++++++++++++ +LL - let mut foo = 0; +LL + const foo: /* Type */ = 0; + | error[E0435]: attempt to use a non-constant value in a constant --> $DIR/parse-error.rs:72:55 @@ -446,8 +448,9 @@ LL | asm!("{}", options(), clobber_abi("C"), const foo); | help: consider using `const` instead of `let` | -LL | const foo: /* Type */ = 0; - | ~~~~~ ++++++++++++ +LL - let mut foo = 0; +LL + const foo: /* Type */ = 0; + | error[E0435]: attempt to use a non-constant value in a constant --> $DIR/parse-error.rs:74:31 @@ -457,8 +460,9 @@ LL | asm!("{a}", a = const foo, a = const bar); | help: consider using `const` instead of `let` | -LL | const foo: /* Type */ = 0; - | ~~~~~ ++++++++++++ +LL - let mut foo = 0; +LL + const foo: /* Type */ = 0; + | error[E0435]: attempt to use a non-constant value in a constant --> $DIR/parse-error.rs:74:46 @@ -468,8 +472,9 @@ LL | asm!("{a}", a = const foo, a = const bar); | help: consider using `const` instead of `let` | -LL | const bar: /* Type */ = 0; - | ~~~~~ ++++++++++++ +LL - let mut bar = 0; +LL + const bar: /* Type */ = 0; + | error: aborting due to 72 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.riscv32e.stderr b/tests/ui/asm/riscv/bad-reg.riscv32e.stderr index 409178df9c5ae..27c8e958e536a 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv32e.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv32e.stderr @@ -22,170 +22,164 @@ error: invalid register `gp`: the global pointer cannot be used as an operand fo LL | asm!("", out("gp") _); | ^^^^^^^^^^^ -error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 - | -LL | asm!("", out("gp") _); - | ^^^^^^^^^^^ - error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:102:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: cannot use register `x16`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:48:18 + --> $DIR/bad-reg.rs:46:18 | LL | asm!("", out("x16") _); | ^^^^^^^^^^^^ error: cannot use register `x17`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:50:18 + --> $DIR/bad-reg.rs:48:18 | LL | asm!("", out("x17") _); | ^^^^^^^^^^^^ error: cannot use register `x18`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:52:18 + --> $DIR/bad-reg.rs:50:18 | LL | asm!("", out("x18") _); | ^^^^^^^^^^^^ error: cannot use register `x19`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:54:18 + --> $DIR/bad-reg.rs:52:18 | LL | asm!("", out("x19") _); | ^^^^^^^^^^^^ error: cannot use register `x20`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:56:18 + --> $DIR/bad-reg.rs:54:18 | LL | asm!("", out("x20") _); | ^^^^^^^^^^^^ error: cannot use register `x21`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:58:18 + --> $DIR/bad-reg.rs:56:18 | LL | asm!("", out("x21") _); | ^^^^^^^^^^^^ error: cannot use register `x22`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:60:18 + --> $DIR/bad-reg.rs:58:18 | LL | asm!("", out("x22") _); | ^^^^^^^^^^^^ error: cannot use register `x23`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:62:18 + --> $DIR/bad-reg.rs:60:18 | LL | asm!("", out("x23") _); | ^^^^^^^^^^^^ error: cannot use register `x24`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:64:18 + --> $DIR/bad-reg.rs:62:18 | LL | asm!("", out("x24") _); | ^^^^^^^^^^^^ error: cannot use register `x25`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:66:18 + --> $DIR/bad-reg.rs:64:18 | LL | asm!("", out("x25") _); | ^^^^^^^^^^^^ error: cannot use register `x26`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:68:18 + --> $DIR/bad-reg.rs:66:18 | LL | asm!("", out("x26") _); | ^^^^^^^^^^^^ error: cannot use register `x27`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:70:18 + --> $DIR/bad-reg.rs:68:18 | LL | asm!("", out("x27") _); | ^^^^^^^^^^^^ error: cannot use register `x28`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:72:18 + --> $DIR/bad-reg.rs:70:18 | LL | asm!("", out("x28") _); | ^^^^^^^^^^^^ error: cannot use register `x29`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:74:18 + --> $DIR/bad-reg.rs:72:18 | LL | asm!("", out("x29") _); | ^^^^^^^^^^^^ error: cannot use register `x30`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:76:18 + --> $DIR/bad-reg.rs:74:18 | LL | asm!("", out("x30") _); | ^^^^^^^^^^^^ error: cannot use register `x31`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:78:18 + --> $DIR/bad-reg.rs:76:18 | LL | asm!("", out("x31") _); | ^^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:82:26 + --> $DIR/bad-reg.rs:80:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:84:26 + --> $DIR/bad-reg.rs:82:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:86:26 + --> $DIR/bad-reg.rs:84:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:89:26 + --> $DIR/bad-reg.rs:87:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:96:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -193,7 +187,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:99:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -201,12 +195,12 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:102:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ | = note: register class `vreg` supports these types: -error: aborting due to 34 previous errors +error: aborting due to 33 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.riscv32gc.stderr b/tests/ui/asm/riscv/bad-reg.riscv32gc.stderr index 4770e70cc2b7d..4ff03d819e60f 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv32gc.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv32gc.stderr @@ -22,50 +22,44 @@ error: invalid register `gp`: the global pointer cannot be used as an operand fo LL | asm!("", out("gp") _); | ^^^^^^^^^^^ -error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 - | -LL | asm!("", out("gp") _); - | ^^^^^^^^^^^ - error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:102:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:96:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -73,7 +67,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:99:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -81,12 +75,12 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:102:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ | = note: register class `vreg` supports these types: -error: aborting due to 14 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.riscv32i.stderr b/tests/ui/asm/riscv/bad-reg.riscv32i.stderr index ae7db1554b196..fbe63eb0563c3 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv32i.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv32i.stderr @@ -22,74 +22,68 @@ error: invalid register `gp`: the global pointer cannot be used as an operand fo LL | asm!("", out("gp") _); | ^^^^^^^^^^^ -error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 - | -LL | asm!("", out("gp") _); - | ^^^^^^^^^^^ - error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:102:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:82:26 + --> $DIR/bad-reg.rs:80:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:84:26 + --> $DIR/bad-reg.rs:82:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:86:26 + --> $DIR/bad-reg.rs:84:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:89:26 + --> $DIR/bad-reg.rs:87:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:96:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -97,7 +91,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:99:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -105,12 +99,12 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:102:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ | = note: register class `vreg` supports these types: -error: aborting due to 18 previous errors +error: aborting due to 17 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.riscv32imafc.stderr b/tests/ui/asm/riscv/bad-reg.riscv32imafc.stderr index 8bc5c9a87fce4..57664cfe893b7 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv32imafc.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv32imafc.stderr @@ -22,50 +22,44 @@ error: invalid register `gp`: the global pointer cannot be used as an operand fo LL | asm!("", out("gp") _); | ^^^^^^^^^^^ -error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 - | -LL | asm!("", out("gp") _); - | ^^^^^^^^^^^ - error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:102:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: `d` target feature is not enabled - --> $DIR/bad-reg.rs:86:35 + --> $DIR/bad-reg.rs:84:35 | LL | asm!("/* {} */", in(freg) d); | ^ @@ -73,7 +67,7 @@ LL | asm!("/* {} */", in(freg) d); = note: this is required to use type `f64` with register class `freg` error: `d` target feature is not enabled - --> $DIR/bad-reg.rs:89:36 + --> $DIR/bad-reg.rs:87:36 | LL | asm!("/* {} */", out(freg) d); | ^ @@ -81,7 +75,7 @@ LL | asm!("/* {} */", out(freg) d); = note: this is required to use type `f64` with register class `freg` error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:96:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -89,7 +83,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:99:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -97,12 +91,12 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:102:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ | = note: register class `vreg` supports these types: -error: aborting due to 16 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.riscv64gc.stderr b/tests/ui/asm/riscv/bad-reg.riscv64gc.stderr index 4770e70cc2b7d..4ff03d819e60f 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv64gc.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv64gc.stderr @@ -22,50 +22,44 @@ error: invalid register `gp`: the global pointer cannot be used as an operand fo LL | asm!("", out("gp") _); | ^^^^^^^^^^^ -error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 - | -LL | asm!("", out("gp") _); - | ^^^^^^^^^^^ - error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:102:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:96:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -73,7 +67,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:99:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -81,12 +75,12 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:102:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ | = note: register class `vreg` supports these types: -error: aborting due to 14 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.riscv64imac.stderr b/tests/ui/asm/riscv/bad-reg.riscv64imac.stderr index ae7db1554b196..fbe63eb0563c3 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv64imac.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv64imac.stderr @@ -22,74 +22,68 @@ error: invalid register `gp`: the global pointer cannot be used as an operand fo LL | asm!("", out("gp") _); | ^^^^^^^^^^^ -error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 - | -LL | asm!("", out("gp") _); - | ^^^^^^^^^^^ - error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:102:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:82:26 + --> $DIR/bad-reg.rs:80:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:84:26 + --> $DIR/bad-reg.rs:82:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:86:26 + --> $DIR/bad-reg.rs:84:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:89:26 + --> $DIR/bad-reg.rs:87:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:96:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -97,7 +91,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:99:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -105,12 +99,12 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:102:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ | = note: register class `vreg` supports these types: -error: aborting due to 18 previous errors +error: aborting due to 17 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.rs b/tests/ui/asm/riscv/bad-reg.rs index 7f0fc00d5489d..7d032d277aa9b 100644 --- a/tests/ui/asm/riscv/bad-reg.rs +++ b/tests/ui/asm/riscv/bad-reg.rs @@ -38,8 +38,6 @@ fn f() { //~^ ERROR invalid register `sp`: the stack pointer cannot be used as an operand for inline asm asm!("", out("gp") _); //~^ ERROR invalid register `gp`: the global pointer cannot be used as an operand for inline asm - asm!("", out("gp") _); - //~^ ERROR invalid register `gp`: the global pointer cannot be used as an operand for inline asm asm!("", out("tp") _); //~^ ERROR invalid register `tp`: the thread pointer cannot be used as an operand for inline asm asm!("", out("zero") _); diff --git a/tests/ui/asm/x86_64/bad-reg.stderr b/tests/ui/asm/x86_64/bad-reg.stderr index 3df1f7b220869..6a02957210bb9 100644 --- a/tests/ui/asm/x86_64/bad-reg.stderr +++ b/tests/ui/asm/x86_64/bad-reg.stderr @@ -3,6 +3,8 @@ error: invalid register class `foo`: unknown register class | LL | asm!("{}", in(foo) foo); | ^^^^^^^^^^^ + | + = note: the following register classes are supported on this target: `reg`, `reg_abcd`, `reg_byte`, `xmm_reg`, `ymm_reg`, `zmm_reg`, `kreg`, `kreg0`, `mmx_reg`, `x87_reg`, `tmm_reg` error: invalid register `foo`: unknown register --> $DIR/bad-reg.rs:14:18 diff --git a/tests/ui/asm/x86_64/issue-82869.stderr b/tests/ui/asm/x86_64/issue-82869.stderr index 3cf9d6d1c1c00..56e4909956913 100644 --- a/tests/ui/asm/x86_64/issue-82869.stderr +++ b/tests/ui/asm/x86_64/issue-82869.stderr @@ -3,12 +3,16 @@ error: invalid register class `vreg`: unknown register class | LL | asm!("add {:d}, {:d}, d0", out(vreg) c, in(vreg) a, in("d0") { | ^^^^^^^^^^^ + | + = note: the following register classes are supported on this target: `reg`, `reg_abcd`, `reg_byte`, `xmm_reg`, `ymm_reg`, `zmm_reg`, `kreg`, `kreg0`, `mmx_reg`, `x87_reg`, `tmm_reg` error: invalid register class `vreg`: unknown register class --> $DIR/issue-82869.rs:11:45 | LL | asm!("add {:d}, {:d}, d0", out(vreg) c, in(vreg) a, in("d0") { | ^^^^^^^^^^ + | + = note: the following register classes are supported on this target: `reg`, `reg_abcd`, `reg_byte`, `xmm_reg`, `ymm_reg`, `zmm_reg`, `kreg`, `kreg0`, `mmx_reg`, `x87_reg`, `tmm_reg` error: invalid register `d0`: unknown register --> $DIR/issue-82869.rs:11:57 diff --git a/tests/ui/asm/x86_64/issue-89875.rs b/tests/ui/asm/x86_64/issue-89875.rs index af940f05fead7..0252859cff0a2 100644 --- a/tests/ui/asm/x86_64/issue-89875.rs +++ b/tests/ui/asm/x86_64/issue-89875.rs @@ -2,8 +2,6 @@ //@ needs-asm-support //@ only-x86_64 -#![feature(target_feature_11)] - use std::arch::asm; #[target_feature(enable = "avx")] diff --git a/tests/ui/asm/x86_64/x86_64_parse_error.stderr b/tests/ui/asm/x86_64/x86_64_parse_error.stderr index b64f6c1127eb6..dfa3e1d0ef207 100644 --- a/tests/ui/asm/x86_64/x86_64_parse_error.stderr +++ b/tests/ui/asm/x86_64/x86_64_parse_error.stderr @@ -20,8 +20,9 @@ LL | asm!("{a}", in("eax") foo, a = const bar); | help: consider using `const` instead of `let` | -LL | const bar: /* Type */ = 0; - | ~~~~~ ++++++++++++ +LL - let mut bar = 0; +LL + const bar: /* Type */ = 0; + | error[E0435]: attempt to use a non-constant value in a constant --> $DIR/x86_64_parse_error.rs:13:46 @@ -31,8 +32,9 @@ LL | asm!("{a}", in("eax") foo, a = const bar); | help: consider using `const` instead of `let` | -LL | const bar: /* Type */ = 0; - | ~~~~~ ++++++++++++ +LL - let mut bar = 0; +LL + const bar: /* Type */ = 0; + | error[E0435]: attempt to use a non-constant value in a constant --> $DIR/x86_64_parse_error.rs:15:42 @@ -42,8 +44,9 @@ LL | asm!("{1}", in("eax") foo, const bar); | help: consider using `const` instead of `let` | -LL | const bar: /* Type */ = 0; - | ~~~~~ ++++++++++++ +LL - let mut bar = 0; +LL + const bar: /* Type */ = 0; + | error: aborting due to 5 previous errors diff --git a/tests/ui/associated-consts/associated-const-ambiguity-report.stderr b/tests/ui/associated-consts/associated-const-ambiguity-report.stderr index 35ee95d1215b1..e68ba503c5059 100644 --- a/tests/ui/associated-consts/associated-const-ambiguity-report.stderr +++ b/tests/ui/associated-consts/associated-const-ambiguity-report.stderr @@ -16,10 +16,12 @@ LL | const ID: i32 = 1; | ^^^^^^^^^^^^^ help: use fully-qualified syntax to disambiguate | -LL | const X: i32 = ::ID; - | ~~~~~~~~~~~~~~ -LL | const X: i32 = ::ID; - | ~~~~~~~~~~~~~~ +LL - const X: i32 = ::ID; +LL + const X: i32 = ::ID; + | +LL - const X: i32 = ::ID; +LL + const X: i32 = ::ID; + | error: aborting due to 1 previous error diff --git a/tests/ui/associated-consts/associated-const-in-trait.rs b/tests/ui/associated-consts/associated-const-in-trait.rs index 4e8143d5795db..4d88f4ff53161 100644 --- a/tests/ui/associated-consts/associated-const-in-trait.rs +++ b/tests/ui/associated-consts/associated-const-in-trait.rs @@ -5,9 +5,10 @@ trait Trait { } impl dyn Trait { - //~^ ERROR the trait `Trait` cannot be made into an object [E0038] + //~^ ERROR the trait `Trait` is not dyn compatible [E0038] const fn n() -> usize { Self::N } - //~^ ERROR the trait `Trait` cannot be made into an object [E0038] + //~^ ERROR the trait `Trait` is not dyn compatible [E0038] + //~| ERROR the trait `Trait` is not dyn compatible } fn main() {} diff --git a/tests/ui/associated-consts/associated-const-in-trait.stderr b/tests/ui/associated-consts/associated-const-in-trait.stderr index b40c100579726..fba7f53c097f2 100644 --- a/tests/ui/associated-consts/associated-const-in-trait.stderr +++ b/tests/ui/associated-consts/associated-const-in-trait.stderr @@ -1,33 +1,51 @@ -error[E0038]: the trait `Trait` cannot be made into an object +error[E0038]: the trait `Trait` is not dyn compatible --> $DIR/associated-const-in-trait.rs:7:6 | LL | impl dyn Trait { - | ^^^^^^^^^ `Trait` cannot be made into an object + | ^^^^^^^^^ `Trait` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/associated-const-in-trait.rs:4:11 | LL | trait Trait { - | ----- this trait cannot be made into an object... + | ----- this trait is not dyn compatible... LL | const N: usize; | ^ ...because it contains this associated `const` = help: consider moving `N` to another trait -error[E0038]: the trait `Trait` cannot be made into an object +error[E0038]: the trait `Trait` is not dyn compatible --> $DIR/associated-const-in-trait.rs:9:29 | LL | const fn n() -> usize { Self::N } - | ^^^^ `Trait` cannot be made into an object + | ^^^^ `Trait` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/associated-const-in-trait.rs:4:11 | LL | trait Trait { - | ----- this trait cannot be made into an object... + | ----- this trait is not dyn compatible... LL | const N: usize; | ^ ...because it contains this associated `const` = help: consider moving `N` to another trait -error: aborting due to 2 previous errors +error[E0038]: the trait `Trait` is not dyn compatible + --> $DIR/associated-const-in-trait.rs:9:29 + | +LL | const fn n() -> usize { Self::N } + | ^^^^^^^ `Trait` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/associated-const-in-trait.rs:4:11 + | +LL | trait Trait { + | ----- this trait is not dyn compatible... +LL | const N: usize; + | ^ ...because it contains this associated `const` + = help: consider moving `N` to another trait + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg2.stderr b/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg2.stderr index 18d458aea809b..165e89010b687 100644 --- a/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg2.stderr +++ b/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg2.stderr @@ -1,15 +1,3 @@ -error[E0599]: no associated item named `C` found for struct `Fail` in the current scope - --> $DIR/wrong-projection-self-ty-invalid-bivariant-arg2.rs:15:23 - | -LL | struct Fail, U>(T); - | ---------------------------------- associated item `C` not found for this struct -... -LL | Fail::::C - | ^ associated item not found in `Fail` - | - = note: the associated item was found for - - `Fail` - error[E0271]: type mismatch resolving `::Assoc == u32` --> $DIR/wrong-projection-self-ty-invalid-bivariant-arg2.rs:15:5 | @@ -27,6 +15,18 @@ note: required by a bound in `Fail` LL | struct Fail, U>(T); | ^^^^^^^^^ required by this bound in `Fail` +error[E0599]: no associated item named `C` found for struct `Fail` in the current scope + --> $DIR/wrong-projection-self-ty-invalid-bivariant-arg2.rs:15:23 + | +LL | struct Fail, U>(T); + | ---------------------------------- associated item `C` not found for this struct +... +LL | Fail::::C + | ^ associated item not found in `Fail` + | + = note: the associated item was found for + - `Fail` + error: aborting due to 2 previous errors Some errors have detailed explanations: E0271, E0599. diff --git a/tests/ui/associated-inherent-types/bugs/wf-check-skipped.next.stderr b/tests/ui/associated-inherent-types/bugs/wf-check-skipped.next.stderr index bf53089675d5b..81ace4ebb6daf 100644 --- a/tests/ui/associated-inherent-types/bugs/wf-check-skipped.next.stderr +++ b/tests/ui/associated-inherent-types/bugs/wf-check-skipped.next.stderr @@ -5,6 +5,8 @@ LL | fn main() -> Foo::Bar::> {} | ^^^^^^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `[u32]` +note: required by an implicit `Sized` bound in `Vec` + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL error: aborting due to 1 previous error diff --git a/tests/ui/associated-inherent-types/dont-select-if-disabled.stderr b/tests/ui/associated-inherent-types/dont-select-if-disabled.stderr index edf54894cd4e1..d245659c20978 100644 --- a/tests/ui/associated-inherent-types/dont-select-if-disabled.stderr +++ b/tests/ui/associated-inherent-types/dont-select-if-disabled.stderr @@ -6,8 +6,9 @@ LL | struct S(S::P); | help: if there were a trait named `Example` with associated type `P` implemented for `S`, you could use the fully-qualified path | -LL | struct S(::P); - | ~~~~~~~~~~~~~~~~~ +LL - struct S(S::P); +LL + struct S(::P); + | error[E0658]: inherent associated types are unstable --> $DIR/dont-select-if-disabled.rs:15:10 diff --git a/tests/ui/associated-inherent-types/issue-109071.no_gate.stderr b/tests/ui/associated-inherent-types/issue-109071.no_gate.stderr index 72d3f5c6d4d42..d5e8fd69645ba 100644 --- a/tests/ui/associated-inherent-types/issue-109071.no_gate.stderr +++ b/tests/ui/associated-inherent-types/issue-109071.no_gate.stderr @@ -28,10 +28,12 @@ LL | fn T() -> Option {} | help: use fully-qualified syntax | -LL | fn T() -> Option< as IntoAsyncIterator>::Item> {} - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | fn T() -> Option< as IntoIterator>::Item> {} - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - fn T() -> Option {} +LL + fn T() -> Option< as IntoAsyncIterator>::Item> {} + | +LL - fn T() -> Option {} +LL + fn T() -> Option< as IntoIterator>::Item> {} + | error[E0658]: inherent associated types are unstable --> $DIR/issue-109071.rs:8:5 diff --git a/tests/ui/associated-inherent-types/issue-109768.stderr b/tests/ui/associated-inherent-types/issue-109768.stderr index e71551f9e7328..18455f4669e91 100644 --- a/tests/ui/associated-inherent-types/issue-109768.stderr +++ b/tests/ui/associated-inherent-types/issue-109768.stderr @@ -43,8 +43,9 @@ LL | struct Wrapper(T); | ^^^^^^^ help: provide the argument | -LL | const WRAPPED_ASSOC_3: Wrapper = Wrapper(/* value */); - | ~~~~~~~~~~~~~ +LL - const WRAPPED_ASSOC_3: Wrapper = Wrapper(); +LL + const WRAPPED_ASSOC_3: Wrapper = Wrapper(/* value */); + | error: aborting due to 4 previous errors diff --git a/tests/ui/associated-item/associated-item-enum.stderr b/tests/ui/associated-item/associated-item-enum.stderr index 3b00588f1d1a5..49f168b854407 100644 --- a/tests/ui/associated-item/associated-item-enum.stderr +++ b/tests/ui/associated-item/associated-item-enum.stderr @@ -9,8 +9,9 @@ LL | Enum::mispellable(); | help: there is an associated function `misspellable` with a similar name | -LL | Enum::misspellable(); - | ~~~~~~~~~~~~ +LL - Enum::mispellable(); +LL + Enum::misspellable(); + | error[E0599]: no variant or associated item named `mispellable_trait` found for enum `Enum` in the current scope --> $DIR/associated-item-enum.rs:18:11 @@ -23,8 +24,9 @@ LL | Enum::mispellable_trait(); | help: there is an associated function `misspellable_trait` with a similar name | -LL | Enum::misspellable_trait(); - | ~~~~~~~~~~~~~~~~~~ +LL - Enum::mispellable_trait(); +LL + Enum::misspellable_trait(); + | error[E0599]: no variant or associated item named `MISPELLABLE` found for enum `Enum` in the current scope --> $DIR/associated-item-enum.rs:19:11 @@ -37,8 +39,9 @@ LL | Enum::MISPELLABLE; | help: there is an associated constant `MISSPELLABLE` with a similar name | -LL | Enum::MISSPELLABLE; - | ~~~~~~~~~~~~ +LL - Enum::MISPELLABLE; +LL + Enum::MISSPELLABLE; + | error: aborting due to 3 previous errors diff --git a/tests/ui/associated-item/issue-48027.rs b/tests/ui/associated-item/issue-48027.rs index d2b51184c999a..715f3935107b4 100644 --- a/tests/ui/associated-item/issue-48027.rs +++ b/tests/ui/associated-item/issue-48027.rs @@ -3,6 +3,6 @@ trait Bar { fn return_n(&self) -> [u8; Bar::X]; //~ ERROR: E0790 } -impl dyn Bar {} //~ ERROR: the trait `Bar` cannot be made into an object +impl dyn Bar {} //~ ERROR: the trait `Bar` is not dyn compatible fn main() {} diff --git a/tests/ui/associated-item/issue-48027.stderr b/tests/ui/associated-item/issue-48027.stderr index 2883259ce2fd8..513961e2bd0ad 100644 --- a/tests/ui/associated-item/issue-48027.stderr +++ b/tests/ui/associated-item/issue-48027.stderr @@ -1,14 +1,15 @@ -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/issue-48027.rs:6:6 | LL | impl dyn Bar {} - | ^^^^^^^ `Bar` cannot be made into an object + | ^^^^^^^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-48027.rs:2:11 | LL | trait Bar { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | const X: usize; | ^ ...because it contains this associated `const` = help: consider moving `X` to another trait diff --git a/tests/ui/associated-type-bounds/assoc-type-eq-with-dyn-atb-fail.stderr b/tests/ui/associated-type-bounds/assoc-type-eq-with-dyn-atb-fail.stderr index b1575abe57112..d5795e7ed7db8 100644 --- a/tests/ui/associated-type-bounds/assoc-type-eq-with-dyn-atb-fail.stderr +++ b/tests/ui/associated-type-bounds/assoc-type-eq-with-dyn-atb-fail.stderr @@ -6,8 +6,9 @@ LL | type Out = Box>; | help: use `impl Trait` to introduce a type instead | -LL | type Out = Box>; - | ~~~~~~ +LL - type Out = Box>; +LL + type Out = Box>; + | error: aborting due to 1 previous error diff --git a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.current.stderr b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.current.stderr new file mode 100644 index 0000000000000..0d57d9d0142d1 --- /dev/null +++ b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.current.stderr @@ -0,0 +1,14 @@ +error[E0382]: use of moved value: `x` + --> $DIR/cant-see-copy-bound-from-child-rigid.rs:18:9 + | +LL | fn foo(x: T::Assoc) -> (T::Assoc, T::Assoc) + | - move occurs because `x` has type `::Assoc`, which does not implement the `Copy` trait +... +LL | (x, x) + | - ^ value used here after move + | | + | value moved here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.next.stderr b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.next.stderr new file mode 100644 index 0000000000000..0d57d9d0142d1 --- /dev/null +++ b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.next.stderr @@ -0,0 +1,14 @@ +error[E0382]: use of moved value: `x` + --> $DIR/cant-see-copy-bound-from-child-rigid.rs:18:9 + | +LL | fn foo(x: T::Assoc) -> (T::Assoc, T::Assoc) + | - move occurs because `x` has type `::Assoc`, which does not implement the `Copy` trait +... +LL | (x, x) + | - ^ value used here after move + | | + | value moved here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.rs b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.rs index 6b3fd7e898d18..fe135031b3138 100644 --- a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.rs +++ b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.rs @@ -1,3 +1,7 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + trait Id { type This: ?Sized; } diff --git a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.stderr b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.stderr deleted file mode 100644 index 3ed73918de313..0000000000000 --- a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0382]: use of moved value: `x` - --> $DIR/cant-see-copy-bound-from-child-rigid.rs:14:9 - | -LL | fn foo(x: T::Assoc) -> (T::Assoc, T::Assoc) - | - move occurs because `x` has type `::Assoc`, which does not implement the `Copy` trait -... -LL | (x, x) - | - ^ value used here after move - | | - | value moved here - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/associated-type-bounds/elision.stderr b/tests/ui/associated-type-bounds/elision.stderr index 36ca5a80024af..75141080dbf2e 100644 --- a/tests/ui/associated-type-bounds/elision.stderr +++ b/tests/ui/associated-type-bounds/elision.stderr @@ -7,8 +7,9 @@ LL | fn f(x: &mut dyn Iterator>) -> Option<&'_ ()> = help: this function's return type contains a borrowed value, but the signature does not say which one of `x`'s 2 lifetimes it is borrowed from help: consider introducing a named lifetime parameter | -LL | fn f<'a>(x: &'a mut dyn Iterator>) -> Option<&'a ()> { x.next() } - | ++++ ++ ~~ ~~ +LL - fn f(x: &mut dyn Iterator>) -> Option<&'_ ()> { x.next() } +LL + fn f<'a>(x: &'a mut dyn Iterator>) -> Option<&'a ()> { x.next() } + | error: associated type bounds are not allowed in `dyn` types --> $DIR/elision.rs:4:27 @@ -18,8 +19,9 @@ LL | fn f(x: &mut dyn Iterator>) -> Option<&'_ ()> | help: use `impl Trait` to introduce a type instead | -LL | fn f(x: &mut dyn Iterator>) -> Option<&'_ ()> { x.next() } - | ~~~~~~ +LL - fn f(x: &mut dyn Iterator>) -> Option<&'_ ()> { x.next() } +LL + fn f(x: &mut dyn Iterator>) -> Option<&'_ ()> { x.next() } + | error: aborting due to 2 previous errors diff --git a/tests/ui/associated-type-bounds/return-type-notation/equality.current.stderr b/tests/ui/associated-type-bounds/return-type-notation/equality.current.stderr deleted file mode 100644 index 26b4d935ac7c6..0000000000000 --- a/tests/ui/associated-type-bounds/return-type-notation/equality.current.stderr +++ /dev/null @@ -1,17 +0,0 @@ -warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/equality.rs:5:12 - | -LL | #![feature(return_type_notation)] - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #109417 for more information - = note: `#[warn(incomplete_features)]` on by default - -error: return type notation is not allowed to use type equality - --> $DIR/equality.rs:14:18 - | -LL | fn test>>>() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error; 1 warning emitted - diff --git a/tests/ui/associated-type-bounds/return-type-notation/equality.next.stderr b/tests/ui/associated-type-bounds/return-type-notation/equality.next.stderr deleted file mode 100644 index 26b4d935ac7c6..0000000000000 --- a/tests/ui/associated-type-bounds/return-type-notation/equality.next.stderr +++ /dev/null @@ -1,17 +0,0 @@ -warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/equality.rs:5:12 - | -LL | #![feature(return_type_notation)] - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #109417 for more information - = note: `#[warn(incomplete_features)]` on by default - -error: return type notation is not allowed to use type equality - --> $DIR/equality.rs:14:18 - | -LL | fn test>>>() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error; 1 warning emitted - diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-ambiguous.stderr b/tests/ui/associated-type-bounds/return-type-notation/path-ambiguous.stderr index 80705424035ce..12f6762cb5171 100644 --- a/tests/ui/associated-type-bounds/return-type-notation/path-ambiguous.stderr +++ b/tests/ui/associated-type-bounds/return-type-notation/path-ambiguous.stderr @@ -12,12 +12,14 @@ LL | T::method(..): Send, | help: use fully-qualified syntax to disambiguate | -LL | ::method(..): Send, - | ~~~~~~~~~~ +LL - T::method(..): Send, +LL + ::method(..): Send, + | help: use fully-qualified syntax to disambiguate | -LL | ::method(..): Send, - | ~~~~~~~~~~ +LL - T::method(..): Send, +LL + ::method(..): Send, + | error[E0221]: ambiguous associated function `method` in bounds of `T` --> $DIR/path-ambiguous.rs:21:5 @@ -33,12 +35,14 @@ LL | T::method(..): Send, | help: use fully-qualified syntax to disambiguate | -LL | ::method(..): Send, - | ~~~~~~~~~~ +LL - T::method(..): Send, +LL + ::method(..): Send, + | help: use fully-qualified syntax to disambiguate | -LL | ::method(..): Send, - | ~~~~~~~~~~ +LL - T::method(..): Send, +LL + ::method(..): Send, + | error: aborting due to 2 previous errors diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-higher-ranked.stderr b/tests/ui/associated-type-bounds/return-type-notation/path-higher-ranked.stderr index 2a9a1a1e8994c..b3a229c58c815 100644 --- a/tests/ui/associated-type-bounds/return-type-notation/path-higher-ranked.stderr +++ b/tests/ui/associated-type-bounds/return-type-notation/path-higher-ranked.stderr @@ -6,8 +6,9 @@ LL | T::method(..): Send, | help: use a fully qualified path with inferred lifetimes | -LL | >::method(..): Send, - | ~~~~~~~~~~~~~~ +LL - T::method(..): Send, +LL + >::method(..): Send, + | error[E0212]: cannot use the associated function of a trait with uninferred generic parameters --> $DIR/path-higher-ranked.rs:19:5 @@ -17,8 +18,9 @@ LL | T::method(..): Send, | help: use a fully qualified path with inferred lifetimes | -LL | >::method(..): Send, - | ~~~~~~~~~~~~~~ +LL - T::method(..): Send, +LL + >::method(..): Send, + | error: aborting due to 2 previous errors diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-no-qself.stderr b/tests/ui/associated-type-bounds/return-type-notation/path-no-qself.stderr index 6dbb5dabc0e43..aad6dfc496b75 100644 --- a/tests/ui/associated-type-bounds/return-type-notation/path-no-qself.stderr +++ b/tests/ui/associated-type-bounds/return-type-notation/path-no-qself.stderr @@ -6,8 +6,9 @@ LL | Trait::method(..): Send, | help: if there were a type named `Example` that implemented `Trait`, you could use the fully-qualified path | -LL | ::method: Send, - | ~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - Trait::method(..): Send, +LL + ::method: Send, + | error: aborting due to 1 previous error diff --git a/tests/ui/associated-type-bounds/suggest-assoc-ty-bound-on-eq-bound.stderr b/tests/ui/associated-type-bounds/suggest-assoc-ty-bound-on-eq-bound.stderr index dbe285c531078..6eb8fabb1852c 100644 --- a/tests/ui/associated-type-bounds/suggest-assoc-ty-bound-on-eq-bound.stderr +++ b/tests/ui/associated-type-bounds/suggest-assoc-ty-bound-on-eq-bound.stderr @@ -10,8 +10,9 @@ LL | fn f(_: impl Trait) {} | +++ help: you might have meant to write a bound here | -LL | fn f(_: impl Trait) {} - | ~ +LL - fn f(_: impl Trait) {} +LL + fn f(_: impl Trait) {} + | error[E0782]: expected a type, found a trait --> $DIR/suggest-assoc-ty-bound-on-eq-bound.rs:9:24 @@ -25,8 +26,9 @@ LL | fn g(_: impl Trait) {} | +++ help: you might have meant to write a bound here | -LL | fn g(_: impl Trait) {} - | ~ +LL - fn g(_: impl Trait) {} +LL + fn g(_: impl Trait) {} + | error[E0782]: expected a type, found a trait --> $DIR/suggest-assoc-ty-bound-on-eq-bound.rs:14:26 @@ -40,8 +42,9 @@ LL | fn h(_: impl Trait = dyn 'static + for<'a> Fn(&'a ())>) {} | +++ help: you might have meant to write a bound here | -LL | fn h(_: impl Trait: 'static + for<'a> Fn(&'a ())>) {} - | ~ +LL - fn h(_: impl Trait = 'static + for<'a> Fn(&'a ())>) {} +LL + fn h(_: impl Trait: 'static + for<'a> Fn(&'a ())>) {} + | error[E0782]: expected a type, found a trait --> $DIR/suggest-assoc-ty-bound-on-eq-bound.rs:20:26 diff --git a/tests/ui/associated-types/associated-type-projection-ambig-between-bound-and-where-clause.stderr b/tests/ui/associated-types/associated-type-projection-ambig-between-bound-and-where-clause.stderr index df01e1e376869..b828eb86b439c 100644 --- a/tests/ui/associated-types/associated-type-projection-ambig-between-bound-and-where-clause.stderr +++ b/tests/ui/associated-types/associated-type-projection-ambig-between-bound-and-where-clause.stderr @@ -12,12 +12,14 @@ LL | fn a(_: C::Color) { | help: use fully-qualified syntax to disambiguate | -LL | fn a(_: ::Color) { - | ~~~~~~~~~~~~ +LL - fn a(_: C::Color) { +LL + fn a(_: ::Color) { + | help: use fully-qualified syntax to disambiguate | -LL | fn a(_: ::Color) { - | ~~~~~~~~~~~~~~~~ +LL - fn a(_: C::Color) { +LL + fn a(_: ::Color) { + | error[E0221]: ambiguous associated type `Color` in bounds of `C` --> $DIR/associated-type-projection-ambig-between-bound-and-where-clause.rs:20:12 @@ -33,12 +35,14 @@ LL | fn b(_: C::Color) where C : Vehicle+Box { | help: use fully-qualified syntax to disambiguate | -LL | fn b(_: ::Color) where C : Vehicle+Box { - | ~~~~~~~~~~~~ +LL - fn b(_: C::Color) where C : Vehicle+Box { +LL + fn b(_: ::Color) where C : Vehicle+Box { + | help: use fully-qualified syntax to disambiguate | -LL | fn b(_: ::Color) where C : Vehicle+Box { - | ~~~~~~~~~~~~~~~~ +LL - fn b(_: C::Color) where C : Vehicle+Box { +LL + fn b(_: ::Color) where C : Vehicle+Box { + | error[E0221]: ambiguous associated type `Color` in bounds of `C` --> $DIR/associated-type-projection-ambig-between-bound-and-where-clause.rs:24:12 @@ -54,12 +58,14 @@ LL | fn c(_: C::Color) where C : Vehicle, C : Box { | help: use fully-qualified syntax to disambiguate | -LL | fn c(_: ::Color) where C : Vehicle, C : Box { - | ~~~~~~~~~~~~ +LL - fn c(_: C::Color) where C : Vehicle, C : Box { +LL + fn c(_: ::Color) where C : Vehicle, C : Box { + | help: use fully-qualified syntax to disambiguate | -LL | fn c(_: ::Color) where C : Vehicle, C : Box { - | ~~~~~~~~~~~~~~~~ +LL - fn c(_: C::Color) where C : Vehicle, C : Box { +LL + fn c(_: ::Color) where C : Vehicle, C : Box { + | error[E0221]: ambiguous associated type `Color` in bounds of `X` --> $DIR/associated-type-projection-ambig-between-bound-and-where-clause.rs:35:20 @@ -75,12 +81,14 @@ LL | fn e(&self, _: X::Color) where X : Box; | help: use fully-qualified syntax to disambiguate | -LL | fn e(&self, _: ::Color) where X : Box; - | ~~~~~~~~~~~~ +LL - fn e(&self, _: X::Color) where X : Box; +LL + fn e(&self, _: ::Color) where X : Box; + | help: use fully-qualified syntax to disambiguate | -LL | fn e(&self, _: ::Color) where X : Box; - | ~~~~~~~~~~~~~~~~ +LL - fn e(&self, _: X::Color) where X : Box; +LL + fn e(&self, _: ::Color) where X : Box; + | error[E0221]: ambiguous associated type `Color` in bounds of `X` --> $DIR/associated-type-projection-ambig-between-bound-and-where-clause.rs:38:20 @@ -96,12 +104,14 @@ LL | fn f(&self, _: X::Color) where X : Box { } | help: use fully-qualified syntax to disambiguate | -LL | fn f(&self, _: ::Color) where X : Box { } - | ~~~~~~~~~~~~ +LL - fn f(&self, _: X::Color) where X : Box { } +LL + fn f(&self, _: ::Color) where X : Box { } + | help: use fully-qualified syntax to disambiguate | -LL | fn f(&self, _: ::Color) where X : Box { } - | ~~~~~~~~~~~~~~~~ +LL - fn f(&self, _: X::Color) where X : Box { } +LL + fn f(&self, _: ::Color) where X : Box { } + | error[E0221]: ambiguous associated type `Color` in bounds of `X` --> $DIR/associated-type-projection-ambig-between-bound-and-where-clause.rs:30:20 @@ -117,12 +127,14 @@ LL | fn d(&self, _: X::Color) where X : Box { } | help: use fully-qualified syntax to disambiguate | -LL | fn d(&self, _: ::Color) where X : Box { } - | ~~~~~~~~~~~~ +LL - fn d(&self, _: X::Color) where X : Box { } +LL + fn d(&self, _: ::Color) where X : Box { } + | help: use fully-qualified syntax to disambiguate | -LL | fn d(&self, _: ::Color) where X : Box { } - | ~~~~~~~~~~~~~~~~ +LL - fn d(&self, _: X::Color) where X : Box { } +LL + fn d(&self, _: ::Color) where X : Box { } + | error: aborting due to 6 previous errors diff --git a/tests/ui/associated-types/associated-type-projection-from-multiple-supertraits.stderr b/tests/ui/associated-types/associated-type-projection-from-multiple-supertraits.stderr index a4874903285aa..063623ebd123f 100644 --- a/tests/ui/associated-types/associated-type-projection-from-multiple-supertraits.stderr +++ b/tests/ui/associated-types/associated-type-projection-from-multiple-supertraits.stderr @@ -20,12 +20,14 @@ LL | fn dent(c: C, color: C::Color) { | help: use fully-qualified syntax to disambiguate | -LL | fn dent(c: C, color: ::Color) { - | ~~~~~~~~~~~~~~~~ +LL - fn dent(c: C, color: C::Color) { +LL + fn dent(c: C, color: ::Color) { + | help: use fully-qualified syntax to disambiguate | -LL | fn dent(c: C, color: ::Color) { - | ~~~~~~~~~~~~ +LL - fn dent(c: C, color: C::Color) { +LL + fn dent(c: C, color: ::Color) { + | error[E0222]: ambiguous associated type `Color` in bounds of `BoxCar` --> $DIR/associated-type-projection-from-multiple-supertraits.rs:23:38 @@ -73,12 +75,14 @@ LL | fn paint(c: C, d: C::Color) { | help: use fully-qualified syntax to disambiguate | -LL | fn paint(c: C, d: ::Color) { - | ~~~~~~~~~~~~~~~~ +LL - fn paint(c: C, d: C::Color) { +LL + fn paint(c: C, d: ::Color) { + | help: use fully-qualified syntax to disambiguate | -LL | fn paint(c: C, d: ::Color) { - | ~~~~~~~~~~~~ +LL - fn paint(c: C, d: C::Color) { +LL + fn paint(c: C, d: ::Color) { + | error[E0191]: the value of the associated types `Color` in `Box`, `Color` in `Vehicle` must be specified --> $DIR/associated-type-projection-from-multiple-supertraits.rs:32:33 diff --git a/tests/ui/associated-types/associated-types-eq-1.stderr b/tests/ui/associated-types/associated-types-eq-1.stderr index 14ef36876740b..581ad25d88ac0 100644 --- a/tests/ui/associated-types/associated-types-eq-1.stderr +++ b/tests/ui/associated-types/associated-types-eq-1.stderr @@ -8,8 +8,9 @@ LL | let _: A = x.boo(); | help: a type parameter with a similar name exists | -LL | let _: I = x.boo(); - | ~ +LL - let _: A = x.boo(); +LL + let _: I = x.boo(); + | help: you might be missing a type parameter | LL | fn foo2(x: I) { diff --git a/tests/ui/associated-types/associated-types-eq-2.stderr b/tests/ui/associated-types/associated-types-eq-2.stderr index ccd13123d7046..19c34241e5f24 100644 --- a/tests/ui/associated-types/associated-types-eq-2.stderr +++ b/tests/ui/associated-types/associated-types-eq-2.stderr @@ -100,8 +100,9 @@ LL | impl Tr2 for Bar { | help: to use `Qux` as a generic argument specify it directly | -LL | impl Tr2 for Bar { - | ~~~ +LL - impl Tr2 for Bar { +LL + impl Tr2 for Bar { + | error[E0107]: trait takes 3 generic arguments but 1 generic argument was supplied --> $DIR/associated-types-eq-2.rs:47:6 @@ -189,8 +190,9 @@ LL | impl Tr2> for Bar { | help: to use `GenericTerm` as a generic argument specify it directly | -LL | impl Tr2> for Bar { - | ~~~~~~~~~~~~~~~~ +LL - impl Tr2> for Bar { +LL + impl Tr2> for Bar { + | error[E0229]: associated item constraints are not allowed here --> $DIR/associated-types-eq-2.rs:76:10 @@ -203,8 +205,12 @@ LL | | = 42, T2 = Qux, T3 = usize> for Bar { | help: to use `42` as a generic argument specify it directly | -LL | impl Tr3<42, T2 = Qux, T3 = usize> for Bar { - | ~~ +LL - impl Tr3 for Bar { +LL + impl Tr3<42, T2 = Qux, T3 = usize> for Bar { + | error[E0107]: trait takes 3 generic arguments but 0 generic arguments were supplied --> $DIR/associated-types-eq-2.rs:84:6 @@ -328,8 +334,9 @@ LL | impl<'a, T> St<'a , T = Qux> { | help: to use `Qux` as a generic argument specify it directly | -LL | impl<'a, T> St<'a , Qux> { - | ~~~ +LL - impl<'a, T> St<'a , T = Qux> { +LL + impl<'a, T> St<'a , Qux> { + | error: aborting due to 25 previous errors diff --git a/tests/ui/associated-types/associated-types-for-unimpl-trait.stderr b/tests/ui/associated-types/associated-types-for-unimpl-trait.stderr index 4cba3990049a5..db18cdf7d5aad 100644 --- a/tests/ui/associated-types/associated-types-for-unimpl-trait.stderr +++ b/tests/ui/associated-types/associated-types-for-unimpl-trait.stderr @@ -10,11 +10,12 @@ LL | fn uhoh(&self, foo: U, bar: ::Value) where Self: S | +++++++++++ error[E0277]: the trait bound `Self: Get` is not satisfied - --> $DIR/associated-types-for-unimpl-trait.rs:11:81 + --> $DIR/associated-types-for-unimpl-trait.rs:11:41 | LL | fn uhoh(&self, foo: U, bar: ::Value) where Self: Sized {} - | ^^ the trait `Get` is not implemented for `Self` + | ^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self` | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: consider further restricting `Self` | LL | fn uhoh(&self, foo: U, bar: ::Value) where Self: Sized, Self: Get {} diff --git a/tests/ui/associated-types/associated-types-in-ambiguous-context.stderr b/tests/ui/associated-types/associated-types-in-ambiguous-context.stderr index c5260adbed43a..d7d2161e7ad97 100644 --- a/tests/ui/associated-types/associated-types-in-ambiguous-context.stderr +++ b/tests/ui/associated-types/associated-types-in-ambiguous-context.stderr @@ -6,8 +6,9 @@ LL | fn get(x: T, y: U) -> Get::Value {} | help: if there were a type named `Example` that implemented `Get`, you could use the fully-qualified path | -LL | fn get(x: T, y: U) -> ::Value {} - | ~~~~~~~~~~~~~~~~~~~~~~~ +LL - fn get(x: T, y: U) -> Get::Value {} +LL + fn get(x: T, y: U) -> ::Value {} + | error[E0223]: ambiguous associated type --> $DIR/associated-types-in-ambiguous-context.rs:13:23 @@ -23,8 +24,9 @@ LL | fn get(&self) -> Get::Value; | help: if there were a type named `Example` that implemented `Get`, you could use the fully-qualified path | -LL | fn get(&self) -> ::Value; - | ~~~~~~~~~~~~~~~~~~~~~~~ +LL - fn get(&self) -> Get::Value; +LL + fn get(&self) -> ::Value; + | error[E0223]: ambiguous associated type --> $DIR/associated-types-in-ambiguous-context.rs:22:17 @@ -40,14 +42,18 @@ LL | type X = std::ops::Deref::Target; | help: use fully-qualified syntax | -LL | type X = ::Target; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | type X = as Deref>::Target; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | type X = as Deref>::Target; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | type X = ::Target; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - type X = std::ops::Deref::Target; +LL + type X = ::Target; + | +LL - type X = std::ops::Deref::Target; +LL + type X = ::Target; + | +LL - type X = std::ops::Deref::Target; +LL + type X = ::Target; + | +LL - type X = std::ops::Deref::Target; +LL + type X = as Deref>::Target; + | and N other candidates error: aborting due to 5 previous errors diff --git a/tests/ui/associated-types/associated-types-no-suitable-bound.stderr b/tests/ui/associated-types/associated-types-no-suitable-bound.stderr index 4f951ee4b4e62..b0598b3810ca4 100644 --- a/tests/ui/associated-types/associated-types-no-suitable-bound.stderr +++ b/tests/ui/associated-types/associated-types-no-suitable-bound.stderr @@ -10,11 +10,12 @@ LL | fn uhoh(foo: ::Value) {} | +++++ error[E0277]: the trait bound `T: Get` is not satisfied - --> $DIR/associated-types-no-suitable-bound.rs:11:40 + --> $DIR/associated-types-no-suitable-bound.rs:11:21 | LL | fn uhoh(foo: ::Value) {} - | ^^ the trait `Get` is not implemented for `T` + | ^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `T` | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: consider restricting type parameter `T` with trait `Get` | LL | fn uhoh(foo: ::Value) {} diff --git a/tests/ui/associated-types/associated-types-no-suitable-supertrait-2.stderr b/tests/ui/associated-types/associated-types-no-suitable-supertrait-2.stderr index c5dcfc00925d7..fce8c7ed384d6 100644 --- a/tests/ui/associated-types/associated-types-no-suitable-supertrait-2.stderr +++ b/tests/ui/associated-types/associated-types-no-suitable-supertrait-2.stderr @@ -10,11 +10,12 @@ LL | fn uhoh(&self, foo: U, bar: ::Value) where Self: Ge | +++++++++++++++ error[E0277]: the trait bound `Self: Get` is not satisfied - --> $DIR/associated-types-no-suitable-supertrait-2.rs:17:62 + --> $DIR/associated-types-no-suitable-supertrait-2.rs:17:40 | LL | fn uhoh(&self, foo: U, bar: ::Value) {} - | ^^ the trait `Get` is not implemented for `Self` + | ^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self` | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: consider further restricting `Self` | LL | fn uhoh(&self, foo: U, bar: ::Value) where Self: Get {} diff --git a/tests/ui/associated-types/associated-types-no-suitable-supertrait.stderr b/tests/ui/associated-types/associated-types-no-suitable-supertrait.stderr index 46cebda078e46..b7d528025bd14 100644 --- a/tests/ui/associated-types/associated-types-no-suitable-supertrait.stderr +++ b/tests/ui/associated-types/associated-types-no-suitable-supertrait.stderr @@ -34,27 +34,29 @@ LL | fn uhoh(&self, foo: U, bar: ::Value) where Self: Ge | +++++++++++++++ error[E0277]: the trait bound `Self: Get` is not satisfied - --> $DIR/associated-types-no-suitable-supertrait.rs:17:62 + --> $DIR/associated-types-no-suitable-supertrait.rs:17:40 | LL | fn uhoh(&self, foo: U, bar: ::Value) {} - | ^^ the trait `Get` is not implemented for `Self` + | ^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self` | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: consider further restricting `Self` | LL | fn uhoh(&self, foo: U, bar: ::Value) where Self: Get {} | +++++++++++++++ error[E0277]: the trait bound `(T, U): Get` is not satisfied - --> $DIR/associated-types-no-suitable-supertrait.rs:23:64 + --> $DIR/associated-types-no-suitable-supertrait.rs:23:40 | LL | fn uhoh(&self, foo: U, bar: <(T, U) as Get>::Value) {} - | ^^ the trait `Get` is not implemented for `(T, U)` + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `(T, U)` | help: this trait has no implementations, consider adding one --> $DIR/associated-types-no-suitable-supertrait.rs:12:1 | LL | trait Get { | ^^^^^^^^^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 5 previous errors diff --git a/tests/ui/associated-types/associated-types-path-1.stderr b/tests/ui/associated-types/associated-types-path-1.stderr index cab9dcec0b6b7..aea70e085c196 100644 --- a/tests/ui/associated-types/associated-types-path-1.stderr +++ b/tests/ui/associated-types/associated-types-path-1.stderr @@ -18,12 +18,14 @@ LL | pub fn f2(a: T, x: T::A) {} | help: use fully-qualified syntax to disambiguate | -LL | pub fn f2(a: T, x: ::A) {} - | ~~~~~~~~~~~~ +LL - pub fn f2(a: T, x: T::A) {} +LL + pub fn f2(a: T, x: ::A) {} + | help: use fully-qualified syntax to disambiguate | -LL | pub fn f2(a: T, x: ::A) {} - | ~~~~~~~~~~~~ +LL - pub fn f2(a: T, x: T::A) {} +LL + pub fn f2(a: T, x: ::A) {} + | error: aborting due to 2 previous errors diff --git a/tests/ui/associated-types/associated-types-path-2.stderr b/tests/ui/associated-types/associated-types-path-2.stderr index 5edd5c864e135..897eb75e3e3d2 100644 --- a/tests/ui/associated-types/associated-types-path-2.stderr +++ b/tests/ui/associated-types/associated-types-path-2.stderr @@ -13,8 +13,9 @@ LL | pub fn f1(a: T, x: T::A) {} | ^^ ------- help: change the type of the numeric literal from `i32` to `u32` | -LL | f1(2i32, 4u32); - | ~~~ +LL - f1(2i32, 4i32); +LL + f1(2i32, 4u32); + | error[E0277]: the trait bound `u32: Foo` is not satisfied --> $DIR/associated-types-path-2.rs:29:8 diff --git a/tests/ui/associated-types/associated-types-project-from-hrtb-in-fn-body.stderr b/tests/ui/associated-types/associated-types-project-from-hrtb-in-fn-body.stderr index e12d42e5ed0cc..42d83fca6ca83 100644 --- a/tests/ui/associated-types/associated-types-project-from-hrtb-in-fn-body.stderr +++ b/tests/ui/associated-types/associated-types-project-from-hrtb-in-fn-body.stderr @@ -7,9 +7,9 @@ LL | fn bar<'a, 'b, I : for<'x> Foo<&'x isize>>( | lifetime `'a` defined here ... LL | let z: I::A = if cond { x } else { y }; - | ^ assignment requires that `'a` must outlive `'b` + | ^ assignment requires that `'b` must outlive `'a` | - = help: consider adding the following bound: `'a: 'b` + = help: consider adding the following bound: `'b: 'a` error: lifetime may not live long enough --> $DIR/associated-types-project-from-hrtb-in-fn-body.rs:22:40 @@ -20,9 +20,9 @@ LL | fn bar<'a, 'b, I : for<'x> Foo<&'x isize>>( | lifetime `'a` defined here ... LL | let z: I::A = if cond { x } else { y }; - | ^ assignment requires that `'b` must outlive `'a` + | ^ assignment requires that `'a` must outlive `'b` | - = help: consider adding the following bound: `'b: 'a` + = help: consider adding the following bound: `'a: 'b` help: `'a` and `'b` must be the same: replace one with the other diff --git a/tests/ui/associated-types/associated-types-project-from-hrtb-in-fn.stderr b/tests/ui/associated-types/associated-types-project-from-hrtb-in-fn.stderr index 83bad291e5666..b0c904fe62be6 100644 --- a/tests/ui/associated-types/associated-types-project-from-hrtb-in-fn.stderr +++ b/tests/ui/associated-types/associated-types-project-from-hrtb-in-fn.stderr @@ -6,8 +6,9 @@ LL | x: I::A) | help: use a fully qualified path with inferred lifetimes | -LL | x: >::A) - | ~~~~~~~~~~~~~~~~~~~~ +LL - x: I::A) +LL + x: >::A) + | error: aborting due to 1 previous error diff --git a/tests/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.stderr b/tests/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.stderr index 48433b15286d4..bd30c25c97bdc 100644 --- a/tests/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.stderr +++ b/tests/ui/associated-types/associated-types-project-from-hrtb-in-trait-method.stderr @@ -6,8 +6,9 @@ LL | fn some_method(&self, arg: I::A); | help: use a fully qualified path with inferred lifetimes | -LL | fn some_method(&self, arg: >::A); - | ~~~~~~~~~~~~~~~~~~~~ +LL - fn some_method(&self, arg: I::A); +LL + fn some_method(&self, arg: >::A); + | error[E0212]: cannot use the associated type of a trait with uninferred generic parameters --> $DIR/associated-types-project-from-hrtb-in-trait-method.rs:32:24 @@ -17,8 +18,9 @@ LL | fn mango(&self) -> X::Assoc { | help: use a fully qualified path with inferred lifetimes | -LL | fn mango(&self) -> >::Assoc { - | ~~~~~~~~~~~~~~~~~~~ +LL - fn mango(&self) -> X::Assoc { +LL + fn mango(&self) -> >::Assoc { + | error: aborting due to 2 previous errors diff --git a/tests/ui/associated-types/cache/project-fn-ret-invariant.oneuse.stderr b/tests/ui/associated-types/cache/project-fn-ret-invariant.oneuse.stderr index 77841780f6216..3ef6b85c407af 100644 --- a/tests/ui/associated-types/cache/project-fn-ret-invariant.oneuse.stderr +++ b/tests/ui/associated-types/cache/project-fn-ret-invariant.oneuse.stderr @@ -7,9 +7,9 @@ LL | fn baz<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { | lifetime `'a` defined here LL | let f = foo; // <-- No consistent type can be inferred for `f` here. LL | let a = bar(f, x); - | ^^^^^^^^^ argument requires that `'a` must outlive `'b` + | ^^^^^^^^^ argument requires that `'b` must outlive `'a` | - = help: consider adding the following bound: `'a: 'b` + = help: consider adding the following bound: `'b: 'a` = note: requirement occurs because of the type `Type<'_>`, which makes the generic argument `'_` invariant = note: the struct `Type<'a>` is invariant over the parameter `'a` = help: see for more information about variance @@ -23,9 +23,9 @@ LL | fn baz<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { | lifetime `'a` defined here ... LL | let b = bar(f, y); - | ^^^^^^^^^ argument requires that `'b` must outlive `'a` + | ^^^^^^^^^ argument requires that `'a` must outlive `'b` | - = help: consider adding the following bound: `'b: 'a` + = help: consider adding the following bound: `'a: 'b` = note: requirement occurs because of the type `Type<'_>`, which makes the generic argument `'_` invariant = note: the struct `Type<'a>` is invariant over the parameter `'a` = help: see for more information about variance diff --git a/tests/ui/associated-types/defaults-specialization.stderr b/tests/ui/associated-types/defaults-specialization.stderr index b4ed99f36f44a..fd2a1a0c2d13a 100644 --- a/tests/ui/associated-types/defaults-specialization.stderr +++ b/tests/ui/associated-types/defaults-specialization.stderr @@ -23,8 +23,9 @@ LL | fn make() -> Self::Ty { found signature `fn() -> u8` help: change the output type to match the trait | -LL | fn make() -> as Tr>::Ty { 0 } - | ~~~~~~~~~~~~~~~~ +LL - fn make() -> u8 { 0 } +LL + fn make() -> as Tr>::Ty { 0 } + | error[E0053]: method `make` has an incompatible type for trait --> $DIR/defaults-specialization.rs:35:18 @@ -44,8 +45,9 @@ LL | fn make() -> Self::Ty { found signature `fn() -> bool` help: change the output type to match the trait | -LL | fn make() -> as Tr>::Ty { true } - | ~~~~~~~~~~~~~~~~ +LL - fn make() -> bool { true } +LL + fn make() -> as Tr>::Ty { true } + | error[E0308]: mismatched types --> $DIR/defaults-specialization.rs:10:9 diff --git a/tests/ui/associated-types/defaults-suitability.current.stderr b/tests/ui/associated-types/defaults-suitability.current.stderr index 61247cee1f34d..5e19674250f0b 100644 --- a/tests/ui/associated-types/defaults-suitability.current.stderr +++ b/tests/ui/associated-types/defaults-suitability.current.stderr @@ -135,7 +135,7 @@ LL | type Baz = T; help: consider further restricting type parameter `T` with trait `Clone` | LL | Self::Baz: Clone, T: std::clone::Clone - | ~~~~~~~~~~~~~~~~~~~~~~ + | ++++++++++++++++++++ error: aborting due to 8 previous errors diff --git a/tests/ui/associated-types/defaults-suitability.next.stderr b/tests/ui/associated-types/defaults-suitability.next.stderr index 61247cee1f34d..5e19674250f0b 100644 --- a/tests/ui/associated-types/defaults-suitability.next.stderr +++ b/tests/ui/associated-types/defaults-suitability.next.stderr @@ -135,7 +135,7 @@ LL | type Baz = T; help: consider further restricting type parameter `T` with trait `Clone` | LL | Self::Baz: Clone, T: std::clone::Clone - | ~~~~~~~~~~~~~~~~~~~~~~ + | ++++++++++++++++++++ error: aborting due to 8 previous errors diff --git a/tests/ui/associated-types/hr-associated-type-bound-object.rs b/tests/ui/associated-types/hr-associated-type-bound-object.rs index fec253f9289ac..825e129dbbda1 100644 --- a/tests/ui/associated-types/hr-associated-type-bound-object.rs +++ b/tests/ui/associated-types/hr-associated-type-bound-object.rs @@ -9,6 +9,7 @@ fn f<'a, T: X<'a> + ?Sized>(x: &>::U) { <>::U>::clone(x); //~^ ERROR the trait bound `for<'b> >::U: Clone` is not satisfied //~| ERROR the trait bound `for<'b> >::U: Clone` is not satisfied + //~| ERROR the trait bound `for<'b> >::U: Clone` is not satisfied //~| ERROR the trait bound `>::U: Clone` is not satisfied } diff --git a/tests/ui/associated-types/hr-associated-type-bound-object.stderr b/tests/ui/associated-types/hr-associated-type-bound-object.stderr index 322d6e7b947c8..5144cfe6d3e7d 100644 --- a/tests/ui/associated-types/hr-associated-type-bound-object.stderr +++ b/tests/ui/associated-types/hr-associated-type-bound-object.stderr @@ -47,6 +47,25 @@ help: consider further restricting the associated type LL | fn f<'a, T: X<'a> + ?Sized>(x: &>::U) where >::U: Clone { | ++++++++++++++++++++++++++++ +error[E0277]: the trait bound `for<'b> >::U: Clone` is not satisfied + --> $DIR/hr-associated-type-bound-object.rs:9:5 + | +LL | <>::U>::clone(x); + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'b> Clone` is not implemented for `>::U` + | +note: required by a bound in `X` + --> $DIR/hr-associated-type-bound-object.rs:3:33 + | +LL | trait X<'a> + | - required by a bound in this trait +LL | where +LL | for<'b> >::U: Clone, + | ^^^^^ required by this bound in `X` +help: consider further restricting the associated type + | +LL | fn f<'a, T: X<'a> + ?Sized>(x: &>::U) where for<'b> >::U: Clone { + | ++++++++++++++++++++++++++++++++++++ + error[E0277]: the trait bound `for<'b> >::U: Clone` is not satisfied --> $DIR/hr-associated-type-bound-object.rs:9:5 | @@ -66,6 +85,6 @@ help: consider further restricting the associated type LL | fn f<'a, T: X<'a> + ?Sized>(x: &>::U) where for<'b> >::U: Clone { | ++++++++++++++++++++++++++++++++++++ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/associated-types/hr-associated-type-bound-param-2.rs b/tests/ui/associated-types/hr-associated-type-bound-param-2.rs index b1a6fe871594b..673f02c7cd0e5 100644 --- a/tests/ui/associated-types/hr-associated-type-bound-param-2.rs +++ b/tests/ui/associated-types/hr-associated-type-bound-param-2.rs @@ -10,6 +10,7 @@ where ::clone(x); //~^ the trait bound `str: Clone` is not satisfied //~| the trait bound `str: Clone` is not satisfied + //~| the trait bound `str: Clone` is not satisfied } } diff --git a/tests/ui/associated-types/hr-associated-type-bound-param-2.stderr b/tests/ui/associated-types/hr-associated-type-bound-param-2.stderr index 74cc2083d26fe..8997832ab52a4 100644 --- a/tests/ui/associated-types/hr-associated-type-bound-param-2.stderr +++ b/tests/ui/associated-types/hr-associated-type-bound-param-2.stderr @@ -15,7 +15,7 @@ LL | for<'b> >::W: Clone, | ^^^^^ required by this bound in `Z` error[E0277]: the trait bound `str: Clone` is not satisfied - --> $DIR/hr-associated-type-bound-param-2.rs:17:14 + --> $DIR/hr-associated-type-bound-param-2.rs:18:14 | LL | type W = str; | ^^^ the trait `Clone` is not implemented for `str` @@ -63,6 +63,22 @@ LL | { LL | type W: ?Sized; | - required by a bound in this associated type +error[E0277]: the trait bound `str: Clone` is not satisfied + --> $DIR/hr-associated-type-bound-param-2.rs:10:9 + | +LL | ::clone(x); + | ^^^^^^^^^^^^^ the trait `Clone` is not implemented for `str` + | + = help: the trait `Clone` is implemented for `String` +note: required by a bound in `Z` + --> $DIR/hr-associated-type-bound-param-2.rs:6:35 + | +LL | trait Z<'a, T: ?Sized> + | - required by a bound in this trait +... +LL | for<'b> >::W: Clone, + | ^^^^^ required by this bound in `Z` + error[E0277]: the trait bound `str: Clone` is not satisfied --> $DIR/hr-associated-type-bound-param-2.rs:10:9 | @@ -80,7 +96,7 @@ LL | for<'b> >::W: Clone, | ^^^^^ required by this bound in `Z` error[E0277]: the trait bound `str: Clone` is not satisfied - --> $DIR/hr-associated-type-bound-param-2.rs:22:10 + --> $DIR/hr-associated-type-bound-param-2.rs:23:10 | LL | 1u16.h("abc"); | ^ the trait `Clone` is not implemented for `str` @@ -95,6 +111,6 @@ LL | for<'b> >::W: Clone, LL | fn h(&self, x: &T::W) { | - required by a bound in this associated function -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/associated-types/impl-wf-cycle-3.stderr b/tests/ui/associated-types/impl-wf-cycle-3.stderr index d3ca06f890b34..b97117a9aab69 100644 --- a/tests/ui/associated-types/impl-wf-cycle-3.stderr +++ b/tests/ui/associated-types/impl-wf-cycle-3.stderr @@ -19,8 +19,9 @@ LL | T: A, | ------------- unsatisfied trait bound introduced here help: replace the associated type with the type specified in this `impl` | -LL | T: A, - | ~~~~ +LL - T: A, +LL + T: A, + | error: aborting due to 1 previous error diff --git a/tests/ui/associated-types/invalid-ctor.fixed b/tests/ui/associated-types/invalid-ctor.fixed new file mode 100644 index 0000000000000..eba3820de0c1c --- /dev/null +++ b/tests/ui/associated-types/invalid-ctor.fixed @@ -0,0 +1,22 @@ +//@ run-rustfix + +#![allow(unused)] + +struct Constructor(i32); + +trait Trait { + type Out; + + fn mk() -> Self::Out; +} + +impl Trait for () { + type Out = Constructor; + + fn mk() -> Self::Out { + Constructor(1) + //~^ ERROR no associated item named `Out` found for unit type `()` + } +} + +fn main() {} diff --git a/tests/ui/associated-types/invalid-ctor.rs b/tests/ui/associated-types/invalid-ctor.rs new file mode 100644 index 0000000000000..73335c065c2ae --- /dev/null +++ b/tests/ui/associated-types/invalid-ctor.rs @@ -0,0 +1,22 @@ +//@ run-rustfix + +#![allow(unused)] + +struct Constructor(i32); + +trait Trait { + type Out; + + fn mk() -> Self::Out; +} + +impl Trait for () { + type Out = Constructor; + + fn mk() -> Self::Out { + Self::Out(1) + //~^ ERROR no associated item named `Out` found for unit type `()` + } +} + +fn main() {} diff --git a/tests/ui/associated-types/invalid-ctor.stderr b/tests/ui/associated-types/invalid-ctor.stderr new file mode 100644 index 0000000000000..0b3bf316f60fa --- /dev/null +++ b/tests/ui/associated-types/invalid-ctor.stderr @@ -0,0 +1,15 @@ +error[E0599]: no associated item named `Out` found for unit type `()` in the current scope + --> $DIR/invalid-ctor.rs:17:15 + | +LL | Self::Out(1) + | ^^^ associated item not found in `()` + | +help: to construct a value of type `Constructor`, use the explicit path + | +LL - Self::Out(1) +LL + Constructor(1) + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/associated-types/issue-38821.stderr b/tests/ui/associated-types/issue-38821.stderr index 58f019704e7a7..dc919299710bc 100644 --- a/tests/ui/associated-types/issue-38821.stderr +++ b/tests/ui/associated-types/issue-38821.stderr @@ -14,7 +14,7 @@ LL | impl IntoNullable for T { help: consider extending the `where` clause, but there might be an alternative better way to express this requirement | LL | Expr: Expression::Nullable>, ::SqlType: NotNull - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | +++++++++++++++++++++++++++++++++++++ error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied --> $DIR/issue-38821.rs:40:1 @@ -38,7 +38,7 @@ LL | impl IntoNullable for T { help: consider extending the `where` clause, but there might be an alternative better way to express this requirement | LL | Expr: Expression::Nullable>, ::SqlType: NotNull - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | +++++++++++++++++++++++++++++++++++++ error[E0277]: the trait bound `::SqlType: NotNull` is not satisfied --> $DIR/issue-38821.rs:23:10 diff --git a/tests/ui/associated-types/issue-54108.current.stderr b/tests/ui/associated-types/issue-54108.current.stderr index 8850b4548e33a..115a591c68f16 100644 --- a/tests/ui/associated-types/issue-54108.current.stderr +++ b/tests/ui/associated-types/issue-54108.current.stderr @@ -13,7 +13,7 @@ LL | type Size: Add; help: consider further restricting the associated type | LL | T: SubEncoder, ::ActualSize: Add - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | ++++++++++++++++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/associated-types/issue-54108.next.stderr b/tests/ui/associated-types/issue-54108.next.stderr index 5e2fa551afe30..e6d65d8246a11 100644 --- a/tests/ui/associated-types/issue-54108.next.stderr +++ b/tests/ui/associated-types/issue-54108.next.stderr @@ -13,7 +13,7 @@ LL | type Size: Add; help: consider further restricting the associated type | LL | T: SubEncoder, ::ActualSize: Add - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | ++++++++++++++++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/associated-types/issue-59324.stderr b/tests/ui/associated-types/issue-59324.stderr index 805c3e60bb6a8..dc8f9cfe895b5 100644 --- a/tests/ui/associated-types/issue-59324.stderr +++ b/tests/ui/associated-types/issue-59324.stderr @@ -65,16 +65,17 @@ LL | pub trait ThriftService: | +++++ error[E0277]: the trait bound `(): Foo` is not satisfied - --> $DIR/issue-59324.rs:23:52 + --> $DIR/issue-59324.rs:23:29 | LL | fn with_factory(factory: dyn ThriftService<()>) {} - | ^^ the trait `Foo` is not implemented for `()` + | ^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `()` | help: this trait has no implementations, consider adding one --> $DIR/issue-59324.rs:3:1 | LL | pub trait Foo: NotFoo { | ^^^^^^^^^^^^^^^^^^^^^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: the size for values of type `(dyn ThriftService<(), AssocType = _> + 'static)` cannot be known at compilation time --> $DIR/issue-59324.rs:23:29 @@ -86,8 +87,9 @@ LL | fn with_factory(factory: dyn ThriftService<()>) {} = help: unsized fn params are gated as an unstable feature help: you can use `impl Trait` as the argument type | -LL | fn with_factory(factory: impl ThriftService<()>) {} - | ~~~~ +LL - fn with_factory(factory: dyn ThriftService<()>) {} +LL + fn with_factory(factory: impl ThriftService<()>) {} + | help: function arguments must have a statically known size, borrowed types always have a known size | LL | fn with_factory(factory: &dyn ThriftService<()>) {} diff --git a/tests/ui/associated-types/issue-91231.rs b/tests/ui/associated-types/issue-91231.rs index d1c99fd44fa02..a04ab60a7fcdb 100644 --- a/tests/ui/associated-types/issue-91231.rs +++ b/tests/ui/associated-types/issue-91231.rs @@ -3,7 +3,7 @@ #![feature(extern_types)] #![allow(dead_code)] -extern { +extern "C" { type Extern; } diff --git a/tests/ui/associated-types/remove-invalid-type-bound-suggest-issue-127555.rs b/tests/ui/associated-types/remove-invalid-type-bound-suggest-issue-127555.rs index b4df58b3c25a8..4dfeab9e8c3f0 100644 --- a/tests/ui/associated-types/remove-invalid-type-bound-suggest-issue-127555.rs +++ b/tests/ui/associated-types/remove-invalid-type-bound-suggest-issue-127555.rs @@ -11,9 +11,9 @@ struct Baz {} impl Foo for Baz { async fn bar(&mut self, _func: F) -> () - //~^ ERROR `F` cannot be sent between threads safely where F: FnMut() + Send, + //~^ impl has stricter requirements than trait { () } diff --git a/tests/ui/associated-types/remove-invalid-type-bound-suggest-issue-127555.stderr b/tests/ui/associated-types/remove-invalid-type-bound-suggest-issue-127555.stderr index e6379954776df..8d5cad4493e85 100644 --- a/tests/ui/associated-types/remove-invalid-type-bound-suggest-issue-127555.stderr +++ b/tests/ui/associated-types/remove-invalid-type-bound-suggest-issue-127555.stderr @@ -1,21 +1,14 @@ -error[E0277]: `F` cannot be sent between threads safely - --> $DIR/remove-invalid-type-bound-suggest-issue-127555.rs:13:5 +error[E0276]: impl has stricter requirements than trait + --> $DIR/remove-invalid-type-bound-suggest-issue-127555.rs:15:22 | -LL | / async fn bar(&mut self, _func: F) -> () -LL | | +LL | / fn bar(&mut self, func: F) -> impl std::future::Future + Send LL | | where -LL | | F: FnMut() + Send, - | |__________________________^ `F` cannot be sent between threads safely - | -note: required by a bound in `::bar` - --> $DIR/remove-invalid-type-bound-suggest-issue-127555.rs:16:22 - | -LL | async fn bar(&mut self, _func: F) -> () - | --- required by a bound in this associated function +LL | | F: FnMut(); + | |___________________- definition of `bar` from trait ... -LL | F: FnMut() + Send, - | ^^^^ required by this bound in `::bar` +LL | F: FnMut() + Send, + | ^^^^ impl has extra requirement `F: Send` error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0276`. diff --git a/tests/ui/async-await/async-closures/arg-mismatch.stderr b/tests/ui/async-await/async-closures/arg-mismatch.stderr index 5c8e665029381..8c11fa1577691 100644 --- a/tests/ui/async-await/async-closures/arg-mismatch.stderr +++ b/tests/ui/async-await/async-closures/arg-mismatch.stderr @@ -13,8 +13,9 @@ LL | let c = async |x| {}; | ^ help: change the type of the numeric literal from `usize` to `i32` | -LL | c(2i32).await; - | ~~~ +LL - c(2usize).await; +LL + c(2i32).await; + | error: aborting due to 1 previous error diff --git a/tests/ui/async-await/async-closures/fn-exception-target-features.rs b/tests/ui/async-await/async-closures/fn-exception-target-features.rs index 82fc776fd2c72..66cc413977032 100644 --- a/tests/ui/async-await/async-closures/fn-exception-target-features.rs +++ b/tests/ui/async-await/async-closures/fn-exception-target-features.rs @@ -1,14 +1,13 @@ //@ edition: 2021 //@ only-x86_64 -#![feature(target_feature_11)] -// `target_feature_11` just to test safe functions w/ target features. - -use std::pin::Pin; use std::future::Future; +use std::pin::Pin; #[target_feature(enable = "sse2")] -fn target_feature() -> Pin + 'static>> { todo!() } +fn target_feature() -> Pin + 'static>> { + todo!() +} fn test(f: impl AsyncFn()) {} diff --git a/tests/ui/async-await/async-closures/fn-exception-target-features.stderr b/tests/ui/async-await/async-closures/fn-exception-target-features.stderr index db5895108bb0e..f0846bfdb1250 100644 --- a/tests/ui/async-await/async-closures/fn-exception-target-features.stderr +++ b/tests/ui/async-await/async-closures/fn-exception-target-features.stderr @@ -1,13 +1,14 @@ -error[E0277]: the trait bound `fn() -> Pin + 'static)>> {target_feature}: AsyncFn()` is not satisfied - --> $DIR/fn-exception-target-features.rs:16:10 +error[E0277]: the trait bound `#[target_features] fn() -> Pin + 'static)>> {target_feature}: AsyncFn()` is not satisfied + --> $DIR/fn-exception-target-features.rs:15:10 | LL | test(target_feature); - | ---- ^^^^^^^^^^^^^^ the trait `AsyncFn()` is not implemented for fn item `fn() -> Pin + 'static)>> {target_feature}` + | ---- ^^^^^^^^^^^^^^ unsatisfied trait bound | | | required by a bound introduced by this call | + = help: the trait `AsyncFn()` is not implemented for fn item `#[target_features] fn() -> Pin + 'static)>> {target_feature}` note: required by a bound in `test` - --> $DIR/fn-exception-target-features.rs:13:17 + --> $DIR/fn-exception-target-features.rs:12:17 | LL | fn test(f: impl AsyncFn()) {} | ^^^^^^^^^ required by this bound in `test` diff --git a/tests/ui/async-await/async-closures/is-not-fn.current.stderr b/tests/ui/async-await/async-closures/is-not-fn.current.stderr new file mode 100644 index 0000000000000..0fab1c15f27df --- /dev/null +++ b/tests/ui/async-await/async-closures/is-not-fn.current.stderr @@ -0,0 +1,19 @@ +error[E0271]: expected `{async closure@is-not-fn.rs:8:14}` to return `()`, but it returns `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25}` + --> $DIR/is-not-fn.rs:8:14 + | +LL | needs_fn(async || {}); + | -------- ^^^^^^^^^^^ expected `()`, found `async` closure body + | | + | required by a bound introduced by this call + | + = note: expected unit type `()` + found `async` closure body `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25}` +note: required by a bound in `needs_fn` + --> $DIR/is-not-fn.rs:7:25 + | +LL | fn needs_fn(x: impl FnOnce()) {} + | ^^^^^^^^ required by this bound in `needs_fn` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/async-await/async-closures/is-not-fn.next.stderr b/tests/ui/async-await/async-closures/is-not-fn.next.stderr new file mode 100644 index 0000000000000..0fab1c15f27df --- /dev/null +++ b/tests/ui/async-await/async-closures/is-not-fn.next.stderr @@ -0,0 +1,19 @@ +error[E0271]: expected `{async closure@is-not-fn.rs:8:14}` to return `()`, but it returns `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25}` + --> $DIR/is-not-fn.rs:8:14 + | +LL | needs_fn(async || {}); + | -------- ^^^^^^^^^^^ expected `()`, found `async` closure body + | | + | required by a bound introduced by this call + | + = note: expected unit type `()` + found `async` closure body `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25}` +note: required by a bound in `needs_fn` + --> $DIR/is-not-fn.rs:7:25 + | +LL | fn needs_fn(x: impl FnOnce()) {} + | ^^^^^^^^ required by this bound in `needs_fn` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/async-await/async-closures/is-not-fn.rs b/tests/ui/async-await/async-closures/is-not-fn.rs index 4acaa5d9809a1..e5ab4742daba1 100644 --- a/tests/ui/async-await/async-closures/is-not-fn.rs +++ b/tests/ui/async-await/async-closures/is-not-fn.rs @@ -1,7 +1,10 @@ //@ edition:2021 +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver fn main() { fn needs_fn(x: impl FnOnce()) {} needs_fn(async || {}); - //~^ ERROR expected `{async closure@is-not-fn.rs:5:14}` to be a closure that returns `()` + //~^ ERROR expected `{async closure@is-not-fn.rs:8:14}` to return `()` } diff --git a/tests/ui/async-await/async-closures/is-not-fn.stderr b/tests/ui/async-await/async-closures/is-not-fn.stderr deleted file mode 100644 index bc1d5e6e9d112..0000000000000 --- a/tests/ui/async-await/async-closures/is-not-fn.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0271]: expected `{async closure@is-not-fn.rs:5:14}` to be a closure that returns `()`, but it returns `{async closure body@$DIR/is-not-fn.rs:5:23: 5:25}` - --> $DIR/is-not-fn.rs:5:14 - | -LL | needs_fn(async || {}); - | -------- ^^^^^^^^^^^ expected `()`, found `async` closure body - | | - | required by a bound introduced by this call - | - = note: expected unit type `()` - found `async` closure body `{async closure body@$DIR/is-not-fn.rs:5:23: 5:25}` -note: required by a bound in `needs_fn` - --> $DIR/is-not-fn.rs:4:25 - | -LL | fn needs_fn(x: impl FnOnce()) {} - | ^^^^^^^^ required by this bound in `needs_fn` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang-2.rs b/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang-2.rs new file mode 100644 index 0000000000000..4cdf4bf8def6c --- /dev/null +++ b/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang-2.rs @@ -0,0 +1,23 @@ +//@ edition: 2021 +//@ build-fail + +// Regression test for . + +use std::future::Future; +use std::ops::AsyncFn; +use std::pin::Pin; + +fn recur<'l>(closure: &'l impl AsyncFn()) -> Pin + 'l>> { + Box::pin(async move { + let _ = closure(); + let _ = recur(&async || { + //~^ ERROR reached the recursion limit + let _ = closure(); + }); + }) +} + +fn main() { + let closure = async || {}; + let _ = recur(&closure); +} diff --git a/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang-2.stderr b/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang-2.stderr new file mode 100644 index 0000000000000..64f4665225ffe --- /dev/null +++ b/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang-2.stderr @@ -0,0 +1,18 @@ +error: reached the recursion limit while instantiating `recur::<{async closure@$DIR/post-mono-higher-ranked-hang-2.rs:13:24: 13:32}>` + --> $DIR/post-mono-higher-ranked-hang-2.rs:13:17 + | +LL | let _ = recur(&async || { + | _________________^ +LL | | +LL | | let _ = closure(); +LL | | }); + | |__________^ + | +note: `recur` defined here + --> $DIR/post-mono-higher-ranked-hang-2.rs:10:1 + | +LL | fn recur<'l>(closure: &'l impl AsyncFn()) -> Pin + 'l>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.rs b/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.rs new file mode 100644 index 0000000000000..f6ebf787f81b9 --- /dev/null +++ b/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.rs @@ -0,0 +1,63 @@ +//@ build-fail +//@ aux-build:block-on.rs +//@ edition:2021 + +// Regression test for . + +extern crate block_on; + +use std::future::Future; +use std::ops::AsyncFnMut; +use std::pin::{Pin, pin}; +use std::task::*; + +trait Db {} + +impl Db for () {} + +struct Env<'db> { + db: &'db (), +} + +#[derive(Debug)] +enum SymPerm<'db> { + Dummy(&'db ()), + Apply(Box>, Box>), +} + +pub struct ToChain<'env, 'db> { + db: &'db dyn crate::Db, + env: &'env Env<'db>, +} + +impl<'env, 'db> ToChain<'env, 'db> { + fn perm_pairs<'l>( + &'l self, + perm: &'l SymPerm<'db>, + yield_chain: &'l mut impl AsyncFnMut(&SymPerm<'db>), + ) -> Pin + 'l>> { + Box::pin(async move { + match perm { + SymPerm::Dummy(_) => yield_chain(perm).await, + SymPerm::Apply(l, r) => { + self.perm_pairs(l, &mut async move |left_pair| { + //~^ ERROR reached the recursion limit while instantiating + self.perm_pairs(r, yield_chain).await + }) + .await + } + } + }) + } +} + +fn main() { + block_on::block_on(async { + let pair = SymPerm::Apply(Box::new(SymPerm::Dummy(&())), Box::new(SymPerm::Dummy(&()))); + ToChain { db: &(), env: &Env { db: &() } } + .perm_pairs(&pair, &mut async |p| { + eprintln!("{p:?}"); + }) + .await; + }); +} diff --git a/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.stderr b/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.stderr new file mode 100644 index 0000000000000..486e5f9416550 --- /dev/null +++ b/tests/ui/async-await/async-closures/post-mono-higher-ranked-hang.stderr @@ -0,0 +1,21 @@ +error: reached the recursion limit while instantiating `ToChain::<'_, '_>::perm_pairs::<{async closure@$DIR/post-mono-higher-ranked-hang.rs:43:45: 43:67}>` + --> $DIR/post-mono-higher-ranked-hang.rs:43:21 + | +LL | / self.perm_pairs(l, &mut async move |left_pair| { +LL | | +LL | | self.perm_pairs(r, yield_chain).await +LL | | }) + | |______________________^ + | +note: `ToChain::<'env, 'db>::perm_pairs` defined here + --> $DIR/post-mono-higher-ranked-hang.rs:34:5 + | +LL | / fn perm_pairs<'l>( +LL | | &'l self, +LL | | perm: &'l SymPerm<'db>, +LL | | yield_chain: &'l mut impl AsyncFnMut(&SymPerm<'db>), +LL | | ) -> Pin + 'l>> { + | |____________________________________________________________^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr index be39dbf313bf0..329cec6dad3a3 100644 --- a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr +++ b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr @@ -7,7 +7,7 @@ LL | let c = async || { println!("{}", *x); }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough LL | outlives::<'a>(c()); LL | outlives::<'a>(call_once(c)); - | ------------ argument requires that `x` is borrowed for `'a` + | ---------------------------- argument requires that `x` is borrowed for `'a` ... LL | } | - `x` dropped here while still borrowed @@ -21,10 +21,10 @@ LL | fn simple<'a>(x: &'a i32) { LL | let c = async move || { println!("{}", *x); }; | - binding `c` declared here LL | outlives::<'a>(c()); - | ^-- - | | - | borrowed value does not live long enough - | argument requires that `c` is borrowed for `'a` + | ---------------^--- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` LL | outlives::<'a>(call_once(c)); LL | } | - `c` dropped here while still borrowed @@ -38,7 +38,7 @@ LL | let c = async || { println!("{}", *x.0); }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough LL | outlives::<'a>(c()); LL | outlives::<'a>(call_once(c)); - | ------------ argument requires that `x` is borrowed for `'a` + | ---------------------------- argument requires that `x` is borrowed for `'a` ... LL | } | - `x` dropped here while still borrowed @@ -52,7 +52,7 @@ LL | let c = async || { println!("{}", *x.0); }; | ---------------------------------- borrow of `x` occurs here LL | outlives::<'a>(c()); LL | outlives::<'a>(call_once(c)); - | ------------ argument requires that `x` is borrowed for `'a` + | ---------------------------- argument requires that `x` is borrowed for `'a` LL | LL | let c = async move || { println!("{}", *x.0); }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ move out of `x` occurs here @@ -66,10 +66,10 @@ LL | fn through_field<'a>(x: S<'a>) { LL | let c = async move || { println!("{}", *x.0); }; | - binding `c` declared here LL | outlives::<'a>(c()); - | ^-- - | | - | borrowed value does not live long enough - | argument requires that `c` is borrowed for `'a` + | ---------------^--- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` LL | outlives::<'a>(call_once(c)); LL | } | - `c` dropped here while still borrowed @@ -83,10 +83,10 @@ LL | fn through_field<'a>(x: S<'a>) { LL | let c = async move || { println!("{}", *x.0); }; | - binding `c` declared here LL | outlives::<'a>(c()); - | --- - | | - | borrow of `c` occurs here - | argument requires that `c` is borrowed for `'a` + | ------------------- + | | | + | | borrow of `c` occurs here + | argument requires that `c` is borrowed for `'a` LL | outlives::<'a>(call_once(c)); | ^ move out of `c` occurs here @@ -99,18 +99,18 @@ LL | let c = async || { println!("{}", *x.0); }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough LL | outlives::<'a>(c()); LL | outlives::<'a>(call_once(c)); - | ------------ argument requires that `x` is borrowed for `'a` + | ---------------------------- argument requires that `x` is borrowed for `'a` LL | } | - `x` dropped here while still borrowed error[E0621]: explicit lifetime required in the type of `x` - --> $DIR/without-precise-captures-we-are-powerless.rs:38:20 + --> $DIR/without-precise-captures-we-are-powerless.rs:38:5 | LL | fn through_field_and_ref<'a>(x: &S<'a>) { | ------ help: add explicit lifetime `'a` to the type of `x`: `&'a S<'a>` ... LL | outlives::<'a>(call_once(c)); - | ^^^^^^^^^^^^ lifetime `'a` required + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime `'a` required error[E0597]: `c` does not live long enough --> $DIR/without-precise-captures-we-are-powerless.rs:43:20 @@ -120,22 +120,22 @@ LL | fn through_field_and_ref_move<'a>(x: &S<'a>) { LL | let c = async move || { println!("{}", *x.0); }; | - binding `c` declared here LL | outlives::<'a>(c()); - | ^-- - | | - | borrowed value does not live long enough - | argument requires that `c` is borrowed for `'a` + | ---------------^--- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'a` LL | outlives::<'a>(call_once(c)); LL | } | - `c` dropped here while still borrowed error[E0621]: explicit lifetime required in the type of `x` - --> $DIR/without-precise-captures-we-are-powerless.rs:44:20 + --> $DIR/without-precise-captures-we-are-powerless.rs:44:5 | LL | fn through_field_and_ref_move<'a>(x: &S<'a>) { | ------ help: add explicit lifetime `'a` to the type of `x`: `&'a S<'a>` ... LL | outlives::<'a>(call_once(c)); - | ^^^^^^^^^^^^ lifetime `'a` required + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime `'a` required error: aborting due to 10 previous errors diff --git a/tests/ui/async-await/async-fn/dyn-pos.rs b/tests/ui/async-await/async-fn/dyn-pos.rs index d71af1bd53e2d..ab4685b07bb0d 100644 --- a/tests/ui/async-await/async-fn/dyn-pos.rs +++ b/tests/ui/async-await/async-fn/dyn-pos.rs @@ -1,6 +1,6 @@ //@ edition:2018 fn foo(x: &dyn AsyncFn()) {} -//~^ ERROR the trait `AsyncFnMut` cannot be made into an object +//~^ ERROR the trait `AsyncFnMut` is not dyn compatible fn main() {} diff --git a/tests/ui/async-await/async-fn/dyn-pos.stderr b/tests/ui/async-await/async-fn/dyn-pos.stderr index 0c90184667198..7d5b37bdbe73b 100644 --- a/tests/ui/async-await/async-fn/dyn-pos.stderr +++ b/tests/ui/async-await/async-fn/dyn-pos.stderr @@ -1,17 +1,14 @@ -error[E0038]: the trait `AsyncFnMut` cannot be made into an object +error[E0038]: the trait `AsyncFnMut` is not dyn compatible --> $DIR/dyn-pos.rs:3:16 | LL | fn foo(x: &dyn AsyncFn()) {} - | ^^^^^^^^^ `AsyncFnMut` cannot be made into an object + | ^^^^^^^^^ `AsyncFnMut` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL | - = note: the trait cannot be made into an object because it contains the generic associated type `CallRefFuture` - = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `AsyncFnMut` for this new enum and using it instead: - &F - &mut F - std::boxed::Box + = note: the trait is not dyn compatible because it contains the generic associated type `CallRefFuture` error: aborting due to 1 previous error diff --git a/tests/ui/async-await/coroutine-desc.stderr b/tests/ui/async-await/coroutine-desc.stderr index 01482a9cb1fb6..84a1a3166ad64 100644 --- a/tests/ui/async-await/coroutine-desc.stderr +++ b/tests/ui/async-await/coroutine-desc.stderr @@ -30,7 +30,6 @@ LL | fun(one(), two()); | | expected all arguments to be this future type because they need to match the type of this parameter | arguments to this function are incorrect | - = help: consider `await`ing on both `Future`s = note: distinct uses of `impl Trait` result in different opaque types note: function defined here --> $DIR/coroutine-desc.rs:7:4 diff --git a/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr b/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr index b60f6a0833867..ce023397db90a 100644 --- a/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr +++ b/tests/ui/async-await/dont-ice-for-type-mismatch-in-closure-in-async.stderr @@ -20,19 +20,15 @@ LL | true = note: expected enum `Option<()>` found type `bool` -error[E0271]: expected `{closure@dont-ice-for-type-mismatch-in-closure-in-async.rs:6:10}` to be a closure that returns `bool`, but it returns `Option<()>` - --> $DIR/dont-ice-for-type-mismatch-in-closure-in-async.rs:6:10 +error[E0271]: expected `{closure@dont-ice-for-type-mismatch-in-closure-in-async.rs:6:10}` to return `bool`, but it returns `Option<()>` + --> $DIR/dont-ice-for-type-mismatch-in-closure-in-async.rs:6:16 | -LL | call(|| -> Option<()> { - | _____----_^ - | | | - | | required by a bound introduced by this call -LL | | -LL | | if true { -LL | | false -... | -LL | | }) - | |_____^ expected `bool`, found `Option<()>` +LL | call(|| -> Option<()> { + | ---- ------^^^^^^^^^^ + | | | | + | | | expected `bool`, found `Option<()>` + | | this closure + | required by a bound introduced by this call | = note: expected type `bool` found enum `Option<()>` diff --git a/tests/ui/async-await/dont-suggest-missing-await.stderr b/tests/ui/async-await/dont-suggest-missing-await.stderr index 45a226c31f840..2ca52b2d5f53b 100644 --- a/tests/ui/async-await/dont-suggest-missing-await.stderr +++ b/tests/ui/async-await/dont-suggest-missing-await.stderr @@ -6,20 +6,11 @@ LL | take_u32(x) | | | arguments to this function are incorrect | -note: calling an async function returns a future - --> $DIR/dont-suggest-missing-await.rs:14:18 - | -LL | take_u32(x) - | ^ note: function defined here --> $DIR/dont-suggest-missing-await.rs:5:4 | LL | fn take_u32(x: u32) {} | ^^^^^^^^ ------ -help: consider `await`ing on the `Future` - | -LL | take_u32(x.await) - | ++++++ error: aborting due to 1 previous error diff --git a/tests/ui/async-await/in-trait/async-example-desugared-boxed.stderr b/tests/ui/async-await/in-trait/async-example-desugared-boxed.stderr index 36f90c7bcd5fe..b7f2879727f24 100644 --- a/tests/ui/async-await/in-trait/async-example-desugared-boxed.stderr +++ b/tests/ui/async-await/in-trait/async-example-desugared-boxed.stderr @@ -17,8 +17,9 @@ LL | #[warn(refining_impl_trait)] = note: `#[warn(refining_impl_trait_reachable)]` implied by `#[warn(refining_impl_trait)]` help: replace the return type so that it matches the trait | -LL | fn foo(&self) -> impl Future { - | ~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - fn foo(&self) -> Pin + '_>> { +LL + fn foo(&self) -> impl Future { + | warning: 1 warning emitted diff --git a/tests/ui/async-await/in-trait/async-example-desugared-manual.stderr b/tests/ui/async-await/in-trait/async-example-desugared-manual.stderr index 8e39559071f16..86546df88c175 100644 --- a/tests/ui/async-await/in-trait/async-example-desugared-manual.stderr +++ b/tests/ui/async-await/in-trait/async-example-desugared-manual.stderr @@ -17,8 +17,9 @@ LL | #[warn(refining_impl_trait)] = note: `#[warn(refining_impl_trait_reachable)]` implied by `#[warn(refining_impl_trait)]` help: replace the return type so that it matches the trait | -LL | fn foo(&self) -> impl Future { - | ~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - fn foo(&self) -> MyFuture { +LL + fn foo(&self) -> impl Future { + | warning: 1 warning emitted diff --git a/tests/ui/async-await/in-trait/dyn-compatibility.rs b/tests/ui/async-await/in-trait/dyn-compatibility.rs index 8174a803e7951..c1b1ec797846b 100644 --- a/tests/ui/async-await/in-trait/dyn-compatibility.rs +++ b/tests/ui/async-await/in-trait/dyn-compatibility.rs @@ -7,5 +7,5 @@ trait Foo { fn main() { let x: &dyn Foo = todo!(); - //~^ ERROR the trait `Foo` cannot be made into an object + //~^ ERROR the trait `Foo` is not dyn compatible } diff --git a/tests/ui/async-await/in-trait/dyn-compatibility.stderr b/tests/ui/async-await/in-trait/dyn-compatibility.stderr index 5cc3b6800ddbc..553bcbf89d590 100644 --- a/tests/ui/async-await/in-trait/dyn-compatibility.stderr +++ b/tests/ui/async-await/in-trait/dyn-compatibility.stderr @@ -1,14 +1,15 @@ -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/dyn-compatibility.rs:9:12 | LL | let x: &dyn Foo = todo!(); - | ^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/dyn-compatibility.rs:5:14 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | async fn foo(&self); | ^^^ ...because method `foo` is `async` = help: consider moving `foo` to another trait diff --git a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs index aa8667a00ca30..b25d8a34aca1c 100644 --- a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs +++ b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs @@ -1,8 +1,5 @@ //@ edition: 2021 -// Test doesn't fail until monomorphization time, unfortunately. -//@ build-fail - fn main() { let _ = async { A.first().await.second().await; diff --git a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr index 8126c6e13942e..4ca6ef8981984 100644 --- a/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr +++ b/tests/ui/async-await/in-trait/indirect-recursion-issue-112047.stderr @@ -1,5 +1,5 @@ error[E0733]: recursion in an async fn requires boxing - --> $DIR/indirect-recursion-issue-112047.rs:34:5 + --> $DIR/indirect-recursion-issue-112047.rs:31:5 | LL | async fn second(self) { | ^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/async-await/incorrect-move-async-order-issue-79694.stderr b/tests/ui/async-await/incorrect-move-async-order-issue-79694.stderr index 782fa4295fd7f..2a3d46cc97f28 100644 --- a/tests/ui/async-await/incorrect-move-async-order-issue-79694.stderr +++ b/tests/ui/async-await/incorrect-move-async-order-issue-79694.stderr @@ -6,8 +6,9 @@ LL | let _ = move async { }; | help: try switching the order | -LL | let _ = async move { }; - | ~~~~~~~~~~ +LL - let _ = move async { }; +LL + let _ = async move { }; + | error: aborting due to 1 previous error diff --git a/tests/ui/async-await/inference_var_self_argument.rs b/tests/ui/async-await/inference_var_self_argument.rs index 4d5ac4abb199b..d03f2b5c50bff 100644 --- a/tests/ui/async-await/inference_var_self_argument.rs +++ b/tests/ui/async-await/inference_var_self_argument.rs @@ -3,7 +3,7 @@ trait Foo { async fn foo(self: &dyn Foo) { - //~^ ERROR: `Foo` cannot be made into an object + //~^ ERROR: `Foo` is not dyn compatible //~| ERROR invalid `self` parameter type: `&dyn Foo` todo!() } diff --git a/tests/ui/async-await/inference_var_self_argument.stderr b/tests/ui/async-await/inference_var_self_argument.stderr index 7b7b3dbc757f1..1fccc32470ff6 100644 --- a/tests/ui/async-await/inference_var_self_argument.stderr +++ b/tests/ui/async-await/inference_var_self_argument.stderr @@ -7,17 +7,18 @@ LL | async fn foo(self: &dyn Foo) { = note: type of `self` must be `Self` or a type that dereferences to it = help: consider changing to `self`, `&self`, `&mut self`, `self: Box`, `self: Rc`, `self: Arc`, or `self: Pin

` (where P is one of the previous types except `Self`) -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/inference_var_self_argument.rs:5:5 | LL | async fn foo(self: &dyn Foo) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/inference_var_self_argument.rs:5:14 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | async fn foo(self: &dyn Foo) { | ^^^ ...because method `foo` is `async` = help: consider moving `foo` to another trait diff --git a/tests/ui/async-await/issue-65634-raw-ident-suggestion.edition2015.stderr b/tests/ui/async-await/issue-65634-raw-ident-suggestion.edition2015.stderr index d4266814a7c47..2bdc1347c815d 100644 --- a/tests/ui/async-await/issue-65634-raw-ident-suggestion.edition2015.stderr +++ b/tests/ui/async-await/issue-65634-raw-ident-suggestion.edition2015.stderr @@ -16,12 +16,14 @@ LL | fn r#struct(&self) { | ^^^^^^^^^^^^^^^^^^ help: disambiguate the method for candidate #1 | -LL | async::r#struct(&r#fn {}); - | ~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - r#fn {}.r#struct(); +LL + async::r#struct(&r#fn {}); + | help: disambiguate the method for candidate #2 | -LL | await::r#struct(&r#fn {}); - | ~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - r#fn {}.r#struct(); +LL + await::r#struct(&r#fn {}); + | error: aborting due to 1 previous error diff --git a/tests/ui/async-await/issue-65634-raw-ident-suggestion.edition2018.stderr b/tests/ui/async-await/issue-65634-raw-ident-suggestion.edition2018.stderr index fe104bfe44544..ab10ab749fedf 100644 --- a/tests/ui/async-await/issue-65634-raw-ident-suggestion.edition2018.stderr +++ b/tests/ui/async-await/issue-65634-raw-ident-suggestion.edition2018.stderr @@ -16,12 +16,14 @@ LL | fn r#struct(&self) { | ^^^^^^^^^^^^^^^^^^ help: disambiguate the method for candidate #1 | -LL | r#async::r#struct(&r#fn {}); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - r#fn {}.r#struct(); +LL + r#async::r#struct(&r#fn {}); + | help: disambiguate the method for candidate #2 | -LL | r#await::r#struct(&r#fn {}); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - r#fn {}.r#struct(); +LL + r#await::r#struct(&r#fn {}); + | error: aborting due to 1 previous error diff --git a/tests/ui/async-await/issue-68523-start.rs b/tests/ui/async-await/issue-68523-start.rs deleted file mode 100644 index ee3baf4990c39..0000000000000 --- a/tests/ui/async-await/issue-68523-start.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ edition:2018 - -#![feature(start)] - -#[start] -pub async fn start(_: isize, _: *const *const u8) -> isize { -//~^ ERROR `#[start]` function is not allowed to be `async` - 0 -} diff --git a/tests/ui/async-await/issue-68523-start.stderr b/tests/ui/async-await/issue-68523-start.stderr deleted file mode 100644 index 5b76ab56e2459..0000000000000 --- a/tests/ui/async-await/issue-68523-start.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0752]: `#[start]` function is not allowed to be `async` - --> $DIR/issue-68523-start.rs:6:1 - | -LL | pub async fn start(_: isize, _: *const *const u8) -> isize { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[start]` is not allowed to be `async` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0752`. diff --git a/tests/ui/async-await/issue-98634.rs b/tests/ui/async-await/issue-98634.rs index 02e869f4325f3..3bfc2bf8a7ca7 100644 --- a/tests/ui/async-await/issue-98634.rs +++ b/tests/ui/async-await/issue-98634.rs @@ -43,8 +43,8 @@ impl Runtime { fn main() { Runtime.block_on(async { StructAsync { callback }.await; - //~^ ERROR expected `callback` to be a fn item that returns `Pin>>`, but it returns `impl Future` - //~| ERROR expected `callback` to be a fn item that returns `Pin>>`, but it returns `impl Future` - //~| ERROR expected `callback` to be a fn item that returns `Pin>>`, but it returns `impl Future` + //~^ ERROR expected `callback` to return `Pin>>`, but it returns `impl Future` + //~| ERROR expected `callback` to return `Pin>>`, but it returns `impl Future` + //~| ERROR expected `callback` to return `Pin>>`, but it returns `impl Future` }); } diff --git a/tests/ui/async-await/issue-98634.stderr b/tests/ui/async-await/issue-98634.stderr index 574904ceafada..e0739ae3a9b87 100644 --- a/tests/ui/async-await/issue-98634.stderr +++ b/tests/ui/async-await/issue-98634.stderr @@ -1,4 +1,4 @@ -error[E0271]: expected `callback` to be a fn item that returns `Pin>>`, but it returns `impl Future` +error[E0271]: expected `callback` to return `Pin>>`, but it returns `impl Future` --> $DIR/issue-98634.rs:45:23 | LL | StructAsync { callback }.await; @@ -10,7 +10,7 @@ note: required by a bound in `StructAsync` LL | pub struct StructAsync Pin>>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `StructAsync` -error[E0271]: expected `callback` to be a fn item that returns `Pin>>`, but it returns `impl Future` +error[E0271]: expected `callback` to return `Pin>>`, but it returns `impl Future` --> $DIR/issue-98634.rs:45:9 | LL | StructAsync { callback }.await; @@ -22,7 +22,7 @@ note: required by a bound in `StructAsync` LL | pub struct StructAsync Pin>>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `StructAsync` -error[E0271]: expected `callback` to be a fn item that returns `Pin>>`, but it returns `impl Future` +error[E0271]: expected `callback` to return `Pin>>`, but it returns `impl Future` --> $DIR/issue-98634.rs:45:34 | LL | StructAsync { callback }.await; diff --git a/tests/ui/async-await/issues/issue-63388-1.rs b/tests/ui/async-await/issues/issue-63388-1.rs index a6f499ba94e29..acfc64baff97d 100644 --- a/tests/ui/async-await/issues/issue-63388-1.rs +++ b/tests/ui/async-await/issues/issue-63388-1.rs @@ -11,8 +11,8 @@ impl Xyz { &'a self, foo: &dyn Foo ) -> &dyn Foo //~ WARNING elided lifetime has a name { - //~^ ERROR explicit lifetime required in the type of `foo` [E0621] foo + //~^ ERROR explicit lifetime required in the type of `foo` [E0621] } } diff --git a/tests/ui/async-await/issues/issue-63388-1.stderr b/tests/ui/async-await/issues/issue-63388-1.stderr index ef74bfe32375e..579caa45bc945 100644 --- a/tests/ui/async-await/issues/issue-63388-1.stderr +++ b/tests/ui/async-await/issues/issue-63388-1.stderr @@ -10,16 +10,13 @@ LL | ) -> &dyn Foo = note: `#[warn(elided_named_lifetimes)]` on by default error[E0621]: explicit lifetime required in the type of `foo` - --> $DIR/issue-63388-1.rs:13:5 + --> $DIR/issue-63388-1.rs:14:9 | -LL | &'a self, foo: &dyn Foo - | -------- help: add explicit lifetime `'a` to the type of `foo`: `&'a (dyn Foo + 'a)` -LL | ) -> &dyn Foo -LL | / { -LL | | -LL | | foo -LL | | } - | |_____^ lifetime `'a` required +LL | &'a self, foo: &dyn Foo + | -------- help: add explicit lifetime `'a` to the type of `foo`: `&'a (dyn Foo + 'a)` +... +LL | foo + | ^^^ lifetime `'a` required error: aborting due to 1 previous error; 1 warning emitted diff --git a/tests/ui/async-await/issues/issue-63388-2.rs b/tests/ui/async-await/issues/issue-63388-2.rs index 85718f4112151..8bb5cfa4a594a 100644 --- a/tests/ui/async-await/issues/issue-63388-2.rs +++ b/tests/ui/async-await/issues/issue-63388-2.rs @@ -11,8 +11,8 @@ impl Xyz { foo: &dyn Foo, bar: &'a dyn Foo ) -> &dyn Foo //~ ERROR missing lifetime specifier { - //~^ ERROR explicit lifetime required in the type of `foo` [E0621] foo + //~^ ERROR explicit lifetime required in the type of `foo` [E0621] } } diff --git a/tests/ui/async-await/issues/issue-63388-2.stderr b/tests/ui/async-await/issues/issue-63388-2.stderr index e515f227c7ef6..7e3c0a1227de8 100644 --- a/tests/ui/async-await/issues/issue-63388-2.stderr +++ b/tests/ui/async-await/issues/issue-63388-2.stderr @@ -13,16 +13,13 @@ LL | ) -> &'a dyn Foo | ++ error[E0621]: explicit lifetime required in the type of `foo` - --> $DIR/issue-63388-2.rs:13:5 + --> $DIR/issue-63388-2.rs:14:9 | -LL | foo: &dyn Foo, bar: &'a dyn Foo - | -------- help: add explicit lifetime `'a` to the type of `foo`: `&'a (dyn Foo + 'a)` -LL | ) -> &dyn Foo -LL | / { -LL | | -LL | | foo -LL | | } - | |_____^ lifetime `'a` required +LL | foo: &dyn Foo, bar: &'a dyn Foo + | -------- help: add explicit lifetime `'a` to the type of `foo`: `&'a (dyn Foo + 'a)` +... +LL | foo + | ^^^ lifetime `'a` required error: aborting due to 2 previous errors diff --git a/tests/ui/async-await/issues/issue-95307.rs b/tests/ui/async-await/issues/issue-95307.rs index 40700c610f33b..27903a667fb5c 100644 --- a/tests/ui/async-await/issues/issue-95307.rs +++ b/tests/ui/async-await/issues/issue-95307.rs @@ -5,8 +5,8 @@ pub trait C { async fn new() -> [u8; _]; - //~^ ERROR: using `_` for array lengths is unstable - //~| ERROR: in expressions, `_` can only be used on the left-hand side of an assignment + //~^ ERROR: the placeholder `_` is not allowed within types on item signatures for functions + //~| ERROR using `_` for array lengths is unstable } fn main() {} diff --git a/tests/ui/async-await/issues/issue-95307.stderr b/tests/ui/async-await/issues/issue-95307.stderr index dd8fcd3690a71..90100f391637a 100644 --- a/tests/ui/async-await/issues/issue-95307.stderr +++ b/tests/ui/async-await/issues/issue-95307.stderr @@ -1,8 +1,8 @@ -error: in expressions, `_` can only be used on the left-hand side of an assignment +error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/issue-95307.rs:7:28 | LL | async fn new() -> [u8; _]; - | ^ `_` not allowed here + | ^ not allowed in type signatures error[E0658]: using `_` for array lengths is unstable --> $DIR/issue-95307.rs:7:28 @@ -16,4 +16,5 @@ LL | async fn new() -> [u8; _]; error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0658`. +Some errors have detailed explanations: E0121, E0658. +For more information about an error, try `rustc --explain E0121`. diff --git a/tests/ui/async-await/pin-ergonomics/sugar-no-const.stderr b/tests/ui/async-await/pin-ergonomics/sugar-no-const.stderr index 822cfffcb8c62..062b6d3f4871a 100644 --- a/tests/ui/async-await/pin-ergonomics/sugar-no-const.stderr +++ b/tests/ui/async-await/pin-ergonomics/sugar-no-const.stderr @@ -8,8 +8,9 @@ LL | let _x: &pin i32 = todo!(); | help: there is a keyword `in` with a similar name | -LL | let _x: &in i32 = todo!(); - | ~~ +LL - let _x: &pin i32 = todo!(); +LL + let _x: &in i32 = todo!(); + | error: aborting due to 1 previous error diff --git a/tests/ui/async-await/return-type-notation/issue-110963-late.next.stderr b/tests/ui/async-await/return-type-notation/issue-110963-late.next.stderr deleted file mode 100644 index 018f4f2207ae7..0000000000000 --- a/tests/ui/async-await/return-type-notation/issue-110963-late.next.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-110963-late.rs:6:12 - | -LL | #![feature(return_type_notation)] - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #109417 for more information - = note: `#[warn(incomplete_features)]` on by default - -warning: 1 warning emitted - diff --git a/tests/ui/async-await/return-type-notation/super-method-bound.next.stderr b/tests/ui/async-await/return-type-notation/super-method-bound.next.stderr deleted file mode 100644 index 5f482b6087865..0000000000000 --- a/tests/ui/async-await/return-type-notation/super-method-bound.next.stderr +++ /dev/null @@ -1,10 +0,0 @@ -warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/super-method-bound.rs:6:31 - | -LL | #![feature(return_type_notation)] | ^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #109417 for more information - = note: `#[warn(incomplete_features)]` on by default - -warning: 1 warning emitted - diff --git a/tests/ui/async-await/suggest-switching-edition-on-await-cargo.stderr b/tests/ui/async-await/suggest-switching-edition-on-await-cargo.stderr index c8bbb3a243cf4..11f5825c232ea 100644 --- a/tests/ui/async-await/suggest-switching-edition-on-await-cargo.stderr +++ b/tests/ui/async-await/suggest-switching-edition-on-await-cargo.stderr @@ -19,8 +19,9 @@ LL | x.await; = note: for more on editions, read https://doc.rust-lang.org/edition-guide help: a field with a similar name exists | -LL | x.awai; - | ~~~~ +LL - x.await; +LL + x.awai; + | error[E0609]: no field `await` on type `Pin<&mut dyn Future>` --> $DIR/suggest-switching-edition-on-await-cargo.rs:34:7 diff --git a/tests/ui/async-await/suggest-switching-edition-on-await.stderr b/tests/ui/async-await/suggest-switching-edition-on-await.stderr index ef5a5f8126988..2ede8d5b7f42e 100644 --- a/tests/ui/async-await/suggest-switching-edition-on-await.stderr +++ b/tests/ui/async-await/suggest-switching-edition-on-await.stderr @@ -19,8 +19,9 @@ LL | x.await; = note: for more on editions, read https://doc.rust-lang.org/edition-guide help: a field with a similar name exists | -LL | x.awai; - | ~~~~ +LL - x.await; +LL + x.awai; + | error[E0609]: no field `await` on type `Pin<&mut dyn Future>` --> $DIR/suggest-switching-edition-on-await.rs:32:7 diff --git a/tests/ui/async-await/unconstrained-lifetimes.rs b/tests/ui/async-await/unconstrained-lifetimes.rs new file mode 100644 index 0000000000000..50ab7bae73d4f --- /dev/null +++ b/tests/ui/async-await/unconstrained-lifetimes.rs @@ -0,0 +1,9 @@ +//@ edition: 2021 + +// Make sure we don't complain about the implicit `-> impl Future` capturing an +// unconstrained anonymous lifetime. + +async fn foo(_: Missing<'_>) {} +//~^ ERROR cannot find type `Missing` in this scope + +fn main() {} diff --git a/tests/ui/async-await/unconstrained-lifetimes.stderr b/tests/ui/async-await/unconstrained-lifetimes.stderr new file mode 100644 index 0000000000000..18a46a3b55518 --- /dev/null +++ b/tests/ui/async-await/unconstrained-lifetimes.stderr @@ -0,0 +1,9 @@ +error[E0412]: cannot find type `Missing` in this scope + --> $DIR/unconstrained-lifetimes.rs:6:17 + | +LL | async fn foo(_: Missing<'_>) {} + | ^^^^^^^ not found in this scope + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0412`. diff --git a/tests/ui/attr-start.rs b/tests/ui/attr-start.rs deleted file mode 100644 index 232f50955b2ee..0000000000000 --- a/tests/ui/attr-start.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ run-pass - -#![feature(start)] - -#[start] -fn start(_argc: isize, _argv: *const *const u8) -> isize { - return 0; -} diff --git a/tests/ui/attributes/check-builtin-attr-ice.stderr b/tests/ui/attributes/check-builtin-attr-ice.stderr index 5a27da565a857..06a4769b2b421 100644 --- a/tests/ui/attributes/check-builtin-attr-ice.stderr +++ b/tests/ui/attributes/check-builtin-attr-ice.stderr @@ -1,20 +1,20 @@ -error[E0433]: failed to resolve: use of undeclared crate or module `should_panic` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `should_panic` --> $DIR/check-builtin-attr-ice.rs:43:7 | LL | #[should_panic::skip] - | ^^^^^^^^^^^^ use of undeclared crate or module `should_panic` + | ^^^^^^^^^^^^ use of unresolved module or unlinked crate `should_panic` -error[E0433]: failed to resolve: use of undeclared crate or module `should_panic` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `should_panic` --> $DIR/check-builtin-attr-ice.rs:47:7 | LL | #[should_panic::a::b::c] - | ^^^^^^^^^^^^ use of undeclared crate or module `should_panic` + | ^^^^^^^^^^^^ use of unresolved module or unlinked crate `should_panic` -error[E0433]: failed to resolve: use of undeclared crate or module `deny` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `deny` --> $DIR/check-builtin-attr-ice.rs:55:7 | LL | #[deny::skip] - | ^^^^ use of undeclared crate or module `deny` + | ^^^^ use of unresolved module or unlinked crate `deny` error: aborting due to 3 previous errors diff --git a/tests/ui/attributes/check-cfg_attr-ice.stderr b/tests/ui/attributes/check-cfg_attr-ice.stderr index dbdf32597f7b2..bed3150bdc2f3 100644 --- a/tests/ui/attributes/check-cfg_attr-ice.stderr +++ b/tests/ui/attributes/check-cfg_attr-ice.stderr @@ -17,83 +17,83 @@ LL | #[cfg_attr::no_such_thing] = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `cfg_attr` --> $DIR/check-cfg_attr-ice.rs:52:3 | LL | #[cfg_attr::no_such_thing] - | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + | ^^^^^^^^ use of unresolved module or unlinked crate `cfg_attr` -error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `cfg_attr` --> $DIR/check-cfg_attr-ice.rs:55:7 | LL | #[cfg_attr::no_such_thing] - | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + | ^^^^^^^^ use of unresolved module or unlinked crate `cfg_attr` -error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `cfg_attr` --> $DIR/check-cfg_attr-ice.rs:57:17 | LL | GiveYouUp(#[cfg_attr::no_such_thing] u8), - | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + | ^^^^^^^^ use of unresolved module or unlinked crate `cfg_attr` -error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `cfg_attr` --> $DIR/check-cfg_attr-ice.rs:64:11 | LL | #[cfg_attr::no_such_thing] - | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + | ^^^^^^^^ use of unresolved module or unlinked crate `cfg_attr` -error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `cfg_attr` --> $DIR/check-cfg_attr-ice.rs:41:7 | LL | #[cfg_attr::no_such_thing] - | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + | ^^^^^^^^ use of unresolved module or unlinked crate `cfg_attr` -error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `cfg_attr` --> $DIR/check-cfg_attr-ice.rs:43:15 | LL | fn from(#[cfg_attr::no_such_thing] any_other_guy: AnyOtherGuy) -> This { - | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + | ^^^^^^^^ use of unresolved module or unlinked crate `cfg_attr` -error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `cfg_attr` --> $DIR/check-cfg_attr-ice.rs:45:11 | LL | #[cfg_attr::no_such_thing] - | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + | ^^^^^^^^ use of unresolved module or unlinked crate `cfg_attr` -error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `cfg_attr` --> $DIR/check-cfg_attr-ice.rs:32:3 | LL | #[cfg_attr::no_such_thing] - | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + | ^^^^^^^^ use of unresolved module or unlinked crate `cfg_attr` -error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `cfg_attr` --> $DIR/check-cfg_attr-ice.rs:24:3 | LL | #[cfg_attr::no_such_thing] - | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + | ^^^^^^^^ use of unresolved module or unlinked crate `cfg_attr` -error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `cfg_attr` --> $DIR/check-cfg_attr-ice.rs:27:7 | LL | #[cfg_attr::no_such_thing] - | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + | ^^^^^^^^ use of unresolved module or unlinked crate `cfg_attr` -error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `cfg_attr` --> $DIR/check-cfg_attr-ice.rs:16:3 | LL | #[cfg_attr::no_such_thing] - | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + | ^^^^^^^^ use of unresolved module or unlinked crate `cfg_attr` -error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `cfg_attr` --> $DIR/check-cfg_attr-ice.rs:19:7 | LL | #[cfg_attr::no_such_thing] - | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + | ^^^^^^^^ use of unresolved module or unlinked crate `cfg_attr` -error[E0433]: failed to resolve: use of undeclared crate or module `cfg_attr` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `cfg_attr` --> $DIR/check-cfg_attr-ice.rs:12:3 | LL | #[cfg_attr::no_such_thing] - | ^^^^^^^^ use of undeclared crate or module `cfg_attr` + | ^^^^^^^^ use of unresolved module or unlinked crate `cfg_attr` error: aborting due to 15 previous errors diff --git a/tests/ui/attributes/field-attributes-vis-unresolved.stderr b/tests/ui/attributes/field-attributes-vis-unresolved.stderr index f8610c08b0220..d689b76eaf8b0 100644 --- a/tests/ui/attributes/field-attributes-vis-unresolved.stderr +++ b/tests/ui/attributes/field-attributes-vis-unresolved.stderr @@ -1,21 +1,21 @@ -error[E0433]: failed to resolve: you might be missing crate `nonexistent` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `nonexistent` --> $DIR/field-attributes-vis-unresolved.rs:17:12 | LL | pub(in nonexistent) field: u8 - | ^^^^^^^^^^^ you might be missing crate `nonexistent` + | ^^^^^^^^^^^ use of unresolved module or unlinked crate `nonexistent` | -help: consider importing the `nonexistent` crate +help: you might be missing a crate named `nonexistent`, add it to your project and import it in your code | LL + extern crate nonexistent; | -error[E0433]: failed to resolve: you might be missing crate `nonexistent` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `nonexistent` --> $DIR/field-attributes-vis-unresolved.rs:22:12 | LL | pub(in nonexistent) u8 - | ^^^^^^^^^^^ you might be missing crate `nonexistent` + | ^^^^^^^^^^^ use of unresolved module or unlinked crate `nonexistent` | -help: consider importing the `nonexistent` crate +help: you might be missing a crate named `nonexistent`, add it to your project and import it in your code | LL + extern crate nonexistent; | diff --git a/tests/ui/attributes/key-value-expansion.rs b/tests/ui/attributes/key-value-expansion.rs index e5700a7593562..9034e0a739823 100644 --- a/tests/ui/attributes/key-value-expansion.rs +++ b/tests/ui/attributes/key-value-expansion.rs @@ -39,7 +39,7 @@ bug!(); macro_rules! doc_comment { ($x:expr) => { #[doc = $x] - extern {} + extern "C" {} }; } diff --git a/tests/ui/attributes/key-value-non-ascii.stderr b/tests/ui/attributes/key-value-non-ascii.stderr index cc01bc46ebd29..fa87ad5ee6d21 100644 --- a/tests/ui/attributes/key-value-non-ascii.stderr +++ b/tests/ui/attributes/key-value-non-ascii.stderr @@ -6,8 +6,9 @@ LL | #[rustc_dummy = b"ffi.rs"] | help: if you meant to use the UTF-8 encoding of 'ffi', use \xHH escapes | -LL | #[rustc_dummy = b"/xEF/xAC/x83.rs"] - | ~~~~~~~~~~~~ +LL - #[rustc_dummy = b"ffi.rs"] +LL + #[rustc_dummy = b"/xEF/xAC/x83.rs"] + | error: aborting due to 1 previous error diff --git a/tests/ui/attributes/rustc-box.rs b/tests/ui/attributes/rustc-box.rs deleted file mode 100644 index b3726fb38671b..0000000000000 --- a/tests/ui/attributes/rustc-box.rs +++ /dev/null @@ -1,18 +0,0 @@ -#![feature(rustc_attrs, stmt_expr_attributes)] - -fn foo(_: u32, _: u32) {} -fn bar(_: u32) {} - -fn main() { - #[rustc_box] - Box::new(1); // OK - #[rustc_box] - Box::pin(1); //~ ERROR `#[rustc_box]` attribute used incorrectly - #[rustc_box] - foo(1, 1); //~ ERROR `#[rustc_box]` attribute used incorrectly - #[rustc_box] - bar(1); //~ ERROR `#[rustc_box]` attribute used incorrectly - #[rustc_box] //~ ERROR `#[rustc_box]` attribute used incorrectly - #[rustfmt::skip] - Box::new(1); -} diff --git a/tests/ui/attributes/rustc-box.stderr b/tests/ui/attributes/rustc-box.stderr deleted file mode 100644 index 073a18c7d58ec..0000000000000 --- a/tests/ui/attributes/rustc-box.stderr +++ /dev/null @@ -1,34 +0,0 @@ -error: `#[rustc_box]` attribute used incorrectly - --> $DIR/rustc-box.rs:10:5 - | -LL | Box::pin(1); - | ^^^^^^^^^^^ - | - = note: `#[rustc_box]` may only be applied to a `Box::new()` call - -error: `#[rustc_box]` attribute used incorrectly - --> $DIR/rustc-box.rs:12:5 - | -LL | foo(1, 1); - | ^^^^^^^^^ - | - = note: `#[rustc_box]` may only be applied to a `Box::new()` call - -error: `#[rustc_box]` attribute used incorrectly - --> $DIR/rustc-box.rs:14:5 - | -LL | bar(1); - | ^^^^^^ - | - = note: `#[rustc_box]` may only be applied to a `Box::new()` call - -error: `#[rustc_box]` attribute used incorrectly - --> $DIR/rustc-box.rs:15:5 - | -LL | #[rustc_box] - | ^^^^^^^^^^^^ - | - = note: no other attributes may be applied - -error: aborting due to 4 previous errors - diff --git a/tests/ui/attributes/rustc_confusables.stderr b/tests/ui/attributes/rustc_confusables.stderr index 9e37d5f50837d..dc71d974daf56 100644 --- a/tests/ui/attributes/rustc_confusables.stderr +++ b/tests/ui/attributes/rustc_confusables.stderr @@ -36,7 +36,7 @@ LL | x.inser(); help: there is a method `insert` with a similar name | LL | x.insert(); - | ~~~~~~ + | + error[E0599]: no method named `foo` found for struct `rustc_confusables_across_crate::BTreeSet` in the current scope --> $DIR/rustc_confusables.rs:15:7 @@ -52,8 +52,9 @@ LL | x.push(); | help: you might have meant to use `insert` | -LL | x.insert(); - | ~~~~~~ +LL - x.push(); +LL + x.insert(); + | error[E0599]: no method named `test` found for struct `rustc_confusables_across_crate::BTreeSet` in the current scope --> $DIR/rustc_confusables.rs:20:7 @@ -69,8 +70,9 @@ LL | x.pulled(); | help: you might have meant to use `pull` | -LL | x.pull(); - | ~~~~ +LL - x.pulled(); +LL + x.pull(); + | error: aborting due to 9 previous errors diff --git a/tests/ui/attributes/rustc_confusables_std_cases.stderr b/tests/ui/attributes/rustc_confusables_std_cases.stderr index 7bf96241ca725..f2d9ebe2c0eae 100644 --- a/tests/ui/attributes/rustc_confusables_std_cases.stderr +++ b/tests/ui/attributes/rustc_confusables_std_cases.stderr @@ -6,8 +6,9 @@ LL | x.push(1); | help: you might have meant to use `insert` | -LL | x.insert(1); - | ~~~~~~ +LL - x.push(1); +LL + x.insert(1); + | error[E0599]: no method named `push_back` found for struct `Vec<_>` in the current scope --> $DIR/rustc_confusables_std_cases.rs:9:7 @@ -17,8 +18,9 @@ LL | x.push_back(1); | help: you might have meant to use `push` | -LL | x.push(1); - | ~~~~ +LL - x.push_back(1); +LL + x.push(1); + | error[E0599]: no method named `push` found for struct `VecDeque` in the current scope --> $DIR/rustc_confusables_std_cases.rs:12:7 @@ -37,7 +39,7 @@ LL | let mut x = VecDeque::new(); help: you might have meant to use `push_back` | LL | x.push_back(1); - | ~~~~~~~~~ + | +++++ error[E0599]: no method named `length` found for struct `Vec<{integer}>` in the current scope --> $DIR/rustc_confusables_std_cases.rs:15:7 @@ -47,8 +49,9 @@ LL | x.length(); | help: you might have meant to use `len` | -LL | x.len(); - | ~~~ +LL - x.length(); +LL + x.len(); + | error[E0599]: no method named `size` found for struct `Vec<{integer}>` in the current scope --> $DIR/rustc_confusables_std_cases.rs:17:7 @@ -60,8 +63,9 @@ help: there is a method `resize` with a similar name, but with different argumen --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL help: you might have meant to use `len` | -LL | x.len(); - | ~~~ +LL - x.size(); +LL + x.len(); + | error[E0308]: mismatched types --> $DIR/rustc_confusables_std_cases.rs:20:14 @@ -77,8 +81,9 @@ note: method defined here --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL help: you might have meant to use `push` | -LL | x.push(42); - | ~~~~ +LL - x.append(42); +LL + x.push(42); + | error[E0308]: mismatched types --> $DIR/rustc_confusables_std_cases.rs:22:24 @@ -93,7 +98,7 @@ note: method defined here help: you might have meant to use `push_str` | LL | String::new().push_str(""); - | ~~~~~~~~ + | ++++ error[E0599]: no method named `append` found for struct `String` in the current scope --> $DIR/rustc_confusables_std_cases.rs:24:19 @@ -103,8 +108,9 @@ LL | String::new().append(""); | help: you might have meant to use `push_str` | -LL | String::new().push_str(""); - | ~~~~~~~~ +LL - String::new().append(""); +LL + String::new().push_str(""); + | error[E0599]: no method named `get_line` found for struct `Stdin` in the current scope --> $DIR/rustc_confusables_std_cases.rs:28:11 @@ -114,8 +120,9 @@ LL | stdin.get_line(&mut buffer).unwrap(); | help: you might have meant to use `read_line` | -LL | stdin.read_line(&mut buffer).unwrap(); - | ~~~~~~~~~ +LL - stdin.get_line(&mut buffer).unwrap(); +LL + stdin.read_line(&mut buffer).unwrap(); + | error: aborting due to 9 previous errors diff --git a/tests/ui/auto-traits/assoc-ty.next.stderr b/tests/ui/auto-traits/assoc-ty.next.stderr index b9f56d6c99c4c..4ce00d1747564 100644 --- a/tests/ui/auto-traits/assoc-ty.next.stderr +++ b/tests/ui/auto-traits/assoc-ty.next.stderr @@ -21,20 +21,21 @@ LL | | } = help: add `#![feature(auto_traits)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0308]: mismatched types - --> $DIR/assoc-ty.rs:15:36 +error[E0271]: type mismatch resolving `<() as Trait>::Output normalizes-to _` + --> $DIR/assoc-ty.rs:15:12 | LL | let _: <() as Trait>::Output = (); - | --------------------- ^^ types differ - | | - | expected due to this + | ^^^^^^^^^^^^^^^^^^^^^ types differ + +error[E0271]: type mismatch resolving `<() as Trait>::Output normalizes-to _` + --> $DIR/assoc-ty.rs:15:12 + | +LL | let _: <() as Trait>::Output = (); + | ^^^^^^^^^^^^^^^^^^^^^ types differ | - = note: expected associated type `<() as Trait>::Output` - found unit type `()` - = help: consider constraining the associated type `<() as Trait>::Output` to `()` or calling a method that returns `<() as Trait>::Output` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0308, E0380, E0658. -For more information about an error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0271, E0380, E0658. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/auto-traits/assoc-ty.rs b/tests/ui/auto-traits/assoc-ty.rs index ada75147f6ea6..efbfead9cd037 100644 --- a/tests/ui/auto-traits/assoc-ty.rs +++ b/tests/ui/auto-traits/assoc-ty.rs @@ -13,5 +13,7 @@ auto trait Trait { fn main() { let _: <() as Trait>::Output = (); - //~^ ERROR mismatched types + //[current]~^ ERROR mismatched types + //[next]~^^ ERROR type mismatch resolving `<() as Trait>::Output normalizes-to _` + //[next]~| ERROR type mismatch resolving `<() as Trait>::Output normalizes-to _` } diff --git a/tests/ui/auto-traits/issue-83857-ub.stderr b/tests/ui/auto-traits/issue-83857-ub.stderr index 7c437b7e6c84d..3536450c75b12 100644 --- a/tests/ui/auto-traits/issue-83857-ub.stderr +++ b/tests/ui/auto-traits/issue-83857-ub.stderr @@ -18,16 +18,10 @@ LL | fn generic(v: Foo, f: fn( as WithAssoc>::Output) -> i | +++++++++++++++++++++ error[E0277]: `Foo` cannot be sent between threads safely - --> $DIR/issue-83857-ub.rs:21:80 + --> $DIR/issue-83857-ub.rs:21:35 | -LL | fn generic(v: Foo, f: fn( as WithAssoc>::Output) -> i32) { - | ________________________________________________________________________________^ -LL | | -LL | | -LL | | f(foo(v)); -LL | | -LL | | } - | |_^ `Foo` cannot be sent between threads safely +LL | fn generic(v: Foo, f: fn( as WithAssoc>::Output) -> i32) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Foo` cannot be sent between threads safely | = help: the trait `Send` is not implemented for `Foo` note: required for `Foo` to implement `WithAssoc` diff --git a/tests/ui/backtrace/backtrace.rs b/tests/ui/backtrace/backtrace.rs index 2579ff5203b4f..487473f4393b5 100644 --- a/tests/ui/backtrace/backtrace.rs +++ b/tests/ui/backtrace/backtrace.rs @@ -1,8 +1,7 @@ //@ run-pass //@ ignore-android FIXME #17520 -//@ ignore-wasm32 spawning processes is not supported +//@ needs-subprocess //@ ignore-openbsd no support for libbacktrace without filename -//@ ignore-sgx no processes //@ ignore-msvc see #62897 and `backtrace-debuginfo.rs` test //@ ignore-fuchsia Backtraces not symbolized //@ compile-flags:-g diff --git a/tests/ui/backtrace/std-backtrace.rs b/tests/ui/backtrace/std-backtrace.rs index 57d953a86408d..7ccbd46152bbf 100644 --- a/tests/ui/backtrace/std-backtrace.rs +++ b/tests/ui/backtrace/std-backtrace.rs @@ -1,8 +1,7 @@ //@ run-pass //@ ignore-android FIXME #17520 -//@ ignore-wasm32 spawning processes is not supported +//@ needs-subprocess //@ ignore-openbsd no support for libbacktrace without filename -//@ ignore-sgx no processes //@ ignore-fuchsia Backtraces not symbolized //@ compile-flags:-g //@ compile-flags:-Cstrip=none diff --git a/tests/ui/binding/expr-match-generic.rs b/tests/ui/binding/expr-match-generic.rs index 975eec42fd054..dcc069b9fb329 100644 --- a/tests/ui/binding/expr-match-generic.rs +++ b/tests/ui/binding/expr-match-generic.rs @@ -5,7 +5,7 @@ type compare = extern "Rust" fn(T, T) -> bool; fn test_generic(expected: T, eq: compare) { let actual: T = match true { true => { expected.clone() }, _ => panic!("wat") }; - assert!((eq(expected, actual))); + assert!(eq(expected, actual)); } fn test_bool() { diff --git a/tests/ui/binding/expr-match.rs b/tests/ui/binding/expr-match.rs index 049beaaf51bf6..ce502ae149033 100644 --- a/tests/ui/binding/expr-match.rs +++ b/tests/ui/binding/expr-match.rs @@ -1,20 +1,17 @@ //@ run-pass - - - // Tests for using match as an expression fn test_basic() { let mut rs: bool = match true { true => { true } false => { false } }; - assert!((rs)); + assert!(rs); rs = match false { true => { false } false => { true } }; - assert!((rs)); + assert!(rs); } fn test_inferrence() { let rs = match true { true => { true } false => { false } }; - assert!((rs)); + assert!(rs); } fn test_alt_as_alt_head() { @@ -25,7 +22,7 @@ fn test_alt_as_alt_head() { true => { false } false => { true } }; - assert!((rs)); + assert!(rs); } fn test_alt_as_block_result() { @@ -34,7 +31,7 @@ fn test_alt_as_block_result() { true => { false } false => { match true { true => { true } false => { false } } } }; - assert!((rs)); + assert!(rs); } pub fn main() { diff --git a/tests/ui/binop/binop-fail-3.rs b/tests/ui/binop/binop-fail-3.rs index b1e70a1c5961c..4e8d7e92ab6b0 100644 --- a/tests/ui/binop/binop-fail-3.rs +++ b/tests/ui/binop/binop-fail-3.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:quux -//@ ignore-emscripten no processes +//@ needs-subprocess fn foo() -> ! { panic!("quux"); diff --git a/tests/ui/binop/binop-panic.rs b/tests/ui/binop/binop-panic.rs index 8dbf62a922e45..8173eb0d6898f 100644 --- a/tests/ui/binop/binop-panic.rs +++ b/tests/ui/binop/binop-panic.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:quux -//@ ignore-emscripten no processes +//@ needs-subprocess fn my_err(s: String) -> ! { println!("{}", s); diff --git a/tests/ui/binop/binops.rs b/tests/ui/binop/binops.rs index 0adbb49b14a37..7142190a45b9a 100644 --- a/tests/ui/binop/binops.rs +++ b/tests/ui/binop/binops.rs @@ -5,23 +5,23 @@ fn test_nil() { assert_eq!((), ()); - assert!((!(() != ()))); - assert!((!(() < ()))); - assert!((() <= ())); - assert!((!(() > ()))); - assert!((() >= ())); + assert!(!(() != ())); + assert!(!(() < ())); + assert!(() <= ()); + assert!(!(() > ())); + assert!(() >= ()); } fn test_bool() { - assert!((!(true < false))); - assert!((!(true <= false))); - assert!((true > false)); - assert!((true >= false)); + assert!(!(true < false)); + assert!(!(true <= false)); + assert!(true > false); + assert!(true >= false); - assert!((false < true)); - assert!((false <= true)); - assert!((!(false > true))); - assert!((!(false >= true))); + assert!(false < true); + assert!(false <= true); + assert!(!(false > true)); + assert!(!(false >= true)); // Bools support bitwise binops assert_eq!(false & false, false); @@ -76,9 +76,9 @@ fn test_class() { } assert_eq!(q, r); r.y = 17; - assert!((r.y != q.y)); + assert!(r.y != q.y); assert_eq!(r.y, 17); - assert!((q != r)); + assert!(q != r); } pub fn main() { diff --git a/tests/ui/binop/placement-syntax.stderr b/tests/ui/binop/placement-syntax.stderr index b20a2ee63538b..e398c0b0702ab 100644 --- a/tests/ui/binop/placement-syntax.stderr +++ b/tests/ui/binop/placement-syntax.stderr @@ -6,8 +6,9 @@ LL | if x<-1 { | help: if you meant to write a comparison against a negative value, add a space in between `<` and `-` | -LL | if x< -1 { - | ~~~ +LL - if x<-1 { +LL + if x< -1 { + | error: aborting due to 1 previous error diff --git a/tests/ui/binop/structured-compare.rs b/tests/ui/binop/structured-compare.rs index 164760cd7a040..7d1eff453028a 100644 --- a/tests/ui/binop/structured-compare.rs +++ b/tests/ui/binop/structured-compare.rs @@ -17,14 +17,14 @@ pub fn main() { let a = (1, 2, 3); let b = (1, 2, 3); assert_eq!(a, b); - assert!((a != (1, 2, 4))); - assert!((a < (1, 2, 4))); - assert!((a <= (1, 2, 4))); - assert!(((1, 2, 4) > a)); - assert!(((1, 2, 4) >= a)); + assert!(a != (1, 2, 4)); + assert!(a < (1, 2, 4)); + assert!(a <= (1, 2, 4)); + assert!((1, 2, 4) > a); + assert!((1, 2, 4) >= a); let x = foo::large; let y = foo::small; - assert!((x != y)); + assert!(x != y); assert_eq!(x, foo::large); - assert!((x != foo::small)); + assert!(x != foo::small); } diff --git a/tests/ui/block-result/issue-3563.stderr b/tests/ui/block-result/issue-3563.stderr index 22606a52f851e..e03453f394647 100644 --- a/tests/ui/block-result/issue-3563.stderr +++ b/tests/ui/block-result/issue-3563.stderr @@ -6,8 +6,9 @@ LL | || self.b() | help: there is a method `a` with a similar name | -LL | || self.a() - | ~ +LL - || self.b() +LL + || self.a() + | error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/already-borrowed-as-mutable-if-let-133941.rs b/tests/ui/borrowck/already-borrowed-as-mutable-if-let-133941.rs new file mode 100644 index 0000000000000..934aac8383db1 --- /dev/null +++ b/tests/ui/borrowck/already-borrowed-as-mutable-if-let-133941.rs @@ -0,0 +1,46 @@ +// https://github.com/rust-lang/rust/issues/133941 +use std::marker::PhantomData; + +struct Bar<'a>(PhantomData<&'a mut i32>); + +impl<'a> Drop for Bar<'a> { + fn drop(&mut self) {} +} + +struct Foo(); + +impl Foo { + fn f(&mut self) -> Option> { + None + } + + fn g(&mut self) {} +} + +fn main() { + let mut foo = Foo(); + while let Some(_) = foo.f() { + //~^ HELP matches! + foo.g(); + //~^ ERROR [E0499] + } + if let Some(_) = foo.f() { + //~^ HELP matches! + foo.g(); + //~^ ERROR [E0499] + } + while let Some(_x) = foo.f() { + foo.g(); + //~^ ERROR [E0499] + } + if let Some(_x) = foo.f() { + foo.g(); + //~^ ERROR [E0499] + } + while let Some(_x) = {let _x = foo.f(); foo.g(); None::<()>} { + //~^ ERROR [E0499] + } + if let Some(_x) = {let _x = foo.f(); foo.g(); None::<()>} { + //~^ ERROR [E0499] + } +} diff --git a/tests/ui/borrowck/already-borrowed-as-mutable-if-let-133941.stderr b/tests/ui/borrowck/already-borrowed-as-mutable-if-let-133941.stderr new file mode 100644 index 0000000000000..2f443797dfa17 --- /dev/null +++ b/tests/ui/borrowck/already-borrowed-as-mutable-if-let-133941.stderr @@ -0,0 +1,91 @@ +error[E0499]: cannot borrow `foo` as mutable more than once at a time + --> $DIR/already-borrowed-as-mutable-if-let-133941.rs:24:9 + | +LL | while let Some(_) = foo.f() { + | ------- + | | + | first mutable borrow occurs here + | a temporary with access to the first borrow is created here ... +LL | +LL | foo.g(); + | ^^^ second mutable borrow occurs here +LL | +LL | } + | - ... and the first borrow might be used here, when that temporary is dropped and runs the destructor for type `Option>` + | +help: consider using the `matches!` macro + | +LL - while let Some(_) = foo.f() { +LL + while matches!(foo.f(), Some(_)) { + | + +error[E0499]: cannot borrow `foo` as mutable more than once at a time + --> $DIR/already-borrowed-as-mutable-if-let-133941.rs:29:9 + | +LL | if let Some(_) = foo.f() { + | ------- + | | + | first mutable borrow occurs here + | a temporary with access to the first borrow is created here ... +LL | +LL | foo.g(); + | ^^^ second mutable borrow occurs here +LL | +LL | } + | - ... and the first borrow might be used here, when that temporary is dropped and runs the destructor for type `Option>` + | +help: consider using the `matches!` macro + | +LL - if let Some(_) = foo.f() { +LL + if matches!(foo.f(), Some(_)) { + | + +error[E0499]: cannot borrow `foo` as mutable more than once at a time + --> $DIR/already-borrowed-as-mutable-if-let-133941.rs:33:9 + | +LL | while let Some(_x) = foo.f() { + | ------- + | | + | first mutable borrow occurs here + | a temporary with access to the first borrow is created here ... +LL | foo.g(); + | ^^^ second mutable borrow occurs here +LL | +LL | } + | - ... and the first borrow might be used here, when that temporary is dropped and runs the destructor for type `Option>` + +error[E0499]: cannot borrow `foo` as mutable more than once at a time + --> $DIR/already-borrowed-as-mutable-if-let-133941.rs:37:9 + | +LL | if let Some(_x) = foo.f() { + | ------- + | | + | first mutable borrow occurs here + | a temporary with access to the first borrow is created here ... +LL | foo.g(); + | ^^^ second mutable borrow occurs here +LL | +LL | } + | - ... and the first borrow might be used here, when that temporary is dropped and runs the destructor for type `Option>` + +error[E0499]: cannot borrow `foo` as mutable more than once at a time + --> $DIR/already-borrowed-as-mutable-if-let-133941.rs:40:45 + | +LL | while let Some(_x) = {let _x = foo.f(); foo.g(); None::<()>} { + | --- ^^^ - first borrow might be used here, when `_x` is dropped and runs the destructor for type `Option>` + | | | + | | second mutable borrow occurs here + | first mutable borrow occurs here + +error[E0499]: cannot borrow `foo` as mutable more than once at a time + --> $DIR/already-borrowed-as-mutable-if-let-133941.rs:43:42 + | +LL | if let Some(_x) = {let _x = foo.f(); foo.g(); None::<()>} { + | --- ^^^ - first borrow might be used here, when `_x` is dropped and runs the destructor for type `Option>` + | | | + | | second mutable borrow occurs here + | first mutable borrow occurs here + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/borrowck/array-disjoint-borrows-issue-135671.rs b/tests/ui/borrowck/array-disjoint-borrows-issue-135671.rs new file mode 100644 index 0000000000000..74b5cfcfb04de --- /dev/null +++ b/tests/ui/borrowck/array-disjoint-borrows-issue-135671.rs @@ -0,0 +1,30 @@ +// This is a regression test for issue #135671 where a MIR refactor about arrays and their lengths +// unexpectedly caused borrowck errors for disjoint borrows of array elements, for which we had no +// tests. This is a collection of a few code samples from that issue. + +//@ check-pass + +struct Test { + a: i32, + b: i32, +} + +fn one() { + let inputs: &mut [_] = &mut [Test { a: 0, b: 0 }]; + let a = &mut inputs[0].a; + let b = &mut inputs[0].b; + + *a = 0; + *b = 1; +} + +fn two() { + let slice = &mut [(0, 0)][..]; + std::mem::swap(&mut slice[0].0, &mut slice[0].1); +} + +fn three(a: &mut [(i32, i32)], i: usize, j: usize) -> (&mut i32, &mut i32) { + (&mut a[i].0, &mut a[j].1) +} + +fn main() {} diff --git a/tests/ui/borrowck/borrowck-local-borrow.rs b/tests/ui/borrowck/borrowck-local-borrow.rs index de6ee5983c864..4d22503e37bae 100644 --- a/tests/ui/borrowck/borrowck-local-borrow.rs +++ b/tests/ui/borrowck/borrowck-local-borrow.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:panic 1 -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { let x = 2; diff --git a/tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr b/tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr index bc11204acf288..d953ed2ad3ef0 100644 --- a/tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr +++ b/tests/ui/borrowck/borrowck-struct-update-with-dtor.stderr @@ -33,8 +33,9 @@ LL | struct B; | ^^^^^^^^ help: if `B` implemented `Clone`, you could clone the value from the field instead of using the functional record update syntax | -LL | let _s2 = S { a: 2, b: s0.b.clone(), c: s0.c.clone() }; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - let _s2 = S { a: 2, ..s0 }; +LL + let _s2 = S { a: 2, b: s0.b.clone(), c: s0.c.clone() }; + | error[E0509]: cannot move out of type `S`, which implements the `Drop` trait --> $DIR/borrowck-struct-update-with-dtor.rs:24:19 @@ -52,8 +53,9 @@ LL | struct B; | ^^^^^^^^ help: if `B` implemented `Clone`, you could clone the value from the field instead of using the functional record update syntax | -LL | let _s2 = S { a: 2, b: s0.b.clone(), c: s0.c.clone() }; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - let _s2 = S { a: 2, ..s0 }; +LL + let _s2 = S { a: 2, b: s0.b.clone(), c: s0.c.clone() }; + | error[E0509]: cannot move out of type `S`, which implements the `Drop` trait --> $DIR/borrowck-struct-update-with-dtor.rs:29:19 @@ -99,8 +101,9 @@ LL | let _s2 = T { a: 2, ..s0 }; | help: clone the value from the field instead of using the functional record update syntax | -LL | let _s2 = T { a: 2, b: s0.b.clone() }; - | ~~~~~~~~~~~~~~~~~ +LL - let _s2 = T { a: 2, ..s0 }; +LL + let _s2 = T { a: 2, b: s0.b.clone() }; + | error[E0509]: cannot move out of type `T`, which implements the `Drop` trait --> $DIR/borrowck-struct-update-with-dtor.rs:42:19 @@ -113,8 +116,9 @@ LL | let _s2 = T { ..s0 }; | help: clone the value from the field instead of using the functional record update syntax | -LL | let _s2 = T { b: s0.b.clone(), ..s0 }; - | ~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - let _s2 = T { ..s0 }; +LL + let _s2 = T { b: s0.b.clone(), ..s0 }; + | error[E0509]: cannot move out of type `T`, which implements the `Drop` trait --> $DIR/borrowck-struct-update-with-dtor.rs:47:32 diff --git a/tests/ui/borrowck/borrowck-thread-local-static-borrow-outlives-fn.stderr b/tests/ui/borrowck/borrowck-thread-local-static-borrow-outlives-fn.stderr index 11ee8f7bb91a5..6e0c69a4eb077 100644 --- a/tests/ui/borrowck/borrowck-thread-local-static-borrow-outlives-fn.stderr +++ b/tests/ui/borrowck/borrowck-thread-local-static-borrow-outlives-fn.stderr @@ -4,7 +4,7 @@ error[E0712]: thread-local variable borrowed past end of function LL | assert_static(&FOO); | ^^^^ thread-local variables cannot be borrowed beyond the end of the function LL | } - | - end of enclosing function is here + | - end of enclosing function is here error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr b/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr index e4dfa6d7fba7c..1e3570fc85570 100644 --- a/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr +++ b/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr @@ -9,8 +9,9 @@ LL | let sfoo: *mut Foo = &mut SFOO; = note: `#[warn(static_mut_refs)]` on by default help: use `&raw mut` instead to create a raw pointer | -LL | let sfoo: *mut Foo = &raw mut SFOO; - | ~~~~~~~~ +LL - let sfoo: *mut Foo = &mut SFOO; +LL + let sfoo: *mut Foo = &raw mut SFOO; + | warning: 1 warning emitted diff --git a/tests/ui/borrowck/fn-item-check-type-params.stderr b/tests/ui/borrowck/fn-item-check-type-params.stderr index 3a29edc55c54f..aafb7e66ef55f 100644 --- a/tests/ui/borrowck/fn-item-check-type-params.stderr +++ b/tests/ui/borrowck/fn-item-check-type-params.stderr @@ -12,12 +12,12 @@ LL | extend_lt(val); | argument requires that `'a` must outlive `'static` error: lifetime may not live long enough - --> $DIR/fn-item-check-type-params.rs:39:12 + --> $DIR/fn-item-check-type-params.rs:39:31 | LL | pub fn test_coercion<'a>() { | -- lifetime `'a` defined here LL | let _: fn(&'a str) -> _ = extend_lt; - | ^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + | ^^^^^^^^^ coercion requires that `'a` must outlive `'static` error[E0716]: temporary value dropped while borrowed --> $DIR/fn-item-check-type-params.rs:48:11 diff --git a/tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr b/tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr index 13c768dcbf636..4ec4d2138db60 100644 --- a/tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr +++ b/tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr @@ -15,10 +15,10 @@ LL | async { LL | let not_static = 0; | ---------- binding `not_static` declared here LL | force_send(async_load(¬_static)); - | -----------^^^^^^^^^^^- - | | | - | | borrowed value does not live long enough - | argument requires that `not_static` is borrowed for `'1` + | ----------------------^^^^^^^^^^^-- + | | | + | | borrowed value does not live long enough + | argument requires that `not_static` is borrowed for `'1` ... LL | } | - `not_static` dropped here while still borrowed diff --git a/tests/ui/borrowck/implied-bound-from-impl-header.rs b/tests/ui/borrowck/implied-bound-from-impl-header.rs new file mode 100644 index 0000000000000..326a62b22f055 --- /dev/null +++ b/tests/ui/borrowck/implied-bound-from-impl-header.rs @@ -0,0 +1,28 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Make sure that we can normalize `>::Assoc` to `&'a T` and get +// its implied bounds in impl header. + +trait Ref<'a> { + type Assoc; +} +impl<'a, T> Ref<'a> for T where T: 'a { + type Assoc = &'a T; +} + +fn outlives<'a, T: 'a>() {} + +trait Trait<'a, T> { + fn test(); +} + +impl<'a, T> Trait<'a, T> for >::Assoc { + fn test() { + outlives::<'a, T>(); + } +} + +fn main() {} diff --git a/tests/ui/borrowck/implied-bound-from-normalized-arg.rs b/tests/ui/borrowck/implied-bound-from-normalized-arg.rs new file mode 100644 index 0000000000000..4e9a4953c6b7e --- /dev/null +++ b/tests/ui/borrowck/implied-bound-from-normalized-arg.rs @@ -0,0 +1,22 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// Make sure that we can normalize `>::Assoc` to `&'a T` and get +// its implied bounds. + +trait Ref<'a> { + type Assoc; +} +impl<'a, T> Ref<'a> for T where T: 'a { + type Assoc = &'a T; +} + +fn outlives<'a, T: 'a>() {} + +fn test<'a, T>(_: >::Assoc) { + outlives::<'a, T>(); +} + +fn main() {} diff --git a/tests/ui/borrowck/index-mut-help.stderr b/tests/ui/borrowck/index-mut-help.stderr index c4c9c1c531399..6c3bd0df20b20 100644 --- a/tests/ui/borrowck/index-mut-help.stderr +++ b/tests/ui/borrowck/index-mut-help.stderr @@ -7,8 +7,9 @@ LL | map["peter"].clear(); = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `HashMap<&str, String>` help: to modify a `HashMap<&str, String>` use `.get_mut()` | -LL | if let Some(val) = map.get_mut("peter") { val.clear(); }; - | ++++++++++++++++++ ~~~~~~~~~ ~~~~~~~ +++ +LL - map["peter"].clear(); +LL + if let Some(val) = map.get_mut("peter") { val.clear(); }; + | error[E0594]: cannot assign to data in an index of `HashMap<&str, String>` --> $DIR/index-mut-help.rs:11:5 @@ -19,12 +20,15 @@ LL | map["peter"] = "0".to_string(); = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `HashMap<&str, String>` help: use `.insert()` to insert a value into a `HashMap<&str, String>`, `.get_mut()` to modify it, or the entry API for more flexibility | -LL | map.insert("peter", "0".to_string()); - | ~~~~~~~~ ~ + -LL | if let Some(val) = map.get_mut("peter") { *val = "0".to_string(); }; - | ++++++++++++++++++ ~~~~~~~~~ ~~~~~~~~ +++ -LL | let val = map.entry("peter").or_insert("0".to_string()); - | +++++++++ ~~~~~~~ ~~~~~~~~~~~~ + +LL - map["peter"] = "0".to_string(); +LL + map.insert("peter", "0".to_string()); + | +LL - map["peter"] = "0".to_string(); +LL + if let Some(val) = map.get_mut("peter") { *val = "0".to_string(); }; + | +LL - map["peter"] = "0".to_string(); +LL + let val = map.entry("peter").or_insert("0".to_string()); + | error[E0596]: cannot borrow data in an index of `HashMap<&str, String>` as mutable --> $DIR/index-mut-help.rs:12:13 diff --git a/tests/ui/borrowck/issue-103095.rs b/tests/ui/borrowck/issue-103095.rs index 3c29bc7615537..53587a16abf86 100644 --- a/tests/ui/borrowck/issue-103095.rs +++ b/tests/ui/borrowck/issue-103095.rs @@ -1,4 +1,7 @@ //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver trait FnOnceForGenericRef: FnOnce(&T) -> Self::FnOutput { type FnOutput; @@ -16,10 +19,7 @@ struct Data> { impl> Data { fn new(value: T, f: D) -> Self { let output = f(&value); - Self { - value: Some(value), - output: Some(output), - } + Self { value: Some(value), output: Some(output) } } } diff --git a/tests/ui/borrowck/issue-115259-suggest-iter-mut.stderr b/tests/ui/borrowck/issue-115259-suggest-iter-mut.stderr index 40ab2e61d6a42..61c01f0f024fe 100644 --- a/tests/ui/borrowck/issue-115259-suggest-iter-mut.stderr +++ b/tests/ui/borrowck/issue-115259-suggest-iter-mut.stderr @@ -9,7 +9,7 @@ LL | self.layers.iter().fold(0, |result, mut layer| result + layer.proce help: you may want to use `iter_mut` here | LL | self.layers.iter_mut().fold(0, |result, mut layer| result + layer.process()) - | ~~~~~~~~ + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/issue-28934.rs b/tests/ui/borrowck/issue-28934.rs index a3ac663c5b5c6..64559d4cf1ddc 100644 --- a/tests/ui/borrowck/issue-28934.rs +++ b/tests/ui/borrowck/issue-28934.rs @@ -3,7 +3,7 @@ //@ run-fail //@ error-pattern:explicit panic -//@ ignore-emscripten no processes +//@ needs-subprocess struct Parser<'i: 't, 't>(&'i u8, &'t u8); diff --git a/tests/ui/borrowck/issue-62387-suggest-iter-mut-2.stderr b/tests/ui/borrowck/issue-62387-suggest-iter-mut-2.stderr index 466f19eb0ab95..c6955317d87d5 100644 --- a/tests/ui/borrowck/issue-62387-suggest-iter-mut-2.stderr +++ b/tests/ui/borrowck/issue-62387-suggest-iter-mut-2.stderr @@ -9,7 +9,7 @@ LL | vec.iter().flat_map(|container| container.things()).cloned().co help: you may want to use `iter_mut` here | LL | vec.iter_mut().flat_map(|container| container.things()).cloned().collect::>(); - | ~~~~~~~~ + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/issue-62387-suggest-iter-mut.stderr b/tests/ui/borrowck/issue-62387-suggest-iter-mut.stderr index fd58e43302025..ae4920b2a8cb0 100644 --- a/tests/ui/borrowck/issue-62387-suggest-iter-mut.stderr +++ b/tests/ui/borrowck/issue-62387-suggest-iter-mut.stderr @@ -9,7 +9,7 @@ LL | v.iter().for_each(|a| a.double()); help: you may want to use `iter_mut` here | LL | v.iter_mut().for_each(|a| a.double()); - | ~~~~~~~~ + | ++++ error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference --> $DIR/issue-62387-suggest-iter-mut.rs:25:39 @@ -22,7 +22,7 @@ LL | v.iter().rev().rev().for_each(|a| a.double()); help: you may want to use `iter_mut` here | LL | v.iter_mut().rev().rev().for_each(|a| a.double()); - | ~~~~~~~~ + | ++++ error: aborting due to 2 previous errors diff --git a/tests/ui/borrowck/issue-7573.rs b/tests/ui/borrowck/issue-7573.rs index 7c07411533ff0..f94cd75ab6e2e 100644 --- a/tests/ui/borrowck/issue-7573.rs +++ b/tests/ui/borrowck/issue-7573.rs @@ -17,6 +17,8 @@ pub fn remove_package_from_database() { lines_to_use.push(installed_id); //~^ ERROR borrowed data escapes outside of closure //~| NOTE `installed_id` escapes the closure body here + //~| NOTE requirement occurs because of a mutable reference to `Vec<&CrateId>` + //~| NOTE mutable references are invariant over their type parameter }; list_database(push_id); diff --git a/tests/ui/borrowck/issue-7573.stderr b/tests/ui/borrowck/issue-7573.stderr index 07a67474c8310..d0121f4ec174f 100644 --- a/tests/ui/borrowck/issue-7573.stderr +++ b/tests/ui/borrowck/issue-7573.stderr @@ -9,6 +9,10 @@ LL | let push_id = |installed_id: &CrateId| { LL | LL | lines_to_use.push(installed_id); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `installed_id` escapes the closure body here + | + = note: requirement occurs because of a mutable reference to `Vec<&CrateId>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/issue-85765-closure.stderr b/tests/ui/borrowck/issue-85765-closure.stderr index 936ddd67bcd81..fa4e544150877 100644 --- a/tests/ui/borrowck/issue-85765-closure.stderr +++ b/tests/ui/borrowck/issue-85765-closure.stderr @@ -6,8 +6,9 @@ LL | rofl.push(Vec::new()); | help: consider changing this binding's type | -LL | let rofl: &mut Vec> = &mut test; - | ~~~~~~~~~~~~~~~~~~ +LL - let rofl: &Vec> = &mut test; +LL + let rofl: &mut Vec> = &mut test; + | error[E0594]: cannot assign to `*r`, which is behind a `&` reference --> $DIR/issue-85765-closure.rs:13:9 @@ -28,8 +29,9 @@ LL | *x = 1; | help: consider changing this binding's type | -LL | let x: &mut usize = &mut{0}; - | ~~~~~~~~~~ +LL - let x: &usize = &mut{0}; +LL + let x: &mut usize = &mut{0}; + | error[E0594]: cannot assign to `*y`, which is behind a `&` reference --> $DIR/issue-85765-closure.rs:27:9 @@ -39,8 +41,9 @@ LL | *y = 1; | help: consider changing this binding's type | -LL | let y: &mut usize = &mut(0); - | ~~~~~~~~~~ +LL - let y: &usize = &mut(0); +LL + let y: &mut usize = &mut(0); + | error: aborting due to 4 previous errors diff --git a/tests/ui/borrowck/issue-85765.stderr b/tests/ui/borrowck/issue-85765.stderr index 57900bfb612e1..9354294f52b21 100644 --- a/tests/ui/borrowck/issue-85765.stderr +++ b/tests/ui/borrowck/issue-85765.stderr @@ -6,8 +6,9 @@ LL | rofl.push(Vec::new()); | help: consider changing this binding's type | -LL | let rofl: &mut Vec> = &mut test; - | ~~~~~~~~~~~~~~~~~~ +LL - let rofl: &Vec> = &mut test; +LL + let rofl: &mut Vec> = &mut test; + | error[E0594]: cannot assign to `*r`, which is behind a `&` reference --> $DIR/issue-85765.rs:12:5 @@ -28,8 +29,9 @@ LL | *x = 1; | help: consider changing this binding's type | -LL | let x: &mut usize = &mut{0}; - | ~~~~~~~~~~ +LL - let x: &usize = &mut{0}; +LL + let x: &mut usize = &mut{0}; + | error[E0594]: cannot assign to `*y`, which is behind a `&` reference --> $DIR/issue-85765.rs:26:5 @@ -39,8 +41,9 @@ LL | *y = 1; | help: consider changing this binding's type | -LL | let y: &mut usize = &mut(0); - | ~~~~~~~~~~ +LL - let y: &usize = &mut(0); +LL + let y: &mut usize = &mut(0); + | error: aborting due to 4 previous errors diff --git a/tests/ui/borrowck/issue-92157.rs b/tests/ui/borrowck/issue-92157.rs index 3a6f8908b219f..a2b685cdf6ede 100644 --- a/tests/ui/borrowck/issue-92157.rs +++ b/tests/ui/borrowck/issue-92157.rs @@ -5,7 +5,7 @@ #[cfg(target_os = "linux")] #[link(name = "c")] -extern {} +extern "C" {} #[lang = "start"] fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize { diff --git a/tests/ui/borrowck/issue-93093.rs b/tests/ui/borrowck/issue-93093.rs index e85b296c983f4..1521b20723892 100644 --- a/tests/ui/borrowck/issue-93093.rs +++ b/tests/ui/borrowck/issue-93093.rs @@ -4,7 +4,7 @@ struct S { } impl S { async fn bar(&self) { //~ HELP consider changing this to be a mutable reference - //~| SUGGESTION &mut self + //~| SUGGESTION mut self.foo += 1; //~ ERROR cannot assign to `self.foo`, which is behind a `&` reference [E0594] } } diff --git a/tests/ui/borrowck/issue-93093.stderr b/tests/ui/borrowck/issue-93093.stderr index b6a2768b61da3..d788ce331973c 100644 --- a/tests/ui/borrowck/issue-93093.stderr +++ b/tests/ui/borrowck/issue-93093.stderr @@ -7,7 +7,7 @@ LL | self.foo += 1; help: consider changing this to be a mutable reference | LL | async fn bar(&mut self) { - | ~~~~~~~~~ + | +++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/no-invalid-mut-suggestion-for-raw-pointer-issue-127562.stderr b/tests/ui/borrowck/no-invalid-mut-suggestion-for-raw-pointer-issue-127562.stderr index dbe834b6b78e1..9bcfbd3379559 100644 --- a/tests/ui/borrowck/no-invalid-mut-suggestion-for-raw-pointer-issue-127562.stderr +++ b/tests/ui/borrowck/no-invalid-mut-suggestion-for-raw-pointer-issue-127562.stderr @@ -6,8 +6,9 @@ LL | unsafe { *ptr = 3; } | help: consider changing this to be a mutable pointer | -LL | let ptr = &raw mut val; - | ~~~ +LL - let ptr = &raw const val; +LL + let ptr = &raw mut val; + | error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/suggest-lt-on-ty-alias-w-generics.stderr b/tests/ui/borrowck/suggest-lt-on-ty-alias-w-generics.stderr index 28b4b4aa290d5..98fb49824ae93 100644 --- a/tests/ui/borrowck/suggest-lt-on-ty-alias-w-generics.stderr +++ b/tests/ui/borrowck/suggest-lt-on-ty-alias-w-generics.stderr @@ -12,8 +12,9 @@ LL | | }) | help: to declare that the trait object captures data from argument `x`, you can add a lifetime parameter `'a` in the type alias | -LL | type Lazy<'a, T> = Box T + 'a>; - | +++ ~~ +LL - type Lazy = Box T + 'static>; +LL + type Lazy<'a, T> = Box T + 'a>; + | error[E0373]: closure may outlive the current function, but it borrows `x`, which is owned by the current function --> $DIR/suggest-lt-on-ty-alias-w-generics.rs:4:14 diff --git a/tests/ui/borrowck/trait-impl-argument-difference-ice.stderr b/tests/ui/borrowck/trait-impl-argument-difference-ice.stderr index 5c70eccfbd35c..190ddeaa8f285 100644 --- a/tests/ui/borrowck/trait-impl-argument-difference-ice.stderr +++ b/tests/ui/borrowck/trait-impl-argument-difference-ice.stderr @@ -41,7 +41,7 @@ LL | let a16 = self.read_word() as u16; help: consider changing this to be a mutable reference in the `impl` method and the `trait` definition | LL | extern "C" fn read_dword(&'_ mut self) -> u16 { - | ~~~~~~~~~~~~ + | +++ error[E0596]: cannot borrow `*self` as mutable, as it is behind a `&` reference --> $DIR/trait-impl-argument-difference-ice.rs:18:19 @@ -52,7 +52,7 @@ LL | let b16 = self.read_word() as u16; help: consider changing this to be a mutable reference in the `impl` method and the `trait` definition | LL | extern "C" fn read_dword(&'_ mut self) -> u16 { - | ~~~~~~~~~~~~ + | +++ error: aborting due to 5 previous errors; 1 warning emitted diff --git a/tests/ui/borrowck/two-phase-surprise-no-conflict.stderr b/tests/ui/borrowck/two-phase-surprise-no-conflict.stderr index 89b15a7a659dd..4d1d7ffd29353 100644 --- a/tests/ui/borrowck/two-phase-surprise-no-conflict.stderr +++ b/tests/ui/borrowck/two-phase-surprise-no-conflict.stderr @@ -71,14 +71,13 @@ error[E0502]: cannot borrow `*reg` as mutable because it is also borrowed as imm LL | fn register_plugins<'a>(mk_reg: impl Fn() -> &'a mut Registry<'a>) { | -- lifetime `'a` defined here ... +LL | let reg = mk_reg(); + | -------- assignment requires that `reg.sess_mut` is borrowed for `'a` LL | reg.register_univ(Box::new(CapturePass::new(®.sess_mut))); - | ^^^^^^^^^^^^^^^^^^-----------------------------------------^ - | | | | - | | | immutable borrow occurs here - | | coercion requires that `reg.sess_mut` is borrowed for `'a` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-------------^^^ + | | | + | | immutable borrow occurs here | mutable borrow occurs here - | - = note: due to object lifetime defaults, `Box LateLintPass<'b>>` actually means `Box<(dyn for<'b> LateLintPass<'b> + 'static)>` error[E0502]: cannot borrow `*reg` as mutable because it is also borrowed as immutable --> $DIR/two-phase-surprise-no-conflict.rs:144:5 @@ -115,14 +114,13 @@ error[E0499]: cannot borrow `*reg` as mutable more than once at a time LL | fn register_plugins<'a>(mk_reg: impl Fn() -> &'a mut Registry<'a>) { | -- lifetime `'a` defined here ... +LL | let reg = mk_reg(); + | -------- assignment requires that `reg.sess_mut` is borrowed for `'a` LL | reg.register_univ(Box::new(CapturePass::new_mut(&mut reg.sess_mut))); - | ^^^^^^^^^^^^^^^^^^-------------------------------------------------^ - | | | | - | | | first mutable borrow occurs here - | | coercion requires that `reg.sess_mut` is borrowed for `'a` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------------^^^ + | | | + | | first mutable borrow occurs here | second mutable borrow occurs here - | - = note: due to object lifetime defaults, `Box LateLintPass<'b>>` actually means `Box<(dyn for<'b> LateLintPass<'b> + 'static)>` error[E0499]: cannot borrow `reg.sess_mut` as mutable more than once at a time --> $DIR/two-phase-surprise-no-conflict.rs:158:53 diff --git a/tests/ui/borrowck/unmatched-arg-and-hir-arg-issue-126385.stderr b/tests/ui/borrowck/unmatched-arg-and-hir-arg-issue-126385.stderr index 0ae301b2090ac..a18c4e7271949 100644 --- a/tests/ui/borrowck/unmatched-arg-and-hir-arg-issue-126385.stderr +++ b/tests/ui/borrowck/unmatched-arg-and-hir-arg-issue-126385.stderr @@ -16,8 +16,9 @@ LL | impl MyStruct<'_> { | ----------------- `Self` is on type `MyStruct` in this `impl` help: the `Self` type doesn't accept type parameters, use the concrete type's name `MyStruct` instead if you want to specify its type parameters | -LL | pub fn f(field: &[u32]) -> MyStruct { - | ~~~~~~~~ +LL - pub fn f(field: &[u32]) -> Self { +LL + pub fn f(field: &[u32]) -> MyStruct { + | error: lifetime may not live long enough --> $DIR/unmatched-arg-and-hir-arg-issue-126385.rs:10:9 diff --git a/tests/ui/box/unit/unwind-unique.rs b/tests/ui/box/unit/unwind-unique.rs index 512327c9af456..1da55c45ee969 100644 --- a/tests/ui/box/unit/unwind-unique.rs +++ b/tests/ui/box/unit/unwind-unique.rs @@ -1,6 +1,6 @@ //@ run-pass //@ needs-unwind -//@ ignore-emscripten no threads support +//@ needs-threads use std::thread; diff --git a/tests/ui/btreemap/btreemap-index-mut-2.stderr b/tests/ui/btreemap/btreemap-index-mut-2.stderr index c42462ee1eb53..74a8aaf8aee9a 100644 --- a/tests/ui/btreemap/btreemap-index-mut-2.stderr +++ b/tests/ui/btreemap/btreemap-index-mut-2.stderr @@ -7,12 +7,15 @@ LL | map[&0] = 1; = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `BTreeMap` help: use `.insert()` to insert a value into a `BTreeMap`, `.get_mut()` to modify it, or the entry API for more flexibility | -LL | map.insert(&0, 1); - | ~~~~~~~~ ~ + -LL | if let Some(val) = map.get_mut(&0) { *val = 1; }; - | ++++++++++++++++++ ~~~~~~~~~ ~~~~~~~~ +++ -LL | let val = map.entry(&0).or_insert(1); - | +++++++++ ~~~~~~~ ~~~~~~~~~~~~ + +LL - map[&0] = 1; +LL + map.insert(&0, 1); + | +LL - map[&0] = 1; +LL + if let Some(val) = map.get_mut(&0) { *val = 1; }; + | +LL - map[&0] = 1; +LL + let val = map.entry(&0).or_insert(1); + | error: aborting due to 1 previous error diff --git a/tests/ui/btreemap/btreemap-index-mut.stderr b/tests/ui/btreemap/btreemap-index-mut.stderr index f402f503c1578..e8850ed2a1742 100644 --- a/tests/ui/btreemap/btreemap-index-mut.stderr +++ b/tests/ui/btreemap/btreemap-index-mut.stderr @@ -7,12 +7,15 @@ LL | map[&0] = 1; = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `BTreeMap` help: use `.insert()` to insert a value into a `BTreeMap`, `.get_mut()` to modify it, or the entry API for more flexibility | -LL | map.insert(&0, 1); - | ~~~~~~~~ ~ + -LL | if let Some(val) = map.get_mut(&0) { *val = 1; }; - | ++++++++++++++++++ ~~~~~~~~~ ~~~~~~~~ +++ -LL | let val = map.entry(&0).or_insert(1); - | +++++++++ ~~~~~~~ ~~~~~~~~~~~~ + +LL - map[&0] = 1; +LL + map.insert(&0, 1); + | +LL - map[&0] = 1; +LL + if let Some(val) = map.get_mut(&0) { *val = 1; }; + | +LL - map[&0] = 1; +LL + let val = map.entry(&0).or_insert(1); + | error: aborting due to 1 previous error diff --git a/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs b/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs new file mode 100644 index 0000000000000..58370bff2200e --- /dev/null +++ b/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.rs @@ -0,0 +1,16 @@ +//@ only-x86_64 + +fn efiapi(f: extern "efiapi" fn(usize, ...)) { + //~^ ERROR: using calling conventions other than `C` or `cdecl` for varargs functions is unstable + f(22, 44); +} +fn sysv(f: extern "sysv64" fn(usize, ...)) { + //~^ ERROR: using calling conventions other than `C` or `cdecl` for varargs functions is unstable + f(22, 44); +} +fn win(f: extern "win64" fn(usize, ...)) { + //~^ ERROR: using calling conventions other than `C` or `cdecl` for varargs functions is unstable + f(22, 44); +} + +fn main() {} diff --git a/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr b/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr new file mode 100644 index 0000000000000..9565575dc4279 --- /dev/null +++ b/tests/ui/c-variadic/feature-gate-extended_varargs_abi_support.stderr @@ -0,0 +1,33 @@ +error[E0658]: using calling conventions other than `C` or `cdecl` for varargs functions is unstable + --> $DIR/feature-gate-extended_varargs_abi_support.rs:3:14 + | +LL | fn efiapi(f: extern "efiapi" fn(usize, ...)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #100189 for more information + = help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: using calling conventions other than `C` or `cdecl` for varargs functions is unstable + --> $DIR/feature-gate-extended_varargs_abi_support.rs:7:12 + | +LL | fn sysv(f: extern "sysv64" fn(usize, ...)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #100189 for more information + = help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: using calling conventions other than `C` or `cdecl` for varargs functions is unstable + --> $DIR/feature-gate-extended_varargs_abi_support.rs:11:11 + | +LL | fn win(f: extern "win64" fn(usize, ...)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #100189 for more information + = help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/c-variadic/issue-86053-1.stderr b/tests/ui/c-variadic/issue-86053-1.stderr index 67a619e46d57d..ce31f0d300f1f 100644 --- a/tests/ui/c-variadic/issue-86053-1.stderr +++ b/tests/ui/c-variadic/issue-86053-1.stderr @@ -64,7 +64,7 @@ LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize help: a trait with a similar name exists | LL | self , ... , self , self , ... ) where Fn : FnOnce ( & 'a & 'b usize ) { - | ~~ + | + help: you might be missing a type parameter | LL | fn ordering4 < 'a , 'b, F > ( a : , self , self , self , diff --git a/tests/ui/c-variadic/variadic-ffi-1.stderr b/tests/ui/c-variadic/variadic-ffi-1.stderr index 7a54d043356a3..061eae9729e2e 100644 --- a/tests/ui/c-variadic/variadic-ffi-1.stderr +++ b/tests/ui/c-variadic/variadic-ffi-1.stderr @@ -14,11 +14,12 @@ note: function defined here --> $DIR/variadic-ffi-1.rs:15:8 | LL | fn foo(f: isize, x: u8, ...); - | ^^^ + | ^^^ - - help: provide the arguments | -LL | foo(/* isize */, /* u8 */); - | ~~~~~~~~~~~~~~~~~~~~~~~ +LL - foo(); +LL + foo(/* isize */, /* u8 */); + | error[E0060]: this function takes at least 2 arguments but 1 argument was supplied --> $DIR/variadic-ffi-1.rs:23:9 @@ -30,11 +31,12 @@ note: function defined here --> $DIR/variadic-ffi-1.rs:15:8 | LL | fn foo(f: isize, x: u8, ...); - | ^^^ + | ^^^ - help: provide the argument | -LL | foo(1, /* u8 */); - | ~~~~~~~~~~~~~ +LL - foo(1); +LL + foo(1, /* u8 */); + | error[E0308]: mismatched types --> $DIR/variadic-ffi-1.rs:25:56 diff --git a/tests/ui/c-variadic/variadic-ffi-2-arm.rs b/tests/ui/c-variadic/variadic-ffi-2-arm.rs index 82f9df5053c19..3b0a71007a0e6 100644 --- a/tests/ui/c-variadic/variadic-ffi-2-arm.rs +++ b/tests/ui/c-variadic/variadic-ffi-2-arm.rs @@ -1,5 +1,6 @@ //@ only-arm //@ build-pass +#![feature(extended_varargs_abi_support)] fn aapcs(f: extern "aapcs" fn(usize, ...)) { f(22, 44); diff --git a/tests/ui/c-variadic/variadic-ffi-2.rs b/tests/ui/c-variadic/variadic-ffi-2.rs index 17a1065279f4e..99f83f22d17c0 100644 --- a/tests/ui/c-variadic/variadic-ffi-2.rs +++ b/tests/ui/c-variadic/variadic-ffi-2.rs @@ -1,4 +1,5 @@ //@ ignore-arm stdcall isn't supported +#![feature(extended_varargs_abi_support)] #[allow(unsupported_fn_ptr_calling_conventions)] fn baz(f: extern "stdcall" fn(usize, ...)) { @@ -7,9 +8,6 @@ fn baz(f: extern "stdcall" fn(usize, ...)) { f(22, 44); } -fn system(f: extern "system" fn(usize, ...)) { - f(22, 44); -} #[cfg(target_arch = "x86_64")] fn sysv(f: extern "sysv64" fn(usize, ...)) { f(22, 44); diff --git a/tests/ui/c-variadic/variadic-ffi-2.stderr b/tests/ui/c-variadic/variadic-ffi-2.stderr index fbf273b1f1dbf..e52de93a92646 100644 --- a/tests/ui/c-variadic/variadic-ffi-2.stderr +++ b/tests/ui/c-variadic/variadic-ffi-2.stderr @@ -1,5 +1,5 @@ error[E0045]: C-variadic function must have a compatible calling convention, like `C`, `cdecl`, `system`, `aapcs`, `win64`, `sysv64` or `efiapi` - --> $DIR/variadic-ffi-2.rs:4:11 + --> $DIR/variadic-ffi-2.rs:5:11 | LL | fn baz(f: extern "stdcall" fn(usize, ...)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention diff --git a/tests/ui/c-variadic/variadic-ffi-4.stderr b/tests/ui/c-variadic/variadic-ffi-4.stderr index c9d90d73dea31..fc9f8036083a6 100644 --- a/tests/ui/c-variadic/variadic-ffi-4.stderr +++ b/tests/ui/c-variadic/variadic-ffi-4.stderr @@ -120,14 +120,14 @@ LL | } | - `ap1` dropped here while still borrowed error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:35:12 + --> $DIR/variadic-ffi-4.rs:35:5 | LL | pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { | ------- ------- has type `VaListImpl<'2>` | | | has type `&mut VaListImpl<'1>` LL | *ap0 = ap1.clone(); - | ^^^^^^^^^^^ argument requires that `'1` must outlive `'2` + | ^^^^ assignment requires that `'2` must outlive `'1` | = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` @@ -141,7 +141,7 @@ LL | pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut | | | has type `&mut VaListImpl<'1>` LL | *ap0 = ap1.clone(); - | ^^^^^^^^^^^ argument requires that `'2` must outlive `'1` + | ^^^^^^^^^^^ argument requires that `'1` must outlive `'2` | = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` diff --git a/tests/ui/cast/cast-as-bool.stderr b/tests/ui/cast/cast-as-bool.stderr index 4ff56a95e49a3..b2c9ae5c6ad61 100644 --- a/tests/ui/cast/cast-as-bool.stderr +++ b/tests/ui/cast/cast-as-bool.stderr @@ -6,8 +6,9 @@ LL | let u = 5 as bool; | help: compare with zero instead | -LL | let u = 5 != 0; - | ~~~~ +LL - let u = 5 as bool; +LL + let u = 5 != 0; + | error[E0054]: cannot cast `i32` as `bool` --> $DIR/cast-as-bool.rs:6:13 @@ -17,8 +18,9 @@ LL | let t = (1 + 2) as bool; | help: compare with zero instead | -LL | let t = (1 + 2) != 0; - | ~~~~ +LL - let t = (1 + 2) as bool; +LL + let t = (1 + 2) != 0; + | error[E0054]: cannot cast `u32` as `bool` --> $DIR/cast-as-bool.rs:10:13 @@ -28,8 +30,9 @@ LL | let _ = 5_u32 as bool; | help: compare with zero instead | -LL | let _ = 5_u32 != 0; - | ~~~~ +LL - let _ = 5_u32 as bool; +LL + let _ = 5_u32 != 0; + | error[E0054]: cannot cast `f64` as `bool` --> $DIR/cast-as-bool.rs:13:13 @@ -39,8 +42,9 @@ LL | let _ = 64.0_f64 as bool; | help: compare with zero instead | -LL | let _ = 64.0_f64 != 0; - | ~~~~ +LL - let _ = 64.0_f64 as bool; +LL + let _ = 64.0_f64 != 0; + | error[E0054]: cannot cast `IntEnum` as `bool` --> $DIR/cast-as-bool.rs:24:13 @@ -86,8 +90,9 @@ LL | let v = "hello" as bool; | help: consider using the `is_empty` method on `&'static str` to determine if it contains anything | -LL | let v = !"hello".is_empty(); - | + ~~~~~~~~~~~ +LL - let v = "hello" as bool; +LL + let v = !"hello".is_empty(); + | error: aborting due to 11 previous errors diff --git a/tests/ui/cast/cast-rfc0401-2.stderr b/tests/ui/cast/cast-rfc0401-2.stderr index b7fb420533e65..f2956cdfa3356 100644 --- a/tests/ui/cast/cast-rfc0401-2.stderr +++ b/tests/ui/cast/cast-rfc0401-2.stderr @@ -6,8 +6,9 @@ LL | let _ = 3 as bool; | help: compare with zero instead | -LL | let _ = 3 != 0; - | ~~~~ +LL - let _ = 3 as bool; +LL + let _ = 3 != 0; + | error: aborting due to 1 previous error diff --git a/tests/ui/cast/ice-cast-type-with-error-124848.stderr b/tests/ui/cast/ice-cast-type-with-error-124848.stderr index 1e2adcc7d9ee1..402ee27386ddd 100644 --- a/tests/ui/cast/ice-cast-type-with-error-124848.stderr +++ b/tests/ui/cast/ice-cast-type-with-error-124848.stderr @@ -48,8 +48,9 @@ LL | struct MyType<'a>(Cell>>, Pin); | ^^^^^^ help: provide the argument | -LL | let mut unpinned = MyType(Cell::new(None), /* value */); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - let mut unpinned = MyType(Cell::new(None)); +LL + let mut unpinned = MyType(Cell::new(None), /* value */); + | error[E0606]: casting `&MyType<'_>` as `*const Cell>>` is invalid --> $DIR/ice-cast-type-with-error-124848.rs:14:20 diff --git a/tests/ui/cast/issue-106883-is-empty.stderr b/tests/ui/cast/issue-106883-is-empty.stderr index 7115c7704ca29..5ebabc9ed8378 100644 --- a/tests/ui/cast/issue-106883-is-empty.stderr +++ b/tests/ui/cast/issue-106883-is-empty.stderr @@ -6,8 +6,9 @@ LL | let _ = "foo" as bool; | help: consider using the `is_empty` method on `&'static str` to determine if it contains anything | -LL | let _ = !"foo".is_empty(); - | + ~~~~~~~~~~~ +LL - let _ = "foo" as bool; +LL + let _ = !"foo".is_empty(); + | error[E0605]: non-primitive cast: `String` as `bool` --> $DIR/issue-106883-is-empty.rs:17:13 @@ -22,8 +23,9 @@ LL | let _ = String::from("foo") as bool; | ^^^^^^^^^^^^^^^^^^^ help: consider using the `is_empty` method on `String` to determine if it contains anything | -LL | let _ = !String::from("foo").is_empty(); - | + ~~~~~~~~~~~ +LL - let _ = String::from("foo") as bool; +LL + let _ = !String::from("foo").is_empty(); + | error[E0605]: non-primitive cast: `Foo` as `bool` --> $DIR/issue-106883-is-empty.rs:20:13 @@ -38,8 +40,9 @@ LL | let _ = Foo as bool; | ^^^ help: consider using the `is_empty` method on `Foo` to determine if it contains anything | -LL | let _ = !Foo.is_empty(); - | + ~~~~~~~~~~~ +LL - let _ = Foo as bool; +LL + let _ = !Foo.is_empty(); + | error[E0606]: casting `&[i32]` as `bool` is invalid --> $DIR/issue-106883-is-empty.rs:25:5 @@ -49,8 +52,9 @@ LL | bar as bool | help: consider using the `is_empty` method on `&[i32]` to determine if it contains anything | -LL | !bar.is_empty() - | + ~~~~~~~~~~~ +LL - bar as bool +LL + !bar.is_empty() + | error: aborting due to 4 previous errors diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs index 18566acc07fe4..f8910c944c61e 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs @@ -42,7 +42,7 @@ fn change_assoc_1<'a, 'b>( // This tests the default borrow check error, without the special casing for return values. fn require_static(_: *const dyn Trait<'static>) {} fn extend_to_static<'a>(ptr: *const dyn Trait<'a>) { - require_static(ptr as _) //~ error: lifetime may not live long enough + require_static(ptr as _) //~ error: borrowed data escapes outside of function } fn main() {} diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr index 6f590585c4a46..faaa6325f3495 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr @@ -154,14 +154,22 @@ help: `'b` and `'a` must be the same: replace one with the other | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: lifetime may not live long enough - --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:45:20 +error[E0521]: borrowed data escapes outside of function + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:45:5 | LL | fn extend_to_static<'a>(ptr: *const dyn Trait<'a>) { - | -- lifetime `'a` defined here + | -- --- + | | | + | | `ptr` declared here, outside of the function body + | | `ptr` is a reference that is only valid in the function body + | lifetime `'a` defined here LL | require_static(ptr as _) - | ^^^^^^^^ cast requires that `'a` must outlive `'static` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `ptr` escapes the function body here + | argument requires that `'a` must outlive `'static` error: aborting due to 11 previous errors -For more information about this error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0308, E0521. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/cenum_impl_drop_cast.rs b/tests/ui/cenum_impl_drop_cast.rs index 96e3d967e2c61..f681434dd86ac 100644 --- a/tests/ui/cenum_impl_drop_cast.rs +++ b/tests/ui/cenum_impl_drop_cast.rs @@ -1,5 +1,3 @@ -#![deny(cenum_impl_drop_cast)] - enum E { A = 0, } @@ -14,5 +12,4 @@ fn main() { let e = E::A; let i = e as u32; //~^ ERROR cannot cast enum `E` into integer `u32` because it implements `Drop` - //~| WARN this was previously accepted } diff --git a/tests/ui/cenum_impl_drop_cast.stderr b/tests/ui/cenum_impl_drop_cast.stderr index 541d15d021d3f..35c69f4b4b785 100644 --- a/tests/ui/cenum_impl_drop_cast.stderr +++ b/tests/ui/cenum_impl_drop_cast.stderr @@ -1,31 +1,8 @@ error: cannot cast enum `E` into integer `u32` because it implements `Drop` - --> $DIR/cenum_impl_drop_cast.rs:15:13 + --> $DIR/cenum_impl_drop_cast.rs:13:13 | LL | let i = e as u32; | ^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #73333 -note: the lint level is defined here - --> $DIR/cenum_impl_drop_cast.rs:1:9 - | -LL | #![deny(cenum_impl_drop_cast)] - | ^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error -Future incompatibility report: Future breakage diagnostic: -error: cannot cast enum `E` into integer `u32` because it implements `Drop` - --> $DIR/cenum_impl_drop_cast.rs:15:13 - | -LL | let i = e as u32; - | ^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #73333 -note: the lint level is defined here - --> $DIR/cenum_impl_drop_cast.rs:1:9 - | -LL | #![deny(cenum_impl_drop_cast)] - | ^^^^^^^^^^^^^^^^^^^^ - diff --git a/tests/ui/cfg/cfg-method-receiver.stderr b/tests/ui/cfg/cfg-method-receiver.stderr index 5767a7c1b4b1c..44f3d8d058e04 100644 --- a/tests/ui/cfg/cfg-method-receiver.stderr +++ b/tests/ui/cfg/cfg-method-receiver.stderr @@ -17,7 +17,7 @@ LL | cbor_map! { #[cfg(test)] 4}; help: you must specify a concrete type for this numeric value, like `i32` | LL | cbor_map! { #[cfg(test)] 4_i32}; - | ~~~~~ + | ++++ error: aborting due to 2 previous errors diff --git a/tests/ui/cfg/conditional-compile.rs b/tests/ui/cfg/conditional-compile.rs index a4f334dd696ab..dff280054d659 100644 --- a/tests/ui/cfg/conditional-compile.rs +++ b/tests/ui/cfg/conditional-compile.rs @@ -85,7 +85,7 @@ pub fn main() { pub fn main() { // Exercise some of the configured items in ways that wouldn't be possible // if they had the FALSE definition - assert!((b)); + assert!(b); let _x: t = true; let _y: tg = tg::bar; diff --git a/tests/ui/cfg/disallowed-cli-cfgs.emscripten_wasm_eh_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.emscripten_wasm_eh_.stderr new file mode 100644 index 0000000000000..8b2ee0e5c0c3d --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.emscripten_wasm_eh_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg emscripten_wasm_eh` flag + | + = note: config `emscripten_wasm_eh` is only supposed to be controlled by `-Z emscripten_wasm_eh` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.rs b/tests/ui/cfg/disallowed-cli-cfgs.rs index 3c9ee87f28ab8..cae9c65cb45ac 100644 --- a/tests/ui/cfg/disallowed-cli-cfgs.rs +++ b/tests/ui/cfg/disallowed-cli-cfgs.rs @@ -7,6 +7,7 @@ //@ revisions: target_has_atomic_equal_alignment_ target_has_atomic_load_store_ //@ revisions: target_thread_local_ relocation_model_ //@ revisions: fmt_debug_ +//@ revisions: emscripten_wasm_eh_ //@ [overflow_checks_]compile-flags: --cfg overflow_checks //@ [debug_assertions_]compile-flags: --cfg debug_assertions @@ -33,5 +34,6 @@ //@ [target_thread_local_]compile-flags: --cfg target_thread_local //@ [relocation_model_]compile-flags: --cfg relocation_model="a" //@ [fmt_debug_]compile-flags: --cfg fmt_debug="shallow" +//@ [emscripten_wasm_eh_]compile-flags: --cfg emscripten_wasm_eh fn main() {} diff --git a/tests/ui/cfg/disallowed-cli-cfgs.test_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.test_.stderr deleted file mode 100644 index 96b5beb021062..0000000000000 --- a/tests/ui/cfg/disallowed-cli-cfgs.test_.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: unexpected `--cfg test` flag - | - = note: config `test` is only supposed to be controlled by `--test` - = note: see for more information - = note: `#[deny(unexpected_builtin_cfgs)]` on by default - -error: aborting due to 1 previous error - diff --git a/tests/ui/check-cfg/allow-same-level.rs b/tests/ui/check-cfg/allow-same-level.rs index ff724174cea54..5eef50e08e21b 100644 --- a/tests/ui/check-cfg/allow-same-level.rs +++ b/tests/ui/check-cfg/allow-same-level.rs @@ -1,12 +1,24 @@ -// This test check that #[allow(unexpected_cfgs)] doesn't work if put on the same level +// This test check that #[allow(unexpected_cfgs)] **doesn't work** +// when put on the same level as the #[cfg] attribute. +// +// It should work, but due to interactions between how #[cfg]s are +// expanded, the lint machinery and the check-cfg impl, we +// miss the #[allow], althrough we probably shoudln't. +// +// cf. https://github.com/rust-lang/rust/issues/124735 // //@ check-pass //@ no-auto-check-cfg -//@ compile-flags: --check-cfg=cfg() +//@ compile-flags: --check-cfg=cfg() --cfg=unknown_but_active_cfg #[allow(unexpected_cfgs)] #[cfg(FALSE)] //~^ WARNING unexpected `cfg` condition name fn bar() {} +#[allow(unexpected_cfgs)] +#[cfg(unknown_but_active_cfg)] +//~^ WARNING unexpected `cfg` condition name +fn bar() {} + fn main() {} diff --git a/tests/ui/check-cfg/allow-same-level.stderr b/tests/ui/check-cfg/allow-same-level.stderr index 5d74b2116543e..a705cd4e5f01e 100644 --- a/tests/ui/check-cfg/allow-same-level.stderr +++ b/tests/ui/check-cfg/allow-same-level.stderr @@ -1,13 +1,21 @@ warning: unexpected `cfg` condition name: `FALSE` - --> $DIR/allow-same-level.rs:8:7 + --> $DIR/allow-same-level.rs:15:7 | LL | #[cfg(FALSE)] | ^^^^^ | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `fmt_debug`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `ub_checks`, `unix`, and `windows` = help: to expect this configuration use `--check-cfg=cfg(FALSE)` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default -warning: 1 warning emitted +warning: unexpected `cfg` condition name: `unknown_but_active_cfg` + --> $DIR/allow-same-level.rs:20:7 + | +LL | #[cfg(unknown_but_active_cfg)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: to expect this configuration use `--check-cfg=cfg(unknown_but_active_cfg)` + = note: see for more information about checking conditional configuration + +warning: 2 warnings emitted diff --git a/tests/ui/check-cfg/cargo-build-script.stderr b/tests/ui/check-cfg/cargo-build-script.stderr index fb48751bc1dbc..03a7156a4d69e 100644 --- a/tests/ui/check-cfg/cargo-build-script.stderr +++ b/tests/ui/check-cfg/cargo-build-script.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `has_foo` LL | #[cfg(has_foo)] | ^^^^^^^ | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `fmt_debug`, `has_bar`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `ub_checks`, `unix`, and `windows` + = help: expected names are: `has_bar` and 31 more = help: consider using a Cargo feature instead = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: [lints.rust] diff --git a/tests/ui/check-cfg/cargo-feature.none.stderr b/tests/ui/check-cfg/cargo-feature.none.stderr index aa2a1ab8fb2f4..b83d1794984de 100644 --- a/tests/ui/check-cfg/cargo-feature.none.stderr +++ b/tests/ui/check-cfg/cargo-feature.none.stderr @@ -1,5 +1,5 @@ warning: unexpected `cfg` condition value: `serde` - --> $DIR/cargo-feature.rs:14:7 + --> $DIR/cargo-feature.rs:15:7 | LL | #[cfg(feature = "serde")] | ^^^^^^^^^^^^^^^^^ help: remove the condition @@ -10,7 +10,7 @@ LL | #[cfg(feature = "serde")] = note: `#[warn(unexpected_cfgs)]` on by default warning: unexpected `cfg` condition value: (none) - --> $DIR/cargo-feature.rs:18:7 + --> $DIR/cargo-feature.rs:19:7 | LL | #[cfg(feature)] | ^^^^^^^ help: remove the condition @@ -20,12 +20,12 @@ LL | #[cfg(feature)] = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `tokio_unstable` - --> $DIR/cargo-feature.rs:22:7 + --> $DIR/cargo-feature.rs:23:7 | LL | #[cfg(tokio_unstable)] | ^^^^^^^^^^^^^^ | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `feature`, `fmt_debug`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `ub_checks`, `unix`, and `windows` + = help: expected names are: `docsrs`, `feature`, and `test` and 31 more = help: consider using a Cargo feature instead = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: [lints.rust] @@ -34,7 +34,7 @@ LL | #[cfg(tokio_unstable)] = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `CONFIG_NVME` - --> $DIR/cargo-feature.rs:26:7 + --> $DIR/cargo-feature.rs:27:7 | LL | #[cfg(CONFIG_NVME = "m")] | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/check-cfg/cargo-feature.rs b/tests/ui/check-cfg/cargo-feature.rs index 13faf7f28200d..a9380ddae1a90 100644 --- a/tests/ui/check-cfg/cargo-feature.rs +++ b/tests/ui/check-cfg/cargo-feature.rs @@ -6,6 +6,7 @@ //@ no-auto-check-cfg //@ revisions: some none //@ rustc-env:CARGO_CRATE_NAME=foo +//@ compile-flags: --check-cfg=cfg(docsrs,test) //@ [none]compile-flags: --check-cfg=cfg(feature,values()) //@ [some]compile-flags: --check-cfg=cfg(feature,values("bitcode")) //@ [some]compile-flags: --check-cfg=cfg(CONFIG_NVME,values("y")) diff --git a/tests/ui/check-cfg/cargo-feature.some.stderr b/tests/ui/check-cfg/cargo-feature.some.stderr index c3ba123985b17..2cddcbbcd7f9e 100644 --- a/tests/ui/check-cfg/cargo-feature.some.stderr +++ b/tests/ui/check-cfg/cargo-feature.some.stderr @@ -1,5 +1,5 @@ warning: unexpected `cfg` condition value: `serde` - --> $DIR/cargo-feature.rs:14:7 + --> $DIR/cargo-feature.rs:15:7 | LL | #[cfg(feature = "serde")] | ^^^^^^^^^^^^^^^^^ @@ -10,7 +10,7 @@ LL | #[cfg(feature = "serde")] = note: `#[warn(unexpected_cfgs)]` on by default warning: unexpected `cfg` condition value: (none) - --> $DIR/cargo-feature.rs:18:7 + --> $DIR/cargo-feature.rs:19:7 | LL | #[cfg(feature)] | ^^^^^^^- help: specify a config value: `= "bitcode"` @@ -20,12 +20,12 @@ LL | #[cfg(feature)] = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `tokio_unstable` - --> $DIR/cargo-feature.rs:22:7 + --> $DIR/cargo-feature.rs:23:7 | LL | #[cfg(tokio_unstable)] | ^^^^^^^^^^^^^^ | - = help: expected names are: `CONFIG_NVME`, `clippy`, `debug_assertions`, `doc`, `doctest`, `feature`, `fmt_debug`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `ub_checks`, `unix`, and `windows` + = help: expected names are: `CONFIG_NVME`, `docsrs`, `feature`, and `test` and 31 more = help: consider using a Cargo feature instead = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: [lints.rust] @@ -34,7 +34,7 @@ LL | #[cfg(tokio_unstable)] = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `m` - --> $DIR/cargo-feature.rs:26:7 + --> $DIR/cargo-feature.rs:27:7 | LL | #[cfg(CONFIG_NVME = "m")] | ^^^^^^^^^^^^^^--- diff --git a/tests/ui/check-cfg/cfg-value-for-cfg-name-duplicate.stderr b/tests/ui/check-cfg/cfg-value-for-cfg-name-duplicate.stderr index b7dc27f9ba9da..68e1259dbb842 100644 --- a/tests/ui/check-cfg/cfg-value-for-cfg-name-duplicate.stderr +++ b/tests/ui/check-cfg/cfg-value-for-cfg-name-duplicate.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `value` LL | #[cfg(value)] | ^^^^^ | - = help: expected names are: `bar`, `bee`, `clippy`, `cow`, `debug_assertions`, `doc`, `doctest`, `fmt_debug`, `foo`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `ub_checks`, `unix`, and `windows` + = help: expected names are: `bar`, `bee`, `cow`, and `foo` and 31 more = help: to expect this configuration use `--check-cfg=cfg(value)` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/cfg-value-for-cfg-name-multiple.stderr b/tests/ui/check-cfg/cfg-value-for-cfg-name-multiple.stderr index d2af81d7787c6..5279f3b09015d 100644 --- a/tests/ui/check-cfg/cfg-value-for-cfg-name-multiple.stderr +++ b/tests/ui/check-cfg/cfg-value-for-cfg-name-multiple.stderr @@ -4,18 +4,20 @@ warning: unexpected `cfg` condition name: `my_value` LL | #[cfg(my_value)] | ^^^^^^^^ | - = help: expected names are: `bar`, `clippy`, `debug_assertions`, `doc`, `doctest`, `fmt_debug`, `foo`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `ub_checks`, `unix`, and `windows` + = help: expected names are: `bar` and `foo` and 31 more = help: to expect this configuration use `--check-cfg=cfg(my_value)` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default help: found config with similar value | -LL | #[cfg(foo = "my_value")] - | ~~~~~~~~~~~~~~~~ +LL - #[cfg(my_value)] +LL + #[cfg(foo = "my_value")] + | help: found config with similar value | -LL | #[cfg(bar = "my_value")] - | ~~~~~~~~~~~~~~~~ +LL - #[cfg(my_value)] +LL + #[cfg(bar = "my_value")] + | warning: 1 warning emitted diff --git a/tests/ui/check-cfg/cfg-value-for-cfg-name.stderr b/tests/ui/check-cfg/cfg-value-for-cfg-name.stderr index 85bf66eb10cb8..883679ddf9c84 100644 --- a/tests/ui/check-cfg/cfg-value-for-cfg-name.stderr +++ b/tests/ui/check-cfg/cfg-value-for-cfg-name.stderr @@ -4,7 +4,6 @@ warning: unexpected `cfg` condition name: `linux` LL | #[cfg(linux)] | ^^^^^ help: found config with similar value: `target_os = "linux"` | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `fmt_debug`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `ub_checks`, `unix`, and `windows` = help: to expect this configuration use `--check-cfg=cfg(linux)` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/compact-names.stderr b/tests/ui/check-cfg/compact-names.stderr index ef0a413bd0d7e..74ed0337e3b12 100644 --- a/tests/ui/check-cfg/compact-names.stderr +++ b/tests/ui/check-cfg/compact-names.stderr @@ -4,7 +4,6 @@ warning: unexpected `cfg` condition name: `target_architecture` LL | #[cfg(target(os = "linux", architecture = "arm"))] | ^^^^^^^^^^^^^^^^^^^^ | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `fmt_debug`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `ub_checks`, `unix`, and `windows` = help: to expect this configuration use `--check-cfg=cfg(target_architecture, values("arm"))` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/diagnotics.cargo.stderr b/tests/ui/check-cfg/diagnotics.cargo.stderr index a440ccaaf584a..ebfc9a935d20c 100644 --- a/tests/ui/check-cfg/diagnotics.cargo.stderr +++ b/tests/ui/check-cfg/diagnotics.cargo.stderr @@ -18,7 +18,7 @@ LL | #[cfg(featur = "foo")] help: there is a config with a similar name and value | LL | #[cfg(feature = "foo")] - | ~~~~~~~ + | + warning: unexpected `cfg` condition name: `featur` --> $DIR/diagnotics.rs:17:7 @@ -30,8 +30,9 @@ LL | #[cfg(featur = "fo")] = note: see for more information about checking conditional configuration help: there is a config with a similar name and different values | -LL | #[cfg(feature = "foo")] - | ~~~~~~~~~~~~~~~ +LL - #[cfg(featur = "fo")] +LL + #[cfg(feature = "foo")] + | warning: unexpected `cfg` condition name: `no_value` --> $DIR/diagnotics.rs:24:7 @@ -60,8 +61,9 @@ LL | #[cfg(no_value = "foo")] = note: see for more information about checking conditional configuration help: there is a config with a similar name and no value | -LL | #[cfg(no_values)] - | ~~~~~~~~~ +LL - #[cfg(no_value = "foo")] +LL + #[cfg(no_values)] + | warning: unexpected `cfg` condition value: `bar` --> $DIR/diagnotics.rs:32:7 diff --git a/tests/ui/check-cfg/diagnotics.rustc.stderr b/tests/ui/check-cfg/diagnotics.rustc.stderr index 6868be482d87f..8860b3ff5da57 100644 --- a/tests/ui/check-cfg/diagnotics.rustc.stderr +++ b/tests/ui/check-cfg/diagnotics.rustc.stderr @@ -20,7 +20,7 @@ LL | #[cfg(featur = "foo")] help: there is a config with a similar name and value | LL | #[cfg(feature = "foo")] - | ~~~~~~~ + | + warning: unexpected `cfg` condition name: `featur` --> $DIR/diagnotics.rs:17:7 @@ -33,8 +33,9 @@ LL | #[cfg(featur = "fo")] = note: see for more information about checking conditional configuration help: there is a config with a similar name and different values | -LL | #[cfg(feature = "foo")] - | ~~~~~~~~~~~~~~~ +LL - #[cfg(featur = "fo")] +LL + #[cfg(feature = "foo")] + | warning: unexpected `cfg` condition name: `no_value` --> $DIR/diagnotics.rs:24:7 @@ -55,8 +56,9 @@ LL | #[cfg(no_value = "foo")] = note: see for more information about checking conditional configuration help: there is a config with a similar name and no value | -LL | #[cfg(no_values)] - | ~~~~~~~~~ +LL - #[cfg(no_value = "foo")] +LL + #[cfg(no_values)] + | warning: unexpected `cfg` condition value: `bar` --> $DIR/diagnotics.rs:32:7 diff --git a/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr b/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr index d49ed3e755182..e7b8f35505761 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.empty_cfg.stderr @@ -4,7 +4,6 @@ warning: unexpected `cfg` condition name: `unknown_key` LL | #[cfg(unknown_key = "value")] | ^^^^^^^^^^^^^^^^^^^^^ | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `fmt_debug`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `ub_checks`, `unix`, and `windows` = help: to expect this configuration use `--check-cfg=cfg(unknown_key, values("value"))` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default @@ -15,7 +14,7 @@ warning: unexpected `cfg` condition value: `value` LL | #[cfg(target_vendor = "value")] | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` + = note: expected values for `target_vendor` are: `amd`, `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `mti`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `feature` diff --git a/tests/ui/check-cfg/exhaustive-names-values.feature.stderr b/tests/ui/check-cfg/exhaustive-names-values.feature.stderr index 81dbbca4f8630..95af0a9092993 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.feature.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.feature.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `unknown_key` LL | #[cfg(unknown_key = "value")] | ^^^^^^^^^^^^^^^^^^^^^ | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `feature`, `fmt_debug`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `ub_checks`, `unix`, and `windows` + = help: expected names are: `feature` and 31 more = help: to expect this configuration use `--check-cfg=cfg(unknown_key, values("value"))` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default @@ -15,7 +15,7 @@ warning: unexpected `cfg` condition value: `value` LL | #[cfg(target_vendor = "value")] | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` + = note: expected values for `target_vendor` are: `amd`, `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `mti`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `unk` diff --git a/tests/ui/check-cfg/exhaustive-names-values.full.stderr b/tests/ui/check-cfg/exhaustive-names-values.full.stderr index 81dbbca4f8630..95af0a9092993 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.full.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.full.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `unknown_key` LL | #[cfg(unknown_key = "value")] | ^^^^^^^^^^^^^^^^^^^^^ | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `feature`, `fmt_debug`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `ub_checks`, `unix`, and `windows` + = help: expected names are: `feature` and 31 more = help: to expect this configuration use `--check-cfg=cfg(unknown_key, values("value"))` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default @@ -15,7 +15,7 @@ warning: unexpected `cfg` condition value: `value` LL | #[cfg(target_vendor = "value")] | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` + = note: expected values for `target_vendor` are: `amd`, `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `mti`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `unk` diff --git a/tests/ui/check-cfg/exhaustive-names.stderr b/tests/ui/check-cfg/exhaustive-names.stderr index d134cfcfd29bd..4d56d24acafca 100644 --- a/tests/ui/check-cfg/exhaustive-names.stderr +++ b/tests/ui/check-cfg/exhaustive-names.stderr @@ -4,7 +4,6 @@ warning: unexpected `cfg` condition name: `unknown_key` LL | #[cfg(unknown_key = "value")] | ^^^^^^^^^^^^^^^^^^^^^ | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `fmt_debug`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `ub_checks`, `unix`, and `windows` = help: to expect this configuration use `--check-cfg=cfg(unknown_key, values("value"))` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/mix.stderr b/tests/ui/check-cfg/mix.stderr index 76c7befd6d336..be4d7c7727636 100644 --- a/tests/ui/check-cfg/mix.stderr +++ b/tests/ui/check-cfg/mix.stderr @@ -44,7 +44,7 @@ warning: unexpected `cfg` condition name: `uu` LL | #[cfg_attr(uu, unix)] | ^^ | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `feature`, `fmt_debug`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `ub_checks`, `unix`, and `windows` + = help: expected names are: `feature` and 31 more = help: to expect this configuration use `--check-cfg=cfg(uu)` = note: see for more information about checking conditional configuration diff --git a/tests/ui/check-cfg/raw-keywords.edition2015.stderr b/tests/ui/check-cfg/raw-keywords.edition2015.stderr index 3ad8ebac959b7..8ca33e088fc94 100644 --- a/tests/ui/check-cfg/raw-keywords.edition2015.stderr +++ b/tests/ui/check-cfg/raw-keywords.edition2015.stderr @@ -14,7 +14,7 @@ warning: unexpected `cfg` condition name: `r#false` LL | #[cfg(r#false)] | ^^^^^^^ | - = help: expected names are: `async`, `clippy`, `debug_assertions`, `doc`, `doctest`, `edition2015`, `edition2021`, `fmt_debug`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `r#true`, `ub_checks`, `unix`, and `windows` + = help: expected names are: `async`, `edition2015`, `edition2021`, and `r#true` and 31 more = help: to expect this configuration use `--check-cfg=cfg(r#false)` = note: see for more information about checking conditional configuration diff --git a/tests/ui/check-cfg/raw-keywords.edition2021.stderr b/tests/ui/check-cfg/raw-keywords.edition2021.stderr index ff43a3326979c..cce55720bdd18 100644 --- a/tests/ui/check-cfg/raw-keywords.edition2021.stderr +++ b/tests/ui/check-cfg/raw-keywords.edition2021.stderr @@ -14,7 +14,7 @@ warning: unexpected `cfg` condition name: `r#false` LL | #[cfg(r#false)] | ^^^^^^^ | - = help: expected names are: `r#async`, `clippy`, `debug_assertions`, `doc`, `doctest`, `edition2015`, `edition2021`, `fmt_debug`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `r#true`, `ub_checks`, `unix`, and `windows` + = help: expected names are: `r#async`, `edition2015`, `edition2021`, and `r#true` and 31 more = help: to expect this configuration use `--check-cfg=cfg(r#false)` = note: see for more information about checking conditional configuration diff --git a/tests/ui/check-cfg/report-in-external-macros.cargo.stderr b/tests/ui/check-cfg/report-in-external-macros.cargo.stderr index b82a09917f4ed..989a01f224412 100644 --- a/tests/ui/check-cfg/report-in-external-macros.cargo.stderr +++ b/tests/ui/check-cfg/report-in-external-macros.cargo.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `my_lib_cfg` LL | cfg_macro::my_lib_macro!(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `feature`, `fmt_debug`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `ub_checks`, `unix`, and `windows` + = help: expected names are: `feature` and 31 more = note: using a cfg inside a macro will use the cfgs from the destination crate and not the ones from the defining crate = help: try referring to `cfg_macro::my_lib_macro` crate for guidance on how handle this unexpected cfg = help: the macro `cfg_macro::my_lib_macro` may come from an old version of the `cfg_macro` crate, try updating your dependency with `cargo update -p cfg_macro` diff --git a/tests/ui/check-cfg/report-in-external-macros.rustc.stderr b/tests/ui/check-cfg/report-in-external-macros.rustc.stderr index 85d84a1e1ee31..95d10e014f33b 100644 --- a/tests/ui/check-cfg/report-in-external-macros.rustc.stderr +++ b/tests/ui/check-cfg/report-in-external-macros.rustc.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `my_lib_cfg` LL | cfg_macro::my_lib_macro!(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `feature`, `fmt_debug`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `ub_checks`, `unix`, and `windows` + = help: expected names are: `feature` and 31 more = note: using a cfg inside a macro will use the cfgs from the destination crate and not the ones from the defining crate = help: try referring to `cfg_macro::my_lib_macro` crate for guidance on how handle this unexpected cfg = help: to expect this configuration use `--check-cfg=cfg(my_lib_cfg)` diff --git a/tests/ui/check-cfg/stmt-no-ice.stderr b/tests/ui/check-cfg/stmt-no-ice.stderr index d8c6b0f3cec8f..3cf43c6aece1c 100644 --- a/tests/ui/check-cfg/stmt-no-ice.stderr +++ b/tests/ui/check-cfg/stmt-no-ice.stderr @@ -4,7 +4,6 @@ warning: unexpected `cfg` condition name: `crossbeam_loom` LL | #[cfg(crossbeam_loom)] | ^^^^^^^^^^^^^^ | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `fmt_debug`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `ub_checks`, `unix`, and `windows` = help: to expect this configuration use `--check-cfg=cfg(crossbeam_loom)` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr index 70fec8a350aa1..70852423bdbef 100644 --- a/tests/ui/check-cfg/target_feature.stderr +++ b/tests/ui/check-cfg/target_feature.stderr @@ -127,6 +127,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `isa-68881` `isa-68882` `jsconv` +`kl` `lahfsahf` `lasx` `lbt` @@ -202,6 +203,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `sme-lutv2` `sme2` `sme2p1` +`soft-float` `spe` `ssbs` `sse` @@ -270,6 +272,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `vsx` `wfxt` `wide-arithmetic` +`widekl` `x87` `xop` `xsave` diff --git a/tests/ui/check-cfg/unexpected-cfg-name.stderr b/tests/ui/check-cfg/unexpected-cfg-name.stderr index 4ca7209cc07b3..7708d9e3cd43a 100644 --- a/tests/ui/check-cfg/unexpected-cfg-name.stderr +++ b/tests/ui/check-cfg/unexpected-cfg-name.stderr @@ -14,7 +14,6 @@ warning: unexpected `cfg` condition name: `test` LL | #[cfg(test)] | ^^^^ | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `fmt_debug`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `ub_checks`, `unix`, and `windows` = help: to expect this configuration use `--check-cfg=cfg(test)` = note: see for more information about checking conditional configuration diff --git a/tests/ui/check-cfg/well-known-names.rs b/tests/ui/check-cfg/well-known-names.rs index b84710ca8399f..39fa3a37d1adc 100644 --- a/tests/ui/check-cfg/well-known-names.rs +++ b/tests/ui/check-cfg/well-known-names.rs @@ -2,7 +2,12 @@ // //@ check-pass //@ no-auto-check-cfg -//@ compile-flags: --check-cfg=cfg() +//@ compile-flags: --check-cfg=cfg() -Zcheck-cfg-all-expected +//@ normalize-stderr: "`, `" -> "`\n`" + +#[cfg(list_all_well_known_cfgs)] +//~^ WARNING unexpected `cfg` condition name +fn in_diagnostics() {} #[cfg(target_oz = "linux")] //~^ WARNING unexpected `cfg` condition name diff --git a/tests/ui/check-cfg/well-known-names.stderr b/tests/ui/check-cfg/well-known-names.stderr index 61d518627baea..4edf608589d5c 100644 --- a/tests/ui/check-cfg/well-known-names.stderr +++ b/tests/ui/check-cfg/well-known-names.stderr @@ -1,29 +1,68 @@ +warning: unexpected `cfg` condition name: `list_all_well_known_cfgs` + --> $DIR/well-known-names.rs:8:7 + | +LL | #[cfg(list_all_well_known_cfgs)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: expected names are: `clippy` +`contract_checks` +`debug_assertions` +`doc` +`doctest` +`fmt_debug` +`miri` +`overflow_checks` +`panic` +`proc_macro` +`relocation_model` +`rustfmt` +`sanitize` +`sanitizer_cfi_generalize_pointers` +`sanitizer_cfi_normalize_integers` +`target_abi` +`target_arch` +`target_endian` +`target_env` +`target_family` +`target_feature` +`target_has_atomic` +`target_has_atomic_equal_alignment` +`target_has_atomic_load_store` +`target_os` +`target_pointer_width` +`target_thread_local` +`target_vendor` +`ub_checks` +`unix`, and `windows` + = help: to expect this configuration use `--check-cfg=cfg(list_all_well_known_cfgs)` + = note: see for more information about checking conditional configuration + = note: `#[warn(unexpected_cfgs)]` on by default + warning: unexpected `cfg` condition name: `target_oz` - --> $DIR/well-known-names.rs:7:7 + --> $DIR/well-known-names.rs:12:7 | LL | #[cfg(target_oz = "linux")] | ^^^^^^^^^^^^^^^^^^^ | = help: to expect this configuration use `--check-cfg=cfg(target_oz, values("linux"))` = note: see for more information about checking conditional configuration - = note: `#[warn(unexpected_cfgs)]` on by default help: there is a config with a similar name and value | -LL | #[cfg(target_os = "linux")] - | ~~~~~~~~~ +LL - #[cfg(target_oz = "linux")] +LL + #[cfg(target_os = "linux")] + | warning: unexpected `cfg` condition name: `features` - --> $DIR/well-known-names.rs:14:7 + --> $DIR/well-known-names.rs:19:7 | LL | #[cfg(features = "foo")] | ^^^^^^^^^^^^^^^^ | - = help: expected names are: `clippy`, `debug_assertions`, `doc`, `doctest`, `fmt_debug`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `ub_checks`, `unix`, and `windows` = help: to expect this configuration use `--check-cfg=cfg(features, values("foo"))` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `feature` - --> $DIR/well-known-names.rs:18:7 + --> $DIR/well-known-names.rs:23:7 | LL | #[cfg(feature = "foo")] | ^^^^^^^^^^^^^^^ @@ -32,7 +71,7 @@ LL | #[cfg(feature = "foo")] = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `uniw` - --> $DIR/well-known-names.rs:22:7 + --> $DIR/well-known-names.rs:27:7 | LL | #[cfg(uniw)] | ^^^^ help: there is a config with a similar name: `unix` @@ -40,5 +79,5 @@ LL | #[cfg(uniw)] = help: to expect this configuration use `--check-cfg=cfg(uniw)` = note: see for more information about checking conditional configuration -warning: 4 warnings emitted +warning: 5 warnings emitted diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index ab69938f51ae6..ba1900fcddb2d 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -129,7 +129,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_abi = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_abi` are: ``, `abi64`, `abiv2`, `abiv2hf`, `eabi`, `eabihf`, `fortanix`, `ilp32`, `llvm`, `macabi`, `sim`, `softfloat`, `spe`, `uwp`, `vec-extabi`, and `x32` + = note: expected values for `target_abi` are: ``, `abi64`, `abiv2`, `abiv2hf`, `eabi`, `eabihf`, `fortanix`, `ilp32`, `ilp32e`, `llvm`, `macabi`, `sim`, `softfloat`, `spe`, `uwp`, `vec-extabi`, and `x32` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -138,7 +138,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_arch = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_arch` are: `aarch64`, `arm`, `arm64ec`, `avr`, `bpf`, `csky`, `hexagon`, `loongarch64`, `m68k`, `mips`, `mips32r6`, `mips64`, `mips64r6`, `msp430`, `nvptx64`, `powerpc`, `powerpc64`, `riscv32`, `riscv64`, `s390x`, `sparc`, `sparc64`, `wasm32`, `wasm64`, `x86`, `x86_64`, and `xtensa` + = note: expected values for `target_arch` are: `aarch64`, `amdgpu`, `arm`, `arm64ec`, `avr`, `bpf`, `csky`, `hexagon`, `loongarch64`, `m68k`, `mips`, `mips32r6`, `mips64`, `mips64r6`, `msp430`, `nvptx64`, `powerpc`, `powerpc64`, `riscv32`, `riscv64`, `s390x`, `sparc`, `sparc64`, `wasm32`, `wasm64`, `x86`, `x86_64`, and `xtensa` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -156,7 +156,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_env = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_env` are: ``, `gnu`, `msvc`, `musl`, `newlib`, `nto70`, `nto71`, `ohos`, `p1`, `p2`, `relibc`, `sgx`, and `uclibc` + = note: expected values for `target_env` are: ``, `gnu`, `msvc`, `musl`, `newlib`, `nto70`, `nto71`, `nto71_iosock`, `nto80`, `ohos`, `p1`, `p2`, `relibc`, `sgx`, and `uclibc` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -201,7 +201,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_os = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_os` are: `aix`, `android`, `cuda`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` + = note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -230,7 +230,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_vendor = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_vendor` are: `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` + = note: expected values for `target_vendor` are: `amd`, `apple`, `espressif`, `fortanix`, `ibm`, `kmc`, `mti`, `nintendo`, `nvidia`, `pc`, `risc0`, `sony`, `sun`, `unikraft`, `unknown`, `uwp`, `win7`, and `wrs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` @@ -274,7 +274,7 @@ LL | #[cfg(target_os = "linuz")] // testing that we suggest `linux` | | | help: there is a expected value with a similar name: `"linux"` | - = note: expected values for `target_os` are: `aix`, `android`, `cuda`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` + = note: expected values for `target_os` are: `aix`, `amdhsa`, `android`, `cuda`, `cygwin`, `dragonfly`, `emscripten`, `espidf`, `freebsd`, `fuchsia`, `haiku`, `hermit`, `horizon`, `hurd`, `illumos`, `ios`, `l4re`, `linux`, `macos`, `netbsd`, `none`, `nto`, `nuttx`, `openbsd`, `psp`, `psx`, `redox`, `rtems`, `solaris`, `solid_asp3`, `teeos`, `trusty`, `tvos`, `uefi`, `unknown`, `visionos`, `vita`, `vxworks`, `wasi`, `watchos`, `windows`, `xous`, and `zkvm` = note: see for more information about checking conditional configuration warning: 28 warnings emitted diff --git a/tests/ui/cleanup-rvalue-temp-during-incomplete-alloc.rs b/tests/ui/cleanup-rvalue-temp-during-incomplete-alloc.rs index 80c5a8fe0995f..4c59df24e4b3f 100644 --- a/tests/ui/cleanup-rvalue-temp-during-incomplete-alloc.rs +++ b/tests/ui/cleanup-rvalue-temp-during-incomplete-alloc.rs @@ -20,7 +20,7 @@ // It's unclear how likely such a bug is to recur, but it seems like a // scenario worth testing. -//@ ignore-emscripten no threads support +//@ needs-threads use std::thread; diff --git a/tests/ui/closures/2229_closure_analysis/bad-pattern.stderr b/tests/ui/closures/2229_closure_analysis/bad-pattern.stderr index b5ad8eb790f2b..ced582c9ff5a0 100644 --- a/tests/ui/closures/2229_closure_analysis/bad-pattern.stderr +++ b/tests/ui/closures/2229_closure_analysis/bad-pattern.stderr @@ -109,7 +109,7 @@ LL | let PAT = v1; help: introduce a variable instead | LL | let PAT_var = v1; - | ~~~~~~~ + | ++++ error: aborting due to 7 previous errors diff --git a/tests/ui/closures/2229_closure_analysis/issue-118144.stderr b/tests/ui/closures/2229_closure_analysis/issue-118144.stderr index 87084e6023729..bfe4afc4b58cb 100644 --- a/tests/ui/closures/2229_closure_analysis/issue-118144.stderr +++ b/tests/ui/closures/2229_closure_analysis/issue-118144.stderr @@ -9,7 +9,7 @@ LL | V(x) = func_arg; help: consider dereferencing to access the inner value using the Deref trait | LL | V(x) = &*func_arg; - | ~~~~~~~~~~ + | ++ error: aborting due to 1 previous error diff --git a/tests/ui/closures/2229_closure_analysis/issue-87987.rs b/tests/ui/closures/2229_closure_analysis/issue-87987.rs deleted file mode 100644 index f79a8f1b57100..0000000000000 --- a/tests/ui/closures/2229_closure_analysis/issue-87987.rs +++ /dev/null @@ -1,27 +0,0 @@ -//@ run-pass -//@ edition:2021 - -struct Props { - field_1: u32, //~ WARNING: fields `field_1` and `field_2` are never read - field_2: u32, -} - -fn main() { - // Test 1 - let props_2 = Props { field_1: 1, field_2: 1 }; - - let _ = || { - let _: Props = props_2; - }; - - // Test 2 - let mut arr = [1, 3, 4, 5]; - - let mref = &mut arr; - - let _c = || match arr { - [_, _, _, _] => println!("A"), - }; - - println!("{:#?}", mref); -} diff --git a/tests/ui/closures/2229_closure_analysis/issue-87987.stderr b/tests/ui/closures/2229_closure_analysis/issue-87987.stderr deleted file mode 100644 index 5696a010c3f80..0000000000000 --- a/tests/ui/closures/2229_closure_analysis/issue-87987.stderr +++ /dev/null @@ -1,14 +0,0 @@ -warning: fields `field_1` and `field_2` are never read - --> $DIR/issue-87987.rs:5:5 - | -LL | struct Props { - | ----- fields in this struct -LL | field_1: u32, - | ^^^^^^^ -LL | field_2: u32, - | ^^^^^^^ - | - = note: `#[warn(dead_code)]` on by default - -warning: 1 warning emitted - diff --git a/tests/ui/closures/2229_closure_analysis/unresolvable-upvar-issue-87987.rs b/tests/ui/closures/2229_closure_analysis/unresolvable-upvar-issue-87987.rs new file mode 100644 index 0000000000000..c501e034c97db --- /dev/null +++ b/tests/ui/closures/2229_closure_analysis/unresolvable-upvar-issue-87987.rs @@ -0,0 +1,46 @@ +//! When a closure syntactically captures a place, but doesn't actually capture +//! it, make sure MIR building doesn't ICE when handling that place. +//! +//! Under the Rust 2021 disjoint capture rules, this sort of non-capture can +//! occur when a place is only inspected by infallible non-binding patterns. + +// FIXME(#135985): On its own, this test should probably just be check-pass. +// But there are few/no other tests that use non-binding array patterns and +// invoke the later parts of the compiler, so building/running has some value. + +//@ run-pass +//@ edition:2021 + +#[expect(dead_code)] +struct Props { + field_1: u32, + field_2: u32, +} + +fn main() { + // Test 1 + let props_2 = Props { field_1: 1, field_2: 1 }; + + let _ = || { + let _: Props = props_2; + }; + + // Test 2 + let mut arr = [1, 3, 4, 5]; + + let mref = &mut arr; + + // These array patterns don't need to inspect the array, so the array + // isn't captured. + let _c = || match arr { + [_, _, _, _] => println!("C"), + }; + let _d = || match arr { + [_, .., _] => println!("D"), + }; + let _e = || match arr { + [_, ..] => println!("E"), + }; + + println!("{:#?}", mref); +} diff --git a/tests/ui/closures/binder/forbid_ambig_const_infers.rs b/tests/ui/closures/binder/forbid_ambig_const_infers.rs new file mode 100644 index 0000000000000..e9d783711ee3e --- /dev/null +++ b/tests/ui/closures/binder/forbid_ambig_const_infers.rs @@ -0,0 +1,9 @@ +#![feature(generic_arg_infer, closure_lifetime_binder)] + +struct Foo([u32; N]); + +fn main() { + let c = for<'a> |b: &'a Foo<_>| -> u32 { b.0[0] }; + //~^ ERROR: implicit types in closure signatures are forbidden when `for<...>` is present + c(&Foo([1_u32; 1])); +} diff --git a/tests/ui/closures/binder/forbid_ambig_const_infers.stderr b/tests/ui/closures/binder/forbid_ambig_const_infers.stderr new file mode 100644 index 0000000000000..396c9e8c916ed --- /dev/null +++ b/tests/ui/closures/binder/forbid_ambig_const_infers.stderr @@ -0,0 +1,10 @@ +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/forbid_ambig_const_infers.rs:6:33 + | +LL | let c = for<'a> |b: &'a Foo<_>| -> u32 { b.0[0] }; + | ------- ^ + | | + | `for<...>` is here + +error: aborting due to 1 previous error + diff --git a/tests/ui/closures/binder/forbid_ambig_type_infers.rs b/tests/ui/closures/binder/forbid_ambig_type_infers.rs new file mode 100644 index 0000000000000..4e717ef3a179d --- /dev/null +++ b/tests/ui/closures/binder/forbid_ambig_type_infers.rs @@ -0,0 +1,9 @@ +#![feature(generic_arg_infer, closure_lifetime_binder)] + +struct Foo(T); + +fn main() { + let c = for<'a> |b: &'a Foo<_>| -> u32 { b.0 }; + //~^ ERROR: implicit types in closure signatures are forbidden when `for<...>` is present + c(&Foo(1_u32)); +} diff --git a/tests/ui/closures/binder/forbid_ambig_type_infers.stderr b/tests/ui/closures/binder/forbid_ambig_type_infers.stderr new file mode 100644 index 0000000000000..8f19d710073ba --- /dev/null +++ b/tests/ui/closures/binder/forbid_ambig_type_infers.stderr @@ -0,0 +1,10 @@ +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/forbid_ambig_type_infers.rs:6:33 + | +LL | let c = for<'a> |b: &'a Foo<_>| -> u32 { b.0 }; + | ------- ^ + | | + | `for<...>` is here + +error: aborting due to 1 previous error + diff --git a/tests/ui/closures/binder/forbid_const_infer.rs b/tests/ui/closures/binder/forbid_const_infer.rs new file mode 100644 index 0000000000000..f5b8bf188dfee --- /dev/null +++ b/tests/ui/closures/binder/forbid_const_infer.rs @@ -0,0 +1,7 @@ +#![feature(generic_arg_infer, closure_lifetime_binder)] + +fn main() { + let c = for<'a> |b: &'a [u32; _]| -> u32 { b[0] }; + //~^ ERROR: implicit types in closure signatures are forbidden when `for<...>` is present + c(&[1_u32; 2]); +} diff --git a/tests/ui/closures/binder/forbid_const_infer.stderr b/tests/ui/closures/binder/forbid_const_infer.stderr new file mode 100644 index 0000000000000..e93685d400e20 --- /dev/null +++ b/tests/ui/closures/binder/forbid_const_infer.stderr @@ -0,0 +1,10 @@ +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/forbid_const_infer.rs:4:35 + | +LL | let c = for<'a> |b: &'a [u32; _]| -> u32 { b[0] }; + | ------- ^ + | | + | `for<...>` is here + +error: aborting due to 1 previous error + diff --git a/tests/ui/closures/binder/nested-closures-regions.stderr b/tests/ui/closures/binder/nested-closures-regions.stderr index a30339ac67b1c..909cbcaa808a5 100644 --- a/tests/ui/closures/binder/nested-closures-regions.stderr +++ b/tests/ui/closures/binder/nested-closures-regions.stderr @@ -9,7 +9,7 @@ LL | for<'a> || -> () { for<'c> |_: &'a ()| -> () {}; }; extern "rust-call" fn((&(),)), (), ] - = note: late-bound region is '?4 + = note: late-bound region is '?3 = note: late-bound region is '?2 = note: number of external vids: 3 = note: where '?1: '?2 @@ -26,7 +26,7 @@ LL | for<'a> || -> () { for<'c> |_: &'a ()| -> () {}; }; extern "rust-call" fn(()), (), ] - = note: late-bound region is '?2 + = note: late-bound region is '?1 note: no external requirements --> $DIR/nested-closures-regions.rs:7:1 diff --git a/tests/ui/closures/closure-expected-type/expect-region-supply-region-2.polonius.stderr b/tests/ui/closures/closure-expected-type/expect-region-supply-region-2.polonius.stderr deleted file mode 100644 index 8846ccef34e2b..0000000000000 --- a/tests/ui/closures/closure-expected-type/expect-region-supply-region-2.polonius.stderr +++ /dev/null @@ -1,37 +0,0 @@ -error: lifetime may not live long enough - --> $DIR/expect-region-supply-region-2.rs:14:30 - | -LL | fn expect_bound_supply_named<'x>() { - | -- lifetime `'x` defined here -... -LL | closure_expecting_bound(|x: &'x u32| { - | ^ - let's call the lifetime of this reference `'1` - | | - | requires that `'1` must outlive `'x` - -error[E0521]: borrowed data escapes outside of closure - --> $DIR/expect-region-supply-region-2.rs:20:9 - | -LL | let mut f: Option<&u32> = None; - | ----- `f` declared here, outside of the closure body -... -LL | closure_expecting_bound(|x: &'x u32| { - | - `x` is a reference that is only valid in the closure body -... -LL | f = Some(x); - | ^^^^^^^^^^^ `x` escapes the closure body here - -error: lifetime may not live long enough - --> $DIR/expect-region-supply-region-2.rs:14:30 - | -LL | fn expect_bound_supply_named<'x>() { - | -- lifetime `'x` defined here -... -LL | closure_expecting_bound(|x: &'x u32| { - | ^ requires that `'x` must outlive `'static` - | - = help: consider replacing `'x` with `'static` - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/closures/diverging-closure.rs b/tests/ui/closures/diverging-closure.rs index dda829d8af42e..2c86f55cf2595 100644 --- a/tests/ui/closures/diverging-closure.rs +++ b/tests/ui/closures/diverging-closure.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:oops -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { let func = || -> ! { diff --git a/tests/ui/closures/eager-mono-with-normalizable-upvars.rs b/tests/ui/closures/eager-mono-with-normalizable-upvars.rs new file mode 100644 index 0000000000000..4d934f7a7a9ad --- /dev/null +++ b/tests/ui/closures/eager-mono-with-normalizable-upvars.rs @@ -0,0 +1,19 @@ +//@ compile-flags: -Clink-dead-code -Csymbol-mangling-version=v0 +//@ build-pass + +// Ensure that when eagerly collecting `test::{closure#0}`, we don't try +// collecting an unnormalized version of the closure (specifically its +// upvars), since the closure captures the RPIT `opaque::{opaque#0}`. + +fn opaque() -> impl Sized {} + +fn test() -> impl FnOnce() { + let opaque = opaque(); + move || { + let opaque = opaque; + } +} + +fn main() { + test()(); +} diff --git a/tests/ui/closures/issue-78720.stderr b/tests/ui/closures/issue-78720.stderr index 5d65c87b0fd61..90672cd83d7cf 100644 --- a/tests/ui/closures/issue-78720.stderr +++ b/tests/ui/closures/issue-78720.stderr @@ -16,7 +16,7 @@ LL | _func: F, help: a trait with a similar name exists | LL | _func: Fn, - | ~~ + | + help: you might be missing a type parameter | LL | struct Map2 { diff --git a/tests/ui/closures/multiple-fn-bounds.stderr b/tests/ui/closures/multiple-fn-bounds.stderr index 861b39b4d076e..9b824fa0eefbd 100644 --- a/tests/ui/closures/multiple-fn-bounds.stderr +++ b/tests/ui/closures/multiple-fn-bounds.stderr @@ -21,8 +21,9 @@ LL | fn foo bool + Fn(char) -> bool>(f: F) { | ^^^^^^^^^^^^^^^^ required by this bound in `foo` help: consider adjusting the signature so it does not borrow its argument | -LL | foo(move |char| v); - | ~~~~ +LL - foo(move |x| v); +LL + foo(move |char| v); + | error: aborting due to 1 previous error diff --git a/tests/ui/closures/return-type-doesnt-match-bound.rs b/tests/ui/closures/return-type-doesnt-match-bound.rs new file mode 100644 index 0000000000000..af15385d4b07f --- /dev/null +++ b/tests/ui/closures/return-type-doesnt-match-bound.rs @@ -0,0 +1,25 @@ +use std::error::Error; +use std::process::exit; + +fn foo(f: F) -> () +where + F: FnOnce() -> Result<(), Box>, +{ + f().or_else(|e| -> ! { //~ ERROR to return + eprintln!("{:?}", e); + exit(1) + }); +} + +fn bar(f: F) -> () +where + F: FnOnce() -> Result<(), Box>, +{ + let c = |e| -> ! { //~ ERROR to return + eprintln!("{:?}", e); + exit(1) + }; + f().or_else(c); +} + +fn main() {} diff --git a/tests/ui/closures/return-type-doesnt-match-bound.stderr b/tests/ui/closures/return-type-doesnt-match-bound.stderr new file mode 100644 index 0000000000000..c0bc2f6084c9c --- /dev/null +++ b/tests/ui/closures/return-type-doesnt-match-bound.stderr @@ -0,0 +1,37 @@ +error[E0271]: expected `{closure@return-type-doesnt-match-bound.rs:8:17}` to return `Result<(), _>`, but it returns `!` + --> $DIR/return-type-doesnt-match-bound.rs:8:24 + | +LL | f().or_else(|e| -> ! { + | ------- -------^ + | | | | + | | | expected `Result<(), _>`, found `!` + | | this closure + | required by a bound introduced by this call + | + = note: expected enum `Result<(), _>` + found type `!` +note: required by a bound in `Result::::or_else` + --> $SRC_DIR/core/src/result.rs:LL:COL + +error[E0271]: expected `{closure@return-type-doesnt-match-bound.rs:18:13}` to return `Result<(), _>`, but it returns `!` + --> $DIR/return-type-doesnt-match-bound.rs:18:20 + | +LL | let c = |e| -> ! { + | -------^ + | | | + | | expected `Result<(), _>`, found `!` + | this closure +... +LL | f().or_else(c); + | ------- - closure used here + | | + | required by a bound introduced by this call + | + = note: expected enum `Result<(), _>` + found type `!` +note: required by a bound in `Result::::or_else` + --> $SRC_DIR/core/src/result.rs:LL:COL + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/closures/supertrait-hint-references-assoc-ty.rs b/tests/ui/closures/supertrait-hint-references-assoc-ty.rs index fa74ffc5bec51..b6a1685cb7206 100644 --- a/tests/ui/closures/supertrait-hint-references-assoc-ty.rs +++ b/tests/ui/closures/supertrait-hint-references-assoc-ty.rs @@ -1,4 +1,7 @@ //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver pub trait Fn0: Fn(i32) -> Self::Out { type Out; diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.stderr index 120d5cc5293b4..63260b5c78fdc 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/gate_test.stderr @@ -1,4 +1,4 @@ -error[E0658]: C-cmse-nonsecure-call ABI is experimental and subject to change +error[E0658]: the extern "C-cmse-nonsecure-call" ABI is experimental and subject to change --> $DIR/gate_test.rs:5:46 | LL | core::mem::transmute:: i32>( diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs index da1327dace5d4..31782b05105f6 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.rs @@ -39,4 +39,4 @@ type WithTransparentTraitObject = //~^ ERROR return value of `"C-cmse-nonsecure-call"` function too large to pass via registers [E0798] type WithVarArgs = extern "C-cmse-nonsecure-call" fn(u32, ...); -//~^ ERROR C-variadic function must have a compatible calling convention, like `C`, `cdecl`, `system`, `aapcs`, `win64`, `sysv64` or `efiapi` [E0045] +//~^ ERROR C-variadic function must have a compatible calling convention, like `C` diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr index f20e67e3d9438..0560f0eec1c02 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/generics.stderr @@ -14,8 +14,9 @@ LL | f1: extern "C-cmse-nonsecure-call" fn(U, u32, u32, u32) -> u64 | help: a type parameter with a similar name exists | -LL | f1: extern "C-cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, - | ~ +LL - f1: extern "C-cmse-nonsecure-call" fn(U, u32, u32, u32) -> u64, +LL + f1: extern "C-cmse-nonsecure-call" fn(T, u32, u32, u32) -> u64, + | help: you might be missing a type parameter | LL | struct Test { diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs index e6af1d60e773f..b052aabb499e7 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs @@ -1,12 +1,12 @@ //@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib //@ needs-llvm-components: arm +//@ add-core-stubs + #![feature(abi_c_cmse_nonsecure_call, no_core, lang_items)] #![no_core] -#[lang = "sized"] -pub trait Sized {} -#[lang = "copy"] -pub trait Copy {} -impl Copy for u32 {} + +extern crate minicore; +use minicore::*; #[repr(C)] pub struct ReprCU64(u64); diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/gate_test.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/gate_test.stderr index dabf16cab3098..0afbbe647af0c 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/gate_test.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/gate_test.stderr @@ -1,4 +1,4 @@ -error[E0658]: C-cmse-nonsecure-entry ABI is experimental and subject to change +error[E0658]: the extern "C-cmse-nonsecure-entry" ABI is experimental and subject to change --> $DIR/gate_test.rs:4:12 | LL | pub extern "C-cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 { diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs index 5746d14f9b1a4..23d55526e5781 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs @@ -1,13 +1,12 @@ //@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib //@ needs-llvm-components: arm +//@ add-core-stubs + #![feature(cmse_nonsecure_entry, no_core, lang_items)] #![no_core] -#[lang = "sized"] -pub trait Sized {} -#[lang = "copy"] -pub trait Copy {} -impl Copy for u32 {} -impl Copy for u8 {} + +extern crate minicore; +use minicore::*; #[repr(C)] pub struct ReprCU64(u64); diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.stderr index 9c885d9531814..d37d9b5e8ff7d 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.stderr @@ -1,5 +1,5 @@ error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/return-via-stack.rs:25:48 + --> $DIR/return-via-stack.rs:24:48 | LL | pub extern "C-cmse-nonsecure-entry" fn f1() -> ReprCU64 { | ^^^^^^^^ this type doesn't fit in the available registers @@ -8,7 +8,7 @@ LL | pub extern "C-cmse-nonsecure-entry" fn f1() -> ReprCU64 { = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/return-via-stack.rs:30:48 + --> $DIR/return-via-stack.rs:29:48 | LL | pub extern "C-cmse-nonsecure-entry" fn f2() -> ReprCBytes { | ^^^^^^^^^^ this type doesn't fit in the available registers @@ -17,7 +17,7 @@ LL | pub extern "C-cmse-nonsecure-entry" fn f2() -> ReprCBytes { = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/return-via-stack.rs:35:48 + --> $DIR/return-via-stack.rs:34:48 | LL | pub extern "C-cmse-nonsecure-entry" fn f3() -> U64Compound { | ^^^^^^^^^^^ this type doesn't fit in the available registers @@ -26,7 +26,7 @@ LL | pub extern "C-cmse-nonsecure-entry" fn f3() -> U64Compound { = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/return-via-stack.rs:40:48 + --> $DIR/return-via-stack.rs:39:48 | LL | pub extern "C-cmse-nonsecure-entry" fn f4() -> ReprCAlign16 { | ^^^^^^^^^^^^ this type doesn't fit in the available registers @@ -35,7 +35,7 @@ LL | pub extern "C-cmse-nonsecure-entry" fn f4() -> ReprCAlign16 { = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/return-via-stack.rs:47:48 + --> $DIR/return-via-stack.rs:46:48 | LL | pub extern "C-cmse-nonsecure-entry" fn f5() -> [u8; 5] { | ^^^^^^^ this type doesn't fit in the available registers @@ -44,7 +44,7 @@ LL | pub extern "C-cmse-nonsecure-entry" fn f5() -> [u8; 5] { = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/return-via-stack.rs:53:50 + --> $DIR/return-via-stack.rs:52:50 | LL | pub extern "C-cmse-nonsecure-entry" fn u128() -> u128 { | ^^^^ this type doesn't fit in the available registers @@ -53,7 +53,7 @@ LL | pub extern "C-cmse-nonsecure-entry" fn u128() -> u128 { = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/return-via-stack.rs:59:50 + --> $DIR/return-via-stack.rs:58:50 | LL | pub extern "C-cmse-nonsecure-entry" fn i128() -> i128 { | ^^^^ this type doesn't fit in the available registers @@ -62,7 +62,7 @@ LL | pub extern "C-cmse-nonsecure-entry" fn i128() -> i128 { = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/return-via-stack.rs:76:56 + --> $DIR/return-via-stack.rs:75:56 | LL | pub extern "C-cmse-nonsecure-entry" fn union_rust() -> ReprRustUnionU64 { | ^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers @@ -71,7 +71,7 @@ LL | pub extern "C-cmse-nonsecure-entry" fn union_rust() -> ReprRustUnionU64 { = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"C-cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/return-via-stack.rs:81:53 + --> $DIR/return-via-stack.rs:80:53 | LL | pub extern "C-cmse-nonsecure-entry" fn union_c() -> ReprCUnionU64 { | ^^^^^^^^^^^^^ this type doesn't fit in the available registers diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.stderr deleted file mode 100644 index 77379f7049d02..0000000000000 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target - --> $DIR/trustzone-only.rs:5:1 - | -LL | pub extern "C-cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0570`. diff --git a/tests/ui/issues/issue-40883.rs b/tests/ui/codegen/StackColoring-not-blowup-stack-issue-40883.rs similarity index 100% rename from tests/ui/issues/issue-40883.rs rename to tests/ui/codegen/StackColoring-not-blowup-stack-issue-40883.rs diff --git a/tests/ui/codegen/issue-99551.rs b/tests/ui/codegen/issue-99551.rs index 9bacbaa6edc72..f498718310d82 100644 --- a/tests/ui/codegen/issue-99551.rs +++ b/tests/ui/codegen/issue-99551.rs @@ -1,5 +1,4 @@ //@ build-pass -#![feature(trait_upcasting)] pub trait A {} pub trait B {} diff --git a/tests/ui/codemap_tests/two_files.stderr b/tests/ui/codemap_tests/two_files.stderr index d833d4944bfe9..3b542fdbd33fc 100644 --- a/tests/ui/codemap_tests/two_files.stderr +++ b/tests/ui/codemap_tests/two_files.stderr @@ -7,7 +7,8 @@ LL | impl Bar for Baz { } help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias --> $DIR/two_files_data.rs:5:1 | -LL | trait Bar = dyn Foo; +LL - type Bar = dyn Foo; +LL + trait Bar = dyn Foo; | error: aborting due to 1 previous error diff --git a/tests/ui/coercion/coerce-expect-unsized-ascribed.stderr b/tests/ui/coercion/coerce-expect-unsized-ascribed.stderr index 0c220a13876b8..b682bdac0341d 100644 --- a/tests/ui/coercion/coerce-expect-unsized-ascribed.stderr +++ b/tests/ui/coercion/coerce-expect-unsized-ascribed.stderr @@ -29,7 +29,7 @@ error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:14:27 | LL | let _ = type_ascribe!(Box::new( { |x| (x as u8) }), Box _>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Box u8>`, found `Box<{closure@coerce-expect-unsized-ascribed.rs:14:39}>` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Box u8>`, found `Box<{closure@...}>` | = note: expected struct `Box u8>` found struct `Box<{closure@$DIR/coerce-expect-unsized-ascribed.rs:14:39: 14:42}>` @@ -85,7 +85,7 @@ error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:22:27 | LL | let _ = type_ascribe!(&{ |x| (x as u8) }, &dyn Fn(i32) -> _); - | ^^^^^^^^^^^^^^^^^^ expected `&dyn Fn(i32) -> u8`, found `&{closure@coerce-expect-unsized-ascribed.rs:22:30}` + | ^^^^^^^^^^^^^^^^^^ expected `&dyn Fn(i32) -> u8`, found `&{closure@...}` | = note: expected reference `&dyn Fn(i32) -> u8` found reference `&{closure@$DIR/coerce-expect-unsized-ascribed.rs:22:30: 22:33}` @@ -123,7 +123,7 @@ error[E0308]: mismatched types --> $DIR/coerce-expect-unsized-ascribed.rs:27:27 | LL | let _ = type_ascribe!(Box::new(|x| (x as u8)), Box _>); - | ^^^^^^^^^^^^^^^^^^^^^^^ expected `Box u8>`, found `Box<{closure@coerce-expect-unsized-ascribed.rs:27:36}>` + | ^^^^^^^^^^^^^^^^^^^^^^^ expected `Box u8>`, found `Box<{closure@...}>` | = note: expected struct `Box u8>` found struct `Box<{closure@$DIR/coerce-expect-unsized-ascribed.rs:27:36: 27:39}>` diff --git a/tests/ui/coherence/auxiliary/pr_review_132289_2_lib.rs b/tests/ui/coherence/auxiliary/pr_review_132289_2_lib.rs new file mode 100644 index 0000000000000..4b79147dce109 --- /dev/null +++ b/tests/ui/coherence/auxiliary/pr_review_132289_2_lib.rs @@ -0,0 +1,37 @@ +pub type A = &'static [usize; 1]; +pub type B = &'static [usize; 100]; + +pub trait Trait

{ + type Assoc; +} + +pub type Dyn

= dyn Trait; + +pub trait LocallyUnimplemented

{} + +impl Trait

for T +where + T: LocallyUnimplemented

, +{ + type Assoc = B; +} + +trait MakeArray { + fn make() -> &'static Arr; +} +impl MakeArray<[usize; N]> for () { + fn make() -> &'static [usize; N] { + &[1337; N] + } +} + +// it would be sound for this return type to be interpreted as being +// either of A or B, if that's what a soundness fix for overlap of +// dyn Trait's impls would entail + +// In this test, we check at the call-site that the interpretation +// is consistent across crates in this specific scenario. +pub fn function

() -> ( as Trait

>::Assoc, usize) { + let val = <() as MakeArray<_>>::make(); + (val, val.len()) +} diff --git a/tests/ui/coherence/auxiliary/pr_review_132289_3_lib.rs b/tests/ui/coherence/auxiliary/pr_review_132289_3_lib.rs new file mode 100644 index 0000000000000..f90be3b2487e5 --- /dev/null +++ b/tests/ui/coherence/auxiliary/pr_review_132289_3_lib.rs @@ -0,0 +1,12 @@ +use std::ops::Index; + +pub trait Trait { + fn f(&self) + where + dyn Index<(), Output = ()>: Index<()>; + // rustc (correctly) determines ^^^^^^^^ this bound to be true +} + +pub fn call(x: &dyn Trait) { + x.f(); // so we can call `f` +} diff --git a/tests/ui/coherence/coherence-impl-trait-for-trait-dyn-compatible.stderr b/tests/ui/coherence/coherence-impl-trait-for-trait-dyn-compatible.stderr index 542be2dbc305c..033bfee226fd5 100644 --- a/tests/ui/coherence/coherence-impl-trait-for-trait-dyn-compatible.stderr +++ b/tests/ui/coherence/coherence-impl-trait-for-trait-dyn-compatible.stderr @@ -1,16 +1,17 @@ -error[E0038]: the trait `DynIncompatible` cannot be made into an object +error[E0038]: the trait `DynIncompatible` is not dyn compatible --> $DIR/coherence-impl-trait-for-trait-dyn-compatible.rs:7:26 | LL | impl DynIncompatible for dyn DynIncompatible { } - | ^^^^^^^^^^^^^^^^^^^ `DynIncompatible` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^ `DynIncompatible` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/coherence-impl-trait-for-trait-dyn-compatible.rs:6:45 | LL | trait DynIncompatible { fn eq(&self, other: Self); } | --------------- ^^^^ ...because method `eq` references the `Self` type in this parameter | | - | this trait cannot be made into an object... + | this trait is not dyn compatible... = help: consider moving `eq` to another trait error[E0046]: not all trait items implemented, missing: `eq` diff --git a/tests/ui/coherence/coherence-negative-impls-copy.rs b/tests/ui/coherence/coherence-negative-impls-copy.rs index 377d750f8baa4..c0d852180a518 100644 --- a/tests/ui/coherence/coherence-negative-impls-copy.rs +++ b/tests/ui/coherence/coherence-negative-impls-copy.rs @@ -18,7 +18,7 @@ impl !Copy for WithDrop {} struct Type; trait Trait {} -extern { +extern "C" { type ExternType; } diff --git a/tests/ui/coherence/conflicting-impl-with-err.stderr b/tests/ui/coherence/conflicting-impl-with-err.stderr index 3009b452dc7a0..75a201797b551 100644 --- a/tests/ui/coherence/conflicting-impl-with-err.stderr +++ b/tests/ui/coherence/conflicting-impl-with-err.stderr @@ -1,14 +1,18 @@ -error[E0433]: failed to resolve: use of undeclared crate or module `nope` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `nope` --> $DIR/conflicting-impl-with-err.rs:4:11 | LL | impl From for Error { - | ^^^^ use of undeclared crate or module `nope` + | ^^^^ use of unresolved module or unlinked crate `nope` + | + = help: you might be missing a crate named `nope` -error[E0433]: failed to resolve: use of undeclared crate or module `nope` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `nope` --> $DIR/conflicting-impl-with-err.rs:5:16 | LL | fn from(_: nope::Thing) -> Self { - | ^^^^ use of undeclared crate or module `nope` + | ^^^^ use of unresolved module or unlinked crate `nope` + | + = help: you might be missing a crate named `nope` error: aborting due to 2 previous errors diff --git a/tests/ui/coherence/occurs-check/associated-type.next.stderr b/tests/ui/coherence/occurs-check/associated-type.next.stderr index 466b991471ed9..25f9523f4e455 100644 --- a/tests/ui/coherence/occurs-check/associated-type.next.stderr +++ b/tests/ui/coherence/occurs-check/associated-type.next.stderr @@ -1,7 +1,5 @@ WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } error[E0119]: conflicting implementations of trait `Overlap fn(&'a (), ())>` for type `for<'a> fn(&'a (), ())` --> $DIR/associated-type.rs:32:1 | diff --git a/tests/ui/coherence/occurs-check/associated-type.old.stderr b/tests/ui/coherence/occurs-check/associated-type.old.stderr index 1e0345f4ec05b..e091ddcacb20a 100644 --- a/tests/ui/coherence/occurs-check/associated-type.old.stderr +++ b/tests/ui/coherence/occurs-check/associated-type.old.stderr @@ -1,7 +1,5 @@ WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } - WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), .. } error[E0119]: conflicting implementations of trait `Overlap fn(&'a (), ())>` for type `for<'a> fn(&'a (), ())` --> $DIR/associated-type.rs:32:1 | diff --git a/tests/ui/coherence/pr-review-132289-1.rs b/tests/ui/coherence/pr-review-132289-1.rs new file mode 100644 index 0000000000000..ab3ed9655e071 --- /dev/null +++ b/tests/ui/coherence/pr-review-132289-1.rs @@ -0,0 +1,52 @@ +// This is a regression test for issues that came up during review of the (closed) +// PR #132289; this single-crate test case is +// the first example from @steffahn during review. +// https://github.com/rust-lang/rust/pull/132289#issuecomment-2564492153 + +//@ check-pass + +type A = &'static [usize; 1]; +type B = &'static [usize; 100]; + +type DynSomething = dyn Something; + +trait Super { + type Assoc; +} +impl Super for Foo { + type Assoc = A; +} + +trait IsDynSomething {} +impl IsDynSomething for DynSomething {} + +impl Super for T +where + T: IsDynSomething, +{ + type Assoc = B; +} + +trait Something: Super { + fn method(&self) -> Self::Assoc; +} + +struct Foo; +impl Something for Foo { + fn method(&self) -> Self::Assoc { + &[1337] + } +} + +fn main() { + let x = &Foo; + let y: &DynSomething = x; + + // no surprises here + let _arr1: A = x.method(); + + // this (`_arr2`) can't ever become B either, soundly + let _arr2: A = y.method(); + // there aren't any other arrays being defined anywhere in this + // test case, besides the length-1 one containing [1337] +} diff --git a/tests/ui/coherence/pr-review-132289-2.rs b/tests/ui/coherence/pr-review-132289-2.rs new file mode 100644 index 0000000000000..95ad86c61ff19 --- /dev/null +++ b/tests/ui/coherence/pr-review-132289-2.rs @@ -0,0 +1,26 @@ +// This is a regression test for issues that came up during review of the (closed) +// PR #132289; this 2-crate test case is adapted from +// the second example from @steffahn during review. +// https://github.com/rust-lang/rust/pull/132289#issuecomment-2564587796 + +//@ run-pass +//@ aux-build: pr_review_132289_2_lib.rs + +extern crate pr_review_132289_2_lib; + +use pr_review_132289_2_lib::{function, Dyn, LocallyUnimplemented}; + +struct Param; + +impl LocallyUnimplemented for Dyn {} + +// it would be sound for `function::`'s return type to be +// either of A or B, if that's what a soundness fix for overlap of +// dyn Trait's impls would entail + +// In this test, we check at this call-site that the interpretation +// is consistent with the function definition's body. +fn main() { + let (arr, len) = function::(); + assert_eq!(arr.len(), len); +} diff --git a/tests/ui/coherence/pr-review-132289-3.rs b/tests/ui/coherence/pr-review-132289-3.rs new file mode 100644 index 0000000000000..7e597baa6ec29 --- /dev/null +++ b/tests/ui/coherence/pr-review-132289-3.rs @@ -0,0 +1,50 @@ +// This is a regression test for issues that came up during review of the (closed) +// PR #132289; this 3-ish-crate (including std) test case is adapted from +// the third example from @steffahn during review. +// https://github.com/rust-lang/rust/pull/132289#issuecomment-2564599221 + +//@ run-pass +//@ check-run-results +//@ aux-build: pr_review_132289_3_lib.rs + +extern crate pr_review_132289_3_lib; + +use std::ops::Index; + +use pr_review_132289_3_lib::{call, Trait}; + +trait SubIndex: Index {} + +struct Param; + +trait Project { + type Ty: ?Sized; +} +impl Project for () { + type Ty = dyn SubIndex; +} + +impl Index for <() as Project>::Ty { + type Output = (); + + fn index(&self, _: Param) -> &() { + &() + } +} + +struct Struct; + +impl Trait for Struct { + fn f(&self) + where + // higher-ranked to allow potentially-false bounds + for<'a> dyn Index<(), Output = ()>: Index<()>, + // after #132289 rustc used to believe this bound false + { + println!("hello!"); + } +} + +fn main() { + call(&Struct); // <- would segfault if the method `f` wasn't part of the vtable +} diff --git a/tests/ui/coherence/pr-review-132289-3.run.stdout b/tests/ui/coherence/pr-review-132289-3.run.stdout new file mode 100644 index 0000000000000..4effa19f4f75f --- /dev/null +++ b/tests/ui/coherence/pr-review-132289-3.run.stdout @@ -0,0 +1 @@ +hello! diff --git a/tests/ui/command/command-argv0.rs b/tests/ui/command/command-argv0.rs index 35625c0b334cb..0907e18b30c7c 100644 --- a/tests/ui/command/command-argv0.rs +++ b/tests/ui/command/command-argv0.rs @@ -1,8 +1,7 @@ //@ run-pass -//@ ignore-windows - this is a unix-specific test -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ only-unix (this is a unix-specific test) +//@ needs-subprocess use std::env; use std::os::unix::process::CommandExt; use std::process::Command; diff --git a/tests/ui/command/command-current-dir.rs b/tests/ui/command/command-current-dir.rs index 23269e4123182..e264cbe4d7045 100644 --- a/tests/ui/command/command-current-dir.rs +++ b/tests/ui/command/command-current-dir.rs @@ -1,7 +1,6 @@ //@ run-pass //@ no-prefer-dynamic We move the binary around, so do not depend dynamically on libstd -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess //@ ignore-fuchsia Needs directory creation privilege use std::env; diff --git a/tests/ui/command/command-exec.rs b/tests/ui/command/command-exec.rs index d2545b0b472cf..77336377e8899 100644 --- a/tests/ui/command/command-exec.rs +++ b/tests/ui/command/command-exec.rs @@ -1,13 +1,9 @@ //@ run-pass -#![allow(stable_features)] -//@ ignore-windows - this is a unix-specific test -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ only-unix (this is a unix-specific test) +//@ needs-subprocess //@ ignore-fuchsia no execvp syscall provided -#![feature(process_exec)] - use std::env; use std::os::unix::process::CommandExt; use std::process::Command; diff --git a/tests/ui/command/command-pre-exec.rs b/tests/ui/command/command-pre-exec.rs index 7242dea27759a..7299f357bd078 100644 --- a/tests/ui/command/command-pre-exec.rs +++ b/tests/ui/command/command-pre-exec.rs @@ -1,11 +1,9 @@ //@ run-pass - -#![allow(stable_features)] -//@ ignore-windows - this is a unix-specific test -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ only-unix (this is a unix-specific test) +//@ needs-subprocess //@ ignore-fuchsia no execvp syscall -#![feature(process_exec, rustc_private)] + +#![feature(rustc_private)] extern crate libc; diff --git a/tests/ui/command/command-setgroups.rs b/tests/ui/command/command-setgroups.rs index c940135d844ca..047f06729af98 100644 --- a/tests/ui/command/command-setgroups.rs +++ b/tests/ui/command/command-setgroups.rs @@ -1,9 +1,8 @@ //@ run-pass -//@ ignore-windows - this is a unix-specific test -//@ ignore-wasm32 -//@ ignore-sgx +//@ only-unix (this is a unix-specific test) //@ ignore-musl - returns dummy result for _SC_NGROUPS_MAX //@ ignore-nto - does not have `/bin/id`, expects groups to be i32 (not u32) +//@ needs-subprocess #![feature(rustc_private)] #![feature(setgroups)] diff --git a/tests/ui/command/command-uid-gid.rs b/tests/ui/command/command-uid-gid.rs index 7a70a0fbd7612..f54a0f50708ba 100644 --- a/tests/ui/command/command-uid-gid.rs +++ b/tests/ui/command/command-uid-gid.rs @@ -1,8 +1,7 @@ //@ run-pass //@ ignore-android -//@ ignore-emscripten -//@ ignore-sgx //@ ignore-fuchsia no '/bin/sh', '/bin/ls' +//@ needs-subprocess #![feature(rustc_private)] diff --git a/tests/ui/command/issue-10626.rs b/tests/ui/command/issue-10626.rs index f8dbb01151377..d2679ec9e29c8 100644 --- a/tests/ui/command/issue-10626.rs +++ b/tests/ui/command/issue-10626.rs @@ -1,6 +1,5 @@ //@ run-pass -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess // Make sure that if a process doesn't have its stdio/stderr descriptors set up // that we don't die in a large ball of fire diff --git a/tests/ui/compare-method/bad-self-type.stderr b/tests/ui/compare-method/bad-self-type.stderr index a3a31f4344717..bc1587883a352 100644 --- a/tests/ui/compare-method/bad-self-type.stderr +++ b/tests/ui/compare-method/bad-self-type.stderr @@ -9,7 +9,7 @@ LL | fn poll(self, _: &mut Context<'_>) -> Poll<()> { help: change the self-receiver type to match the trait | LL | fn poll(self: Pin<&mut MyFuture>, _: &mut Context<'_>) -> Poll<()> { - | ~~~~~~~~~~~~~~~~~~~~~~~~ + | ++++++++++++++++++++ error[E0053]: method `foo` has an incompatible type for trait --> $DIR/bad-self-type.rs:22:18 @@ -26,8 +26,9 @@ LL | fn foo(self); found signature `fn(Box)` help: change the self-receiver type to match the trait | -LL | fn foo(self) {} - | ~~~~ +LL - fn foo(self: Box) {} +LL + fn foo(self) {} + | error[E0053]: method `bar` has an incompatible type for trait --> $DIR/bad-self-type.rs:24:17 diff --git a/tests/ui/compare-method/issue-90444.stderr b/tests/ui/compare-method/issue-90444.stderr index f05c9939c0096..c69d63b3e30bc 100644 --- a/tests/ui/compare-method/issue-90444.stderr +++ b/tests/ui/compare-method/issue-90444.stderr @@ -8,8 +8,9 @@ LL | fn from(_: fn((), (), &mut ())) -> Self { found signature `fn(for<'a> fn((), (), &'a mut ())) -> A` help: change the parameter type to match the trait | -LL | fn from(_: for<'a> fn((), (), &'a ())) -> Self { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - fn from(_: fn((), (), &mut ())) -> Self { +LL + fn from(_: for<'a> fn((), (), &'a ())) -> Self { + | error[E0053]: method `from` has an incompatible type for trait --> $DIR/issue-90444.rs:11:16 @@ -21,8 +22,9 @@ LL | fn from(_: fn((), (), u64)) -> Self { found signature `fn(fn((), (), u64)) -> B` help: change the parameter type to match the trait | -LL | fn from(_: fn((), (), u32)) -> Self { - | ~~~~~~~~~~~~~~~ +LL - fn from(_: fn((), (), u64)) -> Self { +LL + fn from(_: fn((), (), u32)) -> Self { + | error: aborting due to 2 previous errors diff --git a/tests/ui/compare-method/region-extra-2.stderr b/tests/ui/compare-method/region-extra-2.stderr index 3f55f67311763..d19d4f28f12a9 100644 --- a/tests/ui/compare-method/region-extra-2.stderr +++ b/tests/ui/compare-method/region-extra-2.stderr @@ -9,8 +9,9 @@ LL | fn renew<'b: 'a>(self) -> &'b mut [T] where 'a: 'b { | help: copy the `where` clause predicates from the trait | -LL | fn renew<'b: 'a>(self) -> &'b mut [T] where 'b: 'a { - | ~~~~~~~~~~~~ +LL - fn renew<'b: 'a>(self) -> &'b mut [T] where 'a: 'b { +LL + fn renew<'b: 'a>(self) -> &'b mut [T] where 'b: 'a { + | error: aborting due to 1 previous error diff --git a/tests/ui/compare-method/reordered-type-param.stderr b/tests/ui/compare-method/reordered-type-param.stderr index 1e8266e213d7f..536364871a368 100644 --- a/tests/ui/compare-method/reordered-type-param.stderr +++ b/tests/ui/compare-method/reordered-type-param.stderr @@ -18,8 +18,9 @@ LL | fn b(&self, x: C) -> C; = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters help: change the parameter type to match the trait | -LL | fn b(&self, _x: F) -> G { panic!() } - | ~ +LL - fn b(&self, _x: G) -> G { panic!() } +LL + fn b(&self, _x: F) -> G { panic!() } + | error: aborting due to 1 previous error diff --git a/tests/ui/conditional-compilation/cfg-attr-parse.stderr b/tests/ui/conditional-compilation/cfg-attr-parse.stderr index 759df3c90c6f3..1605761e59195 100644 --- a/tests/ui/conditional-compilation/cfg-attr-parse.stderr +++ b/tests/ui/conditional-compilation/cfg-attr-parse.stderr @@ -7,8 +7,9 @@ LL | #[cfg_attr()] = note: for more information, visit help: missing condition and attribute | -LL | #[cfg_attr(condition, attribute, other_attribute, ...)] - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - #[cfg_attr()] +LL + #[cfg_attr(condition, attribute, other_attribute, ...)] + | error: expected `,`, found end of `cfg_attr` input --> $DIR/cfg-attr-parse.rs:8:17 @@ -54,8 +55,9 @@ LL | #[cfg_attr[all(),,]] | help: the delimiters should be `(` and `)` | -LL | #[cfg_attr(all(),,)] - | ~ ~ +LL - #[cfg_attr[all(),,]] +LL + #[cfg_attr(all(),,)] + | error: expected identifier, found `,` --> $DIR/cfg-attr-parse.rs:44:18 @@ -74,8 +76,9 @@ LL | #[cfg_attr{all(),,}] | help: the delimiters should be `(` and `)` | -LL | #[cfg_attr(all(),,)] - | ~ ~ +LL - #[cfg_attr{all(),,}] +LL + #[cfg_attr(all(),,)] + | error: expected identifier, found `,` --> $DIR/cfg-attr-parse.rs:50:18 diff --git a/tests/ui/conditional-compilation/cfg_accessible-not_sure.rs b/tests/ui/conditional-compilation/cfg_accessible-not_sure.rs index e357d3c6cb56f..2ac57f356740f 100644 --- a/tests/ui/conditional-compilation/cfg_accessible-not_sure.rs +++ b/tests/ui/conditional-compilation/cfg_accessible-not_sure.rs @@ -63,7 +63,7 @@ const B: bool = true; // ForeignType::unresolved - error -extern { +extern "C" { type ForeignType; } diff --git a/tests/ui/confuse-field-and-method/issue-33784.stderr b/tests/ui/confuse-field-and-method/issue-33784.stderr index 59a6f4fccd8d1..69f569e09a44d 100644 --- a/tests/ui/confuse-field-and-method/issue-33784.stderr +++ b/tests/ui/confuse-field-and-method/issue-33784.stderr @@ -10,8 +10,9 @@ LL | (p.closure)(); | + + help: there is a method `clone` with a similar name | -LL | p.clone(); - | ~~~~~ +LL - p.closure(); +LL + p.clone(); + | error[E0599]: no method named `fn_ptr` found for reference `&&Obj<{closure@$DIR/issue-33784.rs:25:43: 25:45}>` in the current scope --> $DIR/issue-33784.rs:29:7 diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.stderr index 84281eb53c946..c71ec24dda330 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.stderr +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.stderr @@ -1,32 +1,36 @@ -error[E0038]: the trait `ConstParamTy_` cannot be made into an object - --> $DIR/const_param_ty_dyn_compatibility.rs:6:12 +error[E0038]: the trait `ConstParamTy_` is not dyn compatible + --> $DIR/const_param_ty_dyn_compatibility.rs:6:16 | LL | fn foo(a: &dyn ConstParamTy_) {} - | ^^^^^^^^^^^^^^^^^ `ConstParamTy_` cannot be made into an object + | ^^^^^^^^^^^^^ `ConstParamTy_` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $SRC_DIR/core/src/cmp.rs:LL:COL | - = note: the trait cannot be made into an object because it uses `Self` as a type parameter + = note: the trait is not dyn compatible because it uses `Self` as a type parameter help: consider using an opaque type instead | -LL | fn foo(a: &impl ConstParamTy_) {} - | ~~~~ +LL - fn foo(a: &dyn ConstParamTy_) {} +LL + fn foo(a: &impl ConstParamTy_) {} + | -error[E0038]: the trait `UnsizedConstParamTy` cannot be made into an object - --> $DIR/const_param_ty_dyn_compatibility.rs:9:12 +error[E0038]: the trait `UnsizedConstParamTy` is not dyn compatible + --> $DIR/const_param_ty_dyn_compatibility.rs:9:16 | LL | fn bar(a: &dyn UnsizedConstParamTy) {} - | ^^^^^^^^^^^^^^^^^^^^^^^ `UnsizedConstParamTy` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^ `UnsizedConstParamTy` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $SRC_DIR/core/src/cmp.rs:LL:COL | - = note: the trait cannot be made into an object because it uses `Self` as a type parameter + = note: the trait is not dyn compatible because it uses `Self` as a type parameter help: consider using an opaque type instead | -LL | fn bar(a: &impl UnsizedConstParamTy) {} - | ~~~~ +LL - fn bar(a: &dyn UnsizedConstParamTy) {} +LL + fn bar(a: &impl UnsizedConstParamTy) {} + | error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/bad-subst-const-kind.stderr b/tests/ui/const-generics/bad-subst-const-kind.stderr index 5c8d9c9036356..b360526964255 100644 --- a/tests/ui/const-generics/bad-subst-const-kind.stderr +++ b/tests/ui/const-generics/bad-subst-const-kind.stderr @@ -3,6 +3,8 @@ error: the constant `N` is not of type `usize` | LL | impl Q for [u8; N] { | ^^^^^^^ expected `usize`, found `u64` + | + = note: the length of array `[u8; N]` must be type `usize` error: the constant `13` is not of type `u64` --> $DIR/bad-subst-const-kind.rs:13:24 diff --git a/tests/ui/const-generics/defaults/doesnt_infer.stderr b/tests/ui/const-generics/defaults/doesnt_infer.stderr index c17f57f36bc5f..ef39cef70cbc2 100644 --- a/tests/ui/const-generics/defaults/doesnt_infer.stderr +++ b/tests/ui/const-generics/defaults/doesnt_infer.stderr @@ -2,15 +2,13 @@ error[E0284]: type annotations needed for `Foo<_>` --> $DIR/doesnt_infer.rs:13:9 | LL | let foo = Foo::foo(); - | ^^^ ---------- type must be known at this point + | ^^^ --- type must be known at this point | -note: required by a const generic parameter in `Foo::::foo` - --> $DIR/doesnt_infer.rs:5:6 +note: required by a const generic parameter in `Foo` + --> $DIR/doesnt_infer.rs:3:12 | -LL | impl Foo { - | ^^^^^^^^^^^^ required by this const generic parameter in `Foo::::foo` -LL | fn foo() -> Self { - | --- required by a bound in this associated function +LL | struct Foo; + | ^^^^^^^^^^^^^^^^ required by this const generic parameter in `Foo` help: consider giving `foo` an explicit type, where the value of const parameter `N` is specified | LL | let foo: Foo = Foo::foo(); @@ -20,13 +18,15 @@ error[E0284]: type annotations needed for `Foo<_>` --> $DIR/doesnt_infer.rs:13:9 | LL | let foo = Foo::foo(); - | ^^^ --- type must be known at this point + | ^^^ ---------- type must be known at this point | -note: required by a const generic parameter in `Foo` - --> $DIR/doesnt_infer.rs:3:12 +note: required by a const generic parameter in `Foo::::foo` + --> $DIR/doesnt_infer.rs:5:6 | -LL | struct Foo; - | ^^^^^^^^^^^^^^^^ required by this const generic parameter in `Foo` +LL | impl Foo { + | ^^^^^^^^^^^^ required by this const generic parameter in `Foo::::foo` +LL | fn foo() -> Self { + | --- required by a bound in this associated function help: consider giving `foo` an explicit type, where the value of const parameter `N` is specified | LL | let foo: Foo = Foo::foo(); diff --git a/tests/ui/const-generics/early/invalid-const-arguments.stderr b/tests/ui/const-generics/early/invalid-const-arguments.stderr index cee34e3b71593..86b4d006454f7 100644 --- a/tests/ui/const-generics/early/invalid-const-arguments.stderr +++ b/tests/ui/const-generics/early/invalid-const-arguments.stderr @@ -9,8 +9,9 @@ LL | impl Foo for A {} | help: a struct with a similar name exists | -LL | impl Foo for A {} - | ~ +LL - impl Foo for A {} +LL + impl Foo for A {} + | help: you might be missing a type parameter | LL | impl Foo for A {} @@ -27,8 +28,9 @@ LL | impl Foo for C {} | help: a struct with a similar name exists | -LL | impl Foo for C {} - | ~ +LL - impl Foo for C {} +LL + impl Foo for C {} + | help: you might be missing a type parameter | LL | impl Foo for C {} diff --git a/tests/ui/const-generics/ensure_is_evaluatable.stderr b/tests/ui/const-generics/ensure_is_evaluatable.stderr index 62f8bc34f2edd..0a03ea49de179 100644 --- a/tests/ui/const-generics/ensure_is_evaluatable.stderr +++ b/tests/ui/const-generics/ensure_is_evaluatable.stderr @@ -15,7 +15,7 @@ LL | [(); N + 1]:, help: try adding a `where` bound | LL | [(); M + 1]:, [(); N + 1]: - | ~~~~~~~~~~~~~~ + | ++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/fn_with_two_const_inputs.stderr b/tests/ui/const-generics/fn_with_two_const_inputs.stderr index c0a913a21fd2d..7fb79da2d6143 100644 --- a/tests/ui/const-generics/fn_with_two_const_inputs.stderr +++ b/tests/ui/const-generics/fn_with_two_const_inputs.stderr @@ -15,7 +15,7 @@ LL | [(); N + 1]:, help: try adding a `where` bound | LL | [(); both(N + 1, M + 1)]:, [(); N + 1]: - | ~~~~~~~~~~~~~~ + | ++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/forbid-non-structural_match-types.stderr b/tests/ui/const-generics/forbid-non-structural_match-types.stderr index 94afded9469e8..8ef629329f103 100644 --- a/tests/ui/const-generics/forbid-non-structural_match-types.stderr +++ b/tests/ui/const-generics/forbid-non-structural_match-types.stderr @@ -6,8 +6,8 @@ LL | struct D; | help: add `#[derive(ConstParamTy, PartialEq, Eq)]` to the struct | +LL - struct C; LL + #[derive(ConstParamTy, PartialEq, Eq)] -LL | struct C; | error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/generic_arg_infer/in-signature.stderr b/tests/ui/const-generics/generic_arg_infer/in-signature.stderr index fcac95732d12e..12d84268f955c 100644 --- a/tests/ui/const-generics/generic_arg_infer/in-signature.stderr +++ b/tests/ui/const-generics/generic_arg_infer/in-signature.stderr @@ -27,52 +27,80 @@ LL | fn ty_fn_mixed() -> Bar<_, _> { | help: replace with the correct return type: `Bar` error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants - --> $DIR/in-signature.rs:22:15 + --> $DIR/in-signature.rs:22:20 | LL | const ARR_CT: [u8; _] = [0; 3]; - | ^^^^^^^ not allowed in type signatures + | ^ not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - const ARR_CT: [u8; _] = [0; 3]; +LL + const ARR_CT: [u8; 3] = [0; 3]; + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for static variables - --> $DIR/in-signature.rs:24:20 + --> $DIR/in-signature.rs:24:25 | LL | static ARR_STATIC: [u8; _] = [0; 3]; - | ^^^^^^^ not allowed in type signatures + | ^ not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - static ARR_STATIC: [u8; _] = [0; 3]; +LL + static ARR_STATIC: [u8; 3] = [0; 3]; + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants - --> $DIR/in-signature.rs:26:14 + --> $DIR/in-signature.rs:26:23 | LL | const TY_CT: Bar = Bar::(0); - | ^^^^^^^^^^^ - | | - | not allowed in type signatures - | help: replace with the correct type: `Bar` + | ^ not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - const TY_CT: Bar = Bar::(0); +LL + const TY_CT: Bar = Bar::(0); + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for static variables - --> $DIR/in-signature.rs:28:19 + --> $DIR/in-signature.rs:28:28 | LL | static TY_STATIC: Bar = Bar::(0); - | ^^^^^^^^^^^ - | | - | not allowed in type signatures - | help: replace with the correct type: `Bar` + | ^ not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - static TY_STATIC: Bar = Bar::(0); +LL + static TY_STATIC: Bar = Bar::(0); + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants - --> $DIR/in-signature.rs:30:20 + --> $DIR/in-signature.rs:30:24 | LL | const TY_CT_MIXED: Bar<_, _> = Bar::(0); - | ^^^^^^^^^ - | | - | not allowed in type signatures - | help: replace with the correct type: `Bar` + | ^ ^ not allowed in type signatures + | | + | not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - const TY_CT_MIXED: Bar<_, _> = Bar::(0); +LL + const TY_CT_MIXED: Bar = Bar::(0); + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for static variables - --> $DIR/in-signature.rs:32:25 + --> $DIR/in-signature.rs:32:29 | LL | static TY_STATIC_MIXED: Bar<_, _> = Bar::(0); - | ^^^^^^^^^ - | | - | not allowed in type signatures - | help: replace with the correct type: `Bar` + | ^ ^ not allowed in type signatures + | | + | not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - static TY_STATIC_MIXED: Bar<_, _> = Bar::(0); +LL + static TY_STATIC_MIXED: Bar = Bar::(0); + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated types --> $DIR/in-signature.rs:51:23 diff --git a/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr b/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr index b88fe966b77ce..b07e1f29d0d90 100644 --- a/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr +++ b/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr @@ -2,9 +2,9 @@ error[E0284]: type annotations needed for `Mask<_, _>` --> $DIR/issue-91614.rs:6:9 | LL | let y = Mask::<_, _>::splat(false); - | ^ -------------------------- type must be known at this point + | ^ ------------ type must be known at this point | -note: required by a const generic parameter in `Mask::::splat` +note: required by a const generic parameter in `Mask` --> $SRC_DIR/core/src/../../portable-simd/crates/core_simd/src/masks.rs:LL:COL help: consider giving `y` an explicit type, where the value of const parameter `N` is specified | @@ -15,9 +15,9 @@ error[E0284]: type annotations needed for `Mask<_, _>` --> $DIR/issue-91614.rs:6:9 | LL | let y = Mask::<_, _>::splat(false); - | ^ ------------ type must be known at this point + | ^ -------------------------- type must be known at this point | -note: required by a const generic parameter in `Mask` +note: required by a const generic parameter in `Mask::::splat` --> $SRC_DIR/core/src/../../portable-simd/crates/core_simd/src/masks.rs:LL:COL help: consider giving `y` an explicit type, where the value of const parameter `N` is specified | diff --git a/tests/ui/const-generics/generic_arg_infer/parend_infer.rs b/tests/ui/const-generics/generic_arg_infer/parend_infer.rs new file mode 100644 index 0000000000000..81c42183b3833 --- /dev/null +++ b/tests/ui/const-generics/generic_arg_infer/parend_infer.rs @@ -0,0 +1,12 @@ +//@ check-pass +//@ revisions: gate nogate +#![cfg_attr(gate, feature(generic_arg_infer))] + +fn main() { + // AST Types preserve parens for pretty printing reasons. This means + // that this is parsed as a `TyKind::Paren(TyKind::Infer)`. Generic + // arg lowering therefore needs to take into account not just `TyKind::Infer` + // but `TyKind::Infer` wrapped in arbitrarily many `TyKind::Paren`. + let a: Vec<(_)> = vec![1_u8]; + let a: Vec<(((((_)))))> = vec![1_u8]; +} diff --git a/tests/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.stderr b/tests/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.stderr index 3622ef16a9608..8d0b2e914732d 100644 --- a/tests/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.stderr +++ b/tests/ui/const-generics/generic_const_exprs/abstract-const-as-cast-3.stderr @@ -17,7 +17,7 @@ LL | fn assert_impl() {} help: try adding a `where` bound | LL | EvaluatableU128<{N as u128}>:, [(); { O as u128 } as usize]: { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | +++++++++++++++++++++++++++++ error[E0308]: mismatched types --> $DIR/abstract-const-as-cast-3.rs:17:5 @@ -52,7 +52,7 @@ LL | fn assert_impl() {} help: try adding a `where` bound | LL | EvaluatableU128<{N as u128}>:, [(); { O as u128 } as usize]: { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | +++++++++++++++++++++++++++++ error[E0308]: mismatched types --> $DIR/abstract-const-as-cast-3.rs:20:5 @@ -115,7 +115,7 @@ LL | fn assert_impl() {} help: try adding a `where` bound | LL | EvaluatableU128<{N as _}>:, [(); { O as u128 } as usize]: { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | +++++++++++++++++++++++++++++ error[E0308]: mismatched types --> $DIR/abstract-const-as-cast-3.rs:35:5 @@ -150,7 +150,7 @@ LL | fn assert_impl() {} help: try adding a `where` bound | LL | EvaluatableU128<{N as _}>:, [(); { O as u128 } as usize]: { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | +++++++++++++++++++++++++++++ error[E0308]: mismatched types --> $DIR/abstract-const-as-cast-3.rs:38:5 diff --git a/tests/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.stderr b/tests/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.stderr index f3a38fcc00544..6cf4e881adae8 100644 --- a/tests/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.stderr +++ b/tests/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.stderr @@ -7,7 +7,7 @@ LL | bar::<{ T::ASSOC }>(); help: try adding a `where` bound | LL | fn foo() where [(); U::ASSOC]:, [(); { T::ASSOC }]: { - | ~~~~~~~~~~~~~~~~~~~~~ + | +++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.stderr b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.stderr index 4d1fb02b59e91..3c79cbeb730d5 100644 --- a/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.stderr +++ b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.stderr @@ -27,7 +27,7 @@ LL | foo::<_, L>([(); L + 1 + L]); help: try adding a `where` bound | LL | [(); (L - 1) + 1 + L]:, [(); L + 1 + L]: - | ~~~~~~~~~~~~~~~~~~ + | ++++++++++++++++ error: unconstrained generic constant --> $DIR/issue_114151.rs:17:17 diff --git a/tests/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.stderr b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.stderr index 99eab935a094c..af4543e7ef2bb 100644 --- a/tests/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.stderr +++ b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.stderr @@ -16,7 +16,7 @@ LL | foo::<_, L>([(); L + 1 + L]); help: try adding a `where` bound | LL | [(); (L - 1) + 1 + L]:, [(); L + 1 + L]: - | ~~~~~~~~~~~~~~~~~~ + | ++++++++++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-ret.rs b/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-ret.rs index 1620e257667bb..9ab715d01f7a0 100644 --- a/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-ret.rs +++ b/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-ret.rs @@ -14,8 +14,8 @@ impl Foo for () { } } -fn use_dyn(v: &dyn Foo) { //~ERROR the trait `Foo` cannot be made into an object - v.test(); //~ERROR the trait `Foo` cannot be made into an object +fn use_dyn(v: &dyn Foo) { //~ERROR the trait `Foo` is not dyn compatible + v.test(); //~ERROR the trait `Foo` is not dyn compatible } fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-ret.stderr b/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-ret.stderr index d2017615e67db..8bc6ef093d046 100644 --- a/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-ret.stderr +++ b/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-ret.stderr @@ -1,38 +1,40 @@ -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/dyn-compatibility-err-ret.rs:17:16 | LL | fn use_dyn(v: &dyn Foo) { - | ^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/dyn-compatibility-err-ret.rs:8:8 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn test(&self) -> [u8; bar::()]; | ^^^^ ^^^^^^^^^^^^^^^^^^^ ...because method `test` references the `Self` type in its return type | | | ...because method `test` references the `Self` type in its `where` clause = help: consider moving `test` to another trait - = help: only type `()` implements the trait, consider using it directly instead + = help: only type `()` implements `Foo`; consider using it directly instead. -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/dyn-compatibility-err-ret.rs:18:5 | LL | v.test(); - | ^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/dyn-compatibility-err-ret.rs:8:8 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn test(&self) -> [u8; bar::()]; | ^^^^ ^^^^^^^^^^^^^^^^^^^ ...because method `test` references the `Self` type in its return type | | | ...because method `test` references the `Self` type in its `where` clause = help: consider moving `test` to another trait - = help: only type `()` implements the trait, consider using it directly instead + = help: only type `()` implements `Foo`; consider using it directly instead. error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-where-bounds.rs b/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-where-bounds.rs index b3bbb8426386b..a7b771cd4f840 100644 --- a/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-where-bounds.rs +++ b/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-where-bounds.rs @@ -13,9 +13,9 @@ impl Foo for () { } fn use_dyn(v: &dyn Foo) { - //~^ ERROR the trait `Foo` cannot be made into an object + //~^ ERROR the trait `Foo` is not dyn compatible v.test(); - //~^ ERROR the trait `Foo` cannot be made into an object + //~^ ERROR the trait `Foo` is not dyn compatible } fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-where-bounds.stderr b/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-where-bounds.stderr index 26ca2d4df5ffc..f5eaaa37916db 100644 --- a/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-where-bounds.stderr +++ b/tests/ui/const-generics/generic_const_exprs/dyn-compatibility-err-where-bounds.stderr @@ -1,34 +1,36 @@ -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/dyn-compatibility-err-where-bounds.rs:15:16 | LL | fn use_dyn(v: &dyn Foo) { - | ^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/dyn-compatibility-err-where-bounds.rs:8:8 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn test(&self) where [u8; bar::()]: Sized; | ^^^^ ...because method `test` references the `Self` type in its `where` clause = help: consider moving `test` to another trait - = help: only type `()` implements the trait, consider using it directly instead + = help: only type `()` implements `Foo`; consider using it directly instead. -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/dyn-compatibility-err-where-bounds.rs:17:5 | LL | v.test(); - | ^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/dyn-compatibility-err-where-bounds.rs:8:8 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn test(&self) where [u8; bar::()]: Sized; | ^^^^ ...because method `test` references the `Self` type in its `where` clause = help: consider moving `test` to another trait - = help: only type `()` implements the trait, consider using it directly instead + = help: only type `()` implements `Foo`; consider using it directly instead. error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/generic_const_exprs/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.rs b/tests/ui/const-generics/generic_const_exprs/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.rs index c1d3321f84077..d6bfe6fde82c8 100644 --- a/tests/ui/const-generics/generic_const_exprs/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.rs +++ b/tests/ui/const-generics/generic_const_exprs/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.rs @@ -15,6 +15,7 @@ fn f( }], ) -> impl Iterator { //~^ ERROR expected a type, found a trait +//~| ERROR expected a type, found a trait //~| ERROR `()` is not an iterator } diff --git a/tests/ui/const-generics/generic_const_exprs/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.stderr b/tests/ui/const-generics/generic_const_exprs/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.stderr index 5c4d643a28e97..b9db7461699fe 100644 --- a/tests/ui/const-generics/generic_const_exprs/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.stderr +++ b/tests/ui/const-generics/generic_const_exprs/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.stderr @@ -10,19 +10,27 @@ LL | let f: F = async { 1 }; help: a trait with a similar name exists | LL | let f: Fn = async { 1 }; - | ~~ + | + help: you might be missing a type parameter | LL | fn f( | +++ -error[E0277]: `()` is not an iterator - --> $DIR/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.rs:16:6 +error[E0782]: expected a type, found a trait + --> $DIR/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.rs:16:27 | LL | ) -> impl Iterator { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `()` is not an iterator + | ^^^^^^^^^ + | +help: you can add the `dyn` keyword if you want a trait object + | +LL | ) -> impl Iterator { + | +++ +help: you might have meant to write a bound here + | +LL - ) -> impl Iterator { +LL + ) -> impl Iterator { | - = help: the trait `Iterator` is not implemented for `()` error[E0782]: expected a type, found a trait --> $DIR/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.rs:16:27 @@ -30,16 +38,26 @@ error[E0782]: expected a type, found a trait LL | ) -> impl Iterator { | ^^^^^^^^^ | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: you can add the `dyn` keyword if you want a trait object | LL | ) -> impl Iterator { | +++ help: you might have meant to write a bound here | -LL | ) -> impl Iterator { - | ~ +LL - ) -> impl Iterator { +LL + ) -> impl Iterator { + | + +error[E0277]: `()` is not an iterator + --> $DIR/expected-type-of-closure-body-to-be-a-closure-or-coroutine-ice-113776.rs:16:6 + | +LL | ) -> impl Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `()` is not an iterator + | + = help: the trait `Iterator` is not implemented for `()` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0277, E0412, E0782. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/generic_const_exprs/issue-102768.rs b/tests/ui/const-generics/generic_const_exprs/issue-102768.rs index f2ad7d7ce8b9a..882b27a418e55 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-102768.rs +++ b/tests/ui/const-generics/generic_const_exprs/issue-102768.rs @@ -13,7 +13,7 @@ const _: () = { //~| ERROR associated type takes 0 generic arguments but 1 generic argument //~| ERROR associated type takes 1 lifetime argument but 0 lifetime arguments //~| ERROR associated type takes 0 generic arguments but 1 generic argument - //~| ERROR `X` cannot be made into an object + //~| ERROR `X` is not dyn compatible }; fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/issue-102768.stderr b/tests/ui/const-generics/generic_const_exprs/issue-102768.stderr index 9a75f372879fd..57b61006631f9 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-102768.stderr +++ b/tests/ui/const-generics/generic_const_exprs/issue-102768.stderr @@ -92,17 +92,18 @@ LL | type Y<'a>; | ^ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0038]: the trait `X` cannot be made into an object +error[E0038]: the trait `X` is not dyn compatible --> $DIR/issue-102768.rs:9:24 | LL | fn f2<'a>(arg: Box = &'a ()>>) {} - | ^^^^^^^^^^^^^^^^^^^^ `X` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^ `X` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-102768.rs:5:10 | LL | trait X { - | - this trait cannot be made into an object... + | - this trait is not dyn compatible... LL | type Y<'a>; | ^ ...because it contains the generic associated type `Y` = help: consider moving `Y` to another trait diff --git a/tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr b/tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr index 3739637c27951..6b79b32d13e3b 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr +++ b/tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr @@ -22,15 +22,13 @@ error[E0284]: type annotations needed for `ArrayHolder<_>` --> $DIR/issue-62504.rs:26:9 | LL | let mut array = ArrayHolder::new(); - | ^^^^^^^^^ ------------------ type must be known at this point + | ^^^^^^^^^ ----------- type must be known at this point | -note: required by a const generic parameter in `ArrayHolder::::new` - --> $DIR/issue-62504.rs:16:6 +note: required by a const generic parameter in `ArrayHolder` + --> $DIR/issue-62504.rs:14:20 | -LL | impl ArrayHolder { - | ^^^^^^^^^^^^^^ required by this const generic parameter in `ArrayHolder::::new` -LL | pub const fn new() -> Self { - | --- required by a bound in this associated function +LL | struct ArrayHolder([u32; X]); + | ^^^^^^^^^^^^^^ required by this const generic parameter in `ArrayHolder` help: consider giving `array` an explicit type, where the value of const parameter `X` is specified | LL | let mut array: ArrayHolder = ArrayHolder::new(); @@ -40,13 +38,15 @@ error[E0284]: type annotations needed for `ArrayHolder<_>` --> $DIR/issue-62504.rs:26:9 | LL | let mut array = ArrayHolder::new(); - | ^^^^^^^^^ ----------- type must be known at this point + | ^^^^^^^^^ ------------------ type must be known at this point | -note: required by a const generic parameter in `ArrayHolder` - --> $DIR/issue-62504.rs:14:20 +note: required by a const generic parameter in `ArrayHolder::::new` + --> $DIR/issue-62504.rs:16:6 | -LL | struct ArrayHolder([u32; X]); - | ^^^^^^^^^^^^^^ required by this const generic parameter in `ArrayHolder` +LL | impl ArrayHolder { + | ^^^^^^^^^^^^^^ required by this const generic parameter in `ArrayHolder::::new` +LL | pub const fn new() -> Self { + | --- required by a bound in this associated function help: consider giving `array` an explicit type, where the value of const parameter `X` is specified | LL | let mut array: ArrayHolder = ArrayHolder::new(); diff --git a/tests/ui/const-generics/generic_const_exprs/issue-62504.min.stderr b/tests/ui/const-generics/generic_const_exprs/issue-62504.min.stderr index 8efd433fd1fe9..c53a6598d34ed 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-62504.min.stderr +++ b/tests/ui/const-generics/generic_const_exprs/issue-62504.min.stderr @@ -24,15 +24,13 @@ error[E0284]: type annotations needed for `ArrayHolder<_>` --> $DIR/issue-62504.rs:26:9 | LL | let mut array = ArrayHolder::new(); - | ^^^^^^^^^ ------------------ type must be known at this point + | ^^^^^^^^^ ----------- type must be known at this point | -note: required by a const generic parameter in `ArrayHolder::::new` - --> $DIR/issue-62504.rs:16:6 +note: required by a const generic parameter in `ArrayHolder` + --> $DIR/issue-62504.rs:14:20 | -LL | impl ArrayHolder { - | ^^^^^^^^^^^^^^ required by this const generic parameter in `ArrayHolder::::new` -LL | pub const fn new() -> Self { - | --- required by a bound in this associated function +LL | struct ArrayHolder([u32; X]); + | ^^^^^^^^^^^^^^ required by this const generic parameter in `ArrayHolder` help: consider giving `array` an explicit type, where the value of const parameter `X` is specified | LL | let mut array: ArrayHolder = ArrayHolder::new(); @@ -42,13 +40,15 @@ error[E0284]: type annotations needed for `ArrayHolder<_>` --> $DIR/issue-62504.rs:26:9 | LL | let mut array = ArrayHolder::new(); - | ^^^^^^^^^ ----------- type must be known at this point + | ^^^^^^^^^ ------------------ type must be known at this point | -note: required by a const generic parameter in `ArrayHolder` - --> $DIR/issue-62504.rs:14:20 +note: required by a const generic parameter in `ArrayHolder::::new` + --> $DIR/issue-62504.rs:16:6 | -LL | struct ArrayHolder([u32; X]); - | ^^^^^^^^^^^^^^ required by this const generic parameter in `ArrayHolder` +LL | impl ArrayHolder { + | ^^^^^^^^^^^^^^ required by this const generic parameter in `ArrayHolder::::new` +LL | pub const fn new() -> Self { + | --- required by a bound in this associated function help: consider giving `array` an explicit type, where the value of const parameter `X` is specified | LL | let mut array: ArrayHolder = ArrayHolder::new(); diff --git a/tests/ui/const-generics/generic_const_exprs/issue-80742.stderr b/tests/ui/const-generics/generic_const_exprs/issue-80742.stderr index c851a8380f2c4..07fd231cb7ae9 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-80742.stderr +++ b/tests/ui/const-generics/generic_const_exprs/issue-80742.stderr @@ -4,8 +4,8 @@ error: internal compiler error: compiler/rustc_const_eval/src/interpret/operator Box query stack during panic: -#0 [eval_to_allocation_raw] const-evaluating + checking `::{constant#0}` +#0 [eval_to_allocation_raw] const-evaluating + checking `Inline::{constant#0}` #1 [eval_to_valtree] evaluating type-level constant -end of query stack +... and 2 other queries... use `env RUST_BACKTRACE=1` to see the full query stack error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/generic_const_exprs/lit_type_mismatch.rs b/tests/ui/const-generics/generic_const_exprs/lit_type_mismatch.rs new file mode 100644 index 0000000000000..1ed0965e1bde1 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/lit_type_mismatch.rs @@ -0,0 +1,22 @@ +//! ICE regression test for #114317 and #126182 +//! Type mismatches of literals cause errors int typeck, +//! but those errors cannot be propagated to the various +//! `lit_to_const` call sites. Now `lit_to_const` just delays +//! a bug and produces an error constant on its own. + +#![feature(adt_const_params)] +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +struct A(C); +//~^ ERROR: generic parameters with a default must be trailing +//~| ERROR: mismatched types + +struct Cond; + +struct Thing>(T); +//~^ ERROR: mismatched types + +impl Thing {} + +fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/lit_type_mismatch.stderr b/tests/ui/const-generics/generic_const_exprs/lit_type_mismatch.stderr new file mode 100644 index 0000000000000..e4613e498b275 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/lit_type_mismatch.stderr @@ -0,0 +1,21 @@ +error: generic parameters with a default must be trailing + --> $DIR/lit_type_mismatch.rs:11:16 + | +LL | struct A(C); + | ^ + +error[E0308]: mismatched types + --> $DIR/lit_type_mismatch.rs:11:24 + | +LL | struct A(C); + | ^ expected `()`, found integer + +error[E0308]: mismatched types + --> $DIR/lit_type_mismatch.rs:17:23 + | +LL | struct Thing>(T); + | ^ expected `bool`, found integer + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/generic_const_exprs/type_mismatch.stderr b/tests/ui/const-generics/generic_const_exprs/type_mismatch.stderr index e03580ec007ca..7cb67252da528 100644 --- a/tests/ui/const-generics/generic_const_exprs/type_mismatch.stderr +++ b/tests/ui/const-generics/generic_const_exprs/type_mismatch.stderr @@ -3,6 +3,8 @@ error: the constant `N` is not of type `usize` | LL | impl Q for [u8; N] {} | ^^^^^^^ expected `usize`, found `u64` + | + = note: the length of array `[u8; N]` must be type `usize` error[E0046]: not all trait items implemented, missing: `ASSOC` --> $DIR/type_mismatch.rs:8:1 diff --git a/tests/ui/const-generics/invalid-const-arg-for-type-param.stderr b/tests/ui/const-generics/invalid-const-arg-for-type-param.stderr index 4004ad1903258..2b4c7f4bcff86 100644 --- a/tests/ui/const-generics/invalid-const-arg-for-type-param.stderr +++ b/tests/ui/const-generics/invalid-const-arg-for-type-param.stderr @@ -6,8 +6,9 @@ LL | let _: u32 = 5i32.try_into::<32>().unwrap(); | help: consider moving this generic argument to the `TryInto` trait, which takes up to 1 argument | -LL | let _: u32 = TryInto::<32>::try_into(5i32).unwrap(); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - let _: u32 = 5i32.try_into::<32>().unwrap(); +LL + let _: u32 = TryInto::<32>::try_into(5i32).unwrap(); + | help: remove the unnecessary generics | LL - let _: u32 = 5i32.try_into::<32>().unwrap(); diff --git a/tests/ui/const-generics/issue-80471.stderr b/tests/ui/const-generics/issue-80471.stderr index a8514c5cc0746..8cf3d68e5d691 100644 --- a/tests/ui/const-generics/issue-80471.stderr +++ b/tests/ui/const-generics/issue-80471.stderr @@ -6,8 +6,8 @@ LL | fn foo() {} | help: add `#[derive(ConstParamTy)]` to the struct | +LL - enum Nat { LL + #[derive(ConstParamTy)] -LL | enum Nat { | error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/issues/index_array_bad_type.rs b/tests/ui/const-generics/issues/index_array_bad_type.rs index 41e4dba026c2e..91b89cd3fff3c 100644 --- a/tests/ui/const-generics/issues/index_array_bad_type.rs +++ b/tests/ui/const-generics/issues/index_array_bad_type.rs @@ -1,15 +1,13 @@ -//@ check-fail -//@ compile-flags: -C opt-level=0 +struct Struct(pub [u8; N]); +//~^ ERROR the constant `N` is not of type `usize` -#![crate_type = "lib"] - -// This used to fail in the known-panics lint, as the MIR was ill-typed due to -// the length constant not actually having type usize. -// https://github.com/rust-lang/rust/issues/134352 - -pub struct BadStruct(pub [u8; N]); -//~^ ERROR: the constant `N` is not of type `usize` - -pub fn bad_array_length_type(value: BadStruct<3>) -> u8 { +pub fn function(value: Struct<3>) -> u8 { value.0[0] + //~^ ERROR the constant `3` is not of type `usize` + + // FIXME(const_generics): Ideally we wouldn't report the above error + // b/c `Struct<_>` is never well formed, but I'd rather report too many + // errors rather than ICE the compiler. } + +fn main() {} diff --git a/tests/ui/const-generics/issues/index_array_bad_type.stderr b/tests/ui/const-generics/issues/index_array_bad_type.stderr index e4417192150ee..ceea097337764 100644 --- a/tests/ui/const-generics/issues/index_array_bad_type.stderr +++ b/tests/ui/const-generics/issues/index_array_bad_type.stderr @@ -1,8 +1,18 @@ error: the constant `N` is not of type `usize` - --> $DIR/index_array_bad_type.rs:10:40 + --> $DIR/index_array_bad_type.rs:1:34 | -LL | pub struct BadStruct(pub [u8; N]); - | ^^^^^^^ expected `usize`, found `i64` +LL | struct Struct(pub [u8; N]); + | ^^^^^^^ expected `usize`, found `i128` + | + = note: the length of array `[u8; N]` must be type `usize` + +error: the constant `3` is not of type `usize` + --> $DIR/index_array_bad_type.rs:5:5 + | +LL | value.0[0] + | ^^^^^^^ expected `usize`, found `i128` + | + = note: the length of array `[u8; 3]` must be type `usize` -error: aborting due to 1 previous error +error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/issues/issue-62878.min.stderr b/tests/ui/const-generics/issues/issue-62878.min.stderr index bd17d70a50ba5..1bb111b188df5 100644 --- a/tests/ui/const-generics/issues/issue-62878.min.stderr +++ b/tests/ui/const-generics/issues/issue-62878.min.stderr @@ -18,19 +18,17 @@ help: add `#![feature(adt_const_params)]` to the crate attributes to enable more LL + #![feature(adt_const_params)] | -error[E0747]: type provided when a constant was expected +error[E0658]: const arguments cannot yet be inferred with `_` --> $DIR/issue-62878.rs:10:11 | LL | foo::<_, { [1] }>(); | ^ | - = help: const arguments cannot yet be inferred with `_` -help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable - | -LL + #![feature(generic_arg_infer)] - | + = note: see issue #85077 for more information + = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: aborting due to 3 previous errors -Some errors have detailed explanations: E0747, E0770. -For more information about an error, try `rustc --explain E0747`. +Some errors have detailed explanations: E0658, E0770. +For more information about an error, try `rustc --explain E0658`. diff --git a/tests/ui/const-generics/issues/issue-62878.rs b/tests/ui/const-generics/issues/issue-62878.rs index 0b5269df85ee1..c80b46ddbc440 100644 --- a/tests/ui/const-generics/issues/issue-62878.rs +++ b/tests/ui/const-generics/issues/issue-62878.rs @@ -8,5 +8,5 @@ fn foo() {} fn main() { foo::<_, { [1] }>(); - //[min]~^ ERROR: type provided when a constant was expected + //[min]~^ ERROR: const arguments cannot yet be inferred with `_` } diff --git a/tests/ui/const-generics/issues/issue-87493.stderr b/tests/ui/const-generics/issues/issue-87493.stderr index 73bd6ed73e674..42d32a0ee0502 100644 --- a/tests/ui/const-generics/issues/issue-87493.stderr +++ b/tests/ui/const-generics/issues/issue-87493.stderr @@ -6,8 +6,9 @@ LL | T: MyTrait, | help: if you meant to use an associated type binding, replace `==` with `=` | -LL | T: MyTrait, - | ~ +LL - T: MyTrait, +LL + T: MyTrait, + | error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied --> $DIR/issue-87493.rs:8:8 diff --git a/tests/ui/const-generics/issues/issue-88119.stderr b/tests/ui/const-generics/issues/issue-88119.stderr index 370244fe8c984..c497f1b6d0bdb 100644 --- a/tests/ui/const-generics/issues/issue-88119.stderr +++ b/tests/ui/const-generics/issues/issue-88119.stderr @@ -6,17 +6,29 @@ LL | #![feature(const_trait_impl, generic_const_exprs)] | = help: remove one of these features -error[E0284]: type annotations needed: cannot normalize `<&T as ConstName>::{constant#0}` - --> $DIR/issue-88119.rs:19:49 +error[E0284]: type annotations needed: cannot satisfy `the constant `name_len::()` can be evaluated` + --> $DIR/issue-88119.rs:21:5 | -LL | impl const ConstName for &T - | ^^ cannot normalize `<&T as ConstName>::{constant#0}` +LL | [(); name_len::()]:, + | ^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `the constant `name_len::()` can be evaluated` + | +note: required by a bound in `<&T as ConstName>` + --> $DIR/issue-88119.rs:21:10 + | +LL | [(); name_len::()]:, + | ^^^^^^^^^^^^^^^ required by this bound in `<&T as ConstName>` -error[E0284]: type annotations needed: cannot normalize `<&mut T as ConstName>::{constant#0}` - --> $DIR/issue-88119.rs:26:49 +error[E0284]: type annotations needed: cannot satisfy `the constant `name_len::()` can be evaluated` + --> $DIR/issue-88119.rs:28:5 + | +LL | [(); name_len::()]:, + | ^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `the constant `name_len::()` can be evaluated` + | +note: required by a bound in `<&mut T as ConstName>` + --> $DIR/issue-88119.rs:28:10 | -LL | impl const ConstName for &mut T - | ^^^^^^ cannot normalize `<&mut T as ConstName>::{constant#0}` +LL | [(); name_len::()]:, + | ^^^^^^^^^^^^^^^ required by this bound in `<&mut T as ConstName>` error: aborting due to 3 previous errors diff --git a/tests/ui/const-generics/issues/issue-97278.stderr b/tests/ui/const-generics/issues/issue-97278.stderr index 47e6bf1df4d16..4894ddb7b8db8 100644 --- a/tests/ui/const-generics/issues/issue-97278.stderr +++ b/tests/ui/const-generics/issues/issue-97278.stderr @@ -6,8 +6,8 @@ LL | fn test() {} | help: add `#[derive(ConstParamTy)]` to the struct | +LL - enum Bar { LL + #[derive(ConstParamTy)] -LL | enum Bar { | error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/legacy-const-generics-bad.stderr b/tests/ui/const-generics/legacy-const-generics-bad.stderr index e9ea22e472c18..3a5fa417075ab 100644 --- a/tests/ui/const-generics/legacy-const-generics-bad.stderr +++ b/tests/ui/const-generics/legacy-const-generics-bad.stderr @@ -6,8 +6,9 @@ LL | legacy_const_generics::foo(0, a, 2); | help: consider using `const` instead of `let` | -LL | const a: /* Type */ = 1; - | ~~~~~ ++++++++++++ +LL - let a = 1; +LL + const a: /* Type */ = 1; + | error: generic parameters may not be used in const operations --> $DIR/legacy-const-generics-bad.rs:12:35 diff --git a/tests/ui/const-generics/mgca/unexpected-fn-item-in-array.rs b/tests/ui/const-generics/mgca/unexpected-fn-item-in-array.rs new file mode 100644 index 0000000000000..1c3274f6a3764 --- /dev/null +++ b/tests/ui/const-generics/mgca/unexpected-fn-item-in-array.rs @@ -0,0 +1,13 @@ +// Make sure we don't ICE when encountering an fn item during lowering in mGCA. + +#![feature(min_generic_const_args)] +//~^ WARN the feature `min_generic_const_args` is incomplete + +trait A {} + +impl A<[usize; fn_item]> for () {} +//~^ ERROR the constant `fn_item` is not of type `usize` + +fn fn_item() {} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/unexpected-fn-item-in-array.stderr b/tests/ui/const-generics/mgca/unexpected-fn-item-in-array.stderr new file mode 100644 index 0000000000000..89fc8270e3e74 --- /dev/null +++ b/tests/ui/const-generics/mgca/unexpected-fn-item-in-array.stderr @@ -0,0 +1,19 @@ +warning: the feature `min_generic_const_args` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/unexpected-fn-item-in-array.rs:3:12 + | +LL | #![feature(min_generic_const_args)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #132980 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: the constant `fn_item` is not of type `usize` + --> $DIR/unexpected-fn-item-in-array.rs:8:6 + | +LL | impl A<[usize; fn_item]> for () {} + | ^^^^^^^^^^^^^^^^^^^ expected `usize`, found fn item + | + = note: the length of array `[usize; fn_item]` must be type `usize` + +error: aborting due to 1 previous error; 1 warning emitted + diff --git a/tests/ui/const-generics/min_const_generics/invalid-patterns.32bit.stderr b/tests/ui/const-generics/min_const_generics/invalid-patterns.32bit.stderr index be92429e3abc5..11a824ba73b2a 100644 --- a/tests/ui/const-generics/min_const_generics/invalid-patterns.32bit.stderr +++ b/tests/ui/const-generics/min_const_generics/invalid-patterns.32bit.stderr @@ -1,27 +1,3 @@ -error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:29:21 - | -LL | get_flag::(); - | ^^^^ expected `char`, found `u8` - -error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:31:14 - | -LL | get_flag::<7, 'c'>(); - | ^ expected `bool`, found integer - -error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:33:14 - | -LL | get_flag::<42, 0x5ad>(); - | ^^ expected `bool`, found integer - -error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:33:18 - | -LL | get_flag::<42, 0x5ad>(); - | ^^^^^ expected `char`, found `u8` - error[E0080]: evaluation of constant value failed --> $DIR/invalid-patterns.rs:38:32 | @@ -56,6 +32,30 @@ error[E0080]: evaluation of constant value failed LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); | ^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory +error[E0308]: mismatched types + --> $DIR/invalid-patterns.rs:29:21 + | +LL | get_flag::(); + | ^^^^ expected `char`, found `u8` + +error[E0308]: mismatched types + --> $DIR/invalid-patterns.rs:31:14 + | +LL | get_flag::<7, 'c'>(); + | ^ expected `bool`, found integer + +error[E0308]: mismatched types + --> $DIR/invalid-patterns.rs:33:14 + | +LL | get_flag::<42, 0x5ad>(); + | ^^ expected `bool`, found integer + +error[E0308]: mismatched types + --> $DIR/invalid-patterns.rs:33:18 + | +LL | get_flag::<42, 0x5ad>(); + | ^^^^^ expected `char`, found `u8` + error: aborting due to 8 previous errors Some errors have detailed explanations: E0080, E0308. diff --git a/tests/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr b/tests/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr index be92429e3abc5..11a824ba73b2a 100644 --- a/tests/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr +++ b/tests/ui/const-generics/min_const_generics/invalid-patterns.64bit.stderr @@ -1,27 +1,3 @@ -error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:29:21 - | -LL | get_flag::(); - | ^^^^ expected `char`, found `u8` - -error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:31:14 - | -LL | get_flag::<7, 'c'>(); - | ^ expected `bool`, found integer - -error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:33:14 - | -LL | get_flag::<42, 0x5ad>(); - | ^^ expected `bool`, found integer - -error[E0308]: mismatched types - --> $DIR/invalid-patterns.rs:33:18 - | -LL | get_flag::<42, 0x5ad>(); - | ^^^^^ expected `char`, found `u8` - error[E0080]: evaluation of constant value failed --> $DIR/invalid-patterns.rs:38:32 | @@ -56,6 +32,30 @@ error[E0080]: evaluation of constant value failed LL | get_flag::<{ unsafe { bool_raw.boolean } }, { unsafe { char_raw.character } }>(); | ^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory +error[E0308]: mismatched types + --> $DIR/invalid-patterns.rs:29:21 + | +LL | get_flag::(); + | ^^^^ expected `char`, found `u8` + +error[E0308]: mismatched types + --> $DIR/invalid-patterns.rs:31:14 + | +LL | get_flag::<7, 'c'>(); + | ^ expected `bool`, found integer + +error[E0308]: mismatched types + --> $DIR/invalid-patterns.rs:33:14 + | +LL | get_flag::<42, 0x5ad>(); + | ^^ expected `bool`, found integer + +error[E0308]: mismatched types + --> $DIR/invalid-patterns.rs:33:18 + | +LL | get_flag::<42, 0x5ad>(); + | ^^^^^ expected `char`, found `u8` + error: aborting due to 8 previous errors Some errors have detailed explanations: E0080, E0308. diff --git a/tests/ui/const-generics/normalizing_with_unconstrained_impl_params.rs b/tests/ui/const-generics/normalizing_with_unconstrained_impl_params.rs new file mode 100644 index 0000000000000..ba37087135fe6 --- /dev/null +++ b/tests/ui/const-generics/normalizing_with_unconstrained_impl_params.rs @@ -0,0 +1,18 @@ +//! Regression test for . +//@ check-fail +#![feature(min_specialization)] +impl<'a, T: std::fmt::Debug, const N: usize> Iterator for ConstChunksExact<'a, T, { N }> { + //~^ ERROR not all trait items implemented, missing: `Item` [E0046] + fn next(&mut self) -> Option {} + //~^ ERROR mismatched types [E0308] +} +struct ConstChunksExact<'a, T: '_, const assert: usize> {} +//~^ ERROR `'_` cannot be used here [E0637] +//~| ERROR lifetime parameter `'a` is never used [E0392] +//~| ERROR type parameter `T` is never used [E0392] +impl<'a, T: std::fmt::Debug, const N: usize> Iterator for ConstChunksExact<'a, T, {}> { + //~^ ERROR mismatched types [E0308] + //~| ERROR the const parameter `N` is not constrained by the impl trait, self type, or predicates [E0207] + type Item = &'a [T; N]; } + +fn main() {} diff --git a/tests/ui/const-generics/normalizing_with_unconstrained_impl_params.stderr b/tests/ui/const-generics/normalizing_with_unconstrained_impl_params.stderr new file mode 100644 index 0000000000000..1ee6864759471 --- /dev/null +++ b/tests/ui/const-generics/normalizing_with_unconstrained_impl_params.stderr @@ -0,0 +1,60 @@ +error[E0637]: `'_` cannot be used here + --> $DIR/normalizing_with_unconstrained_impl_params.rs:9:32 + | +LL | struct ConstChunksExact<'a, T: '_, const assert: usize> {} + | ^^ `'_` is a reserved lifetime name + +error[E0308]: mismatched types + --> $DIR/normalizing_with_unconstrained_impl_params.rs:13:83 + | +LL | impl<'a, T: std::fmt::Debug, const N: usize> Iterator for ConstChunksExact<'a, T, {}> { + | ^^ expected `usize`, found `()` + +error[E0046]: not all trait items implemented, missing: `Item` + --> $DIR/normalizing_with_unconstrained_impl_params.rs:4:1 + | +LL | impl<'a, T: std::fmt::Debug, const N: usize> Iterator for ConstChunksExact<'a, T, { N }> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `Item` in implementation + | + = help: implement the missing item: `type Item = /* Type */;` + +error[E0392]: lifetime parameter `'a` is never used + --> $DIR/normalizing_with_unconstrained_impl_params.rs:9:25 + | +LL | struct ConstChunksExact<'a, T: '_, const assert: usize> {} + | ^^ unused lifetime parameter + | + = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData` + +error[E0392]: type parameter `T` is never used + --> $DIR/normalizing_with_unconstrained_impl_params.rs:9:29 + | +LL | struct ConstChunksExact<'a, T: '_, const assert: usize> {} + | ^ unused type parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + +error[E0207]: the const parameter `N` is not constrained by the impl trait, self type, or predicates + --> $DIR/normalizing_with_unconstrained_impl_params.rs:13:30 + | +LL | impl<'a, T: std::fmt::Debug, const N: usize> Iterator for ConstChunksExact<'a, T, {}> { + | ^^^^^^^^^^^^^^ unconstrained const parameter + | + = note: expressions using a const parameter must map each value to a distinct output value + = note: proving the result of expressions other than the parameter are unique is not supported + +error[E0308]: mismatched types + --> $DIR/normalizing_with_unconstrained_impl_params.rs:6:27 + | +LL | fn next(&mut self) -> Option {} + | ---- ^^^^^^^^^^^^^^^^^^ expected `Option<_>`, found `()` + | | + | implicitly returns `()` as its body has no tail or `return` expression + | + = note: expected enum `Option<_>` + found unit type `()` + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0046, E0207, E0308, E0392, E0637. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/const-generics/parser-error-recovery/issue-89013-no-kw.stderr b/tests/ui/const-generics/parser-error-recovery/issue-89013-no-kw.stderr index 78cddcc234c6e..d435af07db2ae 100644 --- a/tests/ui/const-generics/parser-error-recovery/issue-89013-no-kw.stderr +++ b/tests/ui/const-generics/parser-error-recovery/issue-89013-no-kw.stderr @@ -16,8 +16,9 @@ LL | impl Foo for Bar { | help: to use `3` as a generic argument specify it directly | -LL | impl Foo<3> for Bar { - | ~ +LL - impl Foo for Bar { +LL + impl Foo<3> for Bar { + | error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/parser-error-recovery/issue-89013.stderr b/tests/ui/const-generics/parser-error-recovery/issue-89013.stderr index 387eb226e70ed..f852c14b17842 100644 --- a/tests/ui/const-generics/parser-error-recovery/issue-89013.stderr +++ b/tests/ui/const-generics/parser-error-recovery/issue-89013.stderr @@ -28,8 +28,9 @@ LL | impl Foo for Bar { | help: to use `3` as a generic argument specify it directly | -LL | impl Foo<3> for Bar { - | ~ +LL - impl Foo for Bar { +LL + impl Foo<3> for Bar { + | error: aborting due to 3 previous errors diff --git a/tests/ui/const-generics/std/const-generics-range.full.stderr b/tests/ui/const-generics/std/const-generics-range.full.stderr index 5bf48ad738587..2b5c63e6643d8 100644 --- a/tests/ui/const-generics/std/const-generics-range.full.stderr +++ b/tests/ui/const-generics/std/const-generics-range.full.stderr @@ -4,7 +4,7 @@ error[E0741]: `std::ops::Range` must implement `ConstParamTy` to be used LL | struct _Range>; | ^^^^^^^^^^^^^^^^^^^^^^ -error[E0741]: `RangeFrom` must implement `ConstParamTy` to be used as the type of a const generic parameter +error[E0741]: `std::ops::RangeFrom` must implement `ConstParamTy` to be used as the type of a const generic parameter --> $DIR/const-generics-range.rs:13:28 | LL | struct _RangeFrom>; @@ -16,7 +16,7 @@ error[E0741]: `RangeFull` must implement `ConstParamTy` to be used as the type o LL | struct _RangeFull; | ^^^^^^^^^^^^^^^^^^^ -error[E0741]: `RangeInclusive` must implement `ConstParamTy` to be used as the type of a const generic parameter +error[E0741]: `std::ops::RangeInclusive` must implement `ConstParamTy` to be used as the type of a const generic parameter --> $DIR/const-generics-range.rs:24:33 | LL | struct _RangeInclusive>; diff --git a/tests/ui/const-generics/std/const-generics-range.min.stderr b/tests/ui/const-generics/std/const-generics-range.min.stderr index fd23b9b248a06..04e3fe744534f 100644 --- a/tests/ui/const-generics/std/const-generics-range.min.stderr +++ b/tests/ui/const-generics/std/const-generics-range.min.stderr @@ -10,7 +10,7 @@ help: add `#![feature(adt_const_params)]` to the crate attributes to enable more LL + #![feature(adt_const_params)] | -error: `RangeFrom` is forbidden as the type of a const generic parameter +error: `std::ops::RangeFrom` is forbidden as the type of a const generic parameter --> $DIR/const-generics-range.rs:13:28 | LL | struct _RangeFrom>; @@ -34,7 +34,7 @@ help: add `#![feature(adt_const_params)]` to the crate attributes to enable more LL + #![feature(adt_const_params)] | -error: `RangeInclusive` is forbidden as the type of a const generic parameter +error: `std::ops::RangeInclusive` is forbidden as the type of a const generic parameter --> $DIR/const-generics-range.rs:24:33 | LL | struct _RangeInclusive>; diff --git a/tests/ui/const-generics/std/const-generics-range.rs b/tests/ui/const-generics/std/const-generics-range.rs index f959f1e2949a8..3a238ed177e0f 100644 --- a/tests/ui/const-generics/std/const-generics-range.rs +++ b/tests/ui/const-generics/std/const-generics-range.rs @@ -11,7 +11,7 @@ const RANGE : _Range<{ 0 .. 1000 }> = _Range; // `RangeFrom` should be usable within const generics: struct _RangeFrom>; -//[min]~^ ERROR `RangeFrom` is forbidden +//[min]~^ ERROR `std::ops::RangeFrom` is forbidden const RANGE_FROM : _RangeFrom<{ 0 .. }> = _RangeFrom; // `RangeFull` should be usable within const generics: @@ -22,7 +22,7 @@ const RANGE_FULL : _RangeFull<{ .. }> = _RangeFull; // Regression test for #70155 // `RangeInclusive` should be usable within const generics: struct _RangeInclusive>; -//[min]~^ ERROR `RangeInclusive` is forbidden +//[min]~^ ERROR `std::ops::RangeInclusive` is forbidden const RANGE_INCLUSIVE : _RangeInclusive<{ 0 ..= 999 }> = _RangeInclusive; // `RangeTo` should be usable within const generics: diff --git a/tests/ui/const-generics/transmute-fail.stderr b/tests/ui/const-generics/transmute-fail.stderr index 978a9744e88aa..0e26daa3a0f1e 100644 --- a/tests/ui/const-generics/transmute-fail.stderr +++ b/tests/ui/const-generics/transmute-fail.stderr @@ -3,6 +3,8 @@ error: the constant `W` is not of type `usize` | LL | fn bar(v: [[u32; H]; W]) -> [[u32; W]; H] { | ^^^^^^^^^^^^^ expected `usize`, found `bool` + | + = note: the length of array `[[u32; H]; W]` must be type `usize` error[E0512]: cannot transmute between types of different sizes, or dependently-sized types --> $DIR/transmute-fail.rs:11:9 @@ -18,6 +20,8 @@ error: the constant `W` is not of type `usize` | LL | std::mem::transmute(v) | ^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool` + | + = note: the length of array `[[u32; H]; W]` must be type `usize` error[E0512]: cannot transmute between types of different sizes, or dependently-sized types --> $DIR/transmute-fail.rs:26:9 diff --git a/tests/ui/const-generics/type-dependent/type-mismatch.full.stderr b/tests/ui/const-generics/type-dependent/type-mismatch.full.stderr index 4fce1aede954f..95d20de1b432e 100644 --- a/tests/ui/const-generics/type-dependent/type-mismatch.full.stderr +++ b/tests/ui/const-generics/type-dependent/type-mismatch.full.stderr @@ -6,8 +6,9 @@ LL | assert_eq!(R.method::<1u16>(), 1); | help: change the type of the numeric literal from `u16` to `u8` | -LL | assert_eq!(R.method::<1u8>(), 1); - | ~~ +LL - assert_eq!(R.method::<1u16>(), 1); +LL + assert_eq!(R.method::<1u8>(), 1); + | error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/type-dependent/type-mismatch.min.stderr b/tests/ui/const-generics/type-dependent/type-mismatch.min.stderr index 4fce1aede954f..95d20de1b432e 100644 --- a/tests/ui/const-generics/type-dependent/type-mismatch.min.stderr +++ b/tests/ui/const-generics/type-dependent/type-mismatch.min.stderr @@ -6,8 +6,9 @@ LL | assert_eq!(R.method::<1u16>(), 1); | help: change the type of the numeric literal from `u16` to `u8` | -LL | assert_eq!(R.method::<1u8>(), 1); - | ~~ +LL - assert_eq!(R.method::<1u16>(), 1); +LL + assert_eq!(R.method::<1u8>(), 1); + | error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/type-mismatch-in-nested-goal.current.stderr b/tests/ui/const-generics/type-mismatch-in-nested-goal.current.stderr new file mode 100644 index 0000000000000..c6fb07926c802 --- /dev/null +++ b/tests/ui/const-generics/type-mismatch-in-nested-goal.current.stderr @@ -0,0 +1,45 @@ +error: the constant `N` is not of type `bool` + --> $DIR/type-mismatch-in-nested-goal.rs:9:50 + | +LL | fn needs_a(_: [u8; N]) where (): A {} + | ^^^^ expected `bool`, found `usize` + | +note: required by a const generic parameter in `A` + --> $DIR/type-mismatch-in-nested-goal.rs:5:9 + | +LL | trait A {} + | ^^^^^^^^^^^^^ required by this const generic parameter in `A` + +error: the constant `true` is not of type `usize` + --> $DIR/type-mismatch-in-nested-goal.rs:13:13 + | +LL | needs_a([]); + | ------- ^^ expected `usize`, found `bool` + | | + | required by a bound introduced by this call + | +note: required by a const generic parameter in `needs_a` + --> $DIR/type-mismatch-in-nested-goal.rs:9:12 + | +LL | fn needs_a(_: [u8; N]) where (): A {} + | ^^^^^^^^^^^^^^ required by this const generic parameter in `needs_a` + +error[E0308]: mismatched types + --> $DIR/type-mismatch-in-nested-goal.rs:13:13 + | +LL | needs_a([]); + | ------- ^^ expected an array with a size of true, found one with a size of 0 + | | + | arguments to this function are incorrect + | + = note: expected array `[u8; true]` + found array `[_; 0]` +note: function defined here + --> $DIR/type-mismatch-in-nested-goal.rs:9:4 + | +LL | fn needs_a(_: [u8; N]) where (): A {} + | ^^^^^^^ ---------- + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/type-mismatch-in-nested-goal.next.stderr b/tests/ui/const-generics/type-mismatch-in-nested-goal.next.stderr new file mode 100644 index 0000000000000..c6fb07926c802 --- /dev/null +++ b/tests/ui/const-generics/type-mismatch-in-nested-goal.next.stderr @@ -0,0 +1,45 @@ +error: the constant `N` is not of type `bool` + --> $DIR/type-mismatch-in-nested-goal.rs:9:50 + | +LL | fn needs_a(_: [u8; N]) where (): A {} + | ^^^^ expected `bool`, found `usize` + | +note: required by a const generic parameter in `A` + --> $DIR/type-mismatch-in-nested-goal.rs:5:9 + | +LL | trait A {} + | ^^^^^^^^^^^^^ required by this const generic parameter in `A` + +error: the constant `true` is not of type `usize` + --> $DIR/type-mismatch-in-nested-goal.rs:13:13 + | +LL | needs_a([]); + | ------- ^^ expected `usize`, found `bool` + | | + | required by a bound introduced by this call + | +note: required by a const generic parameter in `needs_a` + --> $DIR/type-mismatch-in-nested-goal.rs:9:12 + | +LL | fn needs_a(_: [u8; N]) where (): A {} + | ^^^^^^^^^^^^^^ required by this const generic parameter in `needs_a` + +error[E0308]: mismatched types + --> $DIR/type-mismatch-in-nested-goal.rs:13:13 + | +LL | needs_a([]); + | ------- ^^ expected an array with a size of true, found one with a size of 0 + | | + | arguments to this function are incorrect + | + = note: expected array `[u8; true]` + found array `[_; 0]` +note: function defined here + --> $DIR/type-mismatch-in-nested-goal.rs:9:4 + | +LL | fn needs_a(_: [u8; N]) where (): A {} + | ^^^^^^^ ---------- + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/type-mismatch-in-nested-goal.rs b/tests/ui/const-generics/type-mismatch-in-nested-goal.rs new file mode 100644 index 0000000000000..fd29019f89bc5 --- /dev/null +++ b/tests/ui/const-generics/type-mismatch-in-nested-goal.rs @@ -0,0 +1,17 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) + +trait A {} + +impl A for () {} + +fn needs_a(_: [u8; N]) where (): A {} +//~^ ERROR the constant `N` is not of type `bool` + +pub fn main() { + needs_a([]); + //~^ ERROR the constant `true` is not of type `usize` + //~| ERROR mismatched types + // FIXME(const_generics): we should hide this error as we've already errored above +} diff --git a/tests/ui/const-generics/type_mismatch.stderr b/tests/ui/const-generics/type_mismatch.stderr index d1bb5c1242f02..bd169ed2ec8f5 100644 --- a/tests/ui/const-generics/type_mismatch.stderr +++ b/tests/ui/const-generics/type_mismatch.stderr @@ -3,6 +3,8 @@ error: the constant `N` is not of type `usize` | LL | fn bar() -> [u8; N] {} | ^^^^^^^ expected `usize`, found `u8` + | + = note: the length of array `[u8; N]` must be type `usize` error: the constant `N` is not of type `u8` --> $DIR/type_mismatch.rs:2:11 diff --git a/tests/ui/const-ptr/forbidden_slices.stderr b/tests/ui/const-ptr/forbidden_slices.stderr index fad078ad2b2bd..2e0c04dcf1ea9 100644 --- a/tests/ui/const-ptr/forbidden_slices.stderr +++ b/tests/ui/const-ptr/forbidden_slices.stderr @@ -190,7 +190,7 @@ LL | from_ptr_range(ptr..ptr.add(1)) error[E0080]: could not evaluate static initializer --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | - = note: `ptr_offset_from_unsigned` called on pointers into different allocations + = note: `ptr_offset_from_unsigned` called on two different pointers that are not both derived from the same allocation | note: inside `std::ptr::const_ptr::::sub_ptr` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -205,7 +205,7 @@ LL | pub static R9: &[u32] = unsafe { from_ptr_range(&D0..(&D0 as *const u32).ad error[E0080]: could not evaluate static initializer --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | - = note: `ptr_offset_from_unsigned` called on pointers into different allocations + = note: `ptr_offset_from_unsigned` called on two different pointers that are not both derived from the same allocation | note: inside `std::ptr::const_ptr::::sub_ptr` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL diff --git a/tests/ui/consts/assoc-const-elided-lifetime.stderr b/tests/ui/consts/assoc-const-elided-lifetime.stderr index 3e847298c356a..0c3e455eb2ded 100644 --- a/tests/ui/consts/assoc-const-elided-lifetime.stderr +++ b/tests/ui/consts/assoc-const-elided-lifetime.stderr @@ -18,8 +18,9 @@ LL | #![deny(elided_lifetimes_in_associated_constant)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the `'static` lifetime | -LL | const FOO: Foo<'static> = Foo { x: PhantomData::<&()> }; - | ~~~~~~~ +LL - const FOO: Foo<'_> = Foo { x: PhantomData::<&()> }; +LL + const FOO: Foo<'static> = Foo { x: PhantomData::<&()> }; + | error: `&` without an explicit lifetime name cannot be used here --> $DIR/assoc-const-elided-lifetime.rs:14:16 diff --git a/tests/ui/consts/auxiliary/unstable_intrinsic.rs b/tests/ui/consts/auxiliary/unstable_intrinsic.rs index 9e53a8feb5dc5..45631df78591e 100644 --- a/tests/ui/consts/auxiliary/unstable_intrinsic.rs +++ b/tests/ui/consts/auxiliary/unstable_intrinsic.rs @@ -3,11 +3,9 @@ #[unstable(feature = "unstable", issue = "42")] #[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -pub const unsafe fn size_of_val(x: *const T) -> usize { 42 } +pub const unsafe fn size_of_val(x: *const T) -> usize; #[unstable(feature = "unstable", issue = "42")] #[rustc_const_unstable(feature = "unstable", issue = "42")] #[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -pub const unsafe fn min_align_of_val(x: *const T) -> usize { 42 } +pub const unsafe fn min_align_of_val(x: *const T) -> usize; diff --git a/tests/ui/consts/bad-array-size-in-type-err.rs b/tests/ui/consts/bad-array-size-in-type-err.rs index cb02ad3205db6..0490a9d3620ee 100644 --- a/tests/ui/consts/bad-array-size-in-type-err.rs +++ b/tests/ui/consts/bad-array-size-in-type-err.rs @@ -8,3 +8,14 @@ fn main() { //~^ ERROR mismatched types //~| ERROR the constant `2` is not of type `usize` } + +fn iter(val: BadArraySize::<2>) { + for _ in val.arr {} + //~^ ERROR the constant `2` is not of type `usize` + //~| ERROR `[i32; 2]` is not an iterator +} + +// issue #131102 +pub struct Blorb([String; N]); //~ ERROR the constant `N` is not of type `usize` +pub struct Wrap(Blorb<0>); +pub const fn i(_: Wrap) {} //~ ERROR destructor of `Wrap` cannot be evaluated at compile-time diff --git a/tests/ui/consts/bad-array-size-in-type-err.stderr b/tests/ui/consts/bad-array-size-in-type-err.stderr index 25d14d80c3ec4..84e16f8d931ea 100644 --- a/tests/ui/consts/bad-array-size-in-type-err.stderr +++ b/tests/ui/consts/bad-array-size-in-type-err.stderr @@ -3,6 +3,16 @@ error: the constant `N` is not of type `usize` | LL | arr: [i32; N], | ^^^^^^^^ expected `usize`, found `u8` + | + = note: the length of array `[i32; N]` must be type `usize` + +error: the constant `N` is not of type `usize` + --> $DIR/bad-array-size-in-type-err.rs:19:32 + | +LL | pub struct Blorb([String; N]); + | ^^^^^^^^^^^ expected `usize`, found `u16` + | + = note: the length of array `[String; N]` must be type `usize` error[E0308]: mismatched types --> $DIR/bad-array-size-in-type-err.rs:7:38 @@ -15,7 +25,40 @@ error: the constant `2` is not of type `usize` | LL | let _ = BadArraySize::<2> { arr: [0, 0, 0] }; | ^^^^^^^^^ expected `usize`, found `u8` + | + = note: the length of array `[i32; 2]` must be type `usize` + +error: the constant `2` is not of type `usize` + --> $DIR/bad-array-size-in-type-err.rs:13:14 + | +LL | for _ in val.arr {} + | ^^^^^^^ expected `usize`, found `u8` + | + = note: the length of array `[i32; 2]` must be type `usize` + +error[E0277]: `[i32; 2]` is not an iterator + --> $DIR/bad-array-size-in-type-err.rs:13:14 + | +LL | for _ in val.arr {} + | ^^^^^^^ `[i32; 2]` is not an iterator; try calling `.into_iter()` or `.iter()` + | + = help: the trait `IntoIterator` is not implemented for `[i32; 2]` + = help: the following other types implement trait `IntoIterator`: + &[T; N] + &[T] + &mut [T; N] + &mut [T] + [T; N] + +error[E0493]: destructor of `Wrap` cannot be evaluated at compile-time + --> $DIR/bad-array-size-in-type-err.rs:21:16 + | +LL | pub const fn i(_: Wrap) {} + | ^ - value is dropped here + | | + | the destructor for this type cannot be evaluated in constant functions -error: aborting due to 3 previous errors +error: aborting due to 7 previous errors -For more information about this error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0277, E0308, E0493. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/consts/const-block-const-bound.rs b/tests/ui/consts/const-block-const-bound.rs index 596aac09b3114..b4b89a93e759f 100644 --- a/tests/ui/consts/const-block-const-bound.rs +++ b/tests/ui/consts/const-block-const-bound.rs @@ -1,5 +1,3 @@ -//@ known-bug: #103507 - #![allow(unused)] #![feature(const_trait_impl, negative_impls, const_destruct)] @@ -16,6 +14,6 @@ impl Drop for UnconstDrop { fn main() { const { f(UnconstDrop); - //FIXME ~^ ERROR can't drop + //~^ ERROR trait bound `UnconstDrop: const Destruct` is not satisfied } } diff --git a/tests/ui/consts/const-block-const-bound.stderr b/tests/ui/consts/const-block-const-bound.stderr index 0931eff2175bc..14c62fb4d2573 100644 --- a/tests/ui/consts/const-block-const-bound.stderr +++ b/tests/ui/consts/const-block-const-bound.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `UnconstDrop: const Destruct` is not satisfied - --> $DIR/const-block-const-bound.rs:18:11 + --> $DIR/const-block-const-bound.rs:16:11 | LL | f(UnconstDrop); | - ^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | f(UnconstDrop); | required by a bound introduced by this call | note: required by a bound in `f` - --> $DIR/const-block-const-bound.rs:8:15 + --> $DIR/const-block-const-bound.rs:6:15 | LL | const fn f(x: T) {} | ^^^^^^ required by this bound in `f` diff --git a/tests/ui/consts/const-blocks/fn-call-in-non-const.stderr b/tests/ui/consts/const-blocks/fn-call-in-non-const.stderr index 272c2f045e17c..a3d5054ced358 100644 --- a/tests/ui/consts/const-blocks/fn-call-in-non-const.stderr +++ b/tests/ui/consts/const-blocks/fn-call-in-non-const.stderr @@ -13,8 +13,9 @@ LL | struct Bar; | help: create an inline `const` block | -LL | let _: [Option; 2] = [const { no_copy() }; 2]; - | ~~~~~~~~~~~~~~~~~~~ +LL - let _: [Option; 2] = [no_copy(); 2]; +LL + let _: [Option; 2] = [const { no_copy() }; 2]; + | error: aborting due to 1 previous error diff --git a/tests/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr b/tests/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr index 1de1c78faf6e6..a506f2a282bbb 100644 --- a/tests/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr +++ b/tests/ui/consts/const-eval/dont_promote_unstable_const_fn.stderr @@ -4,7 +4,7 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | const fn bar() -> u32 { foo() } | ^^^^^ | - = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features + = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) | LL + #[rustc_const_unstable(feature = "...", issue = "...")] diff --git a/tests/ui/consts/const-eval/format.rs b/tests/ui/consts/const-eval/format.rs index e56d15e935bc5..1878fc0382767 100644 --- a/tests/ui/consts/const-eval/format.rs +++ b/tests/ui/consts/const-eval/format.rs @@ -1,13 +1,11 @@ const fn failure() { panic!("{:?}", 0); //~^ ERROR cannot call non-const formatting macro in constant functions - //~| ERROR cannot call non-const associated function `Arguments::<'_>::new_v1::<1, 1>` in constant functions } const fn print() { println!("{:?}", 0); //~^ ERROR cannot call non-const formatting macro in constant functions - //~| ERROR cannot call non-const associated function `Arguments::<'_>::new_v1::<2, 1>` in constant functions //~| ERROR cannot call non-const function `_print` in constant functions } diff --git a/tests/ui/consts/const-eval/format.stderr b/tests/ui/consts/const-eval/format.stderr index 25ed44e0f3382..af90acc2a2605 100644 --- a/tests/ui/consts/const-eval/format.stderr +++ b/tests/ui/consts/const-eval/format.stderr @@ -7,17 +7,8 @@ LL | panic!("{:?}", 0); = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: this error originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0015]: cannot call non-const associated function `Arguments::<'_>::new_v1::<1, 1>` in constant functions - --> $DIR/format.rs:2:5 - | -LL | panic!("{:?}", 0); - | ^^^^^^^^^^^^^^^^^ - | - = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants - = note: this error originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) - error[E0015]: cannot call non-const formatting macro in constant functions - --> $DIR/format.rs:8:15 + --> $DIR/format.rs:7:15 | LL | println!("{:?}", 0); | ^^^^ @@ -25,17 +16,8 @@ LL | println!("{:?}", 0); = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0015]: cannot call non-const associated function `Arguments::<'_>::new_v1::<2, 1>` in constant functions - --> $DIR/format.rs:8:5 - | -LL | println!("{:?}", 0); - | ^^^^^^^^^^^^^^^^^^^ - | - = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) - error[E0015]: cannot call non-const function `_print` in constant functions - --> $DIR/format.rs:8:5 + --> $DIR/format.rs:7:5 | LL | println!("{:?}", 0); | ^^^^^^^^^^^^^^^^^^^ @@ -43,6 +25,6 @@ LL | println!("{:?}", 0); = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/consts/const-eval/parse_ints.stderr b/tests/ui/consts/const-eval/parse_ints.stderr index ec9249ece8e39..189d3c3958bbd 100644 --- a/tests/ui/consts/const-eval/parse_ints.stderr +++ b/tests/ui/consts/const-eval/parse_ints.stderr @@ -1,8 +1,10 @@ error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/num/mod.rs:LL:COL | - = note: the evaluated program panicked at 'from_str_radix_int: must lie in the range `[2, 36]`', $SRC_DIR/core/src/num/mod.rs:LL:COL + = note: the evaluated program panicked at 'from_ascii_radix: radix must lie in the range `[2, 36]`', $SRC_DIR/core/src/num/mod.rs:LL:COL | +note: inside `core::num::::from_ascii_radix` + --> $SRC_DIR/core/src/num/mod.rs:LL:COL note: inside `core::num::::from_str_radix` --> $SRC_DIR/core/src/num/mod.rs:LL:COL note: inside `_TOO_LOW` @@ -10,13 +12,15 @@ note: inside `_TOO_LOW` | LL | const _TOO_LOW: () = { u64::from_str_radix("12345ABCD", 1); }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `from_str_radix` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `from_str_int_impl` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/num/mod.rs:LL:COL | - = note: the evaluated program panicked at 'from_str_radix_int: must lie in the range `[2, 36]`', $SRC_DIR/core/src/num/mod.rs:LL:COL + = note: the evaluated program panicked at 'from_ascii_radix: radix must lie in the range `[2, 36]`', $SRC_DIR/core/src/num/mod.rs:LL:COL | +note: inside `core::num::::from_ascii_radix` + --> $SRC_DIR/core/src/num/mod.rs:LL:COL note: inside `core::num::::from_str_radix` --> $SRC_DIR/core/src/num/mod.rs:LL:COL note: inside `_TOO_HIGH` @@ -24,7 +28,7 @@ note: inside `_TOO_HIGH` | LL | const _TOO_HIGH: () = { u64::from_str_radix("12345ABCD", 37); }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `from_str_radix` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `from_str_int_impl` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/tests/ui/consts/const-eval/ub-nonnull.chalk.64bit.stderr b/tests/ui/consts/const-eval/ub-nonnull.chalk.64bit.stderr deleted file mode 100644 index fef6c92af9857..0000000000000 --- a/tests/ui/consts/const-eval/ub-nonnull.chalk.64bit.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0284]: type annotations needed: cannot satisfy `>::Output == _` - --> $DIR/ub-nonnull.rs:19:30 - | -LL | let out_of_bounds_ptr = &ptr[255]; - | ^^^^^^^^ cannot satisfy `>::Output == _` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/consts/const-eval/ub-wide-ptr.chalk.64bit.stderr b/tests/ui/consts/const-eval/ub-wide-ptr.chalk.64bit.stderr deleted file mode 100644 index 533db90ce6c9e..0000000000000 --- a/tests/ui/consts/const-eval/ub-wide-ptr.chalk.64bit.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0282]: type annotations needed - --> $DIR/ub-wide-ptr.rs:90:67 - | -LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); - | ^^^^^^^^^^^^^^ cannot infer type for type parameter `U` declared on the function `transmute` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/consts/const-eval/validation-ice-extern-type-field.rs b/tests/ui/consts/const-eval/validation-ice-extern-type-field.rs index 3502409d576c2..8a32b170c40a4 100644 --- a/tests/ui/consts/const-eval/validation-ice-extern-type-field.rs +++ b/tests/ui/consts/const-eval/validation-ice-extern-type-field.rs @@ -1,6 +1,6 @@ #![feature(extern_types)] -extern { +extern "C" { type Opaque; } diff --git a/tests/ui/consts/const-needs_drop-monomorphic.stderr b/tests/ui/consts/const-needs_drop-monomorphic.stderr index 446d34810c59c..17f197cdd5d2b 100644 --- a/tests/ui/consts/const-needs_drop-monomorphic.stderr +++ b/tests/ui/consts/const-needs_drop-monomorphic.stderr @@ -1,12 +1,3 @@ -error[E0599]: no function or associated item named `assert` found for struct `Bool<{ std::mem::needs_drop::() }>` in the current scope - --> $DIR/const-needs_drop-monomorphic.rs:11:46 - | -LL | struct Bool {} - | -------------------------- function or associated item `assert` not found for this struct -... -LL | Bool::<{ std::mem::needs_drop::() }>::assert(); - | ^^^^^^ function or associated item cannot be called on `Bool<{ std::mem::needs_drop::() }>` due to unsatisfied trait bounds - error: unconstrained generic constant --> $DIR/const-needs_drop-monomorphic.rs:11:5 | @@ -18,6 +9,15 @@ help: try adding a `where` bound LL | fn f() where [(); { std::mem::needs_drop::() } as usize]: { | +++++++++++++++++++++++++++++++++++++++++++++++++++++ +error[E0599]: no function or associated item named `assert` found for struct `Bool<{ std::mem::needs_drop::() }>` in the current scope + --> $DIR/const-needs_drop-monomorphic.rs:11:46 + | +LL | struct Bool {} + | -------------------------- function or associated item `assert` not found for this struct +... +LL | Bool::<{ std::mem::needs_drop::() }>::assert(); + | ^^^^^^ function or associated item cannot be called on `Bool<{ std::mem::needs_drop::() }>` due to unsatisfied trait bounds + error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/consts/const-pattern-irrefutable.stderr b/tests/ui/consts/const-pattern-irrefutable.stderr index 646426c942689..06bd01bff79f0 100644 --- a/tests/ui/consts/const-pattern-irrefutable.stderr +++ b/tests/ui/consts/const-pattern-irrefutable.stderr @@ -13,7 +13,7 @@ LL | let a = 4; help: introduce a variable instead | LL | let a_var = 4; - | ~~~~~ + | ++++ error[E0005]: refutable pattern in local binding --> $DIR/const-pattern-irrefutable.rs:28:9 @@ -29,8 +29,9 @@ LL | let c = 4; = note: the matched value is of type `u8` help: introduce a variable instead | -LL | let b_var = 4; - | ~~~~~ +LL - let c = 4; +LL + let b_var = 4; + | error[E0005]: refutable pattern in local binding --> $DIR/const-pattern-irrefutable.rs:32:9 @@ -47,7 +48,7 @@ LL | let d = (4, 4); help: introduce a variable instead | LL | let d_var = (4, 4); - | ~~~~~ + | ++++ error[E0005]: refutable pattern in local binding --> $DIR/const-pattern-irrefutable.rs:36:9 @@ -69,7 +70,7 @@ LL | struct S { help: introduce a variable instead | LL | let e_var = S { - | ~~~~~ + | ++++ error: aborting due to 4 previous errors diff --git a/tests/ui/consts/const-ptr-is-null.rs b/tests/ui/consts/const-ptr-is-null.rs index 92cf87a9782f2..0abd9afa42246 100644 --- a/tests/ui/consts/const-ptr-is-null.rs +++ b/tests/ui/consts/const-ptr-is-null.rs @@ -12,7 +12,13 @@ const MAYBE_NULL: () = { let ptr = &x as *const i32; // This one is still unambiguous... assert!(!ptr.is_null()); - // but once we shift outside the allocation, we might become null. + // and in fact, any offset not visible by 4 (the alignment) cannot be null, + // even if it goes out-of-bounds... + assert!(!ptr.wrapping_byte_add(13).is_null()); + assert!(!ptr.wrapping_byte_add(18).is_null()); + assert!(!ptr.wrapping_byte_sub(1).is_null()); + // ... but once we shift outside the allocation, with an offset divisible by 4, + // we might become null. assert!(!ptr.wrapping_sub(512).is_null()); //~inside `MAYBE_NULL` }; diff --git a/tests/ui/consts/const-ptr-is-null.stderr b/tests/ui/consts/const-ptr-is-null.stderr index f71b37527726f..5ef79790d93bc 100644 --- a/tests/ui/consts/const-ptr-is-null.stderr +++ b/tests/ui/consts/const-ptr-is-null.stderr @@ -8,7 +8,7 @@ note: inside `std::ptr::const_ptr::::is_null::compiletime` note: inside `std::ptr::const_ptr::::is_null` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL note: inside `MAYBE_NULL` - --> $DIR/const-ptr-is-null.rs:16:14 + --> $DIR/const-ptr-is-null.rs:22:14 | LL | assert!(!ptr.wrapping_sub(512).is_null()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/const-slice-array-deref.rs b/tests/ui/consts/const-slice-array-deref.rs new file mode 100644 index 0000000000000..9d84ed4bdb019 --- /dev/null +++ b/tests/ui/consts/const-slice-array-deref.rs @@ -0,0 +1,9 @@ +const ONE: [u16] = [1]; +//~^ ERROR the size for values of type `[u16]` cannot be known at compilation time +//~| ERROR the size for values of type `[u16]` cannot be known at compilation time +//~| ERROR mismatched types + +const TWO: &'static u16 = &ONE[0]; +//~^ ERROR cannot move a value of type `[u16]` + +fn main() {} diff --git a/tests/ui/consts/const-slice-array-deref.stderr b/tests/ui/consts/const-slice-array-deref.stderr new file mode 100644 index 0000000000000..6e69744144ed9 --- /dev/null +++ b/tests/ui/consts/const-slice-array-deref.stderr @@ -0,0 +1,33 @@ +error[E0277]: the size for values of type `[u16]` cannot be known at compilation time + --> $DIR/const-slice-array-deref.rs:1:12 + | +LL | const ONE: [u16] = [1]; + | ^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u16]` + +error[E0308]: mismatched types + --> $DIR/const-slice-array-deref.rs:1:20 + | +LL | const ONE: [u16] = [1]; + | ^^^ expected `[u16]`, found `[u16; 1]` + +error[E0277]: the size for values of type `[u16]` cannot be known at compilation time + --> $DIR/const-slice-array-deref.rs:1:20 + | +LL | const ONE: [u16] = [1]; + | ^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u16]` + = note: constant expressions must have a statically known size + +error[E0161]: cannot move a value of type `[u16]` + --> $DIR/const-slice-array-deref.rs:6:28 + | +LL | const TWO: &'static u16 = &ONE[0]; + | ^^^ the size of `[u16]` cannot be statically determined + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0161, E0277, E0308. +For more information about an error, try `rustc --explain E0161`. diff --git a/tests/ui/consts/const-unstable-intrinsic.rs b/tests/ui/consts/const-unstable-intrinsic.rs index 8b38067e46e73..890a30c73c8aa 100644 --- a/tests/ui/consts/const-unstable-intrinsic.rs +++ b/tests/ui/consts/const-unstable-intrinsic.rs @@ -2,7 +2,7 @@ //! neither within a crate nor cross-crate. //@ aux-build:unstable_intrinsic.rs #![feature(staged_api, rustc_attrs, intrinsics)] -#![stable(since="1.0.0", feature = "stable")] +#![stable(since = "1.0.0", feature = "stable")] #![feature(local)] extern crate unstable_intrinsic; @@ -30,14 +30,12 @@ const fn const_main() { #[unstable(feature = "local", issue = "42")] #[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -pub const unsafe fn size_of_val(x: *const T) -> usize { 42 } +pub const unsafe fn size_of_val(x: *const T) -> usize; #[unstable(feature = "local", issue = "42")] #[rustc_const_unstable(feature = "local", issue = "42")] #[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -pub const unsafe fn min_align_of_val(x: *const T) -> usize { 42 } +pub const unsafe fn min_align_of_val(x: *const T) -> usize; #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.63.0")] @@ -45,10 +43,7 @@ pub const unsafe fn min_align_of_val(x: *const T) -> usize { 42 } pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { // Const stability attributes are not inherited from parent items. #[rustc_intrinsic] - #[rustc_intrinsic_must_be_overridden] - const unsafe fn copy(src: *const T, dst: *mut T, count: usize) { - unimplemented!() - } + const unsafe fn copy(src: *const T, dst: *mut T, count: usize); unsafe { copy(src, dst, count) } //~^ ERROR cannot be (indirectly) exposed to stable diff --git a/tests/ui/consts/const-unstable-intrinsic.stderr b/tests/ui/consts/const-unstable-intrinsic.stderr index dfca04bef0752..308b02386f5c9 100644 --- a/tests/ui/consts/const-unstable-intrinsic.stderr +++ b/tests/ui/consts/const-unstable-intrinsic.stderr @@ -24,7 +24,10 @@ error: `size_of_val` is not yet stable as a const intrinsic LL | unstable_intrinsic::size_of_val(&x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: add `#![feature(unstable)]` to the crate attributes to enable +help: add `#![feature(unstable)]` to the crate attributes to enable + | +LL + #![feature(unstable)] + | error: `min_align_of_val` is not yet stable as a const intrinsic --> $DIR/const-unstable-intrinsic.rs:20:9 @@ -32,7 +35,10 @@ error: `min_align_of_val` is not yet stable as a const intrinsic LL | unstable_intrinsic::min_align_of_val(&x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: add `#![feature(unstable)]` to the crate attributes to enable +help: add `#![feature(unstable)]` to the crate attributes to enable + | +LL + #![feature(unstable)] + | error: const function that might be (indirectly) exposed to stable cannot use `#[feature(local)]` --> $DIR/const-unstable-intrinsic.rs:24:9 @@ -69,7 +75,7 @@ LL | const fn const_main() { | error: intrinsic `copy::copy` cannot be (indirectly) exposed to stable - --> $DIR/const-unstable-intrinsic.rs:53:14 + --> $DIR/const-unstable-intrinsic.rs:48:14 | LL | unsafe { copy(src, dst, count) } | ^^^^^^^^^^^^^^^^^^^^^ @@ -77,7 +83,7 @@ LL | unsafe { copy(src, dst, count) } = help: mark the caller as `#[rustc_const_unstable]`, or mark the intrinsic `#[rustc_intrinsic_const_stable_indirect]` (but this requires team approval) error: const function that might be (indirectly) exposed to stable cannot use `#[feature(local)]` - --> $DIR/const-unstable-intrinsic.rs:61:9 + --> $DIR/const-unstable-intrinsic.rs:56:9 | LL | super::size_of_val(src); | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/const_in_pattern/non_structural_with_escaping_bounds.rs b/tests/ui/consts/const_in_pattern/non_structural_with_escaping_bounds.rs new file mode 100644 index 0000000000000..e5d095fd61780 --- /dev/null +++ b/tests/ui/consts/const_in_pattern/non_structural_with_escaping_bounds.rs @@ -0,0 +1,15 @@ +#![feature(structural_match)] +impl std::marker::StructuralPartialEq for O { } + +enum O { + Some(*const T), + None, +} + +const C: O Fn(Box)> = O::None; + +fn main() { + match O::None { + C => (), //~ ERROR constant of non-structural type + } +} diff --git a/tests/ui/consts/const_in_pattern/non_structural_with_escaping_bounds.stderr b/tests/ui/consts/const_in_pattern/non_structural_with_escaping_bounds.stderr new file mode 100644 index 0000000000000..371be9982f708 --- /dev/null +++ b/tests/ui/consts/const_in_pattern/non_structural_with_escaping_bounds.stderr @@ -0,0 +1,13 @@ +error: constant of non-structural type `O Fn(Box)>` in a pattern + --> $DIR/non_structural_with_escaping_bounds.rs:13:9 + | +LL | const C: O Fn(Box)> = O::None; + | ----------------------------------------------- constant defined here +... +LL | C => (), + | ^ constant of non-structural type + | + = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details + +error: aborting due to 1 previous error + diff --git a/tests/ui/consts/const_let_assign2.stderr b/tests/ui/consts/const_let_assign2.stderr index 9a1b84dbf09f5..be0ffefc80dee 100644 --- a/tests/ui/consts/const_let_assign2.stderr +++ b/tests/ui/consts/const_let_assign2.stderr @@ -9,8 +9,9 @@ LL | let ptr = unsafe { &mut BB }; = note: `#[warn(static_mut_refs)]` on by default help: use `&raw mut` instead to create a raw pointer | -LL | let ptr = unsafe { &raw mut BB }; - | ~~~~~~~~ +LL - let ptr = unsafe { &mut BB }; +LL + let ptr = unsafe { &raw mut BB }; + | warning: 1 warning emitted diff --git a/tests/ui/consts/fn_trait_refs.stderr b/tests/ui/consts/fn_trait_refs.stderr index e0dbecff8e587..d688bfbde2bc2 100644 --- a/tests/ui/consts/fn_trait_refs.stderr +++ b/tests/ui/consts/fn_trait_refs.stderr @@ -155,90 +155,21 @@ note: `FnMut` can't be used with `~const` because it isn't annotated with `#[con --> $SRC_DIR/core/src/ops/function.rs:LL:COL = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0277]: the trait bound `fn() -> i32 {one}: const Destruct` is not satisfied - --> $DIR/fn_trait_refs.rs:70:32 +error[E0015]: cannot call non-const operator in constants + --> $DIR/fn_trait_refs.rs:71:17 | -LL | let test_one = test_fn(one); - | ------- ^^^ - | | - | required by a bound introduced by this call +LL | assert!(test_one == (1, 1, 1)); + | ^^^^^^^^^^^^^^^^^^^^^ | -note: required by a bound in `test_fn` - --> $DIR/fn_trait_refs.rs:35:24 - | -LL | const fn test_fn(mut f: T) -> (T::Output, T::Output, T::Output) - | ------- required by a bound in this function -LL | where -LL | T: ~const Fn<()> + ~const Destruct, - | ^^^^^^ required by this bound in `test_fn` - -error[E0277]: the trait bound `fn() -> i32 {two}: const Destruct` is not satisfied - --> $DIR/fn_trait_refs.rs:73:36 - | -LL | let test_two = test_fn_mut(two); - | ----------- ^^^ - | | - | required by a bound introduced by this call - | -note: required by a bound in `test_fn_mut` - --> $DIR/fn_trait_refs.rs:49:27 - | -LL | const fn test_fn_mut(mut f: T) -> (T::Output, T::Output) - | ----------- required by a bound in this function -LL | where -LL | T: ~const FnMut<()> + ~const Destruct, - | ^^^^^^ required by this bound in `test_fn_mut` + = note: calls in constants are limited to constant functions, tuple structs and tuple variants -error[E0277]: the trait bound `&T: ~const Destruct` is not satisfied - --> $DIR/fn_trait_refs.rs:39:19 - | -LL | tester_fn(&f), - | --------- ^^ - | | - | required by a bound introduced by this call +error[E0015]: cannot call non-const operator in constants + --> $DIR/fn_trait_refs.rs:74:17 | -note: required by a bound in `tester_fn` - --> $DIR/fn_trait_refs.rs:14:24 +LL | assert!(test_two == (2, 2)); + | ^^^^^^^^^^^^^^^^^^ | -LL | const fn tester_fn(f: T) -> T::Output - | --------- required by a bound in this function -LL | where -LL | T: ~const Fn<()> + ~const Destruct, - | ^^^^^^ required by this bound in `tester_fn` - -error[E0277]: the trait bound `&T: ~const Destruct` is not satisfied - --> $DIR/fn_trait_refs.rs:41:23 - | -LL | tester_fn_mut(&f), - | ------------- ^^ - | | - | required by a bound introduced by this call - | -note: required by a bound in `tester_fn_mut` - --> $DIR/fn_trait_refs.rs:21:27 - | -LL | const fn tester_fn_mut(mut f: T) -> T::Output - | ------------- required by a bound in this function -LL | where -LL | T: ~const FnMut<()> + ~const Destruct, - | ^^^^^^ required by this bound in `tester_fn_mut` - -error[E0277]: the trait bound `&mut T: ~const Destruct` is not satisfied - --> $DIR/fn_trait_refs.rs:53:23 - | -LL | tester_fn_mut(&mut f), - | ------------- ^^^^^^ - | | - | required by a bound introduced by this call - | -note: required by a bound in `tester_fn_mut` - --> $DIR/fn_trait_refs.rs:21:27 - | -LL | const fn tester_fn_mut(mut f: T) -> T::Output - | ------------- required by a bound in this function -LL | where -LL | T: ~const FnMut<()> + ~const Destruct, - | ^^^^^^ required by this bound in `tester_fn_mut` + = note: calls in constants are limited to constant functions, tuple structs and tuple variants error[E0015]: cannot call non-const closure in constant functions --> $DIR/fn_trait_refs.rs:16:5 @@ -264,7 +195,7 @@ LL | f() | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants -error: aborting due to 25 previous errors +error: aborting due to 22 previous errors -Some errors have detailed explanations: E0015, E0277, E0635. +Some errors have detailed explanations: E0015, E0635. For more information about an error, try `rustc --explain E0015`. diff --git a/tests/ui/consts/ice-bad-input-type-for-cast-83056.stderr b/tests/ui/consts/ice-bad-input-type-for-cast-83056.stderr index 115f168852058..4fc5972051c23 100644 --- a/tests/ui/consts/ice-bad-input-type-for-cast-83056.stderr +++ b/tests/ui/consts/ice-bad-input-type-for-cast-83056.stderr @@ -8,8 +8,9 @@ LL | fn f() -> T {} | help: a struct with a similar name exists | -LL | fn f() -> S {} - | ~ +LL - fn f() -> T {} +LL + fn f() -> S {} + | help: you might be missing a type parameter | LL | fn f() -> T {} diff --git a/tests/ui/consts/issue-104768.rs b/tests/ui/consts/issue-104768.rs index 3192daafa0b92..52a8070be4e41 100644 --- a/tests/ui/consts/issue-104768.rs +++ b/tests/ui/consts/issue-104768.rs @@ -1,4 +1,5 @@ const A: &_ = 0_u32; //~^ ERROR: the placeholder `_` is not allowed within types on item signatures for constants +//~| ERROR: mismatched types fn main() {} diff --git a/tests/ui/consts/issue-104768.stderr b/tests/ui/consts/issue-104768.stderr index 8a4a41e4d68a8..bd4a54de0ae45 100644 --- a/tests/ui/consts/issue-104768.stderr +++ b/tests/ui/consts/issue-104768.stderr @@ -1,12 +1,29 @@ +error[E0308]: mismatched types + --> $DIR/issue-104768.rs:1:15 + | +LL | const A: &_ = 0_u32; + | ^^^^^ expected `&_`, found `u32` + | + = note: expected reference `&'static _` + found type `u32` +help: consider borrowing here + | +LL | const A: &_ = &0_u32; + | + + error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants - --> $DIR/issue-104768.rs:1:10 + --> $DIR/issue-104768.rs:1:11 | LL | const A: &_ = 0_u32; - | ^^ - | | - | not allowed in type signatures - | help: replace with the correct type: `u32` + | ^ not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - const A: &_ = 0_u32; +LL + const A: u32 = 0_u32; + | -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0121`. +Some errors have detailed explanations: E0121, E0308. +For more information about an error, try `rustc --explain E0121`. diff --git a/tests/ui/consts/issue-29798.rs b/tests/ui/consts/issue-29798.rs index bdabbad6491f6..f7470d7aac908 100644 --- a/tests/ui/consts/issue-29798.rs +++ b/tests/ui/consts/issue-29798.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:index out of bounds: the len is 5 but the index is 5 -//@ ignore-emscripten no processes +//@ needs-subprocess const fn test(x: usize) -> i32 { [42;5][x] diff --git a/tests/ui/consts/issue-3521.stderr b/tests/ui/consts/issue-3521.stderr index c0e4cdc5a94ce..bddab2ebc05dc 100644 --- a/tests/ui/consts/issue-3521.stderr +++ b/tests/ui/consts/issue-3521.stderr @@ -6,8 +6,9 @@ LL | Bar = foo | help: consider using `const` instead of `let` | -LL | const foo: isize = 100; - | ~~~~~ +LL - let foo: isize = 100; +LL + const foo: isize = 100; + | error: aborting due to 1 previous error diff --git a/tests/ui/consts/issue-91560.stderr b/tests/ui/consts/issue-91560.stderr index 37c8f50d49434..9b06f3775d9fe 100644 --- a/tests/ui/consts/issue-91560.stderr +++ b/tests/ui/consts/issue-91560.stderr @@ -6,8 +6,9 @@ LL | let arr = [0; length]; | help: consider using `const` instead of `let` | -LL | const length: usize = 2; - | ~~~~~ +LL - let mut length: usize = 2; +LL + const length: usize = 2; + | error[E0435]: attempt to use a non-constant value in a constant --> $DIR/issue-91560.rs:17:19 @@ -17,8 +18,9 @@ LL | let arr = [0; length]; | help: consider using `const` instead of `let` | -LL | const length: usize = 2; - | ~~~~~ +LL - let length: usize = 2; +LL + const length: usize = 2; + | error: aborting due to 2 previous errors diff --git a/tests/ui/consts/issue-94675.rs b/tests/ui/consts/issue-94675.rs index 2e30eebb07b2e..e1c6861c5103b 100644 --- a/tests/ui/consts/issue-94675.rs +++ b/tests/ui/consts/issue-94675.rs @@ -1,5 +1,3 @@ -//@ known-bug: #103507 - #![feature(const_trait_impl, const_vec_string_slice)] struct Foo<'a> { @@ -9,9 +7,7 @@ struct Foo<'a> { impl<'a> Foo<'a> { const fn spam(&mut self, baz: &mut Vec) { self.bar[0] = baz.len(); - //FIXME ~^ ERROR: cannot call - //FIXME ~| ERROR: cannot call - //FIXME ~| ERROR: the trait bound + //~^ ERROR: cannot call } } diff --git a/tests/ui/consts/issue-94675.stderr b/tests/ui/consts/issue-94675.stderr index 8cad13724f20e..63a86b456332f 100644 --- a/tests/ui/consts/issue-94675.stderr +++ b/tests/ui/consts/issue-94675.stderr @@ -1,5 +1,5 @@ error[E0015]: cannot call non-const operator in constant functions - --> $DIR/issue-94675.rs:11:17 + --> $DIR/issue-94675.rs:9:17 | LL | self.bar[0] = baz.len(); | ^^^ diff --git a/tests/ui/consts/large_const_alloc.rs b/tests/ui/consts/large_const_alloc.rs index 61a22216ae526..14edc1bb69610 100644 --- a/tests/ui/consts/large_const_alloc.rs +++ b/tests/ui/consts/large_const_alloc.rs @@ -1,5 +1,7 @@ //@ only-64bit // on 32bit and 16bit platforms it is plausible that the maximum allocation size will succeed +// FIXME (#135952) In some cases on AArch64 Linux the diagnostic does not trigger +//@ ignore-aarch64-unknown-linux-gnu const FOO: () = { // 128 TiB, unlikely anyone has that much RAM diff --git a/tests/ui/consts/large_const_alloc.stderr b/tests/ui/consts/large_const_alloc.stderr index 25d660f1217f4..fa7d5977a9567 100644 --- a/tests/ui/consts/large_const_alloc.stderr +++ b/tests/ui/consts/large_const_alloc.stderr @@ -1,11 +1,11 @@ error[E0080]: evaluation of constant value failed - --> $DIR/large_const_alloc.rs:6:13 + --> $DIR/large_const_alloc.rs:8:13 | LL | let x = [0_u8; (1 << 47) - 1]; | ^^^^^^^^^^^^^^^^^^^^^ tried to allocate more memory than available to compiler error[E0080]: could not evaluate static initializer - --> $DIR/large_const_alloc.rs:11:13 + --> $DIR/large_const_alloc.rs:13:13 | LL | let x = [0_u8; (1 << 47) - 1]; | ^^^^^^^^^^^^^^^^^^^^^ tried to allocate more memory than available to compiler diff --git a/tests/ui/consts/min_const_fn/min_const_fn_libstd_stability.stderr b/tests/ui/consts/min_const_fn/min_const_fn_libstd_stability.stderr index 899cec07ac70c..26dedc49a3928 100644 --- a/tests/ui/consts/min_const_fn/min_const_fn_libstd_stability.stderr +++ b/tests/ui/consts/min_const_fn/min_const_fn_libstd_stability.stderr @@ -4,7 +4,7 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | const fn bar() -> u32 { foo() } | ^^^^^ | - = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features + = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) | LL + #[rustc_const_unstable(feature = "...", issue = "...")] @@ -22,7 +22,7 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | const fn bar2() -> u32 { foo2() } | ^^^^^^ | - = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features + = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) | LL + #[rustc_const_unstable(feature = "...", issue = "...")] @@ -57,7 +57,7 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | foo() | ^^^^^ | - = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features + = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) | LL + #[rustc_const_unstable(feature = "...", issue = "...")] @@ -75,7 +75,7 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | const fn bar2_gated() -> u32 { foo2_gated() } | ^^^^^^^^^^^^ | - = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features + = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) | LL + #[rustc_const_unstable(feature = "...", issue = "...")] @@ -93,7 +93,7 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | pub(crate) const fn bar2_gated_stable_indirect() -> u32 { super::foo2_gated() } | ^^^^^^^^^^^^^^^^^^^ | - = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features + = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) | LL + #[rustc_const_unstable(feature = "...", issue = "...")] @@ -111,7 +111,7 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | const fn stable_indirect() -> u32 { foo2_gated() } | ^^^^^^^^^^^^ | - = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features + = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) | LL + #[rustc_const_unstable(feature = "...", issue = "...")] diff --git a/tests/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.stderr b/tests/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.stderr index 442a079020f1b..b61f7db6f43b7 100644 --- a/tests/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.stderr +++ b/tests/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability.stderr @@ -4,7 +4,7 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | const unsafe fn bar() -> u32 { unsafe { foo() } } | ^^^^^ | - = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features + = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) | LL + #[rustc_const_unstable(feature = "...", issue = "...")] @@ -22,7 +22,7 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | const unsafe fn bar2() -> u32 { unsafe { foo2() } } | ^^^^^^ | - = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features + = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) | LL + #[rustc_const_unstable(feature = "...", issue = "...")] @@ -40,7 +40,7 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | const unsafe fn bar2_gated() -> u32 { unsafe { foo2_gated() } } | ^^^^^^^^^^^^ | - = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features + = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) | LL + #[rustc_const_unstable(feature = "...", issue = "...")] diff --git a/tests/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.stderr b/tests/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.stderr index ff37cba7b9aca..fad8e396e9ab5 100644 --- a/tests/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.stderr +++ b/tests/ui/consts/min_const_fn/min_const_unsafe_fn_libstd_stability2.stderr @@ -4,7 +4,7 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | const unsafe fn bar() -> u32 { foo() } | ^^^^^ | - = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features + = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) | LL + #[rustc_const_unstable(feature = "...", issue = "...")] @@ -22,7 +22,7 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | const unsafe fn bar2() -> u32 { foo2() } | ^^^^^^ | - = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features + = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) | LL + #[rustc_const_unstable(feature = "...", issue = "...")] @@ -40,7 +40,7 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | const unsafe fn bar2_gated() -> u32 { foo2_gated() } | ^^^^^^^^^^^^ | - = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features + = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) | LL + #[rustc_const_unstable(feature = "...", issue = "...")] diff --git a/tests/ui/consts/min_const_fn/recursive_const_stab_unmarked_crate_imports.stderr b/tests/ui/consts/min_const_fn/recursive_const_stab_unmarked_crate_imports.stderr index a655c0faab629..bbe749f595893 100644 --- a/tests/ui/consts/min_const_fn/recursive_const_stab_unmarked_crate_imports.stderr +++ b/tests/ui/consts/min_const_fn/recursive_const_stab_unmarked_crate_imports.stderr @@ -4,7 +4,7 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | unstable_if_unmarked_const_fn_crate::not_stably_const(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features + = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) | LL + #[rustc_const_unstable(feature = "...", issue = "...")] diff --git a/tests/ui/consts/min_const_fn/recursive_const_stab_unstable_if_unmarked.stderr b/tests/ui/consts/min_const_fn/recursive_const_stab_unstable_if_unmarked.stderr index d4ba0f9df2daf..9d7b81c822bd1 100644 --- a/tests/ui/consts/min_const_fn/recursive_const_stab_unstable_if_unmarked.stderr +++ b/tests/ui/consts/min_const_fn/recursive_const_stab_unstable_if_unmarked.stderr @@ -4,7 +4,7 @@ error: const function that might be (indirectly) exposed to stable cannot use `# LL | not_stably_const(); | ^^^^^^^^^^^^^^^^^^ | - = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features + = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) | LL + #[rustc_const_unstable(feature = "...", issue = "...")] diff --git a/tests/ui/consts/non-const-value-in-const.stderr b/tests/ui/consts/non-const-value-in-const.stderr index 654b573544c2e..201c310843b38 100644 --- a/tests/ui/consts/non-const-value-in-const.stderr +++ b/tests/ui/consts/non-const-value-in-const.stderr @@ -6,8 +6,9 @@ LL | const Y: i32 = x; | help: consider using `let` instead of `const` | -LL | let Y: i32 = x; - | ~~~ +LL - const Y: i32 = x; +LL + let Y: i32 = x; + | error[E0435]: attempt to use a non-constant value in a constant --> $DIR/non-const-value-in-const.rs:6:17 @@ -17,8 +18,9 @@ LL | let _ = [0; x]; | help: consider using `const` instead of `let` | -LL | const x: /* Type */ = 5; - | ~~~~~ ++++++++++++ +LL - let x = 5; +LL + const x: /* Type */ = 5; + | error: aborting due to 2 previous errors diff --git a/tests/ui/consts/offset_from_ub.rs b/tests/ui/consts/offset_from_ub.rs index 0232b03a813e8..8835690060505 100644 --- a/tests/ui/consts/offset_from_ub.rs +++ b/tests/ui/consts/offset_from_ub.rs @@ -17,7 +17,7 @@ pub const DIFFERENT_ALLOC: usize = { let uninit2 = std::mem::MaybeUninit::::uninit(); let field_ptr: *const Struct = &uninit2 as *const _ as *const Struct; let offset = unsafe { ptr_offset_from(field_ptr, base_ptr) }; //~ERROR evaluation of constant value failed - //~| pointers into different allocations + //~| not both derived from the same allocation offset as usize }; @@ -37,7 +37,7 @@ pub const DIFFERENT_INT: isize = { // offset_from with two different integers: l let ptr1 = 8 as *const u8; let ptr2 = 16 as *const u8; unsafe { ptr_offset_from(ptr2, ptr1) } //~ERROR evaluation of constant value failed - //~| dangling pointer + //~| not both derived from the same allocation }; const OUT_OF_BOUNDS_1: isize = { @@ -46,7 +46,7 @@ const OUT_OF_BOUNDS_1: isize = { let end_ptr = (start_ptr).wrapping_add(length); // First ptr is out of bounds unsafe { ptr_offset_from(end_ptr, start_ptr) } //~ERROR evaluation of constant value failed - //~| expected a pointer to 10 bytes of memory + //~| the memory range between them is not in-bounds of an allocation }; const OUT_OF_BOUNDS_2: isize = { @@ -55,7 +55,7 @@ const OUT_OF_BOUNDS_2: isize = { let end_ptr = (start_ptr).wrapping_add(length); // Second ptr is out of bounds unsafe { ptr_offset_from(start_ptr, end_ptr) } //~ERROR evaluation of constant value failed - //~| expected a pointer to the end of 10 bytes of memory + //~| the memory range between them is not in-bounds of an allocation }; pub const DIFFERENT_ALLOC_UNSIGNED: usize = { @@ -64,7 +64,7 @@ pub const DIFFERENT_ALLOC_UNSIGNED: usize = { let uninit2 = std::mem::MaybeUninit::::uninit(); let field_ptr: *const Struct = &uninit2 as *const _ as *const Struct; unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) } //~ERROR evaluation of constant value failed - //~| pointers into different allocations + //~| not both derived from the same allocation }; pub const TOO_FAR_APART1: isize = { diff --git a/tests/ui/consts/offset_from_ub.stderr b/tests/ui/consts/offset_from_ub.stderr index ac4597ff0119a..1379365cc25cc 100644 --- a/tests/ui/consts/offset_from_ub.stderr +++ b/tests/ui/consts/offset_from_ub.stderr @@ -2,12 +2,12 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:19:27 | LL | let offset = unsafe { ptr_offset_from(field_ptr, base_ptr) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on pointers into different allocations + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on two different pointers that are not both derived from the same allocation error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | - = note: `ptr_offset_from` called on pointers into different allocations + = note: `ptr_offset_from` called on two different pointers that are not both derived from the same allocation | note: inside `std::ptr::const_ptr::::offset_from` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -27,25 +27,25 @@ error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:39:14 | LL | unsafe { ptr_offset_from(ptr2, ptr1) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from` origin: expected a pointer to $BYTES bytes of memory, but got 0x8[noalloc] which is a dangling pointer (it has no provenance) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on two different pointers that are not both derived from the same allocation error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:48:14 | LL | unsafe { ptr_offset_from(end_ptr, start_ptr) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from` origin: expected a pointer to $BYTES bytes of memory, but got ALLOC0 which is only $BYTES bytes from the end of the allocation + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on two different pointers where the memory range between them is not in-bounds of an allocation error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:57:14 | LL | unsafe { ptr_offset_from(start_ptr, end_ptr) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from` origin: expected a pointer to the end of $BYTES bytes of memory, but got ALLOC1+0xa which does not have enough space to the beginning of the allocation + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on two different pointers where the memory range between them is not in-bounds of an allocation error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:66:14 | LL | unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called on pointers into different allocations + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called on two different pointers that are not both derived from the same allocation error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:73:14 @@ -80,7 +80,7 @@ LL | unsafe { ptr_offset_from_unsigned(ptr2, ptr1) } error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | - = note: out-of-bounds `offset_from` origin: expected a pointer to $BYTES bytes of memory, but got a null pointer + = note: `ptr_offset_from` called on two different pointers that are not both derived from the same allocation | note: inside `std::ptr::const_ptr::::offset_from` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL diff --git a/tests/ui/consts/promoted-const-drop.rs b/tests/ui/consts/promoted-const-drop.rs index e09c30ea7857b..1d1897e15d443 100644 --- a/tests/ui/consts/promoted-const-drop.rs +++ b/tests/ui/consts/promoted-const-drop.rs @@ -1,4 +1,4 @@ -#![feature(const_trait_impl)] +#![feature(const_trait_impl, const_destruct)] struct A(); diff --git a/tests/ui/consts/promoted_const_call.rs b/tests/ui/consts/promoted_const_call.rs index c3920ff7241bc..79cb2ea2a02a0 100644 --- a/tests/ui/consts/promoted_const_call.rs +++ b/tests/ui/consts/promoted_const_call.rs @@ -1,6 +1,4 @@ -//@ known-bug: #103507 - -#![feature(const_trait_impl)] +#![feature(const_trait_impl, const_destruct)] struct Panic; impl const Drop for Panic { fn drop(&mut self) { panic!(); } } @@ -8,15 +6,15 @@ impl const Drop for Panic { fn drop(&mut self) { panic!(); } } pub const fn id(x: T) -> T { x } pub const C: () = { let _: &'static _ = &id(&Panic); - //FIXME ~^ ERROR: temporary value dropped while borrowed - //FIXME ~| ERROR: temporary value dropped while borrowed + //~^ ERROR: temporary value dropped while borrowed + //~| ERROR: temporary value dropped while borrowed }; fn main() { let _: &'static _ = &id(&Panic); - //FIXME ~^ ERROR: temporary value dropped while borrowed - //FIXME ~| ERROR: temporary value dropped while borrowed + //~^ ERROR: temporary value dropped while borrowed + //~| ERROR: temporary value dropped while borrowed let _: &'static _ = &&(Panic, 0).1; - //FIXME~^ ERROR: temporary value dropped while borrowed - //FIXME~| ERROR: temporary value dropped while borrowed + //~^ ERROR: temporary value dropped while borrowed + //~| ERROR: temporary value dropped while borrowed } diff --git a/tests/ui/consts/promoted_const_call.stderr b/tests/ui/consts/promoted_const_call.stderr index dd70bb601c47d..7a9cdd687048d 100644 --- a/tests/ui/consts/promoted_const_call.stderr +++ b/tests/ui/consts/promoted_const_call.stderr @@ -1,13 +1,25 @@ -error[E0493]: destructor of `Panic` cannot be evaluated at compile-time - --> $DIR/promoted_const_call.rs:10:30 +error[E0716]: temporary value dropped while borrowed + --> $DIR/promoted_const_call.rs:8:26 + | +LL | let _: &'static _ = &id(&Panic); + | ---------- ^^^^^^^^^^ creates a temporary value which is freed while still in use + | | + | type annotation requires that borrow lasts for `'static` +... +LL | }; + | - temporary value is freed at the end of this statement + +error[E0716]: temporary value dropped while borrowed + --> $DIR/promoted_const_call.rs:8:30 | LL | let _: &'static _ = &id(&Panic); - | ^^^^^ - value is dropped here - | | - | the destructor for this type cannot be evaluated in constants + | ---------- ^^^^^ - temporary value is freed at the end of this statement + | | | + | | creates a temporary value which is freed while still in use + | type annotation requires that borrow lasts for `'static` error[E0716]: temporary value dropped while borrowed - --> $DIR/promoted_const_call.rs:16:26 + --> $DIR/promoted_const_call.rs:14:26 | LL | let _: &'static _ = &id(&Panic); | ---------- ^^^^^^^^^^ creates a temporary value which is freed while still in use @@ -18,7 +30,7 @@ LL | } | - temporary value is freed at the end of this statement error[E0716]: temporary value dropped while borrowed - --> $DIR/promoted_const_call.rs:16:30 + --> $DIR/promoted_const_call.rs:14:30 | LL | let _: &'static _ = &id(&Panic); | ---------- ^^^^^ - temporary value is freed at the end of this statement @@ -27,7 +39,7 @@ LL | let _: &'static _ = &id(&Panic); | type annotation requires that borrow lasts for `'static` error[E0716]: temporary value dropped while borrowed - --> $DIR/promoted_const_call.rs:19:26 + --> $DIR/promoted_const_call.rs:17:26 | LL | let _: &'static _ = &&(Panic, 0).1; | ---------- ^^^^^^^^^^^^^ creates a temporary value which is freed while still in use @@ -38,7 +50,7 @@ LL | } | - temporary value is freed at the end of this statement error[E0716]: temporary value dropped while borrowed - --> $DIR/promoted_const_call.rs:19:27 + --> $DIR/promoted_const_call.rs:17:27 | LL | let _: &'static _ = &&(Panic, 0).1; | ---------- ^^^^^^^^^^ creates a temporary value which is freed while still in use @@ -48,7 +60,6 @@ LL | let _: &'static _ = &&(Panic, 0).1; LL | } | - temporary value is freed at the end of this statement -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors -Some errors have detailed explanations: E0493, E0716. -For more information about an error, try `rustc --explain E0493`. +For more information about this error, try `rustc --explain E0716`. diff --git a/tests/ui/consts/promoted_running_out_of_memory_issue-130687.rs b/tests/ui/consts/promoted_running_out_of_memory_issue-130687.rs index b923a768cbfe5..53618e2e86acd 100644 --- a/tests/ui/consts/promoted_running_out_of_memory_issue-130687.rs +++ b/tests/ui/consts/promoted_running_out_of_memory_issue-130687.rs @@ -3,6 +3,8 @@ // Needs the max type size to be much bigger than the RAM people typically have. //@ only-64bit +// FIXME (#135952) In some cases on AArch64 Linux the diagnostic does not trigger +//@ ignore-aarch64-unknown-linux-gnu pub struct Data([u8; (1 << 47) - 1]); const _: &'static Data = &Data([0; (1 << 47) - 1]); diff --git a/tests/ui/consts/promoted_running_out_of_memory_issue-130687.stderr b/tests/ui/consts/promoted_running_out_of_memory_issue-130687.stderr index f5d767efceb76..aac805dbd8c79 100644 --- a/tests/ui/consts/promoted_running_out_of_memory_issue-130687.stderr +++ b/tests/ui/consts/promoted_running_out_of_memory_issue-130687.stderr @@ -1,5 +1,5 @@ error[E0080]: evaluation of constant value failed - --> $DIR/promoted_running_out_of_memory_issue-130687.rs:8:32 + --> $DIR/promoted_running_out_of_memory_issue-130687.rs:10:32 | LL | const _: &'static Data = &Data([0; (1 << 47) - 1]); | ^^^^^^^^^^^^^^^^^^ tried to allocate more memory than available to compiler diff --git a/tests/ui/consts/recursive-block.rs b/tests/ui/consts/recursive-block.rs new file mode 100644 index 0000000000000..a3dcaa4283669 --- /dev/null +++ b/tests/ui/consts/recursive-block.rs @@ -0,0 +1,7 @@ +const fn foo() { + const { foo::<&T>() } //~ ERROR: queries overflow the depth limit! +} + +fn main () { + const X: () = foo::(); +} diff --git a/tests/ui/consts/recursive-block.stderr b/tests/ui/consts/recursive-block.stderr new file mode 100644 index 0000000000000..90814e2e0002a --- /dev/null +++ b/tests/ui/consts/recursive-block.stderr @@ -0,0 +1,11 @@ +error: queries overflow the depth limit! + --> $DIR/recursive-block.rs:2:11 + | +LL | const { foo::<&T>() } + | ^^^^^^^^^^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_block`) + = note: query depth increased by 1 when computing layout of `foo<&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&i32>` + +error: aborting due to 1 previous error + diff --git a/tests/ui/consts/recursive-const-in-impl.rs b/tests/ui/consts/recursive-const-in-impl.rs new file mode 100644 index 0000000000000..93f7201f921f2 --- /dev/null +++ b/tests/ui/consts/recursive-const-in-impl.rs @@ -0,0 +1,12 @@ +//@ build-fail +#![recursion_limit = "7"] + +struct Thing(T); + +impl Thing { + const X: usize = Thing::>::X; +} + +fn main() { + println!("{}", Thing::::X); //~ ERROR: queries overflow the depth limit! +} diff --git a/tests/ui/consts/recursive-const-in-impl.stderr b/tests/ui/consts/recursive-const-in-impl.stderr new file mode 100644 index 0000000000000..6175112c8cc0d --- /dev/null +++ b/tests/ui/consts/recursive-const-in-impl.stderr @@ -0,0 +1,11 @@ +error: queries overflow the depth limit! + --> $DIR/recursive-const-in-impl.rs:11:14 + | +LL | println!("{}", Thing::::X); + | ^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "14"]` attribute to your crate (`recursive_const_in_impl`) + = note: query depth increased by 9 when simplifying constant for the type system `main::promoted[1]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/consts/refs_check_const_eq-issue-88384.stderr b/tests/ui/consts/refs_check_const_eq-issue-88384.stderr index 62c5c52764115..65e4dc22c2887 100644 --- a/tests/ui/consts/refs_check_const_eq-issue-88384.stderr +++ b/tests/ui/consts/refs_check_const_eq-issue-88384.stderr @@ -15,8 +15,8 @@ LL | struct Foo; | help: add `#[derive(ConstParamTy)]` to the struct | +LL - struct CompileTimeSettings { LL + #[derive(ConstParamTy)] -LL | struct CompileTimeSettings { | error[E0741]: `CompileTimeSettings` must implement `ConstParamTy` to be used as the type of a const generic parameter @@ -27,8 +27,8 @@ LL | impl Foo { | help: add `#[derive(ConstParamTy)]` to the struct | +LL - struct CompileTimeSettings { LL + #[derive(ConstParamTy)] -LL | struct CompileTimeSettings { | error: aborting due to 2 previous errors; 1 warning emitted diff --git a/tests/ui/consts/too_generic_eval_ice.stderr b/tests/ui/consts/too_generic_eval_ice.stderr index b48be16a24c8e..3cc4377514a21 100644 --- a/tests/ui/consts/too_generic_eval_ice.stderr +++ b/tests/ui/consts/too_generic_eval_ice.stderr @@ -32,13 +32,13 @@ LL | [5; Self::HOST_SIZE] == [6; 0] = help: the following other types implement trait `PartialEq`: `&[T]` implements `PartialEq>` `&[T]` implements `PartialEq<[U; N]>` + `&[u8; N]` implements `PartialEq` + `&[u8; N]` implements `PartialEq` + `&[u8]` implements `PartialEq` + `&[u8]` implements `PartialEq` `&mut [T]` implements `PartialEq>` `&mut [T]` implements `PartialEq<[U; N]>` - `[T; N]` implements `PartialEq<&[U]>` - `[T; N]` implements `PartialEq<&mut [U]>` - `[T; N]` implements `PartialEq<[U; N]>` - `[T; N]` implements `PartialEq<[U]>` - and 3 others + and 11 others error: aborting due to 4 previous errors diff --git a/tests/ui/consts/write_to_mut_ref_dest.stock.stderr b/tests/ui/consts/write_to_mut_ref_dest.stock.stderr deleted file mode 100644 index 688d48ec707ca..0000000000000 --- a/tests/ui/consts/write_to_mut_ref_dest.stock.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error[E0658]: mutable references are not allowed in constants - --> $DIR/write_to_mut_ref_dest.rs:11:27 - | -LL | let b: *mut u32 = &mut a; - | ^^^^^^ - | - = note: see issue #57349 for more information - = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: dereferencing raw mutable pointers in constants is unstable - --> $DIR/write_to_mut_ref_dest.rs:12:18 - | -LL | unsafe { *b = 5; } - | ^^^^^^ - | - = note: see issue #57349 for more information - = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/contracts/associated-item.rs b/tests/ui/contracts/associated-item.rs new file mode 100644 index 0000000000000..4a2d05abbc53c --- /dev/null +++ b/tests/ui/contracts/associated-item.rs @@ -0,0 +1,18 @@ +// Ensure we don't ICE when lowering contracts on an associated item. + +//@ compile-flags: --crate-type=lib +//@ check-pass + +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use + +extern crate core; + +use core::contracts::requires; + +struct Foo; + +impl Foo { + #[requires(align > 0 && (align & (align - 1)) == 0)] + pub fn foo(align: i32) {} +} diff --git a/tests/ui/contracts/associated-item.stderr b/tests/ui/contracts/associated-item.stderr new file mode 100644 index 0000000000000..20651026b87a2 --- /dev/null +++ b/tests/ui/contracts/associated-item.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/associated-item.rs:6:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-annotation-limitations.rs b/tests/ui/contracts/contract-annotation-limitations.rs new file mode 100644 index 0000000000000..10b3bacab5cfa --- /dev/null +++ b/tests/ui/contracts/contract-annotation-limitations.rs @@ -0,0 +1,28 @@ +//! Test for some of the existing limitations and the current error messages. +//! Some of these limitations may be removed in the future. + +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] +#![allow(dead_code)] + +/// Represent a 5-star system. +struct Stars(u8); + +impl Stars { + fn is_valid(&self) -> bool { + self.0 <= 5 + } +} + +trait ParseStars { + #[core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] + //~^ ERROR contract annotations is only supported in functions with bodies + fn parse_string(input: String) -> Option; + + #[core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] + //~^ ERROR contract annotations is only supported in functions with bodies + fn parse(input: T) -> Option where T: for<'a> Into<&'a str>; +} + +fn main() { +} diff --git a/tests/ui/contracts/contract-annotation-limitations.stderr b/tests/ui/contracts/contract-annotation-limitations.stderr new file mode 100644 index 0000000000000..14338cf4b8687 --- /dev/null +++ b/tests/ui/contracts/contract-annotation-limitations.stderr @@ -0,0 +1,23 @@ +error: contract annotations is only supported in functions with bodies + --> $DIR/contract-annotation-limitations.rs:18:5 + | +LL | #[core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations is only supported in functions with bodies + --> $DIR/contract-annotation-limitations.rs:22:5 + | +LL | #[core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-annotation-limitations.rs:4:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: aborting due to 2 previous errors; 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-generics.chk_const_fail.stderr b/tests/ui/contracts/contract-attributes-generics.chk_const_fail.stderr new file mode 100644 index 0000000000000..0630811d4f7ea --- /dev/null +++ b/tests/ui/contracts/contract-attributes-generics.chk_const_fail.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-generics.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-generics.chk_fail_post.stderr b/tests/ui/contracts/contract-attributes-generics.chk_fail_post.stderr new file mode 100644 index 0000000000000..0630811d4f7ea --- /dev/null +++ b/tests/ui/contracts/contract-attributes-generics.chk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-generics.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-generics.chk_fail_pre.stderr b/tests/ui/contracts/contract-attributes-generics.chk_fail_pre.stderr new file mode 100644 index 0000000000000..0630811d4f7ea --- /dev/null +++ b/tests/ui/contracts/contract-attributes-generics.chk_fail_pre.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-generics.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-generics.chk_pass.stderr b/tests/ui/contracts/contract-attributes-generics.chk_pass.stderr new file mode 100644 index 0000000000000..0630811d4f7ea --- /dev/null +++ b/tests/ui/contracts/contract-attributes-generics.chk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-generics.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-generics.rs b/tests/ui/contracts/contract-attributes-generics.rs new file mode 100644 index 0000000000000..fd79c6abedd85 --- /dev/null +++ b/tests/ui/contracts/contract-attributes-generics.rs @@ -0,0 +1,71 @@ +//! Test that contracts can be applied to generic functions. + +//@ revisions: unchk_pass chk_pass chk_fail_pre chk_fail_post chk_const_fail +// +//@ [unchk_pass] run-pass +//@ [chk_pass] run-pass +// +//@ [chk_fail_pre] run-fail +//@ [chk_fail_post] run-fail +//@ [chk_const_fail] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +// +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes +//@ [chk_const_fail] compile-flags: -Zcontract-checks=yes + +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +use std::ops::Sub; + +/// Dummy fn contract that precondition fails for val < 0, and post-condition fail for val == 1 +#[core::contracts::requires(val > 0u8.into())] +#[core::contracts::ensures(|ret| *ret > 0u8.into())] +fn decrement(val: T) -> T +where T: PartialOrd + Sub + From +{ + val - 1u8.into() +} + +/// Create a structure that takes a constant parameter. +#[allow(dead_code)] +struct Capped(usize); + +/// Now declare a function to create stars which shouldn't exceed 5 stars. +// Add redundant braces to ensure the built-in macro can handle this syntax. +#[allow(unused_braces)] +#[core::contracts::requires(num <= 5)] +unsafe fn stars_unchecked(num: usize) -> Capped<{ 5 }> { + Capped(num) +} + + +fn main() { + check_decrement(); + check_stars(); +} + +fn check_stars() { + // This should always pass. + let _ = unsafe { stars_unchecked(3) }; + + // This violates the contract. + #[cfg(any(unchk_pass, chk_const_fail))] + let _ = unsafe { stars_unchecked(10) }; +} + +fn check_decrement() { + // This should always pass + assert_eq!(decrement(10u8), 9u8); + + // This should fail requires but pass with no contract check. + #[cfg(any(unchk_pass, chk_fail_pre))] + assert_eq!(decrement(-2i128), -3i128); + + // This should fail ensures but pass with no contract check. + #[cfg(any(unchk_pass, chk_fail_post))] + assert_eq!(decrement(1i32), 0i32); +} diff --git a/tests/ui/contracts/contract-attributes-generics.unchk_pass.stderr b/tests/ui/contracts/contract-attributes-generics.unchk_pass.stderr new file mode 100644 index 0000000000000..0630811d4f7ea --- /dev/null +++ b/tests/ui/contracts/contract-attributes-generics.unchk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-generics.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-nest.chk_fail_post.stderr b/tests/ui/contracts/contract-attributes-nest.chk_fail_post.stderr new file mode 100644 index 0000000000000..9ca95b8bb01a4 --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.chk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-nest.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-nest.chk_fail_pre.stderr b/tests/ui/contracts/contract-attributes-nest.chk_fail_pre.stderr new file mode 100644 index 0000000000000..9ca95b8bb01a4 --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.chk_fail_pre.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-nest.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-nest.chk_pass.stderr b/tests/ui/contracts/contract-attributes-nest.chk_pass.stderr new file mode 100644 index 0000000000000..9ca95b8bb01a4 --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.chk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-nest.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-nest.rs b/tests/ui/contracts/contract-attributes-nest.rs new file mode 100644 index 0000000000000..e1e61b88f282e --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.rs @@ -0,0 +1,45 @@ +//@ revisions: unchk_pass unchk_fail_pre unchk_fail_post chk_pass chk_fail_pre chk_fail_post +// +//@ [unchk_pass] run-pass +//@ [unchk_fail_pre] run-pass +//@ [unchk_fail_post] run-pass +//@ [chk_pass] run-pass +// +//@ [chk_fail_pre] run-fail +//@ [chk_fail_post] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_pre] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no +// +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes + +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +#[core::contracts::requires(x.baz > 0)] +#[core::contracts::ensures(|ret| *ret > 100)] +fn nest(x: Baz) -> i32 +{ + loop { + return x.baz + 50; + } +} + +struct Baz { baz: i32 } + +const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 }; +#[cfg(any(unchk_fail_post, chk_fail_post))] +const BAZ_FAIL_POST: Baz = Baz { baz: 10 }; +#[cfg(any(unchk_fail_pre, chk_fail_pre))] +const BAZ_FAIL_PRE: Baz = Baz { baz: -10 }; + +fn main() { + assert_eq!(nest(BAZ_PASS_PRE_POST), 150); + #[cfg(any(unchk_fail_pre, chk_fail_pre))] + nest(BAZ_FAIL_PRE); + #[cfg(any(unchk_fail_post, chk_fail_post))] + nest(BAZ_FAIL_POST); +} diff --git a/tests/ui/contracts/contract-attributes-nest.unchk_fail_post.stderr b/tests/ui/contracts/contract-attributes-nest.unchk_fail_post.stderr new file mode 100644 index 0000000000000..9ca95b8bb01a4 --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.unchk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-nest.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-nest.unchk_fail_pre.stderr b/tests/ui/contracts/contract-attributes-nest.unchk_fail_pre.stderr new file mode 100644 index 0000000000000..9ca95b8bb01a4 --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.unchk_fail_pre.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-nest.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-nest.unchk_pass.stderr b/tests/ui/contracts/contract-attributes-nest.unchk_pass.stderr new file mode 100644 index 0000000000000..9ca95b8bb01a4 --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.unchk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-nest.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-tail.chk_fail_post.stderr b/tests/ui/contracts/contract-attributes-tail.chk_fail_post.stderr new file mode 100644 index 0000000000000..f87e7e19fa3db --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.chk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-tail.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-tail.chk_fail_pre.stderr b/tests/ui/contracts/contract-attributes-tail.chk_fail_pre.stderr new file mode 100644 index 0000000000000..f87e7e19fa3db --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.chk_fail_pre.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-tail.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-tail.chk_pass.stderr b/tests/ui/contracts/contract-attributes-tail.chk_pass.stderr new file mode 100644 index 0000000000000..f87e7e19fa3db --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.chk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-tail.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-tail.rs b/tests/ui/contracts/contract-attributes-tail.rs new file mode 100644 index 0000000000000..ce4a6be5b82f7 --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.rs @@ -0,0 +1,43 @@ +//@ revisions: unchk_pass unchk_fail_pre unchk_fail_post chk_pass chk_fail_pre chk_fail_post +// +//@ [unchk_pass] run-pass +//@ [unchk_fail_pre] run-pass +//@ [unchk_fail_post] run-pass +//@ [chk_pass] run-pass +// +//@ [chk_fail_pre] run-fail +//@ [chk_fail_post] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_pre] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no +// +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes + +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +#[core::contracts::requires(x.baz > 0)] +#[core::contracts::ensures(|ret| *ret > 100)] +fn tail(x: Baz) -> i32 +{ + x.baz + 50 +} + +struct Baz { baz: i32 } + +const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 }; +#[cfg(any(unchk_fail_post, chk_fail_post))] +const BAZ_FAIL_POST: Baz = Baz { baz: 10 }; +#[cfg(any(unchk_fail_pre, chk_fail_pre))] +const BAZ_FAIL_PRE: Baz = Baz { baz: -10 }; + +fn main() { + assert_eq!(tail(BAZ_PASS_PRE_POST), 150); + #[cfg(any(unchk_fail_pre, chk_fail_pre))] + tail(BAZ_FAIL_PRE); + #[cfg(any(unchk_fail_post, chk_fail_post))] + tail(BAZ_FAIL_POST); +} diff --git a/tests/ui/contracts/contract-attributes-tail.unchk_fail_post.stderr b/tests/ui/contracts/contract-attributes-tail.unchk_fail_post.stderr new file mode 100644 index 0000000000000..f87e7e19fa3db --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.unchk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-tail.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-tail.unchk_fail_pre.stderr b/tests/ui/contracts/contract-attributes-tail.unchk_fail_pre.stderr new file mode 100644 index 0000000000000..f87e7e19fa3db --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.unchk_fail_pre.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-tail.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-tail.unchk_pass.stderr b/tests/ui/contracts/contract-attributes-tail.unchk_pass.stderr new file mode 100644 index 0000000000000..f87e7e19fa3db --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.unchk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-tail.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-captures-via-closure-copy.rs b/tests/ui/contracts/contract-captures-via-closure-copy.rs new file mode 100644 index 0000000000000..32c6d2bf4fe17 --- /dev/null +++ b/tests/ui/contracts/contract-captures-via-closure-copy.rs @@ -0,0 +1,26 @@ +//@ run-fail +//@ compile-flags: -Zcontract-checks=yes + +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +struct Baz { + baz: i32 +} + +#[track_caller] +#[core::contracts::requires(x.baz > 0)] +#[core::contracts::ensures({let old = x.baz; move |ret:&Baz| ret.baz == old*2 })] +// Relevant thing is this: ^^^^^^^^^^^^^^^ +// because we are capturing state that is Copy +fn doubler(x: Baz) -> Baz { + Baz { baz: x.baz + 10 } +} + +fn main() { + assert_eq!(doubler(Baz { baz: 10 }).baz, 20); + assert_eq!(doubler(Baz { baz: 100 }).baz, 200); + // This is a *run-fail* test because it is still exercising the + // contract machinery, specifically because this second invocation + // of `doubler` shows how the code does not meet its contract. +} diff --git a/tests/ui/contracts/contract-captures-via-closure-copy.stderr b/tests/ui/contracts/contract-captures-via-closure-copy.stderr new file mode 100644 index 0000000000000..d92db601608f5 --- /dev/null +++ b/tests/ui/contracts/contract-captures-via-closure-copy.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-captures-via-closure-copy.rs:4:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-captures-via-closure-noncopy.rs b/tests/ui/contracts/contract-captures-via-closure-noncopy.rs new file mode 100644 index 0000000000000..976f64c7fd911 --- /dev/null +++ b/tests/ui/contracts/contract-captures-via-closure-noncopy.rs @@ -0,0 +1,23 @@ +//@ compile-flags: -Zcontract-checks=yes + +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +struct Baz { + baz: i32 +} + +#[track_caller] +#[core::contracts::requires(x.baz > 0)] +#[core::contracts::ensures({let old = x; move |ret:&Baz| ret.baz == old.baz*2 })] +// Relevant thing is this: ^^^^^^^^^^^ +// because we are capturing state that is non-Copy. +//~^^^ ERROR trait bound `Baz: std::marker::Copy` is not satisfied +fn doubler(x: Baz) -> Baz { + Baz { baz: x.baz + 10 } +} + +fn main() { + assert_eq!(doubler(Baz { baz: 10 }).baz, 20); + assert_eq!(doubler(Baz { baz: 100 }).baz, 200); +} diff --git a/tests/ui/contracts/contract-captures-via-closure-noncopy.stderr b/tests/ui/contracts/contract-captures-via-closure-noncopy.stderr new file mode 100644 index 0000000000000..4a47671fee191 --- /dev/null +++ b/tests/ui/contracts/contract-captures-via-closure-noncopy.stderr @@ -0,0 +1,36 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-captures-via-closure-noncopy.rs:3:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0277]: the trait bound `Baz: std::marker::Copy` is not satisfied in `{closure@$DIR/contract-captures-via-closure-noncopy.rs:12:42: 12:57}` + --> $DIR/contract-captures-via-closure-noncopy.rs:12:1 + | +LL | #[core::contracts::ensures({let old = x; move |ret:&Baz| ret.baz == old.baz*2 })] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------------------------------------^^^^ + | | | + | | within this `{closure@$DIR/contract-captures-via-closure-noncopy.rs:12:42: 12:57}` + | | this tail expression is of type `{closure@contract-captures-via-closure-noncopy.rs:12:42}` + | unsatisfied trait bound + | + = help: within `{closure@$DIR/contract-captures-via-closure-noncopy.rs:12:42: 12:57}`, the trait `std::marker::Copy` is not implemented for `Baz` +note: required because it's used within this closure + --> $DIR/contract-captures-via-closure-noncopy.rs:12:42 + | +LL | #[core::contracts::ensures({let old = x; move |ret:&Baz| ret.baz == old.baz*2 })] + | ^^^^^^^^^^^^^^^ +note: required by a bound in `build_check_ensures` + --> $SRC_DIR/core/src/contracts.rs:LL:COL +help: consider annotating `Baz` with `#[derive(Copy)]` + | +LL + #[derive(Copy)] +LL | struct Baz { + | + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_ret.stderr b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_ret.stderr new file mode 100644 index 0000000000000..d693fad446a4d --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_ret.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-ensures-early-fn-exit.rs:16:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_try.stderr b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_try.stderr new file mode 100644 index 0000000000000..d693fad446a4d --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_try.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-ensures-early-fn-exit.rs:16:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_yeet.stderr b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_yeet.stderr new file mode 100644 index 0000000000000..d693fad446a4d --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_yeet.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-ensures-early-fn-exit.rs:16:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_pass.stderr b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_pass.stderr new file mode 100644 index 0000000000000..d693fad446a4d --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-ensures-early-fn-exit.rs:16:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.rs b/tests/ui/contracts/contracts-ensures-early-fn-exit.rs new file mode 100644 index 0000000000000..034cead3b4e9f --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.rs @@ -0,0 +1,49 @@ +//@ revisions: unchk_pass chk_pass chk_fail_try chk_fail_ret chk_fail_yeet +// +//@ [unchk_pass] run-pass +//@ [chk_pass] run-pass +//@ [chk_fail_try] run-fail +//@ [chk_fail_ret] run-fail +//@ [chk_fail_yeet] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_try] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_ret] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_yeet] compile-flags: -Zcontract-checks=yes +//! This test ensures that ensures clauses are checked for different return points of a function. + +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] +#![feature(yeet_expr)] + +/// This ensures will fail in different return points depending on the input. +#[core::contracts::ensures(|ret: &Option| ret.is_some())] +fn try_sum(x: u32, y: u32, z: u32) -> Option { + // Use Yeet to return early. + if x == u32::MAX && (y > 0 || z > 0) { do yeet } + + // Use `?` to early return. + let partial = x.checked_add(y)?; + + // Explicitly use `return` clause. + if u32::MAX - partial < z { + return None; + } + + Some(partial + z) +} + +fn main() { + // This should always succeed + assert_eq!(try_sum(0, 1, 2), Some(3)); + + #[cfg(any(unchk_pass, chk_fail_yeet))] + assert_eq!(try_sum(u32::MAX, 1, 1), None); + + #[cfg(any(unchk_pass, chk_fail_try))] + assert_eq!(try_sum(u32::MAX - 10, 12, 0), None); + + #[cfg(any(unchk_pass, chk_fail_ret))] + assert_eq!(try_sum(u32::MAX - 10, 2, 100), None); +} diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.unchk_pass.stderr b/tests/ui/contracts/contracts-ensures-early-fn-exit.unchk_pass.stderr new file mode 100644 index 0000000000000..d693fad446a4d --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.unchk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-ensures-early-fn-exit.rs:16:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.rs b/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.rs new file mode 100644 index 0000000000000..f01a852fbff34 --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.rs @@ -0,0 +1,15 @@ +//@ run-pass +//@ compile-flags: -Zcontract-checks=yes +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +#[core::contracts::ensures(|ret| *ret > 0)] +fn outer() -> i32 { + let inner_closure = || -> i32 { 0 }; + inner_closure(); + 10 +} + +fn main() { + outer(); +} diff --git a/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.stderr b/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.stderr new file mode 100644 index 0000000000000..49a372b53c7d8 --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-ensures-is-not-inherited-when-nesting.rs:3:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.rs b/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.rs new file mode 100644 index 0000000000000..2c2a4a6978550 --- /dev/null +++ b/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.rs @@ -0,0 +1,17 @@ +//@ run-pass +//@ compile-flags: -Zcontract-checks=yes +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +struct Outer { outer: std::cell::Cell } + +#[core::contracts::requires(x.outer.get() > 0)] +fn outer(x: Outer) { + let inner_closure = || { }; + x.outer.set(0); + inner_closure(); +} + +fn main() { + outer(Outer { outer: 1.into() }); +} diff --git a/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.stderr b/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.stderr new file mode 100644 index 0000000000000..48898c4434ad5 --- /dev/null +++ b/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-requires-is-not-inherited-when-nesting.rs:3:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/disallow-contract-annotation-on-non-fn.rs b/tests/ui/contracts/disallow-contract-annotation-on-non-fn.rs new file mode 100644 index 0000000000000..69be906782a67 --- /dev/null +++ b/tests/ui/contracts/disallow-contract-annotation-on-non-fn.rs @@ -0,0 +1,53 @@ +//! Checks for compilation errors related to adding contracts to non-function items. + +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] +#![allow(dead_code)] + +#[core::contracts::requires(true)] +//~^ ERROR contract annotations can only be used on functions +struct Dummy(usize); + +#[core::contracts::ensures(|v| v == 100)] +//~^ ERROR contract annotations can only be used on functions +const MAX_VAL: usize = 100; + +// FIXME: Improve the error message here. The macro thinks this is a function. +#[core::contracts::ensures(|v| v == 100)] +//~^ ERROR contract annotations is only supported in functions with bodies +type NewDummy = fn(usize) -> Dummy; + +#[core::contracts::ensures(|v| v == 100)] +//~^ ERROR contract annotations is only supported in functions with bodies +const NEW_DUMMY_FN : fn(usize) -> Dummy = || { Dummy(0) }; + +#[core::contracts::requires(true)] +//~^ ERROR contract annotations can only be used on functions +impl Dummy { + + // This should work + #[core::contracts::ensures(|ret| ret.0 == v)] + fn new(v: usize) -> Dummy { + Dummy(v) + } +} + +#[core::contracts::ensures(|dummy| dummy.0 > 0)] +//~^ ERROR contract annotations can only be used on functions +impl From for Dummy { + // This should work. + #[core::contracts::ensures(|ret| ret.0 == v)] + fn from(value: usize) -> Self { + Dummy::new(value) + } +} + +/// You should not be able to annotate a trait either. +#[core::contracts::requires(true)] +//~^ ERROR contract annotations can only be used on functions +pub trait DummyBuilder { + fn build() -> Dummy; +} + +fn main() { +} diff --git a/tests/ui/contracts/disallow-contract-annotation-on-non-fn.stderr b/tests/ui/contracts/disallow-contract-annotation-on-non-fn.stderr new file mode 100644 index 0000000000000..0a7fff8183e09 --- /dev/null +++ b/tests/ui/contracts/disallow-contract-annotation-on-non-fn.stderr @@ -0,0 +1,53 @@ +error: contract annotations can only be used on functions + --> $DIR/disallow-contract-annotation-on-non-fn.rs:7:1 + | +LL | #[core::contracts::requires(true)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations can only be used on functions + --> $DIR/disallow-contract-annotation-on-non-fn.rs:11:1 + | +LL | #[core::contracts::ensures(|v| v == 100)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations is only supported in functions with bodies + --> $DIR/disallow-contract-annotation-on-non-fn.rs:16:1 + | +LL | #[core::contracts::ensures(|v| v == 100)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations is only supported in functions with bodies + --> $DIR/disallow-contract-annotation-on-non-fn.rs:20:1 + | +LL | #[core::contracts::ensures(|v| v == 100)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations can only be used on functions + --> $DIR/disallow-contract-annotation-on-non-fn.rs:24:1 + | +LL | #[core::contracts::requires(true)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations can only be used on functions + --> $DIR/disallow-contract-annotation-on-non-fn.rs:35:1 + | +LL | #[core::contracts::ensures(|dummy| dummy.0 > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations can only be used on functions + --> $DIR/disallow-contract-annotation-on-non-fn.rs:46:1 + | +LL | #[core::contracts::requires(true)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/disallow-contract-annotation-on-non-fn.rs:3:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: aborting due to 7 previous errors; 1 warning emitted + diff --git a/tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs b/tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs new file mode 100644 index 0000000000000..6d8cd3949eedb --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs @@ -0,0 +1,44 @@ +//@ revisions: unchk_pass unchk_fail_pre unchk_fail_post chk_pass chk_fail_pre chk_fail_post +// +//@ [unchk_pass] run-pass +//@ [unchk_fail_pre] run-pass +//@ [unchk_fail_post] run-pass +//@ [chk_pass] run-pass +// +//@ [chk_fail_pre] run-fail +//@ [chk_fail_post] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_pre] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no +// +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes + +#![feature(contracts_internals)] + +fn nest(x: Baz) -> i32 + contract_requires(|| x.baz > 0) + contract_ensures(|ret| *ret > 100) +{ + loop { + return x.baz + 50; + } +} + +struct Baz { baz: i32 } + +const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 }; +#[cfg(any(unchk_fail_post, chk_fail_post))] +const BAZ_FAIL_POST: Baz = Baz { baz: 10 }; +#[cfg(any(unchk_fail_pre, chk_fail_pre))] +const BAZ_FAIL_PRE: Baz = Baz { baz: -10 }; + +fn main() { + assert_eq!(nest(BAZ_PASS_PRE_POST), 150); + #[cfg(any(unchk_fail_pre, chk_fail_pre))] + nest(BAZ_FAIL_PRE); + #[cfg(any(unchk_fail_post, chk_fail_post))] + nest(BAZ_FAIL_POST); +} diff --git a/tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs b/tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs new file mode 100644 index 0000000000000..07ec26f921b80 --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs @@ -0,0 +1,42 @@ +//@ revisions: unchk_pass unchk_fail_pre unchk_fail_post chk_pass chk_fail_pre chk_fail_post +// +//@ [unchk_pass] run-pass +//@ [unchk_fail_pre] run-pass +//@ [unchk_fail_post] run-pass +//@ [chk_pass] run-pass +// +//@ [chk_fail_pre] run-fail +//@ [chk_fail_post] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_pre] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no +// +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes + +#![feature(contracts_internals)] + +fn tail(x: Baz) -> i32 + contract_requires(|| x.baz > 0) + contract_ensures(|ret| *ret > 100) +{ + x.baz + 50 +} + +struct Baz { baz: i32 } + +const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 }; +#[cfg(any(unchk_fail_post, chk_fail_post))] +const BAZ_FAIL_POST: Baz = Baz { baz: 10 }; +#[cfg(any(unchk_fail_pre, chk_fail_pre))] +const BAZ_FAIL_PRE: Baz = Baz { baz: -10 }; + +fn main() { + assert_eq!(tail(BAZ_PASS_PRE_POST), 150); + #[cfg(any(unchk_fail_pre, chk_fail_pre))] + tail(BAZ_FAIL_PRE); + #[cfg(any(unchk_fail_post, chk_fail_post))] + tail(BAZ_FAIL_POST); +} diff --git a/tests/ui/contracts/internal_machinery/contract-intrinsics.rs b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs new file mode 100644 index 0000000000000..ae692afd146fe --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs @@ -0,0 +1,36 @@ +//@ revisions: default unchk_pass chk_pass chk_fail_ensures chk_fail_requires +// +//@ [default] run-pass +//@ [unchk_pass] run-pass +//@ [chk_pass] run-pass +//@ [chk_fail_requires] run-fail +//@ [chk_fail_ensures] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_requires] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_ensures] compile-flags: -Zcontract-checks=yes +#![feature(cfg_contract_checks, contracts_internals, core_intrinsics)] + +fn main() { + #[cfg(any(default, unchk_pass))] // default: disabled + assert_eq!(core::intrinsics::contract_checks(), false); + + #[cfg(chk_pass)] // explicitly enabled + assert_eq!(core::intrinsics::contract_checks(), true); + + // always pass + core::intrinsics::contract_check_requires(|| true); + + // fail if enabled + #[cfg(any(default, unchk_pass, chk_fail_requires))] + core::intrinsics::contract_check_requires(|| false); + + let doubles_to_two = { let old = 2; move |ret| ret + ret == old }; + // Always pass + core::intrinsics::contract_check_ensures(&1, doubles_to_two); + + // Fail if enabled + #[cfg(any(default, unchk_pass, chk_fail_ensures))] + core::intrinsics::contract_check_ensures(&2, doubles_to_two); +} diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.chk_fail_post.stderr b/tests/ui/contracts/internal_machinery/contract-lang-items.chk_fail_post.stderr new file mode 100644 index 0000000000000..a60ce1602659b --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.chk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-lang-items.rs:15:12 + | +LL | #![feature(contracts)] // to access core::contracts + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.chk_pass.stderr b/tests/ui/contracts/internal_machinery/contract-lang-items.chk_pass.stderr new file mode 100644 index 0000000000000..a60ce1602659b --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.chk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-lang-items.rs:15:12 + | +LL | #![feature(contracts)] // to access core::contracts + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.rs b/tests/ui/contracts/internal_machinery/contract-lang-items.rs new file mode 100644 index 0000000000000..e91bbed294d12 --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.rs @@ -0,0 +1,39 @@ +//@ revisions: unchk_pass unchk_fail_post chk_pass chk_fail_post +// +//@ [unchk_pass] run-pass +//@ [unchk_fail_post] run-pass +//@ [chk_pass] run-pass +// +//@ [chk_fail_post] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no +// +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes + +#![feature(contracts)] // to access core::contracts +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] +#![feature(contracts_internals)] // to access check_requires lang item + +fn foo(x: Baz) -> i32 { + let injected_checker = { + core::contracts::build_check_ensures(|ret| *ret > 100) + }; + + let ret = x.baz + 50; + injected_checker(ret) +} + +struct Baz { baz: i32 } + + +const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 }; +#[cfg(any(unchk_fail_post, chk_fail_post))] +const BAZ_FAIL_POST: Baz = Baz { baz: 10 }; + +fn main() { + assert_eq!(foo(BAZ_PASS_PRE_POST), 150); + #[cfg(any(unchk_fail_post, chk_fail_post))] + foo(BAZ_FAIL_POST); +} diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_fail_post.stderr b/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_fail_post.stderr new file mode 100644 index 0000000000000..a60ce1602659b --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-lang-items.rs:15:12 + | +LL | #![feature(contracts)] // to access core::contracts + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_pass.stderr b/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_pass.stderr new file mode 100644 index 0000000000000..a60ce1602659b --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-lang-items.rs:15:12 + | +LL | #![feature(contracts)] // to access core::contracts + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs b/tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs new file mode 100644 index 0000000000000..960ccaed3588a --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs @@ -0,0 +1,15 @@ +//@ run-pass +//@ compile-flags: -Zcontract-checks=yes +#![feature(contracts_internals)] + +fn outer() -> i32 + contract_ensures(|ret| *ret > 0) +{ + let inner_closure = || -> i32 { 0 }; + inner_closure(); + 10 +} + +fn main() { + outer(); +} diff --git a/tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs b/tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs new file mode 100644 index 0000000000000..bee703de16a0b --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs @@ -0,0 +1,17 @@ +//@ run-pass +//@ compile-flags: -Zcontract-checks=yes +#![feature(contracts_internals)] + +struct Outer { outer: std::cell::Cell } + +fn outer(x: Outer) + contract_requires(|| x.outer.get() > 0) +{ + let inner_closure = || { }; + x.outer.set(0); + inner_closure(); +} + +fn main() { + outer(Outer { outer: 1.into() }); +} diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.rs b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs new file mode 100644 index 0000000000000..1b76eef6780fe --- /dev/null +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs @@ -0,0 +1,20 @@ +// gate-test-contracts_internals + +fn main() { + // intrinsics are guarded by contracts_internals feature gate. + core::intrinsics::contract_checks(); + //~^ ERROR use of unstable library feature `contracts_internals` + core::intrinsics::contract_check_requires(|| true); + //~^ ERROR use of unstable library feature `contracts_internals` + core::intrinsics::contract_check_ensures(&1, |_|true); + //~^ ERROR use of unstable library feature `contracts_internals` + + core::contracts::build_check_ensures(|_: &()| true); + //~^ ERROR use of unstable library feature `contracts_internals` + + // ast extensions are guarded by contracts_internals feature gate + fn identity_1() -> i32 contract_requires(|| true) { 10 } + //~^ ERROR contract internal machinery is for internal use only + fn identity_2() -> i32 contract_ensures(|_| true) { 10 } + //~^ ERROR contract internal machinery is for internal use only +} diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr new file mode 100644 index 0000000000000..7302694a7874a --- /dev/null +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr @@ -0,0 +1,63 @@ +error[E0658]: contract internal machinery is for internal use only + --> $DIR/internal-feature-gating.rs:16:28 + | +LL | fn identity_1() -> i32 contract_requires(|| true) { 10 } + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts_internals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: contract internal machinery is for internal use only + --> $DIR/internal-feature-gating.rs:18:28 + | +LL | fn identity_2() -> i32 contract_ensures(|_| true) { 10 } + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts_internals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `contracts_internals` + --> $DIR/internal-feature-gating.rs:5:5 + | +LL | core::intrinsics::contract_checks(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts_internals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `contracts_internals` + --> $DIR/internal-feature-gating.rs:7:5 + | +LL | core::intrinsics::contract_check_requires(|| true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts_internals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `contracts_internals` + --> $DIR/internal-feature-gating.rs:9:5 + | +LL | core::intrinsics::contract_check_ensures(&1, |_|true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts_internals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `contracts_internals` + --> $DIR/internal-feature-gating.rs:12:5 + | +LL | core::contracts::build_check_ensures(|_: &()| true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts_internals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/coroutine/coroutine-region-requirements.migrate.stderr b/tests/ui/coroutine/coroutine-region-requirements.migrate.stderr deleted file mode 100644 index cfee8fc44fe09..0000000000000 --- a/tests/ui/coroutine/coroutine-region-requirements.migrate.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0621]: explicit lifetime required in the type of `x` - --> $DIR/generator-region-requirements.rs:16:51 - | -LL | fn dangle(x: &mut i32) -> &'static mut i32 { - | -------- help: add explicit lifetime `'static` to the type of `x`: `&'static mut i32` -... -LL | GeneratorState::Complete(c) => return c, - | ^ lifetime `'static` required - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0621`. diff --git a/tests/ui/coroutine/coroutine-resume-after-panic.rs b/tests/ui/coroutine/coroutine-resume-after-panic.rs index 2745ebc61326f..1aa547c2a7bd2 100644 --- a/tests/ui/coroutine/coroutine-resume-after-panic.rs +++ b/tests/ui/coroutine/coroutine-resume-after-panic.rs @@ -1,7 +1,7 @@ //@ run-fail //@ needs-unwind //@ error-pattern:coroutine resumed after panicking -//@ ignore-emscripten no processes +//@ needs-subprocess // Test that we get the correct message for resuming a panicked coroutine. diff --git a/tests/ui/coroutine/gen_block.e2024.stderr b/tests/ui/coroutine/gen_block.e2024.stderr index 322259cf2f84f..0491bdbc2e17a 100644 --- a/tests/ui/coroutine/gen_block.e2024.stderr +++ b/tests/ui/coroutine/gen_block.e2024.stderr @@ -1,4 +1,4 @@ -error[E0658]: the `#[coroutines]` attribute is an experimental feature +error[E0658]: the `#[coroutine]` attribute is an experimental feature --> $DIR/gen_block.rs:20:13 | LL | let _ = #[coroutine] || yield true; @@ -8,7 +8,7 @@ LL | let _ = #[coroutine] || yield true; = help: add `#![feature(coroutines)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: the `#[coroutines]` attribute is an experimental feature +error[E0658]: the `#[coroutine]` attribute is an experimental feature --> $DIR/gen_block.rs:24:13 | LL | let _ = #[coroutine] || {}; diff --git a/tests/ui/coroutine/gen_block.none.stderr b/tests/ui/coroutine/gen_block.none.stderr index 15123a49e4855..43437793005a5 100644 --- a/tests/ui/coroutine/gen_block.none.stderr +++ b/tests/ui/coroutine/gen_block.none.stderr @@ -44,7 +44,7 @@ LL | let _ = #[coroutine] || yield true; = help: add `#![feature(coroutines)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: the `#[coroutines]` attribute is an experimental feature +error[E0658]: the `#[coroutine]` attribute is an experimental feature --> $DIR/gen_block.rs:20:13 | LL | let _ = #[coroutine] || yield true; @@ -54,7 +54,7 @@ LL | let _ = #[coroutine] || yield true; = help: add `#![feature(coroutines)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: the `#[coroutines]` attribute is an experimental feature +error[E0658]: the `#[coroutine]` attribute is an experimental feature --> $DIR/gen_block.rs:24:13 | LL | let _ = #[coroutine] || {}; diff --git a/tests/ui/coroutine/gen_block.rs b/tests/ui/coroutine/gen_block.rs index 6734de3b667d2..4494d654eeb62 100644 --- a/tests/ui/coroutine/gen_block.rs +++ b/tests/ui/coroutine/gen_block.rs @@ -18,9 +18,9 @@ fn main() { //~^^ ERROR `yield` can only be used in let _ = #[coroutine] || yield true; //[none]~ ERROR yield syntax is experimental - //~^ ERROR `#[coroutines]` attribute is an experimental feature + //~^ ERROR `#[coroutine]` attribute is an experimental feature //~^^ ERROR yield syntax is experimental let _ = #[coroutine] || {}; - //~^ ERROR `#[coroutines]` attribute is an experimental feature + //~^ ERROR `#[coroutine]` attribute is an experimental feature } diff --git a/tests/ui/coroutine/issue-102645.stderr b/tests/ui/coroutine/issue-102645.stderr index 1ef37d3f7d100..bec0518d8c602 100644 --- a/tests/ui/coroutine/issue-102645.stderr +++ b/tests/ui/coroutine/issue-102645.stderr @@ -8,8 +8,9 @@ note: method defined here --> $SRC_DIR/core/src/ops/coroutine.rs:LL:COL help: provide the argument | -LL | Pin::new(&mut b).resume(()); - | ~~~~ +LL - Pin::new(&mut b).resume(); +LL + Pin::new(&mut b).resume(()); + | error: aborting due to 1 previous error diff --git a/tests/ui/coroutine/issue-105084.rs b/tests/ui/coroutine/issue-105084.rs index 0f6168ec58b02..cddee49901757 100644 --- a/tests/ui/coroutine/issue-105084.rs +++ b/tests/ui/coroutine/issue-105084.rs @@ -2,7 +2,7 @@ #![feature(coroutines)] #![feature(coroutine_clone)] #![feature(coroutine_trait)] -#![feature(rustc_attrs, stmt_expr_attributes)] +#![feature(rustc_attrs, stmt_expr_attributes, liballoc_internals)] use std::ops::Coroutine; use std::pin::Pin; @@ -19,8 +19,7 @@ fn main() { // - create a Box that is ignored for trait computations; // - compute fields (and yields); // - assign to `t`. - let t = #[rustc_box] - Box::new((5, yield)); + let t = std::boxed::box_new((5, yield)); drop(t); }; diff --git a/tests/ui/coroutine/issue-105084.stderr b/tests/ui/coroutine/issue-105084.stderr index 073f1fbea4c63..23c1fdc545922 100644 --- a/tests/ui/coroutine/issue-105084.stderr +++ b/tests/ui/coroutine/issue-105084.stderr @@ -1,5 +1,5 @@ error[E0382]: borrow of moved value: `g` - --> $DIR/issue-105084.rs:39:14 + --> $DIR/issue-105084.rs:38:14 | LL | let mut g = #[coroutine] | ----- move occurs because `g` has type `{coroutine@$DIR/issue-105084.rs:16:5: 16:7}`, which does not implement the `Copy` trait @@ -23,7 +23,7 @@ LL | let mut h = copy(g.clone()); | ++++++++ error[E0277]: the trait bound `Box<(i32, ())>: Copy` is not satisfied in `{coroutine@$DIR/issue-105084.rs:16:5: 16:7}` - --> $DIR/issue-105084.rs:33:17 + --> $DIR/issue-105084.rs:32:17 | LL | || { | -- within this `{coroutine@$DIR/issue-105084.rs:16:5: 16:7}` @@ -32,13 +32,13 @@ LL | let mut h = copy(g); | ^^^^^^^ within `{coroutine@$DIR/issue-105084.rs:16:5: 16:7}`, the trait `Copy` is not implemented for `Box<(i32, ())>` | note: coroutine does not implement `Copy` as this value is used across a yield - --> $DIR/issue-105084.rs:23:22 + --> $DIR/issue-105084.rs:22:41 | -LL | Box::new((5, yield)); - | -------------^^^^^-- - | | | - | | yield occurs here, with `Box::new((5, yield))` maybe used later - | has type `Box<(i32, ())>` which does not implement `Copy` +LL | let t = std::boxed::box_new((5, yield)); + | ------------------------^^^^^-- + | | | + | | yield occurs here, with `std::boxed::box_new((5, yield))` maybe used later + | has type `Box<(i32, ())>` which does not implement `Copy` note: required by a bound in `copy` --> $DIR/issue-105084.rs:10:12 | diff --git a/tests/ui/coroutine/issue-52304.rs b/tests/ui/coroutine/issue-52304.rs index 552bc0028ee04..77dfe8391956f 100644 --- a/tests/ui/coroutine/issue-52304.rs +++ b/tests/ui/coroutine/issue-52304.rs @@ -1,4 +1,6 @@ //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) #![feature(coroutines, coroutine_trait)] diff --git a/tests/ui/coroutine/resume-arg-outlives-2.rs b/tests/ui/coroutine/resume-arg-outlives-2.rs index 387b143ea279e..a805cea9b7ea4 100644 --- a/tests/ui/coroutine/resume-arg-outlives-2.rs +++ b/tests/ui/coroutine/resume-arg-outlives-2.rs @@ -18,8 +18,8 @@ fn demo<'not_static>(s: &'not_static str) -> thread::JoinHandle<()> { // exploit: generator.as_mut().resume(""); generator.as_mut().resume(s); // <- generator hoards it as `let ctx`. - //~^ ERROR borrowed data escapes outside of function thread::spawn(move || { + //~^ ERROR borrowed data escapes outside of function thread::sleep(time::Duration::from_millis(200)); generator.as_mut().resume(""); // <- resumes from the last `yield`, running `dbg!(ctx)`. }) diff --git a/tests/ui/coroutine/resume-arg-outlives-2.stderr b/tests/ui/coroutine/resume-arg-outlives-2.stderr index 3d630d7e7e483..92192d9a55721 100644 --- a/tests/ui/coroutine/resume-arg-outlives-2.stderr +++ b/tests/ui/coroutine/resume-arg-outlives-2.stderr @@ -1,16 +1,20 @@ error[E0521]: borrowed data escapes outside of function - --> $DIR/resume-arg-outlives-2.rs:20:5 + --> $DIR/resume-arg-outlives-2.rs:21:5 | -LL | fn demo<'not_static>(s: &'not_static str) -> thread::JoinHandle<()> { - | ----------- - `s` is a reference that is only valid in the function body - | | - | lifetime `'not_static` defined here +LL | fn demo<'not_static>(s: &'not_static str) -> thread::JoinHandle<()> { + | ----------- - `s` is a reference that is only valid in the function body + | | + | lifetime `'not_static` defined here ... -LL | generator.as_mut().resume(s); // <- generator hoards it as `let ctx`. - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | `s` escapes the function body here - | argument requires that `'not_static` must outlive `'static` +LL | / thread::spawn(move || { +LL | | +LL | | thread::sleep(time::Duration::from_millis(200)); +LL | | generator.as_mut().resume(""); // <- resumes from the last `yield`, running `dbg!(ctx)`. +LL | | }) + | | ^ + | | | + | |______`s` escapes the function body here + | argument requires that `'not_static` must outlive `'static` error: aborting due to 1 previous error diff --git a/tests/ui/coroutine/resume-arg-outlives.stderr b/tests/ui/coroutine/resume-arg-outlives.stderr index 2a6337b494515..0150009c8fa23 100644 --- a/tests/ui/coroutine/resume-arg-outlives.stderr +++ b/tests/ui/coroutine/resume-arg-outlives.stderr @@ -9,12 +9,14 @@ LL | generator | help: consider changing `impl Coroutine<&'not_static str> + 'static`'s explicit `'static` bound to the lifetime of argument `s` | -LL | fn demo<'not_static>(s: &'not_static str) -> Pin + 'not_static>> { - | ~~~~~~~~~~~ +LL - fn demo<'not_static>(s: &'not_static str) -> Pin + 'static>> { +LL + fn demo<'not_static>(s: &'not_static str) -> Pin + 'not_static>> { + | help: alternatively, add an explicit `'static` bound to this reference | -LL | fn demo<'not_static>(s: &'static str) -> Pin + 'static>> { - | ~~~~~~~~~~~~ +LL - fn demo<'not_static>(s: &'not_static str) -> Pin + 'static>> { +LL + fn demo<'not_static>(s: &'static str) -> Pin + 'static>> { + | error: aborting due to 1 previous error diff --git a/tests/ui/coverage-attr/bad-attr-ice.feat.stderr b/tests/ui/coverage-attr/bad-attr-ice.feat.stderr index 50e1c39d4f8bc..a8a70e363b7e6 100644 --- a/tests/ui/coverage-attr/bad-attr-ice.feat.stderr +++ b/tests/ui/coverage-attr/bad-attr-ice.feat.stderr @@ -6,9 +6,11 @@ LL | #[coverage] | help: the following are the possible correct uses | -LL | #[coverage(off)] +LL - #[coverage] +LL + #[coverage(off)] | -LL | #[coverage(on)] +LL - #[coverage] +LL + #[coverage(on)] | error: aborting due to 1 previous error diff --git a/tests/ui/coverage-attr/bad-attr-ice.nofeat.stderr b/tests/ui/coverage-attr/bad-attr-ice.nofeat.stderr index e8bdd99c9b9b6..6443fafef3e41 100644 --- a/tests/ui/coverage-attr/bad-attr-ice.nofeat.stderr +++ b/tests/ui/coverage-attr/bad-attr-ice.nofeat.stderr @@ -6,9 +6,11 @@ LL | #[coverage] | help: the following are the possible correct uses | -LL | #[coverage(off)] +LL - #[coverage] +LL + #[coverage(off)] | -LL | #[coverage(on)] +LL - #[coverage] +LL + #[coverage(on)] | error[E0658]: the `#[coverage]` attribute is an experimental feature diff --git a/tests/ui/coverage-attr/bad-syntax.stderr b/tests/ui/coverage-attr/bad-syntax.stderr index 5592e89070d80..3123066e7bf97 100644 --- a/tests/ui/coverage-attr/bad-syntax.stderr +++ b/tests/ui/coverage-attr/bad-syntax.stderr @@ -6,10 +6,12 @@ LL | #[coverage] | help: the following are the possible correct uses | -LL | #[coverage(off)] - | ~~~~~~~~~~~~~~~~ -LL | #[coverage(on)] - | ~~~~~~~~~~~~~~~ +LL - #[coverage] +LL + #[coverage(off)] + | +LL - #[coverage] +LL + #[coverage(on)] + | error: malformed `coverage` attribute input --> $DIR/bad-syntax.rs:20:1 @@ -19,10 +21,12 @@ LL | #[coverage = true] | help: the following are the possible correct uses | -LL | #[coverage(off)] - | ~~~~~~~~~~~~~~~~ -LL | #[coverage(on)] - | ~~~~~~~~~~~~~~~ +LL - #[coverage = true] +LL + #[coverage(off)] + | +LL - #[coverage = true] +LL + #[coverage(on)] + | error: malformed `coverage` attribute input --> $DIR/bad-syntax.rs:23:1 @@ -32,10 +36,12 @@ LL | #[coverage()] | help: the following are the possible correct uses | -LL | #[coverage(off)] - | ~~~~~~~~~~~~~~~~ -LL | #[coverage(on)] - | ~~~~~~~~~~~~~~~ +LL - #[coverage()] +LL + #[coverage(off)] + | +LL - #[coverage()] +LL + #[coverage(on)] + | error: malformed `coverage` attribute input --> $DIR/bad-syntax.rs:26:1 @@ -45,10 +51,12 @@ LL | #[coverage(off, off)] | help: the following are the possible correct uses | -LL | #[coverage(off)] - | ~~~~~~~~~~~~~~~~ -LL | #[coverage(on)] - | ~~~~~~~~~~~~~~~ +LL - #[coverage(off, off)] +LL + #[coverage(off)] + | +LL - #[coverage(off, off)] +LL + #[coverage(on)] + | error: malformed `coverage` attribute input --> $DIR/bad-syntax.rs:29:1 @@ -58,10 +66,12 @@ LL | #[coverage(off, on)] | help: the following are the possible correct uses | -LL | #[coverage(off)] - | ~~~~~~~~~~~~~~~~ -LL | #[coverage(on)] - | ~~~~~~~~~~~~~~~ +LL - #[coverage(off, on)] +LL + #[coverage(off)] + | +LL - #[coverage(off, on)] +LL + #[coverage(on)] + | error: malformed `coverage` attribute input --> $DIR/bad-syntax.rs:32:1 @@ -71,10 +81,12 @@ LL | #[coverage(bogus)] | help: the following are the possible correct uses | -LL | #[coverage(off)] - | ~~~~~~~~~~~~~~~~ -LL | #[coverage(on)] - | ~~~~~~~~~~~~~~~ +LL - #[coverage(bogus)] +LL + #[coverage(off)] + | +LL - #[coverage(bogus)] +LL + #[coverage(on)] + | error: malformed `coverage` attribute input --> $DIR/bad-syntax.rs:35:1 @@ -84,10 +96,12 @@ LL | #[coverage(bogus, off)] | help: the following are the possible correct uses | -LL | #[coverage(off)] - | ~~~~~~~~~~~~~~~~ -LL | #[coverage(on)] - | ~~~~~~~~~~~~~~~ +LL - #[coverage(bogus, off)] +LL + #[coverage(off)] + | +LL - #[coverage(bogus, off)] +LL + #[coverage(on)] + | error: malformed `coverage` attribute input --> $DIR/bad-syntax.rs:38:1 @@ -97,10 +111,12 @@ LL | #[coverage(off, bogus)] | help: the following are the possible correct uses | -LL | #[coverage(off)] - | ~~~~~~~~~~~~~~~~ -LL | #[coverage(on)] - | ~~~~~~~~~~~~~~~ +LL - #[coverage(off, bogus)] +LL + #[coverage(off)] + | +LL - #[coverage(off, bogus)] +LL + #[coverage(on)] + | error: expected identifier, found `,` --> $DIR/bad-syntax.rs:44:12 diff --git a/tests/ui/coverage-attr/name-value.stderr b/tests/ui/coverage-attr/name-value.stderr index bfd22ed5451bb..31a635b57e546 100644 --- a/tests/ui/coverage-attr/name-value.stderr +++ b/tests/ui/coverage-attr/name-value.stderr @@ -6,9 +6,11 @@ LL | #[coverage = "off"] | help: the following are the possible correct uses | -LL | #[coverage(off)] +LL - #[coverage = "off"] +LL + #[coverage(off)] | -LL | #[coverage(on)] +LL - #[coverage = "off"] +LL + #[coverage(on)] | error: malformed `coverage` attribute input @@ -19,9 +21,11 @@ LL | #![coverage = "off"] | help: the following are the possible correct uses | -LL | #![coverage(off)] +LL - #![coverage = "off"] +LL + #![coverage(off)] | -LL | #![coverage(on)] +LL - #![coverage = "off"] +LL + #![coverage(on)] | error: malformed `coverage` attribute input @@ -32,9 +36,11 @@ LL | #[coverage = "off"] | help: the following are the possible correct uses | -LL | #[coverage(off)] +LL - #[coverage = "off"] +LL + #[coverage(off)] | -LL | #[coverage(on)] +LL - #[coverage = "off"] +LL + #[coverage(on)] | error: malformed `coverage` attribute input @@ -45,9 +51,11 @@ LL | #[coverage = "off"] | help: the following are the possible correct uses | -LL | #[coverage(off)] +LL - #[coverage = "off"] +LL + #[coverage(off)] | -LL | #[coverage(on)] +LL - #[coverage = "off"] +LL + #[coverage(on)] | error: malformed `coverage` attribute input @@ -58,9 +66,11 @@ LL | #[coverage = "off"] | help: the following are the possible correct uses | -LL | #[coverage(off)] +LL - #[coverage = "off"] +LL + #[coverage(off)] | -LL | #[coverage(on)] +LL - #[coverage = "off"] +LL + #[coverage(on)] | error: malformed `coverage` attribute input @@ -71,9 +81,11 @@ LL | #[coverage = "off"] | help: the following are the possible correct uses | -LL | #[coverage(off)] +LL - #[coverage = "off"] +LL + #[coverage(off)] | -LL | #[coverage(on)] +LL - #[coverage = "off"] +LL + #[coverage(on)] | error: malformed `coverage` attribute input @@ -84,9 +96,11 @@ LL | #[coverage = "off"] | help: the following are the possible correct uses | -LL | #[coverage(off)] +LL - #[coverage = "off"] +LL + #[coverage(off)] | -LL | #[coverage(on)] +LL - #[coverage = "off"] +LL + #[coverage(on)] | error: malformed `coverage` attribute input @@ -97,9 +111,11 @@ LL | #[coverage = "off"] | help: the following are the possible correct uses | -LL | #[coverage(off)] +LL - #[coverage = "off"] +LL + #[coverage(off)] | -LL | #[coverage(on)] +LL - #[coverage = "off"] +LL + #[coverage(on)] | error: malformed `coverage` attribute input @@ -110,9 +126,11 @@ LL | #[coverage = "off"] | help: the following are the possible correct uses | -LL | #[coverage(off)] +LL - #[coverage = "off"] +LL + #[coverage(off)] | -LL | #[coverage(on)] +LL - #[coverage = "off"] +LL + #[coverage(on)] | error: malformed `coverage` attribute input @@ -123,9 +141,11 @@ LL | #[coverage = "off"] | help: the following are the possible correct uses | -LL | #[coverage(off)] +LL - #[coverage = "off"] +LL + #[coverage(off)] | -LL | #[coverage(on)] +LL - #[coverage = "off"] +LL + #[coverage(on)] | error: malformed `coverage` attribute input @@ -136,9 +156,11 @@ LL | #[coverage = "off"] | help: the following are the possible correct uses | -LL | #[coverage(off)] +LL - #[coverage = "off"] +LL + #[coverage(off)] | -LL | #[coverage(on)] +LL - #[coverage = "off"] +LL + #[coverage(on)] | error: malformed `coverage` attribute input @@ -149,9 +171,11 @@ LL | #[coverage = "off"] | help: the following are the possible correct uses | -LL | #[coverage(off)] +LL - #[coverage = "off"] +LL + #[coverage(off)] | -LL | #[coverage(on)] +LL - #[coverage = "off"] +LL + #[coverage(on)] | error[E0788]: coverage attribute not allowed here diff --git a/tests/ui/coverage-attr/subword.stderr b/tests/ui/coverage-attr/subword.stderr index a672ff4ac41d3..a5d1a492181be 100644 --- a/tests/ui/coverage-attr/subword.stderr +++ b/tests/ui/coverage-attr/subword.stderr @@ -6,10 +6,12 @@ LL | #[coverage(yes(milord))] | help: the following are the possible correct uses | -LL | #[coverage(off)] - | ~~~~~~~~~~~~~~~~ -LL | #[coverage(on)] - | ~~~~~~~~~~~~~~~ +LL - #[coverage(yes(milord))] +LL + #[coverage(off)] + | +LL - #[coverage(yes(milord))] +LL + #[coverage(on)] + | error: malformed `coverage` attribute input --> $DIR/subword.rs:11:1 @@ -19,10 +21,12 @@ LL | #[coverage(no(milord))] | help: the following are the possible correct uses | -LL | #[coverage(off)] - | ~~~~~~~~~~~~~~~~ -LL | #[coverage(on)] - | ~~~~~~~~~~~~~~~ +LL - #[coverage(no(milord))] +LL + #[coverage(off)] + | +LL - #[coverage(no(milord))] +LL + #[coverage(on)] + | error: malformed `coverage` attribute input --> $DIR/subword.rs:14:1 @@ -32,10 +36,12 @@ LL | #[coverage(yes = "milord")] | help: the following are the possible correct uses | -LL | #[coverage(off)] - | ~~~~~~~~~~~~~~~~ -LL | #[coverage(on)] - | ~~~~~~~~~~~~~~~ +LL - #[coverage(yes = "milord")] +LL + #[coverage(off)] + | +LL - #[coverage(yes = "milord")] +LL + #[coverage(on)] + | error: malformed `coverage` attribute input --> $DIR/subword.rs:17:1 @@ -45,10 +51,12 @@ LL | #[coverage(no = "milord")] | help: the following are the possible correct uses | -LL | #[coverage(off)] - | ~~~~~~~~~~~~~~~~ -LL | #[coverage(on)] - | ~~~~~~~~~~~~~~~ +LL - #[coverage(no = "milord")] +LL + #[coverage(off)] + | +LL - #[coverage(no = "milord")] +LL + #[coverage(on)] + | error: aborting due to 4 previous errors diff --git a/tests/ui/coverage-attr/word-only.stderr b/tests/ui/coverage-attr/word-only.stderr index bad50b0c961da..c034149d8ec92 100644 --- a/tests/ui/coverage-attr/word-only.stderr +++ b/tests/ui/coverage-attr/word-only.stderr @@ -6,9 +6,11 @@ LL | #[coverage] | help: the following are the possible correct uses | -LL | #[coverage(off)] +LL - #[coverage] +LL + #[coverage(off)] | -LL | #[coverage(on)] +LL - #[coverage] +LL + #[coverage(on)] | error: malformed `coverage` attribute input @@ -19,9 +21,11 @@ LL | #![coverage] | help: the following are the possible correct uses | -LL | #![coverage(off)] +LL - #![coverage] +LL + #![coverage(off)] | -LL | #![coverage(on)] +LL - #![coverage] +LL + #![coverage(on)] | error: malformed `coverage` attribute input @@ -32,9 +36,11 @@ LL | #[coverage] | help: the following are the possible correct uses | -LL | #[coverage(off)] +LL - #[coverage] +LL + #[coverage(off)] | -LL | #[coverage(on)] +LL - #[coverage] +LL + #[coverage(on)] | error: malformed `coverage` attribute input @@ -45,9 +51,11 @@ LL | #[coverage] | help: the following are the possible correct uses | -LL | #[coverage(off)] +LL - #[coverage] +LL + #[coverage(off)] | -LL | #[coverage(on)] +LL - #[coverage] +LL + #[coverage(on)] | error: malformed `coverage` attribute input @@ -58,9 +66,11 @@ LL | #[coverage] | help: the following are the possible correct uses | -LL | #[coverage(off)] +LL - #[coverage] +LL + #[coverage(off)] | -LL | #[coverage(on)] +LL - #[coverage] +LL + #[coverage(on)] | error: malformed `coverage` attribute input @@ -71,9 +81,11 @@ LL | #[coverage] | help: the following are the possible correct uses | -LL | #[coverage(off)] +LL - #[coverage] +LL + #[coverage(off)] | -LL | #[coverage(on)] +LL - #[coverage] +LL + #[coverage(on)] | error: malformed `coverage` attribute input @@ -84,9 +96,11 @@ LL | #[coverage] | help: the following are the possible correct uses | -LL | #[coverage(off)] +LL - #[coverage] +LL + #[coverage(off)] | -LL | #[coverage(on)] +LL - #[coverage] +LL + #[coverage(on)] | error: malformed `coverage` attribute input @@ -97,9 +111,11 @@ LL | #[coverage] | help: the following are the possible correct uses | -LL | #[coverage(off)] +LL - #[coverage] +LL + #[coverage(off)] | -LL | #[coverage(on)] +LL - #[coverage] +LL + #[coverage(on)] | error: malformed `coverage` attribute input @@ -110,9 +126,11 @@ LL | #[coverage] | help: the following are the possible correct uses | -LL | #[coverage(off)] +LL - #[coverage] +LL + #[coverage(off)] | -LL | #[coverage(on)] +LL - #[coverage] +LL + #[coverage(on)] | error: malformed `coverage` attribute input @@ -123,9 +141,11 @@ LL | #[coverage] | help: the following are the possible correct uses | -LL | #[coverage(off)] +LL - #[coverage] +LL + #[coverage(off)] | -LL | #[coverage(on)] +LL - #[coverage] +LL + #[coverage(on)] | error: malformed `coverage` attribute input @@ -136,9 +156,11 @@ LL | #[coverage] | help: the following are the possible correct uses | -LL | #[coverage(off)] +LL - #[coverage] +LL + #[coverage(off)] | -LL | #[coverage(on)] +LL - #[coverage] +LL + #[coverage(on)] | error: malformed `coverage` attribute input @@ -149,9 +171,11 @@ LL | #[coverage] | help: the following are the possible correct uses | -LL | #[coverage(off)] +LL - #[coverage] +LL + #[coverage(off)] | -LL | #[coverage(on)] +LL - #[coverage] +LL + #[coverage(on)] | error[E0788]: coverage attribute not allowed here diff --git a/tests/ui/crate-loading/crateresolve1.rs b/tests/ui/crate-loading/crateresolve1.rs index 9200b6a623140..91c34c82558ec 100644 --- a/tests/ui/crate-loading/crateresolve1.rs +++ b/tests/ui/crate-loading/crateresolve1.rs @@ -2,11 +2,11 @@ //@ aux-build:crateresolve1-2.rs //@ aux-build:crateresolve1-3.rs -//@ normalize-stderr: "\.nll/" -> "/" +//@ normalize-stderr: "crateresolve1\..+/auxiliary/" -> "crateresolve1/auxiliary/" //@ normalize-stderr: "\\\?\\" -> "" //@ normalize-stderr: "(lib)?crateresolve1-([123])\.[a-z]+" -> "libcrateresolve1-$2.somelib" -// NOTE: This test is duplicated at `tests/ui/error-codes/E0464.rs`. +// NOTE: This test is duplicated at `tests/ui/error-codes/E0464.rs` and `E0523.rs`. extern crate crateresolve1; //~^ ERROR multiple candidates for `rlib` dependency `crateresolve1` found diff --git a/tests/ui/crate-loading/crateresolve2.rs b/tests/ui/crate-loading/crateresolve2.rs index bec692eb8d2b9..6446740a32108 100644 --- a/tests/ui/crate-loading/crateresolve2.rs +++ b/tests/ui/crate-loading/crateresolve2.rs @@ -4,7 +4,7 @@ //@ aux-build:crateresolve2-2.rs //@ aux-build:crateresolve2-3.rs -//@ normalize-stderr: "\.nll/" -> "/" +//@ normalize-stderr: "crateresolve2\..+/auxiliary/" -> "crateresolve2/auxiliary/" //@ normalize-stderr: "\\\?\\" -> "" extern crate crateresolve2; diff --git a/tests/ui/debuginfo/dwarf-versions.one.stderr b/tests/ui/debuginfo/dwarf-versions.one.stderr new file mode 100644 index 0000000000000..bfc3a0152c649 --- /dev/null +++ b/tests/ui/debuginfo/dwarf-versions.one.stderr @@ -0,0 +1,6 @@ +error: requested DWARF version 1 is not supported + | + = help: supported DWARF versions are 2, 3, 4 and 5 + +error: aborting due to 1 previous error + diff --git a/tests/ui/debuginfo/dwarf-versions.rs b/tests/ui/debuginfo/dwarf-versions.rs new file mode 100644 index 0000000000000..806ade51a997f --- /dev/null +++ b/tests/ui/debuginfo/dwarf-versions.rs @@ -0,0 +1,38 @@ +// This test verifies the expected behavior of various options passed to +// `-Zdwarf-version`: 2 - 5 (valid) with all other options being invalid. + +//@ revisions: zero one two three four five six + +//@[zero] compile-flags: -Zdwarf-version=0 +//@[zero] error-pattern: requested DWARF version 0 is not supported + +//@[one] compile-flags: -Zdwarf-version=1 +//@[one] error-pattern: requested DWARF version 1 is not supported + +//@[two] compile-flags: -Zdwarf-version=2 +//@[two] check-pass + +//@[three] compile-flags: -Zdwarf-version=3 +//@[three] check-pass + +//@[four] compile-flags: -Zdwarf-version=4 +//@[four] check-pass + +//@[five] compile-flags: -Zdwarf-version=5 +//@[five] check-pass + +//@[six] compile-flags: -Zdwarf-version=6 +//@[six] error-pattern: requested DWARF version 6 is not supported + +//@ compile-flags: -g --target x86_64-unknown-linux-gnu --crate-type cdylib +//@ needs-llvm-components: x86 + +#![feature(no_core, lang_items)] + +#![no_core] +#![no_std] + +#[lang = "sized"] +pub trait Sized {} + +pub fn foo() {} diff --git a/tests/ui/debuginfo/dwarf-versions.six.stderr b/tests/ui/debuginfo/dwarf-versions.six.stderr new file mode 100644 index 0000000000000..32ab4e1471d9a --- /dev/null +++ b/tests/ui/debuginfo/dwarf-versions.six.stderr @@ -0,0 +1,6 @@ +error: requested DWARF version 6 is not supported + | + = help: supported DWARF versions are 2, 3, 4 and 5 + +error: aborting due to 1 previous error + diff --git a/tests/ui/debuginfo/dwarf-versions.zero.stderr b/tests/ui/debuginfo/dwarf-versions.zero.stderr new file mode 100644 index 0000000000000..d58d7d3ed1c91 --- /dev/null +++ b/tests/ui/debuginfo/dwarf-versions.zero.stderr @@ -0,0 +1,6 @@ +error: requested DWARF version 0 is not supported + | + = help: supported DWARF versions are 2, 3, 4 and 5 + +error: aborting due to 1 previous error + diff --git a/tests/ui/debuginfo/windows_gnu_split_debuginfo_off.rs b/tests/ui/debuginfo/windows_gnu_split_debuginfo_off.rs new file mode 100644 index 0000000000000..3a8d9c998cf74 --- /dev/null +++ b/tests/ui/debuginfo/windows_gnu_split_debuginfo_off.rs @@ -0,0 +1,29 @@ +//@ revisions: aarch64_gl i686_g i686_gl i686_uwp_g x86_64_g x86_64_gl x86_64_uwp_g +//@ compile-flags: --crate-type cdylib -Csplit-debuginfo=off +//@ check-pass + +//@[aarch64_gl] compile-flags: --target aarch64-pc-windows-gnullvm +//@[aarch64_gl] needs-llvm-components: aarch64 + +//@[i686_g] compile-flags: --target i686-pc-windows-gnu +//@[i686_g] needs-llvm-components: x86 + +//@[i686_gl] compile-flags: --target i686-pc-windows-gnullvm +//@[i686_gl] needs-llvm-components: x86 + +//@[i686_uwp_g] compile-flags: --target i686-uwp-windows-gnu +//@[i686_uwp_g] needs-llvm-components: x86 + +//@[x86_64_g] compile-flags: --target x86_64-pc-windows-gnu +//@[x86_64_g] needs-llvm-components: x86 + +//@[x86_64_gl] compile-flags: --target x86_64-pc-windows-gnullvm +//@[x86_64_gl] needs-llvm-components: x86 + +//@[x86_64_uwp_g] compile-flags: --target x86_64-uwp-windows-gnu +//@[x86_64_uwp_g] needs-llvm-components: x86 + +#![feature(no_core)] + +#![no_core] +#![no_std] diff --git a/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.aarch64_gl.stderr b/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.aarch64_gl.stderr new file mode 100644 index 0000000000000..f3465e64976ea --- /dev/null +++ b/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.aarch64_gl.stderr @@ -0,0 +1,4 @@ +error: `-Csplit-debuginfo=packed` is unstable on this platform + +error: aborting due to 1 previous error + diff --git a/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.i686_g.stderr b/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.i686_g.stderr new file mode 100644 index 0000000000000..f3465e64976ea --- /dev/null +++ b/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.i686_g.stderr @@ -0,0 +1,4 @@ +error: `-Csplit-debuginfo=packed` is unstable on this platform + +error: aborting due to 1 previous error + diff --git a/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.i686_gl.stderr b/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.i686_gl.stderr new file mode 100644 index 0000000000000..f3465e64976ea --- /dev/null +++ b/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.i686_gl.stderr @@ -0,0 +1,4 @@ +error: `-Csplit-debuginfo=packed` is unstable on this platform + +error: aborting due to 1 previous error + diff --git a/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.i686_uwp_g.stderr b/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.i686_uwp_g.stderr new file mode 100644 index 0000000000000..f3465e64976ea --- /dev/null +++ b/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.i686_uwp_g.stderr @@ -0,0 +1,4 @@ +error: `-Csplit-debuginfo=packed` is unstable on this platform + +error: aborting due to 1 previous error + diff --git a/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.rs b/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.rs new file mode 100644 index 0000000000000..896bbac7d8e1e --- /dev/null +++ b/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.rs @@ -0,0 +1,29 @@ +//@ revisions: aarch64_gl i686_g i686_gl i686_uwp_g x86_64_g x86_64_gl x86_64_uwp_g +//@ compile-flags: --crate-type cdylib -Csplit-debuginfo=packed +//@ error-pattern: error: `-Csplit-debuginfo=packed` is unstable on this platform + +//@[aarch64_gl] compile-flags: --target aarch64-pc-windows-gnullvm +//@[aarch64_gl] needs-llvm-components: aarch64 + +//@[i686_g] compile-flags: --target i686-pc-windows-gnu +//@[i686_g] needs-llvm-components: x86 + +//@[i686_gl] compile-flags: --target i686-pc-windows-gnullvm +//@[i686_gl] needs-llvm-components: x86 + +//@[i686_uwp_g] compile-flags: --target i686-uwp-windows-gnu +//@[i686_uwp_g] needs-llvm-components: x86 + +//@[x86_64_g] compile-flags: --target x86_64-pc-windows-gnu +//@[x86_64_g] needs-llvm-components: x86 + +//@[x86_64_gl] compile-flags: --target x86_64-pc-windows-gnullvm +//@[x86_64_gl] needs-llvm-components: x86 + +//@[x86_64_uwp_g] compile-flags: --target x86_64-uwp-windows-gnu +//@[x86_64_uwp_g] needs-llvm-components: x86 + +#![feature(no_core)] + +#![no_core] +#![no_std] diff --git a/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.x86_64_g.stderr b/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.x86_64_g.stderr new file mode 100644 index 0000000000000..f3465e64976ea --- /dev/null +++ b/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.x86_64_g.stderr @@ -0,0 +1,4 @@ +error: `-Csplit-debuginfo=packed` is unstable on this platform + +error: aborting due to 1 previous error + diff --git a/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.x86_64_gl.stderr b/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.x86_64_gl.stderr new file mode 100644 index 0000000000000..f3465e64976ea --- /dev/null +++ b/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.x86_64_gl.stderr @@ -0,0 +1,4 @@ +error: `-Csplit-debuginfo=packed` is unstable on this platform + +error: aborting due to 1 previous error + diff --git a/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.x86_64_uwp_g.stderr b/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.x86_64_uwp_g.stderr new file mode 100644 index 0000000000000..f3465e64976ea --- /dev/null +++ b/tests/ui/debuginfo/windows_gnu_split_debuginfo_packed.x86_64_uwp_g.stderr @@ -0,0 +1,4 @@ +error: `-Csplit-debuginfo=packed` is unstable on this platform + +error: aborting due to 1 previous error + diff --git a/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.aarch64_gl.stderr b/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.aarch64_gl.stderr new file mode 100644 index 0000000000000..0964e21b13bc1 --- /dev/null +++ b/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.aarch64_gl.stderr @@ -0,0 +1,4 @@ +error: `-Csplit-debuginfo=unpacked` is unstable on this platform + +error: aborting due to 1 previous error + diff --git a/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.i686_g.stderr b/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.i686_g.stderr new file mode 100644 index 0000000000000..0964e21b13bc1 --- /dev/null +++ b/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.i686_g.stderr @@ -0,0 +1,4 @@ +error: `-Csplit-debuginfo=unpacked` is unstable on this platform + +error: aborting due to 1 previous error + diff --git a/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.i686_gl.stderr b/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.i686_gl.stderr new file mode 100644 index 0000000000000..0964e21b13bc1 --- /dev/null +++ b/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.i686_gl.stderr @@ -0,0 +1,4 @@ +error: `-Csplit-debuginfo=unpacked` is unstable on this platform + +error: aborting due to 1 previous error + diff --git a/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.i686_uwp_g.stderr b/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.i686_uwp_g.stderr new file mode 100644 index 0000000000000..0964e21b13bc1 --- /dev/null +++ b/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.i686_uwp_g.stderr @@ -0,0 +1,4 @@ +error: `-Csplit-debuginfo=unpacked` is unstable on this platform + +error: aborting due to 1 previous error + diff --git a/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.rs b/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.rs new file mode 100644 index 0000000000000..54a88c9121734 --- /dev/null +++ b/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.rs @@ -0,0 +1,29 @@ +//@ revisions: aarch64_gl i686_g i686_gl i686_uwp_g x86_64_g x86_64_gl x86_64_uwp_g +//@ compile-flags: --crate-type cdylib -Csplit-debuginfo=unpacked +//@ error-pattern: error: `-Csplit-debuginfo=unpacked` is unstable on this platform + +//@[aarch64_gl] compile-flags: --target aarch64-pc-windows-gnullvm +//@[aarch64_gl] needs-llvm-components: aarch64 + +//@[i686_g] compile-flags: --target i686-pc-windows-gnu +//@[i686_g] needs-llvm-components: x86 + +//@[i686_gl] compile-flags: --target i686-pc-windows-gnullvm +//@[i686_gl] needs-llvm-components: x86 + +//@[i686_uwp_g] compile-flags: --target i686-uwp-windows-gnu +//@[i686_uwp_g] needs-llvm-components: x86 + +//@[x86_64_g] compile-flags: --target x86_64-pc-windows-gnu +//@[x86_64_g] needs-llvm-components: x86 + +//@[x86_64_gl] compile-flags: --target x86_64-pc-windows-gnullvm +//@[x86_64_gl] needs-llvm-components: x86 + +//@[x86_64_uwp_g] compile-flags: --target x86_64-uwp-windows-gnu +//@[x86_64_uwp_g] needs-llvm-components: x86 + +#![feature(no_core)] + +#![no_core] +#![no_std] diff --git a/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.x86_64_g.stderr b/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.x86_64_g.stderr new file mode 100644 index 0000000000000..0964e21b13bc1 --- /dev/null +++ b/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.x86_64_g.stderr @@ -0,0 +1,4 @@ +error: `-Csplit-debuginfo=unpacked` is unstable on this platform + +error: aborting due to 1 previous error + diff --git a/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.x86_64_gl.stderr b/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.x86_64_gl.stderr new file mode 100644 index 0000000000000..0964e21b13bc1 --- /dev/null +++ b/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.x86_64_gl.stderr @@ -0,0 +1,4 @@ +error: `-Csplit-debuginfo=unpacked` is unstable on this platform + +error: aborting due to 1 previous error + diff --git a/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.x86_64_uwp_g.stderr b/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.x86_64_uwp_g.stderr new file mode 100644 index 0000000000000..0964e21b13bc1 --- /dev/null +++ b/tests/ui/debuginfo/windows_gnu_split_debuginfo_unpacked.x86_64_uwp_g.stderr @@ -0,0 +1,4 @@ +error: `-Csplit-debuginfo=unpacked` is unstable on this platform + +error: aborting due to 1 previous error + diff --git a/tests/ui/delegation/bad-resolve.rs b/tests/ui/delegation/bad-resolve.rs index f15e6aa81afb0..861f2b15da205 100644 --- a/tests/ui/delegation/bad-resolve.rs +++ b/tests/ui/delegation/bad-resolve.rs @@ -40,7 +40,7 @@ impl Trait for S { } mod prefix {} -reuse unresolved_prefix::{a, b, c}; //~ ERROR use of undeclared crate or module `unresolved_prefix` +reuse unresolved_prefix::{a, b, c}; //~ ERROR use of unresolved module or unlinked crate reuse prefix::{self, super, crate}; //~ ERROR `crate` in paths can only be used in start position fn main() {} diff --git a/tests/ui/delegation/bad-resolve.stderr b/tests/ui/delegation/bad-resolve.stderr index 32d2f3b26cb03..966387e1d6164 100644 --- a/tests/ui/delegation/bad-resolve.stderr +++ b/tests/ui/delegation/bad-resolve.stderr @@ -81,11 +81,13 @@ LL | type Type; LL | impl Trait for S { | ^^^^^^^^^^^^^^^^ missing `Type` in implementation -error[E0433]: failed to resolve: use of undeclared crate or module `unresolved_prefix` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `unresolved_prefix` --> $DIR/bad-resolve.rs:43:7 | LL | reuse unresolved_prefix::{a, b, c}; - | ^^^^^^^^^^^^^^^^^ use of undeclared crate or module `unresolved_prefix` + | ^^^^^^^^^^^^^^^^^ use of unresolved module or unlinked crate `unresolved_prefix` + | + = help: you might be missing a crate named `unresolved_prefix` error[E0433]: failed to resolve: `crate` in paths can only be used in start position --> $DIR/bad-resolve.rs:44:29 diff --git a/tests/ui/delegation/correct_body_owner_parent_found_in_diagnostics.stderr b/tests/ui/delegation/correct_body_owner_parent_found_in_diagnostics.stderr index 2ce3b388073c4..61e2a8f64dd52 100644 --- a/tests/ui/delegation/correct_body_owner_parent_found_in_diagnostics.stderr +++ b/tests/ui/delegation/correct_body_owner_parent_found_in_diagnostics.stderr @@ -52,6 +52,14 @@ LL | pub struct InvariantRef<'a, T: ?Sized>(&'a T, PhantomData<&'a mut &'a T>); LL | pub const NEW: Self = InvariantRef::new(&()); | ^^^ function or associated item not found in `InvariantRef<'_, _>` +error[E0277]: the trait bound `u8: Trait` is not satisfied + --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:22:12 + | +LL | reuse ::{foo, bar, meh} { &const { InvariantRef::<'a>::NEW } } + | ^^ the trait `Trait` is not implemented for `u8` + | + = help: the trait `Trait` is implemented for `Z` + error[E0308]: mismatched types --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:22:53 | @@ -68,6 +76,7 @@ LL | reuse ::{foo, bar, meh} { &const { InvariantRef::<'a>::NEW | ^^ the trait `Trait` is not implemented for `u8` | = help: the trait `Trait` is implemented for `Z` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0308]: mismatched types --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:22:53 @@ -98,15 +107,6 @@ LL | reuse ::{foo, bar, meh} { &const { InvariantRef::<'a>::NEW found struct `InvariantRef<'_, ()>` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0277]: the trait bound `u8: Trait` is not satisfied - --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:22:12 - | -LL | reuse ::{foo, bar, meh} { &const { InvariantRef::<'a>::NEW } } - | ^^ the trait `Trait` is not implemented for `u8` - | - = help: the trait `Trait` is implemented for `Z` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error: aborting due to 10 previous errors Some errors have detailed explanations: E0261, E0277, E0308, E0599. diff --git a/tests/ui/delegation/glob-bad-path.rs b/tests/ui/delegation/glob-bad-path.rs index 7bc4f0153a308..4ac9d68e8dd1c 100644 --- a/tests/ui/delegation/glob-bad-path.rs +++ b/tests/ui/delegation/glob-bad-path.rs @@ -5,7 +5,7 @@ trait Trait {} struct S; impl Trait for u8 { - reuse unresolved::*; //~ ERROR failed to resolve: use of undeclared crate or module `unresolved` + reuse unresolved::*; //~ ERROR failed to resolve: use of unresolved module or unlinked crate `unresolved` reuse S::*; //~ ERROR expected trait, found struct `S` } diff --git a/tests/ui/delegation/glob-bad-path.stderr b/tests/ui/delegation/glob-bad-path.stderr index 0c06364b3f0ab..15d9ca4120332 100644 --- a/tests/ui/delegation/glob-bad-path.stderr +++ b/tests/ui/delegation/glob-bad-path.stderr @@ -4,11 +4,11 @@ error: expected trait, found struct `S` LL | reuse S::*; | ^ not a trait -error[E0433]: failed to resolve: use of undeclared crate or module `unresolved` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `unresolved` --> $DIR/glob-bad-path.rs:8:11 | LL | reuse unresolved::*; - | ^^^^^^^^^^ use of undeclared crate or module `unresolved` + | ^^^^^^^^^^ use of unresolved module or unlinked crate `unresolved` error: aborting due to 2 previous errors diff --git a/tests/ui/delegation/glob-non-impl.rs b/tests/ui/delegation/glob-non-impl.rs index d523731eeb35f..e3a4061fb15a6 100644 --- a/tests/ui/delegation/glob-non-impl.rs +++ b/tests/ui/delegation/glob-non-impl.rs @@ -11,7 +11,7 @@ trait OtherTrait { reuse Trait::*; //~ ERROR glob delegation is only supported in impls } -extern { +extern "C" { reuse Trait::*; //~ ERROR delegation is not supported in `extern` blocks } diff --git a/tests/ui/deprecation/atomic_initializers.stderr b/tests/ui/deprecation/atomic_initializers.stderr index 30fcc9de6181e..3a2e661381886 100644 --- a/tests/ui/deprecation/atomic_initializers.stderr +++ b/tests/ui/deprecation/atomic_initializers.stderr @@ -7,8 +7,9 @@ LL | static FOO: AtomicIsize = ATOMIC_ISIZE_INIT; = note: `#[warn(deprecated)]` on by default help: replace the use of the deprecated constant | -LL | static FOO: AtomicIsize = AtomicIsize::new(0); - | ~~~~~~~~~~~~~~~~~~~ +LL - static FOO: AtomicIsize = ATOMIC_ISIZE_INIT; +LL + static FOO: AtomicIsize = AtomicIsize::new(0); + | warning: 1 warning emitted diff --git a/tests/ui/deprecation/deprecated_ar.rs b/tests/ui/deprecation/deprecated_ar.rs new file mode 100644 index 0000000000000..404d062e6a449 --- /dev/null +++ b/tests/ui/deprecation/deprecated_ar.rs @@ -0,0 +1,4 @@ +//@ check-pass +//@ compile-flags: -Car=foo + +fn main() {} diff --git a/tests/ui/deprecation/deprecated_ar.stderr b/tests/ui/deprecation/deprecated_ar.stderr new file mode 100644 index 0000000000000..de776c674996a --- /dev/null +++ b/tests/ui/deprecation/deprecated_ar.stderr @@ -0,0 +1,2 @@ +warning: `-C ar`: this option is deprecated and does nothing + diff --git a/tests/ui/deprecation/deprecated_inline_threshold.bad_val.stderr b/tests/ui/deprecation/deprecated_inline_threshold.bad_val.stderr new file mode 100644 index 0000000000000..2e7a99010ae9b --- /dev/null +++ b/tests/ui/deprecation/deprecated_inline_threshold.bad_val.stderr @@ -0,0 +1,4 @@ +warning: `-C inline-threshold`: this option is deprecated and does nothing (consider using `-Cllvm-args=--inline-threshold=...`) + +error: incorrect value `asd` for codegen option `inline-threshold` - a number was expected + diff --git a/tests/ui/deprecation/deprecated_inline_threshold.good_val.stderr b/tests/ui/deprecation/deprecated_inline_threshold.good_val.stderr new file mode 100644 index 0000000000000..2d6f3652d252e --- /dev/null +++ b/tests/ui/deprecation/deprecated_inline_threshold.good_val.stderr @@ -0,0 +1,2 @@ +warning: `-C inline-threshold`: this option is deprecated and does nothing (consider using `-Cllvm-args=--inline-threshold=...`) + diff --git a/tests/ui/deprecation/deprecated_inline_threshold.no_val.stderr b/tests/ui/deprecation/deprecated_inline_threshold.no_val.stderr new file mode 100644 index 0000000000000..25104e637e001 --- /dev/null +++ b/tests/ui/deprecation/deprecated_inline_threshold.no_val.stderr @@ -0,0 +1,4 @@ +warning: `-C inline-threshold`: this option is deprecated and does nothing (consider using `-Cllvm-args=--inline-threshold=...`) + +error: codegen option `inline-threshold` requires a number (C inline-threshold=) + diff --git a/tests/ui/deprecation/deprecated_inline_threshold.rs b/tests/ui/deprecation/deprecated_inline_threshold.rs index 56e033b8cf547..b54fa36397af0 100644 --- a/tests/ui/deprecation/deprecated_inline_threshold.rs +++ b/tests/ui/deprecation/deprecated_inline_threshold.rs @@ -1,4 +1,8 @@ -//@ check-pass -//@ compile-flags: -Cinline-threshold=666 +//@ revisions: good_val bad_val no_val +// +//@[good_val] compile-flags: -Cinline-threshold=666 +//@[good_val] check-pass +//@[bad_val] compile-flags: -Cinline-threshold=asd +//@[no_val] compile-flags: -Cinline-threshold fn main() {} diff --git a/tests/ui/deprecation/deprecated_inline_threshold.stderr b/tests/ui/deprecation/deprecated_inline_threshold.stderr deleted file mode 100644 index c4f8ff092b29a..0000000000000 --- a/tests/ui/deprecation/deprecated_inline_threshold.stderr +++ /dev/null @@ -1,2 +0,0 @@ -warning: the `-Cinline-threshold` flag is deprecated and does nothing (consider using `-Cllvm-args=--inline-threshold=...`) - diff --git a/tests/ui/deprecation/deprecated_no_stack_check_opt.rs b/tests/ui/deprecation/deprecated_no_stack_check_opt.rs new file mode 100644 index 0000000000000..62584ec23e336 --- /dev/null +++ b/tests/ui/deprecation/deprecated_no_stack_check_opt.rs @@ -0,0 +1,4 @@ +//@ check-pass +//@ compile-flags: -Cno-stack-check + +fn main() {} diff --git a/tests/ui/deprecation/deprecated_no_stack_check_opt.stderr b/tests/ui/deprecation/deprecated_no_stack_check_opt.stderr new file mode 100644 index 0000000000000..17efba787f070 --- /dev/null +++ b/tests/ui/deprecation/deprecated_no_stack_check_opt.stderr @@ -0,0 +1,2 @@ +warning: `-C no-stack-check`: this option is deprecated and does nothing + diff --git a/tests/ui/deprecation/deprecation-lint.stderr b/tests/ui/deprecation/deprecation-lint.stderr index 2098073409da5..95ae1b04d8611 100644 --- a/tests/ui/deprecation/deprecation-lint.stderr +++ b/tests/ui/deprecation/deprecation-lint.stderr @@ -739,8 +739,11 @@ LL | _) error[E0451]: field `i` of struct `this_crate::nested::DeprecatedStruct` is private --> $DIR/deprecation-lint.rs:280:13 | +LL | let _ = nested::DeprecatedStruct { + | ------------------------ in this type +LL | LL | i: 0 - | ^^^^ private field + | ^ private field error: aborting due to 123 previous errors diff --git a/tests/ui/deprecation/invalid-literal.stderr b/tests/ui/deprecation/invalid-literal.stderr index ca827beda0596..cbe1fcca0238b 100644 --- a/tests/ui/deprecation/invalid-literal.stderr +++ b/tests/ui/deprecation/invalid-literal.stderr @@ -6,12 +6,15 @@ LL | #[deprecated = b"test"] | help: the following are the possible correct uses | -LL | #[deprecated = "reason"] - | ~~~~~~~~~~~~~~~~~~~~~~~~ -LL | #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")] - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | #[deprecated] - | ~~~~~~~~~~~~~ +LL - #[deprecated = b"test"] +LL + #[deprecated = "reason"] + | +LL - #[deprecated = b"test"] +LL + #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")] + | +LL - #[deprecated = b"test"] +LL + #[deprecated] + | error: aborting due to 1 previous error diff --git a/tests/ui/deprecation/issue-84637-deprecated-associated-function.stderr b/tests/ui/deprecation/issue-84637-deprecated-associated-function.stderr index d1f5ea3602a8f..a81143640a070 100644 --- a/tests/ui/deprecation/issue-84637-deprecated-associated-function.stderr +++ b/tests/ui/deprecation/issue-84637-deprecated-associated-function.stderr @@ -11,8 +11,9 @@ LL | #![deny(deprecated)] | ^^^^^^^^^^ help: replace the use of the deprecated method | -LL | let _foo = str::trim_start(" aoeu"); - | ~~~~~~~~~~ +LL - let _foo = str::trim_left(" aoeu"); +LL + let _foo = str::trim_start(" aoeu"); + | error: use of deprecated method `core::str::::trim_left`: superseded by `trim_start` --> $DIR/issue-84637-deprecated-associated-function.rs:8:26 @@ -22,8 +23,9 @@ LL | let _bar = " aoeu".trim_left(); | help: replace the use of the deprecated method | -LL | let _bar = " aoeu".trim_start(); - | ~~~~~~~~~~ +LL - let _bar = " aoeu".trim_left(); +LL + let _bar = " aoeu".trim_start(); + | error: use of deprecated method `std::slice::::connect`: renamed to join --> $DIR/issue-84637-deprecated-associated-function.rs:10:27 @@ -33,8 +35,9 @@ LL | let _baz = ["a", "b"].connect(" "); | help: replace the use of the deprecated method | -LL | let _baz = ["a", "b"].join(" "); - | ~~~~ +LL - let _baz = ["a", "b"].connect(" "); +LL + let _baz = ["a", "b"].join(" "); + | error: aborting due to 3 previous errors diff --git a/tests/ui/deprecation/suggestion.stderr b/tests/ui/deprecation/suggestion.stderr index 5584b6d2f8ffb..58e4219d37b98 100644 --- a/tests/ui/deprecation/suggestion.stderr +++ b/tests/ui/deprecation/suggestion.stderr @@ -11,8 +11,9 @@ LL | #![deny(deprecated)] | ^^^^^^^^^^ help: replace the use of the deprecated function | -LL | bar::replacement(); - | ~~~~~~~~~~~ +LL - bar::deprecated(); +LL + bar::replacement(); + | error: use of deprecated method `Foo::deprecated`: replaced by `replacement` --> $DIR/suggestion.rs:40:9 @@ -22,8 +23,9 @@ LL | foo.deprecated(); | help: replace the use of the deprecated method | -LL | foo.replacement(); - | ~~~~~~~~~~~ +LL - foo.deprecated(); +LL + foo.replacement(); + | error: aborting due to 2 previous errors diff --git a/tests/ui/deref-patterns/issue-71676-1.stderr b/tests/ui/deref-patterns/issue-71676-1.stderr index 164641ff77512..348a2c12ec491 100644 --- a/tests/ui/deref-patterns/issue-71676-1.stderr +++ b/tests/ui/deref-patterns/issue-71676-1.stderr @@ -40,8 +40,9 @@ LL | let _: *const u8 = &mut a; found mutable reference `&mut Emm` help: consider dereferencing | -LL | let _: *const u8 = &***a; - | ~~~ +LL - let _: *const u8 = &mut a; +LL + let _: *const u8 = &***a; + | error[E0308]: mismatched types --> $DIR/issue-71676-1.rs:52:22 diff --git a/tests/ui/derived-errors/issue-30580.stderr b/tests/ui/derived-errors/issue-30580.stderr index 05b5559172994..e8659ea931ddc 100644 --- a/tests/ui/derived-errors/issue-30580.stderr +++ b/tests/ui/derived-errors/issue-30580.stderr @@ -6,8 +6,9 @@ LL | b.c; | help: a field with a similar name exists | -LL | b.a; - | ~ +LL - b.c; +LL + b.a; + | error: aborting due to 1 previous error diff --git a/tests/ui/derives/rustc-decodable-issue-123156.rs b/tests/ui/derives/rustc-decodable-issue-123156.rs deleted file mode 100644 index 1983837ed8d46..0000000000000 --- a/tests/ui/derives/rustc-decodable-issue-123156.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ check-pass -//@ edition:2021 -//@ aux-build:rustc-serialize.rs - -#![crate_type = "lib"] -#![allow(deprecated, soft_unstable)] - -extern crate rustc_serialize; - -#[derive(RustcDecodable)] -pub enum Foo {} diff --git a/tests/ui/derives/rustc-decodable-issue-123156.stderr b/tests/ui/derives/rustc-decodable-issue-123156.stderr deleted file mode 100644 index 93a993b90d814..0000000000000 --- a/tests/ui/derives/rustc-decodable-issue-123156.stderr +++ /dev/null @@ -1,10 +0,0 @@ -Future incompatibility report: Future breakage diagnostic: -warning: use of unstable library feature `rustc_encodable_decodable`: derive macro for `rustc-serialize`; should not be used in new code - --> $DIR/rustc-decodable-issue-123156.rs:10:10 - | -LL | #[derive(RustcDecodable)] - | ^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #64266 - diff --git a/tests/ui/deriving/auxiliary/malicious-macro.rs b/tests/ui/deriving/auxiliary/malicious-macro.rs new file mode 100644 index 0000000000000..6665b40a14f2b --- /dev/null +++ b/tests/ui/deriving/auxiliary/malicious-macro.rs @@ -0,0 +1,31 @@ +#![feature(let_chains)] + +extern crate proc_macro; + +use proc_macro::{Delimiter, TokenStream, TokenTree}; + +#[proc_macro_attribute] +pub fn norepr(_: TokenStream, input: TokenStream) -> TokenStream { + let mut tokens = vec![]; + let mut tts = input.into_iter().fuse().peekable(); + loop { + let Some(token) = tts.next() else { break }; + if let TokenTree::Punct(punct) = &token + && punct.as_char() == '#' + { + if let Some(TokenTree::Group(group)) = tts.peek() + && let Delimiter::Bracket = group.delimiter() + && let Some(TokenTree::Ident(ident)) = group.stream().into_iter().next() + && ident.to_string() == "repr" + { + let _ = tts.next(); + // skip '#' and '[repr(..)] + } else { + tokens.push(token); + } + } else { + tokens.push(token); + } + } + tokens.into_iter().collect() +} diff --git a/tests/ui/deriving/built-in-proc-macro-scope.stdout b/tests/ui/deriving/built-in-proc-macro-scope.stdout index db97c7145ea09..fa4e50968f4de 100644 --- a/tests/ui/deriving/built-in-proc-macro-scope.stdout +++ b/tests/ui/deriving/built-in-proc-macro-scope.stdout @@ -20,6 +20,8 @@ pub struct Ptr<'a, #[pointee] T: ?Sized> { data: &'a mut T, } #[automatically_derived] +impl<'a, T: ?Sized> ::core::marker::CoercePointeeValidated for Ptr<'a, T> { } +#[automatically_derived] impl<'a, T: ?Sized + ::core::marker::Unsize<__S>, __S: ?Sized> ::core::ops::DispatchFromDyn> for Ptr<'a, T> { } diff --git a/tests/ui/deriving/deriving-coerce-pointee-expanded.stdout b/tests/ui/deriving/deriving-coerce-pointee-expanded.stdout index d6eaca5cba188..a774efbbe354b 100644 --- a/tests/ui/deriving/deriving-coerce-pointee-expanded.stdout +++ b/tests/ui/deriving/deriving-coerce-pointee-expanded.stdout @@ -16,6 +16,10 @@ struct MyPointer<'a, #[pointee] T: ?Sized> { ptr: &'a T, } #[automatically_derived] +impl<'a, T: ?Sized> ::core::marker::CoercePointeeValidated for + MyPointer<'a, T> { +} +#[automatically_derived] impl<'a, T: ?Sized + ::core::marker::Unsize<__S>, __S: ?Sized> ::core::ops::DispatchFromDyn> for MyPointer<'a, T> { } @@ -31,6 +35,11 @@ pub struct MyPointer2<'a, Y, Z: MyTrait, #[pointee] T: ?Sized + MyTrait, x: core::marker::PhantomData, } #[automatically_derived] +impl<'a, Y, Z: MyTrait, T: ?Sized + MyTrait, X: MyTrait> + ::core::marker::CoercePointeeValidated for MyPointer2<'a, Y, Z, T, X> + where Y: MyTrait { +} +#[automatically_derived] impl<'a, Y, Z: MyTrait + MyTrait<__S>, T: ?Sized + MyTrait + ::core::marker::Unsize<__S>, __S: ?Sized + MyTrait<__S>, X: MyTrait + MyTrait<__S>> ::core::ops::DispatchFromDyn> @@ -48,6 +57,10 @@ struct MyPointerWithoutPointee<'a, T: ?Sized> { ptr: &'a T, } #[automatically_derived] +impl<'a, T: ?Sized> ::core::marker::CoercePointeeValidated for + MyPointerWithoutPointee<'a, T> { +} +#[automatically_derived] impl<'a, T: ?Sized + ::core::marker::Unsize<__S>, __S: ?Sized> ::core::ops::DispatchFromDyn> for MyPointerWithoutPointee<'a, T> { diff --git a/tests/ui/deriving/deriving-coerce-pointee-neg.rs b/tests/ui/deriving/deriving-coerce-pointee-neg.rs index da25c854c546a..6577500d8eb0f 100644 --- a/tests/ui/deriving/deriving-coerce-pointee-neg.rs +++ b/tests/ui/deriving/deriving-coerce-pointee-neg.rs @@ -1,6 +1,9 @@ +//@ proc-macro: malicious-macro.rs #![feature(derive_coerce_pointee, arbitrary_self_types)] extern crate core; +extern crate malicious_macro; + use std::marker::CoercePointee; #[derive(CoercePointee)] @@ -41,8 +44,8 @@ struct TooManyPointees<'a, #[pointee] A: ?Sized, #[pointee] B: ?Sized>((&'a A, & //~^ ERROR: only one type parameter can be marked as `#[pointee]` when deriving `CoercePointee` traits #[derive(CoercePointee)] -//~^ ERROR: `CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]` struct NotTransparent<'a, #[pointee] T: ?Sized> { + //~^ ERROR: `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout ptr: &'a T, } @@ -131,4 +134,12 @@ struct GlobalCoreSized<'a, #[pointee] T: ?::core::marker::Sized> { ptr: &'a T, } +#[derive(CoercePointee)] +#[malicious_macro::norepr] +#[repr(transparent)] +struct TryToWipeRepr<'a, #[pointee] T: ?Sized> { + //~^ ERROR: `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout [E0802] + ptr: &'a T, +} + fn main() {} diff --git a/tests/ui/deriving/deriving-coerce-pointee-neg.stderr b/tests/ui/deriving/deriving-coerce-pointee-neg.stderr index c1e8be49d37d3..999214bfa9fe3 100644 --- a/tests/ui/deriving/deriving-coerce-pointee-neg.stderr +++ b/tests/ui/deriving/deriving-coerce-pointee-neg.stderr @@ -1,89 +1,81 @@ -error: `CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]` - --> $DIR/deriving-coerce-pointee-neg.rs:6:10 +error[E0802]: `CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]` + --> $DIR/deriving-coerce-pointee-neg.rs:9:10 | LL | #[derive(CoercePointee)] | ^^^^^^^^^^^^^ | = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) -error: `CoercePointee` can only be derived on `struct`s with at least one field - --> $DIR/deriving-coerce-pointee-neg.rs:12:10 +error[E0802]: `CoercePointee` can only be derived on `struct`s with at least one field + --> $DIR/deriving-coerce-pointee-neg.rs:15:10 | LL | #[derive(CoercePointee)] | ^^^^^^^^^^^^^ | = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) -error: `CoercePointee` can only be derived on `struct`s with at least one field - --> $DIR/deriving-coerce-pointee-neg.rs:19:10 +error[E0802]: `CoercePointee` can only be derived on `struct`s with at least one field + --> $DIR/deriving-coerce-pointee-neg.rs:22:10 | LL | #[derive(CoercePointee)] | ^^^^^^^^^^^^^ | = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) -error: `CoercePointee` can only be derived on `struct`s that are generic over at least one type - --> $DIR/deriving-coerce-pointee-neg.rs:26:10 +error[E0802]: `CoercePointee` can only be derived on `struct`s that are generic over at least one type + --> $DIR/deriving-coerce-pointee-neg.rs:29:10 | LL | #[derive(CoercePointee)] | ^^^^^^^^^^^^^ | = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) -error: exactly one generic type parameter must be marked as `#[pointee]` to derive `CoercePointee` traits - --> $DIR/deriving-coerce-pointee-neg.rs:31:10 +error[E0802]: exactly one generic type parameter must be marked as `#[pointee]` to derive `CoercePointee` traits + --> $DIR/deriving-coerce-pointee-neg.rs:34:10 | LL | #[derive(CoercePointee)] | ^^^^^^^^^^^^^ | = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) -error: only one type parameter can be marked as `#[pointee]` when deriving `CoercePointee` traits - --> $DIR/deriving-coerce-pointee-neg.rs:40:39 +error[E0802]: only one type parameter can be marked as `#[pointee]` when deriving `CoercePointee` traits + --> $DIR/deriving-coerce-pointee-neg.rs:43:39 | LL | struct TooManyPointees<'a, #[pointee] A: ?Sized, #[pointee] B: ?Sized>((&'a A, &'a B)); | ^ - here another type parameter is marked as `#[pointee]` -error: `CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]` - --> $DIR/deriving-coerce-pointee-neg.rs:43:10 - | -LL | #[derive(CoercePointee)] - | ^^^^^^^^^^^^^ - | - = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: `derive(CoercePointee)` requires `T` to be marked `?Sized` - --> $DIR/deriving-coerce-pointee-neg.rs:51:36 +error[E0802]: `derive(CoercePointee)` requires `T` to be marked `?Sized` + --> $DIR/deriving-coerce-pointee-neg.rs:54:36 | LL | struct NoMaybeSized<'a, #[pointee] T> { | ^ error: the `#[pointee]` attribute may only be used on generic parameters - --> $DIR/deriving-coerce-pointee-neg.rs:59:5 + --> $DIR/deriving-coerce-pointee-neg.rs:62:5 | LL | #[pointee] | ^^^^^^^^^^ error: the `#[pointee]` attribute may only be used on generic parameters - --> $DIR/deriving-coerce-pointee-neg.rs:69:33 + --> $DIR/deriving-coerce-pointee-neg.rs:72:33 | LL | struct UhOh<#[pointee] T>(T); | ^^^^^^^^^^ error: the `#[pointee]` attribute may only be used on generic parameters - --> $DIR/deriving-coerce-pointee-neg.rs:83:21 + --> $DIR/deriving-coerce-pointee-neg.rs:86:21 | LL | struct UhOh<#[pointee] T>(T); | ^^^^^^^^^^ error: the `#[pointee]` attribute may only be used on generic parameters - --> $DIR/deriving-coerce-pointee-neg.rs:98:25 + --> $DIR/deriving-coerce-pointee-neg.rs:101:25 | LL | struct UhOh<#[pointee] T>(T); | ^^^^^^^^^^ error[E0392]: lifetime parameter `'a` is never used - --> $DIR/deriving-coerce-pointee-neg.rs:15:16 + --> $DIR/deriving-coerce-pointee-neg.rs:18:16 | LL | struct NoField<'a, #[pointee] T: ?Sized> {} | ^^ unused lifetime parameter @@ -91,7 +83,7 @@ LL | struct NoField<'a, #[pointee] T: ?Sized> {} = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData` error[E0392]: type parameter `T` is never used - --> $DIR/deriving-coerce-pointee-neg.rs:15:31 + --> $DIR/deriving-coerce-pointee-neg.rs:18:31 | LL | struct NoField<'a, #[pointee] T: ?Sized> {} | ^ unused type parameter @@ -99,7 +91,7 @@ LL | struct NoField<'a, #[pointee] T: ?Sized> {} = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` error[E0392]: lifetime parameter `'a` is never used - --> $DIR/deriving-coerce-pointee-neg.rs:22:20 + --> $DIR/deriving-coerce-pointee-neg.rs:25:20 | LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>(); | ^^ unused lifetime parameter @@ -107,13 +99,26 @@ LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>(); = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData` error[E0392]: type parameter `T` is never used - --> $DIR/deriving-coerce-pointee-neg.rs:22:35 + --> $DIR/deriving-coerce-pointee-neg.rs:25:35 | LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>(); | ^ unused type parameter | = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` -error: aborting due to 16 previous errors +error[E0802]: `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout + --> $DIR/deriving-coerce-pointee-neg.rs:47:1 + | +LL | struct NotTransparent<'a, #[pointee] T: ?Sized> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0802]: `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout + --> $DIR/deriving-coerce-pointee-neg.rs:140:1 + | +LL | struct TryToWipeRepr<'a, #[pointee] T: ?Sized> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 17 previous errors -For more information about this error, try `rustc --explain E0392`. +Some errors have detailed explanations: E0392, E0802. +For more information about an error, try `rustc --explain E0392`. diff --git a/tests/ui/deriving/do-not-suggest-calling-fn-in-derive-macro.rs b/tests/ui/deriving/do-not-suggest-calling-fn-in-derive-macro.rs new file mode 100644 index 0000000000000..76b93c0c947cb --- /dev/null +++ b/tests/ui/deriving/do-not-suggest-calling-fn-in-derive-macro.rs @@ -0,0 +1,8 @@ +use std::rc::Rc; + +#[derive(PartialEq)] //~ NOTE in this expansion +pub struct Function { + callback: Rc, //~ ERROR binary operation `==` cannot be applied to type `Rc` +} + +fn main() {} diff --git a/tests/ui/deriving/do-not-suggest-calling-fn-in-derive-macro.stderr b/tests/ui/deriving/do-not-suggest-calling-fn-in-derive-macro.stderr new file mode 100644 index 0000000000000..40464a49c3438 --- /dev/null +++ b/tests/ui/deriving/do-not-suggest-calling-fn-in-derive-macro.stderr @@ -0,0 +1,14 @@ +error[E0369]: binary operation `==` cannot be applied to type `Rc` + --> $DIR/do-not-suggest-calling-fn-in-derive-macro.rs:5:5 + | +LL | #[derive(PartialEq)] + | --------- in this derive macro expansion +LL | pub struct Function { +LL | callback: Rc, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0369`. diff --git a/tests/ui/destructuring-assignment/struct_destructure_fail.stderr b/tests/ui/destructuring-assignment/struct_destructure_fail.stderr index 58f8e97dea04a..7efc0b20e54be 100644 --- a/tests/ui/destructuring-assignment/struct_destructure_fail.stderr +++ b/tests/ui/destructuring-assignment/struct_destructure_fail.stderr @@ -26,16 +26,19 @@ LL | Struct { a, _ } = Struct { a: 1, b: 2 }; | help: include the missing field in the pattern | -LL | Struct { a, b } = Struct { a: 1, b: 2 }; - | ~~~~~ +LL - Struct { a, _ } = Struct { a: 1, b: 2 }; +LL + Struct { a, b } = Struct { a: 1, b: 2 }; + | help: if you don't care about this missing field, you can explicitly ignore it | -LL | Struct { a, b: _ } = Struct { a: 1, b: 2 }; - | ~~~~~~~~ +LL - Struct { a, _ } = Struct { a: 1, b: 2 }; +LL + Struct { a, b: _ } = Struct { a: 1, b: 2 }; + | help: or always ignore missing fields here | -LL | Struct { a, .. } = Struct { a: 1, b: 2 }; - | ~~~~~~ +LL - Struct { a, _ } = Struct { a: 1, b: 2 }; +LL + Struct { a, .. } = Struct { a: 1, b: 2 }; + | error[E0797]: base expression required after `..` --> $DIR/struct_destructure_fail.rs:15:19 diff --git a/tests/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr b/tests/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr index 5cc7acba3f334..515d4cc76215c 100644 --- a/tests/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr +++ b/tests/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr @@ -38,8 +38,9 @@ LL | TupleStruct(_, _) = TupleStruct(1, 2); | +++ help: use `..` to ignore all fields | -LL | TupleStruct(..) = TupleStruct(1, 2); - | ~~ +LL - TupleStruct(_) = TupleStruct(1, 2); +LL + TupleStruct(..) = TupleStruct(1, 2); + | error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields --> $DIR/tuple_struct_destructure_fail.rs:32:25 @@ -65,8 +66,9 @@ LL | Enum::SingleVariant(_, _) = Enum::SingleVariant(1, 2); | +++ help: use `..` to ignore all fields | -LL | Enum::SingleVariant(..) = Enum::SingleVariant(1, 2); - | ~~ +LL - Enum::SingleVariant(_) = Enum::SingleVariant(1, 2); +LL + Enum::SingleVariant(..) = Enum::SingleVariant(1, 2); + | error[E0070]: invalid left-hand side of assignment --> $DIR/tuple_struct_destructure_fail.rs:38:12 diff --git a/tests/ui/diagnostic-flags/allow-non-lint-warnings.rs b/tests/ui/diagnostic-flags/allow-non-lint-warnings.rs index 40b9e6536f500..8980ef9c4220f 100644 --- a/tests/ui/diagnostic-flags/allow-non-lint-warnings.rs +++ b/tests/ui/diagnostic-flags/allow-non-lint-warnings.rs @@ -1,8 +1,7 @@ // ignore-tidy-linelength //! Check that `-A warnings` cli flag applies to non-lint warnings as well. //! -//! This test tries to exercise that by checking that the "relaxing a default bound only does -//! something for `?Sized`; all other traits are not bound by default" non-lint warning (normally +//! This test tries to exercise that by checking that a non-lint warning (normally //! warn-by-default) is suppressed if the `-A warnings` cli flag is passed. //! //! Take special note that `warnings` is a special pseudo lint group in relationship to non-lint @@ -14,7 +13,7 @@ //! - Original impl PR: . //! - RFC 507 "Release channels": //! . -#![crate_type = "lib"] +#![feature(rustc_attrs)] //@ revisions: without_flag with_flag @@ -22,6 +21,6 @@ //@ check-pass -pub trait Trait {} -pub fn f() {} -//[without_flag]~^ WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +#[rustc_error(warn)] +fn main() {} +//[without_flag]~^ WARN unexpected annotation used with `#[rustc_error(...)]`! diff --git a/tests/ui/diagnostic-flags/allow-non-lint-warnings.without_flag.stderr b/tests/ui/diagnostic-flags/allow-non-lint-warnings.without_flag.stderr index b037847c70f1b..3f33ccbd1c9f9 100644 --- a/tests/ui/diagnostic-flags/allow-non-lint-warnings.without_flag.stderr +++ b/tests/ui/diagnostic-flags/allow-non-lint-warnings.without_flag.stderr @@ -1,8 +1,8 @@ -warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default - --> $DIR/allow-non-lint-warnings.rs:26:13 +warning: unexpected annotation used with `#[rustc_error(...)]`! + --> $DIR/allow-non-lint-warnings.rs:25:1 | -LL | pub fn f() {} - | ^^^^^^ +LL | fn main() {} + | ^^^^^^^^^ warning: 1 warning emitted diff --git a/tests/ui/diagnostic-width/E0271.ascii.stderr b/tests/ui/diagnostic-width/E0271.ascii.stderr index 7446b1a543e70..9a9c12a938f68 100644 --- a/tests/ui/diagnostic-width/E0271.ascii.stderr +++ b/tests/ui/diagnostic-width/E0271.ascii.stderr @@ -1,4 +1,4 @@ -error[E0271]: type mismatch resolving `>, ...>>, ...>>, ...> as Future>::Error == Foo` +error[E0271]: type mismatch resolving ` as Future>::Error == Foo` --> $DIR/E0271.rs:20:5 | LL | / Box::new( @@ -7,14 +7,16 @@ LL | | Err::<(), _>( LL | | Ok::<_, ()>( ... | LL | | ) - | |_____^ type mismatch resolving `, ...>>, ...> as Future>::Error == Foo` + | |_____^ type mismatch resolving ` as Future>::Error == Foo` | note: expected this to be `Foo` --> $DIR/E0271.rs:10:18 | LL | type Error = E; | ^ - = note: required for the cast from `Box>, ()>>, ()>>, ()>>` to `Box<(dyn Future + 'static)>` + = note: required for the cast from `Box>` to `Box<...>` + = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + = note: consider using `--verbose` to print the full type name to the console error: aborting due to 1 previous error diff --git a/tests/ui/diagnostic-width/E0271.rs b/tests/ui/diagnostic-width/E0271.rs index 061ba45c21980..0618772104143 100644 --- a/tests/ui/diagnostic-width/E0271.rs +++ b/tests/ui/diagnostic-width/E0271.rs @@ -1,7 +1,7 @@ //@ revisions: ascii unicode -//@[ascii] compile-flags: --diagnostic-width=40 -//@[unicode] compile-flags: -Zunstable-options --error-format=human-unicode --diagnostic-width=40 -//@ normalize-stderr: "long-type-\d+" -> "long-type-hash" +//@[ascii] compile-flags: --diagnostic-width=40 -Zwrite-long-types-to-disk=yes +//@[unicode] compile-flags: -Zunstable-options --error-format=human-unicode --diagnostic-width=40 -Zwrite-long-types-to-disk=yes +//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type-\d+.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type-hash.txt'" trait Future { type Error; } diff --git a/tests/ui/diagnostic-width/E0271.unicode.stderr b/tests/ui/diagnostic-width/E0271.unicode.stderr index 72df2a381a422..9c3deae66608c 100644 --- a/tests/ui/diagnostic-width/E0271.unicode.stderr +++ b/tests/ui/diagnostic-width/E0271.unicode.stderr @@ -1,4 +1,4 @@ -error[E0271]: type mismatch resolving `>, ...>>, ...>>, ...> as Future>::Error == Foo` +error[E0271]: type mismatch resolving ` as Future>::Error == Foo` ╭▸ $DIR/E0271.rs:20:5 │ LL │ ┏ Box::new( @@ -7,14 +7,16 @@ LL │ ┃ Err::<(), _>( LL │ ┃ Ok::<_, ()>( ‡ ┃ LL │ ┃ ) - │ ┗━━━━━┛ type mismatch resolving `, ...>>, ...> as Future>::Error == Foo` + │ ┗━━━━━┛ type mismatch resolving ` as Future>::Error == Foo` ╰╴ note: expected this to be `Foo` ╭▸ $DIR/E0271.rs:10:18 │ LL │ type Error = E; │ ━ - ╰ note: required for the cast from `Box>, ()>>, ()>>, ()>>` to `Box<(dyn Future + 'static)>` + ├ note: required for the cast from `Box>` to `Box<...>` + ├ note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + ╰ note: consider using `--verbose` to print the full type name to the console error: aborting due to 1 previous error diff --git a/tests/ui/diagnostic-width/binop.rs b/tests/ui/diagnostic-width/binop.rs new file mode 100644 index 0000000000000..60ba40b8047f9 --- /dev/null +++ b/tests/ui/diagnostic-width/binop.rs @@ -0,0 +1,17 @@ +//@ compile-flags: --diagnostic-width=60 -Zwrite-long-types-to-disk=yes +// The regex below normalizes the long type file name to make it suitable for compare-modes. +//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type-\d+.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type-hash.txt'" +type A = (i32, i32, i32, i32); +type B = (A, A, A, A); +type C = (B, B, B, B); +type D = (C, C, C, C); + +fn foo(x: D) { + x + x; //~ ERROR cannot add `(... +} + +fn bar(x: D) { + !x; //~ ERROR cannot apply unary operator `!` to type `(... +} + +fn main() {} diff --git a/tests/ui/diagnostic-width/binop.stderr b/tests/ui/diagnostic-width/binop.stderr new file mode 100644 index 0000000000000..fd69129c33e44 --- /dev/null +++ b/tests/ui/diagnostic-width/binop.stderr @@ -0,0 +1,24 @@ +error[E0369]: cannot add `(..., ..., ..., ...)` to `(..., ..., ..., ...)` + --> $DIR/binop.rs:10:7 + | +LL | x + x; + | - ^ - (..., ..., ..., ...) + | | + | (..., ..., ..., ...) + | + = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + = note: consider using `--verbose` to print the full type name to the console + +error[E0600]: cannot apply unary operator `!` to type `(..., ..., ..., ...)` + --> $DIR/binop.rs:14:5 + | +LL | !x; + | ^^ cannot apply unary operator `!` + | + = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + = note: consider using `--verbose` to print the full type name to the console + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0369, E0600. +For more information about an error, try `rustc --explain E0369`. diff --git a/tests/ui/diagnostic-width/long-E0308.ascii.stderr b/tests/ui/diagnostic-width/long-E0308.ascii.stderr index d45d6bf329bd3..83da558618870 100644 --- a/tests/ui/diagnostic-width/long-E0308.ascii.stderr +++ b/tests/ui/diagnostic-width/long-E0308.ascii.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/long-E0308.rs:46:9 + --> $DIR/long-E0308.rs:48:9 | LL | let x: Atype< | _____________- @@ -16,15 +16,15 @@ LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok... LL | | Ok("") LL | | )))))))))))))))))))))))))))))) LL | | )))))))))))))))))))))))))))))); - | |__________________________________^ expected `Atype, ...>`, found `Result, ...>` + | |__________________________________^ expected `Atype, i32>, i32>`, found `Result, _>, _>` | - = note: expected struct `Atype, ...>` - found enum `Result, ...>` - = note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308.ascii/long-E0308.long-type-hash.txt' + = note: expected struct `Atype, i32>` + found enum `Result, _>` + = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' = note: consider using `--verbose` to print the full type name to the console error[E0308]: mismatched types - --> $DIR/long-E0308.rs:59:26 + --> $DIR/long-E0308.rs:61:26 | LL | ))))))))))))))))) == Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(O... | __________________________^ @@ -32,15 +32,15 @@ LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(... LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok(""))))))) LL | | )))))))))))))))))))))))))))))) LL | | )))))))))))))))))))))))); - | |____________________________^ expected `Option>`, found `Result, ...>` + | |____________________________^ expected `Option>, _>>`, found `Result, _>, _>` | - = note: expected enum `Option>` - found enum `Result, ...>` - = note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308.ascii/long-E0308.long-type-hash.txt' + = note: expected enum `Option, _>>` + found enum `Result, _>` + = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' = note: consider using `--verbose` to print the full type name to the console error[E0308]: mismatched types - --> $DIR/long-E0308.rs:90:9 + --> $DIR/long-E0308.rs:92:9 | LL | let x: Atype< | ____________- @@ -50,17 +50,17 @@ LL | | Atype< ... | LL | | i32 LL | | > = (); - | | - ^^ expected `Atype, ...>`, found `()` + | | - ^^ expected `Atype, i32>, i32>`, found `()` | |_____| | expected due to this | - = note: expected struct `Atype, ...>` + = note: expected struct `Atype, i32>` found unit type `()` - = note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308.ascii/long-E0308.long-type-hash.txt' + = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' = note: consider using `--verbose` to print the full type name to the console error[E0308]: mismatched types - --> $DIR/long-E0308.rs:93:17 + --> $DIR/long-E0308.rs:95:17 | LL | let _: () = Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(O... | ____________--___^ @@ -70,11 +70,11 @@ LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(... LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok(""))))))) LL | | )))))))))))))))))))))))))))))) LL | | )))))))))))))))))))))))); - | |____________________________^ expected `()`, found `Result, ...>` + | |____________________________^ expected `()`, found `Result, _>, _>` | = note: expected unit type `()` - found enum `Result, ...>` - = note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308.ascii/long-E0308.long-type-hash.txt' + found enum `Result, _>` + = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' = note: consider using `--verbose` to print the full type name to the console error: aborting due to 4 previous errors diff --git a/tests/ui/diagnostic-width/long-E0308.rs b/tests/ui/diagnostic-width/long-E0308.rs index 939872260209c..26383d9418dd8 100644 --- a/tests/ui/diagnostic-width/long-E0308.rs +++ b/tests/ui/diagnostic-width/long-E0308.rs @@ -1,7 +1,9 @@ //@ revisions: ascii unicode //@[ascii] compile-flags: --diagnostic-width=60 -Zwrite-long-types-to-disk=yes //@[unicode] compile-flags: -Zunstable-options --json=diagnostic-unicode --diagnostic-width=60 -Zwrite-long-types-to-disk=yes -//@ normalize-stderr: "long-type-\d+" -> "long-type-hash" + +// The regex below normalizes the long type file name to make it suitable for compare-modes. +//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type-\d+.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type-hash.txt'" mod a { // Force the "short path for unique types" machinery to trip up diff --git a/tests/ui/diagnostic-width/long-E0308.unicode.stderr b/tests/ui/diagnostic-width/long-E0308.unicode.stderr index 3e8d881d7a6b1..54abf576dbd07 100644 --- a/tests/ui/diagnostic-width/long-E0308.unicode.stderr +++ b/tests/ui/diagnostic-width/long-E0308.unicode.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - ╭▸ $DIR/long-E0308.rs:46:9 + ╭▸ $DIR/long-E0308.rs:48:9 │ LL │ let x: Atype< │ ┌─────────────┘ @@ -16,15 +16,15 @@ LL │ ┃ Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(O… LL │ ┃ Ok("") LL │ ┃ )))))))))))))))))))))))))))))) LL │ ┃ )))))))))))))))))))))))))))))); - │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ expected `Atype, ...>`, found `Result, ...>` + │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ expected `Atype, i32>, i32>`, found `Result, _>, _>` │ - ├ note: expected struct `Atype, ...>` - │ found enum `Result, ...>` - ├ note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308.unicode/long-E0308.long-type-hash.txt' + ├ note: expected struct `Atype, i32>` + │ found enum `Result, _>` + ├ note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' ╰ note: consider using `--verbose` to print the full type name to the console error[E0308]: mismatched types - ╭▸ $DIR/long-E0308.rs:59:26 + ╭▸ $DIR/long-E0308.rs:61:26 │ LL │ ))))))))))))))))) == Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(… │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━┛ @@ -32,15 +32,15 @@ LL │ ┃ Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok… LL │ ┃ Ok(Ok(Ok(Ok(Ok(Ok(Ok(""))))))) LL │ ┃ )))))))))))))))))))))))))))))) LL │ ┃ )))))))))))))))))))))))); - │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ expected `Option>`, found `Result, ...>` + │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ expected `Option>, _>>`, found `Result, _>, _>` │ - ├ note: expected enum `Option>` - │ found enum `Result, ...>` - ├ note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308.unicode/long-E0308.long-type-hash.txt' + ├ note: expected enum `Option, _>>` + │ found enum `Result, _>` + ├ note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' ╰ note: consider using `--verbose` to print the full type name to the console error[E0308]: mismatched types - ╭▸ $DIR/long-E0308.rs:90:9 + ╭▸ $DIR/long-E0308.rs:92:9 │ LL │ let x: Atype< │ ┌────────────┘ @@ -50,17 +50,17 @@ LL │ │ Atype< ‡ │ LL │ │ i32 LL │ │ > = (); - │ │ │ ━━ expected `Atype, ...>`, found `()` + │ │ │ ━━ expected `Atype, i32>, i32>`, found `()` │ └─────┤ │ expected due to this │ - ├ note: expected struct `Atype, ...>` + ├ note: expected struct `Atype, i32>` │ found unit type `()` - ├ note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308.unicode/long-E0308.long-type-hash.txt' + ├ note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' ╰ note: consider using `--verbose` to print the full type name to the console error[E0308]: mismatched types - ╭▸ $DIR/long-E0308.rs:93:17 + ╭▸ $DIR/long-E0308.rs:95:17 │ LL │ let _: () = Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(… │ ┏━━━━━━━━━━━━┬─━━━┛ @@ -70,11 +70,11 @@ LL │ ┃ Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok… LL │ ┃ Ok(Ok(Ok(Ok(Ok(Ok(Ok(""))))))) LL │ ┃ )))))))))))))))))))))))))))))) LL │ ┃ )))))))))))))))))))))))); - │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ expected `()`, found `Result, ...>` + │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ expected `()`, found `Result, _>, _>` │ ├ note: expected unit type `()` - │ found enum `Result, ...>` - ├ note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308.unicode/long-E0308.long-type-hash.txt' + │ found enum `Result, _>` + ├ note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' ╰ note: consider using `--verbose` to print the full type name to the console error: aborting due to 4 previous errors diff --git a/tests/ui/diagnostic-width/long-e0277.rs b/tests/ui/diagnostic-width/long-e0277.rs new file mode 100644 index 0000000000000..9b3bd8bb72896 --- /dev/null +++ b/tests/ui/diagnostic-width/long-e0277.rs @@ -0,0 +1,15 @@ +//@ compile-flags: --diagnostic-width=60 -Zwrite-long-types-to-disk=yes +// The regex below normalizes the long type file name to make it suitable for compare-modes. +//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type-\d+.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type-hash.txt'" +type A = (i32, i32, i32, i32); +type B = (A, A, A, A); +type C = (B, B, B, B); +type D = (C, C, C, C); + +trait Trait {} + +fn require_trait() {} + +fn main() { + require_trait::(); //~ ERROR the trait bound `(... +} diff --git a/tests/ui/diagnostic-width/long-e0277.stderr b/tests/ui/diagnostic-width/long-e0277.stderr new file mode 100644 index 0000000000000..a57270df7e252 --- /dev/null +++ b/tests/ui/diagnostic-width/long-e0277.stderr @@ -0,0 +1,23 @@ +error[E0277]: the trait bound `(..., ..., ..., ...): Trait` is not satisfied + --> $DIR/long-e0277.rs:14:21 + | +LL | require_trait::(); + | ^ unsatisfied trait bound + | + = help: the trait `Trait` is not implemented for `(..., ..., ..., ...)` +help: this trait has no implementations, consider adding one + --> $DIR/long-e0277.rs:9:1 + | +LL | trait Trait {} + | ^^^^^^^^^^^ +note: required by a bound in `require_trait` + --> $DIR/long-e0277.rs:11:21 + | +LL | fn require_trait() {} + | ^^^^^ required by this bound in `require_trait` + = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + = note: consider using `--verbose` to print the full type name to the console + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic-width/non-copy-type-moved.rs b/tests/ui/diagnostic-width/non-copy-type-moved.rs new file mode 100644 index 0000000000000..a220c62775eb8 --- /dev/null +++ b/tests/ui/diagnostic-width/non-copy-type-moved.rs @@ -0,0 +1,19 @@ +//@ compile-flags: --diagnostic-width=60 -Zwrite-long-types-to-disk=yes +// The regex below normalizes the long type file name to make it suitable for compare-modes. +//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type-\d+.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type-hash.txt'" + +type A = (String, String, String, String); +type B = (A, A, A, A); +type C = (B, B, B, B); +type D = (C, C, C, C); + +trait Trait {} + +fn require_trait() {} + +fn foo(x: D) { + let _a = x; + let _b = x; //~ ERROR use of moved value +} + +fn main() {} diff --git a/tests/ui/diagnostic-width/non-copy-type-moved.stderr b/tests/ui/diagnostic-width/non-copy-type-moved.stderr new file mode 100644 index 0000000000000..254542c7b3908 --- /dev/null +++ b/tests/ui/diagnostic-width/non-copy-type-moved.stderr @@ -0,0 +1,20 @@ +error[E0382]: use of moved value: `x` + --> $DIR/non-copy-type-moved.rs:16:14 + | +LL | fn foo(x: D) { + | - move occurs because `x` has type `(..., ..., ..., ...)`, which does not implement the `Copy` trait +LL | let _a = x; + | - value moved here +LL | let _b = x; + | ^ value used here after move + | + = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + = note: consider using `--verbose` to print the full type name to the console +help: consider cloning the value if the performance cost is acceptable + | +LL | let _a = x.clone(); + | ++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic-width/secondary-label-with-long-type.rs b/tests/ui/diagnostic-width/secondary-label-with-long-type.rs new file mode 100644 index 0000000000000..c8845af318357 --- /dev/null +++ b/tests/ui/diagnostic-width/secondary-label-with-long-type.rs @@ -0,0 +1,19 @@ +//@ compile-flags: --diagnostic-width=100 -Zwrite-long-types-to-disk=yes +// The regex below normalizes the long type file name to make it suitable for compare-modes. +//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type-\d+.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type-hash.txt'" + +type A = (i32, i32, i32, i32); +type B = (A, A, A, A); +type C = (B, B, B, B); +type D = (C, C, C, C); + +fn foo(x: D) { + let () = x; //~ ERROR mismatched types + //~^ NOTE this expression has type `((..., + //~| NOTE expected `((..., + //~| NOTE expected tuple + //~| NOTE the full name for the type has been written to + //~| NOTE consider using `--verbose` to print the full type name to the console +} + +fn main() {} diff --git a/tests/ui/diagnostic-width/secondary-label-with-long-type.stderr b/tests/ui/diagnostic-width/secondary-label-with-long-type.stderr new file mode 100644 index 0000000000000..a95e17091489e --- /dev/null +++ b/tests/ui/diagnostic-width/secondary-label-with-long-type.stderr @@ -0,0 +1,16 @@ +error[E0308]: mismatched types + --> $DIR/secondary-label-with-long-type.rs:11:9 + | +LL | let () = x; + | ^^ - this expression has type `((..., ..., ..., ...), ..., ..., ...)` + | | + | expected `((..., ..., ..., ...), ..., ..., ...)`, found `()` + | + = note: expected tuple `((..., ..., ..., ...), ..., ..., ...)` + found unit type `()` + = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' + = note: consider using `--verbose` to print the full type name to the console + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr index 1b76669ccb0d7..4f685c508c721 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr +++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr @@ -16,23 +16,13 @@ LL | where LL | T: AsExpression, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::check` -error[E0277]: the trait bound `&str: AsExpression` is not satisfied - --> $DIR/as_expression.rs:55:15 - | -LL | SelectInt.check("bar"); - | ^^^^^ the trait `AsExpression` is not implemented for `&str` - | - = help: the trait `AsExpression` is not implemented for `&str` - but trait `AsExpression` is implemented for it - = help: for that trait implementation, expected `Text`, found `Integer` - error[E0271]: type mismatch resolving `::SqlType == Text` --> $DIR/as_expression.rs:55:5 | LL | SelectInt.check("bar"); | ^^^^^^^^^^^^^^^^^^^^^^ expected `Text`, found `Integer` -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors Some errors have detailed explanations: E0271, E0277. For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs index 583b3c4675a87..48c1ed2b02d71 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs +++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs @@ -53,7 +53,7 @@ impl Foo for T where T: Expression {} fn main() { SelectInt.check("bar"); - //~^ ERROR the trait bound `&str: AsExpression` is not satisfied - //[next]~| the trait bound `&str: AsExpression<::SqlType>` is not satisfied + //[current]~^ ERROR the trait bound `&str: AsExpression` is not satisfied + //[next]~^^ the trait bound `&str: AsExpression<::SqlType>` is not satisfied //[next]~| type mismatch } diff --git a/tests/ui/diagnostic_namespace/suggest_typos.stderr b/tests/ui/diagnostic_namespace/suggest_typos.stderr index ff4ee9717d427..f41e2f6555675 100644 --- a/tests/ui/diagnostic_namespace/suggest_typos.stderr +++ b/tests/ui/diagnostic_namespace/suggest_typos.stderr @@ -11,8 +11,9 @@ LL | #![deny(unknown_or_malformed_diagnostic_attributes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: an attribute with a similar name exists | -LL | #[diagnostic::on_unimplemented] - | ~~~~~~~~~~~~~~~~ +LL - #[diagnostic::onunimplemented] +LL + #[diagnostic::on_unimplemented] + | error: unknown diagnostic attribute --> $DIR/suggest_typos.rs:9:15 @@ -22,8 +23,9 @@ LL | #[diagnostic::un_onimplemented] | help: an attribute with a similar name exists | -LL | #[diagnostic::on_unimplemented] - | ~~~~~~~~~~~~~~~~ +LL - #[diagnostic::un_onimplemented] +LL + #[diagnostic::on_unimplemented] + | error: unknown diagnostic attribute --> $DIR/suggest_typos.rs:14:15 @@ -33,8 +35,9 @@ LL | #[diagnostic::on_implemented] | help: an attribute with a similar name exists | -LL | #[diagnostic::on_unimplemented] - | ~~~~~~~~~~~~~~~~ +LL - #[diagnostic::on_implemented] +LL + #[diagnostic::on_unimplemented] + | error: aborting due to 3 previous errors diff --git a/tests/ui/did_you_mean/bad-assoc-ty.stderr b/tests/ui/did_you_mean/bad-assoc-ty.stderr index 41039ae82a6fa..7e34f4d35b4e6 100644 --- a/tests/ui/did_you_mean/bad-assoc-ty.stderr +++ b/tests/ui/did_you_mean/bad-assoc-ty.stderr @@ -109,8 +109,9 @@ LL | type A = [u8; 4]::AssocTy; | help: if there were a trait named `Example` with associated type `AssocTy` implemented for `[u8; 4]`, you could use the fully-qualified path | -LL | type A = <[u8; 4] as Example>::AssocTy; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - type A = [u8; 4]::AssocTy; +LL + type A = <[u8; 4] as Example>::AssocTy; + | error[E0223]: ambiguous associated type --> $DIR/bad-assoc-ty.rs:5:10 @@ -120,8 +121,9 @@ LL | type B = [u8]::AssocTy; | help: if there were a trait named `Example` with associated type `AssocTy` implemented for `[u8]`, you could use the fully-qualified path | -LL | type B = <[u8] as Example>::AssocTy; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - type B = [u8]::AssocTy; +LL + type B = <[u8] as Example>::AssocTy; + | error[E0223]: ambiguous associated type --> $DIR/bad-assoc-ty.rs:9:10 @@ -131,8 +133,9 @@ LL | type C = (u8)::AssocTy; | help: if there were a trait named `Example` with associated type `AssocTy` implemented for `u8`, you could use the fully-qualified path | -LL | type C = ::AssocTy; - | ~~~~~~~~~~~~~~~~~~~~~~~~ +LL - type C = (u8)::AssocTy; +LL + type C = ::AssocTy; + | error[E0223]: ambiguous associated type --> $DIR/bad-assoc-ty.rs:13:10 @@ -142,8 +145,9 @@ LL | type D = (u8, u8)::AssocTy; | help: if there were a trait named `Example` with associated type `AssocTy` implemented for `(u8, u8)`, you could use the fully-qualified path | -LL | type D = <(u8, u8) as Example>::AssocTy; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - type D = (u8, u8)::AssocTy; +LL + type D = <(u8, u8) as Example>::AssocTy; + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for type aliases --> $DIR/bad-assoc-ty.rs:17:10 @@ -159,8 +163,9 @@ LL | type F = &'static (u8)::AssocTy; | help: if there were a trait named `Example` with associated type `AssocTy` implemented for `u8`, you could use the fully-qualified path | -LL | type F = &'static ::AssocTy; - | ~~~~~~~~~~~~~~~~~~~~~~~~ +LL - type F = &'static (u8)::AssocTy; +LL + type F = &'static ::AssocTy; + | error[E0223]: ambiguous associated type --> $DIR/bad-assoc-ty.rs:27:10 @@ -170,8 +175,9 @@ LL | type G = dyn 'static + (Send)::AssocTy; | help: if there were a trait named `Example` with associated type `AssocTy` implemented for `(dyn Send + 'static)`, you could use the fully-qualified path | -LL | type G = <(dyn Send + 'static) as Example>::AssocTy; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - type G = dyn 'static + (Send)::AssocTy; +LL + type G = <(dyn Send + 'static) as Example>::AssocTy; + | warning: trait objects without an explicit `dyn` are deprecated --> $DIR/bad-assoc-ty.rs:33:10 @@ -195,10 +201,12 @@ LL | type H = Fn(u8) -> (u8)::Output; | help: use fully-qualified syntax | -LL | type H = <(dyn Fn(u8) -> u8 + 'static) as BitOr>::Output; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | type H = <(dyn Fn(u8) -> u8 + 'static) as IntoFuture>::Output; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - type H = Fn(u8) -> (u8)::Output; +LL + type H = <(dyn Fn(u8) -> u8 + 'static) as BitOr>::Output; + | +LL - type H = Fn(u8) -> (u8)::Output; +LL + type H = <(dyn Fn(u8) -> u8 + 'static) as IntoFuture>::Output; + | error[E0223]: ambiguous associated type --> $DIR/bad-assoc-ty.rs:39:19 @@ -212,8 +220,9 @@ LL | type J = ty!(u8); = note: this error originates in the macro `ty` (in Nightly builds, run with -Z macro-backtrace for more info) help: if there were a trait named `Example` with associated type `AssocTy` implemented for `u8`, you could use the fully-qualified path | -LL | ($ty: ty) => (::AssocTy); - | ~~~~~~~~~~~~~~~~~~~~~~~~ +LL - ($ty: ty) => ($ty::AssocTy); +LL + ($ty: ty) => (::AssocTy); + | error[E0223]: ambiguous associated type --> $DIR/bad-assoc-ty.rs:46:10 @@ -223,8 +232,9 @@ LL | type I = ty!()::AssocTy; | help: if there were a trait named `Example` with associated type `AssocTy` implemented for `u8`, you could use the fully-qualified path | -LL | type I = ::AssocTy; - | ~~~~~~~~~~~~~~~~~~~~~~~~ +LL - type I = ty!()::AssocTy; +LL + type I = ::AssocTy; + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/bad-assoc-ty.rs:51:13 @@ -233,11 +243,6 @@ LL | fn foo>(x: X) {} | ^ ^ not allowed in type signatures | | | not allowed in type signatures - | -help: use type parameters instead - | -LL | fn foo, T>(x: X) {} - | ~ ~ +++ error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/bad-assoc-ty.rs:54:34 @@ -247,8 +252,9 @@ LL | fn bar(_: F) where F: Fn() -> _ {} | help: use type parameters instead | -LL | fn bar(_: F) where F: Fn() -> T {} - | +++ ~ +LL - fn bar(_: F) where F: Fn() -> _ {} +LL + fn bar(_: F) where F: Fn() -> T {} + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/bad-assoc-ty.rs:57:19 @@ -258,8 +264,9 @@ LL | fn baz _>(_: F) {} | help: use type parameters instead | -LL | fn baz T, T>(_: F) {} - | ~+++ +LL - fn baz _>(_: F) {} +LL + fn baz T, T>(_: F) {} + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for structs --> $DIR/bad-assoc-ty.rs:60:33 @@ -269,8 +276,9 @@ LL | struct L(F) where F: Fn() -> _; | help: use type parameters instead | -LL | struct L(F) where F: Fn() -> T; - | +++ ~ +LL - struct L(F) where F: Fn() -> _; +LL + struct L(F) where F: Fn() -> T; + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/bad-assoc-ty.rs:82:38 @@ -280,8 +288,9 @@ LL | fn foo(_: F) where F: Fn() -> _ {} | help: use type parameters instead | -LL | fn foo(_: F) where F: Fn() -> T {} - | +++ ~ +LL - fn foo(_: F) where F: Fn() -> _ {} +LL + fn foo(_: F) where F: Fn() -> T {} + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for structs --> $DIR/bad-assoc-ty.rs:62:30 @@ -291,8 +300,9 @@ LL | struct M where F: Fn() -> _ { | help: use type parameters instead | -LL | struct M where F: Fn() -> T { - | +++ ~ +LL - struct M where F: Fn() -> _ { +LL + struct M where F: Fn() -> T { + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for enums --> $DIR/bad-assoc-ty.rs:66:28 @@ -302,8 +312,9 @@ LL | enum N where F: Fn() -> _ { | help: use type parameters instead | -LL | enum N where F: Fn() -> T { - | +++ ~ +LL - enum N where F: Fn() -> _ { +LL + enum N where F: Fn() -> T { + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for unions --> $DIR/bad-assoc-ty.rs:71:29 @@ -313,8 +324,9 @@ LL | union O where F: Fn() -> _ { | help: use type parameters instead | -LL | union O where F: Fn() -> T { - | +++ ~ +LL - union O where F: Fn() -> _ { +LL + union O where F: Fn() -> T { + | error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union --> $DIR/bad-assoc-ty.rs:73:5 @@ -336,8 +348,9 @@ LL | trait P where F: Fn() -> _ { | help: use type parameters instead | -LL | trait P where F: Fn() -> T { - | +++ ~ +LL - trait P where F: Fn() -> _ { +LL + trait P where F: Fn() -> T { + | error: aborting due to 29 previous errors; 1 warning emitted diff --git a/tests/ui/did_you_mean/brackets-to-braces-single-element.stderr b/tests/ui/did_you_mean/brackets-to-braces-single-element.stderr index a7ec192592e0d..d4aeb1eee96a1 100644 --- a/tests/ui/did_you_mean/brackets-to-braces-single-element.stderr +++ b/tests/ui/did_you_mean/brackets-to-braces-single-element.stderr @@ -6,8 +6,9 @@ LL | const A: [&str; 1] = { "hello" }; | help: to create an array, use square brackets instead of curly braces | -LL | const A: [&str; 1] = [ "hello" ]; - | ~ ~ +LL - const A: [&str; 1] = { "hello" }; +LL + const A: [&str; 1] = [ "hello" ]; + | error[E0308]: mismatched types --> $DIR/brackets-to-braces-single-element.rs:4:19 @@ -19,8 +20,9 @@ LL | const B: &[u32] = &{ 1 }; found reference `&{integer}` help: to create an array, use square brackets instead of curly braces | -LL | const B: &[u32] = &[ 1 ]; - | ~ ~ +LL - const B: &[u32] = &{ 1 }; +LL + const B: &[u32] = &[ 1 ]; + | error[E0308]: mismatched types --> $DIR/brackets-to-braces-single-element.rs:7:27 @@ -30,8 +32,9 @@ LL | const C: &&[u32; 1] = &&{ 1 }; | help: to create an array, use square brackets instead of curly braces | -LL | const C: &&[u32; 1] = &&[ 1 ]; - | ~ ~ +LL - const C: &&[u32; 1] = &&{ 1 }; +LL + const C: &&[u32; 1] = &&[ 1 ]; + | error: aborting due to 3 previous errors diff --git a/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.rs b/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.rs new file mode 100644 index 0000000000000..fa1663d49eb2a --- /dev/null +++ b/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.rs @@ -0,0 +1,9 @@ +//@ edition: 2021 + +fn foo() {} + +fn main() { + let _: Vec<(&str, fn())> = [("foo", foo)].into_iter().collect(); //~ ERROR + let _: Vec = [foo].into_iter().collect(); //~ ERROR + let _: Vec = Vec::from([foo]); //~ ERROR +} diff --git a/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.stderr b/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.stderr new file mode 100644 index 0000000000000..d069d39514dcd --- /dev/null +++ b/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.stderr @@ -0,0 +1,59 @@ +error[E0277]: a value of type `Vec<(&str, fn())>` cannot be built from an iterator over elements of type `(&str, fn() {foo})` + --> $DIR/casting-fn-item-to-fn-pointer.rs:6:59 + | +LL | let _: Vec<(&str, fn())> = [("foo", foo)].into_iter().collect(); + | ^^^^^^^ value of type `Vec<(&str, fn())>` cannot be built from `std::iter::Iterator` + | + = help: the trait `FromIterator<(&_, fn() {foo})>` is not implemented for `Vec<(&str, fn())>` + but trait `FromIterator<(&_, fn())>` is implemented for it + = help: for that trait implementation, expected `fn()`, found `fn() {foo}` + = note: fn items are distinct from fn pointers + = help: consider casting the fn item to a fn pointer: `foo as fn()` +note: the method call chain might not have had the expected associated types + --> $DIR/casting-fn-item-to-fn-pointer.rs:6:47 + | +LL | let _: Vec<(&str, fn())> = [("foo", foo)].into_iter().collect(); + | -------------- ^^^^^^^^^^^ `Iterator::Item` is `(&str, fn() {foo})` here + | | + | this expression has type `[(&str, fn() {foo}); 1]` +note: required by a bound in `collect` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + +error[E0277]: a value of type `Vec` cannot be built from an iterator over elements of type `fn() {foo}` + --> $DIR/casting-fn-item-to-fn-pointer.rs:7:42 + | +LL | let _: Vec = [foo].into_iter().collect(); + | ^^^^^^^ value of type `Vec` cannot be built from `std::iter::Iterator` + | + = help: the trait `FromIterator` is not implemented for `Vec` + but trait `FromIterator` is implemented for it + = help: for that trait implementation, expected `fn()`, found `fn() {foo}` + = note: fn items are distinct from fn pointers + = help: consider casting the fn item to a fn pointer: `foo as fn()` +note: the method call chain might not have had the expected associated types + --> $DIR/casting-fn-item-to-fn-pointer.rs:7:30 + | +LL | let _: Vec = [foo].into_iter().collect(); + | ----- ^^^^^^^^^^^ `Iterator::Item` is `fn() {foo}` here + | | + | this expression has type `[fn() {foo}; 1]` +note: required by a bound in `collect` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + +error[E0308]: mismatched types + --> $DIR/casting-fn-item-to-fn-pointer.rs:8:24 + | +LL | let _: Vec = Vec::from([foo]); + | --------- ^^^^^^^^^^^^^^^^ expected `Vec`, found `Vec` + | | + | expected due to this + | + = note: expected struct `Vec` + found struct `Vec` + = note: fn items are distinct from fn pointers + = help: consider casting the fn item to a fn pointer: `foo as fn()` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/did_you_mean/compatible-variants-in-pat.stderr b/tests/ui/did_you_mean/compatible-variants-in-pat.stderr index 5e48871bb01bb..09cf094e6bd72 100644 --- a/tests/ui/did_you_mean/compatible-variants-in-pat.stderr +++ b/tests/ui/did_you_mean/compatible-variants-in-pat.stderr @@ -33,8 +33,9 @@ LL | Some(S) => { | +++++ + help: introduce a new binding instead | -LL | other_s => { - | ~~~~~~~ +LL - S => { +LL + other_s => { + | error[E0308]: mismatched types --> $DIR/compatible-variants-in-pat.rs:32:9 @@ -60,8 +61,9 @@ LL | Err(S) => { | ++++ + help: introduce a new binding instead | -LL | other_s => { - | ~~~~~~~ +LL - S => { +LL + other_s => { + | error: aborting due to 3 previous errors diff --git a/tests/ui/did_you_mean/dont-suggest-hygienic-fields.stderr b/tests/ui/did_you_mean/dont-suggest-hygienic-fields.stderr index 473c9a339fc25..411eec8496339 100644 --- a/tests/ui/did_you_mean/dont-suggest-hygienic-fields.stderr +++ b/tests/ui/did_you_mean/dont-suggest-hygienic-fields.stderr @@ -11,7 +11,7 @@ LL | const CRATE: Crate = Crate { fiel: () }; help: a field with a similar name exists | LL | const CRATE: Crate = Crate { field: () }; - | ~~~~~ + | + error[E0609]: no field `field` on type `Compound` --> $DIR/dont-suggest-hygienic-fields.rs:24:16 diff --git a/tests/ui/did_you_mean/issue-36798.stderr b/tests/ui/did_you_mean/issue-36798.stderr index 70aa3c32bfbf4..233cb1e32486d 100644 --- a/tests/ui/did_you_mean/issue-36798.stderr +++ b/tests/ui/did_you_mean/issue-36798.stderr @@ -6,8 +6,9 @@ LL | f.baz; | help: a field with a similar name exists | -LL | f.bar; - | ~~~ +LL - f.baz; +LL + f.bar; + | error: aborting due to 1 previous error diff --git a/tests/ui/did_you_mean/issue-38147-1.stderr b/tests/ui/did_you_mean/issue-38147-1.stderr index a0392113ab155..6def86e4ba8f1 100644 --- a/tests/ui/did_you_mean/issue-38147-1.stderr +++ b/tests/ui/did_you_mean/issue-38147-1.stderr @@ -7,7 +7,7 @@ LL | self.s.push('x'); help: consider changing this to be a mutable reference | LL | fn f(&mut self) { - | ~~~~~~~~~ + | +++ error: aborting due to 1 previous error diff --git a/tests/ui/did_you_mean/issue-39544.stderr b/tests/ui/did_you_mean/issue-39544.stderr index 8ccb4cbb0c167..62dc027e31f2d 100644 --- a/tests/ui/did_you_mean/issue-39544.stderr +++ b/tests/ui/did_you_mean/issue-39544.stderr @@ -18,7 +18,7 @@ LL | let _ = &mut self.x; help: consider changing this to be a mutable reference | LL | fn foo<'z>(&'z mut self) { - | ~~~~~~~~~~~~ + | +++ error[E0596]: cannot borrow `self.x` as mutable, as it is behind a `&` reference --> $DIR/issue-39544.rs:20:17 @@ -29,7 +29,7 @@ LL | let _ = &mut self.x; help: consider changing this to be a mutable reference | LL | fn foo1(&mut self, other: &Z) { - | ~~~~~~~~~ + | +++ error[E0596]: cannot borrow `other.x` as mutable, as it is behind a `&` reference --> $DIR/issue-39544.rs:21:17 @@ -51,7 +51,7 @@ LL | let _ = &mut self.x; help: consider changing this to be a mutable reference | LL | fn foo2<'a>(&'a mut self, other: &Z) { - | ~~~~~~~~~~~~ + | +++ error[E0596]: cannot borrow `other.x` as mutable, as it is behind a `&` reference --> $DIR/issue-39544.rs:26:17 diff --git a/tests/ui/did_you_mean/issue-41679-tilde-bitwise-negation-attempt.stderr b/tests/ui/did_you_mean/issue-41679-tilde-bitwise-negation-attempt.stderr index 952ac76a003d6..9893aad7486b4 100644 --- a/tests/ui/did_you_mean/issue-41679-tilde-bitwise-negation-attempt.stderr +++ b/tests/ui/did_you_mean/issue-41679-tilde-bitwise-negation-attempt.stderr @@ -6,8 +6,9 @@ LL | let _x = ~1; | help: use `!` to perform bitwise not | -LL | let _x = !1; - | ~ +LL - let _x = ~1; +LL + let _x = !1; + | error: unexpected `1` after identifier --> $DIR/issue-41679-tilde-bitwise-negation-attempt.rs:5:18 @@ -17,8 +18,9 @@ LL | let _y = not 1; | help: use `!` to perform bitwise not | -LL | let _y = !1; - | ~ +LL - let _y = not 1; +LL + let _y = !1; + | error: unexpected keyword `false` after identifier --> $DIR/issue-41679-tilde-bitwise-negation-attempt.rs:6:18 @@ -28,8 +30,9 @@ LL | let _z = not false; | help: use `!` to perform logical negation | -LL | let _z = !false; - | ~ +LL - let _z = not false; +LL + let _z = !false; + | error: unexpected keyword `true` after identifier --> $DIR/issue-41679-tilde-bitwise-negation-attempt.rs:7:18 @@ -39,8 +42,9 @@ LL | let _a = not true; | help: use `!` to perform logical negation | -LL | let _a = !true; - | ~ +LL - let _a = not true; +LL + let _a = !true; + | error: unexpected `v` after identifier --> $DIR/issue-41679-tilde-bitwise-negation-attempt.rs:9:18 @@ -50,8 +54,9 @@ LL | let _v = not v; | help: use `!` to perform logical negation or bitwise not | -LL | let _v = !v; - | ~ +LL - let _v = not v; +LL + let _v = !v; + | error: aborting due to 5 previous errors diff --git a/tests/ui/did_you_mean/issue-42599_available_fields_note.stderr b/tests/ui/did_you_mean/issue-42599_available_fields_note.stderr index d60db01a4653d..8fe46eb7ee184 100644 --- a/tests/ui/did_you_mean/issue-42599_available_fields_note.stderr +++ b/tests/ui/did_you_mean/issue-42599_available_fields_note.stderr @@ -6,8 +6,9 @@ LL | Self { secret_integer: 2, inocently_mispellable: () } | help: a field with a similar name exists | -LL | Self { secret_integer: 2, innocently_misspellable: () } - | ~~~~~~~~~~~~~~~~~~~~~~~ +LL - Self { secret_integer: 2, inocently_mispellable: () } +LL + Self { secret_integer: 2, innocently_misspellable: () } + | error[E0560]: struct `Demo` has no field named `egregiously_nonexistent_field` --> $DIR/issue-42599_available_fields_note.rs:21:39 @@ -25,8 +26,9 @@ LL | let innocent_field_misaccess = demo.inocently_mispellable; | help: a field with a similar name exists | -LL | let innocent_field_misaccess = demo.innocently_misspellable; - | ~~~~~~~~~~~~~~~~~~~~~~~ +LL - let innocent_field_misaccess = demo.inocently_mispellable; +LL + let innocent_field_misaccess = demo.innocently_misspellable; + | error[E0609]: no field `egregiously_nonexistent_field` on type `Demo` --> $DIR/issue-42599_available_fields_note.rs:35:42 diff --git a/tests/ui/did_you_mean/issue-46718-struct-pattern-dotdotdot.stderr b/tests/ui/did_you_mean/issue-46718-struct-pattern-dotdotdot.stderr index 92cbc03e0dd78..633f331a13a4a 100644 --- a/tests/ui/did_you_mean/issue-46718-struct-pattern-dotdotdot.stderr +++ b/tests/ui/did_you_mean/issue-46718-struct-pattern-dotdotdot.stderr @@ -6,8 +6,9 @@ LL | PersonalityInventory { expressivity: exp, ... } => exp | help: to omit remaining fields, use `..` | -LL | PersonalityInventory { expressivity: exp, .. } => exp - | ~~ +LL - PersonalityInventory { expressivity: exp, ... } => exp +LL + PersonalityInventory { expressivity: exp, .. } => exp + | error: aborting due to 1 previous error diff --git a/tests/ui/did_you_mean/issue-46836-identifier-not-instead-of-negation.stderr b/tests/ui/did_you_mean/issue-46836-identifier-not-instead-of-negation.stderr index 6dea6a4fac8f2..3a25181d29ccc 100644 --- a/tests/ui/did_you_mean/issue-46836-identifier-not-instead-of-negation.stderr +++ b/tests/ui/did_you_mean/issue-46836-identifier-not-instead-of-negation.stderr @@ -6,8 +6,9 @@ LL | if not for_you { | help: use `!` to perform logical negation or bitwise not | -LL | if !for_you { - | ~ +LL - if not for_you { +LL + if !for_you { + | error: unexpected `the_worst` after identifier --> $DIR/issue-46836-identifier-not-instead-of-negation.rs:11:15 @@ -17,8 +18,9 @@ LL | while not the_worst { | help: use `!` to perform logical negation or bitwise not | -LL | while !the_worst { - | ~ +LL - while not the_worst { +LL + while !the_worst { + | error: unexpected `println` after identifier --> $DIR/issue-46836-identifier-not-instead-of-negation.rs:20:9 @@ -28,8 +30,9 @@ LL | println!("Then when?"); | help: use `!` to perform logical negation or bitwise not | -LL | if !// lack of braces is [sic] - | ~ +LL - if not // lack of braces is [sic] +LL + if !// lack of braces is [sic] + | error: expected `{`, found `;` --> $DIR/issue-46836-identifier-not-instead-of-negation.rs:20:31 @@ -53,8 +56,9 @@ LL | let resource = not 2; | help: use `!` to perform bitwise not | -LL | let resource = !2; - | ~ +LL - let resource = not 2; +LL + let resource = !2; + | error: unexpected `be_smothered_out_before` after identifier --> $DIR/issue-46836-identifier-not-instead-of-negation.rs:32:27 @@ -64,8 +68,9 @@ LL | let young_souls = not be_smothered_out_before; | help: use `!` to perform logical negation or bitwise not | -LL | let young_souls = !be_smothered_out_before; - | ~ +LL - let young_souls = not be_smothered_out_before; +LL + let young_souls = !be_smothered_out_before; + | error: aborting due to 6 previous errors diff --git a/tests/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr b/tests/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr index 6d92fa5e14e98..d4812d4831b03 100644 --- a/tests/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr +++ b/tests/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.stderr @@ -34,8 +34,9 @@ LL | (Nucleotide::Adenine, Nucleotide::Cytosine, _) => true | + + help: ...or a vertical bar to match on multiple alternatives | -LL | Nucleotide::Adenine | Nucleotide::Cytosine | _ => true - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - Nucleotide::Adenine, Nucleotide::Cytosine, _ => true +LL + Nucleotide::Adenine | Nucleotide::Cytosine | _ => true + | error: unexpected `,` in pattern --> $DIR/issue-48492-tuple-destructure-missing-parens.rs:67:10 diff --git a/tests/ui/did_you_mean/issue-49746-unicode-confusable-in-float-literal-expt.stderr b/tests/ui/did_you_mean/issue-49746-unicode-confusable-in-float-literal-expt.stderr index 4b3d429c7509a..664bf69b9ebc5 100644 --- a/tests/ui/did_you_mean/issue-49746-unicode-confusable-in-float-literal-expt.stderr +++ b/tests/ui/did_you_mean/issue-49746-unicode-confusable-in-float-literal-expt.stderr @@ -12,8 +12,9 @@ LL | const UNIVERSAL_GRAVITATIONAL_CONSTANT: f64 = 6.674e−11; // m³⋅kg⁻¹ | help: Unicode character '−' (Minus Sign) looks like '-' (Minus/Hyphen), but it is not | -LL | const UNIVERSAL_GRAVITATIONAL_CONSTANT: f64 = 6.674e-11; // m³⋅kg⁻¹⋅s⁻² - | ~ +LL - const UNIVERSAL_GRAVITATIONAL_CONSTANT: f64 = 6.674e−11; // m³⋅kg⁻¹⋅s⁻² +LL + const UNIVERSAL_GRAVITATIONAL_CONSTANT: f64 = 6.674e-11; // m³⋅kg⁻¹⋅s⁻² + | error: aborting due to 2 previous errors diff --git a/tests/ui/did_you_mean/issue-54109-and_instead_of_ampersands.stderr b/tests/ui/did_you_mean/issue-54109-and_instead_of_ampersands.stderr index c52102e2631d8..5dad6924dfd7f 100644 --- a/tests/ui/did_you_mean/issue-54109-and_instead_of_ampersands.stderr +++ b/tests/ui/did_you_mean/issue-54109-and_instead_of_ampersands.stderr @@ -7,8 +7,9 @@ LL | let _ = a and b; = note: unlike in e.g., Python and PHP, `&&` and `||` are used for logical operators help: use `&&` to perform logical conjunction | -LL | let _ = a && b; - | ~~ +LL - let _ = a and b; +LL + let _ = a && b; + | error: `and` is not a logical operator --> $DIR/issue-54109-and_instead_of_ampersands.rs:9:10 @@ -19,8 +20,9 @@ LL | if a and b { = note: unlike in e.g., Python and PHP, `&&` and `||` are used for logical operators help: use `&&` to perform logical conjunction | -LL | if a && b { - | ~~ +LL - if a and b { +LL + if a && b { + | error: `or` is not a logical operator --> $DIR/issue-54109-and_instead_of_ampersands.rs:20:15 @@ -31,8 +33,9 @@ LL | let _ = a or b; = note: unlike in e.g., Python and PHP, `&&` and `||` are used for logical operators help: use `||` to perform logical disjunction | -LL | let _ = a || b; - | ~~ +LL - let _ = a or b; +LL + let _ = a || b; + | error: `or` is not a logical operator --> $DIR/issue-54109-and_instead_of_ampersands.rs:22:10 @@ -43,8 +46,9 @@ LL | if a or b { = note: unlike in e.g., Python and PHP, `&&` and `||` are used for logical operators help: use `||` to perform logical disjunction | -LL | if a || b { - | ~~ +LL - if a or b { +LL + if a || b { + | error: `and` is not a logical operator --> $DIR/issue-54109-and_instead_of_ampersands.rs:30:11 @@ -55,8 +59,9 @@ LL | if (a and b) { = note: unlike in e.g., Python and PHP, `&&` and `||` are used for logical operators help: use `&&` to perform logical conjunction | -LL | if (a && b) { - | ~~ +LL - if (a and b) { +LL + if (a && b) { + | error: `or` is not a logical operator --> $DIR/issue-54109-and_instead_of_ampersands.rs:38:11 @@ -67,8 +72,9 @@ LL | if (a or b) { = note: unlike in e.g., Python and PHP, `&&` and `||` are used for logical operators help: use `||` to perform logical disjunction | -LL | if (a || b) { - | ~~ +LL - if (a or b) { +LL + if (a || b) { + | error: `and` is not a logical operator --> $DIR/issue-54109-and_instead_of_ampersands.rs:46:13 @@ -79,8 +85,9 @@ LL | while a and b { = note: unlike in e.g., Python and PHP, `&&` and `||` are used for logical operators help: use `&&` to perform logical conjunction | -LL | while a && b { - | ~~ +LL - while a and b { +LL + while a && b { + | error: `or` is not a logical operator --> $DIR/issue-54109-and_instead_of_ampersands.rs:54:13 @@ -91,8 +98,9 @@ LL | while a or b { = note: unlike in e.g., Python and PHP, `&&` and `||` are used for logical operators help: use `||` to perform logical disjunction | -LL | while a || b { - | ~~ +LL - while a or b { +LL + while a || b { + | error[E0308]: mismatched types --> $DIR/issue-54109-and_instead_of_ampersands.rs:13:33 diff --git a/tests/ui/did_you_mean/issue-54109-without-witness.stderr b/tests/ui/did_you_mean/issue-54109-without-witness.stderr index ee6d9901fcf60..1ad88f0084c89 100644 --- a/tests/ui/did_you_mean/issue-54109-without-witness.stderr +++ b/tests/ui/did_you_mean/issue-54109-without-witness.stderr @@ -7,8 +7,9 @@ LL | let _ = a and b; = note: unlike in e.g., Python and PHP, `&&` and `||` are used for logical operators help: use `&&` to perform logical conjunction | -LL | let _ = a && b; - | ~~ +LL - let _ = a and b; +LL + let _ = a && b; + | error: `and` is not a logical operator --> $DIR/issue-54109-without-witness.rs:15:10 @@ -19,8 +20,9 @@ LL | if a and b { = note: unlike in e.g., Python and PHP, `&&` and `||` are used for logical operators help: use `&&` to perform logical conjunction | -LL | if a && b { - | ~~ +LL - if a and b { +LL + if a && b { + | error: `or` is not a logical operator --> $DIR/issue-54109-without-witness.rs:24:15 @@ -31,8 +33,9 @@ LL | let _ = a or b; = note: unlike in e.g., Python and PHP, `&&` and `||` are used for logical operators help: use `||` to perform logical disjunction | -LL | let _ = a || b; - | ~~ +LL - let _ = a or b; +LL + let _ = a || b; + | error: `or` is not a logical operator --> $DIR/issue-54109-without-witness.rs:26:10 @@ -43,8 +46,9 @@ LL | if a or b { = note: unlike in e.g., Python and PHP, `&&` and `||` are used for logical operators help: use `||` to perform logical disjunction | -LL | if a || b { - | ~~ +LL - if a or b { +LL + if a || b { + | error: `and` is not a logical operator --> $DIR/issue-54109-without-witness.rs:34:11 @@ -55,8 +59,9 @@ LL | if (a and b) { = note: unlike in e.g., Python and PHP, `&&` and `||` are used for logical operators help: use `&&` to perform logical conjunction | -LL | if (a && b) { - | ~~ +LL - if (a and b) { +LL + if (a && b) { + | error: `or` is not a logical operator --> $DIR/issue-54109-without-witness.rs:42:11 @@ -67,8 +72,9 @@ LL | if (a or b) { = note: unlike in e.g., Python and PHP, `&&` and `||` are used for logical operators help: use `||` to perform logical disjunction | -LL | if (a || b) { - | ~~ +LL - if (a or b) { +LL + if (a || b) { + | error: `and` is not a logical operator --> $DIR/issue-54109-without-witness.rs:50:13 @@ -79,8 +85,9 @@ LL | while a and b { = note: unlike in e.g., Python and PHP, `&&` and `||` are used for logical operators help: use `&&` to perform logical conjunction | -LL | while a && b { - | ~~ +LL - while a and b { +LL + while a && b { + | error: `or` is not a logical operator --> $DIR/issue-54109-without-witness.rs:58:13 @@ -91,8 +98,9 @@ LL | while a or b { = note: unlike in e.g., Python and PHP, `&&` and `||` are used for logical operators help: use `||` to perform logical disjunction | -LL | while a || b { - | ~~ +LL - while a or b { +LL + while a || b { + | error: aborting due to 8 previous errors diff --git a/tests/ui/did_you_mean/issue-56028-there-is-an-enum-variant.stderr b/tests/ui/did_you_mean/issue-56028-there-is-an-enum-variant.stderr index b1a15b8594a0a..927f9e842e665 100644 --- a/tests/ui/did_you_mean/issue-56028-there-is-an-enum-variant.stderr +++ b/tests/ui/did_you_mean/issue-56028-there-is-an-enum-variant.stderr @@ -6,14 +6,18 @@ LL | fn setup() -> Set { Set } | help: there is an enum variant `AffixHeart::Set` and 7 others; try using the variant's enum | -LL | fn setup() -> AffixHeart { Set } - | ~~~~~~~~~~ -LL | fn setup() -> CauseToBe { Set } - | ~~~~~~~~~ -LL | fn setup() -> Determine { Set } - | ~~~~~~~~~ -LL | fn setup() -> PutDown { Set } - | ~~~~~~~ +LL - fn setup() -> Set { Set } +LL + fn setup() -> AffixHeart { Set } + | +LL - fn setup() -> Set { Set } +LL + fn setup() -> CauseToBe { Set } + | +LL - fn setup() -> Set { Set } +LL + fn setup() -> Determine { Set } + | +LL - fn setup() -> Set { Set } +LL + fn setup() -> PutDown { Set } + | and 3 other candidates error[E0425]: cannot find value `Set` in this scope diff --git a/tests/ui/did_you_mean/issue-87830-try-brackets-for-arrays.stderr b/tests/ui/did_you_mean/issue-87830-try-brackets-for-arrays.stderr index d5ad1a72b8250..58232e2307d8e 100644 --- a/tests/ui/did_you_mean/issue-87830-try-brackets-for-arrays.stderr +++ b/tests/ui/did_you_mean/issue-87830-try-brackets-for-arrays.stderr @@ -24,8 +24,9 @@ LL | const BAR: [&str; 3] = {"one", "two", "three"}; | help: to make an array, use square brackets instead of curly braces | -LL | const BAR: [&str; 3] = ["one", "two", "three"]; - | ~ ~ +LL - const BAR: [&str; 3] = {"one", "two", "three"}; +LL + const BAR: [&str; 3] = ["one", "two", "three"]; + | error: this is a block expression, not an array --> $DIR/issue-87830-try-brackets-for-arrays.rs:12:5 @@ -35,8 +36,9 @@ LL | {1, 2, 3}; | help: to make an array, use square brackets instead of curly braces | -LL | [1, 2, 3]; - | ~ ~ +LL - {1, 2, 3}; +LL + [1, 2, 3]; + | error: expected one of `.`, `;`, `?`, `}`, or an operator, found `,` --> $DIR/issue-87830-try-brackets-for-arrays.rs:17:6 diff --git a/tests/ui/did_you_mean/pub-macro-rules.stderr b/tests/ui/did_you_mean/pub-macro-rules.stderr index fb9148748ca1e..a91d419d96bc4 100644 --- a/tests/ui/did_you_mean/pub-macro-rules.stderr +++ b/tests/ui/did_you_mean/pub-macro-rules.stderr @@ -6,8 +6,9 @@ LL | pub macro_rules! foo { | help: try exporting the macro | -LL | #[macro_export] macro_rules! foo { - | ~~~~~~~~~~~~~~~ +LL - pub macro_rules! foo { +LL + #[macro_export] macro_rules! foo { + | error: aborting due to 1 previous error diff --git a/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.stderr b/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.stderr index 96742f8bf47cf..6c24a5899eae4 100644 --- a/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.stderr +++ b/tests/ui/did_you_mean/replace-impl-infer-ty-from-trait.stderr @@ -10,8 +10,9 @@ LL | fn bar(i: _, t: _, s: _) -> _ { | help: try replacing `_` with the types in the corresponding trait method signature | -LL | fn bar(i: i32, t: usize, s: &()) -> (usize, i32) { - | ~~~ ~~~~~ ~~~ ~~~~~~~~~~~~ +LL - fn bar(i: _, t: _, s: _) -> _ { +LL + fn bar(i: i32, t: usize, s: &()) -> (usize, i32) { + | error[E0282]: type annotations needed --> $DIR/replace-impl-infer-ty-from-trait.rs:9:15 diff --git a/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.rs b/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.rs index c9a097d3610a1..9cd32ffeb6d0b 100644 --- a/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.rs +++ b/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.rs @@ -2,6 +2,6 @@ fn main() { let _: &Copy + 'static; //~ ERROR expected a path - //~^ ERROR cannot be made into an object + //~^ ERROR is not dyn compatible let _: &'static Copy + 'static; //~ ERROR expected a path } diff --git a/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr b/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr index 8ef0d17844449..4fee6cc9a2227 100644 --- a/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr +++ b/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr @@ -20,14 +20,15 @@ help: try adding parentheses LL | let _: &'static (Copy + 'static); | + + -error[E0038]: the trait `Copy` cannot be made into an object +error[E0038]: the trait `Copy` is not dyn compatible --> $DIR/trait-object-reference-without-parens-suggestion.rs:4:12 | LL | let _: &Copy + 'static; - | ^^^^^ `Copy` cannot be made into an object + | ^^^^^ `Copy` is not dyn compatible | - = note: the trait cannot be made into an object because it requires `Self: Sized` - = note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + = note: the trait is not dyn compatible because it requires `Self: Sized` + = note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit error: aborting due to 3 previous errors diff --git a/tests/ui/did_you_mean/use_instead_of_import.stderr b/tests/ui/did_you_mean/use_instead_of_import.stderr index f8d6de8a11768..aadd7524ec67f 100644 --- a/tests/ui/did_you_mean/use_instead_of_import.stderr +++ b/tests/ui/did_you_mean/use_instead_of_import.stderr @@ -6,8 +6,9 @@ LL | import std::{ | help: items are imported using the `use` keyword | -LL | use std::{ - | ~~~ +LL - import std::{ +LL + use std::{ + | error: expected item, found `require` --> $DIR/use_instead_of_import.rs:9:1 @@ -17,8 +18,9 @@ LL | require std::time::Duration; | help: items are imported using the `use` keyword | -LL | use std::time::Duration; - | ~~~ +LL - require std::time::Duration; +LL + use std::time::Duration; + | error: expected item, found `include` --> $DIR/use_instead_of_import.rs:12:1 @@ -28,8 +30,9 @@ LL | include std::time::Instant; | help: items are imported using the `use` keyword | -LL | use std::time::Instant; - | ~~~ +LL - include std::time::Instant; +LL + use std::time::Instant; + | error: expected item, found `using` --> $DIR/use_instead_of_import.rs:15:5 @@ -39,8 +42,9 @@ LL | pub using std::io; | help: items are imported using the `use` keyword | -LL | pub use std::io; - | ~~~ +LL - pub using std::io; +LL + pub use std::io; + | error: aborting due to 4 previous errors diff --git a/tests/ui/drop/drop-order-comparisons.e2021.fixed b/tests/ui/drop/drop-order-comparisons.e2021.fixed new file mode 100644 index 0000000000000..78cf421cfbf34 --- /dev/null +++ b/tests/ui/drop/drop-order-comparisons.e2021.fixed @@ -0,0 +1,575 @@ +// This tests various aspects of the drop order with a focus on: +// +// - The lifetime of temporaries with the `if let` construct (and with +// various similar constructs) and how these lifetimes were shortened +// for `if let` in Rust 2024. +// +// - The shortening of the lifetimes of temporaries in tail +// expressions in Rust 2024. +// +// - The behavior of `let` chains and how this behavior compares to +// nested `if let` expressions and chained `let .. else` statements. +// +// In the tests below, `Events` tracks a sequence of numbered events. +// Calling `e.mark(..)` logs a numbered event immediately. Calling +// `e.ok(..)` or `e.err(..)` returns an `Ok(_)` or `Err(_)` value, +// respectively, and logs the numbered event when that value is +// dropped. Calling `e.assert()` verifies that the correct number of +// events were logged and that they were logged in the correct order. + +//@ revisions: e2021 e2024 +//@ [e2021] edition: 2021 +//@ [e2021] run-rustfix +//@ [e2021] rustfix-only-machine-applicable +//@ [e2024] edition: 2024 +//@ run-pass + +#![feature(let_chains)] +#![cfg_attr(e2021, warn(rust_2024_compatibility))] + +fn t_bindings() { + let e = Events::new(); + _ = { + e.mark(1); + let _v = e.ok(8); + let _v = e.ok(2).is_ok(); + let _ = e.ok(3); + let Ok(_) = e.ok(4) else { unreachable!() }; + let Ok(_) = e.ok(5).as_ref() else { unreachable!() }; + let _v = e.ok(7); + e.mark(6); + }; + e.assert(8); +} + +fn t_tuples() { + let e = Events::new(); + _ = (e.ok(1), e.ok(4).is_ok(), e.ok(2), e.ok(3).is_ok()); + e.assert(4); +} + +fn t_arrays() { + let e = Events::new(); + trait Tr {} + impl Tr for T {} + fn b<'a, T: 'a>(x: T) -> Box { + Box::new(x) + } + _ = [b(e.ok(1)), b(e.ok(4).is_ok()), b(e.ok(2)), b(e.ok(3).is_ok())]; + e.assert(4); +} + +fn t_fncalls() { + let e = Events::new(); + let f = |_, _, _, _| {}; + _ = f(e.ok(2), e.ok(4).is_ok(), e.ok(1), e.ok(3).is_ok()); + e.assert(4); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_tailexpr_bindings() { + let e = Events::new(); + _ = ({ + let _v = e.ok(2); + let _v = e.ok(1); + e.ok(5).is_ok() + //[e2021]~^ WARN relative drop order changing in Rust 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + }, e.mark(3), e.ok(4)); + e.assert(5); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_tailexpr_bindings() { + let e = Events::new(); + _ = ({ + let _v = e.ok(3); + let _v = e.ok(2); + e.ok(1).is_ok() + }, e.mark(4), e.ok(5)); + e.assert(5); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_tailexpr_tuples() { + let e = Events::new(); + _ = ({ + (e.ok(2), e.ok(6).is_ok(), e.ok(3), e.ok(5).is_ok()) + //[e2021]~^ WARN relative drop order changing in Rust 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + //[e2021]~| WARN relative drop order changing in Rust 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + }, e.mark(1), e.ok(4)); + e.assert(6); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_tailexpr_tuples() { + let e = Events::new(); + _ = ({ + (e.ok(4), e.ok(2).is_ok(), e.ok(5), e.ok(1).is_ok()) + }, e.mark(3), e.ok(6)); + e.assert(6); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_then() { + let e = Events::new(); + _ = (match e.ok(4).as_ref() { Ok(_) => { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + e.mark(1); + } _ => {}}, e.mark(2), e.ok(3)); + e.assert(4); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_then() { + let e = Events::new(); + _ = (if let Ok(_) = e.ok(2).as_ref() { + e.mark(1); + }, e.mark(3), e.ok(4)); + e.assert(4); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_else() { + let e = Events::new(); + _ = (match e.err(4).as_ref() { Ok(_) => {} _ => { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + e.mark(1); + }}, e.mark(2), e.ok(3)); + e.assert(4); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_else() { + let e = Events::new(); + _ = (if let Ok(_) = e.err(1).as_ref() {} else { + e.mark(2); + }, e.mark(3), e.ok(4)); + e.assert(4); +} + +#[rustfmt::skip] +fn t_match_then() { + let e = Events::new(); + _ = (match e.ok(4).as_ref() { + Ok(_) => e.mark(1), + _ => unreachable!(), + }, e.mark(2), e.ok(3)); + e.assert(4); +} + +#[rustfmt::skip] +fn t_match_else() { + let e = Events::new(); + _ = (match e.err(4).as_ref() { + Ok(_) => unreachable!(), + _ => e.mark(1), + }, e.mark(2), e.ok(3)); + e.assert(4); +} + +#[rustfmt::skip] +fn t_let_else_then() { + let e = Events::new(); + _ = ('top: { + 'chain: { + let Ok(_) = e.ok(1).as_ref() else { break 'chain }; + // The "then" branch: + e.mark(2); + break 'top; + } + // The "else" branch: + unreachable!() + }, e.mark(3), e.ok(4)); + e.assert(4); +} + +#[rustfmt::skip] +fn t_let_else_else() { + let e = Events::new(); + _ = ('top: { + 'chain: { + let Ok(_) = e.err(1).as_ref() else { break 'chain }; + // The "then" branch: + unreachable!(); + #[allow(unreachable_code)] + break 'top; + } + // The "else" branch: + e.mark(2); + }, e.mark(3), e.ok(4)); + e.assert(4); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_then_tailexpr() { + let e = Events::new(); + _ = ({ + if let Ok(_) = e.ok(4).as_ref() { + //[e2021]~^ WARN relative drop order changing in Rust 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + e.mark(1); + } + }, e.mark(2), e.ok(3)); + e.assert(4); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_then_tailexpr() { + let e = Events::new(); + _ = ({ + if let Ok(_) = e.ok(2).as_ref() { + e.mark(1); + } + }, e.mark(3), e.ok(4)); + e.assert(4); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_else_tailexpr() { + let e = Events::new(); + _ = ({ + match e.err(4).as_ref() { Ok(_) => {} _ => { + //[e2021]~^ WARN relative drop order changing in Rust 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + //[e2021]~| WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + e.mark(1); + }} + }, e.mark(2), e.ok(3)); + e.assert(4); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_else_tailexpr() { + let e = Events::new(); + _ = ({ + if let Ok(_) = e.err(1).as_ref() {} else { + e.mark(2); + } + }, e.mark(3), e.ok(4)); + e.assert(4); +} + +#[rustfmt::skip] +fn t_if_let_nested_then() { + let e = Events::new(); + _ = { + // The unusual formatting, here and below, is to make the + // comparison with `let` chains more direct. + if e.ok(1).is_ok() { + if let true = e.ok(9).is_ok() { + if let Ok(_v) = e.ok(8) { + if let Ok(_) = e.ok(7) { + if let Ok(_) = e.ok(6).as_ref() { + if e.ok(2).is_ok() { + if let Ok(_v) = e.ok(5) { + if let Ok(_) = e.ok(4).as_ref() { + e.mark(3); + }}}}}}}} + }; + e.assert(9); +} + +#[rustfmt::skip] +fn t_let_else_chained_then() { + let e = Events::new(); + _ = 'top: { + 'chain: { + if e.ok(1).is_ok() {} else { break 'chain }; + let true = e.ok(2).is_ok() else { break 'chain }; + let Ok(_v) = e.ok(9) else { break 'chain }; + let Ok(_) = e.ok(3) else { break 'chain }; + let Ok(_) = e.ok(4).as_ref() else { break 'chain }; + if e.ok(5).is_ok() {} else { break 'chain }; + let Ok(_v) = e.ok(8) else { break 'chain }; + let Ok(_) = e.ok(6).as_ref() else { break 'chain }; + // The "then" branch: + e.mark(7); + break 'top; + } + // The "else" branch: + unreachable!() + }; + e.assert(9); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_chains_then() { + let e = Events::new(); + _ = if e.ok(1).is_ok() + && let true = e.ok(9).is_ok() + && let Ok(_v) = e.ok(5) + && let Ok(_) = e.ok(8) + && let Ok(_) = e.ok(7).as_ref() + && e.ok(2).is_ok() + && let Ok(_v) = e.ok(4) + && let Ok(_) = e.ok(6).as_ref() { + e.mark(3); + }; + e.assert(9); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_chains_then() { + let e = Events::new(); + _ = if e.ok(1).is_ok() + && let true = e.ok(9).is_ok() + && let Ok(_v) = e.ok(8) + && let Ok(_) = e.ok(7) + && let Ok(_) = e.ok(6).as_ref() + && e.ok(2).is_ok() + && let Ok(_v) = e.ok(5) + && let Ok(_) = e.ok(4).as_ref() { + e.mark(3); + }; + e.assert(9); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_nested_else() { + let e = Events::new(); + _ = if e.err(1).is_ok() {} else { + match e.err(9).is_ok() { true => {} _ => { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + match e.err(8) { Ok(_v) => {} _ => { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + match e.err(7) { Ok(_) => {} _ => { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + match e.err(6).as_ref() { Ok(_) => {} _ => { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + if e.err(2).is_ok() {} else { + match e.err(5) { Ok(_v) => {} _ => { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + match e.err(4) { Ok(_) => {} _ => { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + e.mark(3); + }}}}}}}}}}}}}}; + e.assert(9); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_nested_else() { + let e = Events::new(); + _ = if e.err(1).is_ok() {} else { + if let true = e.err(2).is_ok() {} else { + if let Ok(_v) = e.err(3) {} else { + if let Ok(_) = e.err(4) {} else { + if let Ok(_) = e.err(5).as_ref() {} else { + if e.err(6).is_ok() {} else { + if let Ok(_v) = e.err(7) {} else { + if let Ok(_) = e.err(8) {} else { + e.mark(9); + }}}}}}}}; + e.assert(9); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_nested_then_else() { + let e = Events::new(); + _ = if e.ok(1).is_ok() { + if let true = e.ok(9).is_ok() { + if let Ok(_v) = e.ok(8) { + if let Ok(_) = e.ok(7) { + if let Ok(_) = e.ok(6).as_ref() { + if e.ok(2).is_ok() { + if let Ok(_v) = e.ok(5) { + match e.err(4).as_ref() { Ok(_) => {} _ => { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + e.mark(3); + }}}}}}}}}; + e.assert(9); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_nested_then_else() { + let e = Events::new(); + _ = if e.ok(1).is_ok() { + if let true = e.ok(9).is_ok() { + if let Ok(_v) = e.ok(8) { + if let Ok(_) = e.ok(7) { + if let Ok(_) = e.ok(6).as_ref() { + if e.ok(2).is_ok() { + if let Ok(_v) = e.ok(5) { + if let Ok(_) = e.err(3).as_ref() {} else { + e.mark(4); + }}}}}}}}; + e.assert(9); +} + +#[rustfmt::skip] +fn t_let_else_chained_then_else() { + let e = Events::new(); + _ = 'top: { + 'chain: { + if e.ok(1).is_ok() {} else { break 'chain }; + let true = e.ok(2).is_ok() else { break 'chain }; + let Ok(_v) = e.ok(8) else { break 'chain }; + let Ok(_) = e.ok(3) else { break 'chain }; + let Ok(_) = e.ok(4).as_ref() else { break 'chain }; + if e.ok(5).is_ok() {} else { break 'chain }; + let Ok(_v) = e.ok(7) else { break 'chain }; + let Ok(_) = e.err(6).as_ref() else { break 'chain }; + // The "then" branch: + unreachable!(); + #[allow(unreachable_code)] + break 'top; + } + // The "else" branch: + e.mark(9); + }; + e.assert(9); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_chains_then_else() { + let e = Events::new(); + _ = if e.ok(1).is_ok() + && let true = e.ok(9).is_ok() + && let Ok(_v) = e.ok(4) + && let Ok(_) = e.ok(8) + && let Ok(_) = e.ok(7).as_ref() + && e.ok(2).is_ok() + && let Ok(_v) = e.ok(3) + && let Ok(_) = e.err(6) {} else { + e.mark(5); + }; + e.assert(9); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_chains_then_else() { + let e = Events::new(); + _ = if e.ok(1).is_ok() + && let true = e.ok(8).is_ok() + && let Ok(_v) = e.ok(7) + && let Ok(_) = e.ok(6) + && let Ok(_) = e.ok(5).as_ref() + && e.ok(2).is_ok() + && let Ok(_v) = e.ok(4) + && let Ok(_) = e.err(3) {} else { + e.mark(9); + }; + e.assert(9); +} + +fn main() { + t_bindings(); + t_tuples(); + t_arrays(); + t_fncalls(); + t_tailexpr_bindings(); + t_tailexpr_tuples(); + t_if_let_then(); + t_if_let_else(); + t_match_then(); + t_match_else(); + t_let_else_then(); + t_let_else_else(); + t_if_let_then_tailexpr(); + t_if_let_else_tailexpr(); + t_if_let_nested_then(); + t_let_else_chained_then(); + t_if_let_chains_then(); + t_if_let_nested_else(); + t_if_let_nested_then_else(); + t_let_else_chained_then_else(); + t_if_let_chains_then_else(); +} + +// # Test scaffolding + +use core::cell::RefCell; +use std::collections::HashSet; + +/// A buffer to track the order of events. +/// +/// First, numbered events are logged into this buffer. +/// +/// Then, `assert` is called to verify that the correct number of +/// events were logged, and that they were logged in the expected +/// order. +struct Events(RefCell>>); + +impl Events { + const fn new() -> Self { + Self(RefCell::new(Some(Vec::new()))) + } + #[track_caller] + fn assert(&self, max: u64) { + let buf = &self.0; + let v1 = buf.borrow().as_ref().unwrap().clone(); + let mut v2 = buf.borrow().as_ref().unwrap().clone(); + *buf.borrow_mut() = None; + v2.sort(); + let uniq_len = v2.iter().collect::>().len(); + // Check that the sequence is sorted. + assert_eq!(v1, v2); + // Check that there are no duplicates. + assert_eq!(v2.len(), uniq_len); + // Check that the length is the expected one. + assert_eq!(max, uniq_len as u64); + // Check that the last marker is the expected one. + assert_eq!(v2.last().unwrap(), &max); + } + /// Return an `Ok` value that logs its drop. + fn ok(&self, m: u64) -> Result, LogDrop<'_>> { + Ok(LogDrop(self, m)) + } + /// Return an `Err` value that logs its drop. + fn err(&self, m: u64) -> Result { + Err(LogDrop(self, m)) + } + /// Log an event. + fn mark(&self, m: u64) { + self.0.borrow_mut().as_mut().unwrap().push(m); + } +} + +impl Drop for Events { + fn drop(&mut self) { + if self.0.borrow().is_some() { + panic!("failed to call `Events::assert()`"); + } + } +} + +/// A type that logs its drop events. +struct LogDrop<'b>(&'b Events, u64); + +impl<'b> Drop for LogDrop<'b> { + fn drop(&mut self) { + self.0.mark(self.1); + } +} diff --git a/tests/ui/drop/drop-order-comparisons.e2021.stderr b/tests/ui/drop/drop-order-comparisons.e2021.stderr new file mode 100644 index 0000000000000..158d18f68826b --- /dev/null +++ b/tests/ui/drop/drop-order-comparisons.e2021.stderr @@ -0,0 +1,477 @@ +warning: relative drop order changing in Rust 2024 + --> $DIR/drop-order-comparisons.rs:76:9 + | +LL | _ = ({ + | _________- +LL | | let _v = e.ok(2); + | | -- + | | | + | | `_v` calls a custom destructor + | | `_v` will be dropped later as of Edition 2024 +LL | | let _v = e.ok(1); + | | -- + | | | + | | this value will be stored in a temporary; let us call it `#2` + | | `#2` will be dropped later as of Edition 2024 +LL | | e.ok(5).is_ok() + | | ^^^^^^^ + | | | + | | this value will be stored in a temporary; let us call it `#3` + | | up until Edition 2021 `#3` is dropped last but will be dropped earlier in Edition 2024 +... | +LL | | }, e.mark(3), e.ok(4)); + | | - + | | | + | | now the temporary value is dropped here, before the local variables in the block or statement + | |__________________________this value will be stored in a temporary; let us call it `#1` + | `#1` will be dropped later as of Edition 2024 + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: `#3` invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ +note: `#1` invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ +note: `_v` invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ +note: `#2` invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ + = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages +note: the lint level is defined here + --> $DIR/drop-order-comparisons.rs:28:25 + | +LL | #![cfg_attr(e2021, warn(rust_2024_compatibility))] + | ^^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(tail_expr_drop_order)]` implied by `#[warn(rust_2024_compatibility)]` + +warning: relative drop order changing in Rust 2024 + --> $DIR/drop-order-comparisons.rs:100:45 + | +LL | _ = ({ + | _________- +LL | | (e.ok(2), e.ok(6).is_ok(), e.ok(3), e.ok(5).is_ok()) + | | ^^^^^^^ + | | | + | | this value will be stored in a temporary; let us call it `#2` + | | up until Edition 2021 `#2` is dropped last but will be dropped earlier in Edition 2024 +... | +LL | | }, e.mark(1), e.ok(4)); + | | - + | | | + | | now the temporary value is dropped here, before the local variables in the block or statement + | |__________________________this value will be stored in a temporary; let us call it `#1` + | `#1` will be dropped later as of Edition 2024 + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: `#2` invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ +note: `#1` invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ + = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages + +warning: relative drop order changing in Rust 2024 + --> $DIR/drop-order-comparisons.rs:100:19 + | +LL | _ = ({ + | _________- +LL | | (e.ok(2), e.ok(6).is_ok(), e.ok(3), e.ok(5).is_ok()) + | | ^^^^^^^ + | | | + | | this value will be stored in a temporary; let us call it `#2` + | | up until Edition 2021 `#2` is dropped last but will be dropped earlier in Edition 2024 +... | +LL | | }, e.mark(1), e.ok(4)); + | | - + | | | + | | now the temporary value is dropped here, before the local variables in the block or statement + | |__________________________this value will be stored in a temporary; let us call it `#1` + | `#1` will be dropped later as of Edition 2024 + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: `#2` invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ +note: `#1` invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ + = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages + +warning: relative drop order changing in Rust 2024 + --> $DIR/drop-order-comparisons.rs:221:24 + | +LL | _ = ({ + | _________- +LL | | if let Ok(_) = e.ok(4).as_ref() { + | | ^^^^^^^ + | | | + | | this value will be stored in a temporary; let us call it `#2` + | | up until Edition 2021 `#2` is dropped last but will be dropped earlier in Edition 2024 +... | +LL | | }, e.mark(2), e.ok(3)); + | | - + | | | + | | now the temporary value is dropped here, before the local variables in the block or statement + | |__________________________this value will be stored in a temporary; let us call it `#1` + | `#1` will be dropped later as of Edition 2024 + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: `#2` invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ +note: `#1` invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ + = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages + +warning: relative drop order changing in Rust 2024 + --> $DIR/drop-order-comparisons.rs:247:24 + | +LL | _ = ({ + | _________- +LL | | if let Ok(_) = e.err(4).as_ref() {} else { + | | ^^^^^^^^ + | | | + | | this value will be stored in a temporary; let us call it `#2` + | | up until Edition 2021 `#2` is dropped last but will be dropped earlier in Edition 2024 +... | +LL | | }, e.mark(2), e.ok(3)); + | | - + | | | + | | now the temporary value is dropped here, before the local variables in the block or statement + | |__________________________this value will be stored in a temporary; let us call it `#1` + | `#1` will be dropped later as of Edition 2024 + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: `#2` invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ +note: `#1` invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ + = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages + +warning: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/drop-order-comparisons.rs:123:13 + | +LL | _ = (if let Ok(_) = e.ok(4).as_ref() { + | ^^^^^^^^^^^^-------^^^^^^^^^ + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +help: the value is now dropped here in Edition 2024 + --> $DIR/drop-order-comparisons.rs:127:5 + | +LL | }, e.mark(2), e.ok(3)); + | ^ + = note: `#[warn(if_let_rescope)]` implied by `#[warn(rust_2024_compatibility)]` +help: a `match` with a single arm can preserve the drop order up to Edition 2021 + | +LL ~ _ = (match e.ok(4).as_ref() { Ok(_) => { +LL | +LL | +LL | e.mark(1); +LL ~ } _ => {}}, e.mark(2), e.ok(3)); + | + +warning: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/drop-order-comparisons.rs:145:13 + | +LL | _ = (if let Ok(_) = e.err(4).as_ref() {} else { + | ^^^^^^^^^^^^--------^^^^^^^^^ + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +help: the value is now dropped here in Edition 2024 + --> $DIR/drop-order-comparisons.rs:145:44 + | +LL | _ = (if let Ok(_) = e.err(4).as_ref() {} else { + | ^ +help: a `match` with a single arm can preserve the drop order up to Edition 2021 + | +LL ~ _ = (match e.err(4).as_ref() { Ok(_) => {} _ => { +LL | +LL | +LL | e.mark(1); +LL ~ }}, e.mark(2), e.ok(3)); + | + +warning: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/drop-order-comparisons.rs:247:12 + | +LL | if let Ok(_) = e.err(4).as_ref() {} else { + | ^^^^^^^^^^^^--------^^^^^^^^^ + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +help: the value is now dropped here in Edition 2024 + --> $DIR/drop-order-comparisons.rs:247:43 + | +LL | if let Ok(_) = e.err(4).as_ref() {} else { + | ^ +help: a `match` with a single arm can preserve the drop order up to Edition 2021 + | +LL ~ match e.err(4).as_ref() { Ok(_) => {} _ => { +LL | +... +LL | e.mark(1); +LL ~ }} + | + +warning: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/drop-order-comparisons.rs:352:12 + | +LL | if let true = e.err(9).is_ok() {} else { + | ^^^^^^^^^^^--------^^^^^^^^ + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +help: the value is now dropped here in Edition 2024 + --> $DIR/drop-order-comparisons.rs:352:41 + | +LL | if let true = e.err(9).is_ok() {} else { + | ^ +help: a `match` with a single arm can preserve the drop order up to Edition 2021 + | +LL ~ match e.err(9).is_ok() { true => {} _ => { +LL | +... +LL | e.mark(3); +LL ~ }}}}}}}}}; + | + +warning: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/drop-order-comparisons.rs:355:12 + | +LL | if let Ok(_v) = e.err(8) {} else { + | ^^^^^^^^^^^^^-------- + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +help: the value is now dropped here in Edition 2024 + --> $DIR/drop-order-comparisons.rs:355:35 + | +LL | if let Ok(_v) = e.err(8) {} else { + | ^ +help: a `match` with a single arm can preserve the drop order up to Edition 2021 + | +LL ~ match e.err(8) { Ok(_v) => {} _ => { +LL | +... +LL | e.mark(3); +LL ~ }}}}}}}}}; + | + +warning: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/drop-order-comparisons.rs:358:12 + | +LL | if let Ok(_) = e.err(7) {} else { + | ^^^^^^^^^^^^-------- + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +help: the value is now dropped here in Edition 2024 + --> $DIR/drop-order-comparisons.rs:358:34 + | +LL | if let Ok(_) = e.err(7) {} else { + | ^ +help: a `match` with a single arm can preserve the drop order up to Edition 2021 + | +LL ~ match e.err(7) { Ok(_) => {} _ => { +LL | +... +LL | e.mark(3); +LL ~ }}}}}}}}}; + | + +warning: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/drop-order-comparisons.rs:361:12 + | +LL | if let Ok(_) = e.err(6).as_ref() {} else { + | ^^^^^^^^^^^^--------^^^^^^^^^ + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +help: the value is now dropped here in Edition 2024 + --> $DIR/drop-order-comparisons.rs:361:43 + | +LL | if let Ok(_) = e.err(6).as_ref() {} else { + | ^ +help: a `match` with a single arm can preserve the drop order up to Edition 2021 + | +LL ~ match e.err(6).as_ref() { Ok(_) => {} _ => { +LL | +... +LL | e.mark(3); +LL ~ }}}}}}}}}; + | + +warning: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/drop-order-comparisons.rs:365:12 + | +LL | if let Ok(_v) = e.err(5) {} else { + | ^^^^^^^^^^^^^-------- + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +help: the value is now dropped here in Edition 2024 + --> $DIR/drop-order-comparisons.rs:365:35 + | +LL | if let Ok(_v) = e.err(5) {} else { + | ^ +help: a `match` with a single arm can preserve the drop order up to Edition 2021 + | +LL ~ match e.err(5) { Ok(_v) => {} _ => { +LL | +... +LL | e.mark(3); +LL ~ }}}}}}}}}; + | + +warning: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/drop-order-comparisons.rs:368:12 + | +LL | if let Ok(_) = e.err(4) {} else { + | ^^^^^^^^^^^^-------- + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +help: the value is now dropped here in Edition 2024 + --> $DIR/drop-order-comparisons.rs:368:34 + | +LL | if let Ok(_) = e.err(4) {} else { + | ^ +help: a `match` with a single arm can preserve the drop order up to Edition 2021 + | +LL ~ match e.err(4) { Ok(_) => {} _ => { +LL | +LL | +LL | e.mark(3); +LL ~ }}}}}}}}}; + | + +warning: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/drop-order-comparisons.rs:404:12 + | +LL | if let Ok(_) = e.err(4).as_ref() {} else { + | ^^^^^^^^^^^^--------^^^^^^^^^ + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +help: the value is now dropped here in Edition 2024 + --> $DIR/drop-order-comparisons.rs:404:43 + | +LL | if let Ok(_) = e.err(4).as_ref() {} else { + | ^ +help: a `match` with a single arm can preserve the drop order up to Edition 2021 + | +LL ~ match e.err(4).as_ref() { Ok(_) => {} _ => { +LL | +LL | +LL | e.mark(3); +LL ~ }}}}}}}}}; + | + +warning: 15 warnings emitted + diff --git a/tests/ui/drop/drop-order-comparisons.rs b/tests/ui/drop/drop-order-comparisons.rs new file mode 100644 index 0000000000000..78c75a9449f17 --- /dev/null +++ b/tests/ui/drop/drop-order-comparisons.rs @@ -0,0 +1,575 @@ +// This tests various aspects of the drop order with a focus on: +// +// - The lifetime of temporaries with the `if let` construct (and with +// various similar constructs) and how these lifetimes were shortened +// for `if let` in Rust 2024. +// +// - The shortening of the lifetimes of temporaries in tail +// expressions in Rust 2024. +// +// - The behavior of `let` chains and how this behavior compares to +// nested `if let` expressions and chained `let .. else` statements. +// +// In the tests below, `Events` tracks a sequence of numbered events. +// Calling `e.mark(..)` logs a numbered event immediately. Calling +// `e.ok(..)` or `e.err(..)` returns an `Ok(_)` or `Err(_)` value, +// respectively, and logs the numbered event when that value is +// dropped. Calling `e.assert()` verifies that the correct number of +// events were logged and that they were logged in the correct order. + +//@ revisions: e2021 e2024 +//@ [e2021] edition: 2021 +//@ [e2021] run-rustfix +//@ [e2021] rustfix-only-machine-applicable +//@ [e2024] edition: 2024 +//@ run-pass + +#![feature(let_chains)] +#![cfg_attr(e2021, warn(rust_2024_compatibility))] + +fn t_bindings() { + let e = Events::new(); + _ = { + e.mark(1); + let _v = e.ok(8); + let _v = e.ok(2).is_ok(); + let _ = e.ok(3); + let Ok(_) = e.ok(4) else { unreachable!() }; + let Ok(_) = e.ok(5).as_ref() else { unreachable!() }; + let _v = e.ok(7); + e.mark(6); + }; + e.assert(8); +} + +fn t_tuples() { + let e = Events::new(); + _ = (e.ok(1), e.ok(4).is_ok(), e.ok(2), e.ok(3).is_ok()); + e.assert(4); +} + +fn t_arrays() { + let e = Events::new(); + trait Tr {} + impl Tr for T {} + fn b<'a, T: 'a>(x: T) -> Box { + Box::new(x) + } + _ = [b(e.ok(1)), b(e.ok(4).is_ok()), b(e.ok(2)), b(e.ok(3).is_ok())]; + e.assert(4); +} + +fn t_fncalls() { + let e = Events::new(); + let f = |_, _, _, _| {}; + _ = f(e.ok(2), e.ok(4).is_ok(), e.ok(1), e.ok(3).is_ok()); + e.assert(4); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_tailexpr_bindings() { + let e = Events::new(); + _ = ({ + let _v = e.ok(2); + let _v = e.ok(1); + e.ok(5).is_ok() + //[e2021]~^ WARN relative drop order changing in Rust 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + }, e.mark(3), e.ok(4)); + e.assert(5); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_tailexpr_bindings() { + let e = Events::new(); + _ = ({ + let _v = e.ok(3); + let _v = e.ok(2); + e.ok(1).is_ok() + }, e.mark(4), e.ok(5)); + e.assert(5); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_tailexpr_tuples() { + let e = Events::new(); + _ = ({ + (e.ok(2), e.ok(6).is_ok(), e.ok(3), e.ok(5).is_ok()) + //[e2021]~^ WARN relative drop order changing in Rust 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + //[e2021]~| WARN relative drop order changing in Rust 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + }, e.mark(1), e.ok(4)); + e.assert(6); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_tailexpr_tuples() { + let e = Events::new(); + _ = ({ + (e.ok(4), e.ok(2).is_ok(), e.ok(5), e.ok(1).is_ok()) + }, e.mark(3), e.ok(6)); + e.assert(6); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_then() { + let e = Events::new(); + _ = (if let Ok(_) = e.ok(4).as_ref() { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + e.mark(1); + }, e.mark(2), e.ok(3)); + e.assert(4); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_then() { + let e = Events::new(); + _ = (if let Ok(_) = e.ok(2).as_ref() { + e.mark(1); + }, e.mark(3), e.ok(4)); + e.assert(4); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_else() { + let e = Events::new(); + _ = (if let Ok(_) = e.err(4).as_ref() {} else { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + e.mark(1); + }, e.mark(2), e.ok(3)); + e.assert(4); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_else() { + let e = Events::new(); + _ = (if let Ok(_) = e.err(1).as_ref() {} else { + e.mark(2); + }, e.mark(3), e.ok(4)); + e.assert(4); +} + +#[rustfmt::skip] +fn t_match_then() { + let e = Events::new(); + _ = (match e.ok(4).as_ref() { + Ok(_) => e.mark(1), + _ => unreachable!(), + }, e.mark(2), e.ok(3)); + e.assert(4); +} + +#[rustfmt::skip] +fn t_match_else() { + let e = Events::new(); + _ = (match e.err(4).as_ref() { + Ok(_) => unreachable!(), + _ => e.mark(1), + }, e.mark(2), e.ok(3)); + e.assert(4); +} + +#[rustfmt::skip] +fn t_let_else_then() { + let e = Events::new(); + _ = ('top: { + 'chain: { + let Ok(_) = e.ok(1).as_ref() else { break 'chain }; + // The "then" branch: + e.mark(2); + break 'top; + } + // The "else" branch: + unreachable!() + }, e.mark(3), e.ok(4)); + e.assert(4); +} + +#[rustfmt::skip] +fn t_let_else_else() { + let e = Events::new(); + _ = ('top: { + 'chain: { + let Ok(_) = e.err(1).as_ref() else { break 'chain }; + // The "then" branch: + unreachable!(); + #[allow(unreachable_code)] + break 'top; + } + // The "else" branch: + e.mark(2); + }, e.mark(3), e.ok(4)); + e.assert(4); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_then_tailexpr() { + let e = Events::new(); + _ = ({ + if let Ok(_) = e.ok(4).as_ref() { + //[e2021]~^ WARN relative drop order changing in Rust 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + e.mark(1); + } + }, e.mark(2), e.ok(3)); + e.assert(4); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_then_tailexpr() { + let e = Events::new(); + _ = ({ + if let Ok(_) = e.ok(2).as_ref() { + e.mark(1); + } + }, e.mark(3), e.ok(4)); + e.assert(4); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_else_tailexpr() { + let e = Events::new(); + _ = ({ + if let Ok(_) = e.err(4).as_ref() {} else { + //[e2021]~^ WARN relative drop order changing in Rust 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + //[e2021]~| WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + e.mark(1); + } + }, e.mark(2), e.ok(3)); + e.assert(4); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_else_tailexpr() { + let e = Events::new(); + _ = ({ + if let Ok(_) = e.err(1).as_ref() {} else { + e.mark(2); + } + }, e.mark(3), e.ok(4)); + e.assert(4); +} + +#[rustfmt::skip] +fn t_if_let_nested_then() { + let e = Events::new(); + _ = { + // The unusual formatting, here and below, is to make the + // comparison with `let` chains more direct. + if e.ok(1).is_ok() { + if let true = e.ok(9).is_ok() { + if let Ok(_v) = e.ok(8) { + if let Ok(_) = e.ok(7) { + if let Ok(_) = e.ok(6).as_ref() { + if e.ok(2).is_ok() { + if let Ok(_v) = e.ok(5) { + if let Ok(_) = e.ok(4).as_ref() { + e.mark(3); + }}}}}}}} + }; + e.assert(9); +} + +#[rustfmt::skip] +fn t_let_else_chained_then() { + let e = Events::new(); + _ = 'top: { + 'chain: { + if e.ok(1).is_ok() {} else { break 'chain }; + let true = e.ok(2).is_ok() else { break 'chain }; + let Ok(_v) = e.ok(9) else { break 'chain }; + let Ok(_) = e.ok(3) else { break 'chain }; + let Ok(_) = e.ok(4).as_ref() else { break 'chain }; + if e.ok(5).is_ok() {} else { break 'chain }; + let Ok(_v) = e.ok(8) else { break 'chain }; + let Ok(_) = e.ok(6).as_ref() else { break 'chain }; + // The "then" branch: + e.mark(7); + break 'top; + } + // The "else" branch: + unreachable!() + }; + e.assert(9); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_chains_then() { + let e = Events::new(); + _ = if e.ok(1).is_ok() + && let true = e.ok(9).is_ok() + && let Ok(_v) = e.ok(5) + && let Ok(_) = e.ok(8) + && let Ok(_) = e.ok(7).as_ref() + && e.ok(2).is_ok() + && let Ok(_v) = e.ok(4) + && let Ok(_) = e.ok(6).as_ref() { + e.mark(3); + }; + e.assert(9); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_chains_then() { + let e = Events::new(); + _ = if e.ok(1).is_ok() + && let true = e.ok(9).is_ok() + && let Ok(_v) = e.ok(8) + && let Ok(_) = e.ok(7) + && let Ok(_) = e.ok(6).as_ref() + && e.ok(2).is_ok() + && let Ok(_v) = e.ok(5) + && let Ok(_) = e.ok(4).as_ref() { + e.mark(3); + }; + e.assert(9); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_nested_else() { + let e = Events::new(); + _ = if e.err(1).is_ok() {} else { + if let true = e.err(9).is_ok() {} else { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + if let Ok(_v) = e.err(8) {} else { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + if let Ok(_) = e.err(7) {} else { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + if let Ok(_) = e.err(6).as_ref() {} else { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + if e.err(2).is_ok() {} else { + if let Ok(_v) = e.err(5) {} else { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + if let Ok(_) = e.err(4) {} else { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + e.mark(3); + }}}}}}}}; + e.assert(9); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_nested_else() { + let e = Events::new(); + _ = if e.err(1).is_ok() {} else { + if let true = e.err(2).is_ok() {} else { + if let Ok(_v) = e.err(3) {} else { + if let Ok(_) = e.err(4) {} else { + if let Ok(_) = e.err(5).as_ref() {} else { + if e.err(6).is_ok() {} else { + if let Ok(_v) = e.err(7) {} else { + if let Ok(_) = e.err(8) {} else { + e.mark(9); + }}}}}}}}; + e.assert(9); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_nested_then_else() { + let e = Events::new(); + _ = if e.ok(1).is_ok() { + if let true = e.ok(9).is_ok() { + if let Ok(_v) = e.ok(8) { + if let Ok(_) = e.ok(7) { + if let Ok(_) = e.ok(6).as_ref() { + if e.ok(2).is_ok() { + if let Ok(_v) = e.ok(5) { + if let Ok(_) = e.err(4).as_ref() {} else { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + e.mark(3); + }}}}}}}}; + e.assert(9); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_nested_then_else() { + let e = Events::new(); + _ = if e.ok(1).is_ok() { + if let true = e.ok(9).is_ok() { + if let Ok(_v) = e.ok(8) { + if let Ok(_) = e.ok(7) { + if let Ok(_) = e.ok(6).as_ref() { + if e.ok(2).is_ok() { + if let Ok(_v) = e.ok(5) { + if let Ok(_) = e.err(3).as_ref() {} else { + e.mark(4); + }}}}}}}}; + e.assert(9); +} + +#[rustfmt::skip] +fn t_let_else_chained_then_else() { + let e = Events::new(); + _ = 'top: { + 'chain: { + if e.ok(1).is_ok() {} else { break 'chain }; + let true = e.ok(2).is_ok() else { break 'chain }; + let Ok(_v) = e.ok(8) else { break 'chain }; + let Ok(_) = e.ok(3) else { break 'chain }; + let Ok(_) = e.ok(4).as_ref() else { break 'chain }; + if e.ok(5).is_ok() {} else { break 'chain }; + let Ok(_v) = e.ok(7) else { break 'chain }; + let Ok(_) = e.err(6).as_ref() else { break 'chain }; + // The "then" branch: + unreachable!(); + #[allow(unreachable_code)] + break 'top; + } + // The "else" branch: + e.mark(9); + }; + e.assert(9); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_chains_then_else() { + let e = Events::new(); + _ = if e.ok(1).is_ok() + && let true = e.ok(9).is_ok() + && let Ok(_v) = e.ok(4) + && let Ok(_) = e.ok(8) + && let Ok(_) = e.ok(7).as_ref() + && e.ok(2).is_ok() + && let Ok(_v) = e.ok(3) + && let Ok(_) = e.err(6) {} else { + e.mark(5); + }; + e.assert(9); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_chains_then_else() { + let e = Events::new(); + _ = if e.ok(1).is_ok() + && let true = e.ok(8).is_ok() + && let Ok(_v) = e.ok(7) + && let Ok(_) = e.ok(6) + && let Ok(_) = e.ok(5).as_ref() + && e.ok(2).is_ok() + && let Ok(_v) = e.ok(4) + && let Ok(_) = e.err(3) {} else { + e.mark(9); + }; + e.assert(9); +} + +fn main() { + t_bindings(); + t_tuples(); + t_arrays(); + t_fncalls(); + t_tailexpr_bindings(); + t_tailexpr_tuples(); + t_if_let_then(); + t_if_let_else(); + t_match_then(); + t_match_else(); + t_let_else_then(); + t_let_else_else(); + t_if_let_then_tailexpr(); + t_if_let_else_tailexpr(); + t_if_let_nested_then(); + t_let_else_chained_then(); + t_if_let_chains_then(); + t_if_let_nested_else(); + t_if_let_nested_then_else(); + t_let_else_chained_then_else(); + t_if_let_chains_then_else(); +} + +// # Test scaffolding + +use core::cell::RefCell; +use std::collections::HashSet; + +/// A buffer to track the order of events. +/// +/// First, numbered events are logged into this buffer. +/// +/// Then, `assert` is called to verify that the correct number of +/// events were logged, and that they were logged in the expected +/// order. +struct Events(RefCell>>); + +impl Events { + const fn new() -> Self { + Self(RefCell::new(Some(Vec::new()))) + } + #[track_caller] + fn assert(&self, max: u64) { + let buf = &self.0; + let v1 = buf.borrow().as_ref().unwrap().clone(); + let mut v2 = buf.borrow().as_ref().unwrap().clone(); + *buf.borrow_mut() = None; + v2.sort(); + let uniq_len = v2.iter().collect::>().len(); + // Check that the sequence is sorted. + assert_eq!(v1, v2); + // Check that there are no duplicates. + assert_eq!(v2.len(), uniq_len); + // Check that the length is the expected one. + assert_eq!(max, uniq_len as u64); + // Check that the last marker is the expected one. + assert_eq!(v2.last().unwrap(), &max); + } + /// Return an `Ok` value that logs its drop. + fn ok(&self, m: u64) -> Result, LogDrop<'_>> { + Ok(LogDrop(self, m)) + } + /// Return an `Err` value that logs its drop. + fn err(&self, m: u64) -> Result { + Err(LogDrop(self, m)) + } + /// Log an event. + fn mark(&self, m: u64) { + self.0.borrow_mut().as_mut().unwrap().push(m); + } +} + +impl Drop for Events { + fn drop(&mut self) { + if self.0.borrow().is_some() { + panic!("failed to call `Events::assert()`"); + } + } +} + +/// A type that logs its drop events. +struct LogDrop<'b>(&'b Events, u64); + +impl<'b> Drop for LogDrop<'b> { + fn drop(&mut self) { + self.0.mark(self.1); + } +} diff --git a/tests/ui/drop/drop-trait-enum.rs b/tests/ui/drop/drop-trait-enum.rs index 91b5bcdf7301d..5a88d959ec6fa 100644 --- a/tests/ui/drop/drop-trait-enum.rs +++ b/tests/ui/drop/drop-trait-enum.rs @@ -2,7 +2,7 @@ #![allow(dead_code)] #![allow(unused_assignments)] #![allow(unused_variables)] -//@ ignore-emscripten no threads support +//@ needs-threads //@ needs-unwind use std::thread; diff --git a/tests/ui/drop/if-let-rescope-borrowck-suggestions.stderr b/tests/ui/drop/if-let-rescope-borrowck-suggestions.stderr index 3c87e196af6e5..b418ff4684daa 100644 --- a/tests/ui/drop/if-let-rescope-borrowck-suggestions.stderr +++ b/tests/ui/drop/if-let-rescope-borrowck-suggestions.stderr @@ -18,8 +18,9 @@ LL ~ do_something(if let Some(value) = binding.get_ref() { value } else { &0 | help: consider rewriting the `if` into `match` which preserves the extended lifetime | -LL | do_something({ match Droppy.get_ref() { Some(value) => { value } _ => { &0 }}}); - | ~~~~~~~ ++++++++++++++++ ~~~~ ++ +LL - do_something(if let Some(value) = Droppy.get_ref() { value } else { &0 }); +LL + do_something({ match Droppy.get_ref() { Some(value) => { value } _ => { &0 }}}); + | error[E0716]: temporary value dropped while borrowed --> $DIR/if-let-rescope-borrowck-suggestions.rs:23:39 diff --git a/tests/ui/drop/lint-if-let-rescope-false-positives.rs b/tests/ui/drop/lint-if-let-rescope-false-positives.rs new file mode 100644 index 0000000000000..77b7df4bc3bab --- /dev/null +++ b/tests/ui/drop/lint-if-let-rescope-false-positives.rs @@ -0,0 +1,33 @@ +//@ edition: 2021 +//@ check-pass + +#![deny(if_let_rescope)] + +struct Drop; +impl std::ops::Drop for Drop { + fn drop(&mut self) { + println!("drop") + } +} + +impl Drop { + fn as_ref(&self) -> Option { + Some(1) + } +} + +fn consume(_: impl Sized) -> Option { Some(1) } + +fn main() { + let drop = Drop; + + // Make sure we don't drop if we don't actually make a temporary. + if let None = drop.as_ref() {} else {} + + // Make sure we don't lint if we consume the droppy value. + if let None = consume(Drop) {} else {} + + // Make sure we don't lint on field exprs of place exprs. + let tup_place = (Drop, ()); + if let None = consume(tup_place.1) {} else {} +} diff --git a/tests/ui/drop/lint-if-let-rescope-with-macro.stderr b/tests/ui/drop/lint-if-let-rescope-with-macro.stderr index d73a878c74f41..029d5c7492936 100644 --- a/tests/ui/drop/lint-if-let-rescope-with-macro.stderr +++ b/tests/ui/drop/lint-if-let-rescope-with-macro.stderr @@ -2,7 +2,7 @@ error: `if let` assigns a shorter lifetime since Edition 2024 --> $DIR/lint-if-let-rescope-with-macro.rs:12:12 | LL | if let $p = $e { $($conseq)* } else { $($alt)* } - | ^^^ + | ^^^^^^^^^^^ ... LL | / edition_2021_if_let! { LL | | Some(_value), diff --git a/tests/ui/drop/lint-if-let-rescope.fixed b/tests/ui/drop/lint-if-let-rescope.fixed index 182190aa323bc..79858e6f225aa 100644 --- a/tests/ui/drop/lint-if-let-rescope.fixed +++ b/tests/ui/drop/lint-if-let-rescope.fixed @@ -94,6 +94,12 @@ fn main() { //~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021 } + match Some((droppy(), ()).1) { Some(_value) => {} _ => {}} + //~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024 + //~| WARN: this changes meaning in Rust 2024 + //~| HELP: the value is now dropped here in Edition 2024 + //~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021 + // We want to keep the `if let`s below as direct descendents of match arms, // so the formatting is suppressed. #[rustfmt::skip] diff --git a/tests/ui/drop/lint-if-let-rescope.rs b/tests/ui/drop/lint-if-let-rescope.rs index e1b38be0a0f50..9d873c65426f6 100644 --- a/tests/ui/drop/lint-if-let-rescope.rs +++ b/tests/ui/drop/lint-if-let-rescope.rs @@ -94,6 +94,12 @@ fn main() { //~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021 } + if let Some(_value) = Some((droppy(), ()).1) {} else {} + //~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024 + //~| WARN: this changes meaning in Rust 2024 + //~| HELP: the value is now dropped here in Edition 2024 + //~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021 + // We want to keep the `if let`s below as direct descendents of match arms, // so the formatting is suppressed. #[rustfmt::skip] diff --git a/tests/ui/drop/lint-if-let-rescope.stderr b/tests/ui/drop/lint-if-let-rescope.stderr index f6715dbae0508..b17239976cc0d 100644 --- a/tests/ui/drop/lint-if-let-rescope.stderr +++ b/tests/ui/drop/lint-if-let-rescope.stderr @@ -108,8 +108,9 @@ LL | if let Some(1) = { if let Some(_value) = Droppy.get() { Some(1) } else | ^ help: a `match` with a single arm can preserve the drop order up to Edition 2021 | -LL | if let Some(1) = { match Droppy.get() { Some(_value) => { Some(1) } _ => { None }} } { - | ~~~~~ +++++++++++++++++ ~~~~ + +LL - if let Some(1) = { if let Some(_value) = Droppy.get() { Some(1) } else { None } } { +LL + if let Some(1) = { match Droppy.get() { Some(_value) => { Some(1) } _ => { None }} } { + | error: `if let` assigns a shorter lifetime since Edition 2024 --> $DIR/lint-if-let-rescope.rs:72:12 @@ -128,8 +129,9 @@ LL | if (if let Some(_value) = droppy().get() { true } else { false }) { | ^ help: a `match` with a single arm can preserve the drop order up to Edition 2021 | -LL | if (match droppy().get() { Some(_value) => { true } _ => { false }}) { - | ~~~~~ +++++++++++++++++ ~~~~ + +LL - if (if let Some(_value) = droppy().get() { true } else { false }) { +LL + if (match droppy().get() { Some(_value) => { true } _ => { false }}) { + | error: `if let` assigns a shorter lifetime since Edition 2024 --> $DIR/lint-if-let-rescope.rs:78:21 @@ -148,8 +150,9 @@ LL | } else if (((if let Some(_value) = droppy().get() { true } else { false | ^ help: a `match` with a single arm can preserve the drop order up to Edition 2021 | -LL | } else if (((match droppy().get() { Some(_value) => { true } _ => { false }}))) { - | ~~~~~ +++++++++++++++++ ~~~~ + +LL - } else if (((if let Some(_value) = droppy().get() { true } else { false }))) { +LL + } else if (((match droppy().get() { Some(_value) => { true } _ => { false }}))) { + | error: `if let` assigns a shorter lifetime since Edition 2024 --> $DIR/lint-if-let-rescope.rs:90:15 @@ -168,8 +171,30 @@ LL | while (if let Some(_value) = droppy().get() { false } else { true }) { | ^ help: a `match` with a single arm can preserve the drop order up to Edition 2021 | -LL | while (match droppy().get() { Some(_value) => { false } _ => { true }}) { - | ~~~~~ +++++++++++++++++ ~~~~ + +LL - while (if let Some(_value) = droppy().get() { false } else { true }) { +LL + while (match droppy().get() { Some(_value) => { false } _ => { true }}) { + | + +error: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/lint-if-let-rescope.rs:97:8 + | +LL | if let Some(_value) = Some((droppy(), ()).1) {} else {} + | ^^^^^^^^^^^^^^^^^^^^^^^^--------------^^^ + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +help: the value is now dropped here in Edition 2024 + --> $DIR/lint-if-let-rescope.rs:97:51 + | +LL | if let Some(_value) = Some((droppy(), ()).1) {} else {} + | ^ +help: a `match` with a single arm can preserve the drop order up to Edition 2021 + | +LL - if let Some(_value) = Some((droppy(), ()).1) {} else {} +LL + match Some((droppy(), ()).1) { Some(_value) => {} _ => {}} + | -error: aborting due to 7 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/drop/lint-tail-expr-drop-order-borrowck.rs b/tests/ui/drop/lint-tail-expr-drop-order-borrowck.rs new file mode 100644 index 0000000000000..6f64d83f8a0c3 --- /dev/null +++ b/tests/ui/drop/lint-tail-expr-drop-order-borrowck.rs @@ -0,0 +1,51 @@ +// Edition 2024 lint for change in drop order at tail expression +// This lint is to capture potential borrow-checking errors +// due to implementation of RFC 3606 +//@ edition: 2021 + +#![deny(tail_expr_drop_order)] //~ NOTE: the lint level is defined here + +fn should_lint_with_potential_borrowck_err() { + let _ = { String::new().as_str() }.len(); + //~^ ERROR: relative drop order changing + //~| WARN: this changes meaning in Rust 2024 + //~| NOTE: this temporary value will be dropped at the end of the block + //~| borrow later used by call + //~| NOTE: for more information, see +} + +fn should_lint_with_unsafe_block() { + fn f(_: usize) {} + f(unsafe { String::new().as_str() }.len()); + //~^ ERROR: relative drop order changing + //~| WARN: this changes meaning in Rust 2024 + //~| NOTE: this temporary value will be dropped at the end of the block + //~| borrow later used by call + //~| NOTE: for more information, see +} + +#[rustfmt::skip] +fn should_lint_with_big_block() { + fn f(_: T) {} + f({ + &mut || 0 + //~^ ERROR: relative drop order changing + //~| WARN: this changes meaning in Rust 2024 + //~| NOTE: this temporary value will be dropped at the end of the block + //~| borrow later used here + //~| NOTE: for more information, see + }) +} + +fn another_temp_that_is_copy_in_arg() { + fn f() {} + fn g(_: &()) {} + g({ &f() }); + //~^ ERROR: relative drop order changing + //~| WARN: this changes meaning in Rust 2024 + //~| NOTE: this temporary value will be dropped at the end of the block + //~| borrow later used by call + //~| NOTE: for more information, see +} + +fn main() {} diff --git a/tests/ui/drop/lint-tail-expr-drop-order-borrowck.stderr b/tests/ui/drop/lint-tail-expr-drop-order-borrowck.stderr new file mode 100644 index 0000000000000..a55e366dd0be1 --- /dev/null +++ b/tests/ui/drop/lint-tail-expr-drop-order-borrowck.stderr @@ -0,0 +1,52 @@ +error: relative drop order changing in Rust 2024 + --> $DIR/lint-tail-expr-drop-order-borrowck.rs:9:15 + | +LL | let _ = { String::new().as_str() }.len(); + | ^^^^^^^^^^^^^ --- borrow later used by call + | | + | this temporary value will be dropped at the end of the block + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: the lint level is defined here + --> $DIR/lint-tail-expr-drop-order-borrowck.rs:6:9 + | +LL | #![deny(tail_expr_drop_order)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: relative drop order changing in Rust 2024 + --> $DIR/lint-tail-expr-drop-order-borrowck.rs:19:16 + | +LL | f(unsafe { String::new().as_str() }.len()); + | ^^^^^^^^^^^^^ --- borrow later used by call + | | + | this temporary value will be dropped at the end of the block + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see + +error: relative drop order changing in Rust 2024 + --> $DIR/lint-tail-expr-drop-order-borrowck.rs:31:9 + | +LL | &mut || 0 + | ^^^^^^^^^ + | | + | this temporary value will be dropped at the end of the block + | borrow later used here + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see + +error: relative drop order changing in Rust 2024 + --> $DIR/lint-tail-expr-drop-order-borrowck.rs:43:9 + | +LL | g({ &f() }); + | - ^^^^ this temporary value will be dropped at the end of the block + | | + | borrow later used by call + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see + +error: aborting due to 4 previous errors + diff --git a/tests/ui/drop/lint-tail-expr-drop-order.rs b/tests/ui/drop/lint-tail-expr-drop-order.rs index b2a5db0d87133..55a2d1d3b7543 100644 --- a/tests/ui/drop/lint-tail-expr-drop-order.rs +++ b/tests/ui/drop/lint-tail-expr-drop-order.rs @@ -17,7 +17,6 @@ impl Drop for LoudDropper { //~| NOTE: `#1` invokes this custom destructor //~| NOTE: `x` invokes this custom destructor //~| NOTE: `#1` invokes this custom destructor - //~| NOTE: `future` invokes this custom destructor //~| NOTE: `_x` invokes this custom destructor //~| NOTE: `#1` invokes this custom destructor fn drop(&mut self) { diff --git a/tests/ui/drop/lint-tail-expr-drop-order.stderr b/tests/ui/drop/lint-tail-expr-drop-order.stderr index 92afae5af6766..6ff9b7c12681d 100644 --- a/tests/ui/drop/lint-tail-expr-drop-order.stderr +++ b/tests/ui/drop/lint-tail-expr-drop-order.stderr @@ -1,5 +1,5 @@ error: relative drop order changing in Rust 2024 - --> $DIR/lint-tail-expr-drop-order.rs:41:15 + --> $DIR/lint-tail-expr-drop-order.rs:40:15 | LL | let x = LoudDropper; | - @@ -40,7 +40,7 @@ LL | #![deny(tail_expr_drop_order)] | ^^^^^^^^^^^^^^^^^^^^ error: relative drop order changing in Rust 2024 - --> $DIR/lint-tail-expr-drop-order.rs:66:19 + --> $DIR/lint-tail-expr-drop-order.rs:65:19 | LL | let x = LoudDropper; | - @@ -76,7 +76,7 @@ LL | | } = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages error: relative drop order changing in Rust 2024 - --> $DIR/lint-tail-expr-drop-order.rs:93:7 + --> $DIR/lint-tail-expr-drop-order.rs:92:7 | LL | let x = LoudDropper; | - @@ -112,7 +112,7 @@ LL | | } = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages error: relative drop order changing in Rust 2024 - --> $DIR/lint-tail-expr-drop-order.rs:146:5 + --> $DIR/lint-tail-expr-drop-order.rs:145:5 | LL | let future = f(); | ------ @@ -136,19 +136,12 @@ note: `#1` invokes this custom destructor | LL | / impl Drop for LoudDropper { ... | -LL | | } - | |_^ -note: `future` invokes this custom destructor - --> $DIR/lint-tail-expr-drop-order.rs:10:1 - | -LL | / impl Drop for LoudDropper { -... | LL | | } | |_^ = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages error: relative drop order changing in Rust 2024 - --> $DIR/lint-tail-expr-drop-order.rs:163:14 + --> $DIR/lint-tail-expr-drop-order.rs:162:14 | LL | let x = T::default(); | - @@ -170,7 +163,7 @@ LL | } = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages error: relative drop order changing in Rust 2024 - --> $DIR/lint-tail-expr-drop-order.rs:177:5 + --> $DIR/lint-tail-expr-drop-order.rs:176:5 | LL | let x: Result = Ok(LoudDropper); | - @@ -206,7 +199,7 @@ LL | | } = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages error: relative drop order changing in Rust 2024 - --> $DIR/lint-tail-expr-drop-order.rs:221:5 + --> $DIR/lint-tail-expr-drop-order.rs:220:5 | LL | let x = LoudDropper2; | - @@ -226,7 +219,7 @@ LL | } = warning: this changes meaning in Rust 2024 = note: for more information, see note: `#1` invokes this custom destructor - --> $DIR/lint-tail-expr-drop-order.rs:194:5 + --> $DIR/lint-tail-expr-drop-order.rs:193:5 | LL | / impl Drop for LoudDropper3 { LL | | @@ -236,7 +229,7 @@ LL | | } LL | | } | |_____^ note: `x` invokes this custom destructor - --> $DIR/lint-tail-expr-drop-order.rs:206:5 + --> $DIR/lint-tail-expr-drop-order.rs:205:5 | LL | / impl Drop for LoudDropper2 { LL | | @@ -248,7 +241,7 @@ LL | | } = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages error: relative drop order changing in Rust 2024 - --> $DIR/lint-tail-expr-drop-order.rs:234:13 + --> $DIR/lint-tail-expr-drop-order.rs:233:13 | LL | LoudDropper.get() | ^^^^^^^^^^^ diff --git a/tests/ui/drop/tail_expr_drop_order-on-coroutine-unwind.stderr b/tests/ui/drop/tail_expr_drop_order-on-coroutine-unwind.stderr index d98100bc1b04c..b0f971dd5cec6 100644 --- a/tests/ui/drop/tail_expr_drop_order-on-coroutine-unwind.stderr +++ b/tests/ui/drop/tail_expr_drop_order-on-coroutine-unwind.stderr @@ -4,10 +4,14 @@ error: relative drop order changing in Rust 2024 LL | match func().await { | ^^^^^^^----- | | | + | | this value will be stored in a temporary; let us call it `#3` + | | up until Edition 2021 `#3` is dropped last but will be dropped earlier in Edition 2024 | | this value will be stored in a temporary; let us call it `#1` | | `#1` will be dropped later as of Edition 2024 | this value will be stored in a temporary; let us call it `#2` | up until Edition 2021 `#2` is dropped last but will be dropped earlier in Edition 2024 + | `__awaitee` calls a custom destructor + | `__awaitee` will be dropped later as of Edition 2024 ... LL | Err(e) => {} | - diff --git a/tests/ui/drop/tail_expr_drop_order-on-recursive-boxed-fut.rs b/tests/ui/drop/tail_expr_drop_order-on-recursive-boxed-fut.rs new file mode 100644 index 0000000000000..4a72f224d9436 --- /dev/null +++ b/tests/ui/drop/tail_expr_drop_order-on-recursive-boxed-fut.rs @@ -0,0 +1,13 @@ +//@ edition: 2021 +//@ check-pass + +// Make sure we don't cycle error when normalizing types for tail expr drop order lint. + +#![deny(tail_expr_drop_order)] + +async fn test() -> Result<(), Box> { + Box::pin(test()).await?; + Ok(()) +} + +fn main() {} diff --git a/tests/ui/drop/tail_expr_drop_order-on-thread-local.rs b/tests/ui/drop/tail_expr_drop_order-on-thread-local.rs new file mode 100644 index 0000000000000..e38175fd1b653 --- /dev/null +++ b/tests/ui/drop/tail_expr_drop_order-on-thread-local.rs @@ -0,0 +1,56 @@ +//@ check-pass + +#![feature(thread_local)] +#![deny(tail_expr_drop_order)] + +use std::marker::PhantomData; +use std::ops::{Deref, DerefMut}; + +pub struct Global; + +#[thread_local] +static REENTRANCY_STATE: State = State { marker: PhantomData, controller: Global }; + +pub struct Token(PhantomData<*mut ()>); + +pub fn with_mut(f: impl FnOnce(&mut Token) -> T) -> T { + f(&mut REENTRANCY_STATE.borrow_mut()) +} + +pub struct State { + marker: PhantomData<*mut ()>, + controller: T, +} + +impl State { + pub fn borrow_mut(&self) -> TokenMut<'_, T> { + todo!() + } +} + +pub struct TokenMut<'a, T: ?Sized = Global> { + state: &'a State, + token: Token, +} + +impl Deref for TokenMut<'_, T> { + type Target = Token; + + fn deref(&self) -> &Self::Target { + todo!() + } +} + +impl DerefMut for TokenMut<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + todo!() + } +} + +impl Drop for TokenMut<'_, T> { + fn drop(&mut self) { + todo!() + } +} + +fn main() {} diff --git a/tests/ui/drop/terminate-in-initializer.rs b/tests/ui/drop/terminate-in-initializer.rs index 23169aaf65bdc..24ec39fe09638 100644 --- a/tests/ui/drop/terminate-in-initializer.rs +++ b/tests/ui/drop/terminate-in-initializer.rs @@ -1,6 +1,6 @@ //@ run-pass //@ needs-unwind -//@ ignore-emscripten no threads support +//@ needs-threads // Issue #787 // Don't try to clean up uninitialized locals diff --git a/tests/ui/dropck/explicit-drop-bounds.bad1.stderr b/tests/ui/dropck/explicit-drop-bounds.bad1.stderr index 2caa779ffabac..12d7f5b6cd301 100644 --- a/tests/ui/dropck/explicit-drop-bounds.bad1.stderr +++ b/tests/ui/dropck/explicit-drop-bounds.bad1.stderr @@ -12,7 +12,7 @@ LL | struct DropMe(T); help: consider further restricting type parameter `T` with trait `Copy` | LL | [T; 1]: Copy, T: std::marker::Copy // But `[T; 1]: Copy` does not imply `T: Copy` - | ~~~~~~~~~~~~~~~~~~~~~~ + | ++++++++++++++++++++ error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/explicit-drop-bounds.rs:32:18 @@ -28,7 +28,7 @@ LL | struct DropMe(T); help: consider further restricting type parameter `T` with trait `Copy` | LL | [T; 1]: Copy, T: std::marker::Copy // But `[T; 1]: Copy` does not imply `T: Copy` - | ~~~~~~~~~~~~~~~~~~~~~~ + | ++++++++++++++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-3.stderr b/tests/ui/dst/issue-90528-unsizing-suggestion-3.stderr index 774d5ba3c892c..db749436855d5 100644 --- a/tests/ui/dst/issue-90528-unsizing-suggestion-3.stderr +++ b/tests/ui/dst/issue-90528-unsizing-suggestion-3.stderr @@ -68,7 +68,7 @@ LL | fn wants_write(_: impl Write) {} help: consider changing this borrow's mutability | LL | wants_write(&mut [0u8][..]); - | ~~~~ + | +++ error: aborting due to 4 previous errors diff --git a/tests/ui/duplicate/dupe-symbols-7.stderr b/tests/ui/duplicate/dupe-symbols-7.stderr index ab9167e005a37..aa6213af2e4f5 100644 --- a/tests/ui/duplicate/dupe-symbols-7.stderr +++ b/tests/ui/duplicate/dupe-symbols-7.stderr @@ -4,7 +4,7 @@ error: entry symbol `main` declared multiple times LL | fn main(){} | ^^^^^^^^^ | - = help: did you use `#[no_mangle]` on `fn main`? Use `#[start]` instead + = help: did you use `#[no_mangle]` on `fn main`? Use `#![no_main]` to suppress the usual Rust-generated entry point error: aborting due to 1 previous error diff --git a/tests/ui/duplicate/dupe-symbols-8.stderr b/tests/ui/duplicate/dupe-symbols-8.stderr index d7d419c9aa402..0f47d3683b5a5 100644 --- a/tests/ui/duplicate/dupe-symbols-8.stderr +++ b/tests/ui/duplicate/dupe-symbols-8.stderr @@ -4,7 +4,7 @@ error: entry symbol `main` declared multiple times LL | fn main() { | ^^^^^^^^^ | - = help: did you use `#[no_mangle]` on `fn main`? Use `#[start]` instead + = help: did you use `#[no_mangle]` on `fn main`? Use `#![no_main]` to suppress the usual Rust-generated entry point error: aborting due to 1 previous error diff --git a/tests/ui/duplicate/multiple-types-with-same-name-and-derive.rs b/tests/ui/duplicate/multiple-types-with-same-name-and-derive.rs index 72375eb0b3e83..f3bf299aa65f4 100644 --- a/tests/ui/duplicate/multiple-types-with-same-name-and-derive.rs +++ b/tests/ui/duplicate/multiple-types-with-same-name-and-derive.rs @@ -1,5 +1,5 @@ // Here, there are two types with the same name. One of these has a `derive` annotation, but in the -// expansion these `impl`s are associated to the the *other* type. There is a suggestion to remove +// expansion these `impl`s are associated to the *other* type. There is a suggestion to remove // unneeded type parameters, but because we're now point at a type with no type parameters, the // suggestion would suggest removing code from an empty span, which would ICE in nightly. // diff --git a/tests/ui/dyn-compatibility/almost-supertrait-associated-type.rs b/tests/ui/dyn-compatibility/almost-supertrait-associated-type.rs index 83076f7d5fc3b..1b1b8bcf03dc0 100644 --- a/tests/ui/dyn-compatibility/almost-supertrait-associated-type.rs +++ b/tests/ui/dyn-compatibility/almost-supertrait-associated-type.rs @@ -5,8 +5,8 @@ use std::marker::PhantomData; fn transmute(t: T) -> U { (&PhantomData:: as &dyn Foo).transmute(t) - //~^ ERROR the trait `Foo` cannot be made into an object - //~| ERROR the trait `Foo` cannot be made into an object + //~^ ERROR the trait `Foo` is not dyn compatible + //~| ERROR the trait `Foo` is not dyn compatible } struct ActuallySuper; @@ -19,7 +19,7 @@ trait Dyn { type Out; } impl Dyn for dyn Foo + '_ { -//~^ ERROR the trait `Foo` cannot be made into an object +//~^ ERROR the trait `Foo` is not dyn compatible type Out = U; } impl + ?Sized, U> Super for S { diff --git a/tests/ui/dyn-compatibility/almost-supertrait-associated-type.stderr b/tests/ui/dyn-compatibility/almost-supertrait-associated-type.stderr index 99bcccc20c01a..a384697ee083f 100644 --- a/tests/ui/dyn-compatibility/almost-supertrait-associated-type.stderr +++ b/tests/ui/dyn-compatibility/almost-supertrait-associated-type.stderr @@ -1,53 +1,53 @@ -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/almost-supertrait-associated-type.rs:21:20 | LL | impl Dyn for dyn Foo + '_ { - | ^^^^^^^^^^^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/almost-supertrait-associated-type.rs:33:34 | LL | trait Foo: Super - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... ... LL | fn transmute(&self, t: T) -> >::Assoc; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...because method `transmute` references the `Self` type in its return type = help: consider moving `transmute` to another trait - = help: only type `std::marker::PhantomData` implements the trait, consider using it directly instead -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/almost-supertrait-associated-type.rs:7:27 | LL | (&PhantomData:: as &dyn Foo).transmute(t) - | ^^^^^^^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/almost-supertrait-associated-type.rs:33:34 | LL | trait Foo: Super - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... ... LL | fn transmute(&self, t: T) -> >::Assoc; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...because method `transmute` references the `Self` type in its return type = help: consider moving `transmute` to another trait - = help: only type `std::marker::PhantomData` implements the trait, consider using it directly instead -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/almost-supertrait-associated-type.rs:7:6 | LL | (&PhantomData:: as &dyn Foo).transmute(t) - | ^^^^^^^^^^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^^^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/almost-supertrait-associated-type.rs:33:34 | LL | trait Foo: Super - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... ... LL | fn transmute(&self, t: T) -> >::Assoc; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...because method `transmute` references the `Self` type in its return type = help: consider moving `transmute` to another trait - = help: only type `std::marker::PhantomData` implements the trait, consider using it directly instead = note: required for the cast from `&PhantomData` to `&dyn Foo` error: aborting due to 3 previous errors diff --git a/tests/ui/dyn-compatibility/assoc_type_bounds_sized_used.stderr b/tests/ui/dyn-compatibility/assoc_type_bounds_sized_used.stderr index b67a1244eceab..7b152adea4925 100644 --- a/tests/ui/dyn-compatibility/assoc_type_bounds_sized_used.stderr +++ b/tests/ui/dyn-compatibility/assoc_type_bounds_sized_used.stderr @@ -1,17 +1,3 @@ -error[E0599]: the function or associated item `default` exists for associated type `::Bar`, but its trait bounds were not satisfied - --> $DIR/assoc_type_bounds_sized_used.rs:11:30 - | -LL | let _ = ::Bar::default(); - | ^^^^^^^ function or associated item cannot be called on `::Bar` due to unsatisfied trait bounds - | - = note: the following trait bounds were not satisfied: - `T: Sized` - which is required by `::Bar: Default` -help: consider restricting the type parameter to satisfy the trait bound - | -LL | fn bop() where T: Sized { - | ++++++++++++++ - error[E0277]: the size for values of type `T` cannot be known at compilation time --> $DIR/assoc_type_bounds_sized_used.rs:11:14 | @@ -38,6 +24,20 @@ help: consider relaxing the implicit `Sized` restriction LL | type Bar: Default + ?Sized | ++++++++ +error[E0599]: the function or associated item `default` exists for associated type `::Bar`, but its trait bounds were not satisfied + --> $DIR/assoc_type_bounds_sized_used.rs:11:30 + | +LL | let _ = ::Bar::default(); + | ^^^^^^^ function or associated item cannot be called on `::Bar` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `T: Sized` + which is required by `::Bar: Default` +help: consider restricting the type parameter to satisfy the trait bound + | +LL | fn bop() where T: Sized { + | ++++++++++++++ + error: aborting due to 2 previous errors Some errors have detailed explanations: E0277, E0599. diff --git a/tests/ui/dyn-compatibility/associated-consts.curr.stderr b/tests/ui/dyn-compatibility/associated-consts.curr.stderr index 17d184942c701..de24393812399 100644 --- a/tests/ui/dyn-compatibility/associated-consts.curr.stderr +++ b/tests/ui/dyn-compatibility/associated-consts.curr.stderr @@ -1,29 +1,31 @@ -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/associated-consts.rs:12:31 | LL | fn make_bar(t: &T) -> &dyn Bar { - | ^^^^^^^ `Bar` cannot be made into an object + | ^^^^^^^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/associated-consts.rs:9:11 | LL | trait Bar { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | const X: usize; | ^ ...because it contains this associated `const` = help: consider moving `X` to another trait -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/associated-consts.rs:14:5 | LL | t - | ^ `Bar` cannot be made into an object + | ^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/associated-consts.rs:9:11 | LL | trait Bar { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | const X: usize; | ^ ...because it contains this associated `const` = help: consider moving `X` to another trait diff --git a/tests/ui/dyn-compatibility/associated-consts.dyn_compatible_for_dispatch.stderr b/tests/ui/dyn-compatibility/associated-consts.dyn_compatible_for_dispatch.stderr index cc5120232c244..704d833f00ba9 100644 --- a/tests/ui/dyn-compatibility/associated-consts.dyn_compatible_for_dispatch.stderr +++ b/tests/ui/dyn-compatibility/associated-consts.dyn_compatible_for_dispatch.stderr @@ -1,14 +1,15 @@ -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/associated-consts.rs:14:5 | LL | t - | ^ `Bar` cannot be made into an object + | ^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/associated-consts.rs:9:11 | LL | trait Bar { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | const X: usize; | ^ ...because it contains this associated `const` = help: consider moving `X` to another trait diff --git a/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.new.stderr b/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.new.stderr index 83795f3128ec7..87207d607e2a7 100644 --- a/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.new.stderr +++ b/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.new.stderr @@ -7,8 +7,9 @@ LL | fn id(f: Copy) -> usize { = note: `Copy` it is dyn-incompatible, so it can't be `dyn` help: use a new generic type parameter, constrained by `Copy` | -LL | fn id(f: T) -> usize { - | +++++++++ ~ +LL - fn id(f: Copy) -> usize { +LL + fn id(f: T) -> usize { + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn id(f: impl Copy) -> usize { diff --git a/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.old.stderr b/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.old.stderr index 54daefea31c10..b811ef40c26b4 100644 --- a/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.old.stderr +++ b/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.old.stderr @@ -26,14 +26,15 @@ help: if this is a dyn-compatible trait, use `dyn` LL | fn id(f: dyn Copy) -> usize { | +++ -error[E0038]: the trait `Copy` cannot be made into an object +error[E0038]: the trait `Copy` is not dyn compatible --> $DIR/avoid-ice-on-warning-2.rs:4:13 | LL | fn id(f: Copy) -> usize { - | ^^^^ `Copy` cannot be made into an object + | ^^^^ `Copy` is not dyn compatible | - = note: the trait cannot be made into an object because it requires `Self: Sized` - = note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + = note: the trait is not dyn compatible because it requires `Self: Sized` + = note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit error[E0618]: expected function, found `(dyn Copy + 'static)` --> $DIR/avoid-ice-on-warning-2.rs:12:5 diff --git a/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.rs b/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.rs index 3c2da667b3924..312e0d666f1a9 100644 --- a/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.rs +++ b/tests/ui/dyn-compatibility/avoid-ice-on-warning-2.rs @@ -3,7 +3,7 @@ //@[new] edition:2021 fn id(f: Copy) -> usize { //[new]~^ ERROR expected a type, found a trait -//[old]~^^ ERROR the trait `Copy` cannot be made into an object +//[old]~^^ ERROR the trait `Copy` is not dyn compatible //[old]~| ERROR the size for values of type `(dyn Copy + 'static)` //[old]~| WARN trait objects without an explicit `dyn` are deprecated //[old]~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! diff --git a/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.new.stderr b/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.new.stderr index 813b5863738f8..6075e313f4ed4 100644 --- a/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.new.stderr +++ b/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.new.stderr @@ -7,8 +7,9 @@ LL | trait A { fn g(b: B) -> B; } = note: `B` it is dyn-incompatible, so it can't be `dyn` help: use a new generic type parameter, constrained by `B` | -LL | trait A { fn g(b: T) -> B; } - | ++++++ ~ +LL - trait A { fn g(b: B) -> B; } +LL + trait A { fn g(b: T) -> B; } + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | trait A { fn g(b: impl B) -> B; } @@ -34,8 +35,9 @@ LL | trait B { fn f(a: A) -> A; } = note: `A` it is dyn-incompatible, so it can't be `dyn` help: use a new generic type parameter, constrained by `A` | -LL | trait B { fn f(a: T) -> A; } - | ++++++ ~ +LL - trait B { fn f(a: A) -> A; } +LL + trait B { fn f(a: T) -> A; } + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | trait B { fn f(a: impl A) -> A; } diff --git a/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.old.stderr b/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.old.stderr index 6bc2d73a0d0e4..d8935be560948 100644 --- a/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.old.stderr +++ b/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.old.stderr @@ -65,19 +65,20 @@ help: if this is a dyn-compatible trait, use `dyn` LL | trait B { fn f(a: dyn A) -> A; } | +++ -error[E0038]: the trait `A` cannot be made into an object +error[E0038]: the trait `A` is not dyn compatible --> $DIR/avoid-ice-on-warning-3.rs:4:19 | LL | trait B { fn f(a: A) -> A; } - | ^ `A` cannot be made into an object + | ^ `A` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/avoid-ice-on-warning-3.rs:14:14 | LL | trait A { fn g(b: B) -> B; } | - ^ ...because associated function `g` has no `self` parameter | | - | this trait cannot be made into an object... + | this trait is not dyn compatible... help: consider turning `g` into a method by giving it a `&self` argument | LL | trait A { fn g(&self, b: B) -> B; } @@ -101,19 +102,20 @@ help: if this is a dyn-compatible trait, use `dyn` LL | trait A { fn g(b: dyn B) -> B; } | +++ -error[E0038]: the trait `B` cannot be made into an object +error[E0038]: the trait `B` is not dyn compatible --> $DIR/avoid-ice-on-warning-3.rs:14:19 | LL | trait A { fn g(b: B) -> B; } - | ^ `B` cannot be made into an object + | ^ `B` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/avoid-ice-on-warning-3.rs:4:14 | LL | trait B { fn f(a: A) -> A; } | - ^ ...because associated function `f` has no `self` parameter | | - | this trait cannot be made into an object... + | this trait is not dyn compatible... help: consider turning `f` into a method by giving it a `&self` argument | LL | trait B { fn f(&self, a: A) -> A; } diff --git a/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.rs b/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.rs index 00d47225e92fa..9ccbfc15a0daa 100644 --- a/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.rs +++ b/tests/ui/dyn-compatibility/avoid-ice-on-warning-3.rs @@ -4,7 +4,7 @@ trait B { fn f(a: A) -> A; } //[new]~^ ERROR expected a type, found a trait //[new]~| ERROR expected a type, found a trait -//[old]~^^^ ERROR the trait `A` cannot be made into an object +//[old]~^^^ ERROR the trait `A` is not dyn compatible //[old]~| WARN trait objects without an explicit `dyn` are deprecated //[old]~| WARN trait objects without an explicit `dyn` are deprecated //[old]~| WARN trait objects without an explicit `dyn` are deprecated @@ -14,7 +14,7 @@ trait B { fn f(a: A) -> A; } trait A { fn g(b: B) -> B; } //[new]~^ ERROR expected a type, found a trait //[new]~| ERROR expected a type, found a trait -//[old]~^^^ ERROR the trait `B` cannot be made into an object +//[old]~^^^ ERROR the trait `B` is not dyn compatible //[old]~| WARN trait objects without an explicit `dyn` are deprecated //[old]~| WARN trait objects without an explicit `dyn` are deprecated //[old]~| WARN trait objects without an explicit `dyn` are deprecated diff --git a/tests/ui/dyn-compatibility/avoid-ice-on-warning.new.stderr b/tests/ui/dyn-compatibility/avoid-ice-on-warning.new.stderr index e9eb1cdd0c233..3ba4aa12e6d9e 100644 --- a/tests/ui/dyn-compatibility/avoid-ice-on-warning.new.stderr +++ b/tests/ui/dyn-compatibility/avoid-ice-on-warning.new.stderr @@ -6,8 +6,9 @@ LL | fn call_this(f: F) : Fn(&str) + call_that {} | help: use `->` instead | -LL | fn call_this(f: F) -> Fn(&str) + call_that {} - | ~~ +LL - fn call_this(f: F) : Fn(&str) + call_that {} +LL + fn call_this(f: F) -> Fn(&str) + call_that {} + | error[E0405]: cannot find trait `call_that` in this scope --> $DIR/avoid-ice-on-warning.rs:4:36 diff --git a/tests/ui/dyn-compatibility/avoid-ice-on-warning.old.stderr b/tests/ui/dyn-compatibility/avoid-ice-on-warning.old.stderr index 646fb57af9edd..dbfe91e181149 100644 --- a/tests/ui/dyn-compatibility/avoid-ice-on-warning.old.stderr +++ b/tests/ui/dyn-compatibility/avoid-ice-on-warning.old.stderr @@ -6,8 +6,9 @@ LL | fn call_this(f: F) : Fn(&str) + call_that {} | help: use `->` instead | -LL | fn call_this(f: F) -> Fn(&str) + call_that {} - | ~~ +LL - fn call_this(f: F) : Fn(&str) + call_that {} +LL + fn call_this(f: F) -> Fn(&str) + call_that {} + | error[E0405]: cannot find trait `call_that` in this scope --> $DIR/avoid-ice-on-warning.rs:4:36 diff --git a/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.new.fixed b/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.new.fixed index a54892afd3e4b..b5200e9fff515 100644 --- a/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.new.fixed +++ b/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.new.fixed @@ -5,7 +5,7 @@ #![deny(bare_trait_objects)] fn ord_prefer_dot(s: String) -> impl Ord { //[new]~^ ERROR expected a type, found a trait - //[old]~^^ ERROR the trait `Ord` cannot be made into an object + //[old]~^^ ERROR the trait `Ord` is not dyn compatible //[old]~| ERROR trait objects without an explicit `dyn` are deprecated //[old]~| WARNING this is accepted in the current edition (Rust 2015) (s.starts_with("."), s) diff --git a/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.old.stderr b/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.old.stderr index 45c9b0ce5d9cb..7be6cb0d03bbb 100644 --- a/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.old.stderr +++ b/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.old.stderr @@ -16,19 +16,20 @@ help: if this is a dyn-compatible trait, use `dyn` LL | fn ord_prefer_dot(s: String) -> dyn Ord { | +++ -error[E0038]: the trait `Ord` cannot be made into an object +error[E0038]: the trait `Ord` is not dyn compatible --> $DIR/bare-trait-dont-suggest-dyn.rs:6:33 | LL | fn ord_prefer_dot(s: String) -> Ord { - | ^^^ `Ord` cannot be made into an object + | ^^^ `Ord` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $SRC_DIR/core/src/cmp.rs:LL:COL | - = note: the trait cannot be made into an object because it uses `Self` as a type parameter + = note: the trait is not dyn compatible because it uses `Self` as a type parameter ::: $SRC_DIR/core/src/cmp.rs:LL:COL | - = note: the trait cannot be made into an object because it uses `Self` as a type parameter + = note: the trait is not dyn compatible because it uses `Self` as a type parameter help: consider using an opaque type instead | LL | fn ord_prefer_dot(s: String) -> impl Ord { diff --git a/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.rs b/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.rs index cf9be612d2e7e..385fd48102cee 100644 --- a/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.rs +++ b/tests/ui/dyn-compatibility/bare-trait-dont-suggest-dyn.rs @@ -5,7 +5,7 @@ #![deny(bare_trait_objects)] fn ord_prefer_dot(s: String) -> Ord { //[new]~^ ERROR expected a type, found a trait - //[old]~^^ ERROR the trait `Ord` cannot be made into an object + //[old]~^^ ERROR the trait `Ord` is not dyn compatible //[old]~| ERROR trait objects without an explicit `dyn` are deprecated //[old]~| WARNING this is accepted in the current edition (Rust 2015) (s.starts_with("."), s) diff --git a/tests/ui/dyn-compatibility/bounds.rs b/tests/ui/dyn-compatibility/bounds.rs index 1e04d11c516cb..ed4a69129af3d 100644 --- a/tests/ui/dyn-compatibility/bounds.rs +++ b/tests/ui/dyn-compatibility/bounds.rs @@ -5,7 +5,7 @@ trait X { } fn f() -> Box> { - //~^ ERROR the trait `X` cannot be made into an object + //~^ ERROR the trait `X` is not dyn compatible loop {} } diff --git a/tests/ui/dyn-compatibility/bounds.stderr b/tests/ui/dyn-compatibility/bounds.stderr index 9231d524fd173..5473af388c0c3 100644 --- a/tests/ui/dyn-compatibility/bounds.stderr +++ b/tests/ui/dyn-compatibility/bounds.stderr @@ -1,14 +1,15 @@ -error[E0038]: the trait `X` cannot be made into an object +error[E0038]: the trait `X` is not dyn compatible --> $DIR/bounds.rs:7:15 | LL | fn f() -> Box> { - | ^^^^^^^^^^^^^^ `X` cannot be made into an object + | ^^^^^^^^^^^^^^ `X` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/bounds.rs:4:13 | LL | trait X { - | - this trait cannot be made into an object... + | - this trait is not dyn compatible... LL | type U: PartialEq; | ^^^^^^^^^^^^^^^ ...because it uses `Self` as a type parameter diff --git a/tests/ui/dyn-compatibility/default-param-self-projection.rs b/tests/ui/dyn-compatibility/default-param-self-projection.rs new file mode 100644 index 0000000000000..a440cd735daba --- /dev/null +++ b/tests/ui/dyn-compatibility/default-param-self-projection.rs @@ -0,0 +1,17 @@ +trait A::E> {} + +trait D { + type E; +} + +impl A<()> for () {} +impl D for () { + type E = (); +} + +fn f() { + let B: &dyn A = &(); + //~^ ERROR the type parameter `C` must be explicitly specified +} + +fn main() {} diff --git a/tests/ui/dyn-compatibility/default-param-self-projection.stderr b/tests/ui/dyn-compatibility/default-param-self-projection.stderr new file mode 100644 index 0000000000000..ea252a99b0480 --- /dev/null +++ b/tests/ui/dyn-compatibility/default-param-self-projection.stderr @@ -0,0 +1,18 @@ +error[E0393]: the type parameter `C` must be explicitly specified + --> $DIR/default-param-self-projection.rs:13:17 + | +LL | trait A::E> {} + | --------------------------- type parameter `C` must be specified for this +... +LL | let B: &dyn A = &(); + | ^ + | + = note: because the parameter default references `Self`, the parameter must be specified on the object type +help: set the type parameter to the desired type + | +LL | let B: &dyn A = &(); + | +++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0393`. diff --git a/tests/ui/dyn-compatibility/gat-incompatible-supertrait.rs b/tests/ui/dyn-compatibility/gat-incompatible-supertrait.rs new file mode 100644 index 0000000000000..fff29ac2b51a2 --- /dev/null +++ b/tests/ui/dyn-compatibility/gat-incompatible-supertrait.rs @@ -0,0 +1,18 @@ +// Test that the dyn-compatibility diagnostics for GATs refer first to the +// user-named trait, not the GAT-containing supertrait. +// +// NOTE: this test is currently broken, and first reports: +// "the trait `Super` is not dyn compatible" +// +//@ edition:2018 + +trait Super { + type Assoc<'a>; +} + +trait Child: Super {} + +fn take_dyn(_: &dyn Child) {} +//~^ ERROR the trait `Super` is not dyn compatible + +fn main() {} diff --git a/tests/ui/dyn-compatibility/gat-incompatible-supertrait.stderr b/tests/ui/dyn-compatibility/gat-incompatible-supertrait.stderr new file mode 100644 index 0000000000000..cfebd5d694704 --- /dev/null +++ b/tests/ui/dyn-compatibility/gat-incompatible-supertrait.stderr @@ -0,0 +1,19 @@ +error[E0038]: the trait `Super` is not dyn compatible + --> $DIR/gat-incompatible-supertrait.rs:15:21 + | +LL | fn take_dyn(_: &dyn Child) {} + | ^^^^^ `Super` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/gat-incompatible-supertrait.rs:10:10 + | +LL | trait Super { + | ----- this trait is not dyn compatible... +LL | type Assoc<'a>; + | ^^^^^ ...because it contains the generic associated type `Assoc` + = help: consider moving `Assoc` to another trait + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/dyn-compatibility/generics.curr.stderr b/tests/ui/dyn-compatibility/generics.curr.stderr index c63db38a080b4..d29d02b2ff3ca 100644 --- a/tests/ui/dyn-compatibility/generics.curr.stderr +++ b/tests/ui/dyn-compatibility/generics.curr.stderr @@ -1,75 +1,80 @@ -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/generics.rs:18:31 | LL | fn make_bar(t: &T) -> &dyn Bar { - | ^^^^^^^ `Bar` cannot be made into an object + | ^^^^^^^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/generics.rs:10:8 | LL | trait Bar { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn bar(&self, t: T); | ^^^ ...because method `bar` has generic type parameters = help: consider moving `bar` to another trait -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/generics.rs:25:40 | LL | fn make_bar_explicit(t: &T) -> &dyn Bar { - | ^^^^^^^ `Bar` cannot be made into an object + | ^^^^^^^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/generics.rs:10:8 | LL | trait Bar { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn bar(&self, t: T); | ^^^ ...because method `bar` has generic type parameters = help: consider moving `bar` to another trait -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/generics.rs:20:5 | LL | t - | ^ `Bar` cannot be made into an object + | ^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/generics.rs:10:8 | LL | trait Bar { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn bar(&self, t: T); | ^^^ ...because method `bar` has generic type parameters = help: consider moving `bar` to another trait = note: required for the cast from `&T` to `&dyn Bar` -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/generics.rs:27:10 | LL | t as &dyn Bar - | ^^^^^^^^ `Bar` cannot be made into an object + | ^^^^^^^^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/generics.rs:10:8 | LL | trait Bar { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn bar(&self, t: T); | ^^^ ...because method `bar` has generic type parameters = help: consider moving `bar` to another trait -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/generics.rs:27:5 | LL | t as &dyn Bar - | ^ `Bar` cannot be made into an object + | ^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/generics.rs:10:8 | LL | trait Bar { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn bar(&self, t: T); | ^^^ ...because method `bar` has generic type parameters = help: consider moving `bar` to another trait diff --git a/tests/ui/dyn-compatibility/generics.dyn_compatible_for_dispatch.stderr b/tests/ui/dyn-compatibility/generics.dyn_compatible_for_dispatch.stderr index ba2546ef2dc5c..b3565a766fe16 100644 --- a/tests/ui/dyn-compatibility/generics.dyn_compatible_for_dispatch.stderr +++ b/tests/ui/dyn-compatibility/generics.dyn_compatible_for_dispatch.stderr @@ -1,30 +1,32 @@ -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/generics.rs:20:5 | LL | t - | ^ `Bar` cannot be made into an object + | ^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/generics.rs:10:8 | LL | trait Bar { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn bar(&self, t: T); | ^^^ ...because method `bar` has generic type parameters = help: consider moving `bar` to another trait = note: required for the cast from `&T` to `&dyn Bar` -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/generics.rs:27:5 | LL | t as &dyn Bar - | ^ `Bar` cannot be made into an object + | ^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/generics.rs:10:8 | LL | trait Bar { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn bar(&self, t: T); | ^^^ ...because method `bar` has generic type parameters = help: consider moving `bar` to another trait diff --git a/tests/ui/dyn-compatibility/mention-correct-dyn-incompatible-trait.stderr b/tests/ui/dyn-compatibility/mention-correct-dyn-incompatible-trait.stderr index 7378ec023c926..bb3f7899bf1cc 100644 --- a/tests/ui/dyn-compatibility/mention-correct-dyn-incompatible-trait.stderr +++ b/tests/ui/dyn-compatibility/mention-correct-dyn-incompatible-trait.stderr @@ -1,36 +1,38 @@ -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/mention-correct-dyn-incompatible-trait.rs:19:15 | LL | let test: &mut dyn Bar = &mut thing; - | ^^^^^^^^^^^^ `Bar` cannot be made into an object + | ^^^^^^^^^^^^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/mention-correct-dyn-incompatible-trait.rs:4:8 | LL | fn foo(&self, val: T); | ^^^ ...because method `foo` has generic type parameters ... LL | trait Bar: Foo { } - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... = help: consider moving `foo` to another trait - = help: only type `Thing` implements the trait, consider using it directly instead + = help: only type `Thing` implements `Bar`; consider using it directly instead. -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/mention-correct-dyn-incompatible-trait.rs:19:30 | LL | let test: &mut dyn Bar = &mut thing; - | ^^^^^^^^^^ `Bar` cannot be made into an object + | ^^^^^^^^^^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/mention-correct-dyn-incompatible-trait.rs:4:8 | LL | fn foo(&self, val: T); | ^^^ ...because method `foo` has generic type parameters ... LL | trait Bar: Foo { } - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... = help: consider moving `foo` to another trait - = help: only type `Thing` implements the trait, consider using it directly instead + = help: only type `Thing` implements `Bar`; consider using it directly instead. = note: required for the cast from `&mut Thing` to `&mut dyn Bar` error: aborting due to 2 previous errors diff --git a/tests/ui/dyn-compatibility/mentions-Self-in-super-predicates.rs b/tests/ui/dyn-compatibility/mentions-Self-in-super-predicates.rs index c9ec44cc0b815..2ab0c6c8f5dab 100644 --- a/tests/ui/dyn-compatibility/mentions-Self-in-super-predicates.rs +++ b/tests/ui/dyn-compatibility/mentions-Self-in-super-predicates.rs @@ -36,9 +36,9 @@ impl <'x> Expr for SExpr<'x> { fn main() { let a: Box = Box::new(SExpr::new()); - //~^ ERROR: `Expr` cannot be made into an object + //~^ ERROR: `Expr` is not dyn compatible let b: Box = Box::new(SExpr::new()); - //~^ ERROR: `Expr` cannot be made into an object + //~^ ERROR: `Expr` is not dyn compatible // assert_eq!(a , b); } diff --git a/tests/ui/dyn-compatibility/mentions-Self-in-super-predicates.stderr b/tests/ui/dyn-compatibility/mentions-Self-in-super-predicates.stderr index 7578edce7d19e..cf3a7b2308474 100644 --- a/tests/ui/dyn-compatibility/mentions-Self-in-super-predicates.stderr +++ b/tests/ui/dyn-compatibility/mentions-Self-in-super-predicates.stderr @@ -1,47 +1,47 @@ -error[E0038]: the trait `Expr` cannot be made into an object - --> $DIR/mentions-Self-in-super-predicates.rs:12:23 +error[E0038]: the trait `Expr` is not dyn compatible + --> $DIR/mentions-Self-in-super-predicates.rs:12:27 | LL | elements: Vec>, - | ^^^^^^^^^^^^^ `Expr` cannot be made into an object + | ^^^^ `Expr` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/mentions-Self-in-super-predicates.rs:5:21 | LL | trait Expr: Debug + PartialEq { | ---- ^^^^^^^^^ ...because it uses `Self` as a type parameter | | - | this trait cannot be made into an object... - = help: only type `SExpr<'x>` implements the trait, consider using it directly instead + | this trait is not dyn compatible... -error[E0038]: the trait `Expr` cannot be made into an object - --> $DIR/mentions-Self-in-super-predicates.rs:38:16 +error[E0038]: the trait `Expr` is not dyn compatible + --> $DIR/mentions-Self-in-super-predicates.rs:38:20 | LL | let a: Box = Box::new(SExpr::new()); - | ^^^^^^^^ `Expr` cannot be made into an object + | ^^^^ `Expr` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/mentions-Self-in-super-predicates.rs:5:21 | LL | trait Expr: Debug + PartialEq { | ---- ^^^^^^^^^ ...because it uses `Self` as a type parameter | | - | this trait cannot be made into an object... - = help: only type `SExpr<'x>` implements the trait, consider using it directly instead + | this trait is not dyn compatible... -error[E0038]: the trait `Expr` cannot be made into an object - --> $DIR/mentions-Self-in-super-predicates.rs:40:16 +error[E0038]: the trait `Expr` is not dyn compatible + --> $DIR/mentions-Self-in-super-predicates.rs:40:20 | LL | let b: Box = Box::new(SExpr::new()); - | ^^^^^^^^ `Expr` cannot be made into an object + | ^^^^ `Expr` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/mentions-Self-in-super-predicates.rs:5:21 | LL | trait Expr: Debug + PartialEq { | ---- ^^^^^^^^^ ...because it uses `Self` as a type parameter | | - | this trait cannot be made into an object... - = help: only type `SExpr<'x>` implements the trait, consider using it directly instead + | this trait is not dyn compatible... error: aborting due to 3 previous errors diff --git a/tests/ui/dyn-compatibility/mentions-Self.curr.stderr b/tests/ui/dyn-compatibility/mentions-Self.curr.stderr index 434e41cf2182d..2d3fe5ce636cb 100644 --- a/tests/ui/dyn-compatibility/mentions-Self.curr.stderr +++ b/tests/ui/dyn-compatibility/mentions-Self.curr.stderr @@ -1,60 +1,64 @@ -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/mentions-Self.rs:22:31 | LL | fn make_bar(t: &T) -> &dyn Bar { - | ^^^^^^^ `Bar` cannot be made into an object + | ^^^^^^^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/mentions-Self.rs:11:22 | LL | trait Bar { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn bar(&self, x: &Self); | ^^^^^ ...because method `bar` references the `Self` type in this parameter = help: consider moving `bar` to another trait -error[E0038]: the trait `Baz` cannot be made into an object +error[E0038]: the trait `Baz` is not dyn compatible --> $DIR/mentions-Self.rs:28:31 | LL | fn make_baz(t: &T) -> &dyn Baz { - | ^^^^^^^ `Baz` cannot be made into an object + | ^^^^^^^ `Baz` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/mentions-Self.rs:15:22 | LL | trait Baz { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn baz(&self) -> Self; | ^^^^ ...because method `baz` references the `Self` type in its return type = help: consider moving `baz` to another trait -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/mentions-Self.rs:24:5 | LL | t - | ^ `Bar` cannot be made into an object + | ^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/mentions-Self.rs:11:22 | LL | trait Bar { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn bar(&self, x: &Self); | ^^^^^ ...because method `bar` references the `Self` type in this parameter = help: consider moving `bar` to another trait = note: required for the cast from `&T` to `&dyn Bar` -error[E0038]: the trait `Baz` cannot be made into an object +error[E0038]: the trait `Baz` is not dyn compatible --> $DIR/mentions-Self.rs:30:5 | LL | t - | ^ `Baz` cannot be made into an object + | ^ `Baz` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/mentions-Self.rs:15:22 | LL | trait Baz { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn baz(&self) -> Self; | ^^^^ ...because method `baz` references the `Self` type in its return type = help: consider moving `baz` to another trait diff --git a/tests/ui/dyn-compatibility/mentions-Self.dyn_compatible_for_dispatch.stderr b/tests/ui/dyn-compatibility/mentions-Self.dyn_compatible_for_dispatch.stderr index dc2d1f87eb737..91c26a860259b 100644 --- a/tests/ui/dyn-compatibility/mentions-Self.dyn_compatible_for_dispatch.stderr +++ b/tests/ui/dyn-compatibility/mentions-Self.dyn_compatible_for_dispatch.stderr @@ -1,30 +1,32 @@ -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/mentions-Self.rs:24:5 | LL | t - | ^ `Bar` cannot be made into an object + | ^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/mentions-Self.rs:11:22 | LL | trait Bar { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn bar(&self, x: &Self); | ^^^^^ ...because method `bar` references the `Self` type in this parameter = help: consider moving `bar` to another trait = note: required for the cast from `&T` to `&dyn Bar` -error[E0038]: the trait `Baz` cannot be made into an object +error[E0038]: the trait `Baz` is not dyn compatible --> $DIR/mentions-Self.rs:30:5 | LL | t - | ^ `Baz` cannot be made into an object + | ^ `Baz` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/mentions-Self.rs:15:22 | LL | trait Baz { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn baz(&self) -> Self; | ^^^^ ...because method `baz` references the `Self` type in its return type = help: consider moving `baz` to another trait diff --git a/tests/ui/dyn-compatibility/missing-assoc-type.rs b/tests/ui/dyn-compatibility/missing-assoc-type.rs index 21f7fd92e80da..135761dd03656 100644 --- a/tests/ui/dyn-compatibility/missing-assoc-type.rs +++ b/tests/ui/dyn-compatibility/missing-assoc-type.rs @@ -2,6 +2,6 @@ trait Foo { type Bar; } -fn bar(x: &dyn Foo) {} //~ ERROR the trait `Foo` cannot be made into an object +fn bar(x: &dyn Foo) {} //~ ERROR the trait `Foo` is not dyn compatible fn main() {} diff --git a/tests/ui/dyn-compatibility/missing-assoc-type.stderr b/tests/ui/dyn-compatibility/missing-assoc-type.stderr index 184201dd1cee7..5a7560682f2e8 100644 --- a/tests/ui/dyn-compatibility/missing-assoc-type.stderr +++ b/tests/ui/dyn-compatibility/missing-assoc-type.stderr @@ -1,14 +1,15 @@ -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/missing-assoc-type.rs:5:16 | LL | fn bar(x: &dyn Foo) {} - | ^^^ `Foo` cannot be made into an object + | ^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/missing-assoc-type.rs:2:10 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | type Bar; | ^^^ ...because it contains the generic associated type `Bar` = help: consider moving `Bar` to another trait diff --git a/tests/ui/dyn-compatibility/no-static.curr.stderr b/tests/ui/dyn-compatibility/no-static.curr.stderr index 584db77985566..bf9b48e7a43d6 100644 --- a/tests/ui/dyn-compatibility/no-static.curr.stderr +++ b/tests/ui/dyn-compatibility/no-static.curr.stderr @@ -1,17 +1,18 @@ -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/no-static.rs:12:22 | LL | fn diverges() -> Box { - | ^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/no-static.rs:9:8 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn foo() {} | ^^^ ...because associated function `foo` has no `self` parameter - = help: only type `Bar` implements the trait, consider using it directly instead + = help: only type `Bar` implements `Foo`; consider using it directly instead. help: consider turning `foo` into a method by giving it a `&self` argument | LL | fn foo(&self) {} @@ -21,20 +22,21 @@ help: alternatively, consider constraining `foo` so it does not apply to trait o LL | fn foo() where Self: Sized {} | +++++++++++++++++ -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/no-static.rs:22:12 | LL | let b: Box = Box::new(Bar); - | ^^^^^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/no-static.rs:9:8 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn foo() {} | ^^^ ...because associated function `foo` has no `self` parameter - = help: only type `Bar` implements the trait, consider using it directly instead + = help: only type `Bar` implements `Foo`; consider using it directly instead. help: consider turning `foo` into a method by giving it a `&self` argument | LL | fn foo(&self) {} @@ -44,20 +46,21 @@ help: alternatively, consider constraining `foo` so it does not apply to trait o LL | fn foo() where Self: Sized {} | +++++++++++++++++ -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/no-static.rs:22:27 | LL | let b: Box = Box::new(Bar); - | ^^^^^^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/no-static.rs:9:8 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn foo() {} | ^^^ ...because associated function `foo` has no `self` parameter - = help: only type `Bar` implements the trait, consider using it directly instead + = help: only type `Bar` implements `Foo`; consider using it directly instead. = note: required for the cast from `Box` to `Box` help: consider turning `foo` into a method by giving it a `&self` argument | diff --git a/tests/ui/dyn-compatibility/no-static.dyn_compatible_for_dispatch.stderr b/tests/ui/dyn-compatibility/no-static.dyn_compatible_for_dispatch.stderr index f2deb3b8d84b9..d5ad451033461 100644 --- a/tests/ui/dyn-compatibility/no-static.dyn_compatible_for_dispatch.stderr +++ b/tests/ui/dyn-compatibility/no-static.dyn_compatible_for_dispatch.stderr @@ -1,17 +1,18 @@ -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/no-static.rs:22:27 | LL | let b: Box = Box::new(Bar); - | ^^^^^^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/no-static.rs:9:8 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn foo() {} | ^^^ ...because associated function `foo` has no `self` parameter - = help: only type `Bar` implements the trait, consider using it directly instead + = help: only type `Bar` implements `Foo`; consider using it directly instead. = note: required for the cast from `Box` to `Box` help: consider turning `foo` into a method by giving it a `&self` argument | diff --git a/tests/ui/dyn-compatibility/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.stderr b/tests/ui/dyn-compatibility/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.stderr index 4c6d84f05341f..832e7ef4dc38c 100644 --- a/tests/ui/dyn-compatibility/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.stderr +++ b/tests/ui/dyn-compatibility/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.stderr @@ -195,8 +195,9 @@ LL | fn foo(_: &Trait); | help: use a new generic type parameter, constrained by `Trait` | -LL | fn foo(_: &T); - | ++++++++++ ~ +LL - fn foo(_: &Trait); +LL + fn foo(_: &T); + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn foo(_: &impl Trait); @@ -214,8 +215,9 @@ LL | fn bar(_: &'a Trait); | help: use a new generic type parameter, constrained by `Trait` | -LL | fn bar(_: &'a T); - | ++++++++++ ~ +LL - fn bar(_: &'a Trait); +LL + fn bar(_: &'a T); + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn bar(_: &'a impl Trait); @@ -233,8 +235,9 @@ LL | fn alice<'a>(_: &Trait); | help: use a new generic type parameter, constrained by `Trait` | -LL | fn alice<'a, T: Trait>(_: &T); - | ++++++++++ ~ +LL - fn alice<'a>(_: &Trait); +LL + fn alice<'a, T: Trait>(_: &T); + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn alice<'a>(_: &impl Trait); @@ -252,8 +255,9 @@ LL | fn bob<'a>(_: &'a Trait); | help: use a new generic type parameter, constrained by `Trait` | -LL | fn bob<'a, T: Trait>(_: &'a T); - | ++++++++++ ~ +LL - fn bob<'a>(_: &'a Trait); +LL + fn bob<'a, T: Trait>(_: &'a T); + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn bob<'a>(_: &'a impl Trait); @@ -275,8 +279,9 @@ LL | fn cat() -> &impl Trait; | ++++ help: alternatively, you can return an owned trait object | -LL | fn cat() -> Box; - | ~~~~~~~~~~~~~~ +LL - fn cat() -> &Trait; +LL + fn cat() -> Box; + | error[E0782]: expected a type, found a trait --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:69:22 @@ -290,8 +295,9 @@ LL | fn dog<'a>() -> &impl Trait { | ++++ help: alternatively, you can return an owned trait object | -LL | fn dog<'a>() -> Box { - | ~~~~~~~~~~~~~~ +LL - fn dog<'a>() -> &Trait { +LL + fn dog<'a>() -> Box { + | error[E0782]: expected a type, found a trait --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:75:24 @@ -305,8 +311,9 @@ LL | fn kitten() -> &'a impl Trait { | ++++ help: alternatively, you can return an owned trait object | -LL | fn kitten() -> Box { - | ~~~~~~~~~~~~~~ +LL - fn kitten() -> &'a Trait { +LL + fn kitten() -> Box { + | error[E0782]: expected a type, found a trait --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:81:27 @@ -320,8 +327,9 @@ LL | fn puppy<'a>() -> &'a impl Trait { | ++++ help: alternatively, you can return an owned trait object | -LL | fn puppy<'a>() -> Box { - | ~~~~~~~~~~~~~~ +LL - fn puppy<'a>() -> &'a Trait { +LL + fn puppy<'a>() -> Box { + | error[E0782]: expected a type, found a trait --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:86:25 @@ -335,8 +343,9 @@ LL | fn parrot() -> &mut impl Trait { | ++++ help: alternatively, you can return an owned trait object | -LL | fn parrot() -> Box { - | ~~~~~~~~~~~~~~ +LL - fn parrot() -> &mut Trait { +LL + fn parrot() -> Box { + | error[E0782]: expected a type, found a trait --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:93:12 @@ -346,8 +355,9 @@ LL | fn foo(_: &Trait) {} | help: use a new generic type parameter, constrained by `Trait` | -LL | fn foo(_: &T) {} - | ++++++++++ ~ +LL - fn foo(_: &Trait) {} +LL + fn foo(_: &T) {} + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn foo(_: &impl Trait) {} @@ -365,8 +375,9 @@ LL | fn bar(_: &'a Trait) {} | help: use a new generic type parameter, constrained by `Trait` | -LL | fn bar(_: &'a T) {} - | ++++++++++ ~ +LL - fn bar(_: &'a Trait) {} +LL + fn bar(_: &'a T) {} + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn bar(_: &'a impl Trait) {} @@ -384,8 +395,9 @@ LL | fn alice<'a>(_: &Trait) {} | help: use a new generic type parameter, constrained by `Trait` | -LL | fn alice<'a, T: Trait>(_: &T) {} - | ++++++++++ ~ +LL - fn alice<'a>(_: &Trait) {} +LL + fn alice<'a, T: Trait>(_: &T) {} + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn alice<'a>(_: &impl Trait) {} @@ -403,8 +415,9 @@ LL | fn bob<'a>(_: &'a Trait) {} | help: use a new generic type parameter, constrained by `Trait` | -LL | fn bob<'a, T: Trait>(_: &'a T) {} - | ++++++++++ ~ +LL - fn bob<'a>(_: &'a Trait) {} +LL + fn bob<'a, T: Trait>(_: &'a T) {} + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn bob<'a>(_: &'a impl Trait) {} @@ -426,8 +439,9 @@ LL | fn cat() -> &impl Trait { | ++++ help: alternatively, you can return an owned trait object | -LL | fn cat() -> Box { - | ~~~~~~~~~~~~~~ +LL - fn cat() -> &Trait { +LL + fn cat() -> Box { + | error[E0782]: expected a type, found a trait --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:116:18 @@ -441,8 +455,9 @@ LL | fn dog<'a>() -> &impl Trait { | ++++ help: alternatively, you can return an owned trait object | -LL | fn dog<'a>() -> Box { - | ~~~~~~~~~~~~~~ +LL - fn dog<'a>() -> &Trait { +LL + fn dog<'a>() -> Box { + | error[E0782]: expected a type, found a trait --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:122:20 @@ -456,8 +471,9 @@ LL | fn kitten() -> &'a impl Trait { | ++++ help: alternatively, you can return an owned trait object | -LL | fn kitten() -> Box { - | ~~~~~~~~~~~~~~ +LL - fn kitten() -> &'a Trait { +LL + fn kitten() -> Box { + | error[E0782]: expected a type, found a trait --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:128:23 @@ -471,8 +487,9 @@ LL | fn puppy<'a>() -> &'a impl Trait { | ++++ help: alternatively, you can return an owned trait object | -LL | fn puppy<'a>() -> Box { - | ~~~~~~~~~~~~~~ +LL - fn puppy<'a>() -> &'a Trait { +LL + fn puppy<'a>() -> Box { + | error[E0782]: expected a type, found a trait --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:133:21 @@ -486,8 +503,9 @@ LL | fn parrot() -> &mut impl Trait { | ++++ help: alternatively, you can return an owned trait object | -LL | fn parrot() -> Box { - | ~~~~~~~~~~~~~~ +LL - fn parrot() -> &mut Trait { +LL + fn parrot() -> Box { + | error[E0782]: expected a type, found a trait --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:8:16 @@ -497,8 +515,9 @@ LL | fn foo(_: &Trait) {} | help: use a new generic type parameter, constrained by `Trait` | -LL | fn foo(_: &T) {} - | ++++++++++ ~ +LL - fn foo(_: &Trait) {} +LL + fn foo(_: &T) {} + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn foo(_: &impl Trait) {} @@ -516,8 +535,9 @@ LL | fn bar(self, _: &'a Trait) {} | help: use a new generic type parameter, constrained by `Trait` | -LL | fn bar(self, _: &'a T) {} - | ++++++++++ ~ +LL - fn bar(self, _: &'a Trait) {} +LL + fn bar(self, _: &'a T) {} + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn bar(self, _: &'a impl Trait) {} @@ -535,8 +555,9 @@ LL | fn alice<'a>(&self, _: &Trait) {} | help: use a new generic type parameter, constrained by `Trait` | -LL | fn alice<'a, T: Trait>(&self, _: &T) {} - | ++++++++++ ~ +LL - fn alice<'a>(&self, _: &Trait) {} +LL + fn alice<'a, T: Trait>(&self, _: &T) {} + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn alice<'a>(&self, _: &impl Trait) {} @@ -554,8 +575,9 @@ LL | fn bob<'a>(_: &'a Trait) {} | help: use a new generic type parameter, constrained by `Trait` | -LL | fn bob<'a, T: Trait>(_: &'a T) {} - | ++++++++++ ~ +LL - fn bob<'a>(_: &'a Trait) {} +LL + fn bob<'a, T: Trait>(_: &'a T) {} + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn bob<'a>(_: &'a impl Trait) {} @@ -577,8 +599,9 @@ LL | fn cat() -> &impl Trait { | ++++ help: alternatively, you can return an owned trait object | -LL | fn cat() -> Box { - | ~~~~~~~~~~~~~~ +LL - fn cat() -> &Trait { +LL + fn cat() -> Box { + | error[E0782]: expected a type, found a trait --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:27:22 @@ -592,8 +615,9 @@ LL | fn dog<'a>() -> &impl Trait { | ++++ help: alternatively, you can return an owned trait object | -LL | fn dog<'a>() -> Box { - | ~~~~~~~~~~~~~~ +LL - fn dog<'a>() -> &Trait { +LL + fn dog<'a>() -> Box { + | error[E0782]: expected a type, found a trait --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:33:24 @@ -607,8 +631,9 @@ LL | fn kitten() -> &'a impl Trait { | ++++ help: alternatively, you can return an owned trait object | -LL | fn kitten() -> Box { - | ~~~~~~~~~~~~~~ +LL - fn kitten() -> &'a Trait { +LL + fn kitten() -> Box { + | error[E0782]: expected a type, found a trait --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:39:27 @@ -622,8 +647,9 @@ LL | fn puppy<'a>() -> &'a impl Trait { | ++++ help: alternatively, you can return an owned trait object | -LL | fn puppy<'a>() -> Box { - | ~~~~~~~~~~~~~~ +LL - fn puppy<'a>() -> &'a Trait { +LL + fn puppy<'a>() -> Box { + | error[E0782]: expected a type, found a trait --> $DIR/reference-to-bare-trait-in-fn-inputs-and-outputs-issue-125139.rs:44:25 @@ -637,8 +663,9 @@ LL | fn parrot() -> &mut impl Trait { | ++++ help: alternatively, you can return an owned trait object | -LL | fn parrot() -> Box { - | ~~~~~~~~~~~~~~ +LL - fn parrot() -> &mut Trait { +LL + fn parrot() -> Box { + | error: aborting due to 42 previous errors diff --git a/tests/ui/dyn-compatibility/sized-2.curr.stderr b/tests/ui/dyn-compatibility/sized-2.curr.stderr index 1017fde53d313..2d262072d5df2 100644 --- a/tests/ui/dyn-compatibility/sized-2.curr.stderr +++ b/tests/ui/dyn-compatibility/sized-2.curr.stderr @@ -1,28 +1,30 @@ -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/sized-2.rs:14:31 | LL | fn make_bar(t: &T) -> &dyn Bar { - | ^^^^^^^ `Bar` cannot be made into an object + | ^^^^^^^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/sized-2.rs:9:18 | LL | trait Bar - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | where Self : Sized | ^^^^^ ...because it requires `Self: Sized` -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/sized-2.rs:16:5 | LL | t - | ^ `Bar` cannot be made into an object + | ^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/sized-2.rs:9:18 | LL | trait Bar - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | where Self : Sized | ^^^^^ ...because it requires `Self: Sized` = note: required for the cast from `&T` to `&dyn Bar` diff --git a/tests/ui/dyn-compatibility/sized-2.dyn_compatible_for_dispatch.stderr b/tests/ui/dyn-compatibility/sized-2.dyn_compatible_for_dispatch.stderr index 534cf0f1b033f..1fbc10c0c3f8c 100644 --- a/tests/ui/dyn-compatibility/sized-2.dyn_compatible_for_dispatch.stderr +++ b/tests/ui/dyn-compatibility/sized-2.dyn_compatible_for_dispatch.stderr @@ -1,14 +1,15 @@ -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/sized-2.rs:16:5 | LL | t - | ^ `Bar` cannot be made into an object + | ^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/sized-2.rs:9:18 | LL | trait Bar - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | where Self : Sized | ^^^^^ ...because it requires `Self: Sized` = note: required for the cast from `&T` to `&dyn Bar` diff --git a/tests/ui/dyn-compatibility/sized.curr.stderr b/tests/ui/dyn-compatibility/sized.curr.stderr index 613833aad12ee..a197d96700511 100644 --- a/tests/ui/dyn-compatibility/sized.curr.stderr +++ b/tests/ui/dyn-compatibility/sized.curr.stderr @@ -1,30 +1,32 @@ -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/sized.rs:12:32 | LL | fn make_bar(t: &T) -> &dyn Bar { - | ^^^^^^^ `Bar` cannot be made into an object + | ^^^^^^^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/sized.rs:8:12 | LL | trait Bar: Sized { | --- ^^^^^ ...because it requires `Self: Sized` | | - | this trait cannot be made into an object... + | this trait is not dyn compatible... -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/sized.rs:14:5 | LL | t - | ^ `Bar` cannot be made into an object + | ^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/sized.rs:8:12 | LL | trait Bar: Sized { | --- ^^^^^ ...because it requires `Self: Sized` | | - | this trait cannot be made into an object... + | this trait is not dyn compatible... = note: required for the cast from `&T` to `&dyn Bar` error: aborting due to 2 previous errors diff --git a/tests/ui/dyn-compatibility/sized.dyn_compatible_for_dispatch.stderr b/tests/ui/dyn-compatibility/sized.dyn_compatible_for_dispatch.stderr index cf847bc157785..350c8992c6f96 100644 --- a/tests/ui/dyn-compatibility/sized.dyn_compatible_for_dispatch.stderr +++ b/tests/ui/dyn-compatibility/sized.dyn_compatible_for_dispatch.stderr @@ -1,16 +1,17 @@ -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/sized.rs:14:5 | LL | t - | ^ `Bar` cannot be made into an object + | ^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/sized.rs:8:12 | LL | trait Bar: Sized { | --- ^^^^^ ...because it requires `Self: Sized` | | - | this trait cannot be made into an object... + | this trait is not dyn compatible... = note: required for the cast from `&T` to `&dyn Bar` error: aborting due to 1 previous error diff --git a/tests/ui/dyn-compatibility/supertrait-mentions-GAT.rs b/tests/ui/dyn-compatibility/supertrait-mentions-GAT.rs index 14e00d2ef321d..9e5c1bfe4160a 100644 --- a/tests/ui/dyn-compatibility/supertrait-mentions-GAT.rs +++ b/tests/ui/dyn-compatibility/supertrait-mentions-GAT.rs @@ -9,7 +9,7 @@ trait GatTrait { trait SuperTrait: for<'a> GatTrait = T> { fn c(&self) -> dyn SuperTrait; //~^ ERROR associated item referring to unboxed trait object for its own trait - //~| ERROR the trait `SuperTrait` cannot be made into an object + //~| ERROR the trait `SuperTrait` is not dyn compatible } fn main() {} diff --git a/tests/ui/dyn-compatibility/supertrait-mentions-GAT.stderr b/tests/ui/dyn-compatibility/supertrait-mentions-GAT.stderr index ac5a5b28d94d4..582cf1af05468 100644 --- a/tests/ui/dyn-compatibility/supertrait-mentions-GAT.stderr +++ b/tests/ui/dyn-compatibility/supertrait-mentions-GAT.stderr @@ -17,23 +17,25 @@ LL | fn c(&self) -> dyn SuperTrait; | help: you might have meant to use `Self` to refer to the implementing type | -LL | fn c(&self) -> Self; - | ~~~~ +LL - fn c(&self) -> dyn SuperTrait; +LL + fn c(&self) -> Self; + | -error[E0038]: the trait `SuperTrait` cannot be made into an object +error[E0038]: the trait `SuperTrait` is not dyn compatible --> $DIR/supertrait-mentions-GAT.rs:10:20 | LL | fn c(&self) -> dyn SuperTrait; - | ^^^^^^^^^^^^^^^^^ `SuperTrait` cannot be made into an object + | ^^^^^^^^^^^^^^^^^ `SuperTrait` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/supertrait-mentions-GAT.rs:4:10 | LL | type Gat<'a> | ^^^ ...because it contains the generic associated type `Gat` ... LL | trait SuperTrait: for<'a> GatTrait = T> { - | ---------- this trait cannot be made into an object... + | ---------- this trait is not dyn compatible... = help: consider moving `Gat` to another trait error: aborting due to 3 previous errors diff --git a/tests/ui/dyn-compatibility/supertrait-mentions-Self.stderr b/tests/ui/dyn-compatibility/supertrait-mentions-Self.stderr index 6474b115c4628..2ba8e4611cbb0 100644 --- a/tests/ui/dyn-compatibility/supertrait-mentions-Self.stderr +++ b/tests/ui/dyn-compatibility/supertrait-mentions-Self.stderr @@ -18,23 +18,25 @@ help: consider relaxing the implicit `Sized` restriction LL | trait Bar { | ++++++++ -error[E0038]: the trait `Baz` cannot be made into an object - --> $DIR/supertrait-mentions-Self.rs:16:31 +error[E0038]: the trait `Baz` is not dyn compatible + --> $DIR/supertrait-mentions-Self.rs:16:35 | LL | fn make_baz(t: &T) -> &dyn Baz { - | ^^^^^^^ `Baz` cannot be made into an object + | ^^^ `Baz` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/supertrait-mentions-Self.rs:8:13 | LL | trait Baz : Bar { | --- ^^^^^^^^^ ...because it uses `Self` as a type parameter | | - | this trait cannot be made into an object... + | this trait is not dyn compatible... help: consider using an opaque type instead | -LL | fn make_baz(t: &T) -> &impl Baz { - | ~~~~ +LL - fn make_baz(t: &T) -> &dyn Baz { +LL + fn make_baz(t: &T) -> &impl Baz { + | error: aborting due to 2 previous errors diff --git a/tests/ui/dyn-compatibility/taint-const-eval.curr.stderr b/tests/ui/dyn-compatibility/taint-const-eval.curr.stderr index ef0abc1634257..7e80a1d2e4271 100644 --- a/tests/ui/dyn-compatibility/taint-const-eval.curr.stderr +++ b/tests/ui/dyn-compatibility/taint-const-eval.curr.stderr @@ -1,14 +1,15 @@ -error[E0038]: the trait `Qux` cannot be made into an object +error[E0038]: the trait `Qux` is not dyn compatible --> $DIR/taint-const-eval.rs:11:15 | LL | static FOO: &(dyn Qux + Sync) = "desc"; - | ^^^^^^^^^^^^^^ `Qux` cannot be made into an object + | ^^^^^^^^^^^^^^ `Qux` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/taint-const-eval.rs:8:8 | LL | trait Qux { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn bar(); | ^^^ ...because associated function `bar` has no `self` parameter help: consider turning `bar` into a method by giving it a `&self` argument @@ -20,17 +21,18 @@ help: alternatively, consider constraining `bar` so it does not apply to trait o LL | fn bar() where Self: Sized; | +++++++++++++++++ -error[E0038]: the trait `Qux` cannot be made into an object +error[E0038]: the trait `Qux` is not dyn compatible --> $DIR/taint-const-eval.rs:11:33 | LL | static FOO: &(dyn Qux + Sync) = "desc"; - | ^^^^^^ `Qux` cannot be made into an object + | ^^^^^^ `Qux` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/taint-const-eval.rs:8:8 | LL | trait Qux { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn bar(); | ^^^ ...because associated function `bar` has no `self` parameter = note: required for the cast from `&'static str` to `&'static (dyn Qux + Sync + 'static)` @@ -43,17 +45,18 @@ help: alternatively, consider constraining `bar` so it does not apply to trait o LL | fn bar() where Self: Sized; | +++++++++++++++++ -error[E0038]: the trait `Qux` cannot be made into an object +error[E0038]: the trait `Qux` is not dyn compatible --> $DIR/taint-const-eval.rs:11:15 | LL | static FOO: &(dyn Qux + Sync) = "desc"; - | ^^^^^^^^^^^^^^ `Qux` cannot be made into an object + | ^^^^^^^^^^^^^^ `Qux` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/taint-const-eval.rs:8:8 | LL | trait Qux { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn bar(); | ^^^ ...because associated function `bar` has no `self` parameter = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` diff --git a/tests/ui/dyn-compatibility/taint-const-eval.dyn_compatible_for_dispatch.stderr b/tests/ui/dyn-compatibility/taint-const-eval.dyn_compatible_for_dispatch.stderr index 14940365d2325..0bc7d0b14d3dd 100644 --- a/tests/ui/dyn-compatibility/taint-const-eval.dyn_compatible_for_dispatch.stderr +++ b/tests/ui/dyn-compatibility/taint-const-eval.dyn_compatible_for_dispatch.stderr @@ -1,14 +1,15 @@ -error[E0038]: the trait `Qux` cannot be made into an object +error[E0038]: the trait `Qux` is not dyn compatible --> $DIR/taint-const-eval.rs:11:33 | LL | static FOO: &(dyn Qux + Sync) = "desc"; - | ^^^^^^ `Qux` cannot be made into an object + | ^^^^^^ `Qux` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/taint-const-eval.rs:8:8 | LL | trait Qux { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn bar(); | ^^^ ...because associated function `bar` has no `self` parameter = note: required for the cast from `&'static str` to `&'static (dyn Qux + Sync + 'static)` diff --git a/tests/ui/dyn-compatibility/taint-const-eval.rs b/tests/ui/dyn-compatibility/taint-const-eval.rs index 9825ec0ca1c7c..2feae58080bc8 100644 --- a/tests/ui/dyn-compatibility/taint-const-eval.rs +++ b/tests/ui/dyn-compatibility/taint-const-eval.rs @@ -9,8 +9,8 @@ trait Qux { } static FOO: &(dyn Qux + Sync) = "desc"; -//~^ the trait `Qux` cannot be made into an object -//[curr]~| the trait `Qux` cannot be made into an object -//[curr]~| the trait `Qux` cannot be made into an object +//~^ the trait `Qux` is not dyn compatible +//[curr]~| the trait `Qux` is not dyn compatible +//[curr]~| the trait `Qux` is not dyn compatible fn main() {} diff --git a/tests/ui/dyn-compatibility/undispatchable-receiver-and-wc-references-Self.rs b/tests/ui/dyn-compatibility/undispatchable-receiver-and-wc-references-Self.rs index 5c71bd7769ce1..ec32bec7785ac 100644 --- a/tests/ui/dyn-compatibility/undispatchable-receiver-and-wc-references-Self.rs +++ b/tests/ui/dyn-compatibility/undispatchable-receiver-and-wc-references-Self.rs @@ -17,13 +17,13 @@ pub trait Fetcher: Send + Sync { } fn fetcher() -> Box { - //~^ ERROR the trait `Fetcher` cannot be made into an object + //~^ ERROR the trait `Fetcher` is not dyn compatible todo!() } pub fn foo() { let fetcher = fetcher(); - //~^ ERROR the trait `Fetcher` cannot be made into an object + //~^ ERROR the trait `Fetcher` is not dyn compatible let _ = fetcher.get(); - //~^ ERROR the trait `Fetcher` cannot be made into an object + //~^ ERROR the trait `Fetcher` is not dyn compatible } diff --git a/tests/ui/dyn-compatibility/undispatchable-receiver-and-wc-references-Self.stderr b/tests/ui/dyn-compatibility/undispatchable-receiver-and-wc-references-Self.stderr index 8d62ac9d923f9..1299167159e24 100644 --- a/tests/ui/dyn-compatibility/undispatchable-receiver-and-wc-references-Self.stderr +++ b/tests/ui/dyn-compatibility/undispatchable-receiver-and-wc-references-Self.stderr @@ -1,51 +1,54 @@ -error[E0038]: the trait `Fetcher` cannot be made into an object +error[E0038]: the trait `Fetcher` is not dyn compatible --> $DIR/undispatchable-receiver-and-wc-references-Self.rs:19:21 | LL | fn get<'a>(self: &'a Box) -> Pin> + 'a>> | ------------- help: consider changing method `get`'s `self` parameter to be `&self`: `&Self` ... LL | fn fetcher() -> Box { - | ^^^^^^^^^^^ `Fetcher` cannot be made into an object + | ^^^^^^^^^^^ `Fetcher` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/undispatchable-receiver-and-wc-references-Self.rs:11:22 | LL | pub trait Fetcher: Send + Sync { - | ------- this trait cannot be made into an object... + | ------- this trait is not dyn compatible... LL | fn get<'a>(self: &'a Box) -> Pin> + 'a>> | ^^^^^^^^^^^^^ ...because method `get`'s `self` parameter cannot be dispatched on -error[E0038]: the trait `Fetcher` cannot be made into an object +error[E0038]: the trait `Fetcher` is not dyn compatible --> $DIR/undispatchable-receiver-and-wc-references-Self.rs:25:19 | LL | fn get<'a>(self: &'a Box) -> Pin> + 'a>> | ------------- help: consider changing method `get`'s `self` parameter to be `&self`: `&Self` ... LL | let fetcher = fetcher(); - | ^^^^^^^^^ `Fetcher` cannot be made into an object + | ^^^^^^^^^ `Fetcher` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/undispatchable-receiver-and-wc-references-Self.rs:11:22 | LL | pub trait Fetcher: Send + Sync { - | ------- this trait cannot be made into an object... + | ------- this trait is not dyn compatible... LL | fn get<'a>(self: &'a Box) -> Pin> + 'a>> | ^^^^^^^^^^^^^ ...because method `get`'s `self` parameter cannot be dispatched on -error[E0038]: the trait `Fetcher` cannot be made into an object +error[E0038]: the trait `Fetcher` is not dyn compatible --> $DIR/undispatchable-receiver-and-wc-references-Self.rs:27:13 | LL | fn get<'a>(self: &'a Box) -> Pin> + 'a>> | ------------- help: consider changing method `get`'s `self` parameter to be `&self`: `&Self` ... LL | let _ = fetcher.get(); - | ^^^^^^^^^^^^^ `Fetcher` cannot be made into an object + | ^^^^^^^^^^^^^ `Fetcher` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/undispatchable-receiver-and-wc-references-Self.rs:11:22 | LL | pub trait Fetcher: Send + Sync { - | ------- this trait cannot be made into an object... + | ------- this trait is not dyn compatible... LL | fn get<'a>(self: &'a Box) -> Pin> + 'a>> | ^^^^^^^^^^^^^ ...because method `get`'s `self` parameter cannot be dispatched on diff --git a/tests/ui/dyn-keyword/dyn-2021-edition-error.stderr b/tests/ui/dyn-keyword/dyn-2021-edition-error.stderr index 6d1a1618ac033..8c4b809e76b04 100644 --- a/tests/ui/dyn-keyword/dyn-2021-edition-error.stderr +++ b/tests/ui/dyn-keyword/dyn-2021-edition-error.stderr @@ -6,8 +6,9 @@ LL | fn function(x: &SomeTrait, y: Box) { | help: use a new generic type parameter, constrained by `SomeTrait` | -LL | fn function(x: &T, y: Box) { - | ++++++++++++++ ~ +LL - fn function(x: &SomeTrait, y: Box) { +LL + fn function(x: &T, y: Box) { + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn function(x: &impl SomeTrait, y: Box) { diff --git a/tests/ui/dyn-keyword/misspelled-associated-item.rs b/tests/ui/dyn-keyword/misspelled-associated-item.rs new file mode 100644 index 0000000000000..8319e797d6214 --- /dev/null +++ b/tests/ui/dyn-keyword/misspelled-associated-item.rs @@ -0,0 +1,12 @@ +//@ edition: 2021 + +trait Trait { + fn typo() -> Self; +} + +fn main() { + let () = Trait::typoe(); + //~^ ERROR expected a type, found a trait + //~| HELP you can add the `dyn` keyword if you want a trait object + //~| HELP you may have misspelled this associated item +} diff --git a/tests/ui/dyn-keyword/misspelled-associated-item.stderr b/tests/ui/dyn-keyword/misspelled-associated-item.stderr new file mode 100644 index 0000000000000..0880100dc04fb --- /dev/null +++ b/tests/ui/dyn-keyword/misspelled-associated-item.stderr @@ -0,0 +1,19 @@ +error[E0782]: expected a type, found a trait + --> $DIR/misspelled-associated-item.rs:8:14 + | +LL | let () = Trait::typoe(); + | ^^^^^ + | +help: you can add the `dyn` keyword if you want a trait object + | +LL | let () = ::typoe(); + | ++++ + +help: you may have misspelled this associated item, causing `Trait` to be interpreted as a type rather than a trait + | +LL - let () = Trait::typoe(); +LL + let () = Trait::typo(); + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/dyn-star/no-unsize-coerce-dyn-trait.rs b/tests/ui/dyn-star/no-unsize-coerce-dyn-trait.rs index a4eb669e32104..1702fc1ed490e 100644 --- a/tests/ui/dyn-star/no-unsize-coerce-dyn-trait.rs +++ b/tests/ui/dyn-star/no-unsize-coerce-dyn-trait.rs @@ -1,5 +1,5 @@ -#![feature(dyn_star, trait_upcasting)] -//~^ WARN the feature `dyn_star` is incomplete and may not be safe to use and/or cause compiler crashes +#![expect(incomplete_features)] +#![feature(dyn_star)] trait A: B {} trait B {} diff --git a/tests/ui/dyn-star/no-unsize-coerce-dyn-trait.stderr b/tests/ui/dyn-star/no-unsize-coerce-dyn-trait.stderr index 1f7bfb1d5bdc5..289d85072e615 100644 --- a/tests/ui/dyn-star/no-unsize-coerce-dyn-trait.stderr +++ b/tests/ui/dyn-star/no-unsize-coerce-dyn-trait.stderr @@ -1,12 +1,3 @@ -warning: the feature `dyn_star` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/no-unsize-coerce-dyn-trait.rs:1:12 - | -LL | #![feature(dyn_star, trait_upcasting)] - | ^^^^^^^^ - | - = note: see issue #102425 for more information - = note: `#[warn(incomplete_features)]` on by default - error[E0308]: mismatched types --> $DIR/no-unsize-coerce-dyn-trait.rs:11:26 | @@ -18,6 +9,6 @@ LL | let y: Box = x; = note: expected struct `Box` found struct `Box` -error: aborting due to 1 previous error; 1 warning emitted +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/dyn-star/upcast.rs b/tests/ui/dyn-star/upcast.rs index e8e89fc5101b1..01e1b94f87e27 100644 --- a/tests/ui/dyn-star/upcast.rs +++ b/tests/ui/dyn-star/upcast.rs @@ -1,6 +1,6 @@ //@ known-bug: #104800 -#![feature(dyn_star, trait_upcasting)] +#![feature(dyn_star)] trait Foo: Bar { fn hello(&self); diff --git a/tests/ui/dyn-star/upcast.stderr b/tests/ui/dyn-star/upcast.stderr index 801e1c233c1ae..3f244d4b6ae8d 100644 --- a/tests/ui/dyn-star/upcast.stderr +++ b/tests/ui/dyn-star/upcast.stderr @@ -1,7 +1,7 @@ warning: the feature `dyn_star` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/upcast.rs:3:12 | -LL | #![feature(dyn_star, trait_upcasting)] +LL | #![feature(dyn_star)] | ^^^^^^^^ | = note: see issue #102425 for more information diff --git a/tests/ui/editions/auxiliary/nested_macro_rules_dep_2021.rs b/tests/ui/editions/auxiliary/nested_macro_rules_dep_2021.rs new file mode 100644 index 0000000000000..36d3e712dc0e9 --- /dev/null +++ b/tests/ui/editions/auxiliary/nested_macro_rules_dep_2021.rs @@ -0,0 +1,23 @@ +//@ edition: 2021 + +#[macro_export] +macro_rules! make_macro_with_input { + ($i:ident) => { + macro_rules! macro_inner_input { + () => { + pub fn $i() {} + }; + } + }; +} + +#[macro_export] +macro_rules! make_macro { + () => { + macro_rules! macro_inner { + () => { + pub fn gen() {} + }; + } + }; +} diff --git a/tests/ui/editions/auxiliary/nested_macro_rules_dep_2024.rs b/tests/ui/editions/auxiliary/nested_macro_rules_dep_2024.rs new file mode 100644 index 0000000000000..4012398fe6622 --- /dev/null +++ b/tests/ui/editions/auxiliary/nested_macro_rules_dep_2024.rs @@ -0,0 +1,23 @@ +//@ edition: 2024 + +#[macro_export] +macro_rules! make_macro_with_input { + ($i:ident) => { + macro_rules! macro_inner_input { + () => { + pub fn $i() {} + }; + } + }; +} + +#[macro_export] +macro_rules! make_macro { + () => { + macro_rules! macro_inner { + () => { + pub fn gen() {} + }; + } + }; +} diff --git a/tests/ui/editions/nested-macro-rules-edition.e2021.stderr b/tests/ui/editions/nested-macro-rules-edition.e2021.stderr new file mode 100644 index 0000000000000..eac80262364ba --- /dev/null +++ b/tests/ui/editions/nested-macro-rules-edition.e2021.stderr @@ -0,0 +1,27 @@ +error: expected identifier, found reserved keyword `gen` + --> $DIR/nested-macro-rules-edition.rs:30:5 + | +LL | macro_inner_input!{} + | ^^^^^^^^^^^^^^^^^^^^ expected identifier, found reserved keyword + | + = note: this error originates in the macro `macro_inner_input` (in Nightly builds, run with -Z macro-backtrace for more info) +help: escape `gen` to use it as an identifier + | +LL | nested_macro_rules_dep::make_macro_with_input!{r#gen} + | ++ + +error: expected identifier, found reserved keyword `gen` + --> $DIR/nested-macro-rules-edition.rs:35:5 + | +LL | macro_inner!{} + | ^^^^^^^^^^^^^^ expected identifier, found reserved keyword + | + = note: this error originates in the macro `macro_inner` (in Nightly builds, run with -Z macro-backtrace for more info) +help: escape `gen` to use it as an identifier + --> $DIR/auxiliary/nested_macro_rules_dep_2024.rs:19:24 + | +LL | pub fn r#gen() {} + | ++ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/editions/nested-macro-rules-edition.rs b/tests/ui/editions/nested-macro-rules-edition.rs new file mode 100644 index 0000000000000..28d568f77501b --- /dev/null +++ b/tests/ui/editions/nested-macro-rules-edition.rs @@ -0,0 +1,39 @@ +// This checks the behavior of how nested macro_rules definitions are handled +// with regards to edition spans. Prior to https://github.com/rust-lang/rust/pull/133274, +// the compiler would compile the inner macro with the edition of the local crate. +// Afterwards, it uses the edition where the macro was *defined*. +// +// Unfortunately macro_rules compiler discards the edition of any *input* that +// was used to generate the macro. This is possibly not the behavior that we +// want. If we want to keep with the philosophy that code should follow the +// edition rules of the crate where it is written, then presumably we would +// want the input tokens to retain the edition of where they were written. +// +// See https://github.com/rust-lang/rust/issues/135669 for more. +// +// This has two revisions, one where local=2021 and the dep=2024. The other +// revision is vice-versa. + +//@ revisions: e2021 e2024 +//@[e2021] edition:2021 +//@[e2024] edition:2024 +//@[e2021] aux-crate: nested_macro_rules_dep=nested_macro_rules_dep_2024.rs +//@[e2024] aux-crate: nested_macro_rules_dep=nested_macro_rules_dep_2021.rs +//@[e2024] check-pass + +mod with_input { + // If we change the macro_rules input behavior, then this should pass when + // local edition is 2021 because `gen` is written in a context with 2021 + // behavior. For local edition 2024, the reverse would be true and this + // should fail. + nested_macro_rules_dep::make_macro_with_input!{gen} + macro_inner_input!{} + //[e2021]~^ ERROR found reserved keyword +} +mod no_input { + nested_macro_rules_dep::make_macro!{} + macro_inner!{} + //[e2021]~^ ERROR found reserved keyword +} + +fn main() {} diff --git a/tests/ui/empty/empty-struct-braces-expr.stderr b/tests/ui/empty/empty-struct-braces-expr.stderr index 28c701443de4e..8ec8ecf46bf06 100644 --- a/tests/ui/empty/empty-struct-braces-expr.stderr +++ b/tests/ui/empty/empty-struct-braces-expr.stderr @@ -15,11 +15,12 @@ LL | pub struct XEmpty2; help: use struct literal syntax instead | LL | let e1 = Empty1 {}; - | ~~~~~~~~~ + | ++ help: a unit struct with a similar name exists | -LL | let e1 = XEmpty2; - | ~~~~~~~ +LL - let e1 = Empty1; +LL + let e1 = XEmpty2; + | error[E0423]: expected value, found struct `XEmpty1` --> $DIR/empty-struct-braces-expr.rs:22:15 @@ -37,11 +38,12 @@ LL | pub struct XEmpty2; help: use struct literal syntax instead | LL | let xe1 = XEmpty1 {}; - | ~~~~~~~~~~ + | ++ help: a unit struct with a similar name exists | -LL | let xe1 = XEmpty2; - | ~~~~~~~ +LL - let xe1 = XEmpty1; +LL + let xe1 = XEmpty2; + | error[E0423]: expected function, tuple struct or tuple variant, found struct `Empty1` --> $DIR/empty-struct-braces-expr.rs:16:14 @@ -59,12 +61,14 @@ LL | pub struct XEmpty2; | help: use struct literal syntax instead | -LL | let e1 = Empty1 {}; - | ~~~~~~~~~ +LL - let e1 = Empty1(); +LL + let e1 = Empty1 {}; + | help: a unit struct with a similar name exists | -LL | let e1 = XEmpty2(); - | ~~~~~~~ +LL - let e1 = Empty1(); +LL + let e1 = XEmpty2(); + | error[E0533]: expected value, found struct variant `E::Empty3` --> $DIR/empty-struct-braces-expr.rs:18:14 @@ -85,8 +89,9 @@ LL | let e3 = E::Empty3(); | help: you might have meant to create a new value of the struct | -LL | let e3 = E::Empty3 {}; - | ~~ +LL - let e3 = E::Empty3(); +LL + let e3 = E::Empty3 {}; + | error[E0423]: expected function, tuple struct or tuple variant, found struct `XEmpty1` --> $DIR/empty-struct-braces-expr.rs:23:15 @@ -103,12 +108,14 @@ LL | pub struct XEmpty2; | help: use struct literal syntax instead | -LL | let xe1 = XEmpty1 {}; - | ~~~~~~~~~~ +LL - let xe1 = XEmpty1(); +LL + let xe1 = XEmpty1 {}; + | help: a unit struct with a similar name exists | -LL | let xe1 = XEmpty2(); - | ~~~~~~~ +LL - let xe1 = XEmpty1(); +LL + let xe1 = XEmpty2(); + | error[E0599]: no variant or associated item named `Empty3` found for enum `empty_struct::XE` in the current scope --> $DIR/empty-struct-braces-expr.rs:25:19 @@ -119,7 +126,7 @@ LL | let xe3 = XE::Empty3; help: there is a variant with a similar name | LL | let xe3 = XE::XEmpty3; - | ~~~~~~~ + | + error[E0599]: no variant or associated item named `Empty3` found for enum `empty_struct::XE` in the current scope --> $DIR/empty-struct-braces-expr.rs:26:19 @@ -129,8 +136,9 @@ LL | let xe3 = XE::Empty3(); | help: there is a variant with a similar name | -LL | let xe3 = XE::XEmpty3 {}; - | ~~~~~~~~~~ +LL - let xe3 = XE::Empty3(); +LL + let xe3 = XE::XEmpty3 {}; + | error[E0599]: no variant named `Empty1` found for enum `empty_struct::XE` --> $DIR/empty-struct-braces-expr.rs:28:9 @@ -140,8 +148,9 @@ LL | XE::Empty1 {}; | help: there is a variant with a similar name | -LL | XE::XEmpty3 {}; - | ~~~~~~~~~~ +LL - XE::Empty1 {}; +LL + XE::XEmpty3 {}; + | error: aborting due to 9 previous errors diff --git a/tests/ui/empty/empty-struct-braces-pat-2.stderr b/tests/ui/empty/empty-struct-braces-pat-2.stderr index 7fb5cb2034a83..497dc9601a716 100644 --- a/tests/ui/empty/empty-struct-braces-pat-2.stderr +++ b/tests/ui/empty/empty-struct-braces-pat-2.stderr @@ -14,12 +14,14 @@ LL | pub struct XEmpty6(); | help: use struct pattern syntax instead | -LL | Empty1 {} => () - | ~~~~~~~~~ +LL - Empty1() => () +LL + Empty1 {} => () + | help: a tuple struct with a similar name exists | -LL | XEmpty6() => () - | ~~~~~~~ +LL - Empty1() => () +LL + XEmpty6() => () + | error[E0532]: expected tuple struct or tuple variant, found struct `XEmpty1` --> $DIR/empty-struct-braces-pat-2.rs:18:9 @@ -37,12 +39,14 @@ LL | pub struct XEmpty6(); | help: use struct pattern syntax instead | -LL | XEmpty1 {} => () - | ~~~~~~~~~~ +LL - XEmpty1() => () +LL + XEmpty1 {} => () + | help: a tuple struct with a similar name exists | -LL | XEmpty6() => () - | ~~~~~~~ +LL - XEmpty1() => () +LL + XEmpty6() => () + | error[E0532]: expected tuple struct or tuple variant, found struct `Empty1` --> $DIR/empty-struct-braces-pat-2.rs:21:9 @@ -60,12 +64,14 @@ LL | pub struct XEmpty6(); | help: use struct pattern syntax instead | -LL | Empty1 {} => () - | ~~~~~~~~~ +LL - Empty1(..) => () +LL + Empty1 {} => () + | help: a tuple struct with a similar name exists | -LL | XEmpty6(..) => () - | ~~~~~~~ +LL - Empty1(..) => () +LL + XEmpty6(..) => () + | error[E0532]: expected tuple struct or tuple variant, found struct `XEmpty1` --> $DIR/empty-struct-braces-pat-2.rs:24:9 @@ -83,12 +89,14 @@ LL | pub struct XEmpty6(); | help: use struct pattern syntax instead | -LL | XEmpty1 {} => () - | ~~~~~~~~~~ +LL - XEmpty1(..) => () +LL + XEmpty1 {} => () + | help: a tuple struct with a similar name exists | -LL | XEmpty6(..) => () - | ~~~~~~~ +LL - XEmpty1(..) => () +LL + XEmpty6(..) => () + | error: aborting due to 4 previous errors diff --git a/tests/ui/empty/empty-struct-braces-pat-3.stderr b/tests/ui/empty/empty-struct-braces-pat-3.stderr index b2ab7113347ff..6e9618da99d53 100644 --- a/tests/ui/empty/empty-struct-braces-pat-3.stderr +++ b/tests/ui/empty/empty-struct-braces-pat-3.stderr @@ -6,8 +6,9 @@ LL | E::Empty3() => () | help: use the struct variant pattern syntax | -LL | E::Empty3 {} => () - | ~~ +LL - E::Empty3() => () +LL + E::Empty3 {} => () + | error[E0164]: expected tuple struct or tuple variant, found struct variant `XE::XEmpty3` --> $DIR/empty-struct-braces-pat-3.rs:21:9 @@ -17,8 +18,9 @@ LL | XE::XEmpty3() => () | help: use the struct variant pattern syntax | -LL | XE::XEmpty3 {} => () - | ~~ +LL - XE::XEmpty3() => () +LL + XE::XEmpty3 {} => () + | error[E0164]: expected tuple struct or tuple variant, found struct variant `E::Empty3` --> $DIR/empty-struct-braces-pat-3.rs:25:9 @@ -28,8 +30,9 @@ LL | E::Empty3(..) => () | help: use the struct variant pattern syntax | -LL | E::Empty3 {} => () - | ~~ +LL - E::Empty3(..) => () +LL + E::Empty3 {} => () + | error[E0164]: expected tuple struct or tuple variant, found struct variant `XE::XEmpty3` --> $DIR/empty-struct-braces-pat-3.rs:29:9 @@ -39,8 +42,9 @@ LL | XE::XEmpty3(..) => () | help: use the struct variant pattern syntax | -LL | XE::XEmpty3 {} => () - | ~~ +LL - XE::XEmpty3(..) => () +LL + XE::XEmpty3 {} => () + | error: aborting due to 4 previous errors diff --git a/tests/ui/empty/empty-struct-tuple-pat.stderr b/tests/ui/empty/empty-struct-tuple-pat.stderr index 45001c7975324..09b454653f62f 100644 --- a/tests/ui/empty/empty-struct-tuple-pat.stderr +++ b/tests/ui/empty/empty-struct-tuple-pat.stderr @@ -47,11 +47,12 @@ LL | XEmpty5(), help: use the tuple variant pattern syntax instead | LL | XE::XEmpty5() => (), - | ~~~~~~~~~~~~~ + | ++ help: a unit variant with a similar name exists | -LL | XE::XEmpty4 => (), - | ~~~~~~~ +LL - XE::XEmpty5 => (), +LL + XE::XEmpty4 => (), + | error: aborting due to 4 previous errors diff --git a/tests/ui/empty/empty-struct-unit-pat.stderr b/tests/ui/empty/empty-struct-unit-pat.stderr index 5c0b4cffa9469..09a476423c8da 100644 --- a/tests/ui/empty/empty-struct-unit-pat.stderr +++ b/tests/ui/empty/empty-struct-unit-pat.stderr @@ -14,12 +14,14 @@ LL | pub struct XEmpty6(); | help: use this syntax instead | -LL | Empty2 => () - | ~~~~~~ +LL - Empty2() => () +LL + Empty2 => () + | help: a tuple struct with a similar name exists | -LL | XEmpty6() => () - | ~~~~~~~ +LL - Empty2() => () +LL + XEmpty6() => () + | error[E0532]: expected tuple struct or tuple variant, found unit struct `XEmpty2` --> $DIR/empty-struct-unit-pat.rs:24:9 @@ -36,12 +38,14 @@ LL | pub struct XEmpty6(); | help: use this syntax instead | -LL | XEmpty2 => () - | ~~~~~~~ +LL - XEmpty2() => () +LL + XEmpty2 => () + | help: a tuple struct with a similar name exists | -LL | XEmpty6() => () - | ~~~~~~~ +LL - XEmpty2() => () +LL + XEmpty6() => () + | error[E0532]: expected tuple struct or tuple variant, found unit struct `Empty2` --> $DIR/empty-struct-unit-pat.rs:28:9 @@ -59,12 +63,14 @@ LL | pub struct XEmpty6(); | help: use this syntax instead | -LL | Empty2 => () - | ~~~~~~ +LL - Empty2(..) => () +LL + Empty2 => () + | help: a tuple struct with a similar name exists | -LL | XEmpty6(..) => () - | ~~~~~~~ +LL - Empty2(..) => () +LL + XEmpty6(..) => () + | error[E0532]: expected tuple struct or tuple variant, found unit struct `XEmpty2` --> $DIR/empty-struct-unit-pat.rs:32:9 @@ -81,12 +87,14 @@ LL | pub struct XEmpty6(); | help: use this syntax instead | -LL | XEmpty2 => () - | ~~~~~~~ +LL - XEmpty2(..) => () +LL + XEmpty2 => () + | help: a tuple struct with a similar name exists | -LL | XEmpty6(..) => () - | ~~~~~~~ +LL - XEmpty2(..) => () +LL + XEmpty6(..) => () + | error[E0532]: expected tuple struct or tuple variant, found unit variant `E::Empty4` --> $DIR/empty-struct-unit-pat.rs:37:9 @@ -112,12 +120,14 @@ LL | XEmpty5(), | help: use this syntax instead | -LL | XE::XEmpty4 => (), - | ~~~~~~~~~~~ +LL - XE::XEmpty4() => (), +LL + XE::XEmpty4 => (), + | help: a tuple variant with a similar name exists | -LL | XE::XEmpty5() => (), - | ~~~~~~~ +LL - XE::XEmpty4() => (), +LL + XE::XEmpty5() => (), + | error[E0532]: expected tuple struct or tuple variant, found unit variant `E::Empty4` --> $DIR/empty-struct-unit-pat.rs:46:9 @@ -143,12 +153,14 @@ LL | XEmpty5(), | help: use this syntax instead | -LL | XE::XEmpty4 => (), - | ~~~~~~~~~~~ +LL - XE::XEmpty4(..) => (), +LL + XE::XEmpty4 => (), + | help: a tuple variant with a similar name exists | -LL | XE::XEmpty5(..) => (), - | ~~~~~~~ +LL - XE::XEmpty4(..) => (), +LL + XE::XEmpty5(..) => (), + | error: aborting due to 8 previous errors diff --git a/tests/ui/enum-discriminant/discriminant-ill-typed.stderr b/tests/ui/enum-discriminant/discriminant-ill-typed.stderr index 2757145285c79..e9508b7fe962d 100644 --- a/tests/ui/enum-discriminant/discriminant-ill-typed.stderr +++ b/tests/ui/enum-discriminant/discriminant-ill-typed.stderr @@ -6,8 +6,9 @@ LL | OhNo = 0_u8, | help: change the type of the numeric literal from `u8` to `i8` | -LL | OhNo = 0_i8, - | ~~ +LL - OhNo = 0_u8, +LL + OhNo = 0_i8, + | error[E0308]: mismatched types --> $DIR/discriminant-ill-typed.rs:28:16 @@ -17,8 +18,9 @@ LL | OhNo = 0_i8, | help: change the type of the numeric literal from `i8` to `u8` | -LL | OhNo = 0_u8, - | ~~ +LL - OhNo = 0_i8, +LL + OhNo = 0_u8, + | error[E0308]: mismatched types --> $DIR/discriminant-ill-typed.rs:41:16 @@ -28,8 +30,9 @@ LL | OhNo = 0_u16, | help: change the type of the numeric literal from `u16` to `i16` | -LL | OhNo = 0_i16, - | ~~~ +LL - OhNo = 0_u16, +LL + OhNo = 0_i16, + | error[E0308]: mismatched types --> $DIR/discriminant-ill-typed.rs:54:16 @@ -39,8 +42,9 @@ LL | OhNo = 0_i16, | help: change the type of the numeric literal from `i16` to `u16` | -LL | OhNo = 0_u16, - | ~~~ +LL - OhNo = 0_i16, +LL + OhNo = 0_u16, + | error[E0308]: mismatched types --> $DIR/discriminant-ill-typed.rs:67:16 @@ -50,8 +54,9 @@ LL | OhNo = 0_u32, | help: change the type of the numeric literal from `u32` to `i32` | -LL | OhNo = 0_i32, - | ~~~ +LL - OhNo = 0_u32, +LL + OhNo = 0_i32, + | error[E0308]: mismatched types --> $DIR/discriminant-ill-typed.rs:80:16 @@ -61,8 +66,9 @@ LL | OhNo = 0_i32, | help: change the type of the numeric literal from `i32` to `u32` | -LL | OhNo = 0_u32, - | ~~~ +LL - OhNo = 0_i32, +LL + OhNo = 0_u32, + | error[E0308]: mismatched types --> $DIR/discriminant-ill-typed.rs:93:16 @@ -72,8 +78,9 @@ LL | OhNo = 0_u64, | help: change the type of the numeric literal from `u64` to `i64` | -LL | OhNo = 0_i64, - | ~~~ +LL - OhNo = 0_u64, +LL + OhNo = 0_i64, + | error[E0308]: mismatched types --> $DIR/discriminant-ill-typed.rs:106:16 @@ -83,8 +90,9 @@ LL | OhNo = 0_i64, | help: change the type of the numeric literal from `i64` to `u64` | -LL | OhNo = 0_u64, - | ~~~ +LL - OhNo = 0_i64, +LL + OhNo = 0_u64, + | error: aborting due to 8 previous errors diff --git a/tests/ui/enum-discriminant/eval-error.rs b/tests/ui/enum-discriminant/eval-error.rs new file mode 100644 index 0000000000000..08b71d52a8b03 --- /dev/null +++ b/tests/ui/enum-discriminant/eval-error.rs @@ -0,0 +1,37 @@ +union Foo { + a: str, + //~^ ERROR the size for values of type `str` cannot be known at compilation time + //~| ERROR field must implement `Copy` or be wrapped in `ManuallyDrop<...>` +} + +enum Bar { + Boo = { + let _: Option = None; //~ ERROR evaluation of constant value failed + 0 + }, +} + +union Foo2 {} +//~^ ERROR unions cannot have zero fields + +enum Bar2 { + Boo = { + let _: Option = None; + 0 + }, +} + +#[repr(u8, packed)] +//~^ ERROR attribute should be applied to a struct or union +enum Foo3 { + A +} + +enum Bar3 { + Boo = { + let _: Option = None; + 0 + }, +} + +fn main() {} diff --git a/tests/ui/enum-discriminant/eval-error.stderr b/tests/ui/enum-discriminant/eval-error.stderr new file mode 100644 index 0000000000000..6bec2c8b420f7 --- /dev/null +++ b/tests/ui/enum-discriminant/eval-error.stderr @@ -0,0 +1,57 @@ +error: unions cannot have zero fields + --> $DIR/eval-error.rs:14:1 + | +LL | union Foo2 {} + | ^^^^^^^^^^^^^ + +error[E0517]: attribute should be applied to a struct or union + --> $DIR/eval-error.rs:24:12 + | +LL | #[repr(u8, packed)] + | ^^^^^^ +LL | +LL | / enum Foo3 { +LL | | A +LL | | } + | |_- not a struct or union + +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/eval-error.rs:2:8 + | +LL | a: str, + | ^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` + = note: no field of a union may have a dynamically sized type + = help: change the field's type to have a statically known size +help: borrowed types always have a statically known size + | +LL | a: &str, + | + +help: the `Box` type always has a statically known size and allocates its contents in the heap + | +LL | a: Box, + | ++++ + + +error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union + --> $DIR/eval-error.rs:2:5 + | +LL | a: str, + | ^^^^^^ + | + = note: union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>` +help: wrap the field type in `ManuallyDrop<...>` + | +LL | a: std::mem::ManuallyDrop, + | +++++++++++++++++++++++ + + +error[E0080]: evaluation of constant value failed + --> $DIR/eval-error.rs:9:30 + | +LL | let _: Option = None; + | ^^^^ the type `Foo` has an unknown layout + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0080, E0277, E0517, E0740. +For more information about an error, try `rustc --explain E0080`. diff --git a/tests/ui/enum/assoc-fn-call-on-variant.stderr b/tests/ui/enum/assoc-fn-call-on-variant.stderr index 47fc630c92394..5318025f55458 100644 --- a/tests/ui/enum/assoc-fn-call-on-variant.stderr +++ b/tests/ui/enum/assoc-fn-call-on-variant.stderr @@ -6,8 +6,9 @@ LL | E::A::f(); | help: there is an enum variant `E::A`; try using the variant's enum | -LL | E::f(); - | ~ +LL - E::A::f(); +LL + E::f(); + | error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-48838.rs b/tests/ui/enum/closure-in-enum-issue-48838.rs similarity index 100% rename from tests/ui/issues/issue-48838.rs rename to tests/ui/enum/closure-in-enum-issue-48838.rs diff --git a/tests/ui/enum/closure-in-enum-issue-48838.stderr b/tests/ui/enum/closure-in-enum-issue-48838.stderr new file mode 100644 index 0000000000000..17e6c3433343f --- /dev/null +++ b/tests/ui/enum/closure-in-enum-issue-48838.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/closure-in-enum-issue-48838.rs:2:14 + | +LL | Square = |x| x, + | ^^^^^ expected `isize`, found closure + | + = note: expected type `isize` + found closure `{closure@$DIR/closure-in-enum-issue-48838.rs:2:14: 2:17}` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/issues/issue-40350.rs b/tests/ui/enum/enum-inside-enum-issue-40350.rs similarity index 100% rename from tests/ui/issues/issue-40350.rs rename to tests/ui/enum/enum-inside-enum-issue-40350.rs diff --git a/tests/ui/env-macro/error-recovery-issue-55897.stderr b/tests/ui/env-macro/error-recovery-issue-55897.stderr index 5a20bf8b16869..f1cacf5420eb6 100644 --- a/tests/ui/env-macro/error-recovery-issue-55897.stderr +++ b/tests/ui/env-macro/error-recovery-issue-55897.stderr @@ -31,7 +31,7 @@ LL | use env; help: consider importing this module instead | LL | use std::env; - | ~~~~~~~~ + | +++++ error: aborting due to 4 previous errors diff --git a/tests/ui/error-codes/E0027.stderr b/tests/ui/error-codes/E0027.stderr index 7bbafcf0a27ab..3a1ebf2971946 100644 --- a/tests/ui/error-codes/E0027.stderr +++ b/tests/ui/error-codes/E0027.stderr @@ -7,15 +7,15 @@ LL | Dog { age: x } => {} help: include the missing field in the pattern | LL | Dog { age: x, name } => {} - | ~~~~~~~~ + | ++++++ help: if you don't care about this missing field, you can explicitly ignore it | LL | Dog { age: x, name: _ } => {} - | ~~~~~~~~~~~ + | +++++++++ help: or always ignore missing fields here | LL | Dog { age: x, .. } => {} - | ~~~~~~ + | ++++ error[E0027]: pattern does not mention field `age` --> $DIR/E0027.rs:15:9 @@ -25,16 +25,19 @@ LL | Dog { name: x, } => {} | help: include the missing field in the pattern | -LL | Dog { name: x, age } => {} - | ~~~~~~~ +LL - Dog { name: x, } => {} +LL + Dog { name: x, age } => {} + | help: if you don't care about this missing field, you can explicitly ignore it | -LL | Dog { name: x, age: _ } => {} - | ~~~~~~~~~~ +LL - Dog { name: x, } => {} +LL + Dog { name: x, age: _ } => {} + | help: or always ignore missing fields here | -LL | Dog { name: x, .. } => {} - | ~~~~~~ +LL - Dog { name: x, } => {} +LL + Dog { name: x, .. } => {} + | error[E0027]: pattern does not mention field `age` --> $DIR/E0027.rs:19:9 @@ -44,16 +47,19 @@ LL | Dog { name: x , } => {} | help: include the missing field in the pattern | -LL | Dog { name: x, age } => {} - | ~~~~~~~ +LL - Dog { name: x , } => {} +LL + Dog { name: x, age } => {} + | help: if you don't care about this missing field, you can explicitly ignore it | -LL | Dog { name: x, age: _ } => {} - | ~~~~~~~~~~ +LL - Dog { name: x , } => {} +LL + Dog { name: x, age: _ } => {} + | help: or always ignore missing fields here | -LL | Dog { name: x, .. } => {} - | ~~~~~~ +LL - Dog { name: x , } => {} +LL + Dog { name: x, .. } => {} + | error[E0027]: pattern does not mention fields `name`, `age` --> $DIR/E0027.rs:22:9 @@ -63,16 +69,19 @@ LL | Dog {} => {} | help: include the missing fields in the pattern | -LL | Dog { name, age } => {} - | ~~~~~~~~~~~~~ +LL - Dog {} => {} +LL + Dog { name, age } => {} + | help: if you don't care about these missing fields, you can explicitly ignore them | -LL | Dog { name: _, age: _ } => {} - | ~~~~~~~~~~~~~~~~~~~ +LL - Dog {} => {} +LL + Dog { name: _, age: _ } => {} + | help: or always ignore missing fields here | -LL | Dog { .. } => {} - | ~~~~~~ +LL - Dog {} => {} +LL + Dog { .. } => {} + | error: aborting due to 4 previous errors diff --git a/tests/ui/error-codes/E0034.stderr b/tests/ui/error-codes/E0034.stderr index 48b8efcf8a29b..434518741fc4e 100644 --- a/tests/ui/error-codes/E0034.stderr +++ b/tests/ui/error-codes/E0034.stderr @@ -16,10 +16,12 @@ LL | fn foo() {} | ^^^^^^^^ help: use fully-qualified syntax to disambiguate | -LL | ::foo() - | ~~~~~~~~~~~~~~~~~~ -LL | ::foo() - | ~~~~~~~~~~~~~~~~~~ +LL - Test::foo() +LL + ::foo() + | +LL - Test::foo() +LL + ::foo() + | error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0038.stderr b/tests/ui/error-codes/E0038.stderr index 54b489c655f64..63a5249a38645 100644 --- a/tests/ui/error-codes/E0038.stderr +++ b/tests/ui/error-codes/E0038.stderr @@ -1,29 +1,31 @@ -error[E0038]: the trait `Trait` cannot be made into an object +error[E0038]: the trait `Trait` is not dyn compatible --> $DIR/E0038.rs:5:20 | LL | fn call_foo(x: Box) { - | ^^^^^^^^^ `Trait` cannot be made into an object + | ^^^^^^^^^ `Trait` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/E0038.rs:2:22 | LL | trait Trait { - | ----- this trait cannot be made into an object... + | ----- this trait is not dyn compatible... LL | fn foo(&self) -> Self; | ^^^^ ...because method `foo` references the `Self` type in its return type = help: consider moving `foo` to another trait -error[E0038]: the trait `Trait` cannot be made into an object +error[E0038]: the trait `Trait` is not dyn compatible --> $DIR/E0038.rs:7:13 | LL | let y = x.foo(); - | ^^^^^^^ `Trait` cannot be made into an object + | ^^^^^^^ `Trait` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/E0038.rs:2:22 | LL | trait Trait { - | ----- this trait cannot be made into an object... + | ----- this trait is not dyn compatible... LL | fn foo(&self) -> Self; | ^^^^ ...because method `foo` references the `Self` type in its return type = help: consider moving `foo` to another trait diff --git a/tests/ui/error-codes/E0040.stderr b/tests/ui/error-codes/E0040.stderr index 00cccb07c0f55..674c2b70b031e 100644 --- a/tests/ui/error-codes/E0040.stderr +++ b/tests/ui/error-codes/E0040.stderr @@ -6,8 +6,9 @@ LL | x.drop(); | help: consider using `drop` function | -LL | drop(x); - | +++++ ~ +LL - x.drop(); +LL + drop(x); + | error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0054.stderr b/tests/ui/error-codes/E0054.stderr index be35242ad7253..a2e618d9b06b2 100644 --- a/tests/ui/error-codes/E0054.stderr +++ b/tests/ui/error-codes/E0054.stderr @@ -6,8 +6,9 @@ LL | let x_is_nonzero = x as bool; | help: compare with zero instead | -LL | let x_is_nonzero = x != 0; - | ~~~~ +LL - let x_is_nonzero = x as bool; +LL + let x_is_nonzero = x != 0; + | error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0057.stderr b/tests/ui/error-codes/E0057.stderr index ef6e2908b939f..35bd842b2cf4c 100644 --- a/tests/ui/error-codes/E0057.stderr +++ b/tests/ui/error-codes/E0057.stderr @@ -11,8 +11,9 @@ LL | let f = |x| x * 3; | ^^^ help: provide the argument | -LL | let a = f(/* x */); - | ~~~~~~~~~ +LL - let a = f(); +LL + let a = f(/* x */); + | error[E0057]: this function takes 1 argument but 2 arguments were supplied --> $DIR/E0057.rs:5:13 diff --git a/tests/ui/error-codes/E0060.stderr b/tests/ui/error-codes/E0060.stderr index 8387b15b97087..fc52c6fc5ea50 100644 --- a/tests/ui/error-codes/E0060.stderr +++ b/tests/ui/error-codes/E0060.stderr @@ -8,11 +8,12 @@ note: function defined here --> $DIR/E0060.rs:2:8 | LL | fn printf(_: *const u8, ...) -> u32; - | ^^^^^^ + | ^^^^^^ - help: provide the argument | -LL | unsafe { printf(/* *const u8 */); } - | ~~~~~~~~~~~~~~~~~ +LL - unsafe { printf(); } +LL + unsafe { printf(/* *const u8 */); } + | error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0061.stderr b/tests/ui/error-codes/E0061.stderr index 7b180c0712084..b70d607ebeb64 100644 --- a/tests/ui/error-codes/E0061.stderr +++ b/tests/ui/error-codes/E0061.stderr @@ -8,11 +8,12 @@ note: function defined here --> $DIR/E0061.rs:1:4 | LL | fn f(a: u16, b: &str) {} - | ^ ------ ------- + | ^ ------- help: provide the argument | -LL | f(0, /* &str */); - | ~~~~~~~~~~~~~~~ +LL - f(0); +LL + f(0, /* &str */); + | error[E0061]: this function takes 1 argument but 0 arguments were supplied --> $DIR/E0061.rs:9:5 @@ -27,8 +28,9 @@ LL | fn f2(a: u16) {} | ^^ ------ help: provide the argument | -LL | f2(/* u16 */); - | ~~~~~~~~~~~ +LL - f2(); +LL + f2(/* u16 */); + | error: aborting due to 2 previous errors diff --git a/tests/ui/error-codes/E0121.stderr b/tests/ui/error-codes/E0121.stderr index 023d7e011bf3a..b169373f64399 100644 --- a/tests/ui/error-codes/E0121.stderr +++ b/tests/ui/error-codes/E0121.stderr @@ -11,10 +11,13 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/E0121.rs:3:13 | LL | static BAR: _ = "test"; - | ^ - | | - | not allowed in type signatures - | help: replace with the correct type: `&str` + | ^ not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - static BAR: _ = "test"; +LL + static BAR: &str = "test"; + | error: aborting due to 2 previous errors diff --git a/tests/ui/error-codes/E0132.rs b/tests/ui/error-codes/E0132.rs deleted file mode 100644 index fb5e5d7b95a3c..0000000000000 --- a/tests/ui/error-codes/E0132.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![feature(start)] - -#[start] -fn f< T >() {} //~ ERROR E0132 - -fn main() { -} diff --git a/tests/ui/error-codes/E0132.stderr b/tests/ui/error-codes/E0132.stderr deleted file mode 100644 index b1990afa3ae18..0000000000000 --- a/tests/ui/error-codes/E0132.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0132]: `#[start]` function is not allowed to have type parameters - --> $DIR/E0132.rs:4:5 - | -LL | fn f< T >() {} - | ^^^^^ `#[start]` function cannot have type parameters - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0132`. diff --git a/tests/ui/error-codes/E0138.rs b/tests/ui/error-codes/E0138.rs deleted file mode 100644 index 6f3c36282e884..0000000000000 --- a/tests/ui/error-codes/E0138.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![feature(start)] - -#[start] -fn foo(argc: isize, argv: *const *const u8) -> isize { 0 } - -#[start] -fn f(argc: isize, argv: *const *const u8) -> isize { 0 } -//~^ ERROR E0138 diff --git a/tests/ui/error-codes/E0138.stderr b/tests/ui/error-codes/E0138.stderr deleted file mode 100644 index 04877ab4082cb..0000000000000 --- a/tests/ui/error-codes/E0138.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0138]: multiple `start` functions - --> $DIR/E0138.rs:7:1 - | -LL | fn foo(argc: isize, argv: *const *const u8) -> isize { 0 } - | ---------------------------------------------------- previous `#[start]` function here -... -LL | fn f(argc: isize, argv: *const *const u8) -> isize { 0 } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ multiple `start` functions - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0138`. diff --git a/tests/ui/error-codes/E0214.stderr b/tests/ui/error-codes/E0214.stderr index 76b11f30996ea..71ae62d64e3bc 100644 --- a/tests/ui/error-codes/E0214.stderr +++ b/tests/ui/error-codes/E0214.stderr @@ -6,8 +6,9 @@ LL | let v: Vec(&str) = vec!["foo"]; | help: use angle brackets instead | -LL | let v: Vec<&str> = vec!["foo"]; - | ~ ~ +LL - let v: Vec(&str) = vec!["foo"]; +LL + let v: Vec<&str> = vec!["foo"]; + | error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0221.stderr b/tests/ui/error-codes/E0221.stderr index 07e7485b67e03..a18e91806d7d9 100644 --- a/tests/ui/error-codes/E0221.stderr +++ b/tests/ui/error-codes/E0221.stderr @@ -12,12 +12,14 @@ LL | let _: Self::A; | help: use fully-qualified syntax to disambiguate | -LL | let _: ::A; - | ~~~~~~~~~~~~~~~ +LL - let _: Self::A; +LL + let _: ::A; + | help: use fully-qualified syntax to disambiguate | -LL | let _: ::A; - | ~~~~~~~~~~~~~~~ +LL - let _: Self::A; +LL + let _: ::A; + | error[E0221]: ambiguous associated type `Err` in bounds of `Self` --> $DIR/E0221.rs:21:16 @@ -31,8 +33,9 @@ LL | let _: Self::Err; = note: associated type `Err` could derive from `FromStr` help: use fully-qualified syntax to disambiguate | -LL | let _: ::Err; - | ~~~~~~~~~~~~~~ +LL - let _: Self::Err; +LL + let _: ::Err; + | error: aborting due to 2 previous errors diff --git a/tests/ui/error-codes/E0225.stderr b/tests/ui/error-codes/E0225.stderr index a4b33a0b7b408..e6781282c8f1b 100644 --- a/tests/ui/error-codes/E0225.stderr +++ b/tests/ui/error-codes/E0225.stderr @@ -20,8 +20,8 @@ LL | trait Foo = std::io::Read + std::io::Write; LL | let _: Box; | ^^^ | | - | trait alias used in trait object type (additional use) - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias + | second non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: std::io::Read + std::io::Write {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit diff --git a/tests/ui/error-codes/E0229.stderr b/tests/ui/error-codes/E0229.stderr index ab2536cc0c9d5..7c967e89ddbc1 100644 --- a/tests/ui/error-codes/E0229.stderr +++ b/tests/ui/error-codes/E0229.stderr @@ -48,10 +48,10 @@ LL | fn baz(x: &>::A) {} | +++++ error[E0277]: the trait bound `I: Foo` is not satisfied - --> $DIR/E0229.rs:13:39 + --> $DIR/E0229.rs:13:14 | LL | fn baz(x: &>::A) {} - | ^^ the trait `Foo` is not implemented for `I` + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `I` | help: consider restricting type parameter `I` with trait `Foo` | diff --git a/tests/ui/error-codes/E0253.rs b/tests/ui/error-codes/E0253.rs index 284b16da8f22e..8284f791c6480 100644 --- a/tests/ui/error-codes/E0253.rs +++ b/tests/ui/error-codes/E0253.rs @@ -1,10 +1,10 @@ mod foo { pub trait MyTrait { - fn do_something(); + type SomeType; } } -use foo::MyTrait::do_something; +use foo::MyTrait::SomeType; //~^ ERROR E0253 fn main() {} diff --git a/tests/ui/error-codes/E0253.stderr b/tests/ui/error-codes/E0253.stderr index 4ee36b70fe510..954dbc8169395 100644 --- a/tests/ui/error-codes/E0253.stderr +++ b/tests/ui/error-codes/E0253.stderr @@ -1,8 +1,8 @@ -error[E0253]: `do_something` is not directly importable +error[E0253]: `SomeType` is not directly importable --> $DIR/E0253.rs:7:5 | -LL | use foo::MyTrait::do_something; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be imported directly +LL | use foo::MyTrait::SomeType; + | ^^^^^^^^^^^^^^^^^^^^^^ cannot be imported directly error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0259.stderr b/tests/ui/error-codes/E0259.stderr index 975d1a161a014..08d3deb68d233 100644 --- a/tests/ui/error-codes/E0259.stderr +++ b/tests/ui/error-codes/E0259.stderr @@ -10,7 +10,8 @@ LL | extern crate test as alloc; = note: `alloc` must be defined only once in the type namespace of this module help: you can use `as` to change the binding name of the import | -LL | extern crate test as other_alloc; +LL - extern crate test as alloc; +LL + extern crate test as other_alloc; | error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0260.stderr b/tests/ui/error-codes/E0260.stderr index 35698c6535902..cb47b77904a38 100644 --- a/tests/ui/error-codes/E0260.stderr +++ b/tests/ui/error-codes/E0260.stderr @@ -10,7 +10,8 @@ LL | mod alloc { = note: `alloc` must be defined only once in the type namespace of this module help: you can use `as` to change the binding name of the import | -LL | extern crate alloc as other_alloc; +LL - extern crate alloc; +LL + extern crate alloc as other_alloc; | error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0283.stderr b/tests/ui/error-codes/E0283.stderr index 381eca5f2a44a..29baae5f133a2 100644 --- a/tests/ui/error-codes/E0283.stderr +++ b/tests/ui/error-codes/E0283.stderr @@ -30,8 +30,9 @@ LL | impl Into for Impl { where U: From; help: try using a fully qualified path to specify the expected types | -LL | let bar = >::into(foo_impl) * 1u32; - | ++++++++++++++++++++++++ ~ +LL - let bar = foo_impl.into() * 1u32; +LL + let bar = >::into(foo_impl) * 1u32; + | error: aborting due to 2 previous errors diff --git a/tests/ui/error-codes/E0393.stderr b/tests/ui/error-codes/E0393.stderr index 489398b7be59e..4847aa2508dad 100644 --- a/tests/ui/error-codes/E0393.stderr +++ b/tests/ui/error-codes/E0393.stderr @@ -7,7 +7,7 @@ LL | LL | fn together_we_will_rule_the_galaxy(son: &dyn A) {} | ^ | - = note: because of the default `Self` reference, type parameters must be specified on object types + = note: because the parameter default references `Self`, the parameter must be specified on the object type help: set the type parameter to the desired type | LL | fn together_we_will_rule_the_galaxy(son: &dyn A) {} diff --git a/tests/ui/error-codes/E0423.stderr b/tests/ui/error-codes/E0423.stderr index dd7a5b034c587..e50b8bd820cf8 100644 --- a/tests/ui/error-codes/E0423.stderr +++ b/tests/ui/error-codes/E0423.stderr @@ -51,12 +51,14 @@ LL | fn foo() { | help: use struct literal syntax instead | -LL | let f = Foo { a: val }; - | ~~~~~~~~~~~~~~ +LL - let f = Foo(); +LL + let f = Foo { a: val }; + | help: a function with a similar name exists (notice the capitalization difference) | -LL | let f = foo(); - | ~~~ +LL - let f = Foo(); +LL + let f = foo(); + | error: aborting due to 5 previous errors diff --git a/tests/ui/error-codes/E0432.stderr b/tests/ui/error-codes/E0432.stderr index 36fefc95897d6..4ff8b40d1969d 100644 --- a/tests/ui/error-codes/E0432.stderr +++ b/tests/ui/error-codes/E0432.stderr @@ -2,9 +2,9 @@ error[E0432]: unresolved import `something` --> $DIR/E0432.rs:1:5 | LL | use something::Foo; - | ^^^^^^^^^ you might be missing crate `something` + | ^^^^^^^^^ use of unresolved module or unlinked crate `something` | -help: consider importing the `something` crate +help: you might be missing a crate named `something`, add it to your project and import it in your code | LL + extern crate something; | diff --git a/tests/ui/error-codes/E0435.stderr b/tests/ui/error-codes/E0435.stderr index 1ebb997639447..13187472e60c7 100644 --- a/tests/ui/error-codes/E0435.stderr +++ b/tests/ui/error-codes/E0435.stderr @@ -6,8 +6,9 @@ LL | let _: [u8; foo]; | help: consider using `const` instead of `let` | -LL | const foo: usize = 42; - | ~~~~~ +LL - let foo: usize = 42; +LL + const foo: usize = 42; + | error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0451.stderr b/tests/ui/error-codes/E0451.stderr index 419cf117efe04..2cd30095c80d5 100644 --- a/tests/ui/error-codes/E0451.stderr +++ b/tests/ui/error-codes/E0451.stderr @@ -8,7 +8,7 @@ error[E0451]: field `b` of struct `Foo` is private --> $DIR/E0451.rs:18:29 | LL | let f = bar::Foo{ a: 0, b: 0 }; - | ^^^^ private field + | ^ private field error: aborting due to 2 previous errors diff --git a/tests/ui/error-codes/E0462.rs b/tests/ui/error-codes/E0462.rs index 12214331445ff..45d35c345cb5b 100644 --- a/tests/ui/error-codes/E0462.rs +++ b/tests/ui/error-codes/E0462.rs @@ -1,6 +1,6 @@ //@ aux-build:found-staticlib.rs -//@ normalize-stderr: "\.nll/" -> "/" +//@ normalize-stderr: "E0462\..+/auxiliary/" -> "E0462/auxiliary/" //@ normalize-stderr: "\\\?\\" -> "" //@ normalize-stderr: "(lib)?found_staticlib\.[a-z]+" -> "libfound_staticlib.somelib" diff --git a/tests/ui/error-codes/E0464.rs b/tests/ui/error-codes/E0464.rs index aaf4d3a8f5007..d1303ae91b1d9 100644 --- a/tests/ui/error-codes/E0464.rs +++ b/tests/ui/error-codes/E0464.rs @@ -2,7 +2,7 @@ //@ aux-build:crateresolve1-2.rs //@ aux-build:crateresolve1-3.rs -//@ normalize-stderr: "\.nll/" -> "/" +//@ normalize-stderr: "E0464\..+/auxiliary/" -> "E0464/auxiliary/" //@ normalize-stderr: "\\\?\\" -> "" //@ normalize-stderr: "(lib)?crateresolve1-([123])\.[a-z]+" -> "libcrateresolve1-$2.somelib" diff --git a/tests/ui/error-codes/E0516.stderr b/tests/ui/error-codes/E0516.stderr index 62e70a4668d12..f4127678d5b99 100644 --- a/tests/ui/error-codes/E0516.stderr +++ b/tests/ui/error-codes/E0516.stderr @@ -6,8 +6,9 @@ LL | let x: typeof(92) = 92; | help: consider replacing `typeof(...)` with an actual type | -LL | let x: i32 = 92; - | ~~~ +LL - let x: typeof(92) = 92; +LL + let x: i32 = 92; + | error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0523.rs b/tests/ui/error-codes/E0523.rs index aaf4d3a8f5007..7c1869c055b22 100644 --- a/tests/ui/error-codes/E0523.rs +++ b/tests/ui/error-codes/E0523.rs @@ -2,7 +2,7 @@ //@ aux-build:crateresolve1-2.rs //@ aux-build:crateresolve1-3.rs -//@ normalize-stderr: "\.nll/" -> "/" +//@ normalize-stderr: "E0523\..+/auxiliary/" -> "E0523/auxiliary/" //@ normalize-stderr: "\\\?\\" -> "" //@ normalize-stderr: "(lib)?crateresolve1-([123])\.[a-z]+" -> "libcrateresolve1-$2.somelib" diff --git a/tests/ui/error-codes/E0637.stderr b/tests/ui/error-codes/E0637.stderr index d9db89ddb0c97..95a5a58ab8561 100644 --- a/tests/ui/error-codes/E0637.stderr +++ b/tests/ui/error-codes/E0637.stderr @@ -13,8 +13,9 @@ LL | fn underscore_lifetime<'_>(str1: &'_ str, str2: &'_ str) -> &'_ str { = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `str1` or `str2` help: consider introducing a named lifetime parameter | -LL | fn underscore_lifetime<'a, '_>(str1: &'a str, str2: &'a str) -> &'a str { - | +++ ~~ ~~ ~~ +LL - fn underscore_lifetime<'_>(str1: &'_ str, str2: &'_ str) -> &'_ str { +LL + fn underscore_lifetime<'a, '_>(str1: &'a str, str2: &'a str) -> &'a str { + | error[E0637]: `&` without an explicit lifetime name cannot be used here --> $DIR/E0637.rs:13:13 diff --git a/tests/ui/error-codes/E0642.stderr b/tests/ui/error-codes/E0642.stderr index dd9e28ad49277..97309e95b6b49 100644 --- a/tests/ui/error-codes/E0642.stderr +++ b/tests/ui/error-codes/E0642.stderr @@ -6,8 +6,9 @@ LL | fn foo((x, y): (i32, i32)); | help: give this argument a name or use an underscore to ignore it | -LL | fn foo(_: (i32, i32)); - | ~ +LL - fn foo((x, y): (i32, i32)); +LL + fn foo(_: (i32, i32)); + | error[E0642]: patterns aren't allowed in methods without bodies --> $DIR/E0642.rs:11:12 @@ -17,8 +18,9 @@ LL | fn bar((x, y): (i32, i32)) {} | help: give this argument a name or use an underscore to ignore it | -LL | fn bar(_: (i32, i32)) {} - | ~ +LL - fn bar((x, y): (i32, i32)) {} +LL + fn bar(_: (i32, i32)) {} + | error[E0642]: patterns aren't allowed in methods without bodies --> $DIR/E0642.rs:13:15 @@ -28,8 +30,9 @@ LL | fn method(S { .. }: S) {} | help: give this argument a name or use an underscore to ignore it | -LL | fn method(_: S) {} - | ~ +LL - fn method(S { .. }: S) {} +LL + fn method(_: S) {} + | error: aborting due to 3 previous errors diff --git a/tests/ui/error-codes/E0647.rs b/tests/ui/error-codes/E0647.rs deleted file mode 100644 index fc085511cbcb0..0000000000000 --- a/tests/ui/error-codes/E0647.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![no_std] -#![feature(start)] - -extern crate std; - -#[start] -fn start(_: isize, _: *const *const u8) -> isize where (): Copy { //~ ERROR [E0647] - 0 -} diff --git a/tests/ui/error-codes/E0647.stderr b/tests/ui/error-codes/E0647.stderr deleted file mode 100644 index 4b444e5a39726..0000000000000 --- a/tests/ui/error-codes/E0647.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0647]: `#[start]` function is not allowed to have a `where` clause - --> $DIR/E0647.rs:7:50 - | -LL | fn start(_: isize, _: *const *const u8) -> isize where (): Copy { - | ^^^^^^^^^^^^^^ `#[start]` function cannot have a `where` clause - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0647`. diff --git a/tests/ui/error-codes/E0746.stderr b/tests/ui/error-codes/E0746.stderr index ce3e973696945..7155f3d733cdf 100644 --- a/tests/ui/error-codes/E0746.stderr +++ b/tests/ui/error-codes/E0746.stderr @@ -6,8 +6,9 @@ LL | fn foo() -> dyn Trait { Struct } | help: consider returning an `impl Trait` instead of a `dyn Trait` | -LL | fn foo() -> impl Trait { Struct } - | ~~~~ +LL - fn foo() -> dyn Trait { Struct } +LL + fn foo() -> impl Trait { Struct } + | help: alternatively, box the return type, and wrap all of the returned values in `Box::new` | LL | fn foo() -> Box { Box::new(Struct) } @@ -21,8 +22,9 @@ LL | fn bar() -> dyn Trait { | help: consider returning an `impl Trait` instead of a `dyn Trait` | -LL | fn bar() -> impl Trait { - | ~~~~ +LL - fn bar() -> dyn Trait { +LL + fn bar() -> impl Trait { + | help: alternatively, box the return type, and wrap all of the returned values in `Box::new` | LL ~ fn bar() -> Box { diff --git a/tests/ui/error-codes/E0789.rs b/tests/ui/error-codes/E0789.rs index 3acc983edc4ec..08471e1b3f3e9 100644 --- a/tests/ui/error-codes/E0789.rs +++ b/tests/ui/error-codes/E0789.rs @@ -4,7 +4,7 @@ #![feature(staged_api)] #![unstable(feature = "foo_module", reason = "...", issue = "123")] -#[rustc_allowed_through_unstable_modules] +#[rustc_allowed_through_unstable_modules = "use stable path instead"] // #[stable(feature = "foo", since = "1.0")] struct Foo; //~^ ERROR `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute diff --git a/tests/ui/error-codes/ex-E0612.stderr b/tests/ui/error-codes/ex-E0612.stderr index 23c1697b9b20f..54451d3d4526e 100644 --- a/tests/ui/error-codes/ex-E0612.stderr +++ b/tests/ui/error-codes/ex-E0612.stderr @@ -6,8 +6,9 @@ LL | y.1; | help: a field with a similar name exists | -LL | y.0; - | ~ +LL - y.1; +LL + y.0; + | error: aborting due to 1 previous error diff --git a/tests/ui/error-emitter/E0308-clarification.rs b/tests/ui/error-emitter/E0308-clarification.rs new file mode 100644 index 0000000000000..4c15ede00415e --- /dev/null +++ b/tests/ui/error-emitter/E0308-clarification.rs @@ -0,0 +1,16 @@ +//@ compile-flags: -Zunstable-options --error-format=human-unicode --color=always +//@ only-linux +// Ensure that when we have a type error where both types have the same textual representation, the +// diagnostic machinery highlights the clarifying comment that comes after in parentheses. +trait Foo: Copy + ToString {} + +impl Foo for T {} + +fn hide(x: T) -> impl Foo { + x +} + +fn main() { + let mut x = (hide(0_u32), hide(0_i32)); + x = (x.1, x.0); +} diff --git a/tests/ui/error-emitter/E0308-clarification.svg b/tests/ui/error-emitter/E0308-clarification.svg new file mode 100644 index 0000000000000..9432e3a4ee918 --- /dev/null +++ b/tests/ui/error-emitter/E0308-clarification.svg @@ -0,0 +1,97 @@ + + + + + + + error[E0308]: mismatched types + + ╭▸ $DIR/E0308-clarification.rs:15:10 + + + + LL fn hide<T: Foo>(x: T) -> impl Foo { + + ┬─────── + + + + the expected opaque type + + the found opaque type + + + + LL x = (x.1, x.0); + + ━━━ expected `u32`, found `i32` + + + + note: expected opaque type `impl Foo` (`u32`) + + found opaque type `impl Foo` (`i32`) + + note: distinct uses of `impl Trait` result in different opaque types + + + + error[E0308]: mismatched types + + ╭▸ $DIR/E0308-clarification.rs:15:15 + + + + LL fn hide<T: Foo>(x: T) -> impl Foo { + + ┬─────── + + + + the expected opaque type + + the found opaque type + + + + LL x = (x.1, x.0); + + ━━━ expected `i32`, found `u32` + + + + note: expected opaque type `impl Foo` (`i32`) + + found opaque type `impl Foo` (`u32`) + + note: distinct uses of `impl Trait` result in different opaque types + + + + error: aborting due to 2 previous errors + + + + For more information about this error, try `rustc --explain E0308`. + + + + + + diff --git a/tests/ui/error-festival.stderr b/tests/ui/error-festival.stderr index f71fa7e685cc7..9db9536379141 100644 --- a/tests/ui/error-festival.stderr +++ b/tests/ui/error-festival.stderr @@ -6,8 +6,9 @@ LL | y = 2; | help: a local variable with a similar name exists | -LL | x = 2; - | ~ +LL - y = 2; +LL + x = 2; + | help: you might have meant to introduce a new binding | LL | let y = 2; @@ -76,8 +77,9 @@ LL | let x_is_nonzero = x as bool; | help: compare with zero instead | -LL | let x_is_nonzero = x != 0; - | ~~~~ +LL - let x_is_nonzero = x as bool; +LL + let x_is_nonzero = x != 0; + | error[E0606]: casting `&u8` as `u32` is invalid --> $DIR/error-festival.rs:37:18 diff --git a/tests/ui/explicit-tail-calls/become-operator.stderr b/tests/ui/explicit-tail-calls/become-operator.stderr index 26e4343faaea7..9741fbfca6d8a 100644 --- a/tests/ui/explicit-tail-calls/become-operator.stderr +++ b/tests/ui/explicit-tail-calls/become-operator.stderr @@ -16,8 +16,9 @@ LL | become a + b; | help: try using the method directly | -LL | become (a).add(b); - | + ~~~~~~ + +LL - become a + b; +LL + become (a).add(b); + | error: `become` does not support operators --> $DIR/become-operator.rs:19:12 @@ -37,8 +38,9 @@ LL | become !x; | help: try using the method directly | -LL | become (x).not(); - | ~ +++++++ +LL - become !x; +LL + become (x).not(); + | error: `become` does not support operators --> $DIR/become-operator.rs:27:12 @@ -68,8 +70,9 @@ LL | become *b ^= 1; | help: try using the method directly | -LL | become (*b).bitxor_assign(1); - | + ~~~~~~~~~~~~~~~~ + +LL - become *b ^= 1; +LL + become (*b).bitxor_assign(1); + | error: aborting due to 7 previous errors diff --git a/tests/ui/explicit-tail-calls/become-trait-fn.rs b/tests/ui/explicit-tail-calls/become-trait-fn.rs new file mode 100644 index 0000000000000..03255f15dd04b --- /dev/null +++ b/tests/ui/explicit-tail-calls/become-trait-fn.rs @@ -0,0 +1,19 @@ +// regression test for +// this previously caused an ICE, because we would compare `#[track_caller]` of +// the callee and the caller (in tailcalls specifically), leading to a problem +// since `T::f`'s instance can't be resolved (we do not know if the function is +// or isn't marked with `#[track_caller]`!) +// +//@ check-pass +#![expect(incomplete_features)] +#![feature(explicit_tail_calls)] + +trait Tr { + fn f(); +} + +fn g() { + become T::f(); +} + +fn main() {} diff --git a/tests/ui/explicit-tail-calls/callee_is_track_caller.rs b/tests/ui/explicit-tail-calls/callee_is_track_caller.rs new file mode 100644 index 0000000000000..bcb93fda8c856 --- /dev/null +++ b/tests/ui/explicit-tail-calls/callee_is_track_caller.rs @@ -0,0 +1,15 @@ +//@ check-pass +// FIXME(explicit_tail_calls): make this run-pass, once tail calls are properly implemented +#![expect(incomplete_features)] +#![feature(explicit_tail_calls)] + +fn a(x: u32) -> u32 { + become b(x); +} + +#[track_caller] +fn b(x: u32) -> u32 { x + 42 } + +fn main() { + assert_eq!(a(12), 54); +} diff --git a/tests/ui/explicit-tail-calls/caller_is_track_caller.rs b/tests/ui/explicit-tail-calls/caller_is_track_caller.rs new file mode 100644 index 0000000000000..4e5f3f12f8399 --- /dev/null +++ b/tests/ui/explicit-tail-calls/caller_is_track_caller.rs @@ -0,0 +1,16 @@ +#![expect(incomplete_features)] +#![feature(explicit_tail_calls)] + +#[track_caller] +fn a() { + become b(); //~ error: a function marked with `#[track_caller]` cannot perform a tail-call +} + +fn b() {} + +#[track_caller] +fn c() { + become a(); //~ error: a function marked with `#[track_caller]` cannot perform a tail-call +} + +fn main() {} diff --git a/tests/ui/explicit-tail-calls/caller_is_track_caller.stderr b/tests/ui/explicit-tail-calls/caller_is_track_caller.stderr new file mode 100644 index 0000000000000..79b9b45986c47 --- /dev/null +++ b/tests/ui/explicit-tail-calls/caller_is_track_caller.stderr @@ -0,0 +1,14 @@ +error: a function marked with `#[track_caller]` cannot perform a tail-call + --> $DIR/caller_is_track_caller.rs:6:5 + | +LL | become b(); + | ^^^^^^^^^^ + +error: a function marked with `#[track_caller]` cannot perform a tail-call + --> $DIR/caller_is_track_caller.rs:13:5 + | +LL | become a(); + | ^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/explicit-tail-calls/ctfe-arg-bad-borrow.stderr b/tests/ui/explicit-tail-calls/ctfe-arg-bad-borrow.stderr index 75fb13c378c24..ece581dc62638 100644 --- a/tests/ui/explicit-tail-calls/ctfe-arg-bad-borrow.stderr +++ b/tests/ui/explicit-tail-calls/ctfe-arg-bad-borrow.stderr @@ -4,10 +4,9 @@ error[E0597]: `local` does not live long enough LL | let local = Type; | ----- binding `local` declared here LL | become takes_borrow(&local); - | ^^^^^^ borrowed value does not live long enough -LL | -LL | } - | - `local` dropped here while still borrowed + | ^^^^^^- `local` dropped here while still borrowed + | | + | borrowed value does not live long enough error: aborting due to 1 previous error diff --git a/tests/ui/explicit-tail-calls/two-phase.rs b/tests/ui/explicit-tail-calls/two-phase.rs new file mode 100644 index 0000000000000..91365b5e739c6 --- /dev/null +++ b/tests/ui/explicit-tail-calls/two-phase.rs @@ -0,0 +1,14 @@ +// regression test for . +// this test used to ICE because we tried to run drop glue of `x` +// if dropping `_y` (happening at the `become` site) panicked and caused an unwind. +// +//@ check-pass +#![expect(incomplete_features)] +#![feature(explicit_tail_calls)] + +fn f(x: &mut ()) { + let _y = String::new(); + become f(x); +} + +fn main() {} diff --git a/tests/ui/explicit/explicit-call-to-dtor.stderr b/tests/ui/explicit/explicit-call-to-dtor.stderr index 6f40f3998a98c..47d2823c82d35 100644 --- a/tests/ui/explicit/explicit-call-to-dtor.stderr +++ b/tests/ui/explicit/explicit-call-to-dtor.stderr @@ -6,8 +6,9 @@ LL | x.drop(); | help: consider using `drop` function | -LL | drop(x); - | +++++ ~ +LL - x.drop(); +LL + drop(x); + | error: aborting due to 1 previous error diff --git a/tests/ui/explicit/explicit-call-to-supertrait-dtor.stderr b/tests/ui/explicit/explicit-call-to-supertrait-dtor.stderr index 13b6ee9987e88..9f98fb5f26c2a 100644 --- a/tests/ui/explicit/explicit-call-to-supertrait-dtor.stderr +++ b/tests/ui/explicit/explicit-call-to-supertrait-dtor.stderr @@ -6,8 +6,9 @@ LL | self.drop(); | help: consider using `drop` function | -LL | drop(self); - | +++++ ~ +LL - self.drop(); +LL + drop(self); + | error: aborting due to 1 previous error diff --git a/tests/ui/expr/block.rs b/tests/ui/expr/block.rs index bf626c9ead372..70f5c274c214d 100644 --- a/tests/ui/expr/block.rs +++ b/tests/ui/expr/block.rs @@ -4,7 +4,7 @@ // Tests for standalone blocks as expressions -fn test_basic() { let rs: bool = { true }; assert!((rs)); } +fn test_basic() { let rs: bool = { true }; assert!(rs); } struct RS { v1: isize, v2: isize } diff --git a/tests/ui/expr/if/expr-if-panic-fn.rs b/tests/ui/expr/if/expr-if-panic-fn.rs index 4f3d7fd48e36e..0b4742d4a8999 100644 --- a/tests/ui/expr/if/expr-if-panic-fn.rs +++ b/tests/ui/expr/if/expr-if-panic-fn.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:explicit panic -//@ ignore-emscripten no processes +//@ needs-subprocess fn f() -> ! { panic!() diff --git a/tests/ui/expr/if/expr-if-panic.rs b/tests/ui/expr/if/expr-if-panic.rs index 0b43d1d6b0069..2f35878e844d1 100644 --- a/tests/ui/expr/if/expr-if-panic.rs +++ b/tests/ui/expr/if/expr-if-panic.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:explicit panic -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { let _x = if false { diff --git a/tests/ui/expr/if/expr-if.rs b/tests/ui/expr/if/expr-if.rs index ae869c4b77a38..68c2fc2213015 100644 --- a/tests/ui/expr/if/expr-if.rs +++ b/tests/ui/expr/if/expr-if.rs @@ -1,43 +1,43 @@ //@ run-pass // Tests for if as expressions -fn test_if() { let rs: bool = if true { true } else { false }; assert!((rs)); } +fn test_if() { let rs: bool = if true { true } else { false }; assert!(rs); } fn test_else() { let rs: bool = if false { false } else { true }; - assert!((rs)); + assert!(rs); } fn test_elseif1() { let rs: bool = if true { true } else if true { false } else { false }; - assert!((rs)); + assert!(rs); } fn test_elseif2() { let rs: bool = if false { false } else if true { true } else { false }; - assert!((rs)); + assert!(rs); } fn test_elseif3() { let rs: bool = if false { false } else if false { false } else { true }; - assert!((rs)); + assert!(rs); } fn test_inferrence() { let rs = if true { true } else { false }; - assert!((rs)); + assert!(rs); } fn test_if_as_if_condition() { let rs1 = if if false { false } else { true } { true } else { false }; - assert!((rs1)); + assert!(rs1); let rs2 = if if true { false } else { true } { false } else { true }; - assert!((rs2)); + assert!(rs2); } fn test_if_as_block_result() { let rs = if true { if false { false } else { true } } else { false }; - assert!((rs)); + assert!(rs); } pub fn main() { diff --git a/tests/ui/expr/if/if-branch-types.stderr b/tests/ui/expr/if/if-branch-types.stderr index 0e86a24f31b7d..587d2f10a8c09 100644 --- a/tests/ui/expr/if/if-branch-types.stderr +++ b/tests/ui/expr/if/if-branch-types.stderr @@ -8,8 +8,9 @@ LL | let x = if true { 10i32 } else { 10u32 }; | help: change the type of the numeric literal from `u32` to `i32` | -LL | let x = if true { 10i32 } else { 10i32 }; - | ~~~ +LL - let x = if true { 10i32 } else { 10u32 }; +LL + let x = if true { 10i32 } else { 10i32 }; + | error: aborting due to 1 previous error diff --git a/tests/ui/expr/if/if-check-panic.rs b/tests/ui/expr/if/if-check-panic.rs index 4b400deaca46f..eb0413f42fcf1 100644 --- a/tests/ui/expr/if/if-check-panic.rs +++ b/tests/ui/expr/if/if-check-panic.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:Number is odd -//@ ignore-emscripten no processes +//@ needs-subprocess fn even(x: usize) -> bool { if x < 2 { diff --git a/tests/ui/expr/if/if-cond-bot.rs b/tests/ui/expr/if/if-cond-bot.rs index ddb5559ffca7c..56bd5ec35f925 100644 --- a/tests/ui/expr/if/if-cond-bot.rs +++ b/tests/ui/expr/if/if-cond-bot.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:quux -//@ ignore-emscripten no processes +//@ needs-subprocess fn my_err(s: String) -> ! { println!("{}", s); diff --git a/tests/ui/expr/if/if-else-chain-missing-else.rs b/tests/ui/expr/if/if-else-chain-missing-else.rs new file mode 100644 index 0000000000000..995aac07f2f76 --- /dev/null +++ b/tests/ui/expr/if/if-else-chain-missing-else.rs @@ -0,0 +1,20 @@ +enum Cause { Cause1, Cause2 } +struct MyErr { x: Cause } + +fn main() { + _ = f(); +} + +fn f() -> Result { + let res = could_fail(); + let x = if let Ok(x) = res { + x + } else if let Err(e) = res { //~ ERROR `if` and `else` + return Err(e); + }; + Ok(x) +} + +fn could_fail() -> Result { + Ok(0) +} diff --git a/tests/ui/expr/if/if-else-chain-missing-else.stderr b/tests/ui/expr/if/if-else-chain-missing-else.stderr new file mode 100644 index 0000000000000..374c4927e3003 --- /dev/null +++ b/tests/ui/expr/if/if-else-chain-missing-else.stderr @@ -0,0 +1,22 @@ +error[E0308]: `if` and `else` have incompatible types + --> $DIR/if-else-chain-missing-else.rs:12:12 + | +LL | let x = if let Ok(x) = res { + | ______________- +LL | | x + | | - expected because of this +LL | | } else if let Err(e) = res { + | | ____________^ +LL | || return Err(e); +LL | || }; + | || ^ + | ||_____| + | |_____`if` and `else` have incompatible types + | expected `i32`, found `()` + | + = note: `if` expressions without `else` evaluate to `()` + = note: consider adding an `else` block that evaluates to the expected type + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/expr/if/if-else-type-mismatch.stderr b/tests/ui/expr/if/if-else-type-mismatch.stderr index f1fffdb1e7ef8..1cf94c98800bb 100644 --- a/tests/ui/expr/if/if-else-type-mismatch.stderr +++ b/tests/ui/expr/if/if-else-type-mismatch.stderr @@ -13,8 +13,9 @@ LL | | }; | help: change the type of the numeric literal from `u32` to `i32` | -LL | 2i32 - | ~~~ +LL - 2u32 +LL + 2i32 + | error[E0308]: `if` and `else` have incompatible types --> $DIR/if-else-type-mismatch.rs:8:38 @@ -26,8 +27,9 @@ LL | let _ = if true { 42i32 } else { 42u32 }; | help: change the type of the numeric literal from `u32` to `i32` | -LL | let _ = if true { 42i32 } else { 42i32 }; - | ~~~ +LL - let _ = if true { 42i32 } else { 42u32 }; +LL + let _ = if true { 42i32 } else { 42i32 }; + | error[E0308]: `if` and `else` have incompatible types --> $DIR/if-else-type-mismatch.rs:13:9 diff --git a/tests/ui/issues/issue-41272.rs b/tests/ui/expr/if/if-let-no-match-guards-issue-41272.rs similarity index 100% rename from tests/ui/issues/issue-41272.rs rename to tests/ui/expr/if/if-let-no-match-guards-issue-41272.rs diff --git a/tests/ui/expr/if/if-let.stderr b/tests/ui/expr/if/if-let.stderr index c4bba3cb1a8a0..792504a9772f6 100644 --- a/tests/ui/expr/if/if-let.stderr +++ b/tests/ui/expr/if/if-let.stderr @@ -2,7 +2,7 @@ warning: irrefutable `if let` pattern --> $DIR/if-let.rs:6:16 | LL | if let $p = $e $b - | ^^^ + | ^^^^^^^^^^^ ... LL | / foo!(a, 1, { LL | | println!("irrefutable pattern"); diff --git a/tests/ui/expr/issue-22933-2.stderr b/tests/ui/expr/issue-22933-2.stderr index dadc31213621c..07f095f643ee5 100644 --- a/tests/ui/expr/issue-22933-2.stderr +++ b/tests/ui/expr/issue-22933-2.stderr @@ -9,8 +9,9 @@ LL | ApplePie = Delicious::Apple as isize | Delicious::PIE as isize, | help: there is a variant with a similar name | -LL | ApplePie = Delicious::Apple as isize | Delicious::Pie as isize, - | ~~~ +LL - ApplePie = Delicious::Apple as isize | Delicious::PIE as isize, +LL + ApplePie = Delicious::Apple as isize | Delicious::Pie as isize, + | error: aborting due to 1 previous error diff --git a/tests/ui/extern-flag/multiple-opts.stderr b/tests/ui/extern-flag/multiple-opts.stderr index 0aaca5ee253e4..d0f38bad94c6e 100644 --- a/tests/ui/extern-flag/multiple-opts.stderr +++ b/tests/ui/extern-flag/multiple-opts.stderr @@ -1,8 +1,10 @@ -error[E0433]: failed to resolve: use of undeclared crate or module `somedep` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `somedep` --> $DIR/multiple-opts.rs:19:5 | LL | somedep::somefun(); - | ^^^^^^^ use of undeclared crate or module `somedep` + | ^^^^^^^ use of unresolved module or unlinked crate `somedep` + | + = help: you might be missing a crate named `somedep` error: aborting due to 1 previous error diff --git a/tests/ui/extern-flag/noprelude.stderr b/tests/ui/extern-flag/noprelude.stderr index 23b9b2fd94b27..fbd84956f66dc 100644 --- a/tests/ui/extern-flag/noprelude.stderr +++ b/tests/ui/extern-flag/noprelude.stderr @@ -1,8 +1,10 @@ -error[E0433]: failed to resolve: use of undeclared crate or module `somedep` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `somedep` --> $DIR/noprelude.rs:6:5 | LL | somedep::somefun(); - | ^^^^^^^ use of undeclared crate or module `somedep` + | ^^^^^^^ use of unresolved module or unlinked crate `somedep` + | + = help: you might be missing a crate named `somedep` error: aborting due to 1 previous error diff --git a/tests/ui/extern/extern-const.stderr b/tests/ui/extern/extern-const.stderr index 441495866cd9b..c48eca8c36dff 100644 --- a/tests/ui/extern/extern-const.stderr +++ b/tests/ui/extern/extern-const.stderr @@ -7,8 +7,9 @@ LL | const rust_dbg_static_mut: c_int; = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html help: try using a static value | -LL | static rust_dbg_static_mut: c_int; - | ~~~~~~ +LL - const rust_dbg_static_mut: c_int; +LL + static rust_dbg_static_mut: c_int; + | error: aborting due to 1 previous error diff --git a/tests/ui/extern/extern-crate-rename.stderr b/tests/ui/extern/extern-crate-rename.stderr index 43f6e56ab0e70..4d4a585de60c1 100644 --- a/tests/ui/extern/extern-crate-rename.stderr +++ b/tests/ui/extern/extern-crate-rename.stderr @@ -9,8 +9,9 @@ LL | extern crate m2 as m1; = note: `m1` must be defined only once in the type namespace of this module help: you can use `as` to change the binding name of the import | -LL | extern crate m2 as other_m1; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - extern crate m2 as m1; +LL + extern crate m2 as other_m1; + | error: aborting due to 1 previous error diff --git a/tests/ui/extern/extern-crate-visibility.stderr b/tests/ui/extern/extern-crate-visibility.stderr index b239727092ae1..c037da41b5eae 100644 --- a/tests/ui/extern/extern-crate-visibility.stderr +++ b/tests/ui/extern/extern-crate-visibility.stderr @@ -11,8 +11,9 @@ LL | extern crate core; | ^^^^^^^^^^^^^^^^^^ help: consider importing this module instead | -LL | use std::cell; - | ~~~~~~~~~ +LL - use foo::core::cell; +LL + use std::cell; + | error[E0603]: crate import `core` is private --> $DIR/extern-crate-visibility.rs:9:10 @@ -27,8 +28,9 @@ LL | extern crate core; | ^^^^^^^^^^^^^^^^^^ help: consider importing this struct instead | -LL | std::cell::Cell::new(0); - | ~~~~~~~~~~~~~~~ +LL - foo::core::cell::Cell::new(0); +LL + std::cell::Cell::new(0); + | error: aborting due to 2 previous errors diff --git a/tests/ui/extern/extern-prelude-core.rs b/tests/ui/extern/extern-prelude-core.rs index ced1e5c391535..5108c02517c3d 100644 --- a/tests/ui/extern/extern-prelude-core.rs +++ b/tests/ui/extern/extern-prelude-core.rs @@ -1,5 +1,5 @@ //@ run-pass -#![feature(lang_items, start)] +#![feature(lang_items)] #![no_std] extern crate std as other; @@ -11,8 +11,6 @@ mod foo { } } -#[start] -fn start(_argc: isize, _argv: *const *const u8) -> isize { +fn main() { foo::test(); - 0 } diff --git a/tests/ui/extern/extern-type-diag-not-similar.rs b/tests/ui/extern/extern-type-diag-not-similar.rs index 39d00a6c1bc31..cd3eec9f1f7b8 100644 --- a/tests/ui/extern/extern-type-diag-not-similar.rs +++ b/tests/ui/extern/extern-type-diag-not-similar.rs @@ -4,11 +4,11 @@ // because they are both extern types. #![feature(extern_types)] -extern { +extern "C" { type ShouldNotBeMentioned; } -extern { +extern "C" { type Foo; } diff --git a/tests/ui/extern/fictional-abi.rs b/tests/ui/extern/fictional-abi.rs new file mode 100644 index 0000000000000..60b24f8856efc --- /dev/null +++ b/tests/ui/extern/fictional-abi.rs @@ -0,0 +1,3 @@ +#![crate_type = "lib"] + +pub extern "fictional" fn lol() {} //~ ERROR: invalid ABI diff --git a/tests/ui/extern/fictional-abi.stderr b/tests/ui/extern/fictional-abi.stderr new file mode 100644 index 0000000000000..9784a57776fc0 --- /dev/null +++ b/tests/ui/extern/fictional-abi.stderr @@ -0,0 +1,11 @@ +error[E0703]: invalid ABI: found `fictional` + --> $DIR/fictional-abi.rs:3:12 + | +LL | pub extern "fictional" fn lol() {} + | ^^^^^^^^^^^ invalid ABI + | + = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0703`. diff --git a/tests/ui/extern/issue-10025.rs b/tests/ui/extern/issue-10025.rs index 140012f4a1613..9be0f616fd2d6 100644 --- a/tests/ui/extern/issue-10025.rs +++ b/tests/ui/extern/issue-10025.rs @@ -1,5 +1,5 @@ //@ run-pass -#![allow(dead_code)] +#![allow(dead_code, missing_abi)] unsafe extern fn foo() {} unsafe extern "C" fn bar() {} diff --git a/tests/ui/extern/issue-18576.rs b/tests/ui/extern/issue-18576.rs index 0a98e85e4844f..6b41fe7363145 100644 --- a/tests/ui/extern/issue-18576.rs +++ b/tests/ui/extern/issue-18576.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:stop -//@ ignore-emscripten no processes +//@ needs-subprocess // #18576 // Make sure that calling an extern function pointer in an unreachable diff --git a/tests/ui/extern/issue-18819.stderr b/tests/ui/extern/issue-18819.stderr index 6de0ebfe9aee7..566bf6a1f648f 100644 --- a/tests/ui/extern/issue-18819.stderr +++ b/tests/ui/extern/issue-18819.stderr @@ -22,8 +22,9 @@ LL | print_x(&X); | + help: provide the argument | -LL | print_x(/* &dyn Foo */, /* &str */); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - print_x(X); +LL + print_x(/* &dyn Foo */, /* &str */); + | error: aborting due to 1 previous error diff --git a/tests/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs b/tests/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs index e9471d207da48..1cd52b70315c1 100644 --- a/tests/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs +++ b/tests/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs @@ -1,6 +1,6 @@ //@ run-pass //@ needs-unwind -//@ ignore-emscripten no threads support +//@ needs-threads // rust-lang/rust#64655: with panic=unwind, a panic from a subroutine // should still run destructors as it unwinds the stack. However, diff --git a/tests/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs b/tests/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs index 9486b5f1178c4..a44eb3828d0a9 100644 --- a/tests/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs +++ b/tests/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs @@ -1,6 +1,6 @@ //@ run-pass //@ needs-unwind -//@ ignore-emscripten no threads support +//@ needs-threads // rust-lang/rust#64655: with panic=unwind, a panic from a subroutine // should still run destructors as it unwinds the stack. However, diff --git a/tests/ui/extern/issue-95829.rs b/tests/ui/extern/issue-95829.rs index c5ae4c6826597..493d53d2532f2 100644 --- a/tests/ui/extern/issue-95829.rs +++ b/tests/ui/extern/issue-95829.rs @@ -1,6 +1,6 @@ //@ edition:2018 -extern { +extern "C" { async fn L() { //~ ERROR: incorrect function inside `extern` block //~^ ERROR: functions in `extern` blocks cannot have `async` qualifier async fn M() {} diff --git a/tests/ui/extern/issue-95829.stderr b/tests/ui/extern/issue-95829.stderr index 2f396b8cc0415..2acd0fa3a2650 100644 --- a/tests/ui/extern/issue-95829.stderr +++ b/tests/ui/extern/issue-95829.stderr @@ -1,8 +1,8 @@ error: incorrect function inside `extern` block --> $DIR/issue-95829.rs:4:14 | -LL | extern { - | ------ `extern` blocks define existing foreign functions and functions inside of them cannot have a body +LL | extern "C" { + | ---------- `extern` blocks define existing foreign functions and functions inside of them cannot have a body LL | async fn L() { | ______________^___- | | | @@ -18,8 +18,8 @@ LL | | } error: functions in `extern` blocks cannot have `async` qualifier --> $DIR/issue-95829.rs:4:5 | -LL | extern { - | ------ in this `extern` block +LL | extern "C" { + | ---------- in this `extern` block LL | async fn L() { | ^^^^^ help: remove the `async` qualifier diff --git a/tests/ui/extern/not-in-block.rs b/tests/ui/extern/not-in-block.rs index d3bcafdef7b3c..c21616350902e 100644 --- a/tests/ui/extern/not-in-block.rs +++ b/tests/ui/extern/not-in-block.rs @@ -1,4 +1,5 @@ #![crate_type = "lib"] +#![allow(missing_abi)] extern fn none_fn(x: bool) -> i32; //~^ ERROR free function without a body diff --git a/tests/ui/extern/not-in-block.stderr b/tests/ui/extern/not-in-block.stderr index 2544949ab17a8..e35c50343fcc4 100644 --- a/tests/ui/extern/not-in-block.stderr +++ b/tests/ui/extern/not-in-block.stderr @@ -1,32 +1,34 @@ error: free function without a body - --> $DIR/not-in-block.rs:3:1 + --> $DIR/not-in-block.rs:4:1 | LL | extern fn none_fn(x: bool) -> i32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: provide a definition for the function | -LL | extern fn none_fn(x: bool) -> i32 { } - | ~~~~~~~~~~ +LL - extern fn none_fn(x: bool) -> i32; +LL + extern fn none_fn(x: bool) -> i32 { } + | help: if you meant to declare an externally defined function, use an `extern` block | LL | extern { fn none_fn(x: bool) -> i32; } - | ~~~~~~~~ + + | + + error: free function without a body - --> $DIR/not-in-block.rs:5:1 + --> $DIR/not-in-block.rs:6:1 | LL | extern "C" fn c_fn(x: bool) -> i32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: provide a definition for the function | -LL | extern "C" fn c_fn(x: bool) -> i32 { } - | ~~~~~~~~~~ +LL - extern "C" fn c_fn(x: bool) -> i32; +LL + extern "C" fn c_fn(x: bool) -> i32 { } + | help: if you meant to declare an externally defined function, use an `extern` block | LL | extern "C" { fn c_fn(x: bool) -> i32; } - | ~~~~~~~~~~~~ + + | + + error: aborting due to 2 previous errors diff --git a/tests/ui/feature-gates/feature-gate-abi-avr-interrupt.rs b/tests/ui/feature-gates/feature-gate-abi-avr-interrupt.rs index f37c5335deb04..5386628a8e069 100644 --- a/tests/ui/feature-gates/feature-gate-abi-avr-interrupt.rs +++ b/tests/ui/feature-gates/feature-gate-abi-avr-interrupt.rs @@ -9,43 +9,43 @@ trait Sized { } // feature gate is not used. extern "avr-non-blocking-interrupt" fn fu() {} -//~^ ERROR avr-interrupt and avr-non-blocking-interrupt ABIs are experimental +//~^ ERROR extern "avr-non-blocking-interrupt" ABI is experimental extern "avr-interrupt" fn f() {} -//~^ ERROR avr-interrupt and avr-non-blocking-interrupt ABIs are experimental +//~^ ERROR extern "avr-interrupt" ABI is experimental trait T { extern "avr-interrupt" fn m(); - //~^ ERROR avr-interrupt and avr-non-blocking-interrupt ABIs are experimental + //~^ ERROR extern "avr-interrupt" ABI is experimental extern "avr-non-blocking-interrupt" fn mu(); - //~^ ERROR avr-interrupt and avr-non-blocking-interrupt ABIs are experimental + //~^ ERROR extern "avr-non-blocking-interrupt" ABI is experimental extern "avr-interrupt" fn dm() {} - //~^ ERROR avr-interrupt and avr-non-blocking-interrupt ABIs are experimental + //~^ ERROR extern "avr-interrupt" ABI is experimental extern "avr-non-blocking-interrupt" fn dmu() {} - //~^ ERROR avr-interrupt and avr-non-blocking-interrupt ABIs are experimental + //~^ ERROR extern "avr-non-blocking-interrupt" ABI is experimental } struct S; impl T for S { extern "avr-interrupt" fn m() {} - //~^ ERROR avr-interrupt and avr-non-blocking-interrupt ABIs are experimental + //~^ ERROR extern "avr-interrupt" ABI is experimental extern "avr-non-blocking-interrupt" fn mu() {} - //~^ ERROR avr-interrupt and avr-non-blocking-interrupt ABIs are experimental + //~^ ERROR extern "avr-non-blocking-interrupt" ABI is experimental } impl S { extern "avr-interrupt" fn im() {} - //~^ ERROR avr-interrupt and avr-non-blocking-interrupt ABIs are experimental + //~^ ERROR extern "avr-interrupt" ABI is experimental extern "avr-non-blocking-interrupt" fn imu() {} - //~^ ERROR avr-interrupt and avr-non-blocking-interrupt ABIs are experimental + //~^ ERROR extern "avr-non-blocking-interrupt" ABI is experimental } type TA = extern "avr-interrupt" fn(); -//~^ ERROR avr-interrupt and avr-non-blocking-interrupt ABIs are experimental +//~^ ERROR extern "avr-interrupt" ABI is experimental type TAU = extern "avr-non-blocking-interrupt" fn(); -//~^ ERROR avr-interrupt and avr-non-blocking-interrupt ABIs are experimental +//~^ ERROR extern "avr-non-blocking-interrupt" ABI is experimental extern "avr-interrupt" {} -//~^ ERROR avr-interrupt and avr-non-blocking-interrupt ABIs are experimental +//~^ ERROR extern "avr-interrupt" ABI is experimental extern "avr-non-blocking-interrupt" {} -//~^ ERROR avr-interrupt and avr-non-blocking-interrupt ABIs are experimental +//~^ ERROR extern "avr-non-blocking-interrupt" ABI is experimental diff --git a/tests/ui/feature-gates/feature-gate-abi-avr-interrupt.stderr b/tests/ui/feature-gates/feature-gate-abi-avr-interrupt.stderr index c6786699de1b8..d9f3c3adc7f04 100644 --- a/tests/ui/feature-gates/feature-gate-abi-avr-interrupt.stderr +++ b/tests/ui/feature-gates/feature-gate-abi-avr-interrupt.stderr @@ -1,4 +1,4 @@ -error[E0658]: avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change +error[E0658]: the extern "avr-non-blocking-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-avr-interrupt.rs:11:8 | LL | extern "avr-non-blocking-interrupt" fn fu() {} @@ -8,7 +8,7 @@ LL | extern "avr-non-blocking-interrupt" fn fu() {} = help: add `#![feature(abi_avr_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change +error[E0658]: the extern "avr-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-avr-interrupt.rs:13:8 | LL | extern "avr-interrupt" fn f() {} @@ -18,7 +18,7 @@ LL | extern "avr-interrupt" fn f() {} = help: add `#![feature(abi_avr_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change +error[E0658]: the extern "avr-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-avr-interrupt.rs:17:12 | LL | extern "avr-interrupt" fn m(); @@ -28,7 +28,7 @@ LL | extern "avr-interrupt" fn m(); = help: add `#![feature(abi_avr_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change +error[E0658]: the extern "avr-non-blocking-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-avr-interrupt.rs:19:12 | LL | extern "avr-non-blocking-interrupt" fn mu(); @@ -38,7 +38,7 @@ LL | extern "avr-non-blocking-interrupt" fn mu(); = help: add `#![feature(abi_avr_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change +error[E0658]: the extern "avr-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-avr-interrupt.rs:22:12 | LL | extern "avr-interrupt" fn dm() {} @@ -48,7 +48,7 @@ LL | extern "avr-interrupt" fn dm() {} = help: add `#![feature(abi_avr_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change +error[E0658]: the extern "avr-non-blocking-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-avr-interrupt.rs:24:12 | LL | extern "avr-non-blocking-interrupt" fn dmu() {} @@ -58,7 +58,7 @@ LL | extern "avr-non-blocking-interrupt" fn dmu() {} = help: add `#![feature(abi_avr_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change +error[E0658]: the extern "avr-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-avr-interrupt.rs:30:12 | LL | extern "avr-interrupt" fn m() {} @@ -68,7 +68,7 @@ LL | extern "avr-interrupt" fn m() {} = help: add `#![feature(abi_avr_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change +error[E0658]: the extern "avr-non-blocking-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-avr-interrupt.rs:32:12 | LL | extern "avr-non-blocking-interrupt" fn mu() {} @@ -78,7 +78,7 @@ LL | extern "avr-non-blocking-interrupt" fn mu() {} = help: add `#![feature(abi_avr_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change +error[E0658]: the extern "avr-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-avr-interrupt.rs:37:12 | LL | extern "avr-interrupt" fn im() {} @@ -88,7 +88,7 @@ LL | extern "avr-interrupt" fn im() {} = help: add `#![feature(abi_avr_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change +error[E0658]: the extern "avr-non-blocking-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-avr-interrupt.rs:39:12 | LL | extern "avr-non-blocking-interrupt" fn imu() {} @@ -98,7 +98,7 @@ LL | extern "avr-non-blocking-interrupt" fn imu() {} = help: add `#![feature(abi_avr_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change +error[E0658]: the extern "avr-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-avr-interrupt.rs:43:18 | LL | type TA = extern "avr-interrupt" fn(); @@ -108,7 +108,7 @@ LL | type TA = extern "avr-interrupt" fn(); = help: add `#![feature(abi_avr_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change +error[E0658]: the extern "avr-non-blocking-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-avr-interrupt.rs:45:19 | LL | type TAU = extern "avr-non-blocking-interrupt" fn(); @@ -118,7 +118,7 @@ LL | type TAU = extern "avr-non-blocking-interrupt" fn(); = help: add `#![feature(abi_avr_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change +error[E0658]: the extern "avr-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-avr-interrupt.rs:48:8 | LL | extern "avr-interrupt" {} @@ -128,7 +128,7 @@ LL | extern "avr-interrupt" {} = help: add `#![feature(abi_avr_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change +error[E0658]: the extern "avr-non-blocking-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-avr-interrupt.rs:50:8 | LL | extern "avr-non-blocking-interrupt" {} diff --git a/tests/ui/feature-gates/feature-gate-abi-msp430-interrupt.rs b/tests/ui/feature-gates/feature-gate-abi-msp430-interrupt.rs index b0fb4c414d40f..bb69a638ceeab 100644 --- a/tests/ui/feature-gates/feature-gate-abi-msp430-interrupt.rs +++ b/tests/ui/feature-gates/feature-gate-abi-msp430-interrupt.rs @@ -6,29 +6,29 @@ trait Sized { } extern "msp430-interrupt" fn f() {} -//~^ ERROR msp430-interrupt ABI is experimental +//~^ ERROR "msp430-interrupt" ABI is experimental trait T { extern "msp430-interrupt" fn m(); - //~^ ERROR msp430-interrupt ABI is experimental + //~^ ERROR "msp430-interrupt" ABI is experimental extern "msp430-interrupt" fn dm() {} - //~^ ERROR msp430-interrupt ABI is experimental + //~^ ERROR "msp430-interrupt" ABI is experimental } struct S; impl T for S { extern "msp430-interrupt" fn m() {} - //~^ ERROR msp430-interrupt ABI is experimental + //~^ ERROR "msp430-interrupt" ABI is experimental } impl S { extern "msp430-interrupt" fn im() {} - //~^ ERROR msp430-interrupt ABI is experimental + //~^ ERROR "msp430-interrupt" ABI is experimental } type TA = extern "msp430-interrupt" fn(); -//~^ ERROR msp430-interrupt ABI is experimental +//~^ ERROR "msp430-interrupt" ABI is experimental extern "msp430-interrupt" {} -//~^ ERROR msp430-interrupt ABI is experimental +//~^ ERROR "msp430-interrupt" ABI is experimental diff --git a/tests/ui/feature-gates/feature-gate-abi-msp430-interrupt.stderr b/tests/ui/feature-gates/feature-gate-abi-msp430-interrupt.stderr index 5dacc86dcc59c..21ddbf7a86df8 100644 --- a/tests/ui/feature-gates/feature-gate-abi-msp430-interrupt.stderr +++ b/tests/ui/feature-gates/feature-gate-abi-msp430-interrupt.stderr @@ -1,4 +1,4 @@ -error[E0658]: msp430-interrupt ABI is experimental and subject to change +error[E0658]: the extern "msp430-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-msp430-interrupt.rs:8:8 | LL | extern "msp430-interrupt" fn f() {} @@ -8,7 +8,7 @@ LL | extern "msp430-interrupt" fn f() {} = help: add `#![feature(abi_msp430_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: msp430-interrupt ABI is experimental and subject to change +error[E0658]: the extern "msp430-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-msp430-interrupt.rs:12:12 | LL | extern "msp430-interrupt" fn m(); @@ -18,7 +18,7 @@ LL | extern "msp430-interrupt" fn m(); = help: add `#![feature(abi_msp430_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: msp430-interrupt ABI is experimental and subject to change +error[E0658]: the extern "msp430-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-msp430-interrupt.rs:15:12 | LL | extern "msp430-interrupt" fn dm() {} @@ -28,7 +28,7 @@ LL | extern "msp430-interrupt" fn dm() {} = help: add `#![feature(abi_msp430_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: msp430-interrupt ABI is experimental and subject to change +error[E0658]: the extern "msp430-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-msp430-interrupt.rs:21:12 | LL | extern "msp430-interrupt" fn m() {} @@ -38,7 +38,7 @@ LL | extern "msp430-interrupt" fn m() {} = help: add `#![feature(abi_msp430_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: msp430-interrupt ABI is experimental and subject to change +error[E0658]: the extern "msp430-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-msp430-interrupt.rs:26:12 | LL | extern "msp430-interrupt" fn im() {} @@ -48,7 +48,7 @@ LL | extern "msp430-interrupt" fn im() {} = help: add `#![feature(abi_msp430_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: msp430-interrupt ABI is experimental and subject to change +error[E0658]: the extern "msp430-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-msp430-interrupt.rs:30:18 | LL | type TA = extern "msp430-interrupt" fn(); @@ -58,7 +58,7 @@ LL | type TA = extern "msp430-interrupt" fn(); = help: add `#![feature(abi_msp430_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: msp430-interrupt ABI is experimental and subject to change +error[E0658]: the extern "msp430-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-msp430-interrupt.rs:33:8 | LL | extern "msp430-interrupt" {} diff --git a/tests/ui/feature-gates/feature-gate-abi-riscv-interrupt.rs b/tests/ui/feature-gates/feature-gate-abi-riscv-interrupt.rs index 29820f8877d33..6f4989fbd9fe9 100644 --- a/tests/ui/feature-gates/feature-gate-abi-riscv-interrupt.rs +++ b/tests/ui/feature-gates/feature-gate-abi-riscv-interrupt.rs @@ -9,25 +9,25 @@ trait Sized {} // feature gate is not used. extern "riscv-interrupt-m" fn f() {} -//~^ ERROR riscv-interrupt ABIs are experimental +//~^ ERROR "riscv-interrupt-m" ABI is experimental extern "riscv-interrupt-s" fn f_s() {} -//~^ ERROR riscv-interrupt ABIs are experimental +//~^ ERROR "riscv-interrupt-s" ABI is experimental trait T { extern "riscv-interrupt-m" fn m(); - //~^ ERROR riscv-interrupt ABIs are experimental + //~^ ERROR "riscv-interrupt-m" ABI is experimental } struct S; impl T for S { extern "riscv-interrupt-m" fn m() {} - //~^ ERROR riscv-interrupt ABIs are experimental + //~^ ERROR "riscv-interrupt-m" ABI is experimental } impl S { extern "riscv-interrupt-m" fn im() {} - //~^ ERROR riscv-interrupt ABIs are experimental + //~^ ERROR "riscv-interrupt-m" ABI is experimental } type TA = extern "riscv-interrupt-m" fn(); -//~^ ERROR riscv-interrupt ABIs are experimental +//~^ ERROR "riscv-interrupt-m" ABI is experimental diff --git a/tests/ui/feature-gates/feature-gate-abi-riscv-interrupt.stderr b/tests/ui/feature-gates/feature-gate-abi-riscv-interrupt.stderr index 6b7853a320b0d..3c9e99aa1b9b3 100644 --- a/tests/ui/feature-gates/feature-gate-abi-riscv-interrupt.stderr +++ b/tests/ui/feature-gates/feature-gate-abi-riscv-interrupt.stderr @@ -1,4 +1,4 @@ -error[E0658]: riscv-interrupt ABIs are experimental and subject to change +error[E0658]: the extern "riscv-interrupt-m" ABI is experimental and subject to change --> $DIR/feature-gate-abi-riscv-interrupt.rs:11:8 | LL | extern "riscv-interrupt-m" fn f() {} @@ -8,7 +8,7 @@ LL | extern "riscv-interrupt-m" fn f() {} = help: add `#![feature(abi_riscv_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: riscv-interrupt ABIs are experimental and subject to change +error[E0658]: the extern "riscv-interrupt-s" ABI is experimental and subject to change --> $DIR/feature-gate-abi-riscv-interrupt.rs:13:8 | LL | extern "riscv-interrupt-s" fn f_s() {} @@ -18,7 +18,7 @@ LL | extern "riscv-interrupt-s" fn f_s() {} = help: add `#![feature(abi_riscv_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: riscv-interrupt ABIs are experimental and subject to change +error[E0658]: the extern "riscv-interrupt-m" ABI is experimental and subject to change --> $DIR/feature-gate-abi-riscv-interrupt.rs:17:12 | LL | extern "riscv-interrupt-m" fn m(); @@ -28,7 +28,7 @@ LL | extern "riscv-interrupt-m" fn m(); = help: add `#![feature(abi_riscv_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: riscv-interrupt ABIs are experimental and subject to change +error[E0658]: the extern "riscv-interrupt-m" ABI is experimental and subject to change --> $DIR/feature-gate-abi-riscv-interrupt.rs:23:12 | LL | extern "riscv-interrupt-m" fn m() {} @@ -38,7 +38,7 @@ LL | extern "riscv-interrupt-m" fn m() {} = help: add `#![feature(abi_riscv_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: riscv-interrupt ABIs are experimental and subject to change +error[E0658]: the extern "riscv-interrupt-m" ABI is experimental and subject to change --> $DIR/feature-gate-abi-riscv-interrupt.rs:28:12 | LL | extern "riscv-interrupt-m" fn im() {} @@ -48,7 +48,7 @@ LL | extern "riscv-interrupt-m" fn im() {} = help: add `#![feature(abi_riscv_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: riscv-interrupt ABIs are experimental and subject to change +error[E0658]: the extern "riscv-interrupt-m" ABI is experimental and subject to change --> $DIR/feature-gate-abi-riscv-interrupt.rs:32:18 | LL | type TA = extern "riscv-interrupt-m" fn(); diff --git a/tests/ui/feature-gates/feature-gate-abi-x86-interrupt.rs b/tests/ui/feature-gates/feature-gate-abi-x86-interrupt.rs index 812ca12c7c37d..93c59364f10ba 100644 --- a/tests/ui/feature-gates/feature-gate-abi-x86-interrupt.rs +++ b/tests/ui/feature-gates/feature-gate-abi-x86-interrupt.rs @@ -5,24 +5,24 @@ #[lang="sized"] trait Sized { } -extern "x86-interrupt" fn f7() {} //~ ERROR x86-interrupt ABI is experimental +extern "x86-interrupt" fn f7() {} //~ ERROR "x86-interrupt" ABI is experimental trait Tr { - extern "x86-interrupt" fn m7(); //~ ERROR x86-interrupt ABI is experimental - extern "x86-interrupt" fn dm7() {} //~ ERROR x86-interrupt ABI is experimental + extern "x86-interrupt" fn m7(); //~ ERROR "x86-interrupt" ABI is experimental + extern "x86-interrupt" fn dm7() {} //~ ERROR "x86-interrupt" ABI is experimental } struct S; // Methods in trait impl impl Tr for S { - extern "x86-interrupt" fn m7() {} //~ ERROR x86-interrupt ABI is experimental + extern "x86-interrupt" fn m7() {} //~ ERROR "x86-interrupt" ABI is experimental } // Methods in inherent impl impl S { - extern "x86-interrupt" fn im7() {} //~ ERROR x86-interrupt ABI is experimental + extern "x86-interrupt" fn im7() {} //~ ERROR "x86-interrupt" ABI is experimental } -type A7 = extern "x86-interrupt" fn(); //~ ERROR x86-interrupt ABI is experimental +type A7 = extern "x86-interrupt" fn(); //~ ERROR "x86-interrupt" ABI is experimental -extern "x86-interrupt" {} //~ ERROR x86-interrupt ABI is experimental +extern "x86-interrupt" {} //~ ERROR "x86-interrupt" ABI is experimental diff --git a/tests/ui/feature-gates/feature-gate-abi-x86-interrupt.stderr b/tests/ui/feature-gates/feature-gate-abi-x86-interrupt.stderr index 860005cac3416..231cf207c862d 100644 --- a/tests/ui/feature-gates/feature-gate-abi-x86-interrupt.stderr +++ b/tests/ui/feature-gates/feature-gate-abi-x86-interrupt.stderr @@ -1,4 +1,4 @@ -error[E0658]: x86-interrupt ABI is experimental and subject to change +error[E0658]: the extern "x86-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-x86-interrupt.rs:8:8 | LL | extern "x86-interrupt" fn f7() {} @@ -8,7 +8,7 @@ LL | extern "x86-interrupt" fn f7() {} = help: add `#![feature(abi_x86_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: x86-interrupt ABI is experimental and subject to change +error[E0658]: the extern "x86-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-x86-interrupt.rs:10:12 | LL | extern "x86-interrupt" fn m7(); @@ -18,7 +18,7 @@ LL | extern "x86-interrupt" fn m7(); = help: add `#![feature(abi_x86_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: x86-interrupt ABI is experimental and subject to change +error[E0658]: the extern "x86-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-x86-interrupt.rs:11:12 | LL | extern "x86-interrupt" fn dm7() {} @@ -28,7 +28,7 @@ LL | extern "x86-interrupt" fn dm7() {} = help: add `#![feature(abi_x86_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: x86-interrupt ABI is experimental and subject to change +error[E0658]: the extern "x86-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-x86-interrupt.rs:18:12 | LL | extern "x86-interrupt" fn m7() {} @@ -38,7 +38,7 @@ LL | extern "x86-interrupt" fn m7() {} = help: add `#![feature(abi_x86_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: x86-interrupt ABI is experimental and subject to change +error[E0658]: the extern "x86-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-x86-interrupt.rs:23:12 | LL | extern "x86-interrupt" fn im7() {} @@ -48,7 +48,7 @@ LL | extern "x86-interrupt" fn im7() {} = help: add `#![feature(abi_x86_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: x86-interrupt ABI is experimental and subject to change +error[E0658]: the extern "x86-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-x86-interrupt.rs:26:18 | LL | type A7 = extern "x86-interrupt" fn(); @@ -58,7 +58,7 @@ LL | type A7 = extern "x86-interrupt" fn(); = help: add `#![feature(abi_x86_interrupt)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: x86-interrupt ABI is experimental and subject to change +error[E0658]: the extern "x86-interrupt" ABI is experimental and subject to change --> $DIR/feature-gate-abi-x86-interrupt.rs:28:8 | LL | extern "x86-interrupt" {} diff --git a/tests/ui/feature-gates/feature-gate-abi.rs b/tests/ui/feature-gates/feature-gate-abi.rs index 3aa430e736f09..7ab05889c20a7 100644 --- a/tests/ui/feature-gates/feature-gate-abi.rs +++ b/tests/ui/feature-gates/feature-gate-abi.rs @@ -11,49 +11,49 @@ trait Sized { } trait Tuple { } // Functions -extern "rust-intrinsic" fn f1() {} //~ ERROR intrinsics are subject to change +extern "rust-intrinsic" fn f1() {} //~ ERROR extern "rust-intrinsic" ABI is an implementation detail //~^ ERROR intrinsic must be in -extern "rust-intrinsic" fn f2() {} //~ ERROR intrinsics are subject to change +extern "rust-intrinsic" fn f2() {} //~ ERROR extern "rust-intrinsic" ABI is an implementation detail //~^ ERROR intrinsic must be in -extern "rust-call" fn f4(_: ()) {} //~ ERROR rust-call ABI is subject to change +extern "rust-call" fn f4(_: ()) {} //~ ERROR extern "rust-call" ABI is experimental and subject to change // Methods in trait definition trait Tr { - extern "rust-intrinsic" fn m1(); //~ ERROR intrinsics are subject to change + extern "rust-intrinsic" fn m1(); //~ ERROR extern "rust-intrinsic" ABI is an implementation detail //~^ ERROR intrinsic must be in - extern "rust-intrinsic" fn m2(); //~ ERROR intrinsics are subject to change + extern "rust-intrinsic" fn m2(); //~ ERROR extern "rust-intrinsic" ABI is an implementation detail //~^ ERROR intrinsic must be in - extern "rust-call" fn m4(_: ()); //~ ERROR rust-call ABI is subject to change + extern "rust-call" fn m4(_: ()); //~ ERROR extern "rust-call" ABI is experimental and subject to change - extern "rust-call" fn dm4(_: ()) {} //~ ERROR rust-call ABI is subject to change + extern "rust-call" fn dm4(_: ()) {} //~ ERROR extern "rust-call" ABI is experimental and subject to change } struct S; // Methods in trait impl impl Tr for S { - extern "rust-intrinsic" fn m1() {} //~ ERROR intrinsics are subject to change + extern "rust-intrinsic" fn m1() {} //~ ERROR extern "rust-intrinsic" ABI is an implementation detail //~^ ERROR intrinsic must be in - extern "rust-intrinsic" fn m2() {} //~ ERROR intrinsics are subject to change + extern "rust-intrinsic" fn m2() {} //~ ERROR extern "rust-intrinsic" ABI is an implementation detail //~^ ERROR intrinsic must be in - extern "rust-call" fn m4(_: ()) {} //~ ERROR rust-call ABI is subject to change + extern "rust-call" fn m4(_: ()) {} //~ ERROR extern "rust-call" ABI is experimental and subject to change } // Methods in inherent impl impl S { - extern "rust-intrinsic" fn im1() {} //~ ERROR intrinsics are subject to change + extern "rust-intrinsic" fn im1() {} //~ ERROR extern "rust-intrinsic" ABI is an implementation detail //~^ ERROR intrinsic must be in - extern "rust-intrinsic" fn im2() {} //~ ERROR intrinsics are subject to change + extern "rust-intrinsic" fn im2() {} //~ ERROR extern "rust-intrinsic" ABI is an implementation detail //~^ ERROR intrinsic must be in - extern "rust-call" fn im4(_: ()) {} //~ ERROR rust-call ABI is subject to change + extern "rust-call" fn im4(_: ()) {} //~ ERROR extern "rust-call" ABI is experimental and subject to change } // Function pointer types -type A1 = extern "rust-intrinsic" fn(); //~ ERROR intrinsics are subject to change -type A2 = extern "rust-intrinsic" fn(); //~ ERROR intrinsics are subject to change -type A4 = extern "rust-call" fn(_: ()); //~ ERROR rust-call ABI is subject to change +type A1 = extern "rust-intrinsic" fn(); //~ ERROR extern "rust-intrinsic" ABI is an implementation detail +type A2 = extern "rust-intrinsic" fn(); //~ ERROR extern "rust-intrinsic" ABI is an implementation detail +type A4 = extern "rust-call" fn(_: ()); //~ ERROR extern "rust-call" ABI is experimental and subject to change // Foreign modules -extern "rust-intrinsic" {} //~ ERROR intrinsics are subject to change -extern "rust-intrinsic" {} //~ ERROR intrinsics are subject to change -extern "rust-call" {} //~ ERROR rust-call ABI is subject to change +extern "rust-intrinsic" {} //~ ERROR extern "rust-intrinsic" ABI is an implementation detail +extern "rust-intrinsic" {} //~ ERROR extern "rust-intrinsic" ABI is an implementation detail +extern "rust-call" {} //~ ERROR extern "rust-call" ABI is experimental and subject to change diff --git a/tests/ui/feature-gates/feature-gate-abi.stderr b/tests/ui/feature-gates/feature-gate-abi.stderr index dbdfa7b275d49..70ec64e5135e5 100644 --- a/tests/ui/feature-gates/feature-gate-abi.stderr +++ b/tests/ui/feature-gates/feature-gate-abi.stderr @@ -1,4 +1,4 @@ -error[E0658]: intrinsics are subject to change +error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable --> $DIR/feature-gate-abi.rs:14:8 | LL | extern "rust-intrinsic" fn f1() {} @@ -7,7 +7,7 @@ LL | extern "rust-intrinsic" fn f1() {} = help: add `#![feature(intrinsics)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: intrinsics are subject to change +error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable --> $DIR/feature-gate-abi.rs:16:8 | LL | extern "rust-intrinsic" fn f2() {} @@ -16,7 +16,7 @@ LL | extern "rust-intrinsic" fn f2() {} = help: add `#![feature(intrinsics)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: rust-call ABI is subject to change +error[E0658]: the extern "rust-call" ABI is experimental and subject to change --> $DIR/feature-gate-abi.rs:18:8 | LL | extern "rust-call" fn f4(_: ()) {} @@ -26,7 +26,7 @@ LL | extern "rust-call" fn f4(_: ()) {} = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: intrinsics are subject to change +error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable --> $DIR/feature-gate-abi.rs:22:12 | LL | extern "rust-intrinsic" fn m1(); @@ -35,7 +35,7 @@ LL | extern "rust-intrinsic" fn m1(); = help: add `#![feature(intrinsics)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: intrinsics are subject to change +error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable --> $DIR/feature-gate-abi.rs:24:12 | LL | extern "rust-intrinsic" fn m2(); @@ -44,7 +44,7 @@ LL | extern "rust-intrinsic" fn m2(); = help: add `#![feature(intrinsics)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: rust-call ABI is subject to change +error[E0658]: the extern "rust-call" ABI is experimental and subject to change --> $DIR/feature-gate-abi.rs:26:12 | LL | extern "rust-call" fn m4(_: ()); @@ -54,7 +54,7 @@ LL | extern "rust-call" fn m4(_: ()); = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: rust-call ABI is subject to change +error[E0658]: the extern "rust-call" ABI is experimental and subject to change --> $DIR/feature-gate-abi.rs:28:12 | LL | extern "rust-call" fn dm4(_: ()) {} @@ -64,7 +64,7 @@ LL | extern "rust-call" fn dm4(_: ()) {} = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: intrinsics are subject to change +error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable --> $DIR/feature-gate-abi.rs:35:12 | LL | extern "rust-intrinsic" fn m1() {} @@ -73,7 +73,7 @@ LL | extern "rust-intrinsic" fn m1() {} = help: add `#![feature(intrinsics)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: intrinsics are subject to change +error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable --> $DIR/feature-gate-abi.rs:37:12 | LL | extern "rust-intrinsic" fn m2() {} @@ -82,7 +82,7 @@ LL | extern "rust-intrinsic" fn m2() {} = help: add `#![feature(intrinsics)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: rust-call ABI is subject to change +error[E0658]: the extern "rust-call" ABI is experimental and subject to change --> $DIR/feature-gate-abi.rs:39:12 | LL | extern "rust-call" fn m4(_: ()) {} @@ -92,7 +92,7 @@ LL | extern "rust-call" fn m4(_: ()) {} = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: intrinsics are subject to change +error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable --> $DIR/feature-gate-abi.rs:44:12 | LL | extern "rust-intrinsic" fn im1() {} @@ -101,7 +101,7 @@ LL | extern "rust-intrinsic" fn im1() {} = help: add `#![feature(intrinsics)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: intrinsics are subject to change +error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable --> $DIR/feature-gate-abi.rs:46:12 | LL | extern "rust-intrinsic" fn im2() {} @@ -110,7 +110,7 @@ LL | extern "rust-intrinsic" fn im2() {} = help: add `#![feature(intrinsics)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: rust-call ABI is subject to change +error[E0658]: the extern "rust-call" ABI is experimental and subject to change --> $DIR/feature-gate-abi.rs:48:12 | LL | extern "rust-call" fn im4(_: ()) {} @@ -120,7 +120,7 @@ LL | extern "rust-call" fn im4(_: ()) {} = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: intrinsics are subject to change +error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable --> $DIR/feature-gate-abi.rs:52:18 | LL | type A1 = extern "rust-intrinsic" fn(); @@ -129,7 +129,7 @@ LL | type A1 = extern "rust-intrinsic" fn(); = help: add `#![feature(intrinsics)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: intrinsics are subject to change +error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable --> $DIR/feature-gate-abi.rs:53:18 | LL | type A2 = extern "rust-intrinsic" fn(); @@ -138,7 +138,7 @@ LL | type A2 = extern "rust-intrinsic" fn(); = help: add `#![feature(intrinsics)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: rust-call ABI is subject to change +error[E0658]: the extern "rust-call" ABI is experimental and subject to change --> $DIR/feature-gate-abi.rs:54:18 | LL | type A4 = extern "rust-call" fn(_: ()); @@ -148,7 +148,7 @@ LL | type A4 = extern "rust-call" fn(_: ()); = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: intrinsics are subject to change +error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable --> $DIR/feature-gate-abi.rs:57:8 | LL | extern "rust-intrinsic" {} @@ -157,7 +157,7 @@ LL | extern "rust-intrinsic" {} = help: add `#![feature(intrinsics)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: intrinsics are subject to change +error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable --> $DIR/feature-gate-abi.rs:58:8 | LL | extern "rust-intrinsic" {} @@ -166,7 +166,7 @@ LL | extern "rust-intrinsic" {} = help: add `#![feature(intrinsics)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: rust-call ABI is subject to change +error[E0658]: the extern "rust-call" ABI is experimental and subject to change --> $DIR/feature-gate-abi.rs:59:8 | LL | extern "rust-call" {} diff --git a/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.rs b/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.rs new file mode 100644 index 0000000000000..fb04906dafe96 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.rs @@ -0,0 +1,45 @@ +//@ compile-flags: --crate-type=rlib + +#![feature(no_core, lang_items)] +#![no_core] + +#[lang="sized"] +trait Sized { } + +#[lang="tuple_trait"] +trait Tuple { } + +// Functions +extern "gpu-kernel" fn f1(_: ()) {} //~ ERROR "gpu-kernel" ABI is experimental and subject to change +//~^ ERROR is not a supported ABI + +// Methods in trait definition +trait Tr { + extern "gpu-kernel" fn m1(_: ()); //~ ERROR "gpu-kernel" ABI is experimental and subject to change + + extern "gpu-kernel" fn dm1(_: ()) {} //~ ERROR "gpu-kernel" ABI is experimental and subject to change + //~^ ERROR is not a supported ABI +} + +struct S; + +// Methods in trait impl +impl Tr for S { + extern "gpu-kernel" fn m1(_: ()) {} //~ ERROR "gpu-kernel" ABI is experimental and subject to change + //~^ ERROR is not a supported ABI +} + +// Methods in inherent impl +impl S { + extern "gpu-kernel" fn im1(_: ()) {} //~ ERROR "gpu-kernel" ABI is experimental and subject to change + //~^ ERROR is not a supported ABI +} + +// Function pointer types +type A1 = extern "gpu-kernel" fn(_: ()); //~ ERROR "gpu-kernel" ABI is experimental and subject to change +//~^ WARN the calling convention "gpu-kernel" is not supported on this target +//~^^ WARN this was previously accepted by the compiler but is being phased out + +// Foreign modules +extern "gpu-kernel" {} //~ ERROR "gpu-kernel" ABI is experimental and subject to change +//~^ ERROR is not a supported ABI diff --git a/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.stderr b/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.stderr new file mode 100644 index 0000000000000..b05c16e3d9ee2 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-abi_gpu_kernel.stderr @@ -0,0 +1,114 @@ +error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change + --> $DIR/feature-gate-abi_gpu_kernel.rs:13:8 + | +LL | extern "gpu-kernel" fn f1(_: ()) {} + | ^^^^^^^^^^^^ + | + = note: see issue #135467 for more information + = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change + --> $DIR/feature-gate-abi_gpu_kernel.rs:18:12 + | +LL | extern "gpu-kernel" fn m1(_: ()); + | ^^^^^^^^^^^^ + | + = note: see issue #135467 for more information + = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change + --> $DIR/feature-gate-abi_gpu_kernel.rs:20:12 + | +LL | extern "gpu-kernel" fn dm1(_: ()) {} + | ^^^^^^^^^^^^ + | + = note: see issue #135467 for more information + = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change + --> $DIR/feature-gate-abi_gpu_kernel.rs:28:12 + | +LL | extern "gpu-kernel" fn m1(_: ()) {} + | ^^^^^^^^^^^^ + | + = note: see issue #135467 for more information + = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change + --> $DIR/feature-gate-abi_gpu_kernel.rs:34:12 + | +LL | extern "gpu-kernel" fn im1(_: ()) {} + | ^^^^^^^^^^^^ + | + = note: see issue #135467 for more information + = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change + --> $DIR/feature-gate-abi_gpu_kernel.rs:39:18 + | +LL | type A1 = extern "gpu-kernel" fn(_: ()); + | ^^^^^^^^^^^^ + | + = note: see issue #135467 for more information + = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the extern "gpu-kernel" ABI is experimental and subject to change + --> $DIR/feature-gate-abi_gpu_kernel.rs:44:8 + | +LL | extern "gpu-kernel" {} + | ^^^^^^^^^^^^ + | + = note: see issue #135467 for more information + = help: add `#![feature(abi_gpu_kernel)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: the calling convention "gpu-kernel" is not supported on this target + --> $DIR/feature-gate-abi_gpu_kernel.rs:39:11 + | +LL | type A1 = extern "gpu-kernel" fn(_: ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #130260 + = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default + +error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target + --> $DIR/feature-gate-abi_gpu_kernel.rs:44:1 + | +LL | extern "gpu-kernel" {} + | ^^^^^^^^^^^^^^^^^^^^^^ + +error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target + --> $DIR/feature-gate-abi_gpu_kernel.rs:13:1 + | +LL | extern "gpu-kernel" fn f1(_: ()) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target + --> $DIR/feature-gate-abi_gpu_kernel.rs:20:5 + | +LL | extern "gpu-kernel" fn dm1(_: ()) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target + --> $DIR/feature-gate-abi_gpu_kernel.rs:28:5 + | +LL | extern "gpu-kernel" fn m1(_: ()) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0570]: `"gpu-kernel"` is not a supported ABI for the current target + --> $DIR/feature-gate-abi_gpu_kernel.rs:34:5 + | +LL | extern "gpu-kernel" fn im1(_: ()) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 12 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0570, E0658. +For more information about an error, try `rustc --explain E0570`. diff --git a/tests/ui/feature-gates/feature-gate-abi_ptx.rs b/tests/ui/feature-gates/feature-gate-abi_ptx.rs index 83f48430281a3..e742492303ade 100644 --- a/tests/ui/feature-gates/feature-gate-abi_ptx.rs +++ b/tests/ui/feature-gates/feature-gate-abi_ptx.rs @@ -5,22 +5,22 @@ #[lang="sized"] trait Sized { } -extern "ptx-kernel" fn fu() {} //~ ERROR PTX ABIs are experimental +extern "ptx-kernel" fn fu() {} //~ ERROR extern "ptx-kernel" ABI is experimental trait T { - extern "ptx-kernel" fn mu(); //~ ERROR PTX ABIs are experimental - extern "ptx-kernel" fn dmu() {} //~ ERROR PTX ABIs are experimental + extern "ptx-kernel" fn mu(); //~ ERROR extern "ptx-kernel" ABI is experimental + extern "ptx-kernel" fn dmu() {} //~ ERROR extern "ptx-kernel" ABI is experimental } struct S; impl T for S { - extern "ptx-kernel" fn mu() {} //~ ERROR PTX ABIs are experimental + extern "ptx-kernel" fn mu() {} //~ ERROR extern "ptx-kernel" ABI is experimental } impl S { - extern "ptx-kernel" fn imu() {} //~ ERROR PTX ABIs are experimental + extern "ptx-kernel" fn imu() {} //~ ERROR extern "ptx-kernel" ABI is experimental } -type TAU = extern "ptx-kernel" fn(); //~ ERROR PTX ABIs are experimental +type TAU = extern "ptx-kernel" fn(); //~ ERROR extern "ptx-kernel" ABI is experimental -extern "ptx-kernel" {} //~ ERROR PTX ABIs are experimental +extern "ptx-kernel" {} //~ ERROR extern "ptx-kernel" ABI is experimental diff --git a/tests/ui/feature-gates/feature-gate-abi_ptx.stderr b/tests/ui/feature-gates/feature-gate-abi_ptx.stderr index 22b493e577dd5..d128075919b09 100644 --- a/tests/ui/feature-gates/feature-gate-abi_ptx.stderr +++ b/tests/ui/feature-gates/feature-gate-abi_ptx.stderr @@ -1,4 +1,4 @@ -error[E0658]: PTX ABIs are experimental and subject to change +error[E0658]: the extern "ptx-kernel" ABI is experimental and subject to change --> $DIR/feature-gate-abi_ptx.rs:8:8 | LL | extern "ptx-kernel" fn fu() {} @@ -8,7 +8,7 @@ LL | extern "ptx-kernel" fn fu() {} = help: add `#![feature(abi_ptx)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: PTX ABIs are experimental and subject to change +error[E0658]: the extern "ptx-kernel" ABI is experimental and subject to change --> $DIR/feature-gate-abi_ptx.rs:11:12 | LL | extern "ptx-kernel" fn mu(); @@ -18,7 +18,7 @@ LL | extern "ptx-kernel" fn mu(); = help: add `#![feature(abi_ptx)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: PTX ABIs are experimental and subject to change +error[E0658]: the extern "ptx-kernel" ABI is experimental and subject to change --> $DIR/feature-gate-abi_ptx.rs:12:12 | LL | extern "ptx-kernel" fn dmu() {} @@ -28,7 +28,7 @@ LL | extern "ptx-kernel" fn dmu() {} = help: add `#![feature(abi_ptx)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: PTX ABIs are experimental and subject to change +error[E0658]: the extern "ptx-kernel" ABI is experimental and subject to change --> $DIR/feature-gate-abi_ptx.rs:17:12 | LL | extern "ptx-kernel" fn mu() {} @@ -38,7 +38,7 @@ LL | extern "ptx-kernel" fn mu() {} = help: add `#![feature(abi_ptx)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: PTX ABIs are experimental and subject to change +error[E0658]: the extern "ptx-kernel" ABI is experimental and subject to change --> $DIR/feature-gate-abi_ptx.rs:21:12 | LL | extern "ptx-kernel" fn imu() {} @@ -48,7 +48,7 @@ LL | extern "ptx-kernel" fn imu() {} = help: add `#![feature(abi_ptx)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: PTX ABIs are experimental and subject to change +error[E0658]: the extern "ptx-kernel" ABI is experimental and subject to change --> $DIR/feature-gate-abi_ptx.rs:24:19 | LL | type TAU = extern "ptx-kernel" fn(); @@ -58,7 +58,7 @@ LL | type TAU = extern "ptx-kernel" fn(); = help: add `#![feature(abi_ptx)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: PTX ABIs are experimental and subject to change +error[E0658]: the extern "ptx-kernel" ABI is experimental and subject to change --> $DIR/feature-gate-abi_ptx.rs:26:8 | LL | extern "ptx-kernel" {} diff --git a/tests/ui/feature-gates/feature-gate-abi_unadjusted.rs b/tests/ui/feature-gates/feature-gate-abi_unadjusted.rs index 35a7d73288b5d..1dc6adc0e1495 100644 --- a/tests/ui/feature-gates/feature-gate-abi_unadjusted.rs +++ b/tests/ui/feature-gates/feature-gate-abi_unadjusted.rs @@ -1,5 +1,5 @@ extern "unadjusted" fn foo() { -//~^ ERROR: unadjusted ABI is an implementation detail and perma-unstable +//~^ ERROR: "unadjusted" ABI is an implementation detail and perma-unstable } fn main() { diff --git a/tests/ui/feature-gates/feature-gate-abi_unadjusted.stderr b/tests/ui/feature-gates/feature-gate-abi_unadjusted.stderr index 1d5fb11cd3d2b..462fb79d55703 100644 --- a/tests/ui/feature-gates/feature-gate-abi_unadjusted.stderr +++ b/tests/ui/feature-gates/feature-gate-abi_unadjusted.stderr @@ -1,4 +1,4 @@ -error[E0658]: unadjusted ABI is an implementation detail and perma-unstable +error[E0658]: the extern "unadjusted" ABI is an implementation detail and perma-unstable --> $DIR/feature-gate-abi_unadjusted.rs:1:8 | LL | extern "unadjusted" fn foo() { diff --git a/tests/ui/feature-gates/feature-gate-async-fn-in-dyn-trait.rs b/tests/ui/feature-gates/feature-gate-async-fn-in-dyn-trait.rs index d9ff45f57ecba..278a5451e842e 100644 --- a/tests/ui/feature-gates/feature-gate-async-fn-in-dyn-trait.rs +++ b/tests/ui/feature-gates/feature-gate-async-fn-in-dyn-trait.rs @@ -5,10 +5,10 @@ trait Foo { } async fn takes_dyn_trait(x: &dyn Foo) { - //~^ ERROR the trait `Foo` cannot be made into an object + //~^ ERROR the trait `Foo` is not dyn compatible x.bar().await; - //~^ ERROR the trait `Foo` cannot be made into an object - //~| ERROR the trait `Foo` cannot be made into an object + //~^ ERROR the trait `Foo` is not dyn compatible + //~| ERROR the trait `Foo` is not dyn compatible } fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-async-fn-in-dyn-trait.stderr b/tests/ui/feature-gates/feature-gate-async-fn-in-dyn-trait.stderr index f78fc422410be..ab8c092a82654 100644 --- a/tests/ui/feature-gates/feature-gate-async-fn-in-dyn-trait.stderr +++ b/tests/ui/feature-gates/feature-gate-async-fn-in-dyn-trait.stderr @@ -1,44 +1,47 @@ -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/feature-gate-async-fn-in-dyn-trait.rs:7:30 | LL | async fn takes_dyn_trait(x: &dyn Foo) { - | ^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/feature-gate-async-fn-in-dyn-trait.rs:4:14 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | async fn bar(&self); | ^^^ ...because method `bar` is `async` = help: consider moving `bar` to another trait -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/feature-gate-async-fn-in-dyn-trait.rs:9:7 | LL | x.bar().await; - | ^^^ `Foo` cannot be made into an object + | ^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/feature-gate-async-fn-in-dyn-trait.rs:4:14 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | async fn bar(&self); | ^^^ ...because method `bar` is `async` = help: consider moving `bar` to another trait -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/feature-gate-async-fn-in-dyn-trait.rs:9:5 | LL | x.bar().await; - | ^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/feature-gate-async-fn-in-dyn-trait.rs:4:14 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | async fn bar(&self); | ^^^ ...because method `bar` is `async` = help: consider moving `bar` to another trait diff --git a/tests/ui/feature-gates/feature-gate-cfg-contract-checks.rs b/tests/ui/feature-gates/feature-gate-cfg-contract-checks.rs new file mode 100644 index 0000000000000..cd9bf12b5e773 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-contract-checks.rs @@ -0,0 +1,5 @@ +#![crate_type = "lib"] + +pub fn contract_checks_are_enabled() -> bool { + cfg!(contract_checks) //~ ERROR `cfg(contract_checks)` is experimental +} diff --git a/tests/ui/feature-gates/feature-gate-cfg-contract-checks.stderr b/tests/ui/feature-gates/feature-gate-cfg-contract-checks.stderr new file mode 100644 index 0000000000000..89c6d077f97eb --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-contract-checks.stderr @@ -0,0 +1,13 @@ +error[E0658]: `cfg(contract_checks)` is experimental and subject to change + --> $DIR/feature-gate-cfg-contract-checks.rs:4:10 + | +LL | cfg!(contract_checks) + | ^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(cfg_contract_checks)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-cfg-emscripten-wasm-eh.rs b/tests/ui/feature-gates/feature-gate-cfg-emscripten-wasm-eh.rs new file mode 100644 index 0000000000000..cff98b43fe745 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-emscripten-wasm-eh.rs @@ -0,0 +1,4 @@ +//@ compile-flags: --check-cfg=cfg(emscripten_wasm_eh) +#[cfg(not(emscripten_wasm_eh))] +//~^ `cfg(emscripten_wasm_eh)` is experimental +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-cfg-emscripten-wasm-eh.stderr b/tests/ui/feature-gates/feature-gate-cfg-emscripten-wasm-eh.stderr new file mode 100644 index 0000000000000..67769e3c7586b --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-emscripten-wasm-eh.stderr @@ -0,0 +1,12 @@ +error[E0658]: `cfg(emscripten_wasm_eh)` is experimental and subject to change + --> $DIR/feature-gate-cfg-emscripten-wasm-eh.rs:2:11 + | +LL | #[cfg(not(emscripten_wasm_eh))] + | ^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(cfg_emscripten_wasm_eh)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-cfg-target-thread-local.rs b/tests/ui/feature-gates/feature-gate-cfg-target-thread-local.rs index 3ab5a500dfdbb..131a10ea03b1e 100644 --- a/tests/ui/feature-gates/feature-gate-cfg-target-thread-local.rs +++ b/tests/ui/feature-gates/feature-gate-cfg-target-thread-local.rs @@ -1,4 +1,4 @@ -//@ ignore-windows +//@ ignore-windows FIXME(134939): thread_local + no_mangle doesn't work on Windows //@ aux-build:cfg-target-thread-local.rs #![feature(thread_local)] diff --git a/tests/ui/feature-gates/feature-gate-contracts.rs b/tests/ui/feature-gates/feature-gate-contracts.rs new file mode 100644 index 0000000000000..1759d23077b53 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-contracts.rs @@ -0,0 +1,9 @@ +#![crate_type = "lib"] + +#[core::contracts::requires(x > 0)] +pub fn requires_needs_it(x: i32) { } +//~^^ ERROR use of unstable library feature `contracts` + +#[core::contracts::ensures(|ret| *ret > 0)] +pub fn ensures_needs_it() -> i32 { 10 } +//~^^ ERROR use of unstable library feature `contracts` diff --git a/tests/ui/feature-gates/feature-gate-contracts.stderr b/tests/ui/feature-gates/feature-gate-contracts.stderr new file mode 100644 index 0000000000000..4b0f7150973f1 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-contracts.stderr @@ -0,0 +1,23 @@ +error[E0658]: use of unstable library feature `contracts` + --> $DIR/feature-gate-contracts.rs:3:3 + | +LL | #[core::contracts::requires(x > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `contracts` + --> $DIR/feature-gate-contracts.rs:7:3 + | +LL | #[core::contracts::ensures(|ret| *ret > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-default-field-values.stderr b/tests/ui/feature-gates/feature-gate-default-field-values.stderr index d882c322c8eda..104d72a39861d 100644 --- a/tests/ui/feature-gates/feature-gate-default-field-values.stderr +++ b/tests/ui/feature-gates/feature-gate-default-field-values.stderr @@ -130,7 +130,10 @@ error[E0797]: base expression required after `..` LL | let x = Foo { .. }; | ^ | - = help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields +help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields + | +LL + #![feature(default_field_values)] + | help: add a base expression here | LL | let x = Foo { ../* expr */ }; @@ -142,7 +145,10 @@ error[E0797]: base expression required after `..` LL | let z = Foo { baz: 1, .. }; | ^ | - = help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields +help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields + | +LL + #![feature(default_field_values)] + | help: add a base expression here | LL | let z = Foo { baz: 1, ../* expr */ }; @@ -154,7 +160,10 @@ error[E0797]: base expression required after `..` LL | let x = Bar::Foo { .. }; | ^ | - = help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields +help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields + | +LL + #![feature(default_field_values)] + | help: add a base expression here | LL | let x = Bar::Foo { ../* expr */ }; @@ -166,7 +175,10 @@ error[E0797]: base expression required after `..` LL | let z = Bar::Foo { baz: 1, .. }; | ^ | - = help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields +help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields + | +LL + #![feature(default_field_values)] + | help: add a base expression here | LL | let z = Bar::Foo { baz: 1, ../* expr */ }; @@ -178,7 +190,10 @@ error[E0797]: base expression required after `..` LL | let x = Qux:: { .. }; | ^ | - = help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields +help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields + | +LL + #![feature(default_field_values)] + | help: add a base expression here | LL | let x = Qux:: { ../* expr */ }; @@ -190,7 +205,10 @@ error[E0797]: base expression required after `..` LL | assert!(matches!(Qux:: { bar: S, baz: 42, bat: 2, bay: 4, .. }, x)); | ^ | - = help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields +help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields + | +LL + #![feature(default_field_values)] + | help: add a base expression here | LL | assert!(matches!(Qux:: { bar: S, baz: 42, bat: 2, bay: 4, ../* expr */ }, x)); @@ -202,7 +220,10 @@ error[E0797]: base expression required after `..` LL | let y = Opt { mandatory: None, .. }; | ^ | - = help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields +help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields + | +LL + #![feature(default_field_values)] + | help: add a base expression here | LL | let y = Opt { mandatory: None, ../* expr */ }; @@ -214,7 +235,10 @@ error[E0797]: base expression required after `..` LL | assert!(matches!(Opt { mandatory: None, .. }, z)); | ^ | - = help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields +help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields + | +LL + #![feature(default_field_values)] + | help: add a base expression here | LL | assert!(matches!(Opt { mandatory: None, ../* expr */ }, z)); @@ -260,7 +284,10 @@ error[E0797]: base expression required after `..` LL | let y = OptEnum::Variant { mandatory: None, .. }; | ^ | - = help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields +help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields + | +LL + #![feature(default_field_values)] + | help: add a base expression here | LL | let y = OptEnum::Variant { mandatory: None, ../* expr */ }; @@ -272,7 +299,10 @@ error[E0797]: base expression required after `..` LL | assert!(matches!(OptEnum::Variant { mandatory: None, .. }, z)); | ^ | - = help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields +help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields + | +LL + #![feature(default_field_values)] + | help: add a base expression here | LL | assert!(matches!(OptEnum::Variant { mandatory: None, ../* expr */ }, z)); diff --git a/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.rs b/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.rs index 3c9e903d4ba00..37eabbf160220 100644 --- a/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.rs +++ b/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.rs @@ -30,6 +30,6 @@ impl Trait for i32 { fn main() { Ptr(Box::new(4)) as Ptr; - //~^ ERROR the trait `Trait` cannot be made into an object - //~^^ ERROR the trait `Trait` cannot be made into an object + //~^ ERROR the trait `Trait` is not dyn compatible + //~^^ ERROR the trait `Trait` is not dyn compatible } diff --git a/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.stderr b/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.stderr index 28caaf8356f8e..6634ce1211867 100644 --- a/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.stderr +++ b/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.stderr @@ -1,38 +1,40 @@ -error[E0038]: the trait `Trait` cannot be made into an object +error[E0038]: the trait `Trait` is not dyn compatible --> $DIR/feature-gate-dispatch-from-dyn-missing-impl.rs:32:25 | LL | fn ptr(self: Ptr); | --------- help: consider changing method `ptr`'s `self` parameter to be `&self`: `&Self` ... LL | Ptr(Box::new(4)) as Ptr; - | ^^^^^^^^^^^^^^ `Trait` cannot be made into an object + | ^^^^^^^^^^^^^^ `Trait` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/feature-gate-dispatch-from-dyn-missing-impl.rs:25:18 | LL | trait Trait { - | ----- this trait cannot be made into an object... + | ----- this trait is not dyn compatible... LL | fn ptr(self: Ptr); | ^^^^^^^^^ ...because method `ptr`'s `self` parameter cannot be dispatched on - = help: only type `i32` implements the trait, consider using it directly instead + = help: only type `i32` implements `Trait`; consider using it directly instead. -error[E0038]: the trait `Trait` cannot be made into an object +error[E0038]: the trait `Trait` is not dyn compatible --> $DIR/feature-gate-dispatch-from-dyn-missing-impl.rs:32:5 | LL | fn ptr(self: Ptr); | --------- help: consider changing method `ptr`'s `self` parameter to be `&self`: `&Self` ... LL | Ptr(Box::new(4)) as Ptr; - | ^^^^^^^^^^^^^^^^ `Trait` cannot be made into an object + | ^^^^^^^^^^^^^^^^ `Trait` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/feature-gate-dispatch-from-dyn-missing-impl.rs:25:18 | LL | trait Trait { - | ----- this trait cannot be made into an object... + | ----- this trait is not dyn compatible... LL | fn ptr(self: Ptr); | ^^^^^^^^^ ...because method `ptr`'s `self` parameter cannot be dispatched on - = help: only type `i32` implements the trait, consider using it directly instead + = help: only type `i32` implements `Trait`; consider using it directly instead. = note: required for the cast from `Ptr<{integer}>` to `Ptr` error: aborting due to 2 previous errors diff --git a/tests/ui/feature-gates/feature-gate-dyn_compatible_for_dispatch.stderr b/tests/ui/feature-gates/feature-gate-dyn_compatible_for_dispatch.stderr index ed021c154a5b8..2c3edd6e6a5fc 100644 --- a/tests/ui/feature-gates/feature-gate-dyn_compatible_for_dispatch.stderr +++ b/tests/ui/feature-gates/feature-gate-dyn_compatible_for_dispatch.stderr @@ -1,28 +1,30 @@ -error[E0038]: the trait `DynIncompatible1` cannot be made into an object +error[E0038]: the trait `DynIncompatible1` is not dyn compatible --> $DIR/feature-gate-dyn_compatible_for_dispatch.rs:18:40 | LL | fn takes_dyn_incompatible_ref(obj: &dyn DynIncompatible1) { - | ^^^^^^^^^^^^^^^^^^^^ `DynIncompatible1` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^ `DynIncompatible1` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/feature-gate-dyn_compatible_for_dispatch.rs:4:25 | LL | trait DynIncompatible1: Sized {} | ---------------- ^^^^^ ...because it requires `Self: Sized` | | - | this trait cannot be made into an object... + | this trait is not dyn compatible... -error[E0038]: the trait `DynIncompatible2` cannot be made into an object +error[E0038]: the trait `DynIncompatible2` is not dyn compatible --> $DIR/feature-gate-dyn_compatible_for_dispatch.rs:22:46 | LL | fn return_dyn_incompatible_ref() -> &'static dyn DynIncompatible2 { - | ^^^^^^^^^^^^^^^^^^^^ `DynIncompatible2` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^ `DynIncompatible2` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/feature-gate-dyn_compatible_for_dispatch.rs:7:8 | LL | trait DynIncompatible2 { - | ---------------- this trait cannot be made into an object... + | ---------------- this trait is not dyn compatible... LL | fn static_fn() {} | ^^^^^^^^^ ...because associated function `static_fn` has no `self` parameter help: consider turning `static_fn` into a method by giving it a `&self` argument @@ -34,49 +36,52 @@ help: alternatively, consider constraining `static_fn` so it does not apply to t LL | fn static_fn() where Self: Sized {} | +++++++++++++++++ -error[E0038]: the trait `DynIncompatible3` cannot be made into an object +error[E0038]: the trait `DynIncompatible3` is not dyn compatible --> $DIR/feature-gate-dyn_compatible_for_dispatch.rs:27:40 | LL | fn takes_dyn_incompatible_box(obj: Box) { - | ^^^^^^^^^^^^^^^^^^^^ `DynIncompatible3` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^ `DynIncompatible3` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/feature-gate-dyn_compatible_for_dispatch.rs:11:8 | LL | trait DynIncompatible3 { - | ---------------- this trait cannot be made into an object... + | ---------------- this trait is not dyn compatible... LL | fn foo(&self); | ^^^ ...because method `foo` has generic type parameters = help: consider moving `foo` to another trait -error[E0038]: the trait `DynIncompatible4` cannot be made into an object +error[E0038]: the trait `DynIncompatible4` is not dyn compatible --> $DIR/feature-gate-dyn_compatible_for_dispatch.rs:31:48 | LL | fn return_dyn_incompatible_rc() -> std::rc::Rc { - | ^^^^^^^^^^^^^^^^^^^^ `DynIncompatible4` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^ `DynIncompatible4` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/feature-gate-dyn_compatible_for_dispatch.rs:15:22 | LL | trait DynIncompatible4 { - | ---------------- this trait cannot be made into an object... + | ---------------- this trait is not dyn compatible... LL | fn foo(&self, s: &Self); | ^^^^^ ...because method `foo` references the `Self` type in this parameter = help: consider moving `foo` to another trait -error[E0038]: the trait `DynIncompatible1` cannot be made into an object +error[E0038]: the trait `DynIncompatible1` is not dyn compatible --> $DIR/feature-gate-dyn_compatible_for_dispatch.rs:38:16 | LL | impl Trait for dyn DynIncompatible1 {} - | ^^^^^^^^^^^^^^^^^^^^ `DynIncompatible1` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^ `DynIncompatible1` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/feature-gate-dyn_compatible_for_dispatch.rs:4:25 | LL | trait DynIncompatible1: Sized {} | ---------------- ^^^^^ ...because it requires `Self: Sized` | | - | this trait cannot be made into an object... + | this trait is not dyn compatible... error: aborting due to 5 previous errors diff --git a/tests/ui/feature-gates/feature-gate-extern_absolute_paths.stderr b/tests/ui/feature-gates/feature-gate-extern_absolute_paths.stderr index 0234480ac5acb..2f5e3650734ec 100644 --- a/tests/ui/feature-gates/feature-gate-extern_absolute_paths.stderr +++ b/tests/ui/feature-gates/feature-gate-extern_absolute_paths.stderr @@ -15,8 +15,9 @@ LL | let _: u8 = ::core::default::Default(); | help: try using `std` instead of `core` | -LL | let _: u8 = ::std::default::Default(); - | ~~~ +LL - let _: u8 = ::core::default::Default(); +LL + let _: u8 = ::std::default::Default(); + | help: consider importing this module | LL + use std::default; diff --git a/tests/ui/feature-gates/feature-gate-extern_system_varargs.rs b/tests/ui/feature-gates/feature-gate-extern_system_varargs.rs new file mode 100644 index 0000000000000..f5cfbe72ca8db --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-extern_system_varargs.rs @@ -0,0 +1,7 @@ +fn system(f: extern "system" fn(usize, ...)) { + //~^ ERROR using calling conventions other than `C` or `cdecl` for varargs functions is unstable + + f(22, 44); +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-extern_system_varargs.stderr b/tests/ui/feature-gates/feature-gate-extern_system_varargs.stderr new file mode 100644 index 0000000000000..f3206b25264e0 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-extern_system_varargs.stderr @@ -0,0 +1,13 @@ +error[E0658]: using calling conventions other than `C` or `cdecl` for varargs functions is unstable + --> $DIR/feature-gate-extern_system_varargs.rs:1:14 + | +LL | fn system(f: extern "system" fn(usize, ...)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #136946 for more information + = help: add `#![feature(extern_system_varargs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-fn_align.rs b/tests/ui/feature-gates/feature-gate-fn_align.rs index ea873dba269c4..06784a45d76c8 100644 --- a/tests/ui/feature-gates/feature-gate-fn_align.rs +++ b/tests/ui/feature-gates/feature-gate-fn_align.rs @@ -2,3 +2,8 @@ #[repr(align(16))] //~ ERROR `repr(align)` attributes on functions are unstable fn requires_alignment() {} + +trait MyTrait { + #[repr(align)] //~ ERROR `repr(align)` attributes on functions are unstable + fn myfun(); +} diff --git a/tests/ui/feature-gates/feature-gate-fn_align.stderr b/tests/ui/feature-gates/feature-gate-fn_align.stderr index eec332792b76e..cd9900c605117 100644 --- a/tests/ui/feature-gates/feature-gate-fn_align.stderr +++ b/tests/ui/feature-gates/feature-gate-fn_align.stderr @@ -8,6 +8,16 @@ LL | #[repr(align(16))] = help: add `#![feature(fn_align)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 1 previous error +error[E0658]: `repr(align)` attributes on functions are unstable + --> $DIR/feature-gate-fn_align.rs:7:12 + | +LL | #[repr(align)] + | ^^^^^ + | + = note: see issue #82232 for more information + = help: add `#![feature(fn_align)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-generic_arg_infer.normal.stderr b/tests/ui/feature-gates/feature-gate-generic_arg_infer.normal.stderr index 97370f0489b56..73e6988b09cc8 100644 --- a/tests/ui/feature-gates/feature-gate-generic_arg_infer.normal.stderr +++ b/tests/ui/feature-gates/feature-gate-generic_arg_infer.normal.stderr @@ -1,17 +1,5 @@ -error: in expressions, `_` can only be used on the left-hand side of an assignment - --> $DIR/feature-gate-generic_arg_infer.rs:11:27 - | -LL | let _x: [u8; 3] = [0; _]; - | ^ `_` not allowed here - -error: in expressions, `_` can only be used on the left-hand side of an assignment - --> $DIR/feature-gate-generic_arg_infer.rs:14:18 - | -LL | let _y: [u8; _] = [0; 3]; - | ^ `_` not allowed here - error[E0658]: using `_` for array lengths is unstable - --> $DIR/feature-gate-generic_arg_infer.rs:14:18 + --> $DIR/feature-gate-generic_arg_infer.rs:13:18 | LL | let _y: [u8; _] = [0; 3]; | ^ help: consider specifying the array length: `3` @@ -20,17 +8,15 @@ LL | let _y: [u8; _] = [0; 3]; = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0747]: type provided when a constant was expected - --> $DIR/feature-gate-generic_arg_infer.rs:20:20 +error[E0658]: const arguments cannot yet be inferred with `_` + --> $DIR/feature-gate-generic_arg_infer.rs:18:20 | -LL | let _x = foo::<_>([1,2]); +LL | let _x = foo::<_>([1, 2]); | ^ | - = help: const arguments cannot yet be inferred with `_` -help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable - | -LL + #![feature(generic_arg_infer)] - | + = note: see issue #85077 for more information + = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: using `_` for array lengths is unstable --> $DIR/feature-gate-generic_arg_infer.rs:11:27 @@ -42,7 +28,6 @@ LL | let _x: [u8; 3] = [0; _]; = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0658, E0747. -For more information about an error, try `rustc --explain E0658`. +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-generic_arg_infer.rs b/tests/ui/feature-gates/feature-gate-generic_arg_infer.rs index 0473253004a45..147978b0557a6 100644 --- a/tests/ui/feature-gates/feature-gate-generic_arg_infer.rs +++ b/tests/ui/feature-gates/feature-gate-generic_arg_infer.rs @@ -4,20 +4,18 @@ #![cfg_attr(feature, feature(generic_arg_infer))] fn foo(_: [u8; N]) -> [u8; N] { - [0; N] + [0; N] } fn bar() { let _x: [u8; 3] = [0; _]; //[normal]~^ ERROR: using `_` for array lengths is unstable - //[normal]~| ERROR: in expressions, `_` can only be used on the left-hand side of an assignment let _y: [u8; _] = [0; 3]; //[normal]~^ ERROR: using `_` for array lengths is unstable - //[normal]~| ERROR: in expressions, `_` can only be used on the left-hand side of an assignment } fn main() { - let _x = foo::<_>([1,2]); - //[normal]~^ ERROR: type provided when a constant was expected + let _x = foo::<_>([1, 2]); + //[normal]~^ ERROR: const arguments cannot yet be inferred with `_` bar(); } diff --git a/tests/ui/feature-gates/feature-gate-guard-patterns.rs b/tests/ui/feature-gates/feature-gate-guard-patterns.rs index 929e8ef3181f9..52ed89e668b1f 100644 --- a/tests/ui/feature-gates/feature-gate-guard-patterns.rs +++ b/tests/ui/feature-gates/feature-gate-guard-patterns.rs @@ -1,3 +1,5 @@ +#![allow(irrefutable_let_patterns)] + fn match_guards_still_work() { match 0 { 0 if guard(0) => {}, @@ -24,11 +26,9 @@ fn other_guards_dont() { if let (x if guard(x)) = 0 {} //~^ ERROR: guard patterns are experimental - //~| WARN: irrefutable while let (x if guard(x)) = 0 {} //~^ ERROR: guard patterns are experimental - //~| WARN: irrefutable #[cfg(FALSE)] while let (x if guard(x)) = 0 {} diff --git a/tests/ui/feature-gates/feature-gate-guard-patterns.stderr b/tests/ui/feature-gates/feature-gate-guard-patterns.stderr index 0613b5c95a413..8b85b663889f7 100644 --- a/tests/ui/feature-gates/feature-gate-guard-patterns.stderr +++ b/tests/ui/feature-gates/feature-gate-guard-patterns.stderr @@ -1,5 +1,5 @@ error: unexpected parentheses surrounding `match` arm pattern - --> $DIR/feature-gate-guard-patterns.rs:10:9 + --> $DIR/feature-gate-guard-patterns.rs:12:9 | LL | (0 if guard(0)) => {}, | ^ ^ @@ -11,7 +11,7 @@ LL + 0 if guard(0) => {}, | error[E0425]: cannot find value `x` in this scope - --> $DIR/feature-gate-guard-patterns.rs:21:22 + --> $DIR/feature-gate-guard-patterns.rs:23:22 | LL | let ((x if guard(x)) | x) = 0; | ^ not found in this scope @@ -23,13 +23,13 @@ LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) { | ^ | help: the binding `x` is available in a different scope in the same function - --> $DIR/feature-gate-guard-patterns.rs:21:11 + --> $DIR/feature-gate-guard-patterns.rs:23:11 | LL | let ((x if guard(x)) | x) = 0; | ^ error[E0658]: guard patterns are experimental - --> $DIR/feature-gate-guard-patterns.rs:16:15 + --> $DIR/feature-gate-guard-patterns.rs:18:15 | LL | (0 if guard(0)) | 1 => {}, | ^^^^^^^^ @@ -40,7 +40,7 @@ LL | (0 if guard(0)) | 1 => {}, = help: consider using match arm guards error[E0658]: guard patterns are experimental - --> $DIR/feature-gate-guard-patterns.rs:21:16 + --> $DIR/feature-gate-guard-patterns.rs:23:16 | LL | let ((x if guard(x)) | x) = 0; | ^^^^^^^^ @@ -51,7 +51,7 @@ LL | let ((x if guard(x)) | x) = 0; = help: consider using match arm guards error[E0658]: guard patterns are experimental - --> $DIR/feature-gate-guard-patterns.rs:25:18 + --> $DIR/feature-gate-guard-patterns.rs:27:18 | LL | if let (x if guard(x)) = 0 {} | ^^^^^^^^ @@ -62,7 +62,7 @@ LL | if let (x if guard(x)) = 0 {} = help: consider using match arm guards error[E0658]: guard patterns are experimental - --> $DIR/feature-gate-guard-patterns.rs:29:21 + --> $DIR/feature-gate-guard-patterns.rs:30:21 | LL | while let (x if guard(x)) = 0 {} | ^^^^^^^^ @@ -94,26 +94,7 @@ LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) { = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = help: consider using match arm guards -warning: irrefutable `if let` pattern - --> $DIR/feature-gate-guard-patterns.rs:25:8 - | -LL | if let (x if guard(x)) = 0 {} - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this pattern will always match, so the `if let` is useless - = help: consider replacing the `if let` with a `let` - = note: `#[warn(irrefutable_let_patterns)]` on by default - -warning: irrefutable `while let` pattern - --> $DIR/feature-gate-guard-patterns.rs:29:11 - | -LL | while let (x if guard(x)) = 0 {} - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this pattern will always match, so the loop will never exit - = help: consider instead using a `loop { ... }` with a `let` inside it - -error: aborting due to 9 previous errors; 2 warnings emitted +error: aborting due to 9 previous errors Some errors have detailed explanations: E0425, E0658. For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/feature-gates/feature-gate-import-trait-associated-functions.rs b/tests/ui/feature-gates/feature-gate-import-trait-associated-functions.rs new file mode 100644 index 0000000000000..aec13fb020288 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-import-trait-associated-functions.rs @@ -0,0 +1,63 @@ +//@ edition:2018 +use std::collections::HashMap; + +use A::{DEFAULT, new}; +//~^ ERROR `use` associated items of traits is unstable [E0658] +//~| ERROR `use` associated items of traits is unstable [E0658] +use Default::default; +//~^ ERROR `use` associated items of traits is unstable [E0658] + +struct S { + a: HashMap, +} + +impl S { + fn new() -> S { + S { a: default() } + } +} + +trait A: Sized { + const DEFAULT: Option = None; + fn new() -> Self; + fn do_something(&self); +} + +mod b { + use super::A::{self, DEFAULT, new}; + //~^ ERROR `use` associated items of traits is unstable [E0658] + //~| ERROR `use` associated items of traits is unstable [E0658] + + struct B(); + + impl A for B { + const DEFAULT: Option = Some(B()); + fn new() -> Self { + B() + } + + fn do_something(&self) {} + } + + fn f() { + let b: B = new(); + b.do_something(); + let c: B = DEFAULT.unwrap(); + } +} + +impl A for S { + fn new() -> Self { + S::new() + } + + fn do_something(&self) {} +} + +fn f() { + let s: S = new(); + s.do_something(); + let t: Option = DEFAULT; +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-import-trait-associated-functions.stderr b/tests/ui/feature-gates/feature-gate-import-trait-associated-functions.stderr new file mode 100644 index 0000000000000..d342f5bd5512e --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-import-trait-associated-functions.stderr @@ -0,0 +1,53 @@ +error[E0658]: `use` associated items of traits is unstable + --> $DIR/feature-gate-import-trait-associated-functions.rs:4:9 + | +LL | use A::{DEFAULT, new}; + | ^^^^^^^ + | + = note: see issue #134691 for more information + = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `use` associated items of traits is unstable + --> $DIR/feature-gate-import-trait-associated-functions.rs:4:18 + | +LL | use A::{DEFAULT, new}; + | ^^^ + | + = note: see issue #134691 for more information + = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `use` associated items of traits is unstable + --> $DIR/feature-gate-import-trait-associated-functions.rs:7:5 + | +LL | use Default::default; + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #134691 for more information + = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `use` associated items of traits is unstable + --> $DIR/feature-gate-import-trait-associated-functions.rs:27:26 + | +LL | use super::A::{self, DEFAULT, new}; + | ^^^^^^^ + | + = note: see issue #134691 for more information + = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `use` associated items of traits is unstable + --> $DIR/feature-gate-import-trait-associated-functions.rs:27:35 + | +LL | use super::A::{self, DEFAULT, new}; + | ^^^ + | + = note: see issue #134691 for more information + = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-intrinsics.rs b/tests/ui/feature-gates/feature-gate-intrinsics.rs index e0dc3cc579d79..65806a0223e74 100644 --- a/tests/ui/feature-gates/feature-gate-intrinsics.rs +++ b/tests/ui/feature-gates/feature-gate-intrinsics.rs @@ -1,8 +1,8 @@ -extern "rust-intrinsic" { //~ ERROR intrinsics are subject to change +extern "rust-intrinsic" { //~ ERROR "rust-intrinsic" ABI is an implementation detail fn bar(); //~ ERROR unrecognized intrinsic function: `bar` } -extern "rust-intrinsic" fn baz() {} //~ ERROR intrinsics are subject to change +extern "rust-intrinsic" fn baz() {} //~ ERROR "rust-intrinsic" ABI is an implementation detail //~^ ERROR intrinsic must be in fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-intrinsics.stderr b/tests/ui/feature-gates/feature-gate-intrinsics.stderr index 577a620e2d2c5..97246f05258f0 100644 --- a/tests/ui/feature-gates/feature-gate-intrinsics.stderr +++ b/tests/ui/feature-gates/feature-gate-intrinsics.stderr @@ -1,4 +1,4 @@ -error[E0658]: intrinsics are subject to change +error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable --> $DIR/feature-gate-intrinsics.rs:1:8 | LL | extern "rust-intrinsic" { @@ -7,7 +7,7 @@ LL | extern "rust-intrinsic" { = help: add `#![feature(intrinsics)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: intrinsics are subject to change +error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable --> $DIR/feature-gate-intrinsics.rs:5:8 | LL | extern "rust-intrinsic" fn baz() {} diff --git a/tests/ui/feature-gates/feature-gate-keylocker_x86.rs b/tests/ui/feature-gates/feature-gate-keylocker_x86.rs new file mode 100644 index 0000000000000..cef80ad41a892 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-keylocker_x86.rs @@ -0,0 +1,6 @@ +//@ only-x86_64 +#[target_feature(enable = "kl")] +//~^ ERROR: currently unstable +unsafe fn foo() {} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-keylocker_x86.stderr b/tests/ui/feature-gates/feature-gate-keylocker_x86.stderr new file mode 100644 index 0000000000000..ed814d3a3ce2e --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-keylocker_x86.stderr @@ -0,0 +1,13 @@ +error[E0658]: the target feature `kl` is currently unstable + --> $DIR/feature-gate-keylocker_x86.rs:2:18 + | +LL | #[target_feature(enable = "kl")] + | ^^^^^^^^^^^^^ + | + = note: see issue #134813 for more information + = help: add `#![feature(keylocker_x86)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs index debe143b09172..6a832744e3ee9 100644 --- a/tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs +++ b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs @@ -11,14 +11,14 @@ fn foo(_: Box) {} //~^ ERROR `?Trait` is not permitted in trait object types fn bar(_: T) {} //~^ ERROR type parameter has more than one relaxed default bound, only one is supported -//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default -//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +//~| ERROR relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +//~| ERROR relaxing a default bound only does something for `?Sized`; all other traits are not bound by default trait Trait {} // Do not suggest `#![feature(more_maybe_bounds)]` for repetitions fn baz(_ : T) {} //~^ ERROR type parameter has more than one relaxed default bound, only one is supported -//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default -//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +//~| ERROR relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +//~| ERROR relaxing a default bound only does something for `?Sized`; all other traits are not bound by default fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr index e6d65e059977a..729df4eb37cac 100644 --- a/tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr +++ b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr @@ -35,13 +35,13 @@ LL | fn bar(_: T) {} = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +error: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default --> $DIR/feature-gate-more-maybe-bounds.rs:12:11 | LL | fn bar(_: T) {} | ^^^^^^^ -warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +error: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default --> $DIR/feature-gate-more-maybe-bounds.rs:12:21 | LL | fn bar(_: T) {} @@ -53,19 +53,19 @@ error[E0203]: type parameter has more than one relaxed default bound, only one i LL | fn baz(_ : T) {} | ^^^^^^ ^^^^^^ -warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +error: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default --> $DIR/feature-gate-more-maybe-bounds.rs:19:11 | LL | fn baz(_ : T) {} | ^^^^^^ -warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +error: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default --> $DIR/feature-gate-more-maybe-bounds.rs:19:20 | LL | fn baz(_ : T) {} | ^^^^^^ -error: aborting due to 5 previous errors; 4 warnings emitted +error: aborting due to 9 previous errors Some errors have detailed explanations: E0203, E0658. For more information about an error, try `rustc --explain E0203`. diff --git a/tests/ui/feature-gates/feature-gate-negate-unsigned.stderr b/tests/ui/feature-gates/feature-gate-negate-unsigned.stderr index 696326157ce29..a6e0e668261cb 100644 --- a/tests/ui/feature-gates/feature-gate-negate-unsigned.stderr +++ b/tests/ui/feature-gates/feature-gate-negate-unsigned.stderr @@ -7,8 +7,9 @@ LL | let _max: usize = -1; = note: unsigned values cannot be negated help: you may have meant the maximum value of `usize` | -LL | let _max: usize = usize::MAX; - | ~~~~~~~~~~ +LL - let _max: usize = -1; +LL + let _max: usize = usize::MAX; + | error[E0600]: cannot apply unary operator `-` to type `u8` --> $DIR/feature-gate-negate-unsigned.rs:14:14 diff --git a/tests/ui/feature-gates/feature-gate-never_patterns.stderr b/tests/ui/feature-gates/feature-gate-never_patterns.stderr index 9f94e96203518..dcd5db56da882 100644 --- a/tests/ui/feature-gates/feature-gate-never_patterns.stderr +++ b/tests/ui/feature-gates/feature-gate-never_patterns.stderr @@ -10,7 +10,8 @@ LL | (Some(_),) | + + help: ...or a vertical bar to match on multiple alternatives | -LL | Some(_) | +LL - Some(_), +LL + Some(_) | | error[E0658]: `!` patterns are experimental diff --git a/tests/ui/feature-gates/feature-gate-new_range.rs b/tests/ui/feature-gates/feature-gate-new_range.rs new file mode 100644 index 0000000000000..ecb73546d6a60 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-new_range.rs @@ -0,0 +1,10 @@ +#![feature(new_range_api)] + +fn main() { + let a: core::range::RangeFrom = 1..; + //~^ mismatched types + let b: core::range::Range = 2..3; + //~^ mismatched types + let c: core::range::RangeInclusive = 4..=5; + //~^ mismatched types +} diff --git a/tests/ui/feature-gates/feature-gate-new_range.stderr b/tests/ui/feature-gates/feature-gate-new_range.stderr new file mode 100644 index 0000000000000..c4241390418b6 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-new_range.stderr @@ -0,0 +1,48 @@ +error[E0308]: mismatched types + --> $DIR/feature-gate-new_range.rs:4:41 + | +LL | let a: core::range::RangeFrom = 1..; + | -------------------------- ^^^ expected `RangeFrom`, found `RangeFrom<{integer}>` + | | + | expected due to this + | + = note: expected struct `std::range::RangeFrom` + found struct `std::ops::RangeFrom<{integer}>` +help: call `Into::into` on this expression to convert `std::ops::RangeFrom<{integer}>` into `std::range::RangeFrom` + | +LL | let a: core::range::RangeFrom = 1...into(); + | +++++++ + +error[E0308]: mismatched types + --> $DIR/feature-gate-new_range.rs:6:37 + | +LL | let b: core::range::Range = 2..3; + | ---------------------- ^^^^ expected `Range`, found `Range<{integer}>` + | | + | expected due to this + | + = note: expected struct `std::range::Range` + found struct `std::ops::Range<{integer}>` +help: call `Into::into` on this expression to convert `std::ops::Range<{integer}>` into `std::range::Range` + | +LL | let b: core::range::Range = 2..3.into(); + | +++++++ + +error[E0308]: mismatched types + --> $DIR/feature-gate-new_range.rs:8:46 + | +LL | let c: core::range::RangeInclusive = 4..=5; + | ------------------------------- ^^^^^ expected `RangeInclusive`, found `RangeInclusive<{integer}>` + | | + | expected due to this + | + = note: expected struct `std::range::RangeInclusive` + found struct `std::ops::RangeInclusive<{integer}>` +help: call `Into::into` on this expression to convert `std::ops::RangeInclusive<{integer}>` into `std::range::RangeInclusive` + | +LL | let c: core::range::RangeInclusive = 4..=5.into(); + | +++++++ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/feature-gates/feature-gate-optimize_attribute.rs b/tests/ui/feature-gates/feature-gate-optimize_attribute.rs index 7f9cada6a47d5..77cc307c9f453 100644 --- a/tests/ui/feature-gates/feature-gate-optimize_attribute.rs +++ b/tests/ui/feature-gates/feature-gate-optimize_attribute.rs @@ -6,6 +6,9 @@ fn size() {} #[optimize(speed)] //~ ERROR the `#[optimize]` attribute is an experimental feature fn speed() {} +#[optimize(none)] //~ ERROR the `#[optimize]` attribute is an experimental feature +fn none() {} + #[optimize(banana)] //~^ ERROR the `#[optimize]` attribute is an experimental feature //~| ERROR E0722 diff --git a/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr b/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr index ca8f4a078f09e..4e6e4ac2703a8 100644 --- a/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr +++ b/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr @@ -21,6 +21,16 @@ LL | #[optimize(speed)] error[E0658]: the `#[optimize]` attribute is an experimental feature --> $DIR/feature-gate-optimize_attribute.rs:9:1 | +LL | #[optimize(none)] + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #54882 for more information + = help: add `#![feature(optimize_attribute)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the `#[optimize]` attribute is an experimental feature + --> $DIR/feature-gate-optimize_attribute.rs:12:1 + | LL | #[optimize(banana)] | ^^^^^^^^^^^^^^^^^^^ | @@ -29,12 +39,12 @@ LL | #[optimize(banana)] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0722]: invalid argument - --> $DIR/feature-gate-optimize_attribute.rs:9:12 + --> $DIR/feature-gate-optimize_attribute.rs:12:12 | LL | #[optimize(banana)] | ^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0658, E0722. For more information about an error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-repr-simd.rs b/tests/ui/feature-gates/feature-gate-repr-simd.rs index 65ade97c7e1c3..091dc479ef3d4 100644 --- a/tests/ui/feature-gates/feature-gate-repr-simd.rs +++ b/tests/ui/feature-gates/feature-gate-repr-simd.rs @@ -1,9 +1,17 @@ -#[repr(simd)] //~ error: SIMD types are experimental +#[repr(simd)] //~ ERROR: SIMD types are experimental struct Foo([u64; 2]); #[repr(C)] //~ ERROR conflicting representation hints //~^ WARN this was previously accepted -#[repr(simd)] //~ error: SIMD types are experimental +#[repr(simd)] //~ ERROR: SIMD types are experimental struct Bar([u64; 2]); +#[repr(simd)] //~ ERROR: SIMD types are experimental +//~^ ERROR: attribute should be applied to a struct +union U {f: u32} + +#[repr(simd)] //~ ERROR: SIMD types are experimental +//~^ error: attribute should be applied to a struct +enum E { X } + fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-repr-simd.stderr b/tests/ui/feature-gates/feature-gate-repr-simd.stderr index 5a0ceb2dd74fb..3bf8ec6170597 100644 --- a/tests/ui/feature-gates/feature-gate-repr-simd.stderr +++ b/tests/ui/feature-gates/feature-gate-repr-simd.stderr @@ -18,6 +18,26 @@ LL | #[repr(simd)] = help: add `#![feature(repr_simd)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +error[E0658]: SIMD types are experimental and possibly buggy + --> $DIR/feature-gate-repr-simd.rs:9:1 + | +LL | #[repr(simd)] + | ^^^^^^^^^^^^^ + | + = note: see issue #27731 for more information + = help: add `#![feature(repr_simd)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: SIMD types are experimental and possibly buggy + --> $DIR/feature-gate-repr-simd.rs:13:1 + | +LL | #[repr(simd)] + | ^^^^^^^^^^^^^ + | + = note: see issue #27731 for more information + = help: add `#![feature(repr_simd)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0566]: conflicting representation hints --> $DIR/feature-gate-repr-simd.rs:4:8 | @@ -31,10 +51,28 @@ LL | #[repr(simd)] = note: for more information, see issue #68585 = note: `#[deny(conflicting_repr_hints)]` on by default -error: aborting due to 3 previous errors +error[E0517]: attribute should be applied to a struct + --> $DIR/feature-gate-repr-simd.rs:9:8 + | +LL | #[repr(simd)] + | ^^^^ +LL | +LL | union U {f: u32} + | ---------------- not a struct + +error[E0517]: attribute should be applied to a struct + --> $DIR/feature-gate-repr-simd.rs:13:8 + | +LL | #[repr(simd)] + | ^^^^ +LL | +LL | enum E { X } + | ------------ not a struct + +error: aborting due to 7 previous errors -Some errors have detailed explanations: E0566, E0658. -For more information about an error, try `rustc --explain E0566`. +Some errors have detailed explanations: E0517, E0566, E0658. +For more information about an error, try `rustc --explain E0517`. Future incompatibility report: Future breakage diagnostic: error[E0566]: conflicting representation hints --> $DIR/feature-gate-repr-simd.rs:4:8 diff --git a/tests/ui/feature-gates/feature-gate-rust_cold_cc.rs b/tests/ui/feature-gates/feature-gate-rust_cold_cc.rs index 9ba8e32ac07a3..4f47eb55b395f 100644 --- a/tests/ui/feature-gates/feature-gate-rust_cold_cc.rs +++ b/tests/ui/feature-gates/feature-gate-rust_cold_cc.rs @@ -1,21 +1,21 @@ #![crate_type = "lib"] -extern "rust-cold" fn fu() {} //~ ERROR rust-cold is experimental +extern "rust-cold" fn fu() {} //~ ERROR "rust-cold" ABI is experimental trait T { - extern "rust-cold" fn mu(); //~ ERROR rust-cold is experimental - extern "rust-cold" fn dmu() {} //~ ERROR rust-cold is experimental + extern "rust-cold" fn mu(); //~ ERROR "rust-cold" ABI is experimental + extern "rust-cold" fn dmu() {} //~ ERROR "rust-cold" ABI is experimental } struct S; impl T for S { - extern "rust-cold" fn mu() {} //~ ERROR rust-cold is experimental + extern "rust-cold" fn mu() {} //~ ERROR "rust-cold" ABI is experimental } impl S { - extern "rust-cold" fn imu() {} //~ ERROR rust-cold is experimental + extern "rust-cold" fn imu() {} //~ ERROR "rust-cold" ABI is experimental } -type TAU = extern "rust-cold" fn(); //~ ERROR rust-cold is experimental +type TAU = extern "rust-cold" fn(); //~ ERROR "rust-cold" ABI is experimental -extern "rust-cold" {} //~ ERROR rust-cold is experimental +extern "rust-cold" {} //~ ERROR "rust-cold" ABI is experimental diff --git a/tests/ui/feature-gates/feature-gate-rust_cold_cc.stderr b/tests/ui/feature-gates/feature-gate-rust_cold_cc.stderr index eeff9534d528f..9547c64f2b2ec 100644 --- a/tests/ui/feature-gates/feature-gate-rust_cold_cc.stderr +++ b/tests/ui/feature-gates/feature-gate-rust_cold_cc.stderr @@ -1,4 +1,4 @@ -error[E0658]: rust-cold is experimental and subject to change +error[E0658]: the extern "rust-cold" ABI is experimental and subject to change --> $DIR/feature-gate-rust_cold_cc.rs:3:8 | LL | extern "rust-cold" fn fu() {} @@ -8,7 +8,7 @@ LL | extern "rust-cold" fn fu() {} = help: add `#![feature(rust_cold_cc)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: rust-cold is experimental and subject to change +error[E0658]: the extern "rust-cold" ABI is experimental and subject to change --> $DIR/feature-gate-rust_cold_cc.rs:6:12 | LL | extern "rust-cold" fn mu(); @@ -18,7 +18,7 @@ LL | extern "rust-cold" fn mu(); = help: add `#![feature(rust_cold_cc)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: rust-cold is experimental and subject to change +error[E0658]: the extern "rust-cold" ABI is experimental and subject to change --> $DIR/feature-gate-rust_cold_cc.rs:7:12 | LL | extern "rust-cold" fn dmu() {} @@ -28,7 +28,7 @@ LL | extern "rust-cold" fn dmu() {} = help: add `#![feature(rust_cold_cc)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: rust-cold is experimental and subject to change +error[E0658]: the extern "rust-cold" ABI is experimental and subject to change --> $DIR/feature-gate-rust_cold_cc.rs:12:12 | LL | extern "rust-cold" fn mu() {} @@ -38,7 +38,7 @@ LL | extern "rust-cold" fn mu() {} = help: add `#![feature(rust_cold_cc)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: rust-cold is experimental and subject to change +error[E0658]: the extern "rust-cold" ABI is experimental and subject to change --> $DIR/feature-gate-rust_cold_cc.rs:16:12 | LL | extern "rust-cold" fn imu() {} @@ -48,7 +48,7 @@ LL | extern "rust-cold" fn imu() {} = help: add `#![feature(rust_cold_cc)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: rust-cold is experimental and subject to change +error[E0658]: the extern "rust-cold" ABI is experimental and subject to change --> $DIR/feature-gate-rust_cold_cc.rs:19:19 | LL | type TAU = extern "rust-cold" fn(); @@ -58,7 +58,7 @@ LL | type TAU = extern "rust-cold" fn(); = help: add `#![feature(rust_cold_cc)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: rust-cold is experimental and subject to change +error[E0658]: the extern "rust-cold" ABI is experimental and subject to change --> $DIR/feature-gate-rust_cold_cc.rs:21:8 | LL | extern "rust-cold" {} diff --git a/tests/ui/feature-gates/feature-gate-rustc_encodable_decodable.rs b/tests/ui/feature-gates/feature-gate-rustc_encodable_decodable.rs deleted file mode 100644 index 71caf43806d09..0000000000000 --- a/tests/ui/feature-gates/feature-gate-rustc_encodable_decodable.rs +++ /dev/null @@ -1,16 +0,0 @@ -#![crate_type = "lib"] - -// This isn't intended to compile, so it's easiest to just ignore this error. -extern crate rustc_serialize; //~ERROR can't find crate for `rustc_serialize` - -#[derive( - RustcEncodable, - //~^ ERROR use of unstable library feature `rustc_encodable_decodable` - //~^^ WARNING this was previously accepted by the compiler - //~^^^ WARNING use of deprecated macro `RustcEncodable` - RustcDecodable, - //~^ ERROR use of unstable library feature `rustc_encodable_decodable` - //~^^ WARNING this was previously accepted by the compiler - //~^^^ WARNING use of deprecated macro `RustcDecodable` -)] -struct S; diff --git a/tests/ui/feature-gates/feature-gate-rustc_encodable_decodable.stderr b/tests/ui/feature-gates/feature-gate-rustc_encodable_decodable.stderr deleted file mode 100644 index b949dbb9da21c..0000000000000 --- a/tests/ui/feature-gates/feature-gate-rustc_encodable_decodable.stderr +++ /dev/null @@ -1,66 +0,0 @@ -error[E0463]: can't find crate for `rustc_serialize` - --> $DIR/feature-gate-rustc_encodable_decodable.rs:4:1 - | -LL | extern crate rustc_serialize; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't find crate - | - = help: maybe you need to install the missing components with: `rustup component add rust-src rustc-dev llvm-tools-preview` - -error: use of unstable library feature `rustc_encodable_decodable`: derive macro for `rustc-serialize`; should not be used in new code - --> $DIR/feature-gate-rustc_encodable_decodable.rs:7:5 - | -LL | RustcEncodable, - | ^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #64266 - = note: `#[deny(soft_unstable)]` on by default - -warning: use of deprecated macro `RustcEncodable`: rustc-serialize is deprecated and no longer supported - --> $DIR/feature-gate-rustc_encodable_decodable.rs:7:5 - | -LL | RustcEncodable, - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(deprecated)]` on by default - -error: use of unstable library feature `rustc_encodable_decodable`: derive macro for `rustc-serialize`; should not be used in new code - --> $DIR/feature-gate-rustc_encodable_decodable.rs:11:5 - | -LL | RustcDecodable, - | ^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #64266 - -warning: use of deprecated macro `RustcDecodable`: rustc-serialize is deprecated and no longer supported - --> $DIR/feature-gate-rustc_encodable_decodable.rs:11:5 - | -LL | RustcDecodable, - | ^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors; 2 warnings emitted - -For more information about this error, try `rustc --explain E0463`. -Future incompatibility report: Future breakage diagnostic: -error: use of unstable library feature `rustc_encodable_decodable`: derive macro for `rustc-serialize`; should not be used in new code - --> $DIR/feature-gate-rustc_encodable_decodable.rs:7:5 - | -LL | RustcEncodable, - | ^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #64266 - = note: `#[deny(soft_unstable)]` on by default - -Future breakage diagnostic: -error: use of unstable library feature `rustc_encodable_decodable`: derive macro for `rustc-serialize`; should not be used in new code - --> $DIR/feature-gate-rustc_encodable_decodable.rs:11:5 - | -LL | RustcDecodable, - | ^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #64266 - = note: `#[deny(soft_unstable)]` on by default - diff --git a/tests/ui/feature-gates/feature-gate-start.rs b/tests/ui/feature-gates/feature-gate-start.rs deleted file mode 100644 index e617f1c47594d..0000000000000 --- a/tests/ui/feature-gates/feature-gate-start.rs +++ /dev/null @@ -1,3 +0,0 @@ -#[start] -fn foo(_: isize, _: *const *const u8) -> isize { 0 } -//~^ ERROR `#[start]` functions are experimental diff --git a/tests/ui/feature-gates/feature-gate-start.stderr b/tests/ui/feature-gates/feature-gate-start.stderr deleted file mode 100644 index b1859c43718c8..0000000000000 --- a/tests/ui/feature-gates/feature-gate-start.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0658]: `#[start]` functions are experimental and their signature may change over time - --> $DIR/feature-gate-start.rs:2:1 - | -LL | fn foo(_: isize, _: *const *const u8) -> isize { 0 } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #29633 for more information - = help: add `#![feature(start)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-supertrait-item-shadowing.rs b/tests/ui/feature-gates/feature-gate-supertrait-item-shadowing.rs new file mode 100644 index 0000000000000..218d2eefeaff7 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-supertrait-item-shadowing.rs @@ -0,0 +1,22 @@ +trait Sup { + fn method(&self) {} +} + +trait Trait: Sup { + fn method(&self) {} +} + +impl Sup for i32 {} +impl Trait for i32 {} + +fn poly(x: T) { + x.method(); + //~^ ERROR multiple applicable items in scope +} + +fn concrete() { + 1.method(); + //~^ ERROR multiple applicable items in scope +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-supertrait-item-shadowing.stderr b/tests/ui/feature-gates/feature-gate-supertrait-item-shadowing.stderr new file mode 100644 index 0000000000000..5669073f4abb7 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-supertrait-item-shadowing.stderr @@ -0,0 +1,57 @@ +error[E0034]: multiple applicable items in scope + --> $DIR/feature-gate-supertrait-item-shadowing.rs:13:7 + | +LL | x.method(); + | ^^^^^^ multiple `method` found + | +note: candidate #1 is defined in the trait `Sup` + --> $DIR/feature-gate-supertrait-item-shadowing.rs:2:5 + | +LL | fn method(&self) {} + | ^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in the trait `Trait` + --> $DIR/feature-gate-supertrait-item-shadowing.rs:6:5 + | +LL | fn method(&self) {} + | ^^^^^^^^^^^^^^^^ +help: disambiguate the method for candidate #1 + | +LL - x.method(); +LL + Sup::method(&x); + | +help: disambiguate the method for candidate #2 + | +LL - x.method(); +LL + Trait::method(&x); + | + +error[E0034]: multiple applicable items in scope + --> $DIR/feature-gate-supertrait-item-shadowing.rs:18:7 + | +LL | 1.method(); + | ^^^^^^ multiple `method` found + | +note: candidate #1 is defined in an impl of the trait `Sup` for the type `i32` + --> $DIR/feature-gate-supertrait-item-shadowing.rs:2:5 + | +LL | fn method(&self) {} + | ^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in an impl of the trait `Trait` for the type `i32` + --> $DIR/feature-gate-supertrait-item-shadowing.rs:6:5 + | +LL | fn method(&self) {} + | ^^^^^^^^^^^^^^^^ +help: disambiguate the method for candidate #1 + | +LL - 1.method(); +LL + Sup::method(&1); + | +help: disambiguate the method for candidate #2 + | +LL - 1.method(); +LL + Trait::method(&1); + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0034`. diff --git a/tests/ui/feature-gates/feature-gate-trait_upcasting.rs b/tests/ui/feature-gates/feature-gate-trait_upcasting.rs deleted file mode 100644 index e4102f1cfa75d..0000000000000 --- a/tests/ui/feature-gates/feature-gate-trait_upcasting.rs +++ /dev/null @@ -1,13 +0,0 @@ -trait Foo {} - -trait Bar: Foo {} - -impl Foo for () {} - -impl Bar for () {} - -fn main() { - let bar: &dyn Bar = &(); - let foo: &dyn Foo = bar; - //~^ ERROR trait upcasting coercion is experimental [E0658] -} diff --git a/tests/ui/feature-gates/feature-gate-trait_upcasting.stderr b/tests/ui/feature-gates/feature-gate-trait_upcasting.stderr deleted file mode 100644 index 6fd277ae8cc3e..0000000000000 --- a/tests/ui/feature-gates/feature-gate-trait_upcasting.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0658]: cannot cast `dyn Bar` to `dyn Foo`, trait upcasting coercion is experimental - --> $DIR/feature-gate-trait_upcasting.rs:11:25 - | -LL | let foo: &dyn Foo = bar; - | ^^^ - | - = note: see issue #65991 for more information - = help: add `#![feature(trait_upcasting)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: required when coercing `&dyn Bar` into `&dyn Foo` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs index ff528274c59be..7b40c8760e368 100644 --- a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs +++ b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.rs @@ -11,7 +11,7 @@ impl Fn<()> for Foo { //~| ERROR manual implementations of `Fn` are experimental //~| ERROR expected a `FnMut()` closure, found `Foo` extern "rust-call" fn call(self, args: ()) -> () {} - //~^ ERROR rust-call ABI is subject to change + //~^ ERROR "rust-call" ABI is experimental and subject to change //~| ERROR `call` has an incompatible type for trait } struct Foo1; @@ -20,7 +20,7 @@ impl FnOnce() for Foo1 { //~| ERROR manual implementations of `FnOnce` are experimental //~| ERROR not all trait items implemented extern "rust-call" fn call_once(self, args: ()) -> () {} - //~^ ERROR rust-call ABI is subject to change + //~^ ERROR "rust-call" ABI is experimental and subject to change } struct Bar; impl FnMut<()> for Bar { @@ -28,7 +28,7 @@ impl FnMut<()> for Bar { //~| ERROR manual implementations of `FnMut` are experimental //~| ERROR expected a `FnOnce()` closure, found `Bar` extern "rust-call" fn call_mut(&self, args: ()) -> () {} - //~^ ERROR rust-call ABI is subject to change + //~^ ERROR "rust-call" ABI is experimental and subject to change //~| ERROR incompatible type for trait } struct Baz; @@ -37,7 +37,7 @@ impl FnOnce<()> for Baz { //~| ERROR manual implementations of `FnOnce` are experimental //~| ERROR not all trait items implemented extern "rust-call" fn call_once(&self, args: ()) -> () {} - //~^ ERROR rust-call ABI is subject to change + //~^ ERROR "rust-call" ABI is experimental and subject to change } fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr index 584724dfe59cc..214725b77c049 100644 --- a/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr +++ b/tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr @@ -1,4 +1,4 @@ -error[E0658]: rust-call ABI is subject to change +error[E0658]: the extern "rust-call" ABI is experimental and subject to change --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:13:12 | LL | extern "rust-call" fn call(self, args: ()) -> () {} @@ -8,7 +8,7 @@ LL | extern "rust-call" fn call(self, args: ()) -> () {} = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: rust-call ABI is subject to change +error[E0658]: the extern "rust-call" ABI is experimental and subject to change --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:22:12 | LL | extern "rust-call" fn call_once(self, args: ()) -> () {} @@ -18,7 +18,7 @@ LL | extern "rust-call" fn call_once(self, args: ()) -> () {} = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: rust-call ABI is subject to change +error[E0658]: the extern "rust-call" ABI is experimental and subject to change --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:30:12 | LL | extern "rust-call" fn call_mut(&self, args: ()) -> () {} @@ -28,7 +28,7 @@ LL | extern "rust-call" fn call_mut(&self, args: ()) -> () {} = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: rust-call ABI is subject to change +error[E0658]: the extern "rust-call" ABI is experimental and subject to change --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:39:12 | LL | extern "rust-call" fn call_once(&self, args: ()) -> () {} @@ -96,7 +96,7 @@ LL | extern "rust-call" fn call(self, args: ()) -> () {} help: change the self-receiver type to match the trait | LL | extern "rust-call" fn call(&self, args: ()) -> () {} - | ~~~~~ + | + error[E0183]: manual implementations of `FnOnce` are experimental --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:18:6 @@ -165,8 +165,9 @@ LL | extern "rust-call" fn call_mut(&self, args: ()) -> () {} found signature `extern "rust-call" fn(&Bar, ()) -> ()` help: change the self-receiver type to match the trait | -LL | extern "rust-call" fn call_mut(&mut self, args: ()) -> () {} - | ~~~~~~~~~ +LL - extern "rust-call" fn call_mut(&self, args: ()) -> () {} +LL + extern "rust-call" fn call_mut(&mut self, args: ()) -> () {} + | error[E0046]: not all trait items implemented, missing: `Output` --> $DIR/feature-gate-unboxed-closures-manual-impls.rs:35:1 diff --git a/tests/ui/feature-gates/feature-gate-unboxed-closures.rs b/tests/ui/feature-gates/feature-gate-unboxed-closures.rs index ebc5a2536f672..74cc20c581cb9 100644 --- a/tests/ui/feature-gates/feature-gate-unboxed-closures.rs +++ b/tests/ui/feature-gates/feature-gate-unboxed-closures.rs @@ -10,7 +10,7 @@ impl FnOnce<(u32, u32)> for Test { extern "rust-call" fn call_once(self, (a, b): (u32, u32)) -> u32 { a + b } - //~^^^ ERROR rust-call ABI is subject to change + //~^^^ ERROR "rust-call" ABI is experimental and subject to change } fn main() { diff --git a/tests/ui/feature-gates/feature-gate-unboxed-closures.stderr b/tests/ui/feature-gates/feature-gate-unboxed-closures.stderr index 52c18ec34c564..36932c1f86f9f 100644 --- a/tests/ui/feature-gates/feature-gate-unboxed-closures.stderr +++ b/tests/ui/feature-gates/feature-gate-unboxed-closures.stderr @@ -1,4 +1,4 @@ -error[E0658]: rust-call ABI is subject to change +error[E0658]: the extern "rust-call" ABI is experimental and subject to change --> $DIR/feature-gate-unboxed-closures.rs:10:12 | LL | extern "rust-call" fn call_once(self, (a, b): (u32, u32)) -> u32 { diff --git a/tests/ui/feature-gates/feature-gate-unsized_fn_params.stderr b/tests/ui/feature-gates/feature-gate-unsized_fn_params.stderr index 360c73ae2d7b2..30f9585176802 100644 --- a/tests/ui/feature-gates/feature-gate-unsized_fn_params.stderr +++ b/tests/ui/feature-gates/feature-gate-unsized_fn_params.stderr @@ -8,8 +8,9 @@ LL | fn foo(x: dyn Foo) { = help: unsized fn params are gated as an unstable feature help: you can use `impl Trait` as the argument type | -LL | fn foo(x: impl Foo) { - | ~~~~ +LL - fn foo(x: dyn Foo) { +LL + fn foo(x: impl Foo) { + | help: function arguments must have a statically known size, borrowed types always have a known size | LL | fn foo(x: &dyn Foo) { diff --git a/tests/ui/feature-gates/feature-gate-unsized_locals.stderr b/tests/ui/feature-gates/feature-gate-unsized_locals.stderr index f48565b922bf2..ce0f423fb5c72 100644 --- a/tests/ui/feature-gates/feature-gate-unsized_locals.stderr +++ b/tests/ui/feature-gates/feature-gate-unsized_locals.stderr @@ -8,8 +8,9 @@ LL | fn f(f: dyn FnOnce()) {} = help: unsized fn params are gated as an unstable feature help: you can use `impl Trait` as the argument type | -LL | fn f(f: impl FnOnce()) {} - | ~~~~ +LL - fn f(f: dyn FnOnce()) {} +LL + fn f(f: impl FnOnce()) {} + | help: function arguments must have a statically known size, borrowed types always have a known size | LL | fn f(f: &dyn FnOnce()) {} diff --git a/tests/ui/feature-gates/feature-gate-vectorcall.rs b/tests/ui/feature-gates/feature-gate-vectorcall.rs index 73a11a842f377..aafa6a2ed629b 100644 --- a/tests/ui/feature-gates/feature-gate-vectorcall.rs +++ b/tests/ui/feature-gates/feature-gate-vectorcall.rs @@ -9,23 +9,23 @@ trait Sized { } // Test that the "vectorcall" ABI is feature-gated, and cannot be used when // the `vectorcall` feature gate is not used. -extern "vectorcall" fn f() {} //~ ERROR vectorcall is experimental +extern "vectorcall" fn f() {} //~ ERROR "vectorcall" ABI is experimental trait T { - extern "vectorcall" fn m(); //~ ERROR vectorcall is experimental + extern "vectorcall" fn m(); //~ ERROR "vectorcall" ABI is experimental - extern "vectorcall" fn dm() {} //~ ERROR vectorcall is experimental + extern "vectorcall" fn dm() {} //~ ERROR "vectorcall" ABI is experimental } struct S; impl T for S { - extern "vectorcall" fn m() {} //~ ERROR vectorcall is experimental + extern "vectorcall" fn m() {} //~ ERROR "vectorcall" ABI is experimental } impl S { - extern "vectorcall" fn im() {} //~ ERROR vectorcall is experimental + extern "vectorcall" fn im() {} //~ ERROR "vectorcall" ABI is experimental } -type TA = extern "vectorcall" fn(); //~ ERROR vectorcall is experimental +type TA = extern "vectorcall" fn(); //~ ERROR "vectorcall" ABI is experimental -extern "vectorcall" {} //~ ERROR vectorcall is experimental +extern "vectorcall" {} //~ ERROR "vectorcall" ABI is experimental diff --git a/tests/ui/feature-gates/feature-gate-vectorcall.stderr b/tests/ui/feature-gates/feature-gate-vectorcall.stderr index b20e41887b9bf..8f3f47a3d4888 100644 --- a/tests/ui/feature-gates/feature-gate-vectorcall.stderr +++ b/tests/ui/feature-gates/feature-gate-vectorcall.stderr @@ -1,4 +1,4 @@ -error[E0658]: vectorcall is experimental and subject to change +error[E0658]: the extern "vectorcall" ABI is experimental and subject to change --> $DIR/feature-gate-vectorcall.rs:12:8 | LL | extern "vectorcall" fn f() {} @@ -8,7 +8,7 @@ LL | extern "vectorcall" fn f() {} = help: add `#![feature(abi_vectorcall)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: vectorcall is experimental and subject to change +error[E0658]: the extern "vectorcall" ABI is experimental and subject to change --> $DIR/feature-gate-vectorcall.rs:15:12 | LL | extern "vectorcall" fn m(); @@ -18,7 +18,7 @@ LL | extern "vectorcall" fn m(); = help: add `#![feature(abi_vectorcall)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: vectorcall is experimental and subject to change +error[E0658]: the extern "vectorcall" ABI is experimental and subject to change --> $DIR/feature-gate-vectorcall.rs:17:12 | LL | extern "vectorcall" fn dm() {} @@ -28,7 +28,7 @@ LL | extern "vectorcall" fn dm() {} = help: add `#![feature(abi_vectorcall)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: vectorcall is experimental and subject to change +error[E0658]: the extern "vectorcall" ABI is experimental and subject to change --> $DIR/feature-gate-vectorcall.rs:22:12 | LL | extern "vectorcall" fn m() {} @@ -38,7 +38,7 @@ LL | extern "vectorcall" fn m() {} = help: add `#![feature(abi_vectorcall)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: vectorcall is experimental and subject to change +error[E0658]: the extern "vectorcall" ABI is experimental and subject to change --> $DIR/feature-gate-vectorcall.rs:26:12 | LL | extern "vectorcall" fn im() {} @@ -48,7 +48,7 @@ LL | extern "vectorcall" fn im() {} = help: add `#![feature(abi_vectorcall)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: vectorcall is experimental and subject to change +error[E0658]: the extern "vectorcall" ABI is experimental and subject to change --> $DIR/feature-gate-vectorcall.rs:29:18 | LL | type TA = extern "vectorcall" fn(); @@ -58,7 +58,7 @@ LL | type TA = extern "vectorcall" fn(); = help: add `#![feature(abi_vectorcall)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: vectorcall is experimental and subject to change +error[E0658]: the extern "vectorcall" ABI is experimental and subject to change --> $DIR/feature-gate-vectorcall.rs:31:8 | LL | extern "vectorcall" {} diff --git a/tests/ui/feature-gates/feature-gated-feature-in-macro-arg.rs b/tests/ui/feature-gates/feature-gated-feature-in-macro-arg.rs index 37b7d52fafcae..2328798d74c32 100644 --- a/tests/ui/feature-gates/feature-gated-feature-in-macro-arg.rs +++ b/tests/ui/feature-gates/feature-gated-feature-in-macro-arg.rs @@ -5,7 +5,7 @@ fn main() { let a = &[1, 2, 3]; println!("{}", { - extern "rust-intrinsic" { //~ ERROR intrinsics are subject to change + extern "rust-intrinsic" { //~ ERROR "rust-intrinsic" ABI is an implementation detail fn atomic_fence(); } atomic_fence(); //~ ERROR: is unsafe diff --git a/tests/ui/feature-gates/feature-gated-feature-in-macro-arg.stderr b/tests/ui/feature-gates/feature-gated-feature-in-macro-arg.stderr index 3dc11b5612ca9..86f88fdff5fc8 100644 --- a/tests/ui/feature-gates/feature-gated-feature-in-macro-arg.stderr +++ b/tests/ui/feature-gates/feature-gated-feature-in-macro-arg.stderr @@ -1,4 +1,4 @@ -error[E0658]: intrinsics are subject to change +error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable --> $DIR/feature-gated-feature-in-macro-arg.rs:8:16 | LL | extern "rust-intrinsic" { diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs index afffb3b144375..02a56c7e6aa6c 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs @@ -14,8 +14,6 @@ #![rustc_main] //~ ERROR: the `#[rustc_main]` attribute is used internally to specify //~^ ERROR: `rustc_main` attribute cannot be used at crate level //~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -#![start] -//~^ ERROR: `start` attribute cannot be used at crate level #![repr()] //~^ ERROR: `repr` attribute cannot be used at crate level #![path = "3800"] @@ -38,7 +36,6 @@ mod inline { //~| NOTE the inner attribute doesn't annotate this module //~| NOTE the inner attribute doesn't annotate this module //~| NOTE the inner attribute doesn't annotate this module - //~| NOTE the inner attribute doesn't annotate this module mod inner { #![inline] } //~^ ERROR attribute should be applied to function or closure @@ -123,24 +120,6 @@ mod export_name { } } -#[start] -//~^ ERROR: `start` attribute can only be used on functions -mod start { - mod inner { #![start] } - //~^ ERROR: `start` attribute can only be used on functions - - // for `fn f()` case, see feature-gate-start.rs - - #[start] struct S; - //~^ ERROR: `start` attribute can only be used on functions - - #[start] type T = S; - //~^ ERROR: `start` attribute can only be used on functions - - #[start] impl S { } - //~^ ERROR: `start` attribute can only be used on functions -} - #[repr(C)] //~^ ERROR: attribute should be applied to a struct, enum, or union mod repr { diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr index db8c5295a2daa..648bafe646094 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr @@ -8,7 +8,7 @@ LL | #![rustc_main] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: valid forms for the attribute are `#[inline]` and `#[inline(always|never)]` - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:47:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:44:5 | LL | #[inline = "2100"] fn f() { } | ^^^^^^^^^^^^^^^^^^ @@ -17,38 +17,8 @@ LL | #[inline = "2100"] fn f() { } = note: for more information, see issue #57571 = note: `#[deny(ill_formed_attribute_input)]` on by default -error: `start` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:126:1 - | -LL | #[start] - | ^^^^^^^^ - -error: `start` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:129:17 - | -LL | mod inner { #![start] } - | ^^^^^^^^^ - -error: `start` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:134:5 - | -LL | #[start] struct S; - | ^^^^^^^^ - -error: `start` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:137:5 - | -LL | #[start] type T = S; - | ^^^^^^^^ - -error: `start` attribute can only be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:140:5 - | -LL | #[start] impl S { } - | ^^^^^^^^ - error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:32:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:30:1 | LL | #[inline] | ^^^^^^^^^ @@ -59,7 +29,7 @@ LL | | } | |_- not a function or closure error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:66:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:63:1 | LL | #[no_link] | ^^^^^^^^^^ @@ -73,7 +43,7 @@ LL | | } | |_- not an `extern crate` item error: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:92:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:89:1 | LL | #[export_name = "2200"] | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -87,7 +57,7 @@ LL | | } | |_- not a free function, impl method or static error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:144:8 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:123:8 | LL | #[repr(C)] | ^ @@ -100,7 +70,7 @@ LL | | } | |_- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:168:8 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:147:8 | LL | #[repr(Rust)] | ^^^^ @@ -113,19 +83,19 @@ LL | | } | |_- not a struct, enum, or union error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:26:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:24:1 | LL | #![no_link] | ^^^^^^^^^^^ not an `extern crate` item error: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:28:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:26:1 | LL | #![export_name = "2200"] | ^^^^^^^^^^^^^^^^^^^^^^^^ not a free function, impl method or static error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:30:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:28:1 | LL | #![inline] | ^^^^^^^^^^ not a function or closure @@ -160,23 +130,8 @@ LL - #![rustc_main] LL + #[rustc_main] | -error: `start` attribute cannot be used at crate level - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:17:1 - | -LL | #![start] - | ^^^^^^^^^ -... -LL | mod inline { - | ------ the inner attribute doesn't annotate this module - | -help: perhaps you meant to use an outer attribute - | -LL - #![start] -LL + #[start] - | - error: `repr` attribute cannot be used at crate level - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:19:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:17:1 | LL | #![repr()] | ^^^^^^^^^^ @@ -191,7 +146,7 @@ LL + #[repr()] | error: `path` attribute cannot be used at crate level - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:21:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:19:1 | LL | #![path = "3800"] | ^^^^^^^^^^^^^^^^^ @@ -206,7 +161,7 @@ LL + #[path = "3800"] | error: `automatically_derived` attribute cannot be used at crate level - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:23:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:21:1 | LL | #![automatically_derived] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -221,144 +176,144 @@ LL + #[automatically_derived] | error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:43:17 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:40:17 | LL | mod inner { #![inline] } | ------------^^^^^^^^^^-- not a function or closure error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:53:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:50:5 | LL | #[inline] struct S; | ^^^^^^^^^ --------- not a function or closure error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:57:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:54:5 | LL | #[inline] type T = S; | ^^^^^^^^^ ----------- not a function or closure error[E0518]: attribute should be applied to function or closure - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:61:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:58:5 | LL | #[inline] impl S { } | ^^^^^^^^^ ---------- not a function or closure error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:71:17 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:68:17 | LL | mod inner { #![no_link] } | ------------^^^^^^^^^^^-- not an `extern crate` item error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:75:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:72:5 | LL | #[no_link] fn f() { } | ^^^^^^^^^^ ---------- not an `extern crate` item error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:79:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:76:5 | LL | #[no_link] struct S; | ^^^^^^^^^^ --------- not an `extern crate` item error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:83:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:80:5 | LL | #[no_link]type T = S; | ^^^^^^^^^^----------- not an `extern crate` item error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:87:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:84:5 | LL | #[no_link] impl S { } | ^^^^^^^^^^ ---------- not an `extern crate` item error: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:97:17 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:94:17 | LL | mod inner { #![export_name="2200"] } | ------------^^^^^^^^^^^^^^^^^^^^^^-- not a free function, impl method or static error: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:103:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:100:5 | LL | #[export_name = "2200"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^ --------- not a free function, impl method or static error: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:107:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:104:5 | LL | #[export_name = "2200"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^ ----------- not a free function, impl method or static error: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:111:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:108:5 | LL | #[export_name = "2200"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^ ---------- not a free function, impl method or static error: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:116:9 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:113:9 | LL | #[export_name = "2200"] fn foo(); | ^^^^^^^^^^^^^^^^^^^^^^^ --------- not a free function, impl method or static error: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:120:9 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:117:9 | LL | #[export_name = "2200"] fn bar() {} | ^^^^^^^^^^^^^^^^^^^^^^^ ----------- not a free function, impl method or static error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:148:25 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:127:25 | LL | mod inner { #![repr(C)] } | --------------------^---- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:152:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:131:12 | LL | #[repr(C)] fn f() { } | ^ ---------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:158:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:137:12 | LL | #[repr(C)] type T = S; | ^ ----------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:162:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:141:12 | LL | #[repr(C)] impl S { } | ^ ---------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:172:25 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:151:25 | LL | mod inner { #![repr(Rust)] } | --------------------^^^^---- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:176:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:155:12 | LL | #[repr(Rust)] fn f() { } | ^^^^ ---------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:182:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:161:12 | LL | #[repr(Rust)] type T = S; | ^^^^ ----------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:186:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:165:12 | LL | #[repr(Rust)] impl S { } | ^^^^ ---------- not a struct, enum, or union -error: aborting due to 44 previous errors +error: aborting due to 38 previous errors Some errors have detailed explanations: E0517, E0518, E0658. For more information about an error, try `rustc --explain E0517`. diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs index 141927b4de8cc..a196b8ecdb3ea 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs @@ -81,20 +81,11 @@ #![crate_name = "0900"] #![crate_type = "bin"] // cannot pass "0800" here -#![crate_id = "10"] -//~^ WARN use of deprecated attribute -//~| HELP remove this attribute -//~| NOTE `#[warn(deprecated)]` on by default - // FIXME(#44232) we should warn that this isn't used. #![feature(rust1)] //~^ WARN no longer requires an attribute to enable //~| NOTE `#[warn(stable_features)]` on by default -#![no_start] -//~^ WARN use of deprecated attribute -//~| HELP remove this attribute - // (cannot easily gating state of crate-level #[no_main]; but non crate-level is below at "0400") #![no_builtins] #![recursion_limit = "0200"] diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr index 18fb75aafbb3f..1c6868dc95d9f 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr @@ -1,5 +1,5 @@ warning: `#[macro_escape]` is a deprecated synonym for `#[macro_use]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:400:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:391:17 | LL | mod inner { #![macro_escape] } | ^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | mod inner { #![macro_escape] } = help: try an outer attribute: `#[macro_use]` warning: `#[macro_escape]` is a deprecated synonym for `#[macro_use]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:397:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:388:1 | LL | #[macro_escape] | ^^^^^^^^^^^^^^^ @@ -42,166 +42,152 @@ warning: unknown lint: `x5100` LL | #![deny(x5100)] | ^^^^^ -warning: use of deprecated attribute `crate_id`: no longer used - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:84:1 - | -LL | #![crate_id = "10"] - | ^^^^^^^^^^^^^^^^^^^ help: remove this attribute - | - = note: `#[warn(deprecated)]` on by default - -warning: use of deprecated attribute `no_start`: no longer used - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:94:1 - | -LL | #![no_start] - | ^^^^^^^^^^^^ help: remove this attribute - warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:105:8 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:96:8 | LL | #[warn(x5400)] | ^^^^^ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:108:25 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:99:25 | LL | mod inner { #![warn(x5400)] } | ^^^^^ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:111:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:102:12 | LL | #[warn(x5400)] fn f() { } | ^^^^^ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:114:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:105:12 | LL | #[warn(x5400)] struct S; | ^^^^^ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:117:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:108:12 | LL | #[warn(x5400)] type T = S; | ^^^^^ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:120:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:111:12 | LL | #[warn(x5400)] impl S { } | ^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:124:9 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:115:9 | LL | #[allow(x5300)] | ^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:127:26 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:118:26 | LL | mod inner { #![allow(x5300)] } | ^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:130:13 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:121:13 | LL | #[allow(x5300)] fn f() { } | ^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:133:13 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:124:13 | LL | #[allow(x5300)] struct S; | ^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:136:13 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:127:13 | LL | #[allow(x5300)] type T = S; | ^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:139:13 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:130:13 | LL | #[allow(x5300)] impl S { } | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:143:10 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:134:10 | LL | #[forbid(x5200)] | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:146:27 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:137:27 | LL | mod inner { #![forbid(x5200)] } | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:149:14 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:140:14 | LL | #[forbid(x5200)] fn f() { } | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:152:14 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:143:14 | LL | #[forbid(x5200)] struct S; | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:155:14 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:146:14 | LL | #[forbid(x5200)] type T = S; | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:158:14 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:149:14 | LL | #[forbid(x5200)] impl S { } | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:162:8 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:153:8 | LL | #[deny(x5100)] | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:165:25 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:156:25 | LL | mod inner { #![deny(x5100)] } | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:168:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:159:12 | LL | #[deny(x5100)] fn f() { } | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:171:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:162:12 | LL | #[deny(x5100)] struct S; | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:174:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:165:12 | LL | #[deny(x5100)] type T = S; | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:177:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:168:12 | LL | #[deny(x5100)] impl S { } | ^^^^^ warning: `#[macro_export]` only has an effect on macro definitions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:198:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:189:1 | LL | #[macro_export] | ^^^^^^^^^^^^^^^ @@ -213,13 +199,13 @@ LL | #![warn(unused_attributes, unknown_lints)] | ^^^^^^^^^^^^^^^^^ warning: `#[automatically_derived]` only has an effect on implementation blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:266:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:257:1 | LL | #[automatically_derived] | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:284:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:275:1 | LL | #[no_mangle] | ^^^^^^^^^^^^ @@ -234,31 +220,31 @@ LL | | } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: `#[should_panic]` only has an effect on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:324:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:315:1 | LL | #[should_panic] | ^^^^^^^^^^^^^^^ warning: `#[ignore]` only has an effect on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:342:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:333:1 | LL | #[ignore] | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:377:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:368:1 | LL | #[reexport_test_harness_main = "2900"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:417:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:408:1 | LL | #[no_std] | ^^^^^^^^^ warning: attribute should be applied to a function definition - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:453:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:444:1 | LL | #[cold] | ^^^^^^^ @@ -274,7 +260,7 @@ LL | | } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a foreign function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:482:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:473:1 | LL | #[link_name = "1900"] | ^^^^^^^^^^^^^^^^^^^^^ @@ -290,7 +276,7 @@ LL | | } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:521:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:512:1 | LL | #[link_section = "1800"] | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -306,7 +292,7 @@ LL | | } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:553:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:544:1 | LL | #[link()] | ^^^^^^^^^ @@ -322,55 +308,55 @@ LL | | } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: `#[must_use]` has no effect when applied to a module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:604:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:595:1 | LL | #[must_use] | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:617:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:608:1 | LL | #[windows_subsystem = "windows"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:638:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:629:1 | LL | #[crate_name = "0900"] | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:657:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:648:1 | LL | #[crate_type = "0800"] | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:676:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:667:1 | LL | #[feature(x0600)] | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:696:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:687:1 | LL | #[no_main] | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:715:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:706:1 | LL | #[no_builtins] | ^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:734:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:725:1 | LL | #[recursion_limit="0200"] | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:753:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:744:1 | LL | #[type_length_limit="0100"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -432,109 +418,109 @@ LL | #![must_use] | ^^^^^^^^^^^^ warning: `#[macro_use]` only has an effect on `extern crate` and modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:185:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:176:5 | LL | #[macro_use] fn f() { } | ^^^^^^^^^^^^ warning: `#[macro_use]` only has an effect on `extern crate` and modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:188:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:179:5 | LL | #[macro_use] struct S; | ^^^^^^^^^^^^ warning: `#[macro_use]` only has an effect on `extern crate` and modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:191:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:182:5 | LL | #[macro_use] type T = S; | ^^^^^^^^^^^^ warning: `#[macro_use]` only has an effect on `extern crate` and modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:194:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:185:5 | LL | #[macro_use] impl S { } | ^^^^^^^^^^^^ warning: `#[macro_export]` only has an effect on macro definitions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:201:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:192:17 | LL | mod inner { #![macro_export] } | ^^^^^^^^^^^^^^^^ warning: `#[macro_export]` only has an effect on macro definitions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:204:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:195:5 | LL | #[macro_export] fn f() { } | ^^^^^^^^^^^^^^^ warning: `#[macro_export]` only has an effect on macro definitions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:207:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:198:5 | LL | #[macro_export] struct S; | ^^^^^^^^^^^^^^^ warning: `#[macro_export]` only has an effect on macro definitions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:210:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:201:5 | LL | #[macro_export] type T = S; | ^^^^^^^^^^^^^^^ warning: `#[macro_export]` only has an effect on macro definitions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:213:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:204:5 | LL | #[macro_export] impl S { } | ^^^^^^^^^^^^^^^ warning: `#[path]` only has an effect on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:253:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:244:5 | LL | #[path = "3800"] fn f() { } | ^^^^^^^^^^^^^^^^ warning: `#[path]` only has an effect on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:256:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:247:5 | LL | #[path = "3800"] struct S; | ^^^^^^^^^^^^^^^^ warning: `#[path]` only has an effect on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:259:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:250:5 | LL | #[path = "3800"] type T = S; | ^^^^^^^^^^^^^^^^ warning: `#[path]` only has an effect on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:262:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:253:5 | LL | #[path = "3800"] impl S { } | ^^^^^^^^^^^^^^^^ warning: `#[automatically_derived]` only has an effect on implementation blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:269:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:260:17 | LL | mod inner { #![automatically_derived] } | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: `#[automatically_derived]` only has an effect on implementation blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:272:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:263:5 | LL | #[automatically_derived] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: `#[automatically_derived]` only has an effect on implementation blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:275:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:266:5 | LL | #[automatically_derived] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: `#[automatically_derived]` only has an effect on implementation blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:278:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:269:5 | LL | #[automatically_derived] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:289:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:280:17 | LL | mod inner { #![no_mangle] } | ------------^^^^^^^^^^^^^-- not a free function, impl method or static @@ -542,7 +528,7 @@ LL | mod inner { #![no_mangle] } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:296:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:287:5 | LL | #[no_mangle] struct S; | ^^^^^^^^^^^^ --------- not a free function, impl method or static @@ -550,7 +536,7 @@ LL | #[no_mangle] struct S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:301:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:292:5 | LL | #[no_mangle] type T = S; | ^^^^^^^^^^^^ ----------- not a free function, impl method or static @@ -558,7 +544,7 @@ LL | #[no_mangle] type T = S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:306:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:297:5 | LL | #[no_mangle] impl S { } | ^^^^^^^^^^^^ ---------- not a free function, impl method or static @@ -566,7 +552,7 @@ LL | #[no_mangle] impl S { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:312:9 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:303:9 | LL | #[no_mangle] fn foo(); | ^^^^^^^^^^^^ --------- not a free function, impl method or static @@ -574,7 +560,7 @@ LL | #[no_mangle] fn foo(); = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:317:9 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:308:9 | LL | #[no_mangle] fn bar() {} | ^^^^^^^^^^^^ ----------- not a free function, impl method or static @@ -582,163 +568,163 @@ LL | #[no_mangle] fn bar() {} = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: `#[should_panic]` only has an effect on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:327:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:318:17 | LL | mod inner { #![should_panic] } | ^^^^^^^^^^^^^^^^ warning: `#[should_panic]` only has an effect on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:332:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:323:5 | LL | #[should_panic] struct S; | ^^^^^^^^^^^^^^^ warning: `#[should_panic]` only has an effect on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:335:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:326:5 | LL | #[should_panic] type T = S; | ^^^^^^^^^^^^^^^ warning: `#[should_panic]` only has an effect on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:338:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:329:5 | LL | #[should_panic] impl S { } | ^^^^^^^^^^^^^^^ warning: `#[ignore]` only has an effect on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:345:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:336:17 | LL | mod inner { #![ignore] } | ^^^^^^^^^^ warning: `#[ignore]` only has an effect on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:350:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:341:5 | LL | #[ignore] struct S; | ^^^^^^^^^ warning: `#[ignore]` only has an effect on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:353:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:344:5 | LL | #[ignore] type T = S; | ^^^^^^^^^ warning: `#[ignore]` only has an effect on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:356:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:347:5 | LL | #[ignore] impl S { } | ^^^^^^^^^ warning: `#[no_implicit_prelude]` only has an effect on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:364:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:355:5 | LL | #[no_implicit_prelude] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: `#[no_implicit_prelude]` only has an effect on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:367:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:358:5 | LL | #[no_implicit_prelude] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: `#[no_implicit_prelude]` only has an effect on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:370:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:361:5 | LL | #[no_implicit_prelude] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: `#[no_implicit_prelude]` only has an effect on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:373:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:364:5 | LL | #[no_implicit_prelude] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:380:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:371:17 | LL | mod inner { #![reexport_test_harness_main="2900"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:383:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:374:5 | LL | #[reexport_test_harness_main = "2900"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:386:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:377:5 | LL | #[reexport_test_harness_main = "2900"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:389:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:380:5 | LL | #[reexport_test_harness_main = "2900"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:392:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:383:5 | LL | #[reexport_test_harness_main = "2900"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: `#[macro_escape]` only has an effect on `extern crate` and modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:404:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:395:5 | LL | #[macro_escape] fn f() { } | ^^^^^^^^^^^^^^^ warning: `#[macro_escape]` only has an effect on `extern crate` and modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:407:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:398:5 | LL | #[macro_escape] struct S; | ^^^^^^^^^^^^^^^ warning: `#[macro_escape]` only has an effect on `extern crate` and modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:410:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:401:5 | LL | #[macro_escape] type T = S; | ^^^^^^^^^^^^^^^ warning: `#[macro_escape]` only has an effect on `extern crate` and modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:413:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:404:5 | LL | #[macro_escape] impl S { } | ^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:420:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:411:17 | LL | mod inner { #![no_std] } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:423:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:414:5 | LL | #[no_std] fn f() { } | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:426:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:417:5 | LL | #[no_std] struct S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:429:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:420:5 | LL | #[no_std] type T = S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:432:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:423:5 | LL | #[no_std] impl S { } | ^^^^^^^^^ warning: attribute should be applied to a function definition - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:459:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:450:17 | LL | mod inner { #![cold] } | ------------^^^^^^^^-- not a function definition @@ -746,7 +732,7 @@ LL | mod inner { #![cold] } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function definition - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:466:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:457:5 | LL | #[cold] struct S; | ^^^^^^^ --------- not a function definition @@ -754,7 +740,7 @@ LL | #[cold] struct S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function definition - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:471:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:462:5 | LL | #[cold] type T = S; | ^^^^^^^ ----------- not a function definition @@ -762,7 +748,7 @@ LL | #[cold] type T = S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function definition - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:476:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:467:5 | LL | #[cold] impl S { } | ^^^^^^^ ---------- not a function definition @@ -770,7 +756,7 @@ LL | #[cold] impl S { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a foreign function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:488:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:479:5 | LL | #[link_name = "1900"] | ^^^^^^^^^^^^^^^^^^^^^ @@ -780,13 +766,13 @@ LL | extern "C" { } | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! help: try `#[link(name = "1900")]` instead - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:488:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:479:5 | LL | #[link_name = "1900"] | ^^^^^^^^^^^^^^^^^^^^^ warning: attribute should be applied to a foreign function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:495:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:486:17 | LL | mod inner { #![link_name="1900"] } | ------------^^^^^^^^^^^^^^^^^^^^-- not a foreign function or static @@ -794,7 +780,7 @@ LL | mod inner { #![link_name="1900"] } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a foreign function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:500:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:491:5 | LL | #[link_name = "1900"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^ ---------- not a foreign function or static @@ -802,7 +788,7 @@ LL | #[link_name = "1900"] fn f() { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a foreign function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:505:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:496:5 | LL | #[link_name = "1900"] struct S; | ^^^^^^^^^^^^^^^^^^^^^ --------- not a foreign function or static @@ -810,7 +796,7 @@ LL | #[link_name = "1900"] struct S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a foreign function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:510:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:501:5 | LL | #[link_name = "1900"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^ ----------- not a foreign function or static @@ -818,7 +804,7 @@ LL | #[link_name = "1900"] type T = S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a foreign function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:515:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:506:5 | LL | #[link_name = "1900"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^ ---------- not a foreign function or static @@ -826,7 +812,7 @@ LL | #[link_name = "1900"] impl S { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:527:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:518:17 | LL | mod inner { #![link_section="1800"] } | ------------^^^^^^^^^^^^^^^^^^^^^^^-- not a function or static @@ -834,7 +820,7 @@ LL | mod inner { #![link_section="1800"] } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:534:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:525:5 | LL | #[link_section = "1800"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^ --------- not a function or static @@ -842,7 +828,7 @@ LL | #[link_section = "1800"] struct S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:539:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:530:5 | LL | #[link_section = "1800"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^ ----------- not a function or static @@ -850,7 +836,7 @@ LL | #[link_section = "1800"] type T = S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:544:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:535:5 | LL | #[link_section = "1800"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^ ---------- not a function or static @@ -858,7 +844,7 @@ LL | #[link_section = "1800"] impl S { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:559:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:550:17 | LL | mod inner { #![link()] } | ------------^^^^^^^^^^-- not an `extern` block @@ -866,7 +852,7 @@ LL | mod inner { #![link()] } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:564:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:555:5 | LL | #[link()] fn f() { } | ^^^^^^^^^ ---------- not an `extern` block @@ -874,7 +860,7 @@ LL | #[link()] fn f() { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:569:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:560:5 | LL | #[link()] struct S; | ^^^^^^^^^ --------- not an `extern` block @@ -882,7 +868,7 @@ LL | #[link()] struct S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:574:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:565:5 | LL | #[link()] type T = S; | ^^^^^^^^^ ----------- not an `extern` block @@ -890,7 +876,7 @@ LL | #[link()] type T = S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:579:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:570:5 | LL | #[link()] impl S { } | ^^^^^^^^^ ---------- not an `extern` block @@ -898,7 +884,7 @@ LL | #[link()] impl S { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:584:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:575:5 | LL | #[link()] extern "Rust" {} | ^^^^^^^^^ @@ -906,270 +892,270 @@ LL | #[link()] extern "Rust" {} = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: `#[must_use]` has no effect when applied to a module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:606:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:597:17 | LL | mod inner { #![must_use] } | ^^^^^^^^^^^^ warning: `#[must_use]` has no effect when applied to a type alias - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:612:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:603:5 | LL | #[must_use] type T = S; | ^^^^^^^^^^^ warning: `#[must_use]` has no effect when applied to an implementation block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:614:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:605:5 | LL | #[must_use] impl S { } | ^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:620:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:611:17 | LL | mod inner { #![windows_subsystem="windows"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:623:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:614:5 | LL | #[windows_subsystem = "windows"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:626:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:617:5 | LL | #[windows_subsystem = "windows"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:629:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:620:5 | LL | #[windows_subsystem = "windows"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:632:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:623:5 | LL | #[windows_subsystem = "windows"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:641:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:632:17 | LL | mod inner { #![crate_name="0900"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:644:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:635:5 | LL | #[crate_name = "0900"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:647:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:638:5 | LL | #[crate_name = "0900"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:650:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:641:5 | LL | #[crate_name = "0900"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:653:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:644:5 | LL | #[crate_name = "0900"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:660:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:651:17 | LL | mod inner { #![crate_type="0800"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:663:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:654:5 | LL | #[crate_type = "0800"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:666:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:657:5 | LL | #[crate_type = "0800"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:669:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:660:5 | LL | #[crate_type = "0800"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:672:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:663:5 | LL | #[crate_type = "0800"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:679:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:670:17 | LL | mod inner { #![feature(x0600)] } | ^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:682:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:673:5 | LL | #[feature(x0600)] fn f() { } | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:685:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:676:5 | LL | #[feature(x0600)] struct S; | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:688:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:679:5 | LL | #[feature(x0600)] type T = S; | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:691:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:682:5 | LL | #[feature(x0600)] impl S { } | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:699:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:690:17 | LL | mod inner { #![no_main] } | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:702:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:693:5 | LL | #[no_main] fn f() { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:705:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:696:5 | LL | #[no_main] struct S; | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:708:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:699:5 | LL | #[no_main] type T = S; | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:711:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:702:5 | LL | #[no_main] impl S { } | ^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:718:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:709:17 | LL | mod inner { #![no_builtins] } | ^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:721:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:712:5 | LL | #[no_builtins] fn f() { } | ^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:724:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:715:5 | LL | #[no_builtins] struct S; | ^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:727:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:718:5 | LL | #[no_builtins] type T = S; | ^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:730:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:721:5 | LL | #[no_builtins] impl S { } | ^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:737:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:728:17 | LL | mod inner { #![recursion_limit="0200"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:740:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:731:5 | LL | #[recursion_limit="0200"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:743:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:734:5 | LL | #[recursion_limit="0200"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:746:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:737:5 | LL | #[recursion_limit="0200"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:749:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:740:5 | LL | #[recursion_limit="0200"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:756:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:747:17 | LL | mod inner { #![type_length_limit="0100"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:759:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:750:5 | LL | #[type_length_limit="0100"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:762:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:753:5 | LL | #[type_length_limit="0100"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:765:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:756:5 | LL | #[type_length_limit="0100"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:768:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:759:5 | LL | #[type_length_limit="0100"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: the feature `rust1` has been stable since 1.0.0 and no longer requires an attribute to enable - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:90:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:85:12 | LL | #![feature(rust1)] | ^^^^^ | = note: `#[warn(stable_features)]` on by default -warning: 173 warnings emitted +warning: 171 warnings emitted diff --git a/tests/ui/feature-gates/issue-43106-gating-of-macro_use.stderr b/tests/ui/feature-gates/issue-43106-gating-of-macro_use.stderr index 9a12851f20e5f..8987b87f84e9e 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-macro_use.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-macro_use.stderr @@ -24,10 +24,12 @@ LL | #[macro_use = "2700"] struct S; | help: the following are the possible correct uses | -LL | #[macro_use(name1, name2, ...)] struct S; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | #[macro_use] struct S; - | ~~~~~~~~~~~~ +LL - #[macro_use = "2700"] struct S; +LL + #[macro_use(name1, name2, ...)] struct S; + | +LL - #[macro_use = "2700"] struct S; +LL + #[macro_use] struct S; + | error: aborting due to 4 previous errors diff --git a/tests/ui/fmt/format-args-non-identifier-diagnostics.stderr b/tests/ui/fmt/format-args-non-identifier-diagnostics.stderr index 08abba2854ecc..af6bb58071fff 100644 --- a/tests/ui/fmt/format-args-non-identifier-diagnostics.stderr +++ b/tests/ui/fmt/format-args-non-identifier-diagnostics.stderr @@ -6,8 +6,9 @@ LL | println!("{x.0}"); | help: consider using a positional formatting argument instead | -LL | println!("{0}", x.0); - | ~ +++++ +LL - println!("{x.0}"); +LL + println!("{0}", x.0); + | error: aborting due to 1 previous error diff --git a/tests/ui/fmt/format-string-error-2.stderr b/tests/ui/fmt/format-string-error-2.stderr index a2d142e0baba8..6d8c34fdb7097 100644 --- a/tests/ui/fmt/format-string-error-2.stderr +++ b/tests/ui/fmt/format-string-error-2.stderr @@ -6,8 +6,9 @@ LL | println!("\x7B}\u8 {", 1); | help: format of unicode escape sequences uses braces | -LL | println!("\x7B}\u{8} {", 1); - | ~~~~~ +LL - println!("\x7B}\u8 {", 1); +LL + println!("\x7B}\u{8} {", 1); + | error: invalid format string: expected `}`, found `a` --> $DIR/format-string-error-2.rs:5:5 diff --git a/tests/ui/fmt/no-inline-literals-out-of-range.stderr b/tests/ui/fmt/no-inline-literals-out-of-range.stderr index 78ec4888c27a3..e17023887046f 100644 --- a/tests/ui/fmt/no-inline-literals-out-of-range.stderr +++ b/tests/ui/fmt/no-inline-literals-out-of-range.stderr @@ -8,12 +8,14 @@ LL | format_args!("{}", 0x8f_i8); // issue #115423 = note: `#[deny(overflowing_literals)]` on by default help: consider using the type `u8` instead | -LL | format_args!("{}", 0x8f_u8); // issue #115423 - | ~~~~~~~ +LL - format_args!("{}", 0x8f_i8); // issue #115423 +LL + format_args!("{}", 0x8f_u8); // issue #115423 + | help: to use as a negative number (decimal `-113`), consider using the type `u8` for the literal and cast it to `i8` | -LL | format_args!("{}", 0x8f_u8 as i8); // issue #115423 - | ~~~~~~~~~~~~~ +LL - format_args!("{}", 0x8f_i8); // issue #115423 +LL + format_args!("{}", 0x8f_u8 as i8); // issue #115423 + | error: literal out of range for `u8` --> $DIR/no-inline-literals-out-of-range.rs:6:24 @@ -50,7 +52,7 @@ LL | format_args!("{}", 0xffff_ffff); // treat unsuffixed literals as i32 help: to use as a negative number (decimal `-1`), consider using the type `u32` for the literal and cast it to `i32` | LL | format_args!("{}", 0xffff_ffffu32 as i32); // treat unsuffixed literals as i32 - | ~~~~~~~~~~~~~~~~~~~~~ + | ++++++++++ error: aborting due to 5 previous errors diff --git a/tests/ui/fmt/struct-field-as-captured-argument.stderr b/tests/ui/fmt/struct-field-as-captured-argument.stderr index 4ef022cecb042..388c14f932bbc 100644 --- a/tests/ui/fmt/struct-field-as-captured-argument.stderr +++ b/tests/ui/fmt/struct-field-as-captured-argument.stderr @@ -6,8 +6,9 @@ LL | let _ = format!("{foo.field}"); | help: consider using a positional formatting argument instead | -LL | let _ = format!("{0}", foo.field); - | ~ +++++++++++ +LL - let _ = format!("{foo.field}"); +LL + let _ = format!("{0}", foo.field); + | error: invalid format string: field access isn't supported --> $DIR/struct-field-as-captured-argument.rs:12:23 @@ -17,8 +18,9 @@ LL | let _ = format!("{foo.field} {} {bar}", "aa"); | help: consider using a positional formatting argument instead | -LL | let _ = format!("{1} {} {bar}", "aa", foo.field); - | ~ +++++++++++ +LL - let _ = format!("{foo.field} {} {bar}", "aa"); +LL + let _ = format!("{1} {} {bar}", "aa", foo.field); + | error: invalid format string: field access isn't supported --> $DIR/struct-field-as-captured-argument.rs:13:23 @@ -28,8 +30,9 @@ LL | let _ = format!("{foo.field} {} {1} {bar}", "aa", "bb"); | help: consider using a positional formatting argument instead | -LL | let _ = format!("{2} {} {1} {bar}", "aa", "bb", foo.field); - | ~ +++++++++++ +LL - let _ = format!("{foo.field} {} {1} {bar}", "aa", "bb"); +LL + let _ = format!("{2} {} {1} {bar}", "aa", "bb", foo.field); + | error: invalid format string: field access isn't supported --> $DIR/struct-field-as-captured-argument.rs:14:23 @@ -39,8 +42,9 @@ LL | let _ = format!("{foo.field} {} {baz}", "aa", baz = 3); | help: consider using a positional formatting argument instead | -LL | let _ = format!("{1} {} {baz}", "aa", foo.field, baz = 3); - | ~ +++++++++++ +LL - let _ = format!("{foo.field} {} {baz}", "aa", baz = 3); +LL + let _ = format!("{1} {} {baz}", "aa", foo.field, baz = 3); + | error: invalid format string: field access isn't supported --> $DIR/struct-field-as-captured-argument.rs:15:23 @@ -50,8 +54,9 @@ LL | let _ = format!("{foo.field:?} {} {baz}", "aa", baz = 3); | help: consider using a positional formatting argument instead | -LL | let _ = format!("{1:?} {} {baz}", "aa", foo.field, baz = 3); - | ~ +++++++++++ +LL - let _ = format!("{foo.field:?} {} {baz}", "aa", baz = 3); +LL + let _ = format!("{1:?} {} {baz}", "aa", foo.field, baz = 3); + | error: invalid format string: field access isn't supported --> $DIR/struct-field-as-captured-argument.rs:16:23 @@ -61,8 +66,9 @@ LL | let _ = format!("{foo.field:#?} {} {baz}", "aa", baz = 3); | help: consider using a positional formatting argument instead | -LL | let _ = format!("{1:#?} {} {baz}", "aa", foo.field, baz = 3); - | ~ +++++++++++ +LL - let _ = format!("{foo.field:#?} {} {baz}", "aa", baz = 3); +LL + let _ = format!("{1:#?} {} {baz}", "aa", foo.field, baz = 3); + | error: invalid format string: field access isn't supported --> $DIR/struct-field-as-captured-argument.rs:17:23 @@ -72,8 +78,9 @@ LL | let _ = format!("{foo.field:.3} {} {baz}", "aa", baz = 3); | help: consider using a positional formatting argument instead | -LL | let _ = format!("{1:.3} {} {baz}", "aa", foo.field, baz = 3); - | ~ +++++++++++ +LL - let _ = format!("{foo.field:.3} {} {baz}", "aa", baz = 3); +LL + let _ = format!("{1:.3} {} {baz}", "aa", foo.field, baz = 3); + | error: aborting due to 7 previous errors diff --git a/tests/ui/fmt/suggest-wrongly-order-format-parameter.stderr b/tests/ui/fmt/suggest-wrongly-order-format-parameter.stderr index fe693d2e90410..516fb870a5661 100644 --- a/tests/ui/fmt/suggest-wrongly-order-format-parameter.stderr +++ b/tests/ui/fmt/suggest-wrongly-order-format-parameter.stderr @@ -6,8 +6,9 @@ LL | println!("{f:?#}"); | help: did you mean `#?`? | -LL | println!("{f:#?}"); - | ~~ +LL - println!("{f:?#}"); +LL + println!("{f:#?}"); + | error: invalid format string: expected `}`, found `x` --> $DIR/suggest-wrongly-order-format-parameter.rs:18:19 @@ -17,8 +18,9 @@ LL | println!("{f:?x}"); | help: did you mean `x?`? | -LL | println!("{f:x?}"); - | ~~ +LL - println!("{f:?x}"); +LL + println!("{f:x?}"); + | error: invalid format string: expected `}`, found `X` --> $DIR/suggest-wrongly-order-format-parameter.rs:22:19 @@ -28,8 +30,9 @@ LL | println!("{f:?X}"); | help: did you mean `X?`? | -LL | println!("{f:X?}"); - | ~~ +LL - println!("{f:?X}"); +LL + println!("{f:X?}"); + | error: aborting due to 3 previous errors diff --git a/tests/ui/fn/error-recovery-mismatch.rs b/tests/ui/fn/error-recovery-mismatch.rs new file mode 100644 index 0000000000000..a50a30c8c78a6 --- /dev/null +++ b/tests/ui/fn/error-recovery-mismatch.rs @@ -0,0 +1,20 @@ +// Used to ICE due to a size mismatch between the actual fake signature of `fold` and the +// generated signature used reporting the parameter mismatch at the call site. +// See issue #135124 + +trait A { + fn y(&self) + { + fn call() -> impl Sized {} + self.fold(call(), call()); + } + fn fold(&self, _: T, &self._) {} + //~^ ERROR unexpected `self` parameter in function + //~| ERROR expected one of `)` or `,`, found `.` + //~| ERROR identifier `self` is bound more than once in this parameter list + //~| WARNING anonymous parameters are deprecated + //~| WARNING this is accepted in the current edition + //~| ERROR the placeholder `_` is not allowed within types +} + +fn main() {} diff --git a/tests/ui/fn/error-recovery-mismatch.stderr b/tests/ui/fn/error-recovery-mismatch.stderr new file mode 100644 index 0000000000000..f281e77f13b9b --- /dev/null +++ b/tests/ui/fn/error-recovery-mismatch.stderr @@ -0,0 +1,47 @@ +error: unexpected `self` parameter in function + --> $DIR/error-recovery-mismatch.rs:11:29 + | +LL | fn fold(&self, _: T, &self._) {} + | ^^^^^ must be the first parameter of an associated function + +error: expected one of `)` or `,`, found `.` + --> $DIR/error-recovery-mismatch.rs:11:34 + | +LL | fn fold(&self, _: T, &self._) {} + | ^ + | | + | expected one of `)` or `,` + | help: missing `,` + +error[E0415]: identifier `self` is bound more than once in this parameter list + --> $DIR/error-recovery-mismatch.rs:11:30 + | +LL | fn fold(&self, _: T, &self._) {} + | ^^^^ used as parameter more than once + +warning: anonymous parameters are deprecated and will be removed in the next edition + --> $DIR/error-recovery-mismatch.rs:11:35 + | +LL | fn fold(&self, _: T, &self._) {} + | ^ help: try naming the parameter or explicitly ignoring it: `_: _` + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! + = note: for more information, see issue #41686 + = note: `#[warn(anonymous_parameters)]` on by default + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions + --> $DIR/error-recovery-mismatch.rs:11:35 + | +LL | fn fold(&self, _: T, &self._) {} + | ^ not allowed in type signatures + | +help: use type parameters instead + | +LL - fn fold(&self, _: T, &self._) {} +LL + fn fold(&self, _: T, &self.U) {} + | + +error: aborting due to 4 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0121, E0415. +For more information about an error, try `rustc --explain E0121`. diff --git a/tests/ui/fn/expr-fn-panic.rs b/tests/ui/fn/expr-fn-panic.rs index 23946b7533d66..d726aac2a4c07 100644 --- a/tests/ui/fn/expr-fn-panic.rs +++ b/tests/ui/fn/expr-fn-panic.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:explicit panic -//@ ignore-emscripten no processes +//@ needs-subprocess fn f() -> ! { panic!() diff --git a/tests/ui/fn/fn-pointer-mismatch.stderr b/tests/ui/fn/fn-pointer-mismatch.stderr index 9cda11639d0f5..051eb94546795 100644 --- a/tests/ui/fn/fn-pointer-mismatch.stderr +++ b/tests/ui/fn/fn-pointer-mismatch.stderr @@ -51,8 +51,9 @@ LL | let c: fn(u32) -> u32 = &foo; found reference `&fn(_) -> _ {foo}` help: consider removing the reference | -LL | let c: fn(u32) -> u32 = foo; - | ~~~ +LL - let c: fn(u32) -> u32 = &foo; +LL + let c: fn(u32) -> u32 = foo; + | error[E0308]: mismatched types --> $DIR/fn-pointer-mismatch.rs:42:30 @@ -67,7 +68,7 @@ LL | let d: &fn(u32) -> u32 = foo; help: consider using a reference | LL | let d: &fn(u32) -> u32 = &foo; - | ~~~~ + | + error[E0308]: mismatched types --> $DIR/fn-pointer-mismatch.rs:48:30 @@ -82,8 +83,9 @@ LL | let e: &fn(u32) -> u32 = &foo; = note: fn items are distinct from fn pointers help: consider casting to a fn pointer | -LL | let e: &fn(u32) -> u32 = &(foo as fn(u32) -> u32); - | ~~~~~~~~~~~~~~~~~~~~~~~~ +LL - let e: &fn(u32) -> u32 = &foo; +LL + let e: &fn(u32) -> u32 = &(foo as fn(u32) -> u32); + | error: aborting due to 6 previous errors diff --git a/tests/ui/fn/fn-recover-return-sign.stderr b/tests/ui/fn/fn-recover-return-sign.stderr index e6012f3f95024..96aa96f00adbf 100644 --- a/tests/ui/fn/fn-recover-return-sign.stderr +++ b/tests/ui/fn/fn-recover-return-sign.stderr @@ -6,8 +6,9 @@ LL | fn a() => usize { 0 } | help: use `->` instead | -LL | fn a() -> usize { 0 } - | ~~ +LL - fn a() => usize { 0 } +LL + fn a() -> usize { 0 } + | error: return types are denoted using `->` --> $DIR/fn-recover-return-sign.rs:6:7 @@ -17,8 +18,9 @@ LL | fn b(): usize { 0 } | help: use `->` instead | -LL | fn b() -> usize { 0 } - | ~~ +LL - fn b(): usize { 0 } +LL + fn b() -> usize { 0 } + | error: return types are denoted using `->` --> $DIR/fn-recover-return-sign.rs:21:25 @@ -28,8 +30,9 @@ LL | let foo = |a: bool| => bool { a }; | help: use `->` instead | -LL | let foo = |a: bool| -> bool { a }; - | ~~ +LL - let foo = |a: bool| => bool { a }; +LL + let foo = |a: bool| -> bool { a }; + | error: return types are denoted using `->` --> $DIR/fn-recover-return-sign.rs:25:24 @@ -39,8 +42,9 @@ LL | let bar = |a: bool|: bool { a }; | help: use `->` instead | -LL | let bar = |a: bool| -> bool { a }; - | ~~ +LL - let bar = |a: bool|: bool { a }; +LL + let bar = |a: bool| -> bool { a }; + | error: aborting due to 4 previous errors diff --git a/tests/ui/fn/fn-recover-return-sign2.stderr b/tests/ui/fn/fn-recover-return-sign2.stderr index fb88ff7b95045..ba6662b0d46d8 100644 --- a/tests/ui/fn/fn-recover-return-sign2.stderr +++ b/tests/ui/fn/fn-recover-return-sign2.stderr @@ -6,8 +6,9 @@ LL | fn foo() => impl Fn() => bool { | help: use `->` instead | -LL | fn foo() -> impl Fn() => bool { - | ~~ +LL - fn foo() => impl Fn() => bool { +LL + fn foo() -> impl Fn() => bool { + | error: expected one of `+`, `->`, `::`, `where`, or `{`, found `=>` --> $DIR/fn-recover-return-sign2.rs:4:23 diff --git a/tests/ui/fn/fn_def_coercion.rs b/tests/ui/fn/fn_def_coercion.rs index 313be6f28cdcc..31c8fa41de17c 100644 --- a/tests/ui/fn/fn_def_coercion.rs +++ b/tests/ui/fn/fn_def_coercion.rs @@ -46,12 +46,12 @@ fn j<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { fn k<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { let x = match true { - true => foo::<&'c ()>, + true => foo::<&'c ()>, //~ ERROR lifetime may not live long enough false => foo::<&'a ()>, //~ ERROR lifetime may not live long enough }; x(a); - x(b); //~ ERROR lifetime may not live long enough + x(b); x(c); } diff --git a/tests/ui/fn/fn_def_coercion.stderr b/tests/ui/fn/fn_def_coercion.stderr index ec4a1bde7fd61..c2776887b79d4 100644 --- a/tests/ui/fn/fn_def_coercion.stderr +++ b/tests/ui/fn/fn_def_coercion.stderr @@ -6,9 +6,9 @@ LL | fn f<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { | | | lifetime `'a` defined here LL | let mut x = foo::<&'a ()>; - | ^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'b` + | ^^^^^^^^^^^^^ assignment requires that `'b` must outlive `'a` | - = help: consider adding the following bound: `'a: 'b` + = help: consider adding the following bound: `'b: 'a` = note: requirement occurs because of a function pointer to `foo` = note: the function `foo` is invariant over the parameter `T` = help: see for more information about variance @@ -22,9 +22,9 @@ LL | fn f<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { | lifetime `'a` defined here LL | let mut x = foo::<&'a ()>; LL | x = foo::<&'b ()>; - | ^^^^^^^^^^^^^^^^^ assignment requires that `'b` must outlive `'a` + | ^^^^^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'b` | - = help: consider adding the following bound: `'b: 'a` + = help: consider adding the following bound: `'a: 'b` = note: requirement occurs because of a function pointer to `foo` = note: the function `foo` is invariant over the parameter `T` = help: see for more information about variance @@ -53,9 +53,9 @@ LL | fn i<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { | lifetime `'a` defined here LL | let mut x = foo::<&'c ()>; LL | x = foo::<&'b ()>; - | ^^^^^^^^^^^^^^^^^ assignment requires that `'b` must outlive `'a` + | ^^^^^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'b` | - = help: consider adding the following bound: `'b: 'a` + = help: consider adding the following bound: `'a: 'b` = note: requirement occurs because of a function pointer to `foo` = note: the function `foo` is invariant over the parameter `T` = help: see for more information about variance @@ -69,9 +69,9 @@ LL | fn i<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { | lifetime `'a` defined here ... LL | x = foo::<&'a ()>; - | ^^^^^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'b` + | ^^^^^^^^^^^^^^^^^ assignment requires that `'b` must outlive `'a` | - = help: consider adding the following bound: `'a: 'b` + = help: consider adding the following bound: `'b: 'a` = note: requirement occurs because of a function pointer to `foo` = note: the function `foo` is invariant over the parameter `T` = help: see for more information about variance @@ -89,9 +89,9 @@ LL | fn j<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { | lifetime `'a` defined here LL | let x = match true { LL | true => foo::<&'b ()>, - | ^^^^^^^^^^^^^ assignment requires that `'b` must outlive `'a` + | ^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'b` | - = help: consider adding the following bound: `'b: 'a` + = help: consider adding the following bound: `'a: 'b` = note: requirement occurs because of a function pointer to `foo` = note: the function `foo` is invariant over the parameter `T` = help: see for more information about variance @@ -105,9 +105,9 @@ LL | fn j<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { | lifetime `'a` defined here ... LL | false => foo::<&'a ()>, - | ^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'b` + | ^^^^^^^^^^^^^ assignment requires that `'b` must outlive `'a` | - = help: consider adding the following bound: `'a: 'b` + = help: consider adding the following bound: `'b: 'a` = note: requirement occurs because of a function pointer to `foo` = note: the function `foo` is invariant over the parameter `T` = help: see for more information about variance @@ -117,15 +117,15 @@ help: `'a` and `'b` must be the same: replace one with the other = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: lifetime may not live long enough - --> $DIR/fn_def_coercion.rs:50:18 + --> $DIR/fn_def_coercion.rs:49:17 | LL | fn k<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { | -- -- lifetime `'c` defined here | | | lifetime `'a` defined here -... -LL | false => foo::<&'a ()>, - | ^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'c` +LL | let x = match true { +LL | true => foo::<&'c ()>, + | ^^^^^^^^^^^^^ assignment requires that `'a` must outlive `'c` | = help: consider adding the following bound: `'a: 'c` = note: requirement occurs because of a function pointer to `foo` @@ -133,17 +133,20 @@ LL | false => foo::<&'a ()>, = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/fn_def_coercion.rs:54:5 + --> $DIR/fn_def_coercion.rs:50:18 | LL | fn k<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here ... -LL | x(b); - | ^^^^ argument requires that `'b` must outlive `'a` +LL | false => foo::<&'a ()>, + | ^^^^^^^^^^^^^ assignment requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of a function pointer to `foo` + = note: the function `foo` is invariant over the parameter `T` + = help: see for more information about variance help: the following changes may resolve your lifetime errors | diff --git a/tests/ui/fn/implied-bounds-unnorm-associated-type-2.stderr b/tests/ui/fn/implied-bounds-unnorm-associated-type-2.stderr index e57b5af82cded..85e3452fbf2ca 100644 --- a/tests/ui/fn/implied-bounds-unnorm-associated-type-2.stderr +++ b/tests/ui/fn/implied-bounds-unnorm-associated-type-2.stderr @@ -6,7 +6,7 @@ LL | fn g<'a, 'b>() { | | | lifetime `'a` defined here LL | f::<'a, 'b>(()); - | ^^^^^^^^^^^^^^^ requires that `'b` must outlive `'a` + | ^^^^^^^^^^^^^^^ generic argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` = note: requirement occurs because of a function pointer to `f` diff --git a/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.fixed b/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.fixed new file mode 100644 index 0000000000000..b58c3a6720d00 --- /dev/null +++ b/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.fixed @@ -0,0 +1,32 @@ +//@ run-rustfix +#![deny(unused_assignments, unused_variables)] +#![allow(unused_mut)] +struct Object; + +fn change_object(object: &mut Object) { //~ HELP you might have meant to mutate + let object2 = Object; + *object = object2; //~ ERROR mismatched types +} + +fn change_object2(object: &mut Object) { //~ ERROR variable `object` is assigned to, but never used + //~^ HELP you might have meant to mutate + let object2 = Object; + *object = object2; + //~^ ERROR `object2` does not live long enough + //~| ERROR value assigned to `object` is never read +} + +fn change_object3(object: &mut Object) { //~ ERROR variable `object` is assigned to, but never used + //~^ HELP you might have meant to mutate + let mut object2 = Object; //~ HELP consider changing this to be mutable + *object = object2; + //~^ ERROR cannot borrow `object2` as mutable + //~| ERROR value assigned to `object` is never read +} + +fn main() { + let mut object = Object; + change_object(&mut object); + change_object2(&mut object); + change_object3(&mut object); +} diff --git a/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs b/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs new file mode 100644 index 0000000000000..1fd222e0db17f --- /dev/null +++ b/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs @@ -0,0 +1,32 @@ +//@ run-rustfix +#![deny(unused_assignments, unused_variables)] +#![allow(unused_mut)] +struct Object; + +fn change_object(mut object: &Object) { //~ HELP you might have meant to mutate + let object2 = Object; + object = object2; //~ ERROR mismatched types +} + +fn change_object2(mut object: &Object) { //~ ERROR variable `object` is assigned to, but never used + //~^ HELP you might have meant to mutate + let object2 = Object; + object = &object2; + //~^ ERROR `object2` does not live long enough + //~| ERROR value assigned to `object` is never read +} + +fn change_object3(mut object: &mut Object) { //~ ERROR variable `object` is assigned to, but never used + //~^ HELP you might have meant to mutate + let object2 = Object; //~ HELP consider changing this to be mutable + object = &mut object2; + //~^ ERROR cannot borrow `object2` as mutable + //~| ERROR value assigned to `object` is never read +} + +fn main() { + let mut object = Object; + change_object(&mut object); + change_object2(&mut object); + change_object3(&mut object); +} diff --git a/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.stderr b/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.stderr new file mode 100644 index 0000000000000..0330853d922fe --- /dev/null +++ b/tests/ui/fn/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.stderr @@ -0,0 +1,102 @@ +error[E0308]: mismatched types + --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:8:13 + | +LL | fn change_object(mut object: &Object) { + | ------- expected due to this parameter type +LL | let object2 = Object; +LL | object = object2; + | ^^^^^^^ expected `&Object`, found `Object` + | +help: you might have meant to mutate the pointed at value being passed in, instead of changing the reference in the local binding + | +LL ~ fn change_object(object: &mut Object) { +LL | let object2 = Object; +LL ~ *object = object2; + | + +error: value assigned to `object` is never read + --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:14:4 + | +LL | object = &object2; + | ^^^^^^ + | +note: the lint level is defined here + --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:2:9 + | +LL | #![deny(unused_assignments, unused_variables)] + | ^^^^^^^^^^^^^^^^^^ +help: you might have meant to mutate the pointed at value being passed in, instead of changing the reference in the local binding + | +LL ~ fn change_object2(object: &mut Object) { +LL | +LL | let object2 = Object; +LL ~ *object = object2; + | + +error: variable `object` is assigned to, but never used + --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:11:23 + | +LL | fn change_object2(mut object: &Object) { + | ^^^^^^ + | + = note: consider using `_object` instead +note: the lint level is defined here + --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:2:29 + | +LL | #![deny(unused_assignments, unused_variables)] + | ^^^^^^^^^^^^^^^^ + +error[E0597]: `object2` does not live long enough + --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:14:13 + | +LL | fn change_object2(mut object: &Object) { + | - let's call the lifetime of this reference `'1` +LL | +LL | let object2 = Object; + | ------- binding `object2` declared here +LL | object = &object2; + | ---------^^^^^^^^ + | | | + | | borrowed value does not live long enough + | assignment requires that `object2` is borrowed for `'1` +... +LL | } + | - `object2` dropped here while still borrowed + +error: value assigned to `object` is never read + --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:22:5 + | +LL | object = &mut object2; + | ^^^^^^ + | +help: you might have meant to mutate the pointed at value being passed in, instead of changing the reference in the local binding + | +LL ~ fn change_object3(object: &mut Object) { +LL | +LL | let object2 = Object; +LL ~ *object = object2; + | + +error: variable `object` is assigned to, but never used + --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:19:23 + | +LL | fn change_object3(mut object: &mut Object) { + | ^^^^^^ + | + = note: consider using `_object` instead + +error[E0596]: cannot borrow `object2` as mutable, as it is not declared as mutable + --> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:22:14 + | +LL | object = &mut object2; + | ^^^^^^^^^^^^ cannot borrow as mutable + | +help: consider changing this to be mutable + | +LL | let mut object2 = Object; + | +++ + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0308, E0596, E0597. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/fn/param-mismatch-foreign.rs b/tests/ui/fn/param-mismatch-foreign.rs new file mode 100644 index 0000000000000..2ab2bf95448a4 --- /dev/null +++ b/tests/ui/fn/param-mismatch-foreign.rs @@ -0,0 +1,11 @@ +extern "C" { + fn foo(x: i32, y: u32, z: i32); + //~^ NOTE function defined here +} + +fn main() { + foo(1i32, 2i32); + //~^ ERROR this function takes 3 arguments but 2 arguments were supplied + //~| NOTE argument #2 of type `u32` is missing + //~| HELP provide the argument +} diff --git a/tests/ui/fn/param-mismatch-foreign.stderr b/tests/ui/fn/param-mismatch-foreign.stderr new file mode 100644 index 0000000000000..88aa3cd036833 --- /dev/null +++ b/tests/ui/fn/param-mismatch-foreign.stderr @@ -0,0 +1,20 @@ +error[E0061]: this function takes 3 arguments but 2 arguments were supplied + --> $DIR/param-mismatch-foreign.rs:7:5 + | +LL | foo(1i32, 2i32); + | ^^^ ---- argument #2 of type `u32` is missing + | +note: function defined here + --> $DIR/param-mismatch-foreign.rs:2:8 + | +LL | fn foo(x: i32, y: u32, z: i32); + | ^^^ - +help: provide the argument + | +LL - foo(1i32, 2i32); +LL + foo(1i32, /* u32 */, 2i32); + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0061`. diff --git a/tests/ui/fn/param-mismatch-trait-fn.rs b/tests/ui/fn/param-mismatch-trait-fn.rs new file mode 100644 index 0000000000000..69ded6a9068d7 --- /dev/null +++ b/tests/ui/fn/param-mismatch-trait-fn.rs @@ -0,0 +1,10 @@ +trait Foo { + fn same_type(_: T, _: T); +} + +fn f(x: X, y: Y) { + T::same_type([x], Some(y)); + //~^ ERROR mismatched types +} + +fn main() {} diff --git a/tests/ui/fn/param-mismatch-trait-fn.stderr b/tests/ui/fn/param-mismatch-trait-fn.stderr new file mode 100644 index 0000000000000..28e1bcaaf49dc --- /dev/null +++ b/tests/ui/fn/param-mismatch-trait-fn.stderr @@ -0,0 +1,23 @@ +error[E0308]: mismatched types + --> $DIR/param-mismatch-trait-fn.rs:6:23 + | +LL | T::same_type([x], Some(y)); + | ------------ --- ^^^^^^^ expected `[X; 1]`, found `Option` + | | | + | | expected all arguments to be this `[X; 1]` type because they need to match the type of this parameter + | arguments to this function are incorrect + | + = note: expected array `[X; 1]` + found enum `Option` +note: associated function defined here + --> $DIR/param-mismatch-trait-fn.rs:2:8 + | +LL | fn same_type(_: T, _: T); + | ^^^^^^^^^ - - - this parameter needs to match the `[X; 1]` type of parameter #1 + | | | + | | parameter #2 needs to match the `[X; 1]` type of this parameter + | parameter #1 and parameter #2 both reference this parameter `T` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/for-loop-while/break.rs b/tests/ui/for-loop-while/break.rs index 77774792262cd..442e07e148c57 100644 --- a/tests/ui/for-loop-while/break.rs +++ b/tests/ui/for-loop-while/break.rs @@ -8,18 +8,18 @@ pub fn main() { assert_eq!(i, 20); let xs = [1, 2, 3, 4, 5, 6]; for x in &xs { - if *x == 3 { break; } assert!((*x <= 3)); + if *x == 3 { break; } assert!(*x <= 3); } i = 0; - while i < 10 { i += 1; if i % 2 == 0 { continue; } assert!((i % 2 != 0)); } + while i < 10 { i += 1; if i % 2 == 0 { continue; } assert!(i % 2 != 0); } i = 0; loop { - i += 1; if i % 2 == 0 { continue; } assert!((i % 2 != 0)); + i += 1; if i % 2 == 0 { continue; } assert!(i % 2 != 0); if i >= 10 { break; } } let ys = vec![1, 2, 3, 4, 5, 6]; for x in &ys { if *x % 2 == 0 { continue; } - assert!((*x % 2 != 0)); + assert!(*x % 2 != 0); } } diff --git a/tests/ui/for-loop-while/for-loop-no-std.rs b/tests/ui/for-loop-while/for-loop-no-std.rs index 4511146dc75f4..8255d7b4200c0 100644 --- a/tests/ui/for-loop-while/for-loop-no-std.rs +++ b/tests/ui/for-loop-while/for-loop-no-std.rs @@ -1,14 +1,19 @@ //@ run-pass +//@ ignore-emscripten no no_std executables +//@ ignore-wasm different `main` convention #![allow(unused_imports)] -#![feature(lang_items, start)] #![no_std] +#![no_main] +// Import global allocator and panic handler. extern crate std as other; #[macro_use] extern crate alloc; -#[start] -fn start(_argc: isize, _argv: *const *const u8) -> isize { +use alloc::string::ToString; + +#[no_mangle] +extern "C" fn main(_argc: core::ffi::c_int, _argv: *const *const u8) -> core::ffi::c_int { for _ in [1,2,3].iter() { } 0 } diff --git a/tests/ui/for-loop-while/while-cont.rs b/tests/ui/for-loop-while/while-cont.rs index 1640b7e1803bd..73a08c26f9af6 100644 --- a/tests/ui/for-loop-while/while-cont.rs +++ b/tests/ui/for-loop-while/while-cont.rs @@ -3,7 +3,7 @@ pub fn main() { let mut i = 1; while i > 0 { - assert!((i > 0)); + assert!(i > 0); println!("{}", i); i -= 1; continue; diff --git a/tests/ui/for-loop-while/while-let-2.stderr b/tests/ui/for-loop-while/while-let-2.stderr index 1b1cf67924333..355ae6f718ec8 100644 --- a/tests/ui/for-loop-while/while-let-2.stderr +++ b/tests/ui/for-loop-while/while-let-2.stderr @@ -2,7 +2,7 @@ warning: irrefutable `while let` pattern --> $DIR/while-let-2.rs:7:19 | LL | while let $p = $e $b - | ^^^ + | ^^^^^^^^^^^ ... LL | / foo!(_a, 1, { LL | | println!("irrefutable pattern"); diff --git a/tests/ui/for-loop-while/while-loop-constraints-2.rs b/tests/ui/for-loop-while/while-loop-constraints-2.rs index 654f6769902bd..1d2d9c2ecfe60 100644 --- a/tests/ui/for-loop-while/while-loop-constraints-2.rs +++ b/tests/ui/for-loop-while/while-loop-constraints-2.rs @@ -11,5 +11,5 @@ pub fn main() { while false { x = y; y = z; } println!("{}", y); } - assert!((y == 42 && z == 50)); + assert!(y == 42 && z == 50); } diff --git a/tests/ui/force-inlining/asm.rs b/tests/ui/force-inlining/asm.rs new file mode 100644 index 0000000000000..85014ffa51588 --- /dev/null +++ b/tests/ui/force-inlining/asm.rs @@ -0,0 +1,68 @@ +//@ build-fail +//@ compile-flags: --crate-type=lib --target thumbv4t-none-eabi +//@ needs-llvm-components: arm + +// Checks that forced inlining won't mix asm with incompatible instruction sets. + +#![crate_type = "lib"] +#![feature(rustc_attrs)] +#![feature(no_core, lang_items)] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} +#[lang = "copy"] +pub trait Copy {} +#[lang = "freeze"] +pub unsafe trait Freeze {} + +#[lang = "start"] +fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize { + 0 +} + +#[rustc_builtin_macro] +#[macro_export] +macro_rules! asm { + ("assembly template", + $(operands,)* + $(options($(option),*))? + ) => { + /* compiler built-in */ + }; +} + +#[instruction_set(arm::a32)] +#[rustc_force_inline] +fn instruction_set_a32() {} + +#[instruction_set(arm::t32)] +#[rustc_force_inline] +fn instruction_set_t32() {} + +#[rustc_force_inline] +fn instruction_set_default() {} + +#[rustc_force_inline] +fn inline_always_and_using_inline_asm() { + unsafe { asm!("/* do nothing */") }; +} + +#[instruction_set(arm::t32)] +pub fn t32() { + instruction_set_a32(); +//~^ ERROR `instruction_set_a32` could not be inlined into `t32` but is required to be inlined + instruction_set_t32(); + instruction_set_default(); + inline_always_and_using_inline_asm(); +//~^ ERROR `inline_always_and_using_inline_asm` could not be inlined into `t32` but is required to be inlined +} + +pub fn default() { + instruction_set_a32(); +//~^ ERROR `instruction_set_a32` could not be inlined into `default` but is required to be inlined + instruction_set_t32(); +//~^ ERROR `instruction_set_t32` could not be inlined into `default` but is required to be inlined + instruction_set_default(); + inline_always_and_using_inline_asm(); +} diff --git a/tests/ui/force-inlining/asm.stderr b/tests/ui/force-inlining/asm.stderr new file mode 100644 index 0000000000000..ef04688965bb3 --- /dev/null +++ b/tests/ui/force-inlining/asm.stderr @@ -0,0 +1,34 @@ +error: `instruction_set_a32` could not be inlined into `t32` but is required to be inlined + --> $DIR/asm.rs:53:5 + | +LL | instruction_set_a32(); + | ^^^^^^^^^^^^^^^^^^^^^ ...`instruction_set_a32` called here + | + = note: could not be inlined due to: incompatible instruction set + +error: `inline_always_and_using_inline_asm` could not be inlined into `t32` but is required to be inlined + --> $DIR/asm.rs:57:5 + | +LL | inline_always_and_using_inline_asm(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...`inline_always_and_using_inline_asm` called here + | + = note: could not be inlined due to: cannot move inline-asm across instruction sets + +error: `instruction_set_a32` could not be inlined into `default` but is required to be inlined + --> $DIR/asm.rs:62:5 + | +LL | instruction_set_a32(); + | ^^^^^^^^^^^^^^^^^^^^^ ...`instruction_set_a32` called here + | + = note: could not be inlined due to: incompatible instruction set + +error: `instruction_set_t32` could not be inlined into `default` but is required to be inlined + --> $DIR/asm.rs:64:5 + | +LL | instruction_set_t32(); + | ^^^^^^^^^^^^^^^^^^^^^ ...`instruction_set_t32` called here + | + = note: could not be inlined due to: incompatible instruction set + +error: aborting due to 4 previous errors + diff --git a/tests/ui/force-inlining/auxiliary/callees.rs b/tests/ui/force-inlining/auxiliary/callees.rs new file mode 100644 index 0000000000000..7468b8f10985d --- /dev/null +++ b/tests/ui/force-inlining/auxiliary/callees.rs @@ -0,0 +1,10 @@ +//@ compile-flags: --crate-type=lib +#![feature(rustc_attrs)] + +#[rustc_force_inline = "the test requires it"] +pub fn forced_with_reason() { +} + +#[rustc_force_inline] +pub fn forced() { +} diff --git a/tests/ui/force-inlining/cast.rs b/tests/ui/force-inlining/cast.rs new file mode 100644 index 0000000000000..8f51ec8397736 --- /dev/null +++ b/tests/ui/force-inlining/cast.rs @@ -0,0 +1,25 @@ +//@ check-fail +//@ compile-flags: --crate-type=lib +#![allow(internal_features)] +#![feature(rustc_attrs)] + +#[rustc_force_inline] +pub fn callee(x: isize) -> usize { unimplemented!() } + +fn a() { + let _: fn(isize) -> usize = callee; +//~^ ERROR cannot coerce functions which must be inlined to function pointers +} + +fn b() { + let _ = callee as fn(isize) -> usize; +//~^ ERROR non-primitive cast +} + +fn c() { + let _: [fn(isize) -> usize; 2] = [ + callee, +//~^ ERROR cannot coerce functions which must be inlined to function pointers + callee, + ]; +} diff --git a/tests/ui/force-inlining/cast.stderr b/tests/ui/force-inlining/cast.stderr new file mode 100644 index 0000000000000..5313295965956 --- /dev/null +++ b/tests/ui/force-inlining/cast.stderr @@ -0,0 +1,40 @@ +error[E0308]: cannot coerce functions which must be inlined to function pointers + --> $DIR/cast.rs:10:33 + | +LL | let _: fn(isize) -> usize = callee; + | ------------------ ^^^^^^ cannot coerce functions which must be inlined to function pointers + | | + | expected due to this + | + = note: expected fn pointer `fn(_) -> _` + found fn item `fn(_) -> _ {callee}` + = note: fn items are distinct from fn pointers +help: consider casting to a fn pointer + | +LL | let _: fn(isize) -> usize = callee as fn(isize) -> usize; + | +++++++++++++++++++++ + +error[E0605]: non-primitive cast: `fn(isize) -> usize {callee}` as `fn(isize) -> usize` + --> $DIR/cast.rs:15:13 + | +LL | let _ = callee as fn(isize) -> usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast + +error[E0308]: cannot coerce functions which must be inlined to function pointers + --> $DIR/cast.rs:21:9 + | +LL | callee, + | ^^^^^^ cannot coerce functions which must be inlined to function pointers + | + = note: expected fn pointer `fn(_) -> _` + found fn item `fn(_) -> _ {callee}` + = note: fn items are distinct from fn pointers +help: consider casting to a fn pointer + | +LL | callee as fn(isize) -> usize, + | +++++++++++++++++++++ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0308, E0605. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/force-inlining/cross-crate.rs b/tests/ui/force-inlining/cross-crate.rs new file mode 100644 index 0000000000000..a3f0c2c873ea8 --- /dev/null +++ b/tests/ui/force-inlining/cross-crate.rs @@ -0,0 +1,13 @@ +//@ aux-build:callees.rs +//@ build-pass +//@ compile-flags: --crate-type=lib + +extern crate callees; + +// Test that forced inlining across crates works as expected. + +pub fn caller() { + callees::forced(); + + callees::forced_with_reason(); +} diff --git a/tests/ui/force-inlining/deny-async.rs b/tests/ui/force-inlining/deny-async.rs new file mode 100644 index 0000000000000..7c95a53c26fde --- /dev/null +++ b/tests/ui/force-inlining/deny-async.rs @@ -0,0 +1,26 @@ +//@ compile-flags: --crate-type=lib +//@ edition: 2021 + +#![allow(internal_features)] +#![feature(rustc_attrs)] + +// Test that forced inlining into async functions w/ errors works as expected. + +#[rustc_no_mir_inline] +#[rustc_force_inline] +//~^ ERROR `callee` is incompatible with `#[rustc_force_inline]` +pub fn callee() { +} + +#[rustc_no_mir_inline] +#[rustc_force_inline = "the test requires it"] +//~^ ERROR `callee_justified` is incompatible with `#[rustc_force_inline]` +pub fn callee_justified() { +} + +async fn async_caller() { + callee(); + //~^ ERROR `callee` could not be inlined + callee_justified(); + //~^ ERROR `callee_justified` could not be inlined +} diff --git a/tests/ui/force-inlining/deny-async.stderr b/tests/ui/force-inlining/deny-async.stderr new file mode 100644 index 0000000000000..d6516ed875c21 --- /dev/null +++ b/tests/ui/force-inlining/deny-async.stderr @@ -0,0 +1,41 @@ +error: `callee` is incompatible with `#[rustc_force_inline]` + --> $DIR/deny-async.rs:10:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | pub fn callee() { + | --------------- `callee` defined here + | + = note: incompatible due to: #[rustc_no_mir_inline] + +error: `callee_justified` is incompatible with `#[rustc_force_inline]` + --> $DIR/deny-async.rs:16:1 + | +LL | #[rustc_force_inline = "the test requires it"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | pub fn callee_justified() { + | ------------------------- `callee_justified` defined here + | + = note: incompatible due to: #[rustc_no_mir_inline] + +error: `callee` could not be inlined into `async_caller::{closure#0}` but is required to be inlined + --> $DIR/deny-async.rs:22:5 + | +LL | callee(); + | ^^^^^^^^ ...`callee` called here + | + = note: could not be inlined due to: #[rustc_no_mir_inline] + +error: `callee_justified` could not be inlined into `async_caller::{closure#0}` but is required to be inlined + --> $DIR/deny-async.rs:24:5 + | +LL | callee_justified(); + | ^^^^^^^^^^^^^^^^^^ ...`callee_justified` called here + | + = note: could not be inlined due to: #[rustc_no_mir_inline] + = note: `callee_justified` is required to be inlined to: the test requires it + +error: aborting due to 4 previous errors + diff --git a/tests/ui/force-inlining/deny-closure.rs b/tests/ui/force-inlining/deny-closure.rs new file mode 100644 index 0000000000000..31314c450fcf1 --- /dev/null +++ b/tests/ui/force-inlining/deny-closure.rs @@ -0,0 +1,25 @@ +//@ check-fail +//@ compile-flags: --crate-type=lib +#![allow(internal_features)] +#![feature(rustc_attrs)] + +// Test that forced inlining into closures w/ errors works as expected. + +#[rustc_no_mir_inline] +#[rustc_force_inline] +//~^ ERROR `callee` is incompatible with `#[rustc_force_inline]` +pub fn callee() { +} + +#[rustc_no_mir_inline] +#[rustc_force_inline = "the test requires it"] +//~^ ERROR `callee_justified` is incompatible with `#[rustc_force_inline]` +pub fn callee_justified() { +} + +pub fn caller() { + (|| { + callee(); + callee_justified(); + })(); +} diff --git a/tests/ui/force-inlining/deny-closure.stderr b/tests/ui/force-inlining/deny-closure.stderr new file mode 100644 index 0000000000000..e657a29542023 --- /dev/null +++ b/tests/ui/force-inlining/deny-closure.stderr @@ -0,0 +1,24 @@ +error: `callee` is incompatible with `#[rustc_force_inline]` + --> $DIR/deny-closure.rs:9:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | pub fn callee() { + | --------------- `callee` defined here + | + = note: incompatible due to: #[rustc_no_mir_inline] + +error: `callee_justified` is incompatible with `#[rustc_force_inline]` + --> $DIR/deny-closure.rs:15:1 + | +LL | #[rustc_force_inline = "the test requires it"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | pub fn callee_justified() { + | ------------------------- `callee_justified` defined here + | + = note: incompatible due to: #[rustc_no_mir_inline] + +error: aborting due to 2 previous errors + diff --git a/tests/ui/force-inlining/deny.rs b/tests/ui/force-inlining/deny.rs new file mode 100644 index 0000000000000..7712f5f50f316 --- /dev/null +++ b/tests/ui/force-inlining/deny.rs @@ -0,0 +1,23 @@ +//@ check-fail +//@ compile-flags: --crate-type=lib +#![allow(internal_features)] +#![feature(rustc_attrs)] + +// Test that forced inlining w/ errors works as expected. + +#[rustc_no_mir_inline] +#[rustc_force_inline] +//~^ ERROR `callee` is incompatible with `#[rustc_force_inline]` +pub fn callee() { +} + +#[rustc_no_mir_inline] +#[rustc_force_inline = "the test requires it"] +//~^ ERROR `callee_justified` is incompatible with `#[rustc_force_inline]` +pub fn callee_justified() { +} + +pub fn caller() { + callee(); + callee_justified(); +} diff --git a/tests/ui/force-inlining/deny.stderr b/tests/ui/force-inlining/deny.stderr new file mode 100644 index 0000000000000..c276fa28ba895 --- /dev/null +++ b/tests/ui/force-inlining/deny.stderr @@ -0,0 +1,24 @@ +error: `callee` is incompatible with `#[rustc_force_inline]` + --> $DIR/deny.rs:9:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | pub fn callee() { + | --------------- `callee` defined here + | + = note: incompatible due to: #[rustc_no_mir_inline] + +error: `callee_justified` is incompatible with `#[rustc_force_inline]` + --> $DIR/deny.rs:15:1 + | +LL | #[rustc_force_inline = "the test requires it"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | pub fn callee_justified() { + | ------------------------- `callee_justified` defined here + | + = note: incompatible due to: #[rustc_no_mir_inline] + +error: aborting due to 2 previous errors + diff --git a/tests/ui/force-inlining/early-deny.rs b/tests/ui/force-inlining/early-deny.rs new file mode 100644 index 0000000000000..99b03a4e0e2ce --- /dev/null +++ b/tests/ui/force-inlining/early-deny.rs @@ -0,0 +1,21 @@ +//@ check-fail +//@ compile-flags: --crate-type=lib +#![feature(c_variadic)] +#![feature(rustc_attrs)] + +#[rustc_no_mir_inline] +#[rustc_force_inline] +//~^ ERROR `rustc_attr` is incompatible with `#[rustc_force_inline]` +pub fn rustc_attr() { +} + +#[cold] +#[rustc_force_inline] +//~^ ERROR `cold` is incompatible with `#[rustc_force_inline]` +pub fn cold() { +} + +#[rustc_force_inline] +//~^ ERROR `variadic` is incompatible with `#[rustc_force_inline]` +pub unsafe extern "C" fn variadic(args: ...) { +} diff --git a/tests/ui/force-inlining/early-deny.stderr b/tests/ui/force-inlining/early-deny.stderr new file mode 100644 index 0000000000000..abee66fd293c6 --- /dev/null +++ b/tests/ui/force-inlining/early-deny.stderr @@ -0,0 +1,35 @@ +error: `rustc_attr` is incompatible with `#[rustc_force_inline]` + --> $DIR/early-deny.rs:7:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | pub fn rustc_attr() { + | ------------------- `rustc_attr` defined here + | + = note: incompatible due to: #[rustc_no_mir_inline] + +error: `cold` is incompatible with `#[rustc_force_inline]` + --> $DIR/early-deny.rs:13:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | pub fn cold() { + | ------------- `cold` defined here + | + = note: incompatible due to: cold + +error: `variadic` is incompatible with `#[rustc_force_inline]` + --> $DIR/early-deny.rs:18:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | pub unsafe extern "C" fn variadic(args: ...) { + | -------------------------------------------- `variadic` defined here + | + = note: incompatible due to: C variadic + +error: aborting due to 3 previous errors + diff --git a/tests/ui/force-inlining/gate.rs b/tests/ui/force-inlining/gate.rs new file mode 100644 index 0000000000000..d6a01a74a44b5 --- /dev/null +++ b/tests/ui/force-inlining/gate.rs @@ -0,0 +1,12 @@ +//@ compile-flags: --crate-type=lib +#![allow(internal_features)] + +#[rustc_force_inline] +//~^ ERROR #![rustc_force_inline] forces a free function to be inlined +pub fn bare() { +} + +#[rustc_force_inline = "the test requires it"] +//~^ ERROR #![rustc_force_inline] forces a free function to be inlined +pub fn justified() { +} diff --git a/tests/ui/force-inlining/gate.stderr b/tests/ui/force-inlining/gate.stderr new file mode 100644 index 0000000000000..e3973a08c232b --- /dev/null +++ b/tests/ui/force-inlining/gate.stderr @@ -0,0 +1,21 @@ +error[E0658]: #![rustc_force_inline] forces a free function to be inlined + --> $DIR/gate.rs:4:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: #![rustc_force_inline] forces a free function to be inlined + --> $DIR/gate.rs:9:1 + | +LL | #[rustc_force_inline = "the test requires it"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/force-inlining/invalid.rs b/tests/ui/force-inlining/invalid.rs new file mode 100644 index 0000000000000..7574078b245c2 --- /dev/null +++ b/tests/ui/force-inlining/invalid.rs @@ -0,0 +1,164 @@ +//@ edition: 2024 +#![allow(internal_features, unused_imports, unused_macros)] +#![feature(extern_types)] +#![feature(gen_blocks)] +#![feature(rustc_attrs)] +#![feature(stmt_expr_attributes)] +#![feature(trait_alias)] + +// Test that invalid force inlining attributes error as expected. + +#[rustc_force_inline("foo")] +//~^ ERROR malformed `rustc_force_inline` attribute input +pub fn forced1() { +} + +#[rustc_force_inline(bar, baz)] +//~^ ERROR malformed `rustc_force_inline` attribute input +pub fn forced2() { +} + +#[rustc_force_inline(2)] +//~^ ERROR malformed `rustc_force_inline` attribute input +pub fn forced3() { +} + +#[rustc_force_inline = 2] +//~^ ERROR malformed `rustc_force_inline` attribute input +pub fn forced4() { +} + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +extern crate std as other_std; + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +use std::collections::HashMap; + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +static _FOO: &'static str = "FOO"; + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +const _BAR: u32 = 3; + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +mod foo { } + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +unsafe extern "C" { + #[rustc_force_inline] +//~^ ERROR attribute should be applied to a function + static X: &'static u32; + + #[rustc_force_inline] +//~^ ERROR attribute should be applied to a function + type Y; + + #[rustc_force_inline] +//~^ ERROR attribute should be applied to a function + fn foo(); +} + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +type Foo = u32; + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +enum Bar<#[rustc_force_inline] T> { +//~^ ERROR attribute should be applied to a function + #[rustc_force_inline] +//~^ ERROR attribute should be applied to a function + Baz(std::marker::PhantomData), +} + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +struct Qux { + #[rustc_force_inline] +//~^ ERROR attribute should be applied to a function + field: u32, +} + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +union FooBar { + x: u32, + y: u32, +} + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +trait FooBaz { + #[rustc_force_inline] +//~^ ERROR attribute should be applied to a function + type Foo; + #[rustc_force_inline] +//~^ ERROR attribute should be applied to a function + const Bar: i32; + + #[rustc_force_inline] +//~^ ERROR attribute should be applied to a function + fn foo() {} +} + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +trait FooQux = FooBaz; + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +impl Bar { + #[rustc_force_inline] +//~^ ERROR attribute should be applied to a function + fn foo() {} +} + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +impl FooBaz for Bar { + type Foo = u32; + const Bar: i32 = 3; +} + +#[rustc_force_inline] +//~^ ERROR attribute should be applied to a function +macro_rules! barqux { ($foo:tt) => { $foo }; } + +fn barqux(#[rustc_force_inline] _x: u32) {} +//~^ ERROR allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters +//~^^ ERROR attribute should be applied to a function + +#[rustc_force_inline] +//~^ ERROR attribute cannot be applied to a `async`, `gen` or `async gen` function +async fn async_foo() {} + +#[rustc_force_inline] +//~^ ERROR attribute cannot be applied to a `async`, `gen` or `async gen` function +gen fn gen_foo() {} + +#[rustc_force_inline] +//~^ ERROR attribute cannot be applied to a `async`, `gen` or `async gen` function +async gen fn async_gen_foo() {} + +fn main() { + let _x = #[rustc_force_inline] || { }; +//~^ ERROR attribute should be applied to a function + let _y = #[rustc_force_inline] 3 + 4; +//~^ ERROR attribute should be applied to a function + #[rustc_force_inline] +//~^ ERROR attribute should be applied to a function + let _z = 3; + + match _z { + #[rustc_force_inline] +//~^ ERROR attribute should be applied to a function + 1 => (), + _ => (), + } +} diff --git a/tests/ui/force-inlining/invalid.stderr b/tests/ui/force-inlining/invalid.stderr new file mode 100644 index 0000000000000..92b3c314bad18 --- /dev/null +++ b/tests/ui/force-inlining/invalid.stderr @@ -0,0 +1,385 @@ +error: malformed `rustc_force_inline` attribute input + --> $DIR/invalid.rs:11:1 + | +LL | #[rustc_force_inline("foo")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL - #[rustc_force_inline("foo")] +LL + #[rustc_force_inline = "reason"] + | +LL - #[rustc_force_inline("foo")] +LL + #[rustc_force_inline] + | + +error: malformed `rustc_force_inline` attribute input + --> $DIR/invalid.rs:16:1 + | +LL | #[rustc_force_inline(bar, baz)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL - #[rustc_force_inline(bar, baz)] +LL + #[rustc_force_inline = "reason"] + | +LL - #[rustc_force_inline(bar, baz)] +LL + #[rustc_force_inline] + | + +error: malformed `rustc_force_inline` attribute input + --> $DIR/invalid.rs:21:1 + | +LL | #[rustc_force_inline(2)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL - #[rustc_force_inline(2)] +LL + #[rustc_force_inline = "reason"] + | +LL - #[rustc_force_inline(2)] +LL + #[rustc_force_inline] + | + +error: malformed `rustc_force_inline` attribute input + --> $DIR/invalid.rs:26:1 + | +LL | #[rustc_force_inline = 2] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL - #[rustc_force_inline = 2] +LL + #[rustc_force_inline = "reason"] + | +LL - #[rustc_force_inline = 2] +LL + #[rustc_force_inline] + | + +error: allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters + --> $DIR/invalid.rs:133:11 + | +LL | fn barqux(#[rustc_force_inline] _x: u32) {} + | ^^^^^^^^^^^^^^^^^^^^^ + +error: attribute should be applied to a function + --> $DIR/invalid.rs:31:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | extern crate std as other_std; + | ------------------------------ not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:35:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | use std::collections::HashMap; + | ------------------------------ not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:39:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | static _FOO: &'static str = "FOO"; + | ---------------------------------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:43:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | const _BAR: u32 = 3; + | -------------------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:47:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | mod foo { } + | ----------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:51:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | / unsafe extern "C" { +LL | | #[rustc_force_inline] +LL | | +LL | | static X: &'static u32; +... | +LL | | fn foo(); +LL | | } + | |_- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:67:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | type Foo = u32; + | --------------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:71:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | / enum Bar<#[rustc_force_inline] T> { +LL | | +LL | | #[rustc_force_inline] +... | +LL | | } + | |_- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:73:10 + | +LL | enum Bar<#[rustc_force_inline] T> { + | ^^^^^^^^^^^^^^^^^^^^^ - not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:75:5 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | Baz(std::marker::PhantomData), + | -------------------------------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:80:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | / struct Qux { +LL | | #[rustc_force_inline] +LL | | +LL | | field: u32, +LL | | } + | |_- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:83:5 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | field: u32, + | ---------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:88:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | / union FooBar { +LL | | x: u32, +LL | | y: u32, +LL | | } + | |_- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:95:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | / trait FooBaz { +LL | | #[rustc_force_inline] +LL | | +LL | | type Foo; +... | +LL | | fn foo() {} +LL | | } + | |_- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:110:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | trait FooQux = FooBaz; + | ---------------------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:114:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | / impl Bar { +LL | | #[rustc_force_inline] +LL | | +LL | | fn foo() {} +LL | | } + | |_- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:122:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | / impl FooBaz for Bar { +LL | | type Foo = u32; +LL | | const Bar: i32 = 3; +LL | | } + | |_- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:129:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | macro_rules! barqux { ($foo:tt) => { $foo }; } + | ---------------------------------------------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:133:11 + | +LL | fn barqux(#[rustc_force_inline] _x: u32) {} + | ^^^^^^^^^^^^^^^^^^^^^-------- + | | + | not a function definition + +error: attribute cannot be applied to a `async`, `gen` or `async gen` function + --> $DIR/invalid.rs:137:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | async fn async_foo() {} + | -------------------- `async`, `gen` or `async gen` function + +error: attribute cannot be applied to a `async`, `gen` or `async gen` function + --> $DIR/invalid.rs:141:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | gen fn gen_foo() {} + | ---------------- `async`, `gen` or `async gen` function + +error: attribute cannot be applied to a `async`, `gen` or `async gen` function + --> $DIR/invalid.rs:145:1 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | async gen fn async_gen_foo() {} + | ---------------------------- `async`, `gen` or `async gen` function + +error: attribute should be applied to a function + --> $DIR/invalid.rs:150:14 + | +LL | let _x = #[rustc_force_inline] || { }; + | ^^^^^^^^^^^^^^^^^^^^^ ------ not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:152:14 + | +LL | let _y = #[rustc_force_inline] 3 + 4; + | ^^^^^^^^^^^^^^^^^^^^^ - not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:154:5 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | let _z = 3; + | ----------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:159:9 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | 1 => (), + | ------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:98:5 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | type Foo; + | --------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:101:5 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | const Bar: i32; + | --------------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:105:5 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | fn foo() {} + | ----------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:117:5 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | fn foo() {} + | ----------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:54:5 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | static X: &'static u32; + | ----------------------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:58:5 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | type Y; + | ------- not a function definition + +error: attribute should be applied to a function + --> $DIR/invalid.rs:62:5 + | +LL | #[rustc_force_inline] + | ^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | fn foo(); + | --------- not a function definition + +error: aborting due to 38 previous errors + diff --git a/tests/ui/force-inlining/shims.rs b/tests/ui/force-inlining/shims.rs new file mode 100644 index 0000000000000..03b7d07cda39e --- /dev/null +++ b/tests/ui/force-inlining/shims.rs @@ -0,0 +1,9 @@ +//@ build-pass +#![allow(internal_features)] +#![feature(rustc_attrs)] + +#[rustc_force_inline] +fn f() {} +fn g(t: T) { t(); } + +fn main() { g(f); } diff --git a/tests/ui/foreign/issue-91370-foreign-fn-block-impl.rs b/tests/ui/foreign/issue-91370-foreign-fn-block-impl.rs index 2ac3ca29355d8..e8634de86ea49 100644 --- a/tests/ui/foreign/issue-91370-foreign-fn-block-impl.rs +++ b/tests/ui/foreign/issue-91370-foreign-fn-block-impl.rs @@ -1,6 +1,6 @@ // Regression test for issue #91370. -extern { +extern "C" { //~^ `extern` blocks define existing foreign functions fn f() { //~^ incorrect function inside `extern` block diff --git a/tests/ui/foreign/issue-91370-foreign-fn-block-impl.stderr b/tests/ui/foreign/issue-91370-foreign-fn-block-impl.stderr index fea2ab61e929f..155fdf9d09ab0 100644 --- a/tests/ui/foreign/issue-91370-foreign-fn-block-impl.stderr +++ b/tests/ui/foreign/issue-91370-foreign-fn-block-impl.stderr @@ -1,8 +1,8 @@ error: incorrect function inside `extern` block --> $DIR/issue-91370-foreign-fn-block-impl.rs:5:8 | -LL | extern { - | ------ `extern` blocks define existing foreign functions and functions inside of them cannot have a body +LL | extern "C" { + | ---------- `extern` blocks define existing foreign functions and functions inside of them cannot have a body LL | LL | fn f() { | ________^___- diff --git a/tests/ui/foreign/stashed-issue-121451.rs b/tests/ui/foreign/stashed-issue-121451.rs index 97a4af374758d..77a736739bfa0 100644 --- a/tests/ui/foreign/stashed-issue-121451.rs +++ b/tests/ui/foreign/stashed-issue-121451.rs @@ -1,4 +1,4 @@ extern "C" fn _f() -> libc::uintptr_t {} -//~^ ERROR failed to resolve: use of undeclared crate or module `libc` +//~^ ERROR failed to resolve fn main() {} diff --git a/tests/ui/foreign/stashed-issue-121451.stderr b/tests/ui/foreign/stashed-issue-121451.stderr index 440d98d6f460d..31dd3b4fb5e8d 100644 --- a/tests/ui/foreign/stashed-issue-121451.stderr +++ b/tests/ui/foreign/stashed-issue-121451.stderr @@ -1,8 +1,10 @@ -error[E0433]: failed to resolve: use of undeclared crate or module `libc` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `libc` --> $DIR/stashed-issue-121451.rs:1:23 | LL | extern "C" fn _f() -> libc::uintptr_t {} - | ^^^^ use of undeclared crate or module `libc` + | ^^^^ use of unresolved module or unlinked crate `libc` + | + = help: you might be missing a crate named `libc` error: aborting due to 1 previous error diff --git a/tests/ui/format-no-std.rs b/tests/ui/format-no-std.rs index 27c31f48a006c..657b210a9a0b4 100644 --- a/tests/ui/format-no-std.rs +++ b/tests/ui/format-no-std.rs @@ -1,17 +1,20 @@ //@ run-pass //@ ignore-emscripten no no_std executables +//@ ignore-wasm different `main` convention -#![feature(lang_items, start)] +#![feature(lang_items)] #![no_std] +#![no_main] +// Import global allocator and panic handler. extern crate std as other; #[macro_use] extern crate alloc; use alloc::string::ToString; -#[start] -fn start(_argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(_argc: core::ffi::c_int, _argv: *const *const u8) -> core::ffi::c_int { let s = format!("{}", 1_isize); assert_eq!(s, "1".to_string()); diff --git a/tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr b/tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr index d167a60dad387..b09ffb10673e2 100644 --- a/tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr +++ b/tests/ui/functional-struct-update/functional-struct-update-noncopyable.stderr @@ -9,8 +9,9 @@ LL | let _b = A { y: Arc::new(3), ..a }; | help: clone the value from the field instead of using the functional record update syntax | -LL | let _b = A { y: Arc::new(3), x: a.x.clone() }; - | ~~~~~~~~~~~~~~~~ +LL - let _b = A { y: Arc::new(3), ..a }; +LL + let _b = A { y: Arc::new(3), x: a.x.clone() }; + | error: aborting due to 1 previous error diff --git a/tests/ui/generic-associated-types/bugs/issue-87735.stderr b/tests/ui/generic-associated-types/bugs/issue-87735.stderr index d80050652389d..1b95543136303 100644 --- a/tests/ui/generic-associated-types/bugs/issue-87735.stderr +++ b/tests/ui/generic-associated-types/bugs/issue-87735.stderr @@ -22,73 +22,7 @@ help: consider adding an explicit lifetime bound LL | type Output<'a> = FooRef<'a, U> where Self: 'a, U: 'a; | +++++++ -error[E0309]: the parameter type `T` may not live long enough - --> $DIR/issue-87735.rs:31:15 - | -LL | impl<'b, T, U> AsRef2 for Foo - | -- the parameter type `T` must be valid for the lifetime `'b` as defined here... -... -LL | T: AsRef2 = &'b [U]>, - | ^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds... - | -note: ...that is required by this bound - --> $DIR/issue-87735.rs:7:31 - | -LL | type Output<'a> where Self: 'a; - | ^^ -help: consider adding an explicit lifetime bound - | -LL | T: AsRef2 = &'b [U]> + 'b, - | ++++ - -error[E0309]: the parameter type `T` may not live long enough - --> $DIR/issue-87735.rs:36:31 - | -LL | impl<'b, T, U> AsRef2 for Foo - | -- the parameter type `T` must be valid for the lifetime `'b` as defined here... -... -LL | fn as_ref2<'a>(&'a self) -> Self::Output<'a> { - | ^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds... - | -note: ...that is required by this bound - --> $DIR/issue-87735.rs:7:31 - | -LL | type Output<'a> where Self: 'a; - | ^^ -help: consider adding an explicit lifetime bound - | -LL | T: AsRef2 = &'b [U]> + 'b, - | ++++ - -error: lifetime may not live long enough - --> $DIR/issue-87735.rs:37:5 - | -LL | impl<'b, T, U> AsRef2 for Foo - | -- lifetime `'b` defined here -... -LL | fn as_ref2<'a>(&'a self) -> Self::Output<'a> { - | -- lifetime `'a` defined here -LL | FooRef(self.0.as_ref2()) - | ^^^^^^^^^^^^^^^^^^^^^^^^ method was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` - | - = help: consider adding the following bound: `'b: 'a` - -error: lifetime may not live long enough - --> $DIR/issue-87735.rs:37:12 - | -LL | impl<'b, T, U> AsRef2 for Foo - | -- lifetime `'b` defined here -... -LL | fn as_ref2<'a>(&'a self) -> Self::Output<'a> { - | -- lifetime `'a` defined here -LL | FooRef(self.0.as_ref2()) - | ^^^^^^^^^^^^^^^^ argument requires that `'a` must outlive `'b` - | - = help: consider adding the following bound: `'a: 'b` - -help: `'b` and `'a` must be the same: replace one with the other - -error: aborting due to 6 previous errors +error: aborting due to 2 previous errors Some errors have detailed explanations: E0207, E0309. For more information about an error, try `rustc --explain E0207`. diff --git a/tests/ui/generic-associated-types/gat-in-trait-path-undeclared-lifetime.rs b/tests/ui/generic-associated-types/gat-in-trait-path-undeclared-lifetime.rs index 1a4678c7e70b6..b02739a7d0acb 100644 --- a/tests/ui/generic-associated-types/gat-in-trait-path-undeclared-lifetime.rs +++ b/tests/ui/generic-associated-types/gat-in-trait-path-undeclared-lifetime.rs @@ -8,5 +8,5 @@ fn main() { //~| ERROR: binding for associated type `Y` references lifetime //~| ERROR: binding for associated type `Y` references lifetime //~| ERROR: binding for associated type `Y` references lifetime - //~| ERROR: the trait `X` cannot be made into an object + //~| ERROR: the trait `X` is not dyn compatible } diff --git a/tests/ui/generic-associated-types/gat-in-trait-path-undeclared-lifetime.stderr b/tests/ui/generic-associated-types/gat-in-trait-path-undeclared-lifetime.stderr index 867f55b0deead..c9b46de5c33c4 100644 --- a/tests/ui/generic-associated-types/gat-in-trait-path-undeclared-lifetime.stderr +++ b/tests/ui/generic-associated-types/gat-in-trait-path-undeclared-lifetime.stderr @@ -36,17 +36,18 @@ LL | fn _f(arg : Box X = &'a [u32]>>) {} | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0038]: the trait `X` cannot be made into an object +error[E0038]: the trait `X` is not dyn compatible --> $DIR/gat-in-trait-path-undeclared-lifetime.rs:6:19 | LL | fn _f(arg : Box X = &'a [u32]>>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `X` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `X` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/gat-in-trait-path-undeclared-lifetime.rs:2:8 | LL | trait X { - | - this trait cannot be made into an object... + | - this trait is not dyn compatible... LL | type Y<'x>; | ^ ...because it contains the generic associated type `Y` = help: consider moving `Y` to another trait diff --git a/tests/ui/generic-associated-types/gat-in-trait-path.base.stderr b/tests/ui/generic-associated-types/gat-in-trait-path.base.stderr deleted file mode 100644 index 34642f8fdc6c8..0000000000000 --- a/tests/ui/generic-associated-types/gat-in-trait-path.base.stderr +++ /dev/null @@ -1,58 +0,0 @@ -error[E0038]: the trait `Foo` cannot be made into an object - --> $DIR/gat-in-trait-path.rs:26:17 - | -LL | fn f(_arg : Box Foo = &'a ()>>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Foo` cannot be made into an object - | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/gat-in-trait-path.rs:10:10 - | -LL | trait Foo { - | --- this trait cannot be made into an object... -LL | type A<'a> where Self: 'a; - | ^ ...because it contains the generic associated type `A` - = help: consider moving `A` to another trait - = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `Foo` for this new enum and using it instead: - Fooy - Fooer - -error[E0038]: the trait `Foo` cannot be made into an object - --> $DIR/gat-in-trait-path.rs:32:5 - | -LL | f(Box::new(foo)); - | ^^^^^^^^^^^^^ `Foo` cannot be made into an object - | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/gat-in-trait-path.rs:10:10 - | -LL | trait Foo { - | --- this trait cannot be made into an object... -LL | type A<'a> where Self: 'a; - | ^ ...because it contains the generic associated type `A` - = help: consider moving `A` to another trait - = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `Foo` for this new enum and using it instead: - Fooy - Fooer - -error[E0038]: the trait `Foo` cannot be made into an object - --> $DIR/gat-in-trait-path.rs:32:5 - | -LL | f(Box::new(foo)); - | ^^^^^^^^^^^^^ `Foo` cannot be made into an object - | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/gat-in-trait-path.rs:10:10 - | -LL | trait Foo { - | --- this trait cannot be made into an object... -LL | type A<'a> where Self: 'a; - | ^ ...because it contains the generic associated type `A` - = help: consider moving `A` to another trait - = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `Foo` for this new enum and using it instead: - Fooy - Fooer - = note: required for the cast from `Box>` to `Box<(dyn Foo = &'a ()> + 'static)>` - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/gat-in-trait-path.rs b/tests/ui/generic-associated-types/gat-in-trait-path.rs index cd759a73cf278..24cae213e0aa4 100644 --- a/tests/ui/generic-associated-types/gat-in-trait-path.rs +++ b/tests/ui/generic-associated-types/gat-in-trait-path.rs @@ -20,12 +20,11 @@ impl Foo for Fooer { } fn f(_arg : Box Foo = &'a ()>>) {} -//~^ the trait `Foo` cannot be made into an object - +//~^ the trait `Foo` is not dyn compatible fn main() { let foo = Fooer(5); f(Box::new(foo)); - //~^ the trait `Foo` cannot be made into an object - //~| the trait `Foo` cannot be made into an object + //~^ the trait `Foo` is not dyn compatible + //~| the trait `Foo` is not dyn compatible } diff --git a/tests/ui/generic-associated-types/gat-in-trait-path.stderr b/tests/ui/generic-associated-types/gat-in-trait-path.stderr index b2176fa6de3b4..e57f6b48401fc 100644 --- a/tests/ui/generic-associated-types/gat-in-trait-path.stderr +++ b/tests/ui/generic-associated-types/gat-in-trait-path.stderr @@ -1,56 +1,50 @@ -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/gat-in-trait-path.rs:22:17 | LL | fn f(_arg : Box Foo = &'a ()>>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/gat-in-trait-path.rs:6:10 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | type A<'a> where Self: 'a; | ^ ...because it contains the generic associated type `A` = help: consider moving `A` to another trait - = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `Foo` for this new enum and using it instead: - Fooy - Fooer -error[E0038]: the trait `Foo` cannot be made into an object - --> $DIR/gat-in-trait-path.rs:28:5 +error[E0038]: the trait `Foo` is not dyn compatible + --> $DIR/gat-in-trait-path.rs:27:5 | LL | f(Box::new(foo)); - | ^^^^^^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/gat-in-trait-path.rs:6:10 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | type A<'a> where Self: 'a; | ^ ...because it contains the generic associated type `A` = help: consider moving `A` to another trait - = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `Foo` for this new enum and using it instead: - Fooy - Fooer -error[E0038]: the trait `Foo` cannot be made into an object - --> $DIR/gat-in-trait-path.rs:28:5 +error[E0038]: the trait `Foo` is not dyn compatible + --> $DIR/gat-in-trait-path.rs:27:5 | LL | f(Box::new(foo)); - | ^^^^^^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/gat-in-trait-path.rs:6:10 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | type A<'a> where Self: 'a; | ^ ...because it contains the generic associated type `A` = help: consider moving `A` to another trait - = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `Foo` for this new enum and using it instead: - Fooy - Fooer = note: required for the cast from `Box>` to `Box<(dyn Foo = &'a ()> + 'static)>` error: aborting due to 3 previous errors diff --git a/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.rs b/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.rs index c413442701303..85661c1b84480 100644 --- a/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.rs +++ b/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.rs @@ -12,7 +12,7 @@ fn foo<'a>(arg: Box>) {} //~| ERROR associated type takes 0 generic arguments but 1 generic argument //~| ERROR associated type takes 1 lifetime argument but 0 lifetime arguments //~| ERROR at least one trait is required - //~| ERROR: the trait `X` cannot be made into an object + //~| ERROR: the trait `X` is not dyn compatible fn bar<'a>(arg: Box>) {} @@ -20,6 +20,6 @@ fn bar<'a>(arg: Box>) {} //~| ERROR associated type takes 1 lifetime argument but 0 lifetime arguments //~| ERROR associated type takes 1 lifetime argument but 0 lifetime arguments //~| ERROR associated type takes 1 lifetime argument but 0 lifetime arguments - //~| ERROR: the trait `X` cannot be made into an object + //~| ERROR: the trait `X` is not dyn compatible fn main() {} diff --git a/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.stderr b/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.stderr index 97b7019b38515..99300ea1cb7f1 100644 --- a/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.stderr +++ b/tests/ui/generic-associated-types/gat-trait-path-parenthesised-args.stderr @@ -12,8 +12,9 @@ LL | fn foo<'a>(arg: Box>) {} | help: use angle brackets instead | -LL | fn foo<'a>(arg: Box = &'a ()>>) {} - | ~ ~ +LL - fn foo<'a>(arg: Box>) {} +LL + fn foo<'a>(arg: Box = &'a ()>>) {} + | error: parenthesized generic arguments cannot be used in associated type constraints --> $DIR/gat-trait-path-parenthesised-args.rs:18:27 @@ -123,17 +124,18 @@ error[E0224]: at least one trait is required for an object type LL | fn foo<'a>(arg: Box>) {} | ^^ -error[E0038]: the trait `X` cannot be made into an object +error[E0038]: the trait `X` is not dyn compatible --> $DIR/gat-trait-path-parenthesised-args.rs:5:21 | LL | fn foo<'a>(arg: Box>) {} - | ^^^^^^^^^^^^^^^^^^^^^ `X` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^^ `X` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/gat-trait-path-parenthesised-args.rs:2:8 | LL | trait X { - | - this trait cannot be made into an object... + | - this trait is not dyn compatible... LL | type Y<'a>; | ^ ...because it contains the generic associated type `Y` = help: consider moving `Y` to another trait @@ -188,17 +190,18 @@ help: add missing lifetime argument LL | fn bar<'a>(arg: Box>) {} | ++ -error[E0038]: the trait `X` cannot be made into an object +error[E0038]: the trait `X` is not dyn compatible --> $DIR/gat-trait-path-parenthesised-args.rs:18:21 | LL | fn bar<'a>(arg: Box>) {} - | ^^^^^^^^^^^^^^^ `X` cannot be made into an object + | ^^^^^^^^^^^^^^^ `X` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/gat-trait-path-parenthesised-args.rs:2:8 | LL | trait X { - | - this trait cannot be made into an object... + | - this trait is not dyn compatible... LL | type Y<'a>; | ^ ...because it contains the generic associated type `Y` = help: consider moving `Y` to another trait diff --git a/tests/ui/generic-associated-types/impl_bounds.stderr b/tests/ui/generic-associated-types/impl_bounds.stderr index aa56505dd300b..291f0ade439eb 100644 --- a/tests/ui/generic-associated-types/impl_bounds.stderr +++ b/tests/ui/generic-associated-types/impl_bounds.stderr @@ -18,8 +18,9 @@ LL | type B<'a, 'b> = (&'a(), &'b ()) where 'b: 'a; | help: copy the `where` clause predicates from the trait | -LL | type B<'a, 'b> = (&'a(), &'b ()) where 'a: 'b; - | ~~~~~~~~~~~~ +LL - type B<'a, 'b> = (&'a(), &'b ()) where 'b: 'a; +LL + type B<'a, 'b> = (&'a(), &'b ()) where 'a: 'b; + | error[E0277]: the trait bound `T: Copy` is not satisfied --> $DIR/impl_bounds.rs:18:33 diff --git a/tests/ui/generic-associated-types/issue-67510-pass.base.stderr b/tests/ui/generic-associated-types/issue-67510-pass.base.stderr deleted file mode 100644 index cac8010018ec0..0000000000000 --- a/tests/ui/generic-associated-types/issue-67510-pass.base.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error[E0038]: the trait `X` cannot be made into an object - --> $DIR/issue-67510-pass.rs:12:23 - | -LL | fn _func1<'a>(_x: Box=&'a ()>>) {} - | ^^^^^^^^^^^^^^^^^^^ `X` cannot be made into an object - | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/issue-67510-pass.rs:9:10 - | -LL | trait X { - | - this trait cannot be made into an object... -LL | type Y<'a>; - | ^ ...because it contains the generic associated type `Y` - = help: consider moving `Y` to another trait - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/issue-67510-pass.rs b/tests/ui/generic-associated-types/issue-67510-pass.rs index a48d9c37cd4f4..2bfba7f6942fb 100644 --- a/tests/ui/generic-associated-types/issue-67510-pass.rs +++ b/tests/ui/generic-associated-types/issue-67510-pass.rs @@ -5,6 +5,6 @@ trait X { } fn _func1<'a>(_x: Box=&'a ()>>) {} -//~^ ERROR the trait `X` cannot be made into an object +//~^ ERROR the trait `X` is not dyn compatible fn main() {} diff --git a/tests/ui/generic-associated-types/issue-67510-pass.stderr b/tests/ui/generic-associated-types/issue-67510-pass.stderr index 5560cb0f64df2..4b56c4ef35f45 100644 --- a/tests/ui/generic-associated-types/issue-67510-pass.stderr +++ b/tests/ui/generic-associated-types/issue-67510-pass.stderr @@ -1,14 +1,15 @@ -error[E0038]: the trait `X` cannot be made into an object +error[E0038]: the trait `X` is not dyn compatible --> $DIR/issue-67510-pass.rs:7:23 | LL | fn _func1<'a>(_x: Box=&'a ()>>) {} - | ^^^^^^^^^^^^^^^^^^^ `X` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^ `X` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-67510-pass.rs:4:10 | LL | trait X { - | - this trait cannot be made into an object... + | - this trait is not dyn compatible... LL | type Y<'a>; | ^ ...because it contains the generic associated type `Y` = help: consider moving `Y` to another trait diff --git a/tests/ui/generic-associated-types/issue-67510.rs b/tests/ui/generic-associated-types/issue-67510.rs index ab5c25d74da73..5c3150a77ed1b 100644 --- a/tests/ui/generic-associated-types/issue-67510.rs +++ b/tests/ui/generic-associated-types/issue-67510.rs @@ -5,6 +5,6 @@ trait X { fn f(x: Box = &'a ()>>) {} //~^ ERROR: use of undeclared lifetime name `'a` //~| ERROR: use of undeclared lifetime name `'a` -//~| ERROR: the trait `X` cannot be made into an object [E0038] +//~| ERROR: the trait `X` is not dyn compatible [E0038] fn main() {} diff --git a/tests/ui/generic-associated-types/issue-67510.stderr b/tests/ui/generic-associated-types/issue-67510.stderr index 416f04ac2fd6c..f5c494788ea58 100644 --- a/tests/ui/generic-associated-types/issue-67510.stderr +++ b/tests/ui/generic-associated-types/issue-67510.stderr @@ -29,17 +29,18 @@ help: consider introducing lifetime `'a` here LL | fn f<'a>(x: Box = &'a ()>>) {} | ++++ -error[E0038]: the trait `X` cannot be made into an object +error[E0038]: the trait `X` is not dyn compatible --> $DIR/issue-67510.rs:5:13 | LL | fn f(x: Box = &'a ()>>) {} - | ^^^^^^^^^^^^^^^^^^^^^ `X` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^^ `X` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-67510.rs:2:10 | LL | trait X { - | - this trait cannot be made into an object... + | - this trait is not dyn compatible... LL | type Y<'a>; | ^ ...because it contains the generic associated type `Y` = help: consider moving `Y` to another trait diff --git a/tests/ui/generic-associated-types/issue-70304.stderr b/tests/ui/generic-associated-types/issue-70304.stderr index 9b02c1b076837..a181b1035b7c3 100644 --- a/tests/ui/generic-associated-types/issue-70304.stderr +++ b/tests/ui/generic-associated-types/issue-70304.stderr @@ -13,8 +13,9 @@ LL | fn create_doc() -> impl Document = DocCursorImpl<'_>> { = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values | -LL | fn create_doc() -> impl Document = DocCursorImpl<'static>> { - | ~~~~~~~ +LL - fn create_doc() -> impl Document = DocCursorImpl<'_>> { +LL + fn create_doc() -> impl Document = DocCursorImpl<'static>> { + | error: missing required bound on `Cursor` --> $DIR/issue-70304.rs:2:5 diff --git a/tests/ui/generic-associated-types/issue-71176.rs b/tests/ui/generic-associated-types/issue-71176.rs index 7fffe312f4b7b..d3a0caffec1e7 100644 --- a/tests/ui/generic-associated-types/issue-71176.rs +++ b/tests/ui/generic-associated-types/issue-71176.rs @@ -11,13 +11,13 @@ struct Holder { //~^ ERROR: missing generics for associated type //~| ERROR: missing generics for associated type //~| ERROR: missing generics for associated type - //~| ERROR: the trait `Provider` cannot be made into an object + //~| ERROR: the trait `Provider` is not dyn compatible } fn main() { Holder { inner: Box::new(()), - //~^ ERROR: the trait `Provider` cannot be made into an object - //~| ERROR: the trait `Provider` cannot be made into an object + //~^ ERROR: the trait `Provider` is not dyn compatible + //~| ERROR: the trait `Provider` is not dyn compatible }; } diff --git a/tests/ui/generic-associated-types/issue-71176.stderr b/tests/ui/generic-associated-types/issue-71176.stderr index 1cd2ed0d313dc..56439f6dfea84 100644 --- a/tests/ui/generic-associated-types/issue-71176.stderr +++ b/tests/ui/generic-associated-types/issue-71176.stderr @@ -48,53 +48,56 @@ help: add missing lifetime argument LL | inner: Box = B>>, | ++++ -error[E0038]: the trait `Provider` cannot be made into an object +error[E0038]: the trait `Provider` is not dyn compatible --> $DIR/issue-71176.rs:10:14 | LL | inner: Box>, - | ^^^^^^^^^^^^^^^^^^^ `Provider` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^ `Provider` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-71176.rs:2:10 | LL | trait Provider { - | -------- this trait cannot be made into an object... + | -------- this trait is not dyn compatible... LL | type A<'a>; | ^ ...because it contains the generic associated type `A` = help: consider moving `A` to another trait - = help: only type `()` implements the trait, consider using it directly instead + = help: only type `()` implements `Provider`; consider using it directly instead. -error[E0038]: the trait `Provider` cannot be made into an object +error[E0038]: the trait `Provider` is not dyn compatible --> $DIR/issue-71176.rs:19:16 | LL | inner: Box::new(()), - | ^^^^^^^^^^^^ `Provider` cannot be made into an object + | ^^^^^^^^^^^^ `Provider` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-71176.rs:2:10 | LL | trait Provider { - | -------- this trait cannot be made into an object... + | -------- this trait is not dyn compatible... LL | type A<'a>; | ^ ...because it contains the generic associated type `A` = help: consider moving `A` to another trait - = help: only type `()` implements the trait, consider using it directly instead + = help: only type `()` implements `Provider`; consider using it directly instead. -error[E0038]: the trait `Provider` cannot be made into an object +error[E0038]: the trait `Provider` is not dyn compatible --> $DIR/issue-71176.rs:19:16 | LL | inner: Box::new(()), - | ^^^^^^^^^^^^ `Provider` cannot be made into an object + | ^^^^^^^^^^^^ `Provider` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-71176.rs:2:10 | LL | trait Provider { - | -------- this trait cannot be made into an object... + | -------- this trait is not dyn compatible... LL | type A<'a>; | ^ ...because it contains the generic associated type `A` = help: consider moving `A` to another trait - = help: only type `()` implements the trait, consider using it directly instead + = help: only type `()` implements `Provider`; consider using it directly instead. = note: required for the cast from `Box<()>` to `Box<(dyn Provider = _> + 'static), {type error}>` error: aborting due to 6 previous errors diff --git a/tests/ui/generic-associated-types/issue-76535.base.stderr b/tests/ui/generic-associated-types/issue-76535.base.stderr deleted file mode 100644 index a44c8dc51e7e9..0000000000000 --- a/tests/ui/generic-associated-types/issue-76535.base.stderr +++ /dev/null @@ -1,55 +0,0 @@ -error[E0107]: missing generics for associated type `SuperTrait::SubType` - --> $DIR/issue-76535.rs:39:33 - | -LL | let sub: Box> = Box::new(SuperStruct::new(0)); - | ^^^^^^^ expected 1 lifetime argument - | -note: associated type defined here, with 1 lifetime parameter: `'a` - --> $DIR/issue-76535.rs:9:10 - | -LL | type SubType<'a>: SubTrait where Self: 'a; - | ^^^^^^^ -- -help: add missing lifetime argument - | -LL | let sub: Box = SubStruct>> = Box::new(SuperStruct::new(0)); - | ++++ - -error[E0038]: the trait `SuperTrait` cannot be made into an object - --> $DIR/issue-76535.rs:39:14 - | -LL | let sub: Box> = Box::new(SuperStruct::new(0)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `SuperTrait` cannot be made into an object - | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/issue-76535.rs:9:10 - | -LL | pub trait SuperTrait { - | ---------- this trait cannot be made into an object... -LL | type SubType<'a>: SubTrait where Self: 'a; - | ^^^^^^^ ...because it contains the generic associated type `SubType` - = help: consider moving `SubType` to another trait - = help: only type `SuperStruct` is seen to implement the trait in this crate, consider using it directly instead - = note: `SuperTrait` can be implemented in other crates; if you want to support your users passing their own types here, you can't refer to a specific type - -error[E0038]: the trait `SuperTrait` cannot be made into an object - --> $DIR/issue-76535.rs:39:57 - | -LL | let sub: Box> = Box::new(SuperStruct::new(0)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `SuperTrait` cannot be made into an object - | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/issue-76535.rs:9:10 - | -LL | pub trait SuperTrait { - | ---------- this trait cannot be made into an object... -LL | type SubType<'a>: SubTrait where Self: 'a; - | ^^^^^^^ ...because it contains the generic associated type `SubType` - = help: consider moving `SubType` to another trait - = help: only type `SuperStruct` is seen to implement the trait in this crate, consider using it directly instead - = note: `SuperTrait` can be implemented in other crates; if you want to support your users passing their own types here, you can't refer to a specific type - = note: required for the cast from `Box` to `Box = SubStruct<'_>>>` - -error: aborting due to 3 previous errors - -Some errors have detailed explanations: E0038, E0107. -For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/issue-76535.extended.stderr b/tests/ui/generic-associated-types/issue-76535.extended.stderr deleted file mode 100644 index f6fe8b16902b1..0000000000000 --- a/tests/ui/generic-associated-types/issue-76535.extended.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0107]: missing generics for associated type `SuperTrait::SubType` - --> $DIR/issue-76535.rs:39:33 - | -LL | let sub: Box> = Box::new(SuperStruct::new(0)); - | ^^^^^^^ expected 1 lifetime argument - | -note: associated type defined here, with 1 lifetime parameter: `'a` - --> $DIR/issue-76535.rs:9:10 - | -LL | type SubType<'a>: SubTrait where Self: 'a; - | ^^^^^^^ -- -help: add missing lifetime argument - | -LL | let sub: Box = SubStruct>> = Box::new(SuperStruct::new(0)); - | ++++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0107`. diff --git a/tests/ui/generic-associated-types/issue-76535.stderr b/tests/ui/generic-associated-types/issue-76535.stderr index 613ded6f1ef10..b828234afa126 100644 --- a/tests/ui/generic-associated-types/issue-76535.stderr +++ b/tests/ui/generic-associated-types/issue-76535.stderr @@ -14,39 +14,41 @@ help: add missing lifetime argument LL | let sub: Box = SubStruct>> = Box::new(SuperStruct::new(0)); | ++++ -error[E0038]: the trait `SuperTrait` cannot be made into an object +error[E0038]: the trait `SuperTrait` is not dyn compatible --> $DIR/issue-76535.rs:34:14 | LL | let sub: Box> = Box::new(SuperStruct::new(0)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `SuperTrait` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `SuperTrait` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-76535.rs:4:10 | LL | pub trait SuperTrait { - | ---------- this trait cannot be made into an object... + | ---------- this trait is not dyn compatible... LL | type SubType<'a>: SubTrait where Self: 'a; | ^^^^^^^ ...because it contains the generic associated type `SubType` = help: consider moving `SubType` to another trait - = help: only type `SuperStruct` is seen to implement the trait in this crate, consider using it directly instead - = note: `SuperTrait` can be implemented in other crates; if you want to support your users passing their own types here, you can't refer to a specific type + = help: only type `SuperStruct` implements `SuperTrait` within this crate; consider using it directly instead. + = note: `SuperTrait` may be implemented in other crates; if you want to support your users passing their own types here, you can't refer to a specific type -error[E0038]: the trait `SuperTrait` cannot be made into an object +error[E0038]: the trait `SuperTrait` is not dyn compatible --> $DIR/issue-76535.rs:34:57 | LL | let sub: Box> = Box::new(SuperStruct::new(0)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `SuperTrait` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `SuperTrait` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-76535.rs:4:10 | LL | pub trait SuperTrait { - | ---------- this trait cannot be made into an object... + | ---------- this trait is not dyn compatible... LL | type SubType<'a>: SubTrait where Self: 'a; | ^^^^^^^ ...because it contains the generic associated type `SubType` = help: consider moving `SubType` to another trait - = help: only type `SuperStruct` is seen to implement the trait in this crate, consider using it directly instead - = note: `SuperTrait` can be implemented in other crates; if you want to support your users passing their own types here, you can't refer to a specific type + = help: only type `SuperStruct` implements `SuperTrait` within this crate; consider using it directly instead. + = note: `SuperTrait` may be implemented in other crates; if you want to support your users passing their own types here, you can't refer to a specific type = note: required for the cast from `Box` to `Box = SubStruct<'_>>>` error: aborting due to 3 previous errors diff --git a/tests/ui/generic-associated-types/issue-78671.base.stderr b/tests/ui/generic-associated-types/issue-78671.base.stderr deleted file mode 100644 index 9f2be785460f3..0000000000000 --- a/tests/ui/generic-associated-types/issue-78671.base.stderr +++ /dev/null @@ -1,35 +0,0 @@ -error[E0107]: missing generics for associated type `CollectionFamily::Member` - --> $DIR/issue-78671.rs:10:47 - | -LL | Box::new(Family) as &dyn CollectionFamily - | ^^^^^^ expected 1 generic argument - | -note: associated type defined here, with 1 generic parameter: `T` - --> $DIR/issue-78671.rs:7:10 - | -LL | type Member; - | ^^^^^^ - -help: add missing generic argument - | -LL | Box::new(Family) as &dyn CollectionFamily=usize> - | +++ - -error[E0038]: the trait `CollectionFamily` cannot be made into an object - --> $DIR/issue-78671.rs:10:25 - | -LL | Box::new(Family) as &dyn CollectionFamily - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `CollectionFamily` cannot be made into an object - | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/issue-78671.rs:7:10 - | -LL | trait CollectionFamily { - | ---------------- this trait cannot be made into an object... -LL | type Member; - | ^^^^^^ ...because it contains the generic associated type `Member` - = help: consider moving `Member` to another trait - -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0038, E0107. -For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/issue-78671.extended.stderr b/tests/ui/generic-associated-types/issue-78671.extended.stderr deleted file mode 100644 index a5d56256d283b..0000000000000 --- a/tests/ui/generic-associated-types/issue-78671.extended.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0107]: missing generics for associated type `CollectionFamily::Member` - --> $DIR/issue-78671.rs:10:47 - | -LL | Box::new(Family) as &dyn CollectionFamily - | ^^^^^^ expected 1 generic argument - | -note: associated type defined here, with 1 generic parameter: `T` - --> $DIR/issue-78671.rs:7:10 - | -LL | type Member; - | ^^^^^^ - -help: add missing generic argument - | -LL | Box::new(Family) as &dyn CollectionFamily=usize> - | +++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0107`. diff --git a/tests/ui/generic-associated-types/issue-78671.rs b/tests/ui/generic-associated-types/issue-78671.rs index 0871def173130..1e1863799a523 100644 --- a/tests/ui/generic-associated-types/issue-78671.rs +++ b/tests/ui/generic-associated-types/issue-78671.rs @@ -4,7 +4,7 @@ trait CollectionFamily { fn floatify() { Box::new(Family) as &dyn CollectionFamily //~^ ERROR: missing generics for associated type - //~| ERROR: the trait `CollectionFamily` cannot be made into an object + //~| ERROR: the trait `CollectionFamily` is not dyn compatible } struct Family; diff --git a/tests/ui/generic-associated-types/issue-78671.stderr b/tests/ui/generic-associated-types/issue-78671.stderr index fbd76c73895a6..c6da137672de9 100644 --- a/tests/ui/generic-associated-types/issue-78671.stderr +++ b/tests/ui/generic-associated-types/issue-78671.stderr @@ -14,17 +14,18 @@ help: add missing generic argument LL | Box::new(Family) as &dyn CollectionFamily=usize> | +++ -error[E0038]: the trait `CollectionFamily` cannot be made into an object +error[E0038]: the trait `CollectionFamily` is not dyn compatible --> $DIR/issue-78671.rs:5:25 | LL | Box::new(Family) as &dyn CollectionFamily - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `CollectionFamily` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `CollectionFamily` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-78671.rs:2:10 | LL | trait CollectionFamily { - | ---------------- this trait cannot be made into an object... + | ---------------- this trait is not dyn compatible... LL | type Member; | ^^^^^^ ...because it contains the generic associated type `Member` = help: consider moving `Member` to another trait diff --git a/tests/ui/generic-associated-types/issue-79422.base.stderr b/tests/ui/generic-associated-types/issue-79422.base.stderr deleted file mode 100644 index 3ea62bdbb2763..0000000000000 --- a/tests/ui/generic-associated-types/issue-79422.base.stderr +++ /dev/null @@ -1,57 +0,0 @@ -error[E0107]: missing generics for associated type `MapLike::VRefCont` - --> $DIR/issue-79422.rs:47:36 - | -LL | as Box>>; - | ^^^^^^^^ expected 1 lifetime argument - | -note: associated type defined here, with 1 lifetime parameter: `'a` - --> $DIR/issue-79422.rs:23:10 - | -LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a; - | ^^^^^^^^ -- -help: add missing lifetime argument - | -LL | as Box = dyn RefCont<'_, u8>>>; - | ++++ - -error[E0038]: the trait `MapLike` cannot be made into an object - --> $DIR/issue-79422.rs:47:12 - | -LL | as Box>>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `MapLike` cannot be made into an object - | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/issue-79422.rs:23:10 - | -LL | trait MapLike { - | ------- this trait cannot be made into an object... -LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a; - | ^^^^^^^^ ...because it contains the generic associated type `VRefCont` - = help: consider moving `VRefCont` to another trait - = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `MapLike` for this new enum and using it instead: - std::collections::BTreeMap - Source - -error[E0038]: the trait `MapLike` cannot be made into an object - --> $DIR/issue-79422.rs:44:13 - | -LL | let m = Box::new(std::collections::BTreeMap::::new()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `MapLike` cannot be made into an object - | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/issue-79422.rs:23:10 - | -LL | trait MapLike { - | ------- this trait cannot be made into an object... -LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a; - | ^^^^^^^^ ...because it contains the generic associated type `VRefCont` - = help: consider moving `VRefCont` to another trait - = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `MapLike` for this new enum and using it instead: - std::collections::BTreeMap - Source - = note: required for the cast from `Box>` to `Box = (dyn RefCont<'_, u8> + 'static)>>` - -error: aborting due to 3 previous errors - -Some errors have detailed explanations: E0038, E0107. -For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/issue-79422.extended.stderr b/tests/ui/generic-associated-types/issue-79422.extended.stderr deleted file mode 100644 index 031f8d8d851a1..0000000000000 --- a/tests/ui/generic-associated-types/issue-79422.extended.stderr +++ /dev/null @@ -1,36 +0,0 @@ -error[E0107]: missing generics for associated type `MapLike::VRefCont` - --> $DIR/issue-79422.rs:47:36 - | -LL | as Box>>; - | ^^^^^^^^ expected 1 lifetime argument - | -note: associated type defined here, with 1 lifetime parameter: `'a` - --> $DIR/issue-79422.rs:23:10 - | -LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a; - | ^^^^^^^^ -- -help: add missing lifetime argument - | -LL | as Box = dyn RefCont<'_, u8>>>; - | ++++ - -error[E0271]: type mismatch resolving ` as MapLike>::VRefCont<'_> == dyn RefCont<'_, u8>` - --> $DIR/issue-79422.rs:44:13 - | -LL | let m = Box::new(std::collections::BTreeMap::::new()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type mismatch resolving ` as MapLike>::VRefCont<'_> == dyn RefCont<'_, u8>` - | -note: expected this to be `(dyn RefCont<'_, u8> + 'static)` - --> $DIR/issue-79422.rs:28:25 - | -LL | type VRefCont<'a> = &'a V where Self: 'a; - | ^^^^^ - = note: expected trait object `(dyn RefCont<'_, u8> + 'static)` - found reference `&u8` - = help: `&u8` implements `RefCont` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well - = note: required for the cast from `Box>` to `Box = (dyn RefCont<'_, u8> + 'static)>>` - -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0107, E0271. -For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/generic-associated-types/issue-79422.stderr b/tests/ui/generic-associated-types/issue-79422.stderr index 26567e5e927d1..6311e4de272d9 100644 --- a/tests/ui/generic-associated-types/issue-79422.stderr +++ b/tests/ui/generic-associated-types/issue-79422.stderr @@ -14,41 +14,37 @@ help: add missing lifetime argument LL | as Box = dyn RefCont<'_, u8>>>; | ++++ -error[E0038]: the trait `MapLike` cannot be made into an object +error[E0038]: the trait `MapLike` is not dyn compatible --> $DIR/issue-79422.rs:41:12 | LL | as Box>>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `MapLike` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `MapLike` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-79422.rs:18:10 | LL | trait MapLike { - | ------- this trait cannot be made into an object... + | ------- this trait is not dyn compatible... LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a; | ^^^^^^^^ ...because it contains the generic associated type `VRefCont` = help: consider moving `VRefCont` to another trait - = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `MapLike` for this new enum and using it instead: - std::collections::BTreeMap - Source -error[E0038]: the trait `MapLike` cannot be made into an object +error[E0038]: the trait `MapLike` is not dyn compatible --> $DIR/issue-79422.rs:39:13 | LL | let m = Box::new(std::collections::BTreeMap::::new()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `MapLike` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `MapLike` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-79422.rs:18:10 | LL | trait MapLike { - | ------- this trait cannot be made into an object... + | ------- this trait is not dyn compatible... LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a; | ^^^^^^^^ ...because it contains the generic associated type `VRefCont` = help: consider moving `VRefCont` to another trait - = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `MapLike` for this new enum and using it instead: - std::collections::BTreeMap - Source = note: required for the cast from `Box>` to `Box = (dyn RefCont<'_, u8> + 'static)>>` error: aborting due to 3 previous errors diff --git a/tests/ui/generic-associated-types/issue-90014-tait2.next-solver.stderr b/tests/ui/generic-associated-types/issue-90014-tait2.next-solver.stderr deleted file mode 100644 index 85c5dad7fc01a..0000000000000 --- a/tests/ui/generic-associated-types/issue-90014-tait2.next-solver.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/issue-90014-tait2.rs:27:9 - | -LL | fn make_fut(&self) -> Box Trait<'a, Thing = Fut<'a>>> { - | ------------------------------------------- expected `Box<(dyn for<'a> Trait<'a, Thing = Fut<'a>> + 'static)>` because of return type -LL | Box::new((async { () },)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Box>>`, found `Box<(...,)>` - | - = note: expected struct `Box<(dyn for<'a> Trait<'a, Thing = Fut<'a>> + 'static)>` - found struct `Box<({async block@$DIR/issue-90014-tait2.rs:27:19: 27:31},)>` - = help: `({async block@$DIR/issue-90014-tait2.rs:27:19: 27:31},)` implements `Trait` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/generic-associated-types/issue-90014-tait2.rs b/tests/ui/generic-associated-types/issue-90014-tait2.rs index ef54a89aaae58..3f7a9ff63c312 100644 --- a/tests/ui/generic-associated-types/issue-90014-tait2.rs +++ b/tests/ui/generic-associated-types/issue-90014-tait2.rs @@ -3,8 +3,6 @@ //! Unfortunately we don't even reach opaque type collection, as we ICE in typeck before that. //! See #109281 for the original report. //@ edition:2018 -//@ error-pattern: expected generic lifetime parameter, found `'a` - #![feature(type_alias_impl_trait)] use std::future::Future; @@ -24,6 +22,7 @@ impl<'x, T: 'x> Trait<'x> for (T,) { impl Foo<'_> { fn make_fut(&self) -> Box Trait<'a, Thing = Fut<'a>>> { Box::new((async { () },)) + //~^ ERROR expected generic lifetime parameter, found `'a` } } diff --git a/tests/ui/generic-associated-types/issue-90014-tait2.stderr b/tests/ui/generic-associated-types/issue-90014-tait2.stderr index be6f4272ce18c..aa427d42649ae 100644 --- a/tests/ui/generic-associated-types/issue-90014-tait2.stderr +++ b/tests/ui/generic-associated-types/issue-90014-tait2.stderr @@ -1,5 +1,5 @@ error[E0792]: expected generic lifetime parameter, found `'a` - --> $DIR/issue-90014-tait2.rs:26:9 + --> $DIR/issue-90014-tait2.rs:24:9 | LL | type Fut<'a> = impl Future; | -- this generic parameter must be used with a generic lifetime parameter diff --git a/tests/ui/generic-associated-types/issue-91139.migrate.stderr b/tests/ui/generic-associated-types/issue-91139.migrate.stderr deleted file mode 100644 index e3b658558e328..0000000000000 --- a/tests/ui/generic-associated-types/issue-91139.migrate.stderr +++ /dev/null @@ -1,7 +0,0 @@ -error: expected identifier, found `<<` - --> $DIR/issue-91139.rs:1:1 - | - | ^^ expected identifier - -error: aborting due to 1 previous error - diff --git a/tests/ui/generic-associated-types/issue-92096.migrate.stderr b/tests/ui/generic-associated-types/issue-92096.migrate.stderr deleted file mode 100644 index ce1fd6dd9831f..0000000000000 --- a/tests/ui/generic-associated-types/issue-92096.migrate.stderr +++ /dev/null @@ -1,24 +0,0 @@ -error[E0311]: the parameter type `C` may not live long enough - --> $DIR/issue-92096.rs:19:33 - | -LL | fn call_connect(c: &'_ C) -> impl '_ + Future + Send - | ^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `C` will meet its required lifetime bounds - | -help: consider adding an explicit lifetime bound... - | -LL | C: Client + Send + Sync + 'a, - | ++++ - -error[E0311]: the parameter type `C` may not live long enough - --> $DIR/issue-92096.rs:19:33 - | -LL | fn call_connect(c: &'_ C) -> impl '_ + Future + Send - | ^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `C` will meet its required lifetime bounds - | -help: consider adding an explicit lifetime bound... - | -LL | C: Client + Send + Sync + 'a, - | ++++ - -error: aborting due to 2 previous errors - diff --git a/tests/ui/generic-associated-types/mismatched-where-clause-regions.stderr b/tests/ui/generic-associated-types/mismatched-where-clause-regions.stderr index ee90f61aabc74..9ff343c255b7a 100644 --- a/tests/ui/generic-associated-types/mismatched-where-clause-regions.stderr +++ b/tests/ui/generic-associated-types/mismatched-where-clause-regions.stderr @@ -9,8 +9,9 @@ LL | type T<'a2, 'b2> = () where 'b2: 'a2; | help: copy the `where` clause predicates from the trait | -LL | type T<'a2, 'b2> = () where 'a2: 'b2; - | ~~~~~~~~~~~~~~ +LL - type T<'a2, 'b2> = () where 'b2: 'a2; +LL + type T<'a2, 'b2> = () where 'a2: 'b2; + | error: aborting due to 1 previous error diff --git a/tests/ui/generic-associated-types/missing-bounds.stderr b/tests/ui/generic-associated-types/missing-bounds.stderr index 6e0700639e9c0..13e4d2498760e 100644 --- a/tests/ui/generic-associated-types/missing-bounds.stderr +++ b/tests/ui/generic-associated-types/missing-bounds.stderr @@ -7,8 +7,9 @@ LL | impl Add for E where ::Output = B { = note: see issue #20041 for more information help: if `Output` is an associated type you're trying to set, use the associated type binding syntax | -LL | impl Add for E where B: Add { - | ~~~~~~~~~~~~~~~~~~ +LL - impl Add for E where ::Output = B { +LL + impl Add for E where B: Add { + | error[E0308]: mismatched types --> $DIR/missing-bounds.rs:11:11 diff --git a/tests/ui/generic-associated-types/missing_lifetime_args.rs b/tests/ui/generic-associated-types/missing_lifetime_args.rs index 470db5412b281..e0f2db5eb21ed 100644 --- a/tests/ui/generic-associated-types/missing_lifetime_args.rs +++ b/tests/ui/generic-associated-types/missing_lifetime_args.rs @@ -12,7 +12,7 @@ fn foo<'c, 'd>(_arg: Box>) {} //~^ ERROR missing generics for associated type //~| ERROR missing generics for associated type //~| ERROR missing generics for associated type -//~| ERROR the trait `X` cannot be made into an object +//~| ERROR the trait `X` is not dyn compatible fn bar<'a, 'b, 'c>(_arg: Foo<'a, 'b>) {} //~^ ERROR struct takes 3 lifetime arguments but 2 lifetime diff --git a/tests/ui/generic-associated-types/missing_lifetime_args.stderr b/tests/ui/generic-associated-types/missing_lifetime_args.stderr index 61cf4f3dd4a7e..7b6888817f4f4 100644 --- a/tests/ui/generic-associated-types/missing_lifetime_args.stderr +++ b/tests/ui/generic-associated-types/missing_lifetime_args.stderr @@ -48,17 +48,18 @@ help: add missing lifetime arguments LL | fn foo<'c, 'd>(_arg: Box = (&'c u32, &'d u32)>>) {} | ++++++++ -error[E0038]: the trait `X` cannot be made into an object +error[E0038]: the trait `X` is not dyn compatible --> $DIR/missing_lifetime_args.rs:11:26 | LL | fn foo<'c, 'd>(_arg: Box>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `X` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `X` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/missing_lifetime_args.rs:2:10 | LL | trait X { - | - this trait cannot be made into an object... + | - this trait is not dyn compatible... LL | type Y<'a, 'b>; | ^ ...because it contains the generic associated type `Y` = help: consider moving `Y` to another trait diff --git a/tests/ui/generic-associated-types/parse/trait-path-type-error-once-implemented.rs b/tests/ui/generic-associated-types/parse/trait-path-type-error-once-implemented.rs index d6fc3df1026f6..c828691bb3058 100644 --- a/tests/ui/generic-associated-types/parse/trait-path-type-error-once-implemented.rs +++ b/tests/ui/generic-associated-types/parse/trait-path-type-error-once-implemented.rs @@ -10,7 +10,7 @@ const _: () = { //~| ERROR associated type takes 0 generic arguments but 1 generic argument //~| ERROR associated type takes 1 lifetime argument but 0 lifetime arguments //~| ERROR associated type takes 0 generic arguments but 1 generic argument - //~| ERROR the trait `X` cannot be made into an object + //~| ERROR the trait `X` is not dyn compatible }; fn main() {} diff --git a/tests/ui/generic-associated-types/parse/trait-path-type-error-once-implemented.stderr b/tests/ui/generic-associated-types/parse/trait-path-type-error-once-implemented.stderr index 91f0f7b3fcf2d..45bca1a76287d 100644 --- a/tests/ui/generic-associated-types/parse/trait-path-type-error-once-implemented.stderr +++ b/tests/ui/generic-associated-types/parse/trait-path-type-error-once-implemented.stderr @@ -92,17 +92,18 @@ LL | type Y<'a>; | ^ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0038]: the trait `X` cannot be made into an object +error[E0038]: the trait `X` is not dyn compatible --> $DIR/trait-path-type-error-once-implemented.rs:6:23 | LL | fn f2<'a>(arg : Box = &'a ()>>) {} - | ^^^^^^^^^^^^^^^^^^^^ `X` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^ `X` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/trait-path-type-error-once-implemented.rs:2:10 | LL | trait X { - | - this trait cannot be made into an object... + | - this trait is not dyn compatible... LL | type Y<'a>; | ^ ...because it contains the generic associated type `Y` = help: consider moving `Y` to another trait diff --git a/tests/ui/generic-associated-types/trait-objects.base.stderr b/tests/ui/generic-associated-types/trait-objects.base.stderr deleted file mode 100644 index 0b5a9b9f7fb64..0000000000000 --- a/tests/ui/generic-associated-types/trait-objects.base.stderr +++ /dev/null @@ -1,48 +0,0 @@ -error[E0038]: the trait `StreamingIterator` cannot be made into an object - --> $DIR/trait-objects.rs:13:21 - | -LL | fn min_size(x: &mut dyn for<'a> StreamingIterator = &'a i32>) -> usize { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `StreamingIterator` cannot be made into an object - | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/trait-objects.rs:7:10 - | -LL | trait StreamingIterator { - | ----------------- this trait cannot be made into an object... -LL | type Item<'a> where Self: 'a; - | ^^^^ ...because it contains the generic associated type `Item` - = help: consider moving `Item` to another trait - -error[E0038]: the trait `StreamingIterator` cannot be made into an object - --> $DIR/trait-objects.rs:15:7 - | -LL | x.size_hint().0 - | ^^^^^^^^^ `StreamingIterator` cannot be made into an object - | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/trait-objects.rs:7:10 - | -LL | trait StreamingIterator { - | ----------------- this trait cannot be made into an object... -LL | type Item<'a> where Self: 'a; - | ^^^^ ...because it contains the generic associated type `Item` - = help: consider moving `Item` to another trait - -error[E0038]: the trait `StreamingIterator` cannot be made into an object - --> $DIR/trait-objects.rs:15:5 - | -LL | x.size_hint().0 - | ^^^^^^^^^^^^^ `StreamingIterator` cannot be made into an object - | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit - --> $DIR/trait-objects.rs:7:10 - | -LL | trait StreamingIterator { - | ----------------- this trait cannot be made into an object... -LL | type Item<'a> where Self: 'a; - | ^^^^ ...because it contains the generic associated type `Item` - = help: consider moving `Item` to another trait - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/trait-objects.extended.stderr b/tests/ui/generic-associated-types/trait-objects.extended.stderr deleted file mode 100644 index 9f9418e20b9dc..0000000000000 --- a/tests/ui/generic-associated-types/trait-objects.extended.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0521]: borrowed data escapes outside of function - --> $DIR/trait-objects.rs:15:5 - | -LL | fn min_size(x: &mut dyn for<'a> StreamingIterator = &'a i32>) -> usize { - | - - let's call the lifetime of this reference `'1` - | | - | `x` is a reference that is only valid in the function body -LL | -LL | x.size_hint().0 - | ^^^^^^^^^^^^^ - | | - | `x` escapes the function body here - | argument requires that `'1` must outlive `'static` - | - = note: due to current limitations in the borrow checker, this implies a `'static` lifetime - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/generic-associated-types/trait-objects.rs b/tests/ui/generic-associated-types/trait-objects.rs index bad9289ee5eec..ed324b562e1c8 100644 --- a/tests/ui/generic-associated-types/trait-objects.rs +++ b/tests/ui/generic-associated-types/trait-objects.rs @@ -6,10 +6,10 @@ trait StreamingIterator { } fn min_size(x: &mut dyn for<'a> StreamingIterator = &'a i32>) -> usize { - //~^ the trait `StreamingIterator` cannot be made into an object + //~^ the trait `StreamingIterator` is not dyn compatible x.size_hint().0 - //~^ the trait `StreamingIterator` cannot be made into an object - //~| the trait `StreamingIterator` cannot be made into an object + //~^ the trait `StreamingIterator` is not dyn compatible + //~| the trait `StreamingIterator` is not dyn compatible } fn main() {} diff --git a/tests/ui/generic-associated-types/trait-objects.stderr b/tests/ui/generic-associated-types/trait-objects.stderr index 3e74776f999ac..7d95718ec874b 100644 --- a/tests/ui/generic-associated-types/trait-objects.stderr +++ b/tests/ui/generic-associated-types/trait-objects.stderr @@ -1,44 +1,47 @@ -error[E0038]: the trait `StreamingIterator` cannot be made into an object +error[E0038]: the trait `StreamingIterator` is not dyn compatible --> $DIR/trait-objects.rs:8:21 | LL | fn min_size(x: &mut dyn for<'a> StreamingIterator = &'a i32>) -> usize { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `StreamingIterator` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `StreamingIterator` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/trait-objects.rs:2:10 | LL | trait StreamingIterator { - | ----------------- this trait cannot be made into an object... + | ----------------- this trait is not dyn compatible... LL | type Item<'a> where Self: 'a; | ^^^^ ...because it contains the generic associated type `Item` = help: consider moving `Item` to another trait -error[E0038]: the trait `StreamingIterator` cannot be made into an object +error[E0038]: the trait `StreamingIterator` is not dyn compatible --> $DIR/trait-objects.rs:10:7 | LL | x.size_hint().0 - | ^^^^^^^^^ `StreamingIterator` cannot be made into an object + | ^^^^^^^^^ `StreamingIterator` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/trait-objects.rs:2:10 | LL | trait StreamingIterator { - | ----------------- this trait cannot be made into an object... + | ----------------- this trait is not dyn compatible... LL | type Item<'a> where Self: 'a; | ^^^^ ...because it contains the generic associated type `Item` = help: consider moving `Item` to another trait -error[E0038]: the trait `StreamingIterator` cannot be made into an object +error[E0038]: the trait `StreamingIterator` is not dyn compatible --> $DIR/trait-objects.rs:10:5 | LL | x.size_hint().0 - | ^^^^^^^^^^^^^ `StreamingIterator` cannot be made into an object + | ^^^^^^^^^^^^^ `StreamingIterator` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/trait-objects.rs:2:10 | LL | trait StreamingIterator { - | ----------------- this trait cannot be made into an object... + | ----------------- this trait is not dyn compatible... LL | type Item<'a> where Self: 'a; | ^^^^ ...because it contains the generic associated type `Item` = help: consider moving `Item` to another trait diff --git a/tests/ui/generic-const-items/assoc-const-missing-type.rs b/tests/ui/generic-const-items/assoc-const-missing-type.rs index 93160f0b5758d..0c94a4262ef47 100644 --- a/tests/ui/generic-const-items/assoc-const-missing-type.rs +++ b/tests/ui/generic-const-items/assoc-const-missing-type.rs @@ -12,7 +12,6 @@ impl Trait for () { const K = (); //~^ ERROR missing type for `const` item //~| ERROR mismatched types - //~| ERROR mismatched types const Q = ""; //~^ ERROR missing type for `const` item //~| ERROR lifetime parameters or bounds on const `Q` do not match the trait declaration diff --git a/tests/ui/generic-const-items/assoc-const-missing-type.stderr b/tests/ui/generic-const-items/assoc-const-missing-type.stderr index 6f35c0958d45c..5af119dffa7c8 100644 --- a/tests/ui/generic-const-items/assoc-const-missing-type.stderr +++ b/tests/ui/generic-const-items/assoc-const-missing-type.stderr @@ -16,7 +16,7 @@ LL | const K = (); | ^ help: provide a type for the associated constant: `()` error[E0195]: lifetime parameters or bounds on const `Q` do not match the trait declaration - --> $DIR/assoc-const-missing-type.rs:16:12 + --> $DIR/assoc-const-missing-type.rs:15:12 | LL | const Q<'a>: &'a str; | ---- lifetimes in impl do not match this const in trait @@ -25,24 +25,12 @@ LL | const Q = ""; | ^ lifetimes do not match const in trait error: missing type for `const` item - --> $DIR/assoc-const-missing-type.rs:16:12 + --> $DIR/assoc-const-missing-type.rs:15:12 | LL | const Q = ""; | ^ help: provide a type for the associated constant: `: &str` -error[E0308]: mismatched types - --> $DIR/assoc-const-missing-type.rs:12:18 - | -LL | const K = (); - | - ^^ expected type parameter `T`, found `()` - | | - | expected this type parameter - | - = note: expected type parameter `T` - found unit type `()` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0195, E0308. For more information about an error, try `rustc --explain E0195`. diff --git a/tests/ui/generic-const-items/def-site-eval.fail.stderr b/tests/ui/generic-const-items/def-site-eval.fail.stderr new file mode 100644 index 0000000000000..22a5f29169776 --- /dev/null +++ b/tests/ui/generic-const-items/def-site-eval.fail.stderr @@ -0,0 +1,11 @@ +error[E0080]: evaluation of `_::<'_>` failed + --> $DIR/def-site-eval.rs:14:20 + | +LL | const _<'_a>: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/def-site-eval.rs:14:20 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/generic-const-items/def-site-eval.rs b/tests/ui/generic-const-items/def-site-eval.rs new file mode 100644 index 0000000000000..3ed7f96aed02c --- /dev/null +++ b/tests/ui/generic-const-items/def-site-eval.rs @@ -0,0 +1,16 @@ +//! Test that we only evaluate free const items (their def site to be clear) +//! whose generics don't require monomorphization. +#![feature(generic_const_items)] +#![allow(incomplete_features)] + +//@ revisions: fail pass +//@[fail] build-fail (we require monomorphization) +//@[pass] build-pass (we require monomorphization) + +const _<_T>: () = panic!(); +const _: () = panic!(); + +#[cfg(fail)] +const _<'_a>: () = panic!(); //[fail]~ ERROR evaluation of `_::<'_>` failed + +fn main() {} diff --git a/tests/ui/generic-const-items/def-site-mono.rs b/tests/ui/generic-const-items/def-site-mono.rs new file mode 100644 index 0000000000000..f10d450f6bdb7 --- /dev/null +++ b/tests/ui/generic-const-items/def-site-mono.rs @@ -0,0 +1,13 @@ +//! Ensure that we don't try to collect monomorphizeable items inside free const +//! items (their def site to be clear) whose generics require monomorphization. +//! +//! Such items are to be collected at instantiation sites of free consts. + +#![feature(generic_const_items)] +#![allow(incomplete_features)] + +//@ build-pass (we require monomorphization) + +const _IDENTITY: fn(T) -> T = |x| x; + +fn main() {} diff --git a/tests/ui/generic-const-items/duplicate-where-clause.stderr b/tests/ui/generic-const-items/duplicate-where-clause.stderr index 5fa61b01ee9db..6b742dfafedaa 100644 --- a/tests/ui/generic-const-items/duplicate-where-clause.stderr +++ b/tests/ui/generic-const-items/duplicate-where-clause.stderr @@ -9,8 +9,10 @@ LL | P: Eq; | help: consider joining the two `where` clauses into one | -LL | P: Copy, - | ~ +LL - P: Copy +LL - where +LL + P: Copy, + | error: where clauses are not allowed before const item bodies --> $DIR/duplicate-where-clause.rs:19:5 diff --git a/tests/ui/generic-const-items/evaluatable-bounds.unconstrained.stderr b/tests/ui/generic-const-items/evaluatable-bounds.unconstrained.stderr deleted file mode 100644 index b6f9bdce1cb7a..0000000000000 --- a/tests/ui/generic-const-items/evaluatable-bounds.unconstrained.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error: unconstrained generic constant - --> $DIR/evaluatable-bounds.rs:16:5 - | -LL | const ARRAY: [i32; Self::LEN]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: try adding a `where` bound - | -LL | const ARRAY: [i32; Self::LEN] where [(); Self::LEN]:; - | ++++++++++++++++++++++ - -error: aborting due to 1 previous error - diff --git a/tests/ui/generic-const-items/recursive.rs b/tests/ui/generic-const-items/recursive.rs index 8244772168b34..94cf98ec64b25 100644 --- a/tests/ui/generic-const-items/recursive.rs +++ b/tests/ui/generic-const-items/recursive.rs @@ -1,12 +1,11 @@ -// FIXME(generic_const_items): This leads to a stack overflow in the compiler! -//@ known-bug: unknown -//@ ignore-test +//@ build-fail #![feature(generic_const_items)] #![allow(incomplete_features)] +#![recursion_limit = "15"] const RECUR: () = RECUR::<(T,)>; fn main() { - let _ = RECUR::<()>; + let _ = RECUR::<()>; //~ ERROR: queries overflow the depth limit! } diff --git a/tests/ui/generic-const-items/recursive.stderr b/tests/ui/generic-const-items/recursive.stderr new file mode 100644 index 0000000000000..c9a5793742899 --- /dev/null +++ b/tests/ui/generic-const-items/recursive.stderr @@ -0,0 +1,11 @@ +error: queries overflow the depth limit! + --> $DIR/recursive.rs:10:13 + | +LL | let _ = RECUR::<()>; + | ^^^^^^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "30"]` attribute to your crate (`recursive`) + = note: query depth increased by 17 when simplifying constant for the type system `RECUR` + +error: aborting due to 1 previous error + diff --git a/tests/ui/generics/generic-tag-match.rs b/tests/ui/generics/generic-tag-match.rs index dd0291e9d8736..378b51df287b3 100644 --- a/tests/ui/generics/generic-tag-match.rs +++ b/tests/ui/generics/generic-tag-match.rs @@ -7,7 +7,7 @@ enum foo { arm(T), } fn altfoo(f: foo) { let mut hit = false; match f { foo::arm::(_x) => { println!("in arm"); hit = true; } } - assert!((hit)); + assert!(hit); } pub fn main() { altfoo::(foo::arm::(10)); } diff --git a/tests/ui/generics/issue-79605.stderr b/tests/ui/generics/issue-79605.stderr index 67fed200f9693..049f77a658404 100644 --- a/tests/ui/generics/issue-79605.stderr +++ b/tests/ui/generics/issue-79605.stderr @@ -3,11 +3,6 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures | LL | impl X<'_, _> {} | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL | impl X<'_, T> {} - | +++ ~ error: aborting due to 1 previous error diff --git a/tests/ui/generics/issue-95208-ignore-qself.stderr b/tests/ui/generics/issue-95208-ignore-qself.stderr index 7d91fbc14a187..b9a5ffac274f7 100644 --- a/tests/ui/generics/issue-95208-ignore-qself.stderr +++ b/tests/ui/generics/issue-95208-ignore-qself.stderr @@ -6,8 +6,9 @@ LL | impl Struct where ::Item:: std:: | help: use single colon | -LL | impl Struct where ::Item: std::fmt::Display { - | ~ +LL - impl Struct where ::Item:: std::fmt::Display { +LL + impl Struct where ::Item: std::fmt::Display { + | error: aborting due to 1 previous error diff --git a/tests/ui/generics/issue-95208.stderr b/tests/ui/generics/issue-95208.stderr index e047f1265e2e9..0ff6c8be9fd10 100644 --- a/tests/ui/generics/issue-95208.stderr +++ b/tests/ui/generics/issue-95208.stderr @@ -6,8 +6,9 @@ LL | impl Struct where T:: std::fmt::Display { | help: use single colon | -LL | impl Struct where T: std::fmt::Display { - | ~ +LL - impl Struct where T:: std::fmt::Display { +LL + impl Struct where T: std::fmt::Display { + | error: aborting due to 1 previous error diff --git a/tests/ui/generics/overlapping-errors-span-issue-123861.stderr b/tests/ui/generics/overlapping-errors-span-issue-123861.stderr index e0a49343b0e0d..9622dffda9f84 100644 --- a/tests/ui/generics/overlapping-errors-span-issue-123861.stderr +++ b/tests/ui/generics/overlapping-errors-span-issue-123861.stderr @@ -33,8 +33,9 @@ LL | fn mainIterator<_ = _> {} | help: use type parameters instead | -LL | fn mainIterator {} - | ~ ~ +LL - fn mainIterator<_ = _> {} +LL + fn mainIterator {} + | error: aborting due to 4 previous errors diff --git a/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-dotdotdot-bad-syntax.stderr b/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-dotdotdot-bad-syntax.stderr index aa3d2cd2d904f..ec0e09a302ea1 100644 --- a/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-dotdotdot-bad-syntax.stderr +++ b/tests/ui/half-open-range-patterns/half-open-range-pats-inclusive-dotdotdot-bad-syntax.stderr @@ -6,8 +6,9 @@ LL | ...X => {} | help: use `..=` instead | -LL | ..=X => {} - | ~~~ +LL - ...X => {} +LL + ..=X => {} + | error: range-to patterns with `...` are not allowed --> $DIR/half-open-range-pats-inclusive-dotdotdot-bad-syntax.rs:16:9 @@ -17,8 +18,9 @@ LL | ...0 => {} | help: use `..=` instead | -LL | ..=0 => {} - | ~~~ +LL - ...0 => {} +LL + ..=0 => {} + | error: range-to patterns with `...` are not allowed --> $DIR/half-open-range-pats-inclusive-dotdotdot-bad-syntax.rs:17:9 @@ -28,8 +30,9 @@ LL | ...'a' => {} | help: use `..=` instead | -LL | ..='a' => {} - | ~~~ +LL - ...'a' => {} +LL + ..='a' => {} + | error: range-to patterns with `...` are not allowed --> $DIR/half-open-range-pats-inclusive-dotdotdot-bad-syntax.rs:18:9 @@ -39,8 +42,9 @@ LL | ...0.0f32 => {} | help: use `..=` instead | -LL | ..=0.0f32 => {} - | ~~~ +LL - ...0.0f32 => {} +LL + ..=0.0f32 => {} + | error: range-to patterns with `...` are not allowed --> $DIR/half-open-range-pats-inclusive-dotdotdot-bad-syntax.rs:25:17 @@ -54,8 +58,9 @@ LL | mac!(0); = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) help: use `..=` instead | -LL | let ..=$e; - | ~~~ +LL - let ...$e; +LL + let ..=$e; + | error[E0005]: refutable pattern in local binding --> $DIR/half-open-range-pats-inclusive-dotdotdot-bad-syntax.rs:25:17 diff --git a/tests/ui/half-open-range-patterns/half-open-range-pats-ref-ambiguous-interp.stderr b/tests/ui/half-open-range-patterns/half-open-range-pats-ref-ambiguous-interp.stderr index 83a374c3d65db..2d70f75f2c6d0 100644 --- a/tests/ui/half-open-range-patterns/half-open-range-pats-ref-ambiguous-interp.stderr +++ b/tests/ui/half-open-range-patterns/half-open-range-pats-ref-ambiguous-interp.stderr @@ -76,8 +76,9 @@ LL | &...0 | _ => {} | help: use `..=` instead | -LL | &..=0 | _ => {} - | ~~~ +LL - &...0 | _ => {} +LL + &..=0 | _ => {} + | error: the range pattern here has ambiguous interpretation --> $DIR/half-open-range-pats-ref-ambiguous-interp.rs:20:10 diff --git a/tests/ui/hashmap/hashmap-capacity-overflow.rs b/tests/ui/hashmap/hashmap-capacity-overflow.rs index 91aebc3bbba8b..502dbe9fa932e 100644 --- a/tests/ui/hashmap/hashmap-capacity-overflow.rs +++ b/tests/ui/hashmap/hashmap-capacity-overflow.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:capacity overflow -//@ ignore-emscripten no processes +//@ needs-subprocess use std::collections::hash_map::HashMap; use std::mem::size_of; diff --git a/tests/ui/hashmap/hashmap-index-mut.stderr b/tests/ui/hashmap/hashmap-index-mut.stderr index ad33c6f9b1540..e8b22350c59da 100644 --- a/tests/ui/hashmap/hashmap-index-mut.stderr +++ b/tests/ui/hashmap/hashmap-index-mut.stderr @@ -7,12 +7,15 @@ LL | map[&0] = 1; = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `HashMap` help: use `.insert()` to insert a value into a `HashMap`, `.get_mut()` to modify it, or the entry API for more flexibility | -LL | map.insert(&0, 1); - | ~~~~~~~~ ~ + -LL | if let Some(val) = map.get_mut(&0) { *val = 1; }; - | ++++++++++++++++++ ~~~~~~~~~ ~~~~~~~~ +++ -LL | let val = map.entry(&0).or_insert(1); - | +++++++++ ~~~~~~~ ~~~~~~~~~~~~ + +LL - map[&0] = 1; +LL + map.insert(&0, 1); + | +LL - map[&0] = 1; +LL + if let Some(val) = map.get_mut(&0) { *val = 1; }; + | +LL - map[&0] = 1; +LL + let val = map.entry(&0).or_insert(1); + | error: aborting due to 1 previous error diff --git a/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.stderr b/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.stderr index 67417a5e525cf..be2eca3e61ad9 100644 --- a/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.stderr +++ b/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.stderr @@ -11,7 +11,7 @@ LL | | ), LL | | ) { | |_- expected `&dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn Fn(u32) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a))` because of return type LL | f - | ^ expected `&dyn Fn(&dyn Fn(&dyn Fn(&...)))`, found `&dyn Fn(u32)` + | ^ expected `&dyn Fn(&dyn Fn(&dyn Fn(&dyn Fn(&...))))`, found `&dyn Fn(u32)` | = note: expected reference `&dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn Fn(u32) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a))` found reference `&dyn Fn(u32)` diff --git a/tests/ui/higher-ranked/trait-bounds/hrtb-perfect-forwarding.polonius.stderr b/tests/ui/higher-ranked/trait-bounds/hrtb-perfect-forwarding.polonius.stderr deleted file mode 100644 index 795484f110884..0000000000000 --- a/tests/ui/higher-ranked/trait-bounds/hrtb-perfect-forwarding.polonius.stderr +++ /dev/null @@ -1,71 +0,0 @@ -warning: function cannot return without recursing - --> $DIR/hrtb-perfect-forwarding.rs:16:1 - | -LL | / fn no_hrtb<'b, T>(mut t: T) -LL | | where -LL | | T: Bar<&'b isize>, -LL | | { -... | -LL | | no_hrtb(&mut t); - | | --------------- recursive call site -LL | | } - | |_^ cannot return without recursing - | - = note: `#[warn(unconditional_recursion)]` on by default - = help: a `loop` may express intention better if this is on purpose - -warning: function cannot return without recursing - --> $DIR/hrtb-perfect-forwarding.rs:25:1 - | -LL | / fn bar_hrtb(mut t: T) -LL | | where -LL | | T: for<'b> Bar<&'b isize>, -LL | | { -... | -LL | | bar_hrtb(&mut t); - | | ---------------- recursive call site -LL | | } - | |_^ cannot return without recursing - | - = help: a `loop` may express intention better if this is on purpose - -warning: function cannot return without recursing - --> $DIR/hrtb-perfect-forwarding.rs:35:1 - | -LL | / fn foo_hrtb_bar_not<'b, T>(mut t: T) -LL | | where -LL | | T: for<'a> Foo<&'a isize> + Bar<&'b isize>, -LL | | { -... | -LL | | foo_hrtb_bar_not(&mut t); - | | ------------------------ recursive call site -LL | | -LL | | -LL | | } - | |_^ cannot return without recursing - | - = help: a `loop` may express intention better if this is on purpose - -error: higher-ranked subtype error - --> $DIR/hrtb-perfect-forwarding.rs:43:5 - | -LL | foo_hrtb_bar_not(&mut t); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: function cannot return without recursing - --> $DIR/hrtb-perfect-forwarding.rs:48:1 - | -LL | / fn foo_hrtb_bar_hrtb(mut t: T) -LL | | where -LL | | T: for<'a> Foo<&'a isize> + for<'b> Bar<&'b isize>, -LL | | { -LL | | // OK -- now we have `T : for<'b> Bar<&'b isize>`. -LL | | foo_hrtb_bar_hrtb(&mut t); - | | ------------------------- recursive call site -LL | | } - | |_^ cannot return without recursing - | - = help: a `loop` may express intention better if this is on purpose - -error: aborting due to 1 previous error; 4 warnings emitted - diff --git a/tests/ui/higher-ranked/trait-bounds/issue-58451.stderr b/tests/ui/higher-ranked/trait-bounds/issue-58451.stderr index 34d7db9b3cf86..4f9fc0ac6492c 100644 --- a/tests/ui/higher-ranked/trait-bounds/issue-58451.stderr +++ b/tests/ui/higher-ranked/trait-bounds/issue-58451.stderr @@ -11,8 +11,9 @@ LL | fn f(i: I) | ^ ---- help: provide the argument | -LL | f(&[f(/* i */)]); - | ~~~~~~~~~ +LL - f(&[f()]); +LL + f(&[f(/* i */)]); + | error: aborting due to 1 previous error diff --git a/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.rs b/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.rs index e70f6fc3430f6..c8963e078f08a 100644 --- a/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.rs +++ b/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.rs @@ -36,12 +36,10 @@ trait Ty<'a> { fn main() { let v = Unit2.m( - L { - //~^ ERROR to be a closure that returns `Unit3`, but it returns `Unit4` - //~| ERROR type mismatch + L { //~ ERROR type mismatch f: |x| { drop(x); - Unit4 + Unit4 //~ ERROR to return `Unit3`, but it returns `Unit4` }, }, ); diff --git a/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.stderr b/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.stderr index 74610b55dc373..4302570cdbdef 100644 --- a/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.stderr +++ b/tests/ui/higher-ranked/trait-bounds/issue-62203-hrtb-ice.stderr @@ -1,16 +1,15 @@ -error[E0271]: type mismatch resolving ` as T0<'r, (&u8,)>>::O == <_ as Ty<'r>>::V` +error[E0271]: type mismatch resolving ` as T0<'r, (&u8,)>>::O == <_ as Ty<'r>>::V` --> $DIR/issue-62203-hrtb-ice.rs:39:9 | LL | let v = Unit2.m( | - required by a bound introduced by this call LL | / L { -LL | | -LL | | LL | | f: |x| { -... | +LL | | drop(x); +LL | | Unit4 LL | | }, LL | | }, - | |_________^ type mismatch resolving ` as T0<'r, (&u8,)>>::O == <_ as Ty<'r>>::V` + | |_________^ type mismatch resolving ` as T0<'r, (&u8,)>>::O == <_ as Ty<'r>>::V` | note: expected this to be `<_ as Ty<'_>>::V` --> $DIR/issue-62203-hrtb-ice.rs:21:14 @@ -30,21 +29,19 @@ LL | where LL | F: for<'r> T0<'r, (>::V,), O = >::V>, | ^^^^^^^^^^^^^^^^^^^^ required by this bound in `T1::m` -error[E0271]: expected `{closure@issue-62203-hrtb-ice.rs:42:16}` to be a closure that returns `Unit3`, but it returns `Unit4` - --> $DIR/issue-62203-hrtb-ice.rs:39:9 - | -LL | let v = Unit2.m( - | - required by a bound introduced by this call -LL | / L { -LL | | -LL | | -LL | | f: |x| { -... | -LL | | }, -LL | | }, - | |_________^ expected `Unit3`, found `Unit4` - | -note: required for `L<{closure@$DIR/issue-62203-hrtb-ice.rs:42:16: 42:19}>` to implement `for<'r> T0<'r, (&'r u8,)>` +error[E0271]: expected `{closure@issue-62203-hrtb-ice.rs:40:16}` to return `Unit3`, but it returns `Unit4` + --> $DIR/issue-62203-hrtb-ice.rs:42:17 + | +LL | let v = Unit2.m( + | - required by a bound introduced by this call +LL | L { +LL | f: |x| { + | --- this closure +LL | drop(x); +LL | Unit4 + | ^^^^^ expected `Unit3`, found `Unit4` + | +note: required for `L<{closure@$DIR/issue-62203-hrtb-ice.rs:40:16: 40:19}>` to implement `for<'r> T0<'r, (&'r u8,)>` --> $DIR/issue-62203-hrtb-ice.rs:17:16 | LL | impl<'a, A, T> T0<'a, A> for L diff --git a/tests/ui/higher-ranked/trait-bounds/span-bug-issue-121597.rs b/tests/ui/higher-ranked/trait-bounds/span-bug-issue-121597.rs index aeace9f2158ae..5d039cd5dc656 100644 --- a/tests/ui/higher-ranked/trait-bounds/span-bug-issue-121597.rs +++ b/tests/ui/higher-ranked/trait-bounds/span-bug-issue-121597.rs @@ -12,8 +12,8 @@ fn needs_bar(_: *mut Type2) {} fn main() { let x: &dyn Foo = &(); - //~^ ERROR the trait `Foo` cannot be made into an object - //~| ERROR the trait `Foo` cannot be made into an object + //~^ ERROR the trait `Foo` is not dyn compatible + //~| ERROR the trait `Foo` is not dyn compatible needs_bar(x); //~^ ERROR mismatched types diff --git a/tests/ui/higher-ranked/trait-bounds/span-bug-issue-121597.stderr b/tests/ui/higher-ranked/trait-bounds/span-bug-issue-121597.stderr index d48bf8a471d61..183ee678d7a46 100644 --- a/tests/ui/higher-ranked/trait-bounds/span-bug-issue-121597.stderr +++ b/tests/ui/higher-ranked/trait-bounds/span-bug-issue-121597.stderr @@ -1,31 +1,33 @@ -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/span-bug-issue-121597.rs:14:23 | LL | let x: &dyn Foo = &(); - | ^^^ `Foo` cannot be made into an object + | ^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/span-bug-issue-121597.rs:4:12 | LL | trait Foo: for Bar {} | --- ^^^^^^^^^^^^^ ...because where clause cannot reference non-lifetime `for<...>` variables | | - | this trait cannot be made into an object... + | this trait is not dyn compatible... = note: required for the cast from `&()` to `&dyn Foo` -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/span-bug-issue-121597.rs:14:12 | LL | let x: &dyn Foo = &(); - | ^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/span-bug-issue-121597.rs:4:12 | LL | trait Foo: for Bar {} | --- ^^^^^^^^^^^^^ ...because where clause cannot reference non-lifetime `for<...>` variables | | - | this trait cannot be made into an object... + | this trait is not dyn compatible... error[E0308]: mismatched types --> $DIR/span-bug-issue-121597.rs:18:15 diff --git a/tests/ui/hygiene/extern-prelude-from-opaque-fail-2018.rs b/tests/ui/hygiene/extern-prelude-from-opaque-fail-2018.rs index aaf831d1983e4..71c33674b37fc 100644 --- a/tests/ui/hygiene/extern-prelude-from-opaque-fail-2018.rs +++ b/tests/ui/hygiene/extern-prelude-from-opaque-fail-2018.rs @@ -10,7 +10,7 @@ macro a() { mod u { // Late resolution. fn f() { my_core::mem::drop(0); } - //~^ ERROR failed to resolve: use of undeclared crate or module `my_core` + //~^ ERROR failed to resolve: use of unresolved module or unlinked crate `my_core` } } @@ -23,7 +23,7 @@ mod v { mod u { // Late resolution. fn f() { my_core::mem::drop(0); } - //~^ ERROR failed to resolve: use of undeclared crate or module `my_core` + //~^ ERROR failed to resolve: use of unresolved module or unlinked crate `my_core` } fn main() {} diff --git a/tests/ui/hygiene/extern-prelude-from-opaque-fail-2018.stderr b/tests/ui/hygiene/extern-prelude-from-opaque-fail-2018.stderr index cc229764ad3fb..87ef07c27f5bd 100644 --- a/tests/ui/hygiene/extern-prelude-from-opaque-fail-2018.stderr +++ b/tests/ui/hygiene/extern-prelude-from-opaque-fail-2018.stderr @@ -15,25 +15,27 @@ LL | a!(); | = note: this error originates in the macro `a` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0433]: failed to resolve: use of undeclared crate or module `my_core` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `my_core` --> $DIR/extern-prelude-from-opaque-fail-2018.rs:12:18 | LL | fn f() { my_core::mem::drop(0); } - | ^^^^^^^ use of undeclared crate or module `my_core` + | ^^^^^^^ use of unresolved module or unlinked crate `my_core` ... LL | a!(); | ---- in this macro invocation | + = help: you might be missing a crate named `my_core` = help: consider importing this module: std::mem = note: this error originates in the macro `a` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0433]: failed to resolve: use of undeclared crate or module `my_core` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `my_core` --> $DIR/extern-prelude-from-opaque-fail-2018.rs:25:14 | LL | fn f() { my_core::mem::drop(0); } - | ^^^^^^^ use of undeclared crate or module `my_core` + | ^^^^^^^ use of unresolved module or unlinked crate `my_core` | + = help: you might be missing a crate named `my_core` help: consider importing this module | LL + use std::mem; diff --git a/tests/ui/hygiene/extern-prelude-from-opaque-fail.rs b/tests/ui/hygiene/extern-prelude-from-opaque-fail.rs index be3102aeab07f..8265b73cc565b 100644 --- a/tests/ui/hygiene/extern-prelude-from-opaque-fail.rs +++ b/tests/ui/hygiene/extern-prelude-from-opaque-fail.rs @@ -10,7 +10,7 @@ macro a() { mod u { // Late resolution. fn f() { my_core::mem::drop(0); } - //~^ ERROR failed to resolve: use of undeclared crate or module `my_core` + //~^ ERROR failed to resolve: use of unresolved module or unlinked crate `my_core` } } @@ -23,7 +23,7 @@ mod v { mod u { // Late resolution. fn f() { my_core::mem::drop(0); } - //~^ ERROR failed to resolve: use of undeclared crate or module `my_core` + //~^ ERROR failed to resolve: use of unresolved module or unlinked crate `my_core` } fn main() {} diff --git a/tests/ui/hygiene/extern-prelude-from-opaque-fail.stderr b/tests/ui/hygiene/extern-prelude-from-opaque-fail.stderr index 13b2827ef39b8..d36bc9130953e 100644 --- a/tests/ui/hygiene/extern-prelude-from-opaque-fail.stderr +++ b/tests/ui/hygiene/extern-prelude-from-opaque-fail.stderr @@ -15,25 +15,27 @@ LL | a!(); | = note: this error originates in the macro `a` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0433]: failed to resolve: use of undeclared crate or module `my_core` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `my_core` --> $DIR/extern-prelude-from-opaque-fail.rs:12:18 | LL | fn f() { my_core::mem::drop(0); } - | ^^^^^^^ use of undeclared crate or module `my_core` + | ^^^^^^^ use of unresolved module or unlinked crate `my_core` ... LL | a!(); | ---- in this macro invocation | + = help: you might be missing a crate named `my_core` = help: consider importing this module: my_core::mem = note: this error originates in the macro `a` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0433]: failed to resolve: use of undeclared crate or module `my_core` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `my_core` --> $DIR/extern-prelude-from-opaque-fail.rs:25:14 | LL | fn f() { my_core::mem::drop(0); } - | ^^^^^^^ use of undeclared crate or module `my_core` + | ^^^^^^^ use of unresolved module or unlinked crate `my_core` | + = help: you might be missing a crate named `my_core` help: consider importing this module | LL + use my_core::mem; diff --git a/tests/ui/hygiene/globs.stderr b/tests/ui/hygiene/globs.stderr index 180172644402b..3f7a0ae7efa1b 100644 --- a/tests/ui/hygiene/globs.stderr +++ b/tests/ui/hygiene/globs.stderr @@ -9,8 +9,9 @@ LL | f(); | help: a function with a similar name exists | -LL | g(); - | ~ +LL - f(); +LL + g(); + | help: consider importing this function | LL + use foo::f; @@ -35,8 +36,9 @@ LL | | } = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) help: a function with a similar name exists | -LL | f(); - | ~ +LL - g(); +LL + f(); + | help: consider importing this function | LL + use bar::g; diff --git a/tests/ui/impl-trait/auto-trait-coherence.next.stderr b/tests/ui/impl-trait/auto-trait-coherence.next.stderr deleted file mode 100644 index cd91bfcb48d73..0000000000000 --- a/tests/ui/impl-trait/auto-trait-coherence.next.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0119]: conflicting implementations of trait `AnotherTrait` for type `D<_>` - --> $DIR/auto-trait-coherence.rs:24:1 - | -LL | impl AnotherTrait for T {} - | -------------------------------- first implementation here -... -LL | impl AnotherTrait for D { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `D<_>` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/impl-trait/auto-trait-coherence.old.stderr b/tests/ui/impl-trait/auto-trait-coherence.old.stderr deleted file mode 100644 index cd91bfcb48d73..0000000000000 --- a/tests/ui/impl-trait/auto-trait-coherence.old.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0119]: conflicting implementations of trait `AnotherTrait` for type `D<_>` - --> $DIR/auto-trait-coherence.rs:24:1 - | -LL | impl AnotherTrait for T {} - | -------------------------------- first implementation here -... -LL | impl AnotherTrait for D { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `D<_>` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/impl-trait/coherence-treats-tait-ambig.current.stderr b/tests/ui/impl-trait/coherence-treats-tait-ambig.current.stderr deleted file mode 100644 index 444f3d6689f20..0000000000000 --- a/tests/ui/impl-trait/coherence-treats-tait-ambig.current.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0119]: conflicting implementations of trait `Into` for type `Foo` - --> $DIR/coherence-treats-tait-ambig.rs:10:1 - | -LL | impl Into for Foo { - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: conflicting implementation in crate `core`: - - impl Into for T - where U: From; - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-dyn-trait.rs b/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-dyn-trait.rs index 76dbb05f53d66..046ced072bac2 100644 --- a/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-dyn-trait.rs +++ b/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-dyn-trait.rs @@ -19,7 +19,7 @@ impl DynIncompatible for B { } } -fn car() -> dyn DynIncompatible { //~ ERROR the trait `DynIncompatible` cannot be made into an object +fn car() -> dyn DynIncompatible { //~ ERROR the trait `DynIncompatible` is not dyn compatible //~^ ERROR return type cannot have an unboxed trait object if true { return A; @@ -27,11 +27,11 @@ fn car() -> dyn DynIncompatible { //~ ERROR the trait `DynIncompatible` cannot b B } -fn cat() -> Box { //~ ERROR the trait `DynIncompatible` cannot be made into an +fn cat() -> Box { //~ ERROR the trait `DynIncompatible` is not dyn compatible if true { - return Box::new(A); //~ ERROR cannot be made into an object + return Box::new(A); //~ ERROR is not dyn compatible } - Box::new(B) //~ ERROR cannot be made into an object + Box::new(B) //~ ERROR is not dyn compatible } fn main() {} diff --git a/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-dyn-trait.stderr b/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-dyn-trait.stderr index 576bd909cbc7d..2869702d7fc63 100644 --- a/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-dyn-trait.stderr +++ b/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-dyn-trait.stderr @@ -1,19 +1,22 @@ -error[E0038]: the trait `DynIncompatible` cannot be made into an object +error[E0038]: the trait `DynIncompatible` is not dyn compatible --> $DIR/dyn-incompatible-trait-in-return-position-dyn-trait.rs:22:13 | LL | fn car() -> dyn DynIncompatible { - | ^^^^^^^^^^^^^^^^^^^ `DynIncompatible` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^ `DynIncompatible` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/dyn-incompatible-trait-in-return-position-dyn-trait.rs:4:8 | LL | trait DynIncompatible { - | --------------- this trait cannot be made into an object... + | --------------- this trait is not dyn compatible... LL | fn foo() -> Self; | ^^^ ...because associated function `foo` has no `self` parameter - = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `DynIncompatible` for this new enum and using it instead: + = help: the following types implement `DynIncompatible`: A B + consider defining an enum where each variant holds one of these types, + implementing `DynIncompatible` for this new enum and using it instead help: consider turning `foo` into a method by giving it a `&self` argument | LL | fn foo(&self) -> Self; @@ -23,22 +26,25 @@ help: alternatively, consider constraining `foo` so it does not apply to trait o LL | fn foo() -> Self where Self: Sized; | +++++++++++++++++ -error[E0038]: the trait `DynIncompatible` cannot be made into an object +error[E0038]: the trait `DynIncompatible` is not dyn compatible --> $DIR/dyn-incompatible-trait-in-return-position-dyn-trait.rs:30:17 | LL | fn cat() -> Box { - | ^^^^^^^^^^^^^^^^^^^ `DynIncompatible` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^ `DynIncompatible` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/dyn-incompatible-trait-in-return-position-dyn-trait.rs:4:8 | LL | trait DynIncompatible { - | --------------- this trait cannot be made into an object... + | --------------- this trait is not dyn compatible... LL | fn foo() -> Self; | ^^^ ...because associated function `foo` has no `self` parameter - = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `DynIncompatible` for this new enum and using it instead: + = help: the following types implement `DynIncompatible`: A B + consider defining an enum where each variant holds one of these types, + implementing `DynIncompatible` for this new enum and using it instead help: consider turning `foo` into a method by giving it a `&self` argument | LL | fn foo(&self) -> Self; @@ -65,22 +71,25 @@ LL | } LL ~ Box::new(B) | -error[E0038]: the trait `DynIncompatible` cannot be made into an object +error[E0038]: the trait `DynIncompatible` is not dyn compatible --> $DIR/dyn-incompatible-trait-in-return-position-dyn-trait.rs:32:16 | LL | return Box::new(A); - | ^^^^^^^^^^^ `DynIncompatible` cannot be made into an object + | ^^^^^^^^^^^ `DynIncompatible` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/dyn-incompatible-trait-in-return-position-dyn-trait.rs:4:8 | LL | trait DynIncompatible { - | --------------- this trait cannot be made into an object... + | --------------- this trait is not dyn compatible... LL | fn foo() -> Self; | ^^^ ...because associated function `foo` has no `self` parameter - = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `DynIncompatible` for this new enum and using it instead: + = help: the following types implement `DynIncompatible`: A B + consider defining an enum where each variant holds one of these types, + implementing `DynIncompatible` for this new enum and using it instead = note: required for the cast from `Box` to `Box<(dyn DynIncompatible + 'static)>` help: consider turning `foo` into a method by giving it a `&self` argument | @@ -91,22 +100,25 @@ help: alternatively, consider constraining `foo` so it does not apply to trait o LL | fn foo() -> Self where Self: Sized; | +++++++++++++++++ -error[E0038]: the trait `DynIncompatible` cannot be made into an object +error[E0038]: the trait `DynIncompatible` is not dyn compatible --> $DIR/dyn-incompatible-trait-in-return-position-dyn-trait.rs:34:5 | LL | Box::new(B) - | ^^^^^^^^^^^ `DynIncompatible` cannot be made into an object + | ^^^^^^^^^^^ `DynIncompatible` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/dyn-incompatible-trait-in-return-position-dyn-trait.rs:4:8 | LL | trait DynIncompatible { - | --------------- this trait cannot be made into an object... + | --------------- this trait is not dyn compatible... LL | fn foo() -> Self; | ^^^ ...because associated function `foo` has no `self` parameter - = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `DynIncompatible` for this new enum and using it instead: + = help: the following types implement `DynIncompatible`: A B + consider defining an enum where each variant holds one of these types, + implementing `DynIncompatible` for this new enum and using it instead = note: required for the cast from `Box` to `Box<(dyn DynIncompatible + 'static)>` help: consider turning `foo` into a method by giving it a `&self` argument | diff --git a/tests/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr b/tests/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr index 9ed3d21c13ce6..db06363eb628d 100644 --- a/tests/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr +++ b/tests/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr @@ -65,8 +65,9 @@ LL | fn ban() -> dyn Trait { Struct } | help: consider returning an `impl Trait` instead of a `dyn Trait` | -LL | fn ban() -> impl Trait { Struct } - | ~~~~ +LL - fn ban() -> dyn Trait { Struct } +LL + fn ban() -> impl Trait { Struct } + | help: alternatively, box the return type, and wrap all of the returned values in `Box::new` | LL | fn ban() -> Box { Box::new(Struct) } @@ -80,8 +81,9 @@ LL | fn bak() -> dyn Trait { unimplemented!() } | help: consider returning an `impl Trait` instead of a `dyn Trait` | -LL | fn bak() -> impl Trait { unimplemented!() } - | ~~~~ +LL - fn bak() -> dyn Trait { unimplemented!() } +LL + fn bak() -> impl Trait { unimplemented!() } + | help: alternatively, box the return type, and wrap all of the returned values in `Box::new` | LL | fn bak() -> Box { Box::new(unimplemented!()) } @@ -263,8 +265,9 @@ LL | fn bat() -> dyn Trait { | help: consider returning an `impl Trait` instead of a `dyn Trait` | -LL | fn bat() -> impl Trait { - | ~~~~ +LL - fn bat() -> dyn Trait { +LL + fn bat() -> impl Trait { + | help: alternatively, box the return type, and wrap all of the returned values in `Box::new` | LL ~ fn bat() -> Box { @@ -282,8 +285,9 @@ LL | fn bay() -> dyn Trait { | help: consider returning an `impl Trait` instead of a `dyn Trait` | -LL | fn bay() -> impl Trait { - | ~~~~ +LL - fn bay() -> dyn Trait { +LL + fn bay() -> impl Trait { + | help: alternatively, box the return type, and wrap all of the returned values in `Box::new` | LL ~ fn bay() -> Box { diff --git a/tests/ui/impl-trait/equality.stderr b/tests/ui/impl-trait/equality.stderr index fd6f4b34241aa..8106bab98782b 100644 --- a/tests/ui/impl-trait/equality.stderr +++ b/tests/ui/impl-trait/equality.stderr @@ -19,8 +19,9 @@ LL | 0_u32 | help: change the type of the numeric literal from `u32` to `i32` | -LL | 0_i32 - | ~~~ +LL - 0_u32 +LL + 0_i32 + | error[E0277]: cannot add `impl Foo` to `u32` --> $DIR/equality.rs:24:11 diff --git a/tests/ui/impl-trait/generic-with-implicit-hrtb-without-dyn.edition2021.stderr b/tests/ui/impl-trait/generic-with-implicit-hrtb-without-dyn.edition2021.stderr index 1e268bc7f49b9..112341287f8a5 100644 --- a/tests/ui/impl-trait/generic-with-implicit-hrtb-without-dyn.edition2021.stderr +++ b/tests/ui/impl-trait/generic-with-implicit-hrtb-without-dyn.edition2021.stderr @@ -9,6 +9,18 @@ help: you can add the `dyn` keyword if you want a trait object LL | fn ice() -> impl AsRef { | +++ -error: aborting due to 1 previous error +error[E0782]: expected a type, found a trait + --> $DIR/generic-with-implicit-hrtb-without-dyn.rs:6:24 + | +LL | fn ice() -> impl AsRef { + | ^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: you can add the `dyn` keyword if you want a trait object + | +LL | fn ice() -> impl AsRef { + | +++ + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/impl-trait/generic-with-implicit-hrtb-without-dyn.rs b/tests/ui/impl-trait/generic-with-implicit-hrtb-without-dyn.rs index c71f794d5d125..df43250f15153 100644 --- a/tests/ui/impl-trait/generic-with-implicit-hrtb-without-dyn.rs +++ b/tests/ui/impl-trait/generic-with-implicit-hrtb-without-dyn.rs @@ -6,6 +6,7 @@ fn ice() -> impl AsRef { //[edition2015]~^ ERROR: the trait bound `(): AsRef<(dyn for<'a> Fn(&'a ()) + 'static)>` is not satisfied [E0277] //[edition2021]~^^ ERROR: expected a type, found a trait [E0782] + //[edition2021]~| ERROR: expected a type, found a trait [E0782] todo!() } diff --git a/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.current.stderr b/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.current.stderr new file mode 100644 index 0000000000000..146a3d21068cd --- /dev/null +++ b/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.current.stderr @@ -0,0 +1,31 @@ +error[E0407]: method `line_stream` is not a member of trait `X` + --> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:28:5 + | +LL | fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a member of trait `X` + +error[E0049]: type `LineStream` has 0 type parameters but its trait declaration has 1 type parameter + --> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:25:21 + | +LL | type LineStream<'a, Repr> + | -- ---- + | | + | expected 1 type parameter +... +LL | type LineStream<'c, 'd> = impl Stream; + | ^^ ^^ + | | + | found 0 type parameters + +error[E0277]: `()` is not a future + --> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:28:43 + | +LL | fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `()` is not a future + | + = help: the trait `Future` is not implemented for `()` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0049, E0277, E0407. +For more information about an error, try `rustc --explain E0049`. diff --git a/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.next.stderr b/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.next.stderr new file mode 100644 index 0000000000000..e49c8c92d8396 --- /dev/null +++ b/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.next.stderr @@ -0,0 +1,49 @@ +error[E0407]: method `line_stream` is not a member of trait `X` + --> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:28:5 + | +LL | fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a member of trait `X` + +error[E0049]: type `LineStream` has 0 type parameters but its trait declaration has 1 type parameter + --> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:25:21 + | +LL | type LineStream<'a, Repr> + | -- ---- + | | + | expected 1 type parameter +... +LL | type LineStream<'c, 'd> = impl Stream; + | ^^ ^^ + | | + | found 0 type parameters + +error[E0271]: type mismatch resolving `::LineStreamFut<'a, Repr> normalizes-to ()` + --> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:28:43 + | +LL | fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ + +error[E0271]: type mismatch resolving `::LineStreamFut<'a, Repr> normalizes-to _` + --> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:28:43 + | +LL | fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ + | + = note: the return type of a function must have a statically known size + +error[E0271]: type mismatch resolving `::LineStreamFut<'a, Repr> normalizes-to _` + --> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:28:73 + | +LL | fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {} + | ^^ types differ + +error[E0271]: type mismatch resolving `::LineStreamFut<'a, Repr> normalizes-to _` + --> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:28:5 + | +LL | fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0049, E0271, E0407. +For more information about an error, try `rustc --explain E0049`. diff --git a/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.rs b/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.rs index d6fa56663a3f0..bb0c6215de291 100644 --- a/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.rs +++ b/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.rs @@ -1,3 +1,6 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver // test for ICE #112823 // Unexpected parameter Type(Repr) when substituting in region @@ -23,8 +26,12 @@ impl X for Y { //~^ ERROR type `LineStream` has 0 type parameters but its trait declaration has 1 type parameter type LineStreamFut<'a, Repr> = impl Future>; fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {} - //~^ ERROR `()` is not a future - //~^^ method `line_stream` is not a member of trait `X` + //~^ method `line_stream` is not a member of trait `X` + //[current]~^^ ERROR `()` is not a future + //[next]~| ERROR type mismatch resolving `::LineStreamFut<'a, Repr> normalizes-to _` + //[next]~| ERROR type mismatch resolving `::LineStreamFut<'a, Repr> normalizes-to _` + //[next]~| ERROR type mismatch resolving `::LineStreamFut<'a, Repr> normalizes-to ()` + //[next]~| ERROR type mismatch resolving `::LineStreamFut<'a, Repr> normalizes-to _` } pub fn main() {} diff --git a/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.stderr b/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.stderr deleted file mode 100644 index 28a0f7461e2d5..0000000000000 --- a/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.stderr +++ /dev/null @@ -1,31 +0,0 @@ -error[E0407]: method `line_stream` is not a member of trait `X` - --> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:25:5 - | -LL | fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a member of trait `X` - -error[E0049]: type `LineStream` has 0 type parameters but its trait declaration has 1 type parameter - --> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:22:21 - | -LL | type LineStream<'a, Repr> - | -- ---- - | | - | expected 1 type parameter -... -LL | type LineStream<'c, 'd> = impl Stream; - | ^^ ^^ - | | - | found 0 type parameters - -error[E0277]: `()` is not a future - --> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:25:43 - | -LL | fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `()` is not a future - | - = help: the trait `Future` is not implemented for `()` - -error: aborting due to 3 previous errors - -Some errors have detailed explanations: E0049, E0277, E0407. -For more information about an error, try `rustc --explain E0049`. diff --git a/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr b/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr index d0f8f7689d17c..31f39eb90041e 100644 --- a/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr +++ b/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr @@ -7,8 +7,9 @@ LL | fn d() -> impl Fn() -> (impl Debug + '_) { = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values | -LL | fn d() -> impl Fn() -> (impl Debug + 'static) { - | ~~~~~~~ +LL - fn d() -> impl Fn() -> (impl Debug + '_) { +LL + fn d() -> impl Fn() -> (impl Debug + 'static) { + | warning: elided lifetime has a name --> $DIR/impl-fn-hrtb-bounds.rs:14:52 diff --git a/tests/ui/impl-trait/impl-fn-rpit-opaque-107883.rs b/tests/ui/impl-trait/impl-fn-rpit-opaque-107883.rs new file mode 100644 index 0000000000000..3655bff63eb0c --- /dev/null +++ b/tests/ui/impl-trait/impl-fn-rpit-opaque-107883.rs @@ -0,0 +1,37 @@ +//@ check-pass +// Regression test for +#![feature(impl_trait_in_fn_trait_return)] +#![feature(unboxed_closures)] // only for `h` + +use std::fmt::Debug; + +fn f() -> impl Fn(T) -> impl Debug { + |_x| 15 +} + +fn g() -> impl MyFn<(T,), Out = impl Debug> { + |_x| 15 +} + +trait MyFn { + type Out; +} + +impl U> MyFn<(T,)> for F { + type Out = U; +} + +fn h() -> impl Fn<(T,), Output = impl Debug> { + |_x| 15 +} + +fn f_() -> impl Fn(T) -> impl Debug { + std::convert::identity(|_x| 15) +} + +fn f__() -> impl Fn(T) -> impl Debug { + let r = |_x| 15; + r +} + +fn main() {} diff --git a/tests/ui/impl-trait/impl-generic-mismatch-ab.stderr b/tests/ui/impl-trait/impl-generic-mismatch-ab.stderr index 90c31c9e3fc19..9db996cf9ce65 100644 --- a/tests/ui/impl-trait/impl-generic-mismatch-ab.stderr +++ b/tests/ui/impl-trait/impl-generic-mismatch-ab.stderr @@ -17,8 +17,9 @@ LL | fn foo(&self, a: &A, b: &impl Debug); = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters help: change the parameter type to match the trait | -LL | fn foo(&self, a: &B, b: &B) { } - | ~~ +LL - fn foo(&self, a: &impl Debug, b: &B) { } +LL + fn foo(&self, a: &B, b: &B) { } + | error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/impl-generic-mismatch.stderr b/tests/ui/impl-trait/impl-generic-mismatch.stderr index 973b65bfd625a..18347bd0f978f 100644 --- a/tests/ui/impl-trait/impl-generic-mismatch.stderr +++ b/tests/ui/impl-trait/impl-generic-mismatch.stderr @@ -24,8 +24,9 @@ LL | fn bar(&self, _: &impl Debug) { } | help: try changing the `impl Trait` argument to a generic parameter | -LL | fn bar(&self, _: &U) { } - | ++++++++++ ~ +LL - fn bar(&self, _: &impl Debug) { } +LL + fn bar(&self, _: &U) { } + | error[E0643]: method `baz` has incompatible signature for trait --> $DIR/impl-generic-mismatch.rs:26:33 @@ -38,8 +39,9 @@ LL | fn baz(&self, _: &impl Debug, _: &T) { } | help: try changing the `impl Trait` argument to a generic parameter | -LL | fn baz(&self, _: &T, _: &T) { } - | ~~~~~~~~~~~~~~~~~~~~ ~ +LL - fn baz(&self, _: &impl Debug, _: &T) { } +LL + fn baz(&self, _: &T, _: &T) { } + | error[E0643]: method `hash` has incompatible signature for trait --> $DIR/impl-generic-mismatch.rs:37:33 diff --git a/tests/ui/impl-trait/impl-trait-in-macro.stderr b/tests/ui/impl-trait/impl-trait-in-macro.stderr index 4380f47c5de46..2f934694f83f3 100644 --- a/tests/ui/impl-trait/impl-trait-in-macro.stderr +++ b/tests/ui/impl-trait/impl-trait-in-macro.stderr @@ -12,8 +12,8 @@ LL | let mut a = x; LL | a = y; | ^ expected type parameter `impl Debug`, found a different type parameter `impl Debug` | - = note: expected type parameter `impl Debug` (type parameter `impl Debug`) - found type parameter `impl Debug` (type parameter `impl Debug`) + = note: expected type parameter `impl Debug` + found type parameter `impl Debug` = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters diff --git a/tests/ui/impl-trait/in-assoc-type-unconstrained.stderr b/tests/ui/impl-trait/in-assoc-type-unconstrained.stderr index 75cbe43eeb4a1..8351175099c9a 100644 --- a/tests/ui/impl-trait/in-assoc-type-unconstrained.stderr +++ b/tests/ui/impl-trait/in-assoc-type-unconstrained.stderr @@ -43,8 +43,9 @@ LL | fn method() -> () {} | ^^^^^^ help: change the output type to match the trait | -LL | fn method() -> <() as compare_method::Trait>::Ty {} - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - fn method() -> () {} +LL + fn method() -> <() as compare_method::Trait>::Ty {} + | error: unconstrained opaque type --> $DIR/in-assoc-type-unconstrained.rs:20:19 diff --git a/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.stderr b/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.stderr index 1cfc2a6d94495..a95670ced8678 100644 --- a/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.stderr +++ b/tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.stderr @@ -18,6 +18,11 @@ help: this trait has no implementations, consider adding one | LL | trait Foo {} | ^^^^^^^^^ +note: required by a bound in `A` + --> $DIR/alias-bounds-when-not-wf.rs:8:11 + | +LL | type A = T; + | ^^^ required by this bound in `A` error[E0277]: the trait bound `usize: Foo` is not satisfied --> $DIR/alias-bounds-when-not-wf.rs:16:10 diff --git a/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.stderr b/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.stderr index 022df6f906cf4..d3729b6c97370 100644 --- a/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.stderr +++ b/tests/ui/impl-trait/in-trait/bad-item-bound-within-rpitit.stderr @@ -9,8 +9,10 @@ LL | 'b: 'a; | help: copy the `where` clause predicates from the trait | -LL | where Self: 'b; - | ~~~~~~~~~~~~~~ +LL - where +LL - 'b: 'a; +LL + where Self: 'b; + | warning: impl trait in impl method signature does not match trait method signature --> $DIR/bad-item-bound-within-rpitit.rs:18:28 @@ -26,8 +28,9 @@ LL | fn iter(&self) -> impl 'a + Iterator> { = note: `#[warn(refining_impl_trait_reachable)]` on by default help: replace the return type so that it matches the trait | -LL | fn iter(&self) -> impl Iterator::Item<'_>> + '_ { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - fn iter(&self) -> impl 'a + Iterator> { +LL + fn iter(&self) -> impl Iterator::Item<'_>> + '_ { + | error: aborting due to 1 previous error; 1 warning emitted diff --git a/tests/ui/impl-trait/in-trait/cycle-effective-visibilities-during-dyn-compatibility-check.rs b/tests/ui/impl-trait/in-trait/cycle-effective-visibilities-during-dyn-compatibility-check.rs index daf29a0005dec..d6fa34419d2b2 100644 --- a/tests/ui/impl-trait/in-trait/cycle-effective-visibilities-during-dyn-compatibility-check.rs +++ b/tests/ui/impl-trait/in-trait/cycle-effective-visibilities-during-dyn-compatibility-check.rs @@ -14,13 +14,13 @@ impl MyTrait for Outer { } impl dyn MyTrait { - //~^ ERROR the trait `MyTrait` cannot be made into an object + //~^ ERROR the trait `MyTrait` is not dyn compatible fn other(&self) -> impl Marker { - //~^ ERROR the trait `MyTrait` cannot be made into an object + //~^ ERROR the trait `MyTrait` is not dyn compatible MyTrait::foo(&self) //~^ ERROR the trait bound `&dyn MyTrait: MyTrait` is not satisfied //~| ERROR the trait bound `&dyn MyTrait: MyTrait` is not satisfied - //~| ERROR the trait `MyTrait` cannot be made into an object + //~| ERROR the trait `MyTrait` is not dyn compatible } } diff --git a/tests/ui/impl-trait/in-trait/cycle-effective-visibilities-during-dyn-compatibility-check.stderr b/tests/ui/impl-trait/in-trait/cycle-effective-visibilities-during-dyn-compatibility-check.stderr index a975b6204aa5a..61fe9432a1e99 100644 --- a/tests/ui/impl-trait/in-trait/cycle-effective-visibilities-during-dyn-compatibility-check.stderr +++ b/tests/ui/impl-trait/in-trait/cycle-effective-visibilities-during-dyn-compatibility-check.stderr @@ -8,21 +8,22 @@ LL | MyTrait::foo(&self) | = help: the trait `MyTrait` is implemented for `Outer` -error[E0038]: the trait `MyTrait` cannot be made into an object +error[E0038]: the trait `MyTrait` is not dyn compatible --> $DIR/cycle-effective-visibilities-during-dyn-compatibility-check.rs:20:9 | LL | MyTrait::foo(&self) - | ^^^^^^^^^^^^ `MyTrait` cannot be made into an object + | ^^^^^^^^^^^^ `MyTrait` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/cycle-effective-visibilities-during-dyn-compatibility-check.rs:5:22 | LL | trait MyTrait { - | ------- this trait cannot be made into an object... + | ------- this trait is not dyn compatible... LL | fn foo(&self) -> impl Marker; | ^^^^^^^^^^^ ...because method `foo` references an `impl Trait` type in its return type = help: consider moving `foo` to another trait - = help: only type `Outer` implements the trait, consider using it directly instead + = help: only type `Outer` implements `MyTrait`; consider using it directly instead. error[E0277]: the trait bound `&dyn MyTrait: MyTrait` is not satisfied --> $DIR/cycle-effective-visibilities-during-dyn-compatibility-check.rs:20:9 @@ -32,37 +33,39 @@ LL | MyTrait::foo(&self) | = help: the trait `MyTrait` is implemented for `Outer` -error[E0038]: the trait `MyTrait` cannot be made into an object +error[E0038]: the trait `MyTrait` is not dyn compatible --> $DIR/cycle-effective-visibilities-during-dyn-compatibility-check.rs:16:6 | LL | impl dyn MyTrait { - | ^^^^^^^^^^^ `MyTrait` cannot be made into an object + | ^^^^^^^^^^^ `MyTrait` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/cycle-effective-visibilities-during-dyn-compatibility-check.rs:5:22 | LL | trait MyTrait { - | ------- this trait cannot be made into an object... + | ------- this trait is not dyn compatible... LL | fn foo(&self) -> impl Marker; | ^^^^^^^^^^^ ...because method `foo` references an `impl Trait` type in its return type = help: consider moving `foo` to another trait - = help: only type `Outer` implements the trait, consider using it directly instead + = help: only type `Outer` implements `MyTrait`; consider using it directly instead. -error[E0038]: the trait `MyTrait` cannot be made into an object +error[E0038]: the trait `MyTrait` is not dyn compatible --> $DIR/cycle-effective-visibilities-during-dyn-compatibility-check.rs:18:15 | LL | fn other(&self) -> impl Marker { - | ^^^^ `MyTrait` cannot be made into an object + | ^^^^ `MyTrait` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/cycle-effective-visibilities-during-dyn-compatibility-check.rs:5:22 | LL | trait MyTrait { - | ------- this trait cannot be made into an object... + | ------- this trait is not dyn compatible... LL | fn foo(&self) -> impl Marker; | ^^^^^^^^^^^ ...because method `foo` references an `impl Trait` type in its return type = help: consider moving `foo` to another trait - = help: only type `Outer` implements the trait, consider using it directly instead + = help: only type `Outer` implements `MyTrait`; consider using it directly instead. error: aborting due to 5 previous errors diff --git a/tests/ui/impl-trait/in-trait/dont-consider-unconstrained-rpitits.rs b/tests/ui/impl-trait/in-trait/dont-consider-unconstrained-rpitits.rs new file mode 100644 index 0000000000000..fe73306966e62 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/dont-consider-unconstrained-rpitits.rs @@ -0,0 +1,14 @@ +// There's a suggestion that turns `Iterator` into `Iterator` +// if we have more generics than the trait wants. Let's not consider RPITITs +// for this, since that makes no sense right now. + +trait Foo { + fn bar(self) -> impl Sized; +} + +impl Foo for () { + //~^ ERROR trait takes 0 generic arguments but 1 generic argument was supplied + fn bar(self) -> impl Sized {} +} + +fn main() {} diff --git a/tests/ui/impl-trait/in-trait/dont-consider-unconstrained-rpitits.stderr b/tests/ui/impl-trait/in-trait/dont-consider-unconstrained-rpitits.stderr new file mode 100644 index 0000000000000..fb497b89c5f7b --- /dev/null +++ b/tests/ui/impl-trait/in-trait/dont-consider-unconstrained-rpitits.stderr @@ -0,0 +1,17 @@ +error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/dont-consider-unconstrained-rpitits.rs:9:6 + | +LL | impl Foo for () { + | ^^^---- help: remove the unnecessary generics + | | + | expected 0 generic arguments + | +note: trait defined here, with 0 generic parameters + --> $DIR/dont-consider-unconstrained-rpitits.rs:5:7 + | +LL | trait Foo { + | ^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0107`. diff --git a/tests/ui/impl-trait/in-trait/dump.rs b/tests/ui/impl-trait/in-trait/dump.rs new file mode 100644 index 0000000000000..47198d5115058 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/dump.rs @@ -0,0 +1,15 @@ +//@ compile-flags: -Zverbose-internals + +#![feature(precise_capturing_in_traits, rustc_attrs)] +#![rustc_hidden_type_of_opaques] + +trait Foo { + fn hello(&self) -> impl Sized; +} + +fn hello<'s, T: Foo>(x: &'s T) -> impl Sized + use<'s, T> { +//~^ ERROR ::{synthetic#0}<'s/#1> + x.hello() +} + +fn main() {} diff --git a/tests/ui/impl-trait/in-trait/dump.stderr b/tests/ui/impl-trait/in-trait/dump.stderr new file mode 100644 index 0000000000000..958058403852d --- /dev/null +++ b/tests/ui/impl-trait/in-trait/dump.stderr @@ -0,0 +1,8 @@ +error: ::{synthetic#0}<'s/#1> + --> $DIR/dump.rs:10:35 + | +LL | fn hello<'s, T: Foo>(x: &'s T) -> impl Sized + use<'s, T> { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/impl-trait/in-trait/dyn-compatibility.rs b/tests/ui/impl-trait/in-trait/dyn-compatibility.rs index 5cca4ad839c26..85b1ba269fc8c 100644 --- a/tests/ui/impl-trait/in-trait/dyn-compatibility.rs +++ b/tests/ui/impl-trait/in-trait/dyn-compatibility.rs @@ -12,9 +12,9 @@ impl Foo for u32 { fn main() { let i = Box::new(42_u32) as Box; - //~^ ERROR the trait `Foo` cannot be made into an object - //~| ERROR the trait `Foo` cannot be made into an object + //~^ ERROR the trait `Foo` is not dyn compatible + //~| ERROR the trait `Foo` is not dyn compatible let s = i.baz(); - //~^ ERROR the trait `Foo` cannot be made into an object - //~| ERROR the trait `Foo` cannot be made into an object + //~^ ERROR the trait `Foo` is not dyn compatible + //~| ERROR the trait `Foo` is not dyn compatible } diff --git a/tests/ui/impl-trait/in-trait/dyn-compatibility.stderr b/tests/ui/impl-trait/in-trait/dyn-compatibility.stderr index 115cb014b8c34..840c27e183f09 100644 --- a/tests/ui/impl-trait/in-trait/dyn-compatibility.stderr +++ b/tests/ui/impl-trait/in-trait/dyn-compatibility.stderr @@ -1,66 +1,70 @@ -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/dyn-compatibility.rs:14:33 | LL | let i = Box::new(42_u32) as Box; - | ^^^^^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/dyn-compatibility.rs:4:22 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn baz(&self) -> impl Debug; | ^^^^^^^^^^ ...because method `baz` references an `impl Trait` type in its return type = help: consider moving `baz` to another trait - = help: only type `u32` implements the trait, consider using it directly instead + = help: only type `u32` implements `Foo`; consider using it directly instead. -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/dyn-compatibility.rs:17:15 | LL | let s = i.baz(); - | ^^^ `Foo` cannot be made into an object + | ^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/dyn-compatibility.rs:4:22 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn baz(&self) -> impl Debug; | ^^^^^^^^^^ ...because method `baz` references an `impl Trait` type in its return type = help: consider moving `baz` to another trait - = help: only type `u32` implements the trait, consider using it directly instead + = help: only type `u32` implements `Foo`; consider using it directly instead. -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/dyn-compatibility.rs:17:13 | LL | let s = i.baz(); - | ^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/dyn-compatibility.rs:4:22 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn baz(&self) -> impl Debug; | ^^^^^^^^^^ ...because method `baz` references an `impl Trait` type in its return type = help: consider moving `baz` to another trait - = help: only type `u32` implements the trait, consider using it directly instead + = help: only type `u32` implements `Foo`; consider using it directly instead. -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/dyn-compatibility.rs:14:13 | LL | let i = Box::new(42_u32) as Box; - | ^^^^^^^^^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/dyn-compatibility.rs:4:22 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn baz(&self) -> impl Debug; | ^^^^^^^^^^ ...because method `baz` references an `impl Trait` type in its return type = help: consider moving `baz` to another trait - = help: only type `u32` implements the trait, consider using it directly instead + = help: only type `u32` implements `Foo`; consider using it directly instead. = note: required for the cast from `Box` to `Box` error: aborting due to 4 previous errors diff --git a/tests/ui/impl-trait/in-trait/expeced-refree-to-map-to-reearlybound-ice-108580.stderr b/tests/ui/impl-trait/in-trait/expeced-refree-to-map-to-reearlybound-ice-108580.stderr index 94f068cabaa82..b7797317ea68f 100644 --- a/tests/ui/impl-trait/in-trait/expeced-refree-to-map-to-reearlybound-ice-108580.stderr +++ b/tests/ui/impl-trait/in-trait/expeced-refree-to-map-to-reearlybound-ice-108580.stderr @@ -12,8 +12,9 @@ LL | fn bar(&self) -> impl Iterator + '_ { = note: `#[warn(refining_impl_trait_internal)]` on by default help: replace the return type so that it matches the trait | -LL | fn bar(&self) -> impl Iterator + '_ { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - fn bar(&self) -> impl Iterator + '_ { +LL + fn bar(&self) -> impl Iterator + '_ { + | warning: 1 warning emitted diff --git a/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.current.stderr b/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.current.stderr index e38e18857ef66..b6e7e02f3316c 100644 --- a/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.current.stderr +++ b/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.current.stderr @@ -19,11 +19,11 @@ help: consider further restricting type parameter `F` with trait `MyFn` LL | F: Callback + MyFn, | +++++++++++ -error[E0277]: the trait bound `F: MyFn` is not satisfied - --> $DIR/false-positive-predicate-entailment-error.rs:36:30 +error[E0277]: the trait bound `F: Callback` is not satisfied + --> $DIR/false-positive-predicate-entailment-error.rs:42:12 | -LL | fn autobatch(self) -> impl Trait - | ^^^^^^^^^^ the trait `MyFn` is not implemented for `F` +LL | F: Callback, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MyFn` is not implemented for `F` | note: required for `F` to implement `Callback` --> $DIR/false-positive-predicate-entailment-error.rs:14:21 @@ -32,14 +32,14 @@ LL | impl> Callback for F { | ------- ^^^^^^^^^^^ ^ | | | unsatisfied trait bound introduced here -note: required by a bound in `::autobatch` - --> $DIR/false-positive-predicate-entailment-error.rs:43:12 +note: the requirement `F: Callback` appears on the `impl`'s method `autobatch` but not on the corresponding trait's method + --> $DIR/false-positive-predicate-entailment-error.rs:25:8 | -LL | fn autobatch(self) -> impl Trait - | --------- required by a bound in this associated function +LL | trait ChannelSender { + | ------------- in this trait ... -LL | F: Callback, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `::autobatch` +LL | fn autobatch(self) -> impl Trait + | ^^^^^^^^^ this trait's method doesn't have the requirement `F: Callback` help: consider further restricting type parameter `F` with trait `MyFn` | LL | F: Callback + MyFn, @@ -118,7 +118,7 @@ LL | F: Callback + MyFn, | +++++++++++ error[E0277]: the trait bound `F: MyFn` is not satisfied - --> $DIR/false-positive-predicate-entailment-error.rs:43:12 + --> $DIR/false-positive-predicate-entailment-error.rs:42:12 | LL | F: Callback, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MyFn` is not implemented for `F` diff --git a/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.rs b/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.rs index 2987d183e0401..cbe6c32b8901c 100644 --- a/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.rs +++ b/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.rs @@ -38,11 +38,11 @@ impl ChannelSender for Sender { //[current]~| ERROR the trait bound `F: MyFn` is not satisfied //[current]~| ERROR the trait bound `F: MyFn` is not satisfied //[current]~| ERROR the trait bound `F: MyFn` is not satisfied - //[current]~| ERROR the trait bound `F: MyFn` is not satisfied where F: Callback, //[current]~^ ERROR the trait bound `F: MyFn` is not satisfied - { + //[current]~| ERROR the trait bound `F: Callback` is not satisfied + { Thing } } diff --git a/tests/ui/impl-trait/in-trait/foreign-dyn-error.rs b/tests/ui/impl-trait/in-trait/foreign-dyn-error.rs index 600dba03b74b9..5b891170a4f10 100644 --- a/tests/ui/impl-trait/in-trait/foreign-dyn-error.rs +++ b/tests/ui/impl-trait/in-trait/foreign-dyn-error.rs @@ -4,5 +4,5 @@ extern crate rpitit; fn main() { let _: &dyn rpitit::Foo = todo!(); - //~^ ERROR the trait `Foo` cannot be made into an object + //~^ ERROR the trait `Foo` is not dyn compatible } diff --git a/tests/ui/impl-trait/in-trait/foreign-dyn-error.stderr b/tests/ui/impl-trait/in-trait/foreign-dyn-error.stderr index 895d8686742b3..29235ca78a504 100644 --- a/tests/ui/impl-trait/in-trait/foreign-dyn-error.stderr +++ b/tests/ui/impl-trait/in-trait/foreign-dyn-error.stderr @@ -1,15 +1,16 @@ -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/foreign-dyn-error.rs:6:12 | LL | let _: &dyn rpitit::Foo = todo!(); - | ^^^^^^^^^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/auxiliary/rpitit.rs:4:21 | LL | fn bar(self) -> impl Deref; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait cannot be made into an object because method `bar` references an `impl Trait` type in its return type - = help: only type `rpitit::Foreign` implements the trait, consider using it directly instead + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait is not dyn compatible because method `bar` references an `impl Trait` type in its return type + = help: only type `rpitit::Foreign` implements `Foo`; consider using it directly instead. error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/in-trait/foreign.stderr b/tests/ui/impl-trait/in-trait/foreign.stderr index 36114dcf02b6c..8801ccc68b3be 100644 --- a/tests/ui/impl-trait/in-trait/foreign.stderr +++ b/tests/ui/impl-trait/in-trait/foreign.stderr @@ -14,8 +14,9 @@ LL | #[warn(refining_impl_trait)] = note: `#[warn(refining_impl_trait_internal)]` implied by `#[warn(refining_impl_trait)]` help: replace the return type so that it matches the trait | -LL | fn bar(self) -> impl Deref { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - fn bar(self) -> Arc { +LL + fn bar(self) -> impl Deref { + | warning: impl trait in impl method signature does not match trait method signature --> $DIR/foreign.rs:31:21 @@ -32,8 +33,9 @@ LL | #[warn(refining_impl_trait)] | ^^^^^^^^^^^^^^^^^^^ help: replace the return type so that it matches the trait | -LL | fn bar(self) -> impl Deref { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - fn bar(self) -> Arc { +LL + fn bar(self) -> impl Deref { + | warning: 2 warnings emitted diff --git a/tests/ui/impl-trait/in-trait/method-signature-matches.lt.stderr b/tests/ui/impl-trait/in-trait/method-signature-matches.lt.stderr index a23879eb6c376..97f5b2af862e0 100644 --- a/tests/ui/impl-trait/in-trait/method-signature-matches.lt.stderr +++ b/tests/ui/impl-trait/in-trait/method-signature-matches.lt.stderr @@ -15,8 +15,9 @@ LL | fn early<'early, T>(x: &'early T) -> impl Sized; found signature `fn(&())` help: change the parameter type to match the trait | -LL | fn early<'late, T>(_: &'early T) {} - | ~~~~~~~~~ +LL - fn early<'late, T>(_: &'late ()) {} +LL + fn early<'late, T>(_: &'early T) {} + | error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/in-trait/method-signature-matches.mismatch.stderr b/tests/ui/impl-trait/in-trait/method-signature-matches.mismatch.stderr index 9e18517e48c55..588b453761003 100644 --- a/tests/ui/impl-trait/in-trait/method-signature-matches.mismatch.stderr +++ b/tests/ui/impl-trait/in-trait/method-signature-matches.mismatch.stderr @@ -13,8 +13,9 @@ LL | fn owo(x: ()) -> impl Sized; found signature `fn(u8)` help: change the parameter type to match the trait | -LL | fn owo(_: ()) {} - | ~~ +LL - fn owo(_: u8) {} +LL + fn owo(_: ()) {} + | error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/in-trait/method-signature-matches.mismatch_async.stderr b/tests/ui/impl-trait/in-trait/method-signature-matches.mismatch_async.stderr index c01d7322d117e..b0530045bea9a 100644 --- a/tests/ui/impl-trait/in-trait/method-signature-matches.mismatch_async.stderr +++ b/tests/ui/impl-trait/in-trait/method-signature-matches.mismatch_async.stderr @@ -13,8 +13,9 @@ LL | async fn owo(x: ()) {} found signature `fn(u8) -> _` help: change the parameter type to match the trait | -LL | async fn owo(_: ()) {} - | ~~ +LL - async fn owo(_: u8) {} +LL + async fn owo(_: ()) {} + | error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/in-trait/mismatched-where-clauses.rs b/tests/ui/impl-trait/in-trait/mismatched-where-clauses.rs new file mode 100644 index 0000000000000..a2c735cc126a3 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/mismatched-where-clauses.rs @@ -0,0 +1,12 @@ +trait Foo { + fn foo(s: S) -> impl Sized; +} + +trait Bar {} + +impl Foo for () { + fn foo(s: S) -> impl Sized where S: Bar {} + //~^ ERROR impl has stricter requirements than trait +} + +fn main() {} diff --git a/tests/ui/impl-trait/in-trait/mismatched-where-clauses.stderr b/tests/ui/impl-trait/in-trait/mismatched-where-clauses.stderr new file mode 100644 index 0000000000000..cc6e027cad775 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/mismatched-where-clauses.stderr @@ -0,0 +1,12 @@ +error[E0276]: impl has stricter requirements than trait + --> $DIR/mismatched-where-clauses.rs:8:44 + | +LL | fn foo(s: S) -> impl Sized; + | ------------------------------ definition of `foo` from trait +... +LL | fn foo(s: S) -> impl Sized where S: Bar {} + | ^^^ impl has extra requirement `S: Bar` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0276`. diff --git a/tests/ui/impl-trait/in-trait/opaque-and-lifetime-mismatch.stderr b/tests/ui/impl-trait/in-trait/opaque-and-lifetime-mismatch.stderr index 81570781b27da..d575fedbb58de 100644 --- a/tests/ui/impl-trait/in-trait/opaque-and-lifetime-mismatch.stderr +++ b/tests/ui/impl-trait/in-trait/opaque-and-lifetime-mismatch.stderr @@ -86,8 +86,9 @@ LL | fn bar() -> Wrapper; found signature `fn() -> i32` help: change the output type to match the trait | -LL | fn bar() -> Wrapper<'static> { - | ~~~~~~~~~~~~~~~~ +LL - fn bar() -> i32 { +LL + fn bar() -> Wrapper<'static> { + | error[E0107]: struct takes 0 generic arguments but 1 generic argument was supplied --> $DIR/opaque-and-lifetime-mismatch.rs:24:17 diff --git a/tests/ui/impl-trait/in-trait/refine-captures.stderr b/tests/ui/impl-trait/in-trait/refine-captures.stderr index ad2c2a11601db..8a5c8d3c77b0f 100644 --- a/tests/ui/impl-trait/in-trait/refine-captures.stderr +++ b/tests/ui/impl-trait/in-trait/refine-captures.stderr @@ -9,8 +9,9 @@ LL | fn test() -> impl Sized + use<> {} = note: `#[warn(refining_impl_trait_internal)]` on by default help: modify the `use<..>` bound to capture the same lifetimes that the trait does | -LL | fn test() -> impl Sized + use<'a> {} - | ~~~~~~~ +LL - fn test() -> impl Sized + use<> {} +LL + fn test() -> impl Sized + use<'a> {} + | warning: impl trait in impl method captures fewer lifetimes than in trait --> $DIR/refine-captures.rs:22:31 @@ -22,8 +23,9 @@ LL | fn test() -> impl Sized + use<> {} = note: we are soliciting feedback, see issue #121718 for more information help: modify the `use<..>` bound to capture the same lifetimes that the trait does | -LL | fn test() -> impl Sized + use<'a> {} - | ~~~~~~~ +LL - fn test() -> impl Sized + use<> {} +LL + fn test() -> impl Sized + use<'a> {} + | warning: impl trait in impl method captures fewer lifetimes than in trait --> $DIR/refine-captures.rs:27:31 @@ -35,8 +37,9 @@ LL | fn test() -> impl Sized + use<'b> {} = note: we are soliciting feedback, see issue #121718 for more information help: modify the `use<..>` bound to capture the same lifetimes that the trait does | -LL | fn test() -> impl Sized + use<'a, 'b> {} - | ~~~~~~~~~~~ +LL - fn test() -> impl Sized + use<'b> {} +LL + fn test() -> impl Sized + use<'a, 'b> {} + | error: `impl Trait` must mention all type parameters in scope in `use<...>` --> $DIR/refine-captures.rs:32:18 diff --git a/tests/ui/impl-trait/in-trait/refine-resolution-errors.rs b/tests/ui/impl-trait/in-trait/refine-resolution-errors.rs index a9936c7bc3fee..894f592d9e204 100644 --- a/tests/ui/impl-trait/in-trait/refine-resolution-errors.rs +++ b/tests/ui/impl-trait/in-trait/refine-resolution-errors.rs @@ -13,7 +13,6 @@ impl Mirror for () { pub trait First { async fn first() -> <() as Mirror>::Assoc; - //~^ ERROR type annotations needed } impl First for () { diff --git a/tests/ui/impl-trait/in-trait/refine-resolution-errors.stderr b/tests/ui/impl-trait/in-trait/refine-resolution-errors.stderr index 0f5573dda04c1..10ebad2a7d5a1 100644 --- a/tests/ui/impl-trait/in-trait/refine-resolution-errors.stderr +++ b/tests/ui/impl-trait/in-trait/refine-resolution-errors.stderr @@ -4,13 +4,6 @@ error[E0207]: the type parameter `T` is not constrained by the impl trait, self LL | impl Mirror for () { | ^ unconstrained type parameter -error[E0282]: type annotations needed - --> $DIR/refine-resolution-errors.rs:15:5 - | -LL | async fn first() -> <() as Mirror>::Assoc; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0207, E0282. -For more information about an error, try `rustc --explain E0207`. +For more information about this error, try `rustc --explain E0207`. diff --git a/tests/ui/impl-trait/in-trait/refine.stderr b/tests/ui/impl-trait/in-trait/refine.stderr index 8d30b03592166..e381ef8c3f711 100644 --- a/tests/ui/impl-trait/in-trait/refine.stderr +++ b/tests/ui/impl-trait/in-trait/refine.stderr @@ -17,8 +17,9 @@ LL | #![deny(refining_impl_trait)] = note: `#[deny(refining_impl_trait_reachable)]` implied by `#[deny(refining_impl_trait)]` help: replace the return type so that it matches the trait | -LL | fn bar() -> impl Sized {} - | ~~~~~~~~~~ +LL - fn bar() -> impl Copy {} +LL + fn bar() -> impl Sized {} + | error: impl trait in impl method signature does not match trait method signature --> $DIR/refine.rs:15:5 @@ -49,8 +50,9 @@ LL | fn bar() -> () {} = note: we are soliciting feedback, see issue #121718 for more information help: replace the return type so that it matches the trait | -LL | fn bar() -> impl Sized {} - | ~~~~~~~~~~ +LL - fn bar() -> () {} +LL + fn bar() -> impl Sized {} + | error: impl trait in impl method signature does not match trait method signature --> $DIR/refine.rs:27:17 @@ -66,8 +68,9 @@ LL | fn bar() -> () {} = note: `#[deny(refining_impl_trait_internal)]` implied by `#[deny(refining_impl_trait)]` help: replace the return type so that it matches the trait | -LL | fn bar() -> impl Sized {} - | ~~~~~~~~~~ +LL - fn bar() -> () {} +LL + fn bar() -> impl Sized {} + | error: impl trait in impl method signature does not match trait method signature --> $DIR/refine.rs:35:17 @@ -82,8 +85,9 @@ LL | fn bar() -> () {} = note: we are soliciting feedback, see issue #121718 for more information help: replace the return type so that it matches the trait | -LL | fn bar() -> impl Sized {} - | ~~~~~~~~~~ +LL - fn bar() -> () {} +LL + fn bar() -> impl Sized {} + | error: impl trait in impl method signature does not match trait method signature --> $DIR/refine.rs:45:27 @@ -98,8 +102,9 @@ LL | fn bar(&self) -> impl Copy + '_ {} = note: we are soliciting feedback, see issue #121718 for more information help: replace the return type so that it matches the trait | -LL | fn bar(&self) -> impl Sized + '_ {} - | ~~~~~~~~~~~~~~~ +LL - fn bar(&self) -> impl Copy + '_ {} +LL + fn bar(&self) -> impl Sized + '_ {} + | error: impl trait in impl method signature does not match trait method signature --> $DIR/refine.rs:56:9 diff --git a/tests/ui/impl-trait/in-trait/return-dont-satisfy-bounds.rs b/tests/ui/impl-trait/in-trait/return-dont-satisfy-bounds.rs index ee47de2c73283..ff265e576b90f 100644 --- a/tests/ui/impl-trait/in-trait/return-dont-satisfy-bounds.rs +++ b/tests/ui/impl-trait/in-trait/return-dont-satisfy-bounds.rs @@ -8,7 +8,7 @@ impl Foo for Bar { fn foo>(self) -> impl Foo { //~^ ERROR: the trait bound `impl Foo: Foo` is not satisfied [E0277] //~| ERROR: the trait bound `Bar: Foo` is not satisfied [E0277] - //~| ERROR: the trait bound `F2: Foo` is not satisfied + //~| ERROR: impl has stricter requirements than trait self } } diff --git a/tests/ui/impl-trait/in-trait/return-dont-satisfy-bounds.stderr b/tests/ui/impl-trait/in-trait/return-dont-satisfy-bounds.stderr index 663c9a7f2aeda..5cb80386b35f0 100644 --- a/tests/ui/impl-trait/in-trait/return-dont-satisfy-bounds.stderr +++ b/tests/ui/impl-trait/in-trait/return-dont-satisfy-bounds.stderr @@ -1,3 +1,12 @@ +error[E0276]: impl has stricter requirements than trait + --> $DIR/return-dont-satisfy-bounds.rs:8:16 + | +LL | fn foo(self) -> impl Foo; + | -------------------------------- definition of `foo` from trait +... +LL | fn foo>(self) -> impl Foo { + | ^^^^^^^ impl has extra requirement `F2: Foo` + error[E0277]: the trait bound `impl Foo: Foo` is not satisfied --> $DIR/return-dont-satisfy-bounds.rs:8:34 | @@ -11,18 +20,6 @@ note: required by a bound in `Foo::{synthetic#0}` LL | fn foo(self) -> impl Foo; | ^^^^^^ required by this bound in `Foo::{synthetic#0}` -error[E0277]: the trait bound `F2: Foo` is not satisfied - --> $DIR/return-dont-satisfy-bounds.rs:8:34 - | -LL | fn foo>(self) -> impl Foo { - | ^^^^^^^^^^^^ the trait `Foo` is not implemented for `F2` - | -note: required by a bound in `>::foo` - --> $DIR/return-dont-satisfy-bounds.rs:8:16 - | -LL | fn foo>(self) -> impl Foo { - | ^^^^^^^ required by this bound in `>::foo` - error[E0277]: the trait bound `Bar: Foo` is not satisfied --> $DIR/return-dont-satisfy-bounds.rs:8:34 | @@ -38,4 +35,5 @@ LL | self error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0276, E0277. +For more information about an error, try `rustc --explain E0276`. diff --git a/tests/ui/impl-trait/in-trait/rpitit-hidden-types-self-implied-wf-via-param.rs b/tests/ui/impl-trait/in-trait/rpitit-hidden-types-self-implied-wf-via-param.rs index 37b0b2297760a..7a3a59d37c6af 100644 --- a/tests/ui/impl-trait/in-trait/rpitit-hidden-types-self-implied-wf-via-param.rs +++ b/tests/ui/impl-trait/in-trait/rpitit-hidden-types-self-implied-wf-via-param.rs @@ -4,9 +4,9 @@ trait Extend { impl Extend for () { fn extend<'a: 'a>(s: &'a str) -> (Option<&'static &'a ()>, &'static str) - //~^ ERROR in type `&'static &'a ()`, reference has a longer lifetime than the data it references where 'a: 'static, + //~^ impl has stricter requirements than trait { (None, s) } diff --git a/tests/ui/impl-trait/in-trait/rpitit-hidden-types-self-implied-wf-via-param.stderr b/tests/ui/impl-trait/in-trait/rpitit-hidden-types-self-implied-wf-via-param.stderr index 5ace64b690351..dfcf11c57ddc1 100644 --- a/tests/ui/impl-trait/in-trait/rpitit-hidden-types-self-implied-wf-via-param.stderr +++ b/tests/ui/impl-trait/in-trait/rpitit-hidden-types-self-implied-wf-via-param.stderr @@ -1,16 +1,19 @@ -error[E0491]: in type `&'static &'a ()`, reference has a longer lifetime than the data it references - --> $DIR/rpitit-hidden-types-self-implied-wf-via-param.rs:6:38 +error[E0276]: impl has stricter requirements than trait + --> $DIR/rpitit-hidden-types-self-implied-wf-via-param.rs:8:13 | -LL | fn extend<'a: 'a>(s: &'a str) -> (Option<&'static &'a ()>, &'static str) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn extend<'a: 'a>(_: &'a str) -> (impl Sized + 'a, &'static str); + | ----------------------------------------------------------------- definition of `extend` from trait +... +LL | 'a: 'static, + | ^^^^^^^ impl has extra requirement `'a: 'static` | - = note: the pointer is valid for the static lifetime -note: but the referenced data is only valid for the lifetime `'a` as defined here - --> $DIR/rpitit-hidden-types-self-implied-wf-via-param.rs:6:15 +help: copy the `where` clause predicates from the trait + | +LL - where +LL - 'a: 'static, +LL + where 'a: 'a | -LL | fn extend<'a: 'a>(s: &'a str) -> (Option<&'static &'a ()>, &'static str) - | ^^ error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0491`. +For more information about this error, try `rustc --explain E0276`. diff --git a/tests/ui/impl-trait/in-trait/specialization-broken.stderr b/tests/ui/impl-trait/in-trait/specialization-broken.stderr index 8c9f265601540..fbbe1ac40b9ef 100644 --- a/tests/ui/impl-trait/in-trait/specialization-broken.stderr +++ b/tests/ui/impl-trait/in-trait/specialization-broken.stderr @@ -16,8 +16,9 @@ LL | fn bar(&self) -> impl Sized; found signature `fn(&_) -> U` help: change the output type to match the trait | -LL | fn bar(&self) -> impl Sized { - | ~~~~~~~~~~ +LL - fn bar(&self) -> U { +LL + fn bar(&self) -> impl Sized { + | error: method with return-position `impl Trait` in trait cannot be specialized --> $DIR/specialization-broken.rs:15:5 diff --git a/tests/ui/impl-trait/issue-102605.stderr b/tests/ui/impl-trait/issue-102605.stderr index dcb22797173cb..ed6663fa61f93 100644 --- a/tests/ui/impl-trait/issue-102605.stderr +++ b/tests/ui/impl-trait/issue-102605.stderr @@ -14,20 +14,11 @@ LL | convert_result(foo()) | | | arguments to this function are incorrect | -note: calling an async function returns a future - --> $DIR/issue-102605.rs:13:20 - | -LL | convert_result(foo()) - | ^^^^^ note: function defined here --> $DIR/issue-102605.rs:7:4 | LL | fn convert_result(r: Result) -> Option { | ^^^^^^^^^^^^^^ --------------- -help: consider `await`ing on the `Future` - | -LL | convert_result(foo().await) - | ++++++ help: try wrapping the expression in `Err` | LL | convert_result(Err(foo())) diff --git a/tests/ui/impl-trait/issue-72911.stderr b/tests/ui/impl-trait/issue-72911.stderr index 0e86561aa2779..063b7f68dc02a 100644 --- a/tests/ui/impl-trait/issue-72911.stderr +++ b/tests/ui/impl-trait/issue-72911.stderr @@ -1,14 +1,18 @@ -error[E0433]: failed to resolve: use of undeclared crate or module `foo` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `foo` --> $DIR/issue-72911.rs:11:33 | LL | fn gather_from_file(dir_entry: &foo::MissingItem) -> impl Iterator { - | ^^^ use of undeclared crate or module `foo` + | ^^^ use of unresolved module or unlinked crate `foo` + | + = help: you might be missing a crate named `foo` -error[E0433]: failed to resolve: use of undeclared crate or module `foo` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `foo` --> $DIR/issue-72911.rs:16:41 | LL | fn lint_files() -> impl Iterator { - | ^^^ use of undeclared crate or module `foo` + | ^^^ use of unresolved module or unlinked crate `foo` + | + = help: you might be missing a crate named `foo` error: aborting due to 2 previous errors diff --git a/tests/ui/impl-trait/issue-99073-2.stderr b/tests/ui/impl-trait/issue-99073-2.stderr index 06b2b84569f69..0bcac7c7c534b 100644 --- a/tests/ui/impl-trait/issue-99073-2.stderr +++ b/tests/ui/impl-trait/issue-99073-2.stderr @@ -1,12 +1,3 @@ -error[E0792]: expected generic type parameter, found `i32` - --> $DIR/issue-99073-2.rs:9:22 - | -LL | fn test(t: T, recurse: bool) -> impl Display { - | - this generic parameter must be used with a generic type parameter -LL | let f = || { -LL | let i: u32 = test::(-1, false); - | ^^^^^^^^^^^^^^^^^^^^^^ - error: concrete type differs from previous defining opaque type use --> $DIR/issue-99073-2.rs:9:22 | @@ -19,6 +10,15 @@ note: previous use here LL | fn test(t: T, recurse: bool) -> impl Display { | ^^^^^^^^^^^^ +error[E0792]: expected generic type parameter, found `i32` + --> $DIR/issue-99073-2.rs:9:22 + | +LL | fn test(t: T, recurse: bool) -> impl Display { + | - this generic parameter must be used with a generic type parameter +LL | let f = || { +LL | let i: u32 = test::(-1, false); + | ^^^^^^^^^^^^^^^^^^^^^^ + error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/impl-trait/issue-99073.stderr b/tests/ui/impl-trait/issue-99073.stderr index 3c32f1794a0e0..19854ef894051 100644 --- a/tests/ui/impl-trait/issue-99073.stderr +++ b/tests/ui/impl-trait/issue-99073.stderr @@ -1,11 +1,3 @@ -error[E0792]: expected generic type parameter, found `&F` - --> $DIR/issue-99073.rs:6:11 - | -LL | fn fix(f: F) -> impl Fn() { - | - this generic parameter must be used with a generic type parameter -LL | move || f(fix(&f)) - | ^^^^^^^^^^ - error: concrete type differs from previous defining opaque type use --> $DIR/issue-99073.rs:6:13 | @@ -18,6 +10,14 @@ note: previous use here LL | fn fix(f: F) -> impl Fn() { | ^^^^^^^^^ +error[E0792]: expected generic type parameter, found `&F` + --> $DIR/issue-99073.rs:6:11 + | +LL | fn fix(f: F) -> impl Fn() { + | - this generic parameter must be used with a generic type parameter +LL | move || f(fix(&f)) + | ^^^^^^^^^^ + error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/impl-trait/issues/issue-62742.stderr b/tests/ui/impl-trait/issues/issue-62742.stderr index 98d17b02536cc..ee4eb98f4eaf5 100644 --- a/tests/ui/impl-trait/issues/issue-62742.stderr +++ b/tests/ui/impl-trait/issues/issue-62742.stderr @@ -1,3 +1,17 @@ +error[E0277]: the trait bound `RawImpl<_>: Raw<_>` is not satisfied + --> $DIR/issue-62742.rs:4:5 + | +LL | WrongImpl::foo(0i32); + | ^^^^^^^^^ the trait `Raw<_>` is not implemented for `RawImpl<_>` + | + = help: the trait `Raw<_>` is not implemented for `RawImpl<_>` + but trait `Raw<[_]>` is implemented for it +note: required by a bound in `SafeImpl` + --> $DIR/issue-62742.rs:33:35 + | +LL | pub struct SafeImpl>(PhantomData<(A, T)>); + | ^^^^^^ required by this bound in `SafeImpl` + error[E0599]: the function or associated item `foo` exists for struct `SafeImpl<_, RawImpl<_>>`, but its trait bounds were not satisfied --> $DIR/issue-62742.rs:4:16 | @@ -23,14 +37,15 @@ note: the trait `Raw` must be implemented LL | pub trait Raw { | ^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: the trait bound `RawImpl<_>: Raw<_>` is not satisfied - --> $DIR/issue-62742.rs:4:5 +error[E0277]: the trait bound `RawImpl<()>: Raw<()>` is not satisfied + --> $DIR/issue-62742.rs:10:5 | -LL | WrongImpl::foo(0i32); - | ^^^^^^^^^ the trait `Raw<_>` is not implemented for `RawImpl<_>` +LL | WrongImpl::<()>::foo(0i32); + | ^^^^^^^^^^^^^^^ the trait `Raw<()>` is not implemented for `RawImpl<()>` | - = help: the trait `Raw<_>` is not implemented for `RawImpl<_>` - but trait `Raw<[_]>` is implemented for it + = help: the trait `Raw<()>` is not implemented for `RawImpl<()>` + but trait `Raw<[()]>` is implemented for it + = help: for that trait implementation, expected `[()]`, found `()` note: required by a bound in `SafeImpl` --> $DIR/issue-62742.rs:33:35 | @@ -62,21 +77,6 @@ note: the trait `Raw` must be implemented LL | pub trait Raw { | ^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: the trait bound `RawImpl<()>: Raw<()>` is not satisfied - --> $DIR/issue-62742.rs:10:5 - | -LL | WrongImpl::<()>::foo(0i32); - | ^^^^^^^^^^^^^^^ the trait `Raw<()>` is not implemented for `RawImpl<()>` - | - = help: the trait `Raw<()>` is not implemented for `RawImpl<()>` - but trait `Raw<[()]>` is implemented for it - = help: for that trait implementation, expected `[()]`, found `()` -note: required by a bound in `SafeImpl` - --> $DIR/issue-62742.rs:33:35 - | -LL | pub struct SafeImpl>(PhantomData<(A, T)>); - | ^^^^^^ required by this bound in `SafeImpl` - error: aborting due to 4 previous errors Some errors have detailed explanations: E0277, E0599. diff --git a/tests/ui/impl-trait/issues/issue-87340.rs b/tests/ui/impl-trait/issues/issue-87340.rs index b1baaaa6ba5c1..705a4addcb704 100644 --- a/tests/ui/impl-trait/issues/issue-87340.rs +++ b/tests/ui/impl-trait/issues/issue-87340.rs @@ -9,8 +9,6 @@ impl X for () { //~^ ERROR `T` is not constrained by the impl trait, self type, or predicates type I = impl Sized; fn f() -> Self::I {} - //~^ ERROR type annotations needed - //~| ERROR type annotations needed } fn main() {} diff --git a/tests/ui/impl-trait/issues/issue-87340.stderr b/tests/ui/impl-trait/issues/issue-87340.stderr index 1be4087be4242..8513cb2881e1f 100644 --- a/tests/ui/impl-trait/issues/issue-87340.stderr +++ b/tests/ui/impl-trait/issues/issue-87340.stderr @@ -4,19 +4,6 @@ error[E0207]: the type parameter `T` is not constrained by the impl trait, self LL | impl X for () { | ^ unconstrained type parameter -error[E0282]: type annotations needed - --> $DIR/issue-87340.rs:11:23 - | -LL | fn f() -> Self::I {} - | ^^ cannot infer type for type parameter `T` - -error[E0282]: type annotations needed - --> $DIR/issue-87340.rs:11:15 - | -LL | fn f() -> Self::I {} - | ^^^^^^^ cannot infer type for type parameter `T` - -error: aborting due to 3 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0207, E0282. -For more information about an error, try `rustc --explain E0207`. +For more information about this error, try `rustc --explain E0207`. diff --git a/tests/ui/impl-trait/multiple-defining-usages-in-body.stderr b/tests/ui/impl-trait/multiple-defining-usages-in-body.stderr index faa5d3ba4489d..aaa1958b58479 100644 --- a/tests/ui/impl-trait/multiple-defining-usages-in-body.stderr +++ b/tests/ui/impl-trait/multiple-defining-usages-in-body.stderr @@ -1,15 +1,3 @@ -warning: function cannot return without recursing - --> $DIR/multiple-defining-usages-in-body.rs:4:1 - | -LL | fn foo() -> impl Trait { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing -LL | -LL | let a: T = foo::(); - | ------------- recursive call site - | - = help: a `loop` may express intention better if this is on purpose - = note: `#[warn(unconditional_recursion)]` on by default - error: concrete type differs from previous defining opaque type use --> $DIR/multiple-defining-usages-in-body.rs:8:16 | @@ -22,5 +10,17 @@ note: previous use here LL | let a: T = foo::(); | ^^^^^^^^^^^^^ +warning: function cannot return without recursing + --> $DIR/multiple-defining-usages-in-body.rs:4:1 + | +LL | fn foo() -> impl Trait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing +LL | +LL | let a: T = foo::(); + | ------------- recursive call site + | + = help: a `loop` may express intention better if this is on purpose + = note: `#[warn(unconditional_recursion)]` on by default + error: aborting due to 1 previous error; 1 warning emitted diff --git a/tests/ui/impl-trait/multiple-lifetimes/error-handling.polonius.stderr b/tests/ui/impl-trait/multiple-lifetimes/error-handling.polonius.stderr deleted file mode 100644 index c511081a86fff..0000000000000 --- a/tests/ui/impl-trait/multiple-lifetimes/error-handling.polonius.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: lifetime may not live long enough - --> $DIR/error-handling.rs:22:16 - | -LL | fn foo<'a, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { - | -- -- lifetime `'b` defined here - | | - | lifetime `'a` defined here -... -LL | let _: &'b i32 = *u.0; - | ^^^^^^^ type annotation requires that `'a` must outlive `'b` - | - = help: consider adding the following bound: `'a: 'b` - -error: aborting due to 1 previous error - diff --git a/tests/ui/impl-trait/multiple-lifetimes/error-handling.stderr b/tests/ui/impl-trait/multiple-lifetimes/error-handling.stderr index 00709ee7438a7..945fb0fc618fb 100644 --- a/tests/ui/impl-trait/multiple-lifetimes/error-handling.stderr +++ b/tests/ui/impl-trait/multiple-lifetimes/error-handling.stderr @@ -6,6 +6,9 @@ LL | fn foo<'a, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { | | | lifetime `'a` defined here ... +LL | let u = v; + | - requirement occurs due to copying this value +... LL | let _: &'b i32 = *u.0; | ^^^^^^^ type annotation requires that `'a` must outlive `'b` | diff --git a/tests/ui/impl-trait/must_outlive_least_region_or_bound.stderr b/tests/ui/impl-trait/must_outlive_least_region_or_bound.stderr index f620bf6dc389c..46561b66c8ee1 100644 --- a/tests/ui/impl-trait/must_outlive_least_region_or_bound.stderr +++ b/tests/ui/impl-trait/must_outlive_least_region_or_bound.stderr @@ -36,12 +36,14 @@ LL | fn elided2(x: &i32) -> impl Copy + 'static { x } | help: consider changing `impl Copy + 'static`'s explicit `'static` bound to the lifetime of argument `x` | -LL | fn elided2(x: &i32) -> impl Copy + '_ { x } - | ~~ +LL - fn elided2(x: &i32) -> impl Copy + 'static { x } +LL + fn elided2(x: &i32) -> impl Copy + '_ { x } + | help: alternatively, add an explicit `'static` bound to this reference | -LL | fn elided2(x: &'static i32) -> impl Copy + 'static { x } - | ~~~~~~~~~~~~ +LL - fn elided2(x: &i32) -> impl Copy + 'static { x } +LL + fn elided2(x: &'static i32) -> impl Copy + 'static { x } + | error: lifetime may not live long enough --> $DIR/must_outlive_least_region_or_bound.rs:12:55 @@ -51,12 +53,14 @@ LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } | help: consider changing `impl Copy + 'static`'s explicit `'static` bound to the lifetime of argument `x` | -LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'a { x } - | ~~ +LL - fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } +LL + fn explicit2<'a>(x: &'a i32) -> impl Copy + 'a { x } + | help: alternatively, add an explicit `'static` bound to this reference | -LL | fn explicit2<'a>(x: &'static i32) -> impl Copy + 'static { x } - | ~~~~~~~~~~~~ +LL - fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } +LL + fn explicit2<'a>(x: &'static i32) -> impl Copy + 'static { x } + | error[E0621]: explicit lifetime required in the type of `x` --> $DIR/must_outlive_least_region_or_bound.rs:15:41 @@ -91,12 +95,14 @@ LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } | help: consider changing `impl LifetimeTrait<'a> + 'static`'s explicit `'static` bound to the lifetime of argument `x` | -LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'a { x } - | ~~ +LL - fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } +LL + fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'a { x } + | help: alternatively, add an explicit `'static` bound to this reference | -LL | fn with_bound<'a>(x: &'static i32) -> impl LifetimeTrait<'a> + 'static { x } - | ~~~~~~~~~~~~ +LL - fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } +LL + fn with_bound<'a>(x: &'static i32) -> impl LifetimeTrait<'a> + 'static { x } + | error[E0700]: hidden type for `impl Fn(&'a u32)` captures lifetime that does not appear in bounds --> $DIR/must_outlive_least_region_or_bound.rs:42:5 @@ -161,12 +167,14 @@ LL | fn elided4(x: &i32) -> Box { Box::new(x) } | help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `x` | -LL | fn elided4(x: &i32) -> Box { Box::new(x) } - | ~~ +LL - fn elided4(x: &i32) -> Box { Box::new(x) } +LL + fn elided4(x: &i32) -> Box { Box::new(x) } + | help: alternatively, add an explicit `'static` bound to this reference | -LL | fn elided4(x: &'static i32) -> Box { Box::new(x) } - | ~~~~~~~~~~~~ +LL - fn elided4(x: &i32) -> Box { Box::new(x) } +LL + fn elided4(x: &'static i32) -> Box { Box::new(x) } + | error: lifetime may not live long enough --> $DIR/must_outlive_least_region_or_bound.rs:27:60 @@ -176,12 +184,14 @@ LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } | help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `x` | -LL | fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } - | ~~ +LL - fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } +LL + fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } + | help: alternatively, add an explicit `'static` bound to this reference | -LL | fn explicit4<'a>(x: &'static i32) -> Box { Box::new(x) } - | ~~~~~~~~~~~~ +LL - fn explicit4<'a>(x: &'a i32) -> Box { Box::new(x) } +LL + fn explicit4<'a>(x: &'static i32) -> Box { Box::new(x) } + | error: aborting due to 13 previous errors diff --git a/tests/ui/impl-trait/no-method-suggested-traits.stderr b/tests/ui/impl-trait/no-method-suggested-traits.stderr index 676247d1a423b..0a974668188e2 100644 --- a/tests/ui/impl-trait/no-method-suggested-traits.stderr +++ b/tests/ui/impl-trait/no-method-suggested-traits.stderr @@ -18,7 +18,7 @@ LL + use no_method_suggested_traits::qux::PrivPub; help: there is a method `method2` with a similar name | LL | 1u32.method2(); - | ~~~~~~~ + | + error[E0599]: no method named `method` found for struct `Rc<&mut Box<&u32>>` in the current scope --> $DIR/no-method-suggested-traits.rs:26:44 @@ -40,7 +40,7 @@ LL + use no_method_suggested_traits::qux::PrivPub; help: there is a method `method2` with a similar name | LL | std::rc::Rc::new(&mut Box::new(&1u32)).method2(); - | ~~~~~~~ + | + error[E0599]: no method named `method` found for type `char` in the current scope --> $DIR/no-method-suggested-traits.rs:30:9 @@ -59,7 +59,7 @@ LL + use foo::Bar; help: there is a method `method2` with a similar name | LL | 'a'.method2(); - | ~~~~~~~ + | + error[E0599]: no method named `method` found for struct `Rc<&mut Box<&char>>` in the current scope --> $DIR/no-method-suggested-traits.rs:32:43 @@ -75,7 +75,7 @@ LL + use foo::Bar; help: there is a method `method2` with a similar name | LL | std::rc::Rc::new(&mut Box::new(&'a')).method2(); - | ~~~~~~~ + | + error[E0599]: no method named `method` found for type `i32` in the current scope --> $DIR/no-method-suggested-traits.rs:35:10 @@ -96,7 +96,7 @@ LL + use no_method_suggested_traits::foo::PubPub; help: there is a method `method3` with a similar name | LL | 1i32.method3(); - | ~~~~~~~ + | + error[E0599]: no method named `method` found for struct `Rc<&mut Box<&i32>>` in the current scope --> $DIR/no-method-suggested-traits.rs:37:44 @@ -112,7 +112,7 @@ LL + use no_method_suggested_traits::foo::PubPub; help: there is a method `method3` with a similar name | LL | std::rc::Rc::new(&mut Box::new(&1i32)).method3(); - | ~~~~~~~ + | + error[E0599]: no method named `method` found for struct `Foo` in the current scope --> $DIR/no-method-suggested-traits.rs:40:9 diff --git a/tests/ui/impl-trait/normalize-tait-in-const.rs b/tests/ui/impl-trait/normalize-tait-in-const.rs index 1fd543b72e7cc..a735ef766737b 100644 --- a/tests/ui/impl-trait/normalize-tait-in-const.rs +++ b/tests/ui/impl-trait/normalize-tait-in-const.rs @@ -1,4 +1,5 @@ -//@ known-bug: #103507 +//! This is a regression test for . +//@ known-bug: #110395 #![feature(type_alias_impl_trait)] #![feature(const_trait_impl, const_destruct)] diff --git a/tests/ui/impl-trait/normalize-tait-in-const.stderr b/tests/ui/impl-trait/normalize-tait-in-const.stderr index 0f79cefeaecb7..c6cd1b139c5b0 100644 --- a/tests/ui/impl-trait/normalize-tait-in-const.stderr +++ b/tests/ui/impl-trait/normalize-tait-in-const.stderr @@ -1,5 +1,5 @@ error: `~const` can only be applied to `#[const_trait]` traits - --> $DIR/normalize-tait-in-const.rs:26:35 + --> $DIR/normalize-tait-in-const.rs:27:35 | LL | const fn with_positive ~const Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) { | ^^^^^^ can't be applied to `Fn` @@ -8,7 +8,7 @@ note: `Fn` can't be used with `~const` because it isn't annotated with `#[const_ --> $SRC_DIR/core/src/ops/function.rs:LL:COL error: `~const` can only be applied to `#[const_trait]` traits - --> $DIR/normalize-tait-in-const.rs:26:35 + --> $DIR/normalize-tait-in-const.rs:27:35 | LL | const fn with_positive ~const Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) { | ^^^^^^ can't be applied to `Fn` @@ -17,29 +17,14 @@ note: `Fn` can't be used with `~const` because it isn't annotated with `#[const_ --> $SRC_DIR/core/src/ops/function.rs:LL:COL = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0277]: the trait bound `for<'a, 'b> fn(&'a foo::Alias<'b>) {foo}: const Destruct` is not satisfied - --> $DIR/normalize-tait-in-const.rs:33:19 - | -LL | with_positive(foo); - | ------------- ^^^ - | | - | required by a bound introduced by this call - | -note: required by a bound in `with_positive` - --> $DIR/normalize-tait-in-const.rs:26:62 - | -LL | const fn with_positive ~const Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) { - | ^^^^^^ required by this bound in `with_positive` - error[E0015]: cannot call non-const closure in constant functions - --> $DIR/normalize-tait-in-const.rs:27:5 + --> $DIR/normalize-tait-in-const.rs:28:5 | LL | fun(filter_positive()); | ^^^^^^^^^^^^^^^^^^^^^^ | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0015, E0277. -For more information about an error, try `rustc --explain E0015`. +For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/impl-trait/opaque-used-in-extraneous-argument.stderr b/tests/ui/impl-trait/opaque-used-in-extraneous-argument.stderr index 6f0f287fe122e..4198095db65ff 100644 --- a/tests/ui/impl-trait/opaque-used-in-extraneous-argument.stderr +++ b/tests/ui/impl-trait/opaque-used-in-extraneous-argument.stderr @@ -7,8 +7,9 @@ LL | fn frob() -> impl Fn + '_ {} = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values | -LL | fn frob() -> impl Fn + 'static {} - | ~~~~~~~ +LL - fn frob() -> impl Fn + '_ {} +LL + fn frob() -> impl Fn + 'static {} + | error[E0412]: cannot find type `P` in this scope --> $DIR/opaque-used-in-extraneous-argument.rs:5:22 diff --git a/tests/ui/impl-trait/opt-out-bound-not-satisfied.rs b/tests/ui/impl-trait/opt-out-bound-not-satisfied.rs new file mode 100644 index 0000000000000..27c493a13bf91 --- /dev/null +++ b/tests/ui/impl-trait/opt-out-bound-not-satisfied.rs @@ -0,0 +1,11 @@ +//! Regression test for ICE https://github.com/rust-lang/rust/issues/135730 +//! This used + +use std::future::Future; +fn foo() -> impl ?Future { + //~^ ERROR: relaxing a default bound only does something for `?Sized` + //~| ERROR: relaxing a default bound only does something for `?Sized` + () +} + +pub fn main() {} diff --git a/tests/ui/impl-trait/opt-out-bound-not-satisfied.stderr b/tests/ui/impl-trait/opt-out-bound-not-satisfied.stderr new file mode 100644 index 0000000000000..dc4314c58ad2f --- /dev/null +++ b/tests/ui/impl-trait/opt-out-bound-not-satisfied.stderr @@ -0,0 +1,16 @@ +error: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/opt-out-bound-not-satisfied.rs:5:18 + | +LL | fn foo() -> impl ?Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/opt-out-bound-not-satisfied.rs:5:18 + | +LL | fn foo() -> impl ?Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr b/tests/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr index 54849c112f5a0..f59be2febeb3c 100644 --- a/tests/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr +++ b/tests/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr @@ -9,8 +9,9 @@ LL | 1u32 | help: change the type of the numeric literal from `u32` to `i32` | -LL | 1i32 - | ~~~ +LL - 1u32 +LL + 1i32 + | error[E0308]: mismatched types --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:12:16 @@ -23,8 +24,9 @@ LL | return 1u32; | help: change the type of the numeric literal from `u32` to `i32` | -LL | return 1i32; - | ~~~ +LL - return 1u32; +LL + return 1i32; + | error[E0308]: mismatched types --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:20:9 @@ -54,8 +56,9 @@ LL | | } | help: you could change the return type to be a boxed trait object | -LL | fn qux() -> Box { - | ~~~~~~~ + +LL - fn qux() -> impl std::fmt::Display { +LL + fn qux() -> Box { + | help: if you change the return type to expect trait objects, box the returned expressions | LL ~ Box::new(0i32) @@ -64,8 +67,9 @@ LL ~ Box::new(1u32) | help: change the type of the numeric literal from `u32` to `i32` | -LL | 1i32 - | ~~~ +LL - 1u32 +LL + 1i32 + | error[E0308]: mismatched types --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:35:14 @@ -126,8 +130,9 @@ LL | | } | help: you could change the return type to be a boxed trait object | -LL | fn dog() -> Box { - | ~~~~~~~ + +LL - fn dog() -> impl std::fmt::Display { +LL + fn dog() -> Box { + | help: if you change the return type to expect trait objects, box the returned expressions | LL ~ 0 => Box::new(0i32), @@ -135,8 +140,9 @@ LL ~ 1 => Box::new(1u32), | help: change the type of the numeric literal from `u32` to `i32` | -LL | 1 => 1i32, - | ~~~ +LL - 1 => 1u32, +LL + 1 => 1i32, + | error[E0308]: `if` and `else` have incompatible types --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:97:9 @@ -152,8 +158,9 @@ LL | | } | help: you could change the return type to be a boxed trait object | -LL | fn apt() -> Box { - | ~~~~~~~ + +LL - fn apt() -> impl std::fmt::Display { +LL + fn apt() -> Box { + | help: if you change the return type to expect trait objects, box the returned expressions | LL ~ Box::new(0i32) @@ -162,8 +169,9 @@ LL ~ Box::new(1u32) | help: change the type of the numeric literal from `u32` to `i32` | -LL | 1i32 - | ~~~ +LL - 1u32 +LL + 1i32 + | error[E0746]: return type cannot have an unboxed trait object --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:66:13 @@ -173,8 +181,9 @@ LL | fn hat() -> dyn std::fmt::Display { | help: consider returning an `impl Trait` instead of a `dyn Trait` | -LL | fn hat() -> impl std::fmt::Display { - | ~~~~ +LL - fn hat() -> dyn std::fmt::Display { +LL + fn hat() -> impl std::fmt::Display { + | help: alternatively, box the return type, and wrap all of the returned values in `Box::new` | LL ~ fn hat() -> Box { @@ -194,8 +203,9 @@ LL | fn pug() -> dyn std::fmt::Display { | help: consider returning an `impl Trait` instead of a `dyn Trait` | -LL | fn pug() -> impl std::fmt::Display { - | ~~~~ +LL - fn pug() -> dyn std::fmt::Display { +LL + fn pug() -> impl std::fmt::Display { + | help: alternatively, box the return type, and wrap all of the returned values in `Box::new` | LL ~ fn pug() -> Box { diff --git a/tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr b/tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr index a8acecf10c7b2..c8dac3a69cd98 100644 --- a/tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr +++ b/tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr @@ -7,8 +7,9 @@ LL | fn no_elided_lt() -> impl Sized + use<'_> {} = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values | -LL | fn no_elided_lt() -> impl Sized + use<'static> {} - | ~~~~~~~ +LL - fn no_elided_lt() -> impl Sized + use<'_> {} +LL + fn no_elided_lt() -> impl Sized + use<'static> {} + | error[E0261]: use of undeclared lifetime name `'missing` --> $DIR/bad-lifetimes.rs:8:37 diff --git a/tests/ui/impl-trait/precise-capturing/hidden-type-suggestion.stderr b/tests/ui/impl-trait/precise-capturing/hidden-type-suggestion.stderr index b0c4cc2fea0e7..0d8fa650df47b 100644 --- a/tests/ui/impl-trait/precise-capturing/hidden-type-suggestion.stderr +++ b/tests/ui/impl-trait/precise-capturing/hidden-type-suggestion.stderr @@ -80,8 +80,9 @@ LL | fn no_params_yet(_: impl Sized, y: &()) -> impl Sized { | ^^^^^^^^^^ help: add a `use<...>` bound to explicitly capture `'_` after turning all argument-position `impl Trait` into type parameters, noting that this possibly affects the API of this crate | -LL | fn no_params_yet(_: T, y: &()) -> impl Sized + use<'_, T> { - | ++++++++++ ~ ++++++++++++ +LL - fn no_params_yet(_: impl Sized, y: &()) -> impl Sized { +LL + fn no_params_yet(_: T, y: &()) -> impl Sized + use<'_, T> { + | error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds --> $DIR/hidden-type-suggestion.rs:36:5 @@ -101,8 +102,9 @@ LL | fn yes_params_yet<'a, T>(_: impl Sized, y: &'a ()) -> impl Sized { | ^^^^^^^^^^ help: add a `use<...>` bound to explicitly capture `'a` after turning all argument-position `impl Trait` into type parameters, noting that this possibly affects the API of this crate | -LL | fn yes_params_yet<'a, T, U: Sized>(_: U, y: &'a ()) -> impl Sized + use<'a, T, U> { - | ++++++++++ ~ +++++++++++++++ +LL - fn yes_params_yet<'a, T>(_: impl Sized, y: &'a ()) -> impl Sized { +LL + fn yes_params_yet<'a, T, U: Sized>(_: U, y: &'a ()) -> impl Sized + use<'a, T, U> { + | error: aborting due to 6 previous errors diff --git a/tests/ui/impl-trait/precise-capturing/migration-note.rs b/tests/ui/impl-trait/precise-capturing/migration-note.rs index 36db07e5764f5..7587e89409aaa 100644 --- a/tests/ui/impl-trait/precise-capturing/migration-note.rs +++ b/tests/ui/impl-trait/precise-capturing/migration-note.rs @@ -29,11 +29,11 @@ fn needs_static() { let a = display_len(&x); //~^ ERROR `x` does not live long enough //~| NOTE this call may capture more lifetimes than intended - //~| NOTE argument requires that `x` is borrowed for `'static` //~| NOTE borrowed value does not live long enoug fn needs_static(_: impl Sized + 'static) {} needs_static(a); + //~^ NOTE argument requires that `x` is borrowed for `'static` } //~^ NOTE `x` dropped here while still borrowed @@ -76,11 +76,11 @@ fn needs_static_mut() { let a = display_len_mut(&mut x); //~^ ERROR `x` does not live long enough //~| NOTE this call may capture more lifetimes than intended - //~| NOTE argument requires that `x` is borrowed for `'static` //~| NOTE borrowed value does not live long enough fn needs_static(_: impl Sized + 'static) {} needs_static(a); + //~^ NOTE argument requires that `x` is borrowed for `'static` } //~^ NOTE `x` dropped here while still borrowed diff --git a/tests/ui/impl-trait/precise-capturing/migration-note.stderr b/tests/ui/impl-trait/precise-capturing/migration-note.stderr index c9403532dfa35..676b6c12f52a3 100644 --- a/tests/ui/impl-trait/precise-capturing/migration-note.stderr +++ b/tests/ui/impl-trait/precise-capturing/migration-note.stderr @@ -42,11 +42,11 @@ LL | let x = vec![1]; | - binding `x` declared here LL | LL | let a = display_len(&x); - | ------------^^- - | | | - | | borrowed value does not live long enough - | argument requires that `x` is borrowed for `'static` + | ^^ borrowed value does not live long enough ... +LL | needs_static(a); + | --------------- argument requires that `x` is borrowed for `'static` +LL | LL | } | - `x` dropped here while still borrowed | @@ -118,11 +118,11 @@ LL | let mut x = vec![1]; | ----- binding `x` declared here LL | LL | let a = display_len_mut(&mut x); - | ----------------^^^^^^- - | | | - | | borrowed value does not live long enough - | argument requires that `x` is borrowed for `'static` + | ^^^^^^ borrowed value does not live long enough ... +LL | needs_static(a); + | --------------- argument requires that `x` is borrowed for `'static` +LL | LL | } | - `x` dropped here while still borrowed | @@ -305,8 +305,9 @@ LL | fn capture_apit(x: &impl Sized) -> impl Sized {} | ^^^^^^^^^^ help: use the precise capturing `use<...>` syntax to make the captures explicit | -LL | fn capture_apit(x: &T) -> impl Sized + use {} - | ++++++++++ ~ ++++++++ +LL - fn capture_apit(x: &impl Sized) -> impl Sized {} +LL + fn capture_apit(x: &T) -> impl Sized + use {} + | help: consider cloning the value if the performance cost is acceptable | LL | let y = capture_apit(&x.clone()); diff --git a/tests/ui/impl-trait/precise-capturing/overcaptures-2024.stderr b/tests/ui/impl-trait/precise-capturing/overcaptures-2024.stderr index 965f8e7b67224..3f8511a21a06a 100644 --- a/tests/ui/impl-trait/precise-capturing/overcaptures-2024.stderr +++ b/tests/ui/impl-trait/precise-capturing/overcaptures-2024.stderr @@ -100,8 +100,9 @@ LL | fn apit(_: &impl Sized) -> impl Sized {} | ^^^^^^^^^^ help: use the precise capturing `use<...>` syntax to make the captures explicit | -LL | fn apit(_: &T) -> impl Sized + use {} - | ++++++++++ ~ ++++++++ +LL - fn apit(_: &impl Sized) -> impl Sized {} +LL + fn apit(_: &T) -> impl Sized + use {} + | error: `impl Sized` will capture more lifetimes than possibly intended in edition 2024 --> $DIR/overcaptures-2024.rs:37:38 @@ -124,8 +125,9 @@ LL | fn apit2(_: &impl Sized, _: U) -> impl Sized {} | ^^^^^^^^^^ help: use the precise capturing `use<...>` syntax to make the captures explicit | -LL | fn apit2(_: &T, _: U) -> impl Sized + use {} - | ++++++++++ ~ +++++++++++ +LL - fn apit2(_: &impl Sized, _: U) -> impl Sized {} +LL + fn apit2(_: &T, _: U) -> impl Sized + use {} + | error: `impl Sized` will capture more lifetimes than possibly intended in edition 2024 --> $DIR/overcaptures-2024.rs:41:37 diff --git a/tests/ui/impl-trait/precise-capturing/redundant.rs b/tests/ui/impl-trait/precise-capturing/redundant.rs index 075d7c70ac653..32dc09273175d 100644 --- a/tests/ui/impl-trait/precise-capturing/redundant.rs +++ b/tests/ui/impl-trait/precise-capturing/redundant.rs @@ -1,24 +1,24 @@ //@ edition: 2024 -//@ check-pass #![feature(precise_capturing_in_traits)] +#![deny(impl_trait_redundant_captures)] fn hello<'a>() -> impl Sized + use<'a> {} -//~^ WARN all possible in-scope parameters are already captured +//~^ ERROR all possible in-scope parameters are already captured struct Inherent; impl Inherent { fn inherent(&self) -> impl Sized + use<'_> {} - //~^ WARN all possible in-scope parameters are already captured + //~^ ERROR all possible in-scope parameters are already captured } trait Test<'a> { fn in_trait() -> impl Sized + use<'a, Self>; - //~^ WARN all possible in-scope parameters are already captured + //~^ ERROR all possible in-scope parameters are already captured } impl<'a> Test<'a> for () { fn in_trait() -> impl Sized + use<'a> {} - //~^ WARN all possible in-scope parameters are already captured + //~^ ERROR all possible in-scope parameters are already captured } fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/redundant.stderr b/tests/ui/impl-trait/precise-capturing/redundant.stderr index 274d9d2375f7d..5c8b35c228585 100644 --- a/tests/ui/impl-trait/precise-capturing/redundant.stderr +++ b/tests/ui/impl-trait/precise-capturing/redundant.stderr @@ -1,4 +1,4 @@ -warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant +error: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant --> $DIR/redundant.rs:6:19 | LL | fn hello<'a>() -> impl Sized + use<'a> {} @@ -6,9 +6,13 @@ LL | fn hello<'a>() -> impl Sized + use<'a> {} | | | help: remove the `use<...>` syntax | - = note: `#[warn(impl_trait_redundant_captures)]` on by default +note: the lint level is defined here + --> $DIR/redundant.rs:4:9 + | +LL | #![deny(impl_trait_redundant_captures)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant +error: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant --> $DIR/redundant.rs:11:27 | LL | fn inherent(&self) -> impl Sized + use<'_> {} @@ -16,7 +20,7 @@ LL | fn inherent(&self) -> impl Sized + use<'_> {} | | | help: remove the `use<...>` syntax -warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant +error: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant --> $DIR/redundant.rs:16:22 | LL | fn in_trait() -> impl Sized + use<'a, Self>; @@ -24,7 +28,7 @@ LL | fn in_trait() -> impl Sized + use<'a, Self>; | | | help: remove the `use<...>` syntax -warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant +error: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant --> $DIR/redundant.rs:20:22 | LL | fn in_trait() -> impl Sized + use<'a> {} @@ -32,5 +36,5 @@ LL | fn in_trait() -> impl Sized + use<'a> {} | | | help: remove the `use<...>` syntax -warning: 4 warnings emitted +error: aborting due to 4 previous errors diff --git a/tests/ui/impl-trait/precise-capturing/rpitit-outlives-2.rs b/tests/ui/impl-trait/precise-capturing/rpitit-outlives-2.rs new file mode 100644 index 0000000000000..6f7e1a0eaefae --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/rpitit-outlives-2.rs @@ -0,0 +1,19 @@ +//@ check-pass + +// Ensure that we skip uncaptured args from RPITITs when comptuing outlives. + +#![feature(precise_capturing_in_traits)] + +struct Invariant(*mut T); + +trait Foo { + fn hello<'s: 's>(&'s self) -> Invariant>; +} + +fn outlives_static(_: impl Sized + 'static) {} + +fn hello<'s, T: Foo + 'static>(x: &'s T) { + outlives_static(x.hello()); +} + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/rpitit-outlives.rs b/tests/ui/impl-trait/precise-capturing/rpitit-outlives.rs new file mode 100644 index 0000000000000..94d81d766f725 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/rpitit-outlives.rs @@ -0,0 +1,18 @@ +//@ check-pass + +// Ensure that we skip uncaptured args from RPITITs when collecting the regions +// to enforce member constraints in opaque type inference. + +#![feature(precise_capturing_in_traits)] + +struct Invariant(*mut T); + +trait Foo { + fn hello<'s: 's>(&'s self) -> Invariant>; +} + +fn hello<'s, T: Foo>(x: &'s T) -> Invariant { + x.hello() +} + +fn main() {} diff --git a/tests/ui/impl-trait/recursive-coroutine-indirect.current.stderr b/tests/ui/impl-trait/recursive-coroutine-indirect.current.stderr index 9814187e179ef..7f3691123ee87 100644 --- a/tests/ui/impl-trait/recursive-coroutine-indirect.current.stderr +++ b/tests/ui/impl-trait/recursive-coroutine-indirect.current.stderr @@ -1,5 +1,5 @@ error[E0733]: recursion in a coroutine requires boxing - --> $DIR/recursive-coroutine-indirect.rs:11:18 + --> $DIR/recursive-coroutine-indirect.rs:8:18 | LL | #[coroutine] move || { | ^^^^^^^ diff --git a/tests/ui/impl-trait/recursive-coroutine-indirect.next.stderr b/tests/ui/impl-trait/recursive-coroutine-indirect.next.stderr index 9814187e179ef..7f3691123ee87 100644 --- a/tests/ui/impl-trait/recursive-coroutine-indirect.next.stderr +++ b/tests/ui/impl-trait/recursive-coroutine-indirect.next.stderr @@ -1,5 +1,5 @@ error[E0733]: recursion in a coroutine requires boxing - --> $DIR/recursive-coroutine-indirect.rs:11:18 + --> $DIR/recursive-coroutine-indirect.rs:8:18 | LL | #[coroutine] move || { | ^^^^^^^ diff --git a/tests/ui/impl-trait/recursive-coroutine-indirect.rs b/tests/ui/impl-trait/recursive-coroutine-indirect.rs index cec2176049b87..f0727ad374270 100644 --- a/tests/ui/impl-trait/recursive-coroutine-indirect.rs +++ b/tests/ui/impl-trait/recursive-coroutine-indirect.rs @@ -2,9 +2,6 @@ //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -//@[next] build-fail -// Deeply normalizing writeback results of opaques makes this into a post-mono error :( - #![feature(coroutines)] #![allow(unconditional_recursion)] fn coroutine_hold() -> impl Sized { diff --git a/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.stderr b/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.stderr index b127bf418003e..767bd312407a3 100644 --- a/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.stderr +++ b/tests/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle.stderr @@ -11,8 +11,9 @@ LL | fn eq(&self, _other: &(Foo, i32)) -> bool { found signature `fn(&a::Bar, &(a::Foo, _)) -> _` help: change the parameter type to match the trait | -LL | fn eq(&self, _other: &(a::Bar, i32)) -> bool { - | ~~~~~~~~~~~~~~ +LL - fn eq(&self, _other: &(Foo, i32)) -> bool { +LL + fn eq(&self, _other: &(a::Bar, i32)) -> bool { + | error: item does not constrain `a::Foo::{opaque#0}`, but has it in its signature --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:9:12 @@ -45,8 +46,9 @@ LL | fn eq(&self, _other: &(Bar, i32)) -> bool { | ^^ help: change the parameter type to match the trait | -LL | fn eq(&self, _other: &(b::Foo, i32)) -> bool { - | ~~~~~~~~~~~~~~ +LL - fn eq(&self, _other: &(Bar, i32)) -> bool { +LL + fn eq(&self, _other: &(b::Foo, i32)) -> bool { + | error: unconstrained opaque type --> $DIR/recursive-type-alias-impl-trait-declaration-too-subtle.rs:18:16 diff --git a/tests/ui/impl-trait/rpit/early_bound.stderr b/tests/ui/impl-trait/rpit/early_bound.stderr index b0c7bd4199cc2..230dde95764bf 100644 --- a/tests/ui/impl-trait/rpit/early_bound.stderr +++ b/tests/ui/impl-trait/rpit/early_bound.stderr @@ -1,12 +1,3 @@ -error[E0792]: expected generic lifetime parameter, found `'_` - --> $DIR/early_bound.rs:7:17 - | -LL | fn test<'a: 'a>(n: bool) -> impl Sized + 'a { - | -- this generic parameter must be used with a generic lifetime parameter -... -LL | let _ = identity::<&'a ()>(test(false)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: concrete type differs from previous defining opaque type use --> $DIR/early_bound.rs:3:29 | @@ -19,6 +10,15 @@ note: previous use here LL | let _ = identity::<&'a ()>(test(false)); | ^^^^^^^^^^^ +error[E0792]: expected generic lifetime parameter, found `'_` + --> $DIR/early_bound.rs:7:17 + | +LL | fn test<'a: 'a>(n: bool) -> impl Sized + 'a { + | -- this generic parameter must be used with a generic lifetime parameter +... +LL | let _ = identity::<&'a ()>(test(false)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/impl-trait/rpit/non-defining-use.stderr b/tests/ui/impl-trait/rpit/non-defining-use.stderr index 19987d47672b0..10a8232e646d0 100644 --- a/tests/ui/impl-trait/rpit/non-defining-use.stderr +++ b/tests/ui/impl-trait/rpit/non-defining-use.stderr @@ -6,14 +6,6 @@ LL | fn foo() -> impl Sized { LL | let _: () = foo::(); | ^^ -error[E0792]: expected generic type parameter, found `u8` - --> $DIR/non-defining-use.rs:8:12 - | -LL | fn bar(val: T) -> impl Sized { - | - this generic parameter must be used with a generic type parameter -LL | let _: u8 = bar(0u8); - | ^^ - error: concrete type differs from previous defining opaque type use --> $DIR/non-defining-use.rs:8:17 | @@ -26,6 +18,14 @@ note: previous use here LL | fn bar(val: T) -> impl Sized { | ^^^^^^^^^^ +error[E0792]: expected generic type parameter, found `u8` + --> $DIR/non-defining-use.rs:8:12 + | +LL | fn bar(val: T) -> impl Sized { + | - this generic parameter must be used with a generic type parameter +LL | let _: u8 = bar(0u8); + | ^^ + error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/impl-trait/stashed-diag-issue-121504.rs b/tests/ui/impl-trait/stashed-diag-issue-121504.rs index 4ac8ffe8931c6..84686ba4f7d3d 100644 --- a/tests/ui/impl-trait/stashed-diag-issue-121504.rs +++ b/tests/ui/impl-trait/stashed-diag-issue-121504.rs @@ -4,7 +4,7 @@ trait MyTrait { async fn foo(self) -> (Self, i32); } -impl MyTrait for xyz::T { //~ ERROR failed to resolve: use of undeclared crate or module `xyz` +impl MyTrait for xyz::T { //~ ERROR failed to resolve: use of unresolved module or unlinked crate `xyz` async fn foo(self, key: i32) -> (u32, i32) { (self, key) } diff --git a/tests/ui/impl-trait/stashed-diag-issue-121504.stderr b/tests/ui/impl-trait/stashed-diag-issue-121504.stderr index 6a881dc7f9fbd..41c6cc425558d 100644 --- a/tests/ui/impl-trait/stashed-diag-issue-121504.stderr +++ b/tests/ui/impl-trait/stashed-diag-issue-121504.stderr @@ -1,8 +1,10 @@ -error[E0433]: failed to resolve: use of undeclared crate or module `xyz` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `xyz` --> $DIR/stashed-diag-issue-121504.rs:7:18 | LL | impl MyTrait for xyz::T { - | ^^^ use of undeclared crate or module `xyz` + | ^^^ use of unresolved module or unlinked crate `xyz` + | + = help: you might be missing a crate named `xyz` error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/trait_type.stderr b/tests/ui/impl-trait/trait_type.stderr index 989779a617835..ce5b2f1cca626 100644 --- a/tests/ui/impl-trait/trait_type.stderr +++ b/tests/ui/impl-trait/trait_type.stderr @@ -8,8 +8,9 @@ LL | fn fmt(&self, x: &str) -> () { } found signature `fn(&MyType, &str) -> ()` help: change the parameter type to match the trait | -LL | fn fmt(&self, x: &mut Formatter<'_>) -> () { } - | ~~~~~~~~~~~~~~~~~~ +LL - fn fmt(&self, x: &str) -> () { } +LL + fn fmt(&self, x: &mut Formatter<'_>) -> () { } + | error[E0050]: method `fmt` has 1 parameter but the declaration in trait `std::fmt::Display::fmt` has 2 --> $DIR/trait_type.rs:12:11 diff --git a/tests/ui/impl-trait/universal-two-impl-traits.stderr b/tests/ui/impl-trait/universal-two-impl-traits.stderr index 3b4844ab1333d..f2f5dd0a0c841 100644 --- a/tests/ui/impl-trait/universal-two-impl-traits.stderr +++ b/tests/ui/impl-trait/universal-two-impl-traits.stderr @@ -10,8 +10,8 @@ LL | let mut a = x; LL | a = y; | ^ expected type parameter `impl Debug`, found a different type parameter `impl Debug` | - = note: expected type parameter `impl Debug` (type parameter `impl Debug`) - found type parameter `impl Debug` (type parameter `impl Debug`) + = note: expected type parameter `impl Debug` + found type parameter `impl Debug` = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters diff --git a/tests/ui/impl-trait/unsized_coercion5.old.stderr b/tests/ui/impl-trait/unsized_coercion5.old.stderr index e56c026b037c6..72aa92ef6b9c8 100644 --- a/tests/ui/impl-trait/unsized_coercion5.old.stderr +++ b/tests/ui/impl-trait/unsized_coercion5.old.stderr @@ -1,5 +1,5 @@ error[E0277]: the size for values of type `impl Trait + ?Sized` cannot be known at compilation time - --> $DIR/unsized_coercion5.rs:17:32 + --> $DIR/unsized_coercion5.rs:15:32 | LL | let y: Box = x as Box; | ^ doesn't have a size known at compile-time diff --git a/tests/ui/impl-trait/unsized_coercion5.rs b/tests/ui/impl-trait/unsized_coercion5.rs index 81f8a6afe9ac1..51ae4f20671e0 100644 --- a/tests/ui/impl-trait/unsized_coercion5.rs +++ b/tests/ui/impl-trait/unsized_coercion5.rs @@ -5,8 +5,6 @@ //@[next] compile-flags: -Znext-solver //@[next] check-pass -#![feature(trait_upcasting)] - trait Trait {} impl Trait for u32 {} diff --git a/tests/ui/impl-trait/where-allowed.stderr b/tests/ui/impl-trait/where-allowed.stderr index 400df87ca3ffa..ebce9b7e445b6 100644 --- a/tests/ui/impl-trait/where-allowed.stderr +++ b/tests/ui/impl-trait/where-allowed.stderr @@ -364,8 +364,9 @@ LL | fn in_trait_impl_return() -> Self::Out; = note: distinct uses of `impl Trait` result in different opaque types help: change the output type to match the trait | -LL | fn in_trait_impl_return() -> <() as DummyTrait>::Out { () } - | ~~~~~~~~~~~~~~~~~~~~~~~ +LL - fn in_trait_impl_return() -> impl Debug { () } +LL + fn in_trait_impl_return() -> <() as DummyTrait>::Out { () } + | error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions --> $DIR/where-allowed.rs:245:36 diff --git a/tests/ui/impl-unused-tps.stderr b/tests/ui/impl-unused-tps.stderr index da4589dee8278..09c3fce641cfe 100644 --- a/tests/ui/impl-unused-tps.stderr +++ b/tests/ui/impl-unused-tps.stderr @@ -7,6 +7,12 @@ LL | impl Foo for [isize; 0] { LL | impl Foo for U { | ^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `[isize; 0]` +error[E0207]: the type parameter `U` is not constrained by the impl trait, self type, or predicates + --> $DIR/impl-unused-tps.rs:32:9 + | +LL | impl Bar for T { + | ^ unconstrained type parameter + error[E0119]: conflicting implementations of trait `Bar` --> $DIR/impl-unused-tps.rs:40:1 | @@ -46,12 +52,6 @@ error[E0207]: the type parameter `U` is not constrained by the impl trait, self LL | impl Foo for [isize; 1] { | ^ unconstrained type parameter -error[E0207]: the type parameter `U` is not constrained by the impl trait, self type, or predicates - --> $DIR/impl-unused-tps.rs:32:9 - | -LL | impl Bar for T { - | ^ unconstrained type parameter - error[E0207]: the type parameter `U` is not constrained by the impl trait, self type, or predicates --> $DIR/impl-unused-tps.rs:40:9 | diff --git a/tests/ui/imports/bad-import-with-rename.stderr b/tests/ui/imports/bad-import-with-rename.stderr index f9c5cf920e1f1..a8e97d13c55ad 100644 --- a/tests/ui/imports/bad-import-with-rename.stderr +++ b/tests/ui/imports/bad-import-with-rename.stderr @@ -6,8 +6,9 @@ LL | use crate::D::B as _; | help: consider importing this type alias instead | -LL | use A::B as _; - | ~~~~~~~~~ +LL - use crate::D::B as _; +LL + use A::B as _; + | error[E0432]: unresolved import `crate::D::B2` --> $DIR/bad-import-with-rename.rs:10:9 @@ -17,8 +18,9 @@ LL | use crate::D::B2; | help: consider importing this type alias instead | -LL | use A::B2; - | ~~~~~ +LL - use crate::D::B2; +LL + use A::B2; + | error: aborting due to 2 previous errors diff --git a/tests/ui/imports/extern-crate-self/extern-crate-self-fail.stderr b/tests/ui/imports/extern-crate-self/extern-crate-self-fail.stderr index 127765727b401..9a3ebfddc49c5 100644 --- a/tests/ui/imports/extern-crate-self/extern-crate-self-fail.stderr +++ b/tests/ui/imports/extern-crate-self/extern-crate-self-fail.stderr @@ -6,8 +6,9 @@ LL | extern crate self; | help: rename the `self` crate to be able to import it | -LL | extern crate self as name; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - extern crate self; +LL + extern crate self as name; + | error: `#[macro_use]` is not supported on `extern crate self` --> $DIR/extern-crate-self-fail.rs:3:1 diff --git a/tests/ui/imports/extern-prelude-extern-crate-fail.rs b/tests/ui/imports/extern-prelude-extern-crate-fail.rs index 2f018851d1936..84751ecc02b63 100644 --- a/tests/ui/imports/extern-prelude-extern-crate-fail.rs +++ b/tests/ui/imports/extern-prelude-extern-crate-fail.rs @@ -7,7 +7,7 @@ mod n { mod m { fn check() { - two_macros::m!(); //~ ERROR failed to resolve: use of undeclared crate or module `two_macros` + two_macros::m!(); //~ ERROR failed to resolve: use of unresolved module or unlinked crate `two_macros` } } diff --git a/tests/ui/imports/extern-prelude-extern-crate-fail.stderr b/tests/ui/imports/extern-prelude-extern-crate-fail.stderr index f7e37449eebe5..ec53730afa024 100644 --- a/tests/ui/imports/extern-prelude-extern-crate-fail.stderr +++ b/tests/ui/imports/extern-prelude-extern-crate-fail.stderr @@ -9,11 +9,11 @@ LL | define_std_as_non_existent!(); | = note: this error originates in the macro `define_std_as_non_existent` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0433]: failed to resolve: use of undeclared crate or module `two_macros` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `two_macros` --> $DIR/extern-prelude-extern-crate-fail.rs:10:9 | LL | two_macros::m!(); - | ^^^^^^^^^^ use of undeclared crate or module `two_macros` + | ^^^^^^^^^^ use of unresolved module or unlinked crate `two_macros` error: aborting due to 2 previous errors diff --git a/tests/ui/imports/glob-resolve1.stderr b/tests/ui/imports/glob-resolve1.stderr index 4401ef58732e8..75e65681c3ab8 100644 --- a/tests/ui/imports/glob-resolve1.stderr +++ b/tests/ui/imports/glob-resolve1.stderr @@ -38,7 +38,7 @@ LL | | } help: you might have meant to use the following enum variant | LL | B::B1; - | ~~~~~ + | ++++ error[E0425]: cannot find value `C` in this scope --> $DIR/glob-resolve1.rs:29:5 diff --git a/tests/ui/imports/glob-use-std.rs b/tests/ui/imports/glob-use-std.rs index b625543da81f5..d2af816e24f2d 100644 --- a/tests/ui/imports/glob-use-std.rs +++ b/tests/ui/imports/glob-use-std.rs @@ -2,7 +2,7 @@ //@ run-fail //@ error-pattern:panic works -//@ ignore-emscripten no processes +//@ needs-subprocess use std::*; diff --git a/tests/ui/imports/import-from-missing-star-2.stderr b/tests/ui/imports/import-from-missing-star-2.stderr index dd35627c68465..9fe2bdbcfa279 100644 --- a/tests/ui/imports/import-from-missing-star-2.stderr +++ b/tests/ui/imports/import-from-missing-star-2.stderr @@ -2,9 +2,9 @@ error[E0432]: unresolved import `spam` --> $DIR/import-from-missing-star-2.rs:2:9 | LL | use spam::*; - | ^^^^ you might be missing crate `spam` + | ^^^^ use of unresolved module or unlinked crate `spam` | -help: consider importing the `spam` crate +help: you might be missing a crate named `spam`, add it to your project and import it in your code | LL + extern crate spam; | diff --git a/tests/ui/imports/import-from-missing-star-3.stderr b/tests/ui/imports/import-from-missing-star-3.stderr index 1e2412b095975..c0b2e5675d391 100644 --- a/tests/ui/imports/import-from-missing-star-3.stderr +++ b/tests/ui/imports/import-from-missing-star-3.stderr @@ -2,9 +2,9 @@ error[E0432]: unresolved import `spam` --> $DIR/import-from-missing-star-3.rs:2:9 | LL | use spam::*; - | ^^^^ you might be missing crate `spam` + | ^^^^ use of unresolved module or unlinked crate `spam` | -help: consider importing the `spam` crate +help: you might be missing a crate named `spam`, add it to your project and import it in your code | LL + extern crate spam; | @@ -13,9 +13,9 @@ error[E0432]: unresolved import `spam` --> $DIR/import-from-missing-star-3.rs:27:13 | LL | use spam::*; - | ^^^^ you might be missing crate `spam` + | ^^^^ use of unresolved module or unlinked crate `spam` | -help: consider importing the `spam` crate +help: you might be missing a crate named `spam`, add it to your project and import it in your code | LL + extern crate spam; | diff --git a/tests/ui/imports/import-from-missing-star.stderr b/tests/ui/imports/import-from-missing-star.stderr index c9bb9baeb4dde..768e1ea1e2cf8 100644 --- a/tests/ui/imports/import-from-missing-star.stderr +++ b/tests/ui/imports/import-from-missing-star.stderr @@ -2,9 +2,9 @@ error[E0432]: unresolved import `spam` --> $DIR/import-from-missing-star.rs:1:5 | LL | use spam::*; - | ^^^^ you might be missing crate `spam` + | ^^^^ use of unresolved module or unlinked crate `spam` | -help: consider importing the `spam` crate +help: you might be missing a crate named `spam`, add it to your project and import it in your code | LL + extern crate spam; | diff --git a/tests/ui/imports/import-trait-method.rs b/tests/ui/imports/import-trait-method.rs index 97dd68f1e76e7..a24b3a1364447 100644 --- a/tests/ui/imports/import-trait-method.rs +++ b/tests/ui/imports/import-trait-method.rs @@ -2,6 +2,6 @@ trait Foo { fn foo(); } -use Foo::foo; //~ ERROR not directly importable +use Foo::foo; //~ ERROR `use` associated items of traits is unstable [E0658] -fn main() { foo(); } +fn main() { foo(); } //~ ERROR type annotations needed diff --git a/tests/ui/imports/import-trait-method.stderr b/tests/ui/imports/import-trait-method.stderr index 9786eb52d3544..8fe774111b962 100644 --- a/tests/ui/imports/import-trait-method.stderr +++ b/tests/ui/imports/import-trait-method.stderr @@ -1,9 +1,22 @@ -error[E0253]: `foo` is not directly importable +error[E0658]: `use` associated items of traits is unstable --> $DIR/import-trait-method.rs:5:5 | LL | use Foo::foo; - | ^^^^^^^^ cannot be imported directly + | ^^^^^^^^ + | + = note: see issue #134691 for more information + = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0283]: type annotations needed + --> $DIR/import-trait-method.rs:7:13 + | +LL | fn main() { foo(); } + | ^^^^^ cannot infer type + | + = note: cannot satisfy `_: Foo` -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0253`. +Some errors have detailed explanations: E0283, E0658. +For more information about an error, try `rustc --explain E0283`. diff --git a/tests/ui/imports/import3.stderr b/tests/ui/imports/import3.stderr index 157b5b6356687..7f58114678117 100644 --- a/tests/ui/imports/import3.stderr +++ b/tests/ui/imports/import3.stderr @@ -2,9 +2,9 @@ error[E0432]: unresolved import `main` --> $DIR/import3.rs:2:5 | LL | use main::bar; - | ^^^^ you might be missing crate `main` + | ^^^^ use of unresolved module or unlinked crate `main` | -help: consider importing the `main` crate +help: you might be missing a crate named `main`, add it to your project and import it in your code | LL + extern crate main; | diff --git a/tests/ui/imports/issue-109343.stderr b/tests/ui/imports/issue-109343.stderr index e66528e8df528..e1071e45b924c 100644 --- a/tests/ui/imports/issue-109343.stderr +++ b/tests/ui/imports/issue-109343.stderr @@ -2,9 +2,9 @@ error[E0432]: unresolved import `unresolved` --> $DIR/issue-109343.rs:4:9 | LL | pub use unresolved::f; - | ^^^^^^^^^^ you might be missing crate `unresolved` + | ^^^^^^^^^^ use of unresolved module or unlinked crate `unresolved` | -help: consider importing the `unresolved` crate +help: you might be missing a crate named `unresolved`, add it to your project and import it in your code | LL + extern crate unresolved; | diff --git a/tests/ui/imports/issue-1697.rs b/tests/ui/imports/issue-1697.rs index 019237611df0d..3d3d4a17d6c19 100644 --- a/tests/ui/imports/issue-1697.rs +++ b/tests/ui/imports/issue-1697.rs @@ -2,7 +2,7 @@ use unresolved::*; //~^ ERROR unresolved import `unresolved` [E0432] -//~| NOTE you might be missing crate `unresolved` -//~| HELP consider importing the `unresolved` crate +//~| NOTE use of unresolved module or unlinked crate `unresolved` +//~| HELP you might be missing a crate named `unresolved` fn main() {} diff --git a/tests/ui/imports/issue-1697.stderr b/tests/ui/imports/issue-1697.stderr index ec0d94bd672f9..96e371c64f9e5 100644 --- a/tests/ui/imports/issue-1697.stderr +++ b/tests/ui/imports/issue-1697.stderr @@ -2,9 +2,9 @@ error[E0432]: unresolved import `unresolved` --> $DIR/issue-1697.rs:3:5 | LL | use unresolved::*; - | ^^^^^^^^^^ you might be missing crate `unresolved` + | ^^^^^^^^^^ use of unresolved module or unlinked crate `unresolved` | -help: consider importing the `unresolved` crate +help: you might be missing a crate named `unresolved`, add it to your project and import it in your code | LL + extern crate unresolved; | diff --git a/tests/ui/imports/issue-33464.stderr b/tests/ui/imports/issue-33464.stderr index 28fbcee401f91..dba4518467580 100644 --- a/tests/ui/imports/issue-33464.stderr +++ b/tests/ui/imports/issue-33464.stderr @@ -2,9 +2,9 @@ error[E0432]: unresolved import `abc` --> $DIR/issue-33464.rs:3:5 | LL | use abc::one_el; - | ^^^ you might be missing crate `abc` + | ^^^ use of unresolved module or unlinked crate `abc` | -help: consider importing the `abc` crate +help: you might be missing a crate named `abc`, add it to your project and import it in your code | LL + extern crate abc; | @@ -13,9 +13,9 @@ error[E0432]: unresolved import `abc` --> $DIR/issue-33464.rs:5:5 | LL | use abc::{a, bbb, cccccc}; - | ^^^ you might be missing crate `abc` + | ^^^ use of unresolved module or unlinked crate `abc` | -help: consider importing the `abc` crate +help: you might be missing a crate named `abc`, add it to your project and import it in your code | LL + extern crate abc; | @@ -24,9 +24,9 @@ error[E0432]: unresolved import `a_very_long_name` --> $DIR/issue-33464.rs:7:5 | LL | use a_very_long_name::{el, el2}; - | ^^^^^^^^^^^^^^^^ you might be missing crate `a_very_long_name` + | ^^^^^^^^^^^^^^^^ use of unresolved module or unlinked crate `a_very_long_name` | -help: consider importing the `a_very_long_name` crate +help: you might be missing a crate named `a_very_long_name`, add it to your project and import it in your code | LL + extern crate a_very_long_name; | diff --git a/tests/ui/imports/issue-36881.stderr b/tests/ui/imports/issue-36881.stderr index 004836e072c5e..33d628f40da22 100644 --- a/tests/ui/imports/issue-36881.stderr +++ b/tests/ui/imports/issue-36881.stderr @@ -2,9 +2,9 @@ error[E0432]: unresolved import `issue_36881_aux` --> $DIR/issue-36881.rs:5:9 | LL | use issue_36881_aux::Foo; - | ^^^^^^^^^^^^^^^ you might be missing crate `issue_36881_aux` + | ^^^^^^^^^^^^^^^ use of unresolved module or unlinked crate `issue_36881_aux` | -help: consider importing the `issue_36881_aux` crate +help: you might be missing a crate named `issue_36881_aux`, add it to your project and import it in your code | LL + extern crate issue_36881_aux; | diff --git a/tests/ui/imports/issue-37887.stderr b/tests/ui/imports/issue-37887.stderr index cc191a17c2936..b83ba273a0194 100644 --- a/tests/ui/imports/issue-37887.stderr +++ b/tests/ui/imports/issue-37887.stderr @@ -2,9 +2,9 @@ error[E0432]: unresolved import `test` --> $DIR/issue-37887.rs:3:9 | LL | use test::*; - | ^^^^ you might be missing crate `test` + | ^^^^ use of unresolved module or unlinked crate `test` | -help: consider importing the `test` crate +help: you might be missing a crate named `test`, add it to your project and import it in your code | LL + extern crate test; | diff --git a/tests/ui/imports/issue-45799-bad-extern-crate-rename-suggestion-formatting.stderr b/tests/ui/imports/issue-45799-bad-extern-crate-rename-suggestion-formatting.stderr index 80cea1a83d931..def0676a0f82c 100644 --- a/tests/ui/imports/issue-45799-bad-extern-crate-rename-suggestion-formatting.stderr +++ b/tests/ui/imports/issue-45799-bad-extern-crate-rename-suggestion-formatting.stderr @@ -7,7 +7,8 @@ LL | extern crate std; = note: `std` must be defined only once in the type namespace of this module help: you can use `as` to change the binding name of the import | -LL | extern crate std as other_std; +LL - extern crate std; +LL + extern crate std as other_std; | error: aborting due to 1 previous error diff --git a/tests/ui/imports/issue-45829/import-self.stderr b/tests/ui/imports/issue-45829/import-self.stderr index 3c9d4fe6ba6fb..b392d93c15410 100644 --- a/tests/ui/imports/issue-45829/import-self.stderr +++ b/tests/ui/imports/issue-45829/import-self.stderr @@ -33,7 +33,7 @@ LL | use foo::{self}; help: you can use `as` to change the binding name of the import | LL | use foo::{self as other_foo}; - | ~~~~~~~~~~~~~~~~~ + | ++++++++++++ error[E0255]: the name `foo` is defined multiple times --> $DIR/import-self.rs:12:5 @@ -47,8 +47,9 @@ LL | use foo::self; = note: `foo` must be defined only once in the type namespace of this module help: you can use `as` to change the binding name of the import | -LL | use foo as other_foo; - | ~~~~~~~~~~~~ +LL - use foo::self; +LL + use foo as other_foo; + | error[E0252]: the name `A` is defined multiple times --> $DIR/import-self.rs:16:11 @@ -61,8 +62,9 @@ LL | use foo::{self as A}; = note: `A` must be defined only once in the type namespace of this module help: you can use `as` to change the binding name of the import | -LL | use foo::{self as OtherA}; - | ~~~~~~~~~~~~~~ +LL - use foo::{self as A}; +LL + use foo::{self as OtherA}; + | error: aborting due to 5 previous errors diff --git a/tests/ui/imports/issue-45829/issue-45829.stderr b/tests/ui/imports/issue-45829/issue-45829.stderr index c6835c3bd7aaa..9fd0e5a767295 100644 --- a/tests/ui/imports/issue-45829/issue-45829.stderr +++ b/tests/ui/imports/issue-45829/issue-45829.stderr @@ -9,8 +9,9 @@ LL | use foo::{A, B as A}; = note: `A` must be defined only once in the type namespace of this module help: you can use `as` to change the binding name of the import | -LL | use foo::{A, B as OtherA}; - | ~~~~~~~~~ +LL - use foo::{A, B as A}; +LL + use foo::{A, B as OtherA}; + | error: aborting due to 1 previous error diff --git a/tests/ui/imports/issue-45829/rename-extern-vs-use.stderr b/tests/ui/imports/issue-45829/rename-extern-vs-use.stderr index 8f2f7bbac0c9a..98fe16824ff76 100644 --- a/tests/ui/imports/issue-45829/rename-extern-vs-use.stderr +++ b/tests/ui/imports/issue-45829/rename-extern-vs-use.stderr @@ -9,7 +9,8 @@ LL | extern crate issue_45829_b as bar; = note: `bar` must be defined only once in the type namespace of this module help: you can use `as` to change the binding name of the import | -LL | extern crate issue_45829_b as other_bar; +LL - extern crate issue_45829_b as bar; +LL + extern crate issue_45829_b as other_bar; | error: aborting due to 1 previous error diff --git a/tests/ui/imports/issue-45829/rename-extern-with-tab.stderr b/tests/ui/imports/issue-45829/rename-extern-with-tab.stderr index ae26d1fd0bbc5..346f2481c213d 100644 --- a/tests/ui/imports/issue-45829/rename-extern-with-tab.stderr +++ b/tests/ui/imports/issue-45829/rename-extern-with-tab.stderr @@ -9,7 +9,8 @@ LL | extern crate issue_45829_b as issue_45829_a; = note: `issue_45829_a` must be defined only once in the type namespace of this module help: you can use `as` to change the binding name of the import | -LL | extern crate issue_45829_b as other_issue_45829_a; +LL - extern crate issue_45829_b as issue_45829_a; +LL + extern crate issue_45829_b as other_issue_45829_a; | error: aborting due to 1 previous error diff --git a/tests/ui/imports/issue-45829/rename-extern.stderr b/tests/ui/imports/issue-45829/rename-extern.stderr index 46560ef9244ea..f99f433c64241 100644 --- a/tests/ui/imports/issue-45829/rename-extern.stderr +++ b/tests/ui/imports/issue-45829/rename-extern.stderr @@ -9,7 +9,8 @@ LL | extern crate issue_45829_b as issue_45829_a; = note: `issue_45829_a` must be defined only once in the type namespace of this module help: you can use `as` to change the binding name of the import | -LL | extern crate issue_45829_b as other_issue_45829_a; +LL - extern crate issue_45829_b as issue_45829_a; +LL + extern crate issue_45829_b as other_issue_45829_a; | error: aborting due to 1 previous error diff --git a/tests/ui/imports/issue-45829/rename-use-vs-extern.stderr b/tests/ui/imports/issue-45829/rename-use-vs-extern.stderr index fd4fb9db0b6d7..e0647cd3ab68b 100644 --- a/tests/ui/imports/issue-45829/rename-use-vs-extern.stderr +++ b/tests/ui/imports/issue-45829/rename-use-vs-extern.stderr @@ -9,8 +9,9 @@ LL | use std as issue_45829_b; = note: `issue_45829_b` must be defined only once in the type namespace of this module help: you can use `as` to change the binding name of the import | -LL | use std as other_issue_45829_b; - | ~~~~~~~~~~~~~~~~~~~~~~ +LL - use std as issue_45829_b; +LL + use std as other_issue_45829_b; + | error: aborting due to 1 previous error diff --git a/tests/ui/imports/issue-45829/rename-use-with-tabs.stderr b/tests/ui/imports/issue-45829/rename-use-with-tabs.stderr index 178303bbc1d16..c2e63ffa91ea9 100644 --- a/tests/ui/imports/issue-45829/rename-use-with-tabs.stderr +++ b/tests/ui/imports/issue-45829/rename-use-with-tabs.stderr @@ -9,8 +9,9 @@ LL | use foo::{A, bar::B as A}; = note: `A` must be defined only once in the type namespace of this module help: you can use `as` to change the binding name of the import | -LL | use foo::{A, bar::B as OtherA}; - | ~~~~~~~~~ +LL - use foo::{A, bar::B as A}; +LL + use foo::{A, bar::B as OtherA}; + | error: aborting due to 1 previous error diff --git a/tests/ui/imports/issue-45829/rename-with-path.stderr b/tests/ui/imports/issue-45829/rename-with-path.stderr index a83fdb3732436..45fdd46850e04 100644 --- a/tests/ui/imports/issue-45829/rename-with-path.stderr +++ b/tests/ui/imports/issue-45829/rename-with-path.stderr @@ -9,8 +9,9 @@ LL | use std::{collections::HashMap as A, sync::Arc as A}; = note: `A` must be defined only once in the type namespace of this module help: you can use `as` to change the binding name of the import | -LL | use std::{collections::HashMap as A, sync::Arc as OtherA}; - | ~~~~~~~~~ +LL - use std::{collections::HashMap as A, sync::Arc as A}; +LL + use std::{collections::HashMap as A, sync::Arc as OtherA}; + | error: aborting due to 1 previous error diff --git a/tests/ui/imports/issue-45829/rename.stderr b/tests/ui/imports/issue-45829/rename.stderr index 4977909487cfb..dc5775e3d56d2 100644 --- a/tests/ui/imports/issue-45829/rename.stderr +++ b/tests/ui/imports/issue-45829/rename.stderr @@ -9,8 +9,9 @@ LL | use std as core; = note: `core` must be defined only once in the type namespace of this module help: you can use `as` to change the binding name of the import | -LL | use std as other_core; - | ~~~~~~~~~~~~~ +LL - use std as core; +LL + use std as other_core; + | error: aborting due to 1 previous error diff --git a/tests/ui/imports/issue-53269.stderr b/tests/ui/imports/issue-53269.stderr index d25d85bf46f02..c12fc0f378e81 100644 --- a/tests/ui/imports/issue-53269.stderr +++ b/tests/ui/imports/issue-53269.stderr @@ -2,9 +2,9 @@ error[E0432]: unresolved import `nonexistent_module` --> $DIR/issue-53269.rs:6:9 | LL | use nonexistent_module::mac; - | ^^^^^^^^^^^^^^^^^^ you might be missing crate `nonexistent_module` + | ^^^^^^^^^^^^^^^^^^ use of unresolved module or unlinked crate `nonexistent_module` | -help: consider importing the `nonexistent_module` crate +help: you might be missing a crate named `nonexistent_module`, add it to your project and import it in your code | LL + extern crate nonexistent_module; | diff --git a/tests/ui/imports/issue-55457.stderr b/tests/ui/imports/issue-55457.stderr index 9c99b6a20de7c..472e46caf31e1 100644 --- a/tests/ui/imports/issue-55457.stderr +++ b/tests/ui/imports/issue-55457.stderr @@ -11,9 +11,9 @@ error[E0432]: unresolved import `non_existent` --> $DIR/issue-55457.rs:2:5 | LL | use non_existent::non_existent; - | ^^^^^^^^^^^^ you might be missing crate `non_existent` + | ^^^^^^^^^^^^ use of unresolved module or unlinked crate `non_existent` | -help: consider importing the `non_existent` crate +help: you might be missing a crate named `non_existent`, add it to your project and import it in your code | LL + extern crate non_existent; | diff --git a/tests/ui/imports/issue-56125.stderr b/tests/ui/imports/issue-56125.stderr index 0c4a569c7ea71..81336d51df4c6 100644 --- a/tests/ui/imports/issue-56125.stderr +++ b/tests/ui/imports/issue-56125.stderr @@ -6,14 +6,18 @@ LL | use empty::issue_56125; | help: consider importing one of these modules instead | -LL | use crate::m3::last_segment::issue_56125; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | use crate::m3::non_last_segment::non_last_segment::issue_56125; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | use ::issue_56125::issue_56125; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | use ::issue_56125::last_segment::issue_56125; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - use empty::issue_56125; +LL + use crate::m3::last_segment::issue_56125; + | +LL - use empty::issue_56125; +LL + use crate::m3::non_last_segment::non_last_segment::issue_56125; + | +LL - use empty::issue_56125; +LL + use ::issue_56125::issue_56125; + | +LL - use empty::issue_56125; +LL + use ::issue_56125::last_segment::issue_56125; + | and 1 other candidate error[E0659]: `issue_56125` is ambiguous diff --git a/tests/ui/imports/issue-57015.stderr b/tests/ui/imports/issue-57015.stderr index f1ae784524170..d3b9cd21fba0f 100644 --- a/tests/ui/imports/issue-57015.stderr +++ b/tests/ui/imports/issue-57015.stderr @@ -6,8 +6,9 @@ LL | use single_err::something; | help: consider importing this module instead | -LL | use glob_ok::something; - | ~~~~~~~~~~~~~~~~~~ +LL - use single_err::something; +LL + use glob_ok::something; + | error: aborting due to 1 previous error diff --git a/tests/ui/imports/issue-59764.stderr b/tests/ui/imports/issue-59764.stderr index 74525c9c88f5a..1d31e3bda0de4 100644 --- a/tests/ui/imports/issue-59764.stderr +++ b/tests/ui/imports/issue-59764.stderr @@ -182,8 +182,9 @@ LL | use issue_59764::foo::makro as baz; = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined help: a macro with this name exists at the root of the crate | -LL | use issue_59764::makro as baz; - | ~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - use issue_59764::foo::makro as baz; +LL + use issue_59764::makro as baz; + | error[E0432]: unresolved import `issue_59764::foo::makro` --> $DIR/issue-59764.rs:107:33 @@ -223,8 +224,9 @@ LL | use issue_59764::foo::makro; = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined help: a macro with this name exists at the root of the crate | -LL | use issue_59764::makro; - | ~~~~~~~~~~~~~~~~~~ +LL - use issue_59764::foo::makro; +LL + use issue_59764::makro; + | error[E0425]: cannot find function `bar` in this scope --> $DIR/issue-59764.rs:133:5 diff --git a/tests/ui/imports/issue-81413.stderr b/tests/ui/imports/issue-81413.stderr index aa1246c1d2f50..257aca4455c5d 100644 --- a/tests/ui/imports/issue-81413.stderr +++ b/tests/ui/imports/issue-81413.stderr @@ -2,9 +2,9 @@ error[E0432]: unresolved import `doesnt_exist` --> $DIR/issue-81413.rs:7:9 | LL | pub use doesnt_exist::*; - | ^^^^^^^^^^^^ you might be missing crate `doesnt_exist` + | ^^^^^^^^^^^^ use of unresolved module or unlinked crate `doesnt_exist` | -help: consider importing the `doesnt_exist` crate +help: you might be missing a crate named `doesnt_exist`, add it to your project and import it in your code | LL + extern crate doesnt_exist; | diff --git a/tests/ui/imports/multiple-extern-by-macro-for-buitlin.stderr b/tests/ui/imports/multiple-extern-by-macro-for-buitlin.stderr index a84a6c42aa8d1..4a5c85479d33d 100644 --- a/tests/ui/imports/multiple-extern-by-macro-for-buitlin.stderr +++ b/tests/ui/imports/multiple-extern-by-macro-for-buitlin.stderr @@ -14,7 +14,8 @@ LL | m!(); = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) help: you can use `as` to change the binding name of the import | -LL | extern crate std as other_core; +LL - extern crate std as core; +LL + extern crate std as other_core; | error: aborting due to 1 previous error diff --git a/tests/ui/imports/multiple-extern-by-macro-for-custom.stderr b/tests/ui/imports/multiple-extern-by-macro-for-custom.stderr index 556d75a4dbb5f..8b87ae93b4d5e 100644 --- a/tests/ui/imports/multiple-extern-by-macro-for-custom.stderr +++ b/tests/ui/imports/multiple-extern-by-macro-for-custom.stderr @@ -14,7 +14,8 @@ LL | m!(); = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) help: you can use `as` to change the binding name of the import | -LL | extern crate std as other_empty; +LL - extern crate std as empty; +LL + extern crate std as other_empty; | error: aborting due to 1 previous error diff --git a/tests/ui/imports/multiple-extern-by-macro-for-inexist.stderr b/tests/ui/imports/multiple-extern-by-macro-for-inexist.stderr index ec34489f2323d..9a9e538740d7b 100644 --- a/tests/ui/imports/multiple-extern-by-macro-for-inexist.stderr +++ b/tests/ui/imports/multiple-extern-by-macro-for-inexist.stderr @@ -20,7 +20,8 @@ LL | m!(); = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) help: you can use `as` to change the binding name of the import | -LL | extern crate std as other_non_existent; +LL - extern crate std as non_existent; +LL + extern crate std as other_non_existent; | error: aborting due to 2 previous errors diff --git a/tests/ui/imports/no-std-inject.stderr b/tests/ui/imports/no-std-inject.stderr index 597ecdce9eb99..d3952a50cd311 100644 --- a/tests/ui/imports/no-std-inject.stderr +++ b/tests/ui/imports/no-std-inject.stderr @@ -7,8 +7,9 @@ LL | extern crate core; = note: `core` must be defined only once in the type namespace of this module help: you can use `as` to change the binding name of the import | -LL | extern crate core as other_core; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - extern crate core; +LL + extern crate core as other_core; + | error: aborting due to 1 previous error diff --git a/tests/ui/imports/private-std-reexport-suggest-public.stderr b/tests/ui/imports/private-std-reexport-suggest-public.stderr index 222553235aaac..90d84bb4f5b47 100644 --- a/tests/ui/imports/private-std-reexport-suggest-public.stderr +++ b/tests/ui/imports/private-std-reexport-suggest-public.stderr @@ -15,8 +15,9 @@ note: ...and refers to the module `mem` which is defined here = note: you could import this directly help: import `mem` through the re-export | -LL | use std::mem; - | ~~~~~~~~ +LL - use foo::mem; +LL + use std::mem; + | error: aborting due to 1 previous error diff --git a/tests/ui/imports/suggest-import-issue-120074.edition2015.stderr b/tests/ui/imports/suggest-import-issue-120074.edition2015.stderr index 414eeee0fedc8..10b8db62edc91 100644 --- a/tests/ui/imports/suggest-import-issue-120074.edition2015.stderr +++ b/tests/ui/imports/suggest-import-issue-120074.edition2015.stderr @@ -7,7 +7,7 @@ LL | println!("Hello, {}!", crate::bar::do_the_thing); help: a similar path exists | LL | println!("Hello, {}!", crate::foo::bar::do_the_thing); - | ~~~~~~~~ + | +++++ help: consider importing this module | LL + use foo::bar; diff --git a/tests/ui/imports/suggest-import-issue-120074.edition2021.stderr b/tests/ui/imports/suggest-import-issue-120074.edition2021.stderr index 414eeee0fedc8..10b8db62edc91 100644 --- a/tests/ui/imports/suggest-import-issue-120074.edition2021.stderr +++ b/tests/ui/imports/suggest-import-issue-120074.edition2021.stderr @@ -7,7 +7,7 @@ LL | println!("Hello, {}!", crate::bar::do_the_thing); help: a similar path exists | LL | println!("Hello, {}!", crate::foo::bar::do_the_thing); - | ~~~~~~~~ + | +++++ help: consider importing this module | LL + use foo::bar; diff --git a/tests/ui/imports/tool-mod-child.rs b/tests/ui/imports/tool-mod-child.rs index a8249ab01dfc1..38bcdfb024941 100644 --- a/tests/ui/imports/tool-mod-child.rs +++ b/tests/ui/imports/tool-mod-child.rs @@ -1,7 +1,7 @@ use clippy::a; //~ ERROR unresolved import `clippy` -use clippy::a::b; //~ ERROR failed to resolve: you might be missing crate `clippy` +use clippy::a::b; //~ ERROR failed to resolve: use of unresolved module or unlinked crate `clippy` use rustdoc::a; //~ ERROR unresolved import `rustdoc` -use rustdoc::a::b; //~ ERROR failed to resolve: you might be missing crate `rustdoc` +use rustdoc::a::b; //~ ERROR failed to resolve: use of unresolved module or unlinked crate `rustdoc` fn main() {} diff --git a/tests/ui/imports/tool-mod-child.stderr b/tests/ui/imports/tool-mod-child.stderr index ec110ccd75dbc..b0e446fcbf6a7 100644 --- a/tests/ui/imports/tool-mod-child.stderr +++ b/tests/ui/imports/tool-mod-child.stderr @@ -1,10 +1,10 @@ -error[E0433]: failed to resolve: you might be missing crate `clippy` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `clippy` --> $DIR/tool-mod-child.rs:2:5 | LL | use clippy::a::b; - | ^^^^^^ you might be missing crate `clippy` + | ^^^^^^ use of unresolved module or unlinked crate `clippy` | -help: consider importing the `clippy` crate +help: you might be missing a crate named `clippy`, add it to your project and import it in your code | LL + extern crate clippy; | @@ -13,20 +13,20 @@ error[E0432]: unresolved import `clippy` --> $DIR/tool-mod-child.rs:1:5 | LL | use clippy::a; - | ^^^^^^ you might be missing crate `clippy` + | ^^^^^^ use of unresolved module or unlinked crate `clippy` | -help: consider importing the `clippy` crate +help: you might be missing a crate named `clippy`, add it to your project and import it in your code | LL + extern crate clippy; | -error[E0433]: failed to resolve: you might be missing crate `rustdoc` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `rustdoc` --> $DIR/tool-mod-child.rs:5:5 | LL | use rustdoc::a::b; - | ^^^^^^^ you might be missing crate `rustdoc` + | ^^^^^^^ use of unresolved module or unlinked crate `rustdoc` | -help: consider importing the `rustdoc` crate +help: you might be missing a crate named `rustdoc`, add it to your project and import it in your code | LL + extern crate rustdoc; | @@ -35,9 +35,9 @@ error[E0432]: unresolved import `rustdoc` --> $DIR/tool-mod-child.rs:4:5 | LL | use rustdoc::a; - | ^^^^^^^ you might be missing crate `rustdoc` + | ^^^^^^^ use of unresolved module or unlinked crate `rustdoc` | -help: consider importing the `rustdoc` crate +help: you might be missing a crate named `rustdoc`, add it to your project and import it in your code | LL + extern crate rustdoc; | diff --git a/tests/ui/imports/unresolved-imports-used.stderr b/tests/ui/imports/unresolved-imports-used.stderr index 4bf02ff6e3a9f..d39d268521718 100644 --- a/tests/ui/imports/unresolved-imports-used.stderr +++ b/tests/ui/imports/unresolved-imports-used.stderr @@ -14,9 +14,9 @@ error[E0432]: unresolved import `foo` --> $DIR/unresolved-imports-used.rs:11:5 | LL | use foo::bar; - | ^^^ you might be missing crate `foo` + | ^^^ use of unresolved module or unlinked crate `foo` | -help: consider importing the `foo` crate +help: you might be missing a crate named `foo`, add it to your project and import it in your code | LL + extern crate foo; | @@ -25,9 +25,9 @@ error[E0432]: unresolved import `baz` --> $DIR/unresolved-imports-used.rs:12:5 | LL | use baz::*; - | ^^^ you might be missing crate `baz` + | ^^^ use of unresolved module or unlinked crate `baz` | -help: consider importing the `baz` crate +help: you might be missing a crate named `baz`, add it to your project and import it in your code | LL + extern crate baz; | @@ -36,9 +36,9 @@ error[E0432]: unresolved import `foo2` --> $DIR/unresolved-imports-used.rs:14:5 | LL | use foo2::bar2; - | ^^^^ you might be missing crate `foo2` + | ^^^^ use of unresolved module or unlinked crate `foo2` | -help: consider importing the `foo2` crate +help: you might be missing a crate named `foo2`, add it to your project and import it in your code | LL + extern crate foo2; | @@ -47,9 +47,9 @@ error[E0432]: unresolved import `baz2` --> $DIR/unresolved-imports-used.rs:15:5 | LL | use baz2::*; - | ^^^^ you might be missing crate `baz2` + | ^^^^ use of unresolved module or unlinked crate `baz2` | -help: consider importing the `baz2` crate +help: you might be missing a crate named `baz2`, add it to your project and import it in your code | LL + extern crate baz2; | diff --git a/tests/ui/include-macros/parent_dir.stderr b/tests/ui/include-macros/parent_dir.stderr index 0470d5b1f1e3c..23029e911203c 100644 --- a/tests/ui/include-macros/parent_dir.stderr +++ b/tests/ui/include-macros/parent_dir.stderr @@ -7,8 +7,9 @@ LL | let _ = include_str!("include-macros/file.txt"); = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) help: there is a file with the same name in a different directory | -LL | let _ = include_str!("file.txt"); - | ~~~~~~~~~~ +LL - let _ = include_str!("include-macros/file.txt"); +LL + let _ = include_str!("file.txt"); + | error: couldn't read `$DIR/hello.rs`: $FILE_NOT_FOUND_MSG --> $DIR/parent_dir.rs:6:13 @@ -19,8 +20,9 @@ LL | let _ = include_str!("hello.rs"); = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) help: there is a file with the same name in a different directory | -LL | let _ = include_str!("../hello.rs"); - | ~~~~~~~~~~~~~ +LL - let _ = include_str!("hello.rs"); +LL + let _ = include_str!("../hello.rs"); + | error: couldn't read `$DIR/../../data.bin`: $FILE_NOT_FOUND_MSG --> $DIR/parent_dir.rs:8:13 @@ -31,8 +33,9 @@ LL | let _ = include_bytes!("../../data.bin"); = note: this error originates in the macro `include_bytes` (in Nightly builds, run with -Z macro-backtrace for more info) help: there is a file with the same name in a different directory | -LL | let _ = include_bytes!("data.bin"); - | ~~~~~~~~~~ +LL - let _ = include_bytes!("../../data.bin"); +LL + let _ = include_bytes!("data.bin"); + | error: couldn't read `$DIR/tests/ui/include-macros/file.txt`: $FILE_NOT_FOUND_MSG --> $DIR/parent_dir.rs:10:13 @@ -43,8 +46,9 @@ LL | let _ = include_str!("tests/ui/include-macros/file.txt"); = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) help: there is a file with the same name in a different directory | -LL | let _ = include_str!("file.txt"); - | ~~~~~~~~~~ +LL - let _ = include_str!("tests/ui/include-macros/file.txt"); +LL + let _ = include_str!("file.txt"); + | error: aborting due to 4 previous errors diff --git a/tests/ui/inference/ambiguous_type_parameter.stderr b/tests/ui/inference/ambiguous_type_parameter.stderr index 0674deb63ba0c..835999121d611 100644 --- a/tests/ui/inference/ambiguous_type_parameter.stderr +++ b/tests/ui/inference/ambiguous_type_parameter.stderr @@ -6,8 +6,9 @@ LL | InMemoryStore.get_raw(&String::default()); | help: try using a fully qualified path to specify the expected types | -LL | >>::get_raw(&InMemoryStore, &String::default()); - | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ~ +LL - InMemoryStore.get_raw(&String::default()); +LL + >>::get_raw(&InMemoryStore, &String::default()); + | error: aborting due to 1 previous error diff --git a/tests/ui/inference/cannot-infer-closure-circular.stderr b/tests/ui/inference/cannot-infer-closure-circular.stderr index a16e832f8ef91..ee17f7737cf30 100644 --- a/tests/ui/inference/cannot-infer-closure-circular.stderr +++ b/tests/ui/inference/cannot-infer-closure-circular.stderr @@ -9,8 +9,8 @@ LL | Ok(v) | help: consider giving this closure parameter an explicit type, where the type for type parameter `E` is specified | -LL | let x = |r: Result<(), E>| { - | +++++++++++++++ +LL | let x = |r: Result<_, E>| { + | ++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/inference/char-as-str-single.stderr b/tests/ui/inference/char-as-str-single.stderr index 9149efe324071..5455a0fd7ee34 100644 --- a/tests/ui/inference/char-as-str-single.stderr +++ b/tests/ui/inference/char-as-str-single.stderr @@ -8,8 +8,9 @@ LL | let _: char = "a"; | help: if you meant to write a `char` literal, use single quotes | -LL | let _: char = 'a'; - | ~~~ +LL - let _: char = "a"; +LL + let _: char = 'a'; + | error[E0308]: mismatched types --> $DIR/char-as-str-single.rs:10:19 @@ -21,8 +22,9 @@ LL | let _: char = "人"; | help: if you meant to write a `char` literal, use single quotes | -LL | let _: char = '人'; - | ~~~~ +LL - let _: char = "人"; +LL + let _: char = '人'; + | error[E0308]: mismatched types --> $DIR/char-as-str-single.rs:11:19 @@ -34,8 +36,9 @@ LL | let _: char = "'"; | help: if you meant to write a `char` literal, use single quotes | -LL | let _: char = '\''; - | ~~~~ +LL - let _: char = "'"; +LL + let _: char = '\''; + | error[E0308]: mismatched types --> $DIR/char-as-str-single.rs:18:9 @@ -47,8 +50,9 @@ LL | "A" => {} | help: if you meant to write a `char` literal, use single quotes | -LL | 'A' => {} - | ~~~ +LL - "A" => {} +LL + 'A' => {} + | error: aborting due to 4 previous errors diff --git a/tests/ui/inference/erase-type-params-in-label.stderr b/tests/ui/inference/erase-type-params-in-label.stderr index 4e9a74c1e403d..1ec8a33eb8205 100644 --- a/tests/ui/inference/erase-type-params-in-label.stderr +++ b/tests/ui/inference/erase-type-params-in-label.stderr @@ -12,8 +12,8 @@ LL | fn foo(t: T, k: K) -> Foo { | ^^^^^^^ required by this bound in `foo` help: consider giving `foo` an explicit type, where the type for type parameter `W` is specified | -LL | let foo: Foo = foo(1, ""); - | ++++++++++++++++++++++ +LL | let foo: Foo<_, &_, W, Z> = foo(1, ""); + | ++++++++++++++++++ error[E0283]: type annotations needed for `Bar` --> $DIR/erase-type-params-in-label.rs:5:9 @@ -29,8 +29,8 @@ LL | fn bar(t: T, k: K) -> Bar { | ^^^^^^^ required by this bound in `bar` help: consider giving `bar` an explicit type, where the type for type parameter `Z` is specified | -LL | let bar: Bar = bar(1, ""); - | +++++++++++++++++++ +LL | let bar: Bar<_, &_, Z> = bar(1, ""); + | +++++++++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/inference/inference_unstable.stderr b/tests/ui/inference/inference_unstable.stderr index 51f086177dbab..395dcb2661f11 100644 --- a/tests/ui/inference/inference_unstable.stderr +++ b/tests/ui/inference/inference_unstable.stderr @@ -65,8 +65,9 @@ LL | assert_eq!(char::C, 1); = note: for more information, see issue #48919 help: use the fully qualified path to the associated const | -LL | assert_eq!(::C, 1); - | ~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - assert_eq!(char::C, 1); +LL + assert_eq!(::C, 1); + | help: add `#![feature(assoc_const_ipu_iter)]` to the crate attributes to enable `inference_unstable_iterator::IpuIterator::C` | LL + #![feature(assoc_const_ipu_iter)] diff --git a/tests/ui/inference/inference_unstable_featured.stderr b/tests/ui/inference/inference_unstable_featured.stderr index b908c7142d4b1..b267ca497de8a 100644 --- a/tests/ui/inference/inference_unstable_featured.stderr +++ b/tests/ui/inference/inference_unstable_featured.stderr @@ -8,12 +8,14 @@ LL | assert_eq!('x'.ipu_flatten(), 0); = note: candidate #2 is defined in an impl of the trait `IpuItertools` for the type `char` help: disambiguate the method for candidate #1 | -LL | assert_eq!(IpuIterator::ipu_flatten(&'x'), 0); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - assert_eq!('x'.ipu_flatten(), 0); +LL + assert_eq!(IpuIterator::ipu_flatten(&'x'), 0); + | help: disambiguate the method for candidate #2 | -LL | assert_eq!(IpuItertools::ipu_flatten(&'x'), 0); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - assert_eq!('x'.ipu_flatten(), 0); +LL + assert_eq!(IpuItertools::ipu_flatten(&'x'), 0); + | error: aborting due to 1 previous error diff --git a/tests/ui/inference/issue-103587.stderr b/tests/ui/inference/issue-103587.stderr index 589cb7ea7b130..bd6a9b71b86d6 100644 --- a/tests/ui/inference/issue-103587.stderr +++ b/tests/ui/inference/issue-103587.stderr @@ -6,8 +6,9 @@ LL | if let Some(_) == x {} | help: consider using `=` here | -LL | if let Some(_) = x {} - | ~ +LL - if let Some(_) == x {} +LL + if let Some(_) = x {} + | error[E0308]: mismatched types --> $DIR/issue-103587.rs:7:8 diff --git a/tests/ui/inference/issue-104649.stderr b/tests/ui/inference/issue-104649.stderr index 391ed16f349d7..27382e301341f 100644 --- a/tests/ui/inference/issue-104649.stderr +++ b/tests/ui/inference/issue-104649.stderr @@ -6,8 +6,8 @@ LL | let a = A(Result::Ok(Result::Ok(()))); | help: consider giving `a` an explicit type, where the type for type parameter `E` is specified | -LL | let a: A, Error>> = A(Result::Ok(Result::Ok(()))); - | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +LL | let a: A, _>> = A(Result::Ok(Result::Ok(()))); + | ++++++++++++++++++++++++++++++++++++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/inference/issue-12028.stderr b/tests/ui/inference/issue-12028.stderr index 3d7fb13d44757..0d8ef1c938d4c 100644 --- a/tests/ui/inference/issue-12028.stderr +++ b/tests/ui/inference/issue-12028.stderr @@ -7,8 +7,9 @@ LL | self.input_stream(&mut stream); = note: cannot satisfy `<_ as StreamHasher>::S == ::S` help: try using a fully qualified path to specify the expected types | -LL | >::input_stream(self, &mut stream); - | ++++++++++++++++++++++++++++++++++++ ~ +LL - self.input_stream(&mut stream); +LL + >::input_stream(self, &mut stream); + | error: aborting due to 1 previous error diff --git a/tests/ui/inference/issue-70082.stderr b/tests/ui/inference/issue-70082.stderr index 442e7479a9eb4..926ecff4a4fb5 100644 --- a/tests/ui/inference/issue-70082.stderr +++ b/tests/ui/inference/issue-70082.stderr @@ -9,8 +9,9 @@ LL | let y: f64 = 0.01f64 * 1i16.into(); = note: cannot satisfy `>::Output == f64` help: try using a fully qualified path to specify the expected types | -LL | let y: f64 = 0.01f64 * >::into(1i16); - | +++++++++++++++++++++++ ~ +LL - let y: f64 = 0.01f64 * 1i16.into(); +LL + let y: f64 = 0.01f64 * >::into(1i16); + | error: aborting due to 1 previous error diff --git a/tests/ui/inference/issue-71584.rs b/tests/ui/inference/issue-71584.rs index 7e4d45ec4a042..61568e5467c57 100644 --- a/tests/ui/inference/issue-71584.rs +++ b/tests/ui/inference/issue-71584.rs @@ -1,4 +1,4 @@ -//@ ignore-windows different list of satisfying impls +//@ ignore-windows FIXME: We get an extra E0283 on Windows fn main() { let n: u32 = 1; let mut d: u64 = 2; diff --git a/tests/ui/inference/issue-71584.stderr b/tests/ui/inference/issue-71584.stderr index 391d3e7613e03..4bbfef6c44afa 100644 --- a/tests/ui/inference/issue-71584.stderr +++ b/tests/ui/inference/issue-71584.stderr @@ -9,8 +9,9 @@ LL | d = d % n.into(); = note: cannot satisfy `>::Output == u64` help: try using a fully qualified path to specify the expected types | -LL | d = d % >::into(n); - | +++++++++++++++++++++++ ~ +LL - d = d % n.into(); +LL + d = d % >::into(n); + | error: aborting due to 1 previous error diff --git a/tests/ui/inference/issue-72616.stderr b/tests/ui/inference/issue-72616.stderr index 02c92c1c11d02..31a0586301df2 100644 --- a/tests/ui/inference/issue-72616.stderr +++ b/tests/ui/inference/issue-72616.stderr @@ -6,15 +6,19 @@ LL | if String::from("a") == "a".try_into().unwrap() {} | | | type must be known at this point | - = note: multiple `impl`s satisfying `String: PartialEq<_>` found in the `alloc` crate: - - impl PartialEq for String; - - impl<'a, 'b> PartialEq<&'a str> for String; - - impl<'a, 'b> PartialEq> for String; - - impl<'a, 'b> PartialEq for String; + = note: cannot satisfy `String: PartialEq<_>` + = help: the following types implement trait `PartialEq`: + `String` implements `PartialEq<&str>` + `String` implements `PartialEq` + `String` implements `PartialEq` + `String` implements `PartialEq>` + `String` implements `PartialEq` + `String` implements `PartialEq` help: try using a fully qualified path to specify the expected types | -LL | if String::from("a") == <&str as TryInto>::try_into("a").unwrap() {} - | +++++++++++++++++++++++++++++++ ~ +LL - if String::from("a") == "a".try_into().unwrap() {} +LL + if String::from("a") == <&str as TryInto>::try_into("a").unwrap() {} + | error[E0283]: type annotations needed --> $DIR/issue-72616.rs:22:37 @@ -23,14 +27,15 @@ LL | if String::from("a") == "a".try_into().unwrap() {} | ^^^^^^^^ | = note: multiple `impl`s satisfying `_: TryFrom<&str>` found in the following crates: `core`, `std`: - - impl TryFrom<&str> for std::sys_common::net::LookupHost; + - impl TryFrom<&str> for std::sys::net::connection::socket::LookupHost; - impl TryFrom for T where U: Into; = note: required for `&str` to implement `TryInto<_>` help: try using a fully qualified path to specify the expected types | -LL | if String::from("a") == <&str as TryInto>::try_into("a").unwrap() {} - | +++++++++++++++++++++++++++++++ ~ +LL - if String::from("a") == "a".try_into().unwrap() {} +LL + if String::from("a") == <&str as TryInto>::try_into("a").unwrap() {} + | error: aborting due to 2 previous errors diff --git a/tests/ui/inference/issue-72690.stderr b/tests/ui/inference/issue-72690.stderr index 6391672f8617d..4926cf9e981ba 100644 --- a/tests/ui/inference/issue-72690.stderr +++ b/tests/ui/inference/issue-72690.stderr @@ -15,14 +15,16 @@ LL | String::from("x".as_ref()); | ^^^^^^ | = note: multiple `impl`s satisfying `str: AsRef<_>` found in the following crates: `core`, `std`: + - impl AsRef for str; - impl AsRef for str; - impl AsRef for str; - impl AsRef<[u8]> for str; - impl AsRef for str; help: try using a fully qualified path to specify the expected types | -LL | String::from(>::as_ref("x")); - | ++++++++++++++++++++++++++ ~ +LL - String::from("x".as_ref()); +LL + String::from(>::as_ref("x")); + | error[E0283]: type annotations needed --> $DIR/issue-72690.rs:12:9 @@ -41,14 +43,16 @@ LL | |x| String::from("x".as_ref()); | ^^^^^^ | = note: multiple `impl`s satisfying `str: AsRef<_>` found in the following crates: `core`, `std`: + - impl AsRef for str; - impl AsRef for str; - impl AsRef for str; - impl AsRef<[u8]> for str; - impl AsRef for str; help: try using a fully qualified path to specify the expected types | -LL | |x| String::from(>::as_ref("x")); - | ++++++++++++++++++++++++++ ~ +LL - |x| String::from("x".as_ref()); +LL + |x| String::from(>::as_ref("x")); + | error[E0283]: type annotations needed for `&_` --> $DIR/issue-72690.rs:17:9 @@ -57,6 +61,7 @@ LL | let _ = "x".as_ref(); | ^ ------ type must be known at this point | = note: multiple `impl`s satisfying `str: AsRef<_>` found in the following crates: `core`, `std`: + - impl AsRef for str; - impl AsRef for str; - impl AsRef for str; - impl AsRef<[u8]> for str; @@ -83,14 +88,16 @@ LL | String::from("x".as_ref()); | ^^^^^^ | = note: multiple `impl`s satisfying `str: AsRef<_>` found in the following crates: `core`, `std`: + - impl AsRef for str; - impl AsRef for str; - impl AsRef for str; - impl AsRef<[u8]> for str; - impl AsRef for str; help: try using a fully qualified path to specify the expected types | -LL | String::from(>::as_ref("x")); - | ++++++++++++++++++++++++++ ~ +LL - String::from("x".as_ref()); +LL + String::from(>::as_ref("x")); + | error[E0283]: type annotations needed --> $DIR/issue-72690.rs:28:5 @@ -109,14 +116,16 @@ LL | String::from("x".as_ref()); | ^^^^^^ | = note: multiple `impl`s satisfying `str: AsRef<_>` found in the following crates: `core`, `std`: + - impl AsRef for str; - impl AsRef for str; - impl AsRef for str; - impl AsRef<[u8]> for str; - impl AsRef for str; help: try using a fully qualified path to specify the expected types | -LL | String::from(>::as_ref("x")); - | ++++++++++++++++++++++++++ ~ +LL - String::from("x".as_ref()); +LL + String::from(>::as_ref("x")); + | error[E0283]: type annotations needed --> $DIR/issue-72690.rs:37:5 @@ -135,14 +144,16 @@ LL | String::from("x".as_ref()); | ^^^^^^ | = note: multiple `impl`s satisfying `str: AsRef<_>` found in the following crates: `core`, `std`: + - impl AsRef for str; - impl AsRef for str; - impl AsRef for str; - impl AsRef<[u8]> for str; - impl AsRef for str; help: try using a fully qualified path to specify the expected types | -LL | String::from(>::as_ref("x")); - | ++++++++++++++++++++++++++ ~ +LL - String::from("x".as_ref()); +LL + String::from(>::as_ref("x")); + | error[E0283]: type annotations needed --> $DIR/issue-72690.rs:46:5 @@ -161,14 +172,16 @@ LL | String::from("x".as_ref()); | ^^^^^^ | = note: multiple `impl`s satisfying `str: AsRef<_>` found in the following crates: `core`, `std`: + - impl AsRef for str; - impl AsRef for str; - impl AsRef for str; - impl AsRef<[u8]> for str; - impl AsRef for str; help: try using a fully qualified path to specify the expected types | -LL | String::from(>::as_ref("x")); - | ++++++++++++++++++++++++++ ~ +LL - String::from("x".as_ref()); +LL + String::from(>::as_ref("x")); + | error[E0283]: type annotations needed --> $DIR/issue-72690.rs:53:5 @@ -187,14 +200,16 @@ LL | String::from("x".as_ref()); | ^^^^^^ | = note: multiple `impl`s satisfying `str: AsRef<_>` found in the following crates: `core`, `std`: + - impl AsRef for str; - impl AsRef for str; - impl AsRef for str; - impl AsRef<[u8]> for str; - impl AsRef for str; help: try using a fully qualified path to specify the expected types | -LL | String::from(>::as_ref("x")); - | ++++++++++++++++++++++++++ ~ +LL - String::from("x".as_ref()); +LL + String::from(>::as_ref("x")); + | error[E0283]: type annotations needed --> $DIR/issue-72690.rs:62:5 @@ -213,14 +228,16 @@ LL | String::from("x".as_ref()); | ^^^^^^ | = note: multiple `impl`s satisfying `str: AsRef<_>` found in the following crates: `core`, `std`: + - impl AsRef for str; - impl AsRef for str; - impl AsRef for str; - impl AsRef<[u8]> for str; - impl AsRef for str; help: try using a fully qualified path to specify the expected types | -LL | String::from(>::as_ref("x")); - | ++++++++++++++++++++++++++ ~ +LL - String::from("x".as_ref()); +LL + String::from(>::as_ref("x")); + | error: aborting due to 17 previous errors diff --git a/tests/ui/inference/issue-80816.stderr b/tests/ui/inference/issue-80816.stderr index ab62db8e6af21..bca7cd4c3adbb 100644 --- a/tests/ui/inference/issue-80816.stderr +++ b/tests/ui/inference/issue-80816.stderr @@ -21,8 +21,9 @@ LL | impl, P: Deref> Access for P { | unsatisfied trait bound introduced here help: try using a fully qualified path to specify the expected types | -LL | let guard: Guard> = >> as Access>::load(&s); - | ++++++++++++++++++++++++++++++++++++++++++++++++++ ~ +LL - let guard: Guard> = s.load(); +LL + let guard: Guard> = >> as Access>::load(&s); + | error: aborting due to 1 previous error diff --git a/tests/ui/inference/issue-83606.stderr b/tests/ui/inference/issue-83606.stderr index 69d1d71ef3cc0..97ccad9e785e3 100644 --- a/tests/ui/inference/issue-83606.stderr +++ b/tests/ui/inference/issue-83606.stderr @@ -11,8 +11,8 @@ LL | fn foo(_: impl std::fmt::Display) -> [usize; N] { | ^^^^^^^^^^^^^^ required by this const generic parameter in `foo` help: consider giving this pattern a type, where the value of const parameter `N` is specified | -LL | let _: [usize; N] = foo("foo"); - | ++++++++++++ +LL | let _: [_; N] = foo("foo"); + | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/inference/need_type_info/type-alias.stderr b/tests/ui/inference/need_type_info/type-alias.stderr index 2c39a3f56466f..47393557828fd 100644 --- a/tests/ui/inference/need_type_info/type-alias.stderr +++ b/tests/ui/inference/need_type_info/type-alias.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed --> $DIR/type-alias.rs:12:5 | LL | DirectAlias::new() - | ^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T` + | ^^^^^^^^^^^ cannot infer type for type parameter `T` declared on the type alias `DirectAlias` error[E0282]: type annotations needed --> $DIR/type-alias.rs:18:5 @@ -14,7 +14,7 @@ error[E0282]: type annotations needed --> $DIR/type-alias.rs:32:5 | LL | DirectButWithDefaultAlias::new(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T` declared on the type alias `DirectButWithDefaultAlias` error: aborting due to 3 previous errors diff --git a/tests/ui/inference/really-long-type-in-let-binding-without-sufficient-type-info.rs b/tests/ui/inference/really-long-type-in-let-binding-without-sufficient-type-info.rs new file mode 100644 index 0000000000000..4fd15eea9e0f1 --- /dev/null +++ b/tests/ui/inference/really-long-type-in-let-binding-without-sufficient-type-info.rs @@ -0,0 +1,10 @@ +type A = (i32, i32, i32, i32); +type B = (A, A, A, A); +type C = (B, B, B, B); +type D = (C, C, C, C); + +fn foo(x: D) { + let y = Err(x); //~ ERROR type annotations needed for `Result<_ +} + +fn main() {} diff --git a/tests/ui/inference/really-long-type-in-let-binding-without-sufficient-type-info.stderr b/tests/ui/inference/really-long-type-in-let-binding-without-sufficient-type-info.stderr new file mode 100644 index 0000000000000..65fe2ffcb7f16 --- /dev/null +++ b/tests/ui/inference/really-long-type-in-let-binding-without-sufficient-type-info.stderr @@ -0,0 +1,14 @@ +error[E0282]: type annotations needed for `Result<_, ((((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32)), ((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32)), ((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32)), ((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32))), (((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32)), ((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32)), ((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32)), ((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32))), (((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32)), ((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32)), ((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32)), ((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32))), (((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32)), ((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32)), ((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32)), ((i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32), (i32, i32, i32, i32))))>` + --> $DIR/really-long-type-in-let-binding-without-sufficient-type-info.rs:7:9 + | +LL | let y = Err(x); + | ^ ------ type must be known at this point + | +help: consider giving `y` an explicit type, where the type for type parameter `T` is specified + | +LL | let y: Result = Err(x); + | ++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/inference/str-as-char.stderr b/tests/ui/inference/str-as-char.stderr index 4ca71c5f067fd..2e70faa8cfac3 100644 --- a/tests/ui/inference/str-as-char.stderr +++ b/tests/ui/inference/str-as-char.stderr @@ -6,8 +6,9 @@ LL | let _: &str = '"""'; | help: if you meant to write a string literal, use double quotes | -LL | let _: &str = "\"\"\""; - | ~~~~~~~~ +LL - let _: &str = '"""'; +LL + let _: &str = "\"\"\""; + | error: character literal may only contain one codepoint --> $DIR/str-as-char.rs:9:19 @@ -17,8 +18,9 @@ LL | let _: &str = '\"\"\"'; | help: if you meant to write a string literal, use double quotes | -LL | let _: &str = "\"\"\""; - | ~ ~ +LL - let _: &str = '\"\"\"'; +LL + let _: &str = "\"\"\""; + | error: character literal may only contain one codepoint --> $DIR/str-as-char.rs:10:19 @@ -28,8 +30,9 @@ LL | let _: &str = '"\"\"\\"\\"'; | help: if you meant to write a string literal, use double quotes | -LL | let _: &str = "\"\"\\"\\"\\\""; - | ~~~~~~~~~~~~~~~~~~~~ +LL - let _: &str = '"\"\"\\"\\"'; +LL + let _: &str = "\"\"\\"\\"\\\""; + | error[E0308]: mismatched types --> $DIR/str-as-char.rs:7:19 @@ -41,8 +44,9 @@ LL | let _: &str = 'a'; | help: if you meant to write a string literal, use double quotes | -LL | let _: &str = "a"; - | ~ ~ +LL - let _: &str = 'a'; +LL + let _: &str = "a"; + | error: aborting due to 4 previous errors diff --git a/tests/ui/infinite/infinite-assoc.stderr b/tests/ui/infinite/infinite-assoc.stderr index e6b91f1324138..c55d85942a4a8 100644 --- a/tests/ui/infinite/infinite-assoc.stderr +++ b/tests/ui/infinite/infinite-assoc.stderr @@ -6,8 +6,9 @@ LL | struct A((A, ::T)); | help: if you meant to express that the type might not contain a value, use the `Option` wrapper type | -LL | struct A((A, ::T>)); - | +++++++ ~ +LL - struct A((A, ::T)); +LL + struct A((A, ::T>)); + | error[E0072]: recursive type `A` has infinite size --> $DIR/infinite-assoc.rs:12:1 diff --git a/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.rs b/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.rs new file mode 100644 index 0000000000000..62fc079d9d974 --- /dev/null +++ b/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.rs @@ -0,0 +1,78 @@ +//@ build-fail + +//@ error-pattern: reached the recursion limit while instantiating +//@ error-pattern: reached the recursion limit finding the struct tail + +// The regex below normalizes the long type file name to make it suitable for compare-modes. +//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type.txt'" + +// Regression test for #114484: This used to ICE during monomorphization, because we treated +// ` as Pointee>::Metadata` as a rigid projection after reaching the recursion +// limit when finding the struct tail. + +use std::marker::PhantomData; + +trait MyTrait { + fn virtualize(&self) -> &dyn MyTrait; +} + +struct VirtualWrapper(T); + +impl VirtualWrapper { + pub fn wrap(value: &T) -> &Self { + unsafe { &*(value as *const T as *const Self) } + } +} + +impl MyTrait for VirtualWrapper { + fn virtualize(&self) -> &dyn MyTrait { + unsafe { virtualize_my_trait(L, self) } + // unsafe { virtualize_my_trait(L, &self.0) } // <-- this code fixes the problem + } +} + +const unsafe fn virtualize_my_trait(level: u8, obj: &T) -> &dyn MyTrait +where + T: MyTrait + 'static, +{ + const fn gen_vtable_ptr() -> *const () + where + T: MyTrait + 'static, + { + let [_, vtable] = unsafe { + std::mem::transmute::<*const dyn MyTrait, [*const (); 2]>(std::ptr::null::< + VirtualWrapper, + >()) + }; + vtable + } + + struct Vtable(PhantomData); + + impl Vtable + where + T: MyTrait + 'static, + { + const LEVELS: [*const (); 2] = [gen_vtable_ptr::(), gen_vtable_ptr::()]; + } + + let vtable = Vtable::::LEVELS[(level != 0) as usize]; + + let data = obj as *const T as *const (); + let ptr: *const dyn MyTrait = std::mem::transmute([data, vtable]); + + &*ptr +} + +struct SomeData([u8; N]); + +impl MyTrait for SomeData { + fn virtualize(&self) -> &dyn MyTrait { + VirtualWrapper::::wrap(self) + } +} + +fn main() { + let test = SomeData([0; 256]); + test.virtualize(); +} diff --git a/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.stderr b/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.stderr new file mode 100644 index 0000000000000..b67e000bf74a8 --- /dev/null +++ b/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.stderr @@ -0,0 +1,86 @@ +error: reached the recursion limit finding the struct tail for `[u8; 256]` + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` + +error: reached the recursion limit finding the struct tail for `[u8; 256]` + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: reached the recursion limit finding the struct tail for `[u8; 256]` + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: reached the recursion limit finding the struct tail for `[u8; 256]` + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +note: the above error was encountered while instantiating `fn virtualize_my_trait::, 0>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>>` + --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:29:18 + | +LL | unsafe { virtualize_my_trait(L, self) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: reached the recursion limit finding the struct tail for `SomeData<256>` + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` + +error: reached the recursion limit finding the struct tail for `SomeData<256>` + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: reached the recursion limit finding the struct tail for `SomeData<256>` + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: reached the recursion limit finding the struct tail for `SomeData<256>` + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +note: the above error was encountered while instantiating `fn virtualize_my_trait::, 0>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>>` + --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:29:18 + | +LL | unsafe { virtualize_my_trait(L, self) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` + +error: reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +note: the above error was encountered while instantiating `fn virtualize_my_trait::, 0>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>, 1>>` + --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:29:18 + | +LL | unsafe { virtualize_my_trait(L, self) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: reached the recursion limit while instantiating `, 1>, 1>, 1>, 1> as MyTrait>::virtualize` + | +note: ` as MyTrait>::virtualize` defined here + --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:28:5 + | +LL | fn virtualize(&self) -> &dyn MyTrait { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type.txt' + +error: aborting due to 13 previous errors + diff --git a/tests/ui/infinite/infinite-instantiation.polonius.stderr b/tests/ui/infinite/infinite-instantiation.polonius.stderr deleted file mode 100644 index f048c942f1a7b..0000000000000 --- a/tests/ui/infinite/infinite-instantiation.polonius.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: reached the recursion limit while instantiating `function::>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` - --> $DIR/infinite-instantiation.rs:22:9 - | -LL | function(counter - 1, t.to_option()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: `function` defined here - --> $DIR/infinite-instantiation.rs:20:1 - | -LL | fn function(counter: usize, t: T) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: the full type name has been written to '$TEST_BUILD_DIR/infinite/infinite-instantiation.polonius/infinite-instantiation.long-type.txt' - -error: aborting due to 1 previous error - diff --git a/tests/ui/infinite/infinite-instantiation.rs b/tests/ui/infinite/infinite-instantiation.rs index 7e1bff6b12420..d5cb8e79592f1 100644 --- a/tests/ui/infinite/infinite-instantiation.rs +++ b/tests/ui/infinite/infinite-instantiation.rs @@ -1,5 +1,6 @@ //@ build-fail -//@ normalize-stderr: ".nll/" -> "/" +// The regex below normalizes the long type file name to make it suitable for compare-modes. +//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type.txt'" trait ToOpt: Sized { fn to_option(&self) -> Option; diff --git a/tests/ui/infinite/infinite-instantiation.stderr b/tests/ui/infinite/infinite-instantiation.stderr index 43d267fa46bfc..71c745cf5eb8b 100644 --- a/tests/ui/infinite/infinite-instantiation.stderr +++ b/tests/ui/infinite/infinite-instantiation.stderr @@ -1,15 +1,15 @@ error: reached the recursion limit while instantiating `function::>>>>>` - --> $DIR/infinite-instantiation.rs:22:9 + --> $DIR/infinite-instantiation.rs:23:9 | LL | function(counter - 1, t.to_option()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `function` defined here - --> $DIR/infinite-instantiation.rs:20:1 + --> $DIR/infinite-instantiation.rs:21:1 | LL | fn function(counter: usize, t: T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: the full type name has been written to '$TEST_BUILD_DIR/infinite/infinite-instantiation/infinite-instantiation.long-type.txt' + = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type.txt' error: aborting due to 1 previous error diff --git a/tests/ui/infinite/infinite-trait-alias-recursion.rs b/tests/ui/infinite/infinite-trait-alias-recursion.rs index ec86744e68c53..0bd4624de3f1e 100644 --- a/tests/ui/infinite/infinite-trait-alias-recursion.rs +++ b/tests/ui/infinite/infinite-trait-alias-recursion.rs @@ -1,7 +1,7 @@ #![feature(trait_alias)] trait T1 = T2; -//~^ ERROR cycle detected when computing the super predicates of `T1` +//~^ ERROR cycle detected when computing the implied predicates of `T1` trait T2 = T3; diff --git a/tests/ui/infinite/infinite-trait-alias-recursion.stderr b/tests/ui/infinite/infinite-trait-alias-recursion.stderr index b3980cb935efe..5b0cbd5823155 100644 --- a/tests/ui/infinite/infinite-trait-alias-recursion.stderr +++ b/tests/ui/infinite/infinite-trait-alias-recursion.stderr @@ -1,20 +1,20 @@ -error[E0391]: cycle detected when computing the super predicates of `T1` +error[E0391]: cycle detected when computing the implied predicates of `T1` --> $DIR/infinite-trait-alias-recursion.rs:3:12 | LL | trait T1 = T2; | ^^ | -note: ...which requires computing the super predicates of `T2`... +note: ...which requires computing the implied predicates of `T2`... --> $DIR/infinite-trait-alias-recursion.rs:6:12 | LL | trait T2 = T3; | ^^ -note: ...which requires computing the super predicates of `T3`... +note: ...which requires computing the implied predicates of `T3`... --> $DIR/infinite-trait-alias-recursion.rs:8:12 | LL | trait T3 = T1 + T3; | ^^ - = note: ...which again requires computing the super predicates of `T1`, completing the cycle + = note: ...which again requires computing the implied predicates of `T1`, completing the cycle = note: trait aliases cannot be recursive note: cycle used when checking that `T1` is well-formed --> $DIR/infinite-trait-alias-recursion.rs:3:1 diff --git a/tests/ui/inline-const/collect-scopes-in-pat.rs b/tests/ui/inline-const/collect-scopes-in-pat.rs new file mode 100644 index 0000000000000..024fde537413f --- /dev/null +++ b/tests/ui/inline-const/collect-scopes-in-pat.rs @@ -0,0 +1,16 @@ +// @compile-flags: -Zlint-mir +//@ check-pass + +#![feature(inline_const_pat)] + +fn main() { + match 1 { + const { + || match 0 { + x => 0, + }; + 0 + } => (), + _ => (), + } +} diff --git a/tests/ui/inline-const/const-expr-lifetime-err.stderr b/tests/ui/inline-const/const-expr-lifetime-err.stderr index be3fc8d28c51e..031bf98262a87 100644 --- a/tests/ui/inline-const/const-expr-lifetime-err.stderr +++ b/tests/ui/inline-const/const-expr-lifetime-err.stderr @@ -6,10 +6,9 @@ LL | fn foo<'a>() { LL | let y = (); | - binding `y` declared here LL | equate(InvariantRef::new(&y), const { InvariantRef::<'a>::NEW }); - | ------------------^^- - | | | - | | borrowed value does not live long enough - | argument requires that `y` is borrowed for `'a` + | ^^ ----------------------- using this value as a constant requires that `y` is borrowed for `'a` + | | + | borrowed value does not live long enough LL | LL | } | - `y` dropped here while still borrowed diff --git a/tests/ui/inline-const/const-match-pat-lifetime-err.stderr b/tests/ui/inline-const/const-match-pat-lifetime-err.stderr index 95fe7085e5027..7eea1846057af 100644 --- a/tests/ui/inline-const/const-match-pat-lifetime-err.stderr +++ b/tests/ui/inline-const/const-match-pat-lifetime-err.stderr @@ -9,7 +9,7 @@ LL | match InvariantRef::new(&y) { | ^^ borrowed value does not live long enough LL | LL | const { InvariantRef::<'a>::NEW } => (), - | --------------------------------- type annotation requires that `y` is borrowed for `'a` + | ----------------------- using this value as a constant requires that `y` is borrowed for `'a` LL | } LL | } | - `y` dropped here while still borrowed diff --git a/tests/ui/inline-const/cross-const-control-flow-125846.stderr b/tests/ui/inline-const/cross-const-control-flow-125846.stderr index 4aa1c273504cc..0a910e70d09ec 100644 --- a/tests/ui/inline-const/cross-const-control-flow-125846.stderr +++ b/tests/ui/inline-const/cross-const-control-flow-125846.stderr @@ -26,8 +26,9 @@ LL | const { &x }; | help: consider using `const` instead of `let` | -LL | const x: /* Type */ = 1; - | ~~~~~ ++++++++++++ +LL - let x = 1; +LL + const x: /* Type */ = 1; + | error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/cross-const-control-flow-125846.rs:35:22 diff --git a/tests/ui/intrinsics/const-eval-select-bad.rs b/tests/ui/intrinsics/const-eval-select-bad.rs index 7e75de88d2926..3365d57af7ce4 100644 --- a/tests/ui/intrinsics/const-eval-select-bad.rs +++ b/tests/ui/intrinsics/const-eval-select-bad.rs @@ -30,7 +30,7 @@ fn baz(n: bool) -> i32 { const fn return_ty_mismatch() { const_eval_select((1,), foo, bar); - //~^ ERROR expected `bar` to be a fn item that returns `i32`, but it returns `bool` + //~^ ERROR expected `bar` to return `i32`, but it returns `bool` } const fn args_ty_mismatch() { diff --git a/tests/ui/intrinsics/const-eval-select-bad.stderr b/tests/ui/intrinsics/const-eval-select-bad.stderr index e317ed23ab103..bb159bed28224 100644 --- a/tests/ui/intrinsics/const-eval-select-bad.stderr +++ b/tests/ui/intrinsics/const-eval-select-bad.stderr @@ -60,7 +60,7 @@ LL | const_eval_select((), 42, 0xDEADBEEF); = note: expected a function item, found {integer} = help: consult the documentation on `const_eval_select` for more information -error[E0271]: expected `bar` to be a fn item that returns `i32`, but it returns `bool` +error[E0271]: expected `bar` to return `i32`, but it returns `bool` --> $DIR/const-eval-select-bad.rs:32:34 | LL | const_eval_select((1,), foo, bar); diff --git a/tests/ui/intrinsics/incorrect-read_via_copy-defn.rs b/tests/ui/intrinsics/incorrect-read_via_copy-defn.rs index 66de1f60ed9bc..5520430e140b3 100644 --- a/tests/ui/intrinsics/incorrect-read_via_copy-defn.rs +++ b/tests/ui/intrinsics/incorrect-read_via_copy-defn.rs @@ -3,5 +3,5 @@ fn main() { } extern "rust-intrinsic" fn read_via_copy() {} -//~^ ERROR intrinsics are subject to change +//~^ ERROR "rust-intrinsic" ABI is an implementation detail //~| ERROR intrinsic must be in `extern "rust-intrinsic" { ... }` block diff --git a/tests/ui/intrinsics/incorrect-read_via_copy-defn.stderr b/tests/ui/intrinsics/incorrect-read_via_copy-defn.stderr index 362ee185b7b17..c6682693f7409 100644 --- a/tests/ui/intrinsics/incorrect-read_via_copy-defn.stderr +++ b/tests/ui/intrinsics/incorrect-read_via_copy-defn.stderr @@ -1,4 +1,4 @@ -error[E0658]: intrinsics are subject to change +error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable --> $DIR/incorrect-read_via_copy-defn.rs:5:8 | LL | extern "rust-intrinsic" fn read_via_copy() {} diff --git a/tests/ui/intrinsics/incorrect-transmute.rs b/tests/ui/intrinsics/incorrect-transmute.rs index eed524ae8a8a2..15d1ab939ed0b 100644 --- a/tests/ui/intrinsics/incorrect-transmute.rs +++ b/tests/ui/intrinsics/incorrect-transmute.rs @@ -3,5 +3,5 @@ fn main() { } extern "rust-intrinsic" fn transmute() {} -//~^ ERROR intrinsics are subject to change +//~^ ERROR "rust-intrinsic" ABI is an implementation detail //~| ERROR intrinsic must be in `extern "rust-intrinsic" { ... }` block diff --git a/tests/ui/intrinsics/incorrect-transmute.stderr b/tests/ui/intrinsics/incorrect-transmute.stderr index 8123f3d71a295..99dfb9847ff2e 100644 --- a/tests/ui/intrinsics/incorrect-transmute.stderr +++ b/tests/ui/intrinsics/incorrect-transmute.stderr @@ -1,4 +1,4 @@ -error[E0658]: intrinsics are subject to change +error[E0658]: the extern "rust-intrinsic" ABI is an implementation detail and perma-unstable --> $DIR/incorrect-transmute.rs:5:8 | LL | extern "rust-intrinsic" fn transmute() {} diff --git a/tests/ui/intrinsics/not-overridden.rs b/tests/ui/intrinsics/not-overridden.rs index b57b4e5bc06db..2359eee8b26d6 100644 --- a/tests/ui/intrinsics/not-overridden.rs +++ b/tests/ui/intrinsics/not-overridden.rs @@ -9,8 +9,7 @@ //@ rustc-env:RUST_BACKTRACE=0 #[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {} +pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize); fn main() { unsafe { const_deallocate(std::ptr::null_mut(), 0, 0) } diff --git a/tests/ui/intrinsics/not-overridden.stderr b/tests/ui/intrinsics/not-overridden.stderr index b5273a4fa47fc..45c5c37318b89 100644 --- a/tests/ui/intrinsics/not-overridden.stderr +++ b/tests/ui/intrinsics/not-overridden.stderr @@ -1,5 +1,5 @@ error: must be overridden by codegen backend, but isn't - --> $DIR/not-overridden.rs:16:14 + --> $DIR/not-overridden.rs:15:14 | LL | unsafe { const_deallocate(std::ptr::null_mut(), 0, 0) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/intrinsics/panic-uninitialized-zeroed.rs b/tests/ui/intrinsics/panic-uninitialized-zeroed.rs index 67b9832d60140..346a94c37dd86 100644 --- a/tests/ui/intrinsics/panic-uninitialized-zeroed.rs +++ b/tests/ui/intrinsics/panic-uninitialized-zeroed.rs @@ -1,11 +1,10 @@ +// ignore-tidy-linelength +//! This test checks panic emitted from `mem::{uninitialized,zeroed}`. //@ run-pass //@ revisions: default strict //@ [strict]compile-flags: -Zstrict-init-checks -// ignore-tidy-linelength -//@ ignore-wasm32 spawning processes is not supported -//@ ignore-sgx no processes -// -// This test checks panic emitted from `mem::{uninitialized,zeroed}`. +//@ needs-subprocess + #![allow(deprecated, invalid_value)] #![feature(never_type)] diff --git a/tests/ui/intrinsics/reify-intrinsic.rs b/tests/ui/intrinsics/reify-intrinsic.rs index 0d047ccf4a324..5b2324235c1a9 100644 --- a/tests/ui/intrinsics/reify-intrinsic.rs +++ b/tests/ui/intrinsics/reify-intrinsic.rs @@ -3,17 +3,17 @@ #![feature(core_intrinsics, intrinsics)] fn a() { - let _: unsafe extern "rust-intrinsic" fn(isize) -> usize = std::mem::transmute; + let _: unsafe fn(isize) -> usize = std::mem::transmute; //~^ ERROR cannot coerce } fn b() { - let _ = std::mem::transmute as unsafe extern "rust-intrinsic" fn(isize) -> usize; + let _ = std::mem::transmute as unsafe fn(isize) -> usize; //~^ ERROR casting } fn c() { - let _: [unsafe extern "rust-intrinsic" fn(f32) -> f32; 2] = [ + let _: [unsafe fn(f32) -> f32; 2] = [ std::intrinsics::floorf32, //~ ERROR cannot coerce std::intrinsics::log2f32, ]; diff --git a/tests/ui/intrinsics/reify-intrinsic.stderr b/tests/ui/intrinsics/reify-intrinsic.stderr index aea6d263a727d..aea6f838e0d44 100644 --- a/tests/ui/intrinsics/reify-intrinsic.stderr +++ b/tests/ui/intrinsics/reify-intrinsic.stderr @@ -1,19 +1,19 @@ error[E0308]: cannot coerce intrinsics to function pointers - --> $DIR/reify-intrinsic.rs:6:64 + --> $DIR/reify-intrinsic.rs:6:40 | -LL | let _: unsafe extern "rust-intrinsic" fn(isize) -> usize = std::mem::transmute; - | ------------------------------------------------- ^^^^^^^^^^^^^^^^^^^ cannot coerce intrinsics to function pointers +LL | let _: unsafe fn(isize) -> usize = std::mem::transmute; + | ------------------------- ^^^^^^^^^^^^^^^^^^^ cannot coerce intrinsics to function pointers | | | expected due to this | - = note: expected fn pointer `unsafe extern "rust-intrinsic" fn(isize) -> usize` + = note: expected fn pointer `unsafe fn(isize) -> usize` found fn item `unsafe fn(_) -> _ {std::intrinsics::transmute::<_, _>}` -error[E0606]: casting `unsafe fn(_) -> _ {std::intrinsics::transmute::<_, _>}` as `unsafe extern "rust-intrinsic" fn(isize) -> usize` is invalid +error[E0606]: casting `unsafe fn(_) -> _ {std::intrinsics::transmute::<_, _>}` as `unsafe fn(isize) -> usize` is invalid --> $DIR/reify-intrinsic.rs:11:13 | -LL | let _ = std::mem::transmute as unsafe extern "rust-intrinsic" fn(isize) -> usize; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _ = std::mem::transmute as unsafe fn(isize) -> usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: cannot coerce intrinsics to function pointers --> $DIR/reify-intrinsic.rs:17:9 @@ -21,7 +21,7 @@ error[E0308]: cannot coerce intrinsics to function pointers LL | std::intrinsics::floorf32, | ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot coerce intrinsics to function pointers | - = note: expected fn pointer `unsafe extern "rust-intrinsic" fn(_) -> _` + = note: expected fn pointer `unsafe fn(_) -> _` found fn item `unsafe fn(_) -> _ {floorf32}` error: aborting due to 3 previous errors diff --git a/tests/ui/invalid-compile-flags/crate-type-flag.rs b/tests/ui/invalid-compile-flags/crate-type-flag.rs index 42bd72cbfbf52..07d853b330716 100644 --- a/tests/ui/invalid-compile-flags/crate-type-flag.rs +++ b/tests/ui/invalid-compile-flags/crate-type-flag.rs @@ -17,12 +17,12 @@ //@[staticlib] compile-flags: --crate-type=staticlib //@[staticlib] check-pass -//@[dylib] ignore-musl (dylibs are not supported) -//@[dylib] ignore-wasm (dylibs are not supported) +//@[dylib] ignore-musl (dylib is supported, but musl libc is statically linked by default) +//@[dylib] ignore-wasm (dylib is not supported) //@[dylib] compile-flags: --crate-type=dylib //@[dylib] check-pass -//@[cdylib] ignore-musl (cdylibs are not supported) +//@[cdylib] ignore-musl (cdylib is supported, but musl libc is statically linked by default) //@[cdylib] compile-flags: --crate-type=cdylib //@[cdylib] check-pass @@ -30,6 +30,7 @@ //@[bin] check-pass //@[proc_dash_macro] ignore-wasm (proc-macro is not supported) +//@[proc_dash_macro] needs-unwind (panic=abort causes warning to be emitted) //@[proc_dash_macro] compile-flags: --crate-type=proc-macro //@[proc_dash_macro] check-pass @@ -38,9 +39,7 @@ //@[multivalue] compile-flags: --crate-type=lib,rlib,staticlib //@[multivalue] check-pass -//@[multivalue_combined] ignore-musl (dylibs are not supported) -//@[multivalue_combined] ignore-wasm (dylibs are not supported) -//@[multivalue_combined] compile-flags: --crate-type=lib,rlib,staticlib --crate-type=dylib +//@[multivalue_combined] compile-flags: --crate-type=lib,rlib --crate-type=staticlib //@[multivalue_combined] check-pass // `proc-macro` is accepted, but `proc_macro` is not. diff --git a/tests/ui/invalid/invalid-rustc_legacy_const_generics-arguments.rs b/tests/ui/invalid/invalid-rustc_legacy_const_generics-arguments.rs index 6eabd9b1015b7..73a0363904aeb 100644 --- a/tests/ui/invalid/invalid-rustc_legacy_const_generics-arguments.rs +++ b/tests/ui/invalid/invalid-rustc_legacy_const_generics-arguments.rs @@ -21,7 +21,7 @@ struct S; #[rustc_legacy_const_generics(0usize)] //~ ERROR suffixed literals are not allowed in attributes fn foo6() {} -extern { +extern "C" { #[rustc_legacy_const_generics(1)] //~ ERROR attribute should be applied to a function fn foo7(); //~ ERROR foreign items may not have const parameters } diff --git a/tests/ui/invalid/invalid-rustc_legacy_const_generics-issue-123077.stderr b/tests/ui/invalid/invalid-rustc_legacy_const_generics-issue-123077.stderr index 7e05ae4f20c5c..d92836aa063b1 100644 --- a/tests/ui/invalid/invalid-rustc_legacy_const_generics-issue-123077.stderr +++ b/tests/ui/invalid/invalid-rustc_legacy_const_generics-issue-123077.stderr @@ -6,8 +6,9 @@ LL | std::arch::x86_64::_mm_blend_ps(loop {}, loop {}, || ()); | help: try using a const generic argument instead | -LL | std::arch::x86_64::_mm_blend_ps::<{ || () }>(loop {}, loop {}); - | +++++++++++++ ~~~~~~~~~~~~~~~~ +LL - std::arch::x86_64::_mm_blend_ps(loop {}, loop {}, || ()); +LL + std::arch::x86_64::_mm_blend_ps::<{ || () }>(loop {}, loop {}); + | error: invalid argument to a legacy const generic: cannot have const blocks, closures, async blocks or items --> $DIR/invalid-rustc_legacy_const_generics-issue-123077.rs:11:59 @@ -17,8 +18,9 @@ LL | std::arch::x86_64::_mm_blend_ps(loop {}, loop {}, 5 + || ()); | help: try using a const generic argument instead | -LL | std::arch::x86_64::_mm_blend_ps::<{ 5 + (|| ()) }>(loop {}, loop {}); - | +++++++++++++++++++ ~~~~~~~~~~~~~~~~ +LL - std::arch::x86_64::_mm_blend_ps(loop {}, loop {}, 5 + || ()); +LL + std::arch::x86_64::_mm_blend_ps::<{ 5 + (|| ()) }>(loop {}, loop {}); + | error: invalid argument to a legacy const generic: cannot have const blocks, closures, async blocks or items --> $DIR/invalid-rustc_legacy_const_generics-issue-123077.rs:14:61 @@ -28,8 +30,9 @@ LL | std::arch::x86_64::_mm_blend_ps(loop {}, loop {}, foo::<{ 1 + 2 }>()); | help: try using a const generic argument instead | -LL | std::arch::x86_64::_mm_blend_ps::<{ foo::<{ 1 + 2 }>() }>(loop {}, loop {}); - | ++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~ +LL - std::arch::x86_64::_mm_blend_ps(loop {}, loop {}, foo::<{ 1 + 2 }>()); +LL + std::arch::x86_64::_mm_blend_ps::<{ foo::<{ 1 + 2 }>() }>(loop {}, loop {}); + | error: invalid argument to a legacy const generic: cannot have const blocks, closures, async blocks or items --> $DIR/invalid-rustc_legacy_const_generics-issue-123077.rs:17:61 @@ -39,8 +42,9 @@ LL | std::arch::x86_64::_mm_blend_ps(loop {}, loop {}, foo::<3>()); | help: try using a const generic argument instead | -LL | std::arch::x86_64::_mm_blend_ps::<{ foo::<3>() }>(loop {}, loop {}); - | ++++++++++++++++++ ~~~~~~~~~~~~~~~~ +LL - std::arch::x86_64::_mm_blend_ps(loop {}, loop {}, foo::<3>()); +LL + std::arch::x86_64::_mm_blend_ps::<{ foo::<3>() }>(loop {}, loop {}); + | error: invalid argument to a legacy const generic: cannot have const blocks, closures, async blocks or items --> $DIR/invalid-rustc_legacy_const_generics-issue-123077.rs:20:56 @@ -50,8 +54,9 @@ LL | std::arch::x86_64::_mm_blend_ps(loop {}, loop {}, &const {}); | help: try using a const generic argument instead | -LL | std::arch::x86_64::_mm_blend_ps::<{ &const {} }>(loop {}, loop {}); - | +++++++++++++++++ ~~~~~~~~~~~~~~~~ +LL - std::arch::x86_64::_mm_blend_ps(loop {}, loop {}, &const {}); +LL + std::arch::x86_64::_mm_blend_ps::<{ &const {} }>(loop {}, loop {}); + | error: invalid argument to a legacy const generic: cannot have const blocks, closures, async blocks or items --> $DIR/invalid-rustc_legacy_const_generics-issue-123077.rs:24:9 @@ -75,8 +80,9 @@ LL | std::arch::x86_64::_mm_inserti_si64(loop {}, loop {}, || (), 1 + || ()) | help: try using a const generic argument instead | -LL | std::arch::x86_64::_mm_inserti_si64::<{ || () }, { 1 + (|| ()) }>(loop {}, loop {}); - | ++++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~ +LL - std::arch::x86_64::_mm_inserti_si64(loop {}, loop {}, || (), 1 + || ()); +LL + std::arch::x86_64::_mm_inserti_si64::<{ || () }, { 1 + (|| ()) }>(loop {}, loop {}); + | error: aborting due to 7 previous errors diff --git a/tests/ui/invalid_dispatch_from_dyn_impls.stderr b/tests/ui/invalid_dispatch_from_dyn_impls.stderr index 168ed37d0e69f..02718334c733e 100644 --- a/tests/ui/invalid_dispatch_from_dyn_impls.stderr +++ b/tests/ui/invalid_dispatch_from_dyn_impls.stderr @@ -1,4 +1,4 @@ -error[E0378]: the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment, and nothing else +error[E0378]: the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment that don't mention type/const generics, and nothing else --> $DIR/invalid_dispatch_from_dyn_impls.rs:10:1 | LL | / impl DispatchFromDyn> for WrapperWithExtraField @@ -35,7 +35,7 @@ LL | | where LL | | T: Unsize, | |_________________^ -error[E0378]: the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment, and nothing else +error[E0378]: the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment that don't mention type/const generics, and nothing else --> $DIR/invalid_dispatch_from_dyn_impls.rs:46:1 | LL | / impl DispatchFromDyn> for OverAligned diff --git a/tests/ui/issues/auxiliary/issue-111011.rs b/tests/ui/issues/auxiliary/issue-111011.rs deleted file mode 100644 index 0c1a8ce1cf6e7..0000000000000 --- a/tests/ui/issues/auxiliary/issue-111011.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ edition:2021 - -fn foo(x: impl FnOnce() -> Box) {} -// just to make sure async closures can still be suggested for boxing. -fn bar(x: Box X>) {} - -fn main() { - foo(async move || {}); //~ ERROR mismatched types - bar(async move || {}); //~ ERROR mismatched types -} diff --git a/tests/ui/issues/auxiliary/issue-111011.stderr b/tests/ui/issues/auxiliary/issue-111011.stderr deleted file mode 100644 index c0b48c5842f4a..0000000000000 --- a/tests/ui/issues/auxiliary/issue-111011.stderr +++ /dev/null @@ -1,34 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/issue-111011.rs:10:23 - | -LL | foo(async move || {}); - | ^^ expected `Box<_>`, found `async` closure body - | - = note: expected struct `Box<_>` - found `async` closure body `[async closure body@$DIR/issue-111011.rs:10:23: 10:25]` - = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html - -error[E0308]: mismatched types - --> $DIR/issue-111011.rs:11:9 - | -LL | bar(async move || {}); - | --- ^^^^^^^^^^^^^^^^ expected `Box _>`, found closure - | | - | arguments to this function are incorrect - | - = note: expected struct `Box<(dyn FnOnce() -> _ + 'static)>` - found closure `{closure@$DIR/issue-111011.rs:11:9: 11:22}` - = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html -note: function defined here - --> $DIR/issue-111011.rs:7:4 - | -LL | fn bar(x: Box X>) {} - | ^^^ ------------------------- -help: store this in the heap by calling `Box::new` - | -LL | bar(Box::new(async move || {})); - | +++++++++ + - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/issues/issue-12920.rs b/tests/ui/issues/issue-12920.rs index 7f453e499d48c..f3b1b643c45af 100644 --- a/tests/ui/issues/issue-12920.rs +++ b/tests/ui/issues/issue-12920.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:explicit panic -//@ ignore-emscripten no processes +//@ needs-subprocess pub fn main() { panic!(); diff --git a/tests/ui/issues/issue-13202.rs b/tests/ui/issues/issue-13202.rs index 89205fc7fd1a2..99ffba3fba51a 100644 --- a/tests/ui/issues/issue-13202.rs +++ b/tests/ui/issues/issue-13202.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:bad input -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { Some("foo").unwrap_or(panic!("bad input")).to_string(); diff --git a/tests/ui/issues/issue-13497.stderr b/tests/ui/issues/issue-13497.stderr index 7630848f6a512..8016b55d6aadc 100644 --- a/tests/ui/issues/issue-13497.stderr +++ b/tests/ui/issues/issue-13497.stderr @@ -11,8 +11,9 @@ LL | &'static str | +++++++ help: instead, you are more likely to want to return an owned value | -LL | String - | ~~~~~~ +LL - &str +LL + String + | error[E0515]: cannot return value referencing local variable `rawLines` --> $DIR/issue-13497.rs:5:5 diff --git a/tests/ui/issues/issue-17546.stderr b/tests/ui/issues/issue-17546.stderr index cf7ed1bbd6682..25a94dd97232b 100644 --- a/tests/ui/issues/issue-17546.stderr +++ b/tests/ui/issues/issue-17546.stderr @@ -9,12 +9,14 @@ LL | fn new() -> NoResult { | help: try using the variant's enum | -LL | fn new() -> foo::MyEnum { - | ~~~~~~~~~~~ +LL - fn new() -> NoResult { +LL + fn new() -> foo::MyEnum { + | help: an enum with a similar name exists | -LL | fn new() -> Result { - | ~~~~~~ +LL - fn new() -> NoResult { +LL + fn new() -> Result { + | error[E0573]: expected type, found variant `Result` --> $DIR/issue-17546.rs:24:17 @@ -61,12 +63,14 @@ LL | fn newer() -> NoResult { | help: try using the variant's enum | -LL | fn newer() -> foo::MyEnum { - | ~~~~~~~~~~~ +LL - fn newer() -> NoResult { +LL + fn newer() -> foo::MyEnum { + | help: an enum with a similar name exists | -LL | fn newer() -> Result { - | ~~~~~~ +LL - fn newer() -> NoResult { +LL + fn newer() -> Result { + | error: aborting due to 4 previous errors diff --git a/tests/ui/issues/issue-17800.stderr b/tests/ui/issues/issue-17800.stderr index 2a15af50d02a0..322c77eaa1dc5 100644 --- a/tests/ui/issues/issue-17800.stderr +++ b/tests/ui/issues/issue-17800.stderr @@ -6,8 +6,9 @@ LL | MyOption::MySome { x: 42 } => (), | help: use the tuple variant pattern syntax instead | -LL | MyOption::MySome(42) => (), - | ~~~~ +LL - MyOption::MySome { x: 42 } => (), +LL + MyOption::MySome(42) => (), + | error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-17954.stderr b/tests/ui/issues/issue-17954.stderr index bba7e725b1243..0dddea8336483 100644 --- a/tests/ui/issues/issue-17954.stderr +++ b/tests/ui/issues/issue-17954.stderr @@ -5,7 +5,7 @@ LL | let a = &FOO; | ^^^^ thread-local variables cannot be borrowed beyond the end of the function ... LL | } - | - end of enclosing function is here + | - end of enclosing function is here error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-18107.stderr b/tests/ui/issues/issue-18107.stderr index 705f7d0df12ae..dbf2889c22336 100644 --- a/tests/ui/issues/issue-18107.stderr +++ b/tests/ui/issues/issue-18107.stderr @@ -6,8 +6,9 @@ LL | dyn AbstractRenderer | help: consider returning an `impl Trait` instead of a `dyn Trait` | -LL | impl AbstractRenderer - | ~~~~ +LL - dyn AbstractRenderer +LL + impl AbstractRenderer + | help: alternatively, box the return type, and wrap all of the returned values in `Box::new` | LL ~ Box diff --git a/tests/ui/issues/issue-18446.stderr b/tests/ui/issues/issue-18446.stderr index 08a9cfc644f45..25ae303e902b5 100644 --- a/tests/ui/issues/issue-18446.stderr +++ b/tests/ui/issues/issue-18446.stderr @@ -16,8 +16,9 @@ LL | fn foo(&self) {} | ^^^^^^^^^^^^^ help: disambiguate the method for candidate #1 | -LL | T::foo(&x); - | ~~~~~~~~~~ +LL - x.foo(); +LL + T::foo(&x); + | error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-18611.stderr b/tests/ui/issues/issue-18611.stderr index 918654215b372..4fa699de63527 100644 --- a/tests/ui/issues/issue-18611.stderr +++ b/tests/ui/issues/issue-18611.stderr @@ -11,19 +11,17 @@ LL | trait HasState { | ^^^^^^^^^^^^^^ error[E0277]: the trait bound `isize: HasState` is not satisfied - --> $DIR/issue-18611.rs:1:46 + --> $DIR/issue-18611.rs:1:18 | -LL | fn add_state(op: ::State) { - | ______________________________________________^ -... | -LL | | } - | |_^ the trait `HasState` is not implemented for `isize` +LL | fn add_state(op: ::State) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `HasState` is not implemented for `isize` | help: this trait has no implementations, consider adding one --> $DIR/issue-18611.rs:6:1 | LL | trait HasState { | ^^^^^^^^^^^^^^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 2 previous errors diff --git a/tests/ui/issues/issue-18959.stderr b/tests/ui/issues/issue-18959.stderr index 5bb452250aa40..c37c4177bfc1e 100644 --- a/tests/ui/issues/issue-18959.stderr +++ b/tests/ui/issues/issue-18959.stderr @@ -1,77 +1,82 @@ -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/issue-18959.rs:11:12 | LL | fn foo(b: &dyn Bar) { - | ^^^^^^^ `Bar` cannot be made into an object + | ^^^^^^^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-18959.rs:1:20 | LL | pub trait Foo { fn foo(&self, ext_thing: &T); } | ^^^ ...because method `foo` has generic type parameters LL | pub trait Bar: Foo { } - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... = help: consider moving `foo` to another trait -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/issue-18959.rs:13:5 | LL | b.foo(&0) - | ^^^^^^^^^ `Bar` cannot be made into an object + | ^^^^^^^^^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-18959.rs:1:20 | LL | pub trait Foo { fn foo(&self, ext_thing: &T); } | ^^^ ...because method `foo` has generic type parameters LL | pub trait Bar: Foo { } - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... = help: consider moving `foo` to another trait -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/issue-18959.rs:19:15 | LL | let test: &dyn Bar = &mut thing; - | ^^^^^^^^ `Bar` cannot be made into an object + | ^^^^^^^^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-18959.rs:1:20 | LL | pub trait Foo { fn foo(&self, ext_thing: &T); } | ^^^ ...because method `foo` has generic type parameters LL | pub trait Bar: Foo { } - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... = help: consider moving `foo` to another trait -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/issue-18959.rs:19:26 | LL | let test: &dyn Bar = &mut thing; - | ^^^^^^^^^^ `Bar` cannot be made into an object + | ^^^^^^^^^^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-18959.rs:1:20 | LL | pub trait Foo { fn foo(&self, ext_thing: &T); } | ^^^ ...because method `foo` has generic type parameters LL | pub trait Bar: Foo { } - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... = help: consider moving `foo` to another trait = note: required for the cast from `&mut Thing` to `&dyn Bar` -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/issue-18959.rs:22:9 | LL | foo(test); - | ^^^^ `Bar` cannot be made into an object + | ^^^^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-18959.rs:1:20 | LL | pub trait Foo { fn foo(&self, ext_thing: &T); } | ^^^ ...because method `foo` has generic type parameters LL | pub trait Bar: Foo { } - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... = help: consider moving `foo` to another trait error: aborting due to 5 previous errors diff --git a/tests/ui/issues/issue-19380.stderr b/tests/ui/issues/issue-19380.stderr index afbe67befa1ab..f8509891d3abb 100644 --- a/tests/ui/issues/issue-19380.stderr +++ b/tests/ui/issues/issue-19380.stderr @@ -1,17 +1,18 @@ -error[E0038]: the trait `Qiz` cannot be made into an object +error[E0038]: the trait `Qiz` is not dyn compatible --> $DIR/issue-19380.rs:11:29 | LL | foos: &'static [&'static (dyn Qiz + 'static)] - | ^^^^^^^^^^^^^^^^^ `Qiz` cannot be made into an object + | ^^^^^^^^^^^^^^^^^ `Qiz` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-19380.rs:2:6 | LL | trait Qiz { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn qiz(); | ^^^ ...because associated function `qiz` has no `self` parameter - = help: only type `Foo` implements the trait, consider using it directly instead + = help: only type `Foo` implements `Qiz`; consider using it directly instead. help: consider turning `qiz` into a method by giving it a `&self` argument | LL | fn qiz(&self); @@ -21,20 +22,21 @@ help: alternatively, consider constraining `qiz` so it does not apply to trait o LL | fn qiz() where Self: Sized; | +++++++++++++++++ -error[E0038]: the trait `Qiz` cannot be made into an object +error[E0038]: the trait `Qiz` is not dyn compatible --> $DIR/issue-19380.rs:16:33 | LL | const BAR : Bar = Bar { foos: &[&FOO]}; - | ^^^^ `Qiz` cannot be made into an object + | ^^^^ `Qiz` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-19380.rs:2:6 | LL | trait Qiz { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn qiz(); | ^^^ ...because associated function `qiz` has no `self` parameter - = help: only type `Foo` implements the trait, consider using it directly instead + = help: only type `Foo` implements `Qiz`; consider using it directly instead. = note: required for the cast from `&Foo` to `&'static (dyn Qiz + 'static)` help: consider turning `qiz` into a method by giving it a `&self` argument | @@ -45,20 +47,21 @@ help: alternatively, consider constraining `qiz` so it does not apply to trait o LL | fn qiz() where Self: Sized; | +++++++++++++++++ -error[E0038]: the trait `Qiz` cannot be made into an object +error[E0038]: the trait `Qiz` is not dyn compatible --> $DIR/issue-19380.rs:16:31 | LL | const BAR : Bar = Bar { foos: &[&FOO]}; - | ^^^^^^^ `Qiz` cannot be made into an object + | ^^^^^^^ `Qiz` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-19380.rs:2:6 | LL | trait Qiz { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn qiz(); | ^^^ ...because associated function `qiz` has no `self` parameter - = help: only type `Foo` implements the trait, consider using it directly instead + = help: only type `Foo` implements `Qiz`; consider using it directly instead. help: consider turning `qiz` into a method by giving it a `&self` argument | LL | fn qiz(&self); diff --git a/tests/ui/issues/issue-20225.stderr b/tests/ui/issues/issue-20225.stderr index 7d6b09cf7f893..6a3c4e2a836e0 100644 --- a/tests/ui/issues/issue-20225.stderr +++ b/tests/ui/issues/issue-20225.stderr @@ -10,8 +10,9 @@ LL | extern "rust-call" fn call(&self, (_,): (T,)) {} found signature `extern "rust-call" fn(&Foo, (_,))` help: change the parameter type to match the trait | -LL | extern "rust-call" fn call(&self, (_,): (&'a T,)) {} - | ~~~~~~~~ +LL - extern "rust-call" fn call(&self, (_,): (T,)) {} +LL + extern "rust-call" fn call(&self, (_,): (&'a T,)) {} + | error[E0053]: method `call_mut` has an incompatible type for trait --> $DIR/issue-20225.rs:11:51 @@ -25,8 +26,9 @@ LL | extern "rust-call" fn call_mut(&mut self, (_,): (T,)) {} found signature `extern "rust-call" fn(&mut Foo, (_,))` help: change the parameter type to match the trait | -LL | extern "rust-call" fn call_mut(&mut self, (_,): (&'a T,)) {} - | ~~~~~~~~ +LL - extern "rust-call" fn call_mut(&mut self, (_,): (T,)) {} +LL + extern "rust-call" fn call_mut(&mut self, (_,): (&'a T,)) {} + | error[E0053]: method `call_once` has an incompatible type for trait --> $DIR/issue-20225.rs:18:47 @@ -41,8 +43,9 @@ LL | extern "rust-call" fn call_once(self, (_,): (T,)) {} found signature `extern "rust-call" fn(Foo, (_,))` help: change the parameter type to match the trait | -LL | extern "rust-call" fn call_once(self, (_,): (&'a T,)) {} - | ~~~~~~~~ +LL - extern "rust-call" fn call_once(self, (_,): (T,)) {} +LL + extern "rust-call" fn call_once(self, (_,): (&'a T,)) {} + | error: aborting due to 3 previous errors diff --git a/tests/ui/issues/issue-20971.rs b/tests/ui/issues/issue-20971.rs index 377a3d9ea304b..31dd910191935 100644 --- a/tests/ui/issues/issue-20971.rs +++ b/tests/ui/issues/issue-20971.rs @@ -2,7 +2,7 @@ //@ run-fail //@ error-pattern:Hello, world! -//@ ignore-emscripten no processes +//@ needs-subprocess pub trait Parser { type Input; diff --git a/tests/ui/issues/issue-21332.stderr b/tests/ui/issues/issue-21332.stderr index 7c960f7646db7..237b3acc9b4df 100644 --- a/tests/ui/issues/issue-21332.stderr +++ b/tests/ui/issues/issue-21332.stderr @@ -8,8 +8,9 @@ LL | fn next(&mut self) -> Result { Ok(7) } found signature `fn(&mut S) -> Result` help: change the output type to match the trait | -LL | fn next(&mut self) -> Option { Ok(7) } - | ~~~~~~~~~~~ +LL - fn next(&mut self) -> Result { Ok(7) } +LL + fn next(&mut self) -> Option { Ok(7) } + | error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-2190-1.rs b/tests/ui/issues/issue-2190-1.rs index 8db4a84aac860..e4e4bf9dbbee5 100644 --- a/tests/ui/issues/issue-2190-1.rs +++ b/tests/ui/issues/issue-2190-1.rs @@ -1,20 +1,16 @@ -//@ run-pass -#![allow(unused_must_use)] -#![allow(non_upper_case_globals)] - -//@ ignore-emscripten no threads +//@ check-pass use std::thread::Builder; -static generations: usize = 1024+256+128+49; +static GENERATIONS: usize = 1024+256+128+49; fn spawn(mut f: Box) { - Builder::new().stack_size(32 * 1024).spawn(move|| f()); + Builder::new().stack_size(32 * 1024).spawn(move || f()); } fn child_no(x: usize) -> Box { - Box::new(move|| { - if x < generations { + Box::new(move || { + if x < GENERATIONS { spawn(child_no(x+1)); } }) diff --git a/tests/ui/issues/issue-22370.stderr b/tests/ui/issues/issue-22370.stderr index 3dc060963f920..b02d867eb7dd9 100644 --- a/tests/ui/issues/issue-22370.stderr +++ b/tests/ui/issues/issue-22370.stderr @@ -7,7 +7,7 @@ LL | LL | fn f(a: &dyn A) {} | ^ | - = note: because of the default `Self` reference, type parameters must be specified on object types + = note: because the parameter default references `Self`, the parameter must be specified on the object type help: set the type parameter to the desired type | LL | fn f(a: &dyn A) {} diff --git a/tests/ui/issues/issue-22638.polonius.stderr b/tests/ui/issues/issue-22638.polonius.stderr deleted file mode 100644 index 3a94ed7bd3da2..0000000000000 --- a/tests/ui/issues/issue-22638.polonius.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: reached the recursion limit while instantiating `A::matches::$CLOSURE` - --> $DIR/issue-22638.rs:56:9 - | -LL | a.matches(f) - | ^^^^^^^^^^^^ - | -note: `A::matches` defined here - --> $DIR/issue-22638.rs:15:5 - | -LL | pub fn matches(&self, f: &F) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: the full type name has been written to '$TEST_BUILD_DIR/issues/issue-22638.polonius/issue-22638.long-type.txt' - -error: aborting due to 1 previous error - diff --git a/tests/ui/issues/issue-23041.stderr b/tests/ui/issues/issue-23041.stderr index 0142926dd1562..bd0e457fa9da5 100644 --- a/tests/ui/issues/issue-23041.stderr +++ b/tests/ui/issues/issue-23041.stderr @@ -6,8 +6,9 @@ LL | b.downcast_ref::_>(); | help: consider specifying the generic argument | -LL | b.downcast_ref:: _>(); - | ~~~~~~~~~~~~~~ +LL - b.downcast_ref::_>(); +LL + b.downcast_ref:: _>(); + | error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-23073.stderr b/tests/ui/issues/issue-23073.stderr index 6a61df8d46b2c..8aa86887bcfc6 100644 --- a/tests/ui/issues/issue-23073.stderr +++ b/tests/ui/issues/issue-23073.stderr @@ -6,8 +6,9 @@ LL | type FooT = <::Foo>::T; | help: if there were a trait named `Example` with associated type `T` implemented for `::Foo`, you could use the fully-qualified path | -LL | type FooT = <::Foo as Example>::T; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - type FooT = <::Foo>::T; +LL + type FooT = <::Foo as Example>::T; + | error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-23217.stderr b/tests/ui/issues/issue-23217.stderr index d14da75ab72e6..830d260f99d7d 100644 --- a/tests/ui/issues/issue-23217.stderr +++ b/tests/ui/issues/issue-23217.stderr @@ -8,8 +8,9 @@ LL | B = SomeEnum::A, | help: there is a variant with a similar name | -LL | B = SomeEnum::B, - | ~ +LL - B = SomeEnum::A, +LL + B = SomeEnum::B, + | error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-23354-2.rs b/tests/ui/issues/issue-23354-2.rs index 90de1276cc6bc..a296477215ed1 100644 --- a/tests/ui/issues/issue-23354-2.rs +++ b/tests/ui/issues/issue-23354-2.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:panic evaluated -//@ ignore-emscripten no processes +//@ needs-subprocess #[allow(unused_variables)] fn main() { diff --git a/tests/ui/issues/issue-23354.rs b/tests/ui/issues/issue-23354.rs index 31783842dac00..eff5f3d27147a 100644 --- a/tests/ui/issues/issue-23354.rs +++ b/tests/ui/issues/issue-23354.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:panic evaluated -//@ ignore-emscripten no processes +//@ needs-subprocess #[allow(unused_variables)] fn main() { diff --git a/tests/ui/issues/issue-23589.stderr b/tests/ui/issues/issue-23589.stderr index 21d383b0e8ce2..726efea1d6f63 100644 --- a/tests/ui/issues/issue-23589.stderr +++ b/tests/ui/issues/issue-23589.stderr @@ -6,8 +6,9 @@ LL | let v: Vec(&str) = vec!['1', '2']; | help: use angle brackets instead | -LL | let v: Vec<&str> = vec!['1', '2']; - | ~ ~ +LL - let v: Vec(&str) = vec!['1', '2']; +LL + let v: Vec<&str> = vec!['1', '2']; + | error[E0308]: mismatched types --> $DIR/issue-23589.rs:2:29 @@ -17,8 +18,9 @@ LL | let v: Vec(&str) = vec!['1', '2']; | help: if you meant to write a string literal, use double quotes | -LL | let v: Vec(&str) = vec!["1", '2']; - | ~ ~ +LL - let v: Vec(&str) = vec!['1', '2']; +LL + let v: Vec(&str) = vec!["1", '2']; + | error: aborting due to 2 previous errors diff --git a/tests/ui/issues/issue-2470-bounds-check-overflow.rs b/tests/ui/issues/issue-2470-bounds-check-overflow.rs index 241bc8fda9c21..4f300454dcb6e 100644 --- a/tests/ui/issues/issue-2470-bounds-check-overflow.rs +++ b/tests/ui/issues/issue-2470-bounds-check-overflow.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:index out of bounds -//@ ignore-emscripten no processes +//@ needs-subprocess use std::mem; diff --git a/tests/ui/issues/issue-25089.rs b/tests/ui/issues/issue-25089.rs index ea9ab290904c3..929738c3e794d 100644 --- a/tests/ui/issues/issue-25089.rs +++ b/tests/ui/issues/issue-25089.rs @@ -1,6 +1,6 @@ //@ run-pass //@ needs-unwind -//@ ignore-emscripten no threads support +//@ needs-threads use std::thread; diff --git a/tests/ui/issues/issue-25901.rs b/tests/ui/issues/issue-25901.rs index eae038c71a0ea..0ca34da95f55d 100644 --- a/tests/ui/issues/issue-25901.rs +++ b/tests/ui/issues/issue-25901.rs @@ -2,7 +2,7 @@ struct A; struct B; static S: &'static B = &A; -//~^ ERROR cannot call conditionally-const method +//~^ ERROR cannot perform non-const deref coercion use std::ops::Deref; diff --git a/tests/ui/issues/issue-25901.stderr b/tests/ui/issues/issue-25901.stderr index 655a8b78c6a1e..233b5bfee5075 100644 --- a/tests/ui/issues/issue-25901.stderr +++ b/tests/ui/issues/issue-25901.stderr @@ -1,13 +1,23 @@ -error[E0658]: cannot call conditionally-const method `::deref` in statics +error[E0015]: cannot perform non-const deref coercion on `A` in statics --> $DIR/issue-25901.rs:4:24 | LL | static S: &'static B = &A; | ^^ | - = note: see issue #67792 for more information - = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: attempting to deref into `B` +note: deref defined here + --> $DIR/issue-25901.rs:10:5 + | +LL | type Target = B; + | ^^^^^^^^^^^ +note: impl defined here, but it is not `const` + --> $DIR/issue-25901.rs:9:1 + | +LL | impl Deref for A { + | ^^^^^^^^^^^^^^^^ + = note: calls in statics are limited to constant functions, tuple structs and tuple variants + = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)` error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0658`. +For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/issues/issue-26056.stderr b/tests/ui/issues/issue-26056.stderr index be5453ec19dfc..c2168af94969b 100644 --- a/tests/ui/issues/issue-26056.stderr +++ b/tests/ui/issues/issue-26056.stderr @@ -1,16 +1,17 @@ -error[E0038]: the trait `Map` cannot be made into an object - --> $DIR/issue-26056.rs:20:13 +error[E0038]: the trait `Map` is not dyn compatible + --> $DIR/issue-26056.rs:20:17 | LL | as &dyn Map; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Map` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^^^^^^ `Map` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-26056.rs:9:12 | LL | trait Map: MapLookup<::Key> { | --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...because it uses `Self` as a type parameter | | - | this trait cannot be made into an object... + | this trait is not dyn compatible... error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-26655.rs b/tests/ui/issues/issue-26655.rs index 7f1858fdb7d0e..416472b0b269e 100644 --- a/tests/ui/issues/issue-26655.rs +++ b/tests/ui/issues/issue-26655.rs @@ -1,6 +1,6 @@ //@ run-pass //@ needs-unwind -//@ ignore-emscripten no threads support +//@ needs-threads // Check that the destructors of simple enums are run on unwinding diff --git a/tests/ui/issues/issue-27433.stderr b/tests/ui/issues/issue-27433.stderr index f6d5fc2b768f5..d8fd3d84ecbbc 100644 --- a/tests/ui/issues/issue-27433.stderr +++ b/tests/ui/issues/issue-27433.stderr @@ -6,8 +6,9 @@ LL | const FOO : u32 = foo; | help: consider using `let` instead of `const` | -LL | let FOO : u32 = foo; - | ~~~ +LL - const FOO : u32 = foo; +LL + let FOO : u32 = foo; + | error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-2761.rs b/tests/ui/issues/issue-2761.rs index b44a24e09f258..6df7a5118b820 100644 --- a/tests/ui/issues/issue-2761.rs +++ b/tests/ui/issues/issue-2761.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:custom message -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { assert!(false, "custom message"); diff --git a/tests/ui/issues/issue-28971.stderr b/tests/ui/issues/issue-28971.stderr index 7ca57d6b99817..fd689a2b36eeb 100644 --- a/tests/ui/issues/issue-28971.stderr +++ b/tests/ui/issues/issue-28971.stderr @@ -9,8 +9,9 @@ LL | Foo::Baz(..) => (), | help: there is a variant with a similar name | -LL | Foo::Bar(..) => (), - | ~~~ +LL - Foo::Baz(..) => (), +LL + Foo::Bar(..) => (), + | error[E0596]: cannot borrow `f` as mutable, as it is not declared as mutable --> $DIR/issue-28971.rs:15:5 diff --git a/tests/ui/issues/issue-29485.rs b/tests/ui/issues/issue-29485.rs index 56865d0e5db15..a44bcd49c6a9c 100644 --- a/tests/ui/issues/issue-29485.rs +++ b/tests/ui/issues/issue-29485.rs @@ -2,7 +2,7 @@ #![allow(unused_attributes)] //@ aux-build:issue-29485.rs //@ needs-unwind -//@ ignore-emscripten no threads +//@ needs-threads #[feature(recover)] diff --git a/tests/ui/issues/issue-30018-panic.rs b/tests/ui/issues/issue-30018-panic.rs index f5482d7c741df..591848b6f7b4e 100644 --- a/tests/ui/issues/issue-30018-panic.rs +++ b/tests/ui/issues/issue-30018-panic.rs @@ -5,7 +5,7 @@ // SIGTRAP injected by the drop-flag consistency checking. //@ needs-unwind -//@ ignore-emscripten no threads support +//@ needs-threads struct Foo; diff --git a/tests/ui/issues/issue-3029.rs b/tests/ui/issues/issue-3029.rs index a070578969cb2..22d0906ccf701 100644 --- a/tests/ui/issues/issue-3029.rs +++ b/tests/ui/issues/issue-3029.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:so long -//@ ignore-emscripten no processes +//@ needs-subprocess #![allow(unreachable_code)] diff --git a/tests/ui/issues/issue-30380.rs b/tests/ui/issues/issue-30380.rs index 534bb3423d0fb..49fce3d150cd7 100644 --- a/tests/ui/issues/issue-30380.rs +++ b/tests/ui/issues/issue-30380.rs @@ -3,7 +3,7 @@ //@ run-fail //@ error-pattern:panicking destructors ftw! -//@ ignore-emscripten no processes +//@ needs-subprocess struct Observer<'a>(&'a mut FilledOnDrop); diff --git a/tests/ui/issues/issue-32004.stderr b/tests/ui/issues/issue-32004.stderr index 2d2ed5a63015b..fcbec97661b4b 100644 --- a/tests/ui/issues/issue-32004.stderr +++ b/tests/ui/issues/issue-32004.stderr @@ -12,11 +12,12 @@ LL | Foo::Bar => {} help: use the tuple variant pattern syntax instead | LL | Foo::Bar(_) => {} - | ~~~~~~~~~~~ + | +++ help: a unit variant with a similar name exists | -LL | Foo::Baz => {} - | ~~~ +LL - Foo::Bar => {} +LL + Foo::Baz => {} + | error[E0532]: expected tuple struct or tuple variant, found unit struct `S` --> $DIR/issue-32004.rs:16:9 diff --git a/tests/ui/issues/issue-33293.rs b/tests/ui/issues/issue-33293.rs index a6ef007d51fb5..115ae3aad204c 100644 --- a/tests/ui/issues/issue-33293.rs +++ b/tests/ui/issues/issue-33293.rs @@ -1,6 +1,6 @@ fn main() { match 0 { aaa::bbb(_) => () - //~^ ERROR failed to resolve: use of undeclared crate or module `aaa` + //~^ ERROR failed to resolve: use of unresolved module or unlinked crate `aaa` }; } diff --git a/tests/ui/issues/issue-33293.stderr b/tests/ui/issues/issue-33293.stderr index 5badaa153f2b1..a82813194d77b 100644 --- a/tests/ui/issues/issue-33293.stderr +++ b/tests/ui/issues/issue-33293.stderr @@ -1,8 +1,10 @@ -error[E0433]: failed to resolve: use of undeclared crate or module `aaa` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `aaa` --> $DIR/issue-33293.rs:3:9 | LL | aaa::bbb(_) => () - | ^^^ use of undeclared crate or module `aaa` + | ^^^ use of unresolved module or unlinked crate `aaa` + | + = help: you might be missing a crate named `aaa` error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-33770.rs b/tests/ui/issues/issue-33770.rs index 0fa91ac91c4d3..814e8f371765e 100644 --- a/tests/ui/issues/issue-33770.rs +++ b/tests/ui/issues/issue-33770.rs @@ -1,6 +1,5 @@ //@ run-pass -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess use std::process::{Command, Stdio}; use std::env; diff --git a/tests/ui/issues/issue-33941.current.stderr b/tests/ui/issues/issue-33941.current.stderr new file mode 100644 index 0000000000000..d653bbd327427 --- /dev/null +++ b/tests/ui/issues/issue-33941.current.stderr @@ -0,0 +1,32 @@ +error[E0271]: expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)` + --> $DIR/issue-33941.rs:9:36 + | +LL | for _ in HashMap::new().iter().cloned() {} + | ^^^^^^ expected `&_`, found `(&_, &_)` + | + = note: expected reference `&_` + found tuple `(&_, &_)` +note: the method call chain might not have had the expected associated types + --> $DIR/issue-33941.rs:9:29 + | +LL | for _ in HashMap::new().iter().cloned() {} + | -------------- ^^^^^^ `Iterator::Item` is `(&_, &_)` here + | | + | this expression has type `HashMap<_, _>` +note: required by a bound in `cloned` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + +error[E0271]: expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)` + --> $DIR/issue-33941.rs:9:14 + | +LL | for _ in HashMap::new().iter().cloned() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&_`, found `(&_, &_)` + | + = note: expected reference `&_` + found tuple `(&_, &_)` + = note: required for `Cloned>` to implement `Iterator` + = note: required for `Cloned>` to implement `IntoIterator` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/issues/issue-33941.next.stderr b/tests/ui/issues/issue-33941.next.stderr new file mode 100644 index 0000000000000..a5a6e51545a58 --- /dev/null +++ b/tests/ui/issues/issue-33941.next.stderr @@ -0,0 +1,25 @@ +error[E0271]: expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)` + --> $DIR/issue-33941.rs:9:36 + | +LL | for _ in HashMap::new().iter().cloned() {} + | ^^^^^^ expected `&_`, found `(&_, &_)` + | + = note: expected reference `&_` + found tuple `(&_, &_)` +note: required by a bound in `cloned` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + +error[E0271]: expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)` + --> $DIR/issue-33941.rs:9:14 + | +LL | for _ in HashMap::new().iter().cloned() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&_`, found `(&_, &_)` + | + = note: expected reference `&_` + found tuple `(&_, &_)` + = note: required for `Cloned>` to implement `Iterator` + = note: required for `Cloned>` to implement `IntoIterator` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/issues/issue-33941.rs b/tests/ui/issues/issue-33941.rs index 7b5be30834b9d..b0736204a0811 100644 --- a/tests/ui/issues/issue-33941.rs +++ b/tests/ui/issues/issue-33941.rs @@ -1,8 +1,12 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver //@ compile-flags: -Zdeduplicate-diagnostics=yes use std::collections::HashMap; fn main() { - for _ in HashMap::new().iter().cloned() {} //~ ERROR expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)` + for _ in HashMap::new().iter().cloned() {} //~^ ERROR expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)` + //~| ERROR expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)` } diff --git a/tests/ui/issues/issue-33941.stderr b/tests/ui/issues/issue-33941.stderr deleted file mode 100644 index 9535ea57430d0..0000000000000 --- a/tests/ui/issues/issue-33941.stderr +++ /dev/null @@ -1,32 +0,0 @@ -error[E0271]: expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)` - --> $DIR/issue-33941.rs:6:36 - | -LL | for _ in HashMap::new().iter().cloned() {} - | ^^^^^^ expected `&_`, found `(&_, &_)` - | - = note: expected reference `&_` - found tuple `(&_, &_)` -note: the method call chain might not have had the expected associated types - --> $DIR/issue-33941.rs:6:29 - | -LL | for _ in HashMap::new().iter().cloned() {} - | -------------- ^^^^^^ `Iterator::Item` is `(&_, &_)` here - | | - | this expression has type `HashMap<_, _>` -note: required by a bound in `cloned` - --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL - -error[E0271]: expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)` - --> $DIR/issue-33941.rs:6:14 - | -LL | for _ in HashMap::new().iter().cloned() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&_`, found `(&_, &_)` - | - = note: expected reference `&_` - found tuple `(&_, &_)` - = note: required for `Cloned>` to implement `Iterator` - = note: required for `Cloned>` to implement `IntoIterator` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/issues/issue-34209.stderr b/tests/ui/issues/issue-34209.stderr index 4c61d250f52d1..83b40d0c08161 100644 --- a/tests/ui/issues/issue-34209.stderr +++ b/tests/ui/issues/issue-34209.stderr @@ -9,8 +9,9 @@ LL | S::B {} => {}, | help: there is a variant with a similar name | -LL | S::A {} => {}, - | ~ +LL - S::B {} => {}, +LL + S::A {} => {}, + | error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-3521-2.stderr b/tests/ui/issues/issue-3521-2.stderr index a12241cb1dfd5..ecf1ad0403d3e 100644 --- a/tests/ui/issues/issue-3521-2.stderr +++ b/tests/ui/issues/issue-3521-2.stderr @@ -6,8 +6,9 @@ LL | static y: isize = foo + 1; | help: consider using `let` instead of `static` | -LL | let y: isize = foo + 1; - | ~~~ +LL - static y: isize = foo + 1; +LL + let y: isize = foo + 1; + | error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-35570.stderr b/tests/ui/issues/issue-35570.stderr index 0aa6b5e402e44..b39b15fdf4b10 100644 --- a/tests/ui/issues/issue-35570.stderr +++ b/tests/ui/issues/issue-35570.stderr @@ -11,15 +11,10 @@ LL | trait Trait2<'a> { | ^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `for<'a> (): Trait2<'a>` is not satisfied - --> $DIR/issue-35570.rs:8:66 + --> $DIR/issue-35570.rs:8:16 | -LL | fn _ice(param: Box Trait1<<() as Trait2<'a>>::Ty>>) { - | __________________________________________________________________^ -LL | | -LL | | -LL | | let _e: (usize, usize) = unsafe{mem::transmute(param)}; -LL | | } - | |_^ the trait `for<'a> Trait2<'a>` is not implemented for `()` +LL | fn _ice(param: Box Trait1<<() as Trait2<'a>>::Ty>>) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> Trait2<'a>` is not implemented for `()` | help: this trait has no implementations, consider adding one --> $DIR/issue-35570.rs:4:1 diff --git a/tests/ui/issues/issue-3668-non-constant-value-in-constant/issue-3668-2.stderr b/tests/ui/issues/issue-3668-non-constant-value-in-constant/issue-3668-2.stderr index 9661dbf2f62fa..f87514ba83b04 100644 --- a/tests/ui/issues/issue-3668-non-constant-value-in-constant/issue-3668-2.stderr +++ b/tests/ui/issues/issue-3668-non-constant-value-in-constant/issue-3668-2.stderr @@ -6,8 +6,9 @@ LL | static child: isize = x + 1; | help: consider using `let` instead of `static` | -LL | let child: isize = x + 1; - | ~~~ +LL - static child: isize = x + 1; +LL + let child: isize = x + 1; + | error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-3668-non-constant-value-in-constant/issue-3668.stderr b/tests/ui/issues/issue-3668-non-constant-value-in-constant/issue-3668.stderr index 7fad45f4b1a25..06e0192d9574c 100644 --- a/tests/ui/issues/issue-3668-non-constant-value-in-constant/issue-3668.stderr +++ b/tests/ui/issues/issue-3668-non-constant-value-in-constant/issue-3668.stderr @@ -6,8 +6,9 @@ LL | static childVal: Box

= self.child.get(); | help: consider using `let` instead of `static` | -LL | let childVal: Box

= self.child.get(); - | ~~~ +LL - static childVal: Box

= self.child.get(); +LL + let childVal: Box

= self.child.get(); + | error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-3702-2.stderr b/tests/ui/issues/issue-3702-2.stderr index 7ab6520a0cad2..448263aafd44e 100644 --- a/tests/ui/issues/issue-3702-2.stderr +++ b/tests/ui/issues/issue-3702-2.stderr @@ -16,12 +16,14 @@ LL | fn to_int(&self) -> isize { 0 } | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: disambiguate the method for candidate #1 | -LL | Add::to_int(&self) + other.to_int() - | ~~~~~~~~~~~~~~~~~~ +LL - self.to_int() + other.to_int() +LL + Add::to_int(&self) + other.to_int() + | help: disambiguate the method for candidate #2 | -LL | ToPrimitive::to_int(&self) + other.to_int() - | ~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - self.to_int() + other.to_int() +LL + ToPrimitive::to_int(&self) + other.to_int() + | error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.polonius.stderr b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.polonius.stderr deleted file mode 100644 index 08b4573dd0b81..0000000000000 --- a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.polonius.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: reached the recursion limit while instantiating `<(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(.....), ...), ...) as Foo>::recurse` - --> $DIR/issue-37311.rs:17:9 - | -LL | (self, self).recurse(); - | ^^^^^^^^^^^^^^^^^^^^^^ - | -note: `::recurse` defined here - --> $DIR/issue-37311.rs:16:5 - | -LL | fn recurse(&self) { - | ^^^^^^^^^^^^^^^^^ - = note: the full type name has been written to '$TEST_BUILD_DIR/issues/issue-37311-type-length-limit/issue-37311.polonius/issue-37311.long-type.txt' - -error: aborting due to 1 previous error - diff --git a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs index edf4f2fce26a5..ebaf244ac9c24 100644 --- a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs +++ b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs @@ -1,5 +1,6 @@ //@ build-fail -//@ normalize-stderr: ".nll/" -> "/" +// The regex below normalizes the long type file name to make it suitable for compare-modes. +//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type.txt'" trait Mirror { type Image; diff --git a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr index 84ed97572b3af..fbbf80021bee9 100644 --- a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr +++ b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr @@ -1,15 +1,15 @@ error: reached the recursion limit while instantiating `<(&(&(..., ...), ...), ...) as Foo>::recurse` - --> $DIR/issue-37311.rs:17:9 + --> $DIR/issue-37311.rs:18:9 | LL | (self, self).recurse(); | ^^^^^^^^^^^^^^^^^^^^^^ | note: `::recurse` defined here - --> $DIR/issue-37311.rs:16:5 + --> $DIR/issue-37311.rs:17:5 | LL | fn recurse(&self) { | ^^^^^^^^^^^^^^^^^ - = note: the full type name has been written to '$TEST_BUILD_DIR/issues/issue-37311-type-length-limit/issue-37311/issue-37311.long-type.txt' + = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type.txt' error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-37534.rs b/tests/ui/issues/issue-37534.rs index dff89d3888d0c..09d60b7786b99 100644 --- a/tests/ui/issues/issue-37534.rs +++ b/tests/ui/issues/issue-37534.rs @@ -1,5 +1,5 @@ struct Foo {} //~^ ERROR expected trait, found derive macro `Hash` -//~| WARN relaxing a default bound only does something for `?Sized` +//~| ERROR relaxing a default bound only does something for `?Sized` fn main() {} diff --git a/tests/ui/issues/issue-37534.stderr b/tests/ui/issues/issue-37534.stderr index 8747bd5ac6fad..3219854bc70ce 100644 --- a/tests/ui/issues/issue-37534.stderr +++ b/tests/ui/issues/issue-37534.stderr @@ -9,12 +9,12 @@ help: consider importing this trait instead LL + use std::hash::Hash; | -warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +error: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default --> $DIR/issue-37534.rs:1:15 | LL | struct Foo {} | ^^^^^ -error: aborting due to 1 previous error; 1 warning emitted +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0404`. diff --git a/tests/ui/issues/issue-38763.rs b/tests/ui/issues/issue-38763.rs index 05cd648dcfe97..87c758db1723c 100644 --- a/tests/ui/issues/issue-38763.rs +++ b/tests/ui/issues/issue-38763.rs @@ -1,5 +1,5 @@ //@ run-pass -//@ ignore-emscripten +//@ needs-threads #[repr(C)] pub struct Foo(i128); diff --git a/tests/ui/issues/issue-39175.rs b/tests/ui/issues/issue-39175.rs deleted file mode 100644 index 7b801317b714b..0000000000000 --- a/tests/ui/issues/issue-39175.rs +++ /dev/null @@ -1,16 +0,0 @@ -// This test ignores some platforms as the particular extension trait used -// to demonstrate the issue is only available on unix. This is fine as -// the fix to suggested paths is not platform-dependent and will apply on -// these platforms also. - -//@ ignore-windows -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes - -use std::process::Command; -// use std::os::unix::process::CommandExt; - -fn main() { - Command::new("echo").arg("hello").exec(); -//~^ ERROR no method named `exec` -} diff --git a/tests/ui/issues/issue-39175.stderr b/tests/ui/issues/issue-39175.stderr deleted file mode 100644 index bbe8badb65231..0000000000000 --- a/tests/ui/issues/issue-39175.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error[E0599]: no method named `exec` found for mutable reference `&mut Command` in the current scope - --> $DIR/issue-39175.rs:14:39 - | -LL | Command::new("echo").arg("hello").exec(); - | ^^^^ - | - = help: items from traits can only be used if the trait is in scope -help: there is a method `pre_exec` with a similar name, but with different arguments - --> $SRC_DIR/std/src/os/unix/process.rs:LL:COL -help: trait `CommandExt` which provides `exec` is implemented but not in scope; perhaps you want to import it - | -LL + use std::os::unix::process::CommandExt; - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/issues/issue-40235.rs b/tests/ui/issues/issue-40235.rs deleted file mode 100644 index 2bdbb2f229e1f..0000000000000 --- a/tests/ui/issues/issue-40235.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ run-pass -#![allow(unused_variables)] -fn foo() {} - -fn main() { - while let Some(foo) = Some(1) { break } - foo(); -} diff --git a/tests/ui/issues/issue-40610.stderr b/tests/ui/issues/issue-40610.stderr deleted file mode 100644 index 1bd1c4dd57d7e..0000000000000 --- a/tests/ui/issues/issue-40610.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0369]: cannot add `()` to `()` - --> $DIR/issue-40610.rs:4:8 - | -LL | () + f(&[1.0]); - | -- ^ --------- () - | | - | () - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0369`. diff --git a/tests/ui/issues/issue-40749.stderr b/tests/ui/issues/issue-40749.stderr deleted file mode 100644 index f7770e00013c3..0000000000000 --- a/tests/ui/issues/issue-40749.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/issue-40749.rs:2:9 - | -LL | [0; ..10]; - | ^^^^ expected `usize`, found `RangeTo<{integer}>` - | - = note: expected type `usize` - found struct `RangeTo<{integer}>` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/issues/issue-40782.stderr b/tests/ui/issues/issue-40782.stderr deleted file mode 100644 index 614980202385b..0000000000000 --- a/tests/ui/issues/issue-40782.stderr +++ /dev/null @@ -1,24 +0,0 @@ -error: missing `in` in `for` loop - --> $DIR/issue-40782.rs:4:11 - | -LL | for _i 0..2 { - | ^ - | -help: try adding `in` here - | -LL | for _i in 0..2 { - | ++ - -error: missing `in` in `for` loop - --> $DIR/issue-40782.rs:6:12 - | -LL | for _i of 0..2 { - | ^^ - | -help: try using `in` here instead - | -LL | for _i in 0..2 { - | ~~ - -error: aborting due to 2 previous errors - diff --git a/tests/ui/issues/issue-40827.stderr b/tests/ui/issues/issue-40827.stderr deleted file mode 100644 index 7f5c578ae4fff..0000000000000 --- a/tests/ui/issues/issue-40827.stderr +++ /dev/null @@ -1,55 +0,0 @@ -error[E0277]: `Rc` cannot be shared between threads safely - --> $DIR/issue-40827.rs:14:7 - | -LL | f(Foo(Arc::new(Bar::B(None)))); - | - ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Rc` cannot be shared between threads safely - | | - | required by a bound introduced by this call - | - = help: within `Bar`, the trait `Sync` is not implemented for `Rc` -note: required because it appears within the type `Bar` - --> $DIR/issue-40827.rs:6:6 - | -LL | enum Bar { - | ^^^ - = note: required for `Arc` to implement `Send` -note: required because it appears within the type `Foo` - --> $DIR/issue-40827.rs:4:8 - | -LL | struct Foo(Arc); - | ^^^ -note: required by a bound in `f` - --> $DIR/issue-40827.rs:11:9 - | -LL | fn f(_: T) {} - | ^^^^ required by this bound in `f` - -error[E0277]: `Rc` cannot be sent between threads safely - --> $DIR/issue-40827.rs:14:7 - | -LL | f(Foo(Arc::new(Bar::B(None)))); - | - ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Rc` cannot be sent between threads safely - | | - | required by a bound introduced by this call - | - = help: within `Bar`, the trait `Send` is not implemented for `Rc` -note: required because it appears within the type `Bar` - --> $DIR/issue-40827.rs:6:6 - | -LL | enum Bar { - | ^^^ - = note: required for `Arc` to implement `Send` -note: required because it appears within the type `Foo` - --> $DIR/issue-40827.rs:4:8 - | -LL | struct Foo(Arc); - | ^^^ -note: required by a bound in `f` - --> $DIR/issue-40827.rs:11:9 - | -LL | fn f(_: T) {} - | ^^^^ required by this bound in `f` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/issues/issue-40845.stderr b/tests/ui/issues/issue-40845.stderr deleted file mode 100644 index 66bf053204c08..0000000000000 --- a/tests/ui/issues/issue-40845.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: cannot find macro `m` in this scope - --> $DIR/issue-40845.rs:1:11 - | -LL | trait T { m!(); } - | ^ - -error: cannot find macro `m` in this scope - --> $DIR/issue-40845.rs:4:10 - | -LL | impl S { m!(); } - | ^ - -error: aborting due to 2 previous errors - diff --git a/tests/ui/issues/issue-40861.stderr b/tests/ui/issues/issue-40861.stderr deleted file mode 100644 index dec9af4b6d1cb..0000000000000 --- a/tests/ui/issues/issue-40861.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0608]: cannot index into a value of type `()` - --> $DIR/issue-40861.rs:4:7 - | -LL | ()[f(&[1.0])]; - | ^^^^^^^^^^^ - | - = help: to access tuple elements, use tuple indexing syntax (e.g., `tuple.0`) - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0608`. diff --git a/tests/ui/issues/issue-41139.stderr b/tests/ui/issues/issue-41139.stderr deleted file mode 100644 index d7b35245d8f5a..0000000000000 --- a/tests/ui/issues/issue-41139.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0618]: expected function, found `&dyn Fn() -> (dyn Trait + 'static)` - --> $DIR/issue-41139.rs:10:26 - | -LL | fn get_function<'a>() -> &'a dyn Fn() -> dyn Trait { - | -------------------------------------------------- `get_function` defined here returns `&dyn Fn() -> (dyn Trait + 'static)` -... -LL | let t: &dyn Trait = &get_function()(); - | ^^^^^^^^^^^^^^ this trait object returns an unsized value `(dyn Trait + 'static)`, so it cannot be called - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0618`. diff --git a/tests/ui/issues/issue-41213.rs b/tests/ui/issues/issue-41213.rs deleted file mode 100644 index 97f80a99a8322..0000000000000 --- a/tests/ui/issues/issue-41213.rs +++ /dev/null @@ -1,24 +0,0 @@ -//@ run-pass -#![allow(dead_code)] -enum A { - A1, - A2, - A3, -} - -enum B { - B1(String, String), - B2(String, String), -} - -fn main() { - let a = A::A1; - loop { - let _ctor = match a { - A::A3 => break, - A::A1 => B::B1, - A::A2 => B::B2, - }; - break; - } -} diff --git a/tests/ui/issues/issue-41652/issue-41652.stderr b/tests/ui/issues/issue-41652/issue-41652.stderr index a5a2fab2ede03..4a81e76af75ec 100644 --- a/tests/ui/issues/issue-41652/issue-41652.stderr +++ b/tests/ui/issues/issue-41652/issue-41652.stderr @@ -7,7 +7,7 @@ LL | 3.f() help: you must specify a concrete type for this numeric value, like `i32` | LL | 3_i32.f() - | ~~~~~ + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-41726.stderr b/tests/ui/issues/issue-41726.stderr index 250bba222bfdb..d5123ab679f8b 100644 --- a/tests/ui/issues/issue-41726.stderr +++ b/tests/ui/issues/issue-41726.stderr @@ -7,8 +7,9 @@ LL | things[src.as_str()].sort(); = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `HashMap>` help: to modify a `HashMap>` use `.get_mut()` | -LL | if let Some(val) = things.get_mut(src.as_str()) { val.sort(); }; - | ++++++++++++++++++ ~~~~~~~~~ ~~~~~~~ +++ +LL - things[src.as_str()].sort(); +LL + if let Some(val) = things.get_mut(src.as_str()) { val.sort(); }; + | error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-42312.stderr b/tests/ui/issues/issue-42312.stderr index cbdc9ce0f8340..68d9912e3b1f7 100644 --- a/tests/ui/issues/issue-42312.stderr +++ b/tests/ui/issues/issue-42312.stderr @@ -25,8 +25,9 @@ LL | pub fn f(_: dyn ToString) {} = help: unsized fn params are gated as an unstable feature help: you can use `impl Trait` as the argument type | -LL | pub fn f(_: impl ToString) {} - | ~~~~ +LL - pub fn f(_: dyn ToString) {} +LL + pub fn f(_: impl ToString) {} + | help: function arguments must have a statically known size, borrowed types always have a known size | LL | pub fn f(_: &dyn ToString) {} diff --git a/tests/ui/issues/issue-44216-add-system-time.rs b/tests/ui/issues/issue-44216-add-system-time.rs index 207f72fade813..3838d28e33d1b 100644 --- a/tests/ui/issues/issue-44216-add-system-time.rs +++ b/tests/ui/issues/issue-44216-add-system-time.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:overflow -//@ ignore-emscripten no processes +//@ needs-subprocess use std::time::{Duration, SystemTime}; diff --git a/tests/ui/issues/issue-44216-sub-instant.rs b/tests/ui/issues/issue-44216-sub-instant.rs index 2457d2aaa0446..19cd12e685fd1 100644 --- a/tests/ui/issues/issue-44216-sub-instant.rs +++ b/tests/ui/issues/issue-44216-sub-instant.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:overflow -//@ ignore-emscripten no processes +//@ needs-subprocess use std::time::{Instant, Duration}; diff --git a/tests/ui/issues/issue-44216-sub-system-time.rs b/tests/ui/issues/issue-44216-sub-system-time.rs index 7e33f22793374..bd4f66f3e161b 100644 --- a/tests/ui/issues/issue-44216-sub-system-time.rs +++ b/tests/ui/issues/issue-44216-sub-system-time.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:overflow -//@ ignore-emscripten no processes +//@ needs-subprocess use std::time::{Duration, SystemTime}; diff --git a/tests/ui/issues/issue-44239.stderr b/tests/ui/issues/issue-44239.stderr index 1a047d4c63b69..8048e32f149a9 100644 --- a/tests/ui/issues/issue-44239.stderr +++ b/tests/ui/issues/issue-44239.stderr @@ -6,8 +6,9 @@ LL | const N: usize = n; | help: consider using `const` instead of `let` | -LL | const n: usize = 0; - | ~~~~~ +LL - let n: usize = 0; +LL + const n: usize = 0; + | error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-47073-zero-padded-tuple-struct-indices.stderr b/tests/ui/issues/issue-47073-zero-padded-tuple-struct-indices.stderr index 0aa1ae7222f57..0a6fe24d5e349 100644 --- a/tests/ui/issues/issue-47073-zero-padded-tuple-struct-indices.stderr +++ b/tests/ui/issues/issue-47073-zero-padded-tuple-struct-indices.stderr @@ -6,8 +6,9 @@ LL | let _condemned = justice.00; | help: a field with a similar name exists | -LL | let _condemned = justice.0; - | ~ +LL - let _condemned = justice.00; +LL + let _condemned = justice.0; + | error[E0609]: no field `001` on type `Verdict` --> $DIR/issue-47073-zero-padded-tuple-struct-indices.rs:10:31 diff --git a/tests/ui/issues/issue-4736.stderr b/tests/ui/issues/issue-4736.stderr index c1ae2c47b43a7..b099e528ca8a8 100644 --- a/tests/ui/issues/issue-4736.stderr +++ b/tests/ui/issues/issue-4736.stderr @@ -9,8 +9,9 @@ LL | let z = NonCopyable{ p: () }; | help: `NonCopyable` is a tuple struct, use the appropriate syntax | -LL | let z = NonCopyable(/* () */); - | ~~~~~~~~~~~~~~~~~~~~~ +LL - let z = NonCopyable{ p: () }; +LL + let z = NonCopyable(/* () */); + | error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-48728.current.stderr b/tests/ui/issues/issue-48728.current.stderr deleted file mode 100644 index 2a1b4ff781854..0000000000000 --- a/tests/ui/issues/issue-48728.current.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0119]: conflicting implementations of trait `Clone` for type `Node<[_]>` - --> $DIR/issue-48728.rs:9:10 - | -LL | #[derive(Clone)] - | ^^^^^ conflicting implementation for `Node<[_]>` -... -LL | impl Clone for Node<[T]> { - | ------------------------------------------- first implementation here - | - = note: upstream crates may add a new impl of trait `std::clone::Clone` for type `[_]` in future versions - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/issues/issue-48838.stderr b/tests/ui/issues/issue-48838.stderr deleted file mode 100644 index 504ea3e80103a..0000000000000 --- a/tests/ui/issues/issue-48838.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/issue-48838.rs:2:14 - | -LL | Square = |x| x, - | ^^^^^ expected `isize`, found closure - | - = note: expected type `isize` - found closure `{closure@$DIR/issue-48838.rs:2:14: 2:17}` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/issues/issue-4935.stderr b/tests/ui/issues/issue-4935.stderr index 7ee895d91c75d..918f74256c034 100644 --- a/tests/ui/issues/issue-4935.stderr +++ b/tests/ui/issues/issue-4935.stderr @@ -8,7 +8,7 @@ note: function defined here --> $DIR/issue-4935.rs:3:4 | LL | fn foo(a: usize) {} - | ^^^ -------- + | ^^^ help: remove the extra argument | LL - fn main() { foo(5, 6) } diff --git a/tests/ui/issues/issue-50571.stderr b/tests/ui/issues/issue-50571.stderr index 12256ded1c0a8..86709410670c1 100644 --- a/tests/ui/issues/issue-50571.stderr +++ b/tests/ui/issues/issue-50571.stderr @@ -6,8 +6,9 @@ LL | fn foo([a, b]: [i32; 2]) {} | help: give this argument a name or use an underscore to ignore it | -LL | fn foo(_: [i32; 2]) {} - | ~ +LL - fn foo([a, b]: [i32; 2]) {} +LL + fn foo(_: [i32; 2]) {} + | error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-50714-1.rs b/tests/ui/issues/issue-50714-1.rs deleted file mode 100644 index a25940ce1cbef..0000000000000 --- a/tests/ui/issues/issue-50714-1.rs +++ /dev/null @@ -1,11 +0,0 @@ -// Regression test for issue 50714, make sure that this isn't a linker error. - -#![no_std] -#![feature(start)] - -extern crate std; - -#[start] -fn start(_: isize, _: *const *const u8) -> isize where fn(&()): Eq { //~ ERROR [E0647] - 0 -} diff --git a/tests/ui/issues/issue-50714-1.stderr b/tests/ui/issues/issue-50714-1.stderr deleted file mode 100644 index 7593ac3834697..0000000000000 --- a/tests/ui/issues/issue-50714-1.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0647]: `#[start]` function is not allowed to have a `where` clause - --> $DIR/issue-50714-1.rs:9:50 - | -LL | fn start(_: isize, _: *const *const u8) -> isize where fn(&()): Eq { - | ^^^^^^^^^^^^^^^^^ `#[start]` function cannot have a `where` clause - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0647`. diff --git a/tests/ui/issues/issue-50781.rs b/tests/ui/issues/issue-50781.rs index 32253c3c23608..ab90db1cadcf2 100644 --- a/tests/ui/issues/issue-50781.rs +++ b/tests/ui/issues/issue-50781.rs @@ -9,11 +9,11 @@ impl X for () { } impl Trait for dyn X {} -//~^ ERROR the trait `X` cannot be made into an object +//~^ ERROR the trait `X` is not dyn compatible pub fn main() { // Check that this does not segfault. ::foo(&()); - //~^ ERROR the trait `X` cannot be made into an object - //~| ERROR the trait `X` cannot be made into an object + //~^ ERROR the trait `X` is not dyn compatible + //~| ERROR the trait `X` is not dyn compatible } diff --git a/tests/ui/issues/issue-50781.stderr b/tests/ui/issues/issue-50781.stderr index 3e54a53aa95f8..88b83a83e0cfa 100644 --- a/tests/ui/issues/issue-50781.stderr +++ b/tests/ui/issues/issue-50781.stderr @@ -1,51 +1,54 @@ -error[E0038]: the trait `X` cannot be made into an object +error[E0038]: the trait `X` is not dyn compatible --> $DIR/issue-50781.rs:11:16 | LL | impl Trait for dyn X {} - | ^^^^^ `X` cannot be made into an object + | ^^^^^ `X` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-50781.rs:4:8 | LL | trait X { - | - this trait cannot be made into an object... + | - this trait is not dyn compatible... LL | fn foo(&self) where Self: Trait; | ^^^ ...because method `foo` references the `Self` type in its `where` clause = help: consider moving `foo` to another trait - = help: only type `()` implements the trait, consider using it directly instead + = help: only type `()` implements `X`; consider using it directly instead. -error[E0038]: the trait `X` cannot be made into an object +error[E0038]: the trait `X` is not dyn compatible --> $DIR/issue-50781.rs:16:23 | LL | ::foo(&()); - | ^^^ `X` cannot be made into an object + | ^^^ `X` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-50781.rs:4:8 | LL | trait X { - | - this trait cannot be made into an object... + | - this trait is not dyn compatible... LL | fn foo(&self) where Self: Trait; | ^^^ ...because method `foo` references the `Self` type in its `where` clause = help: consider moving `foo` to another trait - = help: only type `()` implements the trait, consider using it directly instead + = help: only type `()` implements `X`; consider using it directly instead. = note: required for the cast from `&()` to `&dyn X` -error[E0038]: the trait `X` cannot be made into an object +error[E0038]: the trait `X` is not dyn compatible --> $DIR/issue-50781.rs:16:6 | LL | ::foo(&()); - | ^^^^^ `X` cannot be made into an object + | ^^^^^ `X` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-50781.rs:4:8 | LL | trait X { - | - this trait cannot be made into an object... + | - this trait is not dyn compatible... LL | fn foo(&self) where Self: Trait; | ^^^ ...because method `foo` references the `Self` type in its `where` clause = help: consider moving `foo` to another trait - = help: only type `()` implements the trait, consider using it directly instead + = help: only type `()` implements `X`; consider using it directly instead. error: aborting due to 3 previous errors diff --git a/tests/ui/issues/issue-51874.stderr b/tests/ui/issues/issue-51874.stderr index 5be3695dd4548..5c9331b4e1e1a 100644 --- a/tests/ui/issues/issue-51874.stderr +++ b/tests/ui/issues/issue-51874.stderr @@ -7,7 +7,7 @@ LL | let a = (1.0).pow(1.0); help: you must specify a concrete type for this numeric value, like `f32` | LL | let a = (1.0_f32).pow(1.0); - | ~~~~~~~ + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-5358-1.stderr b/tests/ui/issues/issue-5358-1.stderr index 1bb946ce4cb54..e68db865dc414 100644 --- a/tests/ui/issues/issue-5358-1.stderr +++ b/tests/ui/issues/issue-5358-1.stderr @@ -15,7 +15,7 @@ LL | S(Either::Right(_)) => {} help: you might have meant to use field `0` whose type is `Either` | LL | match S(Either::Left(5)).0 { - | ~~~~~~~~~~~~~~~~~~~~ + | ++ error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-56175.stderr b/tests/ui/issues/issue-56175.stderr index 6ed35c3a3d3a4..df4cd6ce8a700 100644 --- a/tests/ui/issues/issue-56175.stderr +++ b/tests/ui/issues/issue-56175.stderr @@ -17,7 +17,7 @@ LL + use reexported_trait::Trait; help: there is a method `trait_method_b` with a similar name | LL | reexported_trait::FooStruct.trait_method_b(); - | ~~~~~~~~~~~~~~ + | ++ error[E0599]: no method named `trait_method_b` found for struct `FooStruct` in the current scope --> $DIR/issue-56175.rs:7:33 @@ -37,8 +37,9 @@ LL + use reexported_trait::TraitBRename; | help: there is a method `trait_method` with a similar name | -LL | reexported_trait::FooStruct.trait_method(); - | ~~~~~~~~~~~~ +LL - reexported_trait::FooStruct.trait_method_b(); +LL + reexported_trait::FooStruct.trait_method(); + | error: aborting due to 2 previous errors diff --git a/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741.stderr b/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741.stderr index 3c19b68cffbb3..62d83a5461484 100644 --- a/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741.stderr +++ b/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741.stderr @@ -11,7 +11,7 @@ LL | T::A(a) | T::B(a) => a, help: consider dereferencing the boxed value | LL | let y = match *x { - | ~~ + | + error[E0308]: mismatched types --> $DIR/issue-57741.rs:20:19 @@ -26,7 +26,7 @@ LL | T::A(a) | T::B(a) => a, help: consider dereferencing the boxed value | LL | let y = match *x { - | ~~ + | + error[E0308]: mismatched types --> $DIR/issue-57741.rs:27:9 @@ -41,7 +41,7 @@ LL | S::A { a } | S::B { b: a } => a, help: consider dereferencing the boxed value | LL | let y = match *x { - | ~~ + | + error[E0308]: mismatched types --> $DIR/issue-57741.rs:27:22 @@ -56,7 +56,7 @@ LL | S::A { a } | S::B { b: a } => a, help: consider dereferencing the boxed value | LL | let y = match *x { - | ~~ + | + error: aborting due to 4 previous errors diff --git a/tests/ui/issues/issue-58734.rs b/tests/ui/issues/issue-58734.rs index 9b630666baf85..ee23be87b6b79 100644 --- a/tests/ui/issues/issue-58734.rs +++ b/tests/ui/issues/issue-58734.rs @@ -21,4 +21,5 @@ fn main() { //~^ ERROR no function or associated item named `nonexistent` found //~| WARN trait objects without an explicit `dyn` are deprecated //~| WARN this is accepted in the current edition + //~| ERROR the trait `Trait` is not dyn compatible } diff --git a/tests/ui/issues/issue-58734.stderr b/tests/ui/issues/issue-58734.stderr index a2acd9dcf8189..c4624cecc621c 100644 --- a/tests/ui/issues/issue-58734.stderr +++ b/tests/ui/issues/issue-58734.stderr @@ -12,12 +12,38 @@ help: if this is a dyn-compatible trait, use `dyn` LL | ::nonexistent(()); | ++++ + +error[E0038]: the trait `Trait` is not dyn compatible + --> $DIR/issue-58734.rs:20:5 + | +LL | Trait::nonexistent(()); + | ^^^^^ `Trait` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/issue-58734.rs:4:8 + | +LL | trait Trait { + | ----- this trait is not dyn compatible... +... +LL | fn dyn_incompatible() -> Self; + | ^^^^^^^^^^^^^^^^ ...because associated function `dyn_incompatible` has no `self` parameter + = help: only type `()` implements `Trait`; consider using it directly instead. +help: consider turning `dyn_incompatible` into a method by giving it a `&self` argument + | +LL | fn dyn_incompatible(&self) -> Self; + | +++++ +help: alternatively, consider constraining `dyn_incompatible` so it does not apply to trait objects + | +LL | fn dyn_incompatible() -> Self where Self: Sized; + | +++++++++++++++++ + error[E0599]: no function or associated item named `nonexistent` found for trait object `dyn Trait` in the current scope --> $DIR/issue-58734.rs:20:12 | LL | Trait::nonexistent(()); | ^^^^^^^^^^^ function or associated item not found in `dyn Trait` -error: aborting due to 1 previous error; 1 warning emitted +error: aborting due to 2 previous errors; 1 warning emitted -For more information about this error, try `rustc --explain E0599`. +Some errors have detailed explanations: E0038, E0599. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/issues/issue-5883.stderr b/tests/ui/issues/issue-5883.stderr index d481d0ef94eb2..7c51c0e17af13 100644 --- a/tests/ui/issues/issue-5883.stderr +++ b/tests/ui/issues/issue-5883.stderr @@ -8,8 +8,9 @@ LL | r: dyn A + 'static = help: unsized fn params are gated as an unstable feature help: you can use `impl Trait` as the argument type | -LL | r: impl A + 'static - | ~~~~ +LL - r: dyn A + 'static +LL + r: impl A + 'static + | help: function arguments must have a statically known size, borrowed types always have a known size | LL | r: &dyn A + 'static diff --git a/tests/ui/issues/issue-67552.polonius.stderr b/tests/ui/issues/issue-67552.polonius.stderr deleted file mode 100644 index ca42f87e81942..0000000000000 --- a/tests/ui/issues/issue-67552.polonius.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: reached the recursion limit while instantiating `rec::<&mut &mut &mut &mut &mut &... &mut &mut &mut &mut &mut Empty>` - --> $DIR/issue-67552.rs:28:9 - | -LL | rec(identity(&mut it)) - | ^^^^^^^^^^^^^^^^^^^^^^ - | -note: `rec` defined here - --> $DIR/issue-67552.rs:21:1 - | -LL | / fn rec(mut it: T) -LL | | where -LL | | T: Iterator, - | |________________^ - = note: the full type name has been written to '$TEST_BUILD_DIR/issues/issue-67552.polonius/issue-67552.long-type.txt' - -error: aborting due to 1 previous error - diff --git a/tests/ui/issues/issue-67552.rs b/tests/ui/issues/issue-67552.rs index 343ae4f262fc2..8c7e95bd2e3e7 100644 --- a/tests/ui/issues/issue-67552.rs +++ b/tests/ui/issues/issue-67552.rs @@ -1,6 +1,7 @@ //@ build-fail //@ compile-flags: -Copt-level=0 -//@ normalize-stderr: ".nll/" -> "/" +// The regex below normalizes the long type file name to make it suitable for compare-modes. +//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type.txt'" fn main() { rec(Empty); diff --git a/tests/ui/issues/issue-67552.stderr b/tests/ui/issues/issue-67552.stderr index 1a8d7248b4508..f94cd78c87011 100644 --- a/tests/ui/issues/issue-67552.stderr +++ b/tests/ui/issues/issue-67552.stderr @@ -1,17 +1,17 @@ error: reached the recursion limit while instantiating `rec::<&mut &mut &mut &mut &mut ...>` - --> $DIR/issue-67552.rs:29:9 + --> $DIR/issue-67552.rs:30:9 | LL | rec(identity(&mut it)) | ^^^^^^^^^^^^^^^^^^^^^^ | note: `rec` defined here - --> $DIR/issue-67552.rs:22:1 + --> $DIR/issue-67552.rs:23:1 | LL | / fn rec(mut it: T) LL | | where LL | | T: Iterator, | |________________^ - = note: the full type name has been written to '$TEST_BUILD_DIR/issues/issue-67552/issue-67552.long-type.txt' + = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type.txt' error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-69683.stderr b/tests/ui/issues/issue-69683.stderr index c428ea9ea2c5b..b8e9e89e56eb9 100644 --- a/tests/ui/issues/issue-69683.stderr +++ b/tests/ui/issues/issue-69683.stderr @@ -7,8 +7,9 @@ LL | 0u16.foo(b); = note: cannot satisfy `>::Array == [u8; 3]` help: try using a fully qualified path to specify the expected types | -LL | >::foo(0u16, b); - | +++++++++++++++++++++ ~ +LL - 0u16.foo(b); +LL + >::foo(0u16, b); + | error[E0283]: type annotations needed --> $DIR/issue-69683.rs:30:10 @@ -34,8 +35,9 @@ LL | fn foo(self, x: >::Array); | --- required by a bound in this associated function help: try using a fully qualified path to specify the expected types | -LL | >::foo(0u16, b); - | +++++++++++++++++++++ ~ +LL - 0u16.foo(b); +LL + >::foo(0u16, b); + | error: aborting due to 2 previous errors diff --git a/tests/ui/issues/issue-76077-inaccesible-private-fields/issue-76077-1.stderr b/tests/ui/issues/issue-76077-inaccesible-private-fields/issue-76077-1.stderr index 8e77662b4ba3f..eabb08421121e 100644 --- a/tests/ui/issues/issue-76077-inaccesible-private-fields/issue-76077-1.stderr +++ b/tests/ui/issues/issue-76077-inaccesible-private-fields/issue-76077-1.stderr @@ -6,8 +6,9 @@ LL | let foo::Foo {} = foo::Foo::default(); | help: ignore the inaccessible and unused fields | -LL | let foo::Foo { .. } = foo::Foo::default(); - | ~~~~~~ +LL - let foo::Foo {} = foo::Foo::default(); +LL + let foo::Foo { .. } = foo::Foo::default(); + | error: pattern requires `..` due to inaccessible fields --> $DIR/issue-76077-1.rs:16:9 diff --git a/tests/ui/issues/issue-76191.stderr b/tests/ui/issues/issue-76191.stderr index 3702bfb776991..a397d08214200 100644 --- a/tests/ui/issues/issue-76191.stderr +++ b/tests/ui/issues/issue-76191.stderr @@ -21,11 +21,12 @@ LL | RANGE => {} | `RANGE` is interpreted as a constant, not a new binding | = note: expected type `i32` - found struct `RangeInclusive` + found struct `std::ops::RangeInclusive` help: you may want to move the range into the match block | -LL | 0..=255 => {} - | ~~~~~~~ +LL - RANGE => {} +LL + 0..=255 => {} + | error[E0308]: mismatched types --> $DIR/issue-76191.rs:16:9 @@ -43,7 +44,7 @@ LL | RANGE2 => {} | `RANGE2` is interpreted as a constant, not a new binding | = note: expected type `i32` - found struct `RangeInclusive` + found struct `std::ops::RangeInclusive` = note: constants only support matching by type, if you meant to match against a range of values, consider using a range pattern like `min ..= max` in the match block error: aborting due to 3 previous errors diff --git a/tests/ui/issues/issue-78622.stderr b/tests/ui/issues/issue-78622.stderr index 985d6dde9f2aa..432913a0fc9c1 100644 --- a/tests/ui/issues/issue-78622.stderr +++ b/tests/ui/issues/issue-78622.stderr @@ -6,8 +6,9 @@ LL | S::A:: {} | help: if there were a trait named `Example` with associated type `A` implemented for `S`, you could use the fully-qualified path | -LL | ::A:: {} - | ~~~~~~~~~~~~~~~~~ +LL - S::A:: {} +LL + ::A:: {} + | error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-80607.stderr b/tests/ui/issues/issue-80607.stderr index d096910297b79..8f9f494c8b7a4 100644 --- a/tests/ui/issues/issue-80607.stderr +++ b/tests/ui/issues/issue-80607.stderr @@ -9,8 +9,9 @@ LL | Enum::V1 { x } | help: `Enum::V1` is a tuple variant, use the appropriate syntax | -LL | Enum::V1(/* i32 */) - | ~~~~~~~~~~~ +LL - Enum::V1 { x } +LL + Enum::V1(/* i32 */) + | error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-87199.rs b/tests/ui/issues/issue-87199.rs index 34879c5a7ca3b..6664119e57981 100644 --- a/tests/ui/issues/issue-87199.rs +++ b/tests/ui/issues/issue-87199.rs @@ -6,12 +6,12 @@ // Check that these function definitions only emit warnings, not errors fn arg(_: T) {} -//~^ warning: relaxing a default bound only does something for `?Sized` +//~^ ERROR: relaxing a default bound only does something for `?Sized` fn ref_arg(_: &T) {} -//~^ warning: relaxing a default bound only does something for `?Sized` +//~^ ERROR: relaxing a default bound only does something for `?Sized` fn ret() -> impl Iterator + ?Send { std::iter::empty() } -//~^ warning: relaxing a default bound only does something for `?Sized` -//~| warning: relaxing a default bound only does something for `?Sized` +//~^ ERROR: relaxing a default bound only does something for `?Sized` +//~| ERROR: relaxing a default bound only does something for `?Sized` // Check that there's no `?Sized` relaxation! fn main() { diff --git a/tests/ui/issues/issue-87199.stderr b/tests/ui/issues/issue-87199.stderr index a0ed2946fb4af..acc4e84779c9c 100644 --- a/tests/ui/issues/issue-87199.stderr +++ b/tests/ui/issues/issue-87199.stderr @@ -1,22 +1,22 @@ -warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +error: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default --> $DIR/issue-87199.rs:8:11 | LL | fn arg(_: T) {} | ^^^^^ -warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +error: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default --> $DIR/issue-87199.rs:10:15 | LL | fn ref_arg(_: &T) {} | ^^^^^ -warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +error: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default --> $DIR/issue-87199.rs:12:40 | LL | fn ret() -> impl Iterator + ?Send { std::iter::empty() } | ^^^^^ -warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +error: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default --> $DIR/issue-87199.rs:12:40 | LL | fn ret() -> impl Iterator + ?Send { std::iter::empty() } @@ -41,6 +41,6 @@ help: consider relaxing the implicit `Sized` restriction LL | fn ref_arg(_: &T) {} | ++++++++ -error: aborting due to 1 previous error; 4 warnings emitted +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/issues/issue-8727.polonius.stderr b/tests/ui/issues/issue-8727.polonius.stderr deleted file mode 100644 index 4fb8c2b3aff1c..0000000000000 --- a/tests/ui/issues/issue-8727.polonius.stderr +++ /dev/null @@ -1,26 +0,0 @@ -warning: function cannot return without recursing - --> $DIR/issue-8727.rs:7:1 - | -LL | fn generic() { - | ^^^^^^^^^^^^^^^ cannot return without recursing -LL | generic::>(); - | ---------------------- recursive call site - | - = note: `#[warn(unconditional_recursion)]` on by default - = help: a `loop` may express intention better if this is on purpose - -error: reached the recursion limit while instantiating `generic::>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` - --> $DIR/issue-8727.rs:8:5 - | -LL | generic::>(); - | ^^^^^^^^^^^^^^^^^^^^^^ - | -note: `generic` defined here - --> $DIR/issue-8727.rs:7:1 - | -LL | fn generic() { - | ^^^^^^^^^^^^^^^ - = note: the full type name has been written to '$TEST_BUILD_DIR/issues/issue-8727.polonius/issue-8727.long-type.txt' - -error: aborting due to 1 previous error; 1 warning emitted - diff --git a/tests/ui/issues/issue-8727.rs b/tests/ui/issues/issue-8727.rs index b824be7c12fe0..7767729109eac 100644 --- a/tests/ui/issues/issue-8727.rs +++ b/tests/ui/issues/issue-8727.rs @@ -2,7 +2,8 @@ // recursions. //@ build-fail -//@ normalize-stderr: ".nll/" -> "/" +// The regex below normalizes the long type file name to make it suitable for compare-modes. +//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type.txt'" fn generic() { //~ WARN function cannot return without recursing generic::>(); diff --git a/tests/ui/issues/issue-8727.stderr b/tests/ui/issues/issue-8727.stderr index 9af598fe43f52..22286eb8d7be0 100644 --- a/tests/ui/issues/issue-8727.stderr +++ b/tests/ui/issues/issue-8727.stderr @@ -1,5 +1,5 @@ warning: function cannot return without recursing - --> $DIR/issue-8727.rs:7:1 + --> $DIR/issue-8727.rs:8:1 | LL | fn generic() { | ^^^^^^^^^^^^^^^ cannot return without recursing @@ -10,17 +10,17 @@ LL | generic::>(); = note: `#[warn(unconditional_recursion)]` on by default error: reached the recursion limit while instantiating `generic::>>>>>` - --> $DIR/issue-8727.rs:8:5 + --> $DIR/issue-8727.rs:9:5 | LL | generic::>(); | ^^^^^^^^^^^^^^^^^^^^^^ | note: `generic` defined here - --> $DIR/issue-8727.rs:7:1 + --> $DIR/issue-8727.rs:8:1 | LL | fn generic() { | ^^^^^^^^^^^^^^^ - = note: the full type name has been written to '$TEST_BUILD_DIR/issues/issue-8727/issue-8727.long-type.txt' + = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type.txt' error: aborting due to 1 previous error; 1 warning emitted diff --git a/tests/ui/issues/issue-8761.stderr b/tests/ui/issues/issue-8761.stderr index c70093bafb69b..4a9db56891386 100644 --- a/tests/ui/issues/issue-8761.stderr +++ b/tests/ui/issues/issue-8761.stderr @@ -6,8 +6,9 @@ LL | A = 1i64, | help: change the type of the numeric literal from `i64` to `isize` | -LL | A = 1isize, - | ~~~~~ +LL - A = 1i64, +LL + A = 1isize, + | error[E0308]: mismatched types --> $DIR/issue-8761.rs:5:9 @@ -17,8 +18,9 @@ LL | B = 2u8 | help: change the type of the numeric literal from `u8` to `isize` | -LL | B = 2isize - | ~~~~~ +LL - B = 2u8 +LL + B = 2isize + | error: aborting due to 2 previous errors diff --git a/tests/ui/issues/issue-9575.rs b/tests/ui/issues/issue-9575.rs deleted file mode 100644 index 06b252990b642..0000000000000 --- a/tests/ui/issues/issue-9575.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![feature(start)] - -#[start] -fn start(argc: isize, argv: *const *const u8, crate_map: *const u8) -> isize { - //~^ `#[start]` function has wrong type - 0 -} diff --git a/tests/ui/issues/issue-9575.stderr b/tests/ui/issues/issue-9575.stderr deleted file mode 100644 index 2f6e2687d24c9..0000000000000 --- a/tests/ui/issues/issue-9575.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0308]: `#[start]` function has wrong type - --> $DIR/issue-9575.rs:4:1 - | -LL | fn start(argc: isize, argv: *const *const u8, crate_map: *const u8) -> isize { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of function parameters - | - = note: expected signature `fn(isize, *const *const u8) -> _` - found signature `fn(isize, *const *const u8, *const u8) -> _` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/iterators/into-iter-on-arrays-2018.stderr b/tests/ui/iterators/into-iter-on-arrays-2018.stderr index 9d6bbf06c36bb..d4055c74f7cef 100644 --- a/tests/ui/iterators/into-iter-on-arrays-2018.stderr +++ b/tests/ui/iterators/into-iter-on-arrays-2018.stderr @@ -9,12 +9,14 @@ LL | let _: Iter<'_, i32> = array.into_iter(); = note: `#[warn(array_into_iter)]` on by default help: use `.iter()` instead of `.into_iter()` to avoid ambiguity | -LL | let _: Iter<'_, i32> = array.iter(); - | ~~~~ +LL - let _: Iter<'_, i32> = array.into_iter(); +LL + let _: Iter<'_, i32> = array.iter(); + | help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value | -LL | let _: Iter<'_, i32> = IntoIterator::into_iter(array); - | ++++++++++++++++++++++++ ~ +LL - let _: Iter<'_, i32> = array.into_iter(); +LL + let _: Iter<'_, i32> = IntoIterator::into_iter(array); + | warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 --> $DIR/into-iter-on-arrays-2018.rs:18:44 @@ -53,8 +55,9 @@ LL | for _ in [1, 2, 3].into_iter() {} = note: for more information, see help: use `.iter()` instead of `.into_iter()` to avoid ambiguity | -LL | for _ in [1, 2, 3].iter() {} - | ~~~~ +LL - for _ in [1, 2, 3].into_iter() {} +LL + for _ in [1, 2, 3].iter() {} + | help: or remove `.into_iter()` to iterate by value | LL - for _ in [1, 2, 3].into_iter() {} diff --git a/tests/ui/iterators/into-iter-on-arrays-lint.stderr b/tests/ui/iterators/into-iter-on-arrays-lint.stderr index da388d6b848de..fb8fe79c7c966 100644 --- a/tests/ui/iterators/into-iter-on-arrays-lint.stderr +++ b/tests/ui/iterators/into-iter-on-arrays-lint.stderr @@ -9,12 +9,14 @@ LL | small.into_iter(); = note: `#[warn(array_into_iter)]` on by default help: use `.iter()` instead of `.into_iter()` to avoid ambiguity | -LL | small.iter(); - | ~~~~ +LL - small.into_iter(); +LL + small.iter(); + | help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value | -LL | IntoIterator::into_iter(small); - | ++++++++++++++++++++++++ ~ +LL - small.into_iter(); +LL + IntoIterator::into_iter(small); + | warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 --> $DIR/into-iter-on-arrays-lint.rs:14:12 @@ -26,12 +28,14 @@ LL | [1, 2].into_iter(); = note: for more information, see help: use `.iter()` instead of `.into_iter()` to avoid ambiguity | -LL | [1, 2].iter(); - | ~~~~ +LL - [1, 2].into_iter(); +LL + [1, 2].iter(); + | help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value | -LL | IntoIterator::into_iter([1, 2]); - | ++++++++++++++++++++++++ ~ +LL - [1, 2].into_iter(); +LL + IntoIterator::into_iter([1, 2]); + | warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 --> $DIR/into-iter-on-arrays-lint.rs:17:9 @@ -43,12 +47,14 @@ LL | big.into_iter(); = note: for more information, see help: use `.iter()` instead of `.into_iter()` to avoid ambiguity | -LL | big.iter(); - | ~~~~ +LL - big.into_iter(); +LL + big.iter(); + | help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value | -LL | IntoIterator::into_iter(big); - | ++++++++++++++++++++++++ ~ +LL - big.into_iter(); +LL + IntoIterator::into_iter(big); + | warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 --> $DIR/into-iter-on-arrays-lint.rs:20:15 @@ -60,12 +66,14 @@ LL | [0u8; 33].into_iter(); = note: for more information, see help: use `.iter()` instead of `.into_iter()` to avoid ambiguity | -LL | [0u8; 33].iter(); - | ~~~~ +LL - [0u8; 33].into_iter(); +LL + [0u8; 33].iter(); + | help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value | -LL | IntoIterator::into_iter([0u8; 33]); - | ++++++++++++++++++++++++ ~ +LL - [0u8; 33].into_iter(); +LL + IntoIterator::into_iter([0u8; 33]); + | warning: this method call resolves to `<&[T; N] as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to `<[T; N] as IntoIterator>::into_iter` in Rust 2021 --> $DIR/into-iter-on-arrays-lint.rs:24:21 diff --git a/tests/ui/iterators/into-iter-on-boxed-slices-2021.stderr b/tests/ui/iterators/into-iter-on-boxed-slices-2021.stderr index e82980303995a..7a5a2be5ef068 100644 --- a/tests/ui/iterators/into-iter-on-boxed-slices-2021.stderr +++ b/tests/ui/iterators/into-iter-on-boxed-slices-2021.stderr @@ -9,12 +9,14 @@ LL | let _: Iter<'_, i32> = boxed_slice.into_iter(); = note: `#[warn(boxed_slice_into_iter)]` on by default help: use `.iter()` instead of `.into_iter()` to avoid ambiguity | -LL | let _: Iter<'_, i32> = boxed_slice.iter(); - | ~~~~ +LL - let _: Iter<'_, i32> = boxed_slice.into_iter(); +LL + let _: Iter<'_, i32> = boxed_slice.iter(); + | help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value | -LL | let _: Iter<'_, i32> = IntoIterator::into_iter(boxed_slice); - | ++++++++++++++++++++++++ ~ +LL - let _: Iter<'_, i32> = boxed_slice.into_iter(); +LL + let _: Iter<'_, i32> = IntoIterator::into_iter(boxed_slice); + | warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to ` as IntoIterator>::into_iter` in Rust 2024 --> $DIR/into-iter-on-boxed-slices-2021.rs:18:58 @@ -53,8 +55,9 @@ LL | for _ in (Box::new([1, 2, 3]) as Box<[_]>).into_iter() {} = note: for more information, see help: use `.iter()` instead of `.into_iter()` to avoid ambiguity | -LL | for _ in (Box::new([1, 2, 3]) as Box<[_]>).iter() {} - | ~~~~ +LL - for _ in (Box::new([1, 2, 3]) as Box<[_]>).into_iter() {} +LL + for _ in (Box::new([1, 2, 3]) as Box<[_]>).iter() {} + | help: or remove `.into_iter()` to iterate by value | LL - for _ in (Box::new([1, 2, 3]) as Box<[_]>).into_iter() {} diff --git a/tests/ui/iterators/into-iter-on-boxed-slices-lint.stderr b/tests/ui/iterators/into-iter-on-boxed-slices-lint.stderr index 8a88c7816d25a..6762ed28d368a 100644 --- a/tests/ui/iterators/into-iter-on-boxed-slices-lint.stderr +++ b/tests/ui/iterators/into-iter-on-boxed-slices-lint.stderr @@ -9,12 +9,14 @@ LL | boxed.into_iter(); = note: `#[warn(boxed_slice_into_iter)]` on by default help: use `.iter()` instead of `.into_iter()` to avoid ambiguity | -LL | boxed.iter(); - | ~~~~ +LL - boxed.into_iter(); +LL + boxed.iter(); + | help: or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value | -LL | IntoIterator::into_iter(boxed); - | ++++++++++++++++++++++++ ~ +LL - boxed.into_iter(); +LL + IntoIterator::into_iter(boxed); + | warning: this method call resolves to `<&Box<[T]> as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to ` as IntoIterator>::into_iter` in Rust 2024 --> $DIR/into-iter-on-boxed-slices-lint.rs:13:29 diff --git a/tests/ui/iterators/iter-range.rs b/tests/ui/iterators/iter-range.rs index 9594729c06cf0..ec99331753989 100644 --- a/tests/ui/iterators/iter-range.rs +++ b/tests/ui/iterators/iter-range.rs @@ -2,7 +2,7 @@ fn range_(a: isize, b: isize, mut it: F) where F: FnMut(isize) { - assert!((a < b)); + assert!(a < b); let mut i: isize = a; while i < b { it(i); i += 1; } } diff --git a/tests/ui/json/json-multiple.polonius.stderr b/tests/ui/json/json-multiple.polonius.stderr deleted file mode 100644 index 0e4d442f299c3..0000000000000 --- a/tests/ui/json/json-multiple.polonius.stderr +++ /dev/null @@ -1 +0,0 @@ -{"artifact":"$TEST_BUILD_DIR/json-multiple.polonius/libjson_multiple.rlib","emit":"link"} diff --git a/tests/ui/json/json-multiple.rs b/tests/ui/json/json-multiple.rs index 296a60d24537b..8ad57939d5d16 100644 --- a/tests/ui/json/json-multiple.rs +++ b/tests/ui/json/json-multiple.rs @@ -1,5 +1,6 @@ //@ build-pass //@ ignore-pass (different metadata emitted in different modes) //@ compile-flags: --json=diagnostic-short --json artifacts --error-format=json +//@ normalize-stderr: "json-multiple\..+/libjson_multiple.rlib" -> "json-multiple/libjson_multiple.rlib" #![crate_type = "lib"] diff --git a/tests/ui/json/json-options.polonius.stderr b/tests/ui/json/json-options.polonius.stderr deleted file mode 100644 index e21f6f85d162d..0000000000000 --- a/tests/ui/json/json-options.polonius.stderr +++ /dev/null @@ -1 +0,0 @@ -{"artifact":"$TEST_BUILD_DIR/json-options.polonius/libjson_options.rlib","emit":"link"} diff --git a/tests/ui/json/json-options.rs b/tests/ui/json/json-options.rs index 33df25e27b6a3..a6654bfcac6fa 100644 --- a/tests/ui/json/json-options.rs +++ b/tests/ui/json/json-options.rs @@ -1,5 +1,6 @@ //@ build-pass //@ ignore-pass (different metadata emitted in different modes) //@ compile-flags: --json=diagnostic-short,artifacts --error-format=json +//@ normalize-stderr: "json-options\..+/libjson_options.rlib" -> "json-options/libjson_options.rlib" #![crate_type = "lib"] diff --git a/tests/ui/keyword/extern/keyword-extern-as-identifier-use.stderr b/tests/ui/keyword/extern/keyword-extern-as-identifier-use.stderr index f23f855c9e8e8..dab68258a4720 100644 --- a/tests/ui/keyword/extern/keyword-extern-as-identifier-use.stderr +++ b/tests/ui/keyword/extern/keyword-extern-as-identifier-use.stderr @@ -13,9 +13,9 @@ error[E0432]: unresolved import `r#extern` --> $DIR/keyword-extern-as-identifier-use.rs:1:5 | LL | use extern::foo; - | ^^^^^^ you might be missing crate `r#extern` + | ^^^^^^ use of unresolved module or unlinked crate `r#extern` | -help: consider importing the `r#extern` crate +help: you might be missing a crate named `r#extern`, add it to your project and import it in your code | LL + extern crate r#extern; | diff --git a/tests/ui/kindck/kindck-copy.rs b/tests/ui/kindck/kindck-copy.rs index 6df98c230ea7c..36bf0d2b785f6 100644 --- a/tests/ui/kindck/kindck-copy.rs +++ b/tests/ui/kindck/kindck-copy.rs @@ -45,7 +45,7 @@ fn test<'a,T,U:Copy>(_: &'a isize) { // mutable object types are not ok assert_copy::<&'a mut (dyn Dummy + Send)>(); //~ ERROR : Copy` is not satisfied - // unsafe ptrs are ok + // raw ptrs are ok assert_copy::<*const isize>(); assert_copy::<*const &'a mut isize>(); diff --git a/tests/ui/kindck/kindck-impl-type-params.stderr b/tests/ui/kindck/kindck-impl-type-params.stderr index a0a4ef09216f6..d375aa9cb5961 100644 --- a/tests/ui/kindck/kindck-impl-type-params.stderr +++ b/tests/ui/kindck/kindck-impl-type-params.stderr @@ -112,13 +112,13 @@ LL | struct Foo; // does not impl Copy | error: lifetime may not live long enough - --> $DIR/kindck-impl-type-params.rs:30:19 + --> $DIR/kindck-impl-type-params.rs:30:13 | LL | fn foo<'a>() { | -- lifetime `'a` defined here LL | let t: S<&'a isize> = S(marker::PhantomData); LL | let a = &t as &dyn Gettable<&'a isize>; - | ^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'a` must outlive `'static` error: aborting due to 7 previous errors diff --git a/tests/ui/kindck/kindck-inherited-copy-bound.curr.stderr b/tests/ui/kindck/kindck-inherited-copy-bound.curr.stderr index c392879db3ebb..95048c4454b31 100644 --- a/tests/ui/kindck/kindck-inherited-copy-bound.curr.stderr +++ b/tests/ui/kindck/kindck-inherited-copy-bound.curr.stderr @@ -19,33 +19,35 @@ note: required by a bound in `take_param` LL | fn take_param(foo: &T) { } | ^^^ required by this bound in `take_param` -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/kindck-inherited-copy-bound.rs:28:19 | LL | let z = &x as &dyn Foo; - | ^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/kindck-inherited-copy-bound.rs:10:13 | LL | trait Foo : Copy { | --- ^^^^ ...because it requires `Self: Sized` | | - | this trait cannot be made into an object... + | this trait is not dyn compatible... -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/kindck-inherited-copy-bound.rs:28:13 | LL | let z = &x as &dyn Foo; - | ^^ `Foo` cannot be made into an object + | ^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/kindck-inherited-copy-bound.rs:10:13 | LL | trait Foo : Copy { | --- ^^^^ ...because it requires `Self: Sized` | | - | this trait cannot be made into an object... + | this trait is not dyn compatible... = note: required for the cast from `&Box<{integer}>` to `&dyn Foo` error: aborting due to 3 previous errors diff --git a/tests/ui/kindck/kindck-inherited-copy-bound.dyn_compatible_for_dispatch.stderr b/tests/ui/kindck/kindck-inherited-copy-bound.dyn_compatible_for_dispatch.stderr index 34dcad13af30b..296f011193e9a 100644 --- a/tests/ui/kindck/kindck-inherited-copy-bound.dyn_compatible_for_dispatch.stderr +++ b/tests/ui/kindck/kindck-inherited-copy-bound.dyn_compatible_for_dispatch.stderr @@ -19,19 +19,20 @@ note: required by a bound in `take_param` LL | fn take_param(foo: &T) { } | ^^^ required by this bound in `take_param` -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/kindck-inherited-copy-bound.rs:28:13 | LL | let z = &x as &dyn Foo; - | ^^ `Foo` cannot be made into an object + | ^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/kindck-inherited-copy-bound.rs:10:13 | LL | trait Foo : Copy { | --- ^^^^ ...because it requires `Self: Sized` | | - | this trait cannot be made into an object... + | this trait is not dyn compatible... = note: required for the cast from `&Box` to `&dyn Foo` error: aborting due to 2 previous errors diff --git a/tests/ui/label/label_misspelled.stderr b/tests/ui/label/label_misspelled.stderr index 4b5b9e92ca09a..3f4020e7be0ac 100644 --- a/tests/ui/label/label_misspelled.stderr +++ b/tests/ui/label/label_misspelled.stderr @@ -157,12 +157,14 @@ LL | break foo; | help: use `break` on its own without a value inside this `while` loop | -LL | break; - | ~~~~~ +LL - break foo; +LL + break; + | help: alternatively, you might have meant to use the available loop label | -LL | break 'while_loop; - | ~~~~~~~~~~~ +LL - break foo; +LL + break 'while_loop; + | error[E0571]: `break` with value from a `while` loop --> $DIR/label_misspelled.rs:54:9 @@ -175,12 +177,14 @@ LL | break foo; | help: use `break` on its own without a value inside this `while` loop | -LL | break; - | ~~~~~ +LL - break foo; +LL + break; + | help: alternatively, you might have meant to use the available loop label | -LL | break 'while_let; - | ~~~~~~~~~~ +LL - break foo; +LL + break 'while_let; + | error[E0571]: `break` with value from a `for` loop --> $DIR/label_misspelled.rs:59:9 @@ -193,12 +197,14 @@ LL | break foo; | help: use `break` on its own without a value inside this `for` loop | -LL | break; - | ~~~~~ +LL - break foo; +LL + break; + | help: alternatively, you might have meant to use the available loop label | -LL | break 'for_loop; - | ~~~~~~~~~ +LL - break foo; +LL + break 'for_loop; + | error: aborting due to 11 previous errors; 10 warnings emitted diff --git a/tests/ui/lang-items/issue-19660.rs b/tests/ui/lang-items/issue-19660.rs deleted file mode 100644 index aff57df7ece98..0000000000000 --- a/tests/ui/lang-items/issue-19660.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ error-pattern: requires `copy` lang_item - -#![feature(lang_items, start, no_core)] -#![no_core] - -#[lang = "sized"] -trait Sized { } - -struct S; - -#[start] -fn main(_: isize, _: *const *const u8) -> isize { - let _ = S; - 0 -} diff --git a/tests/ui/lang-items/issue-19660.stderr b/tests/ui/lang-items/issue-19660.stderr deleted file mode 100644 index e5a8a143d0301..0000000000000 --- a/tests/ui/lang-items/issue-19660.stderr +++ /dev/null @@ -1,4 +0,0 @@ -error: requires `copy` lang_item - -error: aborting due to 1 previous error - diff --git a/tests/ui/lang-items/lang-item-missing.rs b/tests/ui/lang-items/lang-item-missing.rs index 8762594202a1c..5b832a5bb8f01 100644 --- a/tests/ui/lang-items/lang-item-missing.rs +++ b/tests/ui/lang-items/lang-item-missing.rs @@ -3,10 +3,11 @@ //@ error-pattern: requires `sized` lang_item -#![feature(start, no_core)] +#![feature(lang_items, no_core)] #![no_core] +#![no_main] -#[start] -fn start(argc: isize, argv: *const *const u8) -> isize { - 0 +#[no_mangle] +extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { + loop {} } diff --git a/tests/ui/lang-items/missing-copy-lang-item-issue-19660.rs b/tests/ui/lang-items/missing-copy-lang-item-issue-19660.rs new file mode 100644 index 0000000000000..9b634ee8ee3e4 --- /dev/null +++ b/tests/ui/lang-items/missing-copy-lang-item-issue-19660.rs @@ -0,0 +1,15 @@ +//@ error-pattern: requires `copy` lang_item + +#![feature(lang_items, no_core)] +#![no_core] +#![no_main] + +#[lang = "sized"] +trait Sized { } + +struct S; + +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { + argc +} diff --git a/tests/ui/lang-items/missing-copy-lang-item-issue-19660.stderr b/tests/ui/lang-items/missing-copy-lang-item-issue-19660.stderr new file mode 100644 index 0000000000000..3dc7716ecd2a6 --- /dev/null +++ b/tests/ui/lang-items/missing-copy-lang-item-issue-19660.stderr @@ -0,0 +1,8 @@ +error: requires `copy` lang_item + --> $DIR/missing-copy-lang-item-issue-19660.rs:14:5 + | +LL | argc + | ^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lang-items/start_lang_item_with_target_feature.rs b/tests/ui/lang-items/start_lang_item_with_target_feature.rs index eb712ba409259..18cd4c9704056 100644 --- a/tests/ui/lang-items/start_lang_item_with_target_feature.rs +++ b/tests/ui/lang-items/start_lang_item_with_target_feature.rs @@ -1,7 +1,7 @@ //@ only-x86_64 //@ check-fail -#![feature(lang_items, no_core, target_feature_11)] +#![feature(lang_items, no_core)] #![no_core] #[lang = "copy"] diff --git a/tests/ui/layout/base-layout-is-sized-ice-123078.stderr b/tests/ui/layout/base-layout-is-sized-ice-123078.stderr index 455bd2cbf8b6e..d8743d4e6d63b 100644 --- a/tests/ui/layout/base-layout-is-sized-ice-123078.stderr +++ b/tests/ui/layout/base-layout-is-sized-ice-123078.stderr @@ -23,7 +23,7 @@ LL | const C: S = unsafe { std::mem::transmute(()) }; | ^^^^^^^^^^^^^^^^^^^ | = note: source type: `()` (0 bits) - = note: target type: `S` (size can vary because of [u8]) + = note: target type: `S` (the type `S` has an unknown layout) error: aborting due to 2 previous errors diff --git a/tests/ui/layout/debug.rs b/tests/ui/layout/debug.rs index 5602c4e711f61..81dc72852543d 100644 --- a/tests/ui/layout/debug.rs +++ b/tests/ui/layout/debug.rs @@ -1,4 +1,5 @@ //@ normalize-stderr: "pref: Align\([1-8] bytes\)" -> "pref: $$SOME_ALIGN" +//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" #![feature(never_type, rustc_attrs, type_alias_impl_trait, repr_simd)] #![crate_type = "lib"] diff --git a/tests/ui/layout/debug.stderr b/tests/ui/layout/debug.stderr index bd31665dac1f7..319c0de26a90e 100644 --- a/tests/ui/layout/debug.stderr +++ b/tests/ui/layout/debug.stderr @@ -1,5 +1,5 @@ error: unions cannot have zero fields - --> $DIR/debug.rs:82:1 + --> $DIR/debug.rs:83:1 | LL | union EmptyUnion {} | ^^^^^^^^^^^^^^^^^^^ @@ -61,6 +61,7 @@ error: layout_of(E) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(12 bytes), @@ -87,13 +88,15 @@ error: layout_of(E) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:7:1 + --> $DIR/debug.rs:8:1 | LL | enum E { Foo, Bar(!, i32, i32) } | ^^^^^^ @@ -138,8 +141,9 @@ error: layout_of(S) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:10:1 + --> $DIR/debug.rs:11:1 | LL | struct S { f1: i32, f2: (), f3: i32 } | ^^^^^^^^ @@ -162,8 +166,9 @@ error: layout_of(U) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:13:1 + --> $DIR/debug.rs:14:1 | LL | union U { f1: (i32, i32), f3: i32 } | ^^^^^^^ @@ -255,6 +260,7 @@ error: layout_of(Result) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, Layout { size: Size(8 bytes), @@ -292,13 +298,15 @@ error: layout_of(Result) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:16:1 + --> $DIR/debug.rs:17:1 | LL | type Test = Result; | ^^^^^^^^^ @@ -325,8 +333,9 @@ error: layout_of(i32) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:19:1 + --> $DIR/debug.rs:20:1 | LL | type T = impl std::fmt::Debug; | ^^^^^^ @@ -349,8 +358,9 @@ error: layout_of(V) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(2 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:25:1 + --> $DIR/debug.rs:26:1 | LL | pub union V { | ^^^^^^^^^^^ @@ -373,8 +383,9 @@ error: layout_of(W) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(2 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:31:1 + --> $DIR/debug.rs:32:1 | LL | pub union W { | ^^^^^^^^^^^ @@ -397,8 +408,9 @@ error: layout_of(Y) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(2 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:37:1 + --> $DIR/debug.rs:38:1 | LL | pub union Y { | ^^^^^^^^^^^ @@ -421,8 +433,9 @@ error: layout_of(P1) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:44:1 + --> $DIR/debug.rs:45:1 | LL | union P1 { x: u32 } | ^^^^^^^^ @@ -445,8 +458,9 @@ error: layout_of(P2) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:48:1 + --> $DIR/debug.rs:49:1 | LL | union P2 { x: (u32, u32) } | ^^^^^^^^ @@ -469,8 +483,9 @@ error: layout_of(P3) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:56:1 + --> $DIR/debug.rs:57:1 | LL | union P3 { x: F32x4 } | ^^^^^^^^ @@ -493,8 +508,9 @@ error: layout_of(P4) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:60:1 + --> $DIR/debug.rs:61:1 | LL | union P4 { x: E } | ^^^^^^^^ @@ -522,8 +538,9 @@ error: layout_of(P5) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:64:1 + --> $DIR/debug.rs:65:1 | LL | union P5 { zst: [u16; 0], byte: u8 } | ^^^^^^^^ @@ -551,20 +568,21 @@ error: layout_of(MaybeUninit) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/debug.rs:67:1 + --> $DIR/debug.rs:68:1 | LL | type X = std::mem::MaybeUninit; | ^^^^^^ error: `#[rustc_layout]` can only be applied to `struct`/`enum`/`union` declarations and type aliases - --> $DIR/debug.rs:70:1 + --> $DIR/debug.rs:71:1 | LL | const C: () = (); | ^^^^^^^^^^^ error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/debug.rs:78:19 + --> $DIR/debug.rs:79:19 | LL | type Impossible = (str, str); | ^^^^^^^^^^ doesn't have a size known at compile-time @@ -572,14 +590,14 @@ LL | type Impossible = (str, str); = help: the trait `Sized` is not implemented for `str` = note: only the last element of a tuple may have a dynamically sized type -error: the type `EmptyUnion` has an unknown layout - --> $DIR/debug.rs:82:1 +error: the type has an unknown layout + --> $DIR/debug.rs:83:1 | LL | union EmptyUnion {} | ^^^^^^^^^^^^^^^^ error: `#[rustc_layout]` can only be applied to `struct`/`enum`/`union` declarations and type aliases - --> $DIR/debug.rs:74:5 + --> $DIR/debug.rs:75:5 | LL | const C: () = (); | ^^^^^^^^^^^ diff --git a/tests/ui/layout/hexagon-enum.rs b/tests/ui/layout/hexagon-enum.rs index e3a5c53671d40..5fa12e479e7db 100644 --- a/tests/ui/layout/hexagon-enum.rs +++ b/tests/ui/layout/hexagon-enum.rs @@ -1,4 +1,5 @@ //@ compile-flags: --target hexagon-unknown-linux-musl +//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" //@ needs-llvm-components: hexagon // // Verify that the hexagon targets implement the repr(C) for enums correctly. diff --git a/tests/ui/layout/hexagon-enum.stderr b/tests/ui/layout/hexagon-enum.stderr index 59fe667923f11..96f0a8c874086 100644 --- a/tests/ui/layout/hexagon-enum.stderr +++ b/tests/ui/layout/hexagon-enum.stderr @@ -61,13 +61,15 @@ error: layout_of(A) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/hexagon-enum.rs:16:1 + --> $DIR/hexagon-enum.rs:17:1 | LL | enum A { Apple } | ^^^^^^ @@ -135,13 +137,15 @@ error: layout_of(B) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/hexagon-enum.rs:20:1 + --> $DIR/hexagon-enum.rs:21:1 | LL | enum B { Banana = 255, } | ^^^^^^ @@ -209,13 +213,15 @@ error: layout_of(C) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(2 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(2 bytes), + randomization_seed: $SEED, } - --> $DIR/hexagon-enum.rs:24:1 + --> $DIR/hexagon-enum.rs:25:1 | LL | enum C { Chaenomeles = 256, } | ^^^^^^ @@ -283,13 +289,15 @@ error: layout_of(P) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/hexagon-enum.rs:28:1 + --> $DIR/hexagon-enum.rs:29:1 | LL | enum P { Peach = 0x1000_0000isize, } | ^^^^^^ @@ -357,13 +365,15 @@ error: layout_of(T) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/hexagon-enum.rs:34:1 + --> $DIR/hexagon-enum.rs:35:1 | LL | enum T { Tangerine = TANGERINE as isize } | ^^^^^^ diff --git a/tests/ui/layout/invalid-unsized-const-eval.rs b/tests/ui/layout/invalid-unsized-const-eval.rs index 2dec0b0faacf2..1f664d30055d6 100644 --- a/tests/ui/layout/invalid-unsized-const-eval.rs +++ b/tests/ui/layout/invalid-unsized-const-eval.rs @@ -10,5 +10,6 @@ struct LazyLock { } static EMPTY_SET: LazyLock = todo!(); +//~^ ERROR could not evaluate static initializer fn main() {} diff --git a/tests/ui/layout/invalid-unsized-const-eval.stderr b/tests/ui/layout/invalid-unsized-const-eval.stderr index bf65782b7a805..a434ca9b2c7cf 100644 --- a/tests/ui/layout/invalid-unsized-const-eval.stderr +++ b/tests/ui/layout/invalid-unsized-const-eval.stderr @@ -7,6 +7,13 @@ LL | data: (dyn Sync, ()), = help: the trait `Sized` is not implemented for `(dyn Sync + 'static)` = note: only the last element of a tuple may have a dynamically sized type -error: aborting due to 1 previous error +error[E0080]: could not evaluate static initializer + --> $DIR/invalid-unsized-const-eval.rs:12:1 + | +LL | static EMPTY_SET: LazyLock = todo!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the type `(dyn Sync, ())` has an unknown layout + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0080, E0277. +For more information about an error, try `rustc --explain E0080`. diff --git a/tests/ui/layout/invalid-unsized-in-always-sized-tail.stderr b/tests/ui/layout/invalid-unsized-in-always-sized-tail.stderr index 3f565d6ee5ba8..f54e97e2a5c76 100644 --- a/tests/ui/layout/invalid-unsized-in-always-sized-tail.stderr +++ b/tests/ui/layout/invalid-unsized-in-always-sized-tail.stderr @@ -18,6 +18,20 @@ LL | struct MySlice(T); | | | this could be changed to `T: ?Sized`... -error: aborting due to 1 previous error +error[E0080]: could not evaluate static initializer + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL + | + = note: the type `MySlice<[bool]>` has an unknown layout + | +note: inside `align_of::` + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL +note: inside `CHECK` + --> $DIR/invalid-unsized-in-always-sized-tail.rs:15:28 + | +LL | static CHECK: () = assert!(align_of::() == 1); + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0080, E0277. +For more information about an error, try `rustc --explain E0080`. diff --git a/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.rs b/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.rs index 328d204aa3cda..ab7e0897ce32c 100644 --- a/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.rs +++ b/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.rs @@ -1,4 +1,5 @@ //@ normalize-stderr: "pref: Align\([1-8] bytes\)" -> "pref: $$PREF_ALIGN" +//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" #![crate_type = "lib"] #![feature(rustc_attrs)] diff --git a/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr b/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr index ca041fb539b93..cd9e4c0278150 100644 --- a/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr +++ b/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr @@ -83,6 +83,7 @@ error: layout_of(MissingPayloadField) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(1 bytes), @@ -103,13 +104,15 @@ error: layout_of(MissingPayloadField) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:16:1 + --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:17:1 | LL | pub enum MissingPayloadField { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -201,6 +204,7 @@ error: layout_of(CommonPayloadField) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(2 bytes), @@ -238,13 +242,15 @@ error: layout_of(CommonPayloadField) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:25:1 + --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:26:1 | LL | pub enum CommonPayloadField { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -334,6 +340,7 @@ error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(2 bytes), @@ -370,13 +377,15 @@ error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:33:1 + --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:34:1 | LL | pub enum CommonPayloadFieldIsMaybeUninit { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -482,6 +491,7 @@ error: layout_of(NicheFirst) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(0 bytes), @@ -502,6 +512,7 @@ error: layout_of(NicheFirst) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(0 bytes), @@ -522,13 +533,15 @@ error: layout_of(NicheFirst) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:41:1 + --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:42:1 | LL | pub enum NicheFirst { | ^^^^^^^^^^^^^^^^^^^ @@ -634,6 +647,7 @@ error: layout_of(NicheSecond) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(0 bytes), @@ -654,6 +668,7 @@ error: layout_of(NicheSecond) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(0 bytes), @@ -674,13 +689,15 @@ error: layout_of(NicheSecond) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:50:1 + --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:51:1 | LL | pub enum NicheSecond { | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/layout/issue-96185-overaligned-enum.rs b/tests/ui/layout/issue-96185-overaligned-enum.rs index 341233a7890eb..19da169105cec 100644 --- a/tests/ui/layout/issue-96185-overaligned-enum.rs +++ b/tests/ui/layout/issue-96185-overaligned-enum.rs @@ -1,4 +1,5 @@ //@ normalize-stderr: "pref: Align\([1-8] bytes\)" -> "pref: $$PREF_ALIGN" +//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" #![crate_type = "lib"] #![feature(rustc_attrs)] diff --git a/tests/ui/layout/issue-96185-overaligned-enum.stderr b/tests/ui/layout/issue-96185-overaligned-enum.stderr index bc40a2aa482ed..15a3f6004f50f 100644 --- a/tests/ui/layout/issue-96185-overaligned-enum.stderr +++ b/tests/ui/layout/issue-96185-overaligned-enum.stderr @@ -57,6 +57,7 @@ error: layout_of(Aligned1) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(8 bytes), @@ -79,6 +80,7 @@ error: layout_of(Aligned1) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, @@ -86,8 +88,9 @@ error: layout_of(Aligned1) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/issue-96185-overaligned-enum.rs:8:1 + --> $DIR/issue-96185-overaligned-enum.rs:9:1 | LL | pub enum Aligned1 { | ^^^^^^^^^^^^^^^^^ @@ -157,6 +160,7 @@ error: layout_of(Aligned2) = Layout { Align(1 bytes), ), unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(1 bytes), @@ -179,6 +183,7 @@ error: layout_of(Aligned2) = Layout { Align(1 bytes), ), unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, @@ -186,8 +191,9 @@ error: layout_of(Aligned2) = Layout { Align(1 bytes), ), unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/issue-96185-overaligned-enum.rs:16:1 + --> $DIR/issue-96185-overaligned-enum.rs:17:1 | LL | pub enum Aligned2 { | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/layout/issue-unsized-tail-restatic-ice-122488.rs b/tests/ui/layout/issue-unsized-tail-restatic-ice-122488.rs index 96c993035ef18..f84c10d8e5c03 100644 --- a/tests/ui/layout/issue-unsized-tail-restatic-ice-122488.rs +++ b/tests/ui/layout/issue-unsized-tail-restatic-ice-122488.rs @@ -6,5 +6,6 @@ struct ArenaSet::Target>(V, U); //~^ ERROR the size for values of type `V` cannot be known at compilation time const DATA: *const ArenaSet> = std::ptr::null_mut(); +//~^ ERROR evaluation of constant value failed pub fn main() {} diff --git a/tests/ui/layout/issue-unsized-tail-restatic-ice-122488.stderr b/tests/ui/layout/issue-unsized-tail-restatic-ice-122488.stderr index f39cb29868af5..220951fab86f8 100644 --- a/tests/ui/layout/issue-unsized-tail-restatic-ice-122488.stderr +++ b/tests/ui/layout/issue-unsized-tail-restatic-ice-122488.stderr @@ -22,6 +22,13 @@ help: the `Box` type always has a statically known size and allocates its conten LL | struct ArenaSet::Target>(Box, U); | ++++ + -error: aborting due to 1 previous error +error[E0080]: evaluation of constant value failed + --> $DIR/issue-unsized-tail-restatic-ice-122488.rs:8:1 + | +LL | const DATA: *const ArenaSet> = std::ptr::null_mut(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the type `ArenaSet, [u8]>` has an unknown layout + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0080, E0277. +For more information about an error, try `rustc --explain E0080`. diff --git a/tests/ui/layout/malformed-unsized-type-in-union.rs b/tests/ui/layout/malformed-unsized-type-in-union.rs index 5d8ec576cf01b..e97024ce9d704 100644 --- a/tests/ui/layout/malformed-unsized-type-in-union.rs +++ b/tests/ui/layout/malformed-unsized-type-in-union.rs @@ -2,6 +2,7 @@ union W { s: dyn Iterator } //~^ ERROR cannot find type `Missing` in this scope +//~| ERROR field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union static ONCE: W = todo!(); diff --git a/tests/ui/layout/malformed-unsized-type-in-union.stderr b/tests/ui/layout/malformed-unsized-type-in-union.stderr index ad4f0cda19e5c..bdfabc0b15190 100644 --- a/tests/ui/layout/malformed-unsized-type-in-union.stderr +++ b/tests/ui/layout/malformed-unsized-type-in-union.stderr @@ -4,6 +4,19 @@ error[E0412]: cannot find type `Missing` in this scope LL | union W { s: dyn Iterator } | ^^^^^^^ not found in this scope -error: aborting due to 1 previous error +error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union + --> $DIR/malformed-unsized-type-in-union.rs:3:11 + | +LL | union W { s: dyn Iterator } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: union fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>` +help: wrap the field type in `ManuallyDrop<...>` + | +LL | union W { s: std::mem::ManuallyDrop> } + | +++++++++++++++++++++++ + + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0412`. +Some errors have detailed explanations: E0412, E0740. +For more information about an error, try `rustc --explain E0412`. diff --git a/tests/ui/layout/randomize.rs b/tests/ui/layout/randomize.rs new file mode 100644 index 0000000000000..27e99327a3196 --- /dev/null +++ b/tests/ui/layout/randomize.rs @@ -0,0 +1,62 @@ +//@ run-pass +//@ revisions: normal randomize-layout +//@ [randomize-layout]compile-flags: -Zrandomize-layout -Zlayout-seed=2 + +#![feature(offset_of_enum)] + +use std::ptr; + + +// these types only have their field offsets taken, they're never constructed +#[allow(dead_code)] +pub struct Foo(u32, T, u8); +#[allow(dead_code)] +pub struct Wrapper(T); +#[repr(transparent)] +#[allow(dead_code)] +pub struct TransparentWrapper(u16); + +const _: () = { + // Behavior of the current non-randomized implementation, not guaranteed + #[cfg(not(randomize_layout))] + assert!(std::mem::offset_of!(Foo::, 1) == std::mem::offset_of!(Foo::>, 1)); + + // under randomization Foo != Foo + #[cfg(randomize_layout)] + assert!(std::mem::offset_of!(Foo::, 1) != std::mem::offset_of!(Foo::>, 1)); + + // Even transparent wrapper inner types get a different layout since associated type + // specialization could result in the outer type behaving differently depending on the exact + // inner type. + #[cfg(randomize_layout)] + assert!( + std::mem::offset_of!(Foo::, 1) != std::mem::offset_of!(Foo::, 1) + ); + + // Currently all fn pointers are treated interchangably even with randomization. Not guaranteed. + // Associated type specialization could also break this. + assert!( + std::mem::offset_of!(Foo::, 1) == std::mem::offset_of!(Foo:: usize>, 1) + ); + + // But subtype coercions must always result in the same layout. + assert!( + std::mem::offset_of!(Foo::, 1) == std::mem::offset_of!(Foo::, 1) + ); + + // Randomization must uphold NPO guarantees + assert!(std::mem::offset_of!(Option::<&usize>, Some.0) == 0); + assert!(std::mem::offset_of!(Result::<&usize, ()>, Ok.0) == 0); +}; + +#[allow(dead_code)] +struct Unsizable(usize, T); + +fn main() { + // offset_of doesn't let us probe the unsized field, check at runtime. + let x = &Unsizable::<[u32; 4]>(0, [0; 4]); + let y: &Unsizable::<[u32]> = x; + + // type coercion must not change the layout. + assert_eq!(ptr::from_ref(&x.1).addr(), ptr::from_ref(&y.1).addr()); +} diff --git a/tests/ui/layout/reprc-power-alignment.rs b/tests/ui/layout/reprc-power-alignment.rs new file mode 100644 index 0000000000000..f6c1df55988ba --- /dev/null +++ b/tests/ui/layout/reprc-power-alignment.rs @@ -0,0 +1,152 @@ +//@ check-pass +//@ compile-flags: --target powerpc64-ibm-aix +//@ needs-llvm-components: powerpc +//@ add-core-stubs +#![feature(no_core)] +#![no_core] +#![no_std] + +extern crate minicore; +use minicore::*; + +#[warn(uses_power_alignment)] + +#[repr(C)] +pub struct Floats { + a: f64, + b: u8, + c: f64, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + d: f32, +} + +pub struct Floats2 { + a: f64, + b: u32, + c: f64, +} + +#[repr(C)] +pub struct Floats3 { + a: f32, + b: f32, + c: i64, +} + +#[repr(C)] +pub struct Floats4 { + a: u64, + b: u32, + c: f32, +} + +#[repr(C)] +pub struct Floats5 { + a: f32, + b: f64, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + c: f32, +} + +#[repr(C)] +pub struct FloatAgg1 { + x: Floats, + y: f64, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type +} + +#[repr(C)] +pub struct FloatAgg2 { + x: i64, + y: Floats, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type +} + +#[repr(C)] +pub struct FloatAgg3 { + x: FloatAgg1, + // NOTE: the "power" alignment rule is infectious to nested struct fields. + y: FloatAgg2, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + z: FloatAgg2, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type +} + +#[repr(C)] +pub struct FloatAgg4 { + x: FloatAgg1, + y: FloatAgg2, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type +} + +#[repr(C)] +pub struct FloatAgg5 { + x: FloatAgg1, + y: FloatAgg2, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + z: FloatAgg3, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type +} + +#[repr(C)] +pub struct FloatAgg6 { + x: i64, + y: Floats, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + z: u8, +} + +#[repr(C)] +pub struct FloatAgg7 { + x: i64, + y: Floats, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + z: u8, + zz: f32, +} + +#[repr(C)] +pub struct A { + d: f64, +} +#[repr(C)] +pub struct B { + a: A, + f: f32, + d: f64, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type +} +#[repr(C)] +pub struct C { + c: u8, + b: B, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type +} +#[repr(C)] +pub struct D { + x: f64, +} +#[repr(C)] +pub struct E { + x: i32, + d: D, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type +} +#[repr(C)] +pub struct F { + a: u8, + b: f64, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type +} +#[repr(C)] +pub struct G { + a: u8, + b: u8, + c: f64, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + d: f32, + e: f64, //~ WARNING repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type +} +// Should not warn on #[repr(packed)]. +#[repr(packed)] +pub struct H { + a: u8, + b: u8, + c: f64, + d: f32, + e: f64, +} +#[repr(C, packed)] +pub struct I { + a: u8, + b: u8, + c: f64, + d: f32, + e: f64, +} + +fn main() { } diff --git a/tests/ui/layout/reprc-power-alignment.stderr b/tests/ui/layout/reprc-power-alignment.stderr new file mode 100644 index 0000000000000..18664e4d655d3 --- /dev/null +++ b/tests/ui/layout/reprc-power-alignment.stderr @@ -0,0 +1,112 @@ +warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + --> $DIR/reprc-power-alignment.rs:18:5 + | +LL | c: f64, + | ^^^^^^ + | +note: the lint level is defined here + --> $DIR/reprc-power-alignment.rs:12:8 + | +LL | #[warn(uses_power_alignment)] + | ^^^^^^^^^^^^^^^^^^^^ + +warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + --> $DIR/reprc-power-alignment.rs:45:5 + | +LL | b: f64, + | ^^^^^^ + | + = note: `#[warn(uses_power_alignment)]` on by default + +warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + --> $DIR/reprc-power-alignment.rs:52:5 + | +LL | y: f64, + | ^^^^^^ + +warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + --> $DIR/reprc-power-alignment.rs:58:5 + | +LL | y: Floats, + | ^^^^^^^^^ + +warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + --> $DIR/reprc-power-alignment.rs:65:5 + | +LL | y: FloatAgg2, + | ^^^^^^^^^^^^ + +warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + --> $DIR/reprc-power-alignment.rs:66:5 + | +LL | z: FloatAgg2, + | ^^^^^^^^^^^^ + +warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + --> $DIR/reprc-power-alignment.rs:72:5 + | +LL | y: FloatAgg2, + | ^^^^^^^^^^^^ + +warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + --> $DIR/reprc-power-alignment.rs:78:5 + | +LL | y: FloatAgg2, + | ^^^^^^^^^^^^ + +warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + --> $DIR/reprc-power-alignment.rs:79:5 + | +LL | z: FloatAgg3, + | ^^^^^^^^^^^^ + +warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + --> $DIR/reprc-power-alignment.rs:85:5 + | +LL | y: Floats, + | ^^^^^^^^^ + +warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + --> $DIR/reprc-power-alignment.rs:92:5 + | +LL | y: Floats, + | ^^^^^^^^^ + +warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + --> $DIR/reprc-power-alignment.rs:105:3 + | +LL | d: f64, + | ^^^^^^ + +warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + --> $DIR/reprc-power-alignment.rs:110:3 + | +LL | b: B, + | ^^^^ + +warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + --> $DIR/reprc-power-alignment.rs:119:3 + | +LL | d: D, + | ^^^^ + +warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + --> $DIR/reprc-power-alignment.rs:124:3 + | +LL | b: f64, + | ^^^^^^ + +warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + --> $DIR/reprc-power-alignment.rs:130:5 + | +LL | c: f64, + | ^^^^^^ + +warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + --> $DIR/reprc-power-alignment.rs:132:5 + | +LL | e: f64, + | ^^^^^^ + +warning: 17 warnings emitted + diff --git a/tests/ui/layout/thumb-enum.rs b/tests/ui/layout/thumb-enum.rs index 57a9a2d813709..2381d9d02926d 100644 --- a/tests/ui/layout/thumb-enum.rs +++ b/tests/ui/layout/thumb-enum.rs @@ -1,4 +1,5 @@ //@ compile-flags: --target thumbv8m.main-none-eabihf +//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" //@ needs-llvm-components: arm // // Verify that thumb targets implement the repr(C) for enums correctly. diff --git a/tests/ui/layout/thumb-enum.stderr b/tests/ui/layout/thumb-enum.stderr index bf043af586b1c..120081d193c69 100644 --- a/tests/ui/layout/thumb-enum.stderr +++ b/tests/ui/layout/thumb-enum.stderr @@ -61,13 +61,15 @@ error: layout_of(A) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/thumb-enum.rs:16:1 + --> $DIR/thumb-enum.rs:17:1 | LL | enum A { Apple } | ^^^^^^ @@ -135,13 +137,15 @@ error: layout_of(B) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/thumb-enum.rs:20:1 + --> $DIR/thumb-enum.rs:21:1 | LL | enum B { Banana = 255, } | ^^^^^^ @@ -209,13 +213,15 @@ error: layout_of(C) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(2 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(2 bytes), + randomization_seed: $SEED, } - --> $DIR/thumb-enum.rs:24:1 + --> $DIR/thumb-enum.rs:25:1 | LL | enum C { Chaenomeles = 256, } | ^^^^^^ @@ -283,13 +289,15 @@ error: layout_of(P) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/thumb-enum.rs:28:1 + --> $DIR/thumb-enum.rs:29:1 | LL | enum P { Peach = 0x1000_0000isize, } | ^^^^^^ @@ -357,13 +365,15 @@ error: layout_of(T) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/thumb-enum.rs:34:1 + --> $DIR/thumb-enum.rs:35:1 | LL | enum T { Tangerine = TANGERINE as isize } | ^^^^^^ diff --git a/tests/ui/layout/uncomputable-due-to-trivial-bounds-ice-135138.rs b/tests/ui/layout/uncomputable-due-to-trivial-bounds-ice-135138.rs new file mode 100644 index 0000000000000..91280e49dcd7e --- /dev/null +++ b/tests/ui/layout/uncomputable-due-to-trivial-bounds-ice-135138.rs @@ -0,0 +1,11 @@ +#![feature(trivial_bounds)] + +fn return_str() +where + str: Sized, +{ + [(); { let _a: Option = None; 0 }]; + //~^ ERROR evaluation of constant value failed +} + +fn main() {} diff --git a/tests/ui/layout/uncomputable-due-to-trivial-bounds-ice-135138.stderr b/tests/ui/layout/uncomputable-due-to-trivial-bounds-ice-135138.stderr new file mode 100644 index 0000000000000..6c7c51db8df7f --- /dev/null +++ b/tests/ui/layout/uncomputable-due-to-trivial-bounds-ice-135138.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/uncomputable-due-to-trivial-bounds-ice-135138.rs:7:16 + | +LL | [(); { let _a: Option = None; 0 }]; + | ^^ the type `Option` has an unknown layout + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/layout/unexpected-unsized-field-issue-135020.rs b/tests/ui/layout/unexpected-unsized-field-issue-135020.rs new file mode 100644 index 0000000000000..c81d037e510f2 --- /dev/null +++ b/tests/ui/layout/unexpected-unsized-field-issue-135020.rs @@ -0,0 +1,7 @@ +//@ check-pass + +fn problem_thingy(items: &mut impl Iterator) { + items.peekable(); +} + +fn main() {} diff --git a/tests/ui/layout/unknown-when-no-type-parameter.rs b/tests/ui/layout/unknown-when-no-type-parameter.rs new file mode 100644 index 0000000000000..94c32cf262f2a --- /dev/null +++ b/tests/ui/layout/unknown-when-no-type-parameter.rs @@ -0,0 +1,14 @@ +#![feature(trivial_bounds)] + +//@ error-pattern: error[E0080]: evaluation of constant value failed +//@ error-pattern: the type `<() as Project>::Assoc` has an unknown layout + +trait Project { + type Assoc; +} + +fn foo() where (): Project { + [(); size_of::<<() as Project>::Assoc>()]; +} + +fn main() {} diff --git a/tests/ui/layout/unknown-when-no-type-parameter.stderr b/tests/ui/layout/unknown-when-no-type-parameter.stderr new file mode 100644 index 0000000000000..d0456e2b3293b --- /dev/null +++ b/tests/ui/layout/unknown-when-no-type-parameter.stderr @@ -0,0 +1,16 @@ +error[E0080]: evaluation of constant value failed + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL + | + = note: the type `<() as Project>::Assoc` has an unknown layout + | +note: inside `std::mem::size_of::<<() as Project>::Assoc>` + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL +note: inside `foo::{constant#0}` + --> $DIR/unknown-when-no-type-parameter.rs:11:10 + | +LL | [(); size_of::<<() as Project>::Assoc>()]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/layout/unknown-when-ptr-metadata-is-DST.rs b/tests/ui/layout/unknown-when-ptr-metadata-is-DST.rs new file mode 100644 index 0000000000000..973235fe65afc --- /dev/null +++ b/tests/ui/layout/unknown-when-ptr-metadata-is-DST.rs @@ -0,0 +1,12 @@ +#![feature(ptr_metadata)] +#![feature(trivial_bounds)] + +fn return_str() +where + str: std::ptr::Pointee, +{ + [(); { let _a: Option<&str> = None; 0 }]; + //~^ ERROR evaluation of constant value failed +} + +fn main() {} diff --git a/tests/ui/layout/unknown-when-ptr-metadata-is-DST.stderr b/tests/ui/layout/unknown-when-ptr-metadata-is-DST.stderr new file mode 100644 index 0000000000000..68bba2e967826 --- /dev/null +++ b/tests/ui/layout/unknown-when-ptr-metadata-is-DST.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/unknown-when-ptr-metadata-is-DST.rs:8:16 + | +LL | [(); { let _a: Option<&str> = None; 0 }]; + | ^^ the type `str` has an unknown layout + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/layout/valid_range_oob.stderr b/tests/ui/layout/valid_range_oob.stderr index 9c360b2cd6e7e..1a0c384125038 100644 --- a/tests/ui/layout/valid_range_oob.stderr +++ b/tests/ui/layout/valid_range_oob.stderr @@ -5,4 +5,4 @@ error: the compiler unexpectedly panicked. this is a bug. query stack during panic: #0 [layout_of] computing layout of `Foo` #1 [eval_to_allocation_raw] const-evaluating + checking `FOO` -end of query stack +... and 2 other queries... use `env RUST_BACKTRACE=1` to see the full query stack diff --git a/tests/ui/layout/zero-sized-array-enum-niche.rs b/tests/ui/layout/zero-sized-array-enum-niche.rs index 152f44bd86375..d3ff016d8aa23 100644 --- a/tests/ui/layout/zero-sized-array-enum-niche.rs +++ b/tests/ui/layout/zero-sized-array-enum-niche.rs @@ -1,4 +1,5 @@ //@ normalize-stderr: "pref: Align\([1-8] bytes\)" -> "pref: $$PREF_ALIGN" +//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" #![crate_type = "lib"] #![feature(rustc_attrs)] diff --git a/tests/ui/layout/zero-sized-array-enum-niche.stderr b/tests/ui/layout/zero-sized-array-enum-niche.stderr index d61408098df7f..b6fcc14c063c5 100644 --- a/tests/ui/layout/zero-sized-array-enum-niche.stderr +++ b/tests/ui/layout/zero-sized-array-enum-niche.stderr @@ -59,6 +59,7 @@ error: layout_of(Result<[u32; 0], bool>) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, Layout { size: Size(2 bytes), @@ -92,13 +93,15 @@ error: layout_of(Result<[u32; 0], bool>) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/zero-sized-array-enum-niche.rs:13:1 + --> $DIR/zero-sized-array-enum-niche.rs:14:1 | LL | type AlignedResult = Result<[u32; 0], bool>; | ^^^^^^^^^^^^^^^^^^ @@ -164,6 +167,7 @@ error: layout_of(MultipleAlignments) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(2 bytes), + randomization_seed: $SEED, }, Layout { size: Size(4 bytes), @@ -188,6 +192,7 @@ error: layout_of(MultipleAlignments) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, Layout { size: Size(2 bytes), @@ -221,13 +226,15 @@ error: layout_of(MultipleAlignments) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/zero-sized-array-enum-niche.rs:21:1 + --> $DIR/zero-sized-array-enum-niche.rs:22:1 | LL | enum MultipleAlignments { | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -293,6 +300,7 @@ error: layout_of(Result<[u32; 0], Packed>>) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, Layout { size: Size(3 bytes), @@ -326,13 +334,15 @@ error: layout_of(Result<[u32; 0], Packed>>) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/zero-sized-array-enum-niche.rs:37:1 + --> $DIR/zero-sized-array-enum-niche.rs:38:1 | LL | type NicheLosesToTagged = Result<[u32; 0], Packed>>; | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -402,6 +412,7 @@ error: layout_of(Result<[u32; 0], Packed>) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, Layout { size: Size(2 bytes), @@ -435,13 +446,15 @@ error: layout_of(Result<[u32; 0], Packed>) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/zero-sized-array-enum-niche.rs:44:1 + --> $DIR/zero-sized-array-enum-niche.rs:45:1 | LL | type NicheWinsOverTagged = Result<[u32; 0], Packed>; | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/lazy-type-alias/def-site-param-defaults-wf.rs b/tests/ui/lazy-type-alias/def-site-param-defaults-wf.rs new file mode 100644 index 0000000000000..90bb0a294f810 --- /dev/null +++ b/tests/ui/lazy-type-alias/def-site-param-defaults-wf.rs @@ -0,0 +1,9 @@ +//! Ensure that we check generic parameter defaults for well-formedness at the definition site. +#![feature(lazy_type_alias)] +#![allow(incomplete_features)] + +type Alias, const N: usize = {0 - 1}> = T; +//~^ ERROR evaluation of constant value failed +//~| ERROR the size for values of type `str` cannot be known at compilation time + +fn main() {} diff --git a/tests/ui/lazy-type-alias/def-site-param-defaults-wf.stderr b/tests/ui/lazy-type-alias/def-site-param-defaults-wf.stderr new file mode 100644 index 0000000000000..da0021ccaf4d3 --- /dev/null +++ b/tests/ui/lazy-type-alias/def-site-param-defaults-wf.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/def-site-param-defaults-wf.rs:5:44 + | +LL | type Alias, const N: usize = {0 - 1}> = T; + | ^^^^^ attempt to compute `0_usize - 1_usize`, which would overflow + +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/def-site-param-defaults-wf.rs:5:16 + | +LL | type Alias, const N: usize = {0 - 1}> = T; + | ^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` +note: required by an implicit `Sized` bound in `Vec` + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0080, E0277. +For more information about an error, try `rustc --explain E0080`. diff --git a/tests/ui/lazy-type-alias/def-site-predicates-wf.rs b/tests/ui/lazy-type-alias/def-site-predicates-wf.rs new file mode 100644 index 0000000000000..5d9235347cb82 --- /dev/null +++ b/tests/ui/lazy-type-alias/def-site-predicates-wf.rs @@ -0,0 +1,13 @@ +//! Ensure that we check the predicates at the definition site for well-formedness. +#![feature(lazy_type_alias)] +#![allow(incomplete_features)] + +type Alias0 = () +where + Vec:; //~ ERROR the size for values of type `str` cannot be known at compilation time + +type Alias1 = () +where + Vec: Sized; //~ ERROR the size for values of type `str` cannot be known at compilation time + +fn main() {} diff --git a/tests/ui/lazy-type-alias/def-site-predicates-wf.stderr b/tests/ui/lazy-type-alias/def-site-predicates-wf.stderr new file mode 100644 index 0000000000000..b44e702c35cb4 --- /dev/null +++ b/tests/ui/lazy-type-alias/def-site-predicates-wf.stderr @@ -0,0 +1,23 @@ +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/def-site-predicates-wf.rs:7:5 + | +LL | Vec:; + | ^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` +note: required by an implicit `Sized` bound in `Vec` + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/def-site-predicates-wf.rs:11:15 + | +LL | Vec: Sized; + | ^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` +note: required by an implicit `Sized` bound in `Vec` + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/lazy-type-alias/inherent-impls-overflow.classic.stderr b/tests/ui/lazy-type-alias/inherent-impls-overflow.classic.stderr deleted file mode 100644 index 2f00a877142c0..0000000000000 --- a/tests/ui/lazy-type-alias/inherent-impls-overflow.classic.stderr +++ /dev/null @@ -1,43 +0,0 @@ -error[E0275]: overflow normalizing the type alias `Loop` - --> $DIR/inherent-impls-overflow.rs:7:13 - | -LL | type Loop = Loop; - | ^^^^ - | - = note: in case this is a recursive type alias, consider using a struct, enum, or union instead - -error[E0275]: overflow normalizing the type alias `Loop` - --> $DIR/inherent-impls-overflow.rs:9:1 - | -LL | impl Loop {} - | ^^^^^^^^^^^^ - | - = note: in case this is a recursive type alias, consider using a struct, enum, or union instead - -error[E0275]: overflow normalizing the type alias `Poly0<(((((((...,),),),),),),)>` - --> $DIR/inherent-impls-overflow.rs:13:17 - | -LL | type Poly0 = Poly1<(T,)>; - | ^^^^^^^^^^^ - | - = note: in case this is a recursive type alias, consider using a struct, enum, or union instead - -error[E0275]: overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>` - --> $DIR/inherent-impls-overflow.rs:16:17 - | -LL | type Poly1 = Poly0<(T,)>; - | ^^^^^^^^^^^ - | - = note: in case this is a recursive type alias, consider using a struct, enum, or union instead - -error[E0275]: overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>` - --> $DIR/inherent-impls-overflow.rs:20:1 - | -LL | impl Poly0<()> {} - | ^^^^^^^^^^^^^^^^^ - | - = note: in case this is a recursive type alias, consider using a struct, enum, or union instead - -error: aborting due to 5 previous errors - -For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/let-else/let-else-deref-coercion.stderr b/tests/ui/let-else/let-else-deref-coercion.stderr index da8b1f4c48e97..543737868c9ff 100644 --- a/tests/ui/let-else/let-else-deref-coercion.stderr +++ b/tests/ui/let-else/let-else-deref-coercion.stderr @@ -9,7 +9,7 @@ LL | let Bar::Present(z) = self else { help: consider dereferencing to access the inner value using the Deref trait | LL | let Bar::Present(z) = &**self else { - | ~~~~~~~ + | +++ error[E0308]: mismatched types --> $DIR/let-else-deref-coercion.rs:68:13 @@ -22,7 +22,7 @@ LL | let Bar(z) = x; help: consider dereferencing to access the inner value using the Deref trait | LL | let Bar(z) = &**x; - | ~~~~ + | +++ error: aborting due to 2 previous errors diff --git a/tests/ui/issues/issue-40408.rs b/tests/ui/lexer/floating-point-0e10-issue-40408.rs similarity index 100% rename from tests/ui/issues/issue-40408.rs rename to tests/ui/lexer/floating-point-0e10-issue-40408.rs diff --git a/tests/ui/lexer/lex-bad-char-literals-1.stderr b/tests/ui/lexer/lex-bad-char-literals-1.stderr index 9dc0a3380631e..0985e274c0284 100644 --- a/tests/ui/lexer/lex-bad-char-literals-1.stderr +++ b/tests/ui/lexer/lex-bad-char-literals-1.stderr @@ -19,8 +19,9 @@ LL | '\●' = help: for more information, visit help: if you meant to write a literal backslash (perhaps escaping in a regular expression), consider a raw string literal | -LL | r"\●" - | ~~~~~ +LL - '\●' +LL + r"\●" + | error: unknown character escape: `\u{25cf}` --> $DIR/lex-bad-char-literals-1.rs:14:7 @@ -32,7 +33,7 @@ LL | "\●" help: if you meant to write a literal backslash (perhaps escaping in a regular expression), consider a raw string literal | LL | r"\●" - | ~~~~~ + | + error: aborting due to 4 previous errors diff --git a/tests/ui/lexer/lex-bad-char-literals-2.stderr b/tests/ui/lexer/lex-bad-char-literals-2.stderr index 76cde00404a15..fa0630728656a 100644 --- a/tests/ui/lexer/lex-bad-char-literals-2.stderr +++ b/tests/ui/lexer/lex-bad-char-literals-2.stderr @@ -6,8 +6,9 @@ LL | 'nope' | help: if you meant to write a string literal, use double quotes | -LL | "nope" - | ~ ~ +LL - 'nope' +LL + "nope" + | error: aborting due to 1 previous error diff --git a/tests/ui/lexer/lex-bad-char-literals-3.stderr b/tests/ui/lexer/lex-bad-char-literals-3.stderr index 3f339b2ef7d93..d8ce17d13a990 100644 --- a/tests/ui/lexer/lex-bad-char-literals-3.stderr +++ b/tests/ui/lexer/lex-bad-char-literals-3.stderr @@ -6,8 +6,9 @@ LL | static c: char = '●●'; | help: if you meant to write a string literal, use double quotes | -LL | static c: char = "●●"; - | ~ ~ +LL - static c: char = '●●'; +LL + static c: char = "●●"; + | error: character literal may only contain one codepoint --> $DIR/lex-bad-char-literals-3.rs:5:20 @@ -17,8 +18,9 @@ LL | let ch: &str = '●●'; | help: if you meant to write a string literal, use double quotes | -LL | let ch: &str = "●●"; - | ~ ~ +LL - let ch: &str = '●●'; +LL + let ch: &str = "●●"; + | error: aborting due to 2 previous errors diff --git a/tests/ui/lexer/lex-bad-char-literals-5.stderr b/tests/ui/lexer/lex-bad-char-literals-5.stderr index 8004157e87f7a..0322783871e63 100644 --- a/tests/ui/lexer/lex-bad-char-literals-5.stderr +++ b/tests/ui/lexer/lex-bad-char-literals-5.stderr @@ -6,8 +6,9 @@ LL | static c: char = '\x10\x10'; | help: if you meant to write a string literal, use double quotes | -LL | static c: char = "\x10\x10"; - | ~ ~ +LL - static c: char = '\x10\x10'; +LL + static c: char = "\x10\x10"; + | error: character literal may only contain one codepoint --> $DIR/lex-bad-char-literals-5.rs:5:20 @@ -17,8 +18,9 @@ LL | let ch: &str = '\x10\x10'; | help: if you meant to write a string literal, use double quotes | -LL | let ch: &str = "\x10\x10"; - | ~ ~ +LL - let ch: &str = '\x10\x10'; +LL + let ch: &str = "\x10\x10"; + | error: aborting due to 2 previous errors diff --git a/tests/ui/lexer/lex-bad-char-literals-6.stderr b/tests/ui/lexer/lex-bad-char-literals-6.stderr index 96d409d59bb25..e5fd62bc0a9ab 100644 --- a/tests/ui/lexer/lex-bad-char-literals-6.stderr +++ b/tests/ui/lexer/lex-bad-char-literals-6.stderr @@ -6,8 +6,9 @@ LL | let x: &str = 'ab'; | help: if you meant to write a string literal, use double quotes | -LL | let x: &str = "ab"; - | ~ ~ +LL - let x: &str = 'ab'; +LL + let x: &str = "ab"; + | error: character literal may only contain one codepoint --> $DIR/lex-bad-char-literals-6.rs:4:19 @@ -17,8 +18,9 @@ LL | let y: char = 'cd'; | help: if you meant to write a string literal, use double quotes | -LL | let y: char = "cd"; - | ~ ~ +LL - let y: char = 'cd'; +LL + let y: char = "cd"; + | error: character literal may only contain one codepoint --> $DIR/lex-bad-char-literals-6.rs:6:13 @@ -28,8 +30,9 @@ LL | let z = 'ef'; | help: if you meant to write a string literal, use double quotes | -LL | let z = "ef"; - | ~ ~ +LL - let z = 'ef'; +LL + let z = "ef"; + | error[E0308]: mismatched types --> $DIR/lex-bad-char-literals-6.rs:13:20 diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-1.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-1.stderr index 57c5f82704ec7..81ee697802b9c 100644 --- a/tests/ui/lexer/lex-bad-str-literal-as-char-1.stderr +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-1.stderr @@ -6,8 +6,9 @@ LL | println!('1 + 1'); | help: if you meant to write a string literal, use double quotes | -LL | println!("1 + 1"); - | ~ ~ +LL - println!('1 + 1'); +LL + println!("1 + 1"); + | error: lifetimes cannot start with a number --> $DIR/lex-bad-str-literal-as-char-1.rs:3:14 diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-2.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-2.stderr index f64761af64193..ed1303cd42843 100644 --- a/tests/ui/lexer/lex-bad-str-literal-as-char-2.stderr +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-2.stderr @@ -6,8 +6,9 @@ LL | println!(' 1 + 1'); | help: if you meant to write a string literal, use double quotes | -LL | println!(" 1 + 1"); - | ~ ~ +LL - println!(' 1 + 1'); +LL + println!(" 1 + 1"); + | error: aborting due to 1 previous error diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2015.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2015.stderr index 06f127426679f..2f92225de1fba 100644 --- a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2015.stderr +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2015.stderr @@ -6,8 +6,9 @@ LL | println!('hello world'); | help: if you meant to write a string literal, use double quotes | -LL | println!("hello world"); - | ~ ~ +LL - println!('hello world'); +LL + println!("hello world"); + | error: aborting due to 1 previous error diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2018.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2018.stderr index 06f127426679f..2f92225de1fba 100644 --- a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2018.stderr +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2018.stderr @@ -6,8 +6,9 @@ LL | println!('hello world'); | help: if you meant to write a string literal, use double quotes | -LL | println!("hello world"); - | ~ ~ +LL - println!('hello world'); +LL + println!("hello world"); + | error: aborting due to 1 previous error diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2021.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2021.stderr index 4170560cfcb04..e10046e58e451 100644 --- a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2021.stderr +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2021.stderr @@ -7,8 +7,9 @@ LL | println!('hello world'); = note: prefixed identifiers and literals are reserved since Rust 2021 help: if you meant to write a string literal, use double quotes | -LL | println!("hello world"); - | ~ ~ +LL - println!('hello world'); +LL + println!("hello world"); + | error[E0762]: unterminated character literal --> $DIR/lex-bad-str-literal-as-char-3.rs:5:26 @@ -18,8 +19,9 @@ LL | println!('hello world'); | help: if you meant to write a string literal, use double quotes | -LL | println!("hello world"); - | ~ ~ +LL - println!('hello world'); +LL + println!("hello world"); + | error: aborting due to 2 previous errors diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-4.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-4.stderr index af42b5b7f7b78..5633783e73828 100644 --- a/tests/ui/lexer/lex-bad-str-literal-as-char-4.stderr +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-4.stderr @@ -7,8 +7,9 @@ LL | println!('hello world'); = note: prefixed identifiers and literals are reserved since Rust 2021 help: if you meant to write a string literal, use double quotes | -LL | println!("hello world"); - | ~ ~ +LL - println!('hello world'); +LL + println!("hello world"); + | error[E0762]: unterminated character literal --> $DIR/lex-bad-str-literal-as-char-4.rs:4:30 @@ -18,8 +19,9 @@ LL | println!('hello world'); | help: if you meant to write a string literal, use double quotes | -LL | println!("hello world"); - | ~ ~ +LL - println!('hello world'); +LL + println!("hello world"); + | error: aborting due to 2 previous errors diff --git a/tests/ui/lifetimes/borrowck-let-suggestion.stderr b/tests/ui/lifetimes/borrowck-let-suggestion.stderr index cca7f52957e4c..4703d7f10dc28 100644 --- a/tests/ui/lifetimes/borrowck-let-suggestion.stderr +++ b/tests/ui/lifetimes/borrowck-let-suggestion.stderr @@ -13,7 +13,7 @@ LL | x.use_mut(); help: consider consuming the `Vec` when turning it into an `Iterator` | LL | let mut x = vec![1].into_iter(); - | ~~~~~~~~~ + | +++++ help: consider using a `let` binding to create a longer lived value | LL ~ let binding = vec![1]; diff --git a/tests/ui/lifetimes/copy_modulo_regions.stderr b/tests/ui/lifetimes/copy_modulo_regions.stderr index 310ddb21647f6..0d69f0323d621 100644 --- a/tests/ui/lifetimes/copy_modulo_regions.stderr +++ b/tests/ui/lifetimes/copy_modulo_regions.stderr @@ -4,7 +4,10 @@ error: lifetime may not live long enough LL | fn foo<'a>() -> [Foo<'a>; 100] { | -- lifetime `'a` defined here LL | [mk_foo::<'a>(); 100] - | ^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static` + | ^^^^^^^^^^^^^^^^^^^^^ + | | + | returning this value requires that `'a` must outlive `'static` + | requirement occurs due to copying this value | = note: requirement occurs because of the type `Foo<'_>`, which makes the generic argument `'_` invariant = note: the struct `Foo<'a>` is invariant over the parameter `'a` diff --git a/tests/ui/lifetimes/fullwidth-ampersand.stderr b/tests/ui/lifetimes/fullwidth-ampersand.stderr index 4645254f4b70d..7fa7343620b0b 100644 --- a/tests/ui/lifetimes/fullwidth-ampersand.stderr +++ b/tests/ui/lifetimes/fullwidth-ampersand.stderr @@ -6,8 +6,9 @@ LL | fn f(_: &&()) -> &() { todo!() } | help: Unicode character '&' (Fullwidth Ampersand) looks like '&' (Ampersand), but it is not | -LL | fn f(_: &&()) -> &() { todo!() } - | ~ +LL - fn f(_: &&()) -> &() { todo!() } +LL + fn f(_: &&()) -> &() { todo!() } + | error[E0106]: missing lifetime specifier --> $DIR/fullwidth-ampersand.rs:3:18 diff --git a/tests/ui/lifetimes/issue-26638.stderr b/tests/ui/lifetimes/issue-26638.stderr index dc18e0f1f7a1d..8aa94f66812f3 100644 --- a/tests/ui/lifetimes/issue-26638.stderr +++ b/tests/ui/lifetimes/issue-26638.stderr @@ -27,8 +27,9 @@ LL | fn parse_type_2(iter: &fn(&u8)->&u8) -> &str { iter() } | + help: ...or alternatively, you might want to return an owned value | -LL | fn parse_type_2(iter: fn(&u8)->&u8) -> String { iter() } - | ~~~~~~ +LL - fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter() } +LL + fn parse_type_2(iter: fn(&u8)->&u8) -> String { iter() } + | error[E0106]: missing lifetime specifier --> $DIR/issue-26638.rs:9:22 @@ -43,8 +44,9 @@ LL | fn parse_type_3() -> &'static str { unimplemented!() } | +++++++ help: instead, you are more likely to want to return an owned value | -LL | fn parse_type_3() -> String { unimplemented!() } - | ~~~~~~ +LL - fn parse_type_3() -> &str { unimplemented!() } +LL + fn parse_type_3() -> String { unimplemented!() } + | error[E0061]: this function takes 1 argument but 0 arguments were supplied --> $DIR/issue-26638.rs:4:47 @@ -54,8 +56,9 @@ LL | fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter() } | help: provide the argument | -LL | fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter(/* &u8 */) } - | ~~~~~~~~~~~ +LL - fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter() } +LL + fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter(/* &u8 */) } + | error[E0308]: mismatched types --> $DIR/issue-26638.rs:4:47 diff --git a/tests/ui/lifetimes/issue-90170-elision-mismatch.stderr b/tests/ui/lifetimes/issue-90170-elision-mismatch.stderr index 82511d07b0efc..e03910ec79e7c 100644 --- a/tests/ui/lifetimes/issue-90170-elision-mismatch.stderr +++ b/tests/ui/lifetimes/issue-90170-elision-mismatch.stderr @@ -7,6 +7,9 @@ LL | pub fn foo(x: &mut Vec<&u8>, y: &u8) { x.push(y); } | | let's call the lifetime of this reference `'1` | let's call the lifetime of this reference `'2` | + = note: requirement occurs because of a mutable reference to `Vec<&u8>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider introducing a named lifetime parameter | LL | pub fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); } @@ -21,10 +24,14 @@ LL | pub fn foo2(x: &mut Vec<&'_ u8>, y: &u8) { x.push(y); } | | let's call the lifetime of this reference `'1` | let's call the lifetime of this reference `'2` | + = note: requirement occurs because of a mutable reference to `Vec<&u8>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider introducing a named lifetime parameter | -LL | pub fn foo2<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); } - | ++++ ~~ ++ +LL - pub fn foo2(x: &mut Vec<&'_ u8>, y: &u8) { x.push(y); } +LL + pub fn foo2<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); } + | error: lifetime may not live long enough --> $DIR/issue-90170-elision-mismatch.rs:7:63 @@ -35,6 +42,9 @@ LL | pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&u8>, y: &u8) { x.push(y); } | | let's call the lifetime of this reference `'1` | let's call the lifetime of this reference `'2` | + = note: requirement occurs because of a mutable reference to `Vec<&u8>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider reusing a named lifetime parameter | LL | pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); } diff --git a/tests/ui/lifetimes/issue-90600-expected-return-static-indirect.rs b/tests/ui/lifetimes/issue-90600-expected-return-static-indirect.rs index ce4cddc9b39b3..30a1811fee547 100644 --- a/tests/ui/lifetimes/issue-90600-expected-return-static-indirect.rs +++ b/tests/ui/lifetimes/issue-90600-expected-return-static-indirect.rs @@ -7,9 +7,9 @@ fn inner(mut foo: &[u8]) { let refcell = RefCell::new(&mut foo); //~^ ERROR `foo` does not live long enough let read = &refcell as &RefCell; - //~^ ERROR lifetime may not live long enough read_thing(read); + //~^ ERROR borrowed data escapes outside of function } fn read_thing(refcell: &RefCell) {} diff --git a/tests/ui/lifetimes/issue-90600-expected-return-static-indirect.stderr b/tests/ui/lifetimes/issue-90600-expected-return-static-indirect.stderr index e4cd54ac33741..4df2e906e222f 100644 --- a/tests/ui/lifetimes/issue-90600-expected-return-static-indirect.stderr +++ b/tests/ui/lifetimes/issue-90600-expected-return-static-indirect.stderr @@ -5,22 +5,32 @@ LL | fn inner(mut foo: &[u8]) { | ------- binding `foo` declared here LL | let refcell = RefCell::new(&mut foo); | ^^^^^^^^ borrowed value does not live long enough -LL | -LL | let read = &refcell as &RefCell; - | ------------------------------ cast requires that `foo` is borrowed for `'static` ... +LL | read_thing(read); + | ---------------- argument requires that `foo` is borrowed for `'static` +LL | LL | } | - `foo` dropped here while still borrowed -error: lifetime may not live long enough - --> $DIR/issue-90600-expected-return-static-indirect.rs:9:16 +error[E0521]: borrowed data escapes outside of function + --> $DIR/issue-90600-expected-return-static-indirect.rs:11:5 | LL | fn inner(mut foo: &[u8]) { - | - let's call the lifetime of this reference `'1` + | ------- - let's call the lifetime of this reference `'1` + | | + | `foo` is a reference that is only valid in the function body ... -LL | let read = &refcell as &RefCell; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'1` must outlive `'static` +LL | read_thing(read); + | ^^^^^^^^^^^^^^^^ + | | + | `foo` escapes the function body here + | argument requires that `'1` must outlive `'static` + | + = note: requirement occurs because of the type `RefCell<(dyn std::io::Read + 'static)>`, which makes the generic argument `(dyn std::io::Read + 'static)` invariant + = note: the struct `RefCell` is invariant over the parameter `T` + = help: see for more information about variance error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0597`. +Some errors have detailed explanations: E0521, E0597. +For more information about an error, try `rustc --explain E0521`. diff --git a/tests/ui/lifetimes/lifetime-errors/ex2b-push-no-existing-names.stderr b/tests/ui/lifetimes/lifetime-errors/ex2b-push-no-existing-names.stderr index 6f7127d4c4c4e..a187cb755dd56 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex2b-push-no-existing-names.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex2b-push-no-existing-names.stderr @@ -8,6 +8,9 @@ LL | fn foo(x: &mut Vec>, y: Ref) { LL | x.push(y); | ^^^^^^^^^ argument requires that `'1` must outlive `'2` | + = note: requirement occurs because of a mutable reference to `Vec>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider introducing a named lifetime parameter | LL | fn foo<'a>(x: &mut Vec>, y: Ref<'a, i32>) { diff --git a/tests/ui/lifetimes/lifetime-errors/ex2c-push-inference-variable.stderr b/tests/ui/lifetimes/lifetime-errors/ex2c-push-inference-variable.stderr index cace80272f5b5..610a669cdedd3 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex2c-push-inference-variable.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex2c-push-inference-variable.stderr @@ -10,6 +10,9 @@ LL | x.push(z); | ^^^^^^^^^ argument requires that `'c` must outlive `'b` | = help: consider adding the following bound: `'c: 'b` + = note: requirement occurs because of a mutable reference to `Vec>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/lifetime-errors/ex2d-push-inference-variable-2.rs b/tests/ui/lifetimes/lifetime-errors/ex2d-push-inference-variable-2.rs index f573230293eef..4cd06e1c02b2a 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex2d-push-inference-variable-2.rs +++ b/tests/ui/lifetimes/lifetime-errors/ex2d-push-inference-variable-2.rs @@ -4,9 +4,9 @@ struct Ref<'a, T: 'a> { fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { let a: &mut Vec> = x; + //~^ ERROR lifetime may not live long enough let b = Ref { data: y.data }; a.push(b); - //~^ ERROR lifetime may not live long enough } fn main() { } diff --git a/tests/ui/lifetimes/lifetime-errors/ex2d-push-inference-variable-2.stderr b/tests/ui/lifetimes/lifetime-errors/ex2d-push-inference-variable-2.stderr index 4a981e4de604d..0da32aacdb3d4 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex2d-push-inference-variable-2.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex2d-push-inference-variable-2.stderr @@ -1,15 +1,17 @@ error: lifetime may not live long enough - --> $DIR/ex2d-push-inference-variable-2.rs:8:5 + --> $DIR/ex2d-push-inference-variable-2.rs:6:33 | LL | fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { | -- -- lifetime `'c` defined here | | | lifetime `'b` defined here -... -LL | a.push(b); - | ^^^^^^^^^ argument requires that `'c` must outlive `'b` +LL | let a: &mut Vec> = x; + | ^ assignment requires that `'c` must outlive `'b` | = help: consider adding the following bound: `'c: 'b` + = note: requirement occurs because of a mutable reference to `Vec>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/lifetime-errors/ex2e-push-inference-variable-3.rs b/tests/ui/lifetimes/lifetime-errors/ex2e-push-inference-variable-3.rs index 4a934bbf080d5..498cea3682473 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex2e-push-inference-variable-3.rs +++ b/tests/ui/lifetimes/lifetime-errors/ex2e-push-inference-variable-3.rs @@ -4,9 +4,9 @@ struct Ref<'a, T: 'a> { fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { let a: &mut Vec> = x; + //~^ ERROR lifetime may not live long enough let b = Ref { data: y.data }; Vec::push(a, b); - //~^ ERROR lifetime may not live long enough } fn main() { } diff --git a/tests/ui/lifetimes/lifetime-errors/ex2e-push-inference-variable-3.stderr b/tests/ui/lifetimes/lifetime-errors/ex2e-push-inference-variable-3.stderr index 2bd047113bca0..4474a898fdc65 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex2e-push-inference-variable-3.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex2e-push-inference-variable-3.stderr @@ -1,15 +1,17 @@ error: lifetime may not live long enough - --> $DIR/ex2e-push-inference-variable-3.rs:8:5 + --> $DIR/ex2e-push-inference-variable-3.rs:6:33 | LL | fn foo<'a, 'b, 'c>(x: &'a mut Vec>, y: Ref<'c, i32>) { | -- -- lifetime `'c` defined here | | | lifetime `'b` defined here -... -LL | Vec::push(a, b); - | ^^^^^^^^^^^^^^^ argument requires that `'c` must outlive `'b` +LL | let a: &mut Vec> = x; + | ^ assignment requires that `'c` must outlive `'b` | = help: consider adding the following bound: `'c: 'b` + = note: requirement occurs because of a mutable reference to `Vec>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-2.rs b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-2.rs index 09ee9accccd2f..66e6eb91a22e3 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-2.rs +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-2.rs @@ -1,6 +1,6 @@ fn foo(&mut (ref mut v, w): &mut (&u8, &u8), x: &u8) { - *v = x; //~^ ERROR lifetime may not live long enough + *v = x; } fn main() { } diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-2.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-2.stderr index 30083b5ef5462..e7cab52084ddd 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-2.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-2.stderr @@ -1,13 +1,15 @@ error: lifetime may not live long enough - --> $DIR/ex3-both-anon-regions-2.rs:2:5 + --> $DIR/ex3-both-anon-regions-2.rs:1:14 | LL | fn foo(&mut (ref mut v, w): &mut (&u8, &u8), x: &u8) { - | - - let's call the lifetime of this reference `'1` - | | - | let's call the lifetime of this reference `'2` -LL | *v = x; - | ^^^^^^ assignment requires that `'1` must outlive `'2` + | ^^^^^^^^^ - - let's call the lifetime of this reference `'1` + | | | + | | let's call the lifetime of this reference `'2` + | assignment requires that `'1` must outlive `'2` | + = note: requirement occurs because of a mutable reference to `&u8` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider introducing a named lifetime parameter | LL | fn foo<'a>(&mut (ref mut v, w): &mut (&'a u8, &u8), x: &'a u8) { diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-3.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-3.stderr index 6ba130308a33a..c67ea19effc07 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-3.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-3.stderr @@ -8,6 +8,9 @@ LL | fn foo(z: &mut Vec<(&u8,&u8)>, (x, y): (&u8, &u8)) { LL | z.push((x,y)); | ^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2` | + = note: requirement occurs because of a mutable reference to `Vec<(&u8, &u8)>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider introducing a named lifetime parameter | LL | fn foo<'a>(z: &mut Vec<(&'a u8,&u8)>, (x, y): (&'a u8, &u8)) { @@ -23,6 +26,9 @@ LL | fn foo(z: &mut Vec<(&u8,&u8)>, (x, y): (&u8, &u8)) { LL | z.push((x,y)); | ^^^^^^^^^^^^^ argument requires that `'3` must outlive `'4` | + = note: requirement occurs because of a mutable reference to `Vec<(&u8, &u8)>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider introducing a named lifetime parameter | LL | fn foo<'a>(z: &mut Vec<(&u8,&'a u8)>, (x, y): (&u8, &'a u8)) { diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-earlybound-regions.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-earlybound-regions.stderr index 352619c0ffc3f..0980b37e535a1 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-earlybound-regions.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-earlybound-regions.stderr @@ -10,6 +10,9 @@ LL | x.push(y); | ^^^^^^^^^ argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of a mutable reference to `Vec>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-latebound-regions.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-latebound-regions.stderr index 16cf009ee48bc..16cd47420a58e 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-latebound-regions.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs-latebound-regions.stderr @@ -9,6 +9,9 @@ LL | x.push(y); | ^^^^^^^^^ argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of a mutable reference to `Vec>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs.stderr index 017bfa7146317..264673ff3e82c 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-both-are-structs.stderr @@ -8,6 +8,9 @@ LL | fn foo(mut x: Vec, y: Ref) { LL | x.push(y); | ^^^^^^^^^ argument requires that `'1` must outlive `'2` | + = note: requirement occurs because of a mutable reference to `Vec>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider introducing a named lifetime parameter | LL | fn foo<'a>(mut x: Vec>, y: Ref<'a>) { diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-latebound-regions.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-latebound-regions.stderr index 080eb43cecbc4..8552755d16852 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-latebound-regions.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-latebound-regions.stderr @@ -9,6 +9,9 @@ LL | x.push(y); | ^^^^^^^^^ argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of a mutable reference to `Vec<&u8>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-fn-items.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-fn-items.stderr index cb629d2e3d3fc..2a2cf6508fdb7 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-fn-items.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-fn-items.stderr @@ -19,6 +19,9 @@ LL | fn foo(x:fn(&u8, &u8), y: Vec<&u8>, z: &u8) { LL | y.push(z); | ^^^^^^^^^ argument requires that `'1` must outlive `'2` | + = note: requirement occurs because of a mutable reference to `Vec<&u8>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider introducing a named lifetime parameter | LL | fn foo<'a>(x:fn(&u8, &u8), y: Vec<&'a u8>, z: &'a u8) { diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-impl-items.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-impl-items.stderr index 420cfa6b569b6..01bfe7829206d 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-impl-items.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-impl-items.stderr @@ -8,6 +8,9 @@ LL | fn foo(x: &mut Vec<&u8>, y: &u8) { LL | x.push(y); | ^^^^^^^^^ argument requires that `'1` must outlive `'2` | + = note: requirement occurs because of a mutable reference to `Vec<&u8>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider introducing a named lifetime parameter and update trait if needed | LL | fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-trait-objects.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-trait-objects.stderr index 05f9308124b1e..41154755b5d88 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-trait-objects.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-using-trait-objects.stderr @@ -19,6 +19,9 @@ LL | fn foo(x:Box , y: Vec<&u8>, z: &u8) { LL | y.push(z); | ^^^^^^^^^ argument requires that `'1` must outlive `'2` | + = note: requirement occurs because of a mutable reference to `Vec<&u8>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider introducing a named lifetime parameter | LL | fn foo<'a>(x:Box , y: Vec<&'a u8>, z: &'a u8) { diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions.stderr index 875d22576e58c..10e8ca852f0f7 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions.stderr @@ -8,6 +8,9 @@ LL | fn foo(x: &mut Vec<&u8>, y: &u8) { LL | x.push(y); | ^^^^^^^^^ argument requires that `'1` must outlive `'2` | + = note: requirement occurs because of a mutable reference to `Vec<&u8>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider introducing a named lifetime parameter | LL | fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { diff --git a/tests/ui/lifetimes/raw/immediately-followed-by-lt.e2021.stderr b/tests/ui/lifetimes/raw/immediately-followed-by-lt.e2021.stderr index e600cc37fc42f..f16c18f85d327 100644 --- a/tests/ui/lifetimes/raw/immediately-followed-by-lt.e2021.stderr +++ b/tests/ui/lifetimes/raw/immediately-followed-by-lt.e2021.stderr @@ -6,8 +6,9 @@ LL | w!('r#long'id); | help: if you meant to write a string literal, use double quotes | -LL | w!("r#long"id); - | ~ ~ +LL - w!('r#long'id); +LL + w!("r#long"id); + | error: aborting due to 1 previous error diff --git a/tests/ui/lifetimes/tail-expr-in-nested-expr.stderr b/tests/ui/lifetimes/tail-expr-in-nested-expr.stderr index 6770da091ce8d..09801a1aad24a 100644 --- a/tests/ui/lifetimes/tail-expr-in-nested-expr.stderr +++ b/tests/ui/lifetimes/tail-expr-in-nested-expr.stderr @@ -2,11 +2,10 @@ error[E0716]: temporary value dropped while borrowed --> $DIR/tail-expr-in-nested-expr.rs:4:15 | LL | let _ = { String::new().as_str() }.len(); - | ^^^^^^^^^^^^^--------- + | ^^^^^^^^^^^^^ - --- borrow later used by call | | | | | temporary value is freed at the end of this statement | creates a temporary value which is freed while still in use - | borrow later used here | = note: consider using a `let` binding to create a longer lived value diff --git a/tests/ui/lifetimes/tail-expr-lock-poisoning.rs b/tests/ui/lifetimes/tail-expr-lock-poisoning.rs index 6af6655149bd9..1c838a9e5e199 100644 --- a/tests/ui/lifetimes/tail-expr-lock-poisoning.rs +++ b/tests/ui/lifetimes/tail-expr-lock-poisoning.rs @@ -1,5 +1,5 @@ //@ revisions: edition2021 edition2024 -//@ ignore-wasm no panic or subprocess support +//@ needs-subprocess //@ [edition2024] edition: 2024 //@ run-pass //@ needs-unwind diff --git a/tests/ui/link-native-libs/issue-109144.rs b/tests/ui/link-native-libs/issue-109144.rs index 2f740e5538973..6970a4989bb53 100644 --- a/tests/ui/link-native-libs/issue-109144.rs +++ b/tests/ui/link-native-libs/issue-109144.rs @@ -1,4 +1,4 @@ #![crate_type = "lib"] #[link(kind = "static", modifiers = "+whole-archive,+bundle")] //~^ ERROR `#[link]` attribute requires a `name = "string"` argument -extern {} +extern "C" {} diff --git a/tests/ui/link-native-libs/suggest-libname-only-1.stderr b/tests/ui/link-native-libs/suggest-libname-only-1.stderr index e142835a9d64c..47f7d92c9f9e2 100644 --- a/tests/ui/link-native-libs/suggest-libname-only-1.stderr +++ b/tests/ui/link-native-libs/suggest-libname-only-1.stderr @@ -1,6 +1,14 @@ +warning: extern declarations without an explicit ABI are deprecated + --> $DIR/suggest-libname-only-1.rs:7:1 + | +LL | extern { } + | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` + | + = note: `#[warn(missing_abi)]` on by default + error: could not find native static library `libfoo.a`, perhaps an -L flag is missing? | = help: only provide the library name `foo`, not the full filename -error: aborting due to 1 previous error +error: aborting due to 1 previous error; 1 warning emitted diff --git a/tests/ui/link-native-libs/suggest-libname-only-2.stderr b/tests/ui/link-native-libs/suggest-libname-only-2.stderr index 392d2f01f619b..a2d8f4c8191bf 100644 --- a/tests/ui/link-native-libs/suggest-libname-only-2.stderr +++ b/tests/ui/link-native-libs/suggest-libname-only-2.stderr @@ -1,6 +1,14 @@ +warning: extern declarations without an explicit ABI are deprecated + --> $DIR/suggest-libname-only-2.rs:7:1 + | +LL | extern { } + | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` + | + = note: `#[warn(missing_abi)]` on by default + error: could not find native static library `bar.lib`, perhaps an -L flag is missing? | = help: only provide the library name `bar`, not the full filename -error: aborting due to 1 previous error +error: aborting due to 1 previous error; 1 warning emitted diff --git a/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs b/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs index a169997927ebf..0717a2d5a6c9e 100644 --- a/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs +++ b/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs @@ -1,5 +1,4 @@ //@ run-pass -//@ ignore-windows //@ ignore-apple //@ ignore-wasm32 common linkage not implemented right now @@ -11,15 +10,14 @@ pub static TEST2: bool = true; #[linkage = "internal"] pub static TEST3: bool = true; +#[cfg(not(target_env = "msvc"))] #[linkage = "linkonce"] pub static TEST4: bool = true; +#[cfg(not(target_env = "msvc"))] #[linkage = "linkonce_odr"] pub static TEST5: bool = true; -#[linkage = "private"] -pub static TEST6: bool = true; - #[linkage = "weak"] pub static TEST7: bool = true; diff --git a/tests/ui/linkage-attr/propagate-generic-issue-18804/main.rs b/tests/ui/linkage-attr/propagate-generic-issue-18804/main.rs index 56a9358cce3cf..8c194ec50df4b 100644 --- a/tests/ui/linkage-attr/propagate-generic-issue-18804/main.rs +++ b/tests/ui/linkage-attr/propagate-generic-issue-18804/main.rs @@ -3,7 +3,6 @@ // functions. Failure results in a linker error. //@ ignore-emscripten no weak symbol support -//@ ignore-windows no extern_weak linkage //@ ignore-apple no extern_weak linkage //@ aux-build:lib.rs diff --git a/tests/ui/lint/ambiguous_wide_pointer_comparisons_suggestions.stderr b/tests/ui/lint/ambiguous_wide_pointer_comparisons_suggestions.stderr index d9190dbb81340..30d7683ef4b49 100644 --- a/tests/ui/lint/ambiguous_wide_pointer_comparisons_suggestions.stderr +++ b/tests/ui/lint/ambiguous_wide_pointer_comparisons_suggestions.stderr @@ -7,12 +7,14 @@ LL | let _ = a == b; = note: `#[warn(ambiguous_wide_pointer_comparisons)]` on by default help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = std::ptr::addr_eq(a, b); - | ++++++++++++++++++ ~ + +LL - let _ = a == b; +LL + let _ = std::ptr::addr_eq(a, b); + | help: use explicit `std::ptr::eq` method to compare metadata and addresses | -LL | let _ = std::ptr::eq(a, b); - | +++++++++++++ ~ + +LL - let _ = a == b; +LL + let _ = std::ptr::eq(a, b); + | warning: 1 warning emitted diff --git a/tests/ui/lint/clashing-extern-fn.rs b/tests/ui/lint/clashing-extern-fn.rs index a12fe81eecdaf..e4477c9620221 100644 --- a/tests/ui/lint/clashing-extern-fn.rs +++ b/tests/ui/lint/clashing-extern-fn.rs @@ -1,7 +1,7 @@ //@ check-pass //@ aux-build:external_extern_fn.rs #![crate_type = "lib"] - +#![feature(pattern_type_macro, pattern_types)] mod redeclared_different_signature { mod a { extern "C" { @@ -248,6 +248,7 @@ mod sameish_members { mod same_sized_members_clash { mod a { + #[allow(uses_power_alignment)] #[repr(C)] struct Point3 { x: f32, @@ -489,3 +490,33 @@ mod hidden_niche { } } } + +mod pattern_types { + mod a { + use std::pat::pattern_type; + #[repr(transparent)] + struct NonZeroUsize(pattern_type!(usize is 1..)); + extern "C" { + fn pt_non_zero_usize() -> pattern_type!(usize is 1..); + fn pt_non_zero_usize_opt() -> Option; + fn pt_non_zero_usize_opt_full_range() -> Option; + //~^ WARN not FFI-safe + fn pt_non_null_ptr() -> pattern_type!(usize is 1..); + fn pt_non_zero_usize_wrapper() -> NonZeroUsize; + fn pt_non_zero_usize_wrapper_opt() -> Option; + } + } + mod b { + extern "C" { + // If there's a clash in either of these cases you're either gaining an incorrect + // invariant that the value is non-zero, or you're missing out on that invariant. Both + // cases are warning for, from both a caller-convenience and optimisation perspective. + fn pt_non_zero_usize() -> usize; + fn pt_non_zero_usize_opt() -> usize; + fn pt_non_null_ptr() -> *const (); + //~^ WARN `pt_non_null_ptr` redeclared with a different signature + fn pt_non_zero_usize_wrapper() -> usize; + fn pt_non_zero_usize_wrapper_opt() -> usize; + } + } +} diff --git a/tests/ui/lint/clashing-extern-fn.stderr b/tests/ui/lint/clashing-extern-fn.stderr index b30dd476a1d74..118b18b224c05 100644 --- a/tests/ui/lint/clashing-extern-fn.stderr +++ b/tests/ui/lint/clashing-extern-fn.stderr @@ -1,5 +1,5 @@ warning: `extern` block uses type `Option`, which is not FFI-safe - --> $DIR/clashing-extern-fn.rs:482:55 + --> $DIR/clashing-extern-fn.rs:483:55 | LL | fn hidden_niche_transparent_no_niche() -> Option; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -9,7 +9,7 @@ LL | fn hidden_niche_transparent_no_niche() -> Option>>`, which is not FFI-safe - --> $DIR/clashing-extern-fn.rs:486:46 + --> $DIR/clashing-extern-fn.rs:487:46 | LL | fn hidden_niche_unsafe_cell() -> Option>>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -17,6 +17,15 @@ LL | fn hidden_niche_unsafe_cell() -> Option`, which is not FFI-safe + --> $DIR/clashing-extern-fn.rs:502:54 + | +LL | fn pt_non_zero_usize_opt_full_range() -> Option; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum + = note: enum has no representation hint + warning: `clash` redeclared with a different signature --> $DIR/clashing-extern-fn.rs:13:13 | @@ -151,7 +160,7 @@ LL | fn draw_point(p: Point); found `unsafe extern "C" fn(sameish_members::b::Point)` warning: `origin` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:269:13 + --> $DIR/clashing-extern-fn.rs:270:13 | LL | fn origin() -> Point3; | ---------------------- `origin` previously declared here @@ -163,7 +172,7 @@ LL | fn origin() -> Point3; found `unsafe extern "C" fn() -> same_sized_members_clash::b::Point3` warning: `transparent_incorrect` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:292:13 + --> $DIR/clashing-extern-fn.rs:293:13 | LL | fn transparent_incorrect() -> T; | -------------------------------- `transparent_incorrect` previously declared here @@ -175,7 +184,7 @@ LL | fn transparent_incorrect() -> isize; found `unsafe extern "C" fn() -> isize` warning: `missing_return_type` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:310:13 + --> $DIR/clashing-extern-fn.rs:311:13 | LL | fn missing_return_type() -> usize; | ---------------------------------- `missing_return_type` previously declared here @@ -187,7 +196,7 @@ LL | fn missing_return_type(); found `unsafe extern "C" fn()` warning: `non_zero_usize` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:328:13 + --> $DIR/clashing-extern-fn.rs:329:13 | LL | fn non_zero_usize() -> core::num::NonZero; | ------------------------------------------------- `non_zero_usize` previously declared here @@ -199,7 +208,7 @@ LL | fn non_zero_usize() -> usize; found `unsafe extern "C" fn() -> usize` warning: `non_null_ptr` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:330:13 + --> $DIR/clashing-extern-fn.rs:331:13 | LL | fn non_null_ptr() -> core::ptr::NonNull; | ----------------------------------------------- `non_null_ptr` previously declared here @@ -211,7 +220,7 @@ LL | fn non_null_ptr() -> *const usize; found `unsafe extern "C" fn() -> *const usize` warning: `option_non_zero_usize_incorrect` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:424:13 + --> $DIR/clashing-extern-fn.rs:425:13 | LL | fn option_non_zero_usize_incorrect() -> usize; | ---------------------------------------------- `option_non_zero_usize_incorrect` previously declared here @@ -223,7 +232,7 @@ LL | fn option_non_zero_usize_incorrect() -> isize; found `unsafe extern "C" fn() -> isize` warning: `option_non_null_ptr_incorrect` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:426:13 + --> $DIR/clashing-extern-fn.rs:427:13 | LL | fn option_non_null_ptr_incorrect() -> *const usize; | --------------------------------------------------- `option_non_null_ptr_incorrect` previously declared here @@ -235,7 +244,7 @@ LL | fn option_non_null_ptr_incorrect() -> *const isize; found `unsafe extern "C" fn() -> *const isize` warning: `hidden_niche_transparent_no_niche` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:482:13 + --> $DIR/clashing-extern-fn.rs:483:13 | LL | fn hidden_niche_transparent_no_niche() -> usize; | ------------------------------------------------ `hidden_niche_transparent_no_niche` previously declared here @@ -247,7 +256,7 @@ LL | fn hidden_niche_transparent_no_niche() -> Option Option` warning: `hidden_niche_unsafe_cell` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:486:13 + --> $DIR/clashing-extern-fn.rs:487:13 | LL | fn hidden_niche_unsafe_cell() -> usize; | --------------------------------------- `hidden_niche_unsafe_cell` previously declared here @@ -258,5 +267,17 @@ LL | fn hidden_niche_unsafe_cell() -> Option usize` found `unsafe extern "C" fn() -> Option>>` -warning: 22 warnings emitted +warning: `pt_non_null_ptr` redeclared with a different signature + --> $DIR/clashing-extern-fn.rs:516:13 + | +LL | fn pt_non_null_ptr() -> pattern_type!(usize is 1..); + | ---------------------------------------------------- `pt_non_null_ptr` previously declared here +... +LL | fn pt_non_null_ptr() -> *const (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration + | + = note: expected `unsafe extern "C" fn() -> (usize) is 1..=` + found `unsafe extern "C" fn() -> *const ()` + +warning: 24 warnings emitted diff --git a/tests/ui/lint/cli-lint-override.forbid_warn.stderr b/tests/ui/lint/cli-lint-override.forbid_warn.stderr index 169be997b48c6..fb8779ad4f15d 100644 --- a/tests/ui/lint/cli-lint-override.forbid_warn.stderr +++ b/tests/ui/lint/cli-lint-override.forbid_warn.stderr @@ -2,7 +2,7 @@ error: extern declarations without an explicit ABI are deprecated --> $DIR/cli-lint-override.rs:12:1 | LL | extern fn foo() {} - | ^^^^^^ help: explicitly specify the C ABI: `extern "C"` + | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` | = note: requested on the command line with `-F missing-abi` diff --git a/tests/ui/lint/cli-lint-override.force_warn_deny.stderr b/tests/ui/lint/cli-lint-override.force_warn_deny.stderr index 574f2ca66a420..10fc13e3f52f8 100644 --- a/tests/ui/lint/cli-lint-override.force_warn_deny.stderr +++ b/tests/ui/lint/cli-lint-override.force_warn_deny.stderr @@ -2,7 +2,7 @@ warning: extern declarations without an explicit ABI are deprecated --> $DIR/cli-lint-override.rs:12:1 | LL | extern fn foo() {} - | ^^^^^^ help: explicitly specify the C ABI: `extern "C"` + | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` | = note: requested on the command line with `--force-warn missing-abi` diff --git a/tests/ui/lint/cli-lint-override.warn_deny.stderr b/tests/ui/lint/cli-lint-override.warn_deny.stderr index bfec37ada95e5..979ca22324f1e 100644 --- a/tests/ui/lint/cli-lint-override.warn_deny.stderr +++ b/tests/ui/lint/cli-lint-override.warn_deny.stderr @@ -2,7 +2,7 @@ error: extern declarations without an explicit ABI are deprecated --> $DIR/cli-lint-override.rs:12:1 | LL | extern fn foo() {} - | ^^^^^^ help: explicitly specify the C ABI: `extern "C"` + | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` | = note: requested on the command line with `-D missing-abi` diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/allow.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/allow.stderr index fd434eacf3de3..e1c12cfd1a501 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/allow.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/allow.stderr @@ -7,6 +7,8 @@ LL | dbg!(String::new().as_ptr()); | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice = help: for more information, see note: the lint level is defined here --> $DIR/allow.rs:7:12 @@ -23,6 +25,8 @@ LL | dbg!(String::new().as_ptr()); | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice = help: for more information, see note: the lint level is defined here --> $DIR/allow.rs:18:12 diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/calls.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/calls.stderr index d1615b76d82e0..41c6cdd0e3ef4 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/calls.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/calls.stderr @@ -7,6 +7,8 @@ LL | let ptr = cstring().as_ptr(); | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `CString` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `CString` inside the function will not suffice = help: for more information, see note: the lint level is defined here --> $DIR/calls.rs:1:9 @@ -23,6 +25,8 @@ LL | let ptr = cstring().as_ptr(); | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `CString` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `CString` inside the function will not suffice = help: for more information, see error: a dangling pointer will be produced because the temporary `CString` will be dropped @@ -34,6 +38,8 @@ LL | let _ptr: *const u8 = cstring().as_ptr().cast(); | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `CString` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `CString` inside the function will not suffice = help: for more information, see error: a dangling pointer will be produced because the temporary `CString` will be dropped @@ -45,6 +51,8 @@ LL | let _ptr: *const u8 = { cstring() }.as_ptr().cast(); | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `CString` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `CString` inside the function will not suffice = help: for more information, see error: a dangling pointer will be produced because the temporary `CString` will be dropped @@ -56,6 +64,8 @@ LL | let _ptr: *const u8 = { cstring().as_ptr() }.cast(); | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `CString` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `CString` inside the function will not suffice = help: for more information, see error: aborting due to 5 previous errors diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.stderr index 5289fbb872342..d4126ba231f76 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.stderr @@ -15,6 +15,8 @@ LL | let s = CString::new("some text").unwrap().as_ptr(); | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `CString` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `CString` inside the function will not suffice = help: for more information, see note: the lint level is defined here --> $DIR/cstring-as-ptr.rs:2:9 @@ -34,6 +36,8 @@ LL | mymacro!(); | ---------- in this macro invocation | = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `CString` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `CString` inside the function will not suffice = help: for more information, see = note: this error originates in the macro `mymacro` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.stderr index 0de794f6ae285..aace55e92cf16 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.stderr @@ -7,6 +7,8 @@ LL | let str1 = String::with_capacity(MAX_PATH).as_mut_ptr(); | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_mut_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_mut_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice = help: for more information, see note: the lint level is defined here --> $DIR/example-from-issue123613.rs:1:9 @@ -23,6 +25,8 @@ LL | let str2 = String::from("TotototototototototototototototototoT").as_ptr | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice = help: for more information, see error: aborting due to 2 previous errors diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/ext.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/ext.stderr index 5d401c89c0ca2..976334ddef9c8 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/ext.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/ext.stderr @@ -7,6 +7,8 @@ LL | let _ptr1 = Vec::::new().as_ptr().dbg(); | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `Vec` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `Vec` inside the function will not suffice = help: for more information, see note: the lint level is defined here --> $DIR/ext.rs:1:9 @@ -23,6 +25,8 @@ LL | let _ptr2 = vec![0].as_ptr().foo(); | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `Vec` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `Vec` inside the function will not suffice = help: for more information, see error: aborting due to 2 previous errors diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/methods.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/methods.stderr index 11c052c158e55..a86a69bc39a28 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/methods.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/methods.stderr @@ -7,6 +7,8 @@ LL | vec![0u8].as_ptr(); | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `Vec` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `Vec` inside the function will not suffice = help: for more information, see note: the lint level is defined here --> $DIR/methods.rs:1:9 @@ -23,6 +25,8 @@ LL | vec![0u8].as_mut_ptr(); | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_mut_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `Vec` to lives at least as long as the pointer returned by the call to `as_mut_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `Vec` inside the function will not suffice = help: for more information, see error: aborting due to 2 previous errors diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.stderr index d2e9ac8c4e957..e8994703cabfa 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.stderr @@ -7,6 +7,8 @@ LL | string().as_ptr(); | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice = help: for more information, see note: the lint level is defined here --> $DIR/temporaries.rs:2:9 @@ -23,6 +25,8 @@ LL | "hello".to_string().as_ptr(); | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice = help: for more information, see error: a dangling pointer will be produced because the temporary `String` will be dropped @@ -34,6 +38,8 @@ LL | (string() + "hello").as_ptr(); | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice = help: for more information, see error: a dangling pointer will be produced because the temporary `String` will be dropped @@ -45,6 +51,8 @@ LL | (if true { String::new() } else { "hello".into() }).as_ptr(); | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice = help: for more information, see error: a dangling pointer will be produced because the temporary `String` will be dropped @@ -58,6 +66,8 @@ LL | .as_ptr(); | ^^^^^^ this pointer will immediately be invalid | = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice = help: for more information, see error: a dangling pointer will be produced because the temporary `String` will be dropped @@ -71,6 +81,8 @@ LL | .as_ptr(); | ^^^^^^ this pointer will immediately be invalid | = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice = help: for more information, see error: a dangling pointer will be produced because the temporary `String` will be dropped @@ -82,6 +94,8 @@ LL | { string() }.as_ptr(); | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice = help: for more information, see error: a dangling pointer will be produced because the temporary `Vec` will be dropped @@ -93,6 +107,8 @@ LL | vec![0u8].as_ptr(); | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `Vec` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `Vec` inside the function will not suffice = help: for more information, see error: aborting due to 8 previous errors diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/types.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/types.stderr index 250ed6dc9e379..fab2459b53f6f 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/types.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/types.stderr @@ -7,6 +7,8 @@ LL | declval::().as_ptr(); | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `CString` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `CString` inside the function will not suffice = help: for more information, see note: the lint level is defined here --> $DIR/types.rs:1:9 @@ -23,6 +25,8 @@ LL | declval::().as_ptr(); | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice = help: for more information, see error: a dangling pointer will be produced because the temporary `Vec` will be dropped @@ -34,6 +38,8 @@ LL | declval::>().as_ptr(); | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `Vec` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `Vec` inside the function will not suffice = help: for more information, see error: a dangling pointer will be produced because the temporary `Box` will be dropped @@ -45,6 +51,8 @@ LL | declval::>().as_ptr(); | this `Box` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `Box` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `Box` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `Box` inside the function will not suffice = help: for more information, see error: a dangling pointer will be produced because the temporary `Box<[u8]>` will be dropped @@ -56,6 +64,8 @@ LL | declval::>().as_ptr(); | this `Box<[u8]>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `Box<[u8]>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `Box<[u8]>` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `Box<[u8]>` inside the function will not suffice = help: for more information, see error: a dangling pointer will be produced because the temporary `Box` will be dropped @@ -67,6 +77,8 @@ LL | declval::>().as_ptr(); | this `Box` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `Box` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `Box` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `Box` inside the function will not suffice = help: for more information, see error: a dangling pointer will be produced because the temporary `Box` will be dropped @@ -78,6 +90,8 @@ LL | declval::>().as_ptr(); | this `Box` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `Box` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `Box` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `Box` inside the function will not suffice = help: for more information, see error: a dangling pointer will be produced because the temporary `[u8; 10]` will be dropped @@ -89,6 +103,8 @@ LL | declval::<[u8; 10]>().as_ptr(); | this `[u8; 10]` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `[u8; 10]` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `[u8; 10]` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `[u8; 10]` inside the function will not suffice = help: for more information, see error: a dangling pointer will be produced because the temporary `Box<[u8; 10]>` will be dropped @@ -100,6 +116,8 @@ LL | declval::>().as_ptr(); | this `Box<[u8; 10]>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `Box<[u8; 10]>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `Box<[u8; 10]>` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `Box<[u8; 10]>` inside the function will not suffice = help: for more information, see error: a dangling pointer will be produced because the temporary `Box>` will be dropped @@ -111,6 +129,8 @@ LL | declval::>>().as_ptr(); | this `Box>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `Box>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `Box>` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `Box>` inside the function will not suffice = help: for more information, see error: a dangling pointer will be produced because the temporary `Box` will be dropped @@ -122,6 +142,8 @@ LL | declval::>().as_ptr(); | this `Box` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `Box` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `Box` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `Box` inside the function will not suffice = help: for more information, see error: a dangling pointer will be produced because the temporary `Box>>>` will be dropped @@ -133,6 +155,8 @@ LL | declval::>>>>().as_ptr(); | this `Box>>>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `Box>>>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `Box>>>` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `Box>>>` inside the function will not suffice = help: for more information, see error: a dangling pointer will be produced because the temporary `Cell` will be dropped @@ -144,6 +168,8 @@ LL | declval::>().as_ptr(); | this `Cell` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `Cell` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `Cell` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `Cell` inside the function will not suffice = help: for more information, see error: a dangling pointer will be produced because the temporary `MaybeUninit` will be dropped @@ -155,6 +181,8 @@ LL | declval::>().as_ptr(); | this `MaybeUninit` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `MaybeUninit` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `MaybeUninit` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `MaybeUninit` inside the function will not suffice = help: for more information, see error: a dangling pointer will be produced because the temporary `Vec` will be dropped @@ -166,6 +194,8 @@ LL | declval::>().as_ptr(); | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `as_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `Vec` to lives at least as long as the pointer returned by the call to `as_ptr` + = help: in particular, if this pointer is returned from the current function, binding the `Vec` inside the function will not suffice = help: for more information, see error: a dangling pointer will be produced because the temporary `UnsafeCell` will be dropped @@ -177,6 +207,8 @@ LL | declval::>().get(); | this `UnsafeCell` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `get` the `UnsafeCell` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `UnsafeCell` to lives at least as long as the pointer returned by the call to `get` + = help: in particular, if this pointer is returned from the current function, binding the `UnsafeCell` inside the function will not suffice = help: for more information, see error: a dangling pointer will be produced because the temporary `SyncUnsafeCell` will be dropped @@ -188,6 +220,8 @@ LL | declval::>().get(); | this `SyncUnsafeCell` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | = note: pointers do not have a lifetime; when calling `get` the `SyncUnsafeCell` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: you must make sure that the variable you bind the `SyncUnsafeCell` to lives at least as long as the pointer returned by the call to `get` + = help: in particular, if this pointer is returned from the current function, binding the `SyncUnsafeCell` inside the function will not suffice = help: for more information, see error: aborting due to 17 previous errors diff --git a/tests/ui/lint/dead-code/lint-dead-code-1.rs b/tests/ui/lint/dead-code/lint-dead-code-1.rs index ddcafedf7bc5f..a7f654b5d8bf4 100644 --- a/tests/ui/lint/dead-code/lint-dead-code-1.rs +++ b/tests/ui/lint/dead-code/lint-dead-code-1.rs @@ -29,6 +29,7 @@ const used_const: isize = 0; pub const used_const2: isize = used_const; const USED_CONST: isize = 1; const CONST_USED_IN_ENUM_DISCRIMINANT: isize = 11; +const CONST_USED_IN_RANGE_PATTERN: isize = 12; pub type typ = *const UsedStruct4; pub struct PubStruct; @@ -81,6 +82,7 @@ pub fn pub_fn() { match i { USED_STATIC => (), USED_CONST => (), + CONST_USED_IN_RANGE_PATTERN..100 => {} _ => () } f::(); diff --git a/tests/ui/lint/dead-code/lint-dead-code-1.stderr b/tests/ui/lint/dead-code/lint-dead-code-1.stderr index eb728b5b93055..c4410114cea88 100644 --- a/tests/ui/lint/dead-code/lint-dead-code-1.stderr +++ b/tests/ui/lint/dead-code/lint-dead-code-1.stderr @@ -17,19 +17,19 @@ LL | const priv_const: isize = 0; | ^^^^^^^^^^ error: struct `PrivStruct` is never constructed - --> $DIR/lint-dead-code-1.rs:35:8 + --> $DIR/lint-dead-code-1.rs:36:8 | LL | struct PrivStruct; | ^^^^^^^^^^ error: enum `priv_enum` is never used - --> $DIR/lint-dead-code-1.rs:64:6 + --> $DIR/lint-dead-code-1.rs:65:6 | LL | enum priv_enum { foo2, bar2 } | ^^^^^^^^^ error: variant `bar3` is never constructed - --> $DIR/lint-dead-code-1.rs:67:5 + --> $DIR/lint-dead-code-1.rs:68:5 | LL | enum used_enum { | --------- variant in this enum @@ -38,25 +38,25 @@ LL | bar3 | ^^^^ error: function `priv_fn` is never used - --> $DIR/lint-dead-code-1.rs:88:4 + --> $DIR/lint-dead-code-1.rs:90:4 | LL | fn priv_fn() { | ^^^^^^^ error: function `foo` is never used - --> $DIR/lint-dead-code-1.rs:93:4 + --> $DIR/lint-dead-code-1.rs:95:4 | LL | fn foo() { | ^^^ error: function `bar` is never used - --> $DIR/lint-dead-code-1.rs:98:4 + --> $DIR/lint-dead-code-1.rs:100:4 | LL | fn bar() { | ^^^ error: function `baz` is never used - --> $DIR/lint-dead-code-1.rs:102:4 + --> $DIR/lint-dead-code-1.rs:104:4 | LL | fn baz() -> impl Copy { | ^^^ diff --git a/tests/ui/lint/dead-code/lint-dead-code-2.rs b/tests/ui/lint/dead-code/lint-dead-code-2.rs index 6bfa4d96f710b..c82088ec54be9 100644 --- a/tests/ui/lint/dead-code/lint-dead-code-2.rs +++ b/tests/ui/lint/dead-code/lint-dead-code-2.rs @@ -1,6 +1,6 @@ #![allow(unused_variables)] #![deny(dead_code)] -#![feature(rustc_attrs, start)] +#![feature(rustc_attrs)] struct Foo; @@ -21,21 +21,16 @@ fn live_fn() {} fn dead_fn() {} //~ ERROR: function `dead_fn` is never used -#[rustc_main] -fn dead_fn2() {} //~ ERROR: function `dead_fn2` is never used - fn used_fn() {} -#[start] -fn start(_: isize, _: *const *const u8) -> isize { +#[rustc_main] +fn actual_main() { used_fn(); let foo = Foo; foo.bar2(); - 0 } // this is not main fn main() { //~ ERROR: function `main` is never used dead_fn(); - dead_fn2(); } diff --git a/tests/ui/lint/dead-code/lint-dead-code-2.stderr b/tests/ui/lint/dead-code/lint-dead-code-2.stderr index 85af553c9867f..4a5f3b8a68771 100644 --- a/tests/ui/lint/dead-code/lint-dead-code-2.stderr +++ b/tests/ui/lint/dead-code/lint-dead-code-2.stderr @@ -10,17 +10,11 @@ note: the lint level is defined here LL | #![deny(dead_code)] | ^^^^^^^^^ -error: function `dead_fn2` is never used - --> $DIR/lint-dead-code-2.rs:25:4 - | -LL | fn dead_fn2() {} - | ^^^^^^^^ - error: function `main` is never used - --> $DIR/lint-dead-code-2.rs:38:4 + --> $DIR/lint-dead-code-2.rs:34:4 | LL | fn main() { | ^^^^ -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/lint/dead-code/tuple-struct-field.stderr b/tests/ui/lint/dead-code/tuple-struct-field.stderr index 434554d7ae5ee..3e1d4e7727459 100644 --- a/tests/ui/lint/dead-code/tuple-struct-field.stderr +++ b/tests/ui/lint/dead-code/tuple-struct-field.stderr @@ -33,8 +33,9 @@ LL | struct UnusedInTheMiddle(i32, f32, String, u8, u32); | help: consider changing the fields to be of unit type to suppress this warning while preserving the field numbering, or remove the fields | -LL | struct UnusedInTheMiddle(i32, (), (), u8, ()); - | ~~ ~~ ~~ +LL - struct UnusedInTheMiddle(i32, f32, String, u8, u32); +LL + struct UnusedInTheMiddle(i32, (), (), u8, ()); + | error: aborting due to 3 previous errors diff --git a/tests/ui/lint/elided-named-lifetimes/static.stderr b/tests/ui/lint/elided-named-lifetimes/static.stderr index fa2a2d3460fea..7ad08dbf04baf 100644 --- a/tests/ui/lint/elided-named-lifetimes/static.stderr +++ b/tests/ui/lint/elided-named-lifetimes/static.stderr @@ -44,8 +44,9 @@ LL | fn underscore(x: &'static u8) -> &'_ u8 { | help: consider specifying it explicitly | -LL | fn underscore(x: &'static u8) -> &'static u8 { - | ~~~~~~~ +LL - fn underscore(x: &'static u8) -> &'_ u8 { +LL + fn underscore(x: &'static u8) -> &'static u8 { + | error: aborting due to 4 previous errors diff --git a/tests/ui/lint/fn-ptr-comparisons.stderr b/tests/ui/lint/fn-ptr-comparisons.stderr index eaba23a461ab6..e699332389864 100644 --- a/tests/ui/lint/fn-ptr-comparisons.stderr +++ b/tests/ui/lint/fn-ptr-comparisons.stderr @@ -10,8 +10,9 @@ LL | let _ = f == a; = note: `#[warn(unpredictable_function_pointer_comparisons)]` on by default help: refactor your code, or use `std::ptr::fn_addr_eq` to suppress the lint | -LL | let _ = std::ptr::fn_addr_eq(f, a as fn()); - | +++++++++++++++++++++ ~ ++++++++ +LL - let _ = f == a; +LL + let _ = std::ptr::fn_addr_eq(f, a as fn()); + | warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique --> $DIR/fn-ptr-comparisons.rs:28:13 @@ -24,8 +25,9 @@ LL | let _ = f != a; = note: for more information visit help: refactor your code, or use `std::ptr::fn_addr_eq` to suppress the lint | -LL | let _ = !std::ptr::fn_addr_eq(f, a as fn()); - | ++++++++++++++++++++++ ~ ++++++++ +LL - let _ = f != a; +LL + let _ = !std::ptr::fn_addr_eq(f, a as fn()); + | warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique --> $DIR/fn-ptr-comparisons.rs:30:13 @@ -38,8 +40,9 @@ LL | let _ = f == g; = note: for more information visit help: refactor your code, or use `std::ptr::fn_addr_eq` to suppress the lint | -LL | let _ = std::ptr::fn_addr_eq(f, g); - | +++++++++++++++++++++ ~ + +LL - let _ = f == g; +LL + let _ = std::ptr::fn_addr_eq(f, g); + | warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique --> $DIR/fn-ptr-comparisons.rs:32:13 @@ -52,8 +55,9 @@ LL | let _ = f == f; = note: for more information visit help: refactor your code, or use `std::ptr::fn_addr_eq` to suppress the lint | -LL | let _ = std::ptr::fn_addr_eq(f, f); - | +++++++++++++++++++++ ~ + +LL - let _ = f == f; +LL + let _ = std::ptr::fn_addr_eq(f, f); + | warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique --> $DIR/fn-ptr-comparisons.rs:34:13 @@ -66,8 +70,9 @@ LL | let _ = g == g; = note: for more information visit help: refactor your code, or use `std::ptr::fn_addr_eq` to suppress the lint | -LL | let _ = std::ptr::fn_addr_eq(g, g); - | +++++++++++++++++++++ ~ + +LL - let _ = g == g; +LL + let _ = std::ptr::fn_addr_eq(g, g); + | warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique --> $DIR/fn-ptr-comparisons.rs:36:13 @@ -80,8 +85,9 @@ LL | let _ = g == g; = note: for more information visit help: refactor your code, or use `std::ptr::fn_addr_eq` to suppress the lint | -LL | let _ = std::ptr::fn_addr_eq(g, g); - | +++++++++++++++++++++ ~ + +LL - let _ = g == g; +LL + let _ = std::ptr::fn_addr_eq(g, g); + | warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique --> $DIR/fn-ptr-comparisons.rs:38:13 @@ -94,8 +100,9 @@ LL | let _ = &g == &g; = note: for more information visit help: refactor your code, or use `std::ptr::fn_addr_eq` to suppress the lint | -LL | let _ = std::ptr::fn_addr_eq(g, g); - | ~~~~~~~~~~~~~~~~~~~~~ ~ + +LL - let _ = &g == &g; +LL + let _ = std::ptr::fn_addr_eq(g, g); + | warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique --> $DIR/fn-ptr-comparisons.rs:40:13 @@ -108,8 +115,9 @@ LL | let _ = a as fn() == g; = note: for more information visit help: refactor your code, or use `std::ptr::fn_addr_eq` to suppress the lint | -LL | let _ = std::ptr::fn_addr_eq(a as fn(), g); - | +++++++++++++++++++++ ~ + +LL - let _ = a as fn() == g; +LL + let _ = std::ptr::fn_addr_eq(a as fn(), g); + | warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique --> $DIR/fn-ptr-comparisons.rs:44:13 @@ -122,8 +130,9 @@ LL | let _ = cfn == c; = note: for more information visit help: refactor your code, or use `std::ptr::fn_addr_eq` to suppress the lint | -LL | let _ = std::ptr::fn_addr_eq(cfn, c as extern "C" fn()); - | +++++++++++++++++++++ ~ +++++++++++++++++++ +LL - let _ = cfn == c; +LL + let _ = std::ptr::fn_addr_eq(cfn, c as extern "C" fn()); + | warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique --> $DIR/fn-ptr-comparisons.rs:48:13 @@ -136,8 +145,9 @@ LL | let _ = argsfn == args; = note: for more information visit help: refactor your code, or use `std::ptr::fn_addr_eq` to suppress the lint | -LL | let _ = std::ptr::fn_addr_eq(argsfn, args as extern "C" fn(i32) -> i32); - | +++++++++++++++++++++ ~ +++++++++++++++++++++++++++++ +LL - let _ = argsfn == args; +LL + let _ = std::ptr::fn_addr_eq(argsfn, args as extern "C" fn(i32) -> i32); + | warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique --> $DIR/fn-ptr-comparisons.rs:52:13 @@ -150,8 +160,9 @@ LL | let _ = t == test; = note: for more information visit help: refactor your code, or use `std::ptr::fn_addr_eq` to suppress the lint | -LL | let _ = std::ptr::fn_addr_eq(t, test as unsafe extern "C" fn()); - | +++++++++++++++++++++ ~ ++++++++++++++++++++++++++ +LL - let _ = t == test; +LL + let _ = std::ptr::fn_addr_eq(t, test as unsafe extern "C" fn()); + | warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique --> $DIR/fn-ptr-comparisons.rs:56:13 @@ -164,8 +175,9 @@ LL | let _ = a1.f == a2.f; = note: for more information visit help: refactor your code, or use `std::ptr::fn_addr_eq` to suppress the lint | -LL | let _ = std::ptr::fn_addr_eq(a1.f, a2.f); - | +++++++++++++++++++++ ~ + +LL - let _ = a1.f == a2.f; +LL + let _ = std::ptr::fn_addr_eq(a1.f, a2.f); + | warning: 12 warnings emitted diff --git a/tests/ui/lint/for_loop_over_fallibles.stderr b/tests/ui/lint/for_loop_over_fallibles.stderr index f695de082572a..dae5e6428c8e2 100644 --- a/tests/ui/lint/for_loop_over_fallibles.stderr +++ b/tests/ui/lint/for_loop_over_fallibles.stderr @@ -7,12 +7,14 @@ LL | for _ in Some(1) {} = note: `#[warn(for_loops_over_fallibles)]` on by default help: to check pattern in a loop use `while let` | -LL | while let Some(_) = Some(1) {} - | ~~~~~~~~~~~~~~~ ~~~ +LL - for _ in Some(1) {} +LL + while let Some(_) = Some(1) {} + | help: consider using `if let` to clear intent | -LL | if let Some(_) = Some(1) {} - | ~~~~~~~~~~~~ ~~~ +LL - for _ in Some(1) {} +LL + if let Some(_) = Some(1) {} + | warning: for loop over a `Result`. This is more readably written as an `if let` statement --> $DIR/for_loop_over_fallibles.rs:9:14 @@ -22,12 +24,14 @@ LL | for _ in Ok::<_, ()>(1) {} | help: to check pattern in a loop use `while let` | -LL | while let Ok(_) = Ok::<_, ()>(1) {} - | ~~~~~~~~~~~~~ ~~~ +LL - for _ in Ok::<_, ()>(1) {} +LL + while let Ok(_) = Ok::<_, ()>(1) {} + | help: consider using `if let` to clear intent | -LL | if let Ok(_) = Ok::<_, ()>(1) {} - | ~~~~~~~~~~ ~~~ +LL - for _ in Ok::<_, ()>(1) {} +LL + if let Ok(_) = Ok::<_, ()>(1) {} + | warning: for loop over an `Option`. This is more readably written as an `if let` statement --> $DIR/for_loop_over_fallibles.rs:15:14 @@ -37,12 +41,14 @@ LL | for _ in [0; 0].iter().next() {} | help: to iterate over `[0; 0].iter()` remove the call to `next` | -LL | for _ in [0; 0].iter().by_ref() {} - | ~~~~~~~~~ +LL - for _ in [0; 0].iter().next() {} +LL + for _ in [0; 0].iter().by_ref() {} + | help: consider using `if let` to clear intent | -LL | if let Some(_) = [0; 0].iter().next() {} - | ~~~~~~~~~~~~ ~~~ +LL - for _ in [0; 0].iter().next() {} +LL + if let Some(_) = [0; 0].iter().next() {} + | warning: for loop over a `Result`. This is more readably written as an `if let` statement --> $DIR/for_loop_over_fallibles.rs:21:14 @@ -52,12 +58,14 @@ LL | for _ in Ok::<_, ()>([0; 0].iter()) {} | help: to check pattern in a loop use `while let` | -LL | while let Ok(_) = Ok::<_, ()>([0; 0].iter()) {} - | ~~~~~~~~~~~~~ ~~~ +LL - for _ in Ok::<_, ()>([0; 0].iter()) {} +LL + while let Ok(_) = Ok::<_, ()>([0; 0].iter()) {} + | help: consider using `if let` to clear intent | -LL | if let Ok(_) = Ok::<_, ()>([0; 0].iter()) {} - | ~~~~~~~~~~ ~~~ +LL - for _ in Ok::<_, ()>([0; 0].iter()) {} +LL + if let Ok(_) = Ok::<_, ()>([0; 0].iter()) {} + | warning: for loop over a `Result`. This is more readably written as an `if let` statement --> $DIR/for_loop_over_fallibles.rs:29:14 @@ -67,16 +75,18 @@ LL | for _ in Ok::<_, ()>([0; 0].iter()) {} | help: to check pattern in a loop use `while let` | -LL | while let Ok(_) = Ok::<_, ()>([0; 0].iter()) {} - | ~~~~~~~~~~~~~ ~~~ +LL - for _ in Ok::<_, ()>([0; 0].iter()) {} +LL + while let Ok(_) = Ok::<_, ()>([0; 0].iter()) {} + | help: consider unwrapping the `Result` with `?` to iterate over its contents | LL | for _ in Ok::<_, ()>([0; 0].iter())? {} | + help: consider using `if let` to clear intent | -LL | if let Ok(_) = Ok::<_, ()>([0; 0].iter()) {} - | ~~~~~~~~~~ ~~~ +LL - for _ in Ok::<_, ()>([0; 0].iter()) {} +LL + if let Ok(_) = Ok::<_, ()>([0; 0].iter()) {} + | warning: for loop over a `Result`. This is more readably written as an `if let` statement --> $DIR/for_loop_over_fallibles.rs:36:14 @@ -86,16 +96,18 @@ LL | for _ in Ok::<_, ()>([0; 0]) {} | help: to check pattern in a loop use `while let` | -LL | while let Ok(_) = Ok::<_, ()>([0; 0]) {} - | ~~~~~~~~~~~~~ ~~~ +LL - for _ in Ok::<_, ()>([0; 0]) {} +LL + while let Ok(_) = Ok::<_, ()>([0; 0]) {} + | help: consider unwrapping the `Result` with `?` to iterate over its contents | LL | for _ in Ok::<_, ()>([0; 0])? {} | + help: consider using `if let` to clear intent | -LL | if let Ok(_) = Ok::<_, ()>([0; 0]) {} - | ~~~~~~~~~~ ~~~ +LL - for _ in Ok::<_, ()>([0; 0]) {} +LL + if let Ok(_) = Ok::<_, ()>([0; 0]) {} + | warning: for loop over a `&Option`. This is more readably written as an `if let` statement --> $DIR/for_loop_over_fallibles.rs:47:14 @@ -105,12 +117,14 @@ LL | for _ in &Some(1) {} | help: to check pattern in a loop use `while let` | -LL | while let Some(_) = &Some(1) {} - | ~~~~~~~~~~~~~~~ ~~~ +LL - for _ in &Some(1) {} +LL + while let Some(_) = &Some(1) {} + | help: consider using `if let` to clear intent | -LL | if let Some(_) = &Some(1) {} - | ~~~~~~~~~~~~ ~~~ +LL - for _ in &Some(1) {} +LL + if let Some(_) = &Some(1) {} + | warning: for loop over a `&Result`. This is more readably written as an `if let` statement --> $DIR/for_loop_over_fallibles.rs:51:14 @@ -120,12 +134,14 @@ LL | for _ in &Ok::<_, ()>(1) {} | help: to check pattern in a loop use `while let` | -LL | while let Ok(_) = &Ok::<_, ()>(1) {} - | ~~~~~~~~~~~~~ ~~~ +LL - for _ in &Ok::<_, ()>(1) {} +LL + while let Ok(_) = &Ok::<_, ()>(1) {} + | help: consider using `if let` to clear intent | -LL | if let Ok(_) = &Ok::<_, ()>(1) {} - | ~~~~~~~~~~ ~~~ +LL - for _ in &Ok::<_, ()>(1) {} +LL + if let Ok(_) = &Ok::<_, ()>(1) {} + | warning: for loop over a `&mut Option`. This is more readably written as an `if let` statement --> $DIR/for_loop_over_fallibles.rs:57:14 @@ -135,12 +151,14 @@ LL | for _ in &mut Some(1) {} | help: to check pattern in a loop use `while let` | -LL | while let Some(_) = &mut Some(1) {} - | ~~~~~~~~~~~~~~~ ~~~ +LL - for _ in &mut Some(1) {} +LL + while let Some(_) = &mut Some(1) {} + | help: consider using `if let` to clear intent | -LL | if let Some(_) = &mut Some(1) {} - | ~~~~~~~~~~~~ ~~~ +LL - for _ in &mut Some(1) {} +LL + if let Some(_) = &mut Some(1) {} + | warning: for loop over a `&mut Result`. This is more readably written as an `if let` statement --> $DIR/for_loop_over_fallibles.rs:61:14 @@ -150,12 +168,14 @@ LL | for _ in &mut Ok::<_, ()>(1) {} | help: to check pattern in a loop use `while let` | -LL | while let Ok(_) = &mut Ok::<_, ()>(1) {} - | ~~~~~~~~~~~~~ ~~~ +LL - for _ in &mut Ok::<_, ()>(1) {} +LL + while let Ok(_) = &mut Ok::<_, ()>(1) {} + | help: consider using `if let` to clear intent | -LL | if let Ok(_) = &mut Ok::<_, ()>(1) {} - | ~~~~~~~~~~ ~~~ +LL - for _ in &mut Ok::<_, ()>(1) {} +LL + if let Ok(_) = &mut Ok::<_, ()>(1) {} + | warning: 10 warnings emitted diff --git a/tests/ui/lint/function-item-references.rs b/tests/ui/lint/function-item-references.rs index 918d72e28a918..4f2fc4de8632e 100644 --- a/tests/ui/lint/function-item-references.rs +++ b/tests/ui/lint/function-item-references.rs @@ -11,7 +11,7 @@ fn baz(x: u32, y: u32) -> u32 { x + y } unsafe fn unsafe_fn() { } extern "C" fn c_fn() { } unsafe extern "C" fn unsafe_c_fn() { } -unsafe extern fn variadic(_x: u32, _args: ...) { } +unsafe extern "C" fn variadic(_x: u32, _args: ...) { } fn take_generic_ref<'a, T>(_x: &'a T) { } fn take_generic_array(_x: [T; N]) { } fn multiple_generic(_x: T, _y: U) { } diff --git a/tests/ui/lint/invalid_value.stderr b/tests/ui/lint/invalid_value.stderr index b4e7421829f82..cc6a2a1c8e532 100644 --- a/tests/ui/lint/invalid_value.stderr +++ b/tests/ui/lint/invalid_value.stderr @@ -323,7 +323,7 @@ LL | let _val: (NonZero, i32) = mem::zeroed(); | ^^^^^^^^^^^^^ this code causes undefined behavior when executed | = note: `std::num::NonZero` must be non-null - = note: because `core::num::nonzero::private::NonZeroU32Inner` must be non-null + = note: because `core::num::niche_types::NonZeroU32Inner` must be non-null error: the type `(NonZero, i32)` does not permit being left uninitialized --> $DIR/invalid_value.rs:95:41 @@ -332,7 +332,7 @@ LL | let _val: (NonZero, i32) = mem::uninitialized(); | ^^^^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed | = note: `std::num::NonZero` must be non-null - = note: because `core::num::nonzero::private::NonZeroU32Inner` must be non-null + = note: because `core::num::niche_types::NonZeroU32Inner` must be non-null = note: integers must be initialized error: the type `*const dyn Send` does not permit zero-initialization @@ -415,7 +415,7 @@ note: because `std::num::NonZero` must be non-null (in this field of the on | LL | Banana(NonZero), | ^^^^^^^^^^^^ - = note: because `core::num::nonzero::private::NonZeroU32Inner` must be non-null + = note: because `core::num::niche_types::NonZeroU32Inner` must be non-null error: the type `OneFruitNonZero` does not permit being left uninitialized --> $DIR/invalid_value.rs:107:37 @@ -429,7 +429,7 @@ note: because `std::num::NonZero` must be non-null (in this field of the on | LL | Banana(NonZero), | ^^^^^^^^^^^^ - = note: because `core::num::nonzero::private::NonZeroU32Inner` must be non-null + = note: because `core::num::niche_types::NonZeroU32Inner` must be non-null = note: integers must be initialized error: the type `bool` does not permit being left uninitialized @@ -602,7 +602,7 @@ LL | let _val: NonZero = mem::transmute(0); | ^^^^^^^^^^^^^^^^^ this code causes undefined behavior when executed | = note: `std::num::NonZero` must be non-null - = note: because `core::num::nonzero::private::NonZeroU32Inner` must be non-null + = note: because `core::num::niche_types::NonZeroU32Inner` must be non-null error: the type `NonNull` does not permit zero-initialization --> $DIR/invalid_value.rs:156:34 diff --git a/tests/ui/lint/issue-106991.rs b/tests/ui/lint/issue-106991.rs index e4d7f765b4a8a..b70f751195a5f 100644 --- a/tests/ui/lint/issue-106991.rs +++ b/tests/ui/lint/issue-106991.rs @@ -3,7 +3,7 @@ fn foo(items: &mut Vec) { } fn bar() -> impl Iterator { - //~^ ERROR expected `foo` to be a fn item that returns `i32`, but it returns `()` [E0271] + //~^ ERROR expected `foo` to return `i32`, but it returns `()` [E0271] let mut x: Vec> = vec![vec![0, 2, 1], vec![5, 4, 3]]; x.iter_mut().map(foo) } diff --git a/tests/ui/lint/issue-106991.stderr b/tests/ui/lint/issue-106991.stderr index 0441a6377d088..15e0ba5337f14 100644 --- a/tests/ui/lint/issue-106991.stderr +++ b/tests/ui/lint/issue-106991.stderr @@ -1,4 +1,4 @@ -error[E0271]: expected `foo` to be a fn item that returns `i32`, but it returns `()` +error[E0271]: expected `foo` to return `i32`, but it returns `()` --> $DIR/issue-106991.rs:5:13 | LL | fn bar() -> impl Iterator { diff --git a/tests/ui/lint/issue-109152.stderr b/tests/ui/lint/issue-109152.stderr index a175964ccf63f..01aa9068da567 100644 --- a/tests/ui/lint/issue-109152.stderr +++ b/tests/ui/lint/issue-109152.stderr @@ -16,8 +16,9 @@ LL | #![deny(map_unit_fn)] | ^^^^^^^^^^^ help: you might have meant to use `Iterator::for_each` | -LL | vec![42].iter().for_each(drop); - | ~~~~~~~~ +LL - vec![42].iter().map(drop); +LL + vec![42].iter().for_each(drop); + | error: aborting due to 1 previous error diff --git a/tests/ui/lint/issue-109529.stderr b/tests/ui/lint/issue-109529.stderr index 9e857d1b0ab5d..51b71cb1007e6 100644 --- a/tests/ui/lint/issue-109529.stderr +++ b/tests/ui/lint/issue-109529.stderr @@ -16,8 +16,9 @@ LL | for _ in 0..(256 as u8) {} | help: use an inclusive range instead | -LL | for _ in 0..=(255 as u8) {} - | + ~~~ +LL - for _ in 0..(256 as u8) {} +LL + for _ in 0..=(255 as u8) {} + | error: aborting due to 2 previous errors diff --git a/tests/ui/lint/issue-35075.stderr b/tests/ui/lint/issue-35075.stderr index 08bdaa728583d..f02f9e678b469 100644 --- a/tests/ui/lint/issue-35075.stderr +++ b/tests/ui/lint/issue-35075.stderr @@ -6,8 +6,9 @@ LL | inner: Foo | help: there is an enum variant `Baz::Foo`; try using the variant's enum | -LL | inner: Baz - | ~~~ +LL - inner: Foo +LL + inner: Baz + | error[E0412]: cannot find type `Foo` in this scope --> $DIR/issue-35075.rs:6:9 @@ -17,8 +18,9 @@ LL | Foo(Foo) | help: there is an enum variant `Baz::Foo`; try using the variant's enum | -LL | Foo(Baz) - | ~~~ +LL - Foo(Foo) +LL + Foo(Baz) + | error: aborting due to 2 previous errors diff --git a/tests/ui/lint/let_underscore/issue-119696-err-on-fn.stderr b/tests/ui/lint/let_underscore/issue-119696-err-on-fn.stderr index 70f9979556a3a..105506968b16a 100644 --- a/tests/ui/lint/let_underscore/issue-119696-err-on-fn.stderr +++ b/tests/ui/lint/let_underscore/issue-119696-err-on-fn.stderr @@ -12,11 +12,12 @@ LL | #![deny(let_underscore_drop)] help: consider binding to an unused variable to avoid immediately dropping the value | LL | let _unused = foo(); - | ~~~~~~~ + | ++++++ help: consider immediately dropping the value | -LL | drop(foo()); - | ~~~~~ + +LL - let _ = foo(); +LL + drop(foo()); + | error: aborting due to 1 previous error diff --git a/tests/ui/lint/let_underscore/issue-119697-extra-let.stderr b/tests/ui/lint/let_underscore/issue-119697-extra-let.stderr index e4b1872bba55a..3ff57ab441dc9 100644 --- a/tests/ui/lint/let_underscore/issue-119697-extra-let.stderr +++ b/tests/ui/lint/let_underscore/issue-119697-extra-let.stderr @@ -11,12 +11,14 @@ LL | #![deny(let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^ help: consider binding to an unused variable to avoid immediately dropping the value | -LL | let _unused = field; - | ~~~~~~~~~~~ +LL - _ = field; +LL + let _unused = field; + | help: consider immediately dropping the value | -LL | drop(field); - | ~~~~~ + +LL - _ = field; +LL + drop(field); + | error: non-binding let on a type that has a destructor --> $DIR/issue-119697-extra-let.rs:17:5 @@ -27,11 +29,12 @@ LL | let _ = field; help: consider binding to an unused variable to avoid immediately dropping the value | LL | let _unused = field; - | ~~~~~~~ + | ++++++ help: consider immediately dropping the value | -LL | drop(field); - | ~~~~~ + +LL - let _ = field; +LL + drop(field); + | error: aborting due to 2 previous errors diff --git a/tests/ui/lint/let_underscore/let_underscore_drop.stderr b/tests/ui/lint/let_underscore/let_underscore_drop.stderr index 09f2587063bb9..001827b0d37f5 100644 --- a/tests/ui/lint/let_underscore/let_underscore_drop.stderr +++ b/tests/ui/lint/let_underscore/let_underscore_drop.stderr @@ -12,11 +12,12 @@ LL | #![warn(let_underscore_drop)] help: consider binding to an unused variable to avoid immediately dropping the value | LL | let _unused = NontrivialDrop; - | ~~~~~~~ + | ++++++ help: consider immediately dropping the value | -LL | drop(NontrivialDrop); - | ~~~~~ + +LL - let _ = NontrivialDrop; +LL + drop(NontrivialDrop); + | warning: 1 warning emitted diff --git a/tests/ui/lint/let_underscore/let_underscore_lock.stderr b/tests/ui/lint/let_underscore/let_underscore_lock.stderr index fb8b9ec220336..a54a23e364b3b 100644 --- a/tests/ui/lint/let_underscore/let_underscore_lock.stderr +++ b/tests/ui/lint/let_underscore/let_underscore_lock.stderr @@ -8,11 +8,12 @@ LL | let _ = data.lock().unwrap(); help: consider binding to an unused variable to avoid immediately dropping the value | LL | let _unused = data.lock().unwrap(); - | ~~~~~~~ + | ++++++ help: consider immediately dropping the value | -LL | drop(data.lock().unwrap()); - | ~~~~~ + +LL - let _ = data.lock().unwrap(); +LL + drop(data.lock().unwrap()); + | error: non-binding let on a synchronization lock --> $DIR/let_underscore_lock.rs:12:9 @@ -23,11 +24,12 @@ LL | let _ = data.lock(); help: consider binding to an unused variable to avoid immediately dropping the value | LL | let _unused = data.lock(); - | ~~~~~~~ + | ++++++ help: consider immediately dropping the value | -LL | drop(data.lock()); - | ~~~~~ + +LL - let _ = data.lock(); +LL + drop(data.lock()); + | error: non-binding let on a synchronization lock --> $DIR/let_underscore_lock.rs:14:10 @@ -39,7 +41,7 @@ LL | let (_, _) = (data.lock(), 1); help: consider binding to an unused variable to avoid immediately dropping the value | LL | let (_unused, _) = (data.lock(), 1); - | ~~~~~~~ + | ++++++ error: non-binding let on a synchronization lock --> $DIR/let_underscore_lock.rs:16:26 @@ -51,7 +53,7 @@ LL | let (_a, Struct { a: _ }) = (0, Struct { a: data.lock() }); help: consider binding to an unused variable to avoid immediately dropping the value | LL | let (_a, Struct { a: _unused }) = (0, Struct { a: data.lock() }); - | ~~~~~~~ + | ++++++ error: non-binding let on a synchronization lock --> $DIR/let_underscore_lock.rs:18:6 diff --git a/tests/ui/lint/linker-warning-bin.rs b/tests/ui/lint/linker-warning-bin.rs new file mode 100644 index 0000000000000..ead0aa002bf28 --- /dev/null +++ b/tests/ui/lint/linker-warning-bin.rs @@ -0,0 +1,6 @@ +//@ build-pass +#![crate_type = "bin"] +#![warn(unused_attributes)] +#![allow(linker_messages)] + +fn main() {} diff --git a/tests/ui/lint/linker-warning.rs b/tests/ui/lint/linker-warning.rs new file mode 100644 index 0000000000000..10e3f56ab9590 --- /dev/null +++ b/tests/ui/lint/linker-warning.rs @@ -0,0 +1,9 @@ +//@ check-pass +#![crate_type = "lib"] +#![warn(unused_attributes)] +#![allow(linker_messages)] +//~^ WARNING unused attribute + +#[allow(linker_messages)] +//~^ WARNING should be an inner attribute +fn foo() {} diff --git a/tests/ui/lint/linker-warning.stderr b/tests/ui/lint/linker-warning.stderr new file mode 100644 index 0000000000000..3a2c392fd0312 --- /dev/null +++ b/tests/ui/lint/linker-warning.stderr @@ -0,0 +1,22 @@ +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` + --> $DIR/linker-warning.rs:7:1 + | +LL | #[allow(linker_messages)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/linker-warning.rs:3:9 + | +LL | #![warn(unused_attributes)] + | ^^^^^^^^^^^^^^^^^ + +warning: unused attribute + --> $DIR/linker-warning.rs:4:1 + | +LL | #![allow(linker_messages)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | + = note: the `linker_warnings` lint can only be controlled at the root of a crate that needs to be linked + +warning: 2 warnings emitted + diff --git a/tests/ui/lint/lint-const-item-mutation.stderr b/tests/ui/lint/lint-const-item-mutation.stderr index 747c38b800764..0e405c306fe46 100644 --- a/tests/ui/lint/lint-const-item-mutation.stderr +++ b/tests/ui/lint/lint-const-item-mutation.stderr @@ -75,10 +75,15 @@ warning: taking a mutable reference to a `const` item --> $DIR/lint-const-item-mutation.rs:42:5 | LL | (&mut MY_STRUCT).use_mut(); - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: each usage of a `const` item creates a new temporary = note: the mutable reference will refer to this temporary, not the original `const` item +note: mutable reference created due to call to this method + --> $DIR/lint-const-item-mutation.rs:9:5 + | +LL | fn use_mut(&mut self) {} + | ^^^^^^^^^^^^^^^^^^^^^ note: `const` item defined here --> $DIR/lint-const-item-mutation.rs:27:1 | diff --git a/tests/ui/lint/lint-ctypes-enum.rs b/tests/ui/lint/lint-ctypes-enum.rs index 19af1de95760b..0d19d5b534713 100644 --- a/tests/ui/lint/lint-ctypes-enum.rs +++ b/tests/ui/lint/lint-ctypes-enum.rs @@ -94,6 +94,7 @@ extern "C" { fn option_transparent_union(x: Option>>); //~^ ERROR `extern` block uses type fn option_repr_rust(x: Option>>); //~ ERROR `extern` block uses type + fn option_u8(x: Option); //~ ERROR `extern` block uses type fn result_ref_t(x: Result<&'static u8, ()>); fn result_fn_t(x: Result); diff --git a/tests/ui/lint/lint-ctypes-enum.stderr b/tests/ui/lint/lint-ctypes-enum.stderr index 8e92e7e694627..a491bd1960563 100644 --- a/tests/ui/lint/lint-ctypes-enum.stderr +++ b/tests/ui/lint/lint-ctypes-enum.stderr @@ -79,8 +79,17 @@ LL | fn option_repr_rust(x: Option>>); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint +error: `extern` block uses type `Option`, which is not FFI-safe + --> $DIR/lint-ctypes-enum.rs:97:21 + | +LL | fn option_u8(x: Option); + | ^^^^^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum + = note: enum has no representation hint + error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:106:33 + --> $DIR/lint-ctypes-enum.rs:107:33 | LL | fn result_nonzero_u128_t(x: Result, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -88,7 +97,7 @@ LL | fn result_nonzero_u128_t(x: Result, ()>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:113:33 + --> $DIR/lint-ctypes-enum.rs:114:33 | LL | fn result_nonzero_i128_t(x: Result, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -96,7 +105,7 @@ LL | fn result_nonzero_i128_t(x: Result, ()>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `Result>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:118:38 + --> $DIR/lint-ctypes-enum.rs:119:38 | LL | fn result_transparent_union_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -105,7 +114,7 @@ LL | fn result_transparent_union_t(x: Result>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:120:30 + --> $DIR/lint-ctypes-enum.rs:121:30 | LL | fn result_repr_rust_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -114,7 +123,7 @@ LL | fn result_repr_rust_t(x: Result>, ()>); = note: enum has no representation hint error: `extern` block uses type `Result, U>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:124:51 + --> $DIR/lint-ctypes-enum.rs:125:51 | LL | fn result_1zst_exhaustive_single_variant_t(x: Result, U>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -123,7 +132,7 @@ LL | fn result_1zst_exhaustive_single_variant_t(x: Result, = note: enum has no representation hint error: `extern` block uses type `Result, B>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:126:53 + --> $DIR/lint-ctypes-enum.rs:127:53 | LL | fn result_1zst_exhaustive_multiple_variant_t(x: Result, B>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -132,7 +141,7 @@ LL | fn result_1zst_exhaustive_multiple_variant_t(x: Result = note: enum has no representation hint error: `extern` block uses type `Result, NonExhaustive>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:128:51 + --> $DIR/lint-ctypes-enum.rs:129:51 | LL | fn result_1zst_non_exhaustive_no_variant_t(x: Result, NonExhaustive>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -141,7 +150,7 @@ LL | fn result_1zst_non_exhaustive_no_variant_t(x: Result, = note: enum has no representation hint error: `extern` block uses type `Result, Field>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:131:49 + --> $DIR/lint-ctypes-enum.rs:132:49 | LL | fn result_1zst_exhaustive_single_field_t(x: Result, Field>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -150,7 +159,7 @@ LL | fn result_1zst_exhaustive_single_field_t(x: Result, Fi = note: enum has no representation hint error: `extern` block uses type `Result>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:133:30 + --> $DIR/lint-ctypes-enum.rs:134:30 | LL | fn result_cascading_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -159,7 +168,7 @@ LL | fn result_cascading_t(x: Result>, ()>); = note: enum has no representation hint error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:144:33 + --> $DIR/lint-ctypes-enum.rs:145:33 | LL | fn result_nonzero_u128_e(x: Result<(), num::NonZero>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -167,7 +176,7 @@ LL | fn result_nonzero_u128_e(x: Result<(), num::NonZero>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:151:33 + --> $DIR/lint-ctypes-enum.rs:152:33 | LL | fn result_nonzero_i128_e(x: Result<(), num::NonZero>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -175,7 +184,7 @@ LL | fn result_nonzero_i128_e(x: Result<(), num::NonZero>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `Result<(), TransparentUnion>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:156:38 + --> $DIR/lint-ctypes-enum.rs:157:38 | LL | fn result_transparent_union_e(x: Result<(), TransparentUnion>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -184,7 +193,7 @@ LL | fn result_transparent_union_e(x: Result<(), TransparentUnion>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:158:30 + --> $DIR/lint-ctypes-enum.rs:159:30 | LL | fn result_repr_rust_e(x: Result<(), Rust>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -193,7 +202,7 @@ LL | fn result_repr_rust_e(x: Result<(), Rust>>); = note: enum has no representation hint error: `extern` block uses type `Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:162:51 + --> $DIR/lint-ctypes-enum.rs:163:51 | LL | fn result_1zst_exhaustive_single_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -202,7 +211,7 @@ LL | fn result_1zst_exhaustive_single_variant_e(x: Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:164:53 + --> $DIR/lint-ctypes-enum.rs:165:53 | LL | fn result_1zst_exhaustive_multiple_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -211,7 +220,7 @@ LL | fn result_1zst_exhaustive_multiple_variant_e(x: Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:166:51 + --> $DIR/lint-ctypes-enum.rs:167:51 | LL | fn result_1zst_non_exhaustive_no_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -220,7 +229,7 @@ LL | fn result_1zst_non_exhaustive_no_variant_e(x: Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:169:49 + --> $DIR/lint-ctypes-enum.rs:170:49 | LL | fn result_1zst_exhaustive_single_field_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -229,7 +238,7 @@ LL | fn result_1zst_exhaustive_single_field_e(x: Result>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:171:30 + --> $DIR/lint-ctypes-enum.rs:172:30 | LL | fn result_cascading_e(x: Result<(), Result<(), num::NonZero>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -238,7 +247,7 @@ LL | fn result_cascading_e(x: Result<(), Result<(), num::NonZero>>); = note: enum has no representation hint error: `extern` block uses type `Result<(), ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:173:27 + --> $DIR/lint-ctypes-enum.rs:174:27 | LL | fn result_unit_t_e(x: Result<(), ()>); | ^^^^^^^^^^^^^^ not FFI-safe @@ -246,5 +255,5 @@ LL | fn result_unit_t_e(x: Result<(), ()>); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint -error: aborting due to 26 previous errors +error: aborting due to 27 previous errors diff --git a/tests/ui/lint/lint-ctypes.rs b/tests/ui/lint/lint-ctypes.rs index dae07930aba60..6dd9be10a48f8 100644 --- a/tests/ui/lint/lint-ctypes.rs +++ b/tests/ui/lint/lint-ctypes.rs @@ -72,7 +72,7 @@ extern "C" { pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: uses type `Box` pub fn raw_array(arr: [u8; 8]); //~ ERROR: uses type `[u8; 8]` - pub fn no_niche_a(a: Option>); + pub fn no_niche_a(a: Option>); //~^ ERROR: uses type `Option>` pub fn no_niche_b(b: Option>); //~^ ERROR: uses type `Option>` diff --git a/tests/ui/lint/lint-ctypes.stderr b/tests/ui/lint/lint-ctypes.stderr index 2c81c7b8e4b67..8137ae868d356 100644 --- a/tests/ui/lint/lint-ctypes.stderr +++ b/tests/ui/lint/lint-ctypes.stderr @@ -227,8 +227,8 @@ LL | pub fn raw_array(arr: [u8; 8]); error: `extern` block uses type `Option>`, which is not FFI-safe --> $DIR/lint-ctypes.rs:75:26 | -LL | pub fn no_niche_a(a: Option>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe +LL | pub fn no_niche_a(a: Option>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe | = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint diff --git a/tests/ui/lint/lint-double-negations.rs b/tests/ui/lint/lint-double-negations.rs new file mode 100644 index 0000000000000..43e61dd7c1327 --- /dev/null +++ b/tests/ui/lint/lint-double-negations.rs @@ -0,0 +1,9 @@ +//@ check-pass +fn main() { + let x = 1; + -x; + -(-x); + --x; //~ WARN use of a double negation + ---x; //~ WARN use of a double negation + let _y = --(-x); //~ WARN use of a double negation +} diff --git a/tests/ui/lint/lint-double-negations.stderr b/tests/ui/lint/lint-double-negations.stderr new file mode 100644 index 0000000000000..9367f74be546d --- /dev/null +++ b/tests/ui/lint/lint-double-negations.stderr @@ -0,0 +1,42 @@ +warning: use of a double negation + --> $DIR/lint-double-negations.rs:6:5 + | +LL | --x; + | ^^^ + | + = note: the prefix `--` could be misinterpreted as a decrement operator which exists in other languages + = note: use `-= 1` if you meant to decrement the value + = note: `#[warn(double_negations)]` on by default +help: add parentheses for clarity + | +LL | -(-x); + | + + + +warning: use of a double negation + --> $DIR/lint-double-negations.rs:7:6 + | +LL | ---x; + | ^^^ + | + = note: the prefix `--` could be misinterpreted as a decrement operator which exists in other languages + = note: use `-= 1` if you meant to decrement the value +help: add parentheses for clarity + | +LL | --(-x); + | + + + +warning: use of a double negation + --> $DIR/lint-double-negations.rs:8:14 + | +LL | let _y = --(-x); + | ^^^^^^ + | + = note: the prefix `--` could be misinterpreted as a decrement operator which exists in other languages + = note: use `-= 1` if you meant to decrement the value +help: add parentheses for clarity + | +LL | let _y = -(-(-x)); + | + + + +warning: 3 warnings emitted + diff --git a/tests/ui/lint/lint-overflowing-int-136675.rs b/tests/ui/lint/lint-overflowing-int-136675.rs new file mode 100644 index 0000000000000..616531519a62f --- /dev/null +++ b/tests/ui/lint/lint-overflowing-int-136675.rs @@ -0,0 +1,4 @@ +fn main() { + if let -129 = 0i8 {} //~ ERROR literal out of range for `i8` + let x: i8 = -129; //~ ERROR literal out of range for `i8` +} diff --git a/tests/ui/lint/lint-overflowing-int-136675.stderr b/tests/ui/lint/lint-overflowing-int-136675.stderr new file mode 100644 index 0000000000000..3b67c663ac2b7 --- /dev/null +++ b/tests/ui/lint/lint-overflowing-int-136675.stderr @@ -0,0 +1,21 @@ +error: literal out of range for `i8` + --> $DIR/lint-overflowing-int-136675.rs:2:12 + | +LL | if let -129 = 0i8 {} + | ^^^^ + | + = note: the literal `-129` does not fit into the type `i8` whose range is `-128..=127` + = help: consider using the type `i16` instead + = note: `#[deny(overflowing_literals)]` on by default + +error: literal out of range for `i8` + --> $DIR/lint-overflowing-int-136675.rs:3:17 + | +LL | let x: i8 = -129; + | ^^^^ + | + = note: the literal `-129` does not fit into the type `i8` whose range is `-128..=127` + = help: consider using the type `i16` instead + +error: aborting due to 2 previous errors + diff --git a/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr b/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr index 24f2500abf82c..f5eec6fc65667 100644 --- a/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr +++ b/tests/ui/lint/lint-strict-provenance-fuzzy-casts.stderr @@ -12,8 +12,9 @@ LL | #![deny(fuzzy_provenance_casts)] | ^^^^^^^^^^^^^^^^^^^^^^ help: use `.with_addr()` to adjust a valid pointer in the same allocation, to this address | -LL | let dangling = (...).with_addr(16_usize); - | ++++++++++++++++ ~ +LL - let dangling = 16_usize as *const u8; +LL + let dangling = (...).with_addr(16_usize); + | error: aborting due to 1 previous error diff --git a/tests/ui/lint/lint-strict-provenance-lossy-casts.stderr b/tests/ui/lint/lint-strict-provenance-lossy-casts.stderr index 390028b349e5f..bcef0ae424e68 100644 --- a/tests/ui/lint/lint-strict-provenance-lossy-casts.stderr +++ b/tests/ui/lint/lint-strict-provenance-lossy-casts.stderr @@ -12,8 +12,9 @@ LL | #![deny(lossy_provenance_casts)] | ^^^^^^^^^^^^^^^^^^^^^^ help: use `.addr()` to obtain the address of a pointer | -LL | let addr: usize = (&x as *const u8).addr(); - | + ~~~~~~~~ +LL - let addr: usize = &x as *const u8 as usize; +LL + let addr: usize = (&x as *const u8).addr(); + | error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `u32` --> $DIR/lint-strict-provenance-lossy-casts.rs:9:22 @@ -25,7 +26,7 @@ LL | let addr_32bit = &x as *const u8 as u32; help: use `.addr()` to obtain the address of a pointer | LL | let addr_32bit = (&x as *const u8).addr() as u32; - | + ~~~~~~~~~~~~~~~ + | + ++++++++ error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` --> $DIR/lint-strict-provenance-lossy-casts.rs:14:20 diff --git a/tests/ui/lint/lint-type-overflow2.rs b/tests/ui/lint/lint-type-overflow2.rs index f007b45b8479d..ac7420326c893 100644 --- a/tests/ui/lint/lint-type-overflow2.rs +++ b/tests/ui/lint/lint-type-overflow2.rs @@ -4,6 +4,7 @@ fn main() { let x2: i8 = --128; //~ ERROR literal out of range for `i8` + //~| WARN use of a double negation let x = -3.40282357e+38_f32; //~ ERROR literal out of range for `f32` let x = 3.40282357e+38_f32; //~ ERROR literal out of range for `f32` diff --git a/tests/ui/lint/lint-type-overflow2.stderr b/tests/ui/lint/lint-type-overflow2.stderr index eb593d062f218..2cfb18e9fe924 100644 --- a/tests/ui/lint/lint-type-overflow2.stderr +++ b/tests/ui/lint/lint-type-overflow2.stderr @@ -1,3 +1,17 @@ +warning: use of a double negation + --> $DIR/lint-type-overflow2.rs:6:18 + | +LL | let x2: i8 = --128; + | ^^^^^ + | + = note: the prefix `--` could be misinterpreted as a decrement operator which exists in other languages + = note: use `-= 1` if you meant to decrement the value + = note: `#[warn(double_negations)]` on by default +help: add parentheses for clarity + | +LL | let x2: i8 = -(-128); + | + + + error: literal out of range for `i8` --> $DIR/lint-type-overflow2.rs:6:20 | @@ -13,7 +27,7 @@ LL | #![deny(overflowing_literals)] | ^^^^^^^^^^^^^^^^^^^^ error: literal out of range for `f32` - --> $DIR/lint-type-overflow2.rs:8:14 + --> $DIR/lint-type-overflow2.rs:9:14 | LL | let x = -3.40282357e+38_f32; | ^^^^^^^^^^^^^^^^^^ @@ -21,7 +35,7 @@ LL | let x = -3.40282357e+38_f32; = note: the literal `3.40282357e+38_f32` does not fit into the type `f32` and will be converted to `f32::INFINITY` error: literal out of range for `f32` - --> $DIR/lint-type-overflow2.rs:9:14 + --> $DIR/lint-type-overflow2.rs:10:14 | LL | let x = 3.40282357e+38_f32; | ^^^^^^^^^^^^^^^^^^ @@ -29,7 +43,7 @@ LL | let x = 3.40282357e+38_f32; = note: the literal `3.40282357e+38_f32` does not fit into the type `f32` and will be converted to `f32::INFINITY` error: literal out of range for `f64` - --> $DIR/lint-type-overflow2.rs:10:14 + --> $DIR/lint-type-overflow2.rs:11:14 | LL | let x = -1.7976931348623159e+308_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -37,12 +51,12 @@ LL | let x = -1.7976931348623159e+308_f64; = note: the literal `1.7976931348623159e+308_f64` does not fit into the type `f64` and will be converted to `f64::INFINITY` error: literal out of range for `f64` - --> $DIR/lint-type-overflow2.rs:11:14 + --> $DIR/lint-type-overflow2.rs:12:14 | LL | let x = 1.7976931348623159e+308_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the literal `1.7976931348623159e+308_f64` does not fit into the type `f64` and will be converted to `f64::INFINITY` -error: aborting due to 5 previous errors +error: aborting due to 5 previous errors; 1 warning emitted diff --git a/tests/ui/lint/lint_map_unit_fn.stderr b/tests/ui/lint/lint_map_unit_fn.stderr index fbf689c54219e..91542af0f6df8 100644 --- a/tests/ui/lint/lint_map_unit_fn.stderr +++ b/tests/ui/lint/lint_map_unit_fn.stderr @@ -18,8 +18,9 @@ LL | #![deny(map_unit_fn)] | ^^^^^^^^^^^ help: you might have meant to use `Iterator::for_each` | -LL | x.iter_mut().for_each(foo); - | ~~~~~~~~ +LL - x.iter_mut().map(foo); +LL + x.iter_mut().for_each(foo); + | error: `Iterator::map` call that discard the iterator's values --> $DIR/lint_map_unit_fn.rs:11:18 @@ -41,8 +42,9 @@ LL | | | }); = note: `Iterator::map`, like many of the methods on `Iterator`, gets executed lazily, meaning that its effects won't be visible until it is iterated help: you might have meant to use `Iterator::for_each` | -LL | x.iter_mut().for_each(|items| { - | ~~~~~~~~ +LL - x.iter_mut().map(|items| { +LL + x.iter_mut().for_each(|items| { + | error: `Iterator::map` call that discard the iterator's values --> $DIR/lint_map_unit_fn.rs:18:18 @@ -59,8 +61,9 @@ LL | x.iter_mut().map(f); = note: `Iterator::map`, like many of the methods on `Iterator`, gets executed lazily, meaning that its effects won't be visible until it is iterated help: you might have meant to use `Iterator::for_each` | -LL | x.iter_mut().for_each(f); - | ~~~~~~~~ +LL - x.iter_mut().map(f); +LL + x.iter_mut().for_each(f); + | error: aborting due to 3 previous errors diff --git a/tests/ui/lint/non-snake-case/lint-non-snake-case-identifiers-suggestion-reserved.stderr b/tests/ui/lint/non-snake-case/lint-non-snake-case-identifiers-suggestion-reserved.stderr index 2841815ecf2b9..ae2a00d3f90a6 100644 --- a/tests/ui/lint/non-snake-case/lint-non-snake-case-identifiers-suggestion-reserved.stderr +++ b/tests/ui/lint/non-snake-case/lint-non-snake-case-identifiers-suggestion-reserved.stderr @@ -30,8 +30,9 @@ LL | #![deny(non_snake_case)] | ^^^^^^^^^^^^^^ help: rename the identifier or convert it to a snake case raw identifier | -LL | mod r#impl {} - | ~~~~~~ +LL - mod Impl {} +LL + mod r#impl {} + | error: function `While` should have a snake case name --> $DIR/lint-non-snake-case-identifiers-suggestion-reserved.rs:8:4 @@ -41,8 +42,9 @@ LL | fn While() {} | help: rename the identifier or convert it to a snake case raw identifier | -LL | fn r#while() {} - | ~~~~~~~ +LL - fn While() {} +LL + fn r#while() {} + | error: variable `Mod` should have a snake case name --> $DIR/lint-non-snake-case-identifiers-suggestion-reserved.rs:12:9 @@ -52,8 +54,9 @@ LL | let Mod: usize = 0; | help: rename the identifier or convert it to a snake case raw identifier | -LL | let r#mod: usize = 0; - | ~~~~~ +LL - let Mod: usize = 0; +LL + let r#mod: usize = 0; + | error: variable `Super` should have a snake case name --> $DIR/lint-non-snake-case-identifiers-suggestion-reserved.rs:16:9 diff --git a/tests/ui/lint/recommend-literal.stderr b/tests/ui/lint/recommend-literal.stderr index 424ecadd4b8cc..263071ca9a754 100644 --- a/tests/ui/lint/recommend-literal.stderr +++ b/tests/ui/lint/recommend-literal.stderr @@ -33,12 +33,14 @@ LL | let v2: Bool = true; | help: a builtin type with a similar name exists | -LL | let v2: bool = true; - | ~~~~ +LL - let v2: Bool = true; +LL + let v2: bool = true; + | help: perhaps you intended to use this type | -LL | let v2: bool = true; - | ~~~~ +LL - let v2: Bool = true; +LL + let v2: bool = true; + | error[E0412]: cannot find type `boolean` in this scope --> $DIR/recommend-literal.rs:19:9 @@ -75,8 +77,9 @@ LL | depth: Option, | help: perhaps you intended to use this type | -LL | depth: Option, - | ~~~ +LL - depth: Option, +LL + depth: Option, + | help: you might be missing a type parameter | LL | struct Data { diff --git a/tests/ui/lint/static-mut-refs.e2021.stderr b/tests/ui/lint/static-mut-refs.e2021.stderr index 5a4e712b3c0df..337d5d0b307ee 100644 --- a/tests/ui/lint/static-mut-refs.e2021.stderr +++ b/tests/ui/lint/static-mut-refs.e2021.stderr @@ -10,7 +10,7 @@ LL | let _y = &X; help: use `&raw const` instead to create a raw pointer | LL | let _y = &raw const X; - | ~~~~~~~~~~ + | +++++++++ warning: creating a mutable reference to mutable static is discouraged --> $DIR/static-mut-refs.rs:42:18 @@ -22,8 +22,9 @@ LL | let _y = &mut X; = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives help: use `&raw mut` instead to create a raw pointer | -LL | let _y = &raw mut X; - | ~~~~~~~~ +LL - let _y = &mut X; +LL + let _y = &raw mut X; + | warning: creating a shared reference to mutable static is discouraged --> $DIR/static-mut-refs.rs:50:22 @@ -45,7 +46,7 @@ LL | let (_b, _c) = (&X, &Y); help: use `&raw const` instead to create a raw pointer | LL | let (_b, _c) = (&raw const X, &Y); - | ~~~~~~~~~~ + | +++++++++ warning: creating a shared reference to mutable static is discouraged --> $DIR/static-mut-refs.rs:54:29 @@ -58,7 +59,7 @@ LL | let (_b, _c) = (&X, &Y); help: use `&raw const` instead to create a raw pointer | LL | let (_b, _c) = (&X, &raw const Y); - | ~~~~~~~~~~ + | +++++++++ warning: creating a shared reference to mutable static is discouraged --> $DIR/static-mut-refs.rs:60:13 @@ -71,7 +72,7 @@ LL | foo(&X); help: use `&raw const` instead to create a raw pointer | LL | foo(&raw const X); - | ~~~~~~~~~~ + | +++++++++ warning: creating a shared reference to mutable static is discouraged --> $DIR/static-mut-refs.rs:66:17 @@ -102,7 +103,7 @@ LL | let _v = &A.value; help: use `&raw const` instead to create a raw pointer | LL | let _v = &raw const A.value; - | ~~~~~~~~~~ + | +++++++++ warning: creating a shared reference to mutable static is discouraged --> $DIR/static-mut-refs.rs:80:18 @@ -115,7 +116,7 @@ LL | let _s = &A.s.value; help: use `&raw const` instead to create a raw pointer | LL | let _s = &raw const A.s.value; - | ~~~~~~~~~~ + | +++++++++ warning: creating a shared reference to mutable static is discouraged --> $DIR/static-mut-refs.rs:84:22 diff --git a/tests/ui/lint/static-mut-refs.e2024.stderr b/tests/ui/lint/static-mut-refs.e2024.stderr index 1b549272bd5f5..cf7f0a86f4fe7 100644 --- a/tests/ui/lint/static-mut-refs.e2024.stderr +++ b/tests/ui/lint/static-mut-refs.e2024.stderr @@ -10,7 +10,7 @@ LL | let _y = &X; help: use `&raw const` instead to create a raw pointer | LL | let _y = &raw const X; - | ~~~~~~~~~~ + | +++++++++ error: creating a mutable reference to mutable static is discouraged --> $DIR/static-mut-refs.rs:42:18 @@ -22,8 +22,9 @@ LL | let _y = &mut X; = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives help: use `&raw mut` instead to create a raw pointer | -LL | let _y = &raw mut X; - | ~~~~~~~~ +LL - let _y = &mut X; +LL + let _y = &raw mut X; + | error: creating a shared reference to mutable static is discouraged --> $DIR/static-mut-refs.rs:50:22 @@ -45,7 +46,7 @@ LL | let (_b, _c) = (&X, &Y); help: use `&raw const` instead to create a raw pointer | LL | let (_b, _c) = (&raw const X, &Y); - | ~~~~~~~~~~ + | +++++++++ error: creating a shared reference to mutable static is discouraged --> $DIR/static-mut-refs.rs:54:29 @@ -58,7 +59,7 @@ LL | let (_b, _c) = (&X, &Y); help: use `&raw const` instead to create a raw pointer | LL | let (_b, _c) = (&X, &raw const Y); - | ~~~~~~~~~~ + | +++++++++ error: creating a shared reference to mutable static is discouraged --> $DIR/static-mut-refs.rs:60:13 @@ -71,7 +72,7 @@ LL | foo(&X); help: use `&raw const` instead to create a raw pointer | LL | foo(&raw const X); - | ~~~~~~~~~~ + | +++++++++ error: creating a shared reference to mutable static is discouraged --> $DIR/static-mut-refs.rs:66:17 @@ -102,7 +103,7 @@ LL | let _v = &A.value; help: use `&raw const` instead to create a raw pointer | LL | let _v = &raw const A.value; - | ~~~~~~~~~~ + | +++++++++ error: creating a shared reference to mutable static is discouraged --> $DIR/static-mut-refs.rs:80:18 @@ -115,7 +116,7 @@ LL | let _s = &A.s.value; help: use `&raw const` instead to create a raw pointer | LL | let _s = &raw const A.s.value; - | ~~~~~~~~~~ + | +++++++++ error: creating a shared reference to mutable static is discouraged --> $DIR/static-mut-refs.rs:84:22 diff --git a/tests/ui/lint/type-overflow.rs b/tests/ui/lint/type-overflow.rs index 7239e1c983764..1e74a8925f621 100644 --- a/tests/ui/lint/type-overflow.rs +++ b/tests/ui/lint/type-overflow.rs @@ -3,20 +3,46 @@ fn main() { let error = 255i8; //~WARNING literal out of range for `i8` + //~^ HELP consider using the type `u8` instead let ok = 0b1000_0001; // should be ok -> i32 let ok = 0b0111_1111i8; // should be ok -> 127i8 let fail = 0b1000_0001i8; //~WARNING literal out of range for `i8` + //~^ HELP consider using the type `u8` instead + //~| HELP consider using the type `u8` for the literal and cast it to `i8` let fail = 0x8000_0000_0000_0000i64; //~WARNING literal out of range for `i64` + //~^ HELP consider using the type `u64` instead + //~| HELP consider using the type `u64` for the literal and cast it to `i64` let fail = 0x1_FFFF_FFFFu32; //~WARNING literal out of range for `u32` + //~^ HELP consider using the type `u64` instead let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; //~^ WARNING literal out of range for `i128` + //~| HELP consider using the type `u128` instead + //~| HELP consider using the type `u128` for the literal and cast it to `i128` + + let fail = 0x8000_0000_0000_0000_0000_0000_0000_0000; + //~^ WARNING literal out of range for `i32` + //~| HELP consider using the type `u128` instead + + let fail = -0x8000_0000_0000_0000_0000_0000_0000_0000; // issue #131849 + //~^ WARNING literal out of range for `i32` + //~| HELP consider using the type `i128` instead + + let fail = -0x8000_0000_0000_0000_0000_0000_0000_0001i128; + //~^ WARNING literal out of range for `i128` + + let fail = 340282366920938463463374607431768211455i8; + //~^ WARNING literal out of range for `i8` + //~| HELP consider using the type `u128` instead let fail = 0x8FFF_FFFF_FFFF_FFFE; //~WARNING literal out of range for `i32` + //~| HELP consider using the type `u64` instead + //~| HELP let fail = -0b1111_1111i8; //~WARNING literal out of range for `i8` + //~| HELP consider using the type `i16` instead } diff --git a/tests/ui/lint/type-overflow.stderr b/tests/ui/lint/type-overflow.stderr index e7c90dcc81bb2..6ba0c9d907c5e 100644 --- a/tests/ui/lint/type-overflow.stderr +++ b/tests/ui/lint/type-overflow.stderr @@ -13,7 +13,7 @@ LL | #![warn(overflowing_literals)] | ^^^^^^^^^^^^^^^^^^^^ warning: literal out of range for `i8` - --> $DIR/type-overflow.rs:10:16 + --> $DIR/type-overflow.rs:11:16 | LL | let fail = 0b1000_0001i8; | ^^^^^^^^^^^^^ @@ -21,15 +21,17 @@ LL | let fail = 0b1000_0001i8; = note: the literal `0b1000_0001i8` (decimal `129`) does not fit into the type `i8` and will become `-127i8` help: consider using the type `u8` instead | -LL | let fail = 0b1000_0001u8; - | ~~~~~~~~~~~~~ +LL - let fail = 0b1000_0001i8; +LL + let fail = 0b1000_0001u8; + | help: to use as a negative number (decimal `-127`), consider using the type `u8` for the literal and cast it to `i8` | -LL | let fail = 0b1000_0001u8 as i8; - | ~~~~~~~~~~~~~~~~~~~ +LL - let fail = 0b1000_0001i8; +LL + let fail = 0b1000_0001u8 as i8; + | warning: literal out of range for `i64` - --> $DIR/type-overflow.rs:12:16 + --> $DIR/type-overflow.rs:15:16 | LL | let fail = 0x8000_0000_0000_0000i64; | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -37,15 +39,17 @@ LL | let fail = 0x8000_0000_0000_0000i64; = note: the literal `0x8000_0000_0000_0000i64` (decimal `9223372036854775808`) does not fit into the type `i64` and will become `-9223372036854775808i64` help: consider using the type `u64` instead | -LL | let fail = 0x8000_0000_0000_0000u64; - | ~~~~~~~~~~~~~~~~~~~~~~~~ +LL - let fail = 0x8000_0000_0000_0000i64; +LL + let fail = 0x8000_0000_0000_0000u64; + | help: to use as a negative number (decimal `-9223372036854775808`), consider using the type `u64` for the literal and cast it to `i64` | -LL | let fail = 0x8000_0000_0000_0000u64 as i64; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - let fail = 0x8000_0000_0000_0000i64; +LL + let fail = 0x8000_0000_0000_0000u64 as i64; + | warning: literal out of range for `u32` - --> $DIR/type-overflow.rs:14:16 + --> $DIR/type-overflow.rs:19:16 | LL | let fail = 0x1_FFFF_FFFFu32; | ^^^^^^^^^^^^^^^^ help: consider using the type `u64` instead: `0x1_FFFF_FFFFu64` @@ -53,7 +57,7 @@ LL | let fail = 0x1_FFFF_FFFFu32; = note: the literal `0x1_FFFF_FFFFu32` (decimal `8589934591`) does not fit into the type `u32` and will become `4294967295u32` warning: literal out of range for `i128` - --> $DIR/type-overflow.rs:16:22 + --> $DIR/type-overflow.rs:22:22 | LL | let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -63,23 +67,60 @@ LL | let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; help: to use as a negative number (decimal `-170141183460469231731687303715884105728`), consider using the type `u128` for the literal and cast it to `i128` | LL | let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000u128 as i128; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | ++++++++++++ warning: literal out of range for `i32` - --> $DIR/type-overflow.rs:19:16 + --> $DIR/type-overflow.rs:27:16 + | +LL | let fail = 0x8000_0000_0000_0000_0000_0000_0000_0000; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the literal `0x8000_0000_0000_0000_0000_0000_0000_0000` (decimal `170141183460469231731687303715884105728`) does not fit into the type `i32` and will become `0i32` + = help: consider using the type `u128` instead + +warning: literal out of range for `i32` + --> $DIR/type-overflow.rs:31:17 + | +LL | let fail = -0x8000_0000_0000_0000_0000_0000_0000_0000; // issue #131849 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the literal `0x8000_0000_0000_0000_0000_0000_0000_0000` (decimal `170141183460469231731687303715884105728`) does not fit into the type `i32` + = note: and the value `-0x8000_0000_0000_0000_0000_0000_0000_0000` will become `0i32` + = help: consider using the type `i128` instead + +warning: literal out of range for `i128` + --> $DIR/type-overflow.rs:35:17 + | +LL | let fail = -0x8000_0000_0000_0000_0000_0000_0000_0001i128; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the literal `0x8000_0000_0000_0000_0000_0000_0000_0001i128` (decimal `170141183460469231731687303715884105729`) does not fit into the type `i128` + = note: and the value `-0x8000_0000_0000_0000_0000_0000_0000_0001i128` will become `170141183460469231731687303715884105727i128` + +warning: literal out of range for `i8` + --> $DIR/type-overflow.rs:38:16 + | +LL | let fail = 340282366920938463463374607431768211455i8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the literal `340282366920938463463374607431768211455i8` does not fit into the type `i8` whose range is `-128..=127` + = help: consider using the type `u128` instead + +warning: literal out of range for `i32` + --> $DIR/type-overflow.rs:42:16 | LL | let fail = 0x8FFF_FFFF_FFFF_FFFE; | ^^^^^^^^^^^^^^^^^^^^^ | = note: the literal `0x8FFF_FFFF_FFFF_FFFE` (decimal `10376293541461622782`) does not fit into the type `i32` and will become `-2i32` - = help: consider using the type `i128` instead + = help: consider using the type `u64` instead help: to use as a negative number (decimal `-2`), consider using the type `u32` for the literal and cast it to `i32` | LL | let fail = 0x8FFF_FFFF_FFFF_FFFEu32 as i32; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | ++++++++++ warning: literal out of range for `i8` - --> $DIR/type-overflow.rs:21:17 + --> $DIR/type-overflow.rs:46:17 | LL | let fail = -0b1111_1111i8; | ^^^^^^^^^^^^^ help: consider using the type `i16` instead: `0b1111_1111i16` @@ -87,5 +128,5 @@ LL | let fail = -0b1111_1111i8; = note: the literal `0b1111_1111i8` (decimal `255`) does not fit into the type `i8` = note: and the value `-0b1111_1111i8` will become `1i8` -warning: 7 warnings emitted +warning: 11 warnings emitted diff --git a/tests/ui/lint/unused/issue-67691-unused-field-in-or-pattern.stderr b/tests/ui/lint/unused/issue-67691-unused-field-in-or-pattern.stderr index 8fc2d1bc88fdd..2d43e61258069 100644 --- a/tests/ui/lint/unused/issue-67691-unused-field-in-or-pattern.stderr +++ b/tests/ui/lint/unused/issue-67691-unused-field-in-or-pattern.stderr @@ -13,7 +13,7 @@ LL | #![deny(unused)] help: try ignoring the field | LL | A { i, j: _ } | B { i, j: _ } => { - | ~~~~ ~~~~ + | +++ +++ error: unused variable: `j` --> $DIR/issue-67691-unused-field-in-or-pattern.rs:30:16 @@ -23,8 +23,9 @@ LL | A { i, ref j } | B { i, ref j } => { | help: try ignoring the field | -LL | A { i, j: _ } | B { i, j: _ } => { - | ~~~~ ~~~~ +LL - A { i, ref j } | B { i, ref j } => { +LL + A { i, j: _ } | B { i, j: _ } => { + | error: unused variable: `j` --> $DIR/issue-67691-unused-field-in-or-pattern.rs:40:21 @@ -35,7 +36,7 @@ LL | Some(A { i, j } | B { i, j }) => { help: try ignoring the field | LL | Some(A { i, j: _ } | B { i, j: _ }) => { - | ~~~~ ~~~~ + | +++ +++ error: unused variable: `j` --> $DIR/issue-67691-unused-field-in-or-pattern.rs:52:21 @@ -45,8 +46,9 @@ LL | Some(A { i, ref j } | B { i, ref j }) => { | help: try ignoring the field | -LL | Some(A { i, j: _ } | B { i, j: _ }) => { - | ~~~~ ~~~~ +LL - Some(A { i, ref j } | B { i, ref j }) => { +LL + Some(A { i, j: _ } | B { i, j: _ }) => { + | error: unused variable: `i` --> $DIR/issue-67691-unused-field-in-or-pattern.rs:62:24 @@ -56,8 +58,9 @@ LL | MixedEnum::A { i } | MixedEnum::B(i) => { | help: try ignoring the field | -LL | MixedEnum::A { i: _ } | MixedEnum::B(_) => { - | ~~~~ ~ +LL - MixedEnum::A { i } | MixedEnum::B(i) => { +LL + MixedEnum::A { i: _ } | MixedEnum::B(_) => { + | error: unused variable: `i` --> $DIR/issue-67691-unused-field-in-or-pattern.rs:70:24 @@ -67,8 +70,9 @@ LL | MixedEnum::A { ref i } | MixedEnum::B(ref i) => { | help: try ignoring the field | -LL | MixedEnum::A { i: _ } | MixedEnum::B(_) => { - | ~~~~ ~ +LL - MixedEnum::A { ref i } | MixedEnum::B(ref i) => { +LL + MixedEnum::A { i: _ } | MixedEnum::B(_) => { + | error: aborting due to 6 previous errors diff --git a/tests/ui/lint/unused/unused-allocation.rs b/tests/ui/lint/unused/unused-allocation.rs index c1a6f5ceaf178..1d5727362ea64 100644 --- a/tests/ui/lint/unused/unused-allocation.rs +++ b/tests/ui/lint/unused/unused-allocation.rs @@ -1,7 +1,5 @@ -#![feature(rustc_attrs, stmt_expr_attributes)] #![deny(unused_allocation)] fn main() { - _ = (#[rustc_box] Box::new([1])).len(); //~ error: unnecessary allocation, use `&` instead _ = Box::new([1]).len(); //~ error: unnecessary allocation, use `&` instead } diff --git a/tests/ui/lint/unused/unused-allocation.stderr b/tests/ui/lint/unused/unused-allocation.stderr index c9ccfbd30e5d4..4487395e9083f 100644 --- a/tests/ui/lint/unused/unused-allocation.stderr +++ b/tests/ui/lint/unused/unused-allocation.stderr @@ -1,20 +1,14 @@ error: unnecessary allocation, use `&` instead - --> $DIR/unused-allocation.rs:5:9 + --> $DIR/unused-allocation.rs:4:9 | -LL | _ = (#[rustc_box] Box::new([1])).len(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | _ = Box::new([1]).len(); + | ^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/unused-allocation.rs:2:9 + --> $DIR/unused-allocation.rs:1:9 | LL | #![deny(unused_allocation)] | ^^^^^^^^^^^^^^^^^ -error: unnecessary allocation, use `&` instead - --> $DIR/unused-allocation.rs:6:9 - | -LL | _ = Box::new([1]).len(); - | ^^^^^^^^^^^^^ - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/lint/wide_pointer_comparisons.stderr b/tests/ui/lint/wide_pointer_comparisons.stderr index 7fe382393d7e1..5a0b914d8320e 100644 --- a/tests/ui/lint/wide_pointer_comparisons.stderr +++ b/tests/ui/lint/wide_pointer_comparisons.stderr @@ -7,8 +7,9 @@ LL | let _ = a == b; = note: `#[warn(ambiguous_wide_pointer_comparisons)]` on by default help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = std::ptr::addr_eq(a, b); - | ++++++++++++++++++ ~ + +LL - let _ = a == b; +LL + let _ = std::ptr::addr_eq(a, b); + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:22:13 @@ -18,8 +19,9 @@ LL | let _ = a != b; | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = !std::ptr::addr_eq(a, b); - | +++++++++++++++++++ ~ + +LL - let _ = a != b; +LL + let _ = !std::ptr::addr_eq(a, b); + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:24:13 @@ -73,8 +75,9 @@ LL | let _ = PartialEq::eq(&a, &b); | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = std::ptr::addr_eq(a, b); - | ~~~~~~~~~~~~~~~~~~ ~ +LL - let _ = PartialEq::eq(&a, &b); +LL + let _ = std::ptr::addr_eq(a, b); + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:35:13 @@ -84,8 +87,9 @@ LL | let _ = PartialEq::ne(&a, &b); | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = !std::ptr::addr_eq(a, b); - | ~~~~~~~~~~~~~~~~~~~ ~ +LL - let _ = PartialEq::ne(&a, &b); +LL + let _ = !std::ptr::addr_eq(a, b); + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:37:13 @@ -95,8 +99,9 @@ LL | let _ = a.eq(&b); | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = std::ptr::addr_eq(a, b); - | ++++++++++++++++++ ~ +LL - let _ = a.eq(&b); +LL + let _ = std::ptr::addr_eq(a, b); + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:39:13 @@ -106,8 +111,9 @@ LL | let _ = a.ne(&b); | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = !std::ptr::addr_eq(a, b); - | +++++++++++++++++++ ~ +LL - let _ = a.ne(&b); +LL + let _ = !std::ptr::addr_eq(a, b); + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:41:13 @@ -183,8 +189,9 @@ LL | let _ = a == b; | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = std::ptr::addr_eq(a.as_ptr(), b.as_ptr()); - | ++++++++++++++++++ ~~~~~~~~~~ ++++++++++ +LL - let _ = a == b; +LL + let _ = std::ptr::addr_eq(a.as_ptr(), b.as_ptr()); + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:59:17 @@ -205,8 +212,9 @@ LL | let _ = &a == &b; | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = std::ptr::addr_eq(a.as_ptr(), b.as_ptr()); - | ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~ ++++++++++ +LL - let _ = &a == &b; +LL + let _ = std::ptr::addr_eq(a.as_ptr(), b.as_ptr()); + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:70:17 @@ -216,8 +224,9 @@ LL | let _ = a == b; | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = std::ptr::addr_eq(*a, *b); - | +++++++++++++++++++ ~~~ + +LL - let _ = a == b; +LL + let _ = std::ptr::addr_eq(*a, *b); + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:72:17 @@ -227,8 +236,9 @@ LL | let _ = a != b; | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = !std::ptr::addr_eq(*a, *b); - | ++++++++++++++++++++ ~~~ + +LL - let _ = a != b; +LL + let _ = !std::ptr::addr_eq(*a, *b); + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:74:17 @@ -282,8 +292,9 @@ LL | let _ = PartialEq::eq(a, b); | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = std::ptr::addr_eq(*a, *b); - | ~~~~~~~~~~~~~~~~~~~ ~~~ +LL - let _ = PartialEq::eq(a, b); +LL + let _ = std::ptr::addr_eq(*a, *b); + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:85:17 @@ -293,8 +304,9 @@ LL | let _ = PartialEq::ne(a, b); | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = !std::ptr::addr_eq(*a, *b); - | ~~~~~~~~~~~~~~~~~~~~ ~~~ +LL - let _ = PartialEq::ne(a, b); +LL + let _ = !std::ptr::addr_eq(*a, *b); + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:87:17 @@ -304,8 +316,9 @@ LL | let _ = PartialEq::eq(&a, &b); | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = std::ptr::addr_eq(*a, *b); - | ~~~~~~~~~~~~~~~~~~~ ~~~ +LL - let _ = PartialEq::eq(&a, &b); +LL + let _ = std::ptr::addr_eq(*a, *b); + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:89:17 @@ -315,8 +328,9 @@ LL | let _ = PartialEq::ne(&a, &b); | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = !std::ptr::addr_eq(*a, *b); - | ~~~~~~~~~~~~~~~~~~~~ ~~~ +LL - let _ = PartialEq::ne(&a, &b); +LL + let _ = !std::ptr::addr_eq(*a, *b); + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:91:17 @@ -326,8 +340,9 @@ LL | let _ = a.eq(b); | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = std::ptr::addr_eq(*a, *b); - | +++++++++++++++++++ ~~~ +LL - let _ = a.eq(b); +LL + let _ = std::ptr::addr_eq(*a, *b); + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:93:17 @@ -337,8 +352,9 @@ LL | let _ = a.ne(b); | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = !std::ptr::addr_eq(*a, *b); - | ++++++++++++++++++++ ~~~ +LL - let _ = a.ne(b); +LL + let _ = !std::ptr::addr_eq(*a, *b); + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:95:17 @@ -414,12 +430,14 @@ LL | let _ = s == s; | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = std::ptr::addr_eq(s, s); - | ++++++++++++++++++ ~ + +LL - let _ = s == s; +LL + let _ = std::ptr::addr_eq(s, s); + | help: use explicit `std::ptr::eq` method to compare metadata and addresses | -LL | let _ = std::ptr::eq(s, s); - | +++++++++++++ ~ + +LL - let _ = s == s; +LL + let _ = std::ptr::eq(s, s); + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:114:13 @@ -429,12 +447,14 @@ LL | let _ = s == s; | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = std::ptr::addr_eq(s, s); - | ++++++++++++++++++ ~ + +LL - let _ = s == s; +LL + let _ = std::ptr::addr_eq(s, s); + | help: use explicit `std::ptr::eq` method to compare metadata and addresses | -LL | let _ = std::ptr::eq(s, s); - | +++++++++++++ ~ + +LL - let _ = s == s; +LL + let _ = std::ptr::eq(s, s); + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:118:17 @@ -444,12 +464,14 @@ LL | let _ = a == b; | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = std::ptr::addr_eq(a, b); - | ++++++++++++++++++ ~ + +LL - let _ = a == b; +LL + let _ = std::ptr::addr_eq(a, b); + | help: use explicit `std::ptr::eq` method to compare metadata and addresses | -LL | let _ = std::ptr::eq(a, b); - | +++++++++++++ ~ + +LL - let _ = a == b; +LL + let _ = std::ptr::eq(a, b); + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:120:17 @@ -459,12 +481,14 @@ LL | let _ = a != b; | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = !std::ptr::addr_eq(a, b); - | +++++++++++++++++++ ~ + +LL - let _ = a != b; +LL + let _ = !std::ptr::addr_eq(a, b); + | help: use explicit `std::ptr::eq` method to compare metadata and addresses | -LL | let _ = !std::ptr::eq(a, b); - | ++++++++++++++ ~ + +LL - let _ = a != b; +LL + let _ = !std::ptr::eq(a, b); + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:122:17 @@ -518,12 +542,14 @@ LL | let _ = PartialEq::eq(&a, &b); | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = std::ptr::addr_eq(a, b); - | ~~~~~~~~~~~~~~~~~~ ~ +LL - let _ = PartialEq::eq(&a, &b); +LL + let _ = std::ptr::addr_eq(a, b); + | help: use explicit `std::ptr::eq` method to compare metadata and addresses | -LL | let _ = std::ptr::eq(a, b); - | ~~~~~~~~~~~~~ ~ +LL - let _ = PartialEq::eq(&a, &b); +LL + let _ = std::ptr::eq(a, b); + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:133:17 @@ -533,12 +559,14 @@ LL | let _ = PartialEq::ne(&a, &b); | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = !std::ptr::addr_eq(a, b); - | ~~~~~~~~~~~~~~~~~~~ ~ +LL - let _ = PartialEq::ne(&a, &b); +LL + let _ = !std::ptr::addr_eq(a, b); + | help: use explicit `std::ptr::eq` method to compare metadata and addresses | -LL | let _ = !std::ptr::eq(a, b); - | ~~~~~~~~~~~~~~ ~ +LL - let _ = PartialEq::ne(&a, &b); +LL + let _ = !std::ptr::eq(a, b); + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:135:17 @@ -548,12 +576,14 @@ LL | let _ = a.eq(&b); | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = std::ptr::addr_eq(a, b); - | ++++++++++++++++++ ~ +LL - let _ = a.eq(&b); +LL + let _ = std::ptr::addr_eq(a, b); + | help: use explicit `std::ptr::eq` method to compare metadata and addresses | -LL | let _ = std::ptr::eq(a, b); - | +++++++++++++ ~ +LL - let _ = a.eq(&b); +LL + let _ = std::ptr::eq(a, b); + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:137:17 @@ -563,12 +593,14 @@ LL | let _ = a.ne(&b); | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | let _ = !std::ptr::addr_eq(a, b); - | +++++++++++++++++++ ~ +LL - let _ = a.ne(&b); +LL + let _ = !std::ptr::addr_eq(a, b); + | help: use explicit `std::ptr::eq` method to compare metadata and addresses | -LL | let _ = !std::ptr::eq(a, b); - | ++++++++++++++ ~ +LL - let _ = a.ne(&b); +LL + let _ = !std::ptr::eq(a, b); + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:142:9 @@ -578,12 +610,14 @@ LL | &*a == &*b | help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | std::ptr::addr_eq(*a, *b) - | ~~~~~~~~~~~~~~~~~~ ~ + +LL - &*a == &*b +LL + std::ptr::addr_eq(*a, *b) + | help: use explicit `std::ptr::eq` method to compare metadata and addresses | -LL | std::ptr::eq(*a, *b) - | ~~~~~~~~~~~~~ ~ + +LL - &*a == &*b +LL + std::ptr::eq(*a, *b) + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:153:14 @@ -608,14 +642,15 @@ LL | cmp!(a, b); = note: this warning originates in the macro `cmp` (in Nightly builds, run with -Z macro-backtrace for more info) help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | -LL | ($a:ident, $b:ident) => { std::ptr::addr_eq($a, $b) } - | ++++++++++++++++++ ~ + +LL - ($a:ident, $b:ident) => { $a == $b } +LL + ($a:ident, $b:ident) => { std::ptr::addr_eq($a, $b) } + | warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:169:37 | LL | ($a:expr, $b:expr) => { $a == $b } - | ^^ + | ^^^^^^^^ ... LL | cmp!(&a, &b); | ------------ in this macro invocation diff --git a/tests/ui/liveness/liveness-unused.rs b/tests/ui/liveness/liveness-unused.rs index ba635e6638c8c..49e7044aeda1f 100644 --- a/tests/ui/liveness/liveness-unused.rs +++ b/tests/ui/liveness/liveness-unused.rs @@ -2,6 +2,7 @@ #![deny(unused_variables)] #![deny(unused_assignments)] #![allow(dead_code, non_camel_case_types, trivial_numeric_casts, dropping_copy_types)] +#![feature(intrinsics)] use std::ops::AddAssign; @@ -137,5 +138,10 @@ fn f7() { drop(a); } +// unused params warnings are not needed for intrinsic functions without bodies +#[rustc_intrinsic] +unsafe fn simd_shuffle(a: T, b: T, i: I) -> U; + + fn main() { } diff --git a/tests/ui/liveness/liveness-unused.stderr b/tests/ui/liveness/liveness-unused.stderr index f6c478ddbc72c..a69fc10dff271 100644 --- a/tests/ui/liveness/liveness-unused.stderr +++ b/tests/ui/liveness/liveness-unused.stderr @@ -1,5 +1,5 @@ warning: unreachable statement - --> $DIR/liveness-unused.rs:92:9 + --> $DIR/liveness-unused.rs:93:9 | LL | continue; | -------- any code following this expression is unreachable @@ -14,7 +14,7 @@ LL | #![warn(unused)] = note: `#[warn(unreachable_code)]` implied by `#[warn(unused)]` error: unused variable: `x` - --> $DIR/liveness-unused.rs:8:7 + --> $DIR/liveness-unused.rs:9:7 | LL | fn f1(x: isize) { | ^ help: if this is intentional, prefix it with an underscore: `_x` @@ -26,25 +26,25 @@ LL | #![deny(unused_variables)] | ^^^^^^^^^^^^^^^^ error: unused variable: `x` - --> $DIR/liveness-unused.rs:12:8 + --> $DIR/liveness-unused.rs:13:8 | LL | fn f1b(x: &mut isize) { | ^ help: if this is intentional, prefix it with an underscore: `_x` error: unused variable: `x` - --> $DIR/liveness-unused.rs:20:9 + --> $DIR/liveness-unused.rs:21:9 | LL | let x: isize; | ^ help: if this is intentional, prefix it with an underscore: `_x` error: unused variable: `x` - --> $DIR/liveness-unused.rs:25:9 + --> $DIR/liveness-unused.rs:26:9 | LL | let x = 3; | ^ help: if this is intentional, prefix it with an underscore: `_x` error: variable `x` is assigned to, but never used - --> $DIR/liveness-unused.rs:30:13 + --> $DIR/liveness-unused.rs:31:13 | LL | let mut x = 3; | ^ @@ -52,7 +52,7 @@ LL | let mut x = 3; = note: consider using `_x` instead error: value assigned to `x` is never read - --> $DIR/liveness-unused.rs:32:5 + --> $DIR/liveness-unused.rs:33:5 | LL | x += 4; | ^ @@ -65,7 +65,7 @@ LL | #![deny(unused_assignments)] | ^^^^^^^^^^^^^^^^^^ error: variable `z` is assigned to, but never used - --> $DIR/liveness-unused.rs:37:13 + --> $DIR/liveness-unused.rs:38:13 | LL | let mut z = 3; | ^ @@ -73,31 +73,31 @@ LL | let mut z = 3; = note: consider using `_z` instead error: unused variable: `i` - --> $DIR/liveness-unused.rs:59:12 + --> $DIR/liveness-unused.rs:60:12 | LL | Some(i) => { | ^ help: if this is intentional, prefix it with an underscore: `_i` error: unused variable: `x` - --> $DIR/liveness-unused.rs:79:9 + --> $DIR/liveness-unused.rs:80:9 | LL | for x in 1..10 { } | ^ help: if this is intentional, prefix it with an underscore: `_x` error: unused variable: `x` - --> $DIR/liveness-unused.rs:84:10 + --> $DIR/liveness-unused.rs:85:10 | LL | for (x, _) in [1, 2, 3].iter().enumerate() { } | ^ help: if this is intentional, prefix it with an underscore: `_x` error: unused variable: `x` - --> $DIR/liveness-unused.rs:89:13 + --> $DIR/liveness-unused.rs:90:13 | LL | for (_, x) in [1, 2, 3].iter().enumerate() { | ^ help: if this is intentional, prefix it with an underscore: `_x` error: variable `x` is assigned to, but never used - --> $DIR/liveness-unused.rs:112:9 + --> $DIR/liveness-unused.rs:113:9 | LL | let x; | ^ @@ -105,7 +105,7 @@ LL | let x; = note: consider using `_x` instead error: value assigned to `x` is never read - --> $DIR/liveness-unused.rs:116:9 + --> $DIR/liveness-unused.rs:117:9 | LL | x = 0; | ^ diff --git a/tests/ui/logging-only-prints-once.rs b/tests/ui/logging-only-prints-once.rs index 5377d5eeae2bc..bb8c29694b52a 100644 --- a/tests/ui/logging-only-prints-once.rs +++ b/tests/ui/logging-only-prints-once.rs @@ -1,5 +1,4 @@ //@ run-pass -//@ ignore-windows //@ needs-threads use std::cell::Cell; diff --git a/tests/ui/loops/for-each-loop-panic.rs b/tests/ui/loops/for-each-loop-panic.rs index 04784cac8f2fc..79cfca93e0e20 100644 --- a/tests/ui/loops/for-each-loop-panic.rs +++ b/tests/ui/loops/for-each-loop-panic.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:moop -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { for _ in 0_usize..10_usize { diff --git a/tests/ui/loops/loop-break-value-no-repeat.stderr b/tests/ui/loops/loop-break-value-no-repeat.stderr index 946057d054398..918ea81a2ed57 100644 --- a/tests/ui/loops/loop-break-value-no-repeat.stderr +++ b/tests/ui/loops/loop-break-value-no-repeat.stderr @@ -8,8 +8,9 @@ LL | break 22 | help: use `break` on its own without a value inside this `for` loop | -LL | break - | ~~~~~ +LL - break 22 +LL + break + | error: aborting due to 1 previous error diff --git a/tests/ui/loops/loop-break-value.stderr b/tests/ui/loops/loop-break-value.stderr index 0912bdbb221ec..3b9735510bd7e 100644 --- a/tests/ui/loops/loop-break-value.stderr +++ b/tests/ui/loops/loop-break-value.stderr @@ -46,12 +46,14 @@ LL | break (); | help: use `break` on its own without a value inside this `while` loop | -LL | break; - | ~~~~~ +LL - break (); +LL + break; + | help: alternatively, you might have meant to use the available loop label | -LL | break 'while_loop; - | ~~~~~~~~~~~ +LL - break (); +LL + break 'while_loop; + | error[E0571]: `break` with value from a `while` loop --> $DIR/loop-break-value.rs:34:13 @@ -64,8 +66,9 @@ LL | break 'while_loop 123; | help: use `break` on its own without a value inside this `while` loop | -LL | break 'while_loop; - | ~~~~~~~~~~~~~~~~~ +LL - break 'while_loop 123; +LL + break 'while_loop; + | error[E0571]: `break` with value from a `while` loop --> $DIR/loop-break-value.rs:42:12 @@ -77,8 +80,9 @@ LL | if break () { | help: use `break` on its own without a value inside this `while` loop | -LL | if break { - | ~~~~~ +LL - if break () { +LL + if break { + | error[E0571]: `break` with value from a `while` loop --> $DIR/loop-break-value.rs:47:9 @@ -90,8 +94,9 @@ LL | break None; | help: use `break` on its own without a value inside this `while` loop | -LL | break; - | ~~~~~ +LL - break None; +LL + break; + | error[E0571]: `break` with value from a `while` loop --> $DIR/loop-break-value.rs:53:13 @@ -104,8 +109,9 @@ LL | break 'while_let_loop "nope"; | help: use `break` on its own without a value inside this `while` loop | -LL | break 'while_let_loop; - | ~~~~~~~~~~~~~~~~~~~~~ +LL - break 'while_let_loop "nope"; +LL + break 'while_let_loop; + | error[E0571]: `break` with value from a `for` loop --> $DIR/loop-break-value.rs:60:9 @@ -117,8 +123,9 @@ LL | break (); | help: use `break` on its own without a value inside this `for` loop | -LL | break; - | ~~~~~ +LL - break (); +LL + break; + | error[E0571]: `break` with value from a `for` loop --> $DIR/loop-break-value.rs:61:9 @@ -131,8 +138,9 @@ LL | break [()]; | help: use `break` on its own without a value inside this `for` loop | -LL | break; - | ~~~~~ +LL - break [()]; +LL + break; + | error[E0571]: `break` with value from a `for` loop --> $DIR/loop-break-value.rs:68:13 @@ -145,8 +153,9 @@ LL | break 'for_loop Some(17); | help: use `break` on its own without a value inside this `for` loop | -LL | break 'for_loop; - | ~~~~~~~~~~~~~~~ +LL - break 'for_loop Some(17); +LL + break 'for_loop; + | error[E0308]: mismatched types --> $DIR/loop-break-value.rs:4:31 diff --git a/tests/ui/lto/auxiliary/dwarf-mixed-versions-lto-aux.rs b/tests/ui/lto/auxiliary/dwarf-mixed-versions-lto-aux.rs new file mode 100644 index 0000000000000..3c81127ee65c0 --- /dev/null +++ b/tests/ui/lto/auxiliary/dwarf-mixed-versions-lto-aux.rs @@ -0,0 +1,5 @@ +//@ compile-flags: -g --crate-type=rlib -Zdwarf-version=4 + +pub fn say_hi() { + println!("hello there") +} diff --git a/tests/ui/lto/dwarf-mixed-versions-lto.rs b/tests/ui/lto/dwarf-mixed-versions-lto.rs new file mode 100644 index 0000000000000..14ef65a868e02 --- /dev/null +++ b/tests/ui/lto/dwarf-mixed-versions-lto.rs @@ -0,0 +1,15 @@ +// This test verifies that we do not produce a warning when performing LTO on a +// crate graph that contains a mix of different DWARF version settings. This +// matches Clang's behavior. + +//@ ignore-msvc Platform must use DWARF +//@ aux-build:dwarf-mixed-versions-lto-aux.rs +//@ compile-flags: -C lto -g -Zdwarf-version=5 +//@ no-prefer-dynamic +//@ build-pass + +extern crate dwarf_mixed_versions_lto_aux; + +fn main() { + dwarf_mixed_versions_lto_aux::say_hi(); +} diff --git a/tests/ui/lto/weak-works.rs b/tests/ui/lto/weak-works.rs index 00e10b97d60cb..1ff47ca602d24 100644 --- a/tests/ui/lto/weak-works.rs +++ b/tests/ui/lto/weak-works.rs @@ -1,7 +1,8 @@ //@ run-pass //@ compile-flags: -C codegen-units=8 -Z thinlto -//@ ignore-windows +//@ ignore-i686-pc-windows-gnu +//@ ignore-x86_64-pc-windows-gnu #![feature(linkage)] diff --git a/tests/ui/macros/assert-as-macro.rs b/tests/ui/macros/assert-as-macro.rs index 391b056292f84..c18a3d89fb635 100644 --- a/tests/ui/macros/assert-as-macro.rs +++ b/tests/ui/macros/assert-as-macro.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:assertion failed: 1 == 2 -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { assert!(1 == 2); diff --git a/tests/ui/macros/assert-eq-macro-msg.rs b/tests/ui/macros/assert-eq-macro-msg.rs index 39eeefeeef90e..9ef7552cc7b2d 100644 --- a/tests/ui/macros/assert-eq-macro-msg.rs +++ b/tests/ui/macros/assert-eq-macro-msg.rs @@ -2,7 +2,7 @@ //@ error-pattern:assertion `left == right` failed: 1 + 1 definitely should be 3 //@ error-pattern: left: 2 //@ error-pattern: right: 3 -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { assert_eq!(1 + 1, 3, "1 + 1 definitely should be 3"); diff --git a/tests/ui/macros/assert-eq-macro-panic.rs b/tests/ui/macros/assert-eq-macro-panic.rs index 22c3a8a634f4b..3c46e2798d11d 100644 --- a/tests/ui/macros/assert-eq-macro-panic.rs +++ b/tests/ui/macros/assert-eq-macro-panic.rs @@ -2,7 +2,7 @@ //@ error-pattern:assertion `left == right` failed //@ error-pattern: left: 14 //@ error-pattern: right: 15 -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { assert_eq!(14, 15); diff --git a/tests/ui/macros/assert-macro-explicit.rs b/tests/ui/macros/assert-macro-explicit.rs index 167581d2525e1..b14a182a876ce 100644 --- a/tests/ui/macros/assert-macro-explicit.rs +++ b/tests/ui/macros/assert-macro-explicit.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:assertion failed: false -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { assert!(false); diff --git a/tests/ui/macros/assert-macro-fmt.rs b/tests/ui/macros/assert-macro-fmt.rs index 475544303796d..3fd0f472d1606 100644 --- a/tests/ui/macros/assert-macro-fmt.rs +++ b/tests/ui/macros/assert-macro-fmt.rs @@ -1,7 +1,7 @@ //@ run-fail //@ error-pattern: panicked //@ error-pattern: test-assert-fmt 42 rust -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { assert!(false, "test-assert-fmt {} {}", 42, "rust"); diff --git a/tests/ui/macros/assert-macro-owned.rs b/tests/ui/macros/assert-macro-owned.rs index 46a59db1390c2..f9b6f5f518059 100644 --- a/tests/ui/macros/assert-macro-owned.rs +++ b/tests/ui/macros/assert-macro-owned.rs @@ -1,7 +1,7 @@ //@ run-fail //@ error-pattern:panicked //@ error-pattern:test-assert-owned -//@ ignore-emscripten no processes +//@ needs-subprocess #![allow(non_fmt_panics)] diff --git a/tests/ui/macros/assert-macro-static.rs b/tests/ui/macros/assert-macro-static.rs index 7d9e345d516a3..8c91d7722fc40 100644 --- a/tests/ui/macros/assert-macro-static.rs +++ b/tests/ui/macros/assert-macro-static.rs @@ -1,7 +1,7 @@ //@ run-fail //@ error-pattern:panicked //@ error-pattern:test-assert-static -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { assert!(false, "test-assert-static"); diff --git a/tests/ui/macros/assert-matches-macro-msg.rs b/tests/ui/macros/assert-matches-macro-msg.rs index efa4121da9232..956bc9cf0f49b 100644 --- a/tests/ui/macros/assert-matches-macro-msg.rs +++ b/tests/ui/macros/assert-matches-macro-msg.rs @@ -2,7 +2,7 @@ //@ error-pattern:assertion `left matches right` failed: 1 + 1 definitely should be 3 //@ error-pattern: left: 2 //@ error-pattern: right: 3 -//@ ignore-emscripten no processes +//@ needs-subprocess #![feature(assert_matches)] diff --git a/tests/ui/macros/assert-ne-macro-msg.rs b/tests/ui/macros/assert-ne-macro-msg.rs index 0a578e1baf93c..24bc4dbea4753 100644 --- a/tests/ui/macros/assert-ne-macro-msg.rs +++ b/tests/ui/macros/assert-ne-macro-msg.rs @@ -2,7 +2,7 @@ //@ error-pattern:assertion `left != right` failed: 1 + 1 definitely should not be 2 //@ error-pattern: left: 2 //@ error-pattern: right: 2 -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { assert_ne!(1 + 1, 2, "1 + 1 definitely should not be 2"); diff --git a/tests/ui/macros/assert-ne-macro-panic.rs b/tests/ui/macros/assert-ne-macro-panic.rs index 9cf5f05e9f18b..c349825baf389 100644 --- a/tests/ui/macros/assert-ne-macro-panic.rs +++ b/tests/ui/macros/assert-ne-macro-panic.rs @@ -2,7 +2,7 @@ //@ error-pattern:assertion `left != right` failed //@ error-pattern: left: 14 //@ error-pattern: right: 14 -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { assert_ne!(14, 14); diff --git a/tests/ui/macros/builtin-prelude-no-accidents.rs b/tests/ui/macros/builtin-prelude-no-accidents.rs index 01691a82dd772..9bebcb75526fb 100644 --- a/tests/ui/macros/builtin-prelude-no-accidents.rs +++ b/tests/ui/macros/builtin-prelude-no-accidents.rs @@ -2,7 +2,7 @@ // because macros with the same names are in prelude. fn main() { - env::current_dir; //~ ERROR use of undeclared crate or module `env` - type A = panic::PanicInfo; //~ ERROR use of undeclared crate or module `panic` - type B = vec::Vec; //~ ERROR use of undeclared crate or module `vec` + env::current_dir; //~ ERROR use of unresolved module or unlinked crate `env` + type A = panic::PanicInfo; //~ ERROR use of unresolved module or unlinked crate `panic` + type B = vec::Vec; //~ ERROR use of unresolved module or unlinked crate `vec` } diff --git a/tests/ui/macros/builtin-prelude-no-accidents.stderr b/tests/ui/macros/builtin-prelude-no-accidents.stderr index c1054230bc9a5..8c7095a6aedf3 100644 --- a/tests/ui/macros/builtin-prelude-no-accidents.stderr +++ b/tests/ui/macros/builtin-prelude-no-accidents.stderr @@ -1,31 +1,34 @@ -error[E0433]: failed to resolve: use of undeclared crate or module `env` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `env` --> $DIR/builtin-prelude-no-accidents.rs:5:5 | LL | env::current_dir; - | ^^^ use of undeclared crate or module `env` + | ^^^ use of unresolved module or unlinked crate `env` | + = help: you might be missing a crate named `env` help: consider importing this module | LL + use std::env; | -error[E0433]: failed to resolve: use of undeclared crate or module `panic` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `panic` --> $DIR/builtin-prelude-no-accidents.rs:6:14 | LL | type A = panic::PanicInfo; - | ^^^^^ use of undeclared crate or module `panic` + | ^^^^^ use of unresolved module or unlinked crate `panic` | + = help: you might be missing a crate named `panic` help: consider importing this module | LL + use std::panic; | -error[E0433]: failed to resolve: use of undeclared crate or module `vec` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `vec` --> $DIR/builtin-prelude-no-accidents.rs:7:14 | LL | type B = vec::Vec; - | ^^^ use of undeclared crate or module `vec` + | ^^^ use of unresolved module or unlinked crate `vec` | + = help: you might be missing a crate named `vec` help: consider importing this module | LL + use std::vec; diff --git a/tests/ui/issues/issue-40136.rs b/tests/ui/macros/const-expr-invocations-issue-40136.rs similarity index 100% rename from tests/ui/issues/issue-40136.rs rename to tests/ui/macros/const-expr-invocations-issue-40136.rs diff --git a/tests/ui/macros/die-macro-2.rs b/tests/ui/macros/die-macro-2.rs index e5456bdfca0f9..d802f189ce1af 100644 --- a/tests/ui/macros/die-macro-2.rs +++ b/tests/ui/macros/die-macro-2.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:test -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { panic!("test"); diff --git a/tests/ui/macros/die-macro-expr.rs b/tests/ui/macros/die-macro-expr.rs index fb92dd66e3dc3..f4fefb0ca37dd 100644 --- a/tests/ui/macros/die-macro-expr.rs +++ b/tests/ui/macros/die-macro-expr.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:test -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { let __isize: isize = panic!("test"); diff --git a/tests/ui/macros/die-macro-pure.rs b/tests/ui/macros/die-macro-pure.rs index 484eed3d720f0..d84787705a1aa 100644 --- a/tests/ui/macros/die-macro-pure.rs +++ b/tests/ui/macros/die-macro-pure.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:test -//@ ignore-emscripten no processes +//@ needs-subprocess fn f() { panic!("test"); diff --git a/tests/ui/macros/expand-full-no-resolution.stderr b/tests/ui/macros/expand-full-no-resolution.stderr index df6f20332bfd8..b836ac51ad9e0 100644 --- a/tests/ui/macros/expand-full-no-resolution.stderr +++ b/tests/ui/macros/expand-full-no-resolution.stderr @@ -9,8 +9,9 @@ LL | format_args!(a!()); | help: the leading underscore in `_a` marks it as unused, consider renaming it to `a` | -LL | macro_rules! a { - | ~ +LL - macro_rules! _a { +LL + macro_rules! a { + | error: cannot find macro `a` in this scope --> $DIR/expand-full-no-resolution.rs:19:10 @@ -23,8 +24,9 @@ LL | env!(a!()); | help: the leading underscore in `_a` marks it as unused, consider renaming it to `a` | -LL | macro_rules! a { - | ~ +LL - macro_rules! _a { +LL + macro_rules! a { + | error: aborting due to 2 previous errors diff --git a/tests/ui/macros/expr_2021_cargo_fix_edition.stderr b/tests/ui/macros/expr_2021_cargo_fix_edition.stderr index fe1fd4a26a028..a2c281d9c0a11 100644 --- a/tests/ui/macros/expr_2021_cargo_fix_edition.stderr +++ b/tests/ui/macros/expr_2021_cargo_fix_edition.stderr @@ -14,7 +14,7 @@ LL | #![warn(edition_2024_expr_fragment_specifier)] help: to keep the existing behavior, use the `expr_2021` fragment specifier | LL | ($e:expr_2021) => { - | ~~~~~~~~~ + | +++++ warning: the `expr` fragment specifier will accept more expressions in the 2024 edition --> $DIR/expr_2021_cargo_fix_edition.rs:11:11 @@ -27,7 +27,7 @@ LL | ($($i:expr)*) => { }; help: to keep the existing behavior, use the `expr_2021` fragment specifier | LL | ($($i:expr_2021)*) => { }; - | ~~~~~~~~~ + | +++++ warning: 2 warnings emitted diff --git a/tests/ui/macros/format-foreign.stderr b/tests/ui/macros/format-foreign.stderr index 7971c2ab2b9b2..ccb6583615cf7 100644 --- a/tests/ui/macros/format-foreign.stderr +++ b/tests/ui/macros/format-foreign.stderr @@ -11,8 +11,9 @@ LL | println!("%.*3$s %s!\n", "Hello,", "World", 4); = note: printf formatting is not supported; see the documentation for `std::fmt` help: format specifiers use curly braces | -LL | println!("{:.2$} {}!\n", "Hello,", "World", 4); - | ~~~~~~ ~~ +LL - println!("%.*3$s %s!\n", "Hello,", "World", 4); +LL + println!("{:.2$} {}!\n", "Hello,", "World", 4); + | error: argument never used --> $DIR/format-foreign.rs:3:29 @@ -75,8 +76,9 @@ LL | println!("$1 $0 $$ $NAME", 1, 2, NAME=3); = note: shell formatting is not supported; see the documentation for `std::fmt` help: format specifiers use curly braces | -LL | println!("{1} {0} $$ {NAME}", 1, 2, NAME=3); - | ~~~ ~~~ ~~~~~~ +LL - println!("$1 $0 $$ $NAME", 1, 2, NAME=3); +LL + println!("{1} {0} $$ {NAME}", 1, 2, NAME=3); + | error: aborting due to 6 previous errors diff --git a/tests/ui/macros/issue-103529.stderr b/tests/ui/macros/issue-103529.stderr index 61e322afc7709..985d9ace8186c 100644 --- a/tests/ui/macros/issue-103529.stderr +++ b/tests/ui/macros/issue-103529.stderr @@ -21,8 +21,9 @@ LL | m! { auto x } | help: write `let` instead of `auto` to introduce a new variable | -LL | m! { let x } - | ~~~ +LL - m! { auto x } +LL + m! { let x } + | error: invalid variable declaration --> $DIR/issue-103529.rs:10:6 @@ -32,8 +33,9 @@ LL | m! { var x } | help: write `let` instead of `var` to introduce a new variable | -LL | m! { let x } - | ~~~ +LL - m! { var x } +LL + m! { let x } + | error: aborting due to 4 previous errors diff --git a/tests/ui/macros/issue-109237.stderr b/tests/ui/macros/issue-109237.stderr index a335786df8642..9d25420af2561 100644 --- a/tests/ui/macros/issue-109237.stderr +++ b/tests/ui/macros/issue-109237.stderr @@ -11,8 +11,9 @@ LL | let _ = statement!(); = note: this error originates in the macro `statement` (in Nightly builds, run with -Z macro-backtrace for more info) help: surround the macro invocation with `{}` to interpret the expansion as a statement | -LL | let _ = { statement!(); }; - | ~~~~~~~~~~~~~~~~~ +LL - let _ = statement!(); +LL + let _ = { statement!(); }; + | error: aborting due to 1 previous error diff --git a/tests/ui/macros/issue-118786.stderr b/tests/ui/macros/issue-118786.stderr index 7fa5c2b83ddd8..af4cc9ad86377 100644 --- a/tests/ui/macros/issue-118786.stderr +++ b/tests/ui/macros/issue-118786.stderr @@ -6,8 +6,9 @@ LL | make_macro!((meow)); | help: change the delimiters to curly braces | -LL | make_macro!({meow}); - | ~ ~ +LL - make_macro!((meow)); +LL + make_macro!({meow}); + | help: add a semicolon | LL | macro_rules! $macro_name; { diff --git a/tests/ui/macros/issue-68060.rs b/tests/ui/macros/issue-68060.rs index 1a826bd60e039..4eddb96848cd0 100644 --- a/tests/ui/macros/issue-68060.rs +++ b/tests/ui/macros/issue-68060.rs @@ -3,8 +3,6 @@ fn main() { .map( #[target_feature(enable = "")] //~^ ERROR: attribute should be applied to a function - //~| ERROR: feature named `` is not valid - //~| NOTE: `` is not valid for this target #[track_caller] //~^ ERROR: `#[track_caller]` on closures is currently unstable //~| NOTE: see issue #87417 diff --git a/tests/ui/macros/issue-68060.stderr b/tests/ui/macros/issue-68060.stderr index 5724a9ea43843..ef2246d5bd6bb 100644 --- a/tests/ui/macros/issue-68060.stderr +++ b/tests/ui/macros/issue-68060.stderr @@ -7,14 +7,8 @@ LL | #[target_feature(enable = "")] LL | |_| (), | ------ not a function definition -error: the feature named `` is not valid for this target - --> $DIR/issue-68060.rs:4:30 - | -LL | #[target_feature(enable = "")] - | ^^^^^^^^^^^ `` is not valid for this target - error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/issue-68060.rs:8:13 + --> $DIR/issue-68060.rs:6:13 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ @@ -23,6 +17,6 @@ LL | #[track_caller] = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/macros/issue-69396-const-no-type-in-macro.rs b/tests/ui/macros/issue-69396-const-no-type-in-macro.rs index 45a30857413b9..c200a1fd0b41e 100644 --- a/tests/ui/macros/issue-69396-const-no-type-in-macro.rs +++ b/tests/ui/macros/issue-69396-const-no-type-in-macro.rs @@ -4,7 +4,7 @@ macro_rules! suite { const A = "A".$fn(); //~^ ERROR the name `A` is defined multiple times //~| ERROR missing type for `const` item - //~| ERROR the placeholder `_` is not allowed within types on item signatures for constants + //~| ERROR missing type for item )* } } diff --git a/tests/ui/macros/issue-69396-const-no-type-in-macro.stderr b/tests/ui/macros/issue-69396-const-no-type-in-macro.stderr index 89aeafebac497..4342d7d88f54b 100644 --- a/tests/ui/macros/issue-69396-const-no-type-in-macro.stderr +++ b/tests/ui/macros/issue-69396-const-no-type-in-macro.stderr @@ -27,14 +27,11 @@ LL | | } | = note: this error originates in the macro `suite` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants +error[E0121]: missing type for item --> $DIR/issue-69396-const-no-type-in-macro.rs:4:20 | LL | const A = "A".$fn(); - | ^ - | | - | not allowed in type signatures - | help: replace with the correct type: `bool` + | ^ not allowed in type signatures ... LL | / suite! { LL | | len; diff --git a/tests/ui/macros/issue-99265.stderr b/tests/ui/macros/issue-99265.stderr index 9185dbff61ee0..a4200bf02cfe5 100644 --- a/tests/ui/macros/issue-99265.stderr +++ b/tests/ui/macros/issue-99265.stderr @@ -74,8 +74,9 @@ LL | println!("Hello {:1$}!", "x", width = 5); | help: use the named argument by name to avoid ambiguity | -LL | println!("Hello {:width$}!", "x", width = 5); - | ~~~~~~ +LL - println!("Hello {:1$}!", "x", width = 5); +LL + println!("Hello {:width$}!", "x", width = 5); + | warning: named argument `f` is not used by name --> $DIR/issue-99265.rs:23:33 @@ -100,8 +101,9 @@ LL | println!("Hello {:1$.2$}!", f = 0.02f32, width = 5, precision = 2); | help: use the named argument by name to avoid ambiguity | -LL | println!("Hello {:1$.precision$}!", f = 0.02f32, width = 5, precision = 2); - | ~~~~~~~~~~ +LL - println!("Hello {:1$.2$}!", f = 0.02f32, width = 5, precision = 2); +LL + println!("Hello {:1$.precision$}!", f = 0.02f32, width = 5, precision = 2); + | warning: named argument `width` is not used by name --> $DIR/issue-99265.rs:23:46 @@ -113,8 +115,9 @@ LL | println!("Hello {:1$.2$}!", f = 0.02f32, width = 5, precision = 2); | help: use the named argument by name to avoid ambiguity | -LL | println!("Hello {:width$.2$}!", f = 0.02f32, width = 5, precision = 2); - | ~~~~~~ +LL - println!("Hello {:1$.2$}!", f = 0.02f32, width = 5, precision = 2); +LL + println!("Hello {:width$.2$}!", f = 0.02f32, width = 5, precision = 2); + | warning: named argument `f` is not used by name --> $DIR/issue-99265.rs:31:34 @@ -126,8 +129,9 @@ LL | println!("Hello {0:1$.2$}!", f = 0.02f32, width = 5, precision = 2); | help: use the named argument by name to avoid ambiguity | -LL | println!("Hello {f:1$.2$}!", f = 0.02f32, width = 5, precision = 2); - | ~ +LL - println!("Hello {0:1$.2$}!", f = 0.02f32, width = 5, precision = 2); +LL + println!("Hello {f:1$.2$}!", f = 0.02f32, width = 5, precision = 2); + | warning: named argument `precision` is not used by name --> $DIR/issue-99265.rs:31:58 @@ -139,8 +143,9 @@ LL | println!("Hello {0:1$.2$}!", f = 0.02f32, width = 5, precision = 2); | help: use the named argument by name to avoid ambiguity | -LL | println!("Hello {0:1$.precision$}!", f = 0.02f32, width = 5, precision = 2); - | ~~~~~~~~~~ +LL - println!("Hello {0:1$.2$}!", f = 0.02f32, width = 5, precision = 2); +LL + println!("Hello {0:1$.precision$}!", f = 0.02f32, width = 5, precision = 2); + | warning: named argument `width` is not used by name --> $DIR/issue-99265.rs:31:47 @@ -152,8 +157,9 @@ LL | println!("Hello {0:1$.2$}!", f = 0.02f32, width = 5, precision = 2); | help: use the named argument by name to avoid ambiguity | -LL | println!("Hello {0:width$.2$}!", f = 0.02f32, width = 5, precision = 2); - | ~~~~~~ +LL - println!("Hello {0:1$.2$}!", f = 0.02f32, width = 5, precision = 2); +LL + println!("Hello {0:width$.2$}!", f = 0.02f32, width = 5, precision = 2); + | warning: named argument `f` is not used by name --> $DIR/issue-99265.rs:49:9 @@ -166,8 +172,9 @@ LL | f = 0.02f32, | help: use the named argument by name to avoid ambiguity | -LL | "{}, Hello {f:2$.3$} {4:5$.6$}! {1}", - | ~ +LL - "{}, Hello {1:2$.3$} {4:5$.6$}! {1}", +LL + "{}, Hello {f:2$.3$} {4:5$.6$}! {1}", + | warning: named argument `precision` is not used by name --> $DIR/issue-99265.rs:54:9 @@ -180,8 +187,9 @@ LL | precision = 2, | help: use the named argument by name to avoid ambiguity | -LL | "{}, Hello {1:2$.precision$} {4:5$.6$}! {1}", - | ~~~~~~~~~~ +LL - "{}, Hello {1:2$.3$} {4:5$.6$}! {1}", +LL + "{}, Hello {1:2$.precision$} {4:5$.6$}! {1}", + | warning: named argument `width` is not used by name --> $DIR/issue-99265.rs:52:9 @@ -194,8 +202,9 @@ LL | width = 5, | help: use the named argument by name to avoid ambiguity | -LL | "{}, Hello {1:width$.3$} {4:5$.6$}! {1}", - | ~~~~~~ +LL - "{}, Hello {1:2$.3$} {4:5$.6$}! {1}", +LL + "{}, Hello {1:width$.3$} {4:5$.6$}! {1}", + | warning: named argument `g` is not used by name --> $DIR/issue-99265.rs:56:9 @@ -208,8 +217,9 @@ LL | g = 0.02f32, | help: use the named argument by name to avoid ambiguity | -LL | "{}, Hello {1:2$.3$} {g:5$.6$}! {1}", - | ~ +LL - "{}, Hello {1:2$.3$} {4:5$.6$}! {1}", +LL + "{}, Hello {1:2$.3$} {g:5$.6$}! {1}", + | warning: named argument `precision2` is not used by name --> $DIR/issue-99265.rs:60:9 @@ -222,8 +232,9 @@ LL | precision2 = 2 | help: use the named argument by name to avoid ambiguity | -LL | "{}, Hello {1:2$.3$} {4:5$.precision2$}! {1}", - | ~~~~~~~~~~~ +LL - "{}, Hello {1:2$.3$} {4:5$.6$}! {1}", +LL + "{}, Hello {1:2$.3$} {4:5$.precision2$}! {1}", + | warning: named argument `width2` is not used by name --> $DIR/issue-99265.rs:58:9 @@ -236,8 +247,9 @@ LL | width2 = 5, | help: use the named argument by name to avoid ambiguity | -LL | "{}, Hello {1:2$.3$} {4:width2$.6$}! {1}", - | ~~~~~~~ +LL - "{}, Hello {1:2$.3$} {4:5$.6$}! {1}", +LL + "{}, Hello {1:2$.3$} {4:width2$.6$}! {1}", + | warning: named argument `f` is not used by name --> $DIR/issue-99265.rs:49:9 @@ -250,8 +262,9 @@ LL | f = 0.02f32, | help: use the named argument by name to avoid ambiguity | -LL | "{}, Hello {1:2$.3$} {4:5$.6$}! {f}", - | ~ +LL - "{}, Hello {1:2$.3$} {4:5$.6$}! {1}", +LL + "{}, Hello {1:2$.3$} {4:5$.6$}! {f}", + | warning: named argument `f` is not used by name --> $DIR/issue-99265.rs:64:31 @@ -276,8 +289,9 @@ LL | println!("Hello {0:0.1}!", f = 0.02f32); | help: use the named argument by name to avoid ambiguity | -LL | println!("Hello {f:0.1}!", f = 0.02f32); - | ~ +LL - println!("Hello {0:0.1}!", f = 0.02f32); +LL + println!("Hello {f:0.1}!", f = 0.02f32); + | warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:79:23 @@ -302,8 +316,9 @@ LL | println!("{:0$}", v = val); | help: use the named argument by name to avoid ambiguity | -LL | println!("{:v$}", v = val); - | ~~ +LL - println!("{:0$}", v = val); +LL + println!("{:v$}", v = val); + | warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:84:24 @@ -315,8 +330,9 @@ LL | println!("{0:0$}", v = val); | help: use the named argument by name to avoid ambiguity | -LL | println!("{v:0$}", v = val); - | ~ +LL - println!("{0:0$}", v = val); +LL + println!("{v:0$}", v = val); + | warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:84:24 @@ -328,8 +344,9 @@ LL | println!("{0:0$}", v = val); | help: use the named argument by name to avoid ambiguity | -LL | println!("{0:v$}", v = val); - | ~~ +LL - println!("{0:0$}", v = val); +LL + println!("{0:v$}", v = val); + | warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:89:26 @@ -354,8 +371,9 @@ LL | println!("{:0$.0$}", v = val); | help: use the named argument by name to avoid ambiguity | -LL | println!("{:0$.v$}", v = val); - | ~~ +LL - println!("{:0$.0$}", v = val); +LL + println!("{:0$.v$}", v = val); + | warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:89:26 @@ -367,8 +385,9 @@ LL | println!("{:0$.0$}", v = val); | help: use the named argument by name to avoid ambiguity | -LL | println!("{:v$.0$}", v = val); - | ~~ +LL - println!("{:0$.0$}", v = val); +LL + println!("{:v$.0$}", v = val); + | warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:96:27 @@ -380,8 +399,9 @@ LL | println!("{0:0$.0$}", v = val); | help: use the named argument by name to avoid ambiguity | -LL | println!("{v:0$.0$}", v = val); - | ~ +LL - println!("{0:0$.0$}", v = val); +LL + println!("{v:0$.0$}", v = val); + | warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:96:27 @@ -393,8 +413,9 @@ LL | println!("{0:0$.0$}", v = val); | help: use the named argument by name to avoid ambiguity | -LL | println!("{0:0$.v$}", v = val); - | ~~ +LL - println!("{0:0$.0$}", v = val); +LL + println!("{0:0$.v$}", v = val); + | warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:96:27 @@ -406,8 +427,9 @@ LL | println!("{0:0$.0$}", v = val); | help: use the named argument by name to avoid ambiguity | -LL | println!("{0:v$.0$}", v = val); - | ~~ +LL - println!("{0:0$.0$}", v = val); +LL + println!("{0:v$.0$}", v = val); + | warning: named argument `a` is not used by name --> $DIR/issue-99265.rs:104:28 @@ -432,8 +454,9 @@ LL | println!("{} {a} {0}", a = 1); | help: use the named argument by name to avoid ambiguity | -LL | println!("{} {a} {a}", a = 1); - | ~ +LL - println!("{} {a} {0}", a = 1); +LL + println!("{} {a} {a}", a = 1); + | warning: named argument `a` is not used by name --> $DIR/issue-99265.rs:115:14 @@ -460,8 +483,9 @@ LL | a = 1.0, b = 1, c = 2, | help: use the named argument by name to avoid ambiguity | -LL | {:1$.c$}", - | ~~ +LL - {:1$.2$}", +LL + {:1$.c$}", + | warning: named argument `b` is not used by name --> $DIR/issue-99265.rs:115:23 @@ -474,8 +498,9 @@ LL | a = 1.0, b = 1, c = 2, | help: use the named argument by name to avoid ambiguity | -LL | {:b$.2$}", - | ~~ +LL - {:1$.2$}", +LL + {:b$.2$}", + | warning: named argument `a` is not used by name --> $DIR/issue-99265.rs:126:14 @@ -488,8 +513,9 @@ LL | a = 1.0, b = 1, c = 2, | help: use the named argument by name to avoid ambiguity | -LL | {a:1$.2$}", - | ~ +LL - {0:1$.2$}", +LL + {a:1$.2$}", + | warning: named argument `c` is not used by name --> $DIR/issue-99265.rs:126:30 @@ -502,8 +528,9 @@ LL | a = 1.0, b = 1, c = 2, | help: use the named argument by name to avoid ambiguity | -LL | {0:1$.c$}", - | ~~ +LL - {0:1$.2$}", +LL + {0:1$.c$}", + | warning: named argument `b` is not used by name --> $DIR/issue-99265.rs:126:23 @@ -516,8 +543,9 @@ LL | a = 1.0, b = 1, c = 2, | help: use the named argument by name to avoid ambiguity | -LL | {0:b$.2$}", - | ~~ +LL - {0:1$.2$}", +LL + {0:b$.2$}", + | warning: named argument `x` is not used by name --> $DIR/issue-99265.rs:132:30 @@ -542,8 +570,9 @@ LL | println!("{{{:1$.2$}}}", x = 1.0, width = 3, precision = 2); | help: use the named argument by name to avoid ambiguity | -LL | println!("{{{:1$.precision$}}}", x = 1.0, width = 3, precision = 2); - | ~~~~~~~~~~ +LL - println!("{{{:1$.2$}}}", x = 1.0, width = 3, precision = 2); +LL + println!("{{{:1$.precision$}}}", x = 1.0, width = 3, precision = 2); + | warning: named argument `width` is not used by name --> $DIR/issue-99265.rs:132:39 @@ -555,8 +584,9 @@ LL | println!("{{{:1$.2$}}}", x = 1.0, width = 3, precision = 2); | help: use the named argument by name to avoid ambiguity | -LL | println!("{{{:width$.2$}}}", x = 1.0, width = 3, precision = 2); - | ~~~~~~ +LL - println!("{{{:1$.2$}}}", x = 1.0, width = 3, precision = 2); +LL + println!("{{{:width$.2$}}}", x = 1.0, width = 3, precision = 2); + | warning: 42 warnings emitted diff --git a/tests/ui/macros/macro-backtrace-invalid-internals.stderr b/tests/ui/macros/macro-backtrace-invalid-internals.stderr index aa8f06a0df13b..836098bd9c04e 100644 --- a/tests/ui/macros/macro-backtrace-invalid-internals.stderr +++ b/tests/ui/macros/macro-backtrace-invalid-internals.stderr @@ -44,7 +44,7 @@ LL | real_method_stmt!(); help: you must specify a concrete type for this numeric value, like `f32` | LL | 2.0_f32.neg() - | ~~~~~~~ + | ++++ error[E0599]: no method named `fake` found for type `{integer}` in the current scope --> $DIR/macro-backtrace-invalid-internals.rs:23:13 @@ -92,7 +92,7 @@ LL | let _ = real_method_expr!(); help: you must specify a concrete type for this numeric value, like `f32` | LL | 2.0_f32.neg() - | ~~~~~~~ + | ++++ error: aborting due to 8 previous errors diff --git a/tests/ui/macros/macro-inner-attributes.rs b/tests/ui/macros/macro-inner-attributes.rs index 6dbfce2135989..a1eb7cd15c4cc 100644 --- a/tests/ui/macros/macro-inner-attributes.rs +++ b/tests/ui/macros/macro-inner-attributes.rs @@ -15,6 +15,6 @@ test!(b, #[rustc_dummy] fn main() { a::bar(); - //~^ ERROR failed to resolve: use of undeclared crate or module `a` + //~^ ERROR failed to resolve: use of unresolved module or unlinked crate `a` b::bar(); } diff --git a/tests/ui/macros/macro-inner-attributes.stderr b/tests/ui/macros/macro-inner-attributes.stderr index b6e10f45e3810..d74b64db5acab 100644 --- a/tests/ui/macros/macro-inner-attributes.stderr +++ b/tests/ui/macros/macro-inner-attributes.stderr @@ -1,13 +1,14 @@ -error[E0433]: failed to resolve: use of undeclared crate or module `a` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `a` --> $DIR/macro-inner-attributes.rs:17:5 | LL | a::bar(); - | ^ use of undeclared crate or module `a` + | ^ use of unresolved module or unlinked crate `a` | help: there is a crate or module with a similar name | -LL | b::bar(); - | ~ +LL - a::bar(); +LL + b::bar(); + | error: aborting due to 1 previous error diff --git a/tests/ui/macros/macro-span-issue-116502.stderr b/tests/ui/macros/macro-span-issue-116502.stderr index da02855660a4f..2a581f7031b95 100644 --- a/tests/ui/macros/macro-span-issue-116502.stderr +++ b/tests/ui/macros/macro-span-issue-116502.stderr @@ -17,13 +17,6 @@ LL | T: Trait; | ---- in this macro invocation | = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) -help: use type parameters instead - | -LL ~ U -LL | }; -LL | } -LL ~ struct S(m!(), T) - | error: aborting due to 1 previous error diff --git a/tests/ui/macros/macro-use-wrong-name.stderr b/tests/ui/macros/macro-use-wrong-name.stderr index 89345866be804..c7f214db225b6 100644 --- a/tests/ui/macros/macro-use-wrong-name.stderr +++ b/tests/ui/macros/macro-use-wrong-name.stderr @@ -11,8 +11,9 @@ LL | macro_rules! macro_one { () => ("one") } | help: a macro with a similar name exists | -LL | macro_one!(); - | ~~~~~~~~~ +LL - macro_two!(); +LL + macro_one!(); + | help: consider importing this macro | LL + use two_macros::macro_two; diff --git a/tests/ui/macros/macro_path_as_generic_bound.stderr b/tests/ui/macros/macro_path_as_generic_bound.stderr index e25ff57e57f36..9fe4ad27aa059 100644 --- a/tests/ui/macros/macro_path_as_generic_bound.stderr +++ b/tests/ui/macros/macro_path_as_generic_bound.stderr @@ -1,8 +1,10 @@ -error[E0433]: failed to resolve: use of undeclared crate or module `m` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `m` --> $DIR/macro_path_as_generic_bound.rs:7:6 | LL | foo!(m::m2::A); - | ^ use of undeclared crate or module `m` + | ^ use of unresolved module or unlinked crate `m` + | + = help: you might be missing a crate named `m` error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-40845.rs b/tests/ui/macros/macros-in-trait-positions-issue-40845.rs similarity index 100% rename from tests/ui/issues/issue-40845.rs rename to tests/ui/macros/macros-in-trait-positions-issue-40845.rs diff --git a/tests/ui/macros/macros-in-trait-positions-issue-40845.stderr b/tests/ui/macros/macros-in-trait-positions-issue-40845.stderr new file mode 100644 index 0000000000000..5c1b5f51e44b2 --- /dev/null +++ b/tests/ui/macros/macros-in-trait-positions-issue-40845.stderr @@ -0,0 +1,14 @@ +error: cannot find macro `m` in this scope + --> $DIR/macros-in-trait-positions-issue-40845.rs:1:11 + | +LL | trait T { m!(); } + | ^ + +error: cannot find macro `m` in this scope + --> $DIR/macros-in-trait-positions-issue-40845.rs:4:10 + | +LL | impl S { m!(); } + | ^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/macros/meta-item-absolute-path.stderr b/tests/ui/macros/meta-item-absolute-path.stderr index af56d935284cc..8fa5e97899ca2 100644 --- a/tests/ui/macros/meta-item-absolute-path.stderr +++ b/tests/ui/macros/meta-item-absolute-path.stderr @@ -1,14 +1,14 @@ -error[E0433]: failed to resolve: you might be missing crate `Absolute` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `Absolute` --> $DIR/meta-item-absolute-path.rs:1:12 | LL | #[derive(::Absolute)] - | ^^^^^^^^ you might be missing crate `Absolute` + | ^^^^^^^^ use of unresolved module or unlinked crate `Absolute` -error[E0433]: failed to resolve: you might be missing crate `Absolute` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `Absolute` --> $DIR/meta-item-absolute-path.rs:1:12 | LL | #[derive(::Absolute)] - | ^^^^^^^^ you might be missing crate `Absolute` + | ^^^^^^^^ use of unresolved module or unlinked crate `Absolute` | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` diff --git a/tests/ui/macros/not-utf8.rs b/tests/ui/macros/not-utf8.rs index 8100d65a9f811..ad8ac39d230e4 100644 --- a/tests/ui/macros/not-utf8.rs +++ b/tests/ui/macros/not-utf8.rs @@ -3,5 +3,5 @@ //@ reference: input.encoding.invalid fn foo() { - include!("not-utf8.bin") + include!("not-utf8.bin"); } diff --git a/tests/ui/macros/not-utf8.stderr b/tests/ui/macros/not-utf8.stderr index 0d587cab5f3ed..17ee8197ac8be 100644 --- a/tests/ui/macros/not-utf8.stderr +++ b/tests/ui/macros/not-utf8.stderr @@ -1,9 +1,14 @@ -error: couldn't read $DIR/not-utf8.bin: stream did not contain valid UTF-8 +error: couldn't read `$DIR/not-utf8.bin`: stream did not contain valid UTF-8 --> $DIR/not-utf8.rs:6:5 | -LL | include!("not-utf8.bin") +LL | include!("not-utf8.bin"); | ^^^^^^^^^^^^^^^^^^^^^^^^ | +note: byte `193` is not valid utf-8 + --> $DIR/not-utf8.bin:1:1 + | +LL | �|�␂!5�cc␕␂�Ӻi��WWj�ȥ�'�}�␒�J�ȉ��W�␞O�@����␜w�V���LO����␔[ ␃_�'���SQ�~ذ��ų&��- ��lN~��!@␌ _#���kQ��h�␝�:�... + | ^ = note: this error originates in the macro `include` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/macros/recovery-allowed.stderr b/tests/ui/macros/recovery-allowed.stderr index 825f7a8faf8e7..00bc65ed9148c 100644 --- a/tests/ui/macros/recovery-allowed.stderr +++ b/tests/ui/macros/recovery-allowed.stderr @@ -6,8 +6,9 @@ LL | please_recover! { not 1 } | help: use `!` to perform bitwise not | -LL | please_recover! { !1 } - | ~ +LL - please_recover! { not 1 } +LL + please_recover! { !1 } + | error: aborting due to 1 previous error diff --git a/tests/ui/macros/rfc-2011-nicer-assert-messages/assert-with-custom-errors-does-not-create-unnecessary-code.rs b/tests/ui/macros/rfc-2011-nicer-assert-messages/assert-with-custom-errors-does-not-create-unnecessary-code.rs index 37d94830db2cf..2a27164f9cba2 100644 --- a/tests/ui/macros/rfc-2011-nicer-assert-messages/assert-with-custom-errors-does-not-create-unnecessary-code.rs +++ b/tests/ui/macros/rfc-2011-nicer-assert-messages/assert-with-custom-errors-does-not-create-unnecessary-code.rs @@ -1,4 +1,6 @@ -//@ compile-flags: --test +// -Zpanic_abort_tests makes this test work on panic=abort targets and +// it's a no-op on panic=unwind targets +//@ compile-flags: --test -Zpanic_abort_tests //@ run-pass #![feature(core_intrinsics, generic_assert)] diff --git a/tests/ui/macros/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs b/tests/ui/macros/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs index 86cc7adb90de6..254d59076e531 100644 --- a/tests/ui/macros/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs +++ b/tests/ui/macros/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs @@ -1,4 +1,6 @@ -//@ compile-flags: --test +// -Zpanic_abort_tests makes this test work on panic=abort targets and +// it's a no-op on panic=unwind targets +//@ compile-flags: --test -Zpanic_abort_tests // ignore-tidy-linelength //@ run-pass diff --git a/tests/ui/macros/stringify.rs b/tests/ui/macros/stringify.rs index a560bf4c6ef43..40033f546d30b 100644 --- a/tests/ui/macros/stringify.rs +++ b/tests/ui/macros/stringify.rs @@ -569,7 +569,7 @@ fn test_pat() { c1!(pat, [ &pat ], "&pat"); c1!(pat, [ &mut pat ], "&mut pat"); - // PatKind::Lit + // PatKind::Expr c1!(pat, [ 1_000_i8 ], "1_000_i8"); // PatKind::Range diff --git a/tests/ui/macros/syntax-extension-source-utils.rs b/tests/ui/macros/syntax-extension-source-utils.rs index a16ebdc750424..2f88e508058c1 100644 --- a/tests/ui/macros/syntax-extension-source-utils.rs +++ b/tests/ui/macros/syntax-extension-source-utils.rs @@ -16,7 +16,7 @@ pub fn main() { assert_eq!(line!(), 16); assert_eq!(column!(), 16); assert_eq!(indirect_line!(), 18); - assert!((file!().ends_with("syntax-extension-source-utils.rs"))); + assert!(file!().ends_with("syntax-extension-source-utils.rs")); assert_eq!(stringify!((2*3) + 5).to_string(), "(2*3) + 5".to_string()); assert!(include!("syntax-extension-source-utils-files/includeme.\ fragment").to_string() @@ -30,7 +30,7 @@ pub fn main() { include_bytes!("syntax-extension-source-utils-files/includeme.fragment") [1] == (42 as u8)); // '*' // The Windows tests are wrapped in an extra module for some reason - assert!((m1::m2::where_am_i().ends_with("m1::m2"))); + assert!(m1::m2::where_am_i().ends_with("m1::m2")); assert_eq!((35, "(2*3) + 5"), (line!(), stringify!((2*3) + 5))); } diff --git a/tests/ui/macros/unimplemented-macro-panic.rs b/tests/ui/macros/unimplemented-macro-panic.rs index d3bff8ca10ba0..804bd61270e2a 100644 --- a/tests/ui/macros/unimplemented-macro-panic.rs +++ b/tests/ui/macros/unimplemented-macro-panic.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:not implemented -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { unimplemented!() diff --git a/tests/ui/macros/unreachable-arg.rs b/tests/ui/macros/unreachable-arg.rs index 1f0d00734865b..702bd053ab022 100644 --- a/tests/ui/macros/unreachable-arg.rs +++ b/tests/ui/macros/unreachable-arg.rs @@ -1,4 +1,4 @@ -//@ ignore-emscripten no processes +//@ needs-subprocess //@ revisions: edition_2015 edition_2021 //@ [edition_2015]edition:2015 diff --git a/tests/ui/macros/unreachable-fmt-msg.rs b/tests/ui/macros/unreachable-fmt-msg.rs index b16394a1920eb..928e0a427ae42 100644 --- a/tests/ui/macros/unreachable-fmt-msg.rs +++ b/tests/ui/macros/unreachable-fmt-msg.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:internal error: entered unreachable code: 6 is not prime -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { unreachable!("{} is not {}", 6u32, "prime"); diff --git a/tests/ui/macros/unreachable-format-arg.rs b/tests/ui/macros/unreachable-format-arg.rs index 449c6bca16bce..e1fdab25a43c8 100644 --- a/tests/ui/macros/unreachable-format-arg.rs +++ b/tests/ui/macros/unreachable-format-arg.rs @@ -1,5 +1,5 @@ //@ run-fail -//@ ignore-emscripten no processes +//@ needs-subprocess //@ revisions: edition_2015 edition_2021 //@ [edition_2015]edition:2015 diff --git a/tests/ui/macros/unreachable-format-args.rs b/tests/ui/macros/unreachable-format-args.rs index 5f8a0e9cdffa1..856fc992685ac 100644 --- a/tests/ui/macros/unreachable-format-args.rs +++ b/tests/ui/macros/unreachable-format-args.rs @@ -1,4 +1,4 @@ -//@ ignore-emscripten no processes +//@ needs-subprocess //@ revisions: edition_2015 edition_2021 //@ [edition_2015]edition:2015 diff --git a/tests/ui/macros/unreachable-macro-panic.rs b/tests/ui/macros/unreachable-macro-panic.rs index 7909bcb76242d..c5cea5551cfcf 100644 --- a/tests/ui/macros/unreachable-macro-panic.rs +++ b/tests/ui/macros/unreachable-macro-panic.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:internal error: entered unreachable code -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { unreachable!() diff --git a/tests/ui/macros/unreachable-static-msg.rs b/tests/ui/macros/unreachable-static-msg.rs index 3e917897da458..a85f8962e7d9c 100644 --- a/tests/ui/macros/unreachable-static-msg.rs +++ b/tests/ui/macros/unreachable-static-msg.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:internal error: entered unreachable code: uhoh -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { unreachable!("uhoh") diff --git a/tests/ui/macros/unreachable.rs b/tests/ui/macros/unreachable.rs index 7909bcb76242d..c5cea5551cfcf 100644 --- a/tests/ui/macros/unreachable.rs +++ b/tests/ui/macros/unreachable.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:internal error: entered unreachable code -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { unreachable!() diff --git a/tests/ui/macros/vec-macro-in-pattern.rs b/tests/ui/macros/vec-macro-in-pattern.rs index 26d7d4280fadb..9b9a1edf54c9e 100644 --- a/tests/ui/macros/vec-macro-in-pattern.rs +++ b/tests/ui/macros/vec-macro-in-pattern.rs @@ -4,7 +4,9 @@ fn main() { match Some(vec![42]) { - Some(vec![43]) => {} //~ ERROR expected pattern, found `#` + Some(vec![43]) => {} //~ ERROR expected a pattern, found a function call + //~| ERROR found associated function + //~| ERROR usage of qualified paths in this context is experimental _ => {} } } diff --git a/tests/ui/macros/vec-macro-in-pattern.stderr b/tests/ui/macros/vec-macro-in-pattern.stderr index f32a2cf8e4350..71ba0ea5ad4f5 100644 --- a/tests/ui/macros/vec-macro-in-pattern.stderr +++ b/tests/ui/macros/vec-macro-in-pattern.stderr @@ -1,14 +1,33 @@ -error: expected pattern, found `#` +error[E0532]: expected a pattern, found a function call + --> $DIR/vec-macro-in-pattern.rs:7:14 + | +LL | Some(vec![43]) => {} + | ^^^^^^^^ not a tuple struct or tuple variant + | + = note: function calls are not allowed in patterns: + = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0658]: usage of qualified paths in this context is experimental --> $DIR/vec-macro-in-pattern.rs:7:14 | LL | Some(vec![43]) => {} | ^^^^^^^^ - | | - | expected pattern - | in this macro invocation - | this macro call doesn't expand to a pattern | + = note: see issue #86935 for more information + = help: add `#![feature(more_qualified_paths)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0164]: expected tuple struct or tuple variant, found associated function `<[_]>::into_vec` + --> $DIR/vec-macro-in-pattern.rs:7:14 + | +LL | Some(vec![43]) => {} + | ^^^^^^^^ `fn` calls are not allowed in patterns + | + = help: for more information, visit https://doc.rust-lang.org/book/ch19-00-patterns.html = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 1 previous error +error: aborting due to 3 previous errors +Some errors have detailed explanations: E0164, E0532, E0658. +For more information about an error, try `rustc --explain E0164`. diff --git a/tests/ui/malformed/malformed-meta-delim.stderr b/tests/ui/malformed/malformed-meta-delim.stderr index 27636c3d546fd..3f2357c435f0c 100644 --- a/tests/ui/malformed/malformed-meta-delim.stderr +++ b/tests/ui/malformed/malformed-meta-delim.stderr @@ -6,8 +6,9 @@ LL | #[allow { foo_lint } ] | help: the delimiters should be `(` and `)` | -LL | #[allow ( foo_lint ) ] - | ~ ~ +LL - #[allow { foo_lint } ] +LL + #[allow ( foo_lint ) ] + | error: wrong meta list delimiters --> $DIR/malformed-meta-delim.rs:8:9 @@ -17,8 +18,9 @@ LL | #[allow [ foo_lint ] ] | help: the delimiters should be `(` and `)` | -LL | #[allow ( foo_lint ) ] - | ~ ~ +LL - #[allow [ foo_lint ] ] +LL + #[allow ( foo_lint ) ] + | error: aborting due to 2 previous errors diff --git a/tests/ui/malformed/malformed-special-attrs.stderr b/tests/ui/malformed/malformed-special-attrs.stderr index 8f2ce20593f56..a6220710cf912 100644 --- a/tests/ui/malformed/malformed-special-attrs.stderr +++ b/tests/ui/malformed/malformed-special-attrs.stderr @@ -7,8 +7,9 @@ LL | #[cfg_attr] = note: for more information, visit help: missing condition and attribute | -LL | #[cfg_attr(condition, attribute, other_attribute, ...)] - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - #[cfg_attr] +LL + #[cfg_attr(condition, attribute, other_attribute, ...)] + | error: malformed `cfg_attr` attribute input --> $DIR/malformed-special-attrs.rs:4:1 @@ -19,8 +20,9 @@ LL | #[cfg_attr = ""] = note: for more information, visit help: missing condition and attribute | -LL | #[cfg_attr(condition, attribute, other_attribute, ...)] - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - #[cfg_attr = ""] +LL + #[cfg_attr(condition, attribute, other_attribute, ...)] + | error: malformed `derive` attribute input --> $DIR/malformed-special-attrs.rs:7:1 diff --git a/tests/ui/match/enum-and-break-in-match-issue-41213.rs b/tests/ui/match/enum-and-break-in-match-issue-41213.rs new file mode 100644 index 0000000000000..7c42a3629c9c0 --- /dev/null +++ b/tests/ui/match/enum-and-break-in-match-issue-41213.rs @@ -0,0 +1,24 @@ +//@ check-pass +#![allow(dead_code)] +enum A { + A1, + A2, + A3, +} + +enum B { + B1(String, String), + B2(String, String), +} + +fn main() { + let a = A::A1; + loop { + let _ctor = match a { + A::A3 => break, + A::A1 => B::B1, + A::A2 => B::B2, + }; + break; + } +} diff --git a/tests/ui/match/expr-match-panic-fn.rs b/tests/ui/match/expr-match-panic-fn.rs index 82991d20df88d..a07c2c6af8f79 100644 --- a/tests/ui/match/expr-match-panic-fn.rs +++ b/tests/ui/match/expr-match-panic-fn.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:explicit panic -//@ ignore-emscripten no processes +//@ needs-subprocess fn f() -> ! { panic!() diff --git a/tests/ui/match/expr-match-panic.rs b/tests/ui/match/expr-match-panic.rs index e332ba83b914f..d15b0a9cd726f 100644 --- a/tests/ui/match/expr-match-panic.rs +++ b/tests/ui/match/expr-match-panic.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:explicit panic -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { let _x = match true { diff --git a/tests/ui/match/issue-56685.stderr b/tests/ui/match/issue-56685.stderr index ccf357d4aa00e..6a1d152ed5bb1 100644 --- a/tests/ui/match/issue-56685.stderr +++ b/tests/ui/match/issue-56685.stderr @@ -12,7 +12,7 @@ LL | #![deny(unused_variables)] help: if this is intentional, prefix it with an underscore | LL | E::A(_x) | E::B(_x) => {} - | ~~ ~~ + | + + error: unused variable: `x` --> $DIR/issue-56685.rs:25:14 @@ -23,7 +23,7 @@ LL | F::A(x, y) | F::B(x, y) => { y }, help: if this is intentional, prefix it with an underscore | LL | F::A(_x, y) | F::B(_x, y) => { y }, - | ~~ ~~ + | + + error: unused variable: `a` --> $DIR/issue-56685.rs:27:14 @@ -46,7 +46,7 @@ LL | let _ = if let F::A(x, y) | F::B(x, y) = F::A(1, 2) { help: if this is intentional, prefix it with an underscore | LL | let _ = if let F::A(_x, y) | F::B(_x, y) = F::A(1, 2) { - | ~~ ~~ + | + + error: unused variable: `x` --> $DIR/issue-56685.rs:39:20 @@ -57,7 +57,7 @@ LL | while let F::A(x, y) | F::B(x, y) = F::A(1, 2) { help: if this is intentional, prefix it with an underscore | LL | while let F::A(_x, y) | F::B(_x, y) = F::A(1, 2) { - | ~~ ~~ + | + + error: aborting due to 6 previous errors diff --git a/tests/ui/match/match-bot-panic.rs b/tests/ui/match/match-bot-panic.rs index a155b5fb3f27e..7ec07ae290d98 100644 --- a/tests/ui/match/match-bot-panic.rs +++ b/tests/ui/match/match-bot-panic.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:explicit panic -//@ ignore-emscripten no processes +//@ needs-subprocess #![allow(unreachable_code)] #![allow(unused_variables)] diff --git a/tests/ui/match/match-disc-bot.rs b/tests/ui/match/match-disc-bot.rs index fdb98a0accb8f..d65dc5aea0753 100644 --- a/tests/ui/match/match-disc-bot.rs +++ b/tests/ui/match/match-disc-bot.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:quux -//@ ignore-emscripten no processes +//@ needs-subprocess fn f() -> ! { panic!("quux") diff --git a/tests/ui/match/match-pattern-field-mismatch.stderr b/tests/ui/match/match-pattern-field-mismatch.stderr index cde7ac972ca88..ea5f6f0f22bb9 100644 --- a/tests/ui/match/match-pattern-field-mismatch.stderr +++ b/tests/ui/match/match-pattern-field-mismatch.stderr @@ -13,8 +13,9 @@ LL | Color::Rgb(_, _, _) => { } | +++ help: use `..` to ignore all fields | -LL | Color::Rgb(..) => { } - | ~~ +LL - Color::Rgb(_, _) => { } +LL + Color::Rgb(..) => { } + | error: aborting due to 1 previous error diff --git a/tests/ui/match/match-wildcards.rs b/tests/ui/match/match-wildcards.rs index 4fddee6666eac..1cc81a7c4159a 100644 --- a/tests/ui/match/match-wildcards.rs +++ b/tests/ui/match/match-wildcards.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:squirrelcupcake -//@ ignore-emscripten no processes +//@ needs-subprocess fn cmp() -> isize { match (Some('a'), None::) { diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2021.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2021.rs deleted file mode 100644 index afea249ffef0b..0000000000000 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2021.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ run-pass -//@ edition: 2021 -//@ revisions: classic structural both -#![allow(incomplete_features)] -#![cfg_attr(any(classic, both), feature(ref_pat_eat_one_layer_2024))] -#![cfg_attr(any(structural, both), feature(ref_pat_eat_one_layer_2024_structural))] - -pub fn main() { - if let &Some(Some(x)) = &Some(&mut Some(0)) { - let _: &u32 = x; - } - if let Some(&x) = Some(&mut 0) { - let _: u32 = x; - } -} diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs deleted file mode 100644 index b145446de0a1c..0000000000000 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs +++ /dev/null @@ -1,88 +0,0 @@ -//@ run-pass -//@ edition: 2024 -//@ revisions: classic structural both -#![allow(incomplete_features)] -#![cfg_attr(any(classic, both), feature(ref_pat_eat_one_layer_2024))] -#![cfg_attr(any(structural, both), feature(ref_pat_eat_one_layer_2024_structural))] - -pub fn main() { - if let Some(Some(&x)) = &Some(&Some(0)) { - let _: u32 = x; - } - if let Some(Some(&x)) = &Some(Some(&0)) { - let _: &u32 = x; - } - if let Some(Some(&&x)) = &Some(Some(&0)) { - let _: u32 = x; - } - if let Some(&Some(x)) = &Some(Some(0)) { - let _: u32 = x; - } - if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { - let _: u32 = x; - } - if let Some(Some(&x)) = &Some(&Some(0)) { - let _: u32 = x; - } - if let Some(&Some(&x)) = &mut Some(&Some(0)) { - let _: u32 = x; - } - if let Some(&Some(x)) = &mut Some(&Some(0)) { - let _: &u32 = x; - } - if let Some(&Some(&mut ref x)) = Some(&Some(&mut 0)) { - let _: &u32 = x; - } - if let &Some(Some(x)) = &Some(&mut Some(0)) { - let _: &u32 = x; - } - if let Some(&Some(&x)) = &Some(&mut Some(0)) { - let _: u32 = x; - } - if let Some(&Some(&x)) = &Some(&Some(0)) { - let _: u32 = x; - } - if let Some(&Some(&x)) = &Some(&mut Some(0)) { - let _: u32 = x; - } - if let Some(&Some(Some(&x))) = &Some(Some(&mut Some(0))) { - let _: u32 = x; - } - if let Some(&Some(&x)) = Some(&Some(&mut 0)) { - let _: u32 = x; - } - if let Some(&Some(x)) = &mut Some(Some(0)) { - let _: u32 = x; - } - #[cfg(any(classic, both))] - if let Some(&mut x) = &mut Some(&0) { - let _: &u32 = x; - } - #[cfg(any(structural, both))] - if let Some(&mut x) = &Some(&mut 0) { - let _: &u32 = x; - } - - fn generic() -> (R, bool) { - R::meow() - } - - trait Ref: Sized { - fn meow() -> (Self, bool); - } - - impl Ref for &'static [(); 0] { - fn meow() -> (Self, bool) { - (&[], false) - } - } - - impl Ref for &'static mut [(); 0] { - fn meow() -> (Self, bool) { - (&mut [], true) - } - } - - let (&_, b) = generic(); - assert!(!b); -} diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.both.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.both.stderr deleted file mode 100644 index f8672d755b959..0000000000000 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.both.stderr +++ /dev/null @@ -1,169 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:8:17 - | -LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { - | ^^^^^^^^^^^^^ --------------- this expression has type `&Option<&Option<{integer}>>` - | | - | types differ in mutability - | - = note: expected reference `&Option<{integer}>` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:11:23 - | -LL | if let Some(&Some(&mut _)) = &Some(&mut Some(0)) { - | ^^^^^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` - | | - | expected integer, found `&mut _` - | - = note: expected type `{integer}` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:15:27 - | -LL | let _: &mut u32 = x; - | -------- ^ types differ in mutability - | | - | expected due to this - | - = note: expected mutable reference `&mut u32` - found reference `&{integer}` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:18:23 - | -LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { - | ^^^^^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>` - | | - | expected integer, found `&mut _` - | - = note: expected type `{integer}` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:21:29 - | -LL | if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) { - | ^^^^^^ ------------------------- this expression has type `&Option>>` - | | - | expected integer, found `&mut _` - | - = note: expected type `{integer}` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:24:17 - | -LL | if let Some(&mut Some(x)) = &Some(Some(0)) { - | ^^^^^^^^^^^^ -------------- this expression has type `&Option>` - | | - | expected `Option<{integer}>`, found `&mut _` - | - = note: expected enum `Option<{integer}>` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:27:17 - | -LL | if let Some(&mut Some(x)) = &Some(Some(0)) { - | ^^^^^^^^^^^^ -------------- this expression has type `&Option>` - | | - | expected `Option<{integer}>`, found `&mut _` - | - = note: expected enum `Option<{integer}>` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:31:9 - | -LL | let &mut _ = &&0; - | ^^^^^^ --- this expression has type `&&{integer}` - | | - | types differ in mutability - | - = note: expected reference `&&{integer}` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:34:9 - | -LL | let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; - | ^^^^^^ ----------------------------- this expression has type `&&&&&&&&&&&&&&&&&&&&&&&&&&&&{integer}` - | | - | types differ in mutability - | - = note: expected reference `&&&&&&&&&&&&&&&&&&&&&&&&&&&&{integer}` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:45:9 - | -LL | let &mut _ = &&mut 0; - | ^^^^^^ ------- this expression has type `&&mut {integer}` - | | - | types differ in mutability - | - = note: expected reference `&&mut {integer}` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:48:9 - | -LL | let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&mut 0; - | ^^^^^^ --------------------------------- this expression has type `&&&&&&&&&&&&&&&&&&&&&&&&&&&&mut {integer}` - | | - | types differ in mutability - | - = note: expected reference `&&&&&&&&&&&&&&&&&&&&&&&&&&&&mut {integer}` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:51:14 - | -LL | let &mut &mut &mut &mut _ = &mut &&&&mut &&&mut &mut 0; - | ^^^^^^^^^^^^^^^^ -------------------------- this expression has type `&mut &&&&mut &&&mut &mut {integer}` - | | - | types differ in mutability - | - = note: expected reference `&&&&mut &&&mut &mut {integer}` - found mutable reference `&mut _` - -error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:60:13 - | -LL | let Foo(mut a) = &Foo(0); - | ^^^^ - | - = note: see issue #123076 for more information - = help: add `#![feature(mut_ref)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:64:13 - | -LL | let Foo(mut a) = &mut Foo(0); - | ^^^^ - | - = note: see issue #123076 for more information - = help: add `#![feature(mut_ref)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0277]: the trait bound `&_: main::Ref` is not satisfied - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:82:14 - | -LL | let &_ = generic(); - | ^^^^^^^^^ the trait `main::Ref` is not implemented for `&_` - | - = help: the trait `main::Ref` is implemented for `&'static mut [(); 0]` -note: required by a bound in `generic` - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:68:19 - | -LL | fn generic() -> R { - | ^^^ required by this bound in `generic` - -error: aborting due to 15 previous errors - -Some errors have detailed explanations: E0277, E0308, E0658. -For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.classic.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.classic.stderr deleted file mode 100644 index a37316b3097b0..0000000000000 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.classic.stderr +++ /dev/null @@ -1,199 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:8:17 - | -LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { - | ^^^^^ - | - = note: cannot match inherited `&` with `&mut` pattern -help: replace this `&mut` pattern with `&` - | -LL | if let Some(&Some(&_)) = &Some(&Some(0)) { - | ~ - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:11:23 - | -LL | if let Some(&Some(&mut _)) = &Some(&mut Some(0)) { - | ^^^^^ - | - = note: cannot match inherited `&` with `&mut` pattern -help: replace this `&mut` pattern with `&` - | -LL | if let Some(&Some(&_)) = &Some(&mut Some(0)) { - | ~ - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:15:27 - | -LL | let _: &mut u32 = x; - | -------- ^ types differ in mutability - | | - | expected due to this - | - = note: expected mutable reference `&mut u32` - found reference `&{integer}` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:18:23 - | -LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { - | ^^^^^ - | - = note: cannot match inherited `&` with `&mut` pattern -help: replace this `&mut` pattern with `&` - | -LL | if let Some(&Some(&_)) = &mut Some(&Some(0)) { - | ~ - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:21:29 - | -LL | if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) { - | ^^^^^ - | - = note: cannot match inherited `&` with `&mut` pattern -help: replace this `&mut` pattern with `&` - | -LL | if let Some(&Some(Some((&_)))) = &Some(Some(&mut Some(0))) { - | ~ - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:24:17 - | -LL | if let Some(&mut Some(x)) = &Some(Some(0)) { - | ^^^^^ - | - = note: cannot match inherited `&` with `&mut` pattern -help: replace this `&mut` pattern with `&` - | -LL | if let Some(&Some(x)) = &Some(Some(0)) { - | ~ - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:27:17 - | -LL | if let Some(&mut Some(x)) = &Some(Some(0)) { - | ^^^^^ - | - = note: cannot match inherited `&` with `&mut` pattern -help: replace this `&mut` pattern with `&` - | -LL | if let Some(&Some(x)) = &Some(Some(0)) { - | ~ - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:31:9 - | -LL | let &mut _ = &&0; - | ^^^^^^ --- this expression has type `&&{integer}` - | | - | types differ in mutability - | - = note: expected reference `&&{integer}` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:34:9 - | -LL | let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; - | ^^^^^^ ----------------------------- this expression has type `&&&&&&&&&&&&&&&&&&&&&&&&&&&&{integer}` - | | - | types differ in mutability - | - = note: expected reference `&&&&&&&&&&&&&&&&&&&&&&&&&&&&{integer}` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:37:17 - | -LL | if let Some(&mut Some(&_)) = &Some(&mut Some(0)) { - | ^^^^^ - | - = note: cannot match inherited `&` with `&mut` pattern -help: replace this `&mut` pattern with `&` - | -LL | if let Some(&Some(&_)) = &Some(&mut Some(0)) { - | ~ - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:41:22 - | -LL | if let Some(Some(&mut x)) = &Some(Some(&mut 0)) { - | ^^^^^ - | - = note: cannot match inherited `&` with `&mut` pattern -help: replace this `&mut` pattern with `&` - | -LL | if let Some(Some(&x)) = &Some(Some(&mut 0)) { - | ~ - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:45:9 - | -LL | let &mut _ = &&mut 0; - | ^^^^^^ ------- this expression has type `&&mut {integer}` - | | - | types differ in mutability - | - = note: expected reference `&&mut {integer}` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:48:9 - | -LL | let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&mut 0; - | ^^^^^^ --------------------------------- this expression has type `&&&&&&&&&&&&&&&&&&&&&&&&&&&&mut {integer}` - | | - | types differ in mutability - | - = note: expected reference `&&&&&&&&&&&&&&&&&&&&&&&&&&&&mut {integer}` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:51:14 - | -LL | let &mut &mut &mut &mut _ = &mut &&&&mut &&&mut &mut 0; - | ^^^^^^^^^^^^^^^^ -------------------------- this expression has type `&mut &&&&mut &&&mut &mut {integer}` - | | - | types differ in mutability - | - = note: expected reference `&&&&mut &&&mut &mut {integer}` - found mutable reference `&mut _` - -error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:60:13 - | -LL | let Foo(mut a) = &Foo(0); - | ^^^^ - | - = note: see issue #123076 for more information - = help: add `#![feature(mut_ref)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:64:13 - | -LL | let Foo(mut a) = &mut Foo(0); - | ^^^^ - | - = note: see issue #123076 for more information - = help: add `#![feature(mut_ref)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0277]: the trait bound `&_: main::Ref` is not satisfied - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:82:14 - | -LL | let &_ = generic(); - | ^^^^^^^^^ the trait `main::Ref` is not implemented for `&_` - | - = help: the trait `main::Ref` is implemented for `&'static mut [(); 0]` -note: required by a bound in `generic` - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:68:19 - | -LL | fn generic() -> R { - | ^^^ required by this bound in `generic` - -error: aborting due to 17 previous errors - -Some errors have detailed explanations: E0277, E0308, E0658. -For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs deleted file mode 100644 index fd616807b285c..0000000000000 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs +++ /dev/null @@ -1,83 +0,0 @@ -//@ edition: 2024 -//@ revisions: classic structural both -#![allow(incomplete_features)] -#![cfg_attr(any(classic, both), feature(ref_pat_eat_one_layer_2024))] -#![cfg_attr(any(structural, both), feature(ref_pat_eat_one_layer_2024_structural))] - -pub fn main() { - if let Some(&mut Some(&_)) = &Some(&Some(0)) { - //~^ ERROR: mismatched types - } - if let Some(&Some(&mut _)) = &Some(&mut Some(0)) { - //~^ ERROR: mismatched types - } - if let Some(&Some(x)) = &mut Some(&Some(0)) { - let _: &mut u32 = x; - //~^ ERROR: mismatched types - } - if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { - //~^ ERROR: mismatched types - } - if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) { - //~^ ERROR: mismatched types - } - if let Some(&mut Some(x)) = &Some(Some(0)) { - //~^ ERROR: mismatched types - } - if let Some(&mut Some(x)) = &Some(Some(0)) { - //~^ ERROR: mismatched types - } - - let &mut _ = &&0; - //~^ ERROR: mismatched types - - let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; - //~^ ERROR: mismatched types - - if let Some(&mut Some(&_)) = &Some(&mut Some(0)) { - //[classic]~^ ERROR: mismatched types - } - - if let Some(Some(&mut x)) = &Some(Some(&mut 0)) { - //[classic]~^ ERROR: mismatched types - } - - let &mut _ = &&mut 0; - //~^ ERROR: mismatched types - - let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&mut 0; - //~^ ERROR: mismatched types - - let &mut &mut &mut &mut _ = &mut &&&&mut &&&mut &mut 0; - //~^ ERROR: mismatched types - - if let Some(&mut _) = &mut Some(&0) { - //[structural]~^ ERROR - } - - struct Foo(u8); - - let Foo(mut a) = &Foo(0); - //~^ ERROR: binding cannot be both mutable and by-reference - a = &42; - - let Foo(mut a) = &mut Foo(0); - //~^ ERROR: binding cannot be both mutable and by-reference - a = &mut 42; - - fn generic() -> R { - R::meow() - } - - trait Ref: Sized { - fn meow() -> Self; - } - - impl Ref for &'static mut [(); 0] { - fn meow() -> Self { - &mut [] - } - } - - let &_ = generic(); //~ERROR: the trait bound `&_: main::Ref` is not satisfied [E0277] -} diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.structural.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.structural.stderr deleted file mode 100644 index 2f62e9974fa3f..0000000000000 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.structural.stderr +++ /dev/null @@ -1,180 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:8:17 - | -LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { - | ^^^^^^^^^^^^^ --------------- this expression has type `&Option<&Option<{integer}>>` - | | - | types differ in mutability - | - = note: expected reference `&Option<{integer}>` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:11:23 - | -LL | if let Some(&Some(&mut _)) = &Some(&mut Some(0)) { - | ^^^^^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` - | | - | expected integer, found `&mut _` - | - = note: expected type `{integer}` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:15:27 - | -LL | let _: &mut u32 = x; - | -------- ^ types differ in mutability - | | - | expected due to this - | - = note: expected mutable reference `&mut u32` - found reference `&{integer}` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:18:23 - | -LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { - | ^^^^^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>` - | | - | expected integer, found `&mut _` - | - = note: expected type `{integer}` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:21:29 - | -LL | if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) { - | ^^^^^^ ------------------------- this expression has type `&Option>>` - | | - | expected integer, found `&mut _` - | - = note: expected type `{integer}` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:24:17 - | -LL | if let Some(&mut Some(x)) = &Some(Some(0)) { - | ^^^^^^^^^^^^ -------------- this expression has type `&Option>` - | | - | expected `Option<{integer}>`, found `&mut _` - | - = note: expected enum `Option<{integer}>` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:27:17 - | -LL | if let Some(&mut Some(x)) = &Some(Some(0)) { - | ^^^^^^^^^^^^ -------------- this expression has type `&Option>` - | | - | expected `Option<{integer}>`, found `&mut _` - | - = note: expected enum `Option<{integer}>` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:31:9 - | -LL | let &mut _ = &&0; - | ^^^^^^ --- this expression has type `&&{integer}` - | | - | types differ in mutability - | - = note: expected reference `&&{integer}` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:34:9 - | -LL | let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; - | ^^^^^^ ----------------------------- this expression has type `&&&&&&&&&&&&&&&&&&&&&&&&&&&&{integer}` - | | - | types differ in mutability - | - = note: expected reference `&&&&&&&&&&&&&&&&&&&&&&&&&&&&{integer}` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:45:9 - | -LL | let &mut _ = &&mut 0; - | ^^^^^^ ------- this expression has type `&&mut {integer}` - | | - | types differ in mutability - | - = note: expected reference `&&mut {integer}` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:48:9 - | -LL | let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&mut 0; - | ^^^^^^ --------------------------------- this expression has type `&&&&&&&&&&&&&&&&&&&&&&&&&&&&mut {integer}` - | | - | types differ in mutability - | - = note: expected reference `&&&&&&&&&&&&&&&&&&&&&&&&&&&&mut {integer}` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:51:14 - | -LL | let &mut &mut &mut &mut _ = &mut &&&&mut &&&mut &mut 0; - | ^^^^^^^^^^^^^^^^ -------------------------- this expression has type `&mut &&&&mut &&&mut &mut {integer}` - | | - | types differ in mutability - | - = note: expected reference `&&&&mut &&&mut &mut {integer}` - found mutable reference `&mut _` - -error[E0308]: mismatched types - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:54:17 - | -LL | if let Some(&mut _) = &mut Some(&0) { - | ^^^^^^ ------------- this expression has type `&mut Option<&{integer}>` - | | - | types differ in mutability - | - = note: expected reference `&{integer}` - found mutable reference `&mut _` - -error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:60:13 - | -LL | let Foo(mut a) = &Foo(0); - | ^^^^ - | - = note: see issue #123076 for more information - = help: add `#![feature(mut_ref)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: binding cannot be both mutable and by-reference - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:64:13 - | -LL | let Foo(mut a) = &mut Foo(0); - | ^^^^ - | - = note: see issue #123076 for more information - = help: add `#![feature(mut_ref)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0277]: the trait bound `&_: main::Ref` is not satisfied - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:82:14 - | -LL | let &_ = generic(); - | ^^^^^^^^^ the trait `main::Ref` is not implemented for `&_` - | - = help: the trait `main::Ref` is implemented for `&'static mut [(); 0]` -note: required by a bound in `generic` - --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:68:19 - | -LL | fn generic() -> R { - | ^^^ required by this bound in `generic` - -error: aborting due to 16 previous errors - -Some errors have detailed explanations: E0277, E0308, E0658. -For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.rs deleted file mode 100644 index 79403b1936528..0000000000000 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ edition: 2024 -#![allow(incomplete_features)] -#![feature(ref_pat_eat_one_layer_2024)] - -pub fn main() { - if let Some(&Some(x)) = Some(&Some(&mut 0)) { - //~^ ERROR: cannot move out of a shared reference [E0507] - let _: &u32 = x; - } - - let &ref mut x = &0; - //~^ cannot borrow data in a `&` reference as mutable [E0596] -} diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.stderr deleted file mode 100644 index a8b813941109c..0000000000000 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail2.stderr +++ /dev/null @@ -1,25 +0,0 @@ -error[E0507]: cannot move out of a shared reference - --> $DIR/ref_pat_eat_one_layer_2024_fail2.rs:6:29 - | -LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) { - | - ^^^^^^^^^^^^^^^^^^^ - | | - | data moved here - | move occurs because `x` has type `&mut u32`, which does not implement the `Copy` trait - | -help: consider removing the borrow - | -LL - if let Some(&Some(x)) = Some(&Some(&mut 0)) { -LL + if let Some(Some(x)) = Some(&Some(&mut 0)) { - | - -error[E0596]: cannot borrow data in a `&` reference as mutable - --> $DIR/ref_pat_eat_one_layer_2024_fail2.rs:11:10 - | -LL | let &ref mut x = &0; - | ^^^^^^^^^ cannot borrow as mutable - -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0507, E0596. -For more information about an error, try `rustc --explain E0507`. diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.fixed b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.fixed deleted file mode 100644 index e69d169966b52..0000000000000 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.fixed +++ /dev/null @@ -1,29 +0,0 @@ -//@ edition: 2024 -//@ run-rustfix -#![allow(incomplete_features)] -#![feature(ref_pat_eat_one_layer_2024)] - -pub fn main() { - if let Some(&mut Some(ref mut x)) = &mut Some(Some(0)) { - //~^ ERROR: cannot borrow as mutable inside an `&` pattern - let _: &mut u8 = x; - } - - if let &mut Some(Some(ref mut x)) = &mut Some(Some(0)) { - //~^ ERROR: cannot borrow as mutable inside an `&` pattern - let _: &mut u8 = x; - } - - macro_rules! pat { - ($var:ident) => { ref mut $var }; - } - let &mut pat!(x) = &mut 0; - //~^ ERROR: cannot borrow as mutable inside an `&` pattern - let _: &mut u8 = x; - - let &mut (ref mut a, ref mut b) = &mut (true, false); - //~^ ERROR: cannot borrow as mutable inside an `&` pattern - //~| ERROR: cannot borrow as mutable inside an `&` pattern - let _: &mut bool = a; - let _: &mut bool = b; -} diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs deleted file mode 100644 index a300cbcd4df52..0000000000000 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs +++ /dev/null @@ -1,29 +0,0 @@ -//@ edition: 2024 -//@ run-rustfix -#![allow(incomplete_features)] -#![feature(ref_pat_eat_one_layer_2024)] - -pub fn main() { - if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { - //~^ ERROR: cannot borrow as mutable inside an `&` pattern - let _: &mut u8 = x; - } - - if let &Some(Some(ref mut x)) = &mut Some(Some(0)) { - //~^ ERROR: cannot borrow as mutable inside an `&` pattern - let _: &mut u8 = x; - } - - macro_rules! pat { - ($var:ident) => { ref mut $var }; - } - let &pat!(x) = &mut 0; - //~^ ERROR: cannot borrow as mutable inside an `&` pattern - let _: &mut u8 = x; - - let &(ref mut a, ref mut b) = &mut (true, false); - //~^ ERROR: cannot borrow as mutable inside an `&` pattern - //~| ERROR: cannot borrow as mutable inside an `&` pattern - let _: &mut bool = a; - let _: &mut bool = b; -} diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.stderr deleted file mode 100644 index 8e135b65253fd..0000000000000 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_ref_mut_inside_and.stderr +++ /dev/null @@ -1,43 +0,0 @@ -error[E0596]: cannot borrow as mutable inside an `&` pattern - --> $DIR/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs:7:31 - | -LL | if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { - | - ^ - | | - | help: replace this `&` with `&mut`: `&mut` - -error[E0596]: cannot borrow as mutable inside an `&` pattern - --> $DIR/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs:12:31 - | -LL | if let &Some(Some(ref mut x)) = &mut Some(Some(0)) { - | - ^ - | | - | help: replace this `&` with `&mut`: `&mut` - -error[E0596]: cannot borrow as mutable inside an `&` pattern - --> $DIR/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs:20:15 - | -LL | let &pat!(x) = &mut 0; - | - ^ - | | - | help: replace this `&` with `&mut`: `&mut` - -error[E0596]: cannot borrow as mutable inside an `&` pattern - --> $DIR/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs:24:19 - | -LL | let &(ref mut a, ref mut b) = &mut (true, false); - | - ^ - | | - | help: replace this `&` with `&mut`: `&mut` - -error[E0596]: cannot borrow as mutable inside an `&` pattern - --> $DIR/ref_pat_eat_one_layer_2024_ref_mut_inside_and.rs:24:30 - | -LL | let &(ref mut a, ref mut b) = &mut (true, false); - | - ^ - | | - | help: replace this `&` with `&mut`: `&mut` - -error: aborting due to 5 previous errors - -For more information about this error, try `rustc --explain E0596`. diff --git a/tests/ui/meta/expected-error-correct-rev.a.stderr b/tests/ui/meta/expected-error-correct-rev.a.stderr index d5b7603d346d5..ae8dd86d36002 100644 --- a/tests/ui/meta/expected-error-correct-rev.a.stderr +++ b/tests/ui/meta/expected-error-correct-rev.a.stderr @@ -8,8 +8,9 @@ LL | let x: u32 = 22_usize; | help: change the type of the numeric literal from `usize` to `u32` | -LL | let x: u32 = 22_u32; - | ~~~ +LL - let x: u32 = 22_usize; +LL + let x: u32 = 22_u32; + | error: aborting due to 1 previous error diff --git a/tests/ui/meta/meta-expected-error-wrong-rev.a.stderr b/tests/ui/meta/meta-expected-error-wrong-rev.a.stderr index a489040f32df6..48fea28024f30 100644 --- a/tests/ui/meta/meta-expected-error-wrong-rev.a.stderr +++ b/tests/ui/meta/meta-expected-error-wrong-rev.a.stderr @@ -8,8 +8,9 @@ LL | let x: u32 = 22_usize; | help: change the type of the numeric literal from `usize` to `u32` | -LL | let x: u32 = 22_u32; - | ~~~ +LL - let x: u32 = 22_usize; +LL + let x: u32 = 22_u32; + | error: aborting due to 1 previous error diff --git a/tests/ui/meta/revision-ok.rs b/tests/ui/meta/revision-ok.rs index c1387f7d18e08..430d3d0a2397b 100644 --- a/tests/ui/meta/revision-ok.rs +++ b/tests/ui/meta/revision-ok.rs @@ -5,7 +5,7 @@ //@ revisions: foo bar //@[foo] error-pattern:foo //@[bar] error-pattern:bar -//@ ignore-emscripten no processes +//@ needs-subprocess #[cfg(foo)] fn die() { diff --git a/tests/ui/methods/bad-wf-when-selecting-method.rs b/tests/ui/methods/bad-wf-when-selecting-method.rs new file mode 100644 index 0000000000000..638d1ffa982fa --- /dev/null +++ b/tests/ui/methods/bad-wf-when-selecting-method.rs @@ -0,0 +1,18 @@ +trait Wf { + type Assoc; +} + +struct Wrapper, U>(T); + +trait Trait { + fn needs_sized(self); +} + +fn test(t: T) { + Wrapper(t).needs_sized(); + //~^ ERROR the trait bound `T: Wf` is not satisfied + //~| ERROR the trait bound `T: Wf` is not satisfied + //~| the method `needs_sized` exists for struct `Wrapper`, but its trait bounds were not satisfied +} + +fn main() {} diff --git a/tests/ui/methods/bad-wf-when-selecting-method.stderr b/tests/ui/methods/bad-wf-when-selecting-method.stderr new file mode 100644 index 0000000000000..e6d500349670c --- /dev/null +++ b/tests/ui/methods/bad-wf-when-selecting-method.stderr @@ -0,0 +1,54 @@ +error[E0277]: the trait bound `T: Wf` is not satisfied + --> $DIR/bad-wf-when-selecting-method.rs:12:13 + | +LL | Wrapper(t).needs_sized(); + | ------- ^ the trait `Wf` is not implemented for `T` + | | + | required by a bound introduced by this call + | +note: required by a bound in `Wrapper` + --> $DIR/bad-wf-when-selecting-method.rs:5:19 + | +LL | struct Wrapper, U>(T); + | ^^^^^^^^^^^^^ required by this bound in `Wrapper` +help: consider restricting type parameter `T` with trait `Wf` + | +LL | fn test(t: T) { + | ++++ + +error[E0277]: the trait bound `T: Wf` is not satisfied + --> $DIR/bad-wf-when-selecting-method.rs:12:5 + | +LL | Wrapper(t).needs_sized(); + | ^^^^^^^^^^ the trait `Wf` is not implemented for `T` + | +note: required by a bound in `Wrapper` + --> $DIR/bad-wf-when-selecting-method.rs:5:19 + | +LL | struct Wrapper, U>(T); + | ^^^^^^^^^^^^^ required by this bound in `Wrapper` +help: consider restricting type parameter `T` with trait `Wf` + | +LL | fn test(t: T) { + | ++++ + +error[E0599]: the method `needs_sized` exists for struct `Wrapper`, but its trait bounds were not satisfied + --> $DIR/bad-wf-when-selecting-method.rs:12:16 + | +LL | struct Wrapper, U>(T); + | ----------------------------------- method `needs_sized` not found for this struct +... +LL | Wrapper(t).needs_sized(); + | ^^^^^^^^^^^ method cannot be called on `Wrapper` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `T: Wf` +help: consider restricting the type parameter to satisfy the trait bound + | +LL | fn test(t: T) where T: Wf { + | +++++++++++ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0277, E0599. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/methods/disambiguate-associated-function-first-arg.stderr b/tests/ui/methods/disambiguate-associated-function-first-arg.stderr index 341b7a9100329..381e29667c824 100644 --- a/tests/ui/methods/disambiguate-associated-function-first-arg.stderr +++ b/tests/ui/methods/disambiguate-associated-function-first-arg.stderr @@ -25,16 +25,19 @@ LL | fn new(_a: Self, _b: i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: disambiguate the associated function for candidate #1 | -LL | ::new(1); - | ~~~~~~~~~~~~~~~~ +LL - _a.new(1); +LL + ::new(1); + | help: disambiguate the associated function for candidate #2 | -LL | ::new(_a, 1); - | ~~~~~~~~~~~~~~~~~~~~ +LL - _a.new(1); +LL + ::new(_a, 1); + | help: disambiguate the associated function for candidate #3 | -LL | ::new(_a, 1); - | ~~~~~~~~~~~~~~~~~~~~ +LL - _a.new(1); +LL + ::new(_a, 1); + | error[E0034]: multiple applicable items in scope --> $DIR/disambiguate-associated-function-first-arg.rs:47:7 @@ -54,12 +57,14 @@ LL | fn f(self) {} | ^^^^^^^^^^ help: disambiguate the method for candidate #1 | -LL | TraitA::f(S); - | ~~~~~~~~~~~~ +LL - S.f(); +LL + TraitA::f(S); + | help: disambiguate the method for candidate #2 | -LL | TraitB::f(S); - | ~~~~~~~~~~~~ +LL - S.f(); +LL + TraitB::f(S); + | error: aborting due to 2 previous errors diff --git a/tests/ui/methods/disambiguate-multiple-blanket-impl.stderr b/tests/ui/methods/disambiguate-multiple-blanket-impl.stderr index ccdd9a95451b6..1b81dc5aafb7f 100644 --- a/tests/ui/methods/disambiguate-multiple-blanket-impl.stderr +++ b/tests/ui/methods/disambiguate-multiple-blanket-impl.stderr @@ -6,10 +6,12 @@ LL | let _: S::Type; | help: use fully-qualified syntax | -LL | let _: ::Type; - | ~~~~~~~~~~~~~~ -LL | let _: ::Type; - | ~~~~~~~~~~~~~~ +LL - let _: S::Type; +LL + let _: ::Type; + | +LL - let _: S::Type; +LL + let _: ::Type; + | error[E0034]: multiple applicable items in scope --> $DIR/disambiguate-multiple-blanket-impl.rs:30:8 @@ -29,10 +31,12 @@ LL | fn foo(&self) {} | ^^^^^^^^^^^^^ help: use fully-qualified syntax to disambiguate | -LL | A::foo(&s); - | ~~~ -LL | B::foo(&s); - | ~~~ +LL - S::foo(&s); +LL + A::foo(&s); + | +LL - S::foo(&s); +LL + B::foo(&s); + | error[E0034]: multiple applicable items in scope --> $DIR/disambiguate-multiple-blanket-impl.rs:33:8 @@ -52,10 +56,12 @@ LL | const CONST: usize = 2; | ^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax to disambiguate | -LL | ::CONST; - | ~~~~~~~~~~ -LL | ::CONST; - | ~~~~~~~~~~ +LL - S::CONST; +LL + ::CONST; + | +LL - S::CONST; +LL + ::CONST; + | error: aborting due to 3 previous errors diff --git a/tests/ui/methods/disambiguate-multiple-impl.stderr b/tests/ui/methods/disambiguate-multiple-impl.stderr index 4172120770c6d..2563c2327b7a5 100644 --- a/tests/ui/methods/disambiguate-multiple-impl.stderr +++ b/tests/ui/methods/disambiguate-multiple-impl.stderr @@ -6,10 +6,12 @@ LL | let _: S::Type = (); | help: use fully-qualified syntax | -LL | let _: ::Type = (); - | ~~~~~~~~~~~~~~ -LL | let _: ::Type = (); - | ~~~~~~~~~~~~~~ +LL - let _: S::Type = (); +LL + let _: ::Type = (); + | +LL - let _: S::Type = (); +LL + let _: ::Type = (); + | error[E0034]: multiple applicable items in scope --> $DIR/disambiguate-multiple-impl.rs:29:8 @@ -29,10 +31,12 @@ LL | fn foo(&self) {} | ^^^^^^^^^^^^^ help: use fully-qualified syntax to disambiguate | -LL | A::foo(&s); - | ~~~ -LL | B::foo(&s); - | ~~~ +LL - S::foo(&s); +LL + A::foo(&s); + | +LL - S::foo(&s); +LL + B::foo(&s); + | error[E0034]: multiple applicable items in scope --> $DIR/disambiguate-multiple-impl.rs:34:16 @@ -52,10 +56,12 @@ LL | const CONST: usize = 2; | ^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax to disambiguate | -LL | let _ = ::CONST; - | ~~~~~~~~~~ -LL | let _ = ::CONST; - | ~~~~~~~~~~ +LL - let _ = S::CONST; +LL + let _ = ::CONST; + | +LL - let _ = S::CONST; +LL + let _ = ::CONST; + | error: aborting due to 3 previous errors diff --git a/tests/ui/methods/disambiguate-multiple-trait-2.stderr b/tests/ui/methods/disambiguate-multiple-trait-2.stderr index 2778f254a5619..08e264c20c893 100644 --- a/tests/ui/methods/disambiguate-multiple-trait-2.stderr +++ b/tests/ui/methods/disambiguate-multiple-trait-2.stderr @@ -12,12 +12,14 @@ LL | let _: T::Type; | help: use fully-qualified syntax to disambiguate | -LL | let _: ::Type; - | ~~~~~~~~~~ +LL - let _: T::Type; +LL + let _: ::Type; + | help: use fully-qualified syntax to disambiguate | -LL | let _: ::Type; - | ~~~~~~~~~~ +LL - let _: T::Type; +LL + let _: ::Type; + | error[E0034]: multiple applicable items in scope --> $DIR/disambiguate-multiple-trait-2.rs:16:7 @@ -37,12 +39,14 @@ LL | fn foo(&self); | ^^^^^^^^^^^^^^ help: disambiguate the method for candidate #1 | -LL | A::foo(&t); - | ~~~~~~~~~~ +LL - t.foo(); +LL + A::foo(&t); + | help: disambiguate the method for candidate #2 | -LL | B::foo(&t); - | ~~~~~~~~~~ +LL - t.foo(); +LL + B::foo(&t); + | error[E0034]: multiple applicable items in scope --> $DIR/disambiguate-multiple-trait-2.rs:20:16 @@ -62,10 +66,12 @@ LL | const CONST: usize; | ^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax to disambiguate | -LL | let _ = ::CONST; - | ~~~~~~~~~~ -LL | let _ = ::CONST; - | ~~~~~~~~~~ +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | +LL - let _ = T::CONST; +LL + let _ = ::CONST; + | error[E0223]: ambiguous associated type --> $DIR/disambiguate-multiple-trait-2.rs:52:12 @@ -75,10 +81,12 @@ LL | let _: S::Type; | help: use fully-qualified syntax | -LL | let _: ::Type; - | ~~~~~~~~~~~~~~ -LL | let _: ::Type; - | ~~~~~~~~~~~~~~ +LL - let _: S::Type; +LL + let _: ::Type; + | +LL - let _: S::Type; +LL + let _: ::Type; + | error[E0034]: multiple applicable items in scope --> $DIR/disambiguate-multiple-trait-2.rs:46:8 @@ -98,10 +106,12 @@ LL | fn foo(&self) {} | ^^^^^^^^^^^^^ help: use fully-qualified syntax to disambiguate | -LL | A::foo(&s); - | ~~~ -LL | B::foo(&s); - | ~~~ +LL - S::foo(&s); +LL + A::foo(&s); + | +LL - S::foo(&s); +LL + B::foo(&s); + | error[E0034]: multiple applicable items in scope --> $DIR/disambiguate-multiple-trait-2.rs:49:16 @@ -121,10 +131,12 @@ LL | const CONST: usize = 1; | ^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax to disambiguate | -LL | let _ = ::CONST; - | ~~~~~~~~~~ -LL | let _ = ::CONST; - | ~~~~~~~~~~ +LL - let _ = S::CONST; +LL + let _ = ::CONST; + | +LL - let _ = S::CONST; +LL + let _ = ::CONST; + | error: aborting due to 6 previous errors diff --git a/tests/ui/methods/disambiguate-multiple-trait.stderr b/tests/ui/methods/disambiguate-multiple-trait.stderr index e00498ca62b43..a977fe2cd033d 100644 --- a/tests/ui/methods/disambiguate-multiple-trait.stderr +++ b/tests/ui/methods/disambiguate-multiple-trait.stderr @@ -6,10 +6,12 @@ LL | let _: S::Type; | help: use fully-qualified syntax | -LL | let _: ::Type; - | ~~~~~~~~~~~~~~ -LL | let _: ::Type; - | ~~~~~~~~~~~~~~ +LL - let _: S::Type; +LL + let _: ::Type; + | +LL - let _: S::Type; +LL + let _: ::Type; + | error[E0034]: multiple applicable items in scope --> $DIR/disambiguate-multiple-trait.rs:24:8 @@ -29,10 +31,12 @@ LL | fn foo(&self) {} | ^^^^^^^^^^^^^ help: use fully-qualified syntax to disambiguate | -LL | A::foo(&s); - | ~~~ -LL | B::foo(&s); - | ~~~ +LL - S::foo(&s); +LL + A::foo(&s); + | +LL - S::foo(&s); +LL + B::foo(&s); + | error[E0034]: multiple applicable items in scope --> $DIR/disambiguate-multiple-trait.rs:27:16 @@ -52,10 +56,12 @@ LL | const CONST: usize = 2; | ^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax to disambiguate | -LL | let _ = ::CONST; - | ~~~~~~~~~~ -LL | let _ = ::CONST; - | ~~~~~~~~~~ +LL - let _ = S::CONST; +LL + let _ = ::CONST; + | +LL - let _ = S::CONST; +LL + let _ = ::CONST; + | error: aborting due to 3 previous errors diff --git a/tests/ui/methods/inherent-bound-in-probe.stderr b/tests/ui/methods/inherent-bound-in-probe.stderr index 8d7cc462280d6..433ef02a2aa4c 100644 --- a/tests/ui/methods/inherent-bound-in-probe.stderr +++ b/tests/ui/methods/inherent-bound-in-probe.stderr @@ -9,10 +9,10 @@ note: required by a bound in `std::iter::IntoIterator::IntoIter` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL error[E0275]: overflow evaluating the requirement `&_: IntoIterator` - --> $DIR/inherent-bound-in-probe.rs:44:17 + --> $DIR/inherent-bound-in-probe.rs:44:9 | LL | Helper::new(&self.0) - | ^^^ + | ^^^^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inherent_bound_in_probe`) note: required for `&BitReaderWrapper<_>` to implement `IntoIterator` @@ -25,11 +25,14 @@ LL | &'a T: IntoIterator, | ------------- unsatisfied trait bound introduced here = note: 126 redundant requirements hidden = note: required for `&BitReaderWrapper>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` to implement `IntoIterator` -note: required by a bound in `Helper<'a, T>` - --> $DIR/inherent-bound-in-probe.rs:25:25 +note: required by a bound in `Helper` + --> $DIR/inherent-bound-in-probe.rs:18:12 | +LL | struct Helper<'a, T> + | ------ required by a bound in this struct +LL | where LL | &'a T: IntoIterator, - | ^^^^^^^^^^^^^ required by this bound in `Helper<'a, T>` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Helper` error: aborting due to 2 previous errors diff --git a/tests/ui/methods/issues/issue-105732.stderr b/tests/ui/methods/issues/issue-105732.stderr index a4924b3e663b7..6244f983550ee 100644 --- a/tests/ui/methods/issues/issue-105732.stderr +++ b/tests/ui/methods/issues/issue-105732.stderr @@ -14,8 +14,9 @@ LL | self.g(); | help: there is a method `f` with a similar name | -LL | self.f(); - | ~ +LL - self.g(); +LL + self.f(); + | error: aborting due to 2 previous errors diff --git a/tests/ui/methods/issues/issue-61525.stderr b/tests/ui/methods/issues/issue-61525.stderr index 35001ae22a6c7..7ac3d3dc0cff0 100644 --- a/tests/ui/methods/issues/issue-61525.stderr +++ b/tests/ui/methods/issues/issue-61525.stderr @@ -32,7 +32,7 @@ note: method defined here --> $DIR/issue-61525.rs:2:8 | LL | fn query(self, q: Q); - | ^^^^^ + | ^^^^^ - error: aborting due to 2 previous errors diff --git a/tests/ui/methods/issues/issue-90315.stderr b/tests/ui/methods/issues/issue-90315.stderr index 0466bb0a0c997..e495701a3d7bc 100644 --- a/tests/ui/methods/issues/issue-90315.stderr +++ b/tests/ui/methods/issues/issue-90315.stderr @@ -182,7 +182,7 @@ LL | let _res: i32 = ..6.take(2).sum(); help: you must specify a concrete type for this numeric value, like `i32` | LL | let _res: i32 = ..6_i32.take(2).sum(); - | ~~~~~ + | ++++ error: aborting due to 18 previous errors diff --git a/tests/ui/methods/method-ambig-one-trait-unknown-int-type.stderr b/tests/ui/methods/method-ambig-one-trait-unknown-int-type.stderr index b2d2d039ff6a2..a5f1b76702f57 100644 --- a/tests/ui/methods/method-ambig-one-trait-unknown-int-type.stderr +++ b/tests/ui/methods/method-ambig-one-trait-unknown-int-type.stderr @@ -25,8 +25,9 @@ LL | impl Foo for Vec { | ^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | as Foo>::foo(&x); - | ++++++++++++++++++++++ ~ +LL - x.foo(); +LL + as Foo>::foo(&x); + | error[E0308]: mismatched types --> $DIR/method-ambig-one-trait-unknown-int-type.rs:33:20 diff --git a/tests/ui/methods/method-ambig-two-traits-cross-crate.stderr b/tests/ui/methods/method-ambig-two-traits-cross-crate.stderr index 0fc0c909ea8f8..707c33c3d9488 100644 --- a/tests/ui/methods/method-ambig-two-traits-cross-crate.stderr +++ b/tests/ui/methods/method-ambig-two-traits-cross-crate.stderr @@ -12,12 +12,14 @@ LL | impl Me2 for usize { fn me(&self) -> usize { *self } } = note: candidate #2 is defined in an impl of the trait `Me` for the type `usize` help: disambiguate the method for candidate #1 | -LL | fn main() { Me2::me(&1_usize); } - | ~~~~~~~~~~~~~~~~~ +LL - fn main() { 1_usize.me(); } +LL + fn main() { Me2::me(&1_usize); } + | help: disambiguate the method for candidate #2 | -LL | fn main() { Me::me(&1_usize); } - | ~~~~~~~~~~~~~~~~ +LL - fn main() { 1_usize.me(); } +LL + fn main() { Me::me(&1_usize); } + | error: aborting due to 1 previous error diff --git a/tests/ui/methods/method-ambig-two-traits-from-bounds.stderr b/tests/ui/methods/method-ambig-two-traits-from-bounds.stderr index 690f979fa37de..f3aa158239840 100644 --- a/tests/ui/methods/method-ambig-two-traits-from-bounds.stderr +++ b/tests/ui/methods/method-ambig-two-traits-from-bounds.stderr @@ -16,12 +16,14 @@ LL | trait B { fn foo(&self); } | ^^^^^^^^^^^^^^ help: disambiguate the method for candidate #1 | -LL | A::foo(&t); - | ~~~~~~~~~~ +LL - t.foo(); +LL + A::foo(&t); + | help: disambiguate the method for candidate #2 | -LL | B::foo(&t); - | ~~~~~~~~~~ +LL - t.foo(); +LL + B::foo(&t); + | error: aborting due to 1 previous error diff --git a/tests/ui/methods/method-ambig-two-traits-from-impls.stderr b/tests/ui/methods/method-ambig-two-traits-from-impls.stderr index 8be6d6d64f7e5..d1c50e8b3d017 100644 --- a/tests/ui/methods/method-ambig-two-traits-from-impls.stderr +++ b/tests/ui/methods/method-ambig-two-traits-from-impls.stderr @@ -16,12 +16,14 @@ LL | fn foo(self) {} | ^^^^^^^^^^^^ help: disambiguate the method for candidate #1 | -LL | A::foo(AB {}); - | ~~~~~~~~~~~~~ +LL - AB {}.foo(); +LL + A::foo(AB {}); + | help: disambiguate the method for candidate #2 | -LL | B::foo(AB {}); - | ~~~~~~~~~~~~~ +LL - AB {}.foo(); +LL + B::foo(AB {}); + | error: aborting due to 1 previous error diff --git a/tests/ui/methods/method-ambig-two-traits-from-impls2.stderr b/tests/ui/methods/method-ambig-two-traits-from-impls2.stderr index 333520847f835..788f1a4c4b3b1 100644 --- a/tests/ui/methods/method-ambig-two-traits-from-impls2.stderr +++ b/tests/ui/methods/method-ambig-two-traits-from-impls2.stderr @@ -16,10 +16,12 @@ LL | fn foo() {} | ^^^^^^^^ help: use fully-qualified syntax to disambiguate | -LL | ::foo(); - | ~~~~~~~~~~~ -LL | ::foo(); - | ~~~~~~~~~~~ +LL - AB::foo(); +LL + ::foo(); + | +LL - AB::foo(); +LL + ::foo(); + | error: aborting due to 1 previous error diff --git a/tests/ui/methods/method-ambig-two-traits-with-default-method.stderr b/tests/ui/methods/method-ambig-two-traits-with-default-method.stderr index b36ef77fb7ea9..605c2a85b070c 100644 --- a/tests/ui/methods/method-ambig-two-traits-with-default-method.stderr +++ b/tests/ui/methods/method-ambig-two-traits-with-default-method.stderr @@ -16,12 +16,14 @@ LL | trait Foo { fn method(&self) {} } | ^^^^^^^^^^^^^^^^ help: disambiguate the method for candidate #1 | -LL | Bar::method(&1_usize); - | ~~~~~~~~~~~~~~~~~~~~~ +LL - 1_usize.method(); +LL + Bar::method(&1_usize); + | help: disambiguate the method for candidate #2 | -LL | Foo::method(&1_usize); - | ~~~~~~~~~~~~~~~~~~~~~ +LL - 1_usize.method(); +LL + Foo::method(&1_usize); + | error: aborting due to 1 previous error diff --git a/tests/ui/methods/method-ambiguity-no-rcvr.stderr b/tests/ui/methods/method-ambiguity-no-rcvr.stderr index 3b6eb07393ac8..c1a77a9973987 100644 --- a/tests/ui/methods/method-ambiguity-no-rcvr.stderr +++ b/tests/ui/methods/method-ambiguity-no-rcvr.stderr @@ -20,12 +20,14 @@ LL | fn foo() {} | ^^^^^^^^ help: disambiguate the associated function for candidate #1 | -LL | ::foo(); - | ~~~~~~~~~~~~~~~~~~~ +LL - Qux.foo(); +LL + ::foo(); + | help: disambiguate the associated function for candidate #2 | -LL | ::foo(); - | ~~~~~~~~~~~~~~~~~~~~~~ +LL - Qux.foo(); +LL + ::foo(); + | error: aborting due to 1 previous error diff --git a/tests/ui/methods/method-call-err-msg.stderr b/tests/ui/methods/method-call-err-msg.stderr index 84005119a8780..7cda928aca9d3 100644 --- a/tests/ui/methods/method-call-err-msg.stderr +++ b/tests/ui/methods/method-call-err-msg.stderr @@ -28,8 +28,9 @@ LL | fn one(self, _: isize) -> Foo { self } | ^^^ -------- help: provide the argument | -LL | .one(/* isize */) - | ~~~~~~~~~~~~~ +LL - .one() +LL + .one(/* isize */) + | error[E0061]: this method takes 2 arguments but 1 argument was supplied --> $DIR/method-call-err-msg.rs:15:7 @@ -41,11 +42,12 @@ note: method defined here --> $DIR/method-call-err-msg.rs:7:8 | LL | fn two(self, _: isize, _: isize) -> Foo { self } - | ^^^ -------- -------- + | ^^^ -------- help: provide the argument | -LL | .two(0, /* isize */); - | ~~~~~~~~~~~~~~~~ +LL - .two(0); +LL + .two(0, /* isize */); + | error[E0599]: `Foo` is not an iterator --> $DIR/method-call-err-msg.rs:19:7 @@ -82,8 +84,9 @@ LL | fn three(self, _: T, _: T, _: T) -> Foo { self } | ^^^^^ ---- ---- ---- help: provide the arguments | -LL | y.three::(/* usize */, /* usize */, /* usize */); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - y.three::(); +LL + y.three::(/* usize */, /* usize */, /* usize */); + | error: aborting due to 5 previous errors diff --git a/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr b/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr index 6159d87c73e7a..d6da3f2cc3988 100644 --- a/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr +++ b/tests/ui/methods/method-deref-to-same-trait-object-with-separate-params.stderr @@ -46,16 +46,19 @@ LL | fn foo(self: Smaht) -> u64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: disambiguate the method for candidate #1 | -LL | let z = FinalFoo::foo(&x); - | ~~~~~~~~~~~~~~~~~ +LL - let z = x.foo(); +LL + let z = FinalFoo::foo(&x); + | help: disambiguate the method for candidate #2 | -LL | let z = NuisanceFoo::foo(x); - | ~~~~~~~~~~~~~~~~~~~ +LL - let z = x.foo(); +LL + let z = NuisanceFoo::foo(x); + | help: disambiguate the method for candidate #3 | -LL | let z = X::foo(x); - | ~~~~~~~~~ +LL - let z = x.foo(); +LL + let z = X::foo(x); + | error[E0308]: mismatched types --> $DIR/method-deref-to-same-trait-object-with-separate-params.rs:139:24 diff --git a/tests/ui/methods/method-not-found-but-doc-alias.stderr b/tests/ui/methods/method-not-found-but-doc-alias.stderr index c49ffa8971f75..2164b7cbbf811 100644 --- a/tests/ui/methods/method-not-found-but-doc-alias.stderr +++ b/tests/ui/methods/method-not-found-but-doc-alias.stderr @@ -9,8 +9,9 @@ LL | Foo.quux(); | help: there is a method `bar` with a similar name | -LL | Foo.bar(); - | ~~~ +LL - Foo.quux(); +LL + Foo.bar(); + | error: aborting due to 1 previous error diff --git a/tests/ui/methods/method-on-ambiguous-numeric-type.stderr b/tests/ui/methods/method-on-ambiguous-numeric-type.stderr index 124270402727d..c9a549513077e 100644 --- a/tests/ui/methods/method-on-ambiguous-numeric-type.stderr +++ b/tests/ui/methods/method-on-ambiguous-numeric-type.stderr @@ -7,7 +7,7 @@ LL | let x = 2.0.neg(); help: you must specify a concrete type for this numeric value, like `f32` | LL | let x = 2.0_f32.neg(); - | ~~~~~~~ + | ++++ error[E0689]: can't call method `neg` on ambiguous numeric type `{float}` --> $DIR/method-on-ambiguous-numeric-type.rs:17:15 diff --git a/tests/ui/methods/suggest-convert-ptr-to-ref.stderr b/tests/ui/methods/suggest-convert-ptr-to-ref.stderr index 0e1565e251adc..7d52b20121e9b 100644 --- a/tests/ui/methods/suggest-convert-ptr-to-ref.stderr +++ b/tests/ui/methods/suggest-convert-ptr-to-ref.stderr @@ -47,8 +47,9 @@ LL | let _ = t.as_mut_ref(); | help: there is a method `as_mut` with a similar name | -LL | let _ = t.as_mut(); - | ~~~~~~ +LL - let _ = t.as_mut_ref(); +LL + let _ = t.as_mut(); + | error[E0599]: no method named `as_ref_mut` found for raw pointer `*mut u8` in the current scope --> $DIR/suggest-convert-ptr-to-ref.rs:13:15 @@ -58,8 +59,9 @@ LL | let _ = t.as_ref_mut(); | help: there is a method `as_mut` with a similar name | -LL | let _ = t.as_mut(); - | ~~~~~~ +LL - let _ = t.as_ref_mut(); +LL + let _ = t.as_mut(); + | error[E0599]: no method named `make_ascii_lowercase` found for raw pointer `*const u8` in the current scope --> $DIR/suggest-convert-ptr-to-ref.rs:16:7 diff --git a/tests/ui/methods/supertrait-shadowing/assoc-const.rs b/tests/ui/methods/supertrait-shadowing/assoc-const.rs new file mode 100644 index 0000000000000..a542ce7d326d1 --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/assoc-const.rs @@ -0,0 +1,23 @@ +//@ run-pass +//@ check-run-results + +#![feature(supertrait_item_shadowing)] +#![allow(dead_code)] + +trait A { + const CONST: i32; +} +impl A for T { + const CONST: i32 = 1; +} + +trait B: A { + const CONST: i32; +} +impl B for T { + const CONST: i32 = 2; +} + +fn main() { + println!("{}", i32::CONST); +} diff --git a/tests/ui/methods/supertrait-shadowing/assoc-const.run.stdout b/tests/ui/methods/supertrait-shadowing/assoc-const.run.stdout new file mode 100644 index 0000000000000..0cfbf08886fca --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/assoc-const.run.stdout @@ -0,0 +1 @@ +2 diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor-2.rs b/tests/ui/methods/supertrait-shadowing/common-ancestor-2.rs new file mode 100644 index 0000000000000..cecf6dccf9d16 --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/common-ancestor-2.rs @@ -0,0 +1,34 @@ +//@ run-pass +//@ check-run-results + +#![feature(supertrait_item_shadowing)] +#![warn(supertrait_item_shadowing_usage)] +#![warn(supertrait_item_shadowing_definition)] +#![allow(dead_code)] + +trait A { + fn hello(&self) { + println!("A"); + } +} +impl A for T {} + +trait B { + fn hello(&self) { + println!("B"); + } +} +impl B for T {} + +trait C: A + B { + fn hello(&self) { + //~^ WARN trait item `hello` from `C` shadows identically named item + println!("C"); + } +} +impl C for T {} + +fn main() { + ().hello(); + //~^ WARN trait item `hello` from `C` shadows identically named item from supertrait +} diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor-2.run.stdout b/tests/ui/methods/supertrait-shadowing/common-ancestor-2.run.stdout new file mode 100644 index 0000000000000..3cc58df837521 --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/common-ancestor-2.run.stdout @@ -0,0 +1 @@ +C diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor-2.stderr b/tests/ui/methods/supertrait-shadowing/common-ancestor-2.stderr new file mode 100644 index 0000000000000..934d5a0f7cfd3 --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/common-ancestor-2.stderr @@ -0,0 +1,47 @@ +warning: trait item `hello` from `C` shadows identically named item from supertrait + --> $DIR/common-ancestor-2.rs:24:5 + | +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `B` and `A` + --> $DIR/common-ancestor-2.rs:10:5 + | +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ +... +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/common-ancestor-2.rs:6:9 + | +LL | #![warn(supertrait_item_shadowing_definition)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: trait item `hello` from `C` shadows identically named item from supertrait + --> $DIR/common-ancestor-2.rs:32:8 + | +LL | ().hello(); + | ^^^^^ + | +note: item from `C` shadows a supertrait item + --> $DIR/common-ancestor-2.rs:24:5 + | +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ +note: items from several supertraits are shadowed: `A` and `B` + --> $DIR/common-ancestor-2.rs:10:5 + | +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ +... +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/common-ancestor-2.rs:5:9 + | +LL | #![warn(supertrait_item_shadowing_usage)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: 2 warnings emitted + diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor-3.rs b/tests/ui/methods/supertrait-shadowing/common-ancestor-3.rs new file mode 100644 index 0000000000000..040fa9aab3987 --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/common-ancestor-3.rs @@ -0,0 +1,44 @@ +//@ run-pass +//@ check-run-results + +#![feature(supertrait_item_shadowing)] +#![warn(supertrait_item_shadowing_usage)] +#![warn(supertrait_item_shadowing_definition)] +#![allow(dead_code)] + +trait A { + fn hello(&self) { + println!("A"); + } +} +impl A for T {} + +trait B { + fn hello(&self) { + println!("B"); + } +} +impl B for T {} + +trait C: A + B { + fn hello(&self) { + //~^ WARN trait item `hello` from `C` shadows identically named item + println!("C"); + } +} +impl C for T {} + +// `D` extends `C` which extends `B` and `A` + +trait D: C { + fn hello(&self) { + //~^ WARN trait item `hello` from `D` shadows identically named item + println!("D"); + } +} +impl D for T {} + +fn main() { + ().hello(); + //~^ WARN trait item `hello` from `D` shadows identically named item from supertrait +} diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor-3.run.stdout b/tests/ui/methods/supertrait-shadowing/common-ancestor-3.run.stdout new file mode 100644 index 0000000000000..178481050188c --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/common-ancestor-3.run.stdout @@ -0,0 +1 @@ +D diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor-3.stderr b/tests/ui/methods/supertrait-shadowing/common-ancestor-3.stderr new file mode 100644 index 0000000000000..28e44a2ff209a --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/common-ancestor-3.stderr @@ -0,0 +1,68 @@ +warning: trait item `hello` from `C` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:24:5 + | +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `B` and `A` + --> $DIR/common-ancestor-3.rs:10:5 + | +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ +... +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/common-ancestor-3.rs:6:9 + | +LL | #![warn(supertrait_item_shadowing_definition)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: trait item `hello` from `D` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:34:5 + | +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `C`, `B`, and `A` + --> $DIR/common-ancestor-3.rs:10:5 + | +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ +... +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ +... +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ + +warning: trait item `hello` from `D` shadows identically named item from supertrait + --> $DIR/common-ancestor-3.rs:42:8 + | +LL | ().hello(); + | ^^^^^ + | +note: item from `D` shadows a supertrait item + --> $DIR/common-ancestor-3.rs:34:5 + | +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ +note: items from several supertraits are shadowed: `A`, `B`, and `C` + --> $DIR/common-ancestor-3.rs:10:5 + | +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ +... +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ +... +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/common-ancestor-3.rs:5:9 + | +LL | #![warn(supertrait_item_shadowing_usage)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: 3 warnings emitted + diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor.rs b/tests/ui/methods/supertrait-shadowing/common-ancestor.rs new file mode 100644 index 0000000000000..8e67c93536755 --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/common-ancestor.rs @@ -0,0 +1,27 @@ +//@ run-pass +//@ check-run-results + +#![feature(supertrait_item_shadowing)] +#![warn(supertrait_item_shadowing_usage)] +#![warn(supertrait_item_shadowing_definition)] +#![allow(dead_code)] + +trait A { + fn hello(&self) { + println!("A"); + } +} +impl A for T {} + +trait B: A { + fn hello(&self) { + //~^ WARN trait item `hello` from `B` shadows identically named item + println!("B"); + } +} +impl B for T {} + +fn main() { + ().hello(); + //~^ WARN trait item `hello` from `B` shadows identically named item from supertrait +} diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor.run.stdout b/tests/ui/methods/supertrait-shadowing/common-ancestor.run.stdout new file mode 100644 index 0000000000000..223b7836fb19f --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/common-ancestor.run.stdout @@ -0,0 +1 @@ +B diff --git a/tests/ui/methods/supertrait-shadowing/common-ancestor.stderr b/tests/ui/methods/supertrait-shadowing/common-ancestor.stderr new file mode 100644 index 0000000000000..f404fa6b53a35 --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/common-ancestor.stderr @@ -0,0 +1,41 @@ +warning: trait item `hello` from `B` shadows identically named item from supertrait + --> $DIR/common-ancestor.rs:17:5 + | +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ + | +note: item from `A` is shadowed by a subtrait item + --> $DIR/common-ancestor.rs:10:5 + | +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/common-ancestor.rs:6:9 + | +LL | #![warn(supertrait_item_shadowing_definition)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: trait item `hello` from `B` shadows identically named item from supertrait + --> $DIR/common-ancestor.rs:25:8 + | +LL | ().hello(); + | ^^^^^ + | +note: item from `B` shadows a supertrait item + --> $DIR/common-ancestor.rs:17:5 + | +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ +note: item from `A` is shadowed by a subtrait item + --> $DIR/common-ancestor.rs:10:5 + | +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/common-ancestor.rs:5:9 + | +LL | #![warn(supertrait_item_shadowing_usage)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: 2 warnings emitted + diff --git a/tests/ui/methods/supertrait-shadowing/definition-site.rs b/tests/ui/methods/supertrait-shadowing/definition-site.rs new file mode 100644 index 0000000000000..2768a6a6b2717 --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/definition-site.rs @@ -0,0 +1,18 @@ +#![feature(supertrait_item_shadowing)] +#![deny(supertrait_item_shadowing_definition)] + +trait SuperSuper { + fn method(); +} + +trait Super: SuperSuper { + fn method(); + //~^ ERROR trait item `method` from `Super` shadows identically named item +} + +trait Sub: Super { + fn method(); + //~^ ERROR trait item `method` from `Sub` shadows identically named item +} + +fn main() {} diff --git a/tests/ui/methods/supertrait-shadowing/definition-site.stderr b/tests/ui/methods/supertrait-shadowing/definition-site.stderr new file mode 100644 index 0000000000000..f0bbf414a690c --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/definition-site.stderr @@ -0,0 +1,34 @@ +error: trait item `method` from `Super` shadows identically named item from supertrait + --> $DIR/definition-site.rs:9:5 + | +LL | fn method(); + | ^^^^^^^^^^^^ + | +note: item from `SuperSuper` is shadowed by a subtrait item + --> $DIR/definition-site.rs:5:5 + | +LL | fn method(); + | ^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/definition-site.rs:2:9 + | +LL | #![deny(supertrait_item_shadowing_definition)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: trait item `method` from `Sub` shadows identically named item from supertrait + --> $DIR/definition-site.rs:14:5 + | +LL | fn method(); + | ^^^^^^^^^^^^ + | +note: items from several supertraits are shadowed: `Super` and `SuperSuper` + --> $DIR/definition-site.rs:5:5 + | +LL | fn method(); + | ^^^^^^^^^^^^ +... +LL | fn method(); + | ^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/methods/supertrait-shadowing/false-subtrait-after-inference.rs b/tests/ui/methods/supertrait-shadowing/false-subtrait-after-inference.rs new file mode 100644 index 0000000000000..c3bc47e6740d8 --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/false-subtrait-after-inference.rs @@ -0,0 +1,26 @@ +#![feature(supertrait_item_shadowing)] +#![warn(supertrait_item_shadowing_usage)] +#![warn(supertrait_item_shadowing_definition)] + +struct W(T); + +trait Upstream { + fn hello(&self) {} +} +impl Upstream for T {} + +trait Downstream: Upstream { + fn hello(&self) {} + //~^ WARN trait item `hello` from `Downstream` shadows identically named item +} +impl Downstream for W where T: Foo {} + +trait Foo {} + +fn main() { + let x = W(Default::default()); + x.hello(); + //~^ ERROR the trait bound `i32: Foo` is not satisfied + //~| WARN trait item `hello` from `Downstream` shadows identically named item from supertrait + let _: i32 = x.0; +} diff --git a/tests/ui/methods/supertrait-shadowing/false-subtrait-after-inference.stderr b/tests/ui/methods/supertrait-shadowing/false-subtrait-after-inference.stderr new file mode 100644 index 0000000000000..47019ca7a320f --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/false-subtrait-after-inference.stderr @@ -0,0 +1,59 @@ +warning: trait item `hello` from `Downstream` shadows identically named item from supertrait + --> $DIR/false-subtrait-after-inference.rs:13:5 + | +LL | fn hello(&self) {} + | ^^^^^^^^^^^^^^^ + | +note: item from `Upstream` is shadowed by a subtrait item + --> $DIR/false-subtrait-after-inference.rs:8:5 + | +LL | fn hello(&self) {} + | ^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/false-subtrait-after-inference.rs:3:9 + | +LL | #![warn(supertrait_item_shadowing_definition)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: trait item `hello` from `Downstream` shadows identically named item from supertrait + --> $DIR/false-subtrait-after-inference.rs:22:7 + | +LL | x.hello(); + | ^^^^^ + | +note: item from `Downstream` shadows a supertrait item + --> $DIR/false-subtrait-after-inference.rs:13:5 + | +LL | fn hello(&self) {} + | ^^^^^^^^^^^^^^^ +note: item from `Upstream` is shadowed by a subtrait item + --> $DIR/false-subtrait-after-inference.rs:8:5 + | +LL | fn hello(&self) {} + | ^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/false-subtrait-after-inference.rs:2:9 + | +LL | #![warn(supertrait_item_shadowing_usage)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `i32: Foo` is not satisfied + --> $DIR/false-subtrait-after-inference.rs:22:7 + | +LL | x.hello(); + | ^^^^^ the trait `Foo` is not implemented for `i32` + | +help: this trait has no implementations, consider adding one + --> $DIR/false-subtrait-after-inference.rs:18:1 + | +LL | trait Foo {} + | ^^^^^^^^^ +note: required for `W` to implement `Downstream` + --> $DIR/false-subtrait-after-inference.rs:16:9 + | +LL | impl Downstream for W where T: Foo {} + | ^^^^^^^^^^ ^^^^ --- unsatisfied trait bound introduced here + +error: aborting due to 1 previous error; 2 warnings emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.rs b/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.rs new file mode 100644 index 0000000000000..2834ca31b7140 --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.rs @@ -0,0 +1,37 @@ +#![feature(supertrait_item_shadowing)] + +trait A { + fn hello(&self) { + println!("A"); + } +} +impl A for T {} + +trait B { + fn hello(&self) { + println!("B"); + } +} +impl B for T {} + +trait C: A + B { + fn hello(&self) { + println!("C"); + } +} +impl C for T {} + +// Since `D` is not a subtrait of `C`, +// we have no obvious lower bound. + +trait D: B { + fn hello(&self) { + println!("D"); + } +} +impl D for T {} + +fn main() { + ().hello(); + //~^ ERROR multiple applicable items in scope +} diff --git a/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.stderr b/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.stderr new file mode 100644 index 0000000000000..231cb9b1cb2bc --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/no-common-ancestor-2.stderr @@ -0,0 +1,50 @@ +error[E0034]: multiple applicable items in scope + --> $DIR/no-common-ancestor-2.rs:35:8 + | +LL | ().hello(); + | ^^^^^ multiple `hello` found + | +note: candidate #1 is defined in an impl of the trait `A` for the type `T` + --> $DIR/no-common-ancestor-2.rs:4:5 + | +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ +note: candidate #2 is defined in an impl of the trait `B` for the type `T` + --> $DIR/no-common-ancestor-2.rs:11:5 + | +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ +note: candidate #3 is defined in an impl of the trait `C` for the type `T` + --> $DIR/no-common-ancestor-2.rs:18:5 + | +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ +note: candidate #4 is defined in an impl of the trait `D` for the type `T` + --> $DIR/no-common-ancestor-2.rs:28:5 + | +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ +help: disambiguate the method for candidate #1 + | +LL - ().hello(); +LL + A::hello(&()); + | +help: disambiguate the method for candidate #2 + | +LL - ().hello(); +LL + B::hello(&()); + | +help: disambiguate the method for candidate #3 + | +LL - ().hello(); +LL + C::hello(&()); + | +help: disambiguate the method for candidate #4 + | +LL - ().hello(); +LL + D::hello(&()); + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0034`. diff --git a/tests/ui/methods/supertrait-shadowing/no-common-ancestor.rs b/tests/ui/methods/supertrait-shadowing/no-common-ancestor.rs new file mode 100644 index 0000000000000..7323439d5884f --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/no-common-ancestor.rs @@ -0,0 +1,20 @@ +#![feature(supertrait_item_shadowing)] + +trait A { + fn hello(&self) { + println!("A"); + } +} +impl A for T {} + +trait B { + fn hello(&self) { + println!("B"); + } +} +impl B for T {} + +fn main() { + ().hello(); + //~^ ERROR multiple applicable items in scope +} diff --git a/tests/ui/methods/supertrait-shadowing/no-common-ancestor.stderr b/tests/ui/methods/supertrait-shadowing/no-common-ancestor.stderr new file mode 100644 index 0000000000000..4e83f60c76514 --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/no-common-ancestor.stderr @@ -0,0 +1,30 @@ +error[E0034]: multiple applicable items in scope + --> $DIR/no-common-ancestor.rs:18:8 + | +LL | ().hello(); + | ^^^^^ multiple `hello` found + | +note: candidate #1 is defined in an impl of the trait `A` for the type `T` + --> $DIR/no-common-ancestor.rs:4:5 + | +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ +note: candidate #2 is defined in an impl of the trait `B` for the type `T` + --> $DIR/no-common-ancestor.rs:11:5 + | +LL | fn hello(&self) { + | ^^^^^^^^^^^^^^^ +help: disambiguate the method for candidate #1 + | +LL - ().hello(); +LL + A::hello(&()); + | +help: disambiguate the method for candidate #2 + | +LL - ().hello(); +LL + B::hello(&()); + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0034`. diff --git a/tests/ui/methods/supertrait-shadowing/out-of-scope.rs b/tests/ui/methods/supertrait-shadowing/out-of-scope.rs new file mode 100644 index 0000000000000..bd263be59cc7d --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/out-of-scope.rs @@ -0,0 +1,25 @@ +//@ run-pass +//@ check-run-results + +#![feature(supertrait_item_shadowing)] +#![allow(dead_code)] + +mod out_of_scope { + pub trait Subtrait: super::Supertrait { + fn hello(&self) { + println!("subtrait"); + } + } + impl Subtrait for T {} +} + +trait Supertrait { + fn hello(&self) { + println!("supertrait"); + } +} +impl Supertrait for T {} + +fn main() { + ().hello(); +} diff --git a/tests/ui/methods/supertrait-shadowing/out-of-scope.run.stdout b/tests/ui/methods/supertrait-shadowing/out-of-scope.run.stdout new file mode 100644 index 0000000000000..1019e5f354346 --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/out-of-scope.run.stdout @@ -0,0 +1 @@ +supertrait diff --git a/tests/ui/methods/supertrait-shadowing/trivially-false-subtrait.rs b/tests/ui/methods/supertrait-shadowing/trivially-false-subtrait.rs new file mode 100644 index 0000000000000..e44c7c18083d0 --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/trivially-false-subtrait.rs @@ -0,0 +1,26 @@ +//@ check-pass + +// Make sure we don't prefer a subtrait that we would've otherwise eliminated +// in `consider_probe` during method probing. + +#![feature(supertrait_item_shadowing)] +#![allow(dead_code)] + +struct W(T); + +trait Upstream { + fn hello(&self) {} +} +impl Upstream for T {} + +trait Downstream: Upstream { + fn hello(&self) {} +} +impl Downstream for W where T: Foo {} + +trait Foo {} + +fn main() { + let x = W(1i32); + x.hello(); +} diff --git a/tests/ui/methods/supertrait-shadowing/type-dependent.rs b/tests/ui/methods/supertrait-shadowing/type-dependent.rs new file mode 100644 index 0000000000000..3af884fd52dcd --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/type-dependent.rs @@ -0,0 +1,29 @@ +//@ run-pass +//@ check-run-results + +// Makes sure we can shadow with type-dependent method syntax. + +#![feature(supertrait_item_shadowing)] +#![allow(dead_code)] + +trait A { + fn hello() { + println!("A"); + } +} +impl A for T {} + +trait B: A { + fn hello() { + println!("B"); + } +} +impl B for T {} + +fn foo() { + T::hello(); +} + +fn main() { + foo::<()>(); +} diff --git a/tests/ui/methods/supertrait-shadowing/type-dependent.run.stdout b/tests/ui/methods/supertrait-shadowing/type-dependent.run.stdout new file mode 100644 index 0000000000000..223b7836fb19f --- /dev/null +++ b/tests/ui/methods/supertrait-shadowing/type-dependent.run.stdout @@ -0,0 +1 @@ +B diff --git a/tests/ui/mir/issue-106062.stderr b/tests/ui/mir/issue-106062.stderr index 30635148dae64..19a1d06e0be09 100644 --- a/tests/ui/mir/issue-106062.stderr +++ b/tests/ui/mir/issue-106062.stderr @@ -6,10 +6,12 @@ LL | async fn connection_handler(handler: impl Sized) -> Result Result { - | ~~~~~~~~~~~~~~~~~~~~ -LL | async fn connection_handler(handler: impl Sized) -> Result { - | ~~~~~~~~~~~~~~~~~~~ +LL - async fn connection_handler(handler: impl Sized) -> Result { +LL + async fn connection_handler(handler: impl Sized) -> Result { + | +LL - async fn connection_handler(handler: impl Sized) -> Result { +LL + async fn connection_handler(handler: impl Sized) -> Result { + | error: aborting due to 1 previous error diff --git a/tests/ui/mir/issue-112269.stderr b/tests/ui/mir/issue-112269.stderr index 80f329e2ce026..fc8bf5d67b559 100644 --- a/tests/ui/mir/issue-112269.stderr +++ b/tests/ui/mir/issue-112269.stderr @@ -12,7 +12,7 @@ LL | let x: i32 = 3; help: introduce a variable instead | LL | let x_var: i32 = 3; - | ~~~~~ + | ++++ error[E0005]: refutable pattern in local binding --> $DIR/issue-112269.rs:7:9 @@ -28,7 +28,7 @@ LL | let y = 4; help: introduce a variable instead | LL | let y_var = 4; - | ~~~~~ + | ++++ error: aborting due to 2 previous errors diff --git a/tests/ui/mir/issue-121103.rs b/tests/ui/mir/issue-121103.rs index e06361a6964c0..247c644c5bb19 100644 --- a/tests/ui/mir/issue-121103.rs +++ b/tests/ui/mir/issue-121103.rs @@ -1,3 +1,3 @@ fn main(_: as lib2::TypeFn>::Output) {} -//~^ ERROR failed to resolve: use of undeclared crate or module `lib2` -//~| ERROR failed to resolve: use of undeclared crate or module `lib2` +//~^ ERROR failed to resolve: use of unresolved module or unlinked crate `lib2` +//~| ERROR failed to resolve: use of unresolved module or unlinked crate `lib2` diff --git a/tests/ui/mir/issue-121103.stderr b/tests/ui/mir/issue-121103.stderr index 913eee9e0c503..3565f6f0cdeff 100644 --- a/tests/ui/mir/issue-121103.stderr +++ b/tests/ui/mir/issue-121103.stderr @@ -1,14 +1,18 @@ -error[E0433]: failed to resolve: use of undeclared crate or module `lib2` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `lib2` --> $DIR/issue-121103.rs:1:38 | LL | fn main(_: as lib2::TypeFn>::Output) {} - | ^^^^ use of undeclared crate or module `lib2` + | ^^^^ use of unresolved module or unlinked crate `lib2` + | + = help: you might be missing a crate named `lib2` -error[E0433]: failed to resolve: use of undeclared crate or module `lib2` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `lib2` --> $DIR/issue-121103.rs:1:13 | LL | fn main(_: as lib2::TypeFn>::Output) {} - | ^^^^ use of undeclared crate or module `lib2` + | ^^^^ use of unresolved module or unlinked crate `lib2` + | + = help: you might be missing a crate named `lib2` error: aborting due to 2 previous errors diff --git a/tests/ui/mir/lint/storage-live.stderr b/tests/ui/mir/lint/storage-live.stderr index c701231951223..651b8e2327ee6 100644 --- a/tests/ui/mir/lint/storage-live.stderr +++ b/tests/ui/mir/lint/storage-live.stderr @@ -1,4 +1,4 @@ -error: internal compiler error: broken MIR in Item(DefId(0:8 ~ storage_live[HASH]::multiple_storage)) (after pass CheckPackedRef) at bb0[1]: +error: internal compiler error: broken MIR in Item(DefId(0:8 ~ storage_live[HASH]::multiple_storage)) (after pass CheckForceInline) at bb0[1]: StorageLive(_1) which already has storage here --> $DIR/storage-live.rs:23:13 | diff --git a/tests/ui/mir/mir_codegen_calls_converging_drops.rs b/tests/ui/mir/mir_codegen_calls_converging_drops.rs index 5c3c8b999b2de..8a6d3926de24a 100644 --- a/tests/ui/mir/mir_codegen_calls_converging_drops.rs +++ b/tests/ui/mir/mir_codegen_calls_converging_drops.rs @@ -2,7 +2,7 @@ //@ error-pattern:converging_fn called //@ error-pattern:0 dropped //@ error-pattern:exit -//@ ignore-emscripten no processes +//@ needs-subprocess struct Droppable(u8); impl Drop for Droppable { diff --git a/tests/ui/mir/mir_codegen_calls_converging_drops_2.rs b/tests/ui/mir/mir_codegen_calls_converging_drops_2.rs index e3cb9a96de0da..e89fbc58a0d1c 100644 --- a/tests/ui/mir/mir_codegen_calls_converging_drops_2.rs +++ b/tests/ui/mir/mir_codegen_calls_converging_drops_2.rs @@ -2,7 +2,7 @@ //@ error-pattern:complex called //@ error-pattern:dropped //@ error-pattern:exit -//@ ignore-emscripten no processes +//@ needs-subprocess struct Droppable; impl Drop for Droppable { diff --git a/tests/ui/mir/mir_codegen_calls_diverging.rs b/tests/ui/mir/mir_codegen_calls_diverging.rs index c62527f01d38c..ce2d7140b074e 100644 --- a/tests/ui/mir/mir_codegen_calls_diverging.rs +++ b/tests/ui/mir/mir_codegen_calls_diverging.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:diverging_fn called -//@ ignore-emscripten no processes +//@ needs-subprocess fn diverging_fn() -> ! { panic!("diverging_fn called") diff --git a/tests/ui/mir/mir_dynamic_drops_1.rs b/tests/ui/mir/mir_dynamic_drops_1.rs index ffb8cc26100e8..0b0a396057452 100644 --- a/tests/ui/mir/mir_dynamic_drops_1.rs +++ b/tests/ui/mir/mir_dynamic_drops_1.rs @@ -1,7 +1,7 @@ //@ run-fail //@ error-pattern:drop 1 //@ error-pattern:drop 2 -//@ ignore-emscripten no processes +//@ needs-subprocess /// Structure which will not allow to be dropped twice. struct Droppable<'a>(&'a mut bool, u32); diff --git a/tests/ui/mir/mir_dynamic_drops_2.rs b/tests/ui/mir/mir_dynamic_drops_2.rs index dc71f414673de..f625f19be26cb 100644 --- a/tests/ui/mir/mir_dynamic_drops_2.rs +++ b/tests/ui/mir/mir_dynamic_drops_2.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:drop 1 -//@ ignore-emscripten no processes +//@ needs-subprocess /// Structure which will not allow to be dropped twice. struct Droppable<'a>(&'a mut bool, u32); diff --git a/tests/ui/mir/mir_dynamic_drops_3.rs b/tests/ui/mir/mir_dynamic_drops_3.rs index fe84502ef1dc1..2a8af4ce1fb4b 100644 --- a/tests/ui/mir/mir_dynamic_drops_3.rs +++ b/tests/ui/mir/mir_dynamic_drops_3.rs @@ -4,7 +4,7 @@ //@ error-pattern:drop 3 //@ error-pattern:drop 2 //@ error-pattern:drop 1 -//@ ignore-emscripten no processes +//@ needs-subprocess /// Structure which will not allow to be dropped twice. struct Droppable<'a>(&'a mut bool, u32); diff --git a/tests/ui/mir/mir_indexing_oob_1.rs b/tests/ui/mir/mir_indexing_oob_1.rs index 3afc2f0b32e62..936484b0c1edd 100644 --- a/tests/ui/mir/mir_indexing_oob_1.rs +++ b/tests/ui/mir/mir_indexing_oob_1.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:index out of bounds: the len is 5 but the index is 10 -//@ ignore-emscripten no processes +//@ needs-subprocess const C: [u32; 5] = [0; 5]; diff --git a/tests/ui/mir/mir_indexing_oob_2.rs b/tests/ui/mir/mir_indexing_oob_2.rs index 6e7c1c83536f4..310a7f8f2b75f 100644 --- a/tests/ui/mir/mir_indexing_oob_2.rs +++ b/tests/ui/mir/mir_indexing_oob_2.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:index out of bounds: the len is 5 but the index is 10 -//@ ignore-emscripten no processes +//@ needs-subprocess const C: &'static [u8; 5] = b"hello"; diff --git a/tests/ui/mir/mir_indexing_oob_3.rs b/tests/ui/mir/mir_indexing_oob_3.rs index 4012864c6ce63..44b3a2c5a6dd9 100644 --- a/tests/ui/mir/mir_indexing_oob_3.rs +++ b/tests/ui/mir/mir_indexing_oob_3.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:index out of bounds: the len is 5 but the index is 10 -//@ ignore-emscripten no processes +//@ needs-subprocess const C: &'static [u8; 5] = b"hello"; diff --git a/tests/ui/mir/null/addrof_null.rs b/tests/ui/mir/null/addrof_null.rs new file mode 100644 index 0000000000000..0d0b7edeef620 --- /dev/null +++ b/tests/ui/mir/null/addrof_null.rs @@ -0,0 +1,14 @@ +// Make sure that we don't insert a check for `addr_of!`. +//@ run-pass +//@ compile-flags: -C debug-assertions + +struct Field { + a: u32, +} + +fn main() { + unsafe { + let ptr: *const Field = std::ptr::null(); + let _ptr = core::ptr::addr_of!((*ptr).a); + } +} diff --git a/tests/ui/mir/null/borrowed_mut_null.rs b/tests/ui/mir/null/borrowed_mut_null.rs new file mode 100644 index 0000000000000..d26452b9dac29 --- /dev/null +++ b/tests/ui/mir/null/borrowed_mut_null.rs @@ -0,0 +1,8 @@ +//@ run-fail +//@ compile-flags: -C debug-assertions +//@ error-pattern: null pointer dereference occurred + +fn main() { + let ptr: *mut u32 = std::ptr::null_mut(); + let _ptr: &mut u32 = unsafe { &mut *ptr }; +} diff --git a/tests/ui/mir/null/borrowed_null.rs b/tests/ui/mir/null/borrowed_null.rs new file mode 100644 index 0000000000000..fefac3a7212c9 --- /dev/null +++ b/tests/ui/mir/null/borrowed_null.rs @@ -0,0 +1,8 @@ +//@ run-fail +//@ compile-flags: -C debug-assertions +//@ error-pattern: null pointer dereference occurred + +fn main() { + let ptr: *const u32 = std::ptr::null(); + let _ptr: &u32 = unsafe { &*ptr }; +} diff --git a/tests/ui/mir/null/borrowed_null_zst.rs b/tests/ui/mir/null/borrowed_null_zst.rs new file mode 100644 index 0000000000000..835727c068b33 --- /dev/null +++ b/tests/ui/mir/null/borrowed_null_zst.rs @@ -0,0 +1,8 @@ +//@ run-fail +//@ compile-flags: -C debug-assertions +//@ error-pattern: null pointer dereference occurred + +fn main() { + let ptr: *const () = std::ptr::null(); + let _ptr: &() = unsafe { &*ptr }; +} diff --git a/tests/ui/mir/null/null_lhs.rs b/tests/ui/mir/null/null_lhs.rs new file mode 100644 index 0000000000000..238d350d1bdaf --- /dev/null +++ b/tests/ui/mir/null/null_lhs.rs @@ -0,0 +1,10 @@ +//@ run-fail +//@ compile-flags: -C debug-assertions +//@ error-pattern: null pointer dereference occurred + +fn main() { + let ptr: *mut u32 = std::ptr::null_mut(); + unsafe { + *(ptr) = 42; + } +} diff --git a/tests/ui/mir/null/null_rhs.rs b/tests/ui/mir/null/null_rhs.rs new file mode 100644 index 0000000000000..18eafb618698f --- /dev/null +++ b/tests/ui/mir/null/null_rhs.rs @@ -0,0 +1,10 @@ +//@ run-fail +//@ compile-flags: -C debug-assertions +//@ error-pattern: null pointer dereference occurred + +fn main() { + let ptr: *mut u32 = std::ptr::null_mut(); + unsafe { + let _v = *ptr; + } +} diff --git a/tests/ui/mir/null/place_without_read.rs b/tests/ui/mir/null/place_without_read.rs new file mode 100644 index 0000000000000..a259053d22bc2 --- /dev/null +++ b/tests/ui/mir/null/place_without_read.rs @@ -0,0 +1,11 @@ +// Make sure that we don't insert a check for places that do not read. +//@ run-pass +//@ compile-flags: -C debug-assertions + +fn main() { + let ptr: *const u16 = std::ptr::null(); + unsafe { + let _ = *ptr; + let _ = &raw const *ptr; + } +} diff --git a/tests/ui/mir/null/place_without_read_zst.rs b/tests/ui/mir/null/place_without_read_zst.rs new file mode 100644 index 0000000000000..c923ccea9a8a5 --- /dev/null +++ b/tests/ui/mir/null/place_without_read_zst.rs @@ -0,0 +1,11 @@ +// Make sure that we don't insert a check for places that do not read. +//@ run-pass +//@ compile-flags: -C debug-assertions + +fn main() { + let ptr: *const () = std::ptr::null(); + unsafe { + let _ = *ptr; + let _ = &raw const *ptr; + } +} diff --git a/tests/ui/mir/null/two_pointers.rs b/tests/ui/mir/null/two_pointers.rs new file mode 100644 index 0000000000000..52b9510be12eb --- /dev/null +++ b/tests/ui/mir/null/two_pointers.rs @@ -0,0 +1,12 @@ +//@ run-fail +//@ compile-flags: -C debug-assertions +//@ error-pattern: null pointer dereference occurred + +fn main() { + let ptr = std::ptr::null(); + let mut dest = 0u32; + let dest_ptr = &mut dest as *mut u32; + unsafe { + *dest_ptr = *(ptr); + } +} diff --git a/tests/ui/mir/null/zero_sized_access.rs b/tests/ui/mir/null/zero_sized_access.rs new file mode 100644 index 0000000000000..e8aaf820c49eb --- /dev/null +++ b/tests/ui/mir/null/zero_sized_access.rs @@ -0,0 +1,15 @@ +// Make sure that we don't insert a check for zero-sized reads or writes to +// null, because they are allowed. +//@ run-pass +//@ compile-flags: -C debug-assertions + +fn main() { + let ptr: *mut () = std::ptr::null_mut(); + unsafe { + *(ptr) = (); + } + let ptr1: *const () = std::ptr::null_mut(); + unsafe { + let _ptr = *ptr1; + } +} diff --git a/tests/ui/mismatched_types/E0053.stderr b/tests/ui/mismatched_types/E0053.stderr index 2559d4487491e..32452af5ceda7 100644 --- a/tests/ui/mismatched_types/E0053.stderr +++ b/tests/ui/mismatched_types/E0053.stderr @@ -13,8 +13,9 @@ LL | fn foo(x: u16); found signature `fn(i16)` help: change the parameter type to match the trait | -LL | fn foo(x: u16) { } - | ~~~ +LL - fn foo(x: i16) { } +LL + fn foo(x: u16) { } + | error[E0053]: method `bar` has an incompatible type for trait --> $DIR/E0053.rs:11:12 @@ -31,8 +32,9 @@ LL | fn bar(&self); found signature `fn(&mut Bar)` help: change the self-receiver type to match the trait | -LL | fn bar(&self) { } - | ~~~~~ +LL - fn bar(&mut self) { } +LL + fn bar(&self) { } + | error: aborting due to 2 previous errors diff --git a/tests/ui/mismatched_types/cast-rfc0401.stderr b/tests/ui/mismatched_types/cast-rfc0401.stderr index 2e5cbb900364a..a427639594415 100644 --- a/tests/ui/mismatched_types/cast-rfc0401.stderr +++ b/tests/ui/mismatched_types/cast-rfc0401.stderr @@ -90,8 +90,9 @@ LL | let _ = 3_i32 as bool; | help: compare with zero instead | -LL | let _ = 3_i32 != 0; - | ~~~~ +LL - let _ = 3_i32 as bool; +LL + let _ = 3_i32 != 0; + | error[E0054]: cannot cast `E` as `bool` --> $DIR/cast-rfc0401.rs:40:13 diff --git a/tests/ui/mismatched_types/closure-arg-count-expected-type-issue-47244.stderr b/tests/ui/mismatched_types/closure-arg-count-expected-type-issue-47244.stderr index 801e8a0ff1d02..b29abfe59c510 100644 --- a/tests/ui/mismatched_types/closure-arg-count-expected-type-issue-47244.stderr +++ b/tests/ui/mismatched_types/closure-arg-count-expected-type-issue-47244.stderr @@ -8,8 +8,9 @@ LL | let _n = m.iter().map(|_, b| { | help: change the closure to accept a tuple instead of individual arguments | -LL | let _n = m.iter().map(|(_, b)| { - | ~~~~~~~~ +LL - let _n = m.iter().map(|_, b| { +LL + let _n = m.iter().map(|(_, b)| { + | error: aborting due to 1 previous error diff --git a/tests/ui/mismatched_types/closure-arg-count.stderr b/tests/ui/mismatched_types/closure-arg-count.stderr index 0e2ca8feec545..8704d0f661be2 100644 --- a/tests/ui/mismatched_types/closure-arg-count.stderr +++ b/tests/ui/mismatched_types/closure-arg-count.stderr @@ -8,8 +8,9 @@ LL | [1, 2, 3].sort_by(|| panic!()); | help: consider changing the closure to take and ignore the expected arguments | -LL | [1, 2, 3].sort_by(|_, _| panic!()); - | ~~~~~~ +LL - [1, 2, 3].sort_by(|| panic!()); +LL + [1, 2, 3].sort_by(|_, _| panic!()); + | error[E0593]: closure is expected to take 2 arguments, but it takes 1 argument --> $DIR/closure-arg-count.rs:7:15 @@ -29,8 +30,9 @@ LL | [1, 2, 3].sort_by(|(tuple, tuple2)| panic!()); | help: change the closure to take multiple arguments instead of a single tuple | -LL | [1, 2, 3].sort_by(|tuple, tuple2| panic!()); - | ~~~~~~~~~~~~~~~ +LL - [1, 2, 3].sort_by(|(tuple, tuple2)| panic!()); +LL + [1, 2, 3].sort_by(|tuple, tuple2| panic!()); + | error[E0593]: closure is expected to take 2 distinct arguments, but it takes a single 2-tuple as argument --> $DIR/closure-arg-count.rs:11:15 @@ -42,8 +44,9 @@ LL | [1, 2, 3].sort_by(|(tuple, tuple2): (usize, _)| panic!()); | help: change the closure to take multiple arguments instead of a single tuple | -LL | [1, 2, 3].sort_by(|tuple, tuple2| panic!()); - | ~~~~~~~~~~~~~~~ +LL - [1, 2, 3].sort_by(|(tuple, tuple2): (usize, _)| panic!()); +LL + [1, 2, 3].sort_by(|tuple, tuple2| panic!()); + | error[E0593]: closure is expected to take 1 argument, but it takes 0 arguments --> $DIR/closure-arg-count.rs:13:5 @@ -61,8 +64,9 @@ LL | fn f>(_: F) {} | ^^^^^^^^^^^^ required by this bound in `f` help: consider changing the closure to take and ignore the expected argument | -LL | f(|_| panic!()); - | ~~~ +LL - f(|| panic!()); +LL + f(|_| panic!()); + | error[E0593]: closure is expected to take 1 argument, but it takes 0 arguments --> $DIR/closure-arg-count.rs:15:5 @@ -80,8 +84,9 @@ LL | fn f>(_: F) {} | ^^^^^^^^^^^^ required by this bound in `f` help: consider changing the closure to take and ignore the expected argument | -LL | f( move |_| panic!()); - | ~~~ +LL - f( move || panic!()); +LL + f( move |_| panic!()); + | error[E0593]: closure is expected to take a single 2-tuple as argument, but it takes 2 distinct arguments --> $DIR/closure-arg-count.rs:18:53 @@ -93,8 +98,9 @@ LL | let _it = vec![1, 2, 3].into_iter().enumerate().map(|i, x| i); | help: change the closure to accept a tuple instead of individual arguments | -LL | let _it = vec![1, 2, 3].into_iter().enumerate().map(|(i, x)| i); - | ~~~~~~~~ +LL - let _it = vec![1, 2, 3].into_iter().enumerate().map(|i, x| i); +LL + let _it = vec![1, 2, 3].into_iter().enumerate().map(|(i, x)| i); + | error[E0593]: closure is expected to take a single 2-tuple as argument, but it takes 2 distinct arguments --> $DIR/closure-arg-count.rs:20:53 @@ -106,8 +112,9 @@ LL | let _it = vec![1, 2, 3].into_iter().enumerate().map(|i: usize, x| i); | help: change the closure to accept a tuple instead of individual arguments | -LL | let _it = vec![1, 2, 3].into_iter().enumerate().map(|(i, x)| i); - | ~~~~~~~~ +LL - let _it = vec![1, 2, 3].into_iter().enumerate().map(|i: usize, x| i); +LL + let _it = vec![1, 2, 3].into_iter().enumerate().map(|(i, x)| i); + | error[E0593]: closure is expected to take a single 2-tuple as argument, but it takes 3 distinct arguments --> $DIR/closure-arg-count.rs:22:53 diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed new file mode 100644 index 0000000000000..ba46a447802c8 --- /dev/null +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed @@ -0,0 +1,13 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@[current] run-rustfix +fn main() { + let _ = (-10..=10).find(|x: &i32| x.signum() == 0); + //[current]~^ ERROR type mismatch in closure arguments + //[next]~^^ ERROR expected a `FnMut(& as Iterator>::Item)` closure, found + let _ = (-10..=10).find(|x: &i32| x.signum() == 0); + //[current]~^ ERROR type mismatch in closure arguments + //[next]~^^ ERROR expected `RangeInclusive<{integer}>` to be an iterator that yields `&&i32`, but it yields `{integer}` + //[next]~| ERROR expected a `FnMut(& as Iterator>::Item)` closure, found +} diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.stderr b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.stderr new file mode 100644 index 0000000000000..c35d70a635cbd --- /dev/null +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.stderr @@ -0,0 +1,38 @@ +error[E0631]: type mismatch in closure arguments + --> $DIR/closure-arg-type-mismatch-issue-45727.rs:6:24 + | +LL | let _ = (-10..=10).find(|x: i32| x.signum() == 0); + | ^^^^ -------- found signature defined here + | | + | expected due to this + | + = note: expected closure signature `for<'a> fn(&'a {integer}) -> _` + found closure signature `fn(i32) -> _` +note: required by a bound in `find` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL +help: consider adjusting the signature so it borrows its argument + | +LL | let _ = (-10..=10).find(|x: &i32| x.signum() == 0); + | + + +error[E0631]: type mismatch in closure arguments + --> $DIR/closure-arg-type-mismatch-issue-45727.rs:9:24 + | +LL | let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); + | ^^^^ ----------- found signature defined here + | | + | expected due to this + | + = note: expected closure signature `for<'a> fn(&'a {integer}) -> _` + found closure signature `fn(&&&i32) -> _` +note: required by a bound in `find` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL +help: consider adjusting the signature so it does not borrow its argument + | +LL - let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); +LL + let _ = (-10..=10).find(|x: &i32| x.signum() == 0); + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0631`. diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.fixed b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.fixed deleted file mode 100644 index e6e3e1551e959..0000000000000 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.fixed +++ /dev/null @@ -1,5 +0,0 @@ -//@ run-rustfix -fn main() { - let _ = (-10..=10).find(|x: &i32| x.signum() == 0); //~ ERROR type mismatch in closure arguments - let _ = (-10..=10).find(|x: &i32| x.signum() == 0); //~ ERROR type mismatch in closure arguments -} diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr new file mode 100644 index 0000000000000..b716131061993 --- /dev/null +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr @@ -0,0 +1,38 @@ +error[E0277]: expected a `FnMut(& as Iterator>::Item)` closure, found `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:6:29: 6:37}` + --> $DIR/closure-arg-type-mismatch-issue-45727.rs:6:29 + | +LL | let _ = (-10..=10).find(|x: i32| x.signum() == 0); + | ---- ^^^^^^^^^^^^^^^^^^^^^^^^ expected an `FnMut(& as Iterator>::Item)` closure, found `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:6:29: 6:37}` + | | + | required by a bound introduced by this call + | + = help: the trait `for<'a> FnMut(&'a as Iterator>::Item)` is not implemented for closure `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:6:29: 6:37}` + = note: expected a closure with arguments `(i32,)` + found a closure with arguments `(& as Iterator>::Item,)` +note: required by a bound in `find` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + +error[E0271]: expected `RangeInclusive<{integer}>` to be an iterator that yields `&&i32`, but it yields `{integer}` + --> $DIR/closure-arg-type-mismatch-issue-45727.rs:9:24 + | +LL | let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); + | ^^^^ expected `&&i32`, found integer + +error[E0277]: expected a `FnMut(& as Iterator>::Item)` closure, found `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:9:29: 9:40}` + --> $DIR/closure-arg-type-mismatch-issue-45727.rs:9:29 + | +LL | let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); + | ---- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected an `FnMut(& as Iterator>::Item)` closure, found `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:9:29: 9:40}` + | | + | required by a bound introduced by this call + | + = help: the trait `for<'a> FnMut(&'a as Iterator>::Item)` is not implemented for closure `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:9:29: 9:40}` + = note: expected a closure with arguments `(&&&i32,)` + found a closure with arguments `(& as Iterator>::Item,)` +note: required by a bound in `find` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0271, E0277. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs index 64e815606d427..0fd56707763e9 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs @@ -1,5 +1,13 @@ -//@ run-rustfix +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@[current] run-rustfix fn main() { - let _ = (-10..=10).find(|x: i32| x.signum() == 0); //~ ERROR type mismatch in closure arguments - let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); //~ ERROR type mismatch in closure arguments + let _ = (-10..=10).find(|x: i32| x.signum() == 0); + //[current]~^ ERROR type mismatch in closure arguments + //[next]~^^ ERROR expected a `FnMut(& as Iterator>::Item)` closure, found + let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); + //[current]~^ ERROR type mismatch in closure arguments + //[next]~^^ ERROR expected `RangeInclusive<{integer}>` to be an iterator that yields `&&i32`, but it yields `{integer}` + //[next]~| ERROR expected a `FnMut(& as Iterator>::Item)` closure, found } diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.stderr b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.stderr deleted file mode 100644 index e52e095e9f729..0000000000000 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.stderr +++ /dev/null @@ -1,38 +0,0 @@ -error[E0631]: type mismatch in closure arguments - --> $DIR/closure-arg-type-mismatch-issue-45727.rs:3:24 - | -LL | let _ = (-10..=10).find(|x: i32| x.signum() == 0); - | ^^^^ -------- found signature defined here - | | - | expected due to this - | - = note: expected closure signature `for<'a> fn(&'a {integer}) -> _` - found closure signature `fn(i32) -> _` -note: required by a bound in `find` - --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL -help: consider adjusting the signature so it borrows its argument - | -LL | let _ = (-10..=10).find(|x: &i32| x.signum() == 0); - | + - -error[E0631]: type mismatch in closure arguments - --> $DIR/closure-arg-type-mismatch-issue-45727.rs:4:24 - | -LL | let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); - | ^^^^ ----------- found signature defined here - | | - | expected due to this - | - = note: expected closure signature `for<'a> fn(&'a {integer}) -> _` - found closure signature `fn(&&&i32) -> _` -note: required by a bound in `find` - --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL -help: consider adjusting the signature so it does not borrow its argument - | -LL - let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); -LL + let _ = (-10..=10).find(|x: &i32| x.signum() == 0); - | - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0631`. diff --git a/tests/ui/mismatched_types/float-literal-inference-restrictions.stderr b/tests/ui/mismatched_types/float-literal-inference-restrictions.stderr index 6b3e0cb505fe8..b345d5e049e4a 100644 --- a/tests/ui/mismatched_types/float-literal-inference-restrictions.stderr +++ b/tests/ui/mismatched_types/float-literal-inference-restrictions.stderr @@ -21,8 +21,9 @@ LL | let y: f32 = 1f64; | help: change the type of the numeric literal from `f64` to `f32` | -LL | let y: f32 = 1f32; - | ~~~ +LL - let y: f32 = 1f64; +LL + let y: f32 = 1f32; + | error: aborting due to 2 previous errors diff --git a/tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.stderr b/tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.stderr index 0a86f884e70db..9fc3a91444746 100644 --- a/tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.stderr +++ b/tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.stderr @@ -31,9 +31,8 @@ note: function defined here --> $DIR/generic-mismatch-reporting-issue-116615.rs:2:4 | LL | fn foo_multi_same(a: T, b: T, c: T, d: T, e: T, f: i32) {} - | ^^^^^^^^^^^^^^ - ---- ---- ---- ---- ---- ------ - | | | | | | | - | | | | | | this parameter needs to match the `&str` type of `a` and `b` + | ^^^^^^^^^^^^^^ - ---- ---- ---- ---- ---- this parameter needs to match the `&str` type of `a` and `b` + | | | | | | | | | | | this parameter needs to match the `&str` type of `a` and `b` | | | | this parameter needs to match the `&str` type of `a` and `b` | | | `c`, `d` and `e` need to match the `&str` type of this parameter @@ -83,9 +82,8 @@ note: function defined here --> $DIR/generic-mismatch-reporting-issue-116615.rs:2:4 | LL | fn foo_multi_same(a: T, b: T, c: T, d: T, e: T, f: i32) {} - | ^^^^^^^^^^^^^^ - ---- ---- ---- ---- ---- ------ - | | | | | | | - | | | | | | `b` and `c` need to match the `&str` type of this parameter + | ^^^^^^^^^^^^^^ - ---- ---- ---- ---- ---- `b` and `c` need to match the `&str` type of this parameter + | | | | | | | | | | | `b` and `c` need to match the `&str` type of this parameter | | | | this parameter needs to match the `&str` type of `a`, `d` and `e` | | | this parameter needs to match the `&str` type of `a`, `d` and `e` diff --git a/tests/ui/mismatched_types/issue-106182.stderr b/tests/ui/mismatched_types/issue-106182.stderr index 2f33628a49164..647a731334421 100644 --- a/tests/ui/mismatched_types/issue-106182.stderr +++ b/tests/ui/mismatched_types/issue-106182.stderr @@ -10,8 +10,9 @@ LL | _S(& (mut _y), _v) => { found reference `&_` help: consider removing `&` from the pattern | -LL | _S(mut _y, _v) => { - | ~~~~~~ +LL - _S(& (mut _y), _v) => { +LL + _S(mut _y, _v) => { + | error: aborting due to 1 previous error diff --git a/tests/ui/mismatched_types/issue-112036.stderr b/tests/ui/mismatched_types/issue-112036.stderr index bd446b3d78cb2..fed6b11a7b292 100644 --- a/tests/ui/mismatched_types/issue-112036.stderr +++ b/tests/ui/mismatched_types/issue-112036.stderr @@ -9,7 +9,7 @@ LL | fn drop(self) {} help: change the self-receiver type to match the trait | LL | fn drop(&mut self) {} - | ~~~~~~~~~ + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/mismatched_types/issue-13033.stderr b/tests/ui/mismatched_types/issue-13033.stderr index 2a266d40e7717..7756217b56000 100644 --- a/tests/ui/mismatched_types/issue-13033.stderr +++ b/tests/ui/mismatched_types/issue-13033.stderr @@ -13,8 +13,9 @@ LL | fn bar(&mut self, other: &mut dyn Foo); found signature `fn(&mut Baz, &dyn Foo)` help: change the parameter type to match the trait | -LL | fn bar(&mut self, other: &mut dyn Foo) {} - | ~~~~~~~~~~~~ +LL - fn bar(&mut self, other: &dyn Foo) {} +LL + fn bar(&mut self, other: &mut dyn Foo) {} + | error: aborting due to 1 previous error diff --git a/tests/ui/mismatched_types/issue-1362.stderr b/tests/ui/mismatched_types/issue-1362.stderr index 6f6fdff66788c..4a2d4c1b45918 100644 --- a/tests/ui/mismatched_types/issue-1362.stderr +++ b/tests/ui/mismatched_types/issue-1362.stderr @@ -8,8 +8,9 @@ LL | let x: u32 = 20i32; | help: change the type of the numeric literal from `i32` to `u32` | -LL | let x: u32 = 20u32; - | ~~~ +LL - let x: u32 = 20i32; +LL + let x: u32 = 20u32; + | error: aborting due to 1 previous error diff --git a/tests/ui/mismatched_types/issue-1448-2.stderr b/tests/ui/mismatched_types/issue-1448-2.stderr index a6f1daefe636b..85730a18d9d28 100644 --- a/tests/ui/mismatched_types/issue-1448-2.stderr +++ b/tests/ui/mismatched_types/issue-1448-2.stderr @@ -13,8 +13,9 @@ LL | fn foo(a: u32) -> u32 { a } | ^^^ ------ help: change the type of the numeric literal from `i32` to `u32` | -LL | println!("{}", foo(10u32)); - | ~~~ +LL - println!("{}", foo(10i32)); +LL + println!("{}", foo(10u32)); + | error: aborting due to 1 previous error diff --git a/tests/ui/mismatched_types/issue-26480.stderr b/tests/ui/mismatched_types/issue-26480.stderr index ae10a00671e61..da8d73225f317 100644 --- a/tests/ui/mismatched_types/issue-26480.stderr +++ b/tests/ui/mismatched_types/issue-26480.stderr @@ -13,7 +13,7 @@ note: function defined here --> $DIR/issue-26480.rs:2:8 | LL | fn write(fildes: i32, buf: *const i8, nbyte: u64) -> i64; - | ^^^^^ + | ^^^^^ ----- = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) help: you can convert a `usize` to a `u64` and panic if the converted value doesn't fit | diff --git a/tests/ui/mismatched_types/mismatch-args-crash-issue-128848.stderr b/tests/ui/mismatched_types/mismatch-args-crash-issue-128848.stderr index 899cf435ddf5d..dbd313fada99c 100644 --- a/tests/ui/mismatched_types/mismatch-args-crash-issue-128848.stderr +++ b/tests/ui/mismatched_types/mismatch-args-crash-issue-128848.stderr @@ -8,8 +8,9 @@ note: method defined here --> $SRC_DIR/core/src/ops/function.rs:LL:COL help: provide the argument | -LL | f.call_once(/* args */) - | ~~~~~~~~~~~~ +LL - f.call_once() +LL + f.call_once(/* args */) + | error: aborting due to 1 previous error diff --git a/tests/ui/mismatched_types/mismatch-args-crash-issue-130400.stderr b/tests/ui/mismatched_types/mismatch-args-crash-issue-130400.stderr index 0e4b94b98e268..d9d99f3d1cf52 100644 --- a/tests/ui/mismatched_types/mismatch-args-crash-issue-130400.stderr +++ b/tests/ui/mismatched_types/mismatch-args-crash-issue-130400.stderr @@ -11,8 +11,9 @@ LL | fn foo(&mut self) -> _ { | ^^^ --------- help: provide the argument | -LL | Self::foo(/* value */) - | ~~~~~~~~~~~~~ +LL - Self::foo() +LL + Self::foo(/* value */) + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types --> $DIR/mismatch-args-crash-issue-130400.rs:2:26 diff --git a/tests/ui/mismatched_types/mismatch-args-vargs-issue-130372.stderr b/tests/ui/mismatched_types/mismatch-args-vargs-issue-130372.stderr index 38f769703582c..7acc361fdb8df 100644 --- a/tests/ui/mismatched_types/mismatch-args-vargs-issue-130372.stderr +++ b/tests/ui/mismatched_types/mismatch-args-vargs-issue-130372.stderr @@ -11,8 +11,9 @@ LL | unsafe extern "C" fn test_va_copy(_: u64, mut ap: ...) {} | ^^^^^^^^^^^^ ------ help: provide the argument | -LL | test_va_copy(/* u64 */); - | ~~~~~~~~~~~ +LL - test_va_copy(); +LL + test_va_copy(/* u64 */); + | error: aborting due to 1 previous error diff --git a/tests/ui/mismatched_types/numeric-literal-cast.stderr b/tests/ui/mismatched_types/numeric-literal-cast.stderr index fcf3eccbcba2b..8ddadcc5a9471 100644 --- a/tests/ui/mismatched_types/numeric-literal-cast.stderr +++ b/tests/ui/mismatched_types/numeric-literal-cast.stderr @@ -13,8 +13,9 @@ LL | fn foo(_: u16) {} | ^^^ ------ help: change the type of the numeric literal from `u8` to `u16` | -LL | foo(1u16); - | ~~~ +LL - foo(1u8); +LL + foo(1u16); + | error[E0308]: mismatched types --> $DIR/numeric-literal-cast.rs:8:10 @@ -31,8 +32,9 @@ LL | fn foo1(_: f64) {} | ^^^^ ------ help: change the type of the numeric literal from `f32` to `f64` | -LL | foo1(2f64); - | ~~~ +LL - foo1(2f32); +LL + foo1(2f64); + | error[E0308]: mismatched types --> $DIR/numeric-literal-cast.rs:10:10 @@ -49,8 +51,9 @@ LL | fn foo2(_: i32) {} | ^^^^ ------ help: change the type of the numeric literal from `i16` to `i32` | -LL | foo2(3i32); - | ~~~ +LL - foo2(3i16); +LL + foo2(3i32); + | error: aborting due to 3 previous errors diff --git a/tests/ui/mismatched_types/overloaded-calls-bad.stderr b/tests/ui/mismatched_types/overloaded-calls-bad.stderr index c52fa71361539..9f5c35a30097d 100644 --- a/tests/ui/mismatched_types/overloaded-calls-bad.stderr +++ b/tests/ui/mismatched_types/overloaded-calls-bad.stderr @@ -25,8 +25,9 @@ LL | impl FnMut<(isize,)> for S { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: provide the argument | -LL | let ans = s(/* isize */); - | ~~~~~~~~~~~~~ +LL - let ans = s(); +LL + let ans = s(/* isize */); + | error[E0057]: this function takes 1 argument but 2 arguments were supplied --> $DIR/overloaded-calls-bad.rs:37:15 diff --git a/tests/ui/mismatched_types/ref-pat-suggestions.stderr b/tests/ui/mismatched_types/ref-pat-suggestions.stderr index 148ed00b01d14..d3b605fabf5c5 100644 --- a/tests/ui/mismatched_types/ref-pat-suggestions.stderr +++ b/tests/ui/mismatched_types/ref-pat-suggestions.stderr @@ -336,8 +336,9 @@ LL | let S(&mut _b) = S(0); | ^^^^^^^ help: consider removing `&mut` from the pattern | -LL | let S(_b) = S(0); - | ~~ +LL - let S(&mut _b) = S(0); +LL + let S(_b) = S(0); + | error[E0308]: mismatched types --> $DIR/ref-pat-suggestions.rs:31:14 diff --git a/tests/ui/mismatched_types/suggest-boxed-trait-objects-instead-of-impl-trait.stderr b/tests/ui/mismatched_types/suggest-boxed-trait-objects-instead-of-impl-trait.stderr index 40182a75a989a..3f58efe19f5b0 100644 --- a/tests/ui/mismatched_types/suggest-boxed-trait-objects-instead-of-impl-trait.stderr +++ b/tests/ui/mismatched_types/suggest-boxed-trait-objects-instead-of-impl-trait.stderr @@ -12,8 +12,9 @@ LL | | } | help: you could change the return type to be a boxed trait object | -LL | fn foo() -> Box { - | ~~~~~~~ + +LL - fn foo() -> impl Trait { +LL + fn foo() -> Box { + | help: if you change the return type to expect trait objects, box the returned expressions | LL ~ Box::new(S) @@ -34,8 +35,9 @@ LL | | } | help: you could change the return type to be a boxed trait object | -LL | fn bar() -> Box { - | ~~~~~~~ + +LL - fn bar() -> impl Trait { +LL + fn bar() -> Box { + | help: if you change the return type to expect trait objects, box the returned expressions | LL ~ true => Box::new(S), diff --git a/tests/ui/mismatched_types/trait-impl-fn-incompatibility.stderr b/tests/ui/mismatched_types/trait-impl-fn-incompatibility.stderr index 2e544a62223a0..d232cc50e5230 100644 --- a/tests/ui/mismatched_types/trait-impl-fn-incompatibility.stderr +++ b/tests/ui/mismatched_types/trait-impl-fn-incompatibility.stderr @@ -13,8 +13,9 @@ LL | fn foo(x: u16); found signature `fn(i16)` help: change the parameter type to match the trait | -LL | fn foo(x: u16) { } - | ~~~ +LL - fn foo(x: i16) { } +LL + fn foo(x: u16) { } + | error[E0053]: method `bar` has an incompatible type for trait --> $DIR/trait-impl-fn-incompatibility.rs:10:28 @@ -31,8 +32,9 @@ LL | fn bar(&mut self, bar: &mut Bar); found signature `fn(&mut Bar, &Bar)` help: change the parameter type to match the trait | -LL | fn bar(&mut self, bar: &mut Bar) { } - | ~~~~~~~~ +LL - fn bar(&mut self, bar: &Bar) { } +LL + fn bar(&mut self, bar: &mut Bar) { } + | error: aborting due to 2 previous errors diff --git a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr index ad423f86ef9e3..9a18798db2139 100644 --- a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr +++ b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr @@ -34,8 +34,9 @@ note: method defined here --> $SRC_DIR/core/src/option.rs:LL:COL help: use `Option::map_or` to deref inner value of `Option` | -LL | arg.map_or(&[], |v| v) - | ~~~~~~ +++++++ +LL - arg.unwrap_or(&[]) +LL + arg.map_or(&[], |v| v) + | error[E0308]: mismatched types --> $DIR/transforming-option-ref-issue-127545.rs:13:19 @@ -58,8 +59,9 @@ note: method defined here --> $SRC_DIR/core/src/option.rs:LL:COL help: use `Option::map_or` to deref inner value of `Option` | -LL | arg.map_or(v, |v| v) - | ~~~~~~ +++++++ +LL - arg.unwrap_or(v) +LL + arg.map_or(v, |v| v) + | error[E0308]: mismatched types --> $DIR/transforming-option-ref-issue-127545.rs:17:19 @@ -82,8 +84,9 @@ note: method defined here --> $SRC_DIR/core/src/result.rs:LL:COL help: use `Result::map_or` to deref inner value of `Result` | -LL | arg.map_or(&[], |v| v) - | ~~~~~~ +++++++ +LL - arg.unwrap_or(&[]) +LL + arg.map_or(&[], |v| v) + | error: aborting due to 4 previous errors diff --git a/tests/ui/missing/missing-block-hint.stderr b/tests/ui/missing/missing-block-hint.stderr index 7a08d70d0ce55..15bf59f1482cd 100644 --- a/tests/ui/missing/missing-block-hint.stderr +++ b/tests/ui/missing/missing-block-hint.stderr @@ -11,8 +11,9 @@ LL | if (foo) => {} | ^^^^^ help: you might have meant to write a "greater than or equal to" comparison | -LL | if (foo) >= {} - | ~~ +LL - if (foo) => {} +LL + if (foo) >= {} + | error: expected `{`, found `bar` --> $DIR/missing-block-hint.rs:7:13 diff --git a/tests/ui/missing/missing-fields-in-struct-pattern.stderr b/tests/ui/missing/missing-fields-in-struct-pattern.stderr index 91a7bd3540e38..1c69592985f24 100644 --- a/tests/ui/missing/missing-fields-in-struct-pattern.stderr +++ b/tests/ui/missing/missing-fields-in-struct-pattern.stderr @@ -6,8 +6,9 @@ LL | if let S { a, b, c, d } = S(1, 2, 3, 4) { | help: use the tuple variant pattern syntax instead | -LL | if let S(a, b, c, d) = S(1, 2, 3, 4) { - | ~~~~~~~~~~~~ +LL - if let S { a, b, c, d } = S(1, 2, 3, 4) { +LL + if let S(a, b, c, d) = S(1, 2, 3, 4) { + | error: aborting due to 1 previous error diff --git a/tests/ui/missing/missing-items/missing-const-parameter.stderr b/tests/ui/missing/missing-items/missing-const-parameter.stderr index d9fea1306514c..c873e24481587 100644 --- a/tests/ui/missing/missing-items/missing-const-parameter.stderr +++ b/tests/ui/missing/missing-items/missing-const-parameter.stderr @@ -52,8 +52,9 @@ LL | struct Image([[u32; C]; R]); | help: a const parameter with a similar name exists | -LL | struct Image([[u32; R]; R]); - | ~ +LL - struct Image([[u32; C]; R]); +LL + struct Image([[u32; R]; R]); + | help: you might be missing a const parameter | LL | struct Image([[u32; C]; R]); diff --git a/tests/ui/missing/missing-items/missing-type-parameter2.stderr b/tests/ui/missing/missing-items/missing-type-parameter2.stderr index f33951c98bfb3..f6418de20b6a4 100644 --- a/tests/ui/missing/missing-items/missing-type-parameter2.stderr +++ b/tests/ui/missing/missing-items/missing-type-parameter2.stderr @@ -9,8 +9,9 @@ LL | impl X {} | help: a struct with a similar name exists | -LL | impl X {} - | ~ +LL - impl X {} +LL + impl X {} + | help: you might be missing a type parameter | LL | impl X {} @@ -26,8 +27,9 @@ LL | impl X {} | help: a type parameter with a similar name exists | -LL | impl X {} - | ~ +LL - impl X {} +LL + impl X {} + | help: you might be missing a type parameter | LL | impl X {} @@ -44,8 +46,9 @@ LL | fn foo(_: T) where T: Send {} | help: a struct with a similar name exists | -LL | fn foo(_: T) where X: Send {} - | ~ +LL - fn foo(_: T) where T: Send {} +LL + fn foo(_: T) where X: Send {} + | help: you might be missing a type parameter | LL | fn foo(_: T) where T: Send {} @@ -62,8 +65,9 @@ LL | fn foo(_: T) where T: Send {} | help: a struct with a similar name exists | -LL | fn foo(_: X) where T: Send {} - | ~ +LL - fn foo(_: T) where T: Send {} +LL + fn foo(_: X) where T: Send {} + | help: you might be missing a type parameter | LL | fn foo(_: T) where T: Send {} @@ -80,8 +84,9 @@ LL | fn bar(_: A) {} | help: a struct with a similar name exists | -LL | fn bar(_: X) {} - | ~ +LL - fn bar(_: A) {} +LL + fn bar(_: X) {} + | help: you might be missing a type parameter | LL | fn bar(_: A) {} diff --git a/tests/ui/modules/path-no-file-name.rs b/tests/ui/modules/path-no-file-name.rs index 23127346e02aa..753a09501235a 100644 --- a/tests/ui/modules/path-no-file-name.rs +++ b/tests/ui/modules/path-no-file-name.rs @@ -1,4 +1,4 @@ -//@ normalize-stderr: "\.:.*\(" -> ".: $$ACCESS_DENIED_MSG (" +//@ normalize-stderr: "\.`:.*\(" -> ".`: $$ACCESS_DENIED_MSG (" //@ normalize-stderr: "os error \d+" -> "os error $$ACCESS_DENIED_CODE" #[path = "."] diff --git a/tests/ui/modules/path-no-file-name.stderr b/tests/ui/modules/path-no-file-name.stderr index 834e8ea6b03de..6274ecfed1365 100644 --- a/tests/ui/modules/path-no-file-name.stderr +++ b/tests/ui/modules/path-no-file-name.stderr @@ -1,4 +1,4 @@ -error: couldn't read $DIR/.: $ACCESS_DENIED_MSG (os error $ACCESS_DENIED_CODE) +error: couldn't read `$DIR/.`: $ACCESS_DENIED_MSG (os error $ACCESS_DENIED_CODE) --> $DIR/path-no-file-name.rs:5:1 | LL | mod m; diff --git a/tests/ui/modules_and_files_visibility/mod_file_disambig.rs b/tests/ui/modules_and_files_visibility/mod_file_disambig.rs index e5958af173b66..1483e3e4630c2 100644 --- a/tests/ui/modules_and_files_visibility/mod_file_disambig.rs +++ b/tests/ui/modules_and_files_visibility/mod_file_disambig.rs @@ -2,5 +2,5 @@ mod mod_file_disambig_aux; //~ ERROR file for module `mod_file_disambig_aux` fou fn main() { assert_eq!(mod_file_aux::bar(), 10); - //~^ ERROR failed to resolve: use of undeclared crate or module `mod_file_aux` + //~^ ERROR failed to resolve: use of unresolved module or unlinked crate `mod_file_aux` } diff --git a/tests/ui/modules_and_files_visibility/mod_file_disambig.stderr b/tests/ui/modules_and_files_visibility/mod_file_disambig.stderr index a2c99396987ef..f82d613015f5d 100644 --- a/tests/ui/modules_and_files_visibility/mod_file_disambig.stderr +++ b/tests/ui/modules_and_files_visibility/mod_file_disambig.stderr @@ -6,11 +6,13 @@ LL | mod mod_file_disambig_aux; | = help: delete or rename one of them to remove the ambiguity -error[E0433]: failed to resolve: use of undeclared crate or module `mod_file_aux` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `mod_file_aux` --> $DIR/mod_file_disambig.rs:4:16 | LL | assert_eq!(mod_file_aux::bar(), 10); - | ^^^^^^^^^^^^ use of undeclared crate or module `mod_file_aux` + | ^^^^^^^^^^^^ use of unresolved module or unlinked crate `mod_file_aux` + | + = help: you might be missing a crate named `mod_file_aux` error: aborting due to 2 previous errors diff --git a/tests/ui/moves/move-deref-coercion.stderr b/tests/ui/moves/move-deref-coercion.stderr index 5760f4a7fdc35..25639075a3f4e 100644 --- a/tests/ui/moves/move-deref-coercion.stderr +++ b/tests/ui/moves/move-deref-coercion.stderr @@ -4,7 +4,7 @@ error[E0382]: borrow of partially moved value: `val` LL | let _val = val.first; | --------- value partially moved here LL | val.inner; - | ^^^^^^^^^ value borrowed here after partial move + | ^^^ value borrowed here after partial move | = note: partial move occurs because `val.first` has type `NotCopy`, which does not implement the `Copy` trait = note: borrow occurs due to deref coercion to `NotCopy` @@ -20,7 +20,7 @@ error[E0382]: borrow of partially moved value: `val` LL | let _val = val.first; | --------- value partially moved here LL | val.inner_method(); - | ^^^^^^^^^^^^^^^^^^ value borrowed here after partial move + | ^^^ value borrowed here after partial move | = note: partial move occurs because `val.first` has type `NotCopy`, which does not implement the `Copy` trait = note: borrow occurs due to deref coercion to `NotCopy` diff --git a/tests/ui/moves/needs-clone-through-deref.stderr b/tests/ui/moves/needs-clone-through-deref.stderr index 1f9aefeb4dd7a..9890ad480a6f0 100644 --- a/tests/ui/moves/needs-clone-through-deref.stderr +++ b/tests/ui/moves/needs-clone-through-deref.stderr @@ -10,8 +10,9 @@ note: `into_iter` takes ownership of the receiver `self`, which moves value --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: you can `clone` the value and consume it, but this might not be your desired behavior | -LL | for _ in as Clone>::clone(&self).into_iter() {} - | ++++++++++++++++++++++++++++++ ~ +LL - for _ in self.clone().into_iter() {} +LL + for _ in as Clone>::clone(&self).into_iter() {} + | error: aborting due to 1 previous error diff --git a/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr b/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr index 755bbc5c21bdb..c626796e01d25 100644 --- a/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr +++ b/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr @@ -10,8 +10,9 @@ note: `HashMap::::into_values` takes ownership of the receiver `self`, --> $SRC_DIR/std/src/collections/hash/map.rs:LL:COL help: you could `clone` the value and consume it, if the `Hash128_1: Clone` trait bound could be satisfied | -LL | let mut copy: Vec = as Clone>::clone(&map).into_values().collect(); - | ++++++++++++++++++++++++++++++++++++++++++++ ~ +LL - let mut copy: Vec = map.clone().into_values().collect(); +LL + let mut copy: Vec = as Clone>::clone(&map).into_values().collect(); + | help: consider annotating `Hash128_1` with `#[derive(Clone)]` | LL + #[derive(Clone)] diff --git a/tests/ui/moves/use_of_moved_value_copy_suggestions.stderr b/tests/ui/moves/use_of_moved_value_copy_suggestions.stderr index 784945dbbaeae..d69e83cce9a7f 100644 --- a/tests/ui/moves/use_of_moved_value_copy_suggestions.stderr +++ b/tests/ui/moves/use_of_moved_value_copy_suggestions.stderr @@ -196,7 +196,7 @@ LL | [t, t]; help: consider further restricting type parameter `T` with trait `Copy` | LL | T:, T: Copy - | ~~~~~~~~~ + | +++++++ error: aborting due to 11 previous errors diff --git a/tests/ui/mut/mutable-class-fields-2.stderr b/tests/ui/mut/mutable-class-fields-2.stderr index eb0c54f885ba8..7a6ff4da2bf52 100644 --- a/tests/ui/mut/mutable-class-fields-2.stderr +++ b/tests/ui/mut/mutable-class-fields-2.stderr @@ -7,7 +7,7 @@ LL | self.how_hungry -= 5; help: consider changing this to be a mutable reference | LL | pub fn eat(&mut self) { - | ~~~~~~~~~ + | +++ error: aborting due to 1 previous error diff --git a/tests/ui/namespace/namespace-mix.stderr b/tests/ui/namespace/namespace-mix.stderr index b80363fe8f848..412ea4aba30b8 100644 --- a/tests/ui/namespace/namespace-mix.stderr +++ b/tests/ui/namespace/namespace-mix.stderr @@ -11,7 +11,7 @@ LL | check(m1::S); help: a tuple struct with a similar name exists | LL | check(m1::TS); - | ~~ + | + help: consider importing one of these constants instead | LL + use m2::S; @@ -39,7 +39,7 @@ LL | pub struct TS(); help: a tuple struct with a similar name exists | LL | check(xm1::TS); - | ~~ + | + help: consider importing one of these constants instead | LL + use m2::S; @@ -65,7 +65,7 @@ LL | check(m7::V); help: a tuple variant with a similar name exists | LL | check(m7::TV); - | ~~ + | + help: consider importing one of these constants instead | LL + use m8::V; @@ -93,7 +93,7 @@ LL | TV(), help: a tuple variant with a similar name exists | LL | check(xm7::TV); - | ~~ + | + help: consider importing one of these constants instead | LL + use m8::V; diff --git a/tests/ui/never_type/dependency-on-fallback-to-unit.stderr b/tests/ui/never_type/dependency-on-fallback-to-unit.stderr index ea3b39c3000f9..bf37cc7b4b47d 100644 --- a/tests/ui/never_type/dependency-on-fallback-to-unit.stderr +++ b/tests/ui/never_type/dependency-on-fallback-to-unit.stderr @@ -15,8 +15,9 @@ LL | false => <_>::default(), = note: `#[warn(dependency_on_unit_never_type_fallback)]` on by default help: use `()` annotations to avoid fallback changes | -LL | false => <()>::default(), - | ~~ +LL - false => <_>::default(), +LL + false => <()>::default(), + | warning: this function depends on never type fallback being `()` --> $DIR/dependency-on-fallback-to-unit.rs:19:1 diff --git a/tests/ui/never_type/dont-suggest-turbofish-from-expansion.rs b/tests/ui/never_type/dont-suggest-turbofish-from-expansion.rs new file mode 100644 index 0000000000000..9a53358464d8c --- /dev/null +++ b/tests/ui/never_type/dont-suggest-turbofish-from-expansion.rs @@ -0,0 +1,20 @@ +#![deny(dependency_on_unit_never_type_fallback)] + +fn create_ok_default() -> Result +where + C: Default, +{ + Ok(C::default()) +} + +fn main() -> Result<(), ()> { + //~^ ERROR this function depends on never type fallback being `()` + //~| WARN this was previously accepted by the compiler but is being phased out + let (returned_value, _) = (|| { + let created = create_ok_default()?; + Ok((created, ())) + })()?; + + let _ = format_args!("{:?}", returned_value); + Ok(()) +} diff --git a/tests/ui/never_type/dont-suggest-turbofish-from-expansion.stderr b/tests/ui/never_type/dont-suggest-turbofish-from-expansion.stderr new file mode 100644 index 0000000000000..3fe642a8401a2 --- /dev/null +++ b/tests/ui/never_type/dont-suggest-turbofish-from-expansion.stderr @@ -0,0 +1,26 @@ +error: this function depends on never type fallback being `()` + --> $DIR/dont-suggest-turbofish-from-expansion.rs:10:1 + | +LL | fn main() -> Result<(), ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust 2024 and in a future release in all editions! + = note: for more information, see + = help: specify the types explicitly +note: in edition 2024, the requirement `!: Default` will fail + --> $DIR/dont-suggest-turbofish-from-expansion.rs:14:23 + | +LL | let created = create_ok_default()?; + | ^^^^^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/dont-suggest-turbofish-from-expansion.rs:1:9 + | +LL | #![deny(dependency_on_unit_never_type_fallback)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: use `()` annotations to avoid fallback changes + | +LL | let created: () = create_ok_default()?; + | ++++ + +error: aborting due to 1 previous error + diff --git a/tests/ui/never_type/fallback-closure-wrap.fallback.stderr b/tests/ui/never_type/fallback-closure-wrap.fallback.stderr index aa4a2760ffdc6..ac99a1fc24cf0 100644 --- a/tests/ui/never_type/fallback-closure-wrap.fallback.stderr +++ b/tests/ui/never_type/fallback-closure-wrap.fallback.stderr @@ -1,16 +1,15 @@ -error[E0271]: expected `{closure@fallback-closure-wrap.rs:18:40}` to be a closure that returns `()`, but it returns `!` - --> $DIR/fallback-closure-wrap.rs:18:31 +error[E0271]: expected `{closure@fallback-closure-wrap.rs:18:40}` to return `()`, but it returns `!` + --> $DIR/fallback-closure-wrap.rs:19:9 | -LL | let error = Closure::wrap(Box::new(move || { - | _______________________________^ -LL | | -LL | | panic!("Can't connect to server."); -LL | | }) as Box); - | |______^ expected `()`, found `!` +LL | let error = Closure::wrap(Box::new(move || { + | ------- this closure +LL | panic!("Can't connect to server."); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `!` | = note: expected unit type `()` found type `!` = note: required for the cast from `Box<{closure@$DIR/fallback-closure-wrap.rs:18:40: 18:47}>` to `Box` + = note: this error originates in the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/never_type/fallback-closure-wrap.rs b/tests/ui/never_type/fallback-closure-wrap.rs index 27020289c683c..e7f7d5aae3f8b 100644 --- a/tests/ui/never_type/fallback-closure-wrap.rs +++ b/tests/ui/never_type/fallback-closure-wrap.rs @@ -16,8 +16,8 @@ use std::marker::PhantomData; fn main() { let error = Closure::wrap(Box::new(move || { - //[fallback]~^ to be a closure that returns `()`, but it returns `!` panic!("Can't connect to server."); + //[fallback]~^ to return `()`, but it returns `!` }) as Box); } diff --git a/tests/ui/never_type/issue-52443.stderr b/tests/ui/never_type/issue-52443.stderr index 1c5a0d65142f8..bb60c33595c25 100644 --- a/tests/ui/never_type/issue-52443.stderr +++ b/tests/ui/never_type/issue-52443.stderr @@ -31,7 +31,7 @@ help: give the `break` a value of the expected type LL | [(); loop { break 42 }]; | ++ -error[E0015]: cannot use `for` loop on `RangeFrom` in constants +error[E0015]: cannot use `for` loop on `std::ops::RangeFrom` in constants --> $DIR/issue-52443.rs:9:21 | LL | [(); { for _ in 0usize.. {}; 0}]; @@ -39,7 +39,7 @@ LL | [(); { for _ in 0usize.. {}; 0}]; | = note: calls in constants are limited to constant functions, tuple structs and tuple variants -error[E0015]: cannot use `for` loop on `RangeFrom` in constants +error[E0015]: cannot use `for` loop on `std::ops::RangeFrom` in constants --> $DIR/issue-52443.rs:9:21 | LL | [(); { for _ in 0usize.. {}; 0}]; diff --git a/tests/ui/never_type/issue-96335.stderr b/tests/ui/never_type/issue-96335.stderr index c3d80a425e057..1193973d5ee8c 100644 --- a/tests/ui/never_type/issue-96335.stderr +++ b/tests/ui/never_type/issue-96335.stderr @@ -6,12 +6,14 @@ LL | 0.....{loop{}1}; | help: use `..` for an exclusive range | -LL | 0....{loop{}1}; - | ~~ +LL - 0.....{loop{}1}; +LL + 0....{loop{}1}; + | help: or `..=` for an inclusive range | -LL | 0..=..{loop{}1}; - | ~~~ +LL - 0.....{loop{}1}; +LL + 0..=..{loop{}1}; + | error[E0308]: mismatched types --> $DIR/issue-96335.rs:2:9 diff --git a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr index 04cd2fcafd673..49b966f32ced5 100644 --- a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr +++ b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr @@ -130,10 +130,6 @@ LL | msg_send!(); = note: for more information, see = help: specify the type explicitly = note: this warning originates in the macro `msg_send` (in Nightly builds, run with -Z macro-backtrace for more info) -help: use `()` annotations to avoid fallback changes - | -LL | match send_message::<() /* ?0 */>() { - | ~~ warning: 10 warnings emitted diff --git a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr index 7adba2cc709c0..4d3692a7b0432 100644 --- a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr +++ b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr @@ -130,10 +130,6 @@ LL | msg_send!(); = note: for more information, see = help: specify the type explicitly = note: this error originates in the macro `msg_send` (in Nightly builds, run with -Z macro-backtrace for more info) -help: use `()` annotations to avoid fallback changes - | -LL | match send_message::<() /* ?0 */>() { - | ~~ warning: the type `!` does not permit zero-initialization --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:12:18 diff --git a/tests/ui/never_type/return-never-coerce.rs b/tests/ui/never_type/return-never-coerce.rs index 559b7d0e985e4..14f07dfeb76a9 100644 --- a/tests/ui/never_type/return-never-coerce.rs +++ b/tests/ui/never_type/return-never-coerce.rs @@ -2,7 +2,7 @@ //@ run-fail //@ error-pattern:aah! -//@ ignore-emscripten no processes +//@ needs-subprocess fn call_another_fn T>(f: F) -> T { f() diff --git a/tests/ui/new-range/disabled.rs b/tests/ui/new-range/disabled.rs new file mode 100644 index 0000000000000..1a5fe3f974360 --- /dev/null +++ b/tests/ui/new-range/disabled.rs @@ -0,0 +1,27 @@ +//@ check-pass + +#![feature(new_range_api)] + +fn main() { + // Unchanged + let a: core::range::RangeFull = ..; + let b: core::range::RangeTo = ..2; + let c: core::range::RangeToInclusive = ..=3; + + let _: core::ops::RangeFull = a; + let _: core::ops::RangeTo = b; + let _: core::ops::RangeToInclusive = c; + + // Changed + let a: core::range::legacy::RangeFrom = 1..; + let b: core::range::legacy::Range = 2..3; + let c: core::range::legacy::RangeInclusive = 4..=5; + + let a: core::ops::RangeFrom = a; + let b: core::ops::Range = b; + let c: core::ops::RangeInclusive = c; + + let _: core::ops::RangeFrom = a.into_iter(); + let _: core::ops::Range = b.into_iter(); + let _: core::ops::RangeInclusive = c.into_iter(); +} diff --git a/tests/ui/new-range/enabled.rs b/tests/ui/new-range/enabled.rs new file mode 100644 index 0000000000000..a5fb76ad52b72 --- /dev/null +++ b/tests/ui/new-range/enabled.rs @@ -0,0 +1,24 @@ +//@ check-pass + +#![feature(new_range_api)] +#![feature(new_range)] + +fn main() { + // Unchanged + let a: core::range::RangeFull = ..; + let b: core::range::RangeTo = ..2; + let c: core::range::RangeToInclusive = ..=3; + + let _: core::ops::RangeFull = a; + let _: core::ops::RangeTo = b; + let _: core::ops::RangeToInclusive = c; + + // Changed + let a: core::range::RangeFrom = 1..; + let b: core::range::Range = 2..3; + let c: core::range::RangeInclusive = 4..=5; + + let _: core::range::IterRangeFrom = a.into_iter(); + let _: core::range::IterRange = b.into_iter(); + let _: core::range::IterRangeInclusive = c.into_iter(); +} diff --git a/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr b/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr index 1f01d3e82609b..7e11b23d681b8 100644 --- a/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr +++ b/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr @@ -9,8 +9,9 @@ LL | S1 { a: unsafe { &mut X1 } } = note: `#[warn(static_mut_refs)]` on by default help: use `&raw mut` instead to create a raw pointer | -LL | S1 { a: unsafe { &raw mut X1 } } - | ~~~~~~~~ +LL - S1 { a: unsafe { &mut X1 } } +LL + S1 { a: unsafe { &raw mut X1 } } + | warning: 1 warning emitted diff --git a/tests/ui/nll/check-normalized-sig-for-wf.current.stderr b/tests/ui/nll/check-normalized-sig-for-wf.current.stderr new file mode 100644 index 0000000000000..b25ae72f7fb9a --- /dev/null +++ b/tests/ui/nll/check-normalized-sig-for-wf.current.stderr @@ -0,0 +1,47 @@ +error[E0597]: `s` does not live long enough + --> $DIR/check-normalized-sig-for-wf.rs:11:7 + | +LL | s: String, + | - binding `s` declared here +... +LL | f(&s).0 + | --^^- + | | | + | | borrowed value does not live long enough + | argument requires that `s` is borrowed for `'static` +LL | +LL | } + | - `s` dropped here while still borrowed + +error[E0521]: borrowed data escapes outside of function + --> $DIR/check-normalized-sig-for-wf.rs:19:5 + | +LL | fn extend(input: &T) -> &'static T { + | ----- - let's call the lifetime of this reference `'1` + | | + | `input` is a reference that is only valid in the function body +... +LL | n(input).0 + | ^^^^^^^^ + | | + | `input` escapes the function body here + | argument requires that `'1` must outlive `'static` + +error[E0521]: borrowed data escapes outside of function + --> $DIR/check-normalized-sig-for-wf.rs:27:5 + | +LL | fn extend_mut<'a, T>(input: &'a mut T) -> &'static mut T { + | -- ----- `input` is a reference that is only valid in the function body + | | + | lifetime `'a` defined here +... +LL | n(input).0 + | ^^^^^^^^ + | | + | `input` escapes the function body here + | argument requires that `'a` must outlive `'static` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0521, E0597. +For more information about an error, try `rustc --explain E0521`. diff --git a/tests/ui/nll/check-normalized-sig-for-wf.next.stderr b/tests/ui/nll/check-normalized-sig-for-wf.next.stderr new file mode 100644 index 0000000000000..b25ae72f7fb9a --- /dev/null +++ b/tests/ui/nll/check-normalized-sig-for-wf.next.stderr @@ -0,0 +1,47 @@ +error[E0597]: `s` does not live long enough + --> $DIR/check-normalized-sig-for-wf.rs:11:7 + | +LL | s: String, + | - binding `s` declared here +... +LL | f(&s).0 + | --^^- + | | | + | | borrowed value does not live long enough + | argument requires that `s` is borrowed for `'static` +LL | +LL | } + | - `s` dropped here while still borrowed + +error[E0521]: borrowed data escapes outside of function + --> $DIR/check-normalized-sig-for-wf.rs:19:5 + | +LL | fn extend(input: &T) -> &'static T { + | ----- - let's call the lifetime of this reference `'1` + | | + | `input` is a reference that is only valid in the function body +... +LL | n(input).0 + | ^^^^^^^^ + | | + | `input` escapes the function body here + | argument requires that `'1` must outlive `'static` + +error[E0521]: borrowed data escapes outside of function + --> $DIR/check-normalized-sig-for-wf.rs:27:5 + | +LL | fn extend_mut<'a, T>(input: &'a mut T) -> &'static mut T { + | -- ----- `input` is a reference that is only valid in the function body + | | + | lifetime `'a` defined here +... +LL | n(input).0 + | ^^^^^^^^ + | | + | `input` escapes the function body here + | argument requires that `'a` must outlive `'static` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0521, E0597. +For more information about an error, try `rustc --explain E0521`. diff --git a/tests/ui/nll/check-normalized-sig-for-wf.rs b/tests/ui/nll/check-normalized-sig-for-wf.rs index cb0f34ce02f7a..9d7411fd8124a 100644 --- a/tests/ui/nll/check-normalized-sig-for-wf.rs +++ b/tests/ui/nll/check-normalized-sig-for-wf.rs @@ -1,3 +1,7 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + // fn whoops( s: String, diff --git a/tests/ui/nll/check-normalized-sig-for-wf.stderr b/tests/ui/nll/check-normalized-sig-for-wf.stderr deleted file mode 100644 index 5c96b0c6561a1..0000000000000 --- a/tests/ui/nll/check-normalized-sig-for-wf.stderr +++ /dev/null @@ -1,47 +0,0 @@ -error[E0597]: `s` does not live long enough - --> $DIR/check-normalized-sig-for-wf.rs:7:7 - | -LL | s: String, - | - binding `s` declared here -... -LL | f(&s).0 - | --^^- - | | | - | | borrowed value does not live long enough - | argument requires that `s` is borrowed for `'static` -LL | -LL | } - | - `s` dropped here while still borrowed - -error[E0521]: borrowed data escapes outside of function - --> $DIR/check-normalized-sig-for-wf.rs:15:5 - | -LL | fn extend(input: &T) -> &'static T { - | ----- - let's call the lifetime of this reference `'1` - | | - | `input` is a reference that is only valid in the function body -... -LL | n(input).0 - | ^^^^^^^^ - | | - | `input` escapes the function body here - | argument requires that `'1` must outlive `'static` - -error[E0521]: borrowed data escapes outside of function - --> $DIR/check-normalized-sig-for-wf.rs:23:5 - | -LL | fn extend_mut<'a, T>(input: &'a mut T) -> &'static mut T { - | -- ----- `input` is a reference that is only valid in the function body - | | - | lifetime `'a` defined here -... -LL | n(input).0 - | ^^^^^^^^ - | | - | `input` escapes the function body here - | argument requires that `'a` must outlive `'static` - -error: aborting due to 3 previous errors - -Some errors have detailed explanations: E0521, E0597. -For more information about an error, try `rustc --explain E0521`. diff --git a/tests/ui/nll/closure-requirements/escape-argument-callee.stderr b/tests/ui/nll/closure-requirements/escape-argument-callee.stderr index a7a59dccf2234..a445534c8d801 100644 --- a/tests/ui/nll/closure-requirements/escape-argument-callee.stderr +++ b/tests/ui/nll/closure-requirements/escape-argument-callee.stderr @@ -17,7 +17,7 @@ LL | let mut closure = expect_sig(|p, y| *p = y); | - - ^^^^^^ assignment requires that `'1` must outlive `'2` | | | | | has type `&'1 i32` - | has type `&'?2 mut &'2 i32` + | has type `&'?1 mut &'2 i32` note: no external requirements --> $DIR/escape-argument-callee.rs:20:1 diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr index f37ce967a1ba3..60087ec992bd6 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr @@ -23,6 +23,10 @@ LL | |_outlives1, _outlives2, _outlives3, x, y| { ... LL | demand_y(x, y, p) | ^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2` + | + = note: requirement occurs because of the type `Cell<&'?34 u32>`, which makes the generic argument `&'?34 u32` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance note: no external requirements --> $DIR/propagate-approximated-fail-no-postdom.rs:38:1 diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-ref.rs b/tests/ui/nll/closure-requirements/propagate-approximated-ref.rs index 1c27e38f83214..f4db723704cb8 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-ref.rs +++ b/tests/ui/nll/closure-requirements/propagate-approximated-ref.rs @@ -41,9 +41,10 @@ fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u3 #[rustc_regions] fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { + //~^ ERROR lifetime may not live long enough + // Only works if 'x: 'y: demand_y(x, y, x.get()) - //~^ ERROR lifetime may not live long enough }); } diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-ref.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-ref.stderr index e2d0b105ab14d..7325a9de8b294 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-ref.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-ref.stderr @@ -23,17 +23,21 @@ LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { = note: defining type: supply error: lifetime may not live long enough - --> $DIR/propagate-approximated-ref.rs:45:9 + --> $DIR/propagate-approximated-ref.rs:43:5 | -LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { - | -- -- lifetime `'b` defined here - | | - | lifetime `'a` defined here -... -LL | demand_y(x, y, x.get()) - | ^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'a` must outlive `'b` +LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | / establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { +... | +LL | | }); + | |______^ argument requires that `'a` must outlive `'b` | = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of the type `Cell<&'?11 u32>`, which makes the generic argument `&'?11 u32` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr index d7933a39eaacf..621c1ea083b25 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr @@ -19,6 +19,10 @@ LL | foo(cell, |cell_a, cell_x| { | `cell_a` declared here, outside of the closure body LL | cell_a.set(cell_x.get()); // forces 'x: 'a, error in closure | ^^^^^^^^^^^^^^^^^^^^^^^^ `cell_x` escapes the closure body here + | + = note: requirement occurs because of the type `Cell<&'?9 u32>`, which makes the generic argument `&'?9 u32` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance note: no external requirements --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:18:1 @@ -56,11 +60,11 @@ error[E0597]: `a` does not live long enough LL | let a = 0; | - binding `a` declared here LL | let cell = Cell::new(&a); - | ----------^^- - | | | - | | borrowed value does not live long enough - | argument requires that `a` is borrowed for `'static` + | ^^ borrowed value does not live long enough ... +LL | cell_x.set(cell_a.get()); // forces 'a: 'x, implies 'a = 'static -> borrow error + | ------------------------ argument requires that `a` is borrowed for `'static` +LL | }) LL | } | - `a` dropped here while still borrowed diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs index 25e212a722568..afabb69ec4cd0 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.rs @@ -30,10 +30,9 @@ fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u3 #[rustc_regions] fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { - //~^ ERROR borrowed data escapes outside of function - // Only works if 'x: 'y: demand_y(x, y, x.get()) + //~^ ERROR borrowed data escapes outside of function }); } diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr index 3c04cf300ad43..b9365c94a1bed 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr @@ -23,23 +23,18 @@ LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { = note: defining type: supply error[E0521]: borrowed data escapes outside of function - --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:32:5 + --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:34:9 | -LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { - | -- ------ `cell_a` is a reference that is only valid in the function body - | | - | lifetime `'a` defined here -LL | / establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { -... | -LL | | }); - | | ^ - | | | - | |______`cell_a` escapes the function body here - | argument requires that `'a` must outlive `'static` - | - = note: requirement occurs because of the type `Cell<&'?9 u32>`, which makes the generic argument `&'?9 u32` invariant - = note: the struct `Cell` is invariant over the parameter `T` - = help: see for more information about variance +LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { + | -- ------ `cell_a` is a reference that is only valid in the function body + | | + | lifetime `'a` defined here +... +LL | demand_y(x, y, x.get()) + | ^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `cell_a` escapes the function body here + | argument requires that `'a` must outlive `'static` error: aborting due to 1 previous error diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs index cda7b22362f3e..9f3e80d3db669 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.rs @@ -33,10 +33,9 @@ fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u3 #[rustc_regions] fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { - //~^ ERROR borrowed data escapes outside of function - // Only works if 'x: 'y: demand_y(x, y, x.get()) + //~^ ERROR borrowed data escapes outside of function }); } diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr index 9f5762ccbfa68..e5d2867103cfc 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr @@ -23,23 +23,18 @@ LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { = note: defining type: supply error[E0521]: borrowed data escapes outside of function - --> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:35:5 + --> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:37:9 | -LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { - | -- ------ `cell_a` is a reference that is only valid in the function body - | | - | lifetime `'a` defined here -LL | / establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { -... | -LL | | }); - | | ^ - | | | - | |______`cell_a` escapes the function body here - | argument requires that `'a` must outlive `'static` - | - = note: requirement occurs because of the type `Cell<&'?10 u32>`, which makes the generic argument `&'?10 u32` invariant - = note: the struct `Cell` is invariant over the parameter `T` - = help: see for more information about variance +LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { + | -- ------ `cell_a` is a reference that is only valid in the function body + | | + | lifetime `'a` defined here +... +LL | demand_y(x, y, x.get()) + | ^^^^^^^^^^^^^^^^^^^^^^^ + | | + | `cell_a` escapes the function body here + | argument requires that `'a` must outlive `'static` error: aborting due to 1 previous error diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-val.rs b/tests/ui/nll/closure-requirements/propagate-approximated-val.rs index e7e2f157604be..4d663c53d27a1 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-val.rs +++ b/tests/ui/nll/closure-requirements/propagate-approximated-val.rs @@ -34,9 +34,10 @@ fn demand_y<'x, 'y>(_outlives1: Cell<&&'x u32>, _outlives2: Cell<&'y &u32>, _y: #[rustc_regions] fn test<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| { + //~^ ERROR lifetime may not live long enough + // Only works if 'x: 'y: demand_y(outlives1, outlives2, x.get()) - //~^ ERROR lifetime may not live long enough }); } diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-val.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-val.stderr index 4787577a6e1a6..a14bfb06e831e 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-val.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-val.stderr @@ -23,17 +23,21 @@ LL | fn test<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { = note: defining type: test error: lifetime may not live long enough - --> $DIR/propagate-approximated-val.rs:38:9 + --> $DIR/propagate-approximated-val.rs:36:5 | -LL | fn test<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { - | -- -- lifetime `'b` defined here - | | - | lifetime `'a` defined here -... -LL | demand_y(outlives1, outlives2, x.get()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'a` must outlive `'b` +LL | fn test<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | / establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| { +... | +LL | | }); + | |______^ argument requires that `'a` must outlive `'b` | = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of the type `Cell<&'?7 u32>`, which makes the generic argument `&'?7 u32` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr index 669b56a0be70c..f48ed2823ddb1 100644 --- a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr +++ b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr @@ -16,12 +16,16 @@ error: lifetime may not live long enough --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:37:9 | LL | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { - | --------- - has type `&'?7 Cell<&'1 u32>` + | --------- - has type `&'?6 Cell<&'1 u32>` | | - | has type `&'?5 Cell<&'2 &'?1 u32>` + | has type `&'?4 Cell<&'2 &'?1 u32>` LL | // Only works if 'x: 'y: LL | demand_y(x, y, x.get()) | ^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2` + | + = note: requirement occurs because of the type `Cell<&'?37 u32>`, which makes the generic argument `&'?37 u32` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance note: no external requirements --> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:34:1 diff --git a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr index 75f476ac5f188..a090e94593fe6 100644 --- a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr +++ b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr @@ -16,12 +16,16 @@ error: lifetime may not live long enough --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:41:9 | LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { - | ---------- ---------- has type `&'?8 Cell<&'2 &'?2 u32>` + | ---------- ---------- has type `&'?7 Cell<&'2 &'?2 u32>` | | - | has type `&'?6 Cell<&'1 &'?1 u32>` + | has type `&'?5 Cell<&'1 &'?1 u32>` LL | // Only works if 'x: 'y: LL | demand_y(x, y, x.get()) | ^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2` + | + = note: requirement occurs because of the type `Cell<&'?43 u32>`, which makes the generic argument `&'?43 u32` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance note: no external requirements --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:38:1 diff --git a/tests/ui/nll/get_default.legacy.stderr b/tests/ui/nll/get_default.legacy.stderr new file mode 100644 index 0000000000000..699250312dc6b --- /dev/null +++ b/tests/ui/nll/get_default.legacy.stderr @@ -0,0 +1,18 @@ +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable + --> $DIR/get_default.rs:35:17 + | +LL | fn err(map: &mut Map) -> &String { + | - let's call the lifetime of this reference `'1` +LL | loop { +LL | match map.get() { + | --- immutable borrow occurs here +LL | Some(v) => { +LL | map.set(String::new()); // We always expect an error here. + | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here +LL | +LL | return v; + | - returning this value requires that `*map` is borrowed for `'1` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0502`. diff --git a/tests/ui/nll/get_default.nll.stderr b/tests/ui/nll/get_default.nll.stderr new file mode 100644 index 0000000000000..9b5976ca7170e --- /dev/null +++ b/tests/ui/nll/get_default.nll.stderr @@ -0,0 +1,48 @@ +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable + --> $DIR/get_default.rs:24:17 + | +LL | fn ok(map: &mut Map) -> &String { + | - let's call the lifetime of this reference `'1` +LL | loop { +LL | match map.get() { + | --- immutable borrow occurs here +LL | Some(v) => { +LL | return v; + | - returning this value requires that `*map` is borrowed for `'1` +... +LL | map.set(String::new()); // Ideally, this would not error. + | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here + +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable + --> $DIR/get_default.rs:35:17 + | +LL | fn err(map: &mut Map) -> &String { + | - let's call the lifetime of this reference `'1` +LL | loop { +LL | match map.get() { + | --- immutable borrow occurs here +LL | Some(v) => { +LL | map.set(String::new()); // We always expect an error here. + | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here +LL | +LL | return v; + | - returning this value requires that `*map` is borrowed for `'1` + +error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable + --> $DIR/get_default.rs:40:17 + | +LL | fn err(map: &mut Map) -> &String { + | - let's call the lifetime of this reference `'1` +LL | loop { +LL | match map.get() { + | --- immutable borrow occurs here +... +LL | return v; + | - returning this value requires that `*map` is borrowed for `'1` +... +LL | map.set(String::new()); // Ideally, this would not error. + | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0502`. diff --git a/tests/ui/nll/get_default.polonius.stderr b/tests/ui/nll/get_default.polonius.stderr index 613d06cce9156..699250312dc6b 100644 --- a/tests/ui/nll/get_default.polonius.stderr +++ b/tests/ui/nll/get_default.polonius.stderr @@ -1,5 +1,5 @@ error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable - --> $DIR/get_default.rs:32:17 + --> $DIR/get_default.rs:35:17 | LL | fn err(map: &mut Map) -> &String { | - let's call the lifetime of this reference `'1` @@ -7,8 +7,8 @@ LL | loop { LL | match map.get() { | --- immutable borrow occurs here LL | Some(v) => { -LL | map.set(String::new()); // Both AST and MIR error here - | ^^^ mutable borrow occurs here +LL | map.set(String::new()); // We always expect an error here. + | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here LL | LL | return v; | - returning this value requires that `*map` is borrowed for `'1` diff --git a/tests/ui/nll/get_default.rs b/tests/ui/nll/get_default.rs index ffac8a33da104..c3c09e9c2c553 100644 --- a/tests/ui/nll/get_default.rs +++ b/tests/ui/nll/get_default.rs @@ -1,7 +1,10 @@ // Basic test for free regions in the NLL code. This test ought to -// report an error due to a reborrowing constraint. Right now, we get -// a variety of errors from the older, AST-based machinery (notably -// borrowck), and then we get the NLL error at the end. +// report an error due to a reborrowing constraint. + +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: nll polonius legacy +//@ [polonius] compile-flags: -Z polonius=next +//@ [legacy] compile-flags: -Z polonius=legacy struct Map { } @@ -19,7 +22,7 @@ fn ok(map: &mut Map) -> &String { } None => { map.set(String::new()); // Ideally, this would not error. - //~^ ERROR borrowed as immutable + //[nll]~^ ERROR borrowed as immutable } } } @@ -29,13 +32,13 @@ fn err(map: &mut Map) -> &String { loop { match map.get() { Some(v) => { - map.set(String::new()); // Both AST and MIR error here + map.set(String::new()); // We always expect an error here. //~^ ERROR borrowed as immutable return v; } None => { - map.set(String::new()); // Ideally, just AST would error here - //~^ ERROR borrowed as immutable + map.set(String::new()); // Ideally, this would not error. + //[nll]~^ ERROR borrowed as immutable } } } diff --git a/tests/ui/nll/get_default.stderr b/tests/ui/nll/get_default.stderr deleted file mode 100644 index af79771e7e1b9..0000000000000 --- a/tests/ui/nll/get_default.stderr +++ /dev/null @@ -1,48 +0,0 @@ -error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable - --> $DIR/get_default.rs:21:17 - | -LL | fn ok(map: &mut Map) -> &String { - | - let's call the lifetime of this reference `'1` -LL | loop { -LL | match map.get() { - | --- immutable borrow occurs here -LL | Some(v) => { -LL | return v; - | - returning this value requires that `*map` is borrowed for `'1` -... -LL | map.set(String::new()); // Ideally, this would not error. - | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here - -error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable - --> $DIR/get_default.rs:32:17 - | -LL | fn err(map: &mut Map) -> &String { - | - let's call the lifetime of this reference `'1` -LL | loop { -LL | match map.get() { - | --- immutable borrow occurs here -LL | Some(v) => { -LL | map.set(String::new()); // Both AST and MIR error here - | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here -LL | -LL | return v; - | - returning this value requires that `*map` is borrowed for `'1` - -error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable - --> $DIR/get_default.rs:37:17 - | -LL | fn err(map: &mut Map) -> &String { - | - let's call the lifetime of this reference `'1` -LL | loop { -LL | match map.get() { - | --- immutable borrow occurs here -... -LL | return v; - | - returning this value requires that `*map` is borrowed for `'1` -... -LL | map.set(String::new()); // Ideally, just AST would error here - | ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0502`. diff --git a/tests/ui/nll/issue-46589.nll.stderr b/tests/ui/nll/issue-46589.nll.stderr new file mode 100644 index 0000000000000..dc80c1d08deca --- /dev/null +++ b/tests/ui/nll/issue-46589.nll.stderr @@ -0,0 +1,15 @@ +error[E0499]: cannot borrow `**other` as mutable more than once at a time + --> $DIR/issue-46589.rs:24:21 + | +LL | *other = match (*other).get_self() { + | -------- first mutable borrow occurs here +LL | Some(s) => s, +LL | None => (*other).new_self() + | ^^^^^^^^ + | | + | second mutable borrow occurs here + | first borrow later used here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/nll/issue-46589.rs b/tests/ui/nll/issue-46589.rs index 417d9cab657a1..10aff0d7b4ef9 100644 --- a/tests/ui/nll/issue-46589.rs +++ b/tests/ui/nll/issue-46589.rs @@ -1,8 +1,9 @@ -// This tests passes in Polonius mode, so is skipped in the automated compare-mode. -// We will manually check it passes in Polonius tests, as we can't have a test here -// which conditionally passes depending on a test revision/compile-flags. - -//@ ignore-compare-mode-polonius +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: nll polonius_next polonius +//@ [polonius_next] check-pass +//@ [polonius_next] compile-flags: -Zpolonius=next +//@ [polonius] check-pass +//@ [polonius] compile-flags: -Zpolonius struct Foo; @@ -21,7 +22,7 @@ impl Foo { *other = match (*other).get_self() { Some(s) => s, None => (*other).new_self() - //~^ ERROR cannot borrow `**other` as mutable more than once at a time [E0499] + //[nll]~^ ERROR cannot borrow `**other` as mutable more than once at a time [E0499] }; let c = other; diff --git a/tests/ui/nll/issue-46589.stderr b/tests/ui/nll/issue-46589.stderr deleted file mode 100644 index abf62aaa51088..0000000000000 --- a/tests/ui/nll/issue-46589.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0499]: cannot borrow `**other` as mutable more than once at a time - --> $DIR/issue-46589.rs:23:21 - | -LL | *other = match (*other).get_self() { - | -------- first mutable borrow occurs here -LL | Some(s) => s, -LL | None => (*other).new_self() - | ^^^^^^^^ - | | - | second mutable borrow occurs here - | first borrow later used here - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/nll/issue-51345-2.rs b/tests/ui/nll/issue-51345-2.rs index f2501fdbab405..39871d56a9af8 100644 --- a/tests/ui/nll/issue-51345-2.rs +++ b/tests/ui/nll/issue-51345-2.rs @@ -1,7 +1,7 @@ //@ run-fail //@ error-pattern:thread 'main' panicked //@ error-pattern:explicit panic -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { let mut vec = vec![]; diff --git a/tests/ui/nll/issue-54779-anon-static-lifetime.rs b/tests/ui/nll/issue-54779-anon-static-lifetime.rs index 260b6b109ca9b..6b8fa608ebbb2 100644 --- a/tests/ui/nll/issue-54779-anon-static-lifetime.rs +++ b/tests/ui/nll/issue-54779-anon-static-lifetime.rs @@ -29,7 +29,7 @@ impl DebugWith for Foo { fmt: &mut std::fmt::Formatter<'_>, ) -> std::fmt::Result { let Foo { bar } = self; - bar.debug_with(cx); //~ ERROR: lifetime may not live long enough + bar.debug_with(cx); //~ borrowed data escapes outside of method Ok(()) } } diff --git a/tests/ui/nll/issue-54779-anon-static-lifetime.stderr b/tests/ui/nll/issue-54779-anon-static-lifetime.stderr index a454ed265685c..03a5590661422 100644 --- a/tests/ui/nll/issue-54779-anon-static-lifetime.stderr +++ b/tests/ui/nll/issue-54779-anon-static-lifetime.stderr @@ -1,11 +1,17 @@ -error: lifetime may not live long enough - --> $DIR/issue-54779-anon-static-lifetime.rs:32:24 +error[E0521]: borrowed data escapes outside of method + --> $DIR/issue-54779-anon-static-lifetime.rs:32:9 | LL | cx: &dyn DebugContext, - | - let's call the lifetime of this reference `'1` + | -- - let's call the lifetime of this reference `'1` + | | + | `cx` is a reference that is only valid in the method body ... LL | bar.debug_with(cx); - | ^^ coercion requires that `'1` must outlive `'static` + | ^^^^^^^^^^^^^^^^^^ + | | + | `cx` escapes the method body here + | argument requires that `'1` must outlive `'static` error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/nll/issue-62007-assign-const-index.stderr b/tests/ui/nll/issue-62007-assign-const-index.stderr index 0db9fe62c3869..32716c47cd4d8 100644 --- a/tests/ui/nll/issue-62007-assign-const-index.stderr +++ b/tests/ui/nll/issue-62007-assign-const-index.stderr @@ -17,10 +17,9 @@ LL | fn to_refs(mut list: [&mut List; 2]) -> Vec<&mut T> { | - let's call the lifetime of this reference `'1` ... LL | if let Some(n) = list[0].next.as_mut() { - | ^^^^^^^^^^^^--------- - | | - | `list[_].next` was mutably borrowed here in the previous iteration of the loop - | argument requires that `list[_].next` is borrowed for `'1` + | ^^^^^^^^^^^^ `list[_].next` was mutably borrowed here in the previous iteration of the loop +LL | list[0] = n; + | ----------- assignment requires that `list[_].next` is borrowed for `'1` error: aborting due to 2 previous errors diff --git a/tests/ui/nll/issue-62007-assign-differing-fields.stderr b/tests/ui/nll/issue-62007-assign-differing-fields.stderr index f1af2e855afe6..d51fd7a1389a0 100644 --- a/tests/ui/nll/issue-62007-assign-differing-fields.stderr +++ b/tests/ui/nll/issue-62007-assign-differing-fields.stderr @@ -17,10 +17,9 @@ LL | fn to_refs<'a, T>(mut list: (&'a mut List, &'a mut List)) -> Vec<&'a | -- lifetime `'a` defined here ... LL | if let Some(n) = (list.0).next.as_mut() { - | ^^^^^^^^^^^^^--------- - | | - | `list.0.next` was mutably borrowed here in the previous iteration of the loop - | argument requires that `list.0.next` is borrowed for `'a` + | ^^^^^^^^^^^^^ `list.0.next` was mutably borrowed here in the previous iteration of the loop +LL | list.1 = n; + | ---------- assignment requires that `list.0.next` is borrowed for `'a` error: aborting due to 2 previous errors diff --git a/tests/ui/nll/issue-67007-escaping-data.rs b/tests/ui/nll/issue-67007-escaping-data.rs index 49ea2e5964fa2..92a47f7884308 100644 --- a/tests/ui/nll/issue-67007-escaping-data.rs +++ b/tests/ui/nll/issue-67007-escaping-data.rs @@ -12,8 +12,8 @@ struct Consumer<'tcx>(&'tcx ()); impl<'tcx> Consumer<'tcx> { fn bad_method<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) { - let other = self.use_fcx(fcx); //~ ERROR lifetime may not live long enough - fcx.use_it(other); + let other = self.use_fcx(fcx); + fcx.use_it(other); //~ ERROR lifetime may not live long enough } fn use_fcx<'a>(&self, _: &FnCtxt<'a, 'tcx>) -> &'a () { diff --git a/tests/ui/nll/issue-67007-escaping-data.stderr b/tests/ui/nll/issue-67007-escaping-data.stderr index eb7b57c7e998a..3b9ed24685180 100644 --- a/tests/ui/nll/issue-67007-escaping-data.stderr +++ b/tests/ui/nll/issue-67007-escaping-data.stderr @@ -1,14 +1,18 @@ error: lifetime may not live long enough - --> $DIR/issue-67007-escaping-data.rs:15:21 + --> $DIR/issue-67007-escaping-data.rs:16:9 | LL | impl<'tcx> Consumer<'tcx> { | ---- lifetime `'tcx` defined here LL | fn bad_method<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) { | -- lifetime `'a` defined here LL | let other = self.use_fcx(fcx); - | ^^^^^^^^^^^^^^^^^ argument requires that `'a` must outlive `'tcx` +LL | fcx.use_it(other); + | ^^^^^^^^^^^^^^^^^ argument requires that `'a` must outlive `'tcx` | = help: consider adding the following bound: `'a: 'tcx` + = note: requirement occurs because of the type `FnCtxt<'_, '_>`, which makes the generic argument `'_` invariant + = note: the struct `FnCtxt<'a, 'tcx>` is invariant over the parameter `'tcx` + = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/nll/issue-95272.rs b/tests/ui/nll/issue-95272.rs index 958cbde37882d..b3325a05e5c07 100644 --- a/tests/ui/nll/issue-95272.rs +++ b/tests/ui/nll/issue-95272.rs @@ -8,8 +8,8 @@ where fn test<'a, 'b>(x: Cell<&'a ()>, y: Cell<&'b ()>) { let f = check; - //~^ ERROR lifetime may not live long enough f(x, y); + //~^ ERROR lifetime may not live long enough } fn main() {} diff --git a/tests/ui/nll/issue-95272.stderr b/tests/ui/nll/issue-95272.stderr index 0453ef8e53ea3..3d1720239e9a0 100644 --- a/tests/ui/nll/issue-95272.stderr +++ b/tests/ui/nll/issue-95272.stderr @@ -1,16 +1,17 @@ error: lifetime may not live long enough - --> $DIR/issue-95272.rs:10:13 + --> $DIR/issue-95272.rs:11:5 | LL | fn test<'a, 'b>(x: Cell<&'a ()>, y: Cell<&'b ()>) { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here LL | let f = check; - | ^^^^^ assignment requires that `'a` must outlive `'b` +LL | f(x, y); + | ^^^^^^^ argument requires that `'a` must outlive `'b` | = help: consider adding the following bound: `'a: 'b` - = note: requirement occurs because of a function pointer to `check` - = note: the function `check` is invariant over the parameter `'a` + = note: requirement occurs because of the type `Cell<&()>`, which makes the generic argument `&()` invariant + = note: the struct `Cell` is invariant over the parameter `T` = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/nll/issue-98589-closures-relate-named-regions.stderr b/tests/ui/nll/issue-98589-closures-relate-named-regions.stderr index 4e741abc2dcf2..4f244b54bd07b 100644 --- a/tests/ui/nll/issue-98589-closures-relate-named-regions.stderr +++ b/tests/ui/nll/issue-98589-closures-relate-named-regions.stderr @@ -11,14 +11,14 @@ LL | || { None::<&'a &'b ()>; }; = help: consider adding the following bound: `'b: 'a` error: lifetime may not live long enough - --> $DIR/issue-98589-closures-relate-named-regions.rs:15:10 + --> $DIR/issue-98589-closures-relate-named-regions.rs:15:5 | LL | fn test_early_late<'a: 'a, 'b>() { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here LL | || { None::<&'a &'b ()>; }; - | ^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'a` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` diff --git a/tests/ui/nll/outlives-suggestion-simple.polonius.stderr b/tests/ui/nll/outlives-suggestion-simple.polonius.stderr deleted file mode 100644 index c00288f2e3c73..0000000000000 --- a/tests/ui/nll/outlives-suggestion-simple.polonius.stderr +++ /dev/null @@ -1,124 +0,0 @@ -error: lifetime may not live long enough - --> $DIR/outlives-suggestion-simple.rs:6:5 - | -LL | fn foo1<'a, 'b>(x: &'a usize) -> &'b usize { - | -- -- lifetime `'b` defined here - | | - | lifetime `'a` defined here -LL | x - | ^ returning this value requires that `'a` must outlive `'b` - | - = help: consider adding the following bound: `'a: 'b` - -error: lifetime may not live long enough - --> $DIR/outlives-suggestion-simple.rs:10:5 - | -LL | fn foo2<'a>(x: &'a usize) -> &'static usize { - | -- lifetime `'a` defined here -LL | x - | ^ returning this value requires that `'a` must outlive `'static` - | - = help: consider replacing `'a` with `'static` - -error: lifetime may not live long enough - --> $DIR/outlives-suggestion-simple.rs:14:5 - | -LL | fn foo3<'a, 'b>(x: &'a usize, y: &'b usize) -> (&'b usize, &'a usize) { - | -- -- lifetime `'b` defined here - | | - | lifetime `'a` defined here -LL | (x, y) - | ^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` - | - = help: consider adding the following bound: `'a: 'b` - -error: lifetime may not live long enough - --> $DIR/outlives-suggestion-simple.rs:14:5 - | -LL | fn foo3<'a, 'b>(x: &'a usize, y: &'b usize) -> (&'b usize, &'a usize) { - | -- -- lifetime `'b` defined here - | | - | lifetime `'a` defined here -LL | (x, y) - | ^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` - | - = help: consider adding the following bound: `'b: 'a` - -help: `'a` and `'b` must be the same: replace one with the other - -error: lifetime may not live long enough - --> $DIR/outlives-suggestion-simple.rs:22:5 - | -LL | fn foo4<'a, 'b, 'c>(x: &'a usize) -> (&'b usize, &'c usize) { - | -- -- lifetime `'b` defined here - | | - | lifetime `'a` defined here -... -LL | (x, x) - | ^^^^^^ returning this value requires that `'a` must outlive `'b` - | - = help: consider adding the following bound: `'a: 'b` - -error: lifetime may not live long enough - --> $DIR/outlives-suggestion-simple.rs:22:5 - | -LL | fn foo4<'a, 'b, 'c>(x: &'a usize) -> (&'b usize, &'c usize) { - | -- -- lifetime `'c` defined here - | | - | lifetime `'a` defined here -... -LL | (x, x) - | ^^^^^^ returning this value requires that `'a` must outlive `'c` - | - = help: consider adding the following bound: `'a: 'c` - -help: add bound `'a: 'b + 'c` - -error: lifetime may not live long enough - --> $DIR/outlives-suggestion-simple.rs:31:9 - | -LL | pub fn foo<'a>(x: &'a usize) -> Self { - | -- lifetime `'a` defined here -LL | Foo { x } - | ^^^^^^^^^ returning this value requires that `'a` must outlive `'static` - | - = help: consider replacing `'a` with `'static` - -error: lifetime may not live long enough - --> $DIR/outlives-suggestion-simple.rs:41:9 - | -LL | impl<'a> Bar<'a> { - | -- lifetime `'a` defined here -LL | pub fn get<'b>(&self) -> &'b usize { - | -- lifetime `'b` defined here -LL | self.x - | ^^^^^^ returning this value requires that `'a` must outlive `'b` - | - = help: consider adding the following bound: `'a: 'b` - -error: lifetime may not live long enough - --> $DIR/outlives-suggestion-simple.rs:52:9 - | -LL | impl<'a> Baz<'a> { - | -- lifetime `'a` defined here -LL | fn get<'b>(&'b self) -> &'a i32 { - | -- lifetime `'b` defined here -LL | self.x - | ^^^^^^ returning this value requires that `'b` must outlive `'a` - | - = help: consider adding the following bound: `'b: 'a` - -error[E0521]: borrowed data escapes outside of associated function - --> $DIR/outlives-suggestion-simple.rs:73:9 - | -LL | fn get_bar(&self) -> Bar2 { - | ----- - | | - | `self` declared here, outside of the associated function body - | `self` is a reference that is only valid in the associated function body -LL | Bar2::new(&self) - | ^^^^^^^^^^^^^^^^ `self` escapes the associated function body here - -error: aborting due to 10 previous errors - -For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/nll/outlives-suggestion-simple.stderr b/tests/ui/nll/outlives-suggestion-simple.stderr index bcffd575aed48..669532005b292 100644 --- a/tests/ui/nll/outlives-suggestion-simple.stderr +++ b/tests/ui/nll/outlives-suggestion-simple.stderr @@ -99,6 +99,10 @@ LL | fn get_bar(&self) -> Bar2 { | - let's call the lifetime of this reference `'1` LL | Bar2::new(&self) | ^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'a` + | + = note: requirement occurs because of the type `Foo2<'_>`, which makes the generic argument `'_` invariant + = note: the struct `Foo2<'a>` is invariant over the parameter `'a` + = help: see for more information about variance error: aborting due to 9 previous errors diff --git a/tests/ui/nll/polonius/assignment-kills-loans.rs b/tests/ui/nll/polonius/assignment-kills-loans.rs index 182262e5c4b9b..56ea9c2f1f937 100644 --- a/tests/ui/nll/polonius/assignment-kills-loans.rs +++ b/tests/ui/nll/polonius/assignment-kills-loans.rs @@ -4,8 +4,11 @@ // facts only on simple assignments, but not projections, incorrectly causing errors to be emitted // for code accepted by NLL. They are all variations from example code in the NLL RFC. +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: polonius_next polonius //@ check-pass -//@ compile-flags: -Z polonius +//@ [polonius_next] compile-flags: -Z polonius=next +//@ [polonius] compile-flags: -Z polonius struct List { value: T, diff --git a/tests/ui/nll/polonius/assignment-to-differing-field.legacy.stderr b/tests/ui/nll/polonius/assignment-to-differing-field.legacy.stderr new file mode 100644 index 0000000000000..cf5594dbd0786 --- /dev/null +++ b/tests/ui/nll/polonius/assignment-to-differing-field.legacy.stderr @@ -0,0 +1,51 @@ +error[E0499]: cannot borrow `list.0.value` as mutable more than once at a time + --> $DIR/assignment-to-differing-field.rs:23:21 + | +LL | fn assignment_to_field_projection<'a, T>( + | -- lifetime `'a` defined here +... +LL | result.push(&mut (list.0).value); + | ^^^^^^^^^^^^^^^^^^^ `list.0.value` was mutably borrowed here in the previous iteration of the loop +... +LL | return result; + | ------ returning this value requires that `list.0.value` is borrowed for `'a` + +error[E0499]: cannot borrow `list.0.next` as mutable more than once at a time + --> $DIR/assignment-to-differing-field.rs:26:26 + | +LL | fn assignment_to_field_projection<'a, T>( + | -- lifetime `'a` defined here +... +LL | if let Some(n) = (list.0).next.as_mut() { + | ^^^^^^^^^^^^^ `list.0.next` was mutably borrowed here in the previous iteration of the loop +LL | +LL | list.1 = n; + | ---------- assignment requires that `list.0.next` is borrowed for `'a` + +error[E0499]: cannot borrow `list.0.0.0.0.0.value` as mutable more than once at a time + --> $DIR/assignment-to-differing-field.rs:40:21 + | +LL | fn assignment_through_projection_chain<'a, T>( + | -- lifetime `'a` defined here +... +LL | result.push(&mut ((((list.0).0).0).0).0.value); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `list.0.0.0.0.0.value` was mutably borrowed here in the previous iteration of the loop +... +LL | return result; + | ------ returning this value requires that `list.0.0.0.0.0.value` is borrowed for `'a` + +error[E0499]: cannot borrow `list.0.0.0.0.0.next` as mutable more than once at a time + --> $DIR/assignment-to-differing-field.rs:43:26 + | +LL | fn assignment_through_projection_chain<'a, T>( + | -- lifetime `'a` defined here +... +LL | if let Some(n) = ((((list.0).0).0).0).0.next.as_mut() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `list.0.0.0.0.0.next` was mutably borrowed here in the previous iteration of the loop +LL | +LL | *((((list.0).0).0).0).1 = n; + | --------------------------- assignment requires that `list.0.0.0.0.0.next` is borrowed for `'a` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/nll/polonius/assignment-to-differing-field.polonius.stderr b/tests/ui/nll/polonius/assignment-to-differing-field.polonius.stderr new file mode 100644 index 0000000000000..cf5594dbd0786 --- /dev/null +++ b/tests/ui/nll/polonius/assignment-to-differing-field.polonius.stderr @@ -0,0 +1,51 @@ +error[E0499]: cannot borrow `list.0.value` as mutable more than once at a time + --> $DIR/assignment-to-differing-field.rs:23:21 + | +LL | fn assignment_to_field_projection<'a, T>( + | -- lifetime `'a` defined here +... +LL | result.push(&mut (list.0).value); + | ^^^^^^^^^^^^^^^^^^^ `list.0.value` was mutably borrowed here in the previous iteration of the loop +... +LL | return result; + | ------ returning this value requires that `list.0.value` is borrowed for `'a` + +error[E0499]: cannot borrow `list.0.next` as mutable more than once at a time + --> $DIR/assignment-to-differing-field.rs:26:26 + | +LL | fn assignment_to_field_projection<'a, T>( + | -- lifetime `'a` defined here +... +LL | if let Some(n) = (list.0).next.as_mut() { + | ^^^^^^^^^^^^^ `list.0.next` was mutably borrowed here in the previous iteration of the loop +LL | +LL | list.1 = n; + | ---------- assignment requires that `list.0.next` is borrowed for `'a` + +error[E0499]: cannot borrow `list.0.0.0.0.0.value` as mutable more than once at a time + --> $DIR/assignment-to-differing-field.rs:40:21 + | +LL | fn assignment_through_projection_chain<'a, T>( + | -- lifetime `'a` defined here +... +LL | result.push(&mut ((((list.0).0).0).0).0.value); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `list.0.0.0.0.0.value` was mutably borrowed here in the previous iteration of the loop +... +LL | return result; + | ------ returning this value requires that `list.0.0.0.0.0.value` is borrowed for `'a` + +error[E0499]: cannot borrow `list.0.0.0.0.0.next` as mutable more than once at a time + --> $DIR/assignment-to-differing-field.rs:43:26 + | +LL | fn assignment_through_projection_chain<'a, T>( + | -- lifetime `'a` defined here +... +LL | if let Some(n) = ((((list.0).0).0).0).0.next.as_mut() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `list.0.0.0.0.0.next` was mutably borrowed here in the previous iteration of the loop +LL | +LL | *((((list.0).0).0).0).1 = n; + | --------------------------- assignment requires that `list.0.0.0.0.0.next` is borrowed for `'a` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/nll/polonius/assignment-to-differing-field.rs b/tests/ui/nll/polonius/assignment-to-differing-field.rs index fb6c956952543..9701cd2bb5e21 100644 --- a/tests/ui/nll/polonius/assignment-to-differing-field.rs +++ b/tests/ui/nll/polonius/assignment-to-differing-field.rs @@ -4,7 +4,10 @@ // that we do not kill too many borrows. Assignments to the `.1` // field projections should leave the borrows on `.0` intact. -//@ compile-flags: -Z polonius +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: polonius legacy +//@ [polonius] compile-flags: -Z polonius=next +//@ [legacy] compile-flags: -Z polonius=legacy struct List { value: T, diff --git a/tests/ui/nll/polonius/assignment-to-differing-field.stderr b/tests/ui/nll/polonius/assignment-to-differing-field.stderr deleted file mode 100644 index acac47eac4f0e..0000000000000 --- a/tests/ui/nll/polonius/assignment-to-differing-field.stderr +++ /dev/null @@ -1,51 +0,0 @@ -error[E0499]: cannot borrow `list.0.value` as mutable more than once at a time - --> $DIR/assignment-to-differing-field.rs:20:21 - | -LL | fn assignment_to_field_projection<'a, T>( - | -- lifetime `'a` defined here -... -LL | result.push(&mut (list.0).value); - | ^^^^^^^^^^^^^^^^^^^ `list.0.value` was mutably borrowed here in the previous iteration of the loop -... -LL | return result; - | ------ returning this value requires that `list.0.value` is borrowed for `'a` - -error[E0499]: cannot borrow `list.0.next` as mutable more than once at a time - --> $DIR/assignment-to-differing-field.rs:23:26 - | -LL | fn assignment_to_field_projection<'a, T>( - | -- lifetime `'a` defined here -... -LL | if let Some(n) = (list.0).next.as_mut() { - | ^^^^^^^^^^^^^--------- - | | - | `list.0.next` was mutably borrowed here in the previous iteration of the loop - | argument requires that `list.0.next` is borrowed for `'a` - -error[E0499]: cannot borrow `list.0.0.0.0.0.value` as mutable more than once at a time - --> $DIR/assignment-to-differing-field.rs:37:21 - | -LL | fn assignment_through_projection_chain<'a, T>( - | -- lifetime `'a` defined here -... -LL | result.push(&mut ((((list.0).0).0).0).0.value); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `list.0.0.0.0.0.value` was mutably borrowed here in the previous iteration of the loop -... -LL | return result; - | ------ returning this value requires that `list.0.0.0.0.0.value` is borrowed for `'a` - -error[E0499]: cannot borrow `list.0.0.0.0.0.next` as mutable more than once at a time - --> $DIR/assignment-to-differing-field.rs:40:26 - | -LL | fn assignment_through_projection_chain<'a, T>( - | -- lifetime `'a` defined here -... -LL | if let Some(n) = ((((list.0).0).0).0).0.next.as_mut() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^--------- - | | - | `list.0.0.0.0.0.next` was mutably borrowed here in the previous iteration of the loop - | argument requires that `list.0.0.0.0.0.next` is borrowed for `'a` - -error: aborting due to 4 previous errors - -For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/nll/polonius/call-kills-loans.rs b/tests/ui/nll/polonius/call-kills-loans.rs index c02293c9a78aa..0657c42f48cda 100644 --- a/tests/ui/nll/polonius/call-kills-loans.rs +++ b/tests/ui/nll/polonius/call-kills-loans.rs @@ -4,8 +4,11 @@ // by NLL but was incorrectly rejected by Polonius because of these // missing `killed` facts. +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: polonius_next polonius //@ check-pass -//@ compile-flags: -Z polonius +//@ [polonius_next] compile-flags: -Z polonius=next +//@ [polonius] compile-flags: -Z polonius struct Thing; diff --git a/tests/ui/nll/polonius/issue-46589.rs b/tests/ui/nll/polonius/issue-46589.rs deleted file mode 100644 index af791f7e24ddd..0000000000000 --- a/tests/ui/nll/polonius/issue-46589.rs +++ /dev/null @@ -1,31 +0,0 @@ -// This test is a copy of `ui/nll/issue-46589.rs` which fails in NLL but succeeds in Polonius. -// As we can't have a test here which conditionally passes depending on a test -// revision/compile-flags. We ensure here that it passes in Polonius mode. - -//@ check-pass -//@ compile-flags: -Z polonius - -struct Foo; - -impl Foo { - fn get_self(&mut self) -> Option<&mut Self> { - Some(self) - } - - fn new_self(&mut self) -> &mut Self { - self - } - - fn trigger_bug(&mut self) { - let other = &mut (&mut *self); - - *other = match (*other).get_self() { - Some(s) => s, - None => (*other).new_self() - }; - - let c = other; - } -} - -fn main() {} diff --git a/tests/ui/nll/polonius/location-insensitive-scopes-liveness.rs b/tests/ui/nll/polonius/location-insensitive-scopes-liveness.rs index cb56c8f6deb4c..677b7aa1df860 100644 --- a/tests/ui/nll/polonius/location-insensitive-scopes-liveness.rs +++ b/tests/ui/nll/polonius/location-insensitive-scopes-liveness.rs @@ -5,9 +5,11 @@ // than `liveness::trace`, on some specific CFGs shapes: a variable was dead during tracing but its // regions were marked live later, and live loans were not recomputed at this point. +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: polonius_next polonius //@ check-pass -//@ revisions: nll polonius -//@ [polonius] compile-flags: -Zpolonius=next +//@ [polonius_next] compile-flags: -Z polonius=next +//@ [polonius] compile-flags: -Z polonius // minimized from wavefc-cli-3.0.0 fn repro1() { diff --git a/tests/ui/nll/polonius/polonius-smoke-test.legacy.stderr b/tests/ui/nll/polonius/polonius-smoke-test.legacy.stderr new file mode 100644 index 0000000000000..1268f6167f855 --- /dev/null +++ b/tests/ui/nll/polonius/polonius-smoke-test.legacy.stderr @@ -0,0 +1,59 @@ +error[E0515]: cannot return reference to local variable `x` + --> $DIR/polonius-smoke-test.rs:10:5 + | +LL | &x + | ^^ returns a reference to data owned by the current function + +error[E0503]: cannot use `x` because it was mutably borrowed + --> $DIR/polonius-smoke-test.rs:16:13 + | +LL | let y = &mut x; + | ------ `x` is borrowed here +LL | let z = x; + | ^ use of borrowed `x` +LL | let w = y; + | - borrow later used here + +error[E0505]: cannot move out of `x` because it is borrowed + --> $DIR/polonius-smoke-test.rs:22:13 + | +LL | pub fn use_while_mut_fr(x: &mut i32) -> &mut i32 { + | - - let's call the lifetime of this reference `'1` + | | + | binding `x` declared here +LL | let y = &mut *x; + | ------- borrow of `*x` occurs here +LL | let z = x; + | ^ move out of `x` occurs here +LL | y + | - returning this value requires that `*x` is borrowed for `'1` + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let y = &mut *x; +LL + let y = &mut x.clone(); + | + +error[E0505]: cannot move out of `s` because it is borrowed + --> $DIR/polonius-smoke-test.rs:46:5 + | +LL | let s = &mut 1; + | - binding `s` declared here +LL | let r = &mut *s; + | ------- borrow of `*s` occurs here +LL | let tmp = foo(&r); +LL | s; + | ^ move out of `s` occurs here +LL | tmp; + | --- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let r = &mut *s; +LL + let r = &mut s.clone(); + | + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0503, E0505, E0515. +For more information about an error, try `rustc --explain E0503`. diff --git a/tests/ui/nll/polonius/polonius-smoke-test.polonius.stderr b/tests/ui/nll/polonius/polonius-smoke-test.polonius.stderr new file mode 100644 index 0000000000000..1268f6167f855 --- /dev/null +++ b/tests/ui/nll/polonius/polonius-smoke-test.polonius.stderr @@ -0,0 +1,59 @@ +error[E0515]: cannot return reference to local variable `x` + --> $DIR/polonius-smoke-test.rs:10:5 + | +LL | &x + | ^^ returns a reference to data owned by the current function + +error[E0503]: cannot use `x` because it was mutably borrowed + --> $DIR/polonius-smoke-test.rs:16:13 + | +LL | let y = &mut x; + | ------ `x` is borrowed here +LL | let z = x; + | ^ use of borrowed `x` +LL | let w = y; + | - borrow later used here + +error[E0505]: cannot move out of `x` because it is borrowed + --> $DIR/polonius-smoke-test.rs:22:13 + | +LL | pub fn use_while_mut_fr(x: &mut i32) -> &mut i32 { + | - - let's call the lifetime of this reference `'1` + | | + | binding `x` declared here +LL | let y = &mut *x; + | ------- borrow of `*x` occurs here +LL | let z = x; + | ^ move out of `x` occurs here +LL | y + | - returning this value requires that `*x` is borrowed for `'1` + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let y = &mut *x; +LL + let y = &mut x.clone(); + | + +error[E0505]: cannot move out of `s` because it is borrowed + --> $DIR/polonius-smoke-test.rs:46:5 + | +LL | let s = &mut 1; + | - binding `s` declared here +LL | let r = &mut *s; + | ------- borrow of `*s` occurs here +LL | let tmp = foo(&r); +LL | s; + | ^ move out of `s` occurs here +LL | tmp; + | --- borrow later used here + | +help: consider cloning the value if the performance cost is acceptable + | +LL - let r = &mut *s; +LL + let r = &mut s.clone(); + | + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0503, E0505, E0515. +For more information about an error, try `rustc --explain E0503`. diff --git a/tests/ui/nll/polonius/polonius-smoke-test.rs b/tests/ui/nll/polonius/polonius-smoke-test.rs index ea5cdb263f5dd..7fbb3f9b47c3b 100644 --- a/tests/ui/nll/polonius/polonius-smoke-test.rs +++ b/tests/ui/nll/polonius/polonius-smoke-test.rs @@ -1,5 +1,9 @@ -// Check that Polonius borrow check works for simple cases. -//@ compile-flags: -Z polonius +// Check that Polonius works for simple cases. + +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: polonius legacy +//@ [polonius] compile-flags: -Z polonius=next +//@ [legacy] compile-flags: -Z polonius=legacy pub fn return_ref_to_local() -> &'static i32 { let x = 0; diff --git a/tests/ui/nll/polonius/polonius-smoke-test.stderr b/tests/ui/nll/polonius/polonius-smoke-test.stderr deleted file mode 100644 index a8a8267290def..0000000000000 --- a/tests/ui/nll/polonius/polonius-smoke-test.stderr +++ /dev/null @@ -1,59 +0,0 @@ -error[E0515]: cannot return reference to local variable `x` - --> $DIR/polonius-smoke-test.rs:6:5 - | -LL | &x - | ^^ returns a reference to data owned by the current function - -error[E0503]: cannot use `x` because it was mutably borrowed - --> $DIR/polonius-smoke-test.rs:12:13 - | -LL | let y = &mut x; - | ------ `x` is borrowed here -LL | let z = x; - | ^ use of borrowed `x` -LL | let w = y; - | - borrow later used here - -error[E0505]: cannot move out of `x` because it is borrowed - --> $DIR/polonius-smoke-test.rs:18:13 - | -LL | pub fn use_while_mut_fr(x: &mut i32) -> &mut i32 { - | - - let's call the lifetime of this reference `'1` - | | - | binding `x` declared here -LL | let y = &mut *x; - | ------- borrow of `*x` occurs here -LL | let z = x; - | ^ move out of `x` occurs here -LL | y - | - returning this value requires that `*x` is borrowed for `'1` - | -help: consider cloning the value if the performance cost is acceptable - | -LL - let y = &mut *x; -LL + let y = &mut x.clone(); - | - -error[E0505]: cannot move out of `s` because it is borrowed - --> $DIR/polonius-smoke-test.rs:42:5 - | -LL | let s = &mut 1; - | - binding `s` declared here -LL | let r = &mut *s; - | ------- borrow of `*s` occurs here -LL | let tmp = foo(&r); -LL | s; - | ^ move out of `s` occurs here -LL | tmp; - | --- borrow later used here - | -help: consider cloning the value if the performance cost is acceptable - | -LL - let r = &mut *s; -LL + let r = &mut s.clone(); - | - -error: aborting due to 4 previous errors - -Some errors have detailed explanations: E0503, E0505, E0515. -For more information about an error, try `rustc --explain E0503`. diff --git a/tests/ui/nll/polonius/storagedead-kills-loans.rs b/tests/ui/nll/polonius/storagedead-kills-loans.rs index 89cf919bb48e0..75a37e6ece575 100644 --- a/tests/ui/nll/polonius/storagedead-kills-loans.rs +++ b/tests/ui/nll/polonius/storagedead-kills-loans.rs @@ -3,8 +3,11 @@ // is correctly accepted by NLL but was incorrectly rejected by // Polonius because of these missing `killed` facts. +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: polonius_next polonius //@ check-pass -//@ compile-flags: -Z polonius +//@ [polonius_next] compile-flags: -Z polonius=next +//@ [polonius] compile-flags: -Z polonius use std::{io, mem}; use std::io::Read; diff --git a/tests/ui/nll/polonius/subset-relations.legacy.stderr b/tests/ui/nll/polonius/subset-relations.legacy.stderr new file mode 100644 index 0000000000000..10d42ca58d9df --- /dev/null +++ b/tests/ui/nll/polonius/subset-relations.legacy.stderr @@ -0,0 +1,14 @@ +error: lifetime may not live long enough + --> $DIR/subset-relations.rs:13:5 + | +LL | fn missing_subset<'a, 'b>(x: &'a u32, y: &'b u32) -> &'a u32 { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | y + | ^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + | + = help: consider adding the following bound: `'b: 'a` + +error: aborting due to 1 previous error + diff --git a/tests/ui/nll/polonius/subset-relations.polonius.stderr b/tests/ui/nll/polonius/subset-relations.polonius.stderr new file mode 100644 index 0000000000000..10d42ca58d9df --- /dev/null +++ b/tests/ui/nll/polonius/subset-relations.polonius.stderr @@ -0,0 +1,14 @@ +error: lifetime may not live long enough + --> $DIR/subset-relations.rs:13:5 + | +LL | fn missing_subset<'a, 'b>(x: &'a u32, y: &'b u32) -> &'a u32 { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | y + | ^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + | + = help: consider adding the following bound: `'b: 'a` + +error: aborting due to 1 previous error + diff --git a/tests/ui/nll/polonius/subset-relations.rs b/tests/ui/nll/polonius/subset-relations.rs index 3c1af1983cfd0..d47e4e05533ee 100644 --- a/tests/ui/nll/polonius/subset-relations.rs +++ b/tests/ui/nll/polonius/subset-relations.rs @@ -3,7 +3,10 @@ // two free regions outlive each other, without any evidence that this // relation holds. -//@ compile-flags: -Z polonius +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: polonius legacy +//@ [polonius] compile-flags: -Z polonius=next +//@ [legacy] compile-flags: -Z polonius=legacy // returning `y` requires that `'b: 'a`, but it's not known to be true fn missing_subset<'a, 'b>(x: &'a u32, y: &'b u32) -> &'a u32 { @@ -22,7 +25,7 @@ fn implied_bounds_subset<'a, 'b>(x: &'a &'b mut u32) -> &'a u32 { // `'b: 'a` is declared, and `'a: 'c` is known via implied bounds: // `'b: 'c` is therefore known to hold transitively -fn transitively_valid_subset<'a, 'b: 'a, 'c>(x: &'c &'a u32, y: &'b u32) -> &'c u32 { +fn transitively_valid_subset<'a, 'b: 'a, 'c>(x: &'c &'a u32, y: &'b u32) -> &'c u32 { y } diff --git a/tests/ui/nll/polonius/subset-relations.stderr b/tests/ui/nll/polonius/subset-relations.stderr deleted file mode 100644 index 9deca6449a8f1..0000000000000 --- a/tests/ui/nll/polonius/subset-relations.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: lifetime may not live long enough - --> $DIR/subset-relations.rs:10:5 - | -LL | fn missing_subset<'a, 'b>(x: &'a u32, y: &'b u32) -> &'a u32 { - | -- -- lifetime `'b` defined here - | | - | lifetime `'a` defined here -LL | y - | ^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` - | - = help: consider adding the following bound: `'b: 'a` - -error: aborting due to 1 previous error - diff --git a/tests/ui/nll/relate_tys/var-appears-twice.stderr b/tests/ui/nll/relate_tys/var-appears-twice.stderr index 3f9a6cec0d2e5..2b2ec88ba8e07 100644 --- a/tests/ui/nll/relate_tys/var-appears-twice.stderr +++ b/tests/ui/nll/relate_tys/var-appears-twice.stderr @@ -5,9 +5,10 @@ LL | let b = 44; | - binding `b` declared here ... LL | let x: DoubleCell<_> = make_cell(&b); - | ------------- ^^ borrowed value does not live long enough - | | - | type annotation requires that `b` is borrowed for `'static` + | ----------^^- + | | | + | | borrowed value does not live long enough + | assignment requires that `b` is borrowed for `'static` ... LL | } | - `b` dropped here while still borrowed diff --git a/tests/ui/nll/ty-outlives/projection-no-regions-closure.stderr b/tests/ui/nll/ty-outlives/projection-no-regions-closure.stderr index 4f93fb4eaea34..a01b1d5174df4 100644 --- a/tests/ui/nll/ty-outlives/projection-no-regions-closure.stderr +++ b/tests/ui/nll/ty-outlives/projection-no-regions-closure.stderr @@ -34,7 +34,7 @@ LL | with_signature(x, |mut y| Box::new(y.next())) help: consider adding an explicit lifetime bound | LL | T: Iterator, ::Item: 'a - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | +++++++++++++++++++++++++ note: external requirements --> $DIR/projection-no-regions-closure.rs:34:23 @@ -96,7 +96,7 @@ LL | with_signature(x, |mut y| Box::new(y.next())) help: consider adding an explicit lifetime bound | LL | T: 'b + Iterator, ::Item: 'a - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | +++++++++++++++++++++++++ note: external requirements --> $DIR/projection-no-regions-closure.rs:52:23 diff --git a/tests/ui/nll/ty-outlives/projection-no-regions-fn.stderr b/tests/ui/nll/ty-outlives/projection-no-regions-fn.stderr index da76ac1c474a3..07debd308c204 100644 --- a/tests/ui/nll/ty-outlives/projection-no-regions-fn.stderr +++ b/tests/ui/nll/ty-outlives/projection-no-regions-fn.stderr @@ -10,7 +10,7 @@ LL | Box::new(x.next()) help: consider adding an explicit lifetime bound | LL | T: Iterator, ::Item: 'a - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | +++++++++++++++++++++++++ error[E0309]: the associated type `::Item` may not live long enough --> $DIR/projection-no-regions-fn.rs:28:5 @@ -24,7 +24,7 @@ LL | Box::new(x.next()) help: consider adding an explicit lifetime bound | LL | T: 'b + Iterator, ::Item: 'a - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | +++++++++++++++++++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/nll/ty-outlives/projection-one-region-closure.stderr b/tests/ui/nll/ty-outlives/projection-one-region-closure.stderr index dda60398198e7..408c57a31eea8 100644 --- a/tests/ui/nll/ty-outlives/projection-one-region-closure.stderr +++ b/tests/ui/nll/ty-outlives/projection-one-region-closure.stderr @@ -24,6 +24,22 @@ LL | | T: Anything<'b>, | = note: defining type: no_relationships_late::<'?1, T> +error: lifetime may not live long enough + --> $DIR/projection-one-region-closure.rs:45:5 + | +LL | fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of the type `Cell<&'?6 ()>`, which makes the generic argument `&'?6 ()` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance + error[E0309]: the parameter type `T` may not live long enough --> $DIR/projection-one-region-closure.rs:45:39 | @@ -38,19 +54,6 @@ help: consider adding an explicit lifetime bound LL | T: Anything<'b> + 'a, | ++++ -error: lifetime may not live long enough - --> $DIR/projection-one-region-closure.rs:45:39 - | -LL | fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) - | -- -- lifetime `'b` defined here - | | - | lifetime `'a` defined here -... -LL | with_signature(cell, t, |cell, t| require(cell, t)); - | ^^^^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` - | - = help: consider adding the following bound: `'b: 'a` - note: external requirements --> $DIR/projection-one-region-closure.rs:56:29 | @@ -77,6 +80,22 @@ LL | | 'a: 'a, | = note: defining type: no_relationships_early::<'?1, '?2, T> +error: lifetime may not live long enough + --> $DIR/projection-one-region-closure.rs:56:5 + | +LL | fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | with_signature(cell, t, |cell, t| require(cell, t)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` + | + = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of the type `Cell<&'?7 ()>`, which makes the generic argument `&'?7 ()` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance + error[E0309]: the parameter type `T` may not live long enough --> $DIR/projection-one-region-closure.rs:56:39 | @@ -91,19 +110,6 @@ help: consider adding an explicit lifetime bound LL | T: Anything<'b> + 'a, | ++++ -error: lifetime may not live long enough - --> $DIR/projection-one-region-closure.rs:56:39 - | -LL | fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) - | -- -- lifetime `'b` defined here - | | - | lifetime `'a` defined here -... -LL | with_signature(cell, t, |cell, t| require(cell, t)); - | ^^^^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` - | - = help: consider adding the following bound: `'b: 'a` - note: external requirements --> $DIR/projection-one-region-closure.rs:70:29 | diff --git a/tests/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr b/tests/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr index 52040663e005c..4ebdf10c5bf3d 100644 --- a/tests/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr +++ b/tests/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr @@ -24,7 +24,7 @@ LL | | T: Anything<'b>, = note: defining type: no_relationships_late::<'?1, T> error: lifetime may not live long enough - --> $DIR/projection-one-region-trait-bound-closure.rs:37:39 + --> $DIR/projection-one-region-trait-bound-closure.rs:37:5 | LL | fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) | -- -- lifetime `'b` defined here @@ -32,9 +32,12 @@ LL | fn no_relationships_late<'a, 'b, T>(cell: Cell<&'a ()>, t: T) | lifetime `'a` defined here ... LL | with_signature(cell, t, |cell, t| require(cell, t)); - | ^^^^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of the type `Cell<&'?6 ()>`, which makes the generic argument `&'?6 ()` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance note: external requirements --> $DIR/projection-one-region-trait-bound-closure.rs:47:29 @@ -62,7 +65,7 @@ LL | | 'a: 'a, = note: defining type: no_relationships_early::<'?1, '?2, T> error: lifetime may not live long enough - --> $DIR/projection-one-region-trait-bound-closure.rs:47:39 + --> $DIR/projection-one-region-trait-bound-closure.rs:47:5 | LL | fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) | -- -- lifetime `'b` defined here @@ -70,9 +73,12 @@ LL | fn no_relationships_early<'a, 'b, T>(cell: Cell<&'a ()>, t: T) | lifetime `'a` defined here ... LL | with_signature(cell, t, |cell, t| require(cell, t)); - | ^^^^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of the type `Cell<&'?7 ()>`, which makes the generic argument `&'?7 ()` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance note: external requirements --> $DIR/projection-one-region-trait-bound-closure.rs:60:29 diff --git a/tests/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr b/tests/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr index c157e89ff8f31..4b779103a6dd8 100644 --- a/tests/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr +++ b/tests/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr @@ -23,16 +23,19 @@ LL | | T: Anything<'b, 'c>, | = note: defining type: no_relationships_late::<'?1, '?2, T> -error[E0309]: the associated type `>::AssocType` may not live long enough +error[E0309]: the associated type `>::AssocType` may not live long enough --> $DIR/projection-two-region-trait-bound-closure.rs:38:39 | LL | fn no_relationships_late<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) - | -- the associated type `>::AssocType` must be valid for the lifetime `'a` as defined here... + | -- the associated type `>::AssocType` must be valid for the lifetime `'a` as defined here... ... LL | with_signature(cell, t, |cell, t| require(cell, t)); - | ^^^^^^^^^^^^^^^^ ...so that the type `>::AssocType` will meet its required lifetime bounds + | ^^^^^^^^^^^^^^^^ ...so that the type `>::AssocType` will meet its required lifetime bounds | - = help: consider adding an explicit lifetime bound `>::AssocType: 'a`... +help: consider adding an explicit lifetime bound + | +LL | T: Anything<'b, 'c>, >::AssocType: 'a + | ++++++++++++++++++++++++++++++++++++++++++++ note: external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:48:29 @@ -59,16 +62,19 @@ LL | | 'a: 'a, | = note: defining type: no_relationships_early::<'?1, '?2, '?3, T> -error[E0309]: the associated type `>::AssocType` may not live long enough +error[E0309]: the associated type `>::AssocType` may not live long enough --> $DIR/projection-two-region-trait-bound-closure.rs:48:39 | LL | fn no_relationships_early<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) - | -- the associated type `>::AssocType` must be valid for the lifetime `'a` as defined here... + | -- the associated type `>::AssocType` must be valid for the lifetime `'a` as defined here... ... LL | with_signature(cell, t, |cell, t| require(cell, t)); - | ^^^^^^^^^^^^^^^^ ...so that the type `>::AssocType` will meet its required lifetime bounds + | ^^^^^^^^^^^^^^^^ ...so that the type `>::AssocType` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound | - = help: consider adding an explicit lifetime bound `>::AssocType: 'a`... +LL | 'a: 'a, >::AssocType: 'a + | ++++++++++++++++++++++++++++++++++++++++++++ note: external requirements --> $DIR/projection-two-region-trait-bound-closure.rs:61:29 @@ -182,7 +188,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` - = note: requirement occurs because of the type `Cell<&'?8 ()>`, which makes the generic argument `&'?8 ()` invariant + = note: requirement occurs because of the type `Cell<&'?6 ()>`, which makes the generic argument `&'?6 ()` invariant = note: the struct `Cell` is invariant over the parameter `T` = help: see for more information about variance diff --git a/tests/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.stderr b/tests/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.stderr index f8ecc0c68267f..4b1e59053c939 100644 --- a/tests/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.stderr +++ b/tests/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.stderr @@ -1,13 +1,16 @@ -error[E0309]: the associated type `>::Output` may not live long enough +error[E0309]: the associated type `>::Output` may not live long enough --> $DIR/projection-where-clause-env-wrong-bound.rs:15:5 | LL | fn foo1<'a, 'b, T>() -> &'a () - | -- the associated type `>::Output` must be valid for the lifetime `'a` as defined here... + | -- the associated type `>::Output` must be valid for the lifetime `'a` as defined here... ... LL | bar::() - | ^^^^^^^^^^^^^^^^ ...so that the type `>::Output` will meet its required lifetime bounds + | ^^^^^^^^^^^^^^^^ ...so that the type `>::Output` will meet its required lifetime bounds | - = help: consider adding an explicit lifetime bound `>::Output: 'a`... +help: consider adding an explicit lifetime bound + | +LL | >::Output: 'b, >::Output: 'a + | ++++++++++++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.rs b/tests/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.rs index 987148dcefb0c..9e3590ca71545 100644 --- a/tests/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.rs +++ b/tests/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.rs @@ -12,7 +12,7 @@ where >::Output: 'a, { bar::<>::Output>() - //~^ ERROR the associated type `>::Output` may not live long enough + //~^ ERROR the associated type `>::Output` may not live long enough } fn bar<'a, T>() -> &'a () diff --git a/tests/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.stderr b/tests/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.stderr index 13ad665e2615e..9c93d663e2d37 100644 --- a/tests/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.stderr +++ b/tests/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.stderr @@ -1,13 +1,16 @@ -error[E0309]: the associated type `>::Output` may not live long enough +error[E0309]: the associated type `>::Output` may not live long enough --> $DIR/projection-where-clause-env-wrong-lifetime.rs:14:5 | LL | fn foo1<'a, 'b, T>() -> &'a () - | -- the associated type `>::Output` must be valid for the lifetime `'a` as defined here... + | -- the associated type `>::Output` must be valid for the lifetime `'a` as defined here... ... LL | bar::<>::Output>() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `>::Output` will meet its required lifetime bounds + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `>::Output` will meet its required lifetime bounds | - = help: consider adding an explicit lifetime bound `>::Output: 'a`... +help: consider adding an explicit lifetime bound + | +LL | >::Output: 'a, >::Output: 'a + | ++++++++++++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/nll/type-check-pointer-comparisons.stderr b/tests/ui/nll/type-check-pointer-comparisons.stderr index 37098b585dfea..e362dfb3c6e7a 100644 --- a/tests/ui/nll/type-check-pointer-comparisons.stderr +++ b/tests/ui/nll/type-check-pointer-comparisons.stderr @@ -6,7 +6,7 @@ LL | fn compare_const<'a, 'b>(x: *const &mut &'a i32, y: *const &mut &'b i32) { | | | lifetime `'a` defined here LL | x == y; - | ^ requires that `'a` must outlive `'b` + | ^^^^^^ argument requires that `'a` must outlive `'b` | = help: consider adding the following bound: `'a: 'b` = note: requirement occurs because of a mutable reference to `&i32` @@ -14,14 +14,14 @@ LL | x == y; = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/type-check-pointer-comparisons.rs:4:10 + --> $DIR/type-check-pointer-comparisons.rs:4:5 | LL | fn compare_const<'a, 'b>(x: *const &mut &'a i32, y: *const &mut &'b i32) { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here LL | x == y; - | ^ requires that `'b` must outlive `'a` + | ^^^^^^ argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` = note: requirement occurs because of a mutable reference to `&i32` @@ -38,7 +38,7 @@ LL | fn compare_mut<'a, 'b>(x: *mut &'a i32, y: *mut &'b i32) { | | | lifetime `'a` defined here LL | x == y; - | ^ requires that `'a` must outlive `'b` + | ^^^^^^ argument requires that `'a` must outlive `'b` | = help: consider adding the following bound: `'a: 'b` = note: requirement occurs because of a mutable pointer to `&i32` @@ -46,14 +46,14 @@ LL | x == y; = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/type-check-pointer-comparisons.rs:10:10 + --> $DIR/type-check-pointer-comparisons.rs:10:5 | LL | fn compare_mut<'a, 'b>(x: *mut &'a i32, y: *mut &'b i32) { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here LL | x == y; - | ^ requires that `'b` must outlive `'a` + | ^^^^^^ argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` = note: requirement occurs because of a mutable pointer to `&i32` @@ -72,7 +72,7 @@ LL | fn compare_fn_ptr<'a, 'b, 'c>(f: fn(&'c mut &'a i32), g: fn(&'c mut &'b i32 | | | lifetime `'a` defined here LL | f == g; - | ^ requires that `'a` must outlive `'b` + | ^^^^^^ argument requires that `'a` must outlive `'b` | = help: consider adding the following bound: `'a: 'b` = note: requirement occurs because of a mutable reference to `&i32` @@ -80,14 +80,14 @@ LL | f == g; = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/type-check-pointer-comparisons.rs:16:10 + --> $DIR/type-check-pointer-comparisons.rs:16:5 | LL | fn compare_fn_ptr<'a, 'b, 'c>(f: fn(&'c mut &'a i32), g: fn(&'c mut &'b i32)) { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here LL | f == g; - | ^ requires that `'b` must outlive `'a` + | ^^^^^^ argument requires that `'b` must outlive `'a` | = help: consider adding the following bound: `'b: 'a` = note: requirement occurs because of a mutable reference to `&i32` diff --git a/tests/ui/nll/user-annotations/adt-nullary-enums.stderr b/tests/ui/nll/user-annotations/adt-nullary-enums.stderr index 644fc94f73087..cdaa934122ce0 100644 --- a/tests/ui/nll/user-annotations/adt-nullary-enums.stderr +++ b/tests/ui/nll/user-annotations/adt-nullary-enums.stderr @@ -1,52 +1,49 @@ error[E0597]: `c` does not live long enough --> $DIR/adt-nullary-enums.rs:33:41 | -LL | let c = 66; - | - binding `c` declared here -LL | combine( -LL | SomeEnum::SomeVariant(Cell::new(&c)), - | ----------^^- - | | | - | | borrowed value does not live long enough - | argument requires that `c` is borrowed for `'static` -... -LL | } - | - `c` dropped here while still borrowed +LL | let c = 66; + | - binding `c` declared here +LL | / combine( +LL | | SomeEnum::SomeVariant(Cell::new(&c)), + | | ^^ borrowed value does not live long enough +LL | | SomeEnum::SomeOtherVariant::>, +LL | | ); + | |_____- argument requires that `c` is borrowed for `'static` +LL | } + | - `c` dropped here while still borrowed error[E0597]: `c` does not live long enough --> $DIR/adt-nullary-enums.rs:41:41 | -LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) { - | -- lifetime `'a` defined here -LL | let c = 66; - | - binding `c` declared here -LL | combine( -LL | SomeEnum::SomeVariant(Cell::new(&c)), - | ----------^^- - | | | - | | borrowed value does not live long enough - | argument requires that `c` is borrowed for `'a` -... -LL | } - | - `c` dropped here while still borrowed +LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) { + | -- lifetime `'a` defined here +LL | let c = 66; + | - binding `c` declared here +LL | / combine( +LL | | SomeEnum::SomeVariant(Cell::new(&c)), + | | ^^ borrowed value does not live long enough +LL | | SomeEnum::SomeOtherVariant::>, +LL | | ); + | |_____- argument requires that `c` is borrowed for `'a` +LL | } + | - `c` dropped here while still borrowed error[E0597]: `c` does not live long enough --> $DIR/adt-nullary-enums.rs:54:45 | -LL | fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { - | -- lifetime `'a` defined here -LL | let _closure = || { -LL | let c = 66; - | - binding `c` declared here -LL | combine( -LL | SomeEnum::SomeVariant(Cell::new(&c)), - | ----------^^- - | | | - | | borrowed value does not live long enough - | argument requires that `c` is borrowed for `'a` -... -LL | }; - | - `c` dropped here while still borrowed +LL | fn annot_reference_named_lifetime_in_closure<'a>(_: &'a u32) { + | -- lifetime `'a` defined here +LL | let _closure = || { +LL | let c = 66; + | - binding `c` declared here +LL | / combine( +LL | | SomeEnum::SomeVariant(Cell::new(&c)), + | | ^^ borrowed value does not live long enough +LL | | SomeEnum::SomeOtherVariant::>, +LL | | ); + | |_________- argument requires that `c` is borrowed for `'a` +LL | }; + | - `c` dropped here while still borrowed error: aborting due to 3 previous errors diff --git a/tests/ui/nll/user-annotations/adt-tuple-struct-calls.stderr b/tests/ui/nll/user-annotations/adt-tuple-struct-calls.stderr index 2084697e7e26d..1478ad1431ba4 100644 --- a/tests/ui/nll/user-annotations/adt-tuple-struct-calls.stderr +++ b/tests/ui/nll/user-annotations/adt-tuple-struct-calls.stderr @@ -4,11 +4,9 @@ error[E0597]: `c` does not live long enough LL | let c = 66; | - binding `c` declared here LL | let f = SomeStruct::<&'static u32>; + | -------------------------- assignment requires that `c` is borrowed for `'static` LL | f(&c); - | --^^- - | | | - | | borrowed value does not live long enough - | argument requires that `c` is borrowed for `'static` + | ^^ borrowed value does not live long enough LL | } | - `c` dropped here while still borrowed @@ -20,11 +18,9 @@ LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) { LL | let c = 66; | - binding `c` declared here LL | let f = SomeStruct::<&'a u32>; + | --------------------- assignment requires that `c` is borrowed for `'a` LL | f(&c); - | --^^- - | | | - | | borrowed value does not live long enough - | argument requires that `c` is borrowed for `'a` + | ^^ borrowed value does not live long enough LL | } | - `c` dropped here while still borrowed @@ -37,11 +33,9 @@ LL | let _closure = || { LL | let c = 66; | - binding `c` declared here LL | let f = SomeStruct::<&'a u32>; + | --------------------- assignment requires that `c` is borrowed for `'a` LL | f(&c); - | --^^- - | | | - | | borrowed value does not live long enough - | argument requires that `c` is borrowed for `'a` + | ^^ borrowed value does not live long enough LL | }; | - `c` dropped here while still borrowed diff --git a/tests/ui/nll/user-annotations/closure-substs.polonius.stderr b/tests/ui/nll/user-annotations/closure-substs.polonius.stderr deleted file mode 100644 index af159a6cd1b85..0000000000000 --- a/tests/ui/nll/user-annotations/closure-substs.polonius.stderr +++ /dev/null @@ -1,61 +0,0 @@ -error: lifetime may not live long enough - --> $DIR/closure-substs.rs:8:16 - | -LL | fn foo<'a>() { - | -- lifetime `'a` defined here -... -LL | return x; - | ^ returning this value requires that `'a` must outlive `'static` - | - = help: consider replacing `'a` with `'static` - -error: lifetime may not live long enough - --> $DIR/closure-substs.rs:15:16 - | -LL | |x: &i32| -> &'static i32 { - | - let's call the lifetime of this reference `'1` -LL | return x; - | ^ returning this value requires that `'1` must outlive `'static` - -error: lifetime may not live long enough - --> $DIR/closure-substs.rs:15:16 - | -LL | |x: &i32| -> &'static i32 { - | - - let's call the lifetime of this reference `'2` - | | - | let's call the lifetime of this reference `'1` -LL | return x; - | ^ returning this value requires that `'1` must outlive `'2` - -error: lifetime may not live long enough - --> $DIR/closure-substs.rs:22:9 - | -LL | fn bar<'a>() { - | -- lifetime `'a` defined here -... -LL | b(x); - | ^^^^ argument requires that `'a` must outlive `'static` - | - = help: consider replacing `'a` with `'static` - -error[E0521]: borrowed data escapes outside of closure - --> $DIR/closure-substs.rs:29:9 - | -LL | |x: &i32, b: fn(&'static i32)| { - | - `x` is a reference that is only valid in the closure body -LL | b(x); - | ^^^^ `x` escapes the closure body here - -error[E0521]: borrowed data escapes outside of closure - --> $DIR/closure-substs.rs:29:9 - | -LL | |x: &i32, b: fn(&'static i32)| { - | - - `b` declared here, outside of the closure body - | | - | `x` is a reference that is only valid in the closure body -LL | b(x); - | ^^^^ `x` escapes the closure body here - -error: aborting due to 6 previous errors - -For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/nll/user-annotations/method-ufcs-1.stderr b/tests/ui/nll/user-annotations/method-ufcs-1.stderr index c42ea0172cf75..087e270c70f7d 100644 --- a/tests/ui/nll/user-annotations/method-ufcs-1.stderr +++ b/tests/ui/nll/user-annotations/method-ufcs-1.stderr @@ -4,11 +4,10 @@ error[E0597]: `a` does not live long enough LL | let a = 22; | - binding `a` declared here ... +LL | let x = <&'static u32 as Bazoom<_>>::method; + | ----------------------------------- assignment requires that `a` is borrowed for `'static` LL | x(&a, b, c); - | --^^------- - | | | - | | borrowed value does not live long enough - | argument requires that `a` is borrowed for `'static` + | ^^ borrowed value does not live long enough LL | } | - `a` dropped here while still borrowed diff --git a/tests/ui/nll/user-annotations/method-ufcs-2.stderr b/tests/ui/nll/user-annotations/method-ufcs-2.stderr index 287337c7d52d3..c89bed3b1b18f 100644 --- a/tests/ui/nll/user-annotations/method-ufcs-2.stderr +++ b/tests/ui/nll/user-annotations/method-ufcs-2.stderr @@ -4,11 +4,10 @@ error[E0597]: `a` does not live long enough LL | let a = 22; | - binding `a` declared here ... +LL | let x = <&'static u32 as Bazoom<_>>::method; + | ----------------------------------- assignment requires that `a` is borrowed for `'static` LL | x(&a, b, c); - | --^^------- - | | | - | | borrowed value does not live long enough - | argument requires that `a` is borrowed for `'static` + | ^^ borrowed value does not live long enough LL | } | - `a` dropped here while still borrowed diff --git a/tests/ui/nll/where_clauses_in_structs.stderr b/tests/ui/nll/where_clauses_in_structs.stderr index 4cc7e5ab1b4c7..19a1ce00e9a8f 100644 --- a/tests/ui/nll/where_clauses_in_structs.stderr +++ b/tests/ui/nll/where_clauses_in_structs.stderr @@ -1,12 +1,12 @@ error: lifetime may not live long enough - --> $DIR/where_clauses_in_structs.rs:11:11 + --> $DIR/where_clauses_in_structs.rs:11:14 | LL | fn bar<'a, 'b>(x: Cell<&'a u32>, y: Cell<&'b u32>) { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here LL | Foo { x, y }; - | ^ this usage requires that `'a` must outlive `'b` + | ^ this usage requires that `'a` must outlive `'b` | = help: consider adding the following bound: `'a: 'b` = note: requirement occurs because of the type `Cell<&u32>`, which makes the generic argument `&u32` invariant diff --git a/tests/ui/no-capture-arc.stderr b/tests/ui/no-capture-arc.stderr index 38432c851c522..4a51ddb67a39d 100644 --- a/tests/ui/no-capture-arc.stderr +++ b/tests/ui/no-capture-arc.stderr @@ -1,5 +1,5 @@ error[E0382]: borrow of moved value: `arc_v` - --> $DIR/no-capture-arc.rs:14:16 + --> $DIR/no-capture-arc.rs:14:18 | LL | let arc_v = Arc::new(v); | ----- move occurs because `arc_v` has type `Arc>`, which does not implement the `Copy` trait @@ -10,7 +10,7 @@ LL | assert_eq!((*arc_v)[3], 4); | ----- variable moved due to use in closure ... LL | assert_eq!((*arc_v)[2], 3); - | ^^^^^^^^ value borrowed here after move + | ^^^^^ value borrowed here after move | = note: borrow occurs due to deref coercion to `Vec` diff --git a/tests/ui/no-reuse-move-arc.stderr b/tests/ui/no-reuse-move-arc.stderr index cdeb6eadc174f..61f4837dc0e66 100644 --- a/tests/ui/no-reuse-move-arc.stderr +++ b/tests/ui/no-reuse-move-arc.stderr @@ -1,5 +1,5 @@ error[E0382]: borrow of moved value: `arc_v` - --> $DIR/no-reuse-move-arc.rs:12:16 + --> $DIR/no-reuse-move-arc.rs:12:18 | LL | let arc_v = Arc::new(v); | ----- move occurs because `arc_v` has type `Arc>`, which does not implement the `Copy` trait @@ -10,7 +10,7 @@ LL | assert_eq!((*arc_v)[3], 4); | ----- variable moved due to use in closure ... LL | assert_eq!((*arc_v)[2], 3); - | ^^^^^^^^ value borrowed here after move + | ^^^^^ value borrowed here after move | = note: borrow occurs due to deref coercion to `Vec` diff --git a/tests/ui/non-fmt-panic.stderr b/tests/ui/non-fmt-panic.stderr index 162802b7f610a..0134a8ddf2924 100644 --- a/tests/ui/non-fmt-panic.stderr +++ b/tests/ui/non-fmt-panic.stderr @@ -184,8 +184,9 @@ LL | std::panic!("{}", 123); | +++++ help: or use std::panic::panic_any instead | -LL | std::panic::panic_any(123); - | ~~~~~~~~~~~~~~~~~~~~~ +LL - std::panic!(123); +LL + std::panic::panic_any(123); + | warning: panic message is not a string literal --> $DIR/non-fmt-panic.rs:31:18 @@ -214,8 +215,9 @@ LL | panic!("{:?}", Some(123)); | +++++++ help: or use std::panic::panic_any instead | -LL | std::panic::panic_any(Some(123)); - | ~~~~~~~~~~~~~~~~~~~~~ +LL - panic!(Some(123)); +LL + std::panic::panic_any(Some(123)); + | warning: panic message contains an unused formatting placeholder --> $DIR/non-fmt-panic.rs:33:12 @@ -267,8 +269,9 @@ LL | panic!("{}", a!()); | +++++ help: or use std::panic::panic_any instead | -LL | std::panic::panic_any(a!()); - | ~~~~~~~~~~~~~~~~~~~~~ +LL - panic!(a!()); +LL + std::panic::panic_any(a!()); + | warning: panic message is not a string literal --> $DIR/non-fmt-panic.rs:47:18 @@ -357,8 +360,9 @@ LL | panic!["{}", 123]; | +++++ help: or use std::panic::panic_any instead | -LL | std::panic::panic_any(123); - | ~~~~~~~~~~~~~~~~~~~~~~ ~ +LL - panic![123]; +LL + std::panic::panic_any(123); + | warning: panic message is not a string literal --> $DIR/non-fmt-panic.rs:55:12 @@ -374,8 +378,9 @@ LL | panic!{"{}", 123}; | +++++ help: or use std::panic::panic_any instead | -LL | std::panic::panic_any(123); - | ~~~~~~~~~~~~~~~~~~~~~~ ~ +LL - panic!{123}; +LL + std::panic::panic_any(123); + | warning: panic message is not a string literal --> $DIR/non-fmt-panic.rs:72:12 @@ -411,8 +416,9 @@ LL | panic!("{:?}", v); | +++++++ help: or use std::panic::panic_any instead | -LL | std::panic::panic_any(v); - | ~~~~~~~~~~~~~~~~~~~~~ +LL - panic!(v); +LL + std::panic::panic_any(v); + | warning: panic message is not a string literal --> $DIR/non-fmt-panic.rs:78:20 @@ -441,8 +447,9 @@ LL | panic!("{}", v); | +++++ help: or use std::panic::panic_any instead | -LL | std::panic::panic_any(v); - | ~~~~~~~~~~~~~~~~~~~~~ +LL - panic!(v); +LL + std::panic::panic_any(v); + | warning: panic message is not a string literal --> $DIR/non-fmt-panic.rs:83:20 @@ -471,8 +478,9 @@ LL | panic!("{}", v); | +++++ help: or use std::panic::panic_any instead | -LL | std::panic::panic_any(v); - | ~~~~~~~~~~~~~~~~~~~~~ +LL - panic!(v); +LL + std::panic::panic_any(v); + | warning: panic message is not a string literal --> $DIR/non-fmt-panic.rs:88:20 diff --git a/tests/ui/not-enough-arguments.stderr b/tests/ui/not-enough-arguments.stderr index 89e9886666745..637c2774d5a31 100644 --- a/tests/ui/not-enough-arguments.stderr +++ b/tests/ui/not-enough-arguments.stderr @@ -8,11 +8,12 @@ note: function defined here --> $DIR/not-enough-arguments.rs:5:4 | LL | fn foo(a: isize, b: isize, c: isize, d:isize) { - | ^^^ -------- -------- -------- ------- + | ^^^ ------- help: provide the argument | -LL | foo(1, 2, 3, /* isize */); - | ~~~~~~~~~~~~~~~~~~~~~~ +LL - foo(1, 2, 3); +LL + foo(1, 2, 3, /* isize */); + | error[E0061]: this function takes 6 arguments but 3 arguments were supplied --> $DIR/not-enough-arguments.rs:29:3 @@ -25,12 +26,7 @@ note: function defined here | LL | fn bar( | ^^^ -LL | a: i32, - | ------ -LL | b: i32, - | ------ -LL | c: i32, - | ------ +... LL | d: i32, | ------ LL | e: i32, @@ -39,8 +35,9 @@ LL | f: i32, | ------ help: provide the arguments | -LL | bar(1, 2, 3, /* i32 */, /* i32 */, /* i32 */); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - bar(1, 2, 3); +LL + bar(1, 2, 3, /* i32 */, /* i32 */, /* i32 */); + | error: aborting due to 2 previous errors diff --git a/tests/ui/numbers-arithmetic/arith-unsigned.rs b/tests/ui/numbers-arithmetic/arith-unsigned.rs index 5a285ceca32db..4a1bae438ca2e 100644 --- a/tests/ui/numbers-arithmetic/arith-unsigned.rs +++ b/tests/ui/numbers-arithmetic/arith-unsigned.rs @@ -3,22 +3,22 @@ // Unsigned integer operations pub fn main() { - assert!((0u8 < 255u8)); - assert!((0u8 <= 255u8)); - assert!((255u8 > 0u8)); - assert!((255u8 >= 0u8)); + assert!(0u8 < 255u8); + assert!(0u8 <= 255u8); + assert!(255u8 > 0u8); + assert!(255u8 >= 0u8); assert_eq!(250u8 / 10u8, 25u8); assert_eq!(255u8 % 10u8, 5u8); - assert!((0u16 < 60000u16)); - assert!((0u16 <= 60000u16)); - assert!((60000u16 > 0u16)); - assert!((60000u16 >= 0u16)); + assert!(0u16 < 60000u16); + assert!(0u16 <= 60000u16); + assert!(60000u16 > 0u16); + assert!(60000u16 >= 0u16); assert_eq!(60000u16 / 10u16, 6000u16); assert_eq!(60005u16 % 10u16, 5u16); - assert!((0u32 < 4000000000u32)); - assert!((0u32 <= 4000000000u32)); - assert!((4000000000u32 > 0u32)); - assert!((4000000000u32 >= 0u32)); + assert!(0u32 < 4000000000u32); + assert!(0u32 <= 4000000000u32); + assert!(4000000000u32 > 0u32); + assert!(4000000000u32 >= 0u32); assert_eq!(4000000000u32 / 10u32, 400000000u32); assert_eq!(4000000005u32 % 10u32, 5u32); // 64-bit numbers have some flakiness yet. Not tested diff --git a/tests/ui/numbers-arithmetic/divide-by-zero.rs b/tests/ui/numbers-arithmetic/divide-by-zero.rs index 626daf9771ded..a05abadf4bb45 100644 --- a/tests/ui/numbers-arithmetic/divide-by-zero.rs +++ b/tests/ui/numbers-arithmetic/divide-by-zero.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:attempt to divide by zero -//@ ignore-emscripten no processes +//@ needs-subprocess #[allow(unconditional_panic)] fn main() { diff --git a/tests/ui/numbers-arithmetic/float-nan.rs b/tests/ui/numbers-arithmetic/float-nan.rs index 7d1af0155da53..ee3718f6f939b 100644 --- a/tests/ui/numbers-arithmetic/float-nan.rs +++ b/tests/ui/numbers-arithmetic/float-nan.rs @@ -2,7 +2,7 @@ pub fn main() { let nan: f64 = f64::NAN; - assert!((nan).is_nan()); + assert!(nan.is_nan()); let inf: f64 = f64::INFINITY; let neg_inf: f64 = -f64::INFINITY; diff --git a/tests/ui/numbers-arithmetic/float2.rs b/tests/ui/numbers-arithmetic/float2.rs index 1b7add01cc631..515220fee9ffe 100644 --- a/tests/ui/numbers-arithmetic/float2.rs +++ b/tests/ui/numbers-arithmetic/float2.rs @@ -15,12 +15,12 @@ pub fn main() { let j = 3.1e+9f64; let k = 3.2e-10f64; assert_eq!(a, b); - assert!((c < b)); + assert!(c < b); assert_eq!(c, d); - assert!((e < g)); - assert!((f < h)); + assert!(e < g); + assert!(f < h); assert_eq!(g, 1000000.0f32); assert_eq!(h, i); - assert!((j > k)); - assert!((k < a)); + assert!(j > k); + assert!(k < a); } diff --git a/tests/ui/numbers-arithmetic/floatlits.rs b/tests/ui/numbers-arithmetic/floatlits.rs index 21f19b69c49e7..2dab224201193 100644 --- a/tests/ui/numbers-arithmetic/floatlits.rs +++ b/tests/ui/numbers-arithmetic/floatlits.rs @@ -4,9 +4,9 @@ pub fn main() { let f = 4.999999999999f64; - assert!((f > 4.90f64)); - assert!((f < 5.0f64)); + assert!(f > 4.90f64); + assert!(f < 5.0f64); let g = 4.90000000001e-10f64; - assert!((g > 5e-11f64)); - assert!((g < 5e-9f64)); + assert!(g > 5e-11f64); + assert!(g < 5e-9f64); } diff --git a/tests/ui/numbers-arithmetic/int-abs-overflow.rs b/tests/ui/numbers-arithmetic/int-abs-overflow.rs index e91141380482d..6397f62d06551 100644 --- a/tests/ui/numbers-arithmetic/int-abs-overflow.rs +++ b/tests/ui/numbers-arithmetic/int-abs-overflow.rs @@ -1,6 +1,6 @@ //@ run-pass //@ compile-flags: -C overflow-checks=on -//@ ignore-emscripten no threads support +//@ needs-threads //@ needs-unwind use std::thread; diff --git a/tests/ui/numbers-arithmetic/issue-8460.rs b/tests/ui/numbers-arithmetic/issue-8460.rs index 9d3044a7ca022..87867fdc93e41 100644 --- a/tests/ui/numbers-arithmetic/issue-8460.rs +++ b/tests/ui/numbers-arithmetic/issue-8460.rs @@ -1,6 +1,6 @@ //@ run-pass #![allow(unused_must_use)] -//@ ignore-emscripten no threads support +//@ needs-threads //@ needs-unwind #![feature(rustc_attrs)] diff --git a/tests/ui/numbers-arithmetic/mod-zero.rs b/tests/ui/numbers-arithmetic/mod-zero.rs index f3cc7c9fc88f0..300bd765c4037 100644 --- a/tests/ui/numbers-arithmetic/mod-zero.rs +++ b/tests/ui/numbers-arithmetic/mod-zero.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:attempt to calculate the remainder with a divisor of zero -//@ ignore-emscripten no processes +//@ needs-subprocess #[allow(unconditional_panic)] fn main() { diff --git a/tests/ui/numbers-arithmetic/overflowing-add.rs b/tests/ui/numbers-arithmetic/overflowing-add.rs index 16583f6eb749f..c1f498c802b41 100644 --- a/tests/ui/numbers-arithmetic/overflowing-add.rs +++ b/tests/ui/numbers-arithmetic/overflowing-add.rs @@ -2,7 +2,7 @@ //@ error-pattern:thread 'main' panicked //@ error-pattern:attempt to add with overflow //@ compile-flags: -C debug-assertions -//@ ignore-emscripten no processes +//@ needs-subprocess #![allow(arithmetic_overflow)] diff --git a/tests/ui/numbers-arithmetic/overflowing-mul.rs b/tests/ui/numbers-arithmetic/overflowing-mul.rs index 59575d2e86e07..0eece5369299b 100644 --- a/tests/ui/numbers-arithmetic/overflowing-mul.rs +++ b/tests/ui/numbers-arithmetic/overflowing-mul.rs @@ -1,7 +1,7 @@ //@ run-fail //@ error-pattern:thread 'main' panicked //@ error-pattern:attempt to multiply with overflow -//@ ignore-emscripten no processes +//@ needs-subprocess //@ compile-flags: -C debug-assertions #![allow(arithmetic_overflow)] diff --git a/tests/ui/numbers-arithmetic/overflowing-neg-nonzero.rs b/tests/ui/numbers-arithmetic/overflowing-neg-nonzero.rs index 8aa0d04e500c7..c5059c002b4bf 100644 --- a/tests/ui/numbers-arithmetic/overflowing-neg-nonzero.rs +++ b/tests/ui/numbers-arithmetic/overflowing-neg-nonzero.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:attempt to negate with overflow -//@ ignore-emscripten no processes +//@ needs-subprocess //@ compile-flags: -C debug-assertions #![allow(arithmetic_overflow)] diff --git a/tests/ui/numbers-arithmetic/overflowing-pow-signed.rs b/tests/ui/numbers-arithmetic/overflowing-pow-signed.rs index 69e22c2262a3a..28deb7cf6bad1 100644 --- a/tests/ui/numbers-arithmetic/overflowing-pow-signed.rs +++ b/tests/ui/numbers-arithmetic/overflowing-pow-signed.rs @@ -1,7 +1,7 @@ //@ run-fail //@ error-pattern:thread 'main' panicked //@ error-pattern:attempt to multiply with overflow -//@ ignore-emscripten no processes +//@ needs-subprocess //@ compile-flags: -C debug-assertions fn main() { diff --git a/tests/ui/numbers-arithmetic/overflowing-pow-unsigned.rs b/tests/ui/numbers-arithmetic/overflowing-pow-unsigned.rs index f980033c480e1..dea9a4d5428be 100644 --- a/tests/ui/numbers-arithmetic/overflowing-pow-unsigned.rs +++ b/tests/ui/numbers-arithmetic/overflowing-pow-unsigned.rs @@ -1,7 +1,7 @@ //@ run-fail //@ error-pattern:thread 'main' panicked //@ error-pattern:attempt to multiply with overflow -//@ ignore-emscripten no processes +//@ needs-subprocess //@ compile-flags: -C debug-assertions fn main() { diff --git a/tests/ui/numbers-arithmetic/overflowing-sub.rs b/tests/ui/numbers-arithmetic/overflowing-sub.rs index 44aadf6b3e70e..88b1b693f6326 100644 --- a/tests/ui/numbers-arithmetic/overflowing-sub.rs +++ b/tests/ui/numbers-arithmetic/overflowing-sub.rs @@ -1,7 +1,7 @@ //@ run-fail //@ error-pattern:thread 'main' panicked //@ error-pattern:attempt to subtract with overflow -//@ ignore-emscripten no processes +//@ needs-subprocess //@ compile-flags: -C debug-assertions #![allow(arithmetic_overflow)] diff --git a/tests/ui/numeric/const-scope.stderr b/tests/ui/numeric/const-scope.stderr index 4e4bcdf234dcf..2c8d4da9d2182 100644 --- a/tests/ui/numeric/const-scope.stderr +++ b/tests/ui/numeric/const-scope.stderr @@ -6,8 +6,9 @@ LL | const C: i32 = 1i8; | help: change the type of the numeric literal from `i8` to `i32` | -LL | const C: i32 = 1i32; - | ~~~ +LL - const C: i32 = 1i8; +LL + const C: i32 = 1i32; + | error[E0308]: mismatched types --> $DIR/const-scope.rs:2:15 @@ -25,8 +26,9 @@ LL | let c: i32 = 1i8; | help: change the type of the numeric literal from `i8` to `i32` | -LL | let c: i32 = 1i32; - | ~~~ +LL - let c: i32 = 1i8; +LL + let c: i32 = 1i32; + | error[E0308]: mismatched types --> $DIR/const-scope.rs:6:17 @@ -46,8 +48,9 @@ LL | let c: i32 = 1i8; | help: change the type of the numeric literal from `i8` to `i32` | -LL | let c: i32 = 1i32; - | ~~~ +LL - let c: i32 = 1i8; +LL + let c: i32 = 1i32; + | error[E0308]: mismatched types --> $DIR/const-scope.rs:11:17 diff --git a/tests/ui/numeric/numeric-fields.stderr b/tests/ui/numeric/numeric-fields.stderr index 8ab1718ff5e8f..6877bb3bef4e5 100644 --- a/tests/ui/numeric/numeric-fields.stderr +++ b/tests/ui/numeric/numeric-fields.stderr @@ -9,8 +9,9 @@ LL | let s = S{0b1: 10, 0: 11}; | help: `S` is a tuple struct, use the appropriate syntax | -LL | let s = S(/* u8 */, /* u16 */); - | ~~~~~~~~~~~~~~~~~~~~~~ +LL - let s = S{0b1: 10, 0: 11}; +LL + let s = S(/* u8 */, /* u16 */); + | error[E0026]: struct `S` does not have a field named `0x1` --> $DIR/numeric-fields.rs:7:17 diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix-i32.stderr b/tests/ui/numeric/numeric-suffix/numeric-suffix-i32.stderr index f4fb14e79923c..6c6b8b51c226d 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix-i32.stderr +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix-i32.stderr @@ -13,8 +13,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `usize` to `i32` | -LL | foo::(42_i32); - | ~~~ +LL - foo::(42_usize); +LL + foo::(42_i32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-i32.rs:32:16 @@ -31,8 +32,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u64` to `i32` | -LL | foo::(42_i32); - | ~~~ +LL - foo::(42_u64); +LL + foo::(42_i32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-i32.rs:36:16 @@ -49,8 +51,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u32` to `i32` | -LL | foo::(42_i32); - | ~~~ +LL - foo::(42_u32); +LL + foo::(42_i32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-i32.rs:40:16 @@ -67,8 +70,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u16` to `i32` | -LL | foo::(42_i32); - | ~~~ +LL - foo::(42_u16); +LL + foo::(42_i32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-i32.rs:44:16 @@ -85,8 +89,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u8` to `i32` | -LL | foo::(42_i32); - | ~~~ +LL - foo::(42_u8); +LL + foo::(42_i32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-i32.rs:48:16 @@ -103,8 +108,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `isize` to `i32` | -LL | foo::(42_i32); - | ~~~ +LL - foo::(42_isize); +LL + foo::(42_i32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-i32.rs:52:16 @@ -121,8 +127,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i64` to `i32` | -LL | foo::(42_i32); - | ~~~ +LL - foo::(42_i64); +LL + foo::(42_i32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-i32.rs:57:16 @@ -139,8 +146,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i16` to `i32` | -LL | foo::(42_i32); - | ~~~ +LL - foo::(42_i16); +LL + foo::(42_i32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-i32.rs:61:16 @@ -157,8 +165,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i8` to `i32` | -LL | foo::(42_i32); - | ~~~ +LL - foo::(42_i8); +LL + foo::(42_i32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-i32.rs:65:16 @@ -175,8 +184,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `f64` to `i32` | -LL | foo::(42i32); - | ~~~ +LL - foo::(42.0_f64); +LL + foo::(42i32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-i32.rs:69:16 @@ -193,8 +203,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `f32` to `i32` | -LL | foo::(42i32); - | ~~~ +LL - foo::(42.0_f32); +LL + foo::(42i32); + | error: aborting due to 11 previous errors diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix-i64.stderr b/tests/ui/numeric/numeric-suffix/numeric-suffix-i64.stderr index 47efe9f08bbd2..7c26dd7be1c64 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix-i64.stderr +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix-i64.stderr @@ -13,8 +13,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `usize` to `i64` | -LL | foo::(42_i64); - | ~~~ +LL - foo::(42_usize); +LL + foo::(42_i64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-i64.rs:32:16 @@ -31,8 +32,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u64` to `i64` | -LL | foo::(42_i64); - | ~~~ +LL - foo::(42_u64); +LL + foo::(42_i64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-i64.rs:36:16 @@ -49,8 +51,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u32` to `i64` | -LL | foo::(42_i64); - | ~~~ +LL - foo::(42_u32); +LL + foo::(42_i64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-i64.rs:40:16 @@ -67,8 +70,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u16` to `i64` | -LL | foo::(42_i64); - | ~~~ +LL - foo::(42_u16); +LL + foo::(42_i64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-i64.rs:44:16 @@ -85,8 +89,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u8` to `i64` | -LL | foo::(42_i64); - | ~~~ +LL - foo::(42_u8); +LL + foo::(42_i64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-i64.rs:48:16 @@ -103,8 +108,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `isize` to `i64` | -LL | foo::(42_i64); - | ~~~ +LL - foo::(42_isize); +LL + foo::(42_i64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-i64.rs:53:16 @@ -121,8 +127,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i32` to `i64` | -LL | foo::(42_i64); - | ~~~ +LL - foo::(42_i32); +LL + foo::(42_i64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-i64.rs:57:16 @@ -139,8 +146,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i16` to `i64` | -LL | foo::(42_i64); - | ~~~ +LL - foo::(42_i16); +LL + foo::(42_i64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-i64.rs:61:16 @@ -157,8 +165,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i8` to `i64` | -LL | foo::(42_i64); - | ~~~ +LL - foo::(42_i8); +LL + foo::(42_i64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-i64.rs:65:16 @@ -175,8 +184,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `f64` to `i64` | -LL | foo::(42i64); - | ~~~ +LL - foo::(42.0_f64); +LL + foo::(42i64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-i64.rs:69:16 @@ -193,8 +203,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `f32` to `i64` | -LL | foo::(42i64); - | ~~~ +LL - foo::(42.0_f32); +LL + foo::(42i64); + | error: aborting due to 11 previous errors diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix-isize.stderr b/tests/ui/numeric/numeric-suffix/numeric-suffix-isize.stderr index 28b79413f68d0..8365350f2bfec 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix-isize.stderr +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix-isize.stderr @@ -13,8 +13,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `usize` to `isize` | -LL | foo::(42_isize); - | ~~~~~ +LL - foo::(42_usize); +LL + foo::(42_isize); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-isize.rs:32:18 @@ -31,8 +32,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u64` to `isize` | -LL | foo::(42_isize); - | ~~~~~ +LL - foo::(42_u64); +LL + foo::(42_isize); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-isize.rs:36:18 @@ -49,8 +51,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u32` to `isize` | -LL | foo::(42_isize); - | ~~~~~ +LL - foo::(42_u32); +LL + foo::(42_isize); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-isize.rs:40:18 @@ -67,8 +70,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u16` to `isize` | -LL | foo::(42_isize); - | ~~~~~ +LL - foo::(42_u16); +LL + foo::(42_isize); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-isize.rs:44:18 @@ -85,8 +89,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u8` to `isize` | -LL | foo::(42_isize); - | ~~~~~ +LL - foo::(42_u8); +LL + foo::(42_isize); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-isize.rs:49:18 @@ -103,8 +108,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i64` to `isize` | -LL | foo::(42_isize); - | ~~~~~ +LL - foo::(42_i64); +LL + foo::(42_isize); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-isize.rs:53:18 @@ -121,8 +127,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i32` to `isize` | -LL | foo::(42_isize); - | ~~~~~ +LL - foo::(42_i32); +LL + foo::(42_isize); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-isize.rs:57:18 @@ -139,8 +146,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i16` to `isize` | -LL | foo::(42_isize); - | ~~~~~ +LL - foo::(42_i16); +LL + foo::(42_isize); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-isize.rs:61:18 @@ -157,8 +165,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i8` to `isize` | -LL | foo::(42_isize); - | ~~~~~ +LL - foo::(42_i8); +LL + foo::(42_isize); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-isize.rs:65:18 @@ -175,8 +184,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `f64` to `isize` | -LL | foo::(42isize); - | ~~~~~ +LL - foo::(42.0_f64); +LL + foo::(42isize); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-isize.rs:69:18 @@ -193,8 +203,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `f32` to `isize` | -LL | foo::(42isize); - | ~~~~~ +LL - foo::(42.0_f32); +LL + foo::(42isize); + | error: aborting due to 11 previous errors diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix-u32.stderr b/tests/ui/numeric/numeric-suffix/numeric-suffix-u32.stderr index d966893a83b48..610e6ece27691 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix-u32.stderr +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix-u32.stderr @@ -13,8 +13,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `usize` to `u32` | -LL | foo::(42_u32); - | ~~~ +LL - foo::(42_usize); +LL + foo::(42_u32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-u32.rs:32:16 @@ -31,8 +32,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u64` to `u32` | -LL | foo::(42_u32); - | ~~~ +LL - foo::(42_u64); +LL + foo::(42_u32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-u32.rs:37:16 @@ -49,8 +51,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u16` to `u32` | -LL | foo::(42_u32); - | ~~~ +LL - foo::(42_u16); +LL + foo::(42_u32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-u32.rs:41:16 @@ -67,8 +70,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u8` to `u32` | -LL | foo::(42_u32); - | ~~~ +LL - foo::(42_u8); +LL + foo::(42_u32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-u32.rs:45:16 @@ -85,8 +89,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `isize` to `u32` | -LL | foo::(42_u32); - | ~~~ +LL - foo::(42_isize); +LL + foo::(42_u32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-u32.rs:49:16 @@ -103,8 +108,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i64` to `u32` | -LL | foo::(42_u32); - | ~~~ +LL - foo::(42_i64); +LL + foo::(42_u32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-u32.rs:53:16 @@ -121,8 +127,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i32` to `u32` | -LL | foo::(42_u32); - | ~~~ +LL - foo::(42_i32); +LL + foo::(42_u32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-u32.rs:57:16 @@ -139,8 +146,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i16` to `u32` | -LL | foo::(42_u32); - | ~~~ +LL - foo::(42_i16); +LL + foo::(42_u32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-u32.rs:61:16 @@ -157,8 +165,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i8` to `u32` | -LL | foo::(42_u32); - | ~~~ +LL - foo::(42_i8); +LL + foo::(42_u32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-u32.rs:65:16 @@ -175,8 +184,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `f64` to `u32` | -LL | foo::(42u32); - | ~~~ +LL - foo::(42.0_f64); +LL + foo::(42u32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-u32.rs:69:16 @@ -193,8 +203,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `f32` to `u32` | -LL | foo::(42u32); - | ~~~ +LL - foo::(42.0_f32); +LL + foo::(42u32); + | error: aborting due to 11 previous errors diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix-u64.stderr b/tests/ui/numeric/numeric-suffix/numeric-suffix-u64.stderr index ff332fa914dd0..112dddccd6f47 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix-u64.stderr +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix-u64.stderr @@ -13,8 +13,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `usize` to `u64` | -LL | foo::(42_u64); - | ~~~ +LL - foo::(42_usize); +LL + foo::(42_u64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-u64.rs:33:16 @@ -31,8 +32,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u32` to `u64` | -LL | foo::(42_u64); - | ~~~ +LL - foo::(42_u32); +LL + foo::(42_u64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-u64.rs:37:16 @@ -49,8 +51,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u16` to `u64` | -LL | foo::(42_u64); - | ~~~ +LL - foo::(42_u16); +LL + foo::(42_u64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-u64.rs:41:16 @@ -67,8 +70,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u8` to `u64` | -LL | foo::(42_u64); - | ~~~ +LL - foo::(42_u8); +LL + foo::(42_u64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-u64.rs:45:16 @@ -85,8 +89,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `isize` to `u64` | -LL | foo::(42_u64); - | ~~~ +LL - foo::(42_isize); +LL + foo::(42_u64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-u64.rs:49:16 @@ -103,8 +108,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i64` to `u64` | -LL | foo::(42_u64); - | ~~~ +LL - foo::(42_i64); +LL + foo::(42_u64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-u64.rs:53:16 @@ -121,8 +127,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i32` to `u64` | -LL | foo::(42_u64); - | ~~~ +LL - foo::(42_i32); +LL + foo::(42_u64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-u64.rs:57:16 @@ -139,8 +146,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i16` to `u64` | -LL | foo::(42_u64); - | ~~~ +LL - foo::(42_i16); +LL + foo::(42_u64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-u64.rs:61:16 @@ -157,8 +165,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i8` to `u64` | -LL | foo::(42_u64); - | ~~~ +LL - foo::(42_i8); +LL + foo::(42_u64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-u64.rs:65:16 @@ -175,8 +184,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `f64` to `u64` | -LL | foo::(42u64); - | ~~~ +LL - foo::(42.0_f64); +LL + foo::(42u64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-u64.rs:69:16 @@ -193,8 +203,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `f32` to `u64` | -LL | foo::(42u64); - | ~~~ +LL - foo::(42.0_f32); +LL + foo::(42u64); + | error: aborting due to 11 previous errors diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix-usize.stderr b/tests/ui/numeric/numeric-suffix/numeric-suffix-usize.stderr index 4889abee69c2a..e7d6a04f18ef3 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix-usize.stderr +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix-usize.stderr @@ -13,8 +13,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u64` to `usize` | -LL | foo::(42_usize); - | ~~~~~ +LL - foo::(42_u64); +LL + foo::(42_usize); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-usize.rs:33:18 @@ -31,8 +32,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u32` to `usize` | -LL | foo::(42_usize); - | ~~~~~ +LL - foo::(42_u32); +LL + foo::(42_usize); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-usize.rs:37:18 @@ -49,8 +51,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u16` to `usize` | -LL | foo::(42_usize); - | ~~~~~ +LL - foo::(42_u16); +LL + foo::(42_usize); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-usize.rs:41:18 @@ -67,8 +70,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u8` to `usize` | -LL | foo::(42_usize); - | ~~~~~ +LL - foo::(42_u8); +LL + foo::(42_usize); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-usize.rs:45:18 @@ -85,8 +89,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `isize` to `usize` | -LL | foo::(42_usize); - | ~~~~~ +LL - foo::(42_isize); +LL + foo::(42_usize); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-usize.rs:49:18 @@ -103,8 +108,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i64` to `usize` | -LL | foo::(42_usize); - | ~~~~~ +LL - foo::(42_i64); +LL + foo::(42_usize); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-usize.rs:53:18 @@ -121,8 +127,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i32` to `usize` | -LL | foo::(42_usize); - | ~~~~~ +LL - foo::(42_i32); +LL + foo::(42_usize); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-usize.rs:57:18 @@ -139,8 +146,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i16` to `usize` | -LL | foo::(42_usize); - | ~~~~~ +LL - foo::(42_i16); +LL + foo::(42_usize); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-usize.rs:61:18 @@ -157,8 +165,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i8` to `usize` | -LL | foo::(42_usize); - | ~~~~~ +LL - foo::(42_i8); +LL + foo::(42_usize); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-usize.rs:65:18 @@ -175,8 +184,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `f64` to `usize` | -LL | foo::(42usize); - | ~~~~~ +LL - foo::(42.0_f64); +LL + foo::(42usize); + | error[E0308]: mismatched types --> $DIR/numeric-suffix-usize.rs:69:18 @@ -193,8 +203,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `f32` to `usize` | -LL | foo::(42usize); - | ~~~~~ +LL - foo::(42.0_f32); +LL + foo::(42usize); + | error: aborting due to 11 previous errors diff --git a/tests/ui/numeric/numeric-suffix/numeric-suffix.stderr b/tests/ui/numeric/numeric-suffix/numeric-suffix.stderr index e05913b9c621c..d26639a76f0ff 100644 --- a/tests/ui/numeric/numeric-suffix/numeric-suffix.stderr +++ b/tests/ui/numeric/numeric-suffix/numeric-suffix.stderr @@ -13,8 +13,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `usize` to `u16` | -LL | foo::(42_u16); - | ~~~ +LL - foo::(42_usize); +LL + foo::(42_u16); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:147:16 @@ -31,8 +32,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u64` to `u16` | -LL | foo::(42_u16); - | ~~~ +LL - foo::(42_u64); +LL + foo::(42_u16); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:151:16 @@ -49,8 +51,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u32` to `u16` | -LL | foo::(42_u16); - | ~~~ +LL - foo::(42_u32); +LL + foo::(42_u16); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:156:16 @@ -67,8 +70,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u8` to `u16` | -LL | foo::(42_u16); - | ~~~ +LL - foo::(42_u8); +LL + foo::(42_u16); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:160:16 @@ -85,8 +89,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `isize` to `u16` | -LL | foo::(42_u16); - | ~~~ +LL - foo::(42_isize); +LL + foo::(42_u16); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:164:16 @@ -103,8 +108,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i64` to `u16` | -LL | foo::(42_u16); - | ~~~ +LL - foo::(42_i64); +LL + foo::(42_u16); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:168:16 @@ -121,8 +127,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i32` to `u16` | -LL | foo::(42_u16); - | ~~~ +LL - foo::(42_i32); +LL + foo::(42_u16); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:172:16 @@ -139,8 +146,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i16` to `u16` | -LL | foo::(42_u16); - | ~~~ +LL - foo::(42_i16); +LL + foo::(42_u16); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:176:16 @@ -157,8 +165,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i8` to `u16` | -LL | foo::(42_u16); - | ~~~ +LL - foo::(42_i8); +LL + foo::(42_u16); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:180:16 @@ -175,8 +184,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `f64` to `u16` | -LL | foo::(42u16); - | ~~~ +LL - foo::(42.0_f64); +LL + foo::(42u16); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:184:16 @@ -193,8 +203,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `f32` to `u16` | -LL | foo::(42u16); - | ~~~ +LL - foo::(42.0_f32); +LL + foo::(42u16); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:189:16 @@ -211,8 +222,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `usize` to `i16` | -LL | foo::(42_i16); - | ~~~ +LL - foo::(42_usize); +LL + foo::(42_i16); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:193:16 @@ -229,8 +241,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u64` to `i16` | -LL | foo::(42_i16); - | ~~~ +LL - foo::(42_u64); +LL + foo::(42_i16); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:197:16 @@ -247,8 +260,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u32` to `i16` | -LL | foo::(42_i16); - | ~~~ +LL - foo::(42_u32); +LL + foo::(42_i16); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:201:16 @@ -265,8 +279,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u16` to `i16` | -LL | foo::(42_i16); - | ~~~ +LL - foo::(42_u16); +LL + foo::(42_i16); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:205:16 @@ -283,8 +298,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u8` to `i16` | -LL | foo::(42_i16); - | ~~~ +LL - foo::(42_u8); +LL + foo::(42_i16); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:209:16 @@ -301,8 +317,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `isize` to `i16` | -LL | foo::(42_i16); - | ~~~ +LL - foo::(42_isize); +LL + foo::(42_i16); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:213:16 @@ -319,8 +336,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i64` to `i16` | -LL | foo::(42_i16); - | ~~~ +LL - foo::(42_i64); +LL + foo::(42_i16); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:217:16 @@ -337,8 +355,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i32` to `i16` | -LL | foo::(42_i16); - | ~~~ +LL - foo::(42_i32); +LL + foo::(42_i16); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:222:16 @@ -355,8 +374,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i8` to `i16` | -LL | foo::(42_i16); - | ~~~ +LL - foo::(42_i8); +LL + foo::(42_i16); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:226:16 @@ -373,8 +393,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `f64` to `i16` | -LL | foo::(42i16); - | ~~~ +LL - foo::(42.0_f64); +LL + foo::(42i16); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:230:16 @@ -391,8 +412,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `f32` to `i16` | -LL | foo::(42i16); - | ~~~ +LL - foo::(42.0_f32); +LL + foo::(42i16); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:235:15 @@ -409,8 +431,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `usize` to `u8` | -LL | foo::(42_u8); - | ~~ +LL - foo::(42_usize); +LL + foo::(42_u8); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:239:15 @@ -427,8 +450,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u64` to `u8` | -LL | foo::(42_u8); - | ~~ +LL - foo::(42_u64); +LL + foo::(42_u8); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:243:15 @@ -445,8 +469,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u32` to `u8` | -LL | foo::(42_u8); - | ~~ +LL - foo::(42_u32); +LL + foo::(42_u8); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:247:15 @@ -463,8 +488,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u16` to `u8` | -LL | foo::(42_u8); - | ~~ +LL - foo::(42_u16); +LL + foo::(42_u8); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:252:15 @@ -481,8 +507,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `isize` to `u8` | -LL | foo::(42_u8); - | ~~ +LL - foo::(42_isize); +LL + foo::(42_u8); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:256:15 @@ -499,8 +526,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i64` to `u8` | -LL | foo::(42_u8); - | ~~ +LL - foo::(42_i64); +LL + foo::(42_u8); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:260:15 @@ -517,8 +545,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i32` to `u8` | -LL | foo::(42_u8); - | ~~ +LL - foo::(42_i32); +LL + foo::(42_u8); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:264:15 @@ -535,8 +564,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i16` to `u8` | -LL | foo::(42_u8); - | ~~ +LL - foo::(42_i16); +LL + foo::(42_u8); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:268:15 @@ -553,8 +583,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i8` to `u8` | -LL | foo::(42_u8); - | ~~ +LL - foo::(42_i8); +LL + foo::(42_u8); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:272:15 @@ -571,8 +602,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `f64` to `u8` | -LL | foo::(42u8); - | ~~ +LL - foo::(42.0_f64); +LL + foo::(42u8); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:276:15 @@ -589,8 +621,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `f32` to `u8` | -LL | foo::(42u8); - | ~~ +LL - foo::(42.0_f32); +LL + foo::(42u8); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:281:15 @@ -607,8 +640,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `usize` to `i8` | -LL | foo::(42_i8); - | ~~ +LL - foo::(42_usize); +LL + foo::(42_i8); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:285:15 @@ -625,8 +659,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u64` to `i8` | -LL | foo::(42_i8); - | ~~ +LL - foo::(42_u64); +LL + foo::(42_i8); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:289:15 @@ -643,8 +678,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u32` to `i8` | -LL | foo::(42_i8); - | ~~ +LL - foo::(42_u32); +LL + foo::(42_i8); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:293:15 @@ -661,8 +697,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u16` to `i8` | -LL | foo::(42_i8); - | ~~ +LL - foo::(42_u16); +LL + foo::(42_i8); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:297:15 @@ -679,8 +716,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u8` to `i8` | -LL | foo::(42_i8); - | ~~ +LL - foo::(42_u8); +LL + foo::(42_i8); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:301:15 @@ -697,8 +735,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `isize` to `i8` | -LL | foo::(42_i8); - | ~~ +LL - foo::(42_isize); +LL + foo::(42_i8); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:305:15 @@ -715,8 +754,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i64` to `i8` | -LL | foo::(42_i8); - | ~~ +LL - foo::(42_i64); +LL + foo::(42_i8); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:309:15 @@ -733,8 +773,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i32` to `i8` | -LL | foo::(42_i8); - | ~~ +LL - foo::(42_i32); +LL + foo::(42_i8); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:313:15 @@ -751,8 +792,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i16` to `i8` | -LL | foo::(42_i8); - | ~~ +LL - foo::(42_i16); +LL + foo::(42_i8); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:318:15 @@ -769,8 +811,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `f64` to `i8` | -LL | foo::(42i8); - | ~~ +LL - foo::(42.0_f64); +LL + foo::(42i8); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:322:15 @@ -787,8 +830,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `f32` to `i8` | -LL | foo::(42i8); - | ~~ +LL - foo::(42.0_f32); +LL + foo::(42i8); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:327:16 @@ -805,8 +849,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `usize` to `f64` | -LL | foo::(42_f64); - | ~~~ +LL - foo::(42_usize); +LL + foo::(42_f64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:331:16 @@ -823,8 +868,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u64` to `f64` | -LL | foo::(42_f64); - | ~~~ +LL - foo::(42_u64); +LL + foo::(42_f64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:335:16 @@ -895,8 +941,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `isize` to `f64` | -LL | foo::(42_f64); - | ~~~ +LL - foo::(42_isize); +LL + foo::(42_f64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:351:16 @@ -913,8 +960,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i64` to `f64` | -LL | foo::(42_f64); - | ~~~ +LL - foo::(42_i64); +LL + foo::(42_f64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:355:16 @@ -985,8 +1033,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `f32` to `f64` | -LL | foo::(42.0_f64); - | ~~~ +LL - foo::(42.0_f32); +LL + foo::(42.0_f64); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:373:16 @@ -1003,8 +1052,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `usize` to `f32` | -LL | foo::(42_f32); - | ~~~ +LL - foo::(42_usize); +LL + foo::(42_f32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:377:16 @@ -1021,8 +1071,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u64` to `f32` | -LL | foo::(42_f32); - | ~~~ +LL - foo::(42_u64); +LL + foo::(42_f32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:381:16 @@ -1039,8 +1090,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `u32` to `f32` | -LL | foo::(42_f32); - | ~~~ +LL - foo::(42_u32); +LL + foo::(42_f32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:385:16 @@ -1093,8 +1145,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `isize` to `f32` | -LL | foo::(42_f32); - | ~~~ +LL - foo::(42_isize); +LL + foo::(42_f32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:397:16 @@ -1111,8 +1164,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i64` to `f32` | -LL | foo::(42_f32); - | ~~~ +LL - foo::(42_i64); +LL + foo::(42_f32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:401:16 @@ -1129,8 +1183,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `i32` to `f32` | -LL | foo::(42_f32); - | ~~~ +LL - foo::(42_i32); +LL + foo::(42_f32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:405:16 @@ -1183,8 +1238,9 @@ LL | fn foo(_x: N) {} | ^^^ ----- help: change the type of the numeric literal from `f64` to `f32` | -LL | foo::(42.0_f32); - | ~~~ +LL - foo::(42.0_f64); +LL + foo::(42.0_f32); + | error[E0308]: mismatched types --> $DIR/numeric-suffix.rs:419:16 diff --git a/tests/ui/object-pointer-types.stderr b/tests/ui/object-pointer-types.stderr index 7d915ebdab657..ac8e069cfd2c8 100644 --- a/tests/ui/object-pointer-types.stderr +++ b/tests/ui/object-pointer-types.stderr @@ -10,7 +10,7 @@ LL | x.owned(); help: there is a method `to_owned` with a similar name | LL | x.to_owned(); - | ~~~~~~~~ + | +++ error[E0599]: no method named `owned` found for mutable reference `&mut dyn Foo` in the current scope --> $DIR/object-pointer-types.rs:17:7 diff --git a/tests/ui/obsolete-in-place/bad.stderr b/tests/ui/obsolete-in-place/bad.stderr index 363dfb77628c4..1409a6637890f 100644 --- a/tests/ui/obsolete-in-place/bad.stderr +++ b/tests/ui/obsolete-in-place/bad.stderr @@ -6,8 +6,9 @@ LL | x <- y; | help: if you meant to write a comparison against a negative value, add a space in between `<` and `-` | -LL | x < - y; - | ~~~ +LL - x <- y; +LL + x < - y; + | error: expected expression, found keyword `in` --> $DIR/bad.rs:10:5 diff --git a/tests/ui/offset-of/offset-of-dst-field.rs b/tests/ui/offset-of/offset-of-dst-field.rs index 5ae15f323577f..2e0bdb151e180 100644 --- a/tests/ui/offset-of/offset-of-dst-field.rs +++ b/tests/ui/offset-of/offset-of-dst-field.rs @@ -16,7 +16,7 @@ struct Beta { z: dyn Trait, } -extern { +extern "C" { type Extern; } diff --git a/tests/ui/on-unimplemented/bad-annotation.stderr b/tests/ui/on-unimplemented/bad-annotation.stderr index 9bb9423788c20..0482a5c58559e 100644 --- a/tests/ui/on-unimplemented/bad-annotation.stderr +++ b/tests/ui/on-unimplemented/bad-annotation.stderr @@ -6,9 +6,11 @@ LL | #[rustc_on_unimplemented] | help: the following are the possible correct uses | -LL | #[rustc_on_unimplemented = "message"] +LL - #[rustc_on_unimplemented] +LL + #[rustc_on_unimplemented = "message"] | -LL | #[rustc_on_unimplemented(/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...")] +LL - #[rustc_on_unimplemented] +LL + #[rustc_on_unimplemented(/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...")] | error[E0230]: there is no parameter `C` on trait `BadAnnotation2` diff --git a/tests/ui/on-unimplemented/issue-104140.stderr b/tests/ui/on-unimplemented/issue-104140.stderr index 4ba5475d9ecaf..5c9d5e8d55398 100644 --- a/tests/ui/on-unimplemented/issue-104140.stderr +++ b/tests/ui/on-unimplemented/issue-104140.stderr @@ -6,10 +6,12 @@ LL | #[rustc_on_unimplemented] | help: the following are the possible correct uses | -LL | #[rustc_on_unimplemented = "message"] - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | #[rustc_on_unimplemented(/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...")] - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - #[rustc_on_unimplemented] +LL + #[rustc_on_unimplemented = "message"] + | +LL - #[rustc_on_unimplemented] +LL + #[rustc_on_unimplemented(/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...")] + | error: aborting due to 1 previous error diff --git a/tests/ui/operator-recovery/less-than-greater-than.stderr b/tests/ui/operator-recovery/less-than-greater-than.stderr index 36a4a81035f1e..429bda9cefcd5 100644 --- a/tests/ui/operator-recovery/less-than-greater-than.stderr +++ b/tests/ui/operator-recovery/less-than-greater-than.stderr @@ -6,8 +6,9 @@ LL | println!("{}", 1 <> 2); | help: `<>` is not a valid comparison operator, use `!=` | -LL | println!("{}", 1 != 2); - | ~~ +LL - println!("{}", 1 <> 2); +LL + println!("{}", 1 != 2); + | error: aborting due to 1 previous error diff --git a/tests/ui/or-patterns/multiple-pattern-typo.stderr b/tests/ui/or-patterns/multiple-pattern-typo.stderr index 2e66f54979b03..fea3ed7669194 100644 --- a/tests/ui/or-patterns/multiple-pattern-typo.stderr +++ b/tests/ui/or-patterns/multiple-pattern-typo.stderr @@ -8,8 +8,9 @@ LL | 1 | 2 || 3 => (), | help: use a single `|` to separate multiple alternative patterns | -LL | 1 | 2 | 3 => (), - | ~ +LL - 1 | 2 || 3 => (), +LL + 1 | 2 | 3 => (), + | error: unexpected token `||` in pattern --> $DIR/multiple-pattern-typo.rs:12:16 @@ -21,8 +22,9 @@ LL | (1 | 2 || 3) => (), | help: use a single `|` to separate multiple alternative patterns | -LL | (1 | 2 | 3) => (), - | ~ +LL - (1 | 2 || 3) => (), +LL + (1 | 2 | 3) => (), + | error: unexpected token `||` in pattern --> $DIR/multiple-pattern-typo.rs:17:16 @@ -34,8 +36,9 @@ LL | (1 | 2 || 3,) => (), | help: use a single `|` to separate multiple alternative patterns | -LL | (1 | 2 | 3,) => (), - | ~ +LL - (1 | 2 || 3,) => (), +LL + (1 | 2 | 3,) => (), + | error: unexpected token `||` in pattern --> $DIR/multiple-pattern-typo.rs:24:18 @@ -47,8 +50,9 @@ LL | TS(1 | 2 || 3) => (), | help: use a single `|` to separate multiple alternative patterns | -LL | TS(1 | 2 | 3) => (), - | ~ +LL - TS(1 | 2 || 3) => (), +LL + TS(1 | 2 | 3) => (), + | error: unexpected token `||` in pattern --> $DIR/multiple-pattern-typo.rs:31:23 @@ -60,8 +64,9 @@ LL | NS { f: 1 | 2 || 3 } => (), | help: use a single `|` to separate multiple alternative patterns | -LL | NS { f: 1 | 2 | 3 } => (), - | ~ +LL - NS { f: 1 | 2 || 3 } => (), +LL + NS { f: 1 | 2 | 3 } => (), + | error: unexpected token `||` in pattern --> $DIR/multiple-pattern-typo.rs:36:16 @@ -73,8 +78,9 @@ LL | [1 | 2 || 3] => (), | help: use a single `|` to separate multiple alternative patterns | -LL | [1 | 2 | 3] => (), - | ~ +LL - [1 | 2 || 3] => (), +LL + [1 | 2 | 3] => (), + | error: unexpected token `||` in pattern --> $DIR/multiple-pattern-typo.rs:41:9 @@ -84,8 +90,9 @@ LL | || 1 | 2 | 3 => (), | help: use a single `|` to separate multiple alternative patterns | -LL | | 1 | 2 | 3 => (), - | ~ +LL - || 1 | 2 | 3 => (), +LL + | 1 | 2 | 3 => (), + | error: aborting due to 7 previous errors diff --git a/tests/ui/or-patterns/remove-leading-vert.stderr b/tests/ui/or-patterns/remove-leading-vert.stderr index 5177e98f0d90a..b92fcb89a404e 100644 --- a/tests/ui/or-patterns/remove-leading-vert.stderr +++ b/tests/ui/or-patterns/remove-leading-vert.stderr @@ -31,8 +31,9 @@ LL | let ( || A): (E); | help: use a single `|` to separate multiple alternative patterns | -LL | let ( | A): (E); - | ~ +LL - let ( || A): (E); +LL + let ( | A): (E); + | error: unexpected token `||` in pattern --> $DIR/remove-leading-vert.rs:17:11 @@ -42,8 +43,9 @@ LL | let [ || A ]: [E; 1]; | help: use a single `|` to separate multiple alternative patterns | -LL | let [ | A ]: [E; 1]; - | ~ +LL - let [ || A ]: [E; 1]; +LL + let [ | A ]: [E; 1]; + | error: unexpected token `||` in pattern --> $DIR/remove-leading-vert.rs:19:13 @@ -53,8 +55,9 @@ LL | let TS( || A ): TS; | help: use a single `|` to separate multiple alternative patterns | -LL | let TS( | A ): TS; - | ~ +LL - let TS( || A ): TS; +LL + let TS( | A ): TS; + | error: unexpected token `||` in pattern --> $DIR/remove-leading-vert.rs:21:17 @@ -64,8 +67,9 @@ LL | let NS { f: || A }: NS; | help: use a single `|` to separate multiple alternative patterns | -LL | let NS { f: | A }: NS; - | ~ +LL - let NS { f: || A }: NS; +LL + let NS { f: | A }: NS; + | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:26:13 @@ -147,8 +151,9 @@ LL | let ( A || B | ): E; | help: use a single `|` to separate multiple alternative patterns | -LL | let ( A | B | ): E; - | ~ +LL - let ( A || B | ): E; +LL + let ( A | B | ): E; + | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:31:18 @@ -203,8 +208,9 @@ LL | A || B | => {} | help: use a single `|` to separate multiple alternative patterns | -LL | A | B | => {} - | ~ +LL - A || B | => {} +LL + A | B | => {} + | error: a trailing `|` is not allowed in an or-pattern --> $DIR/remove-leading-vert.rs:36:16 diff --git a/tests/ui/panic-handler/panic-handler-with-target-feature.rs b/tests/ui/panic-handler/panic-handler-with-target-feature.rs index 8d5ea0703afa5..aec00c637bedb 100644 --- a/tests/ui/panic-handler/panic-handler-with-target-feature.rs +++ b/tests/ui/panic-handler/panic-handler-with-target-feature.rs @@ -1,7 +1,6 @@ //@ compile-flags:-C panic=abort //@ only-x86_64 -#![feature(target_feature_11)] #![no_std] #![no_main] diff --git a/tests/ui/panic-handler/panic-handler-with-target-feature.stderr b/tests/ui/panic-handler/panic-handler-with-target-feature.stderr index cb17da3a4efbe..ddf0ae77a0a1a 100644 --- a/tests/ui/panic-handler/panic-handler-with-target-feature.stderr +++ b/tests/ui/panic-handler/panic-handler-with-target-feature.stderr @@ -1,5 +1,5 @@ error: `#[panic_handler]` function is not allowed to have `#[target_feature]` - --> $DIR/panic-handler-with-target-feature.rs:11:1 + --> $DIR/panic-handler-with-target-feature.rs:10:1 | LL | #[target_feature(enable = "avx2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/panic-handler/weak-lang-item.stderr b/tests/ui/panic-handler/weak-lang-item.stderr index de351d2c3e4aa..e9d444c1c4d8c 100644 --- a/tests/ui/panic-handler/weak-lang-item.stderr +++ b/tests/ui/panic-handler/weak-lang-item.stderr @@ -7,7 +7,8 @@ LL | extern crate core; = note: `core` must be defined only once in the type namespace of this module help: you can use `as` to change the binding name of the import | -LL | extern crate core as other_core; +LL - extern crate core; +LL + extern crate core as other_core; | error: `#[panic_handler]` function required, but not found diff --git a/tests/ui/panic-runtime/abort-link-to-unwinding-crates.rs b/tests/ui/panic-runtime/abort-link-to-unwinding-crates.rs index ce6644e675869..0566d2319df1a 100644 --- a/tests/ui/panic-runtime/abort-link-to-unwinding-crates.rs +++ b/tests/ui/panic-runtime/abort-link-to-unwinding-crates.rs @@ -1,10 +1,8 @@ //@ run-pass -#![allow(unused_variables)] //@ compile-flags:-C panic=abort //@ aux-build:exit-success-if-unwind.rs //@ no-prefer-dynamic -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess extern crate exit_success_if_unwind; @@ -13,7 +11,7 @@ use std::process::Command; fn main() { let mut args = env::args_os(); - let me = args.next().unwrap(); + let _ = args.next().unwrap(); if let Some(s) = args.next() { if &*s == "foo" { diff --git a/tests/ui/panic-runtime/abort.rs b/tests/ui/panic-runtime/abort.rs index caf0243ebdb26..8cdfd018a9282 100644 --- a/tests/ui/panic-runtime/abort.rs +++ b/tests/ui/panic-runtime/abort.rs @@ -1,9 +1,7 @@ //@ run-pass -#![allow(unused_variables)] //@ compile-flags:-C panic=abort //@ no-prefer-dynamic -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess use std::env; use std::process::Command; @@ -18,7 +16,7 @@ impl Drop for Bomb { fn main() { let mut args = env::args_os(); - let me = args.next().unwrap(); + let _ = args.next().unwrap(); if let Some(s) = args.next() { if &*s == "foo" { diff --git a/tests/ui/panic-runtime/lto-abort.rs b/tests/ui/panic-runtime/lto-abort.rs index c66b6a60c7389..cf15ae6435bfd 100644 --- a/tests/ui/panic-runtime/lto-abort.rs +++ b/tests/ui/panic-runtime/lto-abort.rs @@ -1,9 +1,7 @@ //@ run-pass -#![allow(unused_variables)] //@ compile-flags:-C lto -C panic=abort //@ no-prefer-dynamic -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess use std::process::Command; use std::env; @@ -18,7 +16,7 @@ impl Drop for Bomb { fn main() { let mut args = env::args_os(); - let me = args.next().unwrap(); + let _ = args.next().unwrap(); if let Some(s) = args.next() { if &*s == "foo" { diff --git a/tests/ui/panic-runtime/lto-unwind.rs b/tests/ui/panic-runtime/lto-unwind.rs index 5eab2bd56ed1f..93275052f8527 100644 --- a/tests/ui/panic-runtime/lto-unwind.rs +++ b/tests/ui/panic-runtime/lto-unwind.rs @@ -1,11 +1,8 @@ //@ run-pass -#![allow(unused_variables)] - //@ compile-flags:-C lto -C panic=unwind //@ needs-unwind //@ no-prefer-dynamic -//@ ignore-emscripten no processes -//@ ignore-sgx no processes +//@ needs-subprocess use std::process::Command; use std::env; @@ -20,7 +17,7 @@ impl Drop for Bomb { fn main() { let mut args = env::args_os(); - let me = args.next().unwrap(); + let _ = args.next().unwrap(); if let Some(s) = args.next() { if &*s == "foo" { diff --git a/tests/ui/panic-runtime/unwind-interleaved.rs b/tests/ui/panic-runtime/unwind-interleaved.rs index e5505cd893a11..83eb636509755 100644 --- a/tests/ui/panic-runtime/unwind-interleaved.rs +++ b/tests/ui/panic-runtime/unwind-interleaved.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:explicit panic -//@ ignore-emscripten no processes +//@ needs-subprocess fn a() {} diff --git a/tests/ui/panic-runtime/unwind-rec.rs b/tests/ui/panic-runtime/unwind-rec.rs index d4b53c8876813..a855a4de28014 100644 --- a/tests/ui/panic-runtime/unwind-rec.rs +++ b/tests/ui/panic-runtime/unwind-rec.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:explicit panic -//@ ignore-emscripten no processes +//@ needs-subprocess fn build() -> Vec { panic!(); diff --git a/tests/ui/panic-runtime/unwind-rec2.rs b/tests/ui/panic-runtime/unwind-rec2.rs index 6ac9a5a580547..ed02b117fff4b 100644 --- a/tests/ui/panic-runtime/unwind-rec2.rs +++ b/tests/ui/panic-runtime/unwind-rec2.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:explicit panic -//@ ignore-emscripten no processes +//@ needs-subprocess fn build1() -> Vec { vec![0, 0, 0, 0, 0, 0, 0] diff --git a/tests/ui/panic-runtime/unwind-unique.rs b/tests/ui/panic-runtime/unwind-unique.rs index a6cd59690ca92..b57d81842c4dd 100644 --- a/tests/ui/panic-runtime/unwind-unique.rs +++ b/tests/ui/panic-runtime/unwind-unique.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:explicit panic -//@ ignore-emscripten no processes +//@ needs-subprocess fn failfn() { panic!(); diff --git a/tests/ui/panics/abort-on-panic.rs b/tests/ui/panics/abort-on-panic.rs index feccefb253768..d3bf087bd3ef8 100644 --- a/tests/ui/panics/abort-on-panic.rs +++ b/tests/ui/panics/abort-on-panic.rs @@ -8,8 +8,7 @@ // Since we mark some ABIs as "nounwind" to LLVM, we must make sure that // we never unwind through them. -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess use std::io; use std::io::prelude::*; diff --git a/tests/ui/panics/args-panic.rs b/tests/ui/panics/args-panic.rs index 091ced9b47917..9675c99dcb6f8 100644 --- a/tests/ui/panics/args-panic.rs +++ b/tests/ui/panics/args-panic.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:meep -//@ ignore-emscripten no processes +//@ needs-subprocess fn f(_a: isize, _b: isize, _c: Box) { panic!("moop"); diff --git a/tests/ui/panics/doublepanic.rs b/tests/ui/panics/doublepanic.rs index 51945ea708c2e..6fddde82a277a 100644 --- a/tests/ui/panics/doublepanic.rs +++ b/tests/ui/panics/doublepanic.rs @@ -2,7 +2,7 @@ //@ run-fail //@ error-pattern:One -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { panic!("One"); diff --git a/tests/ui/panics/explicit-panic-msg.rs b/tests/ui/panics/explicit-panic-msg.rs index ef0c5b39f0990..e5728ef1e20c5 100644 --- a/tests/ui/panics/explicit-panic-msg.rs +++ b/tests/ui/panics/explicit-panic-msg.rs @@ -4,7 +4,7 @@ //@ run-fail //@ error-pattern:wooooo -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { let mut a = 1; diff --git a/tests/ui/panics/explicit-panic.rs b/tests/ui/panics/explicit-panic.rs index 34e952ef68f9b..7c69289940e49 100644 --- a/tests/ui/panics/explicit-panic.rs +++ b/tests/ui/panics/explicit-panic.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:explicit -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { panic!(); diff --git a/tests/ui/panics/fmt-panic.rs b/tests/ui/panics/fmt-panic.rs index 032f65cb2e4b3..c6cb039684dba 100644 --- a/tests/ui/panics/fmt-panic.rs +++ b/tests/ui/panics/fmt-panic.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:meh -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { let str_var: String = "meh".to_string(); diff --git a/tests/ui/panics/issue-47429-short-backtraces.rs b/tests/ui/panics/issue-47429-short-backtraces.rs index dff885af1b87a..378ade67bfd08 100644 --- a/tests/ui/panics/issue-47429-short-backtraces.rs +++ b/tests/ui/panics/issue-47429-short-backtraces.rs @@ -17,10 +17,8 @@ //@ ignore-msvc see #62897 and `backtrace-debuginfo.rs` test //@ ignore-android FIXME #17520 //@ ignore-openbsd no support for libbacktrace without filename -//@ ignore-wasm no panic or subprocess support -//@ ignore-emscripten no panic or subprocess support -//@ ignore-sgx no subprocess support //@ ignore-fuchsia Backtraces not symbolized +//@ needs-subprocess fn main() { panic!() diff --git a/tests/ui/panics/issue-47429-short-backtraces.run.stderr b/tests/ui/panics/issue-47429-short-backtraces.run.stderr index c6e2d13fb5da7..32dc6592271de 100644 --- a/tests/ui/panics/issue-47429-short-backtraces.run.stderr +++ b/tests/ui/panics/issue-47429-short-backtraces.run.stderr @@ -1,5 +1,5 @@ -thread 'main' panicked at $DIR/issue-47429-short-backtraces.rs:26:5: +thread 'main' panicked at $DIR/issue-47429-short-backtraces.rs:24:5: explicit panic stack backtrace: 0: std::panicking::begin_panic diff --git a/tests/ui/panics/main-panic.rs b/tests/ui/panics/main-panic.rs index b69f1656ca491..0b3d5c3aaec85 100644 --- a/tests/ui/panics/main-panic.rs +++ b/tests/ui/panics/main-panic.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:thread 'main' panicked at -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { panic!() diff --git a/tests/ui/panics/panic-arg.rs b/tests/ui/panics/panic-arg.rs index 10be6d5ff6ce1..037cfda3689a1 100644 --- a/tests/ui/panics/panic-arg.rs +++ b/tests/ui/panics/panic-arg.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:woe -//@ ignore-emscripten no processes +//@ needs-subprocess fn f(a: isize) { println!("{}", a); diff --git a/tests/ui/panics/panic-handler-chain-update-hook.rs b/tests/ui/panics/panic-handler-chain-update-hook.rs index 1f8fe30cfd8c2..662ea9e978f70 100644 --- a/tests/ui/panics/panic-handler-chain-update-hook.rs +++ b/tests/ui/panics/panic-handler-chain-update-hook.rs @@ -2,7 +2,7 @@ //@ needs-unwind #![allow(stable_features)] -//@ ignore-emscripten no threads support +//@ needs-threads #![feature(std_panic)] #![feature(panic_update_hook)] diff --git a/tests/ui/panics/panic-handler-flail-wildly.rs b/tests/ui/panics/panic-handler-flail-wildly.rs index 768c9d4c4c5ec..d42dfd68d9cf8 100644 --- a/tests/ui/panics/panic-handler-flail-wildly.rs +++ b/tests/ui/panics/panic-handler-flail-wildly.rs @@ -4,7 +4,7 @@ #![allow(stable_features)] #![allow(unused_must_use)] -//@ ignore-emscripten no threads support +//@ needs-threads #![feature(std_panic)] diff --git a/tests/ui/panics/panic-handler-set-twice.rs b/tests/ui/panics/panic-handler-set-twice.rs index 902e48b65414e..5f670d5f49298 100644 --- a/tests/ui/panics/panic-handler-set-twice.rs +++ b/tests/ui/panics/panic-handler-set-twice.rs @@ -5,7 +5,7 @@ #![feature(std_panic)] -//@ ignore-emscripten no threads support +//@ needs-threads use std::sync::atomic::{AtomicUsize, Ordering}; use std::panic; diff --git a/tests/ui/panics/panic-in-dtor-drops-fields.rs b/tests/ui/panics/panic-in-dtor-drops-fields.rs index 4d18dc0e05916..38eb6d0acfb81 100644 --- a/tests/ui/panics/panic-in-dtor-drops-fields.rs +++ b/tests/ui/panics/panic-in-dtor-drops-fields.rs @@ -3,7 +3,7 @@ #![allow(dead_code)] #![allow(non_upper_case_globals)] -//@ ignore-emscripten no threads support +//@ needs-threads use std::thread; diff --git a/tests/ui/panics/panic-macro-any-wrapped.rs b/tests/ui/panics/panic-macro-any-wrapped.rs index 7c6790e35fd16..e3a2dc6eed42e 100644 --- a/tests/ui/panics/panic-macro-any-wrapped.rs +++ b/tests/ui/panics/panic-macro-any-wrapped.rs @@ -1,7 +1,7 @@ //@ run-fail //@ error-pattern:panicked //@ error-pattern:Box -//@ ignore-emscripten no processes +//@ needs-subprocess #![allow(non_fmt_panics)] diff --git a/tests/ui/panics/panic-macro-any.rs b/tests/ui/panics/panic-macro-any.rs index 75397333fa4a3..1392929b65c2e 100644 --- a/tests/ui/panics/panic-macro-any.rs +++ b/tests/ui/panics/panic-macro-any.rs @@ -1,7 +1,7 @@ //@ run-fail //@ error-pattern:panicked //@ error-pattern:Box -//@ ignore-emscripten no processes +//@ needs-subprocess #![allow(non_fmt_panics)] diff --git a/tests/ui/panics/panic-macro-explicit.rs b/tests/ui/panics/panic-macro-explicit.rs index 2c7b84d99fea2..b9195e3b0fccf 100644 --- a/tests/ui/panics/panic-macro-explicit.rs +++ b/tests/ui/panics/panic-macro-explicit.rs @@ -1,7 +1,7 @@ //@ run-fail //@ error-pattern:panicked //@ error-pattern:explicit panic -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { panic!(); diff --git a/tests/ui/panics/panic-macro-fmt.rs b/tests/ui/panics/panic-macro-fmt.rs index 1a63a06c75ad3..550cd5c1ee333 100644 --- a/tests/ui/panics/panic-macro-fmt.rs +++ b/tests/ui/panics/panic-macro-fmt.rs @@ -1,7 +1,7 @@ //@ run-fail //@ error-pattern:panicked //@ error-pattern:test-fail-fmt 42 rust -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { panic!("test-fail-fmt {} {}", 42, "rust"); diff --git a/tests/ui/panics/panic-macro-owned.rs b/tests/ui/panics/panic-macro-owned.rs index 1878f3d52abde..4df04c50bc3eb 100644 --- a/tests/ui/panics/panic-macro-owned.rs +++ b/tests/ui/panics/panic-macro-owned.rs @@ -1,7 +1,7 @@ //@ run-fail //@ error-pattern:panicked //@ error-pattern:test-fail-owned -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { panic!("test-fail-owned"); diff --git a/tests/ui/panics/panic-macro-static.rs b/tests/ui/panics/panic-macro-static.rs index 018166e60cfab..1c6258ebed24b 100644 --- a/tests/ui/panics/panic-macro-static.rs +++ b/tests/ui/panics/panic-macro-static.rs @@ -1,7 +1,7 @@ //@ run-fail //@ error-pattern:panicked //@ error-pattern:test-fail-static -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { panic!("test-fail-static"); diff --git a/tests/ui/panics/panic-main.rs b/tests/ui/panics/panic-main.rs index d71fca0754e9e..3876dbb37c37d 100644 --- a/tests/ui/panics/panic-main.rs +++ b/tests/ui/panics/panic-main.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:moop -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { panic!("moop"); diff --git a/tests/ui/panics/panic-parens.rs b/tests/ui/panics/panic-parens.rs index 271d0363cab8a..5440bf18f2567 100644 --- a/tests/ui/panics/panic-parens.rs +++ b/tests/ui/panics/panic-parens.rs @@ -3,7 +3,7 @@ //@ run-fail //@ error-pattern:oops -//@ ignore-emscripten no processes +//@ needs-subprocess fn bigpanic() { while (panic!("oops")) { diff --git a/tests/ui/panics/panic-recover-propagate.rs b/tests/ui/panics/panic-recover-propagate.rs index f8be86be19db6..ef6ae4fd7887d 100644 --- a/tests/ui/panics/panic-recover-propagate.rs +++ b/tests/ui/panics/panic-recover-propagate.rs @@ -1,6 +1,6 @@ //@ run-pass //@ needs-unwind -//@ ignore-emscripten no threads support +//@ needs-threads use std::sync::atomic::{AtomicUsize, Ordering}; use std::panic; diff --git a/tests/ui/panics/panic-set-handler.rs b/tests/ui/panics/panic-set-handler.rs index 39286ca865ba5..41e513e0bd680 100644 --- a/tests/ui/panics/panic-set-handler.rs +++ b/tests/ui/panics/panic-set-handler.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:greetings from the panic handler -//@ ignore-emscripten no processes +//@ needs-subprocess use std::panic; diff --git a/tests/ui/panics/panic-set-unset-handler.rs b/tests/ui/panics/panic-set-unset-handler.rs index 02f1599338b66..66d5003d0f1c5 100644 --- a/tests/ui/panics/panic-set-unset-handler.rs +++ b/tests/ui/panics/panic-set-unset-handler.rs @@ -1,7 +1,7 @@ //@ run-fail //@ error-pattern:thread 'main' panicked //@ error-pattern:foobar -//@ ignore-emscripten no processes +//@ needs-subprocess use std::panic; diff --git a/tests/ui/panics/panic-take-handler-nop.rs b/tests/ui/panics/panic-take-handler-nop.rs index 89e1d234df138..f10582872df21 100644 --- a/tests/ui/panics/panic-take-handler-nop.rs +++ b/tests/ui/panics/panic-take-handler-nop.rs @@ -1,7 +1,7 @@ //@ run-fail //@ error-pattern:thread 'main' panicked //@ error-pattern:foobar -//@ ignore-emscripten no processes +//@ needs-subprocess use std::panic; diff --git a/tests/ui/panics/panic.rs b/tests/ui/panics/panic.rs index b9721ac8230f7..068f187524d5b 100644 --- a/tests/ui/panics/panic.rs +++ b/tests/ui/panics/panic.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:1 == 2 -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { assert!(1 == 2); diff --git a/tests/ui/panics/result-get-panic.rs b/tests/ui/panics/result-get-panic.rs index d7f6dfe8406da..b8dc49f9acaa5 100644 --- a/tests/ui/panics/result-get-panic.rs +++ b/tests/ui/panics/result-get-panic.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:called `Result::unwrap()` on an `Err` value -//@ ignore-emscripten no processes +//@ needs-subprocess use std::result::Result::Err; diff --git a/tests/ui/panics/runtime-switch.rs b/tests/ui/panics/runtime-switch.rs index ffd038f953515..7d5b416934078 100644 --- a/tests/ui/panics/runtime-switch.rs +++ b/tests/ui/panics/runtime-switch.rs @@ -18,9 +18,8 @@ //@ ignore-android FIXME #17520 //@ ignore-openbsd no support for libbacktrace without filename //@ ignore-wasm no backtrace support -//@ ignore-emscripten no panic or subprocess support -//@ ignore-sgx no subprocess support //@ ignore-fuchsia Backtrace not symbolized +//@ needs-subprocess #![feature(panic_backtrace_config)] diff --git a/tests/ui/panics/runtime-switch.run.stderr b/tests/ui/panics/runtime-switch.run.stderr index 458a0ee534a96..70ed127af8643 100644 --- a/tests/ui/panics/runtime-switch.run.stderr +++ b/tests/ui/panics/runtime-switch.run.stderr @@ -1,5 +1,5 @@ -thread 'main' panicked at $DIR/runtime-switch.rs:29:5: +thread 'main' panicked at $DIR/runtime-switch.rs:28:5: explicit panic stack backtrace: 0: std::panicking::begin_panic diff --git a/tests/ui/panics/short-ice-remove-middle-frames-2.rs b/tests/ui/panics/short-ice-remove-middle-frames-2.rs index 48f60b141706b..660530f0eada8 100644 --- a/tests/ui/panics/short-ice-remove-middle-frames-2.rs +++ b/tests/ui/panics/short-ice-remove-middle-frames-2.rs @@ -5,7 +5,6 @@ //@ needs-unwind //@ ignore-android FIXME #17520 //@ ignore-openbsd no support for libbacktrace without filename -//@ ignore-emscripten no panic //@ ignore-sgx Backtraces not symbolized //@ ignore-fuchsia Backtraces not symbolized //@ ignore-msvc the `__rust_{begin,end}_short_backtrace` symbols aren't reliable. diff --git a/tests/ui/panics/short-ice-remove-middle-frames-2.run.stderr b/tests/ui/panics/short-ice-remove-middle-frames-2.run.stderr index 1fddcca69510d..664d51e185dd5 100644 --- a/tests/ui/panics/short-ice-remove-middle-frames-2.run.stderr +++ b/tests/ui/panics/short-ice-remove-middle-frames-2.run.stderr @@ -1,5 +1,5 @@ -thread 'main' panicked at $DIR/short-ice-remove-middle-frames-2.rs:63:5: +thread 'main' panicked at $DIR/short-ice-remove-middle-frames-2.rs:62:5: debug!!! stack backtrace: 0: std::panicking::begin_panic diff --git a/tests/ui/panics/short-ice-remove-middle-frames.rs b/tests/ui/panics/short-ice-remove-middle-frames.rs index 216c512779956..41fb6e9950e6c 100644 --- a/tests/ui/panics/short-ice-remove-middle-frames.rs +++ b/tests/ui/panics/short-ice-remove-middle-frames.rs @@ -5,7 +5,6 @@ //@ needs-unwind //@ ignore-android FIXME #17520 //@ ignore-openbsd no support for libbacktrace without filename -//@ ignore-emscripten no panic //@ ignore-sgx Backtraces not symbolized //@ ignore-fuchsia Backtraces not symbolized //@ ignore-msvc the `__rust_{begin,end}_short_backtrace` symbols aren't reliable. diff --git a/tests/ui/panics/short-ice-remove-middle-frames.run.stderr b/tests/ui/panics/short-ice-remove-middle-frames.run.stderr index 630b09d8d1ec6..e966462331fcf 100644 --- a/tests/ui/panics/short-ice-remove-middle-frames.run.stderr +++ b/tests/ui/panics/short-ice-remove-middle-frames.run.stderr @@ -1,5 +1,5 @@ -thread 'main' panicked at $DIR/short-ice-remove-middle-frames.rs:59:5: +thread 'main' panicked at $DIR/short-ice-remove-middle-frames.rs:58:5: debug!!! stack backtrace: 0: std::panicking::begin_panic diff --git a/tests/ui/panics/test-panic.rs b/tests/ui/panics/test-panic.rs index 29a3c4e9c9f1a..c7af47524cfcf 100644 --- a/tests/ui/panics/test-panic.rs +++ b/tests/ui/panics/test-panic.rs @@ -1,7 +1,7 @@ //@ run-fail //@ check-stdout //@ compile-flags: --test -//@ ignore-emscripten +//@ needs-subprocess #[test] fn test_foo() { diff --git a/tests/ui/panics/test-should-panic-no-message.rs b/tests/ui/panics/test-should-panic-no-message.rs index b6ed6b19dd086..05fc927d876b5 100644 --- a/tests/ui/panics/test-should-panic-no-message.rs +++ b/tests/ui/panics/test-should-panic-no-message.rs @@ -1,7 +1,7 @@ //@ run-fail //@ compile-flags: --test //@ check-stdout -//@ ignore-wasm32 no processes +//@ needs-subprocess #[test] #[should_panic(expected = "foo")] diff --git a/tests/ui/panics/while-body-panics.rs b/tests/ui/panics/while-body-panics.rs index bddcd5d50ce4c..8459a8d63bff3 100644 --- a/tests/ui/panics/while-body-panics.rs +++ b/tests/ui/panics/while-body-panics.rs @@ -2,7 +2,7 @@ //@ run-fail //@ error-pattern:quux -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { let _x: isize = { diff --git a/tests/ui/panics/while-panic.rs b/tests/ui/panics/while-panic.rs index 2961e8599c356..4c8431c71d128 100644 --- a/tests/ui/panics/while-panic.rs +++ b/tests/ui/panics/while-panic.rs @@ -2,7 +2,7 @@ //@ run-fail //@ error-pattern:giraffe -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { panic!("{}", { diff --git a/tests/ui/parallel-rustc/cycle_crash.rs b/tests/ui/parallel-rustc/cycle_crash.rs new file mode 100644 index 0000000000000..94ae11aef39d4 --- /dev/null +++ b/tests/ui/parallel-rustc/cycle_crash.rs @@ -0,0 +1,5 @@ +//@ compile-flags: -Z threads=2 + +const FOO: usize = FOO; //~ERROR cycle detected when simplifying constant for the type system `FOO` + +fn main() {} diff --git a/tests/ui/parallel-rustc/cycle_crash.stderr b/tests/ui/parallel-rustc/cycle_crash.stderr new file mode 100644 index 0000000000000..7af3b8ee532c7 --- /dev/null +++ b/tests/ui/parallel-rustc/cycle_crash.stderr @@ -0,0 +1,18 @@ +error[E0391]: cycle detected when simplifying constant for the type system `FOO` + --> $DIR/cycle_crash.rs:3:1 + | +LL | const FOO: usize = FOO; + | ^^^^^^^^^^^^^^^^ + | +note: ...which requires const-evaluating + checking `FOO`... + --> $DIR/cycle_crash.rs:3:20 + | +LL | const FOO: usize = FOO; + | ^^^ + = note: ...which again requires simplifying constant for the type system `FOO`, completing the cycle + = note: cycle used when running analysis passes on this crate + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/parser/bad-char-literals.stderr b/tests/ui/parser/bad-char-literals.stderr index 5a81ede0336ff..b5b05c2c142cd 100644 --- a/tests/ui/parser/bad-char-literals.stderr +++ b/tests/ui/parser/bad-char-literals.stderr @@ -7,7 +7,7 @@ LL | '''; help: escape the character | LL | '\''; - | ~~ + | + error: character constant must be escaped: `\n` --> $DIR/bad-char-literals.rs:10:6 @@ -41,8 +41,9 @@ LL | '-␀-'; | help: if you meant to write a string literal, use double quotes | -LL | "-␀-"; - | ~ ~ +LL - '-␀-'; +LL + "-␀-"; + | error: character constant must be escaped: `\t` --> $DIR/bad-char-literals.rs:21:6 diff --git a/tests/ui/parser/bad-crate-name.stderr b/tests/ui/parser/bad-crate-name.stderr index c98a620f12371..2218062fa28ef 100644 --- a/tests/ui/parser/bad-crate-name.stderr +++ b/tests/ui/parser/bad-crate-name.stderr @@ -6,8 +6,9 @@ LL | extern crate krate-name-here; | help: if the original crate name uses dashes you need to use underscores in the code | -LL | extern crate krate_name_here; - | ~ ~ +LL - extern crate krate-name-here; +LL + extern crate krate_name_here; + | error[E0463]: can't find crate for `krate_name_here` --> $DIR/bad-crate-name.rs:1:1 diff --git a/tests/ui/parser/bad-escape-suggest-raw-string.stderr b/tests/ui/parser/bad-escape-suggest-raw-string.stderr index 6dd4ad512a8e3..15e99b3cb32ff 100644 --- a/tests/ui/parser/bad-escape-suggest-raw-string.stderr +++ b/tests/ui/parser/bad-escape-suggest-raw-string.stderr @@ -8,7 +8,7 @@ LL | let bad = "ab\[c"; help: if you meant to write a literal backslash (perhaps escaping in a regular expression), consider a raw string literal | LL | let bad = r"ab\[c"; - | ~~~~~~~~ + | + error: aborting due to 1 previous error diff --git a/tests/ui/parser/bad-fn-ptr-qualifier.fixed b/tests/ui/parser/bad-fn-ptr-qualifier.fixed index 558a27cd4562d..8a97a2f09ccab 100644 --- a/tests/ui/parser/bad-fn-ptr-qualifier.fixed +++ b/tests/ui/parser/bad-fn-ptr-qualifier.fixed @@ -2,24 +2,24 @@ //@ edition:2018 // Most of items are taken from ./recover-const-async-fn-ptr.rs but this is able to apply rustfix. -pub type T0 = fn(); //~ ERROR an `fn` pointer type cannot be `const` -pub type T1 = extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` -pub type T2 = unsafe extern fn(); //~ ERROR an `fn` pointer type cannot be `const` -pub type T3 = fn(); //~ ERROR an `fn` pointer type cannot be `async` -pub type T4 = extern fn(); //~ ERROR an `fn` pointer type cannot be `async` -pub type T5 = unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` -pub type T6 = unsafe extern "C" fn(); +pub type T0 = fn(); //~ ERROR an `fn` pointer type cannot be `const` +pub type T1 = extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` +pub type T2 = unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` +pub type T3 = fn(); //~ ERROR an `fn` pointer type cannot be `async` +pub type T4 = extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` +pub type T5 = unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` +pub type T6 = unsafe extern "C" fn(); //~^ ERROR an `fn` pointer type cannot be `const` //~| ERROR an `fn` pointer type cannot be `async` -pub type FTT0 = for<'a> fn(); //~ ERROR an `fn` pointer type cannot be `const` -pub type FTT1 = for<'a> extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` -pub type FTT2 = for<'a> unsafe extern fn(); //~ ERROR an `fn` pointer type cannot be `const` -pub type FTT3 = for<'a> fn(); //~ ERROR an `fn` pointer type cannot be `async` -pub type FTT4 = for<'a> extern fn(); //~ ERROR an `fn` pointer type cannot be `async` -pub type FTT5 = for<'a> unsafe extern "C" fn(); +pub type FTT0 = for<'a> fn(); //~ ERROR an `fn` pointer type cannot be `const` +pub type FTT1 = for<'a> extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` +pub type FTT2 = for<'a> unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` +pub type FTT3 = for<'a> fn(); //~ ERROR an `fn` pointer type cannot be `async` +pub type FTT4 = for<'a> extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` +pub type FTT5 = for<'a> unsafe extern "C" fn(); //~^ ERROR an `fn` pointer type cannot be `async` -pub type FTT6 = for<'a> unsafe extern "C" fn(); +pub type FTT6 = for<'a> unsafe extern "C" fn(); //~^ ERROR an `fn` pointer type cannot be `const` //~| ERROR an `fn` pointer type cannot be `async` diff --git a/tests/ui/parser/bad-fn-ptr-qualifier.rs b/tests/ui/parser/bad-fn-ptr-qualifier.rs index 9750f480935f2..f2611c93b1763 100644 --- a/tests/ui/parser/bad-fn-ptr-qualifier.rs +++ b/tests/ui/parser/bad-fn-ptr-qualifier.rs @@ -4,9 +4,9 @@ pub type T0 = const fn(); //~ ERROR an `fn` pointer type cannot be `const` pub type T1 = const extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` -pub type T2 = const unsafe extern fn(); //~ ERROR an `fn` pointer type cannot be `const` +pub type T2 = const unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` pub type T3 = async fn(); //~ ERROR an `fn` pointer type cannot be `async` -pub type T4 = async extern fn(); //~ ERROR an `fn` pointer type cannot be `async` +pub type T4 = async extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` pub type T5 = async unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` pub type T6 = const async unsafe extern "C" fn(); //~^ ERROR an `fn` pointer type cannot be `const` @@ -14,9 +14,9 @@ pub type T6 = const async unsafe extern "C" fn(); pub type FTT0 = for<'a> const fn(); //~ ERROR an `fn` pointer type cannot be `const` pub type FTT1 = for<'a> const extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` -pub type FTT2 = for<'a> const unsafe extern fn(); //~ ERROR an `fn` pointer type cannot be `const` +pub type FTT2 = for<'a> const unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` pub type FTT3 = for<'a> async fn(); //~ ERROR an `fn` pointer type cannot be `async` -pub type FTT4 = for<'a> async extern fn(); //~ ERROR an `fn` pointer type cannot be `async` +pub type FTT4 = for<'a> async extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` pub type FTT5 = for<'a> async unsafe extern "C" fn(); //~^ ERROR an `fn` pointer type cannot be `async` pub type FTT6 = for<'a> const async unsafe extern "C" fn(); diff --git a/tests/ui/parser/bad-fn-ptr-qualifier.stderr b/tests/ui/parser/bad-fn-ptr-qualifier.stderr index 523ee47b0c942..b9d2625d9f4be 100644 --- a/tests/ui/parser/bad-fn-ptr-qualifier.stderr +++ b/tests/ui/parser/bad-fn-ptr-qualifier.stderr @@ -9,7 +9,7 @@ LL | pub type T0 = const fn(); help: remove the `const` qualifier | LL - pub type T0 = const fn(); -LL + pub type T0 = fn(); +LL + pub type T0 = fn(); | error: an `fn` pointer type cannot be `const` @@ -23,21 +23,21 @@ LL | pub type T1 = const extern "C" fn(); help: remove the `const` qualifier | LL - pub type T1 = const extern "C" fn(); -LL + pub type T1 = extern "C" fn(); +LL + pub type T1 = extern "C" fn(); | error: an `fn` pointer type cannot be `const` --> $DIR/bad-fn-ptr-qualifier.rs:7:15 | -LL | pub type T2 = const unsafe extern fn(); - | -----^^^^^^^^^^^^^^^^^^^ +LL | pub type T2 = const unsafe extern "C" fn(); + | -----^^^^^^^^^^^^^^^^^^^^^^^ | | | `const` because of this | help: remove the `const` qualifier | -LL - pub type T2 = const unsafe extern fn(); -LL + pub type T2 = unsafe extern fn(); +LL - pub type T2 = const unsafe extern "C" fn(); +LL + pub type T2 = unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -51,21 +51,21 @@ LL | pub type T3 = async fn(); help: remove the `async` qualifier | LL - pub type T3 = async fn(); -LL + pub type T3 = fn(); +LL + pub type T3 = fn(); | error: an `fn` pointer type cannot be `async` --> $DIR/bad-fn-ptr-qualifier.rs:9:15 | -LL | pub type T4 = async extern fn(); - | -----^^^^^^^^^^^^ +LL | pub type T4 = async extern "C" fn(); + | -----^^^^^^^^^^^^^^^^ | | | `async` because of this | help: remove the `async` qualifier | -LL - pub type T4 = async extern fn(); -LL + pub type T4 = extern fn(); +LL - pub type T4 = async extern "C" fn(); +LL + pub type T4 = extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -79,7 +79,7 @@ LL | pub type T5 = async unsafe extern "C" fn(); help: remove the `async` qualifier | LL - pub type T5 = async unsafe extern "C" fn(); -LL + pub type T5 = unsafe extern "C" fn(); +LL + pub type T5 = unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -93,7 +93,7 @@ LL | pub type T6 = const async unsafe extern "C" fn(); help: remove the `const` qualifier | LL - pub type T6 = const async unsafe extern "C" fn(); -LL + pub type T6 = async unsafe extern "C" fn(); +LL + pub type T6 = async unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -107,7 +107,7 @@ LL | pub type T6 = const async unsafe extern "C" fn(); help: remove the `async` qualifier | LL - pub type T6 = const async unsafe extern "C" fn(); -LL + pub type T6 = const unsafe extern "C" fn(); +LL + pub type T6 = const unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -121,7 +121,7 @@ LL | pub type FTT0 = for<'a> const fn(); help: remove the `const` qualifier | LL - pub type FTT0 = for<'a> const fn(); -LL + pub type FTT0 = for<'a> fn(); +LL + pub type FTT0 = for<'a> fn(); | error: an `fn` pointer type cannot be `const` @@ -135,21 +135,21 @@ LL | pub type FTT1 = for<'a> const extern "C" fn(); help: remove the `const` qualifier | LL - pub type FTT1 = for<'a> const extern "C" fn(); -LL + pub type FTT1 = for<'a> extern "C" fn(); +LL + pub type FTT1 = for<'a> extern "C" fn(); | error: an `fn` pointer type cannot be `const` --> $DIR/bad-fn-ptr-qualifier.rs:17:17 | -LL | pub type FTT2 = for<'a> const unsafe extern fn(); - | ^^^^^^^^-----^^^^^^^^^^^^^^^^^^^ +LL | pub type FTT2 = for<'a> const unsafe extern "C" fn(); + | ^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^ | | | `const` because of this | help: remove the `const` qualifier | -LL - pub type FTT2 = for<'a> const unsafe extern fn(); -LL + pub type FTT2 = for<'a> unsafe extern fn(); +LL - pub type FTT2 = for<'a> const unsafe extern "C" fn(); +LL + pub type FTT2 = for<'a> unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -163,21 +163,21 @@ LL | pub type FTT3 = for<'a> async fn(); help: remove the `async` qualifier | LL - pub type FTT3 = for<'a> async fn(); -LL + pub type FTT3 = for<'a> fn(); +LL + pub type FTT3 = for<'a> fn(); | error: an `fn` pointer type cannot be `async` --> $DIR/bad-fn-ptr-qualifier.rs:19:17 | -LL | pub type FTT4 = for<'a> async extern fn(); - | ^^^^^^^^-----^^^^^^^^^^^^ +LL | pub type FTT4 = for<'a> async extern "C" fn(); + | ^^^^^^^^-----^^^^^^^^^^^^^^^^ | | | `async` because of this | help: remove the `async` qualifier | -LL - pub type FTT4 = for<'a> async extern fn(); -LL + pub type FTT4 = for<'a> extern fn(); +LL - pub type FTT4 = for<'a> async extern "C" fn(); +LL + pub type FTT4 = for<'a> extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -191,7 +191,7 @@ LL | pub type FTT5 = for<'a> async unsafe extern "C" fn(); help: remove the `async` qualifier | LL - pub type FTT5 = for<'a> async unsafe extern "C" fn(); -LL + pub type FTT5 = for<'a> unsafe extern "C" fn(); +LL + pub type FTT5 = for<'a> unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -205,7 +205,7 @@ LL | pub type FTT6 = for<'a> const async unsafe extern "C" fn(); help: remove the `const` qualifier | LL - pub type FTT6 = for<'a> const async unsafe extern "C" fn(); -LL + pub type FTT6 = for<'a> async unsafe extern "C" fn(); +LL + pub type FTT6 = for<'a> async unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -219,7 +219,7 @@ LL | pub type FTT6 = for<'a> const async unsafe extern "C" fn(); help: remove the `async` qualifier | LL - pub type FTT6 = for<'a> const async unsafe extern "C" fn(); -LL + pub type FTT6 = for<'a> const unsafe extern "C" fn(); +LL + pub type FTT6 = for<'a> const unsafe extern "C" fn(); | error: aborting due to 16 previous errors diff --git a/tests/ui/parser/bad-let-else-statement.stderr b/tests/ui/parser/bad-let-else-statement.stderr index 79d722bb7ac44..ba56452998312 100644 --- a/tests/ui/parser/bad-let-else-statement.stderr +++ b/tests/ui/parser/bad-let-else-statement.stderr @@ -225,8 +225,9 @@ LL | let bad = format_args! {""} else { return; }; | help: use parentheses instead of braces for this macro | -LL | let bad = format_args! ("") else { return; }; - | ~ ~ +LL - let bad = format_args! {""} else { return; }; +LL + let bad = format_args! ("") else { return; }; + | error: right curly brace `}` before `else` in a `let...else` statement not allowed --> $DIR/bad-let-else-statement.rs:207:5 @@ -254,8 +255,9 @@ LL | b!(2); = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) help: use parentheses instead of braces for this macro | -LL | let 0 = a! () else { return; }; - | ~~ +LL - let 0 = a! {} else { return; }; +LL + let 0 = a! () else { return; }; + | warning: irrefutable `let...else` pattern --> $DIR/bad-let-else-statement.rs:95:5 diff --git a/tests/ui/parser/bad-lit-suffixes.rs b/tests/ui/parser/bad-lit-suffixes.rs index c614f49388574..f29dc53d322b9 100644 --- a/tests/ui/parser/bad-lit-suffixes.rs +++ b/tests/ui/parser/bad-lit-suffixes.rs @@ -1,10 +1,10 @@ #![feature(rustc_attrs)] -extern +extern //~ WARN missing_abi "C"suffix //~ ERROR suffixes on string literals are invalid fn foo() {} -extern +extern //~ WARN missing_abi "C"suffix //~ ERROR suffixes on string literals are invalid {} diff --git a/tests/ui/parser/bad-lit-suffixes.stderr b/tests/ui/parser/bad-lit-suffixes.stderr index b5dacdf7d0d3d..d6b50b0e0d1fe 100644 --- a/tests/ui/parser/bad-lit-suffixes.stderr +++ b/tests/ui/parser/bad-lit-suffixes.stderr @@ -30,9 +30,11 @@ LL | #[must_use = "string"suffix] | help: the following are the possible correct uses | -LL | #[must_use = "reason"] +LL - #[must_use = "string"suffix] +LL + #[must_use = "reason"] | -LL | #[must_use] +LL - #[must_use = "string"suffix] +LL + #[must_use] | error: suffixes on string literals are invalid @@ -49,6 +51,20 @@ LL | #[rustc_layout_scalar_valid_range_start(0suffix)] | = help: the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.) +warning: extern declarations without an explicit ABI are deprecated + --> $DIR/bad-lit-suffixes.rs:3:1 + | +LL | extern + | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` + | + = note: `#[warn(missing_abi)]` on by default + +warning: extern declarations without an explicit ABI are deprecated + --> $DIR/bad-lit-suffixes.rs:7:1 + | +LL | extern + | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` + error: suffixes on string literals are invalid --> $DIR/bad-lit-suffixes.rs:12:5 | @@ -149,5 +165,5 @@ LL | 1.0e10suffix; | = help: valid suffixes are `f32` and `f64` -error: aborting due to 21 previous errors +error: aborting due to 21 previous errors; 2 warnings emitted diff --git a/tests/ui/parser/byte-literals.stderr b/tests/ui/parser/byte-literals.stderr index 25e319954418d..1c89e8e2864b6 100644 --- a/tests/ui/parser/byte-literals.stderr +++ b/tests/ui/parser/byte-literals.stderr @@ -40,7 +40,7 @@ LL | b'''; help: escape the character | LL | b'\''; - | ~~ + | + error: non-ASCII character in byte literal --> $DIR/byte-literals.rs:10:7 @@ -50,8 +50,9 @@ LL | b'é'; | help: if you meant to use the unicode code point for 'é', use a \xHH escape | -LL | b'\xE9'; - | ~~~~ +LL - b'é'; +LL + b'\xE9'; + | error[E0763]: unterminated byte constant --> $DIR/byte-literals.rs:11:6 diff --git a/tests/ui/parser/byte-string-literals.stderr b/tests/ui/parser/byte-string-literals.stderr index 24e0eaac8fa91..086337425577f 100644 --- a/tests/ui/parser/byte-string-literals.stderr +++ b/tests/ui/parser/byte-string-literals.stderr @@ -28,8 +28,9 @@ LL | b"é"; | help: if you meant to use the unicode code point for 'é', use a \xHH escape | -LL | b"\xE9"; - | ~~~~ +LL - b"é"; +LL + b"\xE9"; + | error: non-ASCII character in raw byte string literal --> $DIR/byte-string-literals.rs:7:10 diff --git a/tests/ui/parser/const-param-decl-on-type-instead-of-impl.rs b/tests/ui/parser/const-param-decl-on-type-instead-of-impl.rs index 53e3c6f960500..3876fb41d23f1 100644 --- a/tests/ui/parser/const-param-decl-on-type-instead-of-impl.rs +++ b/tests/ui/parser/const-param-decl-on-type-instead-of-impl.rs @@ -11,5 +11,5 @@ fn banana(a: >::BAR) {} fn chaenomeles() { path::path::Struct::() //~^ ERROR unexpected `const` parameter declaration - //~| ERROR failed to resolve: use of undeclared crate or module `path` + //~| ERROR failed to resolve: use of unresolved module or unlinked crate `path` } diff --git a/tests/ui/parser/const-param-decl-on-type-instead-of-impl.stderr b/tests/ui/parser/const-param-decl-on-type-instead-of-impl.stderr index 96885d11ee07f..855163e3d3bd3 100644 --- a/tests/ui/parser/const-param-decl-on-type-instead-of-impl.stderr +++ b/tests/ui/parser/const-param-decl-on-type-instead-of-impl.stderr @@ -6,8 +6,9 @@ LL | impl NInts {} | help: `const` parameters must be declared for the `impl` | -LL | impl NInts {} - | ++++++++++++++++ ~ +LL - impl NInts {} +LL + impl NInts {} + | error: unexpected `const` parameter declaration --> $DIR/const-param-decl-on-type-instead-of-impl.rs:8:17 @@ -21,11 +22,13 @@ error: unexpected `const` parameter declaration LL | path::path::Struct::() | ^^^^^^^^^^^^^^ expected a `const` expression, not a parameter declaration -error[E0433]: failed to resolve: use of undeclared crate or module `path` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `path` --> $DIR/const-param-decl-on-type-instead-of-impl.rs:12:5 | LL | path::path::Struct::() - | ^^^^ use of undeclared crate or module `path` + | ^^^^ use of unresolved module or unlinked crate `path` + | + = help: you might be missing a crate named `path` error[E0412]: cannot find type `T` in this scope --> $DIR/const-param-decl-on-type-instead-of-impl.rs:8:15 diff --git a/tests/ui/parser/default-on-wrong-item-kind.stderr b/tests/ui/parser/default-on-wrong-item-kind.stderr index 392c85e0c43d7..56641565b1668 100644 --- a/tests/ui/parser/default-on-wrong-item-kind.stderr +++ b/tests/ui/parser/default-on-wrong-item-kind.stderr @@ -159,8 +159,9 @@ LL | default const foo: u8; = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html help: try using a static value | -LL | static foo: u8; - | ~~~~~~ +LL - default const foo: u8; +LL + static foo: u8; + | error: a module cannot be `default` --> $DIR/default-on-wrong-item-kind.rs:41:5 diff --git a/tests/ui/parser/do-catch-suggests-try.stderr b/tests/ui/parser/do-catch-suggests-try.stderr index fd3406ae29f89..2eaab8360759b 100644 --- a/tests/ui/parser/do-catch-suggests-try.stderr +++ b/tests/ui/parser/do-catch-suggests-try.stderr @@ -7,8 +7,9 @@ LL | let _: Option<()> = do catch {}; = note: following RFC #2388, the new non-placeholder syntax is `try` help: replace with the new syntax | -LL | let _: Option<()> = try {}; - | ~~~ +LL - let _: Option<()> = do catch {}; +LL + let _: Option<()> = try {}; + | error[E0308]: mismatched types --> $DIR/do-catch-suggests-try.rs:9:33 diff --git a/tests/ui/parser/dotdotdot-expr.stderr b/tests/ui/parser/dotdotdot-expr.stderr index 208c04bd3df50..f0bc57264a607 100644 --- a/tests/ui/parser/dotdotdot-expr.stderr +++ b/tests/ui/parser/dotdotdot-expr.stderr @@ -6,12 +6,14 @@ LL | let _redemptive = 1...21; | help: use `..` for an exclusive range | -LL | let _redemptive = 1..21; - | ~~ +LL - let _redemptive = 1...21; +LL + let _redemptive = 1..21; + | help: or `..=` for an inclusive range | -LL | let _redemptive = 1..=21; - | ~~~ +LL - let _redemptive = 1...21; +LL + let _redemptive = 1..=21; + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/duplicate-where-clauses.stderr b/tests/ui/parser/duplicate-where-clauses.stderr index 8250d4f1e0568..298cdab0c68c3 100644 --- a/tests/ui/parser/duplicate-where-clauses.stderr +++ b/tests/ui/parser/duplicate-where-clauses.stderr @@ -8,8 +8,9 @@ LL | struct A where (): Sized where (): Sized {} | help: consider joining the two `where` clauses into one | -LL | struct A where (): Sized, (): Sized {} - | ~ +LL - struct A where (): Sized where (): Sized {} +LL + struct A where (): Sized, (): Sized {} + | error: cannot define duplicate `where` clauses on an item --> $DIR/duplicate-where-clauses.rs:4:30 @@ -21,8 +22,9 @@ LL | fn b() where (): Sized where (): Sized {} | help: consider joining the two `where` clauses into one | -LL | fn b() where (): Sized, (): Sized {} - | ~ +LL - fn b() where (): Sized where (): Sized {} +LL + fn b() where (): Sized, (): Sized {} + | error: cannot define duplicate `where` clauses on an item --> $DIR/duplicate-where-clauses.rs:7:30 @@ -34,8 +36,9 @@ LL | enum C where (): Sized where (): Sized {} | help: consider joining the two `where` clauses into one | -LL | enum C where (): Sized, (): Sized {} - | ~ +LL - enum C where (): Sized where (): Sized {} +LL + enum C where (): Sized, (): Sized {} + | error: cannot define duplicate `where` clauses on an item --> $DIR/duplicate-where-clauses.rs:10:33 @@ -47,8 +50,9 @@ LL | struct D where (): Sized, where (): Sized {} | help: consider joining the two `where` clauses into one | -LL | struct D where (): Sized, (): Sized {} - | ~ +LL - struct D where (): Sized, where (): Sized {} +LL + struct D where (): Sized, (): Sized {} + | error: cannot define duplicate `where` clauses on an item --> $DIR/duplicate-where-clauses.rs:13:31 @@ -60,8 +64,9 @@ LL | fn e() where (): Sized, where (): Sized {} | help: consider joining the two `where` clauses into one | -LL | fn e() where (): Sized, (): Sized {} - | ~ +LL - fn e() where (): Sized, where (): Sized {} +LL + fn e() where (): Sized, (): Sized {} + | error: cannot define duplicate `where` clauses on an item --> $DIR/duplicate-where-clauses.rs:16:31 @@ -73,8 +78,9 @@ LL | enum F where (): Sized, where (): Sized {} | help: consider joining the two `where` clauses into one | -LL | enum F where (): Sized, (): Sized {} - | ~ +LL - enum F where (): Sized, where (): Sized {} +LL + enum F where (): Sized, (): Sized {} + | error: aborting due to 6 previous errors diff --git a/tests/ui/parser/dyn-trait-compatibility.rs b/tests/ui/parser/dyn-trait-compatibility.rs index 6341e05327754..717b14c5941fd 100644 --- a/tests/ui/parser/dyn-trait-compatibility.rs +++ b/tests/ui/parser/dyn-trait-compatibility.rs @@ -1,7 +1,7 @@ type A0 = dyn; //~^ ERROR cannot find type `dyn` in this scope type A1 = dyn::dyn; -//~^ ERROR use of undeclared crate or module `dyn` +//~^ ERROR use of unresolved module or unlinked crate `dyn` type A2 = dyn; //~^ ERROR cannot find type `dyn` in this scope //~| ERROR cannot find type `dyn` in this scope diff --git a/tests/ui/parser/dyn-trait-compatibility.stderr b/tests/ui/parser/dyn-trait-compatibility.stderr index e34d855a9d4f9..08e0a50010a88 100644 --- a/tests/ui/parser/dyn-trait-compatibility.stderr +++ b/tests/ui/parser/dyn-trait-compatibility.stderr @@ -40,11 +40,13 @@ error[E0412]: cannot find type `dyn` in this scope LL | type A3 = dyn<::dyn>; | ^^^ not found in this scope -error[E0433]: failed to resolve: use of undeclared crate or module `dyn` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `dyn` --> $DIR/dyn-trait-compatibility.rs:3:11 | LL | type A1 = dyn::dyn; - | ^^^ use of undeclared crate or module `dyn` + | ^^^ use of unresolved module or unlinked crate `dyn` + | + = help: you might be missing a crate named `dyn` error: aborting due to 8 previous errors diff --git a/tests/ui/parser/emoji-identifiers.stderr b/tests/ui/parser/emoji-identifiers.stderr index 536afc53f0ceb..f0e90082bff9d 100644 --- a/tests/ui/parser/emoji-identifiers.stderr +++ b/tests/ui/parser/emoji-identifiers.stderr @@ -6,8 +6,9 @@ LL | let _ = i_like_to_😄_a_lot() ➖ 4; | help: Unicode character '➖' (Heavy Minus Sign) looks like '-' (Minus/Hyphen), but it is not | -LL | let _ = i_like_to_😄_a_lot() - 4; - | ~ +LL - let _ = i_like_to_😄_a_lot() ➖ 4; +LL + let _ = i_like_to_😄_a_lot() - 4; + | error: identifiers cannot contain emoji: `ABig👩👩👧👧Family` --> $DIR/emoji-identifiers.rs:1:8 @@ -80,8 +81,9 @@ LL | fn full_of_✨() -> 👀 { | ^^^^^^^^^^^^^^^^^^^^^ help: there is an associated function `full_of_✨` with a similar name | -LL | 👀::full_of_✨() - | ~~~~~~~~~~ +LL - 👀::full_of✨() +LL + 👀::full_of_✨() + | error[E0425]: cannot find function `i_like_to_😄_a_lot` in this scope --> $DIR/emoji-identifiers.rs:13:13 diff --git a/tests/ui/parser/eq-gt-to-gt-eq.stderr b/tests/ui/parser/eq-gt-to-gt-eq.stderr index 73f465f7b9b28..aa47ddecce9ef 100644 --- a/tests/ui/parser/eq-gt-to-gt-eq.stderr +++ b/tests/ui/parser/eq-gt-to-gt-eq.stderr @@ -11,8 +11,9 @@ LL | if a => b {} | ^ help: you might have meant to write a "greater than or equal to" comparison | -LL | if a >= b {} - | ~~ +LL - if a => b {} +LL + if a >= b {} + | error: expected `{`, found `=>` --> $DIR/eq-gt-to-gt-eq.rs:13:10 @@ -27,8 +28,9 @@ LL | if a => 1 {} | ^ help: you might have meant to write a "greater than or equal to" comparison | -LL | if a >= 1 {} - | ~~ +LL - if a => 1 {} +LL + if a >= 1 {} + | error: expected `{`, found `=>` --> $DIR/eq-gt-to-gt-eq.rs:18:10 @@ -43,8 +45,9 @@ LL | if 1 => a {} | ^ help: you might have meant to write a "greater than or equal to" comparison | -LL | if 1 >= a {} - | ~~ +LL - if 1 => a {} +LL + if 1 >= a {} + | error: expected `{`, found `=>` --> $DIR/eq-gt-to-gt-eq.rs:24:10 @@ -59,8 +62,9 @@ LL | if a => b && a != b {} | ^ help: you might have meant to write a "greater than or equal to" comparison | -LL | if a >= b && a != b {} - | ~~ +LL - if a => b && a != b {} +LL + if a >= b && a != b {} + | error: expected `{`, found `=>` --> $DIR/eq-gt-to-gt-eq.rs:30:20 @@ -75,8 +79,9 @@ LL | if a != b && a => b {} | ^^^^^^^^^^^ help: you might have meant to write a "greater than or equal to" comparison | -LL | if a != b && a >= b {} - | ~~ +LL - if a != b && a => b {} +LL + if a != b && a >= b {} + | error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `=>` --> $DIR/eq-gt-to-gt-eq.rs:36:15 @@ -86,8 +91,9 @@ LL | let _ = a => b; | help: you might have meant to write a "greater than or equal to" comparison | -LL | let _ = a >= b; - | ~~ +LL - let _ = a => b; +LL + let _ = a >= b; + | error: expected one of `!`, `.`, `::`, `?`, `{`, or an operator, found `=>` --> $DIR/eq-gt-to-gt-eq.rs:42:13 @@ -99,8 +105,9 @@ LL | match a => b { | help: you might have meant to write a "greater than or equal to" comparison | -LL | match a >= b { - | ~~ +LL - match a => b { +LL + match a >= b { + | error: aborting due to 7 previous errors diff --git a/tests/ui/parser/expr-rarrow-call.stderr b/tests/ui/parser/expr-rarrow-call.stderr index 221e3a74d79f9..2e168ca26fecc 100644 --- a/tests/ui/parser/expr-rarrow-call.stderr +++ b/tests/ui/parser/expr-rarrow-call.stderr @@ -7,8 +7,9 @@ LL | named->foo; = help: the `.` operator will dereference the value if needed help: try using `.` instead | -LL | named.foo; - | ~ +LL - named->foo; +LL + named.foo; + | error: `->` used for field access or method call --> $DIR/expr-rarrow-call.rs:18:12 @@ -19,8 +20,9 @@ LL | unnamed->0; = help: the `.` operator will dereference the value if needed help: try using `.` instead | -LL | unnamed.0; - | ~ +LL - unnamed->0; +LL + unnamed.0; + | error: `->` used for field access or method call --> $DIR/expr-rarrow-call.rs:22:6 @@ -31,8 +33,9 @@ LL | t->0; = help: the `.` operator will dereference the value if needed help: try using `.` instead | -LL | t.0; - | ~ +LL - t->0; +LL + t.0; + | error: `->` used for field access or method call --> $DIR/expr-rarrow-call.rs:23:6 @@ -43,8 +46,9 @@ LL | t->1; = help: the `.` operator will dereference the value if needed help: try using `.` instead | -LL | t.1; - | ~ +LL - t->1; +LL + t.1; + | error: `->` used for field access or method call --> $DIR/expr-rarrow-call.rs:30:8 @@ -55,8 +59,9 @@ LL | foo->clone(); = help: the `.` operator will dereference the value if needed help: try using `.` instead | -LL | foo.clone(); - | ~ +LL - foo->clone(); +LL + foo.clone(); + | error: aborting due to 5 previous errors diff --git a/tests/ui/parser/extern-crate-unexpected-token.stderr b/tests/ui/parser/extern-crate-unexpected-token.stderr index 951b0274b0d46..3d48f0adfa11a 100644 --- a/tests/ui/parser/extern-crate-unexpected-token.stderr +++ b/tests/ui/parser/extern-crate-unexpected-token.stderr @@ -6,8 +6,9 @@ LL | extern crte foo; | help: there is a keyword `crate` with a similar name | -LL | extern crate foo; - | ~~~~~ +LL - extern crte foo; +LL + extern crate foo; + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/extern-no-fn.stderr b/tests/ui/parser/extern-no-fn.stderr index 03826e4a93b7a..67acbf9b87fc2 100644 --- a/tests/ui/parser/extern-no-fn.stderr +++ b/tests/ui/parser/extern-no-fn.stderr @@ -12,7 +12,7 @@ LL | } help: if you meant to call a macro, try | LL | f!(); - | ~~ + | + error: aborting due to 1 previous error diff --git a/tests/ui/parser/fn-body-eq-expr-semi.stderr b/tests/ui/parser/fn-body-eq-expr-semi.stderr index f1255d8642a60..adcb4fef0a376 100644 --- a/tests/ui/parser/fn-body-eq-expr-semi.stderr +++ b/tests/ui/parser/fn-body-eq-expr-semi.stderr @@ -6,8 +6,9 @@ LL | fn foo() = 42; | help: surround the expression with `{` and `}` instead of `=` and `;` | -LL | fn foo() { 42 } - | ~ ~ +LL - fn foo() = 42; +LL + fn foo() { 42 } + | error: function body cannot be `= expression;` --> $DIR/fn-body-eq-expr-semi.rs:5:20 @@ -17,8 +18,9 @@ LL | fn bar() -> u8 = 42; | help: surround the expression with `{` and `}` instead of `=` and `;` | -LL | fn bar() -> u8 { 42 } - | ~ ~ +LL - fn bar() -> u8 = 42; +LL + fn bar() -> u8 { 42 } + | error: function body cannot be `= expression;` --> $DIR/fn-body-eq-expr-semi.rs:9:14 @@ -28,8 +30,9 @@ LL | fn foo() = 42; | help: surround the expression with `{` and `}` instead of `=` and `;` | -LL | fn foo() { 42 } - | ~ ~ +LL - fn foo() = 42; +LL + fn foo() { 42 } + | error: function body cannot be `= expression;` --> $DIR/fn-body-eq-expr-semi.rs:11:20 @@ -39,8 +42,9 @@ LL | fn bar() -> u8 = 42; | help: surround the expression with `{` and `}` instead of `=` and `;` | -LL | fn bar() -> u8 { 42 } - | ~ ~ +LL - fn bar() -> u8 = 42; +LL + fn bar() -> u8 { 42 } + | error: function body cannot be `= expression;` --> $DIR/fn-body-eq-expr-semi.rs:16:14 @@ -50,8 +54,9 @@ LL | fn foo() = 42; | help: surround the expression with `{` and `}` instead of `=` and `;` | -LL | fn foo() { 42 } - | ~ ~ +LL - fn foo() = 42; +LL + fn foo() { 42 } + | error: function body cannot be `= expression;` --> $DIR/fn-body-eq-expr-semi.rs:17:20 @@ -61,8 +66,9 @@ LL | fn bar() -> u8 = 42; | help: surround the expression with `{` and `}` instead of `=` and `;` | -LL | fn bar() -> u8 { 42 } - | ~ ~ +LL - fn bar() -> u8 = 42; +LL + fn bar() -> u8 { 42 } + | error: function body cannot be `= expression;` --> $DIR/fn-body-eq-expr-semi.rs:21:14 @@ -72,8 +78,9 @@ LL | fn foo() = 42; | help: surround the expression with `{` and `}` instead of `=` and `;` | -LL | fn foo() { 42 } - | ~ ~ +LL - fn foo() = 42; +LL + fn foo() { 42 } + | error: function body cannot be `= expression;` --> $DIR/fn-body-eq-expr-semi.rs:22:20 @@ -83,8 +90,9 @@ LL | fn bar() -> u8 = 42; | help: surround the expression with `{` and `}` instead of `=` and `;` | -LL | fn bar() -> u8 { 42 } - | ~ ~ +LL - fn bar() -> u8 = 42; +LL + fn bar() -> u8 { 42 } + | error: incorrect function inside `extern` block --> $DIR/fn-body-eq-expr-semi.rs:9:8 diff --git a/tests/ui/parser/fn-body-optional-semantic-fail.rs b/tests/ui/parser/fn-body-optional-semantic-fail.rs index 12df488802ecd..46f5d7e96d104 100644 --- a/tests/ui/parser/fn-body-optional-semantic-fail.rs +++ b/tests/ui/parser/fn-body-optional-semantic-fail.rs @@ -1,7 +1,11 @@ // Tests the different rules for `fn` forms requiring the presence or lack of a body. +// Also ensures that functions without a body don't show other odd errors. + +trait Trait {} fn main() { fn f1(); //~ ERROR free function without a body + fn f1_rpit() -> impl Trait; //~ ERROR free function without a body fn f2() {} // OK. trait X { diff --git a/tests/ui/parser/fn-body-optional-semantic-fail.stderr b/tests/ui/parser/fn-body-optional-semantic-fail.stderr index 14bcd7c16faa1..525a0a0f68163 100644 --- a/tests/ui/parser/fn-body-optional-semantic-fail.stderr +++ b/tests/ui/parser/fn-body-optional-semantic-fail.stderr @@ -1,13 +1,21 @@ error: free function without a body - --> $DIR/fn-body-optional-semantic-fail.rs:4:5 + --> $DIR/fn-body-optional-semantic-fail.rs:7:5 | LL | fn f1(); | ^^^^^^^- | | | help: provide a definition for the function: `{ }` +error: free function without a body + --> $DIR/fn-body-optional-semantic-fail.rs:8:5 + | +LL | fn f1_rpit() -> impl Trait; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: provide a definition for the function: `{ }` + error: associated function in `impl` without body - --> $DIR/fn-body-optional-semantic-fail.rs:14:9 + --> $DIR/fn-body-optional-semantic-fail.rs:18:9 | LL | fn f1(); | ^^^^^^^- @@ -15,7 +23,7 @@ LL | fn f1(); | help: provide a definition for the function: `{ }` error: associated function in `impl` without body - --> $DIR/fn-body-optional-semantic-fail.rs:19:9 + --> $DIR/fn-body-optional-semantic-fail.rs:23:9 | LL | fn f3(); | ^^^^^^^- @@ -23,7 +31,7 @@ LL | fn f3(); | help: provide a definition for the function: `{ }` error: incorrect function inside `extern` block - --> $DIR/fn-body-optional-semantic-fail.rs:25:12 + --> $DIR/fn-body-optional-semantic-fail.rs:29:12 | LL | extern "C" { | ---------- `extern` blocks define existing foreign functions and functions inside of them cannot have a body @@ -36,5 +44,5 @@ LL | fn f6() {} = help: you might have meant to write a function accessible through FFI, which can be done by writing `extern fn` outside of the `extern` block = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/parser/fn-colon-return-type.stderr b/tests/ui/parser/fn-colon-return-type.stderr index c1cdf4d4975a6..d6d30c5fd0748 100644 --- a/tests/ui/parser/fn-colon-return-type.stderr +++ b/tests/ui/parser/fn-colon-return-type.stderr @@ -6,8 +6,9 @@ LL | fn foo(x: i32): i32 { | help: use `->` instead | -LL | fn foo(x: i32) -> i32 { - | ~~ +LL - fn foo(x: i32): i32 { +LL + fn foo(x: i32) -> i32 { + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/foreign-const-semantic-fail.stderr b/tests/ui/parser/foreign-const-semantic-fail.stderr index d317847f98ad7..b2240738c4974 100644 --- a/tests/ui/parser/foreign-const-semantic-fail.stderr +++ b/tests/ui/parser/foreign-const-semantic-fail.stderr @@ -7,8 +7,9 @@ LL | const A: isize; = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html help: try using a static value | -LL | static A: isize; - | ~~~~~~ +LL - const A: isize; +LL + static A: isize; + | error: extern items cannot be `const` --> $DIR/foreign-const-semantic-fail.rs:6:11 @@ -19,8 +20,9 @@ LL | const B: isize = 42; = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html help: try using a static value | -LL | static B: isize = 42; - | ~~~~~~ +LL - const B: isize = 42; +LL + static B: isize = 42; + | error: incorrect `static` inside `extern` block --> $DIR/foreign-const-semantic-fail.rs:6:11 diff --git a/tests/ui/parser/foreign-const-syntactic-fail.stderr b/tests/ui/parser/foreign-const-syntactic-fail.stderr index 7da2c0190228d..f7466d5d6cd23 100644 --- a/tests/ui/parser/foreign-const-syntactic-fail.stderr +++ b/tests/ui/parser/foreign-const-syntactic-fail.stderr @@ -7,8 +7,9 @@ LL | const A: isize; = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html help: try using a static value | -LL | static A: isize; - | ~~~~~~ +LL - const A: isize; +LL + static A: isize; + | error: extern items cannot be `const` --> $DIR/foreign-const-syntactic-fail.rs:8:11 @@ -19,8 +20,9 @@ LL | const B: isize = 42; = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html help: try using a static value | -LL | static B: isize = 42; - | ~~~~~~ +LL - const B: isize = 42; +LL + static B: isize = 42; + | error: aborting due to 2 previous errors diff --git a/tests/ui/parser/increment-autofix-2.stderr b/tests/ui/parser/increment-autofix-2.stderr index 11e985480d697..3fee967279626 100644 --- a/tests/ui/parser/increment-autofix-2.stderr +++ b/tests/ui/parser/increment-autofix-2.stderr @@ -6,8 +6,9 @@ LL | i++; | help: use `+= 1` instead | -LL | i += 1; - | ~~~~ +LL - i++; +LL + i += 1; + | error: Rust has no postfix increment operator --> $DIR/increment-autofix-2.rs:19:12 @@ -19,8 +20,9 @@ LL | while i++ < 5 { | help: use `+= 1` instead | -LL | while { let tmp = i; i += 1; tmp } < 5 { - | +++++++++++ ~~~~~~~~~~~~~~~ +LL - while i++ < 5 { +LL + while { let tmp = i; i += 1; tmp } < 5 { + | error: Rust has no postfix increment operator --> $DIR/increment-autofix-2.rs:27:8 @@ -30,8 +32,9 @@ LL | tmp++; | help: use `+= 1` instead | -LL | tmp += 1; - | ~~~~ +LL - tmp++; +LL + tmp += 1; + | error: Rust has no postfix increment operator --> $DIR/increment-autofix-2.rs:33:14 @@ -43,8 +46,9 @@ LL | while tmp++ < 5 { | help: use `+= 1` instead | -LL | while { let tmp_ = tmp; tmp += 1; tmp_ } < 5 { - | ++++++++++++ ~~~~~~~~~~~~~~~~~~ +LL - while tmp++ < 5 { +LL + while { let tmp_ = tmp; tmp += 1; tmp_ } < 5 { + | error: Rust has no postfix increment operator --> $DIR/increment-autofix-2.rs:41:16 @@ -54,8 +58,9 @@ LL | foo.bar.qux++; | help: use `+= 1` instead | -LL | foo.bar.qux += 1; - | ~~~~ +LL - foo.bar.qux++; +LL + foo.bar.qux += 1; + | error: Rust has no postfix increment operator --> $DIR/increment-autofix-2.rs:51:10 @@ -65,8 +70,9 @@ LL | s.tmp++; | help: use `+= 1` instead | -LL | s.tmp += 1; - | ~~~~ +LL - s.tmp++; +LL + s.tmp += 1; + | error: Rust has no prefix increment operator --> $DIR/increment-autofix-2.rs:58:5 diff --git a/tests/ui/parser/increment-autofix.stderr b/tests/ui/parser/increment-autofix.stderr index 1dc69fd9f4659..ffff91abee954 100644 --- a/tests/ui/parser/increment-autofix.stderr +++ b/tests/ui/parser/increment-autofix.stderr @@ -20,8 +20,9 @@ LL | while ++i < 5 { | help: use `+= 1` instead | -LL | while { i += 1; i } < 5 { - | ~ +++++++++ +LL - while ++i < 5 { +LL + while { i += 1; i } < 5 { + | error: Rust has no prefix increment operator --> $DIR/increment-autofix.rs:19:5 @@ -45,8 +46,9 @@ LL | while ++tmp < 5 { | help: use `+= 1` instead | -LL | while { tmp += 1; tmp } < 5 { - | ~ +++++++++++ +LL - while ++tmp < 5 { +LL + while { tmp += 1; tmp } < 5 { + | error: aborting due to 4 previous errors diff --git a/tests/ui/parser/intersection-patterns-1.stderr b/tests/ui/parser/intersection-patterns-1.stderr index ed2466b21a751..c191b46fa45db 100644 --- a/tests/ui/parser/intersection-patterns-1.stderr +++ b/tests/ui/parser/intersection-patterns-1.stderr @@ -9,8 +9,9 @@ LL | Some(x) @ y => {} | help: switch the order | -LL | y @ Some(x) => {} - | ~~~~~~~~~~~ +LL - Some(x) @ y => {} +LL + y @ Some(x) => {} + | error: pattern on wrong side of `@` --> $DIR/intersection-patterns-1.rs:27:9 @@ -23,8 +24,9 @@ LL | 1 ..= 5 @ e => {} | help: switch the order | -LL | e @ 1..=5 => {} - | ~~~~~~~~~ +LL - 1 ..= 5 @ e => {} +LL + e @ 1..=5 => {} + | error: aborting due to 2 previous errors diff --git a/tests/ui/parser/issues/issue-100197-mut-let.stderr b/tests/ui/parser/issues/issue-100197-mut-let.stderr index 252ed7d0715d4..e43d5f68607de 100644 --- a/tests/ui/parser/issues/issue-100197-mut-let.stderr +++ b/tests/ui/parser/issues/issue-100197-mut-let.stderr @@ -6,8 +6,9 @@ LL | mut let _x = 123; | help: switch the order of `mut` and `let` | -LL | let mut _x = 123; - | ~~~~~~~ +LL - mut let _x = 123; +LL + let mut _x = 123; + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-101477-enum.stderr b/tests/ui/parser/issues/issue-101477-enum.stderr index 8d4efdd17f7b2..c6dadeab8b33f 100644 --- a/tests/ui/parser/issues/issue-101477-enum.stderr +++ b/tests/ui/parser/issues/issue-101477-enum.stderr @@ -7,8 +7,9 @@ LL | B == 2 = help: enum variants can be `Variant`, `Variant = `, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }` help: try using `=` instead | -LL | B = 2 - | ~ +LL - B == 2 +LL + B = 2 + | error: expected item, found `==` --> $DIR/issue-101477-enum.rs:6:7 diff --git a/tests/ui/parser/issues/issue-101477-let.stderr b/tests/ui/parser/issues/issue-101477-let.stderr index d2671abbdea9e..59e90c8102f75 100644 --- a/tests/ui/parser/issues/issue-101477-let.stderr +++ b/tests/ui/parser/issues/issue-101477-let.stderr @@ -6,8 +6,9 @@ LL | let x == 2; | help: try using `=` instead | -LL | let x = 2; - | ~ +LL - let x == 2; +LL + let x = 2; + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-102806.stderr b/tests/ui/parser/issues/issue-102806.stderr index ba8174a823b2a..cd447c6dec0d2 100644 --- a/tests/ui/parser/issues/issue-102806.stderr +++ b/tests/ui/parser/issues/issue-102806.stderr @@ -6,8 +6,9 @@ LL | let _ = V3 { z: 0.0, ...v}; | help: use `..` to fill in the rest of the fields | -LL | let _ = V3 { z: 0.0, ..v}; - | ~~ +LL - let _ = V3 { z: 0.0, ...v}; +LL + let _ = V3 { z: 0.0, ..v}; + | error: expected `..`, found `...` --> $DIR/issue-102806.rs:14:26 @@ -17,8 +18,9 @@ LL | let _ = V3 { z: 0.0, ...Default::default() }; | help: use `..` to fill in the rest of the fields | -LL | let _ = V3 { z: 0.0, ..Default::default() }; - | ~~ +LL - let _ = V3 { z: 0.0, ...Default::default() }; +LL + let _ = V3 { z: 0.0, ..Default::default() }; + | error: expected identifier, found `...` --> $DIR/issue-102806.rs:17:26 @@ -36,8 +38,9 @@ LL | let V3 { z: val, ... } = v; | help: to omit remaining fields, use `..` | -LL | let V3 { z: val, .. } = v; - | ~~ +LL - let V3 { z: val, ... } = v; +LL + let V3 { z: val, .. } = v; + | error[E0063]: missing fields `x` and `y` in initializer of `V3` --> $DIR/issue-102806.rs:17:13 diff --git a/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.stderr b/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.stderr index b0d8b03ae08c3..97a73b4fd5ed3 100644 --- a/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.stderr +++ b/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.stderr @@ -6,8 +6,9 @@ LL | struct Apple((Apple, Option(Banana ? Citron))); | help: if you meant to express that the type might not contain a value, use the `Option` wrapper type | -LL | struct Apple((Apple, Option(Option Citron))); - | +++++++ ~ +LL - struct Apple((Apple, Option(Banana ? Citron))); +LL + struct Apple((Apple, Option(Option Citron))); + | error: expected one of `)` or `,`, found `Citron` --> $DIR/issue-103748-ICE-wrong-braces.rs:3:38 @@ -31,8 +32,9 @@ LL | struct Apple((Apple, Option(Banana ? Citron))); | help: use angle brackets instead | -LL | struct Apple((Apple, Option)); - | ~ ~ +LL - struct Apple((Apple, Option(Banana ? Citron))); +LL + struct Apple((Apple, Option)); + | error[E0072]: recursive type `Apple` has infinite size --> $DIR/issue-103748-ICE-wrong-braces.rs:3:1 diff --git a/tests/ui/parser/issues/issue-104867-inc-dec-2.stderr b/tests/ui/parser/issues/issue-104867-inc-dec-2.stderr index 4e2d0546851e3..f2412dda050cf 100644 --- a/tests/ui/parser/issues/issue-104867-inc-dec-2.stderr +++ b/tests/ui/parser/issues/issue-104867-inc-dec-2.stderr @@ -6,8 +6,9 @@ LL | let _ = i + ++i; | help: use `+= 1` instead | -LL | let _ = i + { i += 1; i }; - | ~ +++++++++ +LL - let _ = i + ++i; +LL + let _ = i + { i += 1; i }; + | error: Rust has no prefix increment operator --> $DIR/issue-104867-inc-dec-2.rs:8:13 @@ -17,8 +18,9 @@ LL | let _ = ++i + i; | help: use `+= 1` instead | -LL | let _ = { i += 1; i } + i; - | ~ +++++++++ +LL - let _ = ++i + i; +LL + let _ = { i += 1; i } + i; + | error: Rust has no prefix increment operator --> $DIR/issue-104867-inc-dec-2.rs:13:13 @@ -28,8 +30,9 @@ LL | let _ = ++i + ++i; | help: use `+= 1` instead | -LL | let _ = { i += 1; i } + ++i; - | ~ +++++++++ +LL - let _ = ++i + ++i; +LL + let _ = { i += 1; i } + ++i; + | error: Rust has no postfix increment operator --> $DIR/issue-104867-inc-dec-2.rs:18:18 @@ -45,8 +48,9 @@ LL | let _ = i++ + i; | help: use `+= 1` instead | -LL | let _ = { let tmp = i; i += 1; tmp } + i; - | +++++++++++ ~~~~~~~~~~~~~~~ +LL - let _ = i++ + i; +LL + let _ = { let tmp = i; i += 1; tmp } + i; + | error: Rust has no postfix increment operator --> $DIR/issue-104867-inc-dec-2.rs:29:14 @@ -56,8 +60,9 @@ LL | let _ = i++ + i++; | help: use `+= 1` instead | -LL | let _ = { let tmp = i; i += 1; tmp } + i++; - | +++++++++++ ~~~~~~~~~~~~~~~ +LL - let _ = i++ + i++; +LL + let _ = { let tmp = i; i += 1; tmp } + i++; + | error: Rust has no prefix increment operator --> $DIR/issue-104867-inc-dec-2.rs:34:13 @@ -67,8 +72,9 @@ LL | let _ = ++i + i++; | help: use `+= 1` instead | -LL | let _ = { i += 1; i } + i++; - | ~ +++++++++ +LL - let _ = ++i + i++; +LL + let _ = { i += 1; i } + i++; + | error: Rust has no postfix increment operator --> $DIR/issue-104867-inc-dec-2.rs:39:14 @@ -78,8 +84,9 @@ LL | let _ = i++ + ++i; | help: use `+= 1` instead | -LL | let _ = { let tmp = i; i += 1; tmp } + ++i; - | +++++++++++ ~~~~~~~~~~~~~~~ +LL - let _ = i++ + ++i; +LL + let _ = { let tmp = i; i += 1; tmp } + ++i; + | error: Rust has no postfix increment operator --> $DIR/issue-104867-inc-dec-2.rs:44:24 @@ -89,8 +96,9 @@ LL | let _ = (1 + 2 + i)++; | help: use `+= 1` instead | -LL | let _ = { let tmp = (1 + 2 + i); (1 + 2 + i) += 1; tmp }; - | +++++++++++ ~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - let _ = (1 + 2 + i)++; +LL + let _ = { let tmp = (1 + 2 + i); (1 + 2 + i) += 1; tmp }; + | error: Rust has no postfix increment operator --> $DIR/issue-104867-inc-dec-2.rs:49:15 @@ -100,8 +108,9 @@ LL | let _ = (i++ + 1) + 2; | help: use `+= 1` instead | -LL | let _ = ({ let tmp = i; i += 1; tmp } + 1) + 2; - | +++++++++++ ~~~~~~~~~~~~~~~ +LL - let _ = (i++ + 1) + 2; +LL + let _ = ({ let tmp = i; i += 1; tmp } + 1) + 2; + | error: aborting due to 10 previous errors diff --git a/tests/ui/parser/issues/issue-104867-inc-dec.stderr b/tests/ui/parser/issues/issue-104867-inc-dec.stderr index 78bfd3e82f0de..309f8b5693374 100644 --- a/tests/ui/parser/issues/issue-104867-inc-dec.stderr +++ b/tests/ui/parser/issues/issue-104867-inc-dec.stderr @@ -6,8 +6,9 @@ LL | i++; | help: use `+= 1` instead | -LL | i += 1; - | ~~~~ +LL - i++; +LL + i += 1; + | error: Rust has no postfix increment operator --> $DIR/issue-104867-inc-dec.rs:12:8 @@ -17,8 +18,9 @@ LL | s.x++; | help: use `+= 1` instead | -LL | s.x += 1; - | ~~~~ +LL - s.x++; +LL + s.x += 1; + | error: Rust has no postfix increment operator --> $DIR/issue-104867-inc-dec.rs:17:9 @@ -28,8 +30,9 @@ LL | if i++ == 1 {} | help: use `+= 1` instead | -LL | if { let tmp = i; i += 1; tmp } == 1 {} - | +++++++++++ ~~~~~~~~~~~~~~~ +LL - if i++ == 1 {} +LL + if { let tmp = i; i += 1; tmp } == 1 {} + | error: Rust has no prefix increment operator --> $DIR/issue-104867-inc-dec.rs:22:5 @@ -51,8 +54,9 @@ LL | if ++i == 1 { } | help: use `+= 1` instead | -LL | if { i += 1; i } == 1 { } - | ~ +++++++++ +LL - if ++i == 1 { } +LL + if { i += 1; i } == 1 { } + | error: Rust has no postfix increment operator --> $DIR/issue-104867-inc-dec.rs:33:6 @@ -62,8 +66,9 @@ LL | i++; | help: use `+= 1` instead | -LL | i += 1; - | ~~~~ +LL - i++; +LL + i += 1; + | error: Rust has no prefix increment operator --> $DIR/issue-104867-inc-dec.rs:41:5 diff --git a/tests/ui/parser/issues/issue-105366.stderr b/tests/ui/parser/issues/issue-105366.stderr index 18c04dfaf2088..d8c79a0e0eaf6 100644 --- a/tests/ui/parser/issues/issue-105366.stderr +++ b/tests/ui/parser/issues/issue-105366.stderr @@ -6,8 +6,9 @@ LL | fn From for Foo { | help: replace `fn` with `impl` here | -LL | impl From for Foo { - | ~~~~ +LL - fn From for Foo { +LL + impl From for Foo { + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-108495-dec.stderr b/tests/ui/parser/issues/issue-108495-dec.stderr index 85b29038f7c78..b058dae4a6fdd 100644 --- a/tests/ui/parser/issues/issue-108495-dec.stderr +++ b/tests/ui/parser/issues/issue-108495-dec.stderr @@ -12,8 +12,9 @@ LL | let _ = i-- + i--; | help: use `-= 1` instead | -LL | let _ = { let tmp = i; i -= 1; tmp } + i--; - | +++++++++++ ~~~~~~~~~~~~~~~ +LL - let _ = i-- + i--; +LL + let _ = { let tmp = i; i -= 1; tmp } + i--; + | error: Rust has no postfix decrement operator --> $DIR/issue-108495-dec.rs:14:20 @@ -29,8 +30,9 @@ LL | let _ = i-- + --i; | help: use `-= 1` instead | -LL | let _ = { let tmp = i; i -= 1; tmp } + --i; - | +++++++++++ ~~~~~~~~~~~~~~~ +LL - let _ = i-- + --i; +LL + let _ = { let tmp = i; i -= 1; tmp } + --i; + | error: Rust has no postfix decrement operator --> $DIR/issue-108495-dec.rs:24:24 @@ -40,8 +42,9 @@ LL | let _ = (1 + 2 + i)--; | help: use `-= 1` instead | -LL | let _ = { let tmp = (1 + 2 + i); (1 + 2 + i) -= 1; tmp }; - | +++++++++++ ~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - let _ = (1 + 2 + i)--; +LL + let _ = { let tmp = (1 + 2 + i); (1 + 2 + i) -= 1; tmp }; + | error: Rust has no postfix decrement operator --> $DIR/issue-108495-dec.rs:29:15 @@ -51,8 +54,9 @@ LL | let _ = (i-- + 1) + 2; | help: use `-= 1` instead | -LL | let _ = ({ let tmp = i; i -= 1; tmp } + 1) + 2; - | +++++++++++ ~~~~~~~~~~~~~~~ +LL - let _ = (i-- + 1) + 2; +LL + let _ = ({ let tmp = i; i -= 1; tmp } + 1) + 2; + | error: Rust has no postfix decrement operator --> $DIR/issue-108495-dec.rs:35:10 @@ -62,8 +66,9 @@ LL | i--; | help: use `-= 1` instead | -LL | i -= 1; - | ~~~~ +LL - i--; +LL + i -= 1; + | error: aborting due to 7 previous errors diff --git a/tests/ui/parser/issues/issue-110014.stderr b/tests/ui/parser/issues/issue-110014.stderr index 7f1dd592e1238..57420bb349605 100644 --- a/tests/ui/parser/issues/issue-110014.stderr +++ b/tests/ui/parser/issues/issue-110014.stderr @@ -6,8 +6,9 @@ LL | fn`2222222222222222222222222222222222222222() {} | help: Unicode character '`' (Grave Accent) looks like ''' (Single Quote), but it is not | -LL | fn'2222222222222222222222222222222222222222() {} - | ~ +LL - fn`2222222222222222222222222222222222222222() {} +LL + fn'2222222222222222222222222222222222222222() {} + | error: expected identifier, found `2222222222222222222222222222222222222222` --> $DIR/issue-110014.rs:1:4 diff --git a/tests/ui/parser/issues/issue-111416.stderr b/tests/ui/parser/issues/issue-111416.stderr index 36f6c5b018fe8..50ff209afffd3 100644 --- a/tests/ui/parser/issues/issue-111416.stderr +++ b/tests/ui/parser/issues/issue-111416.stderr @@ -6,8 +6,9 @@ LL | let my = monad_bind(mx, T: Try); | help: if `monad_bind` is a struct, use braces as delimiters | -LL | let my = monad_bind { mx, T: Try }; - | ~ ~ +LL - let my = monad_bind(mx, T: Try); +LL + let my = monad_bind { mx, T: Try }; + | help: if `monad_bind` is a function, use the arguments directly | LL - let my = monad_bind(mx, T: Try); diff --git a/tests/ui/parser/issues/issue-118530-ice.stderr b/tests/ui/parser/issues/issue-118530-ice.stderr index 3519fb8777f4d..72c0397e9c95b 100644 --- a/tests/ui/parser/issues/issue-118530-ice.stderr +++ b/tests/ui/parser/issues/issue-118530-ice.stderr @@ -42,8 +42,9 @@ LL | attr::fn bar() -> String { = help: the `.` operator will dereference the value if needed help: try using `.` instead | -LL | attr::fn bar() . String { - | ~ +LL - attr::fn bar() -> String { +LL + attr::fn bar() . String { + | error: expected one of `(`, `.`, `::`, `;`, `?`, `}`, or an operator, found `{` --> $DIR/issue-118530-ice.rs:5:30 diff --git a/tests/ui/parser/issues/issue-14303-fncall.full.stderr b/tests/ui/parser/issues/issue-14303-fncall.full.stderr index 1986f70bf672d..5a017c85c164f 100644 --- a/tests/ui/parser/issues/issue-14303-fncall.full.stderr +++ b/tests/ui/parser/issues/issue-14303-fncall.full.stderr @@ -1,8 +1,8 @@ -error[E0747]: type provided when a lifetime was expected - --> $DIR/issue-14303-fncall.rs:15:26 +error[E0747]: placeholder provided when a lifetime was expected + --> $DIR/issue-14303-fncall.rs:12:77 | -LL | .collect::>>(); - | ^ +LL | let _x = (*start..*end).map(|x| S { a: start, b: end }).collect::>>(); + | ^ error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-14303-fncall.generic_arg.stderr b/tests/ui/parser/issues/issue-14303-fncall.generic_arg.stderr index 2de59b8c746c2..5a017c85c164f 100644 --- a/tests/ui/parser/issues/issue-14303-fncall.generic_arg.stderr +++ b/tests/ui/parser/issues/issue-14303-fncall.generic_arg.stderr @@ -1,8 +1,8 @@ -error[E0747]: inferred provided when a lifetime was expected - --> $DIR/issue-14303-fncall.rs:15:26 +error[E0747]: placeholder provided when a lifetime was expected + --> $DIR/issue-14303-fncall.rs:12:77 | -LL | .collect::>>(); - | ^ +LL | let _x = (*start..*end).map(|x| S { a: start, b: end }).collect::>>(); + | ^ error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-14303-fncall.rs b/tests/ui/parser/issues/issue-14303-fncall.rs index 59d4eab06d6ff..8f7fbec947067 100644 --- a/tests/ui/parser/issues/issue-14303-fncall.rs +++ b/tests/ui/parser/issues/issue-14303-fncall.rs @@ -3,18 +3,15 @@ // we need the above to avoid ast borrowck failure in recovered code #![cfg_attr(generic_arg, feature(generic_arg_infer))] - struct S<'a, T> { a: &'a T, b: &'a T, } fn foo<'a, 'b>(start: &'a usize, end: &'a usize) { - let _x = (*start..*end) - .map(|x| S { a: start, b: end }) - .collect::>>(); - //[generic_arg]~^ ERROR inferred provided when a lifetime was expected - //[full]~^^ ERROR type provided when a lifetime was expected + let _x = (*start..*end).map(|x| S { a: start, b: end }).collect::>>(); + //[generic_arg]~^ ERROR placeholder provided when a lifetime was expected + //[full]~^^ ERROR placeholder provided when a lifetime was expected } fn main() {} diff --git a/tests/ui/parser/issues/issue-17718-const-mut.stderr b/tests/ui/parser/issues/issue-17718-const-mut.stderr index 54b819c3cfb84..16eb773e7f6e3 100644 --- a/tests/ui/parser/issues/issue-17718-const-mut.stderr +++ b/tests/ui/parser/issues/issue-17718-const-mut.stderr @@ -6,7 +6,8 @@ LL | mut | help: you might want to declare a static instead | -LL | static +LL - const +LL + static | error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-23620-invalid-escapes.stderr b/tests/ui/parser/issues/issue-23620-invalid-escapes.stderr index 4a3743579e7a6..0cedc178001c6 100644 --- a/tests/ui/parser/issues/issue-23620-invalid-escapes.stderr +++ b/tests/ui/parser/issues/issue-23620-invalid-escapes.stderr @@ -90,8 +90,9 @@ LL | let _ = "\u8f"; | help: format of unicode escape sequences uses braces | -LL | let _ = "\u{8f}"; - | ~~~~~~ +LL - let _ = "\u8f"; +LL + let _ = "\u{8f}"; + | error: aborting due to 13 previous errors diff --git a/tests/ui/parser/issues/issue-24375.stderr b/tests/ui/parser/issues/issue-24375.stderr index 03cd33f18751c..2af57a52035c3 100644 --- a/tests/ui/parser/issues/issue-24375.stderr +++ b/tests/ui/parser/issues/issue-24375.stderr @@ -7,8 +7,9 @@ LL | tmp[0] => {} = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == tmp[0] => {} - | ~~~ ++++++++++++++++ +LL - tmp[0] => {} +LL + val if val == tmp[0] => {} + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = tmp[0]; diff --git a/tests/ui/parser/issues/issue-30318.stderr b/tests/ui/parser/issues/issue-30318.stderr index 56bc200db1d3a..cd03bd5fecc4a 100644 --- a/tests/ui/parser/issues/issue-30318.stderr +++ b/tests/ui/parser/issues/issue-30318.stderr @@ -9,8 +9,9 @@ LL | fn bar() { } | help: to annotate the function, change the doc comment from inner to outer style | -LL | /// Misplaced comment... - | ~ +LL - //! Misplaced comment... +LL + /// Misplaced comment... + | error: an inner attribute is not permitted in this context --> $DIR/issue-30318.rs:9:1 @@ -38,8 +39,9 @@ LL | fn bat() { } | help: to annotate the function, change the doc comment from inner to outer style | -LL | /** Misplaced comment... */ - | ~ +LL - /*! Misplaced comment... */ +LL + /** Misplaced comment... */ + | error[E0753]: expected outer doc comment --> $DIR/issue-30318.rs:19:1 diff --git a/tests/ui/parser/issues/issue-32214.stderr b/tests/ui/parser/issues/issue-32214.stderr index 2ef4305dfd0e8..5ccd651bb96ae 100644 --- a/tests/ui/parser/issues/issue-32214.stderr +++ b/tests/ui/parser/issues/issue-32214.stderr @@ -8,8 +8,9 @@ LL | pub fn test >() {} | help: move the constraint after the generic argument | -LL | pub fn test >() {} - | ~~~~~~~~~~~~~~ +LL - pub fn test >() {} +LL + pub fn test >() {} + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-34255-1.stderr b/tests/ui/parser/issues/issue-34255-1.stderr index 1e72f040b0366..cd2baaee408c8 100644 --- a/tests/ui/parser/issues/issue-34255-1.stderr +++ b/tests/ui/parser/issues/issue-34255-1.stderr @@ -6,8 +6,9 @@ LL | Test::Drill(field: 42); | help: if `Test::Drill` is a struct, use braces as delimiters | -LL | Test::Drill { field: 42 }; - | ~ ~ +LL - Test::Drill(field: 42); +LL + Test::Drill { field: 42 }; + | help: if `Test::Drill` is a function, use the arguments directly | LL - Test::Drill(field: 42); diff --git a/tests/ui/parser/issues/issue-44406.stderr b/tests/ui/parser/issues/issue-44406.stderr index 78cde9b6dcacc..b2367ce15ea8a 100644 --- a/tests/ui/parser/issues/issue-44406.stderr +++ b/tests/ui/parser/issues/issue-44406.stderr @@ -10,8 +10,9 @@ LL | foo!(true); = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) help: if `bar` is a struct, use braces as delimiters | -LL | bar { baz: $rest } - | ~ ~ +LL - bar(baz: $rest) +LL + bar { baz: $rest } + | help: if `bar` is a function, use the arguments directly | LL - bar(baz: $rest) diff --git a/tests/ui/parser/issues/issue-57684.stderr b/tests/ui/parser/issues/issue-57684.stderr index 39e1c8cd7cc74..5fc55efff0a30 100644 --- a/tests/ui/parser/issues/issue-57684.stderr +++ b/tests/ui/parser/issues/issue-57684.stderr @@ -6,8 +6,9 @@ LL | let _ = X { f1 = 5 }; | help: replace equals symbol with a colon | -LL | let _ = X { f1: 5 }; - | ~ +LL - let _ = X { f1 = 5 }; +LL + let _ = X { f1: 5 }; + | error: expected `:`, found `=` --> $DIR/issue-57684.rs:32:12 @@ -17,8 +18,9 @@ LL | f1 = 5, | help: replace equals symbol with a colon | -LL | f1: 5, - | ~ +LL - f1 = 5, +LL + f1: 5, + | error: aborting due to 2 previous errors diff --git a/tests/ui/parser/issues/issue-5806.rs b/tests/ui/parser/issues/issue-5806.rs index dbd53a7adc433..1a819e22197fe 100644 --- a/tests/ui/parser/issues/issue-5806.rs +++ b/tests/ui/parser/issues/issue-5806.rs @@ -1,4 +1,4 @@ -//@ normalize-stderr: "parser:.*\(" -> "parser: $$ACCESS_DENIED_MSG (" +//@ normalize-stderr: "parser`:.*\(" -> "parser`: $$ACCESS_DENIED_MSG (" //@ normalize-stderr: "os error \d+" -> "os error $$ACCESS_DENIED_CODE" #[path = "../parser"] diff --git a/tests/ui/parser/issues/issue-5806.stderr b/tests/ui/parser/issues/issue-5806.stderr index 4b025bd19a048..88cc982baf259 100644 --- a/tests/ui/parser/issues/issue-5806.stderr +++ b/tests/ui/parser/issues/issue-5806.stderr @@ -1,4 +1,4 @@ -error: couldn't read $DIR/../parser: $ACCESS_DENIED_MSG (os error $ACCESS_DENIED_CODE) +error: couldn't read `$DIR/../parser`: $ACCESS_DENIED_MSG (os error $ACCESS_DENIED_CODE) --> $DIR/issue-5806.rs:5:1 | LL | mod foo; diff --git a/tests/ui/parser/issues/issue-64732.stderr b/tests/ui/parser/issues/issue-64732.stderr index 7ec2df6d3bf7e..d9f8091d2df41 100644 --- a/tests/ui/parser/issues/issue-64732.stderr +++ b/tests/ui/parser/issues/issue-64732.stderr @@ -6,8 +6,9 @@ LL | let _foo = b'hello\0'; | help: if you meant to write a byte string literal, use double quotes | -LL | let _foo = b"hello\0"; - | ~~ ~ +LL - let _foo = b'hello\0'; +LL + let _foo = b"hello\0"; + | error: character literal may only contain one codepoint --> $DIR/issue-64732.rs:6:16 @@ -17,8 +18,9 @@ LL | let _bar = 'hello'; | help: if you meant to write a string literal, use double quotes | -LL | let _bar = "hello"; - | ~ ~ +LL - let _bar = 'hello'; +LL + let _bar = "hello"; + | error: aborting due to 2 previous errors diff --git a/tests/ui/parser/issues/issue-65122-mac-invoc-in-mut-patterns.stderr b/tests/ui/parser/issues/issue-65122-mac-invoc-in-mut-patterns.stderr index 76259b40a9332..dda37d83282c2 100644 --- a/tests/ui/parser/issues/issue-65122-mac-invoc-in-mut-patterns.stderr +++ b/tests/ui/parser/issues/issue-65122-mac-invoc-in-mut-patterns.stderr @@ -30,7 +30,7 @@ error: `mut` must be followed by a named binding --> $DIR/issue-65122-mac-invoc-in-mut-patterns.rs:13:13 | LL | let mut $eval = (); - | ^^^ + | ^^^^ ... LL | mac2! { does_not_exist!() } | --------------------------- in this macro invocation @@ -40,7 +40,7 @@ LL | mac2! { does_not_exist!() } help: remove the `mut` prefix | LL - let mut $eval = (); -LL + let $eval = (); +LL + let $eval = (); | error: cannot find macro `does_not_exist` in this scope diff --git a/tests/ui/parser/issues/issue-65257-invalid-var-decl-recovery.stderr b/tests/ui/parser/issues/issue-65257-invalid-var-decl-recovery.stderr index 49d091cf3914c..7146949ca2f8f 100644 --- a/tests/ui/parser/issues/issue-65257-invalid-var-decl-recovery.stderr +++ b/tests/ui/parser/issues/issue-65257-invalid-var-decl-recovery.stderr @@ -6,8 +6,9 @@ LL | auto n = 0; | help: write `let` instead of `auto` to introduce a new variable | -LL | let n = 0; - | ~~~ +LL - auto n = 0; +LL + let n = 0; + | error: invalid variable declaration --> $DIR/issue-65257-invalid-var-decl-recovery.rs:4:5 @@ -17,8 +18,9 @@ LL | auto m; | help: write `let` instead of `auto` to introduce a new variable | -LL | let m; - | ~~~ +LL - auto m; +LL + let m; + | error: invalid variable declaration --> $DIR/issue-65257-invalid-var-decl-recovery.rs:8:5 @@ -28,8 +30,9 @@ LL | var n = 0; | help: write `let` instead of `var` to introduce a new variable | -LL | let n = 0; - | ~~~ +LL - var n = 0; +LL + let n = 0; + | error: invalid variable declaration --> $DIR/issue-65257-invalid-var-decl-recovery.rs:10:5 @@ -39,8 +42,9 @@ LL | var m; | help: write `let` instead of `var` to introduce a new variable | -LL | let m; - | ~~~ +LL - var m; +LL + let m; + | error: invalid variable declaration --> $DIR/issue-65257-invalid-var-decl-recovery.rs:14:5 @@ -51,7 +55,7 @@ LL | mut n = 0; help: missing keyword | LL | let mut n = 0; - | ~~~~~~~ + | +++ error: invalid variable declaration --> $DIR/issue-65257-invalid-var-decl-recovery.rs:16:5 @@ -62,7 +66,7 @@ LL | mut var; help: missing keyword | LL | let mut var; - | ~~~~~~~ + | +++ error[E0308]: mismatched types --> $DIR/issue-65257-invalid-var-decl-recovery.rs:20:33 diff --git a/tests/ui/parser/issues/issue-68730.stderr b/tests/ui/parser/issues/issue-68730.stderr index 9bd98287db34a..838a6569bdc3f 100644 --- a/tests/ui/parser/issues/issue-68730.stderr +++ b/tests/ui/parser/issues/issue-68730.stderr @@ -23,8 +23,9 @@ LL | enum␀em␀˂˂ = note: character appears once more help: Unicode character '˂' (Modifier Letter Left Arrowhead) looks like '<' (Less-Than Sign), but it is not | -LL | enum␀em␀<< - | ~~ +LL - enum␀em␀˂˂ +LL + enum␀em␀<< + | error: unknown start of token: \u{2c2} --> $DIR/issue-68730.rs:5:10 @@ -34,8 +35,9 @@ LL | enum␀em␀˂˂ | help: Unicode character '˂' (Modifier Letter Left Arrowhead) looks like '<' (Less-Than Sign), but it is not | -LL | enum␀em␀˂< - | ~ +LL - enum␀em␀˂˂ +LL + enum␀em␀˂< + | error: expected one of `#`, `>`, `const`, identifier, or lifetime, found `<` --> $DIR/issue-68730.rs:5:10 diff --git a/tests/ui/parser/issues/issue-70549-resolve-after-recovered-self-ctor.stderr b/tests/ui/parser/issues/issue-70549-resolve-after-recovered-self-ctor.stderr index c2c0faa21d151..9c632a8332a19 100644 --- a/tests/ui/parser/issues/issue-70549-resolve-after-recovered-self-ctor.stderr +++ b/tests/ui/parser/issues/issue-70549-resolve-after-recovered-self-ctor.stderr @@ -12,8 +12,9 @@ LL | fn foo(&mur Self) {} | help: there is a keyword `mut` with a similar name | -LL | fn foo(&mut Self) {} - | ~~~ +LL - fn foo(&mur Self) {} +LL + fn foo(&mut Self) {} + | error: unexpected lifetime `'static` in pattern --> $DIR/issue-70549-resolve-after-recovered-self-ctor.rs:8:13 @@ -41,8 +42,9 @@ LL | fn bar(&'static mur Self) {} | help: there is a keyword `mut` with a similar name | -LL | fn bar(&'static mut Self) {} - | ~~~ +LL - fn bar(&'static mur Self) {} +LL + fn bar(&'static mut Self) {} + | error: expected one of `:`, `@`, or `|`, found keyword `Self` --> $DIR/issue-70549-resolve-after-recovered-self-ctor.rs:14:17 @@ -52,8 +54,9 @@ LL | fn baz(&mur Self @ _) {} | help: there is a keyword `mut` with a similar name | -LL | fn baz(&mut Self @ _) {} - | ~~~ +LL - fn baz(&mur Self @ _) {} +LL + fn baz(&mut Self @ _) {} + | error[E0533]: expected unit struct, found self constructor `Self` --> $DIR/issue-70549-resolve-after-recovered-self-ctor.rs:4:17 diff --git a/tests/ui/parser/issues/issue-73568-lifetime-after-mut.stderr b/tests/ui/parser/issues/issue-73568-lifetime-after-mut.stderr index 2f8728bd78b4f..6b8f8e4fe4ee3 100644 --- a/tests/ui/parser/issues/issue-73568-lifetime-after-mut.stderr +++ b/tests/ui/parser/issues/issue-73568-lifetime-after-mut.stderr @@ -6,8 +6,9 @@ LL | fn x<'a>(x: &mut 'a i32){} | help: place the lifetime before `mut` | -LL | fn x<'a>(x: &'a mut i32){} - | ~~~~~~~ +LL - fn x<'a>(x: &mut 'a i32){} +LL + fn x<'a>(x: &'a mut i32){} + | error[E0178]: expected a path on the left-hand side of `+`, not `&mut 'a` --> $DIR/issue-73568-lifetime-after-mut.rs:14:13 @@ -32,8 +33,9 @@ LL | mac!('a); = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) help: place the lifetime before `mut` | -LL | fn w<$lt>(w: &$lt mut i32) {} - | ~~~~~~~~ +LL - fn w<$lt>(w: &mut $lt i32) {} +LL + fn w<$lt>(w: &$lt mut i32) {} + | error[E0423]: expected value, found trait `Send` --> $DIR/issue-73568-lifetime-after-mut.rs:17:28 diff --git a/tests/ui/parser/issues/issue-84117.stderr b/tests/ui/parser/issues/issue-84117.stderr index 9f603b84434bb..e358bc4a2fb97 100644 --- a/tests/ui/parser/issues/issue-84117.stderr +++ b/tests/ui/parser/issues/issue-84117.stderr @@ -12,8 +12,9 @@ LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str>, } | + help: use `=` if you meant to assign | -LL | let outer_local:e_outer<&str, { let inner_local =e_inner<&str, } - | ~ +LL - let outer_local:e_outer<&str, { let inner_local:e_inner<&str, } +LL + let outer_local:e_outer<&str, { let inner_local =e_inner<&str, } + | error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,` --> $DIR/issue-84117.rs:2:65 @@ -36,8 +37,9 @@ LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str, }> | + help: use `=` if you meant to assign | -LL | let outer_local =e_outer<&str, { let inner_local:e_inner<&str, } - | ~ +LL - let outer_local:e_outer<&str, { let inner_local:e_inner<&str, } +LL + let outer_local =e_outer<&str, { let inner_local:e_inner<&str, } + | error: expected one of `>`, a const expression, lifetime, or type, found `}` --> $DIR/issue-84117.rs:2:67 @@ -54,8 +56,9 @@ LL | let outer_local:e_outer<&str, { let inner_local:e_inner<&str>, } | + help: use `=` if you meant to assign | -LL | let outer_local:e_outer<&str, { let inner_local =e_inner<&str, } - | ~ +LL - let outer_local:e_outer<&str, { let inner_local:e_inner<&str, } +LL + let outer_local:e_outer<&str, { let inner_local =e_inner<&str, } + | error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,` --> $DIR/issue-84117.rs:2:65 diff --git a/tests/ui/parser/issues/issue-84148-1.stderr b/tests/ui/parser/issues/issue-84148-1.stderr index 9261067c22158..ebe9807dfe362 100644 --- a/tests/ui/parser/issues/issue-84148-1.stderr +++ b/tests/ui/parser/issues/issue-84148-1.stderr @@ -6,8 +6,9 @@ LL | fn f(t:for<>t?) | help: if you meant to express that the type might not contain a value, use the `Option` wrapper type | -LL | fn f(t:Optiont>) - | +++++++ ~ +LL - fn f(t:for<>t?) +LL + fn f(t:Optiont>) + | error: expected one of `->`, `where`, or `{`, found `` --> $DIR/issue-84148-1.rs:1:15 diff --git a/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr b/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr index b6e24faf5dabb..a9bad96f9af76 100644 --- a/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr +++ b/tests/ui/parser/issues/issue-87086-colon-path-sep.stderr @@ -9,7 +9,7 @@ LL | Foo:Bar => {} help: maybe write a path separator here | LL | Foo::Bar => {} - | ~~ + | + error: expected one of `!`, `(`, `...`, `..=`, `..`, `::`, `{`, or `|`, found `:` --> $DIR/issue-87086-colon-path-sep.rs:23:17 @@ -22,7 +22,7 @@ LL | qux::Foo:Bar => {} help: maybe write a path separator here | LL | qux::Foo::Bar => {} - | ~~ + | + error: expected one of `@` or `|`, found `:` --> $DIR/issue-87086-colon-path-sep.rs:29:12 @@ -35,7 +35,7 @@ LL | qux:Foo::Baz => {} help: maybe write a path separator here | LL | qux::Foo::Baz => {} - | ~~ + | + error: expected one of `@` or `|`, found `:` --> $DIR/issue-87086-colon-path-sep.rs:35:12 @@ -61,7 +61,7 @@ LL | if let Foo:Bar = f() { help: maybe write a path separator here | LL | if let Foo::Bar = f() { - | ~~ + | + error: expected one of `@` or `|`, found `:` --> $DIR/issue-87086-colon-path-sep.rs:49:16 @@ -100,7 +100,7 @@ LL | Foo:Bar::Baz => {} help: maybe write a path separator here | LL | Foo::Bar::Baz => {} - | ~~ + | + error: expected one of `@` or `|`, found `:` --> $DIR/issue-87086-colon-path-sep.rs:75:12 @@ -113,7 +113,7 @@ LL | Foo:Bar => {} help: maybe write a path separator here | LL | Foo::Bar => {} - | ~~ + | + warning: irrefutable `if let` pattern --> $DIR/issue-87086-colon-path-sep.rs:40:8 diff --git a/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe.rs b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe.rs index 34b687a0f5261..5ecf804de09fe 100644 --- a/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe.rs +++ b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe.rs @@ -1,4 +1,5 @@ //@ edition:2018 +#![allow(missing_abi)] // There is an order to respect for keywords before a function: // `, const, async, unsafe, extern, ""` diff --git a/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe.stderr b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe.stderr index 0e9f7c51e1a45..232da9acef309 100644 --- a/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe.stderr +++ b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe.stderr @@ -1,5 +1,5 @@ error: expected `fn`, found keyword `unsafe` - --> $DIR/wrong-unsafe.rs:9:8 + --> $DIR/wrong-unsafe.rs:10:8 | LL | extern unsafe fn test() {} | -------^^^^^^ diff --git a/tests/ui/parser/issues/issue-89574.rs b/tests/ui/parser/issues/issue-89574.rs index bafb0ce5e6680..276abfe71102b 100644 --- a/tests/ui/parser/issues/issue-89574.rs +++ b/tests/ui/parser/issues/issue-89574.rs @@ -2,5 +2,4 @@ fn main() { const EMPTY_ARRAY = []; //~^ missing type for `const` item //~| ERROR type annotations needed - //~| ERROR type annotations needed } diff --git a/tests/ui/parser/issues/issue-89574.stderr b/tests/ui/parser/issues/issue-89574.stderr index aa5e66b18a927..f40f5aded8ef1 100644 --- a/tests/ui/parser/issues/issue-89574.stderr +++ b/tests/ui/parser/issues/issue-89574.stderr @@ -15,14 +15,6 @@ help: provide a type for the item LL | const EMPTY_ARRAY: = []; | ++++++++ -error[E0282]: type annotations needed - --> $DIR/issue-89574.rs:2:25 - | -LL | const EMPTY_ARRAY = []; - | ^^ cannot infer type - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/parser/issues/issue-90993.stderr b/tests/ui/parser/issues/issue-90993.stderr index a18e93f1f1a98..e9ecc59ec4935 100644 --- a/tests/ui/parser/issues/issue-90993.stderr +++ b/tests/ui/parser/issues/issue-90993.stderr @@ -6,12 +6,14 @@ LL | ...=. | help: use `..` for an exclusive range | -LL | ..=. - | ~~ +LL - ...=. +LL + ..=. + | help: or `..=` for an inclusive range | -LL | ..==. - | ~~~ +LL - ...=. +LL + ..==. + | error: unexpected `=` after inclusive range --> $DIR/issue-90993.rs:2:5 @@ -22,8 +24,9 @@ LL | ...=. = note: inclusive ranges end with a single equals sign (`..=`) help: use `..=` instead | -LL | ..=. - | ~~~ +LL - ...=. +LL + ..=. + | error: expected one of `-`, `;`, `}`, or path, found `.` --> $DIR/issue-90993.rs:2:9 diff --git a/tests/ui/parser/issues/issue-99625-enum-struct-mutually-exclusive.stderr b/tests/ui/parser/issues/issue-99625-enum-struct-mutually-exclusive.stderr index c98b8fa1f1eeb..a4e0efcaeb0db 100644 --- a/tests/ui/parser/issues/issue-99625-enum-struct-mutually-exclusive.stderr +++ b/tests/ui/parser/issues/issue-99625-enum-struct-mutually-exclusive.stderr @@ -6,8 +6,9 @@ LL | pub enum struct Range { | help: replace `enum struct` with | -LL | pub enum Range { - | ~~~~ +LL - pub enum struct Range { +LL + pub enum Range { + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-99910-const-let-mutually-exclusive.stderr b/tests/ui/parser/issues/issue-99910-const-let-mutually-exclusive.stderr index 1ccf44a350d9e..73043de529029 100644 --- a/tests/ui/parser/issues/issue-99910-const-let-mutually-exclusive.stderr +++ b/tests/ui/parser/issues/issue-99910-const-let-mutually-exclusive.stderr @@ -6,8 +6,9 @@ LL | const let _FOO: i32 = 123; | help: remove `let` | -LL | const _FOO: i32 = 123; - | ~~~~~ +LL - const let _FOO: i32 = 123; +LL + const _FOO: i32 = 123; + | error: `const` and `let` are mutually exclusive --> $DIR/issue-99910-const-let-mutually-exclusive.rs:6:5 @@ -17,8 +18,9 @@ LL | let const _BAR: i32 = 123; | help: remove `let` | -LL | const _BAR: i32 = 123; - | ~~~~~ +LL - let const _BAR: i32 = 123; +LL + const _BAR: i32 = 123; + | error: aborting due to 2 previous errors diff --git a/tests/ui/parser/issues/recover-ge-as-fat-arrow.stderr b/tests/ui/parser/issues/recover-ge-as-fat-arrow.stderr index 2df5cca24f06c..997d080f1deed 100644 --- a/tests/ui/parser/issues/recover-ge-as-fat-arrow.stderr +++ b/tests/ui/parser/issues/recover-ge-as-fat-arrow.stderr @@ -17,8 +17,9 @@ LL | _ => { let _: u16 = 2u8; } | help: change the type of the numeric literal from `u8` to `u16` | -LL | _ => { let _: u16 = 2u16; } - | ~~~ +LL - _ => { let _: u16 = 2u8; } +LL + _ => { let _: u16 = 2u16; } + | error: aborting due to 2 previous errors diff --git a/tests/ui/parser/item-kw-case-mismatch.fixed b/tests/ui/parser/item-kw-case-mismatch.fixed index f5afa482712f6..4ee8f9c19dc27 100644 --- a/tests/ui/parser/item-kw-case-mismatch.fixed +++ b/tests/ui/parser/item-kw-case-mismatch.fixed @@ -25,7 +25,7 @@ const unsafe fn _e() {} //~| ERROR keyword `unsafe` is written in the wrong case //~| ERROR keyword `fn` is written in the wrong case -unsafe extern fn _f() {} +unsafe extern "C" fn _f() {} //~^ ERROR keyword `unsafe` is written in the wrong case //~| ERROR keyword `extern` is written in the wrong case diff --git a/tests/ui/parser/item-kw-case-mismatch.rs b/tests/ui/parser/item-kw-case-mismatch.rs index ea224e08a0085..6c858b848cf2c 100644 --- a/tests/ui/parser/item-kw-case-mismatch.rs +++ b/tests/ui/parser/item-kw-case-mismatch.rs @@ -25,7 +25,7 @@ CONST UNSAFE FN _e() {} //~| ERROR keyword `unsafe` is written in the wrong case //~| ERROR keyword `fn` is written in the wrong case -unSAFE EXTern fn _f() {} +unSAFE EXTern "C" fn _f() {} //~^ ERROR keyword `unsafe` is written in the wrong case //~| ERROR keyword `extern` is written in the wrong case diff --git a/tests/ui/parser/item-kw-case-mismatch.stderr b/tests/ui/parser/item-kw-case-mismatch.stderr index 0abc59e064a00..df39eb10fdbe1 100644 --- a/tests/ui/parser/item-kw-case-mismatch.stderr +++ b/tests/ui/parser/item-kw-case-mismatch.stderr @@ -6,8 +6,9 @@ LL | Use std::ptr::read; | help: write it in the correct case (notice the capitalization difference) | -LL | use std::ptr::read; - | ~~~ +LL - Use std::ptr::read; +LL + use std::ptr::read; + | error: keyword `use` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:8:1 @@ -17,8 +18,9 @@ LL | USE std::ptr::write; | help: write it in the correct case | -LL | use std::ptr::write; - | ~~~ +LL - USE std::ptr::write; +LL + use std::ptr::write; + | error: keyword `fn` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:10:7 @@ -28,8 +30,9 @@ LL | async Fn _a() {} | help: write it in the correct case (notice the capitalization difference) | -LL | async fn _a() {} - | ~~ +LL - async Fn _a() {} +LL + async fn _a() {} + | error: keyword `fn` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:13:1 @@ -39,8 +42,9 @@ LL | Fn _b() {} | help: write it in the correct case (notice the capitalization difference) | -LL | fn _b() {} - | ~~ +LL - Fn _b() {} +LL + fn _b() {} + | error: keyword `async` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:16:1 @@ -50,8 +54,9 @@ LL | aSYNC fN _c() {} | help: write it in the correct case | -LL | async fN _c() {} - | ~~~~~ +LL - aSYNC fN _c() {} +LL + async fN _c() {} + | error: keyword `fn` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:16:7 @@ -61,8 +66,9 @@ LL | aSYNC fN _c() {} | help: write it in the correct case | -LL | aSYNC fn _c() {} - | ~~ +LL - aSYNC fN _c() {} +LL + aSYNC fn _c() {} + | error: keyword `async` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:20:1 @@ -72,8 +78,9 @@ LL | Async fn _d() {} | help: write it in the correct case | -LL | async fn _d() {} - | ~~~~~ +LL - Async fn _d() {} +LL + async fn _d() {} + | error: keyword `const` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:23:1 @@ -83,8 +90,9 @@ LL | CONST UNSAFE FN _e() {} | help: write it in the correct case | -LL | const UNSAFE FN _e() {} - | ~~~~~ +LL - CONST UNSAFE FN _e() {} +LL + const UNSAFE FN _e() {} + | error: keyword `unsafe` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:23:7 @@ -94,8 +102,9 @@ LL | CONST UNSAFE FN _e() {} | help: write it in the correct case | -LL | CONST unsafe FN _e() {} - | ~~~~~~ +LL - CONST UNSAFE FN _e() {} +LL + CONST unsafe FN _e() {} + | error: keyword `fn` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:23:14 @@ -105,30 +114,33 @@ LL | CONST UNSAFE FN _e() {} | help: write it in the correct case | -LL | CONST UNSAFE fn _e() {} - | ~~ +LL - CONST UNSAFE FN _e() {} +LL + CONST UNSAFE fn _e() {} + | error: keyword `unsafe` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:28:1 | -LL | unSAFE EXTern fn _f() {} +LL | unSAFE EXTern "C" fn _f() {} | ^^^^^^ | help: write it in the correct case | -LL | unsafe EXTern fn _f() {} - | ~~~~~~ +LL - unSAFE EXTern "C" fn _f() {} +LL + unsafe EXTern "C" fn _f() {} + | error: keyword `extern` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:28:8 | -LL | unSAFE EXTern fn _f() {} +LL | unSAFE EXTern "C" fn _f() {} | ^^^^^^ | help: write it in the correct case | -LL | unSAFE extern fn _f() {} - | ~~~~~~ +LL - unSAFE EXTern "C" fn _f() {} +LL + unSAFE extern "C" fn _f() {} + | error: keyword `extern` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:32:1 @@ -138,8 +150,9 @@ LL | EXTERN "C" FN _g() {} | help: write it in the correct case | -LL | extern "C" FN _g() {} - | ~~~~~~ +LL - EXTERN "C" FN _g() {} +LL + extern "C" FN _g() {} + | error: keyword `fn` is written in the wrong case --> $DIR/item-kw-case-mismatch.rs:32:12 @@ -149,8 +162,9 @@ LL | EXTERN "C" FN _g() {} | help: write it in the correct case | -LL | EXTERN "C" fn _g() {} - | ~~ +LL - EXTERN "C" FN _g() {} +LL + EXTERN "C" fn _g() {} + | error: aborting due to 14 previous errors diff --git a/tests/ui/parser/kw-in-trait-bounds.stderr b/tests/ui/parser/kw-in-trait-bounds.stderr index 3c54e03195031..1892d0b62265a 100644 --- a/tests/ui/parser/kw-in-trait-bounds.stderr +++ b/tests/ui/parser/kw-in-trait-bounds.stderr @@ -6,8 +6,9 @@ LL | fn _f(_: impl fn(), _: &dyn fn()) | help: use `Fn` to refer to the trait (notice the capitalization difference) | -LL | fn _f(_: impl fn(), _: &dyn fn()) - | ~~ +LL - fn _f(_: impl fn(), _: &dyn fn()) +LL + fn _f(_: impl fn(), _: &dyn fn()) + | error: expected identifier, found keyword `fn` --> $DIR/kw-in-trait-bounds.rs:3:27 @@ -17,8 +18,9 @@ LL | fn _f(_: impl fn(), _: &dyn fn()) | help: use `Fn` to refer to the trait (notice the capitalization difference) | -LL | fn _f(_: impl Fn(), _: &dyn fn()) - | ~~ +LL - fn _f(_: impl fn(), _: &dyn fn()) +LL + fn _f(_: impl Fn(), _: &dyn fn()) + | error: expected identifier, found keyword `fn` --> $DIR/kw-in-trait-bounds.rs:3:41 @@ -28,8 +30,9 @@ LL | fn _f(_: impl fn(), _: &dyn fn()) | help: use `Fn` to refer to the trait (notice the capitalization difference) | -LL | fn _f(_: impl fn(), _: &dyn Fn()) - | ~~ +LL - fn _f(_: impl fn(), _: &dyn fn()) +LL + fn _f(_: impl fn(), _: &dyn Fn()) + | error: expected identifier, found keyword `fn` --> $DIR/kw-in-trait-bounds.rs:11:4 @@ -39,8 +42,9 @@ LL | G: fn(), | help: use `Fn` to refer to the trait (notice the capitalization difference) | -LL | G: Fn(), - | ~~ +LL - G: fn(), +LL + G: Fn(), + | error: expected identifier, found keyword `struct` --> $DIR/kw-in-trait-bounds.rs:16:10 diff --git a/tests/ui/parser/lifetime-in-pattern.stderr b/tests/ui/parser/lifetime-in-pattern.stderr index 55f9e56a42923..ffda28b202bda 100644 --- a/tests/ui/parser/lifetime-in-pattern.stderr +++ b/tests/ui/parser/lifetime-in-pattern.stderr @@ -23,8 +23,9 @@ LL | fn test(self: &'a str) { | +++++ help: if this is a parameter name, give it a type | -LL | fn test(str: &TypeName) { - | ~~~~~~~~~~~~~~ +LL - fn test(&'a str) { +LL + fn test(str: &TypeName) { + | help: if this is a type, explicitly ignore the parameter name | LL | fn test(_: &'a str) { diff --git a/tests/ui/parser/lifetime-semicolon.stderr b/tests/ui/parser/lifetime-semicolon.stderr index 4f8e2835e085b..f0e42c36c5a6a 100644 --- a/tests/ui/parser/lifetime-semicolon.stderr +++ b/tests/ui/parser/lifetime-semicolon.stderr @@ -6,8 +6,9 @@ LL | fn foo<'a, 'b>(_x: &mut Foo<'a; 'b>) {} | help: use a comma to separate type parameters | -LL | fn foo<'a, 'b>(_x: &mut Foo<'a, 'b>) {} - | ~ +LL - fn foo<'a, 'b>(_x: &mut Foo<'a; 'b>) {} +LL + fn foo<'a, 'b>(_x: &mut Foo<'a, 'b>) {} + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/lit-err-in-macro.rs b/tests/ui/parser/lit-err-in-macro.rs index cff8ee6b40ca6..ca117ac4a15d4 100644 --- a/tests/ui/parser/lit-err-in-macro.rs +++ b/tests/ui/parser/lit-err-in-macro.rs @@ -1,6 +1,6 @@ macro_rules! f { ($abi:literal) => { - extern $abi fn f() {} + extern $abi fn f() {} //~ WARN missing_abi } } diff --git a/tests/ui/parser/lit-err-in-macro.stderr b/tests/ui/parser/lit-err-in-macro.stderr index 12e6d51906030..9422f22f9c8fe 100644 --- a/tests/ui/parser/lit-err-in-macro.stderr +++ b/tests/ui/parser/lit-err-in-macro.stderr @@ -4,5 +4,17 @@ error: suffixes on string literals are invalid LL | f!("Foo"__); | ^^^^^^^ invalid suffix `__` -error: aborting due to 1 previous error +warning: extern declarations without an explicit ABI are deprecated + --> $DIR/lit-err-in-macro.rs:3:9 + | +LL | extern $abi fn f() {} + | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` +... +LL | f!("Foo"__); + | ----------- in this macro invocation + | + = note: `#[warn(missing_abi)]` on by default + = note: this warning originates in the macro `f` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error; 1 warning emitted diff --git a/tests/ui/parser/macros-no-semicolon-items.stderr b/tests/ui/parser/macros-no-semicolon-items.stderr index 6d2431c4aec47..07fa2439df504 100644 --- a/tests/ui/parser/macros-no-semicolon-items.stderr +++ b/tests/ui/parser/macros-no-semicolon-items.stderr @@ -6,8 +6,9 @@ LL | macro_rules! foo() | help: change the delimiters to curly braces | -LL | macro_rules! foo{} - | ~~ +LL - macro_rules! foo() +LL + macro_rules! foo{} + | help: add a semicolon | LL | macro_rules! foo(); diff --git a/tests/ui/parser/match-arm-without-body.stderr b/tests/ui/parser/match-arm-without-body.stderr index 53cf3480dbf94..9df8485972f33 100644 --- a/tests/ui/parser/match-arm-without-body.stderr +++ b/tests/ui/parser/match-arm-without-body.stderr @@ -18,7 +18,8 @@ LL | (Some(_),) | + + help: ...or a vertical bar to match on multiple alternatives | -LL | Some(_) | +LL - Some(_), +LL + Some(_) | | error: unexpected `,` in pattern diff --git a/tests/ui/parser/match-arm-without-braces.stderr b/tests/ui/parser/match-arm-without-braces.stderr index 4a4a154d86091..70cbdcfde0b5f 100644 --- a/tests/ui/parser/match-arm-without-braces.stderr +++ b/tests/ui/parser/match-arm-without-braces.stderr @@ -8,8 +8,9 @@ LL | Some(Val::Foo) => 3; | help: replace `;` with `,` to end a `match` arm expression | -LL | Some(Val::Foo) => 3, - | ~ +LL - Some(Val::Foo) => 3; +LL + Some(Val::Foo) => 3, + | error: `match` arm body without braces --> $DIR/match-arm-without-braces.rs:31:11 diff --git a/tests/ui/parser/missing-fn-issue-65381-2.stderr b/tests/ui/parser/missing-fn-issue-65381-2.stderr index e13d395d70d7f..17a25bc6671af 100644 --- a/tests/ui/parser/missing-fn-issue-65381-2.stderr +++ b/tests/ui/parser/missing-fn-issue-65381-2.stderr @@ -7,7 +7,7 @@ LL | main(); help: if you meant to call a macro, try | LL | main!(); - | ~~~~~ + | + error: aborting due to 1 previous error diff --git a/tests/ui/parser/misspelled-keywords/assoc-type.stderr b/tests/ui/parser/misspelled-keywords/assoc-type.stderr index 677da53e3400d..e529b477c05bc 100644 --- a/tests/ui/parser/misspelled-keywords/assoc-type.stderr +++ b/tests/ui/parser/misspelled-keywords/assoc-type.stderr @@ -11,8 +11,9 @@ LL | } | help: write keyword `type` in lowercase | -LL | type Result = u8; - | ~~~~ +LL - Type Result = u8; +LL + type Result = u8; + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/misspelled-keywords/async-move.stderr b/tests/ui/parser/misspelled-keywords/async-move.stderr index 4be4b56e5056a..a002d54dc9112 100644 --- a/tests/ui/parser/misspelled-keywords/async-move.stderr +++ b/tests/ui/parser/misspelled-keywords/async-move.stderr @@ -6,8 +6,9 @@ LL | async Move {} | help: write keyword `move` in lowercase | -LL | async move {} - | ~~~~ +LL - async Move {} +LL + async move {} + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/misspelled-keywords/const-fn.stderr b/tests/ui/parser/misspelled-keywords/const-fn.stderr index 5646b26143c40..46a6d8ca779fc 100644 --- a/tests/ui/parser/misspelled-keywords/const-fn.stderr +++ b/tests/ui/parser/misspelled-keywords/const-fn.stderr @@ -6,8 +6,9 @@ LL | cnst fn code() {} | help: there is a keyword `const` with a similar name | -LL | const fn code() {} - | ~~~~~ +LL - cnst fn code() {} +LL + const fn code() {} + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/misspelled-keywords/const-generics.stderr b/tests/ui/parser/misspelled-keywords/const-generics.stderr index fd59999ab635f..2d37656278e34 100644 --- a/tests/ui/parser/misspelled-keywords/const-generics.stderr +++ b/tests/ui/parser/misspelled-keywords/const-generics.stderr @@ -6,8 +6,9 @@ LL | fn foo(_arr: [i32; N]) {} | help: there is a keyword `const` with a similar name | -LL | fn foo(_arr: [i32; N]) {} - | ~~~~~ +LL - fn foo(_arr: [i32; N]) {} +LL + fn foo(_arr: [i32; N]) {} + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/misspelled-keywords/const.stderr b/tests/ui/parser/misspelled-keywords/const.stderr index 35e4d731db768..59346461ce775 100644 --- a/tests/ui/parser/misspelled-keywords/const.stderr +++ b/tests/ui/parser/misspelled-keywords/const.stderr @@ -7,7 +7,7 @@ LL | cons A: u8 = 10; help: there is a keyword `const` with a similar name | LL | const A: u8 = 10; - | ~~~~~ + | + error: aborting due to 1 previous error diff --git a/tests/ui/parser/misspelled-keywords/for-loop.stderr b/tests/ui/parser/misspelled-keywords/for-loop.stderr index d2236ab074da7..6b94e60452a3c 100644 --- a/tests/ui/parser/misspelled-keywords/for-loop.stderr +++ b/tests/ui/parser/misspelled-keywords/for-loop.stderr @@ -6,8 +6,9 @@ LL | form i in 1..10 {} | help: there is a keyword `for` with a similar name | -LL | for i in 1..10 {} - | ~~~ +LL - form i in 1..10 {} +LL + for i in 1..10 {} + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/misspelled-keywords/hrdt.stderr b/tests/ui/parser/misspelled-keywords/hrdt.stderr index 5393a730506d9..e5fc1a5038205 100644 --- a/tests/ui/parser/misspelled-keywords/hrdt.stderr +++ b/tests/ui/parser/misspelled-keywords/hrdt.stderr @@ -6,8 +6,9 @@ LL | Where for<'a> F: Fn(&'a (u8, u16)) -> &'a u8, | help: write keyword `where` in lowercase (notice the capitalization difference) | -LL | where for<'a> F: Fn(&'a (u8, u16)) -> &'a u8, - | ~~~~~ +LL - Where for<'a> F: Fn(&'a (u8, u16)) -> &'a u8, +LL + where for<'a> F: Fn(&'a (u8, u16)) -> &'a u8, + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/misspelled-keywords/impl-block.stderr b/tests/ui/parser/misspelled-keywords/impl-block.stderr index d86ae326ce2e3..8219ed6bfe94c 100644 --- a/tests/ui/parser/misspelled-keywords/impl-block.stderr +++ b/tests/ui/parser/misspelled-keywords/impl-block.stderr @@ -6,8 +6,9 @@ LL | ipml Human {} | help: there is a keyword `impl` with a similar name | -LL | impl Human {} - | ~~~~ +LL - ipml Human {} +LL + impl Human {} + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/misspelled-keywords/impl-return.stderr b/tests/ui/parser/misspelled-keywords/impl-return.stderr index 883f5cea73eff..ff5391461a9fb 100644 --- a/tests/ui/parser/misspelled-keywords/impl-return.stderr +++ b/tests/ui/parser/misspelled-keywords/impl-return.stderr @@ -6,8 +6,9 @@ LL | fn code() -> Impl Display {} | help: write keyword `impl` in lowercase (notice the capitalization difference) | -LL | fn code() -> impl Display {} - | ~~~~ +LL - fn code() -> Impl Display {} +LL + fn code() -> impl Display {} + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/misspelled-keywords/impl-trait-for.stderr b/tests/ui/parser/misspelled-keywords/impl-trait-for.stderr index 8dd5a4645f362..fe2356da4bdda 100644 --- a/tests/ui/parser/misspelled-keywords/impl-trait-for.stderr +++ b/tests/ui/parser/misspelled-keywords/impl-trait-for.stderr @@ -6,8 +6,9 @@ LL | impl Debug form Human {} | help: there is a keyword `for` with a similar name | -LL | impl Debug for Human {} - | ~~~ +LL - impl Debug form Human {} +LL + impl Debug for Human {} + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/misspelled-keywords/impl-trait.stderr b/tests/ui/parser/misspelled-keywords/impl-trait.stderr index 02a0c80831107..4b0c9222a8c90 100644 --- a/tests/ui/parser/misspelled-keywords/impl-trait.stderr +++ b/tests/ui/parser/misspelled-keywords/impl-trait.stderr @@ -6,8 +6,9 @@ LL | fn code() -> u8 {} | help: there is a keyword `impl` with a similar name | -LL | fn code() -> u8 {} - | ~~~~ +LL - fn code() -> u8 {} +LL + fn code() -> u8 {} + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/misspelled-keywords/let-else.stderr b/tests/ui/parser/misspelled-keywords/let-else.stderr index 6f41a0d99dba9..1dde67638ce02 100644 --- a/tests/ui/parser/misspelled-keywords/let-else.stderr +++ b/tests/ui/parser/misspelled-keywords/let-else.stderr @@ -6,8 +6,9 @@ LL | let Some(a) = Some(10) elze {} | help: there is a keyword `else` with a similar name | -LL | let Some(a) = Some(10) else {} - | ~~~~ +LL - let Some(a) = Some(10) elze {} +LL + let Some(a) = Some(10) else {} + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/misspelled-keywords/let-mut.stderr b/tests/ui/parser/misspelled-keywords/let-mut.stderr index 766d2a049090b..aee457e135ba4 100644 --- a/tests/ui/parser/misspelled-keywords/let-mut.stderr +++ b/tests/ui/parser/misspelled-keywords/let-mut.stderr @@ -6,8 +6,9 @@ LL | let muta a = 10; | help: there is a keyword `mut` with a similar name | -LL | let mut a = 10; - | ~~~ +LL - let muta a = 10; +LL + let mut a = 10; + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/misspelled-keywords/let.stderr b/tests/ui/parser/misspelled-keywords/let.stderr index c2dcdef541d81..9ebc4b5afa64a 100644 --- a/tests/ui/parser/misspelled-keywords/let.stderr +++ b/tests/ui/parser/misspelled-keywords/let.stderr @@ -6,8 +6,9 @@ LL | Let a = 10; | help: write keyword `let` in lowercase | -LL | let a = 10; - | ~~~ +LL - Let a = 10; +LL + let a = 10; + | error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `a` --> $DIR/let.rs:7:10 @@ -17,8 +18,9 @@ LL | lett a = 10; | help: there is a keyword `let` with a similar name | -LL | let a = 10; - | ~~~ +LL - lett a = 10; +LL + let a = 10; + | error: aborting due to 2 previous errors diff --git a/tests/ui/parser/misspelled-keywords/match.stderr b/tests/ui/parser/misspelled-keywords/match.stderr index 90780ebd38ef0..1ec8f7c3b81c8 100644 --- a/tests/ui/parser/misspelled-keywords/match.stderr +++ b/tests/ui/parser/misspelled-keywords/match.stderr @@ -6,8 +6,9 @@ LL | matche a {} | help: there is a keyword `match` with a similar name | -LL | match a {} - | ~~~~~ +LL - matche a {} +LL + match a {} + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/misspelled-keywords/mod.stderr b/tests/ui/parser/misspelled-keywords/mod.stderr index 6daeb4e5a152e..1c1866279ce5d 100644 --- a/tests/ui/parser/misspelled-keywords/mod.stderr +++ b/tests/ui/parser/misspelled-keywords/mod.stderr @@ -6,8 +6,9 @@ LL | mode parser; | help: there is a keyword `mod` with a similar name | -LL | mod parser; - | ~~~ +LL - mode parser; +LL + mod parser; + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/misspelled-keywords/pub-fn.stderr b/tests/ui/parser/misspelled-keywords/pub-fn.stderr index 82ca7105a4965..1123c652c0ee7 100644 --- a/tests/ui/parser/misspelled-keywords/pub-fn.stderr +++ b/tests/ui/parser/misspelled-keywords/pub-fn.stderr @@ -6,8 +6,9 @@ LL | puB fn code() {} | help: write keyword `pub` in lowercase | -LL | pub fn code() {} - | ~~~ +LL - puB fn code() {} +LL + pub fn code() {} + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/misspelled-keywords/ref.stderr b/tests/ui/parser/misspelled-keywords/ref.stderr index 398d9d6bb99b1..21b99d6e6638b 100644 --- a/tests/ui/parser/misspelled-keywords/ref.stderr +++ b/tests/ui/parser/misspelled-keywords/ref.stderr @@ -6,8 +6,9 @@ LL | Some(refe list) => println!("{list:?}"), | help: there is a keyword `ref` with a similar name | -LL | Some(ref list) => println!("{list:?}"), - | ~~~ +LL - Some(refe list) => println!("{list:?}"), +LL + Some(ref list) => println!("{list:?}"), + | error[E0023]: this pattern has 2 fields, but the corresponding tuple variant has 1 field --> $DIR/ref.rs:4:14 diff --git a/tests/ui/parser/misspelled-keywords/return.stderr b/tests/ui/parser/misspelled-keywords/return.stderr index efa45f3229909..21188ff8cb776 100644 --- a/tests/ui/parser/misspelled-keywords/return.stderr +++ b/tests/ui/parser/misspelled-keywords/return.stderr @@ -6,8 +6,9 @@ LL | returnn a; | help: there is a keyword `return` with a similar name | -LL | return a; - | ~~~~~~ +LL - returnn a; +LL + return a; + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/misspelled-keywords/static-mut.stderr b/tests/ui/parser/misspelled-keywords/static-mut.stderr index 3c25af548a3df..10cdfe1adc607 100644 --- a/tests/ui/parser/misspelled-keywords/static-mut.stderr +++ b/tests/ui/parser/misspelled-keywords/static-mut.stderr @@ -6,8 +6,9 @@ LL | static muta a: u8 = 0; | help: there is a keyword `mut` with a similar name | -LL | static mut a: u8 = 0; - | ~~~ +LL - static muta a: u8 = 0; +LL + static mut a: u8 = 0; + | error: missing type for `static` item --> $DIR/static-mut.rs:1:12 diff --git a/tests/ui/parser/misspelled-keywords/static.stderr b/tests/ui/parser/misspelled-keywords/static.stderr index 003aa3929bc22..e559f2be10915 100644 --- a/tests/ui/parser/misspelled-keywords/static.stderr +++ b/tests/ui/parser/misspelled-keywords/static.stderr @@ -6,8 +6,9 @@ LL | Static a = 0; | help: write keyword `static` in lowercase (notice the capitalization difference) | -LL | static a = 0; - | ~~~~~~ +LL - Static a = 0; +LL + static a = 0; + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/misspelled-keywords/struct.stderr b/tests/ui/parser/misspelled-keywords/struct.stderr index 559182f9c8f68..edbec3b9456b3 100644 --- a/tests/ui/parser/misspelled-keywords/struct.stderr +++ b/tests/ui/parser/misspelled-keywords/struct.stderr @@ -6,8 +6,9 @@ LL | Struct Foor { | help: write keyword `struct` in lowercase (notice the capitalization difference) | -LL | struct Foor { - | ~~~~~~ +LL - Struct Foor { +LL + struct Foor { + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/misspelled-keywords/unsafe-fn.stderr b/tests/ui/parser/misspelled-keywords/unsafe-fn.stderr index b13281b039511..679dbb6af3728 100644 --- a/tests/ui/parser/misspelled-keywords/unsafe-fn.stderr +++ b/tests/ui/parser/misspelled-keywords/unsafe-fn.stderr @@ -6,8 +6,9 @@ LL | unsafee fn code() {} | help: there is a keyword `unsafe` with a similar name | -LL | unsafe fn code() {} - | ~~~~~~ +LL - unsafee fn code() {} +LL + unsafe fn code() {} + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/misspelled-keywords/use.stderr b/tests/ui/parser/misspelled-keywords/use.stderr index db6dffdb613c5..40584c835b7aa 100644 --- a/tests/ui/parser/misspelled-keywords/use.stderr +++ b/tests/ui/parser/misspelled-keywords/use.stderr @@ -6,8 +6,9 @@ LL | usee a::b; | help: there is a keyword `use` with a similar name | -LL | use a::b; - | ~~~ +LL - usee a::b; +LL + use a::b; + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/misspelled-keywords/where-clause.stderr b/tests/ui/parser/misspelled-keywords/where-clause.stderr index 5143c30ca5103..f0835f6e7dabe 100644 --- a/tests/ui/parser/misspelled-keywords/where-clause.stderr +++ b/tests/ui/parser/misspelled-keywords/where-clause.stderr @@ -8,7 +8,8 @@ LL | wheree | help: there is a keyword `where` with a similar name | -LL | where +LL - wheree +LL + where | error: aborting due to 1 previous error diff --git a/tests/ui/parser/misspelled-keywords/while-loop.stderr b/tests/ui/parser/misspelled-keywords/while-loop.stderr index 7d150443f57e7..2d1136285c844 100644 --- a/tests/ui/parser/misspelled-keywords/while-loop.stderr +++ b/tests/ui/parser/misspelled-keywords/while-loop.stderr @@ -6,8 +6,9 @@ LL | whilee a < b { | help: there is a keyword `while` with a similar name | -LL | while a < b { - | ~~~~~ +LL - whilee a < b { +LL + while a < b { + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/mod_file_not_exist.rs b/tests/ui/parser/mod_file_not_exist.rs index 80a17163087c9..49ce44982ab98 100644 --- a/tests/ui/parser/mod_file_not_exist.rs +++ b/tests/ui/parser/mod_file_not_exist.rs @@ -1,9 +1,8 @@ -//@ ignore-windows - mod not_a_real_file; //~ ERROR file not found for module `not_a_real_file` //~^ HELP to create the module `not_a_real_file`, create file fn main() { assert_eq!(mod_file_aux::bar(), 10); - //~^ ERROR failed to resolve: use of undeclared crate or module `mod_file_aux` + //~^ ERROR failed to resolve: use of unresolved module or unlinked crate `mod_file_aux` + //~| HELP you might be missing a crate named `mod_file_aux` } diff --git a/tests/ui/parser/mod_file_not_exist.stderr b/tests/ui/parser/mod_file_not_exist.stderr index c2f9d30d9ec40..d9e4e8f31f5fe 100644 --- a/tests/ui/parser/mod_file_not_exist.stderr +++ b/tests/ui/parser/mod_file_not_exist.stderr @@ -1,5 +1,5 @@ error[E0583]: file not found for module `not_a_real_file` - --> $DIR/mod_file_not_exist.rs:3:1 + --> $DIR/mod_file_not_exist.rs:1:1 | LL | mod not_a_real_file; | ^^^^^^^^^^^^^^^^^^^^ @@ -7,11 +7,13 @@ LL | mod not_a_real_file; = help: to create the module `not_a_real_file`, create file "$DIR/not_a_real_file.rs" or "$DIR/not_a_real_file/mod.rs" = note: if there is a `mod not_a_real_file` elsewhere in the crate already, import it with `use crate::...` instead -error[E0433]: failed to resolve: use of undeclared crate or module `mod_file_aux` - --> $DIR/mod_file_not_exist.rs:7:16 +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `mod_file_aux` + --> $DIR/mod_file_not_exist.rs:5:16 | LL | assert_eq!(mod_file_aux::bar(), 10); - | ^^^^^^^^^^^^ use of undeclared crate or module `mod_file_aux` + | ^^^^^^^^^^^^ use of unresolved module or unlinked crate `mod_file_aux` + | + = help: you might be missing a crate named `mod_file_aux` error: aborting due to 2 previous errors diff --git a/tests/ui/parser/mod_file_not_exist_windows.rs b/tests/ui/parser/mod_file_not_exist_windows.rs index 88780c0c24e94..bb74684d99447 100644 --- a/tests/ui/parser/mod_file_not_exist_windows.rs +++ b/tests/ui/parser/mod_file_not_exist_windows.rs @@ -5,5 +5,6 @@ mod not_a_real_file; //~ ERROR file not found for module `not_a_real_file` fn main() { assert_eq!(mod_file_aux::bar(), 10); - //~^ ERROR failed to resolve: use of undeclared crate or module `mod_file_aux` + //~^ ERROR failed to resolve: use of unresolved module or unlinked crate `mod_file_aux` + //~| HELP you might be missing a crate named `mod_file_aux` } diff --git a/tests/ui/parser/mod_file_not_exist_windows.stderr b/tests/ui/parser/mod_file_not_exist_windows.stderr index 53b09d8ca53a0..03c762d0ef2dd 100644 --- a/tests/ui/parser/mod_file_not_exist_windows.stderr +++ b/tests/ui/parser/mod_file_not_exist_windows.stderr @@ -7,11 +7,13 @@ LL | mod not_a_real_file; = help: to create the module `not_a_real_file`, create file "$DIR/not_a_real_file.rs" or "$DIR/not_a_real_file/mod.rs" = note: if there is a `mod not_a_real_file` elsewhere in the crate already, import it with `use crate::...` instead -error[E0433]: failed to resolve: use of undeclared crate or module `mod_file_aux` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `mod_file_aux` --> $DIR/mod_file_not_exist_windows.rs:7:16 | LL | assert_eq!(mod_file_aux::bar(), 10); - | ^^^^^^^^^^^^ use of undeclared crate or module `mod_file_aux` + | ^^^^^^^^^^^^ use of unresolved module or unlinked crate `mod_file_aux` + | + = help: you might be missing a crate named `mod_file_aux` error: aborting due to 2 previous errors diff --git a/tests/ui/parser/mod_file_with_path_attr.rs b/tests/ui/parser/mod_file_with_path_attr.rs index ff964f750e296..b7f4a9c6ae02f 100644 --- a/tests/ui/parser/mod_file_with_path_attr.rs +++ b/tests/ui/parser/mod_file_with_path_attr.rs @@ -1,4 +1,4 @@ -//@ normalize-stderr: "not_a_real_file.rs:.*\(" -> "not_a_real_file.rs: $$FILE_NOT_FOUND_MSG (" +//@ normalize-stderr: "not_a_real_file.rs`:.*\(" -> "not_a_real_file.rs`: $$FILE_NOT_FOUND_MSG (" #[path = "not_a_real_file.rs"] mod m; //~ ERROR not_a_real_file.rs diff --git a/tests/ui/parser/mod_file_with_path_attr.stderr b/tests/ui/parser/mod_file_with_path_attr.stderr index 9ccb775daab13..ef8a715712bbd 100644 --- a/tests/ui/parser/mod_file_with_path_attr.stderr +++ b/tests/ui/parser/mod_file_with_path_attr.stderr @@ -1,4 +1,4 @@ -error: couldn't read $DIR/not_a_real_file.rs: $FILE_NOT_FOUND_MSG (os error 2) +error: couldn't read `$DIR/not_a_real_file.rs`: $FILE_NOT_FOUND_MSG (os error 2) --> $DIR/mod_file_with_path_attr.rs:4:1 | LL | mod m; diff --git a/tests/ui/parser/mut-patterns.stderr b/tests/ui/parser/mut-patterns.stderr index ad19a60af34ee..43f6a344bf392 100644 --- a/tests/ui/parser/mut-patterns.stderr +++ b/tests/ui/parser/mut-patterns.stderr @@ -33,8 +33,9 @@ LL | let mut (x @ y) = 0; = note: `mut` may be followed by `variable` and `variable @ pattern` help: add `mut` to each binding | -LL | let (mut x @ mut y) = 0; - | ~~~~~~~~~~~~~~~ +LL - let mut (x @ y) = 0; +LL + let (mut x @ mut y) = 0; + | error: `mut` on a binding may not be repeated --> $DIR/mut-patterns.rs:14:13 @@ -69,8 +70,9 @@ LL | let mut Foo { x: x } = Foo { x: 3 }; = note: `mut` may be followed by `variable` and `variable @ pattern` help: add `mut` to each binding | -LL | let Foo { x: mut x } = Foo { x: 3 }; - | ~~~~~~~~~~~~~~~~ +LL - let mut Foo { x: x } = Foo { x: 3 }; +LL + let Foo { x: mut x } = Foo { x: 3 }; + | error: `mut` must be attached to each individual binding --> $DIR/mut-patterns.rs:27:9 @@ -81,8 +83,9 @@ LL | let mut Foo { x } = Foo { x: 3 }; = note: `mut` may be followed by `variable` and `variable @ pattern` help: add `mut` to each binding | -LL | let Foo { mut x } = Foo { x: 3 }; - | ~~~~~~~~~~~~~ +LL - let mut Foo { x } = Foo { x: 3 }; +LL + let Foo { mut x } = Foo { x: 3 }; + | error: `mut` on a binding may not be repeated --> $DIR/mut-patterns.rs:32:13 @@ -151,8 +154,9 @@ LL | let mut W(mut a, W(b, W(ref c, W(d, B { box f })))) = note: `mut` may be followed by `variable` and `variable @ pattern` help: add `mut` to each binding | -LL | let W(mut a, W(mut b, W(ref c, W(mut d, B { box mut f })))) - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - let mut W(mut a, W(b, W(ref c, W(d, B { box f })))) +LL + let W(mut a, W(mut b, W(ref c, W(mut d, B { box mut f })))) + | error: expected identifier, found `x` --> $DIR/mut-patterns.rs:48:21 diff --git a/tests/ui/parser/not-a-pred.stderr b/tests/ui/parser/not-a-pred.stderr index 6f6a332cb8155..9fedd208e1afe 100644 --- a/tests/ui/parser/not-a-pred.stderr +++ b/tests/ui/parser/not-a-pred.stderr @@ -6,8 +6,9 @@ LL | fn f(a: isize, b: isize) : lt(a, b) { } | help: use `->` instead | -LL | fn f(a: isize, b: isize) -> lt(a, b) { } - | ~~ +LL - fn f(a: isize, b: isize) : lt(a, b) { } +LL + fn f(a: isize, b: isize) -> lt(a, b) { } + | error[E0573]: expected type, found function `lt` --> $DIR/not-a-pred.rs:1:28 diff --git a/tests/ui/parser/public-instead-of-pub-1.stderr b/tests/ui/parser/public-instead-of-pub-1.stderr index 3fbe8d0b164e8..5695624d3b6aa 100644 --- a/tests/ui/parser/public-instead-of-pub-1.stderr +++ b/tests/ui/parser/public-instead-of-pub-1.stderr @@ -6,8 +6,9 @@ LL | public enum Test { | help: write `pub` instead of `public` to make the item public | -LL | pub enum Test { - | ~~~ +LL - public enum Test { +LL + pub enum Test { + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/public-instead-of-pub-3.stderr b/tests/ui/parser/public-instead-of-pub-3.stderr index b9b924e670a46..856bdf185704d 100644 --- a/tests/ui/parser/public-instead-of-pub-3.stderr +++ b/tests/ui/parser/public-instead-of-pub-3.stderr @@ -6,8 +6,9 @@ LL | public const X: i32 = 123; | help: write `pub` instead of `public` to make the item public | -LL | pub const X: i32 = 123; - | ~~~ +LL - public const X: i32 = 123; +LL + pub const X: i32 = 123; + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/public-instead-of-pub.stderr b/tests/ui/parser/public-instead-of-pub.stderr index c98f8a9914e62..29a208cd4847f 100644 --- a/tests/ui/parser/public-instead-of-pub.stderr +++ b/tests/ui/parser/public-instead-of-pub.stderr @@ -6,8 +6,9 @@ LL | public struct X; | help: write `pub` instead of `public` to make the item public | -LL | pub struct X; - | ~~~ +LL - public struct X; +LL + pub struct X; + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/range-inclusive-extra-equals.stderr b/tests/ui/parser/range-inclusive-extra-equals.stderr index a573cdf950cdd..db18cef81b17e 100644 --- a/tests/ui/parser/range-inclusive-extra-equals.stderr +++ b/tests/ui/parser/range-inclusive-extra-equals.stderr @@ -7,8 +7,9 @@ LL | if let 1..==3 = 1 {} = note: inclusive ranges end with a single equals sign (`..=`) help: use `..=` instead | -LL | if let 1..=3 = 1 {} - | ~~~ +LL - if let 1..==3 = 1 {} +LL + if let 1..=3 = 1 {} + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/range_inclusive_dotdotdot.stderr b/tests/ui/parser/range_inclusive_dotdotdot.stderr index 2dc2c87eb7baa..069133a43a382 100644 --- a/tests/ui/parser/range_inclusive_dotdotdot.stderr +++ b/tests/ui/parser/range_inclusive_dotdotdot.stderr @@ -6,12 +6,14 @@ LL | return ...1; | help: use `..` for an exclusive range | -LL | return ..1; - | ~~ +LL - return ...1; +LL + return ..1; + | help: or `..=` for an inclusive range | -LL | return ..=1; - | ~~~ +LL - return ...1; +LL + return ..=1; + | error: unexpected token: `...` --> $DIR/range_inclusive_dotdotdot.rs:12:13 @@ -21,12 +23,14 @@ LL | let x = ...0; | help: use `..` for an exclusive range | -LL | let x = ..0; - | ~~ +LL - let x = ...0; +LL + let x = ..0; + | help: or `..=` for an inclusive range | -LL | let x = ..=0; - | ~~~ +LL - let x = ...0; +LL + let x = ..=0; + | error: unexpected token: `...` --> $DIR/range_inclusive_dotdotdot.rs:16:14 @@ -36,12 +40,14 @@ LL | let x = 5...5; | help: use `..` for an exclusive range | -LL | let x = 5..5; - | ~~ +LL - let x = 5...5; +LL + let x = 5..5; + | help: or `..=` for an inclusive range | -LL | let x = 5..=5; - | ~~~ +LL - let x = 5...5; +LL + let x = 5..=5; + | error: unexpected token: `...` --> $DIR/range_inclusive_dotdotdot.rs:20:15 @@ -51,12 +57,14 @@ LL | for _ in 0...1 {} | help: use `..` for an exclusive range | -LL | for _ in 0..1 {} - | ~~ +LL - for _ in 0...1 {} +LL + for _ in 0..1 {} + | help: or `..=` for an inclusive range | -LL | for _ in 0..=1 {} - | ~~~ +LL - for _ in 0...1 {} +LL + for _ in 0..=1 {} + | error: aborting due to 4 previous errors diff --git a/tests/ui/parser/recover/recover-assoc-lifetime-constraint.stderr b/tests/ui/parser/recover/recover-assoc-lifetime-constraint.stderr index 606b737e72314..d1c803863a2aa 100644 --- a/tests/ui/parser/recover/recover-assoc-lifetime-constraint.stderr +++ b/tests/ui/parser/recover/recover-assoc-lifetime-constraint.stderr @@ -10,8 +10,9 @@ LL | bar::(); = help: if you meant to specify a trait object, write `dyn /* Trait */ + 'a` help: you might have meant to write a bound here | -LL | bar::(); - | ~ +LL - bar::(); +LL + bar::(); + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/recover/recover-const-async-fn-ptr.rs b/tests/ui/parser/recover/recover-const-async-fn-ptr.rs index 2d8a3858aa603..45d753495999b 100644 --- a/tests/ui/parser/recover/recover-const-async-fn-ptr.rs +++ b/tests/ui/parser/recover/recover-const-async-fn-ptr.rs @@ -2,9 +2,9 @@ type T0 = const fn(); //~ ERROR an `fn` pointer type cannot be `const` type T1 = const extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` -type T2 = const unsafe extern fn(); //~ ERROR an `fn` pointer type cannot be `const` +type T2 = const unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` type T3 = async fn(); //~ ERROR an `fn` pointer type cannot be `async` -type T4 = async extern fn(); //~ ERROR an `fn` pointer type cannot be `async` +type T4 = async extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` type T5 = async unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` type T6 = const async unsafe extern "C" fn(); //~^ ERROR an `fn` pointer type cannot be `const` @@ -12,9 +12,9 @@ type T6 = const async unsafe extern "C" fn(); type FT0 = for<'a> const fn(); //~ ERROR an `fn` pointer type cannot be `const` type FT1 = for<'a> const extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` -type FT2 = for<'a> const unsafe extern fn(); //~ ERROR an `fn` pointer type cannot be `const` +type FT2 = for<'a> const unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` type FT3 = for<'a> async fn(); //~ ERROR an `fn` pointer type cannot be `async` -type FT4 = for<'a> async extern fn(); //~ ERROR an `fn` pointer type cannot be `async` +type FT4 = for<'a> async extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` type FT5 = for<'a> async unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` type FT6 = for<'a> const async unsafe extern "C" fn(); //~^ ERROR an `fn` pointer type cannot be `const` diff --git a/tests/ui/parser/recover/recover-const-async-fn-ptr.stderr b/tests/ui/parser/recover/recover-const-async-fn-ptr.stderr index 8e5b76163ade1..4e5927914ccb5 100644 --- a/tests/ui/parser/recover/recover-const-async-fn-ptr.stderr +++ b/tests/ui/parser/recover/recover-const-async-fn-ptr.stderr @@ -9,7 +9,7 @@ LL | type T0 = const fn(); help: remove the `const` qualifier | LL - type T0 = const fn(); -LL + type T0 = fn(); +LL + type T0 = fn(); | error: an `fn` pointer type cannot be `const` @@ -23,21 +23,21 @@ LL | type T1 = const extern "C" fn(); help: remove the `const` qualifier | LL - type T1 = const extern "C" fn(); -LL + type T1 = extern "C" fn(); +LL + type T1 = extern "C" fn(); | error: an `fn` pointer type cannot be `const` --> $DIR/recover-const-async-fn-ptr.rs:5:11 | -LL | type T2 = const unsafe extern fn(); - | -----^^^^^^^^^^^^^^^^^^^ +LL | type T2 = const unsafe extern "C" fn(); + | -----^^^^^^^^^^^^^^^^^^^^^^^ | | | `const` because of this | help: remove the `const` qualifier | -LL - type T2 = const unsafe extern fn(); -LL + type T2 = unsafe extern fn(); +LL - type T2 = const unsafe extern "C" fn(); +LL + type T2 = unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -51,21 +51,21 @@ LL | type T3 = async fn(); help: remove the `async` qualifier | LL - type T3 = async fn(); -LL + type T3 = fn(); +LL + type T3 = fn(); | error: an `fn` pointer type cannot be `async` --> $DIR/recover-const-async-fn-ptr.rs:7:11 | -LL | type T4 = async extern fn(); - | -----^^^^^^^^^^^^ +LL | type T4 = async extern "C" fn(); + | -----^^^^^^^^^^^^^^^^ | | | `async` because of this | help: remove the `async` qualifier | -LL - type T4 = async extern fn(); -LL + type T4 = extern fn(); +LL - type T4 = async extern "C" fn(); +LL + type T4 = extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -79,7 +79,7 @@ LL | type T5 = async unsafe extern "C" fn(); help: remove the `async` qualifier | LL - type T5 = async unsafe extern "C" fn(); -LL + type T5 = unsafe extern "C" fn(); +LL + type T5 = unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -93,7 +93,7 @@ LL | type T6 = const async unsafe extern "C" fn(); help: remove the `const` qualifier | LL - type T6 = const async unsafe extern "C" fn(); -LL + type T6 = async unsafe extern "C" fn(); +LL + type T6 = async unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -107,7 +107,7 @@ LL | type T6 = const async unsafe extern "C" fn(); help: remove the `async` qualifier | LL - type T6 = const async unsafe extern "C" fn(); -LL + type T6 = const unsafe extern "C" fn(); +LL + type T6 = const unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -121,7 +121,7 @@ LL | type FT0 = for<'a> const fn(); help: remove the `const` qualifier | LL - type FT0 = for<'a> const fn(); -LL + type FT0 = for<'a> fn(); +LL + type FT0 = for<'a> fn(); | error: an `fn` pointer type cannot be `const` @@ -135,21 +135,21 @@ LL | type FT1 = for<'a> const extern "C" fn(); help: remove the `const` qualifier | LL - type FT1 = for<'a> const extern "C" fn(); -LL + type FT1 = for<'a> extern "C" fn(); +LL + type FT1 = for<'a> extern "C" fn(); | error: an `fn` pointer type cannot be `const` --> $DIR/recover-const-async-fn-ptr.rs:15:12 | -LL | type FT2 = for<'a> const unsafe extern fn(); - | ^^^^^^^^-----^^^^^^^^^^^^^^^^^^^ +LL | type FT2 = for<'a> const unsafe extern "C" fn(); + | ^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^ | | | `const` because of this | help: remove the `const` qualifier | -LL - type FT2 = for<'a> const unsafe extern fn(); -LL + type FT2 = for<'a> unsafe extern fn(); +LL - type FT2 = for<'a> const unsafe extern "C" fn(); +LL + type FT2 = for<'a> unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -163,21 +163,21 @@ LL | type FT3 = for<'a> async fn(); help: remove the `async` qualifier | LL - type FT3 = for<'a> async fn(); -LL + type FT3 = for<'a> fn(); +LL + type FT3 = for<'a> fn(); | error: an `fn` pointer type cannot be `async` --> $DIR/recover-const-async-fn-ptr.rs:17:12 | -LL | type FT4 = for<'a> async extern fn(); - | ^^^^^^^^-----^^^^^^^^^^^^ +LL | type FT4 = for<'a> async extern "C" fn(); + | ^^^^^^^^-----^^^^^^^^^^^^^^^^ | | | `async` because of this | help: remove the `async` qualifier | -LL - type FT4 = for<'a> async extern fn(); -LL + type FT4 = for<'a> extern fn(); +LL - type FT4 = for<'a> async extern "C" fn(); +LL + type FT4 = for<'a> extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -191,7 +191,7 @@ LL | type FT5 = for<'a> async unsafe extern "C" fn(); help: remove the `async` qualifier | LL - type FT5 = for<'a> async unsafe extern "C" fn(); -LL + type FT5 = for<'a> unsafe extern "C" fn(); +LL + type FT5 = for<'a> unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -205,7 +205,7 @@ LL | type FT6 = for<'a> const async unsafe extern "C" fn(); help: remove the `const` qualifier | LL - type FT6 = for<'a> const async unsafe extern "C" fn(); -LL + type FT6 = for<'a> async unsafe extern "C" fn(); +LL + type FT6 = for<'a> async unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -219,7 +219,7 @@ LL | type FT6 = for<'a> const async unsafe extern "C" fn(); help: remove the `async` qualifier | LL - type FT6 = for<'a> const async unsafe extern "C" fn(); -LL + type FT6 = for<'a> const unsafe extern "C" fn(); +LL + type FT6 = for<'a> const unsafe extern "C" fn(); | error[E0308]: mismatched types diff --git a/tests/ui/parser/recover/recover-fn-ptr-with-generics.rs b/tests/ui/parser/recover/recover-fn-ptr-with-generics.rs index 76c56a715d2a4..ab2cfc961dff1 100644 --- a/tests/ui/parser/recover/recover-fn-ptr-with-generics.rs +++ b/tests/ui/parser/recover/recover-fn-ptr-with-generics.rs @@ -19,7 +19,7 @@ fn main() { type Hmm = fn<>(); //~^ ERROR function pointer types may not have generic parameters - let _: extern fn<'a: 'static>(); + let _: extern "C" fn<'a: 'static>(); //~^ ERROR function pointer types may not have generic parameters //~| ERROR bounds cannot be used in this context diff --git a/tests/ui/parser/recover/recover-fn-ptr-with-generics.stderr b/tests/ui/parser/recover/recover-fn-ptr-with-generics.stderr index 6b6cb2d6bdde7..9023856ef2486 100644 --- a/tests/ui/parser/recover/recover-fn-ptr-with-generics.stderr +++ b/tests/ui/parser/recover/recover-fn-ptr-with-generics.stderr @@ -59,15 +59,15 @@ LL | type Hmm = fn<>(); | ^^ error: function pointer types may not have generic parameters - --> $DIR/recover-fn-ptr-with-generics.rs:22:21 + --> $DIR/recover-fn-ptr-with-generics.rs:22:25 | -LL | let _: extern fn<'a: 'static>(); - | ^^^^^^^^^^^^^ +LL | let _: extern "C" fn<'a: 'static>(); + | ^^^^^^^^^^^^^ | help: consider moving the lifetime parameter to a `for` parameter list | -LL - let _: extern fn<'a: 'static>(); -LL + let _: for<'a> extern fn(); +LL - let _: extern "C" fn<'a: 'static>(); +LL + let _: for<'a> extern "C" fn(); | error: function pointer types may not have generic parameters @@ -101,10 +101,10 @@ LL | type Identity = fn(T) -> T; | ^ not found in this scope error: bounds cannot be used in this context - --> $DIR/recover-fn-ptr-with-generics.rs:22:26 + --> $DIR/recover-fn-ptr-with-generics.rs:22:30 | -LL | let _: extern fn<'a: 'static>(); - | ^^^^^^^ +LL | let _: extern "C" fn<'a: 'static>(); + | ^^^^^^^ error: aborting due to 12 previous errors diff --git a/tests/ui/parser/recover/recover-fn-trait-from-fn-kw.stderr b/tests/ui/parser/recover/recover-fn-trait-from-fn-kw.stderr index aee31d08fe0ec..4e1fcaf49368e 100644 --- a/tests/ui/parser/recover/recover-fn-trait-from-fn-kw.stderr +++ b/tests/ui/parser/recover/recover-fn-trait-from-fn-kw.stderr @@ -6,8 +6,9 @@ LL | fn foo(_: impl fn() -> i32) {} | help: use `Fn` to refer to the trait (notice the capitalization difference) | -LL | fn foo(_: impl Fn() -> i32) {} - | ~~ +LL - fn foo(_: impl fn() -> i32) {} +LL + fn foo(_: impl Fn() -> i32) {} + | error: expected identifier, found keyword `fn` --> $DIR/recover-fn-trait-from-fn-kw.rs:4:12 @@ -17,8 +18,9 @@ LL | fn foo2(_: T) {} | help: use `Fn` to refer to the trait (notice the capitalization difference) | -LL | fn foo2(_: T) {} - | ~~ +LL - fn foo2(_: T) {} +LL + fn foo2(_: T) {} + | error[E0308]: mismatched types --> $DIR/recover-fn-trait-from-fn-kw.rs:8:12 diff --git a/tests/ui/parser/recover/recover-for-loop-parens-around-head.stderr b/tests/ui/parser/recover/recover-for-loop-parens-around-head.stderr index beaa346e76c50..2bc7952def79b 100644 --- a/tests/ui/parser/recover/recover-for-loop-parens-around-head.stderr +++ b/tests/ui/parser/recover/recover-for-loop-parens-around-head.stderr @@ -18,8 +18,9 @@ LL | const _RECOVERY_WITNESS: u32 = 0u8; | help: change the type of the numeric literal from `u8` to `u32` | -LL | const _RECOVERY_WITNESS: u32 = 0u32; - | ~~~ +LL - const _RECOVERY_WITNESS: u32 = 0u8; +LL + const _RECOVERY_WITNESS: u32 = 0u32; + | error: aborting due to 2 previous errors diff --git a/tests/ui/parser/recover/recover-from-bad-variant.stderr b/tests/ui/parser/recover/recover-from-bad-variant.stderr index 0339f86951543..9359ede1346a7 100644 --- a/tests/ui/parser/recover/recover-from-bad-variant.stderr +++ b/tests/ui/parser/recover/recover-from-bad-variant.stderr @@ -6,8 +6,9 @@ LL | let x = Enum::Foo(a: 3, b: 4); | help: if `Enum::Foo` is a struct, use braces as delimiters | -LL | let x = Enum::Foo { a: 3, b: 4 }; - | ~ ~ +LL - let x = Enum::Foo(a: 3, b: 4); +LL + let x = Enum::Foo { a: 3, b: 4 }; + | help: if `Enum::Foo` is a function, use the arguments directly | LL - let x = Enum::Foo(a: 3, b: 4); @@ -22,8 +23,9 @@ LL | Enum::Foo(a, b) => {} | help: the struct variant's fields are being ignored | -LL | Enum::Foo { a: _, b: _ } => {} - | ~~~~~~~~~~~~~~ +LL - Enum::Foo(a, b) => {} +LL + Enum::Foo { a: _, b: _ } => {} + | error[E0769]: tuple variant `Enum::Bar` written as struct variant --> $DIR/recover-from-bad-variant.rs:12:9 @@ -33,8 +35,9 @@ LL | Enum::Bar { a, b } => {} | help: use the tuple variant pattern syntax instead | -LL | Enum::Bar(a, b) => {} - | ~~~~~~ +LL - Enum::Bar { a, b } => {} +LL + Enum::Bar(a, b) => {} + | error: aborting due to 3 previous errors diff --git a/tests/ui/parser/recover/recover-from-homoglyph.stderr b/tests/ui/parser/recover/recover-from-homoglyph.stderr index f11ca9fd58407..38ca0259366c3 100644 --- a/tests/ui/parser/recover/recover-from-homoglyph.stderr +++ b/tests/ui/parser/recover/recover-from-homoglyph.stderr @@ -6,8 +6,9 @@ LL | println!(""); | help: Unicode character ';' (Greek Question Mark) looks like ';' (Semicolon), but it is not | -LL | println!(""); - | ~ +LL - println!(""); +LL + println!(""); + | error[E0308]: mismatched types --> $DIR/recover-from-homoglyph.rs:3:20 diff --git a/tests/ui/parser/recover/recover-missing-semi-before-item.fixed b/tests/ui/parser/recover/recover-missing-semi-before-item.fixed index 6f85452c6fb96..871fa0d24eca7 100644 --- a/tests/ui/parser/recover/recover-missing-semi-before-item.fixed +++ b/tests/ui/parser/recover/recover-missing-semi-before-item.fixed @@ -28,7 +28,7 @@ fn for_fn() { fn for_extern() { let foo = 3; //~ ERROR expected `;`, found keyword `extern` - extern fn foo() {} + extern "C" fn foo() {} } fn for_impl() { diff --git a/tests/ui/parser/recover/recover-missing-semi-before-item.rs b/tests/ui/parser/recover/recover-missing-semi-before-item.rs index f75945b55c2b0..de92603a74c68 100644 --- a/tests/ui/parser/recover/recover-missing-semi-before-item.rs +++ b/tests/ui/parser/recover/recover-missing-semi-before-item.rs @@ -28,7 +28,7 @@ fn for_fn() { fn for_extern() { let foo = 3 //~ ERROR expected `;`, found keyword `extern` - extern fn foo() {} + extern "C" fn foo() {} } fn for_impl() { diff --git a/tests/ui/parser/recover/recover-missing-semi-before-item.stderr b/tests/ui/parser/recover/recover-missing-semi-before-item.stderr index 61c43f2f18998..3b55cd9ddd086 100644 --- a/tests/ui/parser/recover/recover-missing-semi-before-item.stderr +++ b/tests/ui/parser/recover/recover-missing-semi-before-item.stderr @@ -35,7 +35,7 @@ error: expected `;`, found keyword `extern` | LL | let foo = 3 | ^ help: add `;` here -LL | extern fn foo() {} +LL | extern "C" fn foo() {} | ------ unexpected token error: expected `;`, found keyword `impl` diff --git a/tests/ui/parser/recover/recover-pat-exprs.stderr b/tests/ui/parser/recover/recover-pat-exprs.stderr index 281eeced40242..dcc1945d569c0 100644 --- a/tests/ui/parser/recover/recover-pat-exprs.stderr +++ b/tests/ui/parser/recover/recover-pat-exprs.stderr @@ -7,8 +7,9 @@ LL | x.y => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == x.y => (), - | ~~~ +++++++++++++ +LL - x.y => (), +LL + val if val == x.y => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x.y; @@ -30,8 +31,9 @@ LL | x.0 => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == x.0 => (), - | ~~~ +++++++++++++ +LL - x.0 => (), +LL + val if val == x.0 => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x.0; @@ -54,8 +56,9 @@ LL | x._0 => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == x._0 => (), - | ~~~ ++++++++++++++ +LL - x._0 => (), +LL + val if val == x._0 => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x._0; @@ -79,8 +82,9 @@ LL | x.0.1 => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == x.0.1 => (), - | ~~~ +++++++++++++++ +LL - x.0.1 => (), +LL + val if val == x.0.1 => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x.0.1; @@ -104,8 +108,9 @@ LL | x.4.y.17.__z => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == x.4.y.17.__z => (), - | ~~~ ++++++++++++++++++++++ +LL - x.4.y.17.__z => (), +LL + val if val == x.4.y.17.__z => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x.4.y.17.__z; @@ -159,8 +164,9 @@ LL | x[0] => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == x[0] => (), - | ~~~ ++++++++++++++ +LL - x[0] => (), +LL + val if val == x[0] => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x[0]; @@ -181,8 +187,9 @@ LL | x[..] => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == x[..] => (), - | ~~~ +++++++++++++++ +LL - x[..] => (), +LL + val if val == x[..] => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x[..]; @@ -231,8 +238,9 @@ LL | x.f() => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == x.f() => (), - | ~~~ +++++++++++++++ +LL - x.f() => (), +LL + val if val == x.f() => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x.f(); @@ -253,8 +261,9 @@ LL | x._f() => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == x._f() => (), - | ~~~ ++++++++++++++++ +LL - x._f() => (), +LL + val if val == x._f() => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x._f(); @@ -276,8 +285,9 @@ LL | x? => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == x? => (), - | ~~~ ++++++++++++ +LL - x? => (), +LL + val if val == x? => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x?; @@ -300,8 +310,9 @@ LL | ().f() => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == ().f() => (), - | ~~~ ++++++++++++++++ +LL - ().f() => (), +LL + val if val == ().f() => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = ().f(); @@ -325,8 +336,9 @@ LL | (0, x)?.f() => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == (0, x)?.f() => (), - | ~~~ +++++++++++++++++++++ +LL - (0, x)?.f() => (), +LL + val if val == (0, x)?.f() => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = (0, x)?.f(); @@ -350,8 +362,9 @@ LL | x.f().g() => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == x.f().g() => (), - | ~~~ +++++++++++++++++++ +LL - x.f().g() => (), +LL + val if val == x.f().g() => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x.f().g(); @@ -375,8 +388,9 @@ LL | 0.f()?.g()?? => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == 0.f()?.g()?? => (), - | ~~~ ++++++++++++++++++++++ +LL - 0.f()?.g()?? => (), +LL + val if val == 0.f()?.g()?? => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 0.f()?.g()??; @@ -400,8 +414,9 @@ LL | x as usize => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == x as usize => (), - | ~~~ ++++++++++++++++++++ +LL - x as usize => (), +LL + val if val == x as usize => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x as usize; @@ -422,8 +437,9 @@ LL | 0 as usize => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == 0 as usize => (), - | ~~~ ++++++++++++++++++++ +LL - 0 as usize => (), +LL + val if val == 0 as usize => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 0 as usize; @@ -445,8 +461,9 @@ LL | x.f().0.4 as f32 => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == x.f().0.4 as f32 => (), - | ~~~ ++++++++++++++++++++++++++ +LL - x.f().0.4 as f32 => (), +LL + val if val == x.f().0.4 as f32 => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x.f().0.4 as f32; @@ -469,8 +486,9 @@ LL | 1 + 1 => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == 1 + 1 => (), - | ~~~ +++++++++++++++ +LL - 1 + 1 => (), +LL + val if val == 1 + 1 => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = 1 + 1; @@ -491,8 +509,9 @@ LL | (1 + 2) * 3 => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == (1 + 2) * 3 => (), - | ~~~ +++++++++++++++++++++ +LL - (1 + 2) * 3 => (), +LL + val if val == (1 + 2) * 3 => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = (1 + 2) * 3; @@ -514,8 +533,9 @@ LL | x.0 > 2 => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == (x.0 > 2) => (), - | ~~~ +++++++++++++++++++ +LL - x.0 > 2 => (), +LL + val if val == (x.0 > 2) => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x.0 > 2; @@ -539,8 +559,9 @@ LL | x.0 == 2 => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == (x.0 == 2) => (), - | ~~~ ++++++++++++++++++++ +LL - x.0 == 2 => (), +LL + val if val == (x.0 == 2) => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = x.0 == 2; @@ -564,8 +585,9 @@ LL | (x, y.0 > 2) if x != 0 => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to the match arm guard | -LL | (x, val) if x != 0 && val == (y.0 > 2) => (), - | ~~~ +++++++++++++++++++ +LL - (x, y.0 > 2) if x != 0 => (), +LL + (x, val) if x != 0 && val == (y.0 > 2) => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = y.0 > 2; @@ -586,8 +608,9 @@ LL | (x, y.0 > 2) if x != 0 || x != 1 => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to the match arm guard | -LL | (x, val) if (x != 0 || x != 1) && val == (y.0 > 2) => (), - | ~~~ + +++++++++++++++++++++ +LL - (x, y.0 > 2) if x != 0 || x != 1 => (), +LL + (x, val) if (x != 0 || x != 1) && val == (y.0 > 2) => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = y.0 > 2; @@ -626,8 +649,9 @@ LL | u8::MAX.abs() => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == u8::MAX.abs() => (), - | ~~~ +++++++++++++++++++++++ +LL - u8::MAX.abs() => (), +LL + val if val == u8::MAX.abs() => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = u8::MAX.abs(); @@ -648,8 +672,9 @@ LL | z @ w @ v.u() => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | z @ w @ val if val == v.u() => (), - | ~~~ +++++++++++++++ +LL - z @ w @ v.u() => (), +LL + z @ w @ val if val == v.u() => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = v.u(); @@ -673,8 +698,9 @@ LL | y.ilog(3) => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == y.ilog(3) => (), - | ~~~ +++++++++++++++++++ +LL - y.ilog(3) => (), +LL + val if val == y.ilog(3) => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = y.ilog(3); @@ -698,8 +724,9 @@ LL | n + 1 => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == n + 1 => (), - | ~~~ +++++++++++++++ +LL - n + 1 => (), +LL + val if val == n + 1 => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = n + 1; @@ -723,8 +750,9 @@ LL | ("".f() + 14 * 8) => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | (val) if val == "".f() + 14 * 8 => (), - | ~~~ +++++++++++++++++++++++++ +LL - ("".f() + 14 * 8) => (), +LL + (val) if val == "".f() + 14 * 8 => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = "".f() + 14 * 8; @@ -748,8 +776,9 @@ LL | f?() => (), = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | val if val == f?() => (), - | ~~~ ++++++++++++++ +LL - f?() => (), +LL + val if val == f?() => (), + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = f?(); diff --git a/tests/ui/parser/recover/recover-pat-issues.stderr b/tests/ui/parser/recover/recover-pat-issues.stderr index bdd0b2b260e7f..0c65b16dd951f 100644 --- a/tests/ui/parser/recover/recover-pat-issues.stderr +++ b/tests/ui/parser/recover/recover-pat-issues.stderr @@ -7,8 +7,9 @@ LL | Foo("hi".to_owned()) => true, = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | Foo(val) if val == "hi".to_owned() => true, - | ~~~ +++++++++++++++++++++++++ +LL - Foo("hi".to_owned()) => true, +LL + Foo(val) if val == "hi".to_owned() => true, + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = "hi".to_owned(); @@ -29,8 +30,9 @@ LL | Bar { baz: "hi".to_owned() } => true, = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | Bar { baz } if baz == "hi".to_owned() => true, - | ~~~ +++++++++++++++++++++++++ +LL - Bar { baz: "hi".to_owned() } => true, +LL + Bar { baz } if baz == "hi".to_owned() => true, + | help: consider extracting the expression into a `const` | LL + const BAZ: /* Type */ = "hi".to_owned(); @@ -51,8 +53,9 @@ LL | &["foo".to_string()] => {} = note: arbitrary expressions are not allowed in patterns: help: consider moving the expression to a match arm guard | -LL | &[val] if val == "foo".to_string() => {} - | ~~~ +++++++++++++++++++++++++++ +LL - &["foo".to_string()] => {} +LL + &[val] if val == "foo".to_string() => {} + | help: consider extracting the expression into a `const` | LL + const VAL: /* Type */ = "foo".to_string(); diff --git a/tests/ui/parser/recover/recover-range-pats.stderr b/tests/ui/parser/recover/recover-range-pats.stderr index 5c134bd4a82ab..a2f3ba4dd94ff 100644 --- a/tests/ui/parser/recover/recover-range-pats.stderr +++ b/tests/ui/parser/recover/recover-range-pats.stderr @@ -231,8 +231,9 @@ LL | if let ...3 = 0 {} | help: use `..=` instead | -LL | if let ..=3 = 0 {} - | ~~~ +LL - if let ...3 = 0 {} +LL + if let ..=3 = 0 {} + | error: range-to patterns with `...` are not allowed --> $DIR/recover-range-pats.rs:121:12 @@ -242,8 +243,9 @@ LL | if let ...Y = 0 {} | help: use `..=` instead | -LL | if let ..=Y = 0 {} - | ~~~ +LL - if let ...Y = 0 {} +LL + if let ..=Y = 0 {} + | error: range-to patterns with `...` are not allowed --> $DIR/recover-range-pats.rs:123:12 @@ -253,8 +255,9 @@ LL | if let ...true = 0 {} | help: use `..=` instead | -LL | if let ..=true = 0 {} - | ~~~ +LL - if let ...true = 0 {} +LL + if let ..=true = 0 {} + | error: float literals must have an integer part --> $DIR/recover-range-pats.rs:126:15 @@ -275,8 +278,9 @@ LL | if let ....3 = 0 {} | help: use `..=` instead | -LL | if let ..=.3 = 0 {} - | ~~~ +LL - if let ....3 = 0 {} +LL + if let ..=.3 = 0 {} + | error: range-to patterns with `...` are not allowed --> $DIR/recover-range-pats.rs:152:17 @@ -290,8 +294,9 @@ LL | mac!(0); = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) help: use `..=` instead | -LL | let ..=$e; - | ~~~ +LL - let ...$e; +LL + let ..=$e; + | error[E0586]: inclusive range with no end --> $DIR/recover-range-pats.rs:159:19 diff --git a/tests/ui/parser/recover/recover-ref-dyn-mut.stderr b/tests/ui/parser/recover/recover-ref-dyn-mut.stderr index bb0f0b0214c46..2f1649f01c9e3 100644 --- a/tests/ui/parser/recover/recover-ref-dyn-mut.stderr +++ b/tests/ui/parser/recover/recover-ref-dyn-mut.stderr @@ -6,8 +6,9 @@ LL | let r: &dyn mut Trait; | help: place `mut` before `dyn` | -LL | let r: &mut dyn Trait; - | ~~~~~~~~ +LL - let r: &dyn mut Trait; +LL + let r: &mut dyn Trait; + | error[E0405]: cannot find trait `Trait` in this scope --> $DIR/recover-ref-dyn-mut.rs:5:21 diff --git a/tests/ui/parser/recover/turbofish-arg-with-stray-colon.stderr b/tests/ui/parser/recover/turbofish-arg-with-stray-colon.stderr index 551b2e3ff09b0..583b98c650f03 100644 --- a/tests/ui/parser/recover/turbofish-arg-with-stray-colon.stderr +++ b/tests/ui/parser/recover/turbofish-arg-with-stray-colon.stderr @@ -8,7 +8,7 @@ LL | let x = Tr; help: maybe write a path separator here | LL | let x = Tr; - | ~~ + | + error: aborting due to 1 previous error diff --git a/tests/ui/parser/recover/unicode-double-equals-recovery.stderr b/tests/ui/parser/recover/unicode-double-equals-recovery.stderr index 6e10dcce04a32..1931fd4ee45fc 100644 --- a/tests/ui/parser/recover/unicode-double-equals-recovery.stderr +++ b/tests/ui/parser/recover/unicode-double-equals-recovery.stderr @@ -6,8 +6,9 @@ LL | const A: usize ⩵ 2; | help: Unicode character '⩵' (Two Consecutive Equals Signs) looks like '==' (Double Equals Sign), but it is not | -LL | const A: usize == 2; - | ~~ +LL - const A: usize ⩵ 2; +LL + const A: usize == 2; + | error: unexpected `==` --> $DIR/unicode-double-equals-recovery.rs:1:16 @@ -17,8 +18,9 @@ LL | const A: usize ⩵ 2; | help: try using `=` instead | -LL | const A: usize = 2; - | ~ +LL - const A: usize ⩵ 2; +LL + const A: usize = 2; + | error: aborting due to 2 previous errors diff --git a/tests/ui/parser/removed-syntax/removed-syntax-box.stderr b/tests/ui/parser/removed-syntax/removed-syntax-box.stderr index 60c39fd37c434..04e84a10fad68 100644 --- a/tests/ui/parser/removed-syntax/removed-syntax-box.stderr +++ b/tests/ui/parser/removed-syntax/removed-syntax-box.stderr @@ -6,8 +6,9 @@ LL | let _ = box (); | help: use `Box::new()` instead | -LL | let _ = Box::new(()); - | ~~~~~~~~~ + +LL - let _ = box (); +LL + let _ = Box::new(()); + | error: `box_syntax` has been removed --> $DIR/removed-syntax-box.rs:10:13 @@ -17,8 +18,9 @@ LL | let _ = box 1; | help: use `Box::new()` instead | -LL | let _ = Box::new(1); - | ~~~~~~~~~ + +LL - let _ = box 1; +LL + let _ = Box::new(1); + | error: `box_syntax` has been removed --> $DIR/removed-syntax-box.rs:11:13 @@ -28,8 +30,9 @@ LL | let _ = box T { a: 12, b: 18 }; | help: use `Box::new()` instead | -LL | let _ = Box::new(T { a: 12, b: 18 }); - | ~~~~~~~~~ + +LL - let _ = box T { a: 12, b: 18 }; +LL + let _ = Box::new(T { a: 12, b: 18 }); + | error: `box_syntax` has been removed --> $DIR/removed-syntax-box.rs:12:13 @@ -39,8 +42,9 @@ LL | let _ = box [5; 30]; | help: use `Box::new()` instead | -LL | let _ = Box::new([5; 30]); - | ~~~~~~~~~ + +LL - let _ = box [5; 30]; +LL + let _ = Box::new([5; 30]); + | error: `box_syntax` has been removed --> $DIR/removed-syntax-box.rs:13:22 @@ -50,8 +54,9 @@ LL | let _: Box<()> = box (); | help: use `Box::new()` instead | -LL | let _: Box<()> = Box::new(()); - | ~~~~~~~~~ + +LL - let _: Box<()> = box (); +LL + let _: Box<()> = Box::new(()); + | error: aborting due to 5 previous errors diff --git a/tests/ui/parser/suggest-assoc-const.stderr b/tests/ui/parser/suggest-assoc-const.stderr index 70ebeded31374..8cb304ced372f 100644 --- a/tests/ui/parser/suggest-assoc-const.stderr +++ b/tests/ui/parser/suggest-assoc-const.stderr @@ -6,8 +6,9 @@ LL | let _X: i32; | help: consider using `const` instead of `let` for associated const | -LL | const _X: i32; - | ~~~~~ +LL - let _X: i32; +LL + const _X: i32; + | error: aborting due to 1 previous error diff --git a/tests/ui/parser/suggest-remove-compount-assign-let-ice.stderr b/tests/ui/parser/suggest-remove-compount-assign-let-ice.stderr index 59716d69b50f9..477ec516d665a 100644 --- a/tests/ui/parser/suggest-remove-compount-assign-let-ice.stderr +++ b/tests/ui/parser/suggest-remove-compount-assign-let-ice.stderr @@ -6,8 +6,9 @@ LL | let x ➖= 1; | help: Unicode character '➖' (Heavy Minus Sign) looks like '-' (Minus/Hyphen), but it is not | -LL | let x -= 1; - | ~ +LL - let x ➖= 1; +LL + let x -= 1; + | error: can't reassign to an uninitialized variable --> $DIR/suggest-remove-compount-assign-let-ice.rs:13:11 diff --git a/tests/ui/parser/trailing-question-in-type.stderr b/tests/ui/parser/trailing-question-in-type.stderr index a3cd419c0c718..066c38d4c4fa4 100644 --- a/tests/ui/parser/trailing-question-in-type.stderr +++ b/tests/ui/parser/trailing-question-in-type.stderr @@ -6,8 +6,9 @@ LL | fn foo() -> i32? { | help: if you meant to express that the type might not contain a value, use the `Option` wrapper type | -LL | fn foo() -> Option { - | +++++++ ~ +LL - fn foo() -> i32? { +LL + fn foo() -> Option { + | error: invalid `?` in type --> $DIR/trailing-question-in-type.rs:4:15 @@ -17,8 +18,9 @@ LL | let x: i32? = Some(1); | help: if you meant to express that the type might not contain a value, use the `Option` wrapper type | -LL | let x: Option = Some(1); - | +++++++ ~ +LL - let x: i32? = Some(1); +LL + let x: Option = Some(1); + | error: aborting due to 2 previous errors diff --git a/tests/ui/parser/typod-const-in-const-param-def.stderr b/tests/ui/parser/typod-const-in-const-param-def.stderr index 80c0f1deae6fe..bf7168a015738 100644 --- a/tests/ui/parser/typod-const-in-const-param-def.stderr +++ b/tests/ui/parser/typod-const-in-const-param-def.stderr @@ -6,8 +6,9 @@ LL | pub fn foo() {} | help: use the `const` keyword (notice the capitalization difference) | -LL | pub fn foo() {} - | ~~~~~ +LL - pub fn foo() {} +LL + pub fn foo() {} + | error: `const` keyword was mistyped as `Const` --> $DIR/typod-const-in-const-param-def.rs:7:12 @@ -17,8 +18,9 @@ LL | pub fn baz() {} | help: use the `const` keyword (notice the capitalization difference) | -LL | pub fn baz() {} - | ~~~~~ +LL - pub fn baz() {} +LL + pub fn baz() {} + | error: `const` keyword was mistyped as `Const` --> $DIR/typod-const-in-const-param-def.rs:10:15 @@ -28,8 +30,9 @@ LL | pub fn qux() {} | help: use the `const` keyword (notice the capitalization difference) | -LL | pub fn qux() {} - | ~~~~~ +LL - pub fn qux() {} +LL + pub fn qux() {} + | error: `const` keyword was mistyped as `Const` --> $DIR/typod-const-in-const-param-def.rs:13:16 @@ -39,8 +42,9 @@ LL | pub fn quux() {} | help: use the `const` keyword (notice the capitalization difference) | -LL | pub fn quux() {} - | ~~~~~ +LL - pub fn quux() {} +LL + pub fn quux() {} + | error: aborting due to 4 previous errors diff --git a/tests/ui/parser/unicode-character-literal.stderr b/tests/ui/parser/unicode-character-literal.stderr index a1561e7f04b06..8ce2354f0b9c2 100644 --- a/tests/ui/parser/unicode-character-literal.stderr +++ b/tests/ui/parser/unicode-character-literal.stderr @@ -11,8 +11,9 @@ LL | let _spade = '♠️'; | ^ help: if you meant to write a string literal, use double quotes | -LL | let _spade = "♠️"; - | ~ ~ +LL - let _spade = '♠️'; +LL + let _spade = "♠️"; + | error: character literal may only contain one codepoint --> $DIR/unicode-character-literal.rs:12:14 @@ -27,8 +28,9 @@ LL | let _s = 'ṩ̂̊'; | ^ help: if you meant to write a string literal, use double quotes | -LL | let _s = "ṩ̂̊"; - | ~ ~ +LL - let _s = 'ṩ̂̊'; +LL + let _s = "ṩ̂̊"; + | error: character literal may only contain one codepoint --> $DIR/unicode-character-literal.rs:17:14 @@ -43,8 +45,9 @@ LL | let _a = 'Å'; | ^ help: consider using the normalized form `\u{c5}` of this character | -LL | let _a = 'Å'; - | ~ +LL - let _a = 'Å'; +LL + let _a = 'Å'; + | error: aborting due to 3 previous errors diff --git a/tests/ui/parser/unicode-chars.stderr b/tests/ui/parser/unicode-chars.stderr index 086de5ec0997e..a18a6ad10dd6d 100644 --- a/tests/ui/parser/unicode-chars.stderr +++ b/tests/ui/parser/unicode-chars.stderr @@ -6,8 +6,9 @@ LL | let y = 0; | help: Unicode character ';' (Greek Question Mark) looks like ';' (Semicolon), but it is not | -LL | let y = 0; - | ~ +LL - let y = 0; +LL + let y = 0; + | error: unknown start of token: \u{a0} --> $DIR/unicode-chars.rs:5:5 @@ -29,8 +30,9 @@ LL | let _ = 1 ⩵ 2; | help: Unicode character '⩵' (Two Consecutive Equals Signs) looks like '==' (Double Equals Sign), but it is not | -LL | let _ = 1 == 2; - | ~~ +LL - let _ = 1 ⩵ 2; +LL + let _ = 1 == 2; + | error: aborting due to 3 previous errors diff --git a/tests/ui/parser/unicode-control-codepoints.stderr b/tests/ui/parser/unicode-control-codepoints.stderr index 2893194308ed9..27b95f9ac6115 100644 --- a/tests/ui/parser/unicode-control-codepoints.stderr +++ b/tests/ui/parser/unicode-control-codepoints.stderr @@ -22,8 +22,9 @@ LL | println!("{:?}", b"/*� } �if isAdmin� � begin admins only "); | help: if you meant to use the UTF-8 encoding of '\u{202e}', use \xHH escapes | -LL | println!("{:?}", b"/*\xE2\x80\xAE } �if isAdmin� � begin admins only "); - | ~~~~~~~~~~~~ +LL - println!("{:?}", b"/*� } �if isAdmin� � begin admins only "); +LL + println!("{:?}", b"/*\xE2\x80\xAE } �if isAdmin� � begin admins only "); + | error: non-ASCII character in byte string literal --> $DIR/unicode-control-codepoints.rs:18:30 @@ -33,8 +34,9 @@ LL | println!("{:?}", b"/*� } �if isAdmin� � begin admins only "); | help: if you meant to use the UTF-8 encoding of '\u{2066}', use \xHH escapes | -LL | println!("{:?}", b"/*� } \xE2\x81\xA6if isAdmin� � begin admins only "); - | ~~~~~~~~~~~~ +LL - println!("{:?}", b"/*� } �if isAdmin� � begin admins only "); +LL + println!("{:?}", b"/*� } \xE2\x81\xA6if isAdmin� � begin admins only "); + | error: non-ASCII character in byte string literal --> $DIR/unicode-control-codepoints.rs:18:41 @@ -44,8 +46,9 @@ LL | println!("{:?}", b"/*� } �if isAdmin� � begin admins only "); | help: if you meant to use the UTF-8 encoding of '\u{2069}', use \xHH escapes | -LL | println!("{:?}", b"/*� } �if isAdmin\xE2\x81\xA9 � begin admins only "); - | ~~~~~~~~~~~~ +LL - println!("{:?}", b"/*� } �if isAdmin� � begin admins only "); +LL + println!("{:?}", b"/*� } �if isAdmin\xE2\x81\xA9 � begin admins only "); + | error: non-ASCII character in byte string literal --> $DIR/unicode-control-codepoints.rs:18:43 @@ -55,8 +58,9 @@ LL | println!("{:?}", b"/*� } �if isAdmin� � begin admins only "); | help: if you meant to use the UTF-8 encoding of '\u{2066}', use \xHH escapes | -LL | println!("{:?}", b"/*� } �if isAdmin� \xE2\x81\xA6 begin admins only "); - | ~~~~~~~~~~~~ +LL - println!("{:?}", b"/*� } �if isAdmin� � begin admins only "); +LL + println!("{:?}", b"/*� } �if isAdmin� \xE2\x81\xA6 begin admins only "); + | error: non-ASCII character in raw byte string literal --> $DIR/unicode-control-codepoints.rs:23:29 @@ -128,8 +132,9 @@ LL | println!("{:?}", "/*� } �if isAdmin� � begin admins only "); = help: if their presence wasn't intentional, you can remove them help: if you want to keep them but make them visible in your source code, you can escape them | -LL | println!("{:?}", "/*\u{202e} } \u{2066}if isAdmin\u{2069} \u{2066} begin admins only "); - | ~~~~~~~~ ~~~~~~~~ ~~~~~~~~ ~~~~~~~~ +LL - println!("{:?}", "/*� } �if isAdmin� � begin admins only "); +LL + println!("{:?}", "/*\u{202e} } \u{2066}if isAdmin\u{2069} \u{2066} begin admins only "); + | error: unicode codepoint changing visible direction of text present in literal --> $DIR/unicode-control-codepoints.rs:16:22 @@ -147,8 +152,9 @@ LL | println!("{:?}", r##"/*� } �if isAdmin� � begin admins only "## = help: if their presence wasn't intentional, you can remove them help: if you want to keep them but make them visible in your source code, you can escape them | -LL | println!("{:?}", r##"/*\u{202e} } \u{2066}if isAdmin\u{2069} \u{2066} begin admins only "##); - | ~~~~~~~~ ~~~~~~~~ ~~~~~~~~ ~~~~~~~~ +LL - println!("{:?}", r##"/*� } �if isAdmin� � begin admins only "##); +LL + println!("{:?}", r##"/*\u{202e} } \u{2066}if isAdmin\u{2069} \u{2066} begin admins only "##); + | error: unicode codepoint changing visible direction of text present in literal --> $DIR/unicode-control-codepoints.rs:28:22 @@ -163,8 +169,9 @@ LL | println!("{:?}", '�'); = help: if their presence wasn't intentional, you can remove them help: if you want to keep them but make them visible in your source code, you can escape them | -LL | println!("{:?}", '\u{202e}'); - | ~~~~~~~~ +LL - println!("{:?}", '�'); +LL + println!("{:?}", '\u{202e}'); + | error: unicode codepoint changing visible direction of text present in literal --> $DIR/unicode-control-codepoints.rs:31:13 @@ -179,8 +186,9 @@ LL | let _ = c"�"; = help: if their presence wasn't intentional, you can remove them help: if you want to keep them but make them visible in your source code, you can escape them | -LL | let _ = c"\u{202e}"; - | ~~~~~~~~ +LL - let _ = c"�"; +LL + let _ = c"\u{202e}"; + | error: unicode codepoint changing visible direction of text present in literal --> $DIR/unicode-control-codepoints.rs:33:13 @@ -195,8 +203,9 @@ LL | let _ = cr#"�"#; = help: if their presence wasn't intentional, you can remove them help: if you want to keep them but make them visible in your source code, you can escape them | -LL | let _ = cr#"\u{202e}"#; - | ~~~~~~~~ +LL - let _ = cr#"�"#; +LL + let _ = cr#"\u{202e}"#; + | error: unicode codepoint changing visible direction of text present in format string --> $DIR/unicode-control-codepoints.rs:36:14 @@ -211,8 +220,9 @@ LL | println!("{{�}}"); = help: if their presence wasn't intentional, you can remove them help: if you want to keep them but make them visible in your source code, you can escape them | -LL | println!("{{\u{202e}}}"); - | ~~~~~~~~ +LL - println!("{{�}}"); +LL + println!("{{\u{202e}}}"); + | error: unicode codepoint changing visible direction of text present in doc comment --> $DIR/unicode-control-codepoints.rs:43:1 diff --git a/tests/ui/parser/unicode-quote-chars.stderr b/tests/ui/parser/unicode-quote-chars.stderr index 092abeb53cd40..0ffb204d64af2 100644 --- a/tests/ui/parser/unicode-quote-chars.stderr +++ b/tests/ui/parser/unicode-quote-chars.stderr @@ -6,8 +6,9 @@ LL | println!(“hello world”); | help: Unicode characters '“' (Left Double Quotation Mark) and '”' (Right Double Quotation Mark) look like '"' (Quotation Mark), but are not | -LL | println!("hello world"); - | ~~~~~~~~~~~~~ +LL - println!(“hello world”); +LL + println!("hello world"); + | error: unknown start of token: \u{201d} --> $DIR/unicode-quote-chars.rs:2:26 @@ -17,8 +18,9 @@ LL | println!(“hello world”); | help: Unicode character '”' (Right Double Quotation Mark) looks like '"' (Quotation Mark), but it is not | -LL | println!(“hello world"); - | ~ +LL - println!(“hello world”); +LL + println!(“hello world"); + | error: expected `,`, found `world` --> $DIR/unicode-quote-chars.rs:2:21 diff --git a/tests/ui/parser/unnecessary-let.stderr b/tests/ui/parser/unnecessary-let.stderr index 0b28123747a02..d75d1943f2f4e 100644 --- a/tests/ui/parser/unnecessary-let.stderr +++ b/tests/ui/parser/unnecessary-let.stderr @@ -18,8 +18,9 @@ LL | for let _x of [1, 2, 3] {} | help: try using `in` here instead | -LL | for let _x in [1, 2, 3] {} - | ~~ +LL - for let _x of [1, 2, 3] {} +LL + for let _x in [1, 2, 3] {} + | error: expected pattern, found `let` --> $DIR/unnecessary-let.rs:9:9 diff --git a/tests/ui/parser/use-colon-as-mod-sep.stderr b/tests/ui/parser/use-colon-as-mod-sep.stderr index 347b271df9900..9b4cc0ca23776 100644 --- a/tests/ui/parser/use-colon-as-mod-sep.stderr +++ b/tests/ui/parser/use-colon-as-mod-sep.stderr @@ -8,7 +8,7 @@ LL | use std::process:Command; help: use double colon | LL | use std::process::Command; - | ~~ + | + error: expected `::`, found `:` --> $DIR/use-colon-as-mod-sep.rs:5:8 @@ -20,7 +20,7 @@ LL | use std:fs::File; help: use double colon | LL | use std::fs::File; - | ~~ + | + error: expected `::`, found `:` --> $DIR/use-colon-as-mod-sep.rs:7:8 @@ -32,7 +32,7 @@ LL | use std:collections:HashMap; help: use double colon | LL | use std::collections:HashMap; - | ~~ + | + error: expected `::`, found `:` --> $DIR/use-colon-as-mod-sep.rs:7:20 @@ -44,7 +44,7 @@ LL | use std:collections:HashMap; help: use double colon | LL | use std:collections::HashMap; - | ~~ + | + error: aborting due to 4 previous errors diff --git a/tests/ui/parser/utf16-be-without-bom.stderr b/tests/ui/parser/utf16-be-without-bom.stderr index b915c7941b3bb..0493bcbc77a4a 100644 --- a/tests/ui/parser/utf16-be-without-bom.stderr +++ b/tests/ui/parser/utf16-be-without-bom.stderr @@ -110,8 +110,9 @@ LL | ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | help: consider removing the space to spell keyword `fn` | -LL | ␀fn␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ - | ~~ +LL - ␀f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ +LL + ␀fn␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ + | error: aborting due to 14 previous errors diff --git a/tests/ui/parser/utf16-le-without-bom.stderr b/tests/ui/parser/utf16-le-without-bom.stderr index d937a07bc6602..4b195ed0da1a9 100644 --- a/tests/ui/parser/utf16-le-without-bom.stderr +++ b/tests/ui/parser/utf16-le-without-bom.stderr @@ -110,8 +110,9 @@ LL | f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ | help: consider removing the space to spell keyword `fn` | -LL | fn␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ - | ~~ +LL - f␀n␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ +LL + fn␀ ␀m␀a␀i␀n␀(␀)␀ ␀{␀}␀ + | error: aborting due to 14 previous errors diff --git a/tests/ui/pattern/bindings-after-at/wild-before-at-syntactically-rejected.stderr b/tests/ui/pattern/bindings-after-at/wild-before-at-syntactically-rejected.stderr index 1599edd7a992d..0960af705ce4a 100644 --- a/tests/ui/pattern/bindings-after-at/wild-before-at-syntactically-rejected.stderr +++ b/tests/ui/pattern/bindings-after-at/wild-before-at-syntactically-rejected.stderr @@ -9,8 +9,9 @@ LL | let _ @ a = 0; | help: switch the order | -LL | let a @ _ = 0; - | ~~~~~ +LL - let _ @ a = 0; +LL + let a @ _ = 0; + | error: pattern on wrong side of `@` --> $DIR/wild-before-at-syntactically-rejected.rs:10:9 @@ -23,8 +24,9 @@ LL | let _ @ ref a = 0; | help: switch the order | -LL | let ref a @ _ = 0; - | ~~~~~~~~~ +LL - let _ @ ref a = 0; +LL + let ref a @ _ = 0; + | error: pattern on wrong side of `@` --> $DIR/wild-before-at-syntactically-rejected.rs:12:9 @@ -37,8 +39,9 @@ LL | let _ @ ref mut a = 0; | help: switch the order | -LL | let ref mut a @ _ = 0; - | ~~~~~~~~~~~~~ +LL - let _ @ ref mut a = 0; +LL + let ref mut a @ _ = 0; + | error: left-hand side of `@` must be a binding --> $DIR/wild-before-at-syntactically-rejected.rs:14:9 diff --git a/tests/ui/pattern/incorrect-placement-of-pattern-modifiers.stderr b/tests/ui/pattern/incorrect-placement-of-pattern-modifiers.stderr index e80789253c0b1..31f8feb1a7101 100644 --- a/tests/ui/pattern/incorrect-placement-of-pattern-modifiers.stderr +++ b/tests/ui/pattern/incorrect-placement-of-pattern-modifiers.stderr @@ -50,8 +50,9 @@ LL | let _: usize = 3u8; | help: change the type of the numeric literal from `u8` to `usize` | -LL | let _: usize = 3usize; - | ~~~~~ +LL - let _: usize = 3u8; +LL + let _: usize = 3usize; + | error: aborting due to 4 previous errors diff --git a/tests/ui/pattern/issue-110508.rs b/tests/ui/pattern/issue-110508.rs index 6ed0476183ece..74a8d673e8378 100644 --- a/tests/ui/pattern/issue-110508.rs +++ b/tests/ui/pattern/issue-110508.rs @@ -1,5 +1,7 @@ //@ run-pass +#![deny(dead_code)] + #[derive(PartialEq, Eq)] pub enum Foo { FooA(()), @@ -11,6 +13,7 @@ impl Foo { const A2: Foo = Self::FooA(()); const A3: Self = Foo::FooA(()); const A4: Self = Self::FooA(()); + const A5: u32 = 1; } fn main() { @@ -35,4 +38,9 @@ fn main() { Foo::A4 => {}, _ => {}, } + + match 3 { + Foo::A5..5 => {} + _ => {} + } } diff --git a/tests/ui/pattern/issue-72574-1.stderr b/tests/ui/pattern/issue-72574-1.stderr index 653869a237d88..a9076c3dff634 100644 --- a/tests/ui/pattern/issue-72574-1.stderr +++ b/tests/ui/pattern/issue-72574-1.stderr @@ -7,8 +7,9 @@ LL | (_a, _x @ ..) => {} = help: remove this and bind each tuple field independently help: if you don't need to use the contents of _x, discard the tuple's remaining fields | -LL | (_a, ..) => {} - | ~~ +LL - (_a, _x @ ..) => {} +LL + (_a, ..) => {} + | error: `..` patterns are not allowed here --> $DIR/issue-72574-1.rs:4:19 diff --git a/tests/ui/pattern/issue-72574-2.stderr b/tests/ui/pattern/issue-72574-2.stderr index 05650f05cbf5b..8edc2634eed69 100644 --- a/tests/ui/pattern/issue-72574-2.stderr +++ b/tests/ui/pattern/issue-72574-2.stderr @@ -7,8 +7,9 @@ LL | Binder(_a, _x @ ..) => {} = help: remove this and bind each tuple field independently help: if you don't need to use the contents of _x, discard the tuple's remaining fields | -LL | Binder(_a, ..) => {} - | ~~ +LL - Binder(_a, _x @ ..) => {} +LL + Binder(_a, ..) => {} + | error: `..` patterns are not allowed here --> $DIR/issue-72574-2.rs:6:25 diff --git a/tests/ui/pattern/issue-74539.stderr b/tests/ui/pattern/issue-74539.stderr index 7443946c013f7..b6bd084ca4ba3 100644 --- a/tests/ui/pattern/issue-74539.stderr +++ b/tests/ui/pattern/issue-74539.stderr @@ -7,8 +7,9 @@ LL | E::A(x @ ..) => { = help: remove this and bind each tuple field independently help: if you don't need to use the contents of x, discard the tuple's remaining fields | -LL | E::A(..) => { - | ~~ +LL - E::A(x @ ..) => { +LL + E::A(..) => { + | error: `..` patterns are not allowed here --> $DIR/issue-74539.rs:8:18 diff --git a/tests/ui/pattern/issue-74702.stderr b/tests/ui/pattern/issue-74702.stderr index f2e2c8f021bad..5bb58de1c6e8b 100644 --- a/tests/ui/pattern/issue-74702.stderr +++ b/tests/ui/pattern/issue-74702.stderr @@ -7,8 +7,9 @@ LL | let (foo @ ..,) = (0, 0); = help: remove this and bind each tuple field independently help: if you don't need to use the contents of foo, discard the tuple's remaining fields | -LL | let (..,) = (0, 0); - | ~~ +LL - let (foo @ ..,) = (0, 0); +LL + let (..,) = (0, 0); + | error: `..` patterns are not allowed here --> $DIR/issue-74702.rs:2:16 diff --git a/tests/ui/pattern/issue-80186-mut-binding-help-suggestion.stderr b/tests/ui/pattern/issue-80186-mut-binding-help-suggestion.stderr index 622358126b09e..b61f8dbab0093 100644 --- a/tests/ui/pattern/issue-80186-mut-binding-help-suggestion.stderr +++ b/tests/ui/pattern/issue-80186-mut-binding-help-suggestion.stderr @@ -7,8 +7,9 @@ LL | let mut &x = &0; = note: `mut` may be followed by `variable` and `variable @ pattern` help: add `mut` to each binding | -LL | let &(mut x) = &0; - | ~~~~~~~~ +LL - let mut &x = &0; +LL + let &(mut x) = &0; + | error: aborting due to 1 previous error diff --git a/tests/ui/pattern/no_ref_mut_behind_and.rs b/tests/ui/pattern/no_ref_mut_behind_and.rs deleted file mode 100644 index c18d64904d035..0000000000000 --- a/tests/ui/pattern/no_ref_mut_behind_and.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ edition: 2021 -//@ run-pass -#![allow(incomplete_features)] -#![feature(ref_pat_eat_one_layer_2024)] - -fn main() { - let &[[x]] = &[&mut [42]]; - let _: &i32 = x; -} diff --git a/tests/ui/pattern/overflowing-literals.rs b/tests/ui/pattern/overflowing-literals.rs new file mode 100644 index 0000000000000..13016d3f7d197 --- /dev/null +++ b/tests/ui/pattern/overflowing-literals.rs @@ -0,0 +1,21 @@ +//! Check that overflowing literals are in patterns are rejected + +#![feature(pattern_types)] +#![feature(pattern_type_macro)] + +use std::pat::pattern_type; + +type TooBig = pattern_type!(u8 is 500..); +//~^ ERROR: literal out of range for `u8` +type TooSmall = pattern_type!(i8 is -500..); +//~^ ERROR: literal out of range for `i8` +type TooBigSigned = pattern_type!(i8 is 200..); +//~^ ERROR: literal out of range for `i8` + +fn main() { + match 5_u8 { + 500 => {} + //~^ ERROR literal out of range for `u8` + _ => {} + } +} diff --git a/tests/ui/pattern/overflowing-literals.stderr b/tests/ui/pattern/overflowing-literals.stderr new file mode 100644 index 0000000000000..8164b97fd4720 --- /dev/null +++ b/tests/ui/pattern/overflowing-literals.stderr @@ -0,0 +1,37 @@ +error: literal out of range for `u8` + --> $DIR/overflowing-literals.rs:8:35 + | +LL | type TooBig = pattern_type!(u8 is 500..); + | ^^^ + | + = note: the literal `500` does not fit into the type `u8` whose range is `0..=255` + = note: `#[deny(overflowing_literals)]` on by default + +error: literal out of range for `i8` + --> $DIR/overflowing-literals.rs:10:37 + | +LL | type TooSmall = pattern_type!(i8 is -500..); + | ^^^^ + | + = note: the literal `-500` does not fit into the type `i8` whose range is `-128..=127` + = help: consider using the type `i16` instead + +error: literal out of range for `i8` + --> $DIR/overflowing-literals.rs:12:41 + | +LL | type TooBigSigned = pattern_type!(i8 is 200..); + | ^^^ + | + = note: the literal `200` does not fit into the type `i8` whose range is `-128..=127` + = help: consider using the type `u8` instead + +error: literal out of range for `u8` + --> $DIR/overflowing-literals.rs:17:9 + | +LL | 500 => {} + | ^^^ + | + = note: the literal `500` does not fit into the type `u8` whose range is `0..=255` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/pattern/pat-tuple-field-count-cross.stderr b/tests/ui/pattern/pat-tuple-field-count-cross.stderr index 0d7f2e4af69aa..e164281826bf1 100644 --- a/tests/ui/pattern/pat-tuple-field-count-cross.stderr +++ b/tests/ui/pattern/pat-tuple-field-count-cross.stderr @@ -25,12 +25,14 @@ LL | pub struct Z1(); | help: use this syntax instead | -LL | Z0 => {} - | ~~ +LL - Z0() => {} +LL + Z0 => {} + | help: a tuple struct with a similar name exists | -LL | Z1() => {} - | ~~ +LL - Z0() => {} +LL + Z1() => {} + | error[E0532]: expected tuple struct or tuple variant, found unit struct `Z0` --> $DIR/pat-tuple-field-count-cross.rs:10:9 @@ -47,12 +49,14 @@ LL | pub struct Z1(); | help: use this syntax instead | -LL | Z0 => {} - | ~~ +LL - Z0(x) => {} +LL + Z0 => {} + | help: a tuple struct with a similar name exists | -LL | Z1(x) => {} - | ~~ +LL - Z0(x) => {} +LL + Z1(x) => {} + | error[E0532]: expected tuple struct or tuple variant, found unit variant `E1::Z0` --> $DIR/pat-tuple-field-count-cross.rs:31:9 @@ -69,12 +73,14 @@ LL | pub enum E1 { Z0, Z1(), S(u8, u8, u8) } | help: use this syntax instead | -LL | E1::Z0 => {} - | ~~~~~~ +LL - E1::Z0() => {} +LL + E1::Z0 => {} + | help: a tuple variant with a similar name exists | -LL | E1::Z1() => {} - | ~~ +LL - E1::Z0() => {} +LL + E1::Z1() => {} + | error[E0532]: expected tuple struct or tuple variant, found unit variant `E1::Z0` --> $DIR/pat-tuple-field-count-cross.rs:32:9 @@ -91,12 +97,14 @@ LL | pub enum E1 { Z0, Z1(), S(u8, u8, u8) } | help: use this syntax instead | -LL | E1::Z0 => {} - | ~~~~~~ +LL - E1::Z0(x) => {} +LL + E1::Z0 => {} + | help: a tuple variant with a similar name exists | -LL | E1::Z1(x) => {} - | ~~ +LL - E1::Z0(x) => {} +LL + E1::Z1(x) => {} + | error[E0532]: expected unit struct, unit variant or constant, found tuple variant `E1::Z1` --> $DIR/pat-tuple-field-count-cross.rs:35:9 @@ -114,11 +122,12 @@ LL | pub enum E1 { Z0, Z1(), S(u8, u8, u8) } help: use the tuple variant pattern syntax instead | LL | E1::Z1() => {} - | ~~~~~~~~ + | ++ help: a unit variant with a similar name exists | -LL | E1::Z0 => {} - | ~~ +LL - E1::Z1 => {} +LL + E1::Z0 => {} + | error[E0023]: this pattern has 1 field, but the corresponding tuple struct has 0 fields --> $DIR/pat-tuple-field-count-cross.rs:14:12 diff --git a/tests/ui/pattern/pat-tuple-overfield.stderr b/tests/ui/pattern/pat-tuple-overfield.stderr index 54d89e03101dc..b19b9d1934727 100644 --- a/tests/ui/pattern/pat-tuple-overfield.stderr +++ b/tests/ui/pattern/pat-tuple-overfield.stderr @@ -23,12 +23,14 @@ LL | Z0() => {} | help: use this syntax instead | -LL | Z0 => {} - | ~~ +LL - Z0() => {} +LL + Z0 => {} + | help: a tuple struct with a similar name exists | -LL | Z1() => {} - | ~~ +LL - Z0() => {} +LL + Z1() => {} + | error[E0532]: expected tuple struct or tuple variant, found unit struct `Z0` --> $DIR/pat-tuple-overfield.rs:53:9 @@ -43,12 +45,14 @@ LL | Z0(_) => {} | help: use this syntax instead | -LL | Z0 => {} - | ~~ +LL - Z0(_) => {} +LL + Z0 => {} + | help: a tuple struct with a similar name exists | -LL | Z1(_) => {} - | ~~ +LL - Z0(_) => {} +LL + Z1(_) => {} + | error[E0532]: expected tuple struct or tuple variant, found unit struct `Z0` --> $DIR/pat-tuple-overfield.rs:54:9 @@ -63,12 +67,14 @@ LL | Z0(_, _) => {} | help: use this syntax instead | -LL | Z0 => {} - | ~~ +LL - Z0(_, _) => {} +LL + Z0 => {} + | help: a tuple struct with a similar name exists | -LL | Z1(_, _) => {} - | ~~ +LL - Z0(_, _) => {} +LL + Z1(_, _) => {} + | error[E0532]: expected tuple struct or tuple variant, found unit variant `E1::Z0` --> $DIR/pat-tuple-overfield.rs:64:9 @@ -83,12 +89,14 @@ LL | E1::Z0() => {} | help: use this syntax instead | -LL | E1::Z0 => {} - | ~~~~~~ +LL - E1::Z0() => {} +LL + E1::Z0 => {} + | help: a tuple variant with a similar name exists | -LL | E1::Z1() => {} - | ~~ +LL - E1::Z0() => {} +LL + E1::Z1() => {} + | error[E0532]: expected tuple struct or tuple variant, found unit variant `E1::Z0` --> $DIR/pat-tuple-overfield.rs:65:9 @@ -103,12 +111,14 @@ LL | E1::Z0(_) => {} | help: use this syntax instead | -LL | E1::Z0 => {} - | ~~~~~~ +LL - E1::Z0(_) => {} +LL + E1::Z0 => {} + | help: a tuple variant with a similar name exists | -LL | E1::Z1(_) => {} - | ~~ +LL - E1::Z0(_) => {} +LL + E1::Z1(_) => {} + | error[E0532]: expected tuple struct or tuple variant, found unit variant `E1::Z0` --> $DIR/pat-tuple-overfield.rs:66:9 @@ -123,12 +133,14 @@ LL | E1::Z0(_, _) => {} | help: use this syntax instead | -LL | E1::Z0 => {} - | ~~~~~~ +LL - E1::Z0(_, _) => {} +LL + E1::Z0 => {} + | help: a tuple variant with a similar name exists | -LL | E1::Z1(_, _) => {} - | ~~ +LL - E1::Z0(_, _) => {} +LL + E1::Z1(_, _) => {} + | error[E0532]: expected unit struct, unit variant or constant, found tuple variant `E1::Z1` --> $DIR/pat-tuple-overfield.rs:69:9 @@ -144,11 +156,12 @@ LL | E1::Z1 => {} help: use the tuple variant pattern syntax instead | LL | E1::Z1() => {} - | ~~~~~~~~ + | ++ help: a unit variant with a similar name exists | -LL | E1::Z0 => {} - | ~~ +LL - E1::Z1 => {} +LL + E1::Z0 => {} + | error[E0308]: mismatched types --> $DIR/pat-tuple-overfield.rs:19:9 diff --git a/tests/ui/pattern/pat-tuple-underfield.stderr b/tests/ui/pattern/pat-tuple-underfield.stderr index e75f9b38da566..c8620fe85c3c4 100644 --- a/tests/ui/pattern/pat-tuple-underfield.stderr +++ b/tests/ui/pattern/pat-tuple-underfield.stderr @@ -36,8 +36,9 @@ LL | S(_, _) => {} | +++ help: use `..` to ignore all fields | -LL | S(..) => {} - | ~~ +LL - S(_) => {} +LL + S(..) => {} + | error[E0023]: this pattern has 0 fields, but the corresponding tuple struct has 2 fields --> $DIR/pat-tuple-underfield.rs:20:9 @@ -104,8 +105,9 @@ LL | E::S(_, _) => {} | +++ help: use `..` to ignore all fields | -LL | E::S(..) => {} - | ~~ +LL - E::S(_) => {} +LL + E::S(..) => {} + | error[E0023]: this pattern has 0 fields, but the corresponding tuple variant has 2 fields --> $DIR/pat-tuple-underfield.rs:44:9 @@ -158,8 +160,9 @@ LL | Point4( a , _ , _, _) => {} | ++++++ help: use `..` to ignore the rest of the fields | -LL | Point4( a, ..) => {} - | ~~~~ +LL - Point4( a , _ ) => {} +LL + Point4( a, ..) => {} + | error: aborting due to 10 previous errors diff --git a/tests/ui/pattern/patkind-litrange-no-expr.rs b/tests/ui/pattern/patkind-litrange-no-expr.rs index 7ef541cb58528..14d7dc737d652 100644 --- a/tests/ui/pattern/patkind-litrange-no-expr.rs +++ b/tests/ui/pattern/patkind-litrange-no-expr.rs @@ -6,7 +6,7 @@ macro_rules! enum_number { fn foo(value: i32) -> Option<$name> { match value { - $( $value => Some($name::$variant), )* // PatKind::Lit + $( $value => Some($name::$variant), )* // PatKind::Expr $( $value ..= 42 => Some($name::$variant), )* // PatKind::Range _ => None } diff --git a/tests/ui/pattern/patkind-ref-binding-issue-114896.stderr b/tests/ui/pattern/patkind-ref-binding-issue-114896.stderr index e9c2fccaba284..a6623c6306b5b 100644 --- a/tests/ui/pattern/patkind-ref-binding-issue-114896.stderr +++ b/tests/ui/pattern/patkind-ref-binding-issue-114896.stderr @@ -7,7 +7,7 @@ LL | b.make_ascii_uppercase(); help: consider changing this to be mutable | LL | let &(mut b) = a; - | ~~~~~ + + | ++++ + error: aborting due to 1 previous error diff --git a/tests/ui/pattern/patkind-ref-binding-issue-122415.stderr b/tests/ui/pattern/patkind-ref-binding-issue-122415.stderr index e93b8bbacccdd..7fa65e3d6bda6 100644 --- a/tests/ui/pattern/patkind-ref-binding-issue-122415.stderr +++ b/tests/ui/pattern/patkind-ref-binding-issue-122415.stderr @@ -7,7 +7,7 @@ LL | mutate(&mut x); help: consider changing this to be mutable | LL | fn foo(&(mut x): &i32) { - | ~~~~~ + + | ++++ + error: aborting due to 1 previous error diff --git a/tests/ui/pattern/pattern-bad-ref-box-order.stderr b/tests/ui/pattern/pattern-bad-ref-box-order.stderr index a89d3ed21b629..6f47f704688ba 100644 --- a/tests/ui/pattern/pattern-bad-ref-box-order.stderr +++ b/tests/ui/pattern/pattern-bad-ref-box-order.stderr @@ -6,8 +6,9 @@ LL | Some(ref box _i) => {}, | help: swap them | -LL | Some(box ref _i) => {}, - | ~~~~~~~ +LL - Some(ref box _i) => {}, +LL + Some(box ref _i) => {}, + | error: aborting due to 1 previous error diff --git a/tests/ui/pattern/pattern-error-continue.stderr b/tests/ui/pattern/pattern-error-continue.stderr index 10fcccb030162..bb5582dd873a7 100644 --- a/tests/ui/pattern/pattern-error-continue.stderr +++ b/tests/ui/pattern/pattern-error-continue.stderr @@ -12,12 +12,14 @@ LL | A::D(_) => (), | help: use this syntax instead | -LL | A::D => (), - | ~~~~ +LL - A::D(_) => (), +LL + A::D => (), + | help: a tuple variant with a similar name exists | -LL | A::B(_) => (), - | ~ +LL - A::D(_) => (), +LL + A::B(_) => (), + | error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields --> $DIR/pattern-error-continue.rs:17:14 diff --git a/tests/ui/pattern/range-pattern-meant-to-be-slice-rest-pattern.stderr b/tests/ui/pattern/range-pattern-meant-to-be-slice-rest-pattern.stderr index 9d642b9245a26..37b2d96bb0194 100644 --- a/tests/ui/pattern/range-pattern-meant-to-be-slice-rest-pattern.stderr +++ b/tests/ui/pattern/range-pattern-meant-to-be-slice-rest-pattern.stderr @@ -6,8 +6,9 @@ LL | [_, ...tail] => println!("{tail}"), | help: use `..=` instead | -LL | [_, ..=tail] => println!("{tail}"), - | ~~~ +LL - [_, ...tail] => println!("{tail}"), +LL + [_, ..=tail] => println!("{tail}"), + | error[E0425]: cannot find value `rest` in this scope --> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:3:13 @@ -34,8 +35,9 @@ LL | [_, ..tail] => println!("{tail}"), | help: if you meant to collect the rest of the slice in `tail`, use the at operator | -LL | [_, tail @ ..] => println!("{tail}"), - | ~~~~~~~~~ +LL - [_, ..tail] => println!("{tail}"), +LL + [_, tail @ ..] => println!("{tail}"), + | error[E0425]: cannot find value `tail` in this scope --> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:11:35 @@ -51,8 +53,9 @@ LL | [_, ...tail] => println!("{tail}"), | help: if you meant to collect the rest of the slice in `tail`, use the at operator | -LL | [_, tail @ ..] => println!("{tail}"), - | ~~~~~~~~~ +LL - [_, ...tail] => println!("{tail}"), +LL + [_, tail @ ..] => println!("{tail}"), + | error[E0425]: cannot find value `tail` in this scope --> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:17:36 diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic2024.stderr new file mode 100644 index 0000000000000..1c44617830828 --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.classic2024.stderr @@ -0,0 +1,79 @@ +error[E0508]: cannot move out of type `[&mut u32; 1]`, a non-copy array + --> $DIR/borrowck-errors.rs:13:16 + | +LL | let [&x] = &[&mut 0]; + | - ^^^^^^^^^ cannot move out of here + | | + | data moved here + | move occurs because `x` has type `&mut u32`, which does not implement the `Copy` trait + | +help: consider borrowing the pattern binding + | +LL | let [&ref x] = &[&mut 0]; + | +++ + +error[E0508]: cannot move out of type `[&mut u32; 1]`, a non-copy array + --> $DIR/borrowck-errors.rs:19:16 + | +LL | let [&x] = &mut [&mut 0]; + | - ^^^^^^^^^^^^^ cannot move out of here + | | + | data moved here + | move occurs because `x` has type `&mut u32`, which does not implement the `Copy` trait + | +help: consider borrowing the pattern binding + | +LL | let [&ref x] = &mut [&mut 0]; + | +++ + +error[E0507]: cannot move out of a shared reference + --> $DIR/borrowck-errors.rs:27:29 + | +LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) { + | - ^^^^^^^^^^^^^^^^^^^ + | | + | data moved here + | move occurs because `x` has type `&mut u32`, which does not implement the `Copy` trait + | +help: consider removing the borrow + | +LL - if let Some(&Some(x)) = Some(&Some(&mut 0)) { +LL + if let Some(Some(x)) = Some(&Some(&mut 0)) { + | + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/borrowck-errors.rs:32:10 + | +LL | let &ref mut x = &0; + | ^^^^^^^^^ cannot borrow as mutable + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/borrowck-errors.rs:37:23 + | +LL | if let &Some(Some(x)) = &Some(&mut Some(0)) { + | ^ cannot borrow as mutable + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/borrowck-errors.rs:42:11 + | +LL | let &[x] = &&mut [0]; + | ^ cannot borrow as mutable + +error[E0508]: cannot move out of type `[&mut i32; 1]`, a non-copy array + --> $DIR/borrowck-errors.rs:46:20 + | +LL | let [&mut x] = &mut [&mut 0]; + | - ^^^^^^^^^^^^^ cannot move out of here + | | + | data moved here + | move occurs because `x` has type `&mut i32`, which does not implement the `Copy` trait + | +help: consider borrowing the pattern binding + | +LL | let [&mut ref x] = &mut [&mut 0]; + | +++ + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0507, E0508, E0596. +For more information about an error, try `rustc --explain E0507`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs new file mode 100644 index 0000000000000..59cafc50d8661 --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.rs @@ -0,0 +1,50 @@ +//@ revisions: stable2021 classic2024 structural2024 +//@[stable2021] edition: 2021 +//@[classic2024] edition: 2024 +//@[structural2024] edition: 2024 +//! Tests for pattern errors not handled by the pattern typing rules, but by borrowck. +#![allow(incomplete_features)] +#![cfg_attr(classic2024, feature(ref_pat_eat_one_layer_2024))] +#![cfg_attr(structural2024, feature(ref_pat_eat_one_layer_2024_structural))] + +/// These patterns additionally use `&` to match a `&mut` reference type, which causes compilation +/// to fail in HIR typeck on stable. As such, they need to be separate from the other tests. +fn errors_caught_in_hir_typeck_on_stable() { + let [&x] = &[&mut 0]; + //[stable2021]~^ mismatched types + //[stable2021]~| types differ in mutability + //[classic2024]~^^^ ERROR: cannot move out of type + let _: &u32 = x; + + let [&x] = &mut [&mut 0]; + //[stable2021]~^ mismatched types + //[stable2021]~| types differ in mutability + //[classic2024]~^^^ ERROR: cannot move out of type + let _: &u32 = x; +} + +pub fn main() { + if let Some(&Some(x)) = Some(&Some(&mut 0)) { + //~^ ERROR: cannot move out of a shared reference [E0507] + let _: &u32 = x; + } + + let &ref mut x = &0; + //~^ cannot borrow data in a `&` reference as mutable [E0596] + + // For 2021 edition, this is also a regression test for #136223 + // since the maximum mutability is downgraded during the pattern check process. + if let &Some(Some(x)) = &Some(&mut Some(0)) { + //[stable2021,classic2024]~^ ERROR: cannot borrow data in a `&` reference as mutable + let _: &u32 = x; + } + + let &[x] = &&mut [0]; + //[stable2021,classic2024]~^ ERROR: cannot borrow data in a `&` reference as mutable + let _: &u32 = x; + + let [&mut x] = &mut [&mut 0]; + //[classic2024]~^ ERROR: cannot move out of type + #[cfg(stable2021)] let _: u32 = x; + #[cfg(structural2024)] let _: &mut u32 = x; +} diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.stable2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.stable2021.stderr new file mode 100644 index 0000000000000..deefe21ca7d45 --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.stable2021.stderr @@ -0,0 +1,69 @@ +error[E0308]: mismatched types + --> $DIR/borrowck-errors.rs:13:10 + | +LL | let [&x] = &[&mut 0]; + | ^^ --------- this expression has type `&[&mut {integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - let [&x] = &[&mut 0]; +LL + let [x] = &[&mut 0]; + | + +error[E0308]: mismatched types + --> $DIR/borrowck-errors.rs:19:10 + | +LL | let [&x] = &mut [&mut 0]; + | ^^ ------------- this expression has type `&mut [&mut {integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - let [&x] = &mut [&mut 0]; +LL + let [x] = &mut [&mut 0]; + | + +error[E0507]: cannot move out of a shared reference + --> $DIR/borrowck-errors.rs:27:29 + | +LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) { + | - ^^^^^^^^^^^^^^^^^^^ + | | + | data moved here + | move occurs because `x` has type `&mut u32`, which does not implement the `Copy` trait + | +help: consider removing the borrow + | +LL - if let Some(&Some(x)) = Some(&Some(&mut 0)) { +LL + if let Some(Some(x)) = Some(&Some(&mut 0)) { + | + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/borrowck-errors.rs:32:10 + | +LL | let &ref mut x = &0; + | ^^^^^^^^^ cannot borrow as mutable + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/borrowck-errors.rs:37:23 + | +LL | if let &Some(Some(x)) = &Some(&mut Some(0)) { + | ^ cannot borrow as mutable + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/borrowck-errors.rs:42:11 + | +LL | let &[x] = &&mut [0]; + | ^ cannot borrow as mutable + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0308, E0507, E0596. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.structural2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.structural2024.stderr new file mode 100644 index 0000000000000..30d2f9f3d702f --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/borrowck-errors.structural2024.stderr @@ -0,0 +1,25 @@ +error[E0507]: cannot move out of a shared reference + --> $DIR/borrowck-errors.rs:27:29 + | +LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) { + | - ^^^^^^^^^^^^^^^^^^^ + | | + | data moved here + | move occurs because `x` has type `&mut u32`, which does not implement the `Copy` trait + | +help: consider removing the borrow + | +LL - if let Some(&Some(x)) = Some(&Some(&mut 0)) { +LL + if let Some(Some(x)) = Some(&Some(&mut 0)) { + | + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/borrowck-errors.rs:32:10 + | +LL | let &ref mut x = &0; + | ^^^^^^^^^ cannot borrow as mutable + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0507, E0596. +For more information about an error, try `rustc --explain E0507`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/cannot-mutably-deref-shared-ref.classic2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/cannot-mutably-deref-shared-ref.classic2024.stderr new file mode 100644 index 0000000000000..89a52ba7d1ddb --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/cannot-mutably-deref-shared-ref.classic2024.stderr @@ -0,0 +1,58 @@ +error[E0308]: mismatched types + --> $DIR/cannot-mutably-deref-shared-ref.rs:9:9 + | +LL | let &mut _ = &&0; + | ^^^^^^ --- this expression has type `&&{integer}` + | | + | types differ in mutability + | + = note: expected reference `&&{integer}` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/cannot-mutably-deref-shared-ref.rs:12:9 + | +LL | let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; + | ^^^^^^ ----------------------------- this expression has type `&&&&&&&&&&&&&&&&&&&&&&&&&&&&{integer}` + | | + | types differ in mutability + | + = note: expected reference `&&&&&&&&&&&&&&&&&&&&&&&&&&&&{integer}` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/cannot-mutably-deref-shared-ref.rs:15:9 + | +LL | let &mut _ = &&mut 0; + | ^^^^^^ ------- this expression has type `&&mut {integer}` + | | + | types differ in mutability + | + = note: expected reference `&&mut {integer}` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/cannot-mutably-deref-shared-ref.rs:18:9 + | +LL | let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&mut 0; + | ^^^^^^ --------------------------------- this expression has type `&&&&&&&&&&&&&&&&&&&&&&&&&&&&mut {integer}` + | | + | types differ in mutability + | + = note: expected reference `&&&&&&&&&&&&&&&&&&&&&&&&&&&&mut {integer}` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/cannot-mutably-deref-shared-ref.rs:21:14 + | +LL | let &mut &mut &mut &mut _ = &mut &&&&mut &&&mut &mut 0; + | ^^^^^^^^^^^^^^^^ -------------------------- this expression has type `&mut &&&&mut &&&mut &mut {integer}` + | | + | types differ in mutability + | + = note: expected reference `&&&&mut &&&mut &mut {integer}` + found mutable reference `&mut _` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/cannot-mutably-deref-shared-ref.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/cannot-mutably-deref-shared-ref.rs new file mode 100644 index 0000000000000..a493b672c929f --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/cannot-mutably-deref-shared-ref.rs @@ -0,0 +1,23 @@ +//@ edition: 2024 +//@ revisions: classic2024 structural2024 +//! Test that `&mut` patterns don't match shared reference types under new typing rules in Rust 2024 +#![allow(incomplete_features)] +#![cfg_attr(classic2024, feature(ref_pat_eat_one_layer_2024))] +#![cfg_attr(structural2024, feature(ref_pat_eat_one_layer_2024_structural))] + +pub fn main() { + let &mut _ = &&0; + //~^ ERROR: mismatched types + + let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; + //~^ ERROR: mismatched types + + let &mut _ = &&mut 0; + //~^ ERROR: mismatched types + + let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&mut 0; + //~^ ERROR: mismatched types + + let &mut &mut &mut &mut _ = &mut &&&&mut &&&mut &mut 0; + //~^ ERROR: mismatched types +} diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/cannot-mutably-deref-shared-ref.structural2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/cannot-mutably-deref-shared-ref.structural2024.stderr new file mode 100644 index 0000000000000..89a52ba7d1ddb --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/cannot-mutably-deref-shared-ref.structural2024.stderr @@ -0,0 +1,58 @@ +error[E0308]: mismatched types + --> $DIR/cannot-mutably-deref-shared-ref.rs:9:9 + | +LL | let &mut _ = &&0; + | ^^^^^^ --- this expression has type `&&{integer}` + | | + | types differ in mutability + | + = note: expected reference `&&{integer}` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/cannot-mutably-deref-shared-ref.rs:12:9 + | +LL | let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0; + | ^^^^^^ ----------------------------- this expression has type `&&&&&&&&&&&&&&&&&&&&&&&&&&&&{integer}` + | | + | types differ in mutability + | + = note: expected reference `&&&&&&&&&&&&&&&&&&&&&&&&&&&&{integer}` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/cannot-mutably-deref-shared-ref.rs:15:9 + | +LL | let &mut _ = &&mut 0; + | ^^^^^^ ------- this expression has type `&&mut {integer}` + | | + | types differ in mutability + | + = note: expected reference `&&mut {integer}` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/cannot-mutably-deref-shared-ref.rs:18:9 + | +LL | let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&mut 0; + | ^^^^^^ --------------------------------- this expression has type `&&&&&&&&&&&&&&&&&&&&&&&&&&&&mut {integer}` + | | + | types differ in mutability + | + = note: expected reference `&&&&&&&&&&&&&&&&&&&&&&&&&&&&mut {integer}` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/cannot-mutably-deref-shared-ref.rs:21:14 + | +LL | let &mut &mut &mut &mut _ = &mut &&&&mut &&&mut &mut 0; + | ^^^^^^^^^^^^^^^^ -------------------------- this expression has type `&mut &&&&mut &&&mut &mut {integer}` + | | + | types differ in mutability + | + = note: expected reference `&&&&mut &&&mut &mut {integer}` + found mutable reference `&mut _` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/feature-gate-ref_pat_eat_one_layer_2024.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/feature-gate-ref_pat_eat_one_layer_2024.rs similarity index 100% rename from tests/ui/match/ref_pat_eat_one_layer_2024/feature-gate-ref_pat_eat_one_layer_2024.rs rename to tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/feature-gate-ref_pat_eat_one_layer_2024.rs diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/feature-gate-ref_pat_eat_one_layer_2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/feature-gate-ref_pat_eat_one_layer_2024.stderr similarity index 88% rename from tests/ui/match/ref_pat_eat_one_layer_2024/feature-gate-ref_pat_eat_one_layer_2024.stderr rename to tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/feature-gate-ref_pat_eat_one_layer_2024.stderr index 132fe421a18d8..912eeb1c9c041 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/feature-gate-ref_pat_eat_one_layer_2024.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/feature-gate-ref_pat_eat_one_layer_2024.stderr @@ -10,8 +10,9 @@ LL | if let Some(Some(&x)) = &Some(&Some(0)) { found reference `&_` help: consider removing `&` from the pattern | -LL | if let Some(Some(x)) = &Some(&Some(0)) { - | ~ +LL - if let Some(Some(&x)) = &Some(&Some(0)) { +LL + if let Some(Some(x)) = &Some(&Some(0)) { + | error[E0308]: mismatched types --> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:10:23 @@ -70,8 +71,9 @@ LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { | ^^^^^^ help: consider removing `&mut` from the pattern | -LL | if let Some(Some(x)) = &mut Some(&mut Some(0)) { - | ~ +LL - if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { +LL + if let Some(Some(x)) = &mut Some(&mut Some(0)) { + | error[E0308]: mismatched types --> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:25:22 @@ -85,8 +87,9 @@ LL | if let Some(Some(&x)) = &Some(&Some(0)) { found reference `&_` help: consider removing `&` from the pattern | -LL | if let Some(Some(x)) = &Some(&Some(0)) { - | ~ +LL - if let Some(Some(&x)) = &Some(&Some(0)) { +LL + if let Some(Some(x)) = &Some(&Some(0)) { + | error[E0308]: mismatched types --> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:29:27 @@ -100,8 +103,9 @@ LL | if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { found reference `&_` help: consider removing `&` from the pattern | -LL | if let Some(&mut Some(x)) = &Some(&mut Some(0)) { - | ~ +LL - if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { +LL + if let Some(&mut Some(x)) = &Some(&mut Some(0)) { + | error[E0308]: mismatched types --> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:33:23 @@ -120,8 +124,9 @@ LL | if let Some(&Some(&mut x)) = &mut Some(&Some(0)) { | ^^^^^^ help: consider removing `&mut` from the pattern | -LL | if let Some(&Some(x)) = &mut Some(&Some(0)) { - | ~ +LL - if let Some(&Some(&mut x)) = &mut Some(&Some(0)) { +LL + if let Some(&Some(x)) = &mut Some(&Some(0)) { + | error: aborting due to 8 previous errors diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.classic2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.classic2024.stderr new file mode 100644 index 0000000000000..fa95b2b5a575d --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.classic2024.stderr @@ -0,0 +1,37 @@ +error[E0658]: binding cannot be both mutable and by-reference + --> $DIR/mut-ref-mut.rs:14:13 + | +LL | let Foo(mut a) = &Foo(0); + | ^^^^ + | + = note: see issue #123076 for more information + = help: add `#![feature(mut_ref)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: binding cannot be both mutable and by-reference + --> $DIR/mut-ref-mut.rs:19:13 + | +LL | let Foo(mut a) = &mut Foo(0); + | ^^^^ + | + = note: see issue #123076 for more information + = help: add `#![feature(mut_ref)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0308]: mismatched types + --> $DIR/mut-ref-mut.rs:24:10 + | +LL | let [&mut mut x] = &[&mut 0]; + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - let [&mut mut x] = &[&mut 0]; +LL + let [&mut x] = &[&mut 0]; + | + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0308, E0658. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.rs new file mode 100644 index 0000000000000..fbd6514df73d6 --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.rs @@ -0,0 +1,29 @@ +//@ revisions: stable2021 classic2024 structural2024 +//@[stable2021] edition: 2021 +//@[classic2024] edition: 2024 +//@[structural2024] edition: 2024 +//@[stable2021] run-pass +//! Test diagnostics for binding with `mut` when the default binding mode is by-ref. +#![allow(incomplete_features, unused_assignments, unused_variables)] +#![cfg_attr(classic2024, feature(ref_pat_eat_one_layer_2024))] +#![cfg_attr(structural2024, feature(ref_pat_eat_one_layer_2024_structural))] + +pub fn main() { + struct Foo(u8); + + let Foo(mut a) = &Foo(0); + //[classic2024,structural2024]~^ ERROR: binding cannot be both mutable and by-reference + #[cfg(stable2021)] { a = 42 } + #[cfg(any(classic2024, structural2024))] { a = &42 } + + let Foo(mut a) = &mut Foo(0); + //[classic2024,structural2024]~^ ERROR: binding cannot be both mutable and by-reference + #[cfg(stable2021)] { a = 42 } + #[cfg(any(classic2024, structural2024))] { a = &mut 42 } + + let [&mut mut x] = &[&mut 0]; + //[classic2024]~^ ERROR: mismatched types + //[classic2024]~| cannot match inherited `&` with `&mut` pattern + //[structural2024]~^^^ binding cannot be both mutable and by-reference + #[cfg(stable2021)] { x = 0 } +} diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.structural2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.structural2024.stderr new file mode 100644 index 0000000000000..fd82da70a18de --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mut-ref-mut.structural2024.stderr @@ -0,0 +1,33 @@ +error[E0658]: binding cannot be both mutable and by-reference + --> $DIR/mut-ref-mut.rs:14:13 + | +LL | let Foo(mut a) = &Foo(0); + | ^^^^ + | + = note: see issue #123076 for more information + = help: add `#![feature(mut_ref)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: binding cannot be both mutable and by-reference + --> $DIR/mut-ref-mut.rs:19:13 + | +LL | let Foo(mut a) = &mut Foo(0); + | ^^^^ + | + = note: see issue #123076 for more information + = help: add `#![feature(mut_ref)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: binding cannot be both mutable and by-reference + --> $DIR/mut-ref-mut.rs:24:15 + | +LL | let [&mut mut x] = &[&mut 0]; + | ^^^^ + | + = note: see issue #123076 for more information + = help: add `#![feature(mut_ref)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2024.stderr new file mode 100644 index 0000000000000..6726a72631533 --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.classic2024.stderr @@ -0,0 +1,133 @@ +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:12:17 + | +LL | if let Some(&mut x) = &Some(&mut 0) { + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - if let Some(&mut x) = &Some(&mut 0) { +LL + if let Some(&x) = &Some(&mut 0) { + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:18:17 + | +LL | if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { +LL + if let Some(&Some(&x)) = &Some(&mut Some(0)) { + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:24:22 + | +LL | if let Some(Some(&mut x)) = &Some(Some(&mut 0)) { + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - if let Some(Some(&mut x)) = &Some(Some(&mut 0)) { +LL + if let Some(Some(&x)) = &Some(Some(&mut 0)) { + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:31:17 + | +LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - if let Some(&mut Some(&_)) = &Some(&Some(0)) { +LL + if let Some(&Some(&_)) = &Some(&Some(0)) { + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:41:23 + | +LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { +LL + if let Some(&Some(&_)) = &mut Some(&Some(0)) { + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:51:17 + | +LL | if let Some(&mut Some(x)) = &Some(Some(0)) { + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - if let Some(&mut Some(x)) = &Some(Some(0)) { +LL + if let Some(&Some(x)) = &Some(Some(0)) { + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:147:10 + | +LL | let [&mut x] = &[&mut 0]; + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - let [&mut x] = &[&mut 0]; +LL + let [&x] = &[&mut 0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:153:10 + | +LL | let [&mut &x] = &[&mut 0]; + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - let [&mut &x] = &[&mut 0]; +LL + let [&&x] = &[&mut 0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:159:10 + | +LL | let [&mut &ref x] = &[&mut 0]; + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - let [&mut &ref x] = &[&mut 0]; +LL + let [&&ref x] = &[&mut 0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:165:10 + | +LL | let [&mut &(mut x)] = &[&mut 0]; + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - let [&mut &(mut x)] = &[&mut 0]; +LL + let [&&(mut x)] = &[&mut 0]; + | + +error: aborting due to 10 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs new file mode 100644 index 0000000000000..c07c2972cd055 --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.rs @@ -0,0 +1,170 @@ +//@ revisions: stable2021 classic2024 structural2024 +//@[stable2021] edition: 2021 +//@[classic2024] edition: 2024 +//@[structural2024] edition: 2024 +//! Test cases for poorly-typed patterns in edition 2024 which are caught by HIR typeck. These must +//! be separate from cases caught by MIR borrowck or the latter errors may not be emitted. +#![allow(incomplete_features)] +#![cfg_attr(classic2024, feature(ref_pat_eat_one_layer_2024))] +#![cfg_attr(structural2024, feature(ref_pat_eat_one_layer_2024_structural))] + +pub fn main() { + if let Some(&mut x) = &Some(&mut 0) { + //[classic2024]~^ ERROR: mismatched types + //[classic2024]~| cannot match inherited `&` with `&mut` pattern + #[cfg(stable2021)] let _: u32 = x; + #[cfg(structural2024)] let _: &u32 = x; + } + if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { + //[stable2021,classic2024]~^ ERROR: mismatched types + //[stable2021]~| expected integer, found `&_` + //[classic2024]~| cannot match inherited `&` with `&mut` pattern + let _: u32 = x; + } + if let Some(Some(&mut x)) = &Some(Some(&mut 0)) { + //[classic2024]~^ ERROR: mismatched types + //[classic2024]~| cannot match inherited `&` with `&mut` pattern + #[cfg(stable2021)] let _: u32 = x; + #[cfg(structural2024)] let _: &u32 = x; + } + + if let Some(&mut Some(&_)) = &Some(&Some(0)) { + //~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability + //[classic2024,structural2024]~| cannot match inherited `&` with `&mut` pattern + } + if let Some(&Some(&mut _)) = &Some(&mut Some(0)) { + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability + //[structural2024]~| cannot match inherited `&` with `&mut` pattern + } + if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { + //~^ ERROR: mismatched types + //[stable2021]~| expected integer, found `&mut _` + //[classic2024,structural2024]~| cannot match inherited `&` with `&mut` pattern + } + if let Some(&Some(Some(&mut _))) = &Some(Some(&mut Some(0))) { + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| expected `Option<&mut Option<{integer}>>`, found `&_` + //[structural2024]~| cannot match inherited `&` with `&mut` pattern + } + if let Some(&mut Some(x)) = &Some(Some(0)) { + //~^ ERROR: mismatched types + //[stable2021]~| expected `Option<{integer}>`, found `&mut _` + //[classic2024,structural2024]~| cannot match inherited `&` with `&mut` pattern + } +} + +fn structural_errors_0() { + let &[&mut x] = &&mut [0]; + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| expected integer, found `&mut _` + //[structural2024]~| cannot match inherited `&` with `&mut` pattern + let _: u32 = x; + + let &[&mut x] = &mut &mut [0]; + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability + //[structural2024]~| cannot match inherited `&` with `&mut` pattern + let _: u32 = x; + + let &[&mut ref x] = &&mut [0]; + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| expected integer, found `&mut _` + //[structural2024]~| cannot match inherited `&` with `&mut` pattern + let _: &u32 = x; + + let &[&mut ref x] = &mut &mut [0]; + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability + //[structural2024]~| cannot match inherited `&` with `&mut` pattern + let _: &u32 = x; + + let &[&mut mut x] = &&mut [0]; + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| expected integer, found `&mut _` + //[structural2024]~| cannot match inherited `&` with `&mut` pattern + let _: u32 = x; + + let &[&mut mut x] = &mut &mut [0]; + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability + //[structural2024]~| cannot match inherited `&` with `&mut` pattern + let _: u32 = x; +} + +fn structural_errors_1() { + let [&(mut x)] = &[&0]; + //[structural2024]~^ ERROR: binding cannot be both mutable and by-reference + #[cfg(stable2021)] let _: u32 = x; + #[cfg(classic2024)] let _: &u32 = x; + + let [&(mut x)] = &mut [&0]; + //[structural2024]~^ ERROR: binding cannot be both mutable and by-reference + #[cfg(stable2021)] let _: u32 = x; + #[cfg(classic2024)] let _: &u32 = x; +} + +fn structural_errors_2() { + let [&&mut x] = &[&mut 0]; + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability + //[structural2024]~| cannot match inherited `&` with `&mut` pattern + let _: u32 = x; + + let [&&mut x] = &mut [&mut 0]; + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability + //[structural2024]~| cannot match inherited `&` with `&mut` pattern + let _: u32 = x; + + let [&&mut ref x] = &[&mut 0]; + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability + //[structural2024]~| cannot match inherited `&` with `&mut` pattern + let _: &u32 = x; + + let [&&mut ref x] = &mut [&mut 0]; + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability + //[structural2024]~| cannot match inherited `&` with `&mut` pattern + let _: &u32 = x; + + let [&&mut mut x] = &[&mut 0]; + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability + //[structural2024]~| cannot match inherited `&` with `&mut` pattern + let _: u32 = x; + + let [&&mut mut x] = &mut [&mut 0]; + //[stable2021,structural2024]~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability + //[structural2024]~| cannot match inherited `&` with `&mut` pattern + let _: u32 = x; +} + +fn classic_errors_0() { + let [&mut x] = &[&mut 0]; + //[classic2024]~^ ERROR: mismatched types + //[classic2024]~| cannot match inherited `&` with `&mut` pattern + #[cfg(stable2021)] let _: u32 = x; + #[cfg(structural2024)] let _: &u32 = x; + + let [&mut &x] = &[&mut 0]; + //[stable2021,classic2024]~^ ERROR: mismatched types + //[stable2021]~| expected integer, found `&_` + //[classic2024]~| cannot match inherited `&` with `&mut` pattern + let _: u32 = x; + + let [&mut &ref x] = &[&mut 0]; + //[stable2021,classic2024]~^ ERROR: mismatched types + //[stable2021]~| expected integer, found `&_` + //[classic2024]~| cannot match inherited `&` with `&mut` pattern + let _: &u32 = x; + + let [&mut &(mut x)] = &[&mut 0]; + //[stable2021,classic2024]~^ ERROR: mismatched types + //[stable2021]~| expected integer, found `&_` + //[classic2024]~| cannot match inherited `&` with `&mut` pattern + let _: u32 = x; +} diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.stable2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.stable2021.stderr new file mode 100644 index 0000000000000..ad19b122c20d7 --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.stable2021.stderr @@ -0,0 +1,284 @@ +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:18:27 + | +LL | if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { + | ^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { +LL + if let Some(&mut Some(x)) = &Some(&mut Some(0)) { + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:31:17 + | +LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { + | ^^^^^^^^^^^^^ --------------- this expression has type `&Option<&Option<{integer}>>` + | | + | types differ in mutability + | + = note: expected reference `&Option<{integer}>` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:36:17 + | +LL | if let Some(&Some(&mut _)) = &Some(&mut Some(0)) { + | ^^^^^^^^^^^^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut Option<{integer}>` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:41:23 + | +LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { + | ^^^^^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:46:17 + | +LL | if let Some(&Some(Some(&mut _))) = &Some(Some(&mut Some(0))) { + | ^^^^^^^^^^^^^^^^^^^ ------------------------- this expression has type `&Option>>` + | | + | expected `Option<&mut Option<{integer}>>`, found `&_` + | + = note: expected enum `Option<&mut Option<{integer}>>` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:51:17 + | +LL | if let Some(&mut Some(x)) = &Some(Some(0)) { + | ^^^^^^^^^^^^ -------------- this expression has type `&Option>` + | | + | expected `Option<{integer}>`, found `&mut _` + | + = note: expected enum `Option<{integer}>` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:59:11 + | +LL | let &[&mut x] = &&mut [0]; + | ^^^^^^ --------- this expression has type `&&mut [{integer}; 1]` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/pattern-errors.rs:59:11 + | +LL | let &[&mut x] = &&mut [0]; + | ^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - let &[&mut x] = &&mut [0]; +LL + let &[x] = &&mut [0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:65:9 + | +LL | let &[&mut x] = &mut &mut [0]; + | ^^^^^^^^^ ------------- this expression has type `&mut &mut [{integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut &mut [{integer}; 1]` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:71:11 + | +LL | let &[&mut ref x] = &&mut [0]; + | ^^^^^^^^^^ --------- this expression has type `&&mut [{integer}; 1]` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/pattern-errors.rs:71:11 + | +LL | let &[&mut ref x] = &&mut [0]; + | ^^^^^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - let &[&mut ref x] = &&mut [0]; +LL + let &[ref x] = &&mut [0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:77:9 + | +LL | let &[&mut ref x] = &mut &mut [0]; + | ^^^^^^^^^^^^^ ------------- this expression has type `&mut &mut [{integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut &mut [{integer}; 1]` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:83:11 + | +LL | let &[&mut mut x] = &&mut [0]; + | ^^^^^^^^^^ --------- this expression has type `&&mut [{integer}; 1]` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/pattern-errors.rs:83:11 + | +LL | let &[&mut mut x] = &&mut [0]; + | ^^^^^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - let &[&mut mut x] = &&mut [0]; +LL + let &[mut x] = &&mut [0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:89:9 + | +LL | let &[&mut mut x] = &mut &mut [0]; + | ^^^^^^^^^^^^^ ------------- this expression has type `&mut &mut [{integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut &mut [{integer}; 1]` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:109:10 + | +LL | let [&&mut x] = &[&mut 0]; + | ^^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:115:10 + | +LL | let [&&mut x] = &mut [&mut 0]; + | ^^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:121:10 + | +LL | let [&&mut ref x] = &[&mut 0]; + | ^^^^^^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:127:10 + | +LL | let [&&mut ref x] = &mut [&mut 0]; + | ^^^^^^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:133:10 + | +LL | let [&&mut mut x] = &[&mut 0]; + | ^^^^^^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:139:10 + | +LL | let [&&mut mut x] = &mut [&mut 0]; + | ^^^^^^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:153:15 + | +LL | let [&mut &x] = &[&mut 0]; + | ^^ --------- this expression has type `&[&mut {integer}; 1]` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - let [&mut &x] = &[&mut 0]; +LL + let [&mut x] = &[&mut 0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:159:15 + | +LL | let [&mut &ref x] = &[&mut 0]; + | ^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - let [&mut &ref x] = &[&mut 0]; +LL + let [&mut ref x] = &[&mut 0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:165:15 + | +LL | let [&mut &(mut x)] = &[&mut 0]; + | ^^^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - let [&mut &(mut x)] = &[&mut 0]; +LL + let [&mut mut x)] = &[&mut 0]; + | + +error: aborting due to 21 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2024.stderr new file mode 100644 index 0000000000000..fdf48a5a71b5f --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/pattern-errors.structural2024.stderr @@ -0,0 +1,245 @@ +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:31:17 + | +LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) { + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - if let Some(&mut Some(&_)) = &Some(&Some(0)) { +LL + if let Some(&Some(&_)) = &Some(&Some(0)) { + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:36:23 + | +LL | if let Some(&Some(&mut _)) = &Some(&mut Some(0)) { + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - if let Some(&Some(&mut _)) = &Some(&mut Some(0)) { +LL + if let Some(&Some(&_)) = &Some(&mut Some(0)) { + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:41:23 + | +LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - if let Some(&Some(&mut _)) = &mut Some(&Some(0)) { +LL + if let Some(&Some(&_)) = &mut Some(&Some(0)) { + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:46:28 + | +LL | if let Some(&Some(Some(&mut _))) = &Some(Some(&mut Some(0))) { + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - if let Some(&Some(Some(&mut _))) = &Some(Some(&mut Some(0))) { +LL + if let Some(&Some(Some(&_))) = &Some(Some(&mut Some(0))) { + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:51:17 + | +LL | if let Some(&mut Some(x)) = &Some(Some(0)) { + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - if let Some(&mut Some(x)) = &Some(Some(0)) { +LL + if let Some(&Some(x)) = &Some(Some(0)) { + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:59:11 + | +LL | let &[&mut x] = &&mut [0]; + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - let &[&mut x] = &&mut [0]; +LL + let &[&x] = &&mut [0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:65:11 + | +LL | let &[&mut x] = &mut &mut [0]; + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - let &[&mut x] = &mut &mut [0]; +LL + let &[&x] = &mut &mut [0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:71:11 + | +LL | let &[&mut ref x] = &&mut [0]; + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - let &[&mut ref x] = &&mut [0]; +LL + let &[&ref x] = &&mut [0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:77:11 + | +LL | let &[&mut ref x] = &mut &mut [0]; + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - let &[&mut ref x] = &mut &mut [0]; +LL + let &[&ref x] = &mut &mut [0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:83:11 + | +LL | let &[&mut mut x] = &&mut [0]; + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - let &[&mut mut x] = &&mut [0]; +LL + let &[&mut x] = &&mut [0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:89:11 + | +LL | let &[&mut mut x] = &mut &mut [0]; + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - let &[&mut mut x] = &mut &mut [0]; +LL + let &[&mut x] = &mut &mut [0]; + | + +error[E0658]: binding cannot be both mutable and by-reference + --> $DIR/pattern-errors.rs:97:12 + | +LL | let [&(mut x)] = &[&0]; + | ^^^^ + | + = note: see issue #123076 for more information + = help: add `#![feature(mut_ref)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: binding cannot be both mutable and by-reference + --> $DIR/pattern-errors.rs:102:12 + | +LL | let [&(mut x)] = &mut [&0]; + | ^^^^ + | + = note: see issue #123076 for more information + = help: add `#![feature(mut_ref)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:109:11 + | +LL | let [&&mut x] = &[&mut 0]; + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - let [&&mut x] = &[&mut 0]; +LL + let [&&x] = &[&mut 0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:115:11 + | +LL | let [&&mut x] = &mut [&mut 0]; + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - let [&&mut x] = &mut [&mut 0]; +LL + let [&&x] = &mut [&mut 0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:121:11 + | +LL | let [&&mut ref x] = &[&mut 0]; + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - let [&&mut ref x] = &[&mut 0]; +LL + let [&&ref x] = &[&mut 0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:127:11 + | +LL | let [&&mut ref x] = &mut [&mut 0]; + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - let [&&mut ref x] = &mut [&mut 0]; +LL + let [&&ref x] = &mut [&mut 0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:133:11 + | +LL | let [&&mut mut x] = &[&mut 0]; + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - let [&&mut mut x] = &[&mut 0]; +LL + let [&&mut x] = &[&mut 0]; + | + +error[E0308]: mismatched types + --> $DIR/pattern-errors.rs:139:11 + | +LL | let [&&mut mut x] = &mut [&mut 0]; + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - let [&&mut mut x] = &mut [&mut 0]; +LL + let [&&mut x] = &mut [&mut 0]; + | + +error: aborting due to 19 previous errors + +Some errors have detailed explanations: E0308, E0658. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr new file mode 100644 index 0000000000000..56125be2d6fc8 --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.classic2024.stderr @@ -0,0 +1,91 @@ +error[E0308]: mismatched types + --> $DIR/ref-binding-on-inh-ref-errors.rs:54:10 + | +LL | let [&mut ref x] = &[&mut 0]; + | ^^^^^ + | + = note: cannot match inherited `&` with `&mut` pattern +help: replace this `&mut` pattern with `&` + | +LL - let [&mut ref x] = &[&mut 0]; +LL + let [&ref x] = &[&mut 0]; + | + +error: binding modifiers may only be written when the default binding mode is `move` + --> $DIR/ref-binding-on-inh-ref-errors.rs:67:10 + | +LL | let [ref mut x] = &[0]; + | ^^^^^^^ binding modifier not allowed under `ref` default binding mode + | + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/ref-binding-on-inh-ref-errors.rs:67:9 + | +LL | let [ref mut x] = &[0]; + | ^^^^^^^^^^^ this matches on type `&_` +help: make the implied reference pattern explicit + | +LL | let &[ref mut x] = &[0]; + | + + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/ref-binding-on-inh-ref-errors.rs:67:10 + | +LL | let [ref mut x] = &[0]; + | ^^^^^^^^^ cannot borrow as mutable + +error: binding modifiers may only be written when the default binding mode is `move` + --> $DIR/ref-binding-on-inh-ref-errors.rs:75:10 + | +LL | let [ref x] = &[0]; + | ^^^ binding modifier not allowed under `ref` default binding mode + | + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/ref-binding-on-inh-ref-errors.rs:75:9 + | +LL | let [ref x] = &[0]; + | ^^^^^^^ this matches on type `&_` +help: make the implied reference pattern explicit + | +LL | let &[ref x] = &[0]; + | + + +error: binding modifiers may only be written when the default binding mode is `move` + --> $DIR/ref-binding-on-inh-ref-errors.rs:79:10 + | +LL | let [ref x] = &mut [0]; + | ^^^ binding modifier not allowed under `ref mut` default binding mode + | + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/ref-binding-on-inh-ref-errors.rs:79:9 + | +LL | let [ref x] = &mut [0]; + | ^^^^^^^ this matches on type `&mut _` +help: make the implied reference pattern explicit + | +LL | let &mut [ref x] = &mut [0]; + | ++++ + +error: binding modifiers may only be written when the default binding mode is `move` + --> $DIR/ref-binding-on-inh-ref-errors.rs:83:10 + | +LL | let [ref mut x] = &mut [0]; + | ^^^^^^^ binding modifier not allowed under `ref mut` default binding mode + | + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/ref-binding-on-inh-ref-errors.rs:83:9 + | +LL | let [ref mut x] = &mut [0]; + | ^^^^^^^^^^^ this matches on type `&mut _` +help: make the implied reference pattern explicit + | +LL | let &mut [ref mut x] = &mut [0]; + | ++++ + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0308, E0596. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs new file mode 100644 index 0000000000000..4e048570c33c2 --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.rs @@ -0,0 +1,86 @@ +//@ revisions: stable2021 classic2024 structural2024 +//@[stable2021] edition: 2021 +//@[classic2024] edition: 2024 +//@[structural2024] edition: 2024 +//! Tests for errors from binding with `ref x` under a by-ref default binding mode in edition 2024. +//! These can't be in the same body as tests for other errors, since they're emitted during THIR +//! construction. The errors on stable edition 2021 Rust are unrelated. +#![allow(incomplete_features)] +#![cfg_attr(classic2024, feature(ref_pat_eat_one_layer_2024))] +#![cfg_attr(structural2024, feature(ref_pat_eat_one_layer_2024_structural))] + +/// These only fail on the eat-inner variant of the new edition 2024 pattern typing rules. +/// The eat-outer variant eats the inherited reference, so binding with `ref` isn't a problem. +fn errors_from_eating_the_real_reference() { + let [&ref x] = &[&0]; + //[structural2024]~^ ERROR: binding modifiers may only be written when the default binding mode is `move` + #[cfg(stable2021)] let _: &u32 = x; + #[cfg(classic2024)] let _: &&u32 = x; + + let [&ref x] = &mut [&0]; + //[structural2024]~^ ERROR: binding modifiers may only be written when the default binding mode is `move` + #[cfg(stable2021)] let _: &u32 = x; + #[cfg(classic2024)] let _: &&u32 = x; + + let [&mut ref x] = &mut [&mut 0]; + //[structural2024]~^ ERROR: binding modifiers may only be written when the default binding mode is `move` + #[cfg(stable2021)] let _: &u32 = x; + #[cfg(classic2024)] let _: &&mut u32 = x; + + let [&mut ref mut x] = &mut [&mut 0]; + //[structural2024]~^ ERROR: binding modifiers may only be written when the default binding mode is `move` + #[cfg(stable2021)] let _: &mut u32 = x; + #[cfg(classic2024)] let _: &mut &mut u32 = x; +} + +/// To make absolutely sure binding with `ref` ignores inherited references on stable, let's +/// quarantine these typeck errors (from using a `&` pattern to match a `&mut` reference type). +fn errors_from_eating_the_real_reference_caught_in_hir_typeck_on_stable() { + let [&ref x] = &[&mut 0]; + //[stable2021]~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability + //[structural2024]~^^^ ERROR: binding modifiers may only be written when the default binding mode is `move` + #[cfg(classic2024)] let _: &&mut u32 = x; + + let [&ref x] = &mut [&mut 0]; + //[stable2021]~^ ERROR: mismatched types + //[stable2021]~| types differ in mutability + //[structural2024]~^^^ ERROR: binding modifiers may only be written when the default binding mode is `move` + #[cfg(classic2024)] let _: &&mut u32 = x; +} + +/// This one also needs to be quarantined for a typeck error on `classic2024` (eat-outer). +fn errors_dependent_on_eating_order_caught_in_hir_typeck_when_eating_outer() { + let [&mut ref x] = &[&mut 0]; + //[classic2024]~^ ERROR: mismatched types + //[classic2024]~| cannot match inherited `&` with `&mut` pattern + //[structural2024]~^^^ ERROR: binding modifiers may only be written when the default binding mode is `move` + #[cfg(stable2021)] let _: &u32 = x; +} + +/// These should be errors in all editions. In edition 2024, they should be caught by the pattern +/// typing rules disallowing `ref` when there's an inherited reference. In old editions where that +/// resets the binding mode, they're borrowck errors due to binding with `ref mut`. +/// As a quirk of how the edition 2024 error is emitted during THIR construction, it ends up going +/// through borrowck as well, using the old `ref` behavior as a fallback, so we get that error too. +fn borrowck_errors_in_old_editions() { + let [ref mut x] = &[0]; + //~^ ERROR: cannot borrow data in a `&` reference as mutable + //[classic2024,structural2024]~| ERROR: binding modifiers may only be written when the default binding mode is `move` +} + +/// The remaining tests are purely for testing `ref` bindings in the presence of an inherited +/// reference. These should always fail on edition 2024 and succeed on edition 2021. +pub fn main() { + let [ref x] = &[0]; + //[classic2024,structural2024]~^ ERROR: binding modifiers may only be written when the default binding mode is `move` + #[cfg(stable2021)] let _: &u32 = x; + + let [ref x] = &mut [0]; + //[classic2024,structural2024]~^ ERROR: binding modifiers may only be written when the default binding mode is `move` + #[cfg(stable2021)] let _: &u32 = x; + + let [ref mut x] = &mut [0]; + //[classic2024,structural2024]~^ ERROR: binding modifiers may only be written when the default binding mode is `move` + #[cfg(stable2021)] let _: &mut u32 = x; +} diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.stable2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.stable2021.stderr new file mode 100644 index 0000000000000..26095d8460572 --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.stable2021.stderr @@ -0,0 +1,42 @@ +error[E0308]: mismatched types + --> $DIR/ref-binding-on-inh-ref-errors.rs:39:10 + | +LL | let [&ref x] = &[&mut 0]; + | ^^^^^^ --------- this expression has type `&[&mut {integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - let [&ref x] = &[&mut 0]; +LL + let [ref x] = &[&mut 0]; + | + +error[E0308]: mismatched types + --> $DIR/ref-binding-on-inh-ref-errors.rs:45:10 + | +LL | let [&ref x] = &mut [&mut 0]; + | ^^^^^^ ------------- this expression has type `&mut [&mut {integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - let [&ref x] = &mut [&mut 0]; +LL + let [ref x] = &mut [&mut 0]; + | + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/ref-binding-on-inh-ref-errors.rs:67:10 + | +LL | let [ref mut x] = &[0]; + | ^^^^^^^^^ cannot borrow as mutable + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0308, E0596. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr new file mode 100644 index 0000000000000..31930e8c03371 --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-binding-on-inh-ref-errors.structural2024.stderr @@ -0,0 +1,196 @@ +error: binding modifiers may only be written when the default binding mode is `move` + --> $DIR/ref-binding-on-inh-ref-errors.rs:15:11 + | +LL | let [&ref x] = &[&0]; + | ^^^ binding modifier not allowed under `ref` default binding mode + | + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/ref-binding-on-inh-ref-errors.rs:15:9 + | +LL | let [&ref x] = &[&0]; + | ^^^^^^^^ this matches on type `&_` +help: make the implied reference pattern explicit + | +LL | let &[&ref x] = &[&0]; + | + + +error: binding modifiers may only be written when the default binding mode is `move` + --> $DIR/ref-binding-on-inh-ref-errors.rs:20:11 + | +LL | let [&ref x] = &mut [&0]; + | ^^^ binding modifier not allowed under `ref` default binding mode + | + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/ref-binding-on-inh-ref-errors.rs:20:9 + | +LL | let [&ref x] = &mut [&0]; + | ^^^^^^^^ this matches on type `&mut _` +help: make the implied reference pattern explicit + | +LL | let &mut [&ref x] = &mut [&0]; + | ++++ + +error: binding modifiers may only be written when the default binding mode is `move` + --> $DIR/ref-binding-on-inh-ref-errors.rs:25:15 + | +LL | let [&mut ref x] = &mut [&mut 0]; + | ^^^ binding modifier not allowed under `ref mut` default binding mode + | + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/ref-binding-on-inh-ref-errors.rs:25:9 + | +LL | let [&mut ref x] = &mut [&mut 0]; + | ^^^^^^^^^^^^ this matches on type `&mut _` +help: make the implied reference pattern explicit + | +LL | let &mut [&mut ref x] = &mut [&mut 0]; + | ++++ + +error: binding modifiers may only be written when the default binding mode is `move` + --> $DIR/ref-binding-on-inh-ref-errors.rs:30:15 + | +LL | let [&mut ref mut x] = &mut [&mut 0]; + | ^^^^^^^ binding modifier not allowed under `ref mut` default binding mode + | + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/ref-binding-on-inh-ref-errors.rs:30:9 + | +LL | let [&mut ref mut x] = &mut [&mut 0]; + | ^^^^^^^^^^^^^^^^ this matches on type `&mut _` +help: make the implied reference pattern explicit + | +LL | let &mut [&mut ref mut x] = &mut [&mut 0]; + | ++++ + +error: binding modifiers may only be written when the default binding mode is `move` + --> $DIR/ref-binding-on-inh-ref-errors.rs:39:11 + | +LL | let [&ref x] = &[&mut 0]; + | ^^^ binding modifier not allowed under `ref` default binding mode + | + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/ref-binding-on-inh-ref-errors.rs:39:9 + | +LL | let [&ref x] = &[&mut 0]; + | ^^^^^^^^ this matches on type `&_` +help: make the implied reference pattern explicit + | +LL | let &[&ref x] = &[&mut 0]; + | + + +error: binding modifiers may only be written when the default binding mode is `move` + --> $DIR/ref-binding-on-inh-ref-errors.rs:45:11 + | +LL | let [&ref x] = &mut [&mut 0]; + | ^^^ binding modifier not allowed under `ref` default binding mode + | + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/ref-binding-on-inh-ref-errors.rs:45:9 + | +LL | let [&ref x] = &mut [&mut 0]; + | ^^^^^^^^ this matches on type `&mut _` +help: make the implied reference pattern explicit + | +LL | let &mut [&ref x] = &mut [&mut 0]; + | ++++ + +error: binding modifiers may only be written when the default binding mode is `move` + --> $DIR/ref-binding-on-inh-ref-errors.rs:54:15 + | +LL | let [&mut ref x] = &[&mut 0]; + | ^^^ binding modifier not allowed under `ref` default binding mode + | + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/ref-binding-on-inh-ref-errors.rs:54:9 + | +LL | let [&mut ref x] = &[&mut 0]; + | ^^^^^^^^^^^^ this matches on type `&_` +help: make the implied reference pattern explicit + | +LL | let &[&mut ref x] = &[&mut 0]; + | + + +error: binding modifiers may only be written when the default binding mode is `move` + --> $DIR/ref-binding-on-inh-ref-errors.rs:67:10 + | +LL | let [ref mut x] = &[0]; + | ^^^^^^^ binding modifier not allowed under `ref` default binding mode + | + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/ref-binding-on-inh-ref-errors.rs:67:9 + | +LL | let [ref mut x] = &[0]; + | ^^^^^^^^^^^ this matches on type `&_` +help: make the implied reference pattern explicit + | +LL | let &[ref mut x] = &[0]; + | + + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/ref-binding-on-inh-ref-errors.rs:67:10 + | +LL | let [ref mut x] = &[0]; + | ^^^^^^^^^ cannot borrow as mutable + +error: binding modifiers may only be written when the default binding mode is `move` + --> $DIR/ref-binding-on-inh-ref-errors.rs:75:10 + | +LL | let [ref x] = &[0]; + | ^^^ binding modifier not allowed under `ref` default binding mode + | + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/ref-binding-on-inh-ref-errors.rs:75:9 + | +LL | let [ref x] = &[0]; + | ^^^^^^^ this matches on type `&_` +help: make the implied reference pattern explicit + | +LL | let &[ref x] = &[0]; + | + + +error: binding modifiers may only be written when the default binding mode is `move` + --> $DIR/ref-binding-on-inh-ref-errors.rs:79:10 + | +LL | let [ref x] = &mut [0]; + | ^^^ binding modifier not allowed under `ref mut` default binding mode + | + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/ref-binding-on-inh-ref-errors.rs:79:9 + | +LL | let [ref x] = &mut [0]; + | ^^^^^^^ this matches on type `&mut _` +help: make the implied reference pattern explicit + | +LL | let &mut [ref x] = &mut [0]; + | ++++ + +error: binding modifiers may only be written when the default binding mode is `move` + --> $DIR/ref-binding-on-inh-ref-errors.rs:83:10 + | +LL | let [ref mut x] = &mut [0]; + | ^^^^^^^ binding modifier not allowed under `ref mut` default binding mode + | + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/ref-binding-on-inh-ref-errors.rs:83:9 + | +LL | let [ref mut x] = &mut [0]; + | ^^^^^^^^^^^ this matches on type `&mut _` +help: make the implied reference pattern explicit + | +LL | let &mut [ref mut x] = &mut [0]; + | ++++ + +error: aborting due to 12 previous errors + +For more information about this error, try `rustc --explain E0596`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic2024.fixed b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic2024.fixed new file mode 100644 index 0000000000000..c01784d5076b7 --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic2024.fixed @@ -0,0 +1,45 @@ +//@ revisions: stable2021 classic2024 structural2024 +//@[stable2021] edition: 2021 +//@[classic2024] edition: 2024 +//@[structural2024] edition: 2024 +//@[classic2024] run-rustfix +//@[structural2024] run-rustfix +//! Tests for `&` patterns matched against `&mut` reference types where the inner pattern attempts +//! to bind by mutable reference. +#![allow(incomplete_features)] +#![cfg_attr(classic2024, feature(ref_pat_eat_one_layer_2024))] +#![cfg_attr(structural2024, feature(ref_pat_eat_one_layer_2024_structural))] + +pub fn main() { + if let Some(&mut Some(ref mut x)) = &mut Some(Some(0)) { + //[stable2021]~^ ERROR: mismatched types + //[classic2024,structural2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut u8 = x; + } + + if let &mut Some(Some(ref mut x)) = &mut Some(Some(0)) { + //[stable2021]~^ ERROR: mismatched types + //[classic2024,structural2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut u8 = x; + } + + macro_rules! pat { + ($var:ident) => { ref mut $var }; + } + let &mut pat!(x) = &mut 0; + //[stable2021]~^ ERROR: mismatched types + //[classic2024,structural2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut u8 = x; + + let &mut (ref mut a, ref mut b) = &mut (true, false); + //[stable2021]~^ ERROR: mismatched types + //[classic2024,structural2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern + //[classic2024,structural2024]~| ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut bool = a; + let _: &mut bool = b; + + let &mut [x] = &mut &mut [0]; + //[stable2021]~^ ERROR: mismatched types + //[classic2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &u32 = x; +} diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic2024.stderr new file mode 100644 index 0000000000000..5e98b77be40cd --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.classic2024.stderr @@ -0,0 +1,51 @@ +error[E0596]: cannot borrow as mutable inside an `&` pattern + --> $DIR/ref-mut-inside-shared-ref-pat.rs:14:31 + | +LL | if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` + +error[E0596]: cannot borrow as mutable inside an `&` pattern + --> $DIR/ref-mut-inside-shared-ref-pat.rs:20:31 + | +LL | if let &Some(Some(ref mut x)) = &mut Some(Some(0)) { + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` + +error[E0596]: cannot borrow as mutable inside an `&` pattern + --> $DIR/ref-mut-inside-shared-ref-pat.rs:29:15 + | +LL | let &pat!(x) = &mut 0; + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` + +error[E0596]: cannot borrow as mutable inside an `&` pattern + --> $DIR/ref-mut-inside-shared-ref-pat.rs:34:19 + | +LL | let &(ref mut a, ref mut b) = &mut (true, false); + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` + +error[E0596]: cannot borrow as mutable inside an `&` pattern + --> $DIR/ref-mut-inside-shared-ref-pat.rs:34:30 + | +LL | let &(ref mut a, ref mut b) = &mut (true, false); + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` + +error[E0596]: cannot borrow as mutable inside an `&` pattern + --> $DIR/ref-mut-inside-shared-ref-pat.rs:41:11 + | +LL | let &[x] = &mut &mut [0]; + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0596`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.rs new file mode 100644 index 0000000000000..fe40dabb55394 --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.rs @@ -0,0 +1,45 @@ +//@ revisions: stable2021 classic2024 structural2024 +//@[stable2021] edition: 2021 +//@[classic2024] edition: 2024 +//@[structural2024] edition: 2024 +//@[classic2024] run-rustfix +//@[structural2024] run-rustfix +//! Tests for `&` patterns matched against `&mut` reference types where the inner pattern attempts +//! to bind by mutable reference. +#![allow(incomplete_features)] +#![cfg_attr(classic2024, feature(ref_pat_eat_one_layer_2024))] +#![cfg_attr(structural2024, feature(ref_pat_eat_one_layer_2024_structural))] + +pub fn main() { + if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { + //[stable2021]~^ ERROR: mismatched types + //[classic2024,structural2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut u8 = x; + } + + if let &Some(Some(ref mut x)) = &mut Some(Some(0)) { + //[stable2021]~^ ERROR: mismatched types + //[classic2024,structural2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut u8 = x; + } + + macro_rules! pat { + ($var:ident) => { ref mut $var }; + } + let &pat!(x) = &mut 0; + //[stable2021]~^ ERROR: mismatched types + //[classic2024,structural2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut u8 = x; + + let &(ref mut a, ref mut b) = &mut (true, false); + //[stable2021]~^ ERROR: mismatched types + //[classic2024,structural2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern + //[classic2024,structural2024]~| ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut bool = a; + let _: &mut bool = b; + + let &[x] = &mut &mut [0]; + //[stable2021]~^ ERROR: mismatched types + //[classic2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &u32 = x; +} diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.stable2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.stable2021.stderr new file mode 100644 index 0000000000000..72c6c05e18443 --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.stable2021.stderr @@ -0,0 +1,58 @@ +error[E0308]: mismatched types + --> $DIR/ref-mut-inside-shared-ref-pat.rs:14:17 + | +LL | if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { + | ^^^^^^^^^^^^^^^^ ------------------ this expression has type `&mut Option>` + | | + | expected `Option<{integer}>`, found `&_` + | + = note: expected enum `Option<{integer}>` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/ref-mut-inside-shared-ref-pat.rs:20:12 + | +LL | if let &Some(Some(ref mut x)) = &mut Some(Some(0)) { + | ^^^^^^^^^^^^^^^^^^^^^^ ------------------ this expression has type `&mut Option>` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut Option>` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/ref-mut-inside-shared-ref-pat.rs:29:9 + | +LL | let &pat!(x) = &mut 0; + | ^^^^^^^^ ------ this expression has type `&mut {integer}` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/ref-mut-inside-shared-ref-pat.rs:34:9 + | +LL | let &(ref mut a, ref mut b) = &mut (true, false); + | ^^^^^^^^^^^^^^^^^^^^^^^ ------------------ this expression has type `&mut (bool, bool)` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut (bool, bool)` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/ref-mut-inside-shared-ref-pat.rs:41:9 + | +LL | let &[x] = &mut &mut [0]; + | ^^^^ ------------- this expression has type `&mut &mut [{integer}; 1]` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut &mut [{integer}; 1]` + found reference `&_` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural2024.fixed b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural2024.fixed new file mode 100644 index 0000000000000..4ee849b38c53b --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural2024.fixed @@ -0,0 +1,45 @@ +//@ revisions: stable2021 classic2024 structural2024 +//@[stable2021] edition: 2021 +//@[classic2024] edition: 2024 +//@[structural2024] edition: 2024 +//@[classic2024] run-rustfix +//@[structural2024] run-rustfix +//! Tests for `&` patterns matched against `&mut` reference types where the inner pattern attempts +//! to bind by mutable reference. +#![allow(incomplete_features)] +#![cfg_attr(classic2024, feature(ref_pat_eat_one_layer_2024))] +#![cfg_attr(structural2024, feature(ref_pat_eat_one_layer_2024_structural))] + +pub fn main() { + if let Some(&mut Some(ref mut x)) = &mut Some(Some(0)) { + //[stable2021]~^ ERROR: mismatched types + //[classic2024,structural2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut u8 = x; + } + + if let &mut Some(Some(ref mut x)) = &mut Some(Some(0)) { + //[stable2021]~^ ERROR: mismatched types + //[classic2024,structural2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut u8 = x; + } + + macro_rules! pat { + ($var:ident) => { ref mut $var }; + } + let &mut pat!(x) = &mut 0; + //[stable2021]~^ ERROR: mismatched types + //[classic2024,structural2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut u8 = x; + + let &mut (ref mut a, ref mut b) = &mut (true, false); + //[stable2021]~^ ERROR: mismatched types + //[classic2024,structural2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern + //[classic2024,structural2024]~| ERROR: cannot borrow as mutable inside an `&` pattern + let _: &mut bool = a; + let _: &mut bool = b; + + let &[x] = &mut &mut [0]; + //[stable2021]~^ ERROR: mismatched types + //[classic2024]~^^ ERROR: cannot borrow as mutable inside an `&` pattern + let _: &u32 = x; +} diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural2024.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural2024.stderr new file mode 100644 index 0000000000000..69cb6c438b6ed --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref-mut-inside-shared-ref-pat.structural2024.stderr @@ -0,0 +1,43 @@ +error[E0596]: cannot borrow as mutable inside an `&` pattern + --> $DIR/ref-mut-inside-shared-ref-pat.rs:14:31 + | +LL | if let Some(&Some(ref mut x)) = &mut Some(Some(0)) { + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` + +error[E0596]: cannot borrow as mutable inside an `&` pattern + --> $DIR/ref-mut-inside-shared-ref-pat.rs:20:31 + | +LL | if let &Some(Some(ref mut x)) = &mut Some(Some(0)) { + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` + +error[E0596]: cannot borrow as mutable inside an `&` pattern + --> $DIR/ref-mut-inside-shared-ref-pat.rs:29:15 + | +LL | let &pat!(x) = &mut 0; + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` + +error[E0596]: cannot borrow as mutable inside an `&` pattern + --> $DIR/ref-mut-inside-shared-ref-pat.rs:34:19 + | +LL | let &(ref mut a, ref mut b) = &mut (true, false); + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` + +error[E0596]: cannot borrow as mutable inside an `&` pattern + --> $DIR/ref-mut-inside-shared-ref-pat.rs:34:30 + | +LL | let &(ref mut a, ref mut b) = &mut (true, false); + | - ^ + | | + | help: replace this `&` with `&mut`: `&mut` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0596`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref_pat_eat_one_layer_2021.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref_pat_eat_one_layer_2021.rs new file mode 100644 index 0000000000000..ab3264704acc0 --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref_pat_eat_one_layer_2021.rs @@ -0,0 +1,17 @@ +//@ run-pass +//@ edition: 2021 +//@ revisions: classic structural +#![allow(incomplete_features)] +#![cfg_attr(classic, feature(ref_pat_eat_one_layer_2024))] +#![cfg_attr(structural, feature(ref_pat_eat_one_layer_2024_structural))] + +pub fn main() { + #[cfg(structural)] + if let &Some(Some(x)) = &Some(&mut Some(0)) { + let _: &u32 = x; + } + + if let Some(&x) = Some(&mut 0) { + let _: u32 = x; + } +} diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2021_fail.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref_pat_eat_one_layer_2021_fail.rs similarity index 100% rename from tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2021_fail.rs rename to tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref_pat_eat_one_layer_2021_fail.rs diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2021_fail.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref_pat_eat_one_layer_2021_fail.stderr similarity index 87% rename from tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2021_fail.stderr rename to tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref_pat_eat_one_layer_2021_fail.stderr index 1a921234ea069..0158ed0f42357 100644 --- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2021_fail.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/ref_pat_eat_one_layer_2021_fail.stderr @@ -10,8 +10,9 @@ LL | if let Some(Some(&x)) = &Some(&Some(0)) { found reference `&_` help: consider removing `&` from the pattern | -LL | if let Some(Some(x)) = &Some(&Some(0)) { - | ~ +LL - if let Some(Some(&x)) = &Some(&Some(0)) { +LL + if let Some(Some(x)) = &Some(&Some(0)) { + | error[E0308]: mismatched types --> $DIR/ref_pat_eat_one_layer_2021_fail.rs:10:23 @@ -70,8 +71,9 @@ LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { | ^^^^^^ help: consider removing `&mut` from the pattern | -LL | if let Some(Some(x)) = &mut Some(&mut Some(0)) { - | ~ +LL - if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { +LL + if let Some(Some(x)) = &mut Some(&mut Some(0)) { + | error[E0308]: mismatched types --> $DIR/ref_pat_eat_one_layer_2021_fail.rs:25:22 @@ -85,8 +87,9 @@ LL | if let Some(Some(&x)) = &Some(&Some(0)) { found reference `&_` help: consider removing `&` from the pattern | -LL | if let Some(Some(x)) = &Some(&Some(0)) { - | ~ +LL - if let Some(Some(&x)) = &Some(&Some(0)) { +LL + if let Some(Some(x)) = &Some(&Some(0)) { + | error[E0308]: mismatched types --> $DIR/ref_pat_eat_one_layer_2021_fail.rs:29:27 @@ -100,8 +103,9 @@ LL | if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { found reference `&_` help: consider removing `&` from the pattern | -LL | if let Some(&mut Some(x)) = &Some(&mut Some(0)) { - | ~ +LL - if let Some(&mut Some(&x)) = &Some(&mut Some(0)) { +LL + if let Some(&mut Some(x)) = &Some(&mut Some(0)) { + | error[E0308]: mismatched types --> $DIR/ref_pat_eat_one_layer_2021_fail.rs:33:23 @@ -120,8 +124,9 @@ LL | if let Some(&Some(&mut x)) = &mut Some(&Some(0)) { | ^^^^^^ help: consider removing `&mut` from the pattern | -LL | if let Some(&Some(x)) = &mut Some(&Some(0)) { - | ~ +LL - if let Some(&Some(&mut x)) = &mut Some(&Some(0)) { +LL + if let Some(&Some(x)) = &mut Some(&Some(0)) { + | error: aborting due to 8 previous errors diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/trait-selection-sanity.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/trait-selection-sanity.rs new file mode 100644 index 0000000000000..cb8fdb489c028 --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/trait-selection-sanity.rs @@ -0,0 +1,30 @@ +//@ edition: 2024 +//@ revisions: with_impl without_impl +//@[with_impl] run-pass +//! Sanity check that experimental new pattern typing rules work as expected with trait selection + +fn main() { + fn generic() -> (R, bool) { + R::meow() + } + + trait Ref: Sized { + fn meow() -> (Self, bool); + } + + #[cfg(with_impl)] + impl Ref for &'static [(); 0] { + fn meow() -> (Self, bool) { + (&[], false) + } + } + + impl Ref for &'static mut [(); 0] { + fn meow() -> (Self, bool) { + (&mut [], true) + } + } + + let (&_, b) = generic(); //[without_impl]~ ERROR: the trait bound `&_: main::Ref` is not satisfied [E0277] + assert!(!b); +} diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/trait-selection-sanity.without_impl.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/trait-selection-sanity.without_impl.stderr new file mode 100644 index 0000000000000..83e45221facc7 --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/trait-selection-sanity.without_impl.stderr @@ -0,0 +1,16 @@ +error[E0277]: the trait bound `&_: main::Ref` is not satisfied + --> $DIR/trait-selection-sanity.rs:28:19 + | +LL | let (&_, b) = generic(); + | ^^^^^^^^^ the trait `main::Ref` is not implemented for `&_` + | + = help: the trait `main::Ref` is implemented for `&'static mut [(); 0]` +note: required by a bound in `generic` + --> $DIR/trait-selection-sanity.rs:7:19 + | +LL | fn generic() -> (R, bool) { + | ^^^ required by this bound in `generic` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs new file mode 100644 index 0000000000000..3114b9d3bf8cb --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.rs @@ -0,0 +1,127 @@ +//@ revisions: stable2021 classic2024 structural2024 +//@[stable2021] edition: 2021 +//@[classic2024] edition: 2024 +//@[structural2024] edition: 2024 +//@[classic2024] run-pass +//@[structural2024] run-pass +//! Test cases for well-typed patterns in edition 2024. These are in their own file to ensure we +//! pass both HIR typeck and MIR borrowck, as we may skip the latter if grouped with failing tests. +#![allow(incomplete_features, unused_mut)] +#![cfg_attr(classic2024, feature(ref_pat_eat_one_layer_2024))] +#![cfg_attr(structural2024, feature(ref_pat_eat_one_layer_2024_structural))] + +pub fn main() { + // Tests not using match ergonomics. These should always succeed with the same bindings. + if let Some(&Some(&mut ref x)) = Some(&Some(&mut 0)) { + let _: &u32 = x; + } + + // Tests for differences in how many layers of reference are eaten by reference patterns + if let Some(Some(&x)) = &Some(Some(&0)) { + #[cfg(stable2021)] let _: u32 = x; + #[cfg(any(classic2024, structural2024))] let _: &u32 = x; + } + if let Some(&Some(x)) = &mut Some(&Some(0)) { + // This additionally tests that `&` patterns can eat inherited `&mut` refs. + // This is possible on stable when the real reference being eaten is of a `&` type. + #[cfg(stable2021)] let _: u32 = x; + #[cfg(any(classic2024, structural2024))] let _: &u32 = x; + } + if let Some(Some(&&x)) = &Some(Some(&0)) { + //[stable2021]~^ mismatched types + //[stable2021]~| expected integer, found `&_` + let _: u32 = x; + } + + // Tests for eating a lone inherited reference + if let Some(Some(&x)) = &Some(&Some(0)) { + //[stable2021]~^ mismatched types + //[stable2021]~| expected integer, found `&_` + let _: u32 = x; + } + if let Some(&Some(x)) = &Some(Some(0)) { + //[stable2021]~^ mismatched types + //[stable2021]~| expected `Option<{integer}>`, found `&_` + let _: u32 = x; + } + if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { + //[stable2021]~^ mismatched types + //[stable2021]~| expected integer, found `&mut _` + let _: u32 = x; + } + + // Tests for `&` patterns matching real `&mut` reference types + if let Some(&Some(&x)) = Some(&Some(&mut 0)) { + //[stable2021]~^ mismatched types + //[stable2021]~| types differ in mutability + let _: u32 = x; + } + + // Tests for eating only one layer and also eating a lone inherited reference + if let Some(&Some(&x)) = &Some(&Some(0)) { + //[stable2021]~^ mismatched types + //[stable2021]~| expected integer, found `&_` + let _: u32 = x; + } + + // Tests for `&` matching a lone inherited possibly-`&mut` reference + if let Some(&Some(Some(&x))) = &Some(Some(&mut Some(0))) { + //[stable2021]~^ mismatched types + //[stable2021]~| expected `Option<&mut Option<{integer}>>`, found `&_` + let _: u32 = x; + } + if let Some(&Some(x)) = &mut Some(Some(0)) { + //[stable2021]~^ mismatched types + //[stable2021]~| expected `Option<{integer}>`, found `&_` + let _: u32 = x; + } + + // Tests eating one layer, eating a lone inherited ref, and `&` eating `&mut` (realness varies) + if let Some(&Some(&x)) = &Some(&mut Some(0)) { + //[stable2021]~^ mismatched types + //[stable2021]~| types differ in mutability + let _: u32 = x; + } + if let Some(&Some(&x)) = &mut Some(&Some(0)) { + //[stable2021]~^ mismatched types + //[stable2021]~| expected integer, found `&_` + let _: u32 = x; + } + + // Tests for eat-inner rulesets matching on the outer reference if matching on the inner + // reference causes a mutability mismatch, i.e. `Deref(EatInner, FallbackToOuter)`: + let [&mut x] = &mut [&0]; + //[stable2021]~^ mismatched types + //[stable2021]~| types differ in mutability + let _: &u32 = x; + + let [&mut ref x] = &mut [&0]; + //[stable2021]~^ mismatched types + //[stable2021]~| types differ in mutability + let _: &&u32 = x; + + let [&mut ref mut x] = &mut [&0]; + //[stable2021]~^ mismatched types + //[stable2021]~| types differ in mutability + let _: &mut &u32 = x; + + let [&mut mut x] = &mut [&0]; + //[stable2021]~^ mismatched types + //[stable2021]~| types differ in mutability + let _: &u32 = x; + + let [&mut &x] = &mut [&0]; + //[stable2021]~^ mismatched types + //[stable2021]~| types differ in mutability + let _: u32 = x; + + let [&mut &ref x] = &mut [&0]; + //[stable2021]~^ mismatched types + //[stable2021]~| types differ in mutability + let _: &u32 = x; + + let [&mut &(mut x)] = &mut [&0]; + //[stable2021]~^ mismatched types + //[stable2021]~| types differ in mutability + let _: u32 = x; +} diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.stable2021.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.stable2021.stderr new file mode 100644 index 0000000000000..b1a8024397bdc --- /dev/null +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/well-typed-edition-2024.stable2021.stderr @@ -0,0 +1,265 @@ +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:30:23 + | +LL | if let Some(Some(&&x)) = &Some(Some(&0)) { + | ^^ --------------- this expression has type `&Option>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - if let Some(Some(&&x)) = &Some(Some(&0)) { +LL + if let Some(Some(&x)) = &Some(Some(&0)) { + | + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:37:22 + | +LL | if let Some(Some(&x)) = &Some(&Some(0)) { + | ^^ --------------- this expression has type `&Option<&Option<{integer}>>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - if let Some(Some(&x)) = &Some(&Some(0)) { +LL + if let Some(Some(x)) = &Some(&Some(0)) { + | + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:42:17 + | +LL | if let Some(&Some(x)) = &Some(Some(0)) { + | ^^^^^^^^ -------------- this expression has type `&Option>` + | | + | expected `Option<{integer}>`, found `&_` + | + = note: expected enum `Option<{integer}>` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:47:22 + | +LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { + | ^^^^^^ ----------------------- this expression has type `&mut Option<&mut Option<{integer}>>` + | | + | expected integer, found `&mut _` + | + = note: expected type `{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/well-typed-edition-2024.rs:47:22 + | +LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { + | ^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) { +LL + if let Some(Some(x)) = &mut Some(&mut Some(0)) { + | + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:54:23 + | +LL | if let Some(&Some(&x)) = Some(&Some(&mut 0)) { + | ^^ ------------------- this expression has type `Option<&Option<&mut {integer}>>` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut {integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - if let Some(&Some(&x)) = Some(&Some(&mut 0)) { +LL + if let Some(&Some(x)) = Some(&Some(&mut 0)) { + | + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:61:23 + | +LL | if let Some(&Some(&x)) = &Some(&Some(0)) { + | ^^ --------------- this expression has type `&Option<&Option<{integer}>>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - if let Some(&Some(&x)) = &Some(&Some(0)) { +LL + if let Some(&Some(x)) = &Some(&Some(0)) { + | + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:68:17 + | +LL | if let Some(&Some(Some(&x))) = &Some(Some(&mut Some(0))) { + | ^^^^^^^^^^^^^^^ ------------------------- this expression has type `&Option>>` + | | + | expected `Option<&mut Option<{integer}>>`, found `&_` + | + = note: expected enum `Option<&mut Option<{integer}>>` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:73:17 + | +LL | if let Some(&Some(x)) = &mut Some(Some(0)) { + | ^^^^^^^^ ------------------ this expression has type `&mut Option>` + | | + | expected `Option<{integer}>`, found `&_` + | + = note: expected enum `Option<{integer}>` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:80:17 + | +LL | if let Some(&Some(&x)) = &Some(&mut Some(0)) { + | ^^^^^^^^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>` + | | + | types differ in mutability + | + = note: expected mutable reference `&mut Option<{integer}>` + found reference `&_` + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:85:23 + | +LL | if let Some(&Some(&x)) = &mut Some(&Some(0)) { + | ^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>` + | | + | expected integer, found `&_` + | + = note: expected type `{integer}` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - if let Some(&Some(&x)) = &mut Some(&Some(0)) { +LL + if let Some(&Some(x)) = &mut Some(&Some(0)) { + | + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:93:10 + | +LL | let [&mut x] = &mut [&0]; + | ^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` + | | + | types differ in mutability + | + = note: expected reference `&{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/well-typed-edition-2024.rs:93:10 + | +LL | let [&mut x] = &mut [&0]; + | ^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - let [&mut x] = &mut [&0]; +LL + let [x] = &mut [&0]; + | + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:98:10 + | +LL | let [&mut ref x] = &mut [&0]; + | ^^^^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` + | | + | types differ in mutability + | + = note: expected reference `&{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/well-typed-edition-2024.rs:98:10 + | +LL | let [&mut ref x] = &mut [&0]; + | ^^^^^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - let [&mut ref x] = &mut [&0]; +LL + let [ref x] = &mut [&0]; + | + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:103:10 + | +LL | let [&mut ref mut x] = &mut [&0]; + | ^^^^^^^^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` + | | + | types differ in mutability + | + = note: expected reference `&{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/well-typed-edition-2024.rs:103:10 + | +LL | let [&mut ref mut x] = &mut [&0]; + | ^^^^^^^^^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - let [&mut ref mut x] = &mut [&0]; +LL + let [ref mut x] = &mut [&0]; + | + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:108:10 + | +LL | let [&mut mut x] = &mut [&0]; + | ^^^^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` + | | + | types differ in mutability + | + = note: expected reference `&{integer}` + found mutable reference `&mut _` +note: to declare a mutable binding use: `mut x` + --> $DIR/well-typed-edition-2024.rs:108:10 + | +LL | let [&mut mut x] = &mut [&0]; + | ^^^^^^^^^^ +help: consider removing `&mut` from the pattern + | +LL - let [&mut mut x] = &mut [&0]; +LL + let [mut x] = &mut [&0]; + | + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:113:10 + | +LL | let [&mut &x] = &mut [&0]; + | ^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` + | | + | types differ in mutability + | + = note: expected reference `&{integer}` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:118:10 + | +LL | let [&mut &ref x] = &mut [&0]; + | ^^^^^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` + | | + | types differ in mutability + | + = note: expected reference `&{integer}` + found mutable reference `&mut _` + +error[E0308]: mismatched types + --> $DIR/well-typed-edition-2024.rs:123:10 + | +LL | let [&mut &(mut x)] = &mut [&0]; + | ^^^^^^^^^^^^^ --------- this expression has type `&mut [&{integer}; 1]` + | | + | types differ in mutability + | + = note: expected reference `&{integer}` + found mutable reference `&mut _` + +error: aborting due to 17 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed index e2b2c98761046..0a22e939496e5 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.fixed @@ -23,22 +23,22 @@ fn main() { assert_type_eq(x, &mut 0u8); let &Foo(mut x) = &Foo(0); - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let &mut Foo(mut x) = &mut Foo(0); - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); - let &Foo(ref x) = &Foo(0); - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + let Foo(x) = &Foo(0); + //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, &0u8); let &mut Foo(ref x) = &mut Foo(0); - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, &0u8); @@ -55,22 +55,22 @@ fn main() { assert_type_eq(x, &0u8); let &Foo(&x) = &Foo(&0); - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let &Foo(&mut x) = &Foo(&mut 0); - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let &mut Foo(&x) = &mut Foo(&0); - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let &mut Foo(&mut x) = &mut Foo(&mut 0); - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); @@ -79,25 +79,25 @@ fn main() { } if let &&&&&Some(&x) = &&&&&Some(&0u8) { - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); } if let &&&&&Some(&mut x) = &&&&&Some(&mut 0u8) { - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); } if let &&&&&mut Some(&x) = &&&&&mut Some(&0u8) { - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); } if let &mut Some(&mut Some(&mut Some(ref mut x))) = &mut Some(&mut Some(&mut Some(0u8))) { - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, &mut 0u8); } @@ -109,20 +109,20 @@ fn main() { } let &Struct { ref a, mut b, ref c } = &Struct { a: 0, b: 0, c: 0 }; - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); assert_type_eq(b, 0u32); let &Struct { a: &a, ref b, ref c } = &Struct { a: &0, b: &0, c: &0 }; - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: binding modifiers and reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, 0u32); assert_type_eq(b, &&0u32); assert_type_eq(c, &&0u32); if let &Struct { a: &Some(a), b: &Some(&b), c: &Some(ref c) } = - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 &(Struct { a: &Some(&0), b: &Some(&0), c: &Some(&0) }) { @@ -135,10 +135,108 @@ fn main() { // The two patterns are the same syntactically, but because they're defined in different // editions they don't mean the same thing. &(Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => { - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` assert_type_eq(x, 0u32); assert_type_eq(y, 0u32); } _ => {} } + + let &mut [&mut &[ref a]] = &mut [&mut &[0]]; + //~^ ERROR: binding modifiers and reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, &0u32); + + let &[&(_)] = &[&0]; + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + + // NB: Most of the following tests are for possible future improvements to migration suggestions + + // Test removing multiple binding modifiers. + let Struct { a, b, c } = &Struct { a: 0, b: 0, c: 0 }; + //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, &0u32); + assert_type_eq(c, &0u32); + + // Test that we don't change bindings' modes when removing binding modifiers. + let &mut Struct { ref a, ref mut b, ref mut c } = &mut Struct { a: 0, b: 0, c: 0 }; + //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, &0u32); + assert_type_eq(b, &mut 0u32); + assert_type_eq(c, &mut 0u32); + + // Test removing multiple reference patterns of various mutabilities, plus a binding modifier. + let &mut &Struct { a: &[ref a], b: &mut [&[ref b]], ref c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 }; + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, &0u32); + assert_type_eq(b, &0u32); + assert_type_eq(c, &0u32); + + // Test that we don't change bindings' types when removing reference patterns. + let &Foo(&ref a) = &Foo(&0); + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, &0u32); + + // Test that we don't change bindings' modes when adding reference paterns (caught early). + let &(&a, ref b, &[ref c], &mut [&mut (ref d, &[ref e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]); + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, 0u32); + assert_type_eq(b, &0u32); + assert_type_eq(c, &0u32); + assert_type_eq(d, &0u32); + assert_type_eq(e, &0u32); + + // Test that we don't change bindings' modes when adding reference patterns (caught late). + let &(ref a, &mut [ref b], &[mut c]) = &(0, &mut [0], &[0]); + //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, &0u32); + assert_type_eq(b, &0u32); + assert_type_eq(c, 0u32); + + // Test featuring both additions and removals. + let &(&a, &mut (ref b, &[ref c])) = &(&0, &mut (0, &[0])); + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, 0u32); + assert_type_eq(b, &0u32); + assert_type_eq(c, &0u32); + + // Test that bindings' subpatterns' modes are updated properly. + let &[mut a @ ref b] = &[0]; + //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, 0u32); + assert_type_eq(b, &0u32); + + // Test that bindings' subpatterns' modes are checked properly. + let &[ref a @ mut b] = &[0]; + //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, &0u32); + assert_type_eq(b, 0u32); + + // Test that we respect bindings' subpatterns' types when rewriting `&ref x` to `x`. + let [&Foo(&ref a @ ref b), &Foo(&ref c @ d)] = [&Foo(&0); 2]; + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, &0u32); + assert_type_eq(b, &0u32); + assert_type_eq(c, &0u32); + assert_type_eq(d, 0u32); + + // Test that we respect bindings' subpatterns' modes when rewriting `&ref x` to `x`. + let [&Foo(&ref a @ [ref b]), &Foo(&ref c @ [d])] = [&Foo(&[0]); 2]; + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, &[0u32]); + assert_type_eq(b, &0u32); + assert_type_eq(c, &[0u32]); + assert_type_eq(d, 0u32); } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs index 098540adfa2cf..7a6f2269d44a2 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.rs @@ -23,22 +23,22 @@ fn main() { assert_type_eq(x, &mut 0u8); let Foo(mut x) = &Foo(0); - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let Foo(mut x) = &mut Foo(0); - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let Foo(ref x) = &Foo(0); - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, &0u8); let Foo(ref x) = &mut Foo(0); - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, &0u8); @@ -55,22 +55,22 @@ fn main() { assert_type_eq(x, &0u8); let Foo(&x) = &Foo(&0); - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let Foo(&mut x) = &Foo(&mut 0); - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let Foo(&x) = &mut Foo(&0); - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); let Foo(&mut x) = &mut Foo(&mut 0); - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); @@ -79,25 +79,25 @@ fn main() { } if let Some(&x) = &&&&&Some(&0u8) { - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); } if let Some(&mut x) = &&&&&Some(&mut 0u8) { - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); } if let Some(&x) = &&&&&mut Some(&0u8) { - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, 0u8); } if let Some(&mut Some(Some(x))) = &mut Some(&mut Some(&mut Some(0u8))) { - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(x, &mut 0u8); } @@ -109,20 +109,20 @@ fn main() { } let Struct { a, mut b, c } = &Struct { a: 0, b: 0, c: 0 }; - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, &0u32); assert_type_eq(b, 0u32); let Struct { a: &a, b, ref c } = &Struct { a: &0, b: &0, c: &0 }; - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: binding modifiers and reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 assert_type_eq(a, 0u32); assert_type_eq(b, &&0u32); assert_type_eq(c, &&0u32); if let Struct { a: &Some(a), b: Some(&b), c: Some(c) } = - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 //~| WARN: this changes meaning in Rust 2024 &(Struct { a: &Some(&0), b: &Some(&0), c: &Some(&0) }) { @@ -135,10 +135,108 @@ fn main() { // The two patterns are the same syntactically, but because they're defined in different // editions they don't mean the same thing. (Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => { - //~^ ERROR: this pattern relies on behavior which may change in edition 2024 + //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` assert_type_eq(x, 0u32); assert_type_eq(y, 0u32); } _ => {} } + + let [&mut [ref a]] = &mut [&mut &[0]]; + //~^ ERROR: binding modifiers and reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, &0u32); + + let [&(_)] = &[&0]; + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + + // NB: Most of the following tests are for possible future improvements to migration suggestions + + // Test removing multiple binding modifiers. + let Struct { ref a, ref b, c } = &Struct { a: 0, b: 0, c: 0 }; + //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, &0u32); + assert_type_eq(c, &0u32); + + // Test that we don't change bindings' modes when removing binding modifiers. + let Struct { ref a, ref mut b, c } = &mut Struct { a: 0, b: 0, c: 0 }; + //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, &0u32); + assert_type_eq(b, &mut 0u32); + assert_type_eq(c, &mut 0u32); + + // Test removing multiple reference patterns of various mutabilities, plus a binding modifier. + let Struct { a: &[ref a], b: &mut [[b]], c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 }; + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, &0u32); + assert_type_eq(b, &0u32); + assert_type_eq(c, &0u32); + + // Test that we don't change bindings' types when removing reference patterns. + let Foo(&ref a) = &Foo(&0); + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, &0u32); + + // Test that we don't change bindings' modes when adding reference paterns (caught early). + let (&a, b, [c], [(d, [e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]); + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, 0u32); + assert_type_eq(b, &0u32); + assert_type_eq(c, &0u32); + assert_type_eq(d, &0u32); + assert_type_eq(e, &0u32); + + // Test that we don't change bindings' modes when adding reference patterns (caught late). + let (a, [b], [mut c]) = &(0, &mut [0], &[0]); + //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, &0u32); + assert_type_eq(b, &0u32); + assert_type_eq(c, 0u32); + + // Test featuring both additions and removals. + let (&a, (b, &[ref c])) = &(&0, &mut (0, &[0])); + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, 0u32); + assert_type_eq(b, &0u32); + assert_type_eq(c, &0u32); + + // Test that bindings' subpatterns' modes are updated properly. + let [mut a @ b] = &[0]; + //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, 0u32); + assert_type_eq(b, &0u32); + + // Test that bindings' subpatterns' modes are checked properly. + let [a @ mut b] = &[0]; + //~^ ERROR: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, &0u32); + assert_type_eq(b, 0u32); + + // Test that we respect bindings' subpatterns' types when rewriting `&ref x` to `x`. + let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2]; + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, &0u32); + assert_type_eq(b, &0u32); + assert_type_eq(c, &0u32); + assert_type_eq(d, 0u32); + + // Test that we respect bindings' subpatterns' modes when rewriting `&ref x` to `x`. + let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2]; + //~^ ERROR: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + assert_type_eq(a, &[0u32]); + assert_type_eq(b, &0u32); + assert_type_eq(c, &[0u32]); + assert_type_eq(d, 0u32); } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr index 83346b9dd4a84..191800df07a2e 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/migration_lint.stderr @@ -1,11 +1,16 @@ -error: this pattern relies on behavior which may change in edition 2024 +error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 --> $DIR/migration_lint.rs:25:13 | LL | let Foo(mut x) = &Foo(0); - | ^^^ requires binding by-value, but the implicit default is by-reference + | ^^^ binding modifier not allowed under `ref` default binding mode | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:25:9 + | +LL | let Foo(mut x) = &Foo(0); + | ^^^^^^^^^^ this matches on type `&_` note: the lint level is defined here --> $DIR/migration_lint.rs:7:9 | @@ -16,206 +21,546 @@ help: make the implied reference pattern explicit LL | let &Foo(mut x) = &Foo(0); | + -error: this pattern relies on behavior which may change in edition 2024 +error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 --> $DIR/migration_lint.rs:30:13 | LL | let Foo(mut x) = &mut Foo(0); - | ^^^ requires binding by-value, but the implicit default is by-reference + | ^^^ binding modifier not allowed under `ref mut` default binding mode | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:30:9 + | +LL | let Foo(mut x) = &mut Foo(0); + | ^^^^^^^^^^ this matches on type `&mut _` help: make the implied reference pattern explicit | LL | let &mut Foo(mut x) = &mut Foo(0); | ++++ -error: this pattern relies on behavior which may change in edition 2024 +error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 --> $DIR/migration_lint.rs:35:13 | LL | let Foo(ref x) = &Foo(0); - | ^^^ cannot override to bind by-reference when that is the implicit default + | ^^^ binding modifier not allowed under `ref` default binding mode | = warning: this changes meaning in Rust 2024 = note: for more information, see -help: make the implied reference pattern explicit +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:35:9 + | +LL | let Foo(ref x) = &Foo(0); + | ^^^^^^^^^^ this matches on type `&_` +help: remove the unnecessary binding modifier + | +LL - let Foo(ref x) = &Foo(0); +LL + let Foo(x) = &Foo(0); | -LL | let &Foo(ref x) = &Foo(0); - | + -error: this pattern relies on behavior which may change in edition 2024 +error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 --> $DIR/migration_lint.rs:40:13 | LL | let Foo(ref x) = &mut Foo(0); - | ^^^ cannot override to bind by-reference when that is the implicit default + | ^^^ binding modifier not allowed under `ref mut` default binding mode | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:40:9 + | +LL | let Foo(ref x) = &mut Foo(0); + | ^^^^^^^^^^ this matches on type `&mut _` help: make the implied reference pattern explicit | LL | let &mut Foo(ref x) = &mut Foo(0); | ++++ -error: this pattern relies on behavior which may change in edition 2024 +error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 --> $DIR/migration_lint.rs:57:13 | LL | let Foo(&x) = &Foo(&0); - | ^ cannot implicitly match against multiple layers of reference + | ^ reference pattern not allowed under `ref` default binding mode | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:57:9 + | +LL | let Foo(&x) = &Foo(&0); + | ^^^^^^^ this matches on type `&_` help: make the implied reference pattern explicit | LL | let &Foo(&x) = &Foo(&0); | + -error: this pattern relies on behavior which may change in edition 2024 +error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 --> $DIR/migration_lint.rs:62:13 | LL | let Foo(&mut x) = &Foo(&mut 0); - | ^^^^ cannot implicitly match against multiple layers of reference + | ^^^^ reference pattern not allowed under `ref` default binding mode | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:62:9 + | +LL | let Foo(&mut x) = &Foo(&mut 0); + | ^^^^^^^^^^^ this matches on type `&_` help: make the implied reference pattern explicit | LL | let &Foo(&mut x) = &Foo(&mut 0); | + -error: this pattern relies on behavior which may change in edition 2024 +error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 --> $DIR/migration_lint.rs:67:13 | LL | let Foo(&x) = &mut Foo(&0); - | ^ cannot implicitly match against multiple layers of reference + | ^ reference pattern not allowed under `ref mut` default binding mode | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:67:9 + | +LL | let Foo(&x) = &mut Foo(&0); + | ^^^^^^^ this matches on type `&mut _` help: make the implied reference pattern explicit | LL | let &mut Foo(&x) = &mut Foo(&0); | ++++ -error: this pattern relies on behavior which may change in edition 2024 +error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 --> $DIR/migration_lint.rs:72:13 | LL | let Foo(&mut x) = &mut Foo(&mut 0); - | ^^^^ cannot implicitly match against multiple layers of reference + | ^^^^ reference pattern not allowed under `ref mut` default binding mode | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:72:9 + | +LL | let Foo(&mut x) = &mut Foo(&mut 0); + | ^^^^^^^^^^^ this matches on type `&mut _` help: make the implied reference pattern explicit | LL | let &mut Foo(&mut x) = &mut Foo(&mut 0); | ++++ -error: this pattern relies on behavior which may change in edition 2024 +error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 --> $DIR/migration_lint.rs:81:17 | LL | if let Some(&x) = &&&&&Some(&0u8) { - | ^ cannot implicitly match against multiple layers of reference + | ^ reference pattern not allowed under `ref` default binding mode | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:81:12 + | +LL | if let Some(&x) = &&&&&Some(&0u8) { + | ^^^^^^^^ this matches on type `&_` help: make the implied reference patterns explicit | LL | if let &&&&&Some(&x) = &&&&&Some(&0u8) { | +++++ -error: this pattern relies on behavior which may change in edition 2024 +error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 --> $DIR/migration_lint.rs:87:17 | LL | if let Some(&mut x) = &&&&&Some(&mut 0u8) { - | ^^^^ cannot implicitly match against multiple layers of reference + | ^^^^ reference pattern not allowed under `ref` default binding mode | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:87:12 + | +LL | if let Some(&mut x) = &&&&&Some(&mut 0u8) { + | ^^^^^^^^^^^^ this matches on type `&_` help: make the implied reference patterns explicit | LL | if let &&&&&Some(&mut x) = &&&&&Some(&mut 0u8) { | +++++ -error: this pattern relies on behavior which may change in edition 2024 +error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 --> $DIR/migration_lint.rs:93:17 | LL | if let Some(&x) = &&&&&mut Some(&0u8) { - | ^ cannot implicitly match against multiple layers of reference + | ^ reference pattern not allowed under `ref` default binding mode | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:93:12 + | +LL | if let Some(&x) = &&&&&mut Some(&0u8) { + | ^^^^^^^^ this matches on type `&_` help: make the implied reference patterns explicit | LL | if let &&&&&mut Some(&x) = &&&&&mut Some(&0u8) { | ++++++++ -error: this pattern relies on behavior which may change in edition 2024 +error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 --> $DIR/migration_lint.rs:99:17 | LL | if let Some(&mut Some(Some(x))) = &mut Some(&mut Some(&mut Some(0u8))) { - | ^^^^ cannot implicitly match against multiple layers of reference + | ^^^^ reference pattern not allowed under `ref mut` default binding mode | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:99:12 + | +LL | if let Some(&mut Some(Some(x))) = &mut Some(&mut Some(&mut Some(0u8))) { + | ^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&mut _` help: make the implied reference patterns and variable binding mode explicit | LL | if let &mut Some(&mut Some(&mut Some(ref mut x))) = &mut Some(&mut Some(&mut Some(0u8))) { | ++++ ++++ +++++++ -error: this pattern relies on behavior which may change in edition 2024 +error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 --> $DIR/migration_lint.rs:111:21 | LL | let Struct { a, mut b, c } = &Struct { a: 0, b: 0, c: 0 }; - | ^^^ requires binding by-value, but the implicit default is by-reference + | ^^^ binding modifier not allowed under `ref` default binding mode | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:111:9 + | +LL | let Struct { a, mut b, c } = &Struct { a: 0, b: 0, c: 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` help: make the implied reference pattern and variable binding modes explicit | LL | let &Struct { ref a, mut b, ref c } = &Struct { a: 0, b: 0, c: 0 }; | + +++ +++ -error: this pattern relies on behavior which may change in edition 2024 +error: binding modifiers and reference patterns may only be written when the default binding mode is `move` in Rust 2024 --> $DIR/migration_lint.rs:117:21 | LL | let Struct { a: &a, b, ref c } = &Struct { a: &0, b: &0, c: &0 }; - | ^ ^^^ cannot override to bind by-reference when that is the implicit default + | ^ ^^^ binding modifier not allowed under `ref` default binding mode | | - | cannot implicitly match against multiple layers of reference + | reference pattern not allowed under `ref` default binding mode | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:117:9 + | +LL | let Struct { a: &a, b, ref c } = &Struct { a: &0, b: &0, c: &0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` help: make the implied reference pattern and variable binding mode explicit | LL | let &Struct { a: &a, ref b, ref c } = &Struct { a: &0, b: &0, c: &0 }; | + +++ -error: this pattern relies on behavior which may change in edition 2024 +error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 --> $DIR/migration_lint.rs:124:24 | LL | if let Struct { a: &Some(a), b: Some(&b), c: Some(c) } = - | ^ ^ cannot implicitly match against multiple layers of reference + | ^ ^ reference pattern not allowed under `ref` default binding mode | | - | cannot implicitly match against multiple layers of reference + | reference pattern not allowed under `ref` default binding mode | = warning: this changes meaning in Rust 2024 = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:124:12 + | +LL | if let Struct { a: &Some(a), b: Some(&b), c: Some(c) } = + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` help: make the implied reference patterns and variable binding mode explicit | LL | if let &Struct { a: &Some(a), b: &Some(&b), c: &Some(ref c) } = | + + + +++ -error: this pattern relies on behavior which may change in edition 2024 +error: binding modifiers may only be written when the default binding mode is `move` --> $DIR/migration_lint.rs:137:15 | LL | (Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => { - | ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ default binding mode is reset within expansion + | ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ occurs within macro expansion | | - | requires binding by-value, but the implicit default is by-reference + | binding modifier not allowed under `ref` default binding mode | = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:137:9 + | +LL | (Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` = note: this error originates in the macro `migration_lint_macros::mixed_edition_pat` (in Nightly builds, run with -Z macro-backtrace for more info) help: make the implied reference pattern explicit | LL | &(Some(mut x), migration_lint_macros::mixed_edition_pat!(y)) => { | + -error: aborting due to 16 previous errors +error: binding modifiers and reference patterns may only be written when the default binding mode is `move` in Rust 2024 + --> $DIR/migration_lint.rs:145:10 + | +LL | let [&mut [ref a]] = &mut [&mut &[0]]; + | ^^^^ ^^^ binding modifier not allowed under `ref` default binding mode + | | + | reference pattern not allowed under `ref mut` default binding mode + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:145:15 + | +LL | let [&mut [ref a]] = &mut [&mut &[0]]; + | ^^^^^^^ this matches on type `&_` +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:145:9 + | +LL | let [&mut [ref a]] = &mut [&mut &[0]]; + | ^^^^^^^^^^^^^^ this matches on type `&mut _` +help: make the implied reference patterns explicit + | +LL | let &mut [&mut &[ref a]] = &mut [&mut &[0]]; + | ++++ + + +error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + --> $DIR/migration_lint.rs:150:10 + | +LL | let [&(_)] = &[&0]; + | ^ reference pattern not allowed under `ref` default binding mode + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:150:9 + | +LL | let [&(_)] = &[&0]; + | ^^^^^^ this matches on type `&_` +help: make the implied reference pattern explicit + | +LL | let &[&(_)] = &[&0]; + | + + +error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + --> $DIR/migration_lint.rs:157:18 + | +LL | let Struct { ref a, ref b, c } = &Struct { a: 0, b: 0, c: 0 }; + | ^^^ ^^^ binding modifier not allowed under `ref` default binding mode + | | + | binding modifier not allowed under `ref` default binding mode + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:157:9 + | +LL | let Struct { ref a, ref b, c } = &Struct { a: 0, b: 0, c: 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` +help: remove the unnecessary binding modifiers + | +LL - let Struct { ref a, ref b, c } = &Struct { a: 0, b: 0, c: 0 }; +LL + let Struct { a, b, c } = &Struct { a: 0, b: 0, c: 0 }; + | + +error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + --> $DIR/migration_lint.rs:164:18 + | +LL | let Struct { ref a, ref mut b, c } = &mut Struct { a: 0, b: 0, c: 0 }; + | ^^^ ^^^^^^^ binding modifier not allowed under `ref mut` default binding mode + | | + | binding modifier not allowed under `ref mut` default binding mode + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:164:9 + | +LL | let Struct { ref a, ref mut b, c } = &mut Struct { a: 0, b: 0, c: 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&mut _` +help: make the implied reference pattern and variable binding mode explicit + | +LL | let &mut Struct { ref a, ref mut b, ref mut c } = &mut Struct { a: 0, b: 0, c: 0 }; + | ++++ +++++++ + +error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + --> $DIR/migration_lint.rs:172:21 + | +LL | let Struct { a: &[ref a], b: &mut [[b]], c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 }; + | ^ ^^^^ reference pattern not allowed under `ref` default binding mode + | | + | reference pattern not allowed under `ref` default binding mode + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:172:9 + | +LL | let Struct { a: &[ref a], b: &mut [[b]], c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` +help: make the implied reference patterns and variable binding modes explicit + | +LL | let &mut &Struct { a: &[ref a], b: &mut [&[ref b]], ref c } = &mut &Struct { a: &[0], b: &mut [&[0]], c: 0 }; + | ++++++ + +++ +++ + +error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + --> $DIR/migration_lint.rs:180:13 + | +LL | let Foo(&ref a) = &Foo(&0); + | ^ reference pattern not allowed under `ref` default binding mode + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:180:9 + | +LL | let Foo(&ref a) = &Foo(&0); + | ^^^^^^^^^^^ this matches on type `&_` +help: make the implied reference pattern explicit + | +LL | let &Foo(&ref a) = &Foo(&0); + | + + +error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + --> $DIR/migration_lint.rs:186:10 + | +LL | let (&a, b, [c], [(d, [e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]); + | ^ reference pattern not allowed under `ref` default binding mode + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:186:9 + | +LL | let (&a, b, [c], [(d, [e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]); + | ^^^^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` +help: make the implied reference patterns and variable binding modes explicit + | +LL | let &(&a, ref b, &[ref c], &mut [&mut (ref d, &[ref e])]) = &(&0, 0, &[0], &mut [&mut (0, &[0])]); + | + +++ + +++ ++++ ++++ +++ + +++ + +error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + --> $DIR/migration_lint.rs:196:19 + | +LL | let (a, [b], [mut c]) = &(0, &mut [0], &[0]); + | ^^^ binding modifier not allowed under `ref` default binding mode + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:196:9 + | +LL | let (a, [b], [mut c]) = &(0, &mut [0], &[0]); + | ^^^^^^^^^^^^^^^^^ this matches on type `&_` +help: make the implied reference patterns and variable binding modes explicit + | +LL | let &(ref a, &mut [ref b], &[mut c]) = &(0, &mut [0], &[0]); + | + +++ ++++ +++ + + +error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + --> $DIR/migration_lint.rs:204:10 + | +LL | let (&a, (b, &[ref c])) = &(&0, &mut (0, &[0])); + | ^ ^ reference pattern not allowed under `ref` default binding mode + | | + | reference pattern not allowed under `ref` default binding mode + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:204:9 + | +LL | let (&a, (b, &[ref c])) = &(&0, &mut (0, &[0])); + | ^^^^^^^^^^^^^^^^^^^ this matches on type `&_` +help: make the implied reference patterns and variable binding mode explicit + | +LL | let &(&a, &mut (ref b, &[ref c])) = &(&0, &mut (0, &[0])); + | + ++++ +++ + +error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + --> $DIR/migration_lint.rs:212:10 + | +LL | let [mut a @ b] = &[0]; + | ^^^ binding modifier not allowed under `ref` default binding mode + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:212:9 + | +LL | let [mut a @ b] = &[0]; + | ^^^^^^^^^^^ this matches on type `&_` +help: make the implied reference pattern and variable binding mode explicit + | +LL | let &[mut a @ ref b] = &[0]; + | + +++ + +error: binding modifiers may only be written when the default binding mode is `move` in Rust 2024 + --> $DIR/migration_lint.rs:219:14 + | +LL | let [a @ mut b] = &[0]; + | ^^^ binding modifier not allowed under `ref` default binding mode + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:219:9 + | +LL | let [a @ mut b] = &[0]; + | ^^^^^^^^^^^ this matches on type `&_` +help: make the implied reference pattern and variable binding mode explicit + | +LL | let &[ref a @ mut b] = &[0]; + | + +++ + +error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + --> $DIR/migration_lint.rs:226:14 + | +LL | let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2]; + | ^ ^ reference pattern not allowed under `ref` default binding mode + | | + | reference pattern not allowed under `ref` default binding mode + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:226:31 + | +LL | let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2]; + | ^^^^^^^^^^^^^^^ this matches on type `&_` +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:226:10 + | +LL | let [Foo(&ref a @ ref b), Foo(&ref c @ d)] = [&Foo(&0); 2]; + | ^^^^^^^^^^^^^^^^^^^ this matches on type `&_` +help: make the implied reference patterns explicit + | +LL | let [&Foo(&ref a @ ref b), &Foo(&ref c @ d)] = [&Foo(&0); 2]; + | + + + +error: reference patterns may only be written when the default binding mode is `move` in Rust 2024 + --> $DIR/migration_lint.rs:235:14 + | +LL | let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2]; + | ^ ^ reference pattern not allowed under `ref` default binding mode + | | + | reference pattern not allowed under `ref` default binding mode + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:235:33 + | +LL | let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2]; + | ^^^^^^^^^^^^^^^^^ this matches on type `&_` +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/migration_lint.rs:235:10 + | +LL | let [Foo(&ref a @ [ref b]), Foo(&ref c @ [d])] = [&Foo(&[0]); 2]; + | ^^^^^^^^^^^^^^^^^^^^^ this matches on type `&_` +help: make the implied reference patterns explicit + | +LL | let [&Foo(&ref a @ [ref b]), &Foo(&ref c @ [d])] = [&Foo(&[0]); 2]; + | + + + +error: aborting due to 29 previous errors diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.rs b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.rs index 5ba554fc6e5a4..4dc04d90aaf5e 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.rs +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.rs @@ -21,17 +21,17 @@ macro_rules! test_pat_on_type { } test_pat_on_type![(&x,): &(T,)]; //~ ERROR mismatched types -test_pat_on_type![(&x,): &(&T,)]; //~ ERROR this pattern relies on behavior which may change in edition 2024 +test_pat_on_type![(&x,): &(&T,)]; //~ ERROR reference patterns may only be written when the default binding mode is `move` test_pat_on_type![(&x,): &(&mut T,)]; //~ ERROR mismatched types test_pat_on_type![(&mut x,): &(&T,)]; //~ ERROR mismatched types -test_pat_on_type![(&mut x,): &(&mut T,)]; //~ ERROR this pattern relies on behavior which may change in edition 2024 +test_pat_on_type![(&mut x,): &(&mut T,)]; //~ ERROR reference patterns may only be written when the default binding mode is `move` test_pat_on_type![(&x,): &&mut &(T,)]; //~ ERROR mismatched types test_pat_on_type![Foo { f: (&x,) }: Foo]; //~ ERROR mismatched types test_pat_on_type![Foo { f: (&x,) }: &mut Foo]; //~ ERROR mismatched types -test_pat_on_type![Foo { f: &(x,) }: &Foo]; //~ ERROR this pattern relies on behavior which may change in edition 2024 -test_pat_on_type![(mut x,): &(T,)]; //~ ERROR this pattern relies on behavior which may change in edition 2024 -test_pat_on_type![(ref x,): &(T,)]; //~ ERROR this pattern relies on behavior which may change in edition 2024 -test_pat_on_type![(ref mut x,): &mut (T,)]; //~ ERROR this pattern relies on behavior which may change in edition 2024 +test_pat_on_type![Foo { f: &(x,) }: &Foo]; //~ ERROR reference patterns may only be written when the default binding mode is `move` +test_pat_on_type![(mut x,): &(T,)]; //~ ERROR binding modifiers may only be written when the default binding mode is `move` +test_pat_on_type![(ref x,): &(T,)]; //~ ERROR binding modifiers may only be written when the default binding mode is `move` +test_pat_on_type![(ref mut x,): &mut (T,)]; //~ ERROR binding modifiers may only be written when the default binding mode is `move` fn get() -> X { unimplemented!() @@ -40,6 +40,6 @@ fn get() -> X { // Make sure this works even when the underlying type is inferred. This test passes on rust stable. fn infer() -> X { match &get() { - (&x,) => x, //~ ERROR this pattern relies on behavior which may change in edition 2024 + (&x,) => x, //~ ERROR reference patterns may only be written when the default binding mode is `move` } } diff --git a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.stderr b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.stderr index affdca1d44906..0c6b2ff3a2f09 100644 --- a/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.stderr +++ b/tests/ui/pattern/rfc-3627-match-ergonomics-2024/min_match_ergonomics_fail.stderr @@ -99,85 +99,122 @@ LL - test_pat_on_type![Foo { f: (&x,) }: &mut Foo]; LL + test_pat_on_type![Foo { f: (x,) }: &mut Foo]; | -error: this pattern relies on behavior which may change in edition 2024 +error: reference patterns may only be written when the default binding mode is `move` --> $DIR/min_match_ergonomics_fail.rs:24:20 | LL | test_pat_on_type![(&x,): &(&T,)]; - | ^ cannot implicitly match against multiple layers of reference + | ^ reference pattern not allowed under `ref` default binding mode | = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/min_match_ergonomics_fail.rs:24:19 + | +LL | test_pat_on_type![(&x,): &(&T,)]; + | ^^^^^ this matches on type `&_` help: make the implied reference pattern explicit | LL | test_pat_on_type![&(&x,): &(&T,)]; | + -error: this pattern relies on behavior which may change in edition 2024 +error: reference patterns may only be written when the default binding mode is `move` --> $DIR/min_match_ergonomics_fail.rs:27:20 | LL | test_pat_on_type![(&mut x,): &(&mut T,)]; - | ^^^^ cannot implicitly match against multiple layers of reference + | ^^^^ reference pattern not allowed under `ref` default binding mode | = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/min_match_ergonomics_fail.rs:27:19 + | +LL | test_pat_on_type![(&mut x,): &(&mut T,)]; + | ^^^^^^^^^ this matches on type `&_` help: make the implied reference pattern explicit | LL | test_pat_on_type![&(&mut x,): &(&mut T,)]; | + -error: this pattern relies on behavior which may change in edition 2024 +error: reference patterns may only be written when the default binding mode is `move` --> $DIR/min_match_ergonomics_fail.rs:31:28 | LL | test_pat_on_type![Foo { f: &(x,) }: &Foo]; - | ^ cannot implicitly match against multiple layers of reference + | ^ reference pattern not allowed under `ref` default binding mode | = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/min_match_ergonomics_fail.rs:31:19 + | +LL | test_pat_on_type![Foo { f: &(x,) }: &Foo]; + | ^^^^^^^^^^^^^^^^ this matches on type `&_` help: make the implied reference pattern explicit | LL | test_pat_on_type![&Foo { f: &(x,) }: &Foo]; | + -error: this pattern relies on behavior which may change in edition 2024 +error: binding modifiers may only be written when the default binding mode is `move` --> $DIR/min_match_ergonomics_fail.rs:32:20 | LL | test_pat_on_type![(mut x,): &(T,)]; - | ^^^ requires binding by-value, but the implicit default is by-reference + | ^^^ binding modifier not allowed under `ref` default binding mode | = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/min_match_ergonomics_fail.rs:32:19 + | +LL | test_pat_on_type![(mut x,): &(T,)]; + | ^^^^^^^^ this matches on type `&_` help: make the implied reference pattern explicit | LL | test_pat_on_type![&(mut x,): &(T,)]; | + -error: this pattern relies on behavior which may change in edition 2024 +error: binding modifiers may only be written when the default binding mode is `move` --> $DIR/min_match_ergonomics_fail.rs:33:20 | LL | test_pat_on_type![(ref x,): &(T,)]; - | ^^^ cannot override to bind by-reference when that is the implicit default + | ^^^ binding modifier not allowed under `ref` default binding mode | = note: for more information, see -help: make the implied reference pattern explicit +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/min_match_ergonomics_fail.rs:33:19 + | +LL | test_pat_on_type![(ref x,): &(T,)]; + | ^^^^^^^^ this matches on type `&_` +help: remove the unnecessary binding modifier + | +LL - test_pat_on_type![(ref x,): &(T,)]; +LL + test_pat_on_type![(x,): &(T,)]; | -LL | test_pat_on_type![&(ref x,): &(T,)]; - | + -error: this pattern relies on behavior which may change in edition 2024 +error: binding modifiers may only be written when the default binding mode is `move` --> $DIR/min_match_ergonomics_fail.rs:34:20 | LL | test_pat_on_type![(ref mut x,): &mut (T,)]; - | ^^^^^^^ cannot override to bind by-reference when that is the implicit default + | ^^^^^^^ binding modifier not allowed under `ref mut` default binding mode | = note: for more information, see -help: make the implied reference pattern explicit +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/min_match_ergonomics_fail.rs:34:19 + | +LL | test_pat_on_type![(ref mut x,): &mut (T,)]; + | ^^^^^^^^^^^^ this matches on type `&mut _` +help: remove the unnecessary binding modifier + | +LL - test_pat_on_type![(ref mut x,): &mut (T,)]; +LL + test_pat_on_type![(x,): &mut (T,)]; | -LL | test_pat_on_type![&mut (ref mut x,): &mut (T,)]; - | ++++ -error: this pattern relies on behavior which may change in edition 2024 +error: reference patterns may only be written when the default binding mode is `move` --> $DIR/min_match_ergonomics_fail.rs:43:10 | LL | (&x,) => x, - | ^ cannot implicitly match against multiple layers of reference + | ^ reference pattern not allowed under `ref` default binding mode | = note: for more information, see +note: matching on a reference type with a non-reference pattern changes the default binding mode + --> $DIR/min_match_ergonomics_fail.rs:43:9 + | +LL | (&x,) => x, + | ^^^^^ this matches on type `&_` help: make the implied reference pattern explicit | LL | &(&x,) => x, diff --git a/tests/ui/pattern/slice-pattern-refutable.stderr b/tests/ui/pattern/slice-pattern-refutable.stderr index 3d9f769d1341e..ece5e0283e27e 100644 --- a/tests/ui/pattern/slice-pattern-refutable.stderr +++ b/tests/ui/pattern/slice-pattern-refutable.stderr @@ -8,8 +8,9 @@ LL | let [a, b, c] = Zeroes.into() else { | help: try using a fully qualified path to specify the expected types | -LL | let [a, b, c] = >::into(Zeroes) else { - | ++++++++++++++++++++++++++ ~ +LL - let [a, b, c] = Zeroes.into() else { +LL + let [a, b, c] = >::into(Zeroes) else { + | error[E0282]: type annotations needed --> $DIR/slice-pattern-refutable.rs:21:31 @@ -21,8 +22,9 @@ LL | if let [a, b, c] = Zeroes.into() { | help: try using a fully qualified path to specify the expected types | -LL | if let [a, b, c] = >::into(Zeroes) { - | ++++++++++++++++++++++++++ ~ +LL - if let [a, b, c] = Zeroes.into() { +LL + if let [a, b, c] = >::into(Zeroes) { + | error[E0282]: type annotations needed --> $DIR/slice-pattern-refutable.rs:28:31 @@ -34,8 +36,9 @@ LL | if let [a, b, c] = Zeroes.into() { | help: try using a fully qualified path to specify the expected types | -LL | if let [a, b, c] = >::into(Zeroes) { - | ++++++++++++++++++++++++++ ~ +LL - if let [a, b, c] = Zeroes.into() { +LL + if let [a, b, c] = >::into(Zeroes) { + | error: aborting due to 3 previous errors diff --git a/tests/ui/pattern/slice-patterns-ambiguity.stderr b/tests/ui/pattern/slice-patterns-ambiguity.stderr index 690776196ce15..539afed0bc009 100644 --- a/tests/ui/pattern/slice-patterns-ambiguity.stderr +++ b/tests/ui/pattern/slice-patterns-ambiguity.stderr @@ -8,8 +8,9 @@ LL | let &[a, b] = Zeroes.into() else { | help: try using a fully qualified path to specify the expected types | -LL | let &[a, b] = >::into(Zeroes) else { - | +++++++++++++++++++++++++++ ~ +LL - let &[a, b] = Zeroes.into() else { +LL + let &[a, b] = >::into(Zeroes) else { + | error[E0282]: type annotations needed --> $DIR/slice-patterns-ambiguity.rs:32:29 @@ -21,8 +22,9 @@ LL | if let &[a, b] = Zeroes.into() { | help: try using a fully qualified path to specify the expected types | -LL | if let &[a, b] = >::into(Zeroes) { - | +++++++++++++++++++++++++++ ~ +LL - if let &[a, b] = Zeroes.into() { +LL + if let &[a, b] = >::into(Zeroes) { + | error[E0282]: type annotations needed --> $DIR/slice-patterns-ambiguity.rs:39:29 @@ -34,8 +36,9 @@ LL | if let &[a, b] = Zeroes.into() { | help: try using a fully qualified path to specify the expected types | -LL | if let &[a, b] = >::into(Zeroes) { - | +++++++++++++++++++++++++++ ~ +LL - if let &[a, b] = Zeroes.into() { +LL + if let &[a, b] = >::into(Zeroes) { + | error: aborting due to 3 previous errors diff --git a/tests/ui/pattern/struct-pattern-on-non-struct-resolve-error.rs b/tests/ui/pattern/struct-pattern-on-non-struct-resolve-error.rs new file mode 100644 index 0000000000000..17a5bad0e6c0f --- /dev/null +++ b/tests/ui/pattern/struct-pattern-on-non-struct-resolve-error.rs @@ -0,0 +1,10 @@ +// Regression test for #135209. +// We ensure that we don't try to access fields on a non-struct pattern type. +fn main() { + if let as Iterator>::Item { .. } = 1 { + //~^ ERROR E0658 + //~| ERROR E0071 + //~| ERROR E0277 + x //~ ERROR E0425 + } +} diff --git a/tests/ui/pattern/struct-pattern-on-non-struct-resolve-error.stderr b/tests/ui/pattern/struct-pattern-on-non-struct-resolve-error.stderr new file mode 100644 index 0000000000000..793c2d1e97fc0 --- /dev/null +++ b/tests/ui/pattern/struct-pattern-on-non-struct-resolve-error.stderr @@ -0,0 +1,34 @@ +error[E0425]: cannot find value `x` in this scope + --> $DIR/struct-pattern-on-non-struct-resolve-error.rs:8:9 + | +LL | x + | ^ not found in this scope + +error[E0658]: usage of qualified paths in this context is experimental + --> $DIR/struct-pattern-on-non-struct-resolve-error.rs:4:12 + | +LL | if let as Iterator>::Item { .. } = 1 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #86935 for more information + = help: add `#![feature(more_qualified_paths)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0071]: expected struct, variant or union type, found inferred type + --> $DIR/struct-pattern-on-non-struct-resolve-error.rs:4:12 + | +LL | if let as Iterator>::Item { .. } = 1 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a struct + +error[E0277]: `Vec<()>` is not an iterator + --> $DIR/struct-pattern-on-non-struct-resolve-error.rs:4:12 + | +LL | if let as Iterator>::Item { .. } = 1 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Vec<()>` is not an iterator + | + = help: the trait `Iterator` is not implemented for `Vec<()>` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0071, E0277, E0425, E0658. +For more information about an error, try `rustc --explain E0071`. diff --git a/tests/ui/pattern/struct-pattern-with-missing-fields-resolve-error.rs b/tests/ui/pattern/struct-pattern-with-missing-fields-resolve-error.rs index 225891e390ff5..39f9f5a2c0208 100644 --- a/tests/ui/pattern/struct-pattern-with-missing-fields-resolve-error.rs +++ b/tests/ui/pattern/struct-pattern-with-missing-fields-resolve-error.rs @@ -3,6 +3,10 @@ struct Website { title: Option, } +enum Foo { + Bar { a: i32 }, +} + fn main() { let website = Website { url: "http://www.example.com".into(), @@ -18,4 +22,9 @@ fn main() { println!("[{}]({})", title, url); //~ ERROR cannot find value `title` in this scope //~^ NOTE not found in this scope } + + let x = Foo::Bar { a: 1 }; + if let Foo::Bar { .. } = x { //~ NOTE this pattern + println!("{a}"); //~ ERROR cannot find value `a` in this scope + } } diff --git a/tests/ui/pattern/struct-pattern-with-missing-fields-resolve-error.stderr b/tests/ui/pattern/struct-pattern-with-missing-fields-resolve-error.stderr index 80fcd714400d6..b985b771754ee 100644 --- a/tests/ui/pattern/struct-pattern-with-missing-fields-resolve-error.stderr +++ b/tests/ui/pattern/struct-pattern-with-missing-fields-resolve-error.stderr @@ -1,5 +1,5 @@ error: expected `,` - --> $DIR/struct-pattern-with-missing-fields-resolve-error.rs:12:31 + --> $DIR/struct-pattern-with-missing-fields-resolve-error.rs:16:31 | LL | if let Website { url, Some(title) } = website { | ------- ^ @@ -7,13 +7,21 @@ LL | if let Website { url, Some(title) } = website { | while parsing the fields for this pattern error[E0425]: cannot find value `title` in this scope - --> $DIR/struct-pattern-with-missing-fields-resolve-error.rs:18:30 + --> $DIR/struct-pattern-with-missing-fields-resolve-error.rs:22:30 | LL | if let Website { url, .. } = website { | ------------------- this pattern doesn't include `title`, which is available in `Website` LL | println!("[{}]({})", title, url); | ^^^^^ not found in this scope -error: aborting due to 2 previous errors +error[E0425]: cannot find value `a` in this scope + --> $DIR/struct-pattern-with-missing-fields-resolve-error.rs:28:20 + | +LL | if let Foo::Bar { .. } = x { + | --------------- this pattern doesn't include `a`, which is available in `Bar` +LL | println!("{a}"); + | ^ help: a local variable with a similar name exists: `x` + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/pattern/usefulness/doc-hidden-fields.stderr b/tests/ui/pattern/usefulness/doc-hidden-fields.stderr index 158eac9a1bd14..d7e1b54e7499f 100644 --- a/tests/ui/pattern/usefulness/doc-hidden-fields.stderr +++ b/tests/ui/pattern/usefulness/doc-hidden-fields.stderr @@ -18,15 +18,15 @@ LL | let HiddenStruct { one } = HiddenStruct::default(); help: include the missing field in the pattern and ignore the inaccessible fields | LL | let HiddenStruct { one, two, .. } = HiddenStruct::default(); - | ~~~~~~~~~~~ + | +++++++++ help: if you don't care about this missing field, you can explicitly ignore it | LL | let HiddenStruct { one, two: _, .. } = HiddenStruct::default(); - | ~~~~~~~~~~~~~~ + | ++++++++++++ help: or always ignore missing fields here | LL | let HiddenStruct { one, .. } = HiddenStruct::default(); - | ~~~~~~ + | ++++ error[E0027]: pattern does not mention field `two` --> $DIR/doc-hidden-fields.rs:21:9 @@ -37,15 +37,15 @@ LL | let HiddenStruct { one, hide } = HiddenStruct::default(); help: include the missing field in the pattern | LL | let HiddenStruct { one, hide, two } = HiddenStruct::default(); - | ~~~~~~~ + | +++++ help: if you don't care about this missing field, you can explicitly ignore it | LL | let HiddenStruct { one, hide, two: _ } = HiddenStruct::default(); - | ~~~~~~~~~~ + | ++++++++ help: or always ignore missing fields here | LL | let HiddenStruct { one, hide, .. } = HiddenStruct::default(); - | ~~~~~~ + | ++++ error[E0027]: pattern does not mention field `im_hidden` --> $DIR/doc-hidden-fields.rs:24:9 @@ -56,15 +56,15 @@ LL | let InCrate { a, b } = InCrate { a: 0, b: false, im_hidden: 0 }; help: include the missing field in the pattern | LL | let InCrate { a, b, im_hidden } = InCrate { a: 0, b: false, im_hidden: 0 }; - | ~~~~~~~~~~~~~ + | +++++++++++ help: if you don't care about this missing field, you can explicitly ignore it | LL | let InCrate { a, b, im_hidden: _ } = InCrate { a: 0, b: false, im_hidden: 0 }; - | ~~~~~~~~~~~~~~~~ + | ++++++++++++++ help: or always ignore missing fields here | LL | let InCrate { a, b, .. } = InCrate { a: 0, b: false, im_hidden: 0 }; - | ~~~~~~ + | ++++ error: aborting due to 4 previous errors diff --git a/tests/ui/pattern/usefulness/empty-types.min_exh_pats.stderr b/tests/ui/pattern/usefulness/empty-types.min_exh_pats.stderr deleted file mode 100644 index cf37bf67e860b..0000000000000 --- a/tests/ui/pattern/usefulness/empty-types.min_exh_pats.stderr +++ /dev/null @@ -1,727 +0,0 @@ -error: unreachable pattern - --> $DIR/empty-types.rs:51:9 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `!` is uninhabited -note: the lint level is defined here - --> $DIR/empty-types.rs:17:9 - | -LL | #![deny(unreachable_patterns)] - | ^^^^^^^^^^^^^^^^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:54:9 - | -LL | _x => {} - | ^^ - | - = note: this pattern matches no values because `!` is uninhabited - -error[E0004]: non-exhaustive patterns: type `&!` is non-empty - --> $DIR/empty-types.rs:58:11 - | -LL | match ref_never {} - | ^^^^^^^^^ - | - = note: the matched value is of type `&!` - = note: references are always considered inhabited -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match ref_never { -LL + _ => todo!(), -LL + } - | - -error: unreachable pattern - --> $DIR/empty-types.rs:73:9 - | -LL | (_, _) => {} - | ^^^^^^ - | - = note: this pattern matches no values because `(u32, !)` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:80:9 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `(!, !)` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:83:9 - | -LL | (_, _) => {} - | ^^^^^^ - | - = note: this pattern matches no values because `(!, !)` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:87:9 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `!` is uninhabited - -error[E0004]: non-exhaustive patterns: `Ok(_)` not covered - --> $DIR/empty-types.rs:91:11 - | -LL | match res_u32_never {} - | ^^^^^^^^^^^^^ pattern `Ok(_)` not covered - | -note: `Result` defined here - --> $SRC_DIR/core/src/result.rs:LL:COL - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Result` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ match res_u32_never { -LL + Ok(_) => todo!(), -LL + } - | - -error: unreachable pattern - --> $DIR/empty-types.rs:99:9 - | -LL | Err(_) => {} - | ^^^^^^ - | - = note: this pattern matches no values because `!` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:104:9 - | -LL | Err(_) => {} - | ^^^^^^ - | - = note: this pattern matches no values because `!` is uninhabited - -error[E0004]: non-exhaustive patterns: `Ok(1_u32..=u32::MAX)` not covered - --> $DIR/empty-types.rs:101:11 - | -LL | match res_u32_never { - | ^^^^^^^^^^^^^ pattern `Ok(1_u32..=u32::MAX)` not covered - | -note: `Result` defined here - --> $SRC_DIR/core/src/result.rs:LL:COL - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Result` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ Err(_) => {}, -LL ~ Ok(1_u32..=u32::MAX) => todo!() - | - -error[E0005]: refutable pattern in local binding - --> $DIR/empty-types.rs:108:9 - | -LL | let Ok(_x) = res_u32_never.as_ref(); - | ^^^^^^ pattern `Err(_)` not covered - | - = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant - = note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html - = note: the matched value is of type `Result<&u32, &!>` -help: you might want to use `let else` to handle the variant that isn't matched - | -LL | let Ok(_x) = res_u32_never.as_ref() else { todo!() }; - | ++++++++++++++++ - -error[E0005]: refutable pattern in local binding - --> $DIR/empty-types.rs:112:9 - | -LL | let Ok(_x) = &res_u32_never; - | ^^^^^^ pattern `&Err(_)` not covered - | - = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant - = note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html - = note: the matched value is of type `&Result` -help: you might want to use `let else` to handle the variant that isn't matched - | -LL | let Ok(_x) = &res_u32_never else { todo!() }; - | ++++++++++++++++ - -error: unreachable pattern - --> $DIR/empty-types.rs:119:9 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `Result` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:123:9 - | -LL | Ok(_) => {} - | ^^^^^ - | - = note: this pattern matches no values because `Result` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:126:9 - | -LL | Ok(_) => {} - | ^^^^^ - | - = note: this pattern matches no values because `Result` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:127:9 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `Result` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:130:9 - | -LL | Ok(_) => {} - | ^^^^^ - | - = note: this pattern matches no values because `Result` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:131:9 - | -LL | Err(_) => {} - | ^^^^^^ - | - = note: this pattern matches no values because `Result` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:140:13 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `Void` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:143:13 - | -LL | _ if false => {} - | ^ - | - = note: this pattern matches no values because `Void` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:152:13 - | -LL | Some(_) => {} - | ^^^^^^^ - | - = note: this pattern matches no values because `Void` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:156:13 - | -LL | None => {} - | ---- matches all the values already -LL | _ => {} - | ^ unreachable pattern - -error[E0004]: non-exhaustive patterns: `Some(_)` not covered - --> $DIR/empty-types.rs:165:15 - | -LL | match *ref_opt_void { - | ^^^^^^^^^^^^^ pattern `Some(_)` not covered - | -note: `Option` defined here - --> $SRC_DIR/core/src/option.rs:LL:COL - ::: $SRC_DIR/core/src/option.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Option` - = note: `Void` is uninhabited but is not being matched by value, so a wildcard `_` is required -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ None => {}, -LL + Some(_) => todo!() - | - -error: unreachable pattern - --> $DIR/empty-types.rs:208:13 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `!` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:213:13 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `!` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:218:13 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `!` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:223:13 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `!` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:229:13 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `!` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:288:9 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `!` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:291:9 - | -LL | (_, _) => {} - | ^^^^^^ - | - = note: this pattern matches no values because `(!, !)` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:294:9 - | -LL | Ok(_) => {} - | ^^^^^ - | - = note: this pattern matches no values because `Result` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:295:9 - | -LL | Err(_) => {} - | ^^^^^^ - | - = note: this pattern matches no values because `Result` is uninhabited - -error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty - --> $DIR/empty-types.rs:316:11 - | -LL | match *x {} - | ^^ - | - = note: the matched value is of type `(u32, !)` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match *x { -LL + _ => todo!(), -LL ~ } - | - -error[E0004]: non-exhaustive patterns: type `(!, !)` is non-empty - --> $DIR/empty-types.rs:318:11 - | -LL | match *x {} - | ^^ - | - = note: the matched value is of type `(!, !)` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match *x { -LL + _ => todo!(), -LL ~ } - | - -error[E0004]: non-exhaustive patterns: `Ok(_)` and `Err(_)` not covered - --> $DIR/empty-types.rs:320:11 - | -LL | match *x {} - | ^^ patterns `Ok(_)` and `Err(_)` not covered - | -note: `Result` defined here - --> $SRC_DIR/core/src/result.rs:LL:COL - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Result` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms - | -LL ~ match *x { -LL + Ok(_) | Err(_) => todo!(), -LL ~ } - | - -error[E0004]: non-exhaustive patterns: type `[!; 3]` is non-empty - --> $DIR/empty-types.rs:322:11 - | -LL | match *x {} - | ^^ - | - = note: the matched value is of type `[!; 3]` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match *x { -LL + _ => todo!(), -LL ~ } - | - -error[E0004]: non-exhaustive patterns: type `&[!]` is non-empty - --> $DIR/empty-types.rs:327:11 - | -LL | match slice_never {} - | ^^^^^^^^^^^ - | - = note: the matched value is of type `&[!]` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match slice_never { -LL + _ => todo!(), -LL + } - | - -error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered - --> $DIR/empty-types.rs:329:11 - | -LL | match slice_never { - | ^^^^^^^^^^^ pattern `&[_, ..]` not covered - | - = note: the matched value is of type `&[!]` - = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ [] => {}, -LL + &[_, ..] => todo!() - | - -error[E0004]: non-exhaustive patterns: `&[]`, `&[_]` and `&[_, _]` not covered - --> $DIR/empty-types.rs:338:11 - | -LL | match slice_never { - | ^^^^^^^^^^^ patterns `&[]`, `&[_]` and `&[_, _]` not covered - | - = note: the matched value is of type `&[!]` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms - | -LL ~ [_, _, _, ..] => {}, -LL + &[] | &[_] | &[_, _] => todo!() - | - -error[E0004]: non-exhaustive patterns: `&[]` and `&[_, ..]` not covered - --> $DIR/empty-types.rs:352:11 - | -LL | match slice_never { - | ^^^^^^^^^^^ patterns `&[]` and `&[_, ..]` not covered - | - = note: the matched value is of type `&[!]` - = note: match arms with guards don't count towards exhaustivity -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms - | -LL ~ &[..] if false => {}, -LL + &[] | &[_, ..] => todo!() - | - -error[E0004]: non-exhaustive patterns: type `[!]` is non-empty - --> $DIR/empty-types.rs:359:11 - | -LL | match *slice_never {} - | ^^^^^^^^^^^^ - | - = note: the matched value is of type `[!]` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match *slice_never { -LL + _ => todo!(), -LL + } - | - -error: unreachable pattern - --> $DIR/empty-types.rs:369:9 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `[!; 3]` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:372:9 - | -LL | [_, _, _] => {} - | ^^^^^^^^^ - | - = note: this pattern matches no values because `[!; 3]` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:375:9 - | -LL | [_, ..] => {} - | ^^^^^^^ - | - = note: this pattern matches no values because `[!; 3]` is uninhabited - -error[E0004]: non-exhaustive patterns: type `[!; 0]` is non-empty - --> $DIR/empty-types.rs:389:11 - | -LL | match array_0_never {} - | ^^^^^^^^^^^^^ - | - = note: the matched value is of type `[!; 0]` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match array_0_never { -LL + _ => todo!(), -LL + } - | - -error: unreachable pattern - --> $DIR/empty-types.rs:396:9 - | -LL | [] => {} - | -- matches all the values already -LL | _ => {} - | ^ unreachable pattern - -error[E0004]: non-exhaustive patterns: `[]` not covered - --> $DIR/empty-types.rs:398:11 - | -LL | match array_0_never { - | ^^^^^^^^^^^^^ pattern `[]` not covered - | - = note: the matched value is of type `[!; 0]` - = note: match arms with guards don't count towards exhaustivity -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ [..] if false => {}, -LL + [] => todo!() - | - -error: unreachable pattern - --> $DIR/empty-types.rs:417:9 - | -LL | Some(_) => {} - | ^^^^^^^ - | - = note: this pattern matches no values because `!` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:422:9 - | -LL | Some(_a) => {} - | ^^^^^^^^ - | - = note: this pattern matches no values because `!` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:427:9 - | -LL | None => {} - | ---- matches all the values already -LL | // !useful, !reachable -LL | _ => {} - | ^ unreachable pattern - -error: unreachable pattern - --> $DIR/empty-types.rs:432:9 - | -LL | None => {} - | ---- matches all the values already -LL | // !useful, !reachable -LL | _a => {} - | ^^ unreachable pattern - -error[E0004]: non-exhaustive patterns: `&Some(_)` not covered - --> $DIR/empty-types.rs:452:11 - | -LL | match ref_opt_never { - | ^^^^^^^^^^^^^ pattern `&Some(_)` not covered - | -note: `Option` defined here - --> $SRC_DIR/core/src/option.rs:LL:COL - ::: $SRC_DIR/core/src/option.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `&Option` - = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ &None => {}, -LL + &Some(_) => todo!() - | - -error[E0004]: non-exhaustive patterns: `Some(_)` not covered - --> $DIR/empty-types.rs:493:11 - | -LL | match *ref_opt_never { - | ^^^^^^^^^^^^^^ pattern `Some(_)` not covered - | -note: `Option` defined here - --> $SRC_DIR/core/src/option.rs:LL:COL - ::: $SRC_DIR/core/src/option.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Option` - = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ None => {}, -LL + Some(_) => todo!() - | - -error[E0004]: non-exhaustive patterns: `Err(_)` not covered - --> $DIR/empty-types.rs:541:11 - | -LL | match *ref_res_never { - | ^^^^^^^^^^^^^^ pattern `Err(_)` not covered - | -note: `Result` defined here - --> $SRC_DIR/core/src/result.rs:LL:COL - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Result` - = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ Ok(_) => {}, -LL + Err(_) => todo!() - | - -error[E0004]: non-exhaustive patterns: `Err(_)` not covered - --> $DIR/empty-types.rs:552:11 - | -LL | match *ref_res_never { - | ^^^^^^^^^^^^^^ pattern `Err(_)` not covered - | -note: `Result` defined here - --> $SRC_DIR/core/src/result.rs:LL:COL - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Result` - = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ Ok(_a) => {}, -LL + Err(_) => todo!() - | - -error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty - --> $DIR/empty-types.rs:571:11 - | -LL | match *ref_tuple_half_never {} - | ^^^^^^^^^^^^^^^^^^^^^ - | - = note: the matched value is of type `(u32, !)` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match *ref_tuple_half_never { -LL + _ => todo!(), -LL + } - | - -error: unreachable pattern - --> $DIR/empty-types.rs:604:9 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `!` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:607:9 - | -LL | _x => {} - | ^^ - | - = note: this pattern matches no values because `!` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:610:9 - | -LL | _ if false => {} - | ^ - | - = note: this pattern matches no values because `!` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:613:9 - | -LL | _x if false => {} - | ^^ - | - = note: this pattern matches no values because `!` is uninhabited - -error[E0004]: non-exhaustive patterns: `&_` not covered - --> $DIR/empty-types.rs:638:11 - | -LL | match ref_never { - | ^^^^^^^^^ pattern `&_` not covered - | - = note: the matched value is of type `&!` - = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required - = note: references are always considered inhabited - = note: match arms with guards don't count towards exhaustivity -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ &_a if false => {}, -LL + &_ => todo!() - | - -error[E0004]: non-exhaustive patterns: `Ok(_)` not covered - --> $DIR/empty-types.rs:654:11 - | -LL | match *ref_result_never { - | ^^^^^^^^^^^^^^^^^ pattern `Ok(_)` not covered - | -note: `Result` defined here - --> $SRC_DIR/core/src/result.rs:LL:COL - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Result` - = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ Err(_) => {}, -LL + Ok(_) => todo!() - | - -error[E0004]: non-exhaustive patterns: `Some(_)` not covered - --> $DIR/empty-types.rs:674:11 - | -LL | match *x { - | ^^ pattern `Some(_)` not covered - | -note: `Option>` defined here - --> $SRC_DIR/core/src/option.rs:LL:COL - ::: $SRC_DIR/core/src/option.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Option>` - = note: `Result` is uninhabited but is not being matched by value, so a wildcard `_` is required -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ None => {}, -LL + Some(_) => todo!() - | - -error: aborting due to 64 previous errors - -Some errors have detailed explanations: E0004, E0005. -For more information about an error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/usefulness/issue-82772-match-box-as-struct.stderr b/tests/ui/pattern/usefulness/issue-82772-match-box-as-struct.stderr index ba7573839ed29..ee21b4c8d46d8 100644 --- a/tests/ui/pattern/usefulness/issue-82772-match-box-as-struct.stderr +++ b/tests/ui/pattern/usefulness/issue-82772-match-box-as-struct.stderr @@ -2,7 +2,7 @@ error[E0451]: field `1` of struct `Box` is private --> $DIR/issue-82772-match-box-as-struct.rs:4:15 | LL | let Box { 1: _, .. }: Box<()>; - | ^^^^ private field + | ^ private field error: aborting due to 1 previous error diff --git a/tests/ui/pattern/usefulness/match-privately-empty.min_exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/match-privately-empty.min_exhaustive_patterns.stderr deleted file mode 100644 index 261a4b3353f23..0000000000000 --- a/tests/ui/pattern/usefulness/match-privately-empty.min_exhaustive_patterns.stderr +++ /dev/null @@ -1,21 +0,0 @@ -error[E0004]: non-exhaustive patterns: `Some(Private { misc: true, .. })` not covered - --> $DIR/match-privately-empty.rs:15:11 - | -LL | match private::DATA { - | ^^^^^^^^^^^^^ pattern `Some(Private { misc: true, .. })` not covered - | -note: `Option` defined here - --> $SRC_DIR/core/src/option.rs:LL:COL - ::: $SRC_DIR/core/src/option.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Option` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ Some(private::Private { misc: false, .. }) => {}, -LL + Some(Private { misc: true, .. }) => todo!() - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/usefulness/slice_of_empty.min_exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/slice_of_empty.min_exhaustive_patterns.stderr deleted file mode 100644 index f24ce154d149d..0000000000000 --- a/tests/ui/pattern/usefulness/slice_of_empty.min_exhaustive_patterns.stderr +++ /dev/null @@ -1,30 +0,0 @@ -error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered - --> $DIR/slice_of_empty.rs:10:11 - | -LL | match nevers { - | ^^^^^^ pattern `&[_, ..]` not covered - | - = note: the matched value is of type `&[!]` - = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ &[] => (), -LL ~ &[_, ..] => todo!(), - | - -error[E0004]: non-exhaustive patterns: `&[]` and `&[_, _, ..]` not covered - --> $DIR/slice_of_empty.rs:21:11 - | -LL | match nevers { - | ^^^^^^ patterns `&[]` and `&[_, _, ..]` not covered - | - = note: the matched value is of type `&[!]` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms - | -LL ~ &[_] => (), -LL ~ &[] | &[_, _, ..] => todo!(), - | - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/usefulness/stable-gated-fields.stderr b/tests/ui/pattern/usefulness/stable-gated-fields.stderr index d6e9bac7c136b..7c30b530d6d21 100644 --- a/tests/ui/pattern/usefulness/stable-gated-fields.stderr +++ b/tests/ui/pattern/usefulness/stable-gated-fields.stderr @@ -7,15 +7,15 @@ LL | let UnstableStruct { stable } = UnstableStruct::default(); help: include the missing field in the pattern and ignore the inaccessible fields | LL | let UnstableStruct { stable, stable2, .. } = UnstableStruct::default(); - | ~~~~~~~~~~~~~~~ + | +++++++++++++ help: if you don't care about this missing field, you can explicitly ignore it | LL | let UnstableStruct { stable, stable2: _, .. } = UnstableStruct::default(); - | ~~~~~~~~~~~~~~~~~~ + | ++++++++++++++++ help: or always ignore missing fields here | LL | let UnstableStruct { stable, .. } = UnstableStruct::default(); - | ~~~~~~ + | ++++ error: pattern requires `..` due to inaccessible fields --> $DIR/stable-gated-fields.rs:11:9 diff --git a/tests/ui/pattern/usefulness/unstable-gated-fields.stderr b/tests/ui/pattern/usefulness/unstable-gated-fields.stderr index bb10e439ee23d..4487f2735345a 100644 --- a/tests/ui/pattern/usefulness/unstable-gated-fields.stderr +++ b/tests/ui/pattern/usefulness/unstable-gated-fields.stderr @@ -6,16 +6,19 @@ LL | let UnstableStruct { stable, stable2, } = UnstableStruct::default(); | help: include the missing field in the pattern | -LL | let UnstableStruct { stable, stable2, unstable } = UnstableStruct::default(); - | ~~~~~~~~~~~~ +LL - let UnstableStruct { stable, stable2, } = UnstableStruct::default(); +LL + let UnstableStruct { stable, stable2, unstable } = UnstableStruct::default(); + | help: if you don't care about this missing field, you can explicitly ignore it | -LL | let UnstableStruct { stable, stable2, unstable: _ } = UnstableStruct::default(); - | ~~~~~~~~~~~~~~~ +LL - let UnstableStruct { stable, stable2, } = UnstableStruct::default(); +LL + let UnstableStruct { stable, stable2, unstable: _ } = UnstableStruct::default(); + | help: or always ignore missing fields here | -LL | let UnstableStruct { stable, stable2, .. } = UnstableStruct::default(); - | ~~~~~~ +LL - let UnstableStruct { stable, stable2, } = UnstableStruct::default(); +LL + let UnstableStruct { stable, stable2, .. } = UnstableStruct::default(); + | error[E0027]: pattern does not mention field `stable2` --> $DIR/unstable-gated-fields.rs:13:9 @@ -25,16 +28,19 @@ LL | let UnstableStruct { stable, unstable, } = UnstableStruct::default(); | help: include the missing field in the pattern | -LL | let UnstableStruct { stable, unstable, stable2 } = UnstableStruct::default(); - | ~~~~~~~~~~~ +LL - let UnstableStruct { stable, unstable, } = UnstableStruct::default(); +LL + let UnstableStruct { stable, unstable, stable2 } = UnstableStruct::default(); + | help: if you don't care about this missing field, you can explicitly ignore it | -LL | let UnstableStruct { stable, unstable, stable2: _ } = UnstableStruct::default(); - | ~~~~~~~~~~~~~~ +LL - let UnstableStruct { stable, unstable, } = UnstableStruct::default(); +LL + let UnstableStruct { stable, unstable, stable2: _ } = UnstableStruct::default(); + | help: or always ignore missing fields here | -LL | let UnstableStruct { stable, unstable, .. } = UnstableStruct::default(); - | ~~~~~~ +LL - let UnstableStruct { stable, unstable, } = UnstableStruct::default(); +LL + let UnstableStruct { stable, unstable, .. } = UnstableStruct::default(); + | error: aborting due to 2 previous errors diff --git a/tests/ui/pptypedef.stderr b/tests/ui/pptypedef.stderr index 08b90b365e326..96327cfcc653e 100644 --- a/tests/ui/pptypedef.stderr +++ b/tests/ui/pptypedef.stderr @@ -8,8 +8,9 @@ LL | let_in(3u32, |i| { assert!(i == 3i32); }); | help: change the type of the numeric literal from `i32` to `u32` | -LL | let_in(3u32, |i| { assert!(i == 3u32); }); - | ~~~ +LL - let_in(3u32, |i| { assert!(i == 3i32); }); +LL + let_in(3u32, |i| { assert!(i == 3u32); }); + | error[E0308]: mismatched types --> $DIR/pptypedef.rs:8:37 @@ -21,8 +22,9 @@ LL | let_in(3i32, |i| { assert!(i == 3u32); }); | help: change the type of the numeric literal from `u32` to `i32` | -LL | let_in(3i32, |i| { assert!(i == 3i32); }); - | ~~~ +LL - let_in(3i32, |i| { assert!(i == 3u32); }); +LL + let_in(3i32, |i| { assert!(i == 3i32); }); + | error: aborting due to 2 previous errors diff --git a/tests/ui/print-calling-conventions.stdout b/tests/ui/print-calling-conventions.stdout index 4415b3c858e35..539b2d5dee405 100644 --- a/tests/ui/print-calling-conventions.stdout +++ b/tests/ui/print-calling-conventions.stdout @@ -12,6 +12,7 @@ cdecl-unwind efiapi fastcall fastcall-unwind +gpu-kernel msp430-interrupt ptx-kernel riscv-interrupt-m diff --git a/tests/ui/print-stdout-eprint-stderr.rs b/tests/ui/print-stdout-eprint-stderr.rs index e84a9bebc495a..4b356e2fe6172 100644 --- a/tests/ui/print-stdout-eprint-stderr.rs +++ b/tests/ui/print-stdout-eprint-stderr.rs @@ -1,6 +1,5 @@ //@ run-pass -//@ ignore-wasm32 spawning processes is not supported -//@ ignore-sgx no processes +//@ needs-subprocess use std::{env, process}; diff --git a/tests/ui/print_type_sizes/anonymous.rs b/tests/ui/print_type_sizes/anonymous.rs index a3a3222808802..7819e5ea767ed 100644 --- a/tests/ui/print_type_sizes/anonymous.rs +++ b/tests/ui/print_type_sizes/anonymous.rs @@ -1,14 +1,13 @@ -//@ compile-flags: -Z print-type-sizes +//@ compile-flags: -Z print-type-sizes --crate-type=lib //@ build-pass // All of the types that occur in this function are uninteresting, in // that one cannot control the sizes of these types with the same sort // of enum-variant manipulation tricks. -#![feature(start)] +#![no_std] -#[start] -fn start(_: isize, _: *const *const u8) -> isize { +pub fn main() -> isize { let _byte: u8 = 0; let _word: usize = 0; let _tuple: (u8, usize)= (0, 0); diff --git a/tests/ui/print_type_sizes/niche-filling.stdout b/tests/ui/print_type_sizes/niche-filling.stdout index eeb5de5324121..70612490a4716 100644 --- a/tests/ui/print_type_sizes/niche-filling.stdout +++ b/tests/ui/print_type_sizes/niche-filling.stdout @@ -68,7 +68,7 @@ print-type-size type: `Union2, u32>`: 4 bytes, alignment: print-type-size variant `Union2`: 4 bytes print-type-size field `.a`: 4 bytes print-type-size field `.b`: 4 bytes, offset: 0 bytes, alignment: 4 bytes -print-type-size type: `core::num::nonzero::private::NonZeroU32Inner`: 4 bytes, alignment: 4 bytes +print-type-size type: `core::num::niche_types::NonZeroU32Inner`: 4 bytes, alignment: 4 bytes print-type-size field `.0`: 4 bytes print-type-size type: `std::num::NonZero`: 4 bytes, alignment: 4 bytes print-type-size field `.0`: 4 bytes diff --git a/tests/ui/privacy/issue-75907.stderr b/tests/ui/privacy/issue-75907.stderr index 3121cc04478a4..8a6484979c009 100644 --- a/tests/ui/privacy/issue-75907.stderr +++ b/tests/ui/privacy/issue-75907.stderr @@ -13,8 +13,9 @@ LL | let Bar(x, y, Foo(z)) = make_bar(); | private field help: consider making the fields publicly accessible | -LL | pub(crate) struct Bar(pub u8, pub u8, pub Foo); - | ~~~ +++ +LL - pub(crate) struct Bar(pub u8, pub(in crate::foo) u8, Foo); +LL + pub(crate) struct Bar(pub u8, pub u8, pub Foo); + | error[E0532]: cannot match against a tuple struct which contains private fields --> $DIR/issue-75907.rs:15:19 diff --git a/tests/ui/privacy/privacy-in-paths.stderr b/tests/ui/privacy/privacy-in-paths.stderr index 9c3d5e97c62a3..e6ece35865d44 100644 --- a/tests/ui/privacy/privacy-in-paths.stderr +++ b/tests/ui/privacy/privacy-in-paths.stderr @@ -25,8 +25,9 @@ LL | mod bar { | ^^^^^^^ help: consider importing this struct through its public re-export instead | -LL | foo::S::f(); - | ~~~~~~ +LL - ::foo::bar::S::f(); +LL + foo::S::f(); + | error[E0603]: trait `T` is private --> $DIR/privacy-in-paths.rs:26:23 diff --git a/tests/ui/privacy/privacy-ns1.stderr b/tests/ui/privacy/privacy-ns1.stderr index 9710cc48637bc..3396330c993cf 100644 --- a/tests/ui/privacy/privacy-ns1.stderr +++ b/tests/ui/privacy/privacy-ns1.stderr @@ -9,8 +9,9 @@ LL | Bar(); | help: a unit struct with a similar name exists | -LL | Baz(); - | ~~~ +LL - Bar(); +LL + Baz(); + | help: consider importing this function instead | LL + use foo2::Bar; @@ -27,8 +28,9 @@ LL | Bar(); | help: a unit struct with a similar name exists | -LL | Baz(); - | ~~~ +LL - Bar(); +LL + Baz(); + | help: consider importing this function | LL + use foo2::Bar; @@ -45,8 +47,9 @@ LL | let _x: Box; | help: a struct with a similar name exists | -LL | let _x: Box; - | ~~~ +LL - let _x: Box; +LL + let _x: Box; + | help: consider importing this trait | LL + use foo1::Bar; diff --git a/tests/ui/privacy/privacy-ns2.stderr b/tests/ui/privacy/privacy-ns2.stderr index 75e735e1e6a28..ac98682b2b39e 100644 --- a/tests/ui/privacy/privacy-ns2.stderr +++ b/tests/ui/privacy/privacy-ns2.stderr @@ -20,8 +20,9 @@ LL | Bar(); | help: a unit struct with a similar name exists | -LL | Baz(); - | ~~~ +LL - Bar(); +LL + Baz(); + | help: consider importing this function instead | LL + use foo2::Bar; @@ -35,8 +36,9 @@ LL | let _x : Bar(); | help: use `=` if you meant to assign | -LL | let _x = Bar(); - | ~ +LL - let _x : Bar(); +LL + let _x = Bar(); + | help: consider importing this trait instead | LL + use foo1::Bar; diff --git a/tests/ui/privacy/privacy1.rs b/tests/ui/privacy/privacy1.rs index 31f3960100371..9436441ecc6a7 100644 --- a/tests/ui/privacy/privacy1.rs +++ b/tests/ui/privacy/privacy1.rs @@ -1,4 +1,4 @@ -#![feature(lang_items, start, no_core)] +#![feature(lang_items, no_core)] #![no_core] // makes debugging this test *a lot* easier (during resolve) #[lang="sized"] @@ -173,4 +173,4 @@ pub mod mytest { } } -#[start] fn main(_: isize, _: *const *const u8) -> isize { 3 } +fn main() {} diff --git a/tests/ui/privacy/privacy1.stderr b/tests/ui/privacy/privacy1.stderr index a3552e146a65a..cb7b858e54dc8 100644 --- a/tests/ui/privacy/privacy1.stderr +++ b/tests/ui/privacy/privacy1.stderr @@ -142,8 +142,9 @@ LL | mod baz { | ^^^^^^^ help: consider importing this function through its public re-export instead | -LL | bar::foo(); - | ~~~~~~~~ +LL - ::bar::baz::foo(); +LL + bar::foo(); + | error[E0603]: module `baz` is private --> $DIR/privacy1.rs:128:16 @@ -158,8 +159,9 @@ LL | mod baz { | ^^^^^^^ help: consider importing this function through its public re-export instead | -LL | bar::bar(); - | ~~~~~~~~ +LL - ::bar::baz::bar(); +LL + bar::bar(); + | error[E0603]: trait `B` is private --> $DIR/privacy1.rs:157:17 diff --git a/tests/ui/privacy/privacy2.rs b/tests/ui/privacy/privacy2.rs index 33292a65c5d89..ab6d805544eeb 100644 --- a/tests/ui/privacy/privacy2.rs +++ b/tests/ui/privacy/privacy2.rs @@ -1,6 +1,6 @@ //@ compile-flags: -Zdeduplicate-diagnostics=yes -#![feature(start, no_core)] +#![feature(no_core)] #![no_core] // makes debugging this test *a lot* easier (during resolve) // Test to make sure that globs don't leak in regular `use` statements. @@ -26,4 +26,4 @@ fn test2() { //~^ ERROR `foo` is private } -#[start] fn main(_: isize, _: *const *const u8) -> isize { 3 } +fn main() {} diff --git a/tests/ui/privacy/privacy3.rs b/tests/ui/privacy/privacy3.rs index fb1f432410dd1..6298a6bc8cf85 100644 --- a/tests/ui/privacy/privacy3.rs +++ b/tests/ui/privacy/privacy3.rs @@ -1,6 +1,6 @@ //@ compile-flags: -Zdeduplicate-diagnostics=yes -#![feature(start, no_core)] +#![feature( no_core)] #![no_core] // makes debugging this test *a lot* easier (during resolve) // Test to make sure that private items imported through globs remain private @@ -26,4 +26,4 @@ fn test1() { gpriv(); } -#[start] fn main(_: isize, _: *const *const u8) -> isize { 3 } +fn main() {} diff --git a/tests/ui/privacy/privacy4.rs b/tests/ui/privacy/privacy4.rs index fa257b800394f..7341c7752bbf7 100644 --- a/tests/ui/privacy/privacy4.rs +++ b/tests/ui/privacy/privacy4.rs @@ -1,4 +1,4 @@ -#![feature(lang_items, start, no_core)] +#![feature(lang_items, no_core)] #![no_core] // makes debugging this test *a lot* easier (during resolve) #[lang = "sized"] pub trait Sized {} @@ -22,4 +22,4 @@ fn test2() { gpriv(); } -#[start] fn main(_: isize, _: *const *const u8) -> isize { 3 } +fn main() {} diff --git a/tests/ui/privacy/private-struct-field-ctor.stderr b/tests/ui/privacy/private-struct-field-ctor.stderr index 2a35537237a3e..8eb1bf7990b86 100644 --- a/tests/ui/privacy/private-struct-field-ctor.stderr +++ b/tests/ui/privacy/private-struct-field-ctor.stderr @@ -2,7 +2,7 @@ error[E0451]: field `x` of struct `Foo` is private --> $DIR/private-struct-field-ctor.rs:8:22 | LL | let s = a::Foo { x: 1 }; - | ^^^^ private field + | ^ private field error: aborting due to 1 previous error diff --git a/tests/ui/privacy/private-struct-field-pattern.stderr b/tests/ui/privacy/private-struct-field-pattern.stderr index de24d1e09622b..5609596721d40 100644 --- a/tests/ui/privacy/private-struct-field-pattern.stderr +++ b/tests/ui/privacy/private-struct-field-pattern.stderr @@ -2,7 +2,7 @@ error[E0451]: field `x` of struct `Foo` is private --> $DIR/private-struct-field-pattern.rs:15:15 | LL | Foo { x: _ } => {} - | ^^^^ private field + | ^ private field error: aborting due to 1 previous error diff --git a/tests/ui/privacy/restricted/struct-literal-field.stderr b/tests/ui/privacy/restricted/struct-literal-field.stderr index dcdadf1da4bf7..e1cf7c2fadbfe 100644 --- a/tests/ui/privacy/restricted/struct-literal-field.stderr +++ b/tests/ui/privacy/restricted/struct-literal-field.stderr @@ -2,7 +2,7 @@ error[E0451]: field `x` of struct `S` is private --> $DIR/struct-literal-field.rs:18:9 | LL | S { x: 0 }; - | ^^^^ private field + | ^ private field error: aborting due to 1 previous error diff --git a/tests/ui/privacy/restricted/test.rs b/tests/ui/privacy/restricted/test.rs index 3fdfd191b365b..b32b9327f07ee 100644 --- a/tests/ui/privacy/restricted/test.rs +++ b/tests/ui/privacy/restricted/test.rs @@ -47,6 +47,6 @@ fn main() { } mod pathological { - pub(in bad::path) mod m1 {} //~ ERROR failed to resolve: you might be missing crate `bad` + pub(in bad::path) mod m1 {} //~ ERROR failed to resolve: use of unresolved module or unlinked crate `bad` pub(in foo) mod m2 {} //~ ERROR visibilities can only be restricted to ancestor modules } diff --git a/tests/ui/privacy/restricted/test.stderr b/tests/ui/privacy/restricted/test.stderr index 5deaffbdbf3f8..2744b3708a826 100644 --- a/tests/ui/privacy/restricted/test.stderr +++ b/tests/ui/privacy/restricted/test.stderr @@ -1,10 +1,10 @@ -error[E0433]: failed to resolve: you might be missing crate `bad` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `bad` --> $DIR/test.rs:50:12 | LL | pub(in bad::path) mod m1 {} - | ^^^ you might be missing crate `bad` + | ^^^ use of unresolved module or unlinked crate `bad` | -help: consider importing the `bad` crate +help: you might be missing a crate named `bad`, add it to your project and import it in your code | LL + extern crate bad; | diff --git a/tests/ui/privacy/sealed-traits/re-exported-trait.stderr b/tests/ui/privacy/sealed-traits/re-exported-trait.stderr index 6e2f36e3f381d..368389af0b927 100644 --- a/tests/ui/privacy/sealed-traits/re-exported-trait.stderr +++ b/tests/ui/privacy/sealed-traits/re-exported-trait.stderr @@ -11,8 +11,9 @@ LL | mod b { | ^^^^^ help: consider importing this trait through its public re-export instead | -LL | impl a::Trait for S {} - | ~~~~~~~~ +LL - impl a::b::Trait for S {} +LL + impl a::Trait for S {} + | error: aborting due to 1 previous error diff --git a/tests/ui/privacy/suggest-box-new.stderr b/tests/ui/privacy/suggest-box-new.stderr index 1e28b9fbd86bd..b651348de29ea 100644 --- a/tests/ui/privacy/suggest-box-new.stderr +++ b/tests/ui/privacy/suggest-box-new.stderr @@ -10,17 +10,20 @@ LL | let _ = std::collections::HashMap(); help: you might have meant to use an associated function to build this type | LL | let _ = std::collections::HashMap::new(); - | ~~~~~~~ -LL | let _ = std::collections::HashMap::with_capacity(_); - | ~~~~~~~~~~~~~~~~~~ -LL | let _ = std::collections::HashMap::with_hasher(_); - | ~~~~~~~~~~~~~~~~ -LL | let _ = std::collections::HashMap::with_capacity_and_hasher(_, _); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | +++++ +LL - let _ = std::collections::HashMap(); +LL + let _ = std::collections::HashMap::with_capacity(_); + | +LL - let _ = std::collections::HashMap(); +LL + let _ = std::collections::HashMap::with_hasher(_); + | +LL - let _ = std::collections::HashMap(); +LL + let _ = std::collections::HashMap::with_capacity_and_hasher(_, _); + | help: consider using the `Default` trait | LL | let _ = ::default(); - | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | + ++++++++++++++++++++++++++++++++++ error[E0423]: cannot initialize a tuple struct which contains private fields --> $DIR/suggest-box-new.rs:8:19 @@ -36,19 +39,36 @@ note: constructor is not visible here due to private fields = note: private field help: you might have meant to use an associated function to build this type | -LL | wtf: Some(Box::new(_)), - | ~~~~~~~~ -LL | wtf: Some(Box::new_uninit()), - | ~~~~~~~~~~~~~~ -LL | wtf: Some(Box::new_zeroed()), - | ~~~~~~~~~~~~~~ -LL | wtf: Some(Box::new_in(_, _)), - | ~~~~~~~~~~~~~~ +LL - wtf: Some(Box(U { +LL - wtf: None, +LL - x: (), +LL - })), +LL + wtf: Some(Box::new(_)), + | +LL - wtf: Some(Box(U { +LL - wtf: None, +LL - x: (), +LL - })), +LL + wtf: Some(Box::new_uninit()), + | +LL - wtf: Some(Box(U { +LL - wtf: None, +LL - x: (), +LL - })), +LL + wtf: Some(Box::new_zeroed()), + | +LL - wtf: Some(Box(U { +LL - wtf: None, +LL - x: (), +LL - })), +LL + wtf: Some(Box::new_in(_, _)), + | and 12 other candidates help: consider using the `Default` trait | -LL | wtf: Some(::default()), - | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - wtf: Some(Box(U { +LL + wtf: Some(::default()), + | error: cannot construct `HashMap<_, _, _>` with struct literal syntax due to private fields --> $DIR/suggest-box-new.rs:16:13 @@ -59,18 +79,23 @@ LL | let _ = std::collections::HashMap {}; = note: private field `base` that was not provided help: you might have meant to use an associated function to build this type | -LL | let _ = std::collections::HashMap::new(); - | ~~~~~~~ -LL | let _ = std::collections::HashMap::with_capacity(_); - | ~~~~~~~~~~~~~~~~~~ -LL | let _ = std::collections::HashMap::with_hasher(_); - | ~~~~~~~~~~~~~~~~ -LL | let _ = std::collections::HashMap::with_capacity_and_hasher(_, _); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - let _ = std::collections::HashMap {}; +LL + let _ = std::collections::HashMap::new(); + | +LL - let _ = std::collections::HashMap {}; +LL + let _ = std::collections::HashMap::with_capacity(_); + | +LL - let _ = std::collections::HashMap {}; +LL + let _ = std::collections::HashMap::with_hasher(_); + | +LL - let _ = std::collections::HashMap {}; +LL + let _ = std::collections::HashMap::with_capacity_and_hasher(_, _); + | help: consider using the `Default` trait | -LL | let _ = ::default(); - | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - let _ = std::collections::HashMap {}; +LL + let _ = ::default(); + | error: cannot construct `Box<_, _>` with struct literal syntax due to private fields --> $DIR/suggest-box-new.rs:18:13 @@ -81,19 +106,24 @@ LL | let _ = Box {}; = note: private fields `0` and `1` that were not provided help: you might have meant to use an associated function to build this type | -LL | let _ = Box::new(_); - | ~~~~~~~~ -LL | let _ = Box::new_uninit(); - | ~~~~~~~~~~~~~~ -LL | let _ = Box::new_zeroed(); - | ~~~~~~~~~~~~~~ -LL | let _ = Box::new_in(_, _); - | ~~~~~~~~~~~~~~ +LL - let _ = Box {}; +LL + let _ = Box::new(_); + | +LL - let _ = Box {}; +LL + let _ = Box::new_uninit(); + | +LL - let _ = Box {}; +LL + let _ = Box::new_zeroed(); + | +LL - let _ = Box {}; +LL + let _ = Box::new_in(_, _); + | and 12 other candidates help: consider using the `Default` trait | -LL | let _ = ::default(); - | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - let _ = Box {}; +LL + let _ = ::default(); + | error: aborting due to 4 previous errors diff --git a/tests/ui/privacy/suggest-making-field-public.stderr b/tests/ui/privacy/suggest-making-field-public.stderr index e92e9aae310e3..3e52232dd59bf 100644 --- a/tests/ui/privacy/suggest-making-field-public.stderr +++ b/tests/ui/privacy/suggest-making-field-public.stderr @@ -14,8 +14,9 @@ LL | pub struct A(pub(self)String); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider making the field publicly accessible | -LL | pub struct A(pub String); - | ~~~ +LL - pub struct A(pub(self)String); +LL + pub struct A(pub String); + | error[E0423]: cannot initialize a tuple struct which contains private fields --> $DIR/suggest-making-field-public.rs:9:9 @@ -30,8 +31,9 @@ LL | pub struct A(pub(self)String); | ^^^^^^^^^^^^^^^ private field help: consider making the field publicly accessible | -LL | pub struct A(pub String); - | ~~~ +LL - pub struct A(pub(self)String); +LL + pub struct A(pub String); + | error: aborting due to 2 previous errors diff --git a/tests/ui/privacy/sysroot-private.default.stderr b/tests/ui/privacy/sysroot-private.default.stderr new file mode 100644 index 0000000000000..692b1bbd4db35 --- /dev/null +++ b/tests/ui/privacy/sysroot-private.default.stderr @@ -0,0 +1,40 @@ +error[E0405]: cannot find trait `Equivalent` in this scope + --> $DIR/sysroot-private.rs:27:18 + | +LL | trait Trait2: Equivalent {} + | ^^^^^^^^^^ not found in this scope + +error[E0412]: cannot find type `K` in this scope + --> $DIR/sysroot-private.rs:32:35 + | +LL | fn trait_member(val: &T, key: &K) -> bool { + | - ^ + | | + | similarly named type parameter `T` defined here + | +help: a type parameter with a similar name exists + | +LL - fn trait_member(val: &T, key: &K) -> bool { +LL + fn trait_member(val: &T, key: &T) -> bool { + | +help: you might be missing a type parameter + | +LL | fn trait_member(val: &T, key: &K) -> bool { + | +++ + +error[E0220]: associated type `ExpressionStack` not found for `Trait` + --> $DIR/sysroot-private.rs:22:31 + | +LL | type AssociatedTy = dyn Trait; + | ^^^^^^^^^^^^^^^ help: `Trait` has the following associated type: `Bar` + +error[E0425]: cannot find function `memchr2` in this scope + --> $DIR/sysroot-private.rs:40:5 + | +LL | memchr2(b'a', b'b', buf) + | ^^^^^^^ not found in this scope + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0220, E0405, E0412, E0425. +For more information about an error, try `rustc --explain E0220`. diff --git a/tests/ui/privacy/sysroot-private.rs b/tests/ui/privacy/sysroot-private.rs new file mode 100644 index 0000000000000..8681857459273 --- /dev/null +++ b/tests/ui/privacy/sysroot-private.rs @@ -0,0 +1,43 @@ +//! Test that private dependencies of `std` that live in the sysroot do not reach through to +//! diagnostics. +//! +//! This test would be more robust if we could patch the sysroot with an "evil" crate that +//! provided known types that we control; however, this would effectively require rebuilding +//! `std` (or patching crate metadata). So, this test relies on what is currently public API +//! of `std`'s dependencies, but may not be robust against dependency upgrades/changes. + +//@ only-unix Windows sysroots seem to not expose this dependency +//@ ignore-emscripten neither does Emscripten +//@ revisions: default rustc_private_enabled + +// Enabling `rustc_private` should `std`'s dependencies accessible, so they should show up +// in diagnostics. NB: not all diagnostics are affected by this. +#![cfg_attr(rustc_private_enabled, feature(rustc_private))] +#![crate_type = "lib"] + +trait Trait { type Bar; } + +// Attempt to get a suggestion for `gimli::read::op::EvaluationStoreage`, which should not be +// present in diagnostics (it is a dependency of the compiler). +type AssociatedTy = dyn Trait; +//~^ ERROR associated type `ExpressionStack` not found +//[rustc_private_enabled]~| NOTE there is an associated type `ExpressionStack` in the trait `gimli::read::op::EvaluationStorage` + +// Attempt to get a suggestion for `hashbrown::Equivalent` +trait Trait2: Equivalent {} +//~^ ERROR cannot find trait +//~| NOTE not found + +// Attempt to get a suggestion for `hashbrown::Equivalent::equivalent` +fn trait_member(val: &T, key: &K) -> bool { + //~^ ERROR cannot find type `K` + //~| NOTE similarly named + val.equivalent(key) +} + +// Attempt to get a suggestion for `memchr::memchr2` +fn free_function(buf: &[u8]) -> Option { + memchr2(b'a', b'b', buf) + //~^ ERROR cannot find function + //~| NOTE not found +} diff --git a/tests/ui/privacy/sysroot-private.rustc_private_enabled.stderr b/tests/ui/privacy/sysroot-private.rustc_private_enabled.stderr new file mode 100644 index 0000000000000..dc2d890a082cf --- /dev/null +++ b/tests/ui/privacy/sysroot-private.rustc_private_enabled.stderr @@ -0,0 +1,40 @@ +error[E0405]: cannot find trait `Equivalent` in this scope + --> $DIR/sysroot-private.rs:27:18 + | +LL | trait Trait2: Equivalent {} + | ^^^^^^^^^^ not found in this scope + +error[E0412]: cannot find type `K` in this scope + --> $DIR/sysroot-private.rs:32:35 + | +LL | fn trait_member(val: &T, key: &K) -> bool { + | - ^ + | | + | similarly named type parameter `T` defined here + | +help: a type parameter with a similar name exists + | +LL - fn trait_member(val: &T, key: &K) -> bool { +LL + fn trait_member(val: &T, key: &T) -> bool { + | +help: you might be missing a type parameter + | +LL | fn trait_member(val: &T, key: &K) -> bool { + | +++ + +error[E0220]: associated type `ExpressionStack` not found for `Trait` + --> $DIR/sysroot-private.rs:22:31 + | +LL | type AssociatedTy = dyn Trait; + | ^^^^^^^^^^^^^^^ there is an associated type `ExpressionStack` in the trait `gimli::read::op::EvaluationStorage` + +error[E0425]: cannot find function `memchr2` in this scope + --> $DIR/sysroot-private.rs:40:5 + | +LL | memchr2(b'a', b'b', buf) + | ^^^^^^^ not found in this scope + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0220, E0405, E0412, E0425. +For more information about an error, try `rustc --explain E0220`. diff --git a/tests/ui/privacy/union-field-privacy-1.stderr b/tests/ui/privacy/union-field-privacy-1.stderr index b1f0b785ea767..6f883b16d0200 100644 --- a/tests/ui/privacy/union-field-privacy-1.stderr +++ b/tests/ui/privacy/union-field-privacy-1.stderr @@ -2,7 +2,7 @@ error[E0451]: field `c` of union `U` is private --> $DIR/union-field-privacy-1.rs:12:20 | LL | let u = m::U { c: 0 }; - | ^^^^ private field + | ^ private field error[E0451]: field `c` of union `U` is private --> $DIR/union-field-privacy-1.rs:16:16 diff --git a/tests/ui/proc-macro/bad-projection.stderr b/tests/ui/proc-macro/bad-projection.stderr index 2e8668f60de74..aa6b3da6da995 100644 --- a/tests/ui/proc-macro/bad-projection.stderr +++ b/tests/ui/proc-macro/bad-projection.stderr @@ -48,16 +48,17 @@ LL | trait Project { | ^^^^^^^^^^^^^ error[E0277]: the trait bound `(): Project` is not satisfied - --> $DIR/bad-projection.rs:14:40 + --> $DIR/bad-projection.rs:14:17 | LL | pub fn uwu() -> <() as Project>::Assoc {} - | ^^ the trait `Project` is not implemented for `()` + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `Project` is not implemented for `()` | help: this trait has no implementations, consider adding one --> $DIR/bad-projection.rs:9:1 | LL | trait Project { | ^^^^^^^^^^^^^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 5 previous errors diff --git a/tests/ui/proc-macro/disappearing-resolution.stderr b/tests/ui/proc-macro/disappearing-resolution.stderr index e66f721444fc3..734e0cd2ab6df 100644 --- a/tests/ui/proc-macro/disappearing-resolution.stderr +++ b/tests/ui/proc-macro/disappearing-resolution.stderr @@ -22,8 +22,9 @@ LL | pub fn empty_derive(_: TokenStream) -> TokenStream { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ you could import this directly help: import `Empty` directly | -LL | use test_macros::Empty; - | ~~~~~~~~~~~~~~~~~~ +LL - use m::Empty; +LL + use test_macros::Empty; + | error: aborting due to 2 previous errors diff --git a/tests/ui/proc-macro/inner-attr-non-inline-mod.rs b/tests/ui/proc-macro/inner-attr-non-inline-mod.rs index 714463b622508..d4336a7f3e1e0 100644 --- a/tests/ui/proc-macro/inner-attr-non-inline-mod.rs +++ b/tests/ui/proc-macro/inner-attr-non-inline-mod.rs @@ -1,7 +1,6 @@ //@ compile-flags: -Z span-debug //@ error-pattern:custom inner attributes are unstable //@ error-pattern:inner macro attributes are unstable -//@ error-pattern:this was previously accepted //@ proc-macro: test-macros.rs #![no_std] // Don't load unnecessary hygiene information from std diff --git a/tests/ui/proc-macro/inner-attr-non-inline-mod.stderr b/tests/ui/proc-macro/inner-attr-non-inline-mod.stderr index ccc967aaff971..025eec248188b 100644 --- a/tests/ui/proc-macro/inner-attr-non-inline-mod.stderr +++ b/tests/ui/proc-macro/inner-attr-non-inline-mod.stderr @@ -1,3 +1,13 @@ +error[E0658]: custom inner attributes are unstable + --> $DIR/module_with_attrs.rs:3:4 + | +LL | #![rustfmt::skip] + | ^^^^^^^^^^^^^ + | + = note: see issue #54726 for more information + = help: add `#![feature(custom_inner_attributes)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0658]: inner macro attributes are unstable --> $DIR/module_with_attrs.rs:4:4 | @@ -9,7 +19,7 @@ LL | #![print_attr] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: non-inline modules in proc macro input are unstable - --> $DIR/inner-attr-non-inline-mod.rs:14:1 + --> $DIR/inner-attr-non-inline-mod.rs:13:1 | LL | mod module_with_attrs; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -19,7 +29,7 @@ LL | mod module_with_attrs; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: custom inner attributes are unstable - --> $DIR/inner-attr-non-inline-mod.rs:14:1 + --> $DIR/inner-attr-non-inline-mod.rs:13:1 | LL | mod module_with_attrs; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -28,27 +38,6 @@ LL | mod module_with_attrs; = help: add `#![feature(custom_inner_attributes)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: custom inner attributes are unstable - --> $DIR/module_with_attrs.rs:3:4 - | -LL | #![rustfmt::skip] - | ^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #64266 - = note: `#[deny(soft_unstable)]` on by default - error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0658`. -Future incompatibility report: Future breakage diagnostic: -error: custom inner attributes are unstable - --> $DIR/module_with_attrs.rs:3:4 - | -LL | #![rustfmt::skip] - | ^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #64266 - = note: `#[deny(soft_unstable)]` on by default - diff --git a/tests/ui/proc-macro/inner-attr-non-inline-mod.stdout b/tests/ui/proc-macro/inner-attr-non-inline-mod.stdout index aaec40669e67e..450542f68c65c 100644 --- a/tests/ui/proc-macro/inner-attr-non-inline-mod.stdout +++ b/tests/ui/proc-macro/inner-attr-non-inline-mod.stdout @@ -4,35 +4,35 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Alone, - span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "deny", - span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), }, Group { delimiter: Parenthesis, stream: TokenStream [ Ident { ident: "unused_attributes", - span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), }, ], - span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), }, ], - span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), }, Ident { ident: "mod", - span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), }, Ident { ident: "module_with_attrs", - span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), }, Group { delimiter: Brace, @@ -40,38 +40,38 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "rustfmt", - span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), }, Punct { ch: ':', spacing: Joint, - span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), }, Punct { ch: ':', spacing: Alone, - span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), }, Ident { ident: "skip", - span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), }, ], - span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), }, ], - span: $DIR/inner-attr-non-inline-mod.rs:14:1: 14:23 (#0), + span: $DIR/inner-attr-non-inline-mod.rs:13:1: 13:23 (#0), }, ] diff --git a/tests/ui/proc-macro/inner-attrs.stderr b/tests/ui/proc-macro/inner-attrs.stderr index ee8732c650dc9..8b5fec1b4c37e 100644 --- a/tests/ui/proc-macro/inner-attrs.stderr +++ b/tests/ui/proc-macro/inner-attrs.stderr @@ -22,5 +22,13 @@ error: expected non-macro inner attribute, found attribute macro `print_attr` LL | #![print_attr] | ^^^^^^^^^^ not a non-macro inner attribute -error: aborting due to 4 previous errors +warning: extern declarations without an explicit ABI are deprecated + --> $DIR/inner-attrs.rs:82:1 + | +LL | extern { + | ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"` + | + = note: `#[warn(missing_abi)]` on by default + +error: aborting due to 4 previous errors; 1 warning emitted diff --git a/tests/ui/proc-macro/issue-66286.rs b/tests/ui/proc-macro/issue-66286.rs index 57d1af26e934d..882f87fa4ac22 100644 --- a/tests/ui/proc-macro/issue-66286.rs +++ b/tests/ui/proc-macro/issue-66286.rs @@ -5,7 +5,7 @@ extern crate issue_66286; #[issue_66286::vec_ice] -pub extern fn foo(_: Vec(u32)) -> u32 { +pub extern "C" fn foo(_: Vec(u32)) -> u32 { //~^ ERROR: parenthesized type parameters may only be used with a `Fn` trait 0 } diff --git a/tests/ui/proc-macro/issue-66286.stderr b/tests/ui/proc-macro/issue-66286.stderr index fc4c2062fd7bc..fe6bec2216443 100644 --- a/tests/ui/proc-macro/issue-66286.stderr +++ b/tests/ui/proc-macro/issue-66286.stderr @@ -1,13 +1,14 @@ error[E0214]: parenthesized type parameters may only be used with a `Fn` trait - --> $DIR/issue-66286.rs:8:22 + --> $DIR/issue-66286.rs:8:26 | -LL | pub extern fn foo(_: Vec(u32)) -> u32 { - | ^^^^^^^^ only `Fn` traits may use parentheses +LL | pub extern "C" fn foo(_: Vec(u32)) -> u32 { + | ^^^^^^^^ only `Fn` traits may use parentheses | help: use angle brackets instead | -LL | pub extern fn foo(_: Vec) -> u32 { - | ~ ~ +LL - pub extern "C" fn foo(_: Vec(u32)) -> u32 { +LL + pub extern "C" fn foo(_: Vec) -> u32 { + | error: aborting due to 1 previous error diff --git a/tests/ui/proc-macro/issue-86781-bad-inner-doc.stderr b/tests/ui/proc-macro/issue-86781-bad-inner-doc.stderr index aeb824303e715..835648ab435eb 100644 --- a/tests/ui/proc-macro/issue-86781-bad-inner-doc.stderr +++ b/tests/ui/proc-macro/issue-86781-bad-inner-doc.stderr @@ -9,8 +9,9 @@ LL | pub struct Foo; | help: to annotate the struct, change the doc comment from inner to outer style | -LL | /// Inner doc comment - | ~ +LL - //! Inner doc comment +LL + /// Inner doc comment + | error: aborting due to 1 previous error diff --git a/tests/ui/proc-macro/proc-macro-gates.rs b/tests/ui/proc-macro/proc-macro-gates.rs index bf384bc479b51..04e097eb2f745 100644 --- a/tests/ui/proc-macro/proc-macro-gates.rs +++ b/tests/ui/proc-macro/proc-macro-gates.rs @@ -47,7 +47,6 @@ fn attrs() { fn test_case() { #![test] //~ ERROR inner macro attributes are unstable - //~| WARN this was previously accepted } fn main() {} diff --git a/tests/ui/proc-macro/proc-macro-gates.stderr b/tests/ui/proc-macro/proc-macro-gates.stderr index a05a7d0b185e6..3607b062a5fcb 100644 --- a/tests/ui/proc-macro/proc-macro-gates.stderr +++ b/tests/ui/proc-macro/proc-macro-gates.stderr @@ -84,27 +84,16 @@ LL | let _x = #[identity_attr] println!(); = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: inner macro attributes are unstable +error[E0658]: inner macro attributes are unstable --> $DIR/proc-macro-gates.rs:49:8 | LL | #![test] | ^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #64266 - = note: `#[deny(soft_unstable)]` on by default + = note: see issue #54726 for more information + = help: add `#![feature(custom_inner_attributes)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: aborting due to 10 previous errors For more information about this error, try `rustc --explain E0658`. -Future incompatibility report: Future breakage diagnostic: -error: inner macro attributes are unstable - --> $DIR/proc-macro-gates.rs:49:8 - | -LL | #![test] - | ^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #64266 - = note: `#[deny(soft_unstable)]` on by default - diff --git a/tests/ui/proc-macro/quote-debug.rs b/tests/ui/proc-macro/quote-debug.rs deleted file mode 100644 index 11d144d609f5a..0000000000000 --- a/tests/ui/proc-macro/quote-debug.rs +++ /dev/null @@ -1,19 +0,0 @@ -//@ check-pass -//@ force-host -//@ no-prefer-dynamic -//@ compile-flags: -Z unpretty=expanded -//@ needs-unwind compiling proc macros with panic=abort causes a warning -// -// This file is not actually used as a proc-macro - instead, -// it's just used to show the output of the `quote!` macro - -#![feature(proc_macro_quote)] -#![crate_type = "proc-macro"] - -extern crate proc_macro; - -fn main() { - proc_macro::quote! { - let hello = "world"; - } -} diff --git a/tests/ui/proc-macro/quote-debug.stdout b/tests/ui/proc-macro/quote-debug.stdout deleted file mode 100644 index d84b4e051e872..0000000000000 --- a/tests/ui/proc-macro/quote-debug.stdout +++ /dev/null @@ -1,49 +0,0 @@ -#![feature(prelude_import)] -#![no_std] -//@ check-pass -//@ force-host -//@ no-prefer-dynamic -//@ compile-flags: -Z unpretty=expanded -//@ needs-unwind compiling proc macros with panic=abort causes a warning -// -// This file is not actually used as a proc-macro - instead, -// it's just used to show the output of the `quote!` macro - -#![feature(proc_macro_quote)] -#![crate_type = "proc-macro"] -#[prelude_import] -use ::std::prelude::rust_2015::*; -#[macro_use] -extern crate std; - -extern crate proc_macro; - -fn main() { - [crate::TokenStream::from(crate::TokenTree::Ident(crate::Ident::new("let", - crate::Span::recover_proc_macro_span(0)))), - crate::TokenStream::from(crate::TokenTree::Ident(crate::Ident::new("hello", - crate::Span::recover_proc_macro_span(1)))), - crate::TokenStream::from(crate::TokenTree::Punct(crate::Punct::new('=', - crate::Spacing::Alone))), - crate::TokenStream::from(crate::TokenTree::Literal({ - let mut iter = - "\"world\"".parse::().unwrap().into_iter(); - if let (Some(crate::TokenTree::Literal(mut lit)), None) = - (iter.next(), iter.next()) { - lit.set_span(crate::Span::recover_proc_macro_span(2)); - lit - } else { - ::core::panicking::panic("internal error: entered unreachable code") - } - })), - crate::TokenStream::from(crate::TokenTree::Punct(crate::Punct::new(';', - crate::Spacing::Alone)))].iter().cloned().collect::() -} -const _: () = - { - extern crate proc_macro; - #[rustc_proc_macro_decls] - #[used] - #[allow(deprecated)] - static _DECLS: &[proc_macro::bridge::client::ProcMacro] = &[]; - }; diff --git a/tests/ui/proc-macro/quote/auxiliary/basic.rs b/tests/ui/proc-macro/quote/auxiliary/basic.rs new file mode 100644 index 0000000000000..ef726bbfbe3a0 --- /dev/null +++ b/tests/ui/proc-macro/quote/auxiliary/basic.rs @@ -0,0 +1,361 @@ +#![feature(proc_macro_quote)] +#![feature(proc_macro_totokens)] + +extern crate proc_macro; + +use std::borrow::Cow; +use std::ffi::{CStr, CString}; + +use proc_macro::*; + +#[proc_macro] +pub fn run_tests(_: TokenStream) -> TokenStream { + test_quote_impl(); + test_substitution(); + test_advanced(); + test_integer(); + test_floating(); + test_char(); + test_str(); + test_string(); + test_c_str(); + test_c_string(); + test_interpolated_literal(); + test_ident(); + test_underscore(); + test_duplicate(); + test_empty_quote(); + test_box_str(); + test_cow(); + test_append_tokens(); + test_outer_line_comment(); + test_inner_line_comment(); + test_outer_block_comment(); + test_inner_block_comment(); + test_outer_attr(); + test_inner_attr(); + test_quote_raw_id(); + + TokenStream::new() +} + +// Based on https://github.com/dtolnay/quote/blob/0245506323a3616daa2ee41c6ad0b871e4d78ae4/tests/test.rs +// +// FIXME(quote): +// The following tests are removed because they are not supported yet in `proc_macro::quote!` +// +// - quote_spanned: +// - fn test_quote_spanned_impl +// - fn test_type_inference_for_span +// - wrong-type-span.rs +// - format_ident: +// - fn test_format_ident +// - fn test_format_ident_strip_raw +// - repetition: +// - fn test_iter +// - fn test_array +// - fn test_fancy_repetition +// - fn test_nested_fancy_repetition +// - fn test_duplicate_name_repetition +// - fn test_duplicate_name_repetition_no_copy +// - fn test_btreeset_repetition +// - fn test_variable_name_conflict +// - fn test_nonrep_in_repetition +// - fn test_closure +// - fn test_star_after_repetition + +struct X; + +impl ToTokens for X { + fn to_tokens(&self, tokens: &mut TokenStream) { + Ident::new("X", Span::call_site()).to_tokens(tokens) + } +} + +fn test_quote_impl() { + let tokens = quote! { + impl<'a, T: ToTokens> ToTokens for &'a T { + fn to_tokens(&self, tokens: &mut TokenStream) { + (**self).to_tokens(tokens) + } + } + }; + + let expected = r#"impl < 'a, T : ToTokens > ToTokens for & 'a T +{ + fn to_tokens(& self, tokens : & mut TokenStream) + { (** self).to_tokens(tokens) } +}"#; + + assert_eq!(expected, tokens.to_string()); +} + +fn test_substitution() { + let x = X; + let tokens = quote!($x <$x> ($x) [$x] {$x}); + + let expected = "X (X) [X] { X }"; + + assert_eq!(expected, tokens.to_string()); +} + +fn test_advanced() { + let generics = quote!( <'a, T> ); + + let where_clause = quote!( where T: Serialize ); + + let field_ty = quote!(String); + + let item_ty = quote!(Cow<'a, str>); + + let path = quote!(SomeTrait::serialize_with); + + let value = quote!(self.x); + + let tokens = quote! { + struct SerializeWith $generics $where_clause { + value: &'a $field_ty, + phantom: ::std::marker::PhantomData<$item_ty>, + } + + impl $generics ::serde::Serialize for SerializeWith $generics $where_clause { + fn serialize(&self, s: &mut S) -> Result<(), S::Error> + where S: ::serde::Serializer + { + $path(self.value, s) + } + } + + SerializeWith { + value: $value, + phantom: ::std::marker::PhantomData::<$item_ty>, + } + }; + + let expected = r#"struct SerializeWith < 'a, T > where T : Serialize +{ + value : & 'a String, phantom : :: std :: marker :: PhantomData >, +} impl < 'a, T > :: serde :: Serialize for SerializeWith < 'a, T > where T : +Serialize +{ + fn serialize < S > (& self, s : & mut S) -> Result < (), S :: Error > + where S : :: serde :: Serializer + { SomeTrait :: serialize_with(self.value, s) } +} SerializeWith +{ + value : self.x, phantom : :: std :: marker :: PhantomData :: >, +}"#; + + assert_eq!(expected, tokens.to_string()); +} + +fn test_integer() { + let ii8 = -1i8; + let ii16 = -1i16; + let ii32 = -1i32; + let ii64 = -1i64; + let ii128 = -1i128; + let iisize = -1isize; + let uu8 = 1u8; + let uu16 = 1u16; + let uu32 = 1u32; + let uu64 = 1u64; + let uu128 = 1u128; + let uusize = 1usize; + + let tokens = quote! { + 1 1i32 1u256 + $ii8 $ii16 $ii32 $ii64 $ii128 $iisize + $uu8 $uu16 $uu32 $uu64 $uu128 $uusize + }; + let expected = r#"1 1i32 1u256 -1i8 -1i16 -1i32 -1i64 -1i128 -1isize 1u8 1u16 1u32 1u64 1u128 +1usize"#; + assert_eq!(expected, tokens.to_string()); +} + +fn test_floating() { + let e32 = 2.345f32; + + let e64 = 2.345f64; + + let tokens = quote! { + $e32 + $e64 + }; + let expected = concat!("2.345f32 2.345f64"); + assert_eq!(expected, tokens.to_string()); +} + +fn test_char() { + let zero = '\u{1}'; + let dollar = '$'; + let pound = '#'; + let quote = '"'; + let apost = '\''; + let newline = '\n'; + let heart = '\u{2764}'; + + let tokens = quote! { + $zero $dollar $pound $quote $apost $newline $heart + }; + let expected = "'\\u{1}' '$' '#' '\"' '\\'' '\\n' '\u{2764}'"; + assert_eq!(expected, tokens.to_string()); +} + +fn test_str() { + let s = "\u{1} a 'b \" c"; + let tokens = quote!($s); + let expected = "\"\\u{1} a 'b \\\" c\""; + assert_eq!(expected, tokens.to_string()); +} + +fn test_string() { + let s = "\u{1} a 'b \" c".to_string(); + let tokens = quote!($s); + let expected = "\"\\u{1} a 'b \\\" c\""; + assert_eq!(expected, tokens.to_string()); +} + +fn test_c_str() { + let s = CStr::from_bytes_with_nul(b"\x01 a 'b \" c\0").unwrap(); + let tokens = quote!($s); + let expected = "c\"\\u{1} a 'b \\\" c\""; + assert_eq!(expected, tokens.to_string()); +} + +fn test_c_string() { + let s = CString::new(&b"\x01 a 'b \" c"[..]).unwrap(); + let tokens = quote!($s); + let expected = "c\"\\u{1} a 'b \\\" c\""; + assert_eq!(expected, tokens.to_string()); +} + +fn test_interpolated_literal() { + macro_rules! m { + ($literal:literal) => { + quote!($literal) + }; + } + + let tokens = m!(1); + let expected = "1"; + assert_eq!(expected, tokens.to_string()); + + let tokens = m!(-1); + let expected = "- 1"; + assert_eq!(expected, tokens.to_string()); + + let tokens = m!(true); + let expected = "true"; + assert_eq!(expected, tokens.to_string()); + + let tokens = m!(-true); + let expected = "- true"; + assert_eq!(expected, tokens.to_string()); +} + +fn test_ident() { + let foo = Ident::new("Foo", Span::call_site()); + let bar = Ident::new(&format!("Bar{}", 7), Span::call_site()); + let tokens = quote!(struct $foo; enum $bar {}); + let expected = "struct Foo; enum Bar7 {}"; + assert_eq!(expected, tokens.to_string()); +} + +fn test_underscore() { + let tokens = quote!(let _;); + let expected = "let _;"; + assert_eq!(expected, tokens.to_string()); +} + +fn test_duplicate() { + let ch = 'x'; + + let tokens = quote!($ch $ch); + + let expected = "'x' 'x'"; + assert_eq!(expected, tokens.to_string()); +} + +fn test_empty_quote() { + let tokens = quote!(); + assert_eq!("", tokens.to_string()); +} + +fn test_box_str() { + let b = "str".to_owned().into_boxed_str(); + let tokens = quote! { $b }; + assert_eq!("\"str\"", tokens.to_string()); +} + +fn test_cow() { + let owned: Cow = Cow::Owned(Ident::new("owned", Span::call_site())); + + let ident = Ident::new("borrowed", Span::call_site()); + let borrowed = Cow::Borrowed(&ident); + + let tokens = quote! { $owned $borrowed }; + assert_eq!("owned borrowed", tokens.to_string()); +} + +fn test_append_tokens() { + let mut a = quote!(a); + let b = quote!(b); + a.extend(b); + assert_eq!("a b", a.to_string()); +} + +fn test_outer_line_comment() { + let tokens = quote! { + /// doc + }; + let expected = "#[doc = \" doc\"]"; + assert_eq!(expected, tokens.to_string()); +} + +fn test_inner_line_comment() { + let tokens = quote! { + //! doc + }; + let expected = "# ! [doc = \" doc\"]"; + assert_eq!(expected, tokens.to_string()); +} + +fn test_outer_block_comment() { + let tokens = quote! { + /** doc */ + }; + let expected = "#[doc = \" doc \"]"; + assert_eq!(expected, tokens.to_string()); +} + +fn test_inner_block_comment() { + let tokens = quote! { + /*! doc */ + }; + let expected = "# ! [doc = \" doc \"]"; + assert_eq!(expected, tokens.to_string()); +} + +fn test_outer_attr() { + let tokens = quote! { + #[inline] + }; + let expected = "#[inline]"; + assert_eq!(expected, tokens.to_string()); +} + +fn test_inner_attr() { + let tokens = quote! { + #![no_std] + }; + let expected = "#! [no_std]"; + assert_eq!(expected, tokens.to_string()); +} + +fn test_quote_raw_id() { + let id = quote!(r#raw_id); + assert_eq!(id.to_string(), "r#raw_id"); +} diff --git a/tests/ui/proc-macro/quote/basic.rs b/tests/ui/proc-macro/quote/basic.rs new file mode 100644 index 0000000000000..0336dbb785623 --- /dev/null +++ b/tests/ui/proc-macro/quote/basic.rs @@ -0,0 +1,8 @@ +//@ run-pass +//@ proc-macro: basic.rs + +extern crate basic; + +fn main() { + basic::run_tests!(); +} diff --git a/tests/ui/proc-macro/quote/debug.rs b/tests/ui/proc-macro/quote/debug.rs new file mode 100644 index 0000000000000..ce113079e5678 --- /dev/null +++ b/tests/ui/proc-macro/quote/debug.rs @@ -0,0 +1,20 @@ +//@ check-pass +//@ force-host +//@ no-prefer-dynamic +//@ compile-flags: -Z unpretty=expanded +//@ needs-unwind compiling proc macros with panic=abort causes a warning +// +// This file is not actually used as a proc-macro - instead, +// it's just used to show the output of the `quote!` macro + +#![feature(proc_macro_quote)] +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +fn main() { + proc_macro::quote! { + let hello = "world"; + let r#raw_ident = r#"raw"literal"#; + } +} diff --git a/tests/ui/proc-macro/quote/debug.stdout b/tests/ui/proc-macro/quote/debug.stdout new file mode 100644 index 0000000000000..3eaad9eb96997 --- /dev/null +++ b/tests/ui/proc-macro/quote/debug.stdout @@ -0,0 +1,72 @@ +#![feature(prelude_import)] +#![no_std] +//@ check-pass +//@ force-host +//@ no-prefer-dynamic +//@ compile-flags: -Z unpretty=expanded +//@ needs-unwind compiling proc macros with panic=abort causes a warning +// +// This file is not actually used as a proc-macro - instead, +// it's just used to show the output of the `quote!` macro + +#![feature(proc_macro_quote)] +#![crate_type = "proc-macro"] +#[prelude_import] +use ::std::prelude::rust_2015::*; +#[macro_use] +extern crate std; + +extern crate proc_macro; + +fn main() { + { + let mut ts = crate::TokenStream::new(); + crate::ToTokens::to_tokens(&crate::TokenTree::Ident(crate::Ident::new("let", + crate::Span::recover_proc_macro_span(0))), &mut ts); + crate::ToTokens::to_tokens(&crate::TokenTree::Ident(crate::Ident::new("hello", + crate::Span::recover_proc_macro_span(1))), &mut ts); + crate::ToTokens::to_tokens(&crate::TokenTree::Punct(crate::Punct::new('=', + crate::Spacing::Alone)), &mut ts); + crate::ToTokens::to_tokens(&crate::TokenTree::Literal({ + let mut iter = + "\"world\"".parse::().unwrap().into_iter(); + if let (Some(crate::TokenTree::Literal(mut lit)), None) = + (iter.next(), iter.next()) { + lit.set_span(crate::Span::recover_proc_macro_span(2)); + lit + } else { + ::core::panicking::panic("internal error: entered unreachable code") + } + }), &mut ts); + crate::ToTokens::to_tokens(&crate::TokenTree::Punct(crate::Punct::new(';', + crate::Spacing::Alone)), &mut ts); + crate::ToTokens::to_tokens(&crate::TokenTree::Ident(crate::Ident::new("let", + crate::Span::recover_proc_macro_span(3))), &mut ts); + crate::ToTokens::to_tokens(&crate::TokenTree::Ident(crate::Ident::new_raw("raw_ident", + crate::Span::recover_proc_macro_span(4))), &mut ts); + crate::ToTokens::to_tokens(&crate::TokenTree::Punct(crate::Punct::new('=', + crate::Spacing::Alone)), &mut ts); + crate::ToTokens::to_tokens(&crate::TokenTree::Literal({ + let mut iter = + "r#\"raw\"literal\"#".parse::().unwrap().into_iter(); + if let (Some(crate::TokenTree::Literal(mut lit)), None) = + (iter.next(), iter.next()) { + lit.set_span(crate::Span::recover_proc_macro_span(5)); + lit + } else { + ::core::panicking::panic("internal error: entered unreachable code") + } + }), &mut ts); + crate::ToTokens::to_tokens(&crate::TokenTree::Punct(crate::Punct::new(';', + crate::Spacing::Alone)), &mut ts); + ts + } +} +const _: () = + { + extern crate proc_macro; + #[rustc_proc_macro_decls] + #[used] + #[allow(deprecated)] + static _DECLS: &[proc_macro::bridge::client::ProcMacro] = &[]; + }; diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.rs b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.rs new file mode 100644 index 0000000000000..2f67ae1bc6edc --- /dev/null +++ b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.rs @@ -0,0 +1,17 @@ +// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is +// expected to be incorrect. +//@ known-bug: #54722 + +#![feature(proc_macro_quote)] + +extern crate proc_macro; + +use proc_macro::quote; + +fn main() { + let nonrep = ""; + + // Without some protection against repetitions with no iterator somewhere + // inside, this would loop infinitely. + quote!($($nonrep $nonrep)*); +} diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.stderr b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.stderr new file mode 100644 index 0000000000000..5f28a46f3181d --- /dev/null +++ b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated-dup.stderr @@ -0,0 +1,10 @@ +error: proc macro panicked + --> $DIR/does-not-have-iter-interpolated-dup.rs:16:5 + | +LL | quote!($($nonrep $nonrep)*); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: message: `$` must be followed by an ident or `$` in `quote!` + +error: aborting due to 1 previous error + diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.rs b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.rs new file mode 100644 index 0000000000000..1efb3eac64247 --- /dev/null +++ b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.rs @@ -0,0 +1,17 @@ +// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is +// expected to be incorrect. +//@ known-bug: #54722 + +#![feature(proc_macro_quote)] + +extern crate proc_macro; + +use proc_macro::quote; + +fn main() { + let nonrep = ""; + + // Without some protection against repetitions with no iterator somewhere + // inside, this would loop infinitely. + quote!($($nonrep)*); +} diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.stderr b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.stderr new file mode 100644 index 0000000000000..595aa8587634a --- /dev/null +++ b/tests/ui/proc-macro/quote/does-not-have-iter-interpolated.stderr @@ -0,0 +1,10 @@ +error: proc macro panicked + --> $DIR/does-not-have-iter-interpolated.rs:16:5 + | +LL | quote!($($nonrep)*); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: message: `$` must be followed by an ident or `$` in `quote!` + +error: aborting due to 1 previous error + diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-separated.rs b/tests/ui/proc-macro/quote/does-not-have-iter-separated.rs new file mode 100644 index 0000000000000..5f2ddabc390da --- /dev/null +++ b/tests/ui/proc-macro/quote/does-not-have-iter-separated.rs @@ -0,0 +1,13 @@ +// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is +// expected to be incorrect. +//@ known-bug: #54722 + +#![feature(proc_macro_quote)] + +extern crate proc_macro; + +use proc_macro::quote; + +fn main() { + quote!($(a b),*); +} diff --git a/tests/ui/proc-macro/quote/does-not-have-iter-separated.stderr b/tests/ui/proc-macro/quote/does-not-have-iter-separated.stderr new file mode 100644 index 0000000000000..f6f5d7e007d0f --- /dev/null +++ b/tests/ui/proc-macro/quote/does-not-have-iter-separated.stderr @@ -0,0 +1,10 @@ +error: proc macro panicked + --> $DIR/does-not-have-iter-separated.rs:12:5 + | +LL | quote!($(a b),*); + | ^^^^^^^^^^^^^^^^ + | + = help: message: `$` must be followed by an ident or `$` in `quote!` + +error: aborting due to 1 previous error + diff --git a/tests/ui/proc-macro/quote/does-not-have-iter.rs b/tests/ui/proc-macro/quote/does-not-have-iter.rs new file mode 100644 index 0000000000000..25ffd786cc614 --- /dev/null +++ b/tests/ui/proc-macro/quote/does-not-have-iter.rs @@ -0,0 +1,13 @@ +// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is +// expected to be incorrect. +//@ known-bug: #54722 + +#![feature(proc_macro_quote)] + +extern crate proc_macro; + +use proc_macro::quote; + +fn main() { + quote!($(a b)*); +} diff --git a/tests/ui/proc-macro/quote/does-not-have-iter.stderr b/tests/ui/proc-macro/quote/does-not-have-iter.stderr new file mode 100644 index 0000000000000..0ed1daffc8cdf --- /dev/null +++ b/tests/ui/proc-macro/quote/does-not-have-iter.stderr @@ -0,0 +1,10 @@ +error: proc macro panicked + --> $DIR/does-not-have-iter.rs:12:5 + | +LL | quote!($(a b)*); + | ^^^^^^^^^^^^^^^ + | + = help: message: `$` must be followed by an ident or `$` in `quote!` + +error: aborting due to 1 previous error + diff --git a/tests/ui/proc-macro/quote/not-quotable.rs b/tests/ui/proc-macro/quote/not-quotable.rs new file mode 100644 index 0000000000000..7e38b4410528a --- /dev/null +++ b/tests/ui/proc-macro/quote/not-quotable.rs @@ -0,0 +1,12 @@ +#![feature(proc_macro_quote)] + +extern crate proc_macro; + +use std::net::Ipv4Addr; + +use proc_macro::quote; + +fn main() { + let ip = Ipv4Addr::LOCALHOST; + let _ = quote! { $ip }; //~ ERROR the trait bound `Ipv4Addr: ToTokens` is not satisfied +} diff --git a/tests/ui/proc-macro/quote/not-quotable.stderr b/tests/ui/proc-macro/quote/not-quotable.stderr new file mode 100644 index 0000000000000..e349b2dce5300 --- /dev/null +++ b/tests/ui/proc-macro/quote/not-quotable.stderr @@ -0,0 +1,24 @@ +error[E0277]: the trait bound `Ipv4Addr: ToTokens` is not satisfied + --> $DIR/not-quotable.rs:11:13 + | +LL | let _ = quote! { $ip }; + | ^^^^^^^^^^^^^^ + | | + | the trait `ToTokens` is not implemented for `Ipv4Addr` + | required by a bound introduced by this call + | + = help: the following other types implement trait `ToTokens`: + &T + &mut T + Box + CString + Cow<'_, T> + Option + Rc + bool + and 24 others + = note: this error originates in the macro `quote` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/proc-macro/quote/not-repeatable.rs b/tests/ui/proc-macro/quote/not-repeatable.rs new file mode 100644 index 0000000000000..d115da7318156 --- /dev/null +++ b/tests/ui/proc-macro/quote/not-repeatable.rs @@ -0,0 +1,16 @@ +// FIXME(quote): `proc_macro::quote!` doesn't support repetition at the moment, so the stderr is +// expected to be incorrect. +//@ known-bug: #54722 + +#![feature(proc_macro_quote)] + +extern crate proc_macro; + +use proc_macro::quote; + +struct Ipv4Addr; + +fn main() { + let ip = Ipv4Addr; + let _ = quote! { $($ip)* }; +} diff --git a/tests/ui/proc-macro/quote/not-repeatable.stderr b/tests/ui/proc-macro/quote/not-repeatable.stderr new file mode 100644 index 0000000000000..18fbcd7379858 --- /dev/null +++ b/tests/ui/proc-macro/quote/not-repeatable.stderr @@ -0,0 +1,10 @@ +error: proc macro panicked + --> $DIR/not-repeatable.rs:15:13 + | +LL | let _ = quote! { $($ip)* }; + | ^^^^^^^^^^^^^^^^^^ + | + = help: message: `$` must be followed by an ident or `$` in `quote!` + +error: aborting due to 1 previous error + diff --git a/tests/ui/process/core-run-destroy.rs b/tests/ui/process/core-run-destroy.rs index 3f2ea0e844116..b4815c9dfbb52 100644 --- a/tests/ui/process/core-run-destroy.rs +++ b/tests/ui/process/core-run-destroy.rs @@ -1,12 +1,11 @@ //@ run-pass #![allow(unused_must_use)] -#![allow(stable_features)] #![allow(deprecated)] #![allow(unused_imports)] + //@ compile-flags:--test -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess //@ ignore-vxworks no 'cat' and 'sleep' //@ ignore-fuchsia no 'cat' diff --git a/tests/ui/process/env-args-reverse-iterator.rs b/tests/ui/process/env-args-reverse-iterator.rs index 830e953546644..f0afeeb22eb73 100644 --- a/tests/ui/process/env-args-reverse-iterator.rs +++ b/tests/ui/process/env-args-reverse-iterator.rs @@ -1,6 +1,5 @@ //@ run-pass -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess use std::env::args; use std::process::Command; diff --git a/tests/ui/process/fds-are-cloexec.rs b/tests/ui/process/fds-are-cloexec.rs index e7b000b2c499f..f6678379dd675 100644 --- a/tests/ui/process/fds-are-cloexec.rs +++ b/tests/ui/process/fds-are-cloexec.rs @@ -1,9 +1,8 @@ //@ run-pass -//@ ignore-windows +//@ only-unix //@ ignore-android -//@ ignore-wasm32 no processes +//@ needs-subprocess //@ ignore-haiku -//@ ignore-sgx no processes #![feature(rustc_private)] diff --git a/tests/ui/process/inherit-env.rs b/tests/ui/process/inherit-env.rs index 0eb61fcdd53b3..09d5b76141ef3 100644 --- a/tests/ui/process/inherit-env.rs +++ b/tests/ui/process/inherit-env.rs @@ -1,6 +1,5 @@ //@ run-pass -//@ ignore-wasm32 no subprocess support -//@ ignore-sgx no processes +//@ needs-subprocess use std::env; use std::process::Command; diff --git a/tests/ui/process/issue-13304.rs b/tests/ui/process/issue-13304.rs index 6dbf0caaaecee..621c54300d378 100644 --- a/tests/ui/process/issue-13304.rs +++ b/tests/ui/process/issue-13304.rs @@ -1,7 +1,5 @@ //@ run-pass -#![allow(unused_mut)] -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess use std::env; use std::io::prelude::*; @@ -32,7 +30,7 @@ fn parent() { } fn child() { - let mut stdin = io::stdin(); + let stdin = io::stdin(); for line in stdin.lock().lines() { println!("{}", line.unwrap()); } diff --git a/tests/ui/process/issue-14456.rs b/tests/ui/process/issue-14456.rs index fd6da8a5fc471..e67a9d8bad559 100644 --- a/tests/ui/process/issue-14456.rs +++ b/tests/ui/process/issue-14456.rs @@ -1,7 +1,5 @@ //@ run-pass -#![allow(unused_mut)] -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess use std::env; use std::io::prelude::*; @@ -20,7 +18,7 @@ fn main() { fn child() { writeln!(&mut io::stdout(), "foo").unwrap(); writeln!(&mut io::stderr(), "bar").unwrap(); - let mut stdin = io::stdin(); + let stdin = io::stdin(); let mut s = String::new(); stdin.lock().read_line(&mut s).unwrap(); assert_eq!(s.len(), 0); diff --git a/tests/ui/process/issue-14940.rs b/tests/ui/process/issue-14940.rs index 13fb18154a0db..cfbc743250fde 100644 --- a/tests/ui/process/issue-14940.rs +++ b/tests/ui/process/issue-14940.rs @@ -1,6 +1,5 @@ //@ run-pass -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess use std::env; use std::process::Command; diff --git a/tests/ui/process/issue-16272.rs b/tests/ui/process/issue-16272.rs index bf26769d494f6..7270855475346 100644 --- a/tests/ui/process/issue-16272.rs +++ b/tests/ui/process/issue-16272.rs @@ -1,6 +1,5 @@ //@ run-pass -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess use std::process::Command; use std::env; diff --git a/tests/ui/process/issue-20091.rs b/tests/ui/process/issue-20091.rs index b6d94661b75c2..72bf4c0e71eee 100644 --- a/tests/ui/process/issue-20091.rs +++ b/tests/ui/process/issue-20091.rs @@ -1,9 +1,5 @@ //@ run-pass -#![allow(stable_features)] -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes - -#![feature(os)] +//@ needs-subprocess #[cfg(unix)] fn main() { diff --git a/tests/ui/process/issue-30490.rs b/tests/ui/process/issue-30490.rs index 0d918bc3dd549..75b36e7c20b24 100644 --- a/tests/ui/process/issue-30490.rs +++ b/tests/ui/process/issue-30490.rs @@ -1,6 +1,5 @@ //@ run-pass -//@ ignore-emscripten no processes -//@ ignore-sgx no processes +//@ needs-subprocess //@ ignore-fuchsia Child I/O swaps not privileged // Previously libstd would set stdio descriptors of a child process diff --git a/tests/ui/process/multi-panic.rs b/tests/ui/process/multi-panic.rs index ad47925a14987..481fe75c731de 100644 --- a/tests/ui/process/multi-panic.rs +++ b/tests/ui/process/multi-panic.rs @@ -1,6 +1,5 @@ //@ run-pass -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess //@ needs-unwind fn check_for_no_backtrace(test: std::process::Output) { diff --git a/tests/ui/process/no-stdio.rs b/tests/ui/process/no-stdio.rs index 8eebf6dbc7d4e..5cc7cacbb22f0 100644 --- a/tests/ui/process/no-stdio.rs +++ b/tests/ui/process/no-stdio.rs @@ -1,7 +1,6 @@ //@ run-pass //@ ignore-android -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess #![feature(rustc_private)] diff --git a/tests/ui/process/println-with-broken-pipe.rs b/tests/ui/process/println-with-broken-pipe.rs index d88c6dcc12b31..fbac9b6cd958f 100644 --- a/tests/ui/process/println-with-broken-pipe.rs +++ b/tests/ui/process/println-with-broken-pipe.rs @@ -1,7 +1,7 @@ //@ run-pass //@ check-run-results +//@ needs-subprocess //@ ignore-windows -//@ ignore-wasm32 //@ ignore-fuchsia //@ ignore-horizon //@ ignore-android diff --git a/tests/ui/process/process-envs.rs b/tests/ui/process/process-envs.rs index 15285960d16ae..98052f1d3a5d7 100644 --- a/tests/ui/process/process-envs.rs +++ b/tests/ui/process/process-envs.rs @@ -1,6 +1,5 @@ //@ run-pass -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess //@ ignore-vxworks no 'env' //@ ignore-fuchsia no 'env' diff --git a/tests/ui/process/process-exit.rs b/tests/ui/process/process-exit.rs index a75a7306cbcd0..a1ed243b62b69 100644 --- a/tests/ui/process/process-exit.rs +++ b/tests/ui/process/process-exit.rs @@ -1,10 +1,8 @@ //@ run-pass -#![allow(unused_imports)] -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess use std::env; -use std::process::{self, Command, Stdio}; +use std::process::{self, Command}; fn main() { let args: Vec = env::args().collect(); diff --git a/tests/ui/process/process-panic-after-fork.rs b/tests/ui/process/process-panic-after-fork.rs index afb1b721182e5..6e0267e0a54f5 100644 --- a/tests/ui/process/process-panic-after-fork.rs +++ b/tests/ui/process/process-panic-after-fork.rs @@ -1,8 +1,7 @@ //@ run-pass //@ no-prefer-dynamic -//@ ignore-windows -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ only-unix +//@ needs-subprocess //@ ignore-fuchsia no fork #![feature(rustc_private)] diff --git a/tests/ui/process/process-remove-from-env.rs b/tests/ui/process/process-remove-from-env.rs index 21fff4fd45d1d..c1a2b2daf5b63 100644 --- a/tests/ui/process/process-remove-from-env.rs +++ b/tests/ui/process/process-remove-from-env.rs @@ -1,6 +1,5 @@ //@ run-pass -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess //@ ignore-vxworks no 'env' //@ ignore-fuchsia no 'env' diff --git a/tests/ui/process/process-sigpipe.rs b/tests/ui/process/process-sigpipe.rs index 9db130c26bd68..453e53379fc49 100644 --- a/tests/ui/process/process-sigpipe.rs +++ b/tests/ui/process/process-sigpipe.rs @@ -15,7 +15,7 @@ //@ ignore-vxworks no 'sh' //@ ignore-fuchsia no 'sh' -//@ ignore-emscripten No threads +//@ needs-threads //@ only-unix SIGPIPE is a unix feature use std::process; diff --git a/tests/ui/process/process-spawn-nonexistent.rs b/tests/ui/process/process-spawn-nonexistent.rs index 1cd328662994d..3db670624fb37 100644 --- a/tests/ui/process/process-spawn-nonexistent.rs +++ b/tests/ui/process/process-spawn-nonexistent.rs @@ -1,6 +1,5 @@ //@ run-pass -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess //@ ignore-fuchsia ErrorKind not translated use std::io::ErrorKind; diff --git a/tests/ui/process/process-spawn-with-unicode-params.rs b/tests/ui/process/process-spawn-with-unicode-params.rs index 4d2ba49eeac71..65f835c13459c 100644 --- a/tests/ui/process/process-spawn-with-unicode-params.rs +++ b/tests/ui/process/process-spawn-with-unicode-params.rs @@ -7,8 +7,7 @@ // non-ASCII characters. The child process ensures all the strings are // intact. -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess //@ ignore-fuchsia Filesystem manipulation privileged use std::io::prelude::*; diff --git a/tests/ui/process/process-status-inherits-stdin.rs b/tests/ui/process/process-status-inherits-stdin.rs index 39eef34c5f862..d5dd0e55fa339 100644 --- a/tests/ui/process/process-status-inherits-stdin.rs +++ b/tests/ui/process/process-status-inherits-stdin.rs @@ -1,6 +1,5 @@ //@ run-pass -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess use std::env; use std::io; diff --git a/tests/ui/process/signal-exit-status.rs b/tests/ui/process/signal-exit-status.rs index a6acea476367a..33aa83abfc377 100644 --- a/tests/ui/process/signal-exit-status.rs +++ b/tests/ui/process/signal-exit-status.rs @@ -1,7 +1,6 @@ //@ run-pass -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes -//@ ignore-windows +//@ needs-subprocess +//@ only-unix (`code()` returns `None` if terminated by a signal on Unix) //@ ignore-fuchsia code returned as ZX_TASK_RETCODE_EXCEPTION_KILL, FIXME (#58590) #![feature(core_intrinsics)] diff --git a/tests/ui/process/sigpipe-should-be-ignored.rs b/tests/ui/process/sigpipe-should-be-ignored.rs index 44785bee7f80c..3dcf0117ae971 100644 --- a/tests/ui/process/sigpipe-should-be-ignored.rs +++ b/tests/ui/process/sigpipe-should-be-ignored.rs @@ -1,12 +1,9 @@ //@ run-pass +//@ needs-subprocess -#![allow(unused_must_use)] // Be sure that when a SIGPIPE would have been received that the entire process // doesn't die in a ball of fire, but rather it's gracefully handled. -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes - use std::env; use std::io::prelude::*; use std::io; @@ -14,7 +11,7 @@ use std::process::{Command, Stdio}; fn test() { let _ = io::stdin().read_line(&mut String::new()); - io::stdout().write(&[1]); + io::stdout().write(&[1]).unwrap(); assert!(io::stdout().flush().is_err()); } diff --git a/tests/ui/process/tls-exit-status.rs b/tests/ui/process/tls-exit-status.rs index cddcf369da0bd..6dd0d71ef356a 100644 --- a/tests/ui/process/tls-exit-status.rs +++ b/tests/ui/process/tls-exit-status.rs @@ -1,7 +1,7 @@ //@ run-fail //@ error-pattern:nonzero //@ exec-env:RUST_NEWRT=1 -//@ ignore-wasm32 no processes +//@ needs-subprocess use std::env; diff --git a/tests/ui/process/try-wait.rs b/tests/ui/process/try-wait.rs index b6d026d802ff6..dcef43ad348ab 100644 --- a/tests/ui/process/try-wait.rs +++ b/tests/ui/process/try-wait.rs @@ -1,9 +1,5 @@ //@ run-pass - -#![allow(stable_features)] -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes -#![feature(process_try_wait)] +//@ needs-subprocess use std::env; use std::process::Command; diff --git a/tests/ui/process/win-creation-flags.rs b/tests/ui/process/win-creation-flags.rs new file mode 100644 index 0000000000000..0c7c6883d68e3 --- /dev/null +++ b/tests/ui/process/win-creation-flags.rs @@ -0,0 +1,51 @@ +// Test that windows `creation_flags` extension to `Command` works. + +//@ run-pass +//@ only-windows +//@ needs-subprocess + +use std::env; +use std::os::windows::process::CommandExt; +use std::process::{Command, exit}; + +fn main() { + if env::args().skip(1).any(|s| s == "--child") { + child(); + } else { + parent(); + } +} + +fn parent() { + let exe = env::current_exe().unwrap(); + + // Use the DETACH_PROCESS to create a subprocess that isn't attached to the console. + // The subprocess's exit status will be 0 if it's detached. + let status = Command::new(&exe) + .arg("--child") + .creation_flags(DETACH_PROCESS) + .spawn() + .unwrap() + .wait() + .unwrap(); + assert_eq!(status.code(), Some(0)); + + // Try without DETACH_PROCESS to ensure this test works. + let status = Command::new(&exe).arg("--child").spawn().unwrap().wait().unwrap(); + assert_eq!(status.code(), Some(1)); +} + +// exits with 1 if the console is attached or 0 otherwise +fn child() { + // Get the attached console's code page. + // This will fail (return 0) if no console is attached. + let has_console = GetConsoleCP() != 0; + exit(has_console as i32); +} + +// Windows API definitions. +const DETACH_PROCESS: u32 = 0x00000008; +#[link(name = "kernel32")] +unsafe extern "system" { + safe fn GetConsoleCP() -> u32; +} diff --git a/tests/ui/process/win-proc-thread-attributes.rs b/tests/ui/process/win-proc-thread-attributes.rs new file mode 100644 index 0000000000000..91bb0b17e9539 --- /dev/null +++ b/tests/ui/process/win-proc-thread-attributes.rs @@ -0,0 +1,118 @@ +// Tests proc thread attributes by spawning a process with a custom parent process, +// then comparing the parent process ID with the expected parent process ID. + +//@ run-pass +//@ only-windows +//@ needs-subprocess +//@ edition: 2021 + +#![feature(windows_process_extensions_raw_attribute)] + +use std::os::windows::io::AsRawHandle; +use std::os::windows::process::{CommandExt, ProcThreadAttributeList}; +use std::process::{Child, Command}; +use std::{env, mem, ptr, thread, time}; + +// Make a best effort to ensure child processes always exit. +struct ProcessDropGuard(Child); +impl Drop for ProcessDropGuard { + fn drop(&mut self) { + let _ = self.0.kill(); + } +} + +fn main() { + if env::args().skip(1).any(|s| s == "--child") { + child(); + } else { + parent(); + } +} + +fn parent() { + let exe = env::current_exe().unwrap(); + + let (fake_parent_id, child_parent_id) = { + // Create a process to be our fake parent process. + let fake_parent = Command::new(&exe).arg("--child").spawn().unwrap(); + let fake_parent = ProcessDropGuard(fake_parent); + let parent_handle = fake_parent.0.as_raw_handle(); + + // Create another process with the parent process set to the fake. + let mut attribute_list = ProcThreadAttributeList::build() + .attribute(PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parent_handle) + .finish() + .unwrap(); + let child = + Command::new(&exe).arg("--child").spawn_with_attributes(&mut attribute_list).unwrap(); + let child = ProcessDropGuard(child); + + // Return the fake's process id and the child's parent's id. + (process_info(&fake_parent.0).process_id(), process_info(&child.0).parent_id()) + }; + + assert_eq!(fake_parent_id, child_parent_id); +} + +// A process that stays running until killed. +fn child() { + // Don't wait forever if something goes wrong. + thread::sleep(time::Duration::from_secs(60)); +} + +fn process_info(child: &Child) -> PROCESS_BASIC_INFORMATION { + unsafe { + let mut info: PROCESS_BASIC_INFORMATION = mem::zeroed(); + let result = NtQueryInformationProcess( + child.as_raw_handle(), + ProcessBasicInformation, + ptr::from_mut(&mut info).cast(), + mem::size_of_val(&info).try_into().unwrap(), + ptr::null_mut(), + ); + assert_eq!(result, 0); + info + } +} + +// Windows API +mod winapi { + #![allow(nonstandard_style)] + use std::ffi::c_void; + + pub type HANDLE = *mut c_void; + type NTSTATUS = i32; + type PROCESSINFOCLASS = i32; + + pub const ProcessBasicInformation: i32 = 0; + pub const PROC_THREAD_ATTRIBUTE_PARENT_PROCESS: usize = 0x00020000; + #[repr(C)] + pub struct PROCESS_BASIC_INFORMATION { + pub ExitStatus: NTSTATUS, + pub PebBaseAddress: *mut (), + pub AffinityMask: usize, + pub BasePriority: i32, + pub UniqueProcessId: usize, + pub InheritedFromUniqueProcessId: usize, + } + impl PROCESS_BASIC_INFORMATION { + pub fn parent_id(&self) -> usize { + self.InheritedFromUniqueProcessId + } + pub fn process_id(&self) -> usize { + self.UniqueProcessId + } + } + + #[link(name = "ntdll")] + extern "system" { + pub fn NtQueryInformationProcess( + ProcessHandle: HANDLE, + ProcessInformationClass: PROCESSINFOCLASS, + ProcessInformation: *mut c_void, + ProcessInformationLength: u32, + ReturnLength: *mut u32, + ) -> NTSTATUS; + } +} +use winapi::*; diff --git a/tests/ui/pub/pub-ident-fn-or-struct.stderr b/tests/ui/pub/pub-ident-fn-or-struct.stderr index ceadc510c63ef..99c8e5754ef73 100644 --- a/tests/ui/pub/pub-ident-fn-or-struct.stderr +++ b/tests/ui/pub/pub-ident-fn-or-struct.stderr @@ -7,7 +7,7 @@ LL | pub S (foo) bar help: if you meant to call a macro, try | LL | pub S! (foo) bar - | ~~ + | + error: aborting due to 1 previous error diff --git a/tests/ui/pub/pub-restricted.stderr b/tests/ui/pub/pub-restricted.stderr index fc177aa2033e1..6c913938bb89a 100644 --- a/tests/ui/pub/pub-restricted.stderr +++ b/tests/ui/pub/pub-restricted.stderr @@ -11,7 +11,7 @@ LL | pub (a) fn afn() {} help: make this visible only to module `a` with `in` | LL | pub (in a) fn afn() {} - | ~~~~ + | ++ error[E0704]: incorrect visibility restriction --> $DIR/pub-restricted.rs:4:6 @@ -26,7 +26,7 @@ LL | pub (b) fn bfn() {} help: make this visible only to module `b` with `in` | LL | pub (in b) fn bfn() {} - | ~~~~ + | ++ error[E0704]: incorrect visibility restriction --> $DIR/pub-restricted.rs:5:6 @@ -41,7 +41,7 @@ LL | pub (crate::a) fn cfn() {} help: make this visible only to module `crate::a` with `in` | LL | pub (in crate::a) fn cfn() {} - | ~~~~~~~~~~~ + | ++ error[E0704]: incorrect visibility restriction --> $DIR/pub-restricted.rs:22:14 @@ -56,7 +56,7 @@ LL | pub (a) invalid: usize, help: make this visible only to module `a` with `in` | LL | pub (in a) invalid: usize, - | ~~~~ + | ++ error[E0704]: incorrect visibility restriction --> $DIR/pub-restricted.rs:31:6 @@ -71,7 +71,7 @@ LL | pub (xyz) fn xyz() {} help: make this visible only to module `xyz` with `in` | LL | pub (in xyz) fn xyz() {} - | ~~~~~~ + | ++ error[E0742]: visibilities can only be restricted to ancestor modules --> $DIR/pub-restricted.rs:23:17 diff --git a/tests/ui/qualified/qualified-path-params-2.stderr b/tests/ui/qualified/qualified-path-params-2.stderr index 56644bdd46a8d..6641e81013f05 100644 --- a/tests/ui/qualified/qualified-path-params-2.stderr +++ b/tests/ui/qualified/qualified-path-params-2.stderr @@ -6,8 +6,9 @@ LL | type A = ::A::f; | help: if there were a trait named `Example` with associated type `f` implemented for `::A`, you could use the fully-qualified path | -LL | type A = <::A as Example>::f; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - type A = ::A::f; +LL + type A = <::A as Example>::f; + | error: aborting due to 1 previous error diff --git a/tests/ui/query-system/query_depth.stderr b/tests/ui/query-system/query_depth.stderr index d455e0e4ff8fa..f738b01ed6ca7 100644 --- a/tests/ui/query-system/query_depth.stderr +++ b/tests/ui/query-system/query_depth.stderr @@ -5,7 +5,7 @@ LL | fn main() { | ^^^^^^^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "128"]` attribute to your crate (`query_depth`) - = note: query depth increased by 66 when computing layout of `core::option::Option>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` + = note: query depth increased by 65 when computing layout of `core::option::Option>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` error: aborting due to 1 previous error diff --git a/tests/ui/range/issue-54505-no-literals.stderr b/tests/ui/range/issue-54505-no-literals.stderr index 5894bb6ba553f..c6d4384bcd33c 100644 --- a/tests/ui/range/issue-54505-no-literals.stderr +++ b/tests/ui/range/issue-54505-no-literals.stderr @@ -47,7 +47,7 @@ LL | take_range(std::ops::RangeFrom { start: 1 }); | arguments to this function are incorrect | = note: expected reference `&_` - found struct `RangeFrom<{integer}>` + found struct `std::ops::RangeFrom<{integer}>` note: function defined here --> $DIR/issue-54505-no-literals.rs:12:4 | @@ -67,7 +67,7 @@ LL | take_range(::std::ops::RangeFrom { start: 1 }); | arguments to this function are incorrect | = note: expected reference `&_` - found struct `RangeFrom<{integer}>` + found struct `std::ops::RangeFrom<{integer}>` note: function defined here --> $DIR/issue-54505-no-literals.rs:12:4 | @@ -127,7 +127,7 @@ LL | take_range(std::ops::RangeInclusive::new(0, 1)); | arguments to this function are incorrect | = note: expected reference `&_` - found struct `RangeInclusive<{integer}>` + found struct `std::ops::RangeInclusive<{integer}>` note: function defined here --> $DIR/issue-54505-no-literals.rs:12:4 | @@ -147,7 +147,7 @@ LL | take_range(::std::ops::RangeInclusive::new(0, 1)); | arguments to this function are incorrect | = note: expected reference `&_` - found struct `RangeInclusive<{integer}>` + found struct `std::ops::RangeInclusive<{integer}>` note: function defined here --> $DIR/issue-54505-no-literals.rs:12:4 | diff --git a/tests/ui/range/issue-54505.stderr b/tests/ui/range/issue-54505.stderr index 291e097e8659f..8b669b2910f6b 100644 --- a/tests/ui/range/issue-54505.stderr +++ b/tests/ui/range/issue-54505.stderr @@ -27,7 +27,7 @@ LL | take_range(1..); | arguments to this function are incorrect | = note: expected reference `&_` - found struct `RangeFrom<{integer}>` + found struct `std::ops::RangeFrom<{integer}>` note: function defined here --> $DIR/issue-54505.rs:10:4 | @@ -72,7 +72,7 @@ LL | take_range(0..=1); | arguments to this function are incorrect | = note: expected reference `&_` - found struct `RangeInclusive<{integer}>` + found struct `std::ops::RangeInclusive<{integer}>` note: function defined here --> $DIR/issue-54505.rs:10:4 | diff --git a/tests/ui/range/range-1.stderr b/tests/ui/range/range-1.stderr index f77601bc43cd4..37669dd3f47c7 100644 --- a/tests/ui/range/range-1.stderr +++ b/tests/ui/range/range-1.stderr @@ -30,7 +30,7 @@ LL | let range = *arr..; | ^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `[{integer}]` -note: required by an implicit `Sized` bound in `RangeFrom` +note: required by an implicit `Sized` bound in `std::ops::RangeFrom` --> $SRC_DIR/core/src/ops/range.rs:LL:COL error: aborting due to 3 previous errors diff --git a/tests/ui/reachable/issue-948.rs b/tests/ui/reachable/issue-948.rs index 8e239a1115eb4..6181e547acc81 100644 --- a/tests/ui/reachable/issue-948.rs +++ b/tests/ui/reachable/issue-948.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:beep boop -//@ ignore-emscripten no processes +//@ needs-subprocess #![allow(unused_variables)] diff --git a/tests/ui/recursion/issue-38591-non-regular-dropck-recursion.polonius.stderr b/tests/ui/recursion/issue-38591-non-regular-dropck-recursion.polonius.stderr deleted file mode 100644 index ff1a127e63e3e..0000000000000 --- a/tests/ui/recursion/issue-38591-non-regular-dropck-recursion.polonius.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: reached the recursion limit while instantiating `std::ptr::drop_in_place::))` - --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL - | -LL | pub unsafe fn drop_in_place(to_drop: *mut T) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: `std::ptr::drop_in_place` defined here - --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL - | -LL | pub unsafe fn drop_in_place(to_drop: *mut T) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: the full type name has been written to '$TEST_BUILD_DIR/recursion/issue-38591-non-regular-dropck-recursion.polonius/issue-38591-non-regular-dropck-recursion.long-type.txt' - -error: aborting due to 1 previous error - diff --git a/tests/ui/recursion/recursion.polonius.stderr b/tests/ui/recursion/recursion.polonius.stderr deleted file mode 100644 index 737e71e884518..0000000000000 --- a/tests/ui/recursion/recursion.polonius.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: reached the recursion limit while instantiating `test::>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` - --> $DIR/recursion.rs:18:11 - | -LL | _ => {test (n-1, i+1, Cons {head:2*i+1, tail:first}, Cons{head:i*i, tail:second})} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: `test` defined here - --> $DIR/recursion.rs:16:1 - | -LL | fn test (n:isize, i:isize, first:T, second:T) ->isize { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: the full type name has been written to '$TEST_BUILD_DIR/recursion/recursion.polonius/recursion.long-type.txt' - -error: aborting due to 1 previous error - diff --git a/tests/ui/recursion/recursion.rs b/tests/ui/recursion/recursion.rs index f3c633983b199..ce56fe974b7c6 100644 --- a/tests/ui/recursion/recursion.rs +++ b/tests/ui/recursion/recursion.rs @@ -1,6 +1,7 @@ //@ build-fail //@ compile-flags:-C overflow-checks=off -//@ normalize-stderr: ".nll/" -> "/" +// The regex below normalizes the long type file name to make it suitable for compare-modes. +//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type.txt'" enum Nil {NilValue} struct Cons {head:isize, tail:T} diff --git a/tests/ui/recursion/recursion.stderr b/tests/ui/recursion/recursion.stderr index 7a9f04d4bd6d3..cb9f67ba7418d 100644 --- a/tests/ui/recursion/recursion.stderr +++ b/tests/ui/recursion/recursion.stderr @@ -1,15 +1,15 @@ error: reached the recursion limit while instantiating `test::>>>>>` - --> $DIR/recursion.rs:18:11 + --> $DIR/recursion.rs:19:11 | LL | _ => {test (n-1, i+1, Cons {head:2*i+1, tail:first}, Cons{head:i*i, tail:second})} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `test` defined here - --> $DIR/recursion.rs:16:1 + --> $DIR/recursion.rs:17:1 | LL | fn test (n:isize, i:isize, first:T, second:T) ->isize { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: the full type name has been written to '$TEST_BUILD_DIR/recursion/recursion/recursion.long-type.txt' + = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type.txt' error: aborting due to 1 previous error diff --git a/tests/ui/regions/better-blame-constraint-for-outlives-static.rs b/tests/ui/regions/better-blame-constraint-for-outlives-static.rs new file mode 100644 index 0000000000000..77cadb783016c --- /dev/null +++ b/tests/ui/regions/better-blame-constraint-for-outlives-static.rs @@ -0,0 +1,13 @@ +//! diagnostic test for #132749: ensure we pick a decent span and reason to blame for region errors +//! when failing to prove a region outlives 'static + +struct Bytes(&'static [u8]); + +fn deserialize_simple_string(buf: &[u8]) -> (Bytes, &[u8]) { + //~^ NOTE let's call the lifetime of this reference `'1` + let (s, rest) = buf.split_at(2); + (Bytes(s), rest) //~ ERROR lifetime may not live long enough + //~| NOTE this usage requires that `'1` must outlive `'static` +} + +fn main() {} diff --git a/tests/ui/regions/better-blame-constraint-for-outlives-static.stderr b/tests/ui/regions/better-blame-constraint-for-outlives-static.stderr new file mode 100644 index 0000000000000..e1e88829f2c4f --- /dev/null +++ b/tests/ui/regions/better-blame-constraint-for-outlives-static.stderr @@ -0,0 +1,11 @@ +error: lifetime may not live long enough + --> $DIR/better-blame-constraint-for-outlives-static.rs:9:12 + | +LL | fn deserialize_simple_string(buf: &[u8]) -> (Bytes, &[u8]) { + | - let's call the lifetime of this reference `'1` +... +LL | (Bytes(s), rest) + | ^ this usage requires that `'1` must outlive `'static` + +error: aborting due to 1 previous error + diff --git a/tests/ui/regions/issue-26448-1.rs b/tests/ui/regions/issue-26448-1.rs index 0fa40709a7b95..4d1853a93727e 100644 --- a/tests/ui/regions/issue-26448-1.rs +++ b/tests/ui/regions/issue-26448-1.rs @@ -1,4 +1,7 @@ -//@ run-pass +//@ revisions: current next +//@ [next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass pub trait Foo { fn foo(self) -> T; diff --git a/tests/ui/regions/issue-26448-2.rs b/tests/ui/regions/issue-26448-2.rs index 5fd1e90ebfd96..2e124555125eb 100644 --- a/tests/ui/regions/issue-26448-2.rs +++ b/tests/ui/regions/issue-26448-2.rs @@ -1,3 +1,6 @@ +//@ revisions: current next +//@ [next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) //@ check-pass pub struct Bar { diff --git a/tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.stderr b/tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.stderr index fcd0a232a7bd3..9f1315070eb29 100644 --- a/tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.stderr +++ b/tests/ui/regions/lifetime-not-long-enough-suggestion-regression-test-124563.stderr @@ -25,6 +25,10 @@ LL | self.enter_scope(|ctx| { | has type `&mut FooImpl<'2, '_, T>` LL | BarImpl(ctx); | ^^^ this usage requires that `'1` must outlive `'2` + | + = note: requirement occurs because of a mutable reference to `FooImpl<'_, '_, T>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance error: lifetime may not live long enough --> $DIR/lifetime-not-long-enough-suggestion-regression-test-124563.rs:22:9 diff --git a/tests/ui/regions/region-invariant-static-error-reporting.rs b/tests/ui/regions/region-invariant-static-error-reporting.rs index 9ab46a7757897..e58eea3f61ae7 100644 --- a/tests/ui/regions/region-invariant-static-error-reporting.rs +++ b/tests/ui/regions/region-invariant-static-error-reporting.rs @@ -3,7 +3,7 @@ // over time, but this test used to exhibit some pretty bogus messages // that were not remotely helpful. -//@ error-pattern:argument requires that `'a` must outlive `'static` +//@ error-pattern:requires that `'a` must outlive `'static` struct Invariant<'a>(Option<&'a mut &'a mut ()>); @@ -11,9 +11,9 @@ fn mk_static() -> Invariant<'static> { Invariant(None) } fn unify<'a>(x: Option>, f: fn(Invariant<'a>)) { let bad = if x.is_some() { - x.unwrap() //~ ERROR borrowed data escapes outside of function [E0521] + x.unwrap() } else { - mk_static() + mk_static() //~ ERROR lifetime may not live long enough }; f(bad); } diff --git a/tests/ui/regions/region-invariant-static-error-reporting.stderr b/tests/ui/regions/region-invariant-static-error-reporting.stderr index 834d5c6cf5a8b..2ccf36713ae89 100644 --- a/tests/ui/regions/region-invariant-static-error-reporting.stderr +++ b/tests/ui/regions/region-invariant-static-error-reporting.stderr @@ -1,16 +1,11 @@ -error[E0521]: borrowed data escapes outside of function - --> $DIR/region-invariant-static-error-reporting.rs:14:9 +error: lifetime may not live long enough + --> $DIR/region-invariant-static-error-reporting.rs:16:9 | LL | fn unify<'a>(x: Option>, f: fn(Invariant<'a>)) { - | -- - `x` is a reference that is only valid in the function body - | | - | lifetime `'a` defined here -LL | let bad = if x.is_some() { -LL | x.unwrap() - | ^^^^^^^^^^ - | | - | `x` escapes the function body here - | argument requires that `'a` must outlive `'static` + | -- lifetime `'a` defined here +... +LL | mk_static() + | ^^^^^^^^^^^ assignment requires that `'a` must outlive `'static` | = note: requirement occurs because of the type `Invariant<'_>`, which makes the generic argument `'_` invariant = note: the struct `Invariant<'a>` is invariant over the parameter `'a` @@ -18,4 +13,3 @@ LL | x.unwrap() error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/regions/region-object-lifetime-in-coercion.stderr b/tests/ui/regions/region-object-lifetime-in-coercion.stderr index b5bb08c73c890..3880ae8228377 100644 --- a/tests/ui/regions/region-object-lifetime-in-coercion.stderr +++ b/tests/ui/regions/region-object-lifetime-in-coercion.stderr @@ -8,12 +8,14 @@ LL | let x: Box = Box::new(v); | help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v` | -LL | fn a(v: &[u8]) -> Box { - | ~~ +LL - fn a(v: &[u8]) -> Box { +LL + fn a(v: &[u8]) -> Box { + | help: alternatively, add an explicit `'static` bound to this reference | -LL | fn a(v: &'static [u8]) -> Box { - | ~~~~~~~~~~~~~ +LL - fn a(v: &[u8]) -> Box { +LL + fn a(v: &'static [u8]) -> Box { + | error: lifetime may not live long enough --> $DIR/region-object-lifetime-in-coercion.rs:14:5 @@ -25,12 +27,14 @@ LL | Box::new(v) | help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v` | -LL | fn b(v: &[u8]) -> Box { - | ~~ +LL - fn b(v: &[u8]) -> Box { +LL + fn b(v: &[u8]) -> Box { + | help: alternatively, add an explicit `'static` bound to this reference | -LL | fn b(v: &'static [u8]) -> Box { - | ~~~~~~~~~~~~~ +LL - fn b(v: &[u8]) -> Box { +LL + fn b(v: &'static [u8]) -> Box { + | error: lifetime may not live long enough --> $DIR/region-object-lifetime-in-coercion.rs:21:5 diff --git a/tests/ui/regions/regions-close-object-into-object-2.stderr b/tests/ui/regions/regions-close-object-into-object-2.stderr index aacb5ea4e87b7..54364ef0821a6 100644 --- a/tests/ui/regions/regions-close-object-into-object-2.stderr +++ b/tests/ui/regions/regions-close-object-into-object-2.stderr @@ -8,12 +8,14 @@ LL | Box::new(B(&*v)) as Box | help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v` | -LL | fn g<'a, T: 'static>(v: Box + 'a>) -> Box { - | ~~ +LL - fn g<'a, T: 'static>(v: Box + 'a>) -> Box { +LL + fn g<'a, T: 'static>(v: Box + 'a>) -> Box { + | help: alternatively, add an explicit `'static` bound to this reference | -LL | fn g<'a, T: 'static>(v: Box<(dyn A + 'static)>) -> Box { - | ~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - fn g<'a, T: 'static>(v: Box + 'a>) -> Box { +LL + fn g<'a, T: 'static>(v: Box<(dyn A + 'static)>) -> Box { + | error[E0515]: cannot return value referencing local data `*v` --> $DIR/regions-close-object-into-object-2.rs:9:5 diff --git a/tests/ui/regions/regions-close-object-into-object-4.stderr b/tests/ui/regions/regions-close-object-into-object-4.stderr index f6a79be09477c..d119ec57e988b 100644 --- a/tests/ui/regions/regions-close-object-into-object-4.stderr +++ b/tests/ui/regions/regions-close-object-into-object-4.stderr @@ -50,12 +50,14 @@ LL | Box::new(B(&*v)) as Box | help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v` | -LL | fn i<'a, T, U>(v: Box+'a>) -> Box { - | ~~ +LL - fn i<'a, T, U>(v: Box+'a>) -> Box { +LL + fn i<'a, T, U>(v: Box+'a>) -> Box { + | help: alternatively, add an explicit `'static` bound to this reference | -LL | fn i<'a, T, U>(v: Box<(dyn A + 'static)>) -> Box { - | ~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - fn i<'a, T, U>(v: Box+'a>) -> Box { +LL + fn i<'a, T, U>(v: Box<(dyn A + 'static)>) -> Box { + | error[E0515]: cannot return value referencing local data `*v` --> $DIR/regions-close-object-into-object-4.rs:9:5 diff --git a/tests/ui/regions/regions-implied-bounds-projection-gap-hr-1.rs b/tests/ui/regions/regions-implied-bounds-projection-gap-hr-1.rs index 4d77a551f6472..1106352037a08 100644 --- a/tests/ui/regions/regions-implied-bounds-projection-gap-hr-1.rs +++ b/tests/ui/regions/regions-implied-bounds-projection-gap-hr-1.rs @@ -20,8 +20,8 @@ trait Trait2<'a, 'b> { // do not infer that. fn callee<'x, 'y, T>(t: &'x dyn for<'z> Trait1< >::Foo >) //~^ ERROR the trait bound `for<'z> T: Trait2<'y, 'z>` is not satisfied + //~| ERROR the trait bound `for<'z> T: Trait2<'y, 'z>` is not satisfied { - //~^ ERROR the trait bound `for<'z> T: Trait2<'y, 'z>` is not satisfied } fn main() { } diff --git a/tests/ui/regions/regions-implied-bounds-projection-gap-hr-1.stderr b/tests/ui/regions/regions-implied-bounds-projection-gap-hr-1.stderr index 87f0f47f2401e..643746ed46fcf 100644 --- a/tests/ui/regions/regions-implied-bounds-projection-gap-hr-1.stderr +++ b/tests/ui/regions/regions-implied-bounds-projection-gap-hr-1.stderr @@ -10,12 +10,10 @@ LL | fn callee<'x, 'y, T: for<'z> Trait2<'y, 'z>>(t: &'x dyn for<'z> Trait1< T: Trait2<'y, 'z>` is not satisfied - --> $DIR/regions-implied-bounds-projection-gap-hr-1.rs:23:1 + --> $DIR/regions-implied-bounds-projection-gap-hr-1.rs:21:25 | -LL | / { -LL | | -LL | | } - | |_^ the trait `for<'z> Trait2<'y, 'z>` is not implemented for `T` +LL | fn callee<'x, 'y, T>(t: &'x dyn for<'z> Trait1< >::Foo >) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'z> Trait2<'y, 'z>` is not implemented for `T` | help: consider restricting type parameter `T` with trait `Trait2` | diff --git a/tests/ui/regions/regions-proc-bound-capture.stderr b/tests/ui/regions/regions-proc-bound-capture.stderr index 3fe497bf2e99f..3149cd8c9a1bc 100644 --- a/tests/ui/regions/regions-proc-bound-capture.stderr +++ b/tests/ui/regions/regions-proc-bound-capture.stderr @@ -9,12 +9,14 @@ LL | Box::new(move || { *x }) | help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `x` | -LL | fn static_proc(x: &isize) -> Box (isize) + '_> { - | ~~ +LL - fn static_proc(x: &isize) -> Box (isize) + 'static> { +LL + fn static_proc(x: &isize) -> Box (isize) + '_> { + | help: alternatively, add an explicit `'static` bound to this reference | -LL | fn static_proc(x: &'static isize) -> Box (isize) + 'static> { - | ~~~~~~~~~~~~~~ +LL - fn static_proc(x: &isize) -> Box (isize) + 'static> { +LL + fn static_proc(x: &'static isize) -> Box (isize) + 'static> { + | error: aborting due to 1 previous error diff --git a/tests/ui/repeat-expr/repeat_count.stderr b/tests/ui/repeat-expr/repeat_count.stderr index 350ac287507a3..34e29e8366671 100644 --- a/tests/ui/repeat-expr/repeat_count.stderr +++ b/tests/ui/repeat-expr/repeat_count.stderr @@ -6,8 +6,9 @@ LL | let a = [0; n]; | help: consider using `const` instead of `let` | -LL | const n: /* Type */ = 1; - | ~~~~~ ++++++++++++ +LL - let n = 1; +LL + const n: /* Type */ = 1; + | error[E0308]: mismatched types --> $DIR/repeat_count.rs:7:17 @@ -15,6 +16,12 @@ error[E0308]: mismatched types LL | let b = [0; ()]; | ^^ expected `usize`, found `()` +error[E0308]: mismatched types + --> $DIR/repeat_count.rs:31:17 + | +LL | let g = [0; G { g: () }]; + | ^^^^^^^^^^^ expected `usize`, found `G` + error[E0308]: mismatched types --> $DIR/repeat_count.rs:10:17 | @@ -33,12 +40,6 @@ error[E0308]: mismatched types LL | let e = [0; "foo"]; | ^^^^^ expected `usize`, found `&str` -error[E0308]: mismatched types - --> $DIR/repeat_count.rs:31:17 - | -LL | let g = [0; G { g: () }]; - | ^^^^^^^^^^^ expected `usize`, found `G` - error[E0308]: mismatched types --> $DIR/repeat_count.rs:19:17 | @@ -63,8 +64,9 @@ LL | let f = [0; 4u8]; | help: change the type of the numeric literal from `u8` to `usize` | -LL | let f = [0; 4usize]; - | ~~~~~ +LL - let f = [0; 4u8]; +LL + let f = [0; 4usize]; + | error: aborting due to 9 previous errors diff --git a/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.rs b/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.rs new file mode 100644 index 0000000000000..c76e7a1d71665 --- /dev/null +++ b/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.rs @@ -0,0 +1,70 @@ +#[derive(Copy, Clone)] +struct Type; + +struct NewType; + +const fn get_size() -> usize { + 10 +} + +fn get_dyn_size() -> usize { + 10 +} + +fn main() { + let a = ["a", 10]; + //~^ ERROR mismatched types + //~| HELP replace the comma with a semicolon to create an array + + const size_b: usize = 20; + let b = [Type, size_b]; + //~^ ERROR mismatched types + //~| HELP replace the comma with a semicolon to create an array + + let size_c: usize = 13; + let c = [Type, size_c]; + //~^ ERROR mismatched types + + const size_d: bool = true; + let d = [Type, size_d]; + //~^ ERROR mismatched types + + let e = [String::new(), 10]; + //~^ ERROR mismatched types + //~| HELP try using a conversion method + + let f = ["f", get_size()]; + //~^ ERROR mismatched types + //~| HELP replace the comma with a semicolon to create an array + + let m = ["m", get_dyn_size()]; + //~^ ERROR mismatched types + + // is_vec, is_clone, is_usize_like + let g = vec![String::new(), 10]; + //~^ ERROR mismatched types + //~| HELP replace the comma with a semicolon to create a vector + + let dyn_size = 10; + let h = vec![Type, dyn_size]; + //~^ ERROR mismatched types + //~| HELP replace the comma with a semicolon to create a vector + + let i = vec![Type, get_dyn_size()]; + //~^ ERROR mismatched types + //~| HELP replace the comma with a semicolon to create a vector + + let k = vec!['c', 10]; + //~^ ERROR mismatched types + //~| HELP replace the comma with a semicolon to create a vector + + let j = vec![Type, 10_u8]; + //~^ ERROR mismatched types + + let l = vec![NewType, 10]; + //~^ ERROR mismatched types + + let byte_size: u8 = 10; + let h = vec![Type, byte_size]; + //~^ ERROR mismatched types +} diff --git a/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.stderr b/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.stderr new file mode 100644 index 0000000000000..ce2022374f7f4 --- /dev/null +++ b/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.stderr @@ -0,0 +1,131 @@ +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:15:19 + | +LL | let a = ["a", 10]; + | ^^ expected `&str`, found integer + | +help: replace the comma with a semicolon to create an array + | +LL - let a = ["a", 10]; +LL + let a = ["a"; 10]; + | + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:20:20 + | +LL | let b = [Type, size_b]; + | ^^^^^^ expected `Type`, found `usize` + | +help: replace the comma with a semicolon to create an array + | +LL - let b = [Type, size_b]; +LL + let b = [Type; size_b]; + | + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:25:20 + | +LL | let c = [Type, size_c]; + | ^^^^^^ expected `Type`, found `usize` + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:29:20 + | +LL | let d = [Type, size_d]; + | ^^^^^^ expected `Type`, found `bool` + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:32:29 + | +LL | let e = [String::new(), 10]; + | ^^- help: try using a conversion method: `.to_string()` + | | + | expected `String`, found integer + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:36:19 + | +LL | let f = ["f", get_size()]; + | ^^^^^^^^^^ expected `&str`, found `usize` + | +help: replace the comma with a semicolon to create an array + | +LL - let f = ["f", get_size()]; +LL + let f = ["f"; get_size()]; + | + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:40:19 + | +LL | let m = ["m", get_dyn_size()]; + | ^^^^^^^^^^^^^^ expected `&str`, found `usize` + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:44:33 + | +LL | let g = vec![String::new(), 10]; + | ^^ expected `String`, found integer + | +help: replace the comma with a semicolon to create a vector + | +LL - let g = vec![String::new(), 10]; +LL + let g = vec![String::new(); 10]; + | + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:49:24 + | +LL | let h = vec![Type, dyn_size]; + | ^^^^^^^^ expected `Type`, found integer + | +help: replace the comma with a semicolon to create a vector + | +LL - let h = vec![Type, dyn_size]; +LL + let h = vec![Type; dyn_size]; + | + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:53:24 + | +LL | let i = vec![Type, get_dyn_size()]; + | ^^^^^^^^^^^^^^ expected `Type`, found `usize` + | +help: replace the comma with a semicolon to create a vector + | +LL - let i = vec![Type, get_dyn_size()]; +LL + let i = vec![Type; get_dyn_size()]; + | + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:57:23 + | +LL | let k = vec!['c', 10]; + | ^^ expected `char`, found `u8` + | +help: replace the comma with a semicolon to create a vector + | +LL - let k = vec!['c', 10]; +LL + let k = vec!['c'; 10]; + | + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:61:24 + | +LL | let j = vec![Type, 10_u8]; + | ^^^^^ expected `Type`, found `u8` + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:64:27 + | +LL | let l = vec![NewType, 10]; + | ^^ expected `NewType`, found integer + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:68:24 + | +LL | let h = vec![Type, byte_size]; + | ^^^^^^^^^ expected `Type`, found `u8` + +error: aborting due to 14 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/repr/attr-usage-repr.rs b/tests/ui/repr/attr-usage-repr.rs index dc0d4f5be2e30..d8515a12e0557 100644 --- a/tests/ui/repr/attr-usage-repr.rs +++ b/tests/ui/repr/attr-usage-repr.rs @@ -16,18 +16,39 @@ struct SSimd([f64; 2]); struct SInt(f64, f64); #[repr(C)] -enum EExtern { A, B } +enum EExtern { + A, + B, +} #[repr(align(8))] -enum EAlign { A, B } +enum EAlign { + A, + B, +} #[repr(packed)] //~ ERROR: attribute should be applied to a struct -enum EPacked { A, B } +enum EPacked { + A, + B, +} #[repr(simd)] //~ ERROR: attribute should be applied to a struct -enum ESimd { A, B } +enum ESimd { + A, + B, +} #[repr(i8)] -enum EInt { A, B } +enum EInt { + A, + B, +} + +#[repr()] //~ attribute should be applied to a struct, enum, function, associated function, or union [E0517] +type SirThisIsAType = i32; + +#[repr()] +struct EmptyReprArgumentList(i32); fn main() {} diff --git a/tests/ui/repr/attr-usage-repr.stderr b/tests/ui/repr/attr-usage-repr.stderr index 42f65625a466f..a25e68c483f66 100644 --- a/tests/ui/repr/attr-usage-repr.stderr +++ b/tests/ui/repr/attr-usage-repr.stderr @@ -15,21 +15,35 @@ LL | struct SInt(f64, f64); | ---------------------- not an enum error[E0517]: attribute should be applied to a struct or union - --> $DIR/attr-usage-repr.rs:24:8 + --> $DIR/attr-usage-repr.rs:30:8 | -LL | #[repr(packed)] - | ^^^^^^ -LL | enum EPacked { A, B } - | --------------------- not a struct or union +LL | #[repr(packed)] + | ^^^^^^ +LL | / enum EPacked { +LL | | A, +LL | | B, +LL | | } + | |_- not a struct or union error[E0517]: attribute should be applied to a struct - --> $DIR/attr-usage-repr.rs:27:8 + --> $DIR/attr-usage-repr.rs:36:8 | -LL | #[repr(simd)] - | ^^^^ -LL | enum ESimd { A, B } - | ------------------- not a struct +LL | #[repr(simd)] + | ^^^^ +LL | / enum ESimd { +LL | | A, +LL | | B, +LL | | } + | |_- not a struct -error: aborting due to 4 previous errors +error[E0517]: attribute should be applied to a struct, enum, function, associated function, or union + --> $DIR/attr-usage-repr.rs:48:1 + | +LL | #[repr()] + | ^^^^^^^^^ +LL | type SirThisIsAType = i32; + | -------------------------- not a struct, enum, function, associated function, or union + +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0517`. diff --git a/tests/ui/repr/issue-83505-repr-simd.rs b/tests/ui/repr/issue-83505-repr-simd.rs index 280b771d01539..bebbc636728fd 100644 --- a/tests/ui/repr/issue-83505-repr-simd.rs +++ b/tests/ui/repr/issue-83505-repr-simd.rs @@ -5,6 +5,8 @@ #[repr(simd)] //~^ ERROR: attribute should be applied to a struct [E0517] //~| ERROR: unsupported representation for zero-variant enum [E0084] +//~| ERROR: SIMD types are experimental and possibly buggy [E0658] + enum Es {} static CLs: Es; //~^ ERROR: free static item without body diff --git a/tests/ui/repr/issue-83505-repr-simd.stderr b/tests/ui/repr/issue-83505-repr-simd.stderr index df99baaf52293..44e154b4bb613 100644 --- a/tests/ui/repr/issue-83505-repr-simd.stderr +++ b/tests/ui/repr/issue-83505-repr-simd.stderr @@ -1,11 +1,21 @@ error: free static item without body - --> $DIR/issue-83505-repr-simd.rs:9:1 + --> $DIR/issue-83505-repr-simd.rs:11:1 | LL | static CLs: Es; | ^^^^^^^^^^^^^^- | | | help: provide a definition for the static: `= ;` +error[E0658]: SIMD types are experimental and possibly buggy + --> $DIR/issue-83505-repr-simd.rs:5:1 + | +LL | #[repr(simd)] + | ^^^^^^^^^^^^^ + | + = note: see issue #27731 for more information + = help: add `#![feature(repr_simd)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0517]: attribute should be applied to a struct --> $DIR/issue-83505-repr-simd.rs:5:8 | @@ -24,7 +34,7 @@ LL | #[repr(simd)] LL | enum Es {} | ------- zero-variant enum -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0084, E0517. +Some errors have detailed explanations: E0084, E0517, E0658. For more information about an error, try `rustc --explain E0084`. diff --git a/tests/ui/repr/repr-c-dead-variants.aarch64-unknown-linux-gnu.stderr b/tests/ui/repr/repr-c-dead-variants.aarch64-unknown-linux-gnu.stderr index 64a0cb7f31a14..8e8f1d159b740 100644 --- a/tests/ui/repr/repr-c-dead-variants.aarch64-unknown-linux-gnu.stderr +++ b/tests/ui/repr/repr-c-dead-variants.aarch64-unknown-linux-gnu.stderr @@ -55,13 +55,15 @@ error: layout_of(Univariant) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-dead-variants.rs:38:1 + --> $DIR/repr-c-dead-variants.rs:39:1 | LL | enum Univariant { | ^^^^^^^^^^^^^^^ @@ -137,6 +139,7 @@ error: layout_of(TwoVariants) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, Layout { size: Size(8 bytes), @@ -173,13 +176,15 @@ error: layout_of(TwoVariants) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-dead-variants.rs:45:1 + --> $DIR/repr-c-dead-variants.rs:46:1 | LL | enum TwoVariants { | ^^^^^^^^^^^^^^^^ @@ -247,6 +252,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, }, Layout { size: Size(16 bytes), @@ -271,6 +277,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, }, ], }, @@ -278,8 +285,9 @@ error: layout_of(DeadBranchHasOtherField) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-dead-variants.rs:57:1 + --> $DIR/repr-c-dead-variants.rs:58:1 | LL | enum DeadBranchHasOtherField { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/repr/repr-c-dead-variants.armebv7r-none-eabi.stderr b/tests/ui/repr/repr-c-dead-variants.armebv7r-none-eabi.stderr index 5c4daa6d51977..2cd0960ce3eec 100644 --- a/tests/ui/repr/repr-c-dead-variants.armebv7r-none-eabi.stderr +++ b/tests/ui/repr/repr-c-dead-variants.armebv7r-none-eabi.stderr @@ -55,13 +55,15 @@ error: layout_of(Univariant) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-dead-variants.rs:38:1 + --> $DIR/repr-c-dead-variants.rs:39:1 | LL | enum Univariant { | ^^^^^^^^^^^^^^^ @@ -137,6 +139,7 @@ error: layout_of(TwoVariants) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(2 bytes), @@ -173,13 +176,15 @@ error: layout_of(TwoVariants) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-dead-variants.rs:45:1 + --> $DIR/repr-c-dead-variants.rs:46:1 | LL | enum TwoVariants { | ^^^^^^^^^^^^^^^^ @@ -247,6 +252,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, }, Layout { size: Size(16 bytes), @@ -271,6 +277,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, }, ], }, @@ -278,8 +285,9 @@ error: layout_of(DeadBranchHasOtherField) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-dead-variants.rs:57:1 + --> $DIR/repr-c-dead-variants.rs:58:1 | LL | enum DeadBranchHasOtherField { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/repr/repr-c-dead-variants.i686-pc-windows-msvc.stderr b/tests/ui/repr/repr-c-dead-variants.i686-pc-windows-msvc.stderr index 64a0cb7f31a14..8e8f1d159b740 100644 --- a/tests/ui/repr/repr-c-dead-variants.i686-pc-windows-msvc.stderr +++ b/tests/ui/repr/repr-c-dead-variants.i686-pc-windows-msvc.stderr @@ -55,13 +55,15 @@ error: layout_of(Univariant) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-dead-variants.rs:38:1 + --> $DIR/repr-c-dead-variants.rs:39:1 | LL | enum Univariant { | ^^^^^^^^^^^^^^^ @@ -137,6 +139,7 @@ error: layout_of(TwoVariants) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, Layout { size: Size(8 bytes), @@ -173,13 +176,15 @@ error: layout_of(TwoVariants) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-dead-variants.rs:45:1 + --> $DIR/repr-c-dead-variants.rs:46:1 | LL | enum TwoVariants { | ^^^^^^^^^^^^^^^^ @@ -247,6 +252,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, }, Layout { size: Size(16 bytes), @@ -271,6 +277,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, }, ], }, @@ -278,8 +285,9 @@ error: layout_of(DeadBranchHasOtherField) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-dead-variants.rs:57:1 + --> $DIR/repr-c-dead-variants.rs:58:1 | LL | enum DeadBranchHasOtherField { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/repr/repr-c-dead-variants.rs b/tests/ui/repr/repr-c-dead-variants.rs index 3e8ae3d096d86..99f20982a9960 100644 --- a/tests/ui/repr/repr-c-dead-variants.rs +++ b/tests/ui/repr/repr-c-dead-variants.rs @@ -7,6 +7,7 @@ // See also: repr-c-int-dead-variants.rs //@ normalize-stderr: "pref: Align\([1-8] bytes\)" -> "pref: $$SOME_ALIGN" +//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" // This test depends on the value of the `c_enum_min_bits` target option. // As there's no way to actually check it from UI test, we only run this test on a subset of archs. diff --git a/tests/ui/repr/repr-c-dead-variants.x86_64-unknown-linux-gnu.stderr b/tests/ui/repr/repr-c-dead-variants.x86_64-unknown-linux-gnu.stderr index 64a0cb7f31a14..8e8f1d159b740 100644 --- a/tests/ui/repr/repr-c-dead-variants.x86_64-unknown-linux-gnu.stderr +++ b/tests/ui/repr/repr-c-dead-variants.x86_64-unknown-linux-gnu.stderr @@ -55,13 +55,15 @@ error: layout_of(Univariant) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-dead-variants.rs:38:1 + --> $DIR/repr-c-dead-variants.rs:39:1 | LL | enum Univariant { | ^^^^^^^^^^^^^^^ @@ -137,6 +139,7 @@ error: layout_of(TwoVariants) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, Layout { size: Size(8 bytes), @@ -173,13 +176,15 @@ error: layout_of(TwoVariants) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-dead-variants.rs:45:1 + --> $DIR/repr-c-dead-variants.rs:46:1 | LL | enum TwoVariants { | ^^^^^^^^^^^^^^^^ @@ -247,6 +252,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, }, Layout { size: Size(16 bytes), @@ -271,6 +277,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, }, ], }, @@ -278,8 +285,9 @@ error: layout_of(DeadBranchHasOtherField) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-dead-variants.rs:57:1 + --> $DIR/repr-c-dead-variants.rs:58:1 | LL | enum DeadBranchHasOtherField { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/repr/repr-c-int-dead-variants.rs b/tests/ui/repr/repr-c-int-dead-variants.rs index 627569e080d4c..723e573024402 100644 --- a/tests/ui/repr/repr-c-int-dead-variants.rs +++ b/tests/ui/repr/repr-c-int-dead-variants.rs @@ -4,6 +4,7 @@ // See also: repr-c-dead-variants.rs //@ normalize-stderr: "pref: Align\([1-8] bytes\)" -> "pref: $$SOME_ALIGN" +//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" // A simple uninhabited type. enum Void {} diff --git a/tests/ui/repr/repr-c-int-dead-variants.stderr b/tests/ui/repr/repr-c-int-dead-variants.stderr index 75005a64523a3..fa08b323dec2a 100644 --- a/tests/ui/repr/repr-c-int-dead-variants.stderr +++ b/tests/ui/repr/repr-c-int-dead-variants.stderr @@ -55,13 +55,15 @@ error: layout_of(UnivariantU8) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-int-dead-variants.rs:14:1 + --> $DIR/repr-c-int-dead-variants.rs:15:1 | LL | enum UnivariantU8 { | ^^^^^^^^^^^^^^^^^ @@ -137,6 +139,7 @@ error: layout_of(TwoVariantsU8) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(2 bytes), @@ -173,13 +176,15 @@ error: layout_of(TwoVariantsU8) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-int-dead-variants.rs:21:1 + --> $DIR/repr-c-int-dead-variants.rs:22:1 | LL | enum TwoVariantsU8 { | ^^^^^^^^^^^^^^^^^^ @@ -247,6 +252,7 @@ error: layout_of(DeadBranchHasOtherFieldU8) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, }, Layout { size: Size(16 bytes), @@ -271,6 +277,7 @@ error: layout_of(DeadBranchHasOtherFieldU8) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, }, ], }, @@ -278,8 +285,9 @@ error: layout_of(DeadBranchHasOtherFieldU8) = Layout { Align(8 bytes), ), unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, } - --> $DIR/repr-c-int-dead-variants.rs:33:1 + --> $DIR/repr-c-int-dead-variants.rs:34:1 | LL | enum DeadBranchHasOtherFieldU8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/resolve/112590-2.stderr b/tests/ui/resolve/112590-2.stderr index 0db20249d27f5..b39b44396d730 100644 --- a/tests/ui/resolve/112590-2.stderr +++ b/tests/ui/resolve/112590-2.stderr @@ -14,12 +14,13 @@ LL - let _: Vec = super::foo::baf::baz::MyVec::new(); LL + let _: Vec = MyVec::new(); | -error[E0433]: failed to resolve: use of undeclared crate or module `fox` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `fox` --> $DIR/112590-2.rs:18:27 | LL | let _: Vec = fox::bar::baz::MyVec::new(); - | ^^^ use of undeclared crate or module `fox` + | ^^^ use of unresolved module or unlinked crate `fox` | + = help: you might be missing a crate named `fox` help: consider importing this struct through its public re-export | LL + use foo::bar::baz::MyVec; @@ -30,12 +31,13 @@ LL - let _: Vec = fox::bar::baz::MyVec::new(); LL + let _: Vec = MyVec::new(); | -error[E0433]: failed to resolve: use of undeclared crate or module `vec` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `vec` --> $DIR/112590-2.rs:24:15 | LL | type _B = vec::Vec::; - | ^^^ use of undeclared crate or module `vec` + | ^^^ use of unresolved module or unlinked crate `vec` | + = help: you might be missing a crate named `vec` help: consider importing this module | LL + use std::vec; @@ -57,14 +59,16 @@ LL - let _t = std::sync_error::atomic::AtomicBool::new(true); LL + let _t = AtomicBool::new(true); | -error[E0433]: failed to resolve: use of undeclared crate or module `vec` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `vec` --> $DIR/112590-2.rs:23:24 | LL | let _t: Vec = vec::new(); | ^^^ | | - | use of undeclared crate or module `vec` + | use of unresolved module or unlinked crate `vec` | help: a struct with a similar name exists (notice the capitalization): `Vec` + | + = help: you might be missing a crate named `vec` error: aborting due to 5 previous errors diff --git a/tests/ui/resolve/auxiliary/fake_matches.rs b/tests/ui/resolve/auxiliary/fake_matches.rs new file mode 100644 index 0000000000000..6d42972cbace0 --- /dev/null +++ b/tests/ui/resolve/auxiliary/fake_matches.rs @@ -0,0 +1,13 @@ +// Helper for test tests/ui/resolve/const-with-typo-in-pattern-binding-ice-135289.rs + +//@ edition: 2018 + +#[macro_export] +macro_rules! assert_matches { + ( $e:expr , $($pat:pat)|+ ) => { + match $e { + $($pat)|+ => (), + _ => (), + } + }; +} diff --git a/tests/ui/resolve/bad-module.rs b/tests/ui/resolve/bad-module.rs index b23e97c2cf6bc..9fe06ab0f52ed 100644 --- a/tests/ui/resolve/bad-module.rs +++ b/tests/ui/resolve/bad-module.rs @@ -1,7 +1,7 @@ fn main() { let foo = thing::len(Vec::new()); - //~^ ERROR failed to resolve: use of undeclared crate or module `thing` + //~^ ERROR failed to resolve: use of unresolved module or unlinked crate `thing` let foo = foo::bar::baz(); - //~^ ERROR failed to resolve: use of undeclared crate or module `foo` + //~^ ERROR failed to resolve: use of unresolved module or unlinked crate `foo` } diff --git a/tests/ui/resolve/bad-module.stderr b/tests/ui/resolve/bad-module.stderr index 558760c6793ab..0f597e126fdc5 100644 --- a/tests/ui/resolve/bad-module.stderr +++ b/tests/ui/resolve/bad-module.stderr @@ -1,14 +1,18 @@ -error[E0433]: failed to resolve: use of undeclared crate or module `foo` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `foo` --> $DIR/bad-module.rs:5:15 | LL | let foo = foo::bar::baz(); - | ^^^ use of undeclared crate or module `foo` + | ^^^ use of unresolved module or unlinked crate `foo` + | + = help: you might be missing a crate named `foo` -error[E0433]: failed to resolve: use of undeclared crate or module `thing` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `thing` --> $DIR/bad-module.rs:2:15 | LL | let foo = thing::len(Vec::new()); - | ^^^^^ use of undeclared crate or module `thing` + | ^^^^^ use of unresolved module or unlinked crate `thing` + | + = help: you might be missing a crate named `thing` error: aborting due to 2 previous errors diff --git a/tests/ui/resolve/const-with-typo-in-pattern-binding-ice-135289.rs b/tests/ui/resolve/const-with-typo-in-pattern-binding-ice-135289.rs new file mode 100644 index 0000000000000..8267a9250ec93 --- /dev/null +++ b/tests/ui/resolve/const-with-typo-in-pattern-binding-ice-135289.rs @@ -0,0 +1,17 @@ +// This is a non-regression test for issue 135289, where the "const with typo in pattern" diagnostic +// caused an ICE when unexpectedly pretty printing a type for unreachable arms via a macro defined +// in a dependency. + +#![warn(unreachable_patterns)] // needed to reproduce the ICE described in #135289 + +//@ check-pass +//@ aux-build: fake_matches.rs +extern crate fake_matches; + +const _A: u64 = 0; +pub fn f() -> u64 { + 0 +} +fn main() { + fake_matches::assert_matches!(f(), _non_existent); +} diff --git a/tests/ui/resolve/const-with-typo-in-pattern-binding.stderr b/tests/ui/resolve/const-with-typo-in-pattern-binding.stderr index a0cdac3fa2538..ef641eb5681bf 100644 --- a/tests/ui/resolve/const-with-typo-in-pattern-binding.stderr +++ b/tests/ui/resolve/const-with-typo-in-pattern-binding.stderr @@ -28,8 +28,9 @@ LL | _ => {} | help: you might have meant to pattern match against the value of similarly named constant `god` instead of introducing a new catch-all binding | -LL | god => {} - | ~~~ +LL - GOD => {} +LL + god => {} + | error: unreachable pattern --> $DIR/const-with-typo-in-pattern-binding.rs:30:9 @@ -42,8 +43,9 @@ LL | _ => {} | help: you might have meant to pattern match against the value of similarly named constant `GOOD` instead of introducing a new catch-all binding | -LL | GOOD => {} - | ~~~~ +LL - GOOOD => {} +LL + GOOD => {} + | error: unreachable pattern --> $DIR/const-with-typo-in-pattern-binding.rs:36:9 @@ -72,7 +74,7 @@ LL | _ => {} help: you might have meant to pattern match against the value of constant `ARCH` instead of introducing a new catch-all binding | LL | std::env::consts::ARCH => {} - | ~~~~~~~~~~~~~~~~~~~~~~ + | ++++++++++++++++++ error: aborting due to 5 previous errors diff --git a/tests/ui/resolve/editions-crate-root-2015.rs b/tests/ui/resolve/editions-crate-root-2015.rs index 869f4c82c8b71..a2e19bfdf1c57 100644 --- a/tests/ui/resolve/editions-crate-root-2015.rs +++ b/tests/ui/resolve/editions-crate-root-2015.rs @@ -2,10 +2,10 @@ mod inner { fn global_inner(_: ::nonexistant::Foo) { - //~^ ERROR failed to resolve: you might be missing crate `nonexistant` + //~^ ERROR failed to resolve: use of unresolved module or unlinked crate `nonexistant` } fn crate_inner(_: crate::nonexistant::Foo) { - //~^ ERROR failed to resolve: you might be missing crate `nonexistant` + //~^ ERROR failed to resolve: use of unresolved module or unlinked crate `nonexistant` } fn bare_global(_: ::nonexistant) { diff --git a/tests/ui/resolve/editions-crate-root-2015.stderr b/tests/ui/resolve/editions-crate-root-2015.stderr index 7a842aca0fd27..3d203c8ed9676 100644 --- a/tests/ui/resolve/editions-crate-root-2015.stderr +++ b/tests/ui/resolve/editions-crate-root-2015.stderr @@ -1,21 +1,21 @@ -error[E0433]: failed to resolve: you might be missing crate `nonexistant` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `nonexistant` --> $DIR/editions-crate-root-2015.rs:4:26 | LL | fn global_inner(_: ::nonexistant::Foo) { - | ^^^^^^^^^^^ you might be missing crate `nonexistant` + | ^^^^^^^^^^^ use of unresolved module or unlinked crate `nonexistant` | -help: consider importing the `nonexistant` crate +help: you might be missing a crate named `nonexistant`, add it to your project and import it in your code | LL + extern crate nonexistant; | -error[E0433]: failed to resolve: you might be missing crate `nonexistant` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `nonexistant` --> $DIR/editions-crate-root-2015.rs:7:30 | LL | fn crate_inner(_: crate::nonexistant::Foo) { - | ^^^^^^^^^^^ you might be missing crate `nonexistant` + | ^^^^^^^^^^^ use of unresolved module or unlinked crate `nonexistant` | -help: consider importing the `nonexistant` crate +help: you might be missing a crate named `nonexistant`, add it to your project and import it in your code | LL + extern crate nonexistant; | diff --git a/tests/ui/resolve/export-fully-qualified-2018.rs b/tests/ui/resolve/export-fully-qualified-2018.rs index 26e3044d8df0e..ce78b64bf256d 100644 --- a/tests/ui/resolve/export-fully-qualified-2018.rs +++ b/tests/ui/resolve/export-fully-qualified-2018.rs @@ -5,7 +5,7 @@ // want to change eventually. mod foo { - pub fn bar() { foo::baz(); } //~ ERROR failed to resolve: use of undeclared crate or module `foo` + pub fn bar() { foo::baz(); } //~ ERROR failed to resolve: use of unresolved module or unlinked crate `foo` fn baz() { } } diff --git a/tests/ui/resolve/export-fully-qualified-2018.stderr b/tests/ui/resolve/export-fully-qualified-2018.stderr index 378d9832a657a..a985669b8b415 100644 --- a/tests/ui/resolve/export-fully-qualified-2018.stderr +++ b/tests/ui/resolve/export-fully-qualified-2018.stderr @@ -1,8 +1,10 @@ -error[E0433]: failed to resolve: use of undeclared crate or module `foo` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `foo` --> $DIR/export-fully-qualified-2018.rs:8:20 | LL | pub fn bar() { foo::baz(); } - | ^^^ use of undeclared crate or module `foo` + | ^^^ use of unresolved module or unlinked crate `foo` + | + = help: you might be missing a crate named `foo` error: aborting due to 1 previous error diff --git a/tests/ui/resolve/export-fully-qualified.rs b/tests/ui/resolve/export-fully-qualified.rs index 6de33b7e1915f..0be3b81ebb8ff 100644 --- a/tests/ui/resolve/export-fully-qualified.rs +++ b/tests/ui/resolve/export-fully-qualified.rs @@ -5,7 +5,7 @@ // want to change eventually. mod foo { - pub fn bar() { foo::baz(); } //~ ERROR failed to resolve: use of undeclared crate or module `foo` + pub fn bar() { foo::baz(); } //~ ERROR failed to resolve: use of unresolved module or unlinked crate `foo` fn baz() { } } diff --git a/tests/ui/resolve/export-fully-qualified.stderr b/tests/ui/resolve/export-fully-qualified.stderr index 869149d8d3c65..e65483e57eb57 100644 --- a/tests/ui/resolve/export-fully-qualified.stderr +++ b/tests/ui/resolve/export-fully-qualified.stderr @@ -1,8 +1,10 @@ -error[E0433]: failed to resolve: use of undeclared crate or module `foo` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `foo` --> $DIR/export-fully-qualified.rs:8:20 | LL | pub fn bar() { foo::baz(); } - | ^^^ use of undeclared crate or module `foo` + | ^^^ use of unresolved module or unlinked crate `foo` + | + = help: you might be missing a crate named `foo` error: aborting due to 1 previous error diff --git a/tests/ui/resolve/extern-prelude-fail.stderr b/tests/ui/resolve/extern-prelude-fail.stderr index 77c10f5f995bc..199a31244c067 100644 --- a/tests/ui/resolve/extern-prelude-fail.stderr +++ b/tests/ui/resolve/extern-prelude-fail.stderr @@ -2,20 +2,20 @@ error[E0432]: unresolved import `extern_prelude` --> $DIR/extern-prelude-fail.rs:7:9 | LL | use extern_prelude::S; - | ^^^^^^^^^^^^^^ you might be missing crate `extern_prelude` + | ^^^^^^^^^^^^^^ use of unresolved module or unlinked crate `extern_prelude` | -help: consider importing the `extern_prelude` crate +help: you might be missing a crate named `extern_prelude`, add it to your project and import it in your code | LL + extern crate extern_prelude; | -error[E0433]: failed to resolve: you might be missing crate `extern_prelude` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `extern_prelude` --> $DIR/extern-prelude-fail.rs:8:15 | LL | let s = ::extern_prelude::S; - | ^^^^^^^^^^^^^^ you might be missing crate `extern_prelude` + | ^^^^^^^^^^^^^^ use of unresolved module or unlinked crate `extern_prelude` | -help: consider importing the `extern_prelude` crate +help: you might be missing a crate named `extern_prelude`, add it to your project and import it in your code | LL + extern crate extern_prelude; | diff --git a/tests/ui/resolve/issue-100365.stderr b/tests/ui/resolve/issue-100365.stderr index 2d9bab4304d46..7a880c6f31acd 100644 --- a/tests/ui/resolve/issue-100365.stderr +++ b/tests/ui/resolve/issue-100365.stderr @@ -6,8 +6,9 @@ LL | let addr = Into::.into([127, 0, 0, 1]); | help: use the path separator to refer to an item | -LL | let addr = Into::::into([127, 0, 0, 1]); - | ~~ +LL - let addr = Into::.into([127, 0, 0, 1]); +LL + let addr = Into::::into([127, 0, 0, 1]); + | error[E0423]: expected value, found trait `Into` --> $DIR/issue-100365.rs:6:13 @@ -17,8 +18,9 @@ LL | let _ = Into.into(()); | help: use the path separator to refer to an item | -LL | let _ = Into::into(()); - | ~~ +LL - let _ = Into.into(()); +LL + let _ = Into::into(()); + | error[E0423]: expected value, found trait `Into` --> $DIR/issue-100365.rs:10:13 @@ -28,8 +30,9 @@ LL | let _ = Into::<()>.into; | help: use the path separator to refer to an item | -LL | let _ = Into::<()>::into; - | ~~ +LL - let _ = Into::<()>.into; +LL + let _ = Into::<()>::into; + | error[E0423]: expected value, found trait `std::iter::Iterator` --> $DIR/issue-100365.rs:17:9 @@ -65,8 +68,9 @@ LL | let _ = create!(); = note: this error originates in the macro `create` (in Nightly builds, run with -Z macro-backtrace for more info) help: use the path separator to refer to an item | -LL | Into::::into("") - | ~~ +LL - Into::.into("") +LL + Into::::into("") + | error: aborting due to 6 previous errors diff --git a/tests/ui/resolve/issue-101749-2.rs b/tests/ui/resolve/issue-101749-2.rs index 4d3d469447c2b..636ff07c71ceb 100644 --- a/tests/ui/resolve/issue-101749-2.rs +++ b/tests/ui/resolve/issue-101749-2.rs @@ -12,5 +12,5 @@ fn main() { let rect = Rectangle::new(3, 4); // `area` is not implemented for `Rectangle`, so this should not suggest let _ = rect::area(); - //~^ ERROR failed to resolve: use of undeclared crate or module `rect` + //~^ ERROR failed to resolve: use of unresolved module or unlinked crate `rect` } diff --git a/tests/ui/resolve/issue-101749-2.stderr b/tests/ui/resolve/issue-101749-2.stderr index 300aaf26cb7d8..96a20b4bf5a05 100644 --- a/tests/ui/resolve/issue-101749-2.stderr +++ b/tests/ui/resolve/issue-101749-2.stderr @@ -1,8 +1,10 @@ -error[E0433]: failed to resolve: use of undeclared crate or module `rect` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `rect` --> $DIR/issue-101749-2.rs:14:13 | LL | let _ = rect::area(); - | ^^^^ use of undeclared crate or module `rect` + | ^^^^ use of unresolved module or unlinked crate `rect` + | + = help: you might be missing a crate named `rect` error: aborting due to 1 previous error diff --git a/tests/ui/resolve/issue-101749.fixed b/tests/ui/resolve/issue-101749.fixed index 97815793d298e..3244ad7a03139 100644 --- a/tests/ui/resolve/issue-101749.fixed +++ b/tests/ui/resolve/issue-101749.fixed @@ -15,5 +15,5 @@ impl Rectangle { fn main() { let rect = Rectangle::new(3, 4); let _ = rect.area(); - //~^ ERROR failed to resolve: use of undeclared crate or module `rect` + //~^ ERROR failed to resolve: use of unresolved module or unlinked crate `rect` } diff --git a/tests/ui/resolve/issue-101749.rs b/tests/ui/resolve/issue-101749.rs index 994fc86778e08..c977df41d2f56 100644 --- a/tests/ui/resolve/issue-101749.rs +++ b/tests/ui/resolve/issue-101749.rs @@ -15,5 +15,5 @@ impl Rectangle { fn main() { let rect = Rectangle::new(3, 4); let _ = rect::area(); - //~^ ERROR failed to resolve: use of undeclared crate or module `rect` + //~^ ERROR failed to resolve: use of unresolved module or unlinked crate `rect` } diff --git a/tests/ui/resolve/issue-101749.stderr b/tests/ui/resolve/issue-101749.stderr index 05515b1b46052..09e800ec7c310 100644 --- a/tests/ui/resolve/issue-101749.stderr +++ b/tests/ui/resolve/issue-101749.stderr @@ -1,13 +1,15 @@ -error[E0433]: failed to resolve: use of undeclared crate or module `rect` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `rect` --> $DIR/issue-101749.rs:17:13 | LL | let _ = rect::area(); - | ^^^^ use of undeclared crate or module `rect` + | ^^^^ use of unresolved module or unlinked crate `rect` | + = help: you might be missing a crate named `rect` help: you may have meant to call an instance method | -LL | let _ = rect.area(); - | ~ +LL - let _ = rect::area(); +LL + let _ = rect.area(); + | error: aborting due to 1 previous error diff --git a/tests/ui/resolve/issue-103202.stderr b/tests/ui/resolve/issue-103202.stderr index 87fa940ac3b25..cf32efab981f7 100644 --- a/tests/ui/resolve/issue-103202.stderr +++ b/tests/ui/resolve/issue-103202.stderr @@ -6,8 +6,9 @@ LL | fn f(self: &S::x) {} | help: if there were a trait named `Example` with associated type `x` implemented for `S`, you could use the fully-qualified path | -LL | fn f(self: &::x) {} - | ~~~~~~~~~~~~~~~~~ +LL - fn f(self: &S::x) {} +LL + fn f(self: &::x) {} + | error: aborting due to 1 previous error diff --git a/tests/ui/resolve/issue-103474.stderr b/tests/ui/resolve/issue-103474.stderr index e48fb31eccc9a..717892921f142 100644 --- a/tests/ui/resolve/issue-103474.stderr +++ b/tests/ui/resolve/issue-103474.stderr @@ -6,8 +6,9 @@ LL | this.i | help: you might have meant to use `self` here instead | -LL | self.i - | ~~~~ +LL - this.i +LL + self.i + | help: if you meant to use `self`, you are also missing a `self` receiver argument | LL | fn needs_self(&self) { diff --git a/tests/ui/resolve/issue-112472-multi-generics-suggestion.stderr b/tests/ui/resolve/issue-112472-multi-generics-suggestion.stderr index f463e2dad2cfc..1ed6e68c33352 100644 --- a/tests/ui/resolve/issue-112472-multi-generics-suggestion.stderr +++ b/tests/ui/resolve/issue-112472-multi-generics-suggestion.stderr @@ -6,8 +6,9 @@ LL | >::Error: ParseError, | help: constrain the associated type to `ParseError` | -LL | DecodeLine: convert::TryFrom, - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - >::Error: ParseError, +LL + DecodeLine: convert::TryFrom, + | error[E0404]: expected trait, found enum `ParseError` --> $DIR/issue-112472-multi-generics-suggestion.rs:25:45 @@ -17,8 +18,9 @@ LL | >::Error: ParseError, | help: constrain the associated type to `ParseError` | -LL | DecodeLine: TryFrom, - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - >::Error: ParseError, +LL + DecodeLine: TryFrom, + | error: aborting due to 2 previous errors diff --git a/tests/ui/resolve/issue-18252.stderr b/tests/ui/resolve/issue-18252.stderr index 6cb9c1f1dd211..b22ad3f5225bd 100644 --- a/tests/ui/resolve/issue-18252.stderr +++ b/tests/ui/resolve/issue-18252.stderr @@ -6,8 +6,9 @@ LL | let f = Foo::Variant(42); | help: you might have meant to create a new value of the struct | -LL | let f = Foo::Variant { x: /* value */ }; - | ~~~~~~~~~~~~~~~~~~ +LL - let f = Foo::Variant(42); +LL + let f = Foo::Variant { x: /* value */ }; + | error: aborting due to 1 previous error diff --git a/tests/ui/resolve/issue-22692.stderr b/tests/ui/resolve/issue-22692.stderr index be0634ebffc96..546f12b35c156 100644 --- a/tests/ui/resolve/issue-22692.stderr +++ b/tests/ui/resolve/issue-22692.stderr @@ -6,8 +6,9 @@ LL | let _ = String.new(); | help: use the path separator to refer to an item | -LL | let _ = String::new(); - | ~~ +LL - let _ = String.new(); +LL + let _ = String::new(); + | error[E0423]: expected value, found struct `String` --> $DIR/issue-22692.rs:6:13 @@ -17,8 +18,9 @@ LL | let _ = String.default; | help: use the path separator to refer to an item | -LL | let _ = String::default; - | ~~ +LL - let _ = String.default; +LL + let _ = String::default; + | error[E0423]: expected value, found struct `Vec` --> $DIR/issue-22692.rs:10:13 @@ -28,8 +30,9 @@ LL | let _ = Vec::<()>.with_capacity(1); | help: use the path separator to refer to an item | -LL | let _ = Vec::<()>::with_capacity(1); - | ~~ +LL - let _ = Vec::<()>.with_capacity(1); +LL + let _ = Vec::<()>::with_capacity(1); + | error[E0423]: expected value, found struct `std::cell::Cell` --> $DIR/issue-22692.rs:17:9 @@ -43,8 +46,9 @@ LL | Type!().get(); = note: this error originates in the macro `Type` (in Nightly builds, run with -Z macro-backtrace for more info) help: use the path separator to refer to an item | -LL | ::get(); - | ~~~~~~~~~~~ +LL - Type!().get(); +LL + ::get(); + | error[E0423]: expected value, found struct `std::cell::Cell` --> $DIR/issue-22692.rs:17:9 @@ -58,8 +62,9 @@ LL | Type! {}.get; = note: this error originates in the macro `Type` (in Nightly builds, run with -Z macro-backtrace for more info) help: use the path separator to refer to an item | -LL | ::get; - | ~~~~~~~~~~~~ +LL - Type! {}.get; +LL + ::get; + | error[E0423]: expected value, found struct `Vec` --> $DIR/issue-22692.rs:26:9 @@ -73,8 +78,9 @@ LL | let _ = create!(type method); = note: this error originates in the macro `create` (in Nightly builds, run with -Z macro-backtrace for more info) help: use the path separator to refer to an item | -LL | Vec::new() - | ~~ +LL - Vec.new() +LL + Vec::new() + | error[E0423]: expected value, found struct `Vec` --> $DIR/issue-22692.rs:31:9 @@ -88,8 +94,9 @@ LL | let _ = create!(type field); = note: this error originates in the macro `create` (in Nightly builds, run with -Z macro-backtrace for more info) help: use the path separator to refer to an item | -LL | Vec::new - | ~~ +LL - Vec.new +LL + Vec::new + | error[E0423]: expected value, found struct `std::cell::Cell` --> $DIR/issue-22692.rs:17:9 @@ -103,8 +110,9 @@ LL | let _ = create!(macro method); = note: this error originates in the macro `Type` which comes from the expansion of the macro `create` (in Nightly builds, run with -Z macro-backtrace for more info) help: use the path separator to refer to an item | -LL | ::new(0) - | ~~~~~~~~~~~ +LL - Type!().new(0) +LL + ::new(0) + | error: aborting due to 8 previous errors diff --git a/tests/ui/resolve/issue-35675.stderr b/tests/ui/resolve/issue-35675.stderr index 44af65b0768a8..83d92ea7bf1ac 100644 --- a/tests/ui/resolve/issue-35675.stderr +++ b/tests/ui/resolve/issue-35675.stderr @@ -6,8 +6,9 @@ LL | fn should_return_fruit() -> Apple { | help: there is an enum variant `Fruit::Apple`; try using the variant's enum | -LL | fn should_return_fruit() -> Fruit { - | ~~~~~ +LL - fn should_return_fruit() -> Apple { +LL + fn should_return_fruit() -> Fruit { + | error[E0425]: cannot find function, tuple struct or tuple variant `Apple` in this scope --> $DIR/issue-35675.rs:9:5 @@ -57,8 +58,9 @@ LL | fn bar() -> Variant3 { | help: there is an enum variant `x::Enum::Variant3`; try using the variant's enum | -LL | fn bar() -> x::Enum { - | ~~~~~~~ +LL - fn bar() -> Variant3 { +LL + fn bar() -> x::Enum { + | error[E0573]: expected type, found variant `Some` --> $DIR/issue-35675.rs:28:13 diff --git a/tests/ui/resolve/issue-3907-2.stderr b/tests/ui/resolve/issue-3907-2.stderr index 7c47c5973e313..40cdfb7a30255 100644 --- a/tests/ui/resolve/issue-3907-2.stderr +++ b/tests/ui/resolve/issue-3907-2.stderr @@ -1,14 +1,15 @@ -error[E0038]: the trait `issue_3907::Foo` cannot be made into an object +error[E0038]: the trait `issue_3907::Foo` is not dyn compatible --> $DIR/issue-3907-2.rs:11:12 | LL | fn bar(_x: Foo) {} - | ^^^ `issue_3907::Foo` cannot be made into an object + | ^^^ `issue_3907::Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/auxiliary/issue-3907.rs:2:8 | LL | fn bar(); - | ^^^ the trait cannot be made into an object because associated function `bar` has no `self` parameter + | ^^^ the trait is not dyn compatible because associated function `bar` has no `self` parameter error[E0277]: the size for values of type `(dyn issue_3907::Foo + 'static)` cannot be known at compilation time --> $DIR/issue-3907-2.rs:11:12 diff --git a/tests/ui/resolve/issue-3907.stderr b/tests/ui/resolve/issue-3907.stderr index e9dc344496e51..0dc85829160bf 100644 --- a/tests/ui/resolve/issue-3907.stderr +++ b/tests/ui/resolve/issue-3907.stderr @@ -6,7 +6,8 @@ LL | impl Foo for S { | help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias | -LL | trait Foo = dyn issue_3907::Foo; +LL - type Foo = dyn issue_3907::Foo; +LL + trait Foo = dyn issue_3907::Foo; | help: consider importing this trait instead | diff --git a/tests/ui/resolve/issue-39226.stderr b/tests/ui/resolve/issue-39226.stderr index 857f6a735178e..1cd2a5fb2216e 100644 --- a/tests/ui/resolve/issue-39226.stderr +++ b/tests/ui/resolve/issue-39226.stderr @@ -10,11 +10,12 @@ LL | handle: Handle help: use struct literal syntax instead | LL | handle: Handle {} - | ~~~~~~~~~ + | ++ help: a local variable with a similar name exists | -LL | handle: handle - | ~~~~~~ +LL - handle: Handle +LL + handle: handle + | error: aborting due to 1 previous error diff --git a/tests/ui/resolve/issue-42944.stderr b/tests/ui/resolve/issue-42944.stderr index 4ffa9402c667e..53c155d8f1302 100644 --- a/tests/ui/resolve/issue-42944.stderr +++ b/tests/ui/resolve/issue-42944.stderr @@ -23,8 +23,9 @@ LL | pub struct Bx(pub(in crate::foo) ()); | ^^^^^^^^^^^^^^^^^^^^^ private field help: consider making the field publicly accessible | -LL | pub struct Bx(pub ()); - | ~~~ +LL - pub struct Bx(pub(in crate::foo) ()); +LL + pub struct Bx(pub ()); + | error: aborting due to 2 previous errors diff --git a/tests/ui/resolve/issue-5035.stderr b/tests/ui/resolve/issue-5035.stderr index 32b972b21ffad..b249aaa4b2803 100644 --- a/tests/ui/resolve/issue-5035.stderr +++ b/tests/ui/resolve/issue-5035.stderr @@ -15,12 +15,14 @@ LL | impl K for isize {} | help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias | -LL | trait K = dyn I; +LL - type K = dyn I; +LL + trait K = dyn I; | help: a trait with a similar name exists | -LL | impl I for isize {} - | ~ +LL - impl K for isize {} +LL + impl I for isize {} + | error: aborting due to 2 previous errors diff --git a/tests/ui/resolve/issue-5099.stderr b/tests/ui/resolve/issue-5099.stderr index e9b2a9c4d4844..991c39db33514 100644 --- a/tests/ui/resolve/issue-5099.stderr +++ b/tests/ui/resolve/issue-5099.stderr @@ -6,8 +6,9 @@ LL | this.a | help: you might have meant to use `self` here instead | -LL | self.a - | ~~~~ +LL - this.a +LL + self.a + | help: if you meant to use `self`, you are also missing a `self` receiver argument | LL | fn a(&self) -> A { @@ -21,8 +22,9 @@ LL | this.b(x); | help: you might have meant to use `self` here instead | -LL | self.b(x); - | ~~~~ +LL - this.b(x); +LL + self.b(x); + | help: if you meant to use `self`, you are also missing a `self` receiver argument | LL | fn b(&self, x: i32) { @@ -36,8 +38,9 @@ LL | let _ = || this.a; | help: you might have meant to use `self` here instead | -LL | let _ = || self.a; - | ~~~~ +LL - let _ = || this.a; +LL + let _ = || self.a; + | help: if you meant to use `self`, you are also missing a `self` receiver argument | LL | fn c(&self) { diff --git a/tests/ui/resolve/issue-55673.stderr b/tests/ui/resolve/issue-55673.stderr index 7d420126199b1..2695868b77187 100644 --- a/tests/ui/resolve/issue-55673.stderr +++ b/tests/ui/resolve/issue-55673.stderr @@ -6,8 +6,9 @@ LL | T::Baa: std::fmt::Debug, | help: change the associated type name to use `Bar` from `Foo` | -LL | T::Bar: std::fmt::Debug, - | ~~~ +LL - T::Baa: std::fmt::Debug, +LL + T::Bar: std::fmt::Debug, + | error[E0220]: associated type `Baa` not found for `T` --> $DIR/issue-55673.rs:16:8 @@ -18,11 +19,12 @@ LL | T::Baa: std::fmt::Debug, help: consider further restricting type parameter `T` with trait `Foo` | LL | T::Baa: std::fmt::Debug, T: Foo - | ~~~~~~~~ + | ++++++ help: ...and changing the associated type name | -LL | T::Bar: std::fmt::Debug, - | ~~~ +LL - T::Baa: std::fmt::Debug, +LL + T::Bar: std::fmt::Debug, + | error: aborting due to 2 previous errors diff --git a/tests/ui/resolve/issue-73427.stderr b/tests/ui/resolve/issue-73427.stderr index 0a9a504f79caa..fccbfe547cb21 100644 --- a/tests/ui/resolve/issue-73427.stderr +++ b/tests/ui/resolve/issue-73427.stderr @@ -17,14 +17,16 @@ LL | | } | |_^ help: you might have meant to use one of the following enum variants | -LL | (A::Tuple()).foo(); - | ~~~~~~~~~~~~ +LL - A.foo(); +LL + (A::Tuple()).foo(); + | LL | A::Unit.foo(); - | ~~~~~~~ + | ++++++ help: alternatively, the following enum variant is available | -LL | (A::TupleWithFields(/* fields */)).foo(); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - A.foo(); +LL + (A::TupleWithFields(/* fields */)).foo(); + | error[E0423]: expected value, found enum `B` --> $DIR/issue-73427.rs:35:5 @@ -59,11 +61,12 @@ LL | | } help: you might have meant to use the following enum variant | LL | C::Unit.foo(); - | ~~~~~~~ + | ++++++ help: alternatively, the following enum variant is available | -LL | (C::TupleWithFields(/* fields */)).foo(); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - C.foo(); +LL + (C::TupleWithFields(/* fields */)).foo(); + | error[E0423]: expected value, found enum `D` --> $DIR/issue-73427.rs:39:5 @@ -82,11 +85,12 @@ LL | | } help: you might have meant to use the following enum variant | LL | D::Unit.foo(); - | ~~~~~~~ + | ++++++ help: alternatively, the following enum variant is available | -LL | (D::TupleWithFields(/* fields */)).foo(); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - D.foo(); +LL + (D::TupleWithFields(/* fields */)).foo(); + | error[E0423]: expected value, found enum `E` --> $DIR/issue-73427.rs:41:5 @@ -103,8 +107,9 @@ LL | | } | |_^ help: the following enum variant is available | -LL | (E::TupleWithFields(/* fields */)).foo(); - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - E.foo(); +LL + (E::TupleWithFields(/* fields */)).foo(); + | help: consider importing one of these constants instead | LL + use std::f128::consts::E; @@ -137,9 +142,9 @@ LL | | } help: try to match against one of the enum's variants | LL | if let A::Tuple(3) = x { } - | ~~~~~~~~ + | +++++++ LL | if let A::TupleWithFields(3) = x { } - | ~~~~~~~~~~~~~~~~~~ + | +++++++++++++++++ error[E0423]: expected function, tuple struct or tuple variant, found enum `A` --> $DIR/issue-73427.rs:46:13 @@ -162,9 +167,9 @@ LL | | } help: try to construct one of the enum's variants | LL | let x = A::Tuple(3); - | ~~~~~~~~ + | +++++++ LL | let x = A::TupleWithFields(3); - | ~~~~~~~~~~~~~~~~~~ + | +++++++++++++++++ error: aborting due to 7 previous errors diff --git a/tests/ui/resolve/issue-82865.rs b/tests/ui/resolve/issue-82865.rs index 29a898906e96b..4dc12f2f58956 100644 --- a/tests/ui/resolve/issue-82865.rs +++ b/tests/ui/resolve/issue-82865.rs @@ -2,7 +2,7 @@ #![feature(decl_macro)] -use x::y::z; //~ ERROR: failed to resolve: you might be missing crate `x` +use x::y::z; //~ ERROR: failed to resolve: use of unresolved module or unlinked crate `x` macro mac () { Box::z //~ ERROR: no function or associated item diff --git a/tests/ui/resolve/issue-82865.stderr b/tests/ui/resolve/issue-82865.stderr index bc7e0f0798177..090085460b07b 100644 --- a/tests/ui/resolve/issue-82865.stderr +++ b/tests/ui/resolve/issue-82865.stderr @@ -1,10 +1,10 @@ -error[E0433]: failed to resolve: you might be missing crate `x` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `x` --> $DIR/issue-82865.rs:5:5 | LL | use x::y::z; - | ^ you might be missing crate `x` + | ^ use of unresolved module or unlinked crate `x` | -help: consider importing the `x` crate +help: you might be missing a crate named `x`, add it to your project and import it in your code | LL + extern crate x; | diff --git a/tests/ui/resolve/multiple_definitions_attribute_merging.stderr b/tests/ui/resolve/multiple_definitions_attribute_merging.stderr index 804fa079bb99e..ac6307c7a69eb 100644 --- a/tests/ui/resolve/multiple_definitions_attribute_merging.stderr +++ b/tests/ui/resolve/multiple_definitions_attribute_merging.stderr @@ -21,7 +21,7 @@ Box query stack during panic: #0 [mir_built] building MIR for `::eq` #1 [check_unsafety] unsafety-checking `::eq` -end of query stack +... and 1 other queries... use `env RUST_BACKTRACE=1` to see the full query stack error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0428`. diff --git a/tests/ui/resolve/privacy-enum-ctor.stderr b/tests/ui/resolve/privacy-enum-ctor.stderr index 12a6580048e17..f349b9391d15a 100644 --- a/tests/ui/resolve/privacy-enum-ctor.stderr +++ b/tests/ui/resolve/privacy-enum-ctor.stderr @@ -17,12 +17,14 @@ LL | | } | |_________^ help: you might have meant to use the following enum variant | -LL | m::Z::Unit; - | ~~~~~~~~~~ +LL - n::Z; +LL + m::Z::Unit; + | help: alternatively, the following enum variant is available | -LL | (m::Z::Fn(/* fields */)); - | ~~~~~~~~~~~~~~~~~~~~~~~~ +LL - n::Z; +LL + (m::Z::Fn(/* fields */)); + | error[E0423]: expected value, found enum `Z` --> $DIR/privacy-enum-ctor.rs:25:9 @@ -43,12 +45,14 @@ LL | | } | |_________^ help: you might have meant to use the following enum variant | -LL | m::Z::Unit; - | ~~~~~~~~~~ +LL - Z; +LL + m::Z::Unit; + | help: alternatively, the following enum variant is available | -LL | (m::Z::Fn(/* fields */)); - | ~~~~~~~~~~~~~~~~~~~~~~~~ +LL - Z; +LL + (m::Z::Fn(/* fields */)); + | error[E0423]: expected value, found enum `m::E` --> $DIR/privacy-enum-ctor.rs:41:16 @@ -72,16 +76,19 @@ LL | | } | |_____^ help: you might have meant to use the following enum variant | -LL | let _: E = E::Unit; - | ~~~~~~~ +LL - let _: E = m::E; +LL + let _: E = E::Unit; + | help: alternatively, the following enum variant is available | -LL | let _: E = (E::Fn(/* fields */)); - | ~~~~~~~~~~~~~~~~~~~~~ +LL - let _: E = m::E; +LL + let _: E = (E::Fn(/* fields */)); + | help: a function with a similar name exists | -LL | let _: E = m::f; - | ~ +LL - let _: E = m::E; +LL + let _: E = m::f; + | help: consider importing one of these constants instead | LL + use std::f128::consts::E; @@ -118,11 +125,12 @@ LL | | } help: you might have meant to use the following enum variant | LL | let _: E = E::Unit; - | ~~~~~~~ + | ++++++ help: alternatively, the following enum variant is available | -LL | let _: E = (E::Fn(/* fields */)); - | ~~~~~~~~~~~~~~~~~~~~~ +LL - let _: E = E; +LL + let _: E = (E::Fn(/* fields */)); + | help: consider importing one of these constants instead | LL + use std::f128::consts::E; @@ -168,12 +176,14 @@ LL | | } | |_________^ help: you might have meant to use the following enum variant | -LL | let _: Z = m::Z::Unit; - | ~~~~~~~~~~ +LL - let _: Z = m::n::Z; +LL + let _: Z = m::Z::Unit; + | help: alternatively, the following enum variant is available | -LL | let _: Z = (m::Z::Fn(/* fields */)); - | ~~~~~~~~~~~~~~~~~~~~~~~~ +LL - let _: Z = m::n::Z; +LL + let _: Z = (m::Z::Fn(/* fields */)); + | error[E0412]: cannot find type `Z` in this scope --> $DIR/privacy-enum-ctor.rs:61:12 diff --git a/tests/ui/resolve/privacy-struct-ctor.stderr b/tests/ui/resolve/privacy-struct-ctor.stderr index c1fcaaf05738f..1d8c741c964e1 100644 --- a/tests/ui/resolve/privacy-struct-ctor.stderr +++ b/tests/ui/resolve/privacy-struct-ctor.stderr @@ -55,8 +55,9 @@ LL | pub(in m) struct Z(pub(in m::n) u8); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider making the field publicly accessible | -LL | pub(in m) struct Z(pub u8); - | ~~~ +LL - pub(in m) struct Z(pub(in m::n) u8); +LL + pub(in m) struct Z(pub u8); + | error[E0603]: tuple struct constructor `S` is private --> $DIR/privacy-struct-ctor.rs:29:8 @@ -112,8 +113,9 @@ LL | pub(in m) struct Z(pub(in m::n) u8); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider making the field publicly accessible | -LL | pub(in m) struct Z(pub u8); - | ~~~ +LL - pub(in m) struct Z(pub(in m::n) u8); +LL + pub(in m) struct Z(pub u8); + | error[E0603]: tuple struct constructor `S` is private --> $DIR/privacy-struct-ctor.rs:41:16 diff --git a/tests/ui/resolve/proc_macro_generated_packed.stderr b/tests/ui/resolve/proc_macro_generated_packed.stderr index a5a02c9c393ae..8b70059503486 100644 --- a/tests/ui/resolve/proc_macro_generated_packed.stderr +++ b/tests/ui/resolve/proc_macro_generated_packed.stderr @@ -12,6 +12,6 @@ Box query stack during panic: #0 [mir_built] building MIR for `::eq` #1 [check_unsafety] unsafety-checking `::eq` -end of query stack +... and 1 other queries... use `env RUST_BACKTRACE=1` to see the full query stack error: aborting due to 1 previous error diff --git a/tests/ui/resolve/resolve-bad-visibility.stderr b/tests/ui/resolve/resolve-bad-visibility.stderr index 281e5afb22302..ac7e1c735b1f9 100644 --- a/tests/ui/resolve/resolve-bad-visibility.stderr +++ b/tests/ui/resolve/resolve-bad-visibility.stderr @@ -16,24 +16,24 @@ error[E0742]: visibilities can only be restricted to ancestor modules LL | pub(in std::vec) struct F; | ^^^^^^^^ -error[E0433]: failed to resolve: you might be missing crate `nonexistent` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `nonexistent` --> $DIR/resolve-bad-visibility.rs:7:8 | LL | pub(in nonexistent) struct G; - | ^^^^^^^^^^^ you might be missing crate `nonexistent` + | ^^^^^^^^^^^ use of unresolved module or unlinked crate `nonexistent` | -help: consider importing the `nonexistent` crate +help: you might be missing a crate named `nonexistent`, add it to your project and import it in your code | LL + extern crate nonexistent; | -error[E0433]: failed to resolve: you might be missing crate `too_soon` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `too_soon` --> $DIR/resolve-bad-visibility.rs:8:8 | LL | pub(in too_soon) struct H; - | ^^^^^^^^ you might be missing crate `too_soon` + | ^^^^^^^^ use of unresolved module or unlinked crate `too_soon` | -help: consider importing the `too_soon` crate +help: you might be missing a crate named `too_soon`, add it to your project and import it in your code | LL + extern crate too_soon; | diff --git a/tests/ui/resolve/resolve-conflict-extern-crate-vs-extern-crate.stderr b/tests/ui/resolve/resolve-conflict-extern-crate-vs-extern-crate.stderr index 999e9a47d6c5f..f1db2d71b6a66 100644 --- a/tests/ui/resolve/resolve-conflict-extern-crate-vs-extern-crate.stderr +++ b/tests/ui/resolve/resolve-conflict-extern-crate-vs-extern-crate.stderr @@ -3,7 +3,8 @@ error[E0259]: the name `std` is defined multiple times = note: `std` must be defined only once in the type namespace of this module help: you can use `as` to change the binding name of the import | -LL | extern crate std as other_std; +LL - extern crate std; +LL + extern crate std as other_std; | error: aborting due to 1 previous error diff --git a/tests/ui/resolve/resolve-conflict-import-vs-extern-crate.stderr b/tests/ui/resolve/resolve-conflict-import-vs-extern-crate.stderr index a8d0efedb6c42..40c76821bb835 100644 --- a/tests/ui/resolve/resolve-conflict-import-vs-extern-crate.stderr +++ b/tests/ui/resolve/resolve-conflict-import-vs-extern-crate.stderr @@ -7,8 +7,9 @@ LL | use std::slice as std; = note: `std` must be defined only once in the type namespace of this module help: you can use `as` to change the binding name of the import | -LL | use std::slice as other_std; - | ~~~~~~~~~~~~ +LL - use std::slice as std; +LL + use std::slice as other_std; + | error: aborting due to 1 previous error diff --git a/tests/ui/resolve/resolve-inconsistent-names.stderr b/tests/ui/resolve/resolve-inconsistent-names.stderr index d6240fb8f872c..5fac622eef263 100644 --- a/tests/ui/resolve/resolve-inconsistent-names.stderr +++ b/tests/ui/resolve/resolve-inconsistent-names.stderr @@ -35,7 +35,7 @@ LL | (A, B) | (ref B, c) | (c, A) => () help: if you meant to match on unit variant `E::A`, use the full path in the pattern | LL | (E::A, B) | (ref B, c) | (c, A) => () - | ~~~~ + | +++ error[E0408]: variable `B` is not bound in all patterns --> $DIR/resolve-inconsistent-names.rs:19:31 @@ -65,7 +65,7 @@ LL | (CONST1, _) | (_, Const2) => () help: if you meant to match on constant `m::Const2`, use the full path in the pattern | LL | (CONST1, _) | (_, m::Const2) => () - | ~~~~~~~~~ + | +++ error[E0408]: variable `CONST1` is not bound in all patterns --> $DIR/resolve-inconsistent-names.rs:31:23 diff --git a/tests/ui/resolve/resolve-issue-135614-assoc-const.import_trait_associated_functions.stderr b/tests/ui/resolve/resolve-issue-135614-assoc-const.import_trait_associated_functions.stderr new file mode 100644 index 0000000000000..366bc1bf03cc7 --- /dev/null +++ b/tests/ui/resolve/resolve-issue-135614-assoc-const.import_trait_associated_functions.stderr @@ -0,0 +1,19 @@ +error[E0005]: refutable pattern in local binding + --> $DIR/resolve-issue-135614-assoc-const.rs:21:9 + | +LL | let DEFAULT: u32 = 0; + | ^^^^^^^ pattern `1_u32..=u32::MAX` not covered +LL | const DEFAULT: u32 = 0; + | ------------------ missing patterns are not covered because `DEFAULT` is interpreted as a constant pattern, not a new variable + | + = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant + = note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html + = note: the matched value is of type `u32` +help: introduce a variable instead + | +LL | let DEFAULT_var: u32 = 0; + | ++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0005`. diff --git a/tests/ui/resolve/resolve-issue-135614-assoc-const.normal.stderr b/tests/ui/resolve/resolve-issue-135614-assoc-const.normal.stderr new file mode 100644 index 0000000000000..1392eb48f8ccb --- /dev/null +++ b/tests/ui/resolve/resolve-issue-135614-assoc-const.normal.stderr @@ -0,0 +1,30 @@ +error[E0658]: `use` associated items of traits is unstable + --> $DIR/resolve-issue-135614-assoc-const.rs:6:5 + | +LL | use MyDefault::DEFAULT; + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #134691 for more information + = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0005]: refutable pattern in local binding + --> $DIR/resolve-issue-135614-assoc-const.rs:21:9 + | +LL | let DEFAULT: u32 = 0; + | ^^^^^^^ pattern `1_u32..=u32::MAX` not covered +LL | const DEFAULT: u32 = 0; + | ------------------ missing patterns are not covered because `DEFAULT` is interpreted as a constant pattern, not a new variable + | + = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant + = note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html + = note: the matched value is of type `u32` +help: introduce a variable instead + | +LL | let DEFAULT_var: u32 = 0; + | ++++ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0005, E0658. +For more information about an error, try `rustc --explain E0005`. diff --git a/tests/ui/resolve/resolve-issue-135614-assoc-const.rs b/tests/ui/resolve/resolve-issue-135614-assoc-const.rs new file mode 100644 index 0000000000000..5a592922cd461 --- /dev/null +++ b/tests/ui/resolve/resolve-issue-135614-assoc-const.rs @@ -0,0 +1,30 @@ +//@ revisions: normal import_trait_associated_functions +#![cfg_attr(import_trait_associated_functions, feature(import_trait_associated_functions))] + +// Makes sure that imported constant can be used in pattern bindings. + +use MyDefault::DEFAULT; //[normal]~ ERROR `use` associated items of traits is unstable + +trait MyDefault { + const DEFAULT: Self; +} + +impl MyDefault for u32 { + const DEFAULT: u32 = 0; +} + +impl MyDefault for () { + const DEFAULT: () = (); +} + +fn foo(x: u32) -> u32 { + let DEFAULT: u32 = 0; //~ ERROR refutable pattern in local binding + const DEFAULT: u32 = 0; + if let DEFAULT = x { DEFAULT } else { 1 } +} + +fn bar() { + let DEFAULT = (); +} + +fn main() {} diff --git a/tests/ui/resolve/resolve-issue-135614.normal.stderr b/tests/ui/resolve/resolve-issue-135614.normal.stderr new file mode 100644 index 0000000000000..a9adeb0848e6f --- /dev/null +++ b/tests/ui/resolve/resolve-issue-135614.normal.stderr @@ -0,0 +1,13 @@ +error[E0658]: `use` associated items of traits is unstable + --> $DIR/resolve-issue-135614.rs:7:5 + | +LL | use A::b; + | ^^^^ + | + = note: see issue #134691 for more information + = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/resolve/resolve-issue-135614.rs b/tests/ui/resolve/resolve-issue-135614.rs new file mode 100644 index 0000000000000..fec9499365bcb --- /dev/null +++ b/tests/ui/resolve/resolve-issue-135614.rs @@ -0,0 +1,15 @@ +//@ revisions: normal import_trait_associated_functions +//@[import_trait_associated_functions] check-pass +#![cfg_attr(import_trait_associated_functions, feature(import_trait_associated_functions))] + +// Makes sure that imported associated functions are shadowed by the local declarations. + +use A::b; //[normal]~ ERROR `use` associated items of traits is unstable + +trait A { + fn b() {} +} + +fn main() { + let b: (); +} diff --git a/tests/ui/resolve/resolve-variant-assoc-item.stderr b/tests/ui/resolve/resolve-variant-assoc-item.stderr index 9a5a605ac05bd..4b15114f022a9 100644 --- a/tests/ui/resolve/resolve-variant-assoc-item.stderr +++ b/tests/ui/resolve/resolve-variant-assoc-item.stderr @@ -6,8 +6,9 @@ LL | E::V::associated_item; | help: there is an enum variant `E::V`; try using the variant's enum | -LL | E::associated_item; - | ~ +LL - E::V::associated_item; +LL + E::associated_item; + | error[E0433]: failed to resolve: `V` is a variant, not a module --> $DIR/resolve-variant-assoc-item.rs:6:5 @@ -17,8 +18,9 @@ LL | V::associated_item; | help: there is an enum variant `E::V`; try using the variant's enum | -LL | E::associated_item; - | ~ +LL - V::associated_item; +LL + E::associated_item; + | error: aborting due to 2 previous errors diff --git a/tests/ui/resolve/suggest-path-for-tuple-struct.stderr b/tests/ui/resolve/suggest-path-for-tuple-struct.stderr index 12c631f5a8300..68a5b55097826 100644 --- a/tests/ui/resolve/suggest-path-for-tuple-struct.stderr +++ b/tests/ui/resolve/suggest-path-for-tuple-struct.stderr @@ -6,8 +6,9 @@ LL | let _ = SomeTupleStruct.new(); | help: use the path separator to refer to an item | -LL | let _ = SomeTupleStruct::new(); - | ~~ +LL - let _ = SomeTupleStruct.new(); +LL + let _ = SomeTupleStruct::new(); + | error[E0423]: expected value, found struct `SomeRegularStruct` --> $DIR/suggest-path-for-tuple-struct.rs:24:13 @@ -17,8 +18,9 @@ LL | let _ = SomeRegularStruct.new(); | help: use the path separator to refer to an item | -LL | let _ = SomeRegularStruct::new(); - | ~~ +LL - let _ = SomeRegularStruct.new(); +LL + let _ = SomeRegularStruct::new(); + | error: aborting due to 2 previous errors diff --git a/tests/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr b/tests/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr index 9c12fd2644c56..5db943cd10d0f 100644 --- a/tests/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr +++ b/tests/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr @@ -6,8 +6,9 @@ LL | a.I | help: use the path separator to refer to an item | -LL | a::I - | ~~ +LL - a.I +LL + a::I + | error[E0423]: expected value, found module `a` --> $DIR/suggest-path-instead-of-mod-dot-item.rs:23:5 @@ -17,8 +18,9 @@ LL | a.g() | help: use the path separator to refer to an item | -LL | a::g() - | ~~ +LL - a.g() +LL + a::g() + | error[E0423]: expected value, found module `a` --> $DIR/suggest-path-instead-of-mod-dot-item.rs:29:5 @@ -28,8 +30,9 @@ LL | a.b.J | help: use the path separator to refer to an item | -LL | a::b.J - | ~~ +LL - a.b.J +LL + a::b.J + | error[E0423]: expected value, found module `a::b` --> $DIR/suggest-path-instead-of-mod-dot-item.rs:35:5 @@ -42,12 +45,14 @@ LL | a::b.J | help: use the path separator to refer to an item | -LL | a::b::J - | ~~ +LL - a::b.J +LL + a::b::J + | help: a constant with a similar name exists | -LL | a::I.J - | ~ +LL - a::b.J +LL + a::I.J + | error[E0423]: expected value, found module `a` --> $DIR/suggest-path-instead-of-mod-dot-item.rs:42:5 @@ -57,8 +62,9 @@ LL | a.b.f(); | help: use the path separator to refer to an item | -LL | a::b.f(); - | ~~ +LL - a.b.f(); +LL + a::b.f(); + | error[E0423]: expected value, found module `a::b` --> $DIR/suggest-path-instead-of-mod-dot-item.rs:46:12 @@ -82,12 +88,14 @@ LL | a::b.f() | help: use the path separator to refer to an item | -LL | a::b::f() - | ~~ +LL - a::b.f() +LL + a::b::f() + | help: a constant with a similar name exists | -LL | a::I.f() - | ~ +LL - a::b.f() +LL + a::I.f() + | error[E0423]: expected value, found module `a::b` --> $DIR/suggest-path-instead-of-mod-dot-item.rs:59:5 @@ -145,8 +153,9 @@ LL | let _ = create!(method); = note: this error originates in the macro `create` (in Nightly builds, run with -Z macro-backtrace for more info) help: use the path separator to refer to an item | -LL | a::f() - | ~~ +LL - a.f() +LL + a::f() + | error[E0423]: expected value, found module `a` --> $DIR/suggest-path-instead-of-mod-dot-item.rs:85:9 @@ -160,8 +169,9 @@ LL | let _ = create!(field); = note: this error originates in the macro `create` (in Nightly builds, run with -Z macro-backtrace for more info) help: use the path separator to refer to an item | -LL | a::f - | ~~ +LL - a.f +LL + a::f + | error: aborting due to 13 previous errors diff --git a/tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr b/tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr index 5662021a2d523..5832cb69a3dd8 100644 --- a/tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr +++ b/tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr @@ -28,8 +28,9 @@ LL | println!("{self.config}"); | +++++ help: a local variable with a similar name exists | -LL | println!("{cofig}"); - | ~~~~~ +LL - println!("{config}"); +LL + println!("{cofig}"); + | error[E0425]: cannot find value `bah` in this scope --> $DIR/typo-suggestion-for-variable-with-name-similar-to-struct-field.rs:33:9 @@ -46,8 +47,9 @@ LL | Self::bah; | ++++++ help: a function with a similar name exists | -LL | ba; - | ~~ +LL - bah; +LL + ba; + | error[E0425]: cannot find value `BAR` in this scope --> $DIR/typo-suggestion-for-variable-with-name-similar-to-struct-field.rs:35:9 @@ -65,7 +67,7 @@ LL | Self::BAR; help: a constant with a similar name exists | LL | BARR; - | ~~~~ + | + error[E0412]: cannot find type `Baz` in this scope --> $DIR/typo-suggestion-for-variable-with-name-similar-to-struct-field.rs:37:18 @@ -82,8 +84,9 @@ LL | let foo: Self::Baz = "".to_string(); | ++++++ help: a type alias with a similar name exists | -LL | let foo: Bar = "".to_string(); - | ~~~ +LL - let foo: Baz = "".to_string(); +LL + let foo: Bar = "".to_string(); + | error[E0425]: cannot find function `baz` in this scope --> $DIR/typo-suggestion-for-variable-with-name-similar-to-struct-field.rs:31:9 @@ -100,8 +103,9 @@ LL | self.baz(); | +++++ help: a function with a similar name exists | -LL | ba(); - | ~~ +LL - baz(); +LL + ba(); + | error: aborting due to 7 previous errors diff --git a/tests/ui/resolve/typo-suggestion-mistyped-in-path.rs b/tests/ui/resolve/typo-suggestion-mistyped-in-path.rs index 3ce17a14f146b..188e2ca7f1133 100644 --- a/tests/ui/resolve/typo-suggestion-mistyped-in-path.rs +++ b/tests/ui/resolve/typo-suggestion-mistyped-in-path.rs @@ -29,8 +29,8 @@ fn main() { //~| NOTE use of undeclared type `Struc` modul::foo(); - //~^ ERROR failed to resolve: use of undeclared crate or module `modul` - //~| NOTE use of undeclared crate or module `modul` + //~^ ERROR failed to resolve: use of unresolved module or unlinked crate `modul` + //~| NOTE use of unresolved module or unlinked crate `modul` module::Struc::foo(); //~^ ERROR failed to resolve: could not find `Struc` in `module` diff --git a/tests/ui/resolve/typo-suggestion-mistyped-in-path.stderr b/tests/ui/resolve/typo-suggestion-mistyped-in-path.stderr index f4fb7fd955f2b..2d0d0d0f38670 100644 --- a/tests/ui/resolve/typo-suggestion-mistyped-in-path.stderr +++ b/tests/ui/resolve/typo-suggestion-mistyped-in-path.stderr @@ -18,8 +18,9 @@ LL | Struct::fob(); | help: there is an associated function `foo` with a similar name | -LL | Struct::foo(); - | ~~~ +LL - Struct::fob(); +LL + Struct::foo(); + | error[E0433]: failed to resolve: use of undeclared type `Struc` --> $DIR/typo-suggestion-mistyped-in-path.rs:27:5 @@ -30,16 +31,16 @@ LL | Struc::foo(); | use of undeclared type `Struc` | help: a struct with a similar name exists: `Struct` -error[E0433]: failed to resolve: use of undeclared crate or module `modul` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `modul` --> $DIR/typo-suggestion-mistyped-in-path.rs:31:5 | LL | modul::foo(); - | ^^^^^ use of undeclared crate or module `modul` + | ^^^^^ use of unresolved module or unlinked crate `modul` | help: there is a crate or module with a similar name | LL | module::foo(); - | ~~~~~~ + | + error[E0433]: failed to resolve: use of undeclared type `Trai` --> $DIR/typo-suggestion-mistyped-in-path.rs:39:5 diff --git a/tests/ui/resolve/unboxed-closure-sugar-nonexistent-trait.stderr b/tests/ui/resolve/unboxed-closure-sugar-nonexistent-trait.stderr index 8addc0303fb91..f885705a17b5c 100644 --- a/tests/ui/resolve/unboxed-closure-sugar-nonexistent-trait.stderr +++ b/tests/ui/resolve/unboxed-closure-sugar-nonexistent-trait.stderr @@ -12,7 +12,8 @@ LL | fn g isize>(x: F) {} | help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias | -LL | trait Typedef = isize; +LL - type Typedef = isize; +LL + trait Typedef = isize; | error: aborting due to 2 previous errors diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/ICE-130779-never-arm-no-oatherwise-block.rs b/tests/ui/rfcs/rfc-0000-never_patterns/ICE-130779-never-arm-no-oatherwise-block.rs new file mode 100644 index 0000000000000..2a7e730af16ba --- /dev/null +++ b/tests/ui/rfcs/rfc-0000-never_patterns/ICE-130779-never-arm-no-oatherwise-block.rs @@ -0,0 +1,12 @@ +#![feature(never_patterns)] +#![allow(incomplete_features)] + +enum E { A } + +fn main() { + match E::A { + ! | //~ ERROR: a trailing `|` is not allowed in an or-pattern + //~^ ERROR: mismatched types + if true => {} //~ ERROR: a never pattern is always unreachable + } +} diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/ICE-130779-never-arm-no-oatherwise-block.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/ICE-130779-never-arm-no-oatherwise-block.stderr new file mode 100644 index 0000000000000..26731e29ffc57 --- /dev/null +++ b/tests/ui/rfcs/rfc-0000-never_patterns/ICE-130779-never-arm-no-oatherwise-block.stderr @@ -0,0 +1,33 @@ +error: a trailing `|` is not allowed in an or-pattern + --> $DIR/ICE-130779-never-arm-no-oatherwise-block.rs:8:11 + | +LL | ! | + | - ^ + | | + | while parsing this or-pattern starting here + | +help: remove the `|` + | +LL - ! | +LL + ! + | + +error: a never pattern is always unreachable + --> $DIR/ICE-130779-never-arm-no-oatherwise-block.rs:10:20 + | +LL | if true => {} + | ^^ + | | + | this will never be executed + | help: remove this expression + +error: mismatched types + --> $DIR/ICE-130779-never-arm-no-oatherwise-block.rs:8:9 + | +LL | ! | + | ^ a never pattern must be used on an uninhabited type + | + = note: the matched value is of type `E` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/ICE-133063-never-arm-no-otherwise-block.rs b/tests/ui/rfcs/rfc-0000-never_patterns/ICE-133063-never-arm-no-otherwise-block.rs new file mode 100644 index 0000000000000..4f52f6ee4bd91 --- /dev/null +++ b/tests/ui/rfcs/rfc-0000-never_patterns/ICE-133063-never-arm-no-otherwise-block.rs @@ -0,0 +1,16 @@ +#![feature(never_type)] +#![feature(never_patterns)] +#![allow(incomplete_features)] + +enum Void {} + +fn foo(x: Void) { + loop { + match x { + (!|!) if false => {} //~ ERROR a never pattern is always unreachable + _ => {} + } + } +} + +fn main() {} diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/ICE-133063-never-arm-no-otherwise-block.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/ICE-133063-never-arm-no-otherwise-block.stderr new file mode 100644 index 0000000000000..cc451fed31805 --- /dev/null +++ b/tests/ui/rfcs/rfc-0000-never_patterns/ICE-133063-never-arm-no-otherwise-block.stderr @@ -0,0 +1,11 @@ +error: a never pattern is always unreachable + --> $DIR/ICE-133063-never-arm-no-otherwise-block.rs:10:31 + | +LL | (!|!) if false => {} + | ^^ + | | + | this will never be executed + | help: remove this expression + +error: aborting due to 1 previous error + diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/ICE-133117-duplicate-never-arm.rs b/tests/ui/rfcs/rfc-0000-never_patterns/ICE-133117-duplicate-never-arm.rs new file mode 100644 index 0000000000000..bca2ab5657021 --- /dev/null +++ b/tests/ui/rfcs/rfc-0000-never_patterns/ICE-133117-duplicate-never-arm.rs @@ -0,0 +1,14 @@ +#![feature(never_type)] +#![feature(never_patterns)] +#![allow(incomplete_features)] + +enum Void {} + +fn foo(x: Void) { + match x { + (!|!) if true => {} //~ ERROR a never pattern is always unreachable + (!|!) if true => {} //~ ERROR a never pattern is always unreachable + } +} + +fn main() {} diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/ICE-133117-duplicate-never-arm.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/ICE-133117-duplicate-never-arm.stderr new file mode 100644 index 0000000000000..5da9642dc1998 --- /dev/null +++ b/tests/ui/rfcs/rfc-0000-never_patterns/ICE-133117-duplicate-never-arm.stderr @@ -0,0 +1,20 @@ +error: a never pattern is always unreachable + --> $DIR/ICE-133117-duplicate-never-arm.rs:9:26 + | +LL | (!|!) if true => {} + | ^^ + | | + | this will never be executed + | help: remove this expression + +error: a never pattern is always unreachable + --> $DIR/ICE-133117-duplicate-never-arm.rs:10:26 + | +LL | (!|!) if true => {} + | ^^ + | | + | this will never be executed + | help: remove this expression + +error: aborting due to 2 previous errors + diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/always-read-in-closure-capture.rs b/tests/ui/rfcs/rfc-0000-never_patterns/always-read-in-closure-capture.rs new file mode 100644 index 0000000000000..3c4cd57ec24b5 --- /dev/null +++ b/tests/ui/rfcs/rfc-0000-never_patterns/always-read-in-closure-capture.rs @@ -0,0 +1,16 @@ +//@ check-pass + +// Make sure that the closure captures `s` so it can perform a read of `s`. + +#![feature(never_patterns)] +#![allow(incomplete_features)] + +enum Void {} + +fn by_value(s: Void) { + move || { + let ! = s; + }; +} + +fn main() {} diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr deleted file mode 100644 index d78f4a5f6ebb5..0000000000000 --- a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr +++ /dev/null @@ -1,55 +0,0 @@ -error: unreachable pattern - --> $DIR/unreachable.rs:16:9 - | -LL | Err(!), - | ^^^^^^ - | - = note: this pattern matches no values because `Void` is uninhabited -note: the lint level is defined here - --> $DIR/unreachable.rs:6:9 - | -LL | #![deny(unreachable_patterns)] - | ^^^^^^^^^^^^^^^^^^^^ - -error: unreachable pattern - --> $DIR/unreachable.rs:19:19 - | -LL | let (Ok(_x) | Err(!)) = res_void; - | ^^^^^^ - | - = note: this pattern matches no values because `Void` is uninhabited - -error: unreachable pattern - --> $DIR/unreachable.rs:21:12 - | -LL | if let Err(!) = res_void {} - | ^^^^^^ - | - = note: this pattern matches no values because `Void` is uninhabited - -error: unreachable pattern - --> $DIR/unreachable.rs:23:24 - | -LL | if let (Ok(true) | Err(!)) = res_void {} - | ^^^^^^ - | - = note: this pattern matches no values because `Void` is uninhabited - -error: unreachable pattern - --> $DIR/unreachable.rs:25:23 - | -LL | for (Ok(mut _x) | Err(!)) in [res_void] {} - | ^^^^^^ - | - = note: this pattern matches no values because `Void` is uninhabited - -error: unreachable pattern - --> $DIR/unreachable.rs:29:18 - | -LL | fn foo((Ok(_x) | Err(!)): Result) {} - | ^^^^^^ - | - = note: this pattern matches no values because `Void` is uninhabited - -error: aborting due to 6 previous errors - diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.normal.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.normal.stderr deleted file mode 100644 index a3bf8e80ecec7..0000000000000 --- a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.normal.stderr +++ /dev/null @@ -1,44 +0,0 @@ -error: unreachable pattern - --> $DIR/unreachable.rs:16:9 - | -LL | Err(!), - | ^^^^^^ - | -note: the lint level is defined here - --> $DIR/unreachable.rs:6:9 - | -LL | #![deny(unreachable_patterns)] - | ^^^^^^^^^^^^^^^^^^^^ - -error: unreachable pattern - --> $DIR/unreachable.rs:19:19 - | -LL | let (Ok(_x) | Err(!)) = res_void; - | ^^^^^^ - -error: unreachable pattern - --> $DIR/unreachable.rs:21:12 - | -LL | if let Err(!) = res_void {} - | ^^^^^^ - -error: unreachable pattern - --> $DIR/unreachable.rs:23:24 - | -LL | if let (Ok(true) | Err(!)) = res_void {} - | ^^^^^^ - -error: unreachable pattern - --> $DIR/unreachable.rs:25:23 - | -LL | for (Ok(mut _x) | Err(!)) in [res_void] {} - | ^^^^^^ - -error: unreachable pattern - --> $DIR/unreachable.rs:29:18 - | -LL | fn foo((Ok(_x) | Err(!)): Result) {} - | ^^^^^^ - -error: aborting due to 6 previous errors - diff --git a/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.current.stderr b/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.current.stderr deleted file mode 100644 index f2727336bc56b..0000000000000 --- a/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.current.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0277]: the trait bound `Something: Termination` is not satisfied - --> $DIR/issue-103052-2.rs:15:22 - | -LL | fn main() -> Something { - | ^^^^^^^^^ the trait `Termination` is not implemented for `Something` - | -note: required by a bound in `Main::main::{opaque#0}` - --> $DIR/issue-103052-2.rs:9:27 - | -LL | fn main() -> impl std::process::Termination; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Main::main::{opaque#0}` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.next.stderr b/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.next.stderr deleted file mode 100644 index 4bb420664f7f4..0000000000000 --- a/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.next.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0277]: the trait bound `Something: Termination` is not satisfied - --> $DIR/issue-103052-2.rs:15:22 - | -LL | fn main() -> Something { - | ^^^^^^^^^ the trait `Termination` is not implemented for `Something` - | -note: required by a bound in `Main::{opaque#0}` - --> $DIR/issue-103052-2.rs:9:27 - | -LL | fn main() -> impl std::process::Termination; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Main::{opaque#0}` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-box-dyn-error-err.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-box-dyn-error-err.rs index fb6718e55b286..00a0ea04c0d1d 100644 --- a/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-box-dyn-error-err.rs +++ b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-box-dyn-error-err.rs @@ -1,7 +1,7 @@ //@ run-fail //@ error-pattern:returned Box from main() //@ failure-status: 1 -//@ ignore-emscripten no processes +//@ needs-subprocess use std::error::Error; use std::io; diff --git a/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-never.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-never.rs index 91be3afbe225a..3b80c2b49a508 100644 --- a/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-never.rs +++ b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-never.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:oh, dear -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() -> ! { panic!("oh, dear"); diff --git a/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs index f1d972b3c5503..48605309965f7 100644 --- a/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs +++ b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs @@ -1,7 +1,7 @@ //@ run-fail //@ error-pattern:returned Box from main() //@ failure-status: 1 -//@ ignore-emscripten no processes +//@ needs-subprocess use std::io::{Error, ErrorKind}; diff --git a/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-str-err.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-str-err.rs index acf3da2d55f36..8f7b3da31bb4c 100644 --- a/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-str-err.rs +++ b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-str-err.rs @@ -1,7 +1,7 @@ //@ run-fail //@ error-pattern: An error message for you //@ failure-status: 1 -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() -> Result<(), &'static str> { Err("An error message for you") diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/struct.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/struct.stderr index 39b1ef1e078c7..88411f29b16e4 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/struct.stderr +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/struct.stderr @@ -59,7 +59,7 @@ LL | let NormalStruct { first_field, second_field } = ns; help: add `..` at the end of the field list to ignore all other fields | LL | let NormalStruct { first_field, second_field , .. } = ns; - | ~~~~~~ + | ++++ error[E0423]: cannot initialize a tuple struct which contains private fields --> $DIR/struct.rs:20:14 @@ -76,7 +76,7 @@ LL | let TupleStruct { 0: first_field, 1: second_field } = ts; help: add `..` at the end of the field list to ignore all other fields | LL | let TupleStruct { 0: first_field, 1: second_field , .. } = ts; - | ~~~~~~ + | ++++ error[E0638]: `..` required with struct marked as non-exhaustive --> $DIR/struct.rs:35:9 @@ -87,7 +87,7 @@ LL | let UnitStruct { } = us; help: add `..` at the end of the field list to ignore all other fields | LL | let UnitStruct { .. } = us; - | ~~~~ + | ++ error: aborting due to 9 previous errors diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/variant.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/variant.stderr index 4083f57a9cdf9..dcecd53d38a1e 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/variant.stderr +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/variant.stderr @@ -83,7 +83,7 @@ LL | NonExhaustiveVariants::Struct { field } => "" help: add `..` at the end of the field list to ignore all other fields | LL | NonExhaustiveVariants::Struct { field , .. } => "" - | ~~~~~~ + | ++++ error[E0638]: `..` required with variant marked as non-exhaustive --> $DIR/variant.rs:30:12 @@ -94,7 +94,7 @@ LL | if let NonExhaustiveVariants::Struct { field } = variant_struct { help: add `..` at the end of the field list to ignore all other fields | LL | if let NonExhaustiveVariants::Struct { field , .. } = variant_struct { - | ~~~~~~ + | ++++ error: aborting due to 8 previous errors diff --git a/tests/ui/rfcs/rfc-2091-track-caller/error-with-start.rs b/tests/ui/rfcs/rfc-2091-track-caller/error-with-start.rs deleted file mode 100644 index f0e111b578f9f..0000000000000 --- a/tests/ui/rfcs/rfc-2091-track-caller/error-with-start.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![feature(start)] - -#[start] -#[track_caller] //~ ERROR `#[start]` function is not allowed to be `#[track_caller]` -fn start(_argc: isize, _argv: *const *const u8) -> isize { - panic!("{}: oh no", std::panic::Location::caller()); -} diff --git a/tests/ui/rfcs/rfc-2091-track-caller/error-with-start.stderr b/tests/ui/rfcs/rfc-2091-track-caller/error-with-start.stderr deleted file mode 100644 index 2738444f21f64..0000000000000 --- a/tests/ui/rfcs/rfc-2091-track-caller/error-with-start.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: `#[start]` function is not allowed to be `#[track_caller]` - --> $DIR/error-with-start.rs:4:1 - | -LL | #[track_caller] - | ^^^^^^^^^^^^^^^ -LL | fn start(_argc: isize, _argv: *const *const u8) -> isize { - | -------------------------------------------------------- `#[start]` function is not allowed to be `#[track_caller]` - -error: aborting due to 1 previous error - diff --git a/tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-1.stderr b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-1.stderr index 1047dbe1063e8..106268ac2c7ae 100644 --- a/tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-1.stderr +++ b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-1.stderr @@ -2,7 +2,9 @@ error[E0432]: unresolved import `xcrate` --> $DIR/non-existent-1.rs:3:5 | LL | use xcrate::S; - | ^^^^^^ use of undeclared crate or module `xcrate` + | ^^^^^^ use of unresolved module or unlinked crate `xcrate` + | + = help: you might be missing a crate named `xcrate` error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-2126-extern-absolute-paths/not-allowed.stderr b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/not-allowed.stderr index d0c084f7bd5d0..38360e06cbe79 100644 --- a/tests/ui/rfcs/rfc-2126-extern-absolute-paths/not-allowed.stderr +++ b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/not-allowed.stderr @@ -7,7 +7,7 @@ LL | use alloc; help: consider importing this module instead | LL | use std::alloc; - | ~~~~~~~~~~ + | +++++ error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/check-pass.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/check-pass.rs index 674cf930c0a6a..9279e7a955ed8 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/check-pass.rs +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/check-pass.rs @@ -9,8 +9,6 @@ //@ check-pass //@ only-x86_64 -#![feature(target_feature_11)] - #[target_feature(enable = "sse2")] const fn sse2() {} diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/closures-inherit-target_feature.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/closures-inherit-target_feature.rs index 122ef542e7d63..aecea6314d412 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/closures-inherit-target_feature.rs +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/closures-inherit-target_feature.rs @@ -3,8 +3,6 @@ //@ check-pass //@ only-x86_64 -#![feature(target_feature_11)] - #[target_feature(enable = "avx")] fn also_use_avx() { println!("Hello from AVX") diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/feature-gate-target_feature_11.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/feature-gate-target_feature_11.rs deleted file mode 100644 index 2add6b2e18653..0000000000000 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/feature-gate-target_feature_11.rs +++ /dev/null @@ -1,6 +0,0 @@ -//@ only-x86_64 - -#[target_feature(enable = "sse2")] //~ ERROR can only be applied to `unsafe` functions -fn foo() {} - -fn main() {} diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/feature-gate-target_feature_11.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/feature-gate-target_feature_11.stderr deleted file mode 100644 index 4f1994d56fd42..0000000000000 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/feature-gate-target_feature_11.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions - --> $DIR/feature-gate-target_feature_11.rs:3:1 - | -LL | #[target_feature(enable = "sse2")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | fn foo() {} - | -------- not an `unsafe` function - | - = note: see issue #69098 for more information - = help: add `#![feature(target_feature_11)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.rs index 364b4d3581276..43a5a2f16aac1 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.rs +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.rs @@ -1,10 +1,22 @@ //@ only-x86_64 -#![feature(target_feature_11)] +#[target_feature(enable = "avx")] +fn foo_avx() {} #[target_feature(enable = "sse2")] fn foo() {} +#[target_feature(enable = "sse2")] +fn bar() { + let foo: fn() = foo; // this is OK, as we have the necessary target features. + let foo: fn() = foo_avx; //~ ERROR mismatched types +} + fn main() { + if std::is_x86_feature_detected!("sse2") { + unsafe { + bar(); + } + } let foo: fn() = foo; //~ ERROR mismatched types } diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.stderr index cc941be5479e3..5bfbc236bc4e3 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.stderr +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-ptr.stderr @@ -1,5 +1,20 @@ error[E0308]: mismatched types - --> $DIR/fn-ptr.rs:9:21 + --> $DIR/fn-ptr.rs:12:21 + | +LL | #[target_feature(enable = "avx")] + | --------------------------------- `#[target_feature]` added here +... +LL | let foo: fn() = foo_avx; + | ---- ^^^^^^^ cannot coerce functions with `#[target_feature]` to safe function pointers + | | + | expected due to this + | + = note: expected fn pointer `fn()` + found fn item `#[target_features] fn() {foo_avx}` + = note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers + +error[E0308]: mismatched types + --> $DIR/fn-ptr.rs:21:21 | LL | #[target_feature(enable = "sse2")] | ---------------------------------- `#[target_feature]` added here @@ -10,14 +25,9 @@ LL | let foo: fn() = foo; | expected due to this | = note: expected fn pointer `fn()` - found fn item `fn() {foo}` - = note: fn items are distinct from fn pointers + found fn item `#[target_features] fn() {foo}` = note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers -help: consider casting to a fn pointer - | -LL | let foo: fn() = foo as fn(); - | ~~~~~~~~~~~ -error: aborting due to 1 previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs index 3c370a1b8f321..82053a12b133f 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs @@ -1,10 +1,11 @@ //@ only-x86_64 -#![feature(target_feature_11)] - #[target_feature(enable = "avx")] fn foo() {} +#[target_feature(enable = "avx")] +fn bar(arg: i32) {} + #[target_feature(enable = "avx")] unsafe fn foo_unsafe() {} @@ -20,10 +21,15 @@ fn call_once(f: impl FnOnce()) { f() } +fn call_once_i32(f: impl FnOnce(i32)) { + f(0) +} + fn main() { - call(foo); //~ ERROR expected a `Fn()` closure, found `fn() {foo}` - call_mut(foo); //~ ERROR expected a `FnMut()` closure, found `fn() {foo}` - call_once(foo); //~ ERROR expected a `FnOnce()` closure, found `fn() {foo}` + call(foo); //~ ERROR expected a `Fn()` closure, found `#[target_features] fn() {foo}` + call_mut(foo); //~ ERROR expected a `FnMut()` closure, found `#[target_features] fn() {foo}` + call_once(foo); //~ ERROR expected a `FnOnce()` closure, found `#[target_features] fn() {foo}` + call_once_i32(bar); //~ ERROR expected a `FnOnce(i32)` closure, found `#[target_features] fn(i32) {bar}` call(foo_unsafe); //~^ ERROR expected a `Fn()` closure, found `unsafe fn() {foo_unsafe}` diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr index 4c07f4d6b990d..1c6e3905abb11 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr @@ -1,56 +1,76 @@ -error[E0277]: expected a `Fn()` closure, found `fn() {foo}` - --> $DIR/fn-traits.rs:24:10 +error[E0277]: expected a `Fn()` closure, found `#[target_features] fn() {foo}` + --> $DIR/fn-traits.rs:29:10 | LL | call(foo); - | ---- ^^^ expected an `Fn()` closure, found `fn() {foo}` + | ---- ^^^ expected an `Fn()` closure, found `#[target_features] fn() {foo}` | | | required by a bound introduced by this call | - = help: the trait `Fn()` is not implemented for fn item `fn() {foo}` - = note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ }` + = help: the trait `Fn()` is not implemented for fn item `#[target_features] fn() {foo}` + = note: wrap the `#[target_features] fn() {foo}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits + = note: try casting the function to a `fn` pointer or wrapping it in a closure note: required by a bound in `call` - --> $DIR/fn-traits.rs:11:17 + --> $DIR/fn-traits.rs:12:17 | LL | fn call(f: impl Fn()) { | ^^^^ required by this bound in `call` -error[E0277]: expected a `FnMut()` closure, found `fn() {foo}` - --> $DIR/fn-traits.rs:25:14 +error[E0277]: expected a `FnMut()` closure, found `#[target_features] fn() {foo}` + --> $DIR/fn-traits.rs:30:14 | LL | call_mut(foo); - | -------- ^^^ expected an `FnMut()` closure, found `fn() {foo}` + | -------- ^^^ expected an `FnMut()` closure, found `#[target_features] fn() {foo}` | | | required by a bound introduced by this call | - = help: the trait `FnMut()` is not implemented for fn item `fn() {foo}` - = note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ }` + = help: the trait `FnMut()` is not implemented for fn item `#[target_features] fn() {foo}` + = note: wrap the `#[target_features] fn() {foo}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits + = note: try casting the function to a `fn` pointer or wrapping it in a closure note: required by a bound in `call_mut` - --> $DIR/fn-traits.rs:15:25 + --> $DIR/fn-traits.rs:16:25 | LL | fn call_mut(mut f: impl FnMut()) { | ^^^^^^^ required by this bound in `call_mut` -error[E0277]: expected a `FnOnce()` closure, found `fn() {foo}` - --> $DIR/fn-traits.rs:26:15 +error[E0277]: expected a `FnOnce()` closure, found `#[target_features] fn() {foo}` + --> $DIR/fn-traits.rs:31:15 | LL | call_once(foo); - | --------- ^^^ expected an `FnOnce()` closure, found `fn() {foo}` + | --------- ^^^ expected an `FnOnce()` closure, found `#[target_features] fn() {foo}` | | | required by a bound introduced by this call | - = help: the trait `FnOnce()` is not implemented for fn item `fn() {foo}` - = note: wrap the `fn() {foo}` in a closure with no arguments: `|| { /* code */ }` + = help: the trait `FnOnce()` is not implemented for fn item `#[target_features] fn() {foo}` + = note: wrap the `#[target_features] fn() {foo}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits + = note: try casting the function to a `fn` pointer or wrapping it in a closure note: required by a bound in `call_once` - --> $DIR/fn-traits.rs:19:22 + --> $DIR/fn-traits.rs:20:22 | LL | fn call_once(f: impl FnOnce()) { | ^^^^^^^^ required by this bound in `call_once` +error[E0277]: expected a `FnOnce(i32)` closure, found `#[target_features] fn(i32) {bar}` + --> $DIR/fn-traits.rs:32:19 + | +LL | call_once_i32(bar); + | ------------- ^^^ expected an `FnOnce(i32)` closure, found `#[target_features] fn(i32) {bar}` + | | + | required by a bound introduced by this call + | + = help: the trait `FnOnce(i32)` is not implemented for fn item `#[target_features] fn(i32) {bar}` + = note: `#[target_feature]` functions do not implement the `Fn` traits + = note: try casting the function to a `fn` pointer or wrapping it in a closure +note: required by a bound in `call_once_i32` + --> $DIR/fn-traits.rs:24:26 + | +LL | fn call_once_i32(f: impl FnOnce(i32)) { + | ^^^^^^^^^^^ required by this bound in `call_once_i32` + error[E0277]: expected a `Fn()` closure, found `unsafe fn() {foo_unsafe}` - --> $DIR/fn-traits.rs:28:10 + --> $DIR/fn-traits.rs:34:10 | LL | call(foo_unsafe); | ---- ^^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }` @@ -61,14 +81,15 @@ LL | call(foo_unsafe); = note: unsafe function cannot be called generically without an unsafe block = note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits + = note: try casting the function to a `fn` pointer or wrapping it in a closure note: required by a bound in `call` - --> $DIR/fn-traits.rs:11:17 + --> $DIR/fn-traits.rs:12:17 | LL | fn call(f: impl Fn()) { | ^^^^ required by this bound in `call` error[E0277]: expected a `FnMut()` closure, found `unsafe fn() {foo_unsafe}` - --> $DIR/fn-traits.rs:30:14 + --> $DIR/fn-traits.rs:36:14 | LL | call_mut(foo_unsafe); | -------- ^^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }` @@ -79,14 +100,15 @@ LL | call_mut(foo_unsafe); = note: unsafe function cannot be called generically without an unsafe block = note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits + = note: try casting the function to a `fn` pointer or wrapping it in a closure note: required by a bound in `call_mut` - --> $DIR/fn-traits.rs:15:25 + --> $DIR/fn-traits.rs:16:25 | LL | fn call_mut(mut f: impl FnMut()) { | ^^^^^^^ required by this bound in `call_mut` error[E0277]: expected a `FnOnce()` closure, found `unsafe fn() {foo_unsafe}` - --> $DIR/fn-traits.rs:32:15 + --> $DIR/fn-traits.rs:38:15 | LL | call_once(foo_unsafe); | --------- ^^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }` @@ -97,12 +119,13 @@ LL | call_once(foo_unsafe); = note: unsafe function cannot be called generically without an unsafe block = note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits + = note: try casting the function to a `fn` pointer or wrapping it in a closure note: required by a bound in `call_once` - --> $DIR/fn-traits.rs:19:22 + --> $DIR/fn-traits.rs:20:22 | LL | fn call_once(f: impl FnOnce()) { | ^^^^^^^^ required by this bound in `call_once` -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.rs index a1c118478677b..6df89ee97ddf8 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.rs +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.rs @@ -1,7 +1,5 @@ //@ only-x86_64 -#![feature(target_feature_11)] - #[target_feature(enable = "avx2")] fn main() {} //~^ ERROR `main` function is not allowed to have `#[target_feature]` diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.stderr index 57ad1cc8d0802..15f99512f1747 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.stderr +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-main.stderr @@ -1,5 +1,5 @@ error: `main` function is not allowed to have `#[target_feature]` - --> $DIR/issue-108645-target-feature-on-main.rs:6:1 + --> $DIR/issue-108645-target-feature-on-main.rs:4:1 | LL | fn main() {} | ^^^^^^^^^ `main` function is not allowed to have `#[target_feature]` diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.rs deleted file mode 100644 index 6aa8f6fd821ed..0000000000000 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ only-x86_64 - -#![feature(start)] -#![feature(target_feature_11)] - -#[start] -#[target_feature(enable = "avx2")] -//~^ ERROR `#[start]` function is not allowed to have `#[target_feature]` -fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 } diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.stderr deleted file mode 100644 index d0a67c4f6a8d0..0000000000000 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108645-target-feature-on-start.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: `#[start]` function is not allowed to have `#[target_feature]` - --> $DIR/issue-108645-target-feature-on-start.rs:7:1 - | -LL | #[target_feature(enable = "avx2")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | -LL | fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 } - | -------------------------------------------------------- `#[start]` function is not allowed to have `#[target_feature]` - -error: aborting due to 1 previous error - diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108655-inline-always-closure.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108655-inline-always-closure.rs index 6bd810b0956d8..bf6dba6702c9e 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108655-inline-always-closure.rs +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-108655-inline-always-closure.rs @@ -3,8 +3,6 @@ //@ check-pass //@ only-x86_64 -#![feature(target_feature_11)] - #[target_feature(enable = "avx")] pub unsafe fn test() { ({ diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/issue-99876.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-99876.rs index 57dac2e4adb76..141d07876d4ca 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/issue-99876.rs +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/issue-99876.rs @@ -1,7 +1,5 @@ //@ check-pass -#![feature(target_feature_11)] - struct S(T) where [T; (|| {}, 1).1]: Copy; diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/return-fn-ptr.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/return-fn-ptr.rs new file mode 100644 index 0000000000000..5b306438fb083 --- /dev/null +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/return-fn-ptr.rs @@ -0,0 +1,20 @@ +//@ only-x86_64 +//@ run-pass + +#[target_feature(enable = "sse2")] +fn foo() -> bool { + true +} + +#[target_feature(enable = "sse2")] +fn bar() -> fn() -> bool { + foo +} + +fn main() { + if !std::is_x86_feature_detected!("sse2") { + return; + } + let f = unsafe { bar() }; + assert!(f()); +} diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs index fec4e75290fc8..35bf6c110115e 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.rs @@ -2,8 +2,6 @@ // Set the base cpu explicitly, in case the default has been changed. //@ compile-flags: -C target-cpu=x86-64 -#![feature(target_feature_11)] - #[target_feature(enable = "sse2")] const fn sse2() {} diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr index 901bf640845dd..ea4626092340b 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr @@ -1,5 +1,5 @@ error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:27:5 + --> $DIR/safe-calls.rs:25:5 | LL | sse2(); | ^^^^^^ call to function with `#[target_feature]` @@ -8,7 +8,7 @@ LL | sse2(); = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:29:5 + --> $DIR/safe-calls.rs:27:5 | LL | avx_bmi2(); | ^^^^^^^^^^ call to function with `#[target_feature]` @@ -16,7 +16,7 @@ LL | avx_bmi2(); = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:31:5 + --> $DIR/safe-calls.rs:29:5 | LL | Quux.avx_bmi2(); | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` @@ -24,7 +24,7 @@ LL | Quux.avx_bmi2(); = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:38:5 + --> $DIR/safe-calls.rs:36:5 | LL | avx_bmi2(); | ^^^^^^^^^^ call to function with `#[target_feature]` @@ -32,7 +32,7 @@ LL | avx_bmi2(); = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:40:5 + --> $DIR/safe-calls.rs:38:5 | LL | Quux.avx_bmi2(); | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` @@ -40,7 +40,7 @@ LL | Quux.avx_bmi2(); = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:47:5 + --> $DIR/safe-calls.rs:45:5 | LL | avx_bmi2(); | ^^^^^^^^^^ call to function with `#[target_feature]` @@ -48,7 +48,7 @@ LL | avx_bmi2(); = help: in order for the call to be safe, the context requires the following additional target feature: bmi2 error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:49:5 + --> $DIR/safe-calls.rs:47:5 | LL | Quux.avx_bmi2(); | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` @@ -56,7 +56,7 @@ LL | Quux.avx_bmi2(); = help: in order for the call to be safe, the context requires the following additional target feature: bmi2 error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:61:15 + --> $DIR/safe-calls.rs:59:15 | LL | const _: () = sse2(); | ^^^^^^ call to function with `#[target_feature]` @@ -65,7 +65,7 @@ LL | const _: () = sse2(); = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` error[E0133]: call to function `sse2_and_fxsr` with `#[target_feature]` is unsafe and requires unsafe function or block - --> $DIR/safe-calls.rs:64:15 + --> $DIR/safe-calls.rs:62:15 | LL | const _: () = sse2_and_fxsr(); | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` @@ -74,7 +74,7 @@ LL | const _: () = sse2_and_fxsr(); = note: the fxsr and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe block - --> $DIR/safe-calls.rs:69:5 + --> $DIR/safe-calls.rs:67:5 | LL | sse2(); | ^^^^^^ call to function with `#[target_feature]` @@ -83,12 +83,12 @@ LL | sse2(); = help: in order for the call to be safe, the context requires the following additional target feature: sse2 = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` note: an unsafe function restricts its caller, but its body is safe by default - --> $DIR/safe-calls.rs:68:1 + --> $DIR/safe-calls.rs:66:1 | LL | unsafe fn needs_unsafe_block() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/safe-calls.rs:67:8 + --> $DIR/safe-calls.rs:65:8 | LL | #[deny(unsafe_op_in_unsafe_fn)] | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs index df575b0f6b636..bb1dd3b503039 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.rs @@ -1,7 +1,5 @@ //@ only-x86_64 -#![feature(target_feature_11)] - trait Foo { fn foo(&self); unsafe fn unsf_foo(&self); @@ -13,6 +11,7 @@ impl Foo for Bar { #[target_feature(enable = "sse2")] //~^ ERROR cannot be applied to safe trait method fn foo(&self) {} + //~^ ERROR method `foo` has an incompatible type for trait #[target_feature(enable = "sse2")] unsafe fn unsf_foo(&self) {} diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr index 00efbb52f159b..f2ef2b2f3b86b 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/trait-impl.stderr @@ -1,5 +1,5 @@ error: `#[target_feature(..)]` cannot be applied to safe trait method - --> $DIR/trait-impl.rs:13:5 + --> $DIR/trait-impl.rs:11:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be applied to safe trait method @@ -7,8 +7,22 @@ LL | LL | fn foo(&self) {} | ------------- not an `unsafe` function +error[E0053]: method `foo` has an incompatible type for trait + --> $DIR/trait-impl.rs:13:5 + | +LL | fn foo(&self) {} + | ^^^^^^^^^^^^^ expected safe fn, found unsafe fn + | +note: type in trait + --> $DIR/trait-impl.rs:4:5 + | +LL | fn foo(&self); + | ^^^^^^^^^^^^^^ + = note: expected signature `fn(&Bar)` + found signature `#[target_features] fn(&Bar)` + error: `#[target_feature(..)]` cannot be applied to safe trait method - --> $DIR/trait-impl.rs:22:5 + --> $DIR/trait-impl.rs:21:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be applied to safe trait method @@ -16,5 +30,6 @@ LL | LL | fn foo(&self) {} | ------------- not an `unsafe` function -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0053`. diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nofeature.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nofeature.stderr deleted file mode 100644 index f556ecf7f91d3..0000000000000 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nofeature.stderr +++ /dev/null @@ -1,1111 +0,0 @@ -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:31:9 - | -LL | if (let 0 = 1) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:31:9 - | -LL | if (let 0 = 1) {} - | ^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:34:11 - | -LL | if (((let 0 = 1))) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:34:11 - | -LL | if (((let 0 = 1))) {} - | ^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:37:9 - | -LL | if (let 0 = 1) && true {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:37:9 - | -LL | if (let 0 = 1) && true {} - | ^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:40:17 - | -LL | if true && (let 0 = 1) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:40:17 - | -LL | if true && (let 0 = 1) {} - | ^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:43:9 - | -LL | if (let 0 = 1) && (let 0 = 1) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:43:9 - | -LL | if (let 0 = 1) && (let 0 = 1) {} - | ^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:43:24 - | -LL | if (let 0 = 1) && (let 0 = 1) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:43:24 - | -LL | if (let 0 = 1) && (let 0 = 1) {} - | ^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:47:35 - | -LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:47:35 - | -LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:47:48 - | -LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:47:35 - | -LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:47:61 - | -LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:47:35 - | -LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:56:12 - | -LL | while (let 0 = 1) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:56:12 - | -LL | while (let 0 = 1) {} - | ^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:59:14 - | -LL | while (((let 0 = 1))) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:59:14 - | -LL | while (((let 0 = 1))) {} - | ^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:62:12 - | -LL | while (let 0 = 1) && true {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:62:12 - | -LL | while (let 0 = 1) && true {} - | ^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:65:20 - | -LL | while true && (let 0 = 1) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:65:20 - | -LL | while true && (let 0 = 1) {} - | ^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:68:12 - | -LL | while (let 0 = 1) && (let 0 = 1) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:68:12 - | -LL | while (let 0 = 1) && (let 0 = 1) {} - | ^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:68:27 - | -LL | while (let 0 = 1) && (let 0 = 1) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:68:27 - | -LL | while (let 0 = 1) && (let 0 = 1) {} - | ^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:72:38 - | -LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:72:38 - | -LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:72:51 - | -LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:72:38 - | -LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:72:64 - | -LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:72:38 - | -LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:95:9 - | -LL | if &let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:98:9 - | -LL | if !let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:100:9 - | -LL | if *let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:102:9 - | -LL | if -let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:110:9 - | -LL | if (let 0 = 0)? {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:113:16 - | -LL | if true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions - --> $DIR/disallowed-positions.rs:113:13 - | -LL | if true || let 0 = 0 {} - | ^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:115:17 - | -LL | if (true || let 0 = 0) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:117:25 - | -LL | if true && (true || let 0 = 0) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:119:25 - | -LL | if true || (true && let 0 = 0) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:123:12 - | -LL | if x = let 0 = 0 {} - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:126:15 - | -LL | if true..(let 0 = 0) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:129:11 - | -LL | if ..(let 0 = 0) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:131:9 - | -LL | if (let 0 = 0).. {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:135:8 - | -LL | if let Range { start: _, end: _ } = true..true && false {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:138:8 - | -LL | if let Range { start: _, end: _ } = true..true || false {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:144:8 - | -LL | if let Range { start: F, end } = F..|| true {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:150:8 - | -LL | if let Range { start: true, end } = t..&&false {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:154:19 - | -LL | if let true = let true = true {} - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:160:12 - | -LL | while &let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:163:12 - | -LL | while !let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:165:12 - | -LL | while *let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:167:12 - | -LL | while -let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:175:12 - | -LL | while (let 0 = 0)? {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:178:19 - | -LL | while true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions - --> $DIR/disallowed-positions.rs:178:16 - | -LL | while true || let 0 = 0 {} - | ^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:180:20 - | -LL | while (true || let 0 = 0) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:182:28 - | -LL | while true && (true || let 0 = 0) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:184:28 - | -LL | while true || (true && let 0 = 0) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:188:15 - | -LL | while x = let 0 = 0 {} - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:191:18 - | -LL | while true..(let 0 = 0) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:194:14 - | -LL | while ..(let 0 = 0) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:196:12 - | -LL | while (let 0 = 0).. {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:200:11 - | -LL | while let Range { start: _, end: _ } = true..true && false {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:203:11 - | -LL | while let Range { start: _, end: _ } = true..true || false {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:209:11 - | -LL | while let Range { start: F, end } = F..|| true {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:215:11 - | -LL | while let Range { start: true, end } = t..&&false {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:219:22 - | -LL | while let true = let true = true {} - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:236:6 - | -LL | &let 0 = 0; - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:239:6 - | -LL | !let 0 = 0; - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:241:6 - | -LL | *let 0 = 0; - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:243:6 - | -LL | -let 0 = 0; - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:245:13 - | -LL | let _ = let _ = 3; - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:253:6 - | -LL | (let 0 = 0)?; - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:256:13 - | -LL | true || let 0 = 0; - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:258:14 - | -LL | (true || let 0 = 0); - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:260:22 - | -LL | true && (true || let 0 = 0); - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:264:9 - | -LL | x = let 0 = 0; - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:267:12 - | -LL | true..(let 0 = 0); - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:269:8 - | -LL | ..(let 0 = 0); - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:271:6 - | -LL | (let 0 = 0)..; - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:274:6 - | -LL | (let Range { start: _, end: _ } = true..true || false); - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:278:6 - | -LL | (let true = let true = true); - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:278:17 - | -LL | (let true = let true = true); - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:284:25 - | -LL | let x = true && let y = 1; - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:290:19 - | -LL | [1, 2, 3][let _ = ()] - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:295:6 - | -LL | &let 0 = 0 - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:306:17 - | -LL | true && let 1 = 1 - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:311:17 - | -LL | true && let 1 = 1 - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:316:17 - | -LL | true && let 1 = 1 - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:327:17 - | -LL | true && let 1 = 1 - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expressions must be enclosed in braces to be used as const generic arguments - --> $DIR/disallowed-positions.rs:327:9 - | -LL | true && let 1 = 1 - | ^^^^^^^^^^^^^^^^^ - | -help: enclose the `const` expression in braces - | -LL | { true && let 1 = 1 } - | + + - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:337:9 - | -LL | if (let Some(a) = opt && true) { - | ^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:337:9 - | -LL | if (let Some(a) = opt && true) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:341:9 - | -LL | if (let Some(a) = opt) && true { - | ^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:341:9 - | -LL | if (let Some(a) = opt) && true { - | ^^^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:344:9 - | -LL | if (let Some(a) = opt) && (let Some(b) = a) { - | ^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:344:9 - | -LL | if (let Some(a) = opt) && (let Some(b) = a) { - | ^^^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:344:32 - | -LL | if (let Some(a) = opt) && (let Some(b) = a) { - | ^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:344:32 - | -LL | if (let Some(a) = opt) && (let Some(b) = a) { - | ^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:351:9 - | -LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { - | ^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:351:9 - | -LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:351:31 - | -LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { - | ^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:351:31 - | -LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { - | ^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:355:9 - | -LL | if (let Some(a) = opt && (let Some(b) = a)) && true { - | ^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:355:9 - | -LL | if (let Some(a) = opt && (let Some(b) = a)) && true { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:355:31 - | -LL | if (let Some(a) = opt && (let Some(b) = a)) && true { - | ^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:355:31 - | -LL | if (let Some(a) = opt && (let Some(b) = a)) && true { - | ^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:359:9 - | -LL | if (let Some(a) = opt && (true)) && true { - | ^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:359:9 - | -LL | if (let Some(a) = opt && (true)) && true { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:375:22 - | -LL | let x = (true && let y = 1); - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:380:20 - | -LL | ([1, 2, 3][let _ = ()]) - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:87:16 - | -LL | use_expr!((let 0 = 1 && 0 == 0)); - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:89:16 - | -LL | use_expr!((let 0 = 1)); - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/disallowed-positions.rs:47:8 - | -LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/disallowed-positions.rs:47:21 - | -LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/disallowed-positions.rs:72:11 - | -LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/disallowed-positions.rs:72:24 - | -LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/disallowed-positions.rs:348:8 - | -LL | if let Some(a) = opt && (true && true) { - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/disallowed-positions.rs:363:28 - | -LL | if (true && (true)) && let Some(a) = opt { - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/disallowed-positions.rs:365:18 - | -LL | if (true) && let Some(a) = opt { - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/disallowed-positions.rs:367:16 - | -LL | if true && let Some(a) = opt { - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/disallowed-positions.rs:371:8 - | -LL | if let true = (true && fun()) && (true) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:126:8 - | -LL | if true..(let 0 = 0) {} - | ^^^^^^^^^^^^^^^^^ expected `bool`, found `Range` - | - = note: expected type `bool` - found struct `std::ops::Range` - -error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:135:12 - | -LL | if let Range { start: _, end: _ } = true..true && false {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` - | | - | expected `bool`, found `Range<_>` - | - = note: expected type `bool` - found struct `std::ops::Range<_>` - -error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:138:12 - | -LL | if let Range { start: _, end: _ } = true..true || false {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` - | | - | expected `bool`, found `Range<_>` - | - = note: expected type `bool` - found struct `std::ops::Range<_>` - -error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:144:12 - | -LL | if let Range { start: F, end } = F..|| true {} - | ^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `fn() -> bool` - | | - | expected fn pointer, found `Range<_>` - | - = note: expected fn pointer `fn() -> bool` - found struct `std::ops::Range<_>` - -error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:150:12 - | -LL | if let Range { start: true, end } = t..&&false {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `&&bool` - | | - | expected `bool`, found `Range<_>` - | - = note: expected type `bool` - found struct `std::ops::Range<_>` - -error[E0277]: the `?` operator can only be applied to values that implement `Try` - --> $DIR/disallowed-positions.rs:106:20 - | -LL | if let 0 = 0? {} - | ^^ the `?` operator cannot be applied to type `{integer}` - | - = help: the trait `Try` is not implemented for `{integer}` - -error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:191:11 - | -LL | while true..(let 0 = 0) {} - | ^^^^^^^^^^^^^^^^^ expected `bool`, found `Range` - | - = note: expected type `bool` - found struct `std::ops::Range` - -error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:200:15 - | -LL | while let Range { start: _, end: _ } = true..true && false {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` - | | - | expected `bool`, found `Range<_>` - | - = note: expected type `bool` - found struct `std::ops::Range<_>` - -error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:203:15 - | -LL | while let Range { start: _, end: _ } = true..true || false {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` - | | - | expected `bool`, found `Range<_>` - | - = note: expected type `bool` - found struct `std::ops::Range<_>` - -error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:209:15 - | -LL | while let Range { start: F, end } = F..|| true {} - | ^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `fn() -> bool` - | | - | expected fn pointer, found `Range<_>` - | - = note: expected fn pointer `fn() -> bool` - found struct `std::ops::Range<_>` - -error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:215:15 - | -LL | while let Range { start: true, end } = t..&&false {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `&&bool` - | | - | expected `bool`, found `Range<_>` - | - = note: expected type `bool` - found struct `std::ops::Range<_>` - -error[E0277]: the `?` operator can only be applied to values that implement `Try` - --> $DIR/disallowed-positions.rs:171:23 - | -LL | while let 0 = 0? {} - | ^^ the `?` operator cannot be applied to type `{integer}` - | - = help: the trait `Try` is not implemented for `{integer}` - -error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:274:10 - | -LL | (let Range { start: _, end: _ } = true..true || false); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` - | | - | expected `bool`, found `Range<_>` - | - = note: expected type `bool` - found struct `std::ops::Range<_>` - -error[E0277]: the `?` operator can only be applied to values that implement `Try` - --> $DIR/disallowed-positions.rs:249:17 - | -LL | let 0 = 0?; - | ^^ the `?` operator cannot be applied to type `{integer}` - | - = help: the trait `Try` is not implemented for `{integer}` - -error: aborting due to 114 previous errors - -Some errors have detailed explanations: E0277, E0308, E0658. -For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.rs index f33a3d62e2688..5982c771033d0 100644 --- a/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.rs +++ b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.rs @@ -11,7 +11,7 @@ fn test() {} static mut imported_val: i32 = 123; #[link(name = "exporter", kind = "raw-dylib")] -extern { +extern "C" { #[link_ordinal(13)] fn imported_function(); diff --git a/tests/ui/rmeta/emit-artifact-notifications.polonius.stderr b/tests/ui/rmeta/emit-artifact-notifications.polonius.stderr deleted file mode 100644 index 255c7b370f9fd..0000000000000 --- a/tests/ui/rmeta/emit-artifact-notifications.polonius.stderr +++ /dev/null @@ -1 +0,0 @@ -{"artifact":"$TEST_BUILD_DIR/rmeta/emit-artifact-notifications.polonius/libemit_artifact_notifications.rmeta","emit":"metadata"} diff --git a/tests/ui/rmeta/rmeta_meta_main.stderr b/tests/ui/rmeta/rmeta_meta_main.stderr index af11c88d9282e..7ee44ce29b295 100644 --- a/tests/ui/rmeta/rmeta_meta_main.stderr +++ b/tests/ui/rmeta/rmeta_meta_main.stderr @@ -6,8 +6,9 @@ LL | let _ = Foo { field2: 42 }; | help: a field with a similar name exists | -LL | let _ = Foo { field: 42 }; - | ~~~~~ +LL - let _ = Foo { field2: 42 }; +LL + let _ = Foo { field: 42 }; + | error: aborting due to 1 previous error diff --git a/tests/ui/runtime/atomic-print.rs b/tests/ui/runtime/atomic-print.rs index 7352058973637..6b899d675a215 100644 --- a/tests/ui/runtime/atomic-print.rs +++ b/tests/ui/runtime/atomic-print.rs @@ -2,8 +2,8 @@ #![allow(unused_must_use)] #![allow(deprecated)] -//@ ignore-wasm32 no processes or threads -//@ ignore-sgx no processes +//@ needs-threads +//@ needs-subprocess use std::{env, fmt, process, sync, thread}; diff --git a/tests/ui/runtime/backtrace-debuginfo.rs b/tests/ui/runtime/backtrace-debuginfo.rs index da747ded44ff2..afc96d6bb5f54 100644 --- a/tests/ui/runtime/backtrace-debuginfo.rs +++ b/tests/ui/runtime/backtrace-debuginfo.rs @@ -9,8 +9,7 @@ //@ compile-flags:-g -Copt-level=0 -Cllvm-args=-enable-tail-merge=0 //@ compile-flags:-Cforce-frame-pointers=yes //@ compile-flags:-Cstrip=none -//@ ignore-wasm32 spawning processes is not supported -//@ ignore-sgx no processes +//@ needs-subprocess //@ ignore-fuchsia Backtrace not symbolized, trace different line alignment // FIXME(#117097): backtrace (possibly unwinding mechanism) seems to be different on at least diff --git a/tests/ui/runtime/native-print-no-runtime.rs b/tests/ui/runtime/native-print-no-runtime.rs deleted file mode 100644 index f0ed7d97b2caf..0000000000000 --- a/tests/ui/runtime/native-print-no-runtime.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ run-pass - -#![feature(start)] - -#[start] -pub fn main(_: isize, _: *const *const u8) -> isize { - println!("hello"); - 0 -} diff --git a/tests/ui/runtime/on-broken-pipe/auxiliary/assert-sigpipe-disposition.rs b/tests/ui/runtime/on-broken-pipe/auxiliary/assert-sigpipe-disposition.rs index 229408fb72474..f3f9ce0bd879e 100644 --- a/tests/ui/runtime/on-broken-pipe/auxiliary/assert-sigpipe-disposition.rs +++ b/tests/ui/runtime/on-broken-pipe/auxiliary/assert-sigpipe-disposition.rs @@ -4,15 +4,16 @@ //@ compile-flags: -Cpanic=abort //@ no-prefer-dynamic so panic=abort works -#![feature(start, rustc_private)] +#![feature(rustc_private)] +#![no_main] extern crate libc; -// Use #[start] so we don't have a runtime that messes with SIGPIPE. -#[start] -fn start(argc: isize, argv: *const *const u8) -> isize { +// Use no_main so we don't have a runtime that messes with SIGPIPE. +#[no_mangle] +extern "C" fn main(argc: core::ffi::c_int, argv: *const *const u8) -> core::ffi::c_int { assert_eq!(argc, 2, "Must pass SIG_IGN or SIG_DFL as first arg"); - let arg1 = unsafe { std::ffi::CStr::from_ptr(*argv.offset(1) as *const libc::c_char) } + let arg1 = unsafe { core::ffi::CStr::from_ptr(*argv.offset(1) as *const libc::c_char) } .to_str() .unwrap(); @@ -23,8 +24,8 @@ fn start(argc: isize, argv: *const *const u8) -> isize { }; let actual = unsafe { - let mut actual: libc::sigaction = std::mem::zeroed(); - libc::sigaction(libc::SIGPIPE, std::ptr::null(), &mut actual); + let mut actual: libc::sigaction = core::mem::zeroed(); + libc::sigaction(libc::SIGPIPE, core::ptr::null(), &mut actual); #[cfg(not(target_os = "aix"))] { actual.sa_sigaction diff --git a/tests/ui/runtime/out-of-stack.rs b/tests/ui/runtime/out-of-stack.rs index c5300635ad924..6be34afb56035 100644 --- a/tests/ui/runtime/out-of-stack.rs +++ b/tests/ui/runtime/out-of-stack.rs @@ -3,8 +3,7 @@ #![allow(unused_must_use)] #![allow(unconditional_recursion)] //@ ignore-android: FIXME (#20004) -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess //@ ignore-fuchsia must translate zircon signal to SIGABRT, FIXME (#58590) //@ ignore-nto no stack overflow handler used (no alternate stack available) //@ ignore-ios stack overflow handlers aren't enabled diff --git a/tests/ui/runtime/rt-explody-panic-payloads.rs b/tests/ui/runtime/rt-explody-panic-payloads.rs index bd3624a8aee3d..c177fd260ed4f 100644 --- a/tests/ui/runtime/rt-explody-panic-payloads.rs +++ b/tests/ui/runtime/rt-explody-panic-payloads.rs @@ -1,7 +1,6 @@ //@ run-pass //@ needs-unwind -//@ ignore-emscripten no processes -//@ ignore-sgx no processes +//@ needs-subprocess use std::env; use std::process::Command; diff --git a/tests/ui/runtime/running-with-no-runtime.rs b/tests/ui/runtime/running-with-no-runtime.rs index 695025b385906..7ac0dd912dc0d 100644 --- a/tests/ui/runtime/running-with-no-runtime.rs +++ b/tests/ui/runtime/running-with-no-runtime.rs @@ -1,16 +1,15 @@ //@ run-pass -//@ ignore-wasm32 spawning processes is not supported -//@ ignore-sgx no processes +//@ needs-subprocess -#![feature(start)] +#![no_main] use std::ffi::CStr; use std::process::{Command, Output}; use std::panic; use std::str; -#[start] -fn start(argc: isize, argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: core::ffi::c_int, argv: *const *const u8) -> core::ffi::c_int { if argc > 1 { unsafe { match **argv.offset(1) as char { diff --git a/tests/ui/rust-2018/remove-extern-crate.stderr b/tests/ui/rust-2018/remove-extern-crate.stderr index 020db9975c092..cb090c621e9f9 100644 --- a/tests/ui/rust-2018/remove-extern-crate.stderr +++ b/tests/ui/rust-2018/remove-extern-crate.stderr @@ -19,8 +19,9 @@ LL | extern crate core; | help: convert it to a `use` | -LL | use core; - | ~~~ +LL - extern crate core; +LL + use core; + | warning: `extern crate` is not idiomatic in the new edition --> $DIR/remove-extern-crate.rs:45:5 @@ -30,8 +31,9 @@ LL | pub extern crate core; | help: convert it to a `use` | -LL | pub use core; - | ~~~ +LL - pub extern crate core; +LL + pub use core; + | warning: 3 warnings emitted diff --git a/tests/ui/rust-2018/trait-import-suggestions.stderr b/tests/ui/rust-2018/trait-import-suggestions.stderr index 852628885794a..488044ee85245 100644 --- a/tests/ui/rust-2018/trait-import-suggestions.stderr +++ b/tests/ui/rust-2018/trait-import-suggestions.stderr @@ -14,8 +14,9 @@ LL + use crate::foo::foobar::Foobar; | help: there is a method `bar` with a similar name | -LL | x.bar(); - | ~~~ +LL - x.foobar(); +LL + x.bar(); + | error[E0599]: no method named `bar` found for type `u32` in the current scope --> $DIR/trait-import-suggestions.rs:28:7 @@ -34,7 +35,7 @@ LL + use crate::foo::Bar; help: there is a method `foobar` with a similar name | LL | x.foobar(); - | ~~~~~~ + | +++ error[E0599]: no method named `baz` found for type `u32` in the current scope --> $DIR/trait-import-suggestions.rs:29:7 @@ -44,8 +45,9 @@ LL | x.baz(); | help: there is a method `bar` with a similar name | -LL | x.bar(); - | ~~~ +LL - x.baz(); +LL + x.bar(); + | error[E0599]: no function or associated item named `from_str` found for type `u32` in the current scope --> $DIR/trait-import-suggestions.rs:30:18 @@ -60,8 +62,9 @@ LL + use std::str::FromStr; | help: there is an associated function `from` with a similar name | -LL | let y = u32::from("33"); - | ~~~~ +LL - let y = u32::from_str("33"); +LL + let y = u32::from("33"); + | error: aborting due to 4 previous errors diff --git a/tests/ui/rust-2018/unresolved-asterisk-imports.stderr b/tests/ui/rust-2018/unresolved-asterisk-imports.stderr index b6bf109824f70..049d52893d4b7 100644 --- a/tests/ui/rust-2018/unresolved-asterisk-imports.stderr +++ b/tests/ui/rust-2018/unresolved-asterisk-imports.stderr @@ -2,7 +2,9 @@ error[E0432]: unresolved import `not_existing_crate` --> $DIR/unresolved-asterisk-imports.rs:3:5 | LL | use not_existing_crate::*; - | ^^^^^^^^^^^^^^^^^^ use of undeclared crate or module `not_existing_crate` + | ^^^^^^^^^^^^^^^^^^ use of unresolved module or unlinked crate `not_existing_crate` + | + = help: you might be missing a crate named `not_existing_crate` error: aborting due to 1 previous error diff --git a/tests/ui/rust-2021/future-prelude-collision-shadow.stderr b/tests/ui/rust-2021/future-prelude-collision-shadow.stderr index d9c0fa47eca02..966613e12b50c 100644 --- a/tests/ui/rust-2021/future-prelude-collision-shadow.stderr +++ b/tests/ui/rust-2021/future-prelude-collision-shadow.stderr @@ -14,8 +14,9 @@ LL + use std::convert::TryInto; | help: there is a method `into` with a similar name | -LL | let _: u32 = 3u8.into().unwrap(); - | ~~~~ +LL - let _: u32 = 3u8.try_into().unwrap(); +LL + let _: u32 = 3u8.into().unwrap(); + | error: aborting due to 1 previous error diff --git a/tests/ui/rust-2021/ice-return-unsized-can-impl-2.stderr b/tests/ui/rust-2021/ice-return-unsized-can-impl-2.stderr index b8a9a5c81297a..ac19f91881dfe 100644 --- a/tests/ui/rust-2021/ice-return-unsized-can-impl-2.stderr +++ b/tests/ui/rust-2021/ice-return-unsized-can-impl-2.stderr @@ -7,8 +7,9 @@ LL | fn concrete(b: B) -> B; = note: `B` it is dyn-incompatible, so it can't be `dyn` help: use a new generic type parameter, constrained by `B` | -LL | fn concrete(b: T) -> B; - | ++++++ ~ +LL - fn concrete(b: B) -> B; +LL + fn concrete(b: T) -> B; + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn concrete(b: impl B) -> B; @@ -34,8 +35,9 @@ LL | fn f(a: A) -> A; = note: `A` it is dyn-incompatible, so it can't be `dyn` help: use a new generic type parameter, constrained by `A` | -LL | fn f(a: T) -> A; - | ++++++ ~ +LL - fn f(a: A) -> A; +LL + fn f(a: T) -> A; + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn f(a: impl A) -> A; diff --git a/tests/ui/rust-2021/ice-return-unsized-can-impl.stderr b/tests/ui/rust-2021/ice-return-unsized-can-impl.stderr index c0969570e920d..463c6892ca26c 100644 --- a/tests/ui/rust-2021/ice-return-unsized-can-impl.stderr +++ b/tests/ui/rust-2021/ice-return-unsized-can-impl.stderr @@ -7,8 +7,9 @@ LL | fn g(new: B) -> B; = note: `B` it is dyn-incompatible, so it can't be `dyn` help: use a new generic type parameter, constrained by `B` | -LL | fn g(new: T) -> B; - | ++++++ ~ +LL - fn g(new: B) -> B; +LL + fn g(new: T) -> B; + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn g(new: impl B) -> B; diff --git a/tests/ui/rust-2021/ice-unsized-fn-params-2.stderr b/tests/ui/rust-2021/ice-unsized-fn-params-2.stderr index d35c8ab3e4254..7f837bbe50f0f 100644 --- a/tests/ui/rust-2021/ice-unsized-fn-params-2.stderr +++ b/tests/ui/rust-2021/ice-unsized-fn-params-2.stderr @@ -7,8 +7,9 @@ LL | fn guard(_s: Copy) -> bool { = note: `Copy` it is dyn-incompatible, so it can't be `dyn` help: use a new generic type parameter, constrained by `Copy` | -LL | fn guard(_s: T) -> bool { - | +++++++++ ~ +LL - fn guard(_s: Copy) -> bool { +LL + fn guard(_s: T) -> bool { + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn guard(_s: impl Copy) -> bool { diff --git a/tests/ui/rust-2021/ice-unsized-fn-params.stderr b/tests/ui/rust-2021/ice-unsized-fn-params.stderr index d56e9981a2886..c31500ba80046 100644 --- a/tests/ui/rust-2021/ice-unsized-fn-params.stderr +++ b/tests/ui/rust-2021/ice-unsized-fn-params.stderr @@ -7,8 +7,9 @@ LL | fn g(b: B) -> B; = note: `B` it is dyn-incompatible, so it can't be `dyn` help: use a new generic type parameter, constrained by `B` | -LL | fn g(b: T) -> B; - | ++++++ ~ +LL - fn g(b: B) -> B; +LL + fn g(b: T) -> B; + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn g(b: impl B) -> B; @@ -34,8 +35,9 @@ LL | fn f(a: A) -> A; = note: `A` it is dyn-incompatible, so it can't be `dyn` help: use a new generic type parameter, constrained by `A` | -LL | fn f(a: T) -> A; - | ++++++ ~ +LL - fn f(a: A) -> A; +LL + fn f(a: T) -> A; + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn f(a: impl A) -> A; diff --git a/tests/ui/rust-2024/safe-outside-extern.gated.stderr b/tests/ui/rust-2024/safe-outside-extern.gated.stderr deleted file mode 100644 index e0b218281f365..0000000000000 --- a/tests/ui/rust-2024/safe-outside-extern.gated.stderr +++ /dev/null @@ -1,38 +0,0 @@ -error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier - --> $DIR/safe-outside-extern.rs:4:1 - | -LL | safe fn foo() {} - | ^^^^^^^^^^^^^^^^ - -error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier - --> $DIR/safe-outside-extern.rs:8:1 - | -LL | safe static FOO: i32 = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier - --> $DIR/safe-outside-extern.rs:13:5 - | -LL | safe fn foo(); - | ^^^^^^^^^^^^^^ - -error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier - --> $DIR/safe-outside-extern.rs:19:5 - | -LL | safe fn foo() {} - | ^^^^^^^^^^^^^^^^ - -error: function pointers cannot be declared with `safe` safety qualifier - --> $DIR/safe-outside-extern.rs:24:14 - | -LL | type FnPtr = safe fn(i32, i32) -> i32; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error: static items cannot be declared with `unsafe` safety qualifier outside of `extern` block - --> $DIR/safe-outside-extern.rs:28:1 - | -LL | unsafe static LOL: u8 = 0; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 6 previous errors - diff --git a/tests/ui/rust-2024/safe-outside-extern.ungated.stderr b/tests/ui/rust-2024/safe-outside-extern.ungated.stderr deleted file mode 100644 index 98a4c0eab921a..0000000000000 --- a/tests/ui/rust-2024/safe-outside-extern.ungated.stderr +++ /dev/null @@ -1,89 +0,0 @@ -error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier - --> $DIR/safe-outside-extern.rs:4:1 - | -LL | safe fn foo() {} - | ^^^^^^^^^^^^^^^^ - -error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier - --> $DIR/safe-outside-extern.rs:8:1 - | -LL | safe static FOO: i32 = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier - --> $DIR/safe-outside-extern.rs:13:5 - | -LL | safe fn foo(); - | ^^^^^^^^^^^^^^ - -error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier - --> $DIR/safe-outside-extern.rs:19:5 - | -LL | safe fn foo() {} - | ^^^^^^^^^^^^^^^^ - -error: function pointers cannot be declared with `safe` safety qualifier - --> $DIR/safe-outside-extern.rs:24:14 - | -LL | type FnPtr = safe fn(i32, i32) -> i32; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error: static items cannot be declared with `unsafe` safety qualifier outside of `extern` block - --> $DIR/safe-outside-extern.rs:28:1 - | -LL | unsafe static LOL: u8 = 0; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0658]: `unsafe extern {}` blocks and `safe` keyword are experimental - --> $DIR/safe-outside-extern.rs:4:1 - | -LL | safe fn foo() {} - | ^^^^ - | - = note: see issue #123743 for more information - = help: add `#![feature(unsafe_extern_blocks)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `unsafe extern {}` blocks and `safe` keyword are experimental - --> $DIR/safe-outside-extern.rs:8:1 - | -LL | safe static FOO: i32 = 1; - | ^^^^ - | - = note: see issue #123743 for more information - = help: add `#![feature(unsafe_extern_blocks)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `unsafe extern {}` blocks and `safe` keyword are experimental - --> $DIR/safe-outside-extern.rs:13:5 - | -LL | safe fn foo(); - | ^^^^ - | - = note: see issue #123743 for more information - = help: add `#![feature(unsafe_extern_blocks)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `unsafe extern {}` blocks and `safe` keyword are experimental - --> $DIR/safe-outside-extern.rs:19:5 - | -LL | safe fn foo() {} - | ^^^^ - | - = note: see issue #123743 for more information - = help: add `#![feature(unsafe_extern_blocks)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `unsafe extern {}` blocks and `safe` keyword are experimental - --> $DIR/safe-outside-extern.rs:24:14 - | -LL | type FnPtr = safe fn(i32, i32) -> i32; - | ^^^^ - | - = note: see issue #123743 for more information - = help: add `#![feature(unsafe_extern_blocks)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 11 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.fixed b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.fixed index 586881d180767..cba3d7f1f9f1c 100644 --- a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.fixed +++ b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.fixed @@ -4,7 +4,7 @@ macro_rules! tt { ($e:tt) => { #$e - extern fn foo() {} + extern "C" fn foo() {} } } @@ -13,7 +13,7 @@ macro_rules! ident { #[unsafe($e)] //~^ ERROR: unsafe attribute used without unsafe //~| WARN this is accepted in the current edition - extern fn bar() {} + extern "C" fn bar() {} } } @@ -22,24 +22,33 @@ macro_rules! ident2 { #[unsafe($e = $l)] //~^ ERROR: unsafe attribute used without unsafe //~| WARN this is accepted in the current edition - extern fn bars() {} + extern "C" fn bars() {} } } macro_rules! meta { ($m:meta) => { #[$m] - extern fn baz() {} + extern "C" fn baz() {} } } macro_rules! meta2 { ($m:meta) => { #[$m] - extern fn baw() {} + extern "C" fn baw() {} } } +macro_rules! with_cfg_attr { + () => { + #[cfg_attr(all(), unsafe(link_section = ".custom_section"))] + //~^ ERROR: unsafe attribute used without unsafe + //~| WARN this is accepted in the current edition + pub extern "C" fn abc() {} + }; +} + tt!([unsafe(no_mangle)]); //~^ ERROR: unsafe attribute used without unsafe //~| WARN this is accepted in the current edition @@ -52,6 +61,8 @@ meta2!(unsafe(export_name = "baw")); //~| WARN this is accepted in the current edition ident2!(export_name, "bars"); +with_cfg_attr!(); + #[unsafe(no_mangle)] //~^ ERROR: unsafe attribute used without unsafe //~| WARN this is accepted in the current edition diff --git a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.rs b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.rs index 03e122c7d57e7..4bbf9b25de54c 100644 --- a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.rs +++ b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.rs @@ -4,7 +4,7 @@ macro_rules! tt { ($e:tt) => { #$e - extern fn foo() {} + extern "C" fn foo() {} } } @@ -13,7 +13,7 @@ macro_rules! ident { #[$e] //~^ ERROR: unsafe attribute used without unsafe //~| WARN this is accepted in the current edition - extern fn bar() {} + extern "C" fn bar() {} } } @@ -22,24 +22,33 @@ macro_rules! ident2 { #[$e = $l] //~^ ERROR: unsafe attribute used without unsafe //~| WARN this is accepted in the current edition - extern fn bars() {} + extern "C" fn bars() {} } } macro_rules! meta { ($m:meta) => { #[$m] - extern fn baz() {} + extern "C" fn baz() {} } } macro_rules! meta2 { ($m:meta) => { #[$m] - extern fn baw() {} + extern "C" fn baw() {} } } +macro_rules! with_cfg_attr { + () => { + #[cfg_attr(all(), link_section = ".custom_section")] + //~^ ERROR: unsafe attribute used without unsafe + //~| WARN this is accepted in the current edition + pub extern "C" fn abc() {} + }; +} + tt!([no_mangle]); //~^ ERROR: unsafe attribute used without unsafe //~| WARN this is accepted in the current edition @@ -52,6 +61,8 @@ meta2!(export_name = "baw"); //~| WARN this is accepted in the current edition ident2!(export_name, "bars"); +with_cfg_attr!(); + #[no_mangle] //~^ ERROR: unsafe attribute used without unsafe //~| WARN this is accepted in the current edition diff --git a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.stderr b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.stderr index 87330d2693d05..15a48fb71594a 100644 --- a/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.stderr +++ b/tests/ui/rust-2024/unsafe-attributes/unsafe-attributes-fix.stderr @@ -1,5 +1,5 @@ error: unsafe attribute used without unsafe - --> $DIR/unsafe-attributes-fix.rs:43:6 + --> $DIR/unsafe-attributes-fix.rs:52:6 | LL | tt!([no_mangle]); | ^^^^^^^^^ usage of unsafe attribute @@ -34,7 +34,7 @@ LL | #[unsafe($e)] | +++++++ + error: unsafe attribute used without unsafe - --> $DIR/unsafe-attributes-fix.rs:47:7 + --> $DIR/unsafe-attributes-fix.rs:56:7 | LL | meta!(no_mangle); | ^^^^^^^^^ usage of unsafe attribute @@ -47,7 +47,7 @@ LL | meta!(unsafe(no_mangle)); | +++++++ + error: unsafe attribute used without unsafe - --> $DIR/unsafe-attributes-fix.rs:50:8 + --> $DIR/unsafe-attributes-fix.rs:59:8 | LL | meta2!(export_name = "baw"); | ^^^^^^^^^^^ usage of unsafe attribute @@ -77,7 +77,24 @@ LL | #[unsafe($e = $l)] | +++++++ + error: unsafe attribute used without unsafe - --> $DIR/unsafe-attributes-fix.rs:55:3 + --> $DIR/unsafe-attributes-fix.rs:45:27 + | +LL | #[cfg_attr(all(), link_section = ".custom_section")] + | ^^^^^^^^^^^^ usage of unsafe attribute +... +LL | with_cfg_attr!(); + | ---------------- in this macro invocation + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024! + = note: for more information, see + = note: this error originates in the macro `with_cfg_attr` (in Nightly builds, run with -Z macro-backtrace for more info) +help: wrap the attribute in `unsafe(...)` + | +LL | #[cfg_attr(all(), unsafe(link_section = ".custom_section"))] + | +++++++ + + +error: unsafe attribute used without unsafe + --> $DIR/unsafe-attributes-fix.rs:66:3 | LL | #[no_mangle] | ^^^^^^^^^ usage of unsafe attribute @@ -89,5 +106,5 @@ help: wrap the attribute in `unsafe(...)` LL | #[unsafe(no_mangle)] | +++++++ + -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-impl-trait.ungated.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/safe-impl-trait.ungated.stderr deleted file mode 100644 index 80e7a45f57e79..0000000000000 --- a/tests/ui/rust-2024/unsafe-extern-blocks/safe-impl-trait.ungated.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: expected one of `!` or `::`, found keyword `impl` - --> $DIR/safe-impl-trait.rs:5:6 - | -LL | safe impl Bar for () { } - | ^^^^ expected one of `!` or `::` - -error: aborting due to 1 previous error - diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-trait.gated.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/safe-trait.gated.stderr deleted file mode 100644 index de84037f28c58..0000000000000 --- a/tests/ui/rust-2024/unsafe-extern-blocks/safe-trait.gated.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: expected one of `!` or `::`, found keyword `trait` - --> $DIR/safe-trait.rs:4:6 - | -LL | safe trait Foo {} - | ^^^^^ expected one of `!` or `::` - -error: aborting due to 1 previous error - diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-trait.ungated.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/safe-trait.ungated.stderr deleted file mode 100644 index de84037f28c58..0000000000000 --- a/tests/ui/rust-2024/unsafe-extern-blocks/safe-trait.ungated.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: expected one of `!` or `::`, found keyword `trait` - --> $DIR/safe-trait.rs:4:6 - | -LL | safe trait Foo {} - | ^^^^^ expected one of `!` or `::` - -error: aborting due to 1 previous error - diff --git a/tests/ui/asan-odr-win/asan_odr_windows.rs b/tests/ui/sanitizer/asan_odr_windows.rs similarity index 100% rename from tests/ui/asan-odr-win/asan_odr_windows.rs rename to tests/ui/sanitizer/asan_odr_windows.rs diff --git a/tests/ui/asan-odr-win/auxiliary/asan_odr_win-2.rs b/tests/ui/sanitizer/auxiliary/asan_odr_win-2.rs similarity index 100% rename from tests/ui/asan-odr-win/auxiliary/asan_odr_win-2.rs rename to tests/ui/sanitizer/auxiliary/asan_odr_win-2.rs diff --git a/tests/ui/sanitizer/cfi/supertraits.rs b/tests/ui/sanitizer/cfi/supertraits.rs index 4bb6177577f7c..f80b316918804 100644 --- a/tests/ui/sanitizer/cfi/supertraits.rs +++ b/tests/ui/sanitizer/cfi/supertraits.rs @@ -1,4 +1,3 @@ -#![feature(trait_upcasting)] // Check that super-traits are callable. //@ revisions: cfi kcfi diff --git a/tests/ui/sanitizer/memory-eager.rs b/tests/ui/sanitizer/memory-eager.rs index 9e7889fa1bc09..532d7b308f63d 100644 --- a/tests/ui/sanitizer/memory-eager.rs +++ b/tests/ui/sanitizer/memory-eager.rs @@ -15,7 +15,7 @@ // since it will be linked with an uninstrumented version of it. #![feature(core_intrinsics)] -#![feature(start)] +#![no_main] use std::hint::black_box; use std::mem::MaybeUninit; @@ -29,8 +29,8 @@ fn random() -> char { black_box(r) } -#[start] -fn main(_: isize, _: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(_argc: std::ffi::c_int, _argv: *const *const u8) -> std::ffi::c_int { random(); 0 } diff --git a/tests/ui/sanitizer/memory-passing.rs b/tests/ui/sanitizer/memory-passing.rs index c8ab64bfaf833..96a4cd909c7c2 100644 --- a/tests/ui/sanitizer/memory-passing.rs +++ b/tests/ui/sanitizer/memory-passing.rs @@ -12,8 +12,8 @@ // since it will be linked with an uninstrumented version of it. #![feature(core_intrinsics)] -#![feature(start)] #![allow(invalid_value)] +#![no_main] use std::hint::black_box; @@ -25,8 +25,8 @@ fn calling_black_box_on_zst_ok() { black_box(zst); } -#[start] -fn main(_: isize, _: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(_argc: std::ffi::c_int, _argv: *const *const u8) -> std::ffi::c_int { calling_black_box_on_zst_ok(); 0 } diff --git a/tests/ui/sanitizer/memory.rs b/tests/ui/sanitizer/memory.rs index bd2d67717495f..a91fefe4d16da 100644 --- a/tests/ui/sanitizer/memory.rs +++ b/tests/ui/sanitizer/memory.rs @@ -15,8 +15,8 @@ // since it will be linked with an uninstrumented version of it. #![feature(core_intrinsics)] -#![feature(start)] #![allow(invalid_value)] +#![no_main] use std::hint::black_box; use std::mem::MaybeUninit; @@ -39,8 +39,8 @@ fn xor(a: &[isize]) -> isize { s } -#[start] -fn main(_: isize, _: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(_argc: std::ffi::c_int, _argv: *const *const u8) -> std::ffi::c_int { let r = black_box(random as fn() -> [isize; 32])(); - xor(&r) + xor(&r) as std::ffi::c_int } diff --git a/tests/ui/self/arbitrary-self-from-method-substs-ice.rs b/tests/ui/self/arbitrary-self-from-method-substs-ice.rs index 2c0f25fc6ff93..46e4afd8532e1 100644 --- a/tests/ui/self/arbitrary-self-from-method-substs-ice.rs +++ b/tests/ui/self/arbitrary-self-from-method-substs-ice.rs @@ -11,7 +11,7 @@ impl Foo { //~^ ERROR invalid generic `self` parameter type //~| ERROR destructor of `R` cannot be evaluated at compile-time self.0 - //~^ ERROR cannot call conditionally-const method `::deref` in constant function + //~^ ERROR cannot perform non-const deref coercion on `R` in constant functions } } diff --git a/tests/ui/self/arbitrary-self-from-method-substs-ice.stderr b/tests/ui/self/arbitrary-self-from-method-substs-ice.stderr index cf4c219215e08..f217370b024b5 100644 --- a/tests/ui/self/arbitrary-self-from-method-substs-ice.stderr +++ b/tests/ui/self/arbitrary-self-from-method-substs-ice.stderr @@ -1,12 +1,11 @@ -error[E0658]: cannot call conditionally-const method `::deref` in constant functions +error[E0015]: cannot perform non-const deref coercion on `R` in constant functions --> $DIR/arbitrary-self-from-method-substs-ice.rs:13:9 | LL | self.0 | ^^^^^^ | - = note: see issue #67792 for more information - = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: attempting to deref into `Foo` + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants error[E0493]: destructor of `R` cannot be evaluated at compile-time --> $DIR/arbitrary-self-from-method-substs-ice.rs:10:43 @@ -28,5 +27,5 @@ LL | const fn get>(self: R) -> u32 { error: aborting due to 3 previous errors -Some errors have detailed explanations: E0493, E0658, E0801. -For more information about an error, try `rustc --explain E0493`. +Some errors have detailed explanations: E0015, E0493, E0801. +For more information about an error, try `rustc --explain E0015`. diff --git a/tests/ui/self/arbitrary-self-from-method-substs-with-receiver.stderr b/tests/ui/self/arbitrary-self-from-method-substs-with-receiver.stderr index 9af2a08f3712d..bafa290a3cfd0 100644 --- a/tests/ui/self/arbitrary-self-from-method-substs-with-receiver.stderr +++ b/tests/ui/self/arbitrary-self-from-method-substs-with-receiver.stderr @@ -53,7 +53,7 @@ LL | assert_eq!(smart_ptr.a::<&Foo>(), 2); | ^^^^^^^^^ expected `&Foo`, found `SmartPtr<'_, Foo>` | = note: expected reference `&Foo` - found struct `SmartPtr<'_, Foo, >` + found struct `SmartPtr<'_, Foo>` error[E0308]: mismatched types --> $DIR/arbitrary-self-from-method-substs-with-receiver.rs:62:16 @@ -62,7 +62,7 @@ LL | assert_eq!(smart_ptr.b::<&Foo>(), 1); | ^^^^^^^^^ expected `&Foo`, found `SmartPtr<'_, Foo>` | = note: expected reference `&Foo` - found struct `SmartPtr<'_, Foo, >` + found struct `SmartPtr<'_, Foo>` error: aborting due to 8 previous errors diff --git a/tests/ui/self/arbitrary-self-from-method-substs.feature.stderr b/tests/ui/self/arbitrary-self-from-method-substs.feature.stderr index 6e864f44aa348..f67918a2577ac 100644 --- a/tests/ui/self/arbitrary-self-from-method-substs.feature.stderr +++ b/tests/ui/self/arbitrary-self-from-method-substs.feature.stderr @@ -83,7 +83,7 @@ LL | smart_ptr.get::<&Foo>(); | ^^^^^^^^^ expected `&Foo`, found `SmartPtr<'_, Foo>` | = note: expected reference `&Foo` - found struct `SmartPtr<'_, Foo, >` + found struct `SmartPtr<'_, Foo>` error[E0271]: type mismatch resolving `::Receiver == Foo` --> $DIR/arbitrary-self-from-method-substs.rs:92:9 diff --git a/tests/ui/self/arbitrary-self-types-dyn-incompatible.curr.stderr b/tests/ui/self/arbitrary-self-types-dyn-incompatible.curr.stderr index 2eb7597d5c105..8382b4867e228 100644 --- a/tests/ui/self/arbitrary-self-types-dyn-incompatible.curr.stderr +++ b/tests/ui/self/arbitrary-self-types-dyn-incompatible.curr.stderr @@ -1,38 +1,40 @@ -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/arbitrary-self-types-dyn-incompatible.rs:33:32 | LL | fn foo(self: &Rc) -> usize; | --------- help: consider changing method `foo`'s `self` parameter to be `&self`: `&Self` ... LL | let x = Rc::new(5usize) as Rc; - | ^^^^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/arbitrary-self-types-dyn-incompatible.rs:8:18 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn foo(self: &Rc) -> usize; | ^^^^^^^^^ ...because method `foo`'s `self` parameter cannot be dispatched on - = help: only type `usize` implements the trait, consider using it directly instead + = help: only type `usize` implements `Foo`; consider using it directly instead. -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/arbitrary-self-types-dyn-incompatible.rs:33:13 | LL | fn foo(self: &Rc) -> usize; | --------- help: consider changing method `foo`'s `self` parameter to be `&self`: `&Self` ... LL | let x = Rc::new(5usize) as Rc; - | ^^^^^^^^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/arbitrary-self-types-dyn-incompatible.rs:8:18 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn foo(self: &Rc) -> usize; | ^^^^^^^^^ ...because method `foo`'s `self` parameter cannot be dispatched on - = help: only type `usize` implements the trait, consider using it directly instead + = help: only type `usize` implements `Foo`; consider using it directly instead. = note: required for the cast from `Rc` to `Rc` error: aborting due to 2 previous errors diff --git a/tests/ui/self/arbitrary-self-types-dyn-incompatible.dyn_compatible_for_dispatch.stderr b/tests/ui/self/arbitrary-self-types-dyn-incompatible.dyn_compatible_for_dispatch.stderr index 02af692c4a352..d324f4641cf28 100644 --- a/tests/ui/self/arbitrary-self-types-dyn-incompatible.dyn_compatible_for_dispatch.stderr +++ b/tests/ui/self/arbitrary-self-types-dyn-incompatible.dyn_compatible_for_dispatch.stderr @@ -1,20 +1,21 @@ -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/arbitrary-self-types-dyn-incompatible.rs:33:13 | LL | fn foo(self: &Rc) -> usize; | --------- help: consider changing method `foo`'s `self` parameter to be `&self`: `&Self` ... LL | let x = Rc::new(5usize) as Rc; - | ^^^^^^^^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/arbitrary-self-types-dyn-incompatible.rs:8:18 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn foo(self: &Rc) -> usize; | ^^^^^^^^^ ...because method `foo`'s `self` parameter cannot be dispatched on - = help: only type `usize` implements the trait, consider using it directly instead + = help: only type `usize` implements `Foo`; consider using it directly instead. = note: required for the cast from `Rc` to `Rc` error: aborting due to 1 previous error diff --git a/tests/ui/self/arbitrary_self_type_infinite_recursion.rs b/tests/ui/self/arbitrary_self_type_infinite_recursion.rs new file mode 100644 index 0000000000000..6fbf35c0b867b --- /dev/null +++ b/tests/ui/self/arbitrary_self_type_infinite_recursion.rs @@ -0,0 +1,24 @@ +#![feature(arbitrary_self_types)] + +struct MySmartPtr(T); + +impl core::ops::Receiver for MySmartPtr { + type Target = MySmartPtr; +} + +struct Content; + +impl Content { + fn method(self: MySmartPtr) { // note self type + //~^ reached the recursion limit + //~| reached the recursion limit + //~| invalid `self` parameter type + } +} + +fn main() { + let p = MySmartPtr(Content); + p.method(); + //~^ reached the recursion limit + //~| no method named `method` +} diff --git a/tests/ui/self/arbitrary_self_type_infinite_recursion.stderr b/tests/ui/self/arbitrary_self_type_infinite_recursion.stderr new file mode 100644 index 0000000000000..5e652efb36454 --- /dev/null +++ b/tests/ui/self/arbitrary_self_type_infinite_recursion.stderr @@ -0,0 +1,47 @@ +error[E0055]: reached the recursion limit while auto-dereferencing `MySmartPtr` + --> $DIR/arbitrary_self_type_infinite_recursion.rs:12:19 + | +LL | fn method(self: MySmartPtr) { // note self type + | ^^^^^^^^^^^^^^^^ deref recursion limit reached + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`arbitrary_self_type_infinite_recursion`) + +error[E0055]: reached the recursion limit while auto-dereferencing `MySmartPtr` + --> $DIR/arbitrary_self_type_infinite_recursion.rs:12:19 + | +LL | fn method(self: MySmartPtr) { // note self type + | ^^^^^^^^^^^^^^^^ deref recursion limit reached + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`arbitrary_self_type_infinite_recursion`) + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0307]: invalid `self` parameter type: `MySmartPtr` + --> $DIR/arbitrary_self_type_infinite_recursion.rs:12:19 + | +LL | fn method(self: MySmartPtr) { // note self type + | ^^^^^^^^^^^^^^^^ + | + = note: type of `self` must be `Self` or some type implementing `Receiver` + = help: consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box`, `self: Rc`, or `self: Arc` + +error[E0055]: reached the recursion limit while auto-dereferencing `MySmartPtr` + --> $DIR/arbitrary_self_type_infinite_recursion.rs:21:5 + | +LL | p.method(); + | ^^^^^^ deref recursion limit reached + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`arbitrary_self_type_infinite_recursion`) + +error[E0599]: no method named `method` found for struct `MySmartPtr` in the current scope + --> $DIR/arbitrary_self_type_infinite_recursion.rs:21:5 + | +LL | struct MySmartPtr(T); + | -------------------- method `method` not found for this struct +... +LL | p.method(); + | ^^^^^^ method not found in `MySmartPtr` + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0055, E0307, E0599. +For more information about an error, try `rustc --explain E0055`. diff --git a/tests/ui/self/arbitrary_self_type_mut_difference.stderr b/tests/ui/self/arbitrary_self_type_mut_difference.stderr index ffc61ee0d7838..31c1c9b738205 100644 --- a/tests/ui/self/arbitrary_self_type_mut_difference.stderr +++ b/tests/ui/self/arbitrary_self_type_mut_difference.stderr @@ -11,8 +11,9 @@ LL | fn x(self: Pin<&mut Self>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: there is a method `y` with a similar name | -LL | Pin::new(&S).y(); - | ~ +LL - Pin::new(&S).x(); +LL + Pin::new(&S).y(); + | error[E0599]: no method named `y` found for struct `Pin<&mut S>` in the current scope --> $DIR/arbitrary_self_type_mut_difference.rs:12:22 @@ -27,8 +28,9 @@ LL | fn y(self: Pin<&Self>) {} | ^^^^^^^^^^^^^^^^^^^^^^ help: there is a method `x` with a similar name | -LL | Pin::new(&mut S).x(); - | ~ +LL - Pin::new(&mut S).y(); +LL + Pin::new(&mut S).x(); + | error: aborting due to 2 previous errors diff --git a/tests/ui/self/arbitrary_self_types_dispatch_to_vtable.rs b/tests/ui/self/arbitrary_self_types_dispatch_to_vtable.rs new file mode 100644 index 0000000000000..f9e346ea11e21 --- /dev/null +++ b/tests/ui/self/arbitrary_self_types_dispatch_to_vtable.rs @@ -0,0 +1,33 @@ +//@ check-pass + +#![feature(derive_coerce_pointee)] +#![feature(arbitrary_self_types)] + +use std::marker::CoercePointee; +use std::ops::Receiver; + +// `CoercePointee` isn't needed here, it's just a simpler +// (and more conceptual) way of deriving `DispatchFromDyn`. +// You could think of `MyDispatcher` as a smart pointer +// that just doesn't deref to its target type. +#[derive(CoercePointee)] +#[repr(transparent)] +struct MyDispatcher(*const T); + +impl Receiver for MyDispatcher { + type Target = T; +} +struct Test; + +trait Trait { + fn test(self: MyDispatcher); +} + +impl Trait for Test { + fn test(self: MyDispatcher) { + todo!() + } +} +fn main() { + MyDispatcher::(core::ptr::null_mut::()).test(); +} diff --git a/tests/ui/self/arbitrary_self_types_not_allow_call_with_no_deref.stderr b/tests/ui/self/arbitrary_self_types_not_allow_call_with_no_deref.stderr index 4c0ab88493e94..8843efc6ff8c9 100644 --- a/tests/ui/self/arbitrary_self_types_not_allow_call_with_no_deref.stderr +++ b/tests/ui/self/arbitrary_self_types_not_allow_call_with_no_deref.stderr @@ -13,8 +13,9 @@ LL | foo_cpp_ref.0.frobnicate_ref(); | ++ help: there is a method `frobnicate_cpp_ref` with a similar name | -LL | foo_cpp_ref.frobnicate_cpp_ref(); - | ~~~~~~~~~~~~~~~~~~ +LL - foo_cpp_ref.frobnicate_ref(); +LL + foo_cpp_ref.frobnicate_cpp_ref(); + | error[E0599]: no method named `frobnicate_self` found for struct `CppRef` in the current scope --> $DIR/arbitrary_self_types_not_allow_call_with_no_deref.rs:32:17 @@ -31,8 +32,9 @@ LL | foo_cpp_ref.0.frobnicate_self(); // would desugar to `Foo::frobnicate_s | ++ help: there is a method `frobnicate_cpp_ref` with a similar name | -LL | foo_cpp_ref.frobnicate_cpp_ref(); // would desugar to `Foo::frobnicate_self(*foo_cpp_ref)` - | ~~~~~~~~~~~~~~~~~~ +LL - foo_cpp_ref.frobnicate_self(); // would desugar to `Foo::frobnicate_self(*foo_cpp_ref)` +LL + foo_cpp_ref.frobnicate_cpp_ref(); // would desugar to `Foo::frobnicate_self(*foo_cpp_ref)` + | error: aborting due to 2 previous errors diff --git a/tests/ui/self/arbitrary_self_types_recursive_receiver.rs b/tests/ui/self/arbitrary_self_types_recursive_receiver.rs index f3e7f96d7c4de..8b1b6a8a105f3 100644 --- a/tests/ui/self/arbitrary_self_types_recursive_receiver.rs +++ b/tests/ui/self/arbitrary_self_types_recursive_receiver.rs @@ -1,6 +1,22 @@ //@ run-pass #![feature(arbitrary_self_types)] +// When probing for methods, we step forward through a chain of types. The first +// few of those steps can be reached by jumping through the chain of Derefs or the +// chain of Receivers. Later steps can only be reached by following the chain of +// Receivers. For instance, supposing A and B implement both Receiver and Deref, +// while C and D implement only Receiver: +// +// Type A>>> +// +// Deref chain: A -> B -> C +// Receiver chain: A -> B -> C -> D -> E +// +// We report bad type errors from the end of the chain. But at the end of which +// chain? We never morph the type as far as E so the correct behavior is to +// report errors from point C, i.e. the end of the Deref chain. This test case +// ensures we do that. + struct MyNonNull(*const T); impl std::ops::Receiver for MyNonNull { @@ -10,7 +26,13 @@ impl std::ops::Receiver for MyNonNull { #[allow(dead_code)] impl MyNonNull { fn foo(&self) -> *const U { - self.cast::().bar() + let mnn = self.cast::(); + // The following method call is the point of this test. + // If probe.rs reported errors from the last type discovered + // in the Receiver chain, it would be sad here because U is just + // a type variable. But this is a valid call so it ensures + // probe.rs doesn't make that mistake. + mnn.bar() } fn cast(&self) -> MyNonNull { MyNonNull(self.0 as *const U) diff --git a/tests/ui/self/dispatch-from-dyn-layout-2.rs b/tests/ui/self/dispatch-from-dyn-layout-2.rs new file mode 100644 index 0000000000000..cd52f060dc83c --- /dev/null +++ b/tests/ui/self/dispatch-from-dyn-layout-2.rs @@ -0,0 +1,16 @@ +//@ check-pass +// Regression test for #90110. + +// Make sure that object safety checking doesn't freak out when +// we have impossible-to-satisfy `Sized` predicates. + +trait Parser +where + for<'a> (dyn Parser + 'a): Sized, +{ + fn parse_line(&self); +} + +fn foo(_: &dyn Parser) {} + +fn main() {} diff --git a/tests/ui/self/dispatch-from-dyn-layout-3.rs b/tests/ui/self/dispatch-from-dyn-layout-3.rs new file mode 100644 index 0000000000000..6878a4f4ac271 --- /dev/null +++ b/tests/ui/self/dispatch-from-dyn-layout-3.rs @@ -0,0 +1,19 @@ +//@ check-pass + +// Make sure that object safety checking doesn't freak out when +// we have impossible-to-satisfy `DispatchFromDyn` predicates. + +#![feature(dispatch_from_dyn)] +#![feature(arbitrary_self_types)] + +use std::ops::Deref; +use std::ops::DispatchFromDyn; + +trait Trait> +where + for<'a> &'a T: DispatchFromDyn<&'a T>, +{ + fn foo(self: &T) -> Box>; +} + +fn main() {} diff --git a/tests/ui/self/dispatch-from-dyn-layout.rs b/tests/ui/self/dispatch-from-dyn-layout.rs new file mode 100644 index 0000000000000..468dc89a73e61 --- /dev/null +++ b/tests/ui/self/dispatch-from-dyn-layout.rs @@ -0,0 +1,15 @@ +//@ check-pass +// Regression test for #57276. + +// Make sure that object safety checking doesn't freak out when +// we have impossible-to-satisfy `DispatchFromDyn` predicates. + +#![feature(arbitrary_self_types, dispatch_from_dyn)] + +use std::ops::{Deref, DispatchFromDyn}; + +trait Trait + DispatchFromDyn> { + fn foo(self: T) -> dyn Trait; +} + +fn main() {} diff --git a/tests/ui/self/dispatch-from-dyn-zst-transmute-zst-nonzst.rs b/tests/ui/self/dispatch-from-dyn-zst-transmute-zst-nonzst.rs new file mode 100644 index 0000000000000..71f198f7dc70e --- /dev/null +++ b/tests/ui/self/dispatch-from-dyn-zst-transmute-zst-nonzst.rs @@ -0,0 +1,25 @@ +// We used to allow erroneous `DispatchFromDyn` impls whose RHS type contained +// fields that weren't ZSTs. I don't believe this was possible to abuse, but +// it's at least nice to give users better errors. + +#![feature(arbitrary_self_types)] +#![feature(unsize)] +#![feature(dispatch_from_dyn)] + +use std::marker::Unsize; +use std::ops::DispatchFromDyn; + +struct Dispatchable { + _ptr: Box, + z: Z, +} + +impl DispatchFromDyn> for Dispatchable +//~^ ERROR implementing the `DispatchFromDyn` trait requires multiple coercions +where + T: Unsize + ?Sized, + U: ?Sized, +{ +} + +fn main() {} diff --git a/tests/ui/self/dispatch-from-dyn-zst-transmute-zst-nonzst.stderr b/tests/ui/self/dispatch-from-dyn-zst-transmute-zst-nonzst.stderr new file mode 100644 index 0000000000000..1f13c51f67996 --- /dev/null +++ b/tests/ui/self/dispatch-from-dyn-zst-transmute-zst-nonzst.stderr @@ -0,0 +1,16 @@ +error[E0378]: implementing the `DispatchFromDyn` trait requires multiple coercions + --> $DIR/dispatch-from-dyn-zst-transmute-zst-nonzst.rs:17:1 + | +LL | / impl DispatchFromDyn> for Dispatchable +LL | | +LL | | where +LL | | T: Unsize + ?Sized, +LL | | U: ?Sized, + | |______________^ + | + = note: the trait `DispatchFromDyn` may only be implemented for a coercion between structures with a single field being coerced + = note: currently, 2 fields need coercions: `_ptr` (`Box` to `Box`), `z` (`()` to `i32`) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0378`. diff --git a/tests/ui/self/dispatch-from-dyn-zst-transmute.rs b/tests/ui/self/dispatch-from-dyn-zst-transmute.rs new file mode 100644 index 0000000000000..57c255b4d7bca --- /dev/null +++ b/tests/ui/self/dispatch-from-dyn-zst-transmute.rs @@ -0,0 +1,34 @@ +#![feature(arbitrary_self_types)] +#![feature(unsize)] +#![feature(dispatch_from_dyn)] + +use std::marker::PhantomData; +use std::marker::Unsize; +use std::ops::DispatchFromDyn; +use std::ops::Deref; + +struct IsSendToken(PhantomData T>); + +struct Foo<'a, U: ?Sized> { + token: IsSendToken, + ptr: &'a U, +} + +impl<'a, T, U> DispatchFromDyn> for Foo<'a, T> +//~^ ERROR implementing the `DispatchFromDyn` trait requires multiple coercions +where + T: Unsize + ?Sized, + U: ?Sized {} + +trait Bar { + fn f(self: Foo<'_, Self>); +} + +impl Deref for Foo<'_, U> { + type Target = U; + fn deref(&self) -> &U { + self.ptr + } +} + +fn main() {} diff --git a/tests/ui/self/dispatch-from-dyn-zst-transmute.stderr b/tests/ui/self/dispatch-from-dyn-zst-transmute.stderr new file mode 100644 index 0000000000000..5a8ae88b5f1b8 --- /dev/null +++ b/tests/ui/self/dispatch-from-dyn-zst-transmute.stderr @@ -0,0 +1,16 @@ +error[E0378]: implementing the `DispatchFromDyn` trait requires multiple coercions + --> $DIR/dispatch-from-dyn-zst-transmute.rs:17:1 + | +LL | / impl<'a, T, U> DispatchFromDyn> for Foo<'a, T> +LL | | +LL | | where +LL | | T: Unsize + ?Sized, +LL | | U: ?Sized {} + | |_____________^ + | + = note: the trait `DispatchFromDyn` may only be implemented for a coercion between structures with a single field being coerced + = note: currently, 2 fields need coercions: `token` (`IsSendToken` to `IsSendToken`), `ptr` (`&'a T` to `&'a U`) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0378`. diff --git a/tests/ui/self/invalid-self-dyn-receiver.rs b/tests/ui/self/invalid-self-dyn-receiver.rs new file mode 100644 index 0000000000000..a989b331b5e0b --- /dev/null +++ b/tests/ui/self/invalid-self-dyn-receiver.rs @@ -0,0 +1,20 @@ +// Makes sure we don't ICE when encountering a receiver that is *ostensibly* dyn safe, +// because it satisfies `&dyn Bar: DispatchFromDyn<&dyn Bar>`, but is not a valid receiver +// in wfcheck. + +#![feature(arbitrary_self_types)] + +use std::ops::Deref; + +trait Foo: Deref { + fn method(self: &dyn Bar) {} + //~^ ERROR invalid `self` parameter type: `&dyn Bar` +} + +trait Bar {} + +fn test(x: &dyn Foo) { + x.method(); +} + +fn main() {} diff --git a/tests/ui/self/invalid-self-dyn-receiver.stderr b/tests/ui/self/invalid-self-dyn-receiver.stderr new file mode 100644 index 0000000000000..f77f5686ad282 --- /dev/null +++ b/tests/ui/self/invalid-self-dyn-receiver.stderr @@ -0,0 +1,12 @@ +error[E0307]: invalid `self` parameter type: `&dyn Bar` + --> $DIR/invalid-self-dyn-receiver.rs:10:22 + | +LL | fn method(self: &dyn Bar) {} + | ^^^^^^^^ + | + = note: type of `self` must be `Self` or some type implementing `Receiver` + = help: consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box`, `self: Rc`, or `self: Arc` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0307`. diff --git a/tests/ui/self/phantomdata-in-coerce-and-dispatch-impls.rs b/tests/ui/self/phantomdata-in-coerce-and-dispatch-impls.rs new file mode 100644 index 0000000000000..9c7e33830f5fb --- /dev/null +++ b/tests/ui/self/phantomdata-in-coerce-and-dispatch-impls.rs @@ -0,0 +1,25 @@ +//@ check-pass + +#![feature(coerce_unsized, dispatch_from_dyn, unsize)] + +use std::marker::Unsize; +use std::ops::{CoerceUnsized, DispatchFromDyn}; +use std::marker::PhantomData; + +trait Mirror { + type Assoc; +} +impl Mirror for T { + type Assoc = T; +} + +struct W { + t: &'static T, + f: as Mirror>::Assoc, +} + +impl CoerceUnsized> for W where T: Unsize {} + +impl DispatchFromDyn> for W where T: Unsize {} + +fn main() {} diff --git a/tests/ui/self/self-infer.stderr b/tests/ui/self/self-infer.stderr index 4f9e3f21dca52..c6bdff22b6970 100644 --- a/tests/ui/self/self-infer.stderr +++ b/tests/ui/self/self-infer.stderr @@ -6,8 +6,9 @@ LL | fn f(self: _) {} | help: use type parameters instead | -LL | fn f(self: T) {} - | +++ ~ +LL - fn f(self: _) {} +LL + fn f(self: T) {} + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/self-infer.rs:5:17 @@ -17,8 +18,9 @@ LL | fn g(self: &_) {} | help: use type parameters instead | -LL | fn g(self: &T) {} - | +++ ~ +LL - fn g(self: &_) {} +LL + fn g(self: &T) {} + | error: aborting due to 2 previous errors diff --git a/tests/ui/sepcomp/sepcomp-unwind.rs b/tests/ui/sepcomp/sepcomp-unwind.rs index 6a40b5ccc1247..8c25278bb7ec6 100644 --- a/tests/ui/sepcomp/sepcomp-unwind.rs +++ b/tests/ui/sepcomp/sepcomp-unwind.rs @@ -2,7 +2,7 @@ //@ needs-unwind #![allow(dead_code)] //@ compile-flags: -C codegen-units=3 -//@ ignore-emscripten no threads support +//@ needs-threads // Test unwinding through multiple compilation units. diff --git a/tests/ui/simd-abi-checks.rs b/tests/ui/simd-abi-checks.rs index c97767b27941e..3a344a1f5f8fb 100644 --- a/tests/ui/simd-abi-checks.rs +++ b/tests/ui/simd-abi-checks.rs @@ -4,7 +4,7 @@ #![feature(avx512_target_feature)] #![feature(portable_simd)] -#![feature(target_feature_11, simd_ffi)] +#![feature(simd_ffi)] #![allow(improper_ctypes_definitions)] use std::arch::x86_64::*; diff --git a/tests/ui/simd/array-trait.rs b/tests/ui/simd/array-trait.rs index 67583bf82087d..32cbf01428cad 100644 --- a/tests/ui/simd/array-trait.rs +++ b/tests/ui/simd/array-trait.rs @@ -24,10 +24,12 @@ pub struct T([S::Lane; S::SIZE]); //~| ERROR SIMD vector element type should be a primitive scalar //~| ERROR unconstrained generic constant -extern "rust-intrinsic" { - fn simd_insert(x: T, idx: u32, y: E) -> T; - fn simd_extract(x: T, idx: u32) -> E; -} +#[rustc_intrinsic] +unsafe fn simd_insert(x: T, idx: u32, y: E) -> T; + +#[rustc_intrinsic] +unsafe fn simd_extract(x: T, idx: u32) -> E; + pub fn main() { let mut t = T::([0; 4]); diff --git a/tests/ui/simd/array-type.rs b/tests/ui/simd/array-type.rs index 8ca53b1a453fc..d1de700441694 100644 --- a/tests/ui/simd/array-type.rs +++ b/tests/ui/simd/array-type.rs @@ -12,10 +12,12 @@ struct S([i32; 4]); #[derive(Copy, Clone)] struct T([i32; N]); -extern "rust-intrinsic" { - fn simd_insert(x: T, idx: u32, y: E) -> T; - fn simd_extract(x: T, idx: u32) -> E; -} +#[rustc_intrinsic] +unsafe fn simd_insert(x: T, idx: u32, y: E) -> T; + +#[rustc_intrinsic] +unsafe fn simd_extract(x: T, idx: u32) -> E; + pub fn main() { let mut s = S([0; 4]); diff --git a/tests/ui/simd/generics.rs b/tests/ui/simd/generics.rs index f96a7cd75e933..453ed86c14a9c 100644 --- a/tests/ui/simd/generics.rs +++ b/tests/ui/simd/generics.rs @@ -21,9 +21,8 @@ struct B([T; 4]); struct C([T; N]); -extern "rust-intrinsic" { - fn simd_add(x: T, y: T) -> T; -} +#[rustc_intrinsic] +unsafe fn simd_add(x: T, y: T) -> T; fn add>(lhs: T, rhs: T) -> T { lhs + rhs diff --git a/tests/ui/simd/intrinsic/float-math-pass.rs b/tests/ui/simd/intrinsic/float-math-pass.rs index 24b9941133ee7..74cb51a06068e 100644 --- a/tests/ui/simd/intrinsic/float-math-pass.rs +++ b/tests/ui/simd/intrinsic/float-math-pass.rs @@ -15,27 +15,59 @@ #[derive(Copy, Clone, PartialEq, Debug)] struct f32x4(pub [f32; 4]); -extern "rust-intrinsic" { - fn simd_fsqrt(x: T) -> T; - fn simd_fabs(x: T) -> T; - fn simd_fsin(x: T) -> T; - fn simd_fcos(x: T) -> T; - fn simd_fexp(x: T) -> T; - fn simd_fexp2(x: T) -> T; - fn simd_fma(x: T, y: T, z: T) -> T; - fn simd_relaxed_fma(x: T, y: T, z: T) -> T; - fn simd_flog(x: T) -> T; - fn simd_flog10(x: T) -> T; - fn simd_flog2(x: T) -> T; - fn simd_fpow(x: T, y: T) -> T; - fn simd_fpowi(x: T, y: i32) -> T; - - // rounding functions - fn simd_ceil(x: T) -> T; - fn simd_floor(x: T) -> T; - fn simd_round(x: T) -> T; - fn simd_trunc(x: T) -> T; -} +#[rustc_intrinsic] +unsafe fn simd_fsqrt(x: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_fabs(x: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_fsin(x: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_fcos(x: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_fexp(x: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_fexp2(x: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_fma(x: T, y: T, z: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_relaxed_fma(x: T, y: T, z: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_flog(x: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_flog10(x: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_flog2(x: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_fpow(x: T, y: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_fpowi(x: T, y: i32) -> T; + + +// rounding functions +#[rustc_intrinsic] +unsafe fn simd_ceil(x: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_floor(x: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_round(x: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_trunc(x: T) -> T; + macro_rules! assert_approx_eq_f32 { ($a:expr, $b:expr) => ({ diff --git a/tests/ui/simd/intrinsic/generic-arithmetic-2.rs b/tests/ui/simd/intrinsic/generic-arithmetic-2.rs index 663bcdf198193..0fcff8584c850 100644 --- a/tests/ui/simd/intrinsic/generic-arithmetic-2.rs +++ b/tests/ui/simd/intrinsic/generic-arithmetic-2.rs @@ -14,25 +14,54 @@ pub struct u32x4(pub [u32; 4]); #[derive(Copy, Clone)] pub struct f32x4(pub [f32; 4]); -extern "rust-intrinsic" { - fn simd_add(x: T, y: T) -> T; - fn simd_sub(x: T, y: T) -> T; - fn simd_mul(x: T, y: T) -> T; - fn simd_div(x: T, y: T) -> T; - fn simd_rem(x: T, y: T) -> T; - fn simd_shl(x: T, y: T) -> T; - fn simd_shr(x: T, y: T) -> T; - fn simd_and(x: T, y: T) -> T; - fn simd_or(x: T, y: T) -> T; - fn simd_xor(x: T, y: T) -> T; - - fn simd_neg(x: T) -> T; - fn simd_bswap(x: T) -> T; - fn simd_bitreverse(x: T) -> T; - fn simd_ctlz(x: T) -> T; - fn simd_ctpop(x: T) -> T; - fn simd_cttz(x: T) -> T; -} +#[rustc_intrinsic] +unsafe fn simd_add(x: T, y: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_sub(x: T, y: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_mul(x: T, y: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_div(x: T, y: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_rem(x: T, y: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_shl(x: T, y: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_shr(x: T, y: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_and(x: T, y: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_or(x: T, y: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_xor(x: T, y: T) -> T; + + +#[rustc_intrinsic] +unsafe fn simd_neg(x: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_bswap(x: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_bitreverse(x: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_ctlz(x: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_ctpop(x: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_cttz(x: T) -> T; fn main() { let x = i32x4([0, 0, 0, 0]); diff --git a/tests/ui/simd/intrinsic/generic-arithmetic-2.stderr b/tests/ui/simd/intrinsic/generic-arithmetic-2.stderr index 6f5f86d7d374c..e67de2fe9034d 100644 --- a/tests/ui/simd/intrinsic/generic-arithmetic-2.stderr +++ b/tests/ui/simd/intrinsic/generic-arithmetic-2.stderr @@ -1,143 +1,143 @@ error[E0511]: invalid monomorphization of `simd_add` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:81:9 + --> $DIR/generic-arithmetic-2.rs:110:9 | LL | simd_add(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_sub` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:83:9 + --> $DIR/generic-arithmetic-2.rs:112:9 | LL | simd_sub(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_mul` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:85:9 + --> $DIR/generic-arithmetic-2.rs:114:9 | LL | simd_mul(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_div` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:87:9 + --> $DIR/generic-arithmetic-2.rs:116:9 | LL | simd_div(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shl` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:89:9 + --> $DIR/generic-arithmetic-2.rs:118:9 | LL | simd_shl(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shr` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:91:9 + --> $DIR/generic-arithmetic-2.rs:120:9 | LL | simd_shr(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_and` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:93:9 + --> $DIR/generic-arithmetic-2.rs:122:9 | LL | simd_and(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_or` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:95:9 + --> $DIR/generic-arithmetic-2.rs:124:9 | LL | simd_or(0, 0); | ^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_xor` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:97:9 + --> $DIR/generic-arithmetic-2.rs:126:9 | LL | simd_xor(0, 0); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_neg` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:100:9 + --> $DIR/generic-arithmetic-2.rs:129:9 | LL | simd_neg(0); | ^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_bswap` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:102:9 + --> $DIR/generic-arithmetic-2.rs:131:9 | LL | simd_bswap(0); | ^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_bitreverse` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:104:9 + --> $DIR/generic-arithmetic-2.rs:133:9 | LL | simd_bitreverse(0); | ^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_ctlz` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:106:9 + --> $DIR/generic-arithmetic-2.rs:135:9 | LL | simd_ctlz(0); | ^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_cttz` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-arithmetic-2.rs:108:9 + --> $DIR/generic-arithmetic-2.rs:137:9 | LL | simd_cttz(0); | ^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shl` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:111:9 + --> $DIR/generic-arithmetic-2.rs:140:9 | LL | simd_shl(z, z); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shr` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:113:9 + --> $DIR/generic-arithmetic-2.rs:142:9 | LL | simd_shr(z, z); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_and` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:115:9 + --> $DIR/generic-arithmetic-2.rs:144:9 | LL | simd_and(z, z); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_or` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:117:9 + --> $DIR/generic-arithmetic-2.rs:146:9 | LL | simd_or(z, z); | ^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_xor` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:119:9 + --> $DIR/generic-arithmetic-2.rs:148:9 | LL | simd_xor(z, z); | ^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_bswap` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:121:9 + --> $DIR/generic-arithmetic-2.rs:150:9 | LL | simd_bswap(z); | ^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_bitreverse` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:123:9 + --> $DIR/generic-arithmetic-2.rs:152:9 | LL | simd_bitreverse(z); | ^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_ctlz` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:125:9 + --> $DIR/generic-arithmetic-2.rs:154:9 | LL | simd_ctlz(z); | ^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_ctpop` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:127:9 + --> $DIR/generic-arithmetic-2.rs:156:9 | LL | simd_ctpop(z); | ^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_cttz` intrinsic: unsupported operation on `f32x4` with element `f32` - --> $DIR/generic-arithmetic-2.rs:129:9 + --> $DIR/generic-arithmetic-2.rs:158:9 | LL | simd_cttz(z); | ^^^^^^^^^^^^ diff --git a/tests/ui/simd/intrinsic/generic-arithmetic-pass.rs b/tests/ui/simd/intrinsic/generic-arithmetic-pass.rs index e4eb2a9da2718..4a18c0164e4b8 100644 --- a/tests/ui/simd/intrinsic/generic-arithmetic-pass.rs +++ b/tests/ui/simd/intrinsic/generic-arithmetic-pass.rs @@ -23,25 +23,54 @@ macro_rules! all_eq { }}; } -extern "rust-intrinsic" { - fn simd_add(x: T, y: T) -> T; - fn simd_sub(x: T, y: T) -> T; - fn simd_mul(x: T, y: T) -> T; - fn simd_div(x: T, y: T) -> T; - fn simd_rem(x: T, y: T) -> T; - fn simd_shl(x: T, y: T) -> T; - fn simd_shr(x: T, y: T) -> T; - fn simd_and(x: T, y: T) -> T; - fn simd_or(x: T, y: T) -> T; - fn simd_xor(x: T, y: T) -> T; - - fn simd_neg(x: T) -> T; - fn simd_bswap(x: T) -> T; - fn simd_bitreverse(x: T) -> T; - fn simd_ctlz(x: T) -> T; - fn simd_ctpop(x: T) -> T; - fn simd_cttz(x: T) -> T; -} +#[rustc_intrinsic] +unsafe fn simd_add(x: T, y: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_sub(x: T, y: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_mul(x: T, y: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_div(x: T, y: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_rem(x: T, y: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_shl(x: T, y: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_shr(x: T, y: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_and(x: T, y: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_or(x: T, y: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_xor(x: T, y: T) -> T; + + +#[rustc_intrinsic] +unsafe fn simd_neg(x: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_bswap(x: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_bitreverse(x: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_ctlz(x: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_ctpop(x: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_cttz(x: T) -> T; fn main() { let x1 = i32x4([1, 2, 3, 4]); diff --git a/tests/ui/simd/intrinsic/generic-arithmetic-saturating-2.rs b/tests/ui/simd/intrinsic/generic-arithmetic-saturating-2.rs index ec6ac78df1a7a..85464402d6af1 100644 --- a/tests/ui/simd/intrinsic/generic-arithmetic-saturating-2.rs +++ b/tests/ui/simd/intrinsic/generic-arithmetic-saturating-2.rs @@ -14,10 +14,12 @@ pub struct x4(pub [T; 4]); #[derive(Copy, Clone)] pub struct f32x4(pub [f32; 4]); -extern "rust-intrinsic" { - fn simd_saturating_add(x: T, y: T) -> T; - fn simd_saturating_sub(x: T, y: T) -> T; -} +#[rustc_intrinsic] +unsafe fn simd_saturating_add(x: T, y: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_saturating_sub(x: T, y: T) -> T; + fn main() { let x = i32x4([0, 0, 0, 0]); diff --git a/tests/ui/simd/intrinsic/generic-arithmetic-saturating-2.stderr b/tests/ui/simd/intrinsic/generic-arithmetic-saturating-2.stderr index f349cb56560cc..cf275db7e435f 100644 --- a/tests/ui/simd/intrinsic/generic-arithmetic-saturating-2.stderr +++ b/tests/ui/simd/intrinsic/generic-arithmetic-saturating-2.stderr @@ -1,11 +1,11 @@ error[E0511]: invalid monomorphization of `simd_saturating_add` intrinsic: expected element type `f32` of vector type `f32x4` to be a signed or unsigned integer type - --> $DIR/generic-arithmetic-saturating-2.rs:33:9 + --> $DIR/generic-arithmetic-saturating-2.rs:35:9 | LL | simd_saturating_add(z, z); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_saturating_sub` intrinsic: expected element type `f32` of vector type `f32x4` to be a signed or unsigned integer type - --> $DIR/generic-arithmetic-saturating-2.rs:35:9 + --> $DIR/generic-arithmetic-saturating-2.rs:37:9 | LL | simd_saturating_sub(z, z); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/simd/intrinsic/generic-arithmetic-saturating-pass.rs b/tests/ui/simd/intrinsic/generic-arithmetic-saturating-pass.rs index 57bda5c2d624e..5fe65257d15f0 100644 --- a/tests/ui/simd/intrinsic/generic-arithmetic-saturating-pass.rs +++ b/tests/ui/simd/intrinsic/generic-arithmetic-saturating-pass.rs @@ -12,10 +12,12 @@ struct u32x4(pub [u32; 4]); #[derive(Copy, Clone)] struct I32([i32; N]); -extern "rust-intrinsic" { - fn simd_saturating_add(x: T, y: T) -> T; - fn simd_saturating_sub(x: T, y: T) -> T; -} +#[rustc_intrinsic] +unsafe fn simd_saturating_add(x: T, y: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_saturating_sub(x: T, y: T) -> T; + fn main() { // unsigned diff --git a/tests/ui/simd/intrinsic/generic-as.rs b/tests/ui/simd/intrinsic/generic-as.rs index e97bf12c14473..124ca56bc889b 100644 --- a/tests/ui/simd/intrinsic/generic-as.rs +++ b/tests/ui/simd/intrinsic/generic-as.rs @@ -2,9 +2,9 @@ #![feature(repr_simd, intrinsics)] -extern "rust-intrinsic" { - fn simd_as(x: T) -> U; -} + +#[rustc_intrinsic] +unsafe fn simd_as(x: T) -> U; #[derive(Copy, Clone)] #[repr(simd)] diff --git a/tests/ui/simd/intrinsic/generic-bitmask-pass.rs b/tests/ui/simd/intrinsic/generic-bitmask-pass.rs index db10020bd469b..08526991fbe1f 100644 --- a/tests/ui/simd/intrinsic/generic-bitmask-pass.rs +++ b/tests/ui/simd/intrinsic/generic-bitmask-pass.rs @@ -21,9 +21,8 @@ struct u8x4(pub [u8; 4]); #[derive(Copy, Clone, PartialEq, Debug)] struct Tx4(pub [T; 4]); -extern "rust-intrinsic" { - fn simd_bitmask(x: T) -> U; -} +#[rustc_intrinsic] +unsafe fn simd_bitmask(x: T) -> U; fn main() { let z = u32x4([0, 0, 0, 0]); diff --git a/tests/ui/simd/intrinsic/generic-bitmask.rs b/tests/ui/simd/intrinsic/generic-bitmask.rs index 29b9279c370f5..49589d22bbfa9 100644 --- a/tests/ui/simd/intrinsic/generic-bitmask.rs +++ b/tests/ui/simd/intrinsic/generic-bitmask.rs @@ -30,9 +30,8 @@ struct u8x32([u8; 32]); #[derive(Copy, Clone)] struct u8x64([u8; 64]); -extern "rust-intrinsic" { - fn simd_bitmask(x: T) -> U; -} +#[rustc_intrinsic] +unsafe fn simd_bitmask(x: T) -> U; fn main() { let m2 = u32x2([0; 2]); diff --git a/tests/ui/simd/intrinsic/generic-bitmask.stderr b/tests/ui/simd/intrinsic/generic-bitmask.stderr index 0de3f8eead86d..c217bb2d8f1aa 100644 --- a/tests/ui/simd/intrinsic/generic-bitmask.stderr +++ b/tests/ui/simd/intrinsic/generic-bitmask.stderr @@ -1,29 +1,29 @@ error[E0511]: invalid monomorphization of `simd_bitmask` intrinsic: cannot return `u16`, expected `u8` or `[u8; 1]` - --> $DIR/generic-bitmask.rs:53:22 + --> $DIR/generic-bitmask.rs:52:22 | LL | let _: u16 = simd_bitmask(m2); | ^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_bitmask` intrinsic: cannot return `u16`, expected `u8` or `[u8; 1]` - --> $DIR/generic-bitmask.rs:56:22 + --> $DIR/generic-bitmask.rs:55:22 | LL | let _: u16 = simd_bitmask(m8); | ^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_bitmask` intrinsic: cannot return `u32`, expected `u16` or `[u8; 2]` - --> $DIR/generic-bitmask.rs:59:22 + --> $DIR/generic-bitmask.rs:58:22 | LL | let _: u32 = simd_bitmask(m16); | ^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_bitmask` intrinsic: cannot return `u64`, expected `u32` or `[u8; 4]` - --> $DIR/generic-bitmask.rs:62:22 + --> $DIR/generic-bitmask.rs:61:22 | LL | let _: u64 = simd_bitmask(m32); | ^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_bitmask` intrinsic: cannot return `u128`, expected `u64` or `[u8; 8]` - --> $DIR/generic-bitmask.rs:65:23 + --> $DIR/generic-bitmask.rs:64:23 | LL | let _: u128 = simd_bitmask(m64); | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/simd/intrinsic/generic-bswap-byte.rs b/tests/ui/simd/intrinsic/generic-bswap-byte.rs index f1702538165d3..4521573636c01 100644 --- a/tests/ui/simd/intrinsic/generic-bswap-byte.rs +++ b/tests/ui/simd/intrinsic/generic-bswap-byte.rs @@ -10,9 +10,8 @@ struct i8x4([i8; 4]); #[derive(Copy, Clone)] struct u8x4([u8; 4]); -extern "rust-intrinsic" { - fn simd_bswap(x: T) -> T; -} +#[rustc_intrinsic] +unsafe fn simd_bswap(x: T) -> T; fn main() { unsafe { diff --git a/tests/ui/simd/intrinsic/generic-cast-pass.rs b/tests/ui/simd/intrinsic/generic-cast-pass.rs index e0319a6461ad2..2155d84586471 100644 --- a/tests/ui/simd/intrinsic/generic-cast-pass.rs +++ b/tests/ui/simd/intrinsic/generic-cast-pass.rs @@ -3,9 +3,9 @@ #![feature(repr_simd, intrinsics)] -extern "rust-intrinsic" { - fn simd_cast(x: T) -> U; -} + +#[rustc_intrinsic] +unsafe fn simd_cast(x: T) -> U; use std::cmp::{max, min}; diff --git a/tests/ui/simd/intrinsic/generic-cast-pointer-width.rs b/tests/ui/simd/intrinsic/generic-cast-pointer-width.rs index 1c09a9fbf3bfd..9f28898654f2f 100644 --- a/tests/ui/simd/intrinsic/generic-cast-pointer-width.rs +++ b/tests/ui/simd/intrinsic/generic-cast-pointer-width.rs @@ -1,9 +1,8 @@ //@ run-pass #![feature(repr_simd, intrinsics)] -extern "rust-intrinsic" { - fn simd_cast(x: T) -> U; -} +#[rustc_intrinsic] +unsafe fn simd_cast(x: T) -> U; #[derive(Copy, Clone)] #[repr(simd)] diff --git a/tests/ui/simd/intrinsic/generic-cast.rs b/tests/ui/simd/intrinsic/generic-cast.rs index 33978a2f7395d..7f398804eb4f3 100644 --- a/tests/ui/simd/intrinsic/generic-cast.rs +++ b/tests/ui/simd/intrinsic/generic-cast.rs @@ -21,9 +21,8 @@ struct f32x4([f32; 4]); struct f32x8([f32; 8]); -extern "rust-intrinsic" { - fn simd_cast(x: T) -> U; -} +#[rustc_intrinsic] +unsafe fn simd_cast(x: T) -> U; fn main() { let x = i32x4([0, 0, 0, 0]); diff --git a/tests/ui/simd/intrinsic/generic-cast.stderr b/tests/ui/simd/intrinsic/generic-cast.stderr index 2f9d44037afb5..1b6ac03f8c964 100644 --- a/tests/ui/simd/intrinsic/generic-cast.stderr +++ b/tests/ui/simd/intrinsic/generic-cast.stderr @@ -1,23 +1,23 @@ error[E0511]: invalid monomorphization of `simd_cast` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-cast.rs:32:9 + --> $DIR/generic-cast.rs:31:9 | LL | simd_cast::(0); | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_cast` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-cast.rs:34:9 + --> $DIR/generic-cast.rs:33:9 | LL | simd_cast::(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_cast` intrinsic: expected SIMD return type, found non-SIMD `i32` - --> $DIR/generic-cast.rs:36:9 + --> $DIR/generic-cast.rs:35:9 | LL | simd_cast::(x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_cast` intrinsic: expected return type with length 4 (same as input type `i32x4`), found `i32x8` with length 8 - --> $DIR/generic-cast.rs:38:9 + --> $DIR/generic-cast.rs:37:9 | LL | simd_cast::<_, i32x8>(x); | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/simd/intrinsic/generic-comparison-pass.rs b/tests/ui/simd/intrinsic/generic-comparison-pass.rs index a4d84a4c53482..68f98372e1dc7 100644 --- a/tests/ui/simd/intrinsic/generic-comparison-pass.rs +++ b/tests/ui/simd/intrinsic/generic-comparison-pass.rs @@ -14,14 +14,24 @@ struct u32x4(pub [u32; 4]); #[derive(Copy, Clone)] struct f32x4(pub [f32; 4]); -extern "rust-intrinsic" { - fn simd_eq(x: T, y: T) -> U; - fn simd_ne(x: T, y: T) -> U; - fn simd_lt(x: T, y: T) -> U; - fn simd_le(x: T, y: T) -> U; - fn simd_gt(x: T, y: T) -> U; - fn simd_ge(x: T, y: T) -> U; -} + +#[rustc_intrinsic] +unsafe fn simd_eq(x: T, y: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_ne(x: T, y: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_lt(x: T, y: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_le(x: T, y: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_gt(x: T, y: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_ge(x: T, y: T) -> U; macro_rules! cmp { ($method: ident($lhs: expr, $rhs: expr)) => {{ diff --git a/tests/ui/simd/intrinsic/generic-comparison.rs b/tests/ui/simd/intrinsic/generic-comparison.rs index f7f0655f3d244..e5adb49f6a3b0 100644 --- a/tests/ui/simd/intrinsic/generic-comparison.rs +++ b/tests/ui/simd/intrinsic/generic-comparison.rs @@ -11,14 +11,24 @@ struct i32x4([i32; 4]); #[allow(non_camel_case_types)] struct i16x8([i16; 8]); -extern "rust-intrinsic" { - fn simd_eq(x: T, y: T) -> U; - fn simd_ne(x: T, y: T) -> U; - fn simd_lt(x: T, y: T) -> U; - fn simd_le(x: T, y: T) -> U; - fn simd_gt(x: T, y: T) -> U; - fn simd_ge(x: T, y: T) -> U; -} + +#[rustc_intrinsic] +unsafe fn simd_eq(x: T, y: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_ne(x: T, y: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_lt(x: T, y: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_le(x: T, y: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_gt(x: T, y: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_ge(x: T, y: T) -> U; fn main() { let x = i32x4([0, 0, 0, 0]); diff --git a/tests/ui/simd/intrinsic/generic-comparison.stderr b/tests/ui/simd/intrinsic/generic-comparison.stderr index ac4d491882742..cc66d2ce40a91 100644 --- a/tests/ui/simd/intrinsic/generic-comparison.stderr +++ b/tests/ui/simd/intrinsic/generic-comparison.stderr @@ -1,107 +1,107 @@ error[E0511]: invalid monomorphization of `simd_eq` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-comparison.rs:27:9 + --> $DIR/generic-comparison.rs:37:9 | LL | simd_eq::(0, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_ne` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-comparison.rs:29:9 + --> $DIR/generic-comparison.rs:39:9 | LL | simd_ne::(0, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_lt` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-comparison.rs:31:9 + --> $DIR/generic-comparison.rs:41:9 | LL | simd_lt::(0, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_le` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-comparison.rs:33:9 + --> $DIR/generic-comparison.rs:43:9 | LL | simd_le::(0, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_gt` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-comparison.rs:35:9 + --> $DIR/generic-comparison.rs:45:9 | LL | simd_gt::(0, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_ge` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-comparison.rs:37:9 + --> $DIR/generic-comparison.rs:47:9 | LL | simd_ge::(0, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_eq` intrinsic: expected SIMD return type, found non-SIMD `i32` - --> $DIR/generic-comparison.rs:40:9 + --> $DIR/generic-comparison.rs:50:9 | LL | simd_eq::<_, i32>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_ne` intrinsic: expected SIMD return type, found non-SIMD `i32` - --> $DIR/generic-comparison.rs:42:9 + --> $DIR/generic-comparison.rs:52:9 | LL | simd_ne::<_, i32>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_lt` intrinsic: expected SIMD return type, found non-SIMD `i32` - --> $DIR/generic-comparison.rs:44:9 + --> $DIR/generic-comparison.rs:54:9 | LL | simd_lt::<_, i32>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_le` intrinsic: expected SIMD return type, found non-SIMD `i32` - --> $DIR/generic-comparison.rs:46:9 + --> $DIR/generic-comparison.rs:56:9 | LL | simd_le::<_, i32>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_gt` intrinsic: expected SIMD return type, found non-SIMD `i32` - --> $DIR/generic-comparison.rs:48:9 + --> $DIR/generic-comparison.rs:58:9 | LL | simd_gt::<_, i32>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_ge` intrinsic: expected SIMD return type, found non-SIMD `i32` - --> $DIR/generic-comparison.rs:50:9 + --> $DIR/generic-comparison.rs:60:9 | LL | simd_ge::<_, i32>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_eq` intrinsic: expected return type with length 4 (same as input type `i32x4`), found `i16x8` with length 8 - --> $DIR/generic-comparison.rs:53:9 + --> $DIR/generic-comparison.rs:63:9 | LL | simd_eq::<_, i16x8>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_ne` intrinsic: expected return type with length 4 (same as input type `i32x4`), found `i16x8` with length 8 - --> $DIR/generic-comparison.rs:55:9 + --> $DIR/generic-comparison.rs:65:9 | LL | simd_ne::<_, i16x8>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_lt` intrinsic: expected return type with length 4 (same as input type `i32x4`), found `i16x8` with length 8 - --> $DIR/generic-comparison.rs:57:9 + --> $DIR/generic-comparison.rs:67:9 | LL | simd_lt::<_, i16x8>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_le` intrinsic: expected return type with length 4 (same as input type `i32x4`), found `i16x8` with length 8 - --> $DIR/generic-comparison.rs:59:9 + --> $DIR/generic-comparison.rs:69:9 | LL | simd_le::<_, i16x8>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_gt` intrinsic: expected return type with length 4 (same as input type `i32x4`), found `i16x8` with length 8 - --> $DIR/generic-comparison.rs:61:9 + --> $DIR/generic-comparison.rs:71:9 | LL | simd_gt::<_, i16x8>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_ge` intrinsic: expected return type with length 4 (same as input type `i32x4`), found `i16x8` with length 8 - --> $DIR/generic-comparison.rs:63:9 + --> $DIR/generic-comparison.rs:73:9 | LL | simd_ge::<_, i16x8>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/simd/intrinsic/generic-elements-pass.rs b/tests/ui/simd/intrinsic/generic-elements-pass.rs index 7b1bda4fbcd5f..8cb600bc9e45a 100644 --- a/tests/ui/simd/intrinsic/generic-elements-pass.rs +++ b/tests/ui/simd/intrinsic/generic-elements-pass.rs @@ -16,12 +16,15 @@ struct i32x4([i32; 4]); #[allow(non_camel_case_types)] struct i32x8([i32; 8]); -extern "rust-intrinsic" { - fn simd_insert(x: T, idx: u32, y: E) -> T; - fn simd_extract(x: T, idx: u32) -> E; +#[rustc_intrinsic] +unsafe fn simd_insert(x: T, idx: u32, y: E) -> T; - fn simd_shuffle(x: T, y: T, idx: I) -> U; -} +#[rustc_intrinsic] +unsafe fn simd_extract(x: T, idx: u32) -> E; + + +#[rustc_intrinsic] +unsafe fn simd_shuffle(x: T, y: T, idx: I) -> U; #[repr(simd)] struct SimdShuffleIdx([u32; LEN]); diff --git a/tests/ui/simd/intrinsic/generic-elements.rs b/tests/ui/simd/intrinsic/generic-elements.rs index 5d784a25eab4a..4be6645f029c5 100644 --- a/tests/ui/simd/intrinsic/generic-elements.rs +++ b/tests/ui/simd/intrinsic/generic-elements.rs @@ -29,13 +29,20 @@ struct f32x4([f32; 4]); #[allow(non_camel_case_types)] struct f32x8([f32; 8]); -extern "rust-intrinsic" { - fn simd_insert(x: T, idx: u32, y: E) -> T; - fn simd_extract(x: T, idx: u32) -> E; - fn simd_shuffle(x: T, y: T, idx: I) -> U; - fn simd_shuffle_generic(x: T, y: T) -> U; -} +#[rustc_intrinsic] +unsafe fn simd_insert(x: T, idx: u32, y: E) -> T; + +#[rustc_intrinsic] +unsafe fn simd_extract(x: T, idx: u32) -> E; + + +#[rustc_intrinsic] +unsafe fn simd_shuffle(x: T, y: T, idx: I) -> U; + +#[rustc_intrinsic] +unsafe fn simd_shuffle_generic(x: T, y: T) -> U; + #[repr(simd)] struct SimdShuffleIdx([u32; LEN]); diff --git a/tests/ui/simd/intrinsic/generic-elements.stderr b/tests/ui/simd/intrinsic/generic-elements.stderr index fd726d7532600..8104c3ba5a2ed 100644 --- a/tests/ui/simd/intrinsic/generic-elements.stderr +++ b/tests/ui/simd/intrinsic/generic-elements.stderr @@ -1,125 +1,125 @@ error[E0511]: invalid monomorphization of `simd_insert` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-elements.rs:47:9 + --> $DIR/generic-elements.rs:54:9 | LL | simd_insert(0, 0, 0); | ^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_insert` intrinsic: expected inserted type `i32` (element of input `i32x4`), found `f64` - --> $DIR/generic-elements.rs:49:9 + --> $DIR/generic-elements.rs:56:9 | LL | simd_insert(x, 0, 1.0); | ^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_extract` intrinsic: expected return type `i32` (element of input `i32x4`), found `f32` - --> $DIR/generic-elements.rs:51:9 + --> $DIR/generic-elements.rs:58:9 | LL | simd_extract::<_, f32>(x, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-elements.rs:55:9 + --> $DIR/generic-elements.rs:62:9 | LL | simd_shuffle::(0, 0, IDX2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-elements.rs:58:9 + --> $DIR/generic-elements.rs:65:9 | LL | simd_shuffle::(0, 0, IDX4); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-elements.rs:61:9 + --> $DIR/generic-elements.rs:68:9 | LL | simd_shuffle::(0, 0, IDX8); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: expected return element type `i32` (element of input `i32x4`), found `f32x2` with element type `f32` - --> $DIR/generic-elements.rs:64:9 + --> $DIR/generic-elements.rs:71:9 | LL | simd_shuffle::<_, _, f32x2>(x, x, IDX2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: expected return element type `i32` (element of input `i32x4`), found `f32x4` with element type `f32` - --> $DIR/generic-elements.rs:66:9 + --> $DIR/generic-elements.rs:73:9 | LL | simd_shuffle::<_, _, f32x4>(x, x, IDX4); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: expected return element type `i32` (element of input `i32x4`), found `f32x8` with element type `f32` - --> $DIR/generic-elements.rs:68:9 + --> $DIR/generic-elements.rs:75:9 | LL | simd_shuffle::<_, _, f32x8>(x, x, IDX8); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: expected return type of length 2, found `i32x8` with length 8 - --> $DIR/generic-elements.rs:71:9 + --> $DIR/generic-elements.rs:78:9 | LL | simd_shuffle::<_, _, i32x8>(x, x, IDX2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: expected return type of length 4, found `i32x8` with length 8 - --> $DIR/generic-elements.rs:73:9 + --> $DIR/generic-elements.rs:80:9 | LL | simd_shuffle::<_, _, i32x8>(x, x, IDX4); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle` intrinsic: expected return type of length 8, found `i32x2` with length 2 - --> $DIR/generic-elements.rs:75:9 + --> $DIR/generic-elements.rs:82:9 | LL | simd_shuffle::<_, _, i32x2>(x, x, IDX8); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle_generic` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-elements.rs:79:9 + --> $DIR/generic-elements.rs:86:9 | LL | simd_shuffle_generic::(0, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle_generic` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-elements.rs:82:9 + --> $DIR/generic-elements.rs:89:9 | LL | simd_shuffle_generic::(0, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle_generic` intrinsic: expected SIMD input type, found non-SIMD `i32` - --> $DIR/generic-elements.rs:85:9 + --> $DIR/generic-elements.rs:92:9 | LL | simd_shuffle_generic::(0, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle_generic` intrinsic: expected return element type `i32` (element of input `i32x4`), found `f32x2` with element type `f32` - --> $DIR/generic-elements.rs:88:9 + --> $DIR/generic-elements.rs:95:9 | LL | simd_shuffle_generic::<_, f32x2, I2>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle_generic` intrinsic: expected return element type `i32` (element of input `i32x4`), found `f32x4` with element type `f32` - --> $DIR/generic-elements.rs:90:9 + --> $DIR/generic-elements.rs:97:9 | LL | simd_shuffle_generic::<_, f32x4, I4>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle_generic` intrinsic: expected return element type `i32` (element of input `i32x4`), found `f32x8` with element type `f32` - --> $DIR/generic-elements.rs:92:9 + --> $DIR/generic-elements.rs:99:9 | LL | simd_shuffle_generic::<_, f32x8, I8>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle_generic` intrinsic: expected return type of length 2, found `i32x8` with length 8 - --> $DIR/generic-elements.rs:95:9 + --> $DIR/generic-elements.rs:102:9 | LL | simd_shuffle_generic::<_, i32x8, I2>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle_generic` intrinsic: expected return type of length 4, found `i32x8` with length 8 - --> $DIR/generic-elements.rs:97:9 + --> $DIR/generic-elements.rs:104:9 | LL | simd_shuffle_generic::<_, i32x8, I4>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_shuffle_generic` intrinsic: expected return type of length 8, found `i32x2` with length 2 - --> $DIR/generic-elements.rs:99:9 + --> $DIR/generic-elements.rs:106:9 | LL | simd_shuffle_generic::<_, i32x2, I8>(x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/simd/intrinsic/generic-gather-pass.rs b/tests/ui/simd/intrinsic/generic-gather-pass.rs index 3315d1cdaa22d..0b2cf47e98980 100644 --- a/tests/ui/simd/intrinsic/generic-gather-pass.rs +++ b/tests/ui/simd/intrinsic/generic-gather-pass.rs @@ -10,10 +10,11 @@ #[derive(Copy, Clone, PartialEq, Debug)] struct x4(pub [T; 4]); -extern "rust-intrinsic" { - fn simd_gather(x: T, y: U, z: V) -> T; - fn simd_scatter(x: T, y: U, z: V) -> (); -} +#[rustc_intrinsic] +unsafe fn simd_gather(x: T, y: U, z: V) -> T; + +#[rustc_intrinsic] +unsafe fn simd_scatter(x: T, y: U, z: V) -> (); fn main() { let mut x = [0_f32, 1., 2., 3., 4., 5., 6., 7.]; diff --git a/tests/ui/simd/intrinsic/generic-reduction-pass.rs b/tests/ui/simd/intrinsic/generic-reduction-pass.rs index 699fb396259dc..8408d0f203bfb 100644 --- a/tests/ui/simd/intrinsic/generic-reduction-pass.rs +++ b/tests/ui/simd/intrinsic/generic-reduction-pass.rs @@ -24,19 +24,38 @@ struct f32x4(pub [f32; 4]); #[derive(Copy, Clone)] struct b8x4(pub [i8; 4]); -extern "rust-intrinsic" { - fn simd_reduce_add_unordered(x: T) -> U; - fn simd_reduce_mul_unordered(x: T) -> U; - fn simd_reduce_add_ordered(x: T, acc: U) -> U; - fn simd_reduce_mul_ordered(x: T, acc: U) -> U; - fn simd_reduce_min(x: T) -> U; - fn simd_reduce_max(x: T) -> U; - fn simd_reduce_and(x: T) -> U; - fn simd_reduce_or(x: T) -> U; - fn simd_reduce_xor(x: T) -> U; - fn simd_reduce_all(x: T) -> bool; - fn simd_reduce_any(x: T) -> bool; -} +#[rustc_intrinsic] +unsafe fn simd_reduce_add_unordered(x: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_reduce_mul_unordered(x: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_reduce_add_ordered(x: T, acc: U) -> U; + +#[rustc_intrinsic] +unsafe fn simd_reduce_mul_ordered(x: T, acc: U) -> U; + +#[rustc_intrinsic] +unsafe fn simd_reduce_min(x: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_reduce_max(x: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_reduce_and(x: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_reduce_or(x: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_reduce_xor(x: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_reduce_all(x: T) -> bool; + +#[rustc_intrinsic] +unsafe fn simd_reduce_any(x: T) -> bool; fn main() { unsafe { diff --git a/tests/ui/simd/intrinsic/generic-reduction.rs b/tests/ui/simd/intrinsic/generic-reduction.rs index 1986deafb6ab5..ead13250643c1 100644 --- a/tests/ui/simd/intrinsic/generic-reduction.rs +++ b/tests/ui/simd/intrinsic/generic-reduction.rs @@ -15,16 +15,26 @@ pub struct f32x4(pub [f32; 4]); #[derive(Copy, Clone)] pub struct u32x4(pub [u32; 4]); +#[rustc_intrinsic] +unsafe fn simd_reduce_add_ordered(x: T, y: U) -> U; -extern "rust-intrinsic" { - fn simd_reduce_add_ordered(x: T, y: U) -> U; - fn simd_reduce_mul_ordered(x: T, y: U) -> U; - fn simd_reduce_and(x: T) -> U; - fn simd_reduce_or(x: T) -> U; - fn simd_reduce_xor(x: T) -> U; - fn simd_reduce_all(x: T) -> bool; - fn simd_reduce_any(x: T) -> bool; -} +#[rustc_intrinsic] +unsafe fn simd_reduce_mul_ordered(x: T, y: U) -> U; + +#[rustc_intrinsic] +unsafe fn simd_reduce_and(x: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_reduce_or(x: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_reduce_xor(x: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_reduce_all(x: T) -> bool; + +#[rustc_intrinsic] +unsafe fn simd_reduce_any(x: T) -> bool; fn main() { let x = u32x4([0, 0, 0, 0]); diff --git a/tests/ui/simd/intrinsic/generic-reduction.stderr b/tests/ui/simd/intrinsic/generic-reduction.stderr index 1028faf69a7bc..302b9ae1d778a 100644 --- a/tests/ui/simd/intrinsic/generic-reduction.stderr +++ b/tests/ui/simd/intrinsic/generic-reduction.stderr @@ -1,59 +1,59 @@ error[E0511]: invalid monomorphization of `simd_reduce_add_ordered` intrinsic: expected return type `f32` (element of input `f32x4`), found `i32` - --> $DIR/generic-reduction.rs:34:9 + --> $DIR/generic-reduction.rs:44:9 | LL | simd_reduce_add_ordered(z, 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_reduce_mul_ordered` intrinsic: expected return type `f32` (element of input `f32x4`), found `i32` - --> $DIR/generic-reduction.rs:36:9 + --> $DIR/generic-reduction.rs:46:9 | LL | simd_reduce_mul_ordered(z, 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_reduce_and` intrinsic: expected return type `u32` (element of input `u32x4`), found `f32` - --> $DIR/generic-reduction.rs:39:22 + --> $DIR/generic-reduction.rs:49:22 | LL | let _: f32 = simd_reduce_and(x); | ^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_reduce_or` intrinsic: expected return type `u32` (element of input `u32x4`), found `f32` - --> $DIR/generic-reduction.rs:41:22 + --> $DIR/generic-reduction.rs:51:22 | LL | let _: f32 = simd_reduce_or(x); | ^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_reduce_xor` intrinsic: expected return type `u32` (element of input `u32x4`), found `f32` - --> $DIR/generic-reduction.rs:43:22 + --> $DIR/generic-reduction.rs:53:22 | LL | let _: f32 = simd_reduce_xor(x); | ^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_reduce_and` intrinsic: unsupported simd_reduce_and from `f32x4` with element `f32` to `f32` - --> $DIR/generic-reduction.rs:46:22 + --> $DIR/generic-reduction.rs:56:22 | LL | let _: f32 = simd_reduce_and(z); | ^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_reduce_or` intrinsic: unsupported simd_reduce_or from `f32x4` with element `f32` to `f32` - --> $DIR/generic-reduction.rs:48:22 + --> $DIR/generic-reduction.rs:58:22 | LL | let _: f32 = simd_reduce_or(z); | ^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_reduce_xor` intrinsic: unsupported simd_reduce_xor from `f32x4` with element `f32` to `f32` - --> $DIR/generic-reduction.rs:50:22 + --> $DIR/generic-reduction.rs:60:22 | LL | let _: f32 = simd_reduce_xor(z); | ^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_reduce_all` intrinsic: unsupported simd_reduce_all from `f32x4` with element `f32` to `bool` - --> $DIR/generic-reduction.rs:53:23 + --> $DIR/generic-reduction.rs:63:23 | LL | let _: bool = simd_reduce_all(z); | ^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_reduce_any` intrinsic: unsupported simd_reduce_any from `f32x4` with element `f32` to `bool` - --> $DIR/generic-reduction.rs:55:23 + --> $DIR/generic-reduction.rs:65:23 | LL | let _: bool = simd_reduce_any(z); | ^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/simd/intrinsic/generic-select-pass.rs b/tests/ui/simd/intrinsic/generic-select-pass.rs index 5690bad504844..6b1b6cb79dbc9 100644 --- a/tests/ui/simd/intrinsic/generic-select-pass.rs +++ b/tests/ui/simd/intrinsic/generic-select-pass.rs @@ -29,10 +29,12 @@ struct f32x4(pub [f32; 4]); #[derive(Copy, Clone, PartialEq, Debug)] struct b8x4(pub [i8; 4]); -extern "rust-intrinsic" { - fn simd_select(x: T, a: U, b: U) -> U; - fn simd_select_bitmask(x: T, a: U, b: U) -> U; -} +#[rustc_intrinsic] +unsafe fn simd_select(x: T, a: U, b: U) -> U; + +#[rustc_intrinsic] +unsafe fn simd_select_bitmask(x: T, a: U, b: U) -> U; + fn main() { let m0 = b8x4([!0, !0, !0, !0]); diff --git a/tests/ui/simd/intrinsic/generic-select.rs b/tests/ui/simd/intrinsic/generic-select.rs index 52e02649590ab..340fe3f35929c 100644 --- a/tests/ui/simd/intrinsic/generic-select.rs +++ b/tests/ui/simd/intrinsic/generic-select.rs @@ -22,10 +22,13 @@ struct b8x4(pub [i8; 4]); #[derive(Copy, Clone, PartialEq)] struct b8x8(pub [i8; 8]); -extern "rust-intrinsic" { - fn simd_select(x: T, a: U, b: U) -> U; - fn simd_select_bitmask(x: T, a: U, b: U) -> U; -} + +#[rustc_intrinsic] +unsafe fn simd_select(x: T, a: U, b: U) -> U; + +#[rustc_intrinsic] +unsafe fn simd_select_bitmask(x: T, a: U, b: U) -> U; + fn main() { let m4 = b8x4([0, 0, 0, 0]); diff --git a/tests/ui/simd/intrinsic/generic-select.stderr b/tests/ui/simd/intrinsic/generic-select.stderr index d576f1bc77473..a97fc91f951d1 100644 --- a/tests/ui/simd/intrinsic/generic-select.stderr +++ b/tests/ui/simd/intrinsic/generic-select.stderr @@ -1,47 +1,47 @@ error[E0511]: invalid monomorphization of `simd_select` intrinsic: mismatched lengths: mask length `8` != other vector length `4` - --> $DIR/generic-select.rs:39:9 + --> $DIR/generic-select.rs:42:9 | LL | simd_select(m8, x, x); | ^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_select` intrinsic: mask element type is `u32`, expected `i_` - --> $DIR/generic-select.rs:42:9 + --> $DIR/generic-select.rs:45:9 | LL | simd_select(x, x, x); | ^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_select` intrinsic: mask element type is `f32`, expected `i_` - --> $DIR/generic-select.rs:45:9 + --> $DIR/generic-select.rs:48:9 | LL | simd_select(z, z, z); | ^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_select` intrinsic: expected SIMD argument type, found non-SIMD `u32` - --> $DIR/generic-select.rs:48:9 + --> $DIR/generic-select.rs:51:9 | LL | simd_select(m4, 0u32, 1u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_select_bitmask` intrinsic: invalid bitmask `u16`, expected `u8` or `[u8; 1]` - --> $DIR/generic-select.rs:51:9 + --> $DIR/generic-select.rs:54:9 | LL | simd_select_bitmask(0u16, x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_select_bitmask` intrinsic: expected SIMD argument type, found non-SIMD `u32` - --> $DIR/generic-select.rs:54:9 + --> $DIR/generic-select.rs:57:9 | LL | simd_select_bitmask(0u8, 1u32, 2u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_select_bitmask` intrinsic: invalid bitmask `f32`, expected `u8` or `[u8; 1]` - --> $DIR/generic-select.rs:57:9 + --> $DIR/generic-select.rs:60:9 | LL | simd_select_bitmask(0.0f32, x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0511]: invalid monomorphization of `simd_select_bitmask` intrinsic: invalid bitmask `&str`, expected `u8` or `[u8; 1]` - --> $DIR/generic-select.rs:60:9 + --> $DIR/generic-select.rs:63:9 | LL | simd_select_bitmask("x", x, x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/simd/intrinsic/generic-shuffle.rs b/tests/ui/simd/intrinsic/generic-shuffle.rs index 2752718d99d6b..1223b8ebe1903 100644 --- a/tests/ui/simd/intrinsic/generic-shuffle.rs +++ b/tests/ui/simd/intrinsic/generic-shuffle.rs @@ -9,9 +9,9 @@ #[derive(Copy, Clone)] pub struct Simd([T; N]); -extern "rust-intrinsic" { - fn simd_shuffle(a: T, b: T, i: I) -> U; -} +#[rustc_intrinsic] +unsafe fn simd_shuffle(a: T, b: T, i: I) -> U; + fn main() { const I: Simd = Simd([0; 2]); diff --git a/tests/ui/simd/intrinsic/inlining-issue67557-ice.rs b/tests/ui/simd/intrinsic/inlining-issue67557-ice.rs index d9239ef5801ae..b324ac40749fa 100644 --- a/tests/ui/simd/intrinsic/inlining-issue67557-ice.rs +++ b/tests/ui/simd/intrinsic/inlining-issue67557-ice.rs @@ -5,9 +5,8 @@ //@ compile-flags: -Zmir-opt-level=4 #![feature(intrinsics, repr_simd)] -extern "rust-intrinsic" { - fn simd_shuffle(x: T, y: T, idx: I) -> U; -} +#[rustc_intrinsic] +unsafe fn simd_shuffle(x: T, y: T, idx: I) -> U; #[repr(simd)] #[derive(Debug, PartialEq)] diff --git a/tests/ui/simd/intrinsic/inlining-issue67557.rs b/tests/ui/simd/intrinsic/inlining-issue67557.rs index 23dd5075f96b6..319bb15c01570 100644 --- a/tests/ui/simd/intrinsic/inlining-issue67557.rs +++ b/tests/ui/simd/intrinsic/inlining-issue67557.rs @@ -5,9 +5,8 @@ //@ compile-flags: -Zmir-opt-level=4 #![feature(intrinsics, repr_simd)] -extern "rust-intrinsic" { - fn simd_shuffle(x: T, y: T, idx: I) -> U; -} +#[rustc_intrinsic] +unsafe fn simd_shuffle(x: T, y: T, idx: I) -> U; #[repr(simd)] #[derive(Debug, PartialEq)] diff --git a/tests/ui/simd/intrinsic/issue-85855.rs b/tests/ui/simd/intrinsic/issue-85855.rs index dc04699f7f89d..daeea793d1bce 100644 --- a/tests/ui/simd/intrinsic/issue-85855.rs +++ b/tests/ui/simd/intrinsic/issue-85855.rs @@ -5,15 +5,18 @@ #![feature(intrinsics)] #![crate_type="lib"] -extern "rust-intrinsic" { - fn simd_saturating_add<'a, T: 'a>(x: T, y: T); - //~^ ERROR: intrinsic has wrong number of lifetime parameters - fn simd_add<'a, T>(x: T, y: T) -> T; +#[rustc_intrinsic] +unsafe fn simd_saturating_add<'a, T: 'a>(x: T, y: T); +//~^ ERROR: intrinsic has wrong number of lifetime parameters - fn simd_sub(x: T, y: U); - //~^ ERROR: intrinsic has wrong number of type parameters +#[rustc_intrinsic] +unsafe fn simd_add<'a, T>(x: T, y: T) -> T; - fn simd_mul(x: T, y: T); - //~^ ERROR: intrinsic has wrong number of const parameters -} +#[rustc_intrinsic] +unsafe fn simd_sub(x: T, y: U); +//~^ ERROR: intrinsic has wrong number of type parameters + +#[rustc_intrinsic] +unsafe fn simd_mul(x: T, y: T); +//~^ ERROR: intrinsic has wrong number of const parameters diff --git a/tests/ui/simd/intrinsic/issue-85855.stderr b/tests/ui/simd/intrinsic/issue-85855.stderr index fb2f1fbc5b1cc..b91a606ba68a1 100644 --- a/tests/ui/simd/intrinsic/issue-85855.stderr +++ b/tests/ui/simd/intrinsic/issue-85855.stderr @@ -1,20 +1,20 @@ error[E0094]: intrinsic has wrong number of lifetime parameters: found 1, expected 0 - --> $DIR/issue-85855.rs:9:27 + --> $DIR/issue-85855.rs:10:30 | -LL | fn simd_saturating_add<'a, T: 'a>(x: T, y: T); - | ^^^^^^^^^^^ expected 0 lifetime parameters +LL | unsafe fn simd_saturating_add<'a, T: 'a>(x: T, y: T); + | ^^^^^^^^^^^ expected 0 lifetime parameters error[E0094]: intrinsic has wrong number of type parameters: found 2, expected 1 - --> $DIR/issue-85855.rs:14:16 + --> $DIR/issue-85855.rs:17:19 | -LL | fn simd_sub(x: T, y: U); - | ^^^^^^ expected 1 type parameter +LL | unsafe fn simd_sub(x: T, y: U); + | ^^^^^^ expected 1 type parameter error[E0094]: intrinsic has wrong number of const parameters: found 1, expected 0 - --> $DIR/issue-85855.rs:17:16 + --> $DIR/issue-85855.rs:21:19 | -LL | fn simd_mul(x: T, y: T); - | ^^^^^^^^^^^^^^^^^^^ expected 0 const parameters +LL | unsafe fn simd_mul(x: T, y: T); + | ^^^^^^^^^^^^^^^^^^^ expected 0 const parameters error: aborting due to 3 previous errors diff --git a/tests/ui/simd/intrinsic/ptr-cast.rs b/tests/ui/simd/intrinsic/ptr-cast.rs index 0490734b48ac5..559b8ba1b5c2d 100644 --- a/tests/ui/simd/intrinsic/ptr-cast.rs +++ b/tests/ui/simd/intrinsic/ptr-cast.rs @@ -2,11 +2,15 @@ #![feature(repr_simd, intrinsics)] -extern "rust-intrinsic" { - fn simd_cast_ptr(x: T) -> U; - fn simd_expose_provenance(x: T) -> U; - fn simd_with_exposed_provenance(x: T) -> U; -} + +#[rustc_intrinsic] +unsafe fn simd_cast_ptr(x: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_expose_provenance(x: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_with_exposed_provenance(x: T) -> U; #[derive(Copy, Clone)] #[repr(simd)] diff --git a/tests/ui/simd/issue-105439.rs b/tests/ui/simd/issue-105439.rs index 3cb43fc8b1af9..108bb282df2af 100644 --- a/tests/ui/simd/issue-105439.rs +++ b/tests/ui/simd/issue-105439.rs @@ -9,9 +9,8 @@ #[repr(simd)] struct i32x4([i32; 4]); -extern "rust-intrinsic" { - pub(crate) fn simd_add(x: T, y: T) -> T; -} +#[rustc_intrinsic] +pub(crate) unsafe fn simd_add(x: T, y: T) -> T; #[inline(always)] fn to_array(a: i32x4) -> [i32; 4] { diff --git a/tests/ui/simd/issue-39720.rs b/tests/ui/simd/issue-39720.rs index 2b51c0224c633..8d7666faaf933 100644 --- a/tests/ui/simd/issue-39720.rs +++ b/tests/ui/simd/issue-39720.rs @@ -11,9 +11,8 @@ pub struct Char3(pub [i8; 3]); #[derive(Copy, Clone, Debug)] pub struct Short3(pub [i16; 3]); -extern "rust-intrinsic" { - fn simd_cast(x: T) -> U; -} +#[rustc_intrinsic] +unsafe fn simd_cast(x: T) -> U; fn main() { let cast: Short3 = unsafe { simd_cast(Char3([10, -3, -9])) }; diff --git a/tests/ui/simd/issue-85915-simd-ptrs.rs b/tests/ui/simd/issue-85915-simd-ptrs.rs index edf60e0205c29..2e7baf48ee326 100644 --- a/tests/ui/simd/issue-85915-simd-ptrs.rs +++ b/tests/ui/simd/issue-85915-simd-ptrs.rs @@ -22,10 +22,12 @@ struct f32x4([f32; 4]); #[derive(Copy, Clone, PartialEq, Debug)] struct i32x4([i32; 4]); -extern "rust-intrinsic" { - fn simd_gather(x: T, y: U, z: V) -> T; - fn simd_scatter(x: T, y: U, z: V) -> (); -} + +#[rustc_intrinsic] +unsafe fn simd_gather(x: T, y: U, z: V) -> T; + +#[rustc_intrinsic] +unsafe fn simd_scatter(x: T, y: U, z: V) -> (); fn main() { let mut x = [0_f32, 1., 2., 3., 4., 5., 6., 7.]; diff --git a/tests/ui/simd/issue-89193.rs b/tests/ui/simd/issue-89193.rs index 9530124a7cc81..4b4fb9d916968 100644 --- a/tests/ui/simd/issue-89193.rs +++ b/tests/ui/simd/issue-89193.rs @@ -10,9 +10,8 @@ #[derive(Copy, Clone, PartialEq, Debug)] struct x4(pub [T; 4]); -extern "rust-intrinsic" { - fn simd_gather(x: T, y: U, z: V) -> T; -} +#[rustc_intrinsic] +unsafe fn simd_gather(x: T, y: U, z: V) -> T; fn main() { let x: [usize; 4] = [10, 11, 12, 13]; diff --git a/tests/ui/simd/masked-load-store-build-fail.rs b/tests/ui/simd/masked-load-store-build-fail.rs index fbd657763c984..b8742184eb02c 100644 --- a/tests/ui/simd/masked-load-store-build-fail.rs +++ b/tests/ui/simd/masked-load-store-build-fail.rs @@ -1,10 +1,12 @@ //@ build-fail #![feature(repr_simd, intrinsics)] -extern "rust-intrinsic" { - fn simd_masked_load(mask: M, pointer: P, values: T) -> T; - fn simd_masked_store(mask: M, pointer: P, values: T) -> (); -} + +#[rustc_intrinsic] +unsafe fn simd_masked_load(mask: M, pointer: P, values: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_masked_store(mask: M, pointer: P, values: T) -> (); #[derive(Copy, Clone)] #[repr(simd)] diff --git a/tests/ui/simd/masked-load-store-build-fail.stderr b/tests/ui/simd/masked-load-store-build-fail.stderr index 59af83fe0e893..8a8d8eb99e278 100644 --- a/tests/ui/simd/masked-load-store-build-fail.stderr +++ b/tests/ui/simd/masked-load-store-build-fail.stderr @@ -1,5 +1,5 @@ error[E0511]: invalid monomorphization of `simd_masked_load` intrinsic: expected third argument with length 8 (same as input type `Simd`), found `Simd` with length 4 - --> $DIR/masked-load-store-build-fail.rs:18:9 + --> $DIR/masked-load-store-build-fail.rs:20:9 | LL | / simd_masked_load( LL | | Simd::([-1, 0, -1, -1, 0, 0, 0, 0]), @@ -9,7 +9,7 @@ LL | | ); | |_________^ error[E0511]: invalid monomorphization of `simd_masked_load` intrinsic: expected element type `u8` of second argument `*const i8` to be a pointer to the element type `u8` of the first argument `Simd`, found `u8` != `*_ u8` - --> $DIR/masked-load-store-build-fail.rs:25:9 + --> $DIR/masked-load-store-build-fail.rs:27:9 | LL | / simd_masked_load( LL | | Simd::([-1, 0, -1, -1]), @@ -19,7 +19,7 @@ LL | | ); | |_________^ error[E0511]: invalid monomorphization of `simd_masked_load` intrinsic: expected element type `u32` of second argument `*const u8` to be a pointer to the element type `u32` of the first argument `Simd`, found `u32` != `*_ u32` - --> $DIR/masked-load-store-build-fail.rs:32:9 + --> $DIR/masked-load-store-build-fail.rs:34:9 | LL | / simd_masked_load( LL | | Simd::([-1, 0, -1, -1]), @@ -29,7 +29,7 @@ LL | | ); | |_________^ error[E0511]: invalid monomorphization of `simd_masked_load` intrinsic: expected element type `u8` of third argument `Simd` to be a signed integer type - --> $DIR/masked-load-store-build-fail.rs:39:9 + --> $DIR/masked-load-store-build-fail.rs:41:9 | LL | / simd_masked_load( LL | | Simd::([1, 0, 1, 1]), @@ -39,7 +39,7 @@ LL | | ); | |_________^ error[E0511]: invalid monomorphization of `simd_masked_store` intrinsic: expected element type `u32` of second argument `*const u8` to be a pointer to the element type `u32` of the first argument `Simd`, found `u32` != `*mut u32` - --> $DIR/masked-load-store-build-fail.rs:46:9 + --> $DIR/masked-load-store-build-fail.rs:48:9 | LL | / simd_masked_store( LL | | Simd([-1i8; 4]), @@ -49,7 +49,7 @@ LL | | ); | |_________^ error[E0511]: invalid monomorphization of `simd_masked_store` intrinsic: expected element type `u8` of second argument `*const u8` to be a pointer to the element type `u8` of the first argument `Simd`, found `u8` != `*mut u8` - --> $DIR/masked-load-store-build-fail.rs:53:9 + --> $DIR/masked-load-store-build-fail.rs:55:9 | LL | / simd_masked_store( LL | | Simd([-1i8; 4]), @@ -59,7 +59,7 @@ LL | | ); | |_________^ error[E0511]: invalid monomorphization of `simd_masked_store` intrinsic: expected third argument with length 4 (same as input type `Simd`), found `Simd` with length 2 - --> $DIR/masked-load-store-build-fail.rs:60:9 + --> $DIR/masked-load-store-build-fail.rs:62:9 | LL | / simd_masked_store( LL | | Simd([-1i8; 4]), @@ -69,7 +69,7 @@ LL | | ); | |_________^ error[E0511]: invalid monomorphization of `simd_masked_store` intrinsic: expected element type `u8` of third argument `Simd` to be a signed integer type - --> $DIR/masked-load-store-build-fail.rs:67:9 + --> $DIR/masked-load-store-build-fail.rs:69:9 | LL | / simd_masked_store( LL | | Simd([1u32; 4]), diff --git a/tests/ui/simd/masked-load-store-check-fail.rs b/tests/ui/simd/masked-load-store-check-fail.rs index 39c82c41385ce..0f36bf6443f9b 100644 --- a/tests/ui/simd/masked-load-store-check-fail.rs +++ b/tests/ui/simd/masked-load-store-check-fail.rs @@ -1,10 +1,11 @@ //@ check-fail #![feature(repr_simd, intrinsics)] -extern "rust-intrinsic" { - fn simd_masked_load(mask: M, pointer: P, values: T) -> T; - fn simd_masked_store(mask: M, pointer: P, values: T) -> (); -} +#[rustc_intrinsic] +unsafe fn simd_masked_load(mask: M, pointer: P, values: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_masked_store(mask: M, pointer: P, values: T) -> (); #[derive(Copy, Clone)] #[repr(simd)] diff --git a/tests/ui/simd/masked-load-store-check-fail.stderr b/tests/ui/simd/masked-load-store-check-fail.stderr index 5d205d607c9f8..fa65798fc94d3 100644 --- a/tests/ui/simd/masked-load-store-check-fail.stderr +++ b/tests/ui/simd/masked-load-store-check-fail.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/masked-load-store-check-fail.rs:21:13 + --> $DIR/masked-load-store-check-fail.rs:22:13 | LL | let _x: Simd = simd_masked_load( | ---------------- arguments to this function are incorrect @@ -10,7 +10,7 @@ LL | Simd::([9; 4]) = note: expected struct `Simd<_, 2>` found struct `Simd<_, 4>` help: the return type of this call is `Simd` due to the type of the argument passed - --> $DIR/masked-load-store-check-fail.rs:18:31 + --> $DIR/masked-load-store-check-fail.rs:19:31 | LL | let _x: Simd = simd_masked_load( | _______________________________^ @@ -21,13 +21,13 @@ LL | | Simd::([9; 4]) LL | | ); | |_________^ note: function defined here - --> $DIR/masked-load-store-check-fail.rs:5:8 + --> $DIR/masked-load-store-check-fail.rs:5:11 | -LL | fn simd_masked_load(mask: M, pointer: P, values: T) -> T; - | ^^^^^^^^^^^^^^^^ +LL | unsafe fn simd_masked_load(mask: M, pointer: P, values: T) -> T; + | ^^^^^^^^^^^^^^^^ --------- error[E0308]: mismatched types - --> $DIR/masked-load-store-check-fail.rs:28:13 + --> $DIR/masked-load-store-check-fail.rs:29:13 | LL | let _x: Simd = simd_masked_load( | ---------------- arguments to this function are incorrect @@ -38,7 +38,7 @@ LL | default = note: expected struct `Simd` found struct `Simd` help: the return type of this call is `Simd` due to the type of the argument passed - --> $DIR/masked-load-store-check-fail.rs:25:32 + --> $DIR/masked-load-store-check-fail.rs:26:32 | LL | let _x: Simd = simd_masked_load( | ________________________________^ @@ -49,10 +49,10 @@ LL | | default LL | | ); | |_________^ note: function defined here - --> $DIR/masked-load-store-check-fail.rs:5:8 + --> $DIR/masked-load-store-check-fail.rs:5:11 | -LL | fn simd_masked_load(mask: M, pointer: P, values: T) -> T; - | ^^^^^^^^^^^^^^^^ +LL | unsafe fn simd_masked_load(mask: M, pointer: P, values: T) -> T; + | ^^^^^^^^^^^^^^^^ --------- error: aborting due to 2 previous errors diff --git a/tests/ui/simd/masked-load-store.rs b/tests/ui/simd/masked-load-store.rs index 902143f92615e..4b4195f51f186 100644 --- a/tests/ui/simd/masked-load-store.rs +++ b/tests/ui/simd/masked-load-store.rs @@ -1,10 +1,11 @@ //@ run-pass #![feature(repr_simd, intrinsics)] -extern "rust-intrinsic" { - fn simd_masked_load(mask: M, pointer: P, values: T) -> T; - fn simd_masked_store(mask: M, pointer: P, values: T) -> (); -} +#[rustc_intrinsic] +unsafe fn simd_masked_load(mask: M, pointer: P, values: T) -> T; + +#[rustc_intrinsic] +unsafe fn simd_masked_store(mask: M, pointer: P, values: T) -> (); #[derive(Copy, Clone)] #[repr(simd)] diff --git a/tests/ui/simd/monomorphize-shuffle-index.generic.stderr b/tests/ui/simd/monomorphize-shuffle-index.generic.stderr index 2d1fa1f8da225..b0a8da59fac1f 100644 --- a/tests/ui/simd/monomorphize-shuffle-index.generic.stderr +++ b/tests/ui/simd/monomorphize-shuffle-index.generic.stderr @@ -1,5 +1,5 @@ error: overly complex generic constant - --> $DIR/monomorphize-shuffle-index.rs:29:45 + --> $DIR/monomorphize-shuffle-index.rs:32:45 | LL | return simd_shuffle_generic::<_, _, { &Self::I.0 }>(a, b); | ^^----------^^ diff --git a/tests/ui/simd/monomorphize-shuffle-index.rs b/tests/ui/simd/monomorphize-shuffle-index.rs index 140cf6fbe96a9..01926408a2ceb 100644 --- a/tests/ui/simd/monomorphize-shuffle-index.rs +++ b/tests/ui/simd/monomorphize-shuffle-index.rs @@ -4,12 +4,15 @@ #![feature(repr_simd, intrinsics, adt_const_params, unsized_const_params, generic_const_exprs)] #![allow(incomplete_features)] -extern "rust-intrinsic" { - #[cfg(old)] - fn simd_shuffle(a: T, b: T, i: I) -> U; - #[cfg(any(generic, generic_with_fn))] - fn simd_shuffle_generic(a: T, b: T) -> U; -} + +#[rustc_intrinsic] +#[cfg(old)] +unsafe fn simd_shuffle(a: T, b: T, i: I) -> U; + +#[rustc_intrinsic] +#[cfg(any(generic, generic_with_fn))] +unsafe fn simd_shuffle_generic(a: T, b: T) -> U; + #[derive(Copy, Clone)] #[repr(simd)] diff --git a/tests/ui/simd/portable-intrinsics-arent-exposed.stderr b/tests/ui/simd/portable-intrinsics-arent-exposed.stderr index d38667f750d92..71b4273fa0630 100644 --- a/tests/ui/simd/portable-intrinsics-arent-exposed.stderr +++ b/tests/ui/simd/portable-intrinsics-arent-exposed.stderr @@ -15,8 +15,9 @@ LL | use std::simd::intrinsics; | help: consider importing this module instead | -LL | use std::intrinsics; - | ~~~~~~~~~~~~~~~ +LL - use std::simd::intrinsics; +LL + use std::intrinsics; + | error: aborting due to 2 previous errors diff --git a/tests/ui/simd/repr_packed.rs b/tests/ui/simd/repr_packed.rs index 1ba15bda98dd2..a666892226eaa 100644 --- a/tests/ui/simd/repr_packed.rs +++ b/tests/ui/simd/repr_packed.rs @@ -22,9 +22,8 @@ fn check_ty() { check_size_align::(); } -extern "rust-intrinsic" { - fn simd_add(a: T, b: T) -> T; -} +#[rustc_intrinsic] +unsafe fn simd_add(a: T, b: T) -> T; fn main() { check_ty::(); diff --git a/tests/ui/simd/shuffle.rs b/tests/ui/simd/shuffle.rs index 96c0ed2118f9e..2cae5a1e7de2a 100644 --- a/tests/ui/simd/shuffle.rs +++ b/tests/ui/simd/shuffle.rs @@ -8,9 +8,8 @@ use std::marker::ConstParamTy; -extern "rust-intrinsic" { - fn simd_shuffle(a: T, b: T, i: I) -> U; -} +#[rustc_intrinsic] +unsafe fn simd_shuffle(a: T, b: T, i: I) -> U; #[derive(Copy, Clone, ConstParamTy, PartialEq, Eq)] #[repr(simd)] diff --git a/tests/ui/simd/simd-bitmask-notpow2.rs b/tests/ui/simd/simd-bitmask-notpow2.rs index 3499bf33ed577..d7572ef4a2a4d 100644 --- a/tests/ui/simd/simd-bitmask-notpow2.rs +++ b/tests/ui/simd/simd-bitmask-notpow2.rs @@ -4,10 +4,12 @@ //@ ignore-endian-big #![feature(repr_simd, intrinsics)] -extern "rust-intrinsic" { - fn simd_bitmask(v: T) -> U; - fn simd_select_bitmask(m: T, a: U, b: U) -> U; -} +#[rustc_intrinsic] +unsafe fn simd_bitmask(v: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_select_bitmask(m: T, a: U, b: U) -> U; + fn main() { // Non-power-of-2 multi-byte mask. diff --git a/tests/ui/simd/simd-bitmask.rs b/tests/ui/simd/simd-bitmask.rs index 82f73fca95124..4275ab0f40c9d 100644 --- a/tests/ui/simd/simd-bitmask.rs +++ b/tests/ui/simd/simd-bitmask.rs @@ -1,10 +1,12 @@ //@run-pass #![feature(repr_simd, intrinsics)] -extern "rust-intrinsic" { - fn simd_bitmask(v: T) -> U; - fn simd_select_bitmask(m: T, a: U, b: U) -> U; -} +#[rustc_intrinsic] +unsafe fn simd_bitmask(v: T) -> U; + +#[rustc_intrinsic] +unsafe fn simd_select_bitmask(m: T, a: U, b: U) -> U; + #[derive(Copy, Clone)] #[repr(simd)] diff --git a/tests/ui/simd/target-feature-mixup.rs b/tests/ui/simd/target-feature-mixup.rs index 62d87c3a6dc6c..2786251c7951b 100644 --- a/tests/ui/simd/target-feature-mixup.rs +++ b/tests/ui/simd/target-feature-mixup.rs @@ -3,8 +3,7 @@ #![allow(stable_features)] #![allow(overflowing_literals)] -//@ ignore-wasm32 no subprocess support -//@ ignore-sgx no processes +//@ needs-subprocess //@ ignore-fuchsia must translate zircon signal to SIGILL, FIXME (#58590) #![feature(repr_simd, target_feature, cfg_target_feature)] diff --git a/tests/ui/simd/type-generic-monomorphisation-extern-nonnull-ptr.rs b/tests/ui/simd/type-generic-monomorphisation-extern-nonnull-ptr.rs index a969295c9f9cd..65c57c42530a1 100644 --- a/tests/ui/simd/type-generic-monomorphisation-extern-nonnull-ptr.rs +++ b/tests/ui/simd/type-generic-monomorphisation-extern-nonnull-ptr.rs @@ -6,7 +6,7 @@ use std::ptr::NonNull; -extern { +extern "C" { type Extern; } diff --git a/tests/ui/span/issue-34264.stderr b/tests/ui/span/issue-34264.stderr index b581cdd0be24f..c8046a1bddf85 100644 --- a/tests/ui/span/issue-34264.stderr +++ b/tests/ui/span/issue-34264.stderr @@ -60,7 +60,7 @@ note: function defined here --> $DIR/issue-34264.rs:1:4 | LL | fn foo(Option, String) {} - | ^^^ ----------- ------ + | ^^^ help: remove the extra argument | LL - foo(Some(42), 2, ""); @@ -91,7 +91,7 @@ note: function defined here --> $DIR/issue-34264.rs:3:4 | LL | fn bar(x, y: usize) {} - | ^^^ - -------- + | ^^^ help: remove the extra argument | LL - bar(1, 2, 3); diff --git a/tests/ui/span/issue-35987.stderr b/tests/ui/span/issue-35987.stderr index 36c59137b3125..634bb5ed68f54 100644 --- a/tests/ui/span/issue-35987.stderr +++ b/tests/ui/span/issue-35987.stderr @@ -17,10 +17,12 @@ LL | fn add(self, rhs: Self) -> Self::Output { | help: use fully-qualified syntax | -LL | fn add(self, rhs: Self) -> as BitOr>::Output { - | ~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | fn add(self, rhs: Self) -> as IntoFuture>::Output { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - fn add(self, rhs: Self) -> Self::Output { +LL + fn add(self, rhs: Self) -> as BitOr>::Output { + | +LL - fn add(self, rhs: Self) -> Self::Output { +LL + fn add(self, rhs: Self) -> as IntoFuture>::Output { + | error: aborting due to 2 previous errors diff --git a/tests/ui/span/issue-37767.stderr b/tests/ui/span/issue-37767.stderr index 457870821a19a..2bb64e3da8617 100644 --- a/tests/ui/span/issue-37767.stderr +++ b/tests/ui/span/issue-37767.stderr @@ -16,12 +16,14 @@ LL | fn foo(&mut self) {} | ^^^^^^^^^^^^^^^^^ help: disambiguate the method for candidate #1 | -LL | A::foo(&mut a) - | ~~~~~~~~~~~~~~ +LL - a.foo() +LL + A::foo(&mut a) + | help: disambiguate the method for candidate #2 | -LL | B::foo(&mut a) - | ~~~~~~~~~~~~~~ +LL - a.foo() +LL + B::foo(&mut a) + | error[E0034]: multiple applicable items in scope --> $DIR/issue-37767.rs:22:7 @@ -41,12 +43,14 @@ LL | fn foo(&self) {} | ^^^^^^^^^^^^^ help: disambiguate the method for candidate #1 | -LL | C::foo(&a) - | ~~~~~~~~~~ +LL - a.foo() +LL + C::foo(&a) + | help: disambiguate the method for candidate #2 | -LL | D::foo(&a) - | ~~~~~~~~~~ +LL - a.foo() +LL + D::foo(&a) + | error[E0034]: multiple applicable items in scope --> $DIR/issue-37767.rs:34:7 @@ -66,12 +70,14 @@ LL | fn foo(self) {} | ^^^^^^^^^^^^ help: disambiguate the method for candidate #1 | -LL | E::foo(a) - | ~~~~~~~~~ +LL - a.foo() +LL + E::foo(a) + | help: disambiguate the method for candidate #2 | -LL | F::foo(a) - | ~~~~~~~~~ +LL - a.foo() +LL + F::foo(a) + | error: aborting due to 3 previous errors diff --git a/tests/ui/span/issue-42234-unknown-receiver-type.generic_arg.stderr b/tests/ui/span/issue-42234-unknown-receiver-type.generic_arg.stderr index a4b6525657406..6559845c23ec5 100644 --- a/tests/ui/span/issue-42234-unknown-receiver-type.generic_arg.stderr +++ b/tests/ui/span/issue-42234-unknown-receiver-type.generic_arg.stderr @@ -17,10 +17,6 @@ error[E0282]: type annotations needed LL | .sum::<_>() | ^^^ cannot infer type of the type parameter `S` declared on the method `sum` | -help: consider specifying the generic argument - | -LL | .sum::() - | ~~~~~ error: aborting due to 2 previous errors diff --git a/tests/ui/span/issue-81800.stderr b/tests/ui/span/issue-81800.stderr index 86c64573b140f..c1583c3ef8f88 100644 --- a/tests/ui/span/issue-81800.stderr +++ b/tests/ui/span/issue-81800.stderr @@ -6,8 +6,9 @@ LL | fn x˂- | help: Unicode character '˂' (Modifier Letter Left Arrowhead) looks like '<' (Less-Than Sign), but it is not | -LL | fn x<- - | ~ +LL - fn x˂- +LL + fn x<- + | error: expected one of `#`, `>`, `const`, identifier, or lifetime, found `-` --> $DIR/issue-81800.rs:1:6 diff --git a/tests/ui/span/missing-unit-argument.stderr b/tests/ui/span/missing-unit-argument.stderr index 79980f48ab6bc..e77ec3c8447a5 100644 --- a/tests/ui/span/missing-unit-argument.stderr +++ b/tests/ui/span/missing-unit-argument.stderr @@ -8,8 +8,9 @@ note: tuple variant defined here --> $SRC_DIR/core/src/result.rs:LL:COL help: provide the argument | -LL | let _: Result<(), String> = Ok(()); - | ~~~~ +LL - let _: Result<(), String> = Ok(); +LL + let _: Result<(), String> = Ok(()); + | error[E0061]: this function takes 2 arguments but 0 arguments were supplied --> $DIR/missing-unit-argument.rs:12:5 @@ -24,8 +25,9 @@ LL | fn foo(():(), ():()) {} | ^^^ ----- ----- help: provide the arguments | -LL | foo((), ()); - | ~~~~~~~~ +LL - foo(); +LL + foo((), ()); + | error[E0061]: this function takes 2 arguments but 1 argument was supplied --> $DIR/missing-unit-argument.rs:13:5 @@ -37,11 +39,12 @@ note: function defined here --> $DIR/missing-unit-argument.rs:1:4 | LL | fn foo(():(), ():()) {} - | ^^^ ----- ----- + | ^^^ ----- help: provide the argument | -LL | foo((), ()); - | ~~~~~~~~ +LL - foo(()); +LL + foo((), ()); + | error[E0061]: this function takes 1 argument but 0 arguments were supplied --> $DIR/missing-unit-argument.rs:14:5 @@ -56,8 +59,9 @@ LL | fn bar(():()) {} | ^^^ ----- help: provide the argument | -LL | bar(()); - | ~~~~ +LL - bar(); +LL + bar(()); + | error[E0061]: this method takes 1 argument but 0 arguments were supplied --> $DIR/missing-unit-argument.rs:15:7 @@ -72,8 +76,9 @@ LL | fn baz(self, (): ()) { } | ^^^ ------ help: provide the argument | -LL | S.baz(()); - | ~~~~ +LL - S.baz(); +LL + S.baz(()); + | error[E0061]: this method takes 1 argument but 0 arguments were supplied --> $DIR/missing-unit-argument.rs:16:7 @@ -88,8 +93,9 @@ LL | fn generic(self, _: T) { } | ^^^^^^^ ---- help: provide the argument | -LL | S.generic::<()>(()); - | ~~~~ +LL - S.generic::<()>(); +LL + S.generic::<()>(()); + | error: aborting due to 6 previous errors diff --git a/tests/ui/specialization/fuzzed/fuzzing-ice-134905.rs b/tests/ui/specialization/fuzzed/fuzzing-ice-134905.rs new file mode 100644 index 0000000000000..559c23455275b --- /dev/null +++ b/tests/ui/specialization/fuzzed/fuzzing-ice-134905.rs @@ -0,0 +1,22 @@ +// This test previously tried to use a tainted `EvalCtxt` when emitting +// an error during coherence. +#![feature(specialization)] +//~^ WARN the feature `specialization` is incomplete +trait Iterate<'a> { + type Ty: Valid; +} +impl<'a, T> Iterate<'a> for T +where + T: Check, +{ + default type Ty = (); + //~^ ERROR the trait bound `(): Valid` is not satisfied +} + +trait Check {} +impl<'a, T> Eq for T where >::Ty: Valid {} +//~^ ERROR type parameter `T` must be used as the type parameter for some local type + +trait Valid {} + +fn main() {} diff --git a/tests/ui/specialization/fuzzed/fuzzing-ice-134905.stderr b/tests/ui/specialization/fuzzed/fuzzing-ice-134905.stderr new file mode 100644 index 0000000000000..611fef1df66bf --- /dev/null +++ b/tests/ui/specialization/fuzzed/fuzzing-ice-134905.stderr @@ -0,0 +1,40 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/fuzzing-ice-134905.rs:3:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #31844 for more information + = help: consider using `min_specialization` instead, which is more stable and complete + = note: `#[warn(incomplete_features)]` on by default + +error[E0277]: the trait bound `(): Valid` is not satisfied + --> $DIR/fuzzing-ice-134905.rs:12:23 + | +LL | default type Ty = (); + | ^^ the trait `Valid` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/fuzzing-ice-134905.rs:20:1 + | +LL | trait Valid {} + | ^^^^^^^^^^^ +note: required by a bound in `Iterate::Ty` + --> $DIR/fuzzing-ice-134905.rs:6:14 + | +LL | type Ty: Valid; + | ^^^^^ required by this bound in `Iterate::Ty` + +error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct`) + --> $DIR/fuzzing-ice-134905.rs:17:10 + | +LL | impl<'a, T> Eq for T where >::Ty: Valid {} + | ^ type parameter `T` must be used as the type parameter for some local type + | + = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local + = note: only traits defined in the current crate can be implemented for a type parameter + +error: aborting due to 2 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0210, E0277. +For more information about an error, try `rustc --explain E0210`. diff --git a/tests/ui/specialization/min_specialization/issue-79224.stderr b/tests/ui/specialization/min_specialization/issue-79224.stderr index b2118ccd81b67..2ed614f1857d4 100644 --- a/tests/ui/specialization/min_specialization/issue-79224.stderr +++ b/tests/ui/specialization/min_specialization/issue-79224.stderr @@ -35,13 +35,10 @@ LL | impl Display for Cow<'_, B> { | +++++++++++++++++++ error[E0277]: the trait bound `B: Clone` is not satisfied - --> $DIR/issue-79224.rs:30:62 + --> $DIR/issue-79224.rs:30:12 | -LL | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - | ______________________________________________________________^ -... | -LL | | } - | |_____^ the trait `Clone` is not implemented for `B` +LL | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + | ^^^^^ the trait `Clone` is not implemented for `B` | = note: required for `B` to implement `ToOwned` help: consider further restricting type parameter `B` with trait `Clone` diff --git a/tests/ui/specialization/specialization-default-items-drop-coherence.coherence.stderr b/tests/ui/specialization/specialization-default-items-drop-coherence.coherence.stderr deleted file mode 100644 index e9498a003179b..0000000000000 --- a/tests/ui/specialization/specialization-default-items-drop-coherence.coherence.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0119]: conflicting implementations of trait `Overlap` for type `u32` - --> $DIR/specialization-default-items-drop-coherence.rs:29:1 - | -LL | impl Overlap for u32 { - | -------------------- first implementation here -... -LL | impl Overlap for ::Id { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/specialization/specialization-overlap-projection.current.stderr b/tests/ui/specialization/specialization-overlap-projection.current.stderr deleted file mode 100644 index 4e77cb17fbb0a..0000000000000 --- a/tests/ui/specialization/specialization-overlap-projection.current.stderr +++ /dev/null @@ -1,30 +0,0 @@ -warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/specialization-overlap-projection.rs:4:12 - | -LL | #![feature(specialization)] - | ^^^^^^^^^^^^^^ - | - = note: see issue #31844 for more information - = help: consider using `min_specialization` instead, which is more stable and complete - = note: `#[warn(incomplete_features)]` on by default - -error[E0119]: conflicting implementations of trait `Foo` for type `u32` - --> $DIR/specialization-overlap-projection.rs:19:1 - | -LL | impl Foo for u32 {} - | ---------------- first implementation here -LL | impl Foo for ::Output {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32` - -error[E0119]: conflicting implementations of trait `Foo` for type `u32` - --> $DIR/specialization-overlap-projection.rs:21:1 - | -LL | impl Foo for u32 {} - | ---------------- first implementation here -... -LL | impl Foo for ::Output {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32` - -error: aborting due to 2 previous errors; 1 warning emitted - -For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/specialization/specialization-overlap-projection.next.stderr b/tests/ui/specialization/specialization-overlap-projection.next.stderr deleted file mode 100644 index 4e77cb17fbb0a..0000000000000 --- a/tests/ui/specialization/specialization-overlap-projection.next.stderr +++ /dev/null @@ -1,30 +0,0 @@ -warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/specialization-overlap-projection.rs:4:12 - | -LL | #![feature(specialization)] - | ^^^^^^^^^^^^^^ - | - = note: see issue #31844 for more information - = help: consider using `min_specialization` instead, which is more stable and complete - = note: `#[warn(incomplete_features)]` on by default - -error[E0119]: conflicting implementations of trait `Foo` for type `u32` - --> $DIR/specialization-overlap-projection.rs:19:1 - | -LL | impl Foo for u32 {} - | ---------------- first implementation here -LL | impl Foo for ::Output {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32` - -error[E0119]: conflicting implementations of trait `Foo` for type `u32` - --> $DIR/specialization-overlap-projection.rs:21:1 - | -LL | impl Foo for u32 {} - | ---------------- first implementation here -... -LL | impl Foo for ::Output {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32` - -error: aborting due to 2 previous errors; 1 warning emitted - -For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/sse-abi-checks.rs b/tests/ui/sse-abi-checks.rs index c453e91d11b2a..d400e6eb698e9 100644 --- a/tests/ui/sse-abi-checks.rs +++ b/tests/ui/sse-abi-checks.rs @@ -1,6 +1,6 @@ //! Ensure we trigger abi_unsupported_vector_types for target features that are usually enabled //! on a target, but disabled in this file via a `-C` flag. -//@ compile-flags: --crate-type=rlib --target=i686-unknown-linux-gnu -C target-feature=-sse,-sse2 +//@ compile-flags: --crate-type=rlib --target=i586-unknown-linux-gnu -C target-feature=-sse,-sse2 //@ build-pass //@ ignore-pass (test emits codegen-time warnings) //@ needs-llvm-components: x86 diff --git a/tests/ui/stability-attribute/accidental-stable-in-unstable.rs b/tests/ui/stability-attribute/accidental-stable-in-unstable.rs index 86a9d2066eb58..a36a78ee442ce 100644 --- a/tests/ui/stability-attribute/accidental-stable-in-unstable.rs +++ b/tests/ui/stability-attribute/accidental-stable-in-unstable.rs @@ -8,3 +8,4 @@ use core::unicode::UNICODE_VERSION; //~ ERROR use of unstable library feature `u // Known accidental stabilizations with known users // fully stable @ core::mem::transmute use core::intrinsics::transmute; // depended upon by rand_core +//~^WARN deprecated diff --git a/tests/ui/stability-attribute/accidental-stable-in-unstable.stderr b/tests/ui/stability-attribute/accidental-stable-in-unstable.stderr index 9943e6d7ac6ab..16e3676aa6503 100644 --- a/tests/ui/stability-attribute/accidental-stable-in-unstable.stderr +++ b/tests/ui/stability-attribute/accidental-stable-in-unstable.stderr @@ -7,6 +7,14 @@ LL | use core::unicode::UNICODE_VERSION; = help: add `#![feature(unicode_internals)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 1 previous error +warning: use of deprecated module `std::intrinsics`: import this function via `std::mem` instead + --> $DIR/accidental-stable-in-unstable.rs:10:23 + | +LL | use core::intrinsics::transmute; // depended upon by rand_core + | ^^^^^^^^^ + | + = note: `#[warn(deprecated)]` on by default + +error: aborting due to 1 previous error; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/stability-attribute/allowed-through-unstable.rs b/tests/ui/stability-attribute/allowed-through-unstable.rs index 29911a70be942..5baa0fda94037 100644 --- a/tests/ui/stability-attribute/allowed-through-unstable.rs +++ b/tests/ui/stability-attribute/allowed-through-unstable.rs @@ -5,5 +5,5 @@ extern crate allowed_through_unstable_core; -use allowed_through_unstable_core::unstable_module::OldStableTraitAllowedThoughUnstable; +use allowed_through_unstable_core::unstable_module::OldStableTraitAllowedThoughUnstable; //~WARN use of deprecated module `allowed_through_unstable_core::unstable_module`: use the new path instead use allowed_through_unstable_core::unstable_module::NewStableTraitNotAllowedThroughUnstable; //~ ERROR use of unstable library feature `unstable_test_feature` diff --git a/tests/ui/stability-attribute/allowed-through-unstable.stderr b/tests/ui/stability-attribute/allowed-through-unstable.stderr index 00eea9f730d66..bda68045002e4 100644 --- a/tests/ui/stability-attribute/allowed-through-unstable.stderr +++ b/tests/ui/stability-attribute/allowed-through-unstable.stderr @@ -1,3 +1,11 @@ +warning: use of deprecated module `allowed_through_unstable_core::unstable_module`: use the new path instead + --> $DIR/allowed-through-unstable.rs:8:53 + | +LL | use allowed_through_unstable_core::unstable_module::OldStableTraitAllowedThoughUnstable; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(deprecated)]` on by default + error[E0658]: use of unstable library feature `unstable_test_feature` --> $DIR/allowed-through-unstable.rs:9:5 | @@ -8,6 +16,6 @@ LL | use allowed_through_unstable_core::unstable_module::NewStableTraitNotAllowe = help: add `#![feature(unstable_test_feature)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 1 previous error +error: aborting due to 1 previous error; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/stability-attribute/auxiliary/allowed-through-unstable-core.rs b/tests/ui/stability-attribute/auxiliary/allowed-through-unstable-core.rs index b597009a309c9..23c722d6e8eba 100644 --- a/tests/ui/stability-attribute/auxiliary/allowed-through-unstable-core.rs +++ b/tests/ui/stability-attribute/auxiliary/allowed-through-unstable-core.rs @@ -6,7 +6,7 @@ #[unstable(feature = "unstable_test_feature", issue = "1")] pub mod unstable_module { #[stable(feature = "stable_test_feature", since = "1.2.0")] - #[rustc_allowed_through_unstable_modules] + #[rustc_allowed_through_unstable_modules = "use the new path instead"] pub trait OldStableTraitAllowedThoughUnstable {} #[stable(feature = "stable_test_feature", since = "1.2.0")] diff --git a/tests/ui/stability-attribute/const-stability-attribute-implies-no-feature.stderr b/tests/ui/stability-attribute/const-stability-attribute-implies-no-feature.stderr index 0a5f58288fa33..0f2006e932b34 100644 --- a/tests/ui/stability-attribute/const-stability-attribute-implies-no-feature.stderr +++ b/tests/ui/stability-attribute/const-stability-attribute-implies-no-feature.stderr @@ -4,7 +4,10 @@ error: `foobar` is not yet stable as a const fn LL | foobar(); | ^^^^^^^^ | - = help: add `#![feature(const_foobar)]` to the crate attributes to enable +help: add `#![feature(const_foobar)]` to the crate attributes to enable + | +LL + #![feature(const_foobar)] + | error: aborting due to 1 previous error diff --git a/tests/ui/stability-attribute/const-stability-attribute-implies-using-stable.stderr b/tests/ui/stability-attribute/const-stability-attribute-implies-using-stable.stderr index 050834ab67609..34bf1c6c10ac4 100644 --- a/tests/ui/stability-attribute/const-stability-attribute-implies-using-stable.stderr +++ b/tests/ui/stability-attribute/const-stability-attribute-implies-using-stable.stderr @@ -12,7 +12,7 @@ LL | #![deny(stable_features)] help: if you are using features which are still unstable, change to using `const_foobar` | LL | #![feature(const_foobar)] - | ~~~~~~~~~~~~ + | +++ help: if you are using features which are now stable, remove this line | LL - #![feature(const_foo)] diff --git a/tests/ui/stability-attribute/const-stability-attribute-implies-using-unstable.stderr b/tests/ui/stability-attribute/const-stability-attribute-implies-using-unstable.stderr index 50cc14c3b4f65..095c37fd0b680 100644 --- a/tests/ui/stability-attribute/const-stability-attribute-implies-using-unstable.stderr +++ b/tests/ui/stability-attribute/const-stability-attribute-implies-using-unstable.stderr @@ -12,7 +12,7 @@ LL | #![deny(stable_features)] help: if you are using features which are still unstable, change to using `const_foobar` | LL | #![feature(const_foobar)] - | ~~~~~~~~~~~~ + | +++ help: if you are using features which are now stable, remove this line | LL - #![feature(const_foo)] diff --git a/tests/ui/stability-attribute/missing-const-stability.rs b/tests/ui/stability-attribute/missing-const-stability.rs index 1982073073621..c3e72e8394880 100644 --- a/tests/ui/stability-attribute/missing-const-stability.rs +++ b/tests/ui/stability-attribute/missing-const-stability.rs @@ -22,6 +22,7 @@ impl Foo { #[stable(feature = "stable", since = "1.0.0")] #[const_trait] pub trait Bar { +//~^ ERROR trait has missing const stability attribute #[stable(feature = "stable", since = "1.0.0")] fn fun(); } diff --git a/tests/ui/stability-attribute/missing-const-stability.stderr b/tests/ui/stability-attribute/missing-const-stability.stderr index baa4c34af0600..09461e6fb54d6 100644 --- a/tests/ui/stability-attribute/missing-const-stability.stderr +++ b/tests/ui/stability-attribute/missing-const-stability.stderr @@ -4,8 +4,18 @@ error: function has missing const stability attribute LL | pub const fn foo() {} | ^^^^^^^^^^^^^^^^^^^^^ +error: trait has missing const stability attribute + --> $DIR/missing-const-stability.rs:24:1 + | +LL | / pub trait Bar { +LL | | +LL | | #[stable(feature = "stable", since = "1.0.0")] +LL | | fn fun(); +LL | | } + | |_^ + error: function has missing const stability attribute - --> $DIR/missing-const-stability.rs:36:1 + --> $DIR/missing-const-stability.rs:37:1 | LL | pub const unsafe fn size_of_val(x: *const T) -> usize { 42 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,5 +26,5 @@ error: associated function has missing const stability attribute LL | pub const fn foo() {} | ^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/stability-attribute/stability-attribute-implies-using-stable.stderr b/tests/ui/stability-attribute/stability-attribute-implies-using-stable.stderr index d783f1e8e4044..86cb764a4b3e7 100644 --- a/tests/ui/stability-attribute/stability-attribute-implies-using-stable.stderr +++ b/tests/ui/stability-attribute/stability-attribute-implies-using-stable.stderr @@ -12,7 +12,7 @@ LL | #![deny(stable_features)] help: if you are using features which are still unstable, change to using `foobar` | LL | #![feature(foobar)] - | ~~~~~~ + | +++ help: if you are using features which are now stable, remove this line | LL - #![feature(foo)] diff --git a/tests/ui/stability-attribute/stability-attribute-implies-using-unstable.stderr b/tests/ui/stability-attribute/stability-attribute-implies-using-unstable.stderr index 4940650fd4261..2537646eb9835 100644 --- a/tests/ui/stability-attribute/stability-attribute-implies-using-unstable.stderr +++ b/tests/ui/stability-attribute/stability-attribute-implies-using-unstable.stderr @@ -12,7 +12,7 @@ LL | #![deny(stable_features)] help: if you are using features which are still unstable, change to using `foobar` | LL | #![feature(foobar)] - | ~~~~~~ + | +++ help: if you are using features which are now stable, remove this line | LL - #![feature(foo)] diff --git a/tests/ui/static/static-reference-to-fn-1.stderr b/tests/ui/static/static-reference-to-fn-1.stderr index 6bf64974ef59b..72036269b296c 100644 --- a/tests/ui/static/static-reference-to-fn-1.stderr +++ b/tests/ui/static/static-reference-to-fn-1.stderr @@ -9,8 +9,9 @@ LL | func: &foo, = note: fn items are distinct from fn pointers help: consider casting to a fn pointer | -LL | func: &(foo as fn() -> Option), - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - func: &foo, +LL + func: &(foo as fn() -> Option), + | error: aborting due to 1 previous error diff --git a/tests/ui/statics/issue-15261.stderr b/tests/ui/statics/issue-15261.stderr index 4067d151de3d4..7e6aebcbb1f0e 100644 --- a/tests/ui/statics/issue-15261.stderr +++ b/tests/ui/statics/issue-15261.stderr @@ -10,7 +10,7 @@ LL | static n: &'static usize = unsafe { &n_mut }; help: use `&raw const` instead to create a raw pointer | LL | static n: &'static usize = unsafe { &raw const n_mut }; - | ~~~~~~~~~~ + | +++++++++ warning: 1 warning emitted diff --git a/tests/ui/statics/static-mut-shared-parens.stderr b/tests/ui/statics/static-mut-shared-parens.stderr index aa7a760ded875..3d4b55909cdee 100644 --- a/tests/ui/statics/static-mut-shared-parens.stderr +++ b/tests/ui/statics/static-mut-shared-parens.stderr @@ -10,7 +10,7 @@ LL | let _ = unsafe { (&TEST) as *const usize }; help: use `&raw const` instead to create a raw pointer | LL | let _ = unsafe { (&raw const TEST) as *const usize }; - | ~~~~~~~~~~ + | +++++++++ warning: creating a mutable reference to mutable static is discouraged --> $DIR/static-mut-shared-parens.rs:11:22 @@ -22,8 +22,9 @@ LL | let _ = unsafe { ((&mut TEST)) as *const usize }; = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives help: use `&raw mut` instead to create a raw pointer | -LL | let _ = unsafe { ((&raw mut TEST)) as *const usize }; - | ~~~~~~~~ +LL - let _ = unsafe { ((&mut TEST)) as *const usize }; +LL + let _ = unsafe { ((&raw mut TEST)) as *const usize }; + | warning: 2 warnings emitted diff --git a/tests/ui/statics/static-mut-xc.stderr b/tests/ui/statics/static-mut-xc.stderr index 176deb518fce0..48cac28a6eb51 100644 --- a/tests/ui/statics/static-mut-xc.stderr +++ b/tests/ui/statics/static-mut-xc.stderr @@ -55,7 +55,7 @@ LL | static_bound(&static_mut_xc::a); help: use `&raw const` instead to create a raw pointer | LL | static_bound(&raw const static_mut_xc::a); - | ~~~~~~~~~~ + | +++++++++ warning: creating a mutable reference to mutable static is discouraged --> $DIR/static-mut-xc.rs:35:22 @@ -67,8 +67,9 @@ LL | static_bound_set(&mut static_mut_xc::a); = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives help: use `&raw mut` instead to create a raw pointer | -LL | static_bound_set(&raw mut static_mut_xc::a); - | ~~~~~~~~ +LL - static_bound_set(&mut static_mut_xc::a); +LL + static_bound_set(&raw mut static_mut_xc::a); + | warning: 7 warnings emitted diff --git a/tests/ui/statics/static-recursive.stderr b/tests/ui/statics/static-recursive.stderr index f2dd5b8a6cfe8..039934dfc692d 100644 --- a/tests/ui/statics/static-recursive.stderr +++ b/tests/ui/statics/static-recursive.stderr @@ -10,7 +10,7 @@ LL | static mut S: *const u8 = unsafe { &S as *const *const u8 as *const u8 }; help: use `&raw const` instead to create a raw pointer | LL | static mut S: *const u8 = unsafe { &raw const S as *const *const u8 as *const u8 }; - | ~~~~~~~~~~ + | +++++++++ warning: creating a shared reference to mutable static is discouraged --> $DIR/static-recursive.rs:19:20 diff --git a/tests/ui/statics/uninhabited-static.rs b/tests/ui/statics/uninhabited-static.rs index a0f83f450792a..0f7c5ae6ef4a3 100644 --- a/tests/ui/statics/uninhabited-static.rs +++ b/tests/ui/statics/uninhabited-static.rs @@ -2,7 +2,7 @@ #![deny(uninhabited_static)] enum Void {} -extern { +extern "C" { static VOID: Void; //~ ERROR static of uninhabited type //~| WARN: previously accepted static NEVER: !; //~ ERROR static of uninhabited type diff --git a/tests/ui/statics/unsizing-wfcheck-issue-127299.rs b/tests/ui/statics/unsizing-wfcheck-issue-127299.rs index cd15be54ec785..fd07937d90f24 100644 --- a/tests/ui/statics/unsizing-wfcheck-issue-127299.rs +++ b/tests/ui/statics/unsizing-wfcheck-issue-127299.rs @@ -6,12 +6,12 @@ trait Qux { pub struct Lint { pub desc: &'static dyn Qux, - //~^ ERROR cannot be made into an object + //~^ ERROR is not dyn compatible } static FOO: &Lint = &Lint { desc: "desc" }; //~^ ERROR cannot be shared between threads safely -//~| ERROR cannot be made into an object -//~| ERROR cannot be made into an object +//~| ERROR is not dyn compatible +//~| ERROR is not dyn compatible fn main() {} diff --git a/tests/ui/statics/unsizing-wfcheck-issue-127299.stderr b/tests/ui/statics/unsizing-wfcheck-issue-127299.stderr index 35dd570e91fbb..28427161e870c 100644 --- a/tests/ui/statics/unsizing-wfcheck-issue-127299.stderr +++ b/tests/ui/statics/unsizing-wfcheck-issue-127299.stderr @@ -1,14 +1,15 @@ -error[E0038]: the trait `Qux` cannot be made into an object +error[E0038]: the trait `Qux` is not dyn compatible --> $DIR/unsizing-wfcheck-issue-127299.rs:8:24 | LL | pub desc: &'static dyn Qux, - | ^^^^^^^ `Qux` cannot be made into an object + | ^^^^^^^ `Qux` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/unsizing-wfcheck-issue-127299.rs:4:8 | LL | trait Qux { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn bar() -> i32; | ^^^ ...because associated function `bar` has no `self` parameter help: consider turning `bar` into a method by giving it a `&self` argument @@ -36,17 +37,18 @@ LL | pub struct Lint { = note: required because it appears within the type `&'static Lint` = note: shared static variables must have a type that implements `Sync` -error[E0038]: the trait `Qux` cannot be made into an object +error[E0038]: the trait `Qux` is not dyn compatible --> $DIR/unsizing-wfcheck-issue-127299.rs:12:35 | LL | static FOO: &Lint = &Lint { desc: "desc" }; - | ^^^^^^ `Qux` cannot be made into an object + | ^^^^^^ `Qux` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/unsizing-wfcheck-issue-127299.rs:4:8 | LL | trait Qux { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn bar() -> i32; | ^^^ ...because associated function `bar` has no `self` parameter = note: required for the cast from `&'static str` to `&'static (dyn Qux + 'static)` @@ -59,17 +61,18 @@ help: alternatively, consider constraining `bar` so it does not apply to trait o LL | fn bar() -> i32 where Self: Sized; | +++++++++++++++++ -error[E0038]: the trait `Qux` cannot be made into an object +error[E0038]: the trait `Qux` is not dyn compatible --> $DIR/unsizing-wfcheck-issue-127299.rs:12:35 | LL | static FOO: &Lint = &Lint { desc: "desc" }; - | ^^^^^^ `Qux` cannot be made into an object + | ^^^^^^ `Qux` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/unsizing-wfcheck-issue-127299.rs:4:8 | LL | trait Qux { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn bar() -> i32; | ^^^ ...because associated function `bar` has no `self` parameter help: consider turning `bar` into a method by giving it a `&self` argument diff --git a/tests/ui/std/thread-sleep-ms.rs b/tests/ui/std/thread-sleep-ms.rs index 0a3d0253a20ef..2d668b8265c27 100644 --- a/tests/ui/std/thread-sleep-ms.rs +++ b/tests/ui/std/thread-sleep-ms.rs @@ -1,12 +1,9 @@ //@ run-pass -//@ ignore-sgx not supported -//@ ignore-emscripten -// FIXME: test hangs on emscripten -#![allow(deprecated)] -#![allow(unused_imports)] +//@ needs-threads +//@ ignore-emscripten (FIXME: test hangs on emscripten) -use std::thread; +#![allow(deprecated)] fn main() { - thread::sleep_ms(250); + std::thread::sleep_ms(250); } diff --git a/tests/ui/stdio-is-blocking.rs b/tests/ui/stdio-is-blocking.rs index dda100951dded..615530dcd47e4 100644 --- a/tests/ui/stdio-is-blocking.rs +++ b/tests/ui/stdio-is-blocking.rs @@ -1,6 +1,5 @@ //@ run-pass -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess use std::env; use std::io::prelude::*; diff --git a/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.alignment_mismatch.stderr b/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.alignment_mismatch.stderr index 47b17f9fcd7ad..6a8a7b152c547 100644 --- a/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.alignment_mismatch.stderr +++ b/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.alignment_mismatch.stderr @@ -9,8 +9,9 @@ note: if you're trying to build a new `AtomicU64`, consider using `AtomicU64::ne = note: this error originates in the macro `atomic_int` (in Nightly builds, run with -Z macro-backtrace for more info) help: there is an associated function `from` with a similar name | -LL | core::sync::atomic::AtomicU64::from(&mut 0u64); - | ~~~~ +LL - core::sync::atomic::AtomicU64::from_mut(&mut 0u64); +LL + core::sync::atomic::AtomicU64::from(&mut 0u64); + | error: aborting due to 1 previous error diff --git a/tests/ui/str/str-as-char.stderr b/tests/ui/str/str-as-char.stderr index 0638d371c173b..b2c716fa4f56f 100644 --- a/tests/ui/str/str-as-char.stderr +++ b/tests/ui/str/str-as-char.stderr @@ -6,8 +6,9 @@ LL | println!('●●'); | help: if you meant to write a string literal, use double quotes | -LL | println!("●●"); - | ~ ~ +LL - println!('●●'); +LL + println!("●●"); + | error: aborting due to 1 previous error diff --git a/tests/ui/str/str-overrun.rs b/tests/ui/str/str-overrun.rs index b8e245475da92..6d62b837694b0 100644 --- a/tests/ui/str/str-overrun.rs +++ b/tests/ui/str/str-overrun.rs @@ -1,6 +1,6 @@ //@ run-fail //@ error-pattern:index out of bounds: the len is 5 but the index is 5 -//@ ignore-emscripten no processes +//@ needs-subprocess fn main() { let s: String = "hello".to_string(); diff --git a/tests/ui/structs-enums/class-impl-very-parameterized-trait.rs b/tests/ui/structs-enums/class-impl-very-parameterized-trait.rs index 0b37192fc3b34..6caec1e2034ef 100644 --- a/tests/ui/structs-enums/class-impl-very-parameterized-trait.rs +++ b/tests/ui/structs-enums/class-impl-very-parameterized-trait.rs @@ -102,6 +102,6 @@ pub fn main() { let mut spotty: cat = cat::new(2, 57, cat_type::tuxedo); for _ in 0_usize..6 { spotty.speak(); } assert_eq!(spotty.len(), 8); - assert!((spotty.contains_key(&2))); + assert!(spotty.contains_key(&2)); assert_eq!(spotty.get(&3), &cat_type::tuxedo); } diff --git a/tests/ui/structs-enums/class-implement-trait-cross-crate.rs b/tests/ui/structs-enums/class-implement-trait-cross-crate.rs index 7a5969451cb52..781ac6ad10d2c 100644 --- a/tests/ui/structs-enums/class-implement-trait-cross-crate.rs +++ b/tests/ui/structs-enums/class-implement-trait-cross-crate.rs @@ -53,7 +53,7 @@ fn cat(in_x : usize, in_y : isize, in_name: String) -> cat { pub fn main() { let mut nyan = cat(0_usize, 2, "nyan".to_string()); nyan.eat(); - assert!((!nyan.eat())); + assert!(!nyan.eat()); for _ in 1_usize..10_usize { nyan.speak(); }; - assert!((nyan.eat())); + assert!(nyan.eat()); } diff --git a/tests/ui/structs-enums/class-implement-traits.rs b/tests/ui/structs-enums/class-implement-traits.rs index 04a7b706edbce..3a514ff9d7581 100644 --- a/tests/ui/structs-enums/class-implement-traits.rs +++ b/tests/ui/structs-enums/class-implement-traits.rs @@ -57,7 +57,7 @@ fn make_speak(mut c: C) { pub fn main() { let mut nyan = cat(0_usize, 2, "nyan".to_string()); nyan.eat(); - assert!((!nyan.eat())); + assert!(!nyan.eat()); for _ in 1_usize..10_usize { make_speak(nyan.clone()); } diff --git a/tests/ui/structs-enums/classes-cross-crate.rs b/tests/ui/structs-enums/classes-cross-crate.rs index 0160d3fd85c08..6fb5f2e3cc9d7 100644 --- a/tests/ui/structs-enums/classes-cross-crate.rs +++ b/tests/ui/structs-enums/classes-cross-crate.rs @@ -7,7 +7,7 @@ use cci_class_4::kitties::cat; pub fn main() { let mut nyan = cat(0_usize, 2, "nyan".to_string()); nyan.eat(); - assert!((!nyan.eat())); + assert!(!nyan.eat()); for _ in 1_usize..10_usize { nyan.speak(); }; - assert!((nyan.eat())); + assert!(nyan.eat()); } diff --git a/tests/ui/structs-enums/classes.rs b/tests/ui/structs-enums/classes.rs index d1c1922f4b547..05976f6a759af 100644 --- a/tests/ui/structs-enums/classes.rs +++ b/tests/ui/structs-enums/classes.rs @@ -45,7 +45,7 @@ fn cat(in_x : usize, in_y : isize, in_name: String) -> cat { pub fn main() { let mut nyan = cat(0_usize, 2, "nyan".to_string()); nyan.eat(); - assert!((!nyan.eat())); + assert!(!nyan.eat()); for _ in 1_usize..10_usize { nyan.speak(); }; - assert!((nyan.eat())); + assert!(nyan.eat()); } diff --git a/tests/ui/structs-enums/issue-103869.stderr b/tests/ui/structs-enums/issue-103869.stderr index 2334e5e9712c4..10e0f29f8762d 100644 --- a/tests/ui/structs-enums/issue-103869.stderr +++ b/tests/ui/structs-enums/issue-103869.stderr @@ -10,8 +10,9 @@ LL | vec: Vec, = help: enum variants can be `Variant`, `Variant = `, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }` help: perhaps you meant to use `struct` here | -LL | struct VecOrMap { - | ~~~~~~ +LL - enum VecOrMap { +LL + struct VecOrMap { + | error: aborting due to 1 previous error diff --git a/tests/ui/structs-enums/multiple-reprs.rs b/tests/ui/structs-enums/multiple-reprs.rs index 1528db126ae51..ce50b62ce47f7 100644 --- a/tests/ui/structs-enums/multiple-reprs.rs +++ b/tests/ui/structs-enums/multiple-reprs.rs @@ -69,7 +69,7 @@ pub fn main() { assert_eq!(size_of::(), 8); assert_eq!(size_of::(), align_size(10, align_of::())); assert_eq!(size_of::(), align_size(14, align_of::())); - assert_eq!(size_of::(), align_size(6 + size_of::(), align_of::())); + assert_eq!(size_of::(), align_size(6 + c_enum_min_size(), align_of::())); assert_eq!(size_of::(), 21); } @@ -80,3 +80,13 @@ fn align_size(size: usize, align: usize) -> usize { size } } + +// this is `TargetOptions.c_enum_min_bits` which is not available as a `cfg` value so we retrieve +// the value at runtime. On most targets this is `sizeof(c_int)` but on `thumb*-none` is 1 byte +fn c_enum_min_size() -> usize { + #[repr(C)] + enum E { + A, + } + size_of::() +} diff --git a/tests/ui/structs-enums/struct-enum-ignoring-field-with-underscore.stderr b/tests/ui/structs-enums/struct-enum-ignoring-field-with-underscore.stderr index 664a00e3303e0..231bada006cdd 100644 --- a/tests/ui/structs-enums/struct-enum-ignoring-field-with-underscore.stderr +++ b/tests/ui/structs-enums/struct-enum-ignoring-field-with-underscore.stderr @@ -6,8 +6,9 @@ LL | if let Some(Foo::Bar {_}) = foo {} | help: to omit remaining fields, use `..` | -LL | if let Some(Foo::Bar {..}) = foo {} - | ~~ +LL - if let Some(Foo::Bar {_}) = foo {} +LL + if let Some(Foo::Bar {..}) = foo {} + | error: aborting due to 1 previous error diff --git a/tests/ui/structs-enums/tag.rs b/tests/ui/structs-enums/tag.rs index 16e6b2341cf61..542b517e66db7 100644 --- a/tests/ui/structs-enums/tag.rs +++ b/tests/ui/structs-enums/tag.rs @@ -25,6 +25,6 @@ impl PartialEq for colour { fn ne(&self, other: &colour) -> bool { !(*self).eq(other) } } -fn f() { let x = colour::red(1, 2); let y = colour::green; assert!((x != y)); } +fn f() { let x = colour::red(1, 2); let y = colour::green; assert!(x != y); } pub fn main() { f(); } diff --git a/tests/ui/structs-enums/unit-like-struct-drop-run.rs b/tests/ui/structs-enums/unit-like-struct-drop-run.rs index 02d14265f3ef2..3d00871837cf8 100644 --- a/tests/ui/structs-enums/unit-like-struct-drop-run.rs +++ b/tests/ui/structs-enums/unit-like-struct-drop-run.rs @@ -1,6 +1,6 @@ //@ run-pass //@ needs-unwind -//@ ignore-emscripten no threads support +//@ needs-threads // Make sure the destructor is run for unit-like structs. diff --git a/tests/ui/structs/default-field-values/empty-struct.rs b/tests/ui/structs/default-field-values/empty-struct.rs new file mode 100644 index 0000000000000..c9cb861ae27cb --- /dev/null +++ b/tests/ui/structs/default-field-values/empty-struct.rs @@ -0,0 +1,21 @@ +#![feature(default_field_values)] + +// If an API wants users to always use `..` even if they specify all the fields, they should use a +// sentinel field. As of now, that field can't be made private so it is only constructable with this +// syntax, but this might change in the future. + +struct A {} +struct B(); +struct C; +struct D { + x: i32, +} +struct E(i32); + +fn main() { + let _ = A { .. }; //~ ERROR has no fields + let _ = B { .. }; //~ ERROR has no fields + let _ = C { .. }; //~ ERROR has no fields + let _ = D { x: 0, .. }; + let _ = E { 0: 0, .. }; +} diff --git a/tests/ui/structs/default-field-values/empty-struct.stderr b/tests/ui/structs/default-field-values/empty-struct.stderr new file mode 100644 index 0000000000000..079e83415b4b8 --- /dev/null +++ b/tests/ui/structs/default-field-values/empty-struct.stderr @@ -0,0 +1,26 @@ +error: `A` has no fields, `..` needs at least one default field in the struct definition + --> $DIR/empty-struct.rs:16:17 + | +LL | let _ = A { .. }; + | - ^^ + | | + | this type has no fields + +error: `B` has no fields, `..` needs at least one default field in the struct definition + --> $DIR/empty-struct.rs:17:17 + | +LL | let _ = B { .. }; + | - ^^ + | | + | this type has no fields + +error: `C` has no fields, `..` needs at least one default field in the struct definition + --> $DIR/empty-struct.rs:18:17 + | +LL | let _ = C { .. }; + | - ^^ + | | + | this type has no fields + +error: aborting due to 3 previous errors + diff --git a/tests/ui/structs/default-field-values/non-exhaustive-ctor.disabled.stderr b/tests/ui/structs/default-field-values/non-exhaustive-ctor.disabled.stderr new file mode 100644 index 0000000000000..c7689cfd323a0 --- /dev/null +++ b/tests/ui/structs/default-field-values/non-exhaustive-ctor.disabled.stderr @@ -0,0 +1,87 @@ +error[E0658]: default values on fields are experimental + --> $DIR/non-exhaustive-ctor.rs:9:22 + | +LL | pub field: () = (), + | ^^^^^ + | + = note: see issue #132162 for more information + = help: add `#![feature(default_field_values)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: default values on fields are experimental + --> $DIR/non-exhaustive-ctor.rs:11:25 + | +LL | pub field1: Priv = Priv, + | ^^^^^^^ + | + = note: see issue #132162 for more information + = help: add `#![feature(default_field_values)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: default values on fields are experimental + --> $DIR/non-exhaustive-ctor.rs:13:25 + | +LL | pub field2: Priv = Priv, + | ^^^^^^^ + | + = note: see issue #132162 for more information + = help: add `#![feature(default_field_values)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0797]: base expression required after `..` + --> $DIR/non-exhaustive-ctor.rs:20:19 + | +LL | let _ = S { .. }; // ok + | ^ + | +help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields + | +LL + #![feature(default_field_values)] + | +help: add a base expression here + | +LL | let _ = S { ../* expr */ }; // ok + | ++++++++++ + +error[E0797]: base expression required after `..` + --> $DIR/non-exhaustive-ctor.rs:22:30 + | +LL | let _ = S { field: (), .. }; // ok + | ^ + | +help: add `#![feature(default_field_values)]` to the crate attributes to enable default values on `struct` fields + | +LL + #![feature(default_field_values)] + | +help: add a base expression here + | +LL | let _ = S { field: (), ../* expr */ }; // ok + | ++++++++++ + +error[E0063]: missing fields `field`, `field1` and `field2` in initializer of `S` + --> $DIR/non-exhaustive-ctor.rs:24:13 + | +LL | let _ = S { }; + | ^ missing `field`, `field1` and `field2` + | +help: all remaining fields have default values, if you added `#![feature(default_field_values)]` to your crate you could use those values with `..` + | +LL - let _ = S { }; +LL + let _ = S { .. }; + | + +error[E0063]: missing fields `field1` and `field2` in initializer of `S` + --> $DIR/non-exhaustive-ctor.rs:26:13 + | +LL | let _ = S { field: () }; + | ^ missing `field1` and `field2` + | +help: all remaining fields have default values, if you added `#![feature(default_field_values)]` to your crate you could use those values with `..` + | +LL | let _ = S { field: (), .. }; + | ++++ + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0063, E0658, E0797. +For more information about an error, try `rustc --explain E0063`. diff --git a/tests/ui/structs/default-field-values/non-exhaustive-ctor.enabled.fixed b/tests/ui/structs/default-field-values/non-exhaustive-ctor.enabled.fixed new file mode 100644 index 0000000000000..7a371f993e809 --- /dev/null +++ b/tests/ui/structs/default-field-values/non-exhaustive-ctor.enabled.fixed @@ -0,0 +1,28 @@ +//@ revisions: enabled disabled +//@[enabled] run-rustfix +#![allow(private_interfaces, dead_code)] +#![cfg_attr(enabled, feature(default_field_values))] +use m::S; + +mod m { + pub struct S { + pub field: () = (), + //[disabled]~^ ERROR default values on fields are experimental + pub field1: Priv = Priv, + //[disabled]~^ ERROR default values on fields are experimental + pub field2: Priv = Priv, + //[disabled]~^ ERROR default values on fields are experimental + } + struct Priv; +} + +fn main() { + let _ = S { .. }; // ok + //[disabled]~^ ERROR base expression required after `..` + let _ = S { field: (), .. }; // ok + //[disabled]~^ ERROR base expression required after `..` + let _ = S { .. }; + //~^ ERROR missing fields `field`, `field1` and `field2` + let _ = S { field: (), .. }; + //~^ ERROR missing fields `field1` and `field2` +} diff --git a/tests/ui/structs/default-field-values/non-exhaustive-ctor.enabled.stderr b/tests/ui/structs/default-field-values/non-exhaustive-ctor.enabled.stderr new file mode 100644 index 0000000000000..d9b8e76aa0de6 --- /dev/null +++ b/tests/ui/structs/default-field-values/non-exhaustive-ctor.enabled.stderr @@ -0,0 +1,26 @@ +error[E0063]: missing fields `field`, `field1` and `field2` in initializer of `S` + --> $DIR/non-exhaustive-ctor.rs:24:13 + | +LL | let _ = S { }; + | ^ missing `field`, `field1` and `field2` + | +help: all remaining fields have default values, you can use those values with `..` + | +LL - let _ = S { }; +LL + let _ = S { .. }; + | + +error[E0063]: missing fields `field1` and `field2` in initializer of `S` + --> $DIR/non-exhaustive-ctor.rs:26:13 + | +LL | let _ = S { field: () }; + | ^ missing `field1` and `field2` + | +help: all remaining fields have default values, you can use those values with `..` + | +LL | let _ = S { field: (), .. }; + | ++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0063`. diff --git a/tests/ui/structs/default-field-values/non-exhaustive-ctor.rs b/tests/ui/structs/default-field-values/non-exhaustive-ctor.rs new file mode 100644 index 0000000000000..b60b219f8bcf0 --- /dev/null +++ b/tests/ui/structs/default-field-values/non-exhaustive-ctor.rs @@ -0,0 +1,28 @@ +//@ revisions: enabled disabled +//@[enabled] run-rustfix +#![allow(private_interfaces, dead_code)] +#![cfg_attr(enabled, feature(default_field_values))] +use m::S; + +mod m { + pub struct S { + pub field: () = (), + //[disabled]~^ ERROR default values on fields are experimental + pub field1: Priv = Priv, + //[disabled]~^ ERROR default values on fields are experimental + pub field2: Priv = Priv, + //[disabled]~^ ERROR default values on fields are experimental + } + struct Priv; +} + +fn main() { + let _ = S { .. }; // ok + //[disabled]~^ ERROR base expression required after `..` + let _ = S { field: (), .. }; // ok + //[disabled]~^ ERROR base expression required after `..` + let _ = S { }; + //~^ ERROR missing fields `field`, `field1` and `field2` + let _ = S { field: () }; + //~^ ERROR missing fields `field1` and `field2` +} diff --git a/tests/ui/structs/default-field-values/visibility.rs b/tests/ui/structs/default-field-values/visibility.rs new file mode 100644 index 0000000000000..ff1245551b0ad --- /dev/null +++ b/tests/ui/structs/default-field-values/visibility.rs @@ -0,0 +1,42 @@ +#![feature(default_field_values)] +pub mod foo { + #[derive(Default)] + pub struct Alpha { + beta: u8 = 42, + gamma: bool = true, + } +} + +mod bar { + use crate::foo::Alpha; + fn baz() { + let _x = Alpha { .. }; + //~^ ERROR fields `beta` and `gamma` of struct `Alpha` are private + let _x = Alpha { + beta: 0, //~ ERROR fields `beta` and `gamma` of struct `Alpha` are private + gamma: false, + }; + let _x = Alpha { + beta: 0, //~ ERROR fields `beta` and `gamma` of struct `Alpha` are private + .. + }; + let _x = Alpha { beta: 0, .. }; + //~^ ERROR fields `beta` and `gamma` of struct `Alpha` are private + let _x = Alpha { beta: 0, ..Default::default() }; + //~^ ERROR fields `beta` and `gamma` of struct `Alpha` are private + } +} + +pub mod baz { + pub struct S { + x: i32 = 1, + } +} +fn main() { + let _a = baz::S { + .. //~ ERROR field `x` of struct `S` is private + }; + let _b = baz::S { + x: 0, //~ ERROR field `x` of struct `S` is private + }; +} diff --git a/tests/ui/structs/default-field-values/visibility.stderr b/tests/ui/structs/default-field-values/visibility.stderr new file mode 100644 index 0000000000000..38b9603325235 --- /dev/null +++ b/tests/ui/structs/default-field-values/visibility.stderr @@ -0,0 +1,61 @@ +error[E0451]: field `x` of struct `S` is private + --> $DIR/visibility.rs:37:9 + | +LL | let _a = baz::S { + | ------ in this type +LL | .. + | ^^ field `x` is private + +error[E0451]: field `x` of struct `S` is private + --> $DIR/visibility.rs:40:9 + | +LL | let _b = baz::S { + | ------ in this type +LL | x: 0, + | ^ private field + +error[E0451]: fields `beta` and `gamma` of struct `Alpha` are private + --> $DIR/visibility.rs:13:26 + | +LL | let _x = Alpha { .. }; + | ^^ fields `beta` and `gamma` are private + +error[E0451]: fields `beta` and `gamma` of struct `Alpha` are private + --> $DIR/visibility.rs:16:13 + | +LL | let _x = Alpha { + | ----- in this type +LL | beta: 0, + | ^^^^ private field +LL | gamma: false, + | ^^^^^ private field + +error[E0451]: fields `beta` and `gamma` of struct `Alpha` are private + --> $DIR/visibility.rs:20:13 + | +LL | let _x = Alpha { + | ----- in this type +LL | beta: 0, + | ^^^^^^^ private field +LL | .. + | ^^ field `gamma` is private + +error[E0451]: fields `beta` and `gamma` of struct `Alpha` are private + --> $DIR/visibility.rs:23:26 + | +LL | let _x = Alpha { beta: 0, .. }; + | ^^^^^^^ ^^ field `gamma` is private + | | + | private field + +error[E0451]: fields `beta` and `gamma` of struct `Alpha` are private + --> $DIR/visibility.rs:25:26 + | +LL | let _x = Alpha { beta: 0, ..Default::default() }; + | ^^^^^^^ ^^^^^^^^^^^^^^^^^^ field `gamma` is private + | | + | private field + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0451`. diff --git a/tests/ui/structs/ice-struct-tail-normalization-113272.rs b/tests/ui/structs/ice-struct-tail-normalization-113272.rs index 85d3d1b4886f7..ce2871fabb8c5 100644 --- a/tests/ui/structs/ice-struct-tail-normalization-113272.rs +++ b/tests/ui/structs/ice-struct-tail-normalization-113272.rs @@ -13,5 +13,6 @@ struct Other { fn main() { unsafe { std::mem::transmute::, Option<&Other>>(None); + //~^ ERROR cannot transmute between types of different sizes, or dependently-sized types } } diff --git a/tests/ui/structs/ice-struct-tail-normalization-113272.stderr b/tests/ui/structs/ice-struct-tail-normalization-113272.stderr index a205eb80f5c0a..8c55dbca18719 100644 --- a/tests/ui/structs/ice-struct-tail-normalization-113272.stderr +++ b/tests/ui/structs/ice-struct-tail-normalization-113272.stderr @@ -13,7 +13,16 @@ LL | type RefTarget; LL | impl Trait for () where Missing: Trait {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `RefTarget` in implementation -error: aborting due to 2 previous errors +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/ice-struct-tail-normalization-113272.rs:15:9 + | +LL | std::mem::transmute::, Option<&Other>>(None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `Option<()>` (8 bits) + = note: target type: `Option<&Other>` (unable to determine layout for `Other` because `<() as Trait>::RefTarget` cannot be normalized) + +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0046, E0412. +Some errors have detailed explanations: E0046, E0412, E0512. For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/structs/rhs-type.rs b/tests/ui/structs/rhs-type.rs index fde5c16a0680e..8ce924672cfd1 100644 --- a/tests/ui/structs/rhs-type.rs +++ b/tests/ui/structs/rhs-type.rs @@ -3,7 +3,7 @@ //@ run-fail //@ error-pattern:bye -//@ ignore-emscripten no processes +//@ needs-subprocess #![allow(unreachable_code)] #![allow(unused_variables)] diff --git a/tests/ui/structs/struct-field-cfg.stderr b/tests/ui/structs/struct-field-cfg.stderr index f30d343d58287..2bca6f302db72 100644 --- a/tests/ui/structs/struct-field-cfg.stderr +++ b/tests/ui/structs/struct-field-cfg.stderr @@ -20,16 +20,19 @@ LL | let Foo { #[cfg(any())] present: () } = foo; | help: include the missing field in the pattern | -LL | let Foo { present } = foo; - | ~~~~~~~~~~~ +LL - let Foo { #[cfg(any())] present: () } = foo; +LL + let Foo { present } = foo; + | help: if you don't care about this missing field, you can explicitly ignore it | -LL | let Foo { present: _ } = foo; - | ~~~~~~~~~~~~~~ +LL - let Foo { #[cfg(any())] present: () } = foo; +LL + let Foo { present: _ } = foo; + | help: or always ignore missing fields here | -LL | let Foo { .. } = foo; - | ~~~~~~ +LL - let Foo { #[cfg(any())] present: () } = foo; +LL + let Foo { .. } = foo; + | error[E0026]: struct `Foo` does not have a field named `absent` --> $DIR/struct-field-cfg.rs:16:42 diff --git a/tests/ui/structs/struct-fields-hints-no-dupe.stderr b/tests/ui/structs/struct-fields-hints-no-dupe.stderr index 2b88d802833c3..aeba7f00b9dc8 100644 --- a/tests/ui/structs/struct-fields-hints-no-dupe.stderr +++ b/tests/ui/structs/struct-fields-hints-no-dupe.stderr @@ -7,7 +7,7 @@ LL | bar : 42, help: a field with a similar name exists | LL | barr : 42, - | ~~~~ + | + error: aborting due to 1 previous error diff --git a/tests/ui/structs/struct-fields-hints.stderr b/tests/ui/structs/struct-fields-hints.stderr index 8217d7a6e811d..6526e49600ec9 100644 --- a/tests/ui/structs/struct-fields-hints.stderr +++ b/tests/ui/structs/struct-fields-hints.stderr @@ -6,8 +6,9 @@ LL | bar : 42, | help: a field with a similar name exists | -LL | car : 42, - | ~~~ +LL - bar : 42, +LL + car : 42, + | error: aborting due to 1 previous error diff --git a/tests/ui/structs/struct-fields-typo.stderr b/tests/ui/structs/struct-fields-typo.stderr index d87bace727736..dacf2ecea17c7 100644 --- a/tests/ui/structs/struct-fields-typo.stderr +++ b/tests/ui/structs/struct-fields-typo.stderr @@ -6,8 +6,9 @@ LL | let x = foo.baa; | help: a field with a similar name exists | -LL | let x = foo.bar; - | ~~~ +LL - let x = foo.baa; +LL + let x = foo.bar; + | error: aborting due to 1 previous error diff --git a/tests/ui/structs/struct-pat-derived-error.stderr b/tests/ui/structs/struct-pat-derived-error.stderr index d1d68121cf168..7fceb95cc5d2c 100644 --- a/tests/ui/structs/struct-pat-derived-error.stderr +++ b/tests/ui/structs/struct-pat-derived-error.stderr @@ -6,8 +6,9 @@ LL | let A { x, y } = self.d; | help: a field with a similar name exists | -LL | let A { x, y } = self.b; - | ~ +LL - let A { x, y } = self.d; +LL + let A { x, y } = self.b; + | error[E0026]: struct `A` does not have fields named `x`, `y` --> $DIR/struct-pat-derived-error.rs:8:17 @@ -24,15 +25,15 @@ LL | let A { x, y } = self.d; help: include the missing fields in the pattern | LL | let A { x, y, b, c } = self.d; - | ~~~~~~~~ + | ++++++ help: if you don't care about these missing fields, you can explicitly ignore them | LL | let A { x, y, b: _, c: _ } = self.d; - | ~~~~~~~~~~~~~~ + | ++++++++++++ help: or always ignore missing fields here | LL | let A { x, y, .. } = self.d; - | ~~~~~~ + | ++++ error: aborting due to 3 previous errors diff --git a/tests/ui/structs/struct-path-self-type-mismatch.stderr b/tests/ui/structs/struct-path-self-type-mismatch.stderr index bbe5bae29bbe1..6517d7f00ddb1 100644 --- a/tests/ui/structs/struct-path-self-type-mismatch.stderr +++ b/tests/ui/structs/struct-path-self-type-mismatch.stderr @@ -44,8 +44,9 @@ LL | | } = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters help: use the type name directly | -LL | Foo:: { - | ~~~~~~~~ +LL - Self { +LL + Foo:: { + | error: aborting due to 3 previous errors diff --git a/tests/ui/structs/struct-tuple-field-names.stderr b/tests/ui/structs/struct-tuple-field-names.stderr index 5f1ab2f9d6823..953f01e1fb6cc 100644 --- a/tests/ui/structs/struct-tuple-field-names.stderr +++ b/tests/ui/structs/struct-tuple-field-names.stderr @@ -6,8 +6,9 @@ LL | E::S { 0, 1 } => {} | help: use the tuple variant pattern syntax instead | -LL | E::S(_, _) => {} - | ~~~~~~ +LL - E::S { 0, 1 } => {} +LL + E::S(_, _) => {} + | error[E0769]: tuple variant `S` written as struct variant --> $DIR/struct-tuple-field-names.rs:13:9 @@ -17,8 +18,9 @@ LL | S { } => {} | help: use the tuple variant pattern syntax instead | -LL | S(_, _) => {} - | ~~~~~~ +LL - S { } => {} +LL + S(_, _) => {} + | error[E0027]: pattern does not mention field `1` --> $DIR/struct-tuple-field-names.rs:16:12 @@ -29,15 +31,15 @@ LL | if let E::S { 0: a } = x { help: include the missing field in the pattern | LL | if let E::S { 0: a, 1: _ } = x { - | ~~~~~~~~ + | ++++++ help: if you don't care about this missing field, you can explicitly ignore it | LL | if let E::S { 0: a, 1: _ } = x { - | ~~~~~~~~ + | ++++++ help: or always ignore missing fields here | LL | if let E::S { 0: a, .. } = x { - | ~~~~~~ + | ++++ error: aborting due to 3 previous errors diff --git a/tests/ui/structs/suggest-private-fields.stderr b/tests/ui/structs/suggest-private-fields.stderr index f67a4ed78e290..adf90f0e1fd65 100644 --- a/tests/ui/structs/suggest-private-fields.stderr +++ b/tests/ui/structs/suggest-private-fields.stderr @@ -6,8 +6,9 @@ LL | aa: 20, | help: a field with a similar name exists | -LL | a: 20, - | ~ +LL - aa: 20, +LL + a: 20, + | error[E0560]: struct `B` has no field named `bb` --> $DIR/suggest-private-fields.rs:17:9 @@ -25,8 +26,9 @@ LL | aa: 20, | help: a field with a similar name exists | -LL | a: 20, - | ~ +LL - aa: 20, +LL + a: 20, + | error[E0560]: struct `A` has no field named `bb` --> $DIR/suggest-private-fields.rs:24:9 @@ -36,8 +38,9 @@ LL | bb: 20, | help: a field with a similar name exists | -LL | b: 20, - | ~ +LL - bb: 20, +LL + b: 20, + | error: aborting due to 4 previous errors diff --git a/tests/ui/structs/suggest-replacing-field-when-specifying-same-type.stderr b/tests/ui/structs/suggest-replacing-field-when-specifying-same-type.stderr index af530e2b75931..3a828e955773f 100644 --- a/tests/ui/structs/suggest-replacing-field-when-specifying-same-type.stderr +++ b/tests/ui/structs/suggest-replacing-field-when-specifying-same-type.stderr @@ -16,15 +16,15 @@ LL | Foo::Bar { a, aa: 1, c } => (), help: include the missing field in the pattern | LL | Foo::Bar { a, aa: 1, c, b } => (), - | ~~~~~ + | +++ help: if you don't care about this missing field, you can explicitly ignore it | LL | Foo::Bar { a, aa: 1, c, b: _ } => (), - | ~~~~~~~~ + | ++++++ help: or always ignore missing fields here | LL | Foo::Bar { a, aa: 1, c, .. } => (), - | ~~~~~~ + | ++++ error[E0026]: variant `Foo::Baz` does not have a field named `bb` --> $DIR/suggest-replacing-field-when-specifying-same-type.rs:13:20 @@ -44,15 +44,15 @@ LL | Foo::Baz { bb: 1.0 } => (), help: include the missing field in the pattern | LL | Foo::Baz { bb: 1.0, a } => (), - | ~~~~~ + | +++ help: if you don't care about this missing field, you can explicitly ignore it | LL | Foo::Baz { bb: 1.0, a: _ } => (), - | ~~~~~~~~ + | ++++++ help: or always ignore missing fields here | LL | Foo::Baz { bb: 1.0, .. } => (), - | ~~~~~~ + | ++++ error[E0026]: variant `Foo::Bar` does not have a field named `aa` --> $DIR/suggest-replacing-field-when-specifying-same-type.rs:20:23 @@ -69,15 +69,15 @@ LL | Foo::Bar { a, aa: "", c } => (), help: include the missing field in the pattern | LL | Foo::Bar { a, aa: "", c, b } => (), - | ~~~~~ + | +++ help: if you don't care about this missing field, you can explicitly ignore it | LL | Foo::Bar { a, aa: "", c, b: _ } => (), - | ~~~~~~~~ + | ++++++ help: or always ignore missing fields here | LL | Foo::Bar { a, aa: "", c, .. } => (), - | ~~~~~~ + | ++++ error[E0026]: variant `Foo::Baz` does not have a field named `bb` --> $DIR/suggest-replacing-field-when-specifying-same-type.rs:23:20 @@ -94,15 +94,15 @@ LL | Foo::Baz { bb: "" } => (), help: include the missing field in the pattern | LL | Foo::Baz { bb: "", a } => (), - | ~~~~~ + | +++ help: if you don't care about this missing field, you can explicitly ignore it | LL | Foo::Baz { bb: "", a: _ } => (), - | ~~~~~~~~ + | ++++++ help: or always ignore missing fields here | LL | Foo::Baz { bb: "", .. } => (), - | ~~~~~~ + | ++++ error: aborting due to 8 previous errors diff --git a/tests/ui/suggestions/ambiguous-assoc-type-path-suggest-similar-item.rs b/tests/ui/suggestions/ambiguous-assoc-type-path-suggest-similar-item.rs new file mode 100644 index 0000000000000..a9c2c20ef374a --- /dev/null +++ b/tests/ui/suggestions/ambiguous-assoc-type-path-suggest-similar-item.rs @@ -0,0 +1,51 @@ +// https://github.com/rust-lang/rust/issues/109195 +struct Foo; + +impl Foo { + fn bar_baz() {} +} + +impl Foo { + fn bar_quux() {} +} + +fn main() { + String::from::utf8; + //~^ ERROR ambiguous associated type [E0223] + //~| HELP there is an associated function with a similar name: `from_utf8` + String::from::utf8(); + //~^ ERROR ambiguous associated type [E0223] + //~| HELP there is an associated function with a similar name: `from_utf8` + String::from::utf16(); + //~^ ERROR ambiguous associated type [E0223] + //~| HELP there is an associated function with a similar name: `from_utf16` + String::from::method_that_doesnt_exist(); + //~^ ERROR ambiguous associated type [E0223] + //~| HELP if there were a trait named `Example` with associated type `from` + str::into::string(); + //~^ ERROR ambiguous associated type [E0223] + //~| HELP there is an associated function with a similar name: `into_string` + str::char::indices(); + //~^ ERROR ambiguous associated type [E0223] + //~| HELP there is an associated function with a similar name: `char_indices` + Foo::bar::baz; + //~^ ERROR ambiguous associated type [E0223] + //~| HELP there is an associated function with a similar name: `bar_baz` + Foo::bar::quux; + //~^ ERROR ambiguous associated type [E0223] + //~| HELP there is an associated function with a similar name: `bar_quux` + Foo::bar::fizz; + //~^ ERROR ambiguous associated type [E0223] + //~| HELP if there were a trait named `Example` with associated type `bar` + i32::wrapping::add; + //~^ ERROR ambiguous associated type [E0223] + //~| HELP there is an associated function with a similar name: `wrapping_add` + i32::wrapping::method_that_doesnt_exist; + //~^ ERROR ambiguous associated type [E0223] + //~| HELP if there were a trait named `Example` with associated type `wrapping` + + // this one ideally should suggest `downcast_mut_unchecked` + ::downcast::mut_unchecked; + //~^ ERROR ambiguous associated type [E0223] + //~| HELP if there were a trait named `Example` with associated type `downcast` +} diff --git a/tests/ui/suggestions/ambiguous-assoc-type-path-suggest-similar-item.stderr b/tests/ui/suggestions/ambiguous-assoc-type-path-suggest-similar-item.stderr new file mode 100644 index 0000000000000..76e3c21473d63 --- /dev/null +++ b/tests/ui/suggestions/ambiguous-assoc-type-path-suggest-similar-item.stderr @@ -0,0 +1,147 @@ +error[E0223]: ambiguous associated type + --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:13:5 + | +LL | String::from::utf8; + | ^^^^^^^^^^^^ + | +help: there is an associated function with a similar name: `from_utf8` + | +LL - String::from::utf8; +LL + String::from_utf8; + | + +error[E0223]: ambiguous associated type + --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:16:5 + | +LL | String::from::utf8(); + | ^^^^^^^^^^^^ + | +help: there is an associated function with a similar name: `from_utf8` + | +LL - String::from::utf8(); +LL + String::from_utf8(); + | + +error[E0223]: ambiguous associated type + --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:19:5 + | +LL | String::from::utf16(); + | ^^^^^^^^^^^^ + | +help: there is an associated function with a similar name: `from_utf16` + | +LL - String::from::utf16(); +LL + String::from_utf16(); + | + +error[E0223]: ambiguous associated type + --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:22:5 + | +LL | String::from::method_that_doesnt_exist(); + | ^^^^^^^^^^^^ + | +help: if there were a trait named `Example` with associated type `from` implemented for `String`, you could use the fully-qualified path + | +LL - String::from::method_that_doesnt_exist(); +LL + ::from::method_that_doesnt_exist(); + | + +error[E0223]: ambiguous associated type + --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:25:5 + | +LL | str::into::string(); + | ^^^^^^^^^ + | +help: there is an associated function with a similar name: `into_string` + | +LL - str::into::string(); +LL + str::into_string(); + | + +error[E0223]: ambiguous associated type + --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:28:5 + | +LL | str::char::indices(); + | ^^^^^^^^^ + | +help: there is an associated function with a similar name: `char_indices` + | +LL - str::char::indices(); +LL + str::char_indices(); + | + +error[E0223]: ambiguous associated type + --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:31:5 + | +LL | Foo::bar::baz; + | ^^^^^^^^ + | +help: there is an associated function with a similar name: `bar_baz` + | +LL - Foo::bar::baz; +LL + Foo::bar_baz; + | + +error[E0223]: ambiguous associated type + --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:34:5 + | +LL | Foo::bar::quux; + | ^^^^^^^^ + | +help: there is an associated function with a similar name: `bar_quux` + | +LL - Foo::bar::quux; +LL + Foo::bar_quux; + | + +error[E0223]: ambiguous associated type + --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:37:5 + | +LL | Foo::bar::fizz; + | ^^^^^^^^ + | +help: if there were a trait named `Example` with associated type `bar` implemented for `Foo`, you could use the fully-qualified path + | +LL - Foo::bar::fizz; +LL + ::bar::fizz; + | + +error[E0223]: ambiguous associated type + --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:40:5 + | +LL | i32::wrapping::add; + | ^^^^^^^^^^^^^ + | +help: there is an associated function with a similar name: `wrapping_add` + | +LL - i32::wrapping::add; +LL + i32::wrapping_add; + | + +error[E0223]: ambiguous associated type + --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:43:5 + | +LL | i32::wrapping::method_that_doesnt_exist; + | ^^^^^^^^^^^^^ + | +help: if there were a trait named `Example` with associated type `wrapping` implemented for `i32`, you could use the fully-qualified path + | +LL - i32::wrapping::method_that_doesnt_exist; +LL + ::wrapping::method_that_doesnt_exist; + | + +error[E0223]: ambiguous associated type + --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:48:5 + | +LL | ::downcast::mut_unchecked; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: if there were a trait named `Example` with associated type `downcast` implemented for `(dyn Any + 'static)`, you could use the fully-qualified path + | +LL - ::downcast::mut_unchecked; +LL + <(dyn Any + 'static) as Example>::downcast::mut_unchecked; + | + +error: aborting due to 12 previous errors + +For more information about this error, try `rustc --explain E0223`. diff --git a/tests/ui/suggestions/args-instead-of-tuple-errors.stderr b/tests/ui/suggestions/args-instead-of-tuple-errors.stderr index 1051a16b40d7c..8644f4a1dd452 100644 --- a/tests/ui/suggestions/args-instead-of-tuple-errors.stderr +++ b/tests/ui/suggestions/args-instead-of-tuple-errors.stderr @@ -60,8 +60,9 @@ note: tuple variant defined here --> $SRC_DIR/core/src/option.rs:LL:COL help: provide the argument | -LL | let _: Option<(i8,)> = Some(/* (i8,) */); - | ~~~~~~~~~~~~~ +LL - let _: Option<(i8,)> = Some(); +LL + let _: Option<(i8,)> = Some(/* (i8,) */); + | error[E0308]: mismatched types --> $DIR/args-instead-of-tuple-errors.rs:14:34 diff --git a/tests/ui/suggestions/args-instead-of-tuple.stderr b/tests/ui/suggestions/args-instead-of-tuple.stderr index 3ca560f93eb79..4b0bab971ee71 100644 --- a/tests/ui/suggestions/args-instead-of-tuple.stderr +++ b/tests/ui/suggestions/args-instead-of-tuple.stderr @@ -34,8 +34,9 @@ note: tuple variant defined here --> $SRC_DIR/core/src/option.rs:LL:COL help: provide the argument | -LL | let _: Option<()> = Some(()); - | ~~~~ +LL - let _: Option<()> = Some(); +LL + let _: Option<()> = Some(()); + | error[E0308]: mismatched types --> $DIR/args-instead-of-tuple.rs:14:34 diff --git a/tests/ui/suggestions/assoc-const-as-field.stderr b/tests/ui/suggestions/assoc-const-as-field.stderr index 6c095e52ac9e5..54b7ff39926f8 100644 --- a/tests/ui/suggestions/assoc-const-as-field.stderr +++ b/tests/ui/suggestions/assoc-const-as-field.stderr @@ -6,8 +6,9 @@ LL | foo(Mod::Foo.Bar); | help: use the path separator to refer to an item | -LL | foo(Mod::Foo::Bar); - | ~~ +LL - foo(Mod::Foo.Bar); +LL + foo(Mod::Foo::Bar); + | error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/assoc-ct-for-assoc-method.stderr b/tests/ui/suggestions/assoc-ct-for-assoc-method.stderr index 8f00a72f1e81a..6d6fd98303845 100644 --- a/tests/ui/suggestions/assoc-ct-for-assoc-method.stderr +++ b/tests/ui/suggestions/assoc-ct-for-assoc-method.stderr @@ -10,8 +10,9 @@ LL | let x: i32 = MyS::foo; found fn item `fn() -> MyS {MyS::foo}` help: try referring to the associated const `FOO` instead (notice the capitalization difference) | -LL | let x: i32 = MyS::FOO; - | ~~~ +LL - let x: i32 = MyS::foo; +LL + let x: i32 = MyS::FOO; + | error[E0308]: mismatched types --> $DIR/assoc-ct-for-assoc-method.rs:15:18 @@ -25,8 +26,9 @@ LL | let z: i32 = i32::max; found fn item `fn(i32, i32) -> i32 {::max}` help: try referring to the associated const `MAX` instead | -LL | let z: i32 = i32::MAX; - | ~~~ +LL - let z: i32 = i32::max; +LL + let z: i32 = i32::MAX; + | error[E0369]: cannot subtract `{integer}` from `fn(i32, i32) -> i32 {::max}` --> $DIR/assoc-ct-for-assoc-method.rs:22:27 diff --git a/tests/ui/suggestions/bad-hex-float-lit.stderr b/tests/ui/suggestions/bad-hex-float-lit.stderr index bc09abb1a5671..94c0715a4e683 100644 --- a/tests/ui/suggestions/bad-hex-float-lit.stderr +++ b/tests/ui/suggestions/bad-hex-float-lit.stderr @@ -8,10 +8,12 @@ LL | let _f: f32 = 0xAAf32; | help: rewrite this as a decimal floating point literal, or use `as` to turn a hex literal into a float | -LL | let _f: f32 = 0xAA as f32; - | ~~~~~~~~~~~ -LL | let _f: f32 = 170_f32; - | ~~~~~~~ +LL - let _f: f32 = 0xAAf32; +LL + let _f: f32 = 0xAA as f32; + | +LL - let _f: f32 = 0xAAf32; +LL + let _f: f32 = 170_f32; + | error[E0308]: mismatched types --> $DIR/bad-hex-float-lit.rs:6:19 @@ -23,10 +25,12 @@ LL | let _f: f32 = 0xAB_f32; | help: rewrite this as a decimal floating point literal, or use `as` to turn a hex literal into a float | -LL | let _f: f32 = 0xAB as f32; - | ~~~~~~~~~~~ -LL | let _f: f32 = 171_f32; - | ~~~~~~~ +LL - let _f: f32 = 0xAB_f32; +LL + let _f: f32 = 0xAB as f32; + | +LL - let _f: f32 = 0xAB_f32; +LL + let _f: f32 = 171_f32; + | error[E0308]: mismatched types --> $DIR/bad-hex-float-lit.rs:10:19 @@ -38,10 +42,12 @@ LL | let _f: f64 = 0xFF_f64; | help: rewrite this as a decimal floating point literal, or use `as` to turn a hex literal into a float | -LL | let _f: f64 = 0xFF as f64; - | ~~~~~~~~~~~ -LL | let _f: f64 = 255_f64; - | ~~~~~~~ +LL - let _f: f64 = 0xFF_f64; +LL + let _f: f64 = 0xFF as f64; + | +LL - let _f: f64 = 0xFF_f64; +LL + let _f: f64 = 255_f64; + | error: aborting due to 3 previous errors diff --git a/tests/ui/suggestions/bad-infer-in-trait-impl.stderr b/tests/ui/suggestions/bad-infer-in-trait-impl.stderr index 50c398de2b08b..68d8f5402e44c 100644 --- a/tests/ui/suggestions/bad-infer-in-trait-impl.stderr +++ b/tests/ui/suggestions/bad-infer-in-trait-impl.stderr @@ -6,8 +6,9 @@ LL | fn bar(s: _) {} | help: use type parameters instead | -LL | fn bar(s: T) {} - | +++ ~ +LL - fn bar(s: _) {} +LL + fn bar(s: T) {} + | error[E0050]: method `bar` has 1 parameter but the declaration in trait `Foo::bar` has 0 --> $DIR/bad-infer-in-trait-impl.rs:6:15 diff --git a/tests/ui/suggestions/bool_typo_err_suggest.stderr b/tests/ui/suggestions/bool_typo_err_suggest.stderr index 8d59ed63e5432..faf799d0fda22 100644 --- a/tests/ui/suggestions/bool_typo_err_suggest.stderr +++ b/tests/ui/suggestions/bool_typo_err_suggest.stderr @@ -6,8 +6,9 @@ LL | let x = True; | help: you may want to use a bool value instead | -LL | let x = true; - | ~~~~ +LL - let x = True; +LL + let x = true; + | error[E0425]: cannot find value `False` in this scope --> $DIR/bool_typo_err_suggest.rs:9:13 @@ -17,8 +18,9 @@ LL | let y = False; | help: you may want to use a bool value instead (notice the capitalization difference) | -LL | let y = false; - | ~~~~~ +LL - let y = False; +LL + let y = false; + | error: aborting due to 2 previous errors diff --git a/tests/ui/suggestions/bound-suggestions.stderr b/tests/ui/suggestions/bound-suggestions.stderr index e30deb11398e6..f23e086afe4e7 100644 --- a/tests/ui/suggestions/bound-suggestions.stderr +++ b/tests/ui/suggestions/bound-suggestions.stderr @@ -44,7 +44,7 @@ LL | println!("{:?} {:?}", x, y); help: consider further restricting type parameter `Y` with trait `Debug` | LL | fn test_no_bounds_where(x: X, y: Y) where X: std::fmt::Debug, Y: std::fmt::Debug { - | ~~~~~~~~~~~~~~~~~~~~ + | ++++++++++++++++++ error[E0277]: `X` doesn't implement `Debug` --> $DIR/bound-suggestions.rs:33:22 diff --git a/tests/ui/suggestions/box-future-wrong-output.stderr b/tests/ui/suggestions/box-future-wrong-output.stderr index 6a232c3444d93..bac26ae8fb5dd 100644 --- a/tests/ui/suggestions/box-future-wrong-output.stderr +++ b/tests/ui/suggestions/box-future-wrong-output.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/box-future-wrong-output.rs:20:39 | LL | let _: BoxFuture<'static, bool> = async {}.boxed(); - | ------------------------ ^^^^^^^^^^^^^^^^ expected `bool`, found `()` + | ------------------------ ^^^^^^^^^^^^^^^^ expected `Pin>`, found `Pin + Send>>` | | | expected due to this | diff --git a/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr b/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr index 4f92d3aceefe0..0dc17f2c25c38 100644 --- a/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr +++ b/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr @@ -13,7 +13,7 @@ LL | const A: i32 = 2; help: introduce a variable instead | LL | let A_var = 3; - | ~~~~~ + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/crate-or-module-typo.rs b/tests/ui/suggestions/crate-or-module-typo.rs index dbc0605c76bce..393fc7a1f72e0 100644 --- a/tests/ui/suggestions/crate-or-module-typo.rs +++ b/tests/ui/suggestions/crate-or-module-typo.rs @@ -1,6 +1,6 @@ //@ edition:2018 -use st::cell::Cell; //~ ERROR failed to resolve: use of undeclared crate or module `st` +use st::cell::Cell; //~ ERROR failed to resolve: use of unresolved module or unlinked crate `st` mod bar { pub fn bar() { bar::baz(); } //~ ERROR failed to resolve: function `bar` is not a crate or module @@ -11,7 +11,7 @@ mod bar { use bas::bar; //~ ERROR unresolved import `bas` struct Foo { - bar: st::cell::Cell //~ ERROR failed to resolve: use of undeclared crate or module `st` + bar: st::cell::Cell //~ ERROR failed to resolve: use of unresolved module or unlinked crate `st` } fn main() {} diff --git a/tests/ui/suggestions/crate-or-module-typo.stderr b/tests/ui/suggestions/crate-or-module-typo.stderr index 084d0408a8e7d..2ec4fc7ed6ccf 100644 --- a/tests/ui/suggestions/crate-or-module-typo.stderr +++ b/tests/ui/suggestions/crate-or-module-typo.stderr @@ -1,35 +1,36 @@ -error[E0433]: failed to resolve: use of undeclared crate or module `st` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `st` --> $DIR/crate-or-module-typo.rs:3:5 | LL | use st::cell::Cell; - | ^^ use of undeclared crate or module `st` + | ^^ use of unresolved module or unlinked crate `st` | help: there is a crate or module with a similar name | LL | use std::cell::Cell; - | ~~~ + | + error[E0432]: unresolved import `bas` --> $DIR/crate-or-module-typo.rs:11:5 | LL | use bas::bar; - | ^^^ use of undeclared crate or module `bas` + | ^^^ use of unresolved module or unlinked crate `bas` | help: there is a crate or module with a similar name | -LL | use bar::bar; - | ~~~ +LL - use bas::bar; +LL + use bar::bar; + | -error[E0433]: failed to resolve: use of undeclared crate or module `st` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `st` --> $DIR/crate-or-module-typo.rs:14:10 | LL | bar: st::cell::Cell - | ^^ use of undeclared crate or module `st` + | ^^ use of unresolved module or unlinked crate `st` | help: there is a crate or module with a similar name | LL | bar: std::cell::Cell - | ~~~ + | + help: consider importing this module | LL + use std::cell; diff --git a/tests/ui/suggestions/deref-path-method.stderr b/tests/ui/suggestions/deref-path-method.stderr index bfcc2307fd7fb..dc2f6f66437e0 100644 --- a/tests/ui/suggestions/deref-path-method.stderr +++ b/tests/ui/suggestions/deref-path-method.stderr @@ -13,8 +13,9 @@ note: if you're trying to build a new `Vec<_, _>` consider using one of the foll --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL help: the function `contains` is implemented on `[_]` | -LL | <[_]>::contains(&vec, &0); - | ~~~~~ +LL - Vec::contains(&vec, &0); +LL + <[_]>::contains(&vec, &0); + | error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/dont-suggest-boxing-async-closure-body.rs b/tests/ui/suggestions/dont-suggest-boxing-async-closure-body.rs new file mode 100644 index 0000000000000..e03c43d15ee0a --- /dev/null +++ b/tests/ui/suggestions/dont-suggest-boxing-async-closure-body.rs @@ -0,0 +1,12 @@ +//@ edition:2021 +// issue: https://github.com/rust-lang/rust/issues/111011 + +fn foo(x: impl FnOnce() -> Box) {} +// just to make sure async closures can still be suggested for boxing. +fn bar(x: Box X>) {} + +fn main() { + foo(async move || {}); + //~^ ERROR expected `{async closure@dont-suggest-boxing-async-closure-body.rs:9:9}` to return `Box<_>` + bar(async move || {}); //~ ERROR mismatched types +} diff --git a/tests/ui/suggestions/dont-suggest-boxing-async-closure-body.stderr b/tests/ui/suggestions/dont-suggest-boxing-async-closure-body.stderr new file mode 100644 index 0000000000000..abf8e2d824b5d --- /dev/null +++ b/tests/ui/suggestions/dont-suggest-boxing-async-closure-body.stderr @@ -0,0 +1,41 @@ +error[E0271]: expected `{async closure@dont-suggest-boxing-async-closure-body.rs:9:9}` to return `Box<_>`, but it returns `{async closure body@$DIR/dont-suggest-boxing-async-closure-body.rs:9:23: 9:25}` + --> $DIR/dont-suggest-boxing-async-closure-body.rs:9:9 + | +LL | foo(async move || {}); + | --- ^^^^^^^^^^^^^^^^ expected `Box<_>`, found `async` closure body + | | + | required by a bound introduced by this call + | + = note: expected struct `Box<_>` + found `async` closure body `{async closure body@$DIR/dont-suggest-boxing-async-closure-body.rs:9:23: 9:25}` +note: required by a bound in `foo` + --> $DIR/dont-suggest-boxing-async-closure-body.rs:4:31 + | +LL | fn foo(x: impl FnOnce() -> Box) {} + | ^^^^^^ required by this bound in `foo` + +error[E0308]: mismatched types + --> $DIR/dont-suggest-boxing-async-closure-body.rs:11:9 + | +LL | bar(async move || {}); + | --- ^^^^^^^^^^^^^^^^ expected `Box _>`, found `{async closure@dont-suggest-boxing-async-closure-body.rs:11:9}` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box<(dyn FnOnce() -> _ + 'static)>` + found closure `{async closure@$DIR/dont-suggest-boxing-async-closure-body.rs:11:9: 11:22}` + = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html +note: function defined here + --> $DIR/dont-suggest-boxing-async-closure-body.rs:6:4 + | +LL | fn bar(x: Box X>) {} + | ^^^ ------------------------- +help: store this in the heap by calling `Box::new` + | +LL | bar(Box::new(async move || {})); + | +++++++++ + + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0271, E0308. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/suggestions/dont-suggest-try_into-in-macros.stderr b/tests/ui/suggestions/dont-suggest-try_into-in-macros.stderr index 348f7e00d468e..53fdb5880c06f 100644 --- a/tests/ui/suggestions/dont-suggest-try_into-in-macros.stderr +++ b/tests/ui/suggestions/dont-suggest-try_into-in-macros.stderr @@ -6,8 +6,9 @@ LL | assert_eq!(10u64, 10usize); | help: change the type of the numeric literal from `usize` to `u64` | -LL | assert_eq!(10u64, 10u64); - | ~~~ +LL - assert_eq!(10u64, 10usize); +LL + assert_eq!(10u64, 10u64); + | error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/dyn-incompatible-trait-references-self.rs b/tests/ui/suggestions/dyn-incompatible-trait-references-self.rs index 4b3d5faba4655..66b435247d40d 100644 --- a/tests/ui/suggestions/dyn-incompatible-trait-references-self.rs +++ b/tests/ui/suggestions/dyn-incompatible-trait-references-self.rs @@ -6,10 +6,10 @@ trait Trait { //~| ERROR the size for values of type `Self` cannot be known } -fn bar(x: &dyn Trait) {} //~ ERROR the trait `Trait` cannot be made into an object +fn bar(x: &dyn Trait) {} //~ ERROR the trait `Trait` is not dyn compatible trait Other: Sized {} -fn foo(x: &dyn Other) {} //~ ERROR the trait `Other` cannot be made into an object +fn foo(x: &dyn Other) {} //~ ERROR the trait `Other` is not dyn compatible fn main() {} diff --git a/tests/ui/suggestions/dyn-incompatible-trait-references-self.stderr b/tests/ui/suggestions/dyn-incompatible-trait-references-self.stderr index 242c44abd9d66..ae009d260299a 100644 --- a/tests/ui/suggestions/dyn-incompatible-trait-references-self.stderr +++ b/tests/ui/suggestions/dyn-incompatible-trait-references-self.stderr @@ -1,14 +1,15 @@ -error[E0038]: the trait `Trait` cannot be made into an object +error[E0038]: the trait `Trait` is not dyn compatible --> $DIR/dyn-incompatible-trait-references-self.rs:9:12 | LL | fn bar(x: &dyn Trait) {} - | ^^^^^^^^^ `Trait` cannot be made into an object + | ^^^^^^^^^ `Trait` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/dyn-incompatible-trait-references-self.rs:2:22 | LL | trait Trait { - | ----- this trait cannot be made into an object... + | ----- this trait is not dyn compatible... LL | fn baz(&self, _: Self) {} | ^^^^ ...because method `baz` references the `Self` type in this parameter LL | @@ -17,19 +18,20 @@ LL | fn bat(&self) -> Self {} = help: consider moving `baz` to another trait = help: consider moving `bat` to another trait -error[E0038]: the trait `Other` cannot be made into an object +error[E0038]: the trait `Other` is not dyn compatible --> $DIR/dyn-incompatible-trait-references-self.rs:13:12 | LL | fn foo(x: &dyn Other) {} - | ^^^^^^^^^ `Other` cannot be made into an object + | ^^^^^^^^^ `Other` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/dyn-incompatible-trait-references-self.rs:11:14 | LL | trait Other: Sized {} | ----- ^^^^^ ...because it requires `Self: Sized` | | - | this trait cannot be made into an object... + | this trait is not dyn compatible... error[E0277]: the size for values of type `Self` cannot be known at compilation time --> $DIR/dyn-incompatible-trait-references-self.rs:2:22 diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.stderr b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.stderr index c0cfb18955cec..20aa227d10f9e 100644 --- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.stderr +++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021-without-dyn.stderr @@ -1,42 +1,3 @@ -error: associated item referring to unboxed trait object for its own trait - --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:4:13 - | -LL | trait A: Sized { - | - in this trait -LL | fn f(a: A) -> A; - | ^ ^ - | -help: you might have meant to use `Self` to refer to the implementing type - | -LL | fn f(a: Self) -> Self; - | ~~~~ ~~~~ - -error: associated item referring to unboxed trait object for its own trait - --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:10:13 - | -LL | trait B { - | - in this trait -LL | fn f(b: B) -> B; - | ^ ^ - | -help: you might have meant to use `Self` to refer to the implementing type - | -LL | fn f(b: Self) -> Self; - | ~~~~ ~~~~ - -error: associated item referring to unboxed trait object for its own trait - --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:16:20 - | -LL | trait C { - | - in this trait -LL | fn f(&self, c: C) -> C; - | ^ ^ - | -help: you might have meant to use `Self` to refer to the implementing type - | -LL | fn f(&self, c: Self) -> Self; - | ~~~~ ~~~~ - error[E0782]: expected a type, found a trait --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:4:13 | @@ -46,8 +7,9 @@ LL | fn f(a: A) -> A; = note: `A` it is dyn-incompatible, so it can't be `dyn` help: use a new generic type parameter, constrained by `A` | -LL | fn f(a: T) -> A; - | ++++++ ~ +LL - fn f(a: A) -> A; +LL + fn f(a: T) -> A; + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn f(a: impl A) -> A; @@ -73,8 +35,9 @@ LL | fn f(b: B) -> B; = note: `B` it is dyn-incompatible, so it can't be `dyn` help: use a new generic type parameter, constrained by `B` | -LL | fn f(b: T) -> B; - | ++++++ ~ +LL - fn f(b: B) -> B; +LL + fn f(b: T) -> B; + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn f(b: impl B) -> B; @@ -100,8 +63,9 @@ LL | fn f(&self, c: C) -> C; = note: `C` it is dyn-incompatible, so it can't be `dyn` help: use a new generic type parameter, constrained by `C` | -LL | fn f(&self, c: T) -> C; - | ++++++ ~ +LL - fn f(&self, c: C) -> C; +LL + fn f(&self, c: T) -> C; + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn f(&self, c: impl C) -> C; @@ -118,6 +82,48 @@ help: `C` is dyn-incompatible, use `impl C` to return an opaque type, as long as LL | fn f(&self, c: C) -> impl C; | ++++ +error: associated item referring to unboxed trait object for its own trait + --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:4:13 + | +LL | trait A: Sized { + | - in this trait +LL | fn f(a: A) -> A; + | ^ ^ + | +help: you might have meant to use `Self` to refer to the implementing type + | +LL - fn f(a: A) -> A; +LL + fn f(a: Self) -> Self; + | + +error: associated item referring to unboxed trait object for its own trait + --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:10:13 + | +LL | trait B { + | - in this trait +LL | fn f(b: B) -> B; + | ^ ^ + | +help: you might have meant to use `Self` to refer to the implementing type + | +LL - fn f(b: B) -> B; +LL + fn f(b: Self) -> Self; + | + +error: associated item referring to unboxed trait object for its own trait + --> $DIR/dyn-incompatible-trait-should-use-self-2021-without-dyn.rs:16:20 + | +LL | trait C { + | - in this trait +LL | fn f(&self, c: C) -> C; + | ^ ^ + | +help: you might have meant to use `Self` to refer to the implementing type + | +LL - fn f(&self, c: C) -> C; +LL + fn f(&self, c: Self) -> Self; + | + error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.rs b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.rs index 4ab10f40eb68d..747926c400ae0 100644 --- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.rs +++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.rs @@ -3,12 +3,12 @@ trait A: Sized { fn f(a: dyn A) -> dyn A; //~^ ERROR associated item referring to unboxed trait object for its own trait - //~| ERROR the trait `A` cannot be made into an object + //~| ERROR the trait `A` is not dyn compatible } trait B { fn f(a: dyn B) -> dyn B; //~^ ERROR associated item referring to unboxed trait object for its own trait - //~| ERROR the trait `B` cannot be made into an object + //~| ERROR the trait `B` is not dyn compatible } trait C { fn f(&self, a: dyn C) -> dyn C; diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.stderr b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.stderr index 5e0d1a1445230..2e3919db1b75f 100644 --- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.stderr +++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self-2021.stderr @@ -8,22 +8,24 @@ LL | fn f(a: dyn A) -> dyn A; | help: you might have meant to use `Self` to refer to the implementing type | -LL | fn f(a: Self) -> Self; - | ~~~~ ~~~~ +LL - fn f(a: dyn A) -> dyn A; +LL + fn f(a: Self) -> Self; + | -error[E0038]: the trait `A` cannot be made into an object +error[E0038]: the trait `A` is not dyn compatible --> $DIR/dyn-incompatible-trait-should-use-self-2021.rs:4:13 | LL | fn f(a: dyn A) -> dyn A; - | ^^^^^ `A` cannot be made into an object + | ^^^^^ `A` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/dyn-incompatible-trait-should-use-self-2021.rs:3:10 | LL | trait A: Sized { | - ^^^^^ ...because it requires `Self: Sized` | | - | this trait cannot be made into an object... + | this trait is not dyn compatible... error: associated item referring to unboxed trait object for its own trait --> $DIR/dyn-incompatible-trait-should-use-self-2021.rs:9:13 @@ -35,20 +37,22 @@ LL | fn f(a: dyn B) -> dyn B; | help: you might have meant to use `Self` to refer to the implementing type | -LL | fn f(a: Self) -> Self; - | ~~~~ ~~~~ +LL - fn f(a: dyn B) -> dyn B; +LL + fn f(a: Self) -> Self; + | -error[E0038]: the trait `B` cannot be made into an object +error[E0038]: the trait `B` is not dyn compatible --> $DIR/dyn-incompatible-trait-should-use-self-2021.rs:9:13 | LL | fn f(a: dyn B) -> dyn B; - | ^^^^^ `B` cannot be made into an object + | ^^^^^ `B` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/dyn-incompatible-trait-should-use-self-2021.rs:9:8 | LL | trait B { - | - this trait cannot be made into an object... + | - this trait is not dyn compatible... LL | fn f(a: dyn B) -> dyn B; | ^ ...because associated function `f` has no `self` parameter help: consider turning `f` into a method by giving it a `&self` argument diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self.rs b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self.rs index 75f99075eb18f..2893bbc8b7139 100644 --- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self.rs +++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self.rs @@ -2,12 +2,12 @@ trait A: Sized { fn f(a: A) -> A; //~^ ERROR associated item referring to unboxed trait object for its own trait - //~| ERROR the trait `A` cannot be made into an object + //~| ERROR the trait `A` is not dyn compatible } trait B { fn f(a: B) -> B; //~^ ERROR associated item referring to unboxed trait object for its own trait - //~| ERROR the trait `B` cannot be made into an object + //~| ERROR the trait `B` is not dyn compatible } trait C { fn f(&self, a: C) -> C; diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self.stderr b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self.stderr index 93f6ea2b12e67..f4b669d7fcd41 100644 --- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-self.stderr +++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-self.stderr @@ -8,22 +8,24 @@ LL | fn f(a: A) -> A; | help: you might have meant to use `Self` to refer to the implementing type | -LL | fn f(a: Self) -> Self; - | ~~~~ ~~~~ +LL - fn f(a: A) -> A; +LL + fn f(a: Self) -> Self; + | -error[E0038]: the trait `A` cannot be made into an object +error[E0038]: the trait `A` is not dyn compatible --> $DIR/dyn-incompatible-trait-should-use-self.rs:3:13 | LL | fn f(a: A) -> A; - | ^ `A` cannot be made into an object + | ^ `A` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/dyn-incompatible-trait-should-use-self.rs:2:10 | LL | trait A: Sized { | - ^^^^^ ...because it requires `Self: Sized` | | - | this trait cannot be made into an object... + | this trait is not dyn compatible... error: associated item referring to unboxed trait object for its own trait --> $DIR/dyn-incompatible-trait-should-use-self.rs:8:13 @@ -35,20 +37,22 @@ LL | fn f(a: B) -> B; | help: you might have meant to use `Self` to refer to the implementing type | -LL | fn f(a: Self) -> Self; - | ~~~~ ~~~~ +LL - fn f(a: B) -> B; +LL + fn f(a: Self) -> Self; + | -error[E0038]: the trait `B` cannot be made into an object +error[E0038]: the trait `B` is not dyn compatible --> $DIR/dyn-incompatible-trait-should-use-self.rs:8:13 | LL | fn f(a: B) -> B; - | ^ `B` cannot be made into an object + | ^ `B` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/dyn-incompatible-trait-should-use-self.rs:8:8 | LL | trait B { - | - this trait cannot be made into an object... + | - this trait is not dyn compatible... LL | fn f(a: B) -> B; | ^ ...because associated function `f` has no `self` parameter help: consider turning `f` into a method by giving it a `&self` argument diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.fixed b/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.fixed index fd9b78934c7da..2b26d8cc82ee3 100644 --- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.fixed +++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.fixed @@ -6,7 +6,7 @@ trait Trait { fn bar(self: &Self) {} //~ ERROR invalid `self` parameter type } -fn bar(x: &dyn Trait) {} //~ ERROR the trait `Trait` cannot be made into an object +fn bar(x: &dyn Trait) {} //~ ERROR the trait `Trait` is not dyn compatible trait Other {} diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.rs b/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.rs index e4aa0d892391b..b0b02dedb2b90 100644 --- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.rs +++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.rs @@ -6,7 +6,7 @@ trait Trait { fn bar(self: ()) {} //~ ERROR invalid `self` parameter type } -fn bar(x: &dyn Trait) {} //~ ERROR the trait `Trait` cannot be made into an object +fn bar(x: &dyn Trait) {} //~ ERROR the trait `Trait` is not dyn compatible trait Other {} diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.stderr b/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.stderr index beafd7c2ab00f..c275cdccaa8c1 100644 --- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.stderr +++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.stderr @@ -1,14 +1,15 @@ -error[E0038]: the trait `Trait` cannot be made into an object +error[E0038]: the trait `Trait` is not dyn compatible --> $DIR/dyn-incompatible-trait-should-use-where-sized.rs:9:12 | LL | fn bar(x: &dyn Trait) {} - | ^^^^^^^^^ `Trait` cannot be made into an object + | ^^^^^^^^^ `Trait` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/dyn-incompatible-trait-should-use-where-sized.rs:5:8 | LL | trait Trait { - | ----- this trait cannot be made into an object... + | ----- this trait is not dyn compatible... LL | fn foo() where Self: Other, { } | ^^^ ...because associated function `foo` has no `self` parameter LL | fn bar(self: ()) {} @@ -20,11 +21,12 @@ LL | fn foo(&self) where Self: Other, { } help: alternatively, consider constraining `foo` so it does not apply to trait objects | LL | fn foo() where Self: Other, Self: Sized { } - | ~~~~~~~~~~~~~ + | +++++++++++ help: consider changing method `bar`'s `self` parameter to be `&self` | -LL | fn bar(self: &Self) {} - | ~~~~~ +LL - fn bar(self: ()) {} +LL + fn bar(self: &Self) {} + | error[E0307]: invalid `self` parameter type: `()` --> $DIR/dyn-incompatible-trait-should-use-where-sized.rs:6:18 diff --git a/tests/ui/suggestions/expected-boxed-future-isnt-pinned.stderr b/tests/ui/suggestions/expected-boxed-future-isnt-pinned.stderr index 42bc094859a97..61a1d30d31d4a 100644 --- a/tests/ui/suggestions/expected-boxed-future-isnt-pinned.stderr +++ b/tests/ui/suggestions/expected-boxed-future-isnt-pinned.stderr @@ -32,7 +32,7 @@ error[E0308]: mismatched types LL | fn baz + Send + 'static>(x: F) -> BoxFuture<'static, i32> { | - found this type parameter LL | Pin::new(x) - | -------- ^ expected `Box + Send>`, found type parameter `F` + | -------- ^ expected `Box + Send>`, found type parameter `F` | | | arguments to this function are incorrect | help: use `Box::pin` to pin and box this expression: `Box::pin` diff --git a/tests/ui/suggestions/field-access.stderr b/tests/ui/suggestions/field-access.stderr index 007bc6ecf9379..362dae172c78f 100644 --- a/tests/ui/suggestions/field-access.stderr +++ b/tests/ui/suggestions/field-access.stderr @@ -12,7 +12,7 @@ LL | if let B::Fst = a {}; help: you might have meant to use field `b` whose type is `B` | LL | if let B::Fst = a.b {}; - | ~~~ + | ++ error[E0308]: mismatched types --> $DIR/field-access.rs:25:9 @@ -29,7 +29,7 @@ LL | B::Fst => (), help: you might have meant to use field `b` whose type is `B` | LL | match a.b { - | ~~~ + | ++ error[E0308]: mismatched types --> $DIR/field-access.rs:26:9 @@ -46,7 +46,7 @@ LL | B::Snd => (), help: you might have meant to use field `b` whose type is `B` | LL | match a.b { - | ~~~ + | ++ error[E0308]: mismatched types --> $DIR/field-access.rs:32:9 @@ -59,8 +59,9 @@ LL | 1u32 => (), | help: you might have meant to use field `bar` whose type is `u32` | -LL | match unsafe { foo.bar } { - | ~~~~~~~~~~~~~~~~~~ +LL - match foo { +LL + match unsafe { foo.bar } { + | error: aborting due to 4 previous errors diff --git a/tests/ui/suggestions/fn-to-method.import_trait_associated_functions.stderr b/tests/ui/suggestions/fn-to-method.import_trait_associated_functions.stderr new file mode 100644 index 0000000000000..593a90d728faa --- /dev/null +++ b/tests/ui/suggestions/fn-to-method.import_trait_associated_functions.stderr @@ -0,0 +1,39 @@ +error[E0425]: cannot find function `cmp` in this scope + --> $DIR/fn-to-method.rs:12:13 + | +LL | let x = cmp(&1, &2); + | ^^^ not found in this scope + | +help: consider importing one of these associated functions + | +LL + use std::cmp::Ord::cmp; + | +LL + use std::iter::Iterator::cmp; + | + +error[E0425]: cannot find function `len` in this scope + --> $DIR/fn-to-method.rs:16:13 + | +LL | let y = len([1, 2, 3]); + | ^^^ not found in this scope + | +help: consider importing this associated function + | +LL + use std::iter::ExactSizeIterator::len; + | + +error[E0425]: cannot find function `bar` in this scope + --> $DIR/fn-to-method.rs:20:13 + | +LL | let z = bar(Foo); + | ^^^ not found in this scope + | +help: use the `.` operator to call the method `bar` on `Foo` + | +LL - let z = bar(Foo); +LL + let z = Foo.bar(); + | + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/suggestions/fn-to-method.normal.stderr b/tests/ui/suggestions/fn-to-method.normal.stderr new file mode 100644 index 0000000000000..9bd9497ab4dab --- /dev/null +++ b/tests/ui/suggestions/fn-to-method.normal.stderr @@ -0,0 +1,39 @@ +error[E0425]: cannot find function `cmp` in this scope + --> $DIR/fn-to-method.rs:12:13 + | +LL | let x = cmp(&1, &2); + | ^^^ not found in this scope + | +help: use the `.` operator to call the method `Ord::cmp` on `&{integer}` + | +LL - let x = cmp(&1, &2); +LL + let x = (&1).cmp(&2); + | + +error[E0425]: cannot find function `len` in this scope + --> $DIR/fn-to-method.rs:16:13 + | +LL | let y = len([1, 2, 3]); + | ^^^ not found in this scope + | +help: use the `.` operator to call the method `len` on `&[{integer}]` + | +LL - let y = len([1, 2, 3]); +LL + let y = [1, 2, 3].len(); + | + +error[E0425]: cannot find function `bar` in this scope + --> $DIR/fn-to-method.rs:20:13 + | +LL | let z = bar(Foo); + | ^^^ not found in this scope + | +help: use the `.` operator to call the method `bar` on `Foo` + | +LL - let z = bar(Foo); +LL + let z = Foo.bar(); + | + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/suggestions/fn-to-method.rs b/tests/ui/suggestions/fn-to-method.rs index 9a35c3efc41b7..619ac44464942 100644 --- a/tests/ui/suggestions/fn-to-method.rs +++ b/tests/ui/suggestions/fn-to-method.rs @@ -1,4 +1,8 @@ +//@ revisions: normal import_trait_associated_functions +#![cfg_attr(import_trait_associated_functions, feature(import_trait_associated_functions))] struct Foo; +//[import_trait_associated_functions]~^ HELP consider importing one of these associated functions +//[import_trait_associated_functions]~| HELP consider importing this associated function impl Foo { fn bar(self) {} @@ -7,11 +11,11 @@ impl Foo { fn main() { let x = cmp(&1, &2); //~^ ERROR cannot find function `cmp` in this scope - //~| HELP use the `.` operator to call the method `Ord::cmp` on `&{integer}` + //[normal]~| HELP use the `.` operator to call the method `Ord::cmp` on `&{integer}` let y = len([1, 2, 3]); //~^ ERROR cannot find function `len` in this scope - //~| HELP use the `.` operator to call the method `len` on `&[{integer}]` + //[normal]~| HELP use the `.` operator to call the method `len` on `&[{integer}]` let z = bar(Foo); //~^ ERROR cannot find function `bar` in this scope diff --git a/tests/ui/suggestions/fn-to-method.stderr b/tests/ui/suggestions/fn-to-method.stderr deleted file mode 100644 index 36c17e60d3572..0000000000000 --- a/tests/ui/suggestions/fn-to-method.stderr +++ /dev/null @@ -1,38 +0,0 @@ -error[E0425]: cannot find function `cmp` in this scope - --> $DIR/fn-to-method.rs:8:13 - | -LL | let x = cmp(&1, &2); - | ^^^ not found in this scope - | -help: use the `.` operator to call the method `Ord::cmp` on `&{integer}` - | -LL | let x = (&1).cmp(&2); - | ~ ~~~~~~~~~ - -error[E0425]: cannot find function `len` in this scope - --> $DIR/fn-to-method.rs:12:13 - | -LL | let y = len([1, 2, 3]); - | ^^^ not found in this scope - | -help: use the `.` operator to call the method `len` on `&[{integer}]` - | -LL - let y = len([1, 2, 3]); -LL + let y = [1, 2, 3].len(); - | - -error[E0425]: cannot find function `bar` in this scope - --> $DIR/fn-to-method.rs:16:13 - | -LL | let z = bar(Foo); - | ^^^ not found in this scope - | -help: use the `.` operator to call the method `bar` on `Foo` - | -LL - let z = bar(Foo); -LL + let z = Foo.bar(); - | - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/issues/issue-40782.fixed b/tests/ui/suggestions/for-loop-missing-in.fixed similarity index 100% rename from tests/ui/issues/issue-40782.fixed rename to tests/ui/suggestions/for-loop-missing-in.fixed diff --git a/tests/ui/issues/issue-40782.rs b/tests/ui/suggestions/for-loop-missing-in.rs similarity index 100% rename from tests/ui/issues/issue-40782.rs rename to tests/ui/suggestions/for-loop-missing-in.rs diff --git a/tests/ui/suggestions/for-loop-missing-in.stderr b/tests/ui/suggestions/for-loop-missing-in.stderr new file mode 100644 index 0000000000000..4e0cb229d50a2 --- /dev/null +++ b/tests/ui/suggestions/for-loop-missing-in.stderr @@ -0,0 +1,25 @@ +error: missing `in` in `for` loop + --> $DIR/for-loop-missing-in.rs:4:11 + | +LL | for _i 0..2 { + | ^ + | +help: try adding `in` here + | +LL | for _i in 0..2 { + | ++ + +error: missing `in` in `for` loop + --> $DIR/for-loop-missing-in.rs:6:12 + | +LL | for _i of 0..2 { + | ^^ + | +help: try using `in` here instead + | +LL - for _i of 0..2 { +LL + for _i in 0..2 { + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/suggestions/ice-unwrap-probe-many-result-125876.stderr b/tests/ui/suggestions/ice-unwrap-probe-many-result-125876.stderr index d610a3b7cadd2..f943688e6578b 100644 --- a/tests/ui/suggestions/ice-unwrap-probe-many-result-125876.stderr +++ b/tests/ui/suggestions/ice-unwrap-probe-many-result-125876.stderr @@ -31,8 +31,9 @@ LL | std::ptr::from_ref(num).cast_mut().as_deref(); | help: there is a method `as_ref` with a similar name | -LL | std::ptr::from_ref(num).cast_mut().as_ref(); - | ~~~~~~ +LL - std::ptr::from_ref(num).cast_mut().as_deref(); +LL + std::ptr::from_ref(num).cast_mut().as_ref(); + | error: aborting due to 2 previous errors; 2 warnings emitted diff --git a/tests/ui/suggestions/imm-ref-trait-object-literal.stderr b/tests/ui/suggestions/imm-ref-trait-object-literal.stderr index 79fa468dc4947..4b770d572c565 100644 --- a/tests/ui/suggestions/imm-ref-trait-object-literal.stderr +++ b/tests/ui/suggestions/imm-ref-trait-object-literal.stderr @@ -15,7 +15,7 @@ LL | fn foo(_: X) {} help: consider changing this borrow's mutability | LL | foo(&mut s); - | ~~~~ + | +++ error[E0277]: the trait bound `S: Trait` is not satisfied --> $DIR/imm-ref-trait-object-literal.rs:13:7 diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr index 30f4509d49dee..204209179adf3 100644 --- a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr +++ b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr @@ -12,7 +12,7 @@ LL | fn g(mut x: impl Iterator) -> Option<&'static ()> { x.next( help: consider introducing a named lifetime parameter | LL | fn g<'a>(mut x: impl Iterator) -> Option<&'a ()> { x.next() } - | ++++ ~~~ ~~~ + | ++++ ++ ++ help: alternatively, you might want to return an owned value | LL - fn g(mut x: impl Iterator) -> Option<&()> { x.next() } @@ -33,7 +33,7 @@ LL | async fn i(mut x: impl Iterator) -> Option<&'static ()> { x help: consider introducing a named lifetime parameter | LL | async fn i<'a>(mut x: impl Iterator) -> Option<&'a ()> { x.next() } - | ++++ ~~~ ~~~ + | ++++ ++ ++ help: alternatively, you might want to return an owned value | LL - async fn i(mut x: impl Iterator) -> Option<&()> { x.next() } @@ -49,12 +49,14 @@ LL | fn g(mut x: impl Iterator) -> Option<&'_ ()> { x.next() = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values | -LL | fn g(mut x: impl Iterator) -> Option<&'static ()> { x.next() } - | ~~~~~~~ +LL - fn g(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } +LL + fn g(mut x: impl Iterator) -> Option<&'static ()> { x.next() } + | help: consider introducing a named lifetime parameter | -LL | fn g<'a>(mut x: impl Iterator) -> Option<&'a ()> { x.next() } - | ++++ ~~~ ~~~ +LL - fn g(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } +LL + fn g<'a>(mut x: impl Iterator) -> Option<&'a ()> { x.next() } + | help: alternatively, you might want to return an owned value | LL - fn g(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } @@ -70,12 +72,14 @@ LL | async fn i(mut x: impl Iterator) -> Option<&'_ ()> { x.n = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values | -LL | async fn i(mut x: impl Iterator) -> Option<&'static ()> { x.next() } - | ~~~~~~~ +LL - async fn i(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } +LL + async fn i(mut x: impl Iterator) -> Option<&'static ()> { x.next() } + | help: consider introducing a named lifetime parameter | -LL | async fn i<'a>(mut x: impl Iterator) -> Option<&'a ()> { x.next() } - | ++++ ~~~ ~~~ +LL - async fn i(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } +LL + async fn i<'a>(mut x: impl Iterator) -> Option<&'a ()> { x.next() } + | help: alternatively, you might want to return an owned value | LL - async fn i(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } @@ -96,7 +100,7 @@ LL | fn g(mut x: impl Foo) -> Option<&'static ()> { x.next() } help: consider introducing a named lifetime parameter | LL | fn g<'a>(mut x: impl Foo) -> Option<&'a ()> { x.next() } - | ++++ ~~~ + | ++++ ++ help: alternatively, you might want to return an owned value | LL - fn g(mut x: impl Foo) -> Option<&()> { x.next() } @@ -117,7 +121,7 @@ LL | fn g(mut x: impl Foo<()>) -> Option<&'static ()> { x.next() } help: consider introducing a named lifetime parameter | LL | fn g<'a>(mut x: impl Foo<()>) -> Option<&'a ()> { x.next() } - | ++++ ~~~ + | ++++ ++ help: alternatively, you might want to return an owned value | LL - fn g(mut x: impl Foo<()>) -> Option<&()> { x.next() } @@ -176,8 +180,9 @@ LL | fn f(_: impl Iterator) {} = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date help: consider introducing a named lifetime parameter | -LL | fn f<'a>(_: impl Iterator) {} - | ++++ ~~ +LL - fn f(_: impl Iterator) {} +LL + fn f<'a>(_: impl Iterator) {} + | error[E0658]: anonymous lifetimes in `impl Trait` are unstable --> $DIR/impl-trait-missing-lifetime-gated.rs:28:39 @@ -189,8 +194,9 @@ LL | fn g(mut x: impl Iterator) -> Option<&'_ ()> { x.next() = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date help: consider introducing a named lifetime parameter | -LL | fn g<'a>(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } - | ++++ ~~ +LL - fn g(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } +LL + fn g<'a>(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } + | error: lifetime may not live long enough --> $DIR/impl-trait-missing-lifetime-gated.rs:38:73 diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime.stderr b/tests/ui/suggestions/impl-trait-missing-lifetime.stderr index 70998b67c04bf..dfbc883680b23 100644 --- a/tests/ui/suggestions/impl-trait-missing-lifetime.stderr +++ b/tests/ui/suggestions/impl-trait-missing-lifetime.stderr @@ -7,12 +7,14 @@ LL | fn g(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values | -LL | fn g(mut x: impl Iterator) -> Option<&'static ()> { x.next() } - | ~~~~~~~ +LL - fn g(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } +LL + fn g(mut x: impl Iterator) -> Option<&'static ()> { x.next() } + | help: consider introducing a named lifetime parameter | -LL | fn g<'a>(mut x: impl Iterator) -> Option<&'a ()> { x.next() } - | ++++ ~~~ ~~~ +LL - fn g(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } +LL + fn g<'a>(mut x: impl Iterator) -> Option<&'a ()> { x.next() } + | help: alternatively, you might want to return an owned value | LL - fn g(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } @@ -28,12 +30,14 @@ LL | async fn i(mut x: impl Iterator) -> Option<&'_ ()> { x.next( = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values | -LL | async fn i(mut x: impl Iterator) -> Option<&'static ()> { x.next() } - | ~~~~~~~ +LL - async fn i(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } +LL + async fn i(mut x: impl Iterator) -> Option<&'static ()> { x.next() } + | help: consider introducing a named lifetime parameter | -LL | async fn i<'a>(mut x: impl Iterator) -> Option<&'a ()> { x.next() } - | ++++ ~~~ ~~~ +LL - async fn i(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } +LL + async fn i<'a>(mut x: impl Iterator) -> Option<&'a ()> { x.next() } + | help: alternatively, you might want to return an owned value | LL - async fn i(mut x: impl Iterator) -> Option<&'_ ()> { x.next() } diff --git a/tests/ui/suggestions/impl-trait-with-missing-bounds.stderr b/tests/ui/suggestions/impl-trait-with-missing-bounds.stderr index a763eb6f2f854..d0ce7c9ed4e8a 100644 --- a/tests/ui/suggestions/impl-trait-with-missing-bounds.stderr +++ b/tests/ui/suggestions/impl-trait-with-missing-bounds.stderr @@ -14,8 +14,9 @@ LL | fn qux(_: impl std::fmt::Debug) {} | ^^^^^^^^^^^^^^^ required by this bound in `qux` help: introduce a type parameter with a trait bound instead of using `impl Trait` | -LL | fn foo(constraints: I) where ::Item: Debug { - | +++++++++++++ ~ ++++++++++++++++++++++++++++++++++ +LL - fn foo(constraints: impl Iterator) { +LL + fn foo(constraints: I) where ::Item: Debug { + | error[E0277]: `::Item` doesn't implement `Debug` --> $DIR/impl-trait-with-missing-bounds.rs:14:13 @@ -33,8 +34,9 @@ LL | fn qux(_: impl std::fmt::Debug) {} | ^^^^^^^^^^^^^^^ required by this bound in `qux` help: introduce a type parameter with a trait bound instead of using `impl Trait` | -LL | fn bar(t: T, constraints: I) where T: std::fmt::Debug, ::Item: Debug { - | +++++++++++++ ~ ++++++++++++++++++++++++++++++ +LL - fn bar(t: T, constraints: impl Iterator) where T: std::fmt::Debug { +LL + fn bar(t: T, constraints: I) where T: std::fmt::Debug, ::Item: Debug { + | error[E0277]: `::Item` doesn't implement `Debug` --> $DIR/impl-trait-with-missing-bounds.rs:22:13 @@ -52,8 +54,9 @@ LL | fn qux(_: impl std::fmt::Debug) {} | ^^^^^^^^^^^^^^^ required by this bound in `qux` help: introduce a type parameter with a trait bound instead of using `impl Trait` | -LL | fn baz(t: impl std::fmt::Debug, constraints: I) where ::Item: Debug { - | +++++++++++++ ~ ++++++++++++++++++++++++++++++++++ +LL - fn baz(t: impl std::fmt::Debug, constraints: impl Iterator) { +LL + fn baz(t: impl std::fmt::Debug, constraints: I) where ::Item: Debug { + | error[E0277]: `::Item` doesn't implement `Debug` --> $DIR/impl-trait-with-missing-bounds.rs:30:13 @@ -71,8 +74,9 @@ LL | fn qux(_: impl std::fmt::Debug) {} | ^^^^^^^^^^^^^^^ required by this bound in `qux` help: introduce a type parameter with a trait bound instead of using `impl Trait` | -LL | fn bat(t: T, constraints: U, _: I) where ::Item: Debug { - | +++++++++++++ ~ ++++++++++++++++++++++++++++++++++ +LL - fn bat(t: T, constraints: impl Iterator, _: I) { +LL + fn bat(t: T, constraints: U, _: I) where ::Item: Debug { + | error[E0277]: `::Item` doesn't implement `Debug` --> $DIR/impl-trait-with-missing-bounds.rs:37:13 @@ -90,8 +94,9 @@ LL | fn qux(_: impl std::fmt::Debug) {} | ^^^^^^^^^^^^^^^ required by this bound in `qux` help: introduce a type parameter with a trait bound instead of using `impl Trait` | -LL | fn bak(constraints: I) where ::Item: Debug { - | +++++++++++++++++++++++++++++++ ~ ++++++++++++++++++++++++++++++++++ +LL - fn bak(constraints: impl Iterator + std::fmt::Debug) { +LL + fn bak(constraints: I) where ::Item: Debug { + | error[E0277]: `::Item` doesn't implement `Debug` --> $DIR/impl-trait-with-missing-bounds.rs:45:13 @@ -109,8 +114,9 @@ LL | fn qux(_: impl std::fmt::Debug) {} | ^^^^^^^^^^^^^^^ required by this bound in `qux` help: introduce a type parameter with a trait bound instead of using `impl Trait` | -LL | fn baw(constraints: I) where ::Item: Debug { - | ~~~~~~~~~~~~~ ~ ++++++++++++++++++++++++++++++++++ +LL - fn baw<>(constraints: impl Iterator) { +LL + fn baw(constraints: I) where ::Item: Debug { + | error: aborting due to 6 previous errors diff --git a/tests/ui/suggestions/import-visible-path-39175.fixed b/tests/ui/suggestions/import-visible-path-39175.fixed new file mode 100644 index 0000000000000..1f2b5b8b0ce66 --- /dev/null +++ b/tests/ui/suggestions/import-visible-path-39175.fixed @@ -0,0 +1,16 @@ +// This test ignores some platforms as the particular extension trait used +// to demonstrate the issue is only available on unix. This is fine as +// the fix to suggested paths is not platform-dependent and will apply on +// these platforms also. + +//@ run-rustfix +//@ only-unix (the diagnostics is influenced by `use std::os::unix::process::CommandExt;`) + +use std::os::unix::process::CommandExt; +use std::process::Command; +// use std::os::unix::process::CommandExt; + +fn main() { + let _ = Command::new("echo").arg("hello").exec(); +//~^ ERROR no method named `exec` +} diff --git a/tests/ui/suggestions/import-visible-path-39175.rs b/tests/ui/suggestions/import-visible-path-39175.rs new file mode 100644 index 0000000000000..a7e6134b2d9c0 --- /dev/null +++ b/tests/ui/suggestions/import-visible-path-39175.rs @@ -0,0 +1,15 @@ +// This test ignores some platforms as the particular extension trait used +// to demonstrate the issue is only available on unix. This is fine as +// the fix to suggested paths is not platform-dependent and will apply on +// these platforms also. + +//@ run-rustfix +//@ only-unix (the diagnostics is influenced by `use std::os::unix::process::CommandExt;`) + +use std::process::Command; +// use std::os::unix::process::CommandExt; + +fn main() { + let _ = Command::new("echo").arg("hello").exec(); +//~^ ERROR no method named `exec` +} diff --git a/tests/ui/suggestions/import-visible-path-39175.stderr b/tests/ui/suggestions/import-visible-path-39175.stderr new file mode 100644 index 0000000000000..0b558ca4b52b1 --- /dev/null +++ b/tests/ui/suggestions/import-visible-path-39175.stderr @@ -0,0 +1,17 @@ +error[E0599]: no method named `exec` found for mutable reference `&mut Command` in the current scope + --> $DIR/import-visible-path-39175.rs:13:47 + | +LL | let _ = Command::new("echo").arg("hello").exec(); + | ^^^^ + | + = help: items from traits can only be used if the trait is in scope +help: there is a method `pre_exec` with a similar name, but with different arguments + --> $SRC_DIR/std/src/os/unix/process.rs:LL:COL +help: trait `CommandExt` which provides `exec` is implemented but not in scope; perhaps you want to import it + | +LL + use std::os::unix::process::CommandExt; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/suggestions/incorrect-variant-literal.svg b/tests/ui/suggestions/incorrect-variant-literal.svg index 980a7b29a0080..0f2ade633c5ad 100644 --- a/tests/ui/suggestions/incorrect-variant-literal.svg +++ b/tests/ui/suggestions/incorrect-variant-literal.svg @@ -1,4 +1,4 @@ - + ` --> $DIR/suggest-field-through-deref.rs:16:15 @@ -50,8 +53,9 @@ LL | let _ = c.longname; | help: a field with a similar name exists | -LL | let _ = c.unwrap().long_name; - | ~~~~~~~~~~~~~~~~~~ +LL - let _ = c.longname; +LL + let _ = c.unwrap().long_name; + | error[E0609]: no field `long_name` on type `Result` --> $DIR/suggest-field-through-deref.rs:20:15 diff --git a/tests/ui/suggestions/suggest-let-and-typo-issue-132483.stderr b/tests/ui/suggestions/suggest-let-and-typo-issue-132483.stderr index c84f9363f033b..57983a07bf558 100644 --- a/tests/ui/suggestions/suggest-let-and-typo-issue-132483.stderr +++ b/tests/ui/suggestions/suggest-let-and-typo-issue-132483.stderr @@ -6,8 +6,9 @@ LL | x2 = 1; | help: a local variable with a similar name exists | -LL | x1 = 1; - | ~~ +LL - x2 = 1; +LL + x1 = 1; + | help: you might have meant to introduce a new binding | LL | let x2 = 1; diff --git a/tests/ui/suggestions/suggest-let-for-assignment.stderr b/tests/ui/suggestions/suggest-let-for-assignment.stderr index 8d97dbeb14a7a..9dc859dbe0e0c 100644 --- a/tests/ui/suggestions/suggest-let-for-assignment.stderr +++ b/tests/ui/suggestions/suggest-let-for-assignment.stderr @@ -40,8 +40,9 @@ LL | let_some_variable = 6; | help: you might have meant to introduce a new binding | -LL | let some_variable = 6; - | ~~~~~~~~~~~~~~~~~ +LL - let_some_variable = 6; +LL + let some_variable = 6; + | error[E0425]: cannot find value `some_variable` in this scope --> $DIR/suggest-let-for-assignment.rs:11:35 @@ -57,8 +58,9 @@ LL | letother_variable = 6; | help: you might have meant to introduce a new binding | -LL | let other_variable = 6; - | ~~~~~~~~~~~~~~~~~~ +LL - letother_variable = 6; +LL + let other_variable = 6; + | error[E0425]: cannot find value `other_variable` in this scope --> $DIR/suggest-let-for-assignment.rs:14:36 diff --git a/tests/ui/suggestions/suggest-methods.stderr b/tests/ui/suggestions/suggest-methods.stderr index 5bacad8c6e89f..b6925a4c62637 100644 --- a/tests/ui/suggestions/suggest-methods.stderr +++ b/tests/ui/suggestions/suggest-methods.stderr @@ -21,8 +21,9 @@ LL | let _ = s.is_emtpy(); | help: there is a method `is_empty` with a similar name | -LL | let _ = s.is_empty(); - | ~~~~~~~~ +LL - let _ = s.is_emtpy(); +LL + let _ = s.is_empty(); + | error[E0599]: no method named `count_eos` found for type `u32` in the current scope --> $DIR/suggest-methods.rs:27:19 @@ -32,8 +33,9 @@ LL | let _ = 63u32.count_eos(); | help: there is a method `count_zeros` with a similar name | -LL | let _ = 63u32.count_zeros(); - | ~~~~~~~~~~~ +LL - let _ = 63u32.count_eos(); +LL + let _ = 63u32.count_zeros(); + | error[E0599]: no method named `count_o` found for type `u32` in the current scope --> $DIR/suggest-methods.rs:30:19 @@ -44,7 +46,7 @@ LL | let _ = 63u32.count_o(); help: there is a method `count_ones` with a similar name | LL | let _ = 63u32.count_ones(); - | ~~~~~~~~~~ + | +++ error: aborting due to 4 previous errors diff --git a/tests/ui/suggestions/suggest-move-types.stderr b/tests/ui/suggestions/suggest-move-types.stderr index b222e8142bab5..397e8ad29c6ef 100644 --- a/tests/ui/suggestions/suggest-move-types.stderr +++ b/tests/ui/suggestions/suggest-move-types.stderr @@ -8,8 +8,9 @@ LL | struct A> { | help: move the constraint after the generic argument | -LL | struct A> { - | ~~~~~~~~~~~ +LL - struct A> { +LL + struct A> { + | error: generic arguments must come before the first constraint --> $DIR/suggest-move-types.rs:33:43 @@ -21,8 +22,9 @@ LL | struct Al<'a, T, M: OneWithLifetime> { | help: move the constraint after the generic arguments | -LL | struct Al<'a, T, M: OneWithLifetime<'a, T, A = ()>> { - | ~~~~~~~~~~~~~~~ +LL - struct Al<'a, T, M: OneWithLifetime> { +LL + struct Al<'a, T, M: OneWithLifetime<'a, T, A = ()>> { + | error: generic arguments must come before the first constraint --> $DIR/suggest-move-types.rs:40:46 @@ -34,8 +36,9 @@ LL | struct B> { | help: move the constraints after the generic arguments | -LL | struct B> { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - struct B> { +LL + struct B> { + | error: generic arguments must come before the first constraint --> $DIR/suggest-move-types.rs:48:71 @@ -47,8 +50,9 @@ LL | struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { +LL + struct Bl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<'a, 'b, 'c, T, U, V, A = (), B = (), C = ()>> { + | error: generic arguments must come before the first constraint --> $DIR/suggest-move-types.rs:57:28 @@ -60,8 +64,9 @@ LL | struct C> { | help: move the constraints after the generic arguments | -LL | struct C> { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - struct C> { +LL + struct C> { + | error: generic arguments must come before the first constraint --> $DIR/suggest-move-types.rs:65:53 @@ -73,8 +78,9 @@ LL | struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { +LL + struct Cl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<'a, 'b, 'c, T, U, V, A = (), B = (), C = ()>> { + | error: generic arguments must come before the first constraint --> $DIR/suggest-move-types.rs:74:28 @@ -86,8 +92,9 @@ LL | struct D> { | help: move the constraints after the generic arguments | -LL | struct D> { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - struct D> { +LL + struct D> { + | error: generic arguments must come before the first constraint --> $DIR/suggest-move-types.rs:82:53 @@ -99,8 +106,9 @@ LL | struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime> { +LL + struct Dl<'a, 'b, 'c, T, U, V, M: ThreeWithLifetime<'a, 'b, 'c, T, U, V, A = (), B = (), C = ()>> { + | error[E0747]: type provided when a lifetime was expected --> $DIR/suggest-move-types.rs:33:43 diff --git a/tests/ui/suggestions/suggest-null-ptr.stderr b/tests/ui/suggestions/suggest-null-ptr.stderr index 66a79d0749ee8..5be33d47237d2 100644 --- a/tests/ui/suggestions/suggest-null-ptr.stderr +++ b/tests/ui/suggestions/suggest-null-ptr.stderr @@ -12,11 +12,12 @@ note: function defined here --> $DIR/suggest-null-ptr.rs:7:8 | LL | fn foo(ptr: *const u8); - | ^^^ + | ^^^ --- help: if you meant to create a null pointer, use `std::ptr::null()` | -LL | foo(std::ptr::null()); - | ~~~~~~~~~~~~~~~~ +LL - foo(0); +LL + foo(std::ptr::null()); + | error[E0308]: mismatched types --> $DIR/suggest-null-ptr.rs:21:17 @@ -32,11 +33,12 @@ note: function defined here --> $DIR/suggest-null-ptr.rs:9:8 | LL | fn foo_mut(ptr: *mut u8); - | ^^^^^^^ + | ^^^^^^^ --- help: if you meant to create a null pointer, use `std::ptr::null_mut()` | -LL | foo_mut(std::ptr::null_mut()); - | ~~~~~~~~~~~~~~~~~~~~ +LL - foo_mut(0); +LL + foo_mut(std::ptr::null_mut()); + | error[E0308]: mismatched types --> $DIR/suggest-null-ptr.rs:24:15 @@ -52,11 +54,12 @@ note: function defined here --> $DIR/suggest-null-ptr.rs:11:8 | LL | fn usize(ptr: *const usize); - | ^^^^^ + | ^^^^^ --- help: if you meant to create a null pointer, use `std::ptr::null()` | -LL | usize(std::ptr::null()); - | ~~~~~~~~~~~~~~~~ +LL - usize(0); +LL + usize(std::ptr::null()); + | error[E0308]: mismatched types --> $DIR/suggest-null-ptr.rs:27:19 @@ -72,11 +75,12 @@ note: function defined here --> $DIR/suggest-null-ptr.rs:13:8 | LL | fn usize_mut(ptr: *mut usize); - | ^^^^^^^^^ + | ^^^^^^^^^ --- help: if you meant to create a null pointer, use `std::ptr::null_mut()` | -LL | usize_mut(std::ptr::null_mut()); - | ~~~~~~~~~~~~~~~~~~~~ +LL - usize_mut(0); +LL + usize_mut(std::ptr::null_mut()); + | error: aborting due to 4 previous errors diff --git a/tests/ui/suggestions/suggest-ref-mut.rs b/tests/ui/suggestions/suggest-ref-mut.rs index b40439b8e372c..9f5df9303c330 100644 --- a/tests/ui/suggestions/suggest-ref-mut.rs +++ b/tests/ui/suggestions/suggest-ref-mut.rs @@ -3,7 +3,7 @@ struct X(usize); impl X { fn zap(&self) { //~^ HELP - //~| SUGGESTION &mut self + //~| SUGGESTION mut self.0 = 32; //~^ ERROR } diff --git a/tests/ui/suggestions/suggest-ref-mut.stderr b/tests/ui/suggestions/suggest-ref-mut.stderr index cc00022ab8e3d..935a04c052ac9 100644 --- a/tests/ui/suggestions/suggest-ref-mut.stderr +++ b/tests/ui/suggestions/suggest-ref-mut.stderr @@ -7,7 +7,7 @@ LL | self.0 = 32; help: consider changing this to be a mutable reference | LL | fn zap(&mut self) { - | ~~~~~~~~~ + | +++ error[E0594]: cannot assign to `*foo`, which is behind a `&` reference --> $DIR/suggest-ref-mut.rs:15:5 diff --git a/tests/ui/suggestions/suggest-slice-swap.stderr b/tests/ui/suggestions/suggest-slice-swap.stderr index 2840fc0a76116..95b547aad5de9 100644 --- a/tests/ui/suggestions/suggest-slice-swap.stderr +++ b/tests/ui/suggestions/suggest-slice-swap.stderr @@ -9,8 +9,9 @@ LL | std::mem::swap(&mut arr[0], &mut arr[1]); | help: use `.swap()` to swap elements at the specified indices instead | -LL | arr.swap(1, 0); - | ~~~~~~~~~~~~~~ +LL - std::mem::swap(&mut arr[0], &mut arr[1]); +LL + arr.swap(1, 0); + | error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/suggest-std-when-using-type.fixed b/tests/ui/suggestions/suggest-std-when-using-type.fixed index ebbb854175bda..0547940f94d31 100644 --- a/tests/ui/suggestions/suggest-std-when-using-type.fixed +++ b/tests/ui/suggestions/suggest-std-when-using-type.fixed @@ -1,8 +1,5 @@ //@ run-rustfix fn main() { let pi = std::f32::consts::PI; //~ ERROR ambiguous associated type - let bytes = "hello world".as_bytes(); - let string = std::str::from_utf8(bytes).unwrap(); - //~^ ERROR no function or associated item named `from_utf8` found - println!("{pi} {bytes:?} {string}"); + println!("{pi}"); } diff --git a/tests/ui/suggestions/suggest-std-when-using-type.rs b/tests/ui/suggestions/suggest-std-when-using-type.rs index 06aab63d68856..cd55c5d13bd18 100644 --- a/tests/ui/suggestions/suggest-std-when-using-type.rs +++ b/tests/ui/suggestions/suggest-std-when-using-type.rs @@ -1,8 +1,5 @@ //@ run-rustfix fn main() { let pi = f32::consts::PI; //~ ERROR ambiguous associated type - let bytes = "hello world".as_bytes(); - let string = str::from_utf8(bytes).unwrap(); - //~^ ERROR no function or associated item named `from_utf8` found - println!("{pi} {bytes:?} {string}"); + println!("{pi}"); } diff --git a/tests/ui/suggestions/suggest-std-when-using-type.stderr b/tests/ui/suggestions/suggest-std-when-using-type.stderr index 6f890b87b24bd..7f8545f461d9e 100644 --- a/tests/ui/suggestions/suggest-std-when-using-type.stderr +++ b/tests/ui/suggestions/suggest-std-when-using-type.stderr @@ -9,18 +9,6 @@ help: you are looking for the module in `std`, not the primitive type LL | let pi = std::f32::consts::PI; | +++++ -error[E0599]: no function or associated item named `from_utf8` found for type `str` in the current scope - --> $DIR/suggest-std-when-using-type.rs:5:23 - | -LL | let string = str::from_utf8(bytes).unwrap(); - | ^^^^^^^^^ function or associated item not found in `str` - | -help: you are looking for the module in `std`, not the primitive type - | -LL | let string = std::str::from_utf8(bytes).unwrap(); - | +++++ - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0223, E0599. -For more information about an error, try `rustc --explain E0223`. +For more information about this error, try `rustc --explain E0223`. diff --git a/tests/ui/suggestions/suggest-swapping-self-ty-and-trait-edition-2021.stderr b/tests/ui/suggestions/suggest-swapping-self-ty-and-trait-edition-2021.stderr index 0bd601e217034..e73aba51847e4 100644 --- a/tests/ui/suggestions/suggest-swapping-self-ty-and-trait-edition-2021.stderr +++ b/tests/ui/suggestions/suggest-swapping-self-ty-and-trait-edition-2021.stderr @@ -6,8 +6,9 @@ LL | impl<'a, T> Struct for Trait<'a, T> {} | help: `impl` items mention the trait being implemented first and the type it is being implemented for second | -LL | impl<'a, T> Trait<'a, T> for Struct {} - | ~~~~~~~~~~~~ ~~~~~~~~~ +LL - impl<'a, T> Struct for Trait<'a, T> {} +LL + impl<'a, T> Trait<'a, T> for Struct {} + | error[E0404]: expected trait, found enum `Enum` --> $DIR/suggest-swapping-self-ty-and-trait-edition-2021.rs:19:13 @@ -17,8 +18,9 @@ LL | impl<'a, T> Enum for Trait<'a, T> {} | help: `impl` items mention the trait being implemented first and the type it is being implemented for second | -LL | impl<'a, T> Trait<'a, T> for Enum {} - | ~~~~~~~~~~~~ ~~~~~~~ +LL - impl<'a, T> Enum for Trait<'a, T> {} +LL + impl<'a, T> Trait<'a, T> for Enum {} + | error[E0404]: expected trait, found union `Union` --> $DIR/suggest-swapping-self-ty-and-trait-edition-2021.rs:23:13 @@ -28,8 +30,9 @@ LL | impl<'a, T> Union for Trait<'a, T> {} | help: `impl` items mention the trait being implemented first and the type it is being implemented for second | -LL | impl<'a, T> Trait<'a, T> for Union {} - | ~~~~~~~~~~~~ ~~~~~~~~ +LL - impl<'a, T> Union for Trait<'a, T> {} +LL + impl<'a, T> Trait<'a, T> for Union {} + | error[E0392]: type parameter `T` is never used --> $DIR/suggest-swapping-self-ty-and-trait-edition-2021.rs:5:19 diff --git a/tests/ui/suggestions/suggest-swapping-self-ty-and-trait.stderr b/tests/ui/suggestions/suggest-swapping-self-ty-and-trait.stderr index d1da9a89c191b..929f893e34f5a 100644 --- a/tests/ui/suggestions/suggest-swapping-self-ty-and-trait.stderr +++ b/tests/ui/suggestions/suggest-swapping-self-ty-and-trait.stderr @@ -6,8 +6,9 @@ LL | impl<'a, T> Struct for Trait<'a, T> {} | help: `impl` items mention the trait being implemented first and the type it is being implemented for second | -LL | impl<'a, T> Trait<'a, T> for Struct {} - | ~~~~~~~~~~~~ ~~~~~~~~~ +LL - impl<'a, T> Struct for Trait<'a, T> {} +LL + impl<'a, T> Trait<'a, T> for Struct {} + | error[E0404]: expected trait, found enum `Enum` --> $DIR/suggest-swapping-self-ty-and-trait.rs:18:13 @@ -17,8 +18,9 @@ LL | impl<'a, T> Enum for Trait<'a, T> {} | help: `impl` items mention the trait being implemented first and the type it is being implemented for second | -LL | impl<'a, T> Trait<'a, T> for Enum {} - | ~~~~~~~~~~~~ ~~~~~~~ +LL - impl<'a, T> Enum for Trait<'a, T> {} +LL + impl<'a, T> Trait<'a, T> for Enum {} + | error[E0404]: expected trait, found union `Union` --> $DIR/suggest-swapping-self-ty-and-trait.rs:23:13 @@ -28,8 +30,9 @@ LL | impl<'a, T> Union for Trait<'a, T> {} | help: `impl` items mention the trait being implemented first and the type it is being implemented for second | -LL | impl<'a, T> Trait<'a, T> for Union {} - | ~~~~~~~~~~~~ ~~~~~~~~ +LL - impl<'a, T> Union for Trait<'a, T> {} +LL + impl<'a, T> Trait<'a, T> for Union {} + | error[E0392]: type parameter `T` is never used --> $DIR/suggest-swapping-self-ty-and-trait.rs:3:19 diff --git a/tests/ui/suggestions/suggest-trait-in-ufcs-in-hrtb.stderr b/tests/ui/suggestions/suggest-trait-in-ufcs-in-hrtb.stderr index cabaa76a8867d..fac93da98293d 100644 --- a/tests/ui/suggestions/suggest-trait-in-ufcs-in-hrtb.stderr +++ b/tests/ui/suggestions/suggest-trait-in-ufcs-in-hrtb.stderr @@ -6,10 +6,12 @@ LL | impl Foo for Bar where for<'a> <&'a S>::Item: Foo {} | help: use fully-qualified syntax | -LL | impl Foo for Bar where for<'a> <&'a S as IntoAsyncIterator>::Item: Foo {} - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | impl Foo for Bar where for<'a> <&'a S as IntoIterator>::Item: Foo {} - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - impl Foo for Bar where for<'a> <&'a S>::Item: Foo {} +LL + impl Foo for Bar where for<'a> <&'a S as IntoAsyncIterator>::Item: Foo {} + | +LL - impl Foo for Bar where for<'a> <&'a S>::Item: Foo {} +LL + impl Foo for Bar where for<'a> <&'a S as IntoIterator>::Item: Foo {} + | error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/suggest-tryinto-edition-change.stderr b/tests/ui/suggestions/suggest-tryinto-edition-change.stderr index 5be55f75cd15d..0008b4fb5ed2e 100644 --- a/tests/ui/suggestions/suggest-tryinto-edition-change.stderr +++ b/tests/ui/suggestions/suggest-tryinto-edition-change.stderr @@ -31,8 +31,9 @@ LL | let _v: Vec<_> = FromIterator::from_iter(&[1]); = note: 'std::iter::FromIterator' is included in the prelude starting in Edition 2021 help: a trait with a similar name exists | -LL | let _v: Vec<_> = IntoIterator::from_iter(&[1]); - | ~~~~~~~~~~~~ +LL - let _v: Vec<_> = FromIterator::from_iter(&[1]); +LL + let _v: Vec<_> = IntoIterator::from_iter(&[1]); + | help: consider importing this trait | LL + use std::iter::FromIterator; @@ -55,8 +56,9 @@ LL + use std::convert::TryInto; | help: there is a method `into` with a similar name | -LL | let _i: i16 = 0_i32.into().unwrap(); - | ~~~~ +LL - let _i: i16 = 0_i32.try_into().unwrap(); +LL + let _i: i16 = 0_i32.into().unwrap(); + | error: aborting due to 4 previous errors diff --git a/tests/ui/suggestions/suggest-using-chars.stderr b/tests/ui/suggestions/suggest-using-chars.stderr index ba80ec6a2019c..a197223beb056 100644 --- a/tests/ui/suggestions/suggest-using-chars.stderr +++ b/tests/ui/suggestions/suggest-using-chars.stderr @@ -6,8 +6,9 @@ LL | let _ = "foo".iter(); | help: because of the in-memory representation of `&str`, to obtain an `Iterator` over each of its codepoint use method `chars` | -LL | let _ = "foo".chars(); - | ~~~~~ +LL - let _ = "foo".iter(); +LL + let _ = "foo".chars(); + | error[E0599]: no method named `foo` found for reference `&'static str` in the current scope --> $DIR/suggest-using-chars.rs:3:19 @@ -23,8 +24,9 @@ LL | let _ = String::from("bar").iter(); | help: because of the in-memory representation of `&str`, to obtain an `Iterator` over each of its codepoint use method `chars` | -LL | let _ = String::from("bar").chars(); - | ~~~~~ +LL - let _ = String::from("bar").iter(); +LL + let _ = String::from("bar").chars(); + | error[E0599]: no method named `iter` found for reference `&String` in the current scope --> $DIR/suggest-using-chars.rs:5:36 @@ -34,8 +36,9 @@ LL | let _ = (&String::from("bar")).iter(); | help: because of the in-memory representation of `&str`, to obtain an `Iterator` over each of its codepoint use method `chars` | -LL | let _ = (&String::from("bar")).chars(); - | ~~~~~ +LL - let _ = (&String::from("bar")).iter(); +LL + let _ = (&String::from("bar")).chars(); + | error[E0599]: no method named `iter` found for type `{integer}` in the current scope --> $DIR/suggest-using-chars.rs:6:15 diff --git a/tests/ui/suggestions/suggest-variants.stderr b/tests/ui/suggestions/suggest-variants.stderr index d93bf2d8cd72c..50286a9687594 100644 --- a/tests/ui/suggestions/suggest-variants.stderr +++ b/tests/ui/suggestions/suggest-variants.stderr @@ -9,8 +9,9 @@ LL | println!("My shape is {:?}", Shape::Squareee { size: 5}); | help: there is a variant with a similar name | -LL | println!("My shape is {:?}", Shape::Square { size: 5}); - | ~~~~~~ +LL - println!("My shape is {:?}", Shape::Squareee { size: 5}); +LL + println!("My shape is {:?}", Shape::Square { size: 5}); + | error[E0599]: no variant named `Circl` found for enum `Shape` --> $DIR/suggest-variants.rs:13:41 @@ -24,7 +25,7 @@ LL | println!("My shape is {:?}", Shape::Circl { size: 5}); help: there is a variant with a similar name | LL | println!("My shape is {:?}", Shape::Circle { size: 5}); - | ~~~~~~ + | + error[E0599]: no variant named `Rombus` found for enum `Shape` --> $DIR/suggest-variants.rs:14:41 @@ -46,8 +47,9 @@ LL | Shape::Squareee; | help: there is a variant with a similar name | -LL | Shape::Square { size: /* value */ }; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - Shape::Squareee; +LL + Shape::Square { size: /* value */ }; + | error[E0599]: no variant or associated item named `Circl` found for enum `Shape` in the current scope --> $DIR/suggest-variants.rs:16:12 @@ -61,7 +63,7 @@ LL | Shape::Circl; help: there is a variant with a similar name | LL | Shape::Circle { radius: /* value */ }; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | +++++++++++++++++++++++++ error[E0599]: no variant or associated item named `Rombus` found for enum `Shape` in the current scope --> $DIR/suggest-variants.rs:17:12 diff --git a/tests/ui/suggestions/suggest_print_over_printf.stderr b/tests/ui/suggestions/suggest_print_over_printf.stderr index 8a79745133c93..48a50de0f7ab5 100644 --- a/tests/ui/suggestions/suggest_print_over_printf.stderr +++ b/tests/ui/suggestions/suggest_print_over_printf.stderr @@ -6,8 +6,9 @@ LL | printf("%d", x); | help: you may have meant to use the `print` macro | -LL | print!("%d", x); - | ~~~~~~ +LL - printf("%d", x); +LL + print!("%d", x); + | error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/trait-with-missing-associated-type-restriction.stderr b/tests/ui/suggestions/trait-with-missing-associated-type-restriction.stderr index 980c2455c8e41..df59a28c4b97b 100644 --- a/tests/ui/suggestions/trait-with-missing-associated-type-restriction.stderr +++ b/tests/ui/suggestions/trait-with-missing-associated-type-restriction.stderr @@ -94,7 +94,7 @@ note: method defined here --> $DIR/trait-with-missing-associated-type-restriction.rs:9:8 | LL | fn funk(&self, _: Self::A); - | ^^^^ + | ^^^^ - help: consider constraining the associated type `>::A` to `{integer}` | LL | fn bar2>(x: T) { diff --git a/tests/ui/suggestions/type-ascription-instead-of-path-2.stderr b/tests/ui/suggestions/type-ascription-instead-of-path-2.stderr index ba0682cda3212..0b37bf9a57bb7 100644 --- a/tests/ui/suggestions/type-ascription-instead-of-path-2.stderr +++ b/tests/ui/suggestions/type-ascription-instead-of-path-2.stderr @@ -8,7 +8,7 @@ LL | let _ = vec![Ok(2)].into_iter().collect:,_>>()?; help: maybe write a path separator here | LL | let _ = vec![Ok(2)].into_iter().collect::,_>>()?; - | ~~ + | + error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/type-ascription-instead-of-path-in-type.stderr b/tests/ui/suggestions/type-ascription-instead-of-path-in-type.stderr index 56b6a69a283f4..a424bc7e72452 100644 --- a/tests/ui/suggestions/type-ascription-instead-of-path-in-type.stderr +++ b/tests/ui/suggestions/type-ascription-instead-of-path-in-type.stderr @@ -7,7 +7,7 @@ LL | let _: Vec = A::B; help: you might have meant to write a path instead of an associated type bound | LL | let _: Vec = A::B; - | ~~ + | + error[E0107]: struct takes at least 1 generic argument but 0 generic arguments were supplied --> $DIR/type-ascription-instead-of-path-in-type.rs:6:12 diff --git a/tests/ui/suggestions/type-mismatch-byte-literal.stderr b/tests/ui/suggestions/type-mismatch-byte-literal.stderr index 3d27149f0dcf1..7211d77fdcba1 100644 --- a/tests/ui/suggestions/type-mismatch-byte-literal.stderr +++ b/tests/ui/suggestions/type-mismatch-byte-literal.stderr @@ -9,7 +9,7 @@ LL | let _x: u8 = 'X'; help: if you meant to write a byte literal, prefix with `b` | LL | let _x: u8 = b'X'; - | ~~~~ + | + error[E0308]: mismatched types --> $DIR/type-mismatch-byte-literal.rs:11:9 @@ -27,7 +27,7 @@ LL | fn foo(_t: u8) {} help: if you meant to write a byte literal, prefix with `b` | LL | foo(b'#'); - | ~~~~ + | + error[E0308]: mismatched types --> $DIR/type-mismatch-byte-literal.rs:15:18 @@ -40,7 +40,7 @@ LL | let _a: u8 = '\x20'; help: if you meant to write a byte literal, prefix with `b` | LL | let _a: u8 = b'\x20'; - | ~~~~~~~ + | + error[E0308]: mismatched types --> $DIR/type-mismatch-byte-literal.rs:20:9 diff --git a/tests/ui/suggestions/type-mismatch-struct-field-shorthand-2.stderr b/tests/ui/suggestions/type-mismatch-struct-field-shorthand-2.stderr index fb3573ee2a4ee..e60de526945ae 100644 --- a/tests/ui/suggestions/type-mismatch-struct-field-shorthand-2.stderr +++ b/tests/ui/suggestions/type-mismatch-struct-field-shorthand-2.stderr @@ -28,8 +28,9 @@ LL | let _ = RGB { r, g, c }; | help: a field with a similar name exists | -LL | let _ = RGB { r, g, b }; - | ~ +LL - let _ = RGB { r, g, c }; +LL + let _ = RGB { r, g, b }; + | error: aborting due to 3 previous errors diff --git a/tests/ui/suggestions/undeclared-module-alloc.rs b/tests/ui/suggestions/undeclared-module-alloc.rs index e5f22369b941a..a0bddc94471c1 100644 --- a/tests/ui/suggestions/undeclared-module-alloc.rs +++ b/tests/ui/suggestions/undeclared-module-alloc.rs @@ -1,5 +1,5 @@ //@ edition:2018 -use alloc::rc::Rc; //~ ERROR failed to resolve: use of undeclared crate or module `alloc` +use alloc::rc::Rc; //~ ERROR failed to resolve: use of unresolved module or unlinked crate `alloc` fn main() {} diff --git a/tests/ui/suggestions/undeclared-module-alloc.stderr b/tests/ui/suggestions/undeclared-module-alloc.stderr index a439546492b5f..00e498aa9ba9d 100644 --- a/tests/ui/suggestions/undeclared-module-alloc.stderr +++ b/tests/ui/suggestions/undeclared-module-alloc.stderr @@ -1,8 +1,8 @@ -error[E0433]: failed to resolve: use of undeclared crate or module `alloc` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `alloc` --> $DIR/undeclared-module-alloc.rs:3:5 | LL | use alloc::rc::Rc; - | ^^^^^ use of undeclared crate or module `alloc` + | ^^^^^ use of unresolved module or unlinked crate `alloc` | = help: add `extern crate alloc` to use the `alloc` crate diff --git a/tests/ui/suggestions/unnamable-types.rs b/tests/ui/suggestions/unnamable-types.rs index dd2c3536eb92e..094584ff850b3 100644 --- a/tests/ui/suggestions/unnamable-types.rs +++ b/tests/ui/suggestions/unnamable-types.rs @@ -10,7 +10,7 @@ const A = 5; static B: _ = "abc"; //~^ ERROR: the placeholder `_` is not allowed within types on item signatures for static variables //~| NOTE: not allowed in type signatures -//~| HELP: replace with the correct type +//~| HELP: replace this with a fully-specified type // FIXME: this should also suggest a function pointer, as the closure is non-capturing diff --git a/tests/ui/suggestions/unnamable-types.stderr b/tests/ui/suggestions/unnamable-types.stderr index 6623678fd0c2d..bcd1a905194ef 100644 --- a/tests/ui/suggestions/unnamable-types.stderr +++ b/tests/ui/suggestions/unnamable-types.stderr @@ -8,10 +8,13 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/unnamable-types.rs:10:11 | LL | static B: _ = "abc"; - | ^ - | | - | not allowed in type signatures - | help: replace with the correct type: `&str` + | ^ not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - static B: _ = "abc"; +LL + static B: &str = "abc"; + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants --> $DIR/unnamable-types.rs:17:10 diff --git a/tests/ui/suggestions/unnecessary_dot_for_floating_point_literal.stderr b/tests/ui/suggestions/unnecessary_dot_for_floating_point_literal.stderr index 8b48ee9f1247b..ade3f3099a354 100644 --- a/tests/ui/suggestions/unnecessary_dot_for_floating_point_literal.stderr +++ b/tests/ui/suggestions/unnecessary_dot_for_floating_point_literal.stderr @@ -10,8 +10,9 @@ LL | let _: f64 = 0..10; found struct `std::ops::Range<{integer}>` help: remove the unnecessary `.` operator for a floating point literal | -LL | let _: f64 = 0.10; - | ~ +LL - let _: f64 = 0..10; +LL + let _: f64 = 0.10; + | error[E0308]: mismatched types --> $DIR/unnecessary_dot_for_floating_point_literal.rs:3:18 @@ -22,11 +23,12 @@ LL | let _: f64 = 1..; | expected due to this | = note: expected type `f64` - found struct `RangeFrom<{integer}>` + found struct `std::ops::RangeFrom<{integer}>` help: remove the unnecessary `.` operator for a floating point literal | -LL | let _: f64 = 1.; - | ~ +LL - let _: f64 = 1..; +LL + let _: f64 = 1.; + | error[E0308]: mismatched types --> $DIR/unnecessary_dot_for_floating_point_literal.rs:4:18 @@ -40,8 +42,9 @@ LL | let _: f64 = ..10; found struct `RangeTo<{integer}>` help: remove the unnecessary `.` operator and add an integer part for a floating point literal | -LL | let _: f64 = 0.10; - | ~~ +LL - let _: f64 = ..10; +LL + let _: f64 = 0.10; + | error[E0308]: mismatched types --> $DIR/unnecessary_dot_for_floating_point_literal.rs:5:18 diff --git a/tests/ui/symbol-names/basic.legacy.stderr b/tests/ui/symbol-names/basic.legacy.stderr index 2f26c0cf0d3b3..167262dcf06b3 100644 --- a/tests/ui/symbol-names/basic.legacy.stderr +++ b/tests/ui/symbol-names/basic.legacy.stderr @@ -1,10 +1,10 @@ -error: symbol-name(_ZN5basic4main17h144191e1523a280eE) +error: symbol-name(_ZN5basic4main17hc88b9d80a69d119aE) --> $DIR/basic.rs:8:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(basic::main::h144191e1523a280e) +error: demangling(basic::main::hc88b9d80a69d119a) --> $DIR/basic.rs:8:1 | LL | #[rustc_symbol_name] diff --git a/tests/ui/symbol-names/issue-60925.legacy.stderr b/tests/ui/symbol-names/issue-60925.legacy.stderr index cc79cc8b5169e..4e17bdc45777f 100644 --- a/tests/ui/symbol-names/issue-60925.legacy.stderr +++ b/tests/ui/symbol-names/issue-60925.legacy.stderr @@ -1,10 +1,10 @@ -error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17h71f988fda3b6b180E) +error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17hbddb77d6f71afb32E) --> $DIR/issue-60925.rs:21:9 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(issue_60925::foo::Foo::foo::h71f988fda3b6b180) +error: demangling(issue_60925::foo::Foo::foo::hbddb77d6f71afb32) --> $DIR/issue-60925.rs:21:9 | LL | #[rustc_symbol_name] diff --git a/tests/ui/symbol-names/normalize-in-param-env.rs b/tests/ui/symbol-names/normalize-in-param-env.rs new file mode 100644 index 0000000000000..a1453eb13eff5 --- /dev/null +++ b/tests/ui/symbol-names/normalize-in-param-env.rs @@ -0,0 +1,38 @@ +//@ revisions: legacy v0 +//@[v0] compile-flags: -C symbol-mangling-version=v0 +//@[legacy] compile-flags: -C symbol-mangling-version=legacy -Zunstable-options +//@ build-pass + +pub struct Vec2; + +pub trait Point { + type S; +} +impl Point for Vec2 { + type S = f32; +} + +pub trait Point2: Point { + type S2; +} +impl Point2 for Vec2 { + type S2 = Self::S; +} + +trait MyFrom { + fn my_from(); +} +impl MyFrom for P { + fn my_from() { + // This is just a really dumb way to force the legacy symbol mangling to + // mangle the closure's parent impl def path *with* args. Otherwise, + // legacy symbol mangling will strip the args from the instance, meaning + // that we don't trigger the bug. + let c = || {}; + let x = Box::new(c) as Box; + } +} + +fn main() { + >::my_from(); +} diff --git a/tests/ui/tail-cps.rs b/tests/ui/tail-cps.rs index 6305e9ecdbcf1..fe99dadf7951c 100644 --- a/tests/ui/tail-cps.rs +++ b/tests/ui/tail-cps.rs @@ -1,6 +1,6 @@ //@ run-pass -fn checktrue(rs: bool) -> bool { assert!((rs)); return true; } +fn checktrue(rs: bool) -> bool { assert!(rs); return true; } pub fn main() { let k = checktrue; evenk(42, k); oddk(45, k); } diff --git a/tests/ui/target-feature/feature-hierarchy.aarch64-sve2.stderr b/tests/ui/target-feature/feature-hierarchy.aarch64-sve2.stderr deleted file mode 100644 index d5d7d7aa627ba..0000000000000 --- a/tests/ui/target-feature/feature-hierarchy.aarch64-sve2.stderr +++ /dev/null @@ -1,7 +0,0 @@ -warning: target feature `neon` cannot be toggled with `-Ctarget-feature`: unsound on hard-float targets because it changes float ABI - | - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #116344 - -warning: 1 warning emitted - diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.rs index b3171d52c510f..215e64979f736 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.rs +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.rs @@ -1,11 +1,12 @@ -//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib -//@ needs-llvm-components: x86 -#![feature(no_core, lang_items)] +//! Ensure ABI-incompatible features cannot be enabled via `#[target_feature]`. +//@ compile-flags: --target=riscv32e-unknown-none-elf --crate-type=lib +//@ needs-llvm-components: riscv +#![feature(no_core, lang_items, riscv_target_feature)] #![no_core] #[lang = "sized"] pub trait Sized {} -#[target_feature(enable = "x87")] -//~^ERROR: cannot be toggled with +#[target_feature(enable = "d")] +//~^ERROR: cannot be enabled with pub unsafe fn my_fun() {} diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.stderr index 3ebbe69d8aeca..bfe767e5ffb07 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.stderr +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-attribute.stderr @@ -1,8 +1,8 @@ -error: target feature `x87` cannot be toggled with `#[target_feature]`: unsound on hard-float targets because it changes float ABI - --> $DIR/forbidden-hardfloat-target-feature-attribute.rs:9:18 +error: target feature `d` cannot be enabled with `#[target_feature]`: this feature is incompatible with the target ABI + --> $DIR/forbidden-hardfloat-target-feature-attribute.rs:10:18 | -LL | #[target_feature(enable = "x87")] - | ^^^^^^^^^^^^^^ +LL | #[target_feature(enable = "d")] + | ^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-cfg.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-cfg.rs deleted file mode 100644 index 8755791c1c04b..0000000000000 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-cfg.rs +++ /dev/null @@ -1,14 +0,0 @@ -//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib -//@ needs-llvm-components: x86 -//@ check-pass -#![feature(no_core, lang_items)] -#![no_core] -#![allow(unexpected_cfgs)] - -#[lang = "sized"] -pub trait Sized {} - -// The compile_error macro does not exist, so if the `cfg` evaluates to `true` this -// complains about the missing macro rather than showing the error... but that's good enough. -#[cfg(not(target_feature = "x87"))] -compile_error!("the x87 feature *should* be exposed in `cfg`"); diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.rs new file mode 100644 index 0000000000000..81f138b175f29 --- /dev/null +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.rs @@ -0,0 +1,13 @@ +//! Ensure that if disabling a target feature implies disabling an ABI-required target feature, +//! we complain. +//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib +//@ needs-llvm-components: x86 +//@ compile-flags: -Ctarget-feature=-sse +// For now this is just a warning. +//@ build-pass +//@error-pattern: must be enabled to ensure that the ABI +#![feature(no_core, lang_items)] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr new file mode 100644 index 0000000000000..7ec8b04cfce01 --- /dev/null +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr @@ -0,0 +1,7 @@ +warning: target feature `sse2` must be enabled to ensure that the ABI of the current target can be implemented correctly + | + = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116344 + +warning: 1 warning emitted + diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.rs index 95c366bb3f5be..7242bcc85bfd1 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.rs +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.rs @@ -3,6 +3,7 @@ //@ compile-flags: -Ctarget-feature=-neon // For now this is just a warning. //@ build-pass +//@error-pattern: must be enabled to ensure that the ABI #![feature(no_core, lang_items)] #![no_core] diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr index d5d7d7aa627ba..b1186d5d5dc78 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr @@ -1,4 +1,4 @@ -warning: target feature `neon` cannot be toggled with `-Ctarget-feature`: unsound on hard-float targets because it changes float ABI +warning: target feature `neon` must be enabled to ensure that the ABI of the current target can be implemented correctly | = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #116344 diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.rs index fd8023664dad8..7eebcf05dc0f4 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.rs +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.rs @@ -1,8 +1,10 @@ +//! Ensure ABI-required features cannot be disabled via `-Ctarget-feature`. //@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib //@ needs-llvm-components: x86 //@ compile-flags: -Ctarget-feature=-x87 // For now this is just a warning. //@ build-pass +//@error-pattern: must be enabled to ensure that the ABI #![feature(no_core, lang_items)] #![no_core] diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr index 604ad2f991ae9..02398d27501c8 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr @@ -1,7 +1,11 @@ -warning: target feature `x87` cannot be toggled with `-Ctarget-feature`: unsound on hard-float targets because it changes float ABI +warning: target feature `x87` must be enabled to ensure that the ABI of the current target can be implemented correctly | = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #116344 -warning: 1 warning emitted +warning: unstable feature specified for `-Ctarget-feature`: `x87` + | + = note: this feature is not stably supported; its behavior can change in the future + +warning: 2 warnings emitted diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag.rs b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag.rs new file mode 100644 index 0000000000000..f277a309cd698 --- /dev/null +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag.rs @@ -0,0 +1,12 @@ +//! Ensure ABI-incompatible features cannot be enabled via `-Ctarget-feature`. +//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib +//@ needs-llvm-components: x86 +//@ compile-flags: -Ctarget-feature=+soft-float +// For now this is just a warning. +//@ build-pass +//@error-pattern: must be disabled to ensure that the ABI +#![feature(no_core, lang_items, riscv_target_feature)] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag.stderr new file mode 100644 index 0000000000000..e49672f33b9a8 --- /dev/null +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag.stderr @@ -0,0 +1,11 @@ +warning: target feature `soft-float` must be disabled to ensure that the ABI of the current target can be implemented correctly + | + = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116344 + +warning: unstable feature specified for `-Ctarget-feature`: `soft-float` + | + = note: this feature is not stably supported; its behavior can change in the future + +warning: 2 warnings emitted + diff --git a/tests/ui/target-feature/forbidden-target-feature-attribute.rs b/tests/ui/target-feature/forbidden-target-feature-attribute.rs index f13cdd17da639..6bb6f8aaffb6f 100644 --- a/tests/ui/target-feature/forbidden-target-feature-attribute.rs +++ b/tests/ui/target-feature/forbidden-target-feature-attribute.rs @@ -1,11 +1,12 @@ -//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib -//@ needs-llvm-components: x86 +//! Ensure "forbidden" target features cannot be enabled via `#[target_feature]`. +//@ compile-flags: --target=riscv32e-unknown-none-elf --crate-type=lib +//@ needs-llvm-components: riscv #![feature(no_core, lang_items)] #![no_core] #[lang = "sized"] pub trait Sized {} -#[target_feature(enable = "soft-float")] -//~^ERROR: cannot be toggled with +#[target_feature(enable = "forced-atomics")] +//~^ERROR: cannot be enabled with pub unsafe fn my_fun() {} diff --git a/tests/ui/target-feature/forbidden-target-feature-attribute.stderr b/tests/ui/target-feature/forbidden-target-feature-attribute.stderr index 27ac4aaf9605c..f8ea0c0e793fb 100644 --- a/tests/ui/target-feature/forbidden-target-feature-attribute.stderr +++ b/tests/ui/target-feature/forbidden-target-feature-attribute.stderr @@ -1,8 +1,8 @@ -error: target feature `soft-float` cannot be toggled with `#[target_feature]`: unsound because it changes float ABI - --> $DIR/forbidden-target-feature-attribute.rs:9:18 +error: target feature `forced-atomics` cannot be enabled with `#[target_feature]`: unsound because it changes the ABI of atomic operations + --> $DIR/forbidden-target-feature-attribute.rs:10:18 | -LL | #[target_feature(enable = "soft-float")] - | ^^^^^^^^^^^^^^^^^^^^^ +LL | #[target_feature(enable = "forced-atomics")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/target-feature/forbidden-target-feature-cfg.rs b/tests/ui/target-feature/forbidden-target-feature-cfg.rs index 1f001e9f8ff8a..e848ffde018e2 100644 --- a/tests/ui/target-feature/forbidden-target-feature-cfg.rs +++ b/tests/ui/target-feature/forbidden-target-feature-cfg.rs @@ -1,5 +1,6 @@ -//@ compile-flags: --target=x86_64-unknown-none --crate-type=lib -//@ needs-llvm-components: x86 +//! Ensure "forbidden" target features are not exposed via `cfg`. +//@ compile-flags: --target=riscv32e-unknown-none-elf --crate-type=lib +//@ needs-llvm-components: riscv //@ check-pass #![feature(no_core, lang_items)] #![no_core] @@ -10,5 +11,5 @@ pub trait Sized {} // The compile_error macro does not exist, so if the `cfg` evaluates to `true` this // complains about the missing macro rather than showing the error... but that's good enough. -#[cfg(target_feature = "soft-float")] -compile_error!("the soft-float feature should not be exposed in `cfg`"); +#[cfg(target_feature = "forced-atomics")] +compile_error!("the forced-atomics feature should not be exposed in `cfg`"); diff --git a/tests/ui/target-feature/forbidden-target-feature-flag-disable.rs b/tests/ui/target-feature/forbidden-target-feature-flag-disable.rs index b09c53bd46afa..cf85c5212289c 100644 --- a/tests/ui/target-feature/forbidden-target-feature-flag-disable.rs +++ b/tests/ui/target-feature/forbidden-target-feature-flag-disable.rs @@ -1,8 +1,10 @@ -//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib -//@ needs-llvm-components: x86 -//@ compile-flags: -Ctarget-feature=-soft-float +//! Ensure "forbidden" target features cannot be disabled via `-Ctarget-feature`. +//@ compile-flags: --target=riscv32e-unknown-none-elf --crate-type=lib +//@ needs-llvm-components: riscv +//@ compile-flags: -Ctarget-feature=-forced-atomics // For now this is just a warning. //@ build-pass +//@error-pattern: unsound because it changes the ABI #![feature(no_core, lang_items)] #![no_core] diff --git a/tests/ui/target-feature/forbidden-target-feature-flag-disable.stderr b/tests/ui/target-feature/forbidden-target-feature-flag-disable.stderr index 508e1fe0cf476..171ed0de6aaf3 100644 --- a/tests/ui/target-feature/forbidden-target-feature-flag-disable.stderr +++ b/tests/ui/target-feature/forbidden-target-feature-flag-disable.stderr @@ -1,4 +1,4 @@ -warning: target feature `soft-float` cannot be toggled with `-Ctarget-feature`: unsound because it changes float ABI +warning: target feature `forced-atomics` cannot be disabled with `-Ctarget-feature`: unsound because it changes the ABI of atomic operations | = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #116344 diff --git a/tests/ui/target-feature/forbidden-target-feature-flag.rs b/tests/ui/target-feature/forbidden-target-feature-flag.rs index 0f688fde7f434..245841eb0395c 100644 --- a/tests/ui/target-feature/forbidden-target-feature-flag.rs +++ b/tests/ui/target-feature/forbidden-target-feature-flag.rs @@ -1,8 +1,10 @@ -//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib -//@ needs-llvm-components: x86 -//@ compile-flags: -Ctarget-feature=+soft-float +//! Ensure "forbidden" target features cannot be enabled via `-Ctarget-feature`. +//@ compile-flags: --target=riscv32e-unknown-none-elf --crate-type=lib +//@ needs-llvm-components: riscv +//@ compile-flags: -Ctarget-feature=+forced-atomics // For now this is just a warning. //@ build-pass +//@error-pattern: unsound because it changes the ABI #![feature(no_core, lang_items)] #![no_core] diff --git a/tests/ui/target-feature/forbidden-target-feature-flag.stderr b/tests/ui/target-feature/forbidden-target-feature-flag.stderr index 508e1fe0cf476..f8490f066d1d7 100644 --- a/tests/ui/target-feature/forbidden-target-feature-flag.stderr +++ b/tests/ui/target-feature/forbidden-target-feature-flag.stderr @@ -1,4 +1,4 @@ -warning: target feature `soft-float` cannot be toggled with `-Ctarget-feature`: unsound because it changes float ABI +warning: target feature `forced-atomics` cannot be enabled with `-Ctarget-feature`: unsound because it changes the ABI of atomic operations | = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #116344 diff --git a/tests/ui/target-feature/implied-features.rs b/tests/ui/target-feature/implied-features.rs index 4fdd843e6c289..d0b5dd3c3fb99 100644 --- a/tests/ui/target-feature/implied-features.rs +++ b/tests/ui/target-feature/implied-features.rs @@ -1,6 +1,5 @@ //@ only-x86_64 //@ build-pass -#![feature(target_feature_11)] #![allow(dead_code)] #[target_feature(enable = "ssse3")] diff --git a/tests/ui/target-feature/invalid-attribute.rs b/tests/ui/target-feature/invalid-attribute.rs index 2f951c4a00a9e..9ef7a686d253d 100644 --- a/tests/ui/target-feature/invalid-attribute.rs +++ b/tests/ui/target-feature/invalid-attribute.rs @@ -28,13 +28,6 @@ extern "Rust" {} //~^ ERROR malformed `target_feature` attribute unsafe fn foo() {} -#[target_feature(enable = "sse2")] -//~^ ERROR `#[target_feature(..)]` can only be applied to `unsafe` functions -//~| NOTE see issue #69098 -//~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -fn bar() {} -//~^ NOTE not an `unsafe` function - #[target_feature(enable = "sse2")] //~^ ERROR attribute should be applied to a function mod another {} @@ -58,7 +51,7 @@ enum Bar {} #[target_feature(enable = "sse2")] //~^ ERROR attribute should be applied to a function union Qux { -//~^ NOTE not a function + //~^ NOTE not a function f1: u16, f2: u16, } @@ -97,15 +90,18 @@ impl Foo {} trait Quux { fn foo(); //~ NOTE `foo` from trait + //~^ NOTE: type in trait } impl Quux for Foo { #[target_feature(enable = "sse2")] - //~^ ERROR `#[target_feature(..)]` can only be applied to `unsafe` functions - //~| NOTE see issue #69098 - //~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + //~^ ERROR `#[target_feature(..)]` cannot be applied to safe trait method + //~| NOTE cannot be applied to safe trait method fn foo() {} //~^ NOTE not an `unsafe` function + //~| ERROR: incompatible type for trait + //~| NOTE: expected safe fn, found unsafe fn + //~| NOTE: expected signature `fn()` } fn main() { @@ -113,9 +109,8 @@ fn main() { //~^ ERROR attribute should be applied to a function unsafe { foo(); - bar(); } - //~^^^^ NOTE not a function + //~^^^ NOTE not a function #[target_feature(enable = "sse2")] //~^ ERROR attribute should be applied to a function diff --git a/tests/ui/target-feature/invalid-attribute.stderr b/tests/ui/target-feature/invalid-attribute.stderr index bf48911edec5f..dc8a53041640e 100644 --- a/tests/ui/target-feature/invalid-attribute.stderr +++ b/tests/ui/target-feature/invalid-attribute.stderr @@ -32,7 +32,7 @@ LL | extern "Rust" {} | ---------------- not a function definition error: attribute should be applied to a function definition - --> $DIR/invalid-attribute.rs:38:1 + --> $DIR/invalid-attribute.rs:31:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | mod another {} | -------------- not a function definition error: attribute should be applied to a function definition - --> $DIR/invalid-attribute.rs:43:1 + --> $DIR/invalid-attribute.rs:36:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -50,7 +50,7 @@ LL | const FOO: usize = 7; | --------------------- not a function definition error: attribute should be applied to a function definition - --> $DIR/invalid-attribute.rs:48:1 + --> $DIR/invalid-attribute.rs:41:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -59,7 +59,7 @@ LL | struct Foo; | ----------- not a function definition error: attribute should be applied to a function definition - --> $DIR/invalid-attribute.rs:53:1 + --> $DIR/invalid-attribute.rs:46:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -68,7 +68,7 @@ LL | enum Bar {} | ----------- not a function definition error: attribute should be applied to a function definition - --> $DIR/invalid-attribute.rs:58:1 + --> $DIR/invalid-attribute.rs:51:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -81,7 +81,7 @@ LL | | } | |_- not a function definition error: attribute should be applied to a function definition - --> $DIR/invalid-attribute.rs:66:1 + --> $DIR/invalid-attribute.rs:59:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -90,7 +90,7 @@ LL | type Uwu = (); | -------------- not a function definition error: attribute should be applied to a function definition - --> $DIR/invalid-attribute.rs:71:1 + --> $DIR/invalid-attribute.rs:64:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -99,7 +99,7 @@ LL | trait Baz {} | ------------ not a function definition error: attribute should be applied to a function definition - --> $DIR/invalid-attribute.rs:81:1 + --> $DIR/invalid-attribute.rs:74:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -108,7 +108,7 @@ LL | static A: () = (); | ------------------ not a function definition error: attribute should be applied to a function definition - --> $DIR/invalid-attribute.rs:86:1 + --> $DIR/invalid-attribute.rs:79:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -117,7 +117,7 @@ LL | impl Quux for u8 {} | ------------------- not a function definition error: attribute should be applied to a function definition - --> $DIR/invalid-attribute.rs:93:1 + --> $DIR/invalid-attribute.rs:86:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -126,19 +126,18 @@ LL | impl Foo {} | ----------- not a function definition error: attribute should be applied to a function definition - --> $DIR/invalid-attribute.rs:112:5 + --> $DIR/invalid-attribute.rs:108:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | / unsafe { LL | | foo(); -LL | | bar(); LL | | } | |_____- not a function definition error: attribute should be applied to a function definition - --> $DIR/invalid-attribute.rs:120:5 + --> $DIR/invalid-attribute.rs:115:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -164,27 +163,14 @@ error: malformed `target_feature` attribute input LL | #[target_feature(disable = "baz")] | ^^^^^^^^^^^^^^^ help: must be of the form: `enable = ".."` -error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions - --> $DIR/invalid-attribute.rs:31:1 - | -LL | #[target_feature(enable = "sse2")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | fn bar() {} - | -------- not an `unsafe` function - | - = note: see issue #69098 for more information - = help: add `#![feature(target_feature_11)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - error: cannot use `#[inline(always)]` with `#[target_feature]` - --> $DIR/invalid-attribute.rs:76:1 + --> $DIR/invalid-attribute.rs:69:1 | LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ error[E0046]: not all trait items implemented, missing: `foo` - --> $DIR/invalid-attribute.rs:88:1 + --> $DIR/invalid-attribute.rs:81:1 | LL | impl Quux for u8 {} | ^^^^^^^^^^^^^^^^ missing `foo` in implementation @@ -192,20 +178,30 @@ LL | impl Quux for u8 {} LL | fn foo(); | --------- `foo` from trait -error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions - --> $DIR/invalid-attribute.rs:103:5 +error: `#[target_feature(..)]` cannot be applied to safe trait method + --> $DIR/invalid-attribute.rs:97:5 | LL | #[target_feature(enable = "sse2")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be applied to safe trait method ... LL | fn foo() {} | -------- not an `unsafe` function + +error[E0053]: method `foo` has an incompatible type for trait + --> $DIR/invalid-attribute.rs:100:5 | - = note: see issue #69098 for more information - = help: add `#![feature(target_feature_11)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +LL | fn foo() {} + | ^^^^^^^^ expected safe fn, found unsafe fn + | +note: type in trait + --> $DIR/invalid-attribute.rs:92:5 + | +LL | fn foo(); + | ^^^^^^^^^ + = note: expected signature `fn()` + found signature `#[target_features] fn()` error: aborting due to 23 previous errors -Some errors have detailed explanations: E0046, E0658. +Some errors have detailed explanations: E0046, E0053. For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/target-feature/no-llvm-leaks.rs b/tests/ui/target-feature/no-llvm-leaks.rs index 102686864050a..d4f70fe706940 100644 --- a/tests/ui/target-feature/no-llvm-leaks.rs +++ b/tests/ui/target-feature/no-llvm-leaks.rs @@ -19,10 +19,7 @@ impl Copy for bool {} #[stable(feature = "test", since = "1.0.0")] #[rustc_const_stable(feature = "test", since = "1.0.0")] #[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -const unsafe fn unreachable() -> ! { - loop {} -} +const unsafe fn unreachable() -> !; #[rustc_builtin_macro] macro_rules! cfg { diff --git a/tests/ui/target-feature/target-cpu-lacks-required-target-feature.rs b/tests/ui/target-feature/target-cpu-lacks-required-target-feature.rs new file mode 100644 index 0000000000000..28d026c1a9a2d --- /dev/null +++ b/tests/ui/target-feature/target-cpu-lacks-required-target-feature.rs @@ -0,0 +1,12 @@ +//@ compile-flags: --target=i686-unknown-linux-gnu --crate-type=lib +//@ needs-llvm-components: x86 +//@ compile-flags: -Ctarget-cpu=pentium +// For now this is just a warning. +//@ build-pass +//@error-pattern: must be enabled + +#![feature(no_core, lang_items)] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} diff --git a/tests/ui/target-feature/target-cpu-lacks-required-target-feature.stderr b/tests/ui/target-feature/target-cpu-lacks-required-target-feature.stderr new file mode 100644 index 0000000000000..7ec8b04cfce01 --- /dev/null +++ b/tests/ui/target-feature/target-cpu-lacks-required-target-feature.stderr @@ -0,0 +1,7 @@ +warning: target feature `sse2` must be enabled to ensure that the ABI of the current target can be implemented correctly + | + = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116344 + +warning: 1 warning emitted + diff --git a/tests/ui/target_modifiers/auxiliary/default_reg_struct_return.rs b/tests/ui/target_modifiers/auxiliary/default_reg_struct_return.rs new file mode 100644 index 0000000000000..355e7c56e9462 --- /dev/null +++ b/tests/ui/target_modifiers/auxiliary/default_reg_struct_return.rs @@ -0,0 +1,20 @@ +//@ compile-flags: --target i686-unknown-linux-gnu -Cpanic=abort +// Auxiliary build problems with aarch64-apple: +// Shared library linking cc seems to convert "-m32" flag into -arch armv4t +// Auxiliary build problems with i686-mingw: linker `cc` not found +//@ only-x86 +//@ ignore-windows +//@ ignore-apple +//@ needs-llvm-components: x86 +#![crate_type = "rlib"] +#![no_core] +#![feature(no_core, lang_items, repr_simd)] + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +pub fn somefun() {} + +pub struct S; diff --git a/tests/ui/target_modifiers/auxiliary/wrong_regparm.rs b/tests/ui/target_modifiers/auxiliary/wrong_regparm.rs new file mode 100644 index 0000000000000..2e16f1ee747c8 --- /dev/null +++ b/tests/ui/target_modifiers/auxiliary/wrong_regparm.rs @@ -0,0 +1,20 @@ +//@ compile-flags: --target i686-unknown-linux-gnu -Zregparm=2 -Cpanic=abort +// Auxiliary build problems with aarch64-apple: +// Shared library linking cc seems to convert "-m32" flag into -arch armv4t +// Auxiliary build problems with i686-mingw: linker `cc` not found +//@ only-x86 +//@ ignore-windows +//@ ignore-apple +//@ needs-llvm-components: x86 +#![crate_type = "rlib"] +#![no_core] +#![feature(no_core, lang_items, repr_simd)] + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +pub fn somefun() {} + +pub struct S; diff --git a/tests/ui/target_modifiers/auxiliary/wrong_regparm_and_ret.rs b/tests/ui/target_modifiers/auxiliary/wrong_regparm_and_ret.rs new file mode 100644 index 0000000000000..39c6be9d5892e --- /dev/null +++ b/tests/ui/target_modifiers/auxiliary/wrong_regparm_and_ret.rs @@ -0,0 +1,20 @@ +//@ compile-flags: --target i686-unknown-linux-gnu -Zregparm=2 -Zreg-struct-return=true -Cpanic=abort +// Auxiliary build problems with aarch64-apple: +// Shared library linking cc seems to convert "-m32" flag into -arch armv4t +// Auxiliary build problems with i686-mingw: linker `cc` not found +//@ only-x86 +//@ ignore-windows +//@ ignore-apple +//@ needs-llvm-components: x86 +#![crate_type = "rlib"] +#![no_core] +#![feature(no_core, lang_items, repr_simd)] + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +pub fn somefun() {} + +pub struct S; diff --git a/tests/ui/target_modifiers/defaults_check.error.stderr b/tests/ui/target_modifiers/defaults_check.error.stderr new file mode 100644 index 0000000000000..c545dd710690a --- /dev/null +++ b/tests/ui/target_modifiers/defaults_check.error.stderr @@ -0,0 +1,13 @@ +error: mixing `-Zreg-struct-return` will cause an ABI mismatch in crate `defaults_check` + --> $DIR/defaults_check.rs:20:1 + | +LL | #![crate_type = "rlib"] + | ^ + | + = help: the `-Zreg-struct-return` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + = note: `-Zreg-struct-return=true` in this crate is incompatible with `-Zreg-struct-return=` in dependency `default_reg_struct_return` + = help: set `-Zreg-struct-return=` in this crate or `-Zreg-struct-return=true` in `default_reg_struct_return` + = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=reg-struct-return` to silence this error + +error: aborting due to 1 previous error + diff --git a/tests/ui/target_modifiers/defaults_check.rs b/tests/ui/target_modifiers/defaults_check.rs new file mode 100644 index 0000000000000..b8f4848d3a422 --- /dev/null +++ b/tests/ui/target_modifiers/defaults_check.rs @@ -0,0 +1,27 @@ +// Tests that default unspecified target modifier value in dependency crate is ok linked +// with the same value, explicitly specified +//@ aux-crate:default_reg_struct_return=default_reg_struct_return.rs +//@ compile-flags: --target i686-unknown-linux-gnu -Cpanic=abort +//@ revisions:error ok ok_explicit +//@[ok] compile-flags: +//@[ok_explicit] compile-flags: -Zreg-struct-return=false +//@[error] compile-flags: -Zreg-struct-return=true + +// Auxiliary build problems with aarch64-apple: +// Shared library linking cc seems to convert "-m32" flag into -arch armv4t +// Auxiliary build problems with i686-mingw: linker `cc` not found +//@ only-x86 +//@ ignore-windows +//@ ignore-apple +//@ needs-llvm-components: x86 +//@[ok] build-pass +//@[ok_explicit] build-pass + +#![crate_type = "rlib"] +//[error]~^ ERROR mixing `-Zreg-struct-return` will cause an ABI mismatch in crate `defaults_check` +#![no_core] +#![feature(no_core, lang_items, repr_simd)] + +fn foo() { + default_reg_struct_return::somefun(); +} diff --git a/tests/ui/target_modifiers/incompatible_regparm.allow_no_value.stderr b/tests/ui/target_modifiers/incompatible_regparm.allow_no_value.stderr new file mode 100644 index 0000000000000..8597332825b56 --- /dev/null +++ b/tests/ui/target_modifiers/incompatible_regparm.allow_no_value.stderr @@ -0,0 +1,2 @@ +error: codegen option `unsafe-allow-abi-mismatch` requires a comma-separated list of strings (C unsafe-allow-abi-mismatch=) + diff --git a/tests/ui/target_modifiers/incompatible_regparm.error_generated.stderr b/tests/ui/target_modifiers/incompatible_regparm.error_generated.stderr new file mode 100644 index 0000000000000..692fc7a4e3fa7 --- /dev/null +++ b/tests/ui/target_modifiers/incompatible_regparm.error_generated.stderr @@ -0,0 +1,13 @@ +error: mixing `-Zregparm` will cause an ABI mismatch in crate `incompatible_regparm` + --> $DIR/incompatible_regparm.rs:16:1 + | +LL | #![crate_type = "rlib"] + | ^ + | + = help: the `-Zregparm` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + = note: `-Zregparm=1` in this crate is incompatible with `-Zregparm=2` in dependency `wrong_regparm` + = help: set `-Zregparm=2` in this crate or `-Zregparm=1` in `wrong_regparm` + = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=regparm` to silence this error + +error: aborting due to 1 previous error + diff --git a/tests/ui/target_modifiers/incompatible_regparm.rs b/tests/ui/target_modifiers/incompatible_regparm.rs new file mode 100644 index 0000000000000..e866c5aa891b0 --- /dev/null +++ b/tests/ui/target_modifiers/incompatible_regparm.rs @@ -0,0 +1,23 @@ +//@ aux-crate:wrong_regparm=wrong_regparm.rs +//@ compile-flags: --target i686-unknown-linux-gnu -Zregparm=1 -Cpanic=abort +// Auxiliary build problems with aarch64-apple: +// Shared library linking cc seems to convert "-m32" flag into -arch armv4t +// Auxiliary build problems with i686-mingw: linker `cc` not found +//@ only-x86 +//@ ignore-windows +//@ ignore-apple +//@ needs-llvm-components: x86 +//@ revisions:error_generated allow_regparm_mismatch allow_no_value + +//@[allow_regparm_mismatch] compile-flags: -Cunsafe-allow-abi-mismatch=regparm +//@[allow_regparm_mismatch] build-pass +//@[allow_no_value] compile-flags: -Cunsafe-allow-abi-mismatch + +#![crate_type = "rlib"] +//[error_generated]~^ ERROR mixing `-Zregparm` will cause an ABI mismatch in crate `incompatible_regparm` +#![no_core] +#![feature(no_core, lang_items, repr_simd)] + +fn foo() { + wrong_regparm::somefun(); +} diff --git a/tests/ui/target_modifiers/two_flags.rs b/tests/ui/target_modifiers/two_flags.rs new file mode 100644 index 0000000000000..ca17117a267b1 --- /dev/null +++ b/tests/ui/target_modifiers/two_flags.rs @@ -0,0 +1,23 @@ +//@ aux-crate:wrong_regparm_and_ret=wrong_regparm_and_ret.rs +//@ compile-flags: --target i686-unknown-linux-gnu -Cpanic=abort +// Auxiliary build problems with aarch64-apple: +// Shared library linking cc seems to convert "-m32" flag into -arch armv4t +// Auxiliary build problems with i686-mingw: linker `cc` not found +//@ only-x86 +//@ ignore-windows +//@ ignore-apple +//@ needs-llvm-components: x86 +//@ revisions:two_allowed unknown_allowed + +//@[two_allowed] compile-flags: -Cunsafe-allow-abi-mismatch=regparm,reg-struct-return +//@[two_allowed] build-pass +//@[unknown_allowed] compile-flags: -Cunsafe-allow-abi-mismatch=unknown_flag -Zregparm=2 -Zreg-struct-return=true + +#![crate_type = "rlib"] +//[unknown_allowed]~^ ERROR unknown target modifier `unknown_flag`, requested by `-Cunsafe-allow-abi-mismatch=unknown_flag` +#![no_core] +#![feature(no_core, lang_items, repr_simd)] + +fn foo() { + wrong_regparm_and_ret::somefun(); +} diff --git a/tests/ui/target_modifiers/two_flags.unknown_allowed.stderr b/tests/ui/target_modifiers/two_flags.unknown_allowed.stderr new file mode 100644 index 0000000000000..c8040c6e389b5 --- /dev/null +++ b/tests/ui/target_modifiers/two_flags.unknown_allowed.stderr @@ -0,0 +1,8 @@ +error: unknown target modifier `unknown_flag`, requested by `-Cunsafe-allow-abi-mismatch=unknown_flag` + --> $DIR/two_flags.rs:16:1 + | +LL | #![crate_type = "rlib"] + | ^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/test-attrs/inaccessible-test-modules.stderr b/tests/ui/test-attrs/inaccessible-test-modules.stderr index 7635f579d66b9..dfb6985730af5 100644 --- a/tests/ui/test-attrs/inaccessible-test-modules.stderr +++ b/tests/ui/test-attrs/inaccessible-test-modules.stderr @@ -13,7 +13,7 @@ LL | use test as y; help: consider importing this module instead | LL | use test::test as y; - | ~~~~~~~~~~~~~~~ + | ++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/test-attrs/issue-109816.stderr b/tests/ui/test-attrs/issue-109816.stderr index 6f5e3ae6b63f6..433421fff1b57 100644 --- a/tests/ui/test-attrs/issue-109816.stderr +++ b/tests/ui/test-attrs/issue-109816.stderr @@ -9,7 +9,8 @@ LL | struct A5(u32, u8); | help: replace with conditional compilation to make the item only exist when tests are being run | -LL | #[cfg(test)] +LL - #[test] +LL + #[cfg(test)] | error: aborting due to 1 previous error diff --git a/tests/ui/test-attrs/terse.rs b/tests/ui/test-attrs/terse.rs index 6c3f29ed10f99..6e605f994f2a3 100644 --- a/tests/ui/test-attrs/terse.rs +++ b/tests/ui/test-attrs/terse.rs @@ -4,7 +4,7 @@ //@ check-run-results //@ exec-env:RUST_BACKTRACE=0 //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" -//@ ignore-emscripten no threads support +//@ needs-threads //@ needs-unwind #[test] diff --git a/tests/ui/test-attrs/test-attr-non-associated-functions.stderr b/tests/ui/test-attrs/test-attr-non-associated-functions.stderr index 3e3a951aff3e9..0ede0cbb97f33 100644 --- a/tests/ui/test-attrs/test-attr-non-associated-functions.stderr +++ b/tests/ui/test-attrs/test-attr-non-associated-functions.stderr @@ -6,7 +6,8 @@ LL | #[test] | help: replace with conditional compilation to make the item only exist when tests are being run | -LL | #[cfg(test)] +LL - #[test] +LL + #[cfg(test)] | error: the `#[test]` attribute may only be used on a non-associated function @@ -17,7 +18,8 @@ LL | #[test] | help: replace with conditional compilation to make the item only exist when tests are being run | -LL | #[cfg(test)] +LL - #[test] +LL + #[cfg(test)] | error: aborting due to 2 previous errors diff --git a/tests/ui/test-attrs/test-on-not-fn.stderr b/tests/ui/test-attrs/test-on-not-fn.stderr index 7a9913fbcfa11..a282db012540a 100644 --- a/tests/ui/test-attrs/test-on-not-fn.stderr +++ b/tests/ui/test-attrs/test-on-not-fn.stderr @@ -8,8 +8,9 @@ LL | mod test {} | help: replace with conditional compilation to make the item only exist when tests are being run | -LL | #[cfg(test)] - | ~~~~~~~~~~~~ +LL - #[test] +LL + #[cfg(test)] + | error: the `#[test]` attribute may only be used on a non-associated function --> $DIR/test-on-not-fn.rs:6:1 @@ -27,8 +28,9 @@ LL | | } | help: replace with conditional compilation to make the item only exist when tests are being run | -LL | #[cfg(test)] - | ~~~~~~~~~~~~ +LL - #[test] +LL + #[cfg(test)] + | error: the `#[test]` attribute may only be used on a non-associated function --> $DIR/test-on-not-fn.rs:20:1 @@ -40,8 +42,9 @@ LL | extern "C" {} | help: replace with conditional compilation to make the item only exist when tests are being run | -LL | #[cfg(test)] - | ~~~~~~~~~~~~ +LL - #[test] +LL + #[cfg(test)] + | error: the `#[test]` attribute may only be used on a non-associated function --> $DIR/test-on-not-fn.rs:23:1 @@ -53,8 +56,9 @@ LL | trait Foo {} | help: replace with conditional compilation to make the item only exist when tests are being run | -LL | #[cfg(test)] - | ~~~~~~~~~~~~ +LL - #[test] +LL + #[cfg(test)] + | error: the `#[test]` attribute may only be used on a non-associated function --> $DIR/test-on-not-fn.rs:26:1 @@ -66,8 +70,9 @@ LL | impl Foo for i32 {} | help: replace with conditional compilation to make the item only exist when tests are being run | -LL | #[cfg(test)] - | ~~~~~~~~~~~~ +LL - #[test] +LL + #[cfg(test)] + | error: the `#[test]` attribute may only be used on a non-associated function --> $DIR/test-on-not-fn.rs:29:1 @@ -79,8 +84,9 @@ LL | const FOO: i32 = -1_i32; | help: replace with conditional compilation to make the item only exist when tests are being run | -LL | #[cfg(test)] - | ~~~~~~~~~~~~ +LL - #[test] +LL + #[cfg(test)] + | error: the `#[test]` attribute may only be used on a non-associated function --> $DIR/test-on-not-fn.rs:32:1 @@ -92,8 +98,9 @@ LL | static BAR: u64 = 10_000_u64; | help: replace with conditional compilation to make the item only exist when tests are being run | -LL | #[cfg(test)] - | ~~~~~~~~~~~~ +LL - #[test] +LL + #[cfg(test)] + | error: the `#[test]` attribute may only be used on a non-associated function --> $DIR/test-on-not-fn.rs:35:1 @@ -107,8 +114,9 @@ LL | | } | help: replace with conditional compilation to make the item only exist when tests are being run | -LL | #[cfg(test)] - | ~~~~~~~~~~~~ +LL - #[test] +LL + #[cfg(test)] + | error: the `#[test]` attribute may only be used on a non-associated function --> $DIR/test-on-not-fn.rs:40:1 @@ -120,8 +128,9 @@ LL | struct NewI32(i32); | help: replace with conditional compilation to make the item only exist when tests are being run | -LL | #[cfg(test)] - | ~~~~~~~~~~~~ +LL - #[test] +LL + #[cfg(test)] + | error: the `#[test]` attribute may only be used on a non-associated function --> $DIR/test-on-not-fn.rs:43:1 @@ -136,8 +145,9 @@ LL | | } | help: replace with conditional compilation to make the item only exist when tests are being run | -LL | #[cfg(test)] - | ~~~~~~~~~~~~ +LL - #[test] +LL + #[cfg(test)] + | error: the `#[test]` attribute may only be used on a non-associated function --> $DIR/test-on-not-fn.rs:50:1 @@ -153,8 +163,9 @@ LL | | } | help: replace with conditional compilation to make the item only exist when tests are being run | -LL | #[cfg(test)] - | ~~~~~~~~~~~~ +LL - #[test] +LL + #[cfg(test)] + | warning: the `#[test]` attribute may only be used on a non-associated function --> $DIR/test-on-not-fn.rs:61:1 @@ -166,8 +177,9 @@ LL | foo!(); | help: replace with conditional compilation to make the item only exist when tests are being run | -LL | #[cfg(test)] - | ~~~~~~~~~~~~ +LL - #[test] +LL + #[cfg(test)] + | error: aborting due to 11 previous errors; 1 warning emitted diff --git a/tests/ui/test-attrs/test-panic-abort-disabled.rs b/tests/ui/test-attrs/test-panic-abort-disabled.rs index fbe3d7d5d18d7..e83be65f9250c 100644 --- a/tests/ui/test-attrs/test-panic-abort-disabled.rs +++ b/tests/ui/test-attrs/test-panic-abort-disabled.rs @@ -4,8 +4,7 @@ //@ run-flags: --test-threads=1 //@ needs-unwind -//@ ignore-wasm no panic or subprocess support -//@ ignore-emscripten no panic or subprocess support +//@ needs-subprocess #![cfg(test)] diff --git a/tests/ui/test-attrs/test-panic-abort-nocapture.rs b/tests/ui/test-attrs/test-panic-abort-nocapture.rs index 4377ae1ac3bd7..6a1025ea087c0 100644 --- a/tests/ui/test-attrs/test-panic-abort-nocapture.rs +++ b/tests/ui/test-attrs/test-panic-abort-nocapture.rs @@ -7,9 +7,7 @@ //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" //@ ignore-android #120567 -//@ ignore-wasm no panic or subprocess support -//@ ignore-emscripten no panic or subprocess support -//@ ignore-sgx no subprocess support +//@ needs-subprocess #![cfg(test)] diff --git a/tests/ui/test-attrs/test-panic-abort-nocapture.run.stderr b/tests/ui/test-attrs/test-panic-abort-nocapture.run.stderr index 256811711703b..b9f267838b191 100644 --- a/tests/ui/test-attrs/test-panic-abort-nocapture.run.stderr +++ b/tests/ui/test-attrs/test-panic-abort-nocapture.run.stderr @@ -1,11 +1,11 @@ -thread 'main' panicked at $DIR/test-panic-abort-nocapture.rs:34:5: +thread 'main' panicked at $DIR/test-panic-abort-nocapture.rs:32:5: assertion `left == right` failed left: 2 right: 4 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace -thread 'main' panicked at $DIR/test-panic-abort-nocapture.rs:28:5: +thread 'main' panicked at $DIR/test-panic-abort-nocapture.rs:26:5: assertion `left == right` failed left: 2 right: 4 diff --git a/tests/ui/test-attrs/test-panic-abort.rs b/tests/ui/test-attrs/test-panic-abort.rs index 3d203e059a46a..6c9b641fb6fba 100644 --- a/tests/ui/test-attrs/test-panic-abort.rs +++ b/tests/ui/test-attrs/test-panic-abort.rs @@ -7,9 +7,7 @@ //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" //@ ignore-android #120567 -//@ ignore-wasm no panic or subprocess support -//@ ignore-emscripten no panic or subprocess support -//@ ignore-sgx no subprocess support +//@ needs-subprocess #![cfg(test)] #![feature(test)] diff --git a/tests/ui/test-attrs/test-panic-abort.run.stdout b/tests/ui/test-attrs/test-panic-abort.run.stdout index 844808e86370d..0faa7f0dfcec4 100644 --- a/tests/ui/test-attrs/test-panic-abort.run.stdout +++ b/tests/ui/test-attrs/test-panic-abort.run.stdout @@ -18,7 +18,7 @@ testing123 ---- it_fails stderr ---- testing321 -thread 'main' panicked at $DIR/test-panic-abort.rs:39:5: +thread 'main' panicked at $DIR/test-panic-abort.rs:37:5: assertion `left == right` failed left: 2 right: 5 diff --git a/tests/ui/test-attrs/test-runner-hides-start.rs b/tests/ui/test-attrs/test-runner-hides-start.rs deleted file mode 100644 index 444ac237cfa3a..0000000000000 --- a/tests/ui/test-attrs/test-runner-hides-start.rs +++ /dev/null @@ -1,7 +0,0 @@ -//@ run-pass -//@ compile-flags: --test - -#![feature(start)] - -#[start] -fn start(_: isize, _: *const *const u8) -> isize { panic!(); } diff --git a/tests/ui/test-attrs/test-thread-capture.rs b/tests/ui/test-attrs/test-thread-capture.rs index c56f87f2ddac4..0a5b1e9816f2b 100644 --- a/tests/ui/test-attrs/test-thread-capture.rs +++ b/tests/ui/test-attrs/test-thread-capture.rs @@ -4,7 +4,7 @@ //@ check-run-results //@ exec-env:RUST_BACKTRACE=0 //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" -//@ ignore-emscripten no threads support +//@ needs-threads //@ needs-unwind #[test] diff --git a/tests/ui/test-attrs/test-thread-nocapture.rs b/tests/ui/test-attrs/test-thread-nocapture.rs index 5b82e9b272076..ce5db7bf1c374 100644 --- a/tests/ui/test-attrs/test-thread-nocapture.rs +++ b/tests/ui/test-attrs/test-thread-nocapture.rs @@ -4,7 +4,7 @@ //@ check-run-results //@ exec-env:RUST_BACKTRACE=0 //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" -//@ ignore-emscripten no threads support +//@ needs-threads //@ needs-unwind #[test] diff --git a/tests/ui/thir-print/thir-tree-match.stdout b/tests/ui/thir-print/thir-tree-match.stdout index 916f296ccfc6b..910582ae4d9e9 100644 --- a/tests/ui/thir-print/thir-tree-match.stdout +++ b/tests/ui/thir-print/thir-tree-match.stdout @@ -26,16 +26,16 @@ params: [ body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:15:32: 21:2 (#0) kind: Scope { - region_scope: Node(26) - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).26)) + region_scope: Node(28) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).28)) value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:15:32: 21:2 (#0) kind: Block { @@ -47,7 +47,7 @@ body: expr: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:16:5: 20:6 (#0) kind: Scope { @@ -56,14 +56,14 @@ body: value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:16:5: 20:6 (#0) kind: Match { scrutinee: Expr { ty: Foo - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:16:11: 16:14 (#0) kind: Scope { @@ -72,7 +72,7 @@ body: value: Expr { ty: Foo - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:16:11: 16:14 (#0) kind: VarRef { @@ -123,16 +123,16 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(13)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(14)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) kind: Scope { - region_scope: Node(14) - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).14)) + region_scope: Node(15) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).15)) value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(13)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(14)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) kind: Literal( lit: Spanned { node: Bool(true), span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) }, neg: false) @@ -140,8 +140,8 @@ body: } } } - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).13)) - scope: Node(13) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).14)) + scope: Node(14) span: $DIR/thir-tree-match.rs:17:9: 17:40 (#0) } Arm { @@ -175,16 +175,16 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(19)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(20)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) kind: Scope { - region_scope: Node(20) - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).20)) + region_scope: Node(21) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).21)) value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(19)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(20)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) kind: Literal( lit: Spanned { node: Bool(false), span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) }, neg: false) @@ -192,8 +192,8 @@ body: } } } - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).19)) - scope: Node(19) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).20)) + scope: Node(20) span: $DIR/thir-tree-match.rs:18:9: 18:32 (#0) } Arm { @@ -219,16 +219,16 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) kind: Scope { - region_scope: Node(25) - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).25)) + region_scope: Node(27) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).27)) value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) kind: Literal( lit: Spanned { node: Bool(true), span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) }, neg: false) @@ -236,8 +236,8 @@ body: } } } - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).24)) - scope: Node(24) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).26)) + scope: Node(26) span: $DIR/thir-tree-match.rs:19:9: 19:28 (#0) } ] diff --git a/tests/ui/threads-sendsync/eprint-on-tls-drop.rs b/tests/ui/threads-sendsync/eprint-on-tls-drop.rs index 82abf21df3f41..e85c7c83339b4 100644 --- a/tests/ui/threads-sendsync/eprint-on-tls-drop.rs +++ b/tests/ui/threads-sendsync/eprint-on-tls-drop.rs @@ -1,6 +1,6 @@ //@ run-pass //@ needs-threads -//@ ignore-sgx no processes +//@ needs-subprocess use std::cell::RefCell; use std::env; diff --git a/tests/ui/threads-sendsync/issue-24313.rs b/tests/ui/threads-sendsync/issue-24313.rs index 99c6c4a5e12de..83ab5122e82b9 100644 --- a/tests/ui/threads-sendsync/issue-24313.rs +++ b/tests/ui/threads-sendsync/issue-24313.rs @@ -1,6 +1,6 @@ //@ run-pass //@ needs-threads -//@ ignore-sgx no processes +//@ needs-subprocess use std::process::Command; use std::{env, thread}; diff --git a/tests/ui/threads-sendsync/spawning-with-debug.rs b/tests/ui/threads-sendsync/spawning-with-debug.rs index 90a81c1e53bcb..3b7107e2dbfa6 100644 --- a/tests/ui/threads-sendsync/spawning-with-debug.rs +++ b/tests/ui/threads-sendsync/spawning-with-debug.rs @@ -1,7 +1,6 @@ //@ run-pass #![allow(unused_must_use)] #![allow(unused_mut)] -//@ ignore-windows //@ exec-env:RUST_LOG=debug //@ needs-threads diff --git a/tests/ui/threads-sendsync/thread-local-extern-static.rs b/tests/ui/threads-sendsync/thread-local-extern-static.rs index ca66f8ad1d36a..ce2a221ec2e41 100644 --- a/tests/ui/threads-sendsync/thread-local-extern-static.rs +++ b/tests/ui/threads-sendsync/thread-local-extern-static.rs @@ -1,5 +1,5 @@ //@ run-pass -//@ ignore-windows +//@ ignore-windows FIXME(134939): thread_local + no_mangle doesn't work on Windows //@ aux-build:thread-local-extern-static.rs #![feature(cfg_target_thread_local, thread_local)] diff --git a/tests/ui/tool-attributes/unknown-tool-name.rs b/tests/ui/tool-attributes/unknown-tool-name.rs index 73fca61c65d1b..ba21aecc230a8 100644 --- a/tests/ui/tool-attributes/unknown-tool-name.rs +++ b/tests/ui/tool-attributes/unknown-tool-name.rs @@ -1,2 +1,2 @@ -#[foo::bar] //~ ERROR failed to resolve: use of undeclared crate or module `foo` +#[foo::bar] //~ ERROR failed to resolve: use of unresolved module or unlinked crate `foo` fn main() {} diff --git a/tests/ui/tool-attributes/unknown-tool-name.stderr b/tests/ui/tool-attributes/unknown-tool-name.stderr index 361d359a10e47..9b636fcb0bdd7 100644 --- a/tests/ui/tool-attributes/unknown-tool-name.stderr +++ b/tests/ui/tool-attributes/unknown-tool-name.stderr @@ -1,8 +1,8 @@ -error[E0433]: failed to resolve: use of undeclared crate or module `foo` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `foo` --> $DIR/unknown-tool-name.rs:1:3 | LL | #[foo::bar] - | ^^^ use of undeclared crate or module `foo` + | ^^^ use of unresolved module or unlinked crate `foo` error: aborting due to 1 previous error diff --git a/tests/ui/trait-bounds/argument-with-unnecessary-method-call.stderr b/tests/ui/trait-bounds/argument-with-unnecessary-method-call.stderr index 49230c98a12c1..7d795581ea9d8 100644 --- a/tests/ui/trait-bounds/argument-with-unnecessary-method-call.stderr +++ b/tests/ui/trait-bounds/argument-with-unnecessary-method-call.stderr @@ -14,8 +14,9 @@ LL | fn qux(_: impl From) {} | ^^^^^^^^^ required by this bound in `qux` help: try using a fully qualified path to specify the expected types | -LL | qux(>::into(Bar)); - | +++++++++++++++++++++++ ~ +LL - qux(Bar.into()); +LL + qux(>::into(Bar)); + | help: consider removing this method call, as the receiver has type `Bar` and `Bar: From` trivially holds | LL - qux(Bar.into()); diff --git a/tests/ui/issues/issue-40827.rs b/tests/ui/trait-bounds/deep-level-Send-bound-check-issue-40827.rs similarity index 100% rename from tests/ui/issues/issue-40827.rs rename to tests/ui/trait-bounds/deep-level-Send-bound-check-issue-40827.rs diff --git a/tests/ui/trait-bounds/deep-level-Send-bound-check-issue-40827.stderr b/tests/ui/trait-bounds/deep-level-Send-bound-check-issue-40827.stderr new file mode 100644 index 0000000000000..7b59fe72f431e --- /dev/null +++ b/tests/ui/trait-bounds/deep-level-Send-bound-check-issue-40827.stderr @@ -0,0 +1,55 @@ +error[E0277]: `Rc` cannot be shared between threads safely + --> $DIR/deep-level-Send-bound-check-issue-40827.rs:14:7 + | +LL | f(Foo(Arc::new(Bar::B(None)))); + | - ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Rc` cannot be shared between threads safely + | | + | required by a bound introduced by this call + | + = help: within `Bar`, the trait `Sync` is not implemented for `Rc` +note: required because it appears within the type `Bar` + --> $DIR/deep-level-Send-bound-check-issue-40827.rs:6:6 + | +LL | enum Bar { + | ^^^ + = note: required for `Arc` to implement `Send` +note: required because it appears within the type `Foo` + --> $DIR/deep-level-Send-bound-check-issue-40827.rs:4:8 + | +LL | struct Foo(Arc); + | ^^^ +note: required by a bound in `f` + --> $DIR/deep-level-Send-bound-check-issue-40827.rs:11:9 + | +LL | fn f(_: T) {} + | ^^^^ required by this bound in `f` + +error[E0277]: `Rc` cannot be sent between threads safely + --> $DIR/deep-level-Send-bound-check-issue-40827.rs:14:7 + | +LL | f(Foo(Arc::new(Bar::B(None)))); + | - ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Rc` cannot be sent between threads safely + | | + | required by a bound introduced by this call + | + = help: within `Bar`, the trait `Send` is not implemented for `Rc` +note: required because it appears within the type `Bar` + --> $DIR/deep-level-Send-bound-check-issue-40827.rs:6:6 + | +LL | enum Bar { + | ^^^ + = note: required for `Arc` to implement `Send` +note: required because it appears within the type `Foo` + --> $DIR/deep-level-Send-bound-check-issue-40827.rs:4:8 + | +LL | struct Foo(Arc); + | ^^^ +note: required by a bound in `f` + --> $DIR/deep-level-Send-bound-check-issue-40827.rs:11:9 + | +LL | fn f(_: T) {} + | ^^^^ required by this bound in `f` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/trait-bounds/maybe-bound-has-path-args.rs b/tests/ui/trait-bounds/maybe-bound-has-path-args.rs index fd0e96917004d..e5abcae5d219b 100644 --- a/tests/ui/trait-bounds/maybe-bound-has-path-args.rs +++ b/tests/ui/trait-bounds/maybe-bound-has-path-args.rs @@ -2,6 +2,6 @@ trait Trait {} fn test::Trait>() {} //~^ ERROR type arguments are not allowed on module `maybe_bound_has_path_args` -//~| WARN relaxing a default bound only does something for `?Sized` +//~| ERROR relaxing a default bound only does something for `?Sized` fn main() {} diff --git a/tests/ui/trait-bounds/maybe-bound-has-path-args.stderr b/tests/ui/trait-bounds/maybe-bound-has-path-args.stderr index 0c167fff94012..dc55b26c9183c 100644 --- a/tests/ui/trait-bounds/maybe-bound-has-path-args.stderr +++ b/tests/ui/trait-bounds/maybe-bound-has-path-args.stderr @@ -1,4 +1,4 @@ -warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +error: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default --> $DIR/maybe-bound-has-path-args.rs:3:12 | LL | fn test::Trait>() {} @@ -12,6 +12,6 @@ LL | fn test::Trait>() {} | | | not allowed on module `maybe_bound_has_path_args` -error: aborting due to 1 previous error; 1 warning emitted +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0109`. diff --git a/tests/ui/trait-bounds/maybe-bound-with-assoc.rs b/tests/ui/trait-bounds/maybe-bound-with-assoc.rs index b6b2551e4638e..9127c2de16d5e 100644 --- a/tests/ui/trait-bounds/maybe-bound-with-assoc.rs +++ b/tests/ui/trait-bounds/maybe-bound-with-assoc.rs @@ -2,11 +2,11 @@ trait HasAssoc { type Assoc; } fn hasassoc>() {} -//~^ WARN relaxing a default bound +//~^ ERROR relaxing a default bound trait NoAssoc {} fn noassoc>() {} -//~^ WARN relaxing a default bound +//~^ ERROR relaxing a default bound //~| ERROR associated type `Missing` not found for `NoAssoc` fn main() {} diff --git a/tests/ui/trait-bounds/maybe-bound-with-assoc.stderr b/tests/ui/trait-bounds/maybe-bound-with-assoc.stderr index 91d78e59cd5a7..36a1e0ade2063 100644 --- a/tests/ui/trait-bounds/maybe-bound-with-assoc.stderr +++ b/tests/ui/trait-bounds/maybe-bound-with-assoc.stderr @@ -1,10 +1,10 @@ -warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +error: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default --> $DIR/maybe-bound-with-assoc.rs:4:16 | LL | fn hasassoc>() {} | ^^^^^^^^^^^^^^^^^^^^^ -warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +error: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default --> $DIR/maybe-bound-with-assoc.rs:8:15 | LL | fn noassoc>() {} @@ -16,6 +16,6 @@ error[E0220]: associated type `Missing` not found for `NoAssoc` LL | fn noassoc>() {} | ^^^^^^^ associated type `Missing` not found -error: aborting due to 1 previous error; 2 warnings emitted +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/traits/alias/ambiguous.stderr b/tests/ui/traits/alias/ambiguous.stderr index 034e8a3fb7b99..542ee1901296d 100644 --- a/tests/ui/traits/alias/ambiguous.stderr +++ b/tests/ui/traits/alias/ambiguous.stderr @@ -16,12 +16,14 @@ LL | fn foo(&self) {} | ^^^^^^^^^^^^^ help: disambiguate the method for candidate #1 | -LL | A::foo(&t); - | ~~~~~~~~~~ +LL - t.foo(); +LL + A::foo(&t); + | help: disambiguate the method for candidate #2 | -LL | B::foo(&t); - | ~~~~~~~~~~ +LL - t.foo(); +LL + B::foo(&t); + | error: aborting due to 1 previous error diff --git a/tests/ui/traits/alias/dont-elaborate-non-self.stderr b/tests/ui/traits/alias/dont-elaborate-non-self.stderr index 952f78dd3da13..1d96a6a69940c 100644 --- a/tests/ui/traits/alias/dont-elaborate-non-self.stderr +++ b/tests/ui/traits/alias/dont-elaborate-non-self.stderr @@ -8,8 +8,9 @@ LL | fn f(a: dyn F) {} = help: unsized fn params are gated as an unstable feature help: you can use `impl Trait` as the argument type | -LL | fn f(a: impl F) {} - | ~~~~ +LL - fn f(a: dyn F) {} +LL + fn f(a: impl F) {} + | help: function arguments must have a statically known size, borrowed types always have a known size | LL | fn f(a: &dyn F) {} diff --git a/tests/ui/traits/alias/expand-higher-ranked-alias.rs b/tests/ui/traits/alias/expand-higher-ranked-alias.rs new file mode 100644 index 0000000000000..8a301d39f4c46 --- /dev/null +++ b/tests/ui/traits/alias/expand-higher-ranked-alias.rs @@ -0,0 +1,18 @@ +// Make sure we are using the right binder vars when expanding +// `for<'a> Foo<'a>` to `for<'a> Bar<'a>`. + +//@ check-pass + +#![feature(trait_alias)] + +trait Bar<'a> {} + +trait Foo<'a> = Bar<'a>; + +fn test2(_: &(impl for<'a> Foo<'a> + ?Sized)) {} + +fn test(x: &dyn for<'a> Foo<'a>) { + test2(x); +} + +fn main() {} diff --git a/tests/ui/traits/alias/generic-default-in-dyn.rs b/tests/ui/traits/alias/generic-default-in-dyn.rs index d44e1c2a97530..b263e578c31d2 100644 --- a/tests/ui/traits/alias/generic-default-in-dyn.rs +++ b/tests/ui/traits/alias/generic-default-in-dyn.rs @@ -2,9 +2,9 @@ trait SendEqAlias = PartialEq; //~^ ERROR trait aliases are experimental struct Foo(dyn SendEqAlias); -//~^ ERROR the type parameter `Rhs` must be explicitly specified [E0393] +//~^ ERROR the trait alias `SendEqAlias` is not dyn compatible struct Bar(dyn SendEqAlias, T); -//~^ ERROR the type parameter `Rhs` must be explicitly specified [E0393] +//~^ ERROR the trait alias `SendEqAlias` is not dyn compatible fn main() {} diff --git a/tests/ui/traits/alias/generic-default-in-dyn.stderr b/tests/ui/traits/alias/generic-default-in-dyn.stderr index 50031e184c106..c6f8ab4137b20 100644 --- a/tests/ui/traits/alias/generic-default-in-dyn.stderr +++ b/tests/ui/traits/alias/generic-default-in-dyn.stderr @@ -8,29 +8,37 @@ LL | trait SendEqAlias = PartialEq; = help: add `#![feature(trait_alias)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0393]: the type parameter `Rhs` must be explicitly specified +error[E0038]: the trait alias `SendEqAlias` is not dyn compatible --> $DIR/generic-default-in-dyn.rs:4:19 | LL | struct Foo(dyn SendEqAlias); - | ^^^^^^^^^^^^^^ missing reference to `Rhs` - --> $SRC_DIR/core/src/cmp.rs:LL:COL + | ^^^^^^^^^^^^^^ `SendEqAlias` is not dyn compatible | - = note: type parameter `Rhs` must be specified for this +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/generic-default-in-dyn.rs:1:24 | - = note: because of the default `Self` reference, type parameters must be specified on object types +LL | trait SendEqAlias = PartialEq; + | ----------- ^^^^^^^^^ ...because it uses `Self` as a type parameter + | | + | this trait is not dyn compatible... -error[E0393]: the type parameter `Rhs` must be explicitly specified +error[E0038]: the trait alias `SendEqAlias` is not dyn compatible --> $DIR/generic-default-in-dyn.rs:7:19 | LL | struct Bar(dyn SendEqAlias, T); - | ^^^^^^^^^^^^^^ missing reference to `Rhs` - --> $SRC_DIR/core/src/cmp.rs:LL:COL + | ^^^^^^^^^^^^^^ `SendEqAlias` is not dyn compatible | - = note: type parameter `Rhs` must be specified for this +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/generic-default-in-dyn.rs:1:24 | - = note: because of the default `Self` reference, type parameters must be specified on object types +LL | trait SendEqAlias = PartialEq; + | ----------- ^^^^^^^^^ ...because it uses `Self` as a type parameter + | | + | this trait is not dyn compatible... error: aborting due to 3 previous errors -Some errors have detailed explanations: E0393, E0658. -For more information about an error, try `rustc --explain E0393`. +Some errors have detailed explanations: E0038, E0658. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/traits/alias/infinite_normalization.rs b/tests/ui/traits/alias/infinite_normalization.rs new file mode 100644 index 0000000000000..848afc1efb23a --- /dev/null +++ b/tests/ui/traits/alias/infinite_normalization.rs @@ -0,0 +1,11 @@ +//! This test used to get stuck in an infinite +//! recursion during normalization. +//! +//! issue: https://github.com/rust-lang/rust/issues/133901 + +#![feature(trait_alias)] +fn foo>() {} +trait Baz = Baz>; +//~^ ERROR: cycle detected when computing the implied predicates of `Baz` + +fn main() {} diff --git a/tests/ui/traits/alias/infinite_normalization.stderr b/tests/ui/traits/alias/infinite_normalization.stderr new file mode 100644 index 0000000000000..5fe423609e5ac --- /dev/null +++ b/tests/ui/traits/alias/infinite_normalization.stderr @@ -0,0 +1,18 @@ +error[E0391]: cycle detected when computing the implied predicates of `Baz` + --> $DIR/infinite_normalization.rs:8:16 + | +LL | trait Baz = Baz>; + | ^^^^^^^^^^^^^^ + | + = note: ...which immediately requires computing the implied predicates of `Baz` again + = note: trait aliases cannot be recursive +note: cycle used when computing normalized predicates of `foo` + --> $DIR/infinite_normalization.rs:7:1 + | +LL | fn foo>() {} + | ^^^^^^^^^^^^^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/traits/alias/no-duplicates.stderr b/tests/ui/traits/alias/no-duplicates.stderr index bf244b97e9b13..6a901a80554e9 100644 --- a/tests/ui/traits/alias/no-duplicates.stderr +++ b/tests/ui/traits/alias/no-duplicates.stderr @@ -4,32 +4,32 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec LL | trait _0 = Obj; | --- | | - | additional non-auto trait | first non-auto trait + | additional non-auto trait ... LL | type _T00 = dyn _0 + _0; - | -- ^^ trait alias used in trait object type (additional use) + | -- ^^ second non-auto trait comes from this alias | | - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Obj + Obj {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object - --> $DIR/no-duplicates.rs:19:22 + --> $DIR/no-duplicates.rs:19:17 | LL | trait _0 = Obj; | --- | | - | additional non-auto trait | first non-auto trait + | additional non-auto trait LL | trait _1 = _0; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias ... LL | type _T01 = dyn _1 + _0; - | -- ^^ trait alias used in trait object type (additional use) + | ^^ -- first non-auto trait comes from this alias | | - | trait alias used in trait object type (first use) + | second non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Obj + Obj {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit @@ -40,18 +40,18 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec LL | trait _0 = Obj; | --- | | - | additional non-auto trait | first non-auto trait + | additional non-auto trait LL | trait _1 = _0; | -- | | - | referenced here (additional use) - | referenced here (first use) + | first non-auto trait comes from this alias + | second non-auto trait comes from this alias ... LL | type _T02 = dyn _1 + _1; - | -- ^^ trait alias used in trait object type (additional use) + | -- ^^ second non-auto trait comes from this alias | | - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Obj + Obj {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit @@ -62,10 +62,10 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec LL | trait _0 = Obj; | --- additional non-auto trait LL | trait _1 = _0; - | -- referenced here (additional use) + | -- second non-auto trait comes from this alias ... LL | type _T03 = dyn Obj + _1; - | --- ^^ trait alias used in trait object type (additional use) + | --- ^^ second non-auto trait comes from this alias | | | first non-auto trait | @@ -73,17 +73,17 @@ LL | type _T03 = dyn Obj + _1; = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object - --> $DIR/no-duplicates.rs:28:22 + --> $DIR/no-duplicates.rs:28:17 | LL | trait _0 = Obj; - | --- first non-auto trait + | --- additional non-auto trait LL | trait _1 = _0; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias ... LL | type _T04 = dyn _1 + Obj; - | -- ^^^ additional non-auto trait + | ^^ --- first non-auto trait | | - | trait alias used in trait object type (first use) + | second non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Obj + Obj {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit @@ -92,23 +92,17 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec --> $DIR/no-duplicates.rs:37:17 | LL | trait _0 = Obj; - | --- - | | - | additional non-auto trait - | first non-auto trait -LL | trait _1 = _0; - | -- referenced here (additional use) + | --- additional non-auto trait ... LL | trait _2 = _0 + _1; - | -- -- referenced here (additional use) - | | - | referenced here (first use) + | -- second non-auto trait comes from this alias +LL | trait _3 = Obj; + | --- first non-auto trait ... LL | type _T10 = dyn _2 + _3; - | ^^ + | ^^ -- first non-auto trait comes from this alias | | - | trait alias used in trait object type (additional use) - | trait alias used in trait object type (first use) + | second non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Obj + Obj + Obj {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit @@ -120,14 +114,14 @@ LL | trait _0 = Obj; | --- additional non-auto trait ... LL | trait _2 = _0 + _1; - | -- referenced here (additional use) + | -- second non-auto trait comes from this alias LL | trait _3 = Obj; | --- first non-auto trait ... LL | type _T11 = dyn _3 + _2; - | -- ^^ trait alias used in trait object type (additional use) + | -- ^^ second non-auto trait comes from this alias | | - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Obj + Obj + Obj {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit @@ -139,10 +133,10 @@ LL | trait _0 = Obj; | --- additional non-auto trait ... LL | trait _2 = _0 + _1; - | -- referenced here (additional use) + | -- second non-auto trait comes from this alias ... LL | type _T12 = dyn Obj + _2; - | --- ^^ trait alias used in trait object type (additional use) + | --- ^^ second non-auto trait comes from this alias | | | first non-auto trait | @@ -153,42 +147,34 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec --> $DIR/no-duplicates.rs:46:17 | LL | trait _0 = Obj; - | --- - | | - | additional non-auto trait - | first non-auto trait -LL | trait _1 = _0; - | -- referenced here (additional use) + | --- additional non-auto trait ... LL | trait _2 = _0 + _1; - | -- -- referenced here (additional use) - | | - | referenced here (first use) + | -- second non-auto trait comes from this alias ... LL | type _T13 = dyn _2 + Obj; - | ^^ + | ^^ --- first non-auto trait | | - | trait alias used in trait object type (additional use) - | trait alias used in trait object type (first use) + | second non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Obj + Obj + Obj {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object - --> $DIR/no-duplicates.rs:49:22 + --> $DIR/no-duplicates.rs:49:17 | LL | trait _0 = Obj; - | --- first non-auto trait + | --- additional non-auto trait LL | trait _1 = _0; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias ... LL | trait _3 = Obj; - | --- additional non-auto trait + | --- first non-auto trait ... LL | type _T14 = dyn _1 + _3; - | -- ^^ trait alias used in trait object type (additional use) + | ^^ -- first non-auto trait comes from this alias | | - | trait alias used in trait object type (first use) + | second non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Obj + Obj {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit @@ -199,15 +185,15 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec LL | trait _0 = Obj; | --- additional non-auto trait LL | trait _1 = _0; - | -- referenced here (additional use) + | -- second non-auto trait comes from this alias ... LL | trait _3 = Obj; | --- first non-auto trait ... LL | type _T15 = dyn _3 + _1; - | -- ^^ trait alias used in trait object type (additional use) + | -- ^^ second non-auto trait comes from this alias | | - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Obj + Obj {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit @@ -218,17 +204,17 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec LL | trait _0 = Obj; | --- first non-auto trait LL | trait _1 = _0; - | -- referenced here (first use) + | -- first non-auto trait comes from this alias ... LL | trait _3 = Obj; | --- additional non-auto trait LL | trait _4 = _3; - | -- referenced here (additional use) + | -- second non-auto trait comes from this alias ... LL | type _T16 = dyn _1 + _4; - | -- ^^ trait alias used in trait object type (additional use) + | -- ^^ second non-auto trait comes from this alias | | - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Obj + Obj {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit @@ -239,17 +225,17 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec LL | trait _0 = Obj; | --- additional non-auto trait LL | trait _1 = _0; - | -- referenced here (additional use) + | -- second non-auto trait comes from this alias ... LL | trait _3 = Obj; | --- first non-auto trait LL | trait _4 = _3; - | -- referenced here (first use) + | -- first non-auto trait comes from this alias ... LL | type _T17 = dyn _4 + _1; - | -- ^^ trait alias used in trait object type (additional use) + | -- ^^ second non-auto trait comes from this alias | | - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Obj + Obj {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit @@ -260,13 +246,13 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec LL | trait _5 = Obj + Send; | --- | | - | additional non-auto trait | first non-auto trait + | additional non-auto trait LL | LL | type _T20 = dyn _5 + _5; - | -- ^^ trait alias used in trait object type (additional use) + | -- ^^ second non-auto trait comes from this alias | | - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Obj + Obj {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit @@ -278,7 +264,7 @@ LL | trait _5 = Obj + Send; | --- additional non-auto trait ... LL | type _T21 = dyn Obj + _5; - | --- ^^ trait alias used in trait object type (additional use) + | --- ^^ second non-auto trait comes from this alias | | | first non-auto trait | @@ -286,29 +272,29 @@ LL | type _T21 = dyn Obj + _5; = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object - --> $DIR/no-duplicates.rs:71:22 + --> $DIR/no-duplicates.rs:71:17 | LL | trait _5 = Obj + Send; - | --- first non-auto trait + | --- additional non-auto trait ... LL | type _T22 = dyn _5 + Obj; - | -- ^^^ additional non-auto trait + | ^^ --- first non-auto trait | | - | trait alias used in trait object type (first use) + | second non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Obj + Obj {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object - --> $DIR/no-duplicates.rs:74:36 + --> $DIR/no-duplicates.rs:74:17 | LL | trait _5 = Obj + Send; - | --- first non-auto trait + | --- additional non-auto trait ... LL | type _T23 = dyn _5 + Send + Sync + Obj; - | -- ^^^ additional non-auto trait + | ^^ --- first non-auto trait | | - | trait alias used in trait object type (first use) + | second non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Obj + Obj {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit @@ -319,19 +305,19 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec LL | trait _5 = Obj + Send; | --- | | - | additional non-auto trait | first non-auto trait + | additional non-auto trait ... LL | trait _6 = _5 + _5; // ==> Obj + Send + Obj + Send - | -- -- referenced here (additional use) + | -- -- second non-auto trait comes from this alias | | - | referenced here (first use) + | first non-auto trait comes from this alias LL | LL | type _T30 = dyn _6; | ^^ | | - | trait alias used in trait object type (additional use) - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias + | second non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Obj + Obj {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit @@ -342,19 +328,19 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec LL | trait _5 = Obj + Send; | --- | | - | additional non-auto trait | first non-auto trait + | additional non-auto trait ... LL | trait _6 = _5 + _5; // ==> Obj + Send + Obj + Send - | -- -- referenced here (additional use) + | -- -- second non-auto trait comes from this alias | | - | referenced here (first use) + | first non-auto trait comes from this alias ... LL | type _T31 = dyn _6 + Send; | ^^ | | - | trait alias used in trait object type (additional use) - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias + | second non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Obj + Obj {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit @@ -365,38 +351,38 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec LL | trait _5 = Obj + Send; | --- | | - | additional non-auto trait | first non-auto trait + | additional non-auto trait ... LL | trait _6 = _5 + _5; // ==> Obj + Send + Obj + Send - | -- -- referenced here (additional use) + | -- -- second non-auto trait comes from this alias | | - | referenced here (first use) + | first non-auto trait comes from this alias ... LL | type _T32 = dyn Send + _6; | ^^ | | - | trait alias used in trait object type (additional use) - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias + | second non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Obj + Obj {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object - --> $DIR/no-duplicates.rs:95:22 + --> $DIR/no-duplicates.rs:95:17 | LL | trait _5 = Obj + Send; - | --- first non-auto trait + | --- additional non-auto trait ... LL | trait _7 = _5 + Sync; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias LL | trait _8 = Unpin + _7; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias LL | LL | type _T40 = dyn _8 + Obj; - | -- ^^^ additional non-auto trait + | ^^ --- first non-auto trait | | - | trait alias used in trait object type (first use) + | second non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Obj + Obj {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit @@ -408,12 +394,12 @@ LL | trait _5 = Obj + Send; | --- additional non-auto trait ... LL | trait _7 = _5 + Sync; - | -- referenced here (additional use) + | -- second non-auto trait comes from this alias LL | trait _8 = Unpin + _7; - | -- referenced here (additional use) + | -- second non-auto trait comes from this alias ... LL | type _T41 = dyn Obj + _8; - | --- ^^ trait alias used in trait object type (additional use) + | --- ^^ second non-auto trait comes from this alias | | | first non-auto trait | @@ -421,25 +407,25 @@ LL | type _T41 = dyn Obj + _8; = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object - --> $DIR/no-duplicates.rs:101:22 + --> $DIR/no-duplicates.rs:101:17 | LL | trait _3 = Obj; - | --- additional non-auto trait + | --- first non-auto trait LL | trait _4 = _3; - | -- referenced here (additional use) + | -- first non-auto trait comes from this alias ... LL | trait _5 = Obj + Send; - | --- first non-auto trait + | --- additional non-auto trait ... LL | trait _7 = _5 + Sync; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias LL | trait _8 = Unpin + _7; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias ... LL | type _T42 = dyn _8 + _4; - | -- ^^ trait alias used in trait object type (additional use) + | ^^ -- first non-auto trait comes from this alias | | - | trait alias used in trait object type (first use) + | second non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Obj + Obj {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit @@ -450,20 +436,20 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec LL | trait _3 = Obj; | --- first non-auto trait LL | trait _4 = _3; - | -- referenced here (first use) + | -- first non-auto trait comes from this alias ... LL | trait _5 = Obj + Send; | --- additional non-auto trait ... LL | trait _7 = _5 + Sync; - | -- referenced here (additional use) + | -- second non-auto trait comes from this alias LL | trait _8 = Unpin + _7; - | -- referenced here (additional use) + | -- second non-auto trait comes from this alias ... LL | type _T43 = dyn _4 + _8; - | -- ^^ trait alias used in trait object type (additional use) + | -- ^^ second non-auto trait comes from this alias | | - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Obj + Obj {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit @@ -474,20 +460,20 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec LL | trait _3 = Obj; | --- first non-auto trait LL | trait _4 = _3; - | -- referenced here (first use) + | -- first non-auto trait comes from this alias ... LL | trait _5 = Obj + Send; | --- additional non-auto trait ... LL | trait _7 = _5 + Sync; - | -- referenced here (additional use) + | -- second non-auto trait comes from this alias LL | trait _8 = Unpin + _7; - | -- referenced here (additional use) + | -- second non-auto trait comes from this alias ... LL | type _T44 = dyn _4 + Send + Sync + _8; - | -- ^^ trait alias used in trait object type (additional use) + | -- ^^ second non-auto trait comes from this alias | | - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Obj + Obj {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit @@ -500,9 +486,9 @@ LL | trait _9 = for<'a> ObjL<'a>; LL | trait _10 = for<'b> ObjL<'b>; | ---------------- additional non-auto trait LL | type _T50 = dyn _9 + _10; - | -- ^^^ trait alias used in trait object type (additional use) + | -- ^^^ second non-auto trait comes from this alias | | - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: for<'a> ObjL<'a> + for<'b> ObjL<'b> {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit @@ -515,9 +501,9 @@ LL | trait _11 = ObjT fn(&'a u8)>; LL | trait _12 = ObjT fn(&'b u8)>; | ------------------------ additional non-auto trait LL | type _T60 = dyn _11 + _12; - | --- ^^^ trait alias used in trait object type (additional use) + | --- ^^^ second non-auto trait comes from this alias | | - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjT fn(&'a u8)> + ObjT fn(&'b u8)> {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit diff --git a/tests/ui/traits/alias/no-extra-traits.stderr b/tests/ui/traits/alias/no-extra-traits.stderr index 4b1ddf6843c3d..fcdb4937ff594 100644 --- a/tests/ui/traits/alias/no-extra-traits.stderr +++ b/tests/ui/traits/alias/no-extra-traits.stderr @@ -1,15 +1,15 @@ error[E0225]: only auto traits can be used as additional traits in a trait object - --> $DIR/no-extra-traits.rs:16:22 + --> $DIR/no-extra-traits.rs:16:17 | LL | trait _0 = ObjA; - | ---- first non-auto trait + | ---- additional non-auto trait ... LL | type _T00 = dyn _0 + ObjB; - | -- ^^^^ additional non-auto trait + | ^^ ---- first non-auto trait | | - | trait alias used in trait object type (first use) + | second non-auto trait comes from this alias | - = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` + = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object @@ -19,7 +19,7 @@ LL | trait _0 = ObjA; | ---- additional non-auto trait ... LL | type _T01 = dyn ObjB + _0; - | ---- ^^ trait alias used in trait object type (additional use) + | ---- ^^ second non-auto trait comes from this alias | | | first non-auto trait | @@ -32,10 +32,10 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec LL | trait _0 = ObjA; | ---- additional non-auto trait LL | trait _1 = _0; - | -- referenced here (additional use) + | -- second non-auto trait comes from this alias ... LL | type _T02 = dyn ObjB + _1; - | ---- ^^ trait alias used in trait object type (additional use) + | ---- ^^ second non-auto trait comes from this alias | | | first non-auto trait | @@ -43,19 +43,19 @@ LL | type _T02 = dyn ObjB + _1; = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object - --> $DIR/no-extra-traits.rs:25:22 + --> $DIR/no-extra-traits.rs:25:17 | LL | trait _0 = ObjA; - | ---- first non-auto trait + | ---- additional non-auto trait LL | trait _1 = _0; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias ... LL | type _T03 = dyn _1 + ObjB; - | -- ^^^^ additional non-auto trait + | ^^ ---- first non-auto trait | | - | trait alias used in trait object type (first use) + | second non-auto trait comes from this alias | - = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` + = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object @@ -64,34 +64,34 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec LL | trait _2 = ObjB; | ---- | | - | additional non-auto trait | first non-auto trait + | additional non-auto trait LL | trait _3 = _2; - | -- referenced here (additional use) + | -- second non-auto trait comes from this alias ... LL | type _T10 = dyn _2 + _3; - | -- ^^ trait alias used in trait object type (additional use) + | -- ^^ second non-auto trait comes from this alias | | - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjB + ObjB {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object - --> $DIR/no-extra-traits.rs:37:22 + --> $DIR/no-extra-traits.rs:37:17 | LL | trait _2 = ObjB; | ---- | | - | additional non-auto trait | first non-auto trait + | additional non-auto trait LL | trait _3 = _2; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias ... LL | type _T11 = dyn _3 + _2; - | -- ^^ trait alias used in trait object type (additional use) + | ^^ -- first non-auto trait comes from this alias | | - | trait alias used in trait object type (first use) + | second non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjB + ObjB {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit @@ -102,38 +102,38 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec LL | trait _2 = ObjB; | ---- | | - | additional non-auto trait | first non-auto trait + | additional non-auto trait LL | trait _3 = _2; - | -- referenced here (additional use) + | -- second non-auto trait comes from this alias LL | trait _4 = _3; - | -- referenced here (additional use) + | -- second non-auto trait comes from this alias ... LL | type _T12 = dyn _2 + _4; - | -- ^^ trait alias used in trait object type (additional use) + | -- ^^ second non-auto trait comes from this alias | | - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjB + ObjB {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object - --> $DIR/no-extra-traits.rs:43:22 + --> $DIR/no-extra-traits.rs:43:17 | LL | trait _2 = ObjB; | ---- | | - | additional non-auto trait | first non-auto trait + | additional non-auto trait LL | trait _3 = _2; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias LL | trait _4 = _3; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias ... LL | type _T13 = dyn _4 + _2; - | -- ^^ trait alias used in trait object type (additional use) + | ^^ -- first non-auto trait comes from this alias | | - | trait alias used in trait object type (first use) + | second non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjB + ObjB {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit @@ -144,50 +144,50 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec LL | trait _0 = ObjA; | ---- additional non-auto trait LL | trait _1 = _0; - | -- referenced here (additional use) + | -- second non-auto trait comes from this alias ... LL | trait _5 = Sync + ObjB + Send; | ---- first non-auto trait LL | LL | type _T20 = dyn _5 + _1; - | -- ^^ trait alias used in trait object type (additional use) + | -- ^^ second non-auto trait comes from this alias | | - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object - --> $DIR/no-extra-traits.rs:53:22 + --> $DIR/no-extra-traits.rs:53:17 | LL | trait _0 = ObjA; - | ---- first non-auto trait + | ---- additional non-auto trait LL | trait _1 = _0; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias ... LL | trait _5 = Sync + ObjB + Send; - | ---- additional non-auto trait + | ---- first non-auto trait ... LL | type _T21 = dyn _1 + _5; - | -- ^^ trait alias used in trait object type (additional use) + | ^^ -- first non-auto trait comes from this alias | | - | trait alias used in trait object type (first use) + | second non-auto trait comes from this alias | - = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` + = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object - --> $DIR/no-extra-traits.rs:56:22 + --> $DIR/no-extra-traits.rs:56:17 | LL | trait _5 = Sync + ObjB + Send; - | ---- first non-auto trait + | ---- additional non-auto trait ... LL | type _T22 = dyn _5 + ObjA; - | -- ^^^^ additional non-auto trait + | ^^ ---- first non-auto trait | | - | trait alias used in trait object type (first use) + | second non-auto trait comes from this alias | - = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` + = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object @@ -197,7 +197,7 @@ LL | trait _5 = Sync + ObjB + Send; | ---- additional non-auto trait ... LL | type _T23 = dyn ObjA + _5; - | ---- ^^ trait alias used in trait object type (additional use) + | ---- ^^ second non-auto trait comes from this alias | | | first non-auto trait | @@ -210,50 +210,50 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec LL | trait _0 = ObjA; | ---- additional non-auto trait LL | trait _1 = _0; - | -- referenced here (additional use) + | -- second non-auto trait comes from this alias ... LL | trait _5 = Sync + ObjB + Send; | ---- first non-auto trait ... LL | type _T24 = dyn Send + _5 + _1 + Sync; - | -- ^^ trait alias used in trait object type (additional use) + | -- ^^ second non-auto trait comes from this alias | | - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object - --> $DIR/no-extra-traits.rs:65:29 + --> $DIR/no-extra-traits.rs:65:17 | LL | trait _0 = ObjA; - | ---- first non-auto trait + | ---- additional non-auto trait LL | trait _1 = _0; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias ... LL | trait _5 = Sync + ObjB + Send; - | ---- additional non-auto trait + | ---- first non-auto trait ... LL | type _T25 = dyn _1 + Sync + _5 + Send; - | -- ^^ trait alias used in trait object type (additional use) + | ^^ -- first non-auto trait comes from this alias | | - | trait alias used in trait object type (first use) + | second non-auto trait comes from this alias | - = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` + = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object - --> $DIR/no-extra-traits.rs:68:36 + --> $DIR/no-extra-traits.rs:68:31 | LL | trait _5 = Sync + ObjB + Send; - | ---- first non-auto trait + | ---- additional non-auto trait ... LL | type _T26 = dyn Sync + Send + _5 + ObjA; - | -- ^^^^ additional non-auto trait + | ^^ ---- first non-auto trait | | - | trait alias used in trait object type (first use) + | second non-auto trait comes from this alias | - = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` + = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object @@ -263,7 +263,7 @@ LL | trait _5 = Sync + ObjB + Send; | ---- additional non-auto trait ... LL | type _T27 = dyn Send + Sync + ObjA + _5; - | ---- ^^ trait alias used in trait object type (additional use) + | ---- ^^ second non-auto trait comes from this alias | | | first non-auto trait | @@ -274,199 +274,199 @@ error[E0225]: only auto traits can be used as additional traits in a trait objec --> $DIR/no-extra-traits.rs:80:17 | LL | trait _0 = ObjA; - | ---- first non-auto trait + | ---- additional non-auto trait LL | trait _1 = _0; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias ... LL | trait _5 = Sync + ObjB + Send; - | ---- additional non-auto trait + | ---- first non-auto trait ... LL | trait _6 = _1 + _5; - | -- -- referenced here (additional use) + | -- -- first non-auto trait comes from this alias | | - | referenced here (first use) + | second non-auto trait comes from this alias ... LL | type _T30 = dyn _6; | ^^ | | - | trait alias used in trait object type (additional use) - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias + | second non-auto trait comes from this alias | - = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` + = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/no-extra-traits.rs:83:17 | LL | trait _0 = ObjA; - | ---- first non-auto trait + | ---- additional non-auto trait LL | trait _1 = _0; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias ... LL | trait _5 = Sync + ObjB + Send; - | ---- additional non-auto trait + | ---- first non-auto trait ... LL | trait _6 = _1 + _5; - | -- -- referenced here (additional use) + | -- -- first non-auto trait comes from this alias | | - | referenced here (first use) + | second non-auto trait comes from this alias ... LL | type _T31 = dyn _6 + Send; | ^^ | | - | trait alias used in trait object type (additional use) - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias + | second non-auto trait comes from this alias | - = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` + = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/no-extra-traits.rs:86:24 | LL | trait _0 = ObjA; - | ---- first non-auto trait + | ---- additional non-auto trait LL | trait _1 = _0; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias ... LL | trait _5 = Sync + ObjB + Send; - | ---- additional non-auto trait + | ---- first non-auto trait ... LL | trait _6 = _1 + _5; - | -- -- referenced here (additional use) + | -- -- first non-auto trait comes from this alias | | - | referenced here (first use) + | second non-auto trait comes from this alias ... LL | type _T32 = dyn Send + _6; | ^^ | | - | trait alias used in trait object type (additional use) - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias + | second non-auto trait comes from this alias | - = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` + = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/no-extra-traits.rs:89:17 | LL | trait _0 = ObjA; - | ---- first non-auto trait + | ---- additional non-auto trait LL | trait _1 = _0; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias ... LL | trait _5 = Sync + ObjB + Send; - | ---- additional non-auto trait + | ---- first non-auto trait ... LL | trait _6 = _1 + _5; - | -- -- referenced here (additional use) + | -- -- first non-auto trait comes from this alias | | - | referenced here (first use) + | second non-auto trait comes from this alias LL | trait _7 = _6; | -- | | - | referenced here (additional use) - | referenced here (first use) + | first non-auto trait comes from this alias + | second non-auto trait comes from this alias LL | trait _8 = _7; | -- | | - | referenced here (additional use) - | referenced here (first use) + | first non-auto trait comes from this alias + | second non-auto trait comes from this alias ... LL | type _T33 = dyn _8; | ^^ | | - | trait alias used in trait object type (additional use) - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias + | second non-auto trait comes from this alias | - = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` + = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/no-extra-traits.rs:92:17 | LL | trait _0 = ObjA; - | ---- first non-auto trait + | ---- additional non-auto trait LL | trait _1 = _0; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias ... LL | trait _5 = Sync + ObjB + Send; - | ---- additional non-auto trait + | ---- first non-auto trait ... LL | trait _6 = _1 + _5; - | -- -- referenced here (additional use) + | -- -- first non-auto trait comes from this alias | | - | referenced here (first use) + | second non-auto trait comes from this alias LL | trait _7 = _6; | -- | | - | referenced here (additional use) - | referenced here (first use) + | first non-auto trait comes from this alias + | second non-auto trait comes from this alias LL | trait _8 = _7; | -- | | - | referenced here (additional use) - | referenced here (first use) + | first non-auto trait comes from this alias + | second non-auto trait comes from this alias ... LL | type _T34 = dyn _8 + Send; | ^^ | | - | trait alias used in trait object type (additional use) - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias + | second non-auto trait comes from this alias | - = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` + = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object --> $DIR/no-extra-traits.rs:95:24 | LL | trait _0 = ObjA; - | ---- first non-auto trait + | ---- additional non-auto trait LL | trait _1 = _0; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias ... LL | trait _5 = Sync + ObjB + Send; - | ---- additional non-auto trait + | ---- first non-auto trait ... LL | trait _6 = _1 + _5; - | -- -- referenced here (additional use) + | -- -- first non-auto trait comes from this alias | | - | referenced here (first use) + | second non-auto trait comes from this alias LL | trait _7 = _6; | -- | | - | referenced here (additional use) - | referenced here (first use) + | first non-auto trait comes from this alias + | second non-auto trait comes from this alias LL | trait _8 = _7; | -- | | - | referenced here (additional use) - | referenced here (first use) + | first non-auto trait comes from this alias + | second non-auto trait comes from this alias ... LL | type _T35 = dyn Send + _8; | ^^ | | - | trait alias used in trait object type (additional use) - | trait alias used in trait object type (first use) + | first non-auto trait comes from this alias + | second non-auto trait comes from this alias | - = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` + = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object - --> $DIR/no-extra-traits.rs:103:23 + --> $DIR/no-extra-traits.rs:103:17 | LL | trait _5 = Sync + ObjB + Send; - | ---- first non-auto trait + | ---- additional non-auto trait ... LL | trait _9 = _5 + Sync; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias LL | trait _10 = Unpin + _9; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias LL | LL | type _T40 = dyn _10 + ObjA; - | --- ^^^^ additional non-auto trait + | ^^^ ---- first non-auto trait | | - | trait alias used in trait object type (first use) + | second non-auto trait comes from this alias | - = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` + = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object @@ -476,12 +476,12 @@ LL | trait _5 = Sync + ObjB + Send; | ---- additional non-auto trait ... LL | trait _9 = _5 + Sync; - | -- referenced here (additional use) + | -- second non-auto trait comes from this alias LL | trait _10 = Unpin + _9; - | -- referenced here (additional use) + | -- second non-auto trait comes from this alias ... LL | type _T41 = dyn ObjA + _10; - | ---- ^^^ trait alias used in trait object type (additional use) + | ---- ^^^ second non-auto trait comes from this alias | | | first non-auto trait | @@ -489,46 +489,46 @@ LL | type _T41 = dyn ObjA + _10; = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object - --> $DIR/no-extra-traits.rs:109:23 + --> $DIR/no-extra-traits.rs:109:17 | LL | trait _0 = ObjA; - | ---- additional non-auto trait + | ---- first non-auto trait LL | trait _1 = _0; - | -- referenced here (additional use) + | -- first non-auto trait comes from this alias ... LL | trait _5 = Sync + ObjB + Send; - | ---- first non-auto trait + | ---- additional non-auto trait ... LL | trait _9 = _5 + Sync; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias LL | trait _10 = Unpin + _9; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias ... LL | type _T42 = dyn _10 + _1; - | --- ^^ trait alias used in trait object type (additional use) + | ^^^ -- first non-auto trait comes from this alias | | - | trait alias used in trait object type (first use) + | second non-auto trait comes from this alias | - = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` + = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object - --> $DIR/no-extra-traits.rs:112:37 + --> $DIR/no-extra-traits.rs:112:24 | LL | trait _5 = Sync + ObjB + Send; - | ---- first non-auto trait + | ---- additional non-auto trait ... LL | trait _9 = _5 + Sync; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias LL | trait _10 = Unpin + _9; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias ... LL | type _T43 = dyn Send + _10 + Sync + ObjA; - | --- ^^^^ additional non-auto trait + | ^^^ ---- first non-auto trait | | - | trait alias used in trait object type (first use) + | second non-auto trait comes from this alias | - = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` + = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object @@ -538,12 +538,12 @@ LL | trait _5 = Sync + ObjB + Send; | ---- additional non-auto trait ... LL | trait _9 = _5 + Sync; - | -- referenced here (additional use) + | -- second non-auto trait comes from this alias LL | trait _10 = Unpin + _9; - | -- referenced here (additional use) + | -- second non-auto trait comes from this alias ... LL | type _T44 = dyn ObjA + _10 + Send + Sync; - | ---- ^^^ trait alias used in trait object type (additional use) + | ---- ^^^ second non-auto trait comes from this alias | | | first non-auto trait | @@ -551,27 +551,27 @@ LL | type _T44 = dyn ObjA + _10 + Send + Sync; = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error[E0225]: only auto traits can be used as additional traits in a trait object - --> $DIR/no-extra-traits.rs:118:37 + --> $DIR/no-extra-traits.rs:118:31 | LL | trait _0 = ObjA; - | ---- additional non-auto trait + | ---- first non-auto trait LL | trait _1 = _0; - | -- referenced here (additional use) + | -- first non-auto trait comes from this alias ... LL | trait _5 = Sync + ObjB + Send; - | ---- first non-auto trait + | ---- additional non-auto trait ... LL | trait _9 = _5 + Sync; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias LL | trait _10 = Unpin + _9; - | -- referenced here (first use) + | -- second non-auto trait comes from this alias ... LL | type _T45 = dyn Sync + Send + _10 + _1; - | --- ^^ trait alias used in trait object type (additional use) + | ^^^ -- first non-auto trait comes from this alias | | - | trait alias used in trait object type (first use) + | second non-auto trait comes from this alias | - = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjB + ObjA {}` + = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: ObjA + ObjB {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error: aborting due to 28 previous errors diff --git a/tests/ui/traits/alias/object-fail.rs b/tests/ui/traits/alias/object-fail.rs index 5c753ff207c1a..9a1cbad53e71b 100644 --- a/tests/ui/traits/alias/object-fail.rs +++ b/tests/ui/traits/alias/object-fail.rs @@ -5,7 +5,7 @@ trait IteratorAlias = Iterator; fn main() { let _: &dyn EqAlias = &123; - //~^ ERROR the trait `Eq` cannot be made into an object [E0038] + //~^ ERROR the trait alias `EqAlias` is not dyn compatible [E0038] let _: &dyn IteratorAlias = &vec![123].into_iter(); //~^ ERROR must be specified } diff --git a/tests/ui/traits/alias/object-fail.stderr b/tests/ui/traits/alias/object-fail.stderr index 1b89b87db9f8f..d60d884340777 100644 --- a/tests/ui/traits/alias/object-fail.stderr +++ b/tests/ui/traits/alias/object-fail.stderr @@ -1,13 +1,19 @@ -error[E0038]: the trait `Eq` cannot be made into an object - --> $DIR/object-fail.rs:7:13 +error[E0038]: the trait alias `EqAlias` is not dyn compatible + --> $DIR/object-fail.rs:7:17 | LL | let _: &dyn EqAlias = &123; - | ^^^^^^^^^^^ `Eq` cannot be made into an object + | ^^^^^^^ `EqAlias` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $SRC_DIR/core/src/cmp.rs:LL:COL | - = note: the trait cannot be made into an object because it uses `Self` as a type parameter + = note: ...because it uses `Self` as a type parameter + | + ::: $DIR/object-fail.rs:3:7 + | +LL | trait EqAlias = Eq; + | ------- this trait is not dyn compatible... error[E0191]: the value of the associated type `Item` in `Iterator` must be specified --> $DIR/object-fail.rs:9:17 diff --git a/tests/ui/traits/alias/self-in-const-generics.rs b/tests/ui/traits/alias/self-in-const-generics.rs index b0de8ccd67847..a7d0ac9cbb4c2 100644 --- a/tests/ui/traits/alias/self-in-const-generics.rs +++ b/tests/ui/traits/alias/self-in-const-generics.rs @@ -7,6 +7,6 @@ trait Bar {} trait BB = Bar<{ 2 + 1 }>; fn foo(x: &dyn BB) {} -//~^ ERROR the trait alias `BB` cannot be made into an object [E0038] +//~^ ERROR the trait alias `BB` is not dyn compatible [E0038] fn main() {} diff --git a/tests/ui/traits/alias/self-in-const-generics.stderr b/tests/ui/traits/alias/self-in-const-generics.stderr index 3de31b64c8bf9..ea201a2dd977c 100644 --- a/tests/ui/traits/alias/self-in-const-generics.stderr +++ b/tests/ui/traits/alias/self-in-const-generics.stderr @@ -1,10 +1,22 @@ -error[E0038]: the trait alias `BB` cannot be made into an object +error[E0038]: the trait alias `BB` is not dyn compatible --> $DIR/self-in-const-generics.rs:9:16 | LL | fn foo(x: &dyn BB) {} - | ^^ + | ^^ `BB` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/self-in-const-generics.rs:7:12 + | +LL | trait BB = Bar<{ 2 + 1 }>; + | -- ^^^^^^^^^^^^^^ ...because it uses `Self` as a type parameter + | | + | this trait is not dyn compatible... +help: consider using an opaque type instead + | +LL - fn foo(x: &dyn BB) {} +LL + fn foo(x: &impl BB) {} | - = note: it cannot use `Self` as a type parameter in a supertrait or `where`-clause error: aborting due to 1 previous error diff --git a/tests/ui/traits/alias/self-in-generics.rs b/tests/ui/traits/alias/self-in-generics.rs index 433b741532d6c..53752d9cede66 100644 --- a/tests/ui/traits/alias/self-in-generics.rs +++ b/tests/ui/traits/alias/self-in-generics.rs @@ -6,6 +6,6 @@ pub trait SelfInput = Fn(&mut Self); pub fn f(_f: &dyn SelfInput) {} -//~^ ERROR the trait alias `SelfInput` cannot be made into an object [E0038] +//~^ ERROR the trait alias `SelfInput` is not dyn compatible [E0038] fn main() {} diff --git a/tests/ui/traits/alias/self-in-generics.stderr b/tests/ui/traits/alias/self-in-generics.stderr index ffc0a00ad7d9b..abeb11907adce 100644 --- a/tests/ui/traits/alias/self-in-generics.stderr +++ b/tests/ui/traits/alias/self-in-generics.stderr @@ -1,10 +1,24 @@ -error[E0038]: the trait alias `SelfInput` cannot be made into an object +error[E0038]: the trait alias `SelfInput` is not dyn compatible --> $DIR/self-in-generics.rs:8:19 | LL | pub fn f(_f: &dyn SelfInput) {} - | ^^^^^^^^^ + | ^^^^^^^^^ `SelfInput` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/self-in-generics.rs:6:23 + | +LL | pub trait SelfInput = Fn(&mut Self); + | --------- ^^^^^^^^^^^^^ + | | | + | | ...because it uses `Self` as a type parameter + | | ...because it uses `Self` as a type parameter + | this trait is not dyn compatible... +help: consider using an opaque type instead + | +LL - pub fn f(_f: &dyn SelfInput) {} +LL + pub fn f(_f: &impl SelfInput) {} | - = note: it cannot use `Self` as a type parameter in a supertrait or `where`-clause error: aborting due to 1 previous error diff --git a/tests/ui/traits/alias/suggest-trait-alias-instead-of-type.stderr b/tests/ui/traits/alias/suggest-trait-alias-instead-of-type.stderr index afe34a125b202..af4e4214be2db 100644 --- a/tests/ui/traits/alias/suggest-trait-alias-instead-of-type.stderr +++ b/tests/ui/traits/alias/suggest-trait-alias-instead-of-type.stderr @@ -6,7 +6,8 @@ LL | struct Struct(S); | help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias | -LL | trait Strings = Iterator; +LL - type Strings = Iterator; +LL + trait Strings = Iterator; | error: aborting due to 1 previous error diff --git a/tests/ui/traits/associated_type_bound/assoc_type_bound_with_struct.stderr b/tests/ui/traits/associated_type_bound/assoc_type_bound_with_struct.stderr index 0020f9e416df2..2288bd1129c43 100644 --- a/tests/ui/traits/associated_type_bound/assoc_type_bound_with_struct.stderr +++ b/tests/ui/traits/associated_type_bound/assoc_type_bound_with_struct.stderr @@ -9,12 +9,13 @@ LL | struct Foo where T: Bar, ::Baz: String { | help: constrain the associated type to `String` | -LL | struct Foo where T: Bar, T: Bar { - | ~~~~~~~~~~~~~~~~~~~~ +LL - struct Foo where T: Bar, ::Baz: String { +LL + struct Foo where T: Bar, T: Bar { + | help: a trait with a similar name exists | LL | struct Foo where T: Bar, ::Baz: ToString { - | ~~~~~~~~ + | ++ error[E0404]: expected trait, found struct `String` --> $DIR/assoc_type_bound_with_struct.rs:9:54 @@ -27,12 +28,13 @@ LL | struct Qux<'a, T> where T: Bar, <&'a T as Bar>::Baz: String { | help: constrain the associated type to `String` | -LL | struct Qux<'a, T> where T: Bar, &'a T: Bar { - | ~~~~~~~~~~~~~~~~~~~~~~~~ +LL - struct Qux<'a, T> where T: Bar, <&'a T as Bar>::Baz: String { +LL + struct Qux<'a, T> where T: Bar, &'a T: Bar { + | help: a trait with a similar name exists | LL | struct Qux<'a, T> where T: Bar, <&'a T as Bar>::Baz: ToString { - | ~~~~~~~~ + | ++ error[E0404]: expected trait, found struct `String` --> $DIR/assoc_type_bound_with_struct.rs:13:45 @@ -45,12 +47,13 @@ LL | fn foo(_: T) where ::Baz: String { | help: constrain the associated type to `String` | -LL | fn foo(_: T) where T: Bar { - | ~~~~~~~~~~~~~~~~~~~~ +LL - fn foo(_: T) where ::Baz: String { +LL + fn foo(_: T) where T: Bar { + | help: a trait with a similar name exists | LL | fn foo(_: T) where ::Baz: ToString { - | ~~~~~~~~ + | ++ error[E0404]: expected trait, found struct `String` --> $DIR/assoc_type_bound_with_struct.rs:16:57 @@ -63,12 +66,13 @@ LL | fn qux<'a, T: Bar>(_: &'a T) where <&'a T as Bar>::Baz: String { | help: constrain the associated type to `String` | -LL | fn qux<'a, T: Bar>(_: &'a T) where &'a T: Bar { - | ~~~~~~~~~~~~~~~~~~~~~~~~ +LL - fn qux<'a, T: Bar>(_: &'a T) where <&'a T as Bar>::Baz: String { +LL + fn qux<'a, T: Bar>(_: &'a T) where &'a T: Bar { + | help: a trait with a similar name exists | LL | fn qux<'a, T: Bar>(_: &'a T) where <&'a T as Bar>::Baz: ToString { - | ~~~~~~~~ + | ++ error[E0405]: cannot find trait `Unresolved` in this scope --> $DIR/assoc_type_bound_with_struct.rs:19:31 diff --git a/tests/ui/traits/bad-sized.stderr b/tests/ui/traits/bad-sized.stderr index 0e82867ef03b0..21718cf0951ca 100644 --- a/tests/ui/traits/bad-sized.stderr +++ b/tests/ui/traits/bad-sized.stderr @@ -1,12 +1,12 @@ error[E0225]: only auto traits can be used as additional traits in a trait object - --> $DIR/bad-sized.rs:4:28 + --> $DIR/bad-sized.rs:4:20 | LL | let x: Vec = Vec::new(); - | ----- ^^^^^ additional non-auto trait + | ^^^^^ ----- first non-auto trait | | - | first non-auto trait + | additional non-auto trait | - = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Trait + Sized {}` + = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Sized + Trait {}` = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit error: aborting due to 1 previous error diff --git a/tests/ui/traits/bound/not-on-bare-trait-2021.stderr b/tests/ui/traits/bound/not-on-bare-trait-2021.stderr index e50186aff7e67..b81aa54b88dbe 100644 --- a/tests/ui/traits/bound/not-on-bare-trait-2021.stderr +++ b/tests/ui/traits/bound/not-on-bare-trait-2021.stderr @@ -6,8 +6,9 @@ LL | fn foo(_x: Foo + Send) { | help: use a new generic type parameter, constrained by `Foo + Send` | -LL | fn foo(_x: T) { - | +++++++++++++++ ~ +LL - fn foo(_x: Foo + Send) { +LL + fn foo(_x: T) { + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn foo(_x: impl Foo + Send) { @@ -25,8 +26,9 @@ LL | fn bar(x: Foo) -> Foo { | help: use a new generic type parameter, constrained by `Foo` | -LL | fn bar(x: T) -> Foo { - | ++++++++ ~ +LL - fn bar(x: Foo) -> Foo { +LL + fn bar(x: T) -> Foo { + | help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference | LL | fn bar(x: impl Foo) -> Foo { diff --git a/tests/ui/traits/bound/not-on-bare-trait.stderr b/tests/ui/traits/bound/not-on-bare-trait.stderr index c2cb303b01874..9028e66fa020c 100644 --- a/tests/ui/traits/bound/not-on-bare-trait.stderr +++ b/tests/ui/traits/bound/not-on-bare-trait.stderr @@ -39,8 +39,9 @@ LL | fn bar(_x: (dyn Foo + Send)) { = help: unsized fn params are gated as an unstable feature help: you can use `impl Trait` as the argument type | -LL | fn bar(_x: (impl Foo + Send)) { - | ~~~~ +LL - fn bar(_x: (dyn Foo + Send)) { +LL + fn bar(_x: (impl Foo + Send)) { + | help: function arguments must have a statically known size, borrowed types always have a known size | LL | fn bar(_x: (&(dyn Foo + Send))) { diff --git a/tests/ui/traits/bound/not-on-struct.stderr b/tests/ui/traits/bound/not-on-struct.stderr index 2de35dc7fc376..1fb5b1c20670c 100644 --- a/tests/ui/traits/bound/not-on-struct.stderr +++ b/tests/ui/traits/bound/not-on-struct.stderr @@ -166,8 +166,9 @@ LL + fn g() -> Traitor { | help: a trait with a similar name exists | -LL | fn g() -> Trait + 'static { - | ~~~~~ +LL - fn g() -> Traitor + 'static { +LL + fn g() -> Trait + 'static { + | error: aborting due to 11 previous errors diff --git a/tests/ui/traits/coercion-generic-regions.stderr b/tests/ui/traits/coercion-generic-regions.stderr index 576035f8c13e4..c48767095dffb 100644 --- a/tests/ui/traits/coercion-generic-regions.stderr +++ b/tests/ui/traits/coercion-generic-regions.stderr @@ -4,11 +4,9 @@ error[E0597]: `person` does not live long enough LL | let person = "Fred".to_string(); | ------ binding `person` declared here LL | let person: &str = &person; - | ^^^^^^^ - | | - | borrowed value does not live long enough - | assignment requires that `person` is borrowed for `'static` + | ^^^^^^^ borrowed value does not live long enough LL | let s: Box> = Box::new(Struct { person: person }); + | ------ this usage requires that `person` is borrowed for `'static` LL | } | - `person` dropped here while still borrowed diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.current.stderr b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.current.stderr index 03da9159bea0b..4cd87002e4910 100644 --- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.current.stderr +++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.current.stderr @@ -5,7 +5,7 @@ LL | T::Assoc::::func(); | ^^^^^^^^^^^^^ error[E0277]: the trait bound `U: ~const Other` is not satisfied - --> $DIR/assoc-type-const-bound-usage-fail-2.rs:27:5 + --> $DIR/assoc-type-const-bound-usage-fail-2.rs:26:5 | LL | ::Assoc::::func(); | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.next.stderr b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.next.stderr index ce58b486a16ef..4cd87002e4910 100644 --- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.next.stderr +++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.next.stderr @@ -1,11 +1,11 @@ -error[E0277]: the trait bound `::Assoc: ~const Trait` is not satisfied +error[E0277]: the trait bound `U: ~const Other` is not satisfied --> $DIR/assoc-type-const-bound-usage-fail-2.rs:24:5 | LL | T::Assoc::::func(); | ^^^^^^^^^^^^^ -error[E0277]: the trait bound `::Assoc: ~const Trait` is not satisfied - --> $DIR/assoc-type-const-bound-usage-fail-2.rs:27:5 +error[E0277]: the trait bound `U: ~const Other` is not satisfied + --> $DIR/assoc-type-const-bound-usage-fail-2.rs:26:5 | LL | ::Assoc::::func(); | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.rs b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.rs index bdd98eaf541f8..e1c30b5361124 100644 --- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.rs +++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.rs @@ -22,11 +22,9 @@ trait Other {} const fn fails() { T::Assoc::::func(); - //[current]~^ ERROR the trait bound `U: ~const Other` is not satisfied - //[next]~^^ ERROR the trait bound `::Assoc: ~const Trait` is not satisfied + //~^ ERROR the trait bound `U: ~const Other` is not satisfied ::Assoc::::func(); - //[current]~^ ERROR the trait bound `U: ~const Other` is not satisfied - //[next]~^^ ERROR the trait bound `::Assoc: ~const Trait` is not satisfied + //~^ ERROR the trait bound `U: ~const Other` is not satisfied } const fn works() { diff --git a/tests/ui/traits/const-traits/auxiliary/minicore.rs b/tests/ui/traits/const-traits/auxiliary/minicore.rs index e606d896e93d0..08d7817548d7c 100644 --- a/tests/ui/traits/const-traits/auxiliary/minicore.rs +++ b/tests/ui/traits/const-traits/auxiliary/minicore.rs @@ -471,7 +471,6 @@ pub trait StructuralPartialEq {} pub const fn drop(_: T) {} -#[rustc_intrinsic_must_be_overridden] #[rustc_intrinsic] const fn const_eval_select( arg: ARG, @@ -480,7 +479,4 @@ const fn const_eval_select( ) -> RET where F: const FnOnce, - G: FnOnce, -{ - loop {} -} + G: FnOnce; diff --git a/tests/ui/traits/const-traits/auxiliary/staged-api.rs b/tests/ui/traits/const-traits/auxiliary/staged-api.rs index abe22db702c65..933a25769dca2 100644 --- a/tests/ui/traits/const-traits/auxiliary/staged-api.rs +++ b/tests/ui/traits/const-traits/auxiliary/staged-api.rs @@ -4,6 +4,7 @@ #![stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "unstable", issue = "none")] #[const_trait] pub trait MyTrait { #[stable(feature = "rust1", since = "1.0.0")] diff --git a/tests/ui/traits/const-traits/call-const-trait-method-pass.rs b/tests/ui/traits/const-traits/call-const-trait-method-pass.rs index b854b422b3a99..3004647ede075 100644 --- a/tests/ui/traits/const-traits/call-const-trait-method-pass.rs +++ b/tests/ui/traits/const-traits/call-const-trait-method-pass.rs @@ -1,6 +1,6 @@ //@ known-bug: #110395 -#![feature(const_trait_impl)] +#![feature(const_trait_impl, const_ops)] struct Int(i32); diff --git a/tests/ui/traits/const-traits/const-and-non-const-impl.rs b/tests/ui/traits/const-traits/const-and-non-const-impl.rs index 6b96fcf0ae31d..85e2c5d3df62a 100644 --- a/tests/ui/traits/const-traits/const-and-non-const-impl.rs +++ b/tests/ui/traits/const-traits/const-and-non-const-impl.rs @@ -1,6 +1,6 @@ //@ known-bug: #110395 -#![feature(const_trait_impl)] +#![feature(const_trait_impl, const_ops)] pub struct Int(i32); diff --git a/tests/ui/traits/const-traits/const-drop-bound.rs b/tests/ui/traits/const-traits/const-drop-bound.rs index 398fb39064055..4819da7c3a403 100644 --- a/tests/ui/traits/const-traits/const-drop-bound.rs +++ b/tests/ui/traits/const-traits/const-drop-bound.rs @@ -1,5 +1,4 @@ -//@ known-bug: #110395 -// FIXME check-pass +//@ check-pass #![feature(const_trait_impl)] #![feature(const_precise_live_drops, const_destruct)] diff --git a/tests/ui/traits/const-traits/const-drop-bound.stderr b/tests/ui/traits/const-traits/const-drop-bound.stderr deleted file mode 100644 index 78ba0279566de..0000000000000 --- a/tests/ui/traits/const-traits/const-drop-bound.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error[E0277]: the trait bound `Foo: ~const Destruct` is not satisfied - --> $DIR/const-drop-bound.rs:23:9 - | -LL | foo(res) - | --- ^^^ - | | - | required by a bound introduced by this call - | -note: required by a bound in `foo` - --> $DIR/const-drop-bound.rs:9:61 - | -LL | const fn foo(res: Result) -> Option where E: ~const Destruct { - | ^^^^^^ required by this bound in `foo` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/const-drop-fail-2.precise.stderr b/tests/ui/traits/const-traits/const-drop-fail-2.precise.stderr index 7b2cafb612441..2b5e66b1a086c 100644 --- a/tests/ui/traits/const-traits/const-drop-fail-2.precise.stderr +++ b/tests/ui/traits/const-traits/const-drop-fail-2.precise.stderr @@ -1,9 +1,16 @@ -error[E0277]: the trait bound `ConstDropImplWithBounds: const Destruct` is not satisfied +error[E0277]: the trait bound `NonTrivialDrop: const A` is not satisfied --> $DIR/const-drop-fail-2.rs:31:23 | LL | const _: () = check::>( | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | +note: required for `ConstDropImplWithBounds` to implement `const Drop` + --> $DIR/const-drop-fail-2.rs:25:25 + | +LL | impl const Drop for ConstDropImplWithBounds { + | ------ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | unsatisfied trait bound introduced here note: required by a bound in `check` --> $DIR/const-drop-fail-2.rs:21:19 | diff --git a/tests/ui/traits/const-traits/const-drop-fail-2.stock.stderr b/tests/ui/traits/const-traits/const-drop-fail-2.stock.stderr index 7b2cafb612441..2b5e66b1a086c 100644 --- a/tests/ui/traits/const-traits/const-drop-fail-2.stock.stderr +++ b/tests/ui/traits/const-traits/const-drop-fail-2.stock.stderr @@ -1,9 +1,16 @@ -error[E0277]: the trait bound `ConstDropImplWithBounds: const Destruct` is not satisfied +error[E0277]: the trait bound `NonTrivialDrop: const A` is not satisfied --> $DIR/const-drop-fail-2.rs:31:23 | LL | const _: () = check::>( | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | +note: required for `ConstDropImplWithBounds` to implement `const Drop` + --> $DIR/const-drop-fail-2.rs:25:25 + | +LL | impl const Drop for ConstDropImplWithBounds { + | ------ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | unsatisfied trait bound introduced here note: required by a bound in `check` --> $DIR/const-drop-fail-2.rs:21:19 | diff --git a/tests/ui/traits/const-traits/const-drop-fail.new_precise.stderr b/tests/ui/traits/const-traits/const-drop-fail.new_precise.stderr new file mode 100644 index 0000000000000..682f48fe07af6 --- /dev/null +++ b/tests/ui/traits/const-traits/const-drop-fail.new_precise.stderr @@ -0,0 +1,33 @@ +error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied + --> $DIR/const-drop-fail.rs:33:5 + | +LL | const _: () = check($exp); + | ----- required by a bound introduced by this call +... +LL | NonTrivialDrop, + | ^^^^^^^^^^^^^^ + | +note: required by a bound in `check` + --> $DIR/const-drop-fail.rs:24:19 + | +LL | const fn check(_: T) {} + | ^^^^^^ required by this bound in `check` + +error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied + --> $DIR/const-drop-fail.rs:35:5 + | +LL | const _: () = check($exp); + | ----- required by a bound introduced by this call +... +LL | ConstImplWithDropGlue(NonTrivialDrop), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: required by a bound in `check` + --> $DIR/const-drop-fail.rs:24:19 + | +LL | const fn check(_: T) {} + | ^^^^^^ required by this bound in `check` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/const-drop-fail.new_stock.stderr b/tests/ui/traits/const-traits/const-drop-fail.new_stock.stderr new file mode 100644 index 0000000000000..682f48fe07af6 --- /dev/null +++ b/tests/ui/traits/const-traits/const-drop-fail.new_stock.stderr @@ -0,0 +1,33 @@ +error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied + --> $DIR/const-drop-fail.rs:33:5 + | +LL | const _: () = check($exp); + | ----- required by a bound introduced by this call +... +LL | NonTrivialDrop, + | ^^^^^^^^^^^^^^ + | +note: required by a bound in `check` + --> $DIR/const-drop-fail.rs:24:19 + | +LL | const fn check(_: T) {} + | ^^^^^^ required by this bound in `check` + +error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied + --> $DIR/const-drop-fail.rs:35:5 + | +LL | const _: () = check($exp); + | ----- required by a bound introduced by this call +... +LL | ConstImplWithDropGlue(NonTrivialDrop), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: required by a bound in `check` + --> $DIR/const-drop-fail.rs:24:19 + | +LL | const fn check(_: T) {} + | ^^^^^^ required by this bound in `check` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/const-drop-fail.old_precise.stderr b/tests/ui/traits/const-traits/const-drop-fail.old_precise.stderr new file mode 100644 index 0000000000000..682f48fe07af6 --- /dev/null +++ b/tests/ui/traits/const-traits/const-drop-fail.old_precise.stderr @@ -0,0 +1,33 @@ +error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied + --> $DIR/const-drop-fail.rs:33:5 + | +LL | const _: () = check($exp); + | ----- required by a bound introduced by this call +... +LL | NonTrivialDrop, + | ^^^^^^^^^^^^^^ + | +note: required by a bound in `check` + --> $DIR/const-drop-fail.rs:24:19 + | +LL | const fn check(_: T) {} + | ^^^^^^ required by this bound in `check` + +error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied + --> $DIR/const-drop-fail.rs:35:5 + | +LL | const _: () = check($exp); + | ----- required by a bound introduced by this call +... +LL | ConstImplWithDropGlue(NonTrivialDrop), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: required by a bound in `check` + --> $DIR/const-drop-fail.rs:24:19 + | +LL | const fn check(_: T) {} + | ^^^^^^ required by this bound in `check` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/const-drop-fail.old_stock.stderr b/tests/ui/traits/const-traits/const-drop-fail.old_stock.stderr new file mode 100644 index 0000000000000..682f48fe07af6 --- /dev/null +++ b/tests/ui/traits/const-traits/const-drop-fail.old_stock.stderr @@ -0,0 +1,33 @@ +error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied + --> $DIR/const-drop-fail.rs:33:5 + | +LL | const _: () = check($exp); + | ----- required by a bound introduced by this call +... +LL | NonTrivialDrop, + | ^^^^^^^^^^^^^^ + | +note: required by a bound in `check` + --> $DIR/const-drop-fail.rs:24:19 + | +LL | const fn check(_: T) {} + | ^^^^^^ required by this bound in `check` + +error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied + --> $DIR/const-drop-fail.rs:35:5 + | +LL | const _: () = check($exp); + | ----- required by a bound introduced by this call +... +LL | ConstImplWithDropGlue(NonTrivialDrop), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: required by a bound in `check` + --> $DIR/const-drop-fail.rs:24:19 + | +LL | const fn check(_: T) {} + | ^^^^^^ required by this bound in `check` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/const-drop-fail.precise.stderr b/tests/ui/traits/const-traits/const-drop-fail.precise.stderr deleted file mode 100644 index 8b3e777a0b098..0000000000000 --- a/tests/ui/traits/const-traits/const-drop-fail.precise.stderr +++ /dev/null @@ -1,33 +0,0 @@ -error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied - --> $DIR/const-drop-fail.rs:32:5 - | -LL | const _: () = check($exp); - | ----- required by a bound introduced by this call -... -LL | NonTrivialDrop, - | ^^^^^^^^^^^^^^ - | -note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:23:19 - | -LL | const fn check(_: T) {} - | ^^^^^^ required by this bound in `check` - -error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied - --> $DIR/const-drop-fail.rs:34:5 - | -LL | const _: () = check($exp); - | ----- required by a bound introduced by this call -... -LL | ConstImplWithDropGlue(NonTrivialDrop), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:23:19 - | -LL | const fn check(_: T) {} - | ^^^^^^ required by this bound in `check` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/const-drop-fail.rs b/tests/ui/traits/const-traits/const-drop-fail.rs index 5e05b9db474a6..a7f3d5654de93 100644 --- a/tests/ui/traits/const-traits/const-drop-fail.rs +++ b/tests/ui/traits/const-traits/const-drop-fail.rs @@ -1,8 +1,9 @@ -//@ compile-flags: -Znext-solver -//@ revisions: stock precise +//@[new_precise] compile-flags: -Znext-solver +//@[new_stock] compile-flags: -Znext-solver +//@ revisions: new_stock old_stock new_precise old_precise #![feature(const_trait_impl, const_destruct)] -#![cfg_attr(precise, feature(const_precise_live_drops))] +#![cfg_attr(any(new_precise, old_precise), feature(const_precise_live_drops))] use std::marker::{Destruct, PhantomData}; diff --git a/tests/ui/traits/const-traits/const-drop-fail.stock.stderr b/tests/ui/traits/const-traits/const-drop-fail.stock.stderr deleted file mode 100644 index 8b3e777a0b098..0000000000000 --- a/tests/ui/traits/const-traits/const-drop-fail.stock.stderr +++ /dev/null @@ -1,33 +0,0 @@ -error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied - --> $DIR/const-drop-fail.rs:32:5 - | -LL | const _: () = check($exp); - | ----- required by a bound introduced by this call -... -LL | NonTrivialDrop, - | ^^^^^^^^^^^^^^ - | -note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:23:19 - | -LL | const fn check(_: T) {} - | ^^^^^^ required by this bound in `check` - -error[E0277]: the trait bound `NonTrivialDrop: const Destruct` is not satisfied - --> $DIR/const-drop-fail.rs:34:5 - | -LL | const _: () = check($exp); - | ----- required by a bound introduced by this call -... -LL | ConstImplWithDropGlue(NonTrivialDrop), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: required by a bound in `check` - --> $DIR/const-drop-fail.rs:23:19 - | -LL | const fn check(_: T) {} - | ^^^^^^ required by this bound in `check` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/const-impl-trait.stderr b/tests/ui/traits/const-traits/const-impl-trait.stderr index 4e3200594485e..27d7957c00148 100644 --- a/tests/ui/traits/const-traits/const-impl-trait.stderr +++ b/tests/ui/traits/const-traits/const-impl-trait.stderr @@ -99,6 +99,16 @@ note: `PartialEq` can't be used with `~const` because it isn't annotated with `# --> $SRC_DIR/core/src/cmp.rs:LL:COL = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-impl-trait.rs:27:22 + | +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy { + | ^^^^^^ can't be applied to `PartialEq` + | +note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error: `~const` can only be applied to `#[const_trait]` traits --> $DIR/const-impl-trait.rs:23:22 | @@ -120,9 +130,9 @@ note: `PartialEq` can't be used with `~const` because it isn't annotated with `# = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: `~const` can only be applied to `#[const_trait]` traits - --> $DIR/const-impl-trait.rs:27:22 + --> $DIR/const-impl-trait.rs:23:22 | -LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy { +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; | ^^^^^^ can't be applied to `PartialEq` | note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` @@ -181,7 +191,7 @@ LL | a == a | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants -error: aborting due to 20 previous errors +error: aborting due to 21 previous errors Some errors have detailed explanations: E0015, E0635. For more information about an error, try `rustc --explain E0015`. diff --git a/tests/ui/traits/const-traits/cross-crate.stock.stderr b/tests/ui/traits/const-traits/cross-crate.stock.stderr index 09bf9c023c843..7cdde5a079f2d 100644 --- a/tests/ui/traits/const-traits/cross-crate.stock.stderr +++ b/tests/ui/traits/const-traits/cross-crate.stock.stderr @@ -1,9 +1,10 @@ error[E0658]: cannot call conditionally-const method `::func` in constant functions - --> $DIR/cross-crate.rs:22:5 + --> $DIR/cross-crate.rs:22:11 | LL | Const.func(); - | ^^^^^^^^^^^^ + | ^^^^^^ | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: see issue #67792 for more information = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date diff --git a/tests/ui/traits/const-traits/cross-crate.stocknc.stderr b/tests/ui/traits/const-traits/cross-crate.stocknc.stderr index e52e5609b01d4..fb47bf9169fe2 100644 --- a/tests/ui/traits/const-traits/cross-crate.stocknc.stderr +++ b/tests/ui/traits/const-traits/cross-crate.stocknc.stderr @@ -1,23 +1,23 @@ -error[E0658]: cannot call conditionally-const method `::func` in constant functions - --> $DIR/cross-crate.rs:19:5 +error[E0015]: cannot call non-const method `::func` in constant functions + --> $DIR/cross-crate.rs:19:14 | LL | NonConst.func(); - | ^^^^^^^^^^^^^^^ + | ^^^^^^ | - = note: see issue #67792 for more information - = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants error[E0658]: cannot call conditionally-const method `::func` in constant functions - --> $DIR/cross-crate.rs:22:5 + --> $DIR/cross-crate.rs:22:11 | LL | Const.func(); - | ^^^^^^^^^^^^ + | ^^^^^^ | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: see issue #67792 for more information = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0658`. +Some errors have detailed explanations: E0015, E0658. +For more information about an error, try `rustc --explain E0015`. diff --git a/tests/ui/traits/const-traits/double-error-for-unimplemented-trait.rs b/tests/ui/traits/const-traits/double-error-for-unimplemented-trait.rs new file mode 100644 index 0000000000000..f4b01efe95908 --- /dev/null +++ b/tests/ui/traits/const-traits/double-error-for-unimplemented-trait.rs @@ -0,0 +1,22 @@ +// Make sure we don't issue *two* error messages for the trait predicate *and* host predicate. + +#![feature(const_trait_impl)] + +#[const_trait] +trait Trait { + type Out; +} + +const fn needs_const(_: &T) {} + +const IN_CONST: () = { + needs_const(&()); + //~^ ERROR the trait bound `(): Trait` is not satisfied +}; + +const fn conditionally_const() { + needs_const(&()); + //~^ ERROR the trait bound `(): Trait` is not satisfied +} + +fn main() {} diff --git a/tests/ui/traits/const-traits/double-error-for-unimplemented-trait.stderr b/tests/ui/traits/const-traits/double-error-for-unimplemented-trait.stderr new file mode 100644 index 0000000000000..cd68cdaf8a2bf --- /dev/null +++ b/tests/ui/traits/const-traits/double-error-for-unimplemented-trait.stderr @@ -0,0 +1,41 @@ +error[E0277]: the trait bound `(): Trait` is not satisfied + --> $DIR/double-error-for-unimplemented-trait.rs:13:15 + | +LL | needs_const(&()); + | ----------- ^^^ the trait `Trait` is not implemented for `()` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/double-error-for-unimplemented-trait.rs:6:1 + | +LL | trait Trait { + | ^^^^^^^^^^^ +note: required by a bound in `needs_const` + --> $DIR/double-error-for-unimplemented-trait.rs:10:25 + | +LL | const fn needs_const(_: &T) {} + | ^^^^^^^^^^^^ required by this bound in `needs_const` + +error[E0277]: the trait bound `(): Trait` is not satisfied + --> $DIR/double-error-for-unimplemented-trait.rs:18:15 + | +LL | needs_const(&()); + | ----------- ^^^ the trait `Trait` is not implemented for `()` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/double-error-for-unimplemented-trait.rs:6:1 + | +LL | trait Trait { + | ^^^^^^^^^^^ +note: required by a bound in `needs_const` + --> $DIR/double-error-for-unimplemented-trait.rs:10:25 + | +LL | const fn needs_const(_: &T) {} + | ^^^^^^^^^^^^ required by this bound in `needs_const` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/enforce-deref-on-adjust.rs b/tests/ui/traits/const-traits/enforce-deref-on-adjust.rs new file mode 100644 index 0000000000000..d5240b7e18ddb --- /dev/null +++ b/tests/ui/traits/const-traits/enforce-deref-on-adjust.rs @@ -0,0 +1,28 @@ +//@ check-pass + +#![feature(const_deref)] +#![feature(const_trait_impl)] + +use std::ops::Deref; + +struct Wrap(T); +struct Foo; + +impl Foo { + const fn call(&self) {} +} + +impl const Deref for Wrap { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +const fn foo() { + let x = Wrap(Foo); + x.call(); +} + +fn main() {} diff --git a/tests/ui/traits/const-traits/eval-bad-signature.stderr b/tests/ui/traits/const-traits/eval-bad-signature.stderr index a64cf631743d5..52de5283f7fdf 100644 --- a/tests/ui/traits/const-traits/eval-bad-signature.stderr +++ b/tests/ui/traits/const-traits/eval-bad-signature.stderr @@ -13,8 +13,9 @@ LL | fn value() -> u32; found signature `fn() -> i64` help: change the output type to match the trait | -LL | fn value() -> u32 { - | ~~~ +LL - fn value() -> i64 { +LL + fn value() -> u32 { + | error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/generic-bound.rs b/tests/ui/traits/const-traits/generic-bound.rs index 5eb236acde22c..99de21471b20c 100644 --- a/tests/ui/traits/const-traits/generic-bound.rs +++ b/tests/ui/traits/const-traits/generic-bound.rs @@ -1,6 +1,6 @@ //@ check-pass -#![feature(const_trait_impl)] +#![feature(const_trait_impl, const_ops)] use std::marker::PhantomData; diff --git a/tests/ui/traits/const-traits/ice-119717-constant-lifetime.stderr b/tests/ui/traits/const-traits/ice-119717-constant-lifetime.stderr index 5af263de28c48..c6e0c699520b5 100644 --- a/tests/ui/traits/const-traits/ice-119717-constant-lifetime.stderr +++ b/tests/ui/traits/const-traits/ice-119717-constant-lifetime.stderr @@ -24,8 +24,9 @@ LL | fn from_residual(t: T) -> _ { | help: try replacing `_` with the type in the corresponding trait method signature | -LL | fn from_residual(t: T) -> T { - | ~ +LL - fn from_residual(t: T) -> _ { +LL + fn from_residual(t: T) -> T { + | error: aborting due to 3 previous errors diff --git a/tests/ui/traits/const-traits/item-bound-entailment-fails.stderr b/tests/ui/traits/const-traits/item-bound-entailment-fails.stderr index 3fc6f58470947..0d53bc5897ef3 100644 --- a/tests/ui/traits/const-traits/item-bound-entailment-fails.stderr +++ b/tests/ui/traits/const-traits/item-bound-entailment-fails.stderr @@ -16,6 +16,11 @@ error[E0277]: the trait bound `T: ~const Bar` is not satisfied LL | type Assoc = C | ^^^^ | +note: required for `C` to implement `~const Bar` + --> $DIR/item-bound-entailment-fails.rs:14:15 + | +LL | impl const Bar for C where T: ~const Bar {} + | ^^^ ^^^^ ------ unsatisfied trait bound introduced here note: required by a bound in `Foo::Assoc` --> $DIR/item-bound-entailment-fails.rs:5:20 | diff --git a/tests/ui/traits/const-traits/pattern-custom-partial-eq.rs b/tests/ui/traits/const-traits/pattern-custom-partial-eq.rs new file mode 100644 index 0000000000000..1003775be2f0b --- /dev/null +++ b/tests/ui/traits/const-traits/pattern-custom-partial-eq.rs @@ -0,0 +1,54 @@ +//! Ensure that a `const fn` can match on constants of a type that is `PartialEq` +//! but not `const PartialEq`. This is accepted for backwards compatibility reasons. +//@ check-pass +#![feature(const_trait_impl)] + +#[derive(Eq, PartialEq)] +pub struct Y(u8); +pub const GREEN: Y = Y(4); +pub const fn is_green(x: Y) -> bool { + match x { GREEN => true, _ => false } +} + +struct CustomEq; + +impl Eq for CustomEq {} +impl PartialEq for CustomEq { + fn eq(&self, _: &Self) -> bool { + false + } +} + +#[derive(PartialEq, Eq)] +#[allow(unused)] +enum Foo { + Bar, + Baz, + Qux(CustomEq), +} + +const BAR_BAZ: Foo = if 42 == 42 { + Foo::Bar +} else { + Foo::Qux(CustomEq) // dead arm +}; + +const EMPTY: &[CustomEq] = &[]; + +const fn test() { + // BAR_BAZ itself is fine but the enum has other variants + // that are non-structural. Still, this should be accepted. + match Foo::Qux(CustomEq) { + BAR_BAZ => panic!(), + _ => {} + } + + // Similarly, an empty slice of a type that is non-structural + // is accepted. + match &[CustomEq] as &[CustomEq] { + EMPTY => panic!(), + _ => {}, + } +} + +fn main() {} diff --git a/tests/ui/traits/const-traits/staged-api-user-crate.rs b/tests/ui/traits/const-traits/staged-api-user-crate.rs index 7587042cf276d..4aa75a50355b0 100644 --- a/tests/ui/traits/const-traits/staged-api-user-crate.rs +++ b/tests/ui/traits/const-traits/staged-api-user-crate.rs @@ -11,6 +11,7 @@ fn non_const_context() { const fn stable_const_context() { Unstable::func(); //~^ ERROR cannot call conditionally-const associated function `::func` in constant functions + //~| ERROR `staged_api::MyTrait` is not yet stable as a const trait } fn main() {} diff --git a/tests/ui/traits/const-traits/staged-api-user-crate.stderr b/tests/ui/traits/const-traits/staged-api-user-crate.stderr index bf7466b8e1666..8ac83770cf7a4 100644 --- a/tests/ui/traits/const-traits/staged-api-user-crate.stderr +++ b/tests/ui/traits/const-traits/staged-api-user-crate.stderr @@ -4,10 +4,22 @@ error[E0658]: cannot call conditionally-const associated function ` for more information = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 1 previous error +error: `staged_api::MyTrait` is not yet stable as a const trait + --> $DIR/staged-api-user-crate.rs:12:5 + | +LL | Unstable::func(); + | ^^^^^^^^^^^^^^^^ + | +help: add `#![feature(unstable)]` to the crate attributes to enable + | +LL + #![feature(unstable)] + | + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/traits/const-traits/staged-api.rs b/tests/ui/traits/const-traits/staged-api.rs index 755a4e456bcf9..9a030dafd6bc4 100644 --- a/tests/ui/traits/const-traits/staged-api.rs +++ b/tests/ui/traits/const-traits/staged-api.rs @@ -22,7 +22,7 @@ impl const MyTrait for Foo { fn func() {} } -#[rustc_allow_const_fn_unstable(const_trait_impl)] +#[rustc_allow_const_fn_unstable(const_trait_impl, unstable)] const fn conditionally_const() { T::func(); } @@ -37,10 +37,13 @@ fn non_const_context() { const fn const_context() { Unstable::func(); //~^ ERROR cannot use `#[feature(const_trait_impl)]` + //~| ERROR cannot use `#[feature(unstable)]` Foo::func(); //~^ ERROR cannot use `#[feature(const_trait_impl)]` + //~| ERROR cannot use `#[feature(unstable)]` Unstable2::func(); //~^ ERROR cannot use `#[feature(const_trait_impl)]` + //~| ERROR cannot use `#[feature(unstable)]` conditionally_const::(); //~^ ERROR cannot use `#[feature(const_trait_impl)]` } @@ -59,8 +62,23 @@ pub const fn const_context_not_const_stable() { const fn stable_const_context() { Unstable::func(); //~^ ERROR cannot use `#[feature(const_trait_impl)]` + //~| ERROR cannot use `#[feature(unstable)]` Foo::func(); //~^ ERROR cannot use `#[feature(const_trait_impl)]` + //~| ERROR cannot use `#[feature(unstable)]` + const_context_not_const_stable(); + //~^ ERROR cannot use `#[feature(local_feature)]` + conditionally_const::(); + //~^ ERROR cannot use `#[feature(const_trait_impl)]` +} + +const fn implicitly_stable_const_context() { + Unstable::func(); + //~^ ERROR cannot use `#[feature(const_trait_impl)]` + //~| ERROR cannot use `#[feature(unstable)]` + Foo::func(); + //~^ ERROR cannot use `#[feature(const_trait_impl)]` + //~| ERROR cannot use `#[feature(unstable)]` const_context_not_const_stable(); //~^ ERROR cannot use `#[feature(local_feature)]` conditionally_const::(); diff --git a/tests/ui/traits/const-traits/staged-api.stderr b/tests/ui/traits/const-traits/staged-api.stderr index 29aafa4e0f32e..a7a7a1ee7215b 100644 --- a/tests/ui/traits/const-traits/staged-api.stderr +++ b/tests/ui/traits/const-traits/staged-api.stderr @@ -15,8 +15,25 @@ LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn const_context() { | +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]` + --> $DIR/staged-api.rs:38:5 + | +LL | Unstable::func(); + | ^^^^^^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(unstable)] +LL | const fn const_context() { + | + error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` - --> $DIR/staged-api.rs:40:5 + --> $DIR/staged-api.rs:41:5 | LL | Foo::func(); | ^^^^^^^^^^^ @@ -32,8 +49,25 @@ LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn const_context() { | +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]` + --> $DIR/staged-api.rs:41:5 + | +LL | Foo::func(); + | ^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(unstable)] +LL | const fn const_context() { + | + error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` - --> $DIR/staged-api.rs:42:5 + --> $DIR/staged-api.rs:44:5 | LL | Unstable2::func(); | ^^^^^^^^^^^^^^^^^ @@ -49,9 +83,26 @@ LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn const_context() { | -error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]` --> $DIR/staged-api.rs:44:5 | +LL | Unstable2::func(); + | ^^^^^^^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(unstable)] +LL | const fn const_context() { + | + +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:47:5 + | LL | conditionally_const::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | @@ -67,7 +118,7 @@ LL | const fn const_context() { | error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` - --> $DIR/staged-api.rs:60:5 + --> $DIR/staged-api.rs:63:5 | LL | Unstable::func(); | ^^^^^^^^^^^^^^^^ @@ -83,8 +134,25 @@ LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn stable_const_context() { | +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]` + --> $DIR/staged-api.rs:63:5 + | +LL | Unstable::func(); + | ^^^^^^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(unstable)] +LL | const fn stable_const_context() { + | + error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` - --> $DIR/staged-api.rs:62:5 + --> $DIR/staged-api.rs:66:5 | LL | Foo::func(); | ^^^^^^^^^^^ @@ -100,13 +168,30 @@ LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn stable_const_context() { | +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]` + --> $DIR/staged-api.rs:66:5 + | +LL | Foo::func(); + | ^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(unstable)] +LL | const fn stable_const_context() { + | + error: const function that might be (indirectly) exposed to stable cannot use `#[feature(local_feature)]` - --> $DIR/staged-api.rs:64:5 + --> $DIR/staged-api.rs:69:5 | LL | const_context_not_const_stable(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features + = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) | LL + #[rustc_const_unstable(feature = "...", issue = "...")] @@ -119,7 +204,7 @@ LL | const fn stable_const_context() { | error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` - --> $DIR/staged-api.rs:66:5 + --> $DIR/staged-api.rs:71:5 | LL | conditionally_const::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -135,5 +220,108 @@ LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn stable_const_context() { | -error: aborting due to 8 previous errors +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:76:5 + | +LL | Unstable::func(); + | ^^^^^^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn implicitly_stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] +LL | const fn implicitly_stable_const_context() { + | + +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]` + --> $DIR/staged-api.rs:76:5 + | +LL | Unstable::func(); + | ^^^^^^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn implicitly_stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(unstable)] +LL | const fn implicitly_stable_const_context() { + | + +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:79:5 + | +LL | Foo::func(); + | ^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn implicitly_stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] +LL | const fn implicitly_stable_const_context() { + | + +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]` + --> $DIR/staged-api.rs:79:5 + | +LL | Foo::func(); + | ^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn implicitly_stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(unstable)] +LL | const fn implicitly_stable_const_context() { + | + +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(local_feature)]` + --> $DIR/staged-api.rs:82:5 + | +LL | const_context_not_const_stable(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features +help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn implicitly_stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(local_feature)] +LL | const fn implicitly_stable_const_context() { + | + +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:84:5 + | +LL | conditionally_const::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn implicitly_stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] +LL | const fn implicitly_stable_const_context() { + | + +error: aborting due to 19 previous errors diff --git a/tests/ui/traits/const-traits/super-traits-fail-3.nyn.stderr b/tests/ui/traits/const-traits/super-traits-fail-3.nyn.stderr index 8abda1c8f8afe..024db4b6d68d0 100644 --- a/tests/ui/traits/const-traits/super-traits-fail-3.nyn.stderr +++ b/tests/ui/traits/const-traits/super-traits-fail-3.nyn.stderr @@ -39,11 +39,12 @@ LL | #[cfg_attr(any(yyy, yny, nyy, nyn), const_trait)] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: cannot call conditionally-const method `::a` in constant functions - --> $DIR/super-traits-fail-3.rs:36:5 + --> $DIR/super-traits-fail-3.rs:36:7 | LL | x.a(); - | ^^^^^ + | ^^^ | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: see issue #67792 for more information = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date diff --git a/tests/ui/traits/const-traits/super-traits-fail-3.nyy.stderr b/tests/ui/traits/const-traits/super-traits-fail-3.nyy.stderr index 8abda1c8f8afe..024db4b6d68d0 100644 --- a/tests/ui/traits/const-traits/super-traits-fail-3.nyy.stderr +++ b/tests/ui/traits/const-traits/super-traits-fail-3.nyy.stderr @@ -39,11 +39,12 @@ LL | #[cfg_attr(any(yyy, yny, nyy, nyn), const_trait)] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: cannot call conditionally-const method `::a` in constant functions - --> $DIR/super-traits-fail-3.rs:36:5 + --> $DIR/super-traits-fail-3.rs:36:7 | LL | x.a(); - | ^^^^^ + | ^^^ | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants = note: see issue #67792 for more information = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date diff --git a/tests/ui/traits/const-traits/syntactical-unstable.rs b/tests/ui/traits/const-traits/syntactical-unstable.rs new file mode 100644 index 0000000000000..e192e80fabd9e --- /dev/null +++ b/tests/ui/traits/const-traits/syntactical-unstable.rs @@ -0,0 +1,34 @@ +//@ aux-build:staged-api.rs + +// Ensure that we enforce const stability of traits in `~const`/`const` bounds. + +#![feature(const_trait_impl)] + +use std::ops::Deref; + +extern crate staged_api; +use staged_api::MyTrait; + +#[const_trait] +trait Foo: ~const MyTrait { + //~^ ERROR use of unstable const library feature `unstable` + type Item: ~const MyTrait; + //~^ ERROR use of unstable const library feature `unstable` +} + +const fn where_clause() where T: ~const MyTrait {} +//~^ ERROR use of unstable const library feature `unstable` + +const fn nested() where T: Deref {} +//~^ ERROR use of unstable const library feature `unstable` + +const fn rpit() -> impl ~const MyTrait { Local } +//~^ ERROR use of unstable const library feature `unstable` + +struct Local; +impl const MyTrait for Local { +//~^ ERROR use of unstable const library feature `unstable` + fn func() {} +} + +fn main() {} diff --git a/tests/ui/traits/const-traits/syntactical-unstable.stderr b/tests/ui/traits/const-traits/syntactical-unstable.stderr new file mode 100644 index 0000000000000..a2ce2f2b6e9d2 --- /dev/null +++ b/tests/ui/traits/const-traits/syntactical-unstable.stderr @@ -0,0 +1,67 @@ +error[E0658]: use of unstable const library feature `unstable` + --> $DIR/syntactical-unstable.rs:13:19 + | +LL | trait Foo: ~const MyTrait { + | ------ ^^^^^^^ + | | + | trait is not stable as const yet + | + = help: add `#![feature(unstable)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable const library feature `unstable` + --> $DIR/syntactical-unstable.rs:19:44 + | +LL | const fn where_clause() where T: ~const MyTrait {} + | ------ ^^^^^^^ + | | + | trait is not stable as const yet + | + = help: add `#![feature(unstable)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable const library feature `unstable` + --> $DIR/syntactical-unstable.rs:22:52 + | +LL | const fn nested() where T: Deref {} + | ------ ^^^^^^^ + | | + | trait is not stable as const yet + | + = help: add `#![feature(unstable)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable const library feature `unstable` + --> $DIR/syntactical-unstable.rs:25:32 + | +LL | const fn rpit() -> impl ~const MyTrait { Local } + | ------ ^^^^^^^ + | | + | trait is not stable as const yet + | + = help: add `#![feature(unstable)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable const library feature `unstable` + --> $DIR/syntactical-unstable.rs:29:12 + | +LL | impl const MyTrait for Local { + | ^^^^^^^ trait is not stable as const yet + | + = help: add `#![feature(unstable)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable const library feature `unstable` + --> $DIR/syntactical-unstable.rs:15:23 + | +LL | type Item: ~const MyTrait; + | ------ ^^^^^^^ + | | + | trait is not stable as const yet + | + = help: add `#![feature(unstable)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/traits/const-traits/trait-default-body-stability.rs b/tests/ui/traits/const-traits/trait-default-body-stability.rs index 5f7486eb176af..567f1b3c28424 100644 --- a/tests/ui/traits/const-traits/trait-default-body-stability.rs +++ b/tests/ui/traits/const-traits/trait-default-body-stability.rs @@ -38,6 +38,7 @@ impl const FromResidual for T { } #[stable(feature = "foo", since = "1.0")] +#[rustc_const_unstable(feature = "const_tr", issue = "none")] #[const_trait] pub trait Tr { #[stable(feature = "foo", since = "1.0")] diff --git a/tests/ui/traits/const-traits/trait-default-body-stability.stderr b/tests/ui/traits/const-traits/trait-default-body-stability.stderr index 77b81211e8152..a13d9a1e075b2 100644 --- a/tests/ui/traits/const-traits/trait-default-body-stability.stderr +++ b/tests/ui/traits/const-traits/trait-default-body-stability.stderr @@ -17,7 +17,7 @@ LL | impl const FromResidual for T { = note: adding a non-const method body in the future would be a breaking change error[E0015]: `?` is not allowed on `T` in constant functions - --> $DIR/trait-default-body-stability.rs:45:9 + --> $DIR/trait-default-body-stability.rs:46:9 | LL | T? | ^^ @@ -25,7 +25,7 @@ LL | T? = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants error[E0015]: `?` is not allowed on `T` in constant functions - --> $DIR/trait-default-body-stability.rs:45:9 + --> $DIR/trait-default-body-stability.rs:46:9 | LL | T? | ^^ diff --git a/tests/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.stderr b/tests/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.stderr index 460595dd961e0..139488d79f13b 100644 --- a/tests/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.stderr +++ b/tests/ui/traits/do-not-mention-type-params-by-name-in-suggestion-issue-96292.stderr @@ -14,8 +14,9 @@ LL | impl Method for Thing { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | as Method>::method(thing, 42); - | +++++++++++++++++++++++++++++++++++ ~ +LL - thing.method(42); +LL + as Method>::method(thing, 42); + | error: aborting due to 1 previous error diff --git a/tests/ui/traits/fn-pointer/bare-fn-no-impl-fn-ptr-99875.rs b/tests/ui/traits/fn-pointer/bare-fn-no-impl-fn-ptr-99875.rs index cf73fd8d31fbb..f776a6ce4c12f 100644 --- a/tests/ui/traits/fn-pointer/bare-fn-no-impl-fn-ptr-99875.rs +++ b/tests/ui/traits/fn-pointer/bare-fn-no-impl-fn-ptr-99875.rs @@ -1,3 +1,5 @@ +// Sets some arbitrarily large width for more consistent output (see #135288). +//@ compile-flags: --diagnostic-width=120 struct Argument; struct Return; diff --git a/tests/ui/traits/fn-pointer/bare-fn-no-impl-fn-ptr-99875.stderr b/tests/ui/traits/fn-pointer/bare-fn-no-impl-fn-ptr-99875.stderr index 5b89158b0dba2..ba0af763975d4 100644 --- a/tests/ui/traits/fn-pointer/bare-fn-no-impl-fn-ptr-99875.stderr +++ b/tests/ui/traits/fn-pointer/bare-fn-no-impl-fn-ptr-99875.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `fn(Argument) -> Return {function}: Trait` is not satisfied - --> $DIR/bare-fn-no-impl-fn-ptr-99875.rs:12:11 + --> $DIR/bare-fn-no-impl-fn-ptr-99875.rs:14:11 | LL | takes(function); | ----- ^^^^^^^^ the trait `Trait` is not implemented for fn item `fn(Argument) -> Return {function}` @@ -7,7 +7,7 @@ LL | takes(function); | required by a bound introduced by this call | note: required by a bound in `takes` - --> $DIR/bare-fn-no-impl-fn-ptr-99875.rs:9:18 + --> $DIR/bare-fn-no-impl-fn-ptr-99875.rs:11:18 | LL | fn takes(_: impl Trait) {} | ^^^^^ required by this bound in `takes` @@ -16,18 +16,18 @@ help: the trait `Trait` is implemented for fn pointer `fn(Argument) -> Return`, LL | takes(function as fn(Argument) -> Return); | +++++++++++++++++++++++++ -error[E0277]: the trait bound `{closure@$DIR/bare-fn-no-impl-fn-ptr-99875.rs:14:11: 14:34}: Trait` is not satisfied - --> $DIR/bare-fn-no-impl-fn-ptr-99875.rs:14:11 +error[E0277]: the trait bound `{closure@$DIR/bare-fn-no-impl-fn-ptr-99875.rs:16:11: 16:34}: Trait` is not satisfied + --> $DIR/bare-fn-no-impl-fn-ptr-99875.rs:16:11 | LL | takes(|_: Argument| -> Return { todo!() }); | ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound | | | required by a bound introduced by this call | - = help: the trait `Trait` is not implemented for closure `{closure@$DIR/bare-fn-no-impl-fn-ptr-99875.rs:14:11: 14:34}` + = help: the trait `Trait` is not implemented for closure `{closure@$DIR/bare-fn-no-impl-fn-ptr-99875.rs:16:11: 16:34}` = help: the trait `Trait` is implemented for fn pointer `fn(Argument) -> Return` note: required by a bound in `takes` - --> $DIR/bare-fn-no-impl-fn-ptr-99875.rs:9:18 + --> $DIR/bare-fn-no-impl-fn-ptr-99875.rs:11:18 | LL | fn takes(_: impl Trait) {} | ^^^^^ required by this bound in `takes` diff --git a/tests/ui/traits/ice-with-dyn-pointee-errors.stderr b/tests/ui/traits/ice-with-dyn-pointee-errors.stderr index 8bfda71bac1fb..5299236026d7e 100644 --- a/tests/ui/traits/ice-with-dyn-pointee-errors.stderr +++ b/tests/ui/traits/ice-with-dyn-pointee-errors.stderr @@ -2,7 +2,7 @@ error[E0271]: type mismatch resolving ` as Pointee>:: --> $DIR/ice-with-dyn-pointee-errors.rs:9:33 | LL | unknown_sized_object_ptr_in(x) - | --------------------------- ^ expected `()`, found `DynMetadata>` + | --------------------------- ^ expected `()`, found `DynMetadata>` | | | required by a bound introduced by this call | diff --git a/tests/ui/traits/issue-106072.rs b/tests/ui/traits/issue-106072.rs index 696bd765ebc5c..d75c26642c600 100644 --- a/tests/ui/traits/issue-106072.rs +++ b/tests/ui/traits/issue-106072.rs @@ -1,4 +1,6 @@ -#[derive(Clone)] //~ expected a type, found a trait +#[derive(Clone)] +//~^ expected a type, found a trait +//~| expected a type, found a trait struct Foo; trait Foo {} //~ the name `Foo` is defined multiple times fn main() {} diff --git a/tests/ui/traits/issue-106072.stderr b/tests/ui/traits/issue-106072.stderr index 4a48e4e898d31..3e0d6d88086f7 100644 --- a/tests/ui/traits/issue-106072.stderr +++ b/tests/ui/traits/issue-106072.stderr @@ -1,5 +1,5 @@ error[E0428]: the name `Foo` is defined multiple times - --> $DIR/issue-106072.rs:3:1 + --> $DIR/issue-106072.rs:5:1 | LL | struct Foo; | ----------- previous definition of the type `Foo` here @@ -16,7 +16,16 @@ LL | #[derive(Clone)] | = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 2 previous errors +error[E0782]: expected a type, found a trait + --> $DIR/issue-106072.rs:1:10 + | +LL | #[derive(Clone)] + | ^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors Some errors have detailed explanations: E0428, E0782. For more information about an error, try `rustc --explain E0428`. diff --git a/tests/ui/traits/issue-20692.rs b/tests/ui/traits/issue-20692.rs index 1cb2d8c7302a0..10611a232f71c 100644 --- a/tests/ui/traits/issue-20692.rs +++ b/tests/ui/traits/issue-20692.rs @@ -2,10 +2,10 @@ trait Array: Sized + Copy {} fn f(x: &T) { let _ = x - //~^ ERROR `Array` cannot be made into an object + //~^ ERROR `Array` is not dyn compatible as &dyn Array; - //~^ ERROR `Array` cannot be made into an object + //~^ ERROR `Array` is not dyn compatible } fn main() {} diff --git a/tests/ui/traits/issue-20692.stderr b/tests/ui/traits/issue-20692.stderr index 5e6a967fdc4e9..32e29de49a113 100644 --- a/tests/ui/traits/issue-20692.stderr +++ b/tests/ui/traits/issue-20692.stderr @@ -1,32 +1,34 @@ -error[E0038]: the trait `Array` cannot be made into an object +error[E0038]: the trait `Array` is not dyn compatible --> $DIR/issue-20692.rs:7:5 | LL | &dyn Array; - | ^^^^^^^^^^ `Array` cannot be made into an object + | ^^^^^^^^^^ `Array` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-20692.rs:1:14 | LL | trait Array: Sized + Copy {} | ----- ^^^^^ ^^^^ ...because it requires `Self: Sized` | | | | | ...because it requires `Self: Sized` - | this trait cannot be made into an object... + | this trait is not dyn compatible... -error[E0038]: the trait `Array` cannot be made into an object +error[E0038]: the trait `Array` is not dyn compatible --> $DIR/issue-20692.rs:4:13 | LL | let _ = x - | ^ `Array` cannot be made into an object + | ^ `Array` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-20692.rs:1:14 | LL | trait Array: Sized + Copy {} | ----- ^^^^^ ^^^^ ...because it requires `Self: Sized` | | | | | ...because it requires `Self: Sized` - | this trait cannot be made into an object... + | this trait is not dyn compatible... = note: required for the cast from `&T` to `&dyn Array` error: aborting due to 2 previous errors diff --git a/tests/ui/traits/issue-28576.rs b/tests/ui/traits/issue-28576.rs index e19bd2635813e..fd026044401b6 100644 --- a/tests/ui/traits/issue-28576.rs +++ b/tests/ui/traits/issue-28576.rs @@ -6,7 +6,7 @@ pub trait Bar: Foo { //~^ ERROR: the size for values of type `Self` cannot be known //~| ERROR: the size for values of type `Self` cannot be known fn new(&self, b: & - dyn Bar //~ ERROR the trait `Bar` cannot be made into an object + dyn Bar //~ ERROR the trait `Bar` is not dyn compatible ); } diff --git a/tests/ui/traits/issue-28576.stderr b/tests/ui/traits/issue-28576.stderr index 23581f2ee51aa..50d7f8c56b25b 100644 --- a/tests/ui/traits/issue-28576.stderr +++ b/tests/ui/traits/issue-28576.stderr @@ -18,14 +18,16 @@ help: consider relaxing the implicit `Sized` restriction LL | pub trait Foo { | ++++++++ -error[E0038]: the trait `Bar` cannot be made into an object - --> $DIR/issue-28576.rs:9:12 +error[E0038]: the trait `Bar` is not dyn compatible + --> $DIR/issue-28576.rs:9:16 | -LL | / dyn Bar +LL | dyn Bar + | ________________^ LL | | - | |________________________^ `Bar` cannot be made into an object + | |________________________^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-28576.rs:5:16 | LL | pub trait Bar: Foo { @@ -33,11 +35,12 @@ LL | pub trait Bar: Foo { | | | | | | | ...because it uses `Self` as a type parameter | | ...because it uses `Self` as a type parameter - | this trait cannot be made into an object... + | this trait is not dyn compatible... help: consider using an opaque type instead | -LL | impl Bar - | ~~~~ +LL - dyn Bar +LL + impl Bar + | error[E0277]: the size for values of type `Self` cannot be known at compilation time --> $DIR/issue-28576.rs:5:16 diff --git a/tests/ui/traits/issue-35869.stderr b/tests/ui/traits/issue-35869.stderr index 503f9cee246e9..73cf961785433 100644 --- a/tests/ui/traits/issue-35869.stderr +++ b/tests/ui/traits/issue-35869.stderr @@ -13,8 +13,9 @@ LL | fn foo(_: fn(u8) -> ()); found signature `fn(fn(u16))` help: change the parameter type to match the trait | -LL | fn foo(_: fn(u8)) {} - | ~~~~~~ +LL - fn foo(_: fn(u16) -> ()) {} +LL + fn foo(_: fn(u8)) {} + | error[E0053]: method `bar` has an incompatible type for trait --> $DIR/issue-35869.rs:13:15 @@ -31,8 +32,9 @@ LL | fn bar(_: Option); found signature `fn(Option)` help: change the parameter type to match the trait | -LL | fn bar(_: Option) {} - | ~~~~~~~~~~ +LL - fn bar(_: Option) {} +LL + fn bar(_: Option) {} + | error[E0053]: method `baz` has an incompatible type for trait --> $DIR/issue-35869.rs:15:15 @@ -49,8 +51,9 @@ LL | fn baz(_: (u8, u16)); found signature `fn((u16, _))` help: change the parameter type to match the trait | -LL | fn baz(_: (u8, u16)) {} - | ~~~~~~~~~ +LL - fn baz(_: (u16, u16)) {} +LL + fn baz(_: (u8, u16)) {} + | error[E0053]: method `qux` has an incompatible type for trait --> $DIR/issue-35869.rs:17:17 @@ -67,8 +70,9 @@ LL | fn qux() -> u8; found signature `fn() -> u16` help: change the output type to match the trait | -LL | fn qux() -> u8 { 5u16 } - | ~~ +LL - fn qux() -> u16 { 5u16 } +LL + fn qux() -> u8 { 5u16 } + | error: aborting due to 4 previous errors diff --git a/tests/ui/traits/issue-38404.rs b/tests/ui/traits/issue-38404.rs index 9b60116f733dc..36da594c01588 100644 --- a/tests/ui/traits/issue-38404.rs +++ b/tests/ui/traits/issue-38404.rs @@ -1,8 +1,8 @@ trait A: std::ops::Add + Sized {} trait B: A {} trait C: A> {} -//~^ ERROR the trait `B` cannot be made into an object -//~| ERROR the trait `B` cannot be made into an object -//~| ERROR the trait `B` cannot be made into an object +//~^ ERROR the trait `B` is not dyn compatible +//~| ERROR the trait `B` is not dyn compatible +//~| ERROR the trait `B` is not dyn compatible fn main() {} diff --git a/tests/ui/traits/issue-38404.stderr b/tests/ui/traits/issue-38404.stderr index 145eeb88dd5e7..63269d3379ed3 100644 --- a/tests/ui/traits/issue-38404.stderr +++ b/tests/ui/traits/issue-38404.stderr @@ -1,45 +1,48 @@ -error[E0038]: the trait `B` cannot be made into an object - --> $DIR/issue-38404.rs:3:15 +error[E0038]: the trait `B` is not dyn compatible + --> $DIR/issue-38404.rs:3:19 | LL | trait C: A> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^ `B` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^ `B` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-38404.rs:1:13 | LL | trait A: std::ops::Add + Sized {} | ^^^^^^^^^^^^^^^^^^^ ...because it uses `Self` as a type parameter LL | trait B: A {} - | - this trait cannot be made into an object... + | - this trait is not dyn compatible... -error[E0038]: the trait `B` cannot be made into an object - --> $DIR/issue-38404.rs:3:15 +error[E0038]: the trait `B` is not dyn compatible + --> $DIR/issue-38404.rs:3:19 | LL | trait C: A> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^ `B` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^ `B` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-38404.rs:1:13 | LL | trait A: std::ops::Add + Sized {} | ^^^^^^^^^^^^^^^^^^^ ...because it uses `Self` as a type parameter LL | trait B: A {} - | - this trait cannot be made into an object... + | - this trait is not dyn compatible... = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0038]: the trait `B` cannot be made into an object - --> $DIR/issue-38404.rs:3:15 +error[E0038]: the trait `B` is not dyn compatible + --> $DIR/issue-38404.rs:3:19 | LL | trait C: A> {} - | ^^^^^^^^^^^^^^^^^^^^^^^^ `B` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^ `B` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-38404.rs:1:13 | LL | trait A: std::ops::Add + Sized {} | ^^^^^^^^^^^^^^^^^^^ ...because it uses `Self` as a type parameter LL | trait B: A {} - | - this trait cannot be made into an object... + | - this trait is not dyn compatible... = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 3 previous errors diff --git a/tests/ui/traits/issue-38604.rs b/tests/ui/traits/issue-38604.rs index 002a3c43fcba6..d90aa61ef9f33 100644 --- a/tests/ui/traits/issue-38604.rs +++ b/tests/ui/traits/issue-38604.rs @@ -11,6 +11,6 @@ impl Foo for () { } fn main() { - let _f: Box = //~ ERROR `Foo` cannot be made into an object - Box::new(()); //~ ERROR `Foo` cannot be made into an object + let _f: Box = //~ ERROR `Foo` is not dyn compatible + Box::new(()); //~ ERROR `Foo` is not dyn compatible } diff --git a/tests/ui/traits/issue-38604.stderr b/tests/ui/traits/issue-38604.stderr index 5c788b0c85d19..e6a6b44e7304d 100644 --- a/tests/ui/traits/issue-38604.stderr +++ b/tests/ui/traits/issue-38604.stderr @@ -1,32 +1,34 @@ -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/issue-38604.rs:14:13 | LL | let _f: Box = - | ^^^^^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-38604.rs:2:22 | LL | trait Foo where u32: Q { | --- ^^^^^^^ ...because it uses `Self` as a type parameter | | - | this trait cannot be made into an object... - = help: only type `()` implements the trait, consider using it directly instead + | this trait is not dyn compatible... + = help: only type `()` implements `Foo`; consider using it directly instead. -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/issue-38604.rs:15:9 | LL | Box::new(()); - | ^^^^^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-38604.rs:2:22 | LL | trait Foo where u32: Q { | --- ^^^^^^^ ...because it uses `Self` as a type parameter | | - | this trait cannot be made into an object... - = help: only type `()` implements the trait, consider using it directly instead + | this trait is not dyn compatible... + = help: only type `()` implements `Foo`; consider using it directly instead. = note: required for the cast from `Box<()>` to `Box` error: aborting due to 2 previous errors diff --git a/tests/ui/traits/issue-50480.stderr b/tests/ui/traits/issue-50480.stderr index 330b23b5755d7..d3c11238ede16 100644 --- a/tests/ui/traits/issue-50480.stderr +++ b/tests/ui/traits/issue-50480.stderr @@ -47,8 +47,9 @@ LL | struct Bar(T, N, NotDefined, ::Item, Vec, String); | help: a type parameter with a similar name exists | -LL | struct Bar(T, T, NotDefined, ::Item, Vec, String); - | ~ +LL - struct Bar(T, N, NotDefined, ::Item, Vec, String); +LL + struct Bar(T, T, NotDefined, ::Item, Vec, String); + | help: you might be missing a type parameter | LL | struct Bar(T, N, NotDefined, ::Item, Vec, String); diff --git a/tests/ui/traits/issue-52893.stderr b/tests/ui/traits/issue-52893.stderr index c37dde90e336f..3c5df82fcdc87 100644 --- a/tests/ui/traits/issue-52893.stderr +++ b/tests/ui/traits/issue-52893.stderr @@ -22,7 +22,7 @@ note: method defined here --> $DIR/issue-52893.rs:11:8 | LL | fn push(self, other: T) -> Self::PushRes; - | ^^^^ + | ^^^^ ----- error: aborting due to 1 previous error diff --git a/tests/ui/traits/issue-72410.rs b/tests/ui/traits/issue-72410.rs index c95f1dfdca53a..df3738e2730d9 100644 --- a/tests/ui/traits/issue-72410.rs +++ b/tests/ui/traits/issue-72410.rs @@ -12,7 +12,7 @@ pub trait Foo { pub trait Bar { fn map() where for<'a> &'a mut [dyn Bar]: ; - //~^ ERROR: the trait `Bar` cannot be made into an object + //~^ ERROR: the trait `Bar` is not dyn compatible } fn main() {} diff --git a/tests/ui/traits/issue-72410.stderr b/tests/ui/traits/issue-72410.stderr index 6d56a198fc1c0..c9e133437dd86 100644 --- a/tests/ui/traits/issue-72410.stderr +++ b/tests/ui/traits/issue-72410.stderr @@ -1,14 +1,15 @@ -error[E0038]: the trait `Bar` cannot be made into an object +error[E0038]: the trait `Bar` is not dyn compatible --> $DIR/issue-72410.rs:14:19 | LL | where for<'a> &'a mut [dyn Bar]: ; - | ^^^^^^^^^^^^^^^^^ `Bar` cannot be made into an object + | ^^^^^^^^^^^^^^^^^ `Bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-72410.rs:13:8 | LL | pub trait Bar { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn map() | ^^^ ...because associated function `map` has no `self` parameter help: consider turning `map` into a method by giving it a `&self` argument diff --git a/tests/ui/traits/issue-77982.rs b/tests/ui/traits/issue-77982.rs index dce25e62e468b..b2a488e18e828 100644 --- a/tests/ui/traits/issue-77982.rs +++ b/tests/ui/traits/issue-77982.rs @@ -1,5 +1,6 @@ -//@ ignore-windows different list of satisfying impls -//@ ignore-arm different list of satisfying impls +//@ ignore-windows FIXME: We get an extra E0283 on Windows +//@ ignore-arm extra satisfying impls + use std::collections::HashMap; fn what() { diff --git a/tests/ui/traits/issue-77982.stderr b/tests/ui/traits/issue-77982.stderr index 2b26a1b7ab15a..edc7f56ea467b 100644 --- a/tests/ui/traits/issue-77982.stderr +++ b/tests/ui/traits/issue-77982.stderr @@ -1,5 +1,5 @@ error[E0283]: type annotations needed - --> $DIR/issue-77982.rs:10:10 + --> $DIR/issue-77982.rs:11:10 | LL | opts.get(opt.as_ref()); | ^^^ ------------ type must be known at this point @@ -18,7 +18,7 @@ LL | opts.get::(opt.as_ref()); | +++++ error[E0283]: type annotations needed - --> $DIR/issue-77982.rs:10:10 + --> $DIR/issue-77982.rs:11:10 | LL | opts.get(opt.as_ref()); | ^^^ ------ type must be known at this point @@ -36,7 +36,7 @@ LL | opts.get::(opt.as_ref()); | +++++ error[E0283]: type annotations needed - --> $DIR/issue-77982.rs:15:59 + --> $DIR/issue-77982.rs:16:59 | LL | let ips: Vec<_> = (0..100_000).map(|_| u32::from(0u32.into())).collect(); | --- ^^^^ @@ -52,17 +52,18 @@ LL | let ips: Vec<_> = (0..100_000).map(|_| u32::from(0u32.into())).collect( - impl From for u32; help: try using a fully qualified path to specify the expected types | -LL | let ips: Vec<_> = (0..100_000).map(|_| u32::from(>::into(0u32))).collect(); - | +++++++++++++++++++++++ ~ +LL - let ips: Vec<_> = (0..100_000).map(|_| u32::from(0u32.into())).collect(); +LL + let ips: Vec<_> = (0..100_000).map(|_| u32::from(>::into(0u32))).collect(); + | error[E0283]: type annotations needed for `Box<_>` - --> $DIR/issue-77982.rs:38:9 + --> $DIR/issue-77982.rs:39:9 | LL | let _ = ().foo(); | ^ --- type must be known at this point | note: multiple `impl`s satisfying `(): Foo<'_, _>` found - --> $DIR/issue-77982.rs:31:1 + --> $DIR/issue-77982.rs:32:1 | LL | impl Foo<'static, u32> for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -74,13 +75,13 @@ LL | let _: Box = ().foo(); | ++++++++ error[E0283]: type annotations needed for `Box<_>` - --> $DIR/issue-77982.rs:42:9 + --> $DIR/issue-77982.rs:43:9 | LL | let _ = (&()).bar(); | ^ --- type must be known at this point | note: multiple `impl`s satisfying `&(): Bar<'_, _>` found - --> $DIR/issue-77982.rs:34:1 + --> $DIR/issue-77982.rs:35:1 | LL | impl<'a> Bar<'static, u32> for &'a () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/traits/issue-78372.stderr b/tests/ui/traits/issue-78372.stderr index 86234d15a5d4b..d4dfba4f039f6 100644 --- a/tests/ui/traits/issue-78372.stderr +++ b/tests/ui/traits/issue-78372.stderr @@ -19,8 +19,9 @@ LL | impl DispatchFromDyn> for T {} | help: a type parameter with a similar name exists | -LL | impl DispatchFromDyn> for T {} - | ~ +LL - impl DispatchFromDyn> for T {} +LL + impl DispatchFromDyn> for T {} + | help: you might be missing a type parameter | LL | impl DispatchFromDyn> for T {} diff --git a/tests/ui/traits/item-privacy.rs b/tests/ui/traits/item-privacy.rs index a3e1a22e7a83d..cdfd667a6f12e 100644 --- a/tests/ui/traits/item-privacy.rs +++ b/tests/ui/traits/item-privacy.rs @@ -98,9 +98,12 @@ fn check_assoc_const() { S::B; //~ ERROR no associated item named `B` found S::C; // OK // A, B, C are resolved as inherent items, their traits don't need to be in scope - ::A; //~ ERROR associated constant `A` is private - //~^ ERROR the trait `assoc_const::C` cannot be made into an object - ::B; // ERROR the trait `assoc_const::C` cannot be made into an object + ::A; + //~^ ERROR associated constant `A` is private + //~| ERROR the trait `assoc_const::C` is not dyn compatible + //~| ERROR the trait `assoc_const::C` is not dyn compatible + ::B; + //~^ ERROR the trait `assoc_const::C` is not dyn compatible C::C; // OK } diff --git a/tests/ui/traits/item-privacy.stderr b/tests/ui/traits/item-privacy.stderr index c20d2f723c598..4fd9ef9119257 100644 --- a/tests/ui/traits/item-privacy.stderr +++ b/tests/ui/traits/item-privacy.stderr @@ -11,8 +11,9 @@ LL | S.a(); = help: trait `method::A` which provides `a` is implemented but not reachable help: there is a method `b` with a similar name | -LL | S.b(); - | ~ +LL - S.a(); +LL + S.b(); + | error[E0599]: no method named `b` found for struct `S` in the current scope --> $DIR/item-privacy.rs:68:7 @@ -33,8 +34,9 @@ LL + use method::B; | help: there is a method `c` with a similar name | -LL | S.c(); - | ~ +LL - S.b(); +LL + S.c(); + | error[E0624]: method `a` is private --> $DIR/item-privacy.rs:72:7 @@ -104,8 +106,9 @@ LL | S::A; = help: trait `assoc_const::A` which provides `A` is implemented but not reachable help: there is an associated constant `B` with a similar name | -LL | S::B; - | ~ +LL - S::A; +LL + S::B; + | error[E0599]: no associated item named `B` found for struct `S` in the current scope --> $DIR/item-privacy.rs:98:8 @@ -127,6 +130,31 @@ help: trait `B` which provides `B` is implemented but not in scope; perhaps you LL + use assoc_const::B; | +error[E0038]: the trait `assoc_const::C` is not dyn compatible + --> $DIR/item-privacy.rs:101:6 + | +LL | ::A; + | ^^^^^ `assoc_const::C` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/item-privacy.rs:25:15 + | +LL | const A: u8 = 0; + | ^ ...because it contains this associated `const` +... +LL | const B: u8 = 0; + | ^ ...because it contains this associated `const` +... +LL | pub trait C: A + B { + | - this trait is not dyn compatible... +LL | const C: u8 = 0; + | ^ ...because it contains this associated `const` + = help: consider moving `C` to another trait + = help: consider moving `A` to another trait + = help: consider moving `B` to another trait + = help: only type `S` implements `assoc_const::C`; consider using it directly instead. + error[E0624]: associated constant `A` is private --> $DIR/item-privacy.rs:101:14 | @@ -136,13 +164,39 @@ LL | const A: u8 = 0; LL | ::A; | ^ private associated constant -error[E0038]: the trait `assoc_const::C` cannot be made into an object - --> $DIR/item-privacy.rs:101:6 +error[E0038]: the trait `assoc_const::C` is not dyn compatible + --> $DIR/item-privacy.rs:101:5 | LL | ::A; - | ^^^^^ `assoc_const::C` cannot be made into an object + | ^^^^^^^^^^ `assoc_const::C` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/item-privacy.rs:25:15 + | +LL | const A: u8 = 0; + | ^ ...because it contains this associated `const` +... +LL | const B: u8 = 0; + | ^ ...because it contains this associated `const` +... +LL | pub trait C: A + B { + | - this trait is not dyn compatible... +LL | const C: u8 = 0; + | ^ ...because it contains this associated `const` + = help: consider moving `C` to another trait + = help: consider moving `A` to another trait + = help: consider moving `B` to another trait + = help: only type `S` implements `assoc_const::C`; consider using it directly instead. + +error[E0038]: the trait `assoc_const::C` is not dyn compatible + --> $DIR/item-privacy.rs:105:5 + | +LL | ::B; + | ^^^^^^^^^^ `assoc_const::C` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/item-privacy.rs:25:15 | LL | const A: u8 = 0; @@ -152,39 +206,40 @@ LL | const B: u8 = 0; | ^ ...because it contains this associated `const` ... LL | pub trait C: A + B { - | - this trait cannot be made into an object... + | - this trait is not dyn compatible... LL | const C: u8 = 0; | ^ ...because it contains this associated `const` = help: consider moving `C` to another trait = help: consider moving `A` to another trait = help: consider moving `B` to another trait - = help: only type `S` implements the trait, consider using it directly instead + = help: only type `S` implements `assoc_const::C`; consider using it directly instead. error[E0223]: ambiguous associated type - --> $DIR/item-privacy.rs:115:12 + --> $DIR/item-privacy.rs:118:12 | LL | let _: S::A; | ^^^^ | help: if there were a trait named `Example` with associated type `A` implemented for `S`, you could use the fully-qualified path | -LL | let _: ::A; - | ~~~~~~~~~~~~~~~~~ +LL - let _: S::A; +LL + let _: ::A; + | error[E0223]: ambiguous associated type - --> $DIR/item-privacy.rs:116:12 + --> $DIR/item-privacy.rs:119:12 | LL | let _: S::B; | ^^^^ help: use fully-qualified syntax: `::B` error[E0223]: ambiguous associated type - --> $DIR/item-privacy.rs:117:12 + --> $DIR/item-privacy.rs:120:12 | LL | let _: S::C; | ^^^^ help: use fully-qualified syntax: `::C` error[E0624]: associated type `A` is private - --> $DIR/item-privacy.rs:119:12 + --> $DIR/item-privacy.rs:122:12 | LL | type A = u8; | ------ the associated type is defined here @@ -193,7 +248,7 @@ LL | let _: T::A; | ^^^^ private associated type error[E0624]: associated type `A` is private - --> $DIR/item-privacy.rs:128:9 + --> $DIR/item-privacy.rs:131:9 | LL | type A = u8; | ------ the associated type is defined here @@ -201,7 +256,7 @@ LL | type A = u8; LL | A = u8, | ^^^^^^ private associated type -error: aborting due to 15 previous errors +error: aborting due to 17 previous errors Some errors have detailed explanations: E0038, E0223, E0599, E0624. For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/traits/maybe-polarity-pass.rs b/tests/ui/traits/maybe-polarity-pass.rs index 075a0d8dcac4d..1ccd52bc1694a 100644 --- a/tests/ui/traits/maybe-polarity-pass.rs +++ b/tests/ui/traits/maybe-polarity-pass.rs @@ -1,5 +1,3 @@ -//@ check-pass - #![feature(auto_traits)] #![feature(more_maybe_bounds)] #![feature(negative_impls)] @@ -12,9 +10,9 @@ trait Trait4 where Self: Trait1 {} fn foo(_: Box<(dyn Trait3 + ?Trait2)>) {} fn bar(_: &T) {} -//~^ WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default -//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default -//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +//~^ ERROR relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +//~| ERROR relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +//~| ERROR relaxing a default bound only does something for `?Sized`; all other traits are not bound by default struct S; impl !Trait2 for S {} diff --git a/tests/ui/traits/maybe-polarity-pass.stderr b/tests/ui/traits/maybe-polarity-pass.stderr index 09ed52f5b0dd7..1f378dd665ae6 100644 --- a/tests/ui/traits/maybe-polarity-pass.stderr +++ b/tests/ui/traits/maybe-polarity-pass.stderr @@ -1,20 +1,20 @@ -warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default - --> $DIR/maybe-polarity-pass.rs:14:20 +error: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/maybe-polarity-pass.rs:12:20 | LL | fn bar(_: &T) {} | ^^^^^^^ -warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default - --> $DIR/maybe-polarity-pass.rs:14:30 +error: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/maybe-polarity-pass.rs:12:30 | LL | fn bar(_: &T) {} | ^^^^^^^ -warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default - --> $DIR/maybe-polarity-pass.rs:14:40 +error: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/maybe-polarity-pass.rs:12:40 | LL | fn bar(_: &T) {} | ^^^^^^^ -warning: 3 warnings emitted +error: aborting due to 3 previous errors diff --git a/tests/ui/traits/maybe-polarity-repeated.rs b/tests/ui/traits/maybe-polarity-repeated.rs index 4b5ec83fffab0..fd1ef567b3eab 100644 --- a/tests/ui/traits/maybe-polarity-repeated.rs +++ b/tests/ui/traits/maybe-polarity-repeated.rs @@ -3,7 +3,7 @@ trait Trait {} fn foo(_: T) {} //~^ ERROR type parameter has more than one relaxed default bound, only one is supported -//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default -//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +//~| ERROR relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +//~| ERROR relaxing a default bound only does something for `?Sized`; all other traits are not bound by default fn main() {} diff --git a/tests/ui/traits/maybe-polarity-repeated.stderr b/tests/ui/traits/maybe-polarity-repeated.stderr index 610c484fbec59..4fa1dc45bda85 100644 --- a/tests/ui/traits/maybe-polarity-repeated.stderr +++ b/tests/ui/traits/maybe-polarity-repeated.stderr @@ -4,18 +4,18 @@ error[E0203]: type parameter has more than one relaxed default bound, only one i LL | fn foo(_: T) {} | ^^^^^^ ^^^^^^ -warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +error: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default --> $DIR/maybe-polarity-repeated.rs:4:11 | LL | fn foo(_: T) {} | ^^^^^^ -warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +error: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default --> $DIR/maybe-polarity-repeated.rs:4:20 | LL | fn foo(_: T) {} | ^^^^^^ -error: aborting due to 1 previous error; 2 warnings emitted +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0203`. diff --git a/tests/ui/traits/method-argument-mismatch-variance-ice-119867.stderr b/tests/ui/traits/method-argument-mismatch-variance-ice-119867.stderr index e63cc522dd13a..d535c39639f2a 100644 --- a/tests/ui/traits/method-argument-mismatch-variance-ice-119867.stderr +++ b/tests/ui/traits/method-argument-mismatch-variance-ice-119867.stderr @@ -6,8 +6,9 @@ LL | fn deserialize(s: _) {} | help: try replacing `_` with the type in the corresponding trait method signature | -LL | fn deserialize(s: &ArchivedVec) {} - | ~~~~~~~~~~~~~~~ +LL - fn deserialize(s: _) {} +LL + fn deserialize(s: &ArchivedVec) {} + | error[E0186]: method `deserialize` has a `&self` declaration in the trait, but not in the impl --> $DIR/method-argument-mismatch-variance-ice-119867.rs:8:5 diff --git a/tests/ui/traits/missing-for-type-in-impl.e2015.stderr b/tests/ui/traits/missing-for-type-in-impl.e2015.stderr index 541b49b024fda..c8a1329e3d0f6 100644 --- a/tests/ui/traits/missing-for-type-in-impl.e2015.stderr +++ b/tests/ui/traits/missing-for-type-in-impl.e2015.stderr @@ -34,17 +34,18 @@ help: you might have intended to implement this trait for a given type LL | impl Foo for /* Type */ { | ++++++++++++++ -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/missing-for-type-in-impl.rs:8:6 | LL | impl Foo { - | ^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/missing-for-type-in-impl.rs:4:8 | LL | trait Foo { - | --- this trait cannot be made into an object... + | --- this trait is not dyn compatible... LL | fn id(me: T) -> T; | ^^ ...because associated function `id` has no `self` parameter help: consider turning `id` into a method by giving it a `&self` argument diff --git a/tests/ui/traits/missing-for-type-in-impl.e2021.stderr b/tests/ui/traits/missing-for-type-in-impl.e2021.stderr index a49c5d9d45b7b..e79bb0524e93a 100644 --- a/tests/ui/traits/missing-for-type-in-impl.e2021.stderr +++ b/tests/ui/traits/missing-for-type-in-impl.e2021.stderr @@ -1,15 +1,3 @@ -error[E0277]: the trait bound `i64: Foo` is not satisfied - --> $DIR/missing-for-type-in-impl.rs:19:19 - | -LL | let x: i64 = >::id(10); - | ^^^ the trait `Foo` is not implemented for `i64` - | -help: this trait has no implementations, consider adding one - --> $DIR/missing-for-type-in-impl.rs:3:1 - | -LL | trait Foo { - | ^^^^^^^^^^^^ - error[E0782]: expected a type, found a trait --> $DIR/missing-for-type-in-impl.rs:8:6 | @@ -25,6 +13,18 @@ help: you might have intended to implement this trait for a given type LL | impl Foo for /* Type */ { | ++++++++++++++ +error[E0277]: the trait bound `i64: Foo` is not satisfied + --> $DIR/missing-for-type-in-impl.rs:19:19 + | +LL | let x: i64 = >::id(10); + | ^^^ the trait `Foo` is not implemented for `i64` + | +help: this trait has no implementations, consider adding one + --> $DIR/missing-for-type-in-impl.rs:3:1 + | +LL | trait Foo { + | ^^^^^^^^^^^^ + error: aborting due to 2 previous errors Some errors have detailed explanations: E0277, E0782. diff --git a/tests/ui/traits/missing-for-type-in-impl.rs b/tests/ui/traits/missing-for-type-in-impl.rs index e5dd365160984..e8163954274f2 100644 --- a/tests/ui/traits/missing-for-type-in-impl.rs +++ b/tests/ui/traits/missing-for-type-in-impl.rs @@ -11,7 +11,7 @@ impl Foo { //[e2015]~| WARNING trait objects without an explicit `dyn` are deprecated //[e2015]~| WARNING this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! //[e2015]~| WARNING this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! -//[e2015]~| ERROR the trait `Foo` cannot be made into an object +//[e2015]~| ERROR the trait `Foo` is not dyn compatible fn id(me: i64) -> i64 {me} } diff --git a/tests/ui/traits/multidispatch-bad.stderr b/tests/ui/traits/multidispatch-bad.stderr index 0bb095fb0e13f..f605459ca8c62 100644 --- a/tests/ui/traits/multidispatch-bad.stderr +++ b/tests/ui/traits/multidispatch-bad.stderr @@ -13,8 +13,9 @@ LL | fn test(_: T, _: U) | ^^^^ ---- help: change the type of the numeric literal from `i32` to `u32` | -LL | test(22i32, 44u32); - | ~~~ +LL - test(22i32, 44i32); +LL + test(22i32, 44u32); + | error: aborting due to 1 previous error diff --git a/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr b/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr index 377dfc8b52916..425f2d59222e0 100644 --- a/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr +++ b/tests/ui/traits/next-solver/canonical/const-region-infer-to-static-in-binder.stderr @@ -1,8 +1,8 @@ -error[E0284]: type annotations needed: cannot normalize `X::{constant#0}` +error[E0284]: type annotations needed: cannot satisfy `the constant `{ || {} }` can be evaluated` --> $DIR/const-region-infer-to-static-in-binder.rs:4:10 | LL | struct X; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot normalize `X::{constant#0}` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `the constant `{ || {} }` can be evaluated` error: using function pointers as const generic parameters is forbidden --> $DIR/const-region-infer-to-static-in-binder.rs:4:20 diff --git a/tests/ui/traits/next-solver/closure-signature-inference-hr-ambig-alias-naming-self.rs b/tests/ui/traits/next-solver/closure-signature-inference-hr-ambig-alias-naming-self.rs new file mode 100644 index 0000000000000..25649d9290326 --- /dev/null +++ b/tests/ui/traits/next-solver/closure-signature-inference-hr-ambig-alias-naming-self.rs @@ -0,0 +1,52 @@ +//@ check-pass +//@ revisions: current next +//@[next] compile-flags: -Znext-solver + +// When type checking a closure expr we look at the list of unsolved goals +// to determine if there are any bounds on the closure type to infer a signature from. +// +// We attempt to discard goals that name the closure type so as to avoid inferring the +// closure type to something like `?x = closure(sig=fn(?x))`. This test checks that when +// such a goal names the closure type inside of an ambiguous alias and there exists another +// potential goal to infer the closure signature from, we do that. + +trait Trait<'a> { + type Assoc; +} + +impl<'a, F> Trait<'a> for F { + type Assoc = u32; +} + +fn closure_typer1(_: F) +where + F: Fn(u32) + for<'a> Fn(>::Assoc), +{ +} + +fn closure_typer2(_: F) +where + F: for<'a> Fn(>::Assoc) + Fn(u32), +{ +} + +fn main() { + // Here we have some closure with a yet to be inferred type of `?c`. There are two goals + // involving `?c` that can be used to determine the closure signature: + // - `?c: for<'a> Fn<(>::Assoc,), Output = ()>` + // - `?c: Fn<(u32,), Output = ()>` + // + // If we were to infer the argument of the closure (`x` below) to `>::Assoc` + // then we would not be able to call `x.into()` as `x` is some unknown type. Instead we must + // use the `?c: Fn(u32)` goal to infer a signature in order for this code to compile. + // + // As the algorithm for picking a goal to infer the signature from is dependent on the ordering + // of pending goals in the type checker, we test both orderings of bounds to ensure we aren't + // testing that we just *happen* to pick `?c: Fn(u32)`. + closure_typer1(move |x| { + let _: u32 = x.into(); + }); + closure_typer2(move |x| { + let _: u32 = x.into(); + }); +} diff --git a/tests/ui/traits/next-solver/coherence/ambiguity-causes-visitor-hang.rs b/tests/ui/traits/next-solver/coherence/ambiguity-causes-visitor-hang.rs new file mode 100644 index 0000000000000..54854b1b8a522 --- /dev/null +++ b/tests/ui/traits/next-solver/coherence/ambiguity-causes-visitor-hang.rs @@ -0,0 +1,56 @@ +// Computing the ambiguity causes for the overlap ended up +// causing an exponential blowup when recursing into the normalization +// goals for ` as RecursiveSuper>::Assoc`. This test +// takes multiple minutes when doing so and less than a second +// otherwise. + +//@ compile-flags: -Znext-solver=coherence + +trait RecursiveSuper: + Super< + A0 = Self::Assoc, + A1 = Self::Assoc, + A2 = Self::Assoc, + A3 = Self::Assoc, + A4 = Self::Assoc, + A5 = Self::Assoc, + A6 = Self::Assoc, + A7 = Self::Assoc, + A8 = Self::Assoc, + A9 = Self::Assoc, + A10 = Self::Assoc, + A11 = Self::Assoc, + A12 = Self::Assoc, + A13 = Self::Assoc, + A14 = Self::Assoc, + A15 = Self::Assoc, + > +{ + type Assoc; +} + +trait Super { + type A0; + type A1; + type A2; + type A3; + type A4; + type A5; + type A6; + type A7; + type A8; + type A9; + type A10; + type A11; + type A12; + type A13; + type A14; + type A15; +} + +trait Overlap {} +impl Overlap for T {} +impl Overlap for Box {} +//~^ ERROR conflicting implementations of trait `Overlap` for type `Box<_>` + +fn main() {} diff --git a/tests/ui/traits/next-solver/coherence/ambiguity-causes-visitor-hang.stderr b/tests/ui/traits/next-solver/coherence/ambiguity-causes-visitor-hang.stderr new file mode 100644 index 0000000000000..3731dc5b74ec0 --- /dev/null +++ b/tests/ui/traits/next-solver/coherence/ambiguity-causes-visitor-hang.stderr @@ -0,0 +1,14 @@ +error[E0119]: conflicting implementations of trait `Overlap` for type `Box<_>` + --> $DIR/ambiguity-causes-visitor-hang.rs:53:1 + | +LL | impl Overlap for T {} + | ------------------------------------- first implementation here +LL | impl Overlap for Box {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Box<_>` + | + = note: downstream crates may implement trait `Super` for type `std::boxed::Box<_>` + = note: downstream crates may implement trait `RecursiveSuper` for type `std::boxed::Box<_>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/traits/next-solver/coherence/issue-102048.next.stderr b/tests/ui/traits/next-solver/coherence/issue-102048.next.stderr deleted file mode 100644 index 39fde307f23f1..0000000000000 --- a/tests/ui/traits/next-solver/coherence/issue-102048.next.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error[E0119]: conflicting implementations of trait `Trait fn(<_ as WithAssoc1<'a>>::Assoc, <_ as WithAssoc2<'a>>::Assoc)>` for type `(_, _)` - --> $DIR/issue-102048.rs:44:1 - | -LL | / impl Trait fn(>::Assoc, >::Assoc)> for (T, U) -LL | | where -LL | | T: for<'a> WithAssoc1<'a> + for<'a> WithAssoc2<'a, Assoc = i32>, -LL | | U: for<'a> WithAssoc2<'a>, - | |______________________________- first implementation here -... -LL | / impl Trait fn(>::Assoc, u32)> for (T, U) where -LL | | U: for<'a> WithAssoc1<'a> - | |_____________________________^ conflicting implementation for `(_, _)` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/traits/next-solver/diagnostics/alias_relate_error_uses_structurally_normalize.rs b/tests/ui/traits/next-solver/diagnostics/alias_relate_error_uses_structurally_normalize.rs new file mode 100644 index 0000000000000..5cea9bb74d793 --- /dev/null +++ b/tests/ui/traits/next-solver/diagnostics/alias_relate_error_uses_structurally_normalize.rs @@ -0,0 +1,28 @@ +//@ compile-flags: -Znext-solver + +// When encountering a fulfillment error from an `alias-relate` goal failing, we +// would previously manually construct a `normalizes-to` goal involving the alias +// and an infer var. This would then ICE as normalization would return a nested +// goal (the `T: Sized` from the `Trait` impl for `Foo` below) from the root goal +// which is not supported. + +struct Foo(T); + +trait Trait { + type Assoc; +} + +// `T: Sized` being explicit is not required, but the bound being present *is*. +impl Trait for Foo { + type Assoc = u64; +} + +fn bar>(_: T) {} + +fn main() { + let foo = Foo(Default::default()); + bar(foo); + //~^ ERROR: type mismatch resolving ` as Trait>::Assoc == u32` + // Here diagnostics would manually construct a ` as Trait>::Assoc normalizes-to ?x` goal + // which would return a nested goal of `?y: Sized` from the impl. +} diff --git a/tests/ui/traits/next-solver/diagnostics/alias_relate_error_uses_structurally_normalize.stderr b/tests/ui/traits/next-solver/diagnostics/alias_relate_error_uses_structurally_normalize.stderr new file mode 100644 index 0000000000000..ff3cbdb2c7841 --- /dev/null +++ b/tests/ui/traits/next-solver/diagnostics/alias_relate_error_uses_structurally_normalize.stderr @@ -0,0 +1,17 @@ +error[E0271]: type mismatch resolving ` as Trait>::Assoc == u32` + --> $DIR/alias_relate_error_uses_structurally_normalize.rs:24:9 + | +LL | bar(foo); + | --- ^^^ expected `u32`, found `u64` + | | + | required by a bound introduced by this call + | +note: required by a bound in `bar` + --> $DIR/alias_relate_error_uses_structurally_normalize.rs:20:17 + | +LL | fn bar>(_: T) {} + | ^^^^^^^^^^^ required by this bound in `bar` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/traits/next-solver/diagnostics/coerce-in-may-coerce.stderr b/tests/ui/traits/next-solver/diagnostics/coerce-in-may-coerce.stderr index 1938b3375a51a..e4775e41ba1a5 100644 --- a/tests/ui/traits/next-solver/diagnostics/coerce-in-may-coerce.stderr +++ b/tests/ui/traits/next-solver/diagnostics/coerce-in-may-coerce.stderr @@ -10,11 +10,12 @@ note: function defined here --> $DIR/coerce-in-may-coerce.rs:12:4 | LL | fn arg_error(x: ::Assoc, y: ()) { todo!() } - | ^^^^^^^^^ -------------------------- ----- + | ^^^^^^^^^ help: swap these arguments | -LL | arg_error(|| (), ()); - | ~~~~~~~~~~~ +LL - arg_error((), || ()); +LL + arg_error(|| (), ()); + | error: aborting due to 1 previous error diff --git a/tests/ui/traits/next-solver/diagnostics/dont-pick-fnptr-bound-as-leaf.current.stderr b/tests/ui/traits/next-solver/diagnostics/dont-pick-fnptr-bound-as-leaf.current.stderr new file mode 100644 index 0000000000000..a863886181cc5 --- /dev/null +++ b/tests/ui/traits/next-solver/diagnostics/dont-pick-fnptr-bound-as-leaf.current.stderr @@ -0,0 +1,17 @@ +error[E0277]: the trait bound `Foo: Trait` is not satisfied + --> $DIR/dont-pick-fnptr-bound-as-leaf.rs:24:20 + | +LL | requires_trait(Foo); + | -------------- ^^^ the trait `Trait` is not implemented for `Foo` + | | + | required by a bound introduced by this call + | +note: required by a bound in `requires_trait` + --> $DIR/dont-pick-fnptr-bound-as-leaf.rs:19:22 + | +LL | fn requires_trait(_: T) {} + | ^^^^^ required by this bound in `requires_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/diagnostics/dont-pick-fnptr-bound-as-leaf.next.stderr b/tests/ui/traits/next-solver/diagnostics/dont-pick-fnptr-bound-as-leaf.next.stderr new file mode 100644 index 0000000000000..a863886181cc5 --- /dev/null +++ b/tests/ui/traits/next-solver/diagnostics/dont-pick-fnptr-bound-as-leaf.next.stderr @@ -0,0 +1,17 @@ +error[E0277]: the trait bound `Foo: Trait` is not satisfied + --> $DIR/dont-pick-fnptr-bound-as-leaf.rs:24:20 + | +LL | requires_trait(Foo); + | -------------- ^^^ the trait `Trait` is not implemented for `Foo` + | | + | required by a bound introduced by this call + | +note: required by a bound in `requires_trait` + --> $DIR/dont-pick-fnptr-bound-as-leaf.rs:19:22 + | +LL | fn requires_trait(_: T) {} + | ^^^^^ required by this bound in `requires_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/diagnostics/dont-pick-fnptr-bound-as-leaf.rs b/tests/ui/traits/next-solver/diagnostics/dont-pick-fnptr-bound-as-leaf.rs new file mode 100644 index 0000000000000..995f2c9fbeed3 --- /dev/null +++ b/tests/ui/traits/next-solver/diagnostics/dont-pick-fnptr-bound-as-leaf.rs @@ -0,0 +1,28 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) + +// When emitting an error for `Foo: Trait` not holding we attempt to find a nested goal +// to give as the reason why the bound does not hold. This test checks that we do not +// try to tell the user that `Foo: FnPtr` is unimplemented as that would be confusing. + +#![feature(fn_ptr_trait)] + +use std::marker::FnPtr; + +trait Trait {} + +impl Trait for T {} + +struct Foo; + +fn requires_trait(_: T) {} +//~^ NOTE: required by a bound in `requires_trait` +//~| NOTE: required by this bound in `requires_trait` + +fn main() { + requires_trait(Foo); + //~^ ERROR: the trait bound `Foo: Trait` is not satisfied + //~| NOTE: the trait `Trait` is not implemented for `Foo` + //~| NOTE: required by a bound introduced by this call +} diff --git a/tests/ui/traits/next-solver/dont-ice-on-bad-transmute-in-typeck.rs b/tests/ui/traits/next-solver/dont-ice-on-bad-transmute-in-typeck.rs new file mode 100644 index 0000000000000..129e90a07f43a --- /dev/null +++ b/tests/ui/traits/next-solver/dont-ice-on-bad-transmute-in-typeck.rs @@ -0,0 +1,17 @@ +//@ compile-flags: -Znext-solver + +trait Trait<'a> { + type Assoc; +} + +fn foo(x: for<'a> fn(<() as Trait<'a>>::Assoc)) { + //~^ ERROR the trait bound `for<'a> (): Trait<'a>` is not satisfied + //~| ERROR the trait bound `for<'a> (): Trait<'a>` is not satisfied + //~| ERROR the trait bound `for<'a> (): Trait<'a>` is not satisfied + unsafe { std::mem::transmute::<_, ()>(x); } + //~^ ERROR the trait bound `for<'a> (): Trait<'a>` is not satisfied + //~| ERROR the trait bound `for<'a> (): Trait<'a>` is not satisfied + //~| ERROR the trait bound `for<'a> (): Trait<'a>` is not satisfied +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/dont-ice-on-bad-transmute-in-typeck.stderr b/tests/ui/traits/next-solver/dont-ice-on-bad-transmute-in-typeck.stderr new file mode 100644 index 0000000000000..2d42fedae4381 --- /dev/null +++ b/tests/ui/traits/next-solver/dont-ice-on-bad-transmute-in-typeck.stderr @@ -0,0 +1,75 @@ +error[E0277]: the trait bound `for<'a> (): Trait<'a>` is not satisfied + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:7:11 + | +LL | fn foo(x: for<'a> fn(<() as Trait<'a>>::Assoc)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> Trait<'a>` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:3:1 + | +LL | trait Trait<'a> { + | ^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `for<'a> (): Trait<'a>` is not satisfied + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:7:8 + | +LL | fn foo(x: for<'a> fn(<() as Trait<'a>>::Assoc)) { + | ^ the trait `for<'a> Trait<'a>` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:3:1 + | +LL | trait Trait<'a> { + | ^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `for<'a> (): Trait<'a>` is not satisfied + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:11:14 + | +LL | unsafe { std::mem::transmute::<_, ()>(x); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> Trait<'a>` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:3:1 + | +LL | trait Trait<'a> { + | ^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `for<'a> (): Trait<'a>` is not satisfied + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:11:36 + | +LL | unsafe { std::mem::transmute::<_, ()>(x); } + | ^ the trait `for<'a> Trait<'a>` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:3:1 + | +LL | trait Trait<'a> { + | ^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `for<'a> (): Trait<'a>` is not satisfied + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:11:43 + | +LL | unsafe { std::mem::transmute::<_, ()>(x); } + | ^ the trait `for<'a> Trait<'a>` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:3:1 + | +LL | trait Trait<'a> { + | ^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `for<'a> (): Trait<'a>` is not satisfied + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:7:1 + | +LL | fn foo(x: for<'a> fn(<() as Trait<'a>>::Assoc)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> Trait<'a>` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/dont-ice-on-bad-transmute-in-typeck.rs:3:1 + | +LL | trait Trait<'a> { + | ^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/known-type-outlives-has-constraints.rs b/tests/ui/traits/next-solver/known-type-outlives-has-constraints.rs new file mode 100644 index 0000000000000..55fea005ea1ab --- /dev/null +++ b/tests/ui/traits/next-solver/known-type-outlives-has-constraints.rs @@ -0,0 +1,13 @@ +//@ compile-flags: -Znext-solver +//@ check-pass + +trait Norm { + type Out; +} +impl<'a, T: 'a> Norm for &'a T { + type Out = T; +} + +fn hello<'a, T: 'a>() where <&'a T as Norm>::Out: 'a {} + +fn main() {} diff --git a/tests/ui/traits/next-solver/method/path_lookup_wf_constraints.rs b/tests/ui/traits/next-solver/method/path_lookup_wf_constraints.rs new file mode 100644 index 0000000000000..fbe170b1990e7 --- /dev/null +++ b/tests/ui/traits/next-solver/method/path_lookup_wf_constraints.rs @@ -0,0 +1,39 @@ +//@ compile-flags: -Znext-solver +//@ check-pass + +// A regression test for trait-system-refactor-initiative#161 + +trait Constrain { + type Assoc; +} +impl Constrain for () { + type Assoc = (); +} +struct Foo>::Assoc>(T, U); + +impl Foo { + fn foo() {} +} +struct B; +impl Foo { + fn foo() {} +} + +type Alias = Foo; +fn via_guidance() +where + (): Constrain, +{ + // Method selection on `Foo>::Assoc>` is ambiguous. + // only by unnecessarily constraining `?t` to `T` when proving `(): Constrain` + // are we able to select the first impl. + // + // This happens in the old solver when normalizing `Alias`. The new solver doesn't try + // to eagerly normalize `<() as Constrain>::Assoc` so we instead always prove that the + // self type is well-formed before method lookup. + Alias::foo(); +} + +fn main() { + via_guidance::<()>(); +} diff --git a/tests/ui/traits/next-solver/non-wf-in-coerce-pointers.rs b/tests/ui/traits/next-solver/non-wf-in-coerce-pointers.rs new file mode 100644 index 0000000000000..d05def2cb757d --- /dev/null +++ b/tests/ui/traits/next-solver/non-wf-in-coerce-pointers.rs @@ -0,0 +1,17 @@ +//@ compile-flags: -Znext-solver + +trait Wf { + type Assoc; +} + +struct S { + f: &'static <() as Wf>::Assoc, + //~^ ERROR the trait bound `(): Wf` is not satisfied +} + +fn main() { + let x: S = todo!(); + let y: &() = x.f; + //~^ ERROR mismatched types + //~| ERROR the trait bound `(): Wf` is not satisfied +} diff --git a/tests/ui/traits/next-solver/non-wf-in-coerce-pointers.stderr b/tests/ui/traits/next-solver/non-wf-in-coerce-pointers.stderr new file mode 100644 index 0000000000000..32a7766a638da --- /dev/null +++ b/tests/ui/traits/next-solver/non-wf-in-coerce-pointers.stderr @@ -0,0 +1,39 @@ +error[E0277]: the trait bound `(): Wf` is not satisfied + --> $DIR/non-wf-in-coerce-pointers.rs:8:17 + | +LL | f: &'static <() as Wf>::Assoc, + | ^^^^^^^^^^^^^^^^^ the trait `Wf` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/non-wf-in-coerce-pointers.rs:3:1 + | +LL | trait Wf { + | ^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/non-wf-in-coerce-pointers.rs:14:18 + | +LL | let y: &() = x.f; + | --- ^^^ types differ + | | + | expected due to this + | + = note: expected reference `&()` + found reference `&'static <() as Wf>::Assoc` + +error[E0277]: the trait bound `(): Wf` is not satisfied + --> $DIR/non-wf-in-coerce-pointers.rs:14:18 + | +LL | let y: &() = x.f; + | ^^^ the trait `Wf` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/non-wf-in-coerce-pointers.rs:3:1 + | +LL | trait Wf { + | ^^^^^^^^ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/next-solver/normalization-shadowing/global-trait-with-project.rs b/tests/ui/traits/next-solver/normalization-shadowing/global-trait-with-project.rs new file mode 100644 index 0000000000000..dc96652f82ff0 --- /dev/null +++ b/tests/ui/traits/next-solver/normalization-shadowing/global-trait-with-project.rs @@ -0,0 +1,21 @@ +//@ compile-flags: -Znext-solver +//@ check-pass + +// `(): Trait` is a global where-bound with a projection bound. +// This previously resulted in ambiguity as we considered both +// the impl and the where-bound while normalizing. + +trait Trait { + type Assoc; +} +impl Trait for () { + type Assoc = &'static (); +} + +fn foo<'a>(x: <() as Trait>::Assoc) +where + (): Trait, +{ +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/normalize/normalize-unsize-rhs.rs b/tests/ui/traits/next-solver/normalize/normalize-unsize-rhs.rs index dc5912b123a46..bbf9bafde9ff0 100644 --- a/tests/ui/traits/next-solver/normalize/normalize-unsize-rhs.rs +++ b/tests/ui/traits/next-solver/normalize/normalize-unsize-rhs.rs @@ -1,6 +1,5 @@ //@ compile-flags: -Znext-solver //@ check-pass -#![feature(trait_upcasting)] trait A {} trait B: A {} diff --git a/tests/ui/traits/next-solver/specialization-transmute.rs b/tests/ui/traits/next-solver/specialization-transmute.rs index 9e31fed9b1805..376fa22ae1923 100644 --- a/tests/ui/traits/next-solver/specialization-transmute.rs +++ b/tests/ui/traits/next-solver/specialization-transmute.rs @@ -1,5 +1,4 @@ //@ compile-flags: -Znext-solver -//~^ ERROR cannot normalize `::Id: '_` #![feature(specialization)] //~^ WARN the feature `specialization` is incomplete @@ -14,6 +13,7 @@ impl Default for T { // This will be fixed by #111994 fn intu(&self) -> &Self::Id { //~^ ERROR type annotations needed + //~| ERROR cannot normalize `::Id: '_` self //~ ERROR cannot satisfy } } diff --git a/tests/ui/traits/next-solver/specialization-transmute.stderr b/tests/ui/traits/next-solver/specialization-transmute.stderr index b96bfab927d2d..eeb101911c435 100644 --- a/tests/ui/traits/next-solver/specialization-transmute.stderr +++ b/tests/ui/traits/next-solver/specialization-transmute.stderr @@ -1,5 +1,5 @@ warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/specialization-transmute.rs:3:12 + --> $DIR/specialization-transmute.rs:2:12 | LL | #![feature(specialization)] | ^^^^^^^^^^^^^^ @@ -9,12 +9,16 @@ LL | #![feature(specialization)] = note: `#[warn(incomplete_features)]` on by default error: cannot normalize `::Id: '_` + --> $DIR/specialization-transmute.rs:14:5 + | +LL | fn intu(&self) -> &Self::Id { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0284]: type annotations needed: cannot normalize `::Id` - --> $DIR/specialization-transmute.rs:15:23 +error[E0282]: type annotations needed + --> $DIR/specialization-transmute.rs:14:23 | LL | fn intu(&self) -> &Self::Id { - | ^^^^^^^^^ cannot normalize `::Id` + | ^^^^^^^^^ cannot infer type for reference `&::Id` error[E0284]: type annotations needed: cannot satisfy `::Id normalizes-to T` --> $DIR/specialization-transmute.rs:17:9 @@ -36,4 +40,5 @@ LL | fn transmute, U: Copy>(t: T) -> U { error: aborting due to 4 previous errors; 1 warning emitted -For more information about this error, try `rustc --explain E0284`. +Some errors have detailed explanations: E0282, E0284. +For more information about an error, try `rustc --explain E0282`. diff --git a/tests/ui/traits/next-solver/trait-upcast-lhs-needs-normalization.rs b/tests/ui/traits/next-solver/trait-upcast-lhs-needs-normalization.rs index ee6a7a0986dd6..c6094b4577553 100644 --- a/tests/ui/traits/next-solver/trait-upcast-lhs-needs-normalization.rs +++ b/tests/ui/traits/next-solver/trait-upcast-lhs-needs-normalization.rs @@ -1,6 +1,5 @@ //@ check-pass //@ compile-flags: -Znext-solver -#![feature(trait_upcasting)] pub trait A {} pub trait B: A {} diff --git a/tests/ui/traits/next-solver/unsound-region-obligation.rs b/tests/ui/traits/next-solver/unsound-region-obligation.rs index 733be20435487..e7fa60a2e3f60 100644 --- a/tests/ui/traits/next-solver/unsound-region-obligation.rs +++ b/tests/ui/traits/next-solver/unsound-region-obligation.rs @@ -1,4 +1,3 @@ -//~ ERROR the type `&'a ()` does not fulfill the required lifetime //@ compile-flags: -Znext-solver // Regression test for rust-lang/trait-system-refactor-initiative#59 @@ -8,6 +7,7 @@ trait StaticTy { impl StaticTy for () { type Item<'a> = &'a (); + //~^ ERROR the type `&'a ()` does not fulfill the required lifetime } fn main() {} diff --git a/tests/ui/traits/next-solver/unsound-region-obligation.stderr b/tests/ui/traits/next-solver/unsound-region-obligation.stderr index fe96a184f430b..dea3b62334c4d 100644 --- a/tests/ui/traits/next-solver/unsound-region-obligation.stderr +++ b/tests/ui/traits/next-solver/unsound-region-obligation.stderr @@ -1,4 +1,8 @@ error[E0477]: the type `&'a ()` does not fulfill the required lifetime + --> $DIR/unsound-region-obligation.rs:9:21 + | +LL | type Item<'a> = &'a (); + | ^^^^^^ | = note: type must satisfy the static lifetime diff --git a/tests/ui/traits/next-solver/upcast-right-substs.rs b/tests/ui/traits/next-solver/upcast-right-substs.rs index bbb8a039aa7fe..7a566b59b837c 100644 --- a/tests/ui/traits/next-solver/upcast-right-substs.rs +++ b/tests/ui/traits/next-solver/upcast-right-substs.rs @@ -1,6 +1,5 @@ //@ compile-flags: -Znext-solver //@ check-pass -#![feature(trait_upcasting)] trait Foo: Bar + Bar {} diff --git a/tests/ui/traits/non_lifetime_binders/missing-assoc-item.stderr b/tests/ui/traits/non_lifetime_binders/missing-assoc-item.stderr index eecf8e88fb64d..02295307cb22a 100644 --- a/tests/ui/traits/non_lifetime_binders/missing-assoc-item.stderr +++ b/tests/ui/traits/non_lifetime_binders/missing-assoc-item.stderr @@ -15,8 +15,9 @@ LL | for B::Item: Send, | help: if there were a trait named `Example` with associated type `Item` implemented for `B`, you could use the fully-qualified path | -LL | for ::Item: Send, - | ~~~~~~~~~~~~~~~~~~~~ +LL - for B::Item: Send, +LL + for ::Item: Send, + | error: aborting due to 1 previous error; 1 warning emitted diff --git a/tests/ui/traits/non_lifetime_binders/supertrait-dyn-compatibility.rs b/tests/ui/traits/non_lifetime_binders/supertrait-dyn-compatibility.rs index a635edb4485bd..28785ae3dea14 100644 --- a/tests/ui/traits/non_lifetime_binders/supertrait-dyn-compatibility.rs +++ b/tests/ui/traits/non_lifetime_binders/supertrait-dyn-compatibility.rs @@ -17,8 +17,8 @@ impl Bar for () {} fn main() { let x: &dyn Foo = &(); - //~^ ERROR the trait `Foo` cannot be made into an object - //~| ERROR the trait `Foo` cannot be made into an object + //~^ ERROR the trait `Foo` is not dyn compatible + //~| ERROR the trait `Foo` is not dyn compatible needs_bar(x); - //~^ ERROR the trait `Foo` cannot be made into an object + //~^ ERROR the trait `Foo` is not dyn compatible } diff --git a/tests/ui/traits/non_lifetime_binders/supertrait-dyn-compatibility.stderr b/tests/ui/traits/non_lifetime_binders/supertrait-dyn-compatibility.stderr index dd2dca74f9087..43b69d0b50e48 100644 --- a/tests/ui/traits/non_lifetime_binders/supertrait-dyn-compatibility.stderr +++ b/tests/ui/traits/non_lifetime_binders/supertrait-dyn-compatibility.stderr @@ -7,51 +7,54 @@ LL | #![feature(non_lifetime_binders)] = note: see issue #108185 for more information = note: `#[warn(incomplete_features)]` on by default -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/supertrait-dyn-compatibility.rs:19:23 | LL | let x: &dyn Foo = &(); - | ^^^ `Foo` cannot be made into an object + | ^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/supertrait-dyn-compatibility.rs:4:12 | LL | trait Foo: for Bar {} | --- ^^^^^^^^^^^^^ ...because where clause cannot reference non-lifetime `for<...>` variables | | - | this trait cannot be made into an object... - = help: only type `()` implements the trait, consider using it directly instead + | this trait is not dyn compatible... + = help: only type `()` implements `Foo`; consider using it directly instead. = note: required for the cast from `&()` to `&dyn Foo` -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/supertrait-dyn-compatibility.rs:19:12 | LL | let x: &dyn Foo = &(); - | ^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/supertrait-dyn-compatibility.rs:4:12 | LL | trait Foo: for Bar {} | --- ^^^^^^^^^^^^^ ...because where clause cannot reference non-lifetime `for<...>` variables | | - | this trait cannot be made into an object... - = help: only type `()` implements the trait, consider using it directly instead + | this trait is not dyn compatible... + = help: only type `()` implements `Foo`; consider using it directly instead. -error[E0038]: the trait `Foo` cannot be made into an object +error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/supertrait-dyn-compatibility.rs:22:5 | LL | needs_bar(x); - | ^^^^^^^^^ `Foo` cannot be made into an object + | ^^^^^^^^^ `Foo` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/supertrait-dyn-compatibility.rs:4:12 | LL | trait Foo: for Bar {} | --- ^^^^^^^^^^^^^ ...because where clause cannot reference non-lifetime `for<...>` variables | | - | this trait cannot be made into an object... - = help: only type `()` implements the trait, consider using it directly instead + | this trait is not dyn compatible... + = help: only type `()` implements `Foo`; consider using it directly instead. error: aborting due to 3 previous errors; 1 warning emitted diff --git a/tests/ui/type-alias-impl-trait/non-lifetime-binder-in-constraint.rs b/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-in-constraint.rs similarity index 100% rename from tests/ui/type-alias-impl-trait/non-lifetime-binder-in-constraint.rs rename to tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-in-constraint.rs diff --git a/tests/ui/type-alias-impl-trait/non-lifetime-binder-in-constraint.stderr b/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-in-constraint.stderr similarity index 100% rename from tests/ui/type-alias-impl-trait/non-lifetime-binder-in-constraint.stderr rename to tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder-in-constraint.stderr diff --git a/tests/ui/type-alias-impl-trait/non-lifetime-binder.rs b/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder.rs similarity index 100% rename from tests/ui/type-alias-impl-trait/non-lifetime-binder.rs rename to tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder.rs diff --git a/tests/ui/type-alias-impl-trait/non-lifetime-binder.stderr b/tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder.stderr similarity index 100% rename from tests/ui/type-alias-impl-trait/non-lifetime-binder.stderr rename to tests/ui/traits/non_lifetime_binders/type-alias-impl-trait/non-lifetime-binder.stderr diff --git a/tests/ui/traits/non_lifetime_binders/type-match-with-late-bound.stderr b/tests/ui/traits/non_lifetime_binders/type-match-with-late-bound.stderr index 40e16dde6e4a4..9d54675c260b2 100644 --- a/tests/ui/traits/non_lifetime_binders/type-match-with-late-bound.stderr +++ b/tests/ui/traits/non_lifetime_binders/type-match-with-late-bound.stderr @@ -26,7 +26,7 @@ LL | for F: 'a, help: consider adding an explicit lifetime bound | LL | for F: 'a, !1_"F": 'a - | ~~~~~~~~~~~~ + | ++++++++++ error[E0309]: the placeholder type `!1_"F"` may not live long enough --> $DIR/type-match-with-late-bound.rs:11:1 @@ -40,7 +40,7 @@ LL | {} help: consider adding an explicit lifetime bound | LL | for F: 'a, !1_"F": 'a - | ~~~~~~~~~~~~ + | ++++++++++ error[E0309]: the placeholder type `!2_"F"` may not live long enough --> $DIR/type-match-with-late-bound.rs:11:1 @@ -54,7 +54,7 @@ LL | {} help: consider adding an explicit lifetime bound | LL | for F: 'a, !2_"F": 'a - | ~~~~~~~~~~~~ + | ++++++++++ error: aborting due to 3 previous errors; 1 warning emitted diff --git a/tests/ui/traits/not-suggest-non-existing-fully-qualified-path.stderr b/tests/ui/traits/not-suggest-non-existing-fully-qualified-path.stderr index 1d5489845efe6..7a2db203ac3f8 100644 --- a/tests/ui/traits/not-suggest-non-existing-fully-qualified-path.stderr +++ b/tests/ui/traits/not-suggest-non-existing-fully-qualified-path.stderr @@ -21,8 +21,9 @@ LL | T: I, | ---- unsatisfied trait bound introduced here help: try using a fully qualified path to specify the expected types | -LL | as V>::method(a); - | +++++++++++++++++++++++ ~ +LL - a.method(); +LL + as V>::method(a); + | error: aborting due to 1 previous error diff --git a/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.rs b/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.rs index 4aadd45c49c1e..6fcd67b4950f7 100644 --- a/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.rs +++ b/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.rs @@ -8,9 +8,9 @@ trait Try { fn w<'a, T: 'a, F: Fn(&'a T)>() { let b: &dyn FromResidual = &(); - //~^ ERROR: the trait `FromResidual` cannot be made into an object - //~| ERROR: the trait `FromResidual` cannot be made into an object - //~| ERROR: the trait `FromResidual` cannot be made into an object + //~^ ERROR: the trait `FromResidual` is not dyn compatible + //~| ERROR: the trait `FromResidual` is not dyn compatible + //~| ERROR the type parameter `R` must be explicitly specified } fn main() {} diff --git a/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr b/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr index c67a8c05379cd..b4bbd65b2f47c 100644 --- a/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr +++ b/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr @@ -1,22 +1,30 @@ -error[E0038]: the trait `FromResidual` cannot be made into an object +error[E0393]: the type parameter `R` must be explicitly specified --> $DIR/canonicalize-fresh-infer-vars-issue-103626.rs:10:17 | +LL | trait FromResidual::Residual> { + | ----------------------------------------------- type parameter `R` must be specified for this +... LL | let b: &dyn FromResidual = &(); | ^^^^^^^^^^^^ | - = note: it cannot use `Self` as a type parameter in a supertrait or `where`-clause + = note: because the parameter default references `Self`, the parameter must be specified on the object type +help: set the type parameter to the desired type + | +LL | let b: &dyn FromResidual = &(); + | +++ -error[E0038]: the trait `FromResidual` cannot be made into an object +error[E0038]: the trait `FromResidual` is not dyn compatible --> $DIR/canonicalize-fresh-infer-vars-issue-103626.rs:10:32 | LL | let b: &dyn FromResidual = &(); - | ^^^ `FromResidual` cannot be made into an object + | ^^^ `FromResidual` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/canonicalize-fresh-infer-vars-issue-103626.rs:2:8 | LL | trait FromResidual::Residual> { - | ------------ this trait cannot be made into an object... + | ------------ this trait is not dyn compatible... LL | fn from_residual(residual: R) -> Self; | ^^^^^^^^^^^^^ ...because associated function `from_residual` has no `self` parameter = note: required for the cast from `&()` to `&dyn FromResidual<{type error}>` @@ -29,17 +37,18 @@ help: alternatively, consider constraining `from_residual` so it does not apply LL | fn from_residual(residual: R) -> Self where Self: Sized; | +++++++++++++++++ -error[E0038]: the trait `FromResidual` cannot be made into an object +error[E0038]: the trait `FromResidual` is not dyn compatible --> $DIR/canonicalize-fresh-infer-vars-issue-103626.rs:10:12 | LL | let b: &dyn FromResidual = &(); - | ^^^^^^^^^^^^^^^^^ `FromResidual` cannot be made into an object + | ^^^^^^^^^^^^^^^^^ `FromResidual` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/canonicalize-fresh-infer-vars-issue-103626.rs:2:8 | LL | trait FromResidual::Residual> { - | ------------ this trait cannot be made into an object... + | ------------ this trait is not dyn compatible... LL | fn from_residual(residual: R) -> Self; | ^^^^^^^^^^^^^ ...because associated function `from_residual` has no `self` parameter help: consider turning `from_residual` into a method by giving it a `&self` argument @@ -53,4 +62,5 @@ LL | fn from_residual(residual: R) -> Self where Self: Sized; error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0038`. +Some errors have detailed explanations: E0038, E0393. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/traits/object/macro-matcher.rs b/tests/ui/traits/object/macro-matcher.rs index 91097874997b4..675d9f51532a4 100644 --- a/tests/ui/traits/object/macro-matcher.rs +++ b/tests/ui/traits/object/macro-matcher.rs @@ -6,7 +6,7 @@ macro_rules! m { fn main() { m!(dyn Copy + Send + 'static); - //~^ ERROR the trait `Copy` cannot be made into an object + //~^ ERROR the trait `Copy` is not dyn compatible m!(dyn 'static + Send); m!(dyn 'static +); //~ ERROR at least one trait is required for an object type } diff --git a/tests/ui/traits/object/macro-matcher.stderr b/tests/ui/traits/object/macro-matcher.stderr index 7924c86e29400..3c668ce99c7f5 100644 --- a/tests/ui/traits/object/macro-matcher.stderr +++ b/tests/ui/traits/object/macro-matcher.stderr @@ -4,14 +4,15 @@ error[E0224]: at least one trait is required for an object type LL | m!(dyn 'static +); | ^^^^^^^^^^^^^ -error[E0038]: the trait `Copy` cannot be made into an object +error[E0038]: the trait `Copy` is not dyn compatible --> $DIR/macro-matcher.rs:8:8 | LL | m!(dyn Copy + Send + 'static); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ `Copy` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^^^^^^ `Copy` is not dyn compatible | - = note: the trait cannot be made into an object because it requires `Self: Sized` - = note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + = note: the trait is not dyn compatible because it requires `Self: Sized` + = note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit error: aborting due to 2 previous errors diff --git a/tests/ui/traits/object/pretty.stderr b/tests/ui/traits/object/pretty.stderr index af941e69c5f84..2f9fdf151f08c 100644 --- a/tests/ui/traits/object/pretty.stderr +++ b/tests/ui/traits/object/pretty.stderr @@ -110,7 +110,7 @@ error[E0308]: mismatched types --> $DIR/pretty.rs:36:79 | LL | fn dyn_fixed_generic_multi(x: &dyn for<'a> FixedGeneric1<'a, Assoc2 = &u8>) { x } - | - ^ expected `()`, found `&dyn FixedGeneric1<'a, Assoc2 = ...>` + | - ^ expected `()`, found `&dyn FixedGeneric1<'a, Assoc2 = &u8>` | | | help: try adding a return type: `-> &dyn for<'a> FixedGeneric1<'a, Assoc2 = &u8>` | @@ -132,7 +132,7 @@ error[E0308]: mismatched types --> $DIR/pretty.rs:38:73 | LL | fn dyn_any_different_binders(x: &dyn AnyDifferentBinders) { x } - | - ^ expected `()`, found `&dyn AnyDifferentBinders` + | - ^ expected `()`, found `&dyn AnyDifferentBinders` | | | help: try adding a return type: `-> &dyn AnyDifferentBinders` | diff --git a/tests/ui/traits/object/print_vtable_sizes.rs b/tests/ui/traits/object/print_vtable_sizes.rs deleted file mode 100644 index 2b1745da5f308..0000000000000 --- a/tests/ui/traits/object/print_vtable_sizes.rs +++ /dev/null @@ -1,62 +0,0 @@ -//@ check-pass -//@ compile-flags: -Z print-vtable-sizes -#![crate_type = "lib"] - -trait A: AsRef<[T::V]> + AsMut<[T::V]> {} - -trait B: AsRef + AsRef + AsRef + AsRef {} - -trait C { - fn x() {} // not dyn-compatible, shouldn't be reported -} - -// This does not have any upcasting cost, -// because both `Send` and `Sync` are traits -// with no methods -trait D: Send + Sync + help::MarkerWithSuper {} - -// This can't have no cost without reordering, -// because `Super::f`. -trait E: help::MarkerWithSuper + Send + Sync {} - -trait F { - fn a(&self); - fn b(&self); - fn c(&self); - - fn d() -> Self - where - Self: Sized; -} - -trait G: AsRef + AsRef + help::MarkerWithSuper { - fn a(&self); - fn b(&self); - fn c(&self); - fn d(&self); - fn e(&self); - - fn f() -> Self - where - Self: Sized; -} - -// Traits with the same name -const _: () = { - trait S {} -}; -const _: () = { - trait S {} -}; - -mod help { - pub trait V { - type V; - } - - pub trait MarkerWithSuper: Super {} - - pub trait Super { - fn f(&self); - } -} diff --git a/tests/ui/traits/object/print_vtable_sizes.stdout b/tests/ui/traits/object/print_vtable_sizes.stdout deleted file mode 100644 index 4daf476957657..0000000000000 --- a/tests/ui/traits/object/print_vtable_sizes.stdout +++ /dev/null @@ -1,11 +0,0 @@ -print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "A", "entries": "6", "entries_ignoring_upcasting": "5", "entries_for_upcasting": "1", "upcasting_cost_percent": "20" } -print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "G", "entries": "13", "entries_ignoring_upcasting": "11", "entries_for_upcasting": "2", "upcasting_cost_percent": "18.181818181818183" } -print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "B", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" } -print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "D", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" } -print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "E", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" } -print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "F", "entries": "6", "entries_ignoring_upcasting": "6", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" } -print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "_::S", "entries": "3", "entries_ignoring_upcasting": "3", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" } -print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "_::S", "entries": "3", "entries_ignoring_upcasting": "3", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" } -print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "help::MarkerWithSuper", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" } -print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "help::Super", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" } -print-vtable-sizes { "crate_name": "print_vtable_sizes", "trait_name": "help::V", "entries": "3", "entries_ignoring_upcasting": "3", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" } diff --git a/tests/ui/traits/object/safety.stderr b/tests/ui/traits/object/safety.stderr index a2cb656b08d3e..593e42619f412 100644 --- a/tests/ui/traits/object/safety.stderr +++ b/tests/ui/traits/object/safety.stderr @@ -1,17 +1,18 @@ -error[E0038]: the trait `Tr` cannot be made into an object +error[E0038]: the trait `Tr` is not dyn compatible --> $DIR/safety.rs:15:22 | LL | let _: &dyn Tr = &St; - | ^^^ `Tr` cannot be made into an object + | ^^^ `Tr` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/safety.rs:4:8 | LL | trait Tr { - | -- this trait cannot be made into an object... + | -- this trait is not dyn compatible... LL | fn foo(); | ^^^ ...because associated function `foo` has no `self` parameter - = help: only type `St` implements the trait, consider using it directly instead + = help: only type `St` implements `Tr`; consider using it directly instead. = note: required for the cast from `&St` to `&dyn Tr` help: consider turning `foo` into a method by giving it a `&self` argument | @@ -22,20 +23,21 @@ help: alternatively, consider constraining `foo` so it does not apply to trait o LL | fn foo() where Self: Sized; | +++++++++++++++++ -error[E0038]: the trait `Tr` cannot be made into an object +error[E0038]: the trait `Tr` is not dyn compatible --> $DIR/safety.rs:15:12 | LL | let _: &dyn Tr = &St; - | ^^^^^^^ `Tr` cannot be made into an object + | ^^^^^^^ `Tr` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/safety.rs:4:8 | LL | trait Tr { - | -- this trait cannot be made into an object... + | -- this trait is not dyn compatible... LL | fn foo(); | ^^^ ...because associated function `foo` has no `self` parameter - = help: only type `St` implements the trait, consider using it directly instead + = help: only type `St` implements `Tr`; consider using it directly instead. help: consider turning `foo` into a method by giving it a `&self` argument | LL | fn foo(&self); diff --git a/tests/ui/traits/on_unimplemented_long_types.rs b/tests/ui/traits/on_unimplemented_long_types.rs index 98749b8db7ae2..c652b71e51acd 100644 --- a/tests/ui/traits/on_unimplemented_long_types.rs +++ b/tests/ui/traits/on_unimplemented_long_types.rs @@ -1,5 +1,6 @@ //@ compile-flags: --diagnostic-width=60 -Z write-long-types-to-disk=yes -//@ normalize-stderr: "long-type-\d+" -> "long-type-hash" +// The regex below normalizes the long type file name to make it suitable for compare-modes. +//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type-\d+.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type-hash.txt'" pub fn foo() -> impl std::fmt::Display { //~^ ERROR doesn't implement `std::fmt::Display` diff --git a/tests/ui/traits/on_unimplemented_long_types.stderr b/tests/ui/traits/on_unimplemented_long_types.stderr index bddc5695696b5..2705d7c501ec6 100644 --- a/tests/ui/traits/on_unimplemented_long_types.stderr +++ b/tests/ui/traits/on_unimplemented_long_types.stderr @@ -1,5 +1,5 @@ error[E0277]: `Option>>` doesn't implement `std::fmt::Display` - --> $DIR/on_unimplemented_long_types.rs:4:17 + --> $DIR/on_unimplemented_long_types.rs:5:17 | LL | pub fn foo() -> impl std::fmt::Display { | ^^^^^^^^^^^^^^^^^^^^^^ `Option>>` cannot be formatted with the default formatter @@ -13,11 +13,9 @@ LL | | ))))))))))), LL | | ))))))))))) | |_______________- return type was inferred to be `Option>>` here | - = note: the full name for the type has been written to '$TEST_BUILD_DIR/traits/on_unimplemented_long_types/on_unimplemented_long_types.long-type-hash.txt' - = note: consider using `--verbose` to print the full type name to the console = help: the trait `std::fmt::Display` is not implemented for `Option>>` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead - = note: the full name for the type has been written to '$TEST_BUILD_DIR/traits/on_unimplemented_long_types/on_unimplemented_long_types.long-type-hash.txt' + = note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt' = note: consider using `--verbose` to print the full type name to the console error: aborting due to 1 previous error diff --git a/tests/ui/traits/sized-coniductive.rs b/tests/ui/traits/sized-coniductive.rs deleted file mode 100644 index 5f63b166f988b..0000000000000 --- a/tests/ui/traits/sized-coniductive.rs +++ /dev/null @@ -1,14 +0,0 @@ -//@ check-pass -// https://github.com/rust-lang/rust/issues/129541 - -#[derive(Clone)] -struct Test { - field: std::borrow::Cow<'static, [Self]>, -} - -#[derive(Clone)] -struct Hello { - a: <[Hello] as std::borrow::ToOwned>::Owned, -} - -fn main(){} diff --git a/tests/ui/traits/solver-cycles/129541-recursive-struct-and-array-impl.rs b/tests/ui/traits/solver-cycles/129541-recursive-struct-and-array-impl.rs deleted file mode 100644 index defb39aae06d4..0000000000000 --- a/tests/ui/traits/solver-cycles/129541-recursive-struct-and-array-impl.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Regression test for #129541 - -//@ check-pass - -trait Bound {} -trait Normalize { - type Assoc; -} - -impl Normalize for T { - type Assoc = T; -} - -impl Normalize for [T] { - type Assoc = T; -} - -impl Bound for Hello {} -struct Hello { - a: <[Hello] as Normalize>::Assoc, -} - -fn main() {} diff --git a/tests/ui/traits/solver-cycles/129541-recursive-struct.rs b/tests/ui/traits/solver-cycles/129541-recursive-struct.rs index d4339dd54d6c7..4fbcbefec913b 100644 --- a/tests/ui/traits/solver-cycles/129541-recursive-struct.rs +++ b/tests/ui/traits/solver-cycles/129541-recursive-struct.rs @@ -1,5 +1,6 @@ // Regression test for #129541 +//@ revisions: unique multiple //@ check-pass trait Bound {} @@ -7,6 +8,10 @@ trait Normalize { type Assoc; } +#[cfg(multiple)] +impl Normalize for T { + type Assoc = T; +} impl Normalize for [T] { type Assoc = T; } diff --git a/tests/ui/traits/suggest-fully-qualified-closure.stderr b/tests/ui/traits/suggest-fully-qualified-closure.stderr index a2c1115e673a3..8975d04d5b3ed 100644 --- a/tests/ui/traits/suggest-fully-qualified-closure.stderr +++ b/tests/ui/traits/suggest-fully-qualified-closure.stderr @@ -14,8 +14,9 @@ LL | impl MyTrait for Qqq{ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | >::lol::<_>(&q, ||()); - | +++++++++++++++++++++++++++++++ ~ +LL - q.lol(||()); +LL + >::lol::<_>(&q, ||()); + | error: aborting due to 1 previous error diff --git a/tests/ui/traits/suggest-fully-qualified-path-with-adjustment.stderr b/tests/ui/traits/suggest-fully-qualified-path-with-adjustment.stderr index 841acb5ffd3cd..0996227e69701 100644 --- a/tests/ui/traits/suggest-fully-qualified-path-with-adjustment.stderr +++ b/tests/ui/traits/suggest-fully-qualified-path-with-adjustment.stderr @@ -14,8 +14,9 @@ LL | impl Method for Thing { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | >::method(&thing); - | ++++++++++++++++++++++++++++++ ~ +LL - thing.method(); +LL + >::method(&thing); + | error[E0283]: type annotations needed --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:46:11 @@ -33,8 +34,9 @@ LL | impl Method for Thing { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | >::mut_method(&mut thing); - | +++++++++++++++++++++++++++++++++++++ ~ +LL - thing.mut_method(); +LL + >::mut_method(&mut thing); + | error[E0283]: type annotations needed --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:47:11 @@ -52,8 +54,9 @@ LL | impl MethodRef for &Thing { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | <&Thing as MethodRef>::by_self(&thing); - | +++++++++++++++++++++++++++++++++++ ~ +LL - thing.by_self(); +LL + <&Thing as MethodRef>::by_self(&thing); + | error[E0283]: type annotations needed --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:50:14 @@ -71,8 +74,9 @@ LL | impl Method for Thing { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | >::method(&deref_to); - | ++++++++++++++++++++++++++++++ ~ +LL - deref_to.method(); +LL + >::method(&deref_to); + | error[E0283]: type annotations needed --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:51:14 @@ -90,8 +94,9 @@ LL | impl Method for Thing { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | >::mut_method(&mut deref_to); - | +++++++++++++++++++++++++++++++++++++ ~ +LL - deref_to.mut_method(); +LL + >::mut_method(&mut deref_to); + | error[E0283]: type annotations needed --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:52:14 @@ -109,8 +114,9 @@ LL | impl MethodRef for &Thing { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | <&Thing as MethodRef>::by_self(&deref_to); - | +++++++++++++++++++++++++++++++++++ ~ +LL - deref_to.by_self(); +LL + <&Thing as MethodRef>::by_self(&deref_to); + | error[E0283]: type annotations needed --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:55:20 @@ -128,8 +134,9 @@ LL | impl Method for Thing { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | >::method(&deref_deref_to); - | ++++++++++++++++++++++++++++++ ~ +LL - deref_deref_to.method(); +LL + >::method(&deref_deref_to); + | error[E0283]: type annotations needed --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:56:20 @@ -147,8 +154,9 @@ LL | impl Method for Thing { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | >::mut_method(&mut deref_deref_to); - | +++++++++++++++++++++++++++++++++++++ ~ +LL - deref_deref_to.mut_method(); +LL + >::mut_method(&mut deref_deref_to); + | error[E0283]: type annotations needed --> $DIR/suggest-fully-qualified-path-with-adjustment.rs:57:20 @@ -166,8 +174,9 @@ LL | impl MethodRef for &Thing { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | <&Thing as MethodRef>::by_self(&deref_deref_to); - | +++++++++++++++++++++++++++++++++++ ~ +LL - deref_deref_to.by_self(); +LL + <&Thing as MethodRef>::by_self(&deref_deref_to); + | error: aborting due to 9 previous errors diff --git a/tests/ui/traits/suggest-fully-qualified-path-without-adjustment.stderr b/tests/ui/traits/suggest-fully-qualified-path-without-adjustment.stderr index 1865d81bad19c..629904815f312 100644 --- a/tests/ui/traits/suggest-fully-qualified-path-without-adjustment.stderr +++ b/tests/ui/traits/suggest-fully-qualified-path-without-adjustment.stderr @@ -14,8 +14,9 @@ LL | impl Method for Thing { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | >::method(ref_thing); - | +++++++++++++++++++++++++++++ ~ +LL - ref_thing.method(); +LL + >::method(ref_thing); + | error[E0283]: type annotations needed --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:46:15 @@ -33,8 +34,9 @@ LL | impl MethodRef for &Thing { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | <&Thing as MethodRef>::by_self(ref_thing); - | ++++++++++++++++++++++++++++++++++ ~ +LL - ref_thing.by_self(); +LL + <&Thing as MethodRef>::by_self(ref_thing); + | error[E0283]: type annotations needed --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:49:15 @@ -52,8 +54,9 @@ LL | impl Method for Thing { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | >::method(mut_thing); - | +++++++++++++++++++++++++++++ ~ +LL - mut_thing.method(); +LL + >::method(mut_thing); + | error[E0283]: type annotations needed --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:50:15 @@ -71,8 +74,9 @@ LL | impl Method for Thing { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | >::mut_method(mut_thing); - | +++++++++++++++++++++++++++++++++ ~ +LL - mut_thing.mut_method(); +LL + >::mut_method(mut_thing); + | error[E0283]: type annotations needed --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:51:15 @@ -90,8 +94,9 @@ LL | impl MethodRef for &Thing { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | <&Thing as MethodRef>::by_self(mut_thing); - | ++++++++++++++++++++++++++++++++++ ~ +LL - mut_thing.by_self(); +LL + <&Thing as MethodRef>::by_self(mut_thing); + | error[E0283]: type annotations needed --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:54:14 @@ -109,8 +114,9 @@ LL | impl Method for Thing { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | >::method(deref_to); - | +++++++++++++++++++++++++++++ ~ +LL - deref_to.method(); +LL + >::method(deref_to); + | error[E0283]: type annotations needed --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:55:14 @@ -128,8 +134,9 @@ LL | impl Method for Thing { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | >::mut_method(deref_to); - | +++++++++++++++++++++++++++++++++ ~ +LL - deref_to.mut_method(); +LL + >::mut_method(deref_to); + | error[E0283]: type annotations needed --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:56:14 @@ -147,8 +154,9 @@ LL | impl MethodRef for &Thing { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | <&Thing as MethodRef>::by_self(deref_to); - | ++++++++++++++++++++++++++++++++++ ~ +LL - deref_to.by_self(); +LL + <&Thing as MethodRef>::by_self(deref_to); + | error[E0283]: type annotations needed --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:59:20 @@ -166,8 +174,9 @@ LL | impl Method for Thing { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | >::method(deref_deref_to); - | +++++++++++++++++++++++++++++ ~ +LL - deref_deref_to.method(); +LL + >::method(deref_deref_to); + | error[E0283]: type annotations needed --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:60:20 @@ -185,8 +194,9 @@ LL | impl Method for Thing { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | >::mut_method(deref_deref_to); - | +++++++++++++++++++++++++++++++++ ~ +LL - deref_deref_to.mut_method(); +LL + >::mut_method(deref_deref_to); + | error[E0283]: type annotations needed --> $DIR/suggest-fully-qualified-path-without-adjustment.rs:61:20 @@ -204,8 +214,9 @@ LL | impl MethodRef for &Thing { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | <&Thing as MethodRef>::by_self(deref_deref_to); - | ++++++++++++++++++++++++++++++++++ ~ +LL - deref_deref_to.by_self(); +LL + <&Thing as MethodRef>::by_self(deref_deref_to); + | error: aborting due to 11 previous errors diff --git a/tests/ui/traits/test-2.stderr b/tests/ui/traits/test-2.stderr index 3da95b478448f..6a6cb503aa4dc 100644 --- a/tests/ui/traits/test-2.stderr +++ b/tests/ui/traits/test-2.stderr @@ -26,65 +26,74 @@ note: method defined here, with 1 generic parameter: `X` LL | trait bar { fn dup(&self) -> Self; fn blah(&self); } | ^^^^ - -error[E0038]: the trait `bar` cannot be made into an object +error[E0038]: the trait `bar` is not dyn compatible --> $DIR/test-2.rs:13:22 | LL | (Box::new(10) as Box).dup(); - | ^^^^^^^^^^^^ `bar` cannot be made into an object + | ^^^^^^^^^^^^ `bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/test-2.rs:4:30 | LL | trait bar { fn dup(&self) -> Self; fn blah(&self); } | --- ^^^^ ^^^^ ...because method `blah` has generic type parameters | | | | | ...because method `dup` references the `Self` type in its return type - | this trait cannot be made into an object... + | this trait is not dyn compatible... = help: consider moving `dup` to another trait = help: consider moving `blah` to another trait - = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `bar` for this new enum and using it instead: + = help: the following types implement `bar`: i32 u32 + consider defining an enum where each variant holds one of these types, + implementing `bar` for this new enum and using it instead -error[E0038]: the trait `bar` cannot be made into an object +error[E0038]: the trait `bar` is not dyn compatible --> $DIR/test-2.rs:13:5 | LL | (Box::new(10) as Box).dup(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `bar` cannot be made into an object + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/test-2.rs:4:30 | LL | trait bar { fn dup(&self) -> Self; fn blah(&self); } | --- ^^^^ ^^^^ ...because method `blah` has generic type parameters | | | | | ...because method `dup` references the `Self` type in its return type - | this trait cannot be made into an object... + | this trait is not dyn compatible... = help: consider moving `dup` to another trait = help: consider moving `blah` to another trait - = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `bar` for this new enum and using it instead: + = help: the following types implement `bar`: i32 u32 + consider defining an enum where each variant holds one of these types, + implementing `bar` for this new enum and using it instead -error[E0038]: the trait `bar` cannot be made into an object +error[E0038]: the trait `bar` is not dyn compatible --> $DIR/test-2.rs:13:6 | LL | (Box::new(10) as Box).dup(); - | ^^^^^^^^^^^^ `bar` cannot be made into an object + | ^^^^^^^^^^^^ `bar` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/test-2.rs:4:30 | LL | trait bar { fn dup(&self) -> Self; fn blah(&self); } | --- ^^^^ ^^^^ ...because method `blah` has generic type parameters | | | | | ...because method `dup` references the `Self` type in its return type - | this trait cannot be made into an object... + | this trait is not dyn compatible... = help: consider moving `dup` to another trait = help: consider moving `blah` to another trait - = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `bar` for this new enum and using it instead: + = help: the following types implement `bar`: i32 u32 + consider defining an enum where each variant holds one of these types, + implementing `bar` for this new enum and using it instead = note: required for the cast from `Box<{integer}>` to `Box` error: aborting due to 5 previous errors diff --git a/tests/ui/traits/trait-upcasting/add-supertrait-auto-traits.rs b/tests/ui/traits/trait-upcasting/add-supertrait-auto-traits.rs index ffc21a4a9ea50..5b22fce6311bd 100644 --- a/tests/ui/traits/trait-upcasting/add-supertrait-auto-traits.rs +++ b/tests/ui/traits/trait-upcasting/add-supertrait-auto-traits.rs @@ -3,8 +3,6 @@ //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -#![feature(trait_upcasting)] - trait Target {} trait Source: Send + Target {} diff --git a/tests/ui/traits/trait-upcasting/alias-where-clause-isnt-supertrait.rs b/tests/ui/traits/trait-upcasting/alias-where-clause-isnt-supertrait.rs index 4a5e445d1efbe..927a9d6b94f8a 100644 --- a/tests/ui/traits/trait-upcasting/alias-where-clause-isnt-supertrait.rs +++ b/tests/ui/traits/trait-upcasting/alias-where-clause-isnt-supertrait.rs @@ -1,4 +1,3 @@ -#![feature(trait_upcasting)] #![feature(trait_alias)] // Although we *elaborate* `T: Alias` to `i32: B`, we should diff --git a/tests/ui/traits/trait-upcasting/alias-where-clause-isnt-supertrait.stderr b/tests/ui/traits/trait-upcasting/alias-where-clause-isnt-supertrait.stderr index 99c82b88d9c38..1a410519a4edc 100644 --- a/tests/ui/traits/trait-upcasting/alias-where-clause-isnt-supertrait.stderr +++ b/tests/ui/traits/trait-upcasting/alias-where-clause-isnt-supertrait.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/alias-where-clause-isnt-supertrait.rs:27:5 + --> $DIR/alias-where-clause-isnt-supertrait.rs:26:5 | LL | fn test(x: &dyn C) -> &dyn B { | ------ expected `&dyn B` because of return type diff --git a/tests/ui/traits/trait-upcasting/basic.rs b/tests/ui/traits/trait-upcasting/basic.rs index 9b9669565bac4..0a8d16a42dd4d 100644 --- a/tests/ui/traits/trait-upcasting/basic.rs +++ b/tests/ui/traits/trait-upcasting/basic.rs @@ -1,7 +1,5 @@ //@ run-pass -#![feature(trait_upcasting)] - trait Foo: PartialEq + std::fmt::Debug + Send + Sync { fn a(&self) -> i32 { 10 diff --git a/tests/ui/traits/trait-upcasting/correct-supertrait-substitution.rs b/tests/ui/traits/trait-upcasting/correct-supertrait-substitution.rs index 7f4343dbd2f16..0a02784f29cb7 100644 --- a/tests/ui/traits/trait-upcasting/correct-supertrait-substitution.rs +++ b/tests/ui/traits/trait-upcasting/correct-supertrait-substitution.rs @@ -1,5 +1,4 @@ //@ run-pass -#![feature(trait_upcasting)] trait Foo: Bar + Bar {} trait Bar { diff --git a/tests/ui/traits/trait-upcasting/deref-upcast-behavioral-change.rs b/tests/ui/traits/trait-upcasting/deref-upcast-behavioral-change.rs deleted file mode 100644 index e4784fa41011b..0000000000000 --- a/tests/ui/traits/trait-upcasting/deref-upcast-behavioral-change.rs +++ /dev/null @@ -1,35 +0,0 @@ -#![deny(deref_into_dyn_supertrait)] -use std::ops::Deref; - -trait Bar {} -impl Bar for T {} - -trait Foo: Bar { - fn as_dyn_bar_u32<'a>(&self) -> &(dyn Bar + 'a); -} - -impl Foo for () { - fn as_dyn_bar_u32<'a>(&self) -> &(dyn Bar + 'a) { - self - } -} - -impl<'a> Deref for dyn Foo + 'a { - //~^ ERROR this `Deref` implementation is covered by an implicit supertrait coercion - //~| WARN this will change its meaning in a future release! - type Target = dyn Bar + 'a; - - fn deref(&self) -> &Self::Target { - self.as_dyn_bar_u32() - } -} - -fn take_dyn(x: &dyn Bar) -> T { - todo!() -} - -fn main() { - let x: &dyn Foo = &(); - let y = take_dyn(x); - let z: u32 = y; -} diff --git a/tests/ui/traits/trait-upcasting/deref-upcast-behavioral-change.stderr b/tests/ui/traits/trait-upcasting/deref-upcast-behavioral-change.stderr deleted file mode 100644 index fa93e28c73bce..0000000000000 --- a/tests/ui/traits/trait-upcasting/deref-upcast-behavioral-change.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error: this `Deref` implementation is covered by an implicit supertrait coercion - --> $DIR/deref-upcast-behavioral-change.rs:17:1 - | -LL | impl<'a> Deref for dyn Foo + 'a { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `dyn Foo` implements `Deref>` which conflicts with supertrait `Bar` -... -LL | type Target = dyn Bar + 'a; - | -------------------------------- target type is a supertrait of `dyn Foo` - | - = warning: this will change its meaning in a future release! - = note: for more information, see issue #89460 -note: the lint level is defined here - --> $DIR/deref-upcast-behavioral-change.rs:1:9 - | -LL | #![deny(deref_into_dyn_supertrait)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/traits/trait-upcasting/deref-upcast-shadowing-lint.rs b/tests/ui/traits/trait-upcasting/deref-upcast-shadowing-lint.rs new file mode 100644 index 0000000000000..ab84527edcff9 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/deref-upcast-shadowing-lint.rs @@ -0,0 +1,17 @@ +//@ check-pass +#![warn(deref_into_dyn_supertrait)] +use std::ops::Deref; + +trait Bar {} +trait Foo: Bar {} + +impl<'a> Deref for dyn Foo + 'a { + //~^ warn: this `Deref` implementation is covered by an implicit supertrait coercion + type Target = dyn Bar + 'a; + + fn deref(&self) -> &Self::Target { + todo!() + } +} + +fn main() {} diff --git a/tests/ui/traits/trait-upcasting/deref-upcast-shadowing-lint.stderr b/tests/ui/traits/trait-upcasting/deref-upcast-shadowing-lint.stderr new file mode 100644 index 0000000000000..0d7f957a50ebd --- /dev/null +++ b/tests/ui/traits/trait-upcasting/deref-upcast-shadowing-lint.stderr @@ -0,0 +1,17 @@ +warning: this `Deref` implementation is covered by an implicit supertrait coercion + --> $DIR/deref-upcast-shadowing-lint.rs:8:1 + | +LL | impl<'a> Deref for dyn Foo + 'a { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `dyn Foo` implements `Deref>` which conflicts with supertrait `Bar` +LL | +LL | type Target = dyn Bar + 'a; + | -------------------------------- target type is a supertrait of `dyn Foo` + | +note: the lint level is defined here + --> $DIR/deref-upcast-shadowing-lint.rs:2:9 + | +LL | #![warn(deref_into_dyn_supertrait)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: 1 warning emitted + diff --git a/tests/ui/traits/trait-upcasting/diamond.rs b/tests/ui/traits/trait-upcasting/diamond.rs index 303c99b67bd00..c3f15a6d5822b 100644 --- a/tests/ui/traits/trait-upcasting/diamond.rs +++ b/tests/ui/traits/trait-upcasting/diamond.rs @@ -1,7 +1,5 @@ //@ run-pass -#![feature(trait_upcasting)] - trait Foo: PartialEq + std::fmt::Debug + Send + Sync { fn a(&self) -> i32 { 10 diff --git a/tests/ui/traits/trait-upcasting/fewer-associated.rs b/tests/ui/traits/trait-upcasting/fewer-associated.rs index 45e8673b85955..c2ea2a09d3a22 100644 --- a/tests/ui/traits/trait-upcasting/fewer-associated.rs +++ b/tests/ui/traits/trait-upcasting/fewer-associated.rs @@ -4,8 +4,6 @@ //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -#![feature(trait_upcasting)] - trait A: B { type Assoc; } diff --git a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.rs b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.rs index c4c070e49fdb7..9f9bf21b5a991 100644 --- a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.rs +++ b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.rs @@ -6,8 +6,6 @@ // Check that we are able to instantiate a binder during trait upcasting, // and that it doesn't cause any issues with codegen either. -#![feature(trait_upcasting)] - trait Supertrait<'a, 'b> {} trait Subtrait<'a, 'b>: Supertrait<'a, 'b> {} diff --git a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.rs b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.rs index 2cf6fc75e7779..af2594b95f3d2 100644 --- a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.rs +++ b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.rs @@ -5,7 +5,7 @@ // We previously wrongly instantiated binders during trait upcasting, // allowing the super trait to be more generic than the sub trait. // This was unsound. -#![feature(trait_upcasting)] + trait Supertrait<'a, 'b> { fn cast(&self, x: &'a str) -> &'b str; } diff --git a/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.current.stderr b/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.current.stderr index 28be189ff1e1c..596380b0dbb99 100644 --- a/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.current.stderr +++ b/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.current.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/illegal-upcast-from-impl.rs:17:66 + --> $DIR/illegal-upcast-from-impl.rs:15:66 | LL | fn illegal(x: &dyn Sub) -> &dyn Super { x } | ----------------------- ^ expected trait `Super`, found trait `Sub` diff --git a/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.next.stderr b/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.next.stderr index 28be189ff1e1c..596380b0dbb99 100644 --- a/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.next.stderr +++ b/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.next.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/illegal-upcast-from-impl.rs:17:66 + --> $DIR/illegal-upcast-from-impl.rs:15:66 | LL | fn illegal(x: &dyn Sub) -> &dyn Super { x } | ----------------------- ^ expected trait `Super`, found trait `Sub` diff --git a/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.rs b/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.rs index 0c771db412157..43ec4ece42977 100644 --- a/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.rs +++ b/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.rs @@ -2,8 +2,6 @@ //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -#![feature(trait_upcasting)] - trait Super { type Assoc; } diff --git a/tests/ui/traits/trait-upcasting/illegal-upcast-to-impl-opaque.rs b/tests/ui/traits/trait-upcasting/illegal-upcast-to-impl-opaque.rs index d0418e75fab21..2760c1696b56d 100644 --- a/tests/ui/traits/trait-upcasting/illegal-upcast-to-impl-opaque.rs +++ b/tests/ui/traits/trait-upcasting/illegal-upcast-to-impl-opaque.rs @@ -9,8 +9,6 @@ //@[next] rustc-env:RUST_BACKTRACE=0 //@ check-pass -#![feature(trait_upcasting)] - trait Super { type Assoc; } diff --git a/tests/ui/traits/trait-upcasting/impossible-method-modulo-binders-2.rs b/tests/ui/traits/trait-upcasting/impossible-method-modulo-binders-2.rs new file mode 100644 index 0000000000000..c03b5145aa73b --- /dev/null +++ b/tests/ui/traits/trait-upcasting/impossible-method-modulo-binders-2.rs @@ -0,0 +1,39 @@ +//@ build-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +// Regression test for #135462. +#![allow(coherence_leak_check)] + +type A = fn(&'static ()); +type B = fn(&()); + +trait Bound: From> { +} +impl Bound for String {} + +trait Trt { + fn __(&self, x: T) where T: Bound { + T::from(()); + } +} + +impl Trt for S {} + +type GetAssoc = ::Ty; + +trait WithAssoc { + type Ty; +} + +impl WithAssoc for B { + type Ty = String; +} + +impl WithAssoc for A { + type Ty = (); +} + +fn main() { + let x: &'static dyn Trt = &(); +} diff --git a/tests/ui/traits/trait-upcasting/impossible-method-modulo-binders.rs b/tests/ui/traits/trait-upcasting/impossible-method-modulo-binders.rs new file mode 100644 index 0000000000000..63ad1c0a06082 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/impossible-method-modulo-binders.rs @@ -0,0 +1,40 @@ +//@ build-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +trait Foo {} +impl Foo for fn(&'static ()) {} + +trait Bar { + type Assoc: Default; +} +impl Bar for T { + type Assoc = usize; +} +impl Bar for fn(&()) { + type Assoc = (); +} + +fn needs_foo() -> usize { + needs_bar::() +} + +fn needs_bar() -> ::Assoc { + Default::default() +} + +trait Evil { + fn bad(&self) + where + T: Foo, + { + needs_foo::(); + } +} + +impl Evil for () {} + +fn main() { + let x: &dyn Evil = &(); +} diff --git a/tests/ui/traits/trait-upcasting/inference-behavior-change-deref.rs b/tests/ui/traits/trait-upcasting/inference-behavior-change-deref.rs index 79fb643eacd01..0103aaa2ac996 100644 --- a/tests/ui/traits/trait-upcasting/inference-behavior-change-deref.rs +++ b/tests/ui/traits/trait-upcasting/inference-behavior-change-deref.rs @@ -1,6 +1,3 @@ -#![deny(deref_into_dyn_supertrait)] -#![feature(trait_upcasting)] // remove this and the test compiles - use std::ops::Deref; trait Bar {} @@ -32,5 +29,5 @@ fn main() { let x: &dyn Foo = &(); let y = take_dyn(x); let z: u32 = y; - //~^ ERROR mismatched types + //~^ error: mismatched types } diff --git a/tests/ui/traits/trait-upcasting/inference-behavior-change-deref.stderr b/tests/ui/traits/trait-upcasting/inference-behavior-change-deref.stderr index 6b6a26d1593fb..45eabbc723a6b 100644 --- a/tests/ui/traits/trait-upcasting/inference-behavior-change-deref.stderr +++ b/tests/ui/traits/trait-upcasting/inference-behavior-change-deref.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/inference-behavior-change-deref.rs:34:18 + --> $DIR/inference-behavior-change-deref.rs:31:18 | LL | let z: u32 = y; | --- ^ expected `u32`, found `i32` diff --git a/tests/ui/traits/trait-upcasting/invalid-upcast.rs b/tests/ui/traits/trait-upcasting/invalid-upcast.rs index e634bbd5ac6f5..9269a5e3e9ff6 100644 --- a/tests/ui/traits/trait-upcasting/invalid-upcast.rs +++ b/tests/ui/traits/trait-upcasting/invalid-upcast.rs @@ -1,5 +1,3 @@ -#![feature(trait_upcasting)] - trait Foo { fn a(&self) -> i32 { 10 diff --git a/tests/ui/traits/trait-upcasting/invalid-upcast.stderr b/tests/ui/traits/trait-upcasting/invalid-upcast.stderr index 3aa21ee3dddfb..e70b99d28c7e4 100644 --- a/tests/ui/traits/trait-upcasting/invalid-upcast.stderr +++ b/tests/ui/traits/trait-upcasting/invalid-upcast.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/invalid-upcast.rs:53:35 + --> $DIR/invalid-upcast.rs:51:35 | LL | let _: &dyn std::fmt::Debug = baz; | -------------------- ^^^ expected trait `Debug`, found trait `Baz` @@ -10,7 +10,7 @@ LL | let _: &dyn std::fmt::Debug = baz; found reference `&dyn Baz` error[E0308]: mismatched types - --> $DIR/invalid-upcast.rs:55:24 + --> $DIR/invalid-upcast.rs:53:24 | LL | let _: &dyn Send = baz; | --------- ^^^ expected trait `Send`, found trait `Baz` @@ -21,7 +21,7 @@ LL | let _: &dyn Send = baz; found reference `&dyn Baz` error[E0308]: mismatched types - --> $DIR/invalid-upcast.rs:57:24 + --> $DIR/invalid-upcast.rs:55:24 | LL | let _: &dyn Sync = baz; | --------- ^^^ expected trait `Sync`, found trait `Baz` @@ -32,7 +32,7 @@ LL | let _: &dyn Sync = baz; found reference `&dyn Baz` error[E0308]: mismatched types - --> $DIR/invalid-upcast.rs:60:25 + --> $DIR/invalid-upcast.rs:58:25 | LL | let bar: &dyn Bar = baz; | -------- ^^^ expected trait `Bar`, found trait `Baz` @@ -43,7 +43,7 @@ LL | let bar: &dyn Bar = baz; found reference `&dyn Baz` error[E0308]: mismatched types - --> $DIR/invalid-upcast.rs:62:35 + --> $DIR/invalid-upcast.rs:60:35 | LL | let _: &dyn std::fmt::Debug = bar; | -------------------- ^^^ expected trait `Debug`, found trait `Bar` @@ -54,7 +54,7 @@ LL | let _: &dyn std::fmt::Debug = bar; found reference `&dyn Bar` error[E0308]: mismatched types - --> $DIR/invalid-upcast.rs:64:24 + --> $DIR/invalid-upcast.rs:62:24 | LL | let _: &dyn Send = bar; | --------- ^^^ expected trait `Send`, found trait `Bar` @@ -65,7 +65,7 @@ LL | let _: &dyn Send = bar; found reference `&dyn Bar` error[E0308]: mismatched types - --> $DIR/invalid-upcast.rs:66:24 + --> $DIR/invalid-upcast.rs:64:24 | LL | let _: &dyn Sync = bar; | --------- ^^^ expected trait `Sync`, found trait `Bar` @@ -76,7 +76,7 @@ LL | let _: &dyn Sync = bar; found reference `&dyn Bar` error[E0308]: mismatched types - --> $DIR/invalid-upcast.rs:69:25 + --> $DIR/invalid-upcast.rs:67:25 | LL | let foo: &dyn Foo = baz; | -------- ^^^ expected trait `Foo`, found trait `Baz` @@ -87,7 +87,7 @@ LL | let foo: &dyn Foo = baz; found reference `&dyn Baz` error[E0308]: mismatched types - --> $DIR/invalid-upcast.rs:71:35 + --> $DIR/invalid-upcast.rs:69:35 | LL | let _: &dyn std::fmt::Debug = foo; | -------------------- ^^^ expected trait `Debug`, found trait `Foo` @@ -98,7 +98,7 @@ LL | let _: &dyn std::fmt::Debug = foo; found reference `&dyn Foo` error[E0308]: mismatched types - --> $DIR/invalid-upcast.rs:73:24 + --> $DIR/invalid-upcast.rs:71:24 | LL | let _: &dyn Send = foo; | --------- ^^^ expected trait `Send`, found trait `Foo` @@ -109,7 +109,7 @@ LL | let _: &dyn Send = foo; found reference `&dyn Foo` error[E0308]: mismatched types - --> $DIR/invalid-upcast.rs:75:24 + --> $DIR/invalid-upcast.rs:73:24 | LL | let _: &dyn Sync = foo; | --------- ^^^ expected trait `Sync`, found trait `Foo` @@ -120,7 +120,7 @@ LL | let _: &dyn Sync = foo; found reference `&dyn Foo` error[E0308]: mismatched types - --> $DIR/invalid-upcast.rs:78:25 + --> $DIR/invalid-upcast.rs:76:25 | LL | let foo: &dyn Foo = bar; | -------- ^^^ expected trait `Foo`, found trait `Bar` @@ -131,7 +131,7 @@ LL | let foo: &dyn Foo = bar; found reference `&dyn Bar` error[E0308]: mismatched types - --> $DIR/invalid-upcast.rs:80:35 + --> $DIR/invalid-upcast.rs:78:35 | LL | let _: &dyn std::fmt::Debug = foo; | -------------------- ^^^ expected trait `Debug`, found trait `Foo` @@ -142,7 +142,7 @@ LL | let _: &dyn std::fmt::Debug = foo; found reference `&dyn Foo` error[E0308]: mismatched types - --> $DIR/invalid-upcast.rs:82:24 + --> $DIR/invalid-upcast.rs:80:24 | LL | let _: &dyn Send = foo; | --------- ^^^ expected trait `Send`, found trait `Foo` @@ -153,7 +153,7 @@ LL | let _: &dyn Send = foo; found reference `&dyn Foo` error[E0308]: mismatched types - --> $DIR/invalid-upcast.rs:84:24 + --> $DIR/invalid-upcast.rs:82:24 | LL | let _: &dyn Sync = foo; | --------- ^^^ expected trait `Sync`, found trait `Foo` diff --git a/tests/ui/traits/trait-upcasting/issue-11515-upcast-fn_mut-fn.rs b/tests/ui/traits/trait-upcasting/issue-11515-upcast-fn_mut-fn.rs index ef3d366c38139..8bf32efbe2e91 100644 --- a/tests/ui/traits/trait-upcasting/issue-11515-upcast-fn_mut-fn.rs +++ b/tests/ui/traits/trait-upcasting/issue-11515-upcast-fn_mut-fn.rs @@ -1,5 +1,4 @@ //@ run-pass -#![feature(trait_upcasting)] struct Test { func: Box, diff --git a/tests/ui/traits/trait-upcasting/issue-11515.current.stderr b/tests/ui/traits/trait-upcasting/issue-11515.current.stderr deleted file mode 100644 index 8ce7d46012fd6..0000000000000 --- a/tests/ui/traits/trait-upcasting/issue-11515.current.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0658]: cannot cast `dyn Fn()` to `dyn FnMut()`, trait upcasting coercion is experimental - --> $DIR/issue-11515.rs:11:38 - | -LL | let test = Box::new(Test { func: closure }); - | ^^^^^^^ - | - = note: see issue #65991 for more information - = help: add `#![feature(trait_upcasting)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: required when coercing `Box<(dyn Fn() + 'static)>` into `Box<(dyn FnMut() + 'static)>` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/traits/trait-upcasting/issue-11515.next.stderr b/tests/ui/traits/trait-upcasting/issue-11515.next.stderr deleted file mode 100644 index 8ce7d46012fd6..0000000000000 --- a/tests/ui/traits/trait-upcasting/issue-11515.next.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0658]: cannot cast `dyn Fn()` to `dyn FnMut()`, trait upcasting coercion is experimental - --> $DIR/issue-11515.rs:11:38 - | -LL | let test = Box::new(Test { func: closure }); - | ^^^^^^^ - | - = note: see issue #65991 for more information - = help: add `#![feature(trait_upcasting)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: required when coercing `Box<(dyn Fn() + 'static)>` into `Box<(dyn FnMut() + 'static)>` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/traits/trait-upcasting/issue-11515.rs b/tests/ui/traits/trait-upcasting/issue-11515.rs index d251e804c3ea2..3b1f0a19cb9c4 100644 --- a/tests/ui/traits/trait-upcasting/issue-11515.rs +++ b/tests/ui/traits/trait-upcasting/issue-11515.rs @@ -1,3 +1,4 @@ +//@ check-pass //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver @@ -8,5 +9,5 @@ struct Test { fn main() { let closure: Box = Box::new(|| ()); - let test = Box::new(Test { func: closure }); //~ ERROR trait upcasting coercion is experimental [E0658] + let test = Box::new(Test { func: closure }); } diff --git a/tests/ui/traits/trait-upcasting/lifetime.rs b/tests/ui/traits/trait-upcasting/lifetime.rs index ab006f8bedcea..a3cbfd777a0f9 100644 --- a/tests/ui/traits/trait-upcasting/lifetime.rs +++ b/tests/ui/traits/trait-upcasting/lifetime.rs @@ -1,7 +1,5 @@ //@ run-pass -#![feature(trait_upcasting)] - trait Foo: PartialEq + std::fmt::Debug + Send + Sync { fn a(&self) -> i32 { 10 diff --git a/tests/ui/traits/trait-upcasting/lifetime.stderr b/tests/ui/traits/trait-upcasting/lifetime.stderr index ca8f9cf63f3e7..589e9816d5adc 100644 --- a/tests/ui/traits/trait-upcasting/lifetime.stderr +++ b/tests/ui/traits/trait-upcasting/lifetime.stderr @@ -1,5 +1,5 @@ warning: methods `z` and `y` are never used - --> $DIR/lifetime.rs:10:8 + --> $DIR/lifetime.rs:8:8 | LL | trait Foo: PartialEq + std::fmt::Debug + Send + Sync { | --- methods in this trait @@ -13,7 +13,7 @@ LL | fn y(&self) -> i32 { = note: `#[warn(dead_code)]` on by default warning: method `w` is never used - --> $DIR/lifetime.rs:24:8 + --> $DIR/lifetime.rs:22:8 | LL | trait Bar: Foo { | --- method in this trait diff --git a/tests/ui/traits/trait-upcasting/migrate-lint-deny-regions.rs b/tests/ui/traits/trait-upcasting/migrate-lint-deny-regions.rs index da1a9cc2775d0..d33d3bfd5dc20 100644 --- a/tests/ui/traits/trait-upcasting/migrate-lint-deny-regions.rs +++ b/tests/ui/traits/trait-upcasting/migrate-lint-deny-regions.rs @@ -1,4 +1,5 @@ -#![deny(deref_into_dyn_supertrait)] +//@ check-pass +#![warn(deref_into_dyn_supertrait)] use std::ops::Deref; @@ -6,8 +7,7 @@ trait Bar<'a> {} trait Foo<'a>: Bar<'a> {} impl<'a> Deref for dyn Foo<'a> { - //~^ ERROR this `Deref` implementation is covered by an implicit supertrait coercion - //~| WARN this will change its meaning in a future release! + //~^ warn: this `Deref` implementation is covered by an implicit supertrait coercion type Target = dyn Bar<'a>; fn deref(&self) -> &Self::Target { diff --git a/tests/ui/traits/trait-upcasting/migrate-lint-deny-regions.stderr b/tests/ui/traits/trait-upcasting/migrate-lint-deny-regions.stderr index a5f3660d4bcb0..806c57e44a2bf 100644 --- a/tests/ui/traits/trait-upcasting/migrate-lint-deny-regions.stderr +++ b/tests/ui/traits/trait-upcasting/migrate-lint-deny-regions.stderr @@ -1,19 +1,17 @@ -error: this `Deref` implementation is covered by an implicit supertrait coercion - --> $DIR/migrate-lint-deny-regions.rs:8:1 +warning: this `Deref` implementation is covered by an implicit supertrait coercion + --> $DIR/migrate-lint-deny-regions.rs:9:1 | LL | impl<'a> Deref for dyn Foo<'a> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `dyn Foo<'_>` implements `Deref>` which conflicts with supertrait `Bar<'_>` -... +LL | LL | type Target = dyn Bar<'a>; | -------------------------- target type is a supertrait of `dyn Foo<'_>` | - = warning: this will change its meaning in a future release! - = note: for more information, see issue #89460 note: the lint level is defined here - --> $DIR/migrate-lint-deny-regions.rs:1:9 + --> $DIR/migrate-lint-deny-regions.rs:2:9 | -LL | #![deny(deref_into_dyn_supertrait)] +LL | #![warn(deref_into_dyn_supertrait)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 1 previous error +warning: 1 warning emitted diff --git a/tests/ui/traits/trait-upcasting/migrate-lint-deny.rs b/tests/ui/traits/trait-upcasting/migrate-lint-deny.rs deleted file mode 100644 index 926b3649e01b3..0000000000000 --- a/tests/ui/traits/trait-upcasting/migrate-lint-deny.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![deny(deref_into_dyn_supertrait)] - -use std::ops::Deref; - -// issue 89190 -trait A {} -trait B: A {} - -impl<'a> Deref for dyn 'a + B { - //~^ ERROR this `Deref` implementation is covered by an implicit supertrait coercion - //~| WARN this will change its meaning in a future release! - - type Target = dyn A; - fn deref(&self) -> &Self::Target { - todo!() - } -} - -fn take_a(_: &dyn A) {} - -fn whoops(b: &dyn B) { - take_a(b) -} - -fn main() {} diff --git a/tests/ui/traits/trait-upcasting/migrate-lint-deny.stderr b/tests/ui/traits/trait-upcasting/migrate-lint-deny.stderr deleted file mode 100644 index 29997a9b3d9c3..0000000000000 --- a/tests/ui/traits/trait-upcasting/migrate-lint-deny.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error: this `Deref` implementation is covered by an implicit supertrait coercion - --> $DIR/migrate-lint-deny.rs:9:1 - | -LL | impl<'a> Deref for dyn 'a + B { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `dyn B` implements `Deref` which conflicts with supertrait `A` -... -LL | type Target = dyn A; - | -------------------- target type is a supertrait of `dyn B` - | - = warning: this will change its meaning in a future release! - = note: for more information, see issue #89460 -note: the lint level is defined here - --> $DIR/migrate-lint-deny.rs:1:9 - | -LL | #![deny(deref_into_dyn_supertrait)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/traits/trait-upcasting/migrate-lint-different-substs.rs b/tests/ui/traits/trait-upcasting/migrate-lint-different-substs.rs index 8e62c4b95b06c..20806f22886c0 100644 --- a/tests/ui/traits/trait-upcasting/migrate-lint-different-substs.rs +++ b/tests/ui/traits/trait-upcasting/migrate-lint-different-substs.rs @@ -1,4 +1,5 @@ //@ check-pass +#![warn(deref_into_dyn_supertrait)] use std::ops::Deref; @@ -9,8 +10,7 @@ trait Foo: Bar { } impl<'a> Deref for dyn Foo + 'a { - //~^ WARN this `Deref` implementation is covered by an implicit supertrait coercion - //~| WARN this will change its meaning in a future release! + //~^ warn: this `Deref` implementation is covered by an implicit supertrait coercion type Target = dyn Bar + 'a; fn deref(&self) -> &Self::Target { diff --git a/tests/ui/traits/trait-upcasting/migrate-lint-different-substs.stderr b/tests/ui/traits/trait-upcasting/migrate-lint-different-substs.stderr index 6245da5a17604..86cff5233ff49 100644 --- a/tests/ui/traits/trait-upcasting/migrate-lint-different-substs.stderr +++ b/tests/ui/traits/trait-upcasting/migrate-lint-different-substs.stderr @@ -1,15 +1,17 @@ warning: this `Deref` implementation is covered by an implicit supertrait coercion - --> $DIR/migrate-lint-different-substs.rs:11:1 + --> $DIR/migrate-lint-different-substs.rs:12:1 | LL | impl<'a> Deref for dyn Foo + 'a { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `dyn Foo` implements `Deref>` which conflicts with supertrait `Bar` -... +LL | LL | type Target = dyn Bar + 'a; | -------------------------------- target type is a supertrait of `dyn Foo` | - = warning: this will change its meaning in a future release! - = note: for more information, see issue #89460 - = note: `#[warn(deref_into_dyn_supertrait)]` on by default +note: the lint level is defined here + --> $DIR/migrate-lint-different-substs.rs:2:9 + | +LL | #![warn(deref_into_dyn_supertrait)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: 1 warning emitted diff --git a/tests/ui/traits/trait-upcasting/mono-impossible.rs b/tests/ui/traits/trait-upcasting/mono-impossible.rs new file mode 100644 index 0000000000000..f19f0538efa70 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/mono-impossible.rs @@ -0,0 +1,24 @@ +//@ build-pass + +trait Supertrait { + fn method(&self) {} +} +impl Supertrait for () {} + +trait WithAssoc { + type Assoc; +} +trait Trait: Supertrait + Supertrait<()> {} + +fn upcast

(x: &dyn Trait

) -> &dyn Supertrait<()> { + x +} + +fn call

(x: &dyn Trait

) { + x.method(); +} + +fn main() { + println!("{:p}", upcast::<()> as *const ()); + println!("{:p}", call::<()> as *const ()); +} diff --git a/tests/ui/traits/trait-upcasting/multiple-occurrence-ambiguousity.rs b/tests/ui/traits/trait-upcasting/multiple-occurrence-ambiguousity.rs index 754437a3bec77..0bf0d14c259da 100644 --- a/tests/ui/traits/trait-upcasting/multiple-occurrence-ambiguousity.rs +++ b/tests/ui/traits/trait-upcasting/multiple-occurrence-ambiguousity.rs @@ -1,5 +1,4 @@ //@ check-fail -#![feature(trait_upcasting)] trait Bar { fn bar(&self, _: T) {} diff --git a/tests/ui/traits/trait-upcasting/multiple-occurrence-ambiguousity.stderr b/tests/ui/traits/trait-upcasting/multiple-occurrence-ambiguousity.stderr index 70ba1fcaf3838..c888ccc1ebb0d 100644 --- a/tests/ui/traits/trait-upcasting/multiple-occurrence-ambiguousity.stderr +++ b/tests/ui/traits/trait-upcasting/multiple-occurrence-ambiguousity.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/multiple-occurrence-ambiguousity.rs:20:26 + --> $DIR/multiple-occurrence-ambiguousity.rs:19:26 | LL | let t: &dyn Bar<_> = s; | ----------- ^ expected trait `Bar`, found trait `Foo` diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.rs b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.rs new file mode 100644 index 0000000000000..796ddec46ac7a --- /dev/null +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.rs @@ -0,0 +1,27 @@ +#![feature(rustc_attrs)] + +// Test for . + +trait Supertrait { + fn _print_numbers(&self, mem: &[usize; 100]) { + } +} +impl Supertrait for () {} + +trait Trait: Supertrait + Supertrait { + fn say_hello(&self, _: &usize) { + } +} +impl Trait for () {} + +// We should observe compatibility between these two vtables. + +#[rustc_dump_vtable] +type First = dyn for<'a> Trait<&'static (), &'a ()>; +//~^ ERROR vtable entries + +#[rustc_dump_vtable] +type Second = dyn Trait<&'static (), &'static ()>; +//~^ ERROR vtable entries + +fn main() {} diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr new file mode 100644 index 0000000000000..24fa1650ca14c --- /dev/null +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder-vtable.stderr @@ -0,0 +1,26 @@ +error: vtable entries: [ + MetadataDropInPlace, + MetadataSize, + MetadataAlign, + Method( Trait<&(), &'a ()> as Supertrait<&()>>::_print_numbers - shim(reify)), + Method( Trait<&(), &'a ()> as Trait<&(), &()>>::say_hello - shim(reify)), + ] + --> $DIR/multiple-supertraits-modulo-binder-vtable.rs:20:1 + | +LL | type First = dyn for<'a> Trait<&'static (), &'a ()>; + | ^^^^^^^^^^ + +error: vtable entries: [ + MetadataDropInPlace, + MetadataSize, + MetadataAlign, + Method( as Supertrait<&()>>::_print_numbers - shim(reify)), + Method( as Trait<&(), &()>>::say_hello - shim(reify)), + ] + --> $DIR/multiple-supertraits-modulo-binder-vtable.rs:24:1 + | +LL | type Second = dyn Trait<&'static (), &'static ()>; + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.rs b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.rs new file mode 100644 index 0000000000000..a0bb1c57adbc0 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.rs @@ -0,0 +1,24 @@ +// Test for . +// +//@ run-pass +//@ check-run-results + +trait Supertrait { + fn _print_numbers(&self, mem: &[usize; 100]) { + println!("{mem:?}"); + } +} +impl Supertrait for () {} + +trait Trait: Supertrait + Supertrait { + fn say_hello(&self, _: &usize) { + println!("Hello!"); + } +} +impl Trait for () {} + +fn main() { + (&() as &'static dyn for<'a> Trait<&'static (), &'a ()> + as &'static dyn Trait<&'static (), &'static ()>) + .say_hello(&0); +} diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.run.stdout b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.run.stdout new file mode 100644 index 0000000000000..10ddd6d257e01 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-binder.run.stdout @@ -0,0 +1 @@ +Hello! diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.rs b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.rs new file mode 100644 index 0000000000000..6b9ddf9bedcd5 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.rs @@ -0,0 +1,36 @@ +#![feature(rustc_attrs)] + +// Test for . + +trait Supertrait { + fn _print_numbers(&self, mem: &[usize; 100]) { + println!("{mem:?}"); + } +} +impl Supertrait for () {} + +trait Identity { + type Selff; +} +impl Identity for Selff { + type Selff = Selff; +} + +trait Middle: Supertrait<()> + Supertrait { + fn say_hello(&self, _: &usize) { + println!("Hello!"); + } +} +impl Middle for () {} + +trait Trait: Middle<<() as Identity>::Selff> {} + +#[rustc_dump_vtable] +impl Trait for () {} +//~^ ERROR vtable entries + +#[rustc_dump_vtable] +type Virtual = dyn Middle<()>; +//~^ ERROR vtable entries + +fn main() {} diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr new file mode 100644 index 0000000000000..04b1afae7beca --- /dev/null +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization-vtable.stderr @@ -0,0 +1,26 @@ +error: vtable entries: [ + MetadataDropInPlace, + MetadataSize, + MetadataAlign, + Method(<() as Supertrait<()>>::_print_numbers), + Method(<() as Middle<()>>::say_hello), + ] + --> $DIR/multiple-supertraits-modulo-normalization-vtable.rs:29:1 + | +LL | impl Trait for () {} + | ^^^^^^^^^^^^^^^^^ + +error: vtable entries: [ + MetadataDropInPlace, + MetadataSize, + MetadataAlign, + Method( as Supertrait<()>>::_print_numbers - shim(reify)), + Method( as Middle<()>>::say_hello - shim(reify)), + ] + --> $DIR/multiple-supertraits-modulo-normalization-vtable.rs:33:1 + | +LL | type Virtual = dyn Middle<()>; + | ^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.rs b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.rs new file mode 100644 index 0000000000000..08ec49971ac4a --- /dev/null +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.rs @@ -0,0 +1,32 @@ +// Test for . +// +//@ run-pass +//@ check-run-results + +trait Supertrait { + fn _print_numbers(&self, mem: &[usize; 100]) { + println!("{mem:?}"); + } +} +impl Supertrait for () {} + +trait Identity { + type Selff; +} +impl Identity for Selff { + type Selff = Selff; +} + +trait Middle: Supertrait<()> + Supertrait { + fn say_hello(&self, _: &usize) { + println!("Hello!"); + } +} +impl Middle for () {} + +trait Trait: Middle<<() as Identity>::Selff> {} +impl Trait for () {} + +fn main() { + (&() as &dyn Trait as &dyn Middle<()>).say_hello(&0); +} diff --git a/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.run.stdout b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.run.stdout new file mode 100644 index 0000000000000..10ddd6d257e01 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/multiple-supertraits-modulo-normalization.run.stdout @@ -0,0 +1 @@ +Hello! diff --git a/tests/ui/traits/trait-upcasting/normalization.rs b/tests/ui/traits/trait-upcasting/normalization.rs index ae5a6a5243c86..b714479961ff8 100644 --- a/tests/ui/traits/trait-upcasting/normalization.rs +++ b/tests/ui/traits/trait-upcasting/normalization.rs @@ -4,8 +4,6 @@ //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -#![feature(trait_upcasting)] - trait Mirror { type Assoc; } diff --git a/tests/ui/traits/trait-upcasting/prefer-lower-candidates.rs b/tests/ui/traits/trait-upcasting/prefer-lower-candidates.rs new file mode 100644 index 0000000000000..09557ba0260d5 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/prefer-lower-candidates.rs @@ -0,0 +1,27 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass + +// Ensure we don't have ambiguity when upcasting to two supertraits +// that are identical modulo normalization. + +trait Supertrait { + fn method(&self) {} +} +impl Supertrait for () {} + +trait Identity { + type Selff; +} +impl Identity for Selff { + type Selff = Selff; +} +trait Trait

: Supertrait<()> + Supertrait<

::Selff> {} + +impl

Trait

for () {} + +fn main() { + let x: &dyn Trait<()> = &(); + let x: &dyn Supertrait<()> = x; +} diff --git a/tests/ui/traits/trait-upcasting/replace-vptr.rs b/tests/ui/traits/trait-upcasting/replace-vptr.rs index 4829e3c5c1240..6ca5864669bc3 100644 --- a/tests/ui/traits/trait-upcasting/replace-vptr.rs +++ b/tests/ui/traits/trait-upcasting/replace-vptr.rs @@ -1,7 +1,5 @@ //@ run-pass -#![feature(trait_upcasting)] - trait A { fn foo_a(&self); //~ WARN method `foo_a` is never used } diff --git a/tests/ui/traits/trait-upcasting/replace-vptr.stderr b/tests/ui/traits/trait-upcasting/replace-vptr.stderr index 823094761b385..1a8bfd1bfa6e5 100644 --- a/tests/ui/traits/trait-upcasting/replace-vptr.stderr +++ b/tests/ui/traits/trait-upcasting/replace-vptr.stderr @@ -1,5 +1,5 @@ warning: method `foo_a` is never used - --> $DIR/replace-vptr.rs:6:8 + --> $DIR/replace-vptr.rs:4:8 | LL | trait A { | - method in this trait @@ -9,7 +9,7 @@ LL | fn foo_a(&self); = note: `#[warn(dead_code)]` on by default warning: method `foo_c` is never used - --> $DIR/replace-vptr.rs:14:8 + --> $DIR/replace-vptr.rs:12:8 | LL | trait C: A + B { | - method in this trait diff --git a/tests/ui/traits/trait-upcasting/struct.rs b/tests/ui/traits/trait-upcasting/struct.rs index 39da20259a043..51e6f006fbff9 100644 --- a/tests/ui/traits/trait-upcasting/struct.rs +++ b/tests/ui/traits/trait-upcasting/struct.rs @@ -1,7 +1,5 @@ //@ run-pass -#![feature(trait_upcasting)] - use std::rc::Rc; use std::sync::Arc; diff --git a/tests/ui/traits/trait-upcasting/subtrait-method.rs b/tests/ui/traits/trait-upcasting/subtrait-method.rs index 136d15af0e8b2..20277280440c5 100644 --- a/tests/ui/traits/trait-upcasting/subtrait-method.rs +++ b/tests/ui/traits/trait-upcasting/subtrait-method.rs @@ -1,5 +1,3 @@ -#![feature(trait_upcasting)] - trait Foo: PartialEq + std::fmt::Debug + Send + Sync { fn a(&self) -> i32 { 10 diff --git a/tests/ui/traits/trait-upcasting/subtrait-method.stderr b/tests/ui/traits/trait-upcasting/subtrait-method.stderr index 0408be6986bd7..a16daa11d6f7a 100644 --- a/tests/ui/traits/trait-upcasting/subtrait-method.stderr +++ b/tests/ui/traits/trait-upcasting/subtrait-method.stderr @@ -1,87 +1,92 @@ error[E0599]: no method named `c` found for reference `&dyn Bar` in the current scope - --> $DIR/subtrait-method.rs:55:9 + --> $DIR/subtrait-method.rs:53:9 | LL | bar.c(); | ^ | = help: items from traits can only be used if the trait is implemented and in scope note: `Baz` defines an item `c`, perhaps you need to implement it - --> $DIR/subtrait-method.rs:27:1 + --> $DIR/subtrait-method.rs:25:1 | LL | trait Baz: Bar { | ^^^^^^^^^^^^^^ help: there is a method `a` with a similar name | -LL | bar.a(); - | ~ +LL - bar.c(); +LL + bar.a(); + | error[E0599]: no method named `b` found for reference `&dyn Foo` in the current scope - --> $DIR/subtrait-method.rs:59:9 + --> $DIR/subtrait-method.rs:57:9 | LL | foo.b(); | ^ | = help: items from traits can only be used if the trait is implemented and in scope note: `Bar` defines an item `b`, perhaps you need to implement it - --> $DIR/subtrait-method.rs:17:1 + --> $DIR/subtrait-method.rs:15:1 | LL | trait Bar: Foo { | ^^^^^^^^^^^^^^ help: there is a method `a` with a similar name | -LL | foo.a(); - | ~ +LL - foo.b(); +LL + foo.a(); + | error[E0599]: no method named `c` found for reference `&dyn Foo` in the current scope - --> $DIR/subtrait-method.rs:61:9 + --> $DIR/subtrait-method.rs:59:9 | LL | foo.c(); | ^ | = help: items from traits can only be used if the trait is implemented and in scope note: `Baz` defines an item `c`, perhaps you need to implement it - --> $DIR/subtrait-method.rs:27:1 + --> $DIR/subtrait-method.rs:25:1 | LL | trait Baz: Bar { | ^^^^^^^^^^^^^^ help: there is a method `a` with a similar name | -LL | foo.a(); - | ~ +LL - foo.c(); +LL + foo.a(); + | error[E0599]: no method named `b` found for reference `&dyn Foo` in the current scope - --> $DIR/subtrait-method.rs:65:9 + --> $DIR/subtrait-method.rs:63:9 | LL | foo.b(); | ^ | = help: items from traits can only be used if the trait is implemented and in scope note: `Bar` defines an item `b`, perhaps you need to implement it - --> $DIR/subtrait-method.rs:17:1 + --> $DIR/subtrait-method.rs:15:1 | LL | trait Bar: Foo { | ^^^^^^^^^^^^^^ help: there is a method `a` with a similar name | -LL | foo.a(); - | ~ +LL - foo.b(); +LL + foo.a(); + | error[E0599]: no method named `c` found for reference `&dyn Foo` in the current scope - --> $DIR/subtrait-method.rs:67:9 + --> $DIR/subtrait-method.rs:65:9 | LL | foo.c(); | ^ | = help: items from traits can only be used if the trait is implemented and in scope note: `Baz` defines an item `c`, perhaps you need to implement it - --> $DIR/subtrait-method.rs:27:1 + --> $DIR/subtrait-method.rs:25:1 | LL | trait Baz: Bar { | ^^^^^^^^^^^^^^ help: there is a method `a` with a similar name | -LL | foo.a(); - | ~ +LL - foo.c(); +LL + foo.a(); + | error: aborting due to 5 previous errors diff --git a/tests/ui/traits/trait-upcasting/supertraits-modulo-inner-binder.rs b/tests/ui/traits/trait-upcasting/supertraits-modulo-inner-binder.rs new file mode 100644 index 0000000000000..e65cb60d993d9 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/supertraits-modulo-inner-binder.rs @@ -0,0 +1,28 @@ +//@ run-pass + +trait Super { + fn call(&self) + where + U: HigherRanked, + { + } +} + +impl Super for () {} + +trait HigherRanked {} +impl HigherRanked for for<'a> fn(&'a ()) {} + +trait Unimplemented {} +impl HigherRanked for T {} + +trait Sub: Super + Super fn(&'a ())> {} +impl Sub for () {} + +fn main() { + let a: &dyn Sub = &(); + // `Super` and `Super fn(&'a ())>` have different + // vtables and we need to upcast to the latter! + let b: &dyn Super fn(&'a ())> = a; + b.call(); +} diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-1.current.stderr b/tests/ui/traits/trait-upcasting/type-checking-test-1.current.stderr index 9a9ad9bbf7739..2f27d1522a6e9 100644 --- a/tests/ui/traits/trait-upcasting/type-checking-test-1.current.stderr +++ b/tests/ui/traits/trait-upcasting/type-checking-test-1.current.stderr @@ -1,5 +1,5 @@ error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar<_>` - --> $DIR/type-checking-test-1.rs:20:13 + --> $DIR/type-checking-test-1.rs:18:13 | LL | let _ = x as &dyn Bar<_>; // Ambiguous | ^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-1.next.stderr b/tests/ui/traits/trait-upcasting/type-checking-test-1.next.stderr index 9a9ad9bbf7739..2f27d1522a6e9 100644 --- a/tests/ui/traits/trait-upcasting/type-checking-test-1.next.stderr +++ b/tests/ui/traits/trait-upcasting/type-checking-test-1.next.stderr @@ -1,5 +1,5 @@ error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar<_>` - --> $DIR/type-checking-test-1.rs:20:13 + --> $DIR/type-checking-test-1.rs:18:13 | LL | let _ = x as &dyn Bar<_>; // Ambiguous | ^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-1.rs b/tests/ui/traits/trait-upcasting/type-checking-test-1.rs index fd902fd87e07c..b06f5e0452806 100644 --- a/tests/ui/traits/trait-upcasting/type-checking-test-1.rs +++ b/tests/ui/traits/trait-upcasting/type-checking-test-1.rs @@ -2,8 +2,6 @@ //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver -#![feature(trait_upcasting)] - trait Foo: Bar + Bar {} trait Bar { fn bar(&self) -> Option { diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-2.rs b/tests/ui/traits/trait-upcasting/type-checking-test-2.rs index b024b27750bc6..b4df0f5a90258 100644 --- a/tests/ui/traits/trait-upcasting/type-checking-test-2.rs +++ b/tests/ui/traits/trait-upcasting/type-checking-test-2.rs @@ -1,5 +1,3 @@ -#![feature(trait_upcasting)] - trait Foo: Bar + Bar {} trait Bar { fn bar(&self) -> Option { diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-2.stderr b/tests/ui/traits/trait-upcasting/type-checking-test-2.stderr index 3e59b9d33634a..f84ea93dc6792 100644 --- a/tests/ui/traits/trait-upcasting/type-checking-test-2.stderr +++ b/tests/ui/traits/trait-upcasting/type-checking-test-2.stderr @@ -1,11 +1,11 @@ error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar` - --> $DIR/type-checking-test-2.rs:19:13 + --> $DIR/type-checking-test-2.rs:17:13 | LL | let _ = x as &dyn Bar; // Error | ^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object error[E0605]: non-primitive cast: `&dyn Foo` as `&dyn Bar<_>` - --> $DIR/type-checking-test-2.rs:24:13 + --> $DIR/type-checking-test-2.rs:22:13 | LL | let a = x as &dyn Bar<_>; // Ambiguous | ^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-3.polonius.stderr b/tests/ui/traits/trait-upcasting/type-checking-test-3.polonius.stderr deleted file mode 100644 index e6cb6a753998f..0000000000000 --- a/tests/ui/traits/trait-upcasting/type-checking-test-3.polonius.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error: lifetime may not live long enough - --> $DIR/type-checking-test-3.rs:11:13 - | -LL | fn test_wrong1<'a>(x: &dyn Foo<'static>, y: &'a u32) { - | -- lifetime `'a` defined here -LL | let _ = x as &dyn Bar<'a>; // Error - | ^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` - -error: lifetime may not live long enough - --> $DIR/type-checking-test-3.rs:16:13 - | -LL | fn test_wrong2<'a>(x: &dyn Foo<'a>) { - | -- lifetime `'a` defined here -LL | let _ = x as &dyn Bar<'static>; // Error - | ^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` - -error: aborting due to 2 previous errors - diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-3.rs b/tests/ui/traits/trait-upcasting/type-checking-test-3.rs index b2db3a127974c..3685569d98d1a 100644 --- a/tests/ui/traits/trait-upcasting/type-checking-test-3.rs +++ b/tests/ui/traits/trait-upcasting/type-checking-test-3.rs @@ -1,5 +1,3 @@ -#![feature(trait_upcasting)] - trait Foo<'a>: Bar<'a> {} trait Bar<'a> {} diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-3.stderr b/tests/ui/traits/trait-upcasting/type-checking-test-3.stderr index 0a969b611e9d3..d80649638ae33 100644 --- a/tests/ui/traits/trait-upcasting/type-checking-test-3.stderr +++ b/tests/ui/traits/trait-upcasting/type-checking-test-3.stderr @@ -1,13 +1,13 @@ error: lifetime may not live long enough - --> $DIR/type-checking-test-3.rs:11:18 + --> $DIR/type-checking-test-3.rs:9:13 | LL | fn test_wrong1<'a>(x: &dyn Foo<'static>, y: &'a u32) { | -- lifetime `'a` defined here LL | let _ = x as &dyn Bar<'a>; // Error - | ^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + | ^^^^^^^^^^^^^^^^^ cast requires that `'a` must outlive `'static` error: lifetime may not live long enough - --> $DIR/type-checking-test-3.rs:16:18 + --> $DIR/type-checking-test-3.rs:14:18 | LL | fn test_wrong2<'a>(x: &dyn Foo<'a>) { | -- lifetime `'a` defined here diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-4.polonius.stderr b/tests/ui/traits/trait-upcasting/type-checking-test-4.polonius.stderr deleted file mode 100644 index 8d506e5807ece..0000000000000 --- a/tests/ui/traits/trait-upcasting/type-checking-test-4.polonius.stderr +++ /dev/null @@ -1,52 +0,0 @@ -error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:15:13 - | -LL | fn test_wrong1<'a>(x: &dyn Foo<'static>, y: &'a u32) { - | -- lifetime `'a` defined here -LL | let _ = x as &dyn Bar<'static, 'a>; // Error - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` - -error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:20:13 - | -LL | fn test_wrong2<'a>(x: &dyn Foo<'static>, y: &'a u32) { - | -- lifetime `'a` defined here -LL | let _ = x as &dyn Bar<'a, 'static>; // Error - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` - -error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:26:5 - | -LL | fn test_wrong3<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> { - | -- lifetime `'a` defined here -LL | let y = x as &dyn Bar<'_, '_>; -LL | y.get_b() // ERROR - | ^^^^^^^^^ returning this value requires that `'a` must outlive `'static` - -error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:31:5 - | -LL | fn test_wrong4<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> { - | -- lifetime `'a` defined here -LL | <_ as Bar>::get_b(x) // ERROR - | ^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static` - -error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:36:5 - | -LL | fn test_wrong5<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> { - | -- lifetime `'a` defined here -LL | <_ as Bar<'_, '_>>::get_b(x) // ERROR - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static` - -error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:44:5 - | -LL | fn test_wrong6<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> { - | -- lifetime `'a` defined here -... -LL | z.get_b() // ERROR - | ^^^^^^^^^ returning this value requires that `'a` must outlive `'static` - -error: aborting due to 6 previous errors - diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-4.rs b/tests/ui/traits/trait-upcasting/type-checking-test-4.rs index 01759ec7a93c6..20de722ee9b7c 100644 --- a/tests/ui/traits/trait-upcasting/type-checking-test-4.rs +++ b/tests/ui/traits/trait-upcasting/type-checking-test-4.rs @@ -1,5 +1,3 @@ -#![feature(trait_upcasting)] - trait Foo<'a>: Bar<'a, 'a> {} trait Bar<'a, 'b> { fn get_b(&self) -> Option<&'a u32> { diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-4.stderr b/tests/ui/traits/trait-upcasting/type-checking-test-4.stderr index 090120a2327a8..55a5e4cd4971f 100644 --- a/tests/ui/traits/trait-upcasting/type-checking-test-4.stderr +++ b/tests/ui/traits/trait-upcasting/type-checking-test-4.stderr @@ -1,13 +1,13 @@ error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:19:18 + --> $DIR/type-checking-test-4.rs:17:13 | LL | fn test_wrong1<'a>(x: &dyn Foo<'static>, y: &'a u32) { | -- lifetime `'a` defined here LL | let _ = x as &dyn Bar<'static, 'a>; // Error - | ^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'a` must outlive `'static` error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:24:18 + --> $DIR/type-checking-test-4.rs:22:18 | LL | fn test_wrong2<'a>(x: &dyn Foo<'static>, y: &'a u32) { | -- lifetime `'a` defined here @@ -15,7 +15,7 @@ LL | let _ = x as &dyn Bar<'a, 'static>; // Error | ^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:30:5 + --> $DIR/type-checking-test-4.rs:28:5 | LL | fn test_wrong3<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> { | -- lifetime `'a` defined here @@ -24,7 +24,7 @@ LL | y.get_b() // ERROR | ^^^^^^^^^ returning this value requires that `'a` must outlive `'static` error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:35:5 + --> $DIR/type-checking-test-4.rs:33:5 | LL | fn test_wrong4<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> { | -- lifetime `'a` defined here @@ -32,7 +32,7 @@ LL | <_ as Bar>::get_b(x) // ERROR | ^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static` error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:40:5 + --> $DIR/type-checking-test-4.rs:38:5 | LL | fn test_wrong5<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> { | -- lifetime `'a` defined here @@ -40,7 +40,7 @@ LL | <_ as Bar<'_, '_>>::get_b(x) // ERROR | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static` error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:48:5 + --> $DIR/type-checking-test-4.rs:46:5 | LL | fn test_wrong6<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> { | -- lifetime `'a` defined here diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-opaques.rs b/tests/ui/traits/trait-upcasting/type-checking-test-opaques.rs index a3a1ce29465b1..ab3817da28b1e 100644 --- a/tests/ui/traits/trait-upcasting/type-checking-test-opaques.rs +++ b/tests/ui/traits/trait-upcasting/type-checking-test-opaques.rs @@ -1,4 +1,4 @@ -#![feature(trait_upcasting, type_alias_impl_trait)] +#![feature(type_alias_impl_trait)] //@ check-pass diff --git a/tests/ui/traits/trait-upcasting/upcast-defining-opaque.rs b/tests/ui/traits/trait-upcasting/upcast-defining-opaque.rs index 07f1549e177fb..0548eda046842 100644 --- a/tests/ui/traits/trait-upcasting/upcast-defining-opaque.rs +++ b/tests/ui/traits/trait-upcasting/upcast-defining-opaque.rs @@ -3,7 +3,7 @@ //@ ignore-compare-mode-next-solver (explicit revisions) //@check-pass -#![feature(trait_upcasting, type_alias_impl_trait)] +#![feature(type_alias_impl_trait)] trait Super { type Assoc; diff --git a/tests/ui/traits/trait-upcasting/upcast-through-struct-tail.current.stderr b/tests/ui/traits/trait-upcasting/upcast-through-struct-tail.current.stderr deleted file mode 100644 index 239f82791944a..0000000000000 --- a/tests/ui/traits/trait-upcasting/upcast-through-struct-tail.current.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0658]: cannot cast `dyn A` to `dyn B`, trait upcasting coercion is experimental - --> $DIR/upcast-through-struct-tail.rs:11:5 - | -LL | x - | ^ - | - = note: see issue #65991 for more information - = help: add `#![feature(trait_upcasting)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: required when coercing `Box>` into `Box>` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/traits/trait-upcasting/upcast-through-struct-tail.next.stderr b/tests/ui/traits/trait-upcasting/upcast-through-struct-tail.next.stderr deleted file mode 100644 index 239f82791944a..0000000000000 --- a/tests/ui/traits/trait-upcasting/upcast-through-struct-tail.next.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0658]: cannot cast `dyn A` to `dyn B`, trait upcasting coercion is experimental - --> $DIR/upcast-through-struct-tail.rs:11:5 - | -LL | x - | ^ - | - = note: see issue #65991 for more information - = help: add `#![feature(trait_upcasting)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = note: required when coercing `Box>` into `Box>` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/traits/trait-upcasting/upcast-through-struct-tail.rs b/tests/ui/traits/trait-upcasting/upcast-through-struct-tail.rs index e40cca4e0a837..dd4d91f8d4f24 100644 --- a/tests/ui/traits/trait-upcasting/upcast-through-struct-tail.rs +++ b/tests/ui/traits/trait-upcasting/upcast-through-struct-tail.rs @@ -1,3 +1,4 @@ +//@ check-pass //@ revisions: current next //@ ignore-compare-mode-next-solver (explicit revisions) //@[next] compile-flags: -Znext-solver @@ -9,7 +10,6 @@ trait B {} fn test<'a>(x: Box>) -> Box> { x - //~^ ERROR cannot cast `dyn A` to `dyn B`, trait upcasting coercion is experimental } fn main() {} diff --git a/tests/ui/traits/unconstrained-projection-normalization-2.current.stderr b/tests/ui/traits/unconstrained-projection-normalization-2.current.stderr new file mode 100644 index 0000000000000..2bb389c6ec161 --- /dev/null +++ b/tests/ui/traits/unconstrained-projection-normalization-2.current.stderr @@ -0,0 +1,9 @@ +error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates + --> $DIR/unconstrained-projection-normalization-2.rs:14:6 + | +LL | impl Every for Thing { + | ^ unconstrained type parameter + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0207`. diff --git a/tests/ui/traits/unconstrained-projection-normalization-2.next.stderr b/tests/ui/traits/unconstrained-projection-normalization-2.next.stderr new file mode 100644 index 0000000000000..2bb389c6ec161 --- /dev/null +++ b/tests/ui/traits/unconstrained-projection-normalization-2.next.stderr @@ -0,0 +1,9 @@ +error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates + --> $DIR/unconstrained-projection-normalization-2.rs:14:6 + | +LL | impl Every for Thing { + | ^ unconstrained type parameter + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0207`. diff --git a/tests/ui/traits/unconstrained-projection-normalization-2.rs b/tests/ui/traits/unconstrained-projection-normalization-2.rs new file mode 100644 index 0000000000000..6b584c436c606 --- /dev/null +++ b/tests/ui/traits/unconstrained-projection-normalization-2.rs @@ -0,0 +1,21 @@ +// Make sure we don't ICE in `normalize_erasing_regions` when normalizing +// an associated type in an impl with unconstrained non-lifetime params. +// (This time in a function signature) + +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +struct Thing; + +pub trait Every { + type Assoc; +} +impl Every for Thing { +//~^ ERROR the type parameter `T` is not constrained + type Assoc = T; +} + +fn foo(_: ::Assoc) {} + +fn main() {} diff --git a/tests/ui/traits/unconstrained-projection-normalization.current.stderr b/tests/ui/traits/unconstrained-projection-normalization.current.stderr new file mode 100644 index 0000000000000..991f0e8ba666e --- /dev/null +++ b/tests/ui/traits/unconstrained-projection-normalization.current.stderr @@ -0,0 +1,9 @@ +error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates + --> $DIR/unconstrained-projection-normalization.rs:13:6 + | +LL | impl Every for Thing { + | ^ unconstrained type parameter + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0207`. diff --git a/tests/ui/traits/unconstrained-projection-normalization.next.stderr b/tests/ui/traits/unconstrained-projection-normalization.next.stderr new file mode 100644 index 0000000000000..991f0e8ba666e --- /dev/null +++ b/tests/ui/traits/unconstrained-projection-normalization.next.stderr @@ -0,0 +1,9 @@ +error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates + --> $DIR/unconstrained-projection-normalization.rs:13:6 + | +LL | impl Every for Thing { + | ^ unconstrained type parameter + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0207`. diff --git a/tests/ui/traits/unconstrained-projection-normalization.rs b/tests/ui/traits/unconstrained-projection-normalization.rs new file mode 100644 index 0000000000000..fa4ab7fec4c2f --- /dev/null +++ b/tests/ui/traits/unconstrained-projection-normalization.rs @@ -0,0 +1,20 @@ +// Make sure we don't ICE in `normalize_erasing_regions` when normalizing +// an associated type in an impl with unconstrained non-lifetime params. + +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +struct Thing; + +pub trait Every { + type Assoc; +} +impl Every for Thing { +//~^ ERROR the type parameter `T` is not constrained + type Assoc = T; +} + +static I: ::Assoc = 3; + +fn main() {} diff --git a/tests/ui/traits/unsize-goal-escaping-bounds.current.stderr b/tests/ui/traits/unsize-goal-escaping-bounds.current.stderr new file mode 100644 index 0000000000000..e63a0bf50b7aa --- /dev/null +++ b/tests/ui/traits/unsize-goal-escaping-bounds.current.stderr @@ -0,0 +1,19 @@ +error[E0277]: the trait bound `for<'a> (): Unsize<(dyn Trait + 'a)>` is not satisfied + --> $DIR/unsize-goal-escaping-bounds.rs:20:5 + | +LL | foo(); + | ^^^^^ the trait `for<'a> Unsize<(dyn Trait + 'a)>` is not implemented for `()` + | + = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information +note: required by a bound in `foo` + --> $DIR/unsize-goal-escaping-bounds.rs:15:17 + | +LL | fn foo() + | --- required by a bound in this function +LL | where +LL | for<'a> (): Unsize, + | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `foo` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/unsize-goal-escaping-bounds.rs b/tests/ui/traits/unsize-goal-escaping-bounds.rs new file mode 100644 index 0000000000000..fb25f7a423900 --- /dev/null +++ b/tests/ui/traits/unsize-goal-escaping-bounds.rs @@ -0,0 +1,22 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@[next] check-pass +//@ ignore-compare-mode-next-solver (explicit revisions) + +#![feature(unsize)] + +use std::marker::Unsize; + +trait Trait {} +impl Trait for () {} + +fn foo() +where + for<'a> (): Unsize, +{ +} + +fn main() { + foo(); + //[current]~^ ERROR the trait bound `for<'a> (): Unsize<(dyn Trait + 'a)>` is not satisfied +} diff --git a/tests/ui/traits/unspecified-self-in-trait-ref.stderr b/tests/ui/traits/unspecified-self-in-trait-ref.stderr index 22dceadc10de9..6f5ae786de6bc 100644 --- a/tests/ui/traits/unspecified-self-in-trait-ref.stderr +++ b/tests/ui/traits/unspecified-self-in-trait-ref.stderr @@ -97,7 +97,7 @@ LL | pub trait Bar { LL | let e = Bar::::lol(); | ^^^^^^^^^^^^ missing reference to `A` | - = note: because of the default `Self` reference, type parameters must be specified on object types + = note: because the parameter default references `Self`, the parameter must be specified on the object type error: aborting due to 5 previous errors; 5 warnings emitted diff --git a/tests/ui/traits/upcast_reorder.rs b/tests/ui/traits/upcast_reorder.rs index 55e6ad4c368d1..43fc1517a3b31 100644 --- a/tests/ui/traits/upcast_reorder.rs +++ b/tests/ui/traits/upcast_reorder.rs @@ -2,8 +2,6 @@ // // issue: -#![feature(trait_upcasting)] - trait Pollable { #[allow(unused)] fn poll(&self) {} diff --git a/tests/ui/traits/upcast_soundness_bug.rs b/tests/ui/traits/upcast_soundness_bug.rs index 0ddae1d1417c7..4c89fa135e788 100644 --- a/tests/ui/traits/upcast_soundness_bug.rs +++ b/tests/ui/traits/upcast_soundness_bug.rs @@ -1,4 +1,3 @@ -#![feature(trait_upcasting)] //@ check-fail // // issue: diff --git a/tests/ui/traits/upcast_soundness_bug.stderr b/tests/ui/traits/upcast_soundness_bug.stderr index 19d1a5e5926e0..f5fa6bdc9995b 100644 --- a/tests/ui/traits/upcast_soundness_bug.stderr +++ b/tests/ui/traits/upcast_soundness_bug.stderr @@ -1,5 +1,5 @@ error[E0606]: casting `*const dyn Trait` as `*const dyn Trait` is invalid - --> $DIR/upcast_soundness_bug.rs:59:13 + --> $DIR/upcast_soundness_bug.rs:58:13 | LL | let p = p as *const dyn Trait; // <- this is bad! | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/traits/vtable/multiple-markers.rs b/tests/ui/traits/vtable/multiple-markers.rs index 8a9e7a006cf55..8eba5135582d5 100644 --- a/tests/ui/traits/vtable/multiple-markers.rs +++ b/tests/ui/traits/vtable/multiple-markers.rs @@ -2,8 +2,7 @@ // // This test makes sure that multiple marker (method-less) traits can reuse the // same pointer for upcasting. -// -//@ build-fail + #![crate_type = "lib"] #![feature(rustc_attrs)] @@ -17,17 +16,13 @@ trait T { fn method(&self) {} } -#[rustc_dump_vtable] -trait A: M0 + M1 + M2 + T {} //~ error: vtable entries for ``: +trait A: M0 + M1 + M2 + T {} -#[rustc_dump_vtable] -trait B: M0 + M1 + T + M2 {} //~ error: vtable entries for ``: +trait B: M0 + M1 + T + M2 {} -#[rustc_dump_vtable] -trait C: M0 + T + M1 + M2 {} //~ error: vtable entries for ``: +trait C: M0 + T + M1 + M2 {} -#[rustc_dump_vtable] -trait D: T + M0 + M1 + M2 {} //~ error: vtable entries for ``: +trait D: T + M0 + M1 + M2 {} struct S; @@ -35,13 +30,21 @@ impl M0 for S {} impl M1 for S {} impl M2 for S {} impl T for S {} + +#[rustc_dump_vtable] impl A for S {} +//~^ ERROR vtable entries + +#[rustc_dump_vtable] impl B for S {} +//~^ ERROR vtable entries + +#[rustc_dump_vtable] impl C for S {} -impl D for S {} +//~^ ERROR vtable entries -pub fn require_vtables() { - fn require_vtables(_: &dyn A, _: &dyn B, _: &dyn C, _: &dyn D) {} +#[rustc_dump_vtable] +impl D for S {} +//~^ ERROR vtable entries - require_vtables(&S, &S, &S, &S) -} +fn main() {} diff --git a/tests/ui/traits/vtable/multiple-markers.stderr b/tests/ui/traits/vtable/multiple-markers.stderr index 36ac8b24eb530..35dd3c2516d8b 100644 --- a/tests/ui/traits/vtable/multiple-markers.stderr +++ b/tests/ui/traits/vtable/multiple-markers.stderr @@ -1,46 +1,46 @@ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, Method(::method), ] - --> $DIR/multiple-markers.rs:21:1 + --> $DIR/multiple-markers.rs:35:1 | -LL | trait A: M0 + M1 + M2 + T {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl A for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, Method(::method), ] - --> $DIR/multiple-markers.rs:24:1 + --> $DIR/multiple-markers.rs:39:1 | -LL | trait B: M0 + M1 + T + M2 {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl B for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, Method(::method), ] - --> $DIR/multiple-markers.rs:27:1 + --> $DIR/multiple-markers.rs:43:1 | -LL | trait C: M0 + T + M1 + M2 {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl C for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, Method(::method), ] - --> $DIR/multiple-markers.rs:30:1 + --> $DIR/multiple-markers.rs:47:1 | -LL | trait D: T + M0 + M1 + M2 {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | impl D for S {} + | ^^^^^^^^^^^^ error: aborting due to 4 previous errors diff --git a/tests/ui/traits/vtable/vtable-diamond.rs b/tests/ui/traits/vtable/vtable-diamond.rs index 2cfa86c526de3..56053f6d0260e 100644 --- a/tests/ui/traits/vtable/vtable-diamond.rs +++ b/tests/ui/traits/vtable/vtable-diamond.rs @@ -1,44 +1,37 @@ -//@ build-fail #![feature(rustc_attrs)] -#[rustc_dump_vtable] trait A { fn foo_a(&self) {} } -#[rustc_dump_vtable] trait B: A { fn foo_b(&self) {} } -#[rustc_dump_vtable] trait C: A { - //~^ error vtable fn foo_c(&self) {} } -#[rustc_dump_vtable] trait D: B + C { - //~^ error vtable fn foo_d(&self) {} } struct S; +#[rustc_dump_vtable] impl A for S {} +//~^ ERROR vtable entries + +#[rustc_dump_vtable] impl B for S {} -impl C for S {} -impl D for S {} +//~^ ERROR vtable entries -fn foo(d: &dyn D) { - d.foo_d(); -} +#[rustc_dump_vtable] +impl C for S {} +//~^ ERROR vtable entries -fn bar(d: &dyn C) { - d.foo_c(); -} +#[rustc_dump_vtable] +impl D for S {} +//~^ ERROR vtable entries -fn main() { - foo(&S); - bar(&S); -} +fn main() {} diff --git a/tests/ui/traits/vtable/vtable-diamond.stderr b/tests/ui/traits/vtable/vtable-diamond.stderr index f3718c5d852f0..4644bf339b17d 100644 --- a/tests/ui/traits/vtable/vtable-diamond.stderr +++ b/tests/ui/traits/vtable/vtable-diamond.stderr @@ -1,29 +1,52 @@ -error: vtable entries for ``: [ +error: vtable entries: [ + MetadataDropInPlace, + MetadataSize, + MetadataAlign, + Method(::foo_a), + ] + --> $DIR/vtable-diamond.rs:22:1 + | +LL | impl A for S {} + | ^^^^^^^^^^^^ + +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, Method(::foo_a), Method(::foo_b), + ] + --> $DIR/vtable-diamond.rs:26:1 + | +LL | impl B for S {} + | ^^^^^^^^^^^^ + +error: vtable entries: [ + MetadataDropInPlace, + MetadataSize, + MetadataAlign, + Method(::foo_a), Method(::foo_c), - TraitVPtr(), - Method(::foo_d), ] - --> $DIR/vtable-diamond.rs:21:1 + --> $DIR/vtable-diamond.rs:30:1 | -LL | trait D: B + C { - | ^^^^^^^^^^^^^^ +LL | impl C for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, Method(::foo_a), + Method(::foo_b), Method(::foo_c), + TraitVPtr(), + Method(::foo_d), ] - --> $DIR/vtable-diamond.rs:15:1 + --> $DIR/vtable-diamond.rs:34:1 | -LL | trait C: A { - | ^^^^^^^^^^ +LL | impl D for S {} + | ^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/traits/vtable/vtable-dyn-incompatible.rs b/tests/ui/traits/vtable/vtable-dyn-incompatible.rs index 64a8138bdcfb3..fb19dec4ace10 100644 --- a/tests/ui/traits/vtable/vtable-dyn-incompatible.rs +++ b/tests/ui/traits/vtable/vtable-dyn-incompatible.rs @@ -1,15 +1,16 @@ -//@ build-fail #![feature(rustc_attrs)] // Ensure that dyn-incompatible methods in Iterator does not generate // vtable entries. -#[rustc_dump_vtable] trait A: Iterator {} -//~^ error vtable impl A for T where T: Iterator {} +#[rustc_dump_vtable] +type Test = dyn A; +//~^ error vtable + fn foo(_a: &mut dyn A) { } diff --git a/tests/ui/traits/vtable/vtable-dyn-incompatible.stderr b/tests/ui/traits/vtable/vtable-dyn-incompatible.stderr index e442c3eac00ea..c80a763998b1e 100644 --- a/tests/ui/traits/vtable/vtable-dyn-incompatible.stderr +++ b/tests/ui/traits/vtable/vtable-dyn-incompatible.stderr @@ -1,16 +1,16 @@ -error: vtable entries for ` as A>`: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, - Method( as Iterator>::next), - Method( as Iterator>::size_hint), - Method( as Iterator>::advance_by), - Method( as Iterator>::nth), + Method( as Iterator>::next - shim(reify)), + Method( as Iterator>::size_hint - shim(reify)), + Method( as Iterator>::advance_by - shim(reify)), + Method( as Iterator>::nth - shim(reify)), ] - --> $DIR/vtable-dyn-incompatible.rs:8:1 + --> $DIR/vtable-dyn-incompatible.rs:11:1 | -LL | trait A: Iterator {} - | ^^^^^^^^^^^^^^^^^ +LL | type Test = dyn A; + | ^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/traits/vtable/vtable-multi-level.rs b/tests/ui/traits/vtable/vtable-multi-level.rs index bedbf84d30398..67bac49f9f667 100644 --- a/tests/ui/traits/vtable/vtable-multi-level.rs +++ b/tests/ui/traits/vtable/vtable-multi-level.rs @@ -1,4 +1,3 @@ -//@ build-fail #![feature(rustc_attrs)] // O --> G --> C --> A @@ -10,134 +9,126 @@ // |-> M --> K // \-> L -#[rustc_dump_vtable] trait A { - //~^ error vtable fn foo_a(&self) {} } -#[rustc_dump_vtable] trait B { - //~^ error vtable fn foo_b(&self) {} } -#[rustc_dump_vtable] trait C: A + B { - //~^ error vtable fn foo_c(&self) {} } -#[rustc_dump_vtable] trait D { - //~^ error vtable fn foo_d(&self) {} } -#[rustc_dump_vtable] trait E { - //~^ error vtable fn foo_e(&self) {} } -#[rustc_dump_vtable] trait F: D + E { - //~^ error vtable fn foo_f(&self) {} } -#[rustc_dump_vtable] trait G: C + F { fn foo_g(&self) {} } -#[rustc_dump_vtable] trait H { - //~^ error vtable fn foo_h(&self) {} } -#[rustc_dump_vtable] trait I { - //~^ error vtable fn foo_i(&self) {} } -#[rustc_dump_vtable] trait J: H + I { - //~^ error vtable fn foo_j(&self) {} } -#[rustc_dump_vtable] trait K { - //~^ error vtable fn foo_k(&self) {} } -#[rustc_dump_vtable] trait L { - //~^ error vtable fn foo_l(&self) {} } -#[rustc_dump_vtable] trait M: K + L { - //~^ error vtable fn foo_m(&self) {} } -#[rustc_dump_vtable] trait N: J + M { - //~^ error vtable fn foo_n(&self) {} } -#[rustc_dump_vtable] trait O: G + N { - //~^ error vtable fn foo_o(&self) {} } struct S; +#[rustc_dump_vtable] impl A for S {} +//~^ ERROR vtable entries + +#[rustc_dump_vtable] impl B for S {} +//~^ ERROR vtable entries + +#[rustc_dump_vtable] impl C for S {} +//~^ ERROR vtable entries + +#[rustc_dump_vtable] impl D for S {} +//~^ ERROR vtable entries + +#[rustc_dump_vtable] impl E for S {} +//~^ ERROR vtable entries + +#[rustc_dump_vtable] impl F for S {} +//~^ ERROR vtable entries + +#[rustc_dump_vtable] impl G for S {} +//~^ ERROR vtable entries + +#[rustc_dump_vtable] impl H for S {} +//~^ ERROR vtable entries + +#[rustc_dump_vtable] impl I for S {} +//~^ ERROR vtable entries + +#[rustc_dump_vtable] impl J for S {} +//~^ ERROR vtable entries + +#[rustc_dump_vtable] impl K for S {} +//~^ ERROR vtable entries + +#[rustc_dump_vtable] impl L for S {} +//~^ ERROR vtable entries + +#[rustc_dump_vtable] impl M for S {} +//~^ ERROR vtable entries + +#[rustc_dump_vtable] impl N for S {} -impl O for S {} +//~^ ERROR vtable entries -macro_rules! monomorphize_vtable { - ($trait:ident) => {{ - fn foo(_ : &dyn $trait) {} - foo(&S); - }} -} +#[rustc_dump_vtable] +impl O for S {} +//~^ ERROR vtable entries -fn main() { - monomorphize_vtable!(O); - - monomorphize_vtable!(A); - monomorphize_vtable!(B); - monomorphize_vtable!(C); - monomorphize_vtable!(D); - monomorphize_vtable!(E); - monomorphize_vtable!(F); - monomorphize_vtable!(H); - monomorphize_vtable!(I); - monomorphize_vtable!(J); - monomorphize_vtable!(K); - monomorphize_vtable!(L); - monomorphize_vtable!(M); - monomorphize_vtable!(N); -} +fn main() {} diff --git a/tests/ui/traits/vtable/vtable-multi-level.stderr b/tests/ui/traits/vtable/vtable-multi-level.stderr index c4389e23fc10f..961900aa3d291 100644 --- a/tests/ui/traits/vtable/vtable-multi-level.stderr +++ b/tests/ui/traits/vtable/vtable-multi-level.stderr @@ -1,134 +1,119 @@ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, Method(::foo_a), - Method(::foo_b), - TraitVPtr(), - Method(::foo_c), - Method(::foo_d), - TraitVPtr(), - Method(::foo_e), - TraitVPtr(), - Method(::foo_f), - TraitVPtr(), - Method(::foo_g), - Method(::foo_h), - TraitVPtr(), - Method(::foo_i), - TraitVPtr(), - Method(::foo_j), - TraitVPtr(), - Method(::foo_k), - TraitVPtr(), - Method(::foo_l), - TraitVPtr(), - Method(::foo_m), - TraitVPtr(), - Method(::foo_n), - TraitVPtr(), - Method(::foo_o), ] - --> $DIR/vtable-multi-level.rs:97:1 + --> $DIR/vtable-multi-level.rs:75:1 | -LL | trait O: G + N { - | ^^^^^^^^^^^^^^ +LL | impl A for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, - Method(::foo_a), + Method(::foo_b), ] - --> $DIR/vtable-multi-level.rs:14:1 + --> $DIR/vtable-multi-level.rs:79:1 | -LL | trait A { - | ^^^^^^^ +LL | impl B for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, + Method(::foo_a), Method(::foo_b), + TraitVPtr(), + Method(::foo_c), ] - --> $DIR/vtable-multi-level.rs:20:1 + --> $DIR/vtable-multi-level.rs:83:1 | -LL | trait B { - | ^^^^^^^ +LL | impl C for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, - Method(::foo_a), - Method(::foo_b), - TraitVPtr(), - Method(::foo_c), + Method(::foo_d), ] - --> $DIR/vtable-multi-level.rs:26:1 + --> $DIR/vtable-multi-level.rs:87:1 | -LL | trait C: A + B { - | ^^^^^^^^^^^^^^ +LL | impl D for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, - Method(::foo_d), + Method(::foo_e), ] - --> $DIR/vtable-multi-level.rs:32:1 + --> $DIR/vtable-multi-level.rs:91:1 | -LL | trait D { - | ^^^^^^^ +LL | impl E for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, + Method(::foo_d), Method(::foo_e), + TraitVPtr(), + Method(::foo_f), ] - --> $DIR/vtable-multi-level.rs:38:1 + --> $DIR/vtable-multi-level.rs:95:1 | -LL | trait E { - | ^^^^^^^ +LL | impl F for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, + Method(::foo_a), + Method(::foo_b), + TraitVPtr(), + Method(::foo_c), Method(::foo_d), + TraitVPtr(), Method(::foo_e), TraitVPtr(), Method(::foo_f), + TraitVPtr(), + Method(::foo_g), ] - --> $DIR/vtable-multi-level.rs:44:1 + --> $DIR/vtable-multi-level.rs:99:1 | -LL | trait F: D + E { - | ^^^^^^^^^^^^^^ +LL | impl G for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, Method(::foo_h), ] - --> $DIR/vtable-multi-level.rs:55:1 + --> $DIR/vtable-multi-level.rs:103:1 | -LL | trait H { - | ^^^^^^^ +LL | impl H for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, Method(::foo_i), ] - --> $DIR/vtable-multi-level.rs:61:1 + --> $DIR/vtable-multi-level.rs:107:1 | -LL | trait I { - | ^^^^^^^ +LL | impl I for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, @@ -137,34 +122,34 @@ error: vtable entries for ``: [ TraitVPtr(), Method(::foo_j), ] - --> $DIR/vtable-multi-level.rs:67:1 + --> $DIR/vtable-multi-level.rs:111:1 | -LL | trait J: H + I { - | ^^^^^^^^^^^^^^ +LL | impl J for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, Method(::foo_k), ] - --> $DIR/vtable-multi-level.rs:73:1 + --> $DIR/vtable-multi-level.rs:115:1 | -LL | trait K { - | ^^^^^^^ +LL | impl K for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, Method(::foo_l), ] - --> $DIR/vtable-multi-level.rs:79:1 + --> $DIR/vtable-multi-level.rs:119:1 | -LL | trait L { - | ^^^^^^^ +LL | impl L for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, @@ -173,12 +158,12 @@ error: vtable entries for ``: [ TraitVPtr(), Method(::foo_m), ] - --> $DIR/vtable-multi-level.rs:85:1 + --> $DIR/vtable-multi-level.rs:123:1 | -LL | trait M: K + L { - | ^^^^^^^^^^^^^^ +LL | impl M for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, @@ -194,10 +179,46 @@ error: vtable entries for ``: [ TraitVPtr(), Method(::foo_n), ] - --> $DIR/vtable-multi-level.rs:91:1 + --> $DIR/vtable-multi-level.rs:127:1 + | +LL | impl N for S {} + | ^^^^^^^^^^^^ + +error: vtable entries: [ + MetadataDropInPlace, + MetadataSize, + MetadataAlign, + Method(::foo_a), + Method(::foo_b), + TraitVPtr(), + Method(::foo_c), + Method(::foo_d), + TraitVPtr(), + Method(::foo_e), + TraitVPtr(), + Method(::foo_f), + TraitVPtr(), + Method(::foo_g), + Method(::foo_h), + TraitVPtr(), + Method(::foo_i), + TraitVPtr(), + Method(::foo_j), + TraitVPtr(), + Method(::foo_k), + TraitVPtr(), + Method(::foo_l), + TraitVPtr(), + Method(::foo_m), + TraitVPtr(), + Method(::foo_n), + TraitVPtr(), + Method(::foo_o), + ] + --> $DIR/vtable-multi-level.rs:131:1 | -LL | trait N: J + M { - | ^^^^^^^^^^^^^^ +LL | impl O for S {} + | ^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/traits/vtable/vtable-multiple.rs b/tests/ui/traits/vtable/vtable-multiple.rs index beaaf4db6b1c7..51e20ee20c9fc 100644 --- a/tests/ui/traits/vtable/vtable-multiple.rs +++ b/tests/ui/traits/vtable/vtable-multiple.rs @@ -1,33 +1,29 @@ -//@ build-fail #![feature(rustc_attrs)] -#[rustc_dump_vtable] trait A { fn foo_a(&self) {} } -#[rustc_dump_vtable] trait B { - //~^ error vtable fn foo_b(&self) {} } -#[rustc_dump_vtable] trait C: A + B { - //~^ error vtable fn foo_c(&self) {} } struct S; +#[rustc_dump_vtable] impl A for S {} +//~^ error vtable + +#[rustc_dump_vtable] impl B for S {} -impl C for S {} +//~^ error vtable -fn foo(c: &dyn C) {} -fn bar(c: &dyn B) {} +#[rustc_dump_vtable] +impl C for S {} +//~^ error vtable -fn main() { - foo(&S); - bar(&S); -} +fn main() {} diff --git a/tests/ui/traits/vtable/vtable-multiple.stderr b/tests/ui/traits/vtable/vtable-multiple.stderr index 0dcd84433090c..bae44148baa87 100644 --- a/tests/ui/traits/vtable/vtable-multiple.stderr +++ b/tests/ui/traits/vtable/vtable-multiple.stderr @@ -1,27 +1,38 @@ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, Method(::foo_a), + ] + --> $DIR/vtable-multiple.rs:18:1 + | +LL | impl A for S {} + | ^^^^^^^^^^^^ + +error: vtable entries: [ + MetadataDropInPlace, + MetadataSize, + MetadataAlign, Method(::foo_b), - TraitVPtr(), - Method(::foo_c), ] - --> $DIR/vtable-multiple.rs:16:1 + --> $DIR/vtable-multiple.rs:22:1 | -LL | trait C: A + B { - | ^^^^^^^^^^^^^^ +LL | impl B for S {} + | ^^^^^^^^^^^^ -error: vtable entries for ``: [ +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, + Method(::foo_a), Method(::foo_b), + TraitVPtr(), + Method(::foo_c), ] - --> $DIR/vtable-multiple.rs:10:1 + --> $DIR/vtable-multiple.rs:26:1 | -LL | trait B { - | ^^^^^^^ +LL | impl C for S {} + | ^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/traits/vtable/vtable-vacant.rs b/tests/ui/traits/vtable/vtable-vacant.rs index b3c7681570362..b023565c56e66 100644 --- a/tests/ui/traits/vtable/vtable-vacant.rs +++ b/tests/ui/traits/vtable/vtable-vacant.rs @@ -1,18 +1,14 @@ -//@ build-fail #![feature(rustc_attrs)] #![feature(negative_impls)] // B --> A -#[rustc_dump_vtable] trait A { fn foo_a1(&self) {} fn foo_a2(&self) where Self: Send {} } -#[rustc_dump_vtable] trait B: A { - //~^ error vtable fn foo_b1(&self) {} fn foo_b2(&self) where Self: Send {} } @@ -20,11 +16,12 @@ trait B: A { struct S; impl !Send for S {} +#[rustc_dump_vtable] impl A for S {} -impl B for S {} +//~^ error vtable -fn foo(_: &dyn B) {} +#[rustc_dump_vtable] +impl B for S {} +//~^ error vtable -fn main() { - foo(&S); -} +fn main() {} diff --git a/tests/ui/traits/vtable/vtable-vacant.stderr b/tests/ui/traits/vtable/vtable-vacant.stderr index f6961ca010ed8..ed8466f7f2ecd 100644 --- a/tests/ui/traits/vtable/vtable-vacant.stderr +++ b/tests/ui/traits/vtable/vtable-vacant.stderr @@ -1,4 +1,16 @@ -error: vtable entries for ``: [ +error: vtable entries: [ + MetadataDropInPlace, + MetadataSize, + MetadataAlign, + Method(::foo_a1), + Vacant, + ] + --> $DIR/vtable-vacant.rs:20:1 + | +LL | impl A for S {} + | ^^^^^^^^^^^^ + +error: vtable entries: [ MetadataDropInPlace, MetadataSize, MetadataAlign, @@ -7,10 +19,10 @@ error: vtable entries for ``: [ Method(::foo_b1), Vacant, ] - --> $DIR/vtable-vacant.rs:14:1 + --> $DIR/vtable-vacant.rs:24:1 | -LL | trait B: A { - | ^^^^^^^^^^ +LL | impl B for S {} + | ^^^^^^^^^^^^ -error: aborting due to 1 previous error +error: aborting due to 2 previous errors diff --git a/tests/ui/traits/wrong-mul-method-signature.stderr b/tests/ui/traits/wrong-mul-method-signature.stderr index e30b61622ae01..c85775f48871d 100644 --- a/tests/ui/traits/wrong-mul-method-signature.stderr +++ b/tests/ui/traits/wrong-mul-method-signature.stderr @@ -8,8 +8,9 @@ LL | fn mul(self, s: &f64) -> Vec1 { found signature `fn(Vec1, &_) -> Vec1` help: change the parameter type to match the trait | -LL | fn mul(self, s: f64) -> Vec1 { - | ~~~ +LL - fn mul(self, s: &f64) -> Vec1 { +LL + fn mul(self, s: f64) -> Vec1 { + | error[E0053]: method `mul` has an incompatible type for trait --> $DIR/wrong-mul-method-signature.rs:33:21 @@ -21,8 +22,9 @@ LL | fn mul(self, s: f64) -> Vec2 { found signature `fn(Vec2, f64) -> Vec2` help: change the parameter type to match the trait | -LL | fn mul(self, s: Vec2) -> Vec2 { - | ~~~~ +LL - fn mul(self, s: f64) -> Vec2 { +LL + fn mul(self, s: Vec2) -> Vec2 { + | error[E0053]: method `mul` has an incompatible type for trait --> $DIR/wrong-mul-method-signature.rs:52:29 @@ -34,8 +36,9 @@ LL | fn mul(self, s: f64) -> f64 { found signature `fn(Vec3, _) -> f64` help: change the output type to match the trait | -LL | fn mul(self, s: f64) -> i32 { - | ~~~ +LL - fn mul(self, s: f64) -> f64 { +LL + fn mul(self, s: f64) -> i32 { + | error[E0308]: mismatched types --> $DIR/wrong-mul-method-signature.rs:63:45 diff --git a/tests/ui/transmutability/assoc-bound.stderr b/tests/ui/transmutability/assoc-bound.stderr index b3c7680bf2949..4dff24e2002aa 100644 --- a/tests/ui/transmutability/assoc-bound.stderr +++ b/tests/ui/transmutability/assoc-bound.stderr @@ -13,7 +13,7 @@ LL | type AssocB: std::mem::TransmuteFrom<()>; help: consider further restricting the associated type | LL | T: A, ::AssocA: TransmuteFrom<(), Assume { alignment: false, lifetimes: false, safety: false, validity: false }> - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ error[E0277]: `()` cannot be safely transmuted into `<&i32 as A>::AssocA` --> $DIR/assoc-bound.rs:24:19 diff --git a/tests/ui/transmutability/malformed-program-gracefulness/wrong-adt-assume.rs b/tests/ui/transmutability/malformed-program-gracefulness/wrong-adt-assume.rs new file mode 100644 index 0000000000000..2340df2502546 --- /dev/null +++ b/tests/ui/transmutability/malformed-program-gracefulness/wrong-adt-assume.rs @@ -0,0 +1,20 @@ +//! Test that we don't ICE when passing the wrong ADT to ASSUME. + +#![feature(adt_const_params)] +#![feature(transmutability)] + +use std::marker::ConstParamTy; +use std::mem::TransmuteFrom; + +#[derive(ConstParamTy, PartialEq, Eq)] +struct NotAssume; + +fn foo() +where + u8: TransmuteFrom, //~ ERROR the constant `ASSUME` is not of type `Assume` +{ +} + +fn main() { + foo::<{ NotAssume }>(); +} diff --git a/tests/ui/transmutability/malformed-program-gracefulness/wrong-adt-assume.stderr b/tests/ui/transmutability/malformed-program-gracefulness/wrong-adt-assume.stderr new file mode 100644 index 0000000000000..cae700ecfdc77 --- /dev/null +++ b/tests/ui/transmutability/malformed-program-gracefulness/wrong-adt-assume.stderr @@ -0,0 +1,11 @@ +error: the constant `ASSUME` is not of type `Assume` + --> $DIR/wrong-adt-assume.rs:14:9 + | +LL | u8: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Assume`, found `NotAssume` + | +note: required by a const generic parameter in `TransmuteFrom` + --> $SRC_DIR/core/src/mem/transmutability.rs:LL:COL + +error: aborting due to 1 previous error + diff --git a/tests/ui/treat-err-as-bug/panic-causes-oom-112708.stderr b/tests/ui/treat-err-as-bug/panic-causes-oom-112708.stderr deleted file mode 100644 index 2d49071ac49cf..0000000000000 --- a/tests/ui/treat-err-as-bug/panic-causes-oom-112708.stderr +++ /dev/null @@ -1,32 +0,0 @@ -error: denote infinite loops with `loop { ... }` - --> $DIR/panic-causes-oom-112708.rs:13:5 - | -LL | while true {} - | ^^^^^^^^^^ help: use `loop` - | -note: the lint level is defined here - --> $DIR/panic-causes-oom-112708.rs:12:12 - | -LL | #[deny(while_true)] - | ^^^^^^^^^^ - - -query stack during panic: -#0 [early_lint_checks] perform lints prior to macro expansion -#1 [hir_crate] getting the crate HIR -end of query stack - -error: the compiler unexpectedly panicked. this is a bug. - -query stack during panic: -#0 [early_lint_checks] perform lints prior to macro expansion -#1 [hir_crate] getting the crate HIR -end of query stack - -error: the compiler unexpectedly panicked. this is a bug. - -query stack during panic: -#0 [early_lint_checks] perform lints prior to macro expansion -#1 [hir_crate] getting the crate HIR -end of query stack -thread caused non-unwinding panic. aborting. diff --git a/tests/ui/tuple/tuple-index-not-tuple.stderr b/tests/ui/tuple/tuple-index-not-tuple.stderr index a267e41b1bc32..faf9a313478df 100644 --- a/tests/ui/tuple/tuple-index-not-tuple.stderr +++ b/tests/ui/tuple/tuple-index-not-tuple.stderr @@ -6,8 +6,9 @@ LL | origin.0; | help: a field with a similar name exists | -LL | origin.x; - | ~ +LL - origin.0; +LL + origin.x; + | error[E0609]: no field `0` on type `Empty` --> $DIR/tuple-index-not-tuple.rs:8:11 diff --git a/tests/ui/tuple/tuple-index-out-of-bounds.stderr b/tests/ui/tuple/tuple-index-out-of-bounds.stderr index 96090435d06be..8b3c835c3e3ce 100644 --- a/tests/ui/tuple/tuple-index-out-of-bounds.stderr +++ b/tests/ui/tuple/tuple-index-out-of-bounds.stderr @@ -6,8 +6,9 @@ LL | origin.2; | help: a field with a similar name exists | -LL | origin.0; - | ~ +LL - origin.2; +LL + origin.0; + | error[E0609]: no field `2` on type `({integer}, {integer})` --> $DIR/tuple-index-out-of-bounds.rs:12:11 diff --git a/tests/ui/type-alias-enum-variants/enum-variant-generic-args.stderr b/tests/ui/type-alias-enum-variants/enum-variant-generic-args.stderr index 482a314db602e..5039ae8f288ee 100644 --- a/tests/ui/type-alias-enum-variants/enum-variant-generic-args.stderr +++ b/tests/ui/type-alias-enum-variants/enum-variant-generic-args.stderr @@ -43,8 +43,9 @@ LL | impl Enum { | --------------- `Self` is on type `Enum` in this `impl` help: the `Self` type doesn't accept type parameters, use the concrete type's name `Enum` instead if you want to specify its type parameters | -LL | Enum::<()>::TSVariant(()); - | ~~~~ +LL - Self::<()>::TSVariant(()); +LL + Enum::<()>::TSVariant(()); + | error[E0308]: mismatched types --> $DIR/enum-variant-generic-args.rs:17:31 @@ -83,8 +84,9 @@ LL | impl Enum { | --------------- `Self` is on type `Enum` in this `impl` help: the `Self` type doesn't accept type parameters, use the concrete type's name `Enum` instead if you want to specify its type parameters | -LL | Enum::<()>::TSVariant::<()>(()); - | ~~~~ +LL - Self::<()>::TSVariant::<()>(()); +LL + Enum::<()>::TSVariant::<()>(()); + | error[E0109]: type arguments are not allowed on this type --> $DIR/enum-variant-generic-args.rs:20:33 @@ -151,8 +153,9 @@ LL | impl Enum { | --------------- `Self` is on type `Enum` in this `impl` help: the `Self` type doesn't accept type parameters, use the concrete type's name `Enum` instead if you want to specify its type parameters | -LL | Enum::<()>::SVariant { v: () }; - | ~~~~ +LL - Self::<()>::SVariant { v: () }; +LL + Enum::<()>::SVariant { v: () }; + | error[E0308]: mismatched types --> $DIR/enum-variant-generic-args.rs:31:35 @@ -184,8 +187,9 @@ LL | impl Enum { | --------------- `Self` is on type `Enum` in this `impl` help: the `Self` type doesn't accept type parameters, use the concrete type's name `Enum` instead if you want to specify its type parameters | -LL | Enum::<()>::SVariant::<()> { v: () }; - | ~~~~ +LL - Self::<()>::SVariant::<()> { v: () }; +LL + Enum::<()>::SVariant::<()> { v: () }; + | error[E0109]: type arguments are not allowed on this type --> $DIR/enum-variant-generic-args.rs:34:32 @@ -240,8 +244,9 @@ LL | impl Enum { | --------------- `Self` is on type `Enum` in this `impl` help: the `Self` type doesn't accept type parameters, use the concrete type's name `Enum` instead if you want to specify its type parameters | -LL | Enum::<()>::UVariant; - | ~~~~ +LL - Self::<()>::UVariant; +LL + Enum::<()>::UVariant; + | error[E0109]: type arguments are not allowed on self type --> $DIR/enum-variant-generic-args.rs:45:16 @@ -261,8 +266,9 @@ LL | impl Enum { | --------------- `Self` is on type `Enum` in this `impl` help: the `Self` type doesn't accept type parameters, use the concrete type's name `Enum` instead if you want to specify its type parameters | -LL | Enum::<()>::UVariant::<()>; - | ~~~~ +LL - Self::<()>::UVariant::<()>; +LL + Enum::<()>::UVariant::<()>; + | error[E0109]: type arguments are not allowed on this type --> $DIR/enum-variant-generic-args.rs:45:32 @@ -279,6 +285,13 @@ LL | Enum::<()>::TSVariant::<()>(()); | --------- ^^ type argument not allowed | | | not allowed on tuple variant `TSVariant` + | + = note: generic arguments are not allowed on both an enum and its variant's path segments simultaneously; they are only valid in one place or the other +help: remove the generics arguments from one of the path segments + | +LL - Enum::<()>::TSVariant::<()>(()); +LL + Enum::<()>::TSVariant(()); + | error[E0109]: type arguments are not allowed on this type --> $DIR/enum-variant-generic-args.rs:57:24 @@ -348,7 +361,12 @@ LL | Enum::<()>::SVariant::<()> { v: () }; | | | not allowed on variant `SVariant` | - = note: enum variants can't have type parameters + = note: generic arguments are not allowed on both an enum and its variant's path segments simultaneously; they are only valid in one place or the other +help: remove the generics arguments from one of the path segments + | +LL - Enum::<()>::SVariant::<()> { v: () }; +LL + Enum::<()>::SVariant { v: () }; + | error[E0109]: type arguments are not allowed on this type --> $DIR/enum-variant-generic-args.rs:75:23 @@ -445,6 +463,13 @@ LL | Enum::<()>::UVariant::<()>; | -------- ^^ type argument not allowed | | | not allowed on unit variant `UVariant` + | + = note: generic arguments are not allowed on both an enum and its variant's path segments simultaneously; they are only valid in one place or the other +help: remove the generics arguments from one of the path segments + | +LL - Enum::<()>::UVariant::<()>; +LL + Enum::<()>::UVariant; + | error[E0109]: type arguments are not allowed on this type --> $DIR/enum-variant-generic-args.rs:93:23 diff --git a/tests/ui/type-alias-enum-variants/enum-variant-priority-higher-than-other-inherent.stderr b/tests/ui/type-alias-enum-variants/enum-variant-priority-higher-than-other-inherent.stderr index 371f5b10988a8..c4deafd4f6b7a 100644 --- a/tests/ui/type-alias-enum-variants/enum-variant-priority-higher-than-other-inherent.stderr +++ b/tests/ui/type-alias-enum-variants/enum-variant-priority-higher-than-other-inherent.stderr @@ -11,8 +11,9 @@ LL | V(u8) | ^ help: provide the argument | -LL | ::V(/* u8 */); - | ~~~~~~~~~~ +LL - ::V(); +LL + ::V(/* u8 */); + | error[E0308]: mismatched types --> $DIR/enum-variant-priority-higher-than-other-inherent.rs:22:17 diff --git a/tests/ui/type-alias-enum-variants/incorrect-variant-form-through-alias-caught.stderr b/tests/ui/type-alias-enum-variants/incorrect-variant-form-through-alias-caught.stderr index 714b4fd7d25f6..1ed78cbc0da20 100644 --- a/tests/ui/type-alias-enum-variants/incorrect-variant-form-through-alias-caught.stderr +++ b/tests/ui/type-alias-enum-variants/incorrect-variant-form-through-alias-caught.stderr @@ -28,8 +28,9 @@ LL | let Alias::Braced(..) = panic!(); | help: use the struct variant pattern syntax | -LL | let Alias::Braced {} = panic!(); - | ~~ +LL - let Alias::Braced(..) = panic!(); +LL + let Alias::Braced {} = panic!(); + | error[E0618]: expected function, found enum variant `Alias::Unit` --> $DIR/incorrect-variant-form-through-alias-caught.rs:15:5 diff --git a/tests/ui/type-alias-impl-trait/constrain_in_projection.current.stderr b/tests/ui/type-alias-impl-trait/constrain_in_projection.current.stderr index d96c86a2e6f16..580258bbb28e4 100644 --- a/tests/ui/type-alias-impl-trait/constrain_in_projection.current.stderr +++ b/tests/ui/type-alias-impl-trait/constrain_in_projection.current.stderr @@ -7,6 +7,15 @@ LL | let x = >::Assoc::default(); = help: the trait `Trait` is not implemented for `Foo` but trait `Trait<()>` is implemented for it -error: aborting due to 1 previous error +error[E0277]: the trait bound `Foo: Trait` is not satisfied + --> $DIR/constrain_in_projection.rs:24:13 + | +LL | let x = >::Assoc::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `Foo` + | + = help: the trait `Trait` is not implemented for `Foo` + but trait `Trait<()>` is implemented for it + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/type-alias-impl-trait/constrain_in_projection.rs b/tests/ui/type-alias-impl-trait/constrain_in_projection.rs index 7d7d16361ae6d..355c0e1692b2a 100644 --- a/tests/ui/type-alias-impl-trait/constrain_in_projection.rs +++ b/tests/ui/type-alias-impl-trait/constrain_in_projection.rs @@ -23,6 +23,7 @@ impl Trait<()> for Foo { fn bop(_: Bar) { let x = >::Assoc::default(); //[current]~^ `Foo: Trait` is not satisfied + //[current]~| `Foo: Trait` is not satisfied } fn main() {} diff --git a/tests/ui/type-alias-impl-trait/constrain_in_projection2.current.stderr b/tests/ui/type-alias-impl-trait/constrain_in_projection2.current.stderr index 909f1f6d61cbe..777fe1e2788ce 100644 --- a/tests/ui/type-alias-impl-trait/constrain_in_projection2.current.stderr +++ b/tests/ui/type-alias-impl-trait/constrain_in_projection2.current.stderr @@ -8,6 +8,16 @@ LL | let x = >::Assoc::default(); `Foo` implements `Trait<()>` `Foo` implements `Trait` -error: aborting due to 1 previous error +error[E0277]: the trait bound `Foo: Trait` is not satisfied + --> $DIR/constrain_in_projection2.rs:27:13 + | +LL | let x = >::Assoc::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `Foo` + | + = help: the following other types implement trait `Trait`: + `Foo` implements `Trait<()>` + `Foo` implements `Trait` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/type-alias-impl-trait/constrain_in_projection2.rs b/tests/ui/type-alias-impl-trait/constrain_in_projection2.rs index af222f6c15347..16b1329b52f3d 100644 --- a/tests/ui/type-alias-impl-trait/constrain_in_projection2.rs +++ b/tests/ui/type-alias-impl-trait/constrain_in_projection2.rs @@ -27,6 +27,7 @@ fn bop(_: Bar) { let x = >::Assoc::default(); //[next]~^ ERROR: cannot satisfy `Foo: Trait` //[current]~^^ ERROR: `Foo: Trait` is not satisfied + //[current]~| ERROR: `Foo: Trait` is not satisfied } fn main() {} diff --git a/tests/ui/type-alias-impl-trait/generic_underconstrained.stderr b/tests/ui/type-alias-impl-trait/generic_underconstrained.stderr index 98f99cdbfbd65..c24f8fd867fb3 100644 --- a/tests/ui/type-alias-impl-trait/generic_underconstrained.stderr +++ b/tests/ui/type-alias-impl-trait/generic_underconstrained.stderr @@ -15,21 +15,17 @@ LL | fn underconstrain(_: T) -> Underconstrained { | +++++++ error[E0277]: the trait bound `T: Trait` is not satisfied - --> $DIR/generic_underconstrained.rs:9:51 + --> $DIR/generic_underconstrained.rs:9:31 | -LL | fn underconstrain(_: T) -> Underconstrained { - | ___________________________________________________^ -LL | | -LL | | -LL | | unimplemented!() -LL | | } - | |_^ the trait `Trait` is not implemented for `T` +LL | fn underconstrain(_: T) -> Underconstrained { + | ^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `T` | note: required by a bound on the type alias `Underconstrained` --> $DIR/generic_underconstrained.rs:6:26 | LL | type Underconstrained = impl Send; | ^^^^^ required by this bound + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: consider restricting type parameter `T` with trait `Trait` | LL | fn underconstrain(_: T) -> Underconstrained { diff --git a/tests/ui/type-alias-impl-trait/generic_underconstrained2.stderr b/tests/ui/type-alias-impl-trait/generic_underconstrained2.stderr index 5506977a3e702..93df5ddca796f 100644 --- a/tests/ui/type-alias-impl-trait/generic_underconstrained2.stderr +++ b/tests/ui/type-alias-impl-trait/generic_underconstrained2.stderr @@ -31,42 +31,34 @@ LL | fn underconstrained2(_: U, _: V) -> Underconstrained | +++++++++++++++++ error[E0277]: `U` doesn't implement `Debug` - --> $DIR/generic_underconstrained2.rs:8:53 + --> $DIR/generic_underconstrained2.rs:8:33 | -LL | fn underconstrained(_: U) -> Underconstrained { - | _____________________________________________________^ -LL | | -LL | | -LL | | 5u32 -LL | | } - | |_^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug` +LL | fn underconstrained(_: U) -> Underconstrained { + | ^^^^^^^^^^^^^^^^^^^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug` | note: required by a bound on the type alias `Underconstrained` --> $DIR/generic_underconstrained2.rs:5:26 | LL | type Underconstrained = impl Send; | ^^^^^^^^^^^^^^^ required by this bound + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: consider restricting type parameter `U` with trait `Debug` | LL | fn underconstrained(_: U) -> Underconstrained { | +++++++++++++++++ error[E0277]: `V` doesn't implement `Debug` - --> $DIR/generic_underconstrained2.rs:17:64 + --> $DIR/generic_underconstrained2.rs:17:43 | -LL | fn underconstrained2(_: U, _: V) -> Underconstrained2 { - | ________________________________________________________________^ -LL | | -LL | | -LL | | 5u32 -LL | | } - | |_^ `V` cannot be formatted using `{:?}` because it doesn't implement `Debug` +LL | fn underconstrained2(_: U, _: V) -> Underconstrained2 { + | ^^^^^^^^^^^^^^^^^^^^ `V` cannot be formatted using `{:?}` because it doesn't implement `Debug` | note: required by a bound on the type alias `Underconstrained2` --> $DIR/generic_underconstrained2.rs:14:27 | LL | type Underconstrained2 = impl Send; | ^^^^^^^^^^^^^^^ required by this bound + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` help: consider restricting type parameter `V` with trait `Debug` | LL | fn underconstrained2(_: U, _: V) -> Underconstrained2 { diff --git a/tests/ui/type-alias-impl-trait/hkl_forbidden4.stderr b/tests/ui/type-alias-impl-trait/hkl_forbidden4.stderr index 293c8ea09f176..feb161c3b048e 100644 --- a/tests/ui/type-alias-impl-trait/hkl_forbidden4.stderr +++ b/tests/ui/type-alias-impl-trait/hkl_forbidden4.stderr @@ -1,3 +1,15 @@ +error: concrete type differs from previous defining opaque type use + --> $DIR/hkl_forbidden4.rs:12:1 + | +LL | async fn operation(_: &mut ()) -> () { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `FutNothing<'_>`, got `{async fn body of operation()}` + | +note: previous use here + --> $DIR/hkl_forbidden4.rs:14:5 + | +LL | call(operation).await + | ^^^^^^^^^^^^^^^ + error: item does not constrain `FutNothing::{opaque#0}`, but has it in its signature --> $DIR/hkl_forbidden4.rs:18:10 | @@ -35,18 +47,6 @@ LL | LL | call(operation).await | ^^^^^^^^^^^^^^^ -error: concrete type differs from previous defining opaque type use - --> $DIR/hkl_forbidden4.rs:12:1 - | -LL | async fn operation(_: &mut ()) -> () { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `FutNothing<'_>`, got `{async fn body of operation()}` - | -note: previous use here - --> $DIR/hkl_forbidden4.rs:14:5 - | -LL | call(operation).await - | ^^^^^^^^^^^^^^^ - error[E0792]: expected generic lifetime parameter, found `'any` --> $DIR/hkl_forbidden4.rs:22:1 | diff --git a/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.rs b/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.rs index 0ee188d825f0d..2bcb8f06f4fd6 100644 --- a/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.rs +++ b/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.rs @@ -42,7 +42,6 @@ impl>>, U> MyIndex> for Scope { //~^ ERROR the type parameter `T` is not constrained by the impl type O = T; fn my_index(self) -> Self::O { - //~^ ERROR item does not constrain MyFrom::my_from(self.0).ok().unwrap() } } diff --git a/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.stderr b/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.stderr index eace96317dc1d..0ab4c34381a26 100644 --- a/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.stderr +++ b/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.stderr @@ -17,19 +17,6 @@ note: this opaque type is in the signature LL | type DummyT = impl F; | ^^^^^^ -error: item does not constrain `DummyT::{opaque#0}`, but has it in its signature - --> $DIR/ice-failed-to-resolve-instance-for-110696.rs:44:8 - | -LL | fn my_index(self) -> Self::O { - | ^^^^^^^^ - | - = note: consider moving the opaque type's declaration and defining uses into a separate module -note: this opaque type is in the signature - --> $DIR/ice-failed-to-resolve-instance-for-110696.rs:20:18 - | -LL | type DummyT = impl F; - | ^^^^^^ - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0207`. diff --git a/tests/ui/type-alias-impl-trait/impl-with-unconstrained-param.rs b/tests/ui/type-alias-impl-trait/impl-with-unconstrained-param.rs index fcac83500ec4f..1824ff5e2fb82 100644 --- a/tests/ui/type-alias-impl-trait/impl-with-unconstrained-param.rs +++ b/tests/ui/type-alias-impl-trait/impl-with-unconstrained-param.rs @@ -12,8 +12,6 @@ impl X for () { //~^ ERROR the type parameter `T` is not constrained type I = impl Sized; fn f() -> Self::I {} - //~^ ERROR type annotations needed - //~| ERROR type annotations needed } fn main() {} diff --git a/tests/ui/type-alias-impl-trait/impl-with-unconstrained-param.stderr b/tests/ui/type-alias-impl-trait/impl-with-unconstrained-param.stderr index bb0e11d314c33..137a4db81b563 100644 --- a/tests/ui/type-alias-impl-trait/impl-with-unconstrained-param.stderr +++ b/tests/ui/type-alias-impl-trait/impl-with-unconstrained-param.stderr @@ -4,19 +4,6 @@ error[E0207]: the type parameter `T` is not constrained by the impl trait, self LL | impl X for () { | ^ unconstrained type parameter -error[E0282]: type annotations needed - --> $DIR/impl-with-unconstrained-param.rs:14:23 - | -LL | fn f() -> Self::I {} - | ^^ cannot infer type for type parameter `T` - -error[E0282]: type annotations needed - --> $DIR/impl-with-unconstrained-param.rs:14:15 - | -LL | fn f() -> Self::I {} - | ^^^^^^^ cannot infer type for type parameter `T` - -error: aborting due to 3 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0207, E0282. -For more information about an error, try `rustc --explain E0207`. +For more information about this error, try `rustc --explain E0207`. diff --git a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs index 1aa64810d1908..38dd6dd0af209 100644 --- a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs +++ b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs @@ -1,5 +1,4 @@ //@ edition: 2021 -//@ build-fail #![feature(impl_trait_in_assoc_type)] diff --git a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr index 6cbffaaed4d35..304ed12e944c6 100644 --- a/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr +++ b/tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.stderr @@ -1,11 +1,11 @@ error[E0733]: recursion in an async block requires boxing - --> $DIR/indirect-recursion-issue-112047.rs:22:9 + --> $DIR/indirect-recursion-issue-112047.rs:21:9 | LL | async move { recur(self).await; } | ^^^^^^^^^^ ----------------- recursive call here | note: which leads to this async fn - --> $DIR/indirect-recursion-issue-112047.rs:14:1 + --> $DIR/indirect-recursion-issue-112047.rs:13:1 | LL | async fn recur(t: impl Recur) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/issue-74244.rs b/tests/ui/type-alias-impl-trait/issue-74244.rs index ce8a38a3361f8..bb4104b3d2519 100644 --- a/tests/ui/type-alias-impl-trait/issue-74244.rs +++ b/tests/ui/type-alias-impl-trait/issue-74244.rs @@ -14,7 +14,6 @@ impl Allocator for DefaultAllocator { type A = impl Fn(::Buffer); fn foo() -> A { - //~^ ERROR: type annotations needed |_| () } diff --git a/tests/ui/type-alias-impl-trait/issue-74244.stderr b/tests/ui/type-alias-impl-trait/issue-74244.stderr index d2b50ffd86b59..f5ca56bacccf6 100644 --- a/tests/ui/type-alias-impl-trait/issue-74244.stderr +++ b/tests/ui/type-alias-impl-trait/issue-74244.stderr @@ -4,13 +4,6 @@ error[E0207]: the type parameter `T` is not constrained by the impl trait, self LL | impl Allocator for DefaultAllocator { | ^ unconstrained type parameter -error[E0282]: type annotations needed - --> $DIR/issue-74244.rs:16:13 - | -LL | fn foo() -> A { - | ^ cannot infer type for type parameter `T` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0207, E0282. -For more information about an error, try `rustc --explain E0207`. +For more information about this error, try `rustc --explain E0207`. diff --git a/tests/ui/type-alias-impl-trait/issue-77179.stderr b/tests/ui/type-alias-impl-trait/issue-77179.stderr index 85a943c26e2d5..16bbc996d909c 100644 --- a/tests/ui/type-alias-impl-trait/issue-77179.stderr +++ b/tests/ui/type-alias-impl-trait/issue-77179.stderr @@ -28,10 +28,7 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/issue-77179.rs:18:25 | LL | fn bar() -> Pointer<_>; - | ^ - | | - | not allowed in type signatures - | help: use type parameters instead: `T` + | ^ not allowed in type signatures error: aborting due to 3 previous errors diff --git a/tests/ui/type-alias-impl-trait/issue-98604.rs b/tests/ui/type-alias-impl-trait/issue-98604.rs index 9231e82d9f43b..35c25c7f7267c 100644 --- a/tests/ui/type-alias-impl-trait/issue-98604.rs +++ b/tests/ui/type-alias-impl-trait/issue-98604.rs @@ -7,5 +7,5 @@ async fn test() {} #[allow(unused_must_use)] fn main() { Box::new(test) as AsyncFnPtr; - //~^ ERROR expected `test` to be a fn item that returns `Pin>>`, but it returns `impl Future + //~^ ERROR expected `test` to return `Pin>>`, but it returns `impl Future } diff --git a/tests/ui/type-alias-impl-trait/issue-98604.stderr b/tests/ui/type-alias-impl-trait/issue-98604.stderr index 2390b72535625..77c6ba3003f0d 100644 --- a/tests/ui/type-alias-impl-trait/issue-98604.stderr +++ b/tests/ui/type-alias-impl-trait/issue-98604.stderr @@ -1,4 +1,4 @@ -error[E0271]: expected `test` to be a fn item that returns `Pin>>`, but it returns `impl Future` +error[E0271]: expected `test` to return `Pin>>`, but it returns `impl Future` --> $DIR/issue-98604.rs:9:5 | LL | Box::new(test) as AsyncFnPtr; diff --git a/tests/ui/type-alias-impl-trait/issue-98608.rs b/tests/ui/type-alias-impl-trait/issue-98608.rs index 5e026ea4096c3..5612ccd6caed2 100644 --- a/tests/ui/type-alias-impl-trait/issue-98608.rs +++ b/tests/ui/type-alias-impl-trait/issue-98608.rs @@ -4,7 +4,7 @@ fn hi() -> impl Sized { fn main() { let b: Box Box> = Box::new(hi); - //~^ ERROR expected `hi` to be a fn item that returns `Box`, but it returns `impl Sized` + //~^ ERROR expected `hi` to return `Box`, but it returns `impl Sized` let boxed = b(); let null = *boxed; println!("{null:?}"); diff --git a/tests/ui/type-alias-impl-trait/issue-98608.stderr b/tests/ui/type-alias-impl-trait/issue-98608.stderr index d5c56636f66e4..872a6ed435001 100644 --- a/tests/ui/type-alias-impl-trait/issue-98608.stderr +++ b/tests/ui/type-alias-impl-trait/issue-98608.stderr @@ -1,4 +1,4 @@ -error[E0271]: expected `hi` to be a fn item that returns `Box`, but it returns `impl Sized` +error[E0271]: expected `hi` to return `Box`, but it returns `impl Sized` --> $DIR/issue-98608.rs:6:39 | LL | fn hi() -> impl Sized { diff --git a/tests/ui/type-alias-impl-trait/unnameable_type.stderr b/tests/ui/type-alias-impl-trait/unnameable_type.stderr index 5b331c5660d9c..25dc41df41930 100644 --- a/tests/ui/type-alias-impl-trait/unnameable_type.stderr +++ b/tests/ui/type-alias-impl-trait/unnameable_type.stderr @@ -16,8 +16,9 @@ LL | fn dont_define_this(_private: Private) {} found signature `fn(MyPrivate)` help: change the parameter type to match the trait | -LL | fn dont_define_this(private: Private) { - | ~~~~~~~ +LL - fn dont_define_this(private: MyPrivate) { +LL + fn dont_define_this(private: Private) { + | error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/wf-nested.fail.stderr b/tests/ui/type-alias-impl-trait/wf-nested.fail.stderr deleted file mode 100644 index 79b726f83dde0..0000000000000 --- a/tests/ui/type-alias-impl-trait/wf-nested.fail.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error[E0310]: the parameter type `T` may not live long enough - --> $DIR/wf-nested.rs:64:38 - | -LL | fn define() -> OuterOpaque {} - | ^^ - | | - | the parameter type `T` must be valid for the static lifetime... - | ...so that the type `T` will meet its required lifetime bounds - | -help: consider adding an explicit lifetime bound - | -LL | fn define() -> OuterOpaque {} - | +++++++++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/type-alias-impl-trait/wf-nested.pass.stderr b/tests/ui/type-alias-impl-trait/wf-nested.pass.stderr deleted file mode 100644 index b61b69d8e407e..0000000000000 --- a/tests/ui/type-alias-impl-trait/wf-nested.pass.stderr +++ /dev/null @@ -1,31 +0,0 @@ -error[E0310]: the parameter type `T` may not live long enough - --> $DIR/wf-nested.rs:34:38 - | -LL | fn define() -> OuterOpaque {} - | ^^ - | | - | the parameter type `T` must be valid for the static lifetime... - | ...so that the type `T` will meet its required lifetime bounds - | -help: consider adding an explicit lifetime bound - | -LL | fn define() -> OuterOpaque {} - | +++++++++ - -error[E0310]: the parameter type `T` may not live long enough - --> $DIR/wf-nested.rs:37:69 - | -LL | fn define_rpit() -> impl Trait<&'static T, Out = impl Sized> {} - | ^^ - | | - | the parameter type `T` must be valid for the static lifetime... - | ...so that the type `T` will meet its required lifetime bounds - | -help: consider adding an explicit lifetime bound - | -LL | fn define_rpit() -> impl Trait<&'static T, Out = impl Sized> {} - | +++++++++ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr b/tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr deleted file mode 100644 index dbd3a1394f89b..0000000000000 --- a/tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr +++ /dev/null @@ -1,46 +0,0 @@ -error[E0310]: the parameter type `T` may not live long enough - --> $DIR/wf-nested.rs:46:38 - | -LL | fn define() -> OuterOpaque {} - | ^^ - | | - | the parameter type `T` must be valid for the static lifetime... - | ...so that the type `T` will meet its required lifetime bounds - | -help: consider adding an explicit lifetime bound - | -LL | fn define() -> OuterOpaque {} - | +++++++++ - -error[E0310]: the parameter type `T` may not live long enough - --> $DIR/wf-nested.rs:51:17 - | -LL | let _ = outer.get(); - | ^^^^^^^^^^^ - | | - | the parameter type `T` must be valid for the static lifetime... - | ...so that the type `T` will meet its required lifetime bounds - | -help: consider adding an explicit lifetime bound - | -LL | fn test() { - | +++++++++ - -error[E0310]: the parameter type `T` may not live long enough - --> $DIR/wf-nested.rs:51:17 - | -LL | let _ = outer.get(); - | ^^^^^^^^^^^ - | | - | the parameter type `T` must be valid for the static lifetime... - | ...so that the type `T` will meet its required lifetime bounds - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: consider adding an explicit lifetime bound - | -LL | fn test() { - | +++++++++ - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr index e891ff10fdaa9..b0b183d652b09 100644 --- a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr +++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr @@ -17,8 +17,9 @@ LL | type AssokOf = T::Assok; | help: consider fully qualifying and renaming the associated type | -LL | type AssokOf = ::Assoc; - | + +++++++++ ~~~~~ +LL - type AssokOf = T::Assok; +LL + type AssokOf = ::Assoc; + | error[E0220]: associated type `Proj` not found for `T` --> $DIR/unresolved-assoc-ty-suggest-trait.rs:22:21 diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr index 885c6ec9d8e8e..79b9db1edf776 100644 --- a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr +++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr @@ -21,8 +21,9 @@ LL | type AssokOf = T::Assok; | +++++++ help: ...and changing the associated type name | -LL | type AssokOf = T::Assoc; - | ~~~~~ +LL - type AssokOf = T::Assok; +LL + type AssokOf = T::Assoc; + | error[E0220]: associated type `Proj` not found for `T` --> $DIR/unresolved-assoc-ty-suggest-trait.rs:22:21 diff --git a/tests/ui/type-inference/or_else-multiple-type-params.stderr b/tests/ui/type-inference/or_else-multiple-type-params.stderr index 3176a2d490ebc..9bcd07f8bf164 100644 --- a/tests/ui/type-inference/or_else-multiple-type-params.stderr +++ b/tests/ui/type-inference/or_else-multiple-type-params.stderr @@ -6,8 +6,8 @@ LL | .or_else(|err| { | help: try giving this closure an explicit return type | -LL | .or_else(|err| -> Result { - | +++++++++++++++++++ +LL | .or_else(|err| -> Result<_, F> { + | +++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/type/issue-100584.stderr b/tests/ui/type/issue-100584.stderr index e1db14d1f001b..1523bdda7614b 100644 --- a/tests/ui/type/issue-100584.stderr +++ b/tests/ui/type/issue-100584.stderr @@ -20,7 +20,7 @@ LL | let _ = format!("{xyza}"); help: if this is intentional, prefix it with an underscore | LL | fn foo(_xyza: &str) { - | ~~~~~ + | + error: unused variable: `xyza` --> $DIR/issue-100584.rs:7:9 @@ -38,7 +38,7 @@ LL | let _ = format!("aaa{xyza}bbb"); help: if this is intentional, prefix it with an underscore | LL | fn foo3(_xyza: &str) { - | ~~~~~ + | + error: aborting due to 2 previous errors diff --git a/tests/ui/type/issue-103271.stderr b/tests/ui/type/issue-103271.stderr index f4dac51b2b478..1b84033291a5d 100644 --- a/tests/ui/type/issue-103271.stderr +++ b/tests/ui/type/issue-103271.stderr @@ -6,8 +6,9 @@ LL | let iter_fun = <&[u32]>::iter; | help: the function `iter` is implemented on `[u32]` | -LL | let iter_fun = <[u32]>::iter; - | ~~~~~ +LL - let iter_fun = <&[u32]>::iter; +LL + let iter_fun = <[u32]>::iter; + | error[E0599]: no function or associated item named `iter` found for reference `&[u32]` in the current scope --> $DIR/issue-103271.rs:10:33 @@ -17,8 +18,9 @@ LL | let iter_fun2 = <(&[u32])>::iter; | help: the function `iter` is implemented on `[u32]` | -LL | let iter_fun2 = <([u32])>::iter; - | ~~~~~ +LL - let iter_fun2 = <(&[u32])>::iter; +LL + let iter_fun2 = <([u32])>::iter; + | error: aborting due to 2 previous errors diff --git a/tests/ui/type/pattern_types/assoc_const.default.stderr b/tests/ui/type/pattern_types/assoc_const.default.stderr new file mode 100644 index 0000000000000..8cff0cee7b972 --- /dev/null +++ b/tests/ui/type/pattern_types/assoc_const.default.stderr @@ -0,0 +1,38 @@ +error: generic parameters may not be used in const operations + --> $DIR/assoc_const.rs:17:41 + | +LL | fn foo(_: pattern_type!(u32 is ::START..=::END)) {} + | ^ cannot perform const operation using `T` + | + = note: type parameters may not be used in const expressions + = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions + +error: generic parameters may not be used in const operations + --> $DIR/assoc_const.rs:17:61 + | +LL | fn foo(_: pattern_type!(u32 is ::START..=::END)) {} + | ^ cannot perform const operation using `T` + | + = note: type parameters may not be used in const expressions + = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions + +error: generic parameters may not be used in const operations + --> $DIR/assoc_const.rs:20:40 + | +LL | fn bar(_: pattern_type!(u32 is T::START..=T::END)) {} + | ^^^^^^^^ cannot perform const operation using `T` + | + = note: type parameters may not be used in const expressions + = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions + +error: generic parameters may not be used in const operations + --> $DIR/assoc_const.rs:20:51 + | +LL | fn bar(_: pattern_type!(u32 is T::START..=T::END)) {} + | ^^^^^^ cannot perform const operation using `T` + | + = note: type parameters may not be used in const expressions + = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions + +error: aborting due to 4 previous errors + diff --git a/tests/ui/type/pattern_types/assoc_const.rs b/tests/ui/type/pattern_types/assoc_const.rs new file mode 100644 index 0000000000000..90508801990e8 --- /dev/null +++ b/tests/ui/type/pattern_types/assoc_const.rs @@ -0,0 +1,24 @@ +#![feature(pattern_types)] +#![feature(pattern_type_macro)] +#![cfg_attr(const_arg, feature(generic_const_exprs, generic_pattern_types))] +#![expect(incomplete_features)] +// gate-test-generic_pattern_types line to the test file. +//@ revisions: default const_arg + +//@[const_arg] check-pass + +use std::pat::pattern_type; + +trait Foo { + const START: u32; + const END: u32; +} + +fn foo(_: pattern_type!(u32 is ::START..=::END)) {} +//[default]~^ ERROR: generic parameters may not be used in const operations +//[default]~| ERROR: generic parameters may not be used in const operations +fn bar(_: pattern_type!(u32 is T::START..=T::END)) {} +//[default]~^ ERROR: generic parameters may not be used in const operations +//[default]~| ERROR: generic parameters may not be used in const operations + +fn main() {} diff --git a/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs index 55f45ade38804..c28fda6f91aea 100644 --- a/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs +++ b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs @@ -1,10 +1,14 @@ -#![feature(pattern_types, pattern_type_macro)] +//@known-bug: #127972 +//@ failure-status: 101 +//@ normalize-stderr: "note: .*\n\n" -> "" +//@ normalize-stderr: "thread 'rustc' panicked.*\n" -> "" +//@ normalize-stderr: "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: " +//@ rustc-env:RUST_BACKTRACE=0 + +#![feature(pattern_types, pattern_type_macro, generic_const_exprs)] #![allow(internal_features)] type Pat = std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>); -//~^ ERROR type and const arguments are not allowed on const parameter `START` -//~| ERROR type arguments are not allowed on const parameter `END` -//~| ERROR associated item constraints are not allowed here fn main() {} diff --git a/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr index 7f4e6e314f534..ffc6068eb17e1 100644 --- a/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr +++ b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr @@ -1,38 +1,21 @@ -error[E0109]: type and const arguments are not allowed on const parameter `START` - --> $DIR/bad_const_generics_args_on_const_param.rs:5:44 +warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/bad_const_generics_args_on_const_param.rs:8:47 | -LL | std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>); - | ----- ^^ ^^^ ^ type and const arguments not allowed - | | - | not allowed on const parameter `START` - | -note: const parameter `START` defined here - --> $DIR/bad_const_generics_args_on_const_param.rs:4:16 +LL | #![feature(pattern_types, pattern_type_macro, generic_const_exprs)] + | ^^^^^^^^^^^^^^^^^^^ | -LL | type Pat = - | ^^^^^ - -error[E0109]: type arguments are not allowed on const parameter `END` - --> $DIR/bad_const_generics_args_on_const_param.rs:5:64 + = note: see issue #76560 for more information + = error: internal compiler error: compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs:LL:CC: try_lower_anon_const_lit: received const param which shouldn't be possible + --> $DIR/bad_const_generics_args_on_const_param.rs:12:36 | LL | std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>); - | --- ^ type argument not allowed - | | - | not allowed on const parameter `END` - | -note: const parameter `END` defined here - --> $DIR/bad_const_generics_args_on_const_param.rs:4:34 - | -LL | type Pat = - | ^^^ + | ^^^^^^^^^^^^^^^^^^^ -error[E0229]: associated item constraints are not allowed here - --> $DIR/bad_const_generics_args_on_const_param.rs:5:67 - | -LL | std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>); - | ^^^^^^^^^^ associated item constraint not allowed here -error: aborting due to 3 previous errors +Box +query stack during panic: +#0 [type_of] expanding type alias `Pat` +#1 [check_well_formed] checking that `Pat` is well-formed +... and 2 other queries... use `env RUST_BACKTRACE=1` to see the full query stack +error: aborting due to 1 previous error; 1 warning emitted -Some errors have detailed explanations: E0109, E0229. -For more information about an error, try `rustc --explain E0109`. diff --git a/tests/ui/type/pattern_types/bad_pat.rs b/tests/ui/type/pattern_types/bad_pat.rs index 6cb2a0f1f8e74..549b0d11dd187 100644 --- a/tests/ui/type/pattern_types/bad_pat.rs +++ b/tests/ui/type/pattern_types/bad_pat.rs @@ -8,6 +8,6 @@ type NonNullU32_2 = pattern_type!(u32 is 1..=); type Positive2 = pattern_type!(i32 is 0..=); //~^ ERROR: inclusive range with no end type Wild = pattern_type!(() is _); -//~^ ERROR: wildcard patterns are not permitted for pattern types +//~^ ERROR: pattern not supported in pattern types fn main() {} diff --git a/tests/ui/type/pattern_types/bad_pat.stderr b/tests/ui/type/pattern_types/bad_pat.stderr index c857cc3c3add9..d2a5a20bf89b6 100644 --- a/tests/ui/type/pattern_types/bad_pat.stderr +++ b/tests/ui/type/pattern_types/bad_pat.stderr @@ -24,7 +24,7 @@ LL - type Positive2 = pattern_type!(i32 is 0..=); LL + type Positive2 = pattern_type!(i32 is 0..); | -error: wildcard patterns are not permitted for pattern types +error: pattern not supported in pattern types --> $DIR/bad_pat.rs:10:33 | LL | type Wild = pattern_type!(() is _); diff --git a/tests/ui/type/pattern_types/chars.rs b/tests/ui/type/pattern_types/chars.rs new file mode 100644 index 0000000000000..9073998b387b7 --- /dev/null +++ b/tests/ui/type/pattern_types/chars.rs @@ -0,0 +1,12 @@ +//! Check that chars can be used in ranges + +//@ check-pass + +#![feature(pattern_types)] +#![feature(pattern_type_macro)] + +use std::pat::pattern_type; + +const LOWERCASE: pattern_type!(char is 'a'..='z') = unsafe { std::mem::transmute('b') }; + +fn main() {} diff --git a/tests/ui/type/pattern_types/const_block.rs b/tests/ui/type/pattern_types/const_block.rs new file mode 100644 index 0000000000000..ed19b10671af0 --- /dev/null +++ b/tests/ui/type/pattern_types/const_block.rs @@ -0,0 +1,10 @@ +#![feature(pattern_types)] +#![feature(pattern_type_macro)] +#![feature(inline_const_pat)] +//@ check-pass + +use std::pat::pattern_type; + +fn bar(x: pattern_type!(u32 is 0..=const{ 5 + 5 })) {} + +fn main() {} diff --git a/tests/ui/type/pattern_types/const_generics.rs b/tests/ui/type/pattern_types/const_generics.rs index 5cef0dc030552..79d46c010d733 100644 --- a/tests/ui/type/pattern_types/const_generics.rs +++ b/tests/ui/type/pattern_types/const_generics.rs @@ -1,7 +1,7 @@ //@ check-pass -#![feature(pattern_types)] -#![feature(pattern_type_macro)] +#![feature(pattern_types, generic_pattern_types, pattern_type_macro)] +#![expect(incomplete_features)] use std::pat::pattern_type; diff --git a/tests/ui/type/pattern_types/derives.noimpl.stderr b/tests/ui/type/pattern_types/derives.noimpl.stderr deleted file mode 100644 index 9450e5753446b..0000000000000 --- a/tests/ui/type/pattern_types/derives.noimpl.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0369]: binary operation `==` cannot be applied to type `(i32) is 0..=999999999` - --> $DIR/derives.rs:14:20 - | -LL | #[derive(Clone, Copy, PartialEq)] - | --------- in this derive macro expansion -LL | #[repr(transparent)] -LL | struct Nanoseconds(NanoI32); - | ^^^^^^^ - | - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0369`. diff --git a/tests/ui/type/pattern_types/feature-gate-pattern_types.rs b/tests/ui/type/pattern_types/feature-gate-pattern_types.rs index b90ba4784023a..b4f4bd656f5ff 100644 --- a/tests/ui/type/pattern_types/feature-gate-pattern_types.rs +++ b/tests/ui/type/pattern_types/feature-gate-pattern_types.rs @@ -12,3 +12,4 @@ type Positive = pattern_type!(i32 is 0..); //~^ use of unstable library feature `pattern_type_macro` type Always = pattern_type!(Option is Some(_)); //~^ use of unstable library feature `pattern_type_macro` +//~| ERROR pattern not supported in pattern types diff --git a/tests/ui/type/pattern_types/feature-gate-pattern_types.stderr b/tests/ui/type/pattern_types/feature-gate-pattern_types.stderr index 69239d68bdc75..12508bcb54a34 100644 --- a/tests/ui/type/pattern_types/feature-gate-pattern_types.stderr +++ b/tests/ui/type/pattern_types/feature-gate-pattern_types.stderr @@ -48,6 +48,12 @@ LL | type Always = pattern_type!(Option is Some(_)); = help: add `#![feature(pattern_type_macro)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 5 previous errors +error: pattern not supported in pattern types + --> $DIR/feature-gate-pattern_types.rs:13:44 + | +LL | type Always = pattern_type!(Option is Some(_)); + | ^^^^^^^ + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/type/pattern_types/feature-gate-pattern_types2.rs b/tests/ui/type/pattern_types/feature-gate-pattern_types2.rs index 50c355c8de61a..c6b4b325fa35a 100644 --- a/tests/ui/type/pattern_types/feature-gate-pattern_types2.rs +++ b/tests/ui/type/pattern_types/feature-gate-pattern_types2.rs @@ -1,5 +1,4 @@ //@ compile-flags: -Zno-analysis -//@ check-pass #![feature(pattern_type_macro)] @@ -10,3 +9,4 @@ type Percent = pattern_type!(u32 is 0..=100); type Negative = pattern_type!(i32 is ..=0); type Positive = pattern_type!(i32 is 0..); type Always = pattern_type!(Option is Some(_)); +//~^ ERROR: pattern not supported in pattern types diff --git a/tests/ui/type/pattern_types/feature-gate-pattern_types2.stderr b/tests/ui/type/pattern_types/feature-gate-pattern_types2.stderr new file mode 100644 index 0000000000000..81bf9dea0e2fd --- /dev/null +++ b/tests/ui/type/pattern_types/feature-gate-pattern_types2.stderr @@ -0,0 +1,8 @@ +error: pattern not supported in pattern types + --> $DIR/feature-gate-pattern_types2.rs:11:44 + | +LL | type Always = pattern_type!(Option is Some(_)); + | ^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/type/pattern_types/free_const.rs b/tests/ui/type/pattern_types/free_const.rs new file mode 100644 index 0000000000000..2e29fce237970 --- /dev/null +++ b/tests/ui/type/pattern_types/free_const.rs @@ -0,0 +1,13 @@ +//@ check-pass + +#![feature(pattern_types)] +#![feature(pattern_type_macro)] + +use std::pat::pattern_type; + +const START: u32 = 0; +const END: u32 = 10; + +fn foo(_: pattern_type!(u32 is START..=END)) {} + +fn main() {} diff --git a/tests/ui/type/pattern_types/nested.rs b/tests/ui/type/pattern_types/nested.rs new file mode 100644 index 0000000000000..9ca9c7923dedd --- /dev/null +++ b/tests/ui/type/pattern_types/nested.rs @@ -0,0 +1,30 @@ +//! Check that pattern types can only have specific base types + +#![feature(pattern_types)] +#![feature(pattern_type_macro)] + +use std::pat::pattern_type; + +// Undoing an inner pattern type's restrictions should either be forbidden, +// or still validate correctly. +const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!(); +//~^ ERROR: not a valid base type for range patterns +//~| ERROR: mismatched types + +// We want to get the most narrowest version that a pattern could be +const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!(); +//~^ ERROR: not a valid base type for range patterns +//~| ERROR: mismatched types + +const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); +//~^ ERROR: not a valid base type for range patterns +//~| ERROR: mismatched types + +const BAD_NESTING4: pattern_type!(() is ..0) = todo!(); +//~^ ERROR: not a valid base type for range patterns +//~| ERROR: mismatched types + +const BAD_NESTING5: pattern_type!(f32 is 1.0 .. 2.0) = todo!(); +//~^ ERROR: not a valid base type for range patterns + +fn main() {} diff --git a/tests/ui/type/pattern_types/nested.stderr b/tests/ui/type/pattern_types/nested.stderr new file mode 100644 index 0000000000000..b753b0a9c9baa --- /dev/null +++ b/tests/ui/type/pattern_types/nested.stderr @@ -0,0 +1,96 @@ +error: `(u32) is 1..=` is not a valid base type for range patterns + --> $DIR/nested.rs:10:34 + | +LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: range patterns only support integers + --> $DIR/nested.rs:10:63 + | +LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!(); + | ^^^ + +error: `(i32) is 1..=` is not a valid base type for range patterns + --> $DIR/nested.rs:15:35 + | +LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: range patterns only support integers + --> $DIR/nested.rs:15:64 + | +LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!(); + | ^^^^^ + +error: `(i32) is 1..=` is not a valid base type for range patterns + --> $DIR/nested.rs:19:35 + | +LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: range patterns only support integers + --> $DIR/nested.rs:19:64 + | +LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); + | ^^^ + +error: `()` is not a valid base type for range patterns + --> $DIR/nested.rs:23:35 + | +LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!(); + | ^^ + | +note: range patterns only support integers + --> $DIR/nested.rs:23:41 + | +LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!(); + | ^^^ + +error: `f32` is not a valid base type for range patterns + --> $DIR/nested.rs:27:35 + | +LL | const BAD_NESTING5: pattern_type!(f32 is 1.0 .. 2.0) = todo!(); + | ^^^ + | +note: range patterns only support integers + --> $DIR/nested.rs:27:42 + | +LL | const BAD_NESTING5: pattern_type!(f32 is 1.0 .. 2.0) = todo!(); + | ^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/nested.rs:10:63 + | +LL | const BAD_NESTING: pattern_type!(pattern_type!(u32 is 1..) is 0..) = todo!(); + | ^ expected `(u32) is 1..=`, found integer + | + = note: expected pattern type `(u32) is 1..=` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/nested.rs:15:67 + | +LL | const BAD_NESTING2: pattern_type!(pattern_type!(i32 is 1..) is ..=-1) = todo!(); + | ^^ expected `(i32) is 1..=`, found integer + | + = note: expected pattern type `(i32) is 1..=` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/nested.rs:19:66 + | +LL | const BAD_NESTING3: pattern_type!(pattern_type!(i32 is 1..) is ..0) = todo!(); + | ^ expected `(i32) is 1..=`, found integer + | + = note: expected pattern type `(i32) is 1..=` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/nested.rs:23:43 + | +LL | const BAD_NESTING4: pattern_type!(() is ..0) = todo!(); + | ^ expected `()`, found integer + +error: aborting due to 9 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/type/pattern_types/pattern_type_mismatch.rs b/tests/ui/type/pattern_types/pattern_type_mismatch.rs new file mode 100644 index 0000000000000..0c88f27d83686 --- /dev/null +++ b/tests/ui/type/pattern_types/pattern_type_mismatch.rs @@ -0,0 +1,16 @@ +//! Check that pattern types patterns must be of the type of the base type + +#![feature(pattern_types)] +#![feature(pattern_type_macro)] + +use std::pat::pattern_type; + +const BAD_NESTING4: pattern_type!(u8 is 'a'..='a') = todo!(); +//~^ ERROR: mismatched types +//~| ERROR: mismatched types + +const BAD_NESTING5: pattern_type!(char is 1..=1) = todo!(); +//~^ ERROR: mismatched types +//~| ERROR: mismatched types + +fn main() {} diff --git a/tests/ui/type/pattern_types/pattern_type_mismatch.stderr b/tests/ui/type/pattern_types/pattern_type_mismatch.stderr new file mode 100644 index 0000000000000..4af92a89c445a --- /dev/null +++ b/tests/ui/type/pattern_types/pattern_type_mismatch.stderr @@ -0,0 +1,37 @@ +error[E0308]: mismatched types + --> $DIR/pattern_type_mismatch.rs:8:41 + | +LL | const BAD_NESTING4: pattern_type!(u8 is 'a'..='a') = todo!(); + | ^^^ expected `u8`, found `char` + | +help: if you meant to write a byte literal, prefix with `b` + | +LL | const BAD_NESTING4: pattern_type!(u8 is b'a'..='a') = todo!(); + | + + +error[E0308]: mismatched types + --> $DIR/pattern_type_mismatch.rs:8:47 + | +LL | const BAD_NESTING4: pattern_type!(u8 is 'a'..='a') = todo!(); + | ^^^ expected `u8`, found `char` + | +help: if you meant to write a byte literal, prefix with `b` + | +LL | const BAD_NESTING4: pattern_type!(u8 is 'a'..=b'a') = todo!(); + | + + +error[E0308]: mismatched types + --> $DIR/pattern_type_mismatch.rs:12:43 + | +LL | const BAD_NESTING5: pattern_type!(char is 1..=1) = todo!(); + | ^ expected `char`, found `u8` + +error[E0308]: mismatched types + --> $DIR/pattern_type_mismatch.rs:12:47 + | +LL | const BAD_NESTING5: pattern_type!(char is 1..=1) = todo!(); + | ^ expected `char`, found `u8` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/type/pattern_types/range_patterns.rs b/tests/ui/type/pattern_types/range_patterns.rs index ff87444b49e30..446a33195c8bd 100644 --- a/tests/ui/type/pattern_types/range_patterns.rs +++ b/tests/ui/type/pattern_types/range_patterns.rs @@ -3,6 +3,7 @@ #![allow(incomplete_features)] //@ normalize-stderr: "pref: Align\([1-8] bytes\)" -> "pref: $$SOME_ALIGN" +//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" use std::pat::pattern_type; diff --git a/tests/ui/type/pattern_types/range_patterns.stderr b/tests/ui/type/pattern_types/range_patterns.stderr index 0eed7c2ce1ce8..7da8cfd4dbc2d 100644 --- a/tests/ui/type/pattern_types/range_patterns.stderr +++ b/tests/ui/type/pattern_types/range_patterns.stderr @@ -36,8 +36,9 @@ error: layout_of(NonZero) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/range_patterns.rs:10:1 + --> $DIR/range_patterns.rs:11:1 | LL | type X = std::num::NonZeroU32; | ^^^^^^ @@ -73,8 +74,9 @@ error: layout_of((u32) is 1..=) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/range_patterns.rs:12:1 + --> $DIR/range_patterns.rs:13:1 | LL | type Y = pattern_type!(u32 is 1..); | ^^^^^^ @@ -137,6 +139,7 @@ error: layout_of(Option<(u32) is 1..=>) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(4 bytes), @@ -176,13 +179,15 @@ error: layout_of(Option<(u32) is 1..=>) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/range_patterns.rs:14:1 + --> $DIR/range_patterns.rs:15:1 | LL | type Z = Option; | ^^^^^^ @@ -245,6 +250,7 @@ error: layout_of(Option>) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, }, Layout { size: Size(4 bytes), @@ -284,13 +290,15 @@ error: layout_of(Option>) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, }, ], }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/range_patterns.rs:16:1 + --> $DIR/range_patterns.rs:17:1 | LL | type A = Option; | ^^^^^^ @@ -333,8 +341,9 @@ error: layout_of(NonZeroU32New) = Layout { }, max_repr_align: None, unadjusted_abi_align: Align(4 bytes), + randomization_seed: $SEED, } - --> $DIR/range_patterns.rs:18:1 + --> $DIR/range_patterns.rs:19:1 | LL | struct NonZeroU32New(pattern_type!(u32 is 1..)); | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/type/pattern_types/reverse_range.rs b/tests/ui/type/pattern_types/reverse_range.rs new file mode 100644 index 0000000000000..6a245615f1ac0 --- /dev/null +++ b/tests/ui/type/pattern_types/reverse_range.rs @@ -0,0 +1,14 @@ +//! Check that the range start must be smaller than the range end +//@ known-bug: unknown +//@ failure-status: 101 +//@ normalize-stderr: "note: .*\n\n" -> "" +//@ normalize-stderr: "thread 'rustc' panicked.*\n" -> "" +//@ normalize-stderr: "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: " +//@ rustc-env:RUST_BACKTRACE=0 + +#![feature(pattern_types)] +#![feature(pattern_type_macro)] + +use std::pat::pattern_type; + +const NONE: pattern_type!(u8 is 1..0) = unsafe { std::mem::transmute(3_u8) }; diff --git a/tests/ui/type/pattern_types/reverse_range.stderr b/tests/ui/type/pattern_types/reverse_range.stderr new file mode 100644 index 0000000000000..b714ca7d9ab96 --- /dev/null +++ b/tests/ui/type/pattern_types/reverse_range.stderr @@ -0,0 +1,17 @@ +error[E0601]: `main` function not found in crate `reverse_range` + --> $DIR/reverse_range.rs:14:78 + | +LL | const NONE: pattern_type!(u8 is 1..0) = unsafe { std::mem::transmute(3_u8) }; + | ^ consider adding a `main` function to `$DIR/reverse_range.rs` + + +assertion failed: end <= max_value +error: the compiler unexpectedly panicked. this is a bug. + +query stack during panic: +#0 [eval_to_allocation_raw] const-evaluating + checking `NONE` +#1 [eval_to_const_value_raw] simplifying constant for the type system `NONE` +... and 1 other queries... use `env RUST_BACKTRACE=1` to see the full query stack +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0601`. diff --git a/tests/ui/type/pattern_types/signed_ranges.rs b/tests/ui/type/pattern_types/signed_ranges.rs new file mode 100644 index 0000000000000..3725fbda7292a --- /dev/null +++ b/tests/ui/type/pattern_types/signed_ranges.rs @@ -0,0 +1,24 @@ +#![feature(pattern_types)] +#![feature(pattern_type_macro)] + +use std::pat::pattern_type; + +type Sign = pattern_type!(u32 is -10..); +//~^ ERROR: cannot apply unary operator `-` + +type SignedChar = pattern_type!(char is -'A'..); +//~^ ERROR: cannot apply unary operator `-` + +fn main() { + match 42_u8 { + -10..253 => {} + //~^ ERROR `u8: Neg` is not satisfied + _ => {} + } + + match 'A' { + -'\0'..'a' => {} + //~^ ERROR `char: Neg` is not satisfied + _ => {} + } +} diff --git a/tests/ui/type/pattern_types/signed_ranges.stderr b/tests/ui/type/pattern_types/signed_ranges.stderr new file mode 100644 index 0000000000000..79bf8501b639f --- /dev/null +++ b/tests/ui/type/pattern_types/signed_ranges.stderr @@ -0,0 +1,41 @@ +error[E0277]: the trait bound `u8: Neg` is not satisfied + --> $DIR/signed_ranges.rs:14:9 + | +LL | -10..253 => {} + | ^^^ the trait `Neg` is not implemented for `u8` + | + = help: the following other types implement trait `Neg`: + &f128 + &f16 + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + and 12 others + +error[E0277]: the trait bound `char: Neg` is not satisfied + --> $DIR/signed_ranges.rs:20:9 + | +LL | -'\0'..'a' => {} + | ^^^^^ the trait `Neg` is not implemented for `char` + +error[E0600]: cannot apply unary operator `-` to type `u32` + --> $DIR/signed_ranges.rs:6:34 + | +LL | type Sign = pattern_type!(u32 is -10..); + | ^^^ cannot apply unary operator `-` + | + = note: unsigned values cannot be negated + +error[E0600]: cannot apply unary operator `-` to type `char` + --> $DIR/signed_ranges.rs:9:41 + | +LL | type SignedChar = pattern_type!(char is -'A'..); + | ^^^^ cannot apply unary operator `-` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0277, E0600. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/type/pattern_types/transmute.rs b/tests/ui/type/pattern_types/transmute.rs new file mode 100644 index 0000000000000..43dd62a19e706 --- /dev/null +++ b/tests/ui/type/pattern_types/transmute.rs @@ -0,0 +1,32 @@ +#![feature(pattern_types, pattern_type_macro, generic_pattern_types)] +#![expect(incomplete_features)] + +use std::pat::pattern_type; + +// ok +fn create(x: u32) -> pattern_type!(u32 is S..=E) { + unsafe { std::mem::transmute(x) } +} + +// ok +fn unwrap(x: pattern_type!(u32 is S..=E)) -> u32 { + unsafe { std::mem::transmute(x) } +} + +// bad, only when S != u32::MIN or E != u32::MAX will this ok +fn non_base_ty_transmute( + x: Option, +) -> u32 { + unsafe { std::mem::transmute(x) } + //~^ ERROR types of different sizes +} + +// bad, only when S = u32::MIN and E = u32::MAX will this ok +fn wrapped_transmute( + x: Option, +) -> Option { + unsafe { std::mem::transmute(x) } + //~^ ERROR types of different sizes +} + +fn main() {} diff --git a/tests/ui/type/pattern_types/transmute.stderr b/tests/ui/type/pattern_types/transmute.stderr new file mode 100644 index 0000000000000..578549b515c10 --- /dev/null +++ b/tests/ui/type/pattern_types/transmute.stderr @@ -0,0 +1,21 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute.rs:20:14 + | +LL | unsafe { std::mem::transmute(x) } + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `Option<(u32) is S..=E>` (size can vary because of u32) + = note: target type: `u32` (32 bits) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute.rs:28:14 + | +LL | unsafe { std::mem::transmute(x) } + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `Option<(u32) is S..=E>` (size can vary because of u32) + = note: target type: `Option` (64 bits) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0512`. diff --git a/tests/ui/type/pattern_types/unimplemented_pat.rs b/tests/ui/type/pattern_types/unimplemented_pat.rs index b2398cec7c9b5..44851ec1a1049 100644 --- a/tests/ui/type/pattern_types/unimplemented_pat.rs +++ b/tests/ui/type/pattern_types/unimplemented_pat.rs @@ -1,14 +1,14 @@ //! This test ensures we do not ICE for unimplemented -//! patterns unless the feature gate is enabled. +//! patterns even if the feature gate is enabled. -#![feature(pattern_type_macro)] +#![feature(pattern_type_macro, pattern_types)] use std::pat::pattern_type; type Always = pattern_type!(Option is Some(_)); -//~^ ERROR: pattern types are unstable +//~^ ERROR: pattern not supported type Binding = pattern_type!(Option is x); -//~^ ERROR: pattern types are unstable +//~^ ERROR: pattern not supported fn main() {} diff --git a/tests/ui/type/pattern_types/unimplemented_pat.stderr b/tests/ui/type/pattern_types/unimplemented_pat.stderr index 7b0f9cbaa6a96..a1e55ed02c45a 100644 --- a/tests/ui/type/pattern_types/unimplemented_pat.stderr +++ b/tests/ui/type/pattern_types/unimplemented_pat.stderr @@ -1,23 +1,14 @@ -error[E0658]: pattern types are unstable - --> $DIR/unimplemented_pat.rs:8:15 +error: pattern not supported in pattern types + --> $DIR/unimplemented_pat.rs:8:44 | LL | type Always = pattern_type!(Option is Some(_)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #123646 for more information - = help: add `#![feature(pattern_types)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + | ^^^^^^^ -error[E0658]: pattern types are unstable - --> $DIR/unimplemented_pat.rs:11:16 +error: pattern not supported in pattern types + --> $DIR/unimplemented_pat.rs:11:45 | LL | type Binding = pattern_type!(Option is x); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #123646 for more information - = help: add `#![feature(pattern_types)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + | ^ error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/type/pattern_types/validity.rs b/tests/ui/type/pattern_types/validity.rs new file mode 100644 index 0000000000000..77a4e72f6755a --- /dev/null +++ b/tests/ui/type/pattern_types/validity.rs @@ -0,0 +1,37 @@ +//! Check that pattern types have their validity checked + +#![feature(pattern_types)] +#![feature(pattern_type_macro)] + +use std::pat::pattern_type; + +const BAD: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(0) }; +//~^ ERROR: it is undefined behavior to use this value + +const BAD_UNINIT: pattern_type!(u32 is 1..) = + //~^ ERROR: evaluation of constant value failed + unsafe { std::mem::transmute(std::mem::MaybeUninit::::uninit()) }; + +const BAD_PTR: pattern_type!(usize is 1..) = unsafe { std::mem::transmute(&42) }; +//~^ ERROR: evaluation of constant value failed + +const BAD_AGGREGATE: (pattern_type!(u32 is 1..), u32) = (unsafe { std::mem::transmute(0) }, 0); +//~^ ERROR: it is undefined behavior to use this value + +struct Foo(Bar); +struct Bar(pattern_type!(u32 is 1..)); + +const BAD_FOO: Foo = Foo(Bar(unsafe { std::mem::transmute(0) })); +//~^ ERROR: it is undefined behavior to use this value + +const CHAR_UNINIT: pattern_type!(char is 'A'..'Z') = + //~^ ERROR: evaluation of constant value failed + unsafe { std::mem::transmute(std::mem::MaybeUninit::::uninit()) }; + +const CHAR_OOB_PAT: pattern_type!(char is 'A'..'Z') = unsafe { std::mem::transmute('a') }; +//~^ ERROR: it is undefined behavior to use this value + +const CHAR_OOB: pattern_type!(char is 'A'..'Z') = unsafe { std::mem::transmute(u32::MAX) }; +//~^ ERROR: it is undefined behavior to use this value + +fn main() {} diff --git a/tests/ui/type/pattern_types/validity.stderr b/tests/ui/type/pattern_types/validity.stderr new file mode 100644 index 0000000000000..5bc18cfba3f7d --- /dev/null +++ b/tests/ui/type/pattern_types/validity.stderr @@ -0,0 +1,79 @@ +error[E0080]: it is undefined behavior to use this value + --> $DIR/validity.rs:8:1 + | +LL | const BAD: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(0) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0, but expected something greater or equal to 1 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + 00 00 00 00 │ .... + } + +error[E0080]: evaluation of constant value failed + --> $DIR/validity.rs:11:1 + | +LL | const BAD_UNINIT: pattern_type!(u32 is 1..) = + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory + +error[E0080]: evaluation of constant value failed + --> $DIR/validity.rs:15:1 + | +LL | const BAD_PTR: pattern_type!(usize is 1..) = unsafe { std::mem::transmute(&42) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into integer + | + = help: this code performed an operation that depends on the underlying bytes representing a pointer + = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported + +error[E0080]: it is undefined behavior to use this value + --> $DIR/validity.rs:18:1 + | +LL | const BAD_AGGREGATE: (pattern_type!(u32 is 1..), u32) = (unsafe { std::mem::transmute(0) }, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered 0, but expected something greater or equal to 1 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 8, align: 4) { + 00 00 00 00 00 00 00 00 │ ........ + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/validity.rs:24:1 + | +LL | const BAD_FOO: Foo = Foo(Bar(unsafe { std::mem::transmute(0) })); + | ^^^^^^^^^^^^^^^^^^ constructing invalid value at .0.0: encountered 0, but expected something greater or equal to 1 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + 00 00 00 00 │ .... + } + +error[E0080]: evaluation of constant value failed + --> $DIR/validity.rs:27:1 + | +LL | const CHAR_UNINIT: pattern_type!(char is 'A'..'Z') = + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory + +error[E0080]: it is undefined behavior to use this value + --> $DIR/validity.rs:31:1 + | +LL | const CHAR_OOB_PAT: pattern_type!(char is 'A'..'Z') = unsafe { std::mem::transmute('a') }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 97, but expected something in the range 65..=89 + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + 61 00 00 00 │ a... + } + +error[E0080]: it is undefined behavior to use this value + --> $DIR/validity.rs:34:1 + | +LL | const CHAR_OOB: pattern_type!(char is 'A'..'Z') = unsafe { std::mem::transmute(u32::MAX) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0xffffffff, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: 4, align: 4) { + ff ff ff ff │ .... + } + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/type/type-ascription-instead-of-statement-end.stderr b/tests/ui/type/type-ascription-instead-of-statement-end.stderr index 34c8864232393..34759b413d89d 100644 --- a/tests/ui/type/type-ascription-instead-of-statement-end.stderr +++ b/tests/ui/type/type-ascription-instead-of-statement-end.stderr @@ -7,8 +7,9 @@ LL | println!("test"): = note: if you meant to annotate an expression with a type, the type ascription syntax has been removed, see issue #101728 help: use a semicolon instead | -LL | println!("test"); - | ~ +LL - println!("test"): +LL + println!("test"); + | error: expected one of `.`, `;`, `?`, `}`, or an operator, found `:` --> $DIR/type-ascription-instead-of-statement-end.rs:7:21 diff --git a/tests/ui/type/type-ascription-with-fn-call.stderr b/tests/ui/type/type-ascription-with-fn-call.stderr index 2691f10cf3ed7..4222762373dcc 100644 --- a/tests/ui/type/type-ascription-with-fn-call.stderr +++ b/tests/ui/type/type-ascription-with-fn-call.stderr @@ -7,8 +7,9 @@ LL | f() : = note: if you meant to annotate an expression with a type, the type ascription syntax has been removed, see issue #101728 help: use a semicolon instead | -LL | f() ; - | ~ +LL - f() : +LL + f() ; + | error: aborting due to 1 previous error diff --git a/tests/ui/type/type-check/issue-41314.stderr b/tests/ui/type/type-check/issue-41314.stderr index 2a089029b0a51..6a1b22c542cdd 100644 --- a/tests/ui/type/type-check/issue-41314.stderr +++ b/tests/ui/type/type-check/issue-41314.stderr @@ -6,8 +6,9 @@ LL | X::Y { number } => {} | help: use the tuple variant pattern syntax instead | -LL | X::Y(number) => {} - | ~~~~~~~~ +LL - X::Y { number } => {} +LL + X::Y(number) => {} + | error: aborting due to 1 previous error diff --git a/tests/ui/type/type-check/point-at-inference-3.stderr b/tests/ui/type/type-check/point-at-inference-3.stderr index 663799e9f86b4..58460aefdf866 100644 --- a/tests/ui/type/type-check/point-at-inference-3.stderr +++ b/tests/ui/type/type-check/point-at-inference-3.stderr @@ -15,8 +15,9 @@ note: method defined here --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL help: change the type of the numeric literal from `u32` to `i32` | -LL | v.push(1i32); - | ~~~ +LL - v.push(1u32); +LL + v.push(1i32); + | error: aborting due to 1 previous error diff --git a/tests/ui/type/type-check/point-at-inference-4.rs b/tests/ui/type/type-check/point-at-inference-4.rs index 5745b7385327e..258374f299e24 100644 --- a/tests/ui/type/type-check/point-at-inference-4.rs +++ b/tests/ui/type/type-check/point-at-inference-4.rs @@ -4,7 +4,6 @@ impl S { fn infer(&self, a: A, b: B) {} //~^ NOTE method defined here //~| NOTE - //~| NOTE } fn main() { diff --git a/tests/ui/type/type-check/point-at-inference-4.stderr b/tests/ui/type/type-check/point-at-inference-4.stderr index a953ca70ea27a..52d603c598082 100644 --- a/tests/ui/type/type-check/point-at-inference-4.stderr +++ b/tests/ui/type/type-check/point-at-inference-4.stderr @@ -1,5 +1,5 @@ error[E0061]: this method takes 2 arguments but 1 argument was supplied - --> $DIR/point-at-inference-4.rs:12:7 + --> $DIR/point-at-inference-4.rs:11:7 | LL | s.infer(0i32); | ^^^^^------ argument #2 is missing @@ -8,14 +8,15 @@ note: method defined here --> $DIR/point-at-inference-4.rs:4:8 | LL | fn infer(&self, a: A, b: B) {} - | ^^^^^ ---- ---- + | ^^^^^ ---- help: provide the argument | -LL | s.infer(0i32, /* b */); - | ~~~~~~~~~~~~~~~ +LL - s.infer(0i32); +LL + s.infer(0i32, /* b */); + | error[E0308]: mismatched types - --> $DIR/point-at-inference-4.rs:19:24 + --> $DIR/point-at-inference-4.rs:18:24 | LL | s.infer(0i32); | - ---- this argument has type `i32`... @@ -31,8 +32,9 @@ LL | let t: S = s; found struct `S` help: change the type of the numeric literal from `i32` to `u32` | -LL | s.infer(0u32); - | ~~~ +LL - s.infer(0i32); +LL + s.infer(0u32); + | error: aborting due to 2 previous errors diff --git a/tests/ui/type/type-dependent-def-issue-49241.stderr b/tests/ui/type/type-dependent-def-issue-49241.stderr index cf372dc59681e..9b395af8c1392 100644 --- a/tests/ui/type/type-dependent-def-issue-49241.stderr +++ b/tests/ui/type/type-dependent-def-issue-49241.stderr @@ -6,8 +6,9 @@ LL | const l: usize = v.count(); | help: consider using `let` instead of `const` | -LL | let l: usize = v.count(); - | ~~~ +LL - const l: usize = v.count(); +LL + let l: usize = v.count(); + | error: aborting due to 1 previous error diff --git a/tests/ui/type/type-parameter-defaults-referencing-Self-ppaux.stderr b/tests/ui/type/type-parameter-defaults-referencing-Self-ppaux.stderr index 2d5bcf1fbc4cf..eea2e75a2382a 100644 --- a/tests/ui/type/type-parameter-defaults-referencing-Self-ppaux.stderr +++ b/tests/ui/type/type-parameter-defaults-referencing-Self-ppaux.stderr @@ -10,21 +10,20 @@ help: consider using a box or reference as appropriate LL | let y = x as dyn MyAdd; | ^ -error[E0038]: the trait `MyAdd` cannot be made into an object +error[E0038]: the trait `MyAdd` is not dyn compatible --> $DIR/type-parameter-defaults-referencing-Self-ppaux.rs:14:18 | LL | let y = x as dyn MyAdd; - | ^^^^^^^^^^^^^^ `MyAdd` cannot be made into an object + | ^^^^^^^^^^^^^^ `MyAdd` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/type-parameter-defaults-referencing-Self-ppaux.rs:6:55 | LL | trait MyAdd { fn add(&self, other: &Rhs) -> Self; } - | ----- ^^^^ ...because method `add` references the `Self` type in its return type - | | - | this trait cannot be made into an object... + | ----- this trait is not dyn compatible... ^^^^ ...because method `add` references the `Self` type in its return type = help: consider moving `add` to another trait - = help: only type `i32` implements the trait, consider using it directly instead + = help: only type `i32` implements `MyAdd`; consider using it directly instead. error: aborting due to 2 previous errors diff --git a/tests/ui/type/type-parameter-defaults-referencing-Self.stderr b/tests/ui/type/type-parameter-defaults-referencing-Self.stderr index c81405f03f8fa..23f10c9262c7b 100644 --- a/tests/ui/type/type-parameter-defaults-referencing-Self.stderr +++ b/tests/ui/type/type-parameter-defaults-referencing-Self.stderr @@ -7,7 +7,7 @@ LL | trait Foo { LL | fn foo(x: &dyn Foo) { } | ^^^ | - = note: because of the default `Self` reference, type parameters must be specified on object types + = note: because the parameter default references `Self`, the parameter must be specified on the object type help: set the type parameter to the desired type | LL | fn foo(x: &dyn Foo) { } diff --git a/tests/ui/type/wrong-call-return-type-due-to-generic-arg.stderr b/tests/ui/type/wrong-call-return-type-due-to-generic-arg.stderr index fbe6bfeebb149..53920bc9e02ec 100644 --- a/tests/ui/type/wrong-call-return-type-due-to-generic-arg.stderr +++ b/tests/ui/type/wrong-call-return-type-due-to-generic-arg.stderr @@ -13,8 +13,9 @@ LL | fn wrong_arg_type(x: u32) -> u32 { | ^^^^^^^^^^^^^^ ------ help: change the type of the numeric literal from `u16` to `u32` | -LL | let x = wrong_arg_type(0u32); - | ~~~ +LL - let x = wrong_arg_type(0u16); +LL + let x = wrong_arg_type(0u32); + | error[E0308]: mismatched types --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:19:30 @@ -52,8 +53,9 @@ LL | fn function(x: T, y: bool) -> T { | ^^^^^^^^ ---- ------- help: change the type of the numeric literal from `u32` to `u16` | -LL | let x: u16 = function(0u16, 0u8); - | ~~~ +LL - let x: u16 = function(0u32, 0u8); +LL + let x: u16 = function(0u16, 0u8); + | error[E0308]: mismatched types --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:25:27 @@ -77,8 +79,9 @@ LL | fn function(x: T, y: bool) -> T { | ^^^^^^^^ ---- help: change the type of the numeric literal from `u32` to `u16` | -LL | let x: u16 = function(0u16, true); - | ~~~ +LL - let x: u16 = function(0u32, true); +LL + let x: u16 = function(0u16, true); + | error[E0308]: mismatched types --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:26:32 @@ -102,8 +105,9 @@ LL | fn method(&self, x: T) -> T { | ^^^^^^ ---- help: change the type of the numeric literal from `u32` to `u16` | -LL | let x: u16 = (S {}).method(0u16); - | ~~~ +LL - let x: u16 = (S {}).method(0u32); +LL + let x: u16 = (S {}).method(0u16); + | error[E0308]: arguments to this function are incorrect --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:27:5 diff --git a/tests/ui/type_length_limit.polonius.stderr b/tests/ui/type_length_limit.polonius.stderr deleted file mode 100644 index bc09f15918328..0000000000000 --- a/tests/ui/type_length_limit.polonius.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: reached the type-length limit while instantiating `std::mem::drop::>` - --> $SRC_DIR/core/src/mem/mod.rs:LL:COL - | -LL | pub fn drop(_x: T) {} - | ^^^^^^^^^^^^^^^^^^^^^ - | - = note: the full type name has been written to '$TEST_BUILD_DIR/type_length_limit.polonius/type_length_limit.long-type.txt' - = help: consider adding a `#![type_length_limit="8"]` attribute to your crate - -error: aborting due to 1 previous error - diff --git a/tests/ui/type_length_limit.rs b/tests/ui/type_length_limit.rs index 87f5ffd76d7a6..b629455aced7e 100644 --- a/tests/ui/type_length_limit.rs +++ b/tests/ui/type_length_limit.rs @@ -2,6 +2,9 @@ //@ compile-flags: -Copt-level=0 -Zenforce-type-length-limit //~^^ ERROR reached the type-length limit +// The regex below normalizes the long type file name to make it suitable for compare-modes. +//@ normalize-stderr: "'\$TEST_BUILD_DIR/.*\.long-type.txt'" -> "'$$TEST_BUILD_DIR/$$FILE.long-type.txt'" + // Test that the type length limit can be changed. // The exact type depends on optimizations, so disable them. diff --git a/tests/ui/type_length_limit.stderr b/tests/ui/type_length_limit.stderr index 83353547d34db..d913b661c6f07 100644 --- a/tests/ui/type_length_limit.stderr +++ b/tests/ui/type_length_limit.stderr @@ -1,11 +1,11 @@ error: reached the type-length limit while instantiating `std::mem::drop::>` - --> $DIR/type_length_limit.rs:32:5 + --> $DIR/type_length_limit.rs:35:5 | LL | drop::>(None); | ^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider adding a `#![type_length_limit="4010"]` attribute to your crate - = note: the full type name has been written to '$TEST_BUILD_DIR/type_length_limit/type_length_limit.long-type.txt' + = note: the full type name has been written to '$TEST_BUILD_DIR/$FILE.long-type.txt' error: reached the type-length limit while instantiating `<{closure@rt::lang_start<()>::{closure#0}} as FnMut<()>>::call_mut` | diff --git a/tests/ui/typeck/attempted-access-non-fatal.stderr b/tests/ui/typeck/attempted-access-non-fatal.stderr index bff669727a1b8..03334759baf52 100644 --- a/tests/ui/typeck/attempted-access-non-fatal.stderr +++ b/tests/ui/typeck/attempted-access-non-fatal.stderr @@ -18,8 +18,9 @@ LL | let _ = 0.f; | help: if intended to be a floating point literal, consider adding a `0` after the period and a `f32` suffix | -LL | let _ = 0.0f32; - | ~~~~ +LL - let _ = 0.f; +LL + let _ = 0.0f32; + | error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields --> $DIR/attempted-access-non-fatal.rs:7:15 @@ -29,8 +30,9 @@ LL | let _ = 2.l; | help: if intended to be a floating point literal, consider adding a `0` after the period and a `f64` suffix | -LL | let _ = 2.0f64; - | ~~~~ +LL - let _ = 2.l; +LL + let _ = 2.0f64; + | error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields --> $DIR/attempted-access-non-fatal.rs:8:16 @@ -40,8 +42,9 @@ LL | let _ = 12.F; | help: if intended to be a floating point literal, consider adding a `0` after the period and a `f32` suffix | -LL | let _ = 12.0f32; - | ~~~~ +LL - let _ = 12.F; +LL + let _ = 12.0f32; + | error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields --> $DIR/attempted-access-non-fatal.rs:9:16 @@ -51,8 +54,9 @@ LL | let _ = 34.L; | help: if intended to be a floating point literal, consider adding a `0` after the period and a `f64` suffix | -LL | let _ = 34.0f64; - | ~~~~ +LL - let _ = 34.L; +LL + let _ = 34.0f64; + | error: aborting due to 6 previous errors diff --git a/tests/ui/typeck/bad-index-due-to-nested.current.stderr b/tests/ui/typeck/bad-index-due-to-nested.current.stderr new file mode 100644 index 0000000000000..dc3d998c399a9 --- /dev/null +++ b/tests/ui/typeck/bad-index-due-to-nested.current.stderr @@ -0,0 +1,72 @@ +error[E0277]: the trait bound `K: Hash` is not satisfied + --> $DIR/bad-index-due-to-nested.rs:24:5 + | +LL | map[k] + | ^^^ the trait `Hash` is not implemented for `K` + | +note: required for `HashMap` to implement `Index<&K>` + --> $DIR/bad-index-due-to-nested.rs:11:12 + | +LL | impl Index<&K> for HashMap + | ^^^^^^^^^ ^^^^^^^^^^^^^ +LL | where +LL | K: Hash, + | ---- unsatisfied trait bound introduced here +help: consider restricting type parameter `K` with trait `Hash` + | +LL | fn index<'a, K: std::hash::Hash, V>(map: &'a HashMap, k: K) -> &'a V { + | +++++++++++++++++ + +error[E0277]: the trait bound `V: Copy` is not satisfied + --> $DIR/bad-index-due-to-nested.rs:24:5 + | +LL | map[k] + | ^^^ the trait `Copy` is not implemented for `V` + | +note: required for `HashMap` to implement `Index<&K>` + --> $DIR/bad-index-due-to-nested.rs:11:12 + | +LL | impl Index<&K> for HashMap + | ^^^^^^^^^ ^^^^^^^^^^^^^ +... +LL | V: Copy, + | ---- unsatisfied trait bound introduced here +help: consider restricting type parameter `V` with trait `Copy` + | +LL | fn index<'a, K, V: std::marker::Copy>(map: &'a HashMap, k: K) -> &'a V { + | +++++++++++++++++++ + +error[E0308]: mismatched types + --> $DIR/bad-index-due-to-nested.rs:24:9 + | +LL | fn index<'a, K, V>(map: &'a HashMap, k: K) -> &'a V { + | - found this type parameter +LL | map[k] + | ^ expected `&K`, found type parameter `K` + | + = note: expected reference `&_` + found type parameter `_` +help: consider borrowing here + | +LL | map[&k] + | + + +error[E0308]: mismatched types + --> $DIR/bad-index-due-to-nested.rs:24:5 + | +LL | fn index<'a, K, V>(map: &'a HashMap, k: K) -> &'a V { + | - found this type parameter ----- expected `&'a V` because of return type +LL | map[k] + | ^^^^^^ expected `&V`, found type parameter `V` + | + = note: expected reference `&'a _` + found type parameter `_` +help: consider borrowing here + | +LL | &map[k] + | + + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/typeck/bad-index-due-to-nested.next.stderr b/tests/ui/typeck/bad-index-due-to-nested.next.stderr new file mode 100644 index 0000000000000..a0b275b7852ba --- /dev/null +++ b/tests/ui/typeck/bad-index-due-to-nested.next.stderr @@ -0,0 +1,76 @@ +error[E0277]: the trait bound `K: Hash` is not satisfied + --> $DIR/bad-index-due-to-nested.rs:24:5 + | +LL | map[k] + | ^^^ the trait `Hash` is not implemented for `K` + | +note: required for `HashMap` to implement `Index<&K>` + --> $DIR/bad-index-due-to-nested.rs:11:12 + | +LL | impl Index<&K> for HashMap + | ^^^^^^^^^ ^^^^^^^^^^^^^ +LL | where +LL | K: Hash, + | ---- unsatisfied trait bound introduced here +help: consider restricting type parameter `K` with trait `Hash` + | +LL | fn index<'a, K: std::hash::Hash, V>(map: &'a HashMap, k: K) -> &'a V { + | +++++++++++++++++ + +error[E0277]: the trait bound `V: Copy` is not satisfied + --> $DIR/bad-index-due-to-nested.rs:24:5 + | +LL | map[k] + | ^^^ the trait `Copy` is not implemented for `V` + | +note: required for `HashMap` to implement `Index<&K>` + --> $DIR/bad-index-due-to-nested.rs:11:12 + | +LL | impl Index<&K> for HashMap + | ^^^^^^^^^ ^^^^^^^^^^^^^ +... +LL | V: Copy, + | ---- unsatisfied trait bound introduced here +help: consider restricting type parameter `V` with trait `Copy` + | +LL | fn index<'a, K, V: std::marker::Copy>(map: &'a HashMap, k: K) -> &'a V { + | +++++++++++++++++++ + +error[E0308]: mismatched types + --> $DIR/bad-index-due-to-nested.rs:24:9 + | +LL | fn index<'a, K, V>(map: &'a HashMap, k: K) -> &'a V { + | - found this type parameter +LL | map[k] + | ^ expected `&K`, found type parameter `K` + | + = note: expected reference `&_` + found type parameter `_` +help: consider borrowing here + | +LL | map[&k] + | + + +error[E0277]: the trait bound `K: Hash` is not satisfied + --> $DIR/bad-index-due-to-nested.rs:24:5 + | +LL | map[k] + | ^^^^^^ the trait `Hash` is not implemented for `K` + | +note: required for `HashMap` to implement `Index<&K>` + --> $DIR/bad-index-due-to-nested.rs:11:12 + | +LL | impl Index<&K> for HashMap + | ^^^^^^^^^ ^^^^^^^^^^^^^ +LL | where +LL | K: Hash, + | ---- unsatisfied trait bound introduced here +help: consider restricting type parameter `K` with trait `Hash` + | +LL | fn index<'a, K: std::hash::Hash, V>(map: &'a HashMap, k: K) -> &'a V { + | +++++++++++++++++ + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/typeck/bad-index-due-to-nested.rs b/tests/ui/typeck/bad-index-due-to-nested.rs index 2564b530004e5..e7f385865af5f 100644 --- a/tests/ui/typeck/bad-index-due-to-nested.rs +++ b/tests/ui/typeck/bad-index-due-to-nested.rs @@ -1,3 +1,7 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) + use std::hash::Hash; use std::marker::PhantomData; use std::ops::Index; @@ -21,7 +25,8 @@ fn index<'a, K, V>(map: &'a HashMap, k: K) -> &'a V { //~^ ERROR the trait bound `K: Hash` is not satisfied //~| ERROR the trait bound `V: Copy` is not satisfied //~| ERROR mismatched types - //~| ERROR mismatched types + //[current]~| ERROR mismatched types + //[next]~^^^^^ ERROR the trait bound `K: Hash` is not satisfied } fn main() {} diff --git a/tests/ui/typeck/bad-index-due-to-nested.stderr b/tests/ui/typeck/bad-index-due-to-nested.stderr deleted file mode 100644 index dd2ce092368a8..0000000000000 --- a/tests/ui/typeck/bad-index-due-to-nested.stderr +++ /dev/null @@ -1,72 +0,0 @@ -error[E0277]: the trait bound `K: Hash` is not satisfied - --> $DIR/bad-index-due-to-nested.rs:20:5 - | -LL | map[k] - | ^^^ the trait `Hash` is not implemented for `K` - | -note: required for `HashMap` to implement `Index<&K>` - --> $DIR/bad-index-due-to-nested.rs:7:12 - | -LL | impl Index<&K> for HashMap - | ^^^^^^^^^ ^^^^^^^^^^^^^ -LL | where -LL | K: Hash, - | ---- unsatisfied trait bound introduced here -help: consider restricting type parameter `K` with trait `Hash` - | -LL | fn index<'a, K: std::hash::Hash, V>(map: &'a HashMap, k: K) -> &'a V { - | +++++++++++++++++ - -error[E0277]: the trait bound `V: Copy` is not satisfied - --> $DIR/bad-index-due-to-nested.rs:20:5 - | -LL | map[k] - | ^^^ the trait `Copy` is not implemented for `V` - | -note: required for `HashMap` to implement `Index<&K>` - --> $DIR/bad-index-due-to-nested.rs:7:12 - | -LL | impl Index<&K> for HashMap - | ^^^^^^^^^ ^^^^^^^^^^^^^ -... -LL | V: Copy, - | ---- unsatisfied trait bound introduced here -help: consider restricting type parameter `V` with trait `Copy` - | -LL | fn index<'a, K, V: std::marker::Copy>(map: &'a HashMap, k: K) -> &'a V { - | +++++++++++++++++++ - -error[E0308]: mismatched types - --> $DIR/bad-index-due-to-nested.rs:20:9 - | -LL | fn index<'a, K, V>(map: &'a HashMap, k: K) -> &'a V { - | - found this type parameter -LL | map[k] - | ^ expected `&K`, found type parameter `K` - | - = note: expected reference `&_` - found type parameter `_` -help: consider borrowing here - | -LL | map[&k] - | + - -error[E0308]: mismatched types - --> $DIR/bad-index-due-to-nested.rs:20:5 - | -LL | fn index<'a, K, V>(map: &'a HashMap, k: K) -> &'a V { - | - found this type parameter ----- expected `&'a V` because of return type -LL | map[k] - | ^^^^^^ expected `&V`, found type parameter `V` - | - = note: expected reference `&'a _` - found type parameter `_` -help: consider borrowing here - | -LL | &map[k] - | + - -error: aborting due to 4 previous errors - -Some errors have detailed explanations: E0277, E0308. -For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/typeck/check-args-on-fn-err-2.stderr b/tests/ui/typeck/check-args-on-fn-err-2.stderr index 301bb88dbacf6..ccc006a902de4 100644 --- a/tests/ui/typeck/check-args-on-fn-err-2.stderr +++ b/tests/ui/typeck/check-args-on-fn-err-2.stderr @@ -8,8 +8,9 @@ LL | a((), 1i32 == 2u32); | help: change the type of the numeric literal from `u32` to `i32` | -LL | a((), 1i32 == 2i32); - | ~~~ +LL - a((), 1i32 == 2u32); +LL + a((), 1i32 == 2i32); + | error[E0425]: cannot find function `a` in this scope --> $DIR/check-args-on-fn-err-2.rs:2:5 diff --git a/tests/ui/issues/issue-40610.rs b/tests/ui/typeck/coercion-check-for-addition-issue-40610.rs similarity index 100% rename from tests/ui/issues/issue-40610.rs rename to tests/ui/typeck/coercion-check-for-addition-issue-40610.rs diff --git a/tests/ui/typeck/coercion-check-for-addition-issue-40610.stderr b/tests/ui/typeck/coercion-check-for-addition-issue-40610.stderr new file mode 100644 index 0000000000000..5e424862d97a4 --- /dev/null +++ b/tests/ui/typeck/coercion-check-for-addition-issue-40610.stderr @@ -0,0 +1,11 @@ +error[E0369]: cannot add `()` to `()` + --> $DIR/coercion-check-for-addition-issue-40610.rs:4:8 + | +LL | () + f(&[1.0]); + | -- ^ --------- () + | | + | () + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0369`. diff --git a/tests/ui/issues/issue-40861.rs b/tests/ui/typeck/coercion-check-for-indexing-expression-issue-40861.rs similarity index 100% rename from tests/ui/issues/issue-40861.rs rename to tests/ui/typeck/coercion-check-for-indexing-expression-issue-40861.rs diff --git a/tests/ui/typeck/coercion-check-for-indexing-expression-issue-40861.stderr b/tests/ui/typeck/coercion-check-for-indexing-expression-issue-40861.stderr new file mode 100644 index 0000000000000..13bc0cd94f37c --- /dev/null +++ b/tests/ui/typeck/coercion-check-for-indexing-expression-issue-40861.stderr @@ -0,0 +1,11 @@ +error[E0608]: cannot index into a value of type `()` + --> $DIR/coercion-check-for-indexing-expression-issue-40861.rs:4:7 + | +LL | ()[f(&[1.0])]; + | ^^^^^^^^^^^ + | + = help: to access tuple elements, use tuple indexing syntax (e.g., `tuple.0`) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0608`. diff --git a/tests/ui/typeck/cyclic_type_ice.stderr b/tests/ui/typeck/cyclic_type_ice.stderr index 36715b4ee5d1f..4dc02a53c0211 100644 --- a/tests/ui/typeck/cyclic_type_ice.stderr +++ b/tests/ui/typeck/cyclic_type_ice.stderr @@ -22,8 +22,9 @@ LL | let f = |_, _| (); | ^^^^^^ help: provide the argument | -LL | f(/* */, /* */); - | ~~~~~~~~~~~~~~~~ +LL - f(f); +LL + f(/* */, /* */); + | error: aborting due to 2 previous errors diff --git a/tests/ui/typeck/deref-multi.stderr b/tests/ui/typeck/deref-multi.stderr index 4346e273d0d66..02513853c486d 100644 --- a/tests/ui/typeck/deref-multi.stderr +++ b/tests/ui/typeck/deref-multi.stderr @@ -34,8 +34,9 @@ LL | &x | help: consider removing the `&` and dereferencing the borrow instead | -LL | *x - | ~ +LL - &x +LL + *x + | error[E0308]: mismatched types --> $DIR/deref-multi.rs:17:5 @@ -49,8 +50,9 @@ LL | &x found reference `&Box` help: consider removing the `&` and dereferencing the borrow instead | -LL | *x - | ~ +LL - &x +LL + *x + | error[E0308]: mismatched types --> $DIR/deref-multi.rs:22:5 diff --git a/tests/ui/typeck/ice-self-mismatch-const-generics.stderr b/tests/ui/typeck/ice-self-mismatch-const-generics.stderr index c502ea4565f68..8b820668129ff 100644 --- a/tests/ui/typeck/ice-self-mismatch-const-generics.stderr +++ b/tests/ui/typeck/ice-self-mismatch-const-generics.stderr @@ -8,12 +8,13 @@ LL | pub fn new(thing: T) -> GenericStruct<1, T> { LL | Self { thing } | ^^^^^^^^^^^^^^ expected `1`, found `0` | - = note: expected struct `GenericStruct<_, 1>` - found struct `GenericStruct<_, 0>` + = note: expected struct `GenericStruct<1, _>` + found struct `GenericStruct<0, _>` help: use the type name directly | -LL | GenericStruct::<1, T> { thing } - | ~~~~~~~~~~~~~~~~~~~~~ +LL - Self { thing } +LL + GenericStruct::<1, T> { thing } + | error[E0308]: mismatched types --> $DIR/ice-self-mismatch-const-generics.rs:20:9 @@ -25,12 +26,13 @@ LL | pub fn new(thing: T) -> GenericStruct2<1, T> { LL | Self { 0: thing } | ^^^^^^^^^^^^^^^^^ expected `1`, found `0` | - = note: expected struct `GenericStruct2<_, 1>` - found struct `GenericStruct2<_, 0>` + = note: expected struct `GenericStruct2<1, _>` + found struct `GenericStruct2<0, _>` help: use the type name directly | -LL | GenericStruct2::<1, T> { 0: thing } - | ~~~~~~~~~~~~~~~~~~~~~~ +LL - Self { 0: thing } +LL + GenericStruct2::<1, T> { 0: thing } + | error: aborting due to 2 previous errors diff --git a/tests/ui/typeck/ice-unexpected-region-123863.stderr b/tests/ui/typeck/ice-unexpected-region-123863.stderr index 742096f3861f6..8a4d767c14350 100644 --- a/tests/ui/typeck/ice-unexpected-region-123863.stderr +++ b/tests/ui/typeck/ice-unexpected-region-123863.stderr @@ -38,8 +38,9 @@ LL | Inner::concat_strs::<"a">::A | help: if there were a trait named `Example` with associated type `concat_strs` implemented for `Inner<_>`, you could use the fully-qualified path | -LL | as Example>::concat_strs::A - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - Inner::concat_strs::<"a">::A +LL + as Example>::concat_strs::A + | error: aborting due to 3 previous errors diff --git a/tests/ui/typeck/issue-104582.stderr b/tests/ui/typeck/issue-104582.stderr index 61b6b23642cce..704579698b90e 100644 --- a/tests/ui/typeck/issue-104582.stderr +++ b/tests/ui/typeck/issue-104582.stderr @@ -6,8 +6,9 @@ LL | let my_var: String(String?); | help: if you meant to express that the type might not contain a value, use the `Option` wrapper type | -LL | let my_var: String(Option); - | +++++++ ~ +LL - let my_var: String(String?); +LL + let my_var: String(Option); + | error[E0214]: parenthesized type parameters may only be used with a `Fn` trait --> $DIR/issue-104582.rs:2:17 @@ -17,8 +18,9 @@ LL | let my_var: String(String?); | help: use angle brackets instead | -LL | let my_var: String; - | ~ ~ +LL - let my_var: String(String?); +LL + let my_var: String; + | error: aborting due to 2 previous errors diff --git a/tests/ui/typeck/issue-107775.stderr b/tests/ui/typeck/issue-107775.stderr index 180b0183a3f2d..1be2689746941 100644 --- a/tests/ui/typeck/issue-107775.stderr +++ b/tests/ui/typeck/issue-107775.stderr @@ -6,10 +6,12 @@ LL | map.insert(1, Struct::do_something); | | | ... which causes `map` to have type `HashMap<{integer}, fn(u8) -> Pin + Send>> {::do_something::<'_>}>` LL | Self { map } - | ^^^ expected `HashMap Pin<...>>`, found `HashMap<{integer}, ...>` + | ^^^ expected `HashMap Pin>>`, found `HashMap<{integer}, ...>` | = note: expected struct `HashMap Pin + Send + 'static)>>>` found struct `HashMap<{integer}, fn(_) -> Pin + Send>> {::do_something::<'_>}>` + = note: fn items are distinct from fn pointers + = help: consider casting the fn item to a fn pointer: `::do_something::<'_> as fn(u8) -> Pin + Send + 'static)>>` error: aborting due to 1 previous error diff --git a/tests/ui/typeck/issue-110052.stderr b/tests/ui/typeck/issue-110052.stderr index 5eb10d9a30e86..649fc8429b9c2 100644 --- a/tests/ui/typeck/issue-110052.stderr +++ b/tests/ui/typeck/issue-110052.stderr @@ -6,10 +6,12 @@ LL | for<'iter> dyn Validator<<&'iter I>::Item>:, | help: use fully-qualified syntax | -LL | for<'iter> dyn Validator<<&'iter I as IntoAsyncIterator>::Item>:, - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | for<'iter> dyn Validator<<&'iter I as IntoIterator>::Item>:, - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - for<'iter> dyn Validator<<&'iter I>::Item>:, +LL + for<'iter> dyn Validator<<&'iter I as IntoAsyncIterator>::Item>:, + | +LL - for<'iter> dyn Validator<<&'iter I>::Item>:, +LL + for<'iter> dyn Validator<<&'iter I as IntoIterator>::Item>:, + | error: aborting due to 1 previous error diff --git a/tests/ui/typeck/issue-112252-ptr-arithmetics-help.stderr b/tests/ui/typeck/issue-112252-ptr-arithmetics-help.stderr index f81736245f3cf..18a992b2f341d 100644 --- a/tests/ui/typeck/issue-112252-ptr-arithmetics-help.stderr +++ b/tests/ui/typeck/issue-112252-ptr-arithmetics-help.stderr @@ -8,8 +8,9 @@ LL | let _a = _ptr1 + 5; | help: consider using `wrapping_add` or `add` for pointer + {integer} | -LL | let _a = _ptr1.wrapping_add(5); - | ~~~~~~~~~~~~~~ + +LL - let _a = _ptr1 + 5; +LL + let _a = _ptr1.wrapping_add(5); + | error[E0369]: cannot subtract `{integer}` from `*const u32` --> $DIR/issue-112252-ptr-arithmetics-help.rs:7:20 @@ -21,8 +22,9 @@ LL | let _b = _ptr1 - 5; | help: consider using `wrapping_sub` or `sub` for pointer - {integer} | -LL | let _b = _ptr1.wrapping_sub(5); - | ~~~~~~~~~~~~~~ + +LL - let _b = _ptr1 - 5; +LL + let _b = _ptr1.wrapping_sub(5); + | error[E0369]: cannot subtract `*const u32` from `*const u32` --> $DIR/issue-112252-ptr-arithmetics-help.rs:8:20 @@ -34,8 +36,9 @@ LL | let _c = _ptr2 - _ptr1; | help: consider using `offset_from` for pointer - pointer if the pointers point to the same allocation | -LL | let _c = unsafe { _ptr2.offset_from(_ptr1) }; - | ++++++++ ~~~~~~~~~~~~~ +++ +LL - let _c = _ptr2 - _ptr1; +LL + let _c = unsafe { _ptr2.offset_from(_ptr1) }; + | error[E0608]: cannot index into a value of type `*const u32` --> $DIR/issue-112252-ptr-arithmetics-help.rs:9:19 @@ -45,8 +48,9 @@ LL | let _d = _ptr1[5]; | help: consider using `wrapping_add` or `add` for indexing into raw pointer | -LL | let _d = _ptr1.wrapping_add(5); - | ~~~~~~~~~~~~~~ ~ +LL - let _d = _ptr1[5]; +LL + let _d = _ptr1.wrapping_add(5); + | error: aborting due to 4 previous errors diff --git a/tests/ui/typeck/issue-114529-illegal-break-with-value.stderr b/tests/ui/typeck/issue-114529-illegal-break-with-value.stderr index 731f234c162ad..de993df722ceb 100644 --- a/tests/ui/typeck/issue-114529-illegal-break-with-value.stderr +++ b/tests/ui/typeck/issue-114529-illegal-break-with-value.stderr @@ -8,8 +8,9 @@ LL | break 9; | help: use `break` on its own without a value inside this `while` loop | -LL | break; - | ~~~~~ +LL - break 9; +LL + break; + | error[E0571]: `break` with value from a `while` loop --> $DIR/issue-114529-illegal-break-with-value.rs:16:13 @@ -21,8 +22,9 @@ LL | break v; | help: use `break` on its own without a value inside this `while` loop | -LL | break; - | ~~~~~ +LL - break v; +LL + break; + | error[E0571]: `break` with value from a `while` loop --> $DIR/issue-114529-illegal-break-with-value.rs:22:9 @@ -36,8 +38,11 @@ LL | | }); | help: use `break` on its own without a value inside this `while` loop | -LL | break; - | ~~~~~ +LL - break (|| { +LL - let local = 9; +LL - }); +LL + break; + | error: aborting due to 3 previous errors diff --git a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.current.stderr b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.current.stderr new file mode 100644 index 0000000000000..92ad83c330000 --- /dev/null +++ b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.current.stderr @@ -0,0 +1,17 @@ +error[E0308]: mismatched types + --> $DIR/const-in-impl-fn-return-type.rs:20:39 + | +LL | fn func() -> [(); { () }] { + | ^^ expected `usize`, found `()` + +error: the constant `N` is not of type `usize` + --> $DIR/const-in-impl-fn-return-type.rs:12:32 + | +LL | fn func() -> [(); N]; + | ^^^^^^^ expected `usize`, found `u32` + | + = note: the length of array `[(); N]` must be type `usize` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.next.stderr b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.next.stderr new file mode 100644 index 0000000000000..92ad83c330000 --- /dev/null +++ b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.next.stderr @@ -0,0 +1,17 @@ +error[E0308]: mismatched types + --> $DIR/const-in-impl-fn-return-type.rs:20:39 + | +LL | fn func() -> [(); { () }] { + | ^^ expected `usize`, found `()` + +error: the constant `N` is not of type `usize` + --> $DIR/const-in-impl-fn-return-type.rs:12:32 + | +LL | fn func() -> [(); N]; + | ^^^^^^^ expected `usize`, found `u32` + | + = note: the length of array `[(); N]` must be type `usize` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.rs b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.rs index 5eef268872113..6bbac9d45bbe5 100644 --- a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.rs +++ b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.rs @@ -1,4 +1,9 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) + // Regression test for #114918 + // Test that a const generic enclosed in a block within the return type // of an impl fn produces a type mismatch error instead of triggering // a const eval cycle diff --git a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.stderr b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.stderr deleted file mode 100644 index 8017e5446cc71..0000000000000 --- a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/const-in-impl-fn-return-type.rs:15:39 - | -LL | fn func() -> [(); { () }] { - | ^^ expected `usize`, found `()` - -error: the constant `N` is not of type `usize` - --> $DIR/const-in-impl-fn-return-type.rs:7:32 - | -LL | fn func() -> [(); N]; - | ^^^^^^^ expected `usize`, found `u32` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/issue-120856.rs b/tests/ui/typeck/issue-120856.rs index e435a0f9d8e89..51dd63a6f89da 100644 --- a/tests/ui/typeck/issue-120856.rs +++ b/tests/ui/typeck/issue-120856.rs @@ -1,5 +1,5 @@ pub type Archived = ::Archived; -//~^ ERROR failed to resolve: use of undeclared crate or module `m` -//~| ERROR failed to resolve: use of undeclared crate or module `n` +//~^ ERROR failed to resolve: use of unresolved module or unlinked crate `m` +//~| ERROR failed to resolve: use of unresolved module or unlinked crate `n` fn main() {} diff --git a/tests/ui/typeck/issue-120856.stderr b/tests/ui/typeck/issue-120856.stderr index 1fc8b20047355..e366744409f4e 100644 --- a/tests/ui/typeck/issue-120856.stderr +++ b/tests/ui/typeck/issue-120856.stderr @@ -1,20 +1,24 @@ -error[E0433]: failed to resolve: use of undeclared crate or module `n` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `n` --> $DIR/issue-120856.rs:1:37 | LL | pub type Archived = ::Archived; | ^ | | - | use of undeclared crate or module `n` + | use of unresolved module or unlinked crate `n` | help: a trait with a similar name exists: `Fn` + | + = help: you might be missing a crate named `n` -error[E0433]: failed to resolve: use of undeclared crate or module `m` +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `m` --> $DIR/issue-120856.rs:1:25 | LL | pub type Archived = ::Archived; | ^ | | - | use of undeclared crate or module `m` + | use of unresolved module or unlinked crate `m` | help: a type parameter with a similar name exists: `T` + | + = help: you might be missing a crate named `m` error: aborting due to 2 previous errors diff --git a/tests/ui/typeck/issue-29181.stderr b/tests/ui/typeck/issue-29181.stderr index 53addf2fe4d09..ca82405966ecd 100644 --- a/tests/ui/typeck/issue-29181.stderr +++ b/tests/ui/typeck/issue-29181.stderr @@ -13,7 +13,7 @@ LL | let _ = |x: f64| x * 2.0.exp(); help: you must specify a concrete type for this numeric value, like `f32` | LL | let _ = |x: f64| x * 2.0_f32.exp(); - | ~~~~~~~ + | ++++ error: aborting due to 2 previous errors diff --git a/tests/ui/typeck/issue-43189.rs b/tests/ui/typeck/issue-43189.rs index 9c8cc70e63a6a..ee0fe15eebace 100644 --- a/tests/ui/typeck/issue-43189.rs +++ b/tests/ui/typeck/issue-43189.rs @@ -1,7 +1,6 @@ // Issue 46112: An extern crate pub re-exporting libcore was causing // paths rooted from `std` to be misrendered in the diagnostic output. -//@ ignore-windows //@ aux-build:xcrate-issue-43189-a.rs //@ aux-build:xcrate-issue-43189-b.rs diff --git a/tests/ui/typeck/issue-43189.stderr b/tests/ui/typeck/issue-43189.stderr index 8432cbeca2a4b..3cc1ba9ddfe18 100644 --- a/tests/ui/typeck/issue-43189.stderr +++ b/tests/ui/typeck/issue-43189.stderr @@ -1,5 +1,5 @@ error[E0599]: no method named `a` found for unit type `()` in the current scope - --> $DIR/issue-43189.rs:10:8 + --> $DIR/issue-43189.rs:9:8 | LL | ().a(); | ^ method not found in `()` diff --git a/tests/ui/typeck/issue-46112.rs b/tests/ui/typeck/issue-46112.rs index 4671ddd06c831..cc8029771d6d4 100644 --- a/tests/ui/typeck/issue-46112.rs +++ b/tests/ui/typeck/issue-46112.rs @@ -1,7 +1,6 @@ // Issue 46112: An extern crate pub re-exporting libcore was causing // paths rooted from `std` to be misrendered in the diagnostic output. -//@ ignore-windows //@ aux-build:xcrate-issue-46112-rexport-core.rs extern crate xcrate_issue_46112_rexport_core; diff --git a/tests/ui/typeck/issue-46112.stderr b/tests/ui/typeck/issue-46112.stderr index 16beaea75db7f..9d439d10f1ed7 100644 --- a/tests/ui/typeck/issue-46112.stderr +++ b/tests/ui/typeck/issue-46112.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-46112.rs:9:21 + --> $DIR/issue-46112.rs:8:21 | LL | fn main() { test(Ok(())); } | -- ^^ expected `Option<()>`, found `()` @@ -9,7 +9,7 @@ LL | fn main() { test(Ok(())); } = note: expected enum `Option<()>` found unit type `()` help: the type constructed contains `()` due to the type of the argument passed - --> $DIR/issue-46112.rs:9:18 + --> $DIR/issue-46112.rs:8:18 | LL | fn main() { test(Ok(())); } | ^^^--^ diff --git a/tests/ui/typeck/issue-53712.stderr b/tests/ui/typeck/issue-53712.stderr index ffaf5cde1d71e..16a7ce0ea62f7 100644 --- a/tests/ui/typeck/issue-53712.stderr +++ b/tests/ui/typeck/issue-53712.stderr @@ -6,8 +6,9 @@ LL | arr.0; | help: instead of using tuple indexing, use array indexing | -LL | arr[0]; - | ~ + +LL - arr.0; +LL + arr[0]; + | error: aborting due to 1 previous error diff --git a/tests/ui/typeck/issue-79040.rs b/tests/ui/typeck/issue-79040.rs index 03e008207566e..f8e38e7867d52 100644 --- a/tests/ui/typeck/issue-79040.rs +++ b/tests/ui/typeck/issue-79040.rs @@ -1,6 +1,6 @@ fn main() { - const FOO = "hello" + 1; //~ ERROR cannot add `{integer}` to `&str` - //~^ missing type for `const` item - //~| ERROR cannot add `{integer}` to `&str` + const FOO = "hello" + 1; + //~^ ERROR cannot add `{integer}` to `&str` + //~| missing type for `const` item println!("{}", FOO); } diff --git a/tests/ui/typeck/issue-79040.stderr b/tests/ui/typeck/issue-79040.stderr index 39636db85a784..4ab8df8f6c936 100644 --- a/tests/ui/typeck/issue-79040.stderr +++ b/tests/ui/typeck/issue-79040.stderr @@ -17,16 +17,6 @@ help: provide a type for the item LL | const FOO: = "hello" + 1; | ++++++++ -error[E0369]: cannot add `{integer}` to `&str` - --> $DIR/issue-79040.rs:2:25 - | -LL | const FOO = "hello" + 1; - | ------- ^ - {integer} - | | - | &str - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0369`. diff --git a/tests/ui/typeck/issue-82772.stderr b/tests/ui/typeck/issue-82772.stderr index 321143cb9683d..a314306137a48 100644 --- a/tests/ui/typeck/issue-82772.stderr +++ b/tests/ui/typeck/issue-82772.stderr @@ -2,19 +2,19 @@ error[E0451]: field `0` of struct `Box` is private --> $DIR/issue-82772.rs:5:15 | LL | let Box { 0: _, .. }: Box<()>; - | ^^^^ private field + | ^ private field error[E0451]: field `1` of struct `Box` is private --> $DIR/issue-82772.rs:6:15 | LL | let Box { 1: _, .. }: Box<()>; - | ^^^^ private field + | ^ private field error[E0451]: field `1` of struct `ModPrivateStruct` is private --> $DIR/issue-82772.rs:7:28 | LL | let ModPrivateStruct { 1: _, .. } = ModPrivateStruct::default(); - | ^^^^ private field + | ^ private field error: aborting due to 3 previous errors diff --git a/tests/ui/typeck/issue-87872-missing-inaccessible-field-pattern.stderr b/tests/ui/typeck/issue-87872-missing-inaccessible-field-pattern.stderr index b9bdf6f9a3990..ed0e4eb9ece81 100644 --- a/tests/ui/typeck/issue-87872-missing-inaccessible-field-pattern.stderr +++ b/tests/ui/typeck/issue-87872-missing-inaccessible-field-pattern.stderr @@ -6,16 +6,19 @@ LL | let foo::Foo {} = foo::Foo::default(); | help: include the missing field in the pattern and ignore the inaccessible fields | -LL | let foo::Foo { visible, .. } = foo::Foo::default(); - | ~~~~~~~~~~~~~~~ +LL - let foo::Foo {} = foo::Foo::default(); +LL + let foo::Foo { visible, .. } = foo::Foo::default(); + | help: if you don't care about this missing field, you can explicitly ignore it | -LL | let foo::Foo { visible: _, .. } = foo::Foo::default(); - | ~~~~~~~~~~~~~~~~~~ +LL - let foo::Foo {} = foo::Foo::default(); +LL + let foo::Foo { visible: _, .. } = foo::Foo::default(); + | help: or always ignore missing fields here | -LL | let foo::Foo { .. } = foo::Foo::default(); - | ~~~~~~ +LL - let foo::Foo {} = foo::Foo::default(); +LL + let foo::Foo { .. } = foo::Foo::default(); + | error: aborting due to 1 previous error diff --git a/tests/ui/typeck/method-chain-gats.stderr b/tests/ui/typeck/method-chain-gats.stderr index 6338379221471..d3a54dbd0f9e2 100644 --- a/tests/ui/typeck/method-chain-gats.stderr +++ b/tests/ui/typeck/method-chain-gats.stderr @@ -20,7 +20,7 @@ LL | Self::Base: Functor; help: consider further restricting the associated type | LL | T::Base: Functor = T::Base>, ::Base: Functor - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | ++++++++++++++++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/typeck/mismatched-map-under-self.stderr b/tests/ui/typeck/mismatched-map-under-self.stderr index 59de00a58bbea..541de25749e38 100644 --- a/tests/ui/typeck/mismatched-map-under-self.stderr +++ b/tests/ui/typeck/mismatched-map-under-self.stderr @@ -14,7 +14,7 @@ LL | fn values(&self) -> Self::Values; help: change the self-receiver type to match the trait | LL | fn values(&self) -> Self::Values { - | ~~~~~ + | + error[E0631]: type mismatch in function arguments --> $DIR/mismatched-map-under-self.rs:12:18 diff --git a/tests/ui/typeck/path-to-method-sugg-unresolved-expr.cargo-invoked.stderr b/tests/ui/typeck/path-to-method-sugg-unresolved-expr.cargo-invoked.stderr new file mode 100644 index 0000000000000..8a3b87b0d11a5 --- /dev/null +++ b/tests/ui/typeck/path-to-method-sugg-unresolved-expr.cargo-invoked.stderr @@ -0,0 +1,11 @@ +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `page_size` + --> $DIR/path-to-method-sugg-unresolved-expr.rs:5:21 + | +LL | let page_size = page_size::get(); + | ^^^^^^^^^ use of unresolved module or unlinked crate `page_size` + | + = help: if you wanted to use a crate named `page_size`, use `cargo add page_size` to add it to your `Cargo.toml` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0433`. diff --git a/tests/ui/typeck/path-to-method-sugg-unresolved-expr.only-rustc.stderr b/tests/ui/typeck/path-to-method-sugg-unresolved-expr.only-rustc.stderr new file mode 100644 index 0000000000000..34ed5c44d9313 --- /dev/null +++ b/tests/ui/typeck/path-to-method-sugg-unresolved-expr.only-rustc.stderr @@ -0,0 +1,11 @@ +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `page_size` + --> $DIR/path-to-method-sugg-unresolved-expr.rs:5:21 + | +LL | let page_size = page_size::get(); + | ^^^^^^^^^ use of unresolved module or unlinked crate `page_size` + | + = help: you might be missing a crate named `page_size` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0433`. diff --git a/tests/ui/typeck/path-to-method-sugg-unresolved-expr.rs b/tests/ui/typeck/path-to-method-sugg-unresolved-expr.rs index fb56b394493dc..e095850879cde 100644 --- a/tests/ui/typeck/path-to-method-sugg-unresolved-expr.rs +++ b/tests/ui/typeck/path-to-method-sugg-unresolved-expr.rs @@ -1,4 +1,10 @@ +//@ revisions: only-rustc cargo-invoked +//@[only-rustc] unset-rustc-env:CARGO_CRATE_NAME +//@[cargo-invoked] rustc-env:CARGO_CRATE_NAME=foo fn main() { let page_size = page_size::get(); - //~^ ERROR failed to resolve: use of undeclared crate or module `page_size` + //~^ ERROR failed to resolve: use of unresolved module or unlinked crate `page_size` + //~| NOTE use of unresolved module or unlinked crate `page_size` + //[cargo-invoked]~^^^ HELP if you wanted to use a crate named `page_size`, use `cargo add + //[only-rustc]~^^^^ HELP you might be missing a crate named `page_size` } diff --git a/tests/ui/typeck/path-to-method-sugg-unresolved-expr.stderr b/tests/ui/typeck/path-to-method-sugg-unresolved-expr.stderr deleted file mode 100644 index 3e03c17f3b1c3..0000000000000 --- a/tests/ui/typeck/path-to-method-sugg-unresolved-expr.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0433]: failed to resolve: use of undeclared crate or module `page_size` - --> $DIR/path-to-method-sugg-unresolved-expr.rs:2:21 - | -LL | let page_size = page_size::get(); - | ^^^^^^^^^ use of undeclared crate or module `page_size` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0433`. diff --git a/tests/ui/typeck/ptr-null-mutability-suggestions.stderr b/tests/ui/typeck/ptr-null-mutability-suggestions.stderr index 2912977a461a3..aa1e79ac0d4a6 100644 --- a/tests/ui/typeck/ptr-null-mutability-suggestions.stderr +++ b/tests/ui/typeck/ptr-null-mutability-suggestions.stderr @@ -15,8 +15,9 @@ LL | fn expecting_null_mut(_: *mut u8) {} | ^^^^^^^^^^^^^^^^^^ ---------- help: consider using `core::ptr::null_mut` instead | -LL | expecting_null_mut(core::ptr::null_mut()); - | ~~~~~~~~~~~~~~~~~~~~~ +LL - expecting_null_mut(ptr::null()); +LL + expecting_null_mut(core::ptr::null_mut()); + | error: aborting due to 1 previous error diff --git a/tests/ui/typeck/remove-extra-argument.stderr b/tests/ui/typeck/remove-extra-argument.stderr index d4e0dcb50aed5..17e1b613cd9d3 100644 --- a/tests/ui/typeck/remove-extra-argument.stderr +++ b/tests/ui/typeck/remove-extra-argument.stderr @@ -8,7 +8,7 @@ note: function defined here --> $DIR/remove-extra-argument.rs:3:4 | LL | fn l(_a: Vec) {} - | ^ ----------- + | ^ help: remove the extra argument | LL - l(vec![], vec![]) diff --git a/tests/ui/typeck/remove-semi-but-confused-char.stderr b/tests/ui/typeck/remove-semi-but-confused-char.stderr index 2d0b53a60ce40..7f5c0758dd142 100644 --- a/tests/ui/typeck/remove-semi-but-confused-char.stderr +++ b/tests/ui/typeck/remove-semi-but-confused-char.stderr @@ -6,8 +6,9 @@ LL | num * num; | help: Unicode character ';' (Greek Question Mark) looks like ';' (Semicolon), but it is not | -LL | num * num; - | ~ +LL - num * num; +LL + num * num; + | error[E0308]: mismatched types --> $DIR/remove-semi-but-confused-char.rs:5:28 diff --git a/tests/ui/typeck/return_type_containing_closure.rs b/tests/ui/typeck/return_type_containing_closure.rs index b81cac0a58ab9..a9bb89bae2d6a 100644 --- a/tests/ui/typeck/return_type_containing_closure.rs +++ b/tests/ui/typeck/return_type_containing_closure.rs @@ -2,7 +2,7 @@ fn foo() { //~ HELP try adding a return type vec!['a'].iter().map(|c| c) //~^ ERROR mismatched types [E0308] - //~| NOTE expected `()`, found `Map, ...>` + //~| NOTE expected `()`, found `Map, {closure@...}>` //~| NOTE expected unit type `()` //~| HELP consider using a semicolon here } diff --git a/tests/ui/typeck/return_type_containing_closure.stderr b/tests/ui/typeck/return_type_containing_closure.stderr index 3f14650a82cce..a60bf79a57b8a 100644 --- a/tests/ui/typeck/return_type_containing_closure.stderr +++ b/tests/ui/typeck/return_type_containing_closure.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/return_type_containing_closure.rs:3:5 | LL | vec!['a'].iter().map(|c| c) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `Map, ...>` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `Map, {closure@...}>` | = note: expected unit type `()` found struct `Map, {closure@$DIR/return_type_containing_closure.rs:3:26: 3:29}>` diff --git a/tests/ui/typeck/struct-enum-wrong-args.stderr b/tests/ui/typeck/struct-enum-wrong-args.stderr index e58d162901e1c..d1003fbb6b313 100644 --- a/tests/ui/typeck/struct-enum-wrong-args.stderr +++ b/tests/ui/typeck/struct-enum-wrong-args.stderr @@ -38,8 +38,9 @@ note: tuple variant defined here --> $SRC_DIR/core/src/result.rs:LL:COL help: provide the argument | -LL | let _ = Ok(/* value */); - | ~~~~~~~~~~~~~ +LL - let _ = Ok(); +LL + let _ = Ok(/* value */); + | error[E0061]: this struct takes 1 argument but 0 arguments were supplied --> $DIR/struct-enum-wrong-args.rs:9:13 @@ -54,8 +55,9 @@ LL | struct Wrapper(i32); | ^^^^^^^ help: provide the argument | -LL | let _ = Wrapper(/* i32 */); - | ~~~~~~~~~~~ +LL - let _ = Wrapper(); +LL + let _ = Wrapper(/* i32 */); + | error[E0061]: this struct takes 1 argument but 2 arguments were supplied --> $DIR/struct-enum-wrong-args.rs:10:13 @@ -87,8 +89,9 @@ LL | struct DoubleWrapper(i32, i32); | ^^^^^^^^^^^^^ help: provide the arguments | -LL | let _ = DoubleWrapper(/* i32 */, /* i32 */); - | ~~~~~~~~~~~~~~~~~~~~~~ +LL - let _ = DoubleWrapper(); +LL + let _ = DoubleWrapper(/* i32 */, /* i32 */); + | error[E0061]: this struct takes 2 arguments but 1 argument was supplied --> $DIR/struct-enum-wrong-args.rs:12:13 @@ -103,8 +106,9 @@ LL | struct DoubleWrapper(i32, i32); | ^^^^^^^^^^^^^ help: provide the argument | -LL | let _ = DoubleWrapper(5, /* i32 */); - | ~~~~~~~~~~~~~~ +LL - let _ = DoubleWrapper(5); +LL + let _ = DoubleWrapper(5, /* i32 */); + | error[E0061]: this struct takes 2 arguments but 3 arguments were supplied --> $DIR/struct-enum-wrong-args.rs:13:13 diff --git a/tests/ui/typeck/suggest-arg-comma-delete-ice.stderr b/tests/ui/typeck/suggest-arg-comma-delete-ice.stderr index 53608391f3c89..0b899ad271232 100644 --- a/tests/ui/typeck/suggest-arg-comma-delete-ice.stderr +++ b/tests/ui/typeck/suggest-arg-comma-delete-ice.stderr @@ -6,8 +6,9 @@ LL | main(rahh); | help: Unicode character ')' (Fullwidth Right Parenthesis) looks like ')' (Right Parenthesis), but it is not | -LL | main(rahh); - | ~ +LL - main(rahh); +LL + main(rahh); + | error[E0425]: cannot find value `rahh` in this scope --> $DIR/suggest-arg-comma-delete-ice.rs:15:10 diff --git a/tests/ui/typeck/typeck_type_placeholder_item.rs b/tests/ui/typeck/typeck_type_placeholder_item.rs index 437a1aed40307..9f1bfd7909e38 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item.rs +++ b/tests/ui/typeck/typeck_type_placeholder_item.rs @@ -221,6 +221,7 @@ fn value() -> Option<&'static _> { const _: Option<_> = map(value); //~^ ERROR the placeholder `_` is not allowed within types on item signatures for constants +//~| ERROR cannot call non-const function `map::` in constants fn evens_squared(n: usize) -> _ { //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types diff --git a/tests/ui/typeck/typeck_type_placeholder_item.stderr b/tests/ui/typeck/typeck_type_placeholder_item.stderr index e62ebae5fd24b..c5bf9a47e9134 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item.stderr +++ b/tests/ui/typeck/typeck_type_placeholder_item.stderr @@ -67,25 +67,39 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/typeck_type_placeholder_item.rs:13:15 | LL | static TEST3: _ = "test"; - | ^ - | | - | not allowed in type signatures - | help: replace with the correct type: `&str` + | ^ not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - static TEST3: _ = "test"; +LL + static TEST3: &str = "test"; + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for static variables --> $DIR/typeck_type_placeholder_item.rs:16:15 | LL | static TEST4: _ = 145; - | ^ - | | - | not allowed in type signatures - | help: replace with the correct type: `i32` + | ^ not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - static TEST4: _ = 145; +LL + static TEST4: i32 = 145; + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for static variables - --> $DIR/typeck_type_placeholder_item.rs:19:15 + --> $DIR/typeck_type_placeholder_item.rs:19:16 | LL | static TEST5: (_, _) = (1, 2); - | ^^^^^^ not allowed in type signatures + | ^ ^ not allowed in type signatures + | | + | not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - static TEST5: (_, _) = (1, 2); +LL + static TEST5: (i32, i32) = (1, 2); + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/typeck_type_placeholder_item.rs:22:13 @@ -95,8 +109,9 @@ LL | fn test6(_: _) { } | help: use type parameters instead | -LL | fn test6(_: T) { } - | +++ ~ +LL - fn test6(_: _) { } +LL + fn test6(_: T) { } + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/typeck_type_placeholder_item.rs:25:18 @@ -106,8 +121,9 @@ LL | fn test6_b(_: _, _: T) { } | help: use type parameters instead | -LL | fn test6_b(_: U, _: T) { } - | +++ ~ +LL - fn test6_b(_: _, _: T) { } +LL + fn test6_b(_: U, _: T) { } + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/typeck_type_placeholder_item.rs:28:30 @@ -117,8 +133,9 @@ LL | fn test6_c(_: _, _: (T, K, L, A, B)) { } | help: use type parameters instead | -LL | fn test6_c(_: U, _: (T, K, L, A, B)) { } - | +++ ~ +LL - fn test6_c(_: _, _: (T, K, L, A, B)) { } +LL + fn test6_c(_: U, _: (T, K, L, A, B)) { } + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/typeck_type_placeholder_item.rs:31:13 @@ -128,8 +145,9 @@ LL | fn test7(x: _) { let _x: usize = x; } | help: use type parameters instead | -LL | fn test7(x: T) { let _x: usize = x; } - | +++ ~ +LL - fn test7(x: _) { let _x: usize = x; } +LL + fn test7(x: T) { let _x: usize = x; } + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/typeck_type_placeholder_item.rs:34:22 @@ -148,8 +166,9 @@ LL | fn test8(_f: fn() -> _) { } | help: use type parameters instead | -LL | fn test8(_f: fn() -> T) { } - | +++ ~ +LL - fn test8(_f: fn() -> _) { } +LL + fn test8(_f: fn() -> T) { } + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types --> $DIR/typeck_type_placeholder_item.rs:48:26 @@ -177,8 +196,9 @@ LL | fn clone(&self) -> _ { Test9 } | help: try replacing `_` with the type in the corresponding trait method signature | -LL | fn clone(&self) -> Test9 { Test9 } - | ~~~~~ +LL - fn clone(&self) -> _ { Test9 } +LL + fn clone(&self) -> Test9 { Test9 } + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/typeck_type_placeholder_item.rs:62:37 @@ -188,8 +208,9 @@ LL | fn clone_from(&mut self, other: _) { *self = Test9; } | help: try replacing `_` with the type in the corresponding trait method signature | -LL | fn clone_from(&mut self, other: &Test9) { *self = Test9; } - | ~~~~~~ +LL - fn clone_from(&mut self, other: _) { *self = Test9; } +LL + fn clone_from(&mut self, other: &Test9) { *self = Test9; } + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for structs --> $DIR/typeck_type_placeholder_item.rs:67:8 @@ -220,16 +241,25 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/typeck_type_placeholder_item.rs:75:15 | LL | static B: _ = 42; - | ^ - | | - | not allowed in type signatures - | help: replace with the correct type: `i32` + | ^ not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - static B: _ = 42; +LL + static B: i32 = 42; + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for static variables - --> $DIR/typeck_type_placeholder_item.rs:77:15 + --> $DIR/typeck_type_placeholder_item.rs:77:22 | LL | static C: Option<_> = Some(42); - | ^^^^^^^^^ not allowed in type signatures + | ^ not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - static C: Option<_> = Some(42); +LL + static C: Option = Some(42); + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types --> $DIR/typeck_type_placeholder_item.rs:79:21 @@ -254,25 +284,39 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/typeck_type_placeholder_item.rs:85:22 | LL | static FN_TEST3: _ = "test"; - | ^ - | | - | not allowed in type signatures - | help: replace with the correct type: `&str` + | ^ not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - static FN_TEST3: _ = "test"; +LL + static FN_TEST3: &str = "test"; + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for static variables --> $DIR/typeck_type_placeholder_item.rs:88:22 | LL | static FN_TEST4: _ = 145; - | ^ - | | - | not allowed in type signatures - | help: replace with the correct type: `i32` + | ^ not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - static FN_TEST4: _ = 145; +LL + static FN_TEST4: i32 = 145; + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for static variables - --> $DIR/typeck_type_placeholder_item.rs:91:22 + --> $DIR/typeck_type_placeholder_item.rs:91:23 | LL | static FN_TEST5: (_, _) = (1, 2); - | ^^^^^^ not allowed in type signatures + | ^ ^ not allowed in type signatures + | | + | not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - static FN_TEST5: (_, _) = (1, 2); +LL + static FN_TEST5: (i32, i32) = (1, 2); + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/typeck_type_placeholder_item.rs:94:20 @@ -282,8 +326,9 @@ LL | fn fn_test6(_: _) { } | help: use type parameters instead | -LL | fn fn_test6(_: T) { } - | +++ ~ +LL - fn fn_test6(_: _) { } +LL + fn fn_test6(_: T) { } + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/typeck_type_placeholder_item.rs:97:20 @@ -293,8 +338,9 @@ LL | fn fn_test7(x: _) { let _x: usize = x; } | help: use type parameters instead | -LL | fn fn_test7(x: T) { let _x: usize = x; } - | +++ ~ +LL - fn fn_test7(x: _) { let _x: usize = x; } +LL + fn fn_test7(x: T) { let _x: usize = x; } + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/typeck_type_placeholder_item.rs:100:29 @@ -313,8 +359,9 @@ LL | fn fn_test8(_f: fn() -> _) { } | help: use type parameters instead | -LL | fn fn_test8(_f: fn() -> T) { } - | +++ ~ +LL - fn fn_test8(_f: fn() -> _) { } +LL + fn fn_test8(_f: fn() -> T) { } + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/typeck_type_placeholder_item.rs:115:28 @@ -324,8 +371,9 @@ LL | fn clone(&self) -> _ { FnTest9 } | help: try replacing `_` with the type in the corresponding trait method signature | -LL | fn clone(&self) -> FnTest9 { FnTest9 } - | ~~~~~~~ +LL - fn clone(&self) -> _ { FnTest9 } +LL + fn clone(&self) -> FnTest9 { FnTest9 } + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/typeck_type_placeholder_item.rs:118:41 @@ -335,8 +383,9 @@ LL | fn clone_from(&mut self, other: _) { *self = FnTest9; } | help: try replacing `_` with the type in the corresponding trait method signature | -LL | fn clone_from(&mut self, other: &FnTest9) { *self = FnTest9; } - | ~~~~~~~~ +LL - fn clone_from(&mut self, other: _) { *self = FnTest9; } +LL + fn clone_from(&mut self, other: &FnTest9) { *self = FnTest9; } + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for structs --> $DIR/typeck_type_placeholder_item.rs:123:12 @@ -398,8 +447,9 @@ LL | fn method_test1(&self, x: _); | help: use type parameters instead | -LL | fn method_test1(&self, x: T); - | +++ ~ +LL - fn method_test1(&self, x: _); +LL + fn method_test1(&self, x: T); + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/typeck_type_placeholder_item.rs:142:31 @@ -411,8 +461,9 @@ LL | fn method_test2(&self, x: _) -> _; | help: use type parameters instead | -LL | fn method_test2(&self, x: T) -> T; - | +++ ~ ~ +LL - fn method_test2(&self, x: _) -> _; +LL + fn method_test2(&self, x: T) -> T; + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/typeck_type_placeholder_item.rs:144:31 @@ -422,8 +473,9 @@ LL | fn method_test3(&self) -> _; | help: use type parameters instead | -LL | fn method_test3(&self) -> T; - | +++ ~ +LL - fn method_test3(&self) -> _; +LL + fn method_test3(&self) -> T; + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/typeck_type_placeholder_item.rs:146:26 @@ -433,8 +485,9 @@ LL | fn assoc_fn_test1(x: _); | help: use type parameters instead | -LL | fn assoc_fn_test1(x: T); - | +++ ~ +LL - fn assoc_fn_test1(x: _); +LL + fn assoc_fn_test1(x: T); + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/typeck_type_placeholder_item.rs:148:26 @@ -446,8 +499,9 @@ LL | fn assoc_fn_test2(x: _) -> _; | help: use type parameters instead | -LL | fn assoc_fn_test2(x: T) -> T; - | +++ ~ ~ +LL - fn assoc_fn_test2(x: _) -> _; +LL + fn assoc_fn_test2(x: T) -> T; + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/typeck_type_placeholder_item.rs:150:28 @@ -457,8 +511,9 @@ LL | fn assoc_fn_test3() -> _; | help: use type parameters instead | -LL | fn assoc_fn_test3() -> T; - | +++ ~ +LL - fn assoc_fn_test3() -> _; +LL + fn assoc_fn_test3() -> T; + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for structs --> $DIR/typeck_type_placeholder_item.rs:154:21 @@ -468,8 +523,9 @@ LL | struct BadStruct<_>(_); | help: use type parameters instead | -LL | struct BadStruct(T); - | ~ ~ +LL - struct BadStruct<_>(_); +LL + struct BadStruct(T); + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for implementations --> $DIR/typeck_type_placeholder_item.rs:159:15 @@ -478,22 +534,12 @@ LL | impl BadTrait<_> for BadStruct<_> {} | ^ ^ not allowed in type signatures | | | not allowed in type signatures - | -help: use type parameters instead - | -LL | impl BadTrait for BadStruct {} - | +++ ~ ~ error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/typeck_type_placeholder_item.rs:162:34 | LL | fn impl_trait() -> impl BadTrait<_> { | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL | fn impl_trait() -> impl BadTrait { - | +++ ~ error[E0121]: the placeholder `_` is not allowed within types on item signatures for structs --> $DIR/typeck_type_placeholder_item.rs:167:25 @@ -503,8 +549,9 @@ LL | struct BadStruct1<_, _>(_); | help: use type parameters instead | -LL | struct BadStruct1(T); - | ~ ~ +LL - struct BadStruct1<_, _>(_); +LL + struct BadStruct1(T); + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for structs --> $DIR/typeck_type_placeholder_item.rs:172:25 @@ -514,8 +561,9 @@ LL | struct BadStruct2<_, T>(_, T); | help: use type parameters instead | -LL | struct BadStruct2(U, T); - | ~ ~ +LL - struct BadStruct2<_, T>(_, T); +LL + struct BadStruct2(U, T); + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for type aliases --> $DIR/typeck_type_placeholder_item.rs:176:14 @@ -539,10 +587,13 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/typeck_type_placeholder_item.rs:194:14 | LL | const D: _ = 42; - | ^ - | | - | not allowed in type signatures - | help: replace with the correct type: `i32` + | ^ not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - const D: _ = 42; +LL + const D: i32 = 42; + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated constants --> $DIR/typeck_type_placeholder_item.rs:209:14 @@ -569,16 +620,19 @@ LL | fn value() -> Option<&'static _> { | help: replace with the correct return type: `Option<&'static u8>` error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants - --> $DIR/typeck_type_placeholder_item.rs:222:10 + --> $DIR/typeck_type_placeholder_item.rs:222:17 | LL | const _: Option<_> = map(value); - | ^^^^^^^^^ - | | - | not allowed in type signatures - | help: replace with the correct type: `Option` + | ^ not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - const _: Option<_> = map(value); +LL + const _: Option = map(value); + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types - --> $DIR/typeck_type_placeholder_item.rs:225:31 + --> $DIR/typeck_type_placeholder_item.rs:226:31 | LL | fn evens_squared(n: usize) -> _ { | ^ @@ -587,13 +641,13 @@ LL | fn evens_squared(n: usize) -> _ { | help: replace with an appropriate return type: `impl Iterator` error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants - --> $DIR/typeck_type_placeholder_item.rs:230:10 + --> $DIR/typeck_type_placeholder_item.rs:231:10 | LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); | ^ not allowed in type signatures | -note: however, the inferred type `Map, {closure@typeck_type_placeholder_item.rs:230:29}>, {closure@typeck_type_placeholder_item.rs:230:49}>` cannot be named - --> $DIR/typeck_type_placeholder_item.rs:230:14 +note: however, the inferred type `Map, {closure@typeck_type_placeholder_item.rs:231:29}>, {closure@typeck_type_placeholder_item.rs:231:49}>` cannot be named + --> $DIR/typeck_type_placeholder_item.rs:231:14 | LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -615,8 +669,9 @@ LL | fn test10(&self, _x : _) { } | help: use type parameters instead | -LL | fn test10(&self, _x : T) { } - | +++ ~ +LL - fn test10(&self, _x : _) { } +LL + fn test10(&self, _x : T) { } + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types --> $DIR/typeck_type_placeholder_item.rs:107:31 @@ -635,8 +690,9 @@ LL | fn fn_test10(&self, _x : _) { } | help: use type parameters instead | -LL | fn fn_test10(&self, _x : T) { } - | +++ ~ +LL - fn fn_test10(&self, _x : _) { } +LL + fn fn_test10(&self, _x : T) { } + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated types --> $DIR/typeck_type_placeholder_item.rs:202:14 @@ -668,23 +724,31 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures LL | type F: std::ops::Fn(_); | ^ not allowed in type signatures -error[E0015]: cannot call non-const method ` as Iterator>::filter::<{closure@$DIR/typeck_type_placeholder_item.rs:230:29: 230:32}>` in constants - --> $DIR/typeck_type_placeholder_item.rs:230:22 +error[E0015]: cannot call non-const function `map::` in constants + --> $DIR/typeck_type_placeholder_item.rs:222:22 + | +LL | const _: Option<_> = map(value); + | ^^^^^^^^^^ + | + = note: calls in constants are limited to constant functions, tuple structs and tuple variants + +error[E0015]: cannot call non-const method ` as Iterator>::filter::<{closure@$DIR/typeck_type_placeholder_item.rs:231:29: 231:32}>` in constants + --> $DIR/typeck_type_placeholder_item.rs:231:22 | LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); | ^^^^^^^^^^^^^^^^^^^^^^ | = note: calls in constants are limited to constant functions, tuple structs and tuple variants -error[E0015]: cannot call non-const method `, {closure@$DIR/typeck_type_placeholder_item.rs:230:29: 230:32}> as Iterator>::map::` in constants - --> $DIR/typeck_type_placeholder_item.rs:230:45 +error[E0015]: cannot call non-const method `, {closure@$DIR/typeck_type_placeholder_item.rs:231:29: 231:32}> as Iterator>::map::` in constants + --> $DIR/typeck_type_placeholder_item.rs:231:45 | LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); | ^^^^^^^^^^^^^^ | = note: calls in constants are limited to constant functions, tuple structs and tuple variants -error: aborting due to 74 previous errors +error: aborting due to 75 previous errors Some errors have detailed explanations: E0015, E0046, E0121, E0282, E0403. For more information about an error, try `rustc --explain E0015`. diff --git a/tests/ui/typeck/typeck_type_placeholder_item_help.stderr b/tests/ui/typeck/typeck_type_placeholder_item_help.stderr index 32585e2937b77..afdd58e0a0384 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item_help.stderr +++ b/tests/ui/typeck/typeck_type_placeholder_item_help.stderr @@ -11,19 +11,25 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/typeck_type_placeholder_item_help.rs:7:14 | LL | const TEST2: _ = 42u32; - | ^ - | | - | not allowed in type signatures - | help: replace with the correct type: `u32` + | ^ not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - const TEST2: _ = 42u32; +LL + const TEST2: u32 = 42u32; + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants --> $DIR/typeck_type_placeholder_item_help.rs:10:14 | LL | const TEST3: _ = Some(42); - | ^ - | | - | not allowed in type signatures - | help: replace with the correct type: `Option` + | ^ not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - const TEST3: _ = Some(42); +LL + const TEST3: Option = Some(42); + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/typeck_type_placeholder_item_help.rs:13:22 @@ -41,19 +47,25 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/typeck_type_placeholder_item_help.rs:25:18 | LL | const TEST6: _ = 13; - | ^ - | | - | not allowed in type signatures - | help: replace with the correct type: `i32` + | ^ not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - const TEST6: _ = 13; +LL + const TEST6: i32 = 13; + | error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated constants --> $DIR/typeck_type_placeholder_item_help.rs:18:18 | LL | const TEST5: _ = 42; - | ^ - | | - | not allowed in type signatures - | help: replace with the correct type: `i32` + | ^ not allowed in type signatures + | +help: replace this with a fully-specified type + | +LL - const TEST5: _ = 42; +LL + const TEST5: i32 = 42; + | error[E0308]: mismatched types --> $DIR/typeck_type_placeholder_item_help.rs:30:28 diff --git a/tests/ui/issues/issue-41139.rs b/tests/ui/typeck/unsized-rvalue-issue-41139.rs similarity index 100% rename from tests/ui/issues/issue-41139.rs rename to tests/ui/typeck/unsized-rvalue-issue-41139.rs diff --git a/tests/ui/typeck/unsized-rvalue-issue-41139.stderr b/tests/ui/typeck/unsized-rvalue-issue-41139.stderr new file mode 100644 index 0000000000000..aba0423eeb3ec --- /dev/null +++ b/tests/ui/typeck/unsized-rvalue-issue-41139.stderr @@ -0,0 +1,12 @@ +error[E0618]: expected function, found `&dyn Fn() -> (dyn Trait + 'static)` + --> $DIR/unsized-rvalue-issue-41139.rs:10:26 + | +LL | fn get_function<'a>() -> &'a dyn Fn() -> dyn Trait { + | -------------------------------------------------- `get_function` defined here returns `&dyn Fn() -> (dyn Trait + 'static)` +... +LL | let t: &dyn Trait = &get_function()(); + | ^^^^^^^^^^^^^^ this trait object returns an unsized value `(dyn Trait + 'static)`, so it cannot be called + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0618`. diff --git a/tests/ui/typeof/issue-100183.stderr b/tests/ui/typeof/issue-100183.stderr index 57317d449cf3a..765a5c54428f8 100644 --- a/tests/ui/typeof/issue-100183.stderr +++ b/tests/ui/typeof/issue-100183.stderr @@ -6,8 +6,9 @@ LL | y: (typeof("hey"),), | help: consider replacing `typeof(...)` with an actual type | -LL | y: (&str,), - | ~~~~ +LL - y: (typeof("hey"),), +LL + y: (&str,), + | error: aborting due to 1 previous error diff --git a/tests/ui/typeof/issue-29184.stderr b/tests/ui/typeof/issue-29184.stderr index f07c850e55669..d8d43504d7224 100644 --- a/tests/ui/typeof/issue-29184.stderr +++ b/tests/ui/typeof/issue-29184.stderr @@ -6,8 +6,9 @@ LL | let x: typeof(92) = 92; | help: consider replacing `typeof(...)` with an actual type | -LL | let x: i32 = 92; - | ~~~ +LL - let x: typeof(92) = 92; +LL + let x: i32 = 92; + | error: aborting due to 1 previous error diff --git a/tests/ui/typeof/issue-42060.stderr b/tests/ui/typeof/issue-42060.stderr index 86ba9432384b2..733ad37693bec 100644 --- a/tests/ui/typeof/issue-42060.stderr +++ b/tests/ui/typeof/issue-42060.stderr @@ -6,8 +6,9 @@ LL | let other: typeof(thing) = thing; | help: consider using `const` instead of `let` | -LL | const thing: /* Type */ = (); - | ~~~~~ ++++++++++++ +LL - let thing = (); +LL + const thing: /* Type */ = (); + | error[E0435]: attempt to use a non-constant value in a constant --> $DIR/issue-42060.rs:9:13 @@ -17,8 +18,9 @@ LL | ::N | help: consider using `const` instead of `let` | -LL | const q: /* Type */ = 1; - | ~~~~~ ++++++++++++ +LL - let q = 1; +LL + const q: /* Type */ = 1; + | error[E0516]: `typeof` is a reserved keyword but unimplemented --> $DIR/issue-42060.rs:3:16 diff --git a/tests/ui/typeof/type_mismatch.stderr b/tests/ui/typeof/type_mismatch.stderr index e75214cd31a6d..d5494922b1661 100644 --- a/tests/ui/typeof/type_mismatch.stderr +++ b/tests/ui/typeof/type_mismatch.stderr @@ -6,8 +6,9 @@ LL | let b: typeof(a) = 1i8; | help: consider replacing `typeof(...)` with an actual type | -LL | let b: u8 = 1i8; - | ~~ +LL - let b: typeof(a) = 1i8; +LL + let b: u8 = 1i8; + | error[E0308]: mismatched types --> $DIR/type_mismatch.rs:5:24 @@ -19,8 +20,9 @@ LL | let b: typeof(a) = 1i8; | help: change the type of the numeric literal from `i8` to `u8` | -LL | let b: typeof(a) = 1u8; - | ~~ +LL - let b: typeof(a) = 1i8; +LL + let b: typeof(a) = 1u8; + | error: aborting due to 2 previous errors diff --git a/tests/ui/ufcs/bad-builder.stderr b/tests/ui/ufcs/bad-builder.stderr index e466f94d0d842..a3528cb1e7d80 100644 --- a/tests/ui/ufcs/bad-builder.stderr +++ b/tests/ui/ufcs/bad-builder.stderr @@ -13,8 +13,9 @@ note: if you're trying to build a new `Vec` consider using one of the followi --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL help: there is an associated function `new` with a similar name | -LL | Vec::::new() - | ~~~ +LL - Vec::::mew() +LL + Vec::::new() + | error: aborting due to 1 previous error diff --git a/tests/ui/ufcs/ufcs-explicit-self-bad.stderr b/tests/ui/ufcs/ufcs-explicit-self-bad.stderr index 2a8c4edbdb5f3..7b5f7cd9f7033 100644 --- a/tests/ui/ufcs/ufcs-explicit-self-bad.stderr +++ b/tests/ui/ufcs/ufcs-explicit-self-bad.stderr @@ -13,8 +13,9 @@ LL | fn dummy2(&self); found signature `fn(&Bar<_>)` help: change the self-receiver type to match the trait | -LL | fn dummy2(&self) {} - | ~~~~~ +LL - fn dummy2(self: &Bar) {} +LL + fn dummy2(&self) {} + | error[E0307]: invalid `self` parameter type: `isize` --> $DIR/ufcs-explicit-self-bad.rs:8:18 diff --git a/tests/ui/ufcs/ufcs-partially-resolved.stderr b/tests/ui/ufcs/ufcs-partially-resolved.stderr index eef55c8dc686f..0a9c190cb3561 100644 --- a/tests/ui/ufcs/ufcs-partially-resolved.stderr +++ b/tests/ui/ufcs/ufcs-partially-resolved.stderr @@ -24,7 +24,8 @@ LL | let _: ::N; | help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias | -LL | trait A = u32; +LL - type A = u32; +LL + trait A = u32; | error[E0576]: cannot find method or associated constant `N` in trait `Tr` @@ -53,7 +54,8 @@ LL | ::N; | help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias | -LL | trait A = u32; +LL - type A = u32; +LL + trait A = u32; | error[E0404]: expected trait, found enum `E` @@ -100,7 +102,8 @@ LL | let _: ::N::NN; | help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias | -LL | trait A = u32; +LL - type A = u32; +LL + trait A = u32; | error[E0576]: cannot find associated type `N` in trait `Tr` @@ -129,7 +132,8 @@ LL | ::N::NN; | help: you might have meant to use `#![feature(trait_alias)]` instead of a `type` alias | -LL | trait A = u32; +LL - type A = u32; +LL + trait A = u32; | error[E0404]: expected trait, found enum `E` @@ -253,8 +257,9 @@ LL | let _: ::Y::NN; | help: if there were a trait named `Example` with associated type `NN` implemented for `::Y`, you could use the fully-qualified path | -LL | let _: <::Y as Example>::NN; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - let _: ::Y::NN; +LL + let _: <::Y as Example>::NN; + | error[E0599]: no associated item named `NN` found for type `u16` in the current scope --> $DIR/ufcs-partially-resolved.rs:38:20 diff --git a/tests/ui/ufcs/ufcs-qpath-self-mismatch.stderr b/tests/ui/ufcs/ufcs-qpath-self-mismatch.stderr index f8be11a24e3da..0507183488391 100644 --- a/tests/ui/ufcs/ufcs-qpath-self-mismatch.stderr +++ b/tests/ui/ufcs/ufcs-qpath-self-mismatch.stderr @@ -30,8 +30,9 @@ note: method defined here --> $SRC_DIR/core/src/ops/arith.rs:LL:COL help: change the type of the numeric literal from `u32` to `i32` | -LL | >::add(1i32, 2); - | ~~~ +LL - >::add(1u32, 2); +LL + >::add(1i32, 2); + | error[E0308]: mismatched types --> $DIR/ufcs-qpath-self-mismatch.rs:9:31 @@ -52,8 +53,9 @@ note: method defined here --> $SRC_DIR/core/src/ops/arith.rs:LL:COL help: change the type of the numeric literal from `u32` to `i32` | -LL | >::add(1, 2i32); - | ~~~ +LL - >::add(1, 2u32); +LL + >::add(1, 2i32); + | error[E0277]: cannot add `u32` to `i32` --> $DIR/ufcs-qpath-self-mismatch.rs:4:5 diff --git a/tests/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct-3.stderr b/tests/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct-3.stderr index ce2c90f97da6a..1c6470eef8f7d 100644 --- a/tests/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct-3.stderr +++ b/tests/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct-3.stderr @@ -6,8 +6,9 @@ LL | let b = Bar::(isize, usize)::new(); // OK too (for the parser) | help: use angle brackets instead | -LL | let b = Bar::::new(); // OK too (for the parser) - | ~ ~ +LL - let b = Bar::(isize, usize)::new(); // OK too (for the parser) +LL + let b = Bar::::new(); // OK too (for the parser) + | error: aborting due to 1 previous error diff --git a/tests/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.stderr b/tests/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.stderr index 40d3414024553..e2f48f37f0dad 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.stderr @@ -7,6 +7,10 @@ LL | doit(0, &|x, y| { | has type `&Cell<&'2 i32>` LL | x.set(y); | ^^^^^^^^ argument requires that `'1` must outlive `'2` + | + = note: requirement occurs because of the type `Cell<&i32>`, which makes the generic argument `&i32` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance error: aborting due to 1 previous error diff --git a/tests/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr b/tests/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr index 327df50e645d5..d87588d182256 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr @@ -13,8 +13,9 @@ LL | let mut f = |x: isize, y: isize| -> isize { x + y }; | ^^^^^^^^ help: change the type of the numeric literal from `usize` to `isize` | -LL | let z = f(1_isize, 2); - | ~~~~~ +LL - let z = f(1_usize, 2); +LL + let z = f(1_isize, 2); + | error[E0308]: mismatched types --> $DIR/unboxed-closures-type-mismatch.rs:9:15 @@ -38,8 +39,9 @@ LL | let mut g = |x, y| { x + y }; | ^ help: change the type of the numeric literal from `usize` to `i32` | -LL | let z = g(1_i32, 2); - | ~~~ +LL - let z = g(1_usize, 2); +LL + let z = g(1_i32, 2); + | error[E0308]: mismatched types --> $DIR/unboxed-closures-type-mismatch.rs:17:18 @@ -63,8 +65,9 @@ LL | let identity = |x| x; | ^ help: change the type of the numeric literal from `u16` to `u8` | -LL | identity(1u8); - | ~~ +LL - identity(1u16); +LL + identity(1u8); + | error[E0308]: mismatched types --> $DIR/unboxed-closures-type-mismatch.rs:20:18 @@ -111,8 +114,9 @@ LL | let identity = |x| x; | ^ help: change the type of the numeric literal from `u16` to `u8` | -LL | identity(1u8); - | ~~ +LL - identity(1u16); +LL + identity(1u8); + | error[E0308]: mismatched types --> $DIR/unboxed-closures-type-mismatch.rs:33:18 diff --git a/tests/ui/underscore-lifetime/in-fn-return-illegal.stderr b/tests/ui/underscore-lifetime/in-fn-return-illegal.stderr index fb036c695b41c..a53e8adbecf93 100644 --- a/tests/ui/underscore-lifetime/in-fn-return-illegal.stderr +++ b/tests/ui/underscore-lifetime/in-fn-return-illegal.stderr @@ -7,8 +7,9 @@ LL | fn foo(x: &u32, y: &u32) -> &'_ u32 { loop { } } = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y` help: consider introducing a named lifetime parameter | -LL | fn foo<'a>(x: &'a u32, y: &'a u32) -> &'a u32 { loop { } } - | ++++ ++ ++ ~~ +LL - fn foo(x: &u32, y: &u32) -> &'_ u32 { loop { } } +LL + fn foo<'a>(x: &'a u32, y: &'a u32) -> &'a u32 { loop { } } + | error: aborting due to 1 previous error diff --git a/tests/ui/underscore-lifetime/underscore-lifetime-binders.stderr b/tests/ui/underscore-lifetime/underscore-lifetime-binders.stderr index cd74d27dcb55f..d940166e9e28b 100644 --- a/tests/ui/underscore-lifetime/underscore-lifetime-binders.stderr +++ b/tests/ui/underscore-lifetime/underscore-lifetime-binders.stderr @@ -6,8 +6,9 @@ LL | struct Baz<'a>(&'_ &'a u8); | help: consider using the `'a` lifetime | -LL | struct Baz<'a>(&'a &'a u8); - | ~~ +LL - struct Baz<'a>(&'_ &'a u8); +LL + struct Baz<'a>(&'a &'a u8); + | error[E0637]: `'_` cannot be used here --> $DIR/underscore-lifetime-binders.rs:4:8 @@ -30,8 +31,9 @@ LL | fn meh() -> Box Meh<'_>> = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values | -LL | fn meh() -> Box Meh<'static>> - | ~~~~~~~ +LL - fn meh() -> Box Meh<'_>> +LL + fn meh() -> Box Meh<'static>> + | error[E0106]: missing lifetime specifier --> $DIR/underscore-lifetime-binders.rs:16:35 @@ -42,8 +44,9 @@ LL | fn foo2(_: &'_ u8, y: &'_ u8) -> &'_ u8 { y } = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from argument 1 or `y` help: consider introducing a named lifetime parameter | -LL | fn foo2<'a>(_: &'a u8, y: &'a u8) -> &'a u8 { y } - | ++++ ~~ ~~ ~~ +LL - fn foo2(_: &'_ u8, y: &'_ u8) -> &'_ u8 { y } +LL + fn foo2<'a>(_: &'a u8, y: &'a u8) -> &'a u8 { y } + | error: aborting due to 5 previous errors diff --git a/tests/ui/underscore-lifetime/underscore-lifetime-elison-mismatch.stderr b/tests/ui/underscore-lifetime/underscore-lifetime-elison-mismatch.stderr index ed9d22d255839..4b530ca4ba659 100644 --- a/tests/ui/underscore-lifetime/underscore-lifetime-elison-mismatch.stderr +++ b/tests/ui/underscore-lifetime/underscore-lifetime-elison-mismatch.stderr @@ -7,10 +7,14 @@ LL | fn foo(x: &mut Vec<&'_ u8>, y: &'_ u8) { x.push(y); } | | let's call the lifetime of this reference `'1` | let's call the lifetime of this reference `'2` | + = note: requirement occurs because of a mutable reference to `Vec<&u8>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance help: consider introducing a named lifetime parameter | -LL | fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); } - | ++++ ~~ ~~ +LL - fn foo(x: &mut Vec<&'_ u8>, y: &'_ u8) { x.push(y); } +LL + fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); } + | error: aborting due to 1 previous error diff --git a/tests/ui/uninhabited/uninhabited-irrefutable.min_exhaustive_patterns.stderr b/tests/ui/uninhabited/uninhabited-irrefutable.min_exhaustive_patterns.stderr deleted file mode 100644 index 67527ce1ac452..0000000000000 --- a/tests/ui/uninhabited/uninhabited-irrefutable.min_exhaustive_patterns.stderr +++ /dev/null @@ -1,26 +0,0 @@ -error[E0005]: refutable pattern in local binding - --> $DIR/uninhabited-irrefutable.rs:31:9 - | -LL | let Foo::D(_y, _z) = x; - | ^^^^^^^^^^^^^^ pattern `Foo::A(_)` not covered - | - = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant - = note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html -note: `Foo` defined here - --> $DIR/uninhabited-irrefutable.rs:20:6 - | -LL | enum Foo { - | ^^^ -LL | -LL | A(foo::SecretlyEmpty), - | - not covered - = note: pattern `Foo::A(_)` is currently uninhabited, but this variant contains private fields which may become inhabited in the future - = note: the matched value is of type `Foo` -help: you might want to use `let else` to handle the variant that isn't matched - | -LL | let Foo::D(_y, _z) = x else { todo!() }; - | ++++++++++++++++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0005`. diff --git a/tests/ui/union/union-derive-eq.current.stderr b/tests/ui/union/union-derive-eq.current.stderr new file mode 100644 index 0000000000000..151ceebe1ba67 --- /dev/null +++ b/tests/ui/union/union-derive-eq.current.stderr @@ -0,0 +1,21 @@ +error[E0277]: the trait bound `PartialEqNotEq: Eq` is not satisfied + --> $DIR/union-derive-eq.rs:21:5 + | +LL | #[derive(Eq)] + | -- in this derive macro expansion +LL | union U2 { +LL | a: PartialEqNotEq, + | ^^^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `PartialEqNotEq` + | +note: required by a bound in `AssertParamIsEq` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider annotating `PartialEqNotEq` with `#[derive(Eq)]` + | +LL + #[derive(Eq)] +LL | struct PartialEqNotEq; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/union/union-derive-eq.next.stderr b/tests/ui/union/union-derive-eq.next.stderr new file mode 100644 index 0000000000000..151ceebe1ba67 --- /dev/null +++ b/tests/ui/union/union-derive-eq.next.stderr @@ -0,0 +1,21 @@ +error[E0277]: the trait bound `PartialEqNotEq: Eq` is not satisfied + --> $DIR/union-derive-eq.rs:21:5 + | +LL | #[derive(Eq)] + | -- in this derive macro expansion +LL | union U2 { +LL | a: PartialEqNotEq, + | ^^^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `PartialEqNotEq` + | +note: required by a bound in `AssertParamIsEq` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider annotating `PartialEqNotEq` with `#[derive(Eq)]` + | +LL + #[derive(Eq)] +LL | struct PartialEqNotEq; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/union/union-derive-eq.rs b/tests/ui/union/union-derive-eq.rs index e689f8c27d772..085262a72a18f 100644 --- a/tests/ui/union/union-derive-eq.rs +++ b/tests/ui/union/union-derive-eq.rs @@ -1,9 +1,17 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) + #[derive(Eq)] // OK union U1 { a: u8, } -impl PartialEq for U1 { fn eq(&self, rhs: &Self) -> bool { true } } +impl PartialEq for U1 { + fn eq(&self, rhs: &Self) -> bool { + true + } +} #[derive(PartialEq, Copy, Clone)] struct PartialEqNotEq; @@ -13,6 +21,10 @@ union U2 { a: PartialEqNotEq, //~ ERROR the trait bound `PartialEqNotEq: Eq` is not satisfied } -impl PartialEq for U2 { fn eq(&self, rhs: &Self) -> bool { true } } +impl PartialEq for U2 { + fn eq(&self, rhs: &Self) -> bool { + true + } +} fn main() {} diff --git a/tests/ui/union/union-derive-eq.stderr b/tests/ui/union/union-derive-eq.stderr deleted file mode 100644 index b068edd6d69cc..0000000000000 --- a/tests/ui/union/union-derive-eq.stderr +++ /dev/null @@ -1,21 +0,0 @@ -error[E0277]: the trait bound `PartialEqNotEq: Eq` is not satisfied - --> $DIR/union-derive-eq.rs:13:5 - | -LL | #[derive(Eq)] - | -- in this derive macro expansion -LL | union U2 { -LL | a: PartialEqNotEq, - | ^^^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `PartialEqNotEq` - | -note: required by a bound in `AssertParamIsEq` - --> $SRC_DIR/core/src/cmp.rs:LL:COL - = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) -help: consider annotating `PartialEqNotEq` with `#[derive(Eq)]` - | -LL + #[derive(Eq)] -LL | struct PartialEqNotEq; - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/union/union-suggest-field.stderr b/tests/ui/union/union-suggest-field.stderr index 5c428cf6c8993..fc6daca2a4795 100644 --- a/tests/ui/union/union-suggest-field.stderr +++ b/tests/ui/union/union-suggest-field.stderr @@ -6,8 +6,9 @@ LL | let u = U { principle: 0 }; | help: a field with a similar name exists | -LL | let u = U { principal: 0 }; - | ~~~~~~~~~ +LL - let u = U { principle: 0 }; +LL + let u = U { principal: 0 }; + | error[E0609]: no field `principial` on type `U` --> $DIR/union-suggest-field.rs:14:15 @@ -17,8 +18,9 @@ LL | let w = u.principial; | help: a field with a similar name exists | -LL | let w = u.principal; - | ~~~~~~~~~ +LL - let w = u.principial; +LL + let w = u.principal; + | error[E0615]: attempted to take value of method `calculate` on type `U` --> $DIR/union-suggest-field.rs:18:15 diff --git a/tests/ui/unpretty/box.rs b/tests/ui/unpretty/box.rs index 8972eccf3b8fd..83fdeff7a1796 100644 --- a/tests/ui/unpretty/box.rs +++ b/tests/ui/unpretty/box.rs @@ -1,9 +1,8 @@ -//@ compile-flags: -Zunpretty=hir +//@ compile-flags: -Zunpretty=thir-tree //@ check-pass -#![feature(stmt_expr_attributes, rustc_attrs)] +#![feature(liballoc_internals)] fn main() { - let _ = #[rustc_box] - Box::new(1); + let _ = std::boxed::box_new(1); } diff --git a/tests/ui/unpretty/box.stdout b/tests/ui/unpretty/box.stdout index e3b9b9ac20715..92155d0c73b9d 100644 --- a/tests/ui/unpretty/box.stdout +++ b/tests/ui/unpretty/box.stdout @@ -1,14 +1,90 @@ -//@ compile-flags: -Zunpretty=hir -//@ check-pass +DefId(0:3 ~ box[efb9]::main): +params: [ +] +body: + Expr { + ty: () + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(11)), backwards_incompatible: None } + span: $DIR/box.rs:6:11: 8:2 (#0) + kind: + Scope { + region_scope: Node(11) + lint_level: Explicit(HirId(DefId(0:3 ~ box[efb9]::main).11)) + value: + Expr { + ty: () + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(11)), backwards_incompatible: None } + span: $DIR/box.rs:6:11: 8:2 (#0) + kind: + Block { + targeted_by_break: false + span: $DIR/box.rs:6:11: 8:2 (#0) + region_scope: Node(1) + safety_mode: Safe + stmts: [ + Stmt { + kind: Let { + remainder_scope: Remainder { block: 1, first_statement_index: 0} + init_scope: Node(2) + pattern: + Pat: { + ty: std::boxed::Box + span: $DIR/box.rs:7:9: 7:10 (#0) + kind: PatKind { + Wild + } + } + , + initializer: Some( + Expr { + ty: std::boxed::Box + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(2)), backwards_incompatible: None } + span: $DIR/box.rs:7:13: 7:35 (#0) + kind: + Scope { + region_scope: Node(3) + lint_level: Explicit(HirId(DefId(0:3 ~ box[efb9]::main).3)) + value: + Expr { + ty: std::boxed::Box + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(2)), backwards_incompatible: None } + span: $DIR/box.rs:7:13: 7:35 (#0) + kind: + Box { + Expr { + ty: i32 + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(2)), backwards_incompatible: None } + span: $DIR/box.rs:7:33: 7:34 (#0) + kind: + Scope { + region_scope: Node(8) + lint_level: Explicit(HirId(DefId(0:3 ~ box[efb9]::main).8)) + value: + Expr { + ty: i32 + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(2)), backwards_incompatible: None } + span: $DIR/box.rs:7:33: 7:34 (#0) + kind: + Literal( lit: Spanned { node: Int(Pu128(1), Unsuffixed), span: $DIR/box.rs:7:33: 7:34 (#0) }, neg: false) + + } + } + } + } + } + } + } + ) + else_block: None + lint_level: Explicit(HirId(DefId(0:3 ~ box[efb9]::main).9)) + span: $DIR/box.rs:7:5: 7:35 (#0) + } + } + ] + expr: [] + } + } + } + } -#![feature(stmt_expr_attributes, rustc_attrs)] -#[prelude_import] -use ::std::prelude::rust_2015::*; -#[macro_use] -extern crate std; -fn main() { - let _ = - #[rustc_box] - Box::new(1); -} diff --git a/tests/ui/unpretty/expanded-exhaustive.rs b/tests/ui/unpretty/expanded-exhaustive.rs index e052627e71cfe..31af323ecdab5 100644 --- a/tests/ui/unpretty/expanded-exhaustive.rs +++ b/tests/ui/unpretty/expanded-exhaustive.rs @@ -452,15 +452,15 @@ mod items { /// ItemKind::Fn mod item_fn { pub const unsafe extern "C" fn f() {} - pub async unsafe extern fn g() {} + pub async unsafe extern "C" fn g() {} fn h<'a, T>() where T: 'a {} trait TraitItems { - unsafe extern fn f(); + unsafe extern "C" fn f(); } impl TraitItems for _ { - default unsafe extern fn f() {} + default unsafe extern "C" fn f() {} } } @@ -472,7 +472,7 @@ mod items { /// ItemKind::ForeignMod mod item_foreign_mod { unsafe extern "C++" {} - unsafe extern {} + unsafe extern "C" {} } /// ItemKind::GlobalAsm @@ -651,8 +651,8 @@ mod patterns { let &mut pat; } - /// PatKind::Lit - fn pat_lit() { + /// PatKind::Expr + fn pat_expr() { let 1_000_i8; let -""; } diff --git a/tests/ui/unpretty/expanded-exhaustive.stdout b/tests/ui/unpretty/expanded-exhaustive.stdout index 132d00cd8edd5..19ae66f7a07aa 100644 --- a/tests/ui/unpretty/expanded-exhaustive.stdout +++ b/tests/ui/unpretty/expanded-exhaustive.stdout @@ -433,13 +433,13 @@ mod items { /// ItemKind::Fn mod item_fn { pub const unsafe extern "C" fn f() {} - pub async unsafe extern fn g() {} + pub async unsafe extern "C" fn g() {} fn h<'a, T>() where T: 'a {} trait TraitItems { - unsafe extern fn f(); + unsafe extern "C" fn f(); } impl TraitItems for _ { - default unsafe extern fn f() {} + default unsafe extern "C" fn f() {} } } /// ItemKind::Mod @@ -447,7 +447,7 @@ mod items { /// ItemKind::ForeignMod mod item_foreign_mod { unsafe extern "C++" {} - unsafe extern {} + unsafe extern "C" {} } /// ItemKind::GlobalAsm mod item_global_asm { @@ -567,8 +567,8 @@ mod patterns { fn pat_deref() { let deref!(pat); } /// PatKind::Ref fn pat_ref() { let &pat; let &mut pat; } - /// PatKind::Lit - fn pat_lit() { let 1_000_i8; let -""; } + /// PatKind::Expr + fn pat_expr() { let 1_000_i8; let -""; } /// PatKind::Range fn pat_range() { let ..1; let 0..; let 0..1; let 0..=1; let -2..=-1; } /// PatKind::Slice @@ -678,7 +678,7 @@ mod types { /*! FIXME: todo */ } /// TyKind::Pat - fn ty_pat() { let _: u32 is 1..; } + fn ty_pat() { let _: u32 is const 1..; } } mod visibilities { /// VisibilityKind::Public diff --git a/tests/ui/unpretty/staged-api-invalid-path-108697.stderr b/tests/ui/unpretty/staged-api-invalid-path-108697.stderr index 9c6d1a042d755..e68e19c4dc99e 100644 --- a/tests/ui/unpretty/staged-api-invalid-path-108697.stderr +++ b/tests/ui/unpretty/staged-api-invalid-path-108697.stderr @@ -1,4 +1,4 @@ -error: couldn't read $DIR/lol: No such file or directory (os error 2) +error: couldn't read `$DIR/lol`: No such file or directory (os error 2) --> $DIR/staged-api-invalid-path-108697.rs:8:1 | LL | mod foo; diff --git a/tests/ui/unpretty/unpretty-expr-fn-arg.stdout b/tests/ui/unpretty/unpretty-expr-fn-arg.stdout index c424b1afa3465..43aa93c83bd31 100644 --- a/tests/ui/unpretty/unpretty-expr-fn-arg.stdout +++ b/tests/ui/unpretty/unpretty-expr-fn-arg.stdout @@ -14,4 +14,4 @@ extern crate std; fn main() ({ } as ()) -fn foo((-(128 as i8) as i8)...(127 as i8): i8) ({ } as ()) +fn foo(-128...127: i8) ({ } as ()) diff --git a/tests/ui/unresolved/unresolved-asterisk-imports.stderr b/tests/ui/unresolved/unresolved-asterisk-imports.stderr index ed01f3fdbea41..e84f1975112b0 100644 --- a/tests/ui/unresolved/unresolved-asterisk-imports.stderr +++ b/tests/ui/unresolved/unresolved-asterisk-imports.stderr @@ -2,9 +2,9 @@ error[E0432]: unresolved import `not_existing_crate` --> $DIR/unresolved-asterisk-imports.rs:1:5 | LL | use not_existing_crate::*; - | ^^^^^^^^^^^^^^^^^^ you might be missing crate `not_existing_crate` + | ^^^^^^^^^^^^^^^^^^ use of unresolved module or unlinked crate `not_existing_crate` | -help: consider importing the `not_existing_crate` crate +help: you might be missing a crate named `not_existing_crate`, add it to your project and import it in your code | LL + extern crate not_existing_crate; | diff --git a/tests/ui/unresolved/unresolved-candidates.stderr b/tests/ui/unresolved/unresolved-candidates.stderr index 7ef2f6b1a2922..7be1bcd38de59 100644 --- a/tests/ui/unresolved/unresolved-candidates.stderr +++ b/tests/ui/unresolved/unresolved-candidates.stderr @@ -7,7 +7,7 @@ LL | use Trait; help: consider importing this trait instead | LL | use a::Trait; - | ~~~~~~~~ + | +++ error[E0405]: cannot find trait `Trait` in this scope --> $DIR/unresolved-candidates.rs:10:10 diff --git a/tests/ui/unresolved/unresolved-import-avoid-suggesting-global-path.stderr b/tests/ui/unresolved/unresolved-import-avoid-suggesting-global-path.stderr index b0352ab675435..f96cf69a2afc9 100644 --- a/tests/ui/unresolved/unresolved-import-avoid-suggesting-global-path.stderr +++ b/tests/ui/unresolved/unresolved-import-avoid-suggesting-global-path.stderr @@ -6,8 +6,9 @@ LL | use module::SomeUsefulType; | help: consider importing this struct instead | -LL | use library::SomeUsefulType; - | ~~~~~~~~~~~~~~~~~~~~~~~ +LL - use module::SomeUsefulType; +LL + use library::SomeUsefulType; + | error[E0432]: unresolved import `module::SomeUsefulType` --> $DIR/unresolved-import-avoid-suggesting-global-path.rs:28:9 @@ -17,8 +18,9 @@ LL | use module::SomeUsefulType; | help: consider importing this struct instead | -LL | use library::SomeUsefulType; - | ~~~~~~~~~~~~~~~~~~~~~~~ +LL - use module::SomeUsefulType; +LL + use library::SomeUsefulType; + | error: aborting due to 2 previous errors diff --git a/tests/ui/unresolved/unresolved-import-suggest-disambiguated-crate-name.stderr b/tests/ui/unresolved/unresolved-import-suggest-disambiguated-crate-name.stderr index c6812dbb19639..31b943defbaa9 100644 --- a/tests/ui/unresolved/unresolved-import-suggest-disambiguated-crate-name.stderr +++ b/tests/ui/unresolved/unresolved-import-suggest-disambiguated-crate-name.stderr @@ -6,8 +6,9 @@ LL | pub use module::SomeUsefulType; | help: consider importing this struct instead | -LL | pub use ::library::SomeUsefulType; - | ~~~~~~~~~~~~~~~~~~~~~~~~~ +LL - pub use module::SomeUsefulType; +LL + pub use ::library::SomeUsefulType; + | error: aborting due to 1 previous error diff --git a/tests/ui/unresolved/unresolved-import.rs b/tests/ui/unresolved/unresolved-import.rs index ee520d65e6f4a..763e9496734dd 100644 --- a/tests/ui/unresolved/unresolved-import.rs +++ b/tests/ui/unresolved/unresolved-import.rs @@ -1,7 +1,7 @@ use foo::bar; //~^ ERROR unresolved import `foo` [E0432] -//~| NOTE you might be missing crate `foo` -//~| HELP consider importing the `foo` crate +//~| NOTE use of unresolved module or unlinked crate `foo` +//~| HELP you might be missing a crate named `foo` //~| SUGGESTION extern crate foo; use bar::Baz as x; diff --git a/tests/ui/unresolved/unresolved-import.stderr b/tests/ui/unresolved/unresolved-import.stderr index a1ff2f19eb640..c65fe841001d7 100644 --- a/tests/ui/unresolved/unresolved-import.stderr +++ b/tests/ui/unresolved/unresolved-import.stderr @@ -2,9 +2,9 @@ error[E0432]: unresolved import `foo` --> $DIR/unresolved-import.rs:1:5 | LL | use foo::bar; - | ^^^ you might be missing crate `foo` + | ^^^ use of unresolved module or unlinked crate `foo` | -help: consider importing the `foo` crate +help: you might be missing a crate named `foo`, add it to your project and import it in your code | LL + extern crate foo; | diff --git a/tests/ui/unsafe-binders/expr.rs b/tests/ui/unsafe-binders/expr.rs index 0fe68751f0ae8..d437d8f8ac07e 100644 --- a/tests/ui/unsafe-binders/expr.rs +++ b/tests/ui/unsafe-binders/expr.rs @@ -1,3 +1,5 @@ +//@ check-pass + #![feature(unsafe_binders)] //~^ WARN the feature `unsafe_binders` is incomplete @@ -7,8 +9,6 @@ fn main() { unsafe { let x = 1; let binder: unsafe<'a> &'a i32 = wrap_binder!(&x); - //~^ ERROR unsafe binder casts are not fully implemented let rx = *unwrap_binder!(binder); - //~^ ERROR unsafe binder casts are not fully implemented } } diff --git a/tests/ui/unsafe-binders/expr.stderr b/tests/ui/unsafe-binders/expr.stderr index 78a288e10a3c0..07026e18e1252 100644 --- a/tests/ui/unsafe-binders/expr.stderr +++ b/tests/ui/unsafe-binders/expr.stderr @@ -1,5 +1,5 @@ warning: the feature `unsafe_binders` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/expr.rs:1:12 + --> $DIR/expr.rs:3:12 | LL | #![feature(unsafe_binders)] | ^^^^^^^^^^^^^^ @@ -7,17 +7,5 @@ LL | #![feature(unsafe_binders)] = note: see issue #130516 for more information = note: `#[warn(incomplete_features)]` on by default -error: unsafe binder casts are not fully implemented - --> $DIR/expr.rs:9:55 - | -LL | let binder: unsafe<'a> &'a i32 = wrap_binder!(&x); - | ^^ - -error: unsafe binder casts are not fully implemented - --> $DIR/expr.rs:11:34 - | -LL | let rx = *unwrap_binder!(binder); - | ^^^^^^ - -error: aborting due to 2 previous errors; 1 warning emitted +warning: 1 warning emitted diff --git a/tests/ui/unsafe-binders/mismatch.rs b/tests/ui/unsafe-binders/mismatch.rs index 731fe2d1ce9b9..840d938cbe98c 100644 --- a/tests/ui/unsafe-binders/mismatch.rs +++ b/tests/ui/unsafe-binders/mismatch.rs @@ -5,38 +5,31 @@ use std::unsafe_binder::{wrap_binder, unwrap_binder}; fn a() { let _: unsafe<'a> &'a i32 = wrap_binder!(&()); - //~^ ERROR unsafe binder casts are not fully implemented - //~| ERROR mismatched types + //~^ ERROR mismatched types } fn b() { let _: i32 = wrap_binder!(&()); - //~^ ERROR unsafe binder casts are not fully implemented - //~| ERROR `wrap_binder!()` can only wrap into unsafe binder + //~^ ERROR `wrap_binder!()` can only wrap into unsafe binder } fn c() { let y = 1; unwrap_binder!(y); - //~^ ERROR unsafe binder casts are not fully implemented - //~| ERROR expected unsafe binder, found integer as input + //~^ ERROR expected unsafe binder, found integer as input } fn d() { let unknown = Default::default(); + //~^ ERROR type annotations needed unwrap_binder!(unknown); - //~^ ERROR unsafe binder casts are not fully implemented - // FIXME(unsafe_binders): This should report ambiguity once we've removed - // the error above which taints the infcx. } fn e() { let x = wrap_binder!(&42); - //~^ ERROR unsafe binder casts are not fully implemented + //~^ ERROR type annotations needed // Currently, type inference doesn't flow backwards for unsafe binders. // It could, perhaps, but that may cause even more surprising corners. - // FIXME(unsafe_binders): This should report ambiguity once we've removed - // the error above which taints the infcx. let _: unsafe<'a> &'a i32 = x; } diff --git a/tests/ui/unsafe-binders/mismatch.stderr b/tests/ui/unsafe-binders/mismatch.stderr index a720e5dbdc1fe..f64db92eb655a 100644 --- a/tests/ui/unsafe-binders/mismatch.stderr +++ b/tests/ui/unsafe-binders/mismatch.stderr @@ -7,12 +7,6 @@ LL | #![feature(unsafe_binders)] = note: see issue #130516 for more information = note: `#[warn(incomplete_features)]` on by default -error: unsafe binder casts are not fully implemented - --> $DIR/mismatch.rs:7:46 - | -LL | let _: unsafe<'a> &'a i32 = wrap_binder!(&()); - | ^^^ - error[E0308]: mismatched types --> $DIR/mismatch.rs:7:46 | @@ -22,14 +16,8 @@ LL | let _: unsafe<'a> &'a i32 = wrap_binder!(&()); = note: expected reference `&i32` found reference `&()` -error: unsafe binder casts are not fully implemented - --> $DIR/mismatch.rs:13:31 - | -LL | let _: i32 = wrap_binder!(&()); - | ^^^ - error: `wrap_binder!()` can only wrap into unsafe binder, not `i32` - --> $DIR/mismatch.rs:13:18 + --> $DIR/mismatch.rs:12:18 | LL | let _: i32 = wrap_binder!(&()); | ^^^^^^^^^^^^^^^^^ @@ -37,32 +25,35 @@ LL | let _: i32 = wrap_binder!(&()); = note: unsafe binders are the only valid output of wrap = note: this error originates in the macro `wrap_binder` (in Nightly builds, run with -Z macro-backtrace for more info) -error: unsafe binder casts are not fully implemented - --> $DIR/mismatch.rs:20:20 - | -LL | unwrap_binder!(y); - | ^ - error: expected unsafe binder, found integer as input of `unwrap_binder!()` - --> $DIR/mismatch.rs:20:20 + --> $DIR/mismatch.rs:18:20 | LL | unwrap_binder!(y); | ^ | = note: only an unsafe binder type can be unwrapped -error: unsafe binder casts are not fully implemented - --> $DIR/mismatch.rs:27:20 +error[E0282]: type annotations needed + --> $DIR/mismatch.rs:23:9 | +LL | let unknown = Default::default(); + | ^^^^^^^ +LL | LL | unwrap_binder!(unknown); - | ^^^^^^^ + | ------- type must be known at this point + | +help: consider giving `unknown` an explicit type + | +LL | let unknown: /* Type */ = Default::default(); + | ++++++++++++ -error: unsafe binder casts are not fully implemented - --> $DIR/mismatch.rs:34:26 +error[E0282]: type annotations needed + --> $DIR/mismatch.rs:29:26 | LL | let x = wrap_binder!(&42); - | ^^^ + | ^^^ cannot infer type -error: aborting due to 8 previous errors; 1 warning emitted +error: aborting due to 5 previous errors; 1 warning emitted -For more information about this error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0282, E0308. +For more information about an error, try `rustc --explain E0282`. diff --git a/tests/ui/unsafe-binders/moves.rs b/tests/ui/unsafe-binders/moves.rs new file mode 100644 index 0000000000000..9397c2bc20f97 --- /dev/null +++ b/tests/ui/unsafe-binders/moves.rs @@ -0,0 +1,41 @@ +#![feature(unsafe_binders)] +//~^ WARN the feature `unsafe_binders` is incomplete + +use std::mem::{ManuallyDrop, drop}; +use std::unsafe_binder::{unwrap_binder, wrap_binder}; + +#[derive(Default)] +struct NotCopyInner; +type NotCopy = ManuallyDrop; + +fn use_after_wrap() { + unsafe { + let base = NotCopy::default(); + let binder: unsafe<> NotCopy = wrap_binder!(base); + drop(base); + //~^ ERROR use of moved value: `base` + } +} + +fn move_out_of_wrap() { + unsafe { + let binder: unsafe<> NotCopy = wrap_binder!(NotCopy::default()); + drop(unwrap_binder!(binder)); + drop(unwrap_binder!(binder)); + //~^ ERROR use of moved value: `binder` + } +} + +fn not_conflicting() { + unsafe { + let binder: unsafe<> (NotCopy, NotCopy) = + wrap_binder!((NotCopy::default(), NotCopy::default())); + drop(unwrap_binder!(binder).0); + drop(unwrap_binder!(binder).1); + // ^ NOT a problem, since the moves are disjoint. + drop(unwrap_binder!(binder).0); + //~^ ERROR use of moved value: `binder.0` + } +} + +fn main() {} diff --git a/tests/ui/unsafe-binders/moves.stderr b/tests/ui/unsafe-binders/moves.stderr new file mode 100644 index 0000000000000..0f976d9e845a3 --- /dev/null +++ b/tests/ui/unsafe-binders/moves.stderr @@ -0,0 +1,44 @@ +warning: the feature `unsafe_binders` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/moves.rs:1:12 + | +LL | #![feature(unsafe_binders)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #130516 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0382]: use of moved value: `base` + --> $DIR/moves.rs:15:14 + | +LL | let base = NotCopy::default(); + | ---- move occurs because `base` has type `ManuallyDrop`, which does not implement the `Copy` trait +LL | let binder: unsafe<> NotCopy = wrap_binder!(base); + | ---- value moved here +LL | drop(base); + | ^^^^ value used here after move + +error[E0382]: use of moved value: `binder` + --> $DIR/moves.rs:24:14 + | +LL | drop(unwrap_binder!(binder)); + | ---------------------- value moved here +LL | drop(unwrap_binder!(binder)); + | ^^^^^^^^^^^^^^^^^^^^^^ value used here after move + | + = note: move occurs because `binder` has type `ManuallyDrop`, which does not implement the `Copy` trait + = note: this error originates in the macro `unwrap_binder` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0382]: use of moved value: `binder.0` + --> $DIR/moves.rs:36:14 + | +LL | drop(unwrap_binder!(binder).0); + | ------------------------ value moved here +... +LL | drop(unwrap_binder!(binder).0); + | ^^^^^^^^^^^^^^^^^^^^^^^^ value used here after move + | + = note: move occurs because `binder.0` has type `ManuallyDrop`, which does not implement the `Copy` trait + +error: aborting due to 3 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/unsigned-literal-negation.stderr b/tests/ui/unsigned-literal-negation.stderr index b0a730477a1b0..0bedbc1accd3b 100644 --- a/tests/ui/unsigned-literal-negation.stderr +++ b/tests/ui/unsigned-literal-negation.stderr @@ -7,8 +7,9 @@ LL | let x = -1 as usize; = note: unsigned values cannot be negated help: you may have meant the maximum value of `usize` | -LL | let x = usize::MAX; - | ~~~~~~~~~~ +LL - let x = -1 as usize; +LL + let x = usize::MAX; + | error[E0600]: cannot apply unary operator `-` to type `usize` --> $DIR/unsigned-literal-negation.rs:3:13 @@ -19,8 +20,9 @@ LL | let x = (-1) as usize; = note: unsigned values cannot be negated help: you may have meant the maximum value of `usize` | -LL | let x = usize::MAX; - | ~~~~~~~~~~ +LL - let x = (-1) as usize; +LL + let x = usize::MAX; + | error[E0600]: cannot apply unary operator `-` to type `u32` --> $DIR/unsigned-literal-negation.rs:4:18 @@ -31,8 +33,9 @@ LL | let x: u32 = -1; = note: unsigned values cannot be negated help: you may have meant the maximum value of `u32` | -LL | let x: u32 = u32::MAX; - | ~~~~~~~~ +LL - let x: u32 = -1; +LL + let x: u32 = u32::MAX; + | error: aborting due to 3 previous errors diff --git a/tests/ui/unsized/box-instead-of-dyn-fn.stderr b/tests/ui/unsized/box-instead-of-dyn-fn.stderr index 1f1845569ef17..ed17ff69a1237 100644 --- a/tests/ui/unsized/box-instead-of-dyn-fn.stderr +++ b/tests/ui/unsized/box-instead-of-dyn-fn.stderr @@ -6,8 +6,9 @@ LL | fn print_on_or_the_other<'a>(a: i32, b: &'a String) -> dyn Fn() + 'a { | help: consider returning an `impl Trait` instead of a `dyn Trait` | -LL | fn print_on_or_the_other<'a>(a: i32, b: &'a String) -> impl Fn() + 'a { - | ~~~~ +LL - fn print_on_or_the_other<'a>(a: i32, b: &'a String) -> dyn Fn() + 'a { +LL + fn print_on_or_the_other<'a>(a: i32, b: &'a String) -> impl Fn() + 'a { + | help: alternatively, box the return type, and wrap all of the returned values in `Box::new` | LL ~ fn print_on_or_the_other<'a>(a: i32, b: &'a String) -> Box { diff --git a/tests/ui/unsized/issue-91803.stderr b/tests/ui/unsized/issue-91803.stderr index 3b89066499dfe..812fac3585c8a 100644 --- a/tests/ui/unsized/issue-91803.stderr +++ b/tests/ui/unsized/issue-91803.stderr @@ -6,8 +6,9 @@ LL | fn or<'a>(first: &'static dyn Foo<'a>) -> dyn Foo<'a> { | help: consider returning an `impl Trait` instead of a `dyn Trait` | -LL | fn or<'a>(first: &'static dyn Foo<'a>) -> impl Foo<'a> { - | ~~~~ +LL - fn or<'a>(first: &'static dyn Foo<'a>) -> dyn Foo<'a> { +LL + fn or<'a>(first: &'static dyn Foo<'a>) -> impl Foo<'a> { + | help: alternatively, box the return type, and wrap all of the returned values in `Box::new` | LL | fn or<'a>(first: &'static dyn Foo<'a>) -> Box> { diff --git a/tests/ui/unsized/maybe-bounds-where.rs b/tests/ui/unsized/maybe-bounds-where.rs index 7e82a3eb449cb..4c4141631a7c5 100644 --- a/tests/ui/unsized/maybe-bounds-where.rs +++ b/tests/ui/unsized/maybe-bounds-where.rs @@ -11,11 +11,11 @@ trait Trait<'a> {} struct S4(T) where for<'a> T: ?Trait<'a>; //~^ ERROR `?Trait` bounds are only permitted at the point where a type parameter is declared -//~| WARN relaxing a default bound only does something for `?Sized` +//~| ERROR relaxing a default bound only does something for `?Sized` struct S5(*const T) where T: ?Trait<'static> + ?Sized; //~^ ERROR type parameter has more than one relaxed default bound -//~| WARN relaxing a default bound only does something for `?Sized` +//~| ERROR relaxing a default bound only does something for `?Sized` impl S1 { fn f() where T: ?Sized {} diff --git a/tests/ui/unsized/maybe-bounds-where.stderr b/tests/ui/unsized/maybe-bounds-where.stderr index f785126166781..fb6d37c29660a 100644 --- a/tests/ui/unsized/maybe-bounds-where.stderr +++ b/tests/ui/unsized/maybe-bounds-where.stderr @@ -43,7 +43,7 @@ LL | fn f() where T: ?Sized {} = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +error: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default --> $DIR/maybe-bounds-where.rs:12:34 | LL | struct S4(T) where for<'a> T: ?Trait<'a>; @@ -58,13 +58,13 @@ LL | struct S5(*const T) where T: ?Trait<'static> + ?Sized; = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +error: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default --> $DIR/maybe-bounds-where.rs:16:33 | LL | struct S5(*const T) where T: ?Trait<'static> + ?Sized; | ^^^^^^^^^^^^^^^ -error: aborting due to 6 previous errors; 2 warnings emitted +error: aborting due to 8 previous errors Some errors have detailed explanations: E0203, E0658. For more information about an error, try `rustc --explain E0203`. diff --git a/tests/ui/use/import_trait_associated_functions-2015.rs b/tests/ui/use/import_trait_associated_functions-2015.rs new file mode 100644 index 0000000000000..3177aeefb097c --- /dev/null +++ b/tests/ui/use/import_trait_associated_functions-2015.rs @@ -0,0 +1,61 @@ +//@ edition:2015 +//@ check-pass +#![feature(import_trait_associated_functions)] + +use std::collections::HashMap; + +use A::{DEFAULT, new}; +use std::default::Default::default; + +struct S { + a: HashMap, +} + +impl S { + fn new() -> S { + S { a: default() } + } +} + +trait A: Sized { + const DEFAULT: Option = None; + fn new() -> Self; + fn do_something(&self); +} + +mod b { + use super::A::{self, DEFAULT, new}; + + struct B(); + + impl A for B { + const DEFAULT: Option = Some(B()); + fn new() -> Self { + B() + } + + fn do_something(&self) {} + } + + fn f() { + let b: B = new(); + b.do_something(); + let c: B = DEFAULT.unwrap(); + } +} + +impl A for S { + fn new() -> Self { + S::new() + } + + fn do_something(&self) {} +} + +fn f() { + let s: S = new(); + s.do_something(); + let t: Option = DEFAULT; +} + +fn main() {} diff --git a/tests/ui/use/import_trait_associated_functions.rs b/tests/ui/use/import_trait_associated_functions.rs new file mode 100644 index 0000000000000..4dc473404dbd1 --- /dev/null +++ b/tests/ui/use/import_trait_associated_functions.rs @@ -0,0 +1,61 @@ +//@ edition:2018 +//@ check-pass +#![feature(import_trait_associated_functions)] + +use std::collections::HashMap; + +use A::{DEFAULT, new}; +use Default::default; + +struct S { + a: HashMap, +} + +impl S { + fn new() -> S { + S { a: default() } + } +} + +trait A: Sized { + const DEFAULT: Option = None; + fn new() -> Self; + fn do_something(&self); +} + +mod b { + use super::A::{self, DEFAULT, new}; + + struct B(); + + impl A for B { + const DEFAULT: Option = Some(B()); + fn new() -> Self { + B() + } + + fn do_something(&self) {} + } + + fn f() { + let b: B = new(); + b.do_something(); + let c: B = DEFAULT.unwrap(); + } +} + +impl A for S { + fn new() -> Self { + S::new() + } + + fn do_something(&self) {} +} + +fn f() { + let s: S = new(); + s.do_something(); + let t: Option = DEFAULT; +} + +fn main() {} diff --git a/tests/ui/use/use-from-trait-xc.rs b/tests/ui/use/use-from-trait-xc.rs index b7b9c834b327d..b030892aa2695 100644 --- a/tests/ui/use/use-from-trait-xc.rs +++ b/tests/ui/use/use-from-trait-xc.rs @@ -3,13 +3,13 @@ extern crate use_from_trait_xc; use use_from_trait_xc::Trait::foo; -//~^ ERROR `foo` is not directly importable +//~^ ERROR `use` associated items of traits is unstable [E0658] use use_from_trait_xc::Trait::Assoc; //~^ ERROR `Assoc` is not directly importable use use_from_trait_xc::Trait::CONST; -//~^ ERROR `CONST` is not directly importable +//~^ ERROR `use` associated items of traits is unstable [E0658] use use_from_trait_xc::Foo::new; //~ ERROR struct `Foo` is private //~^ ERROR unresolved import `use_from_trait_xc::Foo` diff --git a/tests/ui/use/use-from-trait-xc.stderr b/tests/ui/use/use-from-trait-xc.stderr index 4c4c2f6225f1a..0f8440aa53079 100644 --- a/tests/ui/use/use-from-trait-xc.stderr +++ b/tests/ui/use/use-from-trait-xc.stderr @@ -1,8 +1,12 @@ -error[E0253]: `foo` is not directly importable +error[E0658]: `use` associated items of traits is unstable --> $DIR/use-from-trait-xc.rs:5:5 | LL | use use_from_trait_xc::Trait::foo; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be imported directly + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #134691 for more information + = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0253]: `Assoc` is not directly importable --> $DIR/use-from-trait-xc.rs:8:5 @@ -10,11 +14,15 @@ error[E0253]: `Assoc` is not directly importable LL | use use_from_trait_xc::Trait::Assoc; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be imported directly -error[E0253]: `CONST` is not directly importable +error[E0658]: `use` associated items of traits is unstable --> $DIR/use-from-trait-xc.rs:11:5 | LL | use use_from_trait_xc::Trait::CONST; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be imported directly + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #134691 for more information + = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0432]: unresolved import `use_from_trait_xc::Foo` --> $DIR/use-from-trait-xc.rs:14:24 @@ -66,5 +74,5 @@ LL | struct Foo; error: aborting due to 9 previous errors -Some errors have detailed explanations: E0253, E0432, E0603. +Some errors have detailed explanations: E0253, E0432, E0603, E0658. For more information about an error, try `rustc --explain E0253`. diff --git a/tests/ui/use/use-from-trait.rs b/tests/ui/use/use-from-trait.rs index eab4bb6e3b5be..89b7aaa4ba38e 100644 --- a/tests/ui/use/use-from-trait.rs +++ b/tests/ui/use/use-from-trait.rs @@ -1,6 +1,6 @@ -use Trait::foo; //~ ERROR `foo` is not directly importable +use Trait::foo; //~ ERROR `use` associated items of traits is unstable [E0658] use Trait::Assoc; //~ ERROR `Assoc` is not directly importable -use Trait::C; //~ ERROR `C` is not directly importable +use Trait::C; //~ ERROR `use` associated items of traits is unstable [E0658] use Foo::new; //~ ERROR unresolved import `Foo` [E0432] diff --git a/tests/ui/use/use-from-trait.stderr b/tests/ui/use/use-from-trait.stderr index a5b0e356b34c5..2dd78a3545298 100644 --- a/tests/ui/use/use-from-trait.stderr +++ b/tests/ui/use/use-from-trait.stderr @@ -1,8 +1,12 @@ -error[E0253]: `foo` is not directly importable +error[E0658]: `use` associated items of traits is unstable --> $DIR/use-from-trait.rs:1:5 | LL | use Trait::foo; - | ^^^^^^^^^^ cannot be imported directly + | ^^^^^^^^^^ + | + = note: see issue #134691 for more information + = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0253]: `Assoc` is not directly importable --> $DIR/use-from-trait.rs:2:5 @@ -10,11 +14,15 @@ error[E0253]: `Assoc` is not directly importable LL | use Trait::Assoc; | ^^^^^^^^^^^^ cannot be imported directly -error[E0253]: `C` is not directly importable +error[E0658]: `use` associated items of traits is unstable --> $DIR/use-from-trait.rs:3:5 | LL | use Trait::C; - | ^^^^^^^^ cannot be imported directly + | ^^^^^^^^ + | + = note: see issue #134691 for more information + = help: add `#![feature(import_trait_associated_functions)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0432]: unresolved import `Foo` --> $DIR/use-from-trait.rs:5:5 @@ -30,5 +38,5 @@ LL | use Foo::C2; error: aborting due to 5 previous errors -Some errors have detailed explanations: E0253, E0432. +Some errors have detailed explanations: E0253, E0432, E0658. For more information about an error, try `rustc --explain E0253`. diff --git a/tests/ui/use/use.rs b/tests/ui/use/use.rs index db031500a4ac8..25b8e529c432a 100644 --- a/tests/ui/use/use.rs +++ b/tests/ui/use/use.rs @@ -3,7 +3,7 @@ #![allow(stable_features)] #![allow(unused_imports)] -#![feature(start, no_core, core)] +#![feature(no_core, core)] #![no_core] extern crate std; @@ -18,5 +18,4 @@ mod baz { pub use std::str as x; } -#[start] -pub fn start(_: isize, _: *const *const u8) -> isize { 0 } +fn main() {} diff --git a/tests/ui/variance/variance-associated-types2.stderr b/tests/ui/variance/variance-associated-types2.stderr index 158b09b0630ca..292d60941b185 100644 --- a/tests/ui/variance/variance-associated-types2.stderr +++ b/tests/ui/variance/variance-associated-types2.stderr @@ -1,10 +1,10 @@ error: lifetime may not live long enough - --> $DIR/variance-associated-types2.rs:13:12 + --> $DIR/variance-associated-types2.rs:13:42 | LL | fn take<'a>(_: &'a u32) { | -- lifetime `'a` defined here LL | let _: Box> = make(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + | ^^^^^^ coercion requires that `'a` must outlive `'static` error: aborting due to 1 previous error diff --git a/tests/ui/variance/variance-uniquerc.rs b/tests/ui/variance/variance-uniquerc.rs new file mode 100644 index 0000000000000..0c395ab06eaa3 --- /dev/null +++ b/tests/ui/variance/variance-uniquerc.rs @@ -0,0 +1,27 @@ +// regression test of https://github.com/rust-lang/rust/pull/133572#issuecomment-2543007164 +// we should also test UniqueArc once implemented +// +// inline comments explain how this code *would* compile if UniqueRc was still covariant + +#![feature(unique_rc_arc)] + +use std::rc::UniqueRc; + +fn extend_lifetime<'a, 'b>(x: &'a str) -> &'b str { + let r = UniqueRc::new(""); // UniqueRc<&'static str> + let w = UniqueRc::downgrade(&r); // Weak<&'static str> + let mut r = r; // [IF COVARIANT]: ==>> UniqueRc<&'a str> + *r = x; // assign the &'a str + let _r = UniqueRc::into_rc(r); // Rc<&'a str>, but we only care to activate the weak + let r = w.upgrade().unwrap(); // Rc<&'static str> + *r // &'static str, coerces to &'b str + //~^ ERROR lifetime may not live long enough +} + +fn main() { + let s = String::from("Hello World!"); + let r = extend_lifetime(&s); + println!("{r}"); + drop(s); + println!("{r}"); +} diff --git a/tests/ui/variance/variance-uniquerc.stderr b/tests/ui/variance/variance-uniquerc.stderr new file mode 100644 index 0000000000000..1557f7e40c587 --- /dev/null +++ b/tests/ui/variance/variance-uniquerc.stderr @@ -0,0 +1,15 @@ +error: lifetime may not live long enough + --> $DIR/variance-uniquerc.rs:17:5 + | +LL | fn extend_lifetime<'a, 'b>(x: &'a str) -> &'b str { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | *r // &'static str, coerces to &'b str + | ^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + +error: aborting due to 1 previous error + diff --git a/tests/ui/variants/variant-used-as-type.stderr b/tests/ui/variants/variant-used-as-type.stderr index 64424abbcecce..1857c10a8e9ba 100644 --- a/tests/ui/variants/variant-used-as-type.stderr +++ b/tests/ui/variants/variant-used-as-type.stderr @@ -6,10 +6,12 @@ LL | B(Ty::A), | help: try using the variant's enum | -LL | B(E), - | ~ -LL | B(Ty), - | ~~ +LL - B(Ty::A), +LL + B(E), + | +LL - B(Ty::A), +LL + B(Ty), + | error[E0573]: expected type, found variant `E::A` --> $DIR/variant-used-as-type.rs:17:6 @@ -19,10 +21,12 @@ LL | impl E::A {} | help: try using the variant's enum | -LL | impl E {} - | ~ -LL | impl Ty {} - | ~~ +LL - impl E::A {} +LL + impl E {} + | +LL - impl E::A {} +LL + impl Ty {} + | error: aborting due to 2 previous errors diff --git a/tests/ui/virtual-call-attrs-issue-137646.rs b/tests/ui/virtual-call-attrs-issue-137646.rs new file mode 100644 index 0000000000000..e80bd5768a429 --- /dev/null +++ b/tests/ui/virtual-call-attrs-issue-137646.rs @@ -0,0 +1,45 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/137646. +//! The parameter value at all calls to `check` should be `(1, 1, 1)`. + +//@ run-pass + +use std::hint::black_box; + +type T = (i32, i32, i32); + +pub trait Trait { + fn m(&self, _: T, _: T) {} +} + +impl Trait for () { + fn m(&self, mut _v1: T, v2: T) { + _v1 = (0, 0, 0); + check(v2); + } +} + +pub fn run_1(trait_: &dyn Trait) { + let v1 = (1, 1, 1); + let v2 = (1, 1, 1); + trait_.m(v1, v2); +} + +pub fn run_2(trait_: &dyn Trait) { + let v1 = (1, 1, 1); + let v2 = (1, 1, 1); + trait_.m(v1, v2); + check(v1); + check(v2); +} + +#[inline(never)] +fn check(v: T) { + assert_eq!(v, (1, 1, 1)); +} + +fn main() { + black_box(run_1 as fn(&dyn Trait)); + black_box(run_2 as fn(&dyn Trait)); + run_1(&()); + run_2(&()); +} diff --git a/tests/ui/wait-forked-but-failed-child.rs b/tests/ui/wait-forked-but-failed-child.rs index dd6a7fa0e6505..4a7f2bee9d954 100644 --- a/tests/ui/wait-forked-but-failed-child.rs +++ b/tests/ui/wait-forked-but-failed-child.rs @@ -1,6 +1,5 @@ //@ run-pass -//@ ignore-wasm32 no processes -//@ ignore-sgx no processes +//@ needs-subprocess //@ ignore-vxworks no 'ps' //@ ignore-fuchsia no 'ps' //@ ignore-nto no 'ps' @@ -32,8 +31,17 @@ fn find_zombies() { // https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html let ps_cmd_output = Command::new("ps").args(&["-A", "-o", "pid,ppid,args"]).output().unwrap(); let ps_output = String::from_utf8_lossy(&ps_cmd_output.stdout); + // On AIX, the PPID is not always present, such as when a process is blocked + // (marked as ), or if a process is idle. In these situations, + // the PPID column contains a "-" for the respective process. + // Filter out any lines that have a "-" as the PPID as the PPID is + // expected to be an integer. + let filtered_ps: Vec<_> = ps_output + .lines() + .filter(|line| line.split_whitespace().nth(1) != Some("-")) + .collect(); - for (line_no, line) in ps_output.split('\n').enumerate() { + for (line_no, line) in filtered_ps.into_iter().enumerate() { if 0 < line_no && 0 < line.len() && my_pid == line.split(' ').filter(|w| 0 < w.len()).nth(1) .expect("1st column should be PPID") diff --git a/tests/ui/wasm/wasm-bindgen-broken-error.rs b/tests/ui/wasm/wasm-bindgen-broken-error.rs new file mode 100644 index 0000000000000..d985e87980314 --- /dev/null +++ b/tests/ui/wasm/wasm-bindgen-broken-error.rs @@ -0,0 +1,28 @@ +//@ only-wasm32 +//@ revisions: v0_1_0 v0_2_87 v0_2_88 v0_3_0 v1_0_0 +//@[v0_1_0] check-fail +//@[v0_1_0] rustc-env:CARGO_PKG_VERSION_MAJOR=0 +//@[v0_1_0] rustc-env:CARGO_PKG_VERSION_MINOR=1 +//@[v0_1_0] rustc-env:CARGO_PKG_VERSION_PATCH=0 +//@[v0_2_87] check-fail +//@[v0_2_87] rustc-env:CARGO_PKG_VERSION_MAJOR=0 +//@[v0_2_87] rustc-env:CARGO_PKG_VERSION_MINOR=2 +//@[v0_2_87] rustc-env:CARGO_PKG_VERSION_PATCH=87 +//@[v0_2_88] check-pass +//@[v0_2_88] rustc-env:CARGO_PKG_VERSION_MAJOR=0 +//@[v0_2_88] rustc-env:CARGO_PKG_VERSION_MINOR=2 +//@[v0_2_88] rustc-env:CARGO_PKG_VERSION_PATCH=88 +//@[v0_3_0] check-pass +//@[v0_3_0] rustc-env:CARGO_PKG_VERSION_MAJOR=0 +//@[v0_3_0] rustc-env:CARGO_PKG_VERSION_MINOR=3 +//@[v0_3_0] rustc-env:CARGO_PKG_VERSION_PATCH=0 +//@[v1_0_0] check-pass +//@[v1_0_0] rustc-env:CARGO_PKG_VERSION_MAJOR=1 +//@[v1_0_0] rustc-env:CARGO_PKG_VERSION_MINOR=0 +//@[v1_0_0] rustc-env:CARGO_PKG_VERSION_PATCH=0 + +#![crate_name = "wasm_bindgen"] +//[v0_1_0]~^ ERROR: older versions of the `wasm-bindgen` crate +//[v0_2_87]~^^ ERROR: older versions of the `wasm-bindgen` crate + +fn main() {} diff --git a/tests/ui/wasm/wasm-bindgen-broken-error.v0_1_0.stderr b/tests/ui/wasm/wasm-bindgen-broken-error.v0_1_0.stderr new file mode 100644 index 0000000000000..e1c1ec7ef3392 --- /dev/null +++ b/tests/ui/wasm/wasm-bindgen-broken-error.v0_1_0.stderr @@ -0,0 +1,8 @@ +error: older versions of the `wasm-bindgen` crate are incompatible with current versions of Rust; please update to `wasm-bindgen` v0.2.88 + --> $DIR/wasm-bindgen-broken-error.rs:24:1 + | +LL | #![crate_name = "wasm_bindgen"] + | ^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/wasm/wasm-bindgen-broken-error.v0_2_87.stderr b/tests/ui/wasm/wasm-bindgen-broken-error.v0_2_87.stderr new file mode 100644 index 0000000000000..e1c1ec7ef3392 --- /dev/null +++ b/tests/ui/wasm/wasm-bindgen-broken-error.v0_2_87.stderr @@ -0,0 +1,8 @@ +error: older versions of the `wasm-bindgen` crate are incompatible with current versions of Rust; please update to `wasm-bindgen` v0.2.88 + --> $DIR/wasm-bindgen-broken-error.rs:24:1 + | +LL | #![crate_name = "wasm_bindgen"] + | ^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/weird-exprs.rs b/tests/ui/weird-exprs.rs index 08b5517aae22f..55a8d580a8b7b 100644 --- a/tests/ui/weird-exprs.rs +++ b/tests/ui/weird-exprs.rs @@ -34,7 +34,7 @@ fn what() { let i = &Cell::new(false); let dont = {||the(i)}; dont(); - assert!((i.get())); + assert!(i.get()); } fn zombiejesus() { @@ -69,8 +69,8 @@ fn notsure() { fn canttouchthis() -> usize { fn p() -> bool { true } - let _a = (assert!((true)) == (assert!(p()))); - let _c = (assert!((p())) == ()); + let _a = (assert!(true) == (assert!(p()))); + let _c = (assert!(p()) == ()); let _b: bool = (println!("{}", 0) == (return 0)); } diff --git a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr index 339f7b2cc8207..3b4de0753af0b 100644 --- a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr +++ b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr @@ -96,8 +96,9 @@ LL | fn fnc(&self) -> Trait { | help: you might have meant to use `Self` to refer to the implementing type | -LL | fn fnc(&self) -> Self { - | ~~~~ +LL - fn fnc(&self) -> Trait { +LL + fn fnc(&self) -> Self { + | error: aborting due to 7 previous errors; 3 warnings emitted diff --git a/tests/ui/wf/ice-hir-wf-issue-135341.rs b/tests/ui/wf/ice-hir-wf-issue-135341.rs new file mode 100644 index 0000000000000..7428575aee0db --- /dev/null +++ b/tests/ui/wf/ice-hir-wf-issue-135341.rs @@ -0,0 +1,4 @@ +type A = B; +type B = _; //~ ERROR the placeholder `_` is not allowed within types on item signatures for type aliases + +fn main() {} diff --git a/tests/ui/wf/ice-hir-wf-issue-135341.stderr b/tests/ui/wf/ice-hir-wf-issue-135341.stderr new file mode 100644 index 0000000000000..c568129cf56d0 --- /dev/null +++ b/tests/ui/wf/ice-hir-wf-issue-135341.stderr @@ -0,0 +1,9 @@ +error[E0121]: the placeholder `_` is not allowed within types on item signatures for type aliases + --> $DIR/ice-hir-wf-issue-135341.rs:2:10 + | +LL | type B = _; + | ^ not allowed in type signatures + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0121`. diff --git a/tests/ui/wf/issue-87495.rs b/tests/ui/wf/issue-87495.rs index 5aab7431134ba..ce5c617bbbd38 100644 --- a/tests/ui/wf/issue-87495.rs +++ b/tests/ui/wf/issue-87495.rs @@ -2,7 +2,7 @@ trait T { const CONST: (bool, dyn T); - //~^ ERROR: the trait `T` cannot be made into an object [E0038] + //~^ ERROR: the trait `T` is not dyn compatible [E0038] } fn main() {} diff --git a/tests/ui/wf/issue-87495.stderr b/tests/ui/wf/issue-87495.stderr index 5973fff3e009e..0c293e3576d64 100644 --- a/tests/ui/wf/issue-87495.stderr +++ b/tests/ui/wf/issue-87495.stderr @@ -1,14 +1,15 @@ -error[E0038]: the trait `T` cannot be made into an object +error[E0038]: the trait `T` is not dyn compatible --> $DIR/issue-87495.rs:4:25 | LL | const CONST: (bool, dyn T); - | ^^^^^ `T` cannot be made into an object + | ^^^^^ `T` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/issue-87495.rs:4:11 | LL | trait T { - | - this trait cannot be made into an object... + | - this trait is not dyn compatible... LL | const CONST: (bool, dyn T); | ^^^^^ ...because it contains this associated `const` = help: consider moving `CONST` to another trait diff --git a/tests/ui/issues/issue-40749.rs b/tests/ui/wf/range-expr-root-of-constant-issue-40749.rs similarity index 100% rename from tests/ui/issues/issue-40749.rs rename to tests/ui/wf/range-expr-root-of-constant-issue-40749.rs diff --git a/tests/ui/wf/range-expr-root-of-constant-issue-40749.stderr b/tests/ui/wf/range-expr-root-of-constant-issue-40749.stderr new file mode 100644 index 0000000000000..482773a39440f --- /dev/null +++ b/tests/ui/wf/range-expr-root-of-constant-issue-40749.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/range-expr-root-of-constant-issue-40749.rs:2:9 + | +LL | [0; ..10]; + | ^^^^ expected `usize`, found `RangeTo<{integer}>` + | + = note: expected type `usize` + found struct `RangeTo<{integer}>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/wf/wf-convert-dyn-incompat-trait-obj-box.stderr b/tests/ui/wf/wf-convert-dyn-incompat-trait-obj-box.stderr index 38426545bc8cb..f3e4f2a63e98a 100644 --- a/tests/ui/wf/wf-convert-dyn-incompat-trait-obj-box.stderr +++ b/tests/ui/wf/wf-convert-dyn-incompat-trait-obj-box.stderr @@ -1,49 +1,52 @@ -error[E0038]: the trait `Trait` cannot be made into an object +error[E0038]: the trait `Trait` is not dyn compatible --> $DIR/wf-convert-dyn-incompat-trait-obj-box.rs:16:33 | LL | let t_box: Box = Box::new(S); - | ^^^^^^^^^^^ `Trait` cannot be made into an object + | ^^^^^^^^^^^ `Trait` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/wf-convert-dyn-incompat-trait-obj-box.rs:6:14 | LL | trait Trait: Sized {} | ----- ^^^^^ ...because it requires `Self: Sized` | | - | this trait cannot be made into an object... - = help: only type `S` implements the trait, consider using it directly instead + | this trait is not dyn compatible... + = help: only type `S` implements `Trait`; consider using it directly instead. = note: required for the cast from `Box` to `Box` -error[E0038]: the trait `Trait` cannot be made into an object +error[E0038]: the trait `Trait` is not dyn compatible --> $DIR/wf-convert-dyn-incompat-trait-obj-box.rs:17:15 | LL | takes_box(Box::new(S)); - | ^^^^^^^^^^^ `Trait` cannot be made into an object + | ^^^^^^^^^^^ `Trait` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/wf-convert-dyn-incompat-trait-obj-box.rs:6:14 | LL | trait Trait: Sized {} | ----- ^^^^^ ...because it requires `Self: Sized` | | - | this trait cannot be made into an object... - = help: only type `S` implements the trait, consider using it directly instead + | this trait is not dyn compatible... + = help: only type `S` implements `Trait`; consider using it directly instead. = note: required for the cast from `Box` to `Box<(dyn Trait + 'static)>` -error[E0038]: the trait `Trait` cannot be made into an object +error[E0038]: the trait `Trait` is not dyn compatible --> $DIR/wf-convert-dyn-incompat-trait-obj-box.rs:15:5 | LL | Box::new(S) as Box; - | ^^^^^^^^^^^ `Trait` cannot be made into an object + | ^^^^^^^^^^^ `Trait` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/wf-convert-dyn-incompat-trait-obj-box.rs:6:14 | LL | trait Trait: Sized {} | ----- ^^^^^ ...because it requires `Self: Sized` | | - | this trait cannot be made into an object... - = help: only type `S` implements the trait, consider using it directly instead + | this trait is not dyn compatible... + = help: only type `S` implements `Trait`; consider using it directly instead. = note: required for the cast from `Box` to `Box` error: aborting due to 3 previous errors diff --git a/tests/ui/wf/wf-convert-dyn-incompat-trait-obj.stderr b/tests/ui/wf/wf-convert-dyn-incompat-trait-obj.stderr index 94259aa5b0aa8..716d0e78ff11d 100644 --- a/tests/ui/wf/wf-convert-dyn-incompat-trait-obj.stderr +++ b/tests/ui/wf/wf-convert-dyn-incompat-trait-obj.stderr @@ -1,49 +1,52 @@ -error[E0038]: the trait `Trait` cannot be made into an object +error[E0038]: the trait `Trait` is not dyn compatible --> $DIR/wf-convert-dyn-incompat-trait-obj.rs:16:25 | LL | let t: &dyn Trait = &S; - | ^^ `Trait` cannot be made into an object + | ^^ `Trait` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/wf-convert-dyn-incompat-trait-obj.rs:6:14 | LL | trait Trait: Sized {} | ----- ^^^^^ ...because it requires `Self: Sized` | | - | this trait cannot be made into an object... - = help: only type `S` implements the trait, consider using it directly instead + | this trait is not dyn compatible... + = help: only type `S` implements `Trait`; consider using it directly instead. = note: required for the cast from `&S` to `&dyn Trait` -error[E0038]: the trait `Trait` cannot be made into an object +error[E0038]: the trait `Trait` is not dyn compatible --> $DIR/wf-convert-dyn-incompat-trait-obj.rs:17:17 | LL | takes_trait(&S); - | ^^ `Trait` cannot be made into an object + | ^^ `Trait` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/wf-convert-dyn-incompat-trait-obj.rs:6:14 | LL | trait Trait: Sized {} | ----- ^^^^^ ...because it requires `Self: Sized` | | - | this trait cannot be made into an object... - = help: only type `S` implements the trait, consider using it directly instead + | this trait is not dyn compatible... + = help: only type `S` implements `Trait`; consider using it directly instead. = note: required for the cast from `&S` to `&dyn Trait` -error[E0038]: the trait `Trait` cannot be made into an object +error[E0038]: the trait `Trait` is not dyn compatible --> $DIR/wf-convert-dyn-incompat-trait-obj.rs:15:5 | LL | &S as &dyn Trait; - | ^^ `Trait` cannot be made into an object + | ^^ `Trait` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/wf-convert-dyn-incompat-trait-obj.rs:6:14 | LL | trait Trait: Sized {} | ----- ^^^^^ ...because it requires `Self: Sized` | | - | this trait cannot be made into an object... - = help: only type `S` implements the trait, consider using it directly instead + | this trait is not dyn compatible... + = help: only type `S` implements `Trait`; consider using it directly instead. = note: required for the cast from `&S` to `&dyn Trait` error: aborting due to 3 previous errors diff --git a/tests/ui/wf/wf-dyn-incompat-trait-obj-match.stderr b/tests/ui/wf/wf-dyn-incompat-trait-obj-match.stderr index 6cd4ebf8412b6..a7405ce4caa9f 100644 --- a/tests/ui/wf/wf-dyn-incompat-trait-obj-match.stderr +++ b/tests/ui/wf/wf-dyn-incompat-trait-obj-match.stderr @@ -12,40 +12,46 @@ LL | | } = note: expected reference `&S` found reference `&R` -error[E0038]: the trait `Trait` cannot be made into an object +error[E0038]: the trait `Trait` is not dyn compatible --> $DIR/wf-dyn-incompat-trait-obj-match.rs:26:21 | LL | Some(()) => &S, - | ^^ `Trait` cannot be made into an object + | ^^ `Trait` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/wf-dyn-incompat-trait-obj-match.rs:6:14 | LL | trait Trait: Sized {} | ----- ^^^^^ ...because it requires `Self: Sized` | | - | this trait cannot be made into an object... - = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `Trait` for this new enum and using it instead: + | this trait is not dyn compatible... + = help: the following types implement `Trait`: S R + consider defining an enum where each variant holds one of these types, + implementing `Trait` for this new enum and using it instead = note: required for the cast from `&S` to `&dyn Trait` -error[E0038]: the trait `Trait` cannot be made into an object +error[E0038]: the trait `Trait` is not dyn compatible --> $DIR/wf-dyn-incompat-trait-obj-match.rs:27:17 | LL | None => &R, - | ^^ `Trait` cannot be made into an object + | ^^ `Trait` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/wf-dyn-incompat-trait-obj-match.rs:6:14 | LL | trait Trait: Sized {} | ----- ^^^^^ ...because it requires `Self: Sized` | | - | this trait cannot be made into an object... - = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `Trait` for this new enum and using it instead: + | this trait is not dyn compatible... + = help: the following types implement `Trait`: S R + consider defining an enum where each variant holds one of these types, + implementing `Trait` for this new enum and using it instead = note: required for the cast from `&R` to `&dyn Trait` error: aborting due to 3 previous errors diff --git a/tests/ui/wf/wf-dyn-incompatible.stderr b/tests/ui/wf/wf-dyn-incompatible.stderr index cf016b63c7405..e61b37d92932f 100644 --- a/tests/ui/wf/wf-dyn-incompatible.stderr +++ b/tests/ui/wf/wf-dyn-incompatible.stderr @@ -1,14 +1,15 @@ -error[E0038]: the trait `A` cannot be made into an object +error[E0038]: the trait `A` is not dyn compatible --> $DIR/wf-dyn-incompatible.rs:9:13 | LL | let _x: &dyn A; - | ^^^^^^ `A` cannot be made into an object + | ^^^^^^ `A` is not dyn compatible | -note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit --> $DIR/wf-dyn-incompatible.rs:5:23 | LL | trait A { - | - this trait cannot be made into an object... + | - this trait is not dyn compatible... LL | fn foo(&self, _x: &Self); | ^^^^^ ...because method `foo` references the `Self` type in this parameter = help: consider moving `foo` to another trait diff --git a/tests/ui/wf/wf-fn-where-clause.stderr b/tests/ui/wf/wf-fn-where-clause.stderr index 76671dedabf47..b419bc8347fb2 100644 --- a/tests/ui/wf/wf-fn-where-clause.stderr +++ b/tests/ui/wf/wf-fn-where-clause.stderr @@ -14,14 +14,15 @@ help: consider further restricting type parameter `U` with trait `Copy` LL | fn foo() where T: ExtraCopy, U: std::marker::Copy | ++++++++++++++++++++++ -error[E0038]: the trait `Copy` cannot be made into an object +error[E0038]: the trait `Copy` is not dyn compatible --> $DIR/wf-fn-where-clause.rs:12:16 | LL | fn bar() where Vec:, {} - | ^^^^^^^^^^^^^ `Copy` cannot be made into an object + | ^^^^^^^^^^^^^ `Copy` is not dyn compatible | - = note: the trait cannot be made into an object because it requires `Self: Sized` - = note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + = note: the trait is not dyn compatible because it requires `Self: Sized` + = note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit error[E0277]: the size for values of type `(dyn Copy + 'static)` cannot be known at compilation time --> $DIR/wf-fn-where-clause.rs:12:16 diff --git a/tests/ui/wf/wf-normalization-sized.next.stderr b/tests/ui/wf/wf-normalization-sized.next.stderr index 1e898fb7b78ab..83b56bb6b19ad 100644 --- a/tests/ui/wf/wf-normalization-sized.next.stderr +++ b/tests/ui/wf/wf-normalization-sized.next.stderr @@ -5,6 +5,7 @@ LL | const _: <[[[[[[u8]]]]]] as WellUnformed>::RequestNormalize = (); | ^^^^^^^^^^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `[[[[[u8]]]]]` + = note: slice and array elements must have `Sized` type error[E0277]: the size for values of type `[[[[[u8]]]]]` cannot be known at compilation time --> $DIR/wf-normalization-sized.rs:19:11 @@ -13,6 +14,7 @@ LL | const _: <[[[[[[u8]]]]]] as WellUnformed>::RequestNormalize = (); | ^^^^^^^^^^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `[[[[[u8]]]]]` + = note: slice and array elements must have `Sized` type = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: the size for values of type `str` cannot be known at compilation time @@ -22,6 +24,8 @@ LL | const _: as WellUnformed>::RequestNormalize = (); | ^^^^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `str` +note: required by an implicit `Sized` bound in `Vec` + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL error[E0277]: the size for values of type `str` cannot be known at compilation time --> $DIR/wf-normalization-sized.rs:22:11 @@ -30,6 +34,8 @@ LL | const _: as WellUnformed>::RequestNormalize = (); | ^^^^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `str` +note: required by an implicit `Sized` bound in `Vec` + --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 4 previous errors diff --git a/tests/ui/wf/wf-trait-default-fn-ret.rs b/tests/ui/wf/wf-trait-default-fn-ret.rs index 2103dae8d233a..a7f83dcf678aa 100644 --- a/tests/ui/wf/wf-trait-default-fn-ret.rs +++ b/tests/ui/wf/wf-trait-default-fn-ret.rs @@ -1,5 +1,4 @@ -// Check that we test WF conditions for fn arguments. Because the -// current code is so goofy, this is only a warning for now. +// Check that we test WF conditions for fn arguments. #![feature(rustc_attrs)] #![allow(dead_code)] diff --git a/tests/ui/wf/wf-trait-default-fn-ret.stderr b/tests/ui/wf/wf-trait-default-fn-ret.stderr index f749ac7b1b3e6..f0d1b55edc49a 100644 --- a/tests/ui/wf/wf-trait-default-fn-ret.stderr +++ b/tests/ui/wf/wf-trait-default-fn-ret.stderr @@ -1,11 +1,11 @@ error[E0277]: the trait bound `Self: Eq` is not satisfied - --> $DIR/wf-trait-default-fn-ret.rs:11:22 + --> $DIR/wf-trait-default-fn-ret.rs:10:22 | LL | fn bar(&self) -> Bar { | ^^^^^^^^^ the trait `Eq` is not implemented for `Self` | note: required by a bound in `Bar` - --> $DIR/wf-trait-default-fn-ret.rs:8:14 + --> $DIR/wf-trait-default-fn-ret.rs:7:14 | LL | struct Bar { value: Box } | ^^ required by this bound in `Bar` diff --git a/tests/ui/wf/wf-trait-fn-arg.current.stderr b/tests/ui/wf/wf-trait-fn-arg.current.stderr new file mode 100644 index 0000000000000..d5dd36fad6dd6 --- /dev/null +++ b/tests/ui/wf/wf-trait-fn-arg.current.stderr @@ -0,0 +1,19 @@ +error[E0277]: the trait bound `Self: Eq` is not satisfied + --> $DIR/wf-trait-fn-arg.rs:16:23 + | +LL | fn bar(&self, x: &Bar); + | ^^^^^^^^^ the trait `Eq` is not implemented for `Self` + | +note: required by a bound in `Bar` + --> $DIR/wf-trait-fn-arg.rs:11:15 + | +LL | struct Bar { + | ^^ required by this bound in `Bar` +help: consider further restricting `Self` + | +LL | fn bar(&self, x: &Bar) where Self: Eq; + | ++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/wf/wf-trait-fn-arg.next.stderr b/tests/ui/wf/wf-trait-fn-arg.next.stderr new file mode 100644 index 0000000000000..d5dd36fad6dd6 --- /dev/null +++ b/tests/ui/wf/wf-trait-fn-arg.next.stderr @@ -0,0 +1,19 @@ +error[E0277]: the trait bound `Self: Eq` is not satisfied + --> $DIR/wf-trait-fn-arg.rs:16:23 + | +LL | fn bar(&self, x: &Bar); + | ^^^^^^^^^ the trait `Eq` is not implemented for `Self` + | +note: required by a bound in `Bar` + --> $DIR/wf-trait-fn-arg.rs:11:15 + | +LL | struct Bar { + | ^^ required by this bound in `Bar` +help: consider further restricting `Self` + | +LL | fn bar(&self, x: &Bar) where Self: Eq; + | ++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/wf/wf-trait-fn-arg.rs b/tests/ui/wf/wf-trait-fn-arg.rs index 0445699427e2a..13a5b32828d82 100644 --- a/tests/ui/wf/wf-trait-fn-arg.rs +++ b/tests/ui/wf/wf-trait-fn-arg.rs @@ -1,16 +1,22 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) + // Check that we test WF conditions for fn arguments in a trait definition. #![feature(rustc_attrs)] #![allow(dead_code)] #![allow(unused_variables)] -struct Bar { value: Box } +struct Bar { + value: Box, +} trait Foo { fn bar(&self, x: &Bar); - //~^ ERROR E0277 - // - // Here, Eq ought to be implemented. + //~^ ERROR E0277 + // + // Here, Eq ought to be implemented. } -fn main() { } +fn main() {} diff --git a/tests/ui/wf/wf-trait-fn-arg.stderr b/tests/ui/wf/wf-trait-fn-arg.stderr deleted file mode 100644 index 8b35f36fa68a8..0000000000000 --- a/tests/ui/wf/wf-trait-fn-arg.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0277]: the trait bound `Self: Eq` is not satisfied - --> $DIR/wf-trait-fn-arg.rs:10:23 - | -LL | fn bar(&self, x: &Bar); - | ^^^^^^^^^ the trait `Eq` is not implemented for `Self` - | -note: required by a bound in `Bar` - --> $DIR/wf-trait-fn-arg.rs:7:14 - | -LL | struct Bar { value: Box } - | ^^ required by this bound in `Bar` -help: consider further restricting `Self` - | -LL | fn bar(&self, x: &Bar) where Self: Eq; - | ++++++++++++++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/wf/wf-trait-fn-ret.current.stderr b/tests/ui/wf/wf-trait-fn-ret.current.stderr new file mode 100644 index 0000000000000..0ad786c2fd566 --- /dev/null +++ b/tests/ui/wf/wf-trait-fn-ret.current.stderr @@ -0,0 +1,19 @@ +error[E0277]: the trait bound `Self: Eq` is not satisfied + --> $DIR/wf-trait-fn-ret.rs:15:23 + | +LL | fn bar(&self) -> &Bar; + | ^^^^^^^^^ the trait `Eq` is not implemented for `Self` + | +note: required by a bound in `Bar` + --> $DIR/wf-trait-fn-ret.rs:10:15 + | +LL | struct Bar { + | ^^ required by this bound in `Bar` +help: consider further restricting `Self` + | +LL | fn bar(&self) -> &Bar where Self: Eq; + | ++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/wf/wf-trait-fn-ret.next.stderr b/tests/ui/wf/wf-trait-fn-ret.next.stderr new file mode 100644 index 0000000000000..0ad786c2fd566 --- /dev/null +++ b/tests/ui/wf/wf-trait-fn-ret.next.stderr @@ -0,0 +1,19 @@ +error[E0277]: the trait bound `Self: Eq` is not satisfied + --> $DIR/wf-trait-fn-ret.rs:15:23 + | +LL | fn bar(&self) -> &Bar; + | ^^^^^^^^^ the trait `Eq` is not implemented for `Self` + | +note: required by a bound in `Bar` + --> $DIR/wf-trait-fn-ret.rs:10:15 + | +LL | struct Bar { + | ^^ required by this bound in `Bar` +help: consider further restricting `Self` + | +LL | fn bar(&self) -> &Bar where Self: Eq; + | ++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/wf/wf-trait-fn-ret.rs b/tests/ui/wf/wf-trait-fn-ret.rs index f49e43087701b..c00f6dd10af21 100644 --- a/tests/ui/wf/wf-trait-fn-ret.rs +++ b/tests/ui/wf/wf-trait-fn-ret.rs @@ -1,16 +1,21 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) // Check that we test WF conditions for fn return types in a trait definition. #![feature(rustc_attrs)] #![allow(dead_code)] #![allow(unused_variables)] -struct Bar { value: Box } +struct Bar { + value: Box, +} trait Foo { fn bar(&self) -> &Bar; - //~^ ERROR E0277 - // - // Here, Eq ought to be implemented. + //~^ ERROR E0277 + // + // Here, Eq ought to be implemented. } -fn main() { } +fn main() {} diff --git a/tests/ui/wf/wf-trait-fn-ret.stderr b/tests/ui/wf/wf-trait-fn-ret.stderr deleted file mode 100644 index 3d70f04def2e0..0000000000000 --- a/tests/ui/wf/wf-trait-fn-ret.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0277]: the trait bound `Self: Eq` is not satisfied - --> $DIR/wf-trait-fn-ret.rs:10:23 - | -LL | fn bar(&self) -> &Bar; - | ^^^^^^^^^ the trait `Eq` is not implemented for `Self` - | -note: required by a bound in `Bar` - --> $DIR/wf-trait-fn-ret.rs:7:14 - | -LL | struct Bar { value: Box } - | ^^ required by this bound in `Bar` -help: consider further restricting `Self` - | -LL | fn bar(&self) -> &Bar where Self: Eq; - | ++++++++++++++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/wf/wf-trait-fn-where-clause.current.stderr b/tests/ui/wf/wf-trait-fn-where-clause.current.stderr new file mode 100644 index 0000000000000..db5454d0f3c22 --- /dev/null +++ b/tests/ui/wf/wf-trait-fn-where-clause.current.stderr @@ -0,0 +1,19 @@ +error[E0277]: the trait bound `Self: Eq` is not satisfied + --> $DIR/wf-trait-fn-where-clause.rs:18:20 + | +LL | Bar: Copy; + | ^^^^ the trait `Eq` is not implemented for `Self` + | +note: required by a bound in `Bar` + --> $DIR/wf-trait-fn-where-clause.rs:10:15 + | +LL | struct Bar { + | ^^ required by this bound in `Bar` +help: consider further restricting `Self` + | +LL | Bar: Copy, Self: Eq; + | ++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/wf/wf-trait-fn-where-clause.next.stderr b/tests/ui/wf/wf-trait-fn-where-clause.next.stderr new file mode 100644 index 0000000000000..db5454d0f3c22 --- /dev/null +++ b/tests/ui/wf/wf-trait-fn-where-clause.next.stderr @@ -0,0 +1,19 @@ +error[E0277]: the trait bound `Self: Eq` is not satisfied + --> $DIR/wf-trait-fn-where-clause.rs:18:20 + | +LL | Bar: Copy; + | ^^^^ the trait `Eq` is not implemented for `Self` + | +note: required by a bound in `Bar` + --> $DIR/wf-trait-fn-where-clause.rs:10:15 + | +LL | struct Bar { + | ^^ required by this bound in `Bar` +help: consider further restricting `Self` + | +LL | Bar: Copy, Self: Eq; + | ++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/wf/wf-trait-fn-where-clause.rs b/tests/ui/wf/wf-trait-fn-where-clause.rs index 1d2427ff981d7..9e36682e449d5 100644 --- a/tests/ui/wf/wf-trait-fn-where-clause.rs +++ b/tests/ui/wf/wf-trait-fn-where-clause.rs @@ -1,17 +1,24 @@ -// Check that we test WF conditions for fn where clauses in a trait definition. +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +// Check that we test WF conditions for fn where clauses in a trait definition. #![allow(dead_code)] #![allow(unused_variables)] -struct Bar { value: Box } +struct Bar { + value: Box, +} trait Foo { - fn bar(&self) where Self: Sized, Bar: Copy; - //~^ ERROR E0277 - // - // Here, Eq ought to be implemented. + fn bar(&self) + where + Self: Sized, + Bar: Copy; + //~^ ERROR E0277 + // + // Here, Eq ought to be implemented. } - -fn main() { } +fn main() {} diff --git a/tests/ui/wf/wf-trait-fn-where-clause.stderr b/tests/ui/wf/wf-trait-fn-where-clause.stderr deleted file mode 100644 index 0ad3b58e7c76a..0000000000000 --- a/tests/ui/wf/wf-trait-fn-where-clause.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0277]: the trait bound `Self: Eq` is not satisfied - --> $DIR/wf-trait-fn-where-clause.rs:10:49 - | -LL | fn bar(&self) where Self: Sized, Bar: Copy; - | ^^^^ the trait `Eq` is not implemented for `Self` - | -note: required by a bound in `Bar` - --> $DIR/wf-trait-fn-where-clause.rs:7:14 - | -LL | struct Bar { value: Box } - | ^^ required by this bound in `Bar` -help: consider further restricting `Self` - | -LL | fn bar(&self) where Self: Sized, Bar: Copy, Self: Eq; - | ++++++++++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/where-clauses/where-clauses-method-unsatisfied.current.stderr b/tests/ui/where-clauses/where-clauses-method-unsatisfied.current.stderr new file mode 100644 index 0000000000000..d1de813a2d9f8 --- /dev/null +++ b/tests/ui/where-clauses/where-clauses-method-unsatisfied.current.stderr @@ -0,0 +1,23 @@ +error[E0277]: the trait bound `Bar: Eq` is not satisfied + --> $DIR/where-clauses-method-unsatisfied.rs:24:7 + | +LL | x.equals(&x); + | ^^^^^^ the trait `Eq` is not implemented for `Bar` + | +note: required by a bound in `Foo::::equals` + --> $DIR/where-clauses-method-unsatisfied.rs:16:12 + | +LL | fn equals(&self, u: &Foo) -> bool + | ------ required by a bound in this associated function +LL | where +LL | T: Eq, + | ^^ required by this bound in `Foo::::equals` +help: consider annotating `Bar` with `#[derive(Eq)]` + | +LL + #[derive(Eq)] +LL | struct Bar; // does not implement Eq + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/where-clauses/where-clauses-method-unsatisfied.next.stderr b/tests/ui/where-clauses/where-clauses-method-unsatisfied.next.stderr new file mode 100644 index 0000000000000..d1de813a2d9f8 --- /dev/null +++ b/tests/ui/where-clauses/where-clauses-method-unsatisfied.next.stderr @@ -0,0 +1,23 @@ +error[E0277]: the trait bound `Bar: Eq` is not satisfied + --> $DIR/where-clauses-method-unsatisfied.rs:24:7 + | +LL | x.equals(&x); + | ^^^^^^ the trait `Eq` is not implemented for `Bar` + | +note: required by a bound in `Foo::::equals` + --> $DIR/where-clauses-method-unsatisfied.rs:16:12 + | +LL | fn equals(&self, u: &Foo) -> bool + | ------ required by a bound in this associated function +LL | where +LL | T: Eq, + | ^^ required by this bound in `Foo::::equals` +help: consider annotating `Bar` with `#[derive(Eq)]` + | +LL + #[derive(Eq)] +LL | struct Bar; // does not implement Eq + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/where-clauses/where-clauses-method-unsatisfied.rs b/tests/ui/where-clauses/where-clauses-method-unsatisfied.rs index a8ae02964078b..34e9d9b57d1c6 100644 --- a/tests/ui/where-clauses/where-clauses-method-unsatisfied.rs +++ b/tests/ui/where-clauses/where-clauses-method-unsatisfied.rs @@ -1,14 +1,20 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) // Test that a where clause attached to a method allows us to add // additional constraints to a parameter out of scope. struct Foo { - value: T + value: T, } struct Bar; // does not implement Eq impl Foo { - fn equals(&self, u: &Foo) -> bool where T : Eq { + fn equals(&self, u: &Foo) -> bool + where + T: Eq, + { self.value == u.value } } diff --git a/tests/ui/where-clauses/where-clauses-method-unsatisfied.stderr b/tests/ui/where-clauses/where-clauses-method-unsatisfied.stderr deleted file mode 100644 index 840df342ef9a9..0000000000000 --- a/tests/ui/where-clauses/where-clauses-method-unsatisfied.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error[E0277]: the trait bound `Bar: Eq` is not satisfied - --> $DIR/where-clauses-method-unsatisfied.rs:18:7 - | -LL | x.equals(&x); - | ^^^^^^ the trait `Eq` is not implemented for `Bar` - | -note: required by a bound in `Foo::::equals` - --> $DIR/where-clauses-method-unsatisfied.rs:11:52 - | -LL | fn equals(&self, u: &Foo) -> bool where T : Eq { - | ^^ required by this bound in `Foo::::equals` -help: consider annotating `Bar` with `#[derive(Eq)]` - | -LL + #[derive(Eq)] -LL | struct Bar; // does not implement Eq - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/where-clauses/where-clauses-unsatisfied.current.stderr b/tests/ui/where-clauses/where-clauses-unsatisfied.current.stderr new file mode 100644 index 0000000000000..485b9459ddd0e --- /dev/null +++ b/tests/ui/where-clauses/where-clauses-unsatisfied.current.stderr @@ -0,0 +1,23 @@ +error[E0277]: the trait bound `Struct: Eq` is not satisfied + --> $DIR/where-clauses-unsatisfied.rs:15:10 + | +LL | drop(equal(&Struct, &Struct)) + | ^^^^^ the trait `Eq` is not implemented for `Struct` + | +note: required by a bound in `equal` + --> $DIR/where-clauses-unsatisfied.rs:7:8 + | +LL | fn equal(a: &T, b: &T) -> bool + | ----- required by a bound in this function +LL | where +LL | T: Eq, + | ^^ required by this bound in `equal` +help: consider annotating `Struct` with `#[derive(Eq)]` + | +LL + #[derive(Eq)] +LL | struct Struct; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/where-clauses/where-clauses-unsatisfied.next.stderr b/tests/ui/where-clauses/where-clauses-unsatisfied.next.stderr new file mode 100644 index 0000000000000..485b9459ddd0e --- /dev/null +++ b/tests/ui/where-clauses/where-clauses-unsatisfied.next.stderr @@ -0,0 +1,23 @@ +error[E0277]: the trait bound `Struct: Eq` is not satisfied + --> $DIR/where-clauses-unsatisfied.rs:15:10 + | +LL | drop(equal(&Struct, &Struct)) + | ^^^^^ the trait `Eq` is not implemented for `Struct` + | +note: required by a bound in `equal` + --> $DIR/where-clauses-unsatisfied.rs:7:8 + | +LL | fn equal(a: &T, b: &T) -> bool + | ----- required by a bound in this function +LL | where +LL | T: Eq, + | ^^ required by this bound in `equal` +help: consider annotating `Struct` with `#[derive(Eq)]` + | +LL + #[derive(Eq)] +LL | struct Struct; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/where-clauses/where-clauses-unsatisfied.rs b/tests/ui/where-clauses/where-clauses-unsatisfied.rs index 8b067d30a2a85..48798e2a15dbb 100644 --- a/tests/ui/where-clauses/where-clauses-unsatisfied.rs +++ b/tests/ui/where-clauses/where-clauses-unsatisfied.rs @@ -1,4 +1,13 @@ -fn equal(a: &T, b: &T) -> bool where T : Eq { a == b } +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) + +fn equal(a: &T, b: &T) -> bool +where + T: Eq, +{ + a == b +} struct Struct; diff --git a/tests/ui/where-clauses/where-clauses-unsatisfied.stderr b/tests/ui/where-clauses/where-clauses-unsatisfied.stderr deleted file mode 100644 index 205b82d49bfeb..0000000000000 --- a/tests/ui/where-clauses/where-clauses-unsatisfied.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error[E0277]: the trait bound `Struct: Eq` is not satisfied - --> $DIR/where-clauses-unsatisfied.rs:6:10 - | -LL | drop(equal(&Struct, &Struct)) - | ^^^^^ the trait `Eq` is not implemented for `Struct` - | -note: required by a bound in `equal` - --> $DIR/where-clauses-unsatisfied.rs:1:45 - | -LL | fn equal(a: &T, b: &T) -> bool where T : Eq { a == b } - | ^^ required by this bound in `equal` -help: consider annotating `Struct` with `#[derive(Eq)]` - | -LL + #[derive(Eq)] -LL | struct Struct; - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/while/while-let-scope-issue-40235.rs b/tests/ui/while/while-let-scope-issue-40235.rs new file mode 100644 index 0000000000000..7d5dfc64a9059 --- /dev/null +++ b/tests/ui/while/while-let-scope-issue-40235.rs @@ -0,0 +1,8 @@ +//@ check-pass +#![allow(unused_variables)] +fn foo() {} + +fn main() { + while let Some(foo) = Some(1) { break } + foo(); +} diff --git a/triagebot.toml b/triagebot.toml index 436c88541a5f7..512736b0e8ffe 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -172,6 +172,12 @@ issues). """ label = "O-emscripten" +[ping.relnotes-interest-group] +message = """\ +Hi relnotes-interest-group, this PR adds release notes. Could you review this PR +if you have time? Thanks <3 +""" + [prioritize] label = "I-prioritize" @@ -221,6 +227,16 @@ exclude_labels = [ "T-*", ] +trigger_labels = [ + "A-rustdoc-json", + "A-rustdoc-type-layout", + "A-rustdoc-scrape-examples", + "A-link-to-definition", + "A-cross-crate-reexports", + "A-intra-doc-links", + "A-doc-alias", +] + [autolabel."A-rustdoc-json"] trigger_files = [ "src/librustdoc/json/", @@ -230,6 +246,42 @@ trigger_files = [ "src/tools/jsondoclint", ] +[autolabel."A-attributes"] +trigger_files = [ + "compiler/rustc_codegen_ssa/src/codegen_attrs.rs", + "compiler/rustc_passes/src/check_attr.rs", + "compiler/rustc_attr_parsing", + "compiler/rustc_attr_data_structures", + "compiler/rustc_attr_validation", +] + +[autolabel."T-rustdoc-frontend"] +trigger_labels = [ + "A-rustdoc-search", + "A-rustdoc-ui", + "A-rustdoc-js", +] + +trigger_files = [ + "src/librustdoc/html/", + "tests/rustdoc/", + "tests/rustdoc-gui/", + "tests/rustdoc-js/", + "tests/rustdoc-js-std/", + # note: tests/rustdoc-ui tests the CLI, not the web frontend +] + +[autolabel."A-rustdoc-search"] +trigger_files = [ + "src/librustdoc/html/static/js/search.js", + "tests/rustdoc-js", + "tests/rustdoc-js-std", +] + +trigger_labels = [ + "A-type-based-search", +] + [autolabel."T-compiler"] trigger_files = [ # Source code @@ -379,12 +431,16 @@ trigger_files = [ "src/tools/tidy", "src/tools/rustdoc-gui-test", "src/tools/libcxx-version", - "src/tools/rustc-perf-wrapper", "x.py", "x", "x.ps1" ] +[autolabel."A-bootstrap-stamp"] +trigger_files = [ + "src/bootstrap/src/utils/build_stamp.rs", +] + [autolabel."T-infra"] trigger_files = [ "src/ci", @@ -414,13 +470,20 @@ trigger_files = [ [autolabel."A-testsuite"] trigger_files = [ + "src/bootstrap/src/core/build_steps/test.rs", "src/ci", - "src/tools/compiletest", "src/tools/cargotest", - "src/tools/tidy", + "src/tools/compiletest", + "src/tools/miropt-test-tools", "src/tools/remote-test-server", "src/tools/remote-test-client", - "src/tools/tier-check" + "src/tools/rustdoc-gui-test", + "src/tools/suggest-tests", +] + +[autolabel."A-tidy"] +trigger_files = [ + "src/tools/tidy", ] [autolabel."A-meta"] @@ -498,6 +561,11 @@ trigger_files = [ "src/tools/compiletest" ] +[autolabel."A-rustc-dev-guide"] +trigger_files = [ + "src/doc/rustc-dev-guide", +] + [notify-zulip."I-prioritize"] zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts topic = "#{number} {title}" @@ -819,7 +887,7 @@ cc = ["@rust-lang/rustfmt"] [mentions."compiler/rustc_middle/src/mir/syntax.rs"] message = "This PR changes MIR" -cc = ["@oli-obk", "@RalfJung", "@JakobDegen", "@davidtwco", "@celinval", "@vakaras"] +cc = ["@oli-obk", "@RalfJung", "@JakobDegen", "@davidtwco", "@vakaras"] [mentions."compiler/rustc_error_messages"] message = "`rustc_error_messages` was changed" @@ -990,14 +1058,26 @@ https://github.com/rust-lang/reference/blob/HEAD/src/identifiers.md. """ cc = ["@ehuss"] +[mentions."src/doc/rustc-dev-guide"] +message = "The rustc-dev-guide subtree was changed. If this PR *only* touches the dev guide consider submitting a PR directly to [rust-lang/rustc-dev-guide](https://github.com/rust-lang/rustc-dev-guide/pulls) otherwise thank you for updating the dev guide with your changes." +cc = ["@BoxyUwU", "@jieyouxu", "@kobzol"] + +[mentions."compiler/rustc_codegen_ssa/src/codegen_attrs.rs"] +cc = ["@jdonszelmann"] +[mentions."compiler/rustc_passes/src/check_attr.rs"] +cc = ["@jdonszelmann"] +[mentions."compiler/rustc_attr_parsing"] +cc = ["@jdonszelmann"] +[mentions."compiler/rustc_attr_data_structures"] +cc = ["@jdonszelmann"] +[mentions."compiler/rustc_attr_validation"] +cc = ["@jdonszelmann"] + [assign] warn_non_default_branch.enable = true contributing_url = "https://rustc-dev-guide.rust-lang.org/getting-started.html" users_on_vacation = [ "jyn514", - "celinval", - "nnethercote", - "spastorino", "workingjubilee", ] @@ -1012,7 +1092,6 @@ branch = "stable" [assign.adhoc_groups] compiler = [ "@BoxyUwU", - "@chenyukang", "@cjgillot", "@compiler-errors", "@davidtwco", @@ -1052,6 +1131,8 @@ bootstrap = [ infra-ci = [ "@Mark-Simulacrum", "@Kobzol", + "@marcoieni", + "@jdno", ] rustdoc = [ "@GuillaumeGomez", @@ -1208,7 +1289,7 @@ project-exploit-mitigations = [ "/src/doc/nomicon" = ["@ehuss"] "/src/doc/reference" = ["@ehuss"] "/src/doc/rust-by-example" = ["@ehuss"] -"/src/doc/rustc-dev-guide" = ["@kobzol", "@jieyouxu"] +"/src/doc/rustc-dev-guide" = ["compiler"] "/src/doc/rustdoc" = ["rustdoc"] "/src/doc/style-guide" = ["style-team"] "/src/etc" = ["@Mark-Simulacrum"]